reamkit 1.23.0 → 1.24.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 (57) hide show
  1. package/dist/esm/core/bmp.d.ts +33 -0
  2. package/dist/esm/core/bmp.js +276 -0
  3. package/dist/esm/core/document-model/types.d.ts +37 -0
  4. package/dist/esm/core/drawingml/chart-geometry.js +3 -2
  5. package/dist/esm/core/drawingml/diagram/colors.d.ts +52 -0
  6. package/dist/esm/core/drawingml/diagram/colors.js +125 -0
  7. package/dist/esm/core/drawingml/diagram/data-model.d.ts +47 -0
  8. package/dist/esm/core/drawingml/diagram/data-model.js +119 -0
  9. package/dist/esm/core/drawingml/diagram/layout-engine.d.ts +46 -0
  10. package/dist/esm/core/drawingml/diagram/layout-engine.js +948 -0
  11. package/dist/esm/core/drawingml/diagram/run.d.ts +37 -0
  12. package/dist/esm/core/drawingml/diagram/run.js +50 -0
  13. package/dist/esm/core/drawingml/diagram/to-drawing.d.ts +16 -0
  14. package/dist/esm/core/drawingml/diagram/to-drawing.js +86 -0
  15. package/dist/esm/core/drawingml/preset-geometry.js +116 -6
  16. package/dist/esm/core/drawingml/text-warp.d.ts +58 -0
  17. package/dist/esm/core/drawingml/text-warp.js +355 -0
  18. package/dist/esm/core/drawingml/theme-parser.js +9 -3
  19. package/dist/esm/core/font/measure.d.ts +12 -0
  20. package/dist/esm/core/font/measure.js +36 -0
  21. package/dist/esm/core/images.d.ts +30 -2
  22. package/dist/esm/core/images.js +121 -5
  23. package/dist/esm/core/metafile/emf.js +144 -16
  24. package/dist/esm/core/metafile/picture.d.ts +49 -0
  25. package/dist/esm/core/metafile/picture.js +70 -1
  26. package/dist/esm/core/metafile/wmf.js +53 -4
  27. package/dist/esm/core/ole/escher-blip.js +11 -1
  28. package/dist/esm/excel/print-model.js +2 -2
  29. package/dist/esm/excel/sheet-drawing.js +1 -1
  30. package/dist/esm/excel/sheet-to-flow.js +16 -2
  31. package/dist/esm/html/html-writer.js +2 -1
  32. package/dist/esm/layout/page-doc.d.ts +35 -0
  33. package/dist/esm/layout/page-doc.js +14 -1
  34. package/dist/esm/layout/styled-layout.js +176 -15
  35. package/dist/esm/pdf/shading.js +8 -6
  36. package/dist/esm/pdf/styled-page-emitter.js +42 -3
  37. package/dist/esm/pdf/vector-graphics.js +1 -1
  38. package/dist/esm/pdf-reader/image-decode.js +1 -1
  39. package/dist/esm/pptx/ppt/ppt-reader.js +95 -27
  40. package/dist/esm/pptx/ppt/ppt-text.d.ts +41 -0
  41. package/dist/esm/pptx/ppt/ppt-text.js +327 -37
  42. package/dist/esm/pptx/pptx-reader.js +90 -10
  43. package/dist/esm/pptx/slide-parser.d.ts +4 -1
  44. package/dist/esm/pptx/slide-parser.js +99 -13
  45. package/dist/esm/pptx/sp-helpers.d.ts +7 -3
  46. package/dist/esm/pptx/sp-helpers.js +45 -7
  47. package/dist/esm/pptx/table-style.d.ts +7 -0
  48. package/dist/esm/pptx/table-style.js +59 -7
  49. package/dist/esm/svg/svg-writer.js +1 -0
  50. package/dist/esm/word/document-parser.d.ts +4 -1
  51. package/dist/esm/word/document-parser.js +4 -1
  52. package/dist/esm/word/docx-reader.js +26 -13
  53. package/dist/esm/word/docx-writer.js +4 -0
  54. package/dist/esm/word/drawing-parser.d.ts +20 -0
  55. package/dist/esm/word/drawing-parser.js +37 -4
  56. package/dist/esm/word/table-parser.js +3 -0
  57. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { PathBuilder } from "../vector.js";
2
- import { applyTransform, cloneDc, colorRef, newDeviceContext } from "./picture.js";
2
+ import { applyTransform, clippedAway, cloneDc, colorRef, intersectClip, newDeviceContext, primBounds } from "./picture.js";
3
3
  import { cropDib, fadedDib, readDib } from "./dib.js";
4
4
  import { makeBlitter } from "./blit.js";
5
5
  import { fromSymbolFont } from "./symbol-fonts.js";
@@ -71,7 +71,14 @@ function readEmf(bytes) {
71
71
  };
72
72
  let building;
73
73
  const open = () => building ?? [];
74
- const blitter = makeBlitter((prim) => prims.push(prim));
74
+ const inFigure = () => {
75
+ const tail = building?.[building.length - 1];
76
+ return tail !== void 0 && tail.op !== "close";
77
+ };
78
+ const emit = (prim) => {
79
+ if (!clippedAway(dc, primBounds(prim))) prims.push(prim);
80
+ };
81
+ const blitter = makeBlitter(emit);
75
82
  /**
76
83
  * §2.3.1 — one blit record: the source bitmap into the destination
77
84
  * rectangle, both stated in the record's own fields. The rectangle is mapped
@@ -101,7 +108,10 @@ function readEmf(bytes) {
101
108
  };
102
109
  const strokeOf = () => {
103
110
  if (dc.pen.style === "none") return void 0;
104
- const w = dc.pen.widthLu <= 1 ? 1 : dc.pen.widthLu * Math.abs(sx());
111
+ const m = dc.transform;
112
+ const world = Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) || 1;
113
+ const lu = dc.pen.widthLu * world;
114
+ const w = lu <= 1 ? 1 : lu * Math.abs(sx());
105
115
  return {
106
116
  colorHex: dc.pen.colorHex,
107
117
  widthPt: w,
@@ -117,13 +127,22 @@ function readEmf(bytes) {
117
127
  const st = stroke ? strokeOf() : void 0;
118
128
  const fillHex = fill && !dc.brush.hollow ? dc.brush.colorHex : void 0;
119
129
  if (fillHex === void 0 && !st) return;
120
- prims.push({
130
+ emit({
121
131
  kind: "path",
122
132
  paths: [path],
123
133
  ...fillHex !== void 0 ? { fillColorHex: fillHex } : {},
124
134
  ...st ? { stroke: st } : {}
125
135
  });
126
136
  };
137
+ /** Two mapped corners → the rectangle they bound, in either order. */
138
+ function rectOf(a, b) {
139
+ return {
140
+ left: Math.min(a.x, b.x),
141
+ top: Math.min(a.y, b.y),
142
+ right: Math.max(a.x, b.x),
143
+ bottom: Math.max(a.y, b.y)
144
+ };
145
+ }
127
146
  /** A closed rectangle, as the geometry records draw one. */
128
147
  const rectSegments = (l, t, r, b) => {
129
148
  const p0 = px(l, t);
@@ -176,6 +195,32 @@ function readEmf(bytes) {
176
195
  };
177
196
  haveView = true;
178
197
  break;
198
+ case 30: {
199
+ const a = px(at(8), at(12));
200
+ const b = px(at(16), at(20));
201
+ dc = {
202
+ ...dc,
203
+ clip: intersectClip(dc, rectOf(a, b))
204
+ };
205
+ break;
206
+ }
207
+ case 75: {
208
+ const cbRgnData = u(8);
209
+ const mode = u(12);
210
+ if (cbRgnData === 0 && mode === 5) {
211
+ const { clip: _drop, ...rest } = dc;
212
+ dc = rest;
213
+ } else if (mode === 5 && cbRgnData >= 32 && u(20) === 1) {
214
+ const base = off + 16 + 32;
215
+ const a = px(v.getInt32(base, true), v.getInt32(base + 4, true));
216
+ const b = px(v.getInt32(base + 8, true), v.getInt32(base + 12, true));
217
+ dc = {
218
+ ...dc,
219
+ clip: rectOf(a, b)
220
+ };
221
+ }
222
+ break;
223
+ }
179
224
  case 33:
180
225
  stack.push(cloneDc(dc));
181
226
  break;
@@ -281,7 +326,7 @@ function readEmf(bytes) {
281
326
  dc.y = at(12);
282
327
  const to = px(dc.x, dc.y);
283
328
  if (building) {
284
- if (building.length === 0) building.push({
329
+ if (!inFigure()) building.push({
285
330
  op: "move",
286
331
  x: from.x,
287
332
  y: from.y
@@ -327,6 +372,11 @@ function readEmf(bytes) {
327
372
  case 44:
328
373
  paint(rectSegments(at(8), at(12), at(16), at(20)), true, true);
329
374
  break;
375
+ case 47:
376
+ case 46:
377
+ case 45:
378
+ paint(arcSegments(px, at(8), at(12), at(16), at(20), at(24), at(28), at(32), at(36), type), type !== 45, true);
379
+ break;
330
380
  case 3:
331
381
  case 4:
332
382
  case 2:
@@ -345,10 +395,13 @@ function readEmf(bytes) {
345
395
  const segs = polySegments(px, pts, {
346
396
  closed,
347
397
  bezier: type === 2 || type === 5 || type === 85 || type === 88,
348
- ...type === 5 || type === 6 || type === 88 || type === 89 ? { from: {
349
- x: dc.x,
350
- y: dc.y
351
- } } : {}
398
+ ...type === 5 || type === 6 || type === 88 || type === 89 ? {
399
+ from: {
400
+ x: dc.x,
401
+ y: dc.y
402
+ },
403
+ joined: inFigure()
404
+ } : {}
352
405
  });
353
406
  if (pts.length > 0) {
354
407
  dc.x = pts[pts.length - 1].x;
@@ -395,7 +448,7 @@ function readEmf(bytes) {
395
448
  if (text.trim() !== "") {
396
449
  const p = px(refX, refY);
397
450
  const font = dc.font;
398
- prims.push({
451
+ emit({
399
452
  kind: "text",
400
453
  text: fromSymbolFont(text, font?.family),
401
454
  x: p.x,
@@ -422,6 +475,7 @@ function readEmf(bytes) {
422
475
  const h = at(36);
423
476
  if (cbBmiSrc === 0) {
424
477
  const hex = rop === 66 ? "000000" : rop === 16711778 ? "FFFFFF" : void 0;
478
+ if (hex === void 0 && rop !== 15728673) break;
425
479
  const brush = dc.brush;
426
480
  if (hex !== void 0) dc.brush = {
427
481
  kind: "brush",
@@ -671,12 +725,14 @@ function polySegments(map, pts, o) {
671
725
  if (pts.length === 0) return segs;
672
726
  let i = 0;
673
727
  if (o.from) {
674
- const s = map(o.from.x, o.from.y);
675
- segs.push({
676
- op: "move",
677
- x: s.x,
678
- y: s.y
679
- });
728
+ if (o.joined !== true) {
729
+ const s = map(o.from.x, o.from.y);
730
+ segs.push({
731
+ op: "move",
732
+ x: s.x,
733
+ y: s.y
734
+ });
735
+ }
680
736
  } else {
681
737
  const s = map(pts[0].x, pts[0].y);
682
738
  segs.push({
@@ -712,6 +768,78 @@ function polySegments(map, pts, o) {
712
768
  return segs;
713
769
  }
714
770
  var KAPPA = .5522847498307936;
771
+ /**
772
+ * §2.3.5.2/.4/.13 — the wedge, chord or bare curve an `EMR_ARC`, `EMR_CHORD` or
773
+ * `EMR_PIE` draws.
774
+ *
775
+ * All three state the same thing: an ellipse inscribed in a box, and two points
776
+ * naming RAYS from its centre. The arc runs from where the first ray crosses
777
+ * the ellipse to where the second does, anticlockwise on the page — GDI's
778
+ * default direction — and the two records differ only in how they close: a pie
779
+ * back through the centre, a chord straight across, an arc not at all.
780
+ *
781
+ * Built in the metafile's own coordinates and mapped point by point, exactly as
782
+ * the ellipse and the polygons are: the mapping is affine, so a Bézier's
783
+ * control points survive it.
784
+ *
785
+ * @param map The metafile's logical → page mapping.
786
+ * @param l,t,r,b The box the ellipse is inscribed in.
787
+ * @param sx,sy The point naming the ray the arc starts at.
788
+ * @param ex,ey The point naming the ray it ends at.
789
+ * @param type The record type, which decides how the figure closes.
790
+ * @returns The path segments, in page coordinates.
791
+ */
792
+ function arcSegments(map, l, t, r, b, sx, sy, ex, ey, type) {
793
+ const cx = (l + r) / 2;
794
+ const cy = (t + b) / 2;
795
+ const rx = (r - l) / 2;
796
+ const ry = (b - t) / 2;
797
+ if (!(rx > 0) || !(ry > 0)) return [];
798
+ const angleOf = (x, y) => Math.atan2((y - cy) / ry, (x - cx) / rx);
799
+ const start = angleOf(sx, sy);
800
+ let sweep = angleOf(ex, ey) - start;
801
+ if (sweep >= 0) sweep -= 2 * Math.PI;
802
+ const point = (a) => map(cx + rx * Math.cos(a), cy + ry * Math.sin(a));
803
+ const segs = [];
804
+ const from = point(start);
805
+ if (type === 47) {
806
+ const c = map(cx, cy);
807
+ segs.push({
808
+ op: "move",
809
+ x: c.x,
810
+ y: c.y
811
+ }, {
812
+ op: "line",
813
+ x: from.x,
814
+ y: from.y
815
+ });
816
+ } else segs.push({
817
+ op: "move",
818
+ x: from.x,
819
+ y: from.y
820
+ });
821
+ const steps = Math.max(1, Math.ceil(Math.abs(sweep) / (Math.PI / 2)));
822
+ const delta = sweep / steps;
823
+ const alpha = 4 / 3 * Math.tan(delta / 4);
824
+ for (let i = 0; i < steps; i++) {
825
+ const a0 = start + delta * i;
826
+ const a1 = a0 + delta;
827
+ const t0 = map(cx + rx * Math.cos(a0) - alpha * rx * Math.sin(a0), cy + ry * Math.sin(a0) + alpha * ry * Math.cos(a0));
828
+ const t1 = map(cx + rx * Math.cos(a1) + alpha * rx * Math.sin(a1), cy + ry * Math.sin(a1) - alpha * ry * Math.cos(a1));
829
+ const to = point(a1);
830
+ segs.push({
831
+ op: "cubic",
832
+ x1: t0.x,
833
+ y1: t0.y,
834
+ x2: t1.x,
835
+ y2: t1.y,
836
+ x: to.x,
837
+ y: to.y
838
+ });
839
+ }
840
+ if (type !== 45) segs.push({ op: "close" });
841
+ return segs;
842
+ }
715
843
  function ellipseSegments(map, l, t, r, b) {
716
844
  const cx = (l + r) / 2;
717
845
  const cy = (t + b) / 2;
@@ -102,12 +102,61 @@ export interface DeviceContext {
102
102
  y: number;
103
103
  /** The world→page transform, as `[a, b, c, d, e, f]`. */
104
104
  transform: readonly [number, number, number, number, number, number];
105
+ /**
106
+ * MS-EMF §2.3.2 / MS-WMF §2.3.2 — the region drawing is limited to, in
107
+ * logical units, when the metafile states a RECTANGULAR one. A primitive
108
+ * that falls wholly outside it is not drawn: an embedded workbook's preview
109
+ * clips its sheet to the used range, and unclipped we painted the whole
110
+ * empty grid around it.
111
+ */
112
+ clip?: {
113
+ readonly left: number;
114
+ readonly top: number;
115
+ readonly right: number;
116
+ readonly bottom: number;
117
+ };
105
118
  }
106
119
  export declare const DEFAULT_PEN: MetaPen;
107
120
  export declare const DEFAULT_BRUSH: MetaBrush;
108
121
  /** A device context in its documented initial state (MS-WMF §3.1.5). */
109
122
  export declare function newDeviceContext(): DeviceContext;
110
123
  export declare function cloneDc(dc: DeviceContext): DeviceContext;
124
+ /**
125
+ * Whether a primitive bounded by `box` is wholly outside the device context's
126
+ * clip. Partial overlaps are kept whole — the reader discards what the clip
127
+ * excludes rather than cutting geometry, which is a missing cut, never wrong
128
+ * content.
129
+ *
130
+ * @param dc The device context, for its clip.
131
+ * @param box The primitive's bounds in logical units.
132
+ * @returns `true` when nothing of it would be drawn.
133
+ */
134
+ export declare function clippedAway(dc: DeviceContext, box: {
135
+ left: number;
136
+ top: number;
137
+ right: number;
138
+ bottom: number;
139
+ }): boolean;
140
+ /** The intersection of a device context's clip with another rectangle. */
141
+ export declare function intersectClip(dc: DeviceContext, rect: {
142
+ left: number;
143
+ top: number;
144
+ right: number;
145
+ bottom: number;
146
+ }): NonNullable<DeviceContext['clip']>;
147
+ /**
148
+ * A primitive's bounds in the units it is stored in, for the clip test.
149
+ *
150
+ * @param prim The primitive.
151
+ * @returns Its bounding rectangle; a text run's is its reference point, since
152
+ * the box it fills is not known until it is measured.
153
+ */
154
+ export declare function primBounds(prim: PicturePrim): {
155
+ left: number;
156
+ top: number;
157
+ right: number;
158
+ bottom: number;
159
+ };
111
160
  /** A COLORREF (`0x00bbggrr`) as the 6-hex this codebase's colours use. */
112
161
  export declare function colorRef(v: number): string;
113
162
  /** Apply the world transform to a point. */
@@ -36,6 +36,75 @@ function newDeviceContext() {
36
36
  function cloneDc(dc) {
37
37
  return { ...dc };
38
38
  }
39
+ /**
40
+ * Whether a primitive bounded by `box` is wholly outside the device context's
41
+ * clip. Partial overlaps are kept whole — the reader discards what the clip
42
+ * excludes rather than cutting geometry, which is a missing cut, never wrong
43
+ * content.
44
+ *
45
+ * @param dc The device context, for its clip.
46
+ * @param box The primitive's bounds in logical units.
47
+ * @returns `true` when nothing of it would be drawn.
48
+ */
49
+ function clippedAway(dc, box) {
50
+ const c = dc.clip;
51
+ if (!c) return false;
52
+ return box.right < c.left || box.left > c.right || box.bottom < c.top || box.top > c.bottom;
53
+ }
54
+ /** The intersection of a device context's clip with another rectangle. */
55
+ function intersectClip(dc, rect) {
56
+ const c = dc.clip;
57
+ if (!c) return rect;
58
+ return {
59
+ left: Math.max(c.left, rect.left),
60
+ top: Math.max(c.top, rect.top),
61
+ right: Math.min(c.right, rect.right),
62
+ bottom: Math.min(c.bottom, rect.bottom)
63
+ };
64
+ }
65
+ /**
66
+ * A primitive's bounds in the units it is stored in, for the clip test.
67
+ *
68
+ * @param prim The primitive.
69
+ * @returns Its bounding rectangle; a text run's is its reference point, since
70
+ * the box it fills is not known until it is measured.
71
+ */
72
+ function primBounds(prim) {
73
+ if (prim.kind === "text") return {
74
+ left: prim.x,
75
+ top: prim.y,
76
+ right: prim.x,
77
+ bottom: prim.y
78
+ };
79
+ if (prim.kind === "image") return {
80
+ left: prim.x,
81
+ top: prim.y,
82
+ right: prim.x + prim.width,
83
+ bottom: prim.y + prim.height
84
+ };
85
+ let left = Infinity;
86
+ let top = Infinity;
87
+ let right = -Infinity;
88
+ let bottom = -Infinity;
89
+ for (const path of prim.paths) for (const sg of path.segments) {
90
+ if (!("x" in sg)) continue;
91
+ left = Math.min(left, sg.x);
92
+ right = Math.max(right, sg.x);
93
+ top = Math.min(top, sg.y);
94
+ bottom = Math.max(bottom, sg.y);
95
+ }
96
+ return Number.isFinite(left) ? {
97
+ left,
98
+ top,
99
+ right,
100
+ bottom
101
+ } : {
102
+ left: 0,
103
+ top: 0,
104
+ right: 0,
105
+ bottom: 0
106
+ };
107
+ }
39
108
  /** A COLORREF (`0x00bbggrr`) as the 6-hex this codebase's colours use. */
40
109
  function colorRef(v) {
41
110
  return [
@@ -52,4 +121,4 @@ function applyTransform(t, x, y) {
52
121
  };
53
122
  }
54
123
  //#endregion
55
- export { applyTransform, cloneDc, colorRef, newDeviceContext };
124
+ export { applyTransform, clippedAway, cloneDc, colorRef, intersectClip, newDeviceContext, primBounds };
@@ -1,5 +1,5 @@
1
1
  import { PathBuilder } from "../vector.js";
2
- import { cloneDc, colorRef, newDeviceContext } from "./picture.js";
2
+ import { clippedAway, cloneDc, colorRef, intersectClip, newDeviceContext, primBounds } from "./picture.js";
3
3
  import { cropDib, readDib } from "./dib.js";
4
4
  import { makeBlitter } from "./blit.js";
5
5
  import { fromSymbolFont, symbolGeometryOf, symbolOutline } from "./symbol-fonts.js";
@@ -62,6 +62,7 @@ function readWmf(bytes) {
62
62
  };
63
63
  let origin;
64
64
  const emit = (prim) => {
65
+ if (clippedAway(dc, primBounds(prim))) return;
65
66
  origin ??= {
66
67
  x: win.x,
67
68
  y: win.y
@@ -145,6 +146,17 @@ function readWmf(bytes) {
145
146
  cy: p(0)
146
147
  };
147
148
  break;
149
+ case 1046:
150
+ dc = {
151
+ ...dc,
152
+ clip: intersectClip(dc, {
153
+ bottom: p(0),
154
+ right: p(1),
155
+ top: p(2),
156
+ left: p(3)
157
+ })
158
+ };
159
+ break;
148
160
  case 30:
149
161
  stack.push(cloneDc(dc));
150
162
  break;
@@ -388,12 +400,13 @@ function readWmf(bytes) {
388
400
  right: win.x + (win.cx || 1e3),
389
401
  bottom: win.y + (win.cy || 1e3)
390
402
  };
403
+ const flipped = win.cy < 0 ? prims.map((p) => mirrorPrimY(p, box.top + box.bottom)) : prims;
391
404
  return {
392
- left: box.left,
393
- top: box.top,
405
+ left: Math.min(box.left, box.right),
406
+ top: Math.min(box.top, box.bottom),
394
407
  width: Math.max(1, Math.abs(box.right - box.left)),
395
408
  height: Math.max(1, Math.abs(box.bottom - box.top)),
396
- prims,
409
+ prims: flipped,
397
410
  skipped: [...skipped]
398
411
  };
399
412
  }
@@ -451,6 +464,42 @@ function shiftPrim(prim, dx, dy) {
451
464
  }))
452
465
  };
453
466
  }
467
+ /**
468
+ * A primitive mirrored about a horizontal axis, for a window whose y grows UP.
469
+ *
470
+ * Only the geometry turns over. A text run's reference point names a corner of
471
+ * the cell IN DEVICE SPACE — GDI never draws the glyphs themselves upside down —
472
+ * so the point moves and the alignment flags stay as they are. An image states
473
+ * the TOP of its destination rectangle, which after the mirror is where its
474
+ * bottom used to be.
475
+ *
476
+ * @param prim The primitive, in the metafile's own logical units.
477
+ * @param twiceAxis Twice the y the mirror runs along, so `y' = twiceAxis - y`.
478
+ * @returns The primitive with its y coordinates turned over.
479
+ */
480
+ function mirrorPrimY(prim, twiceAxis) {
481
+ const at = (y) => twiceAxis - y;
482
+ if (prim.kind === "text") return {
483
+ ...prim,
484
+ y: at(prim.y)
485
+ };
486
+ if (prim.kind === "image") return {
487
+ ...prim,
488
+ y: at(prim.y + prim.height)
489
+ };
490
+ return {
491
+ ...prim,
492
+ paths: prim.paths.map((path) => ({
493
+ ...path,
494
+ segments: path.segments.map((sg) => "x" in sg ? {
495
+ ...sg,
496
+ y: at(sg.y),
497
+ ..."y1" in sg ? { y1: at(sg.y1) } : {},
498
+ ..."y2" in sg ? { y2: at(sg.y2) } : {}
499
+ } : sg)
500
+ }))
501
+ };
502
+ }
454
503
  function penStyle(style) {
455
504
  switch (style & 15) {
456
505
  case 1: return "dash";
@@ -1,3 +1,4 @@
1
+ import { dibToBmp } from "../bmp.js";
1
2
  import { unzlibSync } from "fflate";
2
3
  //#region src/core/ole/escher-blip.ts
3
4
  var BLIP_EMF = 61466;
@@ -54,9 +55,18 @@ var RASTER_MAGICS = [
54
55
  */
55
56
  function readEscherBlip(type, instance, data) {
56
57
  if (type === BLIP_EMF || type === BLIP_WMF || type === BLIP_PICT) return readMetafileBlip(instance, data);
57
- if (type === BLIP_JPEG || type === BLIP_PNG || type === BLIP_DIB || type === BLIP_TIFF || type === BLIP_JPEG_CMYK) return scanRaster(data);
58
+ if (type === BLIP_DIB) return readDibBlip(instance, data) ?? scanRaster(data);
59
+ if (type === BLIP_JPEG || type === BLIP_PNG || type === BLIP_TIFF || type === BLIP_JPEG_CMYK) return scanRaster(data);
58
60
  return scanRaster(data);
59
61
  }
62
+ function readDibBlip(instance, data) {
63
+ const uidBytes = TWO_UID_INSTANCES.has(instance) ? 32 : 16;
64
+ for (const at of [uidBytes + 1, uidBytes]) {
65
+ if (data.length <= at) continue;
66
+ const bmp = dibToBmp(data.subarray(at));
67
+ if (bmp) return bmp;
68
+ }
69
+ }
60
70
  function readMetafileBlip(instance, data) {
61
71
  const uidBytes = TWO_UID_INSTANCES.has(instance) ? 32 : 16;
62
72
  const head = uidBytes + METAFILE_HEADER_BYTES;
@@ -1122,8 +1122,8 @@ function shadingFromXf(xf, styles) {
1122
1122
  if (!pattern || pattern === "none") return void 0;
1123
1123
  if (pattern === "solid") return fill.fgColorHex ? { colorHex: fill.fgColorHex } : void 0;
1124
1124
  const density = PATTERN_DENSITY[pattern];
1125
- if (density === void 0 || !fill.fgColorHex) return void 0;
1126
- const blended = blendHex(fill.fgColorHex, fill.bgColorHex ?? "FFFFFF", density);
1125
+ if (density === void 0) return void 0;
1126
+ const blended = blendHex(fill.fgColorHex ?? "000000", fill.bgColorHex ?? "FFFFFF", density);
1127
1127
  return blended ? { colorHex: blended } : void 0;
1128
1128
  }
1129
1129
  function blendHex(fg, bg, fraction) {
@@ -162,7 +162,7 @@ function makeColWidthPt(ws) {
162
162
  return (col) => {
163
163
  for (const c of ws.columns) if (col >= c.min - 1 && col <= c.max - 1) return widthPt(c.widthChars);
164
164
  if (ws.defaultColWidthChars !== void 0) return widthPt(ws.defaultColWidthChars);
165
- if (ws.baseColWidthChars !== void 0) return (columnTwips(ws.baseColWidthChars, 105) + 75) / TWIPS_PER_PT;
165
+ if (ws.baseColWidthChars !== void 0) return (columnTwips(ws.baseColWidthChars, 105) + 150) / TWIPS_PER_PT;
166
166
  return 960 / TWIPS_PER_PT;
167
167
  };
168
168
  }
@@ -401,8 +401,16 @@ function withDrawingsByBand(drawings, gridBody, lefts) {
401
401
  const byBand = lefts.map(() => []);
402
402
  for (const el of drawings) {
403
403
  const x = floatLeftPt(el);
404
- const band = x === void 0 ? 0 : bandOf(x);
405
- byBand[band].push(x === void 0 ? el : shiftFloatLeft(el, lefts[band]));
404
+ if (x === void 0) {
405
+ byBand[0].push(el);
406
+ continue;
407
+ }
408
+ const start = bandOf(x);
409
+ const right = x + (floatWidthPt(el) ?? 0);
410
+ for (let band = start; band < lefts.length; band++) {
411
+ if (band > start && right <= lefts[band]) break;
412
+ byBand[band].push(shiftFloatLeft(el, lefts[band]));
413
+ }
406
414
  }
407
415
  const out = [];
408
416
  let seen = 0;
@@ -416,6 +424,12 @@ function withDrawingsByBand(drawings, gridBody, lefts) {
416
424
  function floatLeftPt(el) {
417
425
  return (el.kind === "chart" ? el.chart.float : el.kind === "image" ? el.image.float : el.kind === "shape" ? el.shape.float : void 0)?.posH?.offsetPt;
418
426
  }
427
+ /** A float's own width, for the block kinds a sheet anchors. */
428
+ function floatWidthPt(el) {
429
+ if (el.kind === "chart") return el.chart.width;
430
+ if (el.kind === "image") return el.image.width;
431
+ if (el.kind === "shape") return el.shape.width;
432
+ }
419
433
  /** The same block with its horizontal anchor measured from `by` points later. */
420
434
  function shiftFloatLeft(el, by) {
421
435
  if (by === 0) return el;
@@ -653,7 +653,8 @@ var MIME_BY_FORMAT = {
653
653
  png: "image/png",
654
654
  jpeg2000: "image/jp2",
655
655
  gif: "image/gif",
656
- tiff: "image/tiff"
656
+ tiff: "image/tiff",
657
+ bmp: "image/bmp"
657
658
  };
658
659
  function dataUri(resource, store) {
659
660
  if (resource === void 0) return void 0;
@@ -326,6 +326,32 @@ export interface TextLineItem extends PageItemBase {
326
326
  * the plot's own tick labels. Absent ⇒ horizontal.
327
327
  */
328
328
  readonly rotationDeg?: number;
329
+ /**
330
+ * §20.1.9.10 `a:prstTxWarp` — the line is WordArt: each glyph is placed on
331
+ * its own, bent through the preset's curve and stretched onto the shape's
332
+ * box. Absent ⇒ the line is set flat, one text matrix for the whole of it.
333
+ */
334
+ readonly warp?: TextLineWarp;
335
+ }
336
+ /**
337
+ * What placing a warped line needs: the preset, the shape box the warped block
338
+ * is stretched onto, and the box the UN-warped block occupies — both in the
339
+ * page's top-left frame, so a glyph's position in the second maps straight into
340
+ * the first.
341
+ */
342
+ export interface TextLineWarp {
343
+ readonly preset: string;
344
+ readonly adjust?: number;
345
+ readonly boxX: Pt;
346
+ readonly boxY: Pt;
347
+ readonly boxWidth: Pt;
348
+ readonly boxHeight: Pt;
349
+ /** The un-warped block's left edge and width (its widest line). */
350
+ readonly srcX: Pt;
351
+ readonly srcWidth: Pt;
352
+ /** The un-warped block's ink top and total ink height. */
353
+ readonly srcTop: Pt;
354
+ readonly srcHeight: Pt;
329
355
  }
330
356
  /** One edge of a table-cell frame; `(x, y)` is the cell box's top-left corner. */
331
357
  export interface BorderItem extends PageItemBase {
@@ -422,6 +448,15 @@ export interface PagePaintPlan {
422
448
  * per {@link PageItemBase.pictureId}. They paint where the shape pass does.
423
449
  */
424
450
  readonly pictures: ReadonlyArray<ReadonlyArray<PageItem>>;
451
+ /**
452
+ * §19.3.1 — the items of a page that STATES its own paint order, each run in
453
+ * the order it paints, the runs sorted by the order they were given. A slide
454
+ * is such a page: its shape tree is a z-order, and a picture standing after a
455
+ * rectangle in it is drawn over that rectangle. Split by kind instead — every
456
+ * image, then every shape — the six logos of 54542_cropped_bitmap.pptx were
457
+ * painted first and the six panels they sit on were painted over them.
458
+ */
459
+ readonly ordered: ReadonlyArray<ReadonlyArray<PageItem>>;
425
460
  }
426
461
  export declare function paintPlan(commands: ReadonlyArray<PageItem>): PagePaintPlan;
427
462
  export interface LaidOutDocument {
@@ -7,11 +7,23 @@ function paintPlan(commands) {
7
7
  const lines = [];
8
8
  const pictures = /* @__PURE__ */ new Map();
9
9
  const behind = [];
10
+ const ordered = /* @__PURE__ */ new Map();
11
+ const orderOf = /* @__PURE__ */ new Map();
10
12
  for (const c of commands) {
11
13
  if (c.behind === true) {
12
14
  behind.push(c);
13
15
  continue;
14
16
  }
17
+ if (c.z !== void 0 && c.type !== "line" && c.type !== "fill" && c.type !== "border") {
18
+ const key = c.pictureId !== void 0 ? `p${c.pictureId}` : `i${orderOf.size}`;
19
+ const run = ordered.get(key);
20
+ if (run) run.push(c);
21
+ else {
22
+ ordered.set(key, [c]);
23
+ orderOf.set(key, c.z);
24
+ }
25
+ continue;
26
+ }
15
27
  if (c.pictureId !== void 0) {
16
28
  const group = pictures.get(c.pictureId);
17
29
  if (group) group.push(c);
@@ -44,7 +56,8 @@ function paintPlan(commands) {
44
56
  borders,
45
57
  shapes,
46
58
  lines,
47
- pictures: [...pictures.values()]
59
+ pictures: [...pictures.values()],
60
+ ordered: [...ordered.entries()].sort((a, b) => (orderOf.get(a[0]) ?? 0) - (orderOf.get(b[0]) ?? 0)).map(([, run]) => run)
48
61
  };
49
62
  }
50
63
  function assertNeverPageItem(item) {