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
+71 -2
View File
@@ -463,11 +463,21 @@ function normalizeComputedSlot(
};
}
}
if (widthSlots.has(context.slot) && context.documentWidthPx) {
const containingBlockWidthPt = computed.containingBlockWidth
? parseCssLengthToPt(computed.containingBlockWidth)
: undefined;
const relativeWidthPx =
containingBlockWidthPt !== undefined
? containingBlockWidthPt / 0.75
: context.documentWidthPx;
if (
(widthSlots.has(context.slot) || context.kind === "structure") &&
relativeWidthPx
) {
const widthPt = parseCssLengthToPt(computed.width);
const widthPx = widthPt === undefined ? undefined : widthPt / 0.75;
if (widthPx !== undefined) {
const percent = (widthPx / context.documentWidthPx) * 100;
const percent = (widthPx / relativeWidthPx) * 100;
style.widthPercent = Math.round(
Math.max(0, Math.min(100, percent))
);
@@ -482,6 +492,27 @@ function normalizeComputedSlot(
}
}
}
if (context.kind === "structure" && context.documentWidthPx) {
const percentage = /^([+-]?(?:\d+\.?\d*|\.\d+))%$/u.exec(
computed.minWidth
);
const minimumWidthPercent = percentage
? Number.parseFloat(percentage[1]!)
: (() => {
const minimumWidthPt = parseCssLengthToPt(computed.minWidth);
return minimumWidthPt === undefined
? undefined
: ((minimumWidthPt / 0.75) / context.documentWidthPx) * 100;
})();
if (
minimumWidthPercent !== undefined &&
minimumWidthPercent > 0
) {
style.minimumWidthPercent = roundDocxValue(
Math.min(100, minimumWidthPercent)
);
}
}
const minimumHeightPt = length(
context,
"min-height",
@@ -491,6 +522,15 @@ function normalizeComputedSlot(
if (minimumHeightPt !== undefined && minimumHeightPt > 0) {
style.minimumHeightPt = minimumHeightPt;
}
const contentPaddingLeftPt = computed.contentPaddingLeft
? length(context, "content-padding-left", computed.contentPaddingLeft, {
allowAuto: true,
nonNegative: true
})
: undefined;
if (contentPaddingLeftPt !== undefined && contentPaddingLeftPt > 0) {
style.contentPaddingLeftPt = contentPaddingLeftPt;
}
if (computed.display === "none") {
style.hidden = true;
}
@@ -512,6 +552,35 @@ function normalizeComputedSlot(
if (resolved) {
style.verticalAlignment = resolved;
}
const childAlignment = {
"flex-start": "left",
start: "left",
center: "center",
"flex-end": "right",
end: "right",
stretch: "stretch"
} as const;
const resolvedChildAlignment =
childAlignment[
computed.alignItems as keyof typeof childAlignment
];
if (resolvedChildAlignment) {
style.childAlignment = resolvedChildAlignment;
}
}
const selfAlignment = {
"flex-start": "left",
start: "left",
center: "center",
"flex-end": "right",
end: "right",
stretch: "stretch"
} as const;
const resolvedSelfAlignment = computed.alignSelf
? selfAlignment[computed.alignSelf as keyof typeof selfAlignment]
: undefined;
if (resolvedSelfAlignment) {
style.selfAlignment = resolvedSelfAlignment;
}
if (computed.display === "flex" || computed.display === "grid") {
diagnostic(context, {