fix: use PSD text metrics for template rendering
This commit is contained in:
parent
eb6a0bf7dd
commit
449a631db4
@ -147,9 +147,22 @@ function buildInitialTemplateLayers(params: {
|
||||
fontId: matchedFont?.font.id,
|
||||
fontFamily: matchedFont?.font.family,
|
||||
fontLabel: matchedFont?.font.name,
|
||||
fontSize: Math.max(16, Math.min(layer.height, Math.round(layer.height * 0.78))),
|
||||
lineHeight: 1.2,
|
||||
letterSpacing: 0,
|
||||
fontSize: Math.max(
|
||||
10,
|
||||
Math.round(
|
||||
Number(
|
||||
layer.detectedFontSize ||
|
||||
Math.max(16, Math.min(layer.height, Math.round(layer.height * 0.78)))
|
||||
)
|
||||
)
|
||||
),
|
||||
lineHeight:
|
||||
Number.isFinite(Number(layer.detectedLineHeight)) && Number(layer.detectedLineHeight) > 0
|
||||
? Number(layer.detectedLineHeight)
|
||||
: 1.2,
|
||||
letterSpacing: Number.isFinite(Number(layer.detectedLetterSpacing))
|
||||
? Number(layer.detectedLetterSpacing)
|
||||
: 0,
|
||||
fontWeight: 500,
|
||||
textColor: "#111111",
|
||||
textAlign: "left",
|
||||
|
||||
@ -73,6 +73,9 @@ export interface ParsedPsdTemplateLayer {
|
||||
defaultText?: string;
|
||||
detectedFontName?: string;
|
||||
detectedFontNames?: string[];
|
||||
detectedFontSize?: number;
|
||||
detectedLineHeight?: number;
|
||||
detectedLetterSpacing?: number;
|
||||
}
|
||||
|
||||
export interface ParsedPsdTemplatePayload {
|
||||
@ -203,6 +206,38 @@ function extractDetectedFontNames(textProperties?: PsdTextPropertiesLike) {
|
||||
return normalizeDetectedFontNames(candidates);
|
||||
}
|
||||
|
||||
function extractTextStyleMetrics(textProperties?: PsdTextPropertiesLike) {
|
||||
const styleRuns = textProperties?.EngineDict?.StyleRun?.RunArray || [];
|
||||
const firstValidRun = styleRuns.find((styleRun) => {
|
||||
const style = styleRun?.StyleSheet?.StyleSheetData;
|
||||
return Number.isFinite(Number(style?.FontSize));
|
||||
});
|
||||
|
||||
const style = firstValidRun?.StyleSheet?.StyleSheetData;
|
||||
const fontSizeValue = Number(style?.FontSize);
|
||||
const leadingValue = Number(style?.Leading);
|
||||
const trackingValue = Number(style?.Tracking);
|
||||
|
||||
const detectedFontSize =
|
||||
Number.isFinite(fontSizeValue) && fontSizeValue > 0 ? fontSizeValue : undefined;
|
||||
const detectedLineHeight =
|
||||
detectedFontSize && Number.isFinite(leadingValue) && leadingValue > 0
|
||||
? Number((leadingValue / detectedFontSize).toFixed(3))
|
||||
: style?.AutoLeading
|
||||
? 1.2
|
||||
: undefined;
|
||||
const detectedLetterSpacing =
|
||||
detectedFontSize && Number.isFinite(trackingValue)
|
||||
? Number(((trackingValue / 1000) * detectedFontSize).toFixed(3))
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
detectedFontSize,
|
||||
detectedLineHeight,
|
||||
detectedLetterSpacing,
|
||||
};
|
||||
}
|
||||
|
||||
function collectPsdLayerEntries(
|
||||
nodes: PsdLayerLike[],
|
||||
ancestors: string[] = [],
|
||||
@ -246,6 +281,13 @@ function collectPsdLayerEntries(
|
||||
const detectedFontNames = defaultText
|
||||
? extractDetectedFontNames(node.textProperties)
|
||||
: [];
|
||||
const textStyleMetrics = defaultText
|
||||
? extractTextStyleMetrics(node.textProperties)
|
||||
: {
|
||||
detectedFontSize: undefined,
|
||||
detectedLineHeight: undefined,
|
||||
detectedLetterSpacing: undefined,
|
||||
};
|
||||
|
||||
result.push({
|
||||
node,
|
||||
@ -263,6 +305,9 @@ function collectPsdLayerEntries(
|
||||
defaultText: defaultText || undefined,
|
||||
detectedFontName: detectedFontNames[0] || undefined,
|
||||
detectedFontNames: detectedFontNames.length > 0 ? detectedFontNames : undefined,
|
||||
detectedFontSize: textStyleMetrics.detectedFontSize,
|
||||
detectedLineHeight: textStyleMetrics.detectedLineHeight,
|
||||
detectedLetterSpacing: textStyleMetrics.detectedLetterSpacing,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -231,17 +231,27 @@ async function createTextOverlayBuffer(params: {
|
||||
maxLines,
|
||||
} = params;
|
||||
|
||||
const normalizedText = String(text || "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
||||
const explicitLineCount = Math.max(1, normalizedText.split("\n").length);
|
||||
const availableHeight = Math.max(8, height - paddingY * 2);
|
||||
const adjustedFontSize =
|
||||
explicitLineCount > 1 && fontSize * Math.max(1, lineHeight) * explicitLineCount > availableHeight * 1.18
|
||||
? Math.max(
|
||||
10,
|
||||
Math.floor((availableHeight / (explicitLineCount * Math.max(1, lineHeight))) * 100) / 100
|
||||
)
|
||||
: fontSize;
|
||||
const availableWidth = Math.max(8, width - paddingX * 2);
|
||||
const lineHeightPx = Math.max(fontSize, fontSize * lineHeight);
|
||||
const lineHeightPx = Math.max(adjustedFontSize, adjustedFontSize * lineHeight);
|
||||
const heightLimitedMaxLines = Math.max(
|
||||
1,
|
||||
Math.floor((height - paddingY * 2 + fontSize * 0.2) / lineHeightPx)
|
||||
Math.floor((height - paddingY * 2 + adjustedFontSize * 0.2) / lineHeightPx)
|
||||
);
|
||||
const resolvedMaxLines = Math.max(1, Math.min(maxLines, heightLimitedMaxLines));
|
||||
const lines = wrapTextByWidth({
|
||||
text,
|
||||
text: normalizedText,
|
||||
maxWidth: availableWidth,
|
||||
fontSize,
|
||||
fontSize: adjustedFontSize,
|
||||
letterSpacing,
|
||||
maxLines: resolvedMaxLines,
|
||||
});
|
||||
@ -266,7 +276,8 @@ async function createTextOverlayBuffer(params: {
|
||||
const tspans = lines
|
||||
.map((line, index) => {
|
||||
const y = paddingY + fontSize + index * lineHeightPx;
|
||||
return `<tspan x="${x}" y="${y}">${escapeXml(line)}</tspan>`;
|
||||
const baselineY = paddingY + adjustedFontSize + index * lineHeightPx;
|
||||
return `<tspan x="${x}" y="${baselineY}">${escapeXml(line)}</tspan>`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
@ -275,7 +286,7 @@ async function createTextOverlayBuffer(params: {
|
||||
<defs>
|
||||
<style>
|
||||
${fontFaceCss}
|
||||
.text { font-family: "${escapedFamily}", sans-serif; font-size: ${fontSize}px; font-weight: ${fontWeight}; fill: ${textColor}; letter-spacing: ${letterSpacing}px; }
|
||||
.text { font-family: "${escapedFamily}", sans-serif; font-size: ${adjustedFontSize}px; font-weight: ${fontWeight}; fill: ${textColor}; letter-spacing: ${letterSpacing}px; }
|
||||
</style>
|
||||
<clipPath id="${clipId}">
|
||||
<rect x="0" y="0" width="${width}" height="${height}" />
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user