Documentation improvements
- multiformat column widths (fixes #591 h/t @sheeeeep) - skip nested BIFF files
This commit is contained in:
parent
ea7a951506
commit
245dd7fd82
@ -37,5 +37,5 @@ test.js
|
||||
.flowconfig
|
||||
*.flow.js
|
||||
bits/
|
||||
odsbits/
|
||||
docbits/
|
||||
tests/
|
||||
|
6
Makefile
6
Makefile
@ -144,8 +144,12 @@ misc/coverage.html: $(TARGET) test.js
|
||||
coveralls: ## Coverage Test + Send to coveralls.io
|
||||
mocha --require blanket --reporter mocha-lcov-reporter -t 20000 | node ./node_modules/coveralls/bin/coveralls.js
|
||||
|
||||
READEPS=$(sort $(wildcard docbits/*.md))
|
||||
README.md: $(READEPS)
|
||||
awk 'FNR==1{p=0}/#/{p=1}p' $^ | tr -d '\15\32' > $@
|
||||
|
||||
.PHONY: readme
|
||||
readme: ## Update README Table of Contents
|
||||
readme: README.md ## Update README Table of Contents
|
||||
markdown-toc -i README.md
|
||||
|
||||
.PHONY: help
|
||||
|
141
README.md
141
README.md
@ -35,10 +35,12 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6.
|
||||
- [Workbook / Worksheet / Cell Object Description](#workbook--worksheet--cell-object-description)
|
||||
* [General Structures](#general-structures)
|
||||
* [Cell Object](#cell-object)
|
||||
* [Data Types](#data-types)
|
||||
* [Formulae](#formulae)
|
||||
+ [Data Types](#data-types)
|
||||
* [Worksheet Object](#worksheet-object)
|
||||
* [Workbook Object](#workbook-object)
|
||||
* [Document Features](#document-features)
|
||||
+ [Formulae](#formulae)
|
||||
+ [Column Properties](#column-properties)
|
||||
- [Parsing Options](#parsing-options)
|
||||
* [Input Type](#input-type)
|
||||
* [Guessing File Type](#guessing-file-type)
|
||||
@ -443,7 +445,7 @@ text from the number format (`cell.z`) and the raw value if possible.
|
||||
The actual array formula is stored in the `f` field of the first cell in the
|
||||
array range. Other cells in the range will omit the `f` field.
|
||||
|
||||
### Data Types
|
||||
#### Data Types
|
||||
|
||||
The raw value is stored in the `v` field, interpreted based on the `t` field.
|
||||
|
||||
@ -482,21 +484,6 @@ Type `z` represents blank stub cells. These do not have any data or type, and
|
||||
are not processed by any of the core library functions. By default these cells
|
||||
will not be generated; the parser `sheetStubs` option must be set to `true`.
|
||||
|
||||
### Formulae
|
||||
|
||||
The A1-style formula string is stored in the `f` field. Even though different
|
||||
file formats store the formulae in different ways, the formats are translated.
|
||||
|
||||
Shared formulae are decompressed and each cell has the correct formula.
|
||||
|
||||
Array formulae are stored in the top-left cell of the array block. All cells
|
||||
of an array formula have a `F` field corresponding to the range. A single-cell
|
||||
formula can be distinguished from a plain formula by the presence of `F` field.
|
||||
|
||||
The `sheet_to_formulae` method generates one line per formula or array formula.
|
||||
Array formulae are rendered in the form `range=formula` while plain cells are
|
||||
rendered in the form `cell=formula or value`.
|
||||
|
||||
### Worksheet Object
|
||||
|
||||
Each key that does not start with `!` maps to a cell (using `A-1` notation)
|
||||
@ -548,6 +535,123 @@ standard, XLS parsing stores core properties in both places. .
|
||||
The workbook's epoch can be determined by examining the workbook's
|
||||
`wb.WBProps.date1904` property.
|
||||
|
||||
### Document Features
|
||||
|
||||
Even for basic features like date storage, the official Excel formats store the
|
||||
same content in different ways. The parsers are expected to convert from the
|
||||
underlying file format representation to the Common Spreadsheet Format. Writers
|
||||
are expected to convert from CSF back to the underlying file format.
|
||||
|
||||
#### Formulae
|
||||
|
||||
The A1-style formula string is stored in the `f` field. Even though different
|
||||
file formats store the formulae in different ways, the formats are translated.
|
||||
Even though some formats store formulae with a leading equal sign, CSF formulae
|
||||
do not start with `=`.
|
||||
|
||||
The worksheet representation of A1=1, A2=2, A3=A1+A2:
|
||||
|
||||
```js
|
||||
{
|
||||
"!ref": "A1:A3",
|
||||
A1: { t:'n', v:1 },
|
||||
A2: { t:'n', v:2 },
|
||||
A3: { t:'n', v:3, f:'A1+A2' }
|
||||
}
|
||||
```
|
||||
|
||||
Shared formulae are decompressed and each cell has the formula corresponding to
|
||||
its cell. Writers generally do not attempt to generate shared formulae.
|
||||
|
||||
Cells with formula entries but no value will be serialized in a way that Excel
|
||||
and other spreadsheet tools will recognize. This library will not automatically
|
||||
compute formula results! For example, to compute `BESSELJ` in a worksheet:
|
||||
|
||||
```js
|
||||
{
|
||||
"!ref": "A1:A3",
|
||||
A1: { t:'n', v:3.14159 },
|
||||
A2: { t:'n', v:2 },
|
||||
A3: { t:'n', f:'BESSELJ(A1,A2)' }
|
||||
}
|
||||
```
|
||||
|
||||
**Array Formulae**
|
||||
|
||||
Array formulae are stored in the top-left cell of the array block. All cells
|
||||
of an array formula have a `F` field corresponding to the range. A single-cell
|
||||
formula can be distinguished from a plain formula by the presence of `F` field.
|
||||
|
||||
For example, setting the cell `C1` to the array formula `{=SUM(A1:A3*B1:B3)}`:
|
||||
|
||||
```js
|
||||
worksheet['C1'] = { t:'n', f: "SUM(A1:A3*B1:B3)", F:"C1:C1" };
|
||||
```
|
||||
|
||||
For a multi-cell array formula, every cell has the same array range but only the
|
||||
first cell has content. Consider `D1:D3=A1:A3*B1:B3`:
|
||||
|
||||
```js
|
||||
worksheet['D1'] = { t:'n', F:"D1:D3", f:"A1:A3*B1:B3" };
|
||||
worksheet['D2'] = { t:'n', F:"D1:D3" };
|
||||
worksheet['D3'] = { t:'n', F:"D1:D3" };
|
||||
```
|
||||
|
||||
Utilities and writers are expected to check for the presence of a `F` field and
|
||||
ignore any possible formula element `f` in cells other than the starting cell.
|
||||
They are not expected to perform validation of the formulae!
|
||||
|
||||
**Formula Output**
|
||||
|
||||
The `sheet_to_formulae` method generates one line per formula or array formula.
|
||||
Array formulae are rendered in the form `range=formula` while plain cells are
|
||||
rendered in the form `cell=formula or value`. Note that string literals are
|
||||
prefixed with an apostrophe `'`, consistent with Excel's formula bar display.
|
||||
|
||||
**Formulae File Format Details**
|
||||
|
||||
| Storage Representation | Formats | Read | Write |
|
||||
|:-----------------------|:-------------------------|:-----:|:-----:|
|
||||
| A1-style strings | XLSX | :o: | :o: |
|
||||
| RC-style strings | XLML and plaintext | :o: | :o: |
|
||||
| BIFF Parsed formulae | XLSB and all XLS formats | :o: | |
|
||||
| OpenFormula formulae | ODS/FODS/UOS | :o: | :o: |
|
||||
|
||||
Since Excel prohibits named cells from colliding with names of A1 or RC style
|
||||
cell references, a (not-so-simple) regex conversion is possible. BIFF Parsed
|
||||
formulae have to be explicitly unwound. OpenFormula formulae can be converted
|
||||
with regexes for the most part.
|
||||
#### Column Properties
|
||||
|
||||
Excel internally stores column widths in a nebulous "Max Digit Width" form. The
|
||||
Max Digit Width is the width of the largest digit when rendered. The internal
|
||||
width must be an integer multiple of the the width divided by 256. ECMA-376
|
||||
describes a formula for converting between pixels and the internal width.
|
||||
|
||||
Given the constraints, it is possible to determine the MDW without actually
|
||||
inspecting the font! The parsers guess the pixel width by converting from width
|
||||
to pixels and back, repeating for all possible MDW and selecting the MDW that
|
||||
minimizes the error. XLML actually stores the pixel width, so the guess works
|
||||
in the opposite direction.
|
||||
|
||||
The `!cols` array in each worksheet, if present, is a collection of `ColInfo`
|
||||
objects which have the following properties:
|
||||
|
||||
```typescript
|
||||
type ColInfo = {
|
||||
MDW?:number; // Excel's "Max Digit Width" unit, always integral
|
||||
width:number; // width in Excel's "Max Digit Width", width*256 is integral
|
||||
wpx?:number; // width in screen pixels
|
||||
wch?:number; // intermediate character calculation
|
||||
};
|
||||
```
|
||||
|
||||
Even though all of the information is made available, writers are expected to
|
||||
follow the priority order:
|
||||
|
||||
1) use `width` field if available
|
||||
2) use `wpx` pixel width if available
|
||||
2) use `wch` character count if available
|
||||
## Parsing Options
|
||||
|
||||
The exported `read` and `readFile` functions accept an options argument:
|
||||
@ -1001,6 +1105,7 @@ OSP-covered specifications:
|
||||
- [MS-XLDM]: Spreadsheet Data Model File Format
|
||||
- [MS-EXSPXML3]: Excel Calculation Version 2 Web Service XML Schema
|
||||
- [XLS]: Microsoft Office Excel 97-2007 Binary File Format Specification
|
||||
- [MS-OI29500]: Office Implementation Information for ISO/IEC 29500 Standards Support
|
||||
|
||||
Open Document Format for Office Applications Version 1.2 (29 September 2011)
|
||||
|
||||
|
@ -75,7 +75,7 @@ function str2cc(str) {
|
||||
}
|
||||
|
||||
function dup(o/*:any*/)/*:any*/ {
|
||||
if(typeof JSON != 'undefined') return JSON.parse(JSON.stringify(o));
|
||||
if(typeof JSON != 'undefined' && !Array.isArray(o)) return JSON.parse(JSON.stringify(o));
|
||||
if(typeof o != 'object' || o == null) return o;
|
||||
var out = {};
|
||||
for(var k in o) if(o.hasOwnProperty(k)) out[k] = dup(o[k]);
|
||||
|
@ -239,10 +239,11 @@ function parse_RecalcId(blob, length) {
|
||||
}
|
||||
|
||||
/* 2.4.87 */
|
||||
function parse_DefaultRowHeight (blob, length) {
|
||||
var f = length == 4 ? blob.read_shift(2) : 0, miyRw;
|
||||
miyRw = blob.read_shift(2); // flags & 0x02 -> hidden, else empty
|
||||
function parse_DefaultRowHeight(blob, length) {
|
||||
var f = blob.read_shift(2);
|
||||
var fl = {Unsynced:f&1,DyZero:(f&2)>>1,ExAsc:(f&4)>>2,ExDsc:(f&8)>>3};
|
||||
/* char is misleading, miyRw and miyRwHidden overlap */
|
||||
var miyRw = blob.read_shift(2);
|
||||
return [fl, miyRw];
|
||||
}
|
||||
|
||||
@ -328,12 +329,13 @@ function parse_MulBlank(blob, length) {
|
||||
}
|
||||
|
||||
/* 2.5.20 2.5.249 TODO: interpret values here */
|
||||
function parse_CellStyleXF(blob, length, style) {
|
||||
function parse_CellStyleXF(blob, length, style, opts) {
|
||||
var o = {};
|
||||
var a = blob.read_shift(4), b = blob.read_shift(4);
|
||||
var c = blob.read_shift(4), d = blob.read_shift(2);
|
||||
o.patternType = XLSFillPattern[c >> 26];
|
||||
|
||||
if(!opts.cellStyles) return o;
|
||||
o.alc = a & 0x07;
|
||||
o.fWrap = (a >> 3) & 0x01;
|
||||
o.alcV = (a >> 4) & 0x07;
|
||||
@ -367,16 +369,16 @@ function parse_CellStyleXF(blob, length, style) {
|
||||
o.fsxButton = (d >> 14) & 0x01;
|
||||
return o;
|
||||
}
|
||||
function parse_CellXF(blob, length) {return parse_CellStyleXF(blob,length,0);}
|
||||
function parse_StyleXF(blob, length) {return parse_CellStyleXF(blob,length,1);}
|
||||
function parse_CellXF(blob, length, opts) {return parse_CellStyleXF(blob,length,0, opts);}
|
||||
function parse_StyleXF(blob, length, opts) {return parse_CellStyleXF(blob,length,1, opts);}
|
||||
|
||||
/* 2.4.353 TODO: actually do this right */
|
||||
function parse_XF(blob, length) {
|
||||
function parse_XF(blob, length, opts) {
|
||||
var o = {};
|
||||
o.ifnt = blob.read_shift(2); o.ifmt = blob.read_shift(2); o.flags = blob.read_shift(2);
|
||||
o.fStyle = (o.flags >> 2) & 0x01;
|
||||
length -= 6;
|
||||
o.data = parse_CellStyleXF(blob, length, o.fStyle);
|
||||
o.data = parse_CellStyleXF(blob, length, o.fStyle, opts);
|
||||
return o;
|
||||
}
|
||||
|
||||
@ -626,12 +628,24 @@ function parse_XFCRC(blob, length) {
|
||||
return o;
|
||||
}
|
||||
|
||||
/* 2.4.53 TODO: parse flags */
|
||||
/* [MS-XLSB] 2.4.323 TODO: parse flags */
|
||||
function parse_ColInfo(blob, length, opts) {
|
||||
if(!opts.cellStyles) return parsenoop(blob, length);
|
||||
var w = opts && opts.biff >= 12 ? 4 : 2;
|
||||
var colFirst = blob.read_shift(w);
|
||||
var colLast = blob.read_shift(w);
|
||||
var coldx = blob.read_shift(w);
|
||||
var ixfe = blob.read_shift(w);
|
||||
var flags = blob.read_shift(2);
|
||||
if(w == 2) blob.l += 2;
|
||||
return {s:colFirst, e:colLast, w:coldx, ixfe:ixfe, flags:flags};
|
||||
}
|
||||
|
||||
|
||||
var parse_Style = parsenoop;
|
||||
var parse_StyleExt = parsenoop;
|
||||
|
||||
var parse_ColInfo = parsenoop;
|
||||
|
||||
var parse_Window2 = parsenoop;
|
||||
|
||||
|
||||
|
@ -50,17 +50,43 @@ function rgb_tint(hex, tint) {
|
||||
}
|
||||
|
||||
/* 18.3.1.13 width calculations */
|
||||
/* [MS-OI29500] 2.1.595 Column Width & Formatting */
|
||||
var DEF_MDW = 7, MAX_MDW = 15, MIN_MDW = 1, MDW = DEF_MDW;
|
||||
function width2px(width) { return (( width + ((128/MDW)|0)/256 )* MDW )|0; }
|
||||
function px2char(px) { return (((px - 5)/MDW * 100 + 0.5)|0)/100; }
|
||||
function char2width(chr) { return (((chr * MDW + 5)/MDW*256)|0)/256; }
|
||||
function width2px(width) { return Math.floor(( width + (Math.round(128/MDW))/256 )* MDW ); }
|
||||
function px2char(px) { return (Math.floor((px - 5)/MDW * 100 + 0.5))/100; }
|
||||
function char2width(chr) { return (Math.round((chr * MDW + 5)/MDW*256))/256; }
|
||||
function px2char_(px) { return (((px - 5)/MDW * 100 + 0.5))/100; }
|
||||
function char2width_(chr) { return (((chr * MDW + 5)/MDW*256))/256; }
|
||||
function cycle_width(collw) { return char2width(px2char(width2px(collw))); }
|
||||
function find_mdw(collw, coll) {
|
||||
if(cycle_width(collw) != collw) {
|
||||
for(MDW=DEF_MDW; MDW>MIN_MDW; --MDW) if(cycle_width(collw) === collw) break;
|
||||
if(MDW === MIN_MDW) for(MDW=DEF_MDW+1; MDW<MAX_MDW; ++MDW) if(cycle_width(collw) === collw) break;
|
||||
if(MDW === MAX_MDW) MDW = DEF_MDW;
|
||||
/* XLSX/XLSB/XLS specify width in units of MDW */
|
||||
function find_mdw_colw(collw) {
|
||||
var delta = Infinity, _MDW = MIN_MDW;
|
||||
for(MDW=MIN_MDW; MDW<MAX_MDW; ++MDW) if(Math.abs(collw - cycle_width(collw)) < delta) { delta = Math.abs(collw - cycle_width(collw)); _MDW = MDW; }
|
||||
MDW = _MDW;
|
||||
}
|
||||
/* XLML specifies width in terms of pixels */
|
||||
function find_mdw_wpx(wpx) {
|
||||
var delta = Infinity, guess = 0, _MDW = MIN_MDW;
|
||||
for(MDW=MIN_MDW; MDW<MAX_MDW; ++MDW) {
|
||||
guess = char2width_(px2char_(wpx))*256;
|
||||
guess = (guess) % 1;
|
||||
if(guess > 0.5) guess--;
|
||||
if(Math.abs(guess) < delta) { delta = Math.abs(guess); _MDW = MDW; }
|
||||
}
|
||||
MDW = _MDW;
|
||||
}
|
||||
|
||||
function process_col(coll/*:ColInfo*/) {
|
||||
if(coll.width) {
|
||||
coll.wpx = width2px(coll.width);
|
||||
coll.wch = px2char(coll.wpx);
|
||||
coll.MDW = MDW;
|
||||
} else if(coll.wpx) {
|
||||
coll.wch = px2char(coll.wpx);
|
||||
coll.width = char2width(coll.wch);
|
||||
coll.MDW = MDW;
|
||||
}
|
||||
if(coll.customWidth) delete coll.customWidth;
|
||||
}
|
||||
|
||||
/* [MS-EXSPXML3] 2.4.54 ST_enmPattern */
|
||||
|
@ -98,14 +98,10 @@ function parse_ws_xml_cols(columns, cols) {
|
||||
for(var coli = 0; coli != cols.length; ++coli) {
|
||||
var coll = parsexmltag(cols[coli], true);
|
||||
var colm=parseInt(coll.min, 10)-1, colM=parseInt(coll.max,10)-1;
|
||||
delete coll.min; delete coll.max;
|
||||
if(!seencol && coll.width) { seencol = true; find_mdw(+coll.width, coll); }
|
||||
if(coll.width) {
|
||||
coll.wpx = width2px(+coll.width);
|
||||
coll.wch = px2char(coll.wpx);
|
||||
coll.MDW = MDW;
|
||||
}
|
||||
while(colm <= colM) columns[colm++] = coll;
|
||||
delete coll.min; delete coll.max; coll.width = +coll.width;
|
||||
if(!seencol && coll.width) { seencol = true; find_mdw_colw(coll.width); }
|
||||
process_col(coll);
|
||||
while(colm <= colM) columns[colm++] = dup(coll);
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +112,9 @@ function write_ws_xml_cols(ws, cols)/*:string*/ {
|
||||
var p = ({min:i+1,max:i+1}/*:any*/);
|
||||
/* wch (chars), wpx (pixels) */
|
||||
width = -1;
|
||||
if(col.wpx) width = px2char(col.wpx);
|
||||
if(col.MDW) MDW = col.MDW;
|
||||
if(col.width);
|
||||
else if(col.wpx) width = px2char(col.wpx);
|
||||
else if(col.wch) width = col.wch;
|
||||
if(width > -1) { p.width = char2width(width); p.customWidth= 1; }
|
||||
o[o.length] = (writextag('col', null, p));
|
||||
|
@ -282,6 +282,10 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
|
||||
for(var i = 0; i < wb.Names['!names'].length; ++i) supbooks[0][i+1] = wb.Names[wb.Names['!names'][i]];
|
||||
|
||||
var colinfo = [], rowinfo = [];
|
||||
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
||||
var seencol = false;
|
||||
|
||||
recordhopper(data, function ws_parse(val, Record) {
|
||||
if(end) return;
|
||||
switch(Record.n) {
|
||||
@ -369,6 +373,16 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
s[encode_col(C) + rr].f = stringify_formula(val[1], refguess, {r:row.r, c:C}, supbooks, opts);
|
||||
break;
|
||||
|
||||
/* identical to 'ColInfo' in XLS */
|
||||
case 'BrtColInfo': {
|
||||
if(!opts.cellStyles) break;
|
||||
while(val.e >= val.s) {
|
||||
colinfo[val.e--] = { width: val.w/256 };
|
||||
if(!seencol) { seencol = true; find_mdw_colw(val.w/256); }
|
||||
process_col(colinfo[val.e+1]);
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'BrtBeginSheet': break;
|
||||
case 'BrtWsProp': break; // TODO
|
||||
case 'BrtSheetCalcProp': break; // TODO
|
||||
@ -384,7 +398,6 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
case 'BrtWsFmtInfoEx14': break; // TODO
|
||||
case 'BrtWsFmtInfo': break; // TODO
|
||||
case 'BrtBeginColInfos': break; // TODO
|
||||
case 'BrtColInfo': break; // TODO
|
||||
case 'BrtEndColInfos': break; // TODO
|
||||
case 'BrtBeginSheetData': break; // TODO
|
||||
case 'BrtEndSheetData': break; // TODO
|
||||
@ -497,6 +510,8 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
}
|
||||
}
|
||||
if(mergecells.length > 0) s["!merges"] = mergecells;
|
||||
if(colinfo.length > 0) s["!cols"] = colinfo;
|
||||
if(rowinfo.length > 0) s["!rows"] = rowinfo;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -178,8 +178,9 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
var mergecells = [];
|
||||
var Props = {}, Custprops = {}, pidx = 0, cp = {};
|
||||
var comments = [], comment = {};
|
||||
var cstys = [], csty;
|
||||
var cstys = [], csty, seencol = false;
|
||||
var arrayf = [];
|
||||
var rowinfo = [];
|
||||
xlmlregex.lastIndex = 0;
|
||||
str = str.replace(/<!--([^\u2603]*?)-->/mg,"");
|
||||
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
|
||||
@ -241,6 +242,8 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
sheetnames.push(sheetname);
|
||||
if(refguess.s.r <= refguess.e.r && refguess.s.c <= refguess.e.c) cursheet["!ref"] = encode_range(refguess);
|
||||
if(mergecells.length) cursheet["!merges"] = mergecells;
|
||||
if(cstys.length > 0) cursheet["!cols"] = cstys;
|
||||
if(rowinfo.length > 0) cursheet["!rows"] = rowinfo;
|
||||
sheets[sheetname] = cursheet;
|
||||
} else {
|
||||
refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
|
||||
@ -251,6 +254,7 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
cursheet = {};
|
||||
mergecells = [];
|
||||
arrayf = [];
|
||||
rowinfo = [];
|
||||
}
|
||||
break;
|
||||
case 'Table':
|
||||
@ -259,7 +263,7 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
else {
|
||||
table = xlml_parsexmltag(Rn[0]);
|
||||
state.push([Rn[3], false]);
|
||||
cstys = [];
|
||||
cstys = []; seencol = false;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -275,8 +279,14 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
case 'Column':
|
||||
if(state[state.length-1][0] !== 'Table') break;
|
||||
csty = xlml_parsexmltag(Rn[0]);
|
||||
csty.wpx = parseInt(csty.Width, 10);
|
||||
if(!seencol && csty.wpx > 10) {
|
||||
seencol = true; find_mdw_wpx(csty.wpx);
|
||||
for(var _col = 0; _col < cstys.length; ++_col) if(cstys[_col]) process_col(cstys[_col]);
|
||||
}
|
||||
if(seencol) process_col(csty);
|
||||
cstys[(csty.Index-1||cstys.length)] = csty;
|
||||
for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = csty;
|
||||
for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = dup(csty);
|
||||
break;
|
||||
|
||||
case 'NamedRange': break;
|
||||
|
@ -100,9 +100,9 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
if(icv < 64) return palette[icv-8] || XLSIcv[icv];
|
||||
return XLSIcv[icv];
|
||||
};
|
||||
var process_cell_style = function pcs(cell, line/*:any*/) {
|
||||
var process_cell_style = function pcs(cell, line/*:any*/, options) {
|
||||
var xfd = line.XF.data;
|
||||
if(!xfd || !xfd.patternType) return;
|
||||
if(!xfd || !xfd.patternType || !options || !options.cellStyles) return;
|
||||
line.s = ({}/*:any*/);
|
||||
line.s.patternType = xfd.patternType;
|
||||
var t;
|
||||
@ -110,8 +110,9 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
if((t = rgb2Hex(get_rgb(xfd.icvBack)))) { line.s.bgColor = {rgb:t}; }
|
||||
};
|
||||
var addcell = function addcell(cell/*:any*/, line/*:any*/, options/*:any*/) {
|
||||
if(file_depth > 1) return;
|
||||
if(!cell_valid) return;
|
||||
if(options.cellStyles && line.XF && line.XF.data) process_cell_style(cell, line);
|
||||
if(options.cellStyles && line.XF && line.XF.data) process_cell_style(cell, line, options);
|
||||
lastcell = cell;
|
||||
last_cell = encode_cell(cell);
|
||||
if(range.s) {
|
||||
@ -149,11 +150,15 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
biff: 8, // BIFF version
|
||||
codepage: 0, // CP from CodePage record
|
||||
winlocked: 0, // fLockWn from WinProtect
|
||||
cellStyles: !!options && !!options.cellStyles,
|
||||
WTF: !!options && !!options.wtf
|
||||
}/*:any*/);
|
||||
if(options.password) opts.password = options.password;
|
||||
var mergecells = [];
|
||||
var objects = [];
|
||||
var colinfo = [], rowinfo = [];
|
||||
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
||||
var seencol = false;
|
||||
var supbooks = ([[]]/*:any*/); // 1-indexed, will hold extern names
|
||||
var sbc = 0, sbci = 0, sbcli = 0;
|
||||
supbooks.SheetNames = opts.snames;
|
||||
@ -260,6 +265,8 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
}
|
||||
if(mergecells.length > 0) out["!merges"] = mergecells;
|
||||
if(objects.length > 0) out["!objects"] = objects;
|
||||
if(colinfo.length > 0) out["!cols"] = colinfo;
|
||||
if(rowinfo.length > 0) out["!rows"] = rowinfo;
|
||||
}
|
||||
if(cur_sheet === "") Preamble = out; else Sheets[cur_sheet] = out;
|
||||
out = {};
|
||||
@ -288,6 +295,9 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
mergecells = [];
|
||||
objects = [];
|
||||
array_formulae = []; opts.arrayf = array_formulae;
|
||||
colinfo = []; rowinfo = [];
|
||||
defwidth = defheight = 0;
|
||||
seencol = false;
|
||||
} break;
|
||||
|
||||
case 'Number': case 'BIFF2NUM': case 'BIFF2INT': {
|
||||
@ -436,6 +446,19 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
case 'ClrtClient': break;
|
||||
case 'XFExt': update_xfext(XFs[val.ixfe], val.ext); break;
|
||||
|
||||
case 'DefColWidth': defwidth = val; break;
|
||||
case 'DefaultRowHeight': defheight = val[1]; break; // TODO: flags
|
||||
|
||||
case 'ColInfo': {
|
||||
if(!opts.cellStyles) break;
|
||||
while(val.e >= val.s) {
|
||||
colinfo[val.e--] = { width: val.w/256 };
|
||||
if(!seencol) { seencol = true; find_mdw_colw(val.w/256); }
|
||||
process_col(colinfo[val.e+1]);
|
||||
}
|
||||
} break;
|
||||
case 'Row': break; // TODO
|
||||
|
||||
case 'NameCmt': break;
|
||||
case 'Header': break; // TODO
|
||||
case 'Footer': break; // TODO
|
||||
@ -443,11 +466,8 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
case 'VCenter': break; // TODO
|
||||
case 'Pls': break; // TODO
|
||||
case 'Setup': break; // TODO
|
||||
case 'DefColWidth': break; // TODO
|
||||
case 'GCW': break;
|
||||
case 'LHRecord': break;
|
||||
case 'ColInfo': break; // TODO
|
||||
case 'Row': break; // TODO
|
||||
case 'DBCell': break; // TODO
|
||||
case 'EntExU2': break; // TODO
|
||||
case 'SxView': break; // TODO
|
||||
@ -465,7 +485,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
case 'Feature11': case 'Feature12': case 'List12': break;
|
||||
case 'Country': country = val; break;
|
||||
case 'RecalcId': break;
|
||||
case 'DefaultRowHeight': case 'DxGCol': break; // TODO: htmlify
|
||||
case 'DxGCol': break; // TODO: htmlify
|
||||
case 'Fbi': case 'Fbi2': case 'GelFrame': break;
|
||||
case 'Font': break; // TODO
|
||||
case 'XFCRC': break; // TODO
|
||||
|
@ -53,7 +53,7 @@ var XLSBRecordEnum = {
|
||||
/*::[*/0x0039/*::]*/: { n:"BrtEndMdxTuple", f:parsenoop },
|
||||
/*::[*/0x003A/*::]*/: { n:"BrtMdxMbrIstr", f:parsenoop },
|
||||
/*::[*/0x003B/*::]*/: { n:"BrtStr", f:parsenoop },
|
||||
/*::[*/0x003C/*::]*/: { n:"BrtColInfo", f:parsenoop },
|
||||
/*::[*/0x003C/*::]*/: { n:"BrtColInfo", f:parse_ColInfo },
|
||||
/*::[*/0x003E/*::]*/: { n:"BrtCellRString", f:parsenoop },
|
||||
/*::[*/0x003F/*::]*/: { n:"BrtCalcChainItem$", f:parse_BrtCalcChainItem$ },
|
||||
/*::[*/0x0040/*::]*/: { n:"BrtDVal", f:parsenoop },
|
||||
|
19
docbits/00_intro.md
Normal file
19
docbits/00_intro.md
Normal file
@ -0,0 +1,19 @@
|
||||
# xlsx
|
||||
|
||||
Parser and writer for various spreadsheet formats. Pure-JS cleanroom
|
||||
implementation from official specifications, related documents, and test files.
|
||||
Emphasis on parsing and writing robustness, cross-format feature compatibility
|
||||
with a unified JS representation, and ES3/ES5 browser compatibility back to IE6.
|
||||
|
||||
[**In-Browser Demo**](http://oss.sheetjs.com/js-xlsx)
|
||||
|
||||
[**Source Code**](http://git.io/xlsx)
|
||||
|
||||
[**Commercial Support**](http://sheetjs.com/reinforcements)
|
||||
|
||||
[**File format support for known spreadsheet data formats:**](#file-formats)
|
||||
|
||||
![circo graph of format support](formats.png)
|
||||
|
||||
|
||||
|
4
docbits/01_toc.md
Normal file
4
docbits/01_toc.md
Normal file
@ -0,0 +1,4 @@
|
||||
## Table of Contents
|
||||
|
||||
<!-- toc -->
|
||||
|
70
docbits/10_install.md
Normal file
70
docbits/10_install.md
Normal file
@ -0,0 +1,70 @@
|
||||
## Installation
|
||||
|
||||
With [npm](https://www.npmjs.org/package/xlsx):
|
||||
|
||||
```bash
|
||||
$ npm install xlsx
|
||||
```
|
||||
|
||||
In the browser:
|
||||
|
||||
```html
|
||||
<script lang="javascript" src="dist/xlsx.core.min.js"></script>
|
||||
```
|
||||
|
||||
With [bower](http://bower.io/search/?q=js-xlsx):
|
||||
|
||||
```bash
|
||||
$ bower install js-xlsx
|
||||
```
|
||||
|
||||
CDNjs automatically pulls the latest version and makes all versions available at
|
||||
<http://cdnjs.com/libraries/xlsx>
|
||||
|
||||
### JS Ecosystem Demos
|
||||
|
||||
The `demos` directory includes sample projects for:
|
||||
|
||||
- [`browserify`](http://browserify.org/)
|
||||
- [`requirejs`](http://requirejs.org/)
|
||||
- [`webpack`](https://webpack.js.org/)
|
||||
|
||||
### Optional Modules
|
||||
|
||||
The node version automatically requires modules for additional features. Some
|
||||
of these modules are rather large in size and are only needed in special
|
||||
circumstances, so they do not ship with the core. For browser use, they must
|
||||
be included directly:
|
||||
|
||||
```html
|
||||
<!-- international support from js-codepage -->
|
||||
<script src="dist/cpexcel.js"></script>
|
||||
```
|
||||
|
||||
An appropriate version for each dependency is included in the dist/ directory.
|
||||
|
||||
The complete single-file version is generated at `dist/xlsx.full.min.js`
|
||||
|
||||
Webpack and browserify builds include optional modules by default. Webpack can
|
||||
be configured to remove support with `resolve.alias`:
|
||||
|
||||
```js
|
||||
/* uncomment the lines below to remove support */
|
||||
resolve: {
|
||||
alias: { "./dist/cpexcel.js": "" } // <-- omit international support
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### ECMAScript 5 Compatibility
|
||||
|
||||
Since xlsx.js uses ES5 functions like `Array#forEach`, older browsers require
|
||||
[Polyfills](http://git.io/QVh77g). This repo and the gh-pages branch include
|
||||
[a shim](https://github.com/SheetJS/js-xlsx/blob/master/shim.js)
|
||||
|
||||
To use the shim, add the shim before the script tag that loads xlsx.js:
|
||||
|
||||
```html
|
||||
<script type="text/javascript" src="/path/to/shim.js"></script>
|
||||
```
|
||||
|
119
docbits/20_import.md
Normal file
119
docbits/20_import.md
Normal file
@ -0,0 +1,119 @@
|
||||
## Parsing Workbooks
|
||||
|
||||
For parsing, the first step is to read the file. This involves acquiring the
|
||||
data and feeding it into the library. Here are a few common scenarios:
|
||||
|
||||
- node readFile:
|
||||
|
||||
```js
|
||||
if(typeof require !== 'undefined') XLSX = require('xlsx');
|
||||
var workbook = XLSX.readFile('test.xlsx');
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
```
|
||||
|
||||
- ajax (for a more complete example that works in older browsers, check the demo
|
||||
at <http://oss.sheetjs.com/js-xlsx/ajax.html>):
|
||||
|
||||
```js
|
||||
/* set up XMLHttpRequest */
|
||||
var url = "test_files/formula_stress_test_ajax.xlsx";
|
||||
var oReq = new XMLHttpRequest();
|
||||
oReq.open("GET", url, true);
|
||||
oReq.responseType = "arraybuffer";
|
||||
|
||||
oReq.onload = function(e) {
|
||||
var arraybuffer = oReq.response;
|
||||
|
||||
/* convert data to binary string */
|
||||
var data = new Uint8Array(arraybuffer);
|
||||
var arr = new Array();
|
||||
for(var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
|
||||
var bstr = arr.join("");
|
||||
|
||||
/* Call XLSX */
|
||||
var workbook = XLSX.read(bstr, {type:"binary"});
|
||||
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
}
|
||||
|
||||
oReq.send();
|
||||
```
|
||||
|
||||
- HTML5 drag-and-drop using readAsBinaryString or readAsArrayBuffer:
|
||||
note: readAsBinaryString and readAsArrayBuffer may not be available in every
|
||||
browser. Use dynamic feature tests to determine which method to use.
|
||||
|
||||
```js
|
||||
/* processing array buffers, only required for readAsArrayBuffer */
|
||||
function fixdata(data) {
|
||||
var o = "", l = 0, w = 10240;
|
||||
for(; l<data.byteLength/w; ++l) o+=String.fromCharCode.apply(null,new Uint8Array(data.slice(l*w,l*w+w)));
|
||||
o+=String.fromCharCode.apply(null, new Uint8Array(data.slice(l*w)));
|
||||
return o;
|
||||
}
|
||||
|
||||
var rABS = true; // true: readAsBinaryString ; false: readAsArrayBuffer
|
||||
/* set up drag-and-drop event */
|
||||
function handleDrop(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
var files = e.dataTransfer.files;
|
||||
var i,f;
|
||||
for (i = 0; i != files.length; ++i) {
|
||||
f = files[i];
|
||||
var reader = new FileReader();
|
||||
var name = f.name;
|
||||
reader.onload = function(e) {
|
||||
var data = e.target.result;
|
||||
|
||||
var workbook;
|
||||
if(rABS) {
|
||||
/* if binary string, read with type 'binary' */
|
||||
workbook = XLSX.read(data, {type: 'binary'});
|
||||
} else {
|
||||
/* if array buffer, convert to base64 */
|
||||
var arr = fixdata(data);
|
||||
workbook = XLSX.read(btoa(arr), {type: 'base64'});
|
||||
}
|
||||
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
};
|
||||
if(rABS) reader.readAsBinaryString(f);
|
||||
else reader.readAsArrayBuffer(f);
|
||||
}
|
||||
}
|
||||
drop_dom_element.addEventListener('drop', handleDrop, false);
|
||||
```
|
||||
|
||||
- HTML5 input file element using readAsBinaryString or readAsArrayBuffer:
|
||||
|
||||
```js
|
||||
/* fixdata and rABS are defined in the drag and drop example */
|
||||
function handleFile(e) {
|
||||
var files = e.target.files;
|
||||
var i,f;
|
||||
for (i = 0; i != files.length; ++i) {
|
||||
f = files[i];
|
||||
var reader = new FileReader();
|
||||
var name = f.name;
|
||||
reader.onload = function(e) {
|
||||
var data = e.target.result;
|
||||
|
||||
var workbook;
|
||||
if(rABS) {
|
||||
/* if binary string, read with type 'binary' */
|
||||
workbook = XLSX.read(data, {type: 'binary'});
|
||||
} else {
|
||||
/* if array buffer, convert to base64 */
|
||||
var arr = fixdata(data);
|
||||
workbook = XLSX.read(btoa(arr), {type: 'base64'});
|
||||
}
|
||||
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
};
|
||||
reader.readAsBinaryString(f);
|
||||
}
|
||||
}
|
||||
input_dom_element.addEventListener('change', handleFile, false);
|
||||
```
|
||||
|
53
docbits/25_manip.md
Normal file
53
docbits/25_manip.md
Normal file
@ -0,0 +1,53 @@
|
||||
## Working with the Workbook
|
||||
|
||||
The full object format is described later in this README.
|
||||
|
||||
This example extracts the value stored in cell A1 from the first worksheet:
|
||||
|
||||
```js
|
||||
var first_sheet_name = workbook.SheetNames[0];
|
||||
var address_of_cell = 'A1';
|
||||
|
||||
/* Get worksheet */
|
||||
var worksheet = workbook.Sheets[first_sheet_name];
|
||||
|
||||
/* Find desired cell */
|
||||
var desired_cell = worksheet[address_of_cell];
|
||||
|
||||
/* Get the value */
|
||||
var desired_value = (desired_cell ? desired_cell.v : undefined);
|
||||
```
|
||||
|
||||
**Complete examples:**
|
||||
|
||||
- <http://oss.sheetjs.com/js-xlsx/> HTML5 File API / Base64 Text / Web Workers
|
||||
|
||||
Note that older versions of IE do not support HTML5 File API, so the base64 mode
|
||||
is used for testing. On OSX you can get the base64 encoding with:
|
||||
|
||||
```bash
|
||||
$ <target_file base64 | pbcopy
|
||||
```
|
||||
|
||||
On Windows XP and up you can get the base64 encoding using `certutil`:
|
||||
|
||||
```cmd
|
||||
> certutil -encode target_file target_file.b64
|
||||
```
|
||||
|
||||
(note: You have to open the file and remove the header and footer lines)
|
||||
|
||||
- <http://oss.sheetjs.com/js-xlsx/ajax.html> XMLHttpRequest
|
||||
|
||||
- <https://github.com/SheetJS/js-xlsx/blob/master/bin/xlsx.njs> node
|
||||
|
||||
The node version installs a command line tool `xlsx` which can read spreadsheet
|
||||
files and output the contents in various formats. The source is available at
|
||||
`xlsx.njs` in the bin directory.
|
||||
|
||||
Some helper functions in `XLSX.utils` generate different views of the sheets:
|
||||
|
||||
- `XLSX.utils.sheet_to_csv` generates CSV
|
||||
- `XLSX.utils.sheet_to_json` generates an array of objects
|
||||
- `XLSX.utils.sheet_to_formulae` generates a list of formulae
|
||||
|
41
docbits/30_export.md
Normal file
41
docbits/30_export.md
Normal file
@ -0,0 +1,41 @@
|
||||
## Writing Workbooks
|
||||
|
||||
For writing, the first step is to generate output data. The helper functions
|
||||
`write` and `writeFile` will produce the data in various formats suitable for
|
||||
dissemination. The second step is to actual share the data with the end point.
|
||||
Assuming `workbook` is a workbook object:
|
||||
|
||||
- nodejs write to file:
|
||||
|
||||
```js
|
||||
/* output format determined by filename */
|
||||
XLSX.writeFile(workbook, 'out.xlsx');
|
||||
/* at this point, out.xlsx is a file that you can distribute */
|
||||
```
|
||||
|
||||
- browser generate binary blob and "download" to client
|
||||
(using [FileSaver.js](https://github.com/eligrey/FileSaver.js/) for download):
|
||||
|
||||
```js
|
||||
/* bookType can be 'xlsx' or 'xlsm' or 'xlsb' or 'ods' */
|
||||
var wopts = { bookType:'xlsx', bookSST:false, type:'binary' };
|
||||
|
||||
var wbout = XLSX.write(workbook,wopts);
|
||||
|
||||
function s2ab(s) {
|
||||
var buf = new ArrayBuffer(s.length);
|
||||
var view = new Uint8Array(buf);
|
||||
for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* the saveAs call downloads a file on the local machine */
|
||||
saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), "test.xlsx");
|
||||
```
|
||||
|
||||
**Complete examples:**
|
||||
|
||||
- <http://sheetjs.com/demos/writexlsx.html> generates a simple file
|
||||
- <http://git.io/WEK88Q> writing an array of arrays in nodejs
|
||||
- <http://sheetjs.com/demos/table.html> exporting an HTML table
|
||||
|
45
docbits/40_interface.md
Normal file
45
docbits/40_interface.md
Normal file
@ -0,0 +1,45 @@
|
||||
## Interface
|
||||
|
||||
`XLSX` is the exposed variable in the browser and the exported node variable
|
||||
|
||||
`XLSX.version` is the version of the library (added by the build script).
|
||||
|
||||
`XLSX.SSF` is an embedded version of the [format library](http://git.io/ssf).
|
||||
|
||||
### Parsing functions
|
||||
|
||||
`XLSX.read(data, read_opts)` attempts to parse `data`.
|
||||
|
||||
`XLSX.readFile(filename, read_opts)` attempts to read `filename` and parse.
|
||||
|
||||
Parse options are described in the [Parsing Options](#parsing-options) section.
|
||||
|
||||
### Writing functions
|
||||
|
||||
`XLSX.write(wb, write_opts)` attempts to write the workbook `wb`
|
||||
|
||||
`XLSX.writeFile(wb, filename, write_opts)` attempts to write `wb` to `filename`
|
||||
|
||||
Write options are described in the [Writing Options](#writing-options) section.
|
||||
|
||||
### Utilities
|
||||
|
||||
Utilities are available in the `XLSX.utils` object:
|
||||
|
||||
**Exporting:**
|
||||
|
||||
- `sheet_to_json` converts a worksheet object to an array of JSON objects.
|
||||
`sheet_to_row_object_array` is an alias that will be removed in the future.
|
||||
- `sheet_to_csv` generates delimiter-separated-values output.
|
||||
- `sheet_to_formulae` generates a list of the formulae (with value fallbacks).
|
||||
|
||||
Exporters are described in the [Utility Functions](#utility-functions) section.
|
||||
|
||||
|
||||
**Cell and cell address manipulation:**
|
||||
|
||||
- `format_cell` generates the text value for a cell (using number formats)
|
||||
- `{en,de}code_{row,col}` convert between 0-indexed rows/cols and A1 forms.
|
||||
- `{en,de}code_cell` converts cell addresses
|
||||
- `{en,de}code_range` converts cell ranges
|
||||
|
23
docbits/50_csf.md
Normal file
23
docbits/50_csf.md
Normal file
@ -0,0 +1,23 @@
|
||||
## Workbook / Worksheet / Cell Object Description
|
||||
|
||||
js-xlsx conforms to the Common Spreadsheet Format (CSF):
|
||||
|
||||
### General Structures
|
||||
|
||||
Cell address objects are stored as `{c:C, r:R}` where `C` and `R` are 0-indexed
|
||||
column and row numbers, respectively. For example, the cell address `B5` is
|
||||
represented by the object `{c:1, r:4}`.
|
||||
|
||||
Cell range objects are stored as `{s:S, e:E}` where `S` is the first cell and
|
||||
`E` is the last cell in the range. The ranges are inclusive. For example, the
|
||||
range `A3:B7` is represented by the object `{s:{c:0, r:2}, e:{c:1, r:6}}`. Utils
|
||||
use the following pattern to walk each of the cells in a range:
|
||||
|
||||
```js
|
||||
for(var R = range.s.r; R <= range.e.r; ++R) {
|
||||
for(var C = range.s.c; C <= range.e.c; ++C) {
|
||||
var cell_address = {c:C, r:R};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
24
docbits/51_cell.md
Normal file
24
docbits/51_cell.md
Normal file
@ -0,0 +1,24 @@
|
||||
### Cell Object
|
||||
|
||||
| Key | Description |
|
||||
| --- | ---------------------------------------------------------------------- |
|
||||
| `v` | raw value (see Data Types section for more info) |
|
||||
| `w` | formatted text (if applicable) |
|
||||
| `t` | cell type: `b` Boolean, `n` Number, `e` error, `s` String, `d` Date |
|
||||
| `f` | cell formula encoded as an A1-style string (if applicable) |
|
||||
| `F` | range of enclosing array if formula is array formula (if applicable) |
|
||||
| `r` | rich text encoding (if applicable) |
|
||||
| `h` | HTML rendering of the rich text (if applicable) |
|
||||
| `c` | comments associated with the cell |
|
||||
| `z` | number format string associated with the cell (if requested) |
|
||||
| `l` | cell hyperlink object (.Target holds link, .tooltip is tooltip) |
|
||||
| `s` | the style/theme of the cell (if applicable) |
|
||||
|
||||
Built-in export utilities (such as the CSV exporter) will use the `w` text if it
|
||||
is available. To change a value, be sure to delete `cell.w` (or set it to
|
||||
`undefined`) before attempting to export. The utilities will regenerate the `w`
|
||||
text from the number format (`cell.z`) and the raw value if possible.
|
||||
|
||||
The actual array formula is stored in the `f` field of the first cell in the
|
||||
array range. Other cells in the range will omit the `f` field.
|
||||
|
39
docbits/52_datatype.md
Normal file
39
docbits/52_datatype.md
Normal file
@ -0,0 +1,39 @@
|
||||
#### Data Types
|
||||
|
||||
The raw value is stored in the `v` field, interpreted based on the `t` field.
|
||||
|
||||
Type `b` is the Boolean type. `v` is interpreted according to JS truth tables.
|
||||
|
||||
Type `e` is the Error type. `v` holds the number and `w` holds the common name:
|
||||
|
||||
| Value | Error Meaning |
|
||||
| -----: | :-------------- |
|
||||
| `0x00` | `#NULL!` |
|
||||
| `0x07` | `#DIV/0!` |
|
||||
| `0x0F` | `#VALUE!` |
|
||||
| `0x17` | `#REF!` |
|
||||
| `0x1D` | `#NAME?` |
|
||||
| `0x24` | `#NUM!` |
|
||||
| `0x2A` | `#N/A` |
|
||||
| `0x2B` | `#GETTING_DATA` |
|
||||
|
||||
Type `n` is the Number type. This includes all forms of data that Excel stores
|
||||
as numbers, such as dates/times and Boolean fields. Excel exclusively uses data
|
||||
that can be fit in an IEEE754 floating point number, just like JS Number, so the
|
||||
`v` field holds the raw number. The `w` field holds formatted text. Dates are
|
||||
stored as numbers by default and converted with `XLSX.SSF.parse_date_code`.
|
||||
|
||||
Type `d` is the Date type, generated only when the option `cellDates` is passed.
|
||||
Since JSON does not have a natural Date type, parsers are generally expected to
|
||||
store ISO 8601 Date strings like you would get from `date.toISOString()`. On
|
||||
the other hand, writers and exporters should be able to handle date strings and
|
||||
JS Date objects. Note that Excel disregards timezone modifiers and treats all
|
||||
dates in the local timezone. js-xlsx does not correct for this error.
|
||||
|
||||
Type `s` is the String type. `v` should be explicitly stored as a string to
|
||||
avoid possible confusion.
|
||||
|
||||
Type `z` represents blank stub cells. These do not have any data or type, and
|
||||
are not processed by any of the core library functions. By default these cells
|
||||
will not be generated; the parser `sheetStubs` option must be set to `true`.
|
||||
|
34
docbits/53_wsobject.md
Normal file
34
docbits/53_wsobject.md
Normal file
@ -0,0 +1,34 @@
|
||||
### Worksheet Object
|
||||
|
||||
Each key that does not start with `!` maps to a cell (using `A-1` notation)
|
||||
|
||||
`worksheet[address]` returns the cell object for the specified address.
|
||||
|
||||
Special worksheet keys (accessible as `worksheet[key]`, each starting with `!`):
|
||||
|
||||
- `ws['!ref']`: A-1 based range representing the worksheet range. Functions that
|
||||
work with sheets should use this parameter to determine the range. Cells that
|
||||
are assigned outside of the range are not processed. In particular, when
|
||||
writing a worksheet by hand, be sure to update the range. For a longer
|
||||
discussion, see <http://git.io/KIaNKQ>
|
||||
|
||||
Functions that handle worksheets should test for the presence of `!ref` field.
|
||||
If the `!ref` is omitted or is not a valid range, functions are free to treat
|
||||
the sheet as empty or attempt to guess the range. The standard utilities that
|
||||
ship with this library treat sheets as empty (for example, the CSV output is
|
||||
empty string).
|
||||
|
||||
When reading a worksheet with the `sheetRows` property set, the ref parameter
|
||||
will use the restricted range. The original range is set at `ws['!fullref']`
|
||||
|
||||
- `ws['!cols']`: array of column properties objects. Column widths are actually
|
||||
stored in files in a normalized manner, measured in terms of the "Maximum
|
||||
Digit Width" (the largest width of the rendered digits 0-9, in pixels). When
|
||||
parsed, the column objects store the pixel width in the `wpx` field, character
|
||||
width in the `wch` field, and the maximum digit width in the `MDW` field.
|
||||
|
||||
- `ws['!merges']`: array of range objects corresponding to the merged cells in
|
||||
the worksheet. Plaintext utilities are unaware of merge cells. CSV export
|
||||
will write all cells in the merge range if they exist, so be sure that only
|
||||
the first cell (upper-left) in the range is set.
|
||||
|
17
docbits/54_wbobject.md
Normal file
17
docbits/54_wbobject.md
Normal file
@ -0,0 +1,17 @@
|
||||
### Workbook Object
|
||||
|
||||
`workbook.SheetNames` is an ordered list of the sheets in the workbook
|
||||
|
||||
`wb.Sheets[sheetname]` returns an object representing the worksheet.
|
||||
|
||||
`wb.Props` is an object storing the standard properties. `wb.Custprops` stores
|
||||
custom properties. Since the XLS standard properties deviate from the XLSX
|
||||
standard, XLS parsing stores core properties in both places. .
|
||||
|
||||
`wb.WBProps` includes more workbook-level properties:
|
||||
|
||||
- Excel supports two epochs (January 1 1900 and January 1 1904), see
|
||||
[1900 vs. 1904 Date System](http://support2.microsoft.com/kb/180162).
|
||||
The workbook's epoch can be determined by examining the workbook's
|
||||
`wb.WBProps.date1904` property.
|
||||
|
7
docbits/60_features.md
Normal file
7
docbits/60_features.md
Normal file
@ -0,0 +1,7 @@
|
||||
### Document Features
|
||||
|
||||
Even for basic features like date storage, the official Excel formats store the
|
||||
same content in different ways. The parsers are expected to convert from the
|
||||
underlying file format representation to the Common Spreadsheet Format. Writers
|
||||
are expected to convert from CSF back to the underlying file format.
|
||||
|
79
docbits/61_formulae.md
Normal file
79
docbits/61_formulae.md
Normal file
@ -0,0 +1,79 @@
|
||||
#### Formulae
|
||||
|
||||
The A1-style formula string is stored in the `f` field. Even though different
|
||||
file formats store the formulae in different ways, the formats are translated.
|
||||
Even though some formats store formulae with a leading equal sign, CSF formulae
|
||||
do not start with `=`.
|
||||
|
||||
The worksheet representation of A1=1, A2=2, A3=A1+A2:
|
||||
|
||||
```js
|
||||
{
|
||||
"!ref": "A1:A3",
|
||||
A1: { t:'n', v:1 },
|
||||
A2: { t:'n', v:2 },
|
||||
A3: { t:'n', v:3, f:'A1+A2' }
|
||||
}
|
||||
```
|
||||
|
||||
Shared formulae are decompressed and each cell has the formula corresponding to
|
||||
its cell. Writers generally do not attempt to generate shared formulae.
|
||||
|
||||
Cells with formula entries but no value will be serialized in a way that Excel
|
||||
and other spreadsheet tools will recognize. This library will not automatically
|
||||
compute formula results! For example, to compute `BESSELJ` in a worksheet:
|
||||
|
||||
```js
|
||||
{
|
||||
"!ref": "A1:A3",
|
||||
A1: { t:'n', v:3.14159 },
|
||||
A2: { t:'n', v:2 },
|
||||
A3: { t:'n', f:'BESSELJ(A1,A2)' }
|
||||
}
|
||||
```
|
||||
|
||||
**Array Formulae**
|
||||
|
||||
Array formulae are stored in the top-left cell of the array block. All cells
|
||||
of an array formula have a `F` field corresponding to the range. A single-cell
|
||||
formula can be distinguished from a plain formula by the presence of `F` field.
|
||||
|
||||
For example, setting the cell `C1` to the array formula `{=SUM(A1:A3*B1:B3)}`:
|
||||
|
||||
```js
|
||||
worksheet['C1'] = { t:'n', f: "SUM(A1:A3*B1:B3)", F:"C1:C1" };
|
||||
```
|
||||
|
||||
For a multi-cell array formula, every cell has the same array range but only the
|
||||
first cell has content. Consider `D1:D3=A1:A3*B1:B3`:
|
||||
|
||||
```js
|
||||
worksheet['D1'] = { t:'n', F:"D1:D3", f:"A1:A3*B1:B3" };
|
||||
worksheet['D2'] = { t:'n', F:"D1:D3" };
|
||||
worksheet['D3'] = { t:'n', F:"D1:D3" };
|
||||
```
|
||||
|
||||
Utilities and writers are expected to check for the presence of a `F` field and
|
||||
ignore any possible formula element `f` in cells other than the starting cell.
|
||||
They are not expected to perform validation of the formulae!
|
||||
|
||||
**Formula Output**
|
||||
|
||||
The `sheet_to_formulae` method generates one line per formula or array formula.
|
||||
Array formulae are rendered in the form `range=formula` while plain cells are
|
||||
rendered in the form `cell=formula or value`. Note that string literals are
|
||||
prefixed with an apostrophe `'`, consistent with Excel's formula bar display.
|
||||
|
||||
**Formulae File Format Details**
|
||||
|
||||
| Storage Representation | Formats | Read | Write |
|
||||
|:-----------------------|:-------------------------|:-----:|:-----:|
|
||||
| A1-style strings | XLSX | :o: | :o: |
|
||||
| RC-style strings | XLML and plaintext | :o: | :o: |
|
||||
| BIFF Parsed formulae | XLSB and all XLS formats | :o: | |
|
||||
| OpenFormula formulae | ODS/FODS/UOS | :o: | :o: |
|
||||
|
||||
Since Excel prohibits named cells from colliding with names of A1 or RC style
|
||||
cell references, a (not-so-simple) regex conversion is possible. BIFF Parsed
|
||||
formulae have to be explicitly unwound. OpenFormula formulae can be converted
|
||||
with regexes for the most part.
|
31
docbits/62_columns.md
Normal file
31
docbits/62_columns.md
Normal file
@ -0,0 +1,31 @@
|
||||
#### Column Properties
|
||||
|
||||
Excel internally stores column widths in a nebulous "Max Digit Width" form. The
|
||||
Max Digit Width is the width of the largest digit when rendered. The internal
|
||||
width must be an integer multiple of the the width divided by 256. ECMA-376
|
||||
describes a formula for converting between pixels and the internal width.
|
||||
|
||||
Given the constraints, it is possible to determine the MDW without actually
|
||||
inspecting the font! The parsers guess the pixel width by converting from width
|
||||
to pixels and back, repeating for all possible MDW and selecting the MDW that
|
||||
minimizes the error. XLML actually stores the pixel width, so the guess works
|
||||
in the opposite direction.
|
||||
|
||||
The `!cols` array in each worksheet, if present, is a collection of `ColInfo`
|
||||
objects which have the following properties:
|
||||
|
||||
```typescript
|
||||
type ColInfo = {
|
||||
MDW?:number; // Excel's "Max Digit Width" unit, always integral
|
||||
width:number; // width in Excel's "Max Digit Width", width*256 is integral
|
||||
wpx?:number; // width in screen pixels
|
||||
wch?:number; // intermediate character calculation
|
||||
};
|
||||
```
|
||||
|
||||
Even though all of the information is made available, writers are expected to
|
||||
follow the priority order:
|
||||
|
||||
1) use `width` field if available
|
||||
2) use `wpx` pixel width if available
|
||||
2) use `wch` character count if available
|
72
docbits/80_parseopts.md
Normal file
72
docbits/80_parseopts.md
Normal file
@ -0,0 +1,72 @@
|
||||
## Parsing Options
|
||||
|
||||
The exported `read` and `readFile` functions accept an options argument:
|
||||
|
||||
| Option Name | Default | Description |
|
||||
| :---------- | ------: | :--------------------------------------------------- |
|
||||
| type | | Input data encoding (see Input Type below) |
|
||||
| cellFormula | true | Save formulae to the .f field ** |
|
||||
| cellHTML | true | Parse rich text and save HTML to the .h field |
|
||||
| cellNF | false | Save number format string to the .z field |
|
||||
| cellStyles | false | Save style/theme info to the .s field |
|
||||
| cellDates | false | Store dates as type `d` (default is `n`) ** |
|
||||
| sheetStubs | false | Create cell objects of type `z` for stub cells |
|
||||
| sheetRows | 0 | If >0, read the first `sheetRows` rows ** |
|
||||
| bookDeps | false | If true, parse calculation chains |
|
||||
| bookFiles | false | If true, add raw files to book object ** |
|
||||
| bookProps | false | If true, only parse enough to get book metadata ** |
|
||||
| bookSheets | false | If true, only parse enough to get the sheet names |
|
||||
| bookVBA | false | If true, expose vbaProject.bin to `vbaraw` field ** |
|
||||
| password | "" | If defined and file is encrypted, use password ** |
|
||||
| WTF | false | If true, throw errors on unexpected file features ** |
|
||||
|
||||
- `cellFormula` option only applies to formats that require extra processing to
|
||||
parse formulae (XLS/XLSB).
|
||||
- Even if `cellNF` is false, formatted text will be generated and saved to `.w`
|
||||
- In some cases, sheets may be parsed even if `bookSheets` is false.
|
||||
- `bookSheets` and `bookProps` combine to give both sets of information
|
||||
- `Deps` will be an empty object if `bookDeps` is falsy
|
||||
- `bookFiles` behavior depends on file type:
|
||||
* `keys` array (paths in the ZIP) for ZIP-based formats
|
||||
* `files` hash (mapping paths to objects representing the files) for ZIP
|
||||
* `cfb` object for formats using CFB containers
|
||||
- `sheetRows-1` rows will be generated when looking at the JSON object output
|
||||
(since the header row is counted as a row when parsing the data)
|
||||
- `bookVBA` merely exposes the raw vba object. It does not parse the data.
|
||||
- `cellDates` currently does not convert numerical dates to JS dates.
|
||||
- Currently only XOR encryption is supported. Unsupported error will be thrown
|
||||
for files employing other encryption methods.
|
||||
- WTF is mainly for development. By default, the parser will suppress read
|
||||
errors on single worksheets, allowing you to read from the worksheets that do
|
||||
parse properly. Setting `WTF:1` forces those errors to be thrown.
|
||||
|
||||
The defaults are enumerated in bits/84\_defaults.js
|
||||
|
||||
### Input Type
|
||||
|
||||
Strings can be interpreted in multiple ways. The `type` parameter for `read`
|
||||
tells the library how to parse the data argument:
|
||||
|
||||
| `type` | expected input |
|
||||
|------------|-----------------------------------------------------------------|
|
||||
| `"base64"` | string: base64 encoding of the file |
|
||||
| `"binary"` | string: binary string (`n`-th byte is `data.charCodeAt(n)`) |
|
||||
| `"buffer"` | nodejs Buffer |
|
||||
| `"array"` | array: array of 8-bit unsigned int (`n`-th byte is `data[n]`) |
|
||||
| `"file"` | string: filename that will be read and processed (nodejs only) |
|
||||
|
||||
### Guessing File Type
|
||||
|
||||
Excel and other spreadsheet tools read the first few bytes and apply other
|
||||
heuristics to determine a file type. This enables file type punning: renaming
|
||||
files with the `.xls` extension will tell your computer to use Excel to open the
|
||||
file but Excel will know how to handle it. This library applies similar logic:
|
||||
|
||||
| Byte 0 | Raw File Type | Spreadsheet Types |
|
||||
|:-------|:--------------|:----------------------------------------------------|
|
||||
| `0xD0` | CFB Container | BIFF 5/8 or password-protected XLSX/XLSB |
|
||||
| `0x09` | BIFF Stream | BIFF 2/3/4/5 |
|
||||
| `0x3C` | XML/HTML | SpreadsheetML or Flat ODS or UOS1 or HTML |
|
||||
| `0x50` | ZIP Archive | XLSB or XLSX/M or ODS or UOS2 |
|
||||
| `0xFE` | UTF8 Text | SpreadsheetML or Flat ODS or UOS1 |
|
||||
|
52
docbits/81_writeopts.md
Normal file
52
docbits/81_writeopts.md
Normal file
@ -0,0 +1,52 @@
|
||||
## Writing Options
|
||||
|
||||
The exported `write` and `writeFile` functions accept an options argument:
|
||||
|
||||
| Option Name | Default | Description |
|
||||
| :---------- | -------: | :-------------------------------------------------- |
|
||||
| type | | Output data encoding (see Output Type below) |
|
||||
| cellDates | `false` | Store dates as type `d` (default is `n`) |
|
||||
| bookSST | `false` | Generate Shared String Table ** |
|
||||
| bookType | `"xlsx"` | Type of Workbook (see below for supported formats) |
|
||||
| sheet | `""` | Name of Worksheet for single-sheet formats ** |
|
||||
| compression | `false` | Use ZIP compression for ZIP-based formats ** |
|
||||
|
||||
- `bookSST` is slower and more memory intensive, but has better compatibility
|
||||
with older versions of iOS Numbers
|
||||
- The raw data is the only thing guaranteed to be saved. Formulae, formatting,
|
||||
and other niceties may not be serialized (pending CSF standardization)
|
||||
- `cellDates` only applies to XLSX output and is not guaranteed to work with
|
||||
third-party readers. Excel itself does not usually write cells with type `d`
|
||||
so non-Excel tools may ignore the data or blow up in the presence of dates.
|
||||
|
||||
### Supported Output Formats
|
||||
|
||||
For broad compatibility with third-party tools, this library supports many
|
||||
output formats. The specific file type is controlled with `bookType` option:
|
||||
|
||||
| bookType | file ext | container | sheets | Description |
|
||||
| :------- | -------: | :-------: | :----- |:--------------------------------- |
|
||||
| `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 |
|
||||
| `fods` | `.fods` | none | multi | Flat OpenDocument Spreadsheet |
|
||||
| `csv` | `.csv` | none | single | Comma Separated Values |
|
||||
|
||||
- `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.
|
||||
|
||||
### Output Type
|
||||
|
||||
The `type` argument for `write` mirrors the `type` argument for `read`:
|
||||
|
||||
| `type` | output |
|
||||
|------------|-----------------------------------------------------------------|
|
||||
| `"base64"` | string: base64 encoding of the file |
|
||||
| `"binary"` | string: binary string (`n`-th byte is `data.charCodeAt(n)`) |
|
||||
| `"buffer"` | nodejs Buffer |
|
||||
| `"file"` | string: name of file to be written (nodejs only) |
|
||||
|
||||
|
124
docbits/82_util.md
Normal file
124
docbits/82_util.md
Normal file
@ -0,0 +1,124 @@
|
||||
## Utility Functions
|
||||
|
||||
The `sheet_to_*` functions accept a worksheet and an optional options object.
|
||||
|
||||
The examples are based on the following worksheet:
|
||||
|
||||
```
|
||||
XXX| A | B | C | D | E | F | G |
|
||||
---+---+---+---+---+---+---+---+
|
||||
1 | S | h | e | e | t | J | S |
|
||||
2 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|
||||
3 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|
||||
```
|
||||
|
||||
### Formulae Output
|
||||
|
||||
`XLSX.utils.sheet_to_formulae` generates an array of commands that represent
|
||||
how a person would enter data into an application. Each entry is of the form
|
||||
`A1-cell-address=formula-or-value`. String literals are prefixed with a `'` in
|
||||
accordance with Excel. For the example sheet:
|
||||
|
||||
```js
|
||||
> var o = XLSX.utils.sheet_to_formulae(ws);
|
||||
> o.filter(function(v, i) { return i % 5 === 0; });
|
||||
[ 'A1=\'S', 'F1=\'J', 'D2=4', 'B3=3', 'G3=8' ]
|
||||
```
|
||||
|
||||
### CSV and general DSV Output
|
||||
|
||||
As an alternative to the `writeFile` CSV type, `XLSX.utils.sheet_to_csv` also
|
||||
produces CSV output. The function takes an options argument:
|
||||
|
||||
| Option Name | Default | Description |
|
||||
| :---------- | :------: | :-------------------------------------------------- |
|
||||
| FS | `","` | "Field Separator" delimiter between fields |
|
||||
| RS | `"\n"` | "Record Separator" delimiter between rows |
|
||||
|
||||
For the example sheet:
|
||||
|
||||
```js
|
||||
> console.log(XLSX.utils.sheet_to_csv(ws));
|
||||
S,h,e,e,t,J,S
|
||||
1,2,3,4,5,6,7
|
||||
2,3,4,5,6,7,8
|
||||
> console.log(XLSX.utils.sheet_to_csv(ws, {FS:"\t"}));
|
||||
S h e e t J S
|
||||
1 2 3 4 5 6 7
|
||||
2 3 4 5 6 7 8
|
||||
> console.log(X.utils.sheet_to_csv(_ws,{FS:":",RS:"|"}));
|
||||
S:h:e:e:t:J:S|1:2:3:4:5:6:7|2:3:4:5:6:7:8|
|
||||
```
|
||||
|
||||
### JSON
|
||||
|
||||
`XLSX.utils.sheet_to_json` and the alias `XLSX.utils.sheet_to_row_object_array`
|
||||
generate different types of JS objects. The function takes an options argument:
|
||||
|
||||
| Option Name | Default | Description |
|
||||
| :---------- | :------: | :-------------------------------------------------- |
|
||||
| raw | `false` | Use raw values (true) or formatted strings (false) |
|
||||
| range | from WS | Override Range (see table below) |
|
||||
| header | | Control output format (see table below) |
|
||||
|
||||
- `raw` only affects cells which have a format code (`.z`) field or a formatted
|
||||
text (`.w`) field.
|
||||
- If `header` is specified, the first row is considered a data row; if `header`
|
||||
is not specified, the first row is the header row and not considered data.
|
||||
- When `header` is not specified, the conversion will automatically disambiguate
|
||||
header entries by affixing `_` and a count starting at `1`. For example, if
|
||||
three columns have header `foo` the output fields are `foo`, `foo_1`, `foo_2`
|
||||
|
||||
`range` is expected to be one of:
|
||||
|
||||
| `range` | Description |
|
||||
| :--------------- | :-------------------------------------------------------- |
|
||||
| (number) | Use worksheet range but set starting row to the value |
|
||||
| (string) | Use specified range (A1-style bounded range string) |
|
||||
| (default) | Use worksheet range (`ws['!ref']`) |
|
||||
|
||||
`header` is expected to be one of:
|
||||
|
||||
| `header` | Description |
|
||||
| :--------------- | :-------------------------------------------------------- |
|
||||
| `1` | Generate an array of arrays |
|
||||
| `"A"` | Row object keys are literal column labels |
|
||||
| array of strings | Use specified strings as keys in row objects |
|
||||
| (default) | Read and disambiguate first row as keys |
|
||||
|
||||
For the example sheet:
|
||||
|
||||
```js
|
||||
> console.log(X.utils.sheet_to_json(_ws));
|
||||
[ { S: 1, h: 2, e: 3, e_1: 4, t: 5, J: 6, S_1: 7 },
|
||||
{ S: 2, h: 3, e: 4, e_1: 5, t: 6, J: 7, S_1: 8 } ]
|
||||
|
||||
> console.log(X.utils.sheet_to_json(_ws, {header:1}));
|
||||
[ [ 'S', 'h', 'e', 'e', 't', 'J', 'S' ],
|
||||
[ 1, 2, 3, 4, 5, 6, 7 ],
|
||||
[ 2, 3, 4, 5, 6, 7, 8 ] ]
|
||||
|
||||
> console.log(X.utils.sheet_to_json(_ws, {header:"A"}));
|
||||
[ { A: 'S', B: 'h', C: 'e', D: 'e', E: 't', F: 'J', G: 'S' },
|
||||
{ A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7 },
|
||||
{ A: 2, B: 3, C: 4, D: 5, E: 6, F: 7, G: 8 } ]
|
||||
> console.log(X.utils.sheet_to_json(_ws, {header:["A","E","I","O","U","6","9"]}));
|
||||
[ { '6': 'J', '9': 'S', A: 'S', E: 'h', I: 'e', O: 'e', U: 't' },
|
||||
{ '6': 6, '9': 7, A: 1, E: 2, I: 3, O: 4, U: 5 },
|
||||
{ '6': 7, '9': 8, A: 2, E: 3, I: 4, O: 5, U: 6 } ]
|
||||
```
|
||||
|
||||
Example showing the effect of `raw`:
|
||||
|
||||
```js
|
||||
> _ws['A2'].w = "1"; // set A2 formatted string value
|
||||
> console.log(X.utils.sheet_to_json(_ws, {header:1}));
|
||||
[ [ 'S', 'h', 'e', 'e', 't', 'J', 'S' ],
|
||||
[ '1', 2, 3, 4, 5, 6, 7 ], // <-- A2 uses the formatted string
|
||||
[ 2, 3, 4, 5, 6, 7, 8 ] ]
|
||||
> console.log(X.utils.sheet_to_json(_ws, {header:1, raw:true}));
|
||||
[ [ 'S', 'h', 'e', 'e', 't', 'J', 'S' ],
|
||||
[ 1, 2, 3, 4, 5, 6, 7 ], // <-- A2 uses the raw value
|
||||
[ 2, 3, 4, 5, 6, 7, 8 ] ]
|
||||
```
|
||||
|
93
docbits/85_filetype.md
Normal file
93
docbits/85_filetype.md
Normal file
@ -0,0 +1,93 @@
|
||||
## File Formats
|
||||
|
||||
Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
|
||||
|
||||
| Format | Read | Write |
|
||||
|:-------------------------------------------------------------|:-----:|:-----:|
|
||||
| **Excel Worksheet/Workbook Formats** |:-----:|:-----:|
|
||||
| Excel 2007+ XML Formats (XLSX/XLSM) | :o: | :o: |
|
||||
| Excel 2007+ Binary Format (XLSB BIFF12) | :o: | :o: |
|
||||
| Excel 2003-2004 XML Format (XML "SpreadsheetML") | :o: | :o: |
|
||||
| Excel 97-2004 (XLS BIFF8) | :o: | |
|
||||
| Excel 5.0/95 (XLS BIFF5) | :o: | |
|
||||
| Excel 4.0 (XLS/XLW BIFF4) | :o: | |
|
||||
| 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: |
|
||||
| **Other Workbook/Worksheet Formats** |:-----:|:-----:|
|
||||
| OpenDocument Spreadsheet (ODS) | :o: | :o: |
|
||||
| Flat XML ODF Spreadsheet (FODS) | :o: | :o: |
|
||||
| Uniform Office Format Spreadsheet (标文通 UOS1/UOS2) | :o: | |
|
||||
| **Other Common Spreadsheet Output Formats** |:-----:|:-----:|
|
||||
| HTML Tables | :o: | |
|
||||
|
||||
### Excel 2007+ XML (XLSX/XLSM)
|
||||
|
||||
XLSX and XLSM files are ZIP containers containing a series of XML files in
|
||||
accordance with the Open Packaging Conventions (OPC). The XLSM filetype, almost
|
||||
identical to XLSX, is used for files containing macros.
|
||||
|
||||
The format is standardized in ECMA-376 and later in ISO/IEC 29500. Excel does
|
||||
not follow the specification, and there are additional documents discussing how
|
||||
Excel deviates from the specification.
|
||||
|
||||
### Excel 2.0-95 (BIFF2/BIFF3/BIFF4/BIFF5)
|
||||
|
||||
BIFF 2/3 XLS are single-sheet streams of binary records. Excel 4 introduced
|
||||
the concept of a workbook (`XLW` files) but also had single-sheet `XLS` format.
|
||||
The structure is largely similar to the Lotus 1-2-3 file formats. BIFF5/8/12
|
||||
extended the format in various ways but largely stuck to the same record format.
|
||||
|
||||
There is no official specification for any of these formats. Excel 95 can write
|
||||
files in these formats, so record lengths and fields were backsolved by writing
|
||||
in all of the supported formats and comparing files. Excel 2016 can generate
|
||||
BIFF5 files, enabling a full suite of file tests starting from XLSX or BIFF2.
|
||||
|
||||
### Excel 97-2004 Binary (BIFF8)
|
||||
|
||||
BIFF8 exclusively uses the Compound File Binary container format, splitting some
|
||||
content into streams within the file. At its core, it still uses an extended
|
||||
version of the binary record format from older versions of BIFF.
|
||||
|
||||
The `MS-XLS` specification covers the basics of the file format, and other
|
||||
specifications expand on serialization of features like properties.
|
||||
|
||||
### Excel 2003-2004 (SpreadsheetML)
|
||||
|
||||
Predating XLSX, SpreadsheetML files are simple XML files. There is no official
|
||||
and comprehensive specification, although MS has released whitepapers on the
|
||||
format. Since Excel 2016 can generate SpreadsheetML files, backsolving is
|
||||
pretty straightforward.
|
||||
|
||||
### Excel 2007+ Binary (XLSB, BIFF12)
|
||||
|
||||
Introduced in parallel with XLSX, the XLSB filetype combines BIFF architecture
|
||||
with the content separation and ZIP container of XLSX. For the most part nodes
|
||||
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)
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
### HTML
|
||||
|
||||
Excel HTML worksheets include special metadata encoded in styles. For example,
|
||||
`mso-number-format` is a localized string containing the number format. Despite
|
||||
the metadata the output is valid HTML, although it does accept bare `&` symbols.
|
||||
|
62
docbits/90_test.md
Normal file
62
docbits/90_test.md
Normal file
@ -0,0 +1,62 @@
|
||||
## Testing
|
||||
|
||||
`make test` will run the node-based tests. By default it runs tests on files in
|
||||
every supported format. To test a specific file type, set `FMTS` to the format
|
||||
you want to test. Feature-specific tests are avaialble with `make test_misc`
|
||||
|
||||
```bash
|
||||
$ make test # run full tests
|
||||
$ make test_xls # only use the XLS test files
|
||||
$ make test_xlsx # only use the XLSX test files
|
||||
$ make test_xlsb # only use the XLSB test files
|
||||
$ make test_xml # only use the XLSB test files
|
||||
$ make test_ods # only use the XLSB test files
|
||||
```
|
||||
|
||||
To enable all errors, set the environment variable `WTF=1`:
|
||||
|
||||
```bash
|
||||
$ make test # run full tests
|
||||
$ WTF=1 make test # enable all error messages
|
||||
```
|
||||
|
||||
Flow and JSHint/JSCS checks are available:
|
||||
|
||||
```bash
|
||||
$ make lint # JSHint and JSCS checks
|
||||
$ make flow # make lint + Flow checking
|
||||
```
|
||||
|
||||
To run the in-browser tests, clone the repo for
|
||||
[oss.sheetjs.com](https://github.com/SheetJS/SheetJS.github.io) and replace
|
||||
the xlsx.js file (then fire up the browser and go to `stress.html`):
|
||||
|
||||
```bash
|
||||
$ cp xlsx.js ../SheetJS.github.io
|
||||
$ cd ../SheetJS.github.io
|
||||
$ simplehttpserver # or "python -mSimpleHTTPServer" or "serve"
|
||||
$ open -a Chromium.app http://localhost:8000/stress.html
|
||||
```
|
||||
### Tested Environments
|
||||
|
||||
- NodeJS 0.8, 0.9, 0.10, 0.11, 0.12, 4.x, 5.x, 6.x, 7.x
|
||||
- IE 6/7/8/9/10/11 (IE6-9 browsers require shims for interacting with client)
|
||||
- Chrome 24+
|
||||
- Safari 6+
|
||||
- FF 18+
|
||||
|
||||
Tests utilize the mocha testing framework. Travis-CI and Sauce Labs links:
|
||||
|
||||
- <https://travis-ci.org/SheetJS/js-xlsx> for XLSX module in nodejs
|
||||
- <https://semaphoreci.com/sheetjs/js-xlsx> for XLSX module in nodejs
|
||||
- <https://travis-ci.org/SheetJS/SheetJS.github.io> for XLS\* modules
|
||||
- <https://saucelabs.com/u/sheetjs> for XLS\* modules using Sauce Labs
|
||||
|
||||
### Test Files
|
||||
|
||||
Test files are housed in [another repo](https://github.com/SheetJS/test_files).
|
||||
|
||||
Running `make init` will refresh the `test_files` submodule and get the files.
|
||||
|
||||
|
||||
|
21
docbits/95_contrib.md
Normal file
21
docbits/95_contrib.md
Normal file
@ -0,0 +1,21 @@
|
||||
## Contributing
|
||||
|
||||
Due to the precarious nature of the Open Specifications Promise, it is very
|
||||
important to ensure code is cleanroom. Consult CONTRIBUTING.md
|
||||
|
||||
The xlsx.js file is constructed from the files in the `bits` subdirectory. The
|
||||
build script (run `make`) will concatenate the individual bits to produce the
|
||||
script. Before submitting a contribution, ensure that running make will produce
|
||||
the xlsx.js file exactly. The simplest way to test is to add the script:
|
||||
|
||||
```bash
|
||||
$ git add xlsx.js
|
||||
$ make clean
|
||||
$ make
|
||||
$ git diff xlsx.js
|
||||
```
|
||||
|
||||
To produce the dist files, run `make dist`. The dist files are updated in each
|
||||
version release and *should not be committed between versions*.
|
||||
|
||||
|
6
docbits/97_license.md
Normal file
6
docbits/97_license.md
Normal file
@ -0,0 +1,6 @@
|
||||
## License
|
||||
|
||||
Please consult the attached LICENSE file for details. All rights not explicitly
|
||||
granted by the Apache 2.0 License are reserved by the Original Author.
|
||||
|
||||
|
30
docbits/98_reference.md
Normal file
30
docbits/98_reference.md
Normal file
@ -0,0 +1,30 @@
|
||||
## References
|
||||
|
||||
ISO/IEC 29500:2012(E) "Information technology — Document description and processing languages — Office Open XML File Formats"
|
||||
|
||||
OSP-covered specifications:
|
||||
|
||||
- [MS-XLSB]: Excel (.xlsb) Binary File Format
|
||||
- [MS-XLSX]: Excel (.xlsx) Extensions to the Office Open XML SpreadsheetML File Format
|
||||
- [MS-OE376]: Office Implementation Information for ECMA-376 Standards Support
|
||||
- [MS-CFB]: Compound File Binary File Format
|
||||
- [MS-XLS]: Excel Binary File Format (.xls) Structure Specification
|
||||
- [MS-ODATA]: Open Data Protocol (OData)
|
||||
- [MS-OFFCRYPTO]: Office Document Cryptography Structure
|
||||
- [MS-OLEDS]: Object Linking and Embedding (OLE) Data Structures
|
||||
- [MS-OLEPS]: Object Linking and Embedding (OLE) Property Set Data Structures
|
||||
- [MS-OSHARED]: Office Common Data Types and Objects Structures
|
||||
- [MS-ODRAW]: Office Drawing Binary File Format
|
||||
- [MS-ODRAWXML]: Office Drawing Extensions to Office Open XML Structure
|
||||
- [MS-OVBA]: Office VBA File Format Structure
|
||||
- [MS-CTXLS]: Excel Custom Toolbar Binary File Format
|
||||
- [MS-XLDM]: Spreadsheet Data Model File Format
|
||||
- [MS-EXSPXML3]: Excel Calculation Version 2 Web Service XML Schema
|
||||
- [XLS]: Microsoft Office Excel 97-2007 Binary File Format Specification
|
||||
- [MS-OI29500]: Office Implementation Information for ISO/IEC 29500 Standards Support
|
||||
|
||||
Open Document Format for Office Applications Version 1.2 (29 September 2011)
|
||||
|
||||
Worksheet File Format (From Lotus) December 1984
|
||||
|
||||
|
17
docbits/99_badges.md
Normal file
17
docbits/99_badges.md
Normal file
@ -0,0 +1,17 @@
|
||||
## Badges
|
||||
|
||||
[![Build Status](https://saucelabs.com/browser-matrix/xlsx.svg)](https://saucelabs.com/u/xlsx)
|
||||
|
||||
[![Build Status](https://travis-ci.org/SheetJS/js-xlsx.svg?branch=master)](https://travis-ci.org/SheetJS/js-xlsx)
|
||||
|
||||
[![Build Status](https://semaphoreci.com/api/v1/sheetjs/js-xlsx/branches/master/shields_badge.svg)](https://semaphoreci.com/sheetjs/js-xlsx)
|
||||
|
||||
[![Coverage Status](http://img.shields.io/coveralls/SheetJS/js-xlsx/master.svg)](https://coveralls.io/r/SheetJS/js-xlsx?branch=master)
|
||||
|
||||
[![NPM Downloads](https://img.shields.io/npm/dt/xlsx.svg)](https://npmjs.org/package/xlsx)
|
||||
|
||||
[![Dependencies Status](https://david-dm.org/sheetjs/js-xlsx/status.svg)](https://david-dm.org/sheetjs/js-xlsx)
|
||||
|
||||
[![ghit.me](https://ghit.me/badge.svg?repo=sheetjs/js-xlsx)](https://ghit.me/repo/sheetjs/js-xlsx)
|
||||
|
||||
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
|
@ -59,4 +59,11 @@ type SST = {
|
||||
};
|
||||
|
||||
type Comment = any;
|
||||
|
||||
type ColInfo = {
|
||||
MDW?:number; // Excel's "Max Digit Width" unit, always integral
|
||||
width:number; // width in Excel's "Max Digit Width", width*256 is integral
|
||||
wpx?:number; // width in screen pixels
|
||||
wch?:number; // intermediate character calculation
|
||||
};
|
||||
*/
|
||||
|
49
test.js
49
test.js
@ -66,6 +66,12 @@ var paths = {
|
||||
nfxlsb: dir + 'number_format.xlsb',
|
||||
dtxlsx: dir + 'xlsx-stream-d-date-cell.xlsx',
|
||||
dtxlsb: dir + 'xlsx-stream-d-date-cell.xlsb',
|
||||
cwxls: dir + 'column_width.xlsx',
|
||||
cwxls5: dir + 'column_width.biff5',
|
||||
cwxml: dir + 'column_width.xml',
|
||||
cwxlsx: dir + 'column_width.xlsx',
|
||||
cwxlsb: dir + 'column_width.xlsx',
|
||||
dtxlsb: dir + 'xlsx-stream-d-date-cell.xlsb',
|
||||
swcxls: dir + 'apachepoi_SimpleWithComments.xls',
|
||||
swcxml: dir + '2011/apachepoi_SimpleWithComments.xls.xml',
|
||||
swcxlsx: dir + 'apachepoi_SimpleWithComments.xlsx',
|
||||
@ -680,6 +686,49 @@ describe('parse features', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('column properties', function() {
|
||||
var wb1, wb2, wb3, wb4, wb5;
|
||||
var bef = (function() {
|
||||
X = require(modp);
|
||||
wb1 = X.readFile(paths.cwxlsx, {cellStyles:true});
|
||||
wb2 = X.readFile(paths.cwxlsb, {cellStyles:true});
|
||||
wb3 = X.readFile(paths.cwxls, {cellStyles:true});
|
||||
wb4 = X.readFile(paths.cwxls5, {cellStyles:true});
|
||||
wb5 = X.readFile(paths.cwxml, {cellStyles:true});
|
||||
});
|
||||
if(typeof before != 'undefined') before(bef);
|
||||
else it('before', bef);
|
||||
it('should have "!cols"', function() {
|
||||
assert(wb1.Sheets.Sheet1['!cols']);
|
||||
assert(wb2.Sheets.Sheet1['!cols']);
|
||||
assert(wb3.Sheets.Sheet1['!cols']);
|
||||
assert(wb4.Sheets.Sheet1['!cols']);
|
||||
assert(wb5.Sheets.Sheet1['!cols']);
|
||||
});
|
||||
it('should have correct widths', function() {
|
||||
[wb1, wb2, wb3, wb4, wb5].map(function(x) { return x.Sheets.Sheet1['!cols']; }).forEach(function(x) {
|
||||
assert.equal(x[1].width, 0.1640625);
|
||||
assert.equal(x[2].width, 16.6640625);
|
||||
assert.equal(x[3].width, 1.6640625);
|
||||
assert.equal(x[4].width, 4.83203125);
|
||||
assert.equal(x[5].width, 8.83203125);
|
||||
assert.equal(x[6].width, 12.83203125);
|
||||
assert.equal(x[7].width, 16.83203125);
|
||||
});
|
||||
});
|
||||
it('should have correct pixels', function() {
|
||||
[wb1, wb2, wb3, wb4, wb5].map(function(x) { return x.Sheets.Sheet1['!cols']; }).forEach(function(x) {
|
||||
assert.equal(x[1].wpx, 1);
|
||||
assert.equal(x[2].wpx, 100);
|
||||
assert.equal(x[3].wpx, 10);
|
||||
assert.equal(x[4].wpx, 29);
|
||||
assert.equal(x[5].wpx, 53);
|
||||
assert.equal(x[6].wpx, 77);
|
||||
assert.equal(x[7].wpx, 101);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('merge cells',function() {
|
||||
var wb1, wb2, wb3, wb4, wb5;
|
||||
var bef = (function() {
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 664ad78895cb8dad33e8e993614555baf7d999ae
|
||||
Subproject commit 7fe887ff7b26b233db24f3d4df513319c493efc8
|
163
xlsx.flow.js
163
xlsx.flow.js
@ -1398,7 +1398,7 @@ function str2cc(str) {
|
||||
}
|
||||
|
||||
function dup(o/*:any*/)/*:any*/ {
|
||||
if(typeof JSON != 'undefined') return JSON.parse(JSON.stringify(o));
|
||||
if(typeof JSON != 'undefined' && !Array.isArray(o)) return JSON.parse(JSON.stringify(o));
|
||||
if(typeof o != 'object' || o == null) return o;
|
||||
var out = {};
|
||||
for(var k in o) if(o.hasOwnProperty(k)) out[k] = dup(o[k]);
|
||||
@ -3786,10 +3786,11 @@ function parse_RecalcId(blob, length) {
|
||||
}
|
||||
|
||||
/* 2.4.87 */
|
||||
function parse_DefaultRowHeight (blob, length) {
|
||||
var f = length == 4 ? blob.read_shift(2) : 0, miyRw;
|
||||
miyRw = blob.read_shift(2); // flags & 0x02 -> hidden, else empty
|
||||
function parse_DefaultRowHeight(blob, length) {
|
||||
var f = blob.read_shift(2);
|
||||
var fl = {Unsynced:f&1,DyZero:(f&2)>>1,ExAsc:(f&4)>>2,ExDsc:(f&8)>>3};
|
||||
/* char is misleading, miyRw and miyRwHidden overlap */
|
||||
var miyRw = blob.read_shift(2);
|
||||
return [fl, miyRw];
|
||||
}
|
||||
|
||||
@ -3875,12 +3876,13 @@ function parse_MulBlank(blob, length) {
|
||||
}
|
||||
|
||||
/* 2.5.20 2.5.249 TODO: interpret values here */
|
||||
function parse_CellStyleXF(blob, length, style) {
|
||||
function parse_CellStyleXF(blob, length, style, opts) {
|
||||
var o = {};
|
||||
var a = blob.read_shift(4), b = blob.read_shift(4);
|
||||
var c = blob.read_shift(4), d = blob.read_shift(2);
|
||||
o.patternType = XLSFillPattern[c >> 26];
|
||||
|
||||
if(!opts.cellStyles) return o;
|
||||
o.alc = a & 0x07;
|
||||
o.fWrap = (a >> 3) & 0x01;
|
||||
o.alcV = (a >> 4) & 0x07;
|
||||
@ -3914,16 +3916,16 @@ function parse_CellStyleXF(blob, length, style) {
|
||||
o.fsxButton = (d >> 14) & 0x01;
|
||||
return o;
|
||||
}
|
||||
function parse_CellXF(blob, length) {return parse_CellStyleXF(blob,length,0);}
|
||||
function parse_StyleXF(blob, length) {return parse_CellStyleXF(blob,length,1);}
|
||||
function parse_CellXF(blob, length, opts) {return parse_CellStyleXF(blob,length,0, opts);}
|
||||
function parse_StyleXF(blob, length, opts) {return parse_CellStyleXF(blob,length,1, opts);}
|
||||
|
||||
/* 2.4.353 TODO: actually do this right */
|
||||
function parse_XF(blob, length) {
|
||||
function parse_XF(blob, length, opts) {
|
||||
var o = {};
|
||||
o.ifnt = blob.read_shift(2); o.ifmt = blob.read_shift(2); o.flags = blob.read_shift(2);
|
||||
o.fStyle = (o.flags >> 2) & 0x01;
|
||||
length -= 6;
|
||||
o.data = parse_CellStyleXF(blob, length, o.fStyle);
|
||||
o.data = parse_CellStyleXF(blob, length, o.fStyle, opts);
|
||||
return o;
|
||||
}
|
||||
|
||||
@ -4173,12 +4175,24 @@ function parse_XFCRC(blob, length) {
|
||||
return o;
|
||||
}
|
||||
|
||||
/* 2.4.53 TODO: parse flags */
|
||||
/* [MS-XLSB] 2.4.323 TODO: parse flags */
|
||||
function parse_ColInfo(blob, length, opts) {
|
||||
if(!opts.cellStyles) return parsenoop(blob, length);
|
||||
var w = opts && opts.biff >= 12 ? 4 : 2;
|
||||
var colFirst = blob.read_shift(w);
|
||||
var colLast = blob.read_shift(w);
|
||||
var coldx = blob.read_shift(w);
|
||||
var ixfe = blob.read_shift(w);
|
||||
var flags = blob.read_shift(2);
|
||||
if(w == 2) blob.l += 2;
|
||||
return {s:colFirst, e:colLast, w:coldx, ixfe:ixfe, flags:flags};
|
||||
}
|
||||
|
||||
|
||||
var parse_Style = parsenoop;
|
||||
var parse_StyleExt = parsenoop;
|
||||
|
||||
var parse_ColInfo = parsenoop;
|
||||
|
||||
var parse_Window2 = parsenoop;
|
||||
|
||||
|
||||
@ -5039,17 +5053,43 @@ function rgb_tint(hex, tint) {
|
||||
}
|
||||
|
||||
/* 18.3.1.13 width calculations */
|
||||
/* [MS-OI29500] 2.1.595 Column Width & Formatting */
|
||||
var DEF_MDW = 7, MAX_MDW = 15, MIN_MDW = 1, MDW = DEF_MDW;
|
||||
function width2px(width) { return (( width + ((128/MDW)|0)/256 )* MDW )|0; }
|
||||
function px2char(px) { return (((px - 5)/MDW * 100 + 0.5)|0)/100; }
|
||||
function char2width(chr) { return (((chr * MDW + 5)/MDW*256)|0)/256; }
|
||||
function width2px(width) { return Math.floor(( width + (Math.round(128/MDW))/256 )* MDW ); }
|
||||
function px2char(px) { return (Math.floor((px - 5)/MDW * 100 + 0.5))/100; }
|
||||
function char2width(chr) { return (Math.round((chr * MDW + 5)/MDW*256))/256; }
|
||||
function px2char_(px) { return (((px - 5)/MDW * 100 + 0.5))/100; }
|
||||
function char2width_(chr) { return (((chr * MDW + 5)/MDW*256))/256; }
|
||||
function cycle_width(collw) { return char2width(px2char(width2px(collw))); }
|
||||
function find_mdw(collw, coll) {
|
||||
if(cycle_width(collw) != collw) {
|
||||
for(MDW=DEF_MDW; MDW>MIN_MDW; --MDW) if(cycle_width(collw) === collw) break;
|
||||
if(MDW === MIN_MDW) for(MDW=DEF_MDW+1; MDW<MAX_MDW; ++MDW) if(cycle_width(collw) === collw) break;
|
||||
if(MDW === MAX_MDW) MDW = DEF_MDW;
|
||||
/* XLSX/XLSB/XLS specify width in units of MDW */
|
||||
function find_mdw_colw(collw) {
|
||||
var delta = Infinity, _MDW = MIN_MDW;
|
||||
for(MDW=MIN_MDW; MDW<MAX_MDW; ++MDW) if(Math.abs(collw - cycle_width(collw)) < delta) { delta = Math.abs(collw - cycle_width(collw)); _MDW = MDW; }
|
||||
MDW = _MDW;
|
||||
}
|
||||
/* XLML specifies width in terms of pixels */
|
||||
function find_mdw_wpx(wpx) {
|
||||
var delta = Infinity, guess = 0, _MDW = MIN_MDW;
|
||||
for(MDW=MIN_MDW; MDW<MAX_MDW; ++MDW) {
|
||||
guess = char2width_(px2char_(wpx))*256;
|
||||
guess = (guess) % 1;
|
||||
if(guess > 0.5) guess--;
|
||||
if(Math.abs(guess) < delta) { delta = Math.abs(guess); _MDW = MDW; }
|
||||
}
|
||||
MDW = _MDW;
|
||||
}
|
||||
|
||||
function process_col(coll/*:ColInfo*/) {
|
||||
if(coll.width) {
|
||||
coll.wpx = width2px(coll.width);
|
||||
coll.wch = px2char(coll.wpx);
|
||||
coll.MDW = MDW;
|
||||
} else if(coll.wpx) {
|
||||
coll.wch = px2char(coll.wpx);
|
||||
coll.width = char2width(coll.wch);
|
||||
coll.MDW = MDW;
|
||||
}
|
||||
if(coll.customWidth) delete coll.customWidth;
|
||||
}
|
||||
|
||||
/* [MS-EXSPXML3] 2.4.54 ST_enmPattern */
|
||||
@ -8552,14 +8592,10 @@ function parse_ws_xml_cols(columns, cols) {
|
||||
for(var coli = 0; coli != cols.length; ++coli) {
|
||||
var coll = parsexmltag(cols[coli], true);
|
||||
var colm=parseInt(coll.min, 10)-1, colM=parseInt(coll.max,10)-1;
|
||||
delete coll.min; delete coll.max;
|
||||
if(!seencol && coll.width) { seencol = true; find_mdw(+coll.width, coll); }
|
||||
if(coll.width) {
|
||||
coll.wpx = width2px(+coll.width);
|
||||
coll.wch = px2char(coll.wpx);
|
||||
coll.MDW = MDW;
|
||||
}
|
||||
while(colm <= colM) columns[colm++] = coll;
|
||||
delete coll.min; delete coll.max; coll.width = +coll.width;
|
||||
if(!seencol && coll.width) { seencol = true; find_mdw_colw(coll.width); }
|
||||
process_col(coll);
|
||||
while(colm <= colM) columns[colm++] = dup(coll);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8570,7 +8606,9 @@ function write_ws_xml_cols(ws, cols)/*:string*/ {
|
||||
var p = ({min:i+1,max:i+1}/*:any*/);
|
||||
/* wch (chars), wpx (pixels) */
|
||||
width = -1;
|
||||
if(col.wpx) width = px2char(col.wpx);
|
||||
if(col.MDW) MDW = col.MDW;
|
||||
if(col.width);
|
||||
else if(col.wpx) width = px2char(col.wpx);
|
||||
else if(col.wch) width = col.wch;
|
||||
if(width > -1) { p.width = char2width(width); p.customWidth= 1; }
|
||||
o[o.length] = (writextag('col', null, p));
|
||||
@ -9074,6 +9112,10 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
|
||||
for(var i = 0; i < wb.Names['!names'].length; ++i) supbooks[0][i+1] = wb.Names[wb.Names['!names'][i]];
|
||||
|
||||
var colinfo = [], rowinfo = [];
|
||||
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
||||
var seencol = false;
|
||||
|
||||
recordhopper(data, function ws_parse(val, Record) {
|
||||
if(end) return;
|
||||
switch(Record.n) {
|
||||
@ -9161,6 +9203,16 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
s[encode_col(C) + rr].f = stringify_formula(val[1], refguess, {r:row.r, c:C}, supbooks, opts);
|
||||
break;
|
||||
|
||||
/* identical to 'ColInfo' in XLS */
|
||||
case 'BrtColInfo': {
|
||||
if(!opts.cellStyles) break;
|
||||
while(val.e >= val.s) {
|
||||
colinfo[val.e--] = { width: val.w/256 };
|
||||
if(!seencol) { seencol = true; find_mdw_colw(val.w/256); }
|
||||
process_col(colinfo[val.e+1]);
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'BrtBeginSheet': break;
|
||||
case 'BrtWsProp': break; // TODO
|
||||
case 'BrtSheetCalcProp': break; // TODO
|
||||
@ -9176,7 +9228,6 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
case 'BrtWsFmtInfoEx14': break; // TODO
|
||||
case 'BrtWsFmtInfo': break; // TODO
|
||||
case 'BrtBeginColInfos': break; // TODO
|
||||
case 'BrtColInfo': break; // TODO
|
||||
case 'BrtEndColInfos': break; // TODO
|
||||
case 'BrtBeginSheetData': break; // TODO
|
||||
case 'BrtEndSheetData': break; // TODO
|
||||
@ -9289,6 +9340,8 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
}
|
||||
}
|
||||
if(mergecells.length > 0) s["!merges"] = mergecells;
|
||||
if(colinfo.length > 0) s["!cols"] = colinfo;
|
||||
if(rowinfo.length > 0) s["!rows"] = rowinfo;
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -10121,8 +10174,9 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
var mergecells = [];
|
||||
var Props = {}, Custprops = {}, pidx = 0, cp = {};
|
||||
var comments = [], comment = {};
|
||||
var cstys = [], csty;
|
||||
var cstys = [], csty, seencol = false;
|
||||
var arrayf = [];
|
||||
var rowinfo = [];
|
||||
xlmlregex.lastIndex = 0;
|
||||
str = str.replace(/<!--([^\u2603]*?)-->/mg,"");
|
||||
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
|
||||
@ -10184,6 +10238,8 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
sheetnames.push(sheetname);
|
||||
if(refguess.s.r <= refguess.e.r && refguess.s.c <= refguess.e.c) cursheet["!ref"] = encode_range(refguess);
|
||||
if(mergecells.length) cursheet["!merges"] = mergecells;
|
||||
if(cstys.length > 0) cursheet["!cols"] = cstys;
|
||||
if(rowinfo.length > 0) cursheet["!rows"] = rowinfo;
|
||||
sheets[sheetname] = cursheet;
|
||||
} else {
|
||||
refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
|
||||
@ -10194,6 +10250,7 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
cursheet = {};
|
||||
mergecells = [];
|
||||
arrayf = [];
|
||||
rowinfo = [];
|
||||
}
|
||||
break;
|
||||
case 'Table':
|
||||
@ -10202,7 +10259,7 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
else {
|
||||
table = xlml_parsexmltag(Rn[0]);
|
||||
state.push([Rn[3], false]);
|
||||
cstys = [];
|
||||
cstys = []; seencol = false;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -10218,8 +10275,14 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
case 'Column':
|
||||
if(state[state.length-1][0] !== 'Table') break;
|
||||
csty = xlml_parsexmltag(Rn[0]);
|
||||
csty.wpx = parseInt(csty.Width, 10);
|
||||
if(!seencol && csty.wpx > 10) {
|
||||
seencol = true; find_mdw_wpx(csty.wpx);
|
||||
for(var _col = 0; _col < cstys.length; ++_col) if(cstys[_col]) process_col(cstys[_col]);
|
||||
}
|
||||
if(seencol) process_col(csty);
|
||||
cstys[(csty.Index-1||cstys.length)] = csty;
|
||||
for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = csty;
|
||||
for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = dup(csty);
|
||||
break;
|
||||
|
||||
case 'NamedRange': break;
|
||||
@ -10891,9 +10954,9 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
if(icv < 64) return palette[icv-8] || XLSIcv[icv];
|
||||
return XLSIcv[icv];
|
||||
};
|
||||
var process_cell_style = function pcs(cell, line/*:any*/) {
|
||||
var process_cell_style = function pcs(cell, line/*:any*/, options) {
|
||||
var xfd = line.XF.data;
|
||||
if(!xfd || !xfd.patternType) return;
|
||||
if(!xfd || !xfd.patternType || !options || !options.cellStyles) return;
|
||||
line.s = ({}/*:any*/);
|
||||
line.s.patternType = xfd.patternType;
|
||||
var t;
|
||||
@ -10901,8 +10964,9 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
if((t = rgb2Hex(get_rgb(xfd.icvBack)))) { line.s.bgColor = {rgb:t}; }
|
||||
};
|
||||
var addcell = function addcell(cell/*:any*/, line/*:any*/, options/*:any*/) {
|
||||
if(file_depth > 1) return;
|
||||
if(!cell_valid) return;
|
||||
if(options.cellStyles && line.XF && line.XF.data) process_cell_style(cell, line);
|
||||
if(options.cellStyles && line.XF && line.XF.data) process_cell_style(cell, line, options);
|
||||
lastcell = cell;
|
||||
last_cell = encode_cell(cell);
|
||||
if(range.s) {
|
||||
@ -10940,11 +11004,15 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
biff: 8, // BIFF version
|
||||
codepage: 0, // CP from CodePage record
|
||||
winlocked: 0, // fLockWn from WinProtect
|
||||
cellStyles: !!options && !!options.cellStyles,
|
||||
WTF: !!options && !!options.wtf
|
||||
}/*:any*/);
|
||||
if(options.password) opts.password = options.password;
|
||||
var mergecells = [];
|
||||
var objects = [];
|
||||
var colinfo = [], rowinfo = [];
|
||||
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
||||
var seencol = false;
|
||||
var supbooks = ([[]]/*:any*/); // 1-indexed, will hold extern names
|
||||
var sbc = 0, sbci = 0, sbcli = 0;
|
||||
supbooks.SheetNames = opts.snames;
|
||||
@ -11051,6 +11119,8 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
}
|
||||
if(mergecells.length > 0) out["!merges"] = mergecells;
|
||||
if(objects.length > 0) out["!objects"] = objects;
|
||||
if(colinfo.length > 0) out["!cols"] = colinfo;
|
||||
if(rowinfo.length > 0) out["!rows"] = rowinfo;
|
||||
}
|
||||
if(cur_sheet === "") Preamble = out; else Sheets[cur_sheet] = out;
|
||||
out = {};
|
||||
@ -11079,6 +11149,9 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
mergecells = [];
|
||||
objects = [];
|
||||
array_formulae = []; opts.arrayf = array_formulae;
|
||||
colinfo = []; rowinfo = [];
|
||||
defwidth = defheight = 0;
|
||||
seencol = false;
|
||||
} break;
|
||||
|
||||
case 'Number': case 'BIFF2NUM': case 'BIFF2INT': {
|
||||
@ -11227,6 +11300,19 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
case 'ClrtClient': break;
|
||||
case 'XFExt': update_xfext(XFs[val.ixfe], val.ext); break;
|
||||
|
||||
case 'DefColWidth': defwidth = val; break;
|
||||
case 'DefaultRowHeight': defheight = val[1]; break; // TODO: flags
|
||||
|
||||
case 'ColInfo': {
|
||||
if(!opts.cellStyles) break;
|
||||
while(val.e >= val.s) {
|
||||
colinfo[val.e--] = { width: val.w/256 };
|
||||
if(!seencol) { seencol = true; find_mdw_colw(val.w/256); }
|
||||
process_col(colinfo[val.e+1]);
|
||||
}
|
||||
} break;
|
||||
case 'Row': break; // TODO
|
||||
|
||||
case 'NameCmt': break;
|
||||
case 'Header': break; // TODO
|
||||
case 'Footer': break; // TODO
|
||||
@ -11234,11 +11320,8 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
case 'VCenter': break; // TODO
|
||||
case 'Pls': break; // TODO
|
||||
case 'Setup': break; // TODO
|
||||
case 'DefColWidth': break; // TODO
|
||||
case 'GCW': break;
|
||||
case 'LHRecord': break;
|
||||
case 'ColInfo': break; // TODO
|
||||
case 'Row': break; // TODO
|
||||
case 'DBCell': break; // TODO
|
||||
case 'EntExU2': break; // TODO
|
||||
case 'SxView': break; // TODO
|
||||
@ -11256,7 +11339,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
case 'Feature11': case 'Feature12': case 'List12': break;
|
||||
case 'Country': country = val; break;
|
||||
case 'RecalcId': break;
|
||||
case 'DefaultRowHeight': case 'DxGCol': break; // TODO: htmlify
|
||||
case 'DxGCol': break; // TODO: htmlify
|
||||
case 'Fbi': case 'Fbi2': case 'GelFrame': break;
|
||||
case 'Font': break; // TODO
|
||||
case 'XFCRC': break; // TODO
|
||||
@ -11576,7 +11659,7 @@ var XLSBRecordEnum = {
|
||||
/*::[*/0x0039/*::]*/: { n:"BrtEndMdxTuple", f:parsenoop },
|
||||
/*::[*/0x003A/*::]*/: { n:"BrtMdxMbrIstr", f:parsenoop },
|
||||
/*::[*/0x003B/*::]*/: { n:"BrtStr", f:parsenoop },
|
||||
/*::[*/0x003C/*::]*/: { n:"BrtColInfo", f:parsenoop },
|
||||
/*::[*/0x003C/*::]*/: { n:"BrtColInfo", f:parse_ColInfo },
|
||||
/*::[*/0x003E/*::]*/: { n:"BrtCellRString", f:parsenoop },
|
||||
/*::[*/0x003F/*::]*/: { n:"BrtCalcChainItem$", f:parse_BrtCalcChainItem$ },
|
||||
/*::[*/0x0040/*::]*/: { n:"BrtDVal", f:parsenoop },
|
||||
|
163
xlsx.js
163
xlsx.js
@ -1352,7 +1352,7 @@ function str2cc(str) {
|
||||
}
|
||||
|
||||
function dup(o) {
|
||||
if(typeof JSON != 'undefined') return JSON.parse(JSON.stringify(o));
|
||||
if(typeof JSON != 'undefined' && !Array.isArray(o)) return JSON.parse(JSON.stringify(o));
|
||||
if(typeof o != 'object' || o == null) return o;
|
||||
var out = {};
|
||||
for(var k in o) if(o.hasOwnProperty(k)) out[k] = dup(o[k]);
|
||||
@ -3734,10 +3734,11 @@ function parse_RecalcId(blob, length) {
|
||||
}
|
||||
|
||||
/* 2.4.87 */
|
||||
function parse_DefaultRowHeight (blob, length) {
|
||||
var f = length == 4 ? blob.read_shift(2) : 0, miyRw;
|
||||
miyRw = blob.read_shift(2); // flags & 0x02 -> hidden, else empty
|
||||
function parse_DefaultRowHeight(blob, length) {
|
||||
var f = blob.read_shift(2);
|
||||
var fl = {Unsynced:f&1,DyZero:(f&2)>>1,ExAsc:(f&4)>>2,ExDsc:(f&8)>>3};
|
||||
/* char is misleading, miyRw and miyRwHidden overlap */
|
||||
var miyRw = blob.read_shift(2);
|
||||
return [fl, miyRw];
|
||||
}
|
||||
|
||||
@ -3823,12 +3824,13 @@ function parse_MulBlank(blob, length) {
|
||||
}
|
||||
|
||||
/* 2.5.20 2.5.249 TODO: interpret values here */
|
||||
function parse_CellStyleXF(blob, length, style) {
|
||||
function parse_CellStyleXF(blob, length, style, opts) {
|
||||
var o = {};
|
||||
var a = blob.read_shift(4), b = blob.read_shift(4);
|
||||
var c = blob.read_shift(4), d = blob.read_shift(2);
|
||||
o.patternType = XLSFillPattern[c >> 26];
|
||||
|
||||
if(!opts.cellStyles) return o;
|
||||
o.alc = a & 0x07;
|
||||
o.fWrap = (a >> 3) & 0x01;
|
||||
o.alcV = (a >> 4) & 0x07;
|
||||
@ -3862,16 +3864,16 @@ function parse_CellStyleXF(blob, length, style) {
|
||||
o.fsxButton = (d >> 14) & 0x01;
|
||||
return o;
|
||||
}
|
||||
function parse_CellXF(blob, length) {return parse_CellStyleXF(blob,length,0);}
|
||||
function parse_StyleXF(blob, length) {return parse_CellStyleXF(blob,length,1);}
|
||||
function parse_CellXF(blob, length, opts) {return parse_CellStyleXF(blob,length,0, opts);}
|
||||
function parse_StyleXF(blob, length, opts) {return parse_CellStyleXF(blob,length,1, opts);}
|
||||
|
||||
/* 2.4.353 TODO: actually do this right */
|
||||
function parse_XF(blob, length) {
|
||||
function parse_XF(blob, length, opts) {
|
||||
var o = {};
|
||||
o.ifnt = blob.read_shift(2); o.ifmt = blob.read_shift(2); o.flags = blob.read_shift(2);
|
||||
o.fStyle = (o.flags >> 2) & 0x01;
|
||||
length -= 6;
|
||||
o.data = parse_CellStyleXF(blob, length, o.fStyle);
|
||||
o.data = parse_CellStyleXF(blob, length, o.fStyle, opts);
|
||||
return o;
|
||||
}
|
||||
|
||||
@ -4121,12 +4123,24 @@ function parse_XFCRC(blob, length) {
|
||||
return o;
|
||||
}
|
||||
|
||||
/* 2.4.53 TODO: parse flags */
|
||||
/* [MS-XLSB] 2.4.323 TODO: parse flags */
|
||||
function parse_ColInfo(blob, length, opts) {
|
||||
if(!opts.cellStyles) return parsenoop(blob, length);
|
||||
var w = opts && opts.biff >= 12 ? 4 : 2;
|
||||
var colFirst = blob.read_shift(w);
|
||||
var colLast = blob.read_shift(w);
|
||||
var coldx = blob.read_shift(w);
|
||||
var ixfe = blob.read_shift(w);
|
||||
var flags = blob.read_shift(2);
|
||||
if(w == 2) blob.l += 2;
|
||||
return {s:colFirst, e:colLast, w:coldx, ixfe:ixfe, flags:flags};
|
||||
}
|
||||
|
||||
|
||||
var parse_Style = parsenoop;
|
||||
var parse_StyleExt = parsenoop;
|
||||
|
||||
var parse_ColInfo = parsenoop;
|
||||
|
||||
var parse_Window2 = parsenoop;
|
||||
|
||||
|
||||
@ -4987,17 +5001,43 @@ function rgb_tint(hex, tint) {
|
||||
}
|
||||
|
||||
/* 18.3.1.13 width calculations */
|
||||
/* [MS-OI29500] 2.1.595 Column Width & Formatting */
|
||||
var DEF_MDW = 7, MAX_MDW = 15, MIN_MDW = 1, MDW = DEF_MDW;
|
||||
function width2px(width) { return (( width + ((128/MDW)|0)/256 )* MDW )|0; }
|
||||
function px2char(px) { return (((px - 5)/MDW * 100 + 0.5)|0)/100; }
|
||||
function char2width(chr) { return (((chr * MDW + 5)/MDW*256)|0)/256; }
|
||||
function width2px(width) { return Math.floor(( width + (Math.round(128/MDW))/256 )* MDW ); }
|
||||
function px2char(px) { return (Math.floor((px - 5)/MDW * 100 + 0.5))/100; }
|
||||
function char2width(chr) { return (Math.round((chr * MDW + 5)/MDW*256))/256; }
|
||||
function px2char_(px) { return (((px - 5)/MDW * 100 + 0.5))/100; }
|
||||
function char2width_(chr) { return (((chr * MDW + 5)/MDW*256))/256; }
|
||||
function cycle_width(collw) { return char2width(px2char(width2px(collw))); }
|
||||
function find_mdw(collw, coll) {
|
||||
if(cycle_width(collw) != collw) {
|
||||
for(MDW=DEF_MDW; MDW>MIN_MDW; --MDW) if(cycle_width(collw) === collw) break;
|
||||
if(MDW === MIN_MDW) for(MDW=DEF_MDW+1; MDW<MAX_MDW; ++MDW) if(cycle_width(collw) === collw) break;
|
||||
if(MDW === MAX_MDW) MDW = DEF_MDW;
|
||||
/* XLSX/XLSB/XLS specify width in units of MDW */
|
||||
function find_mdw_colw(collw) {
|
||||
var delta = Infinity, _MDW = MIN_MDW;
|
||||
for(MDW=MIN_MDW; MDW<MAX_MDW; ++MDW) if(Math.abs(collw - cycle_width(collw)) < delta) { delta = Math.abs(collw - cycle_width(collw)); _MDW = MDW; }
|
||||
MDW = _MDW;
|
||||
}
|
||||
/* XLML specifies width in terms of pixels */
|
||||
function find_mdw_wpx(wpx) {
|
||||
var delta = Infinity, guess = 0, _MDW = MIN_MDW;
|
||||
for(MDW=MIN_MDW; MDW<MAX_MDW; ++MDW) {
|
||||
guess = char2width_(px2char_(wpx))*256;
|
||||
guess = (guess) % 1;
|
||||
if(guess > 0.5) guess--;
|
||||
if(Math.abs(guess) < delta) { delta = Math.abs(guess); _MDW = MDW; }
|
||||
}
|
||||
MDW = _MDW;
|
||||
}
|
||||
|
||||
function process_col(coll) {
|
||||
if(coll.width) {
|
||||
coll.wpx = width2px(coll.width);
|
||||
coll.wch = px2char(coll.wpx);
|
||||
coll.MDW = MDW;
|
||||
} else if(coll.wpx) {
|
||||
coll.wch = px2char(coll.wpx);
|
||||
coll.width = char2width(coll.wch);
|
||||
coll.MDW = MDW;
|
||||
}
|
||||
if(coll.customWidth) delete coll.customWidth;
|
||||
}
|
||||
|
||||
/* [MS-EXSPXML3] 2.4.54 ST_enmPattern */
|
||||
@ -8499,14 +8539,10 @@ function parse_ws_xml_cols(columns, cols) {
|
||||
for(var coli = 0; coli != cols.length; ++coli) {
|
||||
var coll = parsexmltag(cols[coli], true);
|
||||
var colm=parseInt(coll.min, 10)-1, colM=parseInt(coll.max,10)-1;
|
||||
delete coll.min; delete coll.max;
|
||||
if(!seencol && coll.width) { seencol = true; find_mdw(+coll.width, coll); }
|
||||
if(coll.width) {
|
||||
coll.wpx = width2px(+coll.width);
|
||||
coll.wch = px2char(coll.wpx);
|
||||
coll.MDW = MDW;
|
||||
}
|
||||
while(colm <= colM) columns[colm++] = coll;
|
||||
delete coll.min; delete coll.max; coll.width = +coll.width;
|
||||
if(!seencol && coll.width) { seencol = true; find_mdw_colw(coll.width); }
|
||||
process_col(coll);
|
||||
while(colm <= colM) columns[colm++] = dup(coll);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8517,7 +8553,9 @@ function write_ws_xml_cols(ws, cols) {
|
||||
var p = ({min:i+1,max:i+1});
|
||||
/* wch (chars), wpx (pixels) */
|
||||
width = -1;
|
||||
if(col.wpx) width = px2char(col.wpx);
|
||||
if(col.MDW) MDW = col.MDW;
|
||||
if(col.width);
|
||||
else if(col.wpx) width = px2char(col.wpx);
|
||||
else if(col.wch) width = col.wch;
|
||||
if(width > -1) { p.width = char2width(width); p.customWidth= 1; }
|
||||
o[o.length] = (writextag('col', null, p));
|
||||
@ -9021,6 +9059,10 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles) {
|
||||
|
||||
for(var i = 0; i < wb.Names['!names'].length; ++i) supbooks[0][i+1] = wb.Names[wb.Names['!names'][i]];
|
||||
|
||||
var colinfo = [], rowinfo = [];
|
||||
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
||||
var seencol = false;
|
||||
|
||||
recordhopper(data, function ws_parse(val, Record) {
|
||||
if(end) return;
|
||||
switch(Record.n) {
|
||||
@ -9108,6 +9150,16 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles) {
|
||||
s[encode_col(C) + rr].f = stringify_formula(val[1], refguess, {r:row.r, c:C}, supbooks, opts);
|
||||
break;
|
||||
|
||||
/* identical to 'ColInfo' in XLS */
|
||||
case 'BrtColInfo': {
|
||||
if(!opts.cellStyles) break;
|
||||
while(val.e >= val.s) {
|
||||
colinfo[val.e--] = { width: val.w/256 };
|
||||
if(!seencol) { seencol = true; find_mdw_colw(val.w/256); }
|
||||
process_col(colinfo[val.e+1]);
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'BrtBeginSheet': break;
|
||||
case 'BrtWsProp': break; // TODO
|
||||
case 'BrtSheetCalcProp': break; // TODO
|
||||
@ -9123,7 +9175,6 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles) {
|
||||
case 'BrtWsFmtInfoEx14': break; // TODO
|
||||
case 'BrtWsFmtInfo': break; // TODO
|
||||
case 'BrtBeginColInfos': break; // TODO
|
||||
case 'BrtColInfo': break; // TODO
|
||||
case 'BrtEndColInfos': break; // TODO
|
||||
case 'BrtBeginSheetData': break; // TODO
|
||||
case 'BrtEndSheetData': break; // TODO
|
||||
@ -9236,6 +9287,8 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles) {
|
||||
}
|
||||
}
|
||||
if(mergecells.length > 0) s["!merges"] = mergecells;
|
||||
if(colinfo.length > 0) s["!cols"] = colinfo;
|
||||
if(rowinfo.length > 0) s["!rows"] = rowinfo;
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -10066,8 +10119,9 @@ function parse_xlml_xml(d, opts) {
|
||||
var mergecells = [];
|
||||
var Props = {}, Custprops = {}, pidx = 0, cp = {};
|
||||
var comments = [], comment = {};
|
||||
var cstys = [], csty;
|
||||
var cstys = [], csty, seencol = false;
|
||||
var arrayf = [];
|
||||
var rowinfo = [];
|
||||
xlmlregex.lastIndex = 0;
|
||||
str = str.replace(/<!--([^\u2603]*?)-->/mg,"");
|
||||
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
|
||||
@ -10128,6 +10182,8 @@ for(var cma = c; cma <= cc; ++cma) {
|
||||
sheetnames.push(sheetname);
|
||||
if(refguess.s.r <= refguess.e.r && refguess.s.c <= refguess.e.c) cursheet["!ref"] = encode_range(refguess);
|
||||
if(mergecells.length) cursheet["!merges"] = mergecells;
|
||||
if(cstys.length > 0) cursheet["!cols"] = cstys;
|
||||
if(rowinfo.length > 0) cursheet["!rows"] = rowinfo;
|
||||
sheets[sheetname] = cursheet;
|
||||
} else {
|
||||
refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
|
||||
@ -10138,6 +10194,7 @@ for(var cma = c; cma <= cc; ++cma) {
|
||||
cursheet = {};
|
||||
mergecells = [];
|
||||
arrayf = [];
|
||||
rowinfo = [];
|
||||
}
|
||||
break;
|
||||
case 'Table':
|
||||
@ -10146,7 +10203,7 @@ for(var cma = c; cma <= cc; ++cma) {
|
||||
else {
|
||||
table = xlml_parsexmltag(Rn[0]);
|
||||
state.push([Rn[3], false]);
|
||||
cstys = [];
|
||||
cstys = []; seencol = false;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -10162,8 +10219,14 @@ for(var cma = c; cma <= cc; ++cma) {
|
||||
case 'Column':
|
||||
if(state[state.length-1][0] !== 'Table') break;
|
||||
csty = xlml_parsexmltag(Rn[0]);
|
||||
csty.wpx = parseInt(csty.Width, 10);
|
||||
if(!seencol && csty.wpx > 10) {
|
||||
seencol = true; find_mdw_wpx(csty.wpx);
|
||||
for(var _col = 0; _col < cstys.length; ++_col) if(cstys[_col]) process_col(cstys[_col]);
|
||||
}
|
||||
if(seencol) process_col(csty);
|
||||
cstys[(csty.Index-1||cstys.length)] = csty;
|
||||
for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = csty;
|
||||
for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = dup(csty);
|
||||
break;
|
||||
|
||||
case 'NamedRange': break;
|
||||
@ -10834,9 +10897,9 @@ function parse_workbook(blob, options) {
|
||||
if(icv < 64) return palette[icv-8] || XLSIcv[icv];
|
||||
return XLSIcv[icv];
|
||||
};
|
||||
var process_cell_style = function pcs(cell, line) {
|
||||
var process_cell_style = function pcs(cell, line, options) {
|
||||
var xfd = line.XF.data;
|
||||
if(!xfd || !xfd.patternType) return;
|
||||
if(!xfd || !xfd.patternType || !options || !options.cellStyles) return;
|
||||
line.s = ({});
|
||||
line.s.patternType = xfd.patternType;
|
||||
var t;
|
||||
@ -10844,8 +10907,9 @@ function parse_workbook(blob, options) {
|
||||
if((t = rgb2Hex(get_rgb(xfd.icvBack)))) { line.s.bgColor = {rgb:t}; }
|
||||
};
|
||||
var addcell = function addcell(cell, line, options) {
|
||||
if(file_depth > 1) return;
|
||||
if(!cell_valid) return;
|
||||
if(options.cellStyles && line.XF && line.XF.data) process_cell_style(cell, line);
|
||||
if(options.cellStyles && line.XF && line.XF.data) process_cell_style(cell, line, options);
|
||||
lastcell = cell;
|
||||
last_cell = encode_cell(cell);
|
||||
if(range.s) {
|
||||
@ -10883,11 +10947,15 @@ function parse_workbook(blob, options) {
|
||||
biff: 8, // BIFF version
|
||||
codepage: 0, // CP from CodePage record
|
||||
winlocked: 0, // fLockWn from WinProtect
|
||||
cellStyles: !!options && !!options.cellStyles,
|
||||
WTF: !!options && !!options.wtf
|
||||
});
|
||||
if(options.password) opts.password = options.password;
|
||||
var mergecells = [];
|
||||
var objects = [];
|
||||
var colinfo = [], rowinfo = [];
|
||||
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
||||
var seencol = false;
|
||||
var supbooks = ([[]]); // 1-indexed, will hold extern names
|
||||
var sbc = 0, sbci = 0, sbcli = 0;
|
||||
supbooks.SheetNames = opts.snames;
|
||||
@ -10994,6 +11062,8 @@ function parse_workbook(blob, options) {
|
||||
}
|
||||
if(mergecells.length > 0) out["!merges"] = mergecells;
|
||||
if(objects.length > 0) out["!objects"] = objects;
|
||||
if(colinfo.length > 0) out["!cols"] = colinfo;
|
||||
if(rowinfo.length > 0) out["!rows"] = rowinfo;
|
||||
}
|
||||
if(cur_sheet === "") Preamble = out; else Sheets[cur_sheet] = out;
|
||||
out = {};
|
||||
@ -11022,6 +11092,9 @@ function parse_workbook(blob, options) {
|
||||
mergecells = [];
|
||||
objects = [];
|
||||
array_formulae = []; opts.arrayf = array_formulae;
|
||||
colinfo = []; rowinfo = [];
|
||||
defwidth = defheight = 0;
|
||||
seencol = false;
|
||||
} break;
|
||||
|
||||
case 'Number': case 'BIFF2NUM': case 'BIFF2INT': {
|
||||
@ -11170,6 +11243,19 @@ function parse_workbook(blob, options) {
|
||||
case 'ClrtClient': break;
|
||||
case 'XFExt': update_xfext(XFs[val.ixfe], val.ext); break;
|
||||
|
||||
case 'DefColWidth': defwidth = val; break;
|
||||
case 'DefaultRowHeight': defheight = val[1]; break; // TODO: flags
|
||||
|
||||
case 'ColInfo': {
|
||||
if(!opts.cellStyles) break;
|
||||
while(val.e >= val.s) {
|
||||
colinfo[val.e--] = { width: val.w/256 };
|
||||
if(!seencol) { seencol = true; find_mdw_colw(val.w/256); }
|
||||
process_col(colinfo[val.e+1]);
|
||||
}
|
||||
} break;
|
||||
case 'Row': break; // TODO
|
||||
|
||||
case 'NameCmt': break;
|
||||
case 'Header': break; // TODO
|
||||
case 'Footer': break; // TODO
|
||||
@ -11177,11 +11263,8 @@ function parse_workbook(blob, options) {
|
||||
case 'VCenter': break; // TODO
|
||||
case 'Pls': break; // TODO
|
||||
case 'Setup': break; // TODO
|
||||
case 'DefColWidth': break; // TODO
|
||||
case 'GCW': break;
|
||||
case 'LHRecord': break;
|
||||
case 'ColInfo': break; // TODO
|
||||
case 'Row': break; // TODO
|
||||
case 'DBCell': break; // TODO
|
||||
case 'EntExU2': break; // TODO
|
||||
case 'SxView': break; // TODO
|
||||
@ -11199,7 +11282,7 @@ function parse_workbook(blob, options) {
|
||||
case 'Feature11': case 'Feature12': case 'List12': break;
|
||||
case 'Country': country = val; break;
|
||||
case 'RecalcId': break;
|
||||
case 'DefaultRowHeight': case 'DxGCol': break; // TODO: htmlify
|
||||
case 'DxGCol': break; // TODO: htmlify
|
||||
case 'Fbi': case 'Fbi2': case 'GelFrame': break;
|
||||
case 'Font': break; // TODO
|
||||
case 'XFCRC': break; // TODO
|
||||
@ -11519,7 +11602,7 @@ var XLSBRecordEnum = {
|
||||
0x0039: { n:"BrtEndMdxTuple", f:parsenoop },
|
||||
0x003A: { n:"BrtMdxMbrIstr", f:parsenoop },
|
||||
0x003B: { n:"BrtStr", f:parsenoop },
|
||||
0x003C: { n:"BrtColInfo", f:parsenoop },
|
||||
0x003C: { n:"BrtColInfo", f:parse_ColInfo },
|
||||
0x003E: { n:"BrtCellRString", f:parsenoop },
|
||||
0x003F: { n:"BrtCalcChainItem$", f:parse_BrtCalcChainItem$ },
|
||||
0x0040: { n:"BrtDVal", f:parsenoop },
|
||||
|
Loading…
Reference in New Issue
Block a user