mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
fix(producer): dedupe local font embedding by resolved path (#2317)
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user