reamkit 1.12.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 +3 -0
- package/dist/esm/core/ir/sheet.d.ts +49 -3
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +80 -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 +7 -2
- package/dist/esm/excel/print-model.js +218 -19
- 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 +130 -3
- package/dist/esm/excel/slicer-parser.d.ts +15 -0
- package/dist/esm/excel/slicer-parser.js +76 -0
- package/dist/esm/excel/styles-parser.js +37 -0
- package/dist/esm/excel/table-parser.d.ts +10 -0
- package/dist/esm/excel/table-parser.js +36 -4
- package/dist/esm/excel/worksheet-parser.js +181 -1
- package/dist/esm/excel/xlsx-reader.js +203 -17
- package/dist/esm/excel/xlsx-writer.js +19 -3
- package/dist/esm/html/html-writer.js +4 -0
- package/dist/esm/layout/styled-layout.js +72 -1
- package/dist/esm/pptx/slide-parser.d.ts +4 -2
- package/dist/esm/pptx/slide-parser.js +1 -1
- package/package.json +1 -1
|
@@ -8,20 +8,31 @@ 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";
|
|
14
15
|
import { EMPTY_XLSX_STYLES, parseXlsxStyles } from "./styles-parser.js";
|
|
15
16
|
import "./index.js";
|
|
17
|
+
import { resolveCellText } from "./print-model.js";
|
|
16
18
|
import { parseSheetDrawing } from "./sheet-drawing.js";
|
|
17
|
-
import {
|
|
19
|
+
import { parseTablePartFull } from "./table-parser.js";
|
|
18
20
|
import { parsePivotTablePart } from "./pivot-table-parser.js";
|
|
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";
|
|
19
25
|
import { projectSheetDoc } from "./sheet-to-flow.js";
|
|
20
26
|
//#region src/excel/xlsx-reader.ts
|
|
21
27
|
var WORKBOOK_PART = "xl/workbook.xml";
|
|
22
28
|
var SHARED_STRINGS_PART = "xl/sharedStrings.xml";
|
|
23
29
|
var STYLES_PART = "xl/styles.xml";
|
|
24
30
|
var CORE_PROPS_PART = "docProps/core.xml";
|
|
31
|
+
var SLICER_REL_TAIL = "/slicer";
|
|
32
|
+
var SLICER_CACHE_REL_TAIL = "/slicerCache";
|
|
33
|
+
var THREADED_COMMENTS_REL_TAIL = "/threadedComment";
|
|
34
|
+
var PERSON_REL_TAIL = "/person";
|
|
35
|
+
var MAX_SLICER_ITEMS = 256;
|
|
25
36
|
function readXlsx(xlsx) {
|
|
26
37
|
return {
|
|
27
38
|
doc: projectSheetDoc(readXlsxToSheetDoc(xlsx)),
|
|
@@ -35,14 +46,26 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
35
46
|
const { sheets, date1904, definedNames } = parseWorkbook(workbookData);
|
|
36
47
|
if (sheets.length === 0) throw new Error("xlsx has no sheets");
|
|
37
48
|
const sharedStringsData = pkg.getPart(SHARED_STRINGS_PART);
|
|
38
|
-
const sharedStrings = sharedStringsData ? parseSharedStrings(sharedStringsData) :
|
|
49
|
+
const { texts: sharedStrings, runs: sharedStringRuns } = sharedStringsData ? parseSharedStrings(sharedStringsData) : {
|
|
50
|
+
texts: [],
|
|
51
|
+
runs: []
|
|
52
|
+
};
|
|
39
53
|
const stylesData = pkg.getPart(STYLES_PART);
|
|
40
54
|
const styles = stylesData ? parseXlsxStyles(stylesData) : EMPTY_XLSX_STYLES;
|
|
41
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
|
+
}
|
|
42
62
|
const chartData = /* @__PURE__ */ new Map();
|
|
63
|
+
const resources = new ResourceStore();
|
|
43
64
|
const palette = buildThemePalette(pkg, workbookRels);
|
|
44
65
|
const resolveColor = makeColorResolver(palette);
|
|
45
66
|
const sheetsOut = [];
|
|
67
|
+
const tableIndex = /* @__PURE__ */ new Map();
|
|
68
|
+
const pendingSlicers = [];
|
|
46
69
|
for (let sheetIdx = 0; sheetIdx < sheets.length; sheetIdx++) {
|
|
47
70
|
const sheet = sheets[sheetIdx];
|
|
48
71
|
const sheetRel = workbookRels.find((r) => r.id === sheet.relationshipId);
|
|
@@ -51,21 +74,39 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
51
74
|
if (!resolved) continue;
|
|
52
75
|
const worksheet = parseWorksheet(resolved.data);
|
|
53
76
|
const charts = [];
|
|
77
|
+
const images = [];
|
|
78
|
+
let shapes;
|
|
54
79
|
if (worksheet.drawingRelId) {
|
|
55
80
|
const drawingRel = pkg.getPartRelationships(resolved.path).find((r) => r.id === worksheet.drawingRelId);
|
|
56
81
|
const drawing = drawingRel ? pkg.resolveRelatedPart(resolved.path, drawingRel) : void 0;
|
|
57
|
-
if (drawing)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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;
|
|
63
109
|
}
|
|
64
|
-
charts.push({
|
|
65
|
-
chartPartPath: ref.chartPartPath,
|
|
66
|
-
widthPt: ref.widthPt,
|
|
67
|
-
heightPt: ref.heightPt
|
|
68
|
-
});
|
|
69
110
|
}
|
|
70
111
|
}
|
|
71
112
|
let tables;
|
|
@@ -75,11 +116,31 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
75
116
|
for (const rid of worksheet.tablePartRelIds) {
|
|
76
117
|
const rel = wsRels.find((r) => r.id === rid);
|
|
77
118
|
const part = rel ? pkg.resolveRelatedPart(resolved.path, rel) : void 0;
|
|
78
|
-
const
|
|
79
|
-
if (
|
|
119
|
+
const full = part ? parseTablePartFull(part.data) : void 0;
|
|
120
|
+
if (!full) continue;
|
|
121
|
+
resolvedTables.push(resolveTableStyle(full.table, palette));
|
|
122
|
+
if (full.id !== void 0) tableIndex.set(full.id, {
|
|
123
|
+
cells: worksheet.cells,
|
|
124
|
+
ref: full.table.ref,
|
|
125
|
+
headerRows: full.table.headerRowCount,
|
|
126
|
+
filters: full.filters
|
|
127
|
+
});
|
|
80
128
|
}
|
|
81
129
|
if (resolvedTables.length > 0) tables = resolvedTables;
|
|
82
130
|
}
|
|
131
|
+
{
|
|
132
|
+
const wsRels = pkg.getPartRelationships(resolved.path);
|
|
133
|
+
const defs = [];
|
|
134
|
+
for (const rel of wsRels) {
|
|
135
|
+
if (!rel.type.endsWith(SLICER_REL_TAIL)) continue;
|
|
136
|
+
const part = pkg.resolveRelatedPart(resolved.path, rel);
|
|
137
|
+
if (part) defs.push(...parseSlicerPart(part.data));
|
|
138
|
+
}
|
|
139
|
+
if (defs.length > 0) pendingSlicers.push({
|
|
140
|
+
outIdx: sheetsOut.length,
|
|
141
|
+
defs
|
|
142
|
+
});
|
|
143
|
+
}
|
|
83
144
|
let pivotTables;
|
|
84
145
|
{
|
|
85
146
|
const resolvedPivots = [];
|
|
@@ -91,6 +152,59 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
91
152
|
}
|
|
92
153
|
if (resolvedPivots.length > 0) pivotTables = resolvedPivots;
|
|
93
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
|
+
}
|
|
94
208
|
const grid = tables || pivotTables ? {
|
|
95
209
|
...worksheet,
|
|
96
210
|
...tables ? { tables } : {},
|
|
@@ -99,9 +213,32 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
99
213
|
sheetsOut.push({
|
|
100
214
|
name: sheet.name,
|
|
101
215
|
grid,
|
|
102
|
-
...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 } : {}
|
|
103
222
|
});
|
|
104
223
|
}
|
|
224
|
+
if (pendingSlicers.length > 0) {
|
|
225
|
+
const cacheByName = /* @__PURE__ */ new Map();
|
|
226
|
+
for (const rel of workbookRels) {
|
|
227
|
+
if (!rel.type.endsWith(SLICER_CACHE_REL_TAIL)) continue;
|
|
228
|
+
const part = pkg.resolveRelatedPart(WORKBOOK_PART, rel);
|
|
229
|
+
const cache = part ? parseSlicerCachePart(part.data) : void 0;
|
|
230
|
+
if (cache) cacheByName.set(cache.name, cache);
|
|
231
|
+
}
|
|
232
|
+
for (const { outIdx, defs } of pendingSlicers) {
|
|
233
|
+
const sheet = sheetsOut[outIdx];
|
|
234
|
+
if (!sheet) continue;
|
|
235
|
+
const slicers = defs.map((def) => resolveSlicer(def, cacheByName.get(def.cacheName), tableIndex, styles, sharedStrings, date1904, palette));
|
|
236
|
+
sheetsOut[outIdx] = {
|
|
237
|
+
...sheet,
|
|
238
|
+
slicers
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
}
|
|
105
242
|
const coreData = pkg.getPart(CORE_PROPS_PART);
|
|
106
243
|
const info = infoFromCore(coreData ? parseCoreProperties(coreData) : void 0);
|
|
107
244
|
return {
|
|
@@ -109,10 +246,11 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
109
246
|
sheets: sheetsOut,
|
|
110
247
|
styles,
|
|
111
248
|
sharedStrings,
|
|
249
|
+
...sharedStringRuns.some((r) => r !== void 0) ? { sharedStringRuns } : {},
|
|
112
250
|
definedNames,
|
|
113
251
|
date1904,
|
|
114
252
|
...chartData.size > 0 ? { chartData } : {},
|
|
115
|
-
resources
|
|
253
|
+
resources,
|
|
116
254
|
...info ? { info } : {}
|
|
117
255
|
};
|
|
118
256
|
}
|
|
@@ -163,6 +301,54 @@ function resolvePivotStyle(p, palette) {
|
|
|
163
301
|
headerTextHex: "FFFFFF"
|
|
164
302
|
};
|
|
165
303
|
}
|
|
304
|
+
function resolveSlicer(def, cache, tableIndex, styles, sharedStrings, date1904, palette) {
|
|
305
|
+
const items = cache ? resolveTableSlicerItems(cache, tableIndex, styles, sharedStrings, date1904) : [];
|
|
306
|
+
return {
|
|
307
|
+
caption: def.caption,
|
|
308
|
+
columnCount: def.columnCount,
|
|
309
|
+
items,
|
|
310
|
+
...resolveSlicerStyle(def.styleName, palette)
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
function resolveTableSlicerItems(cache, tableIndex, styles, sharedStrings, date1904) {
|
|
314
|
+
const loc = cache.tableId !== void 0 ? tableIndex.get(cache.tableId) : void 0;
|
|
315
|
+
if (!loc || cache.columnId === void 0) return [];
|
|
316
|
+
const colOffset = cache.columnId - 1;
|
|
317
|
+
const absCol = loc.ref.startColumn + colOffset;
|
|
318
|
+
if (absCol < loc.ref.startColumn || absCol > loc.ref.endColumn) return [];
|
|
319
|
+
const firstDataRow = loc.ref.startRow + Math.max(1, loc.headerRows);
|
|
320
|
+
const filter = loc.filters.find((f) => f.colId === colOffset);
|
|
321
|
+
const kept = filter ? new Set(filter.values) : void 0;
|
|
322
|
+
const byRow = /* @__PURE__ */ new Map();
|
|
323
|
+
for (const c of loc.cells) {
|
|
324
|
+
if (c.column !== absCol || c.row < firstDataRow || c.row > loc.ref.endRow) continue;
|
|
325
|
+
byRow.set(c.row, resolveCellText(c, sharedStrings, styles, date1904));
|
|
326
|
+
}
|
|
327
|
+
const seen = /* @__PURE__ */ new Set();
|
|
328
|
+
const out = [];
|
|
329
|
+
for (const row of [...byRow.keys()].sort((a, b) => a - b)) {
|
|
330
|
+
const label = byRow.get(row);
|
|
331
|
+
if (label === "" || seen.has(label)) continue;
|
|
332
|
+
seen.add(label);
|
|
333
|
+
out.push({
|
|
334
|
+
label,
|
|
335
|
+
selected: kept ? kept.has(label) : true
|
|
336
|
+
});
|
|
337
|
+
if (out.length >= MAX_SLICER_ITEMS) break;
|
|
338
|
+
}
|
|
339
|
+
return out;
|
|
340
|
+
}
|
|
341
|
+
function resolveSlicerStyle(styleName, palette) {
|
|
342
|
+
const m = styleName ? /SlicerStyle[A-Za-z]*?(\d+)/i.exec(styleName) : null;
|
|
343
|
+
const column = m ? (Number(m[1]) - 1) % 7 : 1;
|
|
344
|
+
const base = column === 0 ? "7F7F7F" : palette.get(`accent${column}`) ?? "4472C4";
|
|
345
|
+
return {
|
|
346
|
+
headerHex: base,
|
|
347
|
+
headerTextHex: "FFFFFF",
|
|
348
|
+
selectedHex: base,
|
|
349
|
+
selectedTextHex: "FFFFFF"
|
|
350
|
+
};
|
|
351
|
+
}
|
|
166
352
|
function lighten(hex, amount) {
|
|
167
353
|
const m = /^#?([0-9a-fA-F]{6})$/.exec(hex);
|
|
168
354
|
const n = m ? parseInt(m[1], 16) : 4485828;
|
|
@@ -200,7 +200,7 @@ function worksheetXml(grid, tableRelIds = [], drawingRelId) {
|
|
|
200
200
|
return `<row r="${r + 1}"${heightAttrs}>${cells.map(cellXml).join("")}</row>`;
|
|
201
201
|
}).join("");
|
|
202
202
|
const mergesXml = grid.merges.length > 0 ? `<mergeCells count="${grid.merges.length}">${grid.merges.map((m) => `<mergeCell ref="${cellRef(m.startRow, m.startColumn)}:${cellRef(m.endRow, m.endColumn)}"/>`).join("")}</mergeCells>` : "";
|
|
203
|
-
return XML_DECL + `<worksheet xmlns="${MAIN_NS}" xmlns:r="${R_NS}">` + (grid.fitToPage ? "<sheetPr><pageSetUpPr fitToPage=\"1\"/></sheetPr>" : "") + dimension + sheetViewsXml(grid.pane) + colsXml + `<sheetData>${rowsXml}</sheetData>` + mergesXml + conditionalFormattingXml(grid.conditionalFormats) + printOptionsXml(grid.printOptions) + pageMarginsXml(grid.pageMargins) + pageSetupXml(grid.pageSetup) + breaksXml("rowBreaks", grid.rowBreaks) + breaksXml("colBreaks", grid.colBreaks) + (drawingRelId ? `<drawing r:id="${drawingRelId}"/>` : "") + (tableRelIds.length > 0 ? `<tableParts count="${tableRelIds.length}">${tableRelIds.map((id) => `<tablePart r:id="${id}"/>`).join("")}</tableParts>` : "") + sparklineExtXml(grid.sparklines) + "</worksheet>";
|
|
203
|
+
return XML_DECL + `<worksheet xmlns="${MAIN_NS}" xmlns:r="${R_NS}">` + (grid.fitToPage ? "<sheetPr><pageSetUpPr fitToPage=\"1\"/></sheetPr>" : "") + dimension + sheetViewsXml(grid.pane) + colsXml + `<sheetData>${rowsXml}</sheetData>` + mergesXml + conditionalFormattingXml(grid.conditionalFormats) + dataValidationsXml(grid.dataValidations) + printOptionsXml(grid.printOptions) + pageMarginsXml(grid.pageMargins) + pageSetupXml(grid.pageSetup) + breaksXml("rowBreaks", grid.rowBreaks) + breaksXml("colBreaks", grid.colBreaks) + (drawingRelId ? `<drawing r:id="${drawingRelId}"/>` : "") + (tableRelIds.length > 0 ? `<tableParts count="${tableRelIds.length}">${tableRelIds.map((id) => `<tablePart r:id="${id}"/>`).join("")}</tableParts>` : "") + sparklineExtXml(grid.sparklines) + "</worksheet>";
|
|
204
204
|
}
|
|
205
205
|
function sheetViewsXml(pane) {
|
|
206
206
|
if (!pane || pane.frozenRows <= 0 && pane.frozenCols <= 0) return "";
|
|
@@ -230,11 +230,27 @@ 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) {
|
|
236
244
|
return `<cfvo type="${cfvo.type}"${cfvo.val !== void 0 ? ` val="${escapeAttr(cfvo.val)}"` : ""}/>`;
|
|
237
245
|
}
|
|
246
|
+
function dataValidationsXml(dvs) {
|
|
247
|
+
if (!dvs || dvs.length === 0) return "";
|
|
248
|
+
return `<dataValidations count="${dvs.length}">${dvs.map(dataValidationXml).join("")}</dataValidations>`;
|
|
249
|
+
}
|
|
250
|
+
function dataValidationXml(dv) {
|
|
251
|
+
const sqref = dv.ranges.map(rangeRef).join(" ");
|
|
252
|
+
return `<dataValidation${(dv.type !== "none" ? ` type="${dv.type}"` : "") + (dv.operator !== void 0 ? ` operator="${escapeAttr(dv.operator)}"` : "") + (dv.allowBlank ? " allowBlank=\"1\"" : "") + (dv.showDropDown ? " showDropDown=\"1\"" : "") + (dv.showInputMessage ? " showInputMessage=\"1\"" : "") + (dv.showErrorMessage ? " showErrorMessage=\"1\"" : "") + (dv.errorStyle !== void 0 ? ` errorStyle="${escapeAttr(dv.errorStyle)}"` : "") + (dv.promptTitle !== void 0 ? ` promptTitle="${escapeAttr(dv.promptTitle)}"` : "") + (dv.prompt !== void 0 ? ` prompt="${escapeAttr(dv.prompt)}"` : "") + (dv.errorTitle !== void 0 ? ` errorTitle="${escapeAttr(dv.errorTitle)}"` : "") + (dv.error !== void 0 ? ` error="${escapeAttr(dv.error)}"` : "") + ` sqref="${sqref}"`}>${(dv.formula1 !== void 0 ? `<formula1>${escapeText(dv.formula1)}</formula1>` : "") + (dv.formula2 !== void 0 ? `<formula2>${escapeText(dv.formula2)}</formula2>` : "")}</dataValidation>`;
|
|
253
|
+
}
|
|
238
254
|
var X14_NS = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main";
|
|
239
255
|
var XM_NS = "http://schemas.microsoft.com/office/excel/2006/main";
|
|
240
256
|
function sparklineExtXml(sparklines) {
|
|
@@ -305,7 +321,7 @@ function patternFill(f) {
|
|
|
305
321
|
return `<patternFill${f.patternType ? ` patternType="${f.patternType}"` : ""}>${inner}</patternFill>`;
|
|
306
322
|
}
|
|
307
323
|
function borderXml(b) {
|
|
308
|
-
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>`;
|
|
309
325
|
}
|
|
310
326
|
function edgeXml(name, edge) {
|
|
311
327
|
if (!edge || !edge.style) return `<${name}/>`;
|
|
@@ -315,7 +331,7 @@ function edgeXml(name, edge) {
|
|
|
315
331
|
function cellXfXml(xf) {
|
|
316
332
|
const apply = applyAttr("applyNumberFormat", xf.applyNumberFormat) + applyAttr("applyFont", xf.applyFont) + applyAttr("applyFill", xf.applyFill) + applyAttr("applyBorder", xf.applyBorder) + applyAttr("applyAlignment", xf.applyAlignment);
|
|
317
333
|
const a = xf.alignment;
|
|
318
|
-
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\"" : ""}/>` : "";
|
|
319
335
|
return `<xf numFmtId="${xf.numFmtId}" fontId="${xf.fontId}" fillId="${xf.fillId}" borderId="${xf.borderId}"${apply}>` + alignment + "</xf>";
|
|
320
336
|
}
|
|
321
337
|
function applyAttr(name, value) {
|
|
@@ -496,6 +496,7 @@ function emitCell(out, cell, table, ctx, pos) {
|
|
|
496
496
|
if (!cell.properties.shading) css.push("background-color:#fff");
|
|
497
497
|
}
|
|
498
498
|
out.push(`<${tag}${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""} style="${css.join(";")}">`);
|
|
499
|
+
if (cell.properties.dropdown) out.push(cellDropdownSvg());
|
|
499
500
|
if (cell.properties.icon) out.push(cellIconSvg(cell.properties.icon));
|
|
500
501
|
if (cell.properties.sparkline) out.push(cellSparklineSvg(cell.properties.sparkline));
|
|
501
502
|
for (const child of cell.content) emitBlock(out, child, ctx);
|
|
@@ -514,6 +515,9 @@ function cellSparklineSvg(sp) {
|
|
|
514
515
|
}
|
|
515
516
|
return `<svg width="${W}" height="${H}" viewBox="0 0 ${W} ${H}" style="vertical-align:middle"><g transform="translate(0,${H}) scale(1,-1)">${parts.join("")}</g></svg>`;
|
|
516
517
|
}
|
|
518
|
+
function cellDropdownSvg() {
|
|
519
|
+
return "<svg width=\"13\" height=\"13\" viewBox=\"0 0 13 13\" style=\"float:right;margin-left:3px\"><rect x=\"0.5\" y=\"0.5\" width=\"12\" height=\"12\" fill=\"#F0F0F0\" stroke=\"#B0B0B0\"/><polygon points=\"3.4,5 9.6,5 6.5,8.6\" fill=\"#595959\"/></svg>";
|
|
520
|
+
}
|
|
517
521
|
var ICON_EMPTY_HEX = "BFBFBF";
|
|
518
522
|
function cellIconSvg(icon) {
|
|
519
523
|
const fill = `#${icon.colorHex}`;
|
|
@@ -668,6 +668,24 @@ function rectAtPath(x, y, w, h) {
|
|
|
668
668
|
var CF_ICON_SIZE_PT = 9;
|
|
669
669
|
var CF_ICON_GUTTER_PT = 12;
|
|
670
670
|
var CF_ICON_EMPTY_HEX = "BFBFBF";
|
|
671
|
+
var DROPDOWN_BUTTON_PT = 11;
|
|
672
|
+
var DROPDOWN_GUTTER_PT = 13;
|
|
673
|
+
var DROPDOWN_FILL_HEX = "F0F0F0";
|
|
674
|
+
var DROPDOWN_BORDER_HEX = "B0B0B0";
|
|
675
|
+
var DROPDOWN_ARROW_HEX = "595959";
|
|
676
|
+
function buildDropdownPrims(s) {
|
|
677
|
+
return [{
|
|
678
|
+
paths: [rectAtPath(0, 0, s, s)],
|
|
679
|
+
fillColorHex: DROPDOWN_FILL_HEX,
|
|
680
|
+
stroke: {
|
|
681
|
+
colorHex: DROPDOWN_BORDER_HEX,
|
|
682
|
+
widthPt: .5
|
|
683
|
+
}
|
|
684
|
+
}, {
|
|
685
|
+
paths: [trianglePath([s / 2, s * .34], [s * .26, s * .64], [s * .74, s * .64])],
|
|
686
|
+
fillColorHex: DROPDOWN_ARROW_HEX
|
|
687
|
+
}];
|
|
688
|
+
}
|
|
671
689
|
function buildCellIconShape(icon, s) {
|
|
672
690
|
const color = icon.colorHex;
|
|
673
691
|
switch (icon.shape) {
|
|
@@ -1671,8 +1689,9 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
|
|
|
1671
1689
|
const padTopPt = cellMar?.top ?? tableMar?.top ?? 0;
|
|
1672
1690
|
const padBottomPt = cellMar?.bottom ?? tableMar?.bottom ?? 0;
|
|
1673
1691
|
const padLeftBase = cellMar?.left ?? tableMar?.left ?? DEFAULT_CELL_PADDING_TWIPS * TWIP_TO_PT;
|
|
1674
|
-
const
|
|
1692
|
+
const padRightBase = cellMar?.right ?? tableMar?.right ?? DEFAULT_CELL_PADDING_TWIPS * TWIP_TO_PT;
|
|
1675
1693
|
const padLeftPt = padLeftBase + (cell.properties.icon ? CF_ICON_GUTTER_PT : 0);
|
|
1694
|
+
const padRightPt = padRightBase + (cell.properties.dropdown ? DROPDOWN_GUTTER_PT : 0);
|
|
1676
1695
|
const innerWidth = Math.max(1, widthPt - padLeftPt - padRightPt);
|
|
1677
1696
|
const lines = [];
|
|
1678
1697
|
const nestedTables = [];
|
|
@@ -1705,6 +1724,7 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
|
|
|
1705
1724
|
...cell.properties.dataBar ? { dataBar: cell.properties.dataBar } : {},
|
|
1706
1725
|
...cell.properties.icon ? { icon: cell.properties.icon } : {},
|
|
1707
1726
|
...cell.properties.sparkline ? { sparkline: cell.properties.sparkline } : {},
|
|
1727
|
+
...cell.properties.dropdown ? { dropdown: true } : {},
|
|
1708
1728
|
lines,
|
|
1709
1729
|
...nestedTables.length > 0 ? { nestedTables } : {},
|
|
1710
1730
|
contentHeightPt,
|
|
@@ -2362,6 +2382,28 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
|
|
|
2362
2382
|
});
|
|
2363
2383
|
}
|
|
2364
2384
|
}
|
|
2385
|
+
if (cell.dropdown && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
|
|
2386
|
+
const btn = Math.min(DROPDOWN_BUTTON_PT, Math.max(0, row.heightPt - 2));
|
|
2387
|
+
if (btn > 1) {
|
|
2388
|
+
const btnTransform = flipTransform([
|
|
2389
|
+
1,
|
|
2390
|
+
0,
|
|
2391
|
+
0,
|
|
2392
|
+
1,
|
|
2393
|
+
cellX + cell.widthPt - btn - 1.5,
|
|
2394
|
+
rowBottom + (row.heightPt - btn) / 2
|
|
2395
|
+
], pageHeight);
|
|
2396
|
+
for (const prim of buildDropdownPrims(btn)) out.push({
|
|
2397
|
+
type: "shape",
|
|
2398
|
+
shape: {
|
|
2399
|
+
paths: prim.paths,
|
|
2400
|
+
...prim.fillColorHex ? { fillColorHex: prim.fillColorHex } : {},
|
|
2401
|
+
...prim.stroke ? { stroke: prim.stroke } : {},
|
|
2402
|
+
transform: btnTransform
|
|
2403
|
+
}
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2365
2407
|
if (cell.sparkline && cell.sparkline.values.length > 0 && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
|
|
2366
2408
|
const inset = 1.5;
|
|
2367
2409
|
const sw = cell.widthPt - 2 * inset;
|
|
@@ -2388,6 +2430,35 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
|
|
|
2388
2430
|
}
|
|
2389
2431
|
}
|
|
2390
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
|
+
}
|
|
2391
2462
|
if (cell.mergeRole === "middle" || cell.mergeRole === "end") continue;
|
|
2392
2463
|
let textY = rowTop - cell.padTopPt;
|
|
2393
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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reamkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "Ream — convert DOCX, XLSX, PPTX and PDF to PDF, SVG, HTML, DOCX and XLSX, built from scratch on the ECMA-376 and ISO 32000 specifications. Parse once, convert anywhere.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alex Krassavin <info@reamkit.dev>",
|