reamkit 1.31.0 → 1.32.1

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 (98) hide show
  1. package/README.md +10 -0
  2. package/dist/esm/core/bidi/index.d.ts +10 -0
  3. package/dist/esm/core/bidi/index.js +18 -2
  4. package/dist/esm/core/converter/facade.js +10 -5
  5. package/dist/esm/core/converter/project.js +1 -0
  6. package/dist/esm/core/converter/ream.js +17 -6
  7. package/dist/esm/core/converter/sheet-images.d.ts +17 -0
  8. package/dist/esm/core/converter/sheet-images.js +33 -0
  9. package/dist/esm/core/crypto/offcrypto.js +56 -12
  10. package/dist/esm/core/document-model/index.d.ts +1 -1
  11. package/dist/esm/core/document-model/types.d.ts +112 -0
  12. package/dist/esm/core/drawingml/chart-geometry.d.ts +36 -13
  13. package/dist/esm/core/drawingml/chart-geometry.js +372 -146
  14. package/dist/esm/core/drawingml/chart-parser.d.ts +19 -0
  15. package/dist/esm/core/drawingml/chart-parser.js +126 -18
  16. package/dist/esm/core/drawingml/colors.d.ts +6 -0
  17. package/dist/esm/core/drawingml/colors.js +23 -3
  18. package/dist/esm/core/drawingml/shape-render.d.ts +3 -1
  19. package/dist/esm/core/drawingml/shape-render.js +4 -2
  20. package/dist/esm/core/font/measure.d.ts +9 -0
  21. package/dist/esm/core/font/measure.js +9 -1
  22. package/dist/esm/core/fonts/families.js +4 -2
  23. package/dist/esm/core/fonts/scripts.js +4 -2
  24. package/dist/esm/core/ir/flow.d.ts +6 -0
  25. package/dist/esm/core/ir/sheet.d.ts +43 -0
  26. package/dist/esm/core/number-format.d.ts +40 -15
  27. package/dist/esm/core/number-format.js +295 -83
  28. package/dist/esm/core/opc/tag-scan.d.ts +15 -0
  29. package/dist/esm/core/opc/tag-scan.js +41 -0
  30. package/dist/esm/core/opc/xml-entities.js +39 -2
  31. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  32. package/dist/esm/core/spreadsheet-model/types.d.ts +96 -0
  33. package/dist/esm/core/style-cascade/resolver.js +4 -2
  34. package/dist/esm/excel/column-bands.d.ts +39 -6
  35. package/dist/esm/excel/column-bands.js +41 -17
  36. package/dist/esm/excel/comments-parser.js +19 -0
  37. package/dist/esm/excel/conditional-format.d.ts +7 -3
  38. package/dist/esm/excel/conditional-format.js +77 -39
  39. package/dist/esm/excel/formula/context.d.ts +18 -0
  40. package/dist/esm/excel/formula/eval.js +51 -0
  41. package/dist/esm/excel/formula/functions.js +37 -14
  42. package/dist/esm/excel/formula/index.d.ts +2 -2
  43. package/dist/esm/excel/formula/lexer.d.ts +1 -1
  44. package/dist/esm/excel/formula/lexer.js +41 -0
  45. package/dist/esm/excel/formula/parser.d.ts +25 -0
  46. package/dist/esm/excel/formula/parser.js +95 -0
  47. package/dist/esm/excel/print-model.d.ts +38 -1
  48. package/dist/esm/excel/print-model.js +536 -83
  49. package/dist/esm/excel/shared-strings-parser.d.ts +9 -0
  50. package/dist/esm/excel/shared-strings-parser.js +11 -1
  51. package/dist/esm/excel/sheet-drawing.d.ts +38 -2
  52. package/dist/esm/excel/sheet-drawing.js +47 -13
  53. package/dist/esm/excel/sheet-shape-parser.d.ts +6 -2
  54. package/dist/esm/excel/sheet-shape-parser.js +18 -6
  55. package/dist/esm/excel/sheet-to-flow.d.ts +17 -0
  56. package/dist/esm/excel/sheet-to-flow.js +434 -47
  57. package/dist/esm/excel/styles-parser.d.ts +9 -0
  58. package/dist/esm/excel/styles-parser.js +80 -26
  59. package/dist/esm/excel/table-parser.js +5 -0
  60. package/dist/esm/excel/table-style-presets.d.ts +26 -0
  61. package/dist/esm/excel/table-style-presets.js +1555 -0
  62. package/dist/esm/excel/table-style.d.ts +13 -0
  63. package/dist/esm/excel/table-style.js +75 -0
  64. package/dist/esm/excel/tint.d.ts +10 -0
  65. package/dist/esm/excel/tint.js +75 -0
  66. package/dist/esm/excel/vml-drawing.d.ts +41 -3
  67. package/dist/esm/excel/vml-drawing.js +144 -11
  68. package/dist/esm/excel/worksheet-parser.js +89 -15
  69. package/dist/esm/excel/xls/biff-reader.js +10 -2
  70. package/dist/esm/excel/xlsx-reader.js +234 -19
  71. package/dist/esm/excel/xlsx-writer.js +8 -7
  72. package/dist/esm/html/html-writer.js +202 -20
  73. package/dist/esm/layout/fit-pages.d.ts +11 -0
  74. package/dist/esm/layout/fit-pages.js +86 -0
  75. package/dist/esm/layout/line-paint.d.ts +58 -0
  76. package/dist/esm/layout/line-paint.js +107 -0
  77. package/dist/esm/layout/page-doc.d.ts +51 -0
  78. package/dist/esm/layout/page-doc.js +141 -1
  79. package/dist/esm/layout/styled-layout.d.ts +7 -0
  80. package/dist/esm/layout/styled-layout.js +275 -52
  81. package/dist/esm/layout/turned-section.js +14 -2
  82. package/dist/esm/markdown/markdown-writer.js +75 -13
  83. package/dist/esm/pdf/styled-page-emitter.js +49 -71
  84. package/dist/esm/pdf/styled-page-renderer.js +1 -1
  85. package/dist/esm/pdf-reader/annot-draw.js +2 -2
  86. package/dist/esm/pdf-reader/annots.js +3 -1
  87. package/dist/esm/pdf-reader/font.js +4 -4
  88. package/dist/esm/pdf-reader/layout.js +1 -1
  89. package/dist/esm/svg/svg-text.d.ts +58 -0
  90. package/dist/esm/svg/svg-text.js +322 -0
  91. package/dist/esm/svg/svg-writer.js +135 -71
  92. package/dist/esm/word/document-parser.js +1 -1
  93. package/dist/esm/word/docx-reader.js +13 -8
  94. package/dist/esm/word/docx-to-pdf.js +3 -3
  95. package/dist/esm/word/docx-writer.js +11 -2
  96. package/dist/esm/word/font-table.js +89 -10
  97. package/dist/esm/word/numbering-parser.js +1 -1
  98. package/package.json +7 -3
@@ -2,6 +2,25 @@ import { Chart } from '../document-model/index.js';
2
2
  import { ResourceId } from '../ir/index.js';
3
3
  import { OpcPackage } from '../opc/index.js';
4
4
  import { ColorResolver } from './colors.js';
5
+ /**
6
+ * The most points a chart keeps, all its series together: as many as a sheet
7
+ * has rows, the longest range a series can name. A chart part states its own
8
+ * point counts — a cache's `c:ptCount`, each point's `idx`, the range of a
9
+ * `c:f` — and taken at their word, a count of two billion in a part a few
10
+ * hundred bytes long asked for a dense array that ran the process out of
11
+ * memory, and one past 2³² − 1 threw.
12
+ */
13
+ export declare const MOST_CHART_POINTS = 1048576;
14
+ /**
15
+ * How many points each of a chart's `seriesCount` series keeps: an even share
16
+ * of {@link MOST_CHART_POINTS}, so that the bound holds for the chart and not
17
+ * series by series — two thousand series, each declaring a sheet's worth of
18
+ * points, are two billion. A series' points past its share are not read.
19
+ *
20
+ * @param seriesCount How many series the chart has.
21
+ * @returns The points each keeps, at least one.
22
+ */
23
+ export declare function pointsPerSeries(seriesCount: number): number;
5
24
  /**
6
25
  * Parse a DrawingML chart part (chart1.xml) into a {@link Chart}, reading the
7
26
  * CACHED data (`c:numCache` / `c:strCache`) rather than the embedded spreadsheet
@@ -25,6 +25,27 @@ var TYPE_OF_TAG = {
25
25
  "c:scatterChart": "scatter"
26
26
  };
27
27
  /**
28
+ * The most points a chart keeps, all its series together: as many as a sheet
29
+ * has rows, the longest range a series can name. A chart part states its own
30
+ * point counts — a cache's `c:ptCount`, each point's `idx`, the range of a
31
+ * `c:f` — and taken at their word, a count of two billion in a part a few
32
+ * hundred bytes long asked for a dense array that ran the process out of
33
+ * memory, and one past 2³² − 1 threw.
34
+ */
35
+ var MOST_CHART_POINTS = 1048576;
36
+ /**
37
+ * How many points each of a chart's `seriesCount` series keeps: an even share
38
+ * of {@link MOST_CHART_POINTS}, so that the bound holds for the chart and not
39
+ * series by series — two thousand series, each declaring a sheet's worth of
40
+ * points, are two billion. A series' points past its share are not read.
41
+ *
42
+ * @param seriesCount How many series the chart has.
43
+ * @returns The points each keeps, at least one.
44
+ */
45
+ function pointsPerSeries(seriesCount) {
46
+ return Math.max(1, Math.floor(MOST_CHART_POINTS / Math.max(1, seriesCount)));
47
+ }
48
+ /**
28
49
  * Parse a DrawingML chart part (chart1.xml) into a {@link Chart}, reading the
29
50
  * CACHED data (`c:numCache` / `c:strCache`) rather than the embedded spreadsheet
30
51
  * — the cache holds the last-computed categories and values, exactly what Word
@@ -48,6 +69,7 @@ function parseChart(chartXml, resolveColor, resolveImage) {
48
69
  const type = group ? TYPE_OF_TAG[poTag(group)] ?? "unknown" : "unknown";
49
70
  const groupAxIds = (g) => poChildren(g).filter((c) => poIs(c, "c:axId")).map((c) => poAttr(c, "val") ?? "");
50
71
  const primaryAxIds = new Set(group ? groupAxIds(group) : []);
72
+ const most = pointsPerSeries(groups.reduce((n, g) => n + poChildren(g).filter((c) => poIs(c, "c:ser")).length, 0));
51
73
  const serNodes = [];
52
74
  const series = [];
53
75
  let secondaryValAxId;
@@ -58,7 +80,7 @@ function parseChart(chartXml, resolveColor, resolveImage) {
58
80
  for (const s of poChildren(g).filter((c) => poIs(c, "c:ser"))) {
59
81
  serNodes.push(s);
60
82
  series.push({
61
- ...parseSeries(s, resolveColor),
83
+ ...parseSeries(s, resolveColor, most),
62
84
  ...groupType === type ? {} : { type: groupType },
63
85
  ...secondary ? { secondaryAxis: true } : {}
64
86
  });
@@ -71,10 +93,13 @@ function parseChart(chartXml, resolveColor, resolveImage) {
71
93
  const secondaryValAxisTitle = secondaryTitleNode ? collectAT(secondaryTitleNode) || "Axis Title" : void 0;
72
94
  let categories = [];
73
95
  let categoriesRef;
96
+ let categoryGroups;
74
97
  for (const s of serNodes) {
75
98
  const cat = poChildren(s).find((c) => poIs(c, "c:cat"));
76
99
  if (cat) {
77
- categories = denseStrings(cat);
100
+ const levels = multiLevelCategories(cat, most);
101
+ categories = levels ? levels.categories : denseStrings(cat, most);
102
+ categoryGroups = levels?.groups;
78
103
  categoriesRef ??= refFormula(cat);
79
104
  break;
80
105
  }
@@ -99,7 +124,12 @@ function parseChart(chartXml, resolveColor, resolveImage) {
99
124
  const gridLine = lineStyleOf(leftAxNode ? poChildren(leftAxNode).find((c) => poIs(c, "c:majorGridlines")) : void 0, resolveColor);
100
125
  const valAxisMin = axisScaling(plotArea, "c:min");
101
126
  const valAxisMax = axisScaling(plotArea, "c:max");
127
+ const valAxisMajorUnit = majorUnitOf(plotArea);
102
128
  const catAxisReversed = axisOrientation(catAxNode) === "maxMin";
129
+ const catAxisCrosses = axisCrossing(catAxNode);
130
+ const valAxisCrosses = axisCrossing(valAxNode);
131
+ const tickLblPos = catAxNode ? poVal(poChildren(catAxNode).find((c) => poIs(c, "c:tickLblPos"))) : void 0;
132
+ const catTickLabelPos = tickLblPos === "low" || tickLblPos === "high" || tickLblPos === "none" ? tickLblPos : void 0;
103
133
  const chartSpace = tree.find((c) => poIs(c, "c:chartSpace"));
104
134
  const spaceSpPr = poChildren(chartSpace).find((c) => poIs(c, "c:spPr"));
105
135
  const frameLine = spaceSpPr ? poChildren(spaceSpPr).find((c) => poIs(c, "a:ln")) : void 0;
@@ -123,6 +153,7 @@ function parseChart(chartXml, resolveColor, resolveImage) {
123
153
  ...Number.isFinite(gapPercent) && gapPercent >= 0 ? { gapPercent } : {},
124
154
  categories,
125
155
  ...categoriesRef ? { categoriesRef } : {},
156
+ ...categoryGroups && categoryGroups.length > 0 ? { categoryGroups } : {},
126
157
  series,
127
158
  hasLegend: legend !== void 0,
128
159
  ...isLegendPos(legendPos) ? { legendPos } : {},
@@ -132,6 +163,9 @@ function parseChart(chartXml, resolveColor, resolveImage) {
132
163
  ...showValues ? { showValues: true } : {},
133
164
  ...catAxisTitle ? { catAxisTitle } : {},
134
165
  ...catAxisReversed ? { catAxisReversed } : {},
166
+ ...catAxisCrosses !== void 0 ? { catAxisCrosses } : {},
167
+ ...valAxisCrosses !== void 0 ? { valAxisCrosses } : {},
168
+ ...catTickLabelPos ? { catTickLabelPos } : {},
135
169
  ...valAxisTitle ? { valAxisTitle } : {},
136
170
  ...secondaryValAxisTitle ? { secondaryValAxisTitle } : {},
137
171
  ...scatterStyle ? { scatterStyle } : {},
@@ -142,6 +176,7 @@ function parseChart(chartXml, resolveColor, resolveImage) {
142
176
  ...gridLine ? { gridLine } : {},
143
177
  ...valAxisMin !== void 0 ? { valAxisMin } : {},
144
178
  ...valAxisMax !== void 0 ? { valAxisMax } : {},
179
+ ...valAxisMajorUnit !== void 0 ? { valAxisMajorUnit } : {},
145
180
  ...frameFillHex ? { frameFillHex } : {},
146
181
  ...frameFillImage ? { frameFillImage } : {},
147
182
  ...frameLineHex ? { frameLineHex } : {},
@@ -152,11 +187,11 @@ function parseChart(chartXml, resolveColor, resolveImage) {
152
187
  ...numberFormat ? { numberFormat } : {}
153
188
  };
154
189
  }
155
- function parseSeries(ser, resolveColor) {
190
+ function parseSeries(ser, resolveColor, most) {
156
191
  const valNode = poChildren(ser).find((c) => poIs(c, "c:val")) ?? poChildren(ser).find((c) => poIs(c, "c:yVal"));
157
- const values = valNode ? denseNumbers(valNode) : [];
192
+ const values = valNode ? denseNumbers(valNode, most) : [];
158
193
  const xValNode = poChildren(ser).find((c) => poIs(c, "c:xVal"));
159
- const xValues = xValNode ? denseNumbers(xValNode) : void 0;
194
+ const xValues = xValNode ? denseNumbers(xValNode, most) : void 0;
160
195
  const name = seriesName(ser);
161
196
  const colorHex = fillColorOf(poChildren(ser).find((c) => poIs(c, "c:spPr")), resolveColor);
162
197
  const pointColors = dataPointColors(ser, resolveColor);
@@ -298,9 +333,16 @@ function fillColorOf(spPr, resolveColor) {
298
333
  const lnFill = ln ? poChildren(ln).find((c) => poIs(c, "a:solidFill")) : void 0;
299
334
  return lnFill ? colorFromSolidFill(lnFill, resolveColor) : void 0;
300
335
  }
336
+ /** The colour elements a chart's fill names a colour by (§20.1.2.3). */
337
+ var CHART_COLOR_NODES = [
338
+ "a:srgbClr",
339
+ "a:schemeClr",
340
+ "a:sysClr",
341
+ "a:prstClr"
342
+ ];
301
343
  function colorFromSolidFill(solid, resolveColor) {
302
344
  for (const c of poChildren(solid)) {
303
- if (!poIs(c, "a:srgbClr") && !poIs(c, "a:schemeClr")) continue;
345
+ if (!CHART_COLOR_NODES.some((name) => poIs(c, name))) continue;
304
346
  if (!poAttr(c, "val")) continue;
305
347
  return resolveColorNode(c, resolveColor);
306
348
  }
@@ -355,7 +397,7 @@ function cachedTitleText(title) {
355
397
  const tx = poChildren(title).find((c) => poIs(c, "c:tx"));
356
398
  const ref = tx ? poChildren(tx).find((c) => poIs(c, "c:strRef")) : void 0;
357
399
  if (!ref) return void 0;
358
- const cached = denseStrings(ref).filter((t) => t.length > 0);
400
+ const cached = denseStrings(ref, MOST_CHART_POINTS).filter((t) => t.length > 0);
359
401
  return cached.length > 0 ? cached.join(" ") : void 0;
360
402
  }
361
403
  function axisTitle(plotArea, axTag) {
@@ -374,10 +416,30 @@ function axisTitle(plotArea, axTag) {
374
416
  * does not carry — those keep the plain numeric render.
375
417
  */
376
418
  /** §21.2.2.134 `c:scaling/c:orientation` — `minMax` (the default) or `maxMin`. */
419
+ /**
420
+ * §21.2.2.33/§21.2.2.34 — where an axis crosses the one it is drawn against:
421
+ * `crossesAt` a value, or `crosses` at that axis's minimum or maximum.
422
+ * undefined for `autoZero`, the default.
423
+ */
424
+ function axisCrossing(ax) {
425
+ if (!ax) return void 0;
426
+ const at = poChildren(ax).find((c) => poIs(c, "c:crossesAt"));
427
+ const value = at ? Number(poAttr(at, "val")) : NaN;
428
+ if (Number.isFinite(value)) return value;
429
+ const crosses = poVal(poChildren(ax).find((c) => poIs(c, "c:crosses")));
430
+ return crosses === "min" || crosses === "max" ? crosses : void 0;
431
+ }
377
432
  function axisOrientation(ax) {
378
433
  const scaling = ax ? poChildren(ax).find((c) => poIs(c, "c:scaling")) : void 0;
379
434
  return scaling ? poVal(poChildren(scaling).find((c) => poIs(c, "c:orientation"))) : void 0;
380
435
  }
436
+ /** §21.2.2.98 `c:valAx/c:majorUnit` — a positive step, or undefined for "auto". */
437
+ function majorUnitOf(plotArea) {
438
+ const ax = poChildren(plotArea).find((c) => poIs(c, "c:valAx"));
439
+ const node = ax ? poChildren(ax).find((c) => poIs(c, "c:majorUnit")) : void 0;
440
+ const v = node ? Number(poAttr(node, "val")) : NaN;
441
+ return Number.isFinite(v) && v > 0 ? v : void 0;
442
+ }
381
443
  function axisScaling(plotArea, tag) {
382
444
  const ax = poChildren(plotArea).find((c) => poIs(c, "c:valAx"));
383
445
  const scaling = ax ? poChildren(ax).find((c) => poIs(c, "c:scaling")) : void 0;
@@ -469,26 +531,72 @@ function ptCountOf(container) {
469
531
  const pc = cache ? poChildren(cache).find((c) => poIs(c, "c:ptCount")) : void 0;
470
532
  return pc ? poIntAttr(pc, "val") ?? 0 : 0;
471
533
  }
472
- function denseLength(container, pts) {
473
- let max = ptCountOf(container);
474
- for (const p of pts) max = Math.max(max, p.idx + 1);
475
- return max;
534
+ /** Whether `n` can index a point: a whole number from zero. */
535
+ var isPointIndex = (n) => Number.isInteger(n) && n >= 0;
536
+ /**
537
+ * How many slots `count` points and those at `indexes` take — the count, or
538
+ * past it the last of them, and never more than `most`. A count or an index
539
+ * that is no whole number (2.5) asked for an array of no possible length and
540
+ * threw; it counts for nothing.
541
+ */
542
+ function denseLength(count, indexes, most) {
543
+ let length = isPointIndex(count) ? count : 0;
544
+ for (const idx of indexes) if (isPointIndex(idx)) length = Math.max(length, idx + 1);
545
+ return Math.min(length, most);
476
546
  }
477
- function denseNumbers(container) {
547
+ /** A cache's values by point index, gaps 0, at most `most` of them. */
548
+ function denseNumbers(container, most) {
478
549
  const pts = readPts(container);
479
- const arr = new Array(denseLength(container, pts)).fill(0);
550
+ const length = denseLength(ptCountOf(container), pts.map((p) => p.idx), most);
551
+ const arr = new Array(length).fill(0);
480
552
  for (const p of pts) {
481
553
  const n = Number(p.v);
482
- if (Number.isFinite(n)) arr[p.idx] = n;
554
+ if (Number.isFinite(n) && isPointIndex(p.idx) && p.idx < length) arr[p.idx] = n;
483
555
  }
484
556
  return arr;
485
557
  }
486
- function denseStrings(container) {
558
+ /** A cache's texts by point index, gaps empty, at most `most` of them. */
559
+ function denseStrings(container, most) {
487
560
  const pts = readPts(container);
488
- const arr = new Array(denseLength(container, pts)).fill("");
489
- for (const p of pts) arr[p.idx] = p.v;
561
+ const length = denseLength(ptCountOf(container), pts.map((p) => p.idx), most);
562
+ const arr = new Array(length).fill("");
563
+ for (const p of pts) if (isPointIndex(p.idx) && p.idx < length) arr[p.idx] = p.v;
490
564
  return arr;
491
565
  }
566
+ /**
567
+ * §21.2.2.115 `c:multiLvlStrCache` — categories labelled on several levels:
568
+ * the first `c:lvl` labels each category, every later one groups them, a
569
+ * group's label standing at the category it starts at. Read as one flat cache
570
+ * it read as none, and an xlsx chart fell back to the cells its reference
571
+ * names, both columns of them in turn: WithChartSheet.xlsx's six bars stood in
572
+ * the first six of sixteen slots under a jumble of years and measure names.
573
+ *
574
+ * @param cat The `c:cat` element.
575
+ * @param most The most categories it keeps (see {@link pointsPerSeries}).
576
+ * @returns The innermost labels and the outer levels' groups, or undefined
577
+ * for a category axis of one level.
578
+ */
579
+ function multiLevelCategories(cat, most) {
580
+ const cache = poFindDescendant(cat, "c:multiLvlStrCache");
581
+ if (!cache) return void 0;
582
+ const count = poIntAttr(poChildren(cache).find((c) => poIs(c, "c:ptCount")) ?? cache, "val") ?? 0;
583
+ const [inner, ...outer] = poChildren(cache).filter((c) => poIs(c, "c:lvl")).map((lvl) => poChildren(lvl).filter((pt) => poIs(pt, "c:pt")).map((pt) => {
584
+ const v = poChildren(pt).find((c) => poIs(c, "c:v"));
585
+ return {
586
+ start: poIntAttr(pt, "idx") ?? 0,
587
+ label: v ? poText(v) : ""
588
+ };
589
+ }));
590
+ if (!inner) return void 0;
591
+ const length = denseLength(count, inner.map((p) => p.start), most);
592
+ const categories = new Array(length).fill("");
593
+ const kept = (p) => isPointIndex(p.start) && p.start < length;
594
+ for (const p of inner) if (kept(p)) categories[p.start] = p.label;
595
+ return {
596
+ categories,
597
+ groups: outer.map((level) => level.filter(kept).sort((a, b) => a.start - b.start))
598
+ };
599
+ }
492
600
  function isLegendPos(v) {
493
601
  return v === "r" || v === "l" || v === "t" || v === "b";
494
602
  }
@@ -565,4 +673,4 @@ var SCATTER_STYLES = new Set([
565
673
  ]);
566
674
  var REL_CHART_COLOR_STYLE = "http://schemas.microsoft.com/office/2011/relationships/chartColorStyle";
567
675
  //#endregion
568
- export { parseChart, withChartColorStyle };
676
+ export { parseChart, pointsPerSeries, withChartColorStyle };
@@ -40,6 +40,12 @@ export declare function applyColorMods(hex: string, mods: ReadonlyArray<ColorMod
40
40
  * scheme slots when a document carries no custom theme part.
41
41
  */
42
42
  export declare const DEFAULT_THEME_PALETTE: ReadonlyMap<string, string>;
43
+ /**
44
+ * The Office 2023 theme's palette — what Excel colours a workbook that carries
45
+ * no theme part with. Its own PDF of theme slots 0…9 in such a workbook
46
+ * (2026-10-01) paints exactly these: accent1 is 156082, not 2013's 4472C4.
47
+ */
48
+ export declare const OFFICE_2023_THEME_PALETTE: ReadonlyMap<string, string>;
43
49
  /**
44
50
  * Which theme slot each `schemeClr` name stands for. Only the text/background
45
51
  * aliases are ever remapped; anything absent resolves under its own name.
@@ -148,6 +148,25 @@ var DEFAULT_THEME_PALETTE = new Map([
148
148
  ["hlink", "0563C1"],
149
149
  ["folHlink", "954F72"]
150
150
  ]);
151
+ /**
152
+ * The Office 2023 theme's palette — what Excel colours a workbook that carries
153
+ * no theme part with. Its own PDF of theme slots 0…9 in such a workbook
154
+ * (2026-10-01) paints exactly these: accent1 is 156082, not 2013's 4472C4.
155
+ */
156
+ var OFFICE_2023_THEME_PALETTE = new Map([
157
+ ["dk1", "000000"],
158
+ ["lt1", "FFFFFF"],
159
+ ["dk2", "0E2841"],
160
+ ["lt2", "E8E8E8"],
161
+ ["accent1", "156082"],
162
+ ["accent2", "E97132"],
163
+ ["accent3", "196B24"],
164
+ ["accent4", "0F9ED5"],
165
+ ["accent5", "A02B93"],
166
+ ["accent6", "4EA72E"],
167
+ ["hlink", "467886"],
168
+ ["folHlink", "96607D"]
169
+ ]);
151
170
  var DEFAULT_SCHEME_ALIAS = {
152
171
  tx1: "dk1",
153
172
  bg1: "lt1",
@@ -284,8 +303,9 @@ var SYSTEM_COLORS = new Map([
284
303
  function resolveColorNode(c, resolveColor) {
285
304
  if (poIs(c, "a:sysClr")) {
286
305
  const last = poAttr(c, "lastClr");
287
- if (last && /^[0-9A-Fa-f]{6}$/.test(last)) return last.toUpperCase();
288
- return SYSTEM_COLORS.get(poAttr(c, "val") ?? "");
306
+ const hex = last && /^[0-9A-Fa-f]{6}$/.test(last) ? last.toUpperCase() : SYSTEM_COLORS.get(poAttr(c, "val") ?? "");
307
+ const sysMods = readColorMods(c);
308
+ return hex !== void 0 && sysMods.length > 0 ? applyColorMods(hex, sysMods) : hex;
289
309
  }
290
310
  if (poIs(c, "a:prstClr")) {
291
311
  const hex = PRESET_COLORS.get((poAttr(c, "val") ?? "").toLowerCase());
@@ -305,4 +325,4 @@ function resolveColorNode(c, resolveColor) {
305
325
  } : raw);
306
326
  }
307
327
  //#endregion
308
- export { DEFAULT_SCHEME_ALIAS, DEFAULT_THEME_PALETTE, applyColorMods, defaultColorResolver, makeColorResolver, placeholderColors, readColorMods, resolveColorNode };
328
+ export { DEFAULT_SCHEME_ALIAS, DEFAULT_THEME_PALETTE, OFFICE_2023_THEME_PALETTE, defaultColorResolver, makeColorResolver, placeholderColors, readColorMods, resolveColorNode };
@@ -30,7 +30,9 @@ export declare function gradientToSolid(gradient: ShapeGradient): string;
30
30
  * An SVG `<linearGradient>` / `<radialGradient>` definition for a gradient fill
31
31
  * (EP16), shared by the SVG and HTML writers. The linear vector is expressed in
32
32
  * `objectBoundingBox` space; the angle is negated because the shape's own path
33
- * transform flips y (local y-up → page y-down).
33
+ * transform flips y (local y-up → page y-down) — and a radial centre, given in
34
+ * fractions down from the box's top, is measured up from its bottom for the
35
+ * same reason.
34
36
  *
35
37
  * @param id The gradient element id (referenced by `fill="url(#id)"`).
36
38
  * @param g The gradient fill.
@@ -51,7 +51,9 @@ function gradientToSolid(gradient) {
51
51
  * An SVG `<linearGradient>` / `<radialGradient>` definition for a gradient fill
52
52
  * (EP16), shared by the SVG and HTML writers. The linear vector is expressed in
53
53
  * `objectBoundingBox` space; the angle is negated because the shape's own path
54
- * transform flips y (local y-up → page y-down).
54
+ * transform flips y (local y-up → page y-down) — and a radial centre, given in
55
+ * fractions down from the box's top, is measured up from its bottom for the
56
+ * same reason.
55
57
  *
56
58
  * @param id The gradient element id (referenced by `fill="url(#id)"`).
57
59
  * @param g The gradient fill.
@@ -64,7 +66,7 @@ function gradientSvgDef(id, g) {
64
66
  const c = g.center;
65
67
  if (!c) return `<radialGradient id="${id}">${stops}</radialGradient>`;
66
68
  const r = Math.SQRT2 / 2;
67
- return `<radialGradient id="${id}" cx="${n(c.x)}" cy="${n(c.y)}" r="${n(r)}">${stops}</radialGradient>`;
69
+ return `<radialGradient id="${id}" cx="${n(c.x)}" cy="${n(1 - c.y)}" r="${n(r)}">${stops}</radialGradient>`;
68
70
  }
69
71
  const rad = -(g.angle ?? 0) * Math.PI / 180;
70
72
  const dx = Math.cos(rad) / 2;
@@ -29,6 +29,15 @@ export interface FontMeasure {
29
29
  * `[…] TJ` array when shaping moved a glyph off its own advance.
30
30
  */
31
31
  readonly showText: (text: string) => string;
32
+ /**
33
+ * The glyphs `text` is drawn with and the advance each one takes, in font
34
+ * units — exactly what {@link showText} shows, kerning and ligatures
35
+ * included — for a writer that places each glyph itself.
36
+ */
37
+ readonly glyphRun: (text: string) => {
38
+ readonly gids: ReadonlyArray<number>;
39
+ readonly advances: ReadonlyArray<number>;
40
+ };
32
41
  }
33
42
  /**
34
43
  * Build a {@link FontMeasure} over a parsed font.
@@ -52,6 +52,13 @@ function createFontMeasure(parsed, kern = true) {
52
52
  if (hex !== "") parts.push(`<${hex}>`);
53
53
  return `[${parts.join(" ")}] TJ`;
54
54
  };
55
+ const glyphRun = (text) => {
56
+ const shaped = shapeText(text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures, kerning, parsed.joiningForms);
57
+ return {
58
+ gids: shaped.gids,
59
+ advances: shaped.advances
60
+ };
61
+ };
55
62
  const glyf = parsed.tables.get("glyf");
56
63
  const glyphInk = (gid) => {
57
64
  if (!glyf || gid < 0 || gid >= parsed.numGlyphs) return void 0;
@@ -92,7 +99,8 @@ function createFontMeasure(parsed, kern = true) {
92
99
  textWidthPt,
93
100
  textInkPt,
94
101
  encodeTextAsCidHex,
95
- showText
102
+ showText,
103
+ glyphRun
96
104
  };
97
105
  }
98
106
  //#endregion
@@ -28,8 +28,10 @@ function familiesInFlow(flow) {
28
28
  for (const el of elements) if (el.kind === "paragraph") {
29
29
  add(el.paragraph.properties.runProperties?.fontFamily?.ascii);
30
30
  for (const run of el.paragraph.runs) add(run.properties.fontFamily?.ascii);
31
- } else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
32
- else if (el.kind === "shape") shapeText(el.shape);
31
+ } else if (el.kind === "table") {
32
+ for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
33
+ visit(el.table.overlay?.drawings ?? []);
34
+ } else if (el.kind === "shape") shapeText(el.shape);
33
35
  };
34
36
  visit(flow.body);
35
37
  for (const band of flow.headersFooters?.values() ?? []) visit(band);
@@ -163,8 +163,10 @@ function scriptsInFlow(flow) {
163
163
  };
164
164
  const visit = (elements) => {
165
165
  for (const el of elements) if (el.kind === "paragraph") for (const run of el.paragraph.runs) read(run.text);
166
- else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
167
- else if (el.kind === "shape") shapeText(el.shape);
166
+ else if (el.kind === "table") {
167
+ for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
168
+ visit(el.table.overlay?.drawings ?? []);
169
+ } else if (el.kind === "shape") shapeText(el.shape);
168
170
  };
169
171
  visit(flow.body);
170
172
  for (const band of flow.headersFooters?.values() ?? []) visit(band);
@@ -155,4 +155,10 @@ export interface FlowDoc {
155
155
  * room.
156
156
  */
157
157
  readonly typesetBy?: 'word';
158
+ /**
159
+ * The document's front floats cover the text under them, as a spreadsheet's
160
+ * drawings cover its cells: a note's box hides the value beside its cell,
161
+ * and a chart the figures it sits on. Absent, the text is painted over them.
162
+ */
163
+ readonly floatsOverText?: true;
158
164
  }
@@ -13,6 +13,11 @@ export interface SheetChartRef {
13
13
  /** Where the anchor puts it, in points from the grid's top-left. */
14
14
  readonly xPt?: number;
15
15
  readonly yPt?: number;
16
+ /**
17
+ * §20.5.2.3 `fPrintsWithSheet="0"` — on the sheet, not on its paper: a
18
+ * window shows it, a print leaves it out.
19
+ */
20
+ readonly screenOnly?: true;
16
21
  }
17
22
  /**
18
23
  * §20.5.2.1 xdr:pic — a picture anchored over a sheet's grid, sized from its
@@ -26,6 +31,11 @@ export interface SheetImageRef {
26
31
  /** Where the anchor puts it, in points from the grid's top-left. */
27
32
  readonly xPt?: number;
28
33
  readonly yPt?: number;
34
+ /**
35
+ * §20.5.2.3 `fPrintsWithSheet="0"` — on the sheet, not on its paper: a
36
+ * window shows it, a print leaves it out.
37
+ */
38
+ readonly screenOnly?: true;
29
39
  }
30
40
  /** One item (label + selection state) in a {@link SheetSlicer}. */
31
41
  export interface SheetSlicerItem {
@@ -70,7 +80,35 @@ export interface SheetComment {
70
80
  readonly ref: string;
71
81
  readonly author?: string;
72
82
  readonly text: string;
83
+ /** The note as written, run by run with each run's own font — the bold "Author:" line included. */
84
+ readonly runs?: ReadonlyArray<SheetRichRun>;
73
85
  readonly threaded: boolean;
86
+ /**
87
+ * The note's box, when its legacy VML shape SHOWS it (`<x:Visible/>`):
88
+ * Excel draws a shown note on the sheet beside its cell, with a line to the
89
+ * cell's corner, and prints it there when `<pageSetup cellComments>` is
90
+ * `asDisplayed`. Absent ⇒ the note is only flagged in its cell.
91
+ */
92
+ readonly shown?: SheetNoteBox;
93
+ }
94
+ /**
95
+ * Where a shown note stands and how it is painted. Distances are in points
96
+ * from the sheet's top-left corner, measured in the file's own column order —
97
+ * a sheet that reads from the right mirrors them with everything else.
98
+ */
99
+ export interface SheetNoteBox {
100
+ readonly xPt: number;
101
+ readonly yPt: number;
102
+ readonly widthPt: number;
103
+ readonly heightPt: number;
104
+ /** The top-right corner of the cell (or merge) the note belongs to — its line ends there. */
105
+ readonly cornerXPt: number;
106
+ readonly cornerYPt: number;
107
+ readonly fillHex: string;
108
+ readonly lineHex: string;
109
+ readonly shadow: boolean;
110
+ /** The side the note's text is set to; absent ⇒ its start. */
111
+ readonly textAlign?: 'left' | 'center' | 'right';
74
112
  }
75
113
  /**
76
114
  * A form control resolved against its ctrlProp part (E-SHEET W8): the display
@@ -150,6 +188,11 @@ export interface Sheet {
150
188
  readonly images?: ReadonlyArray<SheetImageRef>;
151
189
  /** Drawing shapes on this sheet (E-SHEET W2), fully resolved + anchor-ordered. */
152
190
  readonly shapes?: ReadonlyArray<ShapeBlock>;
191
+ /**
192
+ * The indexes into {@link shapes} of those that do not print with the sheet
193
+ * (§20.5.2.3 `fPrintsWithSheet="0"`): a window shows them, paper does not.
194
+ */
195
+ readonly screenOnlyShapes?: ReadonlySet<number>;
153
196
  /**
154
197
  * Cell hyperlinks resolved to external URLs (E-SHEET W3); the projection sets
155
198
  * `run.href` on covered cells. In-workbook (location-only) links are not carried.
@@ -50,25 +50,50 @@ export declare function excelSerialToDate(serial: number, date1904: boolean): Da
50
50
  */
51
51
  export declare function excelSerialFromUtcParts(year: number, month0: number, day: number, date1904: boolean): number;
52
52
  /**
53
- * A General number rounded to the decimals that fit `maxChars` characters.
53
+ * A General number in its column: the most precise of the ways General may
54
+ * write it ({@link generalRenderings}) that `fits`.
54
55
  *
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.
56
+ * General is not a fixed format: a spreadsheet shows as many places as the
57
+ * column has room for and ROUNDS to that, so 4.3900875881221957 in a column
58
+ * eight characters wide reads 4.390088. We rendered every stored digit and let
59
+ * the cell clip it, which turns the same value into 4.390087 — off by one in
60
+ * the last place shown, with nothing to say a digit was cut. A number too wide
61
+ * for every way of writing it comes back in its finest, and the cell fills
62
+ * with `#` (CellProperties.hashOnOverflow); one under 1 always fits as `0` or
63
+ * `-0`, which is what Excel prints in a column too narrow for anything else.
60
64
  *
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.
65
+ * `fits` decides whether a rendering has room. The print model asks it in the
66
+ * document's own terms — the column's width less the padding it carries, in
67
+ * the font's widest digits — because that is the room Excel gives up places
68
+ * against, whatever face the page is drawn in.
70
69
  *
71
70
  * @param rawValue The cell's stored value.
72
71
  * @param fits Whether a rendering will fit the cell.
73
72
  */
74
73
  export declare function generalToWidth(rawValue: string, fits: (text: string) => boolean): string;
74
+ /**
75
+ * Every way General may write `value` in `chars` characters, the most precise
76
+ * first. One rule, measured in Excel's own PDF on chart axes, which give it
77
+ * nine characters with the sign among them, and in cells, eleven with the
78
+ * sign apart (2026-10-01/02):
79
+ *
80
+ * - the number as it is, its first 15 significant digits, where that fits:
81
+ * 0.000012345, 0.0000003, 12345678901;
82
+ * - else rounded to fewer places or written in scientific notation, whichever
83
+ * keeps the finer last digit — 0.123456789, not 1.23457E-01; 1.23457E-10,
84
+ * not 0; 4E-04, not 0, in a column five characters wide — and of two as
85
+ * fine, the plain one down to 0.0001 and the scientific one below it: 0.0001
86
+ * for 0.000123456 but 1E-05 for 1.00049E-05;
87
+ * - a whole part longer than the room only in scientific notation, and never
88
+ * with the zeros a mantissa or a fraction ends in: 1E+10, 2.5E+12.
89
+ *
90
+ * The digits are the 15 of {@link decimalOf}, rounded half away from zero, so
91
+ * 1.2345E-05 is 1.235E-05, where `toExponential` rounded the binary double
92
+ * down to 1.234E-05.
93
+ *
94
+ * @param value The number.
95
+ * @param chars How many characters General has.
96
+ * @param signCounts Whether a minus sign takes one of them.
97
+ * @returns The renderings, sign included, finest first.
98
+ */
99
+ export declare function generalRenderings(value: number, chars: number, signCounts?: boolean): Generator<string, void, undefined>;