reamkit 1.15.3 → 1.16.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.d.ts +5 -0
- package/dist/esm/core/converter/ream.d.ts +6 -0
- package/dist/esm/core/converter/ream.js +34 -8
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +187 -1
- package/dist/esm/core/drawingml/chart-geometry.d.ts +28 -0
- package/dist/esm/core/drawingml/chart-geometry.js +371 -88
- package/dist/esm/core/drawingml/chart-parser.js +262 -11
- package/dist/esm/core/drawingml/colors.d.ts +6 -6
- package/dist/esm/core/drawingml/colors.js +58 -18
- package/dist/esm/core/drawingml/preset-geometry.js +7 -6
- package/dist/esm/core/drawingml/sparkline-geometry.js +6 -4
- package/dist/esm/core/drawingml/theme-parser.d.ts +41 -0
- package/dist/esm/core/drawingml/theme-parser.js +74 -1
- package/dist/esm/core/font/ttf-subset.js +26 -1
- package/dist/esm/core/images.js +172 -43
- package/dist/esm/core/indexed-colors.d.ts +11 -0
- package/dist/esm/core/indexed-colors.js +81 -0
- package/dist/esm/core/ir/sheet.d.ts +67 -3
- package/dist/esm/{excel → core}/number-format.d.ts +37 -0
- package/dist/esm/core/number-format.js +800 -0
- package/dist/esm/core/opc/alternate-content.d.ts +13 -0
- package/dist/esm/core/opc/alternate-content.js +72 -0
- package/dist/esm/core/opc/core-properties.js +1 -0
- package/dist/esm/core/opc/package.d.ts +14 -1
- package/dist/esm/core/opc/package.js +43 -4
- package/dist/esm/core/opc/relationships.js +1 -0
- package/dist/esm/core/opc/xml-entities.d.ts +12 -0
- package/dist/esm/core/opc/xml-entities.js +91 -0
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +137 -0
- package/dist/esm/core/vector.d.ts +13 -0
- package/dist/esm/excel/activex-parser.d.ts +9 -0
- package/dist/esm/excel/activex-parser.js +16 -1
- package/dist/esm/excel/column-bands.d.ts +26 -1
- package/dist/esm/excel/column-bands.js +156 -20
- package/dist/esm/excel/comments-parser.js +5 -3
- package/dist/esm/excel/conditional-format.d.ts +25 -2
- package/dist/esm/excel/conditional-format.js +63 -19
- package/dist/esm/excel/escaped-text.d.ts +9 -0
- package/dist/esm/excel/escaped-text.js +20 -0
- package/dist/esm/excel/form-control-parser.js +1 -0
- package/dist/esm/excel/formula/dates.js +1 -1
- package/dist/esm/excel/header-footer.d.ts +9 -1
- package/dist/esm/excel/header-footer.js +162 -14
- package/dist/esm/excel/index.d.ts +1 -1
- package/dist/esm/excel/index.js +2 -2
- package/dist/esm/excel/pivot-table-parser.js +1 -0
- package/dist/esm/excel/print-model.d.ts +75 -0
- package/dist/esm/excel/print-model.js +902 -77
- package/dist/esm/excel/printer-settings.d.ts +16 -0
- package/dist/esm/excel/printer-settings.js +36 -0
- package/dist/esm/excel/rich-value.d.ts +13 -0
- package/dist/esm/excel/rich-value.js +120 -0
- package/dist/esm/excel/shared-strings-parser.js +11 -7
- package/dist/esm/excel/sheet-drawing.d.ts +11 -0
- package/dist/esm/excel/sheet-drawing.js +26 -3
- package/dist/esm/excel/sheet-shape-parser.d.ts +53 -1
- package/dist/esm/excel/sheet-shape-parser.js +461 -32
- package/dist/esm/excel/sheet-to-flow.d.ts +23 -2
- package/dist/esm/excel/sheet-to-flow.js +551 -36
- package/dist/esm/excel/slicer-parser.js +1 -0
- package/dist/esm/excel/styles-parser.d.ts +30 -2
- package/dist/esm/excel/styles-parser.js +177 -40
- package/dist/esm/excel/table-parser.js +1 -0
- package/dist/esm/excel/vml-drawing.d.ts +68 -0
- package/dist/esm/excel/vml-drawing.js +158 -0
- package/dist/esm/excel/workbook-parser.d.ts +5 -0
- package/dist/esm/excel/workbook-parser.js +5 -1
- package/dist/esm/excel/worksheet-parser.d.ts +2 -1
- package/dist/esm/excel/worksheet-parser.js +149 -28
- package/dist/esm/excel/xlsx-reader.js +271 -33
- package/dist/esm/excel/xlsx-to-pdf.js +12 -1
- package/dist/esm/html/html-writer.js +11 -2
- package/dist/esm/layout/page-doc.d.ts +26 -0
- package/dist/esm/layout/styled-layout.d.ts +2 -0
- package/dist/esm/layout/styled-layout.js +493 -64
- package/dist/esm/pdf/cid-font.js +20 -9
- package/dist/esm/pdf/objects.d.ts +13 -0
- package/dist/esm/pdf/objects.js +18 -1
- package/dist/esm/pdf/styled-page-emitter.js +164 -32
- package/dist/esm/pdf/text-page-renderer.js +1 -0
- package/dist/esm/pdf/vector-graphics.d.ts +3 -1
- package/dist/esm/pdf/vector-graphics.js +19 -2
- package/dist/esm/pptx/pptx-reader.js +1 -0
- package/dist/esm/pptx/slide-parser.js +12 -1
- package/dist/esm/svg/svg-writer.js +23 -4
- package/dist/esm/word/document-parser.js +1 -0
- package/dist/esm/word/drawing-parser.d.ts +34 -1
- package/dist/esm/word/drawing-parser.js +52 -2
- package/dist/esm/word/numbering-parser.js +1 -0
- package/dist/esm/word/settings-parser.js +1 -0
- package/dist/esm/word/styles-parser.js +1 -0
- package/package.json +7 -3
- package/dist/esm/excel/number-format.js +0 -476
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { ResourceStore } from "../core/ir/resources.js";
|
|
2
2
|
import { FEATURES } from "../core/ir/features.js";
|
|
3
|
+
import { detectImageFormat } from "../core/images.js";
|
|
3
4
|
import { bytesInclude, bytesIncludePartName } from "../core/bytes.js";
|
|
4
5
|
import { DEFAULT_THEME_PALETTE, makeColorResolver } from "../core/drawingml/colors.js";
|
|
5
6
|
import { parseChart, withChartColorStyle } from "../core/drawingml/chart-parser.js";
|
|
6
|
-
import { parseTheme } from "../core/drawingml/theme-parser.js";
|
|
7
|
+
import { parseTheme, parseThemeEffectStyles, parseThemeFillStyles, parseThemeLineWidths } from "../core/drawingml/theme-parser.js";
|
|
7
8
|
import { isOoxmlRel } from "../core/opc/relationship-types.js";
|
|
8
9
|
import { OpcPackage } from "../core/opc/package.js";
|
|
9
10
|
import { parseCoreProperties } from "../core/opc/core-properties.js";
|
|
@@ -11,8 +12,8 @@ import "../core/opc/index.js";
|
|
|
11
12
|
import { parseAreaRef } from "./defined-name-ref.js";
|
|
12
13
|
import { parseSharedStrings } from "./shared-strings-parser.js";
|
|
13
14
|
import { parseWorkbook } from "./workbook-parser.js";
|
|
14
|
-
import { parseWorksheet } from "./worksheet-parser.js";
|
|
15
15
|
import { EMPTY_XLSX_STYLES, parseXlsxStyles } from "./styles-parser.js";
|
|
16
|
+
import { parseWorksheet } from "./worksheet-parser.js";
|
|
16
17
|
import "./index.js";
|
|
17
18
|
import { resolveCellText } from "./print-model.js";
|
|
18
19
|
import { parseSheetDrawing } from "./sheet-drawing.js";
|
|
@@ -21,19 +22,26 @@ import { parsePivotTablePart } from "./pivot-table-parser.js";
|
|
|
21
22
|
import { parseSlicerCachePart, parseSlicerPart } from "./slicer-parser.js";
|
|
22
23
|
import { parseLegacyComments, parsePersons, parseThreadedComments } from "./comments-parser.js";
|
|
23
24
|
import { parseFormControlProps } from "./form-control-parser.js";
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
25
|
+
import { parseVmlDrawing } from "./vml-drawing.js";
|
|
26
|
+
import { parsePrinterSettings } from "./printer-settings.js";
|
|
27
|
+
import { parseRichValueText } from "./rich-value.js";
|
|
28
|
+
import { activeXBinRelId, activeXType, activeXTypeFromPart, parseActiveX, parseActiveXBin } from "./activex-parser.js";
|
|
29
|
+
import { parseDiagramFrames, parseDiagramShapes, parseSheetShapes } from "./sheet-shape-parser.js";
|
|
26
30
|
import { projectSheetDoc } from "./sheet-to-flow.js";
|
|
27
31
|
//#region src/excel/xlsx-reader.ts
|
|
28
32
|
var WORKBOOK_PART = "xl/workbook.xml";
|
|
29
33
|
var SHARED_STRINGS_PART = "xl/sharedStrings.xml";
|
|
30
34
|
var STYLES_PART = "xl/styles.xml";
|
|
31
35
|
var CORE_PROPS_PART = "docProps/core.xml";
|
|
36
|
+
var METADATA_PART = "xl/metadata.xml";
|
|
37
|
+
var RICH_VALUE_STRUCTURE_PART = "xl/richData/rdrichvaluestructure.xml";
|
|
38
|
+
var RICH_VALUE_PART = "xl/richData/rdrichvalue.xml";
|
|
32
39
|
var SLICER_REL_TAIL = "/slicer";
|
|
33
40
|
var SLICER_CACHE_REL_TAIL = "/slicerCache";
|
|
34
41
|
var THREADED_COMMENTS_REL_TAIL = "/threadedComment";
|
|
35
42
|
var PERSON_REL_TAIL = "/person";
|
|
36
43
|
var MAX_SLICER_ITEMS = 256;
|
|
44
|
+
var ACTIVEX_PART = /^xl\/activeX\//i;
|
|
37
45
|
/**
|
|
38
46
|
* Read a `.xlsx` and project it to a {@link FlowDoc} in one step:
|
|
39
47
|
* {@link readXlsxToSheetDoc} then {@link projectSheetDoc}.
|
|
@@ -79,9 +87,11 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
79
87
|
texts: [],
|
|
80
88
|
runs: []
|
|
81
89
|
};
|
|
82
|
-
const stylesData = pkg.getPart(STYLES_PART);
|
|
83
|
-
const styles = stylesData ? parseXlsxStyles(stylesData) : EMPTY_XLSX_STYLES;
|
|
84
90
|
const workbookRels = pkg.getPartRelationships(WORKBOOK_PART);
|
|
91
|
+
const themePalette = buildThemePalette(pkg, workbookRels);
|
|
92
|
+
const richValueText = parseRichValueText(pkg.getPart(METADATA_PART), pkg.getPart(RICH_VALUE_STRUCTURE_PART), pkg.getPart(RICH_VALUE_PART));
|
|
93
|
+
const stylesData = pkg.getPart(STYLES_PART);
|
|
94
|
+
const styles = stylesData ? parseXlsxStyles(stylesData, themePalette) : EMPTY_XLSX_STYLES;
|
|
85
95
|
const persons = /* @__PURE__ */ new Map();
|
|
86
96
|
for (const rel of workbookRels) {
|
|
87
97
|
if (!rel.type.endsWith(PERSON_REL_TAIL)) continue;
|
|
@@ -92,7 +102,12 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
92
102
|
const resources = new ResourceStore();
|
|
93
103
|
const palette = buildThemePalette(pkg, workbookRels);
|
|
94
104
|
const resolveColor = makeColorResolver(palette);
|
|
105
|
+
const themeLineWidths = buildThemeLineWidths(pkg, workbookRels);
|
|
106
|
+
const themeFillStyles = buildThemeFillStyles(pkg, workbookRels);
|
|
107
|
+
const themeEffectStyles = buildThemeEffectStyles(pkg, workbookRels);
|
|
95
108
|
const sheetsOut = [];
|
|
109
|
+
let embeddedObjects = 0;
|
|
110
|
+
let metafilePictures = 0;
|
|
96
111
|
const tableIndex = /* @__PURE__ */ new Map();
|
|
97
112
|
const pendingSlicers = [];
|
|
98
113
|
for (let sheetIdx = 0; sheetIdx < sheets.length; sheetIdx++) {
|
|
@@ -101,7 +116,9 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
101
116
|
if (!sheetRel) continue;
|
|
102
117
|
const resolved = pkg.resolveRelatedPart(WORKBOOK_PART, sheetRel);
|
|
103
118
|
if (!resolved) continue;
|
|
104
|
-
|
|
119
|
+
let worksheet = parseWorksheet(resolved.data, themePalette);
|
|
120
|
+
worksheet = withPrinterPageSetup(worksheet, pkg, resolved.path, pkg.getPartRelationships(resolved.path));
|
|
121
|
+
worksheet = withRichValues(worksheet, richValueText);
|
|
105
122
|
const charts = [];
|
|
106
123
|
const images = [];
|
|
107
124
|
let shapes;
|
|
@@ -120,22 +137,39 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
120
137
|
charts.push({
|
|
121
138
|
chartPartPath: ref.chartPartPath,
|
|
122
139
|
widthPt: ref.widthPt,
|
|
123
|
-
heightPt: ref.heightPt
|
|
140
|
+
heightPt: ref.heightPt,
|
|
141
|
+
xPt: ref.xPt,
|
|
142
|
+
yPt: ref.yPt
|
|
124
143
|
});
|
|
125
144
|
}
|
|
126
145
|
for (const pic of pictures) {
|
|
127
146
|
const bytes = pkg.getPart(pic.imagePartPath);
|
|
128
147
|
if (!bytes) continue;
|
|
148
|
+
if (detectImageFormat(bytes) === null) metafilePictures++;
|
|
129
149
|
images.push({
|
|
130
150
|
resourceId: resources.put(bytes),
|
|
131
151
|
widthPt: pic.widthPt,
|
|
132
|
-
heightPt: pic.heightPt
|
|
152
|
+
heightPt: pic.heightPt,
|
|
153
|
+
xPt: pic.xPt,
|
|
154
|
+
yPt: pic.yPt
|
|
133
155
|
});
|
|
134
156
|
}
|
|
135
157
|
if (bytesInclude(drawing.data, ":sp>") || bytesInclude(drawing.data, ":sp ")) {
|
|
136
|
-
const parsed = parseSheetShapes(drawing.data, worksheet, resolveColor);
|
|
158
|
+
const parsed = parseSheetShapes(drawing.data, worksheet, resolveColor, themeLineWidths, themeFillStyles, themeEffectStyles);
|
|
137
159
|
if (parsed.length > 0) shapes = parsed;
|
|
138
160
|
}
|
|
161
|
+
const frames = parseDiagramFrames(drawing.data, worksheet);
|
|
162
|
+
if (frames.length > 0) {
|
|
163
|
+
const dsp = pkg.getPartRelationships(drawing.path).filter((r) => r.type.endsWith("/diagramDrawing"));
|
|
164
|
+
const fromDiagrams = [];
|
|
165
|
+
frames.forEach((frame, i) => {
|
|
166
|
+
const rel = dsp[i];
|
|
167
|
+
const part = rel ? pkg.resolveRelatedPart(drawing.path, rel) : void 0;
|
|
168
|
+
if (!part) return;
|
|
169
|
+
fromDiagrams.push(...parseDiagramShapes(part.data, frame, resolveColor, themeLineWidths, themeFillStyles, themeEffectStyles));
|
|
170
|
+
});
|
|
171
|
+
if (fromDiagrams.length > 0) shapes = [...shapes ?? [], ...fromDiagrams];
|
|
172
|
+
}
|
|
139
173
|
}
|
|
140
174
|
}
|
|
141
175
|
let tables;
|
|
@@ -219,44 +253,61 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
219
253
|
}
|
|
220
254
|
if (resolvedComments.length > 0) comments = resolvedComments;
|
|
221
255
|
}
|
|
256
|
+
const wsRels = pkg.getPartRelationships(resolved.path);
|
|
257
|
+
const resolvedAx = [];
|
|
258
|
+
const activeXState = (part) => {
|
|
259
|
+
const props = parseActiveX(part.data);
|
|
260
|
+
const binRelId = activeXBinRelId(part.data);
|
|
261
|
+
const binRel = binRelId ? pkg.getPartRelationships(part.path).find((r) => r.id === binRelId) : void 0;
|
|
262
|
+
const binPart = binRel ? pkg.resolveRelatedPart(part.path, binRel) : void 0;
|
|
263
|
+
return {
|
|
264
|
+
type: "control",
|
|
265
|
+
...binPart ? parseActiveXBin(binPart.data) : {},
|
|
266
|
+
...props
|
|
267
|
+
};
|
|
268
|
+
};
|
|
269
|
+
const legacyVml = readLegacyVml(pkg, resolved.path, wsRels, worksheet);
|
|
222
270
|
let formControls;
|
|
223
271
|
if (worksheet.formControls && worksheet.formControls.length > 0) {
|
|
224
|
-
const wsRels = pkg.getPartRelationships(resolved.path);
|
|
225
272
|
const resolvedControls = [];
|
|
226
273
|
for (const fc of worksheet.formControls) {
|
|
274
|
+
if (fc.print === false) continue;
|
|
275
|
+
if (fc.shapeId !== void 0 && legacyVml.nonPrinting.has(fc.shapeId)) continue;
|
|
227
276
|
const rel = wsRels.find((r) => r.id === fc.relId);
|
|
228
277
|
const part = rel ? pkg.resolveRelatedPart(resolved.path, rel) : void 0;
|
|
278
|
+
const box = fc.shapeId !== void 0 ? legacyVml.boxes.get(fc.shapeId) : void 0;
|
|
279
|
+
if (part && ACTIVEX_PART.test(part.path)) {
|
|
280
|
+
resolvedAx.push({
|
|
281
|
+
...activeXState(part),
|
|
282
|
+
type: activeXTypeFromPart(part.data),
|
|
283
|
+
...fc.name ? { name: fc.name } : {},
|
|
284
|
+
...box ? { box } : {}
|
|
285
|
+
});
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
229
288
|
const props = part ? parseFormControlProps(part.data) : {};
|
|
230
289
|
resolvedControls.push({
|
|
231
290
|
...fc.name ? { name: fc.name } : {},
|
|
232
|
-
...props
|
|
291
|
+
...props,
|
|
292
|
+
...box ? { box } : {}
|
|
233
293
|
});
|
|
234
294
|
}
|
|
235
295
|
if (resolvedControls.length > 0) formControls = resolvedControls;
|
|
236
296
|
}
|
|
237
|
-
|
|
238
|
-
if (worksheet.oleObjects && worksheet.oleObjects.length > 0) {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const rel = wsRels.find((r) => r.id === ole.relId);
|
|
243
|
-
const part = rel ? pkg.resolveRelatedPart(resolved.path, rel) : void 0;
|
|
244
|
-
const props = part ? parseActiveX(part.data) : {};
|
|
245
|
-
let binProps = {};
|
|
246
|
-
if (part) {
|
|
247
|
-
const binRelId = activeXBinRelId(part.data);
|
|
248
|
-
const binRel = binRelId ? pkg.getPartRelationships(part.path).find((r) => r.id === binRelId) : void 0;
|
|
249
|
-
const binPart = binRel ? pkg.resolveRelatedPart(part.path, binRel) : void 0;
|
|
250
|
-
if (binPart) binProps = parseActiveXBin(binPart.data);
|
|
251
|
-
}
|
|
252
|
-
resolvedAx.push({
|
|
253
|
-
type: activeXType(ole.progId),
|
|
254
|
-
...binProps,
|
|
255
|
-
...props
|
|
256
|
-
});
|
|
297
|
+
if (legacyVml.controls.length > 0) formControls = [...formControls ?? [], ...legacyVml.controls];
|
|
298
|
+
if (worksheet.oleObjects && worksheet.oleObjects.length > 0) for (const ole of worksheet.oleObjects) {
|
|
299
|
+
if (!/^Forms\./i.test(ole.progId ?? "")) {
|
|
300
|
+
embeddedObjects++;
|
|
301
|
+
continue;
|
|
257
302
|
}
|
|
258
|
-
|
|
303
|
+
const rel = wsRels.find((r) => r.id === ole.relId);
|
|
304
|
+
const part = rel ? pkg.resolveRelatedPart(resolved.path, rel) : void 0;
|
|
305
|
+
resolvedAx.push({
|
|
306
|
+
...part ? activeXState(part) : {},
|
|
307
|
+
type: activeXType(ole.progId)
|
|
308
|
+
});
|
|
259
309
|
}
|
|
310
|
+
const activeXControls = resolvedAx.length > 0 ? resolvedAx : void 0;
|
|
260
311
|
const grid = tables || pivotTables ? {
|
|
261
312
|
...worksheet,
|
|
262
313
|
...tables ? { tables } : {},
|
|
@@ -264,6 +315,7 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
264
315
|
} : worksheet;
|
|
265
316
|
sheetsOut.push({
|
|
266
317
|
name: sheet.name,
|
|
318
|
+
...sheet.hidden ? { hidden: true } : {},
|
|
267
319
|
grid,
|
|
268
320
|
...charts.length > 0 ? { charts } : {},
|
|
269
321
|
...images.length > 0 ? { images } : {},
|
|
@@ -292,6 +344,10 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
292
344
|
};
|
|
293
345
|
}
|
|
294
346
|
}
|
|
347
|
+
for (const [path, chart] of chartData) {
|
|
348
|
+
const resolved = withWorkbookData(chart, sheetsOut, styles, sharedStrings, date1904);
|
|
349
|
+
if (resolved !== chart) chartData.set(path, resolved);
|
|
350
|
+
}
|
|
295
351
|
const coreData = pkg.getPart(CORE_PROPS_PART);
|
|
296
352
|
const info = infoFromCore(coreData ? parseCoreProperties(coreData) : void 0);
|
|
297
353
|
return {
|
|
@@ -304,9 +360,167 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
304
360
|
date1904,
|
|
305
361
|
...chartData.size > 0 ? { chartData } : {},
|
|
306
362
|
resources,
|
|
363
|
+
themePalette,
|
|
364
|
+
...embeddedObjects > 0 ? { embeddedObjects } : {},
|
|
365
|
+
...metafilePictures > 0 ? { metafilePictures } : {},
|
|
307
366
|
...info ? { info } : {}
|
|
308
367
|
};
|
|
309
368
|
}
|
|
369
|
+
/**
|
|
370
|
+
* Fill in the paper size and orientation a `<pageSetup>` leaves to its
|
|
371
|
+
* `printerSettings` part.
|
|
372
|
+
*
|
|
373
|
+
* `<pageSetup>` naming no `paperSize` does not mean "the default": Excel
|
|
374
|
+
* records the print dialog's choice in the DEVMODE of the related part, and
|
|
375
|
+
* LibreOffice reads it. simple-monthly-budget.xlsx and 45540_classic_Header.xlsx
|
|
376
|
+
* both print on Letter that way while we assumed A4 — the most visible
|
|
377
|
+
* difference on every page of either. What the sheet states itself always wins.
|
|
378
|
+
*/
|
|
379
|
+
/**
|
|
380
|
+
* Replace the legacy stand-in in every cell that points at a rich value we can
|
|
381
|
+
* read. Spill.xlsx caches `#VALUE!` in three cells whose rich value says
|
|
382
|
+
* `#SPILL!` — the error Excel shows and the one that explains the sheet, since
|
|
383
|
+
* the file also records which cell three rows down does the blocking.
|
|
384
|
+
*
|
|
385
|
+
* @param worksheet The parsed worksheet.
|
|
386
|
+
* @param richValueText `vm` index → text, from {@link parseRichValueText}.
|
|
387
|
+
* @returns The worksheet, unchanged when nothing resolved (byte-identical path).
|
|
388
|
+
*/
|
|
389
|
+
/**
|
|
390
|
+
* A chart with its uncached references resolved against the workbook.
|
|
391
|
+
*
|
|
392
|
+
* Only what is missing is filled in: a series that cached its values keeps
|
|
393
|
+
* them, and a reference naming a sheet this workbook does not have resolves to
|
|
394
|
+
* nothing rather than to zeros.
|
|
395
|
+
*
|
|
396
|
+
* @param chart The parsed chart.
|
|
397
|
+
* @param sheets Every sheet in the workbook, by tab order.
|
|
398
|
+
* @param styles The style table (for a referenced cell's number format).
|
|
399
|
+
* @param sharedStrings The shared-string table.
|
|
400
|
+
* @param date1904 The workbook's date system.
|
|
401
|
+
* @returns The chart, unchanged when nothing needed resolving.
|
|
402
|
+
*/
|
|
403
|
+
function withWorkbookData(chart, sheets, styles, sharedStrings, date1904) {
|
|
404
|
+
const cellsOf = (ref) => {
|
|
405
|
+
if (ref === void 0) return void 0;
|
|
406
|
+
const area = resolveChartRef(ref, sheets);
|
|
407
|
+
if (!area) return void 0;
|
|
408
|
+
const out = [];
|
|
409
|
+
for (let row = area.startRow; row <= area.endRow; row++) for (let col = area.startColumn; col <= area.endColumn; col++) {
|
|
410
|
+
const cell = area.grid.cells.find((c) => c.row === row && c.column === col);
|
|
411
|
+
out.push(cell ? resolveCellText(cell, sharedStrings, styles, date1904) : "");
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
};
|
|
415
|
+
let changed = false;
|
|
416
|
+
const series = chart.series.map((s) => {
|
|
417
|
+
if (s.values.length > 0 && s.name !== void 0) return s;
|
|
418
|
+
const values = s.values.length > 0 ? s.values : cellsOf(s.valuesRef)?.map((t) => Number(t) || 0);
|
|
419
|
+
const name = s.name ?? cellsOf(s.nameRef)?.find((t) => t.length > 0);
|
|
420
|
+
if (!values && name === void 0) return s;
|
|
421
|
+
changed = true;
|
|
422
|
+
return {
|
|
423
|
+
...s,
|
|
424
|
+
...values ? { values } : {},
|
|
425
|
+
...name !== void 0 ? { name } : {}
|
|
426
|
+
};
|
|
427
|
+
});
|
|
428
|
+
const categories = chart.categories.length > 0 ? chart.categories : cellsOf(chart.categoriesRef);
|
|
429
|
+
if (categories && categories.length > 0 && chart.categories.length === 0) changed = true;
|
|
430
|
+
if (!changed) return chart;
|
|
431
|
+
return {
|
|
432
|
+
...chart,
|
|
433
|
+
series,
|
|
434
|
+
...categories && categories.length > 0 ? { categories } : {}
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
/** `Sheet!A1:B2` → the sheet's grid and the 0-based rectangle it names. */
|
|
438
|
+
function resolveChartRef(ref, sheets) {
|
|
439
|
+
const bang = ref.lastIndexOf("!");
|
|
440
|
+
if (bang < 0) return void 0;
|
|
441
|
+
const name = ref.slice(0, bang).replace(/^'|'$/g, "").replace(/''/g, "'");
|
|
442
|
+
const sheet = sheets.find((s) => s.name === name);
|
|
443
|
+
if (!sheet) return void 0;
|
|
444
|
+
const area = parseAreaRef(ref.slice(bang + 1).replace(/\$/g, ""));
|
|
445
|
+
return area ? {
|
|
446
|
+
grid: sheet.grid,
|
|
447
|
+
...area
|
|
448
|
+
} : void 0;
|
|
449
|
+
}
|
|
450
|
+
function withRichValues(worksheet, richValueText) {
|
|
451
|
+
if (richValueText.size === 0) return worksheet;
|
|
452
|
+
const resolves = (cell) => cell.valueMetadataIndex !== void 0 ? richValueText.get(cell.valueMetadataIndex) : void 0;
|
|
453
|
+
if (!worksheet.cells.some((cell) => resolves(cell) !== void 0)) return worksheet;
|
|
454
|
+
const cells = worksheet.cells.map((cell) => {
|
|
455
|
+
const text = resolves(cell);
|
|
456
|
+
return text === void 0 ? cell : {
|
|
457
|
+
...cell,
|
|
458
|
+
rawValue: text
|
|
459
|
+
};
|
|
460
|
+
});
|
|
461
|
+
return {
|
|
462
|
+
...worksheet,
|
|
463
|
+
cells
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
function withPrinterPageSetup(worksheet, pkg, sheetPath, wsRels) {
|
|
467
|
+
const setup = worksheet.pageSetup;
|
|
468
|
+
const relId = setup?.printerSettingsRelId;
|
|
469
|
+
if (!setup || relId === void 0) return worksheet;
|
|
470
|
+
const { printerSettingsRelId: _resolved, ...rest } = setup;
|
|
471
|
+
const rel = wsRels.find((r) => r.id === relId);
|
|
472
|
+
const part = rel ? pkg.resolveRelatedPart(sheetPath, rel) : void 0;
|
|
473
|
+
const printer = part ? parsePrinterSettings(part.data) : {};
|
|
474
|
+
return {
|
|
475
|
+
...worksheet,
|
|
476
|
+
pageSetup: {
|
|
477
|
+
...rest,
|
|
478
|
+
...rest.paperSize === void 0 && printer.paperSize !== void 0 ? { paperSize: printer.paperSize } : {},
|
|
479
|
+
...rest.orientation === void 0 && printer.orientation !== void 0 ? { orientation: printer.orientation } : {}
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* The sheet's form controls that live only in its legacy VML drawing (§18.3.1.36).
|
|
485
|
+
*
|
|
486
|
+
* Shapes that back an ActiveX control are skipped — their `o:spid` is the
|
|
487
|
+
* `shapeId` of a `<control>` element, which the caller has already resolved
|
|
488
|
+
* through the activeX part. What is left is the Forms-toolbar kind, whose
|
|
489
|
+
* caption and checked state exist nowhere else in the package.
|
|
490
|
+
*/
|
|
491
|
+
function readLegacyVml(pkg, sheetPath, wsRels, worksheet) {
|
|
492
|
+
const empty = {
|
|
493
|
+
controls: [],
|
|
494
|
+
boxes: /* @__PURE__ */ new Map(),
|
|
495
|
+
nonPrinting: /* @__PURE__ */ new Set()
|
|
496
|
+
};
|
|
497
|
+
const relId = worksheet.legacyDrawingRelId;
|
|
498
|
+
if (relId === void 0) return empty;
|
|
499
|
+
const rel = wsRels.find((r) => r.id === relId);
|
|
500
|
+
const part = rel ? pkg.resolveRelatedPart(sheetPath, rel) : void 0;
|
|
501
|
+
if (!part) return empty;
|
|
502
|
+
const activeXShapeIds = new Set((worksheet.formControls ?? []).map((fc) => fc.shapeId).filter((id) => id !== void 0));
|
|
503
|
+
const drawing = parseVmlDrawing(part.data);
|
|
504
|
+
const out = [];
|
|
505
|
+
for (const shape of drawing.controls) {
|
|
506
|
+
if (shape.shapeId !== void 0 && activeXShapeIds.has(shape.shapeId)) continue;
|
|
507
|
+
out.push({
|
|
508
|
+
objectType: shape.objectType,
|
|
509
|
+
...shape.caption ? {
|
|
510
|
+
name: shape.caption,
|
|
511
|
+
caption: shape.caption
|
|
512
|
+
} : {},
|
|
513
|
+
...shape.checked ? { checked: true } : {},
|
|
514
|
+
...shape.box ? { box: shape.box } : {},
|
|
515
|
+
...shape.fontSizePt !== void 0 ? { fontSizePt: shape.fontSizePt } : {}
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
return {
|
|
519
|
+
controls: out,
|
|
520
|
+
boxes: drawing.boxes,
|
|
521
|
+
nonPrinting: drawing.nonPrinting
|
|
522
|
+
};
|
|
523
|
+
}
|
|
310
524
|
function buildThemePalette(pkg, workbookRels) {
|
|
311
525
|
const palette = new Map(DEFAULT_THEME_PALETTE);
|
|
312
526
|
for (const rel of workbookRels) {
|
|
@@ -318,6 +532,30 @@ function buildThemePalette(pkg, workbookRels) {
|
|
|
318
532
|
}
|
|
319
533
|
return palette;
|
|
320
534
|
}
|
|
535
|
+
function buildThemeLineWidths(pkg, workbookRels) {
|
|
536
|
+
for (const rel of workbookRels) {
|
|
537
|
+
if (!isOoxmlRel(rel.type, "theme")) continue;
|
|
538
|
+
const resolved = pkg.resolveRelatedPart(WORKBOOK_PART, rel);
|
|
539
|
+
if (resolved) return parseThemeLineWidths(resolved.data);
|
|
540
|
+
}
|
|
541
|
+
return [];
|
|
542
|
+
}
|
|
543
|
+
function buildThemeEffectStyles(pkg, workbookRels) {
|
|
544
|
+
for (const rel of workbookRels) {
|
|
545
|
+
if (!isOoxmlRel(rel.type, "theme")) continue;
|
|
546
|
+
const resolved = pkg.resolveRelatedPart(WORKBOOK_PART, rel);
|
|
547
|
+
if (resolved) return parseThemeEffectStyles(resolved.data);
|
|
548
|
+
}
|
|
549
|
+
return [];
|
|
550
|
+
}
|
|
551
|
+
function buildThemeFillStyles(pkg, workbookRels) {
|
|
552
|
+
for (const rel of workbookRels) {
|
|
553
|
+
if (!isOoxmlRel(rel.type, "theme")) continue;
|
|
554
|
+
const resolved = pkg.resolveRelatedPart(WORKBOOK_PART, rel);
|
|
555
|
+
if (resolved) return parseThemeFillStyles(resolved.data);
|
|
556
|
+
}
|
|
557
|
+
return [];
|
|
558
|
+
}
|
|
321
559
|
function resolveTableStyle(t, palette) {
|
|
322
560
|
const m = t.styleName ? /TableStyle(Light|Medium|Dark)(\d+)/i.exec(t.styleName) : null;
|
|
323
561
|
if (!m) return t;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { fetchFontSet } from "../core/fonts/remote-fonts.js";
|
|
2
2
|
import { FontRegistry } from "../core/font/font-registry.js";
|
|
3
|
+
import { createFontMeasure } from "../core/font/measure.js";
|
|
3
4
|
import { signPdf } from "../pdf/signature.js";
|
|
4
5
|
import { renderStyledPdf, renderStyledPdfEncrypted } from "../pdf/styled-page-renderer.js";
|
|
5
6
|
import "../pdf/index.js";
|
|
@@ -7,6 +8,11 @@ import { readXlsx } from "./xlsx-reader.js";
|
|
|
7
8
|
import { flowRenderOptions } from "../core/converter/project.js";
|
|
8
9
|
//#region src/excel/xlsx-to-pdf.ts
|
|
9
10
|
/**
|
|
11
|
+
* The point size Excel's column-width unit is quoted at — its default theme
|
|
12
|
+
* font is 11 pt, and 8.43 of its digits are the documented 64 px column.
|
|
13
|
+
*/
|
|
14
|
+
var DEFAULT_WORKBOOK_FONT_PT = 11;
|
|
15
|
+
/**
|
|
10
16
|
* Convert a .xlsx to PDF in one shot.
|
|
11
17
|
*
|
|
12
18
|
* @deprecated Use `Ream.parse(bytes).convert('pdf', options)` — one parse,
|
|
@@ -45,7 +51,12 @@ function prepareXlsxStyledRender(xlsx, options) {
|
|
|
45
51
|
const fonts = options.fonts ?? (options.fontBytes ? { regular: options.fontBytes } : void 0);
|
|
46
52
|
if (!fonts) throw new Error("convertXlsxToPdfSync requires options.fonts or options.fontBytes");
|
|
47
53
|
const registry = FontRegistry.fromBytes(fonts);
|
|
48
|
-
const {
|
|
54
|
+
const { parsed } = registry.resolveByStyle(false, false);
|
|
55
|
+
const digitWidthPt = createFontMeasure(parsed).textWidthPt("0", DEFAULT_WORKBOOK_FONT_PT);
|
|
56
|
+
const { doc: flow } = readXlsx(xlsx, {
|
|
57
|
+
...options.now !== void 0 ? { now: options.now } : {},
|
|
58
|
+
digitWidthPt
|
|
59
|
+
});
|
|
49
60
|
const { fontBytes: _ignoreA, fonts: _ignoreB, section: sectionOverride, info: callerInfo, embedSource, now: _ignoreNow, attachments: callerAttachments, signature: _ignoreSig, layoutProfile: _ignoreProfile, ...renderOptions } = options;
|
|
50
61
|
const section = sectionOverride ?? flow.section;
|
|
51
62
|
const info = flow.info || callerInfo ? {
|
|
@@ -265,7 +265,13 @@ function wedgeSvg(w) {
|
|
|
265
265
|
return `<path d="${svgPathData(b.build().segments, fmt)}" fill="#${w.fillHex}"${stroke}/>`;
|
|
266
266
|
}
|
|
267
267
|
function labelSvg(l, sceneH, anchor) {
|
|
268
|
-
|
|
268
|
+
const rot = l.rotationDeg ? ` transform="rotate(${fmt(-l.rotationDeg)} ${fmt(l.x)} ${fmt(sceneH - l.y)})"` : "";
|
|
269
|
+
return `<text x="${fmt(l.x)}" y="${fmt(sceneH - l.y)}"${rot} font-size="${fmt(l.sizePt)}" font-family="sans-serif" fill="#${l.colorHex}" text-anchor="${anchor}">${escapeText(l.text)}</text>`;
|
|
270
|
+
}
|
|
271
|
+
/** `RRGGBB` as the `r,g,b` triple a CSS `rgba()` takes. */
|
|
272
|
+
function hexToRgbCss(hex) {
|
|
273
|
+
const n = parseInt(hex, 16);
|
|
274
|
+
return `${String(n >> 16 & 255)},${String(n >> 8 & 255)},${String(n & 255)}`;
|
|
269
275
|
}
|
|
270
276
|
function emitShapeBlock(out, shape, ctx) {
|
|
271
277
|
const w = shape.width;
|
|
@@ -278,9 +284,11 @@ function emitShapeBlock(out, shape, ctx) {
|
|
|
278
284
|
const transform = ` transform="matrix(${flipTransform(buildShapeTransform(0, 0, w, h, t?.rotation60k ?? 0, t?.flipH ?? false, t?.flipV ?? false), h).map(fmt).join(" ")})"`;
|
|
279
285
|
const svg = [svgOpen(w, h, shape.altText)];
|
|
280
286
|
if (gradDef) svg.push(`<defs>${gradDef}</defs>`);
|
|
287
|
+
const shadow = shape.shadow;
|
|
288
|
+
const shadowFilter = shadow ? ` filter="drop-shadow(${fmt(shadow.dxPt)}px ${fmt(shadow.dyPt)}px ${fmt(shadow.blurPt / 2)}px rgba(${hexToRgbCss(shadow.colorHex)},${fmt(shadow.alpha)}))"` : "";
|
|
281
289
|
for (const path of paths) {
|
|
282
290
|
const rule = path.fillRule === "evenodd" ? " fill-rule=\"evenodd\"" : "";
|
|
283
|
-
svg.push(`<path d="${svgPathData(path.segments, fmt)}" fill="${fill}"${rule}${stroke}${transform}/>`);
|
|
291
|
+
svg.push(`<path d="${svgPathData(path.segments, fmt)}" fill="${fill}"${rule}${stroke}${transform}${shadowFilter}/>`);
|
|
284
292
|
}
|
|
285
293
|
svg.push("</svg>");
|
|
286
294
|
const figureCss = blockFigureCss(shape.paragraphProperties);
|
|
@@ -491,6 +499,7 @@ function emitCell(out, cell, table, ctx, pos) {
|
|
|
491
499
|
pushBorder(css, "left", c?.left ?? (pos.firstCol ? t?.left : t?.insideV));
|
|
492
500
|
pushBorder(css, "right", c?.right ?? (pos.lastCol ? t?.right : t?.insideV));
|
|
493
501
|
if (cell.properties.shading) css.push(`background-color:#${cell.properties.shading.colorHex}`);
|
|
502
|
+
if (cell.properties.noWrap) css.push("white-space:nowrap", "overflow:hidden");
|
|
494
503
|
if (cell.properties.dataBar) {
|
|
495
504
|
const db = cell.properties.dataBar;
|
|
496
505
|
const clamp = (n) => Math.max(0, Math.min(1, n));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BorderStyle } from '../core/document-model/index.js';
|
|
1
2
|
import { Pt, ResourceId, ResourceStore } from '../core/ir/index.js';
|
|
2
3
|
import { FontMeasure, ParsedTtf } from '../core/font/index.js';
|
|
3
4
|
import { ResolvedParagraphProperties, ResolvedRunProperties } from '../core/style-cascade/index.js';
|
|
@@ -49,6 +50,12 @@ export interface TextToken {
|
|
|
49
50
|
readonly resolvedRun: ResolvedRunProperties;
|
|
50
51
|
readonly font: FontResource;
|
|
51
52
|
readonly fontSizePt: number;
|
|
53
|
+
/**
|
|
54
|
+
* §17.3.2.42 / §18.4.2 `vertAlign` — how far off the baseline this token
|
|
55
|
+
* draws: positive for a superscript, negative for a subscript, absent on the
|
|
56
|
+
* baseline. The line's height is unchanged; only the glyphs move.
|
|
57
|
+
*/
|
|
58
|
+
readonly risePt?: number;
|
|
52
59
|
readonly widthPt: number;
|
|
53
60
|
/**
|
|
54
61
|
* UAX #9 embedding level of this token's characters (0 for pure-LTR docs).
|
|
@@ -167,6 +174,19 @@ export interface TextLineItem extends PageItemBase {
|
|
|
167
174
|
readonly originX: Pt;
|
|
168
175
|
/** Distance from the page TOP down to the text baseline. */
|
|
169
176
|
readonly baselineY: Pt;
|
|
177
|
+
readonly clip?: {
|
|
178
|
+
readonly x: Pt;
|
|
179
|
+
readonly y: Pt;
|
|
180
|
+
readonly width: Pt;
|
|
181
|
+
readonly height: Pt;
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* Counter-clockwise rotation about `(originX, baselineY)`, in degrees. A
|
|
185
|
+
* value-axis title reads bottom-to-top in every reader (§21.2.2.216
|
|
186
|
+
* `c:title/c:tx/…/a:bodyPr@rot` = -5400000, i.e. 90°); drawn flat it sat over
|
|
187
|
+
* the plot's own tick labels. Absent ⇒ horizontal.
|
|
188
|
+
*/
|
|
189
|
+
readonly rotationDeg?: number;
|
|
170
190
|
}
|
|
171
191
|
/** One edge of a table-cell frame; `(x, y)` is the cell box's top-left corner. */
|
|
172
192
|
export interface BorderItem extends PageItemBase {
|
|
@@ -178,6 +198,12 @@ export interface BorderItem extends PageItemBase {
|
|
|
178
198
|
readonly height: Pt;
|
|
179
199
|
readonly borderSizePt: number;
|
|
180
200
|
readonly borderColorHex: string;
|
|
201
|
+
/**
|
|
202
|
+
* §18.18.3 `ST_BorderStyle` — the rule's PATTERN, when it is not solid.
|
|
203
|
+
* `dashed`/`dotted` are line patterns, not weights, and dropping them here
|
|
204
|
+
* painted 57423.xlsx's dashed band as twelve continuous rules.
|
|
205
|
+
*/
|
|
206
|
+
readonly borderStyle?: BorderStyle;
|
|
181
207
|
}
|
|
182
208
|
/** A filled rectangle (cell shading); `(x, y)` is its top-left corner. */
|
|
183
209
|
export interface FillItem extends PageItemBase {
|
|
@@ -278,6 +278,8 @@ export interface SectionRenderCtx {
|
|
|
278
278
|
interface HfBandEntry {
|
|
279
279
|
readonly commands: Array<PageItem>;
|
|
280
280
|
readonly renderDynamic?: (pageNumber: number, totalPages: number) => Array<PageItem>;
|
|
281
|
+
/** Laid-out height of the band, so the body can be kept clear of it. */
|
|
282
|
+
readonly heightPt?: number;
|
|
281
283
|
}
|
|
282
284
|
interface HeaderFooterSet {
|
|
283
285
|
readonly default: HfBandEntry;
|