feat: 完成 DOCX 通用结构映射与严格收口

This commit is contained in:
SkyJourney
2026-07-31 15:19:34 +08:00
parent 6e40374200
commit f3847f3e4b
31 changed files with 3175 additions and 64 deletions
+30 -8
View File
@@ -46,6 +46,35 @@ interface SlotNormalizationContext {
approximate: boolean;
}
function cssLengthToPx(value: string): number {
const valuePt = parseCssLengthToPt(value);
return valuePt === undefined ? 0 : valuePt / 0.75;
}
function resolveDocumentContentWidthPx(
snapshot: DocxThemeStyleSnapshot
): number | undefined {
const computed = snapshot.slots.find(
(entry) => entry.slot === "document"
)?.computed;
if (!computed) {
return undefined;
}
const widthPt = parseCssLengthToPt(computed.width);
if (widthPt === undefined) {
return undefined;
}
const borderWidthPx = [computed.borderLeft, computed.borderRight]
.map((value) => parseCssBorder(value)?.widthPt ?? 0)
.reduce((total, valuePt) => total + valuePt / 0.75, 0);
const contentWidthPx =
widthPt / 0.75 -
cssLengthToPx(computed.paddingLeft) -
cssLengthToPx(computed.paddingRight) -
borderWidthPx;
return contentWidthPx > 0 ? contentWidthPx : widthPt / 0.75;
}
const paragraphKinds = new Set<DocxStyleSlotKind>([
"document",
"paragraph",
@@ -719,14 +748,7 @@ export function resolveDocxThemeTokens(
): DocxThemeTokenSet {
const snapshot = docxThemeStyleSnapshotSchema.parse(input.snapshot);
const diagnostics: DocxThemeDiagnostic[] = [];
const documentWidth = snapshot.slots.find(
(entry) => entry.slot === "document"
)?.computed?.width;
const documentWidthPt = documentWidth
? parseCssLengthToPt(documentWidth)
: undefined;
const documentWidthPx =
documentWidthPt === undefined ? undefined : documentWidthPt / 0.75;
const documentWidthPx = resolveDocumentContentWidthPx(snapshot);
const snapshotBySlot = new Map(
snapshot.slots.map((entry) => [entry.slot, entry])
);
@@ -178,6 +178,31 @@ describe("DOCX 主题令牌归一化", () => {
expect(tokens.slots).toHaveLength(DOCX_STYLE_SLOT_NAMES.length);
});
it("按 border-box 文档的内容区宽度归一化全宽表格", () => {
const tokens = resolveDocxThemeTokens({
snapshot: createSnapshot({
document: {
width: "790px",
paddingLeft: "30px",
paddingRight: "30px",
borderLeft: "0px none rgb(17, 34, 51)",
borderRight: "0px none rgb(17, 34, 51)"
},
table: {
width: "730px"
}
}),
config: {
mode: "auto",
basePreset: "general",
overrides: {},
legacyPreset: false
}
});
expect(findSlot(tokens, "table").style.widthPercent).toBe(100);
});
it("保留封面高度、垂直对齐、轮廓线和隐藏字段语义", () => {
const tokens = resolveDocxThemeTokens({
snapshot: createSnapshot({