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.
- package/README.md +10 -0
- package/dist/esm/core/bidi/index.d.ts +10 -0
- package/dist/esm/core/bidi/index.js +18 -2
- package/dist/esm/core/converter/facade.js +10 -5
- package/dist/esm/core/converter/project.js +1 -0
- package/dist/esm/core/converter/ream.js +17 -6
- package/dist/esm/core/converter/sheet-images.d.ts +17 -0
- package/dist/esm/core/converter/sheet-images.js +33 -0
- package/dist/esm/core/crypto/offcrypto.js +56 -12
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +112 -0
- package/dist/esm/core/drawingml/chart-geometry.d.ts +36 -13
- package/dist/esm/core/drawingml/chart-geometry.js +372 -146
- package/dist/esm/core/drawingml/chart-parser.d.ts +19 -0
- package/dist/esm/core/drawingml/chart-parser.js +126 -18
- package/dist/esm/core/drawingml/colors.d.ts +6 -0
- package/dist/esm/core/drawingml/colors.js +23 -3
- package/dist/esm/core/drawingml/shape-render.d.ts +3 -1
- package/dist/esm/core/drawingml/shape-render.js +4 -2
- package/dist/esm/core/font/measure.d.ts +9 -0
- package/dist/esm/core/font/measure.js +9 -1
- package/dist/esm/core/fonts/families.js +4 -2
- package/dist/esm/core/fonts/scripts.js +4 -2
- package/dist/esm/core/ir/flow.d.ts +6 -0
- package/dist/esm/core/ir/sheet.d.ts +43 -0
- package/dist/esm/core/number-format.d.ts +40 -15
- package/dist/esm/core/number-format.js +295 -83
- package/dist/esm/core/opc/tag-scan.d.ts +15 -0
- package/dist/esm/core/opc/tag-scan.js +41 -0
- package/dist/esm/core/opc/xml-entities.js +39 -2
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +96 -0
- package/dist/esm/core/style-cascade/resolver.js +4 -2
- package/dist/esm/excel/column-bands.d.ts +39 -6
- package/dist/esm/excel/column-bands.js +41 -17
- package/dist/esm/excel/comments-parser.js +19 -0
- package/dist/esm/excel/conditional-format.d.ts +7 -3
- package/dist/esm/excel/conditional-format.js +77 -39
- package/dist/esm/excel/formula/context.d.ts +18 -0
- package/dist/esm/excel/formula/eval.js +51 -0
- package/dist/esm/excel/formula/functions.js +37 -14
- package/dist/esm/excel/formula/index.d.ts +2 -2
- package/dist/esm/excel/formula/lexer.d.ts +1 -1
- package/dist/esm/excel/formula/lexer.js +41 -0
- package/dist/esm/excel/formula/parser.d.ts +25 -0
- package/dist/esm/excel/formula/parser.js +95 -0
- package/dist/esm/excel/print-model.d.ts +38 -1
- package/dist/esm/excel/print-model.js +536 -83
- package/dist/esm/excel/shared-strings-parser.d.ts +9 -0
- package/dist/esm/excel/shared-strings-parser.js +11 -1
- package/dist/esm/excel/sheet-drawing.d.ts +38 -2
- package/dist/esm/excel/sheet-drawing.js +47 -13
- package/dist/esm/excel/sheet-shape-parser.d.ts +6 -2
- package/dist/esm/excel/sheet-shape-parser.js +18 -6
- package/dist/esm/excel/sheet-to-flow.d.ts +17 -0
- package/dist/esm/excel/sheet-to-flow.js +434 -47
- package/dist/esm/excel/styles-parser.d.ts +9 -0
- package/dist/esm/excel/styles-parser.js +80 -26
- package/dist/esm/excel/table-parser.js +5 -0
- package/dist/esm/excel/table-style-presets.d.ts +26 -0
- package/dist/esm/excel/table-style-presets.js +1555 -0
- package/dist/esm/excel/table-style.d.ts +13 -0
- package/dist/esm/excel/table-style.js +75 -0
- package/dist/esm/excel/tint.d.ts +10 -0
- package/dist/esm/excel/tint.js +75 -0
- package/dist/esm/excel/vml-drawing.d.ts +41 -3
- package/dist/esm/excel/vml-drawing.js +144 -11
- package/dist/esm/excel/worksheet-parser.js +89 -15
- package/dist/esm/excel/xls/biff-reader.js +10 -2
- package/dist/esm/excel/xlsx-reader.js +234 -19
- package/dist/esm/excel/xlsx-writer.js +8 -7
- package/dist/esm/html/html-writer.js +202 -20
- package/dist/esm/layout/fit-pages.d.ts +11 -0
- package/dist/esm/layout/fit-pages.js +86 -0
- package/dist/esm/layout/line-paint.d.ts +58 -0
- package/dist/esm/layout/line-paint.js +107 -0
- package/dist/esm/layout/page-doc.d.ts +51 -0
- package/dist/esm/layout/page-doc.js +141 -1
- package/dist/esm/layout/styled-layout.d.ts +7 -0
- package/dist/esm/layout/styled-layout.js +275 -52
- package/dist/esm/layout/turned-section.js +14 -2
- package/dist/esm/markdown/markdown-writer.js +75 -13
- package/dist/esm/pdf/styled-page-emitter.js +49 -71
- package/dist/esm/pdf/styled-page-renderer.js +1 -1
- package/dist/esm/pdf-reader/annot-draw.js +2 -2
- package/dist/esm/pdf-reader/annots.js +3 -1
- package/dist/esm/pdf-reader/font.js +4 -4
- package/dist/esm/pdf-reader/layout.js +1 -1
- package/dist/esm/svg/svg-text.d.ts +58 -0
- package/dist/esm/svg/svg-text.js +322 -0
- package/dist/esm/svg/svg-writer.js +135 -71
- package/dist/esm/word/document-parser.js +1 -1
- package/dist/esm/word/docx-reader.js +13 -8
- package/dist/esm/word/docx-to-pdf.js +3 -3
- package/dist/esm/word/docx-writer.js +11 -2
- package/dist/esm/word/font-table.js +89 -10
- package/dist/esm/word/numbering-parser.js +1 -1
- 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
|
-
|
|
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 (!
|
|
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
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
489
|
-
|
|
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
|
-
|
|
288
|
-
|
|
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,
|
|
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")
|
|
32
|
-
|
|
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")
|
|
167
|
-
|
|
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
|
|
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
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
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
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
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>;
|