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
@@ -11,6 +11,7 @@ import {
PandocRuntime,
resolveReferencePageOptions
} from "@md-to-pdf/docx-engine";
import { DOCX_STYLE_SLOT_NAMES } from "@md-to-pdf/docx-theme-engine";
import { renderMarkdown } from "@md-to-pdf/renderer";
import { unzipSync } from "fflate";
@@ -185,10 +186,17 @@ function inspectDocument(
/<w:sectPr(?:\s[^>]*)?>[\s\S]*?<\/w:sectPr>/gu
)
].map((match) => match[0]);
const fullWidthTableCount = countMatches(
documentXml,
/<w:tblW\s+w:w="5000"\s+w:type="pct"\s*\/>/gu
const tables = [
...documentXml.matchAll(
/<w:tbl(?:\s|>)[\s\S]*?<\/w:tbl>/gu
)
].map((match) => match[0]);
const contentTables = tables.filter((table) =>
/<w:tblStyle\s+w:val="Table"\s*\/>/u.test(table)
);
const fullWidthTableCount = contentTables.filter((table) =>
/<w:tblW\s+w:w="5000"\s+w:type="pct"\s*\/>/u.test(table)
).length;
const internalMarkerCount = countMatches(
documentXml,
/MD_TO_PDF_(?:CONTAINER|SECTION)_/gu
@@ -216,7 +224,9 @@ function inspectDocument(
bytes: content.byteLength,
page: pageOptions,
paragraphCount: countMatches(documentXml, /<w:p(?:\s|>)/gu),
tableCount: countMatches(documentXml, /<w:tbl(?:\s|>)/gu),
tableCount: tables.length,
contentTableCount: contentTables.length,
structuralTableCount: tables.length - contentTables.length,
drawingCount: countMatches(documentXml, /<w:drawing(?:\s|>)/gu),
explicitPageBreaks,
sectionCount: sections.length,
@@ -280,6 +290,7 @@ function createMediaResource(kind, ordinal, kindOrdinal) {
...(kind === "image"
? {}
: { caption: `${kind} 主题验收图 ${kindOrdinal}` }),
alignment: "center",
displayWidthPx: dimensions.width,
displayHeightPx: dimensions.height,
captureX: 0,
@@ -345,8 +356,8 @@ function readThemeTokenReport(themes) {
const tokensByTheme = new Map(
report.tokenSets.map((tokens) => {
assert(
tokens.slots?.length === 56,
`主题 ${tokens.themeId} 的真实令牌槽位不是 56`
tokens.slots?.length === DOCX_STYLE_SLOT_NAMES.length,
`主题 ${tokens.themeId} 的真实令牌槽位不是 ${DOCX_STYLE_SLOT_NAMES.length}`
);
assert(
/^[a-f0-9]{64}$/u.test(tokens.themeFingerprint ?? ""),
@@ -483,7 +494,7 @@ for (const result of results) {
`主题 ${result.id} 存在重复标题`
);
assert(
inspection.fullWidthTableCount === inspection.tableCount,
inspection.fullWidthTableCount === inspection.contentTableCount,
`主题 ${result.id} 存在非内容区全宽表格`
);
if (inspection.profile) {