reamkit 1.15.4 → 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 +61 -0
- 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.js +1 -0
- 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 +898 -76
- 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 +150 -61
- package/dist/esm/excel/xlsx-reader.js +242 -11
- 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,14 +22,20 @@ 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";
|
|
25
|
+
import { parseVmlDrawing } from "./vml-drawing.js";
|
|
26
|
+
import { parsePrinterSettings } from "./printer-settings.js";
|
|
27
|
+
import { parseRichValueText } from "./rich-value.js";
|
|
24
28
|
import { activeXBinRelId, activeXType, activeXTypeFromPart, parseActiveX, parseActiveXBin } from "./activex-parser.js";
|
|
25
|
-
import { parseSheetShapes } from "./sheet-shape-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";
|
|
@@ -80,9 +87,11 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
80
87
|
texts: [],
|
|
81
88
|
runs: []
|
|
82
89
|
};
|
|
83
|
-
const stylesData = pkg.getPart(STYLES_PART);
|
|
84
|
-
const styles = stylesData ? parseXlsxStyles(stylesData) : EMPTY_XLSX_STYLES;
|
|
85
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;
|
|
86
95
|
const persons = /* @__PURE__ */ new Map();
|
|
87
96
|
for (const rel of workbookRels) {
|
|
88
97
|
if (!rel.type.endsWith(PERSON_REL_TAIL)) continue;
|
|
@@ -93,7 +102,12 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
93
102
|
const resources = new ResourceStore();
|
|
94
103
|
const palette = buildThemePalette(pkg, workbookRels);
|
|
95
104
|
const resolveColor = makeColorResolver(palette);
|
|
105
|
+
const themeLineWidths = buildThemeLineWidths(pkg, workbookRels);
|
|
106
|
+
const themeFillStyles = buildThemeFillStyles(pkg, workbookRels);
|
|
107
|
+
const themeEffectStyles = buildThemeEffectStyles(pkg, workbookRels);
|
|
96
108
|
const sheetsOut = [];
|
|
109
|
+
let embeddedObjects = 0;
|
|
110
|
+
let metafilePictures = 0;
|
|
97
111
|
const tableIndex = /* @__PURE__ */ new Map();
|
|
98
112
|
const pendingSlicers = [];
|
|
99
113
|
for (let sheetIdx = 0; sheetIdx < sheets.length; sheetIdx++) {
|
|
@@ -102,7 +116,9 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
102
116
|
if (!sheetRel) continue;
|
|
103
117
|
const resolved = pkg.resolveRelatedPart(WORKBOOK_PART, sheetRel);
|
|
104
118
|
if (!resolved) continue;
|
|
105
|
-
|
|
119
|
+
let worksheet = parseWorksheet(resolved.data, themePalette);
|
|
120
|
+
worksheet = withPrinterPageSetup(worksheet, pkg, resolved.path, pkg.getPartRelationships(resolved.path));
|
|
121
|
+
worksheet = withRichValues(worksheet, richValueText);
|
|
106
122
|
const charts = [];
|
|
107
123
|
const images = [];
|
|
108
124
|
let shapes;
|
|
@@ -121,22 +137,39 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
121
137
|
charts.push({
|
|
122
138
|
chartPartPath: ref.chartPartPath,
|
|
123
139
|
widthPt: ref.widthPt,
|
|
124
|
-
heightPt: ref.heightPt
|
|
140
|
+
heightPt: ref.heightPt,
|
|
141
|
+
xPt: ref.xPt,
|
|
142
|
+
yPt: ref.yPt
|
|
125
143
|
});
|
|
126
144
|
}
|
|
127
145
|
for (const pic of pictures) {
|
|
128
146
|
const bytes = pkg.getPart(pic.imagePartPath);
|
|
129
147
|
if (!bytes) continue;
|
|
148
|
+
if (detectImageFormat(bytes) === null) metafilePictures++;
|
|
130
149
|
images.push({
|
|
131
150
|
resourceId: resources.put(bytes),
|
|
132
151
|
widthPt: pic.widthPt,
|
|
133
|
-
heightPt: pic.heightPt
|
|
152
|
+
heightPt: pic.heightPt,
|
|
153
|
+
xPt: pic.xPt,
|
|
154
|
+
yPt: pic.yPt
|
|
134
155
|
});
|
|
135
156
|
}
|
|
136
157
|
if (bytesInclude(drawing.data, ":sp>") || bytesInclude(drawing.data, ":sp ")) {
|
|
137
|
-
const parsed = parseSheetShapes(drawing.data, worksheet, resolveColor);
|
|
158
|
+
const parsed = parseSheetShapes(drawing.data, worksheet, resolveColor, themeLineWidths, themeFillStyles, themeEffectStyles);
|
|
138
159
|
if (parsed.length > 0) shapes = parsed;
|
|
139
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
|
+
}
|
|
140
173
|
}
|
|
141
174
|
}
|
|
142
175
|
let tables;
|
|
@@ -233,29 +266,40 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
233
266
|
...props
|
|
234
267
|
};
|
|
235
268
|
};
|
|
269
|
+
const legacyVml = readLegacyVml(pkg, resolved.path, wsRels, worksheet);
|
|
236
270
|
let formControls;
|
|
237
271
|
if (worksheet.formControls && worksheet.formControls.length > 0) {
|
|
238
272
|
const resolvedControls = [];
|
|
239
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;
|
|
240
276
|
const rel = wsRels.find((r) => r.id === fc.relId);
|
|
241
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;
|
|
242
279
|
if (part && ACTIVEX_PART.test(part.path)) {
|
|
243
280
|
resolvedAx.push({
|
|
244
281
|
...activeXState(part),
|
|
245
282
|
type: activeXTypeFromPart(part.data),
|
|
246
|
-
...fc.name ? { name: fc.name } : {}
|
|
283
|
+
...fc.name ? { name: fc.name } : {},
|
|
284
|
+
...box ? { box } : {}
|
|
247
285
|
});
|
|
248
286
|
continue;
|
|
249
287
|
}
|
|
250
288
|
const props = part ? parseFormControlProps(part.data) : {};
|
|
251
289
|
resolvedControls.push({
|
|
252
290
|
...fc.name ? { name: fc.name } : {},
|
|
253
|
-
...props
|
|
291
|
+
...props,
|
|
292
|
+
...box ? { box } : {}
|
|
254
293
|
});
|
|
255
294
|
}
|
|
256
295
|
if (resolvedControls.length > 0) formControls = resolvedControls;
|
|
257
296
|
}
|
|
297
|
+
if (legacyVml.controls.length > 0) formControls = [...formControls ?? [], ...legacyVml.controls];
|
|
258
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;
|
|
302
|
+
}
|
|
259
303
|
const rel = wsRels.find((r) => r.id === ole.relId);
|
|
260
304
|
const part = rel ? pkg.resolveRelatedPart(resolved.path, rel) : void 0;
|
|
261
305
|
resolvedAx.push({
|
|
@@ -271,6 +315,7 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
271
315
|
} : worksheet;
|
|
272
316
|
sheetsOut.push({
|
|
273
317
|
name: sheet.name,
|
|
318
|
+
...sheet.hidden ? { hidden: true } : {},
|
|
274
319
|
grid,
|
|
275
320
|
...charts.length > 0 ? { charts } : {},
|
|
276
321
|
...images.length > 0 ? { images } : {},
|
|
@@ -299,6 +344,10 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
299
344
|
};
|
|
300
345
|
}
|
|
301
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
|
+
}
|
|
302
351
|
const coreData = pkg.getPart(CORE_PROPS_PART);
|
|
303
352
|
const info = infoFromCore(coreData ? parseCoreProperties(coreData) : void 0);
|
|
304
353
|
return {
|
|
@@ -311,9 +360,167 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
311
360
|
date1904,
|
|
312
361
|
...chartData.size > 0 ? { chartData } : {},
|
|
313
362
|
resources,
|
|
363
|
+
themePalette,
|
|
364
|
+
...embeddedObjects > 0 ? { embeddedObjects } : {},
|
|
365
|
+
...metafilePictures > 0 ? { metafilePictures } : {},
|
|
314
366
|
...info ? { info } : {}
|
|
315
367
|
};
|
|
316
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
|
+
}
|
|
317
524
|
function buildThemePalette(pkg, workbookRels) {
|
|
318
525
|
const palette = new Map(DEFAULT_THEME_PALETTE);
|
|
319
526
|
for (const rel of workbookRels) {
|
|
@@ -325,6 +532,30 @@ function buildThemePalette(pkg, workbookRels) {
|
|
|
325
532
|
}
|
|
326
533
|
return palette;
|
|
327
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
|
+
}
|
|
328
559
|
function resolveTableStyle(t, palette) {
|
|
329
560
|
const m = t.styleName ? /TableStyle(Light|Medium|Dark)(\d+)/i.exec(t.styleName) : null;
|
|
330
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;
|