- UTF-16 Unicode Text (TXT) write
- Lotus Formatted Text (PRN) read/write
- DBF version 2 field length adjustments
- throw errors if SheetNames is invalid (fixes #376 h/t @pietersv)
This commit is contained in:
SheetJS 2017-04-02 20:16:03 -04:00
parent 3a310bd3a7
commit b9dae134f2
36 changed files with 525 additions and 182 deletions

@ -9,6 +9,7 @@ changes may not be included if they are not expected to break existing code.
* default output format changed to XLSB
* comment text line endings are now normalized
* errors thrown on write when worksheets have invalid names
## 0.9.7 (2017-03-28)

@ -75,9 +75,12 @@ bytes: ## display minified and gzipped file sizes
for i in dist/xlsx.min.js dist/xlsx.{core,full}.min.js; do printj "%-30s %7d %10d" $$i $$(wc -c < $$i) $$(gzip --best --stdout $$i | wc -c); done
.PHONY: graph
graph: formats.png ## Rebuild format conversion graph
graph: formats.png legend.png ## Rebuild format conversion graph
formats.png: formats.dot
circo -Tpng -o$@ $<
legend.png: misc/legend.dot
dot -Tpng -o$@ $<
.PHONY: nexe
nexe: xlsx.exe ## Build nexe standalone executable
@ -176,7 +179,7 @@ readme: README.md ## Update README Table of Contents
markdown-toc -i README.md
.PHONY: book
book: README.md ## Update summary for documentation
book: readme ## Update summary for documentation
printf "# Summary\n\n- [xlsx](README.md#xlsx)\n" > misc/docs/SUMMARY.md
markdown-toc README.md | sed 's/(#/(README.md#/g'>> misc/docs/SUMMARY.md

@ -15,6 +15,7 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6.
![circo graph of format support](formats.png)
![graph legend](legend.png)
## Table of Contents
@ -32,7 +33,7 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6.
* [Parsing functions](#parsing-functions)
* [Writing functions](#writing-functions)
* [Utilities](#utilities)
- [Workbook / Worksheet / Cell Object Description](#workbook--worksheet--cell-object-description)
- [Common Spreadsheet Format](#common-spreadsheet-format)
* [General Structures](#general-structures)
* [Cell Object](#cell-object)
+ [Data Types](#data-types)
@ -58,7 +59,8 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6.
* [Array of Arrays Input](#array-of-arrays-input)
* [HTML Table Input](#html-table-input)
* [Formulae Output](#formulae-output)
* [CSV and general DSV Output](#csv-and-general-dsv-output)
* [Delimiter-Separated Output](#delimiter-separated-output)
+ [UTF-16 Unicode Text](#utf-16-unicode-text)
* [JSON](#json)
- [File Formats](#file-formats)
* [Excel 2007+ XML (XLSX/XLSM)](#excel-2007-xml-xlsxxlsm)
@ -66,11 +68,13 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6.
* [Excel 97-2004 Binary (BIFF8)](#excel-97-2004-binary-biff8)
* [Excel 2003-2004 (SpreadsheetML)](#excel-2003-2004-spreadsheetml)
* [Excel 2007+ Binary (XLSB, BIFF12)](#excel-2007-binary-xlsb-biff12)
* [OpenDocument Spreadsheet (ODS/FODS) and Uniform Office Spreadsheet (UOS1/2)](#opendocument-spreadsheet-odsfods-and-uniform-office-spreadsheet-uos12)
* [Comma-Separated Values (CSV)](#comma-separated-values-csv)
* [OpenDocument Spreadsheet (ODS/FODS)](#opendocument-spreadsheet-odsfods)
+ [Uniform Office Spreadsheet (UOS1/2)](#uniform-office-spreadsheet-uos12)
* [Delimiter-Separated Values (CSV/TXT)](#delimiter-separated-values-csvtxt)
* [Other Single-Worksheet Formats](#other-single-worksheet-formats)
+ [dBASE and Visual FoxPro (DBF)](#dbase-and-visual-foxpro-dbf)
+ [Symbolic Link (SYLK)](#symbolic-link-sylk)
+ [Lotus Formatted Text (PRN)](#lotus-formatted-text-prn)
+ [Data Interchange Format (DIF)](#data-interchange-format-dif)
+ [HTML](#html)
- [Testing](#testing)
@ -429,7 +433,7 @@ Exporters are described in the [Utility Functions](#utility-functions) section.
- `{en,de}code_cell` converts cell addresses
- `{en,de}code_range` converts cell ranges
## Workbook / Worksheet / Cell Object Description
## Common Spreadsheet Format
js-xlsx conforms to the Common Spreadsheet Format (CSF):
@ -879,6 +883,12 @@ file but Excel will know how to handle it. This library applies similar logic:
DBF files are detected based on the first byte as well as the third and fourth
bytes (corresponding to month and day of the file date)
Plaintext format guessing follows the priority order:
| Format | Test |
|:-------|:--------------------------------------------------------------------|
| PRN | (default) |
## Writing Options
The exported `write` and `writeFile` functions accept an options argument:
@ -910,16 +920,22 @@ output formats. The specific file type is controlled with `bookType` option:
| `xlsx` | `.xlsx` | ZIP | multi | Excel 2007+ XML Format |
| `xlsm` | `.xlsm` | ZIP | multi | Excel 2007+ Macro XML Format |
| `xlsb` | `.xlsb` | ZIP | multi | Excel 2007+ Binary Format |
| `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet |
| `biff2` | `.xls` | none | single | Excel 2.0 Worksheet format |
| `xlml` | `.xls` | none | multi | Excel 2003-2004 (SpreadsheetML) |
| `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet |
| `fods` | `.fods` | none | multi | Flat OpenDocument Spreadsheet |
| `csv` | `.csv` | none | single | Comma Separated Values |
| `txt` | `.txt` | none | single | UTF-16 Unicode Text (TXT) |
| `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) |
| `dif` | `.dif` | none | single | Data Interchange Format (DIF) |
| `prn` | `.prn` | none | single | Lotus Formatted Text |
- `compression` only applies to formats with ZIP containers.
- Formats that only support a single sheet require a `sheet` option specifying
the worksheet. If the string is empty, the first worksheet is used.
- `writeFile` will automatically guess the output file format based on the file
extension if `bookType` is not specified. It will choose the first format in
the aforementioned table that matches the extension.
### Output Type
@ -1013,7 +1029,7 @@ accordance with Excel. For the example sheet:
[ 'A1=\'S', 'F1=\'J', 'D2=4', 'B3=3', 'G3=8' ]
```
### CSV and general DSV Output
### Delimiter-Separated Output
As an alternative to the `writeFile` CSV type, `XLSX.utils.sheet_to_csv` also
produces CSV output. The function takes an options argument:
@ -1044,6 +1060,12 @@ S h e e t J S
S:h:e:e:t:J:S|1:2:3:4:5:6:7|2:3:4:5:6:7:8|
```
#### UTF-16 Unicode Text
The `txt` output type uses the tab character as the field separator. If the
codepage library is available (included in the full distribution but not core),
the output will be encoded in codepage `1200` and the BOM will be prepended.
### JSON
`XLSX.utils.sheet_to_json` and the alias `XLSX.utils.sheet_to_row_object_array`
@ -1145,9 +1167,11 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Excel 3.0 (XLS BIFF3) | :o: | |
| Excel 2.0/2.1 (XLS BIFF2) | :o: | :o: |
| **Excel Supported Text Formats** |:-----:|:-----:|
| Delimiter-Separated Values (CSV/TSV/DSV) | | :o: |
| Delimiter-Separated Values (CSV/TXT) | | :o: |
| Data Interchange Format (DIF) | :o: | :o: |
| Symbolic Link (SYLK/SLK) | :o: | :o: |
| Lotus Formatted Text (PRN) | :o: | :o: |
| UTF-16 Unicode Text (TXT) | | :o: |
| **Other Workbook/Worksheet Formats** |:-----:|:-----:|
| OpenDocument Spreadsheet (ODS) | :o: | :o: |
| Flat XML ODF Spreadsheet (FODS) | :o: | :o: |
@ -1203,22 +1227,26 @@ in an XLSX sub-file can be mapped to XLSB records in a corresponding sub-file.
The `MS-XLSB` specification covers the basics of the file format, and other
specifications expand on serialization of features like properties.
### OpenDocument Spreadsheet (ODS/FODS) and Uniform Office Spreadsheet (UOS1/2)
### OpenDocument Spreadsheet (ODS/FODS)
ODS is an XML-in-ZIP format akin to XLSX while FODS is an XML format akin to
SpreadsheetML. Both are detailed in the OASIS standard, but tools like LO/OO
add undocumented extensions.
#### Uniform Office Spreadsheet (UOS1/2)
UOS is a very similar format, and it comes in 2 varieties corresponding to ODS
and FODS respectively. For the most part, the difference between the formats
lies in the names of tags and attributes.
### Comma-Separated Values (CSV)
### Delimiter-Separated Values (CSV/TXT)
Excel CSV deviates from RFC4180 in a number of important ways. The generated
CSV files should generally work in Excel although they may not work in RFC4180
compatible readers.
Excel TXT uses tab as the delimiter and codepage 1200.
### Other Single-Worksheet Formats
Many older formats supported only one worksheet:
@ -1237,6 +1265,12 @@ limited by the general ability to read arbitrary files in the web browser.
There is no real documentation. All knowledge was gathered by saving files in
various versions of Excel to deduce the meaning of fields.
#### Lotus Formatted Text (PRN)
There is no real documentation, and in fact Excel treats PRN as an output-only
file format. Nevertheless we can guess the column widths and reverse-engineer
the original layout.
#### Data Interchange Format (DIF)
There is no unified definition. Visicalc DIF differs from Lotus DIF, and both

@ -21,7 +21,7 @@ program
.option('-Y, --ods', 'emit ODS to <sheetname> or <file>.ods')
.option('-2, --biff2','emit XLS to <sheetname> or <file>.xls (BIFF2)')
.option('-6, --xlml', 'emit SSML to <sheetname> or <file>.xls (2003 XML)')
.option('-T, --fods', 'emit FODS to <sheetname> or <file>.xls (Flat ODS)')
.option('-T, --fods', 'emit FODS to <sheetname> or <file>.fods (Flat ODS)')
.option('-S, --formulae', 'print formulae')
.option('-j, --json', 'emit formatted JSON (all fields text)')
@ -29,6 +29,8 @@ program
.option('-A, --arrays', 'emit rows as JS objects (raw numbers)')
.option('-D, --dif', 'emit data interchange format (dif)')
.option('-K, --sylk', 'emit symbolic link (sylk)')
.option('-P, --prn', 'emit formatted text (prn)')
.option('-t, --txt', 'emit delimited text (txt)')
.option('-F, --field-sep <sep>', 'CSV field separator', ",")
.option('-R, --row-sep <sep>', 'CSV row separator', "\n")
@ -163,10 +165,12 @@ try {
if(program.perf) process.exit(0);
/* single worksheet XLS formats */
/* single worksheet formats */
[
['biff2', '.xls'],
['sylk', '.slk'],
['prn', '.prn'],
['txt', '.txt'],
['dif', '.dif']
].forEach(function(m) { if(program[m[0]]) {
wopts.bookType = m[0];

@ -14,4 +14,6 @@ type EvertNumType = {[string]:number};
type EvertArrType = {[string]:Array<string>};
type StringConv = {(string):string};
type WriteObjStrFactory = {from_sheet(ws:Worksheet, o:any):string};
*/

@ -22,9 +22,8 @@ function aoa_to_sheet(data/*:AOA*/, opts/*:?any*/)/*:Worksheet*/ {
else if(typeof cell.v === 'boolean') cell.t = 'b';
else if(cell.v instanceof Date) {
cell.z = o.dateNF || SSF._table[14];
if(o.cellDates) cell.t = 'd';
else { cell.t = 'n'; cell.v = datenum(cell.v); }
cell.w = SSF.format(cell.z, cell.v);
if(o.cellDates) { cell.t = 'd'; cell.w = SSF.format(cell.z, datenum(cell.v)); }
else { cell.t = 'n'; cell.v = datenum(cell.v); cell.w = SSF.format(cell.z, cell.v); }
}
else cell.t = 's';
ws[cell_ref] = cell;

@ -57,7 +57,7 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
var memo = false;
var vfp = false;
switch(ft) {
case 0x03: break;
case 0x02: case 0x03: break;
case 0x30: vfp = true; memo = true; break;
case 0x31: vfp = true; break;
case 0x83: memo = true; break;
@ -65,33 +65,38 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
case 0xF5: memo = true; break;
default: throw new Error("DBF Unsupported Version: " + ft.toString(16));
}
var filedate = new Date(d.read_shift(1) + 1900, d.read_shift(1) - 1, d.read_shift(1));
var nrow = d.read_shift(4);
var fpos = d.read_shift(2);
var filedate = new Date(), nrow = 0, fpos = 0;
if(ft == 0x02) nrow = d.read_shift(2);
filedate = new Date(d.read_shift(1) + 1900, d.read_shift(1) - 1, d.read_shift(1));
if(ft != 0x02) nrow = d.read_shift(4);
if(ft != 0x02) fpos = d.read_shift(2);
var rlen = d.read_shift(2);
d.l+=16;
var flags = d.read_shift(1);
var flags = 0, current_cp = 1252;
if(ft != 0x02) {
d.l+=16;
flags = d.read_shift(1);
//if(memo && ((flags & 0x02) === 0)) throw new Error("DBF Flags " + flags.toString(16) + " ft " + ft.toString(16));
/* codepage present in FoxPro */
var current_cp = 1252;
if(d[d.l] !== 0) current_cp = dbf_codepage_map[d[d.l]];
d.l+=1;
d.l+=2;
}
var fields = [], field = {};
var hend = fpos - 10 - (vfp ? 264 : 0);
while(d.l < hend) {
while(ft == 0x02 ? d.l < d.length && d[d.l] != 0x0d: d.l < hend) {
field = {};
field.name = cptable.utils.decode(current_cp, d.slice(d.l, d.l+10)).replace(/[\u0000\r\n].*$/g,"");
d.l += 11;
field.type = String.fromCharCode(d.read_shift(1));
field.offset = d.read_shift(4);
if(ft != 0x02) field.offset = d.read_shift(4);
field.len = d.read_shift(1);
if(ft == 0x02) field.offset = d.read_shift(2);
field.dec = d.read_shift(1);
if(field.name.length) fields.push(field);
d.l += 14;
if(ft != 0x02) d.l += 14;
switch(field.type) {
// case 'B': break; // Binary
case 'C': break; // character
@ -113,8 +118,11 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
}
}
if(d[d.l] !== 0x0D) d.l = fpos-1;
if(d.read_shift(1) !== 0x0D) throw new Error("DBF Terminator not found " + d.l + " " + d[d.l]);
d.l = fpos;
else if(ft == 0x02) d.l = 0x209;
if(ft != 0x02) {
if(d.read_shift(1) !== 0x0D) throw new Error("DBF Terminator not found " + d.l + " " + d[d.l]);
d.l = fpos;
}
/* data */
var R = 0, C = 0;
out[0] = [];
@ -162,7 +170,7 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
}
}
}
if(d.l < d.length && d[d.l++] != 0x1A) throw new Error("DBF EOF Marker missing " + (d.l-1) + " of " + d.length + " " + d[d.l-1].toString(16));
if(ft != 0x02) if(d.l < d.length && d[d.l++] != 0x1A) throw new Error("DBF EOF Marker missing " + (d.l-1) + " of " + d.length + " " + d[d.l-1].toString(16));
return out;
}
@ -374,8 +382,9 @@ var PRN = (function() {
function set_text_arr(data/*:string*/, arr/*:AOA*/, R/*:number*/, C/*:number*/) {
if(data === 'TRUE') arr[R][C] = true;
else if(data === 'FALSE') arr[R][C] = false;
else if(data === ""){}
else if(+data == +data) arr[R][C] = +data;
else if(data !== "") arr[R][C] = data;
else arr[R][C] = data;
}
function prn_to_aoa_str(f/*:string*/, opts)/*:AOA*/ {
@ -406,9 +415,27 @@ var PRN = (function() {
function prn_to_workbook(str/*:string*/, opts)/*:Workbook*/ { return sheet_to_workbook(prn_to_sheet(str, opts), opts); }
function sheet_to_prn(ws/*:Worksheet*/, opts/*:?any*/)/*:string*/ {
var o/*:Array<string>*/ = [];
var r = decode_range(ws['!ref']), cell/*:Cell*/;
for(var R = r.s.r; R <= r.e.r; ++R) {
var oo = [];
for(var C = r.s.c; C <= r.e.c; ++C) {
var coord = encode_cell({r:R,c:C});
if(!(cell = ws[coord]) || cell.v == null) { oo.push(" "); continue; }
var w = (cell.w || (format_cell(cell), cell.w) || "").substr(0,10);
while(w.length < 10) w += " ";
oo.push(w + (C == 0 ? " " : ""));
}
o.push(oo.join(""));
}
return o.join("\n");
}
return {
to_workbook: prn_to_workbook,
to_sheet: prn_to_sheet
to_sheet: prn_to_sheet,
from_sheet: sheet_to_prn
};
})();

@ -20,8 +20,9 @@ function parse_comments_xml(data/*:string*/, opts)/*:Array<Comment>*/ {
var cell = decode_cell(y.ref);
if(opts.sheetRows && opts.sheetRows <= cell.r) return;
var textMatch = x.match(/<(?:\w+:)?text>([^\u2603]*)<\/(?:\w+:)?text>/);
var rt = (!textMatch || !textMatch[1]) ? {r:"",t:"",h:""} : parse_si(textMatch[1]);
var rt = !!textMatch && !!textMatch[1] && parse_si(textMatch[1]) || {r:"",t:"",h:""};
comment.r = rt.r;
if(rt.r == "<t></t>") rt.t = rt.h = "";
comment.t = rt.t.replace(/\r\n/g,"\n").replace(/\r/g,"\n");
if(opts.cellHTML) comment.h = rt.h;
commentList.push(comment);
@ -48,7 +49,7 @@ function write_comments_xml(data, opts) {
d[1].forEach(function(c) {
/* 18.7.3 CT_Comment */
o.push('<comment ref="' + d[0] + '" authorId="' + iauthor.indexOf(escapexml(c.a)) + '"><text>');
o.push(writetag("t", c.t));
o.push(writetag("t", c.t == null ? "" : c.t));
o.push('</text></comment>');
});
});

@ -11,7 +11,7 @@ function parse_BrtBeginComment(data, length) {
function write_BrtBeginComment(data, o) {
if(o == null) o = new_buf(36);
o.write_shift(4, data[1].iauthor);
write_UncheckedRfX(data[0], o);
write_UncheckedRfX((data[0]/*:any*/), o);
o.write_shift(4, 0);
o.write_shift(4, 0);
o.write_shift(4, 0);
@ -74,7 +74,7 @@ function write_comments_bin(data, opts) {
data.forEach(function(comment) {
comment[1].forEach(function(c) {
c.iauthor = iauthor.indexOf(c.a);
var range = {s:decode_cell(comment[0])}; range.e = range.s;
var range = {s:decode_cell(comment[0]),e:decode_cell(comment[0])};
write_record(ba, "BrtBeginComment", write_BrtBeginComment([range, c]));
if(c.t && c.t.length > 0) write_record(ba, "BrtCommentText", write_RichStr(c));
write_record(ba, "BrtEndComment");

@ -99,9 +99,16 @@ function parse_wb_defaults(wb) {
_ssfopts.date1904 = parsexmlbool(wb.WBProps.date1904, 'date1904');
}
/* TODO: validate workbook */
function check_wb_names(N) {
var badchars = "][*?\/\\".split("");
N.forEach(function(n,i) {
badchars.forEach(function(c) { if(n.indexOf(c) > -1) throw new Error("Sheet name cannot contain : \\ / ? * [ ]"); });
if(n.length > 31) throw new Error("Sheet names cannot exceed 31 chars");
for(var j = 0; j < i; ++j) if(n == N[j]) throw new Error("Duplicate Sheet Name: " + n);
});
}
function check_wb(wb) {
if(!wb || !wb.SheetNames || !wb.Sheets) throw new Error("Invalid Workbook");
for(var i = 0; i < wb.SheetNames.length; ++i) for(var j = 0; j < i; ++j)
if(wb.SheetNames[i] == wb.SheetNames[j]) throw new Error("Duplicate Sheet Name: " + wb.SheetNames[i]);
check_wb_names(wb.SheetNames);
/* TODO: validate workbook */
}

@ -25,7 +25,7 @@ var parse_content_xml = (function() {
var sheetag/*:: = {name:"", '名称':""}*/;
var rowtag/*:: = {'行号':""}*/;
var Sheets = {}, SheetNames/*:Array<string>*/ = [], ws = {};
var Rn, q/*:: = ({t:"", v:null, z:null, w:""}:any)*/;
var Rn, q/*:: = ({t:"", v:null, z:null, w:"",c:[]}:any)*/;
var ctag = {value:""};
var textp = "", textpidx = 0, textptag/*:: = {}*/;
var R = -1, C = -1, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
@ -80,7 +80,7 @@ var parse_content_xml = (function() {
if(R < range.s.r) range.s.r = R;
ctag = parsexmltag(Rn[0], false);
comments = []; comment = {};
q = ({t:ctag['数据类型'] || ctag['value-type'], v:null/*:: , z:null, w:""*/}/*:any*/);
q = ({t:ctag['数据类型'] || ctag['value-type'], v:null/*:: , z:null, w:"",c:[]*/}/*:any*/);
if(opts.cellFormula) {
if(ctag.formula) ctag.formula = unescapexml(ctag.formula);
if(ctag['number-matrix-columns-spanned'] && ctag['number-matrix-rows-spanned']) {
@ -138,7 +138,7 @@ var parse_content_xml = (function() {
if(range.e.c <= C) range.e.c = C;
}
} else { C += rept; rept = 0; }
q = {/*:: t:"", v:null, z:null, w:""*/};
q = {/*:: t:"", v:null, z:null, w:"",c:[]*/};
textp = "";
}
break; // 9.1.4 <table:table-cell>
@ -162,7 +162,7 @@ var parse_content_xml = (function() {
comments.push(comment);
}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') {state.push([Rn[3], false]);}
creator = ""; creatorpidx = 0;
creator = ""; creatoridx = 0;
textp = ""; textpidx = 0;
break;

@ -1,13 +1,6 @@
/* actual implementation in utils, wrappers are for read/write */
function write_csv_str(wb/*:Workbook*/, o/*:WriteOpts*/) {
var idx = 0;
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
return sheet_to_csv(wb.Sheets[wb.SheetNames[idx]], o);
}
function write_obj_str(factory) {
return function write_str(wb/*:Workbook*/, o/*:WriteOpts*/) {
/* actual implementation elsewhere, wrappers are for read/write */
function write_obj_str(factory/*:WriteObjStrFactory*/) {
return function write_str(wb/*:Workbook*/, o/*:WriteOpts*/)/*:string*/ {
var idx = 0;
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
@ -15,5 +8,8 @@ function write_obj_str(factory) {
};
}
var write_csv_str = write_obj_str({from_sheet:sheet_to_csv});
var write_slk_str = write_obj_str(SYLK);
var write_dif_str = write_obj_str(DIF);
var write_prn_str = write_obj_str(PRN);
var write_txt_str = write_obj_str({from_sheet:sheet_to_txt});

@ -34,9 +34,10 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
/*::if(!wb.Props) throw "unreachable"; */
f = "docProps/app.xml";
if(!wb.Workbook || !wb.Workbook.Sheets) wb.Props.SheetNames = wb.SheetNames;
if(wb.Props && wb.Props.SheetNames){}
else if(!wb.Workbook || !wb.Workbook.Sheets) wb.Props.SheetNames = wb.SheetNames;
// $FlowIgnore
else wb.Props.SheetNames = wb.Workbook.Sheets.filter(function(x) { return x.Hidden != 2; }).map(function(x) { return x.name; });
else wb.Props.SheetNames = wb.SheetNames.map(function(x,i) { return [(wb.Workbook.Sheets[i]||{}).Hidden != 2, x];}).filter(function(x) { return x[0]; }).map(function(x) { return x[1]; });
wb.Props.Worksheets = wb.Props.SheetNames.length;
zip.file(f, write_ext_props(wb.Props, opts));
ct.extprops.push(f);

@ -38,10 +38,11 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
case 0x50: if(n[1] == 0x4B && n[2] < 0x20 && n[3] < 0x20) return read_zip(d, o); break;
case 0xEF: return parse_xlml(d, o);
case 0x03: case 0x83: case 0x8B: return DBF.to_workbook(d, o);
case 0x30: case 0x31: if(n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o); break;
default: throw new Error("Unsupported file " + n.join("|"));
}
throw new Error("Unsupported file format " + n.join("|"));
if(n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o);
if(0x20>n[0]||n[0]>0x7F) throw new Error("Unsupported file " + n.join("|"));
/* TODO: CSV / TXT */
return PRN.to_workbook(d, o);
}
function readFileSync(filename/*:string*/, opts/*:?ParseOpts*/)/*:Workbook*/ {

@ -14,6 +14,20 @@ function write_zip_type(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
return z.generate(oopts);
}
/* TODO: test consistency */
function write_bstr_type(out/*:string*/, opts/*:WriteOpts*/) {
switch(opts.type) {
case "base64": return Base64.encode(out);
case "binary": return out;
case "file": return _fs.writeFileSync(opts.file, out, 'binary');
case "buffer": {
if(has_buf) return new Buffer(out, 'utf8');
else return out.split("").map(function(c) { return c.charCodeAt(0); });
}
}
throw new Error("Unrecognized type " + opts.type);
}
/* TODO: test consistency */
function write_string_type(out/*:string*/, opts/*:WriteOpts*/) {
switch(opts.type) {
@ -50,8 +64,10 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
case 'xlml': return write_string_type(write_xlml(wb, o), o);
case 'slk':
case 'sylk': return write_string_type(write_slk_str(wb, o), o);
case 'txt': return write_bstr_type(write_txt_str(wb, o), o);
case 'csv': return write_string_type(write_csv_str(wb, o), o);
case 'dif': return write_string_type(write_dif_str(wb, o), o);
case 'prn': return write_string_type(write_prn_str(wb, o), o);
case 'fods': return write_string_type(write_ods(wb, o), o);
case 'biff2': return write_binary_type(write_biff_buf(wb, o), o);
case 'xlsx':
@ -74,7 +90,9 @@ function resolve_book_type(o/*?WriteFileOpts*/) {
case '.xml': o.bookType = 'xml'; break;
case '.ods': o.bookType = 'ods'; break;
case '.csv': o.bookType = 'csv'; break;
case '.txt': o.bookType = 'txt'; break;
case '.dif': o.bookType = 'dif'; break;
case '.prn': o.bookType = 'prn'; break;
case '.slk': o.bookType = 'sylk'; break;
}
}

@ -188,6 +188,13 @@ function sheet_to_csv(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
return out;
}
var make_csv = sheet_to_csv;
function sheet_to_txt(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
if(!opts) opts = {}; opts.FS = "\t"; opts.RS = "\n";
var s = sheet_to_csv(sheet, opts);
if(typeof cptable == 'undefined') return s;
var o = cptable.utils.encode(1200, s);
return "\xff\xfe" + o;
}
function sheet_to_formulae(sheet/*:Worksheet*/)/*:Array<string>*/ {
var y = "", x, val="";

@ -15,5 +15,6 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6.
![circo graph of format support](formats.png)
![graph legend](legend.png)

@ -1,4 +1,4 @@
## Workbook / Worksheet / Cell Object Description
## Common Spreadsheet Format
js-xlsx conforms to the Common Spreadsheet Format (CSF):

@ -72,3 +72,9 @@ file but Excel will know how to handle it. This library applies similar logic:
DBF files are detected based on the first byte as well as the third and fourth
bytes (corresponding to month and day of the file date)
Plaintext format guessing follows the priority order:
| Format | Test |
|:-------|:--------------------------------------------------------------------|
| PRN | (default) |

@ -29,16 +29,22 @@ output formats. The specific file type is controlled with `bookType` option:
| `xlsx` | `.xlsx` | ZIP | multi | Excel 2007+ XML Format |
| `xlsm` | `.xlsm` | ZIP | multi | Excel 2007+ Macro XML Format |
| `xlsb` | `.xlsb` | ZIP | multi | Excel 2007+ Binary Format |
| `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet |
| `biff2` | `.xls` | none | single | Excel 2.0 Worksheet format |
| `xlml` | `.xls` | none | multi | Excel 2003-2004 (SpreadsheetML) |
| `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet |
| `fods` | `.fods` | none | multi | Flat OpenDocument Spreadsheet |
| `csv` | `.csv` | none | single | Comma Separated Values |
| `txt` | `.txt` | none | single | UTF-16 Unicode Text (TXT) |
| `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) |
| `dif` | `.dif` | none | single | Data Interchange Format (DIF) |
| `prn` | `.prn` | none | single | Lotus Formatted Text |
- `compression` only applies to formats with ZIP containers.
- Formats that only support a single sheet require a `sheet` option specifying
the worksheet. If the string is empty, the first worksheet is used.
- `writeFile` will automatically guess the output file format based on the file
extension if `bookType` is not specified. It will choose the first format in
the aforementioned table that matches the extension.
### Output Type

@ -78,7 +78,7 @@ accordance with Excel. For the example sheet:
[ 'A1=\'S', 'F1=\'J', 'D2=4', 'B3=3', 'G3=8' ]
```
### CSV and general DSV Output
### Delimiter-Separated Output
As an alternative to the `writeFile` CSV type, `XLSX.utils.sheet_to_csv` also
produces CSV output. The function takes an options argument:
@ -109,6 +109,12 @@ S h e e t J S
S:h:e:e:t:J:S|1:2:3:4:5:6:7|2:3:4:5:6:7:8|
```
#### UTF-16 Unicode Text
The `txt` output type uses the tab character as the field separator. If the
codepage library is available (included in the full distribution but not core),
the output will be encoded in codepage `1200` and the BOM will be prepended.
### JSON
`XLSX.utils.sheet_to_json` and the alias `XLSX.utils.sheet_to_row_object_array`

@ -14,9 +14,11 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Excel 3.0 (XLS BIFF3) | :o: | |
| Excel 2.0/2.1 (XLS BIFF2) | :o: | :o: |
| **Excel Supported Text Formats** |:-----:|:-----:|
| Delimiter-Separated Values (CSV/TSV/DSV) | | :o: |
| Delimiter-Separated Values (CSV/TXT) | | :o: |
| Data Interchange Format (DIF) | :o: | :o: |
| Symbolic Link (SYLK/SLK) | :o: | :o: |
| Lotus Formatted Text (PRN) | :o: | :o: |
| UTF-16 Unicode Text (TXT) | | :o: |
| **Other Workbook/Worksheet Formats** |:-----:|:-----:|
| OpenDocument Spreadsheet (ODS) | :o: | :o: |
| Flat XML ODF Spreadsheet (FODS) | :o: | :o: |
@ -72,22 +74,26 @@ in an XLSX sub-file can be mapped to XLSB records in a corresponding sub-file.
The `MS-XLSB` specification covers the basics of the file format, and other
specifications expand on serialization of features like properties.
### OpenDocument Spreadsheet (ODS/FODS) and Uniform Office Spreadsheet (UOS1/2)
### OpenDocument Spreadsheet (ODS/FODS)
ODS is an XML-in-ZIP format akin to XLSX while FODS is an XML format akin to
SpreadsheetML. Both are detailed in the OASIS standard, but tools like LO/OO
add undocumented extensions.
#### Uniform Office Spreadsheet (UOS1/2)
UOS is a very similar format, and it comes in 2 varieties corresponding to ODS
and FODS respectively. For the most part, the difference between the formats
lies in the names of tags and attributes.
### Comma-Separated Values (CSV)
### Delimiter-Separated Values (CSV/TXT)
Excel CSV deviates from RFC4180 in a number of important ways. The generated
CSV files should generally work in Excel although they may not work in RFC4180
compatible readers.
Excel TXT uses tab as the delimiter and codepage 1200.
### Other Single-Worksheet Formats
Many older formats supported only one worksheet:
@ -106,6 +112,12 @@ limited by the general ability to read arbitrary files in the web browser.
There is no real documentation. All knowledge was gathered by saving files in
various versions of Excel to deduce the meaning of fields.
#### Lotus Formatted Text (PRN)
There is no real documentation, and in fact Excel treats PRN as an output-only
file format. Nevertheless we can guess the column widths and reverse-engineer
the original layout.
#### Data Interchange Format (DIF)
There is no unified definition. Visicalc DIF differs from Lotus DIF, and both

@ -16,7 +16,7 @@ digraph G {
subgraph OD {
node [style=filled,color=yellow];
ods [label="ODS"];
fods [label="Flat\nODS"];
fods [label="FODS"];
uos [label="UOS"];
}
@ -24,12 +24,14 @@ digraph G {
node [style=filled,color=cyan];
html [label="HTML\nTable"];
csv [label="CSV"];
txt [label="TXT\nUTF-16"];
dbf [label="DBF"];
dif [label="DIF"];
slk [label="SYLK"];
prn [label="PRN"];
}
subgraph JSXLSX {
subgraph WORKBOOK {
edge [color=blue];
csf -> xlsx
xlsx -> csf
@ -37,22 +39,28 @@ digraph G {
xlsb -> csf
csf -> xlml
xlml -> csf
xls2 -> csf
csf -> xls2
xls3 -> csf
xls4 -> csf
xls5 -> csf
xls8 -> csf
csf -> slk
slk -> csf
csf -> dif
dif -> csf
csf -> csv
ods -> csf
csf -> ods
fods -> csf
csf -> fods
uos -> csf
}
subgraph WORKSHEET {
edge [color=aquamarine4];
xls2 -> csf
csf -> xls2
xls3 -> csf
xls4 -> csf
csf -> slk
slk -> csf
csf -> dif
dif -> csf
prn -> csf
csf -> prn
csf -> csv
csf -> txt
dbf -> csf
html -> csf
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 119 KiB

BIN
legend.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

@ -12,7 +12,7 @@
* [Parsing functions](README.md#parsing-functions)
* [Writing functions](README.md#writing-functions)
* [Utilities](README.md#utilities)
- [Workbook / Worksheet / Cell Object Description](README.md#workbook--worksheet--cell-object-description)
- [Common Spreadsheet Format](README.md#common-spreadsheet-format)
* [General Structures](README.md#general-structures)
* [Cell Object](README.md#cell-object)
+ [Data Types](README.md#data-types)
@ -38,7 +38,8 @@
* [Array of Arrays Input](README.md#array-of-arrays-input)
* [HTML Table Input](README.md#html-table-input)
* [Formulae Output](README.md#formulae-output)
* [CSV and general DSV Output](README.md#csv-and-general-dsv-output)
* [Delimiter-Separated Output](README.md#delimiter-separated-output)
+ [UTF-16 Unicode Text](README.md#utf-16-unicode-text)
* [JSON](README.md#json)
- [File Formats](README.md#file-formats)
* [Excel 2007+ XML (XLSX/XLSM)](README.md#excel-2007-xml-xlsxxlsm)
@ -46,11 +47,13 @@
* [Excel 97-2004 Binary (BIFF8)](README.md#excel-97-2004-binary-biff8)
* [Excel 2003-2004 (SpreadsheetML)](README.md#excel-2003-2004-spreadsheetml)
* [Excel 2007+ Binary (XLSB, BIFF12)](README.md#excel-2007-binary-xlsb-biff12)
* [OpenDocument Spreadsheet (ODS/FODS) and Uniform Office Spreadsheet (UOS1/2)](README.md#opendocument-spreadsheet-odsfods-and-uniform-office-spreadsheet-uos12)
* [Comma-Separated Values (CSV)](README.md#comma-separated-values-csv)
* [OpenDocument Spreadsheet (ODS/FODS)](README.md#opendocument-spreadsheet-odsfods)
+ [Uniform Office Spreadsheet (UOS1/2)](README.md#uniform-office-spreadsheet-uos12)
* [Delimiter-Separated Values (CSV/TXT)](README.md#delimiter-separated-values-csvtxt)
* [Other Single-Worksheet Formats](README.md#other-single-worksheet-formats)
+ [dBASE and Visual FoxPro (DBF)](README.md#dbase-and-visual-foxpro-dbf)
+ [Symbolic Link (SYLK)](README.md#symbolic-link-sylk)
+ [Lotus Formatted Text (PRN)](README.md#lotus-formatted-text-prn)
+ [Data Interchange Format (DIF)](README.md#data-interchange-format-dif)
+ [HTML](README.md#html)
- [Testing](README.md#testing)

1
misc/docs/legend.png Symbolic link

@ -0,0 +1 @@
../../legend.png

42
misc/legend.dot Normal file

@ -0,0 +1,42 @@
digraph G {
graph [mindist=0];
labelloc=t;
label="Legend"
subgraph cluster_0 {
label="Supported Format Types"
color="white"
XL[label="Excel",style=filled,color=green];
OD[label="ODF",style=filled,color=yellow];
OLD[label="Other",style=filled,color=cyan];
{ edge[style=invis] XL -> OD -> OLD[constraint=false]}
}
subgraph cluster_1 {
label="Workbook Format Conversions (blue arrow)"
color="white"
x1i[label="XLSX"]
c1[shape=doublecircle,label="CSF"];
x1o[label="XLSB"]
{ edge[color=blue]
x1i->c1[constraint=false,label="read"]
c1->x1o[constraint=false,label="write"];
}
}
subgraph cluster_2 {
label="Single-Worksheet Format Conversions (green arrow)"
color="white"
x2i[label="SYLK"]
c2[shape=doublecircle,label="CSF"];
x2o[label="CSV"]
{ edge[color=aquamarine4]
x2i->c2[constraint=false,label="read"]
c2->x2o[constraint=false,label="write"];
}
}
{ edge[style=invis] XL -> x1i -> x2i; }
{ edge[style=invis] OD -> c1 -> c2; }
{ edge[style=invis] OLD -> x1o -> x2o; }
}

10
test.js

@ -510,16 +510,6 @@ describe('input formats', function() {
X.read(fs.readFileSync(paths.cstxlsx));
X.read(fs.readFileSync(paths.cstxlsb));
});
it('should default to base64 type', function() {
assert.throws(function() { X.read(fs.readFileSync(paths.cstxls, 'binary')); });
assert.throws(function() { X.read(fs.readFileSync(paths.cstxml, 'binary')); });
assert.throws(function() { X.read(fs.readFileSync(paths.cstxlsx, 'binary')); });
assert.throws(function() { X.read(fs.readFileSync(paths.cstxlsb, 'binary')); });
X.read(fs.readFileSync(paths.cstxls, 'base64'));
X.read(fs.readFileSync(paths.cstxml, 'base64'));
X.read(fs.readFileSync(paths.cstxlsx, 'base64'));
X.read(fs.readFileSync(paths.cstxlsb, 'base64'));
});
});
describe('output formats', function() {

@ -838,7 +838,7 @@ apachepoi_60273.xls
# apachepoi_60284.xls
apachepoi_AbnormalSharedFormulaFlag.xls
apachepoi_AreaErrPtg.xls
apachepoi_BOOK_in_capitals.xls
# apachepoi_BOOK_in_capitals.xls # note: worksheet length exceeds 31 chars
apachepoi_Basic_Expense_Template_2011.xls
apachepoi_CodeFunctionTestCaseData.xls
apachepoi_ColumnStyle1dp.xls

@ -34,6 +34,7 @@ var paths = {
cstxml: dir + 'comments_stress_test.xls.xml',
cstxlsx: dir + 'comments_stress_test.xlsx',
cstxlsb: dir + 'comments_stress_test.xlsb',
cstods: dir + 'comments_stress_test.ods',
fstxls: dir + 'formula_stress_test.xls',
fstxml: dir + 'formula_stress_test.xls.xml',
fstxlsx: dir + 'formula_stress_test.xlsx',
@ -79,6 +80,21 @@ var N2 = 'XLSB';
var N3 = 'XLS';
var N4 = 'XML';
/* comments_stress_test family */
function check_comments(wb) {
var ws0 = wb.Sheets.Sheet2;
assert.equal(ws0.A1.c[0].a, 'Author');
assert.equal(ws0.A1.c[0].t, 'Author:\nGod thinks this is good');
assert.equal(ws0.C1.c[0].a, 'Author');
assert.equal(ws0.C1.c[0].t, 'I really hope that xlsx decides not to use magic like rPr');
var ws3 = wb.Sheets.Sheet4;
assert.equal(ws3.B1.c[0].a, 'Author');
assert.equal(ws3.B1.c[0].t, 'The next comment is empty');
assert.equal(ws3.B2.c[0].a, 'Author');
assert.equal(ws3.B2.c[0].t, '');
}
describe('parse options', function() {
var html_cell_types = ['s'];
var bef = (function() {
@ -347,10 +363,6 @@ describe('input formats', function() {
assert.throws(function() { X.read(fs.readFileSync(paths.cstxlsb), {type: 'dafuq'}); });
});
it('should default to base64 type', function() {
assert.throws(function() { X.read(fs.readFileSync(paths.cstxls, 'binary')); });
assert.throws(function() { X.read(fs.readFileSync(paths.cstxml, 'binary')); });
assert.throws(function() { X.read(fs.readFileSync(paths.cstxlsx, 'binary')); });
assert.throws(function() { X.read(fs.readFileSync(paths.cstxlsb, 'binary')); });
X.read(fs.readFileSync(paths.cstxls, 'base64'));
X.read(fs.readFileSync(paths.cstxml, 'base64'));
X.read(fs.readFileSync(paths.cstxlsx, 'base64'));
@ -512,6 +524,21 @@ describe('parse features', function() {
assert.equal(ws.B1.c[0].h, '<span style="font-weight: bold;">Yegor Kozlov:</span><span style=""><br/>first cell</span>', "must have the html representation");
});
});
[
['xlsx', paths.cstxlsx],
['xlsb', paths.cstxlsb],
['xls', paths.cstxls],
['xlml', paths.cstxml],
['ods', paths.cstods]
].forEach(function(m) { it(m[0] + ' stress test', function() {
var wb = X.read(fs.readFileSync(m[1]), {type:'binary'});
check_comments(wb);
var ws0 = wb.Sheets.Sheet2;
assert.equal(ws0.A1.c[0].a, 'Author');
assert.equal(ws0.A1.c[0].t, 'Author:\nGod thinks this is good');
assert.equal(ws0.C1.c[0].a, 'Author');
assert.equal(ws0.C1.c[0].t, 'I really hope that xlsx decides not to use magic like rPr');
}); });
});
describe('should parse core properties and custom properties', function() {
@ -793,7 +820,7 @@ describe('roundtrip features', function() {
var bef = (function() { X = require(modp); });
if(typeof before != 'undefined') before(bef);
else it('before', bef);
describe('should parse core properties and custom properties', function() {
describe('should preserve core properties and custom properties', function() {
var wb1, wb2, base = './tmp/cp';
var bef = (function() {
wb1 = X.read(fs.readFileSync(paths.cpxlsx), {type:"binary"});
@ -879,6 +906,7 @@ describe('roundtrip features', function() {
});
});
});
describe('should preserve sheet visibility', function() { [
['xlml', paths.svxml],
['xlsx', paths.svxlsx],
@ -897,6 +925,22 @@ describe('roundtrip features', function() {
});
});
});
describe('should preserve cell comments', function() { [
['xlsx', paths.cstxlsx],
['xlsb', paths.cstxlsb],
//['xls', paths.cstxlsx],
['xlml', paths.cstxml]
//['ods', paths.cstods]
].forEach(function(w) {
it(w[0], function() {
var wb1 = X.read(fs.readFileSync(w[1]), {type:"binary"});
var wb2 = X.read(X.write(wb1, {bookType:w[0], type:"binary"}), {type:"binary"});
check_comments(wb1);
check_comments(wb2);
});
});
});
});
function password_file(x){return x.match(/^password.*\.xls$/); }
@ -907,7 +951,7 @@ describe('invalid files', function() {
['passwords', 'apachepoi_xor-encryption-abc.xls'],
['DOC files', 'word_doc.doc']
].forEach(function(w) { it('should fail on ' + w[0], function() {
assert.throws(function() { X.read(fs.readFileSync(dir + w[1]), {type:"binary"}); });
assert.throws(function() { X.read(fs.readFileSync(dir + w[1], {type:"binary"}), {type:"binary"}); });
assert.throws(function() { X.read(fs.readFileSync(dir+w[1], 'base64'), {type:'base64'}); });
}); });
});

File diff suppressed because one or more lines are too long

@ -10,6 +10,7 @@
./test_files/comments_stress_test.xls.xml
./test_files/comments_stress_test.xlsx
./test_files/comments_stress_test.xlsb
./test_files/comments_stress_test.ods
./test_files/formula_stress_test.xls
./test_files/formula_stress_test.xls.xml
./test_files/formula_stress_test.xlsx

@ -71,11 +71,12 @@ ws['!rows'] = wsrows;
ws['A3'].l = { Target: "http://sheetjs.com", Tooltip: "Visit us <SheetJS.com!>" };
/* TEST: built-in format */
//ws['A1'].z = "0%"; wb.SSF[9] = "0%"; // Format Code 9
ws['B1'].z = "0%"; // Format Code 9
/* TEST: custom format */
//ws['B2'].z = "0.0"; wb.SSF[60] = "0.0"; // Custom
console.log("JSON Data: "); console.log(XLSX.utils.sheet_to_json(ws, {header:1}));
console.log("JSON Data:");console.log(XLSX.utils.sheet_to_json(ws, {header:1}));
/* TEST: hidden sheets */
wb.SheetNames.push("Hidden");
@ -99,7 +100,7 @@ wb.Props = {
/* TEST: comments */
ws['A4'].c = [];
ws['A4'].c.push({a:"SheetJS",t:"I'm a little comment, short and stout!"});
ws['A4'].c.push({a:"SheetJS",t:"I'm a little comment, short and stout!\n\nWell, Stout may be the wrong word"});
console.log("Worksheet Model:")
console.log(ws);
@ -114,6 +115,8 @@ XLSX.writeFile(wb, 'sheetjs.ods');
XLSX.writeFile(wb, 'sheetjs.fods');
XLSX.writeFile(wb, 'sheetjs.slk');
XLSX.writeFile(wb, 'sheetjs.csv');
XLSX.writeFile(wb, 'sheetjs.txt');
XLSX.writeFile(wb, 'sheetjs.prn');
/* test by reading back files */
XLSX.readFile('sheetjs.xlsx');
@ -125,3 +128,5 @@ XLSX.readFile('sheetjs.ods');
XLSX.readFile('sheetjs.fods');
XLSX.readFile('sheetjs.slk');
//XLSX.readFile('sheetjs.csv');
//XLSX.readFile('sheetjs.txt');
XLSX.readFile('sheetjs.prn');

@ -109,6 +109,8 @@ type EvertNumType = {[string]:number};
type EvertArrType = {[string]:Array<string>};
type StringConv = {(string):string};
type WriteObjStrFactory = {from_sheet(ws:Worksheet, o:any):string};
*/
/* ssf.js (C) 2013-present SheetJS -- http://sheetjs.com */
/*jshint -W041 */
@ -2106,9 +2108,8 @@ function aoa_to_sheet(data/*:AOA*/, opts/*:?any*/)/*:Worksheet*/ {
else if(typeof cell.v === 'boolean') cell.t = 'b';
else if(cell.v instanceof Date) {
cell.z = o.dateNF || SSF._table[14];
if(o.cellDates) cell.t = 'd';
else { cell.t = 'n'; cell.v = datenum(cell.v); }
cell.w = SSF.format(cell.z, cell.v);
if(o.cellDates) { cell.t = 'd'; cell.w = SSF.format(cell.z, datenum(cell.v)); }
else { cell.t = 'n'; cell.v = datenum(cell.v); cell.w = SSF.format(cell.z, cell.v); }
}
else cell.t = 's';
ws[cell_ref] = cell;
@ -4816,7 +4817,7 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
var memo = false;
var vfp = false;
switch(ft) {
case 0x03: break;
case 0x02: case 0x03: break;
case 0x30: vfp = true; memo = true; break;
case 0x31: vfp = true; break;
case 0x83: memo = true; break;
@ -4824,33 +4825,38 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
case 0xF5: memo = true; break;
default: throw new Error("DBF Unsupported Version: " + ft.toString(16));
}
var filedate = new Date(d.read_shift(1) + 1900, d.read_shift(1) - 1, d.read_shift(1));
var nrow = d.read_shift(4);
var fpos = d.read_shift(2);
var filedate = new Date(), nrow = 0, fpos = 0;
if(ft == 0x02) nrow = d.read_shift(2);
filedate = new Date(d.read_shift(1) + 1900, d.read_shift(1) - 1, d.read_shift(1));
if(ft != 0x02) nrow = d.read_shift(4);
if(ft != 0x02) fpos = d.read_shift(2);
var rlen = d.read_shift(2);
d.l+=16;
var flags = d.read_shift(1);
var flags = 0, current_cp = 1252;
if(ft != 0x02) {
d.l+=16;
flags = d.read_shift(1);
//if(memo && ((flags & 0x02) === 0)) throw new Error("DBF Flags " + flags.toString(16) + " ft " + ft.toString(16));
/* codepage present in FoxPro */
var current_cp = 1252;
if(d[d.l] !== 0) current_cp = dbf_codepage_map[d[d.l]];
d.l+=1;
d.l+=2;
}
var fields = [], field = {};
var hend = fpos - 10 - (vfp ? 264 : 0);
while(d.l < hend) {
while(ft == 0x02 ? d.l < d.length && d[d.l] != 0x0d: d.l < hend) {
field = {};
field.name = cptable.utils.decode(current_cp, d.slice(d.l, d.l+10)).replace(/[\u0000\r\n].*$/g,"");
d.l += 11;
field.type = String.fromCharCode(d.read_shift(1));
field.offset = d.read_shift(4);
if(ft != 0x02) field.offset = d.read_shift(4);
field.len = d.read_shift(1);
if(ft == 0x02) field.offset = d.read_shift(2);
field.dec = d.read_shift(1);
if(field.name.length) fields.push(field);
d.l += 14;
if(ft != 0x02) d.l += 14;
switch(field.type) {
// case 'B': break; // Binary
case 'C': break; // character
@ -4872,8 +4878,11 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
}
}
if(d[d.l] !== 0x0D) d.l = fpos-1;
if(d.read_shift(1) !== 0x0D) throw new Error("DBF Terminator not found " + d.l + " " + d[d.l]);
d.l = fpos;
else if(ft == 0x02) d.l = 0x209;
if(ft != 0x02) {
if(d.read_shift(1) !== 0x0D) throw new Error("DBF Terminator not found " + d.l + " " + d[d.l]);
d.l = fpos;
}
/* data */
var R = 0, C = 0;
out[0] = [];
@ -4921,7 +4930,7 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
}
}
}
if(d.l < d.length && d[d.l++] != 0x1A) throw new Error("DBF EOF Marker missing " + (d.l-1) + " of " + d.length + " " + d[d.l-1].toString(16));
if(ft != 0x02) if(d.l < d.length && d[d.l++] != 0x1A) throw new Error("DBF EOF Marker missing " + (d.l-1) + " of " + d.length + " " + d[d.l-1].toString(16));
return out;
}
@ -5133,8 +5142,9 @@ var PRN = (function() {
function set_text_arr(data/*:string*/, arr/*:AOA*/, R/*:number*/, C/*:number*/) {
if(data === 'TRUE') arr[R][C] = true;
else if(data === 'FALSE') arr[R][C] = false;
else if(data === ""){}
else if(+data == +data) arr[R][C] = +data;
else if(data !== "") arr[R][C] = data;
else arr[R][C] = data;
}
function prn_to_aoa_str(f/*:string*/, opts)/*:AOA*/ {
@ -5165,9 +5175,27 @@ var PRN = (function() {
function prn_to_workbook(str/*:string*/, opts)/*:Workbook*/ { return sheet_to_workbook(prn_to_sheet(str, opts), opts); }
function sheet_to_prn(ws/*:Worksheet*/, opts/*:?any*/)/*:string*/ {
var o/*:Array<string>*/ = [];
var r = decode_range(ws['!ref']), cell/*:Cell*/;
for(var R = r.s.r; R <= r.e.r; ++R) {
var oo = [];
for(var C = r.s.c; C <= r.e.c; ++C) {
var coord = encode_cell({r:R,c:C});
if(!(cell = ws[coord]) || cell.v == null) { oo.push(" "); continue; }
var w = (cell.w || (format_cell(cell), cell.w) || "").substr(0,10);
while(w.length < 10) w += " ";
oo.push(w + (C == 0 ? " " : ""));
}
o.push(oo.join(""));
}
return o.join("\n");
}
return {
to_workbook: prn_to_workbook,
to_sheet: prn_to_sheet
to_sheet: prn_to_sheet,
from_sheet: sheet_to_prn
};
})();
@ -6741,8 +6769,9 @@ function parse_comments_xml(data/*:string*/, opts)/*:Array<Comment>*/ {
var cell = decode_cell(y.ref);
if(opts.sheetRows && opts.sheetRows <= cell.r) return;
var textMatch = x.match(/<(?:\w+:)?text>([^\u2603]*)<\/(?:\w+:)?text>/);
var rt = (!textMatch || !textMatch[1]) ? {r:"",t:"",h:""} : parse_si(textMatch[1]);
var rt = !!textMatch && !!textMatch[1] && parse_si(textMatch[1]) || {r:"",t:"",h:""};
comment.r = rt.r;
if(rt.r == "<t></t>") rt.t = rt.h = "";
comment.t = rt.t.replace(/\r\n/g,"\n").replace(/\r/g,"\n");
if(opts.cellHTML) comment.h = rt.h;
commentList.push(comment);
@ -6769,7 +6798,7 @@ function write_comments_xml(data, opts) {
d[1].forEach(function(c) {
/* 18.7.3 CT_Comment */
o.push('<comment ref="' + d[0] + '" authorId="' + iauthor.indexOf(escapexml(c.a)) + '"><text>');
o.push(writetag("t", c.t));
o.push(writetag("t", c.t == null ? "" : c.t));
o.push('</text></comment>');
});
});
@ -6790,7 +6819,7 @@ function parse_BrtBeginComment(data, length) {
function write_BrtBeginComment(data, o) {
if(o == null) o = new_buf(36);
o.write_shift(4, data[1].iauthor);
write_UncheckedRfX(data[0], o);
write_UncheckedRfX((data[0]/*:any*/), o);
o.write_shift(4, 0);
o.write_shift(4, 0);
o.write_shift(4, 0);
@ -6853,7 +6882,7 @@ function write_comments_bin(data, opts) {
data.forEach(function(comment) {
comment[1].forEach(function(c) {
c.iauthor = iauthor.indexOf(c.a);
var range = {s:decode_cell(comment[0])}; range.e = range.s;
var range = {s:decode_cell(comment[0]),e:decode_cell(comment[0])};
write_record(ba, "BrtBeginComment", write_BrtBeginComment([range, c]));
if(c.t && c.t.length > 0) write_record(ba, "BrtCommentText", write_RichStr(c));
write_record(ba, "BrtEndComment");
@ -10522,11 +10551,18 @@ function parse_wb_defaults(wb) {
_ssfopts.date1904 = parsexmlbool(wb.WBProps.date1904, 'date1904');
}
/* TODO: validate workbook */
function check_wb_names(N) {
var badchars = "][*?\/\\".split("");
N.forEach(function(n,i) {
badchars.forEach(function(c) { if(n.indexOf(c) > -1) throw new Error("Sheet name cannot contain : \\ / ? * [ ]"); });
if(n.length > 31) throw new Error("Sheet names cannot exceed 31 chars");
for(var j = 0; j < i; ++j) if(n == N[j]) throw new Error("Duplicate Sheet Name: " + n);
});
}
function check_wb(wb) {
if(!wb || !wb.SheetNames || !wb.Sheets) throw new Error("Invalid Workbook");
for(var i = 0; i < wb.SheetNames.length; ++i) for(var j = 0; j < i; ++j)
if(wb.SheetNames[i] == wb.SheetNames[j]) throw new Error("Duplicate Sheet Name: " + wb.SheetNames[i]);
check_wb_names(wb.SheetNames);
/* TODO: validate workbook */
}
/* 18.2 Workbook */
var wbnsregex = /<\w+:workbook/;
@ -14152,7 +14188,7 @@ var parse_content_xml = (function() {
var sheetag/*:: = {name:"", '名称':""}*/;
var rowtag/*:: = {'行号':""}*/;
var Sheets = {}, SheetNames/*:Array<string>*/ = [], ws = {};
var Rn, q/*:: = ({t:"", v:null, z:null, w:""}:any)*/;
var Rn, q/*:: = ({t:"", v:null, z:null, w:"",c:[]}:any)*/;
var ctag = {value:""};
var textp = "", textpidx = 0, textptag/*:: = {}*/;
var R = -1, C = -1, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
@ -14207,7 +14243,7 @@ var parse_content_xml = (function() {
if(R < range.s.r) range.s.r = R;
ctag = parsexmltag(Rn[0], false);
comments = []; comment = {};
q = ({t:ctag['数据类型'] || ctag['value-type'], v:null/*:: , z:null, w:""*/}/*:any*/);
q = ({t:ctag['数据类型'] || ctag['value-type'], v:null/*:: , z:null, w:"",c:[]*/}/*:any*/);
if(opts.cellFormula) {
if(ctag.formula) ctag.formula = unescapexml(ctag.formula);
if(ctag['number-matrix-columns-spanned'] && ctag['number-matrix-rows-spanned']) {
@ -14265,7 +14301,7 @@ var parse_content_xml = (function() {
if(range.e.c <= C) range.e.c = C;
}
} else { C += rept; rept = 0; }
q = {/*:: t:"", v:null, z:null, w:""*/};
q = {/*:: t:"", v:null, z:null, w:"",c:[]*/};
textp = "";
}
break; // 9.1.4 <table:table-cell>
@ -14289,7 +14325,7 @@ var parse_content_xml = (function() {
comments.push(comment);
}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') {state.push([Rn[3], false]);}
creator = ""; creatorpidx = 0;
creator = ""; creatoridx = 0;
textp = ""; textpidx = 0;
break;
@ -14614,16 +14650,9 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
return o.join("");
};
})();
/* actual implementation in utils, wrappers are for read/write */
function write_csv_str(wb/*:Workbook*/, o/*:WriteOpts*/) {
var idx = 0;
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
return sheet_to_csv(wb.Sheets[wb.SheetNames[idx]], o);
}
function write_obj_str(factory) {
return function write_str(wb/*:Workbook*/, o/*:WriteOpts*/) {
/* actual implementation elsewhere, wrappers are for read/write */
function write_obj_str(factory/*:WriteObjStrFactory*/) {
return function write_str(wb/*:Workbook*/, o/*:WriteOpts*/)/*:string*/ {
var idx = 0;
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
@ -14631,8 +14660,11 @@ function write_obj_str(factory) {
};
}
var write_csv_str = write_obj_str({from_sheet:sheet_to_csv});
var write_slk_str = write_obj_str(SYLK);
var write_dif_str = write_obj_str(DIF);
var write_prn_str = write_obj_str(PRN);
var write_txt_str = write_obj_str({from_sheet:sheet_to_txt});
/* Part 3: Packages */
function parse_ods(zip/*:ZIPFile*/, opts/*:?ParseOpts*/) {
opts = opts || ({}/*:any*/);
@ -14925,9 +14957,10 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
/*::if(!wb.Props) throw "unreachable"; */
f = "docProps/app.xml";
if(!wb.Workbook || !wb.Workbook.Sheets) wb.Props.SheetNames = wb.SheetNames;
if(wb.Props && wb.Props.SheetNames){}
else if(!wb.Workbook || !wb.Workbook.Sheets) wb.Props.SheetNames = wb.SheetNames;
// $FlowIgnore
else wb.Props.SheetNames = wb.Workbook.Sheets.filter(function(x) { return x.Hidden != 2; }).map(function(x) { return x.name; });
else wb.Props.SheetNames = wb.SheetNames.map(function(x,i) { return [(wb.Workbook.Sheets[i]||{}).Hidden != 2, x];}).filter(function(x) { return x[0]; }).map(function(x) { return x[1]; });
wb.Props.Worksheets = wb.Props.SheetNames.length;
zip.file(f, write_ext_props(wb.Props, opts));
ct.extprops.push(f);
@ -15045,10 +15078,11 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
case 0x50: if(n[1] == 0x4B && n[2] < 0x20 && n[3] < 0x20) return read_zip(d, o); break;
case 0xEF: return parse_xlml(d, o);
case 0x03: case 0x83: case 0x8B: return DBF.to_workbook(d, o);
case 0x30: case 0x31: if(n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o); break;
default: throw new Error("Unsupported file " + n.join("|"));
}
throw new Error("Unsupported file format " + n.join("|"));
if(n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o);
if(0x20>n[0]||n[0]>0x7F) throw new Error("Unsupported file " + n.join("|"));
/* TODO: CSV / TXT */
return PRN.to_workbook(d, o);
}
function readFileSync(filename/*:string*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
@ -15071,6 +15105,20 @@ function write_zip_type(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
return z.generate(oopts);
}
/* TODO: test consistency */
function write_bstr_type(out/*:string*/, opts/*:WriteOpts*/) {
switch(opts.type) {
case "base64": return Base64.encode(out);
case "binary": return out;
case "file": return _fs.writeFileSync(opts.file, out, 'binary');
case "buffer": {
if(has_buf) return new Buffer(out, 'utf8');
else return out.split("").map(function(c) { return c.charCodeAt(0); });
}
}
throw new Error("Unrecognized type " + opts.type);
}
/* TODO: test consistency */
function write_string_type(out/*:string*/, opts/*:WriteOpts*/) {
switch(opts.type) {
@ -15107,8 +15155,10 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
case 'xlml': return write_string_type(write_xlml(wb, o), o);
case 'slk':
case 'sylk': return write_string_type(write_slk_str(wb, o), o);
case 'txt': return write_bstr_type(write_txt_str(wb, o), o);
case 'csv': return write_string_type(write_csv_str(wb, o), o);
case 'dif': return write_string_type(write_dif_str(wb, o), o);
case 'prn': return write_string_type(write_prn_str(wb, o), o);
case 'fods': return write_string_type(write_ods(wb, o), o);
case 'biff2': return write_binary_type(write_biff_buf(wb, o), o);
case 'xlsx':
@ -15131,7 +15181,9 @@ function resolve_book_type(o/*?WriteFileOpts*/) {
case '.xml': o.bookType = 'xml'; break;
case '.ods': o.bookType = 'ods'; break;
case '.csv': o.bookType = 'csv'; break;
case '.txt': o.bookType = 'txt'; break;
case '.dif': o.bookType = 'dif'; break;
case '.prn': o.bookType = 'prn'; break;
case '.slk': o.bookType = 'sylk'; break;
}
}
@ -15341,6 +15393,13 @@ function sheet_to_csv(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
return out;
}
var make_csv = sheet_to_csv;
function sheet_to_txt(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
if(!opts) opts = {}; opts.FS = "\t"; opts.RS = "\n";
var s = sheet_to_csv(sheet, opts);
if(typeof cptable == 'undefined') return s;
var o = cptable.utils.encode(1200, s);
return "\xff\xfe" + o;
}
function sheet_to_formulae(sheet/*:Worksheet*/)/*:Array<string>*/ {
var y = "", x, val="";

135
xlsx.js

@ -2054,9 +2054,8 @@ function aoa_to_sheet(data, opts) {
else if(typeof cell.v === 'boolean') cell.t = 'b';
else if(cell.v instanceof Date) {
cell.z = o.dateNF || SSF._table[14];
if(o.cellDates) cell.t = 'd';
else { cell.t = 'n'; cell.v = datenum(cell.v); }
cell.w = SSF.format(cell.z, cell.v);
if(o.cellDates) { cell.t = 'd'; cell.w = SSF.format(cell.z, datenum(cell.v)); }
else { cell.t = 'n'; cell.v = datenum(cell.v); cell.w = SSF.format(cell.z, cell.v); }
}
else cell.t = 's';
ws[cell_ref] = cell;
@ -4762,7 +4761,7 @@ function dbf_to_aoa(buf, opts) {
var memo = false;
var vfp = false;
switch(ft) {
case 0x03: break;
case 0x02: case 0x03: break;
case 0x30: vfp = true; memo = true; break;
case 0x31: vfp = true; break;
case 0x83: memo = true; break;
@ -4770,33 +4769,38 @@ function dbf_to_aoa(buf, opts) {
case 0xF5: memo = true; break;
default: throw new Error("DBF Unsupported Version: " + ft.toString(16));
}
var filedate = new Date(d.read_shift(1) + 1900, d.read_shift(1) - 1, d.read_shift(1));
var nrow = d.read_shift(4);
var fpos = d.read_shift(2);
var filedate = new Date(), nrow = 0, fpos = 0;
if(ft == 0x02) nrow = d.read_shift(2);
filedate = new Date(d.read_shift(1) + 1900, d.read_shift(1) - 1, d.read_shift(1));
if(ft != 0x02) nrow = d.read_shift(4);
if(ft != 0x02) fpos = d.read_shift(2);
var rlen = d.read_shift(2);
d.l+=16;
var flags = d.read_shift(1);
var flags = 0, current_cp = 1252;
if(ft != 0x02) {
d.l+=16;
flags = d.read_shift(1);
//if(memo && ((flags & 0x02) === 0)) throw new Error("DBF Flags " + flags.toString(16) + " ft " + ft.toString(16));
/* codepage present in FoxPro */
var current_cp = 1252;
if(d[d.l] !== 0) current_cp = dbf_codepage_map[d[d.l]];
d.l+=1;
d.l+=2;
}
var fields = [], field = {};
var hend = fpos - 10 - (vfp ? 264 : 0);
while(d.l < hend) {
while(ft == 0x02 ? d.l < d.length && d[d.l] != 0x0d: d.l < hend) {
field = {};
field.name = cptable.utils.decode(current_cp, d.slice(d.l, d.l+10)).replace(/[\u0000\r\n].*$/g,"");
d.l += 11;
field.type = String.fromCharCode(d.read_shift(1));
field.offset = d.read_shift(4);
if(ft != 0x02) field.offset = d.read_shift(4);
field.len = d.read_shift(1);
if(ft == 0x02) field.offset = d.read_shift(2);
field.dec = d.read_shift(1);
if(field.name.length) fields.push(field);
d.l += 14;
if(ft != 0x02) d.l += 14;
switch(field.type) {
// case 'B': break; // Binary
case 'C': break; // character
@ -4818,8 +4822,11 @@ function dbf_to_aoa(buf, opts) {
}
}
if(d[d.l] !== 0x0D) d.l = fpos-1;
if(d.read_shift(1) !== 0x0D) throw new Error("DBF Terminator not found " + d.l + " " + d[d.l]);
d.l = fpos;
else if(ft == 0x02) d.l = 0x209;
if(ft != 0x02) {
if(d.read_shift(1) !== 0x0D) throw new Error("DBF Terminator not found " + d.l + " " + d[d.l]);
d.l = fpos;
}
/* data */
var R = 0, C = 0;
out[0] = [];
@ -4867,7 +4874,7 @@ function dbf_to_aoa(buf, opts) {
}
}
}
if(d.l < d.length && d[d.l++] != 0x1A) throw new Error("DBF EOF Marker missing " + (d.l-1) + " of " + d.length + " " + d[d.l-1].toString(16));
if(ft != 0x02) if(d.l < d.length && d[d.l++] != 0x1A) throw new Error("DBF EOF Marker missing " + (d.l-1) + " of " + d.length + " " + d[d.l-1].toString(16));
return out;
}
@ -5079,8 +5086,9 @@ var PRN = (function() {
function set_text_arr(data, arr, R, C) {
if(data === 'TRUE') arr[R][C] = true;
else if(data === 'FALSE') arr[R][C] = false;
else if(data === ""){}
else if(+data == +data) arr[R][C] = +data;
else if(data !== "") arr[R][C] = data;
else arr[R][C] = data;
}
function prn_to_aoa_str(f, opts) {
@ -5111,9 +5119,27 @@ var PRN = (function() {
function prn_to_workbook(str, opts) { return sheet_to_workbook(prn_to_sheet(str, opts), opts); }
function sheet_to_prn(ws, opts) {
var o = [];
var r = decode_range(ws['!ref']), cell;
for(var R = r.s.r; R <= r.e.r; ++R) {
var oo = [];
for(var C = r.s.c; C <= r.e.c; ++C) {
var coord = encode_cell({r:R,c:C});
if(!(cell = ws[coord]) || cell.v == null) { oo.push(" "); continue; }
var w = (cell.w || (format_cell(cell), cell.w) || "").substr(0,10);
while(w.length < 10) w += " ";
oo.push(w + (C == 0 ? " " : ""));
}
o.push(oo.join(""));
}
return o.join("\n");
}
return {
to_workbook: prn_to_workbook,
to_sheet: prn_to_sheet
to_sheet: prn_to_sheet,
from_sheet: sheet_to_prn
};
})();
@ -6687,8 +6713,9 @@ function parse_comments_xml(data, opts) {
var cell = decode_cell(y.ref);
if(opts.sheetRows && opts.sheetRows <= cell.r) return;
var textMatch = x.match(/<(?:\w+:)?text>([^\u2603]*)<\/(?:\w+:)?text>/);
var rt = (!textMatch || !textMatch[1]) ? {r:"",t:"",h:""} : parse_si(textMatch[1]);
var rt = !!textMatch && !!textMatch[1] && parse_si(textMatch[1]) || {r:"",t:"",h:""};
comment.r = rt.r;
if(rt.r == "<t></t>") rt.t = rt.h = "";
comment.t = rt.t.replace(/\r\n/g,"\n").replace(/\r/g,"\n");
if(opts.cellHTML) comment.h = rt.h;
commentList.push(comment);
@ -6715,7 +6742,7 @@ function write_comments_xml(data, opts) {
d[1].forEach(function(c) {
/* 18.7.3 CT_Comment */
o.push('<comment ref="' + d[0] + '" authorId="' + iauthor.indexOf(escapexml(c.a)) + '"><text>');
o.push(writetag("t", c.t));
o.push(writetag("t", c.t == null ? "" : c.t));
o.push('</text></comment>');
});
});
@ -6736,7 +6763,7 @@ function parse_BrtBeginComment(data, length) {
function write_BrtBeginComment(data, o) {
if(o == null) o = new_buf(36);
o.write_shift(4, data[1].iauthor);
write_UncheckedRfX(data[0], o);
write_UncheckedRfX((data[0]), o);
o.write_shift(4, 0);
o.write_shift(4, 0);
o.write_shift(4, 0);
@ -6799,7 +6826,7 @@ function write_comments_bin(data, opts) {
data.forEach(function(comment) {
comment[1].forEach(function(c) {
c.iauthor = iauthor.indexOf(c.a);
var range = {s:decode_cell(comment[0])}; range.e = range.s;
var range = {s:decode_cell(comment[0]),e:decode_cell(comment[0])};
write_record(ba, "BrtBeginComment", write_BrtBeginComment([range, c]));
if(c.t && c.t.length > 0) write_record(ba, "BrtCommentText", write_RichStr(c));
write_record(ba, "BrtEndComment");
@ -10467,11 +10494,18 @@ function parse_wb_defaults(wb) {
_ssfopts.date1904 = parsexmlbool(wb.WBProps.date1904, 'date1904');
}
/* TODO: validate workbook */
function check_wb_names(N) {
var badchars = "][*?\/\\".split("");
N.forEach(function(n,i) {
badchars.forEach(function(c) { if(n.indexOf(c) > -1) throw new Error("Sheet name cannot contain : \\ / ? * [ ]"); });
if(n.length > 31) throw new Error("Sheet names cannot exceed 31 chars");
for(var j = 0; j < i; ++j) if(n == N[j]) throw new Error("Duplicate Sheet Name: " + n);
});
}
function check_wb(wb) {
if(!wb || !wb.SheetNames || !wb.Sheets) throw new Error("Invalid Workbook");
for(var i = 0; i < wb.SheetNames.length; ++i) for(var j = 0; j < i; ++j)
if(wb.SheetNames[i] == wb.SheetNames[j]) throw new Error("Duplicate Sheet Name: " + wb.SheetNames[i]);
check_wb_names(wb.SheetNames);
/* TODO: validate workbook */
}
/* 18.2 Workbook */
var wbnsregex = /<\w+:workbook/;
@ -14230,7 +14264,7 @@ var parse_content_xml = (function() {
comments.push(comment);
}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') {state.push([Rn[3], false]);}
creator = ""; creatorpidx = 0;
creator = ""; creatoridx = 0;
textp = ""; textpidx = 0;
break;
@ -14555,14 +14589,7 @@ var write_content_xml = (function() {
return o.join("");
};
})();
/* actual implementation in utils, wrappers are for read/write */
function write_csv_str(wb, o) {
var idx = 0;
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
return sheet_to_csv(wb.Sheets[wb.SheetNames[idx]], o);
}
/* actual implementation elsewhere, wrappers are for read/write */
function write_obj_str(factory) {
return function write_str(wb, o) {
var idx = 0;
@ -14572,8 +14599,11 @@ function write_obj_str(factory) {
};
}
var write_csv_str = write_obj_str({from_sheet:sheet_to_csv});
var write_slk_str = write_obj_str(SYLK);
var write_dif_str = write_obj_str(DIF);
var write_prn_str = write_obj_str(PRN);
var write_txt_str = write_obj_str({from_sheet:sheet_to_txt});
/* Part 3: Packages */
function parse_ods(zip, opts) {
opts = opts || ({});
@ -14863,9 +14893,10 @@ var zip = new jszip();
add_rels(opts.rels, 2, f, RELS.CORE_PROPS);
f = "docProps/app.xml";
if(!wb.Workbook || !wb.Workbook.Sheets) wb.Props.SheetNames = wb.SheetNames;
if(wb.Props && wb.Props.SheetNames){}
else if(!wb.Workbook || !wb.Workbook.Sheets) wb.Props.SheetNames = wb.SheetNames;
// $FlowIgnore
else wb.Props.SheetNames = wb.Workbook.Sheets.filter(function(x) { return x.Hidden != 2; }).map(function(x) { return x.name; });
else wb.Props.SheetNames = wb.SheetNames.map(function(x,i) { return [(wb.Workbook.Sheets[i]||{}).Hidden != 2, x];}).filter(function(x) { return x[0]; }).map(function(x) { return x[1]; });
wb.Props.Worksheets = wb.Props.SheetNames.length;
zip.file(f, write_ext_props(wb.Props, opts));
ct.extprops.push(f);
@ -14982,10 +15013,11 @@ function readSync(data, opts) {
case 0x50: if(n[1] == 0x4B && n[2] < 0x20 && n[3] < 0x20) return read_zip(d, o); break;
case 0xEF: return parse_xlml(d, o);
case 0x03: case 0x83: case 0x8B: return DBF.to_workbook(d, o);
case 0x30: case 0x31: if(n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o); break;
default: throw new Error("Unsupported file " + n.join("|"));
}
throw new Error("Unsupported file format " + n.join("|"));
if(n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o);
if(0x20>n[0]||n[0]>0x7F) throw new Error("Unsupported file " + n.join("|"));
/* TODO: CSV / TXT */
return PRN.to_workbook(d, o);
}
function readFileSync(filename, opts) {
@ -15008,6 +15040,20 @@ function write_zip_type(wb, opts) {
return z.generate(oopts);
}
/* TODO: test consistency */
function write_bstr_type(out, opts) {
switch(opts.type) {
case "base64": return Base64.encode(out);
case "binary": return out;
case "file": return _fs.writeFileSync(opts.file, out, 'binary');
case "buffer": {
if(has_buf) return new Buffer(out, 'utf8');
else return out.split("").map(function(c) { return c.charCodeAt(0); });
}
}
throw new Error("Unrecognized type " + opts.type);
}
/* TODO: test consistency */
function write_string_type(out, opts) {
switch(opts.type) {
@ -15044,8 +15090,10 @@ function writeSync(wb, opts) {
case 'xlml': return write_string_type(write_xlml(wb, o), o);
case 'slk':
case 'sylk': return write_string_type(write_slk_str(wb, o), o);
case 'txt': return write_bstr_type(write_txt_str(wb, o), o);
case 'csv': return write_string_type(write_csv_str(wb, o), o);
case 'dif': return write_string_type(write_dif_str(wb, o), o);
case 'prn': return write_string_type(write_prn_str(wb, o), o);
case 'fods': return write_string_type(write_ods(wb, o), o);
case 'biff2': return write_binary_type(write_biff_buf(wb, o), o);
case 'xlsx':
@ -15068,7 +15116,9 @@ function resolve_book_type(o/*?WriteFileOpts*/) {
case '.xml': o.bookType = 'xml'; break;
case '.ods': o.bookType = 'ods'; break;
case '.csv': o.bookType = 'csv'; break;
case '.txt': o.bookType = 'txt'; break;
case '.dif': o.bookType = 'dif'; break;
case '.prn': o.bookType = 'prn'; break;
case '.slk': o.bookType = 'sylk'; break;
}
}
@ -15273,6 +15323,13 @@ function sheet_to_csv(sheet, opts) {
return out;
}
var make_csv = sheet_to_csv;
function sheet_to_txt(sheet, opts) {
if(!opts) opts = {}; opts.FS = "\t"; opts.RS = "\n";
var s = sheet_to_csv(sheet, opts);
if(typeof cptable == 'undefined') return s;
var o = cptable.utils.encode(1200, s);
return "\xff\xfe" + o;
}
function sheet_to_formulae(sheet) {
var y = "", x, val="";