fix: use exact uploaded font face for psd export
This commit is contained in:
parent
c8ac5584c3
commit
43083ff90c
@ -10,7 +10,13 @@ import type { AgentPsdFont } from "@/lib/agents/psd-font-config";
|
||||
const execFileAsync = promisify(execFile);
|
||||
const ensuredFontCache = new Map<
|
||||
string,
|
||||
Promise<{ fontFamily: string; installedPath?: string }>
|
||||
Promise<{
|
||||
fontFamily: string;
|
||||
fontFullName?: string;
|
||||
fontPostscriptName?: string;
|
||||
fontStyle?: string;
|
||||
installedPath?: string;
|
||||
}>
|
||||
>();
|
||||
|
||||
function sanitizeFileSegment(value: string, fallback: string) {
|
||||
@ -50,7 +56,7 @@ function getRuntimeFontDirectory() {
|
||||
return path.join(process.cwd(), ".next", "cache", "psd-fonts");
|
||||
}
|
||||
|
||||
function parseFontconfigFamily(stdout: string) {
|
||||
function parseFontconfigToken(stdout: string) {
|
||||
const firstLine = String(stdout || "")
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
@ -66,6 +72,22 @@ function parseFontconfigFamily(stdout: string) {
|
||||
.find(Boolean) || "";
|
||||
}
|
||||
|
||||
function parseFontScanMetadata(stdout: string) {
|
||||
const [fullName = "", family = "", style = "", postscriptName = ""] = String(stdout || "")
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.find(Boolean)
|
||||
?.split("|")
|
||||
.map((item) => item.trim()) || [];
|
||||
|
||||
return {
|
||||
fontFullName: fullName || undefined,
|
||||
fontFamily: family || fullName || "",
|
||||
fontStyle: style || undefined,
|
||||
fontPostscriptName: postscriptName || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async function ensureWritableFontFile(targetPath: string, fontBuffer: Buffer) {
|
||||
const existingFile = await fs.stat(targetPath).catch(() => null);
|
||||
if (existingFile && existingFile.size === fontBuffer.byteLength) {
|
||||
@ -82,12 +104,34 @@ async function detectInstalledFontFamily(targetPath: string, fallbackFamily: str
|
||||
["--format=%{family}\n", targetPath],
|
||||
{ timeout: 15_000 }
|
||||
);
|
||||
return parseFontconfigFamily(stdout) || fallbackFamily;
|
||||
return parseFontconfigToken(stdout) || fallbackFamily;
|
||||
} catch {
|
||||
return fallbackFamily;
|
||||
}
|
||||
}
|
||||
|
||||
async function detectInstalledFontMetadata(targetPath: string, fallbackFamily: string) {
|
||||
try {
|
||||
const { stdout } = await execFileAsync(
|
||||
"fc-scan",
|
||||
["--format=%{fullname[0]}|%{family[0]}|%{style[0]}|%{postscriptname}\n", targetPath],
|
||||
{ timeout: 15_000 }
|
||||
);
|
||||
const metadata = parseFontScanMetadata(stdout);
|
||||
return {
|
||||
...metadata,
|
||||
fontFamily: metadata.fontFamily || fallbackFamily,
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
fontFamily: await detectInstalledFontFamily(targetPath, fallbackFamily),
|
||||
fontFullName: undefined,
|
||||
fontStyle: undefined,
|
||||
fontPostscriptName: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshFontCache(fontDirectory: string) {
|
||||
try {
|
||||
await execFileAsync("fc-cache", ["-f", fontDirectory], { timeout: 20_000 });
|
||||
@ -122,9 +166,10 @@ export async function ensurePsdFontAvailableForRender(params: {
|
||||
|
||||
await ensureWritableFontFile(targetPath, fontBuffer);
|
||||
await refreshFontCache(fontDirectory);
|
||||
const metadata = await detectInstalledFontMetadata(targetPath, fallbackFamily);
|
||||
|
||||
return {
|
||||
fontFamily: await detectInstalledFontFamily(targetPath, fallbackFamily),
|
||||
...metadata,
|
||||
installedPath: targetPath,
|
||||
};
|
||||
})()
|
||||
|
||||
@ -187,7 +187,9 @@ async function createTextOverlayBuffer(params: {
|
||||
fontBuffer?: Buffer;
|
||||
font: AgentPsdFont | undefined;
|
||||
fontFamily: string;
|
||||
renderFontFamilies?: string[];
|
||||
renderFontFamily?: string;
|
||||
useExactFontFace?: boolean;
|
||||
fontSize: number;
|
||||
fontWeight: number;
|
||||
lineHeight: number;
|
||||
@ -205,7 +207,9 @@ async function createTextOverlayBuffer(params: {
|
||||
fontBuffer,
|
||||
font,
|
||||
fontFamily,
|
||||
renderFontFamilies,
|
||||
renderFontFamily,
|
||||
useExactFontFace,
|
||||
fontSize,
|
||||
fontWeight,
|
||||
lineHeight,
|
||||
@ -239,7 +243,7 @@ async function createTextOverlayBuffer(params: {
|
||||
|
||||
const primaryFamilies = Array.from(
|
||||
new Set(
|
||||
[renderFontFamily, fontFamily]
|
||||
[...(renderFontFamilies || []), renderFontFamily, fontFamily]
|
||||
.map((family) => String(family || "").trim())
|
||||
.filter(Boolean)
|
||||
)
|
||||
@ -275,7 +279,7 @@ async function createTextOverlayBuffer(params: {
|
||||
<defs>
|
||||
<style>
|
||||
${fontFaceCss}
|
||||
.text { font-family: ${fontFamilyStack}; font-size: ${adjustedFontSize}px; font-weight: ${fontWeight}; fill: ${textColor}; letter-spacing: ${letterSpacing}px; }
|
||||
.text { font-family: ${fontFamilyStack}; font-size: ${adjustedFontSize}px; font-weight: ${useExactFontFace ? "normal" : fontWeight}; fill: ${textColor}; letter-spacing: ${letterSpacing}px; }
|
||||
</style>
|
||||
<clipPath id="${clipId}">
|
||||
<rect x="0" y="0" width="${width}" height="${height}" />
|
||||
@ -432,7 +436,15 @@ export async function renderAgentPsdTemplate(params: {
|
||||
fontBuffer,
|
||||
font,
|
||||
fontFamily: layer.fontFamily || font?.family || "sans-serif",
|
||||
renderFontFamilies: runtimeFont
|
||||
? [
|
||||
runtimeFont.fontPostscriptName,
|
||||
runtimeFont.fontFullName,
|
||||
runtimeFont.fontFamily,
|
||||
].filter((value): value is string => Boolean(String(value || "").trim()))
|
||||
: undefined,
|
||||
renderFontFamily: runtimeFont?.fontFamily,
|
||||
useExactFontFace: Boolean(runtimeFont?.fontPostscriptName || runtimeFont?.fontFullName),
|
||||
fontSize: layer.fontSize,
|
||||
fontWeight: layer.fontWeight,
|
||||
lineHeight: layer.lineHeight,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user