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
@@ -0,0 +1,241 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("../biff/record-types.cjs");
3
+ const require_biff_cursor = require("../biff/cursor.cjs");
4
+ const require_biff_builder = require("../biff/builder.cjs");
5
+ const require_biff_record_writer = require("../biff/record-writer.cjs");
6
+ //#region src/workbook/print-names.ts
7
+ /** The built-in name index a Print_Area Lbl carries in place of a name ([MS-XLS] 2.4.150's own built-in name table). */
8
+ const BUILTIN_NAME_PRINT_AREA = 6;
9
+ /** The same table's Print_Titles entry. */
10
+ const BUILTIN_NAME_PRINT_TITLES = 7;
11
+ /** Lbl.grbit's fBuiltin bit -- field F of [MS-XLS] 2.4.150 (fHidden, fFunc, fOB, fProc, fCalcExp, fBuiltin, fGrp (6 bits), reserved1, fPublished, fWorkbookParam, reserved2). */
12
+ const LBL_FLAG_BUILTIN = 32;
13
+ /** The Ptg opcodes a print name's own token stream is built from ([MS-XLS] 2.5.198.25's enumeration). Each reference-class token shares its layout with its value- and array-class spellings, exactly as biff/ptg.ts documents, so all three are accepted. */
14
+ const PTG_UNION = 16;
15
+ const PTG_PAREN = 21;
16
+ const PTG_REF3D_REF = 58;
17
+ const PTG_REF3D_VALUE = 90;
18
+ const PTG_REF3D_ARRAY = 122;
19
+ const PTG_AREA3D_REF = 59;
20
+ const PTG_AREA3D_VALUE = 91;
21
+ const PTG_AREA3D_ARRAY = 123;
22
+ const PTG_MEMAREA_REF = 38;
23
+ const PTG_MEMAREA_VALUE = 70;
24
+ const PTG_MEMAREA_ARRAY = 102;
25
+ const PTG_MEMFUNC_REF = 41;
26
+ const PTG_MEMFUNC_VALUE = 73;
27
+ const PTG_MEMFUNC_ARRAY = 105;
28
+ /** PtgMemArea ([MS-XLS] 2.5.198.70) carries four unused bytes ahead of its own cce, where PtgMemFunc ([MS-XLS] 2.5.198.71) carries none. */
29
+ const PTG_MEMAREA_UNUSED_BYTES = 4;
30
+ /** BIFF8's own grid ceilings, which are also how a Print_Titles band says which axis it repeats: a band covering every column of the sheet is a ROW band, and one covering every row is a COLUMN band. */
31
+ const MAX_ROW_INDEX = 65535;
32
+ const MAX_COLUMN_INDEX = 255;
33
+ /**
34
+ * Reads every Lbl record in a globals substream into per-sheet print names, keyed by ZERO-based sheet index -- Lbl's own itab is one-based, and a sheet index everywhere else in this package is not.
35
+ *
36
+ * A name this reader does not act on leaves nothing behind rather than failing the workbook: a user-defined name (fBuiltin clear), a built-in name other than the two below, a workbook-scoped one (itab 0, which no real producer uses for a print area since a print area belongs to a sheet), and a name whose token stream holds a construct outside the narrow vocabulary parsePrintAreas resolves are all simply not carried. The sheet then reads with no printRange or repeat bands, which is the same answer a sheet that genuinely declares none gives.
37
+ */
38
+ function readPrintNames(records) {
39
+ const bySheet = /* @__PURE__ */ new Map();
40
+ for (const record of records) {
41
+ if (record.type !== 24) continue;
42
+ const label = readLbl(record);
43
+ if (label === void 0) continue;
44
+ const existing = bySheet.get(label.sheetIndex) ?? {};
45
+ if (label.builtinName === BUILTIN_NAME_PRINT_AREA) {
46
+ const range = printRangeOf(label.areas);
47
+ if (range !== void 0) bySheet.set(label.sheetIndex, {
48
+ ...existing,
49
+ printRange: range
50
+ });
51
+ } else bySheet.set(label.sheetIndex, {
52
+ ...existing,
53
+ ...repeatBandsOf(label.areas)
54
+ });
55
+ }
56
+ return bySheet;
57
+ }
58
+ /**
59
+ * Lbl ([MS-XLS] 2.4.150): a two-byte grbit, chKey, cch, cce, a two-byte reserved3, itab, four reserved bytes, the Name as an XLUnicodeStringNoCch, then cce bytes of rgce.
60
+ *
61
+ * A built-in name's Name field is that string holding exactly one character whose code unit IS the built-in index ([MS-XLS] 2.4.150: "Each built-in name has a zero-based index value associated with it. A built-in name or its index value MUST be used for this field."), so cch is 1 and the character is read rather than the spelled-out name. A built-in name spelled out in full instead is not resolved -- no producer writes one, and inventing a name-string table to match against would be guessing at which spelling and which locale.
62
+ */
63
+ function readLbl(record) {
64
+ const cursor = new require_biff_cursor.BlockCursor(record.blocks);
65
+ if ((cursor.u16() & LBL_FLAG_BUILTIN) === 0) return;
66
+ cursor.skip(1);
67
+ const cch = cursor.u8();
68
+ const cce = cursor.u16();
69
+ cursor.skip(2);
70
+ const itab = cursor.u16();
71
+ cursor.skip(4);
72
+ const highByte = (cursor.u8() & 1) !== 0;
73
+ if (cch !== 1 || highByte) return;
74
+ const builtinName = cursor.u8();
75
+ if (builtinName !== BUILTIN_NAME_PRINT_AREA && builtinName !== BUILTIN_NAME_PRINT_TITLES) return;
76
+ if (itab === 0) return;
77
+ const areas = parsePrintAreas(cursor.take(cce));
78
+ return areas === void 0 ? void 0 : {
79
+ sheetIndex: itab - 1,
80
+ builtinName,
81
+ areas
82
+ };
83
+ }
84
+ /**
85
+ * Walks a print name's own rgce into the areas it names, or returns undefined for a token stream outside this vocabulary.
86
+ *
87
+ * The whole vocabulary is: a 3D area or single-cell reference (the areas themselves), the union operator joining two of them in a Print_Titles that repeats both a row band and a column band, a mem token wrapping that union (a real producer emits one -- LibreOffice writes PtgMemFunc, Excel may write PtgMemArea -- to say the enclosed reference expression is a single reference result), and PtgParen, a pure display token restating parentheses. Nothing else appears in a print name a spreadsheet application produced, and anything else aborts rather than being partially resolved: half a print range is a wrong print range, not a smaller one.
88
+ *
89
+ * A mem token's own cce covers exactly the sub-expression that follows it, which is the rest of this stream in every real case, so it is read past rather than used to bound a nested parse -- the tokens after it are walked by this same loop either way.
90
+ */
91
+ function parsePrintAreas(rgce) {
92
+ const cursor = new require_biff_cursor.BlockCursor([rgce]);
93
+ const areas = [];
94
+ while (cursor.remainingInBlock() > 0) switch (cursor.u8()) {
95
+ case PTG_AREA3D_REF:
96
+ case PTG_AREA3D_VALUE:
97
+ case PTG_AREA3D_ARRAY:
98
+ cursor.skip(2);
99
+ areas.push(readArea(cursor));
100
+ break;
101
+ case PTG_REF3D_REF:
102
+ case PTG_REF3D_VALUE:
103
+ case PTG_REF3D_ARRAY: {
104
+ cursor.skip(2);
105
+ const row = cursor.u16();
106
+ const column = cursor.u16() & 16383;
107
+ areas.push({
108
+ rowFirst: row,
109
+ rowLast: row,
110
+ columnFirst: column,
111
+ columnLast: column
112
+ });
113
+ break;
114
+ }
115
+ case PTG_MEMAREA_REF:
116
+ case PTG_MEMAREA_VALUE:
117
+ case PTG_MEMAREA_ARRAY:
118
+ cursor.skip(PTG_MEMAREA_UNUSED_BYTES);
119
+ cursor.skip(2);
120
+ break;
121
+ case PTG_MEMFUNC_REF:
122
+ case PTG_MEMFUNC_VALUE:
123
+ case PTG_MEMFUNC_ARRAY:
124
+ cursor.skip(2);
125
+ break;
126
+ case PTG_UNION:
127
+ case PTG_PAREN: break;
128
+ default: return;
129
+ }
130
+ return areas;
131
+ }
132
+ /** RgceArea ([MS-XLS] 2.5.198.105), as PtgArea3d carries it: both row bounds, then both ColRelU column fields, whose low 14 bits hold the column index and whose top two bits say whether each coordinate is relative. A print name's coordinates are always absolute in practice, and are read as plain indices either way -- ContentSheetPrintRange has no relative/absolute distinction to carry one into. */
133
+ function readArea(cursor) {
134
+ return {
135
+ rowFirst: cursor.u16(),
136
+ rowLast: cursor.u16(),
137
+ columnFirst: cursor.u16() & 16383,
138
+ columnLast: cursor.u16() & 16383
139
+ };
140
+ }
141
+ /** A Print_Area name's own range. A name declaring several disjoint areas -- legal in BIFF8, and what Excel writes for a multi-area print selection -- yields only the first: ContentSheetPrintSettings.printRange models one rectangle, and merging several into their bounding box would claim cells print that do not. */
142
+ function printRangeOf(areas) {
143
+ const area = areas[0];
144
+ return area === void 0 ? void 0 : {
145
+ startRow: area.rowFirst,
146
+ startColumn: area.columnFirst,
147
+ endRow: area.rowLast,
148
+ endColumn: area.columnLast
149
+ };
150
+ }
151
+ /**
152
+ * A Print_Titles name's own repeated bands, classified by shape.
153
+ *
154
+ * BIFF8 has no field saying which axis a title band repeats along: a repeated row band is written as an area spanning every column of the sheet ($1:$2, columns 0-255), and a repeated column band as one spanning every row ($A:$A, rows 0-65535). The shape IS the discriminant, and an area that spans both axes at once names the whole sheet, which is neither -- so it is left unclassified rather than being assigned to whichever branch happened to be tested first.
155
+ */
156
+ function repeatBandsOf(areas) {
157
+ const bands = {};
158
+ for (const area of areas) {
159
+ const spansEveryRow = area.rowFirst === 0 && area.rowLast === MAX_ROW_INDEX;
160
+ const spansEveryColumn = area.columnFirst === 0 && area.columnLast === MAX_COLUMN_INDEX;
161
+ if (spansEveryColumn && !spansEveryRow) bands.repeatRows = {
162
+ start: area.rowFirst,
163
+ end: area.rowLast
164
+ };
165
+ else if (spansEveryRow && !spansEveryColumn) bands.repeatColumns = {
166
+ start: area.columnFirst,
167
+ end: area.columnLast
168
+ };
169
+ }
170
+ return bands;
171
+ }
172
+ /**
173
+ * Clamps a coordinate into BIFF8's own row/column ceiling. `ContentSheetPrintRange`/`ContentSheetRepeatRange` bound neither a start nor an end coordinate from above, and `writeArea3d` below writes each one into a 16-bit field regardless (`RecordBuilder.u16` masks with `0xffff`), so an out-of-grid coordinate would otherwise silently wrap to a plausible-looking in-grid one -- a print range asked for through row 70000 landing at row 4464, with nothing downstream to notice.
174
+ *
175
+ * Clamped rather than dropped or refused: a print range or a repeated header band is a rectangle whose intent survives shrinking to the grid's own edge -- "print through the bottom of the sheet" is genuinely what an end coordinate past the last row means once the grid is smaller than the caller assumed -- unlike a single-position page break (see sheet-writer.ts's own inGridBreaks, which drops rather than clamps for exactly that distinction).
176
+ */
177
+ function clampToGrid(index, maxIndex) {
178
+ return Math.min(index, maxIndex);
179
+ }
180
+ /** The Print_Area and Print_Titles entries one sheet's print settings need, or an empty list when it declares neither a print range nor a repeated band. */
181
+ function printNameEntriesFor(sheetIndex, ixti, settings) {
182
+ const entries = [];
183
+ if (settings.printRange !== void 0) entries.push({
184
+ sheetIndex,
185
+ ixti,
186
+ builtinName: BUILTIN_NAME_PRINT_AREA,
187
+ areas: [{
188
+ rowFirst: clampToGrid(settings.printRange.startRow, MAX_ROW_INDEX),
189
+ rowLast: clampToGrid(settings.printRange.endRow, MAX_ROW_INDEX),
190
+ columnFirst: clampToGrid(settings.printRange.startColumn, MAX_COLUMN_INDEX),
191
+ columnLast: clampToGrid(settings.printRange.endColumn, MAX_COLUMN_INDEX)
192
+ }]
193
+ });
194
+ const titleAreas = [];
195
+ if (settings.repeatColumns !== void 0) titleAreas.push({
196
+ rowFirst: 0,
197
+ rowLast: MAX_ROW_INDEX,
198
+ columnFirst: clampToGrid(settings.repeatColumns.start, MAX_COLUMN_INDEX),
199
+ columnLast: clampToGrid(settings.repeatColumns.end, MAX_COLUMN_INDEX)
200
+ });
201
+ if (settings.repeatRows !== void 0) titleAreas.push({
202
+ rowFirst: clampToGrid(settings.repeatRows.start, MAX_ROW_INDEX),
203
+ rowLast: clampToGrid(settings.repeatRows.end, MAX_ROW_INDEX),
204
+ columnFirst: 0,
205
+ columnLast: MAX_COLUMN_INDEX
206
+ });
207
+ if (titleAreas.length > 0) entries.push({
208
+ sheetIndex,
209
+ ixti,
210
+ builtinName: BUILTIN_NAME_PRINT_TITLES,
211
+ areas: titleAreas
212
+ });
213
+ return entries;
214
+ }
215
+ /** PtgArea3d ([MS-XLS] 2.5.198.28), reference class: the opcode, the ixti, then an RgceArea. Every coordinate is written absolute (both ColRelU relative bits clear), which is what a print name means -- a print area does not move relative to anything. */
216
+ function writeArea3d(ixti, area) {
217
+ return new require_biff_builder.RecordBuilder().u8(PTG_AREA3D_REF).u16(ixti).u16(area.rowFirst).u16(area.rowLast).u16(area.columnFirst).u16(area.columnLast).build();
218
+ }
219
+ /**
220
+ * One Lbl record ([MS-XLS] 2.4.150) for a built-in print name, the write-side mirror of readLbl above.
221
+ *
222
+ * A single area is written as a bare PtgArea3d. Two are written as [MS-XLS] 2.5.198.71's own mem-area-expression shape -- PtgMemFunc carrying the byte count of what follows, then the two areas and the PtgUnion joining them -- which is what says the pair is one reference result rather than two loose operands, and is byte-for-byte the structure a real LibreOffice-written Print_Titles carries for the same pair.
223
+ */
224
+ function writePrintNameRecord(entry) {
225
+ const areaTokens = entry.areas.map((area) => writeArea3d(entry.ixti, area));
226
+ const expression = new require_biff_builder.RecordBuilder();
227
+ for (const token of areaTokens) expression.bytes(token);
228
+ for (let index = 1; index < areaTokens.length; index += 1) expression.u8(PTG_UNION);
229
+ const expressionBytes = expression.build();
230
+ const rgce = areaTokens.length > 1 ? new require_biff_builder.RecordBuilder().u8(PTG_MEMFUNC_REF).u16(expressionBytes.length).bytes(expressionBytes).build() : expressionBytes;
231
+ const data = new require_biff_builder.RecordBuilder().u16(LBL_FLAG_BUILTIN).u8(0).u8(1).u16(rgce.length).u16(0).u16(entry.sheetIndex + 1).u32(0).u8(0).u8(entry.builtinName).bytes(rgce).build();
232
+ return require_biff_record_writer.writeRecord(24, data);
233
+ }
234
+ /** Every planned print name as its own Lbl record, in the order given. */
235
+ function writePrintNameRecords(entries) {
236
+ return entries.map(writePrintNameRecord);
237
+ }
238
+ //#endregion
239
+ exports.printNameEntriesFor = printNameEntriesFor;
240
+ exports.readPrintNames = readPrintNames;
241
+ exports.writePrintNameRecords = writePrintNameRecords;
@@ -0,0 +1,2 @@
1
+ import { a as writePrintNameRecords, i as readPrintNames, n as SheetPrintNames, r as printNameEntriesFor, t as PrintNamePlanEntry } from "../print-names-D-njuzVw.cjs";
2
+ export { PrintNamePlanEntry, SheetPrintNames, printNameEntriesFor, readPrintNames, writePrintNameRecords };
@@ -0,0 +1,2 @@
1
+ import { a as writePrintNameRecords, i as readPrintNames, n as SheetPrintNames, r as printNameEntriesFor, t as PrintNamePlanEntry } from "../print-names-DUlpVE00.js";
2
+ export { PrintNamePlanEntry, SheetPrintNames, printNameEntriesFor, readPrintNames, writePrintNameRecords };
@@ -0,0 +1,238 @@
1
+ import "../biff/record-types.js";
2
+ import { BlockCursor } from "../biff/cursor.js";
3
+ import { RecordBuilder } from "../biff/builder.js";
4
+ import { writeRecord } from "../biff/record-writer.js";
5
+ //#region src/workbook/print-names.ts
6
+ /** The built-in name index a Print_Area Lbl carries in place of a name ([MS-XLS] 2.4.150's own built-in name table). */
7
+ const BUILTIN_NAME_PRINT_AREA = 6;
8
+ /** The same table's Print_Titles entry. */
9
+ const BUILTIN_NAME_PRINT_TITLES = 7;
10
+ /** Lbl.grbit's fBuiltin bit -- field F of [MS-XLS] 2.4.150 (fHidden, fFunc, fOB, fProc, fCalcExp, fBuiltin, fGrp (6 bits), reserved1, fPublished, fWorkbookParam, reserved2). */
11
+ const LBL_FLAG_BUILTIN = 32;
12
+ /** The Ptg opcodes a print name's own token stream is built from ([MS-XLS] 2.5.198.25's enumeration). Each reference-class token shares its layout with its value- and array-class spellings, exactly as biff/ptg.ts documents, so all three are accepted. */
13
+ const PTG_UNION = 16;
14
+ const PTG_PAREN = 21;
15
+ const PTG_REF3D_REF = 58;
16
+ const PTG_REF3D_VALUE = 90;
17
+ const PTG_REF3D_ARRAY = 122;
18
+ const PTG_AREA3D_REF = 59;
19
+ const PTG_AREA3D_VALUE = 91;
20
+ const PTG_AREA3D_ARRAY = 123;
21
+ const PTG_MEMAREA_REF = 38;
22
+ const PTG_MEMAREA_VALUE = 70;
23
+ const PTG_MEMAREA_ARRAY = 102;
24
+ const PTG_MEMFUNC_REF = 41;
25
+ const PTG_MEMFUNC_VALUE = 73;
26
+ const PTG_MEMFUNC_ARRAY = 105;
27
+ /** PtgMemArea ([MS-XLS] 2.5.198.70) carries four unused bytes ahead of its own cce, where PtgMemFunc ([MS-XLS] 2.5.198.71) carries none. */
28
+ const PTG_MEMAREA_UNUSED_BYTES = 4;
29
+ /** BIFF8's own grid ceilings, which are also how a Print_Titles band says which axis it repeats: a band covering every column of the sheet is a ROW band, and one covering every row is a COLUMN band. */
30
+ const MAX_ROW_INDEX = 65535;
31
+ const MAX_COLUMN_INDEX = 255;
32
+ /**
33
+ * Reads every Lbl record in a globals substream into per-sheet print names, keyed by ZERO-based sheet index -- Lbl's own itab is one-based, and a sheet index everywhere else in this package is not.
34
+ *
35
+ * A name this reader does not act on leaves nothing behind rather than failing the workbook: a user-defined name (fBuiltin clear), a built-in name other than the two below, a workbook-scoped one (itab 0, which no real producer uses for a print area since a print area belongs to a sheet), and a name whose token stream holds a construct outside the narrow vocabulary parsePrintAreas resolves are all simply not carried. The sheet then reads with no printRange or repeat bands, which is the same answer a sheet that genuinely declares none gives.
36
+ */
37
+ function readPrintNames(records) {
38
+ const bySheet = /* @__PURE__ */ new Map();
39
+ for (const record of records) {
40
+ if (record.type !== 24) continue;
41
+ const label = readLbl(record);
42
+ if (label === void 0) continue;
43
+ const existing = bySheet.get(label.sheetIndex) ?? {};
44
+ if (label.builtinName === BUILTIN_NAME_PRINT_AREA) {
45
+ const range = printRangeOf(label.areas);
46
+ if (range !== void 0) bySheet.set(label.sheetIndex, {
47
+ ...existing,
48
+ printRange: range
49
+ });
50
+ } else bySheet.set(label.sheetIndex, {
51
+ ...existing,
52
+ ...repeatBandsOf(label.areas)
53
+ });
54
+ }
55
+ return bySheet;
56
+ }
57
+ /**
58
+ * Lbl ([MS-XLS] 2.4.150): a two-byte grbit, chKey, cch, cce, a two-byte reserved3, itab, four reserved bytes, the Name as an XLUnicodeStringNoCch, then cce bytes of rgce.
59
+ *
60
+ * A built-in name's Name field is that string holding exactly one character whose code unit IS the built-in index ([MS-XLS] 2.4.150: "Each built-in name has a zero-based index value associated with it. A built-in name or its index value MUST be used for this field."), so cch is 1 and the character is read rather than the spelled-out name. A built-in name spelled out in full instead is not resolved -- no producer writes one, and inventing a name-string table to match against would be guessing at which spelling and which locale.
61
+ */
62
+ function readLbl(record) {
63
+ const cursor = new BlockCursor(record.blocks);
64
+ if ((cursor.u16() & LBL_FLAG_BUILTIN) === 0) return;
65
+ cursor.skip(1);
66
+ const cch = cursor.u8();
67
+ const cce = cursor.u16();
68
+ cursor.skip(2);
69
+ const itab = cursor.u16();
70
+ cursor.skip(4);
71
+ const highByte = (cursor.u8() & 1) !== 0;
72
+ if (cch !== 1 || highByte) return;
73
+ const builtinName = cursor.u8();
74
+ if (builtinName !== BUILTIN_NAME_PRINT_AREA && builtinName !== BUILTIN_NAME_PRINT_TITLES) return;
75
+ if (itab === 0) return;
76
+ const areas = parsePrintAreas(cursor.take(cce));
77
+ return areas === void 0 ? void 0 : {
78
+ sheetIndex: itab - 1,
79
+ builtinName,
80
+ areas
81
+ };
82
+ }
83
+ /**
84
+ * Walks a print name's own rgce into the areas it names, or returns undefined for a token stream outside this vocabulary.
85
+ *
86
+ * The whole vocabulary is: a 3D area or single-cell reference (the areas themselves), the union operator joining two of them in a Print_Titles that repeats both a row band and a column band, a mem token wrapping that union (a real producer emits one -- LibreOffice writes PtgMemFunc, Excel may write PtgMemArea -- to say the enclosed reference expression is a single reference result), and PtgParen, a pure display token restating parentheses. Nothing else appears in a print name a spreadsheet application produced, and anything else aborts rather than being partially resolved: half a print range is a wrong print range, not a smaller one.
87
+ *
88
+ * A mem token's own cce covers exactly the sub-expression that follows it, which is the rest of this stream in every real case, so it is read past rather than used to bound a nested parse -- the tokens after it are walked by this same loop either way.
89
+ */
90
+ function parsePrintAreas(rgce) {
91
+ const cursor = new BlockCursor([rgce]);
92
+ const areas = [];
93
+ while (cursor.remainingInBlock() > 0) switch (cursor.u8()) {
94
+ case PTG_AREA3D_REF:
95
+ case PTG_AREA3D_VALUE:
96
+ case PTG_AREA3D_ARRAY:
97
+ cursor.skip(2);
98
+ areas.push(readArea(cursor));
99
+ break;
100
+ case PTG_REF3D_REF:
101
+ case PTG_REF3D_VALUE:
102
+ case PTG_REF3D_ARRAY: {
103
+ cursor.skip(2);
104
+ const row = cursor.u16();
105
+ const column = cursor.u16() & 16383;
106
+ areas.push({
107
+ rowFirst: row,
108
+ rowLast: row,
109
+ columnFirst: column,
110
+ columnLast: column
111
+ });
112
+ break;
113
+ }
114
+ case PTG_MEMAREA_REF:
115
+ case PTG_MEMAREA_VALUE:
116
+ case PTG_MEMAREA_ARRAY:
117
+ cursor.skip(PTG_MEMAREA_UNUSED_BYTES);
118
+ cursor.skip(2);
119
+ break;
120
+ case PTG_MEMFUNC_REF:
121
+ case PTG_MEMFUNC_VALUE:
122
+ case PTG_MEMFUNC_ARRAY:
123
+ cursor.skip(2);
124
+ break;
125
+ case PTG_UNION:
126
+ case PTG_PAREN: break;
127
+ default: return;
128
+ }
129
+ return areas;
130
+ }
131
+ /** RgceArea ([MS-XLS] 2.5.198.105), as PtgArea3d carries it: both row bounds, then both ColRelU column fields, whose low 14 bits hold the column index and whose top two bits say whether each coordinate is relative. A print name's coordinates are always absolute in practice, and are read as plain indices either way -- ContentSheetPrintRange has no relative/absolute distinction to carry one into. */
132
+ function readArea(cursor) {
133
+ return {
134
+ rowFirst: cursor.u16(),
135
+ rowLast: cursor.u16(),
136
+ columnFirst: cursor.u16() & 16383,
137
+ columnLast: cursor.u16() & 16383
138
+ };
139
+ }
140
+ /** A Print_Area name's own range. A name declaring several disjoint areas -- legal in BIFF8, and what Excel writes for a multi-area print selection -- yields only the first: ContentSheetPrintSettings.printRange models one rectangle, and merging several into their bounding box would claim cells print that do not. */
141
+ function printRangeOf(areas) {
142
+ const area = areas[0];
143
+ return area === void 0 ? void 0 : {
144
+ startRow: area.rowFirst,
145
+ startColumn: area.columnFirst,
146
+ endRow: area.rowLast,
147
+ endColumn: area.columnLast
148
+ };
149
+ }
150
+ /**
151
+ * A Print_Titles name's own repeated bands, classified by shape.
152
+ *
153
+ * BIFF8 has no field saying which axis a title band repeats along: a repeated row band is written as an area spanning every column of the sheet ($1:$2, columns 0-255), and a repeated column band as one spanning every row ($A:$A, rows 0-65535). The shape IS the discriminant, and an area that spans both axes at once names the whole sheet, which is neither -- so it is left unclassified rather than being assigned to whichever branch happened to be tested first.
154
+ */
155
+ function repeatBandsOf(areas) {
156
+ const bands = {};
157
+ for (const area of areas) {
158
+ const spansEveryRow = area.rowFirst === 0 && area.rowLast === MAX_ROW_INDEX;
159
+ const spansEveryColumn = area.columnFirst === 0 && area.columnLast === MAX_COLUMN_INDEX;
160
+ if (spansEveryColumn && !spansEveryRow) bands.repeatRows = {
161
+ start: area.rowFirst,
162
+ end: area.rowLast
163
+ };
164
+ else if (spansEveryRow && !spansEveryColumn) bands.repeatColumns = {
165
+ start: area.columnFirst,
166
+ end: area.columnLast
167
+ };
168
+ }
169
+ return bands;
170
+ }
171
+ /**
172
+ * Clamps a coordinate into BIFF8's own row/column ceiling. `ContentSheetPrintRange`/`ContentSheetRepeatRange` bound neither a start nor an end coordinate from above, and `writeArea3d` below writes each one into a 16-bit field regardless (`RecordBuilder.u16` masks with `0xffff`), so an out-of-grid coordinate would otherwise silently wrap to a plausible-looking in-grid one -- a print range asked for through row 70000 landing at row 4464, with nothing downstream to notice.
173
+ *
174
+ * Clamped rather than dropped or refused: a print range or a repeated header band is a rectangle whose intent survives shrinking to the grid's own edge -- "print through the bottom of the sheet" is genuinely what an end coordinate past the last row means once the grid is smaller than the caller assumed -- unlike a single-position page break (see sheet-writer.ts's own inGridBreaks, which drops rather than clamps for exactly that distinction).
175
+ */
176
+ function clampToGrid(index, maxIndex) {
177
+ return Math.min(index, maxIndex);
178
+ }
179
+ /** The Print_Area and Print_Titles entries one sheet's print settings need, or an empty list when it declares neither a print range nor a repeated band. */
180
+ function printNameEntriesFor(sheetIndex, ixti, settings) {
181
+ const entries = [];
182
+ if (settings.printRange !== void 0) entries.push({
183
+ sheetIndex,
184
+ ixti,
185
+ builtinName: BUILTIN_NAME_PRINT_AREA,
186
+ areas: [{
187
+ rowFirst: clampToGrid(settings.printRange.startRow, MAX_ROW_INDEX),
188
+ rowLast: clampToGrid(settings.printRange.endRow, MAX_ROW_INDEX),
189
+ columnFirst: clampToGrid(settings.printRange.startColumn, MAX_COLUMN_INDEX),
190
+ columnLast: clampToGrid(settings.printRange.endColumn, MAX_COLUMN_INDEX)
191
+ }]
192
+ });
193
+ const titleAreas = [];
194
+ if (settings.repeatColumns !== void 0) titleAreas.push({
195
+ rowFirst: 0,
196
+ rowLast: MAX_ROW_INDEX,
197
+ columnFirst: clampToGrid(settings.repeatColumns.start, MAX_COLUMN_INDEX),
198
+ columnLast: clampToGrid(settings.repeatColumns.end, MAX_COLUMN_INDEX)
199
+ });
200
+ if (settings.repeatRows !== void 0) titleAreas.push({
201
+ rowFirst: clampToGrid(settings.repeatRows.start, MAX_ROW_INDEX),
202
+ rowLast: clampToGrid(settings.repeatRows.end, MAX_ROW_INDEX),
203
+ columnFirst: 0,
204
+ columnLast: MAX_COLUMN_INDEX
205
+ });
206
+ if (titleAreas.length > 0) entries.push({
207
+ sheetIndex,
208
+ ixti,
209
+ builtinName: BUILTIN_NAME_PRINT_TITLES,
210
+ areas: titleAreas
211
+ });
212
+ return entries;
213
+ }
214
+ /** PtgArea3d ([MS-XLS] 2.5.198.28), reference class: the opcode, the ixti, then an RgceArea. Every coordinate is written absolute (both ColRelU relative bits clear), which is what a print name means -- a print area does not move relative to anything. */
215
+ function writeArea3d(ixti, area) {
216
+ return new RecordBuilder().u8(PTG_AREA3D_REF).u16(ixti).u16(area.rowFirst).u16(area.rowLast).u16(area.columnFirst).u16(area.columnLast).build();
217
+ }
218
+ /**
219
+ * One Lbl record ([MS-XLS] 2.4.150) for a built-in print name, the write-side mirror of readLbl above.
220
+ *
221
+ * A single area is written as a bare PtgArea3d. Two are written as [MS-XLS] 2.5.198.71's own mem-area-expression shape -- PtgMemFunc carrying the byte count of what follows, then the two areas and the PtgUnion joining them -- which is what says the pair is one reference result rather than two loose operands, and is byte-for-byte the structure a real LibreOffice-written Print_Titles carries for the same pair.
222
+ */
223
+ function writePrintNameRecord(entry) {
224
+ const areaTokens = entry.areas.map((area) => writeArea3d(entry.ixti, area));
225
+ const expression = new RecordBuilder();
226
+ for (const token of areaTokens) expression.bytes(token);
227
+ for (let index = 1; index < areaTokens.length; index += 1) expression.u8(PTG_UNION);
228
+ const expressionBytes = expression.build();
229
+ const rgce = areaTokens.length > 1 ? new RecordBuilder().u8(PTG_MEMFUNC_REF).u16(expressionBytes.length).bytes(expressionBytes).build() : expressionBytes;
230
+ const data = new RecordBuilder().u16(LBL_FLAG_BUILTIN).u8(0).u8(1).u16(rgce.length).u16(0).u16(entry.sheetIndex + 1).u32(0).u8(0).u8(entry.builtinName).bytes(rgce).build();
231
+ return writeRecord(24, data);
232
+ }
233
+ /** Every planned print name as its own Lbl record, in the order given. */
234
+ function writePrintNameRecords(entries) {
235
+ return entries.map(writePrintNameRecord);
236
+ }
237
+ //#endregion
238
+ export { printNameEntriesFor, readPrintNames, writePrintNameRecords };