import { readFile } from "node:fs/promises";
import { describe, expect, it } from "vitest";
import { prepareDocxFont, xorDocxFont } from "../src/font-embedding.js";
import { embedFontsInGeneratedDocx } from "../src/font-package-transform.js";
import {
readGeneratedDocxPackage,
writeGeneratedDocxPackage
} from "../src/reference-package.js";
import { validateGeneratedDocx } from "../src/validator.js";
import { createTestBaselineReference } from "./reference-test-fixture.js";
const decoder = new TextDecoder();
const encoder = new TextEncoder();
const wordNamespace =
"http://schemas.openxmlformats.org/wordprocessingml/2006/main";
const relationshipNamespace =
"http://schemas.openxmlformats.org/officeDocument/2006/relationships";
const fandolFangUrl = new URL(
"../../../themes/_shared/official-fonts/FandolFang-Regular.woff2",
import.meta.url
);
describe("DOCX 字体包写入", () => {
it("写入 ODTTF、字体关系和内容类型并替换逻辑字体名", async () => {
const source = readGeneratedDocxPackage(
createTestBaselineReference()
);
const entries = new Map(source.entries);
const originalStyles = decoder.decode(
entries.get("word/styles.xml")
);
entries.set(
"word/styles.xml",
encoder.encode(
originalStyles.replace(
"",
''
).replace(
"",
''
)
)
);
entries.set(
"word/document.xml",
encoder.encode(
decoder
.decode(entries.get("word/document.xml"))
.replace(
"",
''
)
)
);
const input = writeGeneratedDocxPackage(entries);
const font = await prepareDocxFont({
family: "Mdpdf Fandol Fang",
aliases: ["FangSong", "STFangsong"],
source:
"theme-shared:official-fonts/FandolFang-Regular.woff2",
weight: 400,
style: "normal",
license: "GPL-3.0-with-font-exception",
content: await readFile(fandolFangUrl)
});
const result = embedFontsInGeneratedDocx(input, [font]);
const output = readGeneratedDocxPackage(result.content);
const fontParts = [...output.entries.keys()].filter((name) =>
/^word\/fonts\/.+\.odttf$/u.test(name)
);
expect(result.report).toMatchObject({
embeddedFaceCount: 1,
embeddedFamilyCount: 1,
replacedFontReferenceCount: 3,
totalSfntBytes: font.sfnt.byteLength
});
expect(fontParts).toHaveLength(1);
const contentTypes = decoder.decode(
output.entries.get("[Content_Types].xml")
);
expect(contentTypes).toContain(
'Extension="odttf" ContentType="application/vnd.openxmlformats-officedocument.obfuscatedFont"'
);
const relationships = decoder.decode(
output.entries.get("word/_rels/fontTable.xml.rels")
);
expect(relationships).toContain(
'Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/font"'
);
expect(relationships).toContain(
`Target="${fontParts[0]!.replace(/^word\//u, "")}"`
);
const fontTable = decoder.decode(
output.entries.get("word/fontTable.xml")
);
expect(fontTable).toContain('w:name="FandolFang"');
expect(fontTable).toContain(
''
);
expect(fontTable).toContain('');
expect(fontTable).toContain('');
expect(fontTable).toContain('');
expect(fontTable).toContain(
''
);
expect(fontTable).toContain("',
''
)
)
);
expect(() =>
validateGeneratedDocx(
writeGeneratedDocxPackage(invalidEntries)
)
).toThrow("字体元数据与嵌入字形不匹配");
});
it("没有字体时保持原始 DOCX 字节不变", () => {
const input = createTestBaselineReference();
const result = embedFontsInGeneratedDocx(input, []);
expect(result.content).toBe(input);
expect(result.report.embeddedFaceCount).toBe(0);
});
});