@rowsncolumns/toolkit 24.4.2 → 24.4.4
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/cjs/excel-parser/cfb-decryptor.d.ts +6 -0
- package/dist/cjs/excel-parser/cfb-decryptor.d.ts.map +1 -1
- package/dist/cjs/excel-parser/cfb-decryptor.js +6 -0
- package/dist/cjs/excel-parser/helpers.d.ts +9 -1
- package/dist/cjs/excel-parser/helpers.d.ts.map +1 -1
- package/dist/cjs/excel-parser/helpers.js +37 -0
- package/dist/cjs/excel-parser/interface.d.ts +15 -2
- package/dist/cjs/excel-parser/interface.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/base/index.d.ts +2 -0
- package/dist/cjs/excel-parser/plugins/base/index.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/base/index.js +3 -0
- package/dist/cjs/excel-parser/plugins/ods/index.d.ts +19 -0
- package/dist/cjs/excel-parser/plugins/ods/index.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/ods/index.js +676 -304
- package/dist/cjs/excel-parser/plugins/ods/styles.d.ts +43 -1
- package/dist/cjs/excel-parser/plugins/ods/styles.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/ods/styles.js +343 -45
- package/dist/cjs/excel-parser/plugins/xls/index.d.ts +55 -1
- package/dist/cjs/excel-parser/plugins/xls/index.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xls/index.js +518 -6
- package/dist/cjs/excel-parser/plugins/xlsb/index.d.ts +4 -0
- package/dist/cjs/excel-parser/plugins/xlsb/index.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xlsb/index.js +33 -14
- package/dist/cjs/excel-parser/plugins/xlsx/chartex.d.ts +37 -0
- package/dist/cjs/excel-parser/plugins/xlsx/chartex.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/chartex.js +244 -0
- package/dist/cjs/excel-parser/plugins/xlsx/charts.d.ts +10 -0
- package/dist/cjs/excel-parser/plugins/xlsx/charts.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xlsx/charts.js +68 -9
- package/dist/cjs/excel-parser/plugins/xlsx/drawing.d.ts +28 -1
- package/dist/cjs/excel-parser/plugins/xlsx/drawing.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xlsx/drawing.js +155 -20
- package/dist/cjs/excel-parser/plugins/xlsx/form-controls.d.ts +70 -0
- package/dist/cjs/excel-parser/plugins/xlsx/form-controls.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/form-controls.js +295 -0
- package/dist/cjs/excel-parser/plugins/xlsx/index.d.ts +35 -2
- package/dist/cjs/excel-parser/plugins/xlsx/index.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xlsx/index.js +737 -257
- package/dist/cjs/excel-parser/plugins/xlsx/pivots.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xlsx/pivots.js +125 -37
- package/dist/cjs/excel-parser/plugins/xlsx/sheet-views.d.ts +3 -0
- package/dist/cjs/excel-parser/plugins/xlsx/sheet-views.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xlsx/sheet-views.js +4 -1
- package/dist/cjs/excel-parser/plugins/xlsx/styles.d.ts +15 -1
- package/dist/cjs/excel-parser/plugins/xlsx/styles.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xlsx/styles.js +102 -6
- package/dist/cjs/excel-parser/registry.d.ts +7 -0
- package/dist/cjs/excel-parser/registry.d.ts.map +1 -1
- package/dist/cjs/excel-parser/registry.js +29 -0
- package/dist/cjs/excel-parser/types.d.ts +17 -2
- package/dist/cjs/excel-parser/types.d.ts.map +1 -1
- package/dist/cjs/excel-parser/workbook.d.ts +3 -0
- package/dist/cjs/excel-parser/workbook.d.ts.map +1 -1
- package/dist/cjs/excel-parser/workbook.js +24 -14
- package/dist/cjs/excel-parser/worker.js +131 -131
- package/dist/cjs/exporter/ods/ods-export.d.ts.map +1 -1
- package/dist/cjs/exporter/ods/ods-export.js +432 -175
- package/dist/cjs/exporter/xlsx/chart-builder.d.ts.map +1 -1
- package/dist/cjs/exporter/xlsx/chart-builder.js +215 -39
- package/dist/cjs/exporter/xlsx/chartex-builder.d.ts +36 -0
- package/dist/cjs/exporter/xlsx/chartex-builder.d.ts.map +1 -0
- package/dist/cjs/exporter/xlsx/chartex-builder.js +109 -0
- package/dist/cjs/exporter/xlsx/form-controls-builder.d.ts +61 -0
- package/dist/cjs/exporter/xlsx/form-controls-builder.d.ts.map +1 -0
- package/dist/cjs/exporter/xlsx/form-controls-builder.js +197 -0
- package/dist/cjs/exporter/xlsx/pivot-builder.d.ts +8 -0
- package/dist/cjs/exporter/xlsx/pivot-builder.d.ts.map +1 -1
- package/dist/cjs/exporter/xlsx/pivot-builder.js +141 -42
- package/dist/cjs/exporter/xlsx/style-builder.js +9 -3
- package/dist/cjs/exporter/xlsx/threaded-comments-builder.d.ts +7 -1
- package/dist/cjs/exporter/xlsx/threaded-comments-builder.d.ts.map +1 -1
- package/dist/cjs/exporter/xlsx/threaded-comments-builder.js +15 -3
- package/dist/cjs/exporter/xlsx/xlsx-export.d.ts +11 -2
- package/dist/cjs/exporter/xlsx/xlsx-export.d.ts.map +1 -1
- package/dist/cjs/exporter/xlsx/xlsx-export.js +991 -297
- package/dist/cjs/helpers/implicit-intersection.d.ts +16 -0
- package/dist/cjs/helpers/implicit-intersection.d.ts.map +1 -0
- package/dist/cjs/helpers/implicit-intersection.js +197 -0
- package/dist/cjs/print-settings.d.ts +105 -0
- package/dist/cjs/print-settings.d.ts.map +1 -0
- package/dist/cjs/print-settings.js +494 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/excel-parser/cfb-decryptor.d.ts +6 -0
- package/dist/esm/excel-parser/cfb-decryptor.d.ts.map +1 -1
- package/dist/esm/excel-parser/cfb-decryptor.js +6 -0
- package/dist/esm/excel-parser/helpers.d.ts +9 -1
- package/dist/esm/excel-parser/helpers.d.ts.map +1 -1
- package/dist/esm/excel-parser/helpers.js +34 -0
- package/dist/esm/excel-parser/interface.d.ts +15 -2
- package/dist/esm/excel-parser/interface.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/base/index.d.ts +2 -0
- package/dist/esm/excel-parser/plugins/base/index.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/base/index.js +3 -0
- package/dist/esm/excel-parser/plugins/ods/index.d.ts +19 -0
- package/dist/esm/excel-parser/plugins/ods/index.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/ods/index.js +347 -33
- package/dist/esm/excel-parser/plugins/ods/styles.d.ts +43 -1
- package/dist/esm/excel-parser/plugins/ods/styles.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/ods/styles.js +276 -2
- package/dist/esm/excel-parser/plugins/xls/index.d.ts +55 -1
- package/dist/esm/excel-parser/plugins/xls/index.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xls/index.js +441 -3
- package/dist/esm/excel-parser/plugins/xlsb/index.d.ts +4 -0
- package/dist/esm/excel-parser/plugins/xlsb/index.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xlsb/index.js +30 -9
- package/dist/esm/excel-parser/plugins/xlsx/chartex.d.ts +37 -0
- package/dist/esm/excel-parser/plugins/xlsx/chartex.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/chartex.js +199 -0
- package/dist/esm/excel-parser/plugins/xlsx/charts.d.ts +10 -0
- package/dist/esm/excel-parser/plugins/xlsx/charts.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xlsx/charts.js +69 -6
- package/dist/esm/excel-parser/plugins/xlsx/drawing.d.ts +28 -1
- package/dist/esm/excel-parser/plugins/xlsx/drawing.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xlsx/drawing.js +99 -0
- package/dist/esm/excel-parser/plugins/xlsx/form-controls.d.ts +70 -0
- package/dist/esm/excel-parser/plugins/xlsx/form-controls.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/form-controls.js +246 -0
- package/dist/esm/excel-parser/plugins/xlsx/index.d.ts +35 -2
- package/dist/esm/excel-parser/plugins/xlsx/index.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xlsx/index.js +454 -32
- package/dist/esm/excel-parser/plugins/xlsx/pivots.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xlsx/pivots.js +86 -6
- package/dist/esm/excel-parser/plugins/xlsx/sheet-views.d.ts +3 -0
- package/dist/esm/excel-parser/plugins/xlsx/sheet-views.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xlsx/sheet-views.js +2 -0
- package/dist/esm/excel-parser/plugins/xlsx/styles.d.ts +15 -1
- package/dist/esm/excel-parser/plugins/xlsx/styles.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xlsx/styles.js +92 -2
- package/dist/esm/excel-parser/registry.d.ts +7 -0
- package/dist/esm/excel-parser/registry.d.ts.map +1 -1
- package/dist/esm/excel-parser/registry.js +19 -0
- package/dist/esm/excel-parser/types.d.ts +17 -2
- package/dist/esm/excel-parser/types.d.ts.map +1 -1
- package/dist/esm/excel-parser/workbook.d.ts +3 -0
- package/dist/esm/excel-parser/workbook.d.ts.map +1 -1
- package/dist/esm/excel-parser/workbook.js +19 -10
- package/dist/esm/excel-parser/worker.js +12 -18
- package/dist/esm/exporter/ods/ods-export.d.ts.map +1 -1
- package/dist/esm/exporter/ods/ods-export.js +306 -35
- package/dist/esm/exporter/xlsx/chart-builder.d.ts.map +1 -1
- package/dist/esm/exporter/xlsx/chart-builder.js +161 -28
- package/dist/esm/exporter/xlsx/chartex-builder.d.ts +36 -0
- package/dist/esm/exporter/xlsx/chartex-builder.d.ts.map +1 -0
- package/dist/esm/exporter/xlsx/chartex-builder.js +97 -0
- package/dist/esm/exporter/xlsx/form-controls-builder.d.ts +61 -0
- package/dist/esm/exporter/xlsx/form-controls-builder.d.ts.map +1 -0
- package/dist/esm/exporter/xlsx/form-controls-builder.js +173 -0
- package/dist/esm/exporter/xlsx/pivot-builder.d.ts +8 -0
- package/dist/esm/exporter/xlsx/pivot-builder.d.ts.map +1 -1
- package/dist/esm/exporter/xlsx/pivot-builder.js +104 -19
- package/dist/esm/exporter/xlsx/style-builder.js +9 -3
- package/dist/esm/exporter/xlsx/threaded-comments-builder.d.ts +7 -1
- package/dist/esm/exporter/xlsx/threaded-comments-builder.d.ts.map +1 -1
- package/dist/esm/exporter/xlsx/threaded-comments-builder.js +15 -3
- package/dist/esm/exporter/xlsx/xlsx-export.d.ts +11 -2
- package/dist/esm/exporter/xlsx/xlsx-export.d.ts.map +1 -1
- package/dist/esm/exporter/xlsx/xlsx-export.js +675 -64
- package/dist/esm/helpers/implicit-intersection.d.ts +16 -0
- package/dist/esm/helpers/implicit-intersection.d.ts.map +1 -0
- package/dist/esm/helpers/implicit-intersection.js +184 -0
- package/dist/esm/print-settings.d.ts +105 -0
- package/dist/esm/print-settings.d.ts.map +1 -0
- package/dist/esm/print-settings.js +422 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/xlsx.d.ts +14 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { generateSelectionsFromFormula, uuid } from "@rowsncolumns/utils";
|
|
2
|
+
import { get } from "lodash-es";
|
|
3
|
+
import { extractChartPosition, } from "./charts";
|
|
4
|
+
/**
|
|
5
|
+
* Excel 2016+ "chartex" charts (`xl/charts/chartEx{N}.xml`, namespace
|
|
6
|
+
* `http://schemas.microsoft.com/office/drawing/2014/chartex`): treemap,
|
|
7
|
+
* sunburst, histogram / pareto, box & whisker, waterfall, funnel. They are
|
|
8
|
+
* anchored through an `mc:AlternateContent` whose `Choice` graphic frame
|
|
9
|
+
* carries `<cx:chart r:id>` instead of `<c:chart>`, and their data lives in
|
|
10
|
+
* `<cx:chartData><cx:data>` dimensions (`strDim type="cat"` for the
|
|
11
|
+
* hierarchy, `numDim type="size"|"val"` for the numbers) referenced by the
|
|
12
|
+
* series through `<cx:dataId>`.
|
|
13
|
+
*
|
|
14
|
+
* The exporter counterpart is `exporter/xlsx/chartex-builder.ts`.
|
|
15
|
+
*/
|
|
16
|
+
export const CHART_EX_REL_TYPE = "http://schemas.microsoft.com/office/2014/relationships/chartEx";
|
|
17
|
+
export const CHART_EX_GRAPHIC_URI = "http://schemas.microsoft.com/office/drawing/2014/chartex";
|
|
18
|
+
const asArray = (value) => value == null ? [] : Array.isArray(value) ? value : [value];
|
|
19
|
+
/** Read a child by local name, tolerating any namespace prefix. */
|
|
20
|
+
const child = (node, localName) => {
|
|
21
|
+
if (!node || typeof node !== "object")
|
|
22
|
+
return undefined;
|
|
23
|
+
if (node[localName] !== undefined)
|
|
24
|
+
return node[localName];
|
|
25
|
+
for (const key of Object.keys(node)) {
|
|
26
|
+
const colon = key.indexOf(":");
|
|
27
|
+
if (colon >= 0 && key.slice(colon + 1) === localName)
|
|
28
|
+
return node[key];
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
};
|
|
32
|
+
const textOf = (node) => {
|
|
33
|
+
if (node == null)
|
|
34
|
+
return undefined;
|
|
35
|
+
if (typeof node === "object") {
|
|
36
|
+
const inner = node.t$;
|
|
37
|
+
return inner == null ? undefined : String(inner);
|
|
38
|
+
}
|
|
39
|
+
return String(node);
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* `<cx:chart r:id>` inside an anchor's `mc:AlternateContent` choice (Excel
|
|
43
|
+
* also writes a plain `xdr:graphicFrame` for some producers — both forms are
|
|
44
|
+
* checked). Returns the `cx:chart` node or undefined for non-chartex anchors.
|
|
45
|
+
*/
|
|
46
|
+
export function getChartExReferenceFromAnchor(anchor) {
|
|
47
|
+
const frames = [];
|
|
48
|
+
for (const alternate of asArray(child(anchor, "AlternateContent"))) {
|
|
49
|
+
for (const choice of asArray(child(alternate, "Choice"))) {
|
|
50
|
+
frames.push(...asArray(child(choice, "graphicFrame")));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
frames.push(...asArray(child(anchor, "graphicFrame")));
|
|
54
|
+
for (const frame of frames) {
|
|
55
|
+
const graphicData = child(child(frame, "graphic"), "graphicData");
|
|
56
|
+
if (!graphicData)
|
|
57
|
+
continue;
|
|
58
|
+
const uri = get(graphicData, "a$.uri");
|
|
59
|
+
const cxChart = child(graphicData, "chart");
|
|
60
|
+
if (uri === CHART_EX_GRAPHIC_URI && cxChart)
|
|
61
|
+
return cxChart;
|
|
62
|
+
// A `cx:`-prefixed chart element without the uri attribute (defensive).
|
|
63
|
+
if (cxChart && Object.keys(graphicData).some((k) => k.startsWith("cx:"))) {
|
|
64
|
+
return cxChart;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
/** `layoutId` → engine chart type. Unknown layouts are not imported. */
|
|
70
|
+
const CHART_TYPE_BY_LAYOUT = {
|
|
71
|
+
treemap: "treemap",
|
|
72
|
+
sunburst: "sunburst",
|
|
73
|
+
waterfall: "waterfall",
|
|
74
|
+
funnel: "funnel",
|
|
75
|
+
boxWhisker: "boxplot",
|
|
76
|
+
// `clusteredColumn` is a histogram / pareto only when it carries `<cx:binning>`.
|
|
77
|
+
};
|
|
78
|
+
const sourcesFromRef = (ref, hostSheetId, resolveSheetId) => {
|
|
79
|
+
if (!ref)
|
|
80
|
+
return [];
|
|
81
|
+
const selections = generateSelectionsFromFormula(`=${ref}`);
|
|
82
|
+
return selections.map((sel) => ({
|
|
83
|
+
...sel.range,
|
|
84
|
+
sheetId: (sel.sheetName ? resolveSheetId?.(sel.sheetName) : undefined) ??
|
|
85
|
+
hostSheetId,
|
|
86
|
+
}));
|
|
87
|
+
};
|
|
88
|
+
const chartData = (sources) => ({ sources });
|
|
89
|
+
/**
|
|
90
|
+
* Split a multi-column hierarchy range (Excel lays the treemap hierarchy out
|
|
91
|
+
* as columns, top level first, leaf last) into `parentLabels` (one entry per
|
|
92
|
+
* parent column) and `labels` (the leaf column).
|
|
93
|
+
*/
|
|
94
|
+
export const splitHierarchyRange = (range) => {
|
|
95
|
+
const parentLabels = [];
|
|
96
|
+
for (let c = range.startColumnIndex; c < range.endColumnIndex; c++) {
|
|
97
|
+
parentLabels.push({ ...range, startColumnIndex: c, endColumnIndex: c });
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
parentLabels,
|
|
101
|
+
labels: {
|
|
102
|
+
...range,
|
|
103
|
+
startColumnIndex: range.endColumnIndex,
|
|
104
|
+
endColumnIndex: range.endColumnIndex,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
const dimensionRef = (data, dimTag, type) => {
|
|
109
|
+
const dims = asArray(child(data, dimTag)).filter((dim) => get(dim, "a$.type") === type);
|
|
110
|
+
const dim = dims[0] ?? (dimTag === "numDim" ? asArray(child(data, dimTag))[0] : undefined);
|
|
111
|
+
const f = child(dim, "f");
|
|
112
|
+
return textOf(Array.isArray(f) ? f[0] : f)?.trim() || undefined;
|
|
113
|
+
};
|
|
114
|
+
/** Parse a `cx:chartSpace` DOM into an `EmbeddedChart` spec (null when unsupported). */
|
|
115
|
+
export function parseChartExSpec(chartSpaceDom, sheetId, resolveSheetId) {
|
|
116
|
+
const chartSpace = child(chartSpaceDom, "chartSpace") ?? chartSpaceDom;
|
|
117
|
+
const chart = child(chartSpace, "chart");
|
|
118
|
+
if (!chart)
|
|
119
|
+
return null;
|
|
120
|
+
const region = child(child(chart, "plotArea"), "plotAreaRegion");
|
|
121
|
+
const series = asArray(child(region, "series"))[0];
|
|
122
|
+
if (!series)
|
|
123
|
+
return null;
|
|
124
|
+
const layoutId = String(get(series, "a$.layoutId") ?? "");
|
|
125
|
+
// Excel nests `<cx:binning>` in `<cx:layoutPr>`; tolerate it directly on the series too.
|
|
126
|
+
const binning = child(child(series, "layoutPr"), "binning") ?? child(series, "binning");
|
|
127
|
+
const chartType = layoutId === "clusteredColumn" && binning
|
|
128
|
+
? "histogram"
|
|
129
|
+
: CHART_TYPE_BY_LAYOUT[layoutId];
|
|
130
|
+
if (!chartType)
|
|
131
|
+
return null;
|
|
132
|
+
const dataId = String(get(child(series, "dataId"), "a$.val") ?? "0");
|
|
133
|
+
const data = asArray(child(child(chartSpace, "chartData"), "data")).find((d) => String(get(d, "a$.id")) === dataId) ?? asArray(child(child(chartSpace, "chartData"), "data"))[0];
|
|
134
|
+
if (!data)
|
|
135
|
+
return null;
|
|
136
|
+
const title = textOf(child(child(child(child(chart, "title"), "tx"), "txData"), "v"));
|
|
137
|
+
const catRef = dimensionRef(data, "strDim", "cat");
|
|
138
|
+
const catSources = sourcesFromRef(catRef, sheetId, resolveSheetId);
|
|
139
|
+
if (chartType === "treemap" || chartType === "sunburst") {
|
|
140
|
+
const sizeRef = dimensionRef(data, "numDim", "size");
|
|
141
|
+
const sizeSources = sourcesFromRef(sizeRef, sheetId, resolveSheetId);
|
|
142
|
+
if (!catSources.length || !sizeSources.length)
|
|
143
|
+
return null;
|
|
144
|
+
const hierarchy = splitHierarchyRange(catSources[0]);
|
|
145
|
+
return {
|
|
146
|
+
chartType,
|
|
147
|
+
...(title ? { title } : {}),
|
|
148
|
+
labels: [chartData([hierarchy.labels])],
|
|
149
|
+
parentLabels: hierarchy.parentLabels.map((range) => chartData([range])),
|
|
150
|
+
sizeData: [chartData(sizeSources)],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const valRef = dimensionRef(data, "numDim", "val");
|
|
154
|
+
const valSources = sourcesFromRef(valRef, sheetId, resolveSheetId);
|
|
155
|
+
if (!valSources.length)
|
|
156
|
+
return null;
|
|
157
|
+
const spec = {
|
|
158
|
+
chartType,
|
|
159
|
+
...(title ? { title } : {}),
|
|
160
|
+
series: [chartData(valSources)],
|
|
161
|
+
...(catSources.length ? { domains: [chartData(catSources)] } : {}),
|
|
162
|
+
};
|
|
163
|
+
if (chartType === "histogram") {
|
|
164
|
+
const binCount = Number(get(child(binning, "binCount"), "a$.val"));
|
|
165
|
+
if (Number.isFinite(binCount) && binCount > 0)
|
|
166
|
+
spec.binCount = binCount;
|
|
167
|
+
}
|
|
168
|
+
return spec;
|
|
169
|
+
}
|
|
170
|
+
/** Resolve + parse one chartex part referenced from a drawing anchor. */
|
|
171
|
+
export async function processChartEx(chartTarget, drawingPath, anchor, sheetId, zipParser, xmlParser, sheetDimensions, resolveSheetId) {
|
|
172
|
+
let chartPath = chartTarget;
|
|
173
|
+
const drawingDir = drawingPath.substring(0, drawingPath.lastIndexOf("/"));
|
|
174
|
+
if (chartPath.startsWith("/")) {
|
|
175
|
+
chartPath = chartPath.slice(1);
|
|
176
|
+
}
|
|
177
|
+
else if (chartPath.startsWith("../")) {
|
|
178
|
+
chartPath = `xl/${chartPath.replace("../", "")}`;
|
|
179
|
+
}
|
|
180
|
+
else if (!chartPath.startsWith("xl/")) {
|
|
181
|
+
chartPath = `${drawingDir}/${chartPath}`;
|
|
182
|
+
}
|
|
183
|
+
let content;
|
|
184
|
+
try {
|
|
185
|
+
content = await zipParser.getText(chartPath);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
if (!content)
|
|
191
|
+
return null;
|
|
192
|
+
const spec = parseChartExSpec(xmlParser.parse(content), sheetId, resolveSheetId);
|
|
193
|
+
if (!spec)
|
|
194
|
+
return null;
|
|
195
|
+
const position = extractChartPosition(anchor, sheetId, sheetDimensions);
|
|
196
|
+
if (!position)
|
|
197
|
+
return null;
|
|
198
|
+
return { chartId: uuid(), spec, position };
|
|
199
|
+
}
|
|
@@ -17,6 +17,16 @@ export declare function extractChartDataRange(plotArea: any, chartType: string,
|
|
|
17
17
|
export declare function extractHiddenDimensionStrategy(chart: any): HIDDEN_DIMENSION_STRATEGY | undefined;
|
|
18
18
|
export declare function extractChartTitle(chart: any): string;
|
|
19
19
|
export declare function determineChartType(chart: any): "bar" | "column" | "line" | "pie" | "doughnut" | "area" | "radar" | "stock" | "scatter" | "bubble" | "treemap" | "sunburst" | "histogram" | "boxplot" | "waterfall" | "funnel";
|
|
20
|
+
/**
|
|
21
|
+
* The authored 3-D / surface chart class, when the plot area's chart
|
|
22
|
+
* element is one of Excel's 3-D variants. These degrade to their 2-D
|
|
23
|
+
* `chartType` (bar3DChart → bar/column, line3DChart → line, pie3DChart →
|
|
24
|
+
* pie, area3DChart / surfaceChart / surface3DChart → area) and the
|
|
25
|
+
* original class is preserved verbatim as `sourceChartType`, matching the
|
|
26
|
+
* Rust importer, so the renderer's echarts-gl pipeline and the exporter's
|
|
27
|
+
* 3-D writer can restore it. Undefined for plain 2-D charts.
|
|
28
|
+
*/
|
|
29
|
+
export declare function extractThreeDSourceChartType(plotArea: any): "BAR_3D" | "LINE_3D" | "PIE_3D" | "AREA_3D" | "SURFACE" | "SURFACE_3D" | undefined;
|
|
20
30
|
/**
|
|
21
31
|
* Phase 5.5 — pull binCount out of the c15:histogramChart extension
|
|
22
32
|
* marker. Returns undefined if absent.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"charts.d.ts","sourceRoot":"","sources":["../../../../../excel-parser/plugins/xlsx/charts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,SAAS,EAET,aAAa,EACb,sBAAsB,EACtB,yBAAyB,EACzB,UAAU,EACX,MAAM,2BAA2B,CAAC;AAInC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAErE;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,GAAG,SAAS,CAAC;AAkDnF,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,kBAAkB,EAC7B,SAAS,EAAE,GAAG,EACd,eAAe,CAAC,EAAE,eAAe,EACjC,cAAc,CAAC,EAAE,cAAc,GAC9B,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"charts.d.ts","sourceRoot":"","sources":["../../../../../excel-parser/plugins/xlsx/charts.ts"],"names":[],"mappings":"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"}
|
|
@@ -120,6 +120,10 @@ export async function processChart(chartTarget, drawingPath, anchor, sheetId, zi
|
|
|
120
120
|
// "pie" with sourceChartType "OF_PIE" plus the split fields the
|
|
121
121
|
// renderer uses to draw the composite layout.
|
|
122
122
|
const ofPieOptions = chartType === "pie" ? extractOfPieOptions(plotArea) : undefined;
|
|
123
|
+
// 3-D / surface variants — preserved as sourceChartType (BAR_3D,
|
|
124
|
+
// LINE_3D, PIE_3D, AREA_3D, SURFACE, SURFACE_3D) on top of the 2-D
|
|
125
|
+
// chartType they degrade to. Disjoint from OF_PIE.
|
|
126
|
+
const threeDSourceChartType = extractThreeDSourceChartType(plotArea);
|
|
123
127
|
const radarStyle = chartType === "radar" ? extractRadarStyle(plotArea) : undefined;
|
|
124
128
|
const binCount = chartType === "histogram"
|
|
125
129
|
? extractC15HistogramBinCount(plotArea)
|
|
@@ -166,6 +170,9 @@ export async function processChart(chartTarget, drawingPath, anchor, sheetId, zi
|
|
|
166
170
|
...(ofPieOptions
|
|
167
171
|
? { sourceChartType: "OF_PIE", ...ofPieOptions }
|
|
168
172
|
: {}),
|
|
173
|
+
...(threeDSourceChartType && !ofPieOptions
|
|
174
|
+
? { sourceChartType: threeDSourceChartType }
|
|
175
|
+
: {}),
|
|
169
176
|
...(radarStyle ? { radarStyle } : {}),
|
|
170
177
|
...(typeof binCount === "number" ? { binCount } : {}),
|
|
171
178
|
...(barLayout?.stackedType
|
|
@@ -240,6 +247,12 @@ function getSeriesElements(plotArea, chartType) {
|
|
|
240
247
|
"areaChart.ser",
|
|
241
248
|
"c:area3DChart.c:ser",
|
|
242
249
|
"area3DChart.ser",
|
|
250
|
+
// Surface / contour charts degrade to area (sourceChartType
|
|
251
|
+
// SURFACE / SURFACE_3D); their series carry the same cat/val shape.
|
|
252
|
+
"c:surfaceChart.c:ser",
|
|
253
|
+
"surfaceChart.ser",
|
|
254
|
+
"c:surface3DChart.c:ser",
|
|
255
|
+
"surface3DChart.ser",
|
|
243
256
|
]);
|
|
244
257
|
case "radar":
|
|
245
258
|
return getWithFallback(plotArea, ["c:radarChart.c:ser", "radarChart.ser"]);
|
|
@@ -562,6 +575,12 @@ export function determineChartType(chart) {
|
|
|
562
575
|
"areaChart",
|
|
563
576
|
"c:area3DChart",
|
|
564
577
|
"area3DChart",
|
|
578
|
+
// Surface / contour degrade to area — the same 2-D base the Rust
|
|
579
|
+
// importer and the renderer's surface pipeline use.
|
|
580
|
+
"c:surfaceChart",
|
|
581
|
+
"surfaceChart",
|
|
582
|
+
"c:surface3DChart",
|
|
583
|
+
"surface3DChart",
|
|
565
584
|
]))
|
|
566
585
|
return "area";
|
|
567
586
|
if (getWithFallback(plotArea, ["c:radarChart", "radarChart"]))
|
|
@@ -576,6 +595,31 @@ export function determineChartType(chart) {
|
|
|
576
595
|
return "bubble";
|
|
577
596
|
return "column"; // default
|
|
578
597
|
}
|
|
598
|
+
/**
|
|
599
|
+
* The authored 3-D / surface chart class, when the plot area's chart
|
|
600
|
+
* element is one of Excel's 3-D variants. These degrade to their 2-D
|
|
601
|
+
* `chartType` (bar3DChart → bar/column, line3DChart → line, pie3DChart →
|
|
602
|
+
* pie, area3DChart / surfaceChart / surface3DChart → area) and the
|
|
603
|
+
* original class is preserved verbatim as `sourceChartType`, matching the
|
|
604
|
+
* Rust importer, so the renderer's echarts-gl pipeline and the exporter's
|
|
605
|
+
* 3-D writer can restore it. Undefined for plain 2-D charts.
|
|
606
|
+
*/
|
|
607
|
+
export function extractThreeDSourceChartType(plotArea) {
|
|
608
|
+
const has = (paths) => Boolean(getWithFallback(plotArea, paths));
|
|
609
|
+
if (has(["c:bar3DChart", "bar3DChart"]))
|
|
610
|
+
return "BAR_3D";
|
|
611
|
+
if (has(["c:line3DChart", "line3DChart"]))
|
|
612
|
+
return "LINE_3D";
|
|
613
|
+
if (has(["c:pie3DChart", "pie3DChart"]))
|
|
614
|
+
return "PIE_3D";
|
|
615
|
+
if (has(["c:area3DChart", "area3DChart"]))
|
|
616
|
+
return "AREA_3D";
|
|
617
|
+
if (has(["c:surface3DChart", "surface3DChart"]))
|
|
618
|
+
return "SURFACE_3D";
|
|
619
|
+
if (has(["c:surfaceChart", "surfaceChart"]))
|
|
620
|
+
return "SURFACE";
|
|
621
|
+
return undefined;
|
|
622
|
+
}
|
|
579
623
|
/**
|
|
580
624
|
* Phase 5.5 — detect the c15 extension marker our exporter writes on
|
|
581
625
|
* the plotArea. Returns the originating chart type (histogram /
|
|
@@ -711,7 +755,16 @@ const CHART_TYPE_CONTAINERS = [
|
|
|
711
755
|
chartType: "line",
|
|
712
756
|
},
|
|
713
757
|
{
|
|
714
|
-
paths: [
|
|
758
|
+
paths: [
|
|
759
|
+
"c:areaChart",
|
|
760
|
+
"areaChart",
|
|
761
|
+
"c:area3DChart",
|
|
762
|
+
"area3DChart",
|
|
763
|
+
"c:surfaceChart",
|
|
764
|
+
"surfaceChart",
|
|
765
|
+
"c:surface3DChart",
|
|
766
|
+
"surface3DChart",
|
|
767
|
+
],
|
|
715
768
|
chartType: "area",
|
|
716
769
|
},
|
|
717
770
|
{
|
|
@@ -1765,13 +1818,23 @@ const mapDataLabelPosition = (val) => {
|
|
|
1765
1818
|
function extractDataLabelsOptions(plotArea, chartType) {
|
|
1766
1819
|
// Look for <c:dLbls> inside the chartType-specific container. We pick
|
|
1767
1820
|
// the first one we find — chart-level labels apply to all series.
|
|
1821
|
+
// The 3-D classes (bar3DChart / line3DChart / pie3DChart / area3DChart)
|
|
1822
|
+
// degrade to the same `chartType` and carry `<c:dLbls>` in the same slot
|
|
1823
|
+
// of their group, so they are looked up alongside the 2-D container.
|
|
1768
1824
|
const containerKeys = {
|
|
1769
|
-
bar: ["c:barChart", "barChart"],
|
|
1770
|
-
column: ["c:barChart", "barChart"],
|
|
1771
|
-
line: ["c:lineChart", "lineChart"],
|
|
1772
|
-
pie: [
|
|
1825
|
+
bar: ["c:barChart", "barChart", "c:bar3DChart", "bar3DChart"],
|
|
1826
|
+
column: ["c:barChart", "barChart", "c:bar3DChart", "bar3DChart"],
|
|
1827
|
+
line: ["c:lineChart", "lineChart", "c:line3DChart", "line3DChart"],
|
|
1828
|
+
pie: [
|
|
1829
|
+
"c:pieChart",
|
|
1830
|
+
"pieChart",
|
|
1831
|
+
"c:ofPieChart",
|
|
1832
|
+
"ofPieChart",
|
|
1833
|
+
"c:pie3DChart",
|
|
1834
|
+
"pie3DChart",
|
|
1835
|
+
],
|
|
1773
1836
|
doughnut: ["c:doughnutChart", "doughnutChart"],
|
|
1774
|
-
area: ["c:areaChart", "areaChart"],
|
|
1837
|
+
area: ["c:areaChart", "areaChart", "c:area3DChart", "area3DChart"],
|
|
1775
1838
|
};
|
|
1776
1839
|
const keys = containerKeys[chartType] ?? [];
|
|
1777
1840
|
let container;
|
|
@@ -1,9 +1,28 @@
|
|
|
1
|
-
import type { EmbeddedObject, EmbedShapeSpec } from "@rowsncolumns/spreadsheet";
|
|
1
|
+
import type { EmbeddedObject, EmbedImageSpec, EmbedShapeSpec } from "@rowsncolumns/spreadsheet";
|
|
2
2
|
import { type XMLParser } from "fast-xml-parser";
|
|
3
3
|
import { type StreamingZipParser } from "../../zip-parser-streaming";
|
|
4
4
|
export type EmbeddedObjectWithPosition = EmbeddedObject & {
|
|
5
5
|
isInsideCell?: boolean;
|
|
6
6
|
};
|
|
7
|
+
/** `processAnchors` anchor-type tags, keyed by the drawing element name. */
|
|
8
|
+
declare const ANCHOR_KIND_BY_ELEMENT: {
|
|
9
|
+
readonly oneCellAnchor: "oneCellAnchor";
|
|
10
|
+
readonly twoCellAnchor: "twoCellAnchors";
|
|
11
|
+
readonly absoluteAnchor: "absoluteAnchors";
|
|
12
|
+
};
|
|
13
|
+
type AnchorElementName = keyof typeof ANCHOR_KIND_BY_ELEMENT;
|
|
14
|
+
/**
|
|
15
|
+
* The `<xdr:wsDr>` anchors in document order — Excel's z-order (first is
|
|
16
|
+
* the back-most object). The unordered parse keeps document order within one
|
|
17
|
+
* element name but groups the three anchor kinds apart, so the kind sequence
|
|
18
|
+
* is read off the raw part and the parsed nodes are dealt out in that order.
|
|
19
|
+
* Anchors the scan does not account for (namespace quirks) trail in their
|
|
20
|
+
* parsed order, so nothing is ever dropped.
|
|
21
|
+
*/
|
|
22
|
+
export declare function anchorsInDocumentOrder(drawing: any, drawingXml: string): Array<{
|
|
23
|
+
kind: (typeof ANCHOR_KIND_BY_ELEMENT)[AnchorElementName];
|
|
24
|
+
node: any;
|
|
25
|
+
}>;
|
|
7
26
|
/**
|
|
8
27
|
* Process different types of image anchors
|
|
9
28
|
* @param anchors The anchor elements from the drawing
|
|
@@ -15,6 +34,13 @@ export type EmbeddedObjectWithPosition = EmbeddedObject & {
|
|
|
15
34
|
* @returns An array of image objects with their properties and binary data
|
|
16
35
|
*/
|
|
17
36
|
export declare function processAnchors(anchors: any, anchorType: string, drawingPath: string, sheetId: number, zipParser: StreamingZipParser, xmlParser: XMLParser): Promise<EmbeddedObjectWithPosition[]>;
|
|
37
|
+
/**
|
|
38
|
+
* Picture-format adjustments off an `<xdr:pic>`: `<a:blipFill><a:srcRect>`
|
|
39
|
+
* (crop), `<a:blip><a:lum bright contrast>` (brightness / contrast) and
|
|
40
|
+
* `<a:blip><a:alphaModFix amt>` (opacity → `transparency`). Only present
|
|
41
|
+
* values are returned so an untouched picture stays a bare image spec.
|
|
42
|
+
*/
|
|
43
|
+
export declare function parsePictureAdjustments(picture: any): Pick<EmbedImageSpec, "crop" | "brightness" | "contrast" | "transparency">;
|
|
18
44
|
/**
|
|
19
45
|
* Whether an `<xdr:sp>` should import as a drawing shape (`EmbedShapeSpec`)
|
|
20
46
|
* rather than a textbox. Text boxes are excluded explicitly (`txBox="1"`), and
|
|
@@ -36,4 +62,5 @@ export declare function parseShapeSpec(shape: any): EmbedShapeSpec;
|
|
|
36
62
|
* @returns Boolean indicating if the image is inside a cell
|
|
37
63
|
*/
|
|
38
64
|
export declare function determineIfOneCellInsideCell(fromColOff: number, fromRowOff: number, fLocksWithSheet: string, cx: number, cy: number): boolean;
|
|
65
|
+
export {};
|
|
39
66
|
//# sourceMappingURL=drawing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drawing.d.ts","sourceRoot":"","sources":["../../../../../excel-parser/plugins/xlsx/drawing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,cAAc,EAEd,cAAc,
|
|
1
|
+
{"version":3,"file":"drawing.d.ts","sourceRoot":"","sources":["../../../../../excel-parser/plugins/xlsx/drawing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,cAAc,EAEd,cAAc,EACd,cAAc,EAYf,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGjD,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAoDrE,MAAM,MAAM,0BAA0B,GAAG,cAAc,GAAG;IACxD,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,4EAA4E;AAC5E,QAAA,MAAM,sBAAsB;;;;CAIlB,CAAC;AAEX,KAAK,iBAAiB,GAAG,MAAM,OAAO,sBAAsB,CAAC;AAK7D;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,GAAG,EACZ,UAAU,EAAE,MAAM,GACjB,KAAK,CAAC;IACP,IAAI,EAAE,CAAC,OAAO,sBAAsB,CAAC,CAAC,iBAAiB,CAAC,CAAC;IACzD,IAAI,EAAE,GAAG,CAAC;CACX,CAAC,CAkCD;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,GAAG,EACZ,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,kBAAkB,EAC7B,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,0BAA0B,EAAE,CAAC,CA8dvC;AASD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,GAAG,GACX,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,YAAY,GAAG,UAAU,GAAG,cAAc,CAAC,CAuC3E;AA6OD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CASlD;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,KAAK,EAAE,GAAG,GAAG,cAAc,CA8CzD;AA8MD;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,EACvB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT,OAAO,CAqBT"}
|
|
@@ -41,6 +41,54 @@ const CELL_IMAGE_OFFSET_THRESHOLD_PX = 1;
|
|
|
41
41
|
function emuToPixels(emuValue) {
|
|
42
42
|
return Math.round(emuValue / EMU_PER_PIXEL);
|
|
43
43
|
}
|
|
44
|
+
/** `processAnchors` anchor-type tags, keyed by the drawing element name. */
|
|
45
|
+
const ANCHOR_KIND_BY_ELEMENT = {
|
|
46
|
+
oneCellAnchor: "oneCellAnchor",
|
|
47
|
+
twoCellAnchor: "twoCellAnchors",
|
|
48
|
+
absoluteAnchor: "absoluteAnchors",
|
|
49
|
+
};
|
|
50
|
+
const ANCHOR_ELEMENT_REGEX = /<(?:[A-Za-z_][\w.-]*:)?(oneCellAnchor|twoCellAnchor|absoluteAnchor)(?=[\s/>])/g;
|
|
51
|
+
/**
|
|
52
|
+
* The `<xdr:wsDr>` anchors in document order — Excel's z-order (first is
|
|
53
|
+
* the back-most object). The unordered parse keeps document order within one
|
|
54
|
+
* element name but groups the three anchor kinds apart, so the kind sequence
|
|
55
|
+
* is read off the raw part and the parsed nodes are dealt out in that order.
|
|
56
|
+
* Anchors the scan does not account for (namespace quirks) trail in their
|
|
57
|
+
* parsed order, so nothing is ever dropped.
|
|
58
|
+
*/
|
|
59
|
+
export function anchorsInDocumentOrder(drawing, drawingXml) {
|
|
60
|
+
const nodesByElement = {
|
|
61
|
+
oneCellAnchor: [],
|
|
62
|
+
twoCellAnchor: [],
|
|
63
|
+
absoluteAnchor: [],
|
|
64
|
+
};
|
|
65
|
+
for (const element of Object.keys(nodesByElement)) {
|
|
66
|
+
const parsed = get(drawing, `xdr:wsDr.xdr:${element}`) || get(drawing, `wsDr.${element}`);
|
|
67
|
+
if (!parsed)
|
|
68
|
+
continue;
|
|
69
|
+
nodesByElement[element] = Array.isArray(parsed) ? parsed : [parsed];
|
|
70
|
+
}
|
|
71
|
+
const cursor = {
|
|
72
|
+
oneCellAnchor: 0,
|
|
73
|
+
twoCellAnchor: 0,
|
|
74
|
+
absoluteAnchor: 0,
|
|
75
|
+
};
|
|
76
|
+
const ordered = [];
|
|
77
|
+
for (const match of drawingXml.matchAll(ANCHOR_ELEMENT_REGEX)) {
|
|
78
|
+
const element = match[1];
|
|
79
|
+
const node = nodesByElement[element][cursor[element]];
|
|
80
|
+
if (node === undefined)
|
|
81
|
+
continue;
|
|
82
|
+
cursor[element] += 1;
|
|
83
|
+
ordered.push({ kind: ANCHOR_KIND_BY_ELEMENT[element], node });
|
|
84
|
+
}
|
|
85
|
+
for (const element of Object.keys(nodesByElement)) {
|
|
86
|
+
for (const node of nodesByElement[element].slice(cursor[element])) {
|
|
87
|
+
ordered.push({ kind: ANCHOR_KIND_BY_ELEMENT[element], node });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return ordered;
|
|
91
|
+
}
|
|
44
92
|
/**
|
|
45
93
|
* Process different types of image anchors
|
|
46
94
|
* @param anchors The anchor elements from the drawing
|
|
@@ -410,6 +458,7 @@ export async function processAnchors(anchors, anchorType, drawingPath, sheetId,
|
|
|
410
458
|
imageUrl: createDataUrl(imageData, imageType),
|
|
411
459
|
type: "image",
|
|
412
460
|
description: imageName,
|
|
461
|
+
...parsePictureAdjustments(picture),
|
|
413
462
|
},
|
|
414
463
|
position,
|
|
415
464
|
draggable: true,
|
|
@@ -418,6 +467,56 @@ export async function processAnchors(anchors, anchorType, drawingPath, sheetId,
|
|
|
418
467
|
}
|
|
419
468
|
return images;
|
|
420
469
|
}
|
|
470
|
+
/** DrawingML per-mille-percent (100000 = 100%) → fraction, or undefined. */
|
|
471
|
+
const pctToFraction = (value) => {
|
|
472
|
+
if (value === undefined || value === null || value === "")
|
|
473
|
+
return undefined;
|
|
474
|
+
const n = Number(value);
|
|
475
|
+
return Number.isFinite(n) ? n / 100000 : undefined;
|
|
476
|
+
};
|
|
477
|
+
/**
|
|
478
|
+
* Picture-format adjustments off an `<xdr:pic>`: `<a:blipFill><a:srcRect>`
|
|
479
|
+
* (crop), `<a:blip><a:lum bright contrast>` (brightness / contrast) and
|
|
480
|
+
* `<a:blip><a:alphaModFix amt>` (opacity → `transparency`). Only present
|
|
481
|
+
* values are returned so an untouched picture stays a bare image spec.
|
|
482
|
+
*/
|
|
483
|
+
export function parsePictureAdjustments(picture) {
|
|
484
|
+
const blipFill = get(picture, "xdr:blipFill") ?? get(picture, "blipFill") ?? undefined;
|
|
485
|
+
const blip = get(blipFill, "a:blip") ?? get(blipFill, "blip");
|
|
486
|
+
const out = {};
|
|
487
|
+
const srcRect = get(blipFill, "a:srcRect.a$") ?? get(blipFill, "srcRect.a$");
|
|
488
|
+
if (srcRect) {
|
|
489
|
+
const crop = {};
|
|
490
|
+
const left = pctToFraction(srcRect.l);
|
|
491
|
+
const top = pctToFraction(srcRect.t);
|
|
492
|
+
const right = pctToFraction(srcRect.r);
|
|
493
|
+
const bottom = pctToFraction(srcRect.b);
|
|
494
|
+
if (left)
|
|
495
|
+
crop.left = left;
|
|
496
|
+
if (top)
|
|
497
|
+
crop.top = top;
|
|
498
|
+
if (right)
|
|
499
|
+
crop.right = right;
|
|
500
|
+
if (bottom)
|
|
501
|
+
crop.bottom = bottom;
|
|
502
|
+
if (Object.keys(crop).length)
|
|
503
|
+
out.crop = crop;
|
|
504
|
+
}
|
|
505
|
+
const lum = get(blip, "a:lum.a$") ?? get(blip, "lum.a$");
|
|
506
|
+
if (lum) {
|
|
507
|
+
const brightness = pctToFraction(lum.bright);
|
|
508
|
+
const contrast = pctToFraction(lum.contrast);
|
|
509
|
+
if (brightness)
|
|
510
|
+
out.brightness = brightness;
|
|
511
|
+
if (contrast)
|
|
512
|
+
out.contrast = contrast;
|
|
513
|
+
}
|
|
514
|
+
const alpha = pctToFraction(get(blip, "a:alphaModFix.a$.amt") ?? get(blip, "alphaModFix.a$.amt"));
|
|
515
|
+
if (alpha !== undefined && alpha < 1) {
|
|
516
|
+
out.transparency = Math.round((1 - alpha) * 100000) / 100000;
|
|
517
|
+
}
|
|
518
|
+
return out;
|
|
519
|
+
}
|
|
421
520
|
/**
|
|
422
521
|
* Parse color from DrawingML color element
|
|
423
522
|
* Supports both RGB (srgbClr) and theme colors (schemeClr)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { EmbeddedObject } from "@rowsncolumns/spreadsheet";
|
|
2
|
+
export declare const CTRL_PROP_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp";
|
|
3
|
+
export declare const VML_DRAWING_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing";
|
|
4
|
+
type CellAnchor = {
|
|
5
|
+
col: number;
|
|
6
|
+
colOff: number;
|
|
7
|
+
row: number;
|
|
8
|
+
rowOff: number;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* `<controlPr><anchor><from>…</from><to>…</to></anchor>` → the engine's
|
|
12
|
+
* pixel overlay position (1-based anchor cell, EMU offsets → px, size from
|
|
13
|
+
* the cell span at the grid defaults — the same approximation the drawing
|
|
14
|
+
* importer uses for `twoCellAnchor`s).
|
|
15
|
+
*/
|
|
16
|
+
export declare const controlAnchorToOverlayPosition: (from: CellAnchor, to: CellAnchor | undefined) => {
|
|
17
|
+
anchorCell: {
|
|
18
|
+
rowIndex: number;
|
|
19
|
+
columnIndex: number;
|
|
20
|
+
};
|
|
21
|
+
offsetXPixels?: number;
|
|
22
|
+
offsetYPixels?: number;
|
|
23
|
+
widthPixels?: number;
|
|
24
|
+
heightPixels?: number;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Parse an `xl/ctrlProps/ctrlPropN.xml` part: `objectType`, every other
|
|
28
|
+
* `formControlPr` attribute verbatim, and the literal `itemLst` items.
|
|
29
|
+
*/
|
|
30
|
+
export declare const parseFormControlPr: (ctrlPropDom: any) => {
|
|
31
|
+
objectType: string;
|
|
32
|
+
properties: Record<string, string>;
|
|
33
|
+
listItems?: string[];
|
|
34
|
+
} | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Captions live only in the legacy VML part: `<v:shape id="_x0000_s1025">
|
|
37
|
+
* … <v:textbox><div>Check Box 1</div></v:textbox>`. Returns shapeId → text.
|
|
38
|
+
* VML is scanned textually — Excel's VML is not reliably well-formed XML.
|
|
39
|
+
*/
|
|
40
|
+
export declare const parseVmlControlCaptions: (vml: string | undefined) => Map<number, string>;
|
|
41
|
+
export type RawWorksheetControl = {
|
|
42
|
+
shapeId?: number;
|
|
43
|
+
name?: string;
|
|
44
|
+
relId?: string;
|
|
45
|
+
controlProperties: Record<string, string>;
|
|
46
|
+
moveWithCells?: boolean;
|
|
47
|
+
sizeWithCells?: boolean;
|
|
48
|
+
from?: CellAnchor;
|
|
49
|
+
to?: CellAnchor;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* `<controls>` from a worksheet DOM, whether wrapped in
|
|
53
|
+
* `<mc:AlternateContent><mc:Choice Requires="x14">` (Excel) or bare.
|
|
54
|
+
*/
|
|
55
|
+
export declare const readWorksheetControls: (sheetDom: any) => RawWorksheetControl[];
|
|
56
|
+
/**
|
|
57
|
+
* Assemble the sheet's form controls. `loadCtrlProp` resolves a control's
|
|
58
|
+
* `r:id` to its parsed `ctrlProp` DOM (undefined when the part is missing —
|
|
59
|
+
* the control is then skipped, Excel would repair it the same way).
|
|
60
|
+
*/
|
|
61
|
+
export declare const buildFormControlEmbeds: ({ controls, sheetId, ctrlProps, captions, }: {
|
|
62
|
+
controls: RawWorksheetControl[];
|
|
63
|
+
sheetId: number;
|
|
64
|
+
ctrlProps: Map<string, ReturnType<typeof parseFormControlPr>>;
|
|
65
|
+
captions: Map<number, string>;
|
|
66
|
+
}) => EmbeddedObject[];
|
|
67
|
+
/** The `r:id` of a worksheet's `<legacyDrawing>` (VML part), if any. */
|
|
68
|
+
export declare const readLegacyDrawingRelId: (sheetDom: any) => string | undefined;
|
|
69
|
+
export {};
|
|
70
|
+
//# sourceMappingURL=form-controls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-controls.d.ts","sourceRoot":"","sources":["../../../../../excel-parser/plugins/xlsx/form-controls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,cAAc,EACf,MAAM,2BAA2B,CAAC;AAgBnC,eAAO,MAAM,kBAAkB,iFACiD,CAAC;AACjF,eAAO,MAAM,oBAAoB,mFACiD,CAAC;AA6CnF,KAAK,UAAU,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAe/E;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,GACzC,MAAM,UAAU,EAChB,IAAI,UAAU,GAAG,SAAS;gBAKZ;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE;oBACrC,MAAM;oBACN,MAAM;kBACR,MAAM;mBACL,MAAM;CAiBxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAC7B,aAAa,GAAG,KACf;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,GAAG,SAkBH,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAClC,KAAK,MAAM,GAAG,SAAS,KACtB,GAAG,CAAC,MAAM,EAAE,MAAM,CAwBpB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,EAAE,CAAC,EAAE,UAAU,CAAC;CACjB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,UAAU,GAAG,KAAG,mBAAmB,EA+BxE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAI,6CAKpC;IACD,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,CAAC;IAC9D,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B,KAAG,cAAc,EA+BjB,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,sBAAsB,GAAI,UAAU,GAAG,KAAG,MAAM,GAAG,SAK/D,CAAC"}
|