reamkit 1.16.0 → 1.18.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 (76) hide show
  1. package/dist/esm/core/bidi/segments.js +1 -2
  2. package/dist/esm/core/converter/project.js +5 -1
  3. package/dist/esm/core/document-model/index.d.ts +1 -1
  4. package/dist/esm/core/document-model/types.d.ts +526 -8
  5. package/dist/esm/core/drawingml/chart-geometry.d.ts +8 -12
  6. package/dist/esm/core/drawingml/chart-geometry.js +58 -9
  7. package/dist/esm/core/drawingml/chart-parser.js +44 -5
  8. package/dist/esm/core/drawingml/colors.d.ts +1 -1
  9. package/dist/esm/core/drawingml/colors.js +27 -1
  10. package/dist/esm/core/drawingml/preset-geometry.js +233 -0
  11. package/dist/esm/core/drawingml/shape-render.d.ts +12 -0
  12. package/dist/esm/core/drawingml/shape-render.js +116 -3
  13. package/dist/esm/core/drawingml/theme-parser.d.ts +10 -0
  14. package/dist/esm/core/drawingml/theme-parser.js +19 -1
  15. package/dist/esm/core/font/measure.d.ts +5 -0
  16. package/dist/esm/core/font/measure.js +21 -1
  17. package/dist/esm/core/images.d.ts +2 -2
  18. package/dist/esm/core/images.js +219 -0
  19. package/dist/esm/core/ir/flow.d.ts +23 -1
  20. package/dist/esm/core/lzw.d.ts +17 -0
  21. package/dist/esm/core/lzw.js +83 -0
  22. package/dist/esm/core/metafile/emf.d.ts +16 -0
  23. package/dist/esm/core/metafile/emf.js +635 -0
  24. package/dist/esm/core/metafile/picture.d.ts +106 -0
  25. package/dist/esm/core/metafile/picture.js +55 -0
  26. package/dist/esm/core/metafile/wmf.d.ts +9 -0
  27. package/dist/esm/core/metafile/wmf.js +371 -0
  28. package/dist/esm/core/numbering/apply.d.ts +5 -3
  29. package/dist/esm/core/numbering/apply.js +42 -11
  30. package/dist/esm/core/numbering/index.js +2 -0
  31. package/dist/esm/core/numbering/state.d.ts +13 -1
  32. package/dist/esm/core/numbering/state.js +256 -15
  33. package/dist/esm/core/po-helpers.js +16 -1
  34. package/dist/esm/core/style-cascade/resolver.js +66 -11
  35. package/dist/esm/core/style-cascade/table.js +35 -11
  36. package/dist/esm/core/style-cascade/types.d.ts +42 -1
  37. package/dist/esm/core/style-cascade/types.js +4 -0
  38. package/dist/esm/core/tiff.d.ts +20 -0
  39. package/dist/esm/core/tiff.js +339 -0
  40. package/dist/esm/core/vector.d.ts +22 -0
  41. package/dist/esm/excel/print-model.js +4 -2
  42. package/dist/esm/excel/sheet-to-flow.js +10 -8
  43. package/dist/esm/excel/worksheet-parser.js +1 -1
  44. package/dist/esm/html/html-writer.js +20 -5
  45. package/dist/esm/layout/page-doc.d.ts +107 -4
  46. package/dist/esm/layout/styled-layout.d.ts +44 -2
  47. package/dist/esm/layout/styled-layout.js +2330 -283
  48. package/dist/esm/pdf/shading.js +76 -6
  49. package/dist/esm/pdf/styled-page-emitter.js +294 -45
  50. package/dist/esm/pdf/vector-graphics.d.ts +29 -2
  51. package/dist/esm/pdf/vector-graphics.js +96 -12
  52. package/dist/esm/pdf-reader/image-decode.js +7 -75
  53. package/dist/esm/pptx/pptx-reader.js +1 -0
  54. package/dist/esm/pptx/slide-parser.d.ts +21 -4
  55. package/dist/esm/pptx/slide-parser.js +86 -23
  56. package/dist/esm/svg/svg-writer.js +2 -2
  57. package/dist/esm/word/document-parser.d.ts +96 -7
  58. package/dist/esm/word/document-parser.js +540 -116
  59. package/dist/esm/word/docx-reader.js +135 -43
  60. package/dist/esm/word/docx-writer.js +6 -1
  61. package/dist/esm/word/drawing-parser.d.ts +58 -5
  62. package/dist/esm/word/drawing-parser.js +1632 -63
  63. package/dist/esm/word/index.d.ts +4 -4
  64. package/dist/esm/word/numbering-parser.d.ts +2 -1
  65. package/dist/esm/word/numbering-parser.js +107 -21
  66. package/dist/esm/word/paragraph-properties.d.ts +11 -1
  67. package/dist/esm/word/paragraph-properties.js +219 -10
  68. package/dist/esm/word/run-properties.js +45 -1
  69. package/dist/esm/word/settings-parser.d.ts +24 -0
  70. package/dist/esm/word/settings-parser.js +31 -5
  71. package/dist/esm/word/shading.d.ts +11 -0
  72. package/dist/esm/word/shading.js +64 -0
  73. package/dist/esm/word/styles-parser.js +8 -2
  74. package/dist/esm/word/table-parser.d.ts +9 -1
  75. package/dist/esm/word/table-parser.js +182 -30
  76. package/package.json +1 -1
@@ -0,0 +1,20 @@
1
+ /** One decoded TIFF image: 8-bit chunky samples plus an optional alpha plane. */
2
+ export interface DecodedTiff {
3
+ readonly width: number;
4
+ readonly height: number;
5
+ /** Samples per pixel in {@link data} — 1 (grey), 3 (RGB) or 4 (CMYK). */
6
+ readonly channels: 1 | 3 | 4;
7
+ /** Row-major 8-bit samples, `width * height * channels` long. */
8
+ readonly data: Uint8Array;
9
+ /** One byte per pixel when the file carries an alpha channel. */
10
+ readonly alpha?: Uint8Array;
11
+ }
12
+ /** Whether the bytes open with a TIFF header (§2: `II*\0` or `MM\0*`). */
13
+ export declare function isTiff(bytes: Uint8Array): boolean;
14
+ /**
15
+ * Decode the first image of a baseline TIFF into 8-bit chunky samples.
16
+ *
17
+ * @throws Error when the file is malformed or uses a feature outside baseline
18
+ * TIFF (planar separation, JPEG/fax compression, floating-point samples).
19
+ */
20
+ export declare function decodeTiff(bytes: Uint8Array): DecodedTiff;
@@ -0,0 +1,339 @@
1
+ import { lzwDecodeMsb } from "./lzw.js";
2
+ import { unzlibSync } from "fflate";
3
+ //#region src/core/tiff.ts
4
+ var MAX_PIXELS = 4e7;
5
+ var TAG = {
6
+ width: 256,
7
+ height: 257,
8
+ bitsPerSample: 258,
9
+ compression: 259,
10
+ photometric: 262,
11
+ fillOrder: 266,
12
+ stripOffsets: 273,
13
+ samplesPerPixel: 277,
14
+ rowsPerStrip: 278,
15
+ stripByteCounts: 279,
16
+ planarConfig: 284,
17
+ predictor: 317,
18
+ colorMap: 320,
19
+ tileWidth: 322,
20
+ tileLength: 323,
21
+ tileOffsets: 324,
22
+ tileByteCounts: 325,
23
+ extraSamples: 338,
24
+ sampleFormat: 339
25
+ };
26
+ /** Whether the bytes open with a TIFF header (§2: `II*\0` or `MM\0*`). */
27
+ function isTiff(bytes) {
28
+ if (bytes.length < 8) return false;
29
+ const le = bytes[0] === 73 && bytes[1] === 73;
30
+ const be = bytes[0] === 77 && bytes[1] === 77;
31
+ if (!le && !be) return false;
32
+ return (le ? bytes[2] | bytes[3] << 8 : bytes[2] << 8 | bytes[3]) === 42;
33
+ }
34
+ /**
35
+ * Decode the first image of a baseline TIFF into 8-bit chunky samples.
36
+ *
37
+ * @throws Error when the file is malformed or uses a feature outside baseline
38
+ * TIFF (planar separation, JPEG/fax compression, floating-point samples).
39
+ */
40
+ function decodeTiff(bytes) {
41
+ if (!isTiff(bytes)) throw new Error("TIFF: not a TIFF file");
42
+ const le = bytes[0] === 73;
43
+ const r = new Reader(bytes, le);
44
+ const ifd = r.readIfd(r.u32(4));
45
+ const width = num(ifd, TAG.width, 0);
46
+ const height = num(ifd, TAG.height, 0);
47
+ if (width <= 0 || height <= 0) throw new Error("TIFF: no image dimensions");
48
+ if (width * height > MAX_PIXELS) throw new Error("TIFF: image too large to decode");
49
+ const samples = num(ifd, TAG.samplesPerPixel, 1);
50
+ const bitsList = list(ifd, TAG.bitsPerSample, [1]);
51
+ const bits = bitsList[0] ?? 1;
52
+ if (bitsList.some((b) => b !== bits)) throw new Error("TIFF: mixed sample depths");
53
+ if (![
54
+ 1,
55
+ 2,
56
+ 4,
57
+ 8,
58
+ 16
59
+ ].includes(bits)) throw new Error(`TIFF: ${String(bits)}-bit samples`);
60
+ if (num(ifd, TAG.planarConfig, 1) !== 1) throw new Error("TIFF: planar separation");
61
+ if (list(ifd, TAG.sampleFormat, [1]).some((f) => f !== 1 && f !== 2)) throw new Error("TIFF: non-integer samples");
62
+ const photometric = num(ifd, TAG.photometric, samples >= 3 ? 2 : 1);
63
+ const compression = num(ifd, TAG.compression, 1);
64
+ const predictor = num(ifd, TAG.predictor, 1);
65
+ if (predictor !== 1 && predictor !== 2) throw new Error("TIFF: floating-point predictor");
66
+ const blocks = blockLayout(ifd, width, height);
67
+ const bytesPerRow = Math.ceil(blocks.blockWidth * samples * bits / 8);
68
+ const raw = new Uint8Array(width * height * samples * (bits === 16 ? 2 : 1));
69
+ const rawRowBytes = width * samples * (bits === 16 ? 2 : 1);
70
+ const blockBytes = bytesPerRow * blocks.blockHeight;
71
+ for (const b of blocks.blocks) {
72
+ const count = b.byteCount > 0 ? b.byteCount : blockBytes;
73
+ const rows = unpredict(decodeBlock(bytes.subarray(b.offset, b.offset + count), compression, blockBytes), predictor, bytesPerRow, samples, bits, le);
74
+ for (let y = 0; y < blocks.blockHeight; y++) {
75
+ const destY = b.y + y;
76
+ if (destY >= height) break;
77
+ const src = y * bytesPerRow;
78
+ if (src >= rows.length) break;
79
+ const row = expandRow(rows.subarray(src, src + bytesPerRow), bits, blocks.blockWidth * samples);
80
+ const keep = Math.min(row.length, (width - b.x) * samples * (bits === 16 ? 2 : 1));
81
+ raw.set(row.subarray(0, keep), destY * rawRowBytes + b.x * samples * (bits === 16 ? 2 : 1));
82
+ }
83
+ }
84
+ return toChannels(bits === 16 ? highBytes(raw, le) : raw, {
85
+ width,
86
+ height,
87
+ samples,
88
+ bits,
89
+ photometric,
90
+ palette: list(ifd, TAG.colorMap, []),
91
+ extra: list(ifd, TAG.extraSamples, [])
92
+ });
93
+ }
94
+ var TYPE_SIZE = new Map([
95
+ [1, 1],
96
+ [2, 1],
97
+ [3, 2],
98
+ [4, 4],
99
+ [5, 8],
100
+ [6, 1],
101
+ [7, 1],
102
+ [8, 2],
103
+ [9, 4],
104
+ [10, 8],
105
+ [11, 4],
106
+ [12, 8]
107
+ ]);
108
+ var Reader = class {
109
+ constructor(b, le) {
110
+ this.b = b;
111
+ this.le = le;
112
+ }
113
+ u16(o) {
114
+ if (o + 2 > this.b.length) throw new Error("TIFF: truncated");
115
+ return this.le ? this.b[o] | this.b[o + 1] << 8 : this.b[o] << 8 | this.b[o + 1];
116
+ }
117
+ u32(o) {
118
+ if (o + 4 > this.b.length) throw new Error("TIFF: truncated");
119
+ return this.le ? (this.b[o] | this.b[o + 1] << 8 | this.b[o + 2] << 16) + this.b[o + 3] * 16777216 : (this.b[o + 3] | this.b[o + 2] << 8 | this.b[o + 1] << 16) + this.b[o] * 16777216;
120
+ }
121
+ /** §2 — the directory at `offset`: a count, then 12-byte entries. */
122
+ readIfd(offset) {
123
+ const count = this.u16(offset);
124
+ const out = /* @__PURE__ */ new Map();
125
+ for (let i = 0; i < count; i++) {
126
+ const e = offset + 2 + i * 12;
127
+ const tag = this.u16(e);
128
+ const type = this.u16(e + 2);
129
+ const n = this.u32(e + 4);
130
+ const size = TYPE_SIZE.get(type) ?? 0;
131
+ if (size === 0 || n > 1e6) continue;
132
+ const at = size * n <= 4 ? e + 8 : this.u32(e + 8);
133
+ out.set(tag, {
134
+ type,
135
+ count: n,
136
+ values: this.readValues(type, n, at)
137
+ });
138
+ }
139
+ return out;
140
+ }
141
+ readValues(type, count, at) {
142
+ const size = TYPE_SIZE.get(type) ?? 1;
143
+ const out = [];
144
+ for (let i = 0; i < count; i++) {
145
+ const o = at + i * size;
146
+ if (o + size > this.b.length) break;
147
+ switch (type) {
148
+ case 1:
149
+ case 2:
150
+ case 6:
151
+ case 7:
152
+ out.push(this.b[o]);
153
+ break;
154
+ case 3:
155
+ out.push(this.u16(o));
156
+ break;
157
+ case 8: {
158
+ const v = this.u16(o);
159
+ out.push(v > 32767 ? v - 65536 : v);
160
+ break;
161
+ }
162
+ case 4:
163
+ out.push(this.u32(o));
164
+ break;
165
+ case 9: {
166
+ const v = this.u32(o);
167
+ out.push(v > 2147483647 ? v - 4294967296 : v);
168
+ break;
169
+ }
170
+ case 5:
171
+ case 10: {
172
+ const d = this.u32(o + 4);
173
+ out.push(d === 0 ? 0 : this.u32(o) / d);
174
+ break;
175
+ }
176
+ default: out.push(0);
177
+ }
178
+ }
179
+ return out;
180
+ }
181
+ };
182
+ function num(ifd, tag, fallback) {
183
+ return ifd.get(tag)?.values[0] ?? fallback;
184
+ }
185
+ function list(ifd, tag, fallback) {
186
+ const e = ifd.get(tag);
187
+ return e && e.values.length > 0 ? e.values : fallback;
188
+ }
189
+ function blockLayout(ifd, width, height) {
190
+ const tileW = num(ifd, TAG.tileWidth, 0);
191
+ const tileH = num(ifd, TAG.tileLength, 0);
192
+ if (tileW > 0 && tileH > 0) {
193
+ const offsets = list(ifd, TAG.tileOffsets, []);
194
+ const counts = list(ifd, TAG.tileByteCounts, []);
195
+ const across = Math.ceil(width / tileW);
196
+ return {
197
+ blocks: offsets.map((offset, i) => ({
198
+ offset,
199
+ byteCount: counts[i] ?? 0,
200
+ x: i % across * tileW,
201
+ y: Math.floor(i / across) * tileH
202
+ })),
203
+ blockWidth: tileW,
204
+ blockHeight: tileH
205
+ };
206
+ }
207
+ const rows = num(ifd, TAG.rowsPerStrip, height) || height;
208
+ const offsets = list(ifd, TAG.stripOffsets, []);
209
+ const counts = list(ifd, TAG.stripByteCounts, []);
210
+ if (offsets.length === 0) throw new Error("TIFF: no image data");
211
+ return {
212
+ blocks: offsets.map((offset, i) => ({
213
+ offset,
214
+ byteCount: counts[i] ?? 0,
215
+ x: 0,
216
+ y: i * rows
217
+ })),
218
+ blockWidth: width,
219
+ blockHeight: rows
220
+ };
221
+ }
222
+ function decodeBlock(data, compression, expected) {
223
+ switch (compression) {
224
+ case 1: return data;
225
+ case 5: return lzwDecodeMsb(data, { limit: expected });
226
+ case 8:
227
+ case 32946: try {
228
+ return unzlibSync(data);
229
+ } catch {
230
+ throw new Error("TIFF: corrupt Deflate strip");
231
+ }
232
+ case 32773: return packBits(data, expected);
233
+ default: throw new Error(`TIFF: compression ${String(compression)}`);
234
+ }
235
+ }
236
+ function packBits(data, expected) {
237
+ const out = new Uint8Array(expected);
238
+ let o = 0;
239
+ for (let i = 0; i < data.length && o < expected;) {
240
+ const n = data[i++];
241
+ if (n === 128) continue;
242
+ if (n < 128) {
243
+ const run = Math.min(n + 1, data.length - i, expected - o);
244
+ out.set(data.subarray(i, i + run), o);
245
+ i += n + 1;
246
+ o += run;
247
+ } else {
248
+ const byte = data[i++] ?? 0;
249
+ const run = Math.min(257 - n, expected - o);
250
+ out.fill(byte, o, o + run);
251
+ o += run;
252
+ }
253
+ }
254
+ return out.subarray(0, o);
255
+ }
256
+ function unpredict(data, predictor, rowBytes, samples, bits, le) {
257
+ if (predictor !== 2 || rowBytes <= 0) return data;
258
+ if (bits !== 8 && bits !== 16) return data;
259
+ const out = data.slice();
260
+ const rows = Math.floor(out.length / rowBytes);
261
+ if (bits === 8) {
262
+ for (let r = 0; r < rows; r++) {
263
+ const off = r * rowBytes;
264
+ for (let i = samples; i < rowBytes; i++) out[off + i] = out[off + i] + out[off + i - samples] & 255;
265
+ }
266
+ return out;
267
+ }
268
+ const stride = samples * 2;
269
+ const hi = le ? 1 : 0;
270
+ const lo = le ? 0 : 1;
271
+ for (let r = 0; r < rows; r++) {
272
+ const off = r * rowBytes;
273
+ for (let i = stride; i + 1 < rowBytes; i += 2) {
274
+ const prev = out[off + i - stride + hi] << 8 | out[off + i - stride + lo];
275
+ const v = (out[off + i + hi] << 8 | out[off + i + lo]) + prev & 65535;
276
+ out[off + i + hi] = v >> 8;
277
+ out[off + i + lo] = v & 255;
278
+ }
279
+ }
280
+ return out;
281
+ }
282
+ function expandRow(row, bits, samplesInRow) {
283
+ if (bits >= 8) return row;
284
+ const out = new Uint8Array(samplesInRow);
285
+ const max = (1 << bits) - 1;
286
+ for (let i = 0; i < samplesInRow; i++) {
287
+ const bit = i * bits;
288
+ const byte = row[bit >> 3] ?? 0;
289
+ const shift = 8 - bits - (bit & 7);
290
+ out[i] = Math.round((byte >> shift & max) * 255 / max);
291
+ }
292
+ return out;
293
+ }
294
+ function highBytes(raw, le) {
295
+ const out = new Uint8Array(raw.length >> 1);
296
+ const hi = le ? 1 : 0;
297
+ for (let i = 0; i < out.length; i++) out[i] = raw[i * 2 + hi];
298
+ return out;
299
+ }
300
+ function toChannels(eight, info) {
301
+ const pixels = info.width * info.height;
302
+ const { photometric, samples } = info;
303
+ if (photometric === 3) {
304
+ const rgb = new Uint8Array(pixels * 3);
305
+ const entries = info.palette.length / 3;
306
+ for (let i = 0; i < pixels; i++) {
307
+ const idx = eight[i * samples] ?? 0;
308
+ const e = info.bits < 8 ? Math.round(idx * ((1 << info.bits) - 1) / 255) : idx;
309
+ rgb[i * 3] = (info.palette[e] ?? 0) >> 8;
310
+ rgb[i * 3 + 1] = (info.palette[entries + e] ?? 0) >> 8;
311
+ rgb[i * 3 + 2] = (info.palette[entries * 2 + e] ?? 0) >> 8;
312
+ }
313
+ return {
314
+ width: info.width,
315
+ height: info.height,
316
+ channels: 3,
317
+ data: rgb
318
+ };
319
+ }
320
+ const color = photometric === 5 ? 4 : photometric === 2 ? 3 : 1;
321
+ if (samples < color) throw new Error("TIFF: too few samples for its colour");
322
+ const alphaAt = info.extra.length > 0 && samples > color ? color : -1;
323
+ const data = samples === color ? eight.subarray(0, pixels * color) : new Uint8Array(pixels * color);
324
+ const alpha = alphaAt >= 0 ? new Uint8Array(pixels) : void 0;
325
+ if (samples !== color) for (let i = 0; i < pixels; i++) {
326
+ for (let c = 0; c < color; c++) data[i * color + c] = eight[i * samples + c] ?? 0;
327
+ if (alpha) alpha[i] = eight[i * samples + alphaAt] ?? 255;
328
+ }
329
+ if (photometric === 0) for (let i = 0; i < data.length; i++) data[i] = 255 - data[i];
330
+ return {
331
+ width: info.width,
332
+ height: info.height,
333
+ channels: color === 4 ? 4 : color === 3 ? 3 : 1,
334
+ data,
335
+ ...alpha ? { alpha } : {}
336
+ };
337
+ }
338
+ //#endregion
339
+ export { decodeTiff, isTiff };
@@ -60,6 +60,23 @@ export interface ShapeGradient {
60
60
  readonly kind: 'linear' | 'radial';
61
61
  /** Linear only: gradient direction in degrees clockwise (0 = left→right). */
62
62
  readonly angle?: number;
63
+ /**
64
+ * Radial only: where the first stop sits inside the box, as fractions of it
65
+ * (`{x: 0, y: 0}` is the top-left corner). Absent means the middle, which is
66
+ * where DrawingML's own `a:path` puts it; VML aims it at a corner instead
67
+ * (`@focus` with an `@angle`), and fill.docx's page sweep starts in one.
68
+ */
69
+ readonly center?: {
70
+ readonly x: number;
71
+ readonly y: number;
72
+ };
73
+ /**
74
+ * Radial only: the shape of the sweep's contours. Circles by default, which
75
+ * is what DrawingML's `a:path path="circle"` draws; VML's `gradientRadial`
76
+ * grows RECTANGLES out to the box instead, and so does the page background
77
+ * of fill.docx.
78
+ */
79
+ readonly sweep?: 'circle' | 'rect';
63
80
  readonly stops: ReadonlyArray<GradientStop>;
64
81
  }
65
82
  /**
@@ -80,6 +97,11 @@ export interface VectorShape {
80
97
  * over `fillColorHex`.
81
98
  */
82
99
  readonly fillGradient?: ShapeGradient;
100
+ /**
101
+ * §20.1.2.3.1 — the fill's opacity, `0..1`. Absent is opaque. Writers without
102
+ * transparency paint `fillColorHex` at full strength.
103
+ */
104
+ readonly fillAlpha?: number;
83
105
  /** Stroke description. Omitted = no stroke. */
84
106
  readonly stroke?: StrokeStyle;
85
107
  /**
@@ -873,6 +873,7 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
873
873
  const rowHeightTwips = scaled && baseRowProps !== void 0 ? Math.max(1, Math.round(baseRowProps.heightTwips * printScale)) : baseRowProps?.heightTwips;
874
874
  const isTitleRow = print.titleRows !== void 0 && absR >= print.titleRows.startRow && absR <= print.titleRows.endRow;
875
875
  const rowProps = {
876
+ cantSplit: true,
876
877
  ...rowHeightTwips !== void 0 ? {
877
878
  height: twipsToPt(rowHeightTwips),
878
879
  heightRule: baseRowProps?.heightRule ?? "atLeast"
@@ -1237,7 +1238,7 @@ function mapBorderStyle(style) {
1237
1238
  sizeEighthPt: 18
1238
1239
  };
1239
1240
  case "dashed": return {
1240
- style: "dashed",
1241
+ style: "dashSmallGap",
1241
1242
  sizeEighthPt: 6
1242
1243
  };
1243
1244
  case "dashDot": return {
@@ -1736,7 +1737,8 @@ function slicerTable(slicer) {
1736
1737
  };
1737
1738
  const rowProps = {
1738
1739
  height: pt(SLICER_ROW_PT),
1739
- heightRule: "atLeast"
1740
+ heightRule: "atLeast",
1741
+ cantSplit: true
1740
1742
  };
1741
1743
  const rows = [];
1742
1744
  rows.push({
@@ -6,6 +6,14 @@ import { buildHeaderFooterContent } from "./header-footer.js";
6
6
  //#region src/excel/sheet-to-flow.ts
7
7
  var HEADER_REL = "_xlsxHeaderDefault";
8
8
  var FOOTER_REL = "_xlsxFooterDefault";
9
+ var PAGE_BREAK_PARAGRAPH = {
10
+ properties: {
11
+ pageBreakBefore: true,
12
+ spacingLine: pt(0),
13
+ spacingLineRule: "exact"
14
+ },
15
+ runs: []
16
+ };
9
17
  /**
10
18
  * Project a {@link SheetDoc} into a {@link FlowDoc} (E-SHEET SA2): each grid sheet
11
19
  * becomes flow blocks (a table + chart/picture/shape/slicer frames, then comment
@@ -61,10 +69,7 @@ function projectSheetDoc(sheet, options = {}) {
61
69
  sheetSections.push(sheetSection);
62
70
  if (printed > 0) body.push({
63
71
  kind: "paragraph",
64
- paragraph: {
65
- properties: { pageBreakBefore: true },
66
- runs: []
67
- }
72
+ paragraph: PAGE_BREAK_PARAGRAPH
68
73
  });
69
74
  const drawings = [];
70
75
  for (const ref of ws.charts ?? []) drawings.push({
@@ -96,10 +101,7 @@ function projectSheetDoc(sheet, options = {}) {
96
101
  for (let band = 0; band < drawingBands.length; band++) {
97
102
  if (band > 0) drawings.push({
98
103
  kind: "paragraph",
99
- paragraph: {
100
- properties: { pageBreakBefore: true },
101
- runs: []
102
- }
104
+ paragraph: PAGE_BREAK_PARAGRAPH
103
105
  });
104
106
  for (const shape of drawingBands[band]) drawings.push({
105
107
  kind: "shape",
@@ -1,7 +1,7 @@
1
1
  import { INDEXED_COLORS } from "../core/indexed-colors.js";
2
+ import { resolveInternalEntities } from "../core/opc/xml-entities.js";
2
3
  import { parseCellRef } from "./cell-reference.js";
3
4
  import { parseAreaRef } from "./defined-name-ref.js";
4
- import { resolveInternalEntities } from "../core/opc/xml-entities.js";
5
5
  import { decodeXstring } from "./escaped-text.js";
6
6
  import { parseDxf } from "./styles-parser.js";
7
7
  import { XMLParser } from "fast-xml-parser";
@@ -1,8 +1,8 @@
1
1
  import { FEATURES } from "../core/ir/features.js";
2
2
  import { detectImageFormat } from "../core/images.js";
3
+ import { PathBuilder, flipTransform, svgPathData } from "../core/vector.js";
3
4
  import { EMPTY_STYLE_SHEET, resolveParagraphProperties, resolveRunProperties } from "../core/style-cascade/resolver.js";
4
5
  import "../core/style-cascade/index.js";
5
- import { PathBuilder, flipTransform, svgPathData } from "../core/vector.js";
6
6
  import { buildSparkline } from "../core/drawingml/sparkline-geometry.js";
7
7
  import { arcPoint, arcToBeziers } from "../core/arc-to-bezier.js";
8
8
  import { buildChartScene } from "../core/drawingml/chart-geometry.js";
@@ -106,7 +106,11 @@ function collectReferencedAnchors(body) {
106
106
  for (const el of els) if (el.kind === "paragraph") {
107
107
  for (const r of el.paragraph.runs) if (r.anchor !== void 0) out.add(r.anchor);
108
108
  } else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
109
- else if (el.kind === "shape" && el.shape.text) visit(el.shape.text.content);
109
+ else if (el.kind === "shape") visitShape(el.shape);
110
+ };
111
+ const visitShape = (shape) => {
112
+ if (shape.text) visit(shape.text.content);
113
+ for (const child of shape.children ?? []) visitShape(child.shape);
110
114
  };
111
115
  visit(body);
112
116
  return out;
@@ -122,7 +126,11 @@ function collectNoteNumbers(flow) {
122
126
  if (r.commentRef !== void 0 && !comments.has(r.commentRef)) comments.set(r.commentRef, comments.size + 1);
123
127
  }
124
128
  else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
125
- else if (el.kind === "shape" && el.shape.text) visit(el.shape.text.content);
129
+ else if (el.kind === "shape") visitShape(el.shape);
130
+ };
131
+ const visitShape = (shape) => {
132
+ if (shape.text) visit(shape.text.content);
133
+ for (const child of shape.children ?? []) visitShape(child.shape);
126
134
  };
127
135
  visit(flow.body);
128
136
  return {
@@ -274,6 +282,11 @@ function hexToRgbCss(hex) {
274
282
  return `${String(n >> 16 & 255)},${String(n >> 8 & 255)},${String(n & 255)}`;
275
283
  }
276
284
  function emitShapeBlock(out, shape, ctx) {
285
+ const children = shape.children ?? [];
286
+ if (!(children.length > 0 && shape.fill.kind === "none" && shape.line === void 0 && (shape.text?.content.length ?? 0) === 0)) emitOneShape(out, shape, ctx);
287
+ for (const child of children) emitShapeBlock(out, child.shape, ctx);
288
+ }
289
+ function emitOneShape(out, shape, ctx) {
277
290
  const w = shape.width;
278
291
  const h = shape.height;
279
292
  const paths = buildShapePaths(shape.geometry, w, h);
@@ -620,7 +633,7 @@ function pieIconSvg(fill, color, empty) {
620
633
  function pushBorder(css, side, border) {
621
634
  if (!border || border.style === "none") return;
622
635
  const width = border.width ?? .5;
623
- const style = border.style === "double" ? "double" : border.style === "dotted" ? "dotted" : border.style === "dashed" ? "dashed" : "solid";
636
+ const style = border.style === "double" ? "double" : border.style === "dotted" ? "dotted" : border.style === "dashed" || border.style === "dashSmallGap" ? "dashed" : "solid";
624
637
  css.push(`border-${side}:${fmt(width)}pt ${style} #${border.colorHex ?? "000000"}`);
625
638
  }
626
639
  function emitImageBlock(out, image, ctx) {
@@ -638,7 +651,9 @@ function emitImageBlock(out, image, ctx) {
638
651
  var MIME_BY_FORMAT = {
639
652
  jpeg: "image/jpeg",
640
653
  png: "image/png",
641
- jpeg2000: "image/jp2"
654
+ jpeg2000: "image/jp2",
655
+ gif: "image/gif",
656
+ tiff: "image/tiff"
642
657
  };
643
658
  function dataUri(resource, store) {
644
659
  if (resource === void 0) return void 0;