npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

xls-codec

v4.15.7

Published

Hand-written reader for the legacy Excel Binary File Format (.xls, BIFF8) as specified by [MS-XLS], mapping a workbook's record stream onto the shared document-schema.js spreadsheet model - the .xls codec for the documents.js family.

Readme

xls-codec

GitHub npm npm version CI

Hand-written reader and writer for the legacy Excel Binary File Format (.xls, BIFF8) as specified by [MS-XLS], mapping a workbook's record stream onto the same document-schema.js spreadsheet model ooxml.js's xlsx support and odf.js's ods support target. Worker-isomorphic: the same code runs under Node and inside a Cloudflare Workers isolate.

A .xls file is not one format but two nested ones. The outer container is an [MS-CFB] compound file — the same "filesystem in a file" that carries .doc and .ppt — holding a stream named Workbook. Inside that stream is BIFF8: a flat sequence of records, each a two-byte type, a two-byte size, and that many bytes of data, organised into substreams delimited by BOF/EOF. This package leaves the outer layer to archive-codec's bounded CFB reader and writer and implements the inner one, from the record framing up to a ContentDocument and back.

Status

Under active development, with real, tested read and write support. Built and shipped:

  • Record framing (src/biff/records.ts, src/biff/record-writer.ts) — the three-component record structure of [MS-XLS] 2.1.4 in both directions, with the 8224-byte data ceiling enforced and every malformed or oversized stream thrown on rather than silently truncated or split into a Continue chain the writer does not implement.
  • Continuation-aware cursor and strings (src/biff/cursor.ts, src/biff/strings.ts, src/biff/string-writer.ts) — Continue records ([MS-XLS] 2.4.58) joined per the rules of the record being continued on read, including the case a naive reader gets wrong: an XLUnicodeRichExtendedString ([MS-XLS] 2.5.293) resuming after a boundary re-states its own fHighByte flag, which may differ from the flag the string started with. All three string shapes (XLUnicodeString, ShortXLUnicodeString, XLUnicodeRichExtendedString) are read and written, compressed (one byte per UTF-16 code unit) whenever every character allows it and uncompressed otherwise.
  • Workbook globals, read (src/workbook/globals.ts) and write (src/workbook/globals-writer.ts) — BoundSheet8 (sheet names, tab order, hidden state, type, and substream offsets), SST with its Continue chain on read, Format (custom number-format codes), Font, XF's fixed prefix plus its trailing CellXF/StyleXF fill/border payload in both directions (src/biff/xf-colors.ts's shared bit-layout packing/unpacking; see Cell decoration), Palette in both directions, the fifteen mandatory built-in Style records, and Date1904.
  • 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), and the cell-value family: Number, BoolErr, LabelSst, Blank, and now Formula (see Formula writing) on write (MulBlank, RK, MulRk, and Label stay read-only — see below).
  • 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 for the exact scope boundary and why it stops at one sheet.
  • 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).
  • Number-format classification and date serials (excel-number-format, 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.
  • 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.
  • Schema mappingreadXlsContent/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.
  • Document metadatatitle/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).
  • 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).
  • 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).
  • 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).
  • 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.
  • 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.
  • 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.
  • Conditional formatting, the CF12-era spellings, written (ExaDev/documents.js#1186) — every rule variant the schema models beyond cellIs writes as one CondFmt12 plus one CF12 record (workbook/conditional-format-write.ts): the text family as genuine ct 0x02 formula conditions in Excel's own generated shape (the search text as the formula's PtgStr operand, the identical spelling the reader's readCfTextFilterRule recovers it from), the operand-free family, top10, aboveAverage, timePeriod, and the three visual-scale rules (colorScale/dataBar/iconSet) through their CFGradient/CFDatabar/CFMultistate payloads with CFColor RGB triples and CFVO thresholds compiled through the same compileFormulaText cell formulas use. A cellIs rule still writes as a base CondFmt/CF pair; the two families share one sheet, base groups first. ipriority is minted (smallest unused positive integer) when a rule states no priority, since [MS-XLS] 2.4.43 requires it present and unique. What genuinely has no BIFF8 spelling throws a BiffWriteError naming it — see the Writer scope table below.
  • Charts, drawings, and images, read; images and non-chart embedded objects, now also written (ExaDev/documents.js#924, #1186) — 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. The write direction (src/drawing/escher-writer.ts, src/workbook/drawing-writer.ts) builds the identical Escher container tree back out — the workbook-wide Blip Store deduplicated by image bytes, a per-sheet shape tree, and the Obj records pairing each shape with what it holds — and a non-chart embedded object's own document rides inside a genuine [MS-XLS] Embedding Storage (src/workbook/embedded-object.ts); see Images and embedded objects, written.

Verified primarily by round trip (src/write.test.ts, plus a dedicated test/workers/write.test.ts proving the whole write path inside a real workerd isolate, not just Node): build a ContentDocument, write it, read it back through this package's own independently-pinned reader, and check the result. Every record's own byte layout is additionally cited to its [MS-XLS] section in the writer's source, matching the reader's own convention.

Writer scope

What writeXlsContent/writeXls cover: every ContentCellValue kind a real .xls can hold (number, percentage, currency, boolean, date, time, dateTime, string, error; an empty cell is written as a Blank record when it carries formatting and as nothing at all when it does not — see below), merged ranges (colSpan/rowSpan), row heights and hidden rows, column widths and hidden columns, multiple sheets, explicit and default number formats, a shared string table deduplicated across the whole workbook, every field of a sheet's own print settings, a cell's own alignment, a cell's own background fill and per-side borders (see Cell decoration), a same-sheet formula (see Formula writing), a cell's own comment text and author (see Cell comments, written), a sheet's own images and non-chart embedded objects (see Images and embedded objects, written), a sheet's own data validations (one Dval wrapper plus one Dv record per rule, every schema field through the same compileFormulaText formula compiler cell formulas use), and a sheet's own conditional formats in full: a cellIs rule as one CondFmt plus one CF record (its style's text colour and solid background encoded as a DXFN through the workbook's own palette), every other variant the schema models as one CondFmt12 plus one CF12 record (the CF12-era spelling, its thresholds, template parameters, and CFColor triples written as the exact inverse of the reader's own walk). What it deliberately does not:

| Not written | Why | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Formula records for a 3D (cross-sheet or external-workbook) reference, an array-constant literal or CSE array formula, a defined name, or a call to a function outside Ftab's own vocabulary | Same-sheet formulas write (see Formula writing); these four constructs each throw a BiffWriteError naming the construct instead, rather than emit a token stream the writer cannot prove round-trips. A 3D reference needs a SupBook/ExternSheet pair this writer only ever mints today for the two built-in print-settings names (see Print settings), not for an arbitrary formula; an array constant/CSE formula needs a PtgExtraArray/Array-record trailer this writer does not build; a defined name has nowhere to resolve against, the same gap the Lbl row below describes; and Excel 2007+ added many worksheet functions BIFF8's own Ftab enumeration never named, resolved through a PtgNameX/add-in mechanism this writer does not implement. | | Per-cell font | The reader does not read one back: ContentSheetCell has no cell-level font field at all (ooxml.js's xlsx reader makes the identical font-scope choice), so writing a real value here would be unverifiable by round trip. Every XF this writer emits still references the same single font. | | MulBlank/RK/MulRk | Pure compaction optimisations over information a plain Blank/Number/LabelSst/BoolErr record already carries losslessly. (Blank itself is written, for a formatted empty cell — see the empty row below.) | | An empty-kind cell carrying no formatting | Written as nothing at all, which is what round-trips: content.ts's reader drops an unformatted blank cell it reads, and a merged range's empty anchor is independently reconstructed from MergeCells alone. A formatted one (a fill, a border, or a non-default alignment) is the opposite case — its formatting exists only in the XF a cell record names, so it gets a real Blank record ([MS-XLS] 2.4.20) and round-trips with that formatting intact. | | A 'chart'-kind embedded object | Writing one means embedding a genuine BIFF8 chart substream — the whole [MS-XLS] chart grammar its series data links drive — which is a chart engine of its own rather than a container for the flattened series table the schema carries; writeXlsContent throws BiffWriteError naming it rather than approximating a chart no BIFF8 record could actually redraw. A sheet's own images and every other embedded-object kind (wordprocessing/presentation/spreadsheet/drawing/formula) do write — see Images and embedded objects, written. | | Defined names (Lbl) | Read only for the two built-in ones a sheet's print range and repeated header bands live in (Print settings); a user-defined name has nowhere to land in document-schema.js's spreadsheet model, so there is no round trip to verify a writer for it against. | | A data validation whose formula text compileFormulaText cannot encode | Every other rule shape writes (see the lead paragraph); a formula naming a construct outside the same-sheet Ptg vocabulary the cell-formula writer supports throws the identical BiffWriteError that cell formula already does, rather than emit a token stream this writer cannot prove round-trips. A comparison-typed rule carrying no operator, or a two-operand operator (between/notBetween) carrying fewer or more than its two formulas, is a malformed model and throws naming it. | | The conditional-format shapes the CF12 record vocabulary itself cannot state | Every rule variant the schema models now writes (workbook/conditional-format-write.ts): a cellIs rule as one CondFmt plus one CF record -- its operator, both formula operands through compileFormulaText, and its style's text colour and solid background as a DXFN resolved through the workbook's own palette, the write-side inverse of readCondFmtGroup/readCf/parseDxfStyle -- and every other variant as one CondFmt12 plus one CF12 record, the CF12-era spelling (ct 0x02 formula condition carrying the search text for the text family, ct 0x05 filter templates for the operand-free/top10/aboveAverage/timePeriod family, CFGradient/CFDatabar/CFMultistate for the visual-scale rules), with an absent priority minted the smallest unused ipriority because [MS-XLS] 2.4.43 requires the field present and unique. What the record vocabulary genuinely diverges from the schema on throws a BiffWriteError naming it rather than being silently dropped or approximated: a timePeriod naming thisYear/lastYear/nextYear (ODF's own calcext extension values -- no icfTemplate names a year-scoped period); an iconSetType outside [MS-XLS] 2.5.36's seventeen built-in sets (a producer-extensible vocabulary with no iIconSet byte); an icon-set threshold count disagreeing with the set the rule names (cStates is pinned to the set's own icon count); stopIfTrue on a colour-scale/data-bar/icon-set rule (2.4.43 pins fStopIfTrue to zero for the visual-scale types); an aboveAverage standard-deviation count above 2 (2.5.23's iParam table admits 0/1/2); and two rules declaring the same priority (contradictory ordering rather than something to renumber). A cellIs rule's own priority/stopIfTrue/source still have no base-CF field and are not written; the CFEx compatibility spelling -- a legacy CF-plus-extension pair keeping a pre-2007 Excel able to evaluate a rule -- is not written either, the CF12 spelling being what this package's own reader resolves either way. | | A comment's replies or createdAt | A cell's text and author write (see the Status list above); legacy BIFF8's Note/Obj/Txo triple has no threading and no per-comment timestamp at all, the same gap src/workbook/comments.ts's own read side already documents, so there is nothing for these two fields to round-trip against. | | A page size no iPaperSize code names | Written as [MS-XLS] 2.4.257's own custom-paper value rather than as a named paper it is not — the dimensions themselves are unwritable, since Setup addresses paper only by code. See Print settings. | | Window1/Window2, CodePage, Index/DBCell, the legacy interface records (InterfaceHdr, WriteAccess, …) | UI and interoperability bookkeeping [MS-XLS]'s own grammar names in the globals/worksheet substreams alongside the content-carrying records above, not data. Index/DBCell specifically is a pure cell-lookup performance optimisation (see [MS-XLS]'s own "Retrieval of Last-Calculated Cell Values Without Loading Cell Table") that this reader — and Excel's own reader — does not require to find a cell; real, well-established minimal BIFF8 writers (e.g. Python's xlwt) omit the same set and produce files Excel opens correctly. The calculation-state records (CalcCount, CalcRefMode, CalcIter, CalcDelta, CalcSaveRecalc) sat in this row until print settings needed them — see Print settings for why the writer emits them now. | | Continue-chain splitting | A record whose data would exceed the 8224-byte single-record ceiling ([MS-XLS] 2.1.4) — an extremely long shared string, an enormous shared string table, or thousands of merged ranges in one sheet — is refused with a thrown BiffWriteError rather than silently split across Continue records. |

Column widths round-trip to the nearest pixel Excel's own integer-pixel-grid quantization allows (matching the read direction's own "honestly approximate" contract, units.ts), never narrower than requested. A .xls cell outside BIFF8's own grid (65536 rows, 256 columns) is refused rather than silently wrapped or truncated.

Read-side gaps

Each deliberate rather than overlooked:

  • Formula expressions, including shared, array, and external-reference formulas. A Formula record's compiled Ptg token stream (src/biff/ptg.ts) is walked and rebuilt into real formula text — literal operands, cell/range references ($-qualified, including 3D cross-sheet references), every arithmetic/comparison/unary/percent operator, explicit parentheses, and both fixed- and variable-arity function calls, resolved by name against [MS-XLS]'s own built-in function table (src/biff/ptg-functions.ts, covering the whole published table — [MS-XLS] 2.5.198.17 — cited to that table's own iftab index; PtgFunc's fixed argument count is a curated subset of it, since PtgFunc's own token carries no count and only a function [MS-XLS]'s grammar states a fixed, non-optional arity for is resolved through it, empirically confirmed against real LibreOffice-written BIFF8 rather than assumed from the grammar alone). Three constructs the read side used to leave ContentSheetCell.formula silently absent for are now resolved: a shared formula — a PtgExp token is joined (src/workbook/sheet.ts's collectFormulaGroups) against the ShrFmla record that follows its group's base Formula record, and the shared expression's own relative tokens (PtgRefN/PtgAreaN, [MS-XLS] 2.5.198.88/2.5.198.31) are re-expanded for each referencing cell's own row/column, including the format's own sheet-edge wraparound rule, while any ordinary (non-relative) token the same expression carries is resolved once and reused unchanged for every referencing cell; an array (CSE) formula — the same PtgExp join against an Array record instead, its expression returned exactly as parsed, with no formula-bar bracing added (Excel's own {...} CSE braces are display syntax the formula-bar shows for an array-entered cell, never characters written into the formula itself — matching ooxml.js's own xlsx convention for the identical construct); and a PtgArray array-constant literal ({1,2,3}-style, whether inside an array formula or an entirely ordinary one like =SUM({1,2,3})) — this bracing genuinely is real, retypeable formula syntax, unlike CSE bracing, and its values are read from the token's own PtgExtraArray trailer rather than left unresolved. A 3D reference's sheet name resolves through EXTERNSHEET and SupBook for both a self-referencing workbook and a genuinely external one — SupBook's virtPath (the external workbook's own path, decoded for the common relative-path forms: same-drive, startup, alternate-startup, and library-relative) and rgst (its sheet names) recover a real [Workbook.xlsx]Sheet1-style label wherever they can, and that label is trusted as real formula text only when both halves were genuinely recovered from the file. An absolute drive volume, a UNC share, or a transfer-protocol URL in virtPath; a virtPath whose final file-path segment carries a bracket character anywhere in it, not only one already shaped like the bracketed sheet-name form ([Book.xlsx]Sheet1) — a bracket-named file is legal per [MS-XLS]'s own grammar, and a bracket's position within that final segment does not settle whether it is genuinely that form: the grammar's own bracketed alternative opens with a literal [ before any directory at all, so a workbook sitting in a subdirectory ([sub + a directory separator + Book.xlsx]Sheet1) puts its closing bracket, non-leading, in the very same final segment this reader isolates once split on the separator — a real instance of the bracketed form just as much as the leading-bracket case ([Book.xlsx]Sheet1 with no directory at all) is; a bracket elsewhere in an otherwise plain, single-directory file name (My[Draft].xlsx) is the case this reader can actually rule the bracketed form out for, since with no directory separator to split on, that whole segment's own leading character settles it — but returning it as a plain file name would still collide with this reader's own [${fileName}] wrapping of the resolved label, producing a raw, unbalanced bracket inside what should read as one clean [Book.xlsx]-style pair — so the whole segment is declined regardless of where the bracket falls or whether this reader could rule the bracketed form out, trading a known false negative for never emitting a mangled label; a SupBook that is a DDE/OLE data source, an add-in, a same-sheet, or an unused link in the first place; or a sheet index [MS-XLS] itself marks unresolvable or workbook-level — none of these has a real name to recover, and each leaves the WHOLE containing formula unresolved, exactly like any other construct this reader cannot turn into real formula text, rather than writing a fabricated placeholder into what a spreadsheet application would otherwise treat as live formula content. What remains genuinely unresolved for the same reason, still leaving formula absent for that cell: a PtgTbl data table, a defined name (PtgName/PtgNameX, for the same reason Lbl is not read at all yet — see below), and a natural-language "Elf" reference.
  • Cell decoration and alignment resolved for real; font still not. XF's trailing CellXF payload's fill pattern/colours and per-side border style/colour are read and resolved through the Palette record (or the default colour table when one is absent) — see Cell decoration for the full mapping, including every named pattern beyond solid, and how this was verified against real LibreOffice-produced BIFF8. The same payload's leading word is resolved too — see Cell alignment for the full alc/alcV mapping. Font records are not read at all: ContentSheetCell has no cell-level font field, and ooxml.js's xlsx reader makes the identical scope choice.
  • Print settings, resolved for real. Every field of ContentSheetPrintSettings is read from the records that carry it, with Excel's own "Normal" preset standing in per field for whatever the file leaves unstated — see Print settings for the record map, the two of BIFF8's own conditional rules that decide how to read Setup, and the three things that genuinely do not come through.
  • Cell comments, resolved for real (ExaDev/documents.js#949). A legacy BIFF8 comment is split across three record kinds a single self-contained xlsx <comment> element never needs: a Note record ([MS-XLS] 2.4.179, wrapping a NoteSh structure) anchors the comment to a cell and names its author, but carries no text of its own; the text lives in a Txo record ([MS-XLS] 2.4.329), its characters carried across trailing Continue records; the two are joined through an Obj record ([MS-XLS] 2.4.181) whose FtCmo names an object id and type, the Note's own idObj field naming that same id, and a Txo always immediately following the Obj record whose shape it belongs to (src/workbook/comments.ts). Read in one independent pass over the whole worksheet substream rather than assuming stream order between a Note and the Obj+Txo pair it names -- both are resolved regardless of which comes first. Legacy BIFF8 has no threading and no per-comment timestamp at all (unlike xlsx's own [MS-XLSX] threaded-comments extension), so ContentSheetCellComment.replies and .createdAt are never populated from an .xls source; rich per-character formatting within a comment (TxORuns) is skipped rather than modelled, the same scope limit this reader already applies to the shared string table's own rich runs, since ContentSheetCellComment.text is a plain string with nowhere to carry them.
  • Data validation, resolved for real (ExaDev/documents.js#1098). Dv ([MS-XLS] 2.4.95) is a fixed, published binary structure -- a bit-packed flags DWORD (valType/errStyle/fAllowBlank/fShowInputMsg/fShowErrorMsg/typOperator), four XLUnicodeStrings (prompt/error titles and messages), one or two DVParsedFormula structures, and a trailing SqRefU range list -- read directly against that spec (src/workbook/data-validation.ts), unlike calcext:condition's own LibreOffice-source-transcribed mini-language odf.js's equivalent reader needed. A DVParsedFormula's own rgce is the identical Ptg token grammar a cell's Formula record carries, so its formula text is recovered with the same parseFormulaText this reader already uses for ordinary cell formulas. DVal ([MS-XLS] 2.4.96), the record that precedes a sheet's own Dv collection, carries only input-window UI state (position, drop-down Obj reference) with no counterpart in ContentSheetDataValidationSchema and is not read for its own fields.
  • Charts, drawings, and images, resolved for real (ExaDev/documents.js#924). A worksheet's own MsoDrawing records ([MS-XLS] 2.1.7.20.3-adjacent, RECORD_MSODRAWING) carry MS-ODRAW (Escher) bytes that split across as many records as the drawing needs and simply concatenate, in stream order, into one continuous Escher record tree (src/drawing/escher.ts, reading [MS-ODRAW]'s own OfficeArtRecordHeader/container-vs-atom framing directly against its spec rather than from memory). Within that tree, src/drawing/shapes.ts walks the DgContainer/SpgrContainer/SpContainer hierarchy for each real top-level shape's own type, id, and cell anchor (ClientAnchorSheet, [MS-XLS] 2.5.163 — one Sp atom's recInstance and ClientAnchor's corner-cell-plus-fractional-offset pair, 1/1024ths of a cell's width and 1/256ths of its height), skipping the invisible "patriarch" group root every drawing wraps its real shapes in. A picture shape's own Opt property table names a pib — a 1-based index into the workbook-wide Blip Store (OfficeArtBstoreContainer, read once from the globals substream's own MsoDrawingGroup stream, src/drawing/blips.ts) — resolved to real image bytes for a PNG or JPEG blip (OfficeArtBlipPNG/OfficeArtBlipJPEG's own literal file bytes, past their rgbUid header); a DIB/EMF/WMF/PICT/TIFF blip is recognised but produces no image, since ContentImageBlockSchema.format has no lossless slot for any of them. Since neither an Escher shape nor a MsoDrawing record names what kind of object it actually holds, each shape is paired 1:1, in document order, with the worksheet substream's own Obj records (skipping any Note-type Obj src/workbook/comments.ts already accounts for) — the same positional correlation every other real BIFF8 reader relies on. An Obj naming a chart locates the chart's own nested BOF(dt=0x0020)/EOF substream — which sits INSIDE the worksheet substream, immediately after the shape that anchors it ([MS-XLS] "Chart Area") — via src/biff/substreams.ts's own stack-based substream nesting (a genuinely new capability this issue needed: splitSubstreams previously treated any BOF met mid-substream as ending it outright, which would have silently dropped every worksheet record written after an embedded chart). src/workbook/chart.ts reads that substream's Series/AI (BRAI)/SeriesText records — a series' own name and its category/value data links — resolving each link's value either from the chart's own on-disk SERIESDATA cache (Dimensions/SIIndex/Number/BoolErr/Blank/Label, present when the chart's data lives on another sheet or is genuinely external) or, for the common case of a chart plotting its own sheet's cells, by resolving the AI's PtgArea3d/PtgRef3d reference token directly against that sheet's own already-mapped cells — and lands as a ContentEmbeddedObject with objectKind: 'chart', its cached series/category table flattened into a small one-sheet spreadsheet ContentDocument, the identical shape ooxml.js's own xlsx/pptx chart reading already produces (ExaDev/documents.js#719): document-schema.js has no richer, chart-type-aware object model yet, so this reader targets the fallback that exists rather than inventing one. Every other recognised shape (an autoshape, a text box, a line, a group) lands as objectKind: 'drawing', a single-page ContentDocument holding one sized-and-positioned ContentShape with no text content modelled. Not modelled at all: a shape's own rotation, fill/line formatting, or text runs; a group's own nested transform (a nested nested group's shapes are still recovered, cell-anchored, but a group's own rotation/scale is not composed into its children's positions); and a chart's own type, axes, and legend, which document-schema.js's flattened-table representation has no field for regardless of what this reader could recover.
  • Conditional formatting, base rules resolved for real (ExaDev/documents.js#1102). A CondFmt record ([MS-XLS] 2.4.56) marks the start of one to three CF records ([MS-XLS] 2.4.42) sharing one cell-range list, read together as a group (src/workbook/conditional-format.ts) since a CF's own ranges live on its parent CondFmt, not on itself. Base BIFF8 (Excel 97) conditional formatting has exactly two rule shapes: a comparison ("Cell Value Is") condition, promoted to ContentSheetConditionalFormat's cellIs variant with its formula(s) recovered through the same parseFormulaText this reader already uses for cell formulas and Dv; and a formula condition, left unpromoted for the same "no closed-form structure without a general formula engine" reason data-validation.ts and ooxml.js's own xlsx cfRule reading already draw. A rule's resulting style -- font colour and fill background -- comes from its CF's own DXFN structure, resolved through the identical icv/Palette colour machinery a regular cell's fill and border already use (Cell decoration); a DXFN naming a user-defined number format (DXFNumUsr) degrades that one rule's style to absent rather than risk misreading the font/fill fields that follow it, since [MS-XLS]'s own prose for DXFNumUsr's length field does not settle whether it counts itself. Every richer rule type Excel 2007+ added has no representation in the base CF record at all; it rides a CF12 record instead, or, for a rule Excel keeps expressible as a legacy formula condition for pre-2007 readers, a CFEx extension record that attaches richer metadata to an ordinary CondFmt-owned CF without promoting it into a CF12 record at all.
  • Conditional formatting, CF12's colour scale/data bar/icon set rules resolved for real (ExaDev/documents.js#1104). CF12's own ct field picks one of six rule shapes ([MS-XLS] 2.4.43); this reader promotes the three that share a genuine array-of-thresholds building block already modelled in document-schema.js -- colour scale, data bar, and icon set (src/workbook/conditional-format-12.ts) -- each reading an array of CFVO threshold objects (the identical num/percent/max/min/formula/percentile vocabulary ContentSheetConditionalFormatValueSchema already carries for ooxml.js's xlsx reader and odf.js's ods reader) paired with CFColor values. A CFColor naming an indexed colour resolves through the same icv/Palette machinery as base CF's own DXFN, then Excel's own TintAndShade model applies the colour's own tint/shade Xnum (xf-colors.ts's applyTint); one naming a plain RGB triple resolves directly, tint included; one naming an automatic or theme colour degrades the whole rule to absent, since this package has no BIFF8 Theme reader to resolve a theme reference against. Icon set's own iIconSet byte maps to the identical ECMA-376 ST_IconSetType vocabulary (3Arrows, 3TrafficLights1, 5Quarters, and the rest of the seventeen built-in sets) ooxml.js's xlsx cfRule reading already carries as a plain string. A record longer than the 8224-byte single-record ceiling continues onto ContinueFrt12 rather than the plain Continue every other record in this package joins against; groupRecords (src/biff/substreams.ts) strips its own restated FrtRefHeader before joining it.
  • Conditional formatting, CF12's filter-dispatched template rules resolved for real (ExaDev/documents.js#1106). ct 0x05 ("filter") dispatches further through icfTemplate, alongside a 16-byte CFExTemplateParams block always present regardless of ct. Of that block's five variants, only two need real parsing -- CFExFilterParams (top10's own fTop/fPercent/iParam) and CFExAveragesTemplateParams (the aboveAverage family's own standard-deviation count) -- since CFExDefaultTemplateParams (duplicateValues/uniqueValues/the four blank/error conditions) is 16 reserved bytes and CFExDateTemplateParams's own dateOp field is a fixed restatement of icfTemplate for all ten date/time periods, so both dispatch directly off icfTemplate with no further byte reading. Unlike colour scale/data bar/icon set, [MS-XLS] does not force a ct 0x05 rule's own cbDxf to zero, so its DXFN12 can carry a genuine font/fill override; that structure resolves through the identical parseDxfStyle base CF's own DXFN already uses. A malformed iParam of zero degrades a top10 rule to absent rather than promoting a rank ContentSheetConditionalFormatSchema itself requires to be positive.
  • Conditional formatting, containsText/notContainsText/beginsWith/endsWith and CFEx's own legacy-CF extension resolved for real (ExaDev/documents.js#1100). icfTemplate 0x0008 ("Contains text") is the one filter-dispatched template that is NOT actually a ct 0x05 rule: CFExTextTemplateParams carries only ctp, naming which of the four text sub-types a rule is, and neither it nor ct 0x05's own CFFilter rgbCT has anywhere to carry the literal search text (confirmed against a second, independent transcription of both structures -- [kinkou/unxls](https://github.com/kinkou