xls-codec 1.0.2 → 2.0.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 (124) hide show
  1. package/README.md +128 -23
  2. package/dist/biff/bof-writer.cjs +18 -0
  3. package/dist/biff/bof-writer.d.cts +5 -0
  4. package/dist/biff/bof-writer.d.ts +5 -0
  5. package/dist/biff/bof-writer.js +17 -0
  6. package/dist/biff/builder.cjs +48 -0
  7. package/dist/biff/builder.d.cts +17 -0
  8. package/dist/biff/builder.d.ts +17 -0
  9. package/dist/biff/builder.js +47 -0
  10. package/dist/biff/cursor.cjs +4 -0
  11. package/dist/biff/cursor.d.cts +1 -27
  12. package/dist/biff/cursor.d.ts +1 -27
  13. package/dist/biff/cursor.js +4 -0
  14. package/dist/biff/errors.cjs +7 -0
  15. package/dist/biff/errors.d.cts +3 -1
  16. package/dist/biff/errors.d.ts +3 -1
  17. package/dist/biff/errors.js +7 -1
  18. package/dist/biff/print-setup.cjs +108 -0
  19. package/dist/biff/print-setup.d.cts +2 -0
  20. package/dist/biff/print-setup.d.ts +2 -0
  21. package/dist/biff/print-setup.js +103 -0
  22. package/dist/biff/ptg-functions.cjs +383 -0
  23. package/dist/biff/ptg-functions.d.cts +7 -0
  24. package/dist/biff/ptg-functions.d.ts +7 -0
  25. package/dist/biff/ptg-functions.js +381 -0
  26. package/dist/biff/ptg.cjs +319 -0
  27. package/dist/biff/ptg.d.cts +2 -0
  28. package/dist/biff/ptg.d.ts +2 -0
  29. package/dist/biff/ptg.js +318 -0
  30. package/dist/biff/record-types.cjs +60 -0
  31. package/dist/biff/record-types.d.cts +41 -1
  32. package/dist/biff/record-types.d.ts +41 -1
  33. package/dist/biff/record-types.js +41 -1
  34. package/dist/biff/record-writer.cjs +29 -0
  35. package/dist/biff/record-writer.d.cts +7 -0
  36. package/dist/biff/record-writer.d.ts +7 -0
  37. package/dist/biff/record-writer.js +27 -0
  38. package/dist/biff/string-writer.cjs +58 -0
  39. package/dist/biff/string-writer.d.cts +13 -0
  40. package/dist/biff/string-writer.d.ts +13 -0
  41. package/dist/biff/string-writer.js +55 -0
  42. package/dist/biff/strings.d.cts +1 -1
  43. package/dist/biff/strings.d.ts +1 -1
  44. package/dist/biff/substreams.d.cts +1 -20
  45. package/dist/biff/substreams.d.ts +1 -20
  46. package/dist/biff/write-errors.cjs +10 -0
  47. package/dist/biff/write-errors.d.cts +6 -0
  48. package/dist/biff/write-errors.d.ts +6 -0
  49. package/dist/biff/write-errors.js +9 -0
  50. package/dist/biff/xf-colors.cjs +374 -0
  51. package/dist/biff/xf-colors.d.cts +2 -0
  52. package/dist/biff/xf-colors.d.ts +2 -0
  53. package/dist/biff/xf-colors.js +339 -0
  54. package/dist/biff/xf-writer.cjs +96 -0
  55. package/dist/biff/xf-writer.d.cts +29 -0
  56. package/dist/biff/xf-writer.d.ts +29 -0
  57. package/dist/biff/xf-writer.js +90 -0
  58. package/dist/container.cjs +13 -4
  59. package/dist/container.d.cts +10 -3
  60. package/dist/container.d.ts +10 -3
  61. package/dist/container.js +12 -4
  62. package/dist/content.cjs +123 -18
  63. package/dist/content.js +123 -18
  64. package/dist/cursor-VMtw9uVP.d.cts +30 -0
  65. package/dist/cursor-VMtw9uVP.d.ts +30 -0
  66. package/dist/index.cjs +71 -5
  67. package/dist/index.d.cts +20 -10
  68. package/dist/index.d.ts +20 -10
  69. package/dist/index.js +17 -7
  70. package/dist/metadata.cjs +14 -0
  71. package/dist/metadata.d.cts +6 -0
  72. package/dist/metadata.d.ts +6 -0
  73. package/dist/metadata.js +13 -0
  74. package/dist/print-names-D-njuzVw.d.cts +41 -0
  75. package/dist/print-names-DUlpVE00.d.ts +41 -0
  76. package/dist/print-setup-B_ihDvm5.d.cts +57 -0
  77. package/dist/print-setup-B_ihDvm5.d.ts +57 -0
  78. package/dist/ptg-B2K8t3js.d.cts +21 -0
  79. package/dist/ptg-B2K8t3js.d.ts +21 -0
  80. package/dist/serial.cjs +41 -0
  81. package/dist/serial.d.cts +7 -1
  82. package/dist/serial.d.ts +7 -1
  83. package/dist/serial.js +39 -1
  84. package/dist/substreams-D7dQiJbp.d.ts +21 -0
  85. package/dist/substreams-Ddtvn_Vr.d.cts +21 -0
  86. package/dist/units.cjs +37 -0
  87. package/dist/units.d.cts +15 -1
  88. package/dist/units.d.ts +15 -1
  89. package/dist/units.js +33 -1
  90. package/dist/workbook/globals-writer.cjs +122 -0
  91. package/dist/workbook/globals-writer.d.cts +39 -0
  92. package/dist/workbook/globals-writer.d.ts +39 -0
  93. package/dist/workbook/globals-writer.js +120 -0
  94. package/dist/workbook/globals.cjs +72 -8
  95. package/dist/workbook/globals.d.cts +26 -2
  96. package/dist/workbook/globals.d.ts +26 -2
  97. package/dist/workbook/globals.js +71 -7
  98. package/dist/workbook/print-names.cjs +241 -0
  99. package/dist/workbook/print-names.d.cts +2 -0
  100. package/dist/workbook/print-names.d.ts +2 -0
  101. package/dist/workbook/print-names.js +238 -0
  102. package/dist/workbook/sheet-writer.cjs +277 -0
  103. package/dist/workbook/sheet-writer.d.cts +12 -0
  104. package/dist/workbook/sheet-writer.d.ts +12 -0
  105. package/dist/workbook/sheet-writer.js +276 -0
  106. package/dist/workbook/sheet.cjs +125 -17
  107. package/dist/workbook/sheet.d.cts +38 -4
  108. package/dist/workbook/sheet.d.ts +38 -4
  109. package/dist/workbook/sheet.js +125 -17
  110. package/dist/write.cjs +311 -0
  111. package/dist/write.d.cts +13 -0
  112. package/dist/write.d.ts +13 -0
  113. package/dist/write.js +309 -0
  114. package/dist/written-cells.cjs +26 -0
  115. package/dist/written-cells.d.cts +16 -0
  116. package/dist/written-cells.d.ts +16 -0
  117. package/dist/written-cells.js +24 -0
  118. package/dist/xf-colors-CehHZtBy.d.cts +91 -0
  119. package/dist/xf-colors-CpykR3B9.d.ts +91 -0
  120. package/package.json +4 -3
  121. package/dist/number-format.cjs +0 -298
  122. package/dist/number-format.d.cts +0 -32
  123. package/dist/number-format.d.ts +0 -32
  124. package/dist/number-format.js +0 -296
@@ -1,10 +1,17 @@
1
1
  //#region src/container.d.ts
2
+ /** The [MS-OLEPS] Property Set Stream a .xls's title/author/dates live in when present ([MS-OSHARED] 2.3.3.2.2) -- a genuinely optional stream, unlike Workbook, since a valid BIFF8 workbook need not carry document properties at all. */
3
+ declare const SUMMARY_INFORMATION_STREAM = "SummaryInformation";
4
+ interface WorkbookStreams {
5
+ readonly workbook: Uint8Array<ArrayBuffer>;
6
+ /** The raw "\x05SummaryInformation" stream bytes, or undefined when the container carries none. */
7
+ readonly metadata: Uint8Array<ArrayBuffer> | undefined;
8
+ }
2
9
  /**
3
- * Extracts the BIFF8 record stream from a .xls file's compound-file container.
10
+ * Extracts the BIFF8 record stream, and the optional metadata stream beside it, from a .xls file's compound-file container.
4
11
  *
5
12
  * Throws rather than returning undefined for anything that is not a readable BIFF8 workbook: a caller wanting a soft answer asks isXlsFile first.
6
13
  */
7
- declare function readWorkbookStream(bytes: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>;
14
+ declare function readWorkbookStreams(bytes: Uint8Array<ArrayBuffer>): WorkbookStreams;
8
15
  /**
9
16
  * Whether these bytes are a workbook this package can read.
10
17
  *
@@ -12,4 +19,4 @@ declare function readWorkbookStream(bytes: Uint8Array<ArrayBuffer>): Uint8Array<
12
19
  */
13
20
  declare function isXlsFile(bytes: Uint8Array<ArrayBuffer>): boolean;
14
21
  //#endregion
15
- export { isXlsFile, readWorkbookStream };
22
+ export { SUMMARY_INFORMATION_STREAM, WorkbookStreams, isXlsFile, readWorkbookStreams };
@@ -1,10 +1,17 @@
1
1
  //#region src/container.d.ts
2
+ /** The [MS-OLEPS] Property Set Stream a .xls's title/author/dates live in when present ([MS-OSHARED] 2.3.3.2.2) -- a genuinely optional stream, unlike Workbook, since a valid BIFF8 workbook need not carry document properties at all. */
3
+ declare const SUMMARY_INFORMATION_STREAM = "SummaryInformation";
4
+ interface WorkbookStreams {
5
+ readonly workbook: Uint8Array<ArrayBuffer>;
6
+ /** The raw "\x05SummaryInformation" stream bytes, or undefined when the container carries none. */
7
+ readonly metadata: Uint8Array<ArrayBuffer> | undefined;
8
+ }
2
9
  /**
3
- * Extracts the BIFF8 record stream from a .xls file's compound-file container.
10
+ * Extracts the BIFF8 record stream, and the optional metadata stream beside it, from a .xls file's compound-file container.
4
11
  *
5
12
  * Throws rather than returning undefined for anything that is not a readable BIFF8 workbook: a caller wanting a soft answer asks isXlsFile first.
6
13
  */
7
- declare function readWorkbookStream(bytes: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>;
14
+ declare function readWorkbookStreams(bytes: Uint8Array<ArrayBuffer>): WorkbookStreams;
8
15
  /**
9
16
  * Whether these bytes are a workbook this package can read.
10
17
  *
@@ -12,4 +19,4 @@ declare function readWorkbookStream(bytes: Uint8Array<ArrayBuffer>): Uint8Array<
12
19
  */
13
20
  declare function isXlsFile(bytes: Uint8Array<ArrayBuffer>): boolean;
14
21
  //#endregion
15
- export { isXlsFile, readWorkbookStream };
22
+ export { SUMMARY_INFORMATION_STREAM, WorkbookStreams, isXlsFile, readWorkbookStreams };
package/dist/container.js CHANGED
@@ -5,16 +5,24 @@ import { CompoundFileFormatError, isCompoundFile, readCompoundFile } from "archi
5
5
  const WORKBOOK_STREAM = "Workbook";
6
6
  /** BIFF5 and BIFF7 workbooks name their stream "Book" instead. Recognised only to say so in an error, never read: those are different formats record-for-record, not older spellings of this one. */
7
7
  const LEGACY_WORKBOOK_STREAM = "Book";
8
+ /** The [MS-OLEPS] Property Set Stream a .xls's title/author/dates live in when present ([MS-OSHARED] 2.3.3.2.2) -- a genuinely optional stream, unlike Workbook, since a valid BIFF8 workbook need not carry document properties at all. */
9
+ const SUMMARY_INFORMATION_STREAM = "SummaryInformation";
8
10
  /**
9
- * Extracts the BIFF8 record stream from a .xls file's compound-file container.
11
+ * Extracts the BIFF8 record stream, and the optional metadata stream beside it, from a .xls file's compound-file container.
10
12
  *
11
13
  * Throws rather than returning undefined for anything that is not a readable BIFF8 workbook: a caller wanting a soft answer asks isXlsFile first.
12
14
  */
13
- function readWorkbookStream(bytes) {
15
+ function readWorkbookStreams(bytes) {
14
16
  if (!isCompoundFile(bytes)) throw new BiffFormatError("not a compound file: a .xls workbook is a [MS-CFB] container holding a 'Workbook' stream");
15
17
  const streams = readWorkbookContainer(bytes);
16
18
  const workbook = streams.find((stream) => stream.path === WORKBOOK_STREAM);
17
- if (workbook !== void 0) return workbook.bytes;
19
+ if (workbook !== void 0) {
20
+ const metadata = streams.find((stream) => stream.path === SUMMARY_INFORMATION_STREAM);
21
+ return {
22
+ workbook: workbook.bytes,
23
+ metadata: metadata?.bytes
24
+ };
25
+ }
18
26
  if (streams.some((stream) => stream.path === LEGACY_WORKBOOK_STREAM)) throw new BiffFormatError("compound file holds a 'Book' stream rather than a 'Workbook' stream, so it is a BIFF5/BIFF7 workbook; this reader implements BIFF8 only");
19
27
  throw new BiffFormatError("compound file holds no 'Workbook' stream, so it is not a .xls workbook");
20
28
  }
@@ -41,4 +49,4 @@ function isXlsFile(bytes) {
41
49
  }
42
50
  }
43
51
  //#endregion
44
- export { isXlsFile, readWorkbookStream };
52
+ export { SUMMARY_INFORMATION_STREAM, isXlsFile, readWorkbookStreams };
package/dist/content.cjs CHANGED
@@ -2,48 +2,99 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("./biff/record-types.cjs");
3
3
  const require_biff_records = require("./biff/records.cjs");
4
4
  const require_container = require("./container.cjs");
5
+ const require_units = require("./units.cjs");
6
+ const require_biff_print_setup = require("./biff/print-setup.cjs");
5
7
  const require_biff_substreams = require("./biff/substreams.cjs");
6
- const require_number_format = require("./number-format.cjs");
8
+ const require_biff_xf_colors = require("./biff/xf-colors.cjs");
7
9
  const require_serial = require("./serial.cjs");
8
10
  const require_workbook_globals = require("./workbook/globals.cjs");
9
11
  const require_workbook_sheet = require("./workbook/sheet.cjs");
12
+ let archive_codec = require("archive-codec");
10
13
  let document_schema_js = require("document-schema.js");
14
+ let excel_number_format = require("excel-number-format");
11
15
  //#region src/content.ts
12
16
  /** Which BoundSheet8 dt values name a sheet this reader maps. 0x00 is a worksheet or dialog sheet; macro sheets, chart sheets, and VBA modules carry no cell table for ContentSheet to hold. */
13
17
  const SHEET_TYPE_WORKSHEET = 0;
14
18
  /**
15
- * Print settings this package emits rather than reads.
19
+ * Excel's own "Normal" page-setup preset, the per-field fallback for a print setting the file states nothing about.
16
20
  *
17
- * ContentSheetPrintSettings makes pageSize, margins, gridlines, headers, and pageOrder REQUIRED, so a sheet cannot be produced without them, and BIFF8 spreads the real values across the Setup, LeftMargin/RightMargin/TopMargin/BottomMargin, PrintGrid, and PrintRowCol records plus a paper-size code table. None of those is read yet, so these are Excel's own documented "Normal" preset -- the same constants ooxml.js falls back to for an xlsx carrying no pageMargins element -- and they are honest defaults rather than the file's own settings. Reading the real ones is tracked as remaining scope rather than guessed at from unverified field offsets.
21
+ * ContentSheetPrintSettings makes pageSize, margins, gridlines, headers, and pageOrder REQUIRED, while [MS-XLS] 2.1.7.20.6's own PAGESETUP production makes every record behind them optional -- so a sheet whose page setup was never touched genuinely carries no Setup and no margin records, and something has to stand in. These are the values Excel itself calls Normal (top/bottom 0.75in, left/right 0.7in, on Letter paper, gridlines and row/column headers not printed, pages down-then-over), and the identical constants ooxml.js falls back to for an xlsx carrying no pageMargins element -- so the same untouched sheet reads the same either way.
22
+ *
23
+ * Each field falls back independently: a sheet that declares a left margin and nothing else keeps its real left margin and takes the preset for the other three, rather than the whole preset displacing the one value the file actually stated.
18
24
  */
19
- const POINTS_PER_INCH = 72;
20
25
  const DEFAULT_PRINT_SETTINGS = {
21
26
  pageSize: document_schema_js.PAGE_SIZE_LETTER,
22
27
  margins: {
23
- topPt: .75 * POINTS_PER_INCH,
24
- rightPt: .7 * POINTS_PER_INCH,
25
- bottomPt: .75 * POINTS_PER_INCH,
26
- leftPt: .7 * POINTS_PER_INCH
28
+ topPt: require_units.inchesToPoints(.75),
29
+ rightPt: require_units.inchesToPoints(.7),
30
+ bottomPt: require_units.inchesToPoints(.75),
31
+ leftPt: require_units.inchesToPoints(.7)
27
32
  },
28
33
  gridlines: false,
29
34
  headers: false,
30
35
  pageOrder: "downThenOver"
31
36
  };
32
37
  /**
38
+ * The iScale value that says "print at actual size", which is what ContentSheetPrintSettings already means by carrying no scalePercent at all.
39
+ *
40
+ * Setup's own iScale is a mandatory field of a mandatory record, with no spelling for "this sheet declares no scale" -- so an untouched sheet still states 100. Reporting that as an explicit scalePercent would put a field on every sheet of every workbook read, carrying nothing a consumer could act on that its absence does not already say, and would mean a document written with no scale came back with one. The two spellings render identically, so collapsing them onto the absent one is lossless in both directions; a scale that is genuinely anything else is reported exactly as the file states it.
41
+ */
42
+ const ACTUAL_SIZE_SCALE_PERCENT = 100;
43
+ /**
44
+ * The print settings a sheet's own records and its built-in print names state, with the Normal preset filling in what they do not.
45
+ *
46
+ * Two of BIFF8's own conditional rules are honoured rather than flattened. A Setup record whose fNoPls bit is set declares its own paper size and scale undefined ([MS-XLS] 2.4.257: "whether the iPaperSize, iScale, iRes, iVRes, iCopies, fNoOrient, and fPortrait data are undefined and ignored"), so neither is read from it -- the page size falls back to the preset and no scalePercent is reported, rather than a paper code the file itself disowns being resolved into a confident page size. And WsBool's own fFitToPage decides which of Setup's two mutually exclusive scaling fields is live: iFitWidth/iFitHeight when set, iScale when clear. Real producers write both regardless (confirmed against LibreOffice-written BIFF8, which carries iScale=100 alongside a real fit-to-page pair, and a real iScale alongside iFitWidth=iFitHeight=1), so reading both would report a scale and a page count that contradict each other.
47
+ */
48
+ function mapPrintSettings(raw, names) {
49
+ const setup = raw.setup;
50
+ const usable = setup !== void 0 && !setup.noPls;
51
+ const settings = {
52
+ pageSize: (usable ? require_biff_print_setup.pageSizeFromSetup(setup) : void 0) ?? DEFAULT_PRINT_SETTINGS.pageSize,
53
+ margins: {
54
+ topPt: raw.marginsPt.top ?? DEFAULT_PRINT_SETTINGS.margins.topPt,
55
+ rightPt: raw.marginsPt.right ?? DEFAULT_PRINT_SETTINGS.margins.rightPt,
56
+ bottomPt: raw.marginsPt.bottom ?? DEFAULT_PRINT_SETTINGS.margins.bottomPt,
57
+ leftPt: raw.marginsPt.left ?? DEFAULT_PRINT_SETTINGS.margins.leftPt
58
+ },
59
+ gridlines: raw.printGridlines ?? DEFAULT_PRINT_SETTINGS.gridlines,
60
+ headers: raw.printHeaders ?? DEFAULT_PRINT_SETTINGS.headers,
61
+ pageOrder: setup?.leftToRight === true ? "overThenDown" : "downThenOver"
62
+ };
63
+ if (setup !== void 0 && raw.fitToPage === true) {
64
+ if (setup.fitWidth > 0 && setup.fitHeight > 0) settings.fitToPages = {
65
+ width: setup.fitWidth,
66
+ height: setup.fitHeight
67
+ };
68
+ } else if (usable && setup.scalePercent > 0 && setup.scalePercent !== ACTUAL_SIZE_SCALE_PERCENT) settings.scalePercent = setup.scalePercent;
69
+ if (raw.rowBreaks.length > 0 || raw.columnBreaks.length > 0) settings.manualBreaks = {
70
+ rows: [...raw.rowBreaks],
71
+ columns: [...raw.columnBreaks]
72
+ };
73
+ if (names?.printRange !== void 0) settings.printRange = names.printRange;
74
+ if (names?.repeatRows !== void 0) settings.repeatRows = names.repeatRows;
75
+ if (names?.repeatColumns !== void 0) settings.repeatColumns = names.repeatColumns;
76
+ return settings;
77
+ }
78
+ /**
33
79
  * Reads a .xls file's bytes into a ContentDocument.
34
80
  *
35
81
  * The counterpart of ooxml.js's readXlsxContent, producing the same shape from the older format.
36
82
  */
37
83
  function readXlsContent(bytes) {
38
- const substreams = require_biff_substreams.splitSubstreams(require_biff_substreams.groupRecords(require_biff_records.readRecords(require_container.readWorkbookStream(bytes))));
84
+ const { workbook, metadata } = require_container.readWorkbookStreams(bytes);
85
+ const substreams = require_biff_substreams.splitSubstreams(require_biff_substreams.groupRecords(require_biff_records.readRecords(workbook)));
39
86
  const globalsSubstream = substreams[0];
40
87
  if (globalsSubstream === void 0) throw new require_biff_records.BiffFormatError("workbook stream holds no substreams, so it carries no globals substream");
41
88
  if (globalsSubstream.records.some((rec) => rec.type === 47)) throw new require_biff_records.BiffFormatError("workbook is encrypted (its globals substream carries a FilePass record); this reader does not decrypt");
42
89
  const globals = require_workbook_globals.readWorkbookGlobals(globalsSubstream.records);
90
+ const sheets = globals.sheets.map((entry, sheetIndex) => ({
91
+ entry,
92
+ sheetIndex
93
+ })).filter(({ entry }) => entry.sheetType === SHEET_TYPE_WORKSHEET).map(({ entry, sheetIndex }) => readSheet(entry, sheetIndex, substreams, globals));
43
94
  return {
44
95
  kind: "spreadsheet",
45
- metadata: {},
46
- sheets: globals.sheets.filter((entry) => entry.sheetType === SHEET_TYPE_WORKSHEET).map((entry) => readSheet(entry, substreams, globals))
96
+ metadata: metadata === void 0 ? {} : (0, archive_codec.summaryInformationToLayoutMetadata)((0, archive_codec.readSummaryInformation)(metadata)),
97
+ sheets
47
98
  };
48
99
  }
49
100
  /** The tree-form read: readXlsContent composed with the schema's own structural transform, exactly as ooxml.js's readXlsx wraps readXlsxContent. */
@@ -55,21 +106,29 @@ function readXls(bytes) {
55
106
  *
56
107
  * The substream is found by the byte offset BoundSheet8's lbPlyPos names, not by position: the order sheets appear in the workbook (which is BoundSheet8 order, and therefore the order of `globals.sheets`) is not required to match the order their substreams were written in. A sheet whose substream cannot be found still produces a ContentSheet, empty -- losing the sheet entirely would be a worse answer than losing its cells, since its name and position are real information the workbook did state.
57
108
  */
58
- function readSheet(entry, substreams, globals) {
109
+ function readSheet(entry, sheetIndex, substreams, globals) {
59
110
  const substream = substreams.find((candidate) => candidate.offset === entry.bofPosition && candidate.documentType === 16);
60
111
  const raw = substream === void 0 ? {
61
112
  cells: [],
62
113
  rows: [],
63
114
  columns: [],
64
- merges: []
65
- } : require_workbook_sheet.readSheetRecords(substream.records, globals.sharedStrings);
115
+ merges: [],
116
+ print: {
117
+ marginsPt: {},
118
+ rowBreaks: [],
119
+ columnBreaks: []
120
+ }
121
+ } : require_workbook_sheet.readSheetRecords(substream.records, globals.sharedStrings, {
122
+ sheets: globals.sheets,
123
+ sheetRanges: globals.sheetRanges
124
+ });
66
125
  return {
67
126
  name: entry.name,
68
127
  cells: mapCells(raw, globals),
69
128
  columns: mapColumns(raw),
70
129
  rows: mapRows(raw),
71
130
  images: [],
72
- printSettings: DEFAULT_PRINT_SETTINGS
131
+ printSettings: mapPrintSettings(raw.print, globals.printNames.get(sheetIndex))
73
132
  };
74
133
  }
75
134
  function mapRows(raw) {
@@ -107,10 +166,15 @@ function mapCells(raw, globals) {
107
166
  /**
108
167
  * Maps one raw cell, or drops it.
109
168
  *
110
- * A blank cell carrying no merge is dropped: ContentSheet's cell array is documented as sparse, holding only cells with something to show, and a Blank or MulBlank record states formatting this reader does not map yet. Dropping it keeps the array honest rather than filling a sheet with thousands of empty entries -- applyMerges below re-materialises the few blanks that anchor a merged range.
169
+ * A blank cell showing nothing at all is dropped: ContentSheet's cell array is documented as sparse, holding only cells with something to show, and dropping the blanks keeps it honest rather than filling a sheet with thousands of empty entries -- applyMerges below re-materialises the few that anchor a merged range.
170
+ *
171
+ * A Blank or MulBlank record whose own XF carries a background or a border is not that case. Its formatting is the entire reason the record exists -- a producer writes one precisely to say "this cell is empty AND looks like this" -- so it becomes an `empty`-kind cell carrying that decoration, which is also what this package's own writer emits for one.
111
172
  */
112
173
  function mapCell(cell, globals) {
113
- if (cell.value.kind === "blank") return;
174
+ const background = backgroundOf(globals, cell.xfIndex);
175
+ const borders = bordersOf(globals, cell.xfIndex);
176
+ const { alignment, verticalAlignment } = alignmentOf(globals, cell.xfIndex);
177
+ if (cell.value.kind === "blank" && background === void 0 && borders === void 0 && alignment === void 0 && verticalAlignment === void 0) return;
114
178
  const formatCode = require_workbook_globals.formatCodeOf(globals, cell.xfIndex);
115
179
  const value = resolveValue(cell, formatCode, globals.date1904);
116
180
  const mapped = {
@@ -120,9 +184,50 @@ function mapCell(cell, globals) {
120
184
  displayText: displayTextOf(value)
121
185
  };
122
186
  if (formatCode !== void 0) mapped.numberFormatCode = formatCode;
187
+ if (cell.formula !== void 0) mapped.formula = cell.formula;
188
+ if (background !== void 0) mapped.background = background;
189
+ if (borders !== void 0) mapped.borders = borders;
190
+ if (alignment !== void 0) mapped.alignment = alignment;
191
+ if (verticalAlignment !== void 0) mapped.verticalAlignment = verticalAlignment;
123
192
  return mapped;
124
193
  }
125
194
  /**
195
+ * A cell's own resolved fill colour, or undefined for a genuinely unfilled cell AND for every fill pattern beyond solid.
196
+ *
197
+ * A non-solid pattern (the 50%/75%/25% gray shades, the stripe and crosshatch family [MS-XLS]'s FillPattern enumeration also names) is a deliberate, permanent gap rather than an oversight: ContentSheetCell.background models one flat colour, and approximating a striped or crosshatched fill as its own foreground colour alone would misreport what the cell actually shows -- see xls-codec's README, "Cell decoration".
198
+ */
199
+ function backgroundOf(globals, xfIndex) {
200
+ const format = globals.cellFormats[xfIndex];
201
+ if (format === void 0) return;
202
+ return require_biff_xf_colors.resolveFillBackground(format.decoration.fillPattern, format.decoration.fillForegroundIcv, globals.palette);
203
+ }
204
+ /** A cell's own resolved horizontal/vertical alignment -- already the exact Alignment/verticalAlignment members (or undefined) globals.ts's readCellFormat resolved through xf-colors.ts's unpackXfAlignment, so this is a lookup rather than a further resolution step, mirroring backgroundOf/bordersOf's own shape. Both fields undefined for a cell whose XF resolves to no CellFormat at all (an out-of-range xfIndex), matching every other resolveXOf helper's behaviour in that case. */
205
+ function alignmentOf(globals, xfIndex) {
206
+ const format = globals.cellFormats[xfIndex];
207
+ if (format === void 0) return {};
208
+ const result = {};
209
+ if (format.alignment.horizontal !== void 0) result.alignment = format.alignment.horizontal;
210
+ if (format.alignment.vertical !== void 0) result.verticalAlignment = format.alignment.vertical;
211
+ return result;
212
+ }
213
+ /** A cell's own resolved per-side borders, or undefined when none of its four sides carry a border this reader resolves (no border at all, or a reserved/unrecognised BorderStyle token, or a colour this package cannot express as a fixed RGB value -- see xf-colors.ts's own resolveBorderEdge). */
214
+ function bordersOf(globals, xfIndex) {
215
+ const format = globals.cellFormats[xfIndex];
216
+ if (format === void 0) return;
217
+ const { decoration } = format;
218
+ const left = require_biff_xf_colors.resolveBorderEdge(decoration.left, globals.palette);
219
+ const right = require_biff_xf_colors.resolveBorderEdge(decoration.right, globals.palette);
220
+ const top = require_biff_xf_colors.resolveBorderEdge(decoration.top, globals.palette);
221
+ const bottom = require_biff_xf_colors.resolveBorderEdge(decoration.bottom, globals.palette);
222
+ if (left === void 0 && right === void 0 && top === void 0 && bottom === void 0) return;
223
+ const borders = {};
224
+ if (left !== void 0) borders.left = left;
225
+ if (right !== void 0) borders.right = right;
226
+ if (top !== void 0) borders.top = top;
227
+ if (bottom !== void 0) borders.bottom = bottom;
228
+ return borders;
229
+ }
230
+ /**
126
231
  * Resolves a raw value into a ContentCellValue, classifying a number through its own format code.
127
232
  *
128
233
  * This is where BIFF8's lack of temporal and percentage cell types is undone: every date, time, percentage, and currency amount is stored as a bare number, and only the format its XF points at says which. A format naming a date the calendar does not have (the 1900 system's phantom leap day, or a negative serial) degrades to the plain number rather than emitting an invalid ISO string.
@@ -135,7 +240,7 @@ function resolveValue(cell, formatCode, date1904) {
135
240
  kind: "number",
136
241
  value: num
137
242
  };
138
- const format = require_number_format.classifyNumberFormat(formatCode);
243
+ const format = (0, excel_number_format.classifyNumberFormat)(formatCode);
139
244
  switch (format.kind) {
140
245
  case "percentage": return {
141
246
  kind: "percentage",
package/dist/content.js CHANGED
@@ -1,48 +1,99 @@
1
1
  import "./biff/record-types.js";
2
2
  import { BiffFormatError, readRecords } from "./biff/records.js";
3
- import { readWorkbookStream } from "./container.js";
3
+ import { readWorkbookStreams } from "./container.js";
4
+ import { inchesToPoints } from "./units.js";
5
+ import { pageSizeFromSetup } from "./biff/print-setup.js";
4
6
  import { groupRecords, splitSubstreams } from "./biff/substreams.js";
5
- import { classifyNumberFormat } from "./number-format.js";
7
+ import { resolveBorderEdge, resolveFillBackground } from "./biff/xf-colors.js";
6
8
  import { serialToIsoDate, serialToIsoDateTime, serialToIsoTime } from "./serial.js";
7
9
  import { formatCodeOf, readWorkbookGlobals } from "./workbook/globals.js";
8
10
  import { readSheetRecords } from "./workbook/sheet.js";
11
+ import { readSummaryInformation, summaryInformationToLayoutMetadata } from "archive-codec";
9
12
  import { PAGE_SIZE_LETTER, assembleTree } from "document-schema.js";
13
+ import { classifyNumberFormat } from "excel-number-format";
10
14
  //#region src/content.ts
11
15
  /** Which BoundSheet8 dt values name a sheet this reader maps. 0x00 is a worksheet or dialog sheet; macro sheets, chart sheets, and VBA modules carry no cell table for ContentSheet to hold. */
12
16
  const SHEET_TYPE_WORKSHEET = 0;
13
17
  /**
14
- * Print settings this package emits rather than reads.
18
+ * Excel's own "Normal" page-setup preset, the per-field fallback for a print setting the file states nothing about.
15
19
  *
16
- * ContentSheetPrintSettings makes pageSize, margins, gridlines, headers, and pageOrder REQUIRED, so a sheet cannot be produced without them, and BIFF8 spreads the real values across the Setup, LeftMargin/RightMargin/TopMargin/BottomMargin, PrintGrid, and PrintRowCol records plus a paper-size code table. None of those is read yet, so these are Excel's own documented "Normal" preset -- the same constants ooxml.js falls back to for an xlsx carrying no pageMargins element -- and they are honest defaults rather than the file's own settings. Reading the real ones is tracked as remaining scope rather than guessed at from unverified field offsets.
20
+ * ContentSheetPrintSettings makes pageSize, margins, gridlines, headers, and pageOrder REQUIRED, while [MS-XLS] 2.1.7.20.6's own PAGESETUP production makes every record behind them optional -- so a sheet whose page setup was never touched genuinely carries no Setup and no margin records, and something has to stand in. These are the values Excel itself calls Normal (top/bottom 0.75in, left/right 0.7in, on Letter paper, gridlines and row/column headers not printed, pages down-then-over), and the identical constants ooxml.js falls back to for an xlsx carrying no pageMargins element -- so the same untouched sheet reads the same either way.
21
+ *
22
+ * Each field falls back independently: a sheet that declares a left margin and nothing else keeps its real left margin and takes the preset for the other three, rather than the whole preset displacing the one value the file actually stated.
17
23
  */
18
- const POINTS_PER_INCH = 72;
19
24
  const DEFAULT_PRINT_SETTINGS = {
20
25
  pageSize: PAGE_SIZE_LETTER,
21
26
  margins: {
22
- topPt: .75 * POINTS_PER_INCH,
23
- rightPt: .7 * POINTS_PER_INCH,
24
- bottomPt: .75 * POINTS_PER_INCH,
25
- leftPt: .7 * POINTS_PER_INCH
27
+ topPt: inchesToPoints(.75),
28
+ rightPt: inchesToPoints(.7),
29
+ bottomPt: inchesToPoints(.75),
30
+ leftPt: inchesToPoints(.7)
26
31
  },
27
32
  gridlines: false,
28
33
  headers: false,
29
34
  pageOrder: "downThenOver"
30
35
  };
31
36
  /**
37
+ * The iScale value that says "print at actual size", which is what ContentSheetPrintSettings already means by carrying no scalePercent at all.
38
+ *
39
+ * Setup's own iScale is a mandatory field of a mandatory record, with no spelling for "this sheet declares no scale" -- so an untouched sheet still states 100. Reporting that as an explicit scalePercent would put a field on every sheet of every workbook read, carrying nothing a consumer could act on that its absence does not already say, and would mean a document written with no scale came back with one. The two spellings render identically, so collapsing them onto the absent one is lossless in both directions; a scale that is genuinely anything else is reported exactly as the file states it.
40
+ */
41
+ const ACTUAL_SIZE_SCALE_PERCENT = 100;
42
+ /**
43
+ * The print settings a sheet's own records and its built-in print names state, with the Normal preset filling in what they do not.
44
+ *
45
+ * Two of BIFF8's own conditional rules are honoured rather than flattened. A Setup record whose fNoPls bit is set declares its own paper size and scale undefined ([MS-XLS] 2.4.257: "whether the iPaperSize, iScale, iRes, iVRes, iCopies, fNoOrient, and fPortrait data are undefined and ignored"), so neither is read from it -- the page size falls back to the preset and no scalePercent is reported, rather than a paper code the file itself disowns being resolved into a confident page size. And WsBool's own fFitToPage decides which of Setup's two mutually exclusive scaling fields is live: iFitWidth/iFitHeight when set, iScale when clear. Real producers write both regardless (confirmed against LibreOffice-written BIFF8, which carries iScale=100 alongside a real fit-to-page pair, and a real iScale alongside iFitWidth=iFitHeight=1), so reading both would report a scale and a page count that contradict each other.
46
+ */
47
+ function mapPrintSettings(raw, names) {
48
+ const setup = raw.setup;
49
+ const usable = setup !== void 0 && !setup.noPls;
50
+ const settings = {
51
+ pageSize: (usable ? pageSizeFromSetup(setup) : void 0) ?? DEFAULT_PRINT_SETTINGS.pageSize,
52
+ margins: {
53
+ topPt: raw.marginsPt.top ?? DEFAULT_PRINT_SETTINGS.margins.topPt,
54
+ rightPt: raw.marginsPt.right ?? DEFAULT_PRINT_SETTINGS.margins.rightPt,
55
+ bottomPt: raw.marginsPt.bottom ?? DEFAULT_PRINT_SETTINGS.margins.bottomPt,
56
+ leftPt: raw.marginsPt.left ?? DEFAULT_PRINT_SETTINGS.margins.leftPt
57
+ },
58
+ gridlines: raw.printGridlines ?? DEFAULT_PRINT_SETTINGS.gridlines,
59
+ headers: raw.printHeaders ?? DEFAULT_PRINT_SETTINGS.headers,
60
+ pageOrder: setup?.leftToRight === true ? "overThenDown" : "downThenOver"
61
+ };
62
+ if (setup !== void 0 && raw.fitToPage === true) {
63
+ if (setup.fitWidth > 0 && setup.fitHeight > 0) settings.fitToPages = {
64
+ width: setup.fitWidth,
65
+ height: setup.fitHeight
66
+ };
67
+ } else if (usable && setup.scalePercent > 0 && setup.scalePercent !== ACTUAL_SIZE_SCALE_PERCENT) settings.scalePercent = setup.scalePercent;
68
+ if (raw.rowBreaks.length > 0 || raw.columnBreaks.length > 0) settings.manualBreaks = {
69
+ rows: [...raw.rowBreaks],
70
+ columns: [...raw.columnBreaks]
71
+ };
72
+ if (names?.printRange !== void 0) settings.printRange = names.printRange;
73
+ if (names?.repeatRows !== void 0) settings.repeatRows = names.repeatRows;
74
+ if (names?.repeatColumns !== void 0) settings.repeatColumns = names.repeatColumns;
75
+ return settings;
76
+ }
77
+ /**
32
78
  * Reads a .xls file's bytes into a ContentDocument.
33
79
  *
34
80
  * The counterpart of ooxml.js's readXlsxContent, producing the same shape from the older format.
35
81
  */
36
82
  function readXlsContent(bytes) {
37
- const substreams = splitSubstreams(groupRecords(readRecords(readWorkbookStream(bytes))));
83
+ const { workbook, metadata } = readWorkbookStreams(bytes);
84
+ const substreams = splitSubstreams(groupRecords(readRecords(workbook)));
38
85
  const globalsSubstream = substreams[0];
39
86
  if (globalsSubstream === void 0) throw new BiffFormatError("workbook stream holds no substreams, so it carries no globals substream");
40
87
  if (globalsSubstream.records.some((rec) => rec.type === 47)) throw new BiffFormatError("workbook is encrypted (its globals substream carries a FilePass record); this reader does not decrypt");
41
88
  const globals = readWorkbookGlobals(globalsSubstream.records);
89
+ const sheets = globals.sheets.map((entry, sheetIndex) => ({
90
+ entry,
91
+ sheetIndex
92
+ })).filter(({ entry }) => entry.sheetType === SHEET_TYPE_WORKSHEET).map(({ entry, sheetIndex }) => readSheet(entry, sheetIndex, substreams, globals));
42
93
  return {
43
94
  kind: "spreadsheet",
44
- metadata: {},
45
- sheets: globals.sheets.filter((entry) => entry.sheetType === SHEET_TYPE_WORKSHEET).map((entry) => readSheet(entry, substreams, globals))
95
+ metadata: metadata === void 0 ? {} : summaryInformationToLayoutMetadata(readSummaryInformation(metadata)),
96
+ sheets
46
97
  };
47
98
  }
48
99
  /** The tree-form read: readXlsContent composed with the schema's own structural transform, exactly as ooxml.js's readXlsx wraps readXlsxContent. */
@@ -54,21 +105,29 @@ function readXls(bytes) {
54
105
  *
55
106
  * The substream is found by the byte offset BoundSheet8's lbPlyPos names, not by position: the order sheets appear in the workbook (which is BoundSheet8 order, and therefore the order of `globals.sheets`) is not required to match the order their substreams were written in. A sheet whose substream cannot be found still produces a ContentSheet, empty -- losing the sheet entirely would be a worse answer than losing its cells, since its name and position are real information the workbook did state.
56
107
  */
57
- function readSheet(entry, substreams, globals) {
108
+ function readSheet(entry, sheetIndex, substreams, globals) {
58
109
  const substream = substreams.find((candidate) => candidate.offset === entry.bofPosition && candidate.documentType === 16);
59
110
  const raw = substream === void 0 ? {
60
111
  cells: [],
61
112
  rows: [],
62
113
  columns: [],
63
- merges: []
64
- } : readSheetRecords(substream.records, globals.sharedStrings);
114
+ merges: [],
115
+ print: {
116
+ marginsPt: {},
117
+ rowBreaks: [],
118
+ columnBreaks: []
119
+ }
120
+ } : readSheetRecords(substream.records, globals.sharedStrings, {
121
+ sheets: globals.sheets,
122
+ sheetRanges: globals.sheetRanges
123
+ });
65
124
  return {
66
125
  name: entry.name,
67
126
  cells: mapCells(raw, globals),
68
127
  columns: mapColumns(raw),
69
128
  rows: mapRows(raw),
70
129
  images: [],
71
- printSettings: DEFAULT_PRINT_SETTINGS
130
+ printSettings: mapPrintSettings(raw.print, globals.printNames.get(sheetIndex))
72
131
  };
73
132
  }
74
133
  function mapRows(raw) {
@@ -106,10 +165,15 @@ function mapCells(raw, globals) {
106
165
  /**
107
166
  * Maps one raw cell, or drops it.
108
167
  *
109
- * A blank cell carrying no merge is dropped: ContentSheet's cell array is documented as sparse, holding only cells with something to show, and a Blank or MulBlank record states formatting this reader does not map yet. Dropping it keeps the array honest rather than filling a sheet with thousands of empty entries -- applyMerges below re-materialises the few blanks that anchor a merged range.
168
+ * A blank cell showing nothing at all is dropped: ContentSheet's cell array is documented as sparse, holding only cells with something to show, and dropping the blanks keeps it honest rather than filling a sheet with thousands of empty entries -- applyMerges below re-materialises the few that anchor a merged range.
169
+ *
170
+ * A Blank or MulBlank record whose own XF carries a background or a border is not that case. Its formatting is the entire reason the record exists -- a producer writes one precisely to say "this cell is empty AND looks like this" -- so it becomes an `empty`-kind cell carrying that decoration, which is also what this package's own writer emits for one.
110
171
  */
111
172
  function mapCell(cell, globals) {
112
- if (cell.value.kind === "blank") return;
173
+ const background = backgroundOf(globals, cell.xfIndex);
174
+ const borders = bordersOf(globals, cell.xfIndex);
175
+ const { alignment, verticalAlignment } = alignmentOf(globals, cell.xfIndex);
176
+ if (cell.value.kind === "blank" && background === void 0 && borders === void 0 && alignment === void 0 && verticalAlignment === void 0) return;
113
177
  const formatCode = formatCodeOf(globals, cell.xfIndex);
114
178
  const value = resolveValue(cell, formatCode, globals.date1904);
115
179
  const mapped = {
@@ -119,9 +183,50 @@ function mapCell(cell, globals) {
119
183
  displayText: displayTextOf(value)
120
184
  };
121
185
  if (formatCode !== void 0) mapped.numberFormatCode = formatCode;
186
+ if (cell.formula !== void 0) mapped.formula = cell.formula;
187
+ if (background !== void 0) mapped.background = background;
188
+ if (borders !== void 0) mapped.borders = borders;
189
+ if (alignment !== void 0) mapped.alignment = alignment;
190
+ if (verticalAlignment !== void 0) mapped.verticalAlignment = verticalAlignment;
122
191
  return mapped;
123
192
  }
124
193
  /**
194
+ * A cell's own resolved fill colour, or undefined for a genuinely unfilled cell AND for every fill pattern beyond solid.
195
+ *
196
+ * A non-solid pattern (the 50%/75%/25% gray shades, the stripe and crosshatch family [MS-XLS]'s FillPattern enumeration also names) is a deliberate, permanent gap rather than an oversight: ContentSheetCell.background models one flat colour, and approximating a striped or crosshatched fill as its own foreground colour alone would misreport what the cell actually shows -- see xls-codec's README, "Cell decoration".
197
+ */
198
+ function backgroundOf(globals, xfIndex) {
199
+ const format = globals.cellFormats[xfIndex];
200
+ if (format === void 0) return;
201
+ return resolveFillBackground(format.decoration.fillPattern, format.decoration.fillForegroundIcv, globals.palette);
202
+ }
203
+ /** A cell's own resolved horizontal/vertical alignment -- already the exact Alignment/verticalAlignment members (or undefined) globals.ts's readCellFormat resolved through xf-colors.ts's unpackXfAlignment, so this is a lookup rather than a further resolution step, mirroring backgroundOf/bordersOf's own shape. Both fields undefined for a cell whose XF resolves to no CellFormat at all (an out-of-range xfIndex), matching every other resolveXOf helper's behaviour in that case. */
204
+ function alignmentOf(globals, xfIndex) {
205
+ const format = globals.cellFormats[xfIndex];
206
+ if (format === void 0) return {};
207
+ const result = {};
208
+ if (format.alignment.horizontal !== void 0) result.alignment = format.alignment.horizontal;
209
+ if (format.alignment.vertical !== void 0) result.verticalAlignment = format.alignment.vertical;
210
+ return result;
211
+ }
212
+ /** A cell's own resolved per-side borders, or undefined when none of its four sides carry a border this reader resolves (no border at all, or a reserved/unrecognised BorderStyle token, or a colour this package cannot express as a fixed RGB value -- see xf-colors.ts's own resolveBorderEdge). */
213
+ function bordersOf(globals, xfIndex) {
214
+ const format = globals.cellFormats[xfIndex];
215
+ if (format === void 0) return;
216
+ const { decoration } = format;
217
+ const left = resolveBorderEdge(decoration.left, globals.palette);
218
+ const right = resolveBorderEdge(decoration.right, globals.palette);
219
+ const top = resolveBorderEdge(decoration.top, globals.palette);
220
+ const bottom = resolveBorderEdge(decoration.bottom, globals.palette);
221
+ if (left === void 0 && right === void 0 && top === void 0 && bottom === void 0) return;
222
+ const borders = {};
223
+ if (left !== void 0) borders.left = left;
224
+ if (right !== void 0) borders.right = right;
225
+ if (top !== void 0) borders.top = top;
226
+ if (bottom !== void 0) borders.bottom = bottom;
227
+ return borders;
228
+ }
229
+ /**
125
230
  * Resolves a raw value into a ContentCellValue, classifying a number through its own format code.
126
231
  *
127
232
  * This is where BIFF8's lack of temporal and percentage cell types is undone: every date, time, percentage, and currency amount is stored as a bare number, and only the format its XF points at says which. A format naming a date the calendar does not have (the 1900 system's phantom leap day, or a negative serial) degrades to the plain number rather than emitting an invalid ISO string.
@@ -0,0 +1,30 @@
1
+ //#region src/biff/cursor.d.ts
2
+ declare class BlockCursor {
3
+ private readonly blocks;
4
+ private blockIndex;
5
+ private offset;
6
+ constructor(blocks: readonly Uint8Array<ArrayBuffer>[]);
7
+ /** Advances past any exhausted or empty blocks, so the cursor always rests either on a readable byte or past the end of the last block. A Continue carrying no data is legal and must not read as the end of the record. */
8
+ private settle;
9
+ private nextByte;
10
+ /** Whether any unread byte remains, in this block or a later one. */
11
+ hasMore(): boolean;
12
+ /** How many unread bytes remain in the block the cursor currently rests in -- the distance to the next continuation boundary, which a string's character run must not cross without consuming a new flag byte. */
13
+ remainingInBlock(): number;
14
+ /** The index of the block the cursor currently rests in, so a caller can detect that a read crossed into a new one. */
15
+ blockPosition(): number;
16
+ u8(): number;
17
+ u16(): number;
18
+ u32(): number;
19
+ i32(): number;
20
+ /** A signed 16-bit integer, sign-extended by shifting the raw value out of and back into the low 16 bits -- what XTI's itabFirst/itabLast ([MS-XLS] 2.5.344) and a handful of other structures carry. */
21
+ i16(): number;
22
+ /** An Xnum ([MS-XLS] 2.5.342): a little-endian IEEE 754 double. */
23
+ f64(): number;
24
+ /** The next `count` bytes, copied out. Spans block boundaries. */
25
+ take(count: number): Uint8Array<ArrayBuffer>;
26
+ /** Advances `count` bytes without materialising them -- for a field this package reads past rather than reads. */
27
+ skip(count: number): void;
28
+ }
29
+ //#endregion
30
+ export { BlockCursor as t };
@@ -0,0 +1,30 @@
1
+ //#region src/biff/cursor.d.ts
2
+ declare class BlockCursor {
3
+ private readonly blocks;
4
+ private blockIndex;
5
+ private offset;
6
+ constructor(blocks: readonly Uint8Array<ArrayBuffer>[]);
7
+ /** Advances past any exhausted or empty blocks, so the cursor always rests either on a readable byte or past the end of the last block. A Continue carrying no data is legal and must not read as the end of the record. */
8
+ private settle;
9
+ private nextByte;
10
+ /** Whether any unread byte remains, in this block or a later one. */
11
+ hasMore(): boolean;
12
+ /** How many unread bytes remain in the block the cursor currently rests in -- the distance to the next continuation boundary, which a string's character run must not cross without consuming a new flag byte. */
13
+ remainingInBlock(): number;
14
+ /** The index of the block the cursor currently rests in, so a caller can detect that a read crossed into a new one. */
15
+ blockPosition(): number;
16
+ u8(): number;
17
+ u16(): number;
18
+ u32(): number;
19
+ i32(): number;
20
+ /** A signed 16-bit integer, sign-extended by shifting the raw value out of and back into the low 16 bits -- what XTI's itabFirst/itabLast ([MS-XLS] 2.5.344) and a handful of other structures carry. */
21
+ i16(): number;
22
+ /** An Xnum ([MS-XLS] 2.5.342): a little-endian IEEE 754 double. */
23
+ f64(): number;
24
+ /** The next `count` bytes, copied out. Spans block boundaries. */
25
+ take(count: number): Uint8Array<ArrayBuffer>;
26
+ /** Advances `count` bytes without materialising them -- for a field this package reads past rather than reads. */
27
+ skip(count: number): void;
28
+ }
29
+ //#endregion
30
+ export { BlockCursor as t };