@stll/folio-core 0.32.2 → 0.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/__fixtures__/paragraphs.d.ts +17 -0
- package/dist/ai-edits/__fixtures__/paragraphs.js +33 -0
- package/dist/ai-edits/apply.d.ts +49 -3
- package/dist/ai-edits/apply.js +652 -86
- package/dist/ai-edits/blockRange.d.ts +7 -3
- package/dist/ai-edits/blockRange.js +14 -6
- package/dist/ai-edits/headless.d.ts +39 -5
- package/dist/ai-edits/headless.js +76 -17
- package/dist/ai-edits/index.d.ts +6 -6
- package/dist/ai-edits/index.js +3 -3
- package/dist/ai-edits/read.d.ts +4 -0
- package/dist/ai-edits/read.js +30 -6
- package/dist/ai-edits/scoped-reading.js +4 -3
- package/dist/ai-edits/snapshot.d.ts +42 -2
- package/dist/ai-edits/snapshot.js +127 -62
- package/dist/ai-edits/table-row-column-mutations.d.ts +24 -1
- package/dist/ai-edits/table-row-column-mutations.js +98 -6
- package/dist/ai-edits/table-targets.d.ts +12 -3
- package/dist/ai-edits/table-targets.js +15 -5
- package/dist/ai-edits/types.d.ts +219 -15
- package/dist/ai-edits/word-diff.d.ts +54 -11
- package/dist/ai-edits/word-diff.js +221 -61
- package/dist/compare/__fixtures__/body-sequence.d.ts +56 -0
- package/dist/compare/__fixtures__/body-sequence.js +136 -0
- package/dist/compare/__fixtures__/nested-table.d.ts +16 -0
- package/dist/compare/__fixtures__/nested-table.js +50 -0
- package/dist/compare/__fixtures__/numbered-list.d.ts +34 -0
- package/dist/compare/__fixtures__/numbered-list.js +85 -0
- package/dist/compare/column-alignment.d.ts +27 -0
- package/dist/compare/column-alignment.js +162 -0
- package/dist/compare/compare.d.ts +107 -0
- package/dist/compare/compare.js +412 -0
- package/dist/compare/formatting.d.ts +29 -0
- package/dist/compare/formatting.js +84 -0
- package/dist/compare/plan.d.ts +22 -0
- package/dist/compare/plan.js +1141 -0
- package/dist/compare/reproducible-package.d.ts +21 -0
- package/dist/compare/reproducible-package.js +47 -0
- package/dist/compare/scenario.d.ts +77 -0
- package/dist/compare/scenario.js +263 -0
- package/dist/compare/types.d.ts +297 -0
- package/dist/compare/types.js +42 -0
- package/dist/compare/verification.d.ts +83 -0
- package/dist/compare/verification.js +189 -0
- package/dist/compat/eigenpal.d.ts +9 -6
- package/dist/compat/eigenpal.js +6 -3
- package/dist/controller/layoutPipeline.d.ts +13 -0
- package/dist/controller/layoutPipeline.js +15 -1
- package/dist/display-list/build/buildContext.d.ts +39 -0
- package/dist/display-list/build/buildContext.js +31 -0
- package/dist/display-list/build/buildDisplayList.d.ts +79 -0
- package/dist/display-list/build/buildDisplayList.js +283 -0
- package/dist/display-list/build/colors.d.ts +20 -0
- package/dist/display-list/build/colors.js +136 -0
- package/dist/display-list/build/floatingImages.d.ts +24 -0
- package/dist/display-list/build/floatingImages.js +41 -0
- package/dist/display-list/build/fontTable.d.ts +31 -0
- package/dist/display-list/build/fontTable.js +97 -0
- package/dist/display-list/build/furniture.d.ts +86 -0
- package/dist/display-list/build/furniture.js +98 -0
- package/dist/display-list/build/glyphs.d.ts +55 -0
- package/dist/display-list/build/glyphs.js +103 -0
- package/dist/display-list/build/headerFooterPrimitives.d.ts +17 -0
- package/dist/display-list/build/headerFooterPrimitives.js +40 -0
- package/dist/display-list/build/imagePrimitives.d.ts +35 -0
- package/dist/display-list/build/imagePrimitives.js +264 -0
- package/dist/display-list/build/pageBorderPrimitives.d.ts +23 -0
- package/dist/display-list/build/pageBorderPrimitives.js +111 -0
- package/dist/display-list/build/pageFurniture.d.ts +35 -0
- package/dist/display-list/build/pageFurniture.js +141 -0
- package/dist/display-list/build/paragraphPrimitives.d.ts +19 -0
- package/dist/display-list/build/paragraphPrimitives.js +819 -0
- package/dist/display-list/build/regions.d.ts +46 -0
- package/dist/display-list/build/regions.js +56 -0
- package/dist/display-list/build/storyPrimitives.d.ts +29 -0
- package/dist/display-list/build/storyPrimitives.js +279 -0
- package/dist/display-list/build/strokes.d.ts +19 -0
- package/dist/display-list/build/strokes.js +78 -0
- package/dist/display-list/build/tablePrimitives.d.ts +38 -0
- package/dist/display-list/build/tablePrimitives.js +409 -0
- package/dist/display-list/build/textBoxPrimitives.d.ts +14 -0
- package/dist/display-list/build/textBoxPrimitives.js +129 -0
- package/dist/display-list/build/textDecorations.d.ts +29 -0
- package/dist/display-list/build/textDecorations.js +29 -0
- package/dist/display-list/build/unsupported.d.ts +57 -0
- package/dist/display-list/build/unsupported.js +0 -0
- package/dist/display-list/build/watermarkPrimitives.d.ts +15 -0
- package/dist/display-list/build/watermarkPrimitives.js +128 -0
- package/dist/display-list/dom/renderDisplayListToDom.d.ts +26 -0
- package/dist/display-list/dom/renderDisplayListToDom.js +620 -0
- package/dist/display-list/editor/displayListPagePainter.d.ts +22 -0
- package/dist/display-list/editor/displayListPagePainter.js +63 -0
- package/dist/display-list/editor/pageRenderer.d.ts +24 -0
- package/dist/display-list/editor/pageRenderer.js +36 -0
- package/dist/display-list/primitives.d.ts +95 -0
- package/dist/display-list/primitives.js +127 -0
- package/dist/display-list/types.d.ts +472 -0
- package/dist/display-list/types.js +0 -0
- package/dist/document-operations.d.ts +39 -8
- package/dist/document-operations.js +169 -16
- package/dist/document-stories.d.ts +16 -0
- package/dist/document-stories.js +48 -4
- package/dist/docx/hyperlinkParser.d.ts +9 -1
- package/dist/docx/hyperlinkParser.js +19 -13
- package/dist/docx/packageParts.d.ts +35 -0
- package/dist/docx/packageParts.js +120 -0
- package/dist/docx/paraIdRangeNormalization.d.ts +40 -0
- package/dist/docx/paraIdRangeNormalization.js +64 -0
- package/dist/docx/paragraphParser.js +101 -26
- package/dist/docx/paragraphTraversal.js +1 -1
- package/dist/docx/renderedPageBreakNormalization.js +3 -1
- package/dist/docx/revisionIdNormalization.d.ts +27 -0
- package/dist/docx/revisionIdNormalization.js +121 -0
- package/dist/docx/rezip.d.ts +6 -0
- package/dist/docx/rezip.js +51 -19
- package/dist/docx/selectiveSave.js +2 -2
- package/dist/docx/serializer/commentSerializer.js +6 -1
- package/dist/docx/serializer/paragraphSerializer.js +73 -20
- package/dist/docx/serializer/runSerializer.js +11 -10
- package/dist/docx/serializer/tableSerializer.js +26 -10
- package/dist/docx/server/applyDocxXmlPatchProposal.js +1 -1
- package/dist/docx/server/createBilingualDocx.js +3 -2
- package/dist/docx/streamingXmlParser.d.ts +13 -1
- package/dist/docx/streamingXmlParser.js +49 -11
- package/dist/docx/unzip.d.ts +1 -2
- package/dist/docx/unzip.js +25 -6
- package/dist/docx/xmlParser.d.ts +3 -1
- package/dist/docx/xmlParser.js +33 -27
- package/dist/export-pdf.d.ts +43 -0
- package/dist/export-pdf.js +98 -0
- package/dist/fonts/headlessMeasure.d.ts +76 -0
- package/dist/fonts/headlessMeasure.js +0 -0
- package/dist/fonts/sfnt/parse.d.ts +51 -0
- package/dist/fonts/sfnt/parse.js +525 -0
- package/dist/fonts/sfnt/subset.d.ts +20 -0
- package/dist/fonts/sfnt/subset.js +350 -0
- package/dist/fonts/sfnt/tables.d.ts +88 -0
- package/dist/fonts/sfnt/tables.js +131 -0
- package/dist/fonts/sfnt/woff.d.ts +14 -0
- package/dist/fonts/sfnt/woff.js +163 -0
- package/dist/generated/text_shaper.js +324 -0
- package/dist/generated/text_shaper_bg.wasm +0 -0
- package/dist/headless-layout.d.ts +75 -0
- package/dist/headless-layout.js +350 -0
- package/dist/index.d.ts +9 -6
- package/dist/index.js +6 -3
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +2 -0
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +3 -1
- package/dist/layout-bridge/convert/toFlowBlocks.js +2 -0
- package/dist/layout-engine/measure/advanceComposition.d.ts +62 -0
- package/dist/layout-engine/measure/advanceComposition.js +148 -0
- package/dist/layout-engine/measure/cache.d.ts +2 -0
- package/dist/layout-engine/measure/measureContainer.js +13 -0
- package/dist/layout-engine/measure/measureParagraph.js +1 -2
- package/dist/layout-engine/measure/measureTypes.d.ts +12 -0
- package/dist/layout-engine/measure/tableInlinePlacement.d.ts +3 -2
- package/dist/layout-engine/measure/tableInlinePlacement.js +14 -1
- package/dist/layout-engine/tableIndentCompatibility.d.ts +5 -0
- package/dist/layout-engine/tableIndentCompatibility.js +15 -0
- package/dist/layout-engine/types.d.ts +8 -0
- package/dist/layout-painter/renderPage.d.ts +29 -1
- package/dist/layout-painter/renderPage.js +47 -19
- package/dist/layout-painter/renderParagraph.d.ts +21 -2
- package/dist/layout-painter/renderParagraph.js +1 -1
- package/dist/layout-painter/renderTable.js +37 -4
- package/dist/managers/editorShortcuts.d.ts +33 -1
- package/dist/managers/editorShortcuts.js +20 -1
- package/dist/markdown/renderRuns.js +7 -2
- package/dist/markdown/renderTable.js +5 -1
- package/dist/model.d.ts +3 -3
- package/dist/model.js +2 -2
- package/dist/pdf/contentStream.d.ts +73 -0
- package/dist/pdf/contentStream.js +143 -0
- package/dist/pdf/fonts.d.ts +96 -0
- package/dist/pdf/fonts.js +593 -0
- package/dist/pdf/images.d.ts +28 -0
- package/dist/pdf/images.js +439 -0
- package/dist/pdf/objects.d.ts +93 -0
- package/dist/pdf/objects.js +237 -0
- package/dist/pdf/pageSpace.d.ts +43 -0
- package/dist/pdf/pageSpace.js +57 -0
- package/dist/pdf/paint.d.ts +32 -0
- package/dist/pdf/paint.js +442 -0
- package/dist/pdf/writePdf.d.ts +56 -0
- package/dist/pdf/writePdf.js +345 -0
- package/dist/prosemirror/commands/comments.js +96 -6
- package/dist/prosemirror/conversion/fromProseDoc.js +41 -7
- package/dist/prosemirror/conversion/toProseDoc.js +41 -4
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +15 -2
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +35 -6
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +2 -2
- package/dist/prosemirror/extensions/features/pasteCleanup.js +4 -3
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.d.ts +3 -1
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +5 -3
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -1
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +9 -3
- package/dist/prosemirror/extensions/nodes/TableExtension.js +56 -39
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +3 -1
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +5 -3
- package/dist/prosemirror/plugins/suggestionMode.js +1 -0
- package/dist/prosemirror/revisionCarriers.js +19 -2
- package/dist/prosemirror/utils/extractTrackedChanges.d.ts +2 -2
- package/dist/prosemirror/utils/extractTrackedChanges.js +59 -23
- package/dist/prosemirror/validation.js +55 -27
- package/dist/prosemirror/zeroWidthAnchors.d.ts +10 -0
- package/dist/prosemirror/zeroWidthAnchors.js +23 -0
- package/dist/redline.js +12 -79
- package/dist/server.d.ts +3 -3
- package/dist/server.js +2 -2
- package/dist/shaping/placeRun.d.ts +51 -0
- package/dist/shaping/placeRun.js +84 -0
- package/dist/shaping/shaper.d.ts +92 -0
- package/dist/shaping/shaper.js +102 -0
- package/dist/types/block-id.d.ts +18 -2
- package/dist/types/block-id.js +49 -5
- package/dist/types/content.d.ts +2 -2
- package/dist/utils/clipboard.js +4 -3
- package/dist/utils/fontResolver.js +183 -0
- package/dist/utils/htmlComments.d.ts +17 -0
- package/dist/utils/htmlComments.js +22 -0
- package/dist/utils/scriptSegments.d.ts +12 -1
- package/dist/utils/scriptSegments.js +17 -1
- package/dist/version-comparison.d.ts +1 -1
- package/package.json +7 -3
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import { createTableCellFlowState, placeTableCellBlock } from "../../layout-engine/measure/tableCellFlow.js";
|
|
2
|
+
import { buildTableCellGrid, buildTableCellPlacements, getSourceCellAt } from "../../layout-engine/measure/tableCellGrid.js";
|
|
3
|
+
import { resolveTableCellPadding } from "../../layout-engine/types.js";
|
|
4
|
+
import { HIT_REGION_KINDS } from "../primitives.js";
|
|
5
|
+
import { parseDisplayColor } from "./colors.js";
|
|
6
|
+
import { paintImageFragment } from "./imagePrimitives.js";
|
|
7
|
+
import { paintParagraphFragment } from "./paragraphPrimitives.js";
|
|
8
|
+
import { blockRegion, createPageComposer } from "./regions.js";
|
|
9
|
+
import { resolveBorderStroke } from "./strokes.js";
|
|
10
|
+
import { UNSUPPORTED_CONSTRUCT } from "./unsupported.js";
|
|
11
|
+
//#region src/display-list/build/tablePrimitives.ts
|
|
12
|
+
/**
|
|
13
|
+
* Table fragments → primitives.
|
|
14
|
+
*
|
|
15
|
+
* Order per cell is fixed here rather than left to a backend: background,
|
|
16
|
+
* borders, content, diagonals. `renderTable.ts` gets that order from CSS
|
|
17
|
+
* (a background paints under its own border, a `z-index: 1` diagonal over the
|
|
18
|
+
* content); nothing in a display list would reproduce it by accident.
|
|
19
|
+
*
|
|
20
|
+
* One deliberate divergence from the painter: `renderCellDiagonalBorder`
|
|
21
|
+
* (`renderTable.ts:533`) emulates a diagonal with a rotated bar whose dash
|
|
22
|
+
* pattern is a CSS gradient. Here it is a real diagonal `line` with the
|
|
23
|
+
* authored stroke pattern, so a backend draws a line rather than reverse
|
|
24
|
+
* engineering a gradient.
|
|
25
|
+
*/
|
|
26
|
+
/** A cell edge is "visible" for collapse purposes on style alone, as the painter decides it. */
|
|
27
|
+
const hasVisibleBorder = (border) => border !== void 0 && border.style !== "none" && border.style !== "nil";
|
|
28
|
+
const strokeFor = (border, context, label) => {
|
|
29
|
+
if (!hasVisibleBorder(border) || !border) return;
|
|
30
|
+
const { stroke, unresolvedColor } = resolveBorderStroke(border);
|
|
31
|
+
if (unresolvedColor !== void 0) context.unsupported.report(UNSUPPORTED_CONSTRUCT.unresolvedColor, context.pageIndex, `${label} colour ${unresolvedColor}`);
|
|
32
|
+
return stroke;
|
|
33
|
+
};
|
|
34
|
+
const paintCell = ({ composer, cell, cellMeasure, box, context, paintTextBox, sides }) => {
|
|
35
|
+
const primitives = [];
|
|
36
|
+
if (cell.background) {
|
|
37
|
+
const fill = parseDisplayColor(cell.background);
|
|
38
|
+
if (fill) primitives.push({
|
|
39
|
+
kind: "rect",
|
|
40
|
+
rect: {
|
|
41
|
+
xPx: box.xPx,
|
|
42
|
+
yPx: box.yPx,
|
|
43
|
+
widthPx: box.widthPx,
|
|
44
|
+
heightPx: box.heightPx
|
|
45
|
+
},
|
|
46
|
+
fill
|
|
47
|
+
});
|
|
48
|
+
else context.unsupported.report(UNSUPPORTED_CONSTRUCT.unresolvedColor, context.pageIndex, `table cell background ${cell.background}`);
|
|
49
|
+
}
|
|
50
|
+
const borders = cell.borders;
|
|
51
|
+
const strokes = {
|
|
52
|
+
top: sides.top ? strokeFor(borders?.top, context, "table cell top border") : void 0,
|
|
53
|
+
bottom: sides.bottom ? strokeFor(borders?.bottom, context, "table cell bottom border") : void 0,
|
|
54
|
+
left: sides.left ? strokeFor(borders?.left, context, "table cell left border") : void 0,
|
|
55
|
+
right: strokeFor(borders?.right, context, "table cell right border")
|
|
56
|
+
};
|
|
57
|
+
const widths = {
|
|
58
|
+
top: strokes.top?.thicknessPx ?? 0,
|
|
59
|
+
right: strokes.right?.thicknessPx ?? 0,
|
|
60
|
+
bottom: strokes.bottom?.thicknessPx ?? 0,
|
|
61
|
+
left: strokes.left?.thicknessPx ?? 0
|
|
62
|
+
};
|
|
63
|
+
if (strokes.top) primitives.push({
|
|
64
|
+
kind: "line",
|
|
65
|
+
x1Px: box.xPx,
|
|
66
|
+
y1Px: box.yPx + widths.top / 2,
|
|
67
|
+
x2Px: box.xPx + box.widthPx,
|
|
68
|
+
y2Px: box.yPx + widths.top / 2,
|
|
69
|
+
stroke: strokes.top
|
|
70
|
+
});
|
|
71
|
+
if (strokes.bottom) primitives.push({
|
|
72
|
+
kind: "line",
|
|
73
|
+
x1Px: box.xPx,
|
|
74
|
+
y1Px: box.yPx + box.heightPx - widths.bottom / 2,
|
|
75
|
+
x2Px: box.xPx + box.widthPx,
|
|
76
|
+
y2Px: box.yPx + box.heightPx - widths.bottom / 2,
|
|
77
|
+
stroke: strokes.bottom
|
|
78
|
+
});
|
|
79
|
+
if (strokes.left) primitives.push({
|
|
80
|
+
kind: "line",
|
|
81
|
+
x1Px: box.xPx + widths.left / 2,
|
|
82
|
+
y1Px: box.yPx,
|
|
83
|
+
x2Px: box.xPx + widths.left / 2,
|
|
84
|
+
y2Px: box.yPx + box.heightPx,
|
|
85
|
+
stroke: strokes.left
|
|
86
|
+
});
|
|
87
|
+
if (strokes.right) primitives.push({
|
|
88
|
+
kind: "line",
|
|
89
|
+
x1Px: box.xPx + box.widthPx - widths.right / 2,
|
|
90
|
+
y1Px: box.yPx,
|
|
91
|
+
x2Px: box.xPx + box.widthPx - widths.right / 2,
|
|
92
|
+
y2Px: box.yPx + box.heightPx,
|
|
93
|
+
stroke: strokes.right
|
|
94
|
+
});
|
|
95
|
+
const padding = resolveTableCellPadding(cell);
|
|
96
|
+
const contentXPx = box.xPx + widths.left + padding.left;
|
|
97
|
+
const contentYPx = box.yPx + widths.top + padding.top;
|
|
98
|
+
const contentWidthPx = Math.max(0, cellMeasure.width - padding.left - padding.right);
|
|
99
|
+
if (cell.textDirection === "btLr" || cell.textDirection === "tbRl") context.unsupported.report(UNSUPPORTED_CONSTRUCT.verticalCellText, context.pageIndex, `w:textDirection ${cell.textDirection} paints horizontally`);
|
|
100
|
+
composer.push(primitives);
|
|
101
|
+
primitives.length = 0;
|
|
102
|
+
paintCellBlocks({
|
|
103
|
+
composer,
|
|
104
|
+
blocks: cell.blocks,
|
|
105
|
+
measures: cellMeasure.blocks,
|
|
106
|
+
xPx: contentXPx,
|
|
107
|
+
yPx: contentYPx,
|
|
108
|
+
widthPx: contentWidthPx,
|
|
109
|
+
context,
|
|
110
|
+
paintTextBox
|
|
111
|
+
});
|
|
112
|
+
const diagonals = [{
|
|
113
|
+
border: borders?.topLeftToBottomRight,
|
|
114
|
+
from: "topLeft"
|
|
115
|
+
}, {
|
|
116
|
+
border: borders?.topRightToBottomLeft,
|
|
117
|
+
from: "topRight"
|
|
118
|
+
}];
|
|
119
|
+
for (const { border, from } of diagonals) {
|
|
120
|
+
const stroke = strokeFor(border, context, "table cell diagonal border");
|
|
121
|
+
if (!stroke) continue;
|
|
122
|
+
primitives.push({
|
|
123
|
+
kind: "line",
|
|
124
|
+
x1Px: box.xPx,
|
|
125
|
+
y1Px: from === "topLeft" ? box.yPx : box.yPx + box.heightPx,
|
|
126
|
+
x2Px: box.xPx + box.widthPx,
|
|
127
|
+
y2Px: from === "topLeft" ? box.yPx + box.heightPx : box.yPx,
|
|
128
|
+
stroke
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
composer.push(primitives);
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* A cell's inner blocks, stacked by the same flow state the measurer used, so
|
|
135
|
+
* paragraph spacing collapses in paint exactly where it collapsed in layout.
|
|
136
|
+
*/
|
|
137
|
+
const paintCellBlocks = ({ composer, blocks, measures, xPx, yPx, widthPx, context, paintTextBox }) => {
|
|
138
|
+
const flowState = createTableCellFlowState();
|
|
139
|
+
for (let index = 0; index < blocks.length; index += 1) {
|
|
140
|
+
const block = blocks[index];
|
|
141
|
+
const measure = measures[index];
|
|
142
|
+
if (!block || !measure) {
|
|
143
|
+
context.unsupported.report(UNSUPPORTED_CONSTRUCT.missingBlock, context.pageIndex, block === void 0 ? `table cell content at index ${String(index)} has no block` : `table cell block ${String(block.id)} has no measure`);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const placement = placeTableCellBlock(flowState, block, measure);
|
|
147
|
+
if (block.kind === "paragraph" && measure.kind === "paragraph") {
|
|
148
|
+
const previous = blocks[index - 1];
|
|
149
|
+
const next = blocks[index + 1];
|
|
150
|
+
const fragment = {
|
|
151
|
+
kind: "paragraph",
|
|
152
|
+
blockId: block.id,
|
|
153
|
+
x: xPx,
|
|
154
|
+
y: yPx + placement.contentTop,
|
|
155
|
+
width: widthPx,
|
|
156
|
+
height: placement.contentHeight,
|
|
157
|
+
fromLine: 0,
|
|
158
|
+
toLine: measure.lines.length
|
|
159
|
+
};
|
|
160
|
+
composer.region(blockRegion({
|
|
161
|
+
fragment,
|
|
162
|
+
kind: HIT_REGION_KINDS.paragraph,
|
|
163
|
+
context
|
|
164
|
+
}), () => {
|
|
165
|
+
paintParagraphFragment({
|
|
166
|
+
composer,
|
|
167
|
+
fragment,
|
|
168
|
+
block,
|
|
169
|
+
measure,
|
|
170
|
+
context,
|
|
171
|
+
...previous?.kind === "paragraph" && previous.attrs?.borders !== void 0 ? { prevBorders: previous.attrs.borders } : {},
|
|
172
|
+
...next?.kind === "paragraph" && next.attrs?.borders !== void 0 ? { nextBorders: next.attrs.borders } : {}
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (block.kind === "table" && measure.kind === "table") {
|
|
178
|
+
const fragment = {
|
|
179
|
+
blockId: block.id,
|
|
180
|
+
x: xPx,
|
|
181
|
+
y: yPx + placement.contentTop,
|
|
182
|
+
width: measure.totalWidth,
|
|
183
|
+
height: measure.totalHeight
|
|
184
|
+
};
|
|
185
|
+
composer.region(blockRegion({
|
|
186
|
+
fragment,
|
|
187
|
+
kind: HIT_REGION_KINDS.table,
|
|
188
|
+
context
|
|
189
|
+
}), () => {
|
|
190
|
+
paintTableBlock({
|
|
191
|
+
composer,
|
|
192
|
+
block,
|
|
193
|
+
measure,
|
|
194
|
+
xPx: fragment.x,
|
|
195
|
+
yPx: fragment.y,
|
|
196
|
+
context,
|
|
197
|
+
paintTextBox
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (block.kind === "image" && measure.kind === "image") {
|
|
203
|
+
const fragment = {
|
|
204
|
+
kind: "image",
|
|
205
|
+
blockId: block.id,
|
|
206
|
+
x: xPx,
|
|
207
|
+
y: yPx + placement.contentTop,
|
|
208
|
+
width: measure.width,
|
|
209
|
+
height: measure.height
|
|
210
|
+
};
|
|
211
|
+
composer.region(blockRegion({
|
|
212
|
+
fragment,
|
|
213
|
+
kind: HIT_REGION_KINDS.image,
|
|
214
|
+
context
|
|
215
|
+
}), () => {
|
|
216
|
+
composer.push(paintImageFragment(fragment, block, context));
|
|
217
|
+
});
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (block.kind === "textBox" && measure.kind === "textBox") {
|
|
221
|
+
const fragment = {
|
|
222
|
+
kind: "textBox",
|
|
223
|
+
blockId: block.id,
|
|
224
|
+
x: xPx,
|
|
225
|
+
y: yPx + placement.contentTop,
|
|
226
|
+
width: measure.width,
|
|
227
|
+
height: measure.height
|
|
228
|
+
};
|
|
229
|
+
composer.region(blockRegion({
|
|
230
|
+
fragment,
|
|
231
|
+
kind: HIT_REGION_KINDS.textBox,
|
|
232
|
+
context
|
|
233
|
+
}), () => {
|
|
234
|
+
paintTextBox({
|
|
235
|
+
composer,
|
|
236
|
+
fragment,
|
|
237
|
+
block,
|
|
238
|
+
measure,
|
|
239
|
+
context
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
context.unsupported.report(UNSUPPORTED_CONSTRUCT.fragmentKind, context.pageIndex, `table cell content of kind ${block.kind} is not painted`);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
const rowIsPainted = (row) => row !== void 0 && row.hidden !== true;
|
|
248
|
+
const paintTableBody = ({ composer, block, measure, xPx, yPx, fromRow, toRow, headerRowCount, context, paintTextBox }) => {
|
|
249
|
+
const grid = buildTableCellGrid(block.rows, measure.columnWidths.length);
|
|
250
|
+
const placements = buildTableCellPlacements({
|
|
251
|
+
grid,
|
|
252
|
+
columnWidths: measure.columnWidths,
|
|
253
|
+
bidi: block.bidi === true
|
|
254
|
+
});
|
|
255
|
+
const rowTops = [0];
|
|
256
|
+
for (const rowMeasure of measure.rows) rowTops.push((rowTops.at(-1) ?? 0) + rowMeasure.height);
|
|
257
|
+
const paintRow = (rowIndex, rowYPx) => {
|
|
258
|
+
const row = block.rows[rowIndex];
|
|
259
|
+
const rowMeasure = measure.rows[rowIndex];
|
|
260
|
+
if (!rowIsPainted(row) || !rowMeasure) return;
|
|
261
|
+
composer.region({
|
|
262
|
+
kind: HIT_REGION_KINDS.tableRow,
|
|
263
|
+
rect: {
|
|
264
|
+
xPx,
|
|
265
|
+
yPx: rowYPx,
|
|
266
|
+
widthPx: measure.columnWidths.reduce((total, width) => total + width, 0),
|
|
267
|
+
heightPx: rowMeasure.height
|
|
268
|
+
},
|
|
269
|
+
model: { rowIndex }
|
|
270
|
+
}, () => {
|
|
271
|
+
paintRowCells(row, rowMeasure, rowIndex, rowYPx);
|
|
272
|
+
});
|
|
273
|
+
};
|
|
274
|
+
const paintRowCells = (row, rowMeasure, rowIndex, rowYPx) => {
|
|
275
|
+
for (let cellIndex = 0; cellIndex < row.cells.length; cellIndex += 1) {
|
|
276
|
+
const cell = row.cells[cellIndex];
|
|
277
|
+
const cellMeasure = rowMeasure.cells[cellIndex];
|
|
278
|
+
if (!cell || !cellMeasure) continue;
|
|
279
|
+
const placement = placements.get(cell);
|
|
280
|
+
if (!placement) continue;
|
|
281
|
+
const rowSpan = Math.max(1, Math.trunc(cell.rowSpan ?? 1));
|
|
282
|
+
const spanEnd = Math.min(rowTops.length - 1, rowIndex + rowSpan);
|
|
283
|
+
const heightPx = spanEnd > rowIndex ? rowTops[spanEnd] - rowTops[rowIndex] : rowMeasure.height;
|
|
284
|
+
const atLogicalStart = placement.sourceColumn === 0;
|
|
285
|
+
const atLogicalEnd = placement.sourceColumn + placement.columnSpan >= measure.columnWidths.length;
|
|
286
|
+
const isFirstColumn = block.bidi === true ? atLogicalEnd : atLogicalStart;
|
|
287
|
+
const aboveCell = getSourceCellAt(grid, rowIndex - 1, placement.sourceColumn);
|
|
288
|
+
const leftNeighborColumn = block.bidi === true ? placement.sourceColumn + placement.columnSpan : placement.sourceColumn - 1;
|
|
289
|
+
const leftCell = getSourceCellAt(grid, rowIndex, leftNeighborColumn);
|
|
290
|
+
const box = {
|
|
291
|
+
xPx: xPx + placement.left,
|
|
292
|
+
yPx: rowYPx,
|
|
293
|
+
widthPx: placement.width,
|
|
294
|
+
heightPx
|
|
295
|
+
};
|
|
296
|
+
composer.region({
|
|
297
|
+
kind: HIT_REGION_KINDS.tableCell,
|
|
298
|
+
rect: box,
|
|
299
|
+
model: {
|
|
300
|
+
rowIndex,
|
|
301
|
+
columnIndex: placement.sourceColumn
|
|
302
|
+
}
|
|
303
|
+
}, () => {
|
|
304
|
+
paintCell({
|
|
305
|
+
composer,
|
|
306
|
+
cell,
|
|
307
|
+
cellMeasure,
|
|
308
|
+
box,
|
|
309
|
+
context,
|
|
310
|
+
paintTextBox,
|
|
311
|
+
sides: {
|
|
312
|
+
top: rowIndex === fromRow || !hasVisibleBorder(aboveCell?.borders?.bottom),
|
|
313
|
+
bottom: true,
|
|
314
|
+
left: isFirstColumn || !hasVisibleBorder(leftCell?.borders?.right),
|
|
315
|
+
right: true
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
let cursorYPx = yPx;
|
|
322
|
+
for (let rowIndex = 0; rowIndex < headerRowCount; rowIndex += 1) {
|
|
323
|
+
const rowMeasure = measure.rows[rowIndex];
|
|
324
|
+
if (!rowMeasure) continue;
|
|
325
|
+
paintRow(rowIndex, cursorYPx);
|
|
326
|
+
cursorYPx += rowMeasure.height;
|
|
327
|
+
}
|
|
328
|
+
for (let rowIndex = fromRow; rowIndex < toRow; rowIndex += 1) {
|
|
329
|
+
const rowMeasure = measure.rows[rowIndex];
|
|
330
|
+
if (!rowMeasure) continue;
|
|
331
|
+
paintRow(rowIndex, cursorYPx);
|
|
332
|
+
cursorYPx += rowMeasure.height;
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
/** A whole, unpaginated table: what a nested table inside a cell or box is. */
|
|
336
|
+
const paintTableBlock = ({ composer, block, measure, xPx, yPx, context, paintTextBox }) => paintTableBody({
|
|
337
|
+
composer,
|
|
338
|
+
block,
|
|
339
|
+
measure,
|
|
340
|
+
xPx,
|
|
341
|
+
yPx,
|
|
342
|
+
fromRow: 0,
|
|
343
|
+
toRow: block.rows.length,
|
|
344
|
+
headerRowCount: 0,
|
|
345
|
+
context,
|
|
346
|
+
paintTextBox
|
|
347
|
+
});
|
|
348
|
+
/**
|
|
349
|
+
* A table fragment's primitives. A fragment that cuts a row mid-content wraps
|
|
350
|
+
* its rows in a `clipGroup` and starts them `topClip` px higher, which is what
|
|
351
|
+
* the painter's negative row-stack offset plus `overflow: hidden` does.
|
|
352
|
+
*/
|
|
353
|
+
const paintTableFragment = ({ composer, fragment, block, measure, context, paintTextBox }) => {
|
|
354
|
+
const headerRowCount = fragment.continuesFromPrev === true ? fragment.headerRowCount ?? 0 : 0;
|
|
355
|
+
let headerHeightPx = 0;
|
|
356
|
+
for (let rowIndex = 0; rowIndex < headerRowCount; rowIndex += 1) headerHeightPx += measure.rows[rowIndex]?.height ?? 0;
|
|
357
|
+
const paintHeaders = (into) => {
|
|
358
|
+
if (headerRowCount === 0) return;
|
|
359
|
+
paintTableBody({
|
|
360
|
+
composer: into,
|
|
361
|
+
block,
|
|
362
|
+
measure,
|
|
363
|
+
xPx: fragment.x,
|
|
364
|
+
yPx: fragment.y,
|
|
365
|
+
fromRow: 0,
|
|
366
|
+
toRow: 0,
|
|
367
|
+
headerRowCount,
|
|
368
|
+
context,
|
|
369
|
+
paintTextBox
|
|
370
|
+
});
|
|
371
|
+
};
|
|
372
|
+
const topClipPx = fragment.topClip ?? 0;
|
|
373
|
+
const paintBody = (into) => {
|
|
374
|
+
paintTableBody({
|
|
375
|
+
composer: into,
|
|
376
|
+
block,
|
|
377
|
+
measure,
|
|
378
|
+
xPx: fragment.x,
|
|
379
|
+
yPx: fragment.y + headerHeightPx - topClipPx,
|
|
380
|
+
fromRow: fragment.fromRow,
|
|
381
|
+
toRow: fragment.toRow,
|
|
382
|
+
headerRowCount: 0,
|
|
383
|
+
context,
|
|
384
|
+
paintTextBox
|
|
385
|
+
});
|
|
386
|
+
};
|
|
387
|
+
if (fragment.topClip === void 0 && fragment.bottomClip === void 0) {
|
|
388
|
+
paintHeaders(composer);
|
|
389
|
+
paintBody(composer);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
paintHeaders(composer);
|
|
393
|
+
const clipped = createPageComposer();
|
|
394
|
+
paintBody(clipped);
|
|
395
|
+
const clip = {
|
|
396
|
+
xPx: fragment.x,
|
|
397
|
+
yPx: fragment.y + headerHeightPx,
|
|
398
|
+
widthPx: fragment.width,
|
|
399
|
+
heightPx: Math.max(0, fragment.height - headerHeightPx)
|
|
400
|
+
};
|
|
401
|
+
composer.push([{
|
|
402
|
+
kind: "clipGroup",
|
|
403
|
+
rect: clip,
|
|
404
|
+
children: [...clipped.primitives()],
|
|
405
|
+
regions: [...clipped.regions()]
|
|
406
|
+
}]);
|
|
407
|
+
};
|
|
408
|
+
//#endregion
|
|
409
|
+
export { paintTableBlock, paintTableFragment };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { TextBoxBlock, TextBoxFragment, TextBoxMeasure } from "../../layout-engine/types.js";
|
|
2
|
+
import { BuildContext } from "./buildContext.js";
|
|
3
|
+
import { PageComposer } from "./regions.js";
|
|
4
|
+
//#region src/display-list/build/textBoxPrimitives.d.ts
|
|
5
|
+
type TextBoxPaintOptions = {
|
|
6
|
+
readonly composer: PageComposer;
|
|
7
|
+
readonly fragment: TextBoxFragment;
|
|
8
|
+
readonly block: TextBoxBlock;
|
|
9
|
+
readonly measure: TextBoxMeasure;
|
|
10
|
+
readonly context: BuildContext;
|
|
11
|
+
};
|
|
12
|
+
declare const paintTextBoxFragment: ({ composer, fragment, block, measure, context }: TextBoxPaintOptions) => void;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { TextBoxPaintOptions, paintTextBoxFragment };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { layoutTextBoxContent } from "../../layout-engine/measure/textBoxParagraphLayout.js";
|
|
2
|
+
import { DEFAULT_TEXTBOX_MARGINS } from "../../layout-engine/types.js";
|
|
3
|
+
import { HIT_REGION_KINDS } from "../primitives.js";
|
|
4
|
+
import { parseDisplayColor } from "./colors.js";
|
|
5
|
+
import { paintParagraphFragment } from "./paragraphPrimitives.js";
|
|
6
|
+
import { blockRegion } from "./regions.js";
|
|
7
|
+
import { resolveBorderStroke } from "./strokes.js";
|
|
8
|
+
import { paintTableBlock } from "./tablePrimitives.js";
|
|
9
|
+
import { UNSUPPORTED_CONSTRUCT } from "./unsupported.js";
|
|
10
|
+
//#region src/display-list/build/textBoxPrimitives.ts
|
|
11
|
+
/**
|
|
12
|
+
* Text-box fragments → primitives: fill, outline, then the inner block story.
|
|
13
|
+
*
|
|
14
|
+
* The box's own content flow is `layoutTextBoxContent`, the same helper the
|
|
15
|
+
* measurer used, so the paragraphs land where their heights were reserved.
|
|
16
|
+
*/
|
|
17
|
+
const paintTextBoxFragment = ({ composer, fragment, block, measure, context }) => {
|
|
18
|
+
const primitives = [];
|
|
19
|
+
const box = {
|
|
20
|
+
xPx: fragment.x,
|
|
21
|
+
yPx: fragment.y,
|
|
22
|
+
widthPx: fragment.width,
|
|
23
|
+
heightPx: fragment.height
|
|
24
|
+
};
|
|
25
|
+
if (block.fillColor) {
|
|
26
|
+
const fill = parseDisplayColor(block.fillColor);
|
|
27
|
+
if (fill) primitives.push({
|
|
28
|
+
kind: "rect",
|
|
29
|
+
rect: box,
|
|
30
|
+
fill
|
|
31
|
+
});
|
|
32
|
+
else context.unsupported.report(UNSUPPORTED_CONSTRUCT.unresolvedColor, context.pageIndex, `text box fill ${block.fillColor}`);
|
|
33
|
+
}
|
|
34
|
+
let outlineWidthPx = 0;
|
|
35
|
+
if (block.outlineWidth !== void 0 && block.outlineWidth > 0) {
|
|
36
|
+
const { stroke, unresolvedColor } = resolveBorderStroke({
|
|
37
|
+
width: block.outlineWidth,
|
|
38
|
+
style: block.outlineStyle ?? "solid",
|
|
39
|
+
color: block.outlineColor ?? "#000000"
|
|
40
|
+
});
|
|
41
|
+
if (unresolvedColor !== void 0) context.unsupported.report(UNSUPPORTED_CONSTRUCT.unresolvedColor, context.pageIndex, `text box outline ${unresolvedColor}`);
|
|
42
|
+
if (stroke) {
|
|
43
|
+
outlineWidthPx = stroke.thicknessPx;
|
|
44
|
+
const inset = stroke.thicknessPx / 2;
|
|
45
|
+
primitives.push({
|
|
46
|
+
kind: "rect",
|
|
47
|
+
rect: {
|
|
48
|
+
xPx: box.xPx + inset,
|
|
49
|
+
yPx: box.yPx + inset,
|
|
50
|
+
widthPx: Math.max(0, box.widthPx - stroke.thicknessPx),
|
|
51
|
+
heightPx: Math.max(0, box.heightPx - stroke.thicknessPx)
|
|
52
|
+
},
|
|
53
|
+
stroke
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const margins = block.margins ?? DEFAULT_TEXTBOX_MARGINS;
|
|
58
|
+
const innerWidthPx = fragment.width - margins.left - margins.right;
|
|
59
|
+
const layout = layoutTextBoxContent(block.content, measure.innerMeasures);
|
|
60
|
+
let cursorYPx = box.yPx + outlineWidthPx + margins.top;
|
|
61
|
+
for (let index = 0; index < block.content.length; index += 1) {
|
|
62
|
+
const contentBlock = block.content[index];
|
|
63
|
+
const contentMeasure = measure.innerMeasures[index];
|
|
64
|
+
const placement = layout.placements[index];
|
|
65
|
+
if (!contentBlock || !contentMeasure || !placement) continue;
|
|
66
|
+
cursorYPx += placement.leadingSpacing;
|
|
67
|
+
if (contentBlock.kind === "paragraph" && contentMeasure.kind === "paragraph") {
|
|
68
|
+
const previous = block.content[index - 1];
|
|
69
|
+
const next = block.content[index + 1];
|
|
70
|
+
composer.push(primitives);
|
|
71
|
+
primitives.length = 0;
|
|
72
|
+
const contentFragment = {
|
|
73
|
+
kind: "paragraph",
|
|
74
|
+
blockId: contentBlock.id,
|
|
75
|
+
x: box.xPx + outlineWidthPx + margins.left,
|
|
76
|
+
y: cursorYPx,
|
|
77
|
+
width: innerWidthPx,
|
|
78
|
+
height: placement.contentHeight,
|
|
79
|
+
fromLine: 0,
|
|
80
|
+
toLine: contentMeasure.lines.length
|
|
81
|
+
};
|
|
82
|
+
composer.region(blockRegion({
|
|
83
|
+
fragment: contentFragment,
|
|
84
|
+
kind: HIT_REGION_KINDS.paragraph,
|
|
85
|
+
context
|
|
86
|
+
}), () => {
|
|
87
|
+
paintParagraphFragment({
|
|
88
|
+
composer,
|
|
89
|
+
fragment: contentFragment,
|
|
90
|
+
block: contentBlock,
|
|
91
|
+
measure: contentMeasure,
|
|
92
|
+
context,
|
|
93
|
+
...previous?.kind === "paragraph" && previous.attrs?.borders !== void 0 ? { prevBorders: previous.attrs.borders } : {},
|
|
94
|
+
...next?.kind === "paragraph" && next.attrs?.borders !== void 0 ? { nextBorders: next.attrs.borders } : {}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
} else if (contentBlock.kind === "table" && contentMeasure.kind === "table") {
|
|
98
|
+
composer.push(primitives);
|
|
99
|
+
primitives.length = 0;
|
|
100
|
+
const contentFragment = {
|
|
101
|
+
blockId: contentBlock.id,
|
|
102
|
+
x: box.xPx + outlineWidthPx + margins.left,
|
|
103
|
+
y: cursorYPx,
|
|
104
|
+
width: contentMeasure.totalWidth,
|
|
105
|
+
height: contentMeasure.totalHeight
|
|
106
|
+
};
|
|
107
|
+
composer.region(blockRegion({
|
|
108
|
+
fragment: contentFragment,
|
|
109
|
+
kind: HIT_REGION_KINDS.table,
|
|
110
|
+
context
|
|
111
|
+
}), () => {
|
|
112
|
+
paintTableBlock({
|
|
113
|
+
composer,
|
|
114
|
+
block: contentBlock,
|
|
115
|
+
measure: contentMeasure,
|
|
116
|
+
xPx: contentFragment.x,
|
|
117
|
+
yPx: contentFragment.y,
|
|
118
|
+
context,
|
|
119
|
+
paintTextBox: paintTextBoxFragment
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
} else context.unsupported.report(UNSUPPORTED_CONSTRUCT.fragmentKind, context.pageIndex, `text box content of kind ${contentBlock.kind} is not painted`);
|
|
123
|
+
cursorYPx += placement.contentHeight;
|
|
124
|
+
}
|
|
125
|
+
if (block.verticalAlign === "middle" || block.verticalAlign === "bottom") context.unsupported.report(UNSUPPORTED_CONSTRUCT.textBoxVerticalAlign, context.pageIndex, `text box anchor ${block.verticalAlign} paints from the top`);
|
|
126
|
+
composer.push(primitives);
|
|
127
|
+
};
|
|
128
|
+
//#endregion
|
|
129
|
+
export { paintTextBoxFragment };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/display-list/build/textDecorations.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Text-decoration geometry.
|
|
4
|
+
*
|
|
5
|
+
* The DOM painter delegates underlines and strikethroughs to CSS
|
|
6
|
+
* `text-decoration`, which derives its position and thickness from the font's
|
|
7
|
+
* own `post`/`OS/2` tables. A display list has to state the geometry, and the
|
|
8
|
+
* builder has no font binary, so these ratios stand in for the font-derived
|
|
9
|
+
* metrics.
|
|
10
|
+
*
|
|
11
|
+
* They are approximations of what a browser computes from the face, chosen to
|
|
12
|
+
* match the common Latin text faces at body sizes. The follow-up is to read
|
|
13
|
+
* `underlinePosition` / `underlineThickness` from the embedded face and thread
|
|
14
|
+
* them through the measure seam alongside ascent and descent; until then a
|
|
15
|
+
* decoration on an unusual face sits a fraction of an em off where Word puts
|
|
16
|
+
* it. Every constant is in one place so that follow-up is a single edit.
|
|
17
|
+
*/
|
|
18
|
+
/** Distance below the baseline to the centre of the underline, as a fraction of the em. */
|
|
19
|
+
declare const UNDERLINE_OFFSET_RATIO = 0.1;
|
|
20
|
+
/** Underline thickness as a fraction of the em, floored so a hairline stays visible. */
|
|
21
|
+
declare const UNDERLINE_THICKNESS_RATIO: number;
|
|
22
|
+
declare const MIN_DECORATION_THICKNESS_PX = 1;
|
|
23
|
+
/** Distance above the baseline to the centre of the strikethrough, as a fraction of the em. */
|
|
24
|
+
declare const STRIKETHROUGH_OFFSET_RATIO = 0.28;
|
|
25
|
+
declare const underlineThicknessPx: (fontSizePx: number) => number;
|
|
26
|
+
declare const underlineCenterYPx: (baselineYPx: number, fontSizePx: number) => number;
|
|
27
|
+
declare const strikethroughCenterYPx: (baselineYPx: number, fontSizePx: number) => number;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { MIN_DECORATION_THICKNESS_PX, STRIKETHROUGH_OFFSET_RATIO, UNDERLINE_OFFSET_RATIO, UNDERLINE_THICKNESS_RATIO, strikethroughCenterYPx, underlineCenterYPx, underlineThicknessPx };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/display-list/build/textDecorations.ts
|
|
2
|
+
/**
|
|
3
|
+
* Text-decoration geometry.
|
|
4
|
+
*
|
|
5
|
+
* The DOM painter delegates underlines and strikethroughs to CSS
|
|
6
|
+
* `text-decoration`, which derives its position and thickness from the font's
|
|
7
|
+
* own `post`/`OS/2` tables. A display list has to state the geometry, and the
|
|
8
|
+
* builder has no font binary, so these ratios stand in for the font-derived
|
|
9
|
+
* metrics.
|
|
10
|
+
*
|
|
11
|
+
* They are approximations of what a browser computes from the face, chosen to
|
|
12
|
+
* match the common Latin text faces at body sizes. The follow-up is to read
|
|
13
|
+
* `underlinePosition` / `underlineThickness` from the embedded face and thread
|
|
14
|
+
* them through the measure seam alongside ascent and descent; until then a
|
|
15
|
+
* decoration on an unusual face sits a fraction of an em off where Word puts
|
|
16
|
+
* it. Every constant is in one place so that follow-up is a single edit.
|
|
17
|
+
*/
|
|
18
|
+
/** Distance below the baseline to the centre of the underline, as a fraction of the em. */
|
|
19
|
+
const UNDERLINE_OFFSET_RATIO = .1;
|
|
20
|
+
/** Underline thickness as a fraction of the em, floored so a hairline stays visible. */
|
|
21
|
+
const UNDERLINE_THICKNESS_RATIO = 1 / 14;
|
|
22
|
+
const MIN_DECORATION_THICKNESS_PX = 1;
|
|
23
|
+
/** Distance above the baseline to the centre of the strikethrough, as a fraction of the em. */
|
|
24
|
+
const STRIKETHROUGH_OFFSET_RATIO = .28;
|
|
25
|
+
const underlineThicknessPx = (fontSizePx) => Math.max(1, fontSizePx * UNDERLINE_THICKNESS_RATIO);
|
|
26
|
+
const underlineCenterYPx = (baselineYPx, fontSizePx) => baselineYPx + fontSizePx * UNDERLINE_OFFSET_RATIO;
|
|
27
|
+
const strikethroughCenterYPx = (baselineYPx, fontSizePx) => baselineYPx - fontSizePx * STRIKETHROUGH_OFFSET_RATIO;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { MIN_DECORATION_THICKNESS_PX, STRIKETHROUGH_OFFSET_RATIO, UNDERLINE_OFFSET_RATIO, UNDERLINE_THICKNESS_RATIO, strikethroughCenterYPx, underlineCenterYPx, underlineThicknessPx };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { DisplayUnsupported } from "../types.js";
|
|
2
|
+
//#region src/display-list/build/unsupported.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Every construct name the builder can report. Named rather than free-form so
|
|
5
|
+
* a consumer can switch over them, and so a typo cannot invent a new category.
|
|
6
|
+
*/
|
|
7
|
+
declare const UNSUPPORTED_CONSTRUCT: {
|
|
8
|
+
/** `w:effect` animations (blinkBackground, shimmer, …). */
|
|
9
|
+
readonly textEffect: "textEffect";
|
|
10
|
+
/** `w:smallCaps`: advances are correct, the small-cap glyph choice is not expressible. */
|
|
11
|
+
readonly smallCaps: "smallCaps";
|
|
12
|
+
/** `w:w` horizontal scale: folded into advances, but the glyphs are not narrowed. */
|
|
13
|
+
readonly horizontalScale: "horizontalScale";
|
|
14
|
+
/** `w:em` emphasis marks. */
|
|
15
|
+
readonly emphasisMark: "emphasisMark";
|
|
16
|
+
/** `w:emboss` / `w:imprint` / `w:shadow` shadow effects. */
|
|
17
|
+
readonly textShadowEffect: "textShadowEffect";
|
|
18
|
+
/** `w:vanish` hidden text, suppressed as Word's print view does. */
|
|
19
|
+
readonly hiddenRun: "hiddenRun";
|
|
20
|
+
/** OMML equations, painted as `renderMathFallback`'s plain text. */
|
|
21
|
+
readonly mathRun: "mathRun";
|
|
22
|
+
/** An image whose bytes are not PNG or JPEG (WMF/EMF/TIFF/SVG/GIF/BMP). */
|
|
23
|
+
readonly imageFormat: "imageFormat";
|
|
24
|
+
/** An image whose bytes the builder could not reach (remote or opaque `src`). */
|
|
25
|
+
readonly imageSource: "imageSource";
|
|
26
|
+
/** A colour literal the producer could not resolve to concrete channels. */
|
|
27
|
+
readonly unresolvedColor: "unresolvedColor";
|
|
28
|
+
/** Runs placed in logical order because the builder runs no bidi algorithm. */
|
|
29
|
+
readonly bidiReordering: "bidiReordering";
|
|
30
|
+
/** `w:textDirection` vertical cell text. */
|
|
31
|
+
readonly verticalCellText: "verticalCellText";
|
|
32
|
+
/** Page borders, which the layout does not carry. */
|
|
33
|
+
readonly pageBorders: "pageBorders";
|
|
34
|
+
/** Watermarks, which the layout does not carry. */
|
|
35
|
+
readonly watermark: "watermark";
|
|
36
|
+
/** Footnote bodies, which the layout does not carry. */
|
|
37
|
+
readonly footnoteContent: "footnoteContent";
|
|
38
|
+
/** Header and footer stories, which the layout does not carry. */
|
|
39
|
+
readonly headerFooterContent: "headerFooterContent";
|
|
40
|
+
/** `a:bodyPr` middle/bottom anchoring inside a text box. */
|
|
41
|
+
readonly textBoxVerticalAlign: "textBoxVerticalAlign";
|
|
42
|
+
/** A fragment kind with no builder. */
|
|
43
|
+
readonly fragmentKind: "fragmentKind";
|
|
44
|
+
/** A fragment whose block or measure is missing from the block lookup. */
|
|
45
|
+
readonly missingBlock: "missingBlock";
|
|
46
|
+
/** A block/measure pair whose kinds disagree. */
|
|
47
|
+
readonly measureMismatch: "measureMismatch";
|
|
48
|
+
};
|
|
49
|
+
type UnsupportedConstruct = (typeof UNSUPPORTED_CONSTRUCT)[keyof typeof UNSUPPORTED_CONSTRUCT];
|
|
50
|
+
declare class UnsupportedCollector {
|
|
51
|
+
private readonly entries;
|
|
52
|
+
private readonly seen;
|
|
53
|
+
report(construct: UnsupportedConstruct, pageIndex: number, detail: string): void;
|
|
54
|
+
snapshot(): readonly DisplayUnsupported[];
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { UNSUPPORTED_CONSTRUCT, UnsupportedCollector, UnsupportedConstruct };
|
|
Binary file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { document_d_exports } from "../../types/document.js";
|
|
2
|
+
import { Page } from "../../layout-engine/types.js";
|
|
3
|
+
import { DisplayPrimitive } from "../types.js";
|
|
4
|
+
import { BuildContext } from "./buildContext.js";
|
|
5
|
+
//#region src/display-list/build/watermarkPrimitives.d.ts
|
|
6
|
+
type WatermarkPaintOptions = {
|
|
7
|
+
readonly page: Page;
|
|
8
|
+
readonly watermark: document_d_exports.Watermark;
|
|
9
|
+
/** Resolved `data:` source for a picture watermark; absent means unresolved. */
|
|
10
|
+
readonly imageSrc?: string;
|
|
11
|
+
readonly context: BuildContext;
|
|
12
|
+
};
|
|
13
|
+
declare const paintWatermark: ({ page, watermark, imageSrc, context }: WatermarkPaintOptions) => readonly DisplayPrimitive[];
|
|
14
|
+
//#endregion
|
|
15
|
+
export { WatermarkPaintOptions, paintWatermark };
|