Files
image-studio/renderer/text-asset.ts
syntaxbullet 5915c62a9a refactor: Update bottom controls for improved styling and functionality
- Adjusted button styles across various components for consistency and better UX.
- Enhanced layout of action controls to utilize whitespace more effectively.
- Updated slider styles for a more modern appearance and improved usability.
- Refined input fields and labels for better accessibility and readability.
- Introduced new app surface styles for a cohesive design across the application.
- Added tests for canvas cursor behavior to ensure correct cursor display during operations.
2026-07-11 14:55:32 +02:00

48 lines
2.8 KiB
TypeScript

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) => `<tspan x="${x}" y="${(index + 0.82) * layer.style.fontSize * layer.style.lineHeight}">${escapeXml(line || " ")}</tspan>`).join("");
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size.w}" height="${size.h}" viewBox="0 0 ${size.w} ${size.h}"><text text-anchor="${anchor}" font-family="${family}" font-size="${layer.style.fontSize}" font-weight="${layer.style.fontWeight}" font-style="${layer.style.fontStyle}" fill="${layer.style.color}">${tspans}</text></svg>`;
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) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&apos;" })[character]!);
}