reamkit 1.15.4 → 1.17.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 (127) hide show
  1. package/dist/esm/core/bidi/segments.js +1 -2
  2. package/dist/esm/core/converter/facade.d.ts +5 -0
  3. package/dist/esm/core/converter/project.js +5 -1
  4. package/dist/esm/core/converter/ream.d.ts +6 -0
  5. package/dist/esm/core/converter/ream.js +34 -8
  6. package/dist/esm/core/document-model/index.d.ts +1 -1
  7. package/dist/esm/core/document-model/types.d.ts +659 -7
  8. package/dist/esm/core/drawingml/chart-geometry.d.ts +34 -10
  9. package/dist/esm/core/drawingml/chart-geometry.js +424 -92
  10. package/dist/esm/core/drawingml/chart-parser.js +301 -11
  11. package/dist/esm/core/drawingml/colors.d.ts +7 -7
  12. package/dist/esm/core/drawingml/colors.js +84 -18
  13. package/dist/esm/core/drawingml/preset-geometry.js +240 -6
  14. package/dist/esm/core/drawingml/shape-render.d.ts +12 -0
  15. package/dist/esm/core/drawingml/shape-render.js +116 -3
  16. package/dist/esm/core/drawingml/sparkline-geometry.js +6 -4
  17. package/dist/esm/core/drawingml/theme-parser.d.ts +51 -0
  18. package/dist/esm/core/drawingml/theme-parser.js +92 -1
  19. package/dist/esm/core/font/measure.d.ts +5 -0
  20. package/dist/esm/core/font/measure.js +21 -1
  21. package/dist/esm/core/font/ttf-subset.js +26 -1
  22. package/dist/esm/core/images.d.ts +2 -2
  23. package/dist/esm/core/images.js +353 -43
  24. package/dist/esm/core/indexed-colors.d.ts +11 -0
  25. package/dist/esm/core/indexed-colors.js +81 -0
  26. package/dist/esm/core/ir/flow.d.ts +23 -1
  27. package/dist/esm/core/ir/sheet.d.ts +61 -0
  28. package/dist/esm/{excel → core}/number-format.d.ts +37 -0
  29. package/dist/esm/core/number-format.js +800 -0
  30. package/dist/esm/core/numbering/apply.d.ts +5 -3
  31. package/dist/esm/core/numbering/apply.js +42 -11
  32. package/dist/esm/core/numbering/index.js +2 -0
  33. package/dist/esm/core/numbering/state.d.ts +13 -1
  34. package/dist/esm/core/numbering/state.js +256 -15
  35. package/dist/esm/core/opc/alternate-content.d.ts +13 -0
  36. package/dist/esm/core/opc/alternate-content.js +72 -0
  37. package/dist/esm/core/opc/core-properties.js +1 -0
  38. package/dist/esm/core/opc/package.d.ts +14 -1
  39. package/dist/esm/core/opc/package.js +43 -4
  40. package/dist/esm/core/opc/relationships.js +1 -0
  41. package/dist/esm/core/opc/xml-entities.d.ts +12 -0
  42. package/dist/esm/core/opc/xml-entities.js +91 -0
  43. package/dist/esm/core/po-helpers.js +16 -1
  44. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  45. package/dist/esm/core/spreadsheet-model/types.d.ts +137 -0
  46. package/dist/esm/core/style-cascade/resolver.js +66 -11
  47. package/dist/esm/core/style-cascade/table.js +35 -11
  48. package/dist/esm/core/style-cascade/types.d.ts +42 -1
  49. package/dist/esm/core/style-cascade/types.js +4 -0
  50. package/dist/esm/core/vector.d.ts +30 -0
  51. package/dist/esm/excel/activex-parser.js +1 -0
  52. package/dist/esm/excel/column-bands.d.ts +26 -1
  53. package/dist/esm/excel/column-bands.js +156 -20
  54. package/dist/esm/excel/comments-parser.js +5 -3
  55. package/dist/esm/excel/conditional-format.d.ts +25 -2
  56. package/dist/esm/excel/conditional-format.js +63 -19
  57. package/dist/esm/excel/escaped-text.d.ts +9 -0
  58. package/dist/esm/excel/escaped-text.js +20 -0
  59. package/dist/esm/excel/form-control-parser.js +1 -0
  60. package/dist/esm/excel/formula/dates.js +1 -1
  61. package/dist/esm/excel/header-footer.d.ts +9 -1
  62. package/dist/esm/excel/header-footer.js +162 -14
  63. package/dist/esm/excel/index.d.ts +1 -1
  64. package/dist/esm/excel/index.js +2 -2
  65. package/dist/esm/excel/pivot-table-parser.js +1 -0
  66. package/dist/esm/excel/print-model.d.ts +75 -0
  67. package/dist/esm/excel/print-model.js +899 -77
  68. package/dist/esm/excel/printer-settings.d.ts +16 -0
  69. package/dist/esm/excel/printer-settings.js +36 -0
  70. package/dist/esm/excel/rich-value.d.ts +13 -0
  71. package/dist/esm/excel/rich-value.js +120 -0
  72. package/dist/esm/excel/shared-strings-parser.js +11 -7
  73. package/dist/esm/excel/sheet-drawing.d.ts +11 -0
  74. package/dist/esm/excel/sheet-drawing.js +26 -3
  75. package/dist/esm/excel/sheet-shape-parser.d.ts +53 -1
  76. package/dist/esm/excel/sheet-shape-parser.js +461 -32
  77. package/dist/esm/excel/sheet-to-flow.d.ts +23 -2
  78. package/dist/esm/excel/sheet-to-flow.js +553 -36
  79. package/dist/esm/excel/slicer-parser.js +1 -0
  80. package/dist/esm/excel/styles-parser.d.ts +30 -2
  81. package/dist/esm/excel/styles-parser.js +177 -40
  82. package/dist/esm/excel/table-parser.js +1 -0
  83. package/dist/esm/excel/vml-drawing.d.ts +68 -0
  84. package/dist/esm/excel/vml-drawing.js +158 -0
  85. package/dist/esm/excel/workbook-parser.d.ts +5 -0
  86. package/dist/esm/excel/workbook-parser.js +5 -1
  87. package/dist/esm/excel/worksheet-parser.d.ts +2 -1
  88. package/dist/esm/excel/worksheet-parser.js +150 -61
  89. package/dist/esm/excel/xlsx-reader.js +242 -11
  90. package/dist/esm/excel/xlsx-to-pdf.js +12 -1
  91. package/dist/esm/html/html-writer.js +29 -6
  92. package/dist/esm/layout/page-doc.d.ts +87 -1
  93. package/dist/esm/layout/styled-layout.d.ts +46 -2
  94. package/dist/esm/layout/styled-layout.js +2345 -274
  95. package/dist/esm/pdf/cid-font.js +20 -9
  96. package/dist/esm/pdf/objects.d.ts +13 -0
  97. package/dist/esm/pdf/objects.js +18 -1
  98. package/dist/esm/pdf/shading.js +76 -6
  99. package/dist/esm/pdf/styled-page-emitter.js +355 -49
  100. package/dist/esm/pdf/text-page-renderer.js +1 -0
  101. package/dist/esm/pdf/vector-graphics.d.ts +31 -2
  102. package/dist/esm/pdf/vector-graphics.js +103 -3
  103. package/dist/esm/pptx/pptx-reader.js +2 -0
  104. package/dist/esm/pptx/slide-parser.d.ts +21 -4
  105. package/dist/esm/pptx/slide-parser.js +98 -24
  106. package/dist/esm/svg/svg-writer.js +23 -4
  107. package/dist/esm/word/document-parser.d.ts +96 -7
  108. package/dist/esm/word/document-parser.js +538 -116
  109. package/dist/esm/word/docx-reader.js +135 -43
  110. package/dist/esm/word/docx-writer.js +6 -1
  111. package/dist/esm/word/drawing-parser.d.ts +86 -5
  112. package/dist/esm/word/drawing-parser.js +1590 -54
  113. package/dist/esm/word/index.d.ts +4 -4
  114. package/dist/esm/word/numbering-parser.d.ts +2 -1
  115. package/dist/esm/word/numbering-parser.js +108 -21
  116. package/dist/esm/word/paragraph-properties.d.ts +11 -1
  117. package/dist/esm/word/paragraph-properties.js +219 -10
  118. package/dist/esm/word/run-properties.js +45 -1
  119. package/dist/esm/word/settings-parser.d.ts +24 -0
  120. package/dist/esm/word/settings-parser.js +32 -5
  121. package/dist/esm/word/shading.d.ts +11 -0
  122. package/dist/esm/word/shading.js +64 -0
  123. package/dist/esm/word/styles-parser.js +9 -2
  124. package/dist/esm/word/table-parser.d.ts +9 -1
  125. package/dist/esm/word/table-parser.js +182 -30
  126. package/package.json +7 -3
  127. package/dist/esm/excel/number-format.js +0 -476
@@ -86,8 +86,33 @@ function presetPaths(preset, w, h, adjust) {
86
86
  [0, h / 2]
87
87
  ])];
88
88
  }
89
+ case "round1Rect":
90
+ case "round2SameRect":
91
+ case "round2DiagRect":
92
+ case "snip1Rect":
93
+ case "snip2SameRect":
94
+ case "snip2DiagRect":
95
+ case "snipRoundRect": return [cornerRect(preset, w, h, adjust)];
96
+ case "star4":
97
+ case "star5":
98
+ case "star6":
99
+ case "star7":
100
+ case "star8":
101
+ case "star10":
102
+ case "star12":
103
+ case "star16":
104
+ case "star24":
105
+ case "star32": {
106
+ const s = STARS.get(preset);
107
+ return s ? [star(w, h, s, adjust)] : null;
108
+ }
89
109
  case "line":
90
110
  case "straightConnector1": return [new PathBuilder().moveTo(0, h).lineTo(w, 0).build()];
111
+ case "bentConnector2": return [new PathBuilder().moveTo(0, h).lineTo(w, h).lineTo(w, 0).build()];
112
+ case "bentConnector3": {
113
+ const bx = clamp(frac(adjust, "adj1", 5e4), 0, 1) * w;
114
+ return [new PathBuilder().moveTo(0, h).lineTo(bx, h).lineTo(bx, 0).lineTo(w, 0).build()];
115
+ }
91
116
  case "rightArrow": return [blockArrow(w, h, adjust, "right")];
92
117
  case "leftArrow": return [blockArrow(w, h, adjust, "left")];
93
118
  case "upArrow": return [blockArrow(w, h, adjust, "up")];
@@ -157,6 +182,214 @@ function customPaths(geom, wPt, hPt) {
157
182
  }
158
183
  return [b.build()];
159
184
  }
185
+ var SQUARE = {
186
+ cut: 0,
187
+ round: false
188
+ };
189
+ var CORNER_RECTS = new Map([
190
+ ["round1Rect", {
191
+ defaults: [16667, 0],
192
+ corners: [
193
+ void 0,
194
+ 0,
195
+ void 0,
196
+ void 0
197
+ ],
198
+ round: true
199
+ }],
200
+ ["snip1Rect", {
201
+ defaults: [16667, 0],
202
+ corners: [
203
+ void 0,
204
+ 0,
205
+ void 0,
206
+ void 0
207
+ ],
208
+ round: false
209
+ }],
210
+ ["round2SameRect", {
211
+ defaults: [16667, 0],
212
+ corners: [
213
+ 0,
214
+ 0,
215
+ 1,
216
+ 1
217
+ ],
218
+ round: true
219
+ }],
220
+ ["snip2SameRect", {
221
+ defaults: [16667, 0],
222
+ corners: [
223
+ 0,
224
+ 0,
225
+ 1,
226
+ 1
227
+ ],
228
+ round: false
229
+ }],
230
+ ["round2DiagRect", {
231
+ defaults: [16667, 0],
232
+ corners: [
233
+ 0,
234
+ 1,
235
+ 0,
236
+ 1
237
+ ],
238
+ round: true
239
+ }],
240
+ ["snip2DiagRect", {
241
+ defaults: [16667, 0],
242
+ corners: [
243
+ 0,
244
+ 1,
245
+ 0,
246
+ 1
247
+ ],
248
+ round: false
249
+ }],
250
+ ["snipRoundRect", {
251
+ defaults: [16667, 16667],
252
+ corners: [
253
+ 0,
254
+ 1,
255
+ void 0,
256
+ void 0
257
+ ],
258
+ round: [false, true]
259
+ }]
260
+ ]);
261
+ function cornerRect(preset, w, h, adjust) {
262
+ const spec = CORNER_RECTS.get(preset);
263
+ const ss = Math.min(w, h);
264
+ const guide = (i) => {
265
+ return clamp((adjust.get(i === 0 ? "adj1" : "adj2") ?? (i === 0 ? adjust.get("adj") : void 0) ?? spec.defaults[i]) / 1e5, 0, .5) * ss;
266
+ };
267
+ return roundedCorners(w, h, spec.corners.map((g) => {
268
+ if (g === void 0) return SQUARE;
269
+ const round = typeof spec.round === "boolean" ? spec.round : spec.round[g];
270
+ return {
271
+ cut: guide(g),
272
+ round
273
+ };
274
+ }));
275
+ }
276
+ function roundedCorners(w, h, [tl, tr, br, bl]) {
277
+ const at = [
278
+ {
279
+ c: tl,
280
+ from: [0, h - tl.cut],
281
+ to: [tl.cut, h],
282
+ centre: [tl.cut, h - tl.cut],
283
+ start: Math.PI
284
+ },
285
+ {
286
+ c: tr,
287
+ from: [w - tr.cut, h],
288
+ to: [w, h - tr.cut],
289
+ centre: [w - tr.cut, h - tr.cut],
290
+ start: Math.PI / 2
291
+ },
292
+ {
293
+ c: br,
294
+ from: [w, br.cut],
295
+ to: [w - br.cut, 0],
296
+ centre: [w - br.cut, br.cut],
297
+ start: 0
298
+ },
299
+ {
300
+ c: bl,
301
+ from: [bl.cut, 0],
302
+ to: [0, bl.cut],
303
+ centre: [bl.cut, bl.cut],
304
+ start: -Math.PI / 2
305
+ }
306
+ ];
307
+ const b = new PathBuilder();
308
+ at.forEach((corner, i) => {
309
+ if (i === 0) b.moveTo(corner.from[0], corner.from[1]);
310
+ else b.lineTo(corner.from[0], corner.from[1]);
311
+ if (corner.c.cut <= 0) return;
312
+ if (corner.c.round) b.append(arcToBeziers(corner.centre[0], corner.centre[1], corner.c.cut, corner.c.cut, corner.start, -Math.PI / 2));
313
+ else b.lineTo(corner.to[0], corner.to[1]);
314
+ });
315
+ return b.close().build();
316
+ }
317
+ var STARS = new Map([
318
+ ["star4", {
319
+ points: 4,
320
+ adj: 12500,
321
+ hf: 1e5,
322
+ vf: 1e5
323
+ }],
324
+ ["star5", {
325
+ points: 5,
326
+ adj: 19098,
327
+ hf: 105146,
328
+ vf: 110557
329
+ }],
330
+ ["star6", {
331
+ points: 6,
332
+ adj: 28868,
333
+ hf: 115470,
334
+ vf: 1e5
335
+ }],
336
+ ["star7", {
337
+ points: 7,
338
+ adj: 34601,
339
+ hf: 102572,
340
+ vf: 105210
341
+ }],
342
+ ["star8", {
343
+ points: 8,
344
+ adj: 37500,
345
+ hf: 1e5,
346
+ vf: 1e5
347
+ }],
348
+ ["star10", {
349
+ points: 10,
350
+ adj: 42533,
351
+ hf: 105146,
352
+ vf: 1e5
353
+ }],
354
+ ["star12", {
355
+ points: 12,
356
+ adj: 37500,
357
+ hf: 1e5,
358
+ vf: 1e5
359
+ }],
360
+ ["star16", {
361
+ points: 16,
362
+ adj: 37500,
363
+ hf: 1e5,
364
+ vf: 1e5
365
+ }],
366
+ ["star24", {
367
+ points: 24,
368
+ adj: 37500,
369
+ hf: 1e5,
370
+ vf: 1e5
371
+ }],
372
+ ["star32", {
373
+ points: 32,
374
+ adj: 37500,
375
+ hf: 1e5,
376
+ vf: 1e5
377
+ }]
378
+ ]);
379
+ function star(w, h, preset, adjust) {
380
+ const inner = clamp(frac(adjust, "adj", preset.adj) * 2, 0, 1);
381
+ const hf = preset.hf / 1e5;
382
+ const vf = preset.vf / 1e5;
383
+ const pts = [];
384
+ for (let i = 0; i < preset.points; i++) {
385
+ const out = -Math.PI / 2 + i * 2 * Math.PI / preset.points;
386
+ for (const [angle, r] of [[out, 1], [out + Math.PI / preset.points, inner]]) {
387
+ const x = w / 2 * (1 + hf * r * Math.cos(angle));
388
+ pts.push([x, h - h / 2 * vf * (1 + r * Math.sin(angle))]);
389
+ }
390
+ }
391
+ return polygon(pts);
392
+ }
160
393
  function regularPolygon(w, h, n) {
161
394
  const cx = w / 2;
162
395
  const cy = h / 2;
@@ -172,10 +405,11 @@ function regularPolygon(w, h, n) {
172
405
  function blockArrow(w, h, adjust, dir) {
173
406
  const thick = clamp(frac(adjust, "adj1", 5e4), 0, 1);
174
407
  const head = clamp(frac(adjust, "adj2", 5e4), 0, 1);
408
+ const ss = Math.min(w, h);
175
409
  if (dir === "right" || dir === "left") {
176
- const sb = h * (1 - thick) / 2;
177
- const st = h * (1 + thick) / 2;
178
- const hl = head * w;
410
+ const sb = (h - ss * thick) / 2;
411
+ const st = (h + ss * thick) / 2;
412
+ const hl = head * ss;
179
413
  if (dir === "right") {
180
414
  const bx = w - hl;
181
415
  return polygon([
@@ -199,9 +433,9 @@ function blockArrow(w, h, adjust, dir) {
199
433
  [w, st]
200
434
  ]);
201
435
  }
202
- const sl = w * (1 - thick) / 2;
203
- const sr = w * (1 + thick) / 2;
204
- const hh = head * h;
436
+ const sl = (w - ss * thick) / 2;
437
+ const sr = (w + ss * thick) / 2;
438
+ const hh = head * ss;
205
439
  if (dir === "up") {
206
440
  const by = h - hh;
207
441
  return polygon([
@@ -37,6 +37,18 @@ export declare function gradientToSolid(gradient: ShapeGradient): string;
37
37
  * @returns The `<linearGradient>` or `<radialGradient>` markup.
38
38
  */
39
39
  export declare function gradientSvgDef(id: string, g: ShapeGradient): string;
40
+ /**
41
+ * §20.1.8.24 / §20.1.8.42 — the arrowheads a line asks for at its ends, as
42
+ * filled paths in the same local frame as the shape's own geometry. The head
43
+ * rides the first point of the first subpath and the tail the last point of
44
+ * the last one, each turned along the segment that reaches it.
45
+ *
46
+ * @param paths The shape's geometry paths.
47
+ * @param line The parsed line (its `headEnd`/`tailEnd` are read).
48
+ * @param strokeWidthPt The pen width the decorations are sized against.
49
+ * @returns The decoration paths (empty when the line asks for none).
50
+ */
51
+ export declare function lineEndPaths(paths: ReadonlyArray<VectorPath>, line: ShapeLine | undefined, strokeWidthPt: number): Array<VectorPath>;
40
52
  /**
41
53
  * Build a {@link StrokeStyle} from a shape's `a:ln` line, resolving the default
42
54
  * width (0.75pt), dash pattern and line cap. Returns `undefined` for no line or
@@ -60,12 +60,125 @@ function gradientToSolid(gradient) {
60
60
  function gradientSvgDef(id, g) {
61
61
  const n = (x) => String(Math.round(x * 1e4) / 1e4);
62
62
  const stops = g.stops.map((s) => `<stop offset="${n(s.offset)}" stop-color="#${s.colorHex}"/>`).join("");
63
- if (g.kind === "radial") return `<radialGradient id="${id}">${stops}</radialGradient>`;
63
+ if (g.kind === "radial") {
64
+ const c = g.center;
65
+ if (!c) return `<radialGradient id="${id}">${stops}</radialGradient>`;
66
+ const r = Math.SQRT2 / 2;
67
+ return `<radialGradient id="${id}" cx="${n(c.x)}" cy="${n(c.y)}" r="${n(r)}">${stops}</radialGradient>`;
68
+ }
64
69
  const rad = -(g.angle ?? 0) * Math.PI / 180;
65
70
  const dx = Math.cos(rad) / 2;
66
71
  const dy = Math.sin(rad) / 2;
67
72
  return `<linearGradient id="${id}" x1="${n(.5 - dx)}" y1="${n(.5 - dy)}" x2="${n(.5 + dx)}" y2="${n(.5 + dy)}">${stops}</linearGradient>`;
68
73
  }
74
+ var LINE_END_STEPS = {
75
+ sm: 3,
76
+ med: 4.5,
77
+ lg: 6
78
+ };
79
+ var MIN_LINE_END_PEN_PT = 1;
80
+ /**
81
+ * §20.1.8.24 / §20.1.8.42 — the arrowheads a line asks for at its ends, as
82
+ * filled paths in the same local frame as the shape's own geometry. The head
83
+ * rides the first point of the first subpath and the tail the last point of
84
+ * the last one, each turned along the segment that reaches it.
85
+ *
86
+ * @param paths The shape's geometry paths.
87
+ * @param line The parsed line (its `headEnd`/`tailEnd` are read).
88
+ * @param strokeWidthPt The pen width the decorations are sized against.
89
+ * @returns The decoration paths (empty when the line asks for none).
90
+ */
91
+ function lineEndPaths(paths, line, strokeWidthPt) {
92
+ if (!line || !line.headEnd && !line.tailEnd || line.fill === "none") return [];
93
+ const pts = pathPoints(paths);
94
+ if (pts.length < 2) return [];
95
+ const out = [];
96
+ const pen = Math.max(strokeWidthPt, MIN_LINE_END_PEN_PT);
97
+ if (line.headEnd) {
98
+ const p = pts[0];
99
+ const q = pts[1];
100
+ const path = endPath(line.headEnd, p, q, pen);
101
+ if (path) out.push(path);
102
+ }
103
+ if (line.tailEnd) {
104
+ const p = pts[pts.length - 1];
105
+ const q = pts[pts.length - 2];
106
+ const path = endPath(line.tailEnd, p, q, pen);
107
+ if (path) out.push(path);
108
+ }
109
+ return out;
110
+ }
111
+ function pathPoints(paths) {
112
+ const out = [];
113
+ for (const path of paths) for (const seg of path.segments) {
114
+ if (seg.op === "close") continue;
115
+ out.push({
116
+ x: seg.x,
117
+ y: seg.y
118
+ });
119
+ }
120
+ return out;
121
+ }
122
+ function endPath(end, tip, from, pen) {
123
+ const dx = tip.x - from.x;
124
+ const dy = tip.y - from.y;
125
+ const len = Math.hypot(dx, dy);
126
+ if (len < 1e-6) return void 0;
127
+ const ux = dx / len;
128
+ const uy = dy / len;
129
+ const nx = -uy;
130
+ const ny = ux;
131
+ const L = LINE_END_STEPS[end.length ?? "med"] * pen;
132
+ const W = LINE_END_STEPS[end.width ?? "med"] * pen;
133
+ const at = (along, across) => ({
134
+ x: tip.x - ux * along + nx * across,
135
+ y: tip.y - uy * along + ny * across
136
+ });
137
+ const segments = [];
138
+ const move = (p) => {
139
+ segments.push({
140
+ op: "move",
141
+ x: p.x,
142
+ y: p.y
143
+ });
144
+ };
145
+ const line2 = (p) => {
146
+ segments.push({
147
+ op: "line",
148
+ x: p.x,
149
+ y: p.y
150
+ });
151
+ };
152
+ if (end.type === "diamond") {
153
+ move(at(0, 0));
154
+ line2(at(L / 2, W / 2));
155
+ line2(at(L, 0));
156
+ line2(at(L / 2, -W / 2));
157
+ } else if (end.type === "oval") {
158
+ const r = W / 2;
159
+ const c = at(L / 2, 0);
160
+ for (let i = 0; i < 8; i++) {
161
+ const a = i / 8 * 2 * Math.PI;
162
+ const p = {
163
+ x: c.x + r * Math.cos(a),
164
+ y: c.y + r * Math.sin(a)
165
+ };
166
+ if (i === 0) move(p);
167
+ else line2(p);
168
+ }
169
+ } else if (end.type === "stealth") {
170
+ move(at(0, 0));
171
+ line2(at(L, W / 2));
172
+ line2(at(L * .6, 0));
173
+ line2(at(L, -W / 2));
174
+ } else {
175
+ move(at(0, 0));
176
+ line2(at(L, W / 2));
177
+ line2(at(L, -W / 2));
178
+ }
179
+ segments.push({ op: "close" });
180
+ return { segments };
181
+ }
69
182
  /**
70
183
  * Build a {@link StrokeStyle} from a shape's `a:ln` line, resolving the default
71
184
  * width (0.75pt), dash pattern and line cap. Returns `undefined` for no line or
@@ -77,7 +190,7 @@ function gradientSvgDef(id, g) {
77
190
  function buildStroke(line) {
78
191
  if (!line || line.fill === "none") return void 0;
79
192
  const widthPt = line.width ?? DEFAULT_LINE_WIDTH_EMU / 12700;
80
- const dash = line.dash && line.dash !== "solid" ? dashPattern(line.dash, widthPt) : void 0;
193
+ const dash = line.customDash?.map((n) => Math.max(.01, n * widthPt)) ?? (line.dash && line.dash !== "solid" ? dashPattern(line.dash, widthPt) : void 0);
81
194
  const cap = line.cap === "flat" ? "butt" : line.cap;
82
195
  return {
83
196
  colorHex: line.colorHex ?? "000000",
@@ -148,4 +261,4 @@ function buildShapeTransform(pageX, pageY, widthPt, heightPt, rotation60k, flipH
148
261
  ];
149
262
  }
150
263
  //#endregion
151
- export { DEFAULT_INSET_LR_PT, DEFAULT_INSET_TB_PT, buildShapePaths, buildShapeTransform, buildStroke, gradientSvgDef, gradientToSolid };
264
+ export { DEFAULT_INSET_LR_PT, DEFAULT_INSET_TB_PT, buildShapePaths, buildShapeTransform, buildStroke, gradientSvgDef, gradientToSolid, lineEndPaths };
@@ -59,14 +59,16 @@ function columnSpark(values, w, h, color) {
59
59
  const slot = w / n;
60
60
  const bw = slot * .72;
61
61
  const off = (slot - bw) / 2;
62
- const pad = h * .06;
63
62
  const paths = [];
63
+ const at = (v) => (v - min) / span * h;
64
+ const zero = Math.min(h, Math.max(0, at(0)));
64
65
  for (let i = 0; i < n; i++) {
65
66
  const v = values[i];
66
67
  if (v === null || v === void 0) continue;
67
- const frac = (v - min) / span;
68
- const bh = Math.max(.4, pad + frac * (h - pad));
69
- paths.push(rectPath(i * slot + off, 0, bw, bh));
68
+ const y = at(v);
69
+ const bottom = Math.min(zero, y);
70
+ const bh = Math.max(.4, Math.abs(y - zero));
71
+ paths.push(rectPath(i * slot + off, bottom, bw, bh));
70
72
  }
71
73
  return paths.length > 0 ? [{
72
74
  paths,
@@ -1,3 +1,4 @@
1
+ import { PoNode } from '../po-helpers.js';
1
2
  /**
2
3
  * Parse a DrawingML theme part (ECMA-376 §20.1.6.2, `a:clrScheme`) into a
3
4
  * `name → hex` colour map. Reads the twelve scheme slots — `dk1`/`lt1`/`dk2`/
@@ -10,3 +11,53 @@
10
11
  * empty when no `a:clrScheme` is present.
11
12
  */
12
13
  export declare function parseTheme(themeXml: Uint8Array): Map<string, string>;
14
+ /**
15
+ * Parse a theme's line-style widths (ECMA-376 §20.1.4.1.21 `a:lnStyleLst`).
16
+ *
17
+ * A shape drawn from the gallery keeps its outline as `<a:lnRef idx="N">`,
18
+ * which is a 1-based index into this list — the reference names the colour and
19
+ * the list holds the width. The standard Office theme is 0.75pt / 2pt / 3pt.
20
+ *
21
+ * @param themeXml The raw theme part bytes, UTF-8.
22
+ * @returns The widths in points, in list order; empty when the theme declares
23
+ * no `a:fmtScheme`.
24
+ */
25
+ export declare function parseThemeLineWidths(themeXml: Uint8Array): Array<number>;
26
+ /**
27
+ * Parse a theme's fill styles (§20.1.4.1.13 `a:fillStyleLst`), as the raw nodes.
28
+ *
29
+ * A shape drawn from the gallery carries no fill of its own — only
30
+ * `<a:fillRef idx="N">`, a 1-based index into this list, and a colour to put
31
+ * where the styles say `phClr`. The standard Office theme's slots are a solid,
32
+ * a subtle gradient and a stronger one, so reading the reference's colour alone
33
+ * paints slot 3 flat: 47504.xlsx's rectangle is a gradient in both references
34
+ * and a single blue in ours.
35
+ *
36
+ * The nodes are handed back unparsed because what they hold is a whole fill —
37
+ * solid, gradient, pattern — which the shape readers already know how to read.
38
+ *
39
+ * @param themeXml The raw theme part bytes, UTF-8.
40
+ * @returns The `a:fillStyleLst` children in list order; empty when the theme
41
+ * declares no `a:fmtScheme`.
42
+ */
43
+ export declare function parseThemeFillStyles(themeXml: Uint8Array): Array<PoNode>;
44
+ /**
45
+ * Parse a theme's BACKGROUND fill styles (§20.1.4.1.7 `a:bgFillStyleLst`), as
46
+ * the raw nodes. An `<a:fillRef>` reaches them with an index past 1000 — slot
47
+ * 1001 is the first — and that is where the page-sized backdrops Word's cover
48
+ * pages are built from live (fdo78957.docx).
49
+ *
50
+ * @param themeXml The raw theme part bytes, UTF-8.
51
+ * @returns The fill children in list order; empty when the theme declares none.
52
+ */
53
+ export declare function parseThemeBgFillStyles(themeXml: Uint8Array): Array<PoNode>;
54
+ /**
55
+ * Parse a theme's effect styles (§20.1.4.1.15 `a:effectStyleLst`), as the raw
56
+ * nodes. `<a:effectRef idx="N">` is a 1-based index into this list, exactly as
57
+ * the fill and line references index theirs.
58
+ *
59
+ * @param themeXml The raw theme part bytes, UTF-8.
60
+ * @returns The `a:effectStyle` children in list order; empty when the theme
61
+ * declares no `a:fmtScheme`.
62
+ */
63
+ export declare function parseThemeEffectStyles(themeXml: Uint8Array): Array<PoNode>;
@@ -3,6 +3,7 @@ import { XMLParser } from "fast-xml-parser";
3
3
  //#region src/core/drawingml/theme-parser.ts
4
4
  var decoder = new TextDecoder("utf-8");
5
5
  var parser = new XMLParser({
6
+ htmlEntities: true,
6
7
  ignoreAttributes: false,
7
8
  attributeNamePrefix: "@_",
8
9
  preserveOrder: true,
@@ -53,6 +54,96 @@ function parseTheme(themeXml) {
53
54
  }
54
55
  return out;
55
56
  }
57
+ /**
58
+ * Parse a theme's line-style widths (ECMA-376 §20.1.4.1.21 `a:lnStyleLst`).
59
+ *
60
+ * A shape drawn from the gallery keeps its outline as `<a:lnRef idx="N">`,
61
+ * which is a 1-based index into this list — the reference names the colour and
62
+ * the list holds the width. The standard Office theme is 0.75pt / 2pt / 3pt.
63
+ *
64
+ * @param themeXml The raw theme part bytes, UTF-8.
65
+ * @returns The widths in points, in list order; empty when the theme declares
66
+ * no `a:fmtScheme`.
67
+ */
68
+ function parseThemeLineWidths(themeXml) {
69
+ const list = poFindByPath(parser.parse(decoder.decode(themeXml)), [
70
+ "a:theme",
71
+ "a:themeElements",
72
+ "a:fmtScheme",
73
+ "a:lnStyleLst"
74
+ ]);
75
+ if (!list) return [];
76
+ const out = [];
77
+ for (const ln of poChildren(list)) {
78
+ if (!poIs(ln, "a:ln")) continue;
79
+ const w = Number(poAttr(ln, "w") ?? "");
80
+ out.push(Number.isFinite(w) && w > 0 ? w / EMU_PER_POINT : .75);
81
+ }
82
+ return out;
83
+ }
84
+ /**
85
+ * Parse a theme's fill styles (§20.1.4.1.13 `a:fillStyleLst`), as the raw nodes.
86
+ *
87
+ * A shape drawn from the gallery carries no fill of its own — only
88
+ * `<a:fillRef idx="N">`, a 1-based index into this list, and a colour to put
89
+ * where the styles say `phClr`. The standard Office theme's slots are a solid,
90
+ * a subtle gradient and a stronger one, so reading the reference's colour alone
91
+ * paints slot 3 flat: 47504.xlsx's rectangle is a gradient in both references
92
+ * and a single blue in ours.
93
+ *
94
+ * The nodes are handed back unparsed because what they hold is a whole fill —
95
+ * solid, gradient, pattern — which the shape readers already know how to read.
96
+ *
97
+ * @param themeXml The raw theme part bytes, UTF-8.
98
+ * @returns The `a:fillStyleLst` children in list order; empty when the theme
99
+ * declares no `a:fmtScheme`.
100
+ */
101
+ function parseThemeFillStyles(themeXml) {
102
+ const list = poFindByPath(parser.parse(decoder.decode(themeXml)), [
103
+ "a:theme",
104
+ "a:themeElements",
105
+ "a:fmtScheme",
106
+ "a:fillStyleLst"
107
+ ]);
108
+ return list ? [...poChildren(list)] : [];
109
+ }
110
+ /**
111
+ * Parse a theme's BACKGROUND fill styles (§20.1.4.1.7 `a:bgFillStyleLst`), as
112
+ * the raw nodes. An `<a:fillRef>` reaches them with an index past 1000 — slot
113
+ * 1001 is the first — and that is where the page-sized backdrops Word's cover
114
+ * pages are built from live (fdo78957.docx).
115
+ *
116
+ * @param themeXml The raw theme part bytes, UTF-8.
117
+ * @returns The fill children in list order; empty when the theme declares none.
118
+ */
119
+ function parseThemeBgFillStyles(themeXml) {
120
+ const list = poFindByPath(parser.parse(decoder.decode(themeXml)), [
121
+ "a:theme",
122
+ "a:themeElements",
123
+ "a:fmtScheme",
124
+ "a:bgFillStyleLst"
125
+ ]);
126
+ return list ? [...poChildren(list)] : [];
127
+ }
128
+ /**
129
+ * Parse a theme's effect styles (§20.1.4.1.15 `a:effectStyleLst`), as the raw
130
+ * nodes. `<a:effectRef idx="N">` is a 1-based index into this list, exactly as
131
+ * the fill and line references index theirs.
132
+ *
133
+ * @param themeXml The raw theme part bytes, UTF-8.
134
+ * @returns The `a:effectStyle` children in list order; empty when the theme
135
+ * declares no `a:fmtScheme`.
136
+ */
137
+ function parseThemeEffectStyles(themeXml) {
138
+ const list = poFindByPath(parser.parse(decoder.decode(themeXml)), [
139
+ "a:theme",
140
+ "a:themeElements",
141
+ "a:fmtScheme",
142
+ "a:effectStyleLst"
143
+ ]);
144
+ return list ? [...poChildren(list)] : [];
145
+ }
146
+ var EMU_PER_POINT = 12700;
56
147
  function colorOf(slot) {
57
148
  for (const c of poChildren(slot)) if (poIs(c, "a:srgbClr")) {
58
149
  const v = poAttr(c, "val");
@@ -63,4 +154,4 @@ function colorOf(slot) {
63
154
  }
64
155
  }
65
156
  //#endregion
66
- export { parseTheme };
157
+ export { parseTheme, parseThemeBgFillStyles, parseThemeEffectStyles, parseThemeFillStyles, parseThemeLineWidths };
@@ -12,6 +12,11 @@ export interface FontMeasure {
12
12
  readonly textWidthPt: (text: string, fontSize: number) => number;
13
13
  /** Encode `text` as a hex string of 4-digit glyph ids (Identity-H addressing). */
14
14
  readonly encodeTextAsCidHex: (text: string) => string;
15
+ /**
16
+ * The complete PDF text-showing operator for `text` — `<hex> Tj`, or a
17
+ * `[…] TJ` array when shaping moved a glyph off its own advance.
18
+ */
19
+ readonly showText: (text: string) => string;
15
20
  }
16
21
  /**
17
22
  * Build a {@link FontMeasure} over a parsed font.
@@ -33,10 +33,30 @@ function createFontMeasure(parsed, kern = true) {
33
33
  for (const gid of shaped.gids) out += gid.toString(16).padStart(4, "0").toUpperCase();
34
34
  return out;
35
35
  };
36
+ const showText = (text) => {
37
+ const shaped = shapeText(text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures, kerning, parsed.joiningForms);
38
+ const parts = [];
39
+ let hex = "";
40
+ let kerned = false;
41
+ for (let i = 0; i < shaped.gids.length; i++) {
42
+ const gid = shaped.gids[i];
43
+ hex += gid.toString(16).padStart(4, "0").toUpperCase();
44
+ const delta = (shaped.advances[i] ?? 0) - (parsed.advanceWidths[gid] ?? 0);
45
+ if (delta === 0) continue;
46
+ const adj = -delta * 1e3 / parsed.unitsPerEm;
47
+ parts.push(`<${hex}>`, String(Number(adj.toFixed(2))));
48
+ hex = "";
49
+ kerned = true;
50
+ }
51
+ if (!kerned) return `<${hex}> Tj`;
52
+ if (hex !== "") parts.push(`<${hex}>`);
53
+ return `[${parts.join(" ")}] TJ`;
54
+ };
36
55
  return {
37
56
  pdfWidthForGid,
38
57
  textWidthPt,
39
- encodeTextAsCidHex
58
+ encodeTextAsCidHex,
59
+ showText
40
60
  };
41
61
  }
42
62
  //#endregion