forked from sheetjs/sheetjs
Page Margins
- XLSB read/write page margins - XLSX/XLS/XLML read page margins - separated encrypted XLSX/XLSB document logic from XLS
This commit is contained in:
parent
0189bc23ca
commit
1587688aea
22
README.md
22
README.md
@ -563,6 +563,28 @@ Special sheet keys (accessible as `sheet[key]`, each starting with `!`):
|
||||
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']`
|
||||
|
||||
- `sheet['!margins']`: Object representing the page margins. The default values
|
||||
follow Excel's "normal" preset. Excel also has a "wide" and a "narrow" preset
|
||||
but they are stored as raw measurements. The main properties are listed below:
|
||||
|
||||
| key | description | "normal" | "wide" | "narrow" |
|
||||
|----------|------------------------|:---------|:-------|:-------- |
|
||||
| `left` | left margin (inches) | `0.7` | `1.0` | `0.25` |
|
||||
| `right` | right margin (inches) | `0.7` | `1.0` | `0.25` |
|
||||
| `top` | top margin (inches) | `0.75` | `1.0` | `0.75` |
|
||||
| `bottom` | bottom margin (inches) | `0.75` | `1.0` | `0.75` |
|
||||
| `header` | header margin (inches) | `0.3` | `0.5` | `0.3` |
|
||||
| `footer` | footer margin (inches) | `0.3` | `0.5` | `0.3` |
|
||||
|
||||
```js
|
||||
/* Set worksheet sheet to "normal" */
|
||||
sheet["!margins"] = { left:0.7, right:0.7, top:0.75, bottom:0.75, header:0.3, footer:0.3 }
|
||||
/* Set worksheet sheet to "wide" */
|
||||
sheet["!margins"] = { left:1.0, right:1.0, top:1.0, bottom:1.0, header:0.5, footer:0.5 }
|
||||
/* Set worksheet sheet to "narrow" */
|
||||
sheet["!margins"] = { left:0.25, right:0.25, top:0.75, bottom:0.75, header:0.3, footer:0.3 }
|
||||
```
|
||||
|
||||
#### Worksheet Object
|
||||
|
||||
In addition to the base sheet keys, worksheets also add:
|
||||
|
@ -36,6 +36,10 @@ var __lpstr, ___lpstr;
|
||||
__lpstr = ___lpstr = function lpstr_(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len-1) : "";};
|
||||
var __lpwstr, ___lpwstr;
|
||||
__lpwstr = ___lpwstr = function lpwstr_(b,i) { var len = 2*__readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len-1) : "";};
|
||||
var __lpp4, ___lpp4;
|
||||
__lpp4 = ___lpp4 = function lpp4_(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? __utf16le(b, i+4,i+4+len) : "";};
|
||||
var __8lpp4, ___8lpp4;
|
||||
__8lpp4 = ___8lpp4 = function lpp4_8(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len) : "";};
|
||||
var __double, ___double;
|
||||
__double = ___double = function(b, idx) { return read_double_le(b, idx);};
|
||||
|
||||
@ -45,6 +49,8 @@ if(has_buf/*:: && typeof Buffer != 'undefined'*/) {
|
||||
__hexlify = function(b,s,l) { return Buffer.isBuffer(b) ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); };
|
||||
__lpstr = function lpstr_b(b,i) { if(!Buffer.isBuffer(b)) return ___lpstr(b, i); var len = b.readUInt32LE(i); return len > 0 ? b.toString('utf8',i+4,i+4+len-1) : "";};
|
||||
__lpwstr = function lpwstr_b(b,i) { if(!Buffer.isBuffer(b)) return ___lpwstr(b, i); var len = 2*b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len-1);};
|
||||
__lpp4 = function lpp4_b(b,i) { if(!Buffer.isBuffer(b)) return ___lpp4(b, i); var len = b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len);};
|
||||
__8lpp4 = function lpp4_8b(b,i) { if(!Buffer.isBuffer(b)) return ___8lpp4(b, i); var len = b.readUInt32LE(i); return b.toString('utf8',i+4,i+4+len);};
|
||||
__utf8 = function utf8_b(b, s,e) { return b.toString('utf8',s,e); };
|
||||
__toBuffer = function(bufs) { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat(bufs[0]) : ___toBuffer(bufs);};
|
||||
bconcat = function(bufs) { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(bufs) : [].concat.apply([], bufs); };
|
||||
@ -58,6 +64,8 @@ if(typeof cptable !== 'undefined') {
|
||||
__utf8 = function(b,s,e) { return cptable.utils.decode(65001, b.slice(s,e)); };
|
||||
__lpstr = function(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(current_codepage, b.slice(i+4, i+4+len-1)) : "";};
|
||||
__lpwstr = function(b,i) { var len = 2*__readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(1200, b.slice(i+4,i+4+len-1)) : "";};
|
||||
__lpp4 = function(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(1200, b.slice(i+4,i+4+len)) : "";};
|
||||
__8lpp4 = function(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(65001, b.slice(i+4,i+4+len)) : "";};
|
||||
}
|
||||
|
||||
var __readUInt8 = function(b, idx) { return b[idx]; };
|
||||
@ -91,6 +99,10 @@ function ReadShift(size/*:number*/, t/*:?string*/) {
|
||||
case 'lpstr': o = __lpstr(this, this.l); size = 5 + o.length; break;
|
||||
/* [MS-OLEDS] 2.1.5 LengthPrefixedUnicodeString */
|
||||
case 'lpwstr': o = __lpwstr(this, this.l); size = 5 + o.length; if(o[o.length-1] == '\u0000') size += 2; break;
|
||||
/* [MS-OFFCRYPTO] 2.1.2 Length-Prefixed Padded Unicode String (UNICODE-LP-P4) */
|
||||
case 'lpp4': size = 4 + __readUInt32LE(this, this.l); o = __lpp4(this, this.l); if(size & 0x02) size += 2; break;
|
||||
/* [MS-OFFCRYPTO] 2.1.3 Length-Prefixed UTF-8 String (UTF-8-LP-P4) */
|
||||
case '8lpp4': size = 4 + __readUInt32LE(this, this.l); o = __8lpp4(this, this.l); if(size & 0x03) size += 4 - (size & 0x03); break;
|
||||
|
||||
case 'cstr': size = 0; o = "";
|
||||
while((w=__readUInt8(this, this.l + size++))!==0) oo.push(_getchar(w));
|
||||
|
@ -655,6 +655,16 @@ function parse_ColInfo(blob, length, opts) {
|
||||
return {s:colFirst, e:colLast, w:coldx, ixfe:ixfe, flags:flags};
|
||||
}
|
||||
|
||||
/* 2.4.257 */
|
||||
function parse_Setup(blob, length, opts) {
|
||||
var o = {};
|
||||
blob.l += 16;
|
||||
o.header = parse_Xnum(blob, 8);
|
||||
o.footer = parse_Xnum(blob, 8);
|
||||
blob.l += 2;
|
||||
return o;
|
||||
}
|
||||
|
||||
/* 2.4.261 */
|
||||
function parse_ShtProps(blob, length, opts) {
|
||||
var def = {area:false};
|
||||
@ -664,7 +674,6 @@ function parse_ShtProps(blob, length, opts) {
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
var parse_Style = parsenoop;
|
||||
var parse_StyleExt = parsenoop;
|
||||
|
||||
@ -747,7 +756,6 @@ var parse_FnGroupName = parsenoop;
|
||||
var parse_FilterMode = parsenoop;
|
||||
var parse_AutoFilterInfo = parsenoop;
|
||||
var parse_AutoFilter = parsenoop;
|
||||
var parse_Setup = parsenoop;
|
||||
var parse_ScenMan = parsenoop;
|
||||
var parse_SCENARIO = parsenoop;
|
||||
var parse_SxView = parsenoop;
|
||||
|
@ -6,38 +6,154 @@ function _JS2ANSI(str/*:string*/)/*:Array<number>*/ {
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.4 Version */
|
||||
function parse_Version(blob, length/*:number*/) {
|
||||
function parse_CRYPTOVersion(blob, length/*:number*/) {
|
||||
var o = {};
|
||||
o.Major = blob.read_shift(2);
|
||||
o.Minor = blob.read_shift(2);
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.5 DataSpaceVersionInfo */
|
||||
function parse_DataSpaceVersionInfo(blob, length) {
|
||||
var o = {};
|
||||
o.id = blob.read_shift(0, 'lpp4');
|
||||
o.R = parse_CRYPTOVersion(blob, 4);
|
||||
o.U = parse_CRYPTOVersion(blob, 4);
|
||||
o.W = parse_CRYPTOVersion(blob, 4);
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.6.1 DataSpaceMapEntry Structure */
|
||||
function parse_DataSpaceMapEntry(blob) {
|
||||
var len = blob.read_shift(4);
|
||||
var end = blob.l + len - 4;
|
||||
var o = {};
|
||||
var cnt = blob.read_shift(4);
|
||||
var comps = [];
|
||||
while(cnt-- > 0) {
|
||||
/* [MS-OFFCRYPTO] 2.1.6.2 DataSpaceReferenceComponent Structure */
|
||||
var rc = {};
|
||||
rc.t = blob.read_shift(4);
|
||||
rc.v = blob.read_shift(0, 'lpp4');
|
||||
comps.push(rc);
|
||||
}
|
||||
o.name = blob.read_shift(0, 'lpp4');
|
||||
o.comps = comps;
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.6 DataSpaceMap */
|
||||
function parse_DataSpaceMap(blob, length) {
|
||||
var o = [];
|
||||
blob.l += 4; // must be 0x8
|
||||
var cnt = blob.read_shift(4);
|
||||
while(cnt-- > 0) o.push(parse_DataSpaceMapEntry(blob));
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.7 DataSpaceDefinition */
|
||||
function parse_DataSpaceDefinition(blob, length) {
|
||||
var o = [];
|
||||
blob.l += 4; // must be 0x8
|
||||
var cnt = blob.read_shift(4);
|
||||
while(cnt-- > 0) o.push(blob.read_shift(0, 'lpp4'));
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.8 DataSpaceDefinition */
|
||||
function parse_TransformInfoHeader(blob, length) {
|
||||
var o = {};
|
||||
var len = blob.read_shift(4);
|
||||
var tgt = blob.l + len - 4;
|
||||
blob.l += 4; // must be 0x1
|
||||
o.id = blob.read_shift(0, 'lpp4');
|
||||
// tgt == len
|
||||
o.name = blob.read_shift(0, 'lpp4');
|
||||
o.R = parse_CRYPTOVersion(blob, 4);
|
||||
o.U = parse_CRYPTOVersion(blob, 4);
|
||||
o.W = parse_CRYPTOVersion(blob, 4);
|
||||
return o;
|
||||
}
|
||||
|
||||
function parse_Primary(blob, length) {
|
||||
/* [MS-OFFCRYPTO] 2.2.6 IRMDSTransformInfo */
|
||||
var hdr = parse_TransformInfoHeader(blob);
|
||||
/* [MS-OFFCRYPTO] 2.1.9 EncryptionTransformInfo */
|
||||
hdr.ename = blob.read_shift(0, '8lpp4');
|
||||
hdr.blksz = blob.read_shift(4);
|
||||
hdr.cmode = blob.read_shift(4);
|
||||
if(blob.read_shift(4) != 0x04) throw new Error("Bad !Primary record");
|
||||
return hdr;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.3.2 Encryption Header */
|
||||
function parse_EncryptionHeader(blob, length/*:number*/) {
|
||||
var tgt = blob.l + length;
|
||||
var o = {};
|
||||
o.Flags = blob.read_shift(4);
|
||||
|
||||
// Check if SizeExtra is 0x00000000
|
||||
var tmp = blob.read_shift(4);
|
||||
if(tmp !== 0) throw 'Unrecognized SizeExtra: ' + tmp;
|
||||
|
||||
o.Flags = (blob.read_shift(4) & 0x3F);
|
||||
blob.l += 4;
|
||||
o.AlgID = blob.read_shift(4);
|
||||
var valid = false;
|
||||
switch(o.AlgID) {
|
||||
case 0: case 0x6801: case 0x660E: case 0x660F: case 0x6610: break;
|
||||
case 0x660E: case 0x660F: case 0x6610: valid = (o.Flags == 0x24); break;
|
||||
case 0x6801: valid = (o.Flags == 0x04); break;
|
||||
case 0: valid = (o.Flags == 0x10 || o.Flags == 0x04 || o.Flags == 0x24); break;
|
||||
default: throw 'Unrecognized encryption algorithm: ' + o.AlgID;
|
||||
}
|
||||
parsenoop(blob, length-12);
|
||||
if(!valid) throw new Error("Encryption Flags/AlgID mismatch");
|
||||
o.AlgIDHash = blob.read_shift(4);
|
||||
o.KeySize = blob.read_shift(4);
|
||||
o.ProviderType = blob.read_shift(4);
|
||||
blob.l += 8;
|
||||
o.CSPName = blob.read_shift((tgt-blob.l)>>1, 'utf16le').slice(0,-1);
|
||||
blob.l = tgt;
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.3.3 Encryption Verifier */
|
||||
function parse_EncryptionVerifier(blob, length/*:number*/) {
|
||||
return parsenoop(blob, length);
|
||||
var o = {};
|
||||
blob.l += 4; // SaltSize must be 0x10
|
||||
o.Salt = blob.slice(blob.l, blob.l+16); blob.l += 16;
|
||||
o.Verifier = blob.slice(blob.l, blob.l+16); blob.l += 16;
|
||||
var sz = blob.read_shift(4);
|
||||
o.VerifierHash = blob.slice(blob.l, blob.l + sz); blob.l += sz;
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.3.4.* EncryptionInfo Stream */
|
||||
function parse_EncryptionInfo(blob, length) {
|
||||
var vers = parse_CRYPTOVersion(blob);
|
||||
switch(vers.Minor) {
|
||||
case 0x02: return parse_EncInfoStd(blob, vers);
|
||||
case 0x03: return parse_EncInfoExt(blob, vers);
|
||||
case 0x04: return parse_EncInfoAgl(blob, vers);
|
||||
}
|
||||
throw new Error("ECMA-376 Encryped file unrecognized Version: " + vers.Minor);
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.3.4.5 EncryptionInfo Stream (Standard Encryption) */
|
||||
function parse_EncInfoStd(blob, vers) {
|
||||
var flags = blob.read_shift(4);
|
||||
if((flags & 0x3F) != 0x24) throw new Error("EncryptionInfo mismatch");
|
||||
var sz = blob.read_shift(4);
|
||||
var tgt = blob.l + sz;
|
||||
var hdr = parse_EncryptionHeader(blob, sz);
|
||||
var verifier = parse_EncryptionVerifier(blob, blob.length - blob.l);
|
||||
return { t:"Std", h:hdr, v:verifier };
|
||||
}
|
||||
/* [MS-OFFCRYPTO] 2.3.4.6 EncryptionInfo Stream (Extensible Encryption) */
|
||||
function parse_EncInfoExt(blob, vers) { throw new Error("File is password-protected: ECMA-376 Extensible"); }
|
||||
/* [MS-OFFCRYPTO] 2.3.4.10 EncryptionInfo Stream (Agile Encryption) */
|
||||
function parse_EncInfoAgl(blob, vers) { throw new Error("File is password-protected: ECMA-376 Agile"); }
|
||||
|
||||
|
||||
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.3.5.1 RC4 CryptoAPI Encryption Header */
|
||||
function parse_RC4CryptoHeader(blob, length/*:number*/) {
|
||||
var o = {};
|
||||
var vers = o.EncryptionVersionInfo = parse_Version(blob, 4); length -= 4;
|
||||
var vers = o.EncryptionVersionInfo = parse_CRYPTOVersion(blob, 4); length -= 4;
|
||||
if(vers.Minor != 2) throw 'unrecognized minor version code: ' + vers.Minor;
|
||||
if(vers.Major > 4 || vers.Major < 2) throw 'unrecognized major version code: ' + vers.Major;
|
||||
o.Flags = blob.read_shift(4); length -= 4;
|
||||
@ -49,7 +165,7 @@ function parse_RC4CryptoHeader(blob, length/*:number*/) {
|
||||
/* [MS-OFFCRYPTO] 2.3.6.1 RC4 Encryption Header */
|
||||
function parse_RC4Header(blob, length/*:number*/) {
|
||||
var o = {};
|
||||
var vers = o.EncryptionVersionInfo = parse_Version(blob, 4); length -= 4;
|
||||
var vers = o.EncryptionVersionInfo = parse_CRYPTOVersion(blob, 4); length -= 4;
|
||||
if(vers.Major != 1 || vers.Minor != 1) throw 'unrecognized version code ' + vers.Major + ' : ' + vers.Minor;
|
||||
o.Salt = blob.read_shift(16);
|
||||
o.EncryptedVerifier = blob.read_shift(16);
|
||||
|
@ -24,6 +24,18 @@ function col_obj_w(C/*:number*/, col) {
|
||||
return p;
|
||||
}
|
||||
|
||||
function default_margins(margins, mode) {
|
||||
if(!margins) return;
|
||||
var defs = [0.7, 0.7, 0.75, 0.75, 0.3, 0.3];
|
||||
if(mode == 'xlml') defs = [1, 1, 1, 1, 0.5, 0.5];
|
||||
if(margins.left == null) margins.left = defs[0];
|
||||
if(margins.right == null) margins.right = defs[1];
|
||||
if(margins.top == null) margins.top = defs[2];
|
||||
if(margins.bottom == null) margins.bottom = defs[3];
|
||||
if(margins.header == null) margins.header = defs[4];
|
||||
if(margins.footer == null) margins.footer = defs[5];
|
||||
}
|
||||
|
||||
function get_cell_style(styles, cell, opts) {
|
||||
var z = opts.revssf[cell.z != null ? cell.z : "General"];
|
||||
for(var i = 0, len = styles.length; i != len; ++i) if(styles[i].numFmtId === z) return i;
|
||||
|
@ -8,6 +8,7 @@ var hlinkregex = /<(?:\w:)?hyperlink [^>]*>/mg;
|
||||
var dimregex = /"(\w*:\w*)"/;
|
||||
var colregex = /<(?:\w:)?col[^>]*[\/]?>/g;
|
||||
var afregex = /<(?:\w:)?autoFilter[^>]*([\/]|>([^\u2603]*)<\/(?:\w:)?autoFilter)>/g;
|
||||
var marginregex= /<(?:\w:)?pageMargins[^>]*\/>/g;
|
||||
/* 18.3 Worksheets */
|
||||
function parse_ws_xml(data/*:?string*/, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
if(!data) return data;
|
||||
@ -57,6 +58,10 @@ function parse_ws_xml(data/*:?string*/, opts, rels, wb, themes, styles)/*:Worksh
|
||||
var hlink = data2.match(hlinkregex);
|
||||
if(hlink) parse_ws_xml_hlinks(s, hlink, rels);
|
||||
|
||||
/* 18.3.1.62 pageMargins CT_PageMargins */
|
||||
var margins = data2.match(marginregex);
|
||||
if(margins) s['!margins'] = parse_ws_xml_margins(parsexmltag(margins[0]));
|
||||
|
||||
if(!s["!ref"] && refguess.e.c >= refguess.s.c && refguess.e.r >= refguess.s.r) s["!ref"] = encode_range(refguess);
|
||||
if(opts.sheetRows > 0 && s["!ref"]) {
|
||||
var tmpref = safe_decode_range(s["!ref"]);
|
||||
@ -131,6 +136,14 @@ function parse_ws_xml_hlinks(s, data/*:Array<string>*/, rels) {
|
||||
}
|
||||
}
|
||||
|
||||
function parse_ws_xml_margins(margin) {
|
||||
var o = {};
|
||||
["left", "right", "top", "bottom", "header", "footer"].forEach(function(k) {
|
||||
if(margin[k]) o[k] = parseFloat(margin[k]);
|
||||
});
|
||||
return o;
|
||||
}
|
||||
|
||||
function parse_ws_xml_cols(columns, cols) {
|
||||
var seencol = false;
|
||||
for(var coli = 0; coli != cols.length; ++coli) {
|
||||
|
@ -289,6 +289,29 @@ function write_BrtColInfo(C/*:number*/, col, o) {
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.4.672 BrtMargins */
|
||||
function parse_BrtMargins(data, length, opts) {
|
||||
return {
|
||||
left: parse_Xnum(data, 8),
|
||||
right: parse_Xnum(data, 8),
|
||||
top: parse_Xnum(data, 8),
|
||||
bottom: parse_Xnum(data, 8),
|
||||
header: parse_Xnum(data, 8),
|
||||
footer: parse_Xnum(data, 8)
|
||||
};
|
||||
}
|
||||
function write_BrtMargins(margins, o) {
|
||||
if(o == null) o = new_buf(6*8);
|
||||
default_margins(margins);
|
||||
write_Xnum(margins.left, o);
|
||||
write_Xnum(margins.right, o);
|
||||
write_Xnum(margins.top, o);
|
||||
write_Xnum(margins.bottom, o);
|
||||
write_Xnum(margins.header, o);
|
||||
write_Xnum(margins.footer, o);
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.4.740 BrtSheetProtection */
|
||||
function write_BrtSheetProtection(sp, o) {
|
||||
if(o == null) o = new_buf(16*4+2);
|
||||
@ -467,6 +490,10 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
s['!autofilter'] = { ref:encode_range(val) };
|
||||
break;
|
||||
|
||||
case 0x01DC: /* 'BrtMargins' */
|
||||
s['!margins'] = val;
|
||||
break;
|
||||
|
||||
case 0x00AF: /* 'BrtAFilterDateGroupItem' */
|
||||
case 0x0284: /* 'BrtActiveX' */
|
||||
case 0x0271: /* 'BrtBigName' */
|
||||
@ -498,7 +525,6 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
case 0x0227: /* 'BrtLegacyDrawing' */
|
||||
case 0x0228: /* 'BrtLegacyDrawingHF' */
|
||||
case 0x0295: /* 'BrtListPart' */
|
||||
case 0x01DC: /* 'BrtMargins' */
|
||||
case 0x027F: /* 'BrtOleObject' */
|
||||
case 0x01DE: /* 'BrtPageSetup' */
|
||||
case 0x0097: /* 'BrtPane' */
|
||||
@ -704,7 +730,7 @@ function write_ws_bin(idx/*:number*/, opts, wb/*:Workbook*/, rels) {
|
||||
/* [DVALS] */
|
||||
write_HLINKS(ba, ws, rels);
|
||||
/* [BrtPrintOptions] */
|
||||
/* [BrtMargins] */
|
||||
if(ws['!margins']) write_record(ba, "BrtMargins", write_BrtMargins(ws['!margins']));
|
||||
/* [BrtPageSetup] */
|
||||
/* [HEADERFOOTER] */
|
||||
/* [RWBRK] */
|
||||
|
@ -528,6 +528,22 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
}
|
||||
else pidx = Rn.index + Rn[0].length;
|
||||
break;
|
||||
case 'Header':
|
||||
if(!cursheet['!margins']) default_margins(cursheet['!margins']={}, 'xlml');
|
||||
cursheet['!margins'].header = parsexmltag(Rn[0]).Margin;
|
||||
break;
|
||||
case 'Footer':
|
||||
if(!cursheet['!margins']) default_margins(cursheet['!margins']={}, 'xlml');
|
||||
cursheet['!margins'].footer = parsexmltag(Rn[0]).Margin;
|
||||
break;
|
||||
case 'PageMargins':
|
||||
var pagemargins = parsexmltag(Rn[0]);
|
||||
if(!cursheet['!margins']) default_margins(cursheet['!margins']={},'xlml');
|
||||
if(pagemargins.Top) cursheet['!margins'].top = pagemargins.Top;
|
||||
if(pagemargins.Left) cursheet['!margins'].left = pagemargins.Left;
|
||||
if(pagemargins.Right) cursheet['!margins'].right = pagemargins.Right;
|
||||
if(pagemargins.Bottom) cursheet['!margins'].bottom = pagemargins.Bottom;
|
||||
break;
|
||||
case 'Unsynced': break;
|
||||
case 'Print': break;
|
||||
case 'Panes': break;
|
||||
@ -535,10 +551,7 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
case 'Pane': break;
|
||||
case 'Number': break;
|
||||
case 'Layout': break;
|
||||
case 'Header': break;
|
||||
case 'Footer': break;
|
||||
case 'PageSetup': break;
|
||||
case 'PageMargins': break;
|
||||
case 'Selected': break;
|
||||
case 'ProtectObjects': break;
|
||||
case 'EnableSelection': break;
|
||||
|
@ -503,12 +503,30 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
} break;
|
||||
case 'Row': break; // TODO
|
||||
|
||||
case 'LeftMargin':
|
||||
case 'RightMargin':
|
||||
case 'TopMargin':
|
||||
case 'BottomMargin':
|
||||
if(!out['!margins']) default_margins(out['!margins'] = {});
|
||||
switch(Rn) {
|
||||
case 'LeftMargin': out['!margins'].left = val; break;
|
||||
case 'RightMargin': out['!margins'].right = val; break;
|
||||
case 'TopMargin': out['!margins'].top = val; break;
|
||||
case 'BottomMargin': out['!margins'].bottom = val; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Setup': // TODO
|
||||
if(!out['!margins']) default_margins(out['!margins'] = {});
|
||||
out['!margins'].header = val.header;
|
||||
out['!margins'].footer = val.footer;
|
||||
break;
|
||||
|
||||
case 'Header': break; // TODO
|
||||
case 'Footer': break; // TODO
|
||||
case 'HCenter': break; // TODO
|
||||
case 'VCenter': break; // TODO
|
||||
case 'Pls': break; // TODO
|
||||
case 'Setup': break; // TODO
|
||||
case 'GCW': break;
|
||||
case 'LHRecord': break;
|
||||
case 'DBCell': break; // TODO
|
||||
@ -703,7 +721,6 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
case 'WebPub': case 'AutoWebPub':
|
||||
|
||||
/* Print Stuff */
|
||||
case 'RightMargin': case 'LeftMargin': case 'TopMargin': case 'BottomMargin':
|
||||
case 'HeaderFooter': case 'HFPicture': case 'PLV':
|
||||
case 'HorizontalPageBreaks': case 'VerticalPageBreaks':
|
||||
/* Behavioral */
|
||||
@ -756,7 +773,6 @@ fix_read_opts(options);
|
||||
reset_cp();
|
||||
var CompObj, Summary, Workbook/*:?any*/;
|
||||
if(cfb.FullPaths) {
|
||||
if(cfb.find("EncryptedPackage")) throw new Error("File is password-protected");
|
||||
CompObj = cfb.find('!CompObj');
|
||||
Summary = cfb.find('!SummaryInformation');
|
||||
Workbook = cfb.find('/Workbook');
|
||||
|
@ -406,7 +406,7 @@ var XLSBRecordEnum = {
|
||||
/*::[*/0x01D9/*::]*/: { n:"BrtBeginColorPalette", f:parsenoop },
|
||||
/*::[*/0x01DA/*::]*/: { n:"BrtEndColorPalette", f:parsenoop },
|
||||
/*::[*/0x01DB/*::]*/: { n:"BrtIndexedColor", f:parsenoop },
|
||||
/*::[*/0x01DC/*::]*/: { n:"BrtMargins", f:parsenoop },
|
||||
/*::[*/0x01DC/*::]*/: { n:"BrtMargins", f:parse_BrtMargins },
|
||||
/*::[*/0x01DD/*::]*/: { n:"BrtPrintOptions", f:parsenoop },
|
||||
/*::[*/0x01DE/*::]*/: { n:"BrtPageSetup", f:parsenoop },
|
||||
/*::[*/0x01DF/*::]*/: { n:"BrtBeginHeaderFooter", f:parsenoop },
|
||||
|
@ -168,3 +168,41 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/* references to [MS-OFFCRYPTO] */
|
||||
function parse_xlsxcfb(cfb, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
var f = 'Version';
|
||||
var data = cfb.find(f);
|
||||
if(!data) throw new Error("ECMA-376 Encrypted file missing " + f);
|
||||
var version = parse_DataSpaceVersionInfo(data.content);
|
||||
|
||||
/* 2.3.4.1 */
|
||||
f = 'DataSpaceMap';
|
||||
data = cfb.find(f);
|
||||
if(!data) throw new Error("ECMA-376 Encrypted file missing " + f);
|
||||
var dsm = parse_DataSpaceMap(data.content);
|
||||
if(dsm.length != 1 || dsm[0].comps.length != 1 || dsm[0].comps[0].t != 0 ||
|
||||
dsm[0].name != "StrongEncryptionDataSpace" || dsm[0].comps[0].v != "EncryptedPackage")
|
||||
throw new Error("ECMA-376 Encrypted file bad " + f);
|
||||
|
||||
f = 'StrongEncryptionDataSpace';
|
||||
data = cfb.find(f);
|
||||
if(!data) throw new Error("ECMA-376 Encrypted file missing " + f);
|
||||
var seds = parse_DataSpaceDefinition(data.content);
|
||||
if(seds.length != 1 || seds[0] != "StrongEncryptionTransform")
|
||||
throw new Error("ECMA-376 Encrypted file bad " + f);
|
||||
|
||||
/* 2.3.4.3 */
|
||||
f = '!Primary';
|
||||
data = cfb.find(f);
|
||||
if(!data) throw new Error("ECMA-376 Encrypted file missing " + f);
|
||||
var hdr = parse_Primary(data.content);
|
||||
|
||||
f = 'EncryptionInfo';
|
||||
data = cfb.find(f);
|
||||
if(!data) throw new Error("ECMA-376 Encrypted file missing " + f);
|
||||
var einfo = parse_EncryptionInfo(data.content);
|
||||
|
||||
throw new Error("File is password-protected");
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,11 @@ function firstbyte(f/*:RawData*/,o/*:?TypeOpts*/)/*:Array<number>*/ {
|
||||
return [x.charCodeAt(0), x.charCodeAt(1), x.charCodeAt(2), x.charCodeAt(3)];
|
||||
}
|
||||
|
||||
function read_cfb(cfb, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
if(cfb.find("EncryptedPackage")) return parse_xlsxcfb(cfb, opts);
|
||||
return parse_xlscfb(cfb, opts);
|
||||
}
|
||||
|
||||
function read_zip(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
/*:: if(!jszip) throw new Error("JSZip is not available"); */
|
||||
var zip, d = data;
|
||||
@ -39,7 +44,7 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
if(!o.type) o.type = (has_buf && Buffer.isBuffer(data)) ? "buffer" : "base64";
|
||||
if(o.type == "file") { o.type = "buffer"; d = _fs.readFileSync(data); }
|
||||
switch((n = firstbyte(d, o))[0]) {
|
||||
case 0xD0: return parse_xlscfb(CFB.read(d, o), o);
|
||||
case 0xD0: return read_cfb(CFB.read(d, o), o);
|
||||
case 0x09: return parse_xlscfb(s2a(o.type === 'base64' ? Base64.decode(d) : d), o);
|
||||
case 0x3C: return parse_xlml(d, o);
|
||||
case 0x49: if(n[1] == 0x44) return SYLK.to_workbook(d, o); break;
|
||||
|
@ -20,3 +20,25 @@ Special sheet keys (accessible as `sheet[key]`, each starting with `!`):
|
||||
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']`
|
||||
|
||||
- `sheet['!margins']`: Object representing the page margins. The default values
|
||||
follow Excel's "normal" preset. Excel also has a "wide" and a "narrow" preset
|
||||
but they are stored as raw measurements. The main properties are listed below:
|
||||
|
||||
| key | description | "normal" | "wide" | "narrow" |
|
||||
|----------|------------------------|:---------|:-------|:-------- |
|
||||
| `left` | left margin (inches) | `0.7` | `1.0` | `0.25` |
|
||||
| `right` | right margin (inches) | `0.7` | `1.0` | `0.25` |
|
||||
| `top` | top margin (inches) | `0.75` | `1.0` | `0.75` |
|
||||
| `bottom` | bottom margin (inches) | `0.75` | `1.0` | `0.75` |
|
||||
| `header` | header margin (inches) | `0.3` | `0.5` | `0.3` |
|
||||
| `footer` | footer margin (inches) | `0.3` | `0.5` | `0.3` |
|
||||
|
||||
```js
|
||||
/* Set worksheet sheet to "normal" */
|
||||
sheet["!margins"] = { left:0.7, right:0.7, top:0.75, bottom:0.75, header:0.3, footer:0.3 }
|
||||
/* Set worksheet sheet to "wide" */
|
||||
sheet["!margins"] = { left:1.0, right:1.0, top:1.0, bottom:1.0, header:0.5, footer:0.5 }
|
||||
/* Set worksheet sheet to "narrow" */
|
||||
sheet["!margins"] = { left:0.25, right:0.25, top:0.75, bottom:0.75, header:0.3, footer:0.3 }
|
||||
```
|
||||
|
||||
|
118
test.js
118
test.js
@ -37,57 +37,76 @@ var paths = {
|
||||
afods: dir + 'AutoFilter.ods',
|
||||
afxlsx: dir + 'AutoFilter.xlsx',
|
||||
afxlsb: dir + 'AutoFilter.xlsb',
|
||||
|
||||
cpxls: dir + 'custom_properties.xls',
|
||||
cpxml: dir + 'custom_properties.xls.xml',
|
||||
cpxlsx: dir + 'custom_properties.xlsx',
|
||||
cpxlsb: dir + 'custom_properties.xlsb',
|
||||
|
||||
cssxls: dir + 'cell_style_simple.xls',
|
||||
cssxml: dir + 'cell_style_simple.xml',
|
||||
cssxlsx: dir + 'cell_style_simple.xlsx',
|
||||
cssxlsb: dir + 'cell_style_simple.xlsb',
|
||||
|
||||
cstxls: dir + 'comments_stress_test.xls',
|
||||
cstxml: dir + 'comments_stress_test.xls.xml',
|
||||
cstxlsx: dir + 'comments_stress_test.xlsx',
|
||||
cstxlsb: dir + 'comments_stress_test.xlsb',
|
||||
cstods: dir + 'comments_stress_test.ods',
|
||||
dnsxls: dir + 'defined_names_simple.xls',
|
||||
dnsxml: dir + 'defined_names_simple.xml',
|
||||
dnsxlsx: dir + 'defined_names_simple.xlsx',
|
||||
dnsxlsb: dir + 'defined_names_simple.xlsb',
|
||||
fstxls: dir + 'formula_stress_test.xls',
|
||||
fstxml: dir + 'formula_stress_test.xls.xml',
|
||||
fstxlsx: dir + 'formula_stress_test.xlsx',
|
||||
fstxlsb: dir + 'formula_stress_test.xlsb',
|
||||
fstods: dir + 'formula_stress_test.ods',
|
||||
hlxls: dir + 'hyperlink_stress_test_2011.xls',
|
||||
hlxml: dir + 'hyperlink_stress_test_2011.xml',
|
||||
hlxlsx: dir + 'hyperlink_stress_test_2011.xlsx',
|
||||
hlxlsb: dir + 'hyperlink_stress_test_2011.xlsb',
|
||||
lonxls: dir + 'LONumbers.xls',
|
||||
lonxlsx: dir + 'LONumbers.xlsx',
|
||||
mcxls: dir + 'merge_cells.xls',
|
||||
mcxml: dir + 'merge_cells.xls.xml',
|
||||
mcxlsx: dir + 'merge_cells.xlsx',
|
||||
mcxlsb: dir + 'merge_cells.xlsb',
|
||||
mcods: dir + 'merge_cells.ods',
|
||||
nfxls: dir + 'number_format.xls',
|
||||
nfxml: dir + 'number_format.xls.xml',
|
||||
nfxlsx: dir + 'number_format.xlsm',
|
||||
nfxlsb: dir + 'number_format.xlsb',
|
||||
dtxls: dir + 'xlsx-stream-d-date-cell.xls',
|
||||
dtxml: dir + 'xlsx-stream-d-date-cell.xls.xml',
|
||||
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',
|
||||
|
||||
dnsxls: dir + 'defined_names_simple.xls',
|
||||
dnsxml: dir + 'defined_names_simple.xml',
|
||||
dnsxlsx: dir + 'defined_names_simple.xlsx',
|
||||
dnsxlsb: dir + 'defined_names_simple.xlsb',
|
||||
|
||||
dtxls: dir + 'xlsx-stream-d-date-cell.xls',
|
||||
dtxml: dir + 'xlsx-stream-d-date-cell.xls.xml',
|
||||
dtxlsx: dir + 'xlsx-stream-d-date-cell.xlsx',
|
||||
dtxlsb: dir + 'xlsx-stream-d-date-cell.xlsb',
|
||||
|
||||
fstxls: dir + 'formula_stress_test.xls',
|
||||
fstxml: dir + 'formula_stress_test.xls.xml',
|
||||
fstxlsx: dir + 'formula_stress_test.xlsx',
|
||||
fstxlsb: dir + 'formula_stress_test.xlsb',
|
||||
fstods: dir + 'formula_stress_test.ods',
|
||||
|
||||
hlxls: dir + 'hyperlink_stress_test_2011.xls',
|
||||
hlxml: dir + 'hyperlink_stress_test_2011.xml',
|
||||
hlxlsx: dir + 'hyperlink_stress_test_2011.xlsx',
|
||||
hlxlsb: dir + 'hyperlink_stress_test_2011.xlsb',
|
||||
|
||||
lonxls: dir + 'LONumbers.xls',
|
||||
lonxlsx: dir + 'LONumbers.xlsx',
|
||||
|
||||
mcxls: dir + 'merge_cells.xls',
|
||||
mcxml: dir + 'merge_cells.xls.xml',
|
||||
mcxlsx: dir + 'merge_cells.xlsx',
|
||||
mcxlsb: dir + 'merge_cells.xlsb',
|
||||
mcods: dir + 'merge_cells.ods',
|
||||
|
||||
nfxls: dir + 'number_format.xls',
|
||||
nfxml: dir + 'number_format.xls.xml',
|
||||
nfxlsx: dir + 'number_format.xlsm',
|
||||
nfxlsb: dir + 'number_format.xlsb',
|
||||
|
||||
pmxls: dir + 'page_margins_2016.xls',
|
||||
pmxls5: dir + 'page_margins_2016_5.xls',
|
||||
pmxml: dir + 'page_margins_2016.xml',
|
||||
pmxlsx: dir + 'page_margins_2016.xlsx',
|
||||
pmxlsb: dir + 'page_margins_2016.xlsb',
|
||||
|
||||
svxls: dir + 'sheet_visibility.xls',
|
||||
svxls5: dir + 'sheet_visibility.xls',
|
||||
svxml: dir + 'sheet_visibility.xml',
|
||||
svxlsx: dir + 'sheet_visibility.xlsx',
|
||||
svxlsb: dir + 'sheet_visibility.xlsb',
|
||||
|
||||
swcxls: dir + 'apachepoi_SimpleWithComments.xls',
|
||||
swcxml: dir + '2011/apachepoi_SimpleWithComments.xls.xml',
|
||||
swcxlsx: dir + 'apachepoi_SimpleWithComments.xlsx',
|
||||
@ -937,6 +956,49 @@ describe('parse features', function() {
|
||||
}); });
|
||||
});
|
||||
|
||||
describe('page margins', function() {
|
||||
function check_margin(margins, exp) {
|
||||
assert.equal(margins.left, exp[0]);
|
||||
assert.equal(margins.right, exp[1]);
|
||||
assert.equal(margins.top, exp[2]);
|
||||
assert.equal(margins.bottom, exp[3]);
|
||||
assert.equal(margins.header, exp[4]);
|
||||
assert.equal(margins.footer, exp[5]);
|
||||
}
|
||||
var wb1, wb2, wb3, wb4, wb5, wbs;
|
||||
var bef = (function() {
|
||||
wb1 = X.readFile(paths.pmxls);
|
||||
wb2 = X.readFile(paths.pmxls5);
|
||||
wb3 = X.readFile(paths.pmxml);
|
||||
wb4 = X.readFile(paths.pmxlsx);
|
||||
wb5 = X.readFile(paths.pmxlsb);
|
||||
wbs = [wb1, wb2, wb3, wb4, wb5];
|
||||
});
|
||||
if(typeof before != 'undefined') before(bef);
|
||||
else it('before', bef);
|
||||
it('should parse normal margin', function() {
|
||||
wbs.forEach(function(wb) {
|
||||
check_margin(wb.Sheets["Normal"]["!margins"], [0.7, 0.7, 0.75, 0.75, 0.3, 0.3]);
|
||||
});
|
||||
});
|
||||
it('should parse wide margins ', function() {
|
||||
wbs.forEach(function(wb) {
|
||||
check_margin(wb.Sheets["Wide"]["!margins"], [1, 1, 1, 1, 0.5, 0.5]);
|
||||
});
|
||||
});
|
||||
it('should parse narrow margins ', function() {
|
||||
wbs.forEach(function(wb) {
|
||||
check_margin(wb.Sheets["Narrow"]["!margins"], [0.25, 0.25, 0.75, 0.75, 0.3, 0.3]);
|
||||
});
|
||||
});
|
||||
it('should parse custom margins ', function() {
|
||||
wbs.forEach(function(wb) {
|
||||
check_margin(wb.Sheets["Custom 1 Inch Centered"]["!margins"], [1, 1, 1, 1, 0.3, 0.3]);
|
||||
check_margin(wb.Sheets["1 Inch HF"]["!margins"], [0.7, 0.7, 0.75, 0.75, 1, 1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('should correctly handle styles', function() {
|
||||
var wsxls, wsxlsx, rn, rn2;
|
||||
var bef = (function() {
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 6a9e5891f206ca9c5ff83489165548a105e5fd29
|
||||
Subproject commit 249b005fddf7cea0b2c2d1aff5a2414e47e70c0e
|
@ -76,6 +76,9 @@ ws['B1'].z = "0%"; // Format Code 9
|
||||
/* TEST: custom format */
|
||||
//ws['B2'].z = "0.0"; wb.SSF[60] = "0.0"; // Custom
|
||||
|
||||
/* TEST: page margins */
|
||||
ws['!margins'] = { left:1.0, right:1.0, top:1.0, bottom:1.0, header:0.5, footer:0.5 };
|
||||
|
||||
console.log("JSON Data:");console.log(XLSX.utils.sheet_to_json(ws, {header:1}));
|
||||
|
||||
/* TEST: hidden sheets */
|
||||
|
307
xlsx.flow.js
307
xlsx.flow.js
@ -1780,6 +1780,10 @@ var __lpstr, ___lpstr;
|
||||
__lpstr = ___lpstr = function lpstr_(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len-1) : "";};
|
||||
var __lpwstr, ___lpwstr;
|
||||
__lpwstr = ___lpwstr = function lpwstr_(b,i) { var len = 2*__readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len-1) : "";};
|
||||
var __lpp4, ___lpp4;
|
||||
__lpp4 = ___lpp4 = function lpp4_(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? __utf16le(b, i+4,i+4+len) : "";};
|
||||
var __8lpp4, ___8lpp4;
|
||||
__8lpp4 = ___8lpp4 = function lpp4_8(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len) : "";};
|
||||
var __double, ___double;
|
||||
__double = ___double = function(b, idx) { return read_double_le(b, idx);};
|
||||
|
||||
@ -1789,6 +1793,8 @@ if(has_buf/*:: && typeof Buffer != 'undefined'*/) {
|
||||
__hexlify = function(b,s,l) { return Buffer.isBuffer(b) ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); };
|
||||
__lpstr = function lpstr_b(b,i) { if(!Buffer.isBuffer(b)) return ___lpstr(b, i); var len = b.readUInt32LE(i); return len > 0 ? b.toString('utf8',i+4,i+4+len-1) : "";};
|
||||
__lpwstr = function lpwstr_b(b,i) { if(!Buffer.isBuffer(b)) return ___lpwstr(b, i); var len = 2*b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len-1);};
|
||||
__lpp4 = function lpp4_b(b,i) { if(!Buffer.isBuffer(b)) return ___lpp4(b, i); var len = b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len);};
|
||||
__8lpp4 = function lpp4_8b(b,i) { if(!Buffer.isBuffer(b)) return ___8lpp4(b, i); var len = b.readUInt32LE(i); return b.toString('utf8',i+4,i+4+len);};
|
||||
__utf8 = function utf8_b(b, s,e) { return b.toString('utf8',s,e); };
|
||||
__toBuffer = function(bufs) { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat(bufs[0]) : ___toBuffer(bufs);};
|
||||
bconcat = function(bufs) { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(bufs) : [].concat.apply([], bufs); };
|
||||
@ -1802,6 +1808,8 @@ if(typeof cptable !== 'undefined') {
|
||||
__utf8 = function(b,s,e) { return cptable.utils.decode(65001, b.slice(s,e)); };
|
||||
__lpstr = function(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(current_codepage, b.slice(i+4, i+4+len-1)) : "";};
|
||||
__lpwstr = function(b,i) { var len = 2*__readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(1200, b.slice(i+4,i+4+len-1)) : "";};
|
||||
__lpp4 = function(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(1200, b.slice(i+4,i+4+len)) : "";};
|
||||
__8lpp4 = function(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(65001, b.slice(i+4,i+4+len)) : "";};
|
||||
}
|
||||
|
||||
var __readUInt8 = function(b, idx) { return b[idx]; };
|
||||
@ -1835,6 +1843,10 @@ function ReadShift(size/*:number*/, t/*:?string*/) {
|
||||
case 'lpstr': o = __lpstr(this, this.l); size = 5 + o.length; break;
|
||||
/* [MS-OLEDS] 2.1.5 LengthPrefixedUnicodeString */
|
||||
case 'lpwstr': o = __lpwstr(this, this.l); size = 5 + o.length; if(o[o.length-1] == '\u0000') size += 2; break;
|
||||
/* [MS-OFFCRYPTO] 2.1.2 Length-Prefixed Padded Unicode String (UNICODE-LP-P4) */
|
||||
case 'lpp4': size = 4 + __readUInt32LE(this, this.l); o = __lpp4(this, this.l); if(size & 0x02) size += 2; break;
|
||||
/* [MS-OFFCRYPTO] 2.1.3 Length-Prefixed UTF-8 String (UTF-8-LP-P4) */
|
||||
case '8lpp4': size = 4 + __readUInt32LE(this, this.l); o = __8lpp4(this, this.l); if(size & 0x03) size += 4 - (size & 0x03); break;
|
||||
|
||||
case 'cstr': size = 0; o = "";
|
||||
while((w=__readUInt8(this, this.l + size++))!==0) oo.push(_getchar(w));
|
||||
@ -4406,6 +4418,16 @@ function parse_ColInfo(blob, length, opts) {
|
||||
return {s:colFirst, e:colLast, w:coldx, ixfe:ixfe, flags:flags};
|
||||
}
|
||||
|
||||
/* 2.4.257 */
|
||||
function parse_Setup(blob, length, opts) {
|
||||
var o = {};
|
||||
blob.l += 16;
|
||||
o.header = parse_Xnum(blob, 8);
|
||||
o.footer = parse_Xnum(blob, 8);
|
||||
blob.l += 2;
|
||||
return o;
|
||||
}
|
||||
|
||||
/* 2.4.261 */
|
||||
function parse_ShtProps(blob, length, opts) {
|
||||
var def = {area:false};
|
||||
@ -4415,7 +4437,6 @@ function parse_ShtProps(blob, length, opts) {
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
var parse_Style = parsenoop;
|
||||
var parse_StyleExt = parsenoop;
|
||||
|
||||
@ -4498,7 +4519,6 @@ var parse_FnGroupName = parsenoop;
|
||||
var parse_FilterMode = parsenoop;
|
||||
var parse_AutoFilterInfo = parsenoop;
|
||||
var parse_AutoFilter = parsenoop;
|
||||
var parse_Setup = parsenoop;
|
||||
var parse_ScenMan = parsenoop;
|
||||
var parse_SCENARIO = parsenoop;
|
||||
var parse_SxView = parsenoop;
|
||||
@ -5870,38 +5890,154 @@ function _JS2ANSI(str/*:string*/)/*:Array<number>*/ {
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.4 Version */
|
||||
function parse_Version(blob, length/*:number*/) {
|
||||
function parse_CRYPTOVersion(blob, length/*:number*/) {
|
||||
var o = {};
|
||||
o.Major = blob.read_shift(2);
|
||||
o.Minor = blob.read_shift(2);
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.5 DataSpaceVersionInfo */
|
||||
function parse_DataSpaceVersionInfo(blob, length) {
|
||||
var o = {};
|
||||
o.id = blob.read_shift(0, 'lpp4');
|
||||
o.R = parse_CRYPTOVersion(blob, 4);
|
||||
o.U = parse_CRYPTOVersion(blob, 4);
|
||||
o.W = parse_CRYPTOVersion(blob, 4);
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.6.1 DataSpaceMapEntry Structure */
|
||||
function parse_DataSpaceMapEntry(blob) {
|
||||
var len = blob.read_shift(4);
|
||||
var end = blob.l + len - 4;
|
||||
var o = {};
|
||||
var cnt = blob.read_shift(4);
|
||||
var comps = [];
|
||||
while(cnt-- > 0) {
|
||||
/* [MS-OFFCRYPTO] 2.1.6.2 DataSpaceReferenceComponent Structure */
|
||||
var rc = {};
|
||||
rc.t = blob.read_shift(4);
|
||||
rc.v = blob.read_shift(0, 'lpp4');
|
||||
comps.push(rc);
|
||||
}
|
||||
o.name = blob.read_shift(0, 'lpp4');
|
||||
o.comps = comps;
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.6 DataSpaceMap */
|
||||
function parse_DataSpaceMap(blob, length) {
|
||||
var o = [];
|
||||
blob.l += 4; // must be 0x8
|
||||
var cnt = blob.read_shift(4);
|
||||
while(cnt-- > 0) o.push(parse_DataSpaceMapEntry(blob));
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.7 DataSpaceDefinition */
|
||||
function parse_DataSpaceDefinition(blob, length) {
|
||||
var o = [];
|
||||
blob.l += 4; // must be 0x8
|
||||
var cnt = blob.read_shift(4);
|
||||
while(cnt-- > 0) o.push(blob.read_shift(0, 'lpp4'));
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.8 DataSpaceDefinition */
|
||||
function parse_TransformInfoHeader(blob, length) {
|
||||
var o = {};
|
||||
var len = blob.read_shift(4);
|
||||
var tgt = blob.l + len - 4;
|
||||
blob.l += 4; // must be 0x1
|
||||
o.id = blob.read_shift(0, 'lpp4');
|
||||
// tgt == len
|
||||
o.name = blob.read_shift(0, 'lpp4');
|
||||
o.R = parse_CRYPTOVersion(blob, 4);
|
||||
o.U = parse_CRYPTOVersion(blob, 4);
|
||||
o.W = parse_CRYPTOVersion(blob, 4);
|
||||
return o;
|
||||
}
|
||||
|
||||
function parse_Primary(blob, length) {
|
||||
/* [MS-OFFCRYPTO] 2.2.6 IRMDSTransformInfo */
|
||||
var hdr = parse_TransformInfoHeader(blob);
|
||||
/* [MS-OFFCRYPTO] 2.1.9 EncryptionTransformInfo */
|
||||
hdr.ename = blob.read_shift(0, '8lpp4');
|
||||
hdr.blksz = blob.read_shift(4);
|
||||
hdr.cmode = blob.read_shift(4);
|
||||
if(blob.read_shift(4) != 0x04) throw new Error("Bad !Primary record");
|
||||
return hdr;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.3.2 Encryption Header */
|
||||
function parse_EncryptionHeader(blob, length/*:number*/) {
|
||||
var tgt = blob.l + length;
|
||||
var o = {};
|
||||
o.Flags = blob.read_shift(4);
|
||||
|
||||
// Check if SizeExtra is 0x00000000
|
||||
var tmp = blob.read_shift(4);
|
||||
if(tmp !== 0) throw 'Unrecognized SizeExtra: ' + tmp;
|
||||
|
||||
o.Flags = (blob.read_shift(4) & 0x3F);
|
||||
blob.l += 4;
|
||||
o.AlgID = blob.read_shift(4);
|
||||
var valid = false;
|
||||
switch(o.AlgID) {
|
||||
case 0: case 0x6801: case 0x660E: case 0x660F: case 0x6610: break;
|
||||
case 0x660E: case 0x660F: case 0x6610: valid = (o.Flags == 0x24); break;
|
||||
case 0x6801: valid = (o.Flags == 0x04); break;
|
||||
case 0: valid = (o.Flags == 0x10 || o.Flags == 0x04 || o.Flags == 0x24); break;
|
||||
default: throw 'Unrecognized encryption algorithm: ' + o.AlgID;
|
||||
}
|
||||
parsenoop(blob, length-12);
|
||||
if(!valid) throw new Error("Encryption Flags/AlgID mismatch");
|
||||
o.AlgIDHash = blob.read_shift(4);
|
||||
o.KeySize = blob.read_shift(4);
|
||||
o.ProviderType = blob.read_shift(4);
|
||||
blob.l += 8;
|
||||
o.CSPName = blob.read_shift((tgt-blob.l)>>1, 'utf16le').slice(0,-1);
|
||||
blob.l = tgt;
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.3.3 Encryption Verifier */
|
||||
function parse_EncryptionVerifier(blob, length/*:number*/) {
|
||||
return parsenoop(blob, length);
|
||||
var o = {};
|
||||
blob.l += 4; // SaltSize must be 0x10
|
||||
o.Salt = blob.slice(blob.l, blob.l+16); blob.l += 16;
|
||||
o.Verifier = blob.slice(blob.l, blob.l+16); blob.l += 16;
|
||||
var sz = blob.read_shift(4);
|
||||
o.VerifierHash = blob.slice(blob.l, blob.l + sz); blob.l += sz;
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.3.4.* EncryptionInfo Stream */
|
||||
function parse_EncryptionInfo(blob, length) {
|
||||
var vers = parse_CRYPTOVersion(blob);
|
||||
switch(vers.Minor) {
|
||||
case 0x02: return parse_EncInfoStd(blob, vers);
|
||||
case 0x03: return parse_EncInfoExt(blob, vers);
|
||||
case 0x04: return parse_EncInfoAgl(blob, vers);
|
||||
}
|
||||
throw new Error("ECMA-376 Encryped file unrecognized Version: " + vers.Minor);
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.3.4.5 EncryptionInfo Stream (Standard Encryption) */
|
||||
function parse_EncInfoStd(blob, vers) {
|
||||
var flags = blob.read_shift(4);
|
||||
if((flags & 0x3F) != 0x24) throw new Error("EncryptionInfo mismatch");
|
||||
var sz = blob.read_shift(4);
|
||||
var tgt = blob.l + sz;
|
||||
var hdr = parse_EncryptionHeader(blob, sz);
|
||||
var verifier = parse_EncryptionVerifier(blob, blob.length - blob.l);
|
||||
return { t:"Std", h:hdr, v:verifier };
|
||||
}
|
||||
/* [MS-OFFCRYPTO] 2.3.4.6 EncryptionInfo Stream (Extensible Encryption) */
|
||||
function parse_EncInfoExt(blob, vers) { throw new Error("File is password-protected: ECMA-376 Extensible"); }
|
||||
/* [MS-OFFCRYPTO] 2.3.4.10 EncryptionInfo Stream (Agile Encryption) */
|
||||
function parse_EncInfoAgl(blob, vers) { throw new Error("File is password-protected: ECMA-376 Agile"); }
|
||||
|
||||
|
||||
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.3.5.1 RC4 CryptoAPI Encryption Header */
|
||||
function parse_RC4CryptoHeader(blob, length/*:number*/) {
|
||||
var o = {};
|
||||
var vers = o.EncryptionVersionInfo = parse_Version(blob, 4); length -= 4;
|
||||
var vers = o.EncryptionVersionInfo = parse_CRYPTOVersion(blob, 4); length -= 4;
|
||||
if(vers.Minor != 2) throw 'unrecognized minor version code: ' + vers.Minor;
|
||||
if(vers.Major > 4 || vers.Major < 2) throw 'unrecognized major version code: ' + vers.Major;
|
||||
o.Flags = blob.read_shift(4); length -= 4;
|
||||
@ -5913,7 +6049,7 @@ function parse_RC4CryptoHeader(blob, length/*:number*/) {
|
||||
/* [MS-OFFCRYPTO] 2.3.6.1 RC4 Encryption Header */
|
||||
function parse_RC4Header(blob, length/*:number*/) {
|
||||
var o = {};
|
||||
var vers = o.EncryptionVersionInfo = parse_Version(blob, 4); length -= 4;
|
||||
var vers = o.EncryptionVersionInfo = parse_CRYPTOVersion(blob, 4); length -= 4;
|
||||
if(vers.Major != 1 || vers.Minor != 1) throw 'unrecognized version code ' + vers.Major + ' : ' + vers.Minor;
|
||||
o.Salt = blob.read_shift(16);
|
||||
o.EncryptedVerifier = blob.read_shift(16);
|
||||
@ -9674,6 +9810,18 @@ function col_obj_w(C/*:number*/, col) {
|
||||
return p;
|
||||
}
|
||||
|
||||
function default_margins(margins, mode) {
|
||||
if(!margins) return;
|
||||
var defs = [0.7, 0.7, 0.75, 0.75, 0.3, 0.3];
|
||||
if(mode == 'xlml') defs = [1, 1, 1, 1, 0.5, 0.5];
|
||||
if(margins.left == null) margins.left = defs[0];
|
||||
if(margins.right == null) margins.right = defs[1];
|
||||
if(margins.top == null) margins.top = defs[2];
|
||||
if(margins.bottom == null) margins.bottom = defs[3];
|
||||
if(margins.header == null) margins.header = defs[4];
|
||||
if(margins.footer == null) margins.footer = defs[5];
|
||||
}
|
||||
|
||||
function get_cell_style(styles, cell, opts) {
|
||||
var z = opts.revssf[cell.z != null ? cell.z : "General"];
|
||||
for(var i = 0, len = styles.length; i != len; ++i) if(styles[i].numFmtId === z) return i;
|
||||
@ -9732,6 +9880,7 @@ var hlinkregex = /<(?:\w:)?hyperlink [^>]*>/mg;
|
||||
var dimregex = /"(\w*:\w*)"/;
|
||||
var colregex = /<(?:\w:)?col[^>]*[\/]?>/g;
|
||||
var afregex = /<(?:\w:)?autoFilter[^>]*([\/]|>([^\u2603]*)<\/(?:\w:)?autoFilter)>/g;
|
||||
var marginregex= /<(?:\w:)?pageMargins[^>]*\/>/g;
|
||||
/* 18.3 Worksheets */
|
||||
function parse_ws_xml(data/*:?string*/, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
if(!data) return data;
|
||||
@ -9781,6 +9930,10 @@ function parse_ws_xml(data/*:?string*/, opts, rels, wb, themes, styles)/*:Worksh
|
||||
var hlink = data2.match(hlinkregex);
|
||||
if(hlink) parse_ws_xml_hlinks(s, hlink, rels);
|
||||
|
||||
/* 18.3.1.62 pageMargins CT_PageMargins */
|
||||
var margins = data2.match(marginregex);
|
||||
if(margins) s['!margins'] = parse_ws_xml_margins(parsexmltag(margins[0]));
|
||||
|
||||
if(!s["!ref"] && refguess.e.c >= refguess.s.c && refguess.e.r >= refguess.s.r) s["!ref"] = encode_range(refguess);
|
||||
if(opts.sheetRows > 0 && s["!ref"]) {
|
||||
var tmpref = safe_decode_range(s["!ref"]);
|
||||
@ -9855,6 +10008,14 @@ function parse_ws_xml_hlinks(s, data/*:Array<string>*/, rels) {
|
||||
}
|
||||
}
|
||||
|
||||
function parse_ws_xml_margins(margin) {
|
||||
var o = {};
|
||||
["left", "right", "top", "bottom", "header", "footer"].forEach(function(k) {
|
||||
if(margin[k]) o[k] = parseFloat(margin[k]);
|
||||
});
|
||||
return o;
|
||||
}
|
||||
|
||||
function parse_ws_xml_cols(columns, cols) {
|
||||
var seencol = false;
|
||||
for(var coli = 0; coli != cols.length; ++coli) {
|
||||
@ -10491,6 +10652,29 @@ function write_BrtColInfo(C/*:number*/, col, o) {
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.4.672 BrtMargins */
|
||||
function parse_BrtMargins(data, length, opts) {
|
||||
return {
|
||||
left: parse_Xnum(data, 8),
|
||||
right: parse_Xnum(data, 8),
|
||||
top: parse_Xnum(data, 8),
|
||||
bottom: parse_Xnum(data, 8),
|
||||
header: parse_Xnum(data, 8),
|
||||
footer: parse_Xnum(data, 8)
|
||||
};
|
||||
}
|
||||
function write_BrtMargins(margins, o) {
|
||||
if(o == null) o = new_buf(6*8);
|
||||
default_margins(margins);
|
||||
write_Xnum(margins.left, o);
|
||||
write_Xnum(margins.right, o);
|
||||
write_Xnum(margins.top, o);
|
||||
write_Xnum(margins.bottom, o);
|
||||
write_Xnum(margins.header, o);
|
||||
write_Xnum(margins.footer, o);
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.4.740 BrtSheetProtection */
|
||||
function write_BrtSheetProtection(sp, o) {
|
||||
if(o == null) o = new_buf(16*4+2);
|
||||
@ -10669,6 +10853,10 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
s['!autofilter'] = { ref:encode_range(val) };
|
||||
break;
|
||||
|
||||
case 0x01DC: /* 'BrtMargins' */
|
||||
s['!margins'] = val;
|
||||
break;
|
||||
|
||||
case 0x00AF: /* 'BrtAFilterDateGroupItem' */
|
||||
case 0x0284: /* 'BrtActiveX' */
|
||||
case 0x0271: /* 'BrtBigName' */
|
||||
@ -10700,7 +10888,6 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
case 0x0227: /* 'BrtLegacyDrawing' */
|
||||
case 0x0228: /* 'BrtLegacyDrawingHF' */
|
||||
case 0x0295: /* 'BrtListPart' */
|
||||
case 0x01DC: /* 'BrtMargins' */
|
||||
case 0x027F: /* 'BrtOleObject' */
|
||||
case 0x01DE: /* 'BrtPageSetup' */
|
||||
case 0x0097: /* 'BrtPane' */
|
||||
@ -10906,7 +11093,7 @@ function write_ws_bin(idx/*:number*/, opts, wb/*:Workbook*/, rels) {
|
||||
/* [DVALS] */
|
||||
write_HLINKS(ba, ws, rels);
|
||||
/* [BrtPrintOptions] */
|
||||
/* [BrtMargins] */
|
||||
if(ws['!margins']) write_record(ba, "BrtMargins", write_BrtMargins(ws['!margins']));
|
||||
/* [BrtPageSetup] */
|
||||
/* [HEADERFOOTER] */
|
||||
/* [RWBRK] */
|
||||
@ -12245,6 +12432,22 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
}
|
||||
else pidx = Rn.index + Rn[0].length;
|
||||
break;
|
||||
case 'Header':
|
||||
if(!cursheet['!margins']) default_margins(cursheet['!margins']={}, 'xlml');
|
||||
cursheet['!margins'].header = parsexmltag(Rn[0]).Margin;
|
||||
break;
|
||||
case 'Footer':
|
||||
if(!cursheet['!margins']) default_margins(cursheet['!margins']={}, 'xlml');
|
||||
cursheet['!margins'].footer = parsexmltag(Rn[0]).Margin;
|
||||
break;
|
||||
case 'PageMargins':
|
||||
var pagemargins = parsexmltag(Rn[0]);
|
||||
if(!cursheet['!margins']) default_margins(cursheet['!margins']={},'xlml');
|
||||
if(pagemargins.Top) cursheet['!margins'].top = pagemargins.Top;
|
||||
if(pagemargins.Left) cursheet['!margins'].left = pagemargins.Left;
|
||||
if(pagemargins.Right) cursheet['!margins'].right = pagemargins.Right;
|
||||
if(pagemargins.Bottom) cursheet['!margins'].bottom = pagemargins.Bottom;
|
||||
break;
|
||||
case 'Unsynced': break;
|
||||
case 'Print': break;
|
||||
case 'Panes': break;
|
||||
@ -12252,10 +12455,7 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
case 'Pane': break;
|
||||
case 'Number': break;
|
||||
case 'Layout': break;
|
||||
case 'Header': break;
|
||||
case 'Footer': break;
|
||||
case 'PageSetup': break;
|
||||
case 'PageMargins': break;
|
||||
case 'Selected': break;
|
||||
case 'ProtectObjects': break;
|
||||
case 'EnableSelection': break;
|
||||
@ -13175,12 +13375,30 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
} break;
|
||||
case 'Row': break; // TODO
|
||||
|
||||
case 'LeftMargin':
|
||||
case 'RightMargin':
|
||||
case 'TopMargin':
|
||||
case 'BottomMargin':
|
||||
if(!out['!margins']) default_margins(out['!margins'] = {});
|
||||
switch(Rn) {
|
||||
case 'LeftMargin': out['!margins'].left = val; break;
|
||||
case 'RightMargin': out['!margins'].right = val; break;
|
||||
case 'TopMargin': out['!margins'].top = val; break;
|
||||
case 'BottomMargin': out['!margins'].bottom = val; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Setup': // TODO
|
||||
if(!out['!margins']) default_margins(out['!margins'] = {});
|
||||
out['!margins'].header = val.header;
|
||||
out['!margins'].footer = val.footer;
|
||||
break;
|
||||
|
||||
case 'Header': break; // TODO
|
||||
case 'Footer': break; // TODO
|
||||
case 'HCenter': break; // TODO
|
||||
case 'VCenter': break; // TODO
|
||||
case 'Pls': break; // TODO
|
||||
case 'Setup': break; // TODO
|
||||
case 'GCW': break;
|
||||
case 'LHRecord': break;
|
||||
case 'DBCell': break; // TODO
|
||||
@ -13375,7 +13593,6 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
case 'WebPub': case 'AutoWebPub':
|
||||
|
||||
/* Print Stuff */
|
||||
case 'RightMargin': case 'LeftMargin': case 'TopMargin': case 'BottomMargin':
|
||||
case 'HeaderFooter': case 'HFPicture': case 'PLV':
|
||||
case 'HorizontalPageBreaks': case 'VerticalPageBreaks':
|
||||
/* Behavioral */
|
||||
@ -13428,7 +13645,6 @@ fix_read_opts(options);
|
||||
reset_cp();
|
||||
var CompObj, Summary, Workbook/*:?any*/;
|
||||
if(cfb.FullPaths) {
|
||||
if(cfb.find("EncryptedPackage")) throw new Error("File is password-protected");
|
||||
CompObj = cfb.find('!CompObj');
|
||||
Summary = cfb.find('!SummaryInformation');
|
||||
Workbook = cfb.find('/Workbook');
|
||||
@ -13881,7 +14097,7 @@ var XLSBRecordEnum = {
|
||||
/*::[*/0x01D9/*::]*/: { n:"BrtBeginColorPalette", f:parsenoop },
|
||||
/*::[*/0x01DA/*::]*/: { n:"BrtEndColorPalette", f:parsenoop },
|
||||
/*::[*/0x01DB/*::]*/: { n:"BrtIndexedColor", f:parsenoop },
|
||||
/*::[*/0x01DC/*::]*/: { n:"BrtMargins", f:parsenoop },
|
||||
/*::[*/0x01DC/*::]*/: { n:"BrtMargins", f:parse_BrtMargins },
|
||||
/*::[*/0x01DD/*::]*/: { n:"BrtPrintOptions", f:parsenoop },
|
||||
/*::[*/0x01DE/*::]*/: { n:"BrtPageSetup", f:parsenoop },
|
||||
/*::[*/0x01DF/*::]*/: { n:"BrtBeginHeaderFooter", f:parsenoop },
|
||||
@ -15692,6 +15908,44 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/* references to [MS-OFFCRYPTO] */
|
||||
function parse_xlsxcfb(cfb, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
var f = 'Version';
|
||||
var data = cfb.find(f);
|
||||
if(!data) throw new Error("ECMA-376 Encrypted file missing " + f);
|
||||
var version = parse_DataSpaceVersionInfo(data.content);
|
||||
|
||||
/* 2.3.4.1 */
|
||||
f = 'DataSpaceMap';
|
||||
data = cfb.find(f);
|
||||
if(!data) throw new Error("ECMA-376 Encrypted file missing " + f);
|
||||
var dsm = parse_DataSpaceMap(data.content);
|
||||
if(dsm.length != 1 || dsm[0].comps.length != 1 || dsm[0].comps[0].t != 0 ||
|
||||
dsm[0].name != "StrongEncryptionDataSpace" || dsm[0].comps[0].v != "EncryptedPackage")
|
||||
throw new Error("ECMA-376 Encrypted file bad " + f);
|
||||
|
||||
f = 'StrongEncryptionDataSpace';
|
||||
data = cfb.find(f);
|
||||
if(!data) throw new Error("ECMA-376 Encrypted file missing " + f);
|
||||
var seds = parse_DataSpaceDefinition(data.content);
|
||||
if(seds.length != 1 || seds[0] != "StrongEncryptionTransform")
|
||||
throw new Error("ECMA-376 Encrypted file bad " + f);
|
||||
|
||||
/* 2.3.4.3 */
|
||||
f = '!Primary';
|
||||
data = cfb.find(f);
|
||||
if(!data) throw new Error("ECMA-376 Encrypted file missing " + f);
|
||||
var hdr = parse_Primary(data.content);
|
||||
|
||||
f = 'EncryptionInfo';
|
||||
data = cfb.find(f);
|
||||
if(!data) throw new Error("ECMA-376 Encrypted file missing " + f);
|
||||
var einfo = parse_EncryptionInfo(data.content);
|
||||
|
||||
throw new Error("File is password-protected");
|
||||
}
|
||||
|
||||
function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
|
||||
_shapeid = 1024;
|
||||
if(opts.bookType == "ods") return write_ods(wb, opts);
|
||||
@ -15834,6 +16088,11 @@ function firstbyte(f/*:RawData*/,o/*:?TypeOpts*/)/*:Array<number>*/ {
|
||||
return [x.charCodeAt(0), x.charCodeAt(1), x.charCodeAt(2), x.charCodeAt(3)];
|
||||
}
|
||||
|
||||
function read_cfb(cfb, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
if(cfb.find("EncryptedPackage")) return parse_xlsxcfb(cfb, opts);
|
||||
return parse_xlscfb(cfb, opts);
|
||||
}
|
||||
|
||||
function read_zip(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
/*:: if(!jszip) throw new Error("JSZip is not available"); */
|
||||
var zip, d = data;
|
||||
@ -15863,7 +16122,7 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
if(!o.type) o.type = (has_buf && Buffer.isBuffer(data)) ? "buffer" : "base64";
|
||||
if(o.type == "file") { o.type = "buffer"; d = _fs.readFileSync(data); }
|
||||
switch((n = firstbyte(d, o))[0]) {
|
||||
case 0xD0: return parse_xlscfb(CFB.read(d, o), o);
|
||||
case 0xD0: return read_cfb(CFB.read(d, o), o);
|
||||
case 0x09: return parse_xlscfb(s2a(o.type === 'base64' ? Base64.decode(d) : d), o);
|
||||
case 0x3C: return parse_xlml(d, o);
|
||||
case 0x49: if(n[1] == 0x44) return SYLK.to_workbook(d, o); break;
|
||||
|
307
xlsx.js
307
xlsx.js
@ -1729,6 +1729,10 @@ var __lpstr, ___lpstr;
|
||||
__lpstr = ___lpstr = function lpstr_(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len-1) : "";};
|
||||
var __lpwstr, ___lpwstr;
|
||||
__lpwstr = ___lpwstr = function lpwstr_(b,i) { var len = 2*__readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len-1) : "";};
|
||||
var __lpp4, ___lpp4;
|
||||
__lpp4 = ___lpp4 = function lpp4_(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? __utf16le(b, i+4,i+4+len) : "";};
|
||||
var __8lpp4, ___8lpp4;
|
||||
__8lpp4 = ___8lpp4 = function lpp4_8(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len) : "";};
|
||||
var __double, ___double;
|
||||
__double = ___double = function(b, idx) { return read_double_le(b, idx);};
|
||||
|
||||
@ -1738,6 +1742,8 @@ if(has_buf) {
|
||||
__hexlify = function(b,s,l) { return Buffer.isBuffer(b) ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); };
|
||||
__lpstr = function lpstr_b(b,i) { if(!Buffer.isBuffer(b)) return ___lpstr(b, i); var len = b.readUInt32LE(i); return len > 0 ? b.toString('utf8',i+4,i+4+len-1) : "";};
|
||||
__lpwstr = function lpwstr_b(b,i) { if(!Buffer.isBuffer(b)) return ___lpwstr(b, i); var len = 2*b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len-1);};
|
||||
__lpp4 = function lpp4_b(b,i) { if(!Buffer.isBuffer(b)) return ___lpp4(b, i); var len = b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len);};
|
||||
__8lpp4 = function lpp4_8b(b,i) { if(!Buffer.isBuffer(b)) return ___8lpp4(b, i); var len = b.readUInt32LE(i); return b.toString('utf8',i+4,i+4+len);};
|
||||
__utf8 = function utf8_b(b, s,e) { return b.toString('utf8',s,e); };
|
||||
__toBuffer = function(bufs) { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat(bufs[0]) : ___toBuffer(bufs);};
|
||||
bconcat = function(bufs) { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(bufs) : [].concat.apply([], bufs); };
|
||||
@ -1751,6 +1757,8 @@ if(typeof cptable !== 'undefined') {
|
||||
__utf8 = function(b,s,e) { return cptable.utils.decode(65001, b.slice(s,e)); };
|
||||
__lpstr = function(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(current_codepage, b.slice(i+4, i+4+len-1)) : "";};
|
||||
__lpwstr = function(b,i) { var len = 2*__readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(1200, b.slice(i+4,i+4+len-1)) : "";};
|
||||
__lpp4 = function(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(1200, b.slice(i+4,i+4+len)) : "";};
|
||||
__8lpp4 = function(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(65001, b.slice(i+4,i+4+len)) : "";};
|
||||
}
|
||||
|
||||
var __readUInt8 = function(b, idx) { return b[idx]; };
|
||||
@ -1784,6 +1792,10 @@ function ReadShift(size, t) {
|
||||
case 'lpstr': o = __lpstr(this, this.l); size = 5 + o.length; break;
|
||||
/* [MS-OLEDS] 2.1.5 LengthPrefixedUnicodeString */
|
||||
case 'lpwstr': o = __lpwstr(this, this.l); size = 5 + o.length; if(o[o.length-1] == '\u0000') size += 2; break;
|
||||
/* [MS-OFFCRYPTO] 2.1.2 Length-Prefixed Padded Unicode String (UNICODE-LP-P4) */
|
||||
case 'lpp4': size = 4 + __readUInt32LE(this, this.l); o = __lpp4(this, this.l); if(size & 0x02) size += 2; break;
|
||||
/* [MS-OFFCRYPTO] 2.1.3 Length-Prefixed UTF-8 String (UTF-8-LP-P4) */
|
||||
case '8lpp4': size = 4 + __readUInt32LE(this, this.l); o = __8lpp4(this, this.l); if(size & 0x03) size += 4 - (size & 0x03); break;
|
||||
|
||||
case 'cstr': size = 0; o = "";
|
||||
while((w=__readUInt8(this, this.l + size++))!==0) oo.push(_getchar(w));
|
||||
@ -4350,6 +4362,16 @@ function parse_ColInfo(blob, length, opts) {
|
||||
return {s:colFirst, e:colLast, w:coldx, ixfe:ixfe, flags:flags};
|
||||
}
|
||||
|
||||
/* 2.4.257 */
|
||||
function parse_Setup(blob, length, opts) {
|
||||
var o = {};
|
||||
blob.l += 16;
|
||||
o.header = parse_Xnum(blob, 8);
|
||||
o.footer = parse_Xnum(blob, 8);
|
||||
blob.l += 2;
|
||||
return o;
|
||||
}
|
||||
|
||||
/* 2.4.261 */
|
||||
function parse_ShtProps(blob, length, opts) {
|
||||
var def = {area:false};
|
||||
@ -4359,7 +4381,6 @@ function parse_ShtProps(blob, length, opts) {
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
var parse_Style = parsenoop;
|
||||
var parse_StyleExt = parsenoop;
|
||||
|
||||
@ -4442,7 +4463,6 @@ var parse_FnGroupName = parsenoop;
|
||||
var parse_FilterMode = parsenoop;
|
||||
var parse_AutoFilterInfo = parsenoop;
|
||||
var parse_AutoFilter = parsenoop;
|
||||
var parse_Setup = parsenoop;
|
||||
var parse_ScenMan = parsenoop;
|
||||
var parse_SCENARIO = parsenoop;
|
||||
var parse_SxView = parsenoop;
|
||||
@ -5814,38 +5834,154 @@ function _JS2ANSI(str) {
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.4 Version */
|
||||
function parse_Version(blob, length) {
|
||||
function parse_CRYPTOVersion(blob, length) {
|
||||
var o = {};
|
||||
o.Major = blob.read_shift(2);
|
||||
o.Minor = blob.read_shift(2);
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.5 DataSpaceVersionInfo */
|
||||
function parse_DataSpaceVersionInfo(blob, length) {
|
||||
var o = {};
|
||||
o.id = blob.read_shift(0, 'lpp4');
|
||||
o.R = parse_CRYPTOVersion(blob, 4);
|
||||
o.U = parse_CRYPTOVersion(blob, 4);
|
||||
o.W = parse_CRYPTOVersion(blob, 4);
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.6.1 DataSpaceMapEntry Structure */
|
||||
function parse_DataSpaceMapEntry(blob) {
|
||||
var len = blob.read_shift(4);
|
||||
var end = blob.l + len - 4;
|
||||
var o = {};
|
||||
var cnt = blob.read_shift(4);
|
||||
var comps = [];
|
||||
while(cnt-- > 0) {
|
||||
/* [MS-OFFCRYPTO] 2.1.6.2 DataSpaceReferenceComponent Structure */
|
||||
var rc = {};
|
||||
rc.t = blob.read_shift(4);
|
||||
rc.v = blob.read_shift(0, 'lpp4');
|
||||
comps.push(rc);
|
||||
}
|
||||
o.name = blob.read_shift(0, 'lpp4');
|
||||
o.comps = comps;
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.6 DataSpaceMap */
|
||||
function parse_DataSpaceMap(blob, length) {
|
||||
var o = [];
|
||||
blob.l += 4; // must be 0x8
|
||||
var cnt = blob.read_shift(4);
|
||||
while(cnt-- > 0) o.push(parse_DataSpaceMapEntry(blob));
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.7 DataSpaceDefinition */
|
||||
function parse_DataSpaceDefinition(blob, length) {
|
||||
var o = [];
|
||||
blob.l += 4; // must be 0x8
|
||||
var cnt = blob.read_shift(4);
|
||||
while(cnt-- > 0) o.push(blob.read_shift(0, 'lpp4'));
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.1.8 DataSpaceDefinition */
|
||||
function parse_TransformInfoHeader(blob, length) {
|
||||
var o = {};
|
||||
var len = blob.read_shift(4);
|
||||
var tgt = blob.l + len - 4;
|
||||
blob.l += 4; // must be 0x1
|
||||
o.id = blob.read_shift(0, 'lpp4');
|
||||
// tgt == len
|
||||
o.name = blob.read_shift(0, 'lpp4');
|
||||
o.R = parse_CRYPTOVersion(blob, 4);
|
||||
o.U = parse_CRYPTOVersion(blob, 4);
|
||||
o.W = parse_CRYPTOVersion(blob, 4);
|
||||
return o;
|
||||
}
|
||||
|
||||
function parse_Primary(blob, length) {
|
||||
/* [MS-OFFCRYPTO] 2.2.6 IRMDSTransformInfo */
|
||||
var hdr = parse_TransformInfoHeader(blob);
|
||||
/* [MS-OFFCRYPTO] 2.1.9 EncryptionTransformInfo */
|
||||
hdr.ename = blob.read_shift(0, '8lpp4');
|
||||
hdr.blksz = blob.read_shift(4);
|
||||
hdr.cmode = blob.read_shift(4);
|
||||
if(blob.read_shift(4) != 0x04) throw new Error("Bad !Primary record");
|
||||
return hdr;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.3.2 Encryption Header */
|
||||
function parse_EncryptionHeader(blob, length) {
|
||||
var tgt = blob.l + length;
|
||||
var o = {};
|
||||
o.Flags = blob.read_shift(4);
|
||||
|
||||
// Check if SizeExtra is 0x00000000
|
||||
var tmp = blob.read_shift(4);
|
||||
if(tmp !== 0) throw 'Unrecognized SizeExtra: ' + tmp;
|
||||
|
||||
o.Flags = (blob.read_shift(4) & 0x3F);
|
||||
blob.l += 4;
|
||||
o.AlgID = blob.read_shift(4);
|
||||
var valid = false;
|
||||
switch(o.AlgID) {
|
||||
case 0: case 0x6801: case 0x660E: case 0x660F: case 0x6610: break;
|
||||
case 0x660E: case 0x660F: case 0x6610: valid = (o.Flags == 0x24); break;
|
||||
case 0x6801: valid = (o.Flags == 0x04); break;
|
||||
case 0: valid = (o.Flags == 0x10 || o.Flags == 0x04 || o.Flags == 0x24); break;
|
||||
default: throw 'Unrecognized encryption algorithm: ' + o.AlgID;
|
||||
}
|
||||
parsenoop(blob, length-12);
|
||||
if(!valid) throw new Error("Encryption Flags/AlgID mismatch");
|
||||
o.AlgIDHash = blob.read_shift(4);
|
||||
o.KeySize = blob.read_shift(4);
|
||||
o.ProviderType = blob.read_shift(4);
|
||||
blob.l += 8;
|
||||
o.CSPName = blob.read_shift((tgt-blob.l)>>1, 'utf16le').slice(0,-1);
|
||||
blob.l = tgt;
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.3.3 Encryption Verifier */
|
||||
function parse_EncryptionVerifier(blob, length) {
|
||||
return parsenoop(blob, length);
|
||||
var o = {};
|
||||
blob.l += 4; // SaltSize must be 0x10
|
||||
o.Salt = blob.slice(blob.l, blob.l+16); blob.l += 16;
|
||||
o.Verifier = blob.slice(blob.l, blob.l+16); blob.l += 16;
|
||||
var sz = blob.read_shift(4);
|
||||
o.VerifierHash = blob.slice(blob.l, blob.l + sz); blob.l += sz;
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.3.4.* EncryptionInfo Stream */
|
||||
function parse_EncryptionInfo(blob, length) {
|
||||
var vers = parse_CRYPTOVersion(blob);
|
||||
switch(vers.Minor) {
|
||||
case 0x02: return parse_EncInfoStd(blob, vers);
|
||||
case 0x03: return parse_EncInfoExt(blob, vers);
|
||||
case 0x04: return parse_EncInfoAgl(blob, vers);
|
||||
}
|
||||
throw new Error("ECMA-376 Encryped file unrecognized Version: " + vers.Minor);
|
||||
}
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.3.4.5 EncryptionInfo Stream (Standard Encryption) */
|
||||
function parse_EncInfoStd(blob, vers) {
|
||||
var flags = blob.read_shift(4);
|
||||
if((flags & 0x3F) != 0x24) throw new Error("EncryptionInfo mismatch");
|
||||
var sz = blob.read_shift(4);
|
||||
var tgt = blob.l + sz;
|
||||
var hdr = parse_EncryptionHeader(blob, sz);
|
||||
var verifier = parse_EncryptionVerifier(blob, blob.length - blob.l);
|
||||
return { t:"Std", h:hdr, v:verifier };
|
||||
}
|
||||
/* [MS-OFFCRYPTO] 2.3.4.6 EncryptionInfo Stream (Extensible Encryption) */
|
||||
function parse_EncInfoExt(blob, vers) { throw new Error("File is password-protected: ECMA-376 Extensible"); }
|
||||
/* [MS-OFFCRYPTO] 2.3.4.10 EncryptionInfo Stream (Agile Encryption) */
|
||||
function parse_EncInfoAgl(blob, vers) { throw new Error("File is password-protected: ECMA-376 Agile"); }
|
||||
|
||||
|
||||
|
||||
|
||||
/* [MS-OFFCRYPTO] 2.3.5.1 RC4 CryptoAPI Encryption Header */
|
||||
function parse_RC4CryptoHeader(blob, length) {
|
||||
var o = {};
|
||||
var vers = o.EncryptionVersionInfo = parse_Version(blob, 4); length -= 4;
|
||||
var vers = o.EncryptionVersionInfo = parse_CRYPTOVersion(blob, 4); length -= 4;
|
||||
if(vers.Minor != 2) throw 'unrecognized minor version code: ' + vers.Minor;
|
||||
if(vers.Major > 4 || vers.Major < 2) throw 'unrecognized major version code: ' + vers.Major;
|
||||
o.Flags = blob.read_shift(4); length -= 4;
|
||||
@ -5857,7 +5993,7 @@ function parse_RC4CryptoHeader(blob, length) {
|
||||
/* [MS-OFFCRYPTO] 2.3.6.1 RC4 Encryption Header */
|
||||
function parse_RC4Header(blob, length) {
|
||||
var o = {};
|
||||
var vers = o.EncryptionVersionInfo = parse_Version(blob, 4); length -= 4;
|
||||
var vers = o.EncryptionVersionInfo = parse_CRYPTOVersion(blob, 4); length -= 4;
|
||||
if(vers.Major != 1 || vers.Minor != 1) throw 'unrecognized version code ' + vers.Major + ' : ' + vers.Minor;
|
||||
o.Salt = blob.read_shift(16);
|
||||
o.EncryptedVerifier = blob.read_shift(16);
|
||||
@ -9617,6 +9753,18 @@ function col_obj_w(C, col) {
|
||||
return p;
|
||||
}
|
||||
|
||||
function default_margins(margins, mode) {
|
||||
if(!margins) return;
|
||||
var defs = [0.7, 0.7, 0.75, 0.75, 0.3, 0.3];
|
||||
if(mode == 'xlml') defs = [1, 1, 1, 1, 0.5, 0.5];
|
||||
if(margins.left == null) margins.left = defs[0];
|
||||
if(margins.right == null) margins.right = defs[1];
|
||||
if(margins.top == null) margins.top = defs[2];
|
||||
if(margins.bottom == null) margins.bottom = defs[3];
|
||||
if(margins.header == null) margins.header = defs[4];
|
||||
if(margins.footer == null) margins.footer = defs[5];
|
||||
}
|
||||
|
||||
function get_cell_style(styles, cell, opts) {
|
||||
var z = opts.revssf[cell.z != null ? cell.z : "General"];
|
||||
for(var i = 0, len = styles.length; i != len; ++i) if(styles[i].numFmtId === z) return i;
|
||||
@ -9675,6 +9823,7 @@ var hlinkregex = /<(?:\w:)?hyperlink [^>]*>/mg;
|
||||
var dimregex = /"(\w*:\w*)"/;
|
||||
var colregex = /<(?:\w:)?col[^>]*[\/]?>/g;
|
||||
var afregex = /<(?:\w:)?autoFilter[^>]*([\/]|>([^\u2603]*)<\/(?:\w:)?autoFilter)>/g;
|
||||
var marginregex= /<(?:\w:)?pageMargins[^>]*\/>/g;
|
||||
/* 18.3 Worksheets */
|
||||
function parse_ws_xml(data, opts, rels, wb, themes, styles) {
|
||||
if(!data) return data;
|
||||
@ -9724,6 +9873,10 @@ function parse_ws_xml(data, opts, rels, wb, themes, styles) {
|
||||
var hlink = data2.match(hlinkregex);
|
||||
if(hlink) parse_ws_xml_hlinks(s, hlink, rels);
|
||||
|
||||
/* 18.3.1.62 pageMargins CT_PageMargins */
|
||||
var margins = data2.match(marginregex);
|
||||
if(margins) s['!margins'] = parse_ws_xml_margins(parsexmltag(margins[0]));
|
||||
|
||||
if(!s["!ref"] && refguess.e.c >= refguess.s.c && refguess.e.r >= refguess.s.r) s["!ref"] = encode_range(refguess);
|
||||
if(opts.sheetRows > 0 && s["!ref"]) {
|
||||
var tmpref = safe_decode_range(s["!ref"]);
|
||||
@ -9798,6 +9951,14 @@ function parse_ws_xml_hlinks(s, data, rels) {
|
||||
}
|
||||
}
|
||||
|
||||
function parse_ws_xml_margins(margin) {
|
||||
var o = {};
|
||||
["left", "right", "top", "bottom", "header", "footer"].forEach(function(k) {
|
||||
if(margin[k]) o[k] = parseFloat(margin[k]);
|
||||
});
|
||||
return o;
|
||||
}
|
||||
|
||||
function parse_ws_xml_cols(columns, cols) {
|
||||
var seencol = false;
|
||||
for(var coli = 0; coli != cols.length; ++coli) {
|
||||
@ -10434,6 +10595,29 @@ function write_BrtColInfo(C, col, o) {
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.4.672 BrtMargins */
|
||||
function parse_BrtMargins(data, length, opts) {
|
||||
return {
|
||||
left: parse_Xnum(data, 8),
|
||||
right: parse_Xnum(data, 8),
|
||||
top: parse_Xnum(data, 8),
|
||||
bottom: parse_Xnum(data, 8),
|
||||
header: parse_Xnum(data, 8),
|
||||
footer: parse_Xnum(data, 8)
|
||||
};
|
||||
}
|
||||
function write_BrtMargins(margins, o) {
|
||||
if(o == null) o = new_buf(6*8);
|
||||
default_margins(margins);
|
||||
write_Xnum(margins.left, o);
|
||||
write_Xnum(margins.right, o);
|
||||
write_Xnum(margins.top, o);
|
||||
write_Xnum(margins.bottom, o);
|
||||
write_Xnum(margins.header, o);
|
||||
write_Xnum(margins.footer, o);
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.4.740 BrtSheetProtection */
|
||||
function write_BrtSheetProtection(sp, o) {
|
||||
if(o == null) o = new_buf(16*4+2);
|
||||
@ -10612,6 +10796,10 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles) {
|
||||
s['!autofilter'] = { ref:encode_range(val) };
|
||||
break;
|
||||
|
||||
case 0x01DC: /* 'BrtMargins' */
|
||||
s['!margins'] = val;
|
||||
break;
|
||||
|
||||
case 0x00AF: /* 'BrtAFilterDateGroupItem' */
|
||||
case 0x0284: /* 'BrtActiveX' */
|
||||
case 0x0271: /* 'BrtBigName' */
|
||||
@ -10643,7 +10831,6 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles) {
|
||||
case 0x0227: /* 'BrtLegacyDrawing' */
|
||||
case 0x0228: /* 'BrtLegacyDrawingHF' */
|
||||
case 0x0295: /* 'BrtListPart' */
|
||||
case 0x01DC: /* 'BrtMargins' */
|
||||
case 0x027F: /* 'BrtOleObject' */
|
||||
case 0x01DE: /* 'BrtPageSetup' */
|
||||
case 0x0097: /* 'BrtPane' */
|
||||
@ -10849,7 +11036,7 @@ function write_ws_bin(idx, opts, wb, rels) {
|
||||
/* [DVALS] */
|
||||
write_HLINKS(ba, ws, rels);
|
||||
/* [BrtPrintOptions] */
|
||||
/* [BrtMargins] */
|
||||
if(ws['!margins']) write_record(ba, "BrtMargins", write_BrtMargins(ws['!margins']));
|
||||
/* [BrtPageSetup] */
|
||||
/* [HEADERFOOTER] */
|
||||
/* [RWBRK] */
|
||||
@ -12185,6 +12372,22 @@ for(var cma = c; cma <= cc; ++cma) {
|
||||
}
|
||||
else pidx = Rn.index + Rn[0].length;
|
||||
break;
|
||||
case 'Header':
|
||||
if(!cursheet['!margins']) default_margins(cursheet['!margins']={}, 'xlml');
|
||||
cursheet['!margins'].header = parsexmltag(Rn[0]).Margin;
|
||||
break;
|
||||
case 'Footer':
|
||||
if(!cursheet['!margins']) default_margins(cursheet['!margins']={}, 'xlml');
|
||||
cursheet['!margins'].footer = parsexmltag(Rn[0]).Margin;
|
||||
break;
|
||||
case 'PageMargins':
|
||||
var pagemargins = parsexmltag(Rn[0]);
|
||||
if(!cursheet['!margins']) default_margins(cursheet['!margins']={},'xlml');
|
||||
if(pagemargins.Top) cursheet['!margins'].top = pagemargins.Top;
|
||||
if(pagemargins.Left) cursheet['!margins'].left = pagemargins.Left;
|
||||
if(pagemargins.Right) cursheet['!margins'].right = pagemargins.Right;
|
||||
if(pagemargins.Bottom) cursheet['!margins'].bottom = pagemargins.Bottom;
|
||||
break;
|
||||
case 'Unsynced': break;
|
||||
case 'Print': break;
|
||||
case 'Panes': break;
|
||||
@ -12192,10 +12395,7 @@ for(var cma = c; cma <= cc; ++cma) {
|
||||
case 'Pane': break;
|
||||
case 'Number': break;
|
||||
case 'Layout': break;
|
||||
case 'Header': break;
|
||||
case 'Footer': break;
|
||||
case 'PageSetup': break;
|
||||
case 'PageMargins': break;
|
||||
case 'Selected': break;
|
||||
case 'ProtectObjects': break;
|
||||
case 'EnableSelection': break;
|
||||
@ -13114,12 +13314,30 @@ function parse_workbook(blob, options) {
|
||||
} break;
|
||||
case 'Row': break; // TODO
|
||||
|
||||
case 'LeftMargin':
|
||||
case 'RightMargin':
|
||||
case 'TopMargin':
|
||||
case 'BottomMargin':
|
||||
if(!out['!margins']) default_margins(out['!margins'] = {});
|
||||
switch(Rn) {
|
||||
case 'LeftMargin': out['!margins'].left = val; break;
|
||||
case 'RightMargin': out['!margins'].right = val; break;
|
||||
case 'TopMargin': out['!margins'].top = val; break;
|
||||
case 'BottomMargin': out['!margins'].bottom = val; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Setup': // TODO
|
||||
if(!out['!margins']) default_margins(out['!margins'] = {});
|
||||
out['!margins'].header = val.header;
|
||||
out['!margins'].footer = val.footer;
|
||||
break;
|
||||
|
||||
case 'Header': break; // TODO
|
||||
case 'Footer': break; // TODO
|
||||
case 'HCenter': break; // TODO
|
||||
case 'VCenter': break; // TODO
|
||||
case 'Pls': break; // TODO
|
||||
case 'Setup': break; // TODO
|
||||
case 'GCW': break;
|
||||
case 'LHRecord': break;
|
||||
case 'DBCell': break; // TODO
|
||||
@ -13314,7 +13532,6 @@ function parse_workbook(blob, options) {
|
||||
case 'WebPub': case 'AutoWebPub':
|
||||
|
||||
/* Print Stuff */
|
||||
case 'RightMargin': case 'LeftMargin': case 'TopMargin': case 'BottomMargin':
|
||||
case 'HeaderFooter': case 'HFPicture': case 'PLV':
|
||||
case 'HorizontalPageBreaks': case 'VerticalPageBreaks':
|
||||
/* Behavioral */
|
||||
@ -13367,7 +13584,6 @@ fix_read_opts(options);
|
||||
reset_cp();
|
||||
var CompObj, Summary, Workbook;
|
||||
if(cfb.FullPaths) {
|
||||
if(cfb.find("EncryptedPackage")) throw new Error("File is password-protected");
|
||||
CompObj = cfb.find('!CompObj');
|
||||
Summary = cfb.find('!SummaryInformation');
|
||||
Workbook = cfb.find('/Workbook');
|
||||
@ -13820,7 +14036,7 @@ var XLSBRecordEnum = {
|
||||
0x01D9: { n:"BrtBeginColorPalette", f:parsenoop },
|
||||
0x01DA: { n:"BrtEndColorPalette", f:parsenoop },
|
||||
0x01DB: { n:"BrtIndexedColor", f:parsenoop },
|
||||
0x01DC: { n:"BrtMargins", f:parsenoop },
|
||||
0x01DC: { n:"BrtMargins", f:parse_BrtMargins },
|
||||
0x01DD: { n:"BrtPrintOptions", f:parsenoop },
|
||||
0x01DE: { n:"BrtPageSetup", f:parsenoop },
|
||||
0x01DF: { n:"BrtBeginHeaderFooter", f:parsenoop },
|
||||
@ -15630,6 +15846,44 @@ function parse_zip(zip, opts) {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/* references to [MS-OFFCRYPTO] */
|
||||
function parse_xlsxcfb(cfb, opts) {
|
||||
var f = 'Version';
|
||||
var data = cfb.find(f);
|
||||
if(!data) throw new Error("ECMA-376 Encrypted file missing " + f);
|
||||
var version = parse_DataSpaceVersionInfo(data.content);
|
||||
|
||||
/* 2.3.4.1 */
|
||||
f = 'DataSpaceMap';
|
||||
data = cfb.find(f);
|
||||
if(!data) throw new Error("ECMA-376 Encrypted file missing " + f);
|
||||
var dsm = parse_DataSpaceMap(data.content);
|
||||
if(dsm.length != 1 || dsm[0].comps.length != 1 || dsm[0].comps[0].t != 0 ||
|
||||
dsm[0].name != "StrongEncryptionDataSpace" || dsm[0].comps[0].v != "EncryptedPackage")
|
||||
throw new Error("ECMA-376 Encrypted file bad " + f);
|
||||
|
||||
f = 'StrongEncryptionDataSpace';
|
||||
data = cfb.find(f);
|
||||
if(!data) throw new Error("ECMA-376 Encrypted file missing " + f);
|
||||
var seds = parse_DataSpaceDefinition(data.content);
|
||||
if(seds.length != 1 || seds[0] != "StrongEncryptionTransform")
|
||||
throw new Error("ECMA-376 Encrypted file bad " + f);
|
||||
|
||||
/* 2.3.4.3 */
|
||||
f = '!Primary';
|
||||
data = cfb.find(f);
|
||||
if(!data) throw new Error("ECMA-376 Encrypted file missing " + f);
|
||||
var hdr = parse_Primary(data.content);
|
||||
|
||||
f = 'EncryptionInfo';
|
||||
data = cfb.find(f);
|
||||
if(!data) throw new Error("ECMA-376 Encrypted file missing " + f);
|
||||
var einfo = parse_EncryptionInfo(data.content);
|
||||
|
||||
throw new Error("File is password-protected");
|
||||
}
|
||||
|
||||
function write_zip(wb, opts) {
|
||||
_shapeid = 1024;
|
||||
if(opts.bookType == "ods") return write_ods(wb, opts);
|
||||
@ -15770,6 +16024,11 @@ function firstbyte(f,o) {
|
||||
return [x.charCodeAt(0), x.charCodeAt(1), x.charCodeAt(2), x.charCodeAt(3)];
|
||||
}
|
||||
|
||||
function read_cfb(cfb, opts) {
|
||||
if(cfb.find("EncryptedPackage")) return parse_xlsxcfb(cfb, opts);
|
||||
return parse_xlscfb(cfb, opts);
|
||||
}
|
||||
|
||||
function read_zip(data, opts) {
|
||||
var zip, d = data;
|
||||
var o = opts||{};
|
||||
@ -15798,7 +16057,7 @@ function readSync(data, opts) {
|
||||
if(!o.type) o.type = (has_buf && Buffer.isBuffer(data)) ? "buffer" : "base64";
|
||||
if(o.type == "file") { o.type = "buffer"; d = _fs.readFileSync(data); }
|
||||
switch((n = firstbyte(d, o))[0]) {
|
||||
case 0xD0: return parse_xlscfb(CFB.read(d, o), o);
|
||||
case 0xD0: return read_cfb(CFB.read(d, o), o);
|
||||
case 0x09: return parse_xlscfb(s2a(o.type === 'base64' ? Base64.decode(d) : d), o);
|
||||
case 0x3C: return parse_xlml(d, o);
|
||||
case 0x49: if(n[1] == 0x44) return SYLK.to_workbook(d, o); break;
|
||||
|
Loading…
Reference in New Issue
Block a user