import type { Asset } from "@core/asset"; import type { TextLayer } from "@core/text-layer"; import { measureTextLayer } from "@core/text-layer"; /** Produces a derived SVG texture; text content remains the only authoritative state. */ export function textLayerRenderAsset(layer: TextLayer): Asset { const size = measureTextLayer(layer); const lines = layer.content.split("\n"); const anchor = layer.style.alignment === "left" ? "start" : layer.style.alignment === "center" ? "middle" : "end"; const x = layer.style.alignment === "left" ? 0 : layer.style.alignment === "center" ? size.w / 2 : size.w; const family = escapeXml(layer.style.fontFamily); const tspans = lines.map((line, index) => `${escapeXml(line || " ")}`).join(""); const svg = `${tspans}`; return { id: `text-render:${layer.id}`, name: layer.name, mimeType: "image/svg+xml", source: `data:image/svg+xml,${encodeURIComponent(svg)}`, intrinsicSize: size }; } /** Rasterizes authoritative text for the live WebGL renderer without relying on browser SVG text decoding. */ export function rasterizedTextLayerRenderAsset(layer: TextLayer): Asset { const size = measureTextLayer(layer); const canvas = document.createElement("canvas"); canvas.width = Math.max(1, Math.ceil(size.w)); canvas.height = Math.max(1, Math.ceil(size.h)); const context = canvas.getContext("2d"); if (!context) throw new Error("Failed to create text rasterization context"); context.clearRect(0, 0, canvas.width, canvas.height); context.fillStyle = layer.style.color; context.font = `${layer.style.fontStyle} ${layer.style.fontWeight} ${layer.style.fontSize}px ${JSON.stringify(layer.style.fontFamily)}, sans-serif`; context.textAlign = layer.style.alignment; context.textBaseline = "alphabetic"; const x = layer.style.alignment === "left" ? 0 : layer.style.alignment === "center" ? size.w / 2 : size.w; for (const [index, line] of layer.content.split("\n").entries()) { context.fillText(line || " ", x, (index + 0.82) * layer.style.fontSize * layer.style.lineHeight); } return { id: `text-render:${layer.id}`, name: layer.name, mimeType: "image/png", source: canvas.toDataURL("image/png"), intrinsicSize: size, }; } function escapeXml(value: string) { return value.replace(/[&<>"']/g, (character) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[character]!); }