reamkit 1.29.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 (106) 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 +65 -0
  6. package/dist/esm/core/drawingml/shape-render.js +13 -1
  7. package/dist/esm/core/font/index.d.ts +2 -0
  8. package/dist/esm/core/font/ttf-build.d.ts +113 -0
  9. package/dist/esm/core/font/ttf-build.js +1224 -0
  10. package/dist/esm/core/font/ttf-subset.d.ts +19 -0
  11. package/dist/esm/core/font/ttf-subset.js +14 -1
  12. package/dist/esm/core/fonts/index.d.ts +1 -1
  13. package/dist/esm/core/fonts/provider.d.ts +17 -0
  14. package/dist/esm/core/fonts/provider.js +27 -2
  15. package/dist/esm/core/fonts/remote-fonts.d.ts +8 -0
  16. package/dist/esm/core/fonts/remote-fonts.js +108 -18
  17. package/dist/esm/core/ir/flow.d.ts +88 -1
  18. package/dist/esm/core/numbering/index.d.ts +1 -1
  19. package/dist/esm/core/numbering/state.d.ts +11 -1
  20. package/dist/esm/core/numbering/state.js +10 -1
  21. package/dist/esm/core/style-cascade/resolver.js +35 -4
  22. package/dist/esm/core/style-cascade/types.d.ts +19 -1
  23. package/dist/esm/core/style-cascade/types.js +3 -0
  24. package/dist/esm/index.d.ts +2 -2
  25. package/dist/esm/layout/page-doc.d.ts +10 -3
  26. package/dist/esm/layout/styled-layout.d.ts +48 -7
  27. package/dist/esm/layout/styled-layout.js +881 -119
  28. package/dist/esm/layout/turned-section.d.ts +38 -0
  29. package/dist/esm/layout/turned-section.js +193 -0
  30. package/dist/esm/pdf/styled-page-emitter.js +78 -2
  31. package/dist/esm/pdf-reader/annot-draw.js +93 -1
  32. package/dist/esm/pdf-reader/annots.d.ts +18 -0
  33. package/dist/esm/pdf-reader/annots.js +86 -9
  34. package/dist/esm/pdf-reader/cff-outline.d.ts +27 -0
  35. package/dist/esm/pdf-reader/cff-outline.js +169 -21
  36. package/dist/esm/pdf-reader/cmap.js +5 -2
  37. package/dist/esm/pdf-reader/content.d.ts +70 -4
  38. package/dist/esm/pdf-reader/content.js +172 -12
  39. package/dist/esm/pdf-reader/display.d.ts +36 -0
  40. package/dist/esm/pdf-reader/display.js +82 -1
  41. package/dist/esm/pdf-reader/document.js +5 -1
  42. package/dist/esm/pdf-reader/embedded-fonts.d.ts +25 -0
  43. package/dist/esm/pdf-reader/embedded-fonts.js +78 -9
  44. package/dist/esm/pdf-reader/encodings.d.ts +8 -0
  45. package/dist/esm/pdf-reader/encodings.js +25 -3
  46. package/dist/esm/pdf-reader/face-outlines.d.ts +78 -0
  47. package/dist/esm/pdf-reader/face-outlines.js +362 -0
  48. package/dist/esm/pdf-reader/figures.d.ts +52 -0
  49. package/dist/esm/pdf-reader/figures.js +433 -0
  50. package/dist/esm/pdf-reader/flow-build.d.ts +224 -7
  51. package/dist/esm/pdf-reader/flow-build.js +545 -39
  52. package/dist/esm/pdf-reader/font.d.ts +27 -1
  53. package/dist/esm/pdf-reader/font.js +632 -63
  54. package/dist/esm/pdf-reader/glyf-outline.d.ts +33 -0
  55. package/dist/esm/pdf-reader/glyf-outline.js +148 -3
  56. package/dist/esm/pdf-reader/glyph-names.js +154 -1
  57. package/dist/esm/pdf-reader/glyph-shapes.d.ts +18 -0
  58. package/dist/esm/pdf-reader/glyph-shapes.js +57 -0
  59. package/dist/esm/pdf-reader/image-decode.js +70 -4
  60. package/dist/esm/pdf-reader/images.d.ts +5 -0
  61. package/dist/esm/pdf-reader/images.js +4 -2
  62. package/dist/esm/pdf-reader/jbig2.d.ts +40 -1
  63. package/dist/esm/pdf-reader/jbig2.js +78 -16
  64. package/dist/esm/pdf-reader/jpeg.d.ts +6 -3
  65. package/dist/esm/pdf-reader/jpeg.js +21 -1
  66. package/dist/esm/pdf-reader/layout.d.ts +119 -2
  67. package/dist/esm/pdf-reader/layout.js +2219 -184
  68. package/dist/esm/pdf-reader/lexer.d.ts +10 -0
  69. package/dist/esm/pdf-reader/lexer.js +17 -0
  70. package/dist/esm/pdf-reader/page-numbers.d.ts +53 -0
  71. package/dist/esm/pdf-reader/page-numbers.js +167 -0
  72. package/dist/esm/pdf-reader/pattern-tint.d.ts +11 -1
  73. package/dist/esm/pdf-reader/pattern-tint.js +21 -3
  74. package/dist/esm/pdf-reader/regions.d.ts +25 -0
  75. package/dist/esm/pdf-reader/regions.js +167 -0
  76. package/dist/esm/pdf-reader/shading.d.ts +58 -2
  77. package/dist/esm/pdf-reader/shading.js +181 -11
  78. package/dist/esm/pdf-reader/struct-tree.js +112 -8
  79. package/dist/esm/pdf-reader/tagged.js +207 -28
  80. package/dist/esm/pdf-reader/text-rules.js +1 -1
  81. package/dist/esm/pdf-reader/text.d.ts +6 -3
  82. package/dist/esm/pdf-reader/text.js +106 -22
  83. package/dist/esm/pdf-reader/type1-outline.d.ts +11 -0
  84. package/dist/esm/pdf-reader/type1-outline.js +63 -8
  85. package/dist/esm/pdf-reader/vector.d.ts +8 -2
  86. package/dist/esm/pdf-reader/vector.js +105 -6
  87. package/dist/esm/word/doc/doc-reader.js +6 -2
  88. package/dist/esm/word/doc/doc-text.d.ts +6 -0
  89. package/dist/esm/word/doc/doc-text.js +19 -1
  90. package/dist/esm/word/document-parser.d.ts +2 -2
  91. package/dist/esm/word/document-parser.js +11 -1
  92. package/dist/esm/word/docx-reader.js +5 -3
  93. package/dist/esm/word/docx-writer.js +341 -54
  94. package/dist/esm/word/drawing-parser.d.ts +5 -3
  95. package/dist/esm/word/drawing-parser.js +56 -10
  96. package/dist/esm/word/font-embed.d.ts +30 -0
  97. package/dist/esm/word/font-embed.js +173 -0
  98. package/dist/esm/word/font-table.d.ts +10 -0
  99. package/dist/esm/word/font-table.js +13 -1
  100. package/dist/esm/word/index.js +1 -1
  101. package/dist/esm/word/numbering-parser.d.ts +3 -1
  102. package/dist/esm/word/numbering-parser.js +2 -1
  103. package/dist/esm/word/paragraph-properties.d.ts +7 -6
  104. package/dist/esm/word/paragraph-properties.js +16 -2
  105. package/dist/esm/word/run-properties.js +26 -0
  106. package/package.json +12 -5
@@ -1,3 +1,4 @@
1
+ import { postScriptFsType } from "./cff-outline.js";
1
2
  //#region src/pdf-reader/type1-outline.ts
2
3
  /**
3
4
  * Read an embedded Type 1 program.
@@ -31,7 +32,13 @@ function type1Font(program) {
31
32
  return out;
32
33
  },
33
34
  has: (name) => parsed.charstrings.has(name),
34
- ...parsed.encoding ? { encoding: parsed.encoding } : {}
35
+ advance: (name) => {
36
+ const charstring = parsed.charstrings.get(name);
37
+ const width = charstring ? statedWidth(charstring) : void 0;
38
+ return width === void 0 ? void 0 : width * parsed.scale * 1e3;
39
+ },
40
+ ...parsed.encoding ? { encoding: parsed.encoding } : {},
41
+ ...parsed.fsType !== void 0 ? { fsType: parsed.fsType } : {}
35
42
  };
36
43
  }
37
44
  function parseType1(program) {
@@ -47,11 +54,13 @@ function parseType1(program) {
47
54
  const charstrings = readCharstrings(plain, text, lenIV);
48
55
  if (charstrings.size === 0) return void 0;
49
56
  const encoding = readEncoding(clear);
57
+ const fsType = postScriptFsType(clear);
50
58
  return {
51
59
  charstrings,
52
60
  subrs: readSubrs(plain, text, lenIV),
53
61
  ...encoding ? { encoding } : {},
54
- scale: readMatrixScale(clear) ?? .001
62
+ scale: readMatrixScale(clear) ?? .001,
63
+ ...fsType !== void 0 ? { fsType } : {}
55
64
  };
56
65
  }
57
66
  /** The `eexec` key (§7), and the one every charstring is encrypted with (§6.2). */
@@ -211,6 +220,7 @@ function runGlyph(font, program, depth) {
211
220
  let inFlex = false;
212
221
  let x = 0;
213
222
  let y = 0;
223
+ let sideBearing = 0;
214
224
  let open = false;
215
225
  let steps = 0;
216
226
  const moveTo = () => {
@@ -255,6 +265,7 @@ function runGlyph(font, program, depth) {
255
265
  case 13:
256
266
  x = stack[0] ?? 0;
257
267
  y = 0;
268
+ sideBearing = x;
258
269
  stack.length = 0;
259
270
  break;
260
271
  case 9:
@@ -404,18 +415,25 @@ function runGlyph(font, program, depth) {
404
415
  break;
405
416
  }
406
417
  if (op2 === 6) {
407
- const base = STANDARD_ENCODING[stack[3] ?? 0];
408
- const charstring = base !== void 0 ? font.charstrings.get(base) : void 0;
409
- if (charstring && depth < 2) {
418
+ const [asb = 0, adx = 0, ady = 0, bchar = 0, achar = 0] = stack;
419
+ if (open) out.push({ op: "close" });
420
+ open = false;
421
+ const part = (charCode, dx, dy) => {
422
+ const name = STANDARD_ENCODING[charCode];
423
+ const charstring = name !== void 0 ? font.charstrings.get(name) : void 0;
424
+ if (!charstring || depth >= 2) return;
410
425
  const drawn = runGlyph(font, charstring, depth + 1);
411
- if (drawn) out.push(...drawn.map((seg) => unscale(seg, font.scale)));
412
- }
426
+ if (drawn) out.push(...drawn.map((seg) => shift(unscale(seg, font.scale), dx, dy)));
427
+ };
428
+ part(bchar, 0, 0);
429
+ part(achar, adx + sideBearing - asb, ady);
413
430
  stack.length = 0;
414
431
  return false;
415
432
  }
416
433
  if (op2 === 7) {
417
434
  x = stack[0] ?? 0;
418
435
  y = stack[1] ?? 0;
436
+ sideBearing = x;
419
437
  stack.length = 0;
420
438
  break;
421
439
  }
@@ -457,10 +475,47 @@ function scaleSeg(seg, k) {
457
475
  y: seg.y * k
458
476
  };
459
477
  }
460
- /** The base of a `seac` comes back already scaled; this puts it back in glyph space. */
478
+ /**
479
+ * §6.4 — the width a charstring states before it draws: the second operand of
480
+ * `hsbw` (`sbx wx hsbw`), or the third of `sbw` (`sbx sby wx wy sbw`).
481
+ */
482
+ function statedWidth(code) {
483
+ const stack = [];
484
+ for (let i = 0; i < code.length && stack.length <= 4;) {
485
+ const b = code[i++];
486
+ if (b >= 32 && b <= 246) stack.push(b - 139);
487
+ else if (b >= 247 && b <= 250) stack.push((b - 247) * 256 + (code[i++] ?? 0) + 108);
488
+ else if (b >= 251 && b <= 254) stack.push(-(b - 251) * 256 - (code[i++] ?? 0) - 108);
489
+ else if (b === 255) {
490
+ stack.push(code[i] << 24 | code[i + 1] << 16 | code[i + 2] << 8 | code[i + 3] | 0);
491
+ i += 4;
492
+ } else if (b === 13) return stack[1];
493
+ else if (b === 12 && code[i] === 7) return stack[2];
494
+ else return void 0;
495
+ }
496
+ }
497
+ /** The parts of a `seac` come back already scaled; this puts them back in glyph space. */
461
498
  function unscale(seg, k) {
462
499
  return scaleSeg(seg, k === 0 ? 1 : 1 / k);
463
500
  }
501
+ /** A segment moved by `(dx, dy)`: where a `seac` puts its accent. */
502
+ function shift(seg, dx, dy) {
503
+ if (seg.op === "close" || dx === 0 && dy === 0) return seg;
504
+ if (seg.op === "cubic") return {
505
+ op: "cubic",
506
+ x1: seg.x1 + dx,
507
+ y1: seg.y1 + dy,
508
+ x2: seg.x2 + dx,
509
+ y2: seg.y2 + dy,
510
+ x: seg.x + dx,
511
+ y: seg.y + dy
512
+ };
513
+ return {
514
+ op: seg.op,
515
+ x: seg.x + dx,
516
+ y: seg.y + dy
517
+ };
518
+ }
464
519
  /** §C.1 — the names `seac` selects its two parts by, at the codes it states. */
465
520
  var STANDARD_ENCODING = {
466
521
  32: "space",
@@ -1,5 +1,5 @@
1
+ import { PageGradient } from './shading.js';
1
2
  import { PathSeg } from './content.js';
2
- import { ShapeGradient } from '../core/vector.js';
3
3
  import { Loss } from '../core/ir/index.js';
4
4
  import { PdfFile, PdfPage } from './document.js';
5
5
  /**
@@ -21,7 +21,7 @@ export interface PdfVector {
21
21
  /** Present iff a qualifying solid fill survived (EP10). */
22
22
  readonly fillHex?: string;
23
23
  /** Present iff a shading-pattern fill survived (EP16c). */
24
- readonly gradient?: ShapeGradient;
24
+ readonly gradient?: PageGradient;
25
25
  /** §11.6.4.4 — how opaque the fill is, when the page asked for less than all. */
26
26
  readonly alpha?: number;
27
27
  /**
@@ -33,6 +33,12 @@ export interface PdfVector {
33
33
  readonly strokeHex?: string;
34
34
  /** Stroke width in page-space points (EP11). */
35
35
  readonly lineWidth?: number;
36
+ /** §8.4.3.6 — the stroke's dash lengths in page-space points, where it is dashed. */
37
+ readonly dash?: ReadonlyArray<number>;
38
+ /** §8.4.3.3 — the stroke's cap, where it is not the butt cap. */
39
+ readonly cap?: 'round' | 'square';
40
+ /** §11.6.4.4 `/CA` — how opaque the stroke is, where less than all. */
41
+ readonly strokeAlpha?: number;
36
42
  readonly minX: number;
37
43
  readonly minY: number;
38
44
  readonly maxX: number;
@@ -2,9 +2,10 @@ import { PDF_NULL, PdfName, PdfStream } from "../pdf/objects.js";
2
2
  import { FEATURES } from "../core/ir/features.js";
3
3
  import { buildAlphaMap, buildColorSpaceMap, buildShadingMap, gradientShading, shadingTypeOf } from "./shading.js";
4
4
  import { IDENTITY, interpretContent, multiply } from "./content.js";
5
- import { collectPageAppearances } from "./annots.js";
5
+ import { appearanceContent, collectPageAppearances } from "./annots.js";
6
6
  import { hiddenProperties, hiddenXObject } from "./optional-content.js";
7
7
  import { buildFonts } from "./text.js";
8
+ import { patternTint, tintedHex } from "./pattern-tint.js";
8
9
  //#region src/pdf-reader/vector.ts
9
10
  function paintedVectors(file, page) {
10
11
  const out = [];
@@ -22,6 +23,31 @@ function paintedVectors(file, page) {
22
23
  stateCache.set(resources, made);
23
24
  return made;
24
25
  };
26
+ const tints = /* @__PURE__ */ new Map();
27
+ const tinted = (resources, vector) => {
28
+ const name = vector.patternName;
29
+ if (name === void 0) return vector;
30
+ let known = tints.get(resources);
31
+ if (!known) tints.set(resources, known = /* @__PURE__ */ new Map());
32
+ const key = `${name}\u0000${vector.patternPaint ?? ""}`;
33
+ let hex = known.get(key);
34
+ if (!known.has(key)) {
35
+ let tint;
36
+ try {
37
+ tint = patternTint(file, resources, name, vector.patternPaint);
38
+ } catch {
39
+ tint = void 0;
40
+ }
41
+ hex = tint ? tintedHex(tint.colorHex, tint.coverage) : void 0;
42
+ known.set(key, hex);
43
+ }
44
+ if (hex === void 0) return vector;
45
+ const { patternName: _named, patternPaint: _paint, ...plain } = vector;
46
+ return {
47
+ ...plain,
48
+ fillHex: hex
49
+ };
50
+ };
25
51
  const walk = (resources, content, baseCtm, depth, prefix) => {
26
52
  if (out.length >= MAX_VECTORS) return;
27
53
  const xobjects = resources ? file.get(resources, "XObject") : PDF_NULL;
@@ -34,7 +60,7 @@ function paintedVectors(file, page) {
34
60
  const found = dict instanceof Map ? file.resolve(dict.get(paint.name) ?? PDF_NULL) : PDF_NULL;
35
61
  const sh = found instanceof PdfStream ? found.dict : found instanceof Map ? found : void 0;
36
62
  if (sh && shadingTypeOf(file, sh) === 1) continue;
37
- const gradient = sh ? gradientShading(file, sh) : void 0;
63
+ const gradient = sh ? gradientShading(file, sh, paint.ctm) : void 0;
38
64
  if (paint.masked) {
39
65
  bareShadings++;
40
66
  continue;
@@ -74,7 +100,7 @@ function paintedVectors(file, page) {
74
100
  if (out.length >= MAX_VECTORS) return;
75
101
  if (event.vector) {
76
102
  out.push({
77
- ...event.vector,
103
+ ...tinted(resources, event.vector),
78
104
  orderKey: [...prefix, event.order]
79
105
  });
80
106
  continue;
@@ -102,7 +128,7 @@ function paintedVectors(file, page) {
102
128
  };
103
129
  walk(page.resources, file.pageContent(page), IDENTITY, 0, []);
104
130
  collectPageAppearances(file, page).forEach((appearance, index) => {
105
- walk(appearance.resources ?? page.resources, file.streamData(appearance.stream), appearance.ctm, 1, [Number.MAX_SAFE_INTEGER, index]);
131
+ walk(appearance.resources ?? page.resources, appearanceContent(file, appearance), appearance.ctm, 1, [Number.MAX_SAFE_INTEGER, index]);
106
132
  });
107
133
  return {
108
134
  placements: out,
@@ -196,7 +222,10 @@ function collectPageVectors(file, page, occupied = []) {
196
222
  ...filled && v.darkens === true ? { darkens: true } : {},
197
223
  ...stroked ? {
198
224
  strokeHex: v.strokeHex,
199
- ...v.lineWidth !== void 0 ? { lineWidth: v.lineWidth } : {}
225
+ ...v.lineWidth !== void 0 ? { lineWidth: v.lineWidth } : {},
226
+ ...v.dash !== void 0 ? { dash: v.dash } : {},
227
+ ...v.cap !== void 0 ? { cap: v.cap } : {},
228
+ ...v.strokeAlpha !== void 0 ? { strokeAlpha: v.strokeAlpha } : {}
200
229
  } : {},
201
230
  ...b,
202
231
  ...v.glyph === true ? { glyph: true } : {},
@@ -237,15 +266,23 @@ function overlaps(a, b) {
237
266
  * path intersection, and where the two merely overlap the answer is the smaller
238
267
  * of them — bounded, never larger than the truth.
239
268
  *
269
+ * A path is measured by where its curves reach, not where their handles stand,
270
+ * and one that reaches past its clip by no more than a sliver is inside it. An
271
+ * appearance's `/BBox` is drawn round the path it holds: comments.pdf signs its
272
+ * sixth page with a stamp of sixty-seven strokes that its box meets exactly,
273
+ * and a handle standing 0.03pt past the box made the signature a path round
274
+ * its clip — it came back a black bar the width of the text.
275
+ *
240
276
  * @param v The painted path as the interpreter saw it.
241
277
  * @returns The path to draw, or `undefined` when the clip leaves nothing.
242
278
  */
243
279
  function clipped(v) {
244
280
  const clip = v.clip;
245
281
  if (!clip) return v;
246
- const b = bbox(v.segs);
282
+ const b = reachOf(v.segs);
247
283
  if (!b) return v;
248
284
  if (clip.minX >= b.maxX || clip.maxX <= b.minX || clip.minY >= b.maxY || clip.maxY <= b.minY) return;
285
+ if (b.minX >= clip.minX - SLIVER_PT && b.maxX <= clip.maxX + SLIVER_PT && b.minY >= clip.minY - SLIVER_PT && b.maxY <= clip.maxY + SLIVER_PT) return v;
249
286
  const pathArea = Math.max(0, b.maxX - b.minX) * Math.max(0, b.maxY - b.minY);
250
287
  if (Math.max(0, clip.maxX - clip.minX) * Math.max(0, clip.maxY - clip.minY) >= pathArea) return v;
251
288
  return {
@@ -254,6 +291,68 @@ function clipped(v) {
254
291
  segs: clip.segs
255
292
  };
256
293
  }
294
+ /** How far past its clip a path may reach and still be inside it: a box's rounding. */
295
+ var SLIVER_PT = .5;
296
+ /**
297
+ * The box a path's curves reach: its points, and the turns of its curves —
298
+ * a Bézier stays inside its handles, and rarely reaches them.
299
+ *
300
+ * @param segs The path.
301
+ */
302
+ function reachOf(segs) {
303
+ let minX = Infinity;
304
+ let minY = Infinity;
305
+ let maxX = -Infinity;
306
+ let maxY = -Infinity;
307
+ const add = (x, y) => {
308
+ minX = Math.min(minX, x);
309
+ minY = Math.min(minY, y);
310
+ maxX = Math.max(maxX, x);
311
+ maxY = Math.max(maxY, y);
312
+ };
313
+ let [x, y] = [0, 0];
314
+ let [startX, startY] = [0, 0];
315
+ for (const s of segs) {
316
+ if (s.op === "close") {
317
+ [x, y] = [startX, startY];
318
+ continue;
319
+ }
320
+ if (s.op === "cubic") {
321
+ const [x0, y0] = [x, y];
322
+ for (const t of [...turnsOf(x0, s.x1, s.x2, s.x), ...turnsOf(y0, s.y1, s.y2, s.y)]) add(bezierAt(x0, s.x1, s.x2, s.x, t), bezierAt(y0, s.y1, s.y2, s.y, t));
323
+ }
324
+ add(s.x, s.y);
325
+ [x, y] = [s.x, s.y];
326
+ if (s.op === "move") [startX, startY] = [s.x, s.y];
327
+ }
328
+ return Number.isFinite(minX) ? {
329
+ minX,
330
+ minY,
331
+ maxX,
332
+ maxY
333
+ } : void 0;
334
+ }
335
+ /** Where, between its ends, one coordinate of a cubic Bézier turns back. */
336
+ function turnsOf(p0, p1, p2, p3) {
337
+ const a = p3 - 3 * p2 + 3 * p1 - p0;
338
+ const b = 2 * (p2 - 2 * p1 + p0);
339
+ const c = p1 - p0;
340
+ let roots = [];
341
+ if (Math.abs(a) < FLAT) {
342
+ if (Math.abs(b) >= FLAT) roots = [-c / b];
343
+ } else {
344
+ const d = b * b - 4 * a * c;
345
+ if (d >= 0) roots = [(-b + Math.sqrt(d)) / (2 * a), (-b - Math.sqrt(d)) / (2 * a)];
346
+ }
347
+ return roots.filter((t) => t > 0 && t < 1);
348
+ }
349
+ /** A coefficient this small is none: the curve's derivative is of lower degree. */
350
+ var FLAT = 1e-12;
351
+ /** One coordinate of a cubic Bézier at `t`. */
352
+ function bezierAt(p0, p1, p2, p3, t) {
353
+ const u = 1 - t;
354
+ return u * u * u * p0 + 3 * u * u * t * p1 + 3 * u * t * t * p2 + t * t * t * p3;
355
+ }
257
356
  function bbox(segs) {
258
357
  let minX = Infinity;
259
358
  let minY = Infinity;
@@ -178,7 +178,8 @@ function readDoc(bytes) {
178
178
  },
179
179
  styles: EMPTY_STYLE_SHEET,
180
180
  resources,
181
- ...headersFooters.size > 0 ? { headersFooters } : {}
181
+ ...headersFooters.size > 0 ? { headersFooters } : {},
182
+ typesetBy: "word"
182
183
  },
183
184
  losses: [content.encrypted ? DOC_ENCRYPTED_LOSS : DOC_TEXT_LOSS]
184
185
  };
@@ -406,7 +407,10 @@ function toParaProperties(p) {
406
407
  ...p.indentRightTwips !== void 0 ? { indentRight: pt(p.indentRightTwips / 20) } : {},
407
408
  ...p.indentFirstTwips !== void 0 ? { indentFirstLine: pt(p.indentFirstTwips / 20) } : {},
408
409
  ...p.spaceBeforeTwips !== void 0 ? { spacingBefore: pt(p.spaceBeforeTwips / 20) } : {},
409
- ...p.spaceAfterTwips !== void 0 ? { spacingAfter: pt(p.spaceAfterTwips / 20) } : {}
410
+ ...p.spaceAfterTwips !== void 0 ? { spacingAfter: pt(p.spaceAfterTwips / 20) } : {},
411
+ ...p.widowControl !== void 0 ? { widowControl: p.widowControl } : {},
412
+ ...p.keepNext !== void 0 ? { keepNext: p.keepNext } : {},
413
+ ...p.keepLines !== void 0 ? { keepLines: p.keepLines } : {}
410
414
  };
411
415
  }
412
416
  function underlineStyle(kul) {
@@ -74,6 +74,12 @@ export interface DocParaProps {
74
74
  readonly listIlfo?: number;
75
75
  /** `sprmPIlvl` — list level (0–8). */
76
76
  readonly listIlvl?: number;
77
+ /** `sprmPFWidowControl` — no first or last line left alone on a page. */
78
+ readonly widowControl?: boolean;
79
+ /** `sprmPFKeepFollow` — on the same page as the start of the next paragraph. */
80
+ readonly keepNext?: boolean;
81
+ /** `sprmPFKeep` — every line on one page. */
82
+ readonly keepLines?: boolean;
77
83
  }
78
84
  /** One maximal same-formatting run of text, or an embedded picture. */
79
85
  export interface DocRun {
@@ -40,6 +40,9 @@ var SPRM_P_DYA_BEFORE = 42003;
40
40
  var SPRM_P_DYA_AFTER = 42004;
41
41
  var SPRM_P_F_IN_TABLE = 9238;
42
42
  var SPRM_P_F_TTP = 9239;
43
+ var SPRM_P_F_WIDOW_CONTROL = 9265;
44
+ var SPRM_P_F_KEEP = 9221;
45
+ var SPRM_P_F_KEEP_FOLLOW = 9222;
43
46
  var SPRM_T_DEF_TABLE = 54792;
44
47
  var SPRM_T_DEF_TABLE_SHD = 54802;
45
48
  var MAX_ROW_CELLS = 256;
@@ -433,6 +436,9 @@ function decodePapxGrpprl(d) {
433
436
  let cellShadings;
434
437
  let listIlfo;
435
438
  let listIlvl;
439
+ let widowControl;
440
+ let keepNext;
441
+ let keepLines;
436
442
  for (const s of sprms(d)) switch (s.sprm) {
437
443
  case SPRM_P_JC:
438
444
  jc = d[s.op];
@@ -458,6 +464,15 @@ function decodePapxGrpprl(d) {
458
464
  case SPRM_P_F_TTP:
459
465
  rowEnd = d[s.op] !== 0;
460
466
  break;
467
+ case SPRM_P_F_WIDOW_CONTROL:
468
+ widowControl = d[s.op] !== 0;
469
+ break;
470
+ case SPRM_P_F_KEEP_FOLLOW:
471
+ keepNext = d[s.op] !== 0;
472
+ break;
473
+ case SPRM_P_F_KEEP:
474
+ keepLines = d[s.op] !== 0;
475
+ break;
461
476
  case SPRM_T_DEF_TABLE: {
462
477
  const cols = d[s.op] ?? 0;
463
478
  const edges = [];
@@ -498,7 +513,10 @@ function decodePapxGrpprl(d) {
498
513
  ...cellDescriptors ? { cellDescriptors } : {},
499
514
  ...cellShadings ? { cellShadings } : {},
500
515
  ...listIlfo !== void 0 ? { listIlfo } : {},
501
- ...listIlvl !== void 0 ? { listIlvl } : {}
516
+ ...listIlvl !== void 0 ? { listIlvl } : {},
517
+ ...widowControl !== void 0 ? { widowControl } : {},
518
+ ...keepNext !== void 0 ? { keepNext } : {},
519
+ ...keepLines !== void 0 ? { keepLines } : {}
502
520
  };
503
521
  }
504
522
  function* sprms(d) {
@@ -80,8 +80,8 @@ export interface ParseContext {
80
80
  readonly openCommentRanges?: Set<string>;
81
81
  /**
82
82
  * §17.3.1.1/§17.3.1.3 — what `w:beforeAutospacing`/`w:afterAutospacing`
83
- * resolve to for THIS document: 14pt when it states no compatibility mode,
84
- * and nothing (the default) for every Word 2007-or-later one.
83
+ * resolve to: HTML's 14pt for a Word document, nothing (the default) where
84
+ * the caller gives none.
85
85
  */
86
86
  readonly autoSpacingPt?: number;
87
87
  }
@@ -9,6 +9,7 @@ import { parseRunProperties } from "./run-properties.js";
9
9
  import { parseParagraphProperties } from "./paragraph-properties.js";
10
10
  import { poElementToFlat } from "./po-to-flat.js";
11
11
  import { parseBorders, parseTable } from "./table-parser.js";
12
+ import { FORMATS } from "./numbering-parser.js";
12
13
  import { XMLParser } from "fast-xml-parser";
13
14
  //#region src/word/document-parser.ts
14
15
  var decoder = new TextDecoder("utf-8");
@@ -199,11 +200,13 @@ function parseSectPrNode(sectPr) {
199
200
  let margins;
200
201
  let titlePg = false;
201
202
  let pageNumberStart;
203
+ let pageNumberFormat;
202
204
  let lineNumbering;
203
205
  let columns;
204
206
  let sectionStart;
205
207
  let pageBorders;
206
208
  let gridLinePitchPt;
209
+ let textDirection;
207
210
  const headers = [];
208
211
  const footers = [];
209
212
  for (const child of poChildren(sectPr)) if (poIs(child, "w:pgSz")) {
@@ -251,6 +254,8 @@ function parseSectPrNode(sectPr) {
251
254
  } else if (poIs(child, "w:pgNumType")) {
252
255
  const start = poIntAttr(child, "start");
253
256
  if (start !== void 0) pageNumberStart = start;
257
+ const fmt = poAttr(child, "fmt");
258
+ if (fmt !== void 0 && FORMATS.has(fmt) && fmt !== "decimal") pageNumberFormat = fmt;
254
259
  } else if (poIs(child, "w:cols")) columns = parseColumns(child);
255
260
  else if (poIs(child, "w:pgBorders")) {
256
261
  const borders = parseBorders(child);
@@ -265,6 +270,9 @@ function parseSectPrNode(sectPr) {
265
270
  } else if (poIs(child, "w:type")) {
266
271
  const val = poAttr(child, "val");
267
272
  sectionStart = val === "continuous" || val === "oddPage" || val === "evenPage" ? val : "nextPage";
273
+ } else if (poIs(child, "w:textDirection")) {
274
+ const val = poAttr(child, "val");
275
+ if (val === "tbRl" || val === "btLr") textDirection = val;
268
276
  }
269
277
  return {
270
278
  pageSize,
@@ -273,11 +281,13 @@ function parseSectPrNode(sectPr) {
273
281
  footers,
274
282
  ...titlePg ? { titlePg: true } : {},
275
283
  ...pageNumberStart !== void 0 ? { pageNumberStart } : {},
284
+ ...pageNumberFormat !== void 0 ? { pageNumberFormat } : {},
276
285
  ...lineNumbering ? { lineNumbering } : {},
277
286
  ...columns ? { columns } : {},
278
287
  ...sectionStart ? { sectionStart } : {},
279
288
  ...pageBorders ? { pageBorders } : {},
280
- ...gridLinePitchPt !== void 0 ? { gridLinePitchPt } : {}
289
+ ...gridLinePitchPt !== void 0 ? { gridLinePitchPt } : {},
290
+ ...textDirection ? { textDirection } : {}
281
291
  };
282
292
  }
283
293
  function parseColumns(cols) {
@@ -15,10 +15,10 @@ import { OpcPackage } from "../core/opc/package.js";
15
15
  import { parseCoreProperties } from "../core/opc/core-properties.js";
16
16
  import "../core/opc/index.js";
17
17
  import { parseXml } from "../pptx/pptx-reader.js";
18
+ import { EMPTY_NUMBERING, parseNumbering } from "./numbering-parser.js";
18
19
  import { EMPTY_SECTION, applyAuthorIds, bodyIndexForBlock, newBlockCounter, parseBackgroundColor, parseBackgroundFill, parseCommentThreads, parseDocument, parseHeaderFooter, parseNotes, parsePeople, parseSections } from "./document-parser.js";
19
20
  import { parseStyles } from "./styles-parser.js";
20
21
  import { loadEmbeddedFonts } from "./font-table.js";
21
- import { EMPTY_NUMBERING, parseNumbering } from "./numbering-parser.js";
22
22
  import { EMPTY_SETTINGS, parseSettings } from "./settings-parser.js";
23
23
  import "./index.js";
24
24
  //#region src/word/docx-reader.ts
@@ -66,7 +66,7 @@ function readDocx(docx) {
66
66
  const settings = settingsData ? parseSettings(settingsData) : EMPTY_SETTINGS;
67
67
  const ctx = {
68
68
  resolveColor,
69
- ...settings.compatibilityMode === void 0 ? { autoSpacingPt: 14 } : {},
69
+ autoSpacingPt: 14,
70
70
  ...themeLineWidths && themeLineWidths.length > 0 ? { themeLineWidths } : {},
71
71
  ...themeStyles ? { themeStyles } : {},
72
72
  ...themeFonts ? { themeFonts } : {},
@@ -142,7 +142,9 @@ function readDocx(docx) {
142
142
  ...settings.doNotExpandShiftReturn ? { doNotExpandShiftReturn: true } : {},
143
143
  ...settings.displayBackgroundShape && backgroundFill ? { pageBackgroundFill: backgroundFill } : {},
144
144
  ...settings.displayBackgroundShape && backgroundColorHex ? { pageBackgroundColorHex: backgroundColorHex } : {},
145
- ...settings.gutterAtTop ? { gutterAtTop: true } : {}
145
+ ...settings.gutterAtTop ? { gutterAtTop: true } : {},
146
+ ...settings.compatibilityMode !== void 0 ? { compatibilityMode: settings.compatibilityMode } : {},
147
+ typesetBy: "word"
146
148
  },
147
149
  losses
148
150
  };