fix: 重建DOCX视觉一致性门禁

This commit is contained in:
SkyJourney
2026-08-03 10:09:43 +08:00
parent 842c3f1782
commit 28c88faabb
11 changed files with 811 additions and 49 deletions
+50 -27
View File
@@ -10,6 +10,7 @@ import {
createPdfVisualDiffReport,
createWordPdfAdapter,
createWpsPdfAdapter,
getBlockingVisualDiffIssues,
renderPdfVisualDiffHtml,
serializePdfVisualDiffJson
} from "../packages/document-visual-diff/dist/index.js";
@@ -41,24 +42,6 @@ const pageSizes = {
A4: { portrait: [595.276, 841.89], landscape: [841.89, 595.276] },
Letter: { portrait: [612, 792], landscape: [792, 612] }
};
const strictIssueCodes = new Set([
"PAGE_SIZE_MISMATCH",
"PAGE_ORIENTATION_MISMATCH",
"CONTENT_MISMATCH",
"PAGE_NUMBER_VISIBILITY_MISMATCH",
"PAGE_NUMBER_TEXT_MISMATCH",
"PAGE_NUMBER_ALIGNMENT_MISMATCH",
"PAGE_NUMBER_VERTICAL_POSITION_MISMATCH",
"HEADER_VISIBILITY_MISMATCH",
"HEADER_TEXT_MISSING",
"HEADER_VERTICAL_POSITION_MISMATCH",
"EDITABLE_PARAGRAPH_MAPPING_MISMATCH",
"TEXT_BLOCK_LINE_COUNT_MISMATCH",
"PARAGRAPH_LINE_BREAK_MISMATCH",
"PARAGRAPH_LINE_GEOMETRY_MISMATCH",
"COVER_LAYOUT_MISMATCH"
]);
const cases = [
{
id: "official-theme-a4-portrait",
@@ -124,6 +107,20 @@ const cases = [
}
}
},
{
id: "tender-blind-cover-a4-portrait",
themeId: "tender-blind",
coverPageCount: 1,
paper: { format: "A4", orientation: "portrait" },
config: {}
},
{
id: "tender-classic-cover-a4-portrait",
themeId: "tender-classic",
coverPageCount: 1,
paper: { format: "A4", orientation: "portrait" },
config: {}
},
{
id: "formal-custom-a4-landscape",
themeId: "formal-regulation",
@@ -379,8 +376,7 @@ function buildPageSemantics(pageCount, coverPageCount, config) {
}
function reportGateFailures(report, label) {
const failures = report.issues
.filter((issue) => strictIssueCodes.has(issue.code))
const failures = getBlockingVisualDiffIssues(report)
.map((issue) => `${label}: ${issue.code} - ${issue.message}`);
if (report.basic.baselineEditableCoverage !== 1) {
failures.push(
@@ -400,18 +396,21 @@ function reportSummary(report) {
const paragraphIssues = report.basic.paragraphLayouts?.flatMap(
(paragraph) => paragraph.issues
) ?? [];
const semanticBlocks = report.basic.semanticBlockVisuals ?? [];
const blockingIssueCodes = [
...new Set(getBlockingVisualDiffIssues(report).map((issue) => issue.code))
];
return {
status: report.status,
issueCodes: [...new Set(report.issues.map((issue) => issue.code))],
strictIssueCodes: [
...new Set(
report.issues
.filter((issue) => strictIssueCodes.has(issue.code))
.map((issue) => issue.code)
)
],
blockingIssueCodes,
strictIssueCodes: blockingIssueCodes,
editableExact: report.basic.candidateEditableExact,
paragraphIssueCount: paragraphIssues.length,
semanticBlockCount: semanticBlocks.length,
failedSemanticBlockCount: semanticBlocks.filter(
(comparison) => comparison.status === "failed"
).length,
bodyFlow: report.basic.bodyFlow
};
}
@@ -453,6 +452,30 @@ function writeReportArtifacts(directory, prefix, report) {
page.artifacts.heatmapPng
);
}
for (const comparison of report.basic.semanticBlockVisuals ?? []) {
for (const line of comparison.lines) {
if (!line.artifacts) {
continue;
}
const stem = `${prefix}-block-${comparison.expectation.index + 1}-line-${line.lineIndex + 1}`;
fs.writeFileSync(
path.join(directory, `${stem}-baseline.png`),
line.artifacts.baselinePng
);
fs.writeFileSync(
path.join(directory, `${stem}-candidate.png`),
line.artifacts.candidatePng
);
fs.writeFileSync(
path.join(directory, `${stem}-overlay.png`),
line.artifacts.overlayPng
);
fs.writeFileSync(
path.join(directory, `${stem}-heatmap.png`),
line.artifacts.heatmapPng
);
}
}
}
fs.mkdirSync(outputDirectory, { recursive: true });