forked from sheetjs/sheetjs
		
	version bump 0.7.0: Basic write support
- very basic XLSX / XLSM write support with roundtrip tests (XLSB stubs)
- reorganized source tree
- new XLSB range check ensures that A1 is not emitted for empty sheets
- SSF table emitted in output (consistent with js-xls)
- CLI supports writing
Backwards-incompatible changes:
o new Property aliases (see CORE_PROPS and EXT_PROPS)
o FILETIME custom properties parsed as JS Dates
o `xlsx2csv` -> `xlsx` (and `bin/xlsx{2csv,}.njs`)
			
			
This commit is contained in:
		
							parent
							
								
									b645f6ef98
								
							
						
					
					
						commit
						d15b81e0e9
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,2 +1,3 @@ | ||||
| node_modules | ||||
| misc/coverage.html | ||||
| tmp | ||||
|  | ||||
| @ -5,6 +5,7 @@ node_js: | ||||
| before_install: | ||||
|   - "npm install -g mocha" | ||||
|   - "npm install blanket" | ||||
|   - "npm install xlsjs" | ||||
|   - "npm install coveralls mocha-lcov-reporter" | ||||
| before_script: | ||||
|   - "make init" | ||||
|  | ||||
| @ -5,7 +5,7 @@ order to maintain that, every contributor must be vigilant. | ||||
| 
 | ||||
| There have been many projects in the past that have been very lax regarding | ||||
| licensing, and we are of the opinion that those are ticking timebombs and that | ||||
| no corporate product should depend on them.   | ||||
| no corporate product should depend on them. | ||||
| 
 | ||||
| 
 | ||||
| # Required Reading | ||||
| @ -23,7 +23,7 @@ Before thinking about contributing, make sure that: | ||||
| 
 | ||||
| - You are not, nor have ever been, an employee of Microsoft Corporation. | ||||
| 
 | ||||
| - You have not signed any NDAs or Shared Source Agreements with Microsoft  | ||||
| - You have not signed any NDAs or Shared Source Agreements with Microsoft | ||||
|   Corporation or a subsidiary | ||||
| 
 | ||||
| - You have not consulted any existing relevant codebase (if you have, please | ||||
| @ -42,11 +42,11 @@ Keep these in mind as you work: | ||||
|   consult in the process (and be extra careful not to use unlicensed code on | ||||
|   the internet. | ||||
| 
 | ||||
| - You are working on your own time.  Unless they explicitly grant permission,  | ||||
| - You are working on your own time.  Unless they explicitly grant permission, | ||||
|   your employer may be the ultimate owner of your IP | ||||
| 
 | ||||
| 
 | ||||
| # Post-Contribution  | ||||
| # Post-Contribution | ||||
| 
 | ||||
| Before contributions are merged, you will receive an email (at the address | ||||
| associated with the git commit) and will be asked to confirm the aforementioned | ||||
|  | ||||
							
								
								
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							| @ -1,4 +1,4 @@ | ||||
| Copyright (C) 2012-2014 SheetJS  | ||||
| Copyright (C) 2012-2014  SheetJS | ||||
| 
 | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|  | ||||
							
								
								
									
										8
									
								
								Makefile
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										8
									
								
								Makefile
									
									
									
									
									
								
							| @ -1,7 +1,7 @@ | ||||
| LIB=xlsx | ||||
| DEPS=$(wildcard bits/*.js) | ||||
| TARGET=$(LIB).js | ||||
| FMT=xlsx xlsm xlsb misc | ||||
| FMT=xlsx xlsm xlsb misc full | ||||
| REQS=jszip.js | ||||
| ADDONS=dist/cpexcel.js | ||||
| 
 | ||||
| @ -25,6 +25,7 @@ init: | ||||
| 
 | ||||
| .PHONY: test mocha | ||||
| test mocha: test.js | ||||
| 	mkdir -p tmp | ||||
| 	mocha -R spec | ||||
| 
 | ||||
| TESTFMT=$(patsubst %,test_%,$(FMT)) | ||||
| @ -42,6 +43,11 @@ cov: misc/coverage.html | ||||
| cov-spin: | ||||
| 	make cov & bash misc/spin.sh $$! | ||||
| 
 | ||||
| COVFMT=$(patsubst %,cov_%,$(FMT)) | ||||
| .PHONY: $(COVFMT) | ||||
| $(COVFMT): cov_%: | ||||
| 	FMTS=$* make cov | ||||
| 
 | ||||
| misc/coverage.html: $(TARGET) test.js | ||||
| 	mocha --require blanket -R html-cov > $@ | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										44
									
								
								README.md
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										44
									
								
								README.md
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| # xlsx | ||||
| 
 | ||||
| Currently a parser for XLSX/XLSM/XLSB files.  Cleanroom implementation from the | ||||
| Parser and writer for XLSX/XLSM/XLSB files.  Cleanroom implementation from the | ||||
| ISO 29500  Office Open XML specifications, [MS-XLSB], and related documents. | ||||
| 
 | ||||
| ## Installation | ||||
| @ -45,7 +45,7 @@ The complete single-file version is generated at `dist/xlsx.full.min.js` | ||||
| 
 | ||||
| Simple usage (walks through every cell of every sheet and dumps the values): | ||||
| 
 | ||||
|     var XLSX = require('xlsx'); | ||||
|     if(typeof require !== 'undefined') XLSX = require('xlsx'); | ||||
|     var workbook = XLSX.readFile('test.xlsx'); | ||||
|     var sheet_name_list = workbook.SheetNames; | ||||
|     sheet_name_list.forEach(function(y) { | ||||
| @ -56,9 +56,9 @@ Simple usage (walks through every cell of every sheet and dumps the values): | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
| The node version installs a binary `xlsx2csv` which can read XLSX/XLSM/XLSB | ||||
| The node version installs a binary `xlsx` which can read XLSX/XLSM/XLSB | ||||
| files and output the contents in various formats.  The source is available at | ||||
| `xlsx2csv.njs` in the bin directory. | ||||
| `xlsx.njs` in the bin directory. | ||||
| 
 | ||||
| See <http://oss.sheetjs.com/js-xlsx/> for a browser example. | ||||
| 
 | ||||
| @ -76,12 +76,27 @@ Some helper functions in `XLSX.utils` generate different views of the sheets: | ||||
| 
 | ||||
| For more details: | ||||
| 
 | ||||
| - `bin/xlsx2csv.njs` is a tool for node | ||||
| - `bin/xlsx.njs` is a tool for node | ||||
| - `index.html` is the live demo | ||||
| - `bits/90_utils.js` contains the logic for generating CSV and JSON from sheets | ||||
| 
 | ||||
| ## Interface | ||||
| 
 | ||||
| `XLSX` is the exposed variable in the browser and the exported variable in node | ||||
| 
 | ||||
| 
 | ||||
| `XLSX.read(data, read_opts)` attempts to parse `data`. | ||||
| 
 | ||||
| `XLSX.readFile(filename, read_opts)` attempts to read `filename` and parse. | ||||
| 
 | ||||
| `XLSX.write(wb, write_opts)` attempts to write the workbook `wb` | ||||
| 
 | ||||
| `XLSX.writeFile(wb, filename, write_opts)` attempts to write `wb` to `filename` | ||||
| 
 | ||||
| ## Cell Object Description | ||||
| 
 | ||||
| js-xlsx conforms to the Common Spreadsheet Format (CSF): | ||||
| 
 | ||||
| `.SheetNames` is an ordered list of the sheets in the workbook | ||||
| 
 | ||||
| `.Sheets[sheetname]` returns a data structure representing the sheet.  Each key | ||||
| @ -102,7 +117,7 @@ that does not start with `!` corresponds to a cell (using `A-1` notation). | ||||
| 
 | ||||
| For dates, `.v` holds the raw date code from the sheet and `.w` holds the text | ||||
| 
 | ||||
| ## Options | ||||
| ## Parsing Options | ||||
| 
 | ||||
| The exported `read` and `readFile` functions accept an options argument: | ||||
| 
 | ||||
| @ -133,6 +148,21 @@ The exported `read` and `readFile` functions accept an options argument: | ||||
| 
 | ||||
| The defaults are enumerated in bits/84_defaults.js | ||||
| 
 | ||||
| ## Writing Options | ||||
| 
 | ||||
| The exported `write` and `writeFile` functions accept an options argument: | ||||
| 
 | ||||
| | Option Name | Default | Description | | ||||
| | :---------- | ------: | :---------- | | ||||
| | bookSST     | false   | Generate Shared String Table ** | | ||||
| | bookType    | 'xlsx'  | Type of Workbook ("xlsx" or "xlsm" or "xlsb") | | ||||
| 
 | ||||
| - `bookSST` is slower and more memory intensive, but has better compatibility | ||||
|   with iOS Numbers | ||||
| - `bookType = 'xlsb'` is stubbed and far from complete  | ||||
| - The raw data is the only thing guaranteed to be saved.  Formulae, formatting, | ||||
|   and other niceties are not serialized (pending CSF standardization) | ||||
| 
 | ||||
| ## Tested Environments | ||||
| 
 | ||||
|  - Node 0.8, 0.10 (latest release) | ||||
| @ -165,6 +195,8 @@ $ simplehttpserver # or "python -mSimpleHTTPServer" or "serve" | ||||
| $ open -a Chromium.app http://localhost:8000/stress.html | ||||
| ``` | ||||
| 
 | ||||
| For a much smaller test, run `make test_misc`. | ||||
| 
 | ||||
| ## Contributing | ||||
| 
 | ||||
| Due to the precarious nature of the Open Specifications Promise, it is very | ||||
|  | ||||
| @ -10,9 +10,13 @@ program | ||||
| 	.option('-f, --file <file>', 'use specified workbook') | ||||
| 	.option('-s, --sheet <sheet>', 'print specified sheet (default first sheet)') | ||||
| 	.option('-l, --list-sheets', 'list sheet names and exit') | ||||
| 	.option('-o, --output <file>', 'output to specified file') | ||||
| 	/*.option('-B, --xlsb', 'emit XLSB to <sheetname> or <file>.xlsb') */ | ||||
| 	.option('-M, --xlsm', 'emit XLSM to <sheetname> or <file>.xlsm') | ||||
| 	.option('-X, --xlsx', 'emit XLSX to <sheetname> or <file>.xlsx') | ||||
| 	.option('-S, --formulae', 'print formulae') | ||||
| 	.option('-j, --json', 'emit formatted JSON rather than CSV (all fields text)') | ||||
| 	.option('-J, --raw-js', 'emit raw JS object rather than CSV (raw numbers)') | ||||
| 	.option('-j, --json', 'emit formatted JSON (all fields text)') | ||||
| 	.option('-J, --raw-js', 'emit raw JS object (raw numbers)') | ||||
| 	.option('-F, --field-sep <sep>', 'CSV field separator', ",") | ||||
| 	.option('-R, --row-sep <sep>', 'CSV row separator', "\n") | ||||
| 	.option('-n, --sheet-rows <num>', 'Number of rows to process (0=all rows)') | ||||
| @ -21,6 +25,7 @@ program | ||||
| 	.option('-q, --quiet', 'quiet mode'); | ||||
| 
 | ||||
| program.on('--help', function() { | ||||
| 	console.log('  Default output format is CSV'); | ||||
| 	console.log('  Support email: dev@sheetjs.com'); | ||||
| 	console.log('  Web Demo: http://oss.sheetjs.com/js-'+n+'/'); | ||||
| }); | ||||
| @ -36,19 +41,22 @@ if(program.sheet) sheetname = program.sheet; | ||||
| if(program.file) filename = program.file; | ||||
| 
 | ||||
| if(!filename) { | ||||
| 	console.error(n + "2csv: must specify a filename"); | ||||
| 	console.error(n + ": must specify a filename"); | ||||
| 	process.exit(1); | ||||
| } | ||||
| 
 | ||||
| if(!fs.existsSync(filename)) { | ||||
| 	console.error(n + "2csv: " + filename + ": No such file or directory"); | ||||
| 	console.error(n + ": " + filename + ": No such file or directory"); | ||||
| 	process.exit(2); | ||||
| } | ||||
| 
 | ||||
| var opts = {}, wb; | ||||
| if(program.listSheets) opts.bookSheets = true; | ||||
| if(program.sheetRows) opts.sheetRows = program.sheetRows; | ||||
| 
 | ||||
| if(program.xlsx || program.xlsm || program.xlsb) { | ||||
| 	opts.cellNF = true; | ||||
| 	if(program.output) sheetname = program.output; | ||||
| } | ||||
| if(program.dev) { | ||||
| 	X.verbose = 2; | ||||
| 	opts.WTF = true; | ||||
| @ -57,7 +65,7 @@ if(program.dev) { | ||||
| else try { | ||||
| 	wb = X.readFile(filename, opts); | ||||
| } catch(e) { | ||||
| 	var msg = (program.quiet) ? "" : n + "2csv: error parsing "; | ||||
| 	var msg = (program.quiet) ? "" : n + ": error parsing "; | ||||
| 	msg += filename + ": " + e; | ||||
| 	console.error(msg); | ||||
| 	process.exit(3); | ||||
| @ -69,6 +77,12 @@ if(program.listSheets) { | ||||
| 	process.exit(0); | ||||
| } | ||||
| 
 | ||||
| var wopts = {WTF:opts.WTF}; | ||||
| 
 | ||||
| if(program.xlsx) return X.writeFile(wb, sheetname || (filename + ".xlsx"), wopts); | ||||
| if(program.xlsm) return X.writeFile(wb, sheetname || (filename + ".xlsm"), wopts); | ||||
| if(program.xlsb) return X.writeFile(wb, sheetname || (filename + ".xlsb"), wopts); | ||||
| 
 | ||||
| var target_sheet = sheetname || ''; | ||||
| if(target_sheet === '') target_sheet = wb.SheetNames[0]; | ||||
| 
 | ||||
| @ -77,12 +91,15 @@ try { | ||||
| 	ws = wb.Sheets[target_sheet]; | ||||
| 	if(!ws) throw "Sheet " + target_sheet + " cannot be found"; | ||||
| } catch(e) { | ||||
| 	console.error(n + "2csv: error parsing "+filename+" "+target_sheet+": " + e); | ||||
| 	console.error(n + ": error parsing "+filename+" "+target_sheet+": " + e); | ||||
| 	process.exit(4); | ||||
| } | ||||
| 
 | ||||
| if(!program.quiet) console.error(target_sheet); | ||||
| if(program.formulae) console.log(X.utils.get_formulae(ws).join("\n")); | ||||
| else if(program.json) console.log(JSON.stringify(X.utils.sheet_to_row_object_array(ws))); | ||||
| else if(program.rawJs) console.log(JSON.stringify(X.utils.sheet_to_row_object_array(ws,{raw:true}))); | ||||
| else console.log(X.utils.make_csv(ws, {FS:program.fieldSep, RS:program.rowSep})); | ||||
| var oo = "";  | ||||
| if(program.formulae) oo = X.utils.get_formulae(ws).join("\n"); | ||||
| else if(program.json) oo = JSON.stringify(X.utils.sheet_to_row_object_array(ws)); | ||||
| else if(program.rawJs) oo = JSON.stringify(X.utils.sheet_to_row_object_array(ws,{raw:true})); | ||||
| else oo = X.utils.make_csv(ws, {FS:program.fieldSep, RS:program.rowSep}); | ||||
| 
 | ||||
| if(program.output) fs.writeFileSync(program.output, oo); | ||||
| @ -1 +1 @@ | ||||
| XLSX.version = '0.6.2'; | ||||
| XLSX.version = '0.7.0'; | ||||
|  | ||||
							
								
								
									
										13
									
								
								bits/30_jsutils.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										13
									
								
								bits/30_jsutils.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| function isval(x) { return typeof x !== "undefined" && x !== null; } | ||||
| 
 | ||||
| function keys(o) { return Object.keys(o).filter(function(x) { return o.hasOwnProperty(x); }); } | ||||
| 
 | ||||
| function evert(obj, arr) { | ||||
| 	var o = {}; | ||||
| 	keys(obj).forEach(function(k) { | ||||
| 		if(!obj.hasOwnProperty(k)) return; | ||||
| 		if(!arr) o[obj[k]] = k; | ||||
| 		else (o[obj[k]]=o[obj[k]]||[]).push(k); | ||||
| 	}); | ||||
| 	return o; | ||||
| } | ||||
| @ -1,4 +1,5 @@ | ||||
| var _chr = function(c) { return String.fromCharCode(c); }; | ||||
| var _ord = function(c) { return c.charCodeAt(0); }; | ||||
| var attregexg=/([\w:]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g; | ||||
| var attregex=/([\w:]+)=((?:")(?:[^"]*)(?:")|(?:')(?:[^']*)(?:'))/; | ||||
| function parsexmltag(tag) { | ||||
| @ -13,12 +14,6 @@ function parsexmltag(tag) { | ||||
| 	return z; | ||||
| } | ||||
| 
 | ||||
| function evert(obj) { | ||||
| 	var o = {}; | ||||
| 	Object.keys(obj).forEach(function(k) { if(obj.hasOwnProperty(k)) o[obj[k]] = k; }); | ||||
| 	return o; | ||||
| } | ||||
| 
 | ||||
| var encodings = { | ||||
| 	'"': '"', | ||||
| 	''': "'", | ||||
| @ -38,6 +33,7 @@ function unescapexml(text){ | ||||
| function escapexml(text){ | ||||
| 	var s = text + ''; | ||||
| 	rencstr.forEach(function(y){s=s.replace(new RegExp(y,'g'), rencoding[y]);}); | ||||
| 	s = s.replace(/[\u0000-\u0007]/g,function(s) { return "_x" + ("0000"+_ord(s).toString(16)).substr(-4) + "_";}); /* TODO: verify range */ | ||||
| 	return s; | ||||
| } | ||||
| 
 | ||||
| @ -83,4 +79,37 @@ function parseVector(data) { | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| function isval(x) { return typeof x !== "undefined" && x !== null; } | ||||
| function writetag(f,g) {return '<' + f + (g.match(/(^\s|\s$|\n)/)?' xml:space="preserve"' : "") + '>' + g + '</' + f + '>';} | ||||
| 
 | ||||
| /*jshint -W041 */ | ||||
| function writextag(f,g,h) { return '<' + f + (h != null ? keys(h).map(function(k) { return " " + k + '="' + h[k] + '"';}).join("") : "") + (g == null ? "/" : (g.match(/(^\s|\s$|\n)/)?' xml:space="preserve"' : "") + '>' + g + '</' + f) + '>';} | ||||
| 
 | ||||
| function write_w3cdtf(d, t) { try { return d.toISOString().replace(/\.\d*/,""); } catch(e) { if(t) throw e; } } | ||||
| 
 | ||||
| function write_vt(s) { | ||||
| 	if(typeof s == 'string') return writextag('vt:lpwstr', s); | ||||
| 	if(typeof s == 'number') return writextag((s|0)==s?'vt:i4':'vt:r8', String(s)); | ||||
| 	if(typeof s == 'boolean') return writextag('vt:bool', s?'true':'false'); | ||||
| 	if(s instanceof Date) return writextag('vt:filetime', write_w3cdtf(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/', | ||||
| 	'dcmitype': 'http://purl.org/dc/dcmitype/', | ||||
| 	'mx': 'http://schemas.microsoft.com/office/mac/excel/2008/main', | ||||
| 	'r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships', | ||||
| 	'sjs': 'http://schemas.openxmlformats.org/package/2006/sheetjs/core-properties', | ||||
| 	'vt': 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes', | ||||
| 	'xsi': 'http://www.w3.org/2001/XMLSchema-instance', | ||||
| 	'xsd': 'http://www.w3.org/2001/XMLSchema' | ||||
| }; | ||||
| 
 | ||||
| XMLNS.main = [ | ||||
| 	'http://schemas.openxmlformats.org/spreadsheetml/2006/main', | ||||
| 	'http://purl.oclc.org/ooxml/spreadsheetml/main', | ||||
| 	'http://schemas.microsoft.com/office/excel/2006/main', | ||||
| 	'http://schemas.microsoft.com/office/excel/2006/2' | ||||
| ]; | ||||
| @ -13,3 +13,8 @@ var recordhopper = function(data, cb, opts) { | ||||
| 		if(cb(d, R, RT)) return; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| /* control buffer usage for fixed-length buffers */ | ||||
| var blobhopper = function() { | ||||
| 	var bufs = []; | ||||
| }; | ||||
| @ -5,15 +5,9 @@ | ||||
| /* [MS-XLSB] 2.1.7 Part Enumeration */ | ||||
| var ct2type = { | ||||
| 	/* Workbook */ | ||||
| 	"application/vnd.ms-excel.main": "workbooks", | ||||
| 	"application/vnd.ms-excel.sheet.macroEnabled.main+xml": "workbooks", | ||||
| 	"application/vnd.ms-excel.sheet.binary.macroEnabled.main": "workbooks", | ||||
| 	"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml": "workbooks", | ||||
| 	"application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml": "TODO", /* Template */ | ||||
| 
 | ||||
| 	/* Worksheet */ | ||||
| 	"application/vnd.ms-excel.worksheet": "sheets", | ||||
| 	"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml": "sheets", | ||||
| 	"application/vnd.ms-excel.binIndexWs": "TODO", /* Binary Index */ | ||||
| 
 | ||||
| 	/* Chartsheet */ | ||||
| @ -30,14 +24,6 @@ var ct2type = { | ||||
| 	"application/vnd.ms-excel.intlmacrosheet": "TODO", | ||||
| 	"application/vnd.ms-excel.binIndexMs": "TODO", /* Binary Index */ | ||||
| 
 | ||||
| 	/* Shared Strings */ | ||||
| 	"application/vnd.ms-excel.sharedStrings": "strs", | ||||
| 	"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml": "strs", | ||||
| 
 | ||||
| 	/* Styles */ | ||||
| 	"application/vnd.ms-excel.styles": "styles", | ||||
| 	"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml": "styles", | ||||
| 
 | ||||
| 	/* File Properties */ | ||||
| 	"application/vnd.openxmlformats-package.core-properties+xml": "coreprops", | ||||
| 	"application/vnd.openxmlformats-officedocument.custom-properties+xml": "custprops", | ||||
| @ -151,99 +137,48 @@ var ct2type = { | ||||
| 	/* VML */ | ||||
| 	"application/vnd.openxmlformats-officedocument.vmlDrawing": "TODO", | ||||
| 
 | ||||
| 	"application/vnd.openxmlformats-package.relationships+xml": "TODO", | ||||
| 	"application/vnd.openxmlformats-package.relationships+xml": "rels", | ||||
| 	"application/vnd.openxmlformats-officedocument.oleObject": "TODO", | ||||
| 
 | ||||
| 	"foo": "bar" | ||||
| 	"sheet": "js" | ||||
| }; | ||||
| 
 | ||||
| var XMLNS_CT = 'http://schemas.openxmlformats.org/package/2006/content-types'; | ||||
| 
 | ||||
| function parseProps(data) { | ||||
| 	var p = { Company:'' }, q = {}; | ||||
| 	var strings = ["Application", "DocSecurity", "Company", "AppVersion"]; | ||||
| 	var bools = ["HyperlinksChanged","SharedDoc","LinksUpToDate","ScaleCrop"]; | ||||
| 	var xtra = ["HeadingPairs", "TitlesOfParts"]; | ||||
| 	var xtracp = ["category", "contentStatus", "lastModifiedBy", "lastPrinted", "revision", "version"]; | ||||
| 	var xtradc = ["creator", "description", "identifier", "language", "subject", "title"]; | ||||
| 	var xtradcterms = ["created", "modified"]; | ||||
| 	xtra = xtra.concat(xtracp.map(function(x) { return "cp:" + x; })); | ||||
| 	xtra = xtra.concat(xtradc.map(function(x) { return "dc:" + x; })); | ||||
| 	xtra = xtra.concat(xtradcterms.map(function(x) { return "dcterms:" + x; })); | ||||
| 
 | ||||
| 
 | ||||
| 	strings.forEach(function(f){p[f] = (data.match(matchtag(f))||[])[1];}); | ||||
| 	bools.forEach(function(f){p[f] = (data.match(matchtag(f))||[])[1] == "true";}); | ||||
| 	xtra.forEach(function(f) { | ||||
| 		var cur = data.match(new RegExp("<" + f + "[^>]*>(.*)<\/" + f + ">")); | ||||
| 		if(cur && cur.length > 0) q[f] = cur[1]; | ||||
| 	}); | ||||
| 
 | ||||
| 	if(q.HeadingPairs && q.TitlesOfParts) { | ||||
| 		var v = parseVector(q.HeadingPairs); | ||||
| 		var j = 0, widx = 0; | ||||
| 		for(var i = 0; i !== v.length; ++i) { | ||||
| 			switch(v[i].v) { | ||||
| 				case "Worksheets": widx = j; p.Worksheets = +(v[++i].v); break; | ||||
| 				case "Named Ranges": ++i; break; // TODO: Handle Named Ranges
 | ||||
| 			} | ||||
| var CT_LIST = (function(){ | ||||
| 	var o = { | ||||
| 		workbooks: { | ||||
| 			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", | ||||
| 			xlsm: "application/vnd.ms-excel.sheet.macroEnabled.main+xml", | ||||
| 			xlsb: "application/vnd.ms-excel.sheet.binary.macroEnabled.main", | ||||
| 			xltx: "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml" | ||||
| 		}, | ||||
| 		strs: { /* Shared Strings */ | ||||
| 			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml", | ||||
| 			xlsb: "application/vnd.ms-excel.sharedStrings" | ||||
| 		}, | ||||
| 		sheets: { | ||||
| 			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml", | ||||
| 			xlsb: "application/vnd.ms-excel.worksheet" | ||||
| 		}, | ||||
| 		styles: {/* Styles */ | ||||
| 			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", | ||||
| 			xlsb: "application/vnd.ms-excel.styles" | ||||
| 		} | ||||
| 		var parts = parseVector(q.TitlesOfParts).map(function(x) { return utf8read(x.v); }); | ||||
| 		p.SheetNames = parts.slice(widx, widx + p.Worksheets); | ||||
| 	} | ||||
| 	p.Creator = q["dc:creator"]; | ||||
| 	p.LastModifiedBy = q["cp:lastModifiedBy"]; | ||||
| 	p.CreatedDate = new Date(q["dcterms:created"]); | ||||
| 	p.ModifiedDate = new Date(q["dcterms:modified"]); | ||||
| 	return p; | ||||
| } | ||||
| 	}; | ||||
| 	keys(o).forEach(function(k) { if(!o[k].xlsm) o[k].xlsm = o[k].xlsx; }); | ||||
| 	keys(o).forEach(function(k){ keys(o[k]).forEach(function(v) { ct2type[o[k][v]] = k; }); }); | ||||
| 	return o; | ||||
| })(); | ||||
| 
 | ||||
| /* 15.2.12.2 Custom File Properties Part */ | ||||
| function parseCustomProps(data) { | ||||
| 	var p = {}, name; | ||||
| 	data.match(/<[^>]+>([^<]*)/g).forEach(function(x) { | ||||
| 		var y = parsexmltag(x); | ||||
| 		switch(y[0]) { | ||||
| 			case '<property': name = y.name; break; | ||||
| 			case '</property>': name = null; break; | ||||
| 			default: if (x.indexOf('<vt:') === 0) { | ||||
| 				var toks = x.split('>'); | ||||
| 				var type = toks[0].substring(4), text = toks[1]; | ||||
| 				/* 22.4.2.32 (CT_Variant). Omit the binary types from 22.4 (Variant Types) */ | ||||
| 				switch(type) { | ||||
| 					case 'lpstr': case 'lpwstr': case 'bstr': case 'lpwstr': | ||||
| 						p[name] = unescapexml(text); | ||||
| 						break; | ||||
| 					case 'bool': | ||||
| 						p[name] = parsexmlbool(text, '<vt:bool>'); | ||||
| 						break; | ||||
| 					case 'i1': case 'i2': case 'i4': case 'i8': case 'int': case 'uint': | ||||
| 						p[name] = parseInt(text, 10); | ||||
| 						break; | ||||
| 					case 'r4': case 'r8': case 'decimal': | ||||
| 						p[name] = parseFloat(text); | ||||
| 						break; | ||||
| 					case 'filetime': case 'date': | ||||
| 						p[name] = text; // should we make this into a date?
 | ||||
| 						break; | ||||
| 					case 'cy': case 'error': | ||||
| 						p[name] = unescapexml(text); | ||||
| 						break; | ||||
| 					default: | ||||
| 						console.warn('Unexpected', x, type, toks); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}); | ||||
| 	return p; | ||||
| } | ||||
| var type2ct = evert(ct2type, true); | ||||
| 
 | ||||
| var ctext = {}; | ||||
| function parseCT(data, opts) { | ||||
| XMLNS.CT = 'http://schemas.openxmlformats.org/package/2006/content-types'; | ||||
| 
 | ||||
| function parse_ct(data, opts) { | ||||
| 	var ctext = {}; | ||||
| 	if(!data || !data.match) return data; | ||||
| 	var ct = { workbooks: [], sheets: [], calcchains: [], themes: [], styles: [], | ||||
| 		coreprops: [], extprops: [], custprops: [], strs:[], comments: [], vba: [], | ||||
| 		TODO:[], xmlns: "" }; | ||||
| 		TODO:[], rels:[], xmlns: "" }; | ||||
| 	(data.match(/<[^>]*>/g)||[]).forEach(function(x) { | ||||
| 		var y = parsexmltag(x); | ||||
| 		switch(y[0]) { | ||||
| @ -252,11 +187,11 @@ function parseCT(data, opts) { | ||||
| 			case '<Default': ctext[y.Extension] = y.ContentType; break; | ||||
| 			case '<Override': | ||||
| 				if(y.ContentType in ct2type)ct[ct2type[y.ContentType]].push(y.PartName); | ||||
| 				else if(opts.WTF) console.error(y.ContentType); | ||||
| 				else if(opts.WTF) console.error(y); | ||||
| 				break; | ||||
| 		} | ||||
| 	}); | ||||
| 	if(ct.xmlns !== XMLNS_CT) throw new Error("Unknown Namespace: " + ct.xmlns); | ||||
| 	if(ct.xmlns !== XMLNS.CT) throw new Error("Unknown Namespace: " + ct.xmlns); | ||||
| 	ct.calcchain = ct.calcchains.length > 0 ? ct.calcchains[0] : ""; | ||||
| 	ct.sst = ct.strs.length > 0 ? ct.strs[0] : ""; | ||||
| 	ct.style = ct.styles.length > 0 ? ct.styles[0] : ""; | ||||
| @ -265,44 +200,54 @@ function parseCT(data, opts) { | ||||
| 	return ct; | ||||
| } | ||||
| 
 | ||||
| var CTYPE_XML_ROOT = writextag('Types', null, { | ||||
| 	'xmlns': XMLNS.CT, | ||||
| 	'xmlns:xsd': XMLNS.xsd, | ||||
| 	'xmlns:xsi': XMLNS.xsi | ||||
| }); | ||||
| 
 | ||||
| var CTYPE_DEFAULTS = [ | ||||
| 	['xml', 'application/xml'], | ||||
| 	['rels', type2ct.rels[0]] | ||||
| ].map(function(x) { | ||||
| 	return writextag('Default', null, {'Extension':x[0], 'ContentType': x[1]}); | ||||
| }); | ||||
| 
 | ||||
| /* 9.3.2 OPC Relationships Markup */ | ||||
| function parseRels(data, currentFilePath) { | ||||
| 	if (!data) return data; | ||||
| 	if (currentFilePath.charAt(0) !== '/') { | ||||
| 		currentFilePath = '/'+currentFilePath; | ||||
| 	} | ||||
| 	var rels = {}; | ||||
| 	var hash = {}; | ||||
| 	var resolveRelativePathIntoAbsolute = function (to) { | ||||
| 		var toksFrom = currentFilePath.split('/'); | ||||
| 		toksFrom.pop(); // folder path
 | ||||
| 		var toksTo = to.split('/'); | ||||
| 		var reversed = []; | ||||
| 		while (toksTo.length !== 0) { | ||||
| 			var tokTo = toksTo.shift(); | ||||
| 			if (tokTo === '..') { | ||||
| 				toksFrom.pop(); | ||||
| 			} else if (tokTo !== '.') { | ||||
| 				toksFrom.push(tokTo); | ||||
| 			} | ||||
| function write_ct(ct, opts) { | ||||
| 	var o = [], v; | ||||
| 	o.push(XML_HEADER); | ||||
| 	o.push(CTYPE_XML_ROOT); | ||||
| 	o = o.concat(CTYPE_DEFAULTS); | ||||
| 	var f1 = function(w) { | ||||
| 		if(ct[w] && ct[w].length > 0) { | ||||
| 			v = ct[w][0]; | ||||
| 			o.push(writextag('Override', null, { | ||||
| 				'PartName': (v[0] == '/' ? "":"/") + v, | ||||
| 				'ContentType': CT_LIST[w][opts.bookType || 'xlsx']  | ||||
| 			})); | ||||
| 		} | ||||
| 		return toksFrom.join('/'); | ||||
| 	}; | ||||
| 
 | ||||
| 	data.match(/<[^>]*>/g).forEach(function(x) { | ||||
| 		var y = parsexmltag(x); | ||||
| 		/* 9.3.2.2 OPC_Relationships */ | ||||
| 		if (y[0] === '<Relationship') { | ||||
| 			var rel = {}; rel.Type = y.Type; rel.Target = y.Target; rel.Id = y.Id; rel.TargetMode = y.TargetMode; | ||||
| 			var canonictarget = y.TargetMode === 'External' ? y.Target : resolveRelativePathIntoAbsolute(y.Target); | ||||
| 			rels[canonictarget] = rel; | ||||
| 			hash[y.Id] = rel; | ||||
| 		} | ||||
| 	}); | ||||
| 	rels["!id"] = hash; | ||||
| 	return rels; | ||||
| 	var f2 = function(w) { | ||||
| 		ct[w].forEach(function(v) { | ||||
| 			o.push(writextag('Override', null, { | ||||
| 				'PartName': (v[0] == '/' ? "":"/") + v, | ||||
| 				'ContentType': CT_LIST[w][opts.bookType || 'xlsx']  | ||||
| 			})); | ||||
| 		}); | ||||
| 	}; | ||||
| 	var f3 = function(t) { | ||||
| 		(ct[t]||[]).forEach(function(v) { | ||||
| 			o.push(writextag('Override', null, { | ||||
| 				'PartName': (v[0] == '/' ? "":"/") + v, | ||||
| 				'ContentType': type2ct[t][0] | ||||
| 			})); | ||||
| 		}); | ||||
| 	}; | ||||
| 	f1('workbooks'); | ||||
| 	f2('sheets'); | ||||
| 	f3('themes');	 | ||||
| 	['strs', 'styles'].forEach(f1); | ||||
| 	['coreprops', 'extprops', 'custprops'].forEach(f3); | ||||
| 	if(o.length>2){ o.push('</Types>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										61
									
								
								bits/41_rels.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										61
									
								
								bits/41_rels.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | ||||
| /* 9.3.2 OPC Relationships Markup */ | ||||
| var RELS = { | ||||
| 	WB: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", | ||||
| 	SHEET: "http://sheetjs.openxmlformats.org/officeDocument/2006/relationships/officeDocument" | ||||
| }; | ||||
| 
 | ||||
| function parse_rels(data, currentFilePath) { | ||||
| 	if (!data) return data; | ||||
| 	if (currentFilePath.charAt(0) !== '/') { | ||||
| 		currentFilePath = '/'+currentFilePath; | ||||
| 	} | ||||
| 	var rels = {}; | ||||
| 	var hash = {}; | ||||
| 	var resolveRelativePathIntoAbsolute = function (to) { | ||||
| 		var toksFrom = currentFilePath.split('/'); | ||||
| 		toksFrom.pop(); // folder path
 | ||||
| 		var toksTo = to.split('/'); | ||||
| 		var reversed = []; | ||||
| 		while (toksTo.length !== 0) { | ||||
| 			var tokTo = toksTo.shift(); | ||||
| 			if (tokTo === '..') { | ||||
| 				toksFrom.pop(); | ||||
| 			} else if (tokTo !== '.') { | ||||
| 				toksFrom.push(tokTo); | ||||
| 			} | ||||
| 		} | ||||
| 		return toksFrom.join('/'); | ||||
| 	}; | ||||
| 
 | ||||
| 	data.match(/<[^>]*>/g).forEach(function(x) { | ||||
| 		var y = parsexmltag(x); | ||||
| 		/* 9.3.2.2 OPC_Relationships */ | ||||
| 		if (y[0] === '<Relationship') { | ||||
| 			var rel = {}; rel.Type = y.Type; rel.Target = y.Target; rel.Id = y.Id; rel.TargetMode = y.TargetMode; | ||||
| 			var canonictarget = y.TargetMode === 'External' ? y.Target : resolveRelativePathIntoAbsolute(y.Target); | ||||
| 			rels[canonictarget] = rel; | ||||
| 			hash[y.Id] = rel; | ||||
| 		} | ||||
| 	}); | ||||
| 	rels["!id"] = hash; | ||||
| 	return rels; | ||||
| } | ||||
| 
 | ||||
| XMLNS.RELS = 'http://schemas.openxmlformats.org/package/2006/relationships'; | ||||
| 
 | ||||
| var RELS_ROOT = writextag('Relationships', null, { | ||||
| 	//'xmlns:ns0': XMLNS.RELS,
 | ||||
| 	'xmlns': XMLNS.RELS | ||||
| }); | ||||
| 
 | ||||
| /* TODO */ | ||||
| function write_rels(rels) { | ||||
| 	var o = []; | ||||
| 	o.push(XML_HEADER); | ||||
| 	o.push(RELS_ROOT); | ||||
| 	keys(rels['!id']).forEach(function(rid) { var rel = rels['!id'][rid]; | ||||
| 		o.push(writextag('Relationship', null, rel)); | ||||
| 	}); | ||||
| 	if(o.length>2){ o.push('</Relationships>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| } | ||||
							
								
								
									
										66
									
								
								bits/43_coreprops.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										66
									
								
								bits/43_coreprops.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | ||||
| /* ECMA-376 Part II 11.1 Core Properties Part */ | ||||
| /* [MS-OSHARED] 2.3.3.2.[1-2].1 (PIDSI/PIDDSI) */ | ||||
| var CORE_PROPS = [ | ||||
| 	["cp:category", "Category"], | ||||
| 	["cp:contentStatus", "ContentStatus"], | ||||
| 	["cp:keywords", "Keywords"], | ||||
| 	["cp:lastModifiedBy", "LastAuthor"], | ||||
| 	["cp:lastPrinted", "LastPrinted"], | ||||
| 	["cp:revision", "RevNumber"], | ||||
| 	["cp:version", "Version"], | ||||
| 	["dc:creator", "Author"], | ||||
| 	["dc:description", "Comments"], | ||||
| 	["dc:identifier", "Identifier"], | ||||
| 	["dc:language", "Language"], | ||||
| 	["dc:subject", "Subject"], | ||||
| 	["dc:title", "Title"], | ||||
| 	["dcterms:created", "CreatedDate", 'date'], | ||||
| 	["dcterms:modified", "ModifiedDate", 'date'] | ||||
| ]; | ||||
| 
 | ||||
| XMLNS.CORE_PROPS = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties"; | ||||
| RELS.CORE_PROPS  = 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties'; | ||||
| 
 | ||||
| 
 | ||||
| function parse_core_props(data) { | ||||
| 	var p = {}; | ||||
| 
 | ||||
| 	CORE_PROPS.forEach(function(f) { | ||||
| 		var g = "(?:"+ f[0].substr(0,f[0].indexOf(":")) +":)"+ f[0].substr(f[0].indexOf(":")+1); | ||||
| 		var cur = data.match(new RegExp("<" + g + "[^>]*>(.*)<\/" + g + ">")); | ||||
| 		if(cur && cur.length > 0) p[f[1]] = cur[1]; | ||||
| 		if(f[2] === 'date' && p[f[1]]) p[f[1]] = new Date(p[f[1]]); | ||||
| 	}); | ||||
| 
 | ||||
| 	return p; | ||||
| } | ||||
| 
 | ||||
| var CORE_PROPS_XML_ROOT = writextag('cp:coreProperties', null, { | ||||
| 	//'xmlns': XMLNS.CORE_PROPS,
 | ||||
| 	'xmlns:cp': XMLNS.CORE_PROPS, | ||||
| 	'xmlns:dc': XMLNS.dc, | ||||
| 	'xmlns:dcterms': XMLNS.dcterms, | ||||
| 	'xmlns:dcmitype': XMLNS.dcmitype, | ||||
| 	'xmlns:xsi': XMLNS.xsi | ||||
| }); | ||||
| 
 | ||||
| function write_core_props(cp, opts) { | ||||
| 	var o = [], p = {}; | ||||
| 	o.push(XML_HEADER); | ||||
| 	o.push(CORE_PROPS_XML_ROOT); | ||||
| 	if(!cp) return o.join(""); | ||||
| 
 | ||||
| 	var doit = function(f, g, h) { | ||||
| 		if(p[f] || typeof g === 'undefined' || g === "") return; | ||||
| 		if(typeof g !== 'string') g = String(g); /* TODO: remove */ | ||||
| 		p[f] = g; | ||||
| 		o.push(h ? writextag(f,g,h) : writetag(f,g)); | ||||
| 	}; | ||||
| 
 | ||||
| 	if(typeof cp.CreatedDate !== 'undefined') doit("dcterms:created", write_w3cdtf(cp.CreatedDate, opts.WTF), {"xsi:type":"dcterms:W3CDTF"}); | ||||
| 	if(typeof cp.ModifiedDate !== 'undefined') doit("dcterms:modified", write_w3cdtf(cp.ModifiedDate, opts.WTF), {"xsi:type":"dcterms:W3CDTF"}); | ||||
| 
 | ||||
| 	CORE_PROPS.forEach(function(f) { doit(f[0], cp[f[1]]); }); | ||||
| 	if(o.length>2){ o.push('</cp:coreProperties>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| } | ||||
							
								
								
									
										75
									
								
								bits/44_extprops.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										75
									
								
								bits/44_extprops.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| /* 15.2.12.3 Extended File Properties Part */ | ||||
| /* [MS-OSHARED] 2.3.3.2.[1-2].1 (PIDSI/PIDDSI) */ | ||||
| var EXT_PROPS = [ | ||||
| 	["Application", "Application", "string"], | ||||
| 	["AppVersion", "AppVersion", "string"], | ||||
| 	["Company", "Company", "string"], | ||||
| 	["DocSecurity", "DocSecurity", "string"], | ||||
| 	["Manager", "Manager", "string"], | ||||
| 	["HyperlinksChanged", "HyperlinksChanged", "bool"], | ||||
| 	["SharedDoc", "SharedDoc", "bool"], | ||||
| 	["LinksUpToDate", "LinksUpToDate", "bool"], | ||||
| 	["ScaleCrop", "ScaleCrop", "bool"], | ||||
| 	["HeadingPairs", "HeadingPairs", "raw"], | ||||
| 	["TitlesOfParts", "TitlesOfParts", "raw"], | ||||
| ]; | ||||
| 
 | ||||
| XMLNS.EXT_PROPS = "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"; | ||||
| RELS.EXT_PROPS  = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties'; | ||||
| 
 | ||||
| function parse_ext_props(data, p) { | ||||
| 	var q = {}; if(!p) p = {}; | ||||
| 
 | ||||
| 	EXT_PROPS.forEach(function(f) { | ||||
| 		switch(f[2]) { | ||||
| 			case "string": p[f[1]] = (data.match(matchtag(f[0]))||[])[1]; break; | ||||
| 			case "bool": p[f[1]] = (data.match(matchtag(f[0]))||[])[1] === "true"; break; | ||||
| 			case "raw": | ||||
| 				var cur = data.match(new RegExp("<" + f[0] + "[^>]*>(.*)<\/" + f[0] + ">")); | ||||
| 				if(cur && cur.length > 0) q[f[1]] = cur[1]; | ||||
| 				break; | ||||
| 		} | ||||
| 	}); | ||||
| 
 | ||||
| 	if(q.HeadingPairs && q.TitlesOfParts) { | ||||
| 		var v = parseVector(q.HeadingPairs); | ||||
| 		var j = 0, widx = 0; | ||||
| 		for(var i = 0; i !== v.length; ++i) { | ||||
| 			switch(v[i].v) { | ||||
| 				case "Worksheets": widx = j; p.Worksheets = +(v[++i].v); break; | ||||
| 				case "Named Ranges": ++i; break; // TODO: Handle Named Ranges
 | ||||
| 			} | ||||
| 		} | ||||
| 		var parts = parseVector(q.TitlesOfParts).map(function(x) { return utf8read(x.v); }); | ||||
| 		p.SheetNames = parts.slice(widx, widx + p.Worksheets); | ||||
| 	} | ||||
| 	return p; | ||||
| } | ||||
| 
 | ||||
| var EXT_PROPS_XML_ROOT = writextag('Properties', null, { | ||||
| 	'xmlns': XMLNS.EXT_PROPS, | ||||
| 	'xmlns:vt': XMLNS.vt | ||||
| }); | ||||
| 
 | ||||
| function write_ext_props(cp, opts) { | ||||
| 	var o = [], p = {}, W = writextag; | ||||
| 	o.push(XML_HEADER); | ||||
| 	o.push(EXT_PROPS_XML_ROOT); | ||||
| 	if(!cp) return o.join(""); | ||||
| 
 | ||||
| 	EXT_PROPS.forEach(function(f) { | ||||
| 		if(typeof cp[f[1]] === 'undefined') return; | ||||
| 		var v; | ||||
| 		switch(f[2]) { | ||||
| 			case 'string': v = cp[f[1]]; break; | ||||
| 			case 'bool': v = cp[f[1]] ? 'true' : 'false'; break; | ||||
| 		} | ||||
| 		if(typeof v !== 'undefined') o.push(W(f[0], v)); | ||||
| 	}); | ||||
| 
 | ||||
| 	/* TODO: HeadingPairs, TitlesOfParts */ | ||||
| 	o.push(W('HeadingPairs', W('vt:vector', W('vt:variant', '<vt:lpstr>Worksheets</vt:lpstr>')+W('vt:variant', W('vt:i4', String(cp.Worksheets))), {size:2, baseType:"variant"}))); | ||||
| 	o.push(W('TitlesOfParts', W('vt:vector', cp.SheetNames.map(function(s) { return "<vt:lpstr>" + s + "</vt:lpstr>"; }).join(""), {size: cp.Worksheets, baseType:"lpstr"}))); | ||||
| 	if(o.length>2){ o.push('</Properties>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| } | ||||
							
								
								
									
										70
									
								
								bits/45_custprops.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										70
									
								
								bits/45_custprops.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | ||||
| /* 15.2.12.2 Custom File Properties Part */ | ||||
| XMLNS.CUST_PROPS = "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"; | ||||
| RELS.CUST_PROPS  = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties'; | ||||
| 
 | ||||
| function parse_cust_props(data, opts) { | ||||
| 	var p = {}, name; | ||||
| 	data.match(/<[^>]+>([^<]*)/g).forEach(function(x) { | ||||
| 		var y = parsexmltag(x); | ||||
| 		switch(y[0]) { | ||||
| 			case '<?xml': break; | ||||
| 			case '<Properties': | ||||
| 				if(y.xmlns !== XMLNS.CUST_PROPS) throw "unrecognized xmlns " + y.xmlns; | ||||
| 				if(y.xmlnsvt && y.xmlnsvt !== XMLNS.vt) throw "unrecognized vt " + y.xmlnsvt; | ||||
| 				break; | ||||
| 			case '<property': name = y.name; break; | ||||
| 			case '</property>': name = null; break; | ||||
| 			default: if (x.indexOf('<vt:') === 0) { | ||||
| 				var toks = x.split('>'); | ||||
| 				var type = toks[0].substring(4), text = toks[1]; | ||||
| 				/* 22.4.2.32 (CT_Variant). Omit the binary types from 22.4 (Variant Types) */ | ||||
| 				switch(type) { | ||||
| 					case 'lpstr': case 'lpwstr': case 'bstr': case 'lpwstr': | ||||
| 						p[name] = unescapexml(text); | ||||
| 						break; | ||||
| 					case 'bool': | ||||
| 						p[name] = parsexmlbool(text, '<vt:bool>'); | ||||
| 						break; | ||||
| 					case 'i1': case 'i2': case 'i4': case 'i8': case 'int': case 'uint': | ||||
| 						p[name] = parseInt(text, 10); | ||||
| 						break; | ||||
| 					case 'r4': case 'r8': case 'decimal': | ||||
| 						p[name] = parseFloat(text); | ||||
| 						break; | ||||
| 					case 'filetime': case 'date': | ||||
| 						p[name] = new Date(text); | ||||
| 						break; | ||||
| 					case 'cy': case 'error': | ||||
| 						p[name] = unescapexml(text); | ||||
| 						break; | ||||
| 					default: | ||||
| 						console.warn('Unexpected', x, type, toks); | ||||
| 				} | ||||
| 			} else if(x.substr(0,2) === "</") { | ||||
| 			} else if(opts.WTF) throw new Error(x); | ||||
| 		} | ||||
| 	}); | ||||
| 	return p; | ||||
| } | ||||
| 
 | ||||
| var CUST_PROPS_XML_ROOT = writextag('Properties', null, { | ||||
| 	'xmlns': XMLNS.CUST_PROPS, | ||||
| 	'xmlns:vt': XMLNS.vt | ||||
| }); | ||||
| 
 | ||||
| function write_cust_props(cp, opts) { | ||||
| 	var o = [], p = {}; | ||||
| 	o.push(XML_HEADER); | ||||
| 	o.push(CUST_PROPS_XML_ROOT); | ||||
| 	if(!cp) return o.join(""); | ||||
| 	var pid = 1; | ||||
| 	keys(cp).forEach(function(k) { ++pid;  | ||||
| 		o.push(writextag('property', write_vt(cp[k]), { | ||||
| 			'fmtid': '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}', | ||||
| 			'pid': pid, | ||||
| 			'name': k | ||||
| 		})); | ||||
| 	}); | ||||
| 	if(o.length>2){ o.push('</Properties>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| } | ||||
| @ -19,7 +19,7 @@ var CS2CP = { | ||||
| 	222:   874, /* THAI */ | ||||
| 	238:  1250, /* EASTEUROPE */ | ||||
| 	255:  1252, /* OEM */ | ||||
|     69:   6969  /* MISC */ | ||||
| 	69:   6969  /* MISC */ | ||||
| }; | ||||
| 
 | ||||
| /* Parse a list of <r> tags */ | ||||
| @ -169,3 +169,18 @@ var parse_sst_xml = function(data, opts) { | ||||
| 	return s; | ||||
| }; | ||||
| 
 | ||||
| RELS.SST = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"; | ||||
| 
 | ||||
| var write_sst_xml = function(sst, opts) { | ||||
| 	if(!opts.bookSST) return ""; | ||||
| 	var o = []; | ||||
| 	o.push(XML_HEADER); | ||||
| 	o.push(writextag('sst', null, { | ||||
| 		xmlns: XMLNS.main[0], | ||||
| 		count: sst.Count, | ||||
| 		uniqueCount: sst.Unique | ||||
| 	})); | ||||
| 	sst.forEach(function(s) { o.push("<si>" + (s.r ? s.r : "<t>" + escapexml(s.t) + "</t>") + "</si>"); });	 | ||||
| 	if(o.length>2){ o.push('</sst>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| }; | ||||
|  | ||||
| @ -20,3 +20,5 @@ var parse_sst_bin = function(data, opts) { | ||||
| 	}); | ||||
| 	return s; | ||||
| }; | ||||
| 
 | ||||
| var write_sst_bin = function(sst, opts) { }; | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| /* 18.8.31 numFmts CT_NumFmts */ | ||||
| function parseNumFmts(t, opts) { | ||||
| function parse_numFmts(t, opts) { | ||||
| 	styles.NumberFmt = []; | ||||
| 	for(var y in SSF._table) styles.NumberFmt[y] = SSF._table[y]; | ||||
| 	t[0].match(/<[^>]*>/g).forEach(function(x) { | ||||
| @ -15,8 +15,21 @@ function parseNumFmts(t, opts) { | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| function write_numFmts(NF, opts) { | ||||
| 	var o = []; | ||||
| 	o.push("<numFmts>"); | ||||
| 	[[5,8],[23,26],[41,44],[63,66],[164,392]].forEach(function(r) { | ||||
| 		for(var i = r[0]; i <= r[1]; ++i) if(NF[i])  | ||||
| 		o.push(writextag('numFmt',null,{numFmtId:i,formatCode:escapexml(NF[i])})); | ||||
| 	}); | ||||
| 	o.push("</numFmts>"); | ||||
| 	if(o.length === 2) return ""; | ||||
| 	o[0] = writextag('numFmts', null, { count:o.length-2 }).replace("/>", ">"); | ||||
| 	return o.join(""); | ||||
| } | ||||
| 
 | ||||
| /* 18.8.10 cellXfs CT_CellXfs */ | ||||
| function parseCXfs(t, opts) { | ||||
| function parse_cellXfs(t, opts) { | ||||
| 	styles.CellXf = []; | ||||
| 	t[0].match(/<[^>]*>/g).forEach(function(x) { | ||||
| 		var y = parsexmltag(x); | ||||
| @ -42,13 +55,23 @@ function parseCXfs(t, opts) { | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| function write_cellXfs(cellXfs) { | ||||
| 	var o = []; | ||||
| 	o.push(writextag('cellXfs',null)); | ||||
| 	cellXfs.forEach(function(c) { o.push(writextag('xf', null, c)); }); | ||||
| 	o.push("</cellXfs>"); | ||||
| 	if(o.length === 2) return ""; | ||||
| 	o[0] = writextag('cellXfs',null, {count:o.length-2}).replace("/>",">"); | ||||
| 	return o.join(""); | ||||
| } | ||||
| 
 | ||||
| /* 18.8 Styles CT_Stylesheet*/ | ||||
| function parse_sty_xml(data, opts) { | ||||
| 	/* 18.8.39 styleSheet CT_Stylesheet */ | ||||
| 	var t; | ||||
| 
 | ||||
| 	/* numFmts CT_NumFmts ? */ | ||||
| 	if((t=data.match(/<numFmts([^>]*)>.*<\/numFmts>/))) parseNumFmts(t, opts); | ||||
| 	if((t=data.match(/<numFmts([^>]*)>.*<\/numFmts>/))) parse_numFmts(t, opts); | ||||
| 
 | ||||
| 	/* fonts CT_Fonts ? */ | ||||
| 	/* fills CT_Fills ? */ | ||||
| @ -56,7 +79,7 @@ function parse_sty_xml(data, opts) { | ||||
| 	/* cellStyleXfs CT_CellStyleXfs ? */ | ||||
| 
 | ||||
| 	/* cellXfs CT_CellXfs ? */ | ||||
| 	if((t=data.match(/<cellXfs([^>]*)>.*<\/cellXfs>/))) parseCXfs(t, opts); | ||||
| 	if((t=data.match(/<cellXfs([^>]*)>.*<\/cellXfs>/))) parse_cellXfs(t, opts); | ||||
| 
 | ||||
| 	/* dxfs CT_Dxfs ? */ | ||||
| 	/* tableStyles CT_TableStyles ? */ | ||||
| @ -65,3 +88,28 @@ function parse_sty_xml(data, opts) { | ||||
| 
 | ||||
| 	return styles; | ||||
| } | ||||
| 
 | ||||
| var STYLES_XML_ROOT = writextag('styleSheet', null, { | ||||
| 	'xmlns': XMLNS.main[0], | ||||
| 	'xmlns:vt': XMLNS.vt | ||||
| }); | ||||
| 
 | ||||
| RELS.STY = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"; | ||||
| 
 | ||||
| function write_sty_xml(wb, opts) { | ||||
| 	var o = [], p = {}, W = writextag, w; | ||||
| 	o.push(XML_HEADER); | ||||
| 	o.push(STYLES_XML_ROOT); | ||||
| 	if((w = write_numFmts(wb.SSF))) o.push(w); | ||||
|   o.push('<fonts count="1"><font><sz val="12"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font></fonts>'); | ||||
|   o.push('<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>'); | ||||
| 	o.push('<borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders>'); | ||||
| 	o.push('<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>'); | ||||
| 	if((w = write_cellXfs(opts.cellXfs))) o.push(w); | ||||
| 	o.push('<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>'); | ||||
| 	o.push('<dxfs count="0"/>'); | ||||
| 	o.push('<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleMedium4"/>'); | ||||
| 
 | ||||
| 	if(o.length>2){ o.push('</styleSheet>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| } | ||||
|  | ||||
							
								
								
									
										3
									
								
								bits/59_theme.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										3
									
								
								bits/59_theme.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -5,7 +5,7 @@ function parse_comments(zip, dirComments, sheets, sheetRels, opts) { | ||||
| 		var comments=parse_cmnt(getzipdata(zip, canonicalpath.replace(/^\//,''), true), canonicalpath, opts); | ||||
| 		if(!comments || !comments.length) continue; | ||||
| 		// find the sheets targeted by these comments
 | ||||
| 		var sheetNames = Object.keys(sheets); | ||||
| 		var sheetNames = keys(sheets); | ||||
| 		for(var j = 0; j != sheetNames.length; ++j) { | ||||
| 			var sheetName = sheetNames[j]; | ||||
| 			var rels = sheetRels[sheetName]; | ||||
|  | ||||
| @ -1,3 +1,23 @@ | ||||
| var strs = {}; // shared strings
 | ||||
| var _ssfopts = {}; // spreadsheet formatting options
 | ||||
| 
 | ||||
| RELS.WS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"; | ||||
| 
 | ||||
| function get_sst_id(sst, str) { | ||||
| 	for(var i = 0; i != sst.length; ++i) if(sst[i].t === str) { sst.Count ++; return i; } | ||||
| 	sst[sst.length] = {t:str}; sst.Count ++; sst.Unique ++; return sst.length-1; | ||||
| } | ||||
| 
 | ||||
| function get_cell_style(styles, cell, opts) { | ||||
| 	var z = opts.revssf[cell.z]; | ||||
| 	for(var i = 0; i != styles.length; ++i) if(styles[i].numFmtId === z) return i; | ||||
| 	styles[styles.length] = { | ||||
| 		numFmtId:z, | ||||
| 		fontId:0, | ||||
| 		fillId:0, | ||||
| 		borderId:0, | ||||
| 		xfId:0, | ||||
| 		applyNumberFormat:1 | ||||
| 	}; | ||||
| 	return styles.length-1; | ||||
| } | ||||
|  | ||||
| @ -12,7 +12,7 @@ function parse_ws_xml(data, opts, rels) { | ||||
| 	var mergecells = []; | ||||
| 	if(data.match(/<\/mergeCells>/)) { | ||||
| 		var merges = data.match(/<mergeCell ref="([A-Z0-9:]+)"\s*\/>/g); | ||||
| 		mergecells = merges.map(function(range) {  | ||||
| 		mergecells = merges.map(function(range) { | ||||
| 			return decode_range(/<mergeCell ref="([A-Z0-9:]+)"\s*\/>/.exec(range)[1]); | ||||
| 		}); | ||||
| 	} | ||||
| @ -123,3 +123,55 @@ function parse_ws_xml(data, opts, rels) { | ||||
| 	return s; | ||||
| } | ||||
| 
 | ||||
| var WS_XML_ROOT = writextag('worksheet', null, { | ||||
| 	'xmlns': XMLNS.main[0], | ||||
| 	'xmlns:r': XMLNS.r | ||||
| }); | ||||
| 
 | ||||
| var write_ws_xml_cell = function(cell, ref, ws, opts, idx, wb) { | ||||
| 	var v = writextag('v', escapexml(String(cell.v))), o = {r:ref}; | ||||
| 	if(cell.z) o.s = get_cell_style(opts.cellXfs, cell, opts);  | ||||
| 	/* TODO: cell style */ | ||||
| 	if(typeof cell.v === 'undefined') return ""; | ||||
| 	switch(cell.t) { | ||||
| 		case 's': case 'str': { | ||||
| 			if(opts.bookSST) { | ||||
| 				v = writextag('v', String(get_sst_id(opts.Strings, cell.v))); | ||||
| 				o.t = "s"; return writextag('c', v, o); | ||||
| 			} else { o.t = "str"; return writextag('c', v, o); } | ||||
| 		} break; | ||||
| 		case 'n': o.t = "n"; return writextag('c', v, o); | ||||
| 		case 'b': o.t = "b"; return writextag('c', v, o); | ||||
| 		case 'e': o.t = "e"; return writextag('c', v, o);  | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| var write_ws_xml_data = function(ws, opts, idx, wb) { | ||||
| 	var o = [], r = [], range = utils.decode_range(ws['!ref']), cell, ref; | ||||
| 	for(var R = range.s.r; R <= range.e.r; ++R) { | ||||
| 		r = []; | ||||
| 		for(var C = range.s.c; C <= range.e.c; ++C) { | ||||
| 			ref = utils.encode_cell({c:C, r:R}); | ||||
| 			if(!ws[ref]) continue; | ||||
| 			if((cell = write_ws_xml_cell(ws[ref], ref, ws, opts, idx, wb))) r.push(cell); | ||||
| 		} | ||||
| 		if(r.length) o.push(writextag('row', r.join(""), {r:encode_row(R)})); | ||||
| 	} | ||||
| 	return o.join(""); | ||||
| }; | ||||
| 
 | ||||
| var write_ws_xml = function(idx, opts, wb) { | ||||
| 	var o = [], s = wb.SheetNames[idx], ws = wb.Sheets[s] || {}, sidx = 0, rdata = ""; | ||||
| 	o.push(XML_HEADER); | ||||
| 	o.push(WS_XML_ROOT); | ||||
| 	o.push(writextag('dimension', null, {'ref': ws['!ref'] || 'A1'})); | ||||
| 
 | ||||
| 	sidx = o.length; | ||||
| 	o.push(writextag('sheetData', null)); | ||||
| 	if(ws['!ref']) rdata = write_ws_xml_data(ws, opts, idx, wb); | ||||
| 	if(rdata.length) o.push(rdata); | ||||
| 	if(o.length>sidx+1){ o.push('</sheetData>'); o[sidx]=o[sidx].replace("/>",">"); } | ||||
| 
 | ||||
| 	if(o.length>2){ o.push('</worksheet>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| }; | ||||
|  | ||||
| @ -317,7 +317,7 @@ var parse_ws_bin = function(data, opts, rels) { | ||||
| 			default: if(!pass || opts.WTF) throw new Error("Unexpected record " + R.n); | ||||
| 		} | ||||
| 	}, opts); | ||||
| 	if(!s["!ref"] && ref) s["!ref"] = encode_range(ref); | ||||
| 	if(!s["!ref"] && (refguess.s.r < 1000000 || ref.e.r > 0 || ref.e.c > 0 || ref.s.r > 0 || ref.s.c > 0)) s["!ref"] = encode_range(ref); | ||||
| 	if(opts.sheetRows && s["!ref"]) { | ||||
| 		var tmpref = decode_range(s["!ref"]); | ||||
| 		if(opts.sheetRows < +tmpref.e.r) { | ||||
| @ -334,3 +334,4 @@ var parse_ws_bin = function(data, opts, rels) { | ||||
| 	return s; | ||||
| }; | ||||
| 
 | ||||
| var write_ws_bin = function(wb, opts, rels) {}; | ||||
|  | ||||
| @ -1,10 +1,3 @@ | ||||
| var XMLNS_WB = [ | ||||
| 	'http://purl.oclc.org/ooxml/spreadsheetml/main', | ||||
| 	'http://schemas.openxmlformats.org/spreadsheetml/2006/main', | ||||
| 	'http://schemas.microsoft.com/office/excel/2006/main', | ||||
| 	'http://schemas.microsoft.com/office/excel/2006/2' | ||||
| ]; | ||||
| 
 | ||||
| /* 18.2 Workbook */ | ||||
| function parse_wb_xml(data) { | ||||
| 	var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, xmlns: "" }; | ||||
| @ -110,7 +103,7 @@ function parse_wb_xml(data) { | ||||
| 			case '</mc:AlternateContent>': pass=false; break; | ||||
| 		} | ||||
| 	}); | ||||
| 	if(XMLNS_WB.indexOf(wb.xmlns) === -1) throw new Error("Unknown Namespace: " + wb.xmlns); | ||||
| 	if(XMLNS.main.indexOf(wb.xmlns) === -1) throw new Error("Unknown Namespace: " + wb.xmlns); | ||||
| 
 | ||||
| 	var z; | ||||
| 	/* defaults */ | ||||
| @ -125,3 +118,28 @@ function parse_wb_xml(data) { | ||||
| 	return wb; | ||||
| } | ||||
| 
 | ||||
| var WB_XML_ROOT = writextag('workbook', null, { | ||||
| 	'xmlns': XMLNS.main[0], | ||||
| 	//'xmlns:mx': XMLNS.mx,
 | ||||
| 	//'xmlns:s': XMLNS.main[0],
 | ||||
| 	'xmlns:r': XMLNS.r | ||||
| }); | ||||
| 
 | ||||
| var write_wb_xml = function(wb, opts) { | ||||
| 	var o = []; | ||||
| 	o.push(XML_HEADER); | ||||
| 	o.push(WB_XML_ROOT); | ||||
| 	/* TODO: put this somewhere else */ | ||||
| 	var date1904 = "false"; | ||||
| 	try { date1904 = parsexmlbool(wb.Workbook.WBProps.date1904) ? "true" : "false"; } catch(e) { date1904 = "false"; } | ||||
| 	o.push(writextag('workbookPr', null, {date1904:date1904})); | ||||
| 	o.push("<sheets>"); | ||||
| 	var i = 1; | ||||
| 	wb.SheetNames.forEach(function(s) { | ||||
| 		o.push(writextag('sheet',null,{name:s, sheetId:String(i), "r:id":"rId"+i})); | ||||
| 		++i; | ||||
| 	}); | ||||
| 	o.push("</sheets>"); | ||||
| 	if(o.length>2){ o.push('</workbook>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| }; | ||||
|  | ||||
| @ -69,3 +69,7 @@ var parse_wb_bin = function(data, opts) { | ||||
| 
 | ||||
| 	return wb; | ||||
| }; | ||||
| 
 | ||||
| var write_wb_bin = function(wb, opts) { | ||||
| 
 | ||||
| }; | ||||
|  | ||||
| @ -1,23 +1,48 @@ | ||||
| function parse_wb(data, name, opts) { | ||||
| 	return name.substr(-4)===".bin" ? parse_wb_bin(data, opts) : parse_wb_xml(data, opts); | ||||
| 	return (name.substr(-4)===".bin" ? parse_wb_bin : parse_wb_xml)(data, opts); | ||||
| } | ||||
| 
 | ||||
| function parse_ws(data, name, opts, rels) { | ||||
| 	return name.substr(-4)===".bin" ? parse_ws_bin(data, opts, rels) : parse_ws_xml(data, opts, rels); | ||||
| 	return (name.substr(-4)===".bin" ? parse_ws_bin : parse_ws_xml)(data, opts, rels); | ||||
| } | ||||
| 
 | ||||
| function parse_sty(data, name, opts) { | ||||
| 	return name.substr(-4)===".bin" ? parse_sty_bin(data, opts) : parse_sty_xml(data, opts); | ||||
| 	return (name.substr(-4)===".bin" ? parse_sty_bin : parse_sty_xml)(data, opts); | ||||
| } | ||||
| 
 | ||||
| function parse_sst(data, name, opts) { | ||||
| 	return name.substr(-4)===".bin" ? parse_sst_bin(data, opts) : parse_sst_xml(data, opts); | ||||
| 	return (name.substr(-4)===".bin" ? parse_sst_bin : parse_sst_xml)(data, opts); | ||||
| } | ||||
| 
 | ||||
| function parse_cmnt(data, name, opts) { | ||||
| 	return name.substr(-4)===".bin" ? parse_comments_bin(data, opts) : parse_comments_xml(data, opts); | ||||
| 	return (name.substr(-4)===".bin" ? parse_comments_bin : parse_comments_xml)(data, opts); | ||||
| } | ||||
| 
 | ||||
| function parse_cc(data, name, opts) { | ||||
| 	return name.substr(-4)===".bin" ? parse_cc_bin(data, opts) : parse_cc_xml(data, opts); | ||||
| 	return (name.substr(-4)===".bin" ? parse_cc_bin : parse_cc_xml)(data, opts); | ||||
| } | ||||
| 
 | ||||
| function write_wb(wb, name, opts) { | ||||
| 	return (name.substr(-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts); | ||||
| } | ||||
| 
 | ||||
| function write_ws(data, name, opts, wb) { | ||||
| 	return (name.substr(-4)===".bin" ? write_ws_bin : write_ws_xml)(data, opts, wb); | ||||
| } | ||||
| 
 | ||||
| function write_sty(data, name, opts) { | ||||
| 	return (name.substr(-4)===".bin" ? write_sty_bin : write_sty_xml)(data, opts); | ||||
| } | ||||
| 
 | ||||
| function write_sst(data, name, opts) { | ||||
| 	return (name.substr(-4)===".bin" ? write_sst_bin : write_sst_xml)(data, opts); | ||||
| } | ||||
| /* | ||||
| function write_cmnt(data, name, opts) { | ||||
| 	return (name.substr(-4)===".bin" ? write_comments_bin : write_comments_xml)(data, opts); | ||||
| } | ||||
| 
 | ||||
| function write_cc(data, name, opts) { | ||||
| 	return (name.substr(-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts); | ||||
| } | ||||
| */ | ||||
|  | ||||
| @ -1,22 +1,34 @@ | ||||
| function fixopts(opts) { | ||||
| 	var defaults = [ | ||||
| 		['cellNF', false], /* emit cell number format string as .z */ | ||||
| 		['cellHTML', true], /* emit html string as .h */ | ||||
| 		['cellFormula', true], /* emit formulae as .f */ | ||||
| 
 | ||||
| 		['sheetStubs', false], /* emit empty cells */ | ||||
| 		['sheetRows', 0, 'n'], /* read n rows (0 = read all rows) */ | ||||
| 
 | ||||
| 		['bookDeps', false], /* parse calculation chains */ | ||||
| 		['bookSheets', false], /* only try to get sheet names (no Sheets) */ | ||||
| 		['bookProps', false], /* only try to get properties (no Sheets) */ | ||||
| 		['bookFiles', false], /* include raw file structure (keys, files) */ | ||||
| 		['bookVBA', false], /* include vba raw data (vbaraw) */ | ||||
| 
 | ||||
| 		['WTF', false] /* WTF mode (throws errors) */ | ||||
| 	]; | ||||
| 	defaults.forEach(function(d) { | ||||
| 		if(typeof opts[d[0]] === 'undefined') opts[d[0]] = d[1]; | ||||
| 		if(d[2] === 'n') opts[d[0]] = Number(opts[d[0]]); | ||||
| 	}); | ||||
| function fix_opts(defaults) { | ||||
| 	return function(opts) { | ||||
| 		defaults.forEach(function(d) { | ||||
| 			if(typeof opts[d[0]] === 'undefined') opts[d[0]] = d[1]; | ||||
| 			if(d[2] === 'n') opts[d[0]] = Number(opts[d[0]]); | ||||
| 		}); | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| var fix_read_opts = fix_opts([ | ||||
| 	['cellNF', false], /* emit cell number format string as .z */ | ||||
| 	['cellHTML', true], /* emit html string as .h */ | ||||
| 	['cellFormula', true], /* emit formulae as .f */ | ||||
| 
 | ||||
| 	['sheetStubs', false], /* emit empty cells */ | ||||
| 	['sheetRows', 0, 'n'], /* read n rows (0 = read all rows) */ | ||||
| 
 | ||||
| 	['bookDeps', false], /* parse calculation chains */ | ||||
| 	['bookSheets', false], /* only try to get sheet names (no Sheets) */ | ||||
| 	['bookProps', false], /* only try to get properties (no Sheets) */ | ||||
| 	['bookFiles', false], /* include raw file structure (keys, files) */ | ||||
| 	['bookVBA', false], /* include vba raw data (vbaraw) */ | ||||
| 
 | ||||
| 	['WTF', false] /* WTF mode (throws errors) */ | ||||
| ]); | ||||
| 
 | ||||
| 
 | ||||
| var fix_write_opts = fix_opts([ | ||||
| 	['bookSST', false], /* Generate Shared String Table */ | ||||
| 
 | ||||
| 	['bookType', 'xlsx'], /* Type of workbook (xlsx/m/b) */ | ||||
| 
 | ||||
| 	['WTF', false] /* WTF mode (throws errors) */ | ||||
| ]); | ||||
|  | ||||
| @ -1,10 +1,10 @@ | ||||
| function parseZip(zip, opts) { | ||||
| function parse_zip(zip, opts) { | ||||
| 	make_ssf(SSF); | ||||
| 	opts = opts || {}; | ||||
| 	fixopts(opts); | ||||
| 	fix_read_opts(opts); | ||||
| 	reset_cp(); | ||||
| 	var entries = Object.keys(zip.files); | ||||
| 	var keys = entries.filter(function(x){return x.substr(-1) != '/';}).sort(); | ||||
| 	var dir = parseCT(getzipdata(zip, '[Content_Types].xml'), opts); | ||||
| 	var entries = keys(zip.files).filter(function(x){return x.substr(-1) != '/';}).sort(); | ||||
| 	var dir = parse_ct(getzipdata(zip, '[Content_Types].xml'), opts); | ||||
| 	var xlsb = false; | ||||
| 	var sheets, binname; | ||||
| 	if(dir.workbooks.length === 0) { | ||||
| @ -19,7 +19,7 @@ function parseZip(zip, opts) { | ||||
| 	} | ||||
| 
 | ||||
| 	if(!opts.bookSheets && !opts.bookProps) { | ||||
| 		strs = {}; | ||||
| 		strs = []; | ||||
| 		if(dir.sst) strs=parse_sst(getzipdata(zip, dir.sst.replace(/^\//,'')), dir.sst, opts); | ||||
| 
 | ||||
| 		styles = {}; | ||||
| @ -29,17 +29,21 @@ function parseZip(zip, opts) { | ||||
| 	var wb = parse_wb(getzipdata(zip, dir.workbooks[0].replace(/^\//,'')), dir.workbooks[0], opts); | ||||
| 
 | ||||
| 	var props = {}, propdata = ""; | ||||
| 	try { | ||||
| 		propdata = dir.coreprops.length !== 0 ? getzipdata(zip, dir.coreprops[0].replace(/^\//,'')) : ""; | ||||
| 		propdata += dir.extprops.length !== 0 ? getzipdata(zip, dir.extprops[0].replace(/^\//,'')) : ""; | ||||
| 		props = propdata !== "" ? parseProps(propdata) : {}; | ||||
| 	} catch(e) { } | ||||
| 
 | ||||
| 	if(dir.coreprops.length !== 0) { | ||||
| 		propdata = getzipdata(zip, dir.coreprops[0].replace(/^\//,''), true); | ||||
| 		if(propdata) props = parse_core_props(propdata); | ||||
| 		if(dir.extprops.length !== 0) { | ||||
| 			propdata = getzipdata(zip, dir.extprops[0].replace(/^\//,''), true); | ||||
| 			if(propdata) parse_ext_props(propdata, props); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var custprops = {}; | ||||
| 	if(!opts.bookSheets || opts.bookProps) { | ||||
| 		if (dir.custprops.length !== 0) { | ||||
| 			propdata = getzipdata(zip, dir.custprops[0].replace(/^\//,''), true); | ||||
| 			if(propdata) custprops = parseCustomProps(propdata); | ||||
| 			if(propdata) custprops = parse_cust_props(propdata, opts); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| @ -60,7 +64,6 @@ function parseZip(zip, opts) { | ||||
| 	var sheetRels = {}; | ||||
| 	var path, relsPath; | ||||
| 	if(!props.Worksheets) { | ||||
| 		/* Google Docs doesn't generate the appropriate metadata, so we impute: */ | ||||
| 		var wbsheets = wb.Sheets; | ||||
| 		props.Worksheets = wbsheets.length; | ||||
| 		props.SheetNames = []; | ||||
| @ -76,7 +79,7 @@ function parseZip(zip, opts) { | ||||
| 			path = 'xl/worksheets/sheet'+(i+1-nmode)+(xlsb?'.bin':'.xml'); | ||||
| 			path = path.replace(/sheet0\./,"sheet."); | ||||
| 			relsPath = path.replace(/^(.*)(\/)([^\/]*)$/, "$1/_rels/$3.rels"); | ||||
| 			sheetRels[props.SheetNames[i]]=parseRels(getzipdata(zip, relsPath, true), path); | ||||
| 			sheetRels[props.SheetNames[i]]=parse_rels(getzipdata(zip, relsPath, true), path); | ||||
| 			sheets[props.SheetNames[i]]=parse_ws(getzipdata(zip, path),path,opts,sheetRels[props.SheetNames[i]]); | ||||
| 		} catch(e) { if(opts.WTF) throw e; } | ||||
| 	} | ||||
| @ -93,9 +96,10 @@ function parseZip(zip, opts) { | ||||
| 		SheetNames: props.SheetNames, | ||||
| 		Strings: strs, | ||||
| 		Styles: styles, | ||||
| 		SSF: SSF.get_table() | ||||
| 	}; | ||||
| 	if(opts.bookFiles) { | ||||
| 		out.keys = keys; | ||||
| 		out.keys = entries; | ||||
| 		out.files = zip.files; | ||||
| 	} | ||||
| 	if(opts.bookVBA) { | ||||
|  | ||||
							
								
								
									
										85
									
								
								bits/86_writezip.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										85
									
								
								bits/86_writezip.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,85 @@ | ||||
| function add_rels(rels, rId, f, type, relobj) { | ||||
| 	if(!relobj) relobj = {}; | ||||
| 	if(!rels['!id']) rels['!id'] = {}; | ||||
| 	relobj.Id = 'rId' + rId; | ||||
| 	relobj.Type = type;  | ||||
| 	relobj.Target = f; | ||||
| 	if(rels['!id'][relobj.Id]) throw new Error("Cannot rewrite rId " + rId); | ||||
| 	rels['!id'][relobj.Id] = relobj; | ||||
| 	rels[('/' + relobj.Target).replace("//","/")] = relobj; | ||||
| } | ||||
| 
 | ||||
| function write_zip(wb, opts) { | ||||
| 	if(wb && wb.SSF) { | ||||
| 		make_ssf(SSF); SSF.load_table(wb.SSF); | ||||
| 		opts.revssf = evert(wb.SSF); opts.revssf[wb.SSF[65535]] = 0; | ||||
| 	} | ||||
| 	opts.rels = {}; opts.wbrels = {}; | ||||
| 	opts.Strings = []; opts.Strings.Count = 0; opts.Strings.Unique = 0; | ||||
| 	var wbext = opts.bookType == "xlsb" ? "bin" : "xml"; | ||||
| 	var ct = { workbooks: [], sheets: [], calcchains: [], themes: [], styles: [], | ||||
| 		coreprops: [], extprops: [], custprops: [], strs:[], comments: [], vba: [], | ||||
| 		TODO:[], rels:[], xmlns: "" }; | ||||
| 	fix_write_opts(opts = opts || {}); | ||||
| 	var zip = new jszip(); | ||||
| 	var f = "", rId = 0; | ||||
| 
 | ||||
| 	opts.cellXfs = []; | ||||
| 
 | ||||
| 	f = "docProps/core.xml"; | ||||
| 	zip.file(f, write_core_props(wb.Props, opts)); | ||||
| 	ct.coreprops.push(f); | ||||
| 	add_rels(opts.rels, 3, f, RELS.CORE_PROPS); | ||||
| 
 | ||||
| 	f = "docProps/app.xml"; | ||||
| 	wb.Props.SheetNames = wb.SheetNames; | ||||
| 	wb.Props.Worksheets = wb.SheetNames.length; | ||||
| 	zip.file(f, write_ext_props(wb.Props, opts)); | ||||
| 	ct.extprops.push(f); | ||||
| 	add_rels(opts.rels, 4, f, RELS.EXT_PROPS); | ||||
| 
 | ||||
| 	if(wb.Custprops !== wb.Props) { /* TODO: fix xlsjs */ | ||||
| 		f = "docProps/custom.xml"; | ||||
| 		zip.file(f, write_cust_props(wb.Custprops, opts)); | ||||
| 		ct.custprops.push(f); | ||||
| 		add_rels(opts.rels, 5, f, RELS.CUST_PROPS); | ||||
| 	} | ||||
| 
 | ||||
| 	f = "xl/workbook." + wbext; | ||||
| 	zip.file(f, write_wb(wb, f, opts)); | ||||
| 	ct.workbooks.push(f); | ||||
| 	add_rels(opts.rels, 1, f, RELS.WB); | ||||
| 
 | ||||
| 	wb.SheetNames.forEach(function(s, i) { | ||||
| 		rId = i+1; f = "xl/worksheets/sheet" + rId + "." + wbext; | ||||
| 		zip.file(f, write_ws(i, f, opts, wb)); | ||||
| 		ct.sheets.push(f); | ||||
| 		add_rels(opts.wbrels, rId, "worksheets/sheet" + rId + "." + wbext, RELS.WS); | ||||
| 	}); | ||||
| 
 | ||||
| 	if((opts.Strings||[]).length > 0) { | ||||
| 		f = "xl/sharedStrings." + wbext; | ||||
| 		zip.file(f, write_sst(opts.Strings, f, opts)); | ||||
| 		ct.strs.push(f); | ||||
| 		add_rels(opts.wbrels, ++rId, "sharedStrings." + wbext, RELS.SST); | ||||
| 	} | ||||
| 
 | ||||
| 	/* TODO: something more intelligent with themes */ | ||||
| 	 | ||||
| /*	f = "xl/theme/theme1.xml" | ||||
| 	zip.file(f, write_theme()); | ||||
| 	ct.themes.push(f); | ||||
| 	add_rels(opts.wbrels, ++rId, "theme/theme1.xml", RELS.THEME);*/ | ||||
| 
 | ||||
| 	/* TODO: something more intelligent with styles */ | ||||
| 
 | ||||
| 	f = "xl/styles.xml"; | ||||
| 	zip.file(f, write_sty(wb, f, opts)); | ||||
| 	ct.styles.push(f); | ||||
| 	add_rels(opts.wbrels, ++rId, "styles." + wbext, RELS.STY); | ||||
| 
 | ||||
| 	zip.file("[Content_Types].xml", write_ct(ct, opts)); | ||||
| 	zip.file('_rels/.rels', write_rels(opts.rels)); | ||||
| 	zip.file('xl/_rels/workbook.xml.rels', write_rels(opts.wbrels)); | ||||
| 	return zip; | ||||
| } | ||||
| @ -1,19 +1,19 @@ | ||||
| function readSync(data, options) { | ||||
| function readSync(data, opts) { | ||||
| 	var zip, d = data; | ||||
| 	var o = options||{}; | ||||
| 	switch((o.type||"base64")){ | ||||
| 		case "file": | ||||
| 			if(typeof Buffer !== 'undefined') { zip=new jszip(d=_fs.readFileSync(data)); break; } | ||||
| 			d = _fs.readFileSync(data).toString('base64'); | ||||
| 			/* falls through */ | ||||
| 	var o = opts||{}; | ||||
| 	if(!o.type) o.type = (typeof Buffer !== 'undefined' && data instanceof Buffer) ? "buffer" : "base64"; | ||||
| 	switch(o.type) { | ||||
| 		case "base64": zip = new jszip(d, { base64:true }); break; | ||||
| 		case "binary": zip = new jszip(d, { base64:false }); break; | ||||
| 		case "buffer": zip = new jszip(d); break; | ||||
| 		case "file": zip=new jszip(d=_fs.readFileSync(data)); break; | ||||
| 		default: throw new Error("Unrecognized type " + o.type); | ||||
| 	} | ||||
| 	return parseZip(zip, o); | ||||
| 	return parse_zip(zip, o); | ||||
| } | ||||
| 
 | ||||
| function readFileSync(data, options) { | ||||
| 	var o = options||{}; o.type = 'file'; | ||||
| function readFileSync(data, opts) { | ||||
| 	var o = opts||{}; o.type = 'file'; | ||||
| 	return readSync(data, o); | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										22
									
								
								bits/89_write.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										22
									
								
								bits/89_write.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| function writeSync(wb, opts) { | ||||
| 	var o = opts||{}; | ||||
| 	var z = write_zip(wb, o); | ||||
| 	switch(o.type) { | ||||
| 		case "base64": return z.generate({type:"base64"}); | ||||
| 		case "binary": return z.generate({type:"string"}); | ||||
| 		case "buffer": return z.generate({type:"nodebuffer"}); | ||||
| 		case "file": return _fs.writeFileSync(o.file, z.generate({type:"nodebuffer"})); | ||||
| 		default: throw new Error("Unrecognized type " + o.type); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| function writeFileSync(wb, filename, opts) { | ||||
| 	var o = opts||{}; o.type = 'file'; | ||||
| 	o.file = filename; | ||||
| 	switch(o.file.substr(-5).toLowerCase()) { | ||||
| 		case '.xlsm': o.bookType = 'xlsm'; break; | ||||
| 		case '.xlsb': o.bookType = 'xlsb'; break; | ||||
| 	} | ||||
| 	return writeSync(wb, o); | ||||
| } | ||||
| 
 | ||||
| @ -1,5 +1,7 @@ | ||||
| XLSX.parseZip = parseZip; | ||||
| XLSX.parseZip = parse_zip; | ||||
| XLSX.read = readSync; | ||||
| XLSX.readFile = readFileSync; | ||||
| XLSX.write = writeSync; | ||||
| XLSX.writeFile = writeFileSync; | ||||
| XLSX.utils = utils; | ||||
| XLSX.SSF = SSF; | ||||
|  | ||||
							
								
								
									
										2
									
								
								dist/LICENSE
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								dist/LICENSE
									
									
									
									
										vendored
									
									
								
							| @ -1,4 +1,4 @@ | ||||
| Copyright (C) 2012-2014 SheetJS  | ||||
| Copyright (C) 2012-2014  SheetJS | ||||
| 
 | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|  | ||||
							
								
								
									
										9
									
								
								dist/xlsx.core.min.js
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										9
									
								
								dist/xlsx.core.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								dist/xlsx.core.min.map
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								dist/xlsx.core.min.map
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										9
									
								
								dist/xlsx.full.min.js
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										9
									
								
								dist/xlsx.full.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								dist/xlsx.full.min.map
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								dist/xlsx.full.min.map
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1353
									
								
								dist/xlsx.js
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1353
									
								
								dist/xlsx.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										7
									
								
								dist/xlsx.min.js
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										7
									
								
								dist/xlsx.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								dist/xlsx.min.map
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								dist/xlsx.min.map
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -1,11 +1,11 @@ | ||||
| { | ||||
| 	"name": "xlsx", | ||||
| 	"version": "0.6.2", | ||||
| 	"version": "0.7.0", | ||||
| 	"author": "sheetjs", | ||||
| 	"description": "XLSB / XLSX / XLSM (Excel 2007+ Spreadsheet) parser", | ||||
| 	"keywords": [ "xlsx", "xlsb", "xlsm", "office", "excel", "spreadsheet" ], | ||||
| 	"bin": { | ||||
| 		"xlsx2csv": "./bin/xlsx2csv.njs" | ||||
| 		"xlsx": "./bin/xlsx.njs" | ||||
| 	}, | ||||
| 	"main": "./xlsx", | ||||
| 	"dependencies": { | ||||
|  | ||||
							
								
								
									
										186
									
								
								test.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										186
									
								
								test.js
									
									
									
									
									
								
							| @ -3,9 +3,11 @@ var X; | ||||
| var fs = require('fs'), assert = require('assert'); | ||||
| describe('source',function(){it('should load',function(){X=require('./');});}); | ||||
| 
 | ||||
| var opts = {}; | ||||
| var opts = {cellNF: true}; | ||||
| if(process.env.WTF) opts.WTF = true; | ||||
| var ex = [".xlsb", ".xlsm", ".xlsx"]; | ||||
| var fullex = [".xlsb", ".xlsm", ".xlsx"]; | ||||
| var ex = fullex; | ||||
| if(process.env.FMTS === "full") process.env.FMTS = ex.join(":"); | ||||
| if(process.env.FMTS) ex=process.env.FMTS.split(":").map(function(x){return x[0]==="."?x:"."+x;}); | ||||
| var exp = ex.map(function(x){ return x + ".pending"; }); | ||||
| function test_file(x){return ex.indexOf(x.substr(-5))>=0||exp.indexOf(x.substr(-13))>=0;} | ||||
| @ -43,8 +45,9 @@ var paths = { | ||||
| var N1 = 'XLSX'; | ||||
| var N2 = 'XLSB'; | ||||
| 
 | ||||
| function parsetest(x, wb, full) { | ||||
| 	describe(x + ' should have all bits', function() { | ||||
| function parsetest(x, wb, full, ext) { | ||||
| 	ext = (ext ? " [" + ext + "]": ""); | ||||
| 	describe(x + ext + ' should have all bits', function() { | ||||
| 		var sname = dir + '2011/' + x + '.sheetnames'; | ||||
| 		it('should have all sheets', function() { | ||||
| 			wb.SheetNames.forEach(function(y) { assert(wb.Sheets[y], 'bad sheet ' + y); }); | ||||
| @ -55,21 +58,21 @@ function parsetest(x, wb, full) { | ||||
| 			assert.equal(names, file); | ||||
| 		} : null); | ||||
| 	}); | ||||
| 	describe(x + ' should generate CSV', function() { | ||||
| 	describe(x + ext + ' should generate CSV', function() { | ||||
| 		wb.SheetNames.forEach(function(ws, i) { | ||||
| 			it('#' + i + ' (' + ws + ')', function() { | ||||
| 				X.utils.make_csv(wb.Sheets[ws]); | ||||
| 			}); | ||||
| 		}); | ||||
| 	}); | ||||
| 	describe(x + ' should generate JSON', function() { | ||||
| 	describe(x + ext + ' should generate JSON', function() { | ||||
| 		wb.SheetNames.forEach(function(ws, i) { | ||||
| 			it('#' + i + ' (' + ws + ')', function() { | ||||
| 				X.utils.sheet_to_row_object_array(wb.Sheets[ws]); | ||||
| 			}); | ||||
| 		}); | ||||
| 	}); | ||||
| 	describe(x + ' should generate formulae', function() { | ||||
| 	describe(x + ext + ' should generate formulae', function() { | ||||
| 		wb.SheetNames.forEach(function(ws, i) { | ||||
| 			it('#' + i + ' (' + ws + ')', function() { | ||||
| 				X.utils.get_formulae(wb.Sheets[ws]); | ||||
| @ -87,7 +90,7 @@ function parsetest(x, wb, full) { | ||||
| 		} | ||||
| 		return name; | ||||
| 	}; | ||||
| 	describe(x + ' should generate correct CSV output', function() { | ||||
| 	describe(x + ext + ' should generate correct CSV output', function() { | ||||
| 		wb.SheetNames.forEach(function(ws, i) { | ||||
| 			var name = getfile(dir, x, i, ".csv"); | ||||
| 			it('#' + i + ' (' + ws + ')', fs.existsSync(name) ? function() { | ||||
| @ -97,7 +100,7 @@ function parsetest(x, wb, full) { | ||||
| 			} : null); | ||||
| 		}); | ||||
| 	}); | ||||
| 	describe(x + ' should generate correct JSON output', function() { | ||||
| 	describe(x + ext + ' should generate correct JSON output', function() { | ||||
| 		wb.SheetNames.forEach(function(ws, i) { | ||||
| 			var rawjson = getfile(dir, x, i, ".rawjson"); | ||||
| 			if(fs.existsSync(rawjson)) it('#' + i + ' (' + ws + ')', function() { | ||||
| @ -115,7 +118,7 @@ function parsetest(x, wb, full) { | ||||
| 		}); | ||||
| 	}); | ||||
| 	if(!fs.existsSync(dir + '2013/' + x + '.xlsb')) return; | ||||
| 	describe(x + '.xlsb from 2013', function() { | ||||
| 	describe(x + ext + '.xlsb from 2013', function() { | ||||
| 		it('should parse', function() { | ||||
| 			var wb = X.readFile(dir + '2013/' + x + '.xlsb', opts); | ||||
| 		}); | ||||
| @ -127,6 +130,9 @@ describe('should parse test files', function() { | ||||
| 		it(x, x.substr(-8) == ".pending" ? null : function() { | ||||
| 			var wb = X.readFile(dir + x, opts); | ||||
| 			parsetest(x, wb, true); | ||||
| 			['.xlsx', '.xlsm'].forEach(function(ext, idx) { | ||||
| 				parsetest(x, X.read(X.write(wb, {type:"buffer", bookType:ext.replace(/\./,""), bookSST: idx != 1})), true, ext); | ||||
| 			}); | ||||
| 		}); | ||||
| 	}); | ||||
| 	fileA.forEach(function(x) { | ||||
| @ -137,7 +143,7 @@ describe('should parse test files', function() { | ||||
| 	}); | ||||
| }); | ||||
| 
 | ||||
| describe('options', function() { | ||||
| describe('parse options', function() { | ||||
| 	var html_cell_types = ['s']; | ||||
| 	before(function() { | ||||
| 		X = require('./'); | ||||
| @ -308,9 +314,76 @@ describe('input formats', function() { | ||||
| 		X.read(fs.readFileSync(paths.cst1, 'base64'), {type: 'base64'}); | ||||
| 		X.read(fs.readFileSync(paths.cst2, 'base64'), {type: 'base64'}); | ||||
| 	}); | ||||
| 	it('should read buffers', function() { | ||||
| 		X.read(fs.readFileSync(paths.cst1), {type: 'buffer'}); | ||||
| 		X.read(fs.readFileSync(paths.cst2), {type: 'buffer'}); | ||||
| 	}); | ||||
| 	it('should throw if format is unknown', function() { | ||||
| 		assert.throws(function() { X.read(fs.readFileSync(paths.cst1), {type: 'dafuq'}); }); | ||||
| 		assert.throws(function() { X.read(fs.readFileSync(paths.cst2), {type: 'dafuq'}); }); | ||||
| 	}); | ||||
| 	it('should infer buffer type', function() { | ||||
| 		X.read(fs.readFileSync(paths.cst1)); | ||||
| 		X.read(fs.readFileSync(paths.cst2)); | ||||
| 	}); | ||||
| 	it('should default to base64 type', function() { | ||||
| 		assert.throws(function() { X.read(fs.readFileSync(paths.cst1, 'binary')); }); | ||||
| 		assert.throws(function() { X.read(fs.readFileSync(paths.cst2, 'binary')); }); | ||||
| 		X.read(fs.readFileSync(paths.cst1, 'base64')); | ||||
| 		X.read(fs.readFileSync(paths.cst2, 'base64')); | ||||
| 	}); | ||||
| }); | ||||
| 
 | ||||
| describe('features', function() { | ||||
| describe('output formats', function() { | ||||
| 	var wb1, wb2; | ||||
| 	before(function() { | ||||
| 		X = require('./'); | ||||
| 		wb1 = X.readFile(paths.cp1); | ||||
| 		wb2 = X.readFile(paths.cp2); | ||||
| 	}); | ||||
| 	it('should write binary strings', function() { | ||||
| 		X.write(wb1, {type: 'binary'}); | ||||
| 		X.write(wb2, {type: 'binary'}); | ||||
| 		X.read(X.write(wb1, {type: 'binary'}), {type: 'binary'}); | ||||
| 		X.read(X.write(wb2, {type: 'binary'}), {type: 'binary'}); | ||||
| 	}); | ||||
| 	it('should write base64 strings', function() { | ||||
| 		X.write(wb1, {type: 'base64'}); | ||||
| 		X.write(wb2, {type: 'base64'}); | ||||
| 		X.read(X.write(wb1, {type: 'base64'}), {type: 'base64'}); | ||||
| 		X.read(X.write(wb2, {type: 'base64'}), {type: 'base64'}); | ||||
| 	}); | ||||
| 	it('should write buffers', function() { | ||||
| 		X.write(wb1, {type: 'buffer'}); | ||||
| 		X.write(wb2, {type: 'buffer'}); | ||||
| 		X.read(X.write(wb1, {type: 'buffer'}), {type: 'buffer'}); | ||||
| 		X.read(X.write(wb2, {type: 'buffer'}), {type: 'buffer'}); | ||||
| 	}); | ||||
| 	it('should throw if format is unknown', function() { | ||||
| 		assert.throws(function() { X.write(wb1, {type: 'dafuq'}); }); | ||||
| 		assert.throws(function() { X.write(wb2, {type: 'dafuq'}); }); | ||||
| 	}); | ||||
| }); | ||||
| 
 | ||||
| function coreprop(wb) { | ||||
| 	assert.equal(wb.Props.Title, 'Example with properties'); | ||||
| 	assert.equal(wb.Props.Subject, 'Test it before you code it'); | ||||
| 	assert.equal(wb.Props.Author, 'Pony Foo'); | ||||
| 	assert.equal(wb.Props.Manager, 'Despicable Drew'); | ||||
| 	assert.equal(wb.Props.Company, 'Vector Inc'); | ||||
| 	assert.equal(wb.Props.Category, 'Quirky'); | ||||
| 	assert.equal(wb.Props.Keywords, 'example humor'); | ||||
| 	assert.equal(wb.Props.Comments, 'some comments'); | ||||
| 	assert.equal(wb.Props.LastAuthor, 'Hugues'); | ||||
| } | ||||
| function custprop(wb) { | ||||
| 	assert.equal(wb.Custprops['I am a boolean'], true); | ||||
| 	assert.equal(wb.Custprops['Date completed'].toISOString(), '1967-03-09T16:30:00.000Z'); | ||||
| 	assert.equal(wb.Custprops.Status, 2); | ||||
| 	assert.equal(wb.Custprops.Counter, -3.14); | ||||
| } | ||||
| 
 | ||||
| describe('parse features', function() { | ||||
| 	it('should have comment as part of cell properties', function(){ | ||||
| 		var X = require('./'); | ||||
| 		var sheet = 'Sheet1'; | ||||
| @ -335,17 +408,6 @@ describe('features', function() { | ||||
| 			wb2 = X.readFile(paths.cp2); | ||||
| 		}); | ||||
| 
 | ||||
| 		function coreprop(wb) { | ||||
| 			assert.equal(wb.Props.Company, 'Vector Inc'); | ||||
| 			assert.equal(wb.Props.Creator, 'Pony Foo'); | ||||
| 		} | ||||
| 		function custprop(wb) { | ||||
| 			assert.equal(wb.Custprops['I am a boolean'], true); | ||||
| 			assert.equal(wb.Custprops['Date completed'], '1967-03-09T16:30:00Z'); | ||||
| 			assert.equal(wb.Custprops.Status, 2); | ||||
| 			assert.equal(wb.Custprops.Counter, -3.14); | ||||
| 		} | ||||
| 
 | ||||
| 		it(N1 + ' should parse core properties', function() { coreprop(wb1); }); | ||||
| 		it(N2 + ' should parse core properties', function() { coreprop(wb2); }); | ||||
| 		it(N1 + ' should parse custom properties', function() { custprop(wb1); }); | ||||
| @ -434,17 +496,75 @@ describe('features', function() { | ||||
| 	}); | ||||
| }); | ||||
| 
 | ||||
| describe('invalid files', function() { | ||||
| 	it('should fail on passwords', function() { | ||||
| 		assert.throws(function() { X.readFile(dir + 'excel-reader-xlsx_error03.xlsx'); }); | ||||
| describe('roundtrip features', function() { | ||||
| 	before(function() { | ||||
| 		X = require('./'); | ||||
| 	}); | ||||
| 	it('should fail on XLS files', function() { | ||||
| 		assert.throws(function() { X.readFile(dir + 'roo_type_excel.xlsx'); }); | ||||
| 	describe('should parse core properties and custom properties', function() { | ||||
| 		var wb1, wb2, base = './tmp/cp'; | ||||
| 		before(function() { | ||||
| 			wb1 = X.readFile(paths.cp1); | ||||
| 			wb2 = X.readFile(paths.cp2); | ||||
| 			fullex.forEach(function(p) { | ||||
| 				X.writeFile(wb1, base + '.xlsm' + p); | ||||
| 				X.writeFile(wb2, base + '.xlsb' + p); | ||||
| 			}); | ||||
| 		}); | ||||
| 		fullex.forEach(function(p) { ['.xlsm','.xlsb'].forEach(function(q) { | ||||
| 			it(q + p + ' should roundtrip core and custom properties', function() { | ||||
| 				var wb = X.readFile(base + q + p); | ||||
| 				coreprop(wb); | ||||
| 				custprop(wb); | ||||
| 			}); }); | ||||
| 		}); | ||||
| 	}); | ||||
| 	it('should fail on ODS files', function() { | ||||
| 		assert.throws(function() { X.readFile(dir + 'roo_type_openoffice.xlsx');}); | ||||
| 	}); | ||||
| 	it('should fail on DOC files', function() { | ||||
| 		assert.throws(function() { X.readFile(dir + 'word_doc.doc');}); | ||||
| 	/* the XLSJS require should not cause the test suite to fail */ | ||||
| 	var XLSJS; | ||||
| 	try { | ||||
| 		XLSJS = require('xlsjs'); | ||||
| 		var xls = XLSJS.readFile('./test_files/formula_stress_test.xls'); | ||||
| 		var xml = XLSJS.readFile('./test_files/formula_stress_test.xls.xml'); | ||||
| 	} catch(e) { return; } | ||||
| 	describe('xlsjs conversions', function() { [ | ||||
| 			['XLS', 'formula_stress_test.xls'], | ||||
| 			['XML', 'formula_stress_test.xls.xml'] | ||||
| 		].forEach(function(w) { | ||||
| 			it('should be able to write ' + w[0] + ' files from xlsjs', function() { | ||||
| 				var xls = XLSJS.readFile('./test_files/' + w[1]); | ||||
| 				X.writeFile(xls, './tmp/' + w[1] + '.xlsx'); | ||||
| 				X.writeFile(xls, './tmp/' + w[1] + '.xlsb'); | ||||
| 			}); | ||||
| 		}); | ||||
| 	}); | ||||
| }); | ||||
| 
 | ||||
| describe('invalid files', function() { | ||||
| 	describe('parse', function() { [ | ||||
| 			['passwords', 'excel-reader-xlsx_error03.xlsx'], | ||||
| 			['XLS files', 'roo_type_excel.xlsx'], | ||||
| 			['ODS files', 'roo_type_openoffice.xlsx'], | ||||
| 			['DOC files', 'word_doc.doc'] | ||||
| 		].forEach(function(w) { it('should fail on ' + w[0], function() { assert.throws(function() { X.readFile(dir + w[1]); }); }); }); | ||||
| 	}); | ||||
| 	describe('write', function() { | ||||
| 		it('should pass', function() { X.write(X.readFile(paths.fst1), {type:'binary'}); }); | ||||
| 		it('should pass if a sheet is missing', function() { | ||||
| 			var wb = X.readFile(paths.fst1); delete wb.Sheets[wb.SheetNames[0]]; | ||||
| 			X.read(X.write(wb, {type:'binary'}), {type:'binary'}); | ||||
| 		}); | ||||
| 		it('should fail if SheetNames is missing', function() { | ||||
| 			var wb = X.readFile(paths.fst1); | ||||
| 			assert.throws(function() { | ||||
| 				delete wb.SheetNames; | ||||
| 				X.write(wb, {type:'binary'}); | ||||
| 			}); | ||||
| 		}); | ||||
| 		it('should fail if Sheets is missing', function() { | ||||
| 			var wb = X.readFile(paths.fst1); | ||||
| 			assert.throws(function() { | ||||
| 				delete wb.Sheets; | ||||
| 				X.write(wb, {type:'binary'}); | ||||
| 			}); | ||||
| 		}); | ||||
| 	}); | ||||
| }); | ||||
|  | ||||
| @ -1 +1,3 @@ | ||||
| calendar_stress_test.xlsx | ||||
| large_strings.xlsb | ||||
| time_stress_test_1.xlsb | ||||
|  | ||||
| @ -7,7 +7,6 @@ comments_stress_test.xlsb | ||||
| custom_properties.xlsb | ||||
| formula_stress_test.xlsb | ||||
| hyperlink_stress_test_2011.xlsb | ||||
| large_strings.xlsb | ||||
| merge_cells.xlsb | ||||
| named_ranges_2011.xlsb | ||||
| number_format.xlsb | ||||
| @ -15,7 +14,6 @@ pivot_table_named_range.xlsb | ||||
| pivot_table_test.xlsb | ||||
| rich_text_stress.xlsb | ||||
| text_and_numbers.xlsb | ||||
| time_stress_test_1.xlsb | ||||
| xlsx-stream-d-date-cell.xlsb | ||||
| 2013/apachepoi_29982.xls.xlsb | ||||
| 2013/apachepoi_43251.xls.xlsb | ||||
|  | ||||
							
								
								
									
										1353
									
								
								xlsx.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1353
									
								
								xlsx.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Loading…
	
		Reference in New Issue
	
	Block a user