reamkit 1.13.0 → 1.14.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/document-model/types.d.ts +2 -0
- package/dist/esm/core/ir/sheet.d.ts +35 -3
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +62 -1
- package/dist/esm/excel/comments-parser.d.ts +4 -0
- package/dist/esm/excel/comments-parser.js +104 -0
- package/dist/esm/excel/conditional-format.d.ts +2 -2
- package/dist/esm/excel/conditional-format.js +113 -11
- package/dist/esm/excel/form-control-parser.d.ts +6 -0
- package/dist/esm/excel/form-control-parser.js +29 -0
- package/dist/esm/excel/header-footer.d.ts +2 -0
- package/dist/esm/excel/header-footer.js +127 -0
- package/dist/esm/excel/index.d.ts +1 -1
- package/dist/esm/excel/print-model.d.ts +4 -2
- package/dist/esm/excel/print-model.js +123 -16
- package/dist/esm/excel/shared-strings-parser.d.ts +6 -1
- package/dist/esm/excel/shared-strings-parser.js +91 -12
- package/dist/esm/excel/sheet-drawing.d.ts +13 -1
- package/dist/esm/excel/sheet-drawing.js +47 -14
- package/dist/esm/excel/sheet-shape-parser.d.ts +4 -0
- package/dist/esm/excel/sheet-shape-parser.js +96 -0
- package/dist/esm/excel/sheet-to-flow.js +125 -2
- package/dist/esm/excel/styles-parser.js +37 -0
- package/dist/esm/excel/worksheet-parser.js +125 -1
- package/dist/esm/excel/xlsx-reader.js +107 -14
- package/dist/esm/excel/xlsx-writer.js +10 -2
- package/dist/esm/layout/styled-layout.js +29 -0
- package/dist/esm/pptx/slide-parser.d.ts +4 -2
- package/dist/esm/pptx/slide-parser.js +1 -1
- package/package.json +1 -1
|
@@ -2,14 +2,18 @@ import { pt } from "../core/ir/units.js";
|
|
|
2
2
|
import { EMPTY_STYLE_SHEET, resolveBodyStyles } from "../core/style-cascade/resolver.js";
|
|
3
3
|
import "../core/style-cascade/index.js";
|
|
4
4
|
import { resolvePrintArea, resolvePrintTitleRows, sectionFromWorksheet, slicerTable, worksheetToBody } from "./print-model.js";
|
|
5
|
+
import { buildHeaderFooterContent } from "./header-footer.js";
|
|
5
6
|
//#region src/excel/sheet-to-flow.ts
|
|
7
|
+
var HEADER_REL = "_xlsxHeaderDefault";
|
|
8
|
+
var FOOTER_REL = "_xlsxFooterDefault";
|
|
6
9
|
function projectSheetDoc(sheet) {
|
|
7
10
|
const body = [];
|
|
8
11
|
let firstSheetSection;
|
|
12
|
+
const headersFooters = /* @__PURE__ */ new Map();
|
|
9
13
|
const sheetGrids = new Map(sheet.sheets.map((s) => [s.name, s.grid]));
|
|
10
14
|
for (let sheetIdx = 0; sheetIdx < sheet.sheets.length; sheetIdx++) {
|
|
11
15
|
const ws = sheet.sheets[sheetIdx];
|
|
12
|
-
if (sheetIdx === 0) firstSheetSection = sectionFromWorksheet(ws.grid);
|
|
16
|
+
if (sheetIdx === 0) firstSheetSection = withHeaderFooter(sectionFromWorksheet(ws.grid), ws, headersFooters);
|
|
13
17
|
if (sheetIdx > 0) body.push({
|
|
14
18
|
kind: "paragraph",
|
|
15
19
|
paragraph: {
|
|
@@ -24,7 +28,9 @@ function projectSheetDoc(sheet) {
|
|
|
24
28
|
...printArea ? { printArea } : {},
|
|
25
29
|
...titleRows ? { titleRows } : {},
|
|
26
30
|
gridLines,
|
|
27
|
-
sheetGrids
|
|
31
|
+
sheetGrids,
|
|
32
|
+
...ws.hyperlinks ? { hyperlinks: ws.hyperlinks } : {},
|
|
33
|
+
...sheet.sharedStringRuns ? { sharedStringRuns: sheet.sharedStringRuns } : {}
|
|
28
34
|
}));
|
|
29
35
|
for (const ref of ws.charts ?? []) body.push({
|
|
30
36
|
kind: "chart",
|
|
@@ -35,10 +41,25 @@ function projectSheetDoc(sheet) {
|
|
|
35
41
|
paragraphProperties: {}
|
|
36
42
|
}
|
|
37
43
|
});
|
|
44
|
+
for (const img of ws.images ?? []) body.push({
|
|
45
|
+
kind: "image",
|
|
46
|
+
image: {
|
|
47
|
+
resource: img.resourceId,
|
|
48
|
+
width: pt(img.widthPt),
|
|
49
|
+
height: pt(img.heightPt),
|
|
50
|
+
paragraphProperties: {}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
for (const shape of ws.shapes ?? []) body.push({
|
|
54
|
+
kind: "shape",
|
|
55
|
+
shape
|
|
56
|
+
});
|
|
38
57
|
for (const slicer of ws.slicers ?? []) body.push({
|
|
39
58
|
kind: "table",
|
|
40
59
|
table: slicerTable(slicer)
|
|
41
60
|
});
|
|
61
|
+
if (ws.comments && ws.comments.length > 0) body.push(...commentBlocks(ws.comments));
|
|
62
|
+
if (ws.formControls && ws.formControls.length > 0) body.push(...formControlBlocks(ws.formControls));
|
|
42
63
|
}
|
|
43
64
|
return {
|
|
44
65
|
kind: "flow",
|
|
@@ -48,8 +69,110 @@ function projectSheetDoc(sheet) {
|
|
|
48
69
|
styles: EMPTY_STYLE_SHEET,
|
|
49
70
|
resources: sheet.resources,
|
|
50
71
|
...sheet.chartData && sheet.chartData.size > 0 ? { charts: sheet.chartData } : {},
|
|
72
|
+
...headersFooters.size > 0 ? { headersFooters } : {},
|
|
51
73
|
...sheet.info ? { info: sheet.info } : {}
|
|
52
74
|
};
|
|
53
75
|
}
|
|
76
|
+
function commentBlocks(comments) {
|
|
77
|
+
const out = [{
|
|
78
|
+
kind: "paragraph",
|
|
79
|
+
paragraph: {
|
|
80
|
+
properties: {},
|
|
81
|
+
runs: [{
|
|
82
|
+
text: "Comments",
|
|
83
|
+
properties: { bold: true }
|
|
84
|
+
}]
|
|
85
|
+
}
|
|
86
|
+
}];
|
|
87
|
+
for (const c of comments) {
|
|
88
|
+
const body = c.text.replace(/\s+/g, " ").trim();
|
|
89
|
+
const label = c.author ? `${c.ref} — ${c.author}: ` : `${c.ref}: `;
|
|
90
|
+
out.push({
|
|
91
|
+
kind: "paragraph",
|
|
92
|
+
paragraph: {
|
|
93
|
+
properties: {},
|
|
94
|
+
runs: [{
|
|
95
|
+
text: label,
|
|
96
|
+
properties: { bold: true }
|
|
97
|
+
}, ...body.length > 0 ? [{
|
|
98
|
+
text: body,
|
|
99
|
+
properties: {}
|
|
100
|
+
}] : []]
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
function formControlBlocks(controls) {
|
|
107
|
+
const out = [{
|
|
108
|
+
kind: "paragraph",
|
|
109
|
+
paragraph: {
|
|
110
|
+
properties: {},
|
|
111
|
+
runs: [{
|
|
112
|
+
text: "Form controls",
|
|
113
|
+
properties: { bold: true }
|
|
114
|
+
}]
|
|
115
|
+
}
|
|
116
|
+
}];
|
|
117
|
+
for (const c of controls) out.push({
|
|
118
|
+
kind: "paragraph",
|
|
119
|
+
paragraph: {
|
|
120
|
+
properties: {},
|
|
121
|
+
runs: [{
|
|
122
|
+
text: formControlLabel(c),
|
|
123
|
+
properties: {}
|
|
124
|
+
}]
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
function formControlLabel(c) {
|
|
130
|
+
const name = c.name ?? c.objectType ?? "Control";
|
|
131
|
+
switch ((c.objectType ?? "").toLowerCase()) {
|
|
132
|
+
case "checkbox": return `${c.checked ? "[x]" : "[ ]"} ${name}`;
|
|
133
|
+
case "radio": return `${c.checked ? "(o)" : "( )"} ${name}`;
|
|
134
|
+
case "buttons": return `[ ${name} ]`;
|
|
135
|
+
case "spin":
|
|
136
|
+
case "scroll": return c.value !== void 0 ? `${name} (value ${c.value})` : name;
|
|
137
|
+
case "drop":
|
|
138
|
+
case "list": return `${name} (list)`;
|
|
139
|
+
default: return c.objectType ? `${name} (${c.objectType})` : name;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function withHeaderFooter(section, ws, headersFooters) {
|
|
143
|
+
const hf = ws.grid.headerFooter;
|
|
144
|
+
if (!hf || !hf.oddHeader && !hf.oddFooter) return section;
|
|
145
|
+
const headers = [];
|
|
146
|
+
const footers = [];
|
|
147
|
+
if (hf.oddHeader) {
|
|
148
|
+
const content = buildHeaderFooterContent(hf.oddHeader, ws.name);
|
|
149
|
+
if (content.length > 0) {
|
|
150
|
+
headersFooters.set(HEADER_REL, resolveBodyStyles(content, EMPTY_STYLE_SHEET));
|
|
151
|
+
headers.push({
|
|
152
|
+
type: "default",
|
|
153
|
+
relationshipId: HEADER_REL
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (hf.oddFooter) {
|
|
158
|
+
const content = buildHeaderFooterContent(hf.oddFooter, ws.name);
|
|
159
|
+
if (content.length > 0) {
|
|
160
|
+
headersFooters.set(FOOTER_REL, resolveBodyStyles(content, EMPTY_STYLE_SHEET));
|
|
161
|
+
footers.push({
|
|
162
|
+
type: "default",
|
|
163
|
+
relationshipId: FOOTER_REL
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (headers.length === 0 && footers.length === 0) return section;
|
|
168
|
+
return {
|
|
169
|
+
...section ?? {
|
|
170
|
+
headers: [],
|
|
171
|
+
footers: []
|
|
172
|
+
},
|
|
173
|
+
headers,
|
|
174
|
+
footers
|
|
175
|
+
};
|
|
176
|
+
}
|
|
54
177
|
//#endregion
|
|
55
178
|
export { projectSheetDoc };
|
|
@@ -101,6 +101,12 @@ function parseBorders(root) {
|
|
|
101
101
|
if (right) border.right = right;
|
|
102
102
|
if (bottom) border.bottom = bottom;
|
|
103
103
|
if (left) border.left = left;
|
|
104
|
+
const diagonal = parseBorderEdge(asObject(obj["diagonal"]));
|
|
105
|
+
if (diagonal) {
|
|
106
|
+
border.diagonal = diagonal;
|
|
107
|
+
if (boolAttr(obj, "diagonalUp")) border.diagonalUp = true;
|
|
108
|
+
if (boolAttr(obj, "diagonalDown")) border.diagonalDown = true;
|
|
109
|
+
}
|
|
104
110
|
out.push(border);
|
|
105
111
|
}
|
|
106
112
|
return out;
|
|
@@ -169,6 +175,13 @@ function parseFills(root) {
|
|
|
169
175
|
if (fg) fill.fgColorHex = fg;
|
|
170
176
|
if (bg) fill.bgColorHex = bg;
|
|
171
177
|
}
|
|
178
|
+
if (!fill.patternType) {
|
|
179
|
+
const avg = averageGradientColor(asObject(obj["gradientFill"]));
|
|
180
|
+
if (avg) {
|
|
181
|
+
fill.patternType = "solid";
|
|
182
|
+
fill.fgColorHex = avg;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
172
185
|
out.push(fill);
|
|
173
186
|
}
|
|
174
187
|
return out;
|
|
@@ -205,6 +218,11 @@ function parseCellXfs(root) {
|
|
|
205
218
|
if (h) a.horizontal = h;
|
|
206
219
|
if (v) a.vertical = v;
|
|
207
220
|
if (wrap !== void 0) a.wrapText = wrap;
|
|
221
|
+
const indent = numAttr(align, "indent");
|
|
222
|
+
if (indent !== void 0 && indent > 0) a.indent = indent;
|
|
223
|
+
const rotation = numAttr(align, "textRotation");
|
|
224
|
+
if (rotation !== void 0 && rotation !== 0) a.textRotation = rotation;
|
|
225
|
+
if (boolAttr(align, "shrinkToFit")) a.shrinkToFit = true;
|
|
208
226
|
if (Object.keys(a).length > 0) xf.alignment = a;
|
|
209
227
|
}
|
|
210
228
|
out.push(xf);
|
|
@@ -250,6 +268,25 @@ function childToggle(obj, childName) {
|
|
|
250
268
|
if (val === void 0) return true;
|
|
251
269
|
return !(val === "false" || val === "0");
|
|
252
270
|
}
|
|
271
|
+
function averageGradientColor(gf) {
|
|
272
|
+
if (!gf) return void 0;
|
|
273
|
+
let r = 0;
|
|
274
|
+
let g = 0;
|
|
275
|
+
let b = 0;
|
|
276
|
+
let n = 0;
|
|
277
|
+
for (const s of asArray(gf["stop"])) {
|
|
278
|
+
const so = asObject(s);
|
|
279
|
+
const hex = so ? colorOf(asObject(so["color"])) : void 0;
|
|
280
|
+
if (!hex) continue;
|
|
281
|
+
r += parseInt(hex.slice(0, 2), 16);
|
|
282
|
+
g += parseInt(hex.slice(2, 4), 16);
|
|
283
|
+
b += parseInt(hex.slice(4, 6), 16);
|
|
284
|
+
n++;
|
|
285
|
+
}
|
|
286
|
+
if (n === 0) return void 0;
|
|
287
|
+
const h = (x) => Math.round(x / n).toString(16).padStart(2, "0").toUpperCase();
|
|
288
|
+
return `${h(r)}${h(g)}${h(b)}`;
|
|
289
|
+
}
|
|
253
290
|
function colorOf(node) {
|
|
254
291
|
if (!node) return void 0;
|
|
255
292
|
const rgb = strAttr(node, "rgb");
|
|
@@ -38,6 +38,9 @@ function parseWorksheet(data) {
|
|
|
38
38
|
const drawingRelId = drawingNode && typeof drawingNode === "object" ? strAttr(drawingNode, "id") : void 0;
|
|
39
39
|
const conditionalFormats = parseConditionalFormatting(wsObj);
|
|
40
40
|
const dataValidations = parseDataValidations(wsObj);
|
|
41
|
+
const hyperlinks = parseHyperlinks(wsObj);
|
|
42
|
+
const headerFooter = parseHeaderFooter(wsObj);
|
|
43
|
+
const formControls = parseFormControls(wsObj);
|
|
41
44
|
const sparklines = parseSparklines(wsObj);
|
|
42
45
|
const tablePartRelIds = parseTableParts(wsObj);
|
|
43
46
|
const printModel = {
|
|
@@ -51,6 +54,9 @@ function parseWorksheet(data) {
|
|
|
51
54
|
...drawingRelId !== void 0 ? { drawingRelId } : {},
|
|
52
55
|
...conditionalFormats.length > 0 ? { conditionalFormats } : {},
|
|
53
56
|
...dataValidations.length > 0 ? { dataValidations } : {},
|
|
57
|
+
...hyperlinks.length > 0 ? { hyperlinks } : {},
|
|
58
|
+
...headerFooter ? { headerFooter } : {},
|
|
59
|
+
...formControls.length > 0 ? { formControls } : {},
|
|
54
60
|
...sparklines.length > 0 ? { sparklines } : {},
|
|
55
61
|
...tablePartRelIds.length > 0 ? { tablePartRelIds } : {}
|
|
56
62
|
};
|
|
@@ -354,6 +360,41 @@ function boolAttr(obj, key) {
|
|
|
354
360
|
const raw = strAttr(obj, key);
|
|
355
361
|
return raw === "1" || raw === "true";
|
|
356
362
|
}
|
|
363
|
+
function parseHeaderFooter(ws) {
|
|
364
|
+
const node = asObjectNode(ws["headerFooter"]);
|
|
365
|
+
if (!node) return void 0;
|
|
366
|
+
const oddHeader = formulaText(node["oddHeader"]);
|
|
367
|
+
const oddFooter = formulaText(node["oddFooter"]);
|
|
368
|
+
if (!oddHeader && !oddFooter) return void 0;
|
|
369
|
+
return {
|
|
370
|
+
...oddHeader ? { oddHeader } : {},
|
|
371
|
+
...oddFooter ? { oddFooter } : {}
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function parseHyperlinks(ws) {
|
|
375
|
+
const node = asObjectNode(ws["hyperlinks"]);
|
|
376
|
+
if (!node) return [];
|
|
377
|
+
const out = [];
|
|
378
|
+
for (const h of toArray(node["hyperlink"])) {
|
|
379
|
+
const obj = asObjectNode(h);
|
|
380
|
+
if (!obj) continue;
|
|
381
|
+
const ref = strAttr(obj, "ref");
|
|
382
|
+
if (!ref) continue;
|
|
383
|
+
const relId = strAttr(obj, "id");
|
|
384
|
+
const location = strAttr(obj, "location");
|
|
385
|
+
if (relId === void 0 && location === void 0) continue;
|
|
386
|
+
const display = strAttr(obj, "display");
|
|
387
|
+
const tooltip = strAttr(obj, "tooltip");
|
|
388
|
+
out.push({
|
|
389
|
+
ref,
|
|
390
|
+
...relId !== void 0 ? { relId } : {},
|
|
391
|
+
...location !== void 0 ? { location } : {},
|
|
392
|
+
...display !== void 0 ? { display } : {},
|
|
393
|
+
...tooltip !== void 0 ? { tooltip } : {}
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
return out;
|
|
397
|
+
}
|
|
357
398
|
function parseSqref(sqref) {
|
|
358
399
|
const out = [];
|
|
359
400
|
for (const token of sqref.split(/\s+/)) {
|
|
@@ -380,14 +421,73 @@ var CF_OPERATORS = new Set([
|
|
|
380
421
|
]);
|
|
381
422
|
function parseCfRule(obj) {
|
|
382
423
|
const priority = parseNumericAttr(obj, "priority") ?? 0;
|
|
383
|
-
|
|
424
|
+
const type = strAttr(obj, "type");
|
|
425
|
+
switch (type) {
|
|
384
426
|
case "cellIs": return parseCellIsRule(obj, priority);
|
|
385
427
|
case "colorScale": return parseColorScaleRule(obj, priority);
|
|
386
428
|
case "dataBar": return parseDataBarRule(obj, priority);
|
|
387
429
|
case "iconSet": return parseIconSetRule(obj, priority);
|
|
430
|
+
case "top10": return parseTop10Rule(obj, priority);
|
|
431
|
+
case "aboveAverage": return parseAboveAverageRule(obj, priority);
|
|
432
|
+
case "duplicateValues":
|
|
433
|
+
case "uniqueValues": return parseDupUniqueRule(type, obj, priority);
|
|
434
|
+
case "containsText":
|
|
435
|
+
case "notContainsText":
|
|
436
|
+
case "beginsWith":
|
|
437
|
+
case "endsWith": return parseTextRule(type, obj, priority);
|
|
388
438
|
default: return;
|
|
389
439
|
}
|
|
390
440
|
}
|
|
441
|
+
function parseTop10Rule(obj, priority) {
|
|
442
|
+
const dxfId = parseNumericAttr(obj, "dxfId");
|
|
443
|
+
if (dxfId === void 0) return void 0;
|
|
444
|
+
return {
|
|
445
|
+
type: "top10",
|
|
446
|
+
priority,
|
|
447
|
+
rank: parseNumericAttr(obj, "rank") ?? 10,
|
|
448
|
+
percent: boolAttr(obj, "percent"),
|
|
449
|
+
bottom: boolAttr(obj, "bottom"),
|
|
450
|
+
dxfId
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
function parseAboveAverageRule(obj, priority) {
|
|
454
|
+
const dxfId = parseNumericAttr(obj, "dxfId");
|
|
455
|
+
if (dxfId === void 0) return void 0;
|
|
456
|
+
const aboveRaw = strAttr(obj, "aboveAverage");
|
|
457
|
+
const aboveAverage = aboveRaw === void 0 ? true : aboveRaw === "1" || aboveRaw === "true";
|
|
458
|
+
const stdDev = parseNumericAttr(obj, "stdDev");
|
|
459
|
+
return {
|
|
460
|
+
type: "aboveAverage",
|
|
461
|
+
priority,
|
|
462
|
+
aboveAverage,
|
|
463
|
+
equalAverage: boolAttr(obj, "equalAverage"),
|
|
464
|
+
...stdDev !== void 0 ? { stdDev } : {},
|
|
465
|
+
dxfId
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
function parseDupUniqueRule(type, obj, priority) {
|
|
469
|
+
const dxfId = parseNumericAttr(obj, "dxfId");
|
|
470
|
+
if (dxfId === void 0) return void 0;
|
|
471
|
+
return {
|
|
472
|
+
type,
|
|
473
|
+
priority,
|
|
474
|
+
dxfId
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
function parseTextRule(type, obj, priority) {
|
|
478
|
+
const dxfId = parseNumericAttr(obj, "dxfId");
|
|
479
|
+
if (dxfId === void 0) return void 0;
|
|
480
|
+
const text = strAttr(obj, "text");
|
|
481
|
+
if (text === void 0) return void 0;
|
|
482
|
+
const formula = formulaText(obj["formula"]);
|
|
483
|
+
return {
|
|
484
|
+
type,
|
|
485
|
+
priority,
|
|
486
|
+
text,
|
|
487
|
+
dxfId,
|
|
488
|
+
...formula !== void 0 ? { formula } : {}
|
|
489
|
+
};
|
|
490
|
+
}
|
|
391
491
|
function parseIconSetRule(obj, priority) {
|
|
392
492
|
const is = obj["iconSet"];
|
|
393
493
|
if (!is || typeof is !== "object") return void 0;
|
|
@@ -545,6 +645,30 @@ function parseSparklines(ws) {
|
|
|
545
645
|
}
|
|
546
646
|
return out;
|
|
547
647
|
}
|
|
648
|
+
function parseFormControls(ws) {
|
|
649
|
+
const out = [];
|
|
650
|
+
const seen = /* @__PURE__ */ new Set();
|
|
651
|
+
collectControls(asObjectNode(ws["controls"]), out, seen);
|
|
652
|
+
for (const ext of toArray(asObjectNode(ws["extLst"])?.["ext"])) collectControls(asObjectNode(asObjectNode(ext)?.["controls"]), out, seen);
|
|
653
|
+
return out;
|
|
654
|
+
}
|
|
655
|
+
function collectControls(node, out, seen) {
|
|
656
|
+
if (!node) return;
|
|
657
|
+
const direct = toArray(node["control"]);
|
|
658
|
+
const fromChoice = toArray(asObjectNode(node["AlternateContent"])?.["Choice"]).flatMap((c) => toArray(asObjectNode(c)?.["control"]));
|
|
659
|
+
for (const c of [...direct, ...fromChoice]) {
|
|
660
|
+
const obj = asObjectNode(c);
|
|
661
|
+
if (!obj) continue;
|
|
662
|
+
const relId = strAttr(obj, "id");
|
|
663
|
+
if (!relId || seen.has(relId)) continue;
|
|
664
|
+
seen.add(relId);
|
|
665
|
+
const name = strAttr(obj, "name");
|
|
666
|
+
out.push({
|
|
667
|
+
relId,
|
|
668
|
+
...name ? { name } : {}
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
}
|
|
548
672
|
function asObjectNode(v) {
|
|
549
673
|
return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
|
|
550
674
|
}
|
|
@@ -8,6 +8,7 @@ import { isOoxmlRel } from "../core/opc/relationship-types.js";
|
|
|
8
8
|
import { OpcPackage } from "../core/opc/package.js";
|
|
9
9
|
import { parseCoreProperties } from "../core/opc/core-properties.js";
|
|
10
10
|
import "../core/opc/index.js";
|
|
11
|
+
import { parseAreaRef } from "./defined-name-ref.js";
|
|
11
12
|
import { parseSharedStrings } from "./shared-strings-parser.js";
|
|
12
13
|
import { parseWorkbook } from "./workbook-parser.js";
|
|
13
14
|
import { parseWorksheet } from "./worksheet-parser.js";
|
|
@@ -18,6 +19,9 @@ import { parseSheetDrawing } from "./sheet-drawing.js";
|
|
|
18
19
|
import { parseTablePartFull } from "./table-parser.js";
|
|
19
20
|
import { parsePivotTablePart } from "./pivot-table-parser.js";
|
|
20
21
|
import { parseSlicerCachePart, parseSlicerPart } from "./slicer-parser.js";
|
|
22
|
+
import { parseLegacyComments, parsePersons, parseThreadedComments } from "./comments-parser.js";
|
|
23
|
+
import { parseFormControlProps } from "./form-control-parser.js";
|
|
24
|
+
import { parseSheetShapes } from "./sheet-shape-parser.js";
|
|
21
25
|
import { projectSheetDoc } from "./sheet-to-flow.js";
|
|
22
26
|
//#region src/excel/xlsx-reader.ts
|
|
23
27
|
var WORKBOOK_PART = "xl/workbook.xml";
|
|
@@ -26,6 +30,8 @@ var STYLES_PART = "xl/styles.xml";
|
|
|
26
30
|
var CORE_PROPS_PART = "docProps/core.xml";
|
|
27
31
|
var SLICER_REL_TAIL = "/slicer";
|
|
28
32
|
var SLICER_CACHE_REL_TAIL = "/slicerCache";
|
|
33
|
+
var THREADED_COMMENTS_REL_TAIL = "/threadedComment";
|
|
34
|
+
var PERSON_REL_TAIL = "/person";
|
|
29
35
|
var MAX_SLICER_ITEMS = 256;
|
|
30
36
|
function readXlsx(xlsx) {
|
|
31
37
|
return {
|
|
@@ -40,11 +46,21 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
40
46
|
const { sheets, date1904, definedNames } = parseWorkbook(workbookData);
|
|
41
47
|
if (sheets.length === 0) throw new Error("xlsx has no sheets");
|
|
42
48
|
const sharedStringsData = pkg.getPart(SHARED_STRINGS_PART);
|
|
43
|
-
const sharedStrings = sharedStringsData ? parseSharedStrings(sharedStringsData) :
|
|
49
|
+
const { texts: sharedStrings, runs: sharedStringRuns } = sharedStringsData ? parseSharedStrings(sharedStringsData) : {
|
|
50
|
+
texts: [],
|
|
51
|
+
runs: []
|
|
52
|
+
};
|
|
44
53
|
const stylesData = pkg.getPart(STYLES_PART);
|
|
45
54
|
const styles = stylesData ? parseXlsxStyles(stylesData) : EMPTY_XLSX_STYLES;
|
|
46
55
|
const workbookRels = pkg.getPartRelationships(WORKBOOK_PART);
|
|
56
|
+
const persons = /* @__PURE__ */ new Map();
|
|
57
|
+
for (const rel of workbookRels) {
|
|
58
|
+
if (!rel.type.endsWith(PERSON_REL_TAIL)) continue;
|
|
59
|
+
const part = pkg.resolveRelatedPart(WORKBOOK_PART, rel);
|
|
60
|
+
if (part) for (const [id, name] of parsePersons(part.data)) persons.set(id, name);
|
|
61
|
+
}
|
|
47
62
|
const chartData = /* @__PURE__ */ new Map();
|
|
63
|
+
const resources = new ResourceStore();
|
|
48
64
|
const palette = buildThemePalette(pkg, workbookRels);
|
|
49
65
|
const resolveColor = makeColorResolver(palette);
|
|
50
66
|
const sheetsOut = [];
|
|
@@ -58,21 +74,39 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
58
74
|
if (!resolved) continue;
|
|
59
75
|
const worksheet = parseWorksheet(resolved.data);
|
|
60
76
|
const charts = [];
|
|
77
|
+
const images = [];
|
|
78
|
+
let shapes;
|
|
61
79
|
if (worksheet.drawingRelId) {
|
|
62
80
|
const drawingRel = pkg.getPartRelationships(resolved.path).find((r) => r.id === worksheet.drawingRelId);
|
|
63
81
|
const drawing = drawingRel ? pkg.resolveRelatedPart(resolved.path, drawingRel) : void 0;
|
|
64
|
-
if (drawing)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
82
|
+
if (drawing) {
|
|
83
|
+
const { charts: chartRefs, pictures } = parseSheetDrawing(drawing.data, drawing.path, pkg, worksheet);
|
|
84
|
+
for (const ref of chartRefs) {
|
|
85
|
+
if (!chartData.has(ref.chartPartPath)) {
|
|
86
|
+
const chartXml = pkg.getPart(ref.chartPartPath);
|
|
87
|
+
const parsed = chartXml ? parseChart(chartXml, resolveColor) : null;
|
|
88
|
+
if (!parsed) continue;
|
|
89
|
+
chartData.set(ref.chartPartPath, withChartColorStyle(parsed, pkg, ref.chartPartPath, resolveColor));
|
|
90
|
+
}
|
|
91
|
+
charts.push({
|
|
92
|
+
chartPartPath: ref.chartPartPath,
|
|
93
|
+
widthPt: ref.widthPt,
|
|
94
|
+
heightPt: ref.heightPt
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
for (const pic of pictures) {
|
|
98
|
+
const bytes = pkg.getPart(pic.imagePartPath);
|
|
99
|
+
if (!bytes) continue;
|
|
100
|
+
images.push({
|
|
101
|
+
resourceId: resources.put(bytes),
|
|
102
|
+
widthPt: pic.widthPt,
|
|
103
|
+
heightPt: pic.heightPt
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (bytesInclude(drawing.data, ":sp>") || bytesInclude(drawing.data, ":sp ")) {
|
|
107
|
+
const parsed = parseSheetShapes(drawing.data, worksheet, resolveColor);
|
|
108
|
+
if (parsed.length > 0) shapes = parsed;
|
|
70
109
|
}
|
|
71
|
-
charts.push({
|
|
72
|
-
chartPartPath: ref.chartPartPath,
|
|
73
|
-
widthPt: ref.widthPt,
|
|
74
|
-
heightPt: ref.heightPt
|
|
75
|
-
});
|
|
76
110
|
}
|
|
77
111
|
}
|
|
78
112
|
let tables;
|
|
@@ -118,6 +152,59 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
118
152
|
}
|
|
119
153
|
if (resolvedPivots.length > 0) pivotTables = resolvedPivots;
|
|
120
154
|
}
|
|
155
|
+
let hyperlinks;
|
|
156
|
+
if (worksheet.hyperlinks && worksheet.hyperlinks.length > 0) {
|
|
157
|
+
const wsRels = pkg.getPartRelationships(resolved.path);
|
|
158
|
+
const resolvedLinks = [];
|
|
159
|
+
for (const h of worksheet.hyperlinks) {
|
|
160
|
+
if (h.relId === void 0) continue;
|
|
161
|
+
const rel = wsRels.find((r) => r.id === h.relId);
|
|
162
|
+
if (!rel || rel.targetMode !== "External" || !rel.target) continue;
|
|
163
|
+
const area = parseAreaRef(h.ref);
|
|
164
|
+
if (!area) continue;
|
|
165
|
+
resolvedLinks.push({
|
|
166
|
+
ref: {
|
|
167
|
+
startColumn: area.startColumn,
|
|
168
|
+
startRow: area.startRow,
|
|
169
|
+
endColumn: area.endColumn,
|
|
170
|
+
endRow: area.endRow
|
|
171
|
+
},
|
|
172
|
+
url: rel.target
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
if (resolvedLinks.length > 0) hyperlinks = resolvedLinks;
|
|
176
|
+
}
|
|
177
|
+
let comments;
|
|
178
|
+
{
|
|
179
|
+
const wsRels = pkg.getPartRelationships(resolved.path);
|
|
180
|
+
const resolvedComments = [];
|
|
181
|
+
for (const rel of wsRels) {
|
|
182
|
+
if (!isOoxmlRel(rel.type, "comments")) continue;
|
|
183
|
+
const part = pkg.resolveRelatedPart(resolved.path, rel);
|
|
184
|
+
if (part) resolvedComments.push(...parseLegacyComments(part.data));
|
|
185
|
+
}
|
|
186
|
+
for (const rel of wsRels) {
|
|
187
|
+
if (!rel.type.endsWith(THREADED_COMMENTS_REL_TAIL)) continue;
|
|
188
|
+
const part = pkg.resolveRelatedPart(resolved.path, rel);
|
|
189
|
+
if (part) resolvedComments.push(...parseThreadedComments(part.data, persons));
|
|
190
|
+
}
|
|
191
|
+
if (resolvedComments.length > 0) comments = resolvedComments;
|
|
192
|
+
}
|
|
193
|
+
let formControls;
|
|
194
|
+
if (worksheet.formControls && worksheet.formControls.length > 0) {
|
|
195
|
+
const wsRels = pkg.getPartRelationships(resolved.path);
|
|
196
|
+
const resolvedControls = [];
|
|
197
|
+
for (const fc of worksheet.formControls) {
|
|
198
|
+
const rel = wsRels.find((r) => r.id === fc.relId);
|
|
199
|
+
const part = rel ? pkg.resolveRelatedPart(resolved.path, rel) : void 0;
|
|
200
|
+
const props = part ? parseFormControlProps(part.data) : {};
|
|
201
|
+
resolvedControls.push({
|
|
202
|
+
...fc.name ? { name: fc.name } : {},
|
|
203
|
+
...props
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
if (resolvedControls.length > 0) formControls = resolvedControls;
|
|
207
|
+
}
|
|
121
208
|
const grid = tables || pivotTables ? {
|
|
122
209
|
...worksheet,
|
|
123
210
|
...tables ? { tables } : {},
|
|
@@ -126,7 +213,12 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
126
213
|
sheetsOut.push({
|
|
127
214
|
name: sheet.name,
|
|
128
215
|
grid,
|
|
129
|
-
...charts.length > 0 ? { charts } : {}
|
|
216
|
+
...charts.length > 0 ? { charts } : {},
|
|
217
|
+
...images.length > 0 ? { images } : {},
|
|
218
|
+
...shapes ? { shapes } : {},
|
|
219
|
+
...hyperlinks ? { hyperlinks } : {},
|
|
220
|
+
...comments ? { comments } : {},
|
|
221
|
+
...formControls ? { formControls } : {}
|
|
130
222
|
});
|
|
131
223
|
}
|
|
132
224
|
if (pendingSlicers.length > 0) {
|
|
@@ -154,10 +246,11 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
154
246
|
sheets: sheetsOut,
|
|
155
247
|
styles,
|
|
156
248
|
sharedStrings,
|
|
249
|
+
...sharedStringRuns.some((r) => r !== void 0) ? { sharedStringRuns } : {},
|
|
157
250
|
definedNames,
|
|
158
251
|
date1904,
|
|
159
252
|
...chartData.size > 0 ? { chartData } : {},
|
|
160
|
-
resources
|
|
253
|
+
resources,
|
|
161
254
|
...info ? { info } : {}
|
|
162
255
|
};
|
|
163
256
|
}
|
|
@@ -230,6 +230,14 @@ function cfRuleXml(rule) {
|
|
|
230
230
|
case "colorScale": return `<cfRule type="colorScale"${p}><colorScale>` + rule.cfvos.map(cfvoXml).join("") + rule.colorsHex.map((c) => `<color rgb="FF${c}"/>`).join("") + "</colorScale></cfRule>";
|
|
231
231
|
case "dataBar": return `<cfRule type="dataBar"${p}><dataBar` + (rule.minLength !== void 0 ? ` minLength="${rule.minLength}"` : "") + (rule.maxLength !== void 0 ? ` maxLength="${rule.maxLength}"` : "") + ">" + rule.cfvos.map(cfvoXml).join("") + `<color rgb="FF${rule.colorHex}"/></dataBar></cfRule>`;
|
|
232
232
|
case "iconSet": return `<cfRule type="iconSet"${p}><iconSet iconSet="${rule.iconSet}"` + (rule.reverse ? " reverse=\"1\"" : "") + ">" + rule.cfvos.map(cfvoXml).join("") + "</iconSet></cfRule>";
|
|
233
|
+
case "top10": return `<cfRule type="top10"${p} dxfId="${rule.dxfId}" rank="${rule.rank}"` + (rule.percent ? " percent=\"1\"" : "") + (rule.bottom ? " bottom=\"1\"" : "") + "/>";
|
|
234
|
+
case "aboveAverage": return `<cfRule type="aboveAverage"${p} dxfId="${rule.dxfId}"` + (rule.aboveAverage ? "" : " aboveAverage=\"0\"") + (rule.equalAverage ? " equalAverage=\"1\"" : "") + (rule.stdDev !== void 0 ? ` stdDev="${rule.stdDev}"` : "") + "/>";
|
|
235
|
+
case "duplicateValues":
|
|
236
|
+
case "uniqueValues": return `<cfRule type="${rule.type}"${p} dxfId="${rule.dxfId}"/>`;
|
|
237
|
+
case "containsText":
|
|
238
|
+
case "notContainsText":
|
|
239
|
+
case "beginsWith":
|
|
240
|
+
case "endsWith": return `<cfRule type="${rule.type}"${p} operator="${rule.type}" dxfId="${rule.dxfId}" text="${escapeAttr(rule.text)}">` + (rule.formula !== void 0 ? `<formula>${escapeText(rule.formula)}</formula>` : "") + "</cfRule>";
|
|
233
241
|
}
|
|
234
242
|
}
|
|
235
243
|
function cfvoXml(cfvo) {
|
|
@@ -313,7 +321,7 @@ function patternFill(f) {
|
|
|
313
321
|
return `<patternFill${f.patternType ? ` patternType="${f.patternType}"` : ""}>${inner}</patternFill>`;
|
|
314
322
|
}
|
|
315
323
|
function borderXml(b) {
|
|
316
|
-
return `<border>${edgeXml("left", b.left)}${edgeXml("right", b.right)}${edgeXml("top", b.top)}${edgeXml("bottom", b.bottom)}</border>`;
|
|
324
|
+
return `<border${b.diagonal ? `${b.diagonalUp ? " diagonalUp=\"1\"" : ""}${b.diagonalDown ? " diagonalDown=\"1\"" : ""}` : ""}>${edgeXml("left", b.left)}${edgeXml("right", b.right)}${edgeXml("top", b.top)}${edgeXml("bottom", b.bottom)}${edgeXml("diagonal", b.diagonal)}</border>`;
|
|
317
325
|
}
|
|
318
326
|
function edgeXml(name, edge) {
|
|
319
327
|
if (!edge || !edge.style) return `<${name}/>`;
|
|
@@ -323,7 +331,7 @@ function edgeXml(name, edge) {
|
|
|
323
331
|
function cellXfXml(xf) {
|
|
324
332
|
const apply = applyAttr("applyNumberFormat", xf.applyNumberFormat) + applyAttr("applyFont", xf.applyFont) + applyAttr("applyFill", xf.applyFill) + applyAttr("applyBorder", xf.applyBorder) + applyAttr("applyAlignment", xf.applyAlignment);
|
|
325
333
|
const a = xf.alignment;
|
|
326
|
-
const alignment = a ? `<alignment${a.horizontal ? ` horizontal="${a.horizontal}"` : ""}${a.vertical ? ` vertical="${a.vertical}"` : ""}${a.wrapText ? " wrapText=\"1\"" : ""}/>` : "";
|
|
334
|
+
const alignment = a ? `<alignment${a.horizontal ? ` horizontal="${a.horizontal}"` : ""}${a.vertical ? ` vertical="${a.vertical}"` : ""}${a.wrapText ? " wrapText=\"1\"" : ""}${a.textRotation !== void 0 ? ` textRotation="${a.textRotation}"` : ""}${a.indent !== void 0 ? ` indent="${a.indent}"` : ""}${a.shrinkToFit ? " shrinkToFit=\"1\"" : ""}/>` : "";
|
|
327
335
|
return `<xf numFmtId="${xf.numFmtId}" fontId="${xf.fontId}" fillId="${xf.fillId}" borderId="${xf.borderId}"${apply}>` + alignment + "</xf>";
|
|
328
336
|
}
|
|
329
337
|
function applyAttr(name, value) {
|
|
@@ -2430,6 +2430,35 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
|
|
|
2430
2430
|
}
|
|
2431
2431
|
}
|
|
2432
2432
|
emitCellBorders(out, cell, cellX, rowBottom, row.heightPt, pageHeight, row.rowIdx, row.rowCount, colCount);
|
|
2433
|
+
const diagDown = cell.borders.diagonalDown;
|
|
2434
|
+
const diagUp = cell.borders.diagonalUp;
|
|
2435
|
+
if ((diagDown || diagUp) && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
|
|
2436
|
+
const transform = flipTransform([
|
|
2437
|
+
1,
|
|
2438
|
+
0,
|
|
2439
|
+
0,
|
|
2440
|
+
1,
|
|
2441
|
+
cellX,
|
|
2442
|
+
rowBottom
|
|
2443
|
+
], pageHeight);
|
|
2444
|
+
const w = cell.widthPt;
|
|
2445
|
+
const h = row.heightPt;
|
|
2446
|
+
const pushDiagonal = (b, y0, y1) => {
|
|
2447
|
+
out.push({
|
|
2448
|
+
type: "shape",
|
|
2449
|
+
shape: {
|
|
2450
|
+
paths: [new PathBuilder().moveTo(0, y0).lineTo(w, y1).build()],
|
|
2451
|
+
stroke: {
|
|
2452
|
+
colorHex: b.colorHex ?? "000000",
|
|
2453
|
+
widthPt: b.width ?? .75
|
|
2454
|
+
},
|
|
2455
|
+
transform
|
|
2456
|
+
}
|
|
2457
|
+
});
|
|
2458
|
+
};
|
|
2459
|
+
if (diagDown) pushDiagonal(diagDown, h, 0);
|
|
2460
|
+
if (diagUp) pushDiagonal(diagUp, 0, h);
|
|
2461
|
+
}
|
|
2433
2462
|
if (cell.mergeRole === "middle" || cell.mergeRole === "end") continue;
|
|
2434
2463
|
let textY = rowTop - cell.padTopPt;
|
|
2435
2464
|
for (const line of cell.lines) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { BodyElement, FloatAnchor, ShapeBlock, ShapeFill } from '../core/document-model/index.js';
|
|
1
|
+
import { BodyElement, FloatAnchor, ShapeBlock, ShapeFill, ShapeGeometry, ShapeTextBody } from '../core/document-model/index.js';
|
|
2
2
|
import { ColorResolver } from '../core/drawingml/colors.js';
|
|
3
3
|
import { Loss, Pt, ResourceId } from '../core/ir/index.js';
|
|
4
4
|
import { PoNode } from '../core/po-helpers.js';
|
|
5
5
|
import { PlaceholderCascade } from './placeholder-cascade.js';
|
|
6
|
-
import { ShapeBoxEmu } from './sp-helpers.js';
|
|
6
|
+
import { PlaceholderRef, ShapeBoxEmu } from './sp-helpers.js';
|
|
7
7
|
export interface SlideContext {
|
|
8
8
|
readonly cascade?: PlaceholderCascade;
|
|
9
9
|
readonly resolveImage?: (relId: string) => ResourceId | undefined;
|
|
@@ -21,4 +21,6 @@ export declare function noDiagramOverrideLoss(where?: string): Loss;
|
|
|
21
21
|
export declare function parseDiagramDrawing(spTree: PoNode, transform: GroupTransform, makeFloat: (box: ShapeBoxEmu) => FloatAnchor, colors: ColorResolver, resolveLink: LinkResolver): Array<ShapeBlock>;
|
|
22
22
|
export declare function parseBackgroundFill(bg: PoNode, colors: ColorResolver): ShapeFill | undefined;
|
|
23
23
|
export declare function backdropElement(fill: ShapeFill, widthPt: Pt, heightPt: Pt): BodyElement;
|
|
24
|
+
export declare function parseGeometry(spPr: PoNode | undefined): ShapeGeometry;
|
|
25
|
+
export declare function parseTxBody(txBody: PoNode, ph: PlaceholderRef | undefined, cascade: PlaceholderCascade | undefined, colors: ColorResolver, resolveLink: LinkResolver): ShapeTextBody | undefined;
|
|
24
26
|
export {};
|
|
@@ -424,4 +424,4 @@ function cellFillHex(tcPr, colors) {
|
|
|
424
424
|
}
|
|
425
425
|
}
|
|
426
426
|
//#endregion
|
|
427
|
-
export { backdropElement, diagramTransform, noDiagramOverrideLoss, parseBackgroundFill, parseDiagramDrawing, parseSlideShapes };
|
|
427
|
+
export { backdropElement, diagramTransform, noDiagramOverrideLoss, parseBackgroundFill, parseDiagramDrawing, parseGeometry, parseSlideShapes, parseTxBody };
|