reamkit 1.30.0 → 1.31.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.
Files changed (78) hide show
  1. package/README.md +60 -24
  2. package/dist/esm/core/converter/project.js +3 -1
  3. package/dist/esm/core/crypto/offcrypto.js +1 -1
  4. package/dist/esm/core/document-model/index.d.ts +1 -1
  5. package/dist/esm/core/document-model/types.d.ts +63 -0
  6. package/dist/esm/core/font/index.d.ts +2 -0
  7. package/dist/esm/core/font/ttf-build.d.ts +113 -0
  8. package/dist/esm/core/font/ttf-build.js +1224 -0
  9. package/dist/esm/core/font/ttf-subset.d.ts +19 -0
  10. package/dist/esm/core/font/ttf-subset.js +14 -1
  11. package/dist/esm/core/fonts/provider.d.ts +17 -0
  12. package/dist/esm/core/fonts/provider.js +27 -2
  13. package/dist/esm/core/fonts/remote-fonts.js +8 -1
  14. package/dist/esm/core/ir/flow.d.ts +71 -1
  15. package/dist/esm/core/numbering/index.d.ts +1 -1
  16. package/dist/esm/core/numbering/state.d.ts +11 -1
  17. package/dist/esm/core/numbering/state.js +10 -1
  18. package/dist/esm/core/style-cascade/resolver.js +35 -4
  19. package/dist/esm/core/style-cascade/types.d.ts +19 -1
  20. package/dist/esm/core/style-cascade/types.js +3 -0
  21. package/dist/esm/index.d.ts +2 -2
  22. package/dist/esm/layout/page-doc.d.ts +10 -3
  23. package/dist/esm/layout/styled-layout.d.ts +48 -7
  24. package/dist/esm/layout/styled-layout.js +881 -119
  25. package/dist/esm/layout/turned-section.d.ts +38 -0
  26. package/dist/esm/layout/turned-section.js +193 -0
  27. package/dist/esm/pdf/styled-page-emitter.js +78 -2
  28. package/dist/esm/pdf-reader/cff-outline.d.ts +27 -0
  29. package/dist/esm/pdf-reader/cff-outline.js +169 -21
  30. package/dist/esm/pdf-reader/content.d.ts +53 -0
  31. package/dist/esm/pdf-reader/content.js +9 -1
  32. package/dist/esm/pdf-reader/display.d.ts +36 -0
  33. package/dist/esm/pdf-reader/display.js +66 -1
  34. package/dist/esm/pdf-reader/document.js +5 -1
  35. package/dist/esm/pdf-reader/embedded-fonts.d.ts +2 -1
  36. package/dist/esm/pdf-reader/embedded-fonts.js +32 -2
  37. package/dist/esm/pdf-reader/encodings.d.ts +8 -0
  38. package/dist/esm/pdf-reader/encodings.js +25 -3
  39. package/dist/esm/pdf-reader/face-outlines.d.ts +78 -0
  40. package/dist/esm/pdf-reader/face-outlines.js +362 -0
  41. package/dist/esm/pdf-reader/figures.d.ts +52 -0
  42. package/dist/esm/pdf-reader/figures.js +433 -0
  43. package/dist/esm/pdf-reader/flow-build.d.ts +131 -5
  44. package/dist/esm/pdf-reader/flow-build.js +345 -26
  45. package/dist/esm/pdf-reader/font.js +321 -36
  46. package/dist/esm/pdf-reader/glyf-outline.d.ts +33 -0
  47. package/dist/esm/pdf-reader/glyf-outline.js +135 -1
  48. package/dist/esm/pdf-reader/glyph-names.js +154 -1
  49. package/dist/esm/pdf-reader/layout.d.ts +93 -0
  50. package/dist/esm/pdf-reader/layout.js +1703 -214
  51. package/dist/esm/pdf-reader/page-numbers.d.ts +53 -0
  52. package/dist/esm/pdf-reader/page-numbers.js +167 -0
  53. package/dist/esm/pdf-reader/tagged.js +182 -23
  54. package/dist/esm/pdf-reader/text.d.ts +6 -3
  55. package/dist/esm/pdf-reader/text.js +98 -9
  56. package/dist/esm/pdf-reader/type1-outline.d.ts +11 -0
  57. package/dist/esm/pdf-reader/type1-outline.js +63 -8
  58. package/dist/esm/pdf-reader/vector.js +71 -1
  59. package/dist/esm/word/doc/doc-reader.js +6 -2
  60. package/dist/esm/word/doc/doc-text.d.ts +6 -0
  61. package/dist/esm/word/doc/doc-text.js +19 -1
  62. package/dist/esm/word/document-parser.d.ts +2 -2
  63. package/dist/esm/word/document-parser.js +11 -1
  64. package/dist/esm/word/docx-reader.js +5 -3
  65. package/dist/esm/word/docx-writer.js +207 -21
  66. package/dist/esm/word/drawing-parser.d.ts +5 -3
  67. package/dist/esm/word/drawing-parser.js +49 -8
  68. package/dist/esm/word/font-embed.d.ts +30 -0
  69. package/dist/esm/word/font-embed.js +173 -0
  70. package/dist/esm/word/font-table.d.ts +10 -0
  71. package/dist/esm/word/font-table.js +13 -1
  72. package/dist/esm/word/index.js +1 -1
  73. package/dist/esm/word/numbering-parser.d.ts +3 -1
  74. package/dist/esm/word/numbering-parser.js +2 -1
  75. package/dist/esm/word/paragraph-properties.d.ts +7 -6
  76. package/dist/esm/word/paragraph-properties.js +14 -2
  77. package/dist/esm/word/run-properties.js +26 -0
  78. package/package.json +8 -3
@@ -33,14 +33,22 @@ function paragraphBlock(text, outlineLevel) {
33
33
  * them up to "78110"; in 7-point footnotes the same space is half as wide
34
34
  * again as the page's, and pushes their lines over.
35
35
  *
36
+ * A space between two words one text markup marks is marked with them: the
37
+ * band a highlighter lays runs across the gap. comments.pdf highlights two
38
+ * lines on its sixth page, and with its spaces left bare the band came back
39
+ * broken at every word.
40
+ *
36
41
  * @param before The span the space follows, where there is one.
42
+ * @param after What follows the space, where anything does.
37
43
  * @returns The space.
38
44
  */
39
- function spaceAfter(before) {
45
+ function spaceAfter(before, after) {
46
+ const markup = before?.markup;
40
47
  return {
41
48
  text: " ",
42
49
  ...before?.sizePt !== void 0 ? { sizePt: before.sizePt } : {},
43
- ...before?.fontName !== void 0 ? { fontName: before.fontName } : {}
50
+ ...before?.fontName !== void 0 ? { fontName: before.fontName } : {},
51
+ ...markup !== void 0 && after?.markup !== void 0 && sameMarkup(markup, after.markup) ? { markup } : {}
44
52
  };
45
53
  }
46
54
  /**
@@ -53,7 +61,7 @@ function paragraphFromRuns(spans, outlineLevel, placement) {
53
61
  const merged = [];
54
62
  for (const s of spans) {
55
63
  const last = merged[merged.length - 1];
56
- if (last && last.href === s.href && last.sizePt === s.sizePt && last.colorHex === s.colorHex && last.fontName === s.fontName && last.outline?.colorHex === s.outline?.colorHex && last.outline?.widthPt === s.outline?.widthPt && last.bold === s.bold && last.italic === s.italic && last.script === s.script && sameMarkup(last.markup, s.markup)) last.text += s.text;
64
+ if (last && last.href === s.href && last.sizePt === s.sizePt && last.colorHex === s.colorHex && last.fontName === s.fontName && last.outline?.colorHex === s.outline?.colorHex && last.outline?.widthPt === s.outline?.widthPt && last.bold === s.bold && last.italic === s.italic && last.script === s.script && last.widthScale === s.widthScale && sameMarkup(last.markup, s.markup)) last.text += s.text;
57
65
  else merged.push({
58
66
  text: s.text,
59
67
  ...s.href !== void 0 ? { href: s.href } : {},
@@ -64,7 +72,8 @@ function paragraphFromRuns(spans, outlineLevel, placement) {
64
72
  ...s.bold !== void 0 ? { bold: s.bold } : {},
65
73
  ...s.italic !== void 0 ? { italic: s.italic } : {},
66
74
  ...s.script !== void 0 ? { script: s.script } : {},
67
- ...s.markup !== void 0 ? { markup: s.markup } : {}
75
+ ...s.markup !== void 0 ? { markup: s.markup } : {},
76
+ ...s.widthScale !== void 0 ? { widthScale: s.widthScale } : {}
68
77
  });
69
78
  }
70
79
  const runs = merged.map((m) => ({
@@ -98,7 +107,8 @@ function paragraphFromRuns(spans, outlineLevel, placement) {
98
107
  ...r.markup?.highlightHex !== void 0 ? { shadingColorHex: r.markup.highlightHex } : {},
99
108
  ...r.markup?.underline !== void 0 ? { underline: r.markup.underline } : {},
100
109
  ...r.markup?.underlineHex !== void 0 ? { underlineColorHex: r.markup.underlineHex } : {},
101
- ...r.markup?.strike === true ? { strike: true } : {}
110
+ ...r.markup?.strike === true ? { strike: true } : {},
111
+ ...r.widthScale !== void 0 ? { widthScale: r.widthScale } : {}
102
112
  },
103
113
  ...r.href ? { href: r.href } : {}
104
114
  }))
@@ -181,10 +191,6 @@ function imageBlock(image, resources, alt, frame, zOrder, behind = false) {
181
191
  * @returns A shape carrying the text, anchored where the glyphs were.
182
192
  */
183
193
  function positionedText(spans, box, frame, zOrder, rotation60k) {
184
- const paragraph = paragraphFromRuns(spans, void 0, {
185
- spacingLine: pt(Math.max(1, box.height)),
186
- spacingLineRule: "exact"
187
- });
188
194
  return {
189
195
  kind: "shape",
190
196
  shape: {
@@ -200,22 +206,134 @@ function positionedText(spans, box, frame, zOrder, rotation60k) {
200
206
  offsetPt: pt(Math.max(0, frame.top - box.y - box.height))
201
207
  }
202
208
  },
203
- width: pt(Math.max(1, box.width)),
204
- height: pt(Math.max(1, box.height)),
205
- ...rotation60k !== void 0 ? { transform: { rotation60k } } : {},
209
+ ...lineBox(spans, box, rotation60k),
210
+ paragraphProperties: FLOAT_CARRIER
211
+ }
212
+ };
213
+ }
214
+ /** A line of text in a box of its own, sized to the box, which nobody sees. */
215
+ function lineBox(spans, box, rotation60k, noWrap = false) {
216
+ const paragraph = paragraphFromRuns(spans, void 0, {
217
+ spacingLine: pt(Math.max(1, box.height)),
218
+ spacingLineRule: "exact"
219
+ });
220
+ return {
221
+ width: pt(Math.max(1, box.width)),
222
+ height: pt(Math.max(1, box.height)),
223
+ ...rotation60k !== void 0 ? { transform: { rotation60k } } : {},
224
+ geometry: {
225
+ kind: "preset",
226
+ preset: "rect"
227
+ },
228
+ fill: { kind: "none" },
229
+ text: {
230
+ content: [paragraph],
231
+ insetLeft: pt(0),
232
+ insetTop: pt(0),
233
+ insetRight: pt(0),
234
+ insetBottom: pt(0),
235
+ ...noWrap ? { noWrap: true } : {}
236
+ }
237
+ };
238
+ }
239
+ /** A path of a figure, drawn where the page drew it. */
240
+ function vectorMember(v) {
241
+ const el = shapeBlock(v);
242
+ if (el.kind !== "shape") throw new Error("a path is drawn as a shape");
243
+ return {
244
+ shape: el.shape,
245
+ left: v.minX,
246
+ top: v.maxY
247
+ };
248
+ }
249
+ /**
250
+ * A picture of a figure: a box filled with it (§20.1.8.14), which is what a
251
+ * `pic:pic` inside a group is — the shadows comments.pdf lays under each of
252
+ * its boxes.
253
+ */
254
+ function imageMember(image, resources) {
255
+ const turned = image.rotationDeg !== void 0 || image.flipV === true;
256
+ return {
257
+ shape: {
258
+ width: pt(image.widthPt),
259
+ height: pt(image.heightPt),
260
+ ...turned ? { transform: {
261
+ ...image.rotationDeg !== void 0 ? { rotation60k: Math.round(-image.rotationDeg * 6e4) } : {},
262
+ ...image.flipV === true ? { flipV: true } : {}
263
+ } } : {},
206
264
  geometry: {
207
265
  kind: "preset",
208
266
  preset: "rect"
209
267
  },
210
- fill: { kind: "none" },
211
- text: {
212
- content: [paragraph],
213
- insetLeft: pt(0),
214
- insetTop: pt(0),
215
- insetRight: pt(0),
216
- insetBottom: pt(0)
268
+ fill: {
269
+ kind: "picture",
270
+ imageResource: resources.put(image.bytes),
271
+ ...image.crop ? { imageCrop: image.crop } : {},
272
+ ...image.alpha !== void 0 ? { alpha: image.alpha } : {}
217
273
  },
218
- paragraphProperties: FLOAT_CARRIER
274
+ paragraphProperties: {}
275
+ },
276
+ left: image.x,
277
+ top: image.y + image.heightPt
278
+ };
279
+ }
280
+ /**
281
+ * A label of a figure: a line of its words, in a box as wide as they are.
282
+ * The box never wraps — set in a face a little wider than the page's, one
283
+ * word would break in two.
284
+ */
285
+ function labelMember(spans, box, rotation60k) {
286
+ return {
287
+ shape: {
288
+ ...lineBox(spans, box, rotation60k, true),
289
+ paragraphProperties: {}
290
+ },
291
+ left: box.x,
292
+ top: box.y + box.height
293
+ };
294
+ }
295
+ /** The box a figure's parts stand in, on the page (y-up). */
296
+ function membersBox(members) {
297
+ return {
298
+ left: Math.min(...members.map((m) => m.left)),
299
+ top: Math.max(...members.map((m) => m.top)),
300
+ right: Math.max(...members.map((m) => m.left + m.shape.width)),
301
+ bottom: Math.min(...members.map((m) => m.top - m.shape.height))
302
+ };
303
+ }
304
+ /**
305
+ * §20.5.2.17 — a figure as the one drawing it is: a group holding its paths,
306
+ * its pictures and its labels at their places in it, set in a paragraph of
307
+ * its own. An inline drawing takes the room it is tall (a line holding
308
+ * nothing but a picture is the picture's height in Word), so the text after
309
+ * it follows it, and the figure goes with its paragraph wherever the text
310
+ * puts that.
311
+ *
312
+ * @param members Its parts, in the order they are painted.
313
+ * @param properties The paragraph it stands in.
314
+ */
315
+ function figureBlock(members, properties) {
316
+ const box = membersBox(members);
317
+ return {
318
+ kind: "shape",
319
+ shape: {
320
+ width: pt(box.right - box.left),
321
+ height: pt(box.top - box.bottom),
322
+ children: members.map((m) => ({
323
+ shape: m.shape,
324
+ xPt: pt(m.left - box.left),
325
+ yPt: pt(box.top - m.top)
326
+ })),
327
+ geometry: {
328
+ kind: "custom",
329
+ custom: {
330
+ pathWidth: 0,
331
+ pathHeight: 0,
332
+ commands: []
333
+ }
334
+ },
335
+ fill: { kind: "none" },
336
+ paragraphProperties: properties
219
337
  }
220
338
  };
221
339
  }
@@ -332,10 +450,10 @@ function shapeBlock(v, frame, zOrder, behind = false) {
332
450
  * this single geometry — a known approximation, still far better than a fixed
333
451
  * `A4`. Returns `undefined` when there is no usable first-page box.
334
452
  */
335
- function sectionFromPdfPages(pages) {
453
+ function sectionFromPdfPages(pages, display) {
336
454
  const first = pages[0];
337
455
  if (!first) return void 0;
338
- const shown = displayOf(first);
456
+ const shown = display ?? displayOf(first);
339
457
  const width = shown.width;
340
458
  const height = shown.height;
341
459
  if (!(width > 0 && height > 0)) return void 0;
@@ -356,6 +474,102 @@ function sectionFromPdfPages(pages) {
356
474
  };
357
475
  }
358
476
  /**
477
+ * §17.6.20 — a section read in a page's text frame (see `textFrameOf`), set
478
+ * back on the sheet the page is shown on, its lines running down it (`tbRl`).
479
+ *
480
+ * The frame is the sheet turned back a quarter, so its measurements carry
481
+ * over edge for edge: the frame's left margin is where each line starts, the
482
+ * sheet's top one; its top margin is where the first line stands, the
483
+ * sheet's right one.
484
+ *
485
+ * @param section The section as the frame measured it.
486
+ * @param display The geometry its pages were read in.
487
+ * @returns The section on the sheet, or the section unchanged for a page read
488
+ * on its sheet.
489
+ */
490
+ function sectionOnSheet(section, display) {
491
+ const sheet = display?.sheet;
492
+ if (!section || !sheet) return section;
493
+ const m = section.margins;
494
+ return {
495
+ ...section,
496
+ pageSize: {
497
+ width: pt(sheet.width),
498
+ height: pt(sheet.height),
499
+ orientation: sheet.width > sheet.height ? "landscape" : "portrait"
500
+ },
501
+ ...m ? { margins: {
502
+ ...m,
503
+ top: m.left,
504
+ right: m.top,
505
+ bottom: m.right,
506
+ left: m.bottom
507
+ } } : {},
508
+ textDirection: "tbRl"
509
+ };
510
+ }
511
+ /**
512
+ * A mark anchored to a page read in its text frame, set on the SHEET the page
513
+ * is shown on: where the page drew it, turned with the page.
514
+ *
515
+ * Word lays a `tbRl` section's text down the sheet but stands a drawing
516
+ * anchored to the page at its offsets on the sheet, upright (§17.6.20) — so a
517
+ * picture the frame placed is carried onto the sheet, and turned a quarter
518
+ * clockwise as the words around it are.
519
+ *
520
+ * @param el A body element, anchored or not.
521
+ * @param sheetWidth The sheet's width, which is the frame's height.
522
+ * @returns The element, anchored on the sheet.
523
+ */
524
+ function floatOntoSheet(el, sheetWidth) {
525
+ const onto = (float, width, height) => {
526
+ if (float.posH?.relativeFrom !== "page" || float.posV?.relativeFrom !== "page") return void 0;
527
+ const cx = (float.posH.offsetPt ?? 0) + width / 2;
528
+ const cy = (float.posV.offsetPt ?? 0) + height / 2;
529
+ return {
530
+ ...float,
531
+ posH: {
532
+ relativeFrom: "page",
533
+ offsetPt: pt(sheetWidth - cy - width / 2)
534
+ },
535
+ posV: {
536
+ relativeFrom: "page",
537
+ offsetPt: pt(cx - height / 2)
538
+ }
539
+ };
540
+ };
541
+ const quarter = (rotation60k) => ((rotation60k ?? 0) + 90 * 6e4) % (360 * 6e4);
542
+ if (el.kind === "image" && el.image.float) {
543
+ const float = onto(el.image.float, el.image.width, el.image.height);
544
+ if (!float) return el;
545
+ return {
546
+ ...el,
547
+ image: {
548
+ ...el.image,
549
+ float,
550
+ rotation60k: quarter(el.image.rotation60k)
551
+ }
552
+ };
553
+ }
554
+ if (el.kind === "shape" && el.shape.float) {
555
+ const float = onto(el.shape.float, el.shape.width, el.shape.height);
556
+ if (!float) return el;
557
+ const rotation60k = quarter(el.shape.transform?.rotation60k);
558
+ return {
559
+ ...el,
560
+ shape: {
561
+ ...el.shape,
562
+ float,
563
+ transform: {
564
+ ...el.shape.transform,
565
+ rotation60k
566
+ }
567
+ }
568
+ };
569
+ }
570
+ return el;
571
+ }
572
+ /**
359
573
  * §8.4.3.6 — a page's dash pattern as DrawingML states one (§20.1.8.21
360
574
  * `a:custDash`): dash and gap in turn, as multiples of the pen's width.
361
575
  *
@@ -579,24 +793,129 @@ function withMeasuredMargins(section, shown, pageRuns, pageMarks = [], foot = []
579
793
  };
580
794
  }
581
795
  /**
796
+ * The runs set in a face the page KERNED or LIGATED, marked so (see
797
+ * `pageSpacing`, `FaceOutlines.ligatures`): a word processor kerns nothing a
798
+ * run does not ask it to, and set unkerned, a line kerned on the page runs
799
+ * longer than it did there; a ligature the embedded face carries is formed
800
+ * where the run asks for its face's ligatures.
801
+ *
802
+ * @param blocks The blocks to mark, tables and text boxes within them too.
803
+ * @param typeset Run font name → what its runs ask for.
804
+ * @returns The blocks, their runs in those faces asking for it.
805
+ */
806
+ function typesetRuns(blocks, typeset) {
807
+ if (typeset.size === 0) return [...blocks];
808
+ const shape = (s) => ({
809
+ ...s,
810
+ ...s.text ? { text: {
811
+ ...s.text,
812
+ content: typesetRuns(s.text.content, typeset)
813
+ } } : {},
814
+ ...s.children ? { children: s.children.map((child) => ({
815
+ ...child,
816
+ shape: shape(child.shape)
817
+ })) } : {}
818
+ });
819
+ return blocks.map((el) => {
820
+ if (el.kind === "paragraph") {
821
+ const runs = el.paragraph.runs.map((run) => {
822
+ const face = run.properties.fontFamily?.ascii;
823
+ const asked = face !== void 0 ? typeset.get(face) : void 0;
824
+ return asked ? {
825
+ ...run,
826
+ properties: {
827
+ ...run.properties,
828
+ ...asked
829
+ }
830
+ } : run;
831
+ });
832
+ return {
833
+ ...el,
834
+ paragraph: {
835
+ ...el.paragraph,
836
+ runs
837
+ }
838
+ };
839
+ }
840
+ if (el.kind === "table") {
841
+ const rows = el.table.rows.map((row) => ({
842
+ ...row,
843
+ cells: row.cells.map((cell) => ({
844
+ ...cell,
845
+ content: typesetRuns(cell.content, typeset)
846
+ }))
847
+ }));
848
+ return {
849
+ ...el,
850
+ table: {
851
+ ...el.table,
852
+ rows
853
+ }
854
+ };
855
+ }
856
+ if (el.kind === "shape") return {
857
+ ...el,
858
+ shape: shape(el.shape)
859
+ };
860
+ return el;
861
+ });
862
+ }
863
+ /**
864
+ * What each face's runs ask for: kerning where the page kerned the face, and
865
+ * its standard ligatures where the page drew one of them (the ligatures the
866
+ * face's embedded font then forms are exactly the ones the page drew).
867
+ *
868
+ * @param kerned The run font names of the faces the page kerned.
869
+ * @param faceOutlines Run font name → the face's outlines, ligatures among them.
870
+ */
871
+ function typesetting(kerned, faceOutlines) {
872
+ const out = /* @__PURE__ */ new Map();
873
+ for (const face of kerned) out.set(face, { kerningMinPt: KERN_FROM_PT });
874
+ for (const [face, outlines] of faceOutlines ?? []) if (outlines.ligatures && outlines.ligatures.size > 0) out.set(face, {
875
+ ...out.get(face),
876
+ ligatures: "standard"
877
+ });
878
+ return out;
879
+ }
880
+ /** §17.3.2.19 — kerned from one point up: at every size a page is set in. */
881
+ var KERN_FROM_PT = pt(1);
882
+ /**
582
883
  * Assemble the final {@link FlowDoc} for a reconstruction: the body elements
583
884
  * with their styles resolved against the empty style sheet, the lifted-image
584
885
  * resource store, and the optional page {@link SectionProperties}. Shared by
585
886
  * both reconstruction paths (the tagged fast-path EP3 and the heuristic layout
586
887
  * path EP4).
587
888
  */
588
- function buildFlowDoc(body, resources = new ResourceStore(), section, embeddedFonts, sections = [], headersFooters, faceFamilies) {
889
+ function buildFlowDoc(body, resources = new ResourceStore(), section, embeddedFonts, sections = [], headersFooters, faceFamilies, faceOutlines, kerned = /* @__PURE__ */ new Set()) {
890
+ const typeset = typesetting(kerned, faceOutlines);
891
+ const bands = headersFooters && typeset.size > 0 ? new Map([...headersFooters].map(([id, blocks]) => [id, typesetRuns(blocks, typeset)])) : headersFooters;
589
892
  return {
590
893
  kind: "flow",
591
- body: resolveBodyStyles([...body], EMPTY_STYLE_SHEET),
894
+ body: resolveBodyStyles(typesetRuns(body, typeset), RECONSTRUCTION_SHEET),
592
895
  sections,
593
- ...headersFooters && headersFooters.size > 0 ? { headersFooters } : {},
896
+ ...bands && bands.size > 0 ? { headersFooters: bands } : {},
594
897
  ...section ? { section } : {},
595
898
  ...embeddedFonts && embeddedFonts.size > 0 ? { embeddedFonts } : {},
596
899
  ...faceFamilies && faceFamilies.size > 0 ? { faceFamilies } : {},
900
+ ...faceOutlines && faceOutlines.size > 0 ? { faceOutlines } : {},
901
+ compatibilityMode: CURRENT_WORD_LAYOUT,
597
902
  styles: EMPTY_STYLE_SHEET,
598
903
  resources
599
904
  };
600
905
  }
906
+ /** [MS-DOCX] `compatibilityMode` 15 — Word 2013's layout, a current Word's. */
907
+ var CURRENT_WORD_LAYOUT = 15;
908
+ /**
909
+ * §17.3.1.44 — the sheet a reconstruction's paragraphs resolve against: the
910
+ * empty one, with widow control off. Every source page opens a page of its
911
+ * own, and the page already broke its paragraphs where it did; where a
912
+ * reconstructed column runs a line long, widow control — on wherever nothing
913
+ * says otherwise — carries a second line over with it, and the column after
914
+ * runs longer still.
915
+ */
916
+ var RECONSTRUCTION_SHEET = {
917
+ ...EMPTY_STYLE_SHEET,
918
+ defaultParagraphProperties: { widowControl: false }
919
+ };
601
920
  //#endregion
602
- export { BASELINE_AT, CARRIER_LINE_PT, GUESSED_MARGIN, NATURAL_LINE_EM, buildFlowDoc, dedupeLosses, imageBlock, paragraphBlock, paragraphFromRuns, positionedText, sectionFromPdfPages, shapeBlock, spaceAfter, textSizeOf, tooSmallToRead, withMeasuredMargins };
921
+ export { ASCENDER, BASELINE_AT, CARRIER_LINE_PT, GUESSED_MARGIN, NATURAL_LINE_EM, buildFlowDoc, dedupeLosses, figureBlock, floatOntoSheet, imageBlock, imageMember, labelMember, membersBox, paragraphBlock, paragraphFromRuns, positionedText, sectionFromPdfPages, sectionOnSheet, shapeBlock, spaceAfter, textSizeOf, tooSmallToRead, vectorMember, withMeasuredMargins };