feat: 完成 DOCX R4 元素级视觉门禁
建立 Chromium、Word 与 WPS 的可编辑内容、段落几何、页眉页脚、封面、媒体和逐页视觉比较链路。 支持封面独立分节、正文页码重启、主题页边距与横向纸张,并将自然分页差异降为诊断项。 修正代码块内边距和折叠表格单元格边框映射,14 套主题生产矩阵与六组布局视觉矩阵通过。
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
calculateContentSimilarity,
|
||||
comparePdfSnapshotsBasic,
|
||||
createPdfDocumentSnapshot,
|
||||
type PdfDocumentSnapshot,
|
||||
type PdfTextLineSnapshot,
|
||||
type VisualPageSemanticExpectation,
|
||||
} from "../src/index.js";
|
||||
import { createMinimalPdf } from "./pdf-fixture.js";
|
||||
|
||||
@@ -19,7 +22,105 @@ async function snapshot(
|
||||
});
|
||||
}
|
||||
|
||||
function textLine(
|
||||
text: string,
|
||||
y: number,
|
||||
role: PdfTextLineSnapshot["role"] = "content",
|
||||
): PdfTextLineSnapshot {
|
||||
return {
|
||||
text,
|
||||
normalizedText: text,
|
||||
bounds: { x: 72, y, width: 200, height: 12 },
|
||||
baselineY: y + 10,
|
||||
role,
|
||||
items: [],
|
||||
};
|
||||
}
|
||||
|
||||
function withLines(
|
||||
source: PdfDocumentSnapshot,
|
||||
lines: PdfTextLineSnapshot[],
|
||||
): PdfDocumentSnapshot {
|
||||
const page = source.pages[0];
|
||||
if (!page) {
|
||||
throw new Error("测试快照缺少页面");
|
||||
}
|
||||
return {
|
||||
...source,
|
||||
contentText: lines.map((line) => line.normalizedText).join("\n"),
|
||||
pages: [{ ...page, lines }],
|
||||
};
|
||||
}
|
||||
|
||||
const pageSemantics: VisualPageSemanticExpectation[] = [
|
||||
{
|
||||
physicalPageNumber: 1,
|
||||
kind: "body-first",
|
||||
logicalPageNumber: 1,
|
||||
logicalPageCount: 1,
|
||||
headerVisible: true,
|
||||
headerSlots: [{ alignment: "left", text: "页眉左栏" }],
|
||||
footerVisible: true,
|
||||
footerAlignment: "center",
|
||||
pageNumberText: "1 / 1",
|
||||
},
|
||||
];
|
||||
|
||||
describe("PDF 基础视觉门禁", () => {
|
||||
it("将 PDF 字体映射产生的等价部首字形规范为正文汉字", () => {
|
||||
expect(
|
||||
calculateContentSimilarity(
|
||||
"示例市人⺠政府办公室",
|
||||
"示例市人民政府办公室",
|
||||
),
|
||||
).toBe(1);
|
||||
expect(calculateContentSimilarity("项目⻔户", "项目门户")).toBe(1);
|
||||
});
|
||||
|
||||
it("以 DOCX 可编辑正文为语义基准并容许 Chromium 媒体内部文本", async () => {
|
||||
const baseline = withLines(await snapshot("baseline"), [
|
||||
textLine("页眉左栏", 10),
|
||||
textLine("正文甲", 100),
|
||||
textLine("媒体内部标签", 150),
|
||||
textLine("正文乙", 200),
|
||||
textLine("1 / 1", 820, "page-number"),
|
||||
]);
|
||||
const candidate = withLines(await snapshot("candidate"), [
|
||||
textLine("页眉左栏", 10),
|
||||
textLine("正文甲☒", 100),
|
||||
textLine("正文乙", 200),
|
||||
textLine("1 / 1", 820, "page-number"),
|
||||
]);
|
||||
const result = comparePdfSnapshotsBasic(baseline, candidate, {}, {
|
||||
expectedEditableText: "正文甲正文乙",
|
||||
pageSemantics,
|
||||
});
|
||||
expect(result.status).toBe("passed");
|
||||
expect(result.baselineEditableCoverage).toBe(1);
|
||||
expect(result.candidateEditableSimilarity).toBe(1);
|
||||
expect(result.candidateEditableExact).toBe(true);
|
||||
});
|
||||
|
||||
it("候选缺少任一可编辑正文字符时严格失败", async () => {
|
||||
const baseline = withLines(await snapshot("baseline"), [
|
||||
textLine("正文甲正文乙", 100),
|
||||
]);
|
||||
const candidate = withLines(await snapshot("candidate"), [
|
||||
textLine("正文甲正文", 100),
|
||||
]);
|
||||
const result = comparePdfSnapshotsBasic(baseline, candidate, {}, {
|
||||
expectedEditableText: "正文甲正文乙",
|
||||
});
|
||||
expect(result.status).toBe("failed");
|
||||
expect(result.baselineEditableCoverage).toBe(1);
|
||||
expect(result.candidateEditableExact).toBe(false);
|
||||
expect(result.issues).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ code: "CONTENT_MISMATCH" }),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("通过纸张和正文一致的文档", async () => {
|
||||
const baseline = await snapshot("baseline");
|
||||
const candidate = await snapshot("candidate");
|
||||
|
||||
Reference in New Issue
Block a user