feat: 完成 DOCX R4 元素级视觉门禁

建立 Chromium、Word 与 WPS 的可编辑内容、段落几何、页眉页脚、封面、媒体和逐页视觉比较链路。

支持封面独立分节、正文页码重启、主题页边距与横向纸张,并将自然分页差异降为诊断项。

修正代码块内边距和折叠表格单元格边框映射,14 套主题生产矩阵与六组布局视觉矩阵通过。
This commit is contained in:
SkyJourney
2026-08-02 03:27:11 +08:00
parent f9f5fccfc9
commit 58f87cc19f
100 changed files with 10409 additions and 386 deletions
@@ -186,13 +186,25 @@ const htmlRoles: Readonly<Record<SemanticDocumentRole, HtmlRole>> = {
}
};
function renderNode(node: SemanticDocumentNode): string {
function renderNode(
node: SemanticDocumentNode,
options: {
regionKind?: "cover";
breakAfter?: "next-page";
} = {}
): string {
const role = htmlRoles[node.role];
const content =
node.kind === "group"
? node.children.map(renderNode).join("")
? node.children.map((child) => renderNode(child)).join("")
: `${node.label ? `<span>${escapeHtml(node.label)}</span>` : ""}${escapeHtml(node.text)}`;
return `<${role.tagName} class="${role.className}">${content}</${role.tagName}>`;
const regionAttribute = options.regionKind
? ` data-semantic-region="${options.regionKind}"`
: "";
const breakAttribute = options.breakAfter
? ` data-semantic-break-after="${options.breakAfter}"`
: "";
return `<${role.tagName} class="${role.className}"${regionAttribute}${breakAttribute}>${content}</${role.tagName}>`;
}
export function renderDocumentStructure(
@@ -206,13 +218,24 @@ export function renderDocumentStructure(
(region) =>
region.kind === "cover" || region.kind === "prefix"
)
.flatMap((region) => region.nodes)
.map(renderNode)
.flatMap((region) =>
region.nodes.map((node, index) =>
renderNode(node, {
...(region.kind === "cover"
? { regionKind: "cover" as const }
: {}),
...(index === region.nodes.length - 1 &&
region.section?.breakAfter
? { breakAfter: region.section.breakAfter }
: {})
})
)
)
.join("");
const suffixHtml = model.regions
.filter((region) => region.kind === "suffix")
.flatMap((region) => region.nodes)
.map(renderNode)
.map((node) => renderNode(node))
.join("");
return {
profile: model.profile,
@@ -199,6 +199,12 @@ document:
expect(projectReport.articleHtml).toContain(
'class="doc-cover doc-cover-project-report"'
);
expect(projectReport.articleHtml).toContain(
'data-semantic-region="cover"'
);
expect(projectReport.articleHtml).toContain(
'data-semantic-break-after="next-page"'
);
expect(tender.articleHtml).toContain(
'class="doc-cover doc-cover-tender"'
);