reamkit 1.15.4 → 1.16.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 (94) hide show
  1. package/dist/esm/core/converter/facade.d.ts +5 -0
  2. package/dist/esm/core/converter/ream.d.ts +6 -0
  3. package/dist/esm/core/converter/ream.js +34 -8
  4. package/dist/esm/core/document-model/index.d.ts +1 -1
  5. package/dist/esm/core/document-model/types.d.ts +187 -1
  6. package/dist/esm/core/drawingml/chart-geometry.d.ts +28 -0
  7. package/dist/esm/core/drawingml/chart-geometry.js +371 -88
  8. package/dist/esm/core/drawingml/chart-parser.js +262 -11
  9. package/dist/esm/core/drawingml/colors.d.ts +6 -6
  10. package/dist/esm/core/drawingml/colors.js +58 -18
  11. package/dist/esm/core/drawingml/preset-geometry.js +7 -6
  12. package/dist/esm/core/drawingml/sparkline-geometry.js +6 -4
  13. package/dist/esm/core/drawingml/theme-parser.d.ts +41 -0
  14. package/dist/esm/core/drawingml/theme-parser.js +74 -1
  15. package/dist/esm/core/font/ttf-subset.js +26 -1
  16. package/dist/esm/core/images.js +172 -43
  17. package/dist/esm/core/indexed-colors.d.ts +11 -0
  18. package/dist/esm/core/indexed-colors.js +81 -0
  19. package/dist/esm/core/ir/sheet.d.ts +61 -0
  20. package/dist/esm/{excel → core}/number-format.d.ts +37 -0
  21. package/dist/esm/core/number-format.js +800 -0
  22. package/dist/esm/core/opc/alternate-content.d.ts +13 -0
  23. package/dist/esm/core/opc/alternate-content.js +72 -0
  24. package/dist/esm/core/opc/core-properties.js +1 -0
  25. package/dist/esm/core/opc/package.d.ts +14 -1
  26. package/dist/esm/core/opc/package.js +43 -4
  27. package/dist/esm/core/opc/relationships.js +1 -0
  28. package/dist/esm/core/opc/xml-entities.d.ts +12 -0
  29. package/dist/esm/core/opc/xml-entities.js +91 -0
  30. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  31. package/dist/esm/core/spreadsheet-model/types.d.ts +137 -0
  32. package/dist/esm/core/vector.d.ts +13 -0
  33. package/dist/esm/excel/activex-parser.js +1 -0
  34. package/dist/esm/excel/column-bands.d.ts +26 -1
  35. package/dist/esm/excel/column-bands.js +156 -20
  36. package/dist/esm/excel/comments-parser.js +5 -3
  37. package/dist/esm/excel/conditional-format.d.ts +25 -2
  38. package/dist/esm/excel/conditional-format.js +63 -19
  39. package/dist/esm/excel/escaped-text.d.ts +9 -0
  40. package/dist/esm/excel/escaped-text.js +20 -0
  41. package/dist/esm/excel/form-control-parser.js +1 -0
  42. package/dist/esm/excel/formula/dates.js +1 -1
  43. package/dist/esm/excel/header-footer.d.ts +9 -1
  44. package/dist/esm/excel/header-footer.js +162 -14
  45. package/dist/esm/excel/index.d.ts +1 -1
  46. package/dist/esm/excel/index.js +2 -2
  47. package/dist/esm/excel/pivot-table-parser.js +1 -0
  48. package/dist/esm/excel/print-model.d.ts +75 -0
  49. package/dist/esm/excel/print-model.js +898 -76
  50. package/dist/esm/excel/printer-settings.d.ts +16 -0
  51. package/dist/esm/excel/printer-settings.js +36 -0
  52. package/dist/esm/excel/rich-value.d.ts +13 -0
  53. package/dist/esm/excel/rich-value.js +120 -0
  54. package/dist/esm/excel/shared-strings-parser.js +11 -7
  55. package/dist/esm/excel/sheet-drawing.d.ts +11 -0
  56. package/dist/esm/excel/sheet-drawing.js +26 -3
  57. package/dist/esm/excel/sheet-shape-parser.d.ts +53 -1
  58. package/dist/esm/excel/sheet-shape-parser.js +461 -32
  59. package/dist/esm/excel/sheet-to-flow.d.ts +23 -2
  60. package/dist/esm/excel/sheet-to-flow.js +551 -36
  61. package/dist/esm/excel/slicer-parser.js +1 -0
  62. package/dist/esm/excel/styles-parser.d.ts +30 -2
  63. package/dist/esm/excel/styles-parser.js +177 -40
  64. package/dist/esm/excel/table-parser.js +1 -0
  65. package/dist/esm/excel/vml-drawing.d.ts +68 -0
  66. package/dist/esm/excel/vml-drawing.js +158 -0
  67. package/dist/esm/excel/workbook-parser.d.ts +5 -0
  68. package/dist/esm/excel/workbook-parser.js +5 -1
  69. package/dist/esm/excel/worksheet-parser.d.ts +2 -1
  70. package/dist/esm/excel/worksheet-parser.js +150 -61
  71. package/dist/esm/excel/xlsx-reader.js +242 -11
  72. package/dist/esm/excel/xlsx-to-pdf.js +12 -1
  73. package/dist/esm/html/html-writer.js +11 -2
  74. package/dist/esm/layout/page-doc.d.ts +26 -0
  75. package/dist/esm/layout/styled-layout.d.ts +2 -0
  76. package/dist/esm/layout/styled-layout.js +493 -64
  77. package/dist/esm/pdf/cid-font.js +20 -9
  78. package/dist/esm/pdf/objects.d.ts +13 -0
  79. package/dist/esm/pdf/objects.js +18 -1
  80. package/dist/esm/pdf/styled-page-emitter.js +164 -32
  81. package/dist/esm/pdf/text-page-renderer.js +1 -0
  82. package/dist/esm/pdf/vector-graphics.d.ts +3 -1
  83. package/dist/esm/pdf/vector-graphics.js +19 -2
  84. package/dist/esm/pptx/pptx-reader.js +1 -0
  85. package/dist/esm/pptx/slide-parser.js +12 -1
  86. package/dist/esm/svg/svg-writer.js +23 -4
  87. package/dist/esm/word/document-parser.js +1 -0
  88. package/dist/esm/word/drawing-parser.d.ts +34 -1
  89. package/dist/esm/word/drawing-parser.js +52 -2
  90. package/dist/esm/word/numbering-parser.js +1 -0
  91. package/dist/esm/word/settings-parser.js +1 -0
  92. package/dist/esm/word/styles-parser.js +1 -0
  93. package/package.json +7 -3
  94. package/dist/esm/excel/number-format.js +0 -476
@@ -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,78 @@ 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 effect styles (§20.1.4.1.15 `a:effectStyleLst`), as the raw
112
+ * nodes. `<a:effectRef idx="N">` is a 1-based index into this list, exactly as
113
+ * the fill and line references index theirs.
114
+ *
115
+ * @param themeXml The raw theme part bytes, UTF-8.
116
+ * @returns The `a:effectStyle` children in list order; empty when the theme
117
+ * declares no `a:fmtScheme`.
118
+ */
119
+ function parseThemeEffectStyles(themeXml) {
120
+ const list = poFindByPath(parser.parse(decoder.decode(themeXml)), [
121
+ "a:theme",
122
+ "a:themeElements",
123
+ "a:fmtScheme",
124
+ "a:effectStyleLst"
125
+ ]);
126
+ return list ? [...poChildren(list)] : [];
127
+ }
128
+ var EMU_PER_POINT = 12700;
56
129
  function colorOf(slot) {
57
130
  for (const c of poChildren(slot)) if (poIs(c, "a:srgbClr")) {
58
131
  const v = poAttr(c, "val");
@@ -63,4 +136,4 @@ function colorOf(slot) {
63
136
  }
64
137
  }
65
138
  //#endregion
66
- export { parseTheme };
139
+ export { parseTheme, parseThemeEffectStyles, parseThemeFillStyles, parseThemeLineWidths };
@@ -2,6 +2,31 @@
2
2
  var HEAD_CHECKSUM_ADJUSTMENT_OFFSET = 8;
3
3
  var HEAD_INDEX_TO_LOC_FORMAT_OFFSET = 50;
4
4
  var MAGIC_HEAD_CHECKSUM = 2981146554;
5
+ /**
6
+ * The tables a `/FontFile2` may carry (ISO 32000-1 §9.9.2).
7
+ *
8
+ * The spec names `glyf`, `head`, `hhea`, `hmtx`, `loca` and `maxp` as required,
9
+ * `cvt `/`fpgm`/`prep` when the glyphs are hinted, and `cmap` for a SYMBOLIC
10
+ * font. A CIDFontType2 with `/CIDToGIDMap /Identity` is not symbolic in that
11
+ * sense — §9.7.4.2 makes the CID the glyph index and never consults `cmap`, so
12
+ * 9.3 KB of it rode along in every font we embedded. Everything else is dead
13
+ * weight too, in a PDF that does its own positioning and text extraction: we copied `post`, `GPOS`, `GSUB`, `GDEF`, `name`,
14
+ * `hdmx`, `LTSH` and the rest through untouched, and they were 120 KB of the
15
+ * 136 KB font program in 55906-MultiSheetRefs.xlsx. `OS/2` is kept because
16
+ * viewers read its metrics when a descriptor is incomplete.
17
+ */
18
+ var EMBEDDABLE_TABLES = new Set([
19
+ "glyf",
20
+ "head",
21
+ "hhea",
22
+ "hmtx",
23
+ "loca",
24
+ "maxp",
25
+ "cvt ",
26
+ "fpgm",
27
+ "prep",
28
+ "OS/2"
29
+ ]);
5
30
  var COMPOSITE_ARG_1_AND_2_ARE_WORDS = 1;
6
31
  var COMPOSITE_WE_HAVE_A_SCALE = 8;
7
32
  var COMPOSITE_MORE_COMPONENTS = 32;
@@ -115,7 +140,7 @@ function assembleSubsetTtf(parsed, newGlyf, newLoca) {
115
140
  newHead[HEAD_CHECKSUM_ADJUSTMENT_OFFSET + 3] = 0;
116
141
  newHead[HEAD_INDEX_TO_LOC_FORMAT_OFFSET] = 0;
117
142
  newHead[HEAD_INDEX_TO_LOC_FORMAT_OFFSET + 1] = 1;
118
- const orderedTags = [...parsed.tables.keys()];
143
+ const orderedTags = [...parsed.tables.keys()].filter((t) => EMBEDDABLE_TABLES.has(t));
119
144
  const tableData = /* @__PURE__ */ new Map();
120
145
  for (const tag of orderedTags) if (tag === "glyf") tableData.set(tag, newGlyf);
121
146
  else if (tag === "loca") tableData.set(tag, newLocaBytes);
@@ -146,6 +146,8 @@ function decodePng(bytes) {
146
146
  let bitDepth = 0;
147
147
  let colorType = 0;
148
148
  let interlaceMethod = 0;
149
+ let palette;
150
+ let paletteAlpha;
149
151
  const idatChunks = [];
150
152
  let pos = 8;
151
153
  while (pos + 12 <= bytes.length) {
@@ -158,57 +160,161 @@ function decodePng(bytes) {
158
160
  bitDepth = bytes[dataOff + 8];
159
161
  colorType = bytes[dataOff + 9];
160
162
  interlaceMethod = bytes[dataOff + 12];
161
- } else if (type === "IDAT") idatChunks.push(bytes.subarray(dataOff, dataOff + len));
163
+ } else if (type === "PLTE") palette = bytes.subarray(dataOff, dataOff + len);
164
+ else if (type === "tRNS") paletteAlpha = bytes.subarray(dataOff, dataOff + len);
165
+ else if (type === "IDAT") idatChunks.push(bytes.subarray(dataOff, dataOff + len));
162
166
  else if (type === "IEND") break;
163
167
  pos = dataOff + len + 4;
164
168
  }
165
169
  if (width === 0 || height === 0) throw new Error("PNG: missing IHDR");
166
170
  if (idatChunks.length === 0) throw new Error("PNG: no IDAT");
167
- if (interlaceMethod !== 0) throw new Error("PNG: interlaced not supported");
168
- if (bitDepth !== 8) throw new Error(`PNG: bit depth ${bitDepth} not supported`);
169
- const inflated = unzlibSync(concatBytes(idatChunks));
171
+ if (![
172
+ 1,
173
+ 2,
174
+ 4,
175
+ 8,
176
+ 16
177
+ ].includes(bitDepth)) throw new Error(`PNG: bit depth ${bitDepth} not supported`);
170
178
  const channels = pngChannels(colorType);
171
179
  if (channels === 0) throw new Error(`PNG color type ${colorType} not supported`);
172
- const bpp = channels;
173
- const scanlineBytes = width * bpp;
174
- const expected = height * (1 + scanlineBytes);
175
- if (inflated.length !== expected) throw new Error(`PNG: inflated size ${inflated.length} != expected ${expected} (interlaced or malformed)`);
176
- const raw = new Uint8Array(height * scanlineBytes);
177
- let prev = null;
178
- for (let y = 0; y < height; y++) {
179
- const inOff = y * (1 + scanlineBytes);
180
- const filterType = inflated[inOff];
181
- const decoded = new Uint8Array(scanlineBytes);
182
- for (let x = 0; x < scanlineBytes; x++) {
183
- const filt = inflated[inOff + 1 + x];
184
- const a = x < bpp ? 0 : decoded[x - bpp];
185
- const b = prev ? prev[x] : 0;
186
- const c = !prev || x < bpp ? 0 : prev[x - bpp];
187
- let v;
188
- switch (filterType) {
189
- case 0:
190
- v = filt;
191
- break;
192
- case 1:
193
- v = filt + a;
194
- break;
195
- case 2:
196
- v = filt + b;
197
- break;
198
- case 3:
199
- v = filt + Math.floor((a + b) / 2);
200
- break;
201
- case 4:
202
- v = filt + paethPredictor(a, b, c);
203
- break;
204
- default: throw new Error(`PNG: unknown filter type ${filterType}`);
180
+ const raw = decodeSamples(unzlibSync(concatBytes(idatChunks)), width, height, channels, bitDepth, interlaceMethod === 1, colorType !== 3);
181
+ return splitChannels(width, height, colorType, raw, palette, paletteAlpha);
182
+ }
183
+ /**
184
+ * §7.2 / §9 — the image's samples, one byte each, in `width × height ×
185
+ * channels` order: the IDAT stream inflated, each scanline unfiltered against
186
+ * the one before it, and (when the image is interlaced) each of Adam7's seven
187
+ * passes scattered back into the full-size grid.
188
+ *
189
+ * @param inflated The inflated IDAT stream.
190
+ * @param width Image width in pixels.
191
+ * @param height Image height in pixels.
192
+ * @param channels Samples per pixel for the colour type.
193
+ * @param bitDepth Bits per sample (1, 2, 4, 8 or 16).
194
+ * @param interlaced True for interlace method 1 (Adam7).
195
+ * @param scaleToByte Stretch a sub-byte sample to 0…255 — right for an
196
+ * intensity, wrong for a palette index.
197
+ * @returns One byte per sample.
198
+ */
199
+ function decodeSamples(inflated, width, height, channels, bitDepth, interlaced, scaleToByte) {
200
+ const out = new Uint8Array(width * height * channels);
201
+ const passes = interlaced ? ADAM7_PASSES : [{
202
+ xStart: 0,
203
+ yStart: 0,
204
+ xStep: 1,
205
+ yStep: 1
206
+ }];
207
+ const maxSample = (1 << Math.min(bitDepth, 8)) - 1;
208
+ const bpp = Math.max(1, Math.ceil(channels * bitDepth / 8));
209
+ let off = 0;
210
+ for (const p of passes) {
211
+ const pw = Math.ceil((width - p.xStart) / p.xStep);
212
+ const ph = Math.ceil((height - p.yStart) / p.yStep);
213
+ if (pw <= 0 || ph <= 0) continue;
214
+ const lineBytes = Math.ceil(pw * channels * bitDepth / 8);
215
+ let prev = null;
216
+ for (let y = 0; y < ph; y++) {
217
+ if (off + 1 + lineBytes > inflated.length) throw new Error("PNG: truncated image data");
218
+ const filterType = inflated[off];
219
+ const line = unfilterScanline(inflated, off + 1, lineBytes, bpp, filterType, prev);
220
+ off += 1 + lineBytes;
221
+ const destY = p.yStart + y * p.yStep;
222
+ for (let px = 0; px < pw; px++) {
223
+ const destX = p.xStart + px * p.xStep;
224
+ const dest = (destY * width + destX) * channels;
225
+ for (let ch = 0; ch < channels; ch++) {
226
+ const v = sampleAt(line, px * channels + ch, bitDepth);
227
+ out[dest + ch] = scaleToByte && bitDepth < 8 ? Math.round(v * 255 / maxSample) : v;
228
+ }
205
229
  }
206
- decoded[x] = v & 255;
230
+ prev = line;
231
+ }
232
+ }
233
+ return out;
234
+ }
235
+ /**
236
+ * §9.2 — Adam7's seven passes, each a sub-grid of the image: where the pass
237
+ * starts and how far apart its pixels sit.
238
+ */
239
+ var ADAM7_PASSES = [
240
+ {
241
+ xStart: 0,
242
+ yStart: 0,
243
+ xStep: 8,
244
+ yStep: 8
245
+ },
246
+ {
247
+ xStart: 4,
248
+ yStart: 0,
249
+ xStep: 8,
250
+ yStep: 8
251
+ },
252
+ {
253
+ xStart: 0,
254
+ yStart: 4,
255
+ xStep: 4,
256
+ yStep: 8
257
+ },
258
+ {
259
+ xStart: 2,
260
+ yStart: 0,
261
+ xStep: 4,
262
+ yStep: 4
263
+ },
264
+ {
265
+ xStart: 0,
266
+ yStart: 2,
267
+ xStep: 2,
268
+ yStep: 4
269
+ },
270
+ {
271
+ xStart: 1,
272
+ yStart: 0,
273
+ xStep: 2,
274
+ yStep: 2
275
+ },
276
+ {
277
+ xStart: 0,
278
+ yStart: 1,
279
+ xStep: 1,
280
+ yStep: 2
281
+ }
282
+ ];
283
+ function unfilterScanline(src, from, lineBytes, bpp, filterType, prev) {
284
+ const decoded = new Uint8Array(lineBytes);
285
+ for (let x = 0; x < lineBytes; x++) {
286
+ const filt = src[from + x];
287
+ const a = x < bpp ? 0 : decoded[x - bpp];
288
+ const b = prev ? prev[x] : 0;
289
+ const c = !prev || x < bpp ? 0 : prev[x - bpp];
290
+ let v;
291
+ switch (filterType) {
292
+ case 0:
293
+ v = filt;
294
+ break;
295
+ case 1:
296
+ v = filt + a;
297
+ break;
298
+ case 2:
299
+ v = filt + b;
300
+ break;
301
+ case 3:
302
+ v = filt + Math.floor((a + b) / 2);
303
+ break;
304
+ case 4:
305
+ v = filt + paethPredictor(a, b, c);
306
+ break;
307
+ default: throw new Error(`PNG: unknown filter type ${filterType}`);
207
308
  }
208
- raw.set(decoded, y * scanlineBytes);
209
- prev = decoded;
309
+ decoded[x] = v & 255;
210
310
  }
211
- return splitChannels(width, height, colorType, raw);
311
+ return decoded;
312
+ }
313
+ function sampleAt(line, i, bitDepth) {
314
+ if (bitDepth === 8) return line[i];
315
+ if (bitDepth === 16) return line[i * 2];
316
+ const per = 8 / bitDepth;
317
+ return (line[Math.floor(i / per)] ?? 0) >> 8 - bitDepth * (i % per + 1) & (1 << bitDepth) - 1;
212
318
  }
213
319
  function flattenAlphaOnWhite(decoded) {
214
320
  const alpha = decoded.smaskRaw;
@@ -231,7 +337,7 @@ function flattenAlphaOnWhite(decoded) {
231
337
  bitsPerComponent: decoded.bitsPerComponent
232
338
  };
233
339
  }
234
- function splitChannels(width, height, colorType, raw) {
340
+ function splitChannels(width, height, colorType, raw, palette, paletteAlpha) {
235
341
  const pixelCount = width * height;
236
342
  if (colorType === 0) return {
237
343
  width,
@@ -240,6 +346,28 @@ function splitChannels(width, height, colorType, raw) {
240
346
  colorSpace: "DeviceGray",
241
347
  bitsPerComponent: 8
242
348
  };
349
+ if (colorType === 3) {
350
+ if (!palette || palette.length < 3) throw new Error("PNG: palette image without PLTE");
351
+ const entries = Math.floor(palette.length / 3);
352
+ const rgb = new Uint8Array(pixelCount * 3);
353
+ const alpha = paletteAlpha && paletteAlpha.length > 0 ? new Uint8Array(pixelCount) : void 0;
354
+ for (let i = 0; i < pixelCount; i++) {
355
+ const idx = raw[i];
356
+ const src = (idx < entries ? idx : 0) * 3;
357
+ rgb[i * 3] = palette[src];
358
+ rgb[i * 3 + 1] = palette[src + 1];
359
+ rgb[i * 3 + 2] = palette[src + 2];
360
+ if (alpha) alpha[i] = paletteAlpha[idx] ?? 255;
361
+ }
362
+ return {
363
+ width,
364
+ height,
365
+ raw: rgb,
366
+ colorSpace: "DeviceRGB",
367
+ bitsPerComponent: 8,
368
+ ...alpha ? { smaskRaw: alpha } : {}
369
+ };
370
+ }
243
371
  if (colorType === 2) return {
244
372
  width,
245
373
  height,
@@ -281,11 +409,12 @@ function splitChannels(width, height, colorType, raw) {
281
409
  smaskRaw: alpha
282
410
  };
283
411
  }
284
- throw new Error(`PNG color type ${colorType} not supported (palette/indexed?)`);
412
+ throw new Error(`PNG color type ${colorType} not supported`);
285
413
  }
286
414
  function pngChannels(colorType) {
287
415
  if (colorType === 0) return 1;
288
416
  if (colorType === 2) return 3;
417
+ if (colorType === 3) return 1;
289
418
  if (colorType === 4) return 2;
290
419
  if (colorType === 6) return 4;
291
420
  return 0;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * ECMA-376 §18.8.27 — the legacy indexed colour palette a `<color indexed="N">`
3
+ * refers to. It predates `rgb` and theme colours and is still what Excel writes
4
+ * for anything picked from the classic 40-colour dropdown, so a file using it
5
+ * is not unusual: tdf58243.xlsx colours its header cells `indexed="10"`, and
6
+ * ignoring the attribute rendered them black where every other reader shows red.
7
+ *
8
+ * Entries 0-7 are repeated at 8-15 (a quirk of the original palette); 64 and 65
9
+ * are the system foreground and background.
10
+ */
11
+ export declare const INDEXED_COLORS: ReadonlyArray<string>;
@@ -0,0 +1,81 @@
1
+ //#region src/core/indexed-colors.ts
2
+ /**
3
+ * ECMA-376 §18.8.27 — the legacy indexed colour palette a `<color indexed="N">`
4
+ * refers to. It predates `rgb` and theme colours and is still what Excel writes
5
+ * for anything picked from the classic 40-colour dropdown, so a file using it
6
+ * is not unusual: tdf58243.xlsx colours its header cells `indexed="10"`, and
7
+ * ignoring the attribute rendered them black where every other reader shows red.
8
+ *
9
+ * Entries 0-7 are repeated at 8-15 (a quirk of the original palette); 64 and 65
10
+ * are the system foreground and background.
11
+ */
12
+ var INDEXED_COLORS = [
13
+ "000000",
14
+ "FFFFFF",
15
+ "FF0000",
16
+ "00FF00",
17
+ "0000FF",
18
+ "FFFF00",
19
+ "FF00FF",
20
+ "00FFFF",
21
+ "000000",
22
+ "FFFFFF",
23
+ "FF0000",
24
+ "00FF00",
25
+ "0000FF",
26
+ "FFFF00",
27
+ "FF00FF",
28
+ "00FFFF",
29
+ "800000",
30
+ "008000",
31
+ "000080",
32
+ "808000",
33
+ "800080",
34
+ "008080",
35
+ "C0C0C0",
36
+ "808080",
37
+ "9999FF",
38
+ "993366",
39
+ "FFFFCC",
40
+ "CCFFFF",
41
+ "660066",
42
+ "FF8080",
43
+ "0066CC",
44
+ "CCCCFF",
45
+ "000080",
46
+ "FF00FF",
47
+ "FFFF00",
48
+ "00FFFF",
49
+ "800080",
50
+ "800000",
51
+ "008080",
52
+ "0000FF",
53
+ "00CCFF",
54
+ "CCFFFF",
55
+ "CCFFCC",
56
+ "FFFF99",
57
+ "99CCFF",
58
+ "FF99CC",
59
+ "CC99FF",
60
+ "FFCC99",
61
+ "3366FF",
62
+ "33CCCC",
63
+ "99CC00",
64
+ "FFCC00",
65
+ "FF9900",
66
+ "FF6600",
67
+ "666699",
68
+ "969696",
69
+ "003366",
70
+ "339966",
71
+ "003300",
72
+ "333300",
73
+ "993300",
74
+ "993366",
75
+ "333399",
76
+ "333333",
77
+ "000000",
78
+ "FFFFFF"
79
+ ];
80
+ //#endregion
81
+ export { INDEXED_COLORS };
@@ -10,6 +10,9 @@ export interface SheetChartRef {
10
10
  readonly chartPartPath: string;
11
11
  readonly widthPt: number;
12
12
  readonly heightPt: number;
13
+ /** Where the anchor puts it, in points from the grid's top-left. */
14
+ readonly xPt?: number;
15
+ readonly yPt?: number;
13
16
  }
14
17
  /**
15
18
  * §20.5.2.1 xdr:pic — a picture anchored over a sheet's grid, sized from its
@@ -20,6 +23,9 @@ export interface SheetImageRef {
20
23
  readonly resourceId: ResourceId;
21
24
  readonly widthPt: number;
22
25
  readonly heightPt: number;
26
+ /** Where the anchor puts it, in points from the grid's top-left. */
27
+ readonly xPt?: number;
28
+ readonly yPt?: number;
23
29
  }
24
30
  /** One item (label + selection state) in a {@link SheetSlicer}. */
25
31
  export interface SheetSlicerItem {
@@ -73,10 +79,38 @@ export interface SheetComment {
73
79
  * spin/scroll/list controls. Render-only.
74
80
  */
75
81
  export interface SheetFormControl {
82
+ /**
83
+ * §18.3.1.19 `<control name>` — the shape's NAME, an identifier like
84
+ * "CheckBox28". Excel shows it in the name box, never on the page; it is what
85
+ * the after-the-grid listing calls a control that has no geometry.
86
+ */
76
87
  readonly name?: string;
88
+ /**
89
+ * The visible label, from the control's legacy VML `<v:textbox>`. A control
90
+ * drawn in place draws THIS beside its widget — never {@link name}, which is
91
+ * an internal id: 45540_form_Header.xlsx has forty captionless check boxes
92
+ * and printing their names put "CheckBox28" across the form's own text.
93
+ */
94
+ readonly caption?: string;
77
95
  readonly objectType?: string;
78
96
  readonly checked?: boolean;
79
97
  readonly value?: number;
98
+ /** Where the control sits on the sheet, in points from the grid's origin. */
99
+ readonly box?: SheetControlBox;
100
+ /** The caption's declared size, when the shape names one. */
101
+ readonly fontSizePt?: number;
102
+ }
103
+ /**
104
+ * A control's rectangle on the sheet, in points from the grid's top-left — read
105
+ * from its legacy VML shape, the only part that says where a control goes.
106
+ * Absent when the drawing carries no geometry, in which case the control is
107
+ * listed after the grid rather than drawn in place.
108
+ */
109
+ export interface SheetControlBox {
110
+ readonly xPt: number;
111
+ readonly yPt: number;
112
+ readonly widthPt: number;
113
+ readonly heightPt: number;
80
114
  }
81
115
  /**
82
116
  * An ActiveX control resolved against its activeX part (E-SHEET W10): the control
@@ -87,6 +121,8 @@ export interface SheetFormControl {
87
121
  */
88
122
  export interface SheetActiveXControl {
89
123
  readonly type: string;
124
+ /** Where the control sits on the sheet — from the VML shape sharing its `shapeId`. */
125
+ readonly box?: SheetControlBox;
90
126
  /** §18.3.1.19 `<control name>` — the control's identifier, when it came that way. */
91
127
  readonly name?: string;
92
128
  readonly caption?: string;
@@ -97,6 +133,12 @@ export interface SheetActiveXControl {
97
133
  export interface Sheet {
98
134
  /** The sheet (tab) name. */
99
135
  readonly name: string;
136
+ /**
137
+ * §18.2.19 `<sheet @state>` — the tab is `hidden` or `veryHidden`. Carried
138
+ * because a consumer that shows the workbook (the HTML writer) may still want
139
+ * it; the paginated projection skips it, as Excel and LibreOffice do.
140
+ */
141
+ readonly hidden?: boolean;
100
142
  /**
101
143
  * The grid + per-sheet geometry exactly as parsed: cells, columns, rows,
102
144
  * merges, dimensions, pageSetup/printOptions, manual breaks, drawingRelId.
@@ -165,4 +207,23 @@ export interface SheetDoc {
165
207
  readonly resources: ResourceStore;
166
208
  /** Document metadata from docProps/core.xml. */
167
209
  readonly info?: DocumentInfo;
210
+ /**
211
+ * §20.1.6.2 the workbook theme's colour scheme, slot name → 6-hex. Cell
212
+ * colours are resolved against it at parse time; a header/footer's `&K` theme
213
+ * reference is resolved at projection time, which is why the palette travels.
214
+ */
215
+ readonly themePalette?: ReadonlyMap<string, string>;
216
+ /**
217
+ * §18.3.* — how many embedded objects (`<oleObject progId="Package">` and the
218
+ * like) the workbook carries. They print as an icon and a caption in Excel
219
+ * and Calc, both of which live in a metafile; the projection reports them.
220
+ */
221
+ readonly embeddedObjects?: number;
222
+ /**
223
+ * §20.5.2.x — how many anchored pictures are a metafile (WMF / EMF / PICT)
224
+ * rather than a raster. They keep their anchor and their box, and draw
225
+ * nothing: replaying a metafile is a different feature from embedding an
226
+ * image. The projection reports them.
227
+ */
228
+ readonly metafilePictures?: number;
168
229
  }
@@ -13,6 +13,20 @@
13
13
  * @returns The formatted display string.
14
14
  */
15
15
  export declare function applyNumberFormat(rawValue: string, numFmtId: number, customFormats: ReadonlyMap<number, string>, date1904?: boolean): string;
16
+ /**
17
+ * The colour a number format paints its cell in (§18.8.31), as a 6-digit RRGGBB,
18
+ * or undefined when the section that applies declares none.
19
+ *
20
+ * A `[Red]` at the head of a section is part of the format, not decoration: the
21
+ * accounting codes write the negative section as `[Red]-#,##0.00`, and rendering
22
+ * it in the cell's own font colour loses the one signal the format exists for.
23
+ * `[Color 12]` indexes the same legacy palette a `<color indexed>` does.
24
+ *
25
+ * @param rawValue The cell's raw stored value — it selects the section.
26
+ * @param numFmtId The number-format id from the cell's `cellXf`.
27
+ * @param customFormats Custom format codes by id (from `<numFmts>`).
28
+ */
29
+ export declare function numberFormatColorHex(rawValue: string, numFmtId: number, customFormats: ReadonlyMap<number, string>): string | undefined;
16
30
  /**
17
31
  * Excel serial date → JS `Date`. The default 1900 epoch uses 1899-12-30 as day 0
18
32
  * (so serial 1 is 1900-01-01) and inherits the Lotus 1-2-3 leap-year bug: serial 60
@@ -35,3 +49,26 @@ export declare function excelSerialToDate(serial: number, date1904: boolean): Da
35
49
  * @param month0 The 0-indexed month (0 = January).
36
50
  */
37
51
  export declare function excelSerialFromUtcParts(year: number, month0: number, day: number, date1904: boolean): number;
52
+ /**
53
+ * A General number rounded to the decimals that fit `maxChars` characters.
54
+ *
55
+ * General is not a fixed format: a spreadsheet shows as many decimal places as
56
+ * the column has room for and ROUNDS to that, so 4.3900875881221957 in a
57
+ * column eight characters wide reads 4.390088. We rendered every stored digit
58
+ * and let the cell clip it, which turns the same value into 4.390087 — off by
59
+ * one in the last place shown, with nothing to say a digit was cut.
60
+ *
61
+ * The integer part is never dropped: a number too wide even without decimals
62
+ * keeps them all and the cell says so its own way (see hashOnOverflow).
63
+ *
64
+ * `fits` decides, and it must measure in the face this will be DRAWN in rather
65
+ * than count characters against the column's width in the document's unit.
66
+ * Counted that way we produced nine digits for a column that holds nine of
67
+ * Excel's and eight of ours, and the layout clipped the ninth — the very
68
+ * truncation this exists to prevent. Fewer digits than the reference shows is a
69
+ * coarser number; a clipped one is a different number.
70
+ *
71
+ * @param rawValue The cell's stored value.
72
+ * @param fits Whether a rendering will fit the cell.
73
+ */
74
+ export declare function generalToWidth(rawValue: string, fits: (text: string) => boolean): string;