reamkit 1.15.4 → 1.17.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.
- package/dist/esm/core/bidi/segments.js +1 -2
- package/dist/esm/core/converter/facade.d.ts +5 -0
- package/dist/esm/core/converter/project.js +5 -1
- package/dist/esm/core/converter/ream.d.ts +6 -0
- package/dist/esm/core/converter/ream.js +34 -8
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +659 -7
- package/dist/esm/core/drawingml/chart-geometry.d.ts +34 -10
- package/dist/esm/core/drawingml/chart-geometry.js +424 -92
- package/dist/esm/core/drawingml/chart-parser.js +301 -11
- package/dist/esm/core/drawingml/colors.d.ts +7 -7
- package/dist/esm/core/drawingml/colors.js +84 -18
- package/dist/esm/core/drawingml/preset-geometry.js +240 -6
- package/dist/esm/core/drawingml/shape-render.d.ts +12 -0
- package/dist/esm/core/drawingml/shape-render.js +116 -3
- package/dist/esm/core/drawingml/sparkline-geometry.js +6 -4
- package/dist/esm/core/drawingml/theme-parser.d.ts +51 -0
- package/dist/esm/core/drawingml/theme-parser.js +92 -1
- package/dist/esm/core/font/measure.d.ts +5 -0
- package/dist/esm/core/font/measure.js +21 -1
- package/dist/esm/core/font/ttf-subset.js +26 -1
- package/dist/esm/core/images.d.ts +2 -2
- package/dist/esm/core/images.js +353 -43
- package/dist/esm/core/indexed-colors.d.ts +11 -0
- package/dist/esm/core/indexed-colors.js +81 -0
- package/dist/esm/core/ir/flow.d.ts +23 -1
- package/dist/esm/core/ir/sheet.d.ts +61 -0
- package/dist/esm/{excel → core}/number-format.d.ts +37 -0
- package/dist/esm/core/number-format.js +800 -0
- package/dist/esm/core/numbering/apply.d.ts +5 -3
- package/dist/esm/core/numbering/apply.js +42 -11
- package/dist/esm/core/numbering/index.js +2 -0
- package/dist/esm/core/numbering/state.d.ts +13 -1
- package/dist/esm/core/numbering/state.js +256 -15
- package/dist/esm/core/opc/alternate-content.d.ts +13 -0
- package/dist/esm/core/opc/alternate-content.js +72 -0
- package/dist/esm/core/opc/core-properties.js +1 -0
- package/dist/esm/core/opc/package.d.ts +14 -1
- package/dist/esm/core/opc/package.js +43 -4
- package/dist/esm/core/opc/relationships.js +1 -0
- package/dist/esm/core/opc/xml-entities.d.ts +12 -0
- package/dist/esm/core/opc/xml-entities.js +91 -0
- package/dist/esm/core/po-helpers.js +16 -1
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +137 -0
- package/dist/esm/core/style-cascade/resolver.js +66 -11
- package/dist/esm/core/style-cascade/table.js +35 -11
- package/dist/esm/core/style-cascade/types.d.ts +42 -1
- package/dist/esm/core/style-cascade/types.js +4 -0
- package/dist/esm/core/vector.d.ts +30 -0
- package/dist/esm/excel/activex-parser.js +1 -0
- package/dist/esm/excel/column-bands.d.ts +26 -1
- package/dist/esm/excel/column-bands.js +156 -20
- package/dist/esm/excel/comments-parser.js +5 -3
- package/dist/esm/excel/conditional-format.d.ts +25 -2
- package/dist/esm/excel/conditional-format.js +63 -19
- package/dist/esm/excel/escaped-text.d.ts +9 -0
- package/dist/esm/excel/escaped-text.js +20 -0
- package/dist/esm/excel/form-control-parser.js +1 -0
- package/dist/esm/excel/formula/dates.js +1 -1
- package/dist/esm/excel/header-footer.d.ts +9 -1
- package/dist/esm/excel/header-footer.js +162 -14
- package/dist/esm/excel/index.d.ts +1 -1
- package/dist/esm/excel/index.js +2 -2
- package/dist/esm/excel/pivot-table-parser.js +1 -0
- package/dist/esm/excel/print-model.d.ts +75 -0
- package/dist/esm/excel/print-model.js +899 -77
- package/dist/esm/excel/printer-settings.d.ts +16 -0
- package/dist/esm/excel/printer-settings.js +36 -0
- package/dist/esm/excel/rich-value.d.ts +13 -0
- package/dist/esm/excel/rich-value.js +120 -0
- package/dist/esm/excel/shared-strings-parser.js +11 -7
- package/dist/esm/excel/sheet-drawing.d.ts +11 -0
- package/dist/esm/excel/sheet-drawing.js +26 -3
- package/dist/esm/excel/sheet-shape-parser.d.ts +53 -1
- package/dist/esm/excel/sheet-shape-parser.js +461 -32
- package/dist/esm/excel/sheet-to-flow.d.ts +23 -2
- package/dist/esm/excel/sheet-to-flow.js +553 -36
- package/dist/esm/excel/slicer-parser.js +1 -0
- package/dist/esm/excel/styles-parser.d.ts +30 -2
- package/dist/esm/excel/styles-parser.js +177 -40
- package/dist/esm/excel/table-parser.js +1 -0
- package/dist/esm/excel/vml-drawing.d.ts +68 -0
- package/dist/esm/excel/vml-drawing.js +158 -0
- package/dist/esm/excel/workbook-parser.d.ts +5 -0
- package/dist/esm/excel/workbook-parser.js +5 -1
- package/dist/esm/excel/worksheet-parser.d.ts +2 -1
- package/dist/esm/excel/worksheet-parser.js +150 -61
- package/dist/esm/excel/xlsx-reader.js +242 -11
- package/dist/esm/excel/xlsx-to-pdf.js +12 -1
- package/dist/esm/html/html-writer.js +29 -6
- package/dist/esm/layout/page-doc.d.ts +87 -1
- package/dist/esm/layout/styled-layout.d.ts +46 -2
- package/dist/esm/layout/styled-layout.js +2345 -274
- package/dist/esm/pdf/cid-font.js +20 -9
- package/dist/esm/pdf/objects.d.ts +13 -0
- package/dist/esm/pdf/objects.js +18 -1
- package/dist/esm/pdf/shading.js +76 -6
- package/dist/esm/pdf/styled-page-emitter.js +355 -49
- package/dist/esm/pdf/text-page-renderer.js +1 -0
- package/dist/esm/pdf/vector-graphics.d.ts +31 -2
- package/dist/esm/pdf/vector-graphics.js +103 -3
- package/dist/esm/pptx/pptx-reader.js +2 -0
- package/dist/esm/pptx/slide-parser.d.ts +21 -4
- package/dist/esm/pptx/slide-parser.js +98 -24
- package/dist/esm/svg/svg-writer.js +23 -4
- package/dist/esm/word/document-parser.d.ts +96 -7
- package/dist/esm/word/document-parser.js +538 -116
- package/dist/esm/word/docx-reader.js +135 -43
- package/dist/esm/word/docx-writer.js +6 -1
- package/dist/esm/word/drawing-parser.d.ts +86 -5
- package/dist/esm/word/drawing-parser.js +1590 -54
- package/dist/esm/word/index.d.ts +4 -4
- package/dist/esm/word/numbering-parser.d.ts +2 -1
- package/dist/esm/word/numbering-parser.js +108 -21
- package/dist/esm/word/paragraph-properties.d.ts +11 -1
- package/dist/esm/word/paragraph-properties.js +219 -10
- package/dist/esm/word/run-properties.js +45 -1
- package/dist/esm/word/settings-parser.d.ts +24 -0
- package/dist/esm/word/settings-parser.js +32 -5
- package/dist/esm/word/shading.d.ts +11 -0
- package/dist/esm/word/shading.js +64 -0
- package/dist/esm/word/styles-parser.js +9 -2
- package/dist/esm/word/table-parser.d.ts +9 -1
- package/dist/esm/word/table-parser.js +182 -30
- package/package.json +7 -3
- package/dist/esm/excel/number-format.js +0 -476
|
@@ -4,6 +4,7 @@ import { XMLParser } from "fast-xml-parser";
|
|
|
4
4
|
//#region src/core/drawingml/chart-parser.ts
|
|
5
5
|
var decoder = new TextDecoder("utf-8");
|
|
6
6
|
var parser = new XMLParser({
|
|
7
|
+
htmlEntities: true,
|
|
7
8
|
ignoreAttributes: false,
|
|
8
9
|
attributeNamePrefix: "@_",
|
|
9
10
|
preserveOrder: true,
|
|
@@ -37,35 +38,90 @@ var TYPE_OF_TAG = {
|
|
|
37
38
|
* @returns The parsed chart, or `null` when there is no `c:chart` / `c:plotArea`.
|
|
38
39
|
*/
|
|
39
40
|
function parseChart(chartXml, resolveColor) {
|
|
40
|
-
const
|
|
41
|
+
const tree = parser.parse(decoder.decode(chartXml));
|
|
42
|
+
const chart = poFindByPath(tree, ["c:chartSpace", "c:chart"]);
|
|
41
43
|
if (!chart) return null;
|
|
42
44
|
const plotArea = poChildren(chart).find((c) => poIs(c, "c:plotArea"));
|
|
43
45
|
if (!plotArea) return null;
|
|
44
|
-
const
|
|
46
|
+
const groups = poChildren(plotArea).filter((c) => (poTag(c) ?? "") in TYPE_OF_TAG);
|
|
47
|
+
const group = groups[0];
|
|
45
48
|
const type = group ? TYPE_OF_TAG[poTag(group)] ?? "unknown" : "unknown";
|
|
46
|
-
const
|
|
47
|
-
const
|
|
49
|
+
const groupAxIds = (g) => poChildren(g).filter((c) => poIs(c, "c:axId")).map((c) => poAttr(c, "val") ?? "");
|
|
50
|
+
const primaryAxIds = new Set(group ? groupAxIds(group) : []);
|
|
51
|
+
const serNodes = [];
|
|
52
|
+
const series = [];
|
|
53
|
+
let secondaryValAxId;
|
|
54
|
+
for (const g of groups) {
|
|
55
|
+
const groupType = TYPE_OF_TAG[poTag(g)] ?? "unknown";
|
|
56
|
+
const own = groupAxIds(g).filter((id) => !primaryAxIds.has(id));
|
|
57
|
+
const secondary = own.length > 0;
|
|
58
|
+
for (const s of poChildren(g).filter((c) => poIs(c, "c:ser"))) {
|
|
59
|
+
serNodes.push(s);
|
|
60
|
+
series.push({
|
|
61
|
+
...parseSeries(s, resolveColor),
|
|
62
|
+
...groupType === type ? {} : { type: groupType },
|
|
63
|
+
...secondary ? { secondaryAxis: true } : {}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (secondary && series.length > 0) secondaryValAxId ??= own.find((id) => poChildren(plotArea).some((c) => poIs(c, "c:valAx") && poChildren(c).some((k) => poIs(k, "c:axId") && poAttr(k, "val") === id)));
|
|
67
|
+
}
|
|
68
|
+
const secondaryValAx = secondaryValAxId ? poChildren(plotArea).find((c) => poIs(c, "c:valAx") && poChildren(c).some((k) => poIs(k, "c:axId") && poAttr(k, "val") === secondaryValAxId)) : void 0;
|
|
69
|
+
const secondaryDeleted = secondaryValAx !== void 0 && poChildren(secondaryValAx).some((c) => poIs(c, "c:delete") && poAttr(c, "val") === "1");
|
|
70
|
+
const secondaryTitleNode = secondaryValAx && !secondaryDeleted ? poChildren(secondaryValAx).find((c) => poIs(c, "c:title")) : void 0;
|
|
71
|
+
const secondaryValAxisTitle = secondaryTitleNode ? collectAT(secondaryTitleNode) || "Axis Title" : void 0;
|
|
48
72
|
let categories = [];
|
|
73
|
+
let categoriesRef;
|
|
49
74
|
for (const s of serNodes) {
|
|
50
75
|
const cat = poChildren(s).find((c) => poIs(c, "c:cat"));
|
|
51
76
|
if (cat) {
|
|
52
77
|
categories = denseStrings(cat);
|
|
78
|
+
categoriesRef ??= refFormula(cat);
|
|
53
79
|
break;
|
|
54
80
|
}
|
|
55
81
|
}
|
|
82
|
+
const scatterStyleRaw = group ? poVal(poChildren(group).find((c) => poIs(c, "c:scatterStyle"))) : void 0;
|
|
83
|
+
const scatterStyle = SCATTER_STYLES.has(scatterStyleRaw ?? "") ? scatterStyleRaw : void 0;
|
|
84
|
+
const lineMarkers = group && poIs(group, "c:lineChart") ? poVal(poChildren(group).find((c) => poIs(c, "c:marker"))) === "1" : false;
|
|
56
85
|
const barDir = group ? poVal(poChildren(group).find((c) => poIs(c, "c:barDir"))) : void 0;
|
|
57
86
|
const grouping = group ? poVal(poChildren(group).find((c) => poIs(c, "c:grouping"))) : void 0;
|
|
58
87
|
const doughnut = group ? poIs(group, "c:doughnutChart") : false;
|
|
59
88
|
const showValues = group ? chartShowsValues(group) : false;
|
|
60
89
|
const catAxisTitle = axisTitle(plotArea, "c:catAx");
|
|
61
90
|
const valAxisTitle = axisTitle(plotArea, "c:valAx");
|
|
91
|
+
const catAxNode = poChildren(plotArea).find((c) => poIs(c, "c:catAx"));
|
|
92
|
+
const valAxNode = poChildren(plotArea).find((c) => poIs(c, "c:valAx"));
|
|
93
|
+
const axisAt = (...where) => poChildren(plotArea).find((c) => (poIs(c, "c:catAx") || poIs(c, "c:valAx") || poIs(c, "c:dateAx")) && c !== secondaryValAx && where.includes(poVal(poChildren(c).find((k) => poIs(k, "c:axPos"))) ?? ""));
|
|
94
|
+
const bottomAxNode = axisAt("b", "t") ?? catAxNode;
|
|
95
|
+
const leftAxNode = axisAt("l", "r") ?? valAxNode;
|
|
96
|
+
const catAxisLine = lineStyleOf(bottomAxNode, resolveColor);
|
|
97
|
+
const valAxisLine = lineStyleOf(leftAxNode, resolveColor);
|
|
98
|
+
const secondaryValAxisLine = lineStyleOf(secondaryValAx, resolveColor);
|
|
99
|
+
const gridLine = lineStyleOf(leftAxNode ? poChildren(leftAxNode).find((c) => poIs(c, "c:majorGridlines")) : void 0, resolveColor);
|
|
100
|
+
const valAxisMin = axisScaling(plotArea, "c:min");
|
|
101
|
+
const valAxisMax = axisScaling(plotArea, "c:max");
|
|
102
|
+
const catAxisReversed = axisOrientation(catAxNode) === "maxMin";
|
|
103
|
+
const chartSpace = tree.find((c) => poIs(c, "c:chartSpace"));
|
|
104
|
+
const spaceSpPr = poChildren(chartSpace).find((c) => poIs(c, "c:spPr"));
|
|
105
|
+
const frameLine = spaceSpPr ? poChildren(spaceSpPr).find((c) => poIs(c, "a:ln")) : void 0;
|
|
106
|
+
const frameStyle = lineStyleOf(chartSpace, resolveColor);
|
|
107
|
+
const frameFillHex = spaceSpPr ? frameFillOf(spaceSpPr, resolveColor) : "FFFFFF";
|
|
108
|
+
const plotSpPr = poChildren(plotArea).find((c) => poIs(c, "c:spPr"));
|
|
109
|
+
const plotFillHex = plotSpPr ? frameFillOf(plotSpPr, resolveColor) : void 0;
|
|
110
|
+
const plotLine = lineStyleOf(plotArea, resolveColor);
|
|
111
|
+
const frameLineHex = spaceSpPr ? frameLine ? frameFillOf(frameLine, resolveColor) : void 0 : "D9D9D9";
|
|
112
|
+
const numberFormat = valueFormatCode(plotArea);
|
|
62
113
|
const legend = poChildren(chart).find((c) => poIs(c, "c:legend"));
|
|
63
114
|
const legendPos = legend ? poVal(poChildren(legend).find((c) => poIs(c, "c:legendPos"))) : void 0;
|
|
64
|
-
const title = chartTitle(chart);
|
|
115
|
+
const title = chartTitle(chart, series);
|
|
116
|
+
const gapNode = poChildren(plotArea).flatMap((g) => poChildren(g)).find((c) => poIs(c, "c:gapWidth"));
|
|
117
|
+
const gapRaw = gapNode ? poVal(gapNode) : void 0;
|
|
118
|
+
const gapPercent = gapRaw === void 0 ? NaN : Number(gapRaw);
|
|
65
119
|
return {
|
|
66
120
|
type,
|
|
67
121
|
...title ? { title } : {},
|
|
122
|
+
...Number.isFinite(gapPercent) && gapPercent >= 0 ? { gapPercent } : {},
|
|
68
123
|
categories,
|
|
124
|
+
...categoriesRef ? { categoriesRef } : {},
|
|
69
125
|
series,
|
|
70
126
|
hasLegend: legend !== void 0,
|
|
71
127
|
...isLegendPos(legendPos) ? { legendPos } : {},
|
|
@@ -74,7 +130,24 @@ function parseChart(chartXml, resolveColor) {
|
|
|
74
130
|
...doughnut ? { doughnut: true } : {},
|
|
75
131
|
...showValues ? { showValues: true } : {},
|
|
76
132
|
...catAxisTitle ? { catAxisTitle } : {},
|
|
77
|
-
...
|
|
133
|
+
...catAxisReversed ? { catAxisReversed } : {},
|
|
134
|
+
...valAxisTitle ? { valAxisTitle } : {},
|
|
135
|
+
...secondaryValAxisTitle ? { secondaryValAxisTitle } : {},
|
|
136
|
+
...scatterStyle ? { scatterStyle } : {},
|
|
137
|
+
...lineMarkers ? { lineMarkers: true } : {},
|
|
138
|
+
...catAxisLine ? { catAxisLine } : {},
|
|
139
|
+
...valAxisLine ? { valAxisLine } : {},
|
|
140
|
+
...secondaryValAxisLine ? { secondaryValAxisLine } : {},
|
|
141
|
+
...gridLine ? { gridLine } : {},
|
|
142
|
+
...valAxisMin !== void 0 ? { valAxisMin } : {},
|
|
143
|
+
...valAxisMax !== void 0 ? { valAxisMax } : {},
|
|
144
|
+
...frameFillHex ? { frameFillHex } : {},
|
|
145
|
+
...frameLineHex ? { frameLineHex } : {},
|
|
146
|
+
...frameStyle?.widthPt !== void 0 ? { frameLineWidthPt: frameStyle.widthPt } : {},
|
|
147
|
+
...frameStyle?.dash ? { frameLineDash: frameStyle.dash } : {},
|
|
148
|
+
...plotFillHex ? { plotFillHex } : {},
|
|
149
|
+
...plotLine && plotLine.none !== true ? { plotLine } : {},
|
|
150
|
+
...numberFormat ? { numberFormat } : {}
|
|
78
151
|
};
|
|
79
152
|
}
|
|
80
153
|
function parseSeries(ser, resolveColor) {
|
|
@@ -85,12 +158,49 @@ function parseSeries(ser, resolveColor) {
|
|
|
85
158
|
const name = seriesName(ser);
|
|
86
159
|
const colorHex = fillColorOf(poChildren(ser).find((c) => poIs(c, "c:spPr")), resolveColor);
|
|
87
160
|
const pointColors = dataPointColors(ser, resolveColor);
|
|
161
|
+
const pointLabels = customDataLabels(ser);
|
|
162
|
+
const marker = seriesMarker(ser);
|
|
163
|
+
const line = lineStyleOf(ser, resolveColor);
|
|
164
|
+
const valuesRef = valNode ? refFormula(valNode) : void 0;
|
|
165
|
+
const nameRef = refFormula(poChildren(ser).find((c) => poIs(c, "c:tx")));
|
|
88
166
|
return {
|
|
89
167
|
values,
|
|
168
|
+
...valuesRef ? { valuesRef } : {},
|
|
169
|
+
...nameRef ? { nameRef } : {},
|
|
90
170
|
...xValues && xValues.length > 0 ? { xValues } : {},
|
|
91
171
|
...name ? { name } : {},
|
|
92
172
|
...colorHex ? { colorHex } : {},
|
|
93
|
-
...pointColors.length > 0 ? { pointColors } : {}
|
|
173
|
+
...pointColors.length > 0 ? { pointColors } : {},
|
|
174
|
+
...pointLabels.length > 0 ? { pointLabels } : {},
|
|
175
|
+
...marker ? { marker } : {},
|
|
176
|
+
...line ? { line } : {}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
var MARKER_SYMBOLS = new Set([
|
|
180
|
+
"circle",
|
|
181
|
+
"dash",
|
|
182
|
+
"diamond",
|
|
183
|
+
"dot",
|
|
184
|
+
"none",
|
|
185
|
+
"plus",
|
|
186
|
+
"square",
|
|
187
|
+
"star",
|
|
188
|
+
"triangle",
|
|
189
|
+
"x"
|
|
190
|
+
]);
|
|
191
|
+
/**
|
|
192
|
+
* §21.2.2.106 `c:marker` — the series' own point symbol. `auto` (and the
|
|
193
|
+
* picture marker we cannot draw) read as absent, leaving the reader's default.
|
|
194
|
+
*/
|
|
195
|
+
function seriesMarker(ser) {
|
|
196
|
+
const node = poChildren(ser).find((c) => poIs(c, "c:marker"));
|
|
197
|
+
if (!node) return void 0;
|
|
198
|
+
const symbol = poVal(poChildren(node).find((c) => poIs(c, "c:symbol")));
|
|
199
|
+
if (!symbol || !MARKER_SYMBOLS.has(symbol)) return void 0;
|
|
200
|
+
const sizePt = poIntAttr(poChildren(node).find((c) => poIs(c, "c:size")), "val");
|
|
201
|
+
return {
|
|
202
|
+
symbol,
|
|
203
|
+
...sizePt !== void 0 && sizePt > 0 ? { sizePt } : {}
|
|
94
204
|
};
|
|
95
205
|
}
|
|
96
206
|
function seriesName(ser) {
|
|
@@ -100,6 +210,29 @@ function seriesName(ser) {
|
|
|
100
210
|
if (direct) return poText(direct) || void 0;
|
|
101
211
|
return readPts(tx)[0]?.v || void 0;
|
|
102
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* §21.2.2.49 — the labels the author typed, by point index. A `<c:dLbl>` with
|
|
215
|
+
* its own `<c:tx><c:rich>` replaces whatever the chart would have computed for
|
|
216
|
+
* that point, and it is the only place that text exists.
|
|
217
|
+
*/
|
|
218
|
+
function customDataLabels(ser) {
|
|
219
|
+
const dLbls = poChildren(ser).find((c) => poIs(c, "c:dLbls"));
|
|
220
|
+
if (!dLbls) return [];
|
|
221
|
+
const out = [];
|
|
222
|
+
for (const dLbl of poChildren(dLbls)) {
|
|
223
|
+
if (!poIs(dLbl, "c:dLbl")) continue;
|
|
224
|
+
const idxNode = poChildren(dLbl).find((c) => poIs(c, "c:idx"));
|
|
225
|
+
const tx = poChildren(dLbl).find((c) => poIs(c, "c:tx"));
|
|
226
|
+
if (!tx) continue;
|
|
227
|
+
const text = collectAT(tx).trim();
|
|
228
|
+
if (text.length === 0) continue;
|
|
229
|
+
out.push({
|
|
230
|
+
idx: idxNode ? poIntAttr(idxNode, "val") ?? 0 : 0,
|
|
231
|
+
text
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
return out;
|
|
235
|
+
}
|
|
103
236
|
function dataPointColors(ser, resolveColor) {
|
|
104
237
|
const out = [];
|
|
105
238
|
for (const dPt of poChildren(ser)) {
|
|
@@ -114,11 +247,31 @@ function dataPointColors(ser, resolveColor) {
|
|
|
114
247
|
}
|
|
115
248
|
return out;
|
|
116
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* The BACKGROUND fill of a frame (the chart space, the plot rectangle): its own
|
|
252
|
+
* `a:solidFill` and nothing else. Unlike a series, a frame that states
|
|
253
|
+
* `<a:noFill/>` is not filled in the colour of its own rule —
|
|
254
|
+
* Chart_Plot_BorderLine_Style.docx rules its plot in orange and we painted the
|
|
255
|
+
* whole plot orange.
|
|
256
|
+
*
|
|
257
|
+
* @param spPr The frame's `c:spPr`, or `undefined`.
|
|
258
|
+
* @param resolveColor Maps a DrawingML colour reference to 6-hex.
|
|
259
|
+
* @returns The fill colour, or `undefined` when the frame states none.
|
|
260
|
+
*/
|
|
261
|
+
function frameFillOf(spPr, resolveColor) {
|
|
262
|
+
if (!spPr) return void 0;
|
|
263
|
+
const solid = poChildren(spPr).find((c) => poIs(c, "a:solidFill"));
|
|
264
|
+
return solid ? colorFromSolidFill(solid, resolveColor) : void 0;
|
|
265
|
+
}
|
|
117
266
|
function fillColorOf(spPr, resolveColor) {
|
|
118
267
|
if (!spPr) return void 0;
|
|
119
268
|
const directFill = poChildren(spPr).find((c) => poIs(c, "a:solidFill"));
|
|
120
269
|
const fromFill = directFill ? colorFromSolidFill(directFill, resolveColor) : void 0;
|
|
121
270
|
if (fromFill) return fromFill;
|
|
271
|
+
const grad = poChildren(spPr).find((c) => poIs(c, "a:gradFill"));
|
|
272
|
+
const firstStop = grad ? poChildren(grad).filter((c) => poIs(c, "a:gsLst")).flatMap((lst) => poChildren(lst)).find((gs) => poIs(gs, "a:gs")) : void 0;
|
|
273
|
+
const fromGrad = firstStop ? colorFromSolidFill(firstStop, resolveColor) : void 0;
|
|
274
|
+
if (fromGrad) return fromGrad;
|
|
122
275
|
const ln = poChildren(spPr).find((c) => poIs(c, "a:ln"));
|
|
123
276
|
const lnFill = ln ? poChildren(ln).find((c) => poIs(c, "a:solidFill")) : void 0;
|
|
124
277
|
return lnFill ? colorFromSolidFill(lnFill, resolveColor) : void 0;
|
|
@@ -139,14 +292,123 @@ function collectAT(node) {
|
|
|
139
292
|
walk(node);
|
|
140
293
|
return text;
|
|
141
294
|
}
|
|
142
|
-
|
|
295
|
+
/**
|
|
296
|
+
* §21.2.2.213 `c:title` — the chart's title, authored or generated.
|
|
297
|
+
*
|
|
298
|
+
* `c:tx` is OPTIONAL: a `<c:title>` with none asks the application to make the
|
|
299
|
+
* title up, and §21.2.2.10's `c:autoTitleDeleted val="0"` says that generated
|
|
300
|
+
* title has not been removed. Excel and LibreOffice agree on the rule — one
|
|
301
|
+
* series means the series' name, anything else the placeholder "Chart Title" —
|
|
302
|
+
* and reading only the `a:t` runs gave 56557.xlsx a blank page where the
|
|
303
|
+
* reference draws its title. Eleven chart parts across eight corpus files are
|
|
304
|
+
* written this way.
|
|
305
|
+
*
|
|
306
|
+
* The placeholder is an application string and therefore a locale: this file
|
|
307
|
+
* was authored in Swedish, where Excel prints "Diagramrubrik". English is what
|
|
308
|
+
* both references print here, and what we have to pick.
|
|
309
|
+
*
|
|
310
|
+
* @param chart The `c:chart` element.
|
|
311
|
+
* @param series The chart's series, for the single-series case.
|
|
312
|
+
* @returns The title text, or undefined when there is no title to draw.
|
|
313
|
+
*/
|
|
314
|
+
function chartTitle(chart, series) {
|
|
143
315
|
const title = poChildren(chart).find((c) => poIs(c, "c:title"));
|
|
144
|
-
|
|
316
|
+
if (!title) return void 0;
|
|
317
|
+
const authored = collectAT(title) || cachedTitleText(title);
|
|
318
|
+
if (authored) return authored;
|
|
319
|
+
if (poVal(poChildren(chart).find((c) => poIs(c, "c:autoTitleDeleted"))) === "1") return void 0;
|
|
320
|
+
return (series.length === 1 ? series[0]?.name : void 0) ?? "Chart Title";
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* §21.2.2.215 — a title the author gave as a FORMULA: `c:tx/c:strRef` with the
|
|
324
|
+
* text in its `c:strCache`, and not one `a:t` run anywhere. Read for rich text
|
|
325
|
+
* alone, such a title came out as the generated placeholder —
|
|
326
|
+
* chartTitle_withTitleFormula.xlsx printed "Chart Title" where both references
|
|
327
|
+
* print "Formula Title from Excel 2016".
|
|
328
|
+
*
|
|
329
|
+
* @param title The `c:title` node.
|
|
330
|
+
* @returns The cached text, or undefined when the title carries no cache.
|
|
331
|
+
*/
|
|
332
|
+
function cachedTitleText(title) {
|
|
333
|
+
const tx = poChildren(title).find((c) => poIs(c, "c:tx"));
|
|
334
|
+
const ref = tx ? poChildren(tx).find((c) => poIs(c, "c:strRef")) : void 0;
|
|
335
|
+
if (!ref) return void 0;
|
|
336
|
+
const cached = denseStrings(ref).filter((t) => t.length > 0);
|
|
337
|
+
return cached.length > 0 ? cached.join(" ") : void 0;
|
|
145
338
|
}
|
|
146
339
|
function axisTitle(plotArea, axTag) {
|
|
147
340
|
const ax = poChildren(plotArea).find((c) => poIs(c, axTag));
|
|
148
341
|
const title = ax ? poChildren(ax).find((c) => poIs(c, "c:title")) : void 0;
|
|
149
|
-
|
|
342
|
+
if (!title) return void 0;
|
|
343
|
+
return collectAT(title) || cachedTitleText(title) || "Axis Title";
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* §21.2.2.121 `c:numFmt` on the value axis — the number format its tick labels
|
|
347
|
+
* and the chart's data labels are drawn in. It is the same code grammar cells
|
|
348
|
+
* use (§18.8.31), so a currency axis reads as currency: without it a monthly
|
|
349
|
+
* budget's axis ran 0/1000/2000 where every other reader shows $0/$1,000/$2,000.
|
|
350
|
+
*
|
|
351
|
+
* `sourceLinked="1"` means "whatever the source cells use", which the chart part
|
|
352
|
+
* does not carry — those keep the plain numeric render.
|
|
353
|
+
*/
|
|
354
|
+
/** §21.2.2.134 `c:scaling/c:orientation` — `minMax` (the default) or `maxMin`. */
|
|
355
|
+
function axisOrientation(ax) {
|
|
356
|
+
const scaling = ax ? poChildren(ax).find((c) => poIs(c, "c:scaling")) : void 0;
|
|
357
|
+
return scaling ? poVal(poChildren(scaling).find((c) => poIs(c, "c:orientation"))) : void 0;
|
|
358
|
+
}
|
|
359
|
+
function axisScaling(plotArea, tag) {
|
|
360
|
+
const ax = poChildren(plotArea).find((c) => poIs(c, "c:valAx"));
|
|
361
|
+
const scaling = ax ? poChildren(ax).find((c) => poIs(c, "c:scaling")) : void 0;
|
|
362
|
+
const node = scaling ? poChildren(scaling).find((c) => poIs(c, tag)) : void 0;
|
|
363
|
+
const v = node ? Number(poAttr(node, "val")) : NaN;
|
|
364
|
+
return Number.isFinite(v) ? v : void 0;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* §21.2.2.196 `c:spPr/a:ln` — an axis's (or a series') own rule.
|
|
368
|
+
* `<a:ln><a:noFill/>` means it draws nothing, which is not the same as having
|
|
369
|
+
* no `c:spPr` at all: the first hides the line, the second leaves it to the
|
|
370
|
+
* renderer's default.
|
|
371
|
+
*
|
|
372
|
+
* @param owner The `c:catAx` / `c:valAx` / `c:majorGridlines` / `c:ser`
|
|
373
|
+
* node.
|
|
374
|
+
* @param resolveColor Maps a DrawingML colour reference to 6 hex digits.
|
|
375
|
+
* @returns The rule, or undefined when the node says nothing about it.
|
|
376
|
+
*/
|
|
377
|
+
var DASHES = new Set([
|
|
378
|
+
"solid",
|
|
379
|
+
"dot",
|
|
380
|
+
"dash",
|
|
381
|
+
"dashDot",
|
|
382
|
+
"lgDash",
|
|
383
|
+
"lgDashDot",
|
|
384
|
+
"sysDash",
|
|
385
|
+
"sysDot"
|
|
386
|
+
]);
|
|
387
|
+
function lineStyleOf(owner, resolveColor) {
|
|
388
|
+
const spPr = owner ? poChildren(owner).find((c) => poIs(c, "c:spPr")) : void 0;
|
|
389
|
+
const ln = spPr ? poChildren(spPr).find((c) => poIs(c, "a:ln")) : void 0;
|
|
390
|
+
if (!ln) return void 0;
|
|
391
|
+
if (poChildren(ln).some((c) => poIs(c, "a:noFill"))) return { none: true };
|
|
392
|
+
const solid = poChildren(ln).find((c) => poIs(c, "a:solidFill"));
|
|
393
|
+
const colorHex = solid ? colorFromSolidFill(solid, resolveColor) : void 0;
|
|
394
|
+
const emu = Number(poAttr(ln, "w"));
|
|
395
|
+
const widthPt = Number.isFinite(emu) && emu > 0 ? emu / 12700 : void 0;
|
|
396
|
+
const prst = poChildren(ln).find((c) => poIs(c, "a:prstDash"));
|
|
397
|
+
const dashVal = prst ? poAttr(prst, "val") : void 0;
|
|
398
|
+
const dash = dashVal !== void 0 && DASHES.has(dashVal) ? dashVal : void 0;
|
|
399
|
+
if (!colorHex && widthPt === void 0 && !dash) return void 0;
|
|
400
|
+
return {
|
|
401
|
+
...colorHex ? { colorHex } : {},
|
|
402
|
+
...widthPt !== void 0 ? { widthPt } : {},
|
|
403
|
+
...dash ? { dash } : {}
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
function valueFormatCode(plotArea) {
|
|
407
|
+
const ax = poChildren(plotArea).find((c) => poIs(c, "c:valAx"));
|
|
408
|
+
const numFmt = ax ? poChildren(ax).find((c) => poIs(c, "c:numFmt")) : void 0;
|
|
409
|
+
const code = numFmt ? poAttr(numFmt, "formatCode") : void 0;
|
|
410
|
+
if (code === void 0 || code.trim().length === 0) return void 0;
|
|
411
|
+
return code.trim().toLowerCase() === "general" ? void 0 : code;
|
|
150
412
|
}
|
|
151
413
|
function dLblsShowVal(dLbls) {
|
|
152
414
|
if (!dLbls) return false;
|
|
@@ -158,6 +420,13 @@ function chartShowsValues(group) {
|
|
|
158
420
|
for (const ser of poChildren(group)) if (poIs(ser, "c:ser") && dLblsShowVal(poChildren(ser).find((c) => poIs(c, "c:dLbls")))) return true;
|
|
159
421
|
return false;
|
|
160
422
|
}
|
|
423
|
+
/** The `<c:f>` inside a c:val / c:cat / c:tx, or undefined when there is none. */
|
|
424
|
+
function refFormula(container) {
|
|
425
|
+
if (!container) return void 0;
|
|
426
|
+
const f = poFindDescendant(container, "c:f");
|
|
427
|
+
const text = f ? poText(f).trim() : "";
|
|
428
|
+
return text.length > 0 ? text : void 0;
|
|
429
|
+
}
|
|
161
430
|
function readPts(container) {
|
|
162
431
|
const cache = poFindDescendant(container, "c:numCache") ?? poFindDescendant(container, "c:strCache");
|
|
163
432
|
if (!cache) return [];
|
|
@@ -249,8 +518,29 @@ function withChartColorStyle(chart, pkg, chartPartPath, resolveColor) {
|
|
|
249
518
|
seriesColorCycle: cycle
|
|
250
519
|
};
|
|
251
520
|
}
|
|
252
|
-
|
|
521
|
+
const themed = ACCENT_SLOTS.map((scheme) => resolveColor({ scheme }));
|
|
522
|
+
return themed.every((c) => c !== void 0) ? {
|
|
523
|
+
...chart,
|
|
524
|
+
seriesColorCycle: themed
|
|
525
|
+
} : chart;
|
|
253
526
|
}
|
|
527
|
+
/** §20.1.4.1.5 — the accent slots a chart cycles its series through. */
|
|
528
|
+
var ACCENT_SLOTS = [
|
|
529
|
+
"accent1",
|
|
530
|
+
"accent2",
|
|
531
|
+
"accent3",
|
|
532
|
+
"accent4",
|
|
533
|
+
"accent5",
|
|
534
|
+
"accent6"
|
|
535
|
+
];
|
|
536
|
+
var SCATTER_STYLES = new Set([
|
|
537
|
+
"none",
|
|
538
|
+
"line",
|
|
539
|
+
"lineMarker",
|
|
540
|
+
"marker",
|
|
541
|
+
"smooth",
|
|
542
|
+
"smoothMarker"
|
|
543
|
+
]);
|
|
254
544
|
var REL_CHART_COLOR_STYLE = "http://schemas.microsoft.com/office/2011/relationships/chartColorStyle";
|
|
255
545
|
//#endregion
|
|
256
546
|
export { parseChart, withChartColorStyle };
|
|
@@ -2,11 +2,11 @@ import { PoNode } from '../po-helpers.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* A colour transform child (§20.1.2.3): `lumMod`/`lumOff` modulate luminance,
|
|
4
4
|
* `shade` darkens toward black, `tint` lightens toward white. `val` is normalised
|
|
5
|
-
* to 0..1 (the XML stores thousandths of a percent). `alpha`
|
|
5
|
+
* to 0..1 (the XML stores thousandths of a percent). `alpha` composites over white
|
|
6
6
|
* (solid fills emit no transparency).
|
|
7
7
|
*/
|
|
8
8
|
export interface ColorMod {
|
|
9
|
-
readonly kind: 'lumMod' | 'lumOff' | 'shade' | 'tint' | 'alpha';
|
|
9
|
+
readonly kind: 'lumMod' | 'lumOff' | 'shade' | 'tint' | 'alpha' | 'satMod' | 'hueOff' | 'satOff';
|
|
10
10
|
readonly val: number;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
@@ -60,11 +60,11 @@ export declare const defaultColorResolver: ColorResolver;
|
|
|
60
60
|
*/
|
|
61
61
|
export declare function readColorMods(colorNode: PoNode): Array<ColorMod>;
|
|
62
62
|
/**
|
|
63
|
-
* Resolve an `a:srgbClr` / `a:schemeClr` node to a hex string (with
|
|
64
|
-
* transforms applied). Returns `undefined` when the node is some other
|
|
65
|
-
* valueless, or the resolver does not know the colour. Container
|
|
66
|
-
* (stop at the first colour node vs continue past unresolved
|
|
67
|
-
* callers.
|
|
63
|
+
* Resolve an `a:srgbClr` / `a:schemeClr` / `a:sysClr` node to a hex string (with
|
|
64
|
+
* colour transforms applied). Returns `undefined` when the node is some other
|
|
65
|
+
* element, valueless, or the resolver does not know the colour. Container
|
|
66
|
+
* traversal policy (stop at the first colour node vs continue past unresolved
|
|
67
|
+
* ones) stays with the callers.
|
|
68
68
|
*
|
|
69
69
|
* @param c The candidate colour node.
|
|
70
70
|
* @param resolveColor The resolver mapping a {@link RawColor} to hex.
|
|
@@ -96,6 +96,19 @@ function applyColorMods(hex, mods) {
|
|
|
96
96
|
const [h, s, l] = rgbToHsl(r, g, b);
|
|
97
97
|
const l2 = m.kind === "lumMod" ? l * m.val : clamp01(l + m.val);
|
|
98
98
|
[r, g, b] = hslToRgb(h, s, l2);
|
|
99
|
+
} else if (m.kind === "hueOff") {
|
|
100
|
+
const [h, sat, l] = rgbToHsl(r, g, b);
|
|
101
|
+
[r, g, b] = hslToRgb(((h + m.val) % 360 + 360) % 360, sat, l);
|
|
102
|
+
} else if (m.kind === "satOff") {
|
|
103
|
+
const [h, sat, l] = rgbToHsl(r, g, b);
|
|
104
|
+
[r, g, b] = hslToRgb(h, clamp01(sat + m.val), l);
|
|
105
|
+
} else if (m.kind === "alpha") {
|
|
106
|
+
r = r * m.val + (1 - m.val);
|
|
107
|
+
g = g * m.val + (1 - m.val);
|
|
108
|
+
b = b * m.val + (1 - m.val);
|
|
109
|
+
} else {
|
|
110
|
+
const [h, s, l] = rgbToHsl(r, g, b);
|
|
111
|
+
[r, g, b] = hslToRgb(h, clamp01(s * m.val), l);
|
|
99
112
|
}
|
|
100
113
|
const toHex = (x) => Math.round(clamp01(x) * 255).toString(16).padStart(2, "0");
|
|
101
114
|
return (toHex(r) + toHex(g) + toHex(b)).toUpperCase();
|
|
@@ -148,39 +161,92 @@ function makeColorResolver(palette) {
|
|
|
148
161
|
}
|
|
149
162
|
/** A {@link ColorResolver} backed by the {@link DEFAULT_THEME_PALETTE}. */
|
|
150
163
|
var defaultColorResolver = makeColorResolver(DEFAULT_THEME_PALETTE);
|
|
164
|
+
var PRESET_COLORS = new Map([
|
|
165
|
+
["black", "000000"],
|
|
166
|
+
["white", "FFFFFF"],
|
|
167
|
+
["gray", "808080"],
|
|
168
|
+
["grey", "808080"],
|
|
169
|
+
["darkGray".toLowerCase(), "A9A9A9"],
|
|
170
|
+
["lightGray".toLowerCase(), "D3D3D3"],
|
|
171
|
+
["red", "FF0000"],
|
|
172
|
+
["green", "008000"],
|
|
173
|
+
["blue", "0000FF"],
|
|
174
|
+
["yellow", "FFFF00"],
|
|
175
|
+
["cyan", "00FFFF"],
|
|
176
|
+
["magenta", "FF00FF"],
|
|
177
|
+
["dkGray".toLowerCase(), "A9A9A9"],
|
|
178
|
+
["ltGray".toLowerCase(), "D3D3D3"]
|
|
179
|
+
]);
|
|
151
180
|
/**
|
|
152
181
|
* Read the colour transform children ({@link ColorMod}s) under an `a:srgbClr` /
|
|
153
182
|
* `a:schemeClr` node, normalising each `val` from thousandths-of-a-percent to 0..1.
|
|
154
183
|
*/
|
|
155
184
|
function readColorMods(colorNode) {
|
|
156
185
|
const mods = [];
|
|
157
|
-
for (const c of poChildren(colorNode))
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
186
|
+
for (const c of poChildren(colorNode)) {
|
|
187
|
+
for (const kind of [
|
|
188
|
+
"lumMod",
|
|
189
|
+
"lumOff",
|
|
190
|
+
"shade",
|
|
191
|
+
"tint",
|
|
192
|
+
"alpha",
|
|
193
|
+
"satMod",
|
|
194
|
+
"satOff"
|
|
195
|
+
]) if (poIs(c, `a:${kind}`)) {
|
|
196
|
+
const v = poIntAttr(c, "val");
|
|
197
|
+
if (v !== void 0) mods.push({
|
|
198
|
+
kind,
|
|
199
|
+
val: v / 1e5
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (poIs(c, "a:hueOff")) {
|
|
203
|
+
const v = poIntAttr(c, "val");
|
|
204
|
+
if (v !== void 0) mods.push({
|
|
205
|
+
kind: "hueOff",
|
|
206
|
+
val: v / 6e4
|
|
207
|
+
});
|
|
208
|
+
}
|
|
169
209
|
}
|
|
170
210
|
return mods;
|
|
171
211
|
}
|
|
172
212
|
/**
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
|
|
213
|
+
* §20.1.2.3.32 `a:sysClr` — the host system's own colour. `val` names it and
|
|
214
|
+
* `lastClr` carries what the producing application resolved it to, which is
|
|
215
|
+
* what a reader uses; the two names that matter without one are the window
|
|
216
|
+
* background and its text.
|
|
217
|
+
*/
|
|
218
|
+
var SYSTEM_COLORS = new Map([
|
|
219
|
+
["window", "FFFFFF"],
|
|
220
|
+
["windowText", "000000"],
|
|
221
|
+
["windowFrame", "000000"],
|
|
222
|
+
["btnText", "000000"],
|
|
223
|
+
["btnFace", "F0F0F0"],
|
|
224
|
+
["highlight", "0078D7"],
|
|
225
|
+
["highlightText", "FFFFFF"]
|
|
226
|
+
]);
|
|
227
|
+
/**
|
|
228
|
+
* Resolve an `a:srgbClr` / `a:schemeClr` / `a:sysClr` node to a hex string (with
|
|
229
|
+
* colour transforms applied). Returns `undefined` when the node is some other
|
|
230
|
+
* element, valueless, or the resolver does not know the colour. Container
|
|
231
|
+
* traversal policy (stop at the first colour node vs continue past unresolved
|
|
232
|
+
* ones) stays with the callers.
|
|
178
233
|
*
|
|
179
234
|
* @param c The candidate colour node.
|
|
180
235
|
* @param resolveColor The resolver mapping a {@link RawColor} to hex.
|
|
181
236
|
* @returns The resolved RRGGBB, or `undefined`.
|
|
182
237
|
*/
|
|
183
238
|
function resolveColorNode(c, resolveColor) {
|
|
239
|
+
if (poIs(c, "a:sysClr")) {
|
|
240
|
+
const last = poAttr(c, "lastClr");
|
|
241
|
+
if (last && /^[0-9A-Fa-f]{6}$/.test(last)) return last.toUpperCase();
|
|
242
|
+
return SYSTEM_COLORS.get(poAttr(c, "val") ?? "");
|
|
243
|
+
}
|
|
244
|
+
if (poIs(c, "a:prstClr")) {
|
|
245
|
+
const hex = PRESET_COLORS.get((poAttr(c, "val") ?? "").toLowerCase());
|
|
246
|
+
if (hex === void 0) return void 0;
|
|
247
|
+
const presetMods = readColorMods(c);
|
|
248
|
+
return presetMods.length > 0 ? applyColorMods(hex, presetMods) : hex;
|
|
249
|
+
}
|
|
184
250
|
const isSrgb = poIs(c, "a:srgbClr");
|
|
185
251
|
if (!isSrgb && !poIs(c, "a:schemeClr")) return void 0;
|
|
186
252
|
const v = poAttr(c, "val");
|
|
@@ -193,4 +259,4 @@ function resolveColorNode(c, resolveColor) {
|
|
|
193
259
|
} : raw);
|
|
194
260
|
}
|
|
195
261
|
//#endregion
|
|
196
|
-
export { DEFAULT_THEME_PALETTE, defaultColorResolver, makeColorResolver, resolveColorNode };
|
|
262
|
+
export { DEFAULT_THEME_PALETTE, applyColorMods, defaultColorResolver, makeColorResolver, readColorMods, resolveColorNode };
|