reamkit 1.30.0 → 1.32.0
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/README.md +70 -24
- package/dist/esm/core/bidi/index.d.ts +10 -0
- package/dist/esm/core/bidi/index.js +18 -2
- package/dist/esm/core/converter/facade.js +10 -5
- package/dist/esm/core/converter/project.js +4 -1
- package/dist/esm/core/converter/ream.js +17 -6
- package/dist/esm/core/converter/sheet-images.d.ts +17 -0
- package/dist/esm/core/converter/sheet-images.js +33 -0
- package/dist/esm/core/crypto/offcrypto.js +1 -1
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +175 -0
- package/dist/esm/core/drawingml/chart-geometry.d.ts +21 -8
- package/dist/esm/core/drawingml/chart-geometry.js +198 -108
- package/dist/esm/core/drawingml/chart-parser.js +73 -2
- package/dist/esm/core/drawingml/colors.d.ts +6 -0
- package/dist/esm/core/drawingml/colors.js +23 -3
- package/dist/esm/core/drawingml/shape-render.d.ts +3 -1
- package/dist/esm/core/drawingml/shape-render.js +4 -2
- package/dist/esm/core/font/index.d.ts +2 -0
- package/dist/esm/core/font/measure.d.ts +9 -0
- package/dist/esm/core/font/measure.js +9 -1
- package/dist/esm/core/font/ttf-build.d.ts +113 -0
- package/dist/esm/core/font/ttf-build.js +1224 -0
- package/dist/esm/core/font/ttf-subset.d.ts +19 -0
- package/dist/esm/core/font/ttf-subset.js +14 -1
- package/dist/esm/core/fonts/families.js +4 -2
- package/dist/esm/core/fonts/provider.d.ts +17 -0
- package/dist/esm/core/fonts/provider.js +27 -2
- package/dist/esm/core/fonts/remote-fonts.js +8 -1
- package/dist/esm/core/fonts/scripts.js +4 -2
- package/dist/esm/core/ir/flow.d.ts +77 -1
- package/dist/esm/core/ir/sheet.d.ts +43 -0
- package/dist/esm/core/number-format.js +16 -11
- package/dist/esm/core/numbering/index.d.ts +1 -1
- package/dist/esm/core/numbering/state.d.ts +11 -1
- package/dist/esm/core/numbering/state.js +10 -1
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +96 -0
- package/dist/esm/core/style-cascade/resolver.js +39 -6
- package/dist/esm/core/style-cascade/types.d.ts +19 -1
- package/dist/esm/core/style-cascade/types.js +3 -0
- package/dist/esm/excel/column-bands.d.ts +39 -6
- package/dist/esm/excel/column-bands.js +41 -17
- package/dist/esm/excel/comments-parser.js +19 -0
- package/dist/esm/excel/conditional-format.d.ts +7 -3
- package/dist/esm/excel/conditional-format.js +77 -39
- package/dist/esm/excel/formula/context.d.ts +18 -0
- package/dist/esm/excel/formula/eval.js +51 -0
- package/dist/esm/excel/formula/index.d.ts +2 -2
- package/dist/esm/excel/formula/lexer.d.ts +1 -1
- package/dist/esm/excel/formula/lexer.js +41 -0
- package/dist/esm/excel/formula/parser.d.ts +25 -0
- package/dist/esm/excel/formula/parser.js +95 -0
- package/dist/esm/excel/print-model.d.ts +38 -1
- package/dist/esm/excel/print-model.js +522 -80
- package/dist/esm/excel/shared-strings-parser.d.ts +9 -0
- package/dist/esm/excel/shared-strings-parser.js +11 -1
- package/dist/esm/excel/sheet-drawing.d.ts +38 -2
- package/dist/esm/excel/sheet-drawing.js +47 -13
- package/dist/esm/excel/sheet-shape-parser.d.ts +6 -2
- package/dist/esm/excel/sheet-shape-parser.js +18 -6
- package/dist/esm/excel/sheet-to-flow.d.ts +17 -0
- package/dist/esm/excel/sheet-to-flow.js +434 -47
- package/dist/esm/excel/styles-parser.d.ts +9 -0
- package/dist/esm/excel/styles-parser.js +80 -26
- package/dist/esm/excel/table-parser.js +5 -0
- package/dist/esm/excel/table-style-presets.d.ts +26 -0
- package/dist/esm/excel/table-style-presets.js +1555 -0
- package/dist/esm/excel/table-style.d.ts +13 -0
- package/dist/esm/excel/table-style.js +75 -0
- package/dist/esm/excel/tint.d.ts +10 -0
- package/dist/esm/excel/tint.js +75 -0
- package/dist/esm/excel/vml-drawing.d.ts +41 -3
- package/dist/esm/excel/vml-drawing.js +143 -10
- package/dist/esm/excel/worksheet-parser.js +89 -15
- package/dist/esm/excel/xls/biff-reader.js +10 -2
- package/dist/esm/excel/xlsx-reader.js +195 -14
- package/dist/esm/excel/xlsx-writer.js +8 -7
- package/dist/esm/html/html-writer.js +202 -20
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/layout/fit-pages.d.ts +11 -0
- package/dist/esm/layout/fit-pages.js +86 -0
- package/dist/esm/layout/line-paint.d.ts +58 -0
- package/dist/esm/layout/line-paint.js +107 -0
- package/dist/esm/layout/page-doc.d.ts +61 -3
- package/dist/esm/layout/page-doc.js +141 -1
- package/dist/esm/layout/styled-layout.d.ts +55 -7
- package/dist/esm/layout/styled-layout.js +1154 -169
- package/dist/esm/layout/turned-section.d.ts +38 -0
- package/dist/esm/layout/turned-section.js +205 -0
- package/dist/esm/markdown/markdown-writer.js +17 -6
- package/dist/esm/pdf/styled-page-emitter.js +114 -60
- package/dist/esm/pdf/styled-page-renderer.js +1 -1
- package/dist/esm/pdf-reader/cff-outline.d.ts +27 -0
- package/dist/esm/pdf-reader/cff-outline.js +169 -21
- package/dist/esm/pdf-reader/content.d.ts +53 -0
- package/dist/esm/pdf-reader/content.js +9 -1
- package/dist/esm/pdf-reader/display.d.ts +36 -0
- package/dist/esm/pdf-reader/display.js +66 -1
- package/dist/esm/pdf-reader/document.js +5 -1
- package/dist/esm/pdf-reader/embedded-fonts.d.ts +2 -1
- package/dist/esm/pdf-reader/embedded-fonts.js +32 -2
- package/dist/esm/pdf-reader/encodings.d.ts +8 -0
- package/dist/esm/pdf-reader/encodings.js +25 -3
- package/dist/esm/pdf-reader/face-outlines.d.ts +78 -0
- package/dist/esm/pdf-reader/face-outlines.js +362 -0
- package/dist/esm/pdf-reader/figures.d.ts +52 -0
- package/dist/esm/pdf-reader/figures.js +433 -0
- package/dist/esm/pdf-reader/flow-build.d.ts +131 -5
- package/dist/esm/pdf-reader/flow-build.js +345 -26
- package/dist/esm/pdf-reader/font.js +321 -36
- package/dist/esm/pdf-reader/glyf-outline.d.ts +33 -0
- package/dist/esm/pdf-reader/glyf-outline.js +135 -1
- package/dist/esm/pdf-reader/glyph-names.js +154 -1
- package/dist/esm/pdf-reader/layout.d.ts +93 -0
- package/dist/esm/pdf-reader/layout.js +1703 -214
- package/dist/esm/pdf-reader/page-numbers.d.ts +53 -0
- package/dist/esm/pdf-reader/page-numbers.js +167 -0
- package/dist/esm/pdf-reader/tagged.js +182 -23
- package/dist/esm/pdf-reader/text.d.ts +6 -3
- package/dist/esm/pdf-reader/text.js +98 -9
- package/dist/esm/pdf-reader/type1-outline.d.ts +11 -0
- package/dist/esm/pdf-reader/type1-outline.js +63 -8
- package/dist/esm/pdf-reader/vector.js +71 -1
- package/dist/esm/svg/svg-text.d.ts +58 -0
- package/dist/esm/svg/svg-text.js +322 -0
- package/dist/esm/svg/svg-writer.js +135 -71
- package/dist/esm/word/doc/doc-reader.js +6 -2
- package/dist/esm/word/doc/doc-text.d.ts +6 -0
- package/dist/esm/word/doc/doc-text.js +19 -1
- package/dist/esm/word/document-parser.d.ts +2 -2
- package/dist/esm/word/document-parser.js +11 -1
- package/dist/esm/word/docx-reader.js +5 -3
- package/dist/esm/word/docx-writer.js +218 -23
- package/dist/esm/word/drawing-parser.d.ts +5 -3
- package/dist/esm/word/drawing-parser.js +49 -8
- package/dist/esm/word/font-embed.d.ts +30 -0
- package/dist/esm/word/font-embed.js +173 -0
- package/dist/esm/word/font-table.d.ts +10 -0
- package/dist/esm/word/font-table.js +13 -1
- package/dist/esm/word/index.js +1 -1
- package/dist/esm/word/numbering-parser.d.ts +3 -1
- package/dist/esm/word/numbering-parser.js +2 -1
- package/dist/esm/word/paragraph-properties.d.ts +7 -6
- package/dist/esm/word/paragraph-properties.js +14 -2
- package/dist/esm/word/run-properties.js +26 -0
- package/package.json +12 -3
|
@@ -3,6 +3,7 @@ import { scriptForCodepoint } from "../core/fonts/scripts.js";
|
|
|
3
3
|
import { shapeText } from "../core/font/opentype-layout.js";
|
|
4
4
|
import { hasSubstitutable, substituteLetters } from "../core/font/ligatures.js";
|
|
5
5
|
import { createFontMeasure } from "../core/font/measure.js";
|
|
6
|
+
import { pageItemBounds } from "./page-doc.js";
|
|
6
7
|
import { halfPtToPt, pt } from "../core/ir/units.js";
|
|
7
8
|
import { ResourceStore } from "../core/ir/resources.js";
|
|
8
9
|
import { headingLevelOf } from "../core/outline.js";
|
|
@@ -16,6 +17,7 @@ import { FORCED_BREAK, breakLines } from "../core/line-breaker/knuth-plass.js";
|
|
|
16
17
|
import { greedyBreakLines } from "../core/line-breaker/greedy.js";
|
|
17
18
|
import { cjkBreakBetween, splitCjkSegment } from "../core/line-breaker/cjk.js";
|
|
18
19
|
import "../core/line-breaker/index.js";
|
|
20
|
+
import { formatCounter } from "../core/numbering/state.js";
|
|
19
21
|
import { DEFAULT_RESOLVED_PARAGRAPH, DEFAULT_RESOLVED_RUN } from "../core/style-cascade/types.js";
|
|
20
22
|
import { resolveParagraphProperties, resolveRunProperties } from "../core/style-cascade/resolver.js";
|
|
21
23
|
import "../core/style-cascade/index.js";
|
|
@@ -28,6 +30,7 @@ import { layoutMath, mathGlyphSegments, variantStyle } from "./math-layout.js";
|
|
|
28
30
|
import { rectPath } from "../core/drawingml/preset-geometry.js";
|
|
29
31
|
import { buildShapePaths, buildShapeTransform, buildStroke, gradientToSolid, lineEndPaths } from "../core/drawingml/shape-render.js";
|
|
30
32
|
import { StructTreeBuilder } from "../pdf/struct-tree.js";
|
|
33
|
+
import { floatIntoFrame, turnOntoSheet } from "./turned-section.js";
|
|
31
34
|
//#region src/layout/styled-layout.ts
|
|
32
35
|
function parsePdfAProfile(pdfA) {
|
|
33
36
|
const part = Number(pdfA.charAt(6));
|
|
@@ -39,6 +42,260 @@ function parsePdfAProfile(pdfA) {
|
|
|
39
42
|
version: part === 1 ? "1.4" : "1.7"
|
|
40
43
|
};
|
|
41
44
|
}
|
|
45
|
+
function leadingOf(options) {
|
|
46
|
+
const profile = options.layoutProfile ?? "ream";
|
|
47
|
+
if (profile !== "ream") return profile;
|
|
48
|
+
return options.typesetBy === "word" ? "word" : "flat";
|
|
49
|
+
}
|
|
50
|
+
var FLAT_LEADING = {
|
|
51
|
+
model: "flat",
|
|
52
|
+
markLinePt: 0
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* The single line Word sets a family at, in 2048ths of the size: the ascent,
|
|
56
|
+
* the gap it adds above it, and the descent. It is the face's `hhea` line (the
|
|
57
|
+
* `OS/2` typo line where the face sets USE_TYPO_METRICS), not the `usWin` cell
|
|
58
|
+
* — Book Antiqua stands 1.206 of its size in Word, its cell is 1.243. Measured
|
|
59
|
+
* in Word for Mac on the families marked `*`; the rest read by the same rule
|
|
60
|
+
* from the faces Office ships. The layout draws most of them with a twin or a
|
|
61
|
+
* fallback whose own line differs (Caladea stands 1.15 of its size, Cambria
|
|
62
|
+
* 1.172), so a run's height comes from here, by the family it names, and the
|
|
63
|
+
* face in hand answers only for a family not listed.
|
|
64
|
+
*/
|
|
65
|
+
var WORD_LINE_METRICS = (() => {
|
|
66
|
+
const arial = [
|
|
67
|
+
1854,
|
|
68
|
+
67,
|
|
69
|
+
434
|
|
70
|
+
];
|
|
71
|
+
const times = [
|
|
72
|
+
1825,
|
|
73
|
+
87,
|
|
74
|
+
443
|
|
75
|
+
];
|
|
76
|
+
const courier = [
|
|
77
|
+
1705,
|
|
78
|
+
0,
|
|
79
|
+
615
|
|
80
|
+
];
|
|
81
|
+
const aptos = [
|
|
82
|
+
1923,
|
|
83
|
+
0,
|
|
84
|
+
577
|
|
85
|
+
];
|
|
86
|
+
const franklin = [
|
|
87
|
+
1877,
|
|
88
|
+
0,
|
|
89
|
+
445
|
|
90
|
+
];
|
|
91
|
+
const century = [
|
|
92
|
+
2019,
|
|
93
|
+
0,
|
|
94
|
+
443
|
|
95
|
+
];
|
|
96
|
+
const lucida = [
|
|
97
|
+
1980,
|
|
98
|
+
0,
|
|
99
|
+
432
|
|
100
|
+
];
|
|
101
|
+
return new Map([
|
|
102
|
+
["calibri", [
|
|
103
|
+
1950,
|
|
104
|
+
0,
|
|
105
|
+
550
|
|
106
|
+
]],
|
|
107
|
+
["calibri light", [
|
|
108
|
+
1950,
|
|
109
|
+
0,
|
|
110
|
+
550
|
|
111
|
+
]],
|
|
112
|
+
["carlito", [
|
|
113
|
+
1950,
|
|
114
|
+
0,
|
|
115
|
+
550
|
|
116
|
+
]],
|
|
117
|
+
["cambria", [
|
|
118
|
+
1946,
|
|
119
|
+
0,
|
|
120
|
+
455
|
|
121
|
+
]],
|
|
122
|
+
["arial", arial],
|
|
123
|
+
["helvetica", arial],
|
|
124
|
+
["liberation sans", arial],
|
|
125
|
+
["arimo", arial],
|
|
126
|
+
["arial narrow", [
|
|
127
|
+
1916,
|
|
128
|
+
0,
|
|
129
|
+
434
|
|
130
|
+
]],
|
|
131
|
+
["arial black", [
|
|
132
|
+
2254,
|
|
133
|
+
0,
|
|
134
|
+
634
|
|
135
|
+
]],
|
|
136
|
+
["times new roman", times],
|
|
137
|
+
["times", times],
|
|
138
|
+
["liberation serif", times],
|
|
139
|
+
["tinos", times],
|
|
140
|
+
["courier new", courier],
|
|
141
|
+
["courier", courier],
|
|
142
|
+
["liberation mono", courier],
|
|
143
|
+
["cousine", courier],
|
|
144
|
+
["aptos", aptos],
|
|
145
|
+
["aptos narrow", aptos],
|
|
146
|
+
["aptos light", aptos],
|
|
147
|
+
["aptos semibold", aptos],
|
|
148
|
+
["aptos extrabold", aptos],
|
|
149
|
+
["aptos black", aptos],
|
|
150
|
+
["georgia", [
|
|
151
|
+
1878,
|
|
152
|
+
0,
|
|
153
|
+
449
|
|
154
|
+
]],
|
|
155
|
+
["verdana", [
|
|
156
|
+
2059,
|
|
157
|
+
0,
|
|
158
|
+
430
|
|
159
|
+
]],
|
|
160
|
+
["tahoma", [
|
|
161
|
+
2049,
|
|
162
|
+
0,
|
|
163
|
+
423
|
|
164
|
+
]],
|
|
165
|
+
["trebuchet ms", [
|
|
166
|
+
1923,
|
|
167
|
+
0,
|
|
168
|
+
455
|
|
169
|
+
]],
|
|
170
|
+
["candara", [
|
|
171
|
+
1484,
|
|
172
|
+
452,
|
|
173
|
+
564
|
|
174
|
+
]],
|
|
175
|
+
["consolas", [
|
|
176
|
+
1521,
|
|
177
|
+
350,
|
|
178
|
+
527
|
|
179
|
+
]],
|
|
180
|
+
["constantia", [
|
|
181
|
+
1538,
|
|
182
|
+
452,
|
|
183
|
+
510
|
|
184
|
+
]],
|
|
185
|
+
["corbel", [
|
|
186
|
+
1523,
|
|
187
|
+
425,
|
|
188
|
+
525
|
|
189
|
+
]],
|
|
190
|
+
["century gothic", [
|
|
191
|
+
2060,
|
|
192
|
+
0,
|
|
193
|
+
451
|
|
194
|
+
]],
|
|
195
|
+
["century", century],
|
|
196
|
+
["century schoolbook", century],
|
|
197
|
+
["garamond", [
|
|
198
|
+
1765,
|
|
199
|
+
0,
|
|
200
|
+
539
|
|
201
|
+
]],
|
|
202
|
+
["book antiqua", [
|
|
203
|
+
1891,
|
|
204
|
+
0,
|
|
205
|
+
578
|
|
206
|
+
]],
|
|
207
|
+
["palatino linotype", [
|
|
208
|
+
2150,
|
|
209
|
+
0,
|
|
210
|
+
613
|
|
211
|
+
]],
|
|
212
|
+
["bookman old style", [
|
|
213
|
+
1929,
|
|
214
|
+
0,
|
|
215
|
+
475
|
|
216
|
+
]],
|
|
217
|
+
["rockwell", [
|
|
218
|
+
1937,
|
|
219
|
+
0,
|
|
220
|
+
468
|
|
221
|
+
]],
|
|
222
|
+
["franklin gothic book", franklin],
|
|
223
|
+
["franklin gothic medium", franklin],
|
|
224
|
+
["franklin gothic demi", franklin],
|
|
225
|
+
["gill sans mt", [
|
|
226
|
+
1903,
|
|
227
|
+
0,
|
|
228
|
+
472
|
|
229
|
+
]],
|
|
230
|
+
["tw cen mt", [
|
|
231
|
+
1753,
|
|
232
|
+
0,
|
|
233
|
+
477
|
|
234
|
+
]],
|
|
235
|
+
["lucida sans unicode", [
|
|
236
|
+
2246,
|
|
237
|
+
0,
|
|
238
|
+
901
|
|
239
|
+
]],
|
|
240
|
+
["lucida sans", lucida],
|
|
241
|
+
["lucida bright", lucida],
|
|
242
|
+
["lucida console", [
|
|
243
|
+
1616,
|
|
244
|
+
0,
|
|
245
|
+
432
|
|
246
|
+
]],
|
|
247
|
+
["comic sans ms", [
|
|
248
|
+
2257,
|
|
249
|
+
0,
|
|
250
|
+
597
|
|
251
|
+
]],
|
|
252
|
+
["impact", [
|
|
253
|
+
2066,
|
|
254
|
+
0,
|
|
255
|
+
432
|
|
256
|
+
]],
|
|
257
|
+
["goudy old style", [
|
|
258
|
+
1832,
|
|
259
|
+
140,
|
|
260
|
+
486
|
|
261
|
+
]],
|
|
262
|
+
["calisto mt", [
|
|
263
|
+
1894,
|
|
264
|
+
0,
|
|
265
|
+
470
|
|
266
|
+
]],
|
|
267
|
+
["perpetua", [
|
|
268
|
+
1679,
|
|
269
|
+
0,
|
|
270
|
+
668
|
|
271
|
+
]],
|
|
272
|
+
["symbol", [
|
|
273
|
+
2059,
|
|
274
|
+
0,
|
|
275
|
+
450
|
|
276
|
+
]],
|
|
277
|
+
["wingdings", [
|
|
278
|
+
1841,
|
|
279
|
+
0,
|
|
280
|
+
432
|
|
281
|
+
]],
|
|
282
|
+
["wingdings 2", [
|
|
283
|
+
1727,
|
|
284
|
+
0,
|
|
285
|
+
432
|
|
286
|
+
]],
|
|
287
|
+
["wingdings 3", [
|
|
288
|
+
1900,
|
|
289
|
+
0,
|
|
290
|
+
432
|
|
291
|
+
]],
|
|
292
|
+
["webdings", [
|
|
293
|
+
1638,
|
|
294
|
+
0,
|
|
295
|
+
410
|
|
296
|
+
]]
|
|
297
|
+
]);
|
|
298
|
+
})();
|
|
42
299
|
/** §17.6.4 — how heavy Word draws the rule between columns. */
|
|
43
300
|
var COLUMN_SEPARATOR_PT = .5;
|
|
44
301
|
/**
|
|
@@ -55,7 +312,6 @@ var FOOTNOTE_RULE_PT = .75;
|
|
|
55
312
|
var FOOTNOTE_RULE_GAP_ABOVE = 4;
|
|
56
313
|
var FOOTNOTE_SEPARATOR_HEIGHT = 10;
|
|
57
314
|
var LINE_NUMBER_GAP_PT = 18;
|
|
58
|
-
var FLOAT_TEXT_GAP = 6;
|
|
59
315
|
var MIN_WRAP_WIDTH = 36;
|
|
60
316
|
function isOutOfFlowFloat(f) {
|
|
61
317
|
return f !== void 0 && f.wrap !== "topAndBottom";
|
|
@@ -315,6 +571,7 @@ function layoutStyledDocument(body, options) {
|
|
|
315
571
|
const blocks = numberedBody.map((el, idx) => {
|
|
316
572
|
while (sectionIdx < sectionCtxs.length - 1 && idx >= sectionCtxs[sectionIdx].endIndex) sectionIdx++;
|
|
317
573
|
const ctx = sectionCtxs[sectionIdx];
|
|
574
|
+
const own = ctx.sheet ? floatIntoFrame(el, ctx.sheet) : el;
|
|
318
575
|
const width = ctx.columns ? ctx.columns[0].widthPt : ctx.contentWidth;
|
|
319
576
|
const box = {
|
|
320
577
|
pageWidthPt: ctx.pageWidth,
|
|
@@ -327,7 +584,7 @@ function layoutStyledDocument(body, options) {
|
|
|
327
584
|
const group = bodyFrames.starts.get(idx);
|
|
328
585
|
if (group) return layoutFrameBlock(group, ctx.options ?? options, fontResources, imageResources, width);
|
|
329
586
|
if (bodyFrames.continued.has(idx)) return emptyBlock(options);
|
|
330
|
-
return layoutBodyElement(
|
|
587
|
+
return layoutBodyElement(own, ctx.options ?? options, fontResources, imageResources, width, ctx.pageContentHeight, box);
|
|
331
588
|
});
|
|
332
589
|
for (let i = 0; i < blocks.length; i++) {
|
|
333
590
|
const here = blocks[i];
|
|
@@ -359,9 +616,15 @@ function layoutStyledDocument(body, options) {
|
|
|
359
616
|
let laid = void 0;
|
|
360
617
|
if (content && n !== void 0 && !noteAssigned.deferredFootnotes.includes(id)) {
|
|
361
618
|
const noteBlocks = substituteNoteNumber(content, n).map((el) => layoutBodyElement(el, sectionCtx.options ?? options, fontResources, imageResources, sectionCtx.contentWidth, sectionCtx.pageContentHeight));
|
|
619
|
+
const spacing = new SpacingStack(collapsesSpacing(sectionCtx.options ?? options));
|
|
362
620
|
laid = {
|
|
363
621
|
blocks: noteBlocks,
|
|
364
|
-
heightPt: noteBlocks.reduce((sum, b) =>
|
|
622
|
+
heightPt: noteBlocks.reduce((sum, b) => {
|
|
623
|
+
if (b.kind !== "paragraph") return sum;
|
|
624
|
+
const before = spacing.open(b.spacingBeforePt);
|
|
625
|
+
spacing.close(b.spacingAfterPt);
|
|
626
|
+
return sum + before + b.heightPt + b.spacingAfterPt;
|
|
627
|
+
}, 0)
|
|
365
628
|
};
|
|
366
629
|
}
|
|
367
630
|
byId.set(id, laid);
|
|
@@ -435,7 +698,7 @@ function layoutStyledDocument(body, options) {
|
|
|
435
698
|
const commentNotes = options.commentAnnotations && options.comments ? buildCommentNotes(noteAssigned.comments, options.comments) : void 0;
|
|
436
699
|
const reflowParagraph = (paragraph, width, widths) => layoutParagraphBlock(paragraph, options, fontResources, imageResources, width, widths);
|
|
437
700
|
return {
|
|
438
|
-
pages: paginateSections(blocks, sectionCtxs, structBuilder, options.language ?? "en-US", notePlan, bookmarks, reflowParagraph, backgroundImageOf(options, imageResources)),
|
|
701
|
+
pages: paginateSections(blocks, sectionCtxs, structBuilder, options.language ?? "en-US", notePlan, bookmarks, reflowParagraph, backgroundImageOf(options, imageResources), collapsesSpacing(options), options.compatibilityMode),
|
|
439
702
|
resources: options.resources ?? new ResourceStore(),
|
|
440
703
|
fontResources,
|
|
441
704
|
imageResources,
|
|
@@ -509,9 +772,52 @@ function buildSectionContext(section, options, headersFooters, fontResources, im
|
|
|
509
772
|
bottom: dims.marginBottom
|
|
510
773
|
}, relativeBox);
|
|
511
774
|
const columns = buildColumnGeometry(section.properties.columns, contentWidth);
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
775
|
+
const bodyMarginsFor = (band) => {
|
|
776
|
+
const header = pickBand(headerSet, band).heightPt ?? 0;
|
|
777
|
+
const footer = pickBand(footerSet, band).heightPt ?? 0;
|
|
778
|
+
return {
|
|
779
|
+
top: Math.max(dims.marginTop, header > 0 ? dims.headerOffsetPt + header : 0),
|
|
780
|
+
bottom: Math.max(dims.marginBottom, footer > 0 ? dims.footerOffsetPt + footer : 0)
|
|
781
|
+
};
|
|
782
|
+
};
|
|
783
|
+
const bandMargins = {
|
|
784
|
+
default: bodyMarginsFor("default"),
|
|
785
|
+
first: bodyMarginsFor("first"),
|
|
786
|
+
even: bodyMarginsFor("even")
|
|
787
|
+
};
|
|
788
|
+
const marginTop = bandMargins.default.top;
|
|
789
|
+
const marginBottom = bandMargins.default.bottom;
|
|
790
|
+
if (section.properties.textDirection !== void 0) {
|
|
791
|
+
const sheet = {
|
|
792
|
+
width: dims.pageWidth,
|
|
793
|
+
height: dims.pageHeight,
|
|
794
|
+
marginLeft: dims.marginLeft,
|
|
795
|
+
marginRight: dims.marginRight,
|
|
796
|
+
marginTop,
|
|
797
|
+
marginBottom
|
|
798
|
+
};
|
|
799
|
+
const across = sheet.height - sheet.marginTop - sheet.marginBottom;
|
|
800
|
+
const turnedColumns = buildColumnGeometry(section.properties.columns, across);
|
|
801
|
+
return {
|
|
802
|
+
endIndex: section.endIndex,
|
|
803
|
+
properties: section.properties,
|
|
804
|
+
pageWidth: sheet.height,
|
|
805
|
+
pageHeight: sheet.width,
|
|
806
|
+
marginLeft: sheet.marginTop,
|
|
807
|
+
marginTop: sheet.marginRight,
|
|
808
|
+
marginBottom: sheet.marginLeft,
|
|
809
|
+
contentWidth: across,
|
|
810
|
+
pageContentHeight: sheet.width - sheet.marginRight - sheet.marginLeft,
|
|
811
|
+
...turnedColumns ? { columns: turnedColumns } : {},
|
|
812
|
+
headerSet,
|
|
813
|
+
footerSet,
|
|
814
|
+
titlePg: section.properties.titlePg === true,
|
|
815
|
+
evenAndOddHeaders: section.properties.evenAndOddHeaders === true,
|
|
816
|
+
continuous: section.properties.sectionStart === "continuous",
|
|
817
|
+
options: secOptions,
|
|
818
|
+
sheet
|
|
819
|
+
};
|
|
820
|
+
}
|
|
515
821
|
return {
|
|
516
822
|
endIndex: section.endIndex,
|
|
517
823
|
properties: section.properties,
|
|
@@ -519,12 +825,13 @@ function buildSectionContext(section, options, headersFooters, fontResources, im
|
|
|
519
825
|
pageHeight: dims.pageHeight,
|
|
520
826
|
marginLeft: dims.marginLeft,
|
|
521
827
|
marginTop,
|
|
522
|
-
marginBottom
|
|
828
|
+
marginBottom,
|
|
523
829
|
contentWidth,
|
|
524
|
-
pageContentHeight: dims.pageHeight - marginTop -
|
|
830
|
+
pageContentHeight: dims.pageHeight - marginTop - marginBottom,
|
|
525
831
|
...columns ? { columns } : {},
|
|
526
832
|
headerSet,
|
|
527
833
|
footerSet,
|
|
834
|
+
bandMargins,
|
|
528
835
|
titlePg: section.properties.titlePg === true,
|
|
529
836
|
evenAndOddHeaders: section.properties.evenAndOddHeaders === true,
|
|
530
837
|
continuous: section.properties.sectionStart === "continuous",
|
|
@@ -572,15 +879,15 @@ function layoutHeaderSet(section, headersFooters, options, fontResources, imageR
|
|
|
572
879
|
let measured = 0;
|
|
573
880
|
const render = (c) => {
|
|
574
881
|
const blocks = laidOutBlocksFor(c, options, fontResources, contentWidth, imageResources, relativeBox);
|
|
575
|
-
measured = blocksHeight(blocks);
|
|
576
|
-
return markPagination(drawBlocksSequentially(blocks, marginLeft, pageHeight - headerOffsetPt, pageHeight, contentWidth, void 0, bandMargins));
|
|
882
|
+
measured = blocksHeight(blocks, collapsesSpacing(options));
|
|
883
|
+
return markPagination(drawBlocksSequentially(blocks, marginLeft, pageHeight - headerOffsetPt, pageHeight, contentWidth, void 0, bandMargins, collapsesSpacing(options)));
|
|
577
884
|
};
|
|
578
885
|
if (contentHasPageFields(content)) {
|
|
579
|
-
render(substitutePageFields(content, 1, 1));
|
|
886
|
+
render(substitutePageFields(content, "1", 1));
|
|
580
887
|
return {
|
|
581
888
|
commands: [],
|
|
582
889
|
heightPt: measured,
|
|
583
|
-
renderDynamic: (
|
|
890
|
+
renderDynamic: (page, total) => render(substitutePageFields(content, page, total))
|
|
584
891
|
};
|
|
585
892
|
}
|
|
586
893
|
return {
|
|
@@ -609,11 +916,61 @@ function balancedColumnHeight(bandHeightPt, colCount, roomPt) {
|
|
|
609
916
|
if (bandHeightPt > roomPt * colCount) return 0;
|
|
610
917
|
return Math.min(roomPt, bandHeightPt / colCount);
|
|
611
918
|
}
|
|
612
|
-
/**
|
|
613
|
-
|
|
919
|
+
/**
|
|
920
|
+
* §17.3.1.33 — the space between paragraphs down one story (a page, a cell, a
|
|
921
|
+
* band, a box). Where the document is set as Word sets it, two paragraphs
|
|
922
|
+
* stand the LARGER of the first's space after and the second's space before
|
|
923
|
+
* apart, not the two together. Measured in Word for Mac, in modes 14 and 15
|
|
924
|
+
* alike: 10pt after and 10pt before stand 10pt apart; 10 and 24 stand 24,
|
|
925
|
+
* whichever comes first; 6 and 12 stand 12 either way. An empty paragraph
|
|
926
|
+
* between takes its share of both sides. Elsewhere the two add up.
|
|
927
|
+
*/
|
|
928
|
+
var SpacingStack = class {
|
|
929
|
+
/** The space after the last block, which a paragraph right below may share. */
|
|
930
|
+
shared = 0;
|
|
931
|
+
constructor(collapse) {
|
|
932
|
+
this.collapse = collapse;
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* @param beforePt A block's own space before.
|
|
936
|
+
* @returns The room to open above it.
|
|
937
|
+
*/
|
|
938
|
+
open(beforePt) {
|
|
939
|
+
const room = this.collapse ? Math.max(0, beforePt - this.shared) : beforePt;
|
|
940
|
+
this.shared = 0;
|
|
941
|
+
return room;
|
|
942
|
+
}
|
|
943
|
+
/** @param spacingPt The space a block just left below itself, as far as a paragraph may share it. */
|
|
944
|
+
close(spacingPt) {
|
|
945
|
+
this.shared = spacingPt;
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
/** Whether a layout stacks paragraph spacing as Word does (see {@link SpacingStack}). */
|
|
949
|
+
function collapsesSpacing(options) {
|
|
950
|
+
return options.typesetBy === "word";
|
|
951
|
+
}
|
|
952
|
+
/** A block's space after, less the line a picture's spacing adds under it: the part a paragraph below shares. */
|
|
953
|
+
function sharedAfter(block) {
|
|
954
|
+
return block.spacingAfterPt - (block.lineExtraPt ?? 0);
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Total laid-out height of a run of blocks, paragraph spacing included.
|
|
958
|
+
*
|
|
959
|
+
* @param blocks The blocks, in order.
|
|
960
|
+
* @param collapse Whether their spacing stacks as Word stacks it (see {@link SpacingStack}).
|
|
961
|
+
* @returns The height in points.
|
|
962
|
+
*/
|
|
963
|
+
function blocksHeight(blocks, collapse = false) {
|
|
964
|
+
const spacing = new SpacingStack(collapse);
|
|
614
965
|
return blocks.reduce((sum, b) => {
|
|
615
966
|
if (b.kind !== "paragraph" && isOutOfFlowFloat(b.float)) return sum;
|
|
616
|
-
|
|
967
|
+
if (!("spacingBeforePt" in b)) {
|
|
968
|
+
spacing.close(0);
|
|
969
|
+
return sum + b.heightPt;
|
|
970
|
+
}
|
|
971
|
+
const before = spacing.open(b.spacingBeforePt);
|
|
972
|
+
spacing.close(sharedAfter(b));
|
|
973
|
+
return sum + before + b.heightPt + b.spacingAfterPt;
|
|
617
974
|
}, 0);
|
|
618
975
|
}
|
|
619
976
|
function layoutFooterSet(section, headersFooters, options, fontResources, imageResources, contentWidth, marginLeft, pageHeight, footerOffsetPt, bandMargins, relativeBox) {
|
|
@@ -622,15 +979,25 @@ function layoutFooterSet(section, headersFooters, options, fontResources, imageR
|
|
|
622
979
|
if (!ref) return { commands: [] };
|
|
623
980
|
const content = headersFooters.get(ref.relationshipId);
|
|
624
981
|
if (!content) return { commands: [] };
|
|
982
|
+
let measured = 0;
|
|
625
983
|
const render = (c) => {
|
|
626
984
|
const blocks = laidOutBlocksFor(c, options, fontResources, contentWidth, imageResources, relativeBox);
|
|
627
|
-
|
|
985
|
+
const totalHeight = blocksHeight(blocks, collapsesSpacing(options));
|
|
986
|
+
measured = totalHeight;
|
|
987
|
+
return markPagination(drawBlocksSequentially(blocks, marginLeft, footerOffsetPt + totalHeight, pageHeight, contentWidth, void 0, bandMargins, collapsesSpacing(options)));
|
|
628
988
|
};
|
|
629
|
-
if (contentHasPageFields(content))
|
|
630
|
-
|
|
631
|
-
|
|
989
|
+
if (contentHasPageFields(content)) {
|
|
990
|
+
render(substitutePageFields(content, "1", 1));
|
|
991
|
+
return {
|
|
992
|
+
commands: [],
|
|
993
|
+
heightPt: measured,
|
|
994
|
+
renderDynamic: (page, total) => render(substitutePageFields(content, page, total))
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
return {
|
|
998
|
+
commands: render(content),
|
|
999
|
+
heightPt: measured
|
|
632
1000
|
};
|
|
633
|
-
return { commands: render(content) };
|
|
634
1001
|
};
|
|
635
1002
|
return {
|
|
636
1003
|
default: band("default"),
|
|
@@ -642,7 +1009,20 @@ function contentHasPageFields(content) {
|
|
|
642
1009
|
for (const el of content) if (el.kind === "paragraph" && el.paragraph.runs.some((r) => r.field !== void 0)) return true;
|
|
643
1010
|
return false;
|
|
644
1011
|
}
|
|
645
|
-
|
|
1012
|
+
/**
|
|
1013
|
+
* §17.6.12 — a page's number as its section prints it: in the numerals
|
|
1014
|
+
* `w:pgNumType w:fmt` names (§17.18.59), decimal where it names none. A format
|
|
1015
|
+
* that has no numeral for the number — a roman zero — prints it in digits.
|
|
1016
|
+
*
|
|
1017
|
+
* @param pageNumber The page's number in its section's count.
|
|
1018
|
+
* @param format The section's page-number format.
|
|
1019
|
+
* @returns The text a PAGE field shows on that page.
|
|
1020
|
+
*/
|
|
1021
|
+
function pageNumberText(pageNumber, format) {
|
|
1022
|
+
const text = format === void 0 ? "" : formatCounter(format, pageNumber);
|
|
1023
|
+
return text !== "" ? text : String(pageNumber);
|
|
1024
|
+
}
|
|
1025
|
+
function substitutePageFields(content, pageText, totalPages) {
|
|
646
1026
|
return content.map((el) => {
|
|
647
1027
|
if (el.kind !== "paragraph") return el;
|
|
648
1028
|
if (!el.paragraph.runs.some((r) => r.field !== void 0)) return el;
|
|
@@ -652,7 +1032,7 @@ function substitutePageFields(content, pageNumber, totalPages) {
|
|
|
652
1032
|
...el.paragraph,
|
|
653
1033
|
runs: el.paragraph.runs.map((r) => r.field === void 0 ? r : {
|
|
654
1034
|
...r,
|
|
655
|
-
text:
|
|
1035
|
+
text: r.field === "PAGE" ? pageText : String(totalPages)
|
|
656
1036
|
})
|
|
657
1037
|
}
|
|
658
1038
|
};
|
|
@@ -953,12 +1333,33 @@ function layoutBodyElement(el, options, fontResources, imageResources, contentWi
|
|
|
953
1333
|
if (el.kind === "image") {
|
|
954
1334
|
const res = el.image.resource ? imageResources?.get(el.image.resource) : void 0;
|
|
955
1335
|
const meta = res?.metafile;
|
|
956
|
-
return meta ? layoutMetafileBlock(el.image, meta, res.metafileImages, options, fontResources, contentWidth, maxHeight, box) : layoutImageBlock(el.image, imageResources, contentWidth, maxHeight, box);
|
|
1336
|
+
return meta ? layoutMetafileBlock(el.image, meta, res.metafileImages, options, fontResources, contentWidth, maxHeight, box) : layoutImageBlock(el.image, imageResources, contentWidth, maxHeight, box, options);
|
|
957
1337
|
}
|
|
958
1338
|
if (el.kind === "chart") return layoutChartBlock(el.chart, options, fontResources, contentWidth, maxHeight, imageResources);
|
|
959
1339
|
return layoutShapeBlock(el.shape, options, fontResources, imageResources, contentWidth, maxHeight, box);
|
|
960
1340
|
}
|
|
961
|
-
|
|
1341
|
+
/**
|
|
1342
|
+
* §17.3.1.33 — what a paragraph's line spacing adds under a picture that
|
|
1343
|
+
* stands on a line of its own. Word sets that line as tall as the picture,
|
|
1344
|
+
* and a spacing of more than one line adds what the extra lines of the
|
|
1345
|
+
* paragraph mark's font would, not a share of the picture: in Word for Mac a
|
|
1346
|
+
* 100pt picture's next paragraph stands 100, 102.5 and 113 points down at
|
|
1347
|
+
* single, 1.15 and double spacing of an 11pt mark.
|
|
1348
|
+
*
|
|
1349
|
+
* @param pp The paragraph the picture stands on, resolved.
|
|
1350
|
+
* @param options The render options its mark's font resolves against; absent
|
|
1351
|
+
* for a picture out of the flow.
|
|
1352
|
+
* @returns The extra points, 0 where the spacing is not a multiple above one.
|
|
1353
|
+
*/
|
|
1354
|
+
function pictureLineExtra(pp, options) {
|
|
1355
|
+
if (!options) return 0;
|
|
1356
|
+
if (pp.spacingLine === void 0 || (pp.spacingLineRule ?? "auto") !== "auto") return 0;
|
|
1357
|
+
const multiple = Math.round(pp.spacingLine * 20) / 240;
|
|
1358
|
+
if (multiple <= 1) return 0;
|
|
1359
|
+
if (leadingOf(options) !== "flat") return (multiple - 1) * paragraphLeading(pp, options).markLinePt;
|
|
1360
|
+
return (multiple - 1) * resolveRunProperties({}, pp, options.styles).fontSizePt * 1.2;
|
|
1361
|
+
}
|
|
1362
|
+
function layoutImageBlock(image, imageResources, contentWidth, maxHeight, box, options) {
|
|
962
1363
|
let widthPt = relativeWidth(image.relativeSize, contentWidth, box) ?? image.width;
|
|
963
1364
|
let heightPt = relativeHeight(image.relativeSize, maxHeight, box) ?? (image.relativeSize?.widthPct && image.width > 0 ? image.height * (widthPt / image.width) : image.height);
|
|
964
1365
|
if (widthPt > contentWidth && !image.float) {
|
|
@@ -993,6 +1394,7 @@ function layoutImageBlock(image, imageResources, contentWidth, maxHeight, box) {
|
|
|
993
1394
|
heightPt = reservedH;
|
|
994
1395
|
drawInset = box;
|
|
995
1396
|
}
|
|
1397
|
+
const lineExtraPt = image.float ? 0 : pictureLineExtra(image.paragraphProperties, options);
|
|
996
1398
|
const res = image.resource ? imageResources?.get(image.resource) : void 0;
|
|
997
1399
|
const resolvedAlignment = image.paragraphProperties.alignment ?? "left";
|
|
998
1400
|
return {
|
|
@@ -1010,7 +1412,8 @@ function layoutImageBlock(image, imageResources, contentWidth, maxHeight, box) {
|
|
|
1010
1412
|
...image.flipV ? { flipV: true } : {},
|
|
1011
1413
|
...drawInset ? { drawInset } : {},
|
|
1012
1414
|
spacingBeforePt: image.paragraphProperties.spacingBefore ?? 0,
|
|
1013
|
-
spacingAfterPt: image.paragraphProperties.spacingAfter ?? 0,
|
|
1415
|
+
spacingAfterPt: (image.paragraphProperties.spacingAfter ?? 0) + lineExtraPt,
|
|
1416
|
+
...lineExtraPt > 0 ? { lineExtraPt } : {},
|
|
1014
1417
|
...image.altText ? { altText: image.altText } : {},
|
|
1015
1418
|
...image.float ? { float: image.float } : {}
|
|
1016
1419
|
};
|
|
@@ -1054,8 +1457,10 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1054
1457
|
const upright = text?.upright;
|
|
1055
1458
|
const warp = text?.warp;
|
|
1056
1459
|
if (text && text.content.length > 0) {
|
|
1057
|
-
const innerWidth = warp ? Number.MAX_SAFE_INTEGER : vertical ? Math.max(1, heightPt - insetTopPt - insetBottomPt) : Math.max(1, widthPt - insetLeftPt - insetRightPt);
|
|
1460
|
+
const innerWidth = warp || text.noWrap === true ? Number.MAX_SAFE_INTEGER : vertical ? Math.max(1, heightPt - insetTopPt - insetBottomPt) : Math.max(1, widthPt - insetLeftPt - insetRightPt);
|
|
1461
|
+
const spacing = new SpacingStack(collapsesSpacing(options));
|
|
1058
1462
|
for (const el of text.content) {
|
|
1463
|
+
if (el.kind !== "paragraph") spacing.close(0);
|
|
1059
1464
|
if (el.kind === "image") {
|
|
1060
1465
|
const res = el.image.resource ? imageResources?.get(el.image.resource) : void 0;
|
|
1061
1466
|
const scaled = Math.min(1, innerWidth / Math.max(1, el.image.width));
|
|
@@ -1129,7 +1534,8 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1129
1534
|
continue;
|
|
1130
1535
|
}
|
|
1131
1536
|
const blk = layoutParagraphBlock(el.paragraph, options, fontResources, imageResources, innerWidth);
|
|
1132
|
-
const
|
|
1537
|
+
const opened = spacing.open(blk.spacingBeforePt);
|
|
1538
|
+
const before = textLines.length > 0 ? opened : 0;
|
|
1133
1539
|
if (before > 0) {
|
|
1134
1540
|
const last = textLines.length - 1;
|
|
1135
1541
|
textLineGaps.set(last, (textLineGaps.get(last) ?? 0) + before);
|
|
@@ -1144,6 +1550,7 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1144
1550
|
textLineGaps.set(last, (textLineGaps.get(last) ?? 0) + blk.spacingAfterPt);
|
|
1145
1551
|
}
|
|
1146
1552
|
textHeightPt += blk.spacingAfterPt;
|
|
1553
|
+
spacing.close(blk.spacingAfterPt);
|
|
1147
1554
|
}
|
|
1148
1555
|
}
|
|
1149
1556
|
if (text?.autoFit && !vertical && textLines.length > 0) heightPt = textHeightPt + insetTopPt + insetBottomPt;
|
|
@@ -1259,6 +1666,7 @@ function layoutMetafileBlock(image, pic, blitted, options, fontResources, conten
|
|
|
1259
1666
|
const heightPt = placed.heightPt;
|
|
1260
1667
|
const layout = rotateDrawing(metafileDrawing(pic, widthPt, heightPt, options, fontResources, image.colorChange, blitted), (image.rotation60k ?? 0) / 6e4, widthPt / 2, heightPt / 2);
|
|
1261
1668
|
const pp = image.paragraphProperties;
|
|
1669
|
+
const lineExtraPt = image.float ? 0 : pictureLineExtra(pp, options);
|
|
1262
1670
|
return {
|
|
1263
1671
|
kind: "chart",
|
|
1264
1672
|
widthPt,
|
|
@@ -1266,7 +1674,8 @@ function layoutMetafileBlock(image, pic, blitted, options, fontResources, conten
|
|
|
1266
1674
|
layout,
|
|
1267
1675
|
resolvedAlignment: pp.alignment ?? "left",
|
|
1268
1676
|
spacingBeforePt: pp.spacingBefore ?? 0,
|
|
1269
|
-
spacingAfterPt: pp.spacingAfter ?? 0,
|
|
1677
|
+
spacingAfterPt: (pp.spacingAfter ?? 0) + lineExtraPt,
|
|
1678
|
+
...lineExtraPt > 0 ? { lineExtraPt } : {},
|
|
1270
1679
|
figureRole: "Image",
|
|
1271
1680
|
...image.altText ? { altText: image.altText } : {},
|
|
1272
1681
|
...image.float ? { float: image.float } : {}
|
|
@@ -1433,7 +1842,7 @@ function metafileDrawing(pic, widthPt, heightPt, options, fontResources, colorCh
|
|
|
1433
1842
|
function layoutChartBlock(block, options, fontResources, contentWidth, maxHeight, imageResources) {
|
|
1434
1843
|
let widthPt = block.width;
|
|
1435
1844
|
let heightPt = block.height;
|
|
1436
|
-
if (widthPt > contentWidth && widthPt > 0) {
|
|
1845
|
+
if (widthPt > contentWidth && widthPt > 0 && !isOutOfFlowFloat(block.float)) {
|
|
1437
1846
|
const scale = contentWidth / widthPt;
|
|
1438
1847
|
widthPt = contentWidth;
|
|
1439
1848
|
heightPt *= scale;
|
|
@@ -1503,8 +1912,13 @@ function rectAtPath(x, y, w, h) {
|
|
|
1503
1912
|
return new PathBuilder().moveTo(x, y).lineTo(x + w, y).lineTo(x + w, y + h).lineTo(x, y + h).close().build();
|
|
1504
1913
|
}
|
|
1505
1914
|
var CF_ICON_SIZE_PT = 9;
|
|
1915
|
+
/** How far inside its cell Excel keeps a data bar, on every side — 2px. */
|
|
1916
|
+
var DATA_BAR_INSET_PT = 1.5;
|
|
1506
1917
|
var CF_ICON_GUTTER_PT = 12;
|
|
1507
1918
|
var CF_ICON_EMPTY_HEX = "BFBFBF";
|
|
1919
|
+
var NOTE_FLAG_PT = 4.5;
|
|
1920
|
+
var NOTE_FLAG_HEX = "FF0000";
|
|
1921
|
+
var NOTE_THREAD_HEX = "7030A0";
|
|
1508
1922
|
var DROPDOWN_BUTTON_PT = 11;
|
|
1509
1923
|
var DROPDOWN_FILL_HEX = "F0F0F0";
|
|
1510
1924
|
var DROPDOWN_BORDER_HEX = "B0B0B0";
|
|
@@ -1730,8 +2144,10 @@ function collectImageResources(body, headersFooters, options) {
|
|
|
1730
2144
|
if (el.image.resource) seen.add(el.image.resource);
|
|
1731
2145
|
} else if (el.kind === "paragraph") {
|
|
1732
2146
|
for (const run of el.paragraph.runs) if (run.inlineImage?.resource) seen.add(run.inlineImage.resource);
|
|
1733
|
-
} else if (el.kind === "table")
|
|
1734
|
-
|
|
2147
|
+
} else if (el.kind === "table") {
|
|
2148
|
+
for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
|
|
2149
|
+
visit(el.table.overlay?.drawings ?? []);
|
|
2150
|
+
} else if (el.kind === "shape") {
|
|
1735
2151
|
const shapeImages = (sh) => {
|
|
1736
2152
|
if (sh.fill.imageResource) seen.add(sh.fill.imageResource);
|
|
1737
2153
|
if (sh.text) visit(sh.text.content);
|
|
@@ -2059,28 +2475,47 @@ function emitCellFloat(cf, ctx, frame) {
|
|
|
2059
2475
|
if (!float) return;
|
|
2060
2476
|
const x = floatXIn(frame, float, cf.block.widthPt);
|
|
2061
2477
|
const topYUp = floatTopYUpIn(frame, float, cf.block.heightPt);
|
|
2062
|
-
|
|
2063
|
-
if (cf.block.kind === "shape") emitShapeItems(cf.block, x, topYUp - cf.block.heightPt, sink, ctx.pageHeight, void 0);
|
|
2064
|
-
else {
|
|
2065
|
-
const ins = cf.block.drawInset;
|
|
2066
|
-
sink.push({
|
|
2067
|
-
type: "image",
|
|
2068
|
-
x: pt(x + (ins?.dxPt ?? 0)),
|
|
2069
|
-
y: pt(ctx.pageHeight - (topYUp - (ins?.dyTopPt ?? 0))),
|
|
2070
|
-
width: pt(ins?.widthPt ?? cf.block.widthPt),
|
|
2071
|
-
height: pt(ins?.heightPt ?? cf.block.heightPt),
|
|
2072
|
-
imageResourceName: cf.block.resourceName,
|
|
2073
|
-
...cf.block.crop ? { crop: cf.block.crop } : {},
|
|
2074
|
-
...cf.block.wash ? { wash: cf.block.wash } : {},
|
|
2075
|
-
...cf.block.alpha !== void 0 ? { alpha: cf.block.alpha } : {},
|
|
2076
|
-
...cf.block.rotationDeg ? { rotationDeg: cf.block.rotationDeg } : {},
|
|
2077
|
-
...cf.block.flipH ? { flipH: true } : {},
|
|
2078
|
-
...cf.block.flipV ? { flipV: true } : {}
|
|
2079
|
-
});
|
|
2080
|
-
if (cf.block.outline) sink.push(...pictureOutlineItems(cf.block.outline, x + (ins?.dxPt ?? 0), ctx.pageHeight - (topYUp - (ins?.dyTopPt ?? 0)), ins?.widthPt ?? cf.block.widthPt, ins?.heightPt ?? cf.block.heightPt, void 0));
|
|
2081
|
-
}
|
|
2478
|
+
emitDrawing(cf.block, x, topYUp, ctx.sink(float.behind === true, float.zOrder), ctx.pageHeight, void 0);
|
|
2082
2479
|
if (float.wrap !== "none") ctx.exclude(float, x, topYUp, cf.block.widthPt, cf.block.heightPt);
|
|
2083
2480
|
}
|
|
2481
|
+
/**
|
|
2482
|
+
* A drawing out of the flow — a picture, a shape or a chart — in the items it
|
|
2483
|
+
* paints, its top-left corner at `(x, topYUp)` in the y-up cursor frame.
|
|
2484
|
+
*
|
|
2485
|
+
* @param block The drawing, laid out.
|
|
2486
|
+
* @param x Its left edge on the page.
|
|
2487
|
+
* @param topYUp Its top, y-up.
|
|
2488
|
+
* @param sink Where its items go.
|
|
2489
|
+
* @param pageHeight The page's height, for the flip into the page frame.
|
|
2490
|
+
* @param figId The tagged-PDF Figure its items belong to, if any.
|
|
2491
|
+
*/
|
|
2492
|
+
function emitDrawing(block, x, topYUp, sink, pageHeight, figId) {
|
|
2493
|
+
if (block.kind === "shape") {
|
|
2494
|
+
emitShapeItems(block, x, topYUp - block.heightPt, sink, pageHeight, figId);
|
|
2495
|
+
return;
|
|
2496
|
+
}
|
|
2497
|
+
if (block.kind === "chart") {
|
|
2498
|
+
sink.push(...chartPageItems(block, x, topYUp - block.heightPt, pageHeight, figId));
|
|
2499
|
+
return;
|
|
2500
|
+
}
|
|
2501
|
+
const ins = block.drawInset;
|
|
2502
|
+
sink.push({
|
|
2503
|
+
type: "image",
|
|
2504
|
+
x: pt(x + (ins?.dxPt ?? 0)),
|
|
2505
|
+
y: pt(pageHeight - (topYUp - (ins?.dyTopPt ?? 0))),
|
|
2506
|
+
width: pt(ins?.widthPt ?? block.widthPt),
|
|
2507
|
+
height: pt(ins?.heightPt ?? block.heightPt),
|
|
2508
|
+
imageResourceName: block.resourceName,
|
|
2509
|
+
...block.crop ? { crop: block.crop } : {},
|
|
2510
|
+
...block.wash ? { wash: block.wash } : {},
|
|
2511
|
+
...block.alpha !== void 0 ? { alpha: block.alpha } : {},
|
|
2512
|
+
...block.rotationDeg ? { rotationDeg: block.rotationDeg } : {},
|
|
2513
|
+
...block.flipH ? { flipH: true } : {},
|
|
2514
|
+
...block.flipV ? { flipV: true } : {},
|
|
2515
|
+
...figId !== void 0 ? { structId: figId } : {}
|
|
2516
|
+
});
|
|
2517
|
+
if (block.outline) sink.push(...pictureOutlineItems(block.outline, x + (ins?.dxPt ?? 0), pageHeight - (topYUp - (ins?.dyTopPt ?? 0)), ins?.widthPt ?? block.widthPt, ins?.heightPt ?? block.heightPt, figId));
|
|
2518
|
+
}
|
|
2084
2519
|
function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
|
|
2085
2520
|
const castsUnderPicture = sh.fillImageResourceName !== void 0 ? sh.shadow : void 0;
|
|
2086
2521
|
const shadowedFill = castsUnderPicture !== void 0;
|
|
@@ -2231,9 +2666,10 @@ function bandAnchorTop(float, cursorY, pageHeight, heightPt = 0, margins) {
|
|
|
2231
2666
|
const offset = float.posV?.offsetPt ?? 0;
|
|
2232
2667
|
return float.posV?.relativeFrom === "page" ? pageHeight - offset : cursorY - offset;
|
|
2233
2668
|
}
|
|
2234
|
-
function drawBlocksSequentially(blocks, startX, startY, pageHeight, contentWidth, structId, bandMargins) {
|
|
2669
|
+
function drawBlocksSequentially(blocks, startX, startY, pageHeight, contentWidth, structId, bandMargins, collapse = false) {
|
|
2235
2670
|
const out = [];
|
|
2236
2671
|
let cursorY = startY;
|
|
2672
|
+
const spacing = new SpacingStack(collapse);
|
|
2237
2673
|
const isBehind = (b) => "float" in b && b.float?.behind === true;
|
|
2238
2674
|
const ordered = blocks.some(isBehind) ? [...blocks.filter(isBehind), ...blocks.filter((b) => !isBehind(b))] : blocks;
|
|
2239
2675
|
for (const block of ordered) {
|
|
@@ -2252,18 +2688,20 @@ function drawBlocksSequentially(blocks, startX, startY, pageHeight, contentWidth
|
|
|
2252
2688
|
emitRowChunk(out, row, tableX, cursorY, pageHeight, block.colCount);
|
|
2253
2689
|
cursorY -= row.heightPt;
|
|
2254
2690
|
}
|
|
2691
|
+
spacing.close(0);
|
|
2255
2692
|
continue;
|
|
2256
2693
|
}
|
|
2257
2694
|
if (block.kind === "chart") {
|
|
2258
|
-
cursorY -= block.spacingBeforePt + block.heightPt;
|
|
2695
|
+
cursorY -= spacing.open(block.spacingBeforePt) + block.heightPt;
|
|
2259
2696
|
const offset = alignmentOffset(block.resolvedAlignment, block.widthPt, contentWidth);
|
|
2260
2697
|
out.push(...chartPageItems(block, startX + offset, cursorY, pageHeight, structId));
|
|
2261
2698
|
cursorY -= block.spacingAfterPt;
|
|
2699
|
+
spacing.close(sharedAfter(block));
|
|
2262
2700
|
continue;
|
|
2263
2701
|
}
|
|
2264
2702
|
if (block.kind === "image") {
|
|
2265
|
-
cursorY -= block.spacingBeforePt;
|
|
2266
2703
|
const anchored = bandAnchor(block.float, block.widthPt, contentWidth, startX);
|
|
2704
|
+
cursorY -= anchored === void 0 ? spacing.open(block.spacingBeforePt) : block.spacingBeforePt;
|
|
2267
2705
|
const offset = anchored ?? alignmentOffset(block.resolvedAlignment, block.widthPt, contentWidth);
|
|
2268
2706
|
const top = anchored === void 0 || !block.float ? cursorY : bandAnchorTop(block.float, cursorY, pageHeight, block.heightPt, bandMargins);
|
|
2269
2707
|
out.push({
|
|
@@ -2283,19 +2721,21 @@ function drawBlocksSequentially(blocks, startX, startY, pageHeight, contentWidth
|
|
|
2283
2721
|
if (block.outline) out.push(...pictureOutlineItems(block.outline, startX + offset, pageHeight - top, block.widthPt, block.heightPt, structId));
|
|
2284
2722
|
if (anchored === void 0) cursorY -= block.heightPt;
|
|
2285
2723
|
cursorY -= block.spacingAfterPt;
|
|
2724
|
+
if (anchored === void 0) spacing.close(sharedAfter(block));
|
|
2286
2725
|
continue;
|
|
2287
2726
|
}
|
|
2288
2727
|
if (block.kind === "shape") {
|
|
2289
|
-
cursorY -= block.spacingBeforePt;
|
|
2290
2728
|
const anchored = bandAnchor(block.float, block.widthPt, contentWidth, startX);
|
|
2729
|
+
cursorY -= anchored === void 0 ? spacing.open(block.spacingBeforePt) : block.spacingBeforePt;
|
|
2291
2730
|
const offset = anchored ?? alignmentOffset(block.resolvedAlignment, block.widthPt, contentWidth);
|
|
2292
2731
|
const top = anchored === void 0 || !block.float ? cursorY : bandAnchorTop(block.float, cursorY, pageHeight, block.heightPt, bandMargins);
|
|
2293
2732
|
emitShapeItems(block, startX + offset, top - block.heightPt, out, pageHeight, structId);
|
|
2294
2733
|
if (anchored === void 0) cursorY -= block.heightPt;
|
|
2295
2734
|
cursorY -= block.spacingAfterPt;
|
|
2735
|
+
if (anchored === void 0) spacing.close(block.spacingAfterPt);
|
|
2296
2736
|
continue;
|
|
2297
2737
|
}
|
|
2298
|
-
cursorY -= block.spacingBeforePt;
|
|
2738
|
+
cursorY -= spacing.open(block.spacingBeforePt);
|
|
2299
2739
|
const bandTopY = cursorY;
|
|
2300
2740
|
for (const line of block.lines) {
|
|
2301
2741
|
const h = computeLineHeight(line, block.resolved);
|
|
@@ -2312,6 +2752,7 @@ function drawBlocksSequentially(blocks, startX, startY, pageHeight, contentWidth
|
|
|
2312
2752
|
}
|
|
2313
2753
|
out.push(...paragraphDecoration(block.resolved, startX + block.resolved.indentLeft, startX + contentWidth - block.resolved.indentRight, bandTopY, cursorY, pageHeight));
|
|
2314
2754
|
cursorY -= block.spacingAfterPt;
|
|
2755
|
+
spacing.close(block.spacingAfterPt);
|
|
2315
2756
|
}
|
|
2316
2757
|
return out;
|
|
2317
2758
|
}
|
|
@@ -2604,6 +3045,7 @@ function lookupFont(resources, fontKey) {
|
|
|
2604
3045
|
}
|
|
2605
3046
|
function collectFontResources(body, headersFooters, options, imageResources) {
|
|
2606
3047
|
const used = /* @__PURE__ */ new Map();
|
|
3048
|
+
const numerals = [...new Set([options.section, ...(options.sections ?? []).map((sec) => sec.properties)].flatMap((p) => p?.pageNumberFormat !== void 0 ? [p.pageNumberFormat] : []))].map((fmt) => Array.from({ length: 200 }, (_, i) => formatCounter(fmt, i + 1)).join("")).join("");
|
|
2607
3049
|
const addRun = (run, para) => {
|
|
2608
3050
|
const resolved = resolveRunProperties(run.properties, para.properties, options.styles);
|
|
2609
3051
|
const { fontKey, parsed } = runFontKeyAndParsed(options, resolved.fontFamily.ascii, resolved.bold, resolved.italic);
|
|
@@ -2678,7 +3120,7 @@ function collectFontResources(body, headersFooters, options, imageResources) {
|
|
|
2678
3120
|
if (run.inlineImage && !run.text) continue;
|
|
2679
3121
|
if (run.field !== void 0) {
|
|
2680
3122
|
addRun({
|
|
2681
|
-
text: `${run.text}0123456789`,
|
|
3123
|
+
text: `${run.text}0123456789${numerals}`,
|
|
2682
3124
|
properties: run.properties
|
|
2683
3125
|
}, el.paragraph);
|
|
2684
3126
|
continue;
|
|
@@ -2692,17 +3134,19 @@ function collectFontResources(body, headersFooters, options, imageResources) {
|
|
|
2692
3134
|
}
|
|
2693
3135
|
addRun(run, el.paragraph);
|
|
2694
3136
|
}
|
|
2695
|
-
} else if (el.kind === "table")
|
|
2696
|
-
|
|
2697
|
-
if (
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
3137
|
+
} else if (el.kind === "table") {
|
|
3138
|
+
for (const row of el.table.rows) for (const cell of row.cells) {
|
|
3139
|
+
if (cell.properties.hashOnOverflow === true) for (const inner of cell.content) {
|
|
3140
|
+
if (inner.kind !== "paragraph") continue;
|
|
3141
|
+
for (const run of inner.paragraph.runs) addRun({
|
|
3142
|
+
text: "#",
|
|
3143
|
+
properties: run.properties
|
|
3144
|
+
}, inner.paragraph);
|
|
3145
|
+
}
|
|
3146
|
+
visit(cell.content);
|
|
2702
3147
|
}
|
|
2703
|
-
visit(
|
|
2704
|
-
}
|
|
2705
|
-
else if (el.kind === "shape") {
|
|
3148
|
+
visit(el.table.overlay?.drawings ?? []);
|
|
3149
|
+
} else if (el.kind === "shape") {
|
|
2706
3150
|
const shapeText = (sh) => {
|
|
2707
3151
|
if (sh.text) visit(sh.text.content);
|
|
2708
3152
|
for (const child of sh.children ?? []) shapeText(child.shape);
|
|
@@ -2793,13 +3237,10 @@ function collectFontResources(body, headersFooters, options, imageResources) {
|
|
|
2793
3237
|
* the paragraph mark's own font (§17.3.1.31 `w:rPr` on the `w:pPr`, which is
|
|
2794
3238
|
* exactly the run properties Word keeps for a paragraph that holds no runs).
|
|
2795
3239
|
*/
|
|
2796
|
-
function emptyLine(paragraph, options, resolved, availableWidthPt
|
|
2797
|
-
const
|
|
2798
|
-
const
|
|
2799
|
-
const
|
|
2800
|
-
const { variant } = options.registry.resolveByStyle(mark.bold, mark.italic);
|
|
2801
|
-
const parsed = fontResources.get(variant)?.parsed;
|
|
2802
|
-
const metric = profile !== "ream" && parsed ? fontLeadingPt(parsed, fontSizePt, profile) : void 0;
|
|
3240
|
+
function emptyLine(paragraph, options, resolved, availableWidthPt) {
|
|
3241
|
+
const fontSizePt = resolveRunProperties(paragraph.properties.runProperties ?? {}, paragraph.properties, options.styles).fontSizePt;
|
|
3242
|
+
const model = leadingOf(options);
|
|
3243
|
+
const metric = model === "flat" ? void 0 : markLine(paragraph.properties, options, model);
|
|
2803
3244
|
return {
|
|
2804
3245
|
tokens: [],
|
|
2805
3246
|
contentWidthPt: 0,
|
|
@@ -2810,10 +3251,37 @@ function emptyLine(paragraph, options, resolved, availableWidthPt, fontResources
|
|
|
2810
3251
|
isLastInParagraph: true,
|
|
2811
3252
|
...metric && metric.ascentPt + metric.descentPt > 0 ? {
|
|
2812
3253
|
metricHeightPt: metric.ascentPt + metric.descentPt + metric.lineGapPt,
|
|
2813
|
-
metricDescentPt: metric.descentPt
|
|
3254
|
+
metricDescentPt: metric.descentPt,
|
|
3255
|
+
metricTextHeightPt: metric.ascentPt + metric.descentPt + metric.lineGapPt
|
|
2814
3256
|
} : {}
|
|
2815
3257
|
};
|
|
2816
3258
|
}
|
|
3259
|
+
/**
|
|
3260
|
+
* The single line of a paragraph mark's font (§17.3.1.29 `w:rPr` on the
|
|
3261
|
+
* `w:pPr`) under a model that reads faces: what an empty paragraph stands, and
|
|
3262
|
+
* what a spacing of more lines adds to a line with no text on it.
|
|
3263
|
+
*
|
|
3264
|
+
* @param pp The paragraph's properties.
|
|
3265
|
+
* @param options Render options (styles, fonts).
|
|
3266
|
+
* @param model The line model.
|
|
3267
|
+
* @returns Its ascent, descent and gap, in points.
|
|
3268
|
+
*/
|
|
3269
|
+
function markLine(pp, options, model) {
|
|
3270
|
+
const mark = resolveRunProperties(pp.runProperties ?? {}, pp, options.styles);
|
|
3271
|
+
const family = mark.fontFamily.ascii;
|
|
3272
|
+
const { parsed } = runFontKeyAndParsed(options, family, mark.bold, mark.italic);
|
|
3273
|
+
return fontLeadingPt(parsed, mark.fontSizePt, model, family);
|
|
3274
|
+
}
|
|
3275
|
+
/** What a paragraph's lines are reckoned from, under the layout's model. */
|
|
3276
|
+
function paragraphLeading(pp, options) {
|
|
3277
|
+
const model = leadingOf(options);
|
|
3278
|
+
if (model === "flat") return FLAT_LEADING;
|
|
3279
|
+
const m = markLine(pp, options, model);
|
|
3280
|
+
return {
|
|
3281
|
+
model,
|
|
3282
|
+
markLinePt: m.ascentPt + m.descentPt + m.lineGapPt
|
|
3283
|
+
};
|
|
3284
|
+
}
|
|
2817
3285
|
function layoutParagraphBlock(paragraph, options, fontResources, imageResources, contentWidth, lineWidths) {
|
|
2818
3286
|
const cascaded = resolveParagraphProperties(paragraph.properties, options.styles);
|
|
2819
3287
|
const baseResolved = options.gridLinePitchPt !== void 0 && cascaded.snapToGrid !== false ? {
|
|
@@ -2842,7 +3310,8 @@ function layoutParagraphBlock(paragraph, options, fontResources, imageResources,
|
|
|
2842
3310
|
const tokens = tokenizeParagraph(paragraph, options, fontResources, imageResources, contentWidth, baseDir);
|
|
2843
3311
|
const firstLineWidth = paragraphMaxWidth(resolved, contentWidth, true);
|
|
2844
3312
|
const otherWidth = paragraphMaxWidth(resolved, contentWidth, false);
|
|
2845
|
-
const
|
|
3313
|
+
const leading = paragraphLeading(paragraph.properties, options);
|
|
3314
|
+
const runWrap = (part, firstWidth, widths) => wrap(part, firstWidth, otherWidth, resolved, options.hyphenator, options.layoutProfile ?? "ream", leading, options.doNotExpandShiftReturn === true, widths);
|
|
2846
3315
|
const parts = splitAtColumnBreaks(tokens);
|
|
2847
3316
|
const columnBreakLines = /* @__PURE__ */ new Set();
|
|
2848
3317
|
let wrapped;
|
|
@@ -2858,12 +3327,13 @@ function layoutParagraphBlock(paragraph, options, fontResources, imageResources,
|
|
|
2858
3327
|
})));
|
|
2859
3328
|
}
|
|
2860
3329
|
}
|
|
2861
|
-
const lines = wrapped.length > 0 ? wrapped : [emptyLine(paragraph, options, resolved, Math.max(firstLineWidth, 0)
|
|
3330
|
+
const lines = wrapped.length > 0 ? wrapped : [emptyLine(paragraph, options, resolved, Math.max(firstLineWidth, 0))];
|
|
2862
3331
|
let heightPt = 0;
|
|
2863
3332
|
for (const line of lines) heightPt += computeLineHeight(line, resolved);
|
|
2864
3333
|
const numbering = paragraph.properties.numbering;
|
|
2865
3334
|
const breakIdx = paragraph.runs.findIndex((r) => r.pageBreak === true);
|
|
2866
|
-
const
|
|
3335
|
+
const holds = (r) => r.text !== "" || r.inlineImage !== void 0 || r.math !== void 0;
|
|
3336
|
+
const leadingPageBreak = breakIdx >= 0 && paragraph.runs.slice(0, breakIdx + 1).every((r) => r.text === "" && r.inlineImage === void 0) && paragraph.runs.slice(breakIdx + 1).some(holds);
|
|
2867
3337
|
const trailingPageBreak = paragraph.runs.some((r, i) => r.pageBreak === true && (!leadingPageBreak || i > breakIdx));
|
|
2868
3338
|
return {
|
|
2869
3339
|
kind: "paragraph",
|
|
@@ -3186,7 +3656,7 @@ function measureRunText(plan, text, face) {
|
|
|
3186
3656
|
font: plan.font,
|
|
3187
3657
|
...plan.synthetic ? { synthetic: plan.synthetic } : {}
|
|
3188
3658
|
};
|
|
3189
|
-
const base = drawn.font.measure.textWidthPt(text, plan.fontSizePt) * (drawn.synthetic?.widthScale ?? 1);
|
|
3659
|
+
const base = drawn.font.measure.textWidthPt(text, plan.fontSizePt) * (drawn.synthetic?.widthScale ?? 1) * (plan.resolvedRun.widthScale ?? 1);
|
|
3190
3660
|
const extra = plan.resolvedRun.letterSpacingPt;
|
|
3191
3661
|
return extra === void 0 || extra === 0 ? base : base + extra * [...text].length;
|
|
3192
3662
|
}
|
|
@@ -3634,7 +4104,7 @@ function splitToWidth(tok, widthPt) {
|
|
|
3634
4104
|
}
|
|
3635
4105
|
/** The fewest characters a mid-word break may leave on a line. */
|
|
3636
4106
|
var MIN_SPLIT_CHARS = 6;
|
|
3637
|
-
function lineFromRange(entries, start, breakIdx, availableWidthPt, isFirst, resolved,
|
|
4107
|
+
function lineFromRange(entries, start, breakIdx, availableWidthPt, isFirst, resolved, leading, allowEmpty = false) {
|
|
3638
4108
|
let st = start;
|
|
3639
4109
|
let et = breakIdx;
|
|
3640
4110
|
while (st < et && (entries[st].token === null || !isFirst && entries[st].token?.isSpace === true)) st++;
|
|
@@ -3662,10 +4132,12 @@ function lineFromRange(entries, start, breakIdx, availableWidthPt, isFirst, reso
|
|
|
3662
4132
|
let maxSize = 0;
|
|
3663
4133
|
let mathAscent = 0;
|
|
3664
4134
|
let mathDescent = 0;
|
|
3665
|
-
const useMetric =
|
|
3666
|
-
let
|
|
3667
|
-
let
|
|
3668
|
-
let
|
|
4135
|
+
const useMetric = leading.model !== "flat";
|
|
4136
|
+
let textAscent = 0;
|
|
4137
|
+
let textDescent = 0;
|
|
4138
|
+
let markerAscent = 0;
|
|
4139
|
+
let markerDescent = 0;
|
|
4140
|
+
let pictureHeight = 0;
|
|
3669
4141
|
const prints = lineTokens.some((t) => !t.isSpace);
|
|
3670
4142
|
for (const t of lineTokens) {
|
|
3671
4143
|
contentWidth += t.widthPt;
|
|
@@ -3676,13 +4148,22 @@ function lineFromRange(entries, start, breakIdx, availableWidthPt, isFirst, reso
|
|
|
3676
4148
|
mathAscent = Math.max(mathAscent, t.ascentPt);
|
|
3677
4149
|
mathDescent = Math.max(mathDescent, t.descentPt);
|
|
3678
4150
|
} else if (useMetric && t.kind === "text") {
|
|
3679
|
-
const m = fontLeadingPt(t.font.parsed, t.fontSizePt,
|
|
3680
|
-
|
|
3681
|
-
if (
|
|
3682
|
-
|
|
3683
|
-
|
|
4151
|
+
const m = fontLeadingPt(t.font.parsed, t.fontSizePt, leading.model, wordFamilyOf(t));
|
|
4152
|
+
const above = m.ascentPt + m.lineGapPt;
|
|
4153
|
+
if (t.listMarker === true) {
|
|
4154
|
+
markerAscent = Math.max(markerAscent, above);
|
|
4155
|
+
markerDescent = Math.max(markerDescent, m.descentPt);
|
|
4156
|
+
} else {
|
|
4157
|
+
textAscent = Math.max(textAscent, above);
|
|
4158
|
+
textDescent = Math.max(textDescent, m.descentPt);
|
|
4159
|
+
}
|
|
4160
|
+
} else if (useMetric && t.kind === "image") pictureHeight = Math.max(pictureHeight, t.heightPt);
|
|
3684
4161
|
}
|
|
3685
|
-
const
|
|
4162
|
+
const hasText = textAscent + textDescent > 0;
|
|
4163
|
+
const ascent = Math.max(textAscent, markerAscent);
|
|
4164
|
+
const descent = hasText ? textDescent : markerDescent;
|
|
4165
|
+
const hasMetric = useMetric && ascent + descent + pictureHeight > 0;
|
|
4166
|
+
const textLine = ascent + descent > 0 ? ascent + descent : leading.markLinePt;
|
|
3686
4167
|
return {
|
|
3687
4168
|
tokens: lineTokens,
|
|
3688
4169
|
contentWidthPt: contentWidth,
|
|
@@ -3694,8 +4175,9 @@ function lineFromRange(entries, start, breakIdx, availableWidthPt, isFirst, reso
|
|
|
3694
4175
|
mathAscentPt: mathAscent,
|
|
3695
4176
|
mathDescentPt: mathDescent,
|
|
3696
4177
|
...hasMetric ? {
|
|
3697
|
-
metricHeightPt:
|
|
3698
|
-
metricDescentPt:
|
|
4178
|
+
metricHeightPt: Math.max(ascent, pictureHeight) + descent,
|
|
4179
|
+
metricDescentPt: descent,
|
|
4180
|
+
metricTextHeightPt: textLine
|
|
3699
4181
|
} : {}
|
|
3700
4182
|
};
|
|
3701
4183
|
}
|
|
@@ -3715,7 +4197,7 @@ function withLeadingTabWidths(tokens, resolved, firstLineWidth) {
|
|
|
3715
4197
|
}
|
|
3716
4198
|
return out;
|
|
3717
4199
|
}
|
|
3718
|
-
function wrap(tokens, firstLineWidth, otherWidth, resolved, hyphenator, profile, noExpandShiftReturn, lineWidths) {
|
|
4200
|
+
function wrap(tokens, firstLineWidth, otherWidth, resolved, hyphenator, profile, leading, noExpandShiftReturn, lineWidths) {
|
|
3719
4201
|
if (tokens.length === 0) return [];
|
|
3720
4202
|
const widths = lineWidths ?? [firstLineWidth, otherWidth];
|
|
3721
4203
|
const entries = paragraphItemStream(withLeadingTabWidths(tokens, resolved, widths[0] ?? firstLineWidth), hyphenator, Math.max(0, ...widths));
|
|
@@ -3728,7 +4210,7 @@ function wrap(tokens, firstLineWidth, otherWidth, resolved, hyphenator, profile,
|
|
|
3728
4210
|
let lineIdx = 0;
|
|
3729
4211
|
for (const [i, breakIdx] of at.entries()) {
|
|
3730
4212
|
const width = lineIdx < widths.length ? widths[lineIdx] : widths[widths.length - 1];
|
|
3731
|
-
const line = lineFromRange(entries, start, breakIdx, width, isFirst, resolved,
|
|
4213
|
+
const line = lineFromRange(entries, start, breakIdx, width, isFirst, resolved, leading, i < at.length - 1);
|
|
3732
4214
|
if (line) out.push(noExpandShiftReturn && entries[breakIdx]?.softBreak ? {
|
|
3733
4215
|
...line,
|
|
3734
4216
|
noJustify: true
|
|
@@ -3748,14 +4230,55 @@ function wrap(tokens, firstLineWidth, otherWidth, resolved, hyphenator, profile,
|
|
|
3748
4230
|
if (lines.length > 0) lines[lines.length - 1].isLastInParagraph = true;
|
|
3749
4231
|
return lines;
|
|
3750
4232
|
}
|
|
3751
|
-
|
|
4233
|
+
/**
|
|
4234
|
+
* §17.3.2.26 — the family Word sets a token's characters in: East Asian text
|
|
4235
|
+
* in the run's `eastAsia` font, complex-script text (and a `w:rtl` run) in its
|
|
4236
|
+
* `cs` one, the rest in `ascii` — `hAnsi` above ASCII. The layout draws a run
|
|
4237
|
+
* with its ascii face and hands what that face lacks to another, so the name
|
|
4238
|
+
* the run gives for THESE characters is the one Word's line is looked up by:
|
|
4239
|
+
* a Chinese sentence in a Calibri run does not stand on Calibri's line.
|
|
4240
|
+
*
|
|
4241
|
+
* @param t The text token.
|
|
4242
|
+
* @returns The family's name, when the run gives one for that slot.
|
|
4243
|
+
*/
|
|
4244
|
+
function wordFamilyOf(t) {
|
|
4245
|
+
const fonts = t.resolvedRun.fontFamily;
|
|
4246
|
+
if (t.resolvedRun.rtl) return fonts.cs;
|
|
4247
|
+
let high = false;
|
|
4248
|
+
for (const ch of t.text) {
|
|
4249
|
+
const cp = ch.codePointAt(0) ?? 0;
|
|
4250
|
+
if (cp <= 127) continue;
|
|
4251
|
+
high = true;
|
|
4252
|
+
const script = scriptForCodepoint(cp);
|
|
4253
|
+
if (script === "hebrew" || script === "arabic" || script === "thai") return fonts.cs;
|
|
4254
|
+
if (script === "kr" || script === "jp" || script === "sc") return fonts.eastAsia;
|
|
4255
|
+
}
|
|
4256
|
+
return high ? fonts.hAnsi ?? fonts.ascii : fonts.ascii;
|
|
4257
|
+
}
|
|
4258
|
+
/**
|
|
4259
|
+
* A face's single line at a size: its ascent, its descent, and the gap set
|
|
4260
|
+
* above the text. Under `'word'` a family Word's own line is known for (see
|
|
4261
|
+
* {@link WORD_LINE_METRICS}) takes that; otherwise the face in hand gives its
|
|
4262
|
+
* hhea line, or the OS/2 typo one when it sets USE_TYPO_METRICS.
|
|
4263
|
+
*
|
|
4264
|
+
* @param parsed The face the run is drawn with.
|
|
4265
|
+
* @param sizePt The run's size.
|
|
4266
|
+
* @param model The line model (never `'flat'`, which asks no face).
|
|
4267
|
+
* @param family The family the run names, when it names one.
|
|
4268
|
+
* @returns The three, in points; the descent as a magnitude.
|
|
4269
|
+
*/
|
|
4270
|
+
function fontLeadingPt(parsed, sizePt, model, family) {
|
|
4271
|
+
const known = model === "word" && family !== void 0 ? WORD_LINE_METRICS.get(family.trim().toLowerCase()) : void 0;
|
|
4272
|
+
if (known) {
|
|
4273
|
+
const k = sizePt / 2048;
|
|
4274
|
+
return {
|
|
4275
|
+
ascentPt: known[0] * k,
|
|
4276
|
+
lineGapPt: known[1] * k,
|
|
4277
|
+
descentPt: known[2] * k
|
|
4278
|
+
};
|
|
4279
|
+
}
|
|
3752
4280
|
const s = sizePt / parsed.unitsPerEm;
|
|
3753
4281
|
const vm = parsed.vmetrics;
|
|
3754
|
-
if (profile === "word") return {
|
|
3755
|
-
ascentPt: vm.winAscent * s,
|
|
3756
|
-
descentPt: vm.winDescent * s,
|
|
3757
|
-
lineGapPt: 0
|
|
3758
|
-
};
|
|
3759
4282
|
const asc = vm.useTypoMetrics ? vm.typoAscent : parsed.ascender;
|
|
3760
4283
|
const desc = vm.useTypoMetrics ? vm.typoDescent : parsed.descender;
|
|
3761
4284
|
const gap = vm.useTypoMetrics ? vm.typoLineGap : parsed.lineGap;
|
|
@@ -3780,7 +4303,8 @@ function naturalLineHeight(line, p) {
|
|
|
3780
4303
|
if (p.spacingLineRule === "atLeast" && p.spacingLine > 0) return Math.max(natural, p.spacingLine, mathNeed);
|
|
3781
4304
|
const lineTwips = Math.round(p.spacingLine * 20);
|
|
3782
4305
|
const multiple = lineTwips > 0 ? lineTwips / 240 : 1;
|
|
3783
|
-
|
|
4306
|
+
const scaled = line.metricTextHeightPt === void 0 ? natural * multiple : natural + (multiple - 1) * line.metricTextHeightPt;
|
|
4307
|
+
return Math.max(scaled, mathNeed);
|
|
3784
4308
|
}
|
|
3785
4309
|
function lineDescent(line) {
|
|
3786
4310
|
const fs = line.maxFontSizePt || 12;
|
|
@@ -3922,16 +4446,18 @@ function layoutTableBlock(table, options, fontResources, imageResources, content
|
|
|
3922
4446
|
}
|
|
3923
4447
|
aboveBordersByCol = nextAbove;
|
|
3924
4448
|
const rp = table.rows[r].properties;
|
|
3925
|
-
rows.push(rp.isHeader || rp.pageBreakBefore ? {
|
|
4449
|
+
rows.push(rp.isHeader || rp.pageBreakBefore || rp.sheetTopPt !== void 0 ? {
|
|
3926
4450
|
...rl,
|
|
3927
4451
|
...rp.isHeader ? { isHeader: true } : {},
|
|
3928
|
-
...rp.pageBreakBefore ? { breakBefore: true } : {}
|
|
4452
|
+
...rp.pageBreakBefore ? { breakBefore: true } : {},
|
|
4453
|
+
...rp.sheetTopPt !== void 0 ? { sheetTopPt: rp.sheetTopPt } : {}
|
|
3929
4454
|
} : rl);
|
|
3930
4455
|
}
|
|
3931
4456
|
growRowsForMerges(rows);
|
|
3932
4457
|
const heightPt = rows.reduce((s, r) => s + r.heightPt, 0);
|
|
3933
4458
|
const totalWidthPt = columnWidthsPt.reduce((s, w) => s + w, 0);
|
|
3934
|
-
const xOffsetPt = (table.properties.indentPt ?? 0) + tableXOffset(table.properties.alignment, contentWidth, totalWidthPt);
|
|
4459
|
+
const xOffsetPt = (table.properties.indentPt ?? 0) + tableXOffset(table.properties.alignment, contentWidth, totalWidthPt) - legacyTableOutdent(table, rows, options);
|
|
4460
|
+
const overlay = table.overlay ? layoutOverlay(table.overlay, options, fontResources, imageResources, contentWidth) : void 0;
|
|
3935
4461
|
return {
|
|
3936
4462
|
kind: "table",
|
|
3937
4463
|
...table.properties.float ? { float: table.properties.float } : {},
|
|
@@ -3939,7 +4465,40 @@ function layoutTableBlock(table, options, fontResources, imageResources, content
|
|
|
3939
4465
|
heightPt,
|
|
3940
4466
|
totalWidthPt,
|
|
3941
4467
|
colCount,
|
|
3942
|
-
xOffsetPt
|
|
4468
|
+
xOffsetPt,
|
|
4469
|
+
...overlay ? { overlay } : {}
|
|
4470
|
+
};
|
|
4471
|
+
}
|
|
4472
|
+
/**
|
|
4473
|
+
* The drawings over a spreadsheet grid (TableOverlay), each laid out as the
|
|
4474
|
+
* float it is and placed by its corner in the sheet's own frame.
|
|
4475
|
+
*
|
|
4476
|
+
* @param overlay The drawings and the part of the sheet the table prints.
|
|
4477
|
+
* @param options The render options.
|
|
4478
|
+
* @param fontResources The fonts the drawings' text is set in.
|
|
4479
|
+
* @param imageResources The pictures they show.
|
|
4480
|
+
* @param contentWidth The width between the margins.
|
|
4481
|
+
* @returns The overlay laid out, or undefined when none of it draws.
|
|
4482
|
+
*/
|
|
4483
|
+
function layoutOverlay(overlay, options, fontResources, imageResources, contentWidth) {
|
|
4484
|
+
const drawings = [];
|
|
4485
|
+
for (const el of overlay.drawings) {
|
|
4486
|
+
if (el.kind !== "image" && el.kind !== "shape" && el.kind !== "chart") continue;
|
|
4487
|
+
const block = layoutBodyElement(el, options, fontResources, imageResources, contentWidth);
|
|
4488
|
+
if (block.kind !== "image" && block.kind !== "shape" && block.kind !== "chart") continue;
|
|
4489
|
+
drawings.push({
|
|
4490
|
+
block,
|
|
4491
|
+
xPt: block.float?.posH?.offsetPt ?? 0,
|
|
4492
|
+
yPt: block.float?.posV?.offsetPt ?? 0
|
|
4493
|
+
});
|
|
4494
|
+
}
|
|
4495
|
+
if (drawings.length === 0) return void 0;
|
|
4496
|
+
return {
|
|
4497
|
+
sheetLeftPt: overlay.sheetLeftPt,
|
|
4498
|
+
widthPt: overlay.widthPt,
|
|
4499
|
+
leftPt: overlay.leftPt,
|
|
4500
|
+
mirrored: overlay.mirrored === true,
|
|
4501
|
+
drawings
|
|
3943
4502
|
};
|
|
3944
4503
|
}
|
|
3945
4504
|
/**
|
|
@@ -3968,6 +4527,29 @@ function growRowsForMerges(rows) {
|
|
|
3968
4527
|
};
|
|
3969
4528
|
}
|
|
3970
4529
|
}
|
|
4530
|
+
/**
|
|
4531
|
+
* [MS-DOCX] `compatibilityMode` — how far a table stands out past its indent
|
|
4532
|
+
* in a document laid out as Word 2010 and earlier lay it out. There the
|
|
4533
|
+
* indent places the first cell's TEXT, not the table's edge, so the table
|
|
4534
|
+
* stands out by that cell's left margin. Measured in Word for Mac: with
|
|
4535
|
+
* first-cell margins of 5.4, 15 and 20pt, the text stands at the margin in a
|
|
4536
|
+
* document of mode 14 and in one that states no mode, and 5.4, 15 and 20pt in
|
|
4537
|
+
* at mode 15. A 36pt `w:tblInd` puts the text 36pt in, not 41.4. A centred
|
|
4538
|
+
* table does not move. Only a Word document's table moves; a spreadsheet has
|
|
4539
|
+
* no such history.
|
|
4540
|
+
*
|
|
4541
|
+
* @param table The table.
|
|
4542
|
+
* @param rows Its laid-out rows, the first cell's margin resolved.
|
|
4543
|
+
* @param options Render options: whose document, and which mode.
|
|
4544
|
+
* @returns The points to move the table left by, 0 where it stays.
|
|
4545
|
+
*/
|
|
4546
|
+
function legacyTableOutdent(table, rows, options) {
|
|
4547
|
+
if (options.typesetBy !== "word" || (options.compatibilityMode ?? 0) >= 15) return 0;
|
|
4548
|
+
if ((table.properties.alignment ?? "left") !== "left" || table.properties.float) return 0;
|
|
4549
|
+
const first = rows[0];
|
|
4550
|
+
if (!first || first.columnXOffsets[0] !== 0) return 0;
|
|
4551
|
+
return first.cells[0]?.padLeftPt ?? 0;
|
|
4552
|
+
}
|
|
3971
4553
|
function tableXOffset(alignment, contentWidth, totalWidthPt) {
|
|
3972
4554
|
const slack = contentWidth - totalWidthPt;
|
|
3973
4555
|
if (slack <= 0 || !alignment || alignment === "left") return 0;
|
|
@@ -4126,7 +4708,7 @@ function measureSingleLine(paragraph, options, fontResources) {
|
|
|
4126
4708
|
const { fontKey, synthetic } = runFontKeyAndParsed(options, resolved.fontFamily.ascii, resolved.bold, resolved.italic);
|
|
4127
4709
|
const font = lookupFont(fontResources, fontKey);
|
|
4128
4710
|
const fontSizePt = resolved.fontSizePt;
|
|
4129
|
-
total += font.measure.textWidthPt(run.text, fontSizePt) * (synthetic?.widthScale ?? 1);
|
|
4711
|
+
total += font.measure.textWidthPt(run.text, fontSizePt) * (synthetic?.widthScale ?? 1) * (resolved.widthScale ?? 1);
|
|
4130
4712
|
}
|
|
4131
4713
|
return total;
|
|
4132
4714
|
}
|
|
@@ -4166,19 +4748,30 @@ function layoutTableRow(row, rowIdx, rowCount, colCount, tableProps, columnWidth
|
|
|
4166
4748
|
let widthPt = 0;
|
|
4167
4749
|
for (let k = 0; k < span && colIdx + k < columnWidthsPt.length; k++) widthPt += columnWidthsPt[colIdx + k];
|
|
4168
4750
|
const paintSpan = Math.min(span, Math.max(1, cell.properties.paintColumns ?? span));
|
|
4751
|
+
const paintFrom = cell.properties.paintAtEnd === true ? span - paintSpan : 0;
|
|
4169
4752
|
let paintWidthPt = 0;
|
|
4170
|
-
|
|
4753
|
+
let paintOffsetPt = 0;
|
|
4754
|
+
for (let k = 0; k < paintFrom + paintSpan && colIdx + k < columnWidthsPt.length; k++) if (k < paintFrom) paintOffsetPt += columnWidthsPt[colIdx + k];
|
|
4755
|
+
else paintWidthPt += columnWidthsPt[colIdx + k];
|
|
4171
4756
|
columnXOffsets.push(cursorX);
|
|
4172
4757
|
const mergeRole = rowMergeRoles[cellIdx] ?? "standalone";
|
|
4173
4758
|
const leftNeighbor = cellIdx > 0 ? row.cells[cellIdx - 1].properties.borders : void 0;
|
|
4174
4759
|
const cellLayout = layoutTableCell(cell, tableProps, widthPt, options, fontResources, imageResources, rowIdx, colIdx, span, rowCount, colCount, mergeRole, leftNeighbor, aboveOfSpan(aboveBordersByCol, colIdx, span));
|
|
4175
4760
|
cells.push(paintWidthPt < widthPt ? {
|
|
4176
4761
|
...cellLayout,
|
|
4177
|
-
paintWidthPt
|
|
4762
|
+
paintWidthPt,
|
|
4763
|
+
...paintOffsetPt > 0 ? { paintOffsetPt } : {}
|
|
4178
4764
|
} : cellLayout);
|
|
4179
4765
|
cursorX += widthPt;
|
|
4180
4766
|
colIdx += span;
|
|
4181
4767
|
}
|
|
4768
|
+
const bands = options.typesetBy === "word" ? withBorderBands(cells, rowIdx === rowCount - 1) : void 0;
|
|
4769
|
+
const bandSpread = bands && (bands.topPt > 0 || bands.bottomPt > 0) ? bands : void 0;
|
|
4770
|
+
if (bandSpread) cells.splice(0, cells.length, ...bandSpread.cells);
|
|
4771
|
+
const borderBands = bandSpread ? { borderBands: {
|
|
4772
|
+
topPt: bandSpread.topPt,
|
|
4773
|
+
bottomPt: bandSpread.bottomPt
|
|
4774
|
+
} } : {};
|
|
4182
4775
|
let heightPt = 0;
|
|
4183
4776
|
for (const c of cells) {
|
|
4184
4777
|
if (c.mergeRole === "start") continue;
|
|
@@ -4192,7 +4785,8 @@ function layoutTableRow(row, rowIdx, rowCount, colCount, tableProps, columnWidth
|
|
|
4192
4785
|
columnXOffsets,
|
|
4193
4786
|
rowIdx,
|
|
4194
4787
|
rowCount,
|
|
4195
|
-
...row.properties.cantSplit ? { cantSplit: true } : {}
|
|
4788
|
+
...row.properties.cantSplit ? { cantSplit: true } : {},
|
|
4789
|
+
...borderBands
|
|
4196
4790
|
};
|
|
4197
4791
|
} else if (row.properties.height && row.properties.heightRule === "atLeast") heightPt = Math.max(heightPt, row.properties.height);
|
|
4198
4792
|
return {
|
|
@@ -4201,7 +4795,42 @@ function layoutTableRow(row, rowIdx, rowCount, colCount, tableProps, columnWidth
|
|
|
4201
4795
|
columnXOffsets,
|
|
4202
4796
|
rowIdx,
|
|
4203
4797
|
rowCount,
|
|
4204
|
-
...row.properties.cantSplit ? { cantSplit: true } : {}
|
|
4798
|
+
...row.properties.cantSplit ? { cantSplit: true } : {},
|
|
4799
|
+
...borderBands
|
|
4800
|
+
};
|
|
4801
|
+
}
|
|
4802
|
+
/**
|
|
4803
|
+
* §17.4.38 — a table row as Word sets it: every horizontal border takes the
|
|
4804
|
+
* room it is wide, the edge above a row in that row and the table's bottom
|
|
4805
|
+
* edge in its last one, and the text stands clear of it. Measured in Word for
|
|
4806
|
+
* Mac, a row of 11pt Calibri with no cell margins stands 13.44pt apart from
|
|
4807
|
+
* the next with no borders, 13.92 under 0.5pt ones, 14.94 under 1.5pt and
|
|
4808
|
+
* 16.43 under 3pt; a one-row table between two paragraphs adds its top and
|
|
4809
|
+
* its bottom border whole, 3pt each at 3pt. LibreOffice does the same. The
|
|
4810
|
+
* room is the widest border along the edge, and it goes into the cells' top
|
|
4811
|
+
* and bottom padding, which is what places their text and sizes the row.
|
|
4812
|
+
*
|
|
4813
|
+
* @param cells The row's laid-out cells, their borders resolved.
|
|
4814
|
+
* @param lastRow Whether the row is the table's last, whose bottom border is
|
|
4815
|
+
* the table's own.
|
|
4816
|
+
* @returns The cells with the room in their padding, and the room taken.
|
|
4817
|
+
*/
|
|
4818
|
+
function withBorderBands(cells, lastRow) {
|
|
4819
|
+
let topPt = 0;
|
|
4820
|
+
let bottomPt = 0;
|
|
4821
|
+
for (const c of cells) {
|
|
4822
|
+
topPt = Math.max(topPt, borderWeight(c.borders.top));
|
|
4823
|
+
if (lastRow) bottomPt = Math.max(bottomPt, borderWeight(c.borders.bottom));
|
|
4824
|
+
}
|
|
4825
|
+
return {
|
|
4826
|
+
cells: cells.map((c) => ({
|
|
4827
|
+
...c,
|
|
4828
|
+
padTopPt: c.padTopPt + topPt,
|
|
4829
|
+
padBottomPt: c.padBottomPt + bottomPt,
|
|
4830
|
+
totalHeightPt: c.totalHeightPt + topPt + bottomPt
|
|
4831
|
+
})),
|
|
4832
|
+
topPt,
|
|
4833
|
+
bottomPt
|
|
4205
4834
|
};
|
|
4206
4835
|
}
|
|
4207
4836
|
/**
|
|
@@ -4223,14 +4852,16 @@ function clipCellToHeight(cell, heightPt) {
|
|
|
4223
4852
|
let used = 0;
|
|
4224
4853
|
for (const line of cell.lines) {
|
|
4225
4854
|
const h = computeLineHeight(line, line.resolved);
|
|
4226
|
-
|
|
4855
|
+
const below = line.metricDescentPt ?? line.maxFontSizePt * .2;
|
|
4856
|
+
if (kept.length > 0 && used + h - below > room) break;
|
|
4227
4857
|
kept.push(line);
|
|
4228
4858
|
used += h;
|
|
4229
4859
|
}
|
|
4230
4860
|
return {
|
|
4231
4861
|
...cell,
|
|
4232
4862
|
lines: kept,
|
|
4233
|
-
contentHeightPt: used
|
|
4863
|
+
contentHeightPt: used,
|
|
4864
|
+
...used > room ? { clipped: true } : {}
|
|
4234
4865
|
};
|
|
4235
4866
|
}
|
|
4236
4867
|
/**
|
|
@@ -4278,8 +4909,9 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
|
|
|
4278
4909
|
let contentHeightPt = 0;
|
|
4279
4910
|
let clipped = false;
|
|
4280
4911
|
let pendingGapPt = 0;
|
|
4912
|
+
const collapse = collapsesSpacing(options);
|
|
4281
4913
|
const openParagraph = (spacingBeforePt) => {
|
|
4282
|
-
const gap = pendingGapPt + spacingBeforePt;
|
|
4914
|
+
const gap = collapse ? Math.max(pendingGapPt, spacingBeforePt) : pendingGapPt + spacingBeforePt;
|
|
4283
4915
|
pendingGapPt = 0;
|
|
4284
4916
|
if (gap <= 0) return;
|
|
4285
4917
|
lineGaps.set(lines.length, (lineGaps.get(lines.length) ?? 0) + gap);
|
|
@@ -4316,7 +4948,6 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
|
|
|
4316
4948
|
lines.push(line);
|
|
4317
4949
|
contentHeightPt += computeLineHeight(line, block.resolved);
|
|
4318
4950
|
}
|
|
4319
|
-
contentHeightPt += block.spacingAfterPt;
|
|
4320
4951
|
pendingGapPt = block.spacingAfterPt;
|
|
4321
4952
|
} else if (el.kind === "shape" && !isOutOfFlowFloat(el.shape.float)) {
|
|
4322
4953
|
const block = layoutShapeBlock(el.shape, options, fontResources, imageResources, innerWidth);
|
|
@@ -4339,6 +4970,13 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
|
|
|
4339
4970
|
}
|
|
4340
4971
|
const colEnd = colStart + colSpan - 1;
|
|
4341
4972
|
const borders = resolveCellBorders(cell.properties.borders, tableProps.borders, rowIdx, colStart, colEnd, rowCount, colCount, mergeRole, leftNeighborBorders, aboveNeighborBorders);
|
|
4973
|
+
const grid = tableProps.gridlines;
|
|
4974
|
+
const ruled = grid && !cell.properties.shading?.colorHex ? {
|
|
4975
|
+
...borders.top || mergeRole === "middle" || mergeRole === "end" ? {} : { top: grid },
|
|
4976
|
+
...borders.left ? {} : { left: grid },
|
|
4977
|
+
...borders.bottom || rowIdx !== rowCount - 1 || mergeRole === "start" || mergeRole === "middle" ? {} : { bottom: grid },
|
|
4978
|
+
...borders.right || colEnd !== colCount - 1 ? {} : { right: grid }
|
|
4979
|
+
} : void 0;
|
|
4342
4980
|
const alongPt = turned ? lines.reduce((a, l) => Math.max(a, l.contentWidthPt), 0) : 0;
|
|
4343
4981
|
const heightPt = turned ? alongPt : contentHeightPt;
|
|
4344
4982
|
const totalHeightPt = mergeRole === "middle" || mergeRole === "end" ? 0 : padTopPt + heightPt + padBottomPt;
|
|
@@ -4348,11 +4986,15 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
|
|
|
4348
4986
|
padRightPt,
|
|
4349
4987
|
padBottomPt,
|
|
4350
4988
|
padLeftPt,
|
|
4351
|
-
borders
|
|
4989
|
+
borders: ruled ? {
|
|
4990
|
+
...ruled,
|
|
4991
|
+
...borders
|
|
4992
|
+
} : borders,
|
|
4352
4993
|
...cell.properties.shading?.colorHex ? { shadingColorHex: cell.properties.shading.colorHex } : {},
|
|
4353
4994
|
...cell.properties.dataBar ? { dataBar: cell.properties.dataBar } : {},
|
|
4354
4995
|
...cell.properties.icon ? { icon: cell.properties.icon } : {},
|
|
4355
4996
|
...cell.properties.sparkline ? { sparkline: cell.properties.sparkline } : {},
|
|
4997
|
+
...cell.properties.noteFlag ? { noteFlag: cell.properties.noteFlag } : {},
|
|
4356
4998
|
lines,
|
|
4357
4999
|
...lineGaps.size > 0 ? { lineGaps } : {},
|
|
4358
5000
|
...nestedTables.length > 0 ? { nestedTables } : {},
|
|
@@ -4492,7 +5134,7 @@ var PageAssembler = class PageAssembler {
|
|
|
4492
5134
|
this.ctx = sectionCtxs[0];
|
|
4493
5135
|
this.pageStartCtx = this.ctx;
|
|
4494
5136
|
this.pageNumber = this.ctx.properties.pageNumberStart ?? 1;
|
|
4495
|
-
this.cursorY = this.
|
|
5137
|
+
this.cursorY = this.pageTopY();
|
|
4496
5138
|
this.colStartY = this.cursorY;
|
|
4497
5139
|
this.bandTopY = this.cursorY;
|
|
4498
5140
|
}
|
|
@@ -4509,6 +5151,56 @@ var PageAssembler = class PageAssembler {
|
|
|
4509
5151
|
current = [];
|
|
4510
5152
|
pendingPageBreak = false;
|
|
4511
5153
|
cursorY;
|
|
5154
|
+
/** Whether paragraph spacing stacks as Word stacks it (see {@link SpacingStack}). */
|
|
5155
|
+
collapseSpacing = false;
|
|
5156
|
+
/**
|
|
5157
|
+
* Where the last block's space after ended, on which page, and how much of
|
|
5158
|
+
* it a paragraph right below may share — nothing once the cursor has moved
|
|
5159
|
+
* on, to a new column or page or past a table.
|
|
5160
|
+
*/
|
|
5161
|
+
spacingMark = void 0;
|
|
5162
|
+
/**
|
|
5163
|
+
* Whether the next block opens a section on a page of its own (the
|
|
5164
|
+
* document's first included), whose space before Word keeps at the top.
|
|
5165
|
+
*/
|
|
5166
|
+
sectionStart = true;
|
|
5167
|
+
/**
|
|
5168
|
+
* Whether a paragraph that breaks to a new page by its own
|
|
5169
|
+
* `w:pageBreakBefore` keeps its space before there — Word 2010 and earlier
|
|
5170
|
+
* (compatibilityMode below 15, or none) keep it; Word 2013 drops it.
|
|
5171
|
+
*/
|
|
5172
|
+
keepsBeforeAtOwnBreak = false;
|
|
5173
|
+
/**
|
|
5174
|
+
* §17.3.1.33 — the room to open above a block, where the spacing stacks as
|
|
5175
|
+
* Word stacks it (see {@link SpacingStack}): the space after the block right
|
|
5176
|
+
* above it shared, and nothing at all at the top of a page or column. Word
|
|
5177
|
+
* for Mac drops a 24pt space before there whether the paragraph ran over
|
|
5178
|
+
* onto the page, broke to it after a `w:br w:type="page"` or ran into the
|
|
5179
|
+
* next column, in every mode; it keeps it for the first paragraph of the
|
|
5180
|
+
* document and of a section, and — below mode 15 — for a paragraph that
|
|
5181
|
+
* breaks to the page by its own `w:pageBreakBefore`.
|
|
5182
|
+
*
|
|
5183
|
+
* @param beforePt A block's own space before.
|
|
5184
|
+
* @param opensSection Whether the block is the first of a section on a page of its own.
|
|
5185
|
+
* @param ownBreak Whether the block broke to this page by its own `w:pageBreakBefore`.
|
|
5186
|
+
* @returns The room to open above it.
|
|
5187
|
+
*/
|
|
5188
|
+
openBefore(beforePt, opensSection = false, ownBreak = false) {
|
|
5189
|
+
const mark = this.spacingMark;
|
|
5190
|
+
this.spacingMark = void 0;
|
|
5191
|
+
if (!this.collapseSpacing) return beforePt;
|
|
5192
|
+
if (!this.colHasContent()) return opensSection || ownBreak && this.keepsBeforeAtOwnBreak ? beforePt : 0;
|
|
5193
|
+
if (!mark || mark.page !== this.current || mark.y !== this.cursorY) return beforePt;
|
|
5194
|
+
return Math.max(0, beforePt - mark.shared);
|
|
5195
|
+
}
|
|
5196
|
+
/** @param sharedPt The space after the block just placed, as far as a paragraph below may share it. */
|
|
5197
|
+
closeAfter(sharedPt) {
|
|
5198
|
+
this.spacingMark = {
|
|
5199
|
+
y: this.cursorY,
|
|
5200
|
+
page: this.current,
|
|
5201
|
+
shared: sharedPt
|
|
5202
|
+
};
|
|
5203
|
+
}
|
|
4512
5204
|
/**
|
|
4513
5205
|
* §17.6.4 multi-column flow: content fills column after column before the page
|
|
4514
5206
|
* flushes. `colStartLen` marks where the current column's items begin in
|
|
@@ -4557,6 +5249,16 @@ var PageAssembler = class PageAssembler {
|
|
|
4557
5249
|
* all and eleven controls wider than its page.
|
|
4558
5250
|
*/
|
|
4559
5251
|
pageHasContent = () => this.current.length > 0 || this.floatsBehind.length > 0 || this.floatsFront.length > 0;
|
|
5252
|
+
/**
|
|
5253
|
+
* §17.10 — the body's margins on the page being laid out: below the header
|
|
5254
|
+
* band that page shows and above its footer band (see `bandMargins`).
|
|
5255
|
+
*/
|
|
5256
|
+
pageMargins = () => this.ctx.bandMargins?.[bandForPage(this.pageInSection, this.globalPageIdx, this.ctx.titlePg, this.ctx.evenAndOddHeaders)] ?? {
|
|
5257
|
+
top: this.ctx.marginTop,
|
|
5258
|
+
bottom: this.ctx.marginBottom
|
|
5259
|
+
};
|
|
5260
|
+
/** Where the body of the page being laid out begins, y-up. */
|
|
5261
|
+
pageTopY = () => this.ctx.pageHeight - this.pageMargins().top;
|
|
4560
5262
|
/** Overflow step: next column on this page, or a fresh page after the last. */
|
|
4561
5263
|
advanceColumn = () => {
|
|
4562
5264
|
if (this.ctx.columns && this.colIdx + 1 < this.ctx.columns.length) {
|
|
@@ -4652,7 +5354,7 @@ var PageAssembler = class PageAssembler {
|
|
|
4652
5354
|
*/
|
|
4653
5355
|
beginBalancedBand = (bandHeightPt) => {
|
|
4654
5356
|
const colCount = this.ctx.columns?.length ?? 1;
|
|
4655
|
-
const room = this.cursorY - (this.
|
|
5357
|
+
const room = this.cursorY - (this.pageMargins().bottom + this.noteReserve);
|
|
4656
5358
|
const share = colCount > 1 ? balancedColumnHeight(bandHeightPt, colCount, room) : 0;
|
|
4657
5359
|
this.balanceHeightPt = share;
|
|
4658
5360
|
this.balanceBottomY = share > 0 ? this.cursorY - share : void 0;
|
|
@@ -4677,6 +5379,17 @@ var PageAssembler = class PageAssembler {
|
|
|
4677
5379
|
floatsBehind = [];
|
|
4678
5380
|
floatsFront = [];
|
|
4679
5381
|
/**
|
|
5382
|
+
* The front floats as the page paints them: over its text, when the
|
|
5383
|
+
* document's floats cover what is under them (StyledRenderOptions
|
|
5384
|
+
* floatsOverText); in the ordinary passes, under it, otherwise.
|
|
5385
|
+
*/
|
|
5386
|
+
inFront(items) {
|
|
5387
|
+
return this.ctx.options?.floatsOverText === true ? items.map((item) => ({
|
|
5388
|
+
...item,
|
|
5389
|
+
over: true
|
|
5390
|
+
})) : items;
|
|
5391
|
+
}
|
|
5392
|
+
/**
|
|
4680
5393
|
* Side-wrapping floats (wrapSquare/tight/through): rectangles the body text
|
|
4681
5394
|
* must flow around. Page-scoped, like the float graphics.
|
|
4682
5395
|
*/
|
|
@@ -4737,7 +5450,7 @@ var PageAssembler = class PageAssembler {
|
|
|
4737
5450
|
* margin (plus any footnote reserve); inside a BALANCED band it is the line
|
|
4738
5451
|
* the band's share of the height reaches, so the columns come out even.
|
|
4739
5452
|
*/
|
|
4740
|
-
bottomLimit = () => Math.max(this.
|
|
5453
|
+
bottomLimit = () => Math.max(this.pageMargins().bottom + this.noteReserve, this.balanceBottomY ?? Number.NEGATIVE_INFINITY);
|
|
4741
5454
|
/**
|
|
4742
5455
|
* §17.6.4 — a `continuous` section of several columns whose content fits the
|
|
4743
5456
|
* page is BALANCED: Word evens the columns out rather than filling the first
|
|
@@ -4772,8 +5485,8 @@ var PageAssembler = class PageAssembler {
|
|
|
4772
5485
|
xOffset: 0
|
|
4773
5486
|
};
|
|
4774
5487
|
const cl = this.colLeft();
|
|
4775
|
-
const leftRoom = e.x0 -
|
|
4776
|
-
const rightRoom = cl + full -
|
|
5488
|
+
const leftRoom = e.x0 - cl;
|
|
5489
|
+
const rightRoom = cl + full - e.x1;
|
|
4777
5490
|
if (Math.max(leftRoom, rightRoom) < MIN_WRAP_WIDTH) return {
|
|
4778
5491
|
width: full,
|
|
4779
5492
|
xOffset: 0,
|
|
@@ -4806,19 +5519,22 @@ var PageAssembler = class PageAssembler {
|
|
|
4806
5519
|
if (this.exclusions.length === 0 || block.lines.length === 0) return void 0;
|
|
4807
5520
|
const h0 = computeLineHeight(block.lines[0], block.resolved);
|
|
4808
5521
|
if (h0 <= 0) return void 0;
|
|
5522
|
+
const p = block.resolved;
|
|
5523
|
+
const full = this.colWidth();
|
|
5524
|
+
const hold = (width, xOffset, first) => Math.max(1, (xOffset > 0 ? width - p.indentRight : Math.min(width, full - p.indentRight)) - p.indentLeft - (first ? p.indentFirstLine : 0));
|
|
4809
5525
|
const widths = [];
|
|
4810
5526
|
let narrowed = false;
|
|
4811
5527
|
let y = startY;
|
|
4812
5528
|
for (let i = 0; i < 200; i++) {
|
|
4813
5529
|
const g = this.lineGeometryAt(y, h0);
|
|
4814
5530
|
if (g.blockedUntilYUp !== void 0) {
|
|
4815
|
-
widths.push(
|
|
5531
|
+
widths.push(hold(full, 0, widths.length === 0));
|
|
4816
5532
|
y = g.blockedUntilYUp - h0;
|
|
4817
5533
|
if (y < this.bottomLimit()) break;
|
|
4818
5534
|
continue;
|
|
4819
5535
|
}
|
|
4820
|
-
widths.push(g.width);
|
|
4821
|
-
if (g.pair) widths.push(g.pair.width);
|
|
5536
|
+
widths.push(hold(g.width, g.xOffset, widths.length === 0));
|
|
5537
|
+
if (g.pair) widths.push(hold(g.pair.width, g.pair.xOffset, false));
|
|
4822
5538
|
if (g.width < this.colWidth()) narrowed = true;
|
|
4823
5539
|
else if (narrowed) break;
|
|
4824
5540
|
y -= h0;
|
|
@@ -4850,13 +5566,43 @@ var PageAssembler = class PageAssembler {
|
|
|
4850
5566
|
return out;
|
|
4851
5567
|
};
|
|
4852
5568
|
/**
|
|
5569
|
+
* The first of a paragraph's lines, from `from` on, that the current column
|
|
5570
|
+
* no longer holds: each line is counted with the notes it brings onto the
|
|
5571
|
+
* page, the way the line loop places it, and an explicit column break ends
|
|
5572
|
+
* the column where it stands.
|
|
5573
|
+
*
|
|
5574
|
+
* @param pb The paragraph being placed.
|
|
5575
|
+
* @param from The first line not yet placed.
|
|
5576
|
+
* @returns The index of the first line that does not stay in this column;
|
|
5577
|
+
* `pb.lines.length` when every one does.
|
|
5578
|
+
*/
|
|
5579
|
+
firstLineOverflowing = (pb, from) => {
|
|
5580
|
+
let y = this.cursorY;
|
|
5581
|
+
let reserve = 0;
|
|
5582
|
+
let notesOnPage = this.pageNotes.length > 0;
|
|
5583
|
+
const counted = /* @__PURE__ */ new Set();
|
|
5584
|
+
for (let i = from; i < pb.lines.length; i++) {
|
|
5585
|
+
if (i > from && pb.columnBreakLines?.has(i)) return i;
|
|
5586
|
+
const line = pb.lines[i];
|
|
5587
|
+
const h = computeLineHeight(line, pb.resolved);
|
|
5588
|
+
const notes = this.lineFootnotes(line).filter((x) => !counted.has(x.id));
|
|
5589
|
+
const added = notes.reduce((sum, x) => sum + x.heightPt, 0) + (!notesOnPage && notes.length > 0 ? FOOTNOTE_SEPARATOR_HEIGHT : 0);
|
|
5590
|
+
if (y - h < this.bottomLimit() + reserve + added) return i;
|
|
5591
|
+
y -= h;
|
|
5592
|
+
reserve += added;
|
|
5593
|
+
if (notes.length > 0) notesOnPage = true;
|
|
5594
|
+
for (const x of notes) counted.add(x.id);
|
|
5595
|
+
}
|
|
5596
|
+
return pb.lines.length;
|
|
5597
|
+
};
|
|
5598
|
+
/**
|
|
4853
5599
|
* The notes band for the flushing page: separator rule + each note's blocks
|
|
4854
5600
|
* stacked inside the reserved area. Tagged: each note is a Note→P element.
|
|
4855
5601
|
*/
|
|
4856
5602
|
renderNotesBand = () => {
|
|
4857
5603
|
if (this.pageNotes.length === 0) return [];
|
|
4858
5604
|
const out = [];
|
|
4859
|
-
const top = this.
|
|
5605
|
+
const top = this.pageMargins().bottom + this.noteReserve;
|
|
4860
5606
|
out.push({
|
|
4861
5607
|
type: "fill",
|
|
4862
5608
|
x: pt(this.ctx.marginLeft),
|
|
@@ -4872,7 +5618,7 @@ var PageAssembler = class PageAssembler {
|
|
|
4872
5618
|
const noteNode = this.builder.create("Note", this.builder.root);
|
|
4873
5619
|
structId = this.builder.create("P", noteNode).id;
|
|
4874
5620
|
}
|
|
4875
|
-
out.push(...drawBlocksSequentially(note.blocks, this.ctx.marginLeft, cursor, this.ctx.pageHeight, this.ctx.contentWidth, structId));
|
|
5621
|
+
out.push(...drawBlocksSequentially(note.blocks, this.ctx.marginLeft, cursor, this.ctx.pageHeight, this.ctx.contentWidth, structId, void 0, this.collapseSpacing));
|
|
4876
5622
|
cursor -= note.heightPt;
|
|
4877
5623
|
}
|
|
4878
5624
|
return out;
|
|
@@ -4895,35 +5641,38 @@ var PageAssembler = class PageAssembler {
|
|
|
4895
5641
|
const band = bandForPage(this.pageInSection, this.globalPageIdx, this.ctx.titlePg, this.ctx.evenAndOddHeaders);
|
|
4896
5642
|
const header = pickBand(this.ctx.headerSet, band);
|
|
4897
5643
|
const footer = pickBand(this.ctx.footerSet, band);
|
|
5644
|
+
const pageText = pageNumberText(this.pageNumber, this.ctx.properties.pageNumberFormat);
|
|
4898
5645
|
if (header.renderDynamic) this.dynBands.push({
|
|
4899
5646
|
pageIdx: this.pages.length,
|
|
4900
|
-
|
|
5647
|
+
pageText,
|
|
4901
5648
|
position: "header",
|
|
4902
5649
|
render: header.renderDynamic
|
|
4903
5650
|
});
|
|
4904
5651
|
if (footer.renderDynamic) this.dynBands.push({
|
|
4905
5652
|
pageIdx: this.pages.length,
|
|
4906
|
-
|
|
5653
|
+
pageText,
|
|
4907
5654
|
position: "footer",
|
|
4908
5655
|
render: footer.renderDynamic
|
|
4909
5656
|
});
|
|
5657
|
+
const sheet = this.ctx.sheet;
|
|
5658
|
+
const onSheet = (items) => sheet ? items.map((item) => turnOntoSheet(item, sheet.width)) : items;
|
|
4910
5659
|
this.pages.push({
|
|
4911
5660
|
commands: [
|
|
4912
|
-
...this.pageBackgroundItems(),
|
|
4913
|
-
...this.pageBorderItems(),
|
|
4914
|
-
...this.columnSeparatorItems(),
|
|
5661
|
+
...onSheet(this.pageBackgroundItems()),
|
|
5662
|
+
...onSheet(this.pageBorderItems()),
|
|
5663
|
+
...onSheet(this.columnSeparatorItems()),
|
|
4915
5664
|
...header.commands,
|
|
4916
|
-
...PageAssembler.byZ(this.floatsBehind).map((item) => ({
|
|
5665
|
+
...onSheet(PageAssembler.byZ(this.floatsBehind).map((item) => ({
|
|
4917
5666
|
...item,
|
|
4918
5667
|
behind: true
|
|
4919
|
-
})),
|
|
4920
|
-
...this.current,
|
|
4921
|
-
...PageAssembler.byZ(this.floatsFront),
|
|
4922
|
-
...this.renderNotesBand(),
|
|
5668
|
+
}))),
|
|
5669
|
+
...onSheet(this.current),
|
|
5670
|
+
...onSheet(this.inFront(PageAssembler.byZ(this.floatsFront))),
|
|
5671
|
+
...onSheet(this.renderNotesBand()),
|
|
4923
5672
|
...footer.commands
|
|
4924
5673
|
],
|
|
4925
|
-
width: pt(this.ctx.pageWidth),
|
|
4926
|
-
height: pt(this.ctx.pageHeight)
|
|
5674
|
+
width: pt(sheet?.width ?? this.ctx.pageWidth),
|
|
5675
|
+
height: pt(sheet?.height ?? this.ctx.pageHeight)
|
|
4927
5676
|
});
|
|
4928
5677
|
this.current = [];
|
|
4929
5678
|
this.floatsBehind = [];
|
|
@@ -4937,7 +5686,7 @@ var PageAssembler = class PageAssembler {
|
|
|
4937
5686
|
this.globalPageIdx++;
|
|
4938
5687
|
this.pageNumber++;
|
|
4939
5688
|
if ((this.ctx.properties.lineNumbering?.restart ?? "newPage") === "newPage") this.lineNumber = 0;
|
|
4940
|
-
this.cursorY = this.
|
|
5689
|
+
this.cursorY = this.pageTopY();
|
|
4941
5690
|
this.colStartY = this.cursorY;
|
|
4942
5691
|
this.bandTopY = this.cursorY;
|
|
4943
5692
|
this.balanceBottomY = void 0;
|
|
@@ -5126,32 +5875,179 @@ var PageAssembler = class PageAssembler {
|
|
|
5126
5875
|
this.noteReserve = 0;
|
|
5127
5876
|
this.colIdx = 0;
|
|
5128
5877
|
this.colStartLen = 0;
|
|
5129
|
-
this.cursorY = this.
|
|
5878
|
+
this.cursorY = this.pageTopY();
|
|
5130
5879
|
this.colStartY = this.cursorY;
|
|
5131
5880
|
this.bandTopY = this.cursorY;
|
|
5132
5881
|
this.balanceBottomY = void 0;
|
|
5133
5882
|
this.balanceHeightPt = 0;
|
|
5134
5883
|
};
|
|
5135
5884
|
};
|
|
5136
|
-
|
|
5885
|
+
/**
|
|
5886
|
+
* §20.5 — one spreadsheet table's drawings as its pages show them
|
|
5887
|
+
* (TableBlock.overlay). The table lays its rows down page by page, and each
|
|
5888
|
+
* stretch of sheet rows a page takes unbroken is a window onto the sheet:
|
|
5889
|
+
* every drawing over that stretch is drawn where it stands relative to those
|
|
5890
|
+
* rows and cut off at the window's edges. A chart across a page break so
|
|
5891
|
+
* prints in two pieces, one on each page, and a drawing in the title rows on
|
|
5892
|
+
* every page that repeats them — which is what Excel prints.
|
|
5893
|
+
*/
|
|
5894
|
+
var SheetView = class {
|
|
5895
|
+
runs = [];
|
|
5896
|
+
figures = /* @__PURE__ */ new Map();
|
|
5897
|
+
/**
|
|
5898
|
+
* @param overlay The table's drawings and the part of the sheet it prints.
|
|
5899
|
+
* @param tableX The table's left edge on the page.
|
|
5900
|
+
* @param builder The tagged-PDF structure, when the document is tagged.
|
|
5901
|
+
*/
|
|
5902
|
+
constructor(overlay, tableX, builder) {
|
|
5903
|
+
this.overlay = overlay;
|
|
5904
|
+
this.tableX = tableX;
|
|
5905
|
+
this.builder = builder;
|
|
5906
|
+
}
|
|
5907
|
+
/**
|
|
5908
|
+
* A row, or a piece of one, laid down on the page in hand.
|
|
5909
|
+
*
|
|
5910
|
+
* @param sheetTop Where its top stands on the sheet; undefined for a row the sheet does not have.
|
|
5911
|
+
* @param topYUp Where its top stands on the page, y-up.
|
|
5912
|
+
* @param heightPt How tall it was laid out.
|
|
5913
|
+
*/
|
|
5914
|
+
lay(sheetTop, topYUp, heightPt) {
|
|
5915
|
+
if (sheetTop === void 0) return;
|
|
5916
|
+
const last = this.runs[this.runs.length - 1];
|
|
5917
|
+
if (last && Math.abs(last.bottomYUp - topYUp) < .01 && Math.abs(last.sheetBottom - sheetTop) < .5) {
|
|
5918
|
+
last.sheetBottom = sheetTop + heightPt;
|
|
5919
|
+
last.bottomYUp = topYUp - heightPt;
|
|
5920
|
+
return;
|
|
5921
|
+
}
|
|
5922
|
+
this.runs.push({
|
|
5923
|
+
sheetTop,
|
|
5924
|
+
sheetBottom: sheetTop + heightPt,
|
|
5925
|
+
topYUp,
|
|
5926
|
+
bottomYUp: topYUp - heightPt
|
|
5927
|
+
});
|
|
5928
|
+
}
|
|
5929
|
+
/**
|
|
5930
|
+
* Draw what the page's rows show of the drawings, and start over for the
|
|
5931
|
+
* next page.
|
|
5932
|
+
*
|
|
5933
|
+
* @param asm The page assembler, still on the page the rows were laid on.
|
|
5934
|
+
*/
|
|
5935
|
+
show(asm) {
|
|
5936
|
+
const o = this.overlay;
|
|
5937
|
+
const pageHeight = asm.ctx.pageHeight;
|
|
5938
|
+
for (const run of this.runs) {
|
|
5939
|
+
const window = {
|
|
5940
|
+
x: pt(this.tableX + o.leftPt),
|
|
5941
|
+
y: pt(pageHeight - run.topYUp),
|
|
5942
|
+
width: pt(o.widthPt),
|
|
5943
|
+
height: pt(run.topYUp - run.bottomYUp)
|
|
5944
|
+
};
|
|
5945
|
+
o.drawings.forEach((d, i) => {
|
|
5946
|
+
const { block } = d;
|
|
5947
|
+
if (d.yPt > run.sheetBottom || d.yPt + block.heightPt < run.sheetTop) return;
|
|
5948
|
+
const left = d.xPt - o.sheetLeftPt;
|
|
5949
|
+
if (left > o.widthPt || left + block.widthPt < 0) return;
|
|
5950
|
+
const x = this.tableX + o.leftPt + (o.mirrored ? o.widthPt - left - block.widthPt : left);
|
|
5951
|
+
const items = [];
|
|
5952
|
+
emitDrawing(block, x, run.topYUp - (d.yPt - run.sheetTop), items, pageHeight, void 0);
|
|
5953
|
+
const shown = items.filter((item) => {
|
|
5954
|
+
const box = pageItemBounds(item);
|
|
5955
|
+
return box.x1 >= window.x && box.x0 <= window.x + window.width && box.y1 >= window.y && box.y0 <= window.y + window.height;
|
|
5956
|
+
});
|
|
5957
|
+
if (shown.length === 0) return;
|
|
5958
|
+
const figId = this.figure(i, block);
|
|
5959
|
+
seenThrough(PageAssembler.zSink(asm.floatsFront, block.float?.zOrder), window).push(...figId === void 0 ? shown : shown.map((item) => ({
|
|
5960
|
+
...item,
|
|
5961
|
+
structId: figId
|
|
5962
|
+
})));
|
|
5963
|
+
});
|
|
5964
|
+
}
|
|
5965
|
+
this.runs = [];
|
|
5966
|
+
}
|
|
5967
|
+
/** The tagged-PDF Figure a drawing is, made once however many pages show it. */
|
|
5968
|
+
figure(i, block) {
|
|
5969
|
+
if (!this.builder) return void 0;
|
|
5970
|
+
let id = this.figures.get(i);
|
|
5971
|
+
if (id === void 0) {
|
|
5972
|
+
const role = block.kind === "chart" ? block.figureRole ?? "Chart" : block.kind === "image" ? "Image" : "Shape";
|
|
5973
|
+
id = createFigure(this.builder, block.altText, role);
|
|
5974
|
+
this.figures.set(i, id);
|
|
5975
|
+
}
|
|
5976
|
+
return id;
|
|
5977
|
+
}
|
|
5978
|
+
};
|
|
5979
|
+
/** A sink that marks each item it takes as seen through `window` (PageItemBase.window). */
|
|
5980
|
+
var seenThrough = (target, window) => ({ push: (...items) => target.push(...items.map((item) => ({
|
|
5981
|
+
...item,
|
|
5982
|
+
window
|
|
5983
|
+
}))) });
|
|
5984
|
+
/**
|
|
5985
|
+
* §17.3.1.14/15 — the height a paragraph needs in one column: all of its lines
|
|
5986
|
+
* where it keeps them together, and where it keeps with the next, the whole of
|
|
5987
|
+
* every paragraph in the chain it heads and as much of the block after as has
|
|
5988
|
+
* to stand beside it — a paragraph's first line, or its first two (all of a
|
|
5989
|
+
* short one) where its own widow control would not leave one alone; a table's
|
|
5990
|
+
* first row; a picture. The space between them counts, the chain's own space
|
|
5991
|
+
* before does not. A chain is decided at its head: one that follows a
|
|
5992
|
+
* paragraph kept with it moved with that paragraph or not at all, and asks
|
|
5993
|
+
* only whether its own lines stay together.
|
|
5994
|
+
*
|
|
5995
|
+
* @param blocks The section's blocks.
|
|
5996
|
+
* @param from The paragraph asked about.
|
|
5997
|
+
* @param end The section's end: nothing is kept with what lies past it.
|
|
5998
|
+
* @returns The height, or undefined where the paragraph asks for neither.
|
|
5999
|
+
*/
|
|
6000
|
+
function keptTogetherHeight(blocks, from, end, collapse = false) {
|
|
6001
|
+
const first = blocks[from];
|
|
6002
|
+
if (first?.kind !== "paragraph") return void 0;
|
|
6003
|
+
const before = blocks[from - 1];
|
|
6004
|
+
const inChain = before?.kind === "paragraph" && before.resolved.keepNext && !before.pageBreakAfter && !first.resolved.pageBreakBefore && !first.pageBreakBefore;
|
|
6005
|
+
const keepsNext = first.resolved.keepNext && !inChain;
|
|
6006
|
+
if (!keepsNext && !first.resolved.keepLines) return void 0;
|
|
6007
|
+
let height = first.heightPt;
|
|
6008
|
+
let current = first;
|
|
6009
|
+
for (let i = from + 1; keepsNext && current.resolved.keepNext && !current.pageBreakAfter; i++) {
|
|
6010
|
+
const next = i < end ? blocks[i] : void 0;
|
|
6011
|
+
if (next?.kind === "paragraph") {
|
|
6012
|
+
if (next.resolved.pageBreakBefore || next.pageBreakBefore) break;
|
|
6013
|
+
height += collapse ? Math.max(current.spacingAfterPt, next.spacingBeforePt) : current.spacingAfterPt + next.spacingBeforePt;
|
|
6014
|
+
if (next.resolved.keepNext) {
|
|
6015
|
+
height += next.heightPt;
|
|
6016
|
+
current = next;
|
|
6017
|
+
continue;
|
|
6018
|
+
}
|
|
6019
|
+
const count = next.lines.length;
|
|
6020
|
+
const stays = next.resolved.keepLines ? count : next.resolved.widowControl ? count <= 3 ? count : 2 : 1;
|
|
6021
|
+
for (const line of next.lines.slice(0, stays)) height += computeLineHeight(line, next.resolved);
|
|
6022
|
+
} else if (next?.kind === "table" && !next.float) height += current.spacingAfterPt + (next.rows[0]?.heightPt ?? 0);
|
|
6023
|
+
else if (next?.kind === "image" && !next.float) height += current.spacingAfterPt + next.heightPt;
|
|
6024
|
+
break;
|
|
6025
|
+
}
|
|
6026
|
+
return height;
|
|
6027
|
+
}
|
|
6028
|
+
function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", notes, bookmarkPositions, reflowParagraph, backgroundImage, typesetByWord = false, compatibilityMode) {
|
|
5137
6029
|
if (sectionCtxs.length === 0) return [];
|
|
5138
6030
|
const asm = new PageAssembler(sectionCtxs, builder, notes, bookmarkPositions, backgroundImage);
|
|
6031
|
+
asm.collapseSpacing = typesetByWord;
|
|
6032
|
+
asm.keepsBeforeAtOwnBreak = (compatibilityMode ?? 0) < 15;
|
|
5139
6033
|
const balanceIfEndsContinuous = (fromBlock) => {
|
|
5140
6034
|
const here = asm.ctx;
|
|
5141
6035
|
const next = sectionCtxs[asm.secIdx + 1];
|
|
5142
6036
|
if ((here.columns?.length ?? 1) < 2) return;
|
|
5143
6037
|
if (!next?.continuous) return;
|
|
5144
6038
|
if (next.pageWidth !== here.pageWidth || next.pageHeight !== here.pageHeight) return;
|
|
6039
|
+
if (next.sheet === void 0 !== (here.sheet === void 0)) return;
|
|
5145
6040
|
const end = Math.min(here.endIndex, blocks.length);
|
|
5146
|
-
asm.beginBalancedBand(blocksHeight(blocks.slice(fromBlock, end)));
|
|
6041
|
+
asm.beginBalancedBand(blocksHeight(blocks.slice(fromBlock, end), typesetByWord));
|
|
5147
6042
|
};
|
|
5148
6043
|
balanceIfEndsContinuous(0);
|
|
6044
|
+
let brokenFor;
|
|
5149
6045
|
for (let blockIdx = 0; blockIdx < blocks.length; blockIdx++) {
|
|
5150
6046
|
while (asm.secIdx < sectionCtxs.length - 1 && blockIdx >= asm.ctx.endIndex) {
|
|
5151
6047
|
const next = sectionCtxs[asm.secIdx + 1];
|
|
5152
|
-
if (next.continuous && next.pageWidth === asm.ctx.pageWidth && next.pageHeight === asm.ctx.pageHeight) {
|
|
6048
|
+
if (next.continuous && next.pageWidth === asm.ctx.pageWidth && next.pageHeight === asm.ctx.pageHeight && next.sheet === void 0 === (asm.ctx.sheet === void 0)) {
|
|
5153
6049
|
const end = Math.min(next.endIndex, blocks.length);
|
|
5154
|
-
asm.startBandSection(next, blocksHeight(blocks.slice(blockIdx, end)));
|
|
6050
|
+
asm.startBandSection(next, blocksHeight(blocks.slice(blockIdx, end), typesetByWord));
|
|
5155
6051
|
continue;
|
|
5156
6052
|
}
|
|
5157
6053
|
if (asm.pageHasContent() || asm.pageInSection === 0) asm.flushPage(true);
|
|
@@ -5160,7 +6056,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
5160
6056
|
const wants = opening?.sectionStart;
|
|
5161
6057
|
if (wants === "oddPage" || wants === "evenPage") {
|
|
5162
6058
|
if ((opening?.pageNumberStart ?? asm.pageNumber) % 2 === 1 !== (wants === "oddPage")) {
|
|
5163
|
-
asm.cursorY = asm.
|
|
6059
|
+
asm.cursorY = asm.pageTopY();
|
|
5164
6060
|
asm.flushPage(true);
|
|
5165
6061
|
}
|
|
5166
6062
|
}
|
|
@@ -5168,18 +6064,43 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
5168
6064
|
asm.ctx = sectionCtxs[asm.secIdx];
|
|
5169
6065
|
asm.restartPageNumbers(asm.ctx);
|
|
5170
6066
|
asm.pageInSection = 0;
|
|
5171
|
-
asm.cursorY = asm.
|
|
6067
|
+
asm.cursorY = asm.pageTopY();
|
|
6068
|
+
asm.sectionStart = true;
|
|
5172
6069
|
balanceIfEndsContinuous(blockIdx);
|
|
5173
6070
|
}
|
|
5174
6071
|
const block = blocks[blockIdx];
|
|
6072
|
+
const opensSection = asm.sectionStart;
|
|
6073
|
+
const anchored = "float" in block && isOutOfFlowFloat(block.float);
|
|
6074
|
+
if (!anchored) asm.sectionStart = false;
|
|
5175
6075
|
if (asm.pendingPageBreak) {
|
|
5176
6076
|
asm.pendingPageBreak = false;
|
|
5177
6077
|
asm.flushPage(true);
|
|
5178
6078
|
}
|
|
6079
|
+
if (typesetByWord && anchored && asm.pageHasContent() && brokenFor === void 0) {
|
|
6080
|
+
let owner = blockIdx + 1;
|
|
6081
|
+
while (owner < blocks.length) {
|
|
6082
|
+
const b = blocks[owner];
|
|
6083
|
+
if (!("float" in b && isOutOfFlowFloat(b.float))) break;
|
|
6084
|
+
owner++;
|
|
6085
|
+
}
|
|
6086
|
+
const para = blocks[owner];
|
|
6087
|
+
if (para?.kind === "paragraph" && para.resolved.pageBreakBefore) {
|
|
6088
|
+
asm.flushPage();
|
|
6089
|
+
brokenFor = owner;
|
|
6090
|
+
}
|
|
6091
|
+
}
|
|
5179
6092
|
if (builder && !(block.kind === "paragraph" && block.list)) asm.listStack.length = 0;
|
|
5180
6093
|
if (block.kind === "paragraph") {
|
|
5181
|
-
|
|
5182
|
-
|
|
6094
|
+
const brokeAhead = brokenFor === blockIdx;
|
|
6095
|
+
brokenFor = void 0;
|
|
6096
|
+
const ownBreak = block.resolved.pageBreakBefore && (brokeAhead || asm.pageHasContent());
|
|
6097
|
+
if (!brokeAhead && (block.resolved.pageBreakBefore || block.pageBreakBefore) && asm.pageHasContent()) asm.flushPage();
|
|
6098
|
+
const columnInUse = asm.colHasContent();
|
|
6099
|
+
const opensDocument = opensSection && asm.pages.length === 0 && asm.secIdx === 0;
|
|
6100
|
+
const beforePt = asm.collapseSpacing && opensDocument && block.resolved.spacingBeforeAuto !== void 0 ? 0 : block.spacingBeforePt;
|
|
6101
|
+
asm.cursorY -= asm.openBefore(beforePt, opensSection, ownBreak);
|
|
6102
|
+
const kept = keptTogetherHeight(blocks, blockIdx, asm.ctx.endIndex, typesetByWord);
|
|
6103
|
+
if (kept !== void 0 && columnInUse && asm.exclusions.length === 0 && kept > asm.cursorY - asm.bottomLimit()) asm.advanceColumn();
|
|
5183
6104
|
let pb = block;
|
|
5184
6105
|
const colW = asm.colWidth();
|
|
5185
6106
|
if (reflowParagraph && pb.source && Math.abs(colW - asm.builtWidth()) > .5) pb = reflowParagraph(pb.source, colW);
|
|
@@ -5187,8 +6108,26 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
5187
6108
|
const widths = asm.lineWidthsFor(pb, asm.cursorY);
|
|
5188
6109
|
if (widths) pb = reflowParagraph(pb.source, colW, widths);
|
|
5189
6110
|
}
|
|
5190
|
-
|
|
5191
|
-
|
|
6111
|
+
let borderTopY = asm.cursorY;
|
|
6112
|
+
let borderPage = asm.pages.length;
|
|
6113
|
+
const keptColumnEnd = (from) => {
|
|
6114
|
+
const n = pb.lines.length;
|
|
6115
|
+
if (!pb.resolved.widowControl || n < 2 || asm.exclusions.length > 0) return void 0;
|
|
6116
|
+
let end = asm.firstLineOverflowing(pb, from);
|
|
6117
|
+
if (end >= n) return void 0;
|
|
6118
|
+
if (n - end === 1) end--;
|
|
6119
|
+
if (from === 0 && end === 1) end = 0;
|
|
6120
|
+
if (end <= from) return asm.colHasContent() ? from : from + 1;
|
|
6121
|
+
return end;
|
|
6122
|
+
};
|
|
6123
|
+
const advanceBefore = (lineIdx) => {
|
|
6124
|
+
asm.advanceColumn();
|
|
6125
|
+
if (lineIdx === 0) {
|
|
6126
|
+
borderTopY = asm.cursorY;
|
|
6127
|
+
borderPage = asm.pages.length;
|
|
6128
|
+
}
|
|
6129
|
+
};
|
|
6130
|
+
let columnEnd = keptColumnEnd(0);
|
|
5192
6131
|
let structId;
|
|
5193
6132
|
let markerLblId;
|
|
5194
6133
|
const firstLine = pb.lines[0];
|
|
@@ -5205,13 +6144,21 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
5205
6144
|
let firstLineOfBlock = true;
|
|
5206
6145
|
let pairPending;
|
|
5207
6146
|
for (const [lineIdx, line] of pb.lines.entries()) {
|
|
5208
|
-
if (pb.columnBreakLines?.has(lineIdx) && asm.colHasContent())
|
|
6147
|
+
if (pb.columnBreakLines?.has(lineIdx) && asm.colHasContent()) {
|
|
6148
|
+
advanceBefore(lineIdx);
|
|
6149
|
+
columnEnd = keptColumnEnd(lineIdx);
|
|
6150
|
+
}
|
|
6151
|
+
if (lineIdx === columnEnd && asm.colHasContent()) {
|
|
6152
|
+
advanceBefore(lineIdx);
|
|
6153
|
+
columnEnd = keptColumnEnd(lineIdx);
|
|
6154
|
+
}
|
|
5209
6155
|
const h = computeLineHeight(line, pb.resolved);
|
|
5210
6156
|
let newNotes = asm.lineFootnotes(line);
|
|
5211
6157
|
const addedReserve = (sub) => sub.reduce((sum, x) => sum + x.heightPt, 0) + (asm.pageNotes.length === 0 && sub.length > 0 ? FOOTNOTE_SEPARATOR_HEIGHT : 0);
|
|
5212
6158
|
if (asm.cursorY - h < asm.bottomLimit() + addedReserve(newNotes) && asm.colHasContent()) {
|
|
5213
|
-
|
|
6159
|
+
advanceBefore(lineIdx);
|
|
5214
6160
|
newNotes = asm.lineFootnotes(line);
|
|
6161
|
+
columnEnd = keptColumnEnd(lineIdx);
|
|
5215
6162
|
}
|
|
5216
6163
|
if (newNotes.length > 0) {
|
|
5217
6164
|
asm.noteReserve += addedReserve(newNotes);
|
|
@@ -5291,6 +6238,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
5291
6238
|
}
|
|
5292
6239
|
if (asm.pages.length === borderPage) asm.current.push(...paragraphDecoration(pb.resolved, asm.colLeft() + pb.resolved.indentLeft, asm.colLeft() + asm.colWidth() - pb.resolved.indentRight, borderTopY, asm.cursorY, asm.ctx.pageHeight));
|
|
5293
6240
|
asm.cursorY -= pb.spacingAfterPt;
|
|
6241
|
+
asm.closeAfter(pb.spacingAfterPt);
|
|
5294
6242
|
if (pb.pageBreakAfter) asm.pendingPageBreak = true;
|
|
5295
6243
|
} else if (block.kind === "image") {
|
|
5296
6244
|
const figId = builder ? createFigure(builder, block.altText, "Image") : void 0;
|
|
@@ -5321,12 +6269,13 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
5321
6269
|
emitImageAt(fx, fy, PageAssembler.zSink(block.float.behind ? asm.floatsBehind : asm.floatsFront, block.float.zOrder));
|
|
5322
6270
|
if (block.float.wrap !== "none") asm.excludeFloat(block.float, fx, fy, block.widthPt, block.heightPt);
|
|
5323
6271
|
} else {
|
|
5324
|
-
asm.cursorY -= block.spacingBeforePt;
|
|
6272
|
+
asm.cursorY -= asm.openBefore(block.spacingBeforePt, opensSection);
|
|
5325
6273
|
if (asm.cursorY - block.heightPt < asm.bottomLimit() && asm.colHasContent()) asm.advanceColumn();
|
|
5326
6274
|
asm.cursorY -= block.heightPt;
|
|
5327
6275
|
const offset = alignmentOffset(block.resolvedAlignment, block.widthPt, asm.colWidth());
|
|
5328
6276
|
emitImageAt(asm.colLeft() + offset, asm.cursorY + block.heightPt, asm.current);
|
|
5329
6277
|
asm.cursorY -= block.spacingAfterPt;
|
|
6278
|
+
asm.closeAfter(sharedAfter(block));
|
|
5330
6279
|
}
|
|
5331
6280
|
} else if (block.kind === "shape") {
|
|
5332
6281
|
const figId = builder ? createFigure(builder, block.altText, "Shape") : void 0;
|
|
@@ -5337,12 +6286,13 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
5337
6286
|
emitShapeAt(fx, fy - block.heightPt, PageAssembler.zSink(block.float.behind ? asm.floatsBehind : asm.floatsFront, block.float.zOrder));
|
|
5338
6287
|
if (block.float.wrap !== "none") asm.excludeFloat(block.float, fx, fy, block.widthPt, block.heightPt);
|
|
5339
6288
|
} else {
|
|
5340
|
-
asm.cursorY -= block.spacingBeforePt;
|
|
6289
|
+
asm.cursorY -= asm.openBefore(block.spacingBeforePt, opensSection);
|
|
5341
6290
|
if (asm.cursorY - block.heightPt < asm.bottomLimit() && asm.colHasContent()) asm.advanceColumn();
|
|
5342
6291
|
asm.cursorY -= block.heightPt;
|
|
5343
6292
|
const offset = alignmentOffset(block.resolvedAlignment, block.widthPt, asm.colWidth());
|
|
5344
6293
|
emitShapeAt(asm.colLeft() + offset, asm.cursorY, asm.current);
|
|
5345
6294
|
asm.cursorY -= block.spacingAfterPt;
|
|
6295
|
+
asm.closeAfter(block.spacingAfterPt);
|
|
5346
6296
|
}
|
|
5347
6297
|
} else if (block.kind === "chart") {
|
|
5348
6298
|
const figId = builder ? createFigure(builder, block.altText, block.figureRole ?? "Chart") : void 0;
|
|
@@ -5355,12 +6305,13 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
5355
6305
|
emitChartAt(fx, fy - block.heightPt, PageAssembler.zSink(block.float.behind ? asm.floatsBehind : asm.floatsFront, block.float.zOrder));
|
|
5356
6306
|
if (block.float.wrap !== "none") asm.excludeFloat(block.float, fx, fy, block.widthPt, block.heightPt);
|
|
5357
6307
|
} else {
|
|
5358
|
-
asm.cursorY -= block.spacingBeforePt;
|
|
6308
|
+
asm.cursorY -= asm.openBefore(block.spacingBeforePt, opensSection);
|
|
5359
6309
|
if (asm.cursorY - block.heightPt < asm.bottomLimit() && asm.colHasContent()) asm.advanceColumn();
|
|
5360
6310
|
asm.cursorY -= block.heightPt;
|
|
5361
6311
|
const offset = alignmentOffset(block.resolvedAlignment, block.widthPt, asm.colWidth());
|
|
5362
6312
|
emitChartAt(asm.colLeft() + offset, asm.cursorY, asm.current);
|
|
5363
6313
|
asm.cursorY -= block.spacingAfterPt;
|
|
6314
|
+
asm.closeAfter(sharedAfter(block));
|
|
5364
6315
|
}
|
|
5365
6316
|
} else {
|
|
5366
6317
|
const colCount = block.rows.reduce((max, r) => Math.max(max, r.cells.reduce((s, c) => Math.max(s, c.colStart + c.colSpan), 0)), 0);
|
|
@@ -5383,6 +6334,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
5383
6334
|
const headerHeightPt = headerRows.reduce((s, r) => s + r.heightPt, 0);
|
|
5384
6335
|
const tableNode = builder ? builder.create("Table", builder.root) : void 0;
|
|
5385
6336
|
const tableX = asm.colLeft() + block.xOffsetPt;
|
|
6337
|
+
const sheetView = block.overlay ? new SheetView(block.overlay, tableX, builder) : void 0;
|
|
5386
6338
|
for (let ri = 0; ri < block.rows.length; ri++) {
|
|
5387
6339
|
const row = block.rows[ri];
|
|
5388
6340
|
const isLeadingHeader = ri < headerRows.length;
|
|
@@ -5403,20 +6355,25 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
5403
6355
|
const room = asm.cursorY - asm.bottomLimit();
|
|
5404
6356
|
const page = asm.ctx.pageContentHeight;
|
|
5405
6357
|
const chunks = !row.cantSplit && row.heightPt > room && asm.colHasContent() && room > 0 ? splitRowIntoChunks(row, room, page) : row.heightPt > page ? splitRowIntoChunks(row, page) : [row];
|
|
6358
|
+
let chunkOffsetPt = 0;
|
|
5406
6359
|
for (let ci = 0; ci < chunks.length; ci++) {
|
|
5407
6360
|
const chunk = chunks[ci];
|
|
5408
|
-
const forcedBreak = ci === 0 && row.breakBefore && !isLeadingHeader && asm.pageHasContent();
|
|
6361
|
+
const forcedBreak = ci === 0 && row.breakBefore && (ri === 0 || !isLeadingHeader) && asm.pageHasContent();
|
|
5409
6362
|
const overflow = asm.cursorY - chunk.heightPt < asm.bottomLimit() && asm.colHasContent();
|
|
5410
6363
|
if (forcedBreak || overflow) {
|
|
6364
|
+
sheetView?.show(asm);
|
|
5411
6365
|
if (forcedBreak) asm.flushPage();
|
|
5412
6366
|
else asm.advanceColumn();
|
|
5413
6367
|
if (!isLeadingHeader && headerRows.length > 0 && asm.cursorY - headerHeightPt - chunk.heightPt >= asm.bottomLimit()) for (const hr of headerRows) {
|
|
5414
6368
|
emitRowChunk(asm.current, hr, tableX, asm.cursorY, asm.ctx.pageHeight, colCount, void 0);
|
|
6369
|
+
sheetView?.lay(hr.sheetTopPt, asm.cursorY, hr.heightPt);
|
|
5415
6370
|
asm.cursorY -= hr.heightPt;
|
|
5416
6371
|
}
|
|
5417
6372
|
}
|
|
5418
6373
|
const mergedHeights = chunks.length === 1 && row.cells.some((c) => c.mergeRole === "start") ? row.cells.map((c) => c.mergeRole === "start" ? mergedBoxHeightPt(block.rows, ri, c.colStart, asm.cursorY - asm.bottomLimit()) : void 0) : void 0;
|
|
5419
6374
|
emitRowChunk(asm.current, chunk, tableX, asm.cursorY, asm.ctx.pageHeight, colCount, cellStructIds, mergedHeights, cellAnchorCtx(asm));
|
|
6375
|
+
sheetView?.lay(row.sheetTopPt !== void 0 ? row.sheetTopPt + chunkOffsetPt : void 0, asm.cursorY, chunk.heightPt);
|
|
6376
|
+
chunkOffsetPt += chunk.heightPt;
|
|
5420
6377
|
asm.cursorY -= chunk.heightPt;
|
|
5421
6378
|
if (ci === chunks.length - 1 && ri < block.rows.length - 1) {
|
|
5422
6379
|
const next = block.rows[ri + 1];
|
|
@@ -5427,12 +6384,13 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
5427
6384
|
}
|
|
5428
6385
|
}
|
|
5429
6386
|
}
|
|
6387
|
+
sheetView?.show(asm);
|
|
5430
6388
|
}
|
|
5431
6389
|
}
|
|
5432
6390
|
asm.flushPage();
|
|
5433
6391
|
if (asm.pages.length === 0) asm.flushPage(true);
|
|
5434
6392
|
for (const d of asm.dynBands) {
|
|
5435
|
-
const cmds = d.render(d.
|
|
6393
|
+
const cmds = d.render(d.pageText, asm.pages.length);
|
|
5436
6394
|
const page = asm.pages[d.pageIdx];
|
|
5437
6395
|
if (d.position === "header") page.commands.unshift(...cmds);
|
|
5438
6396
|
else page.commands.push(...cmds);
|
|
@@ -5507,7 +6465,7 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
|
|
|
5507
6465
|
}
|
|
5508
6466
|
out.push({
|
|
5509
6467
|
type: "fill",
|
|
5510
|
-
x: pt(marginLeft + (row.columnXOffsets[i] ?? 0)),
|
|
6468
|
+
x: pt(marginLeft + (row.columnXOffsets[i] ?? 0) + (cell.paintOffsetPt ?? 0)),
|
|
5511
6469
|
y: pt(pageHeight - rowBottom - row.heightPt - FILL_SEAM_PT),
|
|
5512
6470
|
width: pt(width),
|
|
5513
6471
|
height: pt(row.heightPt + FILL_SEAM_PT),
|
|
@@ -5522,16 +6480,19 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
|
|
|
5522
6480
|
const structId = cellStructIds?.[i];
|
|
5523
6481
|
if (cell.dataBar && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
|
|
5524
6482
|
const start = Math.max(0, Math.min(1, cell.dataBar.startFraction ?? 0));
|
|
5525
|
-
const
|
|
6483
|
+
const inner = Math.max(0, cell.widthPt - 2 * DATA_BAR_INSET_PT);
|
|
6484
|
+
const barWidth = inner * Math.max(0, Math.min(1, cell.dataBar.fraction));
|
|
5526
6485
|
if (barWidth > 0) {
|
|
5527
|
-
const barX = cellX +
|
|
5528
|
-
const
|
|
6486
|
+
const barX = cellX + DATA_BAR_INSET_PT + inner * start;
|
|
6487
|
+
const boxHeightPt = Math.max(row.heightPt, mergedHeights?.[i] ?? row.heightPt);
|
|
6488
|
+
const barHeightPt = Math.max(0, boxHeightPt - 2 * DATA_BAR_INSET_PT);
|
|
6489
|
+
const barBottomYUp = rowBottom + row.heightPt - boxHeightPt + DATA_BAR_INSET_PT;
|
|
5529
6490
|
out.push({
|
|
5530
6491
|
type: "shape",
|
|
5531
6492
|
shape: {
|
|
5532
|
-
paths: [rectAtPath(0, 0, barWidth,
|
|
6493
|
+
paths: [rectAtPath(0, 0, barWidth, barHeightPt)],
|
|
5533
6494
|
fillColorHex: cell.dataBar.colorHex,
|
|
5534
|
-
fillGradient: {
|
|
6495
|
+
...cell.dataBar.solid ? {} : { fillGradient: {
|
|
5535
6496
|
kind: "linear",
|
|
5536
6497
|
angle: cell.dataBar.negative ? 180 : 0,
|
|
5537
6498
|
stops: [{
|
|
@@ -5541,7 +6502,7 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
|
|
|
5541
6502
|
offset: 1,
|
|
5542
6503
|
colorHex: "FFFFFF"
|
|
5543
6504
|
}]
|
|
5544
|
-
},
|
|
6505
|
+
} },
|
|
5545
6506
|
transform: flipTransform([
|
|
5546
6507
|
1,
|
|
5547
6508
|
0,
|
|
@@ -5598,6 +6559,28 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
|
|
|
5598
6559
|
});
|
|
5599
6560
|
}
|
|
5600
6561
|
}
|
|
6562
|
+
if (cell.noteFlag && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
|
|
6563
|
+
const left = cell.paintOffsetPt ?? 0;
|
|
6564
|
+
const right = left + (cell.paintWidthPt ?? cell.widthPt);
|
|
6565
|
+
const side = Math.min(NOTE_FLAG_PT, row.heightPt, right - left);
|
|
6566
|
+
const top = row.heightPt;
|
|
6567
|
+
const corner = cell.noteFlag.atLeft ? new PathBuilder().moveTo(left, top).lineTo(left + side, top).lineTo(left, top - side) : new PathBuilder().moveTo(right - side, top).lineTo(right, top).lineTo(right, top - side);
|
|
6568
|
+
out.push({
|
|
6569
|
+
type: "shape",
|
|
6570
|
+
shape: {
|
|
6571
|
+
paths: [corner.close().build()],
|
|
6572
|
+
fillColorHex: cell.noteFlag.kind === "thread" ? NOTE_THREAD_HEX : NOTE_FLAG_HEX,
|
|
6573
|
+
transform: flipTransform([
|
|
6574
|
+
1,
|
|
6575
|
+
0,
|
|
6576
|
+
0,
|
|
6577
|
+
1,
|
|
6578
|
+
cellX,
|
|
6579
|
+
rowBottom
|
|
6580
|
+
], pageHeight)
|
|
6581
|
+
}
|
|
6582
|
+
});
|
|
6583
|
+
}
|
|
5601
6584
|
if (cell.sparkline && cell.sparkline.values.length > 0 && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
|
|
5602
6585
|
const inset = 1.5;
|
|
5603
6586
|
const sw = cell.widthPt - 2 * inset;
|
|
@@ -5623,7 +6606,7 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
|
|
|
5623
6606
|
});
|
|
5624
6607
|
}
|
|
5625
6608
|
}
|
|
5626
|
-
emitCellBorders(out, cell, cellX, rowBottom, row.heightPt, pageHeight, row.rowIdx, row.rowCount, colCount);
|
|
6609
|
+
emitCellBorders(out, cell, cellX, rowBottom, row.heightPt, pageHeight, row.rowIdx, row.rowCount, colCount, row.borderBands);
|
|
5627
6610
|
const diagDown = cell.borders.diagonalDown;
|
|
5628
6611
|
const diagUp = cell.borders.diagonalUp;
|
|
5629
6612
|
if ((diagDown || diagUp) && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
|
|
@@ -5840,17 +6823,19 @@ function splitRowIntoChunks(row, capacity, restCapacity = capacity) {
|
|
|
5840
6823
|
return out.length > 0 ? out : [row];
|
|
5841
6824
|
}
|
|
5842
6825
|
var FILL_SEAM_PT = .07;
|
|
5843
|
-
function emitCellBorders(out, cell, cellX, cellY, rowHeight, pageHeight, rowIdx, rowCount, colCount) {
|
|
6826
|
+
function emitCellBorders(out, cell, cellX, cellY, rowHeight, pageHeight, rowIdx, rowCount, colCount, bands) {
|
|
5844
6827
|
const pushSide = (side, border) => {
|
|
5845
6828
|
if (!border || border.style === "none") return;
|
|
5846
6829
|
const sz = border.width ?? DEFAULT_BORDER_SIZE_EIGHTH * EIGHTH_PT;
|
|
6830
|
+
const lowered = side === "top" ? (bands?.topPt ?? 0) / 2 : 0;
|
|
6831
|
+
const raised = side === "bottom" ? (bands?.bottomPt ?? 0) / 2 : 0;
|
|
5847
6832
|
out.push({
|
|
5848
6833
|
type: "border",
|
|
5849
6834
|
side,
|
|
5850
|
-
x: pt(cellX),
|
|
5851
|
-
y: pt(pageHeight - cellY - rowHeight),
|
|
6835
|
+
x: pt(side === "left" ? cellX : cellX + (cell.paintOffsetPt ?? 0)),
|
|
6836
|
+
y: pt(pageHeight - cellY - rowHeight + lowered),
|
|
5852
6837
|
width: pt(cell.paintWidthPt ?? cell.widthPt),
|
|
5853
|
-
height: pt(rowHeight),
|
|
6838
|
+
height: pt(rowHeight - lowered - raised),
|
|
5854
6839
|
borderSizePt: sz,
|
|
5855
6840
|
borderColorHex: border.colorHex ?? "000000",
|
|
5856
6841
|
...border.style !== "single" ? { borderStyle: border.style } : {}
|