documonster 0.19.0 → 0.20.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 (160) hide show
  1. package/README.md +1 -1
  2. package/README_zh.md +1 -1
  3. package/dist/esm/modules/archive/compression/compress.base.js +20 -6
  4. package/dist/esm/modules/archive/compression/compress.browser.js +45 -32
  5. package/dist/esm/modules/archive/compression/compress.js +74 -18
  6. package/dist/esm/modules/archive/compression/deflate-fallback.js +14 -3
  7. package/dist/esm/modules/archive/compression/worker-pool/pool.browser.js +9 -3
  8. package/dist/esm/modules/archive/compression/worker-pool/worker-script.js +35 -7
  9. package/dist/esm/modules/archive/core/errors.js +26 -4
  10. package/dist/esm/modules/archive/index.base.js +1 -1
  11. package/dist/esm/modules/archive/unzip/zip-extract-core.js +74 -34
  12. package/dist/esm/modules/archive/unzip/zip-parser.js +55 -9
  13. package/dist/esm/modules/archive/zip-spec/zip-parser-core.js +14 -2
  14. package/dist/esm/modules/csv/parse/scanner/scanner.js +3 -0
  15. package/dist/esm/modules/csv/parse/sync.js +3 -2
  16. package/dist/esm/modules/csv/stream/parser.js +40 -4
  17. package/dist/esm/modules/csv/worker/worker-script.generated.js +1 -1
  18. package/dist/esm/modules/excel/core/cell.js +22 -2
  19. package/dist/esm/modules/excel/core/table.js +39 -2
  20. package/dist/esm/modules/excel/core/worksheet-core.js +39 -0
  21. package/dist/esm/modules/excel/core/worksheet-load.js +3 -1
  22. package/dist/esm/modules/excel/stream/workbook-reader.browser.js +34 -11
  23. package/dist/esm/modules/excel/surface/cell.js +64 -23
  24. package/dist/esm/modules/excel/utils/cell-format.js +3 -2
  25. package/dist/esm/modules/excel/xlsb/read/package.js +3 -1
  26. package/dist/esm/modules/excel/xlsx/read/package.js +17 -1
  27. package/dist/esm/modules/pdf/builder/document-builder.js +3 -1
  28. package/dist/esm/modules/pdf/builder/pdf-editor.js +218 -58
  29. package/dist/esm/modules/pdf/core/pdf-kdf.browser.js +9 -1
  30. package/dist/esm/modules/pdf/core/pdf-kdf.js +8 -0
  31. package/dist/esm/modules/pdf/core/pdf-writer.js +159 -149
  32. package/dist/esm/modules/pdf/core/pdfa.js +7 -3
  33. package/dist/esm/modules/pdf/errors.js +25 -0
  34. package/dist/esm/modules/pdf/index.js +1 -1
  35. package/dist/esm/modules/pdf/reader/pdf-decrypt.js +136 -74
  36. package/dist/esm/modules/pdf/reader/pdf-document.js +314 -105
  37. package/dist/esm/modules/pdf/reader/pdf-parser.js +29 -11
  38. package/dist/esm/modules/pdf/reader/pdf-reader.js +8 -1
  39. package/dist/esm/modules/pdf/reader/stream-filters.js +139 -77
  40. package/dist/esm/modules/pdf/render/pdf-exporter.js +3 -1
  41. package/dist/esm/modules/pdf/word-bridge.js +22 -48
  42. package/dist/esm/modules/word/advanced/field-engine.js +75 -56
  43. package/dist/esm/modules/word/builder/document-handle.js +15 -1
  44. package/dist/esm/modules/word/convert/docx-to-semantic.js +84 -83
  45. package/dist/esm/modules/word/convert/html/html-renderer.js +107 -78
  46. package/dist/esm/modules/word/convert/markdown/markdown-import.js +43 -10
  47. package/dist/esm/modules/word/convert/markdown/markdown-renderer.js +98 -130
  48. package/dist/esm/modules/word/convert/odt/odt.js +149 -55
  49. package/dist/esm/modules/word/core/text-utils.js +9 -0
  50. package/dist/esm/modules/word/errors.js +1 -1
  51. package/dist/esm/modules/word/layout/layout-constants.js +20 -23
  52. package/dist/esm/modules/word/layout/layout-full.js +259 -280
  53. package/dist/esm/modules/word/layout/layout.js +140 -197
  54. package/dist/esm/modules/word/query/final-view.js +49 -0
  55. package/dist/esm/modules/word/query/heading.js +81 -0
  56. package/dist/esm/modules/word/query/merge.js +39 -22
  57. package/dist/esm/modules/word/query/numbering-counter.js +108 -0
  58. package/dist/esm/modules/word/query/page-field.js +212 -0
  59. package/dist/esm/modules/word/query/search.js +15 -14
  60. package/dist/esm/modules/word/query/split.js +15 -25
  61. package/dist/esm/modules/word/query/style-resolve.js +196 -53
  62. package/dist/esm/modules/word/query/style-resolver.js +24 -0
  63. package/dist/esm/modules/word/reader/chart-parser.js +17 -7
  64. package/dist/esm/modules/word/reader/comments-parser.js +4 -4
  65. package/dist/esm/modules/word/reader/doc-props-parsers.js +10 -9
  66. package/dist/esm/modules/word/reader/docx-reader.js +46 -32
  67. package/dist/esm/modules/word/reader/metadata-parsers.js +5 -5
  68. package/dist/esm/modules/word/reader/numbering-parser.js +4 -3
  69. package/dist/esm/modules/word/reader/part-xml.js +26 -0
  70. package/dist/esm/modules/word/reader/reader-context.js +3 -3
  71. package/dist/esm/modules/word/reader/settings-parser.js +3 -3
  72. package/dist/esm/modules/word/reader/styles-parser.js +3 -3
  73. package/dist/esm/modules/word/reader/theme-parser.js +4 -3
  74. package/dist/esm/modules/word/surface/query.js +1 -1
  75. package/dist/esm/modules/word/writer/docx-packager.js +4 -1
  76. package/dist/esm/modules/word/writer/streaming-writer.js +11 -2
  77. package/dist/esm/modules/xml/dom.js +4 -8
  78. package/dist/esm/modules/xml/errors.js +1 -3
  79. package/dist/esm/modules/xml/sax.js +8 -7
  80. package/dist/esm/modules/xml/to-object.js +4 -8
  81. package/dist/esm/utils/crypto.browser.js +147 -0
  82. package/dist/esm/utils/crypto.js +14 -0
  83. package/dist/esm/utils/limits.js +19 -0
  84. package/dist/iife/documonster.archive.iife.min.js +39 -11
  85. package/dist/iife/documonster.csv.iife.min.js +17 -17
  86. package/dist/iife/documonster.draw.iife.min.js +1 -1
  87. package/dist/iife/documonster.excel.iife.min.js +88 -60
  88. package/dist/iife/documonster.formula.iife.min.js +1 -1
  89. package/dist/iife/documonster.markdown.iife.min.js +1 -1
  90. package/dist/iife/documonster.mermaid.iife.min.js +1 -1
  91. package/dist/iife/documonster.pdf.iife.min.js +44 -42
  92. package/dist/iife/documonster.stream.iife.min.js +1 -1
  93. package/dist/iife/documonster.word.iife.min.js +95 -67
  94. package/dist/iife/documonster.xml.iife.min.js +6 -6
  95. package/dist/types/modules/archive/compression/compress.base.d.ts +12 -3
  96. package/dist/types/modules/archive/compression/compress.browser.d.ts +7 -4
  97. package/dist/types/modules/archive/compression/compress.d.ts +9 -4
  98. package/dist/types/modules/archive/compression/deflate-fallback.d.ts +3 -1
  99. package/dist/types/modules/archive/compression/worker-pool/pool.browser.d.ts +4 -1
  100. package/dist/types/modules/archive/compression/worker-pool/types.d.ts +4 -0
  101. package/dist/types/modules/archive/core/errors.d.ts +31 -2
  102. package/dist/types/modules/archive/index.base.d.ts +1 -1
  103. package/dist/types/modules/archive/unzip/zip-extract-core.d.ts +9 -2
  104. package/dist/types/modules/archive/unzip/zip-parser.d.ts +33 -0
  105. package/dist/types/modules/archive/zip-spec/zip-parser-core.d.ts +6 -0
  106. package/dist/types/modules/csv/parse/scanner/types.d.ts +5 -0
  107. package/dist/types/modules/csv/stream/parser.d.ts +16 -0
  108. package/dist/types/modules/csv/worker/worker-script.generated.d.ts +1 -1
  109. package/dist/types/modules/excel/core/table.d.ts +15 -0
  110. package/dist/types/modules/excel/core/worksheet-core.d.ts +26 -0
  111. package/dist/types/modules/excel/stream/workbook-reader.browser.d.ts +11 -0
  112. package/dist/types/modules/excel/surface/cell.d.ts +8 -8
  113. package/dist/types/modules/excel/xlsx/types.d.ts +16 -0
  114. package/dist/types/modules/pdf/builder/document-builder.d.ts +7 -0
  115. package/dist/types/modules/pdf/builder/pdf-editor.d.ts +49 -5
  116. package/dist/types/modules/pdf/core/pdf-kdf.browser.d.ts +4 -0
  117. package/dist/types/modules/pdf/core/pdf-kdf.d.ts +4 -0
  118. package/dist/types/modules/pdf/core/pdf-writer.d.ts +49 -10
  119. package/dist/types/modules/pdf/core/pdfa.d.ts +2 -0
  120. package/dist/types/modules/pdf/errors.d.ts +19 -0
  121. package/dist/types/modules/pdf/index.d.ts +2 -2
  122. package/dist/types/modules/pdf/reader/pdf-document.d.ts +83 -5
  123. package/dist/types/modules/pdf/reader/pdf-parser.d.ts +2 -2
  124. package/dist/types/modules/pdf/reader/pdf-reader.d.ts +7 -0
  125. package/dist/types/modules/pdf/reader/stream-filters.d.ts +5 -1
  126. package/dist/types/modules/pdf/types.d.ts +7 -0
  127. package/dist/types/modules/word/convert/conversion-ir.d.ts +2 -0
  128. package/dist/types/modules/word/core/text-utils.d.ts +7 -1
  129. package/dist/types/modules/word/errors.d.ts +3 -3
  130. package/dist/types/modules/word/index.base.d.ts +1 -1
  131. package/dist/types/modules/word/layout/layout-constants.d.ts +13 -9
  132. package/dist/types/modules/word/query/final-view.d.ts +26 -0
  133. package/dist/types/modules/word/query/heading.d.ts +30 -0
  134. package/dist/types/modules/word/query/numbering-counter.d.ts +47 -0
  135. package/dist/types/modules/word/query/page-field.d.ts +79 -0
  136. package/dist/types/modules/word/query/search.d.ts +8 -2
  137. package/dist/types/modules/word/query/split.d.ts +4 -1
  138. package/dist/types/modules/word/query/style-resolve.d.ts +58 -11
  139. package/dist/types/modules/word/query/style-resolver.d.ts +29 -0
  140. package/dist/types/modules/word/reader/chart-parser.d.ts +2 -2
  141. package/dist/types/modules/word/reader/comments-parser.d.ts +1 -1
  142. package/dist/types/modules/word/reader/doc-props-parsers.d.ts +4 -4
  143. package/dist/types/modules/word/reader/metadata-parsers.d.ts +2 -2
  144. package/dist/types/modules/word/reader/numbering-parser.d.ts +1 -1
  145. package/dist/types/modules/word/reader/part-xml.d.ts +14 -0
  146. package/dist/types/modules/word/reader/reader-context.d.ts +1 -1
  147. package/dist/types/modules/word/reader/settings-parser.d.ts +1 -1
  148. package/dist/types/modules/word/reader/styles-parser.d.ts +1 -1
  149. package/dist/types/modules/word/reader/theme-parser.d.ts +1 -1
  150. package/dist/types/modules/word/surface/query.d.ts +2 -1
  151. package/dist/types/modules/word/types.d.ts +5 -1
  152. package/dist/types/modules/word/writer/docx-packager.d.ts +7 -0
  153. package/dist/types/modules/word/writer/streaming-writer.d.ts +7 -0
  154. package/dist/types/modules/xml/errors.d.ts +9 -0
  155. package/dist/types/modules/xml/index.d.ts +1 -0
  156. package/dist/types/modules/xml/sax.d.ts +2 -1
  157. package/dist/types/utils/crypto.browser.d.ts +10 -0
  158. package/dist/types/utils/crypto.d.ts +10 -0
  159. package/dist/types/utils/limits.d.ts +12 -0
  160. package/package.json +1 -1
@@ -1280,13 +1280,33 @@ class FormulaValue {
1280
1280
  return this._translatedFormula;
1281
1281
  }
1282
1282
  toCsvString() {
1283
- return `${this.model.result ?? ""}`;
1283
+ return formulaResultText(this.model.result, true);
1284
1284
  }
1285
1285
  release() { }
1286
1286
  toString() {
1287
- return this.model.result ? this.model.result.toString() : "";
1287
+ return formulaResultText(this.model.result, false);
1288
1288
  }
1289
1289
  }
1290
+ /**
1291
+ * A formula's cached result as text — the same text the result would have as a plain cell value.
1292
+ *
1293
+ * Written out rather than `result ? result.toString() : ""`, which had two bugs: a falsy result (`0`,
1294
+ * `false`) read as no result at all, and an error result is an object, so it read `"[object Object]"`
1295
+ * where a stored error reads its code. A `Date` renders as {@link DateValue} renders one: ISO for CSV,
1296
+ * `Date#toString` otherwise.
1297
+ */
1298
+ function formulaResultText(result, csv) {
1299
+ if (result === undefined || result === null) {
1300
+ return "";
1301
+ }
1302
+ if (result instanceof Date) {
1303
+ return csv ? result.toISOString() : result.toString();
1304
+ }
1305
+ if (typeof result === "object") {
1306
+ return result.error;
1307
+ }
1308
+ return String(result);
1309
+ }
1290
1310
  class SharedStringValue {
1291
1311
  constructor(cell, value) {
1292
1312
  this.model = { address: cell.address, type: Types.SharedString, value };
@@ -1,6 +1,6 @@
1
- import { cellGetValue, cellSetValue, setFacetShared } from "./cell.js";
1
+ import { cellFormula, cellGetValue, cellResult, cellSetValue, setFacetShared } from "./cell.js";
2
2
  import { sharedCellFacet } from "./style-sharing.js";
3
- import { getRow, getSheetWorkbook, rowGetCell } from "./worksheet-core.js";
3
+ import { findCell, getRow, getSheetWorkbook, rowGetCell } from "./worksheet-core.js";
4
4
  import { TableError } from "../errors.js";
5
5
  import { colCache } from "../utils/col-cache.js";
6
6
  function clearStaleSortState(t) {
@@ -107,6 +107,43 @@ const SUBTOTAL_FUNCTIONS = {
107
107
  var: 110,
108
108
  sum: 109
109
109
  };
110
+ /**
111
+ * Carry the totals row's cached results from the loaded sheet into a table model — **for a reader only**.
112
+ *
113
+ * The results are `<v>` values in the sheet's cells; the table part has nowhere to keep them, and
114
+ * {@link createTable} rewrites the totals cells from the model. A reader that does not call this loses
115
+ * every cached total on load.
116
+ *
117
+ * It belongs to the load path and nowhere else. A cached value is evidence about the data the *file*
118
+ * held; a table a caller builds with `Table.add` brings its own rows, and an old result sitting in the
119
+ * same cell — even under an identical formula — says nothing about them.
120
+ *
121
+ * `range` is the table's full extent (header and totals included). Only a result cached under the exact
122
+ * formula this table writes is taken, and a result the model already carries wins.
123
+ */
124
+ export function adoptLoadedTotals(worksheet, table, range) {
125
+ if (table.totalsRow !== true) {
126
+ return;
127
+ }
128
+ const decoded = colCache.decode(range);
129
+ if (!("dimensions" in decoded)) {
130
+ return;
131
+ }
132
+ const t = { worksheet, table };
133
+ table.columns.forEach((column, j) => {
134
+ if (j === 0 || column.totalsRowResult !== undefined) {
135
+ return;
136
+ }
137
+ const cell = findCell(worksheet, decoded.bottom, decoded.left + j);
138
+ if (!cell) {
139
+ return;
140
+ }
141
+ const result = cellResult(cell);
142
+ if (result !== undefined && cellFormula(cell) === tableGetFormula(t, column)) {
143
+ column.totalsRowResult = result;
144
+ }
145
+ });
146
+ }
110
147
  /** Create a table bound to a worksheet, validating + storing on-sheet if a model is given. */
111
148
  export function createTable(worksheet, table) {
112
149
  const t = { worksheet, table: table };
@@ -173,6 +173,45 @@ export function findCell(ws, r, c) {
173
173
  const row = ws._rows[address.row - 1];
174
174
  return row ? rowFindCell(row, address.col) : undefined;
175
175
  }
176
+ /**
177
+ * The existing cell at a reference, or `undefined` — resolved the way {@link getCell} resolves it, with
178
+ * the same errors for a malformed reference, but **without creating** anything.
179
+ *
180
+ * This is what a public reader resolves through. `getCell` materialises the row and the cell, so a read
181
+ * through it widened `rowCount`, `<dimension>` and, in a styled row or column, wrote a styled empty cell
182
+ * into the saved file (see `core/__tests__/read-side-effects.test.ts`).
183
+ *
184
+ * Validation matches `getCell` exactly, including its leniency: both decode the reference with
185
+ * `colCache.getAddress`, and `getCell` validates the *decoded* address only when it has to create a cell
186
+ * (in `cellCreate`). So this validates only on a miss, and a reference that decodes to an existing cell
187
+ * is accepted by both — strict validation of the raw string would be a separate, wider change. The hit
188
+ * path therefore allocates nothing and checks nothing beyond the decode.
189
+ */
190
+ export function locateCell(ws, r, c) {
191
+ const decoded = colCache.getAddress(r, c);
192
+ const cell = ws._rows[decoded.row - 1]?.cells[decoded.col - 1];
193
+ if (cell === undefined) {
194
+ colCache.validateAddress(decoded.address);
195
+ }
196
+ return cell;
197
+ }
198
+ /**
199
+ * The fully-qualified address of a reference, validated like {@link getCell} — for a reader that answers
200
+ * from the address alone (defined names, validation rules, an absent cell's inherited format).
201
+ */
202
+ export function resolveAddress(ws, r, c) {
203
+ const decoded = colCache.getAddress(r, c);
204
+ colCache.validateAddress(decoded.address);
205
+ return { sheetName: ws._name, address: decoded.address, row: decoded.row, col: decoded.col };
206
+ }
207
+ /**
208
+ * The number format an empty cell at `(row, col)` would inherit if it were
209
+ * created — the row's, else the column's — read without creating the row, the
210
+ * column record or the cell. Mirrors the `numFmt` precedence in `mergeCellStyle`.
211
+ */
212
+ export function inheritedNumFmt(ws, row, col) {
213
+ return ws._rows[row - 1]?.style.numFmt || ws._columns[col - 1]?.style.numFmt || undefined;
214
+ }
176
215
  export function getCell(ws, r, c) {
177
216
  const address = colCache.getAddress(r, c);
178
217
  const row = getRow(ws, address.row);
@@ -3,7 +3,7 @@ import { createDataValidations } from "./data-validations.js";
3
3
  import { formCheckboxFromModel } from "./form-control.js";
4
4
  import { imageCreate } from "./image.js";
5
5
  import { rowCreate } from "./row.js";
6
- import { createTable, tableSetModel } from "./table.js";
6
+ import { adoptLoadedTotals, createTable, tableSetModel } from "./table.js";
7
7
  import { mergeCellsWithoutStyle, setSheetName } from "./worksheet.js";
8
8
  import { columnFromModel, getSheetName, getSheetWorkbook, rowSetModel } from "./worksheet-core.js";
9
9
  // Type for data validation model - maps address to validation
@@ -69,6 +69,8 @@ export function setSheetModel(ws, value) {
69
69
  }
70
70
  ws.sheetProtection = value.sheetProtection;
71
71
  ws.tables = value.tables.reduce((tables, table) => {
72
+ // The cells were applied above, so the totals row's cached results are there to carry over.
73
+ adoptLoadedTotals(ws, table, table.tableRef ?? table.ref);
72
74
  const t = createTable(ws, table);
73
75
  tableSetModel(t, table);
74
76
  tables[table.name] = t;
@@ -8,6 +8,7 @@
8
8
  * Node.js uses `workbook-reader.ts`, which extends the same base implementation
9
9
  * with filesystem-specific features (filename input + temp-file buffering).
10
10
  */
11
+ import { FileTooLargeError } from "../../archive/core/errors.js";
11
12
  import { createParse } from "#platform/modules/archive/unzip/stream";
12
13
  import { ExcelFileError } from "../errors.js";
13
14
  import { HyperlinkReader } from "./hyperlink-reader.js";
@@ -72,10 +73,30 @@ import { MetadataXform } from "../xlsx/xform/core/metadata-xform.js";
72
73
  import { RelationshipsXform } from "../xlsx/xform/core/relationships-xform.js";
73
74
  import { parseXformStream } from "../xlsx/xform/parse-xform.js";
74
75
  import { StylesXform } from "../xlsx/xform/style/styles-xform.js";
75
- import { Readable } from "#platform/modules/stream/index";
76
+ import { Readable, Transform } from "#platform/modules/stream/index";
76
77
  import { EventEmitter } from "../../../utils/event-emitter.js";
78
+ import { assertLimitOption } from "../../../utils/limits.js";
77
79
  import { decodeOoxmlEscape } from "#platform/utils/utils";
78
80
  import { SaxParser, saxStream } from "../../xml/sax.js";
81
+ /**
82
+ * `entry`'s bytes, failing with `FileTooLargeError` as soon as more than
83
+ * `limit` arrive — before a hostile part can be held in memory.
84
+ */
85
+ function boundedPart(entry, limit) {
86
+ let seen = 0;
87
+ const bounded = new Transform({
88
+ transform(chunk, _encoding, callback) {
89
+ seen += chunk.length;
90
+ if (seen > limit) {
91
+ callback(new FileTooLargeError(entry.path, `holds more than ${limit} bytes (maxEntrySize)`));
92
+ return;
93
+ }
94
+ callback(null, chunk);
95
+ }
96
+ });
97
+ entry.on("error", (err) => bounded.destroy(err));
98
+ return entry.pipe(bounded);
99
+ }
79
100
  // ============================================================================
80
101
  // Base Class
81
102
  // ============================================================================
@@ -99,6 +120,8 @@ export class WorkbookReaderBase extends EventEmitter {
99
120
  this.WorksheetReaderClass = WorksheetReaderClass;
100
121
  this.HyperlinkReaderClass = HyperlinkReaderClass;
101
122
  this._maxBufferedBytes = options.maxBufferedWorksheetBytes ?? 256 * 1024 * 1024;
123
+ assertLimitOption("maxEntrySize", options.maxEntrySize);
124
+ this._maxEntrySize = options.maxEntrySize ?? 512 * 1024 * 1024;
102
125
  this.options = {
103
126
  worksheets: "emit",
104
127
  sharedStrings: "cache",
@@ -664,21 +687,21 @@ export class WorkbookReaderBase extends EventEmitter {
664
687
  case OOXML_PATHS.rootRels:
665
688
  break;
666
689
  case OOXML_PATHS.xlWorkbookRels:
667
- await this._parseRels(entry);
690
+ await this._parseRels(boundedPart(entry, this._maxEntrySize));
668
691
  break;
669
692
  case OOXML_PATHS.xlWorkbook:
670
- await this._parseWorkbook(entry);
693
+ await this._parseWorkbook(boundedPart(entry, this._maxEntrySize));
671
694
  break;
672
695
  case OOXML_PATHS.xlSharedStrings:
673
- for await (const item of this._parseSharedStrings(entry)) {
696
+ for await (const item of this._parseSharedStrings(boundedPart(entry, this._maxEntrySize))) {
674
697
  yield { eventType: "shared-strings", value: item };
675
698
  }
676
699
  break;
677
700
  case OOXML_PATHS.xlStyles:
678
- await this._parseStyles(entry);
701
+ await this._parseStyles(boundedPart(entry, this._maxEntrySize));
679
702
  break;
680
703
  case OOXML_PATHS.xlMetadata:
681
- await this._parseMetadata(entry);
704
+ await this._parseMetadata(boundedPart(entry, this._maxEntrySize));
682
705
  break;
683
706
  // **The binary package's three prerequisite parts.**
684
707
  //
@@ -688,18 +711,18 @@ export class WorkbookReaderBase extends EventEmitter {
688
711
  // problem in both containers, and were already solved here for one of them.
689
712
  default:
690
713
  if (normalizedPath.toLowerCase() === "xl/_rels/workbook.bin.rels") {
691
- await this._parseRels(entry);
714
+ await this._parseRels(boundedPart(entry, this._maxEntrySize));
692
715
  continue;
693
716
  }
694
717
  switch (xlsbPartKind(normalizedPath)) {
695
718
  case "workbook":
696
- await this._parseXlsbWorkbook(entry);
719
+ await this._parseXlsbWorkbook(boundedPart(entry, this._maxEntrySize));
697
720
  continue;
698
721
  case "sharedStrings":
699
- await this._parseXlsbSharedStrings(entry);
722
+ await this._parseXlsbSharedStrings(boundedPart(entry, this._maxEntrySize));
700
723
  continue;
701
724
  case "styles":
702
- await this._parseXlsbStyles(entry);
725
+ await this._parseXlsbStyles(boundedPart(entry, this._maxEntrySize));
703
726
  continue;
704
727
  default:
705
728
  break;
@@ -735,7 +758,7 @@ export class WorkbookReaderBase extends EventEmitter {
735
758
  }
736
759
  sheetNo = getWorksheetNoFromWorksheetRelsPath(normalizedPath)?.toString();
737
760
  if (sheetNo) {
738
- yield* this._parseHyperlinks(iterateStream(entry), sheetNo);
761
+ yield* this._parseHyperlinks(iterateStream(boundedPart(entry, this._maxEntrySize)), sheetNo);
739
762
  continue;
740
763
  }
741
764
  break;
@@ -1,6 +1,9 @@
1
- import { cellAddName, cellAlignment, cellDateKind, cellGetDateParts, cellGetTemporal, cellSetDateParts, cellBorder, cellComment, cellDataValidation, cellDisplayText, cellEffectiveType, cellFill, cellFont, cellFormula, cellFullAddress, cellGetModel, cellOwnStyle, cellGetValue, cellHyperlink, cellIsMerged, cellMaster, cellNames, cellNote, cellNumFmt, cellProtection, cellRemoveAllNames, cellRemoveName, cellResult, cellSetAlignment, cellSetBorder, cellSetComment, cellSetDataValidation, cellSetFill, cellSetFont, cellSetModel, cellSetName, cellSetNames, cellSetNote, cellSetNumFmt, cellSetProtection, cellSetResult, cellSetStyle, cellSetValue, cellText, cellType, cellView } from "../core/cell.js";
1
+ import { cellAddName, cellAlignment, cellDateKind, cellGetDateParts, cellGetTemporal, cellSetDateParts, cellBorder, cellComment, cellDisplayText, cellEffectiveType, cellFill, cellFont, cellFormula, cellGetModel, cellOwnStyle, cellGetValue, cellHyperlink, cellIsMerged, cellMaster, cellNote, cellNumFmt, cellProtection, cellRemoveAllNames, cellRemoveName, cellResult, cellSetAlignment, cellSetBorder, cellSetComment, cellSetDataValidation, cellSetFill, cellSetFont, cellSetModel, cellSetName, cellSetNames, cellSetNote, cellSetNumFmt, cellSetProtection, cellSetResult, cellSetStyle, cellSetValue, cellText, cellType, cellView } from "../core/cell.js";
2
+ import { dataValidationFind } from "../core/data-validations.js";
3
+ import { definedNamesGetNamesEx } from "../core/defined-names.js";
4
+ import { ValueType } from "../core/enums.js";
2
5
  import { getCellStyle } from "../core/workbook-core.js";
3
- import { findCell, getCell, getSheetWorkbook } from "../core/worksheet-core.js";
6
+ import { getCell, getSheetWorkbook, inheritedNumFmt, locateCell, resolveAddress } from "../core/worksheet-core.js";
4
7
  import { ExcelError } from "../errors.js";
5
8
  /**
6
9
  * Resolve the `(ws, addr)` / `(ws, row, col)` reader overload pair.
@@ -14,6 +17,23 @@ import { ExcelError } from "../errors.js";
14
17
  function target(ws, addr, col, argc) {
15
18
  return argc >= 3 ? getCell(ws, addr, col) : getCell(ws, addr);
16
19
  }
20
+ /**
21
+ * {@link target} for a reader that answers with a value: resolves the same overload pair and fails on the
22
+ * same malformed references, but never creates the row or the cell — `undefined` when nothing is there,
23
+ * and the reader answers as for an empty cell.
24
+ *
25
+ * A read must not change what the workbook saves — materialising widened `rowCount` and `<dimension>`,
26
+ * and in a styled row or column wrote a styled empty cell into the file. The style-facet readers are the
27
+ * deliberate exception: they hand back the live facet object for the caller to edit in place
28
+ * (`Cell.getStyle(ws, "B2").font = …`), and there is nothing to edit until the cell exists.
29
+ */
30
+ function peek(ws, addr, col, argc) {
31
+ return argc >= 3 ? locateCell(ws, addr, col) : locateCell(ws, addr);
32
+ }
33
+ /** The overload pair resolved to an address, for a reader that answers from the address alone. */
34
+ function addressOf(ws, addr, col, argc) {
35
+ return argc >= 3 ? resolveAddress(ws, addr, col) : resolveAddress(ws, addr);
36
+ }
17
37
  /**
18
38
  * {@link target}, for a reader that hands a *style facet* back to the caller.
19
39
  *
@@ -40,14 +60,16 @@ function targetWithValue(ws, addr, valueOrCol, value, argc) {
40
60
  : [getCell(ws, addr), valueOrCol];
41
61
  }
42
62
  export function getValue(ws, addr, col) {
43
- return cellGetValue(target(ws, addr, col, arguments.length));
63
+ const cell = peek(ws, addr, col, arguments.length);
64
+ return cell ? cellGetValue(cell) : null;
44
65
  }
45
66
  export function setValue(ws, addr, valueOrCol, value) {
46
67
  const [cell, resolved] = targetWithValue(ws, addr, valueOrCol, value, arguments.length);
47
68
  cellSetValue(cell, resolved);
48
69
  }
49
70
  export function getDateParts(ws, addr, col) {
50
- return cellGetDateParts(target(ws, addr, col, arguments.length));
71
+ const cell = peek(ws, addr, col, arguments.length);
72
+ return cell ? cellGetDateParts(cell) : undefined;
51
73
  }
52
74
  export function setDateParts(ws, addr, partsOrCol, partsOrKind, kind) {
53
75
  if (typeof partsOrCol === "number") {
@@ -57,30 +79,36 @@ export function setDateParts(ws, addr, partsOrCol, partsOrKind, kind) {
57
79
  cellSetDateParts(getCell(ws, addr), partsOrCol, partsOrKind);
58
80
  }
59
81
  export function getDateKind(ws, addr, col) {
60
- return cellDateKind(target(ws, addr, col, arguments.length));
82
+ const cell = peek(ws, addr, col, arguments.length);
83
+ return cell ? cellDateKind(cell) : undefined;
61
84
  }
62
85
  export function getTemporal(ws, addr, colOrKind, kind) {
63
- return typeof colOrKind === "number"
64
- ? cellGetTemporal(getCell(ws, addr, colOrKind), kind)
65
- : cellGetTemporal(getCell(ws, addr), colOrKind);
86
+ const cell = typeof colOrKind === "number" ? locateCell(ws, addr, colOrKind) : locateCell(ws, addr);
87
+ return cell ? cellGetTemporal(cell, typeof colOrKind === "number" ? kind : colOrKind) : undefined;
66
88
  }
67
89
  export function getText(ws, addr, col) {
68
- return cellText(target(ws, addr, col, arguments.length));
90
+ const cell = peek(ws, addr, col, arguments.length);
91
+ return cell ? cellText(cell) : "";
69
92
  }
70
93
  export function getDisplayText(ws, addr, col) {
71
- return cellDisplayText(target(ws, addr, col, arguments.length));
94
+ const cell = peek(ws, addr, col, arguments.length);
95
+ return cell ? cellDisplayText(cell) : "";
72
96
  }
73
97
  export function getType(ws, addr, col) {
74
- return cellType(target(ws, addr, col, arguments.length));
98
+ const cell = peek(ws, addr, col, arguments.length);
99
+ return cell ? cellType(cell) : ValueType.Null;
75
100
  }
76
101
  export function getEffectiveType(ws, addr, col) {
77
- return cellEffectiveType(target(ws, addr, col, arguments.length));
102
+ const cell = peek(ws, addr, col, arguments.length);
103
+ return cell ? cellEffectiveType(cell) : ValueType.Null;
78
104
  }
79
105
  export function getFormula(ws, addr, col) {
80
- return cellFormula(target(ws, addr, col, arguments.length));
106
+ const cell = peek(ws, addr, col, arguments.length);
107
+ return cell ? cellFormula(cell) : undefined;
81
108
  }
82
109
  export function getResult(ws, addr, col) {
83
- return cellResult(target(ws, addr, col, arguments.length));
110
+ const cell = peek(ws, addr, col, arguments.length);
111
+ return cell ? cellResult(cell) : undefined;
84
112
  }
85
113
  export function getStyle(ws, addr, col) {
86
114
  return cellOwnStyle(target(ws, addr, col, arguments.length));
@@ -101,23 +129,28 @@ export function applyCellStyle(ws, addr, nameOrCol, name) {
101
129
  cellSetStyle(getCell(ws, addr), { styleName });
102
130
  }
103
131
  export function isMerged(ws, addr, col) {
104
- return cellIsMerged(target(ws, addr, col, arguments.length));
132
+ const cell = peek(ws, addr, col, arguments.length);
133
+ return cell ? cellIsMerged(cell) : false;
105
134
  }
106
135
  export function getMergeMaster(ws, addr, col) {
107
136
  return cellMaster(target(ws, addr, col, arguments.length));
108
137
  }
109
138
  export function getHyperlink(ws, addr, col) {
110
- return cellHyperlink(target(ws, addr, col, arguments.length));
139
+ const cell = peek(ws, addr, col, arguments.length);
140
+ return cell ? cellHyperlink(cell) : undefined;
111
141
  }
112
142
  export function getNote(ws, addr, col) {
113
- return cellNote(target(ws, addr, col, arguments.length));
143
+ const cell = peek(ws, addr, col, arguments.length);
144
+ return cell ? cellNote(cell) : undefined;
114
145
  }
115
146
  export function setNote(ws, addr, noteOrCol, note) {
116
147
  const [cell, resolved] = targetWithValue(ws, addr, noteOrCol, note, arguments.length);
117
148
  cellSetNote(cell, resolved);
118
149
  }
119
150
  export function getNames(ws, addr, col) {
120
- return cellNames(target(ws, addr, col, arguments.length));
151
+ // Defined names live on the workbook, keyed by address; the cell itself holds none.
152
+ const address = addressOf(ws, addr, col, arguments.length);
153
+ return definedNamesGetNamesEx(getSheetWorkbook(ws)._definedNames, address);
121
154
  }
122
155
  export function addName(ws, addr, nameOrCol, name) {
123
156
  const [cell, resolved] = targetWithValue(ws, addr, nameOrCol, name, arguments.length);
@@ -139,7 +172,9 @@ export function removeAllNames(ws, addr, col) {
139
172
  cellRemoveAllNames(target(ws, addr, col, arguments.length));
140
173
  }
141
174
  export function getValidation(ws, addr, col) {
142
- return cellDataValidation(target(ws, addr, col, arguments.length));
175
+ // Validation rules live on the sheet, keyed by address; the cell itself holds none.
176
+ const address = addressOf(ws, addr, col, arguments.length);
177
+ return dataValidationFind(ws.dataValidations, address.address);
143
178
  }
144
179
  export function setValidation(ws, addr, valueOrCol, value) {
145
180
  const [cell, resolved] = targetWithValue(ws, addr, valueOrCol, value, arguments.length);
@@ -160,7 +195,12 @@ export function setFont(ws, addr, valueOrCol, value) {
160
195
  cellSetFont(cell, resolved);
161
196
  }
162
197
  export function getNumFmt(ws, addr, col) {
163
- return cellNumFmt(target(ws, addr, col, arguments.length));
198
+ const cell = peek(ws, addr, col, arguments.length);
199
+ if (cell) {
200
+ return cellNumFmt(cell);
201
+ }
202
+ const { row, col: column } = addressOf(ws, addr, col, arguments.length);
203
+ return inheritedNumFmt(ws, row, column);
164
204
  }
165
205
  export function setNumFmt(ws, addr, valueOrCol, value) {
166
206
  const [cell, resolved] = targetWithValue(ws, addr, valueOrCol, value, arguments.length);
@@ -195,7 +235,8 @@ export function setProtection(ws, addr, valueOrCol, value) {
195
235
  cellSetProtection(cell, resolved);
196
236
  }
197
237
  export function getComment(ws, addr, col) {
198
- return cellComment(target(ws, addr, col, arguments.length));
238
+ const cell = peek(ws, addr, col, arguments.length);
239
+ return cell ? cellComment(cell) : undefined;
199
240
  }
200
241
  export function setComment(ws, addr, commentOrCol, comment) {
201
242
  const [cell, resolved] = targetWithValue(ws, addr, commentOrCol, comment, arguments.length);
@@ -206,7 +247,7 @@ export function setResult(ws, addr, valueOrCol, value) {
206
247
  cellSetResult(cell, resolved);
207
248
  }
208
249
  export function getFullAddress(ws, addr, col) {
209
- return cellFullAddress(target(ws, addr, col, arguments.length));
250
+ return addressOf(ws, addr, col, arguments.length);
210
251
  }
211
252
  export function find(ws, addr, col) {
212
253
  // Same `argc` discipline as `target`: which overload was called is decided by
@@ -215,7 +256,7 @@ export function find(ws, addr, col) {
215
256
  // `(ws, "A1", 99)`; a JavaScript caller that gets past them lands on the
216
257
  // (row, col) path and resolves some other address, exactly as every other
217
258
  // reader in this namespace does.
218
- return arguments.length >= 3 ? findCell(ws, addr, col) : findCell(ws, addr);
259
+ return peek(ws, addr, col, arguments.length);
219
260
  }
220
261
  /**
221
262
  * Read a cell **handle** — the `CellData` handed to `Row.eachCell` /
@@ -1030,8 +1030,9 @@ export function getCellDisplayText(cell, dateFormat) {
1030
1030
  typeof value === "string") {
1031
1031
  return formatCellValue(value, fmt, dateFormat);
1032
1032
  }
1033
- // Formula type — use the result value
1034
- if (typeof value === "object" && "formula" in value) {
1033
+ // Formula type — use the result value. A shared-formula clone carries `sharedFormula` and no `formula`,
1034
+ // and is formatted exactly like the cell that defines it.
1035
+ if (typeof value === "object" && ("formula" in value || "sharedFormula" in value)) {
1035
1036
  const result = value.result;
1036
1037
  if (result == null) {
1037
1038
  return "";
@@ -21,7 +21,7 @@ import { dataValidationAdd } from "../../core/data-validations.js";
21
21
  import { definedNamesAdd } from "../../core/defined-names.js";
22
22
  import { loadWorkbookModel } from "../../core/model-load.js";
23
23
  import { applyPrintName, isPrintName } from "../../core/print-names.js";
24
- import { createTable, tableSetModel } from "../../core/table.js";
24
+ import { adoptLoadedTotals, createTable, tableSetModel } from "../../core/table.js";
25
25
  import { addWorkbookImage, defineCellStyle, getDefinedNames, getWorksheets } from "../../core/workbook-core.js";
26
26
  import { addWorksheet, createWorkbook, getWorkbookModel } from "../../core/workbook.browser.js";
27
27
  import { addBackgroundImage, mergeCells, getColumn as worksheetColumn, getRow as worksheetRow, setHeaderFooterImage } from "../../core/worksheet.js";
@@ -734,6 +734,8 @@ async function readInto(workbook, bytes, source, options) {
734
734
  // the workbook so a second table cannot take it. Assigning the array put an array where a record
735
735
  // belonged and `setModel` then threw reading `.name` off an index.
736
736
  for (const model of found) {
737
+ // `ref` is the full range here — the XLSB reader has no separate `tableRef`.
738
+ adoptLoadedTotals(worksheet, model, model.tableRef ?? model.ref);
737
739
  const handle = createTable(worksheet, model);
738
740
  tableSetModel(handle, model);
739
741
  worksheet.tables[model.name] = handle;
@@ -98,11 +98,27 @@ export async function readXlsxInto(workbook, data, options) {
98
98
  }
99
99
  return readXlsxBytesInto(workbook, buffer, options);
100
100
  }
101
+ /**
102
+ * Document-level container defaults. The archive library leaves both bounds
103
+ * unbounded because what a process can afford is host-dependent; a workbook is
104
+ * a narrower thing. A part count of 10,000 mirrors the Word reader's
105
+ * `maxPartCount` and is far above real workbooks (each sheet adds ~2–6 parts:
106
+ * sheet XML, rels, drawing, comments, table), which reach the low hundreds.
107
+ * 2 GiB total sits above what Excel itself can open — sheet XML compresses
108
+ * ~10:1, so this admits a ~200 MB `.xlsx` — while rejecting the many-entries
109
+ * bombs a per-entry bound alone lets through. Pass `Infinity` to lift either.
110
+ */
111
+ const XLSX_DEFAULT_MAX_ENTRIES = 10000;
112
+ const XLSX_DEFAULT_MAX_TOTAL_UNCOMPRESSED_SIZE = 2 * 1024 * 1024 * 1024;
101
113
  /**
102
114
  * Internal: Load from Uint8Array buffer
103
115
  */
104
116
  export async function readXlsxBytesInto(workbook, buffer, options) {
105
- const parser = new ZipParser(buffer);
117
+ const parser = new ZipParser(buffer, {
118
+ ...options?.zip,
119
+ maxEntries: options?.zip?.maxEntries ?? XLSX_DEFAULT_MAX_ENTRIES,
120
+ maxTotalUncompressedSize: options?.zip?.maxTotalUncompressedSize ?? XLSX_DEFAULT_MAX_TOTAL_UNCOMPRESSED_SIZE
121
+ });
106
122
  const filesMap = await parser.extractAll();
107
123
  // Convert Map to Record for readXlsxFilesInto
108
124
  const allFiles = {};
@@ -1382,7 +1382,9 @@ export class PdfDocumentBuilder {
1382
1382
  if (snapshot.metadata.title ||
1383
1383
  snapshot.metadata.author ||
1384
1384
  snapshot.metadata.subject ||
1385
- snapshot.metadata.creator) {
1385
+ snapshot.metadata.creator ||
1386
+ snapshot.metadata.creationDate ||
1387
+ snapshot.metadata.modDate) {
1386
1388
  writer.addInfoDict(snapshot.metadata);
1387
1389
  }
1388
1390
  // Encryption