version bump 0.11.4: BIFF8 XLS write

- xlsx bin script takes `-8, --xls` options for writing BIFF8
- updated CFB to 0.12.1, CRC32 to 1.1.1
- test file spelling error (h/t @jsoref)
- minified script renames write_shift / read_shift
- UTF8 and XML entity processing

Issues:
- fixes #815 h/t @Neroth
- fixes #739 h/t @LittleBreak @PWDream
- fixes #553 h/t @keyiis
- fixes #492 h/t @FlyingSailor @simonchan2013
This commit is contained in:
SheetJS 2017-09-22 18:18:51 -04:00
parent f03e32fc9a
commit d02650055d
45 changed files with 7607 additions and 6374 deletions

1
.gitignore vendored

@ -20,6 +20,7 @@ tmp
*.[uU][oO][sS]
*.[wW][kKqQbB][S1234567890]
*.[qQ][pP][wW]
*.[bB][iI][fF][fF][23458]
*.123
*.htm
*.html

@ -22,6 +22,7 @@ tmp
*.[uU][oO][sS]
*.[wW][kKqQbB][S1234567890]
*.[qQ][pP][wW]
*.[bB][iI][fF][fF][23458]
*.123
*.htm
*.html

@ -1564,7 +1564,8 @@ output formats. The specific file type is controlled with `bookType` option:
| `xlsx` | `.xlsx` | ZIP | multi | Excel 2007+ XML Format |
| `xlsm` | `.xlsm` | ZIP | multi | Excel 2007+ Macro XML Format |
| `xlsb` | `.xlsb` | ZIP | multi | Excel 2007+ Binary Format |
| `biff2` | `.xls` | none | single | Excel 2.0 Worksheet format |
| `biff8` | `.xls` | CFB | multi | Excel 97-2004 Workbook Format |
| `biff2` | `.xls` | none | single | Excel 2.0 Worksheet Format |
| `xlml` | `.xls` | none | multi | Excel 2003-2004 (SpreadsheetML) |
| `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet |
| `fods` | `.fods` | none | multi | Flat OpenDocument Spreadsheet |
@ -1887,7 +1888,7 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file 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 97-2004 (XLS BIFF8) | :o: | :o: |
| Excel 5.0/95 (XLS BIFF5) | :o: | |
| Excel 4.0 (XLS/XLW BIFF4) | :o: | |
| Excel 3.0 (XLS BIFF3) | :o: | |

@ -20,6 +20,10 @@ program
.option('-M, --xlsm', 'emit XLSM to <sheetname> or <file>.xlsm')
.option('-X, --xlsx', 'emit XLSX to <sheetname> or <file>.xlsx')
.option('-Y, --ods', 'emit ODS to <sheetname> or <file>.ods')
.option('-8, --xls', 'emit XLS to <sheetname> or <file>.xls (BIFF8)')
//.option('-5, --biff5','emit XLS to <sheetname> or <file>.xls (BIFF5)')
//.option('-4, --biff4','emit XLS to <sheetname> or <file>.xls (BIFF4)')
//.option('-3, --biff3','emit XLS to <sheetname> or <file>.xls (BIFF3)')
.option('-2, --biff2','emit XLS to <sheetname> or <file>.xls (BIFF2)')
.option('-6, --xlml', 'emit SSML to <sheetname> or <file>.xls (2003 XML)')
.option('-T, --fods', 'emit FODS to <sheetname> or <file>.fods (Flat ODS)')
@ -52,15 +56,22 @@ program.on('--help', function() {
console.log(' Web Demo: http://oss.sheetjs.com/js-'+n+'/');
});
/* output formats, update list with full option name */
var workbook_formats = ['xlsx', 'xlsm', 'xlsb', 'ods', 'fods'];
/* flag, bookType, default ext */
var workbook_formats = [
['xlsx', 'xlsx', 'xlsx'],
['xlsm', 'xlsm', 'xlsm'],
['xlsb', 'xlsb', 'xlsb'],
['xls', 'xls', 'xls'],
//['biff5', 'biff5', 'xls'],
['ods', 'ods', 'ods'],
['fods', 'fods', 'fods']
];
var wb_formats_2 = [
['xlml', 'xlml', 'xls']
['xlml', 'xlml', 'xls']
];
program.parse(process.argv);
var filename = "", sheetname = '';
var filename = '', sheetname = '';
if(program.args[0]) {
filename = program.args[0];
if(program.args[1]) sheetname = program.args[1];
@ -89,13 +100,12 @@ function wb_fmt() {
opts.cellNF = true;
if(program.output) sheetname = program.output;
}
function isfmt(m) {
function isfmt(m/*:string*/)/*:boolean*/ {
if(!program.output) return false;
var t = m.charAt(0) == "." ? m : "." + m;
console.log(m);
return program.output.slice(-m.length) == m;
var t = m.charAt(0) === "." ? m : "." + m;
return program.output.slice(-t.length) === t;
}
workbook_formats.forEach(function(m) { if(program[m] || isfmt(m)) { wb_fmt(); } });
workbook_formats.forEach(function(m) { if(program[m[0]] || isfmt(m[0])) { wb_fmt(); } });
wb_formats_2.forEach(function(m) { if(program[m[0]] || isfmt(m[0])) { wb_fmt(); } });
if(seen) {
} else if(program.formulae) opts.cellFormula = true;
@ -135,8 +145,9 @@ var wopts = ({WTF:opts.WTF, bookSST:program.sst}/*:any*/);
if(program.compress) wopts.compression = true;
/* full workbook formats */
workbook_formats.forEach(function(m) { if(program[m] || isfmt(m)) {
X.writeFile(wb, program.output || sheetname || ((filename || "") + "." + m), wopts);
workbook_formats.forEach(function(m) { if(program[m[0]] || isfmt(m[0])) {
wopts.bookType = m[1];
X.writeFile(wb, program.output || sheetname || ((filename || "") + "." + m[2]), wopts);
process.exit(0);
} });
@ -169,6 +180,8 @@ if(program.readOnly) process.exit(0);
/* single worksheet formats */
[
['biff2', '.xls'],
//['biff3', '.xls'],
//['biff4', '.xls'],
['sylk', '.slk'],
['html', '.html'],
['prn', '.prn'],
@ -180,8 +193,7 @@ if(program.readOnly) process.exit(0);
process.exit(0);
} });
var oo = "";
var strm = false;
var oo = "", strm = false;
if(!program.quiet) console.error(target_sheet);
if(program.formulae) oo = X.utils.sheet_to_formulae(ws).join("\n");
else if(program.json) oo = JSON.stringify(X.utils.sheet_to_json(ws));

@ -1 +1 @@
XLSX.version = '0.11.3';
XLSX.version = '0.11.4';

@ -12,10 +12,13 @@ declare var bconcat:any;
declare var s2a:any;
declare var chr0:any;
declare var chr1:any;
declare var new_buf:any;
*/
/* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*jshint eqnull:true */
/*exported CFB */
/*global module, require:false, process:false, Buffer:false, Uint8Array:false */
/*::
declare var DO_NOT_EXPORT_CFB:?boolean;
@ -35,15 +38,35 @@ type CFBFiles = {[n:string]:CFBEntry};
/* [MS-CFB] v20130118 */
var CFB = (function _CFB(){
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
exports.version = '0.12.1';
exports.version = '0.13.1';
/* [MS-CFB] 2.6.4 */
function namecmp(l/*:string*/, r/*:string*/)/*:number*/ {
var L = l.split("/"), R = r.split("/");
for(var i = 0, c = 0, Z = Math.min(L.length, R.length); i < Z; ++i) {
if((c = L[i].length - R[i].length)) return c;
if(L[i] != R[i]) return L[i] < R[i] ? -1 : 1;
}
return L.length - R.length;
}
function dirname(p/*:string*/)/*:string*/ {
if(p.charAt(p.length - 1) == "/") return (p.slice(0,-1).indexOf("/") === -1) ? p : dirname(p.slice(0, -1));
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(0, c+1);
}
function filename(p/*:string*/)/*:string*/ {
if(p.charAt(p.length - 1) == "/") return filename(p.slice(0, -1));
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1);
}
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
var mver = 3; // major version
var ssz = 512; // sector size
var mver = 3;
var ssz = 512;
var nmfs = 0; // number of mini FAT sectors
var ndfs = 0; // number of DIFAT sectors
var dir_start = 0; // first directory sector location
var minifat_start = 0; // first mini FAT sector location
var difat_start = 0; // first mini FAT sector location
var difat_sec_cnt = 0;
var dir_start = 0;
var minifat_start = 0;
var difat_start = 0;
var fat_addrs/*:Array<number>*/ = []; // locations of FAT sectors
@ -67,11 +90,10 @@ var header/*:RawBytes*/ = file.slice(0,ssz);
check_shifts(blob, mver);
// Number of Directory Sectors
var nds/*:number*/ = blob.read_shift(4, 'i');
if(mver === 3 && nds !== 0) throw new Error('# Directory Sectors: Expected 0 saw ' + nds);
var dir_cnt/*:number*/ = blob.read_shift(4, 'i');
if(mver === 3 && dir_cnt !== 0) throw new Error('# Directory Sectors: Expected 0 saw ' + dir_cnt);
// Number of FAT Sectors
//var nfs = blob.read_shift(4, 'i');
blob.l += 4;
// First Directory Sector Location
@ -93,7 +115,7 @@ nmfs = blob.read_shift(4, 'i');
difat_start = blob.read_shift(4, 'i');
// Number of DIFAT Sectors
ndfs = blob.read_shift(4, 'i');
difat_sec_cnt = blob.read_shift(4, 'i');
// Grab FAT Sector Locations
for(var q = -1, j = 0; j < 109; ++j) { /* 109 = (512 - blob.l)>>>2; */
@ -105,7 +127,7 @@ for(var q = -1, j = 0; j < 109; ++j) { /* 109 = (512 - blob.l)>>>2; */
/** Break the file up into sectors */
var sectors/*:Array<RawBytes>*/ = sectorify(file, ssz);
sleuth_fat(difat_start, ndfs, sectors, ssz, fat_addrs);
sleuth_fat(difat_start, difat_sec_cnt, sectors, ssz, fat_addrs);
/** Chains */
var sector_list/*:SectorList*/ = make_sector_list(sectors, dir_start, fat_addrs, ssz);
@ -121,19 +143,17 @@ var files/*:CFBFiles*/ = {}, Paths/*:Array<string>*/ = [], FileIndex/*:CFBFileIn
read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, FileIndex);
build_full_paths(FileIndex, FullPathDir, FullPaths, Paths);
Paths.shift();
var root_name/*:string*/ = Paths.shift();
/* [MS-CFB] 2.6.4 (Unicode 3.0.1 case conversion) */
var find_path = make_find_path(FullPaths, Paths, FileIndex, files, root_name);
return {
raw: {header: header, sectors: sectors},
var o = {
FileIndex: FileIndex,
FullPaths: FullPaths,
FullPathDir: FullPathDir,
find: find_path
FullPathDir: FullPathDir
};
// $FlowIgnore
if(options && options.raw) o.raw = {header: header, sectors: sectors};
return o;
} // parse
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
@ -220,25 +240,8 @@ function build_full_paths(FI/*:CFBFileIndex*/, FPD/*:CFBFullPathDir*/, FP/*:Arra
}
}
/* [MS-CFB] 2.6.4 */
function make_find_path(FullPaths/*:Array<string>*/, Paths/*:Array<string>*/, FileIndex/*:CFBFileIndex*/, files/*:CFBFiles*/, root_name/*:string*/)/*:CFBFindPath*/ {
var UCFullPaths/*:Array<string>*/ = [];
var UCPaths/*:Array<string>*/ = [], i = 0;
for(i = 0; i < FullPaths.length; ++i) UCFullPaths[i] = FullPaths[i].toUpperCase().replace(chr0,'').replace(chr1,'!');
for(i = 0; i < Paths.length; ++i) UCPaths[i] = Paths[i].toUpperCase().replace(chr0,'').replace(chr1,'!');
return function find_path(path/*:string*/)/*:?CFBEntry*/ {
var k/*:boolean*/ = false;
if(path.charCodeAt(0) === 47 /* "/" */) { k=true; path = root_name + path; }
else k = path.indexOf("/") !== -1;
var UCPath/*:string*/ = path.toUpperCase().replace(chr0,'').replace(chr1,'!');
var w/*:number*/ = k === true ? UCFullPaths.indexOf(UCPath) : UCPaths.indexOf(UCPath);
if(w === -1) return null;
return k === true ? FileIndex[w] : files[Paths[w]];
};
}
/** Chase down the rest of the DIFAT chain to build a comprehensive list
DIFAT chains by storing the next sector number as the last 32 bytes */
DIFAT chains by storing the next sector number as the last 32 bits */
function sleuth_fat(idx/*:number*/, cnt/*:number*/, sectors/*:Array<RawBytes>*/, ssz/*:number*/, fat_addrs)/*:void*/ {
var q/*:number*/ = ENDOFCHAIN;
if(idx === ENDOFCHAIN) {
@ -256,7 +259,6 @@ function sleuth_fat(idx/*:number*/, cnt/*:number*/, sectors/*:Array<RawBytes>*/,
/** Follow the linked list of sectors for a given starting point */
function get_sector_list(sectors/*:Array<RawBytes>*/, start/*:number*/, fat_addrs/*:Array<number>*/, ssz/*:number*/, chkd/*:?Array<boolean>*/)/*:SectorEntry*/ {
var sl = sectors.length;
var buf/*:Array<number>*/ = [], buf_chain/*:Array<any>*/ = [];
if(!chkd) chkd = [];
var modulus = ssz - 1, j = 0, jj = 0;
@ -355,22 +357,259 @@ function read_date(blob/*:RawBytes|CFBlob*/, offset/*:number*/)/*:Date*/ {
}
var fs/*:: = require('fs'); */;
function readFileSync(filename/*:string*/, options/*:CFBReadOpts*/) {
function read_file(filename/*:string*/, options/*:CFBReadOpts*/) {
if(fs == null) fs = require('fs');
return parse(fs.readFileSync(filename), options);
}
function readSync(blob/*:RawBytes|string*/, options/*:CFBReadOpts*/) {
function read(blob/*:RawBytes|string*/, options/*:CFBReadOpts*/) {
switch(options && options.type || "base64") {
case "file": /*:: if(typeof blob !== 'string') throw "Must pass a filename when type='file'"; */return readFileSync(blob, options);
case "file": /*:: if(typeof blob !== 'string') throw "Must pass a filename when type='file'"; */return read_file(blob, options);
case "base64": /*:: if(typeof blob !== 'string') throw "Must pass a base64-encoded binary string when type='file'"; */return parse(s2a(Base64.decode(blob)), options);
case "binary": /*:: if(typeof blob !== 'string') throw "Must pass a binary string when type='file'"; */return parse(s2a(blob), options);
}
return parse(/*::typeof blob == 'string' ? new Buffer(blob, 'utf-8') : */blob, options);
}
function init_cfb(cfb/*:CFBContainer*/, opts/*:?any*/)/*:void*/ {
var o = opts || {}, root = o.root || "Root Entry";
if(!cfb.FullPaths) cfb.FullPaths = [];
if(!cfb.FileIndex) cfb.FileIndex = [];
if(cfb.FullPaths.length !== cfb.FileIndex.length) throw new Error("inconsistent CFB structure");
if(cfb.FullPaths.length === 0) {
cfb.FullPaths[0] = root + "/";
cfb.FileIndex[0] = ({ name: root, type: 5 }/*:any*/);
}
if(o.CLSID) cfb.FileIndex[0].clsid = o.CLSID;
seed_cfb(cfb);
}
function seed_cfb(cfb/*:CFBContainer*/)/*:void*/ {
var nm = "\u0001Sh33tJ5";
if(CFB.find(cfb, "/" + nm)) return;
var p = new_buf(4); p[0] = 55; p[1] = p[3] = 50; p[2] = 54;
cfb.FileIndex.push(({ name: nm, type: 2, content:p, size:4, L:69, R:69, C:69 }/*:any*/));
cfb.FullPaths.push(cfb.FullPaths[0] + nm);
rebuild_cfb(cfb);
}
function rebuild_cfb(cfb/*:CFBContainer*/, f/*:?boolean*/)/*:void*/ {
init_cfb(cfb);
var gc = false, s = false;
for(var i = cfb.FullPaths.length - 1; i >= 0; --i) {
var _file = cfb.FileIndex[i];
switch(_file.type) {
case 0:
if(s) gc = true;
else { cfb.FileIndex.pop(); cfb.FullPaths.pop(); }
break;
case 1: case 2: case 5:
s = true;
if(isNaN(_file.R * _file.L * _file.C)) gc = true;
if(_file.R > -1 && _file.L > -1 && _file.R == _file.L) gc = true;
break;
default: gc = true; break;
}
}
if(!gc && !f) return;
var now = new Date(1987, 1, 19), j = 0;
var data/*:Array<[string, CFBEntry]>*/ = [];
for(i = 0; i < cfb.FullPaths.length; ++i) {
if(cfb.FileIndex[i].type === 0) continue;
data.push([cfb.FullPaths[i], cfb.FileIndex[i]]);
}
for(i = 0; i < data.length; ++i) {
var dad = dirname(data[i][0]);
s = false;
for(j = 0; j < data.length; ++j) if(data[j][0] === dad) s = true;
if(!s) data.push([dad, ({
name: filename(dad).replace("/",""),
type: 1,
clsid: HEADER_CLSID,
ct: now, mt: now,
content: null
}/*:any*/)]);
}
data.sort(function(x,y) { return namecmp(x[0], y[0]); });
cfb.FullPaths = []; cfb.FileIndex = [];
for(i = 0; i < data.length; ++i) { cfb.FullPaths[i] = data[i][0]; cfb.FileIndex[i] = data[i][1]; }
for(i = 0; i < data.length; ++i) {
var elt = cfb.FileIndex[i];
var nm = cfb.FullPaths[i];
elt.name = filename(nm).replace("/","");
elt.L = elt.R = elt.C = -(elt.color = 1);
elt.size = elt.content ? elt.content.length : 0;
elt.start = 0;
elt.clsid = (elt.clsid || HEADER_CLSID);
if(i === 0) {
elt.C = data.length > 1 ? 1 : -1;
elt.size = 0;
elt.type = 5;
} else if(nm.slice(-1) == "/") {
for(j=i+1;j < data.length; ++j) if(dirname(cfb.FullPaths[j])==nm) break;
elt.C = j >= data.length ? -1 : j;
for(j=i+1;j < data.length; ++j) if(dirname(cfb.FullPaths[j])==dirname(nm)) break;
elt.R = j >= data.length ? -1 : j;
elt.type = 1;
} else {
if(dirname(cfb.FullPaths[i+1]||"") == dirname(nm)) elt.R = i + 1;
elt.type = 2;
}
}
}
function _write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
rebuild_cfb(cfb);
var L = (function(cfb/*:CFBContainer*/)/*:Array<number>*/{
var mini_size = 0, fat_size = 0;
for(var i = 0; i < cfb.FileIndex.length; ++i) {
var file = cfb.FileIndex[i];
if(!file.content) continue;
/*:: if(file.content == null) throw new Error("unreachable"); */
var flen = file.content.length;
if(flen === 0){}
else if(flen < 0x1000) mini_size += (flen + 0x3F) >> 6;
else fat_size += (flen + 0x01FF) >> 9;
}
var dir_cnt = (cfb.FullPaths.length +3) >> 2;
var mini_cnt = (mini_size + 7) >> 3;
var mfat_cnt = (mini_size + 0x7F) >> 7;
var fat_base = mini_cnt + fat_size + dir_cnt + mfat_cnt;
var fat_cnt = (fat_base + 0x7F) >> 7;
var difat_cnt = fat_cnt <= 109 ? 0 : Math.ceil((fat_cnt-109)/0x7F);
while(((fat_base + fat_cnt + difat_cnt + 0x7F) >> 7) > fat_cnt) difat_cnt = ++fat_cnt <= 109 ? 0 : Math.ceil((fat_cnt-109)/0x7F);
var L = [1, difat_cnt, fat_cnt, mfat_cnt, dir_cnt, fat_size, mini_size, 0];
cfb.FileIndex[0].size = mini_size << 6;
L[7] = (cfb.FileIndex[0].start=L[0]+L[1]+L[2]+L[3]+L[4]+L[5])+((L[6]+7) >> 3);
return L;
})(cfb);
var o = new_buf(L[7] << 9);
var i = 0, T = 0;
{
for(i = 0; i < 8; ++i) o.write_shift(1, HEADER_SIG[i]);
for(i = 0; i < 8; ++i) o.write_shift(2, 0);
o.write_shift(2, 0x003E);
o.write_shift(2, 0x0003);
o.write_shift(2, 0xFFFE);
o.write_shift(2, 0x0009);
o.write_shift(2, 0x0006);
for(i = 0; i < 3; ++i) o.write_shift(2, 0);
o.write_shift(4, 0);
o.write_shift(4, L[2]);
o.write_shift(4, L[0] + L[1] + L[2] + L[3] - 1);
o.write_shift(4, 0);
o.write_shift(4, 1<<12);
o.write_shift(4, L[3] ? L[0] + L[1] + L[2] - 1: ENDOFCHAIN);
o.write_shift(4, L[3]);
o.write_shift(-4, L[1] ? L[0] - 1: ENDOFCHAIN);
o.write_shift(4, L[1]);
for(i = 0; i < 109; ++i) o.write_shift(-4, i < L[2] ? L[1] + i : -1);
}
if(L[1]) {
for(T = 0; T < L[1]; ++T) {
for(; i < 236 + T * 127; ++i) o.write_shift(-4, i < L[2] ? L[1] + i : -1);
o.write_shift(-4, T === L[1] - 1 ? ENDOFCHAIN : T + 1);
}
}
var chainit = function(w/*:number*/)/*:void*/ {
for(T += w; i<T-1; ++i) o.write_shift(-4, i+1);
if(w) { ++i; o.write_shift(-4, ENDOFCHAIN); }
};
T = i = 0;
for(T+=L[1]; i<T; ++i) o.write_shift(-4, consts.DIFSECT);
for(T+=L[2]; i<T; ++i) o.write_shift(-4, consts.FATSECT);
chainit(L[3]);
chainit(L[4]);
var j/*:number*/ = 0, flen/*:number*/ = 0;
var file/*:CFBEntry*/ = cfb.FileIndex[0];
for(; j < cfb.FileIndex.length; ++j) {
file = cfb.FileIndex[j];
if(!file.content) continue;
/*:: if(file.content == null) throw new Error("unreachable"); */
flen = file.content.length;
if(flen < 0x1000) continue;
file.start = T;
chainit((flen + 0x01FF) >> 9);
}
chainit((L[6] + 7) >> 3);
while(o.l & 0x1FF) o.write_shift(-4, consts.ENDOFCHAIN);
T = i = 0;
for(j = 0; j < cfb.FileIndex.length; ++j) {
file = cfb.FileIndex[j];
if(!file.content) continue;
/*:: if(file.content == null) throw new Error("unreachable"); */
flen = file.content.length;
if(!flen || flen >= 0x1000) continue;
file.start = T;
chainit((flen + 0x3F) >> 6);
}
while(o.l & 0x1FF) o.write_shift(-4, consts.ENDOFCHAIN);
for(i = 0; i < L[4]<<2; ++i) {
var nm = cfb.FullPaths[i];
if(!nm || nm.length === 0) {
for(j = 0; j < 17; ++j) o.write_shift(4, 0);
for(j = 0; j < 3; ++j) o.write_shift(4, -1);
for(j = 0; j < 12; ++j) o.write_shift(4, 0);
continue;
}
file = cfb.FileIndex[i];
if(i === 0) file.start = file.size ? file.start - 1 : ENDOFCHAIN;
flen = 2*(file.name.length+1);
o.write_shift(64, file.name, "utf16le");
o.write_shift(2, flen);
o.write_shift(1, file.type);
o.write_shift(1, file.color);
o.write_shift(-4, file.L);
o.write_shift(-4, file.R);
o.write_shift(-4, file.C);
if(!file.clsid) for(j = 0; j < 4; ++j) o.write_shift(4, 0);
else o.write_shift(16, file.clsid, "hex");
o.write_shift(4, file.state || 0);
o.write_shift(4, 0); o.write_shift(4, 0);
o.write_shift(4, 0); o.write_shift(4, 0);
o.write_shift(4, file.start);
o.write_shift(4, file.size); o.write_shift(4, 0);
}
for(i = 1; i < cfb.FileIndex.length; ++i) {
file = cfb.FileIndex[i];
/*:: if(!file.content) throw new Error("unreachable"); */
if(file.size >= 0x1000) {
o.l = (file.start+1) << 9;
for(j = 0; j < file.size; ++j) o.write_shift(1, file.content[j]);
for(; j & 0x1FF; ++j) o.write_shift(1, 0);
}
}
for(i = 1; i < cfb.FileIndex.length; ++i) {
file = cfb.FileIndex[i];
/*:: if(!file.content) throw new Error("unreachable"); */
if(file.size > 0 && file.size < 0x1000) {
for(j = 0; j < file.size; ++j) o.write_shift(1, file.content[j]);
for(; j & 0x3F; ++j) o.write_shift(1, 0);
}
}
while(o.l < o.length) o.write_shift(1, 0);
return o;
}
/* [MS-CFB] 2.6.4 (Unicode 3.0.1 case conversion) */
function find(cfb/*:CFBContainer*/, path/*:string*/)/*:?CFBEntry*/ {
return cfb.find(path);
//return cfb.find(path);
var UCFullPaths/*:Array<string>*/ = cfb.FullPaths.map(function(x) { return x.toUpperCase(); });
var UCPaths/*:Array<string>*/ = UCFullPaths.map(function(x) { var y = x.split("/"); return y[y.length - (x.slice(-1) == "/" ? 2 : 1)]; });
var k/*:boolean*/ = false;
if(path.charCodeAt(0) === 47 /* "/" */) { k = true; path = UCFullPaths[0].slice(0, -1) + path; }
else k = path.indexOf("/") !== -1;
var UCPath/*:string*/ = path.toUpperCase();
var w/*:number*/ = k === true ? UCFullPaths.indexOf(UCPath) : UCPaths.indexOf(UCPath);
if(w !== -1) return cfb.FileIndex[w];
UCPath = UCPath.replace(chr0,'').replace(chr1,'!');
for(w = 0; w < UCFullPaths.length; ++w) {
if(UCFullPaths[w].replace(chr0,'').replace(chr1,'!') == UCPath) return cfb.FileIndex[w];
if(UCPaths[w].replace(chr0,'').replace(chr1,'!') == UCPath) return cfb.FileIndex[w];
}
return null;
}
/** CFB Constants */
var MSSZ = 64; /* Mini Sector Size = 1<<6 */
@ -379,9 +618,10 @@ var MSSZ = 64; /* Mini Sector Size = 1<<6 */
var ENDOFCHAIN = -2;
/* 2.2 Compound File Header */
var HEADER_SIGNATURE = 'd0cf11e0a1b11ae1';
var HEADER_SIG = [0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1];
var HEADER_CLSID = '00000000000000000000000000000000';
var consts = {
/* 2.1 Compound File Sector Numbers and Types */
/* 2.1 Compund File Sector Numbers and Types */
MAXREGSECT: -6,
DIFSECT: -4,
FATSECT: -3,
@ -397,10 +637,92 @@ var consts = {
EntryTypes: ['unknown','storage','stream','lockbytes','property','root']
};
function write_file(cfb/*:CFBContainer*/, filename/*:string*/, options/*:CFBWriteOpts*/)/*:void*/ {
var o = _write(cfb, options);
/*:: if(typeof Buffer == 'undefined' || !Buffer.isBuffer(o) || !(o instanceof Buffer)) throw new Error("unreachable"); */
fs.writeFileSync(filename, o);
}
function a2s(o/*:RawBytes*/)/*:string*/ {
var out = new Array(o.length);
for(var i = 0; i < o.length; ++i) out[i] = String.fromCharCode(o[i]);
return out.join("");
}
function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string*/ {
var o = _write(cfb, options);
switch(options && options.type) {
case "file": fs.writeFileSync(options.filename, (o/*:any*/)); return o;
case "binary": return a2s(o);
case "base64": return Base64.encode(a2s(o));
}
return o;
}
function cfb_new(opts/*:?any*/)/*:CFBContainer*/ {
var o/*:CFBContainer*/ = ({}/*:any*/);
init_cfb(o, opts);
return o;
}
function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, opts/*:?any*/)/*:CFBEntry*/ {
init_cfb(cfb);
var file = CFB.find(cfb, name);
if(!file) {
var fpath = cfb.FullPaths[0];
if(name.slice(0, fpath.length) == fpath) fpath = name;
else {
if(fpath.slice(-1) != "/") fpath += "/";
fpath = (fpath + name).replace("//","/");
}
file = ({name: filename(name)}/*:any*/);
cfb.FileIndex.push(file);
cfb.FullPaths.push(fpath);
CFB.utils.cfb_gc(cfb);
}
/*:: if(!file) throw new Error("unreachable"); */
file.content = (content/*:any*/);
file.size = content ? content.length : 0;
if(opts) {
if(opts.CLSID) file.clsid = opts.CLSID;
}
return file;
}
function cfb_del(cfb/*:CFBContainer*/, name/*:string*/)/*:boolean*/ {
init_cfb(cfb);
var file = CFB.find(cfb, name);
if(file) for(var j = 0; j < cfb.FileIndex.length; ++j) if(cfb.FileIndex[j] == file) {
cfb.FileIndex.splice(j, 1);
cfb.FullPaths.splice(j, 1);
return true;
}
return false;
}
function cfb_mov(cfb/*:CFBContainer*/, old_name/*:string*/, new_name/*:string*/)/*:boolean*/ {
init_cfb(cfb);
var file = CFB.find(cfb, old_name);
if(file) for(var j = 0; j < cfb.FileIndex.length; ++j) if(cfb.FileIndex[j] == file) {
cfb.FileIndex[j].name = filename(new_name);
cfb.FullPaths[j] = new_name;
return true;
}
return false;
}
function cfb_gc(cfb/*:CFBContainer*/)/*:void*/ { rebuild_cfb(cfb, true); }
exports.find = find;
exports.read = readSync;
exports.read = read;
exports.parse = parse;
exports.write = write;
exports.writeFile = write_file;
exports.utils = {
cfb_new: cfb_new,
cfb_add: cfb_add,
cfb_del: cfb_del,
cfb_mov: cfb_mov,
cfb_gc: cfb_gc,
ReadShift: ReadShift,
CheckField: CheckField,
prep_blob: prep_blob,

@ -1,5 +1,7 @@
var XML_HEADER = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n';
var attregexg=/([^"\s?>\/]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:')|([^'">\s]+))/g;
var tagregex=/<[\/\?]?[a-zA-Z0-9:]+(?:\s+[^"\s?>\/]+=(?:"[^"]*"|'[^']*'|[^'">\s]+))*\s?[\/\?]?>/g;
if(!(XML_HEADER.match(tagregex))) tagregex = /<[^>]*>/g;
var nsregex=/<\w*:/, nsregex2 = /<(\/?)\w+:/;
function parsexmltag(tag/*:string*/, skip_root/*:?boolean*/)/*:any*/ {
var z = ({}/*:any*/);
@ -174,7 +176,6 @@ function write_vt(s) {
throw new Error("Unable to serialize " + s);
}
var XML_HEADER = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n';
var XMLNS = ({
'dc': 'http://purl.org/dc/elements/1.1/',
'dcterms': 'http://purl.org/dc/terms/',

@ -13,6 +13,7 @@ function write_double_le(b, v/*:number*/, idx/*:number*/) {
var bs = ((v < 0 || 1/v == -Infinity) ? 1 : 0) << 7, e = 0, m = 0;
var av = bs ? -v : v;
if(!isFinite(av)) { e = 0x7ff; m = isNaN(v) ? 0x6969 : 0; }
else if(av == 0) e = m = 0;
else {
e = Math.floor(Math.log(av) / Math.LN2);
m = av * Math.pow(2, 52 - e);
@ -164,6 +165,20 @@ function WriteShift(t/*:number*/, val/*:string|number*/, f/*:?string*/) {
/*:: if(typeof val !== 'string') throw new Error("unreachable"); */
for(i = 0; i != val.length; ++i) this[this.l + i] = val.charCodeAt(i) & 0xFF;
size = val.length;
} else if(f === 'hex') {
for(; i < t; ++i) {
/*:: if(typeof val !== "string") throw new Error("unreachable"); */
this[this.l++] = parseInt(val.slice(2*i, 2*i+2), 16)||0;
} return this;
} else if(f === 'utf16le') {
var end/*:number*/ = this.l + t;
for(i = 0; i < Math.min(val.length, t); ++i) {
var cc = val.charCodeAt(i);
this[this.l++] = cc & 0xff;
this[this.l++] = cc >> 8;
}
while(this.l < end) this[this.l++] = 0;
return this;
} else /*:: if(typeof val === 'number') */ switch(t) {
case 1: size = 1; this[this.l] = val&0xFF; break;
case 2: size = 2; this[this.l] = val&0xFF; val >>>= 8; this[this.l+1] = val&0xFF; break;

@ -12,7 +12,7 @@ function recordhopper(data, cb/*:RecordHopperCB*/, opts/*:?any*/) {
length = tmpbyte & 0x7F;
for(cntbyte = 1; cntbyte <4 && (tmpbyte & 0x80); ++cntbyte) length += ((tmpbyte = data.read_shift(1)) & 0x7F)<<(7*cntbyte);
tgt = data.l + length;
var d = R.f(data, length, opts);
var d = (R.f||parsenoop)(data, length, opts);
data.l = tgt;
if(cb(d, R.n, RT)) return;
}
@ -53,7 +53,7 @@ function buf_array()/*:BufArray*/ {
}
function write_record(ba/*:BufArray*/, type/*:string*/, payload, length/*:?number*/) {
var t/*:number*/ = Number(evert_RE[type]), l;
var t/*:number*/ = +XLSBRE[type], l;
if(isNaN(t)) return; // TODO: throw something here?
if(!length) length = XLSBRecordEnum[t].p || (payload||[]).length || 0;
l = 1 + (t >= 0x80 ? 1 : 0) + 1 + length;

@ -267,6 +267,7 @@ function parse_PropertySetStream(file, PIDSI) {
function parsenoop2(blob, length) { blob.read_shift(length); return null; }
function writezeroes(n, o) { if(!o) o=new_buf(n); for(var j=0; j<n; ++j) o.write_shift(1, 0); return o; }
function parslurp(blob, length, cb) {
var arr = [], target = blob.l + length;
@ -275,21 +276,26 @@ function parslurp(blob, length, cb) {
return arr;
}
function parsebool(blob, length) { return blob.read_shift(length) === 0x1; }
function parsebool(blob, length/*:number*/) { return blob.read_shift(length) === 0x1; }
function writebool(v/*:any*/, o) { if(!o) o=new_buf(2); o.write_shift(2, +!!v); return o; }
function parseuint16(blob/*::, length:?number, opts:?any*/) { return blob.read_shift(2, 'u'); }
function writeuint16(v/*:number*/, o) { if(!o) o=new_buf(2); o.write_shift(2, v); return o; }
function parseuint16a(blob, length/*:: :?number, opts:?any*/) { return parslurp(blob,length,parseuint16);}
/* --- 2.5 Structures --- */
/* [MS-XLS] 2.5.14 Boolean */
var parse_Boolean = parsebool;
/* [MS-XLS] 2.5.10 Bes (boolean or error) */
function parse_Bes(blob/*::, length*/) {
var v = blob.read_shift(1), t = blob.read_shift(1);
return t === 0x01 ? v : v === 0x01;
}
function write_Bes(v, t/*:string*/, o) {
if(!o) o = new_buf(2);
o.write_shift(1, +v);
o.write_shift(1, t == 'e' ? 1 : 0);
return o;
}
/* [MS-XLS] 2.5.240 ShortXLUnicodeString */
function parse_ShortXLUnicodeString(blob, length, opts) {
@ -366,7 +372,7 @@ function parse_ControlInfo(blob, length, opts) {
}
/* [MS-OSHARED] 2.3.7.6 URLMoniker TODO: flags */
var parse_URLMoniker = function(blob/*::, length, opts*/) {
function parse_URLMoniker(blob/*::, length, opts*/) {
var len = blob.read_shift(4), start = blob.l;
var extra = false;
if(len > 24) {
@ -378,10 +384,10 @@ var parse_URLMoniker = function(blob/*::, length, opts*/) {
var url = blob.read_shift((extra?len-24:len)>>1, 'utf16le').replace(chr0,"");
if(extra) blob.l += 24;
return url;
};
}
/* [MS-OSHARED] 2.3.7.8 FileMoniker TODO: all fields */
var parse_FileMoniker = function(blob, length) {
function parse_FileMoniker(blob, length) {
var cAnti = blob.read_shift(2);
var ansiLength = blob.read_shift(4);
var ansiPath = blob.read_shift(ansiLength, 'cstr');
@ -393,27 +399,27 @@ var parse_FileMoniker = function(blob, length) {
var usKeyValue = blob.read_shift(2);
var unicodePath = blob.read_shift(cbUnicodePathBytes>>1, 'utf16le').replace(chr0,"");
return unicodePath;
};
}
/* [MS-OSHARED] 2.3.7.2 HyperlinkMoniker TODO: all the monikers */
var parse_HyperlinkMoniker = function(blob, length) {
function parse_HyperlinkMoniker(blob, length) {
var clsid = blob.read_shift(16); length -= 16;
switch(clsid) {
case "e0c9ea79f9bace118c8200aa004ba90b": return parse_URLMoniker(blob, length);
case "0303000000000000c000000000000046": return parse_FileMoniker(blob, length);
default: throw new Error("Unsupported Moniker " + clsid);
}
};
}
/* [MS-OSHARED] 2.3.7.9 HyperlinkString */
var parse_HyperlinkString = function(blob, length) {
function parse_HyperlinkString(blob, length) {
var len = blob.read_shift(4);
var o = blob.read_shift(len, 'utf16le').replace(chr0, "");
return o;
};
}
/* [MS-OSHARED] 2.3.7.1 Hyperlink Object TODO: unify params with XLSX */
var parse_Hyperlink = function(blob, length) {
function parse_Hyperlink(blob, length) {
var end = blob.l + length;
var sVer = blob.read_shift(4);
if(sVer !== 2) throw new Error("Unrecognized streamVersion: " + sVer);
@ -431,7 +437,7 @@ var parse_Hyperlink = function(blob, length) {
var target = (targetFrameName||moniker||oleMoniker);
if(location) target+="#"+location;
return {Target: target};
};
}
/* 2.5.178 LongRGBA */
function parse_LongRGBA(blob, length) { var r = blob.read_shift(1), g = blob.read_shift(1), b = blob.read_shift(1), a = blob.read_shift(1); return [r,g,b,a]; }

@ -7,6 +7,13 @@ function parse_XLSCell(blob, length)/*:Cell*/ {
var ixfe = blob.read_shift(2);
return ({r:rw, c:col, ixfe:ixfe}/*:any*/);
}
function write_XLSCell(R/*:number*/, C/*:number*/, ixfe/*:number*/, o) {
if(!o) o = new_buf(6);
o.write_shift(2, R);
o.write_shift(2, C);
o.write_shift(2, ixfe||0);
return o;
}
/* 2.5.134 */
function parse_frtHeader(blob) {
@ -20,10 +27,6 @@ function parse_frtHeader(blob) {
function parse_OptXLUnicodeString(blob, length, opts) { return length === 0 ? "" : parse_XLUnicodeString2(blob, length, opts); }
/* 2.5.158 */
//var HIDEOBJENUM = ['SHOWALL', 'SHOWPLACEHOLDER', 'HIDEALL'];
var parse_HideObjEnum = parseuint16;
/* 2.5.344 */
function parse_XTI(blob, length, opts) {
var w = opts.biff > 8 ? 4 : 2;
@ -136,9 +139,6 @@ function parse_FtArray(blob, length, ot) {
return fts;
}
/* 2.5.129 */
var parse_FontIndex = parseuint16;
/* --- 2.4 Records --- */
/* 2.4.21 */
@ -157,6 +157,29 @@ function parse_BOF(blob, length) {
blob.read_shift(length);
return o;
}
function write_BOF(wb/*:Workbook*/, t/*:number*/, o) {
var h = 0x0600, w = 16;
switch(o.bookType) {
case 'biff8': break;
case 'biff5': h = 0x0500; w = 8; break;
case 'biff4': h = 0x0004; w = 6; break;
case 'biff3': h = 0x0003; w = 6; break;
case 'biff2': h = 0x0002; w = 4; break;
default: throw new Error("unsupported BIFF version");
}
var out = new_buf(w);
out.write_shift(2, h);
out.write_shift(2, t);
if(w > 4) out.write_shift(2, 0x7262);
if(w > 6) out.write_shift(2, 0x07CD);
if(w > 8) {
out.write_shift(2, 0xC009);
out.write_shift(2, 0x0001);
out.write_shift(2, 0x0706);
out.write_shift(2, 0x0000);
}
return out;
}
/* 2.4.146 */
@ -177,6 +200,15 @@ function parse_WriteAccess(blob, length, opts) {
blob.read_shift(length + l - blob.l);
return UserName;
}
function write_WriteAccess(s/*:string*/, opts) {
var o = new_buf(112);
o.write_shift(opts.biff == 8 ? 2 : 1, 7);
o.write_shift(1, 0);
o.write_shift(4, 0x33336853);
o.write_shift(4, 0x00534A74);
while(o.l < o.length) o.write_shift(1, 0);
return o;
}
/* 2.4.28 */
function parse_BoundSheet8(blob, length, opts) {
@ -193,6 +225,16 @@ function parse_BoundSheet8(blob, length, opts) {
if(name.length === 0) name = "Sheet1";
return { pos:pos, hs:hidden, dt:dt, name:name };
}
function write_BoundSheet8(data, opts) {
var o = new_buf(8 + 2 * data.name.length);
o.write_shift(4, data.pos);
o.write_shift(1, data.hs || 0);
o.write_shift(1, data.dt);
o.write_shift(1, data.name.length);
o.write_shift(1, 1);
o.write_shift(2 * data.name.length, data.name, 'utf16le');
return o;
}
/* 2.4.265 TODO */
function parse_SST(blob, length)/*:SST*/ {
@ -243,7 +285,6 @@ function parse_ForceFullCalculation(blob, length) {
}
var parse_CompressPictures = parsenoop2; /* 2.4.55 Not interesting */
@ -275,6 +316,19 @@ function parse_Window1(blob, length) {
return { Pos: [xWn, yWn], Dim: [dxWn, dyWn], Flags: flags, CurTab: iTabCur,
FirstTab: iTabFirst, Selected: ctabSel, TabRatio: wTabRatio };
}
function write_Window1(opts) {
var o = new_buf(18);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 0x7260);
o.write_shift(2, 0x44c0);
o.write_shift(2, 0x38);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 1);
o.write_shift(2, 0x01f4);
return o;
}
/* 2.4.122 TODO */
function parse_Font(blob, length, opts) {
@ -307,6 +361,15 @@ function parse_Label(blob, length, opts) {
cell.val = str;
return cell;
}
function write_Label(R/*:number*/, C/*:number*/, v/*:string*/, opts) {
var o = new_buf(6 + 3 + 2 * v.length);
write_XLSCell(R, C, 0, o);
o.write_shift(2, v.length);
o.write_shift(1, 1);
o.write_shift(2 * v.length, v, 'utf16le');
return o;
}
/* 2.4.126 Number Formats */
function parse_Format(blob, length, opts) {
@ -325,6 +388,15 @@ function parse_Dimensions(blob, length, opts) {
blob.l = end;
return {s: {r:r, c:c}, e: {r:R, c:C}};
}
function write_Dimensions(range, opts) {
var o = new_buf(14);
o.write_shift(4, range.s.r);
o.write_shift(4, range.e.r + 1);
o.write_shift(2, range.s.c);
o.write_shift(2, range.e.c + 1);
o.write_shift(2, 0);
return o;
}
/* 2.4.220 */
function parse_RK(blob, length) {
@ -419,6 +491,13 @@ function parse_Guts(blob, length) {
if(out[0] > 7 || out[1] > 7) throw new Error("Bad Gutters: " + out.join("|"));
return out;
}
function write_Guts(guts/*:Array<number>*/) {
var o = new_buf(8);
o.write_shift(4, 0);
o.write_shift(2, guts[0] ? guts[0] + 1 : 0);
o.write_shift(2, guts[1] ? guts[1] + 1 : 0);
return o;
}
/* 2.4.24 */
function parse_BoolErr(blob, length, opts) {
@ -429,6 +508,12 @@ function parse_BoolErr(blob, length, opts) {
cell.t = (val === true || val === false) ? 'b' : 'e';
return cell;
}
function write_BoolErr(R/*:number*/, C/*:number*/, v, opts, t/*:string*/) {
var o = new_buf(8);
write_XLSCell(R, C, 0, o);
write_Bes(v, t, o);
return o;
}
/* 2.4.180 Number */
function parse_Number(blob, length) {
@ -437,6 +522,12 @@ function parse_Number(blob, length) {
cell.val = xnum;
return cell;
}
function write_Number(R/*:number*/, C/*:number*/, v, opts) {
var o = new_buf(14);
write_XLSCell(R, C, 0, o);
write_Xnum(v, o);
return o;
}
var parse_XLHeaderFooter = parse_OptXLUnicodeString; // TODO: parse 2.4.136
@ -530,7 +621,6 @@ function parse_BIFF5ExternSheet(blob, length, opts) {
var o = parse_ShortXLUnicodeString(blob, length, opts);
return o.charCodeAt(0) == 0x03 ? o.slice(1) : o;
}
var parse_ExternCount = parseuint16;
/* 2.4.176 TODO: check older biff */
function parse_NameCmt(blob, length, opts) {
@ -652,7 +742,7 @@ try {
else controlInfo = parse_ControlInfo(blob, 6, opts);
var cchText = blob.read_shift(2);
var cbRuns = blob.read_shift(2);
var ifntEmpty = parse_FontIndex(blob, 2);
var ifntEmpty = parseuint16(blob, 2);
var len = blob.read_shift(2);
blob.l += len;
//var fmla = parse_ObjFmla(blob, s + length - blob.l);
@ -681,22 +771,22 @@ try {
}
/* 2.4.140 */
var parse_HLink = function(blob, length) {
function parse_HLink(blob, length) {
var ref = parse_Ref8U(blob, 8);
blob.l += 16; /* CLSID */
var hlink = parse_Hyperlink(blob, length-24);
return [ref, hlink];
};
}
/* 2.4.141 */
var parse_HLinkTooltip = function(blob, length) {
function parse_HLinkTooltip(blob, length) {
var end = blob.l + length;
blob.read_shift(2);
var ref = parse_Ref8U(blob, 8);
var wzTooltip = blob.read_shift((length-10)/2, 'dbcs-cont');
wzTooltip = wzTooltip.replace(chr0,"");
return [ref, wzTooltip];
};
}
/* 2.4.63 */
function parse_Country(blob, length) {
@ -705,6 +795,12 @@ function parse_Country(blob, length) {
d = blob.read_shift(2); o[1] = CountryEnum[d] || d;
return o;
}
function write_Country(o) {
if(!o) o = new_buf(4);
o.write_shift(2, 0x01);
o.write_shift(2, 0x01);
return o;
}
/* 2.4.50 ClrtClient */
function parse_ClrtClient(blob, length) {
@ -764,315 +860,16 @@ function parse_ShtProps(blob, length, opts) {
return def;
}
var parse_Style = parsenoop;
var parse_StyleExt = parsenoop;
/* 2.4.241 */
function write_RRTabId(n/*:number*/) {
var out = new_buf(2 * n);
for(var i = 0; i < n; ++i) out.write_shift(2, i+1);
return out;
}
var parse_Window2 = parsenoop;
var parse_Backup = parsebool; /* 2.4.14 */
var parse_Blank = parse_XLSCell; /* 2.4.20 Just the cell */
var parse_BottomMargin = parse_Xnum; /* 2.4.27 */
var parse_BuiltInFnGroupCount = parseuint16; /* 2.4.30 0x0E or 0x10 but excel 2011 generates 0x11? */
var parse_CalcCount = parseuint16; /* 2.4.31 #Iterations */
var parse_CalcDelta = parse_Xnum; /* 2.4.32 */
var parse_CalcIter = parsebool; /* 2.4.33 1=iterative calc */
var parse_CalcMode = parseuint16; /* 2.4.34 0=manual, 1=auto (def), 2=table */
var parse_CalcPrecision = parsebool; /* 2.4.35 */
var parse_CalcRefMode = parsenoop2; /* 2.4.36 */
var parse_CalcSaveRecalc = parsebool; /* 2.4.37 */
var parse_CodePage = parseuint16; /* 2.4.52 */
var parse_Compat12 = parsebool; /* 2.4.54 true = no compatibility check */
var parse_Date1904 = parsebool; /* 2.4.77 - 1=1904,0=1900 */
var parse_DefColWidth = parseuint16; /* 2.4.89 */
var parse_DSF = parsenoop2; /* 2.4.94 -- MUST be ignored */
var parse_EntExU2 = parsenoop2; /* 2.4.102 -- Explicitly says to ignore */
var parse_EOF = parsenoop2; /* 2.4.103 */
var parse_Excel9File = parsenoop2; /* 2.4.104 -- Optional and unused */
var parse_FeatHdr = parsenoop2; /* 2.4.112 */
var parse_FontX = parseuint16; /* 2.4.123 */
var parse_Footer = parse_XLHeaderFooter; /* 2.4.124 */
var parse_GridSet = parseuint16; /* 2.4.132, =1 */
var parse_HCenter = parsebool; /* 2.4.135 sheet centered horizontal on print */
var parse_Header = parse_XLHeaderFooter; /* 2.4.136 */
var parse_HideObj = parse_HideObjEnum; /* 2.4.139 */
var parse_InterfaceEnd = parsenoop2; /* 2.4.145 -- noop */
var parse_LeftMargin = parse_Xnum; /* 2.4.151 */
var parse_Mms = parsenoop2; /* 2.4.169 -- Explicitly says to ignore */
var parse_ObjProtect = parsebool; /* 2.4.183 -- must be 1 if present */
var parse_Password = parseuint16; /* 2.4.191 */
var parse_PrintGrid = parsebool; /* 2.4.202 */
var parse_PrintRowCol = parsebool; /* 2.4.203 */
var parse_PrintSize = parseuint16; /* 2.4.204 0:3 */
var parse_Prot4Rev = parsebool; /* 2.4.205 */
var parse_Prot4RevPass = parseuint16; /* 2.4.206 */
var parse_Protect = parsebool; /* 2.4.207 */
var parse_RefreshAll = parsebool; /* 2.4.217 -- must be 0 if not template */
var parse_RightMargin = parse_Xnum; /* 2.4.219 */
var parse_RRTabId = parseuint16a; /* 2.4.241 */
var parse_ScenarioProtect = parsebool; /* 2.4.245 */
var parse_Scl = parseuint16a; /* 2.4.247 num, den */
var parse_String = parse_XLUnicodeString; /* 2.4.268 */
var parse_SxBool = parsebool; /* 2.4.274 */
var parse_TopMargin = parse_Xnum; /* 2.4.328 */
var parse_UsesELFs = parsebool; /* 2.4.337 -- should be 0 */
var parse_VCenter = parsebool; /* 2.4.342 */
var parse_WinProtect = parsebool; /* 2.4.347 */
var parse_WriteProtect = parsenoop; /* 2.4.350 empty record */
/* ---- */
var parse_VerticalPageBreaks = parsenoop;
var parse_HorizontalPageBreaks = parsenoop;
var parse_Selection = parsenoop;
var parse_Continue = parsenoop;
var parse_Pane = parsenoop;
var parse_Pls = parsenoop;
var parse_DCon = parsenoop;
var parse_DConRef = parsenoop;
var parse_DConName = parsenoop;
var parse_XCT = parsenoop;
var parse_CRN = parsenoop;
var parse_FileSharing = parsenoop;
var parse_Uncalced = parsenoop;
var parse_Template = parsenoop;
var parse_Intl = parsenoop;
var parse_WsBool = parsenoop;
var parse_Sort = parsenoop;
var parse_Sync = parsenoop;
var parse_LPr = parsenoop;
var parse_DxGCol = parsenoop;
var parse_FnGroupName = parsenoop;
var parse_FilterMode = parsenoop;
var parse_AutoFilterInfo = parsenoop;
var parse_AutoFilter = parsenoop;
var parse_ScenMan = parsenoop;
var parse_SCENARIO = parsenoop;
var parse_SxView = parsenoop;
var parse_Sxvd = parsenoop;
var parse_SXVI = parsenoop;
var parse_SxIvd = parsenoop;
var parse_SXLI = parsenoop;
var parse_SXPI = parsenoop;
var parse_DocRoute = parsenoop;
var parse_RecipName = parsenoop;
var parse_SXDI = parsenoop;
var parse_SXDB = parsenoop;
var parse_SXFDB = parsenoop;
var parse_SXDBB = parsenoop;
var parse_SXNum = parsenoop;
var parse_SxErr = parsenoop;
var parse_SXInt = parsenoop;
var parse_SXString = parsenoop;
var parse_SXDtr = parsenoop;
var parse_SxNil = parsenoop;
var parse_SXTbl = parsenoop;
var parse_SXTBRGIITM = parsenoop;
var parse_SxTbpg = parsenoop;
var parse_ObProj = parsenoop;
var parse_SXStreamID = parsenoop;
var parse_DBCell = parsenoop;
var parse_SXRng = parsenoop;
var parse_SxIsxoper = parsenoop;
var parse_BookBool = parsenoop;
var parse_DbOrParamQry = parsenoop;
var parse_OleObjectSize = parsenoop;
var parse_SXVS = parsenoop;
var parse_BkHim = parsenoop;
var parse_MsoDrawingGroup = parsenoop;
var parse_MsoDrawing = parsenoop;
var parse_MsoDrawingSelection = parsenoop;
var parse_PhoneticInfo = parsenoop;
var parse_SxRule = parsenoop;
var parse_SXEx = parsenoop;
var parse_SxFilt = parsenoop;
<