diff --git a/bits/27_csfutils.js b/bits/27_csfutils.js
index a31de3d..5eaf3fe 100644
--- a/bits/27_csfutils.js
+++ b/bits/27_csfutils.js
@@ -135,7 +135,12 @@ function sheet_add_aoa(_ws/*:?Worksheet*/, data/*:AOA*/, opts/*:?any*/)/*:Worksh
if(data[R][C] && typeof data[R][C] === 'object' && !Array.isArray(data[R][C]) && !(data[R][C] instanceof Date)) cell = data[R][C];
else {
if(Array.isArray(cell.v)) { cell.f = data[R][C][1]; cell.v = cell.v[0]; }
- if(cell.v === null) { if(cell.f) cell.t = 'n'; else if(!o.sheetStubs) continue; else cell.t = 'z'; }
+ if(cell.v === null) {
+ if(cell.f) cell.t = 'n';
+ else if(o.nullError) { cell.t = 'e'; cell.v = 0; }
+ else if(!o.sheetStubs) continue;
+ else cell.t = 'z';
+ }
else if(typeof cell.v === 'number') cell.t = 'n';
else if(typeof cell.v === 'boolean') cell.t = 'b';
else if(cell.v instanceof Date) {
diff --git a/bits/32_odmanrdf.js b/bits/32_odmanrdf.js
index 1902d90..ab910cb 100644
--- a/bits/32_odmanrdf.js
+++ b/bits/32_odmanrdf.js
@@ -57,7 +57,7 @@ function write_rdf(rdf) {
}
/* TODO: pull properties */
var write_meta_ods/*:{(wb:Workbook, opts:any):string}*/ = (function() {
- var payload = 'Sheet' + 'JS ' + XLSX.version + '';
+ var payload = 'Sheet' + 'JS ' + XLSX.version + '';
return function wmo(/*:: wb: Workbook, opts: any*/)/*:string*/ {
return payload;
};
diff --git a/bits/38_xlstypes.js b/bits/38_xlstypes.js
index 5008570..32e3cbc 100644
--- a/bits/38_xlstypes.js
+++ b/bits/38_xlstypes.js
@@ -523,16 +523,17 @@ function parse_URLMoniker(blob/*::, length, opts*/) {
/* [MS-OSHARED] 2.3.7.8 FileMoniker TODO: all fields */
function parse_FileMoniker(blob/*::, length*/) {
- blob.l += 2; //var cAnti = blob.read_shift(2);
+ var cAnti = blob.read_shift(2);
+ var preamble = ""; while(cAnti-- > 0) preamble += "../";
var ansiPath = blob.read_shift(0, 'lpstr-ansi');
blob.l += 2; //var endServer = blob.read_shift(2);
if(blob.read_shift(2) != 0xDEAD) throw new Error("Bad FileMoniker");
var sz = blob.read_shift(4);
- if(sz === 0) return ansiPath.replace(/\\/g,"/");
+ if(sz === 0) return preamble + ansiPath.replace(/\\/g,"/");
var bytes = blob.read_shift(4);
if(blob.read_shift(2) != 3) throw new Error("Bad FileMoniker");
var unicodePath = blob.read_shift(bytes>>1, 'utf16le').replace(chr0,"");
- return unicodePath;
+ return preamble + unicodePath;
}
/* [MS-OSHARED] 2.3.7.2 HyperlinkMoniker TODO: all the monikers */
@@ -551,6 +552,13 @@ function parse_HyperlinkString(blob/*::, length*/) {
var o = len > 0 ? blob.read_shift(len, 'utf16le').replace(chr0, "") : "";
return o;
}
+function write_HyperlinkString(str/*:string*/, o) {
+ if(!o) o = new_buf(6 + str.length * 2);
+ o.write_shift(4, 1 + str.length);
+ for(var i = 0; i < str.length; ++i) o.write_shift(2, str.charCodeAt(i));
+ o.write_shift(2, 0);
+ return o;
+}
/* [MS-OSHARED] 2.3.7.1 Hyperlink Object */
function parse_Hyperlink(blob, length)/*:Hyperlink*/ {
@@ -571,6 +579,7 @@ function parse_Hyperlink(blob, length)/*:Hyperlink*/ {
var target = targetFrameName||moniker||oleMoniker||"";
if(target && Loc) target+="#"+Loc;
if(!target) target = "#" + Loc;
+ if((flags & 0x0002) && target.charAt(0) == "/" && target.charAt(1) != "/") target = "file://" + target;
var out = ({Target:target}/*:any*/);
if(guid) out.guid = guid;
if(fileTime) out.time = fileTime;
@@ -580,29 +589,31 @@ function parse_Hyperlink(blob, length)/*:Hyperlink*/ {
function write_Hyperlink(hl) {
var out = new_buf(512), i = 0;
var Target = hl.Target;
- var F = Target.indexOf("#") > -1 ? 0x1f : 0x17;
+ if(Target.slice(0,7) == "file://") Target = Target.slice(7);
+ var hashidx = Target.indexOf("#");
+ var F = hashidx > -1 ? 0x1f : 0x17;
switch(Target.charAt(0)) { case "#": F=0x1c; break; case ".": F&=~2; break; }
out.write_shift(4,2); out.write_shift(4, F);
var data = [8,6815827,6619237,4849780,83]; for(i = 0; i < data.length; ++i) out.write_shift(4, data[i]);
if(F == 0x1C) {
Target = Target.slice(1);
- out.write_shift(4, Target.length + 1);
- for(i = 0; i < Target.length; ++i) out.write_shift(2, Target.charCodeAt(i));
- out.write_shift(2, 0);
+ write_HyperlinkString(Target, out);
} else if(F & 0x02) {
data = "e0 c9 ea 79 f9 ba ce 11 8c 82 00 aa 00 4b a9 0b".split(" ");
for(i = 0; i < data.length; ++i) out.write_shift(1, parseInt(data[i], 16));
- out.write_shift(4, 2*(Target.length + 1));
- for(i = 0; i < Target.length; ++i) out.write_shift(2, Target.charCodeAt(i));
+ var Pretarget = hashidx > -1 ? Target.slice(0, hashidx) : Target;
+ out.write_shift(4, 2*(Pretarget.length + 1));
+ for(i = 0; i < Pretarget.length; ++i) out.write_shift(2, Pretarget.charCodeAt(i));
out.write_shift(2, 0);
+ if(F & 0x08) write_HyperlinkString(hashidx > -1 ? Target.slice(hashidx+1): "", out);
} else {
data = "03 03 00 00 00 00 00 00 c0 00 00 00 00 00 00 46".split(" ");
for(i = 0; i < data.length; ++i) out.write_shift(1, parseInt(data[i], 16));
var P = 0;
while(Target.slice(P*3,P*3+3)=="../"||Target.slice(P*3,P*3+3)=="..\\") ++P;
out.write_shift(2, P);
- out.write_shift(4, Target.length + 1);
- for(i = 0; i < Target.length; ++i) out.write_shift(1, Target.charCodeAt(i) & 0xFF);
+ out.write_shift(4, Target.length - 3 * P + 1);
+ for(i = 0; i < Target.length - 3 * P; ++i) out.write_shift(1, Target.charCodeAt(i + 3 * P) & 0xFF);
out.write_shift(1, 0);
out.write_shift(2, 0xFFFF);
out.write_shift(2, 0xDEAD);
diff --git a/bits/40_harb.js b/bits/40_harb.js
index 6edce7c..a823197 100644
--- a/bits/40_harb.js
+++ b/bits/40_harb.js
@@ -352,7 +352,7 @@ var SYLK = (function() {
Au:'ù', Bu:'ú', Cu:'û', Hu:'ü',
KC:'Ç', Kc:'ç', q:'æ', z:'œ', a:'Æ', j:'Œ',
DN:209, Dn:241, Hy:255,
- S:169, c:170, R:174, B:180,
+ S:169, c:170, R:174, "B ":180,
/*::[*/0/*::]*/:176, /*::[*/1/*::]*/:177, /*::[*/2/*::]*/:178,
/*::[*/3/*::]*/:179, /*::[*/5/*::]*/:181, /*::[*/6/*::]*/:182,
/*::[*/7/*::]*/:183, Q:185, k:186, b:208, i:216, l:222, s:240, y:248,
diff --git a/bits/67_wsxml.js b/bits/67_wsxml.js
index beed368..23f8a3c 100644
--- a/bits/67_wsxml.js
+++ b/bits/67_wsxml.js
@@ -156,9 +156,9 @@ function parse_ws_xml_hlinks(s, data/*:Array*/, rels) {
var rel = ((rels || {})['!id']||[])[val.id];
if(rel) {
val.Target = rel.Target;
- if(val.location) val.Target += "#"+val.location;
+ if(val.location) val.Target += "#"+unescapexml(val.location);
} else {
- val.Target = "#" + val.location;
+ val.Target = "#" + unescapexml(val.location);
rel = {Target: val.Target, TargetMode: 'Internal'};
}
val.Rel = rel;
diff --git a/bits/75_xlml.js b/bits/75_xlml.js
index 08aac77..9fec98b 100644
--- a/bits/75_xlml.js
+++ b/bits/75_xlml.js
@@ -183,6 +183,7 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ {
else str = utf8read(str);
}
var opening = str.slice(0, 1024).toLowerCase(), ishtml = false;
+ if((opening.indexOf(">") & 1023) > Math.min((opening.indexOf(",") & 1023), (opening.indexOf(";")&1023))) { var _o = dup(opts); _o.type = "string"; return PRN.to_workbook(str, _o); }
if(opening.indexOf("= 0) ishtml = true; });
if(ishtml) return HTML_.to_workbook(str, opts);
var Rn;
@@ -226,7 +227,7 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ {
} else cursheet[encode_col(c) + encode_row(r)] = cell;
}
if(cell.HRef) {
- cell.l = ({Target:cell.HRef}/*:any*/);
+ cell.l = ({Target:unescapexml(cell.HRef)}/*:any*/);
if(cell.HRefScreenTip) cell.l.Tooltip = cell.HRefScreenTip;
delete cell.HRef; delete cell.HRefScreenTip;
}
diff --git a/bits/80_parseods.js b/bits/80_parseods.js
index c539a75..fb04786 100644
--- a/bits/80_parseods.js
+++ b/bits/80_parseods.js
@@ -221,7 +221,7 @@ var parse_content_xml = (function() {
case 'scripts': // 3.12
case 'styles': // TODO
case 'font-face-decls': // 3.14
- case 'master-styles': //3.15.4 -- relevant for FODS
+ case 'master-styles': // 3.15.4 -- relevant for FODS
if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], true]);
break;
@@ -515,11 +515,11 @@ var parse_content_xml = (function() {
if(Rn[1]!== '/') {
atag = parsexmltag(Rn[0], false);
if(!atag.href) break;
- atag.Target = atag.href; delete atag.href;
+ atag.Target = unescapexml(atag.href); delete atag.href;
if(atag.Target.charAt(0) == "#" && atag.Target.indexOf(".") > -1) {
_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);
}
break;
diff --git a/bits/81_writeods.js b/bits/81_writeods.js
index b8ececb..773b761 100644
--- a/bits/81_writeods.js
+++ b/bits/81_writeods.js
@@ -37,9 +37,17 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() {
var R=0,C=0, range = decode_range(ws['!ref']||"A1");
var marr/*:Array*/ = ws['!merges'] || [], mi = 0;
var dense = Array.isArray(ws);
- for(R = 0; R < range.s.r; ++R) o.push(' \n');
+ if(ws["!cols"]) {
+ for(C = 0; C <= range.e.c; ++C) o.push(' \n');
+ }
+ var H = "", ROWS = ws["!rows"]||[];
+ for(R = 0; R < range.s.r; ++R) {
+ H = ROWS[R] ? ' table:style-name="ro' + ROWS[R].ods + '"' : "";
+ o.push(' \n');
+ }
for(; R <= range.e.r; ++R) {
- o.push(' \n');
+ H = ROWS[R] ? ' table:style-name="ro' + ROWS[R].ods + '"' : "";
+ o.push(' \n');
for(C=0; C < range.s.c; ++C) o.push(null_cell_xml);
for(; C <= range.e.c; ++C) {
var skip = false, ct = {}, textp = "";
@@ -92,8 +100,11 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() {
}
var text_p = write_text_p(textp);
if(cell.l && cell.l.Target) {
- var _tgt = cell.l.Target; _tgt = _tgt.charAt(0) == "#" ? "#" + csf_to_ods_3D(_tgt.slice(1)) : _tgt;
- text_p = writextag('text:a', text_p, {'xlink:href': _tgt});
+ var _tgt = cell.l.Target;
+ _tgt = _tgt.charAt(0) == "#" ? "#" + csf_to_ods_3D(_tgt.slice(1)) : _tgt;
+ // TODO: choose correct parent path format based on link delimiters
+ if(_tgt.charAt(0) != "#" && !_tgt.match(/^\w+:/)) _tgt = '../' + _tgt;
+ text_p = writextag('text:a', text_p, {'xlink:href': _tgt.replace(/&/g, "&")});
}
o.push(' ' + writextag('table:table-cell', writextag('text:p', text_p, {}), ct) + '\n');
}
@@ -103,7 +114,7 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() {
return o.join("");
};
- var write_automatic_styles_ods = function(o/*:Array*/) {
+ var write_automatic_styles_ods = function(o/*:Array*/, wb) {
o.push(' \n');
o.push(' \n');
@@ -114,6 +125,41 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() {
o.push(' \n');
o.push(' \n');
+ /* column styles */
+ var cidx = 0;
+ wb.SheetNames.map(function(n) { return wb.Sheets[n]; }).forEach(function(ws) {
+ if(!ws) return;
+ if(ws["!cols"]) {
+ for(var C = 0; C < ws["!cols"].length; ++C) if(ws["!cols"][C]) {
+ var colobj = ws["!cols"][C];
+ if(colobj.width == null && colobj.wpx == null && colobj.wch == null) continue;
+ process_col(colobj);
+ colobj.ods = cidx;
+ var w = ws["!cols"][C].wpx + "px";
+ o.push(' \n');
+ o.push(' \n');
+ o.push(' \n');
+ ++cidx;
+ }
+ }
+ });
+
+ /* row styles */
+ var ridx = 0;
+ wb.SheetNames.map(function(n) { return wb.Sheets[n]; }).forEach(function(ws) {
+ if(!ws) return;
+ if(ws["!rows"]) {
+ for(var R = 0; R < ws["!rows"].length; ++R) if(ws["!rows"][R]) {
+ ws["!rows"][R].ods = ridx;
+ var h = ws["!rows"][R].hpx + "px";
+ o.push(' \n');
+ o.push(' \n');
+ o.push(' \n');
+ ++ridx;
+ }
+ }
+ });
+
/* table */
o.push(' \n'); // style:master-page-name="mp1">\n');
o.push(' \n');
@@ -175,9 +221,13 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() {
'office:mimetype': "application/vnd.oasis.opendocument.spreadsheet"
});
- if(opts.bookType == "fods") o.push('\n');
- else o.push('\n');
- write_automatic_styles_ods(o);
+ if(opts.bookType == "fods") {
+ o.push('\n');
+ o.push(write_meta_ods().replace(/office:document-meta/g, "office:meta"));
+ // TODO: settings (equiv of settings.xml for ODS)
+ } else o.push('\n');
+ // o.push(' \n');
+ write_automatic_styles_ods(o, wb);
o.push(' \n');
o.push(' \n');
for(var i = 0; i != wb.SheetNames.length; ++i) o.push(write_ws(wb.Sheets[wb.SheetNames[i]], wb, i, opts));
@@ -217,7 +267,7 @@ function write_ods(wb/*:any*/, opts/*:any*/) {
/* TODO: this is hard-coded to satiate excel */
f = "meta.xml";
- zip_add_file(zip, f, write_meta_ods(/*::wb, opts*/));
+ zip_add_file(zip, f, XML_HEADER + write_meta_ods(/*::wb, opts*/));
manifest.push([f, "text/xml"]);
rdf.push([f, "MetadataFile"]);
diff --git a/bits/87_read.js b/bits/87_read.js
index c15b0d7..e866cfd 100644
--- a/bits/87_read.js
+++ b/bits/87_read.js
@@ -66,9 +66,9 @@ function read_prn(data, d, o, str) {
function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
reset_cp();
- if(typeof ArrayBuffer !== 'undefined' && data instanceof ArrayBuffer) return readSync(new Uint8Array(data), opts);
- var d = data, n = [0,0,0,0], str = false;
var o = opts||{};
+ if(typeof ArrayBuffer !== 'undefined' && data instanceof ArrayBuffer) return readSync(new Uint8Array(data), (o = dup(o), o.type = "array", o));
+ var d = data, n = [0,0,0,0], str = false;
if(o.cellStyles) { o.cellNF = true; o.sheetStubs = true; }
_ssfopts = {};
if(o.dateNF) _ssfopts.dateNF = o.dateNF;
diff --git a/bits/90_utils.js b/bits/90_utils.js
index efb1ddf..af0d998 100644
--- a/bits/90_utils.js
+++ b/bits/90_utils.js
@@ -23,13 +23,14 @@ function make_json_row(sheet/*:Worksheet*/, r/*:Range*/, R/*:number*/, cols/*:Ar
var v = val.v;
switch(val.t){
case 'z': if(v == null) break; continue;
- case 'e': v = void 0; break;
+ case 'e': v = (v == 0 ? null : void 0); break;
case 's': case 'd': case 'b': case 'n': break;
default: throw new Error('unrecognized type ' + val.t);
}
if(hdr[C] != null) {
if(v == null) {
- if(defval !== undefined) row[hdr[C]] = defval;
+ if(val.t == "e" && v === null) row[hdr[C]] = null;
+ else if(defval !== undefined) row[hdr[C]] = defval;
else if(raw && v === null) row[hdr[C]] = null;
else continue;
} else {
@@ -221,6 +222,7 @@ function sheet_add_json(_ws/*:?Worksheet*/, js/*:Array*/, opts)/*:Worksheet
if(!o.cellDates) { t = 'n'; v = datenum(v); }
z = (o.dateNF || SSF._table[14]);
}
+ else if(v === null && o.nullError) { t = 'e'; v = 0; }
if(!cell) ws[ref] = cell = ({t:t, v:v}/*:any*/);
else {
cell.t = t; cell.v = v;
diff --git a/docbits/64_cellprops.md b/docbits/64_cellprops.md
index d0b48fb..f4cb805 100644
--- a/docbits/64_cellprops.md
+++ b/docbits/64_cellprops.md
@@ -1,5 +1,14 @@
#### Hyperlinks
+
+ Format Support (click to show)
+
+**Cell Hyperlinks**: XLSX/M, XLSB, BIFF8 XLS, XLML, ODS
+
+**Tooltips**: XLSX/M, XLSB, BIFF8 XLS, XLML
+
+
+
Hyperlinks are stored in the `l` key of cell objects. The `Target` field of the
hyperlink object is the target of the link, including the URI fragment. Tooltips
are stored in the `Tooltip` field and are displayed when you move your mouse
@@ -9,16 +18,55 @@ For example, the following snippet creates a link from cell `A3` to
with the tip `"Find us @ SheetJS.com!"`:
```js
-ws['A3'].l = { Target:"https://sheetjs.com", Tooltip:"Find us @ SheetJS.com!" };
+ws['A1'].l = { Target:"https://sheetjs.com", Tooltip:"Find us @ SheetJS.com!" };
```
Note that Excel does not automatically style hyperlinks -- they will generally
be displayed as normal text.
+_Remote Links_
+
+HTTP / HTTPS links can be used directly:
+
+```js
+ws['A2'].l = { Target:"https://docs.sheetjs.com/#hyperlinks" };
+ws['A3'].l = { Target:"http://localhost:7262/yes_localhost_works" };
+```
+
+Excel also supports `mailto` email links with subject line:
+
+```js
+ws['A4'].l = { Target:"mailto:ignored@dev.null" };
+ws['A5'].l = { Target:"mailto:ignored@dev.null?subject=Test Subject" };
+```
+
+_Local Links_
+
+Links to absolute paths should use the `file://` URI scheme:
+
+```js
+ws['B1'].l = { Target:"file:///SheetJS/t.xlsx" }; /* Link to /SheetJS/t.xlsx */
+ws['B2'].l = { Target:"file:///c:/SheetJS.xlsx" }; /* Link to c:\SheetJS.xlsx */
+```
+
+Links to relative paths can be specified without a scheme:
+
+```js
+ws['B3'].l = { Target:"SheetJS.xlsb" }; /* Link to SheetJS.xlsb */
+ws['B4'].l = { Target:"../SheetJS.xlsm" }; /* Link to ../SheetJS.xlsm */
+```
+
+Relative Paths have undefined behavior in the SpreadsheetML 2003 format. Excel
+2019 will treat a `..\` parent mark as two levels up.
+
+_Internal Links_
+
Links where the target is a cell or range or defined name in the same workbook
("Internal Links") are marked with a leading hash character:
```js
-ws['A2'].l = { Target:"#E2" }; /* link to cell E2 */
+ws['C1'].l = { Target:"#E2" }; /* Link to cell E2 */
+ws['C2'].l = { Target:"#Sheet2!E2" }; /* Link to cell E2 in sheet Sheet2 */
+ws['C3'].l = { Target:"#SomeDefinedName" }; /* Link to Defined Name */
```
diff --git a/docbits/82_util.md b/docbits/82_util.md
index e770fb6..46fb680 100644
--- a/docbits/82_util.md
+++ b/docbits/82_util.md
@@ -22,11 +22,12 @@ as the corresponding styles. Dates are stored as date or numbers. Array holes
and explicit `undefined` values are skipped. `null` values may be stubbed. All
other values are stored as strings. The function takes an options argument:
-| Option Name | Default | Description |
-| :---------- | :------: | :-------------------------------------------------- |
-|`dateNF` | FMT 14 | Use specified date format in string output |
-|`cellDates` | false | Store dates as type `d` (default is `n`) |
-|`sheetStubs` | false | Create cell objects of type `z` for `null` values |
+| Option Name | Default | Description |
+| :---------- | :-----: | :--------------------------------------------------- |
+|`dateNF` | FMT 14 | Use specified date format in string output |
+|`cellDates` | false | Store dates as type `d` (default is `n`) |
+|`sheetStubs` | false | Create cell objects of type `z` for `null` values |
+|`nullError` | false | If true, emit `#NULL!` error cells for `null` values |
Examples (click to show)
@@ -46,12 +47,13 @@ var ws = XLSX.utils.aoa_to_sheet([
existing worksheet object. It follows the same process as `aoa_to_sheet` and
accepts an options argument:
-| Option Name | Default | Description |
-| :---------- | :------: | :-------------------------------------------------- |
-|`dateNF` | FMT 14 | Use specified date format in string output |
-|`cellDates` | false | Store dates as type `d` (default is `n`) |
-|`sheetStubs` | false | Create cell objects of type `z` for `null` values |
-|`origin` | | Use specified cell as starting point (see below) |
+| Option Name | Default | Description |
+| :---------- | :-----: | :--------------------------------------------------- |
+|`dateNF` | FMT 14 | Use specified date format in string output |
+|`cellDates` | false | Store dates as type `d` (default is `n`) |
+|`sheetStubs` | false | Create cell objects of type `z` for `null` values |
+|`nullError` | false | If true, emit `#NULL!` error cells for `null` values |
+|`origin` | | Use specified cell as starting point (see below) |
`origin` is expected to be one of:
@@ -110,11 +112,14 @@ default column order is determined by the first appearance of the field using
|`dateNF` | FMT 14 | Use specified date format in string output |
|`cellDates` | false | Store dates as type `d` (default is `n`) |
|`skipHeader` | false | If true, do not include header row in output |
+|`nullError` | false | If true, emit `#NULL!` error cells for `null` values |
- All fields from each row will be written. If `header` is an array and it does
not contain a particular field, the key will be appended to the array.
- Cell types are deduced from the type of each value. For example, a `Date`
object will generate a Date cell, while a string will generate a Text cell.
+- Null values will be skipped by default. If `nullError` is true, an error cell
+ corresponding to `#NULL!` will be written to the worksheet.
Examples (click to show)
@@ -145,13 +150,14 @@ var ws = XLSX.utils.json_to_sheet([
worksheet object. It follows the same process as `json_to_sheet` and accepts
an options argument:
-| Option Name | Default | Description |
-| :---------- | :------: | :-------------------------------------------------- |
-|`header` | | Use specified column order (default `Object.keys`) |
-|`dateNF` | FMT 14 | Use specified date format in string output |
-|`cellDates` | false | Store dates as type `d` (default is `n`) |
-|`skipHeader` | false | If true, do not include header row in output |
-|`origin` | | Use specified cell as starting point (see below) |
+| Option Name | Default | Description |
+| :---------- | :-----: | :--------------------------------------------------- |
+|`header` | | Use specified column order (default `Object.keys`) |
+|`dateNF` | FMT 14 | Use specified date format in string output |
+|`cellDates` | false | Store dates as type `d` (default is `n`) |
+|`skipHeader` | false | If true, do not include header row in output |
+|`nullError` | false | If true, emit `#NULL!` error cells for `null` values |
+|`origin` | | Use specified cell as starting point (see below) |
`origin` is expected to be one of:
diff --git a/package.json b/package.json
index 082d2fc..1c1af17 100644
--- a/package.json
+++ b/package.json
@@ -41,7 +41,7 @@
"fflate": "^0.3.8",
"ssf": "~0.11.2",
"wmf": "~1.0.1",
- "word": "~0.4.0"
+ "word": "~0.3.0"
},
"devDependencies": {
"@sheetjs/uglify-js": "~2.7.3",
diff --git a/tests.lst b/tests.lst
index b5c793f..19468e7 100644
--- a/tests.lst
+++ b/tests.lst
@@ -277,9 +277,9 @@ apachepoi_evaluate_formula_with_structured_table_references.xlsx
apachepoi_headerFooterTest.xlsx
apachepoi_noSharedStringTable.xlsx
apachepoi_picture.xlsx
-apachepoi_poc-shared-strings.xlsx
-apachepoi_poc-xmlbomb-empty.xlsx
-apachepoi_poc-xmlbomb.xlsx
+# apachepoi_poc-shared-strings.xlsx # string length exceeds 32767 chars
+# apachepoi_poc-xmlbomb-empty.xlsx # string length exceeds 32767 chars
+# apachepoi_poc-xmlbomb.xlsx # string length exceeds 32767 chars
# apachepoi_protected_passtika.xlsx # password
apachepoi_ref-56737.xlsx
apachepoi_ref2-56737.xlsx
@@ -777,7 +777,7 @@ apachepoi_46136-NoWarnings.xls
apachepoi_46136-WithWarnings.xls
apachepoi_46137.xls
apachepoi_46250.xls
-apachepoi_46368.xls
+# apachepoi_46368.xls # string length exceeds 32767 chars
apachepoi_46445.xls
apachepoi_46515.xls
apachepoi_46670_http.xls