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
@@ -44,13 +44,13 @@ import {
documentBaseCss,
documentGeometryCss,
documentInteractionCss,
formatPageNumber,
resolvePageNumberAlignment,
shouldRenderPageNumber,
type PagedPreviewPayload
} from "./paged-preview.js";
import { constrainSemanticCoversToPage } from "./semantic-cover-fit.js";
import type { PagedRenderTarget } from "./paged-render-target.js";
import { preparePdfDocumentLinks } from "./pdf-document-links.js";
import { classifyPagedPages } from "./paged-page-sequence.js";
import { applyPagedPageDecorations } from "./paged-page-decorations.js";
export type {
PagedDocumentRenderResult,
@@ -155,48 +155,6 @@ function mountMeasurementContainer(
};
}
function applyPageNumbers(
container: ParentNode,
payload: PagedPreviewPayload,
totalPages: number
) {
if (!payload.exportConfig.footer.enabled) {
return;
}
const pages = Array.from(
container.querySelectorAll<HTMLElement>(".pagedjs_page")
);
for (const [pageIndex, page] of pages.entries()) {
if (
!shouldRenderPageNumber(
payload.exportConfig.footer,
pageIndex
)
) {
continue;
}
const alignment = resolvePageNumberAlignment(
payload.exportConfig.footer,
pageIndex
);
const content = page.querySelector<HTMLElement>(
`.pagedjs_margin-bottom-${alignment} .pagedjs_margin-content`
);
if (!content) {
continue;
}
content.textContent = formatPageNumber(
payload.exportConfig.footer,
pageIndex,
totalPages
);
content.setAttribute("data-page-number-rendered", "true");
}
}
function removeTrailingPdfPageBreak(container: ParentNode) {
const pages = Array.from(
container.querySelectorAll<HTMLElement>(".pagedjs_page")
@@ -215,6 +173,7 @@ function createPagedRenderIdentity(
mermaidOutput: options.mermaidOutput,
themeCss: payload.themeCss,
exportConfig: payload.exportConfig,
semanticDocument: payload.semanticDocument,
metadata: payload.metadata,
features: payload.features
});
@@ -676,7 +635,8 @@ export class PagedDocumentRuntime {
if (
payload.features.includes("echarts") ||
content.querySelector(".mermaid svg") ||
content.querySelector("img.md-document-image")
content.querySelector("img.md-document-image") ||
content.querySelector('[data-semantic-region="cover"]')
) {
const measurement = mountMeasurementContainer(
documentRef,
@@ -687,6 +647,10 @@ export class PagedDocumentRuntime {
);
try {
await documentRef.fonts.ready;
constrainSemanticCoversToPage(
measurement.host,
getPageContentDimensions(payload.exportConfig).height
);
const echartsStartedAt = performance.now();
echartsErrors = await this.renderECharts(
@@ -902,7 +866,8 @@ export class PagedDocumentRuntime {
}
const finalizeStartedAt = performance.now();
applyPageNumbers(this.root, payload, pageCount);
const pageSequence = classifyPagedPages(this.root, payload);
applyPagedPageDecorations(this.root, payload, pageSequence);
if (options.target === "pdf") {
removeTrailingPdfPageBreak(this.root);
}