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.
Files changed (95) hide show
  1. package/dist/esm/core/converter/facade.d.ts +5 -0
  2. package/dist/esm/core/converter/ream.d.ts +6 -0
  3. package/dist/esm/core/converter/ream.js +34 -8
  4. package/dist/esm/core/document-model/index.d.ts +1 -1
  5. package/dist/esm/core/document-model/types.d.ts +187 -1
  6. package/dist/esm/core/drawingml/chart-geometry.d.ts +28 -0
  7. package/dist/esm/core/drawingml/chart-geometry.js +371 -88
  8. package/dist/esm/core/drawingml/chart-parser.js +262 -11
  9. package/dist/esm/core/drawingml/colors.d.ts +6 -6
  10. package/dist/esm/core/drawingml/colors.js +58 -18
  11. package/dist/esm/core/drawingml/preset-geometry.js +7 -6
  12. package/dist/esm/core/drawingml/sparkline-geometry.js +6 -4
  13. package/dist/esm/core/drawingml/theme-parser.d.ts +41 -0
  14. package/dist/esm/core/drawingml/theme-parser.js +74 -1
  15. package/dist/esm/core/font/ttf-subset.js +26 -1
  16. package/dist/esm/core/images.js +172 -43
  17. package/dist/esm/core/indexed-colors.d.ts +11 -0
  18. package/dist/esm/core/indexed-colors.js +81 -0
  19. package/dist/esm/core/ir/sheet.d.ts +67 -3
  20. package/dist/esm/{excel → core}/number-format.d.ts +37 -0
  21. package/dist/esm/core/number-format.js +800 -0
  22. package/dist/esm/core/opc/alternate-content.d.ts +13 -0
  23. package/dist/esm/core/opc/alternate-content.js +72 -0
  24. package/dist/esm/core/opc/core-properties.js +1 -0
  25. package/dist/esm/core/opc/package.d.ts +14 -1
  26. package/dist/esm/core/opc/package.js +43 -4
  27. package/dist/esm/core/opc/relationships.js +1 -0
  28. package/dist/esm/core/opc/xml-entities.d.ts +12 -0
  29. package/dist/esm/core/opc/xml-entities.js +91 -0
  30. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  31. package/dist/esm/core/spreadsheet-model/types.d.ts +137 -0
  32. package/dist/esm/core/vector.d.ts +13 -0
  33. package/dist/esm/excel/activex-parser.d.ts +9 -0
  34. package/dist/esm/excel/activex-parser.js +16 -1
  35. package/dist/esm/excel/column-bands.d.ts +26 -1
  36. package/dist/esm/excel/column-bands.js +156 -20
  37. package/dist/esm/excel/comments-parser.js +5 -3
  38. package/dist/esm/excel/conditional-format.d.ts +25 -2
  39. package/dist/esm/excel/conditional-format.js +63 -19
  40. package/dist/esm/excel/escaped-text.d.ts +9 -0
  41. package/dist/esm/excel/escaped-text.js +20 -0
  42. package/dist/esm/excel/form-control-parser.js +1 -0
  43. package/dist/esm/excel/formula/dates.js +1 -1
  44. package/dist/esm/excel/header-footer.d.ts +9 -1
  45. package/dist/esm/excel/header-footer.js +162 -14
  46. package/dist/esm/excel/index.d.ts +1 -1
  47. package/dist/esm/excel/index.js +2 -2
  48. package/dist/esm/excel/pivot-table-parser.js +1 -0
  49. package/dist/esm/excel/print-model.d.ts +75 -0
  50. package/dist/esm/excel/print-model.js +902 -77
  51. package/dist/esm/excel/printer-settings.d.ts +16 -0
  52. package/dist/esm/excel/printer-settings.js +36 -0
  53. package/dist/esm/excel/rich-value.d.ts +13 -0
  54. package/dist/esm/excel/rich-value.js +120 -0
  55. package/dist/esm/excel/shared-strings-parser.js +11 -7
  56. package/dist/esm/excel/sheet-drawing.d.ts +11 -0
  57. package/dist/esm/excel/sheet-drawing.js +26 -3
  58. package/dist/esm/excel/sheet-shape-parser.d.ts +53 -1
  59. package/dist/esm/excel/sheet-shape-parser.js +461 -32
  60. package/dist/esm/excel/sheet-to-flow.d.ts +23 -2
  61. package/dist/esm/excel/sheet-to-flow.js +551 -36
  62. package/dist/esm/excel/slicer-parser.js +1 -0
  63. package/dist/esm/excel/styles-parser.d.ts +30 -2
  64. package/dist/esm/excel/styles-parser.js +177 -40
  65. package/dist/esm/excel/table-parser.js +1 -0
  66. package/dist/esm/excel/vml-drawing.d.ts +68 -0
  67. package/dist/esm/excel/vml-drawing.js +158 -0
  68. package/dist/esm/excel/workbook-parser.d.ts +5 -0
  69. package/dist/esm/excel/workbook-parser.js +5 -1
  70. package/dist/esm/excel/worksheet-parser.d.ts +2 -1
  71. package/dist/esm/excel/worksheet-parser.js +149 -28
  72. package/dist/esm/excel/xlsx-reader.js +271 -33
  73. package/dist/esm/excel/xlsx-to-pdf.js +12 -1
  74. package/dist/esm/html/html-writer.js +11 -2
  75. package/dist/esm/layout/page-doc.d.ts +26 -0
  76. package/dist/esm/layout/styled-layout.d.ts +2 -0
  77. package/dist/esm/layout/styled-layout.js +493 -64
  78. package/dist/esm/pdf/cid-font.js +20 -9
  79. package/dist/esm/pdf/objects.d.ts +13 -0
  80. package/dist/esm/pdf/objects.js +18 -1
  81. package/dist/esm/pdf/styled-page-emitter.js +164 -32
  82. package/dist/esm/pdf/text-page-renderer.js +1 -0
  83. package/dist/esm/pdf/vector-graphics.d.ts +3 -1
  84. package/dist/esm/pdf/vector-graphics.js +19 -2
  85. package/dist/esm/pptx/pptx-reader.js +1 -0
  86. package/dist/esm/pptx/slide-parser.js +12 -1
  87. package/dist/esm/svg/svg-writer.js +23 -4
  88. package/dist/esm/word/document-parser.js +1 -0
  89. package/dist/esm/word/drawing-parser.d.ts +34 -1
  90. package/dist/esm/word/drawing-parser.js +52 -2
  91. package/dist/esm/word/numbering-parser.js +1 -0
  92. package/dist/esm/word/settings-parser.js +1 -0
  93. package/dist/esm/word/styles-parser.js +1 -0
  94. package/package.json +7 -3
  95. 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 { activeXBinRelId, activeXType, parseActiveX, parseActiveXBin } from "./activex-parser.js";
25
- import { parseSheetShapes } from "./sheet-shape-parser.js";
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
- const worksheet = parseWorksheet(resolved.data);
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
- let activeXControls;
238
- if (worksheet.oleObjects && worksheet.oleObjects.length > 0) {
239
- const wsRels = pkg.getPartRelationships(resolved.path);
240
- const resolvedAx = [];
241
- for (const ole of worksheet.oleObjects) {
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
- if (resolvedAx.length > 0) activeXControls = resolvedAx;
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 { doc: flow } = readXlsx(xlsx, options.now !== void 0 ? { now: options.now } : {});
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
- return `<text x="${fmt(l.x)}" y="${fmt(sceneH - l.y)}" font-size="${fmt(l.sizePt)}" font-family="sans-serif" fill="#${l.colorHex}" text-anchor="${anchor}">${escapeText(l.text)}</text>`;
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;