Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 14 additions & 48 deletions packages/dev/lottiePlayer/src/maths/boundingBox.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import {
type RawElement,
type RawFont,
type RawEllipseShape,
type RawPathShape,
type RawRectangleShape,
type RawStrokeShape,
type RawTextData,
type RawTextDocument,
} from "../parsing/rawTypes";
import { type RawElement, type RawFont, type RawEllipseShape, type RawPathShape, type RawRectangleShape, type RawStrokeShape, type RawTextData } from "../parsing/rawTypes";
import { GetInitialVectorValues, GetInitialBezierData } from "../parsing/rawPropertyHelpers";
import { ApplyLottieTextContext, MeasureLottieText, ResolveLottieText } from "../parsing/textLayout";

/**
* Represents a bounding box for a shape in the animation.
Expand Down Expand Up @@ -114,55 +106,29 @@ export function GetTextBoundingBox(
variables: Map<string, string>
): BoundingBox | undefined {
spritesCanvasContext.save();
let textInfo: RawTextDocument | undefined = undefined;
if (textData.d && textData.d.k && textData.d.k.length > 0) {
textInfo = textData.d.k[0].s as RawTextDocument;
}

if (!textInfo) {
spritesCanvasContext.restore();
return undefined;
}

const fontSize = textInfo.s;
const fontFamily = textInfo.f;
const finalFont = rawFonts.get(fontFamily);
if (!finalFont) {
const resolvedText = ResolveLottieText(textData, rawFonts, variables);
if (!resolvedText) {
spritesCanvasContext.restore();
return undefined;
}

const weight = finalFont.fWeight || "400"; // Default to normal weight if not specified
spritesCanvasContext.font = `${weight} ${fontSize}px ${finalFont.fFamily}`;

if (textInfo.sc !== undefined && textInfo.sc.length >= 3 && textInfo.sw !== undefined && textInfo.sw > 0) {
spritesCanvasContext.lineWidth = textInfo.sw;
}

// Text is supported as a possible variable (for localization for example)
// Check if the text is a variable and replace it if it is
let text = textInfo.t;
const variableText = variables.get(text);
if (variableText !== undefined) {
text = variableText;
}
const metrics = spritesCanvasContext.measureText(text);
ApplyLottieTextContext(spritesCanvasContext, resolvedText);

const widthPx = Math.ceil(metrics.width);
const heightPx = Math.ceil(metrics.actualBoundingBoxAscent) + Math.ceil(metrics.actualBoundingBoxDescent);
const layout = MeasureLottieText(resolvedText, (text) => spritesCanvasContext.measureText(text));

spritesCanvasContext.restore();

return {
width: widthPx,
height: heightPx,
centerX: widthPx / 2,
centerY: heightPx / 2,
offsetX: 0, // The bounding box calculated by the canvas for the text is always centered in (0, 0)
offsetY: 0, // The bounding box calculated by the canvas for the text is always centered in (0, 0)
width: layout.width,
height: layout.height,
centerX: layout.width / 2,
centerY: layout.height / 2,
offsetX: layout.offsetX,
offsetY: layout.offsetY,
strokeInset: 0, // Text bounding box ignores stroke padding here
actualBoundingBoxAscent: metrics.actualBoundingBoxAscent,
actualBoundingBoxDescent: metrics.actualBoundingBoxDescent,
actualBoundingBoxAscent: layout.baselineOffsetY,
actualBoundingBoxDescent: layout.descent,
};
Comment thread
VicenteCartas marked this conversation as resolved.
}

Expand Down
2 changes: 2 additions & 0 deletions packages/dev/lottiePlayer/src/parsing/rawTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@ export type RawTextDocumentKeyframe = {
};

export type RawTextDocument = {
sz?: number[]; // Paragraph box size [width, height]
ps?: number[]; // Paragraph box top-left position [x, y] relative to the text layer origin
f: string; // Font family
s: number; // Font size
lh: number; // Line height
Expand Down
58 changes: 20 additions & 38 deletions packages/dev/lottiePlayer/src/parsing/spritePacker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import {
type RawRectangleShape,
type RawStrokeShape,
type RawTextData,
type RawTextDocument,
} from "./rawTypes";
import { GetInitialScalarValue, GetInitialVectorValues, GetInitialBezierData } from "./rawPropertyHelpers";
import { ApplyLottieTextContext, DrawLottieText, MeasureLottieText, ResolveLottieText } from "./textLayout";

import { type BoundingBox, GetShapesBoundingBox, GetTextBoundingBox } from "../maths/boundingBox";

Expand Down Expand Up @@ -243,7 +243,7 @@ export class SpritePacker {
const page = this._getPageWithRoom(this._spriteAtlasInfo.cellWidth, this._spriteAtlasInfo.cellHeight);

// Draw the text in the canvas
this._drawText(textData, boundingBox, scalingFactor, page);
this._drawText(textData, scalingFactor, page);
this._extrudeSpriteEdges(page, page.currentX, page.currentY, this._spriteAtlasInfo.cellWidth, this._spriteAtlasInfo.cellHeight);
page.isDirty = true;

Expand Down Expand Up @@ -473,66 +473,48 @@ export class SpritePacker {
page.context.restore();
}

private _drawText(textData: RawTextData, boundingBox: BoundingBox, scalingFactor: IVector2Like, page: AtlasPage): void {
private _drawText(textData: RawTextData, scalingFactor: IVector2Like, page: AtlasPage): void {
if (this._rawFonts === undefined) {
return;
}

const textInfo = textData.d.k[0].s as RawTextDocument;

const fontFamily = textInfo.f;
const finalFont = this._rawFonts.get(fontFamily);
if (!finalFont) {
const resolvedText = ResolveLottieText(textData, this._rawFonts, this._variables);
if (!resolvedText) {
return;
}

page.context.save();
page.context.translate(page.currentX, page.currentY);
page.context.scale(scalingFactor.x, scalingFactor.y);

// Set up font (same setup as GetTextBoundingBox for measurement consistency)
const weight = finalFont.fWeight || "400";
page.context.font = `${weight} ${textInfo.s}px ${finalFont.fFamily}`;

if (textInfo.sc !== undefined && textInfo.sc.length >= 3 && textInfo.sw !== undefined && textInfo.sw > 0) {
page.context.lineWidth = textInfo.sw;
}

// Clip to cell bounds to prevent text overdraw into adjacent cells
page.context.beginPath();
page.context.rect(0, 0, boundingBox.width, boundingBox.height);
page.context.clip();

if (textInfo.fc !== undefined && textInfo.fc.length >= 3) {
const rawFillStyle = textInfo.fc;
// Resolve fill color, including the variable-name indirection (fc can be a string variable name).
if (resolvedText.textInfo.fc !== undefined && resolvedText.textInfo.fc.length >= 3) {
const rawFillStyle = resolvedText.textInfo.fc;
if (Array.isArray(rawFillStyle)) {
// If the fill style is an array, we assume it's a color array
page.context.fillStyle = this._lottieColorToCSSColor(rawFillStyle, 1);
} else {
// If it's a string, we need to get the value from the variables map
const variableFillStyle = this._variables.get(rawFillStyle);
Comment thread
VicenteCartas marked this conversation as resolved.
Outdated
if (variableFillStyle !== undefined) {
page.context.fillStyle = variableFillStyle;
}
}
}

if (textInfo.sc !== undefined && textInfo.sc.length >= 3 && textInfo.sw !== undefined && textInfo.sw > 0) {
page.context.strokeStyle = this._lottieColorToCSSColor(textInfo.sc, 1);
if (resolvedText.hasStroke) {
// ResolveLottieText only sets hasStroke when sc is present and well-formed, so the non-null assertion here is safe.
page.context.strokeStyle = this._lottieColorToCSSColor(resolvedText.textInfo.sc!, 1);
}

// Text is supported as a possible variable (for localization for example)
// Check if the text is a variable and replace it if it is
let text = textInfo.t;
const variableText = this._variables.get(text);
if (variableText !== undefined) {
text = variableText;
}
ApplyLottieTextContext(page.context, resolvedText);

page.context.fillText(text, 0, boundingBox.actualBoundingBoxAscent!);
if (textInfo.sc !== undefined && textInfo.sc.length >= 3 && textInfo.sw !== undefined && textInfo.sw > 0 && textInfo.of === true) {
page.context.strokeText(text, 0, boundingBox.actualBoundingBoxAscent!);
}
const layout = MeasureLottieText(resolvedText, (text) => page.context.measureText(text));

// Clip to cell bounds to prevent text overdraw into adjacent cells
page.context.beginPath();
page.context.rect(0, 0, layout.width, layout.height);
page.context.clip();

DrawLottieText(page.context, resolvedText, layout);

page.context.restore();
}
Expand Down
Loading
Loading