Skip to content

Changelog

All notable changes to TabularJS. The full history is on GitHub.

Lotus 1-2-3 WK3, WK4 and .123 files now return their data, files with the wrong extension open, and a long list of fixes found by testing real files from Excel, LibreOffice and openpyxl.

  • Lotus 1-2-3 WK3, WK4 and .123 (1-2-3 97 and Millennium): numbers, labels, formula results, error and N/A cells, notes, sheet names, column widths and hidden columns. Formulas return their saved results, as for WK1
  • Wrong extensions: an .xlsx renamed .xls, an .xls saved as .xlsx, or a CSV, HTML or XML 2003 file saved as .xls is parsed as what it really is. options.extension is never overridden
  • Password-protected .xlsx files report Workbook is encrypted with a password
  • SYLK: the encoding option
  • XLSX: shared formulas reached only the first cell of a filled range; every cell now gets its own formula. Inline strings (written by openpyxl and many generators) came back empty, 0 came back as "0", error cells read ##VALUE!, and shared strings kept & and phonetic readings
  • DIF: the last row was duplicated, Excel files came back with as many columns as rows (52 x 52 instead of 52 x 17), and TRUE/FALSE, NA and ERROR came back as numbers
  • SYLK: text with a semicolon (;;) was cut off
  • HTML: only the first <tbody> was read, <th> cells were moved in front of <td> cells, and entities were not decoded in Node.js
  • ODS: comment text leaked into cell values, references to other sheets kept OpenDocument syntax ([$Rates.A1] now reads Rates!A1), and the lines of a multi-line cell were glued together
  • CSV: a byte order mark stayed in the first cell when the file was passed as a path; UTF-16 files with a byte order mark are decoded correctly
  • Node.js: File objects (from FormData in Next.js and other frameworks) failed for .xlsx and .ods
  • 540 automated tests (up from 433)

Fixes found by running the LibreOffice import test corpus against TabularJS.

  • dBase (.dbf): text fields are decoded with the codepage the file declares (Cyrillic, Central European, CJK…), or with options.encoding when given; before, they came out garbled
  • dBase (.dbf): a crafted file could declare millions of records it does not contain and exhaust memory; records are now limited to what the file holds
  • SYLK: files with CR-only line endings (classic Mac Excel) parsed as empty
  • Lotus 1-2-3: formula cells exposed raw token bytes as cells[address].formula; only the calculated value is returned now. String results of formulas no longer include stray header bytes
  • DIF / Numbers: no more debug messages printed to the console while parsing
  • 433 automated tests (up from 425)

Legacy Excel files (.xls) now come through much more complete: formulas that point to other sheets, named ranges, array formulas and external workbooks are decoded, formatted text no longer corrupts the cells after it, and write-protected workbooks open normally.

  • Formulas across sheets in .xls: =Sheet2!A1, ='My Sheet'!A1:B2, ranges over several sheets (=SUM(Jan:Dec!B2)) and references to other workbooks (='[Book2.xls]Prices'!A1). References to deleted sheets become #REF!.
  • Named ranges in .xls: =SUM(Total), built-in names such as Print_Area, names from other workbooks and add-in / user-defined functions (=MYUDF(A1)).
  • Array formulas in .xls: every cell of an array range gets its formula, and array constants such as {1,2;"a",TRUE} are decoded.
  • Encrypted .xls: “read-only recommended” / write-protected workbooks open normally.
  • XLS formulas: 3-D references (tRef3d, tArea3d, tRefErr3d, tAreaErr3d) resolved through the workbook’s SUPBOOK and EXTERNSHEET records
  • XLS formulas: defined names, built-in names, external names and add-in functions (tName, tNameX)
  • XLS formulas: array formulas (ARRAY record) and array constants (tArray)
  • XLS formulas: memory tokens (tMemArea, tMemFunc, …) and the full set of error values (#NULL!, #DIV/0!, #VALUE!, #REF!, #NAME?, #NUM!, #N/A)
  • Encrypted .xls: Office 97 RC4 workbooks protected with Excel’s default password are decrypted; other passwords, RC4 CryptoAPI and XOR obfuscation raise a clear Workbook is encrypted error
  • TypeScript definitions for byte input (Uint8Array, ArrayBuffer), options.extension, worksheetState, gridline, media and definedNames
  • HTML strings and DOM elements as input: tabularjs('<table>...</table>')
  • 425 automated tests (up from 374)
  • XLS text: rich-text and phonetic (Far East) strings broke the shared string table, so every later cell read the wrong text, and strings past the first 8 KB were lost. On 400 real-world workbooks compared with xlrd, value mismatches dropped from about 40% of the files to zero
  • XLS sheets: a chart embedded in a worksheet cut the sheet short, and a chart sheet shifted the data of every following sheet
  • XLS formula results: boolean, error and empty-string results were read as numbers; error cells now read #DIV/0! and so on instead of #ERROR7
  • XLS formulas: zero-argument functions (NOW(), TODAY(), PI()) swallowed the preceding operand
  • XLS formulas: a formula that cannot be decoded exactly now shows the value Excel saved for the cell instead of =ERROR or a wrong formula
  • XLS: inline strings written by older tools (LABEL records) were ignored
  • HTML: every <table> on a page now becomes a worksheet (only the first one was returned before); use tableIndex to pick one
  • CSV: the encoding option was ignored (files were always read as UTF-8)
  • TXT: a delimiter passed for .txt files was overridden by tab
  • Truncated .xls files: files cut short by a download now return the readable part instead of failing with Sector out of bounds

The package now imports correctly in Node.js, accepts raw bytes, and detects the format from the file content. It also adds protection against malicious files and ships a much smaller browser bundle.

import tabularjs from 'tabularjs';
// From fetch, a database blob, an upload stream...
const bytes = new Uint8Array(await (await fetch('/report')).arrayBuffer());
// The format is detected from the content...
const result = await tabularjs(bytes);
// ...or forced explicitly
const csv = await tabularjs(bytes, { extension: 'csv' });
  • tabularjs(uint8Array | arrayBuffer | buffer) input, plus an options.extension override
  • Content-based format detection (magic bytes) when the extension is missing or unrecognized: xlsx, ods, numbers, xls (OLE2), Lotus, dBase, SYLK, DIF, XML, HTML, CSV and TSV
  • Apple Numbers (.numbers) parser (experimental)
  • Zip-bomb protection on every xlsx, ods and numbers entry, and allocation limits on OLE2 containers (crafted .xls protection)
  • 374 automated tests (up from 312)
  • Packaging: the npm package could not be imported in Node.js at all. An exports map now gives Node the ESM source, bundlers a real ES module (dist/index.mjs), and <script> tags the UMD build
  • XLS formulas: rows above 16384 decoded wrong, and ranges in shared formulas derailed parsing
  • XLSX: images and charts referenced with several ../ segments failed to resolve
  • Encoding: Cyrillic (windows-1251) and CJK text files were decoded as latin1 or utf-8, producing garbled text
  • Smaller browser bundle: 512 KiB to 196 KiB. iconv-lite and chardet are now optional dependencies used only in Node.js for legacy DOS codepages

First public release.

  • Parsers for Excel 2007+ (.xlsx), Excel 97-2003 (.xls), OpenDocument (.ods), XML Spreadsheet 2003, CSV/TSV, HTML tables, Lotus 1-2-3, SYLK, DIF and dBase
  • Automatic format detection by extension
  • Formulas, merged cells, styles, comments, named ranges, hidden rows and columns, column widths and row heights
  • Multiple worksheets and worksheet visibility
  • Works in Node.js and the browser; output is directly compatible with Jspreadsheet CE and Pro