reamkit 1.22.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 (101) 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/converter/ream.d.ts +16 -0
  4. package/dist/esm/core/converter/ream.js +59 -9
  5. package/dist/esm/core/document-model/types.d.ts +55 -0
  6. package/dist/esm/core/drawingml/chart-geometry.js +14 -7
  7. package/dist/esm/core/drawingml/colors.d.ts +4 -2
  8. package/dist/esm/core/drawingml/colors.js +15 -0
  9. package/dist/esm/core/drawingml/diagram/colors.d.ts +52 -0
  10. package/dist/esm/core/drawingml/diagram/colors.js +125 -0
  11. package/dist/esm/core/drawingml/diagram/data-model.d.ts +47 -0
  12. package/dist/esm/core/drawingml/diagram/data-model.js +119 -0
  13. package/dist/esm/core/drawingml/diagram/layout-engine.d.ts +46 -0
  14. package/dist/esm/core/drawingml/diagram/layout-engine.js +948 -0
  15. package/dist/esm/core/drawingml/diagram/run.d.ts +37 -0
  16. package/dist/esm/core/drawingml/diagram/run.js +50 -0
  17. package/dist/esm/core/drawingml/diagram/to-drawing.d.ts +16 -0
  18. package/dist/esm/core/drawingml/diagram/to-drawing.js +86 -0
  19. package/dist/esm/core/drawingml/preset-geometry.js +116 -6
  20. package/dist/esm/core/drawingml/text-warp.d.ts +58 -0
  21. package/dist/esm/core/drawingml/text-warp.js +355 -0
  22. package/dist/esm/core/drawingml/theme-parser.d.ts +38 -0
  23. package/dist/esm/core/drawingml/theme-parser.js +73 -4
  24. package/dist/esm/core/font/measure.d.ts +12 -0
  25. package/dist/esm/core/font/measure.js +36 -0
  26. package/dist/esm/core/fonts/families.d.ts +10 -0
  27. package/dist/esm/core/fonts/families.js +44 -0
  28. package/dist/esm/core/fonts/index.d.ts +3 -2
  29. package/dist/esm/core/fonts/remote-fonts.d.ts +50 -0
  30. package/dist/esm/core/fonts/remote-fonts.js +114 -8
  31. package/dist/esm/core/fonts/scripts.d.ts +26 -0
  32. package/dist/esm/core/fonts/scripts.js +177 -0
  33. package/dist/esm/core/images.d.ts +30 -2
  34. package/dist/esm/core/images.js +121 -5
  35. package/dist/esm/core/metafile/blit.d.ts +33 -0
  36. package/dist/esm/core/metafile/blit.js +67 -0
  37. package/dist/esm/core/metafile/dib.d.ts +61 -0
  38. package/dist/esm/core/metafile/dib.js +205 -0
  39. package/dist/esm/core/metafile/emf.js +280 -38
  40. package/dist/esm/core/metafile/picture.d.ts +61 -1
  41. package/dist/esm/core/metafile/picture.js +70 -1
  42. package/dist/esm/core/metafile/wmf.js +202 -13
  43. package/dist/esm/core/ole/escher-blip.d.ts +12 -0
  44. package/dist/esm/core/ole/escher-blip.js +98 -0
  45. package/dist/esm/{pdf-reader → core}/png-encode.js +1 -1
  46. package/dist/esm/excel/print-model.js +5 -3
  47. package/dist/esm/excel/sheet-drawing.js +1 -1
  48. package/dist/esm/excel/sheet-to-flow.js +16 -2
  49. package/dist/esm/excel/styles-parser.js +1 -1
  50. package/dist/esm/excel/xls/biff-reader.js +53 -18
  51. package/dist/esm/excel/xls/biff-styles.d.ts +5 -2
  52. package/dist/esm/excel/xls/biff-styles.js +9 -7
  53. package/dist/esm/excel/xls/escher.js +90 -24
  54. package/dist/esm/html/html-writer.js +2 -1
  55. package/dist/esm/layout/page-doc.d.ts +74 -0
  56. package/dist/esm/layout/page-doc.js +14 -1
  57. package/dist/esm/layout/styled-layout.d.ts +2 -2
  58. package/dist/esm/layout/styled-layout.js +475 -48
  59. package/dist/esm/pdf/shading.js +8 -6
  60. package/dist/esm/pdf/styled-page-emitter.js +77 -5
  61. package/dist/esm/pdf/vector-graphics.js +1 -1
  62. package/dist/esm/pdf-reader/image-decode.js +1 -1
  63. package/dist/esm/pptx/embedded-fonts.d.ts +31 -0
  64. package/dist/esm/pptx/embedded-fonts.js +104 -0
  65. package/dist/esm/pptx/placeholder-cascade.d.ts +73 -6
  66. package/dist/esm/pptx/placeholder-cascade.js +114 -18
  67. package/dist/esm/pptx/ppt/ppt-reader.js +191 -13
  68. package/dist/esm/pptx/ppt/ppt-text.d.ts +107 -1
  69. package/dist/esm/pptx/ppt/ppt-text.js +873 -144
  70. package/dist/esm/pptx/pptx-reader.d.ts +8 -0
  71. package/dist/esm/pptx/pptx-reader.js +117 -15
  72. package/dist/esm/pptx/preset-table-styles.d.ts +9 -0
  73. package/dist/esm/pptx/preset-table-styles.js +27 -0
  74. package/dist/esm/pptx/slide-parser.d.ts +11 -2
  75. package/dist/esm/pptx/slide-parser.js +203 -44
  76. package/dist/esm/pptx/sp-helpers.d.ts +9 -4
  77. package/dist/esm/pptx/sp-helpers.js +48 -9
  78. package/dist/esm/pptx/table-style.d.ts +16 -1
  79. package/dist/esm/pptx/table-style.js +104 -10
  80. package/dist/esm/svg/svg-writer.js +1 -0
  81. package/dist/esm/word/document-parser.d.ts +11 -2
  82. package/dist/esm/word/document-parser.js +10 -7
  83. package/dist/esm/word/docx-reader.js +32 -16
  84. package/dist/esm/word/docx-to-pdf.d.ts +8 -7
  85. package/dist/esm/word/docx-to-pdf.js +25 -7
  86. package/dist/esm/word/docx-writer.js +4 -0
  87. package/dist/esm/word/drawing-parser.d.ts +20 -0
  88. package/dist/esm/word/drawing-parser.js +67 -22
  89. package/dist/esm/word/numbering-parser.d.ts +2 -1
  90. package/dist/esm/word/numbering-parser.js +6 -6
  91. package/dist/esm/word/paragraph-properties.d.ts +2 -1
  92. package/dist/esm/word/paragraph-properties.js +2 -2
  93. package/dist/esm/word/run-properties.d.ts +5 -2
  94. package/dist/esm/word/run-properties.js +13 -8
  95. package/dist/esm/word/styles-parser.d.ts +2 -1
  96. package/dist/esm/word/styles-parser.js +12 -12
  97. package/dist/esm/word/table-parser.js +6 -3
  98. package/dist/esm/word/theme-fonts.d.ts +10 -0
  99. package/dist/esm/word/theme-fonts.js +28 -0
  100. package/package.json +1 -1
  101. /package/dist/esm/{pdf-reader → core}/png-encode.d.ts +0 -0
@@ -0,0 +1,33 @@
1
+ /** One decoded bitmap: chunky 8-bit samples, plus alpha when the file carries it. */
2
+ export interface DecodedBmp {
3
+ readonly width: number;
4
+ readonly height: number;
5
+ /** Row-major RGB samples, `width * height * 3` long. */
6
+ readonly data: Uint8Array;
7
+ /** One byte per pixel, present only when the file states real transparency. */
8
+ readonly alpha?: Uint8Array;
9
+ /** `biXPelsPerMeter`/`biYPelsPerMeter` turned into pixels per inch. */
10
+ readonly dpiX?: number;
11
+ readonly dpiY?: number;
12
+ }
13
+ /** Whether these bytes open with the `BM` signature of a bitmap FILE. */
14
+ export declare function isBmp(bytes: Uint8Array): boolean;
15
+ /**
16
+ * MS-ODRAW §2.2.28 — a `BlipDIB`'s payload is a bitmap without its 14-byte
17
+ * BITMAPFILEHEADER, which the Escher record makes redundant. Put it back.
18
+ *
19
+ * @param dib The bytes from the DIB header onwards.
20
+ * @returns A complete BMP file, or `undefined` when the header does not read
21
+ * as a bitmap at all.
22
+ */
23
+ export declare function dibToBmp(dib: Uint8Array): Uint8Array | undefined;
24
+ /**
25
+ * Decode a Windows bitmap into 8-bit RGB samples.
26
+ *
27
+ * @param bytes A complete BMP file (`BM` signature included).
28
+ * @returns The samples, their size and any alpha channel.
29
+ * @throws Error when the file is malformed, or carries a bitmap this does not
30
+ * read — a JPEG or PNG smuggled inside a DIB (`BI_JPEG`/`BI_PNG`),
31
+ * which is a whole other file wearing a bitmap header.
32
+ */
33
+ export declare function decodeBmp(bytes: Uint8Array): DecodedBmp;
@@ -0,0 +1,276 @@
1
+ //#region src/core/bmp.ts
2
+ var FILE_HEADER_BYTES = 14;
3
+ var CORE_HEADER_BYTES = 12;
4
+ var INFO_HEADER_BYTES = 40;
5
+ var MAX_PIXELS = 4e7;
6
+ var BI_RGB = 0;
7
+ var BI_RLE8 = 1;
8
+ var BI_RLE4 = 2;
9
+ var BI_BITFIELDS = 3;
10
+ var BI_ALPHABITFIELDS = 6;
11
+ /** Whether these bytes open with the `BM` signature of a bitmap FILE. */
12
+ function isBmp(bytes) {
13
+ return bytes.length >= FILE_HEADER_BYTES && bytes[0] === 66 && bytes[1] === 77;
14
+ }
15
+ /**
16
+ * MS-ODRAW §2.2.28 — a `BlipDIB`'s payload is a bitmap without its 14-byte
17
+ * BITMAPFILEHEADER, which the Escher record makes redundant. Put it back.
18
+ *
19
+ * @param dib The bytes from the DIB header onwards.
20
+ * @returns A complete BMP file, or `undefined` when the header does not read
21
+ * as a bitmap at all.
22
+ */
23
+ function dibToBmp(dib) {
24
+ const head = readDibHeader(dib, 0);
25
+ if (!head) return void 0;
26
+ const offBits = FILE_HEADER_BYTES + head.headerBytes + head.maskBytes + head.paletteBytes;
27
+ const out = new Uint8Array(FILE_HEADER_BYTES + dib.length);
28
+ const view = new DataView(out.buffer);
29
+ out[0] = 66;
30
+ out[1] = 77;
31
+ view.setUint32(2, out.length, true);
32
+ view.setUint32(10, offBits, true);
33
+ out.set(dib, FILE_HEADER_BYTES);
34
+ return out;
35
+ }
36
+ /**
37
+ * Decode a Windows bitmap into 8-bit RGB samples.
38
+ *
39
+ * @param bytes A complete BMP file (`BM` signature included).
40
+ * @returns The samples, their size and any alpha channel.
41
+ * @throws Error when the file is malformed, or carries a bitmap this does not
42
+ * read — a JPEG or PNG smuggled inside a DIB (`BI_JPEG`/`BI_PNG`),
43
+ * which is a whole other file wearing a bitmap header.
44
+ */
45
+ function decodeBmp(bytes) {
46
+ if (!isBmp(bytes)) throw new Error("BMP: not a bitmap");
47
+ const file = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
48
+ const head = readDibHeader(bytes, FILE_HEADER_BYTES);
49
+ if (!head) throw new Error("BMP: unreadable header");
50
+ const { width, height, topDown, bitCount, compression } = head;
51
+ if (width <= 0 || height <= 0) throw new Error("BMP: empty bitmap");
52
+ if (width * height > MAX_PIXELS) throw new Error("BMP: image too large");
53
+ if (compression === 4 || compression === 5) throw new Error("BMP: embedded JPEG/PNG bitmaps are not supported");
54
+ const stated = file.getUint32(10, true);
55
+ const computed = FILE_HEADER_BYTES + head.headerBytes + head.maskBytes + head.paletteBytes;
56
+ const pixelsAt = stated >= computed && stated < bytes.length ? stated : computed;
57
+ const palette = readPalette(bytes, FILE_HEADER_BYTES + head.headerBytes, head);
58
+ const count = width * height;
59
+ const data = new Uint8Array(count * 3);
60
+ const rowOf = (y) => topDown ? y : height - 1 - y;
61
+ if (compression === BI_RLE8 || compression === BI_RLE4) {
62
+ const indices = decodeRle(bytes.subarray(pixelsAt), width, height, compression === BI_RLE4);
63
+ for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
64
+ const at = (rowOf(y) * width + x) * 3;
65
+ const p = (indices[y * width + x] ?? 0) * 3;
66
+ data[at] = palette[p] ?? 0;
67
+ data[at + 1] = palette[p + 1] ?? 0;
68
+ data[at + 2] = palette[p + 2] ?? 0;
69
+ }
70
+ return {
71
+ width,
72
+ height,
73
+ data,
74
+ ...density(head)
75
+ };
76
+ }
77
+ const stride = ((width * bitCount + 31) / 32 | 0) * 4;
78
+ const alpha = head.alphaMask !== 0 ? new Uint8Array(count) : void 0;
79
+ const shifts = {
80
+ r: maskShift(head.redMask),
81
+ g: maskShift(head.greenMask),
82
+ b: maskShift(head.blueMask),
83
+ a: maskShift(head.alphaMask)
84
+ };
85
+ for (let y = 0; y < height; y++) {
86
+ const row = pixelsAt + y * stride;
87
+ if (row + stride > bytes.length + stride) break;
88
+ for (let x = 0; x < width; x++) {
89
+ const at = (rowOf(y) * width + x) * 3;
90
+ if (bitCount <= 8) {
91
+ const p = paletteIndex(bytes, row, x, bitCount) * 3;
92
+ data[at] = palette[p] ?? 0;
93
+ data[at + 1] = palette[p + 1] ?? 0;
94
+ data[at + 2] = palette[p + 2] ?? 0;
95
+ continue;
96
+ }
97
+ if (bitCount === 24) {
98
+ const o = row + x * 3;
99
+ data[at] = bytes[o + 2] ?? 0;
100
+ data[at + 1] = bytes[o + 1] ?? 0;
101
+ data[at + 2] = bytes[o] ?? 0;
102
+ continue;
103
+ }
104
+ const raw = bitCount === 16 ? (bytes[row + x * 2] ?? 0) | (bytes[row + x * 2 + 1] ?? 0) << 8 : ((bytes[row + x * 4] ?? 0) | (bytes[row + x * 4 + 1] ?? 0) << 8 | (bytes[row + x * 4 + 2] ?? 0) << 16 | (bytes[row + x * 4 + 3] ?? 0) << 24) >>> 0;
105
+ data[at] = channel(raw, head.redMask, shifts.r);
106
+ data[at + 1] = channel(raw, head.greenMask, shifts.g);
107
+ data[at + 2] = channel(raw, head.blueMask, shifts.b);
108
+ if (alpha) alpha[rowOf(y) * width + x] = channel(raw, head.alphaMask, shifts.a);
109
+ }
110
+ }
111
+ const opaque = alpha?.every((v) => v === 0) ?? true;
112
+ return {
113
+ width,
114
+ height,
115
+ data,
116
+ ...alpha && !opaque ? { alpha } : {},
117
+ ...density(head)
118
+ };
119
+ }
120
+ function readDibHeader(bytes, at) {
121
+ if (bytes.length < at + 4) return void 0;
122
+ const v = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
123
+ const headerBytes = v.getUint32(at, true);
124
+ const core = headerBytes === CORE_HEADER_BYTES;
125
+ if (!core && headerBytes < INFO_HEADER_BYTES) return void 0;
126
+ if (bytes.length < at + headerBytes) return void 0;
127
+ const width = core ? v.getInt16(at + 4, true) : v.getInt32(at + 4, true);
128
+ const rawHeight = core ? v.getInt16(at + 6, true) : v.getInt32(at + 8, true);
129
+ const planes = core ? v.getUint16(at + 6, true) : v.getUint16(at + 12, true);
130
+ const bitCount = core ? v.getUint16(at + 8, true) : v.getUint16(at + 14, true);
131
+ const compression = core ? BI_RGB : v.getUint32(at + 16, true);
132
+ if (planes !== 1) return void 0;
133
+ if (![
134
+ 1,
135
+ 2,
136
+ 4,
137
+ 8,
138
+ 16,
139
+ 24,
140
+ 32
141
+ ].includes(bitCount)) return void 0;
142
+ if (compression > 6) return void 0;
143
+ if (width <= 0 || rawHeight === 0) return void 0;
144
+ const clrUsed = core ? 0 : v.getUint32(at + 32, true);
145
+ const inHeader = headerBytes >= 52;
146
+ const maskBytes = !inHeader && (compression === BI_BITFIELDS || compression === BI_ALPHABITFIELDS) ? compression === BI_ALPHABITFIELDS ? 16 : 12 : 0;
147
+ const maskAt = inHeader ? at + 40 : at + headerBytes;
148
+ const stated = compression === BI_BITFIELDS || compression === BI_ALPHABITFIELDS || inHeader;
149
+ const readMask = (i) => stated && bytes.length >= maskAt + (i + 1) * 4 ? v.getUint32(maskAt + i * 4, true) : 0;
150
+ const defaults = bitCount === 16 ? [
151
+ 31744,
152
+ 992,
153
+ 31
154
+ ] : [
155
+ 16711680,
156
+ 65280,
157
+ 255
158
+ ];
159
+ const red = readMask(0) || defaults[0];
160
+ const green = readMask(1) || defaults[1];
161
+ const blue = readMask(2) || defaults[2];
162
+ const alphaMask = headerBytes >= 56 && bytes.length >= maskAt + 16 ? v.getUint32(maskAt + 12, true) : 0;
163
+ const paletteEntryBytes = core ? 3 : 4;
164
+ return {
165
+ headerBytes,
166
+ maskBytes,
167
+ paletteBytes: (bitCount <= 8 ? clrUsed || 1 << bitCount : 0) * paletteEntryBytes,
168
+ paletteEntryBytes,
169
+ width,
170
+ height: Math.abs(rawHeight),
171
+ topDown: rawHeight < 0,
172
+ bitCount,
173
+ compression,
174
+ redMask: red,
175
+ greenMask: green,
176
+ blueMask: blue,
177
+ alphaMask,
178
+ pxPerMeterX: core ? 0 : v.getInt32(at + 24, true),
179
+ pxPerMeterY: core ? 0 : v.getInt32(at + 28, true)
180
+ };
181
+ }
182
+ /** The palette as flat RGB triples, whichever entry width the header uses. */
183
+ function readPalette(bytes, at, head) {
184
+ const entries = head.paletteBytes / head.paletteEntryBytes;
185
+ const out = new Uint8Array(entries * 3);
186
+ for (let i = 0; i < entries; i++) {
187
+ const o = at + i * head.paletteEntryBytes;
188
+ out[i * 3] = bytes[o + 2] ?? 0;
189
+ out[i * 3 + 1] = bytes[o + 1] ?? 0;
190
+ out[i * 3 + 2] = bytes[o] ?? 0;
191
+ }
192
+ return out;
193
+ }
194
+ /** The palette index of one pixel of a 1/2/4/8-bit row. */
195
+ function paletteIndex(bytes, row, x, bitCount) {
196
+ if (bitCount === 8) return bytes[row + x] ?? 0;
197
+ const perByte = 8 / bitCount;
198
+ return (bytes[row + (x / perByte | 0)] ?? 0) >> 8 - bitCount * (x % perByte + 1) & (1 << bitCount) - 1;
199
+ }
200
+ /** How far right a mask's field sits, so its bits can be read off. */
201
+ function maskShift(mask) {
202
+ if (mask === 0) return 0;
203
+ let shift = 0;
204
+ while ((mask >>> shift & 1) === 0) shift++;
205
+ return shift;
206
+ }
207
+ /** One channel of a packed pixel, stretched to the full byte range. */
208
+ function channel(raw, mask, shift) {
209
+ if (mask === 0) return 0;
210
+ const width = (mask >>> shift).toString(2).length;
211
+ const value = (raw & mask) >>> shift;
212
+ const max = (1 << width) - 1;
213
+ return max === 0 ? 0 : Math.round(value * 255 / max);
214
+ }
215
+ /** §biXPelsPerMeter — the resolution the bitmap claims, in pixels per inch. */
216
+ function density(head) {
217
+ if (head.pxPerMeterX <= 0 || head.pxPerMeterY <= 0) return {};
218
+ return {
219
+ dpiX: head.pxPerMeterX * .0254,
220
+ dpiY: head.pxPerMeterY * .0254
221
+ };
222
+ }
223
+ /**
224
+ * §BI_RLE8 / §BI_RLE4 — run-length rows of palette indices.
225
+ *
226
+ * Each pair is either a run (a non-zero count and the index to repeat) or an
227
+ * escape (a zero count and a code): 0 ends the row, 1 ends the bitmap, 2 is a
228
+ * delta that skips forward, and anything else is that many literal pixels,
229
+ * padded to a two-byte boundary.
230
+ *
231
+ * @param data The bytes from the first run onwards.
232
+ * @param width The bitmap's width in pixels.
233
+ * @param height Its height in rows.
234
+ * @param four Whether this is the four-bit form, which packs two indices per byte.
235
+ * @returns One palette index per pixel, top row first.
236
+ */
237
+ function decodeRle(data, width, height, four) {
238
+ const out = new Uint8Array(width * height);
239
+ let x = 0;
240
+ let y = 0;
241
+ let at = 0;
242
+ const put = (index) => {
243
+ if (x < width && y < height) out[y * width + x] = index;
244
+ x++;
245
+ };
246
+ while (at + 1 < data.length && y < height) {
247
+ const count = data[at];
248
+ const value = data[at + 1];
249
+ at += 2;
250
+ if (count > 0) {
251
+ for (let i = 0; i < count; i++) put(four ? i % 2 === 0 ? value >> 4 : value & 15 : value);
252
+ continue;
253
+ }
254
+ if (value === 0) {
255
+ x = 0;
256
+ y++;
257
+ continue;
258
+ }
259
+ if (value === 1) break;
260
+ if (value === 2) {
261
+ x += data[at] ?? 0;
262
+ y += data[at + 1] ?? 0;
263
+ at += 2;
264
+ continue;
265
+ }
266
+ const bytesUsed = four ? (value + 1) / 2 | 0 : value;
267
+ for (let i = 0; i < value; i++) {
268
+ const byte = data[at + (four ? i / 2 | 0 : i)] ?? 0;
269
+ put(four ? i % 2 === 0 ? byte >> 4 : byte & 15 : byte);
270
+ }
271
+ at += bytesUsed + bytesUsed % 2;
272
+ }
273
+ return out;
274
+ }
275
+ //#endregion
276
+ export { decodeBmp, dibToBmp, isBmp };
@@ -143,6 +143,22 @@ export declare class Ream {
143
143
  * @returns The font bytes and, for docx, optional per-family registries.
144
144
  */
145
145
  private resolveFonts;
146
+ /**
147
+ * Fetch one face per writing system the document holds text in — the curated
148
+ * families are Latin, and Han, Kana, Hangul, Arabic and the geometric symbols
149
+ * are a notdef box in every one of them.
150
+ *
151
+ * Only the regular weight is fetched: Noto Sans SC is ten megabytes, and a
152
+ * bold run in it is better stroked (see `SyntheticFace`) than downloaded four
153
+ * times over. A face that fails to arrive is a recorded loss, not a throw —
154
+ * the rest of the document still renders.
155
+ *
156
+ * @param into The registry map the run resolver looks in.
157
+ * @param flow The parsed document.
158
+ * @param options The convert options (for the injectable `fetch`).
159
+ * @param losses Where a face that could not be fetched records itself.
160
+ */
161
+ private addScriptFonts;
146
162
  /**
147
163
  * In strict mode, throw {@link ConversionLossError} for the first recorded loss.
148
164
  *
@@ -1,4 +1,5 @@
1
- import { fetchFontSet } from "../fonts/remote-fonts.js";
1
+ import { fetchFontSet, fetchScriptFont, resolveFamilyKey } from "../fonts/remote-fonts.js";
2
+ import { scriptsInFlow } from "../fonts/scripts.js";
2
3
  import { FontRegistry } from "../font/font-registry.js";
3
4
  import { createFontMeasure } from "../font/measure.js";
4
5
  import { ConversionLossError } from "../ir/loss.js";
@@ -12,8 +13,8 @@ import { writeDocx } from "../../word/docx-writer.js";
12
13
  import { writeXlsx } from "../../excel/xlsx-writer.js";
13
14
  import { writeHtml } from "../../html/html-writer.js";
14
15
  import { writeSvg } from "../../svg/svg-writer.js";
15
- import { resolveDocxAutoFonts } from "../../word/docx-to-pdf.js";
16
16
  import { DEFAULT_READERS, resolveFontsViaChain, toFlowDoc } from "./facade.js";
17
+ import { familiesInFlow } from "../fonts/families.js";
17
18
  import { decryptPackage, isEncryptedPackage } from "../crypto/offcrypto.js";
18
19
  //#region src/core/converter/ream.ts
19
20
  /**
@@ -150,7 +151,7 @@ var Ream = class Ream {
150
151
  losses
151
152
  };
152
153
  }
153
- const { fonts, registriesByFamily } = await this.resolveFonts(options, losses);
154
+ const { fonts, registriesByFamily } = await this.resolveFonts(options, losses, flow);
154
155
  const registry = FontRegistry.fromBytes(fonts);
155
156
  const paginated = this.sheet ? projectSheetDoc(this.sheet, {
156
157
  ...options.now ? { now: options.now } : {},
@@ -209,7 +210,7 @@ var Ream = class Ream {
209
210
  * @param losses The mutable loss list a substitution is appended to.
210
211
  * @returns The font bytes and, for docx, optional per-family registries.
211
212
  */
212
- async resolveFonts(options, losses) {
213
+ async resolveFonts(options, losses, flow) {
213
214
  const explicit = options.fonts ?? (options.fontBytes ? { regular: options.fontBytes } : void 0);
214
215
  if (explicit) return { fonts: explicit };
215
216
  if (options.fontProviders && options.fontProviders.length > 0) {
@@ -217,11 +218,60 @@ var Ream = class Ream {
217
218
  if (loss) losses.push(loss);
218
219
  if (fonts) return { fonts };
219
220
  }
220
- if (this.readerId === "docx") return resolveDocxAutoFonts(this.source, options);
221
- return { fonts: await fetchFontSet({
222
- ...options.fontFamily ? { family: options.fontFamily } : {},
223
- ...options.fontFetch ? { fetch: options.fontFetch } : {}
224
- }) };
221
+ const fetchOpt = options.fontFetch ? { fetch: options.fontFetch } : {};
222
+ const keys = options.fontFamily ? new Set([resolveFamilyKey(options.fontFamily)]) : familiesInFlow(flow);
223
+ if (keys.size <= 1 && scriptsInFlow(flow).scripts.size === 0) {
224
+ const family = keys.values().next().value;
225
+ return { fonts: await fetchFontSet({
226
+ ...family ? { family } : {},
227
+ ...fetchOpt
228
+ }) };
229
+ }
230
+ const registriesByFamily = /* @__PURE__ */ new Map();
231
+ let baseBytes;
232
+ for (const key of keys) {
233
+ const bytes = await fetchFontSet({
234
+ family: key,
235
+ ...fetchOpt
236
+ });
237
+ registriesByFamily.set(key, FontRegistry.fromBytes(bytes));
238
+ if (key === "arimo" || !baseBytes) baseBytes = bytes;
239
+ }
240
+ await this.addScriptFonts(registriesByFamily, flow, options, losses);
241
+ return {
242
+ fonts: baseBytes,
243
+ registriesByFamily
244
+ };
245
+ }
246
+ /**
247
+ * Fetch one face per writing system the document holds text in — the curated
248
+ * families are Latin, and Han, Kana, Hangul, Arabic and the geometric symbols
249
+ * are a notdef box in every one of them.
250
+ *
251
+ * Only the regular weight is fetched: Noto Sans SC is ten megabytes, and a
252
+ * bold run in it is better stroked (see `SyntheticFace`) than downloaded four
253
+ * times over. A face that fails to arrive is a recorded loss, not a throw —
254
+ * the rest of the document still renders.
255
+ *
256
+ * @param into The registry map the run resolver looks in.
257
+ * @param flow The parsed document.
258
+ * @param options The convert options (for the injectable `fetch`).
259
+ * @param losses Where a face that could not be fetched records itself.
260
+ */
261
+ async addScriptFonts(into, flow, options, losses) {
262
+ const { scripts } = scriptsInFlow(flow);
263
+ for (const script of scripts) {
264
+ const bytes = await fetchScriptFont(script, options.fontFetch);
265
+ if (!bytes) {
266
+ losses.push({
267
+ feature: "font",
268
+ detail: `no face could be fetched for the ${script} script; its text draws as boxes`,
269
+ severity: "substituted"
270
+ });
271
+ continue;
272
+ }
273
+ into.set(script, FontRegistry.fromBytes(bytes));
274
+ }
225
275
  }
226
276
  /**
227
277
  * In strict mode, throw {@link ConversionLossError} for the first recorded loss.
@@ -787,6 +787,13 @@ export interface CellProperties {
787
787
  * is a word-processor table's default.
788
788
  */
789
789
  readonly verticalAlign?: 'top' | 'center' | 'bottom';
790
+ /**
791
+ * §17.4.71 `w:textDirection` / §21.1.3.17 `a:tcPr@vert` — the cell's text is
792
+ * turned a quarter: `vert` reads top-to-bottom (turned clockwise), `vert270`
793
+ * bottom-to-top. Its lines then run along the cell's HEIGHT and stack across
794
+ * its width, which is what makes a narrow header column readable.
795
+ */
796
+ readonly textDirection?: 'vert' | 'vert270';
790
797
  }
791
798
  /** §17.4.81 `w:trPr` — a table row's properties: height, split/header flags. */
792
799
  export interface RowProperties {
@@ -1025,6 +1032,34 @@ export interface ShapeFill {
1025
1032
  * text box: stretched, such a fill is a blur where it should be a pattern.
1026
1033
  */
1027
1034
  readonly tiled?: boolean;
1035
+ /**
1036
+ * The size ONE copy of a tiled fill occupies, when the shape states it —
1037
+ * MS-ODRAW §2.3.7.11/.12 `fillWidth` / `fillHeight`. Absent, a tile is the
1038
+ * picture at its own resolution, which is the default both formats give.
1039
+ */
1040
+ readonly tileSizePt?: {
1041
+ readonly widthPt: number;
1042
+ readonly heightPt: number;
1043
+ };
1044
+ /**
1045
+ * §20.1.8.58 `a:tile @sx @sy` — how far the picture is scaled BEFORE it is
1046
+ * repeated, as fractions of its own size (1 = 100 %). A texture halved tiles
1047
+ * four times as often, and read as a plain repeat it tiles once.
1048
+ */
1049
+ readonly tileScale?: {
1050
+ readonly sx: number;
1051
+ readonly sy: number;
1052
+ };
1053
+ /**
1054
+ * Where the grid of copies is anchored. DrawingML pins it to the shape's
1055
+ * top-left corner (§20.1.8.58 `a:tile @algn`, which defaults to `tl`); an
1056
+ * MS-ODRAW texture fill (§2.3.7.13 `fillOriginX`/`fillOriginY`, both
1057
+ * defaulting to nothing) centres it on the shape instead. It only shows when
1058
+ * the copies do not divide the box evenly — and it shows most when ONE copy
1059
+ * is larger than the box, where the difference is which part of the picture
1060
+ * is visible at all.
1061
+ */
1062
+ readonly tileFromCentre?: boolean;
1028
1063
  /**
1029
1064
  * §20.1.8.14 `a:blipFill` — the picture painted across the shape's box. A
1030
1065
  * DrawingML picture IS a shape with one of these, which is how a `pic:pic`
@@ -1144,6 +1179,26 @@ export interface ShapeTextBody {
1144
1179
  * WordArt is set at whatever size fills the box it was drawn in.
1145
1180
  */
1146
1181
  readonly fitToBox?: boolean;
1182
+ /**
1183
+ * §20.1.9.10 `a:prstTxWarp` — DrawingML WordArt: the preset curve the text is
1184
+ * bent through, and the `a:avLst` `adj` guide when the file states one. The
1185
+ * warped text does not wrap and is stretched to fill the shape's box, so the
1186
+ * size its runs state stops deciding how large it is drawn. `textNoShape` is
1187
+ * not carried: it is the enumeration's "no warp" member, and a body under it
1188
+ * is an ordinary text box.
1189
+ */
1190
+ readonly warp?: {
1191
+ readonly preset: string;
1192
+ readonly adjust?: number;
1193
+ };
1194
+ /**
1195
+ * §20.1.10.42 `a:normAutofit` — the text SHRINKS to fit the box, and only
1196
+ * shrinks: a size that already fits is left alone. PowerPoint writes the
1197
+ * scale it settled on into `@fontScale` and that is applied at parse; this
1198
+ * is for the text that arrives without one, which is every box of a diagram
1199
+ * laid out from its layout part rather than from a cached drawing.
1200
+ */
1201
+ readonly shrinkToFit?: boolean;
1147
1202
  /**
1148
1203
  * `wps:txbx @id` / `wps:linkedTxbx @id @seq` — the chain of boxes this one
1149
1204
  * belongs to. Text that overruns a box continues in the next of its chain;
@@ -46,7 +46,7 @@ function niceScale(dataMin, dataMax, maxTicks = 6) {
46
46
  const lo = Math.min(dataMin, dataMax);
47
47
  let hi = Math.max(dataMin, dataMax);
48
48
  if (lo === hi) hi = lo + 1;
49
- const step = niceNum(niceNum(hi - lo, false) / Math.max(1, maxTicks - 1), true);
49
+ const step = niceNum((hi - lo) / Math.max(1, maxTicks - 1), true);
50
50
  const headroom = hi > 0 ? hi * 1.05 : hi;
51
51
  return {
52
52
  min: Math.floor(lo / step) * step,
@@ -712,11 +712,17 @@ function autoRange(vals) {
712
712
  return [lo > 0 && lo < 5 / 6 * hi ? 0 : lo, hi];
713
713
  }
714
714
  /**
715
- * Ticks that fit along an axis `extentPt` long (mirrors {@link axisScale}).
716
- * A tick every 32pt is what both references draw: at 24 a 250pt plot carried
717
- * eleven of them (0, 0.5, 1 … 5.5) where both label 0…6 in whole numbers.
715
+ * How many ticks an axis `extentPt` long carries (mirrors {@link axisScale}).
716
+ *
717
+ * A tall axis takes more of them — a fixed six drew 0/100/200/300 down a plot
718
+ * where both references fit 0/50/…/300 — but the appetite is milder than it
719
+ * looks: asked for one every 32pt, a 427pt chart wanted ten, and Excel labels
720
+ * that chart's 0…5 data 0/1/…/6 where we drew half-steps (chart-texture-bg
721
+ * .pptx). One per ~48pt and never more than eight satisfies both, now that
722
+ * {@link niceScale} measures its step against the range the data spans rather
723
+ * than against Heckbert's rounded-up one.
718
724
  */
719
- var tickBudget = (extentPt) => Math.min(10, Math.max(4, Math.round(extentPt / 32)));
725
+ var tickBudget = (extentPt) => Math.min(8, Math.max(4, Math.round(extentPt / 48)));
720
726
  /** Side (points) of the square stamped for a series that names no symbol. */
721
727
  var DEFAULT_MARKER_PT = 4;
722
728
  /**
@@ -869,9 +875,10 @@ function buildPieScene(chart, wPt, hPt, measure) {
869
875
  });
870
876
  const mid = ang + sweep / 2;
871
877
  const pct = Math.round(v / total * 100);
878
+ const own = chart.showValues ? fmtDataLabel(chart, v) : `${pct}%`;
872
879
  const custom = series.pointLabels?.find((l) => l.idx === i)?.text;
873
- if (custom !== void 0 || pct >= 5) labels.push({
874
- text: custom ?? `${pct}%`,
880
+ if (custom !== void 0 || chart.showValues || pct >= 5) labels.push({
881
+ text: custom ?? own,
875
882
  x: cx + Math.cos(mid) * labelR,
876
883
  y: cy + Math.sin(mid) * labelR - 9 / 3,
877
884
  sizePt: 9,
@@ -3,10 +3,12 @@ import { PoNode } from '../po-helpers.js';
3
3
  * A colour transform child (§20.1.2.3): `lumMod`/`lumOff` modulate luminance,
4
4
  * `shade` darkens toward black, `tint` lightens toward white. `val` is normalised
5
5
  * to 0..1 (the XML stores thousandths of a percent). `alpha` composites over white
6
- * (solid fills emit no transparency).
6
+ * (solid fills emit no transparency). `gamma`/`invGamma` carry no value: they
7
+ * shift the colour into the other light and back, which changes what the
8
+ * transforms between them mean.
7
9
  */
8
10
  export interface ColorMod {
9
- readonly kind: 'lumMod' | 'lumOff' | 'shade' | 'tint' | 'alpha' | 'satMod' | 'hueMod' | 'hueOff' | 'satOff';
11
+ readonly kind: 'lumMod' | 'lumOff' | 'shade' | 'tint' | 'alpha' | 'satMod' | 'hueMod' | 'hueOff' | 'satOff' | 'gamma' | 'invGamma';
10
12
  readonly val: number;
11
13
  }
12
14
  /**
@@ -99,6 +99,13 @@ function applyColorMods(hex, mods) {
99
99
  r = toSrgb(toLinear(r) * m.val + (1 - m.val));
100
100
  g = toSrgb(toLinear(g) * m.val + (1 - m.val));
101
101
  b = toSrgb(toLinear(b) * m.val + (1 - m.val));
102
+ } else if (m.kind === "gamma" || m.kind === "invGamma") {
103
+ const shift = m.kind === "gamma" ? toSrgb : toLinear;
104
+ [r, g, b] = [
105
+ shift(r),
106
+ shift(g),
107
+ shift(b)
108
+ ];
102
109
  } else if (m.kind === "lumMod" || m.kind === "lumOff") {
103
110
  const [h, s, l] = rgbToHsl(r, g, b);
104
111
  const l2 = m.kind === "lumMod" ? l * m.val : clamp01(l + m.val);
@@ -230,6 +237,14 @@ function readColorMods(colorNode) {
230
237
  val: v / 1e5
231
238
  });
232
239
  }
240
+ if (poIs(c, "a:gamma")) mods.push({
241
+ kind: "gamma",
242
+ val: 0
243
+ });
244
+ if (poIs(c, "a:invGamma")) mods.push({
245
+ kind: "invGamma",
246
+ val: 0
247
+ });
233
248
  if (poIs(c, "a:hueOff")) {
234
249
  const v = poIntAttr(c, "val");
235
250
  if (v !== void 0) mods.push({
@@ -0,0 +1,52 @@
1
+ import { PoNode } from '../../po-helpers.js';
2
+ /** The colours part, as the drawing writer asks it. */
3
+ export declare class DiagramColors {
4
+ private readonly byLabel;
5
+ constructor(tree: ReadonlyArray<PoNode>);
6
+ /**
7
+ * The `a:solidFill` for a box, or undefined when the part states none.
8
+ *
9
+ * @param label The layout node's `styleLbl`.
10
+ * @param index The box's place in the run of siblings.
11
+ */
12
+ fill(label: string | undefined, index: number): string | undefined;
13
+ /**
14
+ * Whether the part states this label at all.
15
+ *
16
+ * A label it names but gives no fill — `revTx`, the reversed text of a
17
+ * process arrow — is a box with NO fill, which is not the same as a file
18
+ * that ships no colours and falls back to one accent for everything.
19
+ */
20
+ knows(label: string | undefined): boolean;
21
+ /**
22
+ * The `a:ln` for a box's OUTLINE, where the part states one.
23
+ *
24
+ * A follower box is often a near-white wash with the node's own accent drawn
25
+ * round it (`conFgAcc1` is `lt1` at 90% inside an `accent1` line): without the
26
+ * line it is a white rectangle on a white slide and reads as nothing at all.
27
+ *
28
+ * @param label The layout node's `styleLbl`.
29
+ * @param index The box's place in the run of siblings.
30
+ */
31
+ line(label: string | undefined, index: number): string | undefined;
32
+ /** The `a:solidFill` for a box's TEXT, where the part overrides the style. */
33
+ textFill(label: string | undefined, index: number): string | undefined;
34
+ private pick;
35
+ }
36
+ /**
37
+ * §21.4.2.20 `dgm:pt/dgm:spPr` — the formatting the AUTHOR set on one point,
38
+ * which beats whatever the colours part gives that point's style label.
39
+ *
40
+ * A cycle-matrix whose second quarter was recoloured by hand states it here and
41
+ * nowhere else: read only from the colours part, all four quarters came out the
42
+ * one accent the label names, and the orange quarter and the orange outline
43
+ * round its callout both vanished.
44
+ *
45
+ * @param spPr The point's `dgm:spPr`, when it has one.
46
+ * @returns The fill and the outline it overrides, each as DrawingML, when it
47
+ * overrides them.
48
+ */
49
+ export declare function pointPaint(spPr: PoNode | undefined): {
50
+ fill?: string;
51
+ line?: string;
52
+ };