fix: 修复DOCX独立封面视觉门禁

统一 Chromium 与 DOCX 的受限高度封面布局,补齐可编辑边框、装饰边和 Office 页面舍入适配。

按四套独立封面主题、纵横方向及五组页边距执行 40 个 Word/WPS 真实视觉场景,严格失败为 0,并保留正文诊断供 D4 修复。
This commit is contained in:
SkyJourney
2026-08-03 18:15:21 +08:00
parent 9dee7e8605
commit b275c671fc
19 changed files with 785 additions and 82 deletions
+50 -5
View File
@@ -58,9 +58,13 @@ const matrixDefinition = summarizeDocxLayoutVisualMatrix(
const selectedCaseId =
process.env.MD_TO_PDF_LAYOUT_VISUAL_CASE?.trim() || undefined;
const selectedScope =
process.env.MD_TO_PDF_LAYOUT_VISUAL_SCOPE?.trim() || "full";
const selectedCases = selectedCaseId
? cases.filter((entry) => entry.id === selectedCaseId)
: cases;
: selectedScope === "cover"
? cases.filter((entry) => entry.coverPageCount > 0)
: cases;
function assert(condition, message) {
if (!condition) {
@@ -287,6 +291,17 @@ function reportGateFailures(report, label) {
return failures;
}
function reportCoverGateFailures(report, label) {
return getBlockingVisualDiffIssues(report)
.filter(
(issue) =>
issue.code === "COVER_LAYOUT_MISMATCH" ||
issue.baselinePageNumber === 1 ||
issue.candidatePageNumber === 1
)
.map((issue) => `${label}: ${issue.code} - ${issue.message}`);
}
function reportSummary(report) {
const paragraphIssues = report.basic.paragraphLayouts?.flatMap(
(paragraph) => paragraph.issues
@@ -462,6 +477,10 @@ assert(
selectedCases.length > 0,
`视觉矩阵用例不存在:${selectedCaseId}`
);
assert(
selectedScope === "full" || selectedScope === "cover",
`不支持的视觉矩阵作用域:${selectedScope}`
);
const wordAdapter = createWordPdfAdapter({ timeoutMs: 240_000 });
const wpsAdapter = createWpsPdfAdapter({ timeoutMs: 240_000 });
const wordCapability = await wordAdapter.probe();
@@ -471,6 +490,7 @@ assert(wpsCapability.available, `WPS Writer 不可用:${wpsCapability.detail}`
const summaries = [];
const gateFailures = [];
const diagnosticFailures = [];
for (const caseDefinition of selectedCases) {
process.stderr.write(`[layout visual matrix] ${caseDefinition.id}\n`);
const { caseDirectory, result } = await generateCase(caseDefinition);
@@ -569,7 +589,7 @@ for (const caseDefinition of selectedCases) {
writeReportArtifacts(rasterDirectory, "wps", wpsReport);
writeReportArtifacts(rasterDirectory, "office", officeReport);
const currentFailures = [...new Set([
const allCurrentFailures = [...new Set([
...pageGeometryFailures(chromium, caseDefinition, "Chromium"),
...pageGeometryFailures(word, caseDefinition, "Microsoft Word"),
...pageGeometryFailures(wps, caseDefinition, "WPS Writer"),
@@ -589,9 +609,19 @@ for (const caseDefinition of selectedCases) {
...reportGateFailures(wpsReport, "Chromium/WPS"),
...reportGateFailures(officeReport, "Word/WPS")
])];
const currentFailures = selectedScope === "cover"
? [...new Set([
...reportCoverGateFailures(wordReport, "Chromium/Word"),
...reportCoverGateFailures(wpsReport, "Chromium/WPS"),
...reportCoverGateFailures(officeReport, "Word/WPS")
])]
: allCurrentFailures;
gateFailures.push(
...currentFailures.map((failure) => `${caseDefinition.id}: ${failure}`)
);
diagnosticFailures.push(
...allCurrentFailures.map((failure) => `${caseDefinition.id}: ${failure}`)
);
summaries.push({
id: caseDefinition.id,
themeId: caseDefinition.themeId,
@@ -623,6 +653,7 @@ for (const caseDefinition of selectedCases) {
office: reportSummary(officeReport)
},
gateFailures: currentFailures,
diagnosticFailures: allCurrentFailures,
files: {
chromiumPdf: path.relative(repositoryDirectory, chromiumPdfPath),
docx: path.relative(repositoryDirectory, docxPath),
@@ -638,8 +669,13 @@ const summary = {
generatedAt: new Date().toISOString(),
matrixDefinition,
selectedCaseId,
selectedScope,
execution: {
mode: selectedCaseId ? "single-case" : "full-matrix",
mode: selectedCaseId
? "single-case"
: selectedScope === "cover"
? "cover-matrix"
: "full-matrix",
executedCaseCount: summaries.length,
expectedFullMatrixCaseCount: matrixDefinition.expectedCaseCount,
complete: !selectedCaseId && summaries.length === matrixDefinition.expectedCaseCount
@@ -650,15 +686,24 @@ const summary = {
caseCount: summaries.length,
gatePassed: gateFailures.length === 0,
gateFailures,
diagnosticFailures,
cases: summaries
};
const summaryPath = path.join(
outputDirectory,
selectedCaseId ? `summary-${selectedCaseId}.json` : "summary.json"
selectedCaseId
? `summary-${selectedCaseId}.json`
: selectedScope === "cover"
? "summary-cover.json"
: "summary.json"
);
const matrixHtmlPath = path.join(
outputDirectory,
selectedCaseId ? `matrix-${selectedCaseId}.html` : "matrix.html"
selectedCaseId
? `matrix-${selectedCaseId}.html`
: selectedScope === "cover"
? "matrix-cover.html"
: "matrix.html"
);
fs.writeFileSync(summaryPath, `${JSON.stringify(summary, null, 2)}\n`, "utf8");
fs.writeFileSync(matrixHtmlPath, renderMatrixSummaryHtml(summary), "utf8");