fix(producer): dedupe local font embedding by resolved path (#2317)

This commit is contained in:
Miguel Ángel
2026-07-13 02:01:28 -04:00
committed by GitHub
parent 796d5df156
commit 9940503102
2 changed files with 48 additions and 8 deletions
@@ -4,6 +4,7 @@ import { mkdtempSync, writeFileSync, mkdirSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { parseHTML } from "linkedom";
import { defaultLogger } from "../logger.js";
import {
collectExternalAssets,
compileForRender,
@@ -858,6 +859,40 @@ describe("system-primary font normalization", () => {
});
});
describe("local font embedding", () => {
it("embeds one font file once when sub-compositions use equivalent relative paths", async () => {
const projectDir = mkdtempSync(join(tmpdir(), "hf-local-font-dedupe-"));
const assetsDir = join(projectDir, "assets");
mkdirSync(assetsDir, { recursive: true });
writeFileSync(join(assetsDir, "shared.woff2"), "fake-woff2");
writeFileSync(
join(projectDir, "index.html"),
`<!DOCTYPE html>
<html><head><style>
@font-face { font-family: "A"; src: url("assets/shared.woff2"); }
@font-face { font-family: "B"; src: url("./assets/shared.woff2"); }
@font-face { font-family: "C"; src: url("assets/../assets/shared.woff2"); }
</style></head><body>
<div data-composition-id="root" data-width="640" data-height="360" data-duration="1">Text</div>
</body></html>`,
);
const originalInfo = defaultLogger.info;
const embeddedMessages: string[] = [];
defaultLogger.info = (message) => {
if (message.includes("Embedded local font file")) embeddedMessages.push(message);
};
try {
await compileForRender(projectDir, join(projectDir, "index.html"), projectDir);
} finally {
defaultLogger.info = originalInfo;
}
expect(embeddedMessages).toHaveLength(1);
});
});
describe("template-wrapped sub-composition media offsets", () => {
function writeTemplateWrappedProject(
hostAttrs: string,
+13 -8
View File
@@ -1605,7 +1605,8 @@ async function embedLocalFontFaces(html: string, projectDir: string): Promise<st
const styleBlockRe = /<style\b[^>]*>([\s\S]*?)<\/style>/gi;
const fontFaceRe = /@font-face\s*\{([^}]*)\}/gi;
let result = html;
const embedded = new Set<string>();
const embeddedPaths = new Set<string>();
const dataUriByAbsolutePath = new Map<string, string>();
let styleMatch: RegExpExecArray | null;
while ((styleMatch = styleBlockRe.exec(html)) !== null) {
@@ -1618,19 +1619,23 @@ async function embedLocalFontFaces(html: string, projectDir: string): Promise<st
let urlMatch: RegExpExecArray | null;
while ((urlMatch = urlRe.exec(block)) !== null) {
const localPath = urlMatch[1];
if (!localPath || embedded.has(localPath)) continue;
if (!localPath || embeddedPaths.has(localPath)) continue;
const absPath = localPath.startsWith("/") ? localPath : resolve(projectDir, localPath);
if (!isPathInside(absPath, projectDir)) continue;
if (!existsSync(absPath)) continue;
const ext = absPath.match(/\.(woff2?|ttf|otf|ttc)$/i)?.[1]?.toLowerCase() ?? "ttf";
try {
const buffer = readFileSync(absPath);
const dataUri = await toDataUri(buffer, ext);
let dataUri = dataUriByAbsolutePath.get(absPath);
if (!dataUri) {
const buffer = readFileSync(absPath);
dataUri = await toDataUri(buffer, ext);
dataUriByAbsolutePath.set(absPath, dataUri);
defaultLogger.info(
`[Compiler] Embedded local font file: ${localPath} (${(buffer.length / 1024).toFixed(0)} KB → data URI)`,
);
}
result = result.replaceAll(localPath, dataUri);
embedded.add(localPath);
defaultLogger.info(
`[Compiler] Embedded local font file: ${localPath} (${(buffer.length / 1024).toFixed(0)} KB → data URI)`,
);
embeddedPaths.add(localPath);
} catch {
// File read or compression failed — keep the original path
}