diff options
Diffstat (limited to 'apps/web/src/lib/text/layout.ts')
| -rw-r--r-- | apps/web/src/lib/text/layout.ts | 226 |
1 files changed, 0 insertions, 226 deletions
diff --git a/apps/web/src/lib/text/layout.ts b/apps/web/src/lib/text/layout.ts deleted file mode 100644 index a3badfdc..00000000 --- a/apps/web/src/lib/text/layout.ts +++ /dev/null @@ -1,226 +0,0 @@ -import type { TextBackground, TextElement } from "@/lib/timeline"; -import { DEFAULTS } from "@/lib/timeline/defaults"; - -type TextRect = { - left: number; - top: number; - width: number; - height: number; -}; - -export interface TextBlockMeasurement { - visualCenterOffset: number; - height: number; - maxWidth: number; -} - -export type TextCanvasContext = - | CanvasRenderingContext2D - | OffscreenCanvasRenderingContext2D; - -const TEXT_DECORATION_THICKNESS_RATIO = 0.07; -const STRIKETHROUGH_VERTICAL_RATIO = 0.35; - -export function setCanvasLetterSpacing({ - ctx, - letterSpacingPx, -}: { - ctx: TextCanvasContext; - letterSpacingPx: number; -}): void { - if ("letterSpacing" in ctx) { - (ctx as CanvasRenderingContext2D & { letterSpacing: string }).letterSpacing = - `${letterSpacingPx}px`; - } -} - -export function getMetricAscent({ - metrics, - fallbackFontSize, -}: { - metrics: TextMetrics; - fallbackFontSize: number; -}): number { - return metrics.actualBoundingBoxAscent ?? fallbackFontSize * 0.8; -} - -export function getMetricDescent({ - metrics, - fallbackFontSize, -}: { - metrics: TextMetrics; - fallbackFontSize: number; -}): number { - return metrics.actualBoundingBoxDescent ?? fallbackFontSize * 0.2; -} - -export function measureTextBlock({ - lineMetrics, - lineHeightPx, -}: { - lineMetrics: TextMetrics[]; - lineHeightPx: number; -}): TextBlockMeasurement { - let maxWidth = 0; - - for (const metrics of lineMetrics) { - maxWidth = Math.max(maxWidth, metrics.width); - } - - const lineCount = lineMetrics.length; - const height = lineCount * lineHeightPx; - const visualCenterOffset = ((lineCount - 1) * lineHeightPx) / 2; - - return { visualCenterOffset, height, maxWidth }; -} - -function getTextRect({ - textAlign, - block, -}: { - textAlign: TextElement["textAlign"]; - block: TextBlockMeasurement; -}): TextRect { - const textAlignToLeft: Record<typeof textAlign, number> = { - left: 0, - right: -block.maxWidth, - center: -block.maxWidth / 2, - }; - const left = textAlignToLeft[textAlign]; - - return { - left, - top: -block.height / 2, - width: block.maxWidth, - height: block.height, - }; -} - -function isTextBackgroundVisible({ - background, -}: { - background: TextBackground; -}): boolean { - return ( - background.enabled && - Boolean(background.color) && - background.color !== "transparent" - ); -} - -export function getTextBackgroundRect({ - textAlign, - block, - background, - fontSizeRatio = 1, -}: { - textAlign: TextElement["textAlign"]; - block: TextBlockMeasurement; - background: TextBackground; - fontSizeRatio?: number; -}): TextRect | null { - if (!isTextBackgroundVisible({ background })) { - return null; - } - - const textRect = getTextRect({ textAlign, block }); - const paddingX = - (background.paddingX ?? DEFAULTS.text.background.paddingX) * fontSizeRatio; - const paddingY = - (background.paddingY ?? DEFAULTS.text.background.paddingY) * fontSizeRatio; - const offsetX = background.offsetX ?? DEFAULTS.text.background.offsetX; - const offsetY = background.offsetY ?? DEFAULTS.text.background.offsetY; - - return { - left: textRect.left - paddingX + offsetX, - top: textRect.top - paddingY + offsetY, - width: textRect.width + paddingX * 2, - height: textRect.height + paddingY * 2, - }; -} - -export function getTextVisualRect({ - textAlign, - block, - background, - fontSizeRatio = 1, -}: { - textAlign: TextElement["textAlign"]; - block: TextBlockMeasurement; - background: TextBackground; - fontSizeRatio?: number; -}): TextRect { - const textRect = getTextRect({ textAlign, block }); - const backgroundRect = getTextBackgroundRect({ - textAlign, - block, - background, - fontSizeRatio, - }); - - if (!backgroundRect) { - return textRect; - } - - const left = Math.min(textRect.left, backgroundRect.left); - const top = Math.min(textRect.top, backgroundRect.top); - const right = Math.max( - textRect.left + textRect.width, - backgroundRect.left + backgroundRect.width, - ); - const bottom = Math.max( - textRect.top + textRect.height, - backgroundRect.top + backgroundRect.height, - ); - - return { - left, - top, - width: right - left, - height: bottom - top, - }; -} - -export function drawTextDecoration({ - ctx, - textDecoration, - lineWidth, - lineY, - metrics, - scaledFontSize, - textAlign, -}: { - ctx: TextCanvasContext; - textDecoration: string; - lineWidth: number; - lineY: number; - metrics: TextMetrics; - scaledFontSize: number; - textAlign: CanvasTextAlign; -}): void { - if (textDecoration === "none" || !textDecoration) return; - - const thickness = Math.max( - 1, - scaledFontSize * TEXT_DECORATION_THICKNESS_RATIO, - ); - const ascent = getMetricAscent({ metrics, fallbackFontSize: scaledFontSize }); - const descent = getMetricDescent({ - metrics, - fallbackFontSize: scaledFontSize, - }); - - let xStart = -lineWidth / 2; - if (textAlign === "left") xStart = 0; - if (textAlign === "right") xStart = -lineWidth; - - if (textDecoration === "underline") { - const underlineY = lineY + descent + thickness; - ctx.fillRect(xStart, underlineY, lineWidth, thickness); - } - - if (textDecoration === "line-through") { - const strikeY = lineY - (ascent - descent) * STRIKETHROUGH_VERTICAL_RATIO; - ctx.fillRect(xStart, strikeY, lineWidth, thickness); - } -} |
