ai-chatbot/lib/agents/psd-font-matching.ts
Your Name 9b745e5591
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
fix: improve psd text font recovery
2026-04-10 20:19:21 +08:00

130 lines
3.5 KiB
TypeScript

import "server-only";
import type { AgentPsdFont } from "@/lib/agents/psd-font-config";
function normalizeFontToken(value: string) {
return String(value || "")
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, "");
}
function isIgnoredFontName(value: string) {
const normalized = normalizeFontToken(value);
return (
!normalized ||
normalized === "adobeinvisfont" ||
normalized.startsWith("adobeheiti") ||
normalized.startsWith("adobesong") ||
normalized.startsWith("adobeming")
);
}
export function normalizeDetectedFontNames(value: unknown) {
if (!Array.isArray(value)) return [] as string[];
return Array.from(
new Set(
value
.map((item) => String(item || "").trim())
.filter((item) => item && !isIgnoredFontName(item))
)
);
}
function getPsdFontIdentityTokens(font: Pick<AgentPsdFont, "name" | "family">) {
return Array.from(
new Set(
[font.name, font.family]
.map((item) => normalizeFontToken(String(item || "")))
.filter(Boolean)
)
);
}
export function doesPsdFontMatchDetectedNames(params: {
detectedFontNames: string[];
font: Pick<AgentPsdFont, "name" | "family">;
}) {
const detectedFontNames = normalizeDetectedFontNames(params.detectedFontNames);
if (detectedFontNames.length === 0) {
return true;
}
const fontTokens = new Set(getPsdFontIdentityTokens(params.font));
return detectedFontNames.some((detectedFontName) =>
fontTokens.has(normalizeFontToken(detectedFontName))
);
}
export function findBestMatchingPsdFont(params: {
detectedFontNames: string[];
fonts: AgentPsdFont[];
}) {
const detectedFontNames = normalizeDetectedFontNames(params.detectedFontNames);
if (detectedFontNames.length === 0 || params.fonts.length === 0) {
return null;
}
const exactMap = new Map<string, AgentPsdFont>();
const normalizedMap = new Map<string, AgentPsdFont>();
for (const font of params.fonts) {
for (const candidate of [font.name, font.family]) {
const raw = String(candidate || "").trim();
if (!raw) continue;
exactMap.set(raw.toLowerCase(), font);
normalizedMap.set(normalizeFontToken(raw), font);
}
}
for (const detectedFontName of detectedFontNames) {
const exactMatch = exactMap.get(detectedFontName.toLowerCase());
if (exactMatch) {
return {
font: exactMatch,
detectedFontName,
confidence: "high" as const,
};
}
}
for (const detectedFontName of detectedFontNames) {
const normalizedDetected = normalizeFontToken(detectedFontName);
const normalizedMatch = normalizedMap.get(normalizedDetected);
if (normalizedMatch) {
return {
font: normalizedMatch,
detectedFontName,
confidence: "high" as const,
};
}
}
for (const detectedFontName of detectedFontNames) {
const normalizedDetected = normalizeFontToken(detectedFontName);
if (!normalizedDetected) continue;
const containsMatch = params.fonts.find((font) => {
const fontName = normalizeFontToken(font.name);
const fontFamily = normalizeFontToken(font.family);
return (
fontName.includes(normalizedDetected) ||
fontFamily.includes(normalizedDetected) ||
normalizedDetected.includes(fontName) ||
normalizedDetected.includes(fontFamily)
);
});
if (containsMatch) {
return {
font: containsMatch,
detectedFontName,
confidence: "medium" as const,
};
}
}
return null;
}