reamkit 1.11.0 → 1.13.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/converter/facade.js +1 -1
- package/dist/esm/core/converter/project.js +1 -0
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +24 -0
- package/dist/esm/core/ir/features.d.ts +1 -0
- package/dist/esm/core/ir/features.js +1 -0
- package/dist/esm/core/ir/flow.d.ts +3 -1
- package/dist/esm/core/ir/sheet.d.ts +14 -0
- package/dist/esm/core/po-helpers.d.ts +2 -0
- package/dist/esm/core/po-helpers.js +14 -1
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +34 -0
- package/dist/esm/excel/index.d.ts +1 -1
- package/dist/esm/excel/pivot-table-parser.d.ts +2 -0
- package/dist/esm/excel/pivot-table-parser.js +68 -0
- package/dist/esm/excel/print-model.d.ts +5 -2
- package/dist/esm/excel/print-model.js +123 -9
- package/dist/esm/excel/sheet-to-flow.js +5 -1
- package/dist/esm/excel/slicer-parser.d.ts +15 -0
- package/dist/esm/excel/slicer-parser.js +76 -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 +56 -0
- package/dist/esm/excel/xlsx-reader.js +129 -5
- package/dist/esm/excel/xlsx-writer.js +9 -1
- package/dist/esm/html/html-writer.js +56 -2
- package/dist/esm/layout/page-doc.d.ts +1 -0
- package/dist/esm/layout/styled-layout.d.ts +8 -1
- package/dist/esm/layout/styled-layout.js +154 -6
- package/dist/esm/pdf/styled-page-emitter.js +38 -0
- package/dist/esm/pptx/pptx-reader.d.ts +2 -0
- package/dist/esm/pptx/pptx-reader.js +30 -3
- package/dist/esm/pptx/slide-parser.d.ts +8 -2
- package/dist/esm/pptx/slide-parser.js +80 -12
- package/dist/esm/word/document-parser.d.ts +17 -2
- package/dist/esm/word/document-parser.js +178 -18
- package/dist/esm/word/docx-reader.js +54 -4
- package/dist/esm/word/docx-writer.js +84 -1
- package/dist/esm/word/drawing-parser.d.ts +7 -0
- package/dist/esm/word/drawing-parser.js +13 -0
- package/dist/esm/word/index.d.ts +2 -1
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ var parser = new XMLParser({
|
|
|
9
9
|
parseTagValue: false,
|
|
10
10
|
removeNSPrefix: true
|
|
11
11
|
});
|
|
12
|
-
function
|
|
12
|
+
function parseTablePartFull(data) {
|
|
13
13
|
const t = asObj(parser.parse(decoder.decode(data))["table"]);
|
|
14
14
|
if (!t) return void 0;
|
|
15
15
|
const refStr = strAttr(t, "ref");
|
|
@@ -18,7 +18,8 @@ function parseTablePart(data) {
|
|
|
18
18
|
const si = asObj(t["tableStyleInfo"]);
|
|
19
19
|
const name = strAttr(t, "name");
|
|
20
20
|
const styleName = si ? strAttr(si, "name") : void 0;
|
|
21
|
-
|
|
21
|
+
const af = asObj(t["autoFilter"]);
|
|
22
|
+
const table = {
|
|
22
23
|
ref,
|
|
23
24
|
...name ? { name } : {},
|
|
24
25
|
...styleName ? { styleName } : {},
|
|
@@ -27,9 +28,40 @@ function parseTablePart(data) {
|
|
|
27
28
|
showColumnStripes: si ? boolAttr(si, "showColumnStripes") : false,
|
|
28
29
|
showFirstColumn: si ? boolAttr(si, "showFirstColumn") : false,
|
|
29
30
|
showLastColumn: si ? boolAttr(si, "showLastColumn") : false,
|
|
30
|
-
autoFilter:
|
|
31
|
+
autoFilter: af !== void 0
|
|
32
|
+
};
|
|
33
|
+
const id = numAttr(t, "id");
|
|
34
|
+
return {
|
|
35
|
+
table,
|
|
36
|
+
...id !== void 0 ? { id } : {},
|
|
37
|
+
filters: parseFilterColumns(af)
|
|
31
38
|
};
|
|
32
39
|
}
|
|
40
|
+
function parseFilterColumns(af) {
|
|
41
|
+
if (!af) return [];
|
|
42
|
+
const out = [];
|
|
43
|
+
for (const fc of asArray(af["filterColumn"])) {
|
|
44
|
+
const col = asObj(fc);
|
|
45
|
+
if (!col) continue;
|
|
46
|
+
const colId = numAttr(col, "colId");
|
|
47
|
+
if (colId === void 0) continue;
|
|
48
|
+
const filters = asObj(col["filters"]);
|
|
49
|
+
if (!filters) continue;
|
|
50
|
+
const values = [];
|
|
51
|
+
for (const f of asArray(filters["filter"])) {
|
|
52
|
+
const v = asObj(f) ? strAttr(asObj(f), "val") : void 0;
|
|
53
|
+
if (v !== void 0) values.push(v);
|
|
54
|
+
}
|
|
55
|
+
out.push({
|
|
56
|
+
colId,
|
|
57
|
+
values
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
function asArray(v) {
|
|
63
|
+
return Array.isArray(v) ? v : v === void 0 || v === null ? [] : [v];
|
|
64
|
+
}
|
|
33
65
|
function asObj(v) {
|
|
34
66
|
return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
|
|
35
67
|
}
|
|
@@ -48,4 +80,4 @@ function boolAttr(obj, name) {
|
|
|
48
80
|
return v === "1" || v === "true";
|
|
49
81
|
}
|
|
50
82
|
//#endregion
|
|
51
|
-
export {
|
|
83
|
+
export { parseTablePartFull };
|
|
@@ -37,6 +37,7 @@ function parseWorksheet(data) {
|
|
|
37
37
|
const drawingNode = wsObj["drawing"];
|
|
38
38
|
const drawingRelId = drawingNode && typeof drawingNode === "object" ? strAttr(drawingNode, "id") : void 0;
|
|
39
39
|
const conditionalFormats = parseConditionalFormatting(wsObj);
|
|
40
|
+
const dataValidations = parseDataValidations(wsObj);
|
|
40
41
|
const sparklines = parseSparklines(wsObj);
|
|
41
42
|
const tablePartRelIds = parseTableParts(wsObj);
|
|
42
43
|
const printModel = {
|
|
@@ -49,6 +50,7 @@ function parseWorksheet(data) {
|
|
|
49
50
|
...pane ? { pane } : {},
|
|
50
51
|
...drawingRelId !== void 0 ? { drawingRelId } : {},
|
|
51
52
|
...conditionalFormats.length > 0 ? { conditionalFormats } : {},
|
|
53
|
+
...dataValidations.length > 0 ? { dataValidations } : {},
|
|
52
54
|
...sparklines.length > 0 ? { sparklines } : {},
|
|
53
55
|
...tablePartRelIds.length > 0 ? { tablePartRelIds } : {}
|
|
54
56
|
};
|
|
@@ -298,6 +300,60 @@ function parseConditionalFormatting(ws) {
|
|
|
298
300
|
}
|
|
299
301
|
return out;
|
|
300
302
|
}
|
|
303
|
+
var DV_TYPES = new Set([
|
|
304
|
+
"none",
|
|
305
|
+
"whole",
|
|
306
|
+
"decimal",
|
|
307
|
+
"list",
|
|
308
|
+
"date",
|
|
309
|
+
"time",
|
|
310
|
+
"textLength",
|
|
311
|
+
"custom"
|
|
312
|
+
]);
|
|
313
|
+
function parseDataValidations(ws) {
|
|
314
|
+
const node = asObjectNode(ws["dataValidations"]);
|
|
315
|
+
if (!node) return [];
|
|
316
|
+
const out = [];
|
|
317
|
+
for (const dv of toArray(node["dataValidation"])) {
|
|
318
|
+
const obj = asObjectNode(dv);
|
|
319
|
+
if (!obj) continue;
|
|
320
|
+
const sqref = strAttr(obj, "sqref");
|
|
321
|
+
if (!sqref) continue;
|
|
322
|
+
const ranges = parseSqref(sqref);
|
|
323
|
+
if (ranges.length === 0) continue;
|
|
324
|
+
const typeRaw = strAttr(obj, "type");
|
|
325
|
+
const type = typeRaw && DV_TYPES.has(typeRaw) ? typeRaw : "none";
|
|
326
|
+
const operator = strAttr(obj, "operator");
|
|
327
|
+
const errorStyle = strAttr(obj, "errorStyle");
|
|
328
|
+
const formula1 = formulaText(obj["formula1"]);
|
|
329
|
+
const formula2 = formulaText(obj["formula2"]);
|
|
330
|
+
const promptTitle = strAttr(obj, "promptTitle");
|
|
331
|
+
const prompt = strAttr(obj, "prompt");
|
|
332
|
+
const errorTitle = strAttr(obj, "errorTitle");
|
|
333
|
+
const error = strAttr(obj, "error");
|
|
334
|
+
out.push({
|
|
335
|
+
type,
|
|
336
|
+
ranges,
|
|
337
|
+
...operator !== void 0 ? { operator } : {},
|
|
338
|
+
...boolAttr(obj, "allowBlank") ? { allowBlank: true } : {},
|
|
339
|
+
...boolAttr(obj, "showDropDown") ? { showDropDown: true } : {},
|
|
340
|
+
...boolAttr(obj, "showInputMessage") ? { showInputMessage: true } : {},
|
|
341
|
+
...boolAttr(obj, "showErrorMessage") ? { showErrorMessage: true } : {},
|
|
342
|
+
...errorStyle !== void 0 ? { errorStyle } : {},
|
|
343
|
+
...formula1 !== void 0 ? { formula1 } : {},
|
|
344
|
+
...formula2 !== void 0 ? { formula2 } : {},
|
|
345
|
+
...promptTitle !== void 0 ? { promptTitle } : {},
|
|
346
|
+
...prompt !== void 0 ? { prompt } : {},
|
|
347
|
+
...errorTitle !== void 0 ? { errorTitle } : {},
|
|
348
|
+
...error !== void 0 ? { error } : {}
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
return out;
|
|
352
|
+
}
|
|
353
|
+
function boolAttr(obj, key) {
|
|
354
|
+
const raw = strAttr(obj, key);
|
|
355
|
+
return raw === "1" || raw === "true";
|
|
356
|
+
}
|
|
301
357
|
function parseSqref(sqref) {
|
|
302
358
|
const out = [];
|
|
303
359
|
for (const token of sqref.split(/\s+/)) {
|
|
@@ -13,14 +13,20 @@ import { parseWorkbook } from "./workbook-parser.js";
|
|
|
13
13
|
import { parseWorksheet } from "./worksheet-parser.js";
|
|
14
14
|
import { EMPTY_XLSX_STYLES, parseXlsxStyles } from "./styles-parser.js";
|
|
15
15
|
import "./index.js";
|
|
16
|
+
import { resolveCellText } from "./print-model.js";
|
|
16
17
|
import { parseSheetDrawing } from "./sheet-drawing.js";
|
|
17
|
-
import {
|
|
18
|
+
import { parseTablePartFull } from "./table-parser.js";
|
|
19
|
+
import { parsePivotTablePart } from "./pivot-table-parser.js";
|
|
20
|
+
import { parseSlicerCachePart, parseSlicerPart } from "./slicer-parser.js";
|
|
18
21
|
import { projectSheetDoc } from "./sheet-to-flow.js";
|
|
19
22
|
//#region src/excel/xlsx-reader.ts
|
|
20
23
|
var WORKBOOK_PART = "xl/workbook.xml";
|
|
21
24
|
var SHARED_STRINGS_PART = "xl/sharedStrings.xml";
|
|
22
25
|
var STYLES_PART = "xl/styles.xml";
|
|
23
26
|
var CORE_PROPS_PART = "docProps/core.xml";
|
|
27
|
+
var SLICER_REL_TAIL = "/slicer";
|
|
28
|
+
var SLICER_CACHE_REL_TAIL = "/slicerCache";
|
|
29
|
+
var MAX_SLICER_ITEMS = 256;
|
|
24
30
|
function readXlsx(xlsx) {
|
|
25
31
|
return {
|
|
26
32
|
doc: projectSheetDoc(readXlsxToSheetDoc(xlsx)),
|
|
@@ -42,6 +48,8 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
42
48
|
const palette = buildThemePalette(pkg, workbookRels);
|
|
43
49
|
const resolveColor = makeColorResolver(palette);
|
|
44
50
|
const sheetsOut = [];
|
|
51
|
+
const tableIndex = /* @__PURE__ */ new Map();
|
|
52
|
+
const pendingSlicers = [];
|
|
45
53
|
for (let sheetIdx = 0; sheetIdx < sheets.length; sheetIdx++) {
|
|
46
54
|
const sheet = sheets[sheetIdx];
|
|
47
55
|
const sheetRel = workbookRels.find((r) => r.id === sheet.relationshipId);
|
|
@@ -74,14 +82,46 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
74
82
|
for (const rid of worksheet.tablePartRelIds) {
|
|
75
83
|
const rel = wsRels.find((r) => r.id === rid);
|
|
76
84
|
const part = rel ? pkg.resolveRelatedPart(resolved.path, rel) : void 0;
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
85
|
+
const full = part ? parseTablePartFull(part.data) : void 0;
|
|
86
|
+
if (!full) continue;
|
|
87
|
+
resolvedTables.push(resolveTableStyle(full.table, palette));
|
|
88
|
+
if (full.id !== void 0) tableIndex.set(full.id, {
|
|
89
|
+
cells: worksheet.cells,
|
|
90
|
+
ref: full.table.ref,
|
|
91
|
+
headerRows: full.table.headerRowCount,
|
|
92
|
+
filters: full.filters
|
|
93
|
+
});
|
|
79
94
|
}
|
|
80
95
|
if (resolvedTables.length > 0) tables = resolvedTables;
|
|
81
96
|
}
|
|
82
|
-
|
|
97
|
+
{
|
|
98
|
+
const wsRels = pkg.getPartRelationships(resolved.path);
|
|
99
|
+
const defs = [];
|
|
100
|
+
for (const rel of wsRels) {
|
|
101
|
+
if (!rel.type.endsWith(SLICER_REL_TAIL)) continue;
|
|
102
|
+
const part = pkg.resolveRelatedPart(resolved.path, rel);
|
|
103
|
+
if (part) defs.push(...parseSlicerPart(part.data));
|
|
104
|
+
}
|
|
105
|
+
if (defs.length > 0) pendingSlicers.push({
|
|
106
|
+
outIdx: sheetsOut.length,
|
|
107
|
+
defs
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
let pivotTables;
|
|
111
|
+
{
|
|
112
|
+
const resolvedPivots = [];
|
|
113
|
+
for (const rel of pkg.getPartRelationships(resolved.path)) {
|
|
114
|
+
if (!isOoxmlRel(rel.type, "pivotTable")) continue;
|
|
115
|
+
const part = pkg.resolveRelatedPart(resolved.path, rel);
|
|
116
|
+
const parsed = part ? parsePivotTablePart(part.data) : void 0;
|
|
117
|
+
if (parsed) resolvedPivots.push(resolvePivotStyle(parsed, palette));
|
|
118
|
+
}
|
|
119
|
+
if (resolvedPivots.length > 0) pivotTables = resolvedPivots;
|
|
120
|
+
}
|
|
121
|
+
const grid = tables || pivotTables ? {
|
|
83
122
|
...worksheet,
|
|
84
|
-
tables
|
|
123
|
+
...tables ? { tables } : {},
|
|
124
|
+
...pivotTables ? { pivotTables } : {}
|
|
85
125
|
} : worksheet;
|
|
86
126
|
sheetsOut.push({
|
|
87
127
|
name: sheet.name,
|
|
@@ -89,6 +129,24 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
89
129
|
...charts.length > 0 ? { charts } : {}
|
|
90
130
|
});
|
|
91
131
|
}
|
|
132
|
+
if (pendingSlicers.length > 0) {
|
|
133
|
+
const cacheByName = /* @__PURE__ */ new Map();
|
|
134
|
+
for (const rel of workbookRels) {
|
|
135
|
+
if (!rel.type.endsWith(SLICER_CACHE_REL_TAIL)) continue;
|
|
136
|
+
const part = pkg.resolveRelatedPart(WORKBOOK_PART, rel);
|
|
137
|
+
const cache = part ? parseSlicerCachePart(part.data) : void 0;
|
|
138
|
+
if (cache) cacheByName.set(cache.name, cache);
|
|
139
|
+
}
|
|
140
|
+
for (const { outIdx, defs } of pendingSlicers) {
|
|
141
|
+
const sheet = sheetsOut[outIdx];
|
|
142
|
+
if (!sheet) continue;
|
|
143
|
+
const slicers = defs.map((def) => resolveSlicer(def, cacheByName.get(def.cacheName), tableIndex, styles, sharedStrings, date1904, palette));
|
|
144
|
+
sheetsOut[outIdx] = {
|
|
145
|
+
...sheet,
|
|
146
|
+
slicers
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
92
150
|
const coreData = pkg.getPart(CORE_PROPS_PART);
|
|
93
151
|
const info = infoFromCore(coreData ? parseCoreProperties(coreData) : void 0);
|
|
94
152
|
return {
|
|
@@ -132,6 +190,72 @@ function resolveTableStyle(t, palette) {
|
|
|
132
190
|
headerTextHex: "FFFFFF"
|
|
133
191
|
};
|
|
134
192
|
}
|
|
193
|
+
function resolvePivotStyle(p, palette) {
|
|
194
|
+
const m = p.styleName ? /PivotStyle(Light|Medium|Dark)(\d+)/i.exec(p.styleName) : null;
|
|
195
|
+
if (!m) return p;
|
|
196
|
+
const kind = m[1].toLowerCase();
|
|
197
|
+
const column = (Number(m[2]) - 1) % 7;
|
|
198
|
+
const base = column === 0 ? "7F7F7F" : palette.get(`accent${column}`) ?? "4472C4";
|
|
199
|
+
if (kind === "light") return {
|
|
200
|
+
...p,
|
|
201
|
+
headerHex: lighten(base, .6),
|
|
202
|
+
bandHex: lighten(base, .85)
|
|
203
|
+
};
|
|
204
|
+
return {
|
|
205
|
+
...p,
|
|
206
|
+
headerHex: base,
|
|
207
|
+
bandHex: lighten(base, .8),
|
|
208
|
+
headerTextHex: "FFFFFF"
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function resolveSlicer(def, cache, tableIndex, styles, sharedStrings, date1904, palette) {
|
|
212
|
+
const items = cache ? resolveTableSlicerItems(cache, tableIndex, styles, sharedStrings, date1904) : [];
|
|
213
|
+
return {
|
|
214
|
+
caption: def.caption,
|
|
215
|
+
columnCount: def.columnCount,
|
|
216
|
+
items,
|
|
217
|
+
...resolveSlicerStyle(def.styleName, palette)
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function resolveTableSlicerItems(cache, tableIndex, styles, sharedStrings, date1904) {
|
|
221
|
+
const loc = cache.tableId !== void 0 ? tableIndex.get(cache.tableId) : void 0;
|
|
222
|
+
if (!loc || cache.columnId === void 0) return [];
|
|
223
|
+
const colOffset = cache.columnId - 1;
|
|
224
|
+
const absCol = loc.ref.startColumn + colOffset;
|
|
225
|
+
if (absCol < loc.ref.startColumn || absCol > loc.ref.endColumn) return [];
|
|
226
|
+
const firstDataRow = loc.ref.startRow + Math.max(1, loc.headerRows);
|
|
227
|
+
const filter = loc.filters.find((f) => f.colId === colOffset);
|
|
228
|
+
const kept = filter ? new Set(filter.values) : void 0;
|
|
229
|
+
const byRow = /* @__PURE__ */ new Map();
|
|
230
|
+
for (const c of loc.cells) {
|
|
231
|
+
if (c.column !== absCol || c.row < firstDataRow || c.row > loc.ref.endRow) continue;
|
|
232
|
+
byRow.set(c.row, resolveCellText(c, sharedStrings, styles, date1904));
|
|
233
|
+
}
|
|
234
|
+
const seen = /* @__PURE__ */ new Set();
|
|
235
|
+
const out = [];
|
|
236
|
+
for (const row of [...byRow.keys()].sort((a, b) => a - b)) {
|
|
237
|
+
const label = byRow.get(row);
|
|
238
|
+
if (label === "" || seen.has(label)) continue;
|
|
239
|
+
seen.add(label);
|
|
240
|
+
out.push({
|
|
241
|
+
label,
|
|
242
|
+
selected: kept ? kept.has(label) : true
|
|
243
|
+
});
|
|
244
|
+
if (out.length >= MAX_SLICER_ITEMS) break;
|
|
245
|
+
}
|
|
246
|
+
return out;
|
|
247
|
+
}
|
|
248
|
+
function resolveSlicerStyle(styleName, palette) {
|
|
249
|
+
const m = styleName ? /SlicerStyle[A-Za-z]*?(\d+)/i.exec(styleName) : null;
|
|
250
|
+
const column = m ? (Number(m[1]) - 1) % 7 : 1;
|
|
251
|
+
const base = column === 0 ? "7F7F7F" : palette.get(`accent${column}`) ?? "4472C4";
|
|
252
|
+
return {
|
|
253
|
+
headerHex: base,
|
|
254
|
+
headerTextHex: "FFFFFF",
|
|
255
|
+
selectedHex: base,
|
|
256
|
+
selectedTextHex: "FFFFFF"
|
|
257
|
+
};
|
|
258
|
+
}
|
|
135
259
|
function lighten(hex, amount) {
|
|
136
260
|
const m = /^#?([0-9a-fA-F]{6})$/.exec(hex);
|
|
137
261
|
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 "";
|
|
@@ -235,6 +235,14 @@ function cfRuleXml(rule) {
|
|
|
235
235
|
function cfvoXml(cfvo) {
|
|
236
236
|
return `<cfvo type="${cfvo.type}"${cfvo.val !== void 0 ? ` val="${escapeAttr(cfvo.val)}"` : ""}/>`;
|
|
237
237
|
}
|
|
238
|
+
function dataValidationsXml(dvs) {
|
|
239
|
+
if (!dvs || dvs.length === 0) return "";
|
|
240
|
+
return `<dataValidations count="${dvs.length}">${dvs.map(dataValidationXml).join("")}</dataValidations>`;
|
|
241
|
+
}
|
|
242
|
+
function dataValidationXml(dv) {
|
|
243
|
+
const sqref = dv.ranges.map(rangeRef).join(" ");
|
|
244
|
+
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>`;
|
|
245
|
+
}
|
|
238
246
|
var X14_NS = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main";
|
|
239
247
|
var XM_NS = "http://schemas.microsoft.com/office/excel/2006/main";
|
|
240
248
|
function sparklineExtXml(sparklines) {
|
|
@@ -41,6 +41,7 @@ function writeHtml(flow) {
|
|
|
41
41
|
for (const el of flow.body) emitBlock(out, el, ctx);
|
|
42
42
|
emitNotesSection(out, flow.footnotes, ctx.notes.footnotes, "fn", ctx);
|
|
43
43
|
emitNotesSection(out, flow.endnotes, ctx.notes.endnotes, "en", ctx);
|
|
44
|
+
emitCommentsSection(out, flow.comments, ctx.notes.comments, ctx);
|
|
44
45
|
out.push("</article>");
|
|
45
46
|
out.push("</body>");
|
|
46
47
|
out.push("</html>");
|
|
@@ -76,7 +77,10 @@ var BASE_CSS = [
|
|
|
76
77
|
".tab{display:inline-block;min-width:18pt}",
|
|
77
78
|
"img{vertical-align:baseline}",
|
|
78
79
|
".notes{margin-top:18pt;font-size:smaller}",
|
|
79
|
-
".notes hr{margin:0 0 6pt;border:none;border-top:0.75pt solid #000;width:144pt;margin-left:0}"
|
|
80
|
+
".notes hr{margin:0 0 6pt;border:none;border-top:0.75pt solid #000;width:144pt;margin-left:0}",
|
|
81
|
+
".comment-range{background:#fff3a3}",
|
|
82
|
+
".comment-replies{margin-left:18pt}",
|
|
83
|
+
".comments .resolved>.comment-meta::after{content:\" \\2713\";color:#3a7d3a}"
|
|
80
84
|
].join("");
|
|
81
85
|
function collectReferencedAnchors(body) {
|
|
82
86
|
const out = /* @__PURE__ */ new Set();
|
|
@@ -92,10 +96,12 @@ function collectReferencedAnchors(body) {
|
|
|
92
96
|
function collectNoteNumbers(flow) {
|
|
93
97
|
const footnotes = /* @__PURE__ */ new Map();
|
|
94
98
|
const endnotes = /* @__PURE__ */ new Map();
|
|
99
|
+
const comments = /* @__PURE__ */ new Map();
|
|
95
100
|
const visit = (els) => {
|
|
96
101
|
for (const el of els) if (el.kind === "paragraph") for (const r of el.paragraph.runs) {
|
|
97
102
|
if (r.footnoteRef !== void 0 && !footnotes.has(r.footnoteRef)) footnotes.set(r.footnoteRef, footnotes.size + 1);
|
|
98
103
|
if (r.endnoteRef !== void 0 && !endnotes.has(r.endnoteRef)) endnotes.set(r.endnoteRef, endnotes.size + 1);
|
|
104
|
+
if (r.commentRef !== void 0 && !comments.has(r.commentRef)) comments.set(r.commentRef, comments.size + 1);
|
|
99
105
|
}
|
|
100
106
|
else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
|
|
101
107
|
else if (el.kind === "shape" && el.shape.text) visit(el.shape.text.content);
|
|
@@ -103,7 +109,8 @@ function collectNoteNumbers(flow) {
|
|
|
103
109
|
visit(flow.body);
|
|
104
110
|
return {
|
|
105
111
|
footnotes,
|
|
106
|
-
endnotes
|
|
112
|
+
endnotes,
|
|
113
|
+
comments
|
|
107
114
|
};
|
|
108
115
|
}
|
|
109
116
|
function emitNotesSection(out, content, numbers, prefix, ctx) {
|
|
@@ -118,6 +125,43 @@ function emitNotesSection(out, content, numbers, prefix, ctx) {
|
|
|
118
125
|
}
|
|
119
126
|
out.push("</ol></section>");
|
|
120
127
|
}
|
|
128
|
+
function emitCommentsSection(out, comments, numbers, ctx) {
|
|
129
|
+
if (!comments || numbers.size === 0) return;
|
|
130
|
+
const ordered = [...numbers.entries()].sort((a, b) => a[1] - b[1]).map(([id]) => id);
|
|
131
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
132
|
+
const roots = [];
|
|
133
|
+
for (const id of ordered) {
|
|
134
|
+
const parent = comments.get(id)?.parentId;
|
|
135
|
+
if (parent !== void 0 && parent !== id && numbers.has(parent)) {
|
|
136
|
+
const kids = childrenOf.get(parent);
|
|
137
|
+
if (kids) kids.push(id);
|
|
138
|
+
else childrenOf.set(parent, [id]);
|
|
139
|
+
} else roots.push(id);
|
|
140
|
+
}
|
|
141
|
+
const seen = /* @__PURE__ */ new Set();
|
|
142
|
+
const emitOne = (id) => {
|
|
143
|
+
if (seen.has(id)) return;
|
|
144
|
+
seen.add(id);
|
|
145
|
+
const c = comments.get(id);
|
|
146
|
+
const n = numbers.get(id);
|
|
147
|
+
if (!c || n === void 0) return;
|
|
148
|
+
out.push(`<li id="cm-${n}" class="${c.done ? "comment resolved" : "comment"}">`);
|
|
149
|
+
const meta = [[c.author !== void 0 ? c.author + (c.authorId ? ` <${c.authorId}>` : "") : c.authorId, c.date].filter((s) => s !== void 0 && s.length > 0).join(", "), c.done ? "Resolved" : void 0].filter((s) => s).join(" — ");
|
|
150
|
+
if (meta) out.push(`<p class="comment-meta">${textHtml(meta)}</p>`);
|
|
151
|
+
for (const el of c.content) emitBlock(out, el, ctx);
|
|
152
|
+
out.push(`<a href="#cmref-${n}">\u21a9</a>`);
|
|
153
|
+
const kids = childrenOf.get(id);
|
|
154
|
+
if (kids && kids.length > 0) {
|
|
155
|
+
out.push("<ol class=\"comment-replies\">");
|
|
156
|
+
for (const k of kids) emitOne(k);
|
|
157
|
+
out.push("</ol>");
|
|
158
|
+
}
|
|
159
|
+
out.push("</li>");
|
|
160
|
+
};
|
|
161
|
+
out.push("<section class=\"comments\"><hr/><h2>Comments</h2><ol>");
|
|
162
|
+
for (const id of roots) emitOne(id);
|
|
163
|
+
out.push("</ol></section>");
|
|
164
|
+
}
|
|
121
165
|
function emitBlock(out, el, ctx) {
|
|
122
166
|
if (el.kind === "paragraph") emitParagraph(out, el.paragraph, ctx);
|
|
123
167
|
else if (el.kind === "table") emitTable(out, el.table, ctx);
|
|
@@ -272,6 +316,11 @@ function runHtml(run, p, ctx) {
|
|
|
272
316
|
const prefix = isFoot ? "fn" : "en";
|
|
273
317
|
return `<sup><a href="#${prefix}-${n}" id="${prefix}ref-${n}">${n}</a></sup>`;
|
|
274
318
|
}
|
|
319
|
+
if (run.commentRef !== void 0) {
|
|
320
|
+
const n = ctx.notes.comments.get(run.commentRef);
|
|
321
|
+
if (n === void 0) return "";
|
|
322
|
+
return `<sup class="comment-ref"><a href="#cm-${n}" id="cmref-${n}">[${n}]</a></sup>`;
|
|
323
|
+
}
|
|
275
324
|
if (run.noteNumber) return "";
|
|
276
325
|
if (run.math !== void 0) {
|
|
277
326
|
ctx.losses.push({
|
|
@@ -293,6 +342,7 @@ function runHtml(run, p, ctx) {
|
|
|
293
342
|
let html = `<span${resolved.rtl ? " dir=\"rtl\"" : ""}${style ? ` style="${style}"` : ""}>${textHtml(run.text)}</span>`;
|
|
294
343
|
if (resolved.verticalAlign === "superscript") html = `<sup>${html}</sup>`;
|
|
295
344
|
else if (resolved.verticalAlign === "subscript") html = `<sub>${html}</sub>`;
|
|
345
|
+
if (run.commentRangeRefs && run.commentRangeRefs.length > 0) html = `<span class="comment-range">${html}</span>`;
|
|
296
346
|
if (run.href === void 0 && run.anchor !== void 0) return `<a href="#bm-${encodeURIComponent(run.anchor)}">${html}</a>`;
|
|
297
347
|
if (run.href !== void 0) {
|
|
298
348
|
const safe = sanitizeHref(run.href);
|
|
@@ -446,6 +496,7 @@ function emitCell(out, cell, table, ctx, pos) {
|
|
|
446
496
|
if (!cell.properties.shading) css.push("background-color:#fff");
|
|
447
497
|
}
|
|
448
498
|
out.push(`<${tag}${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""} style="${css.join(";")}">`);
|
|
499
|
+
if (cell.properties.dropdown) out.push(cellDropdownSvg());
|
|
449
500
|
if (cell.properties.icon) out.push(cellIconSvg(cell.properties.icon));
|
|
450
501
|
if (cell.properties.sparkline) out.push(cellSparklineSvg(cell.properties.sparkline));
|
|
451
502
|
for (const child of cell.content) emitBlock(out, child, ctx);
|
|
@@ -464,6 +515,9 @@ function cellSparklineSvg(sp) {
|
|
|
464
515
|
}
|
|
465
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>`;
|
|
466
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
|
+
}
|
|
467
521
|
var ICON_EMPTY_HEX = "BFBFBF";
|
|
468
522
|
function cellIconSvg(icon) {
|
|
469
523
|
const fill = `#${icon.colorHex}`;
|
|
@@ -17,6 +17,7 @@ export interface TextToken {
|
|
|
17
17
|
readonly footnoteRef?: string;
|
|
18
18
|
readonly anchor?: string;
|
|
19
19
|
readonly listMarker?: true;
|
|
20
|
+
readonly highlight?: true;
|
|
20
21
|
readonly resolvedRun: ResolvedRunProperties;
|
|
21
22
|
readonly font: FontResource;
|
|
22
23
|
readonly fontSizePt: number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BodyElement, Chart, DocumentInfo, Numbering, Section, SectionProperties, StyleSheet } from '../core/document-model/index.js';
|
|
1
|
+
import { BodyElement, Chart, Comment, DocumentInfo, Numbering, Section, SectionProperties, StyleSheet } from '../core/document-model/index.js';
|
|
2
2
|
import { FontRegistry } from '../core/font/index.js';
|
|
3
3
|
import { FamilyKey } from '../core/fonts/index.js';
|
|
4
4
|
import { Hyphenator } from '../core/hyphenation/index.js';
|
|
@@ -28,6 +28,8 @@ export interface StyledRenderOptions {
|
|
|
28
28
|
readonly headersFooters?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
|
|
29
29
|
readonly footnotes?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
|
|
30
30
|
readonly endnotes?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
|
|
31
|
+
readonly comments?: ReadonlyMap<string, Comment>;
|
|
32
|
+
readonly commentAnnotations?: boolean;
|
|
31
33
|
readonly resources?: ResourceStore;
|
|
32
34
|
readonly charts?: ReadonlyMap<string, Chart>;
|
|
33
35
|
readonly pageWidth?: number;
|
|
@@ -53,12 +55,17 @@ export interface BookmarkPosition {
|
|
|
53
55
|
readonly pageIdx: number;
|
|
54
56
|
readonly yTopPt: number;
|
|
55
57
|
}
|
|
58
|
+
export interface CommentNote {
|
|
59
|
+
readonly author?: string;
|
|
60
|
+
readonly contents: string;
|
|
61
|
+
}
|
|
56
62
|
export interface PdfLayoutAux {
|
|
57
63
|
readonly structBuilder: StructTreeBuilder | undefined;
|
|
58
64
|
readonly sectionCtxs: ReadonlyArray<SectionRenderCtx>;
|
|
59
65
|
readonly pdfaProfile: PdfAProfile | undefined;
|
|
60
66
|
readonly tagged: boolean;
|
|
61
67
|
readonly bookmarks: ReadonlyMap<string, BookmarkPosition>;
|
|
68
|
+
readonly commentNotes?: ReadonlyMap<string, CommentNote>;
|
|
62
69
|
}
|
|
63
70
|
export interface LaidOutPdfDocument extends LaidOutDocument {
|
|
64
71
|
readonly pdf: PdfLayoutAux;
|