TXT/PRN
- 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:
parent
3a310bd3a7
commit
b9dae134f2
@ -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)
|
||||
|
||||
|
7
Makefile
7
Makefile
@ -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
|
||||
|
||||
|
54
README.md
54
README.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
|
||||
|
30
formats.dot
30
formats.dot
@ -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
|
||||
}
|
||||
|
BIN
formats.png
BIN
formats.png
Binary file not shown.
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 119 KiB |
BIN
legend.png
Normal file
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
1
misc/docs/legend.png
Symbolic link
@ -0,0 +1 @@
|
||||
../../legend.png
|
42
misc/legend.dot
Normal file
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
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');
|
||||
|
147
xlsx.flow.js
147
xlsx.flow.js
@ -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;
|
||||