feat: 完成 DOCX 通用结构映射与严格收口

This commit is contained in:
SkyJourney
2026-07-31 15:19:34 +08:00
parent 6e40374200
commit f3847f3e4b
31 changed files with 3175 additions and 64 deletions
@@ -229,7 +229,8 @@ describe("动态 reference.docx", () => {
fontSizePt: 14,
color: "#112233",
lineSpacing: 1.8,
firstLineIndentPt: 28
firstLineIndentPt: 28,
alignment: "justify"
}
},
{
@@ -297,7 +298,9 @@ describe("动态 reference.docx", () => {
expect(stylesXml).toContain('w:val="112233"');
expect(stylesXml).toContain('w:styleId="Heading1"');
expect(stylesXml).toContain('w:val="AA0000"');
expect(stylesXml).toContain('w:w="5000" w:type="pct"');
expect(stylesXml).toContain('<w:jc w:val="both"/>');
expect(stylesXml).not.toContain('w:val="justify"');
expect(stylesXml).not.toContain("<w:tblW");
expect(stylesXml).toContain('w:color="445566"');
expect(fontTableXml).toContain(
'w:name="Source Han Serif SC"'
@@ -435,6 +438,48 @@ describe("动态 reference.docx", () => {
);
});
it("拒绝重复样式 ID 和非法两端对齐枚举", () => {
const result = createDynamicReferenceDocx(
createBaselineReference(),
createOptions(defaultExportConfig)
);
const entries = new Map(
Object.entries(unzipSync(result.content))
);
const styles = decoder.decode(entries.get("word/styles.xml")!);
entries.set(
"word/styles.xml",
encoder.encode(
styles.replace(
"</w:styles>",
'<w:style w:type="paragraph" w:styleId="Normal"/>' +
"</w:styles>"
)
)
);
expect(() =>
validateDynamicReferenceDocx(
writeReferenceDocxPackage(entries)
)
).toThrow(/ IDNormal/u);
entries.set(
"word/styles.xml",
encoder.encode(
styles.replace(
/<w:jc w:val="[^"]+"\/>/u,
'<w:jc w:val="justify"/>'
)
)
);
expect(() =>
validateDynamicReferenceDocx(
writeReferenceDocxPackage(entries)
)
).toThrow(/使/u);
});
it("最终 DOCX 使用媒体输出上限而非模板的 2 MiB 上限", () => {
const result = createDynamicReferenceDocx(
createBaselineReference(),