Compare commits

...

17 Commits

Author SHA1 Message Date
1517aa1bca make_csv_row: Always quote fields with CR or LF 2025-09-22 01:27:06 -04:00
b4bb01c731 XLSX shared formula shift nit
- hid ODS number format warning behind WTF (fixes #3324 h/t @gboysko)
2025-08-27 21:46:09 -04:00
4495a9253e XLSB new records (fixes #3286 h/t @travisskyles) 2025-04-30 13:21:01 -04:00
0e4eb976e1 export nits
- XLSX write dense mode null check (fixes #3261 h/t @mbornstein)
- Use `subarray` for Uint8Array/Buffer ops (h/t @hardonthebeat)
2025-02-22 15:18:51 -05:00
9c3853ba25 fix: missing break condition in make_json_row 2025-01-10 16:26:17 +05:30
318e2319ee ODS parse hyperlink tooltips 2024-11-10 21:27:04 -05:00
6c0f950f83 feat: Add Sheet Protection for XLS (BIFF8) (#3202)
Fixes #3201

Reviewed-on: sheetjs/sheetjs#3202
Co-authored-by: Lucas Picchi <lucas.picchi@exeo.com.ar>
Co-committed-by: Lucas Picchi <lucas.picchi@exeo.com.ar>
2024-10-26 20:37:20 +00:00
235ed7ccfb update CONTRIBUTING.md (fixes #3233 h/t @Akxe) 2024-10-21 09:43:40 -04:00
2d6c821261 Parse DIF-esque CSV (fixes #3230 h/t @lowkeyfish) 2024-10-02 01:04:56 -04:00
36debb0eaa RollupJS workaround (fixes #3219 h/t @lvzhenbo) 2024-09-19 19:45:18 -04:00
df48a059c3 Ignore negative sign for a symmetric rounding. 2024-09-18 17:31:02 -07:00
647cdb89f5 Add test files to .gitignore 2024-09-09 18:14:32 -03:00
6912bdb2d4 fix: Add DenseSheetData type (#3195) 2024-09-05 10:25:52 +08:00
5550b90704 fix: infinite loop due to hidden row in XLSX.stream.to_json (#3178)
**Title:**

Fix for Incorrect Row Indexing and Infinite Loop in stream.to_json Function

**Description:**

This pull request addresses two key issues in the `stream.to_json` function of the SheetJS library:

1. **Infinite Loop**: Previously, when a row was hidden, the function skipped processing the current row without incrementing the row counter `R`, resulting in an infinite loop during execution.

2. **Incorrect Row Indexing**: The row index was incorrectly accessed using a one-based index, whereas the actual row index was zero-based. This led to unintended hidden rows being included in the stream and skipping of subsequent non-hidden rows.

**Changes:**

- Modified the hidden row check to use the correct zero-based indexing
- Incremented `R` after identifying a hidden row to ensure the loop progresses to the next row.
- Ensured that the stream correctly processes all rows, skipping hidden rows without affecting subsequent rows.

**Testing:**

- Validated that the `stream.to_json` function no longer enters an infinite loop when encountering hidden rows.
- Confirmed that the correct rows are processed and pushed to the stream, with hidden rows being appropriately skipped.

**Impact:**

This fix enhances the reliability of the SheetJS library, ensuring accurate data streaming from spreadsheets without infinite loops or incorrect row handling.

**Checklist:**

- [x] Code changes have been tested locally.
- [x] The changes adhere to the project's coding standards and guidelines.

**Additional Information:**

These changes are crucial for maintaining the library's functionality in production environments, where precise data handling is essential.

**References:**

Closes #3176.

---

Reviewed-on: sheetjs/sheetjs#3178
Co-authored-by: deepak-negi-web <deepak-negi-web@noreply.git.sheetjs.com>
Co-committed-by: deepak-negi-web <deepak-negi-web@noreply.git.sheetjs.com>
2024-08-21 05:34:31 +00:00
9368a85b5f XLSX encoded entities (fixes #3177)
- HTML DOM ingress support formulae (`data-f`)
- Sheet Visibility for ODS / FODS (fixes #3162)
2024-08-19 12:43:37 -04:00
2669c7cf1c actually remove test_files submodule 2024-07-19 14:57:19 -04:00
b61eed74fc do not bail when XLS properies cannot be parsed 2024-07-17 14:23:02 -04:00
43 changed files with 383 additions and 176 deletions

2
.gitignore vendored

@ -39,3 +39,5 @@ tmp
*.sheetjs
*.exe
*.img
test_files.zip
test_files/

4
.gitmodules vendored

@ -1,4 +0,0 @@
[submodule "test_files"]
path = test_files
url = https://github.com/SheetJS/test_files
ignore = dirty

@ -4,6 +4,11 @@ This log is intended to keep track of backwards-incompatible changes, including
but not limited to API changes and file location changes. Minor behavioral
changes may not be included if they are not expected to break existing code.
* Sheet Visibility for ODS / FODS (h/t @edemaine)
* HTML DOM ingress support formulae (`data-f`)
* Proper handling of XLSX encoded entities (h/t @inreoh)
* Proper handling of invalid DIF sheets that match heuristics (h/t @lowkeyfish)
## v0.20.3
* Correct parsing of NUMBERS and ODS merge cells (h/t @s-ashwin)

@ -1,69 +1,74 @@
# Contributing
The SheetJS Libraries should be free and clear to use in your projects. In
order to maintain that, every contributor must be vigilant.
SheetJS CE should be free and clear to use in your projects. To ensure that
remains true, each contributor must be vigilant and each contribution must be
carefully scrutinized from a technical and legal perspective.
There have been many projects in the past that have been very lax regarding
licensing. We are of the opinion that those are ticking timebombs and that no
commercial product should depend on them.
Many commercial products and open source projects have been very lax regarding
licensing. They are ticking timebombs that no commercial product should use.
# Required Reading
## Required Reading
These are pretty short reads and emphasize the importance of proper licensing:
- https://github.com/jazzband/tablib/issues/114 (discussion of other tools)
- https://web.archive.org/web/20200916173942/https://github.com/jazzband/tablib/issues/114
- https://web.archive.org/web/20120615223756/http://www.codinghorror.com/blog/2007/04/pick-a-license-any-license.html
- https://web.archive.org/web/20240909210554/https://github.com/stephen-hardy/xlsx.js/issues/8
# Raising Issues
## Raising Issues
Issues should generally be accompanied by test files. Since github does not
support attachments, the best method is to send files to <sheetjs@gmail.com>
(subject line should contain issue number or message) or to share using some
storage service. Unless expressly permitted, any attachments will not be
shared or included in a test suite (although I will ask :)
Issues should generally be accompanied by test files. It is strongly recommended
to use the [issue tracker](https://git.sheetjs.com/sheetjs/sheetjs/issues).
If sending email to a gmail account is problematic, the <dev@sheetjs.com> email
inbox is self-hosted.
If they cannot be shared publicly, please send files to <oss@sheetjs.com>
(subject line should contain issue number or message) or share links to files
hosted on a storage service. Unless expressly permitted, attachments will not be
shared outside of SheetJS LLC or included in a test suite.
# Opening Pull Requests
If a NDA is required, please send an email to <oss@sheetjs.com> with subject
line "Non-Disclosure Agreemeant Request".
[Squash commits](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History)
before opening a pull request, If the pull request addresses documentation or
demos, add `[ci skip]` in the body or title of the commit message to skip tests.
# Pre-Contribution Checklist
## Opening Pull Requests
Please raise an issue before opening pull requests. It is easy to solve a
specific problem without considering the full context or implications.
## Pre-Contribution Checklist
Before thinking about contributing, make sure that:
- You are not, nor have ever been, an employee of Microsoft Corporation.
- You have not signed any NDAs or Shared Source Agreements with Microsoft
Corporation or a subsidiary
Corporation or a subsidiary.
- You have not consulted any existing relevant codebase (if you have, please
take note of which codebases were consulted).
If you cannot attest to each of these items, the best approach is to raise an
issue. If it is a particularly high-priority issue, please drop an email to
<sheetjs@gmail.com> and it will be prioritized.
issue. If it is a particularly high-priority issue, please drop an email to
<support@sheetjs.com> and it will be prioritized.
# Intra-Contribution
## Intra-Contribution
Keep these in mind as you work:
- Your contributions are your original work. Take note of any resources you
- Your contributions are your original work. Take note of any resources you
consult in the process. Be extra careful not to use unlicensed code on the
Internet or code generated by a large language model or other AI tool.
- You are working on your own time. Unless they explicitly grant permission,
- You are working on your own time. Unless they explicitly grant permission,
your employer may be the ultimate owner of your IP
# Post-Contribution
Before contributions are merged, you will receive an email (at the address
associated with the git commit) and will be asked to confirm the aforementioned
items. Ensure that the email addresses associated with the commits are valid.
## Post-Contribution
Before certain contributions are merged, you will receive an email (at the
address associated with the git commit) and will be asked to confirm the
aforementioned items. Ensure that the email addresses associated with the
commits are valid.

@ -151,6 +151,10 @@ test-esm: test.mjs ## Run Node ESM test suite
test.ts: test.mts
node -pe 'var data = fs.readFileSync("'$<'", "utf8"); data.split("\n").map(function(l) { return l.replace(/^describe\((.*?)function\(\)/, "Deno.test($$1async function(t)").replace(/\b(?:it|describe)\((.*?)function\(\)/g, "await t.step($$1async function(t)").replace("assert.ok", "assert.assert"); }).join("\n")' > $@
# NOTE: `bun test test.mjs` does not actually run the tests, hence test.test.mjs
test.test.mjs: test.mjs
cp $< $@
.PHONY: test-bun
test-bun: test.test.mjs ## Run Bun test suite
bun test $<

@ -31,9 +31,6 @@ encodings for XLS and other legacy spreadsheet formats
- [`dta`](packages/dta): Stata DTA file processor
- [`test_files`](https://github.com/sheetjs/test_files): Test files and various
plaintext baselines.
## License
Please consult the attached LICENSE file for details. All rights not explicitly

@ -2,6 +2,7 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* eslint-env node */
/* vim: set ts=2 ft=javascript: */
var n = "xlsx";
var X = require('../');
try { X = require('../xlsx.flow'); } catch(e) {}
@ -17,7 +18,7 @@ try { program = require('commander'); } catch(e) {
"For older versions of node, explicitly install `xlsx-cli` globally:",
" $ npm i -g xlsx-cli",
" $ xlsx-cli --help"
].forEach(function(m) { console.error(m); });
].forEach(function (m) { console.error(m); });
process.exit(1);
}
program
@ -65,9 +66,10 @@ program
.option('-F, --field-sep <sep>', 'CSV field separator', ",")
.option('-R, --row-sep <sep>', 'CSV row separator', "\n")
.option('-n, --sheet-rows <num>', 'Number of rows to process (0=all rows)')
.option('--date-format <string>', 'output date format, for example yyyy-mm-dd')
.option('--codepage <cp>', 'default to specified codepage when ambiguous')
.option('--req <module>', 'require module before processing')
.option('--sst', 'generate shared string table for XLS* formats')
.option('-d, --no-dim', 'recalculate worksheet range')
.option('--compress', 'use compression when writing XLSX/M/B and ODS')
.option('--read', 'read but do not generate output')
.option('--book', 'for single-sheet formats, emit a file per worksheet')
@ -102,18 +104,18 @@ var wb_formats_2 = [
program.parse(process.argv);
var filename = '', sheetname = '';
if(program.args[0]) {
if (program.args[0]) {
filename = program.args[0];
if(program.args[1]) sheetname = program.args[1];
if (program.args[1]) sheetname = program.args[1];
}
if(program.sheet) sheetname = program.sheet;
if(program.file) filename = program.file;
if (program.sheet) sheetname = program.sheet;
if (program.file) filename = program.file;
if(!filename) {
if (!filename) {
console.error(n + ": must specify a filename");
process.exit(1);
}
if(!fs.existsSync(filename)) {
if (!fs.existsSync(filename)) {
console.error(n + ": " + filename + ": No such file or directory");
process.exit(2);
}
@ -209,15 +211,15 @@ wb_formats_2.forEach(function(m) { if(program[m[0]] || isfmt(m[0])) {
} });
var target_sheet = sheetname || '';
if(target_sheet === '') {
if(+program.sheetIndex < (wb.SheetNames||[]).length) target_sheet = wb.SheetNames[+program.sheetIndex];
else target_sheet = (wb.SheetNames||[""])[0];
if (target_sheet === '') {
if (+program.sheetIndex < (wb.SheetNames || []).length) target_sheet = wb.SheetNames[+program.sheetIndex];
else target_sheet = (wb.SheetNames || [""])[0];
}
var ws;
try {
ws = wb.Sheets[target_sheet];
if(!ws) {
if (!ws) {
console.error("Sheet " + target_sheet + " cannot be found");
process.exit(3);
}
@ -226,7 +228,7 @@ try {
process.exit(4);
}
if(!program.quiet && !program.book) console.error(target_sheet);
if (!program.quiet && !program.book) console.error(target_sheet);
/* single worksheet file formats */
[
@ -254,21 +256,21 @@ if(!program.quiet && !program.book) console.error(target_sheet);
process.exit(0);
} });
function outit(o, fn) { if(fn) fs.writeFileSync(fn, o); else console.log(o); }
function outit(o, fn) { if (fn) fs.writeFileSync(fn, o); else console.log(o); }
function doit(cb) {
/*:: if(!wb) throw new Error("unreachable"); */
if(program.book) wb.SheetNames.forEach(function(n, i) {
/*:: if(!wb) throw new Error("unreachable"); */
/*:: if (!wb) throw new Error("unreachable"); */
if (program.book) wb.SheetNames.forEach(function (n, i) {
/*:: if (!wb) throw new Error("unreachable"); */
outit(cb(wb.Sheets[n]), (program.output || sheetname || filename) + "." + i);
});
else outit(cb(ws), program.output);
}
var jso = {};
switch(true) {
switch (true) {
case program.formulae:
doit(function(ws) { return X.utils.sheet_to_formulae(ws).join("\n"); });
doit(function (ws) { return X.utils.sheet_to_formulae(ws).join("\n"); });
break;
case program.arrays: jso.header = 1;
@ -276,33 +278,33 @@ switch(true) {
case program.rawJs: jso.raw = true;
/* falls through */
case program.json:
doit(function(ws) { return JSON.stringify(X.utils.sheet_to_json(ws,jso)); });
doit(function (ws) { return JSON.stringify(X.utils.sheet_to_json(ws, jso)); });
break;
default:
if(!program.book) {
var stream = X.stream.to_csv(ws, {FS:program.fieldSep||",", RS:program.rowSep||"\n"});
if(program.output) stream.pipe(fs.createWriteStream(program.output));
if (!program.book) {
var stream = X.stream.to_csv(ws, { FS: program.fieldSep || ",", RS: program.rowSep || "\n" });
if (program.output) stream.pipe(fs.createWriteStream(program.output));
else stream.pipe(process.stdout);
} else doit(function(ws) { return X.utils.sheet_to_csv(ws,{FS:program.fieldSep, RS:program.rowSep}); });
} else doit(function (ws) { return X.utils.sheet_to_csv(ws, { FS: program.fieldSep, RS: program.rowSep }); });
break;
}
function dump_props(wb/*:Workbook*/) {
var propaoa = [];
if(Object.assign && Object.entries) propaoa = Object.entries(Object.assign({}, wb.Props, wb.Custprops));
if (Object.assign && Object.entries) propaoa = Object.entries(Object.assign({}, wb.Props, wb.Custprops));
else {
var Keys/*:: :Array<string> = []*/, pi;
if(wb.Props) {
if (wb.Props) {
Keys = Object.keys(wb.Props);
for(pi = 0; pi < Keys.length; ++pi) {
if(Object.prototype.hasOwnProperty.call(Keys, Keys[pi])) propaoa.push([Keys[pi], Keys[/*::+*/Keys[pi]]]);
for (pi = 0; pi < Keys.length; ++pi) {
if (Object.prototype.hasOwnProperty.call(Keys, Keys[pi])) propaoa.push([Keys[pi], Keys[/*::+*/Keys[pi]]]);
}
}
if(wb.Custprops) {
if (wb.Custprops) {
Keys = Object.keys(wb.Custprops);
for(pi = 0; pi < Keys.length; ++pi) {
if(Object.prototype.hasOwnProperty.call(Keys, Keys[pi])) propaoa.push([Keys[pi], Keys[/*::+*/Keys[pi]]]);
for (pi = 0; pi < Keys.length; ++pi) {
if (Object.prototype.hasOwnProperty.call(Keys, Keys[pi])) propaoa.push([Keys[pi], Keys[/*::+*/Keys[pi]]]);
}
}
}

@ -421,7 +421,11 @@ function hashq(str/*:string*/)/*:string*/ {
}
return o;
}
function rnd(val/*:number*/, d/*:number*/)/*:string*/ { var dd = Math.pow(10,d); return ""+(Math.round(val * dd)/dd); }
function rnd(val/*:number*/, d/*:number*/)/*:string*/ {
var sgn = val < 0 ? -1 : 1;
var dd = Math.pow(10,d);
return ""+sgn*(Math.round(sgn * val * dd)/dd);
}
function dec(val/*:number*/, d/*:number*/)/*:number*/ {
var _frac = val - Math.floor(val), dd = Math.pow(10,d);
if (d < ('' + Math.round(_frac * dd)).length) return 0;

@ -67,7 +67,7 @@ var rencoding = /*#__PURE__*/evert(encodings);
// TODO: CP remap (need to read file version to determine OS)
var unescapexml/*:StringConv*/ = /*#__PURE__*/(function() {
/* 22.4.2.4 bstr (Basic String) */
var encregex = /&(?:quot|apos|gt|lt|amp|#x?([\da-fA-F]+));/ig, coderegex = /_x([\da-fA-F]{4})_/ig;
var encregex = /&(?:quot|apos|gt|lt|amp|#x?([\da-fA-F]+));/ig, coderegex = /_x([\da-fA-F]{4})_/g;
function raw_unescapexml(text/*:string*/)/*:string*/ {
var s = text + '', i = s.indexOf("<![CDATA[");
if(i == -1) return s.replace(encregex, function($$, $1) { return encodings[$$]||String.fromCharCode(parseInt($1,$$.indexOf("x")>-1?16:10))||$$; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
@ -153,7 +153,13 @@ function utf8readb(data) {
function utf8readc(data) { return Buffer_from(data, 'binary').toString('utf8'); }
var utf8corpus = "foo bar baz\u00e2\u0098\u0083\u00f0\u009f\u008d\u00a3";
var utf8read = has_buf && (/*#__PURE__*/utf8readc(utf8corpus) == /*#__PURE__*/utf8reada(utf8corpus) && utf8readc || /*#__PURE__*/utf8readb(utf8corpus) == /*#__PURE__*/utf8reada(utf8corpus) && utf8readb) || utf8reada;
var utf8read = /*#__PURE__*/(function() {
if(has_buf) {
if(utf8readc(utf8corpus) == utf8reada(utf8corpus)) return utf8readc;
if(utf8readb(utf8corpus) == utf8reada(utf8corpus)) return utf8readb;
}
return utf8reada;
})();
var utf8write/*:StringConv*/ = has_buf ? function(data) { return Buffer_from(data, 'utf8').toString("binary"); } : function(orig/*:string*/)/*:string*/ {
var out/*:Array<string>*/ = [], i = 0, c = 0, d = 0;

@ -21,7 +21,8 @@ function recordhopper(data, cb/*:RecordHopperCB*/, opts/*:?any*/) {
/* control buffer usage for fixed-length buffers */
function buf_array()/*:BufArray*/ {
var bufs/*:Array<Block>*/ = [], blksz = has_buf ? 16384 : 2048;
var has_buf_copy = has_buf && (typeof new_buf(blksz).copy == "function");
var has_buf_subarray = has_buf && (typeof new_buf(blksz).subarray == "function");
var newblk = function ba_newblk(sz/*:number*/)/*:Block*/ {
var o/*:Block*/ = (new_buf(sz)/*:any*/);
prep_blob(o, 0);
@ -55,7 +56,10 @@ function buf_array()/*:BufArray*/ {
};
var push = function ba_push(buf) {
endbuf(); curbuf = buf; if(curbuf.l == null) curbuf.l = curbuf.length; next(blksz);
if(curbuf.l > 0) bufs.push(curbuf.slice(0, curbuf.l));
bufs.push(buf);
curbuf = has_buf_subarray ? curbuf.subarray(curbuf.l || 0) : curbuf.slice(curbuf.l || 0);
prep_blob(curbuf, 0);
};
return ({ next:next, push:push, end:end, _bufs:bufs, end2:end2 }/*:any*/);

@ -1138,3 +1138,17 @@ function read_wb_ID(d, opts) {
}
}
function read_wb_TABL(d, opts) {
var o = opts || {}, OLD_WTF = !!o.WTF; o.WTF = true;
try {
var out = DIF.to_workbook(d, o);
if(!out || !out.Sheets) throw "DIF bad workbook";
var ws = out.Sheets[out.SheetNames[0]];
if(!ws || !ws["!ref"]) throw "DIF empty worksheet";
o.WTF = OLD_WTF;
return out;
} catch(e) {
o.WTF = OLD_WTF;
return PRN.to_workbook(d, opts);
}
}

@ -52,6 +52,10 @@ function parse_xlmeta_bin(data, name, _opts) {
var metatype = 2;
recordhopper(data, function(val, R, RT) {
switch (RT) {
case 58:
break;
case 59:
break;
case 335:
out.Types.push({ name: val.name });
break;

@ -35,6 +35,7 @@ function parse_xlink_bin(data, rel, name/*:string*/, _opts) {
case 0x0249: /* 'BrtSupNameFmla' */
case 0x024A: /* 'BrtSupNameBits' */
case 0x024B: /* 'BrtSupNameEnd' */
case 0x13F4: /* 'BrtExternalLinksAlternateUrls' */
break;
case 0x0023: /* 'BrtFRTBegin' */

@ -23,9 +23,10 @@ var rc_to_a1 = /*#__PURE__*/(function(){
};
})();
var crefregex = /(^|[^._A-Z0-9])(\$?)([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])(\$?)(\d{1,7})(?![_.\(A-Za-z0-9])/g;
/* TODO: check if engines support \b */
var crefregex = /(^|[^._A-Za-z0-9])(\$?)([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])(\$?)(\d{1,7})(?![_.\(A-Za-z0-9])/g;
try {
crefregex = /(^|[^._A-Z0-9])([$]?)([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])([$]?)(10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5})(?![_.\(A-Za-z0-9])/g;
crefregex = /(^|[^._A-Za-z0-9])([$]?)([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])([$]?)(10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5})(?![_.\(A-Za-z0-9])/g;
}catch(e){}
var a1_to_rc = /*#__PURE__*/(function(){
return function a1_to_rc(fstr/*:string*/, base/*:CellAddress*/) {

@ -6,7 +6,7 @@ var mergecregex = /<(?:\w+:)?mergeCell ref=["'][A-Z0-9:]+['"]\s*[\/]?>/g;
var hlinkregex = /<(?:\w+:)?hyperlink [^<>]*>/mg;
var dimregex = /"(\w*:\w*)"/;
var colregex = /<(?:\w+:)?col\b[^<>]*[\/]?>/g;
var afregex = /<(?:\w+:)?autoFilter[^>]*/g;
var afregex = /<(?:\w:)?autoFilter[^>]*([\/]|>([\s\S]*)<\/(?:\w:)?autoFilter)>/g;
var marginregex= /<(?:\w+:)?pageMargins[^<>]*\/>/g;
var sheetprregex = /<(?:\w+:)?sheetPr\b[^<>]*?\/>/;
@ -217,7 +217,7 @@ function write_ws_xml_cols(ws, cols)/*:string*/ {
}
function parse_ws_xml_autofilter(data/*:string*/) {
var o = { ref: (data.match(/ref="([^"]*)"/)||[])[1]};
var o = { ref: (data.match(/ref=["']([^"']*)["']/)||[])[1]};
return o;
}
function write_ws_xml_autofilter(data, ws, wb, idx)/*:string*/ {
@ -529,7 +529,7 @@ function write_ws_xml_data(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbook
r = [];
rr = encode_row(R);
var data_R = dense ? data[R] : [];
for(C = range.s.c; C <= range.e.c; ++C) {
if(data_R) for(C = range.s.c; C <= range.e.c; ++C) {
ref = cols[C] + rr;
var _cell = dense ? data_R[C] : ws[ref];
if(_cell === undefined) continue;

@ -505,7 +505,7 @@ function parse_BrtDVal(/*data, length, opts*/) {
}
function parse_BrtDVal14(/*data, length, opts*/) {
}
/* [MS-XLSB] 2.1.7.61 Worksheet */
/* [MS-XLSB] 2.1.7.62 Worksheet */
function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/*:Worksheet*/ {
if(!data) return data;
var opts = _opts || {};

@ -627,14 +627,14 @@ function parse_xls_props(cfb/*:CFBContainer*/, props, o) {
if(DSI && DSI.size > 0) try {
var DocSummary = parse_PropertySetStream(DSI, DocSummaryPIDDSI, PSCLSID.DSI);
for(var d in DocSummary) props[d] = DocSummary[d];
} catch(e) {if(o.WTF) throw e;/* empty */}
} catch(e) {if(o.WTF) console.error(e && e.message || e);}
/* [MS-OSHARED] 2.3.3.2.1 Summary Information Property Set*/
var SI = CFB.find(cfb, '/!SummaryInformation');
if(SI && SI.size > 0) try {
var Summary = parse_PropertySetStream(SI, SummaryPIDSI, PSCLSID.SI);
for(var s in Summary) if(props[s] == null) props[s] = Summary[s];
} catch(e) {if(o.WTF) throw e;/* empty */}
} catch(e) {if(o.WTF) console.error(e && e.message || e);}
if(props.HeadingPairs && props.TitlesOfParts) {
load_props_pairs(props.HeadingPairs, props.TitlesOfParts, props, o);

@ -856,6 +856,25 @@ var XLSBRecordEnum = {
0x13E8: { /* n:"BrtEndCalcFeatures", */ T:-1 },
0x13E9: { /* n:"BrtCalcFeature" */ },
0x13EB: { /* n:"BrtExternalLinksPr" */ },
0x13EC: { /* n:"BrtPivotCacheImplicitMeasureSupport" */ },
0x13ED: { /* n:"BrtPivotFieldIgnorableAfter" */ },
0x13EE: { /* n:"BrtPivotHierarchyIgnorableAfter" */ },
0x13EF: { /* n:"BrtPivotDataFieldFutureData" */ },
0x13F1: { /* n:"BrtPivotCacheRichData" */ },
0x13F4: { /* n:"BrtExternalLinksAlternateUrls" */ },
0x13F5: { /* n:"BrtBeginPivotVersionInfo" */ },
0x13F6: { /* n:"BrtEndPivotVersionInfo" */ },
0x13F7: { /* n:"BrtBeginCacheVersionInfo" */ },
0x13F8: { /* n:"BrtEndCacheVersionInfo" */ },
0x13F9: { /* n:"BrtPivotRequiredFeature" */ },
0x13FA: { /* n:"BrtPivotLastUsedFeature" */ },
0x13FD: { /* n:"BrtExternalCodeService" */ },
0x1407: { /* n:"BrtShowDataTypeIcons" */ },
0x140A: { /* n:"BrtSXDIAggregation" */ },
0x140B: { /* n:"BrtPivotFieldFeatureSupportInfo" */ },
0x140C: { /* n:"BrtPivotCacheAutoRefresh" */ },
0x140E: { /* n:"BrtShowDataTypeIconsUserShView" */ },
0x140F: { /* n:"BrtWorkbookCompatibilityVersion" */ },
0xFFFF: { n:"" }
};

@ -431,8 +431,54 @@ function write_FMTS_biff8(ba, NF/*:?SSFTable*/, opts) {
});
}
function write_ws_protect_biff8(sp) {
/* SheetProtection */
var flags = 0x0000;
[
["objects", false, 0x0001], // fObjects - Bit 0 (Edit objects)
["scenarios", false, 0x0002], // fScenarios - Bit 1 (Edit scenarios)
["formatCells", true, 0x0004], // fFormatCells - Bit 2 (Change cell formatting)
["formatColumns", true, 0x0008], // fFormatColumns - Bit 3 (Change column formatting)
["formatRows", true, 0x0010], // fFormatRows - Bit 4 (Change row formatting)
["insertColumns", true, 0x0020], // fInsertColumns - Bit 5 (Insert columns)
["insertRows", true, 0x0040], // fInsertRows - Bit 6 (Insert rows)
["insertHyperlinks", true, 0x0080], // fInsertHyperlinks - Bit Bit 7 (Insert hyperlinks)
["deleteColumns", true, 0x0100], // fDeleteColumns - Bit 8 (Delete columns)
["deleteRows", true, 0x0200], // fDeleteRows - Bit 9 (Delete rows)
["selectLockedCells", false, 0x0400], // fSelLockedCells - Bit 10 (Select locked cells)
["sort", true, 0x0800], // fSort - Bit 11 (Sort a cell range)
["autoFilter", true, 0x1000], // fAutoFilter - Bit 12 (Edit auto filters)
["pivotTables", true, 0x2000], // fPivotTables - Bit 13 (Edit PivotTables)
["selectUnlockedCells", false, 0x4000] // fSelUnlockedCells - Bit 14 (Select unlocked cells)
].forEach(function(n) {
if(n[1]) flags |= sp[n[0]] != null && !sp[n[0]] ? n[2] : 0x0000;
else flags |= sp[n[0]] != null && sp[n[0]] ? 0x0000 : n[2];
});
/* [MS-XLS] 2.4.112 */
var featHdr = new_buf(23);
/* [MS-XLS] 2.5.135 */
featHdr.write_shift(2, 0x0867);
featHdr.write_shift(2, 0x0000);
featHdr.write_shift(4, 0x00000000);
featHdr.write_shift(4, 0x00000000);
/* [MS-XLS] 2.5.237 */
featHdr.write_shift(2, 0x0002); // SharedFeatureType ISFPROTECTION
/* Reserved byte */
featHdr.write_shift(1, 0x01);
/* cbHdrData */
featHdr.write_shift(4, 0xffffffff);
/* [MS-XLS] 2.5.104 */
featHdr.write_shift(4, flags);
return featHdr;
}
function write_FEAT(ba, ws) {
/* [MS-XLS] 2.4.112 */
/* ISFPROTECTION */
if(ws['!protect']) write_biff_rec(ba, 0x0867 /* FeatHdr */, write_ws_protect_biff8(ws['!protect']));
/* ISFFEC2 */
var o = new_buf(19);
o.write_shift(4, 0x867); o.write_shift(4, 0); o.write_shift(4, 0);
o.write_shift(2, 3); o.write_shift(1, 1); o.write_shift(4, 0);
@ -537,6 +583,14 @@ function write_ws_biff8(idx/*:number*/, opts, wb/*:Workbook*/) {
/* Footer (string) */
write_biff_rec(ba, 0x0083 /* HCenter */, writebool(false));
write_biff_rec(ba, 0x0084 /* VCenter */, writebool(false));
/* PROTECTION */
if(ws['!protect']){
var sp = ws['!protect'];
/* [MS-XLS] 2.4.207 */
write_biff_rec(ba, 0x0012 /* Protect */, writeuint16(1));
/* [MS-XLS] 2.4.191 */
if(sp.password) write_biff_rec(ba, 0x0013 /* Password */, writeuint16(crypto_CreatePasswordVerifier_Method1(sp.password)));
}
/* ... */
if(b8) write_ws_cols_biff8(ba, ws["!cols"]);
/* ... */

@ -92,6 +92,7 @@ function make_html_row(ws/*:Worksheet*/, r/*:Range*/, R/*:number*/, o/*:Sheet2HT
// note: data-v is unaffected by the timezone interpretation
if(cell.v != null) sp["data-v"] = escapehtml(cell.v instanceof Date ? cell.v.toISOString() : cell.v);
if(cell.z != null) sp["data-z"] = cell.z;
if(cell.f != null) sp["data-f"] = escapehtml(cell.f);
if(cell.l && (cell.l.Target || "#").charAt(0) != "#") w = '<a href="' + escapehtml(cell.l.Target) +'">' + w + '</a>';
}
sp.id = (o.id || "sjs") + "-" + coord;
@ -136,12 +137,6 @@ function sheet_to_html(ws/*:Worksheet*/, opts/*:?Sheet2HTMLOpts*//*, wb:?Workboo
}
function sheet_add_dom(ws/*:Worksheet*/, table/*:HTMLElement*/, _opts/*:?any*/)/*:Worksheet*/ {
var rows/*:HTMLCollection<HTMLTableRowElement>*/ = table.rows;
if(!rows) {
/* not an HTML TABLE */
throw "Unsupported origin when " + table.tagName + " is not a TABLE";
}
var opts = _opts || {};
var dense = ws["!data"] != null;
var or_R = 0, or_C = 0;
@ -153,7 +148,6 @@ function sheet_add_dom(ws/*:Worksheet*/, table/*:HTMLElement*/, _opts/*:?any*/)/
}
}
var sheetRows = Math.min(opts.sheetRows||10000000, rows.length);
var range/*:Range*/ = {s:{r:0,c:0},e:{r:or_R,c:or_C}};
if(ws["!ref"]) {
var _range/*:Range*/ = decode_range(ws["!ref"]);
@ -163,6 +157,15 @@ function sheet_add_dom(ws/*:Worksheet*/, table/*:HTMLElement*/, _opts/*:?any*/)/
range.e.c = Math.max(range.e.c, _range.e.c);
if(or_R == -1) range.e.r = or_R = _range.e.r + 1;
}
var rows/*:HTMLCollection<HTMLTableRowElement>*/ = table.rows;
if(!rows) {
/* not an HTML TABLE */
throw "Unsupported origin when " + table.tagName + " is not a TABLE";
}
var sheetRows = Math.min(opts.sheetRows||10000000, rows.length);
var merges/*:Array<Range>*/ = [], midx = 0;
var rowinfo/*:Array<RowInfo>*/ = ws["!rows"] || (ws["!rows"] = []);
var _R = 0, R = 0, _C = 0, C = 0, RS = 0, CS = 0;
@ -179,13 +182,16 @@ function sheet_add_dom(ws/*:Worksheet*/, table/*:HTMLElement*/, _opts/*:?any*/)/
if (opts.display && is_dom_element_hidden(elt)) continue;
var v/*:?string*/ = elt.hasAttribute('data-v') ? elt.getAttribute('data-v') : elt.hasAttribute('v') ? elt.getAttribute('v') : htmldecode(elt.innerHTML);
var z/*:?string*/ = elt.getAttribute('data-z') || elt.getAttribute('z');
var f/*:?string*/ = elt.hasAttribute('data-f') ? elt.getAttribute('data-f') : elt.hasAttribute('f') ? elt.getAttribute('f') : null;
for(midx = 0; midx < merges.length; ++midx) {
var m/*:Range*/ = merges[midx];
if(m.s.c == C + or_C && m.s.r < R + or_R && R + or_R <= m.e.r) { C = m.e.c+1 - or_C; midx = -1; }
}
/* TODO: figure out how to extract nonstandard mso- style */
CS = +elt.getAttribute("colspan") || 1;
if( ((RS = (+elt.getAttribute("rowspan") || 1)))>1 || CS>1) merges.push({s:{r:R + or_R,c:C + or_C},e:{r:R + or_R + (RS||1) - 1, c:C + or_C + (CS||1) - 1}});
if( ((RS = (+elt.getAttribute("rowspan") || 1)))>1 || CS>1) {
merges.push({s:{r:R + or_R,c:C + or_C},e:{r:R + or_R + (RS||1) - 1, c:C + or_C + (CS||1) - 1}});
}
var o/*:Cell*/ = {t:'s', v:v};
var _t/*:string*/ = elt.getAttribute("data-t") || elt.getAttribute("t") || "";
if(v != null) {
@ -210,6 +216,7 @@ function sheet_add_dom(ws/*:Worksheet*/, table/*:HTMLElement*/, _opts/*:?any*/)/
l = Aelts[Aelti].getAttribute("href"); if(l.charAt(0) != "#") break;
}
if(l && l.charAt(0) != "#" && l.slice(0, 11).toLowerCase() != 'javascript:') o.l = ({ Target: l });
if(f != null) o.f = f;
if(dense) { if(!ws["!data"][R + or_R]) ws["!data"][R + or_R] = []; ws["!data"][R + or_R][C + or_C] = o; }
else ws[encode_cell({c:C + or_C, r:R + or_R})] = o;
if(range.e.c < C + or_C) range.e.c = C + or_C;

@ -205,7 +205,7 @@ function parse_ods_styles(d/*:string*/, _opts, _nfm) {
// TODO: handle more complex maps
y = parsexmltag(Rn[0], false);
if(unescapexml(y["condition"]) == "value()>=0") NF = number_format_map[y["apply-style-name"]] + ";" + NF;
else console.error("ODS number format may be incorrect: " + y["condition"]);
else if(_opts && _opts.WTF) console.error("ODS number format may be incorrect: " + y["condition"]);
break;
case 'number': // <number:number> 16.29.3
@ -252,11 +252,11 @@ function parse_content_xml(d/*:string*/, _opts, _nfm)/*:Workbook*/ {
var textR = [], oldtextR = [];
var R = -1, C = -1, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
var row_ol = 0;
var number_format_map = _nfm || {}, styles = {};
var number_format_map = _nfm || {}, styles = {}, tstyles = {};
var merges/*:Array<Range>*/ = [], mrange = {}, mR = 0, mC = 0;
var rowinfo/*:Array<RowInfo>*/ = [], rowpeat = 1, colpeat = 1;
var arrayf/*:Array<[Range, string]>*/ = [];
var WB = {Names:[], WBProps:{}};
var WB = {Names:[], WBProps:{}, Sheets:[]};
var atag = ({}/*:any*/);
var _Ref/*:[string, string]*/ = ["", ""];
var comments/*:Array<Comment>*/ = [], comment/*:Comment*/ = ({}/*:any*/);
@ -282,6 +282,10 @@ function parse_content_xml(d/*:string*/, _opts, _nfm)/*:Workbook*/ {
if(typeof JSON !== 'undefined') JSON.stringify(sheetag);
SheetNames.push(sheetag.name);
Sheets[sheetag.name] = ws;
WB.Sheets.push({
/* TODO: CodeName */
Hidden: (tstyles[sheetag["style-name"]] && tstyles[sheetag["style-name"]]["display"] ? (parsexmlbool(tstyles[sheetag["style-name"]]["display"]) ? 0 : 1) : 0)
});
intable = false;
}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
@ -529,12 +533,16 @@ function parse_content_xml(d/*:string*/, _opts, _nfm)/*:Workbook*/ {
case 'style': { // 16.2 <style:style>
var styletag = parsexmltag(Rn[0], false);
if(styletag["family"] == "table-cell" && number_format_map[styletag["data-style-name"]]) styles[styletag["name"]] = number_format_map[styletag["data-style-name"]];
else if(styletag["family"] == "table") tstyles[styletag["name"]] = styletag;
} break;
case 'map': break; // 16.3 <style:map>
case 'font-face': break; // 16.21 <style:font-face>
case 'paragraph-properties': break; // 17.6 <style:paragraph-properties>
case 'table-properties': break; // 17.15 <style:table-properties>
case 'table-properties': { // 17.15 <style:table-properties>
var proptag = parsexmltag(Rn[0], false);
if(styletag && styletag.family == "table") styletag.display = proptag.display;
} break;
case 'table-column-properties': break; // 17.16 <style:table-column-properties>
case 'table-row-properties': break; // 17.17 <style:table-row-properties>
case 'table-cell-properties': break; // 17.18 <style:table-cell-properties>
@ -756,6 +764,10 @@ function parse_content_xml(d/*:string*/, _opts, _nfm)/*:Workbook*/ {
_Ref = ods_to_csf_3D(atag.Target.slice(1));
atag.Target = "#" + _Ref[0] + "!" + _Ref[1];
} else if(atag.Target.match(/^\.\.[\\\/]/)) atag.Target = atag.Target.slice(3);
/* Appendix D.2 Hyperlink Titles */
if(atag.title) {
atag.Tooltip = unescapexml(atag.title); delete atag.title;
}
}
break;

@ -214,7 +214,9 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = /* @__PURE__ */(function
var write_ws = function(ws, wb/*:Workbook*/, i/*:number*/, opts, nfs, date1904)/*:string*/ {
/* Section 9 Tables */
var o/*:Array<string>*/ = [];
o.push(' <table:table table:name="' + escapexml(wb.SheetNames[i]) + '" table:style-name="ta1">\n');
var tstyle = "ta1";
if(((((wb||{}).Workbook||{}).Sheets||[])[i]||{}).Hidden) tstyle = "ta2";
o.push(' <table:table table:name="' + escapexml(wb.SheetNames[i]) + '" table:style-name="' + tstyle + '">\n');
var R=0,C=0, range = decode_range(ws['!ref']||"A1");
var marr/*:Array<Range>*/ = ws['!merges'] || [], mi = 0;
var dense = ws["!data"] != null;
@ -362,6 +364,9 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = /* @__PURE__ */(function
o.push(' <style:style style:name="ta1" style:family="table" style:master-page-name="mp1">\n');
o.push(' <style:table-properties table:display="true" style:writing-mode="lr-tb"/>\n');
o.push(' </style:style>\n');
o.push(' <style:style style:name="ta2" style:family="table" style:master-page-name="mp1">\n');
o.push(' <style:table-properties table:display="false" style:writing-mode="lr-tb"/>\n');
o.push(' </style:style>\n');
o.push(' <number:date-style style:name="N37" number:automatic-order="true">\n');
o.push(' <number:month number:style="long"/>\n');

@ -99,7 +99,7 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
if(n[1] === 0x49 && n[2] === 0x2a && n[3] === 0x00) throw new Error("TIFF Image File is not a spreadsheet");
if(n[1] === 0x44) return read_wb_ID(d, o);
break;
case 0x54: if(n[1] === 0x41 && n[2] === 0x42 && n[3] === 0x4C) return DIF.to_workbook(d, o); break;
case 0x54: if(n[1] === 0x41 && n[2] === 0x42 && n[3] === 0x4C) return read_wb_TABL(d, o); break;
case 0x50: return (n[1] === 0x4B && n[2] < 0x09 && n[3] < 0x09) ? read_zip(d, o) : read_prn(data, d, o, str);
case 0xEF: return n[3] === 0x3C ? parse_xlml(d, o) : read_prn(data, d, o, str);
case 0xFF:

@ -111,7 +111,7 @@ function write_binary_type(out, opts/*:WriteOpts*/)/*:any*/ {
function writeSyncXLSX(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
reset_cp();
check_wb(wb);
if(!opts || !opts.unsafe) check_wb(wb);
var o = dup(opts||{});
if(o.cellStyles) { o.cellNF = true; o.sheetStubs = true; }
if(o.type == "array") { o.type = "binary"; var out/*:string*/ = (writeSyncXLSX(wb, o)/*:any*/); o.type = "array"; return s2ab(out); }
@ -120,7 +120,7 @@ function writeSyncXLSX(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
reset_cp();
check_wb(wb);
if(!opts || !opts.unsafe) check_wb(wb);
var o = dup(opts||{});
if(o.cellStyles) { o.cellNF = true; o.sheetStubs = true; }
if(o.type == "array") { o.type = "binary"; var out/*:string*/ = (writeSync(wb, o)/*:any*/); o.type = "array"; return s2ab(out); }

@ -24,7 +24,7 @@ function make_json_row(sheet/*:Worksheet*/, r/*:Range*/, R/*:number*/, cols/*:Ar
switch(val.t){
case 'z': if(v == null) break; continue;
case 'e': v = (v == 0 ? null : void 0); break;
case 's': case 'b':
case 's': case 'b': break;
case 'n': if(!val.z || !fmt_is_date(val.z)) break;
v = numdate(v); // TODO: date1904 setting should also be stored in worksheet object
if(typeof v == "number") break;
@ -116,7 +116,7 @@ function make_csv_row(sheet/*:Worksheet*/, r/*:Range*/, R/*:number*/, cols/*:Arr
else if(val.v != null) {
isempty = false;
txt = ''+(o.rawNumbers && val.t == "n" ? val.v : format_cell(val, null, o));
for(var i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34 || o.forceQuotes) {txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
for(var i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 10 ||cc === 13 || cc === 34 || o.forceQuotes) {txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
if(txt == "ID" && w == 0 && row.length == 0) txt = '"ID"';
} else if(val.f != null && !val.F) {
isempty = false;

@ -103,7 +103,10 @@ function write_json_stream(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
R = r.s.r + offset;
stream._read = function() {
while(R <= r.e.r) {
if ((rowinfo[R-1]||{}).hidden) continue;
if ((rowinfo[R]||{}).hidden) {
++R;
continue;
};
var row = make_json_row(sheet, r, R, cols, header, hdr, o);
++R;
if((row.isempty === false) || (header === 1 ? o.blankrows !== false : !!o.blankrows)) {

@ -52,6 +52,10 @@ function parse_xlmeta_bin(data, name, _opts) {
var metatype = 2;
recordhopper(data, function(val, R, RT) {
switch (RT) {
case 58:
break;
case 59:
break;
case 335:
out.Types.push({ name: val.name });
break;

@ -73,15 +73,37 @@ function parse_xlmeta_bin(data: RawData, name: string, _opts?: ParseXLMetaOption
// case 0x014D: /* BrtEndMetadata */
// case 0x014E: /* BrtBeginEsmdtinfo */
// case 0x0150: /* BrtEndEsmdtinfo */
// case 0x0151: /* BrtBeginEsmdb */
// case 0x0152: /* BrtEndEsmdb */
// case 0x0153: /* BrtBeginEsfmd */
// case 0x0154: /* BrtEndEsfmd */
// case 0x0174: /* BrtBeginEsmdx */
// case 0x0175: /* BrtEndEsmdx */
// case 0x0176: /* BrtBeginMdxSet */
// case 0x0177: /* BrtEndMdxSet */
// case 0x0178: /* BrtBeginMdxMbrProp */
// case 0x0179: /* BrtEndMdxMbrProp */
// case 0x017A: /* BrtBeginMdxKPI */
// case 0x017B: /* BrtEndMdxKPI */
// case 0x017C: /* BrtBeginEsstr */
// case 0x017D: /* BrtEndEsstr */
// case 0x0034: /* BrtBeginFmd */
// case 0x0035: /* BrtEndFmd */
// case 0x0036: /* BrtBeginMdx */
// case 0x0037: /* BrtEndMdx */
// case 0x0038: /* BrtBeginMdxTuple */
// case 0x0039: /* BrtEndMdxTuple */
// case 0x1000: /* BrtBeginDynamicArrayPr */
// case 0x1001: /* BrtEndDynamicArrayPr */
// case 0x138A: /* BrtBeginRichValueBlock */
// case 0x138B: /* BrtEndRichValueBlock */
case 0x003A: /* BrtMdxMbrIstr */
break;
case 0x003B: /* BrtStr */
break;
case 0x014F: /* BrtMdtinfo */
out.Types.push({name: (val as BrtMdtinfo).name}); break;

@ -4,7 +4,7 @@
/* these are type imports and do not show up in the generated JS */
import { CFB$Container, CFB$Entry } from 'cfb';
import { WorkBook, WorkSheet, Range, CellObject, ParsingOptions, WritingOptions, DenseWorkSheet, Comments } from '../';
import type { utils, NumberFormat } from "../";
import type { utils, NumberFormat, DenseSheetData } from "../";
declare var encode_col: typeof utils.encode_col;
declare var encode_row: typeof utils.encode_row;
@ -1697,7 +1697,7 @@ function write_numbers_tma(cfb: CFB$Container, deps: Dependents, ws: WorkSheet,
if(trunc) console.error(`Truncating to ${encode_range(range)}`);
/* preprocess data and build up shared string table */
var data: CellObject[][] = [];
var data: DenseSheetData = [];
if(ws["!data"]) data = ws["!data"];
else {
var colstr: string[] = [];
@ -1708,7 +1708,7 @@ function write_numbers_tma(cfb: CFB$Container, deps: Dependents, ws: WorkSheet,
for(_C = 0; _C <= range.e.c; ++_C) {
var _cell = ws[colstr[_C] + _R];
if(!_cell) continue;
data[R_][_C] = _cell;
data[R_]![_C] = _cell;
}
}
}

@ -142,10 +142,7 @@
"url": "https://git.sheetjs.com/SheetJS/sheetjs"
},
"scripts": {
"pretest": "npm run lint",
"test": "npm run tests-only",
"pretest-only": "git submodule init && git submodule update",
"tests-only": "make travis",
"test": "make travis",
"build": "make",
"lint": "make fullint",
"dtslint": "dtslint types"

@ -13,8 +13,8 @@ a { text-decoration: none }
<pre>
<b><a href="http://sheetjs.com">SSF (Spreadsheet Number Format) Live Demo</a></b>
<a href="https://github.com/SheetJS/ssf">Source Code Repo</a>
<a href="https://github.com/SheetJS/ssf/issues">Issues? Something look weird? Click here and report an issue</a>
<a href="https://git.sheetjs.com/sheetjs/sheetjs/src/branch/master/packages/ssf">Source Code Repo</a>
<a href="https://git.sheetjs.com/SheetJS/sheetjs/issues">Issues? Something look weird? Click here and report an issue</a>
</pre>
<table>
<tr><td><b>Format code:</b></td><td><input type="text" id="fmt" value="General"></td></tr>

@ -1446,7 +1446,7 @@ SSF.load_table = function load_table(tbl/*:{[n:number]:string}*/) { for(var i=0;
## Fraction Library
The implementation is from [our frac library](https://github.com/SheetJS/frac/):
The implementation is from [our frac library](https://git.sheetjs.com/SheetJS/frac/):
```js>bits/30_frac.js
function frac(x, D, mixed) {

13
test.js

@ -1772,8 +1772,8 @@ describe('roundtrip features', function() {
['xlsx', paths.svxlsx],
['xlsb', paths.svxlsb],
['xls', paths.svxls],
['biff5', paths.svxls5]
// ['ods', paths.svods]
['biff5', paths.svxls5],
['ods', paths.svods]
].forEach(function(w) {
it(w[0], function() {
var wb1 = X.read(fs.readFileSync(w[1]), {type:TYPE});
@ -1783,7 +1783,9 @@ describe('roundtrip features', function() {
assert.equal(wbs1.length, wbs2.length);
for(var i = 0; i < wbs1.length; ++i) {
assert.equal(wbs1[i].name, wbs2[i].name);
assert.equal(wbs1[i].Hidden, wbs2[i].Hidden);
/* NOTE: ODS does not support the equivalent of "Very Hidden" */
if(w[0] != "ods") assert.equal(wbs1[i].Hidden, wbs2[i].Hidden);
else assert.equal(!!wbs1[i].Hidden, !!wbs2[i].Hidden);
}
});
});
@ -3102,6 +3104,11 @@ describe('corner cases', function() {
}
});
});
(fs.existsSync(dir + 'shared_formula.xlsx') ? it : it.skip)('should properly handle defined names in shared formulae', function() {
var wb = X.read(fs.readFileSync(dir + 'shared_formula.xlsx'), {type:TYPE});
var formulae = X.utils.sheet_to_formulae(wb.Sheets[wb.SheetNames[0]]);
assert.equal(formulae[4], 'A5=nvRTX6090');
});
});
describe('encryption', function() {

5
test.mjs generated

@ -3103,6 +3103,11 @@ describe('corner cases', function() {
}
});
});
(fs.existsSync(dir + 'shared_formula.xlsx') ? it : it.skip)('should properly handle defined names in shared formulae', function() {
var wb = X.read(fs.readFileSync(dir + 'shared_formula.xlsx'), {type:TYPE});
var formulae = X.utils.sheet_to_formulae(wb.Sheets[wb.SheetNames[0]]);
assert.equal(formulae[4], 'A5=nvRTX6090');
});
});
describe('encryption', function() {

@ -9,10 +9,10 @@ declare type EmptyFunc = (() => void) | null;
declare var afterEach:(test:EmptyFunc)=>void;
declare var cptable: any;
*/
import * as assert_ from 'https://deno.land/std/testing/asserts.ts';
import * as base64_ from 'https://deno.land/std/encoding/base64.ts';
import * as assert_ from 'https://deno.land/std@0.169.0/testing/asserts.ts';
import * as base64_ from 'https://deno.land/std@0.169.0/encoding/base64.ts';
const assert: any = {...assert_};
assert.throws = function(f: () => void) { assert.assertThrows(function() { try { f(); } catch(e) { throw e instanceof Error ? e : new Error(e); }})};
assert.throws = function(f: () => void) { assert.assertThrows(function() { try { f(); } catch(e) { throw e instanceof Error ? e : new Error(e as string); }})};
assert.doesNotThrow = function(f: ()=>void) { f(); };
assert.equal = assert.assertEquals;
assert.notEqual = assert.assertNotEquals;
@ -36,7 +36,7 @@ function readFileSync2(x: string, e?: ShEncoding): Uint8Array | string {
if(!e) return u8;
switch(e) {
case 'utf-8': return new TextDecoder().decode(u8);
case 'base64': return base64_.encode(u8);
case 'base64': return base64_.encode(u8 as any);
case 'buffer': return u8;
case 'binary': return Array.from({length: u8.length}, (_,i) => String.fromCharCode(u8[i])).join("");
}
@ -2724,7 +2724,7 @@ describe('dense mode', function() {
wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true, dense: true});
ws = wb.Sheets[wb.SheetNames[0]];
assert.ok(!ws["A1"]);
assert.equal(ws["!data"]?.[0][0].v, "Link to Sheet2");
assert.equal(ws["!data"]?.[0]![0]!.v, "Link to Sheet2");
});
if(!browser) artifax.forEach(function(p) {
var wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true});
@ -2736,7 +2736,7 @@ describe('dense mode', function() {
ws = wb.Sheets[wb.SheetNames[0]];
assert.ok(!ws["A1"]);
assert.ok(!!ws["!data"]);
assert.ok(ws["!data"]?.[0][0]);
assert.ok(ws["!data"]?.[0]![0]);
});
});
it('aoa_to_sheet', function() {
@ -2744,28 +2744,28 @@ describe('dense mode', function() {
var sp = X.utils.aoa_to_sheet(aoa); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]);
sp = X.utils.aoa_to_sheet(aoa, {}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]);
sp = X.utils.aoa_to_sheet(aoa, {dense: false}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]);
var ds = X.utils.aoa_to_sheet(aoa, {dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.ok(!ds["A2"]);
var ds = X.utils.aoa_to_sheet(aoa, {dense: true}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.ok(!ds["A2"]);
});
it('json_to_sheet', function() {
var json = [{"SheetJS": 5433795}];
var sp = X.utils.json_to_sheet(json); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]);
sp = X.utils.json_to_sheet(json, {}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]);
sp = X.utils.json_to_sheet(json, {dense: false}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]);
var ds = X.utils.json_to_sheet(json, {dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.ok(!ds["A2"]);
var ds = X.utils.json_to_sheet(json, {dense: true}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.ok(!ds["A2"]);
});
it('sheet_add_aoa', function() {
var aoa = [["SheetJS"]];
var sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_aoa(sp, [[5433795]], {origin:-1}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]);
sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_aoa(sp, [[5433795]], {origin:-1, dense: true}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]);
var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.ok(!ds["A2"]);
ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1, dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.ok(!ds["A2"]);
var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.ok(!ds["A2"]);
ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1, dense: true}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.ok(!ds["A2"]);
});
it('sheet_add_json', function() {
var aoa = [["SheetJS"]];
var sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader:true}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]);
sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader: true, dense: true}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]);
var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.ok(!ds["A2"]);
ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true, dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.ok(!ds["A2"]);
var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.ok(!ds["A2"]);
ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true, dense: true}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.ok(!ds["A2"]);
});
for(var ofmti = 0; ofmti < ofmt.length; ++ofmti) { var f = ofmt[ofmti];
it('write ' + f, function() {
@ -3022,6 +3022,11 @@ describe('corner cases', function() {
}
});
});
if(fs.existsSync(dir + 'shared_formula.xlsx')) it('should properly handle defined names in shared formulae', function() {
var wb = X.read(fs.readFileSync(dir + 'shared_formula.xlsx'), {type:TYPE});
var formulae = X.utils.sheet_to_formulae(wb.Sheets[wb.SheetNames[0]]);
assert.equal(formulae[4], 'A5=nvRTX6090');
});
});
describe('encryption', function() {
@ -3033,14 +3038,14 @@ describe('encryption', function() {
X.read(fs.readFileSync(dir + x), {type:TYPE,password:'Password',WTF:opts.WTF});
throw new Error("incorrect password was accepted");
} catch(e) {
if(e.message != "Password is incorrect") throw e;
if((e as any).message != "Password is incorrect") throw e;
}
});
it('should recognize correct password', function() {
try {
X.read(fs.readFileSync(dir + x), {type:TYPE,password:'password',WTF:opts.WTF});
} catch(e) {
if(e.message == "Password is incorrect") throw e;
if((e as any).message == "Password is incorrect") throw e;
}
});
if(false) it('should decrypt file', function() {

19
test.sh

@ -2,8 +2,12 @@
set -euxo pipefail
TZONES=(America/New_York Europe/London Asia/Seoul America/Los_Angeles Europe/Berlin Asia/Kolkata Asia/Shanghai America/Cancun America/Anchorage America/Barbados Asia/Tokyo America/Cayman Pacific/Honolulu America/Mexico_City Asia/Hong_Kong Europe/Paris Atlantic/Azores)
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
if [ -e datetest.js ]; then
sudo n 20;
nvm use 20
# sudo n 20;
for TZ in ${TZONES[@]}; do
echo "$TZ"
env TZ="$TZ" mocha -R dot datetest.js
@ -11,19 +15,22 @@ if [ -e datetest.js ]; then
fi
# min test
for n in 20 10 0.8 0.10 0.12 4 6 8 12 14 16 18; do
sudo n $n
for n in 20 10 0.8 0.10 0.12 4 6 8 12 14 16 18 22 24; do
nvm use $n
# sudo n $n
env WTF=1 make testdot_misc
for TZ in ${TZONES[@]}; do
sudo n $n
# sudo n $n
nvm use $n
env WTF=1 TZ="$TZ" make testdot_misc
done
done
# full test
for n in 20 10 0.12; do
for n in 24 16 0.12; do
for TZ in America/New_York Asia/Seoul Asia/Kolkata Europe/Paris; do
sudo n $n
# sudo n $n
nvm use $n
env WTF=1 TZ="$TZ" make testdot
done
done

5
test.test.mjs generated

@ -3103,6 +3103,11 @@ describe('corner cases', function() {
}
});
});
(fs.existsSync(dir + 'shared_formula.xlsx') ? it : it.skip)('should properly handle defined names in shared formulae', function() {
var wb = X.read(fs.readFileSync(dir + 'shared_formula.xlsx'), {type:TYPE});
var formulae = X.utils.sheet_to_formulae(wb.Sheets[wb.SheetNames[0]]);
assert.equal(formulae[4], 'A5=nvRTX6090');
});
});
describe('encryption', function() {

33
test.ts

@ -9,10 +9,10 @@ declare type EmptyFunc = (() => void) | null;
declare var afterEach:(test:EmptyFunc)=>void;
declare var cptable: any;
*/
import * as assert_ from 'https://deno.land/std/testing/asserts.ts';
import * as base64_ from 'https://deno.land/std/encoding/base64.ts';
import * as assert_ from 'https://deno.land/std@0.169.0/testing/asserts.ts';
import * as base64_ from 'https://deno.land/std@0.169.0/encoding/base64.ts';
const assert: any = {...assert_};
assert.throws = function(f: () => void) { assert.assertThrows(function() { try { f(); } catch(e) { throw e instanceof Error ? e : new Error(e); }})};
assert.throws = function(f: () => void) { assert.assertThrows(function() { try { f(); } catch(e) { throw e instanceof Error ? e : new Error(e as string); }})};
assert.doesNotThrow = function(f: ()=>void) { f(); };
assert.equal = assert.assertEquals;
assert.notEqual = assert.assertNotEquals;
@ -36,7 +36,7 @@ function readFileSync2(x: string, e?: ShEncoding): Uint8Array | string {
if(!e) return u8;
switch(e) {
case 'utf-8': return new TextDecoder().decode(u8);
case 'base64': return base64_.encode(u8);
case 'base64': return base64_.encode(u8 as any);
case 'buffer': return u8;
case 'binary': return Array.from({length: u8.length}, (_,i) => String.fromCharCode(u8[i])).join("");
}
@ -2724,7 +2724,7 @@ Deno.test('dense mode', async function(t) {
wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true, dense: true});
ws = wb.Sheets[wb.SheetNames[0]];
assert.assert(!ws["A1"]);
assert.equal(ws["!data"]?.[0][0].v, "Link to Sheet2");
assert.equal(ws["!data"]?.[0]![0]!.v, "Link to Sheet2");
});
if(!browser) artifax.forEach(function(p) {
var wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true});
@ -2736,7 +2736,7 @@ Deno.test('dense mode', async function(t) {
ws = wb.Sheets[wb.SheetNames[0]];
assert.assert(!ws["A1"]);
assert.assert(!!ws["!data"]);
assert.assert(ws["!data"]?.[0][0]);
assert.assert(ws["!data"]?.[0]![0]);
});
});
await t.step('aoa_to_sheet', async function(t) {
@ -2744,28 +2744,28 @@ Deno.test('dense mode', async function(t) {
var sp = X.utils.aoa_to_sheet(aoa); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
sp = X.utils.aoa_to_sheet(aoa, {}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
sp = X.utils.aoa_to_sheet(aoa, {dense: false}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
var ds = X.utils.aoa_to_sheet(aoa, {dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]);
var ds = X.utils.aoa_to_sheet(aoa, {dense: true}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.assert(!ds["A2"]);
});
await t.step('json_to_sheet', async function(t) {
var json = [{"SheetJS": 5433795}];
var sp = X.utils.json_to_sheet(json); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
sp = X.utils.json_to_sheet(json, {}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
sp = X.utils.json_to_sheet(json, {dense: false}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
var ds = X.utils.json_to_sheet(json, {dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]);
var ds = X.utils.json_to_sheet(json, {dense: true}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.assert(!ds["A2"]);
});
await t.step('sheet_add_aoa', async function(t) {
var aoa = [["SheetJS"]];
var sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_aoa(sp, [[5433795]], {origin:-1}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_aoa(sp, [[5433795]], {origin:-1, dense: true}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]);
ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1, dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]);
var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.assert(!ds["A2"]);
ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1, dense: true}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.assert(!ds["A2"]);
});
await t.step('sheet_add_json', async function(t) {
var aoa = [["SheetJS"]];
var sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader:true}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader: true, dense: true}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]);
ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true, dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]);
var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.assert(!ds["A2"]);
ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true, dense: true}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.assert(!ds["A2"]);
});
for(var ofmti = 0; ofmti < ofmt.length; ++ofmti) { var f = ofmt[ofmti];
await t.step('write ' + f, async function(t) {
@ -3022,6 +3022,11 @@ Deno.test('corner cases', async function(t) {
}
});
});
if(fs.existsSync(dir + 'shared_formula.xlsx')) await t.step('should properly handle defined names in shared formulae', async function(t) {
var wb = X.read(fs.readFileSync(dir + 'shared_formula.xlsx'), {type:TYPE});
var formulae = X.utils.sheet_to_formulae(wb.Sheets[wb.SheetNames[0]]);
assert.equal(formulae[4], 'A5=nvRTX6090');
});
});
Deno.test('encryption', async function(t) {
@ -3033,14 +3038,14 @@ Deno.test('encryption', async function(t) {
X.read(fs.readFileSync(dir + x), {type:TYPE,password:'Password',WTF:opts.WTF});
throw new Error("incorrect password was accepted");
} catch(e) {
if(e.message != "Password is incorrect") throw e;
if((e as any).message != "Password is incorrect") throw e;
}
});
await t.step('should recognize correct password', async function(t) {
try {
X.read(fs.readFileSync(dir + x), {type:TYPE,password:'password',WTF:opts.WTF});
} catch(e) {
if(e.message == "Password is incorrect") throw e;
if((e as any).message == "Password is incorrect") throw e;
}
});
if(false) await t.step('should decrypt file', async function(t) {

@ -1 +0,0 @@
Subproject commit 1ea05a3eee0a746c102dea42e729b31e4ebfca35

@ -9,10 +9,10 @@ declare type EmptyFunc = (() => void) | null;
declare var afterEach:(test:EmptyFunc)=>void;
declare var cptable: any;
*/
import * as assert_ from 'https://deno.land/std/testing/asserts.ts';
import * as base64_ from 'https://deno.land/std/encoding/base64.ts';
import * as assert_ from 'https://deno.land/std@0.169.0/testing/asserts.ts';
import * as base64_ from 'https://deno.land/std@0.169.0/encoding/base64.ts';
const assert: any = {...assert_};
assert.throws = function(f: () => void) { assert.assertThrows(function() { try { f(); } catch(e) { throw e instanceof Error ? e : new Error(e); }})};
assert.throws = function(f: () => void) { assert.assertThrows(function() { try { f(); } catch(e) { throw e instanceof Error ? e : new Error(e as string); }})};
assert.doesNotThrow = function(f: ()=>void) { f(); };
assert.equal = assert.assertEquals;
assert.notEqual = assert.assertNotEquals;
@ -35,7 +35,7 @@ function readFileSync2(x: string, e?: ShEncoding): Uint8Array | string {
if(!e) return u8;
switch(e) {
case 'utf-8': return new TextDecoder().decode(u8);
case 'base64': return base64_.encode(u8);
case 'base64': return base64_.encode(u8 as any);
case 'buffer': return u8;
case 'binary': return Array.from({length: u8.length}, (_,i) => String.fromCharCode(u8[i])).join("");
}
@ -2723,7 +2723,7 @@ Deno.test('dense mode', async function(t) {
wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true, dense: true});
ws = wb.Sheets[wb.SheetNames[0]];
assert.assert(!ws["A1"]);
assert.equal(ws["!data"]?.[0][0].v, "Link to Sheet2");
assert.equal(ws["!data"]?.[0]![0]!.v, "Link to Sheet2");
});
if(!browser) artifax.forEach(function(p) {
var wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true});
@ -2735,7 +2735,7 @@ Deno.test('dense mode', async function(t) {
ws = wb.Sheets[wb.SheetNames[0]];
assert.assert(!ws["A1"]);
assert.assert(!!ws["!data"]);
assert.assert(ws["!data"]?.[0][0]);
assert.assert(ws["!data"]?.[0]![0]);
});
});
await t.step('aoa_to_sheet', async function(t) {
@ -2743,28 +2743,28 @@ Deno.test('dense mode', async function(t) {
var sp = X.utils.aoa_to_sheet(aoa); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
sp = X.utils.aoa_to_sheet(aoa, {}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
sp = X.utils.aoa_to_sheet(aoa, {dense: false}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
var ds = X.utils.aoa_to_sheet(aoa, {dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]);
var ds = X.utils.aoa_to_sheet(aoa, {dense: true}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.assert(!ds["A2"]);
});
await t.step('json_to_sheet', async function(t) {
var json = [{"SheetJS": 5433795}];
var sp = X.utils.json_to_sheet(json); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
sp = X.utils.json_to_sheet(json, {}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
sp = X.utils.json_to_sheet(json, {dense: false}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
var ds = X.utils.json_to_sheet(json, {dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]);
var ds = X.utils.json_to_sheet(json, {dense: true}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.assert(!ds["A2"]);
});
await t.step('sheet_add_aoa', async function(t) {
var aoa = [["SheetJS"]];
var sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_aoa(sp, [[5433795]], {origin:-1}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_aoa(sp, [[5433795]], {origin:-1, dense: true}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]);
ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1, dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]);
var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.assert(!ds["A2"]);
ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1, dense: true}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.assert(!ds["A2"]);
});
await t.step('sheet_add_json', async function(t) {
var aoa = [["SheetJS"]];
var sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader:true}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader: true, dense: true}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]);
var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]);
ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true, dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]);
var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.assert(!ds["A2"]);
ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true, dense: true}); assert.equal(ds["!data"]?.[1]![0]!.v, 5433795); assert.assert(!ds["A2"]);
});
for(var ofmti = 0; ofmti < ofmt.length; ++ofmti) { var f = ofmt[ofmti];
await t.step('write ' + f, async function(t) {
@ -3021,6 +3021,11 @@ Deno.test('corner cases', async function(t) {
}
});
});
if(fs.existsSync(dir + 'shared_formula.xlsx')) await t.step('should properly handle defined names in shared formulae', async function(t) {
var wb = X.read(fs.readFileSync(dir + 'shared_formula.xlsx'), {type:TYPE});
var formulae = X.utils.sheet_to_formulae(wb.Sheets[wb.SheetNames[0]]);
assert.equal(formulae[4], 'A5=nvRTX6090');
});
});
Deno.test('encryption', async function(t) {
@ -3032,14 +3037,14 @@ Deno.test('encryption', async function(t) {
X.read(fs.readFileSync(dir + x), {type:TYPE,password:'Password',WTF:opts.WTF});
throw new Error("incorrect password was accepted");
} catch(e) {
if(e.message != "Password is incorrect") throw e;
if((e as any).message != "Password is incorrect") throw e;
}
});
await t.step('should recognize correct password', async function(t) {
try {
X.read(fs.readFileSync(dir + x), {type:TYPE,password:'password',WTF:opts.WTF});
} catch(e) {
if(e.message == "Password is incorrect") throw e;
if((e as any).message == "Password is incorrect") throw e;
}
});
if(false) await t.step('should decrypt file', async function(t) {

@ -690,7 +690,6 @@ apachepoi_15573.xls
apachepoi_1904DateWindowing.xls
apachepoi_19599-1.xls
apachepoi_19599-2.xls
apachepoi_22742.xls
apachepoi_24207.xls
apachepoi_24215.xls
apachepoi_25183.xls

5
tests/core.js generated

@ -3102,6 +3102,11 @@ describe('corner cases', function() {
}
});
});
(fs.existsSync(dir + 'shared_formula.xlsx') ? it : it.skip)('should properly handle defined names in shared formulae', function() {
var wb = X.read(fs.readFileSync(dir + 'shared_formula.xlsx'), {type:TYPE});
var formulae = X.utils.sheet_to_formulae(wb.Sheets[wb.SheetNames[0]]);
assert.equal(formulae[4], 'A5=nvRTX6090');
});
});
describe('encryption', function() {

21
types/index.d.ts vendored

@ -317,6 +317,9 @@ export interface WritingOptions extends CommonOptions {
/** Record Separator ("row separator") for CSV / Text output */
RS?: string;
/** Skip certain validity checks (NOTE: generated files may not open in Excel) */
unsafe?: boolean;
}
/** Workbook Object */
@ -575,14 +578,14 @@ export interface Sheet {
* Dense-mode store cells in the '!data' key
* Special keys start with '!'
*/
[cell: string]: CellObject | CellObject[][] | SheetKeys | any;
[cell: string]: CellObject | DenseSheetData | SheetKeys | any;
/**
* Dense-mode worksheets store data in an array of arrays
*
* Cells are accessed with sheet['!data'][R][C] (where R and C are 0-indexed)
*/
'!data'?: CellObject[][];
'!data'?: DenseSheetData;
/** Sheet type */
'!type'?: SheetType;
@ -599,15 +602,16 @@ export interface DenseSheet extends Sheet {
* Special keys start with '!'
* Dense-mode worksheets store data in the '!data' key
*/
[cell: string]: CellObject[][] | SheetKeys | any;
[cell: string]: DenseSheetData | SheetKeys | any;
/**
* Dense-mode worksheets store data in an array of arrays
*
* Cells are accessed with sheet['!data'][R][C] (where R and C are 0-indexed)
*/
'!data': CellObject[][];
'!data': DenseSheetData;
}
export type DenseSheetData = ((CellObject|undefined)[]|undefined)[];
/** General object representing a sparse Sheet (worksheet or chartsheet) */
export interface SparseSheet extends Sheet {
/**
@ -650,14 +654,7 @@ export interface WorkSheet extends Sheet {
'!autofilter'?: AutoFilterInfo;
}
/** Dense Worksheet Object */
export interface DenseWorkSheet extends DenseSheet {
/**
* Dense-mode worksheets store data in an array of arrays
*
* Cells are accessed with sheet['!data'][R][C] (where R and C are 0-indexed)
*/
'!data': CellObject[][];
}
export interface DenseWorkSheet extends DenseSheet {}
/**
* Worksheet Object with CellObject type