xls-codec 4.12.6 → 4.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/biff/font.cjs +127 -0
- package/dist/biff/font.d.cts +2 -0
- package/dist/biff/font.d.ts +2 -0
- package/dist/biff/font.js +119 -0
- package/dist/biff/ptg-writer.cjs +1 -1
- package/dist/biff/ptg-writer.js +1 -1
- package/dist/biff/string-writer.cjs +1 -1
- package/dist/biff/string-writer.js +1 -1
- package/dist/biff/xf-writer.cjs +1 -22
- package/dist/biff/xf-writer.d.cts +1 -2
- package/dist/biff/xf-writer.d.ts +1 -2
- package/dist/biff/xf-writer.js +2 -22
- package/dist/{conditional-format-12-C3nwy1f8.d.ts → conditional-format-12-BTN59oHJ.d.ts} +2 -2
- package/dist/{conditional-format-12-D65-ruTq.d.cts → conditional-format-12-ZkrPfAP4.d.cts} +2 -2
- package/dist/{conditional-format-Cth_afMy.d.cts → conditional-format-BzTJe8Fm.d.cts} +1 -1
- package/dist/{conditional-format-BRMUTRP1.d.ts → conditional-format-D4U0eLcY.d.ts} +1 -1
- package/dist/content.cjs +52 -6
- package/dist/content.js +52 -6
- package/dist/{data-validation-BgUt8CbE.d.cts → data-validation-CTeIudWM.d.cts} +1 -1
- package/dist/{data-validation-Cq6iD8lB.d.ts → data-validation-Cjdk9imN.d.ts} +1 -1
- package/dist/defined-names-CqBJJmOJ.d.cts +39 -0
- package/dist/defined-names-DJZ27hRp.d.ts +39 -0
- package/dist/font-CBvAGxD8.d.cts +45 -0
- package/dist/font-DcXHYgZ2.d.ts +45 -0
- package/dist/index.cjs +5 -6
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +7 -7
- package/dist/workbook/chart.d.cts +1 -1
- package/dist/workbook/chart.d.ts +1 -1
- package/dist/workbook/comment-writer.cjs +1 -1
- package/dist/workbook/comment-writer.js +1 -1
- package/dist/workbook/conditional-format-12.d.cts +1 -1
- package/dist/workbook/conditional-format-12.d.ts +1 -1
- package/dist/workbook/conditional-format-ex.d.cts +3 -3
- package/dist/workbook/conditional-format-ex.d.ts +3 -3
- package/dist/workbook/conditional-format-write.cjs +1 -1
- package/dist/workbook/conditional-format-write.js +1 -1
- package/dist/workbook/conditional-format.cjs +1 -1
- package/dist/workbook/conditional-format.d.cts +1 -1
- package/dist/workbook/conditional-format.d.ts +1 -1
- package/dist/workbook/conditional-format.js +1 -1
- package/dist/workbook/data-validation-write.cjs +1 -1
- package/dist/workbook/data-validation-write.js +1 -1
- package/dist/workbook/data-validation.d.cts +1 -1
- package/dist/workbook/data-validation.d.ts +1 -1
- package/dist/workbook/defined-names.cjs +186 -0
- package/dist/workbook/defined-names.d.cts +2 -0
- package/dist/workbook/defined-names.d.ts +2 -0
- package/dist/workbook/defined-names.js +182 -0
- package/dist/workbook/drawing.d.cts +1 -1
- package/dist/workbook/drawing.d.ts +1 -1
- package/dist/workbook/globals-writer.cjs +7 -7
- package/dist/workbook/globals-writer.d.cts +9 -2
- package/dist/workbook/globals-writer.d.ts +9 -2
- package/dist/workbook/globals-writer.js +8 -8
- package/dist/workbook/globals.cjs +8 -2
- package/dist/workbook/globals.d.cts +6 -1
- package/dist/workbook/globals.d.ts +6 -1
- package/dist/workbook/globals.js +8 -2
- package/dist/workbook/print-names.cjs +1 -1
- package/dist/workbook/print-names.js +1 -1
- package/dist/workbook/sheet-writer.cjs +4 -4
- package/dist/workbook/sheet-writer.js +4 -4
- package/dist/workbook/sheet.cjs +1 -1
- package/dist/workbook/sheet.d.cts +4 -4
- package/dist/workbook/sheet.d.ts +4 -4
- package/dist/workbook/sheet.js +1 -1
- package/dist/write.cjs +56 -12
- package/dist/write.js +57 -13
- package/dist/written-cells.cjs +4 -2
- package/dist/written-cells.d.cts +2 -2
- package/dist/written-cells.d.ts +2 -2
- package/dist/written-cells.js +4 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -16,13 +16,15 @@ Under active development, with real, tested **read and write** support. Built an
|
|
|
16
16
|
- **Worksheet substreams**, read (`src/workbook/sheet.ts`) and write (`src/workbook/sheet-writer.ts`) — `Dimensions`, `Row` (height and hidden state), `ColInfo` (width and hidden state), `MergeCells`, the print-settings record group (see [Print settings](#print-settings)), and the cell-value family: `Number`, `BoolErr`, `LabelSst`, `Blank`, and now `Formula` (see [Formula writing](#formula-writing)) on write (`MulBlank`, `RK`, `MulRk`, and `Label` stay read-only — see below).
|
|
17
17
|
- **Formula writing, same-sheet only** (`src/biff/ptg-writer.ts`) — a `ContentSheetCell.formula`'s own text, compiled back into a `Formula` record's `Ptg` token stream: literal operands, same-sheet cell/range references (`$`-qualified), every arithmetic/comparison/unary/percent operator, explicit parentheses, and a function call resolved by name against `ptg-functions.ts`'s own `Ftab` table (`PtgFunc` when its fixed arity matches, `PtgFuncVar` otherwise) — see [Formula writing](#formula-writing) for the exact scope boundary and why it stops at one sheet.
|
|
18
18
|
- **Cell comments** (`src/workbook/comment-writer.ts`) — a `ContentSheetCellComment`'s `text` and `author` written back out as the `Note`/`Obj`/`Txo` triple `src/workbook/comments.ts` already reads, one object id per commented cell; `replies` and `createdAt` have nowhere to land, matching the reader's own documented gap (legacy BIFF8 has no threading or per-comment timestamp at all).
|
|
19
|
-
- **Number-format classification and date serials** ([`excel-number-format`](../excel-number-format/README.md), `src/serial.ts`) — what turns a bare number into the schema's own `percentage`/`currency`/`date`/`time`/`dateTime` value kinds and back, honouring the workbook's own epoch flag (the writer always emits the 1900 system) and refusing the 1900 system's phantom leap day in both directions. The classification itself is a dependency, not local code: this package shares it with `ooxml.js`'s xlsx support, since it is the identical mini-language in both formats (ExaDev/documents.js#848). A cell's own `numberFormatCode` is preserved verbatim on write when present; absent, it resolves to a representative
|
|
19
|
+
- **Number-format classification and date serials** ([`excel-number-format`](../excel-number-format/README.md), `src/serial.ts`) — what turns a bare number into the schema's own `percentage`/`currency`/`date`/`time`/`dateTime` value kinds and back, honouring the workbook's own epoch flag (the writer always emits the 1900 system) and refusing the 1900 system's phantom leap day in both directions. The classification itself is a dependency, not local code: this package shares it with `ooxml.js`'s xlsx support, since it is the identical mini-language in both formats (ExaDev/documents.js#848). A cell's own `numberFormatCode` is preserved verbatim on write when present; absent, it resolves to a representative code for its value kind (`General` for a plain number/string/boolean/error, `0%` for a percentage, a `[$USD]#,##0.00`-shaped bracket format for a currency naming an ISO 4217 code — the one carrier the code survives the round trip through, the identical encoding `ooxml.js`'s own xlsx writer states for the same schema field — a bare `$` format for a currency with no code or a symbol that is not an ISO code, `mm-dd-yy`/`h:mm:ss`/`m/d/yy h:mm` for date/time/dateTime), and the workbook-wide `Format`/`XF` table is deduplicated across every sheet so two cells sharing one code share one entry.
|
|
20
20
|
- **Formula text recovery, including shared, array, and external-reference formulas** (`src/biff/ptg.ts`, `src/biff/ptg-functions.ts`, `src/workbook/sheet.ts`'s `collectFormulaGroups`, `src/workbook/globals.ts`'s `readSupBook`) — a Formula record's compiled `rgce` token stream ([MS-XLS] 2.5.198's `Ptg` vocabulary) read and rebuilt into the infix text a spreadsheet application would show: literal operands (`PtgInt`/`PtgNum`/`PtgStr`/`PtgBool`/`PtgErr`/`PtgMissArg`), cell and range references including their 3D (cross-sheet) forms (`PtgRef`/`PtgArea`/`PtgRef3d`/`PtgArea3d`, `$`-qualified per their own relative/absolute flags), every arithmetic/comparison/unary/percent operator and explicit parentheses, function calls through both `PtgFunc` (fixed arity, resolved from a curated table of [MS-XLS]'s own Ftab grammar) and `PtgFuncVar` (variable arity, its own on-disk `cparams`), a **shared formula** (`PtgExp` joined against the `ShrFmla` record that follows its group's base cell, its relative `PtgRefN`/`PtgAreaN` tokens re-expanded for each referencing cell's own position, alongside any ordinary, non-relative token the same shared expression carries), an **array formula** (`PtgExp` joined against an `Array` record instead, its expression returned with no further wrapping — Excel's own `{...}` CSE bracing is formula-bar display syntax, never written into the formula itself, matching `ooxml.js`'s own xlsx convention; a `PtgArray` array-constant literal's `{...}` is genuine syntax, not this bracing, and is resolved from its own `PtgExtraArray` trailer wherever one appears, array-entered or not), and a **3D reference's sheet name**, resolved through `EXTERNSHEET` and `SupBook` for both a self-referencing workbook and a genuinely external one (its own file name and sheet name(s) recovered as far as `SupBook`'s `virtPath`/`rgst` fields allow); a DDE/OLE/add-in/same-sheet/unused link, an unresolved sheet index, or an undecoded `virtPath` form has no real name to recover, and leaves the whole containing formula unresolved exactly like any other unsupported construct, rather than writing a fabricated placeholder into what would otherwise be real formula text (`src/workbook/globals.ts`'s own `sheetRanges`) — see "Formula expressions" under Read-side gaps below for the exact boundary of what still resolves to nothing at all.
|
|
21
21
|
- **Schema mapping** — `readXlsContent`/`readXls` (`src/content.ts`) as before, now also populating `ContentSheetCell.formula` wherever the Ptg reader above resolves it; `writeXlsContent`/`writeXls` (`src/write.ts`) the counterpart, taking a `ContentDocument`/`DocumentTree` of `kind: 'spreadsheet'` and producing genuine `.xls` bytes: a real BIFF8 `Workbook` stream (globals substream, one worksheet substream per sheet, `BoundSheet8.lbPlyPos` patched to each sheet's real byte offset once every substream's length is known) wrapped in a real [MS-CFB] compound file via `archive-codec`'s `writeCompoundFile`.
|
|
22
22
|
- **Document metadata** — `title`/`subject`/`author`/`keywords`/`createdIso`/`modifiedIso` read from a `"\x05SummaryInformation"` stream when one is present, and written back to one whenever the input's metadata carries anything that stream can hold (see [Metadata](#metadata)).
|
|
23
23
|
- **Cell decoration** — a cell's background fill (every named `FillPattern` value, solid and pattern alike) and per-side borders, read from and written to `XF`'s trailing `CellXF` payload plus the workbook's own `Palette` record, in both directions and verified against real LibreOffice-produced BIFF8, not just this package's own round trip (see [Cell decoration](#cell-decoration)).
|
|
24
24
|
- **Print settings** — every field of `ContentSheetPrintSettings`: page size and orientation, all four margins, gridline and row/column-header printing, page order, print scale or fit-to-page counts, manual page breaks, the print range, and the repeated header rows and columns — read from and written to the nine worksheet-substream records and the two built-in defined names that carry them, in both directions and verified against real LibreOffice-produced BIFF8 (see [Print settings](#print-settings)).
|
|
25
25
|
- **Cell alignment** — a cell's own horizontal (`left`/`center`/`right`/`justify`) and vertical (`top`/`middle`) alignment, read from and written to `XF`'s trailing `CellXF`/`StyleXF` payload's own leading word, in both directions and verified against real LibreOffice-produced BIFF8 (see [Cell alignment](#cell-alignment)).
|
|
26
|
+
- **Per-cell fonts** — a cell's own font (`ContentSheetCell.font`: bold/italic/underline/strike, family, size, colour), read from the Font record its XF's own `ifnt` indexes into the workbook's font table and written back into an interned font table the writer builds from the cells it writes (`biff/font.ts` holds both directions of the one record layout). A cell's font is derived by diffing that entry against the table's own first font — the Normal style's, which is what "the format's default" concretely means for a given file — so only properties the cell genuinely differs in are stated, the same default-omission policy alignment and fill already apply.
|
|
27
|
+
- **Defined names** — a workbook's own defined names, read from and written to the `Lbl` records ([MS-XLS] 2.4.150) of the globals substream (`workbook/defined-names.ts`): the name as spelled (a built-in under its `_xlnm.` spelling), the `refersTo` rebuilt from the record's compiled Ptg stream by the same parser a cell formula uses, and a sheet-local scope carried as `scopeSheetIndex` translated into the document's own sheets-array position. The two print built-ins stay where they already live, on print settings.
|
|
26
28
|
- **Data validation, read-only (ExaDev/documents.js#1098)** — a sheet's own `Dv` records (`src/workbook/data-validation.ts`) into `ContentSheet.dataValidations`: type, comparison operator, one or two formulas (via the same `Ptg` reader `Formula` records already use), allow-blank/show-input/show-error flags, error style, and prompt/error titles and messages, resolved directly against [MS-XLS]'s own published `Dv`/`DVal`/`DVParsedFormula`/`SqRefU` field layouts rather than a producer-specific convention.
|
|
27
29
|
- **Charts, drawings, and images, read-only (ExaDev/documents.js#924)** — a worksheet's own MS-ODRAW (Escher) drawing layer (`src/drawing/escher.ts`, `src/drawing/shapes.ts`, `src/drawing/blips.ts`), the workbook-wide Blip Store every picture shape's own `pib` property indexes into, and an embedded chart's own nested substream (`src/workbook/chart.ts`), joined into `ContentSheet.images` and `ContentSheet.embeddedObjects` by `src/workbook/drawing.ts` — see "Charts, drawings, and images, resolved for real" under Read-side gaps below.
|
|
28
30
|
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
require("./record-types.cjs");
|
|
3
|
+
const require_biff_builder = require("./builder.cjs");
|
|
4
|
+
const require_biff_cursor = require("./cursor.cjs");
|
|
5
|
+
const require_biff_write_errors = require("./write-errors.cjs");
|
|
6
|
+
const require_biff_record_writer = require("./record-writer.cjs");
|
|
7
|
+
const require_biff_strings = require("./strings.cjs");
|
|
8
|
+
const require_biff_xf_colors = require("./xf-colors.cjs");
|
|
9
|
+
//#region src/biff/font.ts
|
|
10
|
+
/** dyHeight's own unit: a twentieth of a point, the same twip the docx side of this family measures character heights in. */
|
|
11
|
+
const TWIPS_PER_POINT = 20;
|
|
12
|
+
/** grbit's fItalic bit ([MS-XLS] 2.4.122's own field layout: fItalic is the second bit). */
|
|
13
|
+
const FONT_FLAG_ITALIC = 2;
|
|
14
|
+
/** grbit's fStrikeOut bit, three bits above fItalic in the same field. */
|
|
15
|
+
const FONT_FLAG_STRIKEOUT = 8;
|
|
16
|
+
/** [MS-XLS] 2.4.122's own bls table names exactly two weights -- 400 normal and 700 bold -- but the field only MUST be "0, or greater than or equal to 100 and less than or equal to 1000", so a third-party file can carry an intermediate GDI weight. 700 is the threshold rather than an equality test so such a weight still resolves one way; this package's own writer always states one of the two named values. */
|
|
17
|
+
const FONT_WEIGHT_BOLD = 700;
|
|
18
|
+
const FONT_WEIGHT_NORMAL = 400;
|
|
19
|
+
/** uls's "no underline" value; 0x01 single, 0x02 double, and the two accounting styles all state underline=true, since ContentFont's underline is a boolean with no double spelling -- the same collapse the border reader applies to the dash-family BorderStyle tokens. */
|
|
20
|
+
const FONT_UNDERLINE_NONE = 0;
|
|
21
|
+
const FONT_UNDERLINE_SINGLE = 1;
|
|
22
|
+
/** icv's own "Automatic"/System Window Text special value ([MS-XLS] "IcvFont": the one value outside the 0x08-0x3F palette range a font colour may legally carry) -- the colour a font that states none of its own carries, and the one this package's writer emits for a ContentFont leaving color absent. Deliberately NOT resolved on read: "automatic" means "whatever the display settings say", a moving target with no fixed RGB triple, matching how the fill reader treats an Automatic icv as no colour at all. */
|
|
23
|
+
const FONT_COLOR_AUTOMATIC = 32767;
|
|
24
|
+
/**
|
|
25
|
+
* The font-table entry a ContentFont resolves to, for every property it leaves absent or false: the Normal style's own value, Excel 97-2003's classic Arial 10pt with no flags and an Automatic colour. Entry 0 of every font table this package writes carries exactly these fields, so a cell whose ContentFont normalises back to them references font 0 and needs no entry of its own -- the write-side mirror of the read side diffing every cell font against entry 0.
|
|
26
|
+
*/
|
|
27
|
+
const NORMAL_FONT_FIELDS = {
|
|
28
|
+
name: "Arial",
|
|
29
|
+
heightTwips: 200,
|
|
30
|
+
bold: false,
|
|
31
|
+
italic: false,
|
|
32
|
+
strikeout: false,
|
|
33
|
+
underline: false,
|
|
34
|
+
colorIcv: FONT_COLOR_AUTOMATIC
|
|
35
|
+
};
|
|
36
|
+
/** Font ([MS-XLS] 2.4.122): dyHeight, grbit, icv, bls, sss, uls, bFamily, bCharSet, unused3, then the name as a ShortXLUnicodeString whose fHighByte MUST be 1 regardless of the name's own content. */
|
|
37
|
+
function readFontRecord(record) {
|
|
38
|
+
const cursor = new require_biff_cursor.BlockCursor(record.blocks);
|
|
39
|
+
const heightTwips = cursor.u16();
|
|
40
|
+
const flags = cursor.u16();
|
|
41
|
+
const colorIcv = cursor.u16();
|
|
42
|
+
const weight = cursor.u16();
|
|
43
|
+
cursor.skip(2);
|
|
44
|
+
const underline = cursor.u8();
|
|
45
|
+
cursor.skip(3);
|
|
46
|
+
return {
|
|
47
|
+
name: require_biff_strings.readShortXLUnicodeString(cursor),
|
|
48
|
+
heightTwips,
|
|
49
|
+
bold: weight >= FONT_WEIGHT_BOLD,
|
|
50
|
+
italic: (flags & FONT_FLAG_ITALIC) !== 0,
|
|
51
|
+
strikeout: (flags & FONT_FLAG_STRIKEOUT) !== 0,
|
|
52
|
+
underline: underline !== FONT_UNDERLINE_NONE,
|
|
53
|
+
colorIcv
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The cell-level font one font-table entry resolves to, as ContentSheetCell.font carries it: only the properties that DIFFER from the workbook's own first font, or undefined when the entry is that font outright -- the format's default, which the schema models as the field being absent rather than an explicitly restated copy of it.
|
|
58
|
+
*
|
|
59
|
+
* The diff is per property, since a real cell font usually differs from the default in one or two respects and agrees in the rest: a Courier-bold cell font against an Arial default yields { fontFamily: "Courier", bold: true } and says nothing about size, which the default already settles. A colour that differs from the default's but does not resolve to a fixed RGB (the Automatic sentinel, a chart/tooltip display colour) is left unstated for the same reason resolveIcvColor declines it everywhere else: there is no honest triple to write. The icv comparison is on the raw index rather than the resolved colour, so a cell font carrying the SAME index as the default states no colour even where that index resolves -- the file said "the same colour as the default", not a colour that happens to coincide.
|
|
60
|
+
*/
|
|
61
|
+
function contentFontOf(font, baseline, resolveColor) {
|
|
62
|
+
const result = {};
|
|
63
|
+
if (font.bold !== baseline.bold) result.bold = font.bold;
|
|
64
|
+
if (font.italic !== baseline.italic) result.italic = font.italic;
|
|
65
|
+
if (font.underline !== baseline.underline) result.underline = font.underline;
|
|
66
|
+
if (font.strikeout !== baseline.strikeout) result.strike = font.strikeout;
|
|
67
|
+
if (font.name !== baseline.name) result.fontFamily = font.name;
|
|
68
|
+
if (font.heightTwips !== baseline.heightTwips) result.sizePt = font.heightTwips / TWIPS_PER_POINT;
|
|
69
|
+
if (font.colorIcv !== baseline.colorIcv) {
|
|
70
|
+
const color = resolveColor(font.colorIcv);
|
|
71
|
+
if (color !== void 0) result.color = color;
|
|
72
|
+
}
|
|
73
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
74
|
+
}
|
|
75
|
+
/** Resolves a font colour icv through the same Icv table machinery a fill or border colour already uses -- IcvFont ([MS-XLS] "IcvFont") is an Icv constrained to the palette range plus the Automatic sentinel, so the shared resolver covers it without a second copy. */
|
|
76
|
+
function resolveFontColor(icv, palette) {
|
|
77
|
+
return require_biff_xf_colors.resolveIcvColor(icv, palette);
|
|
78
|
+
}
|
|
79
|
+
/** The write-side mirror of readFontRecord: the identical layout in the opposite direction, in the one place the read side already cites. dyHeight's own 20-8191 range and fontName's own 1-31 length are the record's stated MUSTs, so a ContentFont asking for something outside them is refused here rather than silently clipped into a spec-illegal byte sequence. */
|
|
80
|
+
function writeFontRecord(fields) {
|
|
81
|
+
if ((fields.heightTwips < 20 || fields.heightTwips > 8191) && fields.heightTwips !== 0) throw new require_biff_write_errors.BiffWriteError(`font height ${fields.heightTwips} twips is outside the 20-8191 range [MS-XLS] 2.4.122's own dyHeight field allows`);
|
|
82
|
+
if (fields.name.length < 1 || fields.name.length > 31) throw new require_biff_write_errors.BiffWriteError(`font name ${JSON.stringify(fields.name)} is ${fields.name.length} UTF-16 code units, outside the 1-31 fontName itself allows ([MS-XLS] 2.4.122: "String length MUST be greater than or equal to 1 and less than or equal to 31")`);
|
|
83
|
+
const grbit = (fields.italic ? FONT_FLAG_ITALIC : 0) | (fields.strikeout ? FONT_FLAG_STRIKEOUT : 0);
|
|
84
|
+
const data = new require_biff_builder.RecordBuilder().u16(fields.heightTwips).u16(grbit).u16(fields.colorIcv).u16(fields.bold ? FONT_WEIGHT_BOLD : FONT_WEIGHT_NORMAL).u16(0).u8(fields.underline ? FONT_UNDERLINE_SINGLE : FONT_UNDERLINE_NONE).u8(0).u8(0).u8(0).bytes(fontNameBytes(fields.name)).build();
|
|
85
|
+
return require_biff_record_writer.writeRecord(49, data);
|
|
86
|
+
}
|
|
87
|
+
/** Forces the uncompressed (fHighByte=1) ShortXLUnicodeString encoding a font name MUST use ([MS-XLS] 2.4.122: "The fontName.fHighByte field MUST equal 1"), writing every character as a full 16-bit code unit -- unlike every other ShortXLUnicodeString this package writes. */
|
|
88
|
+
function fontNameBytes(name) {
|
|
89
|
+
const builder = new require_biff_builder.RecordBuilder().u8(name.length).u8(1);
|
|
90
|
+
for (let index = 0; index < name.length; index += 1) builder.u16(name.charCodeAt(index));
|
|
91
|
+
return builder.build();
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The font-table entry a ContentSheetCell.font resolves to: every property it leaves absent or false takes the Normal font's own value, since the schema models an absent property as "the format's default" and entry 0 of the table is exactly that default. `icvOf` resolves a stated colour into the workbook's own colour plan, the identical resolution a fill or border colour already draws.
|
|
95
|
+
*/
|
|
96
|
+
function xfFontFieldsOf(font, icvOf) {
|
|
97
|
+
if (font === void 0) return NORMAL_FONT_FIELDS;
|
|
98
|
+
return {
|
|
99
|
+
name: font.fontFamily ?? NORMAL_FONT_FIELDS.name,
|
|
100
|
+
heightTwips: font.sizePt === void 0 ? NORMAL_FONT_FIELDS.heightTwips : Math.round(font.sizePt * TWIPS_PER_POINT),
|
|
101
|
+
bold: font.bold === true,
|
|
102
|
+
italic: font.italic === true,
|
|
103
|
+
strikeout: font.strike === true,
|
|
104
|
+
underline: font.underline === true,
|
|
105
|
+
colorIcv: font.color === void 0 ? FONT_COLOR_AUTOMATIC : icvOf(font.color)
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Whether a ContentSheetCell.font resolves to anything other than the Normal font's own fields -- the "does this font carry formatting at all" question every write-side pass shares (written-cells.ts's record predicate, write.ts's font-table and XF interning). Answered through the identical normalisation xfFontFieldsOf applies, so a font that merely restates a default value -- fontFamily "Arial" on a workbook whose default is Arial, or sizePt 10 -- counts as none, exactly as the read side's diff against font-table entry 0 already treats it.
|
|
110
|
+
*
|
|
111
|
+
* A stated colour always differs, because the Normal entry's own colour is the Automatic sentinel, which no palette resolution ever returns (a resolved icv is always a real table index), so the comparison needs no colour plan of its own.
|
|
112
|
+
*/
|
|
113
|
+
function cellFontDiffersFromNormal(font) {
|
|
114
|
+
if (font.fontFamily !== void 0 && font.fontFamily !== NORMAL_FONT_FIELDS.name) return true;
|
|
115
|
+
if (font.sizePt !== void 0 && Math.round(font.sizePt * TWIPS_PER_POINT) !== NORMAL_FONT_FIELDS.heightTwips) return true;
|
|
116
|
+
if (font.bold === true || font.italic === true || font.underline === true || font.strike === true) return true;
|
|
117
|
+
return font.color !== void 0;
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
exports.FONT_COLOR_AUTOMATIC = FONT_COLOR_AUTOMATIC;
|
|
121
|
+
exports.NORMAL_FONT_FIELDS = NORMAL_FONT_FIELDS;
|
|
122
|
+
exports.cellFontDiffersFromNormal = cellFontDiffersFromNormal;
|
|
123
|
+
exports.contentFontOf = contentFontOf;
|
|
124
|
+
exports.readFontRecord = readFontRecord;
|
|
125
|
+
exports.resolveFontColor = resolveFontColor;
|
|
126
|
+
exports.writeFontRecord = writeFontRecord;
|
|
127
|
+
exports.xfFontFieldsOf = xfFontFieldsOf;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as contentFontOf, c as writeFontRecord, i as cellFontDiffersFromNormal, l as xfFontFieldsOf, n as NORMAL_FONT_FIELDS, o as readFontRecord, r as XfFontFields, s as resolveFontColor, t as FONT_COLOR_AUTOMATIC } from "../font-CBvAGxD8.cjs";
|
|
2
|
+
export { FONT_COLOR_AUTOMATIC, NORMAL_FONT_FIELDS, XfFontFields, cellFontDiffersFromNormal, contentFontOf, readFontRecord, resolveFontColor, writeFontRecord, xfFontFieldsOf };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as contentFontOf, c as writeFontRecord, i as cellFontDiffersFromNormal, l as xfFontFieldsOf, n as NORMAL_FONT_FIELDS, o as readFontRecord, r as XfFontFields, s as resolveFontColor, t as FONT_COLOR_AUTOMATIC } from "../font-DcXHYgZ2.js";
|
|
2
|
+
export { FONT_COLOR_AUTOMATIC, NORMAL_FONT_FIELDS, XfFontFields, cellFontDiffersFromNormal, contentFontOf, readFontRecord, resolveFontColor, writeFontRecord, xfFontFieldsOf };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import "./record-types.js";
|
|
2
|
+
import { RecordBuilder } from "./builder.js";
|
|
3
|
+
import { BlockCursor } from "./cursor.js";
|
|
4
|
+
import { BiffWriteError } from "./write-errors.js";
|
|
5
|
+
import { writeRecord } from "./record-writer.js";
|
|
6
|
+
import { readShortXLUnicodeString } from "./strings.js";
|
|
7
|
+
import { resolveIcvColor } from "./xf-colors.js";
|
|
8
|
+
//#region src/biff/font.ts
|
|
9
|
+
/** dyHeight's own unit: a twentieth of a point, the same twip the docx side of this family measures character heights in. */
|
|
10
|
+
const TWIPS_PER_POINT = 20;
|
|
11
|
+
/** grbit's fItalic bit ([MS-XLS] 2.4.122's own field layout: fItalic is the second bit). */
|
|
12
|
+
const FONT_FLAG_ITALIC = 2;
|
|
13
|
+
/** grbit's fStrikeOut bit, three bits above fItalic in the same field. */
|
|
14
|
+
const FONT_FLAG_STRIKEOUT = 8;
|
|
15
|
+
/** [MS-XLS] 2.4.122's own bls table names exactly two weights -- 400 normal and 700 bold -- but the field only MUST be "0, or greater than or equal to 100 and less than or equal to 1000", so a third-party file can carry an intermediate GDI weight. 700 is the threshold rather than an equality test so such a weight still resolves one way; this package's own writer always states one of the two named values. */
|
|
16
|
+
const FONT_WEIGHT_BOLD = 700;
|
|
17
|
+
const FONT_WEIGHT_NORMAL = 400;
|
|
18
|
+
/** uls's "no underline" value; 0x01 single, 0x02 double, and the two accounting styles all state underline=true, since ContentFont's underline is a boolean with no double spelling -- the same collapse the border reader applies to the dash-family BorderStyle tokens. */
|
|
19
|
+
const FONT_UNDERLINE_NONE = 0;
|
|
20
|
+
const FONT_UNDERLINE_SINGLE = 1;
|
|
21
|
+
/** icv's own "Automatic"/System Window Text special value ([MS-XLS] "IcvFont": the one value outside the 0x08-0x3F palette range a font colour may legally carry) -- the colour a font that states none of its own carries, and the one this package's writer emits for a ContentFont leaving color absent. Deliberately NOT resolved on read: "automatic" means "whatever the display settings say", a moving target with no fixed RGB triple, matching how the fill reader treats an Automatic icv as no colour at all. */
|
|
22
|
+
const FONT_COLOR_AUTOMATIC = 32767;
|
|
23
|
+
/**
|
|
24
|
+
* The font-table entry a ContentFont resolves to, for every property it leaves absent or false: the Normal style's own value, Excel 97-2003's classic Arial 10pt with no flags and an Automatic colour. Entry 0 of every font table this package writes carries exactly these fields, so a cell whose ContentFont normalises back to them references font 0 and needs no entry of its own -- the write-side mirror of the read side diffing every cell font against entry 0.
|
|
25
|
+
*/
|
|
26
|
+
const NORMAL_FONT_FIELDS = {
|
|
27
|
+
name: "Arial",
|
|
28
|
+
heightTwips: 200,
|
|
29
|
+
bold: false,
|
|
30
|
+
italic: false,
|
|
31
|
+
strikeout: false,
|
|
32
|
+
underline: false,
|
|
33
|
+
colorIcv: FONT_COLOR_AUTOMATIC
|
|
34
|
+
};
|
|
35
|
+
/** Font ([MS-XLS] 2.4.122): dyHeight, grbit, icv, bls, sss, uls, bFamily, bCharSet, unused3, then the name as a ShortXLUnicodeString whose fHighByte MUST be 1 regardless of the name's own content. */
|
|
36
|
+
function readFontRecord(record) {
|
|
37
|
+
const cursor = new BlockCursor(record.blocks);
|
|
38
|
+
const heightTwips = cursor.u16();
|
|
39
|
+
const flags = cursor.u16();
|
|
40
|
+
const colorIcv = cursor.u16();
|
|
41
|
+
const weight = cursor.u16();
|
|
42
|
+
cursor.skip(2);
|
|
43
|
+
const underline = cursor.u8();
|
|
44
|
+
cursor.skip(3);
|
|
45
|
+
return {
|
|
46
|
+
name: readShortXLUnicodeString(cursor),
|
|
47
|
+
heightTwips,
|
|
48
|
+
bold: weight >= FONT_WEIGHT_BOLD,
|
|
49
|
+
italic: (flags & FONT_FLAG_ITALIC) !== 0,
|
|
50
|
+
strikeout: (flags & FONT_FLAG_STRIKEOUT) !== 0,
|
|
51
|
+
underline: underline !== FONT_UNDERLINE_NONE,
|
|
52
|
+
colorIcv
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The cell-level font one font-table entry resolves to, as ContentSheetCell.font carries it: only the properties that DIFFER from the workbook's own first font, or undefined when the entry is that font outright -- the format's default, which the schema models as the field being absent rather than an explicitly restated copy of it.
|
|
57
|
+
*
|
|
58
|
+
* The diff is per property, since a real cell font usually differs from the default in one or two respects and agrees in the rest: a Courier-bold cell font against an Arial default yields { fontFamily: "Courier", bold: true } and says nothing about size, which the default already settles. A colour that differs from the default's but does not resolve to a fixed RGB (the Automatic sentinel, a chart/tooltip display colour) is left unstated for the same reason resolveIcvColor declines it everywhere else: there is no honest triple to write. The icv comparison is on the raw index rather than the resolved colour, so a cell font carrying the SAME index as the default states no colour even where that index resolves -- the file said "the same colour as the default", not a colour that happens to coincide.
|
|
59
|
+
*/
|
|
60
|
+
function contentFontOf(font, baseline, resolveColor) {
|
|
61
|
+
const result = {};
|
|
62
|
+
if (font.bold !== baseline.bold) result.bold = font.bold;
|
|
63
|
+
if (font.italic !== baseline.italic) result.italic = font.italic;
|
|
64
|
+
if (font.underline !== baseline.underline) result.underline = font.underline;
|
|
65
|
+
if (font.strikeout !== baseline.strikeout) result.strike = font.strikeout;
|
|
66
|
+
if (font.name !== baseline.name) result.fontFamily = font.name;
|
|
67
|
+
if (font.heightTwips !== baseline.heightTwips) result.sizePt = font.heightTwips / TWIPS_PER_POINT;
|
|
68
|
+
if (font.colorIcv !== baseline.colorIcv) {
|
|
69
|
+
const color = resolveColor(font.colorIcv);
|
|
70
|
+
if (color !== void 0) result.color = color;
|
|
71
|
+
}
|
|
72
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
73
|
+
}
|
|
74
|
+
/** Resolves a font colour icv through the same Icv table machinery a fill or border colour already uses -- IcvFont ([MS-XLS] "IcvFont") is an Icv constrained to the palette range plus the Automatic sentinel, so the shared resolver covers it without a second copy. */
|
|
75
|
+
function resolveFontColor(icv, palette) {
|
|
76
|
+
return resolveIcvColor(icv, palette);
|
|
77
|
+
}
|
|
78
|
+
/** The write-side mirror of readFontRecord: the identical layout in the opposite direction, in the one place the read side already cites. dyHeight's own 20-8191 range and fontName's own 1-31 length are the record's stated MUSTs, so a ContentFont asking for something outside them is refused here rather than silently clipped into a spec-illegal byte sequence. */
|
|
79
|
+
function writeFontRecord(fields) {
|
|
80
|
+
if ((fields.heightTwips < 20 || fields.heightTwips > 8191) && fields.heightTwips !== 0) throw new BiffWriteError(`font height ${fields.heightTwips} twips is outside the 20-8191 range [MS-XLS] 2.4.122's own dyHeight field allows`);
|
|
81
|
+
if (fields.name.length < 1 || fields.name.length > 31) throw new BiffWriteError(`font name ${JSON.stringify(fields.name)} is ${fields.name.length} UTF-16 code units, outside the 1-31 fontName itself allows ([MS-XLS] 2.4.122: "String length MUST be greater than or equal to 1 and less than or equal to 31")`);
|
|
82
|
+
const grbit = (fields.italic ? FONT_FLAG_ITALIC : 0) | (fields.strikeout ? FONT_FLAG_STRIKEOUT : 0);
|
|
83
|
+
const data = new RecordBuilder().u16(fields.heightTwips).u16(grbit).u16(fields.colorIcv).u16(fields.bold ? FONT_WEIGHT_BOLD : FONT_WEIGHT_NORMAL).u16(0).u8(fields.underline ? FONT_UNDERLINE_SINGLE : FONT_UNDERLINE_NONE).u8(0).u8(0).u8(0).bytes(fontNameBytes(fields.name)).build();
|
|
84
|
+
return writeRecord(49, data);
|
|
85
|
+
}
|
|
86
|
+
/** Forces the uncompressed (fHighByte=1) ShortXLUnicodeString encoding a font name MUST use ([MS-XLS] 2.4.122: "The fontName.fHighByte field MUST equal 1"), writing every character as a full 16-bit code unit -- unlike every other ShortXLUnicodeString this package writes. */
|
|
87
|
+
function fontNameBytes(name) {
|
|
88
|
+
const builder = new RecordBuilder().u8(name.length).u8(1);
|
|
89
|
+
for (let index = 0; index < name.length; index += 1) builder.u16(name.charCodeAt(index));
|
|
90
|
+
return builder.build();
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The font-table entry a ContentSheetCell.font resolves to: every property it leaves absent or false takes the Normal font's own value, since the schema models an absent property as "the format's default" and entry 0 of the table is exactly that default. `icvOf` resolves a stated colour into the workbook's own colour plan, the identical resolution a fill or border colour already draws.
|
|
94
|
+
*/
|
|
95
|
+
function xfFontFieldsOf(font, icvOf) {
|
|
96
|
+
if (font === void 0) return NORMAL_FONT_FIELDS;
|
|
97
|
+
return {
|
|
98
|
+
name: font.fontFamily ?? NORMAL_FONT_FIELDS.name,
|
|
99
|
+
heightTwips: font.sizePt === void 0 ? NORMAL_FONT_FIELDS.heightTwips : Math.round(font.sizePt * TWIPS_PER_POINT),
|
|
100
|
+
bold: font.bold === true,
|
|
101
|
+
italic: font.italic === true,
|
|
102
|
+
strikeout: font.strike === true,
|
|
103
|
+
underline: font.underline === true,
|
|
104
|
+
colorIcv: font.color === void 0 ? FONT_COLOR_AUTOMATIC : icvOf(font.color)
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Whether a ContentSheetCell.font resolves to anything other than the Normal font's own fields -- the "does this font carry formatting at all" question every write-side pass shares (written-cells.ts's record predicate, write.ts's font-table and XF interning). Answered through the identical normalisation xfFontFieldsOf applies, so a font that merely restates a default value -- fontFamily "Arial" on a workbook whose default is Arial, or sizePt 10 -- counts as none, exactly as the read side's diff against font-table entry 0 already treats it.
|
|
109
|
+
*
|
|
110
|
+
* A stated colour always differs, because the Normal entry's own colour is the Automatic sentinel, which no palette resolution ever returns (a resolved icv is always a real table index), so the comparison needs no colour plan of its own.
|
|
111
|
+
*/
|
|
112
|
+
function cellFontDiffersFromNormal(font) {
|
|
113
|
+
if (font.fontFamily !== void 0 && font.fontFamily !== NORMAL_FONT_FIELDS.name) return true;
|
|
114
|
+
if (font.sizePt !== void 0 && Math.round(font.sizePt * TWIPS_PER_POINT) !== NORMAL_FONT_FIELDS.heightTwips) return true;
|
|
115
|
+
if (font.bold === true || font.italic === true || font.underline === true || font.strike === true) return true;
|
|
116
|
+
return font.color !== void 0;
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
export { FONT_COLOR_AUTOMATIC, NORMAL_FONT_FIELDS, cellFontDiffersFromNormal, contentFontOf, readFontRecord, resolveFontColor, writeFontRecord, xfFontFieldsOf };
|
package/dist/biff/ptg-writer.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_biff_errors = require("./errors.cjs");
|
|
3
2
|
const require_biff_write_errors = require("./write-errors.cjs");
|
|
3
|
+
const require_biff_errors = require("./errors.cjs");
|
|
4
4
|
const require_biff_ptg_functions = require("./ptg-functions.cjs");
|
|
5
5
|
const require_biff_string_writer = require("./string-writer.cjs");
|
|
6
6
|
let document_schema_js = require("document-schema.js");
|
package/dist/biff/ptg-writer.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { errorCodeOf } from "./errors.js";
|
|
2
1
|
import { BiffWriteError } from "./write-errors.js";
|
|
2
|
+
import { errorCodeOf } from "./errors.js";
|
|
3
3
|
import { FTAB_FIXED_ARITY, FTAB_IFTAB_BY_NAME } from "./ptg-functions.js";
|
|
4
4
|
import { writeShortXLUnicodeString } from "./string-writer.js";
|
|
5
5
|
import { columnLettersToIndex } from "document-schema.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_biff_write_errors = require("./write-errors.cjs");
|
|
3
2
|
const require_biff_builder = require("./builder.cjs");
|
|
3
|
+
const require_biff_write_errors = require("./write-errors.cjs");
|
|
4
4
|
//#region src/biff/string-writer.ts
|
|
5
5
|
/** Bit 0 of a string's flags byte: set when each character occupies a full two-byte UTF-16 code unit ([MS-XLS] 2.5.293/2.5.294/2.5.240's own fHighByte). */
|
|
6
6
|
const FLAG_HIGH_BYTE = 1;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BiffWriteError } from "./write-errors.js";
|
|
2
1
|
import { RecordBuilder } from "./builder.js";
|
|
2
|
+
import { BiffWriteError } from "./write-errors.js";
|
|
3
3
|
//#region src/biff/string-writer.ts
|
|
4
4
|
/** Bit 0 of a string's flags byte: set when each character occupies a full two-byte UTF-16 code unit ([MS-XLS] 2.5.293/2.5.294/2.5.240's own fHighByte). */
|
|
5
5
|
const FLAG_HIGH_BYTE = 1;
|
package/dist/biff/xf-writer.cjs
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_biff_record_types = require("./record-types.cjs");
|
|
3
|
-
const require_biff_xf_colors = require("./xf-colors.cjs");
|
|
4
|
-
const require_biff_write_errors = require("./write-errors.cjs");
|
|
5
3
|
const require_biff_builder = require("./builder.cjs");
|
|
6
4
|
const require_biff_record_writer = require("./record-writer.cjs");
|
|
5
|
+
const require_biff_xf_colors = require("./xf-colors.cjs");
|
|
7
6
|
const require_biff_string_writer = require("./string-writer.cjs");
|
|
8
7
|
//#region src/biff/xf-writer.ts
|
|
9
8
|
/** Packs the shared trot/indent/fJustLast/fShrinkToFit/iReadOrder word every CellXF and StyleXF opens with, at whatever alc/alcV tokens the caller resolved (xf-colors.ts's horizAlignTokenFor/vertAlignTokenFor -- ALC_GENERAL(0)/ALCV_BOTTOM(2) for a caller passing neither, the identical bytes this word always carried before alignment was modelled). fWrap=0, fJustLast=0, trot=0, cIndent=0, fShrinkToFit=0, reserved1=0, iReadOrder=0: this writer never sets any of them, since ContentSheetCell has no field for wrap/rotation/indent/shrink-to-fit/reading-order. Returns the low 24 bits (alc..iReadOrder); the caller ORs in whatever the next 8 bits mean for its own shape (CellXF's fAtr* flags, or StyleXF's all-zero unused byte). */
|
|
@@ -54,25 +53,6 @@ function writeStyleRecord(options) {
|
|
|
54
53
|
const data = new require_biff_builder.RecordBuilder().u16(ixfeWord).u8(options.istyBuiltIn).u8(options.iLevel).build();
|
|
55
54
|
return require_biff_record_writer.writeRecord(659, data);
|
|
56
55
|
}
|
|
57
|
-
/** Font ([MS-XLS] 2.4.122): height in twips, colour/weight/script/underline defaults for an undecorated font, then the name as a ShortXLUnicodeString whose fHighByte MUST be 1 per the spec regardless of the name's own content ("The fontName.fHighByte field MUST equal 1") -- unlike every other ShortXLUnicodeString this package writes, so it is encoded uncompressed here directly rather than through writeShortXLUnicodeString, whose auto-compression would otherwise write 1-byte-per-character data under a flags byte that has to claim 2 bytes each. */
|
|
58
|
-
const FONT_FLAG_ITALIC_ETC = 0;
|
|
59
|
-
const FONT_COLOUR_AUTOMATIC = 32767;
|
|
60
|
-
const FONT_WEIGHT_NORMAL = 400;
|
|
61
|
-
const FONT_SCRIPT_NORMAL = 0;
|
|
62
|
-
const FONT_UNDERLINE_NONE = 0;
|
|
63
|
-
const FONT_FAMILY_SWISS = 2;
|
|
64
|
-
const FONT_CHARSET_ANSI = 0;
|
|
65
|
-
/** Forces the uncompressed (fHighByte=1) ShortXLUnicodeString encoding a font name MUST use, writing every character as a full 16-bit code unit regardless of whether a narrower encoding would also fit. */
|
|
66
|
-
function writeFontNameString(name) {
|
|
67
|
-
const builder = new require_biff_builder.RecordBuilder().u8(name.length).u8(1);
|
|
68
|
-
for (let index = 0; index < name.length; index += 1) builder.u16(name.charCodeAt(index));
|
|
69
|
-
return builder.build();
|
|
70
|
-
}
|
|
71
|
-
function writeFontRecord(name, heightTwips) {
|
|
72
|
-
if (name.length > 255) throw new require_biff_write_errors.BiffWriteError(`font name ${JSON.stringify(name)} is ${name.length} UTF-16 code units, above the 255 a ShortXLUnicodeString cch can hold`);
|
|
73
|
-
const data = new require_biff_builder.RecordBuilder().u16(heightTwips).u16(FONT_FLAG_ITALIC_ETC).u16(FONT_COLOUR_AUTOMATIC).u16(FONT_WEIGHT_NORMAL).u16(FONT_SCRIPT_NORMAL).u8(FONT_UNDERLINE_NONE).u8(FONT_FAMILY_SWISS).u8(FONT_CHARSET_ANSI).u8(0).bytes(writeFontNameString(name)).build();
|
|
74
|
-
return require_biff_record_writer.writeRecord(49, data);
|
|
75
|
-
}
|
|
76
56
|
/** Format ([MS-XLS] 2.4.126): a two-byte identifier then the format code as an XLUnicodeString. */
|
|
77
57
|
function writeFormatRecord(id, code) {
|
|
78
58
|
const data = new require_biff_builder.RecordBuilder().u16(id).bytes(require_biff_string_writer.writeXLUnicodeString(code)).build();
|
|
@@ -89,7 +69,6 @@ function writePaletteRecord(colors) {
|
|
|
89
69
|
}
|
|
90
70
|
//#endregion
|
|
91
71
|
exports.writeCellXfRecord = writeCellXfRecord;
|
|
92
|
-
exports.writeFontRecord = writeFontRecord;
|
|
93
72
|
exports.writeFormatRecord = writeFormatRecord;
|
|
94
73
|
exports.writePaletteRecord = writePaletteRecord;
|
|
95
74
|
exports.writeStyleRecord = writeStyleRecord;
|
|
@@ -20,10 +20,9 @@ declare function writeStyleRecord(options: {
|
|
|
20
20
|
readonly istyBuiltIn: number;
|
|
21
21
|
readonly iLevel: number;
|
|
22
22
|
}): Uint8Array<ArrayBuffer>;
|
|
23
|
-
declare function writeFontRecord(name: string, heightTwips: number): Uint8Array<ArrayBuffer>;
|
|
24
23
|
/** Format ([MS-XLS] 2.4.126): a two-byte identifier then the format code as an XLUnicodeString. */
|
|
25
24
|
declare function writeFormatRecord(id: number, code: string): Uint8Array<ArrayBuffer>;
|
|
26
25
|
/** Palette ([MS-XLS] 2.4.188): ccv (MUST be 56) then that many LongRGB colour entries -- write.ts's own colour-interning pass decides whether a workbook needs this record at all, and hands it exactly 56 colours (icv 8 first) when it does. */
|
|
27
26
|
declare function writePaletteRecord(colors: readonly Color[]): Uint8Array<ArrayBuffer>;
|
|
28
27
|
//#endregion
|
|
29
|
-
export { writeCellXfRecord,
|
|
28
|
+
export { writeCellXfRecord, writeFormatRecord, writePaletteRecord, writeStyleRecord, writeStyleXfRecord };
|
package/dist/biff/xf-writer.d.ts
CHANGED
|
@@ -20,10 +20,9 @@ declare function writeStyleRecord(options: {
|
|
|
20
20
|
readonly istyBuiltIn: number;
|
|
21
21
|
readonly iLevel: number;
|
|
22
22
|
}): Uint8Array<ArrayBuffer>;
|
|
23
|
-
declare function writeFontRecord(name: string, heightTwips: number): Uint8Array<ArrayBuffer>;
|
|
24
23
|
/** Format ([MS-XLS] 2.4.126): a two-byte identifier then the format code as an XLUnicodeString. */
|
|
25
24
|
declare function writeFormatRecord(id: number, code: string): Uint8Array<ArrayBuffer>;
|
|
26
25
|
/** Palette ([MS-XLS] 2.4.188): ccv (MUST be 56) then that many LongRGB colour entries -- write.ts's own colour-interning pass decides whether a workbook needs this record at all, and hands it exactly 56 colours (icv 8 first) when it does. */
|
|
27
26
|
declare function writePaletteRecord(colors: readonly Color[]): Uint8Array<ArrayBuffer>;
|
|
28
27
|
//#endregion
|
|
29
|
-
export { writeCellXfRecord,
|
|
28
|
+
export { writeCellXfRecord, writeFormatRecord, writePaletteRecord, writeStyleRecord, writeStyleXfRecord };
|
package/dist/biff/xf-writer.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { RECORD_FORMAT } from "./record-types.js";
|
|
2
|
-
import { horizAlignTokenFor, longRgbBytesOf, packXfDecorationWords, vertAlignTokenFor } from "./xf-colors.js";
|
|
3
|
-
import { BiffWriteError } from "./write-errors.js";
|
|
4
2
|
import { RecordBuilder } from "./builder.js";
|
|
5
3
|
import { writeRecord } from "./record-writer.js";
|
|
4
|
+
import { horizAlignTokenFor, longRgbBytesOf, packXfDecorationWords, vertAlignTokenFor } from "./xf-colors.js";
|
|
6
5
|
import { writeXLUnicodeString } from "./string-writer.js";
|
|
7
6
|
//#region src/biff/xf-writer.ts
|
|
8
7
|
/** Packs the shared trot/indent/fJustLast/fShrinkToFit/iReadOrder word every CellXF and StyleXF opens with, at whatever alc/alcV tokens the caller resolved (xf-colors.ts's horizAlignTokenFor/vertAlignTokenFor -- ALC_GENERAL(0)/ALCV_BOTTOM(2) for a caller passing neither, the identical bytes this word always carried before alignment was modelled). fWrap=0, fJustLast=0, trot=0, cIndent=0, fShrinkToFit=0, reserved1=0, iReadOrder=0: this writer never sets any of them, since ContentSheetCell has no field for wrap/rotation/indent/shrink-to-fit/reading-order. Returns the low 24 bits (alc..iReadOrder); the caller ORs in whatever the next 8 bits mean for its own shape (CellXF's fAtr* flags, or StyleXF's all-zero unused byte). */
|
|
@@ -53,25 +52,6 @@ function writeStyleRecord(options) {
|
|
|
53
52
|
const data = new RecordBuilder().u16(ixfeWord).u8(options.istyBuiltIn).u8(options.iLevel).build();
|
|
54
53
|
return writeRecord(659, data);
|
|
55
54
|
}
|
|
56
|
-
/** Font ([MS-XLS] 2.4.122): height in twips, colour/weight/script/underline defaults for an undecorated font, then the name as a ShortXLUnicodeString whose fHighByte MUST be 1 per the spec regardless of the name's own content ("The fontName.fHighByte field MUST equal 1") -- unlike every other ShortXLUnicodeString this package writes, so it is encoded uncompressed here directly rather than through writeShortXLUnicodeString, whose auto-compression would otherwise write 1-byte-per-character data under a flags byte that has to claim 2 bytes each. */
|
|
57
|
-
const FONT_FLAG_ITALIC_ETC = 0;
|
|
58
|
-
const FONT_COLOUR_AUTOMATIC = 32767;
|
|
59
|
-
const FONT_WEIGHT_NORMAL = 400;
|
|
60
|
-
const FONT_SCRIPT_NORMAL = 0;
|
|
61
|
-
const FONT_UNDERLINE_NONE = 0;
|
|
62
|
-
const FONT_FAMILY_SWISS = 2;
|
|
63
|
-
const FONT_CHARSET_ANSI = 0;
|
|
64
|
-
/** Forces the uncompressed (fHighByte=1) ShortXLUnicodeString encoding a font name MUST use, writing every character as a full 16-bit code unit regardless of whether a narrower encoding would also fit. */
|
|
65
|
-
function writeFontNameString(name) {
|
|
66
|
-
const builder = new RecordBuilder().u8(name.length).u8(1);
|
|
67
|
-
for (let index = 0; index < name.length; index += 1) builder.u16(name.charCodeAt(index));
|
|
68
|
-
return builder.build();
|
|
69
|
-
}
|
|
70
|
-
function writeFontRecord(name, heightTwips) {
|
|
71
|
-
if (name.length > 255) throw new BiffWriteError(`font name ${JSON.stringify(name)} is ${name.length} UTF-16 code units, above the 255 a ShortXLUnicodeString cch can hold`);
|
|
72
|
-
const data = new RecordBuilder().u16(heightTwips).u16(FONT_FLAG_ITALIC_ETC).u16(FONT_COLOUR_AUTOMATIC).u16(FONT_WEIGHT_NORMAL).u16(FONT_SCRIPT_NORMAL).u8(FONT_UNDERLINE_NONE).u8(FONT_FAMILY_SWISS).u8(FONT_CHARSET_ANSI).u8(0).bytes(writeFontNameString(name)).build();
|
|
73
|
-
return writeRecord(49, data);
|
|
74
|
-
}
|
|
75
55
|
/** Format ([MS-XLS] 2.4.126): a two-byte identifier then the format code as an XLUnicodeString. */
|
|
76
56
|
function writeFormatRecord(id, code) {
|
|
77
57
|
const data = new RecordBuilder().u16(id).bytes(writeXLUnicodeString(code)).build();
|
|
@@ -87,4 +67,4 @@ function writePaletteRecord(colors) {
|
|
|
87
67
|
return writeRecord(146, builder.build());
|
|
88
68
|
}
|
|
89
69
|
//#endregion
|
|
90
|
-
export { writeCellXfRecord,
|
|
70
|
+
export { writeCellXfRecord, writeFormatRecord, writePaletteRecord, writeStyleRecord, writeStyleXfRecord };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { r as FormulaSheetContext } from "./ptg-75v7MhaA.js";
|
|
2
1
|
import { t as RecordGroup } from "./substreams-CgTHx5kX.js";
|
|
3
|
-
import {
|
|
2
|
+
import { r as FormulaSheetContext } from "./ptg-75v7MhaA.js";
|
|
3
|
+
import { a as RawConditionalFormatStyle } from "./conditional-format-D4U0eLcY.js";
|
|
4
4
|
import { Color, ContentSheetConditionalFormat, ContentSheetConditionalFormatValue, ContentSheetRange } from "document-schema.js";
|
|
5
5
|
//#region src/workbook/conditional-format-12.d.ts
|
|
6
6
|
type RawCfColor = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { r as FormulaSheetContext } from "./ptg-75v7MhaA.cjs";
|
|
2
1
|
import { t as RecordGroup } from "./substreams-XDNgDvRJ.cjs";
|
|
3
|
-
import {
|
|
2
|
+
import { r as FormulaSheetContext } from "./ptg-75v7MhaA.cjs";
|
|
3
|
+
import { a as RawConditionalFormatStyle } from "./conditional-format-BzTJe8Fm.cjs";
|
|
4
4
|
import { Color, ContentSheetConditionalFormat, ContentSheetConditionalFormatValue, ContentSheetRange } from "document-schema.js";
|
|
5
5
|
//#region src/workbook/conditional-format-12.d.ts
|
|
6
6
|
type RawCfColor = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { r as FormulaSheetContext } from "./ptg-75v7MhaA.cjs";
|
|
2
1
|
import { t as RecordGroup } from "./substreams-XDNgDvRJ.cjs";
|
|
2
|
+
import { r as FormulaSheetContext } from "./ptg-75v7MhaA.cjs";
|
|
3
3
|
import { ContentSheetRange, SheetRuleOperator } from "document-schema.js";
|
|
4
4
|
//#region src/workbook/conditional-format.d.ts
|
|
5
5
|
interface RawConditionalFormatFill {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { r as FormulaSheetContext } from "./ptg-75v7MhaA.js";
|
|
2
1
|
import { t as RecordGroup } from "./substreams-CgTHx5kX.js";
|
|
2
|
+
import { r as FormulaSheetContext } from "./ptg-75v7MhaA.js";
|
|
3
3
|
import { ContentSheetRange, SheetRuleOperator } from "document-schema.js";
|
|
4
4
|
//#region src/workbook/conditional-format.d.ts
|
|
5
5
|
interface RawConditionalFormatFill {
|
package/dist/content.cjs
CHANGED
|
@@ -4,11 +4,13 @@ const require_biff_records = require("./biff/records.cjs");
|
|
|
4
4
|
const require_container = require("./container.cjs");
|
|
5
5
|
const require_units = require("./units.cjs");
|
|
6
6
|
const require_biff_print_setup = require("./biff/print-setup.cjs");
|
|
7
|
-
const require_biff_substreams = require("./biff/substreams.cjs");
|
|
8
7
|
const require_biff_xf_colors = require("./biff/xf-colors.cjs");
|
|
8
|
+
const require_biff_font = require("./biff/font.cjs");
|
|
9
|
+
const require_biff_substreams = require("./biff/substreams.cjs");
|
|
9
10
|
const require_drawing_blips = require("./drawing/blips.cjs");
|
|
10
11
|
const require_drawing_bytes = require("./drawing/bytes.cjs");
|
|
11
12
|
const require_workbook_comments = require("./workbook/comments.cjs");
|
|
13
|
+
const require_workbook_defined_names = require("./workbook/defined-names.cjs");
|
|
12
14
|
const require_workbook_drawing = require("./workbook/drawing.cjs");
|
|
13
15
|
const require_serial = require("./serial.cjs");
|
|
14
16
|
const require_workbook_globals = require("./workbook/globals.cjs");
|
|
@@ -96,15 +98,46 @@ function readXlsContent(bytes, password) {
|
|
|
96
98
|
const globals = require_workbook_globals.readWorkbookGlobals(globalsSubstream.records);
|
|
97
99
|
const blipStore = require_drawing_blips.readBlipStore(concatDrawingGroupBytes(globalsSubstream.records));
|
|
98
100
|
const documentMetadata = metadata === void 0 ? {} : (0, archive_codec.summaryInformationToLayoutMetadata)((0, archive_codec.readSummaryInformation)(metadata));
|
|
101
|
+
const worksheetEntries = globals.sheets.map((entry, sheetIndex) => ({
|
|
102
|
+
entry,
|
|
103
|
+
sheetIndex
|
|
104
|
+
})).filter(({ entry }) => entry.sheetType === SHEET_TYPE_WORKSHEET);
|
|
99
105
|
return {
|
|
100
106
|
kind: "spreadsheet",
|
|
101
107
|
metadata: documentMetadata,
|
|
102
|
-
sheets:
|
|
103
|
-
|
|
104
|
-
sheetIndex
|
|
105
|
-
})).filter(({ entry }) => entry.sheetType === SHEET_TYPE_WORKSHEET).map(({ entry, sheetIndex }) => readSheet(entry, sheetIndex, substreams, globals, blipStore, documentMetadata))
|
|
108
|
+
sheets: worksheetEntries.map(({ entry, sheetIndex }) => readSheet(entry, sheetIndex, substreams, globals, blipStore, documentMetadata)),
|
|
109
|
+
...mapDefinedNames(globalsSubstream.records, globals, worksheetEntries)
|
|
106
110
|
};
|
|
107
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* The document's own `names` array, from the workbook's Lbl records -- absent when the workbook declares no name this reader resolves, matching the schema's optional field.
|
|
114
|
+
*
|
|
115
|
+
* A name's Lbl-scoped sheetIndex is a position in the FULL BoundSheet8 collection, while ContentDefinedNameSchema's scopeSheetIndex names a position in the document's own (worksheet-only) sheets array, so each one is translated through the same filter the sheets themselves went through. A name scoped to a sheet that did not survive the filter -- a chart or macro sheet -- has no scope the schema can express, and is dropped whole rather than re-scoped to a neighbouring index or silently promoted to workbook-global: a wrong scope changes which sheet the name belongs to, not just how it is displayed.
|
|
116
|
+
*/
|
|
117
|
+
function mapDefinedNames(globalsRecords, globals, worksheetEntries) {
|
|
118
|
+
const documentIndexOfSheet = new Map(worksheetEntries.map(({ sheetIndex }, documentIndex) => [sheetIndex, documentIndex]));
|
|
119
|
+
const names = [];
|
|
120
|
+
for (const raw of require_workbook_defined_names.readDefinedNames(globalsRecords, {
|
|
121
|
+
sheets: globals.sheets,
|
|
122
|
+
sheetRanges: globals.sheetRanges
|
|
123
|
+
})) {
|
|
124
|
+
if (raw.sheetIndex === void 0) {
|
|
125
|
+
names.push({
|
|
126
|
+
name: raw.name,
|
|
127
|
+
refersTo: raw.refersTo
|
|
128
|
+
});
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const scopeSheetIndex = documentIndexOfSheet.get(raw.sheetIndex);
|
|
132
|
+
if (scopeSheetIndex === void 0) continue;
|
|
133
|
+
names.push({
|
|
134
|
+
name: raw.name,
|
|
135
|
+
refersTo: raw.refersTo,
|
|
136
|
+
scopeSheetIndex
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return names.length > 0 ? { names } : {};
|
|
140
|
+
}
|
|
108
141
|
/** Every MsoDrawingGroup record's own data, in stream order, concatenated into one Escher byte stream -- the workbook-wide counterpart of a worksheet's own MsoDrawing concatenation (drawing/shapes.ts's own readSheetShapes), carrying the Blip Store rather than any one sheet's shape tree. */
|
|
109
142
|
function concatDrawingGroupBytes(records) {
|
|
110
143
|
const chunks = records.filter((record) => record.type === 235).map((record) => record.blocks[0]).filter((block) => block !== void 0);
|
|
@@ -383,7 +416,8 @@ function mapCell(cell, globals) {
|
|
|
383
416
|
const background = backgroundOf(globals, cell.xfIndex);
|
|
384
417
|
const borders = bordersOf(globals, cell.xfIndex);
|
|
385
418
|
const { alignment, verticalAlignment } = alignmentOf(globals, cell.xfIndex);
|
|
386
|
-
|
|
419
|
+
const font = fontOf(globals, cell.xfIndex);
|
|
420
|
+
if (cell.value.kind === "blank" && background === void 0 && borders === void 0 && alignment === void 0 && verticalAlignment === void 0 && font === void 0) return;
|
|
387
421
|
const formatCode = require_workbook_globals.formatCodeOf(globals, cell.xfIndex);
|
|
388
422
|
const value = resolveValue(cell, formatCode, globals.date1904);
|
|
389
423
|
const mapped = {
|
|
@@ -393,6 +427,7 @@ function mapCell(cell, globals) {
|
|
|
393
427
|
displayText: displayTextOf(value)
|
|
394
428
|
};
|
|
395
429
|
if (formatCode !== void 0) mapped.numberFormatCode = formatCode;
|
|
430
|
+
if (font !== void 0) mapped.font = font;
|
|
396
431
|
if (cell.formula !== void 0) mapped.formula = cell.formula;
|
|
397
432
|
if (background !== void 0) mapped.background = background;
|
|
398
433
|
if (borders !== void 0) mapped.borders = borders;
|
|
@@ -419,6 +454,17 @@ function alignmentOf(globals, xfIndex) {
|
|
|
419
454
|
if (format.alignment.vertical !== void 0) result.verticalAlignment = format.alignment.vertical;
|
|
420
455
|
return result;
|
|
421
456
|
}
|
|
457
|
+
/**
|
|
458
|
+
* A cell's own font, or undefined when the cell states none of its own: the font its XF's ifnt names, diffed against the workbook's own first font (the Normal style's, entry 0 of the font table) so that only properties the cell genuinely differs in survive -- the same default-omission policy alignmentOf applies to ALCGEN/ALCVBOT and the fill reader to FLSNULL. A cell whose XF resolves to no CellFormat at all, or whose font index resolves past the end of the font table, carries no font, matching every other resolveXOf helper's behaviour for an out-of-range index.
|
|
459
|
+
*/
|
|
460
|
+
function fontOf(globals, xfIndex) {
|
|
461
|
+
const format = globals.cellFormats[xfIndex];
|
|
462
|
+
if (format === void 0) return;
|
|
463
|
+
const font = globals.fonts[format.fontIndex];
|
|
464
|
+
const baseline = globals.fonts[0];
|
|
465
|
+
if (font === void 0 || baseline === void 0) return;
|
|
466
|
+
return require_biff_font.contentFontOf(font, baseline, (icv) => require_biff_font.resolveFontColor(icv, globals.palette));
|
|
467
|
+
}
|
|
422
468
|
/** 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). */
|
|
423
469
|
function bordersOf(globals, xfIndex) {
|
|
424
470
|
const format = globals.cellFormats[xfIndex];
|