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("字体元数据与嵌入字形不匹配"); }, 15_000); it("没有字体时保持原始 DOCX 字节不变", () => { const input = createTestBaselineReference(); const result = embedFontsInGeneratedDocx(input, []); expect(result.content).toBe(input); expect(result.report.embeddedFaceCount).toBe(0); }); });