forked from sheetjs/sheetjs
		
	Lotus / Quattro Pro read support
This commit is contained in:
		
							parent
							
								
									01d1c32fa1
								
							
						
					
					
						commit
						6a3afe56c2
					
				
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -7,7 +7,7 @@ v8.log | ||||
| tmp | ||||
| *.txt | ||||
| *.[cC][sS][vV] | ||||
| *.[dD][iI][fF] | ||||
| *.[dD][iIbB][fF] | ||||
| *.[pP][rR][nN] | ||||
| *.[sS][lL][kK] | ||||
| *.socialcalc | ||||
| @ -17,6 +17,9 @@ tmp | ||||
| *.[fF][oO][dD][sS] | ||||
| *.[xX][mM][lL] | ||||
| *.[uU][oO][sS] | ||||
| *.[wW][kKqQbB][S1234567890] | ||||
| *.[qQ][pP][wW] | ||||
| *.123 | ||||
| *.htm | ||||
| *.html | ||||
| *.sheetjs | ||||
|  | ||||
| @ -5,10 +5,12 @@ index.html | ||||
| misc/ | ||||
| node_modules | ||||
| *.tgz | ||||
| _book | ||||
| book.json | ||||
| tmp | ||||
| *.txt | ||||
| *.[cC][sS][vV] | ||||
| *.[dD][iI][fF] | ||||
| *.[dD][iIbB][fF] | ||||
| *.[pP][rR][nN] | ||||
| *.[sS][lL][kK] | ||||
| *.socialcalc | ||||
| @ -18,6 +20,9 @@ tmp | ||||
| *.[fF][oO][dD][sS] | ||||
| *.[xX][mM][lL] | ||||
| *.[uU][oO][sS] | ||||
| *.[wW][kKqQbB][S1234567890] | ||||
| *.[qQ][pP][wW] | ||||
| *.123 | ||||
| *.htm | ||||
| *.html | ||||
| *.sheetjs | ||||
| @ -39,5 +44,3 @@ test.js | ||||
| bits/ | ||||
| docbits/ | ||||
| tests/ | ||||
| _book | ||||
| book.json | ||||
|  | ||||
| @ -7,6 +7,10 @@ changes may not be included if they are not expected to break existing code. | ||||
| 
 | ||||
| ## Unreleased | ||||
| 
 | ||||
| * | ||||
| 
 | ||||
| ## 0.9.9 (2017-04-03) | ||||
| 
 | ||||
| * default output format changed to XLSB | ||||
| * comment text line endings are now normalized | ||||
| * errors thrown on write when worksheets have invalid names | ||||
|  | ||||
							
								
								
									
										51
									
								
								README.md
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										51
									
								
								README.md
									
									
									
									
									
								
							| @ -11,6 +11,8 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6. | ||||
| 
 | ||||
| [**Commercial Support**](http://sheetjs.com/reinforcements) | ||||
| 
 | ||||
| [**Rendered Documentation**](https://sheetjs.gitbooks.io/docs/) | ||||
| 
 | ||||
| [**File format support for known spreadsheet data formats:**](#file-formats) | ||||
| 
 | ||||
|  | ||||
| @ -68,9 +70,12 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6. | ||||
|   * [Excel 97-2004 Binary (BIFF8)](#excel-97-2004-binary-biff8) | ||||
|   * [Excel 2003-2004 (SpreadsheetML)](#excel-2003-2004-spreadsheetml) | ||||
|   * [Excel 2007+ Binary (XLSB, BIFF12)](#excel-2007-binary-xlsb-biff12) | ||||
|   * [OpenDocument Spreadsheet (ODS/FODS)](#opendocument-spreadsheet-odsfods) | ||||
|     + [Uniform Office Spreadsheet (UOS1/2)](#uniform-office-spreadsheet-uos12) | ||||
|   * [Delimiter-Separated Values (CSV/TXT)](#delimiter-separated-values-csvtxt) | ||||
|   * [Other Workbook Formats](#other-workbook-formats) | ||||
|     + [Lotus 1-2-3 (WKS/WK1/WK2/WK3/WK4/123)](#lotus-1-2-3-wkswk1wk2wk3wk4123) | ||||
|     + [Quattro Pro (WQ1/WQ2/WB1/WB2/WB3/QPW)](#quattro-pro-wq1wq2wb1wb2wb3qpw) | ||||
|     + [OpenDocument Spreadsheet (ODS/FODS)](#opendocument-spreadsheet-odsfods) | ||||
|     + [Uniform Office Spreadsheet (UOS1/2)](#uniform-office-spreadsheet-uos12) | ||||
|   * [Other Single-Worksheet Formats](#other-single-worksheet-formats) | ||||
|     + [dBASE and Visual FoxPro (DBF)](#dbase-and-visual-foxpro-dbf) | ||||
|     + [Symbolic Link (SYLK)](#symbolic-link-sylk) | ||||
| @ -872,13 +877,14 @@ file but Excel will know how to handle it.  This library applies similar logic: | ||||
| 
 | ||||
| | Byte 0 | Raw File Type | Spreadsheet Types                                   | | ||||
| |:-------|:--------------|:----------------------------------------------------| | ||||
| | `0xD0` | CFB Container | BIFF 5/8 or password-protected XLSX/XLSB            | | ||||
| | `0xD0` | CFB Container | BIFF 5/8 or password-protected XLSX/XLSB or WQ3/QPW | | ||||
| | `0x09` | BIFF Stream   | BIFF 2/3/4/5                                        | | ||||
| | `0x3C` | XML/HTML      | SpreadsheetML / Flat ODS / UOS1 / HTML / plaintext  | | ||||
| | `0x50` | ZIP Archive   | XLSB or XLSX/M or ODS or UOS2 or plaintext          | | ||||
| | `0x49` | Plain Text    | SYLK or plaintext                                   | | ||||
| | `0x54` | Plain Text    | DIF or plaintext                                    | | ||||
| | `0xFE` | UTF16 Encoded | SpreadsheetML or Flat ODS or UOS1 or plaintext      | | ||||
| | `0x00` | Record Stream | Lotus WK\* or Quattro Pro or plaintext              | | ||||
| 
 | ||||
| DBF files are detected based on the first byte as well as the third and fourth | ||||
| bytes (corresponding to month and day of the file date) | ||||
| @ -1181,6 +1187,8 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats: | ||||
| | Flat XML ODF Spreadsheet (FODS)                              |  :o:  |  :o:  | | ||||
| | Uniform Office Format Spreadsheet (标文通 UOS1/UOS2)         |  :o:  |       | | ||||
| | dBASE II/III/IV / Visual FoxPro (DBF)                        |  :o:  |       | | ||||
| | Lotus 1-2-3 (WKS/WK1/WK2/WK3/WK4/123)                        |  :o:  |       | | ||||
| | Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW)            |  :o:  |       | | ||||
| | **Other Common Spreadsheet Output Formats**                  |:-----:|:-----:| | ||||
| | HTML Tables                                                  |  :o:  |       | | ||||
| 
 | ||||
| @ -1231,7 +1239,34 @@ in an XLSX sub-file can be mapped to XLSB records in a corresponding sub-file. | ||||
| The `MS-XLSB` specification covers the basics of the file format, and other | ||||
| specifications expand on serialization of features like properties. | ||||
| 
 | ||||
| ### OpenDocument Spreadsheet (ODS/FODS) | ||||
| ### Delimiter-Separated Values (CSV/TXT) | ||||
| 
 | ||||
| Excel CSV deviates from RFC4180 in a number of important ways.  The generated | ||||
| CSV files should generally work in Excel although they may not work in RFC4180 | ||||
| compatible readers.  The parser should generally understand Excel CSV. | ||||
| 
 | ||||
| Excel TXT uses tab as the delimiter and codepage 1200. | ||||
| 
 | ||||
| ### Other Workbook Formats | ||||
| 
 | ||||
| Support for other formats is generally far XLS/XLSB/XLSX support, due in large | ||||
| part to a lack of publicly available documentation.  Test files were produced in | ||||
| the respective apps and compared to their XLS exports to determine structure. | ||||
| The main focus is data extraction. | ||||
| 
 | ||||
| #### Lotus 1-2-3 (WKS/WK1/WK2/WK3/WK4/123) | ||||
| 
 | ||||
| The Lotus formats consist of binary records similar to the BIFF structure. Lotus | ||||
| did release a whitepaper decades ago covering the original WK1 format.  Other | ||||
| features were deduced by producing files and comparing to Excel support. | ||||
| 
 | ||||
| #### Quattro Pro (WQ1/WQ2/WB1/WB2/WB3/QPW) | ||||
| 
 | ||||
| The Quattro Pro formats use binary records in the same way as BIFF and Lotus. | ||||
| Some of the newer formats (namely WB3 and QPW) use a CFB enclosure just like | ||||
| BIFF8 XLS. | ||||
| 
 | ||||
| #### OpenDocument Spreadsheet (ODS/FODS) | ||||
| 
 | ||||
| ODS is an XML-in-ZIP format akin to XLSX while FODS is an XML format akin to | ||||
| SpreadsheetML.  Both are detailed in the OASIS standard, but tools like LO/OO | ||||
| @ -1243,14 +1278,6 @@ UOS is a very similar format, and it comes in 2 varieties corresponding to ODS | ||||
| and FODS respectively.  For the most part, the difference between the formats | ||||
| lies in the names of tags and attributes. | ||||
| 
 | ||||
| ### Delimiter-Separated Values (CSV/TXT) | ||||
| 
 | ||||
| Excel CSV deviates from RFC4180 in a number of important ways.  The generated | ||||
| CSV files should generally work in Excel although they may not work in RFC4180 | ||||
| compatible readers.  The parser should generally understand Excel CSV. | ||||
| 
 | ||||
| Excel TXT uses tab as the delimiter and codepage 1200. | ||||
| 
 | ||||
| ### Other Single-Worksheet Formats | ||||
| 
 | ||||
| Many older formats supported only one worksheet: | ||||
|  | ||||
| @ -412,11 +412,12 @@ var PRN = (function() { | ||||
| 		if(str.substr(0,1024).indexOf("\t") == -1) sep = ","; else sep = "\t"; | ||||
| 		var R = 0, C = 0, v = 0; | ||||
| 		var start = 0, end = 0, sepcc = sep.charCodeAt(0), instr = false, cc=0; | ||||
| 		str = str.replace(/\r\n/g, "\n"); | ||||
| 		for(;end < str.length;++end) switch((cc=str.charCodeAt(end))) { | ||||
| 			case 0x22: instr = !instr; break; | ||||
| 			case sepcc: case 0x0a: if(instr) break; | ||||
| 			var s = str.slice(start, end); | ||||
| 			var cell = ({}/*:any*/) | ||||
| 			var cell = ({}/*:any*/); | ||||
| 			if(s.charCodeAt(0) == 0x3D) { cell.t = 'n'; cell.f = s.substr(1); } | ||||
| 			else if(s == "TRUE") { cell.t = 'b'; cell.v = true; } | ||||
| 			else if(s == "FALSE") { cell.t = 'b'; cell.v = false; } | ||||
| @ -426,7 +427,7 @@ var PRN = (function() { | ||||
| 			start = end+1; | ||||
| 			if(range.e.c < C) range.e.c = C; | ||||
| 			if(range.e.r < R) range.e.r = R; | ||||
| 			if(cc == sepcc) ++C; else { C = 0; ++R; }; break; | ||||
| 			if(cc == sepcc) ++C; else { C = 0; ++R; } break; | ||||
| 			default: break; | ||||
| 		} | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										320
									
								
								bits/41_lotus.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										320
									
								
								bits/41_lotus.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,320 @@ | ||||
| var WK_ = (function() { | ||||
| 	function lotushopper(data, cb/*:RecordHopperCB*/, opts/*:any*/) { | ||||
| 		if(!data) return; | ||||
| 		prep_blob(data, data.l || 0); | ||||
| 		var Enum = opts.Enum || WK1Enum; | ||||
| 		while(data.l < data.length) { | ||||
| 			var RT = data.read_shift(2); | ||||
| 			var R = Enum[RT] || Enum[0xFF]; | ||||
| 			var length = data.read_shift(2); | ||||
| 			var tgt = data.l + length; | ||||
| 			var d = R.f(data, length, opts); | ||||
| 			data.l = tgt; | ||||
| 			if(cb(d, R, RT)) return; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	function lotus_to_workbook(d/*:RawData*/, opts) { | ||||
| 		switch(opts.type) { | ||||
| 			case 'base64': return lotus_to_workbook_buf(s2a(Base64.decode(d)), opts); | ||||
| 			case 'binary': return lotus_to_workbook_buf(s2a(d), opts); | ||||
| 			case 'buffer': | ||||
| 			case 'array': return lotus_to_workbook_buf(d, opts); | ||||
| 		} | ||||
| 		throw "Unsupported type " + opts.type; | ||||
| 	} | ||||
| 
 | ||||
| 	function lotus_to_workbook_buf(d,opts)/*:Workbook*/ { | ||||
| 		if(!d) return d; | ||||
| 		var o = opts || {}; | ||||
| 
 | ||||
| 		var s = {}, n = "Sheet1", sidx = 0; | ||||
| 		var sheets = {}, snames = [n]; | ||||
| 
 | ||||
| 		var refguess = {s: {r:0, c:0}, e: {r:0, c:0} }; | ||||
| 
 | ||||
| 		if(d[2] == 0x02) o.Enum = WK1Enum; | ||||
| 		else if(d[2] == 0x1a) o.Enum = WK3Enum; | ||||
| 		else if(d[2] == 0x0e) { o.Enum = WK3Enum; o.qpro = true; d.l = 0; } | ||||
| 		else throw new Error("Unrecognized LOTUS BOF " + d[2]); | ||||
| 		lotushopper(d, function(val, R, RT) { | ||||
| 			if(d[2] == 0x02) switch(RT) { | ||||
| 				case 0x00: | ||||
| 					o.vers = val; | ||||
| 					if(val >= 0x1000) o.qpro = true; | ||||
| 					break; | ||||
| 				case 0x06: refguess = val; break; /* RANGE */ | ||||
| 				case 0x0F: /* LABEL */ | ||||
| 					if(!opts.qpro) val[1].v = val[1].v.substr(1); | ||||
| 					/* falls through */ | ||||
| 				case 0x0D: /* INTEGER */ | ||||
| 				case 0x0E: /* NUMBER */ | ||||
| 				case 0x10: /* FORMULA */ | ||||
| 				case 0x33: /* STRING */ | ||||
| 					s[encode_cell(val[0])] = val[1]; | ||||
| 					/* TODO: FORMAT */ | ||||
| 					break; | ||||
| 			} else switch(RT) { | ||||
| 				case 0x16: /* LABEL16 */ | ||||
| 					val[1].v = val[1].v.substr(1); | ||||
| 					/* falls through */ | ||||
| 				case 0x17: /* NUMBER17 */ | ||||
| 				case 0x18: /* NUMBER18 */ | ||||
| 				case 0x19: /* FORMULA19 */ | ||||
| 				case 0x25: /* NUMBER25 */ | ||||
| 				case 0x27: /* NUMBER27 */ | ||||
| 				case 0x28: /* FORMULA28 */ | ||||
| 					if(val[3] > sidx) { | ||||
| 						s["!ref"] = encode_range(refguess); | ||||
| 						sheets[n] = s; | ||||
| 						s = {}; | ||||
| 						refguess = {s: {r:0, c:0}, e: {r:0, c:0} }; | ||||
| 						sidx = val[3]; n = "Sheet" + (sidx + 1); | ||||
| 						snames.push(n); | ||||
| 					} | ||||
| 					s[encode_cell(val[0])] = val[1]; | ||||
| 					if(refguess.e.c < val[0].c) refguess.e.c = val[0].c; | ||||
| 					if(refguess.e.r < val[0].r) refguess.e.r = val[0].r; | ||||
| 					break; | ||||
| 				default: break; | ||||
| 			} | ||||
| 		}, o); | ||||
| 
 | ||||
| 		s["!ref"] = encode_range(refguess); | ||||
| 		sheets[n] = s; | ||||
| 		return { SheetNames: snames, Sheets:sheets }; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_RANGE(blob, length) { | ||||
| 		var o = {s:{c:0,r:0},e:{c:0,r:0}}; | ||||
| 		o.s.c = blob.read_shift(2); | ||||
| 		o.s.r = blob.read_shift(2); | ||||
| 		o.e.c = blob.read_shift(2); | ||||
| 		o.e.r = blob.read_shift(2); | ||||
| 		if(o.s.c == 0xFFFF) o.s.c = o.e.c = o.s.r = o.e.r = 0; | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_cell(blob, length, opts) { | ||||
| 		var o = [{c:0,r:0}, {t:'n',v:0}, 0]; | ||||
| 		if(opts.qpro && opts.vers != 0x5120) { | ||||
| 			o[0].c = blob.read_shift(1); | ||||
| 			blob.l++; | ||||
| 			o[0].r = blob.read_shift(2); | ||||
| 			blob.l+=2; | ||||
| 		} else { | ||||
| 			o[2] = blob.read_shift(1); | ||||
| 			o[0].c = blob.read_shift(2); o[0].r = blob.read_shift(2); | ||||
| 		} | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_LABEL(blob, length, opts) { | ||||
| 		var tgt = blob.l + length; | ||||
| 		var o = parse_cell(blob, length, opts); | ||||
| 		o[1].t = 's'; | ||||
| 		if(opts.vers == 0x5120) { | ||||
| 			blob.l++; | ||||
| 			var len = blob.read_shift(1); | ||||
| 			o[1].v = blob.read_shift(len, 'utf8'); | ||||
| 			return o; | ||||
| 		} | ||||
| 		if(opts.qpro) blob.l++; | ||||
| 		o[1].v = blob.read_shift(tgt - blob.l, 'cstr'); | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_INTEGER(blob, length, opts) { | ||||
| 		var o = parse_cell(blob, length, opts); | ||||
| 		o[1].v = blob.read_shift(2, 'i'); | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_NUMBER(blob, length, opts) { | ||||
| 		var o = parse_cell(blob, length, opts); | ||||
| 		o[1].v = blob.read_shift(8, 'f'); | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_FORMULA(blob, length, opts) { | ||||
| 		var tgt = blob.l + length; | ||||
| 		var o = parse_cell(blob, length, opts); | ||||
| 		/* TODO: formula */ | ||||
| 		o[1].v = blob.read_shift(8, 'f'); | ||||
| 		if(opts.qpro) blob.l = tgt; | ||||
| 		else { | ||||
| 			var flen = blob.read_shift(2); | ||||
| 			blob.l += flen; | ||||
| 		} | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_cell_3(blob, length) { | ||||
| 		var o = [{c:0,r:0}, {t:'n',v:0}, 0]; | ||||
| 		o[0].r = blob.read_shift(2); o[3] = blob[blob.l++]; o[0].c = blob[blob.l++]; | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_LABEL_16(blob, length) { | ||||
| 		var o = parse_cell_3(blob, length); | ||||
| 		o[1].t = 's'; | ||||
| 		o[1].v = blob.read_shift(length - 4, 'cstr'); | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_NUMBER_18(blob, length) { | ||||
| 		var o = parse_cell_3(blob, length); | ||||
| 		o[1].v = blob.read_shift(2); | ||||
| 		var v = o[1].v >> 1; | ||||
| 		/* TODO: figure out all of the corner cases */ | ||||
| 		if(o[1].v & 0x1) { | ||||
| 			switch(v & 0x07) { | ||||
| 				case 1: v = (v >> 3) * 500; break; | ||||
| 				case 2: v = (v >> 3) / 20; break; | ||||
| 				case 4: v = (v >> 3) / 2000; break; | ||||
| 				case 6: v = (v >> 3) / 16; break; | ||||
| 				case 7: v = (v >> 3) / 64; break; | ||||
| 				default: throw "unknown NUMBER_18 encoding " + (v & 0x07); | ||||
| 			} | ||||
| 		} | ||||
| 		o[1].v = v; | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_NUMBER_17(blob, length) { | ||||
| 		var o = parse_cell_3(blob, length); | ||||
| 		var v1 = blob.read_shift(4); | ||||
| 		var v2 = blob.read_shift(4); | ||||
| 		var e = blob.read_shift(2); | ||||
| 		if(e == 0xFFFF) { o[1].v = 0; return o; } | ||||
| 		var s = e & 0x8000; e = (e&0x7FFF) - 16446; | ||||
| 		o[1].v = ((e > 0 ? (v2 << e) : (v2 >>> -e)) + (e > -32 ? (v1 << (e + 32)) : (v1 >>> -(e + 32)))); | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_FORMULA_19(blob, length) { | ||||
| 		var o = parse_NUMBER_17(blob, 14); | ||||
| 		blob.l += length - 14; /* TODO: formula */ | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_NUMBER_25(blob, length) { | ||||
| 		var o = parse_cell_3(blob, length); | ||||
| 		var v1 = blob.read_shift(4); | ||||
| 		o[1].v = v1 >> 6; | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_NUMBER_27(blob, length) { | ||||
| 		var o = parse_cell_3(blob, length); | ||||
| 		var v1 = blob.read_shift(8,'f'); | ||||
| 		o[1].v = v1; | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_FORMULA_28(blob, length) { | ||||
| 		var o = parse_NUMBER_27(blob, 14); | ||||
| 		blob.l += length - 10; /* TODO: formula */ | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	var WK1Enum = { | ||||
| 		/*::[*/0x0000/*::]*/: { n:"BOF", f:parseuint16 }, | ||||
| 		/*::[*/0x0001/*::]*/: { n:"EOF", f:parsenoop }, | ||||
| 		/*::[*/0x0002/*::]*/: { n: "CALCMODE", f:parsenoop }, | ||||
| 		/*::[*/0x0003/*::]*/: { n:"CALCORDER", f:parsenoop }, | ||||
| 		/*::[*/0x0004/*::]*/: { n:"SPLIT", f:parsenoop }, | ||||
| 		/*::[*/0x0005/*::]*/: { n:"SYNC", f:parsenoop }, | ||||
| 		/*::[*/0x0006/*::]*/: { n:"RANGE", f:parse_RANGE }, | ||||
| 		/*::[*/0x0007/*::]*/: { n:"WINDOW1", f:parsenoop }, | ||||
| 		/*::[*/0x0008/*::]*/: { n:"COLW1", f:parsenoop }, | ||||
| 		/*::[*/0x0009/*::]*/: { n:"WINTWO", f:parsenoop }, | ||||
| 		/*::[*/0x000A/*::]*/: { n:"COLW2", f:parsenoop }, | ||||
| 		/*::[*/0x000B/*::]*/: { n:"NAME", f:parsenoop }, | ||||
| 		/*::[*/0x000C/*::]*/: { n:"BLANK", f:parsenoop }, | ||||
| 		/*::[*/0x000D/*::]*/: { n:"INTEGER", f:parse_INTEGER }, | ||||
| 		/*::[*/0x000E/*::]*/: { n:"NUMBER", f:parse_NUMBER }, | ||||
| 		/*::[*/0x000F/*::]*/: { n:"LABEL", f:parse_LABEL }, | ||||
| 		/*::[*/0x0010/*::]*/: { n:"FORMULA", f:parse_FORMULA }, | ||||
| 		/*::[*/0x0018/*::]*/: { n:"TABLE", f:parsenoop }, | ||||
| 		/*::[*/0x0019/*::]*/: { n:"ORANGE", f:parsenoop }, | ||||
| 		/*::[*/0x001A/*::]*/: { n:"PRANGE", f:parsenoop }, | ||||
| 		/*::[*/0x001B/*::]*/: { n:"SRANGE", f:parsenoop }, | ||||
| 		/*::[*/0x001C/*::]*/: { n:"FRANGE", f:parsenoop }, | ||||
| 		/*::[*/0x001D/*::]*/: { n:"KRANGE1", f:parsenoop }, | ||||
| 		/*::[*/0x0020/*::]*/: { n:"HRANGE", f:parsenoop }, | ||||
| 		/*::[*/0x0023/*::]*/: { n:"KRANGE2", f:parsenoop }, | ||||
| 		/*::[*/0x0024/*::]*/: { n:"PROTEC", f:parsenoop }, | ||||
| 		/*::[*/0x0025/*::]*/: { n:"FOOTER", f:parsenoop }, | ||||
| 		/*::[*/0x0026/*::]*/: { n:"HEADER", f:parsenoop }, | ||||
| 		/*::[*/0x0027/*::]*/: { n:"SETUP", f:parsenoop }, | ||||
| 		/*::[*/0x0028/*::]*/: { n:"MARGINS", f:parsenoop }, | ||||
| 		/*::[*/0x0029/*::]*/: { n:"LABELFMT", f:parsenoop }, | ||||
| 		/*::[*/0x002A/*::]*/: { n:"TITLES", f:parsenoop }, | ||||
| 		/*::[*/0x002B/*::]*/: { n:"SHEETJS", f:parsenoop }, | ||||
| 		/*::[*/0x002D/*::]*/: { n:"GRAPH", f:parsenoop }, | ||||
| 		/*::[*/0x002E/*::]*/: { n:"NGRAPH", f:parsenoop }, | ||||
| 		/*::[*/0x002F/*::]*/: { n:"CALCCOUNT", f:parsenoop }, | ||||
| 		/*::[*/0x0030/*::]*/: { n:"UNFORMATTED", f:parsenoop }, | ||||
| 		/*::[*/0x0031/*::]*/: { n:"CURSORW12", f:parsenoop }, | ||||
| 		/*::[*/0x0032/*::]*/: { n:"WINDOW", f:parsenoop }, | ||||
| 		/*::[*/0x0033/*::]*/: { n:"STRING", f:parse_LABEL }, | ||||
| 		/*::[*/0x0037/*::]*/: { n:"PASSWORD", f:parsenoop }, | ||||
| 		/*::[*/0x0038/*::]*/: { n:"LOCKED", f:parsenoop }, | ||||
| 		/*::[*/0x003C/*::]*/: { n:"QUERY", f:parsenoop }, | ||||
| 		/*::[*/0x003D/*::]*/: { n:"QUERYNAME", f:parsenoop }, | ||||
| 		/*::[*/0x003E/*::]*/: { n:"PRINT", f:parsenoop }, | ||||
| 		/*::[*/0x003F/*::]*/: { n:"PRINTNAME", f:parsenoop }, | ||||
| 		/*::[*/0x0040/*::]*/: { n:"GRAPH2", f:parsenoop }, | ||||
| 		/*::[*/0x0041/*::]*/: { n:"GRAPHNAME", f:parsenoop }, | ||||
| 		/*::[*/0x0042/*::]*/: { n:"ZOOM", f:parsenoop }, | ||||
| 		/*::[*/0x0043/*::]*/: { n:"SYMSPLIT", f:parsenoop }, | ||||
| 		/*::[*/0x0044/*::]*/: { n:"NSROWS", f:parsenoop }, | ||||
| 		/*::[*/0x0045/*::]*/: { n:"NSCOLS", f:parsenoop }, | ||||
| 		/*::[*/0x0046/*::]*/: { n:"RULER", f:parsenoop }, | ||||
| 		/*::[*/0x0047/*::]*/: { n:"NNAME", f:parsenoop }, | ||||
| 		/*::[*/0x0048/*::]*/: { n:"ACOMM", f:parsenoop }, | ||||
| 		/*::[*/0x0049/*::]*/: { n:"AMACRO", f:parsenoop }, | ||||
| 		/*::[*/0x004A/*::]*/: { n:"PARSE", f:parsenoop }, | ||||
| 		/*::[*/0x00FF/*::]*/: { n:"", f:parsenoop } | ||||
| 	}; | ||||
| 
 | ||||
| 	var WK3Enum = { | ||||
| 		/*::[*/0x0000/*::]*/: { n:"BOF", f:parsenoop }, | ||||
| 		/*::[*/0x0001/*::]*/: { n:"EOF", f:parsenoop }, | ||||
| 		/*::[*/0x0003/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0004/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0005/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0006/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0007/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0009/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x000a/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x000b/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x000c/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x000e/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x000f/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0010/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0011/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0012/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0013/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0015/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0016/*::]*/: { n:"LABEL16", f:parse_LABEL_16}, | ||||
| 		/*::[*/0x0017/*::]*/: { n:"NUMBER17", f:parse_NUMBER_17 }, | ||||
| 		/*::[*/0x0018/*::]*/: { n:"NUMBER18", f:parse_NUMBER_18 }, | ||||
| 		/*::[*/0x0019/*::]*/: { n:"FORMULA19", f:parse_FORMULA_19}, | ||||
| 		/*::[*/0x001a/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x001b/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x001c/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x001d/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x001e/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x001f/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0021/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0025/*::]*/: { n:"NUMBER25", f:parse_NUMBER_25 }, | ||||
| 		/*::[*/0x0027/*::]*/: { n:"NUMBER27", f:parse_NUMBER_27 }, | ||||
| 		/*::[*/0x0028/*::]*/: { n:"FORMULA28", f:parse_FORMULA_28 }, | ||||
| 		/*::[*/0x00FF/*::]*/: { n:"", f:parsenoop } | ||||
| 	}; | ||||
| 	return { | ||||
| 		to_workbook: lotus_to_workbook | ||||
| 	}; | ||||
| })(); | ||||
| @ -734,6 +734,10 @@ if(CompObj) CompObjP = parse_compobj(CompObj); | ||||
| if(options.bookProps && !options.bookSheets) WorkbookP = ({}/*:any*/); | ||||
| else { | ||||
| 	if(Workbook) WorkbookP = parse_workbook(Workbook.content, options, !!Workbook.find); | ||||
| 	/* Quattro Pro 7-8 */ | ||||
| 	else if(cfb.find('PerfectOffice_MAIN')) WorkbookP = WK_.to_workbook(cfb.find('PerfectOffice_MAIN').content, options); | ||||
| 	/* Quattro Pro 9 */ | ||||
| 	else if(cfb.find('NativeContent_MAIN')) WorkbookP = WK_.to_workbook(cfb.find('NativeContent_MAIN').content, options); | ||||
| 	else throw new Error("Cannot find Workbook stream"); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1236,7 +1236,7 @@ var XLSRecordEnum = { | ||||
| 	/*::[*/0x08c5/*::]*/: { n:"ListCF", f:parsenoop }, | ||||
| 	/*::[*/0x08c6/*::]*/: { n:"FMQry", f:parsenoop }, | ||||
| 	/*::[*/0x08c7/*::]*/: { n:"FMSQry", f:parsenoop }, | ||||
| 	/*::[*/0x08c8/*::]*/: { n:"PLV", f:parsenoop }, /* supposedly PLV for Excel 11 */ | ||||
| 	/*::[*/0x08c8/*::]*/: { n:"PLV", f:parsenoop }, | ||||
| 	/*::[*/0x08c9/*::]*/: { n:"LnExt", f:parsenoop }, | ||||
| 	/*::[*/0x08ca/*::]*/: { n:"MkrExt", f:parsenoop }, | ||||
| 	/*::[*/0x08cb/*::]*/: { n:"CrtCoopt", f:parsenoop }, | ||||
|  | ||||
| @ -55,6 +55,7 @@ function parse_dom_table(table/*:HTMLElement*/, opts/*:?any*/)/*:Worksheet*/ { | ||||
| 			var o = {t:'s', v:v}; | ||||
| 			if(v != null && v.length && !isNaN(Number(v))) o = {t:'n', v:Number(v)}; | ||||
| 			ws[encode_cell({c:C, r:R})] = o; | ||||
| 			if(range.e.c < C) range.e.c = C; | ||||
| 			C += CS; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -47,6 +47,7 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ { | ||||
| 		case 0x50: if(n[1] == 0x4B && n[2] < 0x20 && n[3] < 0x20) return read_zip(d, o); break; | ||||
| 		case 0xEF: return parse_xlml(d, o); | ||||
| 		case 0xFF: if(n[1] == 0xFE){ return read_utf16(d, o); } break; | ||||
| 		case 0x00: if(n[1] == 0x00 && n[2] >= 0x02 && n[3] == 0x00) return WK_.to_workbook(d, o); break; | ||||
| 		case 0x03: case 0x83: case 0x8B: return DBF.to_workbook(d, o); | ||||
| 	} | ||||
| 	if(n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o); | ||||
|  | ||||
| @ -11,6 +11,8 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6. | ||||
| 
 | ||||
| [**Commercial Support**](http://sheetjs.com/reinforcements) | ||||
| 
 | ||||
| [**Rendered Documentation**](https://sheetjs.gitbooks.io/docs/) | ||||
| 
 | ||||
| [**File format support for known spreadsheet data formats:**](#file-formats) | ||||
| 
 | ||||
|  | ||||
|  | ||||
| @ -61,13 +61,14 @@ file but Excel will know how to handle it.  This library applies similar logic: | ||||
| 
 | ||||
| | Byte 0 | Raw File Type | Spreadsheet Types                                   | | ||||
| |:-------|:--------------|:----------------------------------------------------| | ||||
| | `0xD0` | CFB Container | BIFF 5/8 or password-protected XLSX/XLSB            | | ||||
| | `0xD0` | CFB Container | BIFF 5/8 or password-protected XLSX/XLSB or WQ3/QPW | | ||||
| | `0x09` | BIFF Stream   | BIFF 2/3/4/5                                        | | ||||
| | `0x3C` | XML/HTML      | SpreadsheetML / Flat ODS / UOS1 / HTML / plaintext  | | ||||
| | `0x50` | ZIP Archive   | XLSB or XLSX/M or ODS or UOS2 or plaintext          | | ||||
| | `0x49` | Plain Text    | SYLK or plaintext                                   | | ||||
| | `0x54` | Plain Text    | DIF or plaintext                                    | | ||||
| | `0xFE` | UTF16 Encoded | SpreadsheetML or Flat ODS or UOS1 or plaintext      | | ||||
| | `0x00` | Record Stream | Lotus WK\* or Quattro Pro or plaintext              | | ||||
| 
 | ||||
| DBF files are detected based on the first byte as well as the third and fourth | ||||
| bytes (corresponding to month and day of the file date) | ||||
|  | ||||
| @ -24,6 +24,8 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats: | ||||
| | Flat XML ODF Spreadsheet (FODS)                              |  :o:  |  :o:  | | ||||
| | Uniform Office Format Spreadsheet (标文通 UOS1/UOS2)         |  :o:  |       | | ||||
| | dBASE II/III/IV / Visual FoxPro (DBF)                        |  :o:  |       | | ||||
| | Lotus 1-2-3 (WKS/WK1/WK2/WK3/WK4/123)                        |  :o:  |       | | ||||
| | Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW)            |  :o:  |       | | ||||
| | **Other Common Spreadsheet Output Formats**                  |:-----:|:-----:| | ||||
| | HTML Tables                                                  |  :o:  |       | | ||||
| 
 | ||||
| @ -74,7 +76,34 @@ in an XLSX sub-file can be mapped to XLSB records in a corresponding sub-file. | ||||
| The `MS-XLSB` specification covers the basics of the file format, and other | ||||
| specifications expand on serialization of features like properties. | ||||
| 
 | ||||
| ### OpenDocument Spreadsheet (ODS/FODS) | ||||
| ### Delimiter-Separated Values (CSV/TXT) | ||||
| 
 | ||||
| Excel CSV deviates from RFC4180 in a number of important ways.  The generated | ||||
| CSV files should generally work in Excel although they may not work in RFC4180 | ||||
| compatible readers.  The parser should generally understand Excel CSV. | ||||
| 
 | ||||
| Excel TXT uses tab as the delimiter and codepage 1200. | ||||
| 
 | ||||
| ### Other Workbook Formats | ||||
| 
 | ||||
| Support for other formats is generally far XLS/XLSB/XLSX support, due in large | ||||
| part to a lack of publicly available documentation.  Test files were produced in | ||||
| the respective apps and compared to their XLS exports to determine structure. | ||||
| The main focus is data extraction. | ||||
| 
 | ||||
| #### Lotus 1-2-3 (WKS/WK1/WK2/WK3/WK4/123) | ||||
| 
 | ||||
| The Lotus formats consist of binary records similar to the BIFF structure. Lotus | ||||
| did release a whitepaper decades ago covering the original WK1 format.  Other | ||||
| features were deduced by producing files and comparing to Excel support. | ||||
| 
 | ||||
| #### Quattro Pro (WQ1/WQ2/WB1/WB2/WB3/QPW) | ||||
| 
 | ||||
| The Quattro Pro formats use binary records in the same way as BIFF and Lotus. | ||||
| Some of the newer formats (namely WB3 and QPW) use a CFB enclosure just like | ||||
| BIFF8 XLS. | ||||
| 
 | ||||
| #### OpenDocument Spreadsheet (ODS/FODS) | ||||
| 
 | ||||
| ODS is an XML-in-ZIP format akin to XLSX while FODS is an XML format akin to | ||||
| SpreadsheetML.  Both are detailed in the OASIS standard, but tools like LO/OO | ||||
| @ -86,14 +115,6 @@ UOS is a very similar format, and it comes in 2 varieties corresponding to ODS | ||||
| and FODS respectively.  For the most part, the difference between the formats | ||||
| lies in the names of tags and attributes. | ||||
| 
 | ||||
| ### Delimiter-Separated Values (CSV/TXT) | ||||
| 
 | ||||
| Excel CSV deviates from RFC4180 in a number of important ways.  The generated | ||||
| CSV files should generally work in Excel although they may not work in RFC4180 | ||||
| compatible readers.  The parser should generally understand Excel CSV. | ||||
| 
 | ||||
| Excel TXT uses tab as the delimiter and codepage 1200. | ||||
| 
 | ||||
| ### Other Single-Worksheet Formats | ||||
| 
 | ||||
| Many older formats supported only one worksheet: | ||||
|  | ||||
| @ -25,6 +25,10 @@ digraph G { | ||||
| 		dif   [label="DIF"]; | ||||
| 		slk   [label="SYLK"]; | ||||
| 		prn   [label="PRN"]; | ||||
| 		wk1   [label="WK1/2\n123"]; | ||||
| 		wk3   [label="WK3/4"]; | ||||
| 		wqb   [label="WQ*\nWB*"]; | ||||
| 		qpw   [label="QPW"]; | ||||
| 	} | ||||
| 
 | ||||
| 	subgraph WORKBOOK { | ||||
| @ -42,6 +46,8 @@ digraph G { | ||||
| 		fods -> csf | ||||
| 		csf -> fods | ||||
| 		uos -> csf | ||||
| 		wk3 -> csf | ||||
| 		qpw -> csf | ||||
| 	} | ||||
| 	subgraph WORKSHEET { | ||||
| 		edge [color=aquamarine4]; | ||||
| @ -52,6 +58,8 @@ digraph G { | ||||
| 		csf -> slk | ||||
| 		slk -> csf | ||||
| 		csf -> dif | ||||
| 		wk1 -> csf | ||||
| 		wqb -> csf | ||||
| 		dif -> csf | ||||
| 		prn -> csf | ||||
| 		csf -> prn | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								formats.png
									
									
									
									
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						
									
										
											BIN
										
									
								
								formats.png
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 168 KiB | 
| @ -47,9 +47,12 @@ | ||||
|   * [Excel 97-2004 Binary (BIFF8)](README.md#excel-97-2004-binary-biff8) | ||||
|   * [Excel 2003-2004 (SpreadsheetML)](README.md#excel-2003-2004-spreadsheetml) | ||||
|   * [Excel 2007+ Binary (XLSB, BIFF12)](README.md#excel-2007-binary-xlsb-biff12) | ||||
|   * [OpenDocument Spreadsheet (ODS/FODS)](README.md#opendocument-spreadsheet-odsfods) | ||||
|     + [Uniform Office Spreadsheet (UOS1/2)](README.md#uniform-office-spreadsheet-uos12) | ||||
|   * [Delimiter-Separated Values (CSV/TXT)](README.md#delimiter-separated-values-csvtxt) | ||||
|   * [Other Workbook Formats](README.md#other-workbook-formats) | ||||
|     + [Lotus 1-2-3 (WKS/WK1/WK2/WK3/WK4/123)](README.md#lotus-1-2-3-wkswk1wk2wk3wk4123) | ||||
|     + [Quattro Pro (WQ1/WQ2/WB1/WB2/WB3/QPW)](README.md#quattro-pro-wq1wq2wb1wb2wb3qpw) | ||||
|     + [OpenDocument Spreadsheet (ODS/FODS)](README.md#opendocument-spreadsheet-odsfods) | ||||
|     + [Uniform Office Spreadsheet (UOS1/2)](README.md#uniform-office-spreadsheet-uos12) | ||||
|   * [Other Single-Worksheet Formats](README.md#other-single-worksheet-formats) | ||||
|     + [dBASE and Visual FoxPro (DBF)](README.md#dbase-and-visual-foxpro-dbf) | ||||
|     + [Symbolic Link (SYLK)](README.md#symbolic-link-sylk) | ||||
|  | ||||
| @ -117,6 +117,7 @@ XLSX.writeFile(wb, 'sheetjs.slk'); | ||||
| XLSX.writeFile(wb, 'sheetjs.csv'); | ||||
| XLSX.writeFile(wb, 'sheetjs.txt'); | ||||
| XLSX.writeFile(wb, 'sheetjs.prn'); | ||||
| XLSX.writeFile(wb, 'sheetjs.dif'); | ||||
| 
 | ||||
| /* test by reading back files */ | ||||
| XLSX.readFile('sheetjs.xlsx'); | ||||
| @ -127,6 +128,7 @@ XLSX.readFile('sheetjs.xml.xls'); | ||||
| XLSX.readFile('sheetjs.ods'); | ||||
| XLSX.readFile('sheetjs.fods'); | ||||
| XLSX.readFile('sheetjs.slk'); | ||||
| //XLSX.readFile('sheetjs.csv');
 | ||||
| //XLSX.readFile('sheetjs.txt');
 | ||||
| XLSX.readFile('sheetjs.csv'); | ||||
| XLSX.readFile('sheetjs.txt'); | ||||
| XLSX.readFile('sheetjs.prn'); | ||||
| XLSX.readFile('sheetjs.dif'); | ||||
|  | ||||
							
								
								
									
										333
									
								
								xlsx.flow.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										333
									
								
								xlsx.flow.js
									
									
									
									
									
								
							| @ -5172,11 +5172,12 @@ var PRN = (function() { | ||||
| 		if(str.substr(0,1024).indexOf("\t") == -1) sep = ","; else sep = "\t"; | ||||
| 		var R = 0, C = 0, v = 0; | ||||
| 		var start = 0, end = 0, sepcc = sep.charCodeAt(0), instr = false, cc=0; | ||||
| 		str = str.replace(/\r\n/g, "\n"); | ||||
| 		for(;end < str.length;++end) switch((cc=str.charCodeAt(end))) { | ||||
| 			case 0x22: instr = !instr; break; | ||||
| 			case sepcc: case 0x0a: if(instr) break; | ||||
| 			var s = str.slice(start, end); | ||||
| 			var cell = ({}/*:any*/) | ||||
| 			var cell = ({}/*:any*/); | ||||
| 			if(s.charCodeAt(0) == 0x3D) { cell.t = 'n'; cell.f = s.substr(1); } | ||||
| 			else if(s == "TRUE") { cell.t = 'b'; cell.v = true; } | ||||
| 			else if(s == "FALSE") { cell.t = 'b'; cell.v = false; } | ||||
| @ -5186,7 +5187,7 @@ var PRN = (function() { | ||||
| 			start = end+1; | ||||
| 			if(range.e.c < C) range.e.c = C; | ||||
| 			if(range.e.r < R) range.e.r = R; | ||||
| 			if(cc == sepcc) ++C; else { C = 0; ++R; }; break; | ||||
| 			if(cc == sepcc) ++C; else { C = 0; ++R; } break; | ||||
| 			default: break; | ||||
| 		} | ||||
| 
 | ||||
| @ -5236,6 +5237,326 @@ var PRN = (function() { | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| var WK_ = (function() { | ||||
| 	function lotushopper(data, cb/*:RecordHopperCB*/, opts/*:any*/) { | ||||
| 		if(!data) return; | ||||
| 		prep_blob(data, data.l || 0); | ||||
| 		var Enum = opts.Enum || WK1Enum; | ||||
| 		while(data.l < data.length) { | ||||
| 			var RT = data.read_shift(2); | ||||
| 			var R = Enum[RT] || Enum[0xFF]; | ||||
| 			var length = data.read_shift(2); | ||||
| 			var tgt = data.l + length; | ||||
| 			var d = R.f(data, length, opts); | ||||
| 			data.l = tgt; | ||||
| 			if(cb(d, R, RT)) return; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	function lotus_to_workbook(d/*:RawData*/, opts) { | ||||
| 		switch(opts.type) { | ||||
| 			case 'base64': return lotus_to_workbook_buf(s2a(Base64.decode(d)), opts); | ||||
| 			case 'binary': return lotus_to_workbook_buf(s2a(d), opts); | ||||
| 			case 'buffer': | ||||
| 			case 'array': return lotus_to_workbook_buf(d, opts); | ||||
| 		} | ||||
| 		throw "Unsupported type " + opts.type; | ||||
| 	} | ||||
| 
 | ||||
| 	function lotus_to_workbook_buf(d,opts)/*:Workbook*/ { | ||||
| 		if(!d) return d; | ||||
| 		var o = opts || {}; | ||||
| 
 | ||||
| 		var s = {}, n = "Sheet1", sidx = 0; | ||||
| 		var sheets = {}, snames = [n]; | ||||
| 
 | ||||
| 		var refguess = {s: {r:0, c:0}, e: {r:0, c:0} }; | ||||
| 
 | ||||
| 		if(d[2] == 0x02) o.Enum = WK1Enum; | ||||
| 		else if(d[2] == 0x1a) o.Enum = WK3Enum; | ||||
| 		else if(d[2] == 0x0e) { o.Enum = WK3Enum; o.qpro = true; d.l = 0; } | ||||
| 		else throw new Error("Unrecognized LOTUS BOF " + d[2]); | ||||
| 		lotushopper(d, function(val, R, RT) { | ||||
| 			if(d[2] == 0x02) switch(RT) { | ||||
| 				case 0x00: | ||||
| 					o.vers = val; | ||||
| 					if(val >= 0x1000) o.qpro = true; | ||||
| 					break; | ||||
| 				case 0x06: refguess = val; break; /* RANGE */ | ||||
| 				case 0x0F: /* LABEL */ | ||||
| 					if(!opts.qpro) val[1].v = val[1].v.substr(1); | ||||
| 					/* falls through */ | ||||
| 				case 0x0D: /* INTEGER */ | ||||
| 				case 0x0E: /* NUMBER */ | ||||
| 				case 0x10: /* FORMULA */ | ||||
| 				case 0x33: /* STRING */ | ||||
| 					s[encode_cell(val[0])] = val[1]; | ||||
| 					/* TODO: FORMAT */ | ||||
| 					break; | ||||
| 			} else switch(RT) { | ||||
| 				case 0x16: /* LABEL16 */ | ||||
| 					val[1].v = val[1].v.substr(1); | ||||
| 					/* falls through */ | ||||
| 				case 0x17: /* NUMBER17 */ | ||||
| 				case 0x18: /* NUMBER18 */ | ||||
| 				case 0x19: /* FORMULA19 */ | ||||
| 				case 0x25: /* NUMBER25 */ | ||||
| 				case 0x27: /* NUMBER27 */ | ||||
| 				case 0x28: /* FORMULA28 */ | ||||
| 					if(val[3] > sidx) { | ||||
| 						s["!ref"] = encode_range(refguess); | ||||
| 						sheets[n] = s; | ||||
| 						s = {}; | ||||
| 						refguess = {s: {r:0, c:0}, e: {r:0, c:0} }; | ||||
| 						sidx = val[3]; n = "Sheet" + (sidx + 1); | ||||
| 						snames.push(n); | ||||
| 					} | ||||
| 					s[encode_cell(val[0])] = val[1]; | ||||
| 					if(refguess.e.c < val[0].c) refguess.e.c = val[0].c; | ||||
| 					if(refguess.e.r < val[0].r) refguess.e.r = val[0].r; | ||||
| 					break; | ||||
| 				default: break; | ||||
| 			} | ||||
| 		}, o); | ||||
| 
 | ||||
| 		s["!ref"] = encode_range(refguess); | ||||
| 		sheets[n] = s; | ||||
| 		return { SheetNames: snames, Sheets:sheets }; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_RANGE(blob, length) { | ||||
| 		var o = {s:{c:0,r:0},e:{c:0,r:0}}; | ||||
| 		o.s.c = blob.read_shift(2); | ||||
| 		o.s.r = blob.read_shift(2); | ||||
| 		o.e.c = blob.read_shift(2); | ||||
| 		o.e.r = blob.read_shift(2); | ||||
| 		if(o.s.c == 0xFFFF) o.s.c = o.e.c = o.s.r = o.e.r = 0; | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_cell(blob, length, opts) { | ||||
| 		var o = [{c:0,r:0}, {t:'n',v:0}, 0]; | ||||
| 		if(opts.qpro && opts.vers != 0x5120) { | ||||
| 			o[0].c = blob.read_shift(1); | ||||
| 			blob.l++; | ||||
| 			o[0].r = blob.read_shift(2); | ||||
| 			blob.l+=2; | ||||
| 		} else { | ||||
| 			o[2] = blob.read_shift(1); | ||||
| 			o[0].c = blob.read_shift(2); o[0].r = blob.read_shift(2); | ||||
| 		} | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_LABEL(blob, length, opts) { | ||||
| 		var tgt = blob.l + length; | ||||
| 		var o = parse_cell(blob, length, opts); | ||||
| 		o[1].t = 's'; | ||||
| 		if(opts.vers == 0x5120) { | ||||
| 			blob.l++; | ||||
| 			var len = blob.read_shift(1); | ||||
| 			o[1].v = blob.read_shift(len, 'utf8'); | ||||
| 			return o; | ||||
| 		} | ||||
| 		if(opts.qpro) blob.l++; | ||||
| 		o[1].v = blob.read_shift(tgt - blob.l, 'cstr'); | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_INTEGER(blob, length, opts) { | ||||
| 		var o = parse_cell(blob, length, opts); | ||||
| 		o[1].v = blob.read_shift(2, 'i'); | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_NUMBER(blob, length, opts) { | ||||
| 		var o = parse_cell(blob, length, opts); | ||||
| 		o[1].v = blob.read_shift(8, 'f'); | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_FORMULA(blob, length, opts) { | ||||
| 		var tgt = blob.l + length; | ||||
| 		var o = parse_cell(blob, length, opts); | ||||
| 		/* TODO: formula */ | ||||
| 		o[1].v = blob.read_shift(8, 'f'); | ||||
| 		if(opts.qpro) blob.l = tgt; | ||||
| 		else { | ||||
| 			var flen = blob.read_shift(2); | ||||
| 			blob.l += flen; | ||||
| 		} | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_cell_3(blob, length) { | ||||
| 		var o = [{c:0,r:0}, {t:'n',v:0}, 0]; | ||||
| 		o[0].r = blob.read_shift(2); o[3] = blob[blob.l++]; o[0].c = blob[blob.l++]; | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_LABEL_16(blob, length) { | ||||
| 		var o = parse_cell_3(blob, length); | ||||
| 		o[1].t = 's'; | ||||
| 		o[1].v = blob.read_shift(length - 4, 'cstr'); | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_NUMBER_18(blob, length) { | ||||
| 		var o = parse_cell_3(blob, length); | ||||
| 		o[1].v = blob.read_shift(2); | ||||
| 		var v = o[1].v >> 1; | ||||
| 		/* TODO: figure out all of the corner cases */ | ||||
| 		if(o[1].v & 0x1) { | ||||
| 			switch(v & 0x07) { | ||||
| 				case 1: v = (v >> 3) * 500; break; | ||||
| 				case 2: v = (v >> 3) / 20; break; | ||||
| 				case 4: v = (v >> 3) / 2000; break; | ||||
| 				case 6: v = (v >> 3) / 16; break; | ||||
| 				case 7: v = (v >> 3) / 64; break; | ||||
| 				default: throw "unknown NUMBER_18 encoding " + (v & 0x07); | ||||
| 			} | ||||
| 		} | ||||
| 		o[1].v = v; | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_NUMBER_17(blob, length) { | ||||
| 		var o = parse_cell_3(blob, length); | ||||
| 		var v1 = blob.read_shift(4); | ||||
| 		var v2 = blob.read_shift(4); | ||||
| 		var e = blob.read_shift(2); | ||||
| 		if(e == 0xFFFF) { o[1].v = 0; return o; } | ||||
| 		var s = e & 0x8000; e = (e&0x7FFF) - 16446; | ||||
| 		o[1].v = ((e > 0 ? (v2 << e) : (v2 >>> -e)) + (e > -32 ? (v1 << (e + 32)) : (v1 >>> -(e + 32)))); | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_FORMULA_19(blob, length) { | ||||
| 		var o = parse_NUMBER_17(blob, 14); | ||||
| 		blob.l += length - 14; /* TODO: formula */ | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_NUMBER_25(blob, length) { | ||||
| 		var o = parse_cell_3(blob, length); | ||||
| 		var v1 = blob.read_shift(4); | ||||
| 		o[1].v = v1 >> 6; | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_NUMBER_27(blob, length) { | ||||
| 		var o = parse_cell_3(blob, length); | ||||
| 		var v1 = blob.read_shift(8,'f'); | ||||
| 		o[1].v = v1; | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_FORMULA_28(blob, length) { | ||||
| 		var o = parse_NUMBER_27(blob, 14); | ||||
| 		blob.l += length - 10; /* TODO: formula */ | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	var WK1Enum = { | ||||
| 		/*::[*/0x0000/*::]*/: { n:"BOF", f:parseuint16 }, | ||||
| 		/*::[*/0x0001/*::]*/: { n:"EOF", f:parsenoop }, | ||||
| 		/*::[*/0x0002/*::]*/: { n: "CALCMODE", f:parsenoop }, | ||||
| 		/*::[*/0x0003/*::]*/: { n:"CALCORDER", f:parsenoop }, | ||||
| 		/*::[*/0x0004/*::]*/: { n:"SPLIT", f:parsenoop }, | ||||
| 		/*::[*/0x0005/*::]*/: { n:"SYNC", f:parsenoop }, | ||||
| 		/*::[*/0x0006/*::]*/: { n:"RANGE", f:parse_RANGE }, | ||||
| 		/*::[*/0x0007/*::]*/: { n:"WINDOW1", f:parsenoop }, | ||||
| 		/*::[*/0x0008/*::]*/: { n:"COLW1", f:parsenoop }, | ||||
| 		/*::[*/0x0009/*::]*/: { n:"WINTWO", f:parsenoop }, | ||||
| 		/*::[*/0x000A/*::]*/: { n:"COLW2", f:parsenoop }, | ||||
| 		/*::[*/0x000B/*::]*/: { n:"NAME", f:parsenoop }, | ||||
| 		/*::[*/0x000C/*::]*/: { n:"BLANK", f:parsenoop }, | ||||
| 		/*::[*/0x000D/*::]*/: { n:"INTEGER", f:parse_INTEGER }, | ||||
| 		/*::[*/0x000E/*::]*/: { n:"NUMBER", f:parse_NUMBER }, | ||||
| 		/*::[*/0x000F/*::]*/: { n:"LABEL", f:parse_LABEL }, | ||||
| 		/*::[*/0x0010/*::]*/: { n:"FORMULA", f:parse_FORMULA }, | ||||
| 		/*::[*/0x0018/*::]*/: { n:"TABLE", f:parsenoop }, | ||||
| 		/*::[*/0x0019/*::]*/: { n:"ORANGE", f:parsenoop }, | ||||
| 		/*::[*/0x001A/*::]*/: { n:"PRANGE", f:parsenoop }, | ||||
| 		/*::[*/0x001B/*::]*/: { n:"SRANGE", f:parsenoop }, | ||||
| 		/*::[*/0x001C/*::]*/: { n:"FRANGE", f:parsenoop }, | ||||
| 		/*::[*/0x001D/*::]*/: { n:"KRANGE1", f:parsenoop }, | ||||
| 		/*::[*/0x0020/*::]*/: { n:"HRANGE", f:parsenoop }, | ||||
| 		/*::[*/0x0023/*::]*/: { n:"KRANGE2", f:parsenoop }, | ||||
| 		/*::[*/0x0024/*::]*/: { n:"PROTEC", f:parsenoop }, | ||||
| 		/*::[*/0x0025/*::]*/: { n:"FOOTER", f:parsenoop }, | ||||
| 		/*::[*/0x0026/*::]*/: { n:"HEADER", f:parsenoop }, | ||||
| 		/*::[*/0x0027/*::]*/: { n:"SETUP", f:parsenoop }, | ||||
| 		/*::[*/0x0028/*::]*/: { n:"MARGINS", f:parsenoop }, | ||||
| 		/*::[*/0x0029/*::]*/: { n:"LABELFMT", f:parsenoop }, | ||||
| 		/*::[*/0x002A/*::]*/: { n:"TITLES", f:parsenoop }, | ||||
| 		/*::[*/0x002B/*::]*/: { n:"SHEETJS", f:parsenoop }, | ||||
| 		/*::[*/0x002D/*::]*/: { n:"GRAPH", f:parsenoop }, | ||||
| 		/*::[*/0x002E/*::]*/: { n:"NGRAPH", f:parsenoop }, | ||||
| 		/*::[*/0x002F/*::]*/: { n:"CALCCOUNT", f:parsenoop }, | ||||
| 		/*::[*/0x0030/*::]*/: { n:"UNFORMATTED", f:parsenoop }, | ||||
| 		/*::[*/0x0031/*::]*/: { n:"CURSORW12", f:parsenoop }, | ||||
| 		/*::[*/0x0032/*::]*/: { n:"WINDOW", f:parsenoop }, | ||||
| 		/*::[*/0x0033/*::]*/: { n:"STRING", f:parse_LABEL }, | ||||
| 		/*::[*/0x0037/*::]*/: { n:"PASSWORD", f:parsenoop }, | ||||
| 		/*::[*/0x0038/*::]*/: { n:"LOCKED", f:parsenoop }, | ||||
| 		/*::[*/0x003C/*::]*/: { n:"QUERY", f:parsenoop }, | ||||
| 		/*::[*/0x003D/*::]*/: { n:"QUERYNAME", f:parsenoop }, | ||||
| 		/*::[*/0x003E/*::]*/: { n:"PRINT", f:parsenoop }, | ||||
| 		/*::[*/0x003F/*::]*/: { n:"PRINTNAME", f:parsenoop }, | ||||
| 		/*::[*/0x0040/*::]*/: { n:"GRAPH2", f:parsenoop }, | ||||
| 		/*::[*/0x0041/*::]*/: { n:"GRAPHNAME", f:parsenoop }, | ||||
| 		/*::[*/0x0042/*::]*/: { n:"ZOOM", f:parsenoop }, | ||||
| 		/*::[*/0x0043/*::]*/: { n:"SYMSPLIT", f:parsenoop }, | ||||
| 		/*::[*/0x0044/*::]*/: { n:"NSROWS", f:parsenoop }, | ||||
| 		/*::[*/0x0045/*::]*/: { n:"NSCOLS", f:parsenoop }, | ||||
| 		/*::[*/0x0046/*::]*/: { n:"RULER", f:parsenoop }, | ||||
| 		/*::[*/0x0047/*::]*/: { n:"NNAME", f:parsenoop }, | ||||
| 		/*::[*/0x0048/*::]*/: { n:"ACOMM", f:parsenoop }, | ||||
| 		/*::[*/0x0049/*::]*/: { n:"AMACRO", f:parsenoop }, | ||||
| 		/*::[*/0x004A/*::]*/: { n:"PARSE", f:parsenoop }, | ||||
| 		/*::[*/0x00FF/*::]*/: { n:"", f:parsenoop } | ||||
| 	}; | ||||
| 
 | ||||
| 	var WK3Enum = { | ||||
| 		/*::[*/0x0000/*::]*/: { n:"BOF", f:parsenoop }, | ||||
| 		/*::[*/0x0001/*::]*/: { n:"EOF", f:parsenoop }, | ||||
| 		/*::[*/0x0003/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0004/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0005/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0006/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0007/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0009/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x000a/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x000b/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x000c/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x000e/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x000f/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0010/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0011/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0012/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0013/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0015/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0016/*::]*/: { n:"LABEL16", f:parse_LABEL_16}, | ||||
| 		/*::[*/0x0017/*::]*/: { n:"NUMBER17", f:parse_NUMBER_17 }, | ||||
| 		/*::[*/0x0018/*::]*/: { n:"NUMBER18", f:parse_NUMBER_18 }, | ||||
| 		/*::[*/0x0019/*::]*/: { n:"FORMULA19", f:parse_FORMULA_19}, | ||||
| 		/*::[*/0x001a/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x001b/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x001c/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x001d/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x001e/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x001f/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0021/*::]*/: { n:"??", f:parsenoop }, | ||||
| 		/*::[*/0x0025/*::]*/: { n:"NUMBER25", f:parse_NUMBER_25 }, | ||||
| 		/*::[*/0x0027/*::]*/: { n:"NUMBER27", f:parse_NUMBER_27 }, | ||||
| 		/*::[*/0x0028/*::]*/: { n:"FORMULA28", f:parse_FORMULA_28 }, | ||||
| 		/*::[*/0x00FF/*::]*/: { n:"", f:parsenoop } | ||||
| 	}; | ||||
| 	return { | ||||
| 		to_workbook: lotus_to_workbook | ||||
| 	}; | ||||
| })(); | ||||
| /* 18.4.1 charset to codepage mapping */ | ||||
| var CS2CP = ({ | ||||
| 	/*::[*/0/*::]*/:    1252, /* ANSI */ | ||||
| @ -12750,6 +13071,10 @@ if(CompObj) CompObjP = parse_compobj(CompObj); | ||||
| if(options.bookProps && !options.bookSheets) WorkbookP = ({}/*:any*/); | ||||
| else { | ||||
| 	if(Workbook) WorkbookP = parse_workbook(Workbook.content, options, !!Workbook.find); | ||||
| 	/* Quattro Pro 7-8 */ | ||||
| 	else if(cfb.find('PerfectOffice_MAIN')) WorkbookP = WK_.to_workbook(cfb.find('PerfectOffice_MAIN').content, options); | ||||
| 	/* Quattro Pro 9 */ | ||||
| 	else if(cfb.find('NativeContent_MAIN')) WorkbookP = WK_.to_workbook(cfb.find('NativeContent_MAIN').content, options); | ||||
| 	else throw new Error("Cannot find Workbook stream"); | ||||
| } | ||||
| 
 | ||||
| @ -14013,7 +14338,7 @@ var XLSRecordEnum = { | ||||
| 	/*::[*/0x08c5/*::]*/: { n:"ListCF", f:parsenoop }, | ||||
| 	/*::[*/0x08c6/*::]*/: { n:"FMQry", f:parsenoop }, | ||||
| 	/*::[*/0x08c7/*::]*/: { n:"FMSQry", f:parsenoop }, | ||||
| 	/*::[*/0x08c8/*::]*/: { n:"PLV", f:parsenoop }, /* supposedly PLV for Excel 11 */ | ||||
| 	/*::[*/0x08c8/*::]*/: { n:"PLV", f:parsenoop }, | ||||
| 	/*::[*/0x08c9/*::]*/: { n:"LnExt", f:parsenoop }, | ||||
| 	/*::[*/0x08ca/*::]*/: { n:"MkrExt", f:parsenoop }, | ||||
| 	/*::[*/0x08cb/*::]*/: { n:"CrtCoopt", f:parsenoop }, | ||||
| @ -14187,6 +14512,7 @@ function parse_dom_table(table/*:HTMLElement*/, opts/*:?any*/)/*:Worksheet*/ { | ||||
| 			var o = {t:'s', v:v}; | ||||
| 			if(v != null && v.length && !isNaN(Number(v))) o = {t:'n', v:Number(v)}; | ||||
| 			ws[encode_cell({c:C, r:R})] = o; | ||||
| 			if(range.e.c < C) range.e.c = C; | ||||
| 			C += CS; | ||||
| 		} | ||||
| 	} | ||||
| @ -15124,6 +15450,7 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ { | ||||
| 		case 0x50: if(n[1] == 0x4B && n[2] < 0x20 && n[3] < 0x20) return read_zip(d, o); break; | ||||
| 		case 0xEF: return parse_xlml(d, o); | ||||
| 		case 0xFF: if(n[1] == 0xFE){ return read_utf16(d, o); } break; | ||||
| 		case 0x00: if(n[1] == 0x00 && n[2] >= 0x02 && n[3] == 0x00) return WK_.to_workbook(d, o); break; | ||||
| 		case 0x03: case 0x83: case 0x8B: return DBF.to_workbook(d, o); | ||||
| 	} | ||||
| 	if(n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o); | ||||
|  | ||||
							
								
								
									
										333
									
								
								xlsx.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										333
									
								
								xlsx.js
									
									
									
									
									
								
							| @ -5116,11 +5116,12 @@ var PRN = (function() { | ||||
| 		if(str.substr(0,1024).indexOf("\t") == -1) sep = ","; else sep = "\t"; | ||||
| 		var R = 0, C = 0, v = 0; | ||||
| 		var start = 0, end = 0, sepcc = sep.charCodeAt(0), instr = false, cc=0; | ||||
| 		str = str.replace(/\r\n/g, "\n"); | ||||
| 		for(;end < str.length;++end) switch((cc=str.charCodeAt(end))) { | ||||
| 			case 0x22: instr = !instr; break; | ||||
| 			case sepcc: case 0x0a: if(instr) break; | ||||
| 			var s = str.slice(start, end); | ||||
| 			var cell = ({}) | ||||
| 			var cell = ({}); | ||||
| 			if(s.charCodeAt(0) == 0x3D) { cell.t = 'n'; cell.f = s.substr(1); } | ||||
| 			else if(s == "TRUE") { cell.t = 'b'; cell.v = true; } | ||||
| 			else if(s == "FALSE") { cell.t = 'b'; cell.v = false; } | ||||
| @ -5130,7 +5131,7 @@ var PRN = (function() { | ||||
| 			start = end+1; | ||||
| 			if(range.e.c < C) range.e.c = C; | ||||
| 			if(range.e.r < R) range.e.r = R; | ||||
| 			if(cc == sepcc) ++C; else { C = 0; ++R; }; break; | ||||
| 			if(cc == sepcc) ++C; else { C = 0; ++R; } break; | ||||
| 			default: break; | ||||
| 		} | ||||
| 
 | ||||
| @ -5180,6 +5181,326 @@ var PRN = (function() { | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| var WK_ = (function() { | ||||
| 	function lotushopper(data, cb, opts) { | ||||
| 		if(!data) return; | ||||
| 		prep_blob(data, data.l || 0); | ||||
| 		var Enum = opts.Enum || WK1Enum; | ||||
| 		while(data.l < data.length) { | ||||
| 			var RT = data.read_shift(2); | ||||
| 			var R = Enum[RT] || Enum[0xFF]; | ||||
| 			var length = data.read_shift(2); | ||||
| 			var tgt = data.l + length; | ||||
| 			var d = R.f(data, length, opts); | ||||
| 			data.l = tgt; | ||||
| 			if(cb(d, R, RT)) return; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	function lotus_to_workbook(d, opts) { | ||||
| 		switch(opts.type) { | ||||
| 			case 'base64': return lotus_to_workbook_buf(s2a(Base64.decode(d)), opts); | ||||
| 			case 'binary': return lotus_to_workbook_buf(s2a(d), opts); | ||||
| 			case 'buffer': | ||||
| 			case 'array': return lotus_to_workbook_buf(d, opts); | ||||
| 		} | ||||
| 		throw "Unsupported type " + opts.type; | ||||
| 	} | ||||
| 
 | ||||
| 	function lotus_to_workbook_buf(d,opts) { | ||||
| 		if(!d) return d; | ||||
| 		var o = opts || {}; | ||||
| 
 | ||||
| 		var s = {}, n = "Sheet1", sidx = 0; | ||||
| 		var sheets = {}, snames = [n]; | ||||
| 
 | ||||
| 		var refguess = {s: {r:0, c:0}, e: {r:0, c:0} }; | ||||
| 
 | ||||
| 		if(d[2] == 0x02) o.Enum = WK1Enum; | ||||
| 		else if(d[2] == 0x1a) o.Enum = WK3Enum; | ||||
| 		else if(d[2] == 0x0e) { o.Enum = WK3Enum; o.qpro = true; d.l = 0; } | ||||
| 		else throw new Error("Unrecognized LOTUS BOF " + d[2]); | ||||
| 		lotushopper(d, function(val, R, RT) { | ||||
| 			if(d[2] == 0x02) switch(RT) { | ||||
| 				case 0x00: | ||||
| 					o.vers = val; | ||||
| 					if(val >= 0x1000) o.qpro = true; | ||||
| 					break; | ||||
| 				case 0x06: refguess = val; break; /* RANGE */ | ||||
| 				case 0x0F: /* LABEL */ | ||||
| 					if(!opts.qpro) val[1].v = val[1].v.substr(1); | ||||
| 					/* falls through */ | ||||
| 				case 0x0D: /* INTEGER */ | ||||
| 				case 0x0E: /* NUMBER */ | ||||
| 				case 0x10: /* FORMULA */ | ||||
| 				case 0x33: /* STRING */ | ||||
| 					s[encode_cell(val[0])] = val[1]; | ||||
| 					/* TODO: FORMAT */ | ||||
| 					break; | ||||
| 			} else switch(RT) { | ||||
| 				case 0x16: /* LABEL16 */ | ||||
| 					val[1].v = val[1].v.substr(1); | ||||
| 					/* falls through */ | ||||
| 				case 0x17: /* NUMBER17 */ | ||||
| 				case 0x18: /* NUMBER18 */ | ||||
| 				case 0x19: /* FORMULA19 */ | ||||
| 				case 0x25: /* NUMBER25 */ | ||||
| 				case 0x27: /* NUMBER27 */ | ||||
| 				case 0x28: /* FORMULA28 */ | ||||
| 					if(val[3] > sidx) { | ||||
| 						s["!ref"] = encode_range(refguess); | ||||
| 						sheets[n] = s; | ||||
| 						s = {}; | ||||
| 						refguess = {s: {r:0, c:0}, e: {r:0, c:0} }; | ||||
| 						sidx = val[3]; n = "Sheet" + (sidx + 1); | ||||
| 						snames.push(n); | ||||
| 					} | ||||
| 					s[encode_cell(val[0])] = val[1]; | ||||
| 					if(refguess.e.c < val[0].c) refguess.e.c = val[0].c; | ||||
| 					if(refguess.e.r < val[0].r) refguess.e.r = val[0].r; | ||||
| 					break; | ||||
| 				default: break; | ||||
| 			} | ||||
| 		}, o); | ||||
| 
 | ||||
| 		s["!ref"] = encode_range(refguess); | ||||
| 		sheets[n] = s; | ||||
| 		return { SheetNames: snames, Sheets:sheets }; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_RANGE(blob, length) { | ||||
| 		var o = {s:{c:0,r:0},e:{c:0,r:0}}; | ||||
| 		o.s.c = blob.read_shift(2); | ||||
| 		o.s.r = blob.read_shift(2); | ||||
| 		o.e.c = blob.read_shift(2); | ||||
| 		o.e.r = blob.read_shift(2); | ||||
| 		if(o.s.c == 0xFFFF) o.s.c = o.e.c = o.s.r = o.e.r = 0; | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_cell(blob, length, opts) { | ||||
| 		var o = [{c:0,r:0}, {t:'n',v:0}, 0]; | ||||
| 		if(opts.qpro && opts.vers != 0x5120) { | ||||
| 			o[0].c = blob.read_shift(1); | ||||
| 			blob.l++; | ||||
| 			o[0].r = blob.read_shift(2); | ||||
| 			blob.l+=2; | ||||
| 		} else { | ||||
| 			o[2] = blob.read_shift(1); | ||||
| 			o[0].c = blob.read_shift(2); o[0].r = blob.read_shift(2); | ||||
| 		} | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_LABEL(blob, length, opts) { | ||||
| 		var tgt = blob.l + length; | ||||
| 		var o = parse_cell(blob, length, opts); | ||||
| 		o[1].t = 's'; | ||||
| 		if(opts.vers == 0x5120) { | ||||
| 			blob.l++; | ||||
| 			var len = blob.read_shift(1); | ||||
| 			o[1].v = blob.read_shift(len, 'utf8'); | ||||
| 			return o; | ||||
| 		} | ||||
| 		if(opts.qpro) blob.l++; | ||||
| 		o[1].v = blob.read_shift(tgt - blob.l, 'cstr'); | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_INTEGER(blob, length, opts) { | ||||
| 		var o = parse_cell(blob, length, opts); | ||||
| 		o[1].v = blob.read_shift(2, 'i'); | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_NUMBER(blob, length, opts) { | ||||
| 		var o = parse_cell(blob, length, opts); | ||||
| 		o[1].v = blob.read_shift(8, 'f'); | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_FORMULA(blob, length, opts) { | ||||
| 		var tgt = blob.l + length; | ||||
| 		var o = parse_cell(blob, length, opts); | ||||
| 		/* TODO: formula */ | ||||
| 		o[1].v = blob.read_shift(8, 'f'); | ||||
| 		if(opts.qpro) blob.l = tgt; | ||||
| 		else { | ||||
| 			var flen = blob.read_shift(2); | ||||
| 			blob.l += flen; | ||||
| 		} | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_cell_3(blob, length) { | ||||
| 		var o = [{c:0,r:0}, {t:'n',v:0}, 0]; | ||||
| 		o[0].r = blob.read_shift(2); o[3] = blob[blob.l++]; o[0].c = blob[blob.l++]; | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_LABEL_16(blob, length) { | ||||
| 		var o = parse_cell_3(blob, length); | ||||
| 		o[1].t = 's'; | ||||
| 		o[1].v = blob.read_shift(length - 4, 'cstr'); | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_NUMBER_18(blob, length) { | ||||
| 		var o = parse_cell_3(blob, length); | ||||
| 		o[1].v = blob.read_shift(2); | ||||
| 		var v = o[1].v >> 1; | ||||
| 		/* TODO: figure out all of the corner cases */ | ||||
| 		if(o[1].v & 0x1) { | ||||
| 			switch(v & 0x07) { | ||||
| 				case 1: v = (v >> 3) * 500; break; | ||||
| 				case 2: v = (v >> 3) / 20; break; | ||||
| 				case 4: v = (v >> 3) / 2000; break; | ||||
| 				case 6: v = (v >> 3) / 16; break; | ||||
| 				case 7: v = (v >> 3) / 64; break; | ||||
| 				default: throw "unknown NUMBER_18 encoding " + (v & 0x07); | ||||
| 			} | ||||
| 		} | ||||
| 		o[1].v = v; | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_NUMBER_17(blob, length) { | ||||
| 		var o = parse_cell_3(blob, length); | ||||
| 		var v1 = blob.read_shift(4); | ||||
| 		var v2 = blob.read_shift(4); | ||||
| 		var e = blob.read_shift(2); | ||||
| 		if(e == 0xFFFF) { o[1].v = 0; return o; } | ||||
| 		var s = e & 0x8000; e = (e&0x7FFF) - 16446; | ||||
| 		o[1].v = ((e > 0 ? (v2 << e) : (v2 >>> -e)) + (e > -32 ? (v1 << (e + 32)) : (v1 >>> -(e + 32)))); | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_FORMULA_19(blob, length) { | ||||
| 		var o = parse_NUMBER_17(blob, 14); | ||||
| 		blob.l += length - 14; /* TODO: formula */ | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_NUMBER_25(blob, length) { | ||||
| 		var o = parse_cell_3(blob, length); | ||||
| 		var v1 = blob.read_shift(4); | ||||
| 		o[1].v = v1 >> 6; | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_NUMBER_27(blob, length) { | ||||
| 		var o = parse_cell_3(blob, length); | ||||
| 		var v1 = blob.read_shift(8,'f'); | ||||
| 		o[1].v = v1; | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	function parse_FORMULA_28(blob, length) { | ||||
| 		var o = parse_NUMBER_27(blob, 14); | ||||
| 		blob.l += length - 10; /* TODO: formula */ | ||||
| 		return o; | ||||
| 	} | ||||
| 
 | ||||
| 	var WK1Enum = { | ||||
| 0x0000: { n:"BOF", f:parseuint16 }, | ||||
| 0x0001: { n:"EOF", f:parsenoop }, | ||||
| 0x0002: { n: "CALCMODE", f:parsenoop }, | ||||
| 0x0003: { n:"CALCORDER", f:parsenoop }, | ||||
| 0x0004: { n:"SPLIT", f:parsenoop }, | ||||
| 0x0005: { n:"SYNC", f:parsenoop }, | ||||
| 0x0006: { n:"RANGE", f:parse_RANGE }, | ||||
| 0x0007: { n:"WINDOW1", f:parsenoop }, | ||||
| 0x0008: { n:"COLW1", f:parsenoop }, | ||||
| 0x0009: { n:"WINTWO", f:parsenoop }, | ||||
| 0x000A: { n:"COLW2", f:parsenoop }, | ||||
| 0x000B: { n:"NAME", f:parsenoop }, | ||||
| 0x000C: { n:"BLANK", f:parsenoop }, | ||||
| 0x000D: { n:"INTEGER", f:parse_INTEGER }, | ||||
| 0x000E: { n:"NUMBER", f:parse_NUMBER }, | ||||
| 0x000F: { n:"LABEL", f:parse_LABEL }, | ||||
| 0x0010: { n:"FORMULA", f:parse_FORMULA }, | ||||
| 0x0018: { n:"TABLE", f:parsenoop }, | ||||
| 0x0019: { n:"ORANGE", f:parsenoop }, | ||||
| 0x001A: { n:"PRANGE", f:parsenoop }, | ||||
| 0x001B: { n:"SRANGE", f:parsenoop }, | ||||
| 0x001C: { n:"FRANGE", f:parsenoop }, | ||||
| 0x001D: { n:"KRANGE1", f:parsenoop }, | ||||
| 0x0020: { n:"HRANGE", f:parsenoop }, | ||||
| 0x0023: { n:"KRANGE2", f:parsenoop }, | ||||
| 0x0024: { n:"PROTEC", f:parsenoop }, | ||||
| 0x0025: { n:"FOOTER", f:parsenoop }, | ||||
| 0x0026: { n:"HEADER", f:parsenoop }, | ||||
| 0x0027: { n:"SETUP", f:parsenoop }, | ||||
| 0x0028: { n:"MARGINS", f:parsenoop }, | ||||
| 0x0029: { n:"LABELFMT", f:parsenoop }, | ||||
| 0x002A: { n:"TITLES", f:parsenoop }, | ||||
| 0x002B: { n:"SHEETJS", f:parsenoop }, | ||||
| 0x002D: { n:"GRAPH", f:parsenoop }, | ||||
| 0x002E: { n:"NGRAPH", f:parsenoop }, | ||||
| 0x002F: { n:"CALCCOUNT", f:parsenoop }, | ||||
| 0x0030: { n:"UNFORMATTED", f:parsenoop }, | ||||
| 0x0031: { n:"CURSORW12", f:parsenoop }, | ||||
| 0x0032: { n:"WINDOW", f:parsenoop }, | ||||
| 0x0033: { n:"STRING", f:parse_LABEL }, | ||||
| 0x0037: { n:"PASSWORD", f:parsenoop }, | ||||
| 0x0038: { n:"LOCKED", f:parsenoop }, | ||||
| 0x003C: { n:"QUERY", f:parsenoop }, | ||||
| 0x003D: { n:"QUERYNAME", f:parsenoop }, | ||||
| 0x003E: { n:"PRINT", f:parsenoop }, | ||||
| 0x003F: { n:"PRINTNAME", f:parsenoop }, | ||||
| 0x0040: { n:"GRAPH2", f:parsenoop }, | ||||
| 0x0041: { n:"GRAPHNAME", f:parsenoop }, | ||||
| 0x0042: { n:"ZOOM", f:parsenoop }, | ||||
| 0x0043: { n:"SYMSPLIT", f:parsenoop }, | ||||
| 0x0044: { n:"NSROWS", f:parsenoop }, | ||||
| 0x0045: { n:"NSCOLS", f:parsenoop }, | ||||
| 0x0046: { n:"RULER", f:parsenoop }, | ||||
| 0x0047: { n:"NNAME", f:parsenoop }, | ||||
| 0x0048: { n:"ACOMM", f:parsenoop }, | ||||
| 0x0049: { n:"AMACRO", f:parsenoop }, | ||||
| 0x004A: { n:"PARSE", f:parsenoop }, | ||||
| 0x00FF: { n:"", f:parsenoop } | ||||
| 	}; | ||||
| 
 | ||||
| 	var WK3Enum = { | ||||
| 0x0000: { n:"BOF", f:parsenoop }, | ||||
| 0x0001: { n:"EOF", f:parsenoop }, | ||||
| 0x0003: { n:"??", f:parsenoop }, | ||||
| 0x0004: { n:"??", f:parsenoop }, | ||||
| 0x0005: { n:"??", f:parsenoop }, | ||||
| 0x0006: { n:"??", f:parsenoop }, | ||||
| 0x0007: { n:"??", f:parsenoop }, | ||||
| 0x0009: { n:"??", f:parsenoop }, | ||||
| 0x000a: { n:"??", f:parsenoop }, | ||||
| 0x000b: { n:"??", f:parsenoop }, | ||||
| 0x000c: { n:"??", f:parsenoop }, | ||||
| 0x000e: { n:"??", f:parsenoop }, | ||||
| 0x000f: { n:"??", f:parsenoop }, | ||||
| 0x0010: { n:"??", f:parsenoop }, | ||||
| 0x0011: { n:"??", f:parsenoop }, | ||||
| 0x0012: { n:"??", f:parsenoop }, | ||||
| 0x0013: { n:"??", f:parsenoop }, | ||||
| 0x0015: { n:"??", f:parsenoop }, | ||||
| 0x0016: { n:"LABEL16", f:parse_LABEL_16}, | ||||
| 0x0017: { n:"NUMBER17", f:parse_NUMBER_17 }, | ||||
| 0x0018: { n:"NUMBER18", f:parse_NUMBER_18 }, | ||||
| 0x0019: { n:"FORMULA19", f:parse_FORMULA_19}, | ||||
| 0x001a: { n:"??", f:parsenoop }, | ||||
| 0x001b: { n:"??", f:parsenoop }, | ||||
| 0x001c: { n:"??", f:parsenoop }, | ||||
| 0x001d: { n:"??", f:parsenoop }, | ||||
| 0x001e: { n:"??", f:parsenoop }, | ||||
| 0x001f: { n:"??", f:parsenoop }, | ||||
| 0x0021: { n:"??", f:parsenoop }, | ||||
| 0x0025: { n:"NUMBER25", f:parse_NUMBER_25 }, | ||||
| 0x0027: { n:"NUMBER27", f:parse_NUMBER_27 }, | ||||
| 0x0028: { n:"FORMULA28", f:parse_FORMULA_28 }, | ||||
| 0x00FF: { n:"", f:parsenoop } | ||||
| 	}; | ||||
| 	return { | ||||
| 		to_workbook: lotus_to_workbook | ||||
| 	}; | ||||
| })(); | ||||
| /* 18.4.1 charset to codepage mapping */ | ||||
| var CS2CP = ({ | ||||
| 0:    1252, /* ANSI */ | ||||
| @ -12689,6 +13010,10 @@ if(CompObj) CompObjP = parse_compobj(CompObj); | ||||
| if(options.bookProps && !options.bookSheets) WorkbookP = ({}); | ||||
| else { | ||||
| 	if(Workbook) WorkbookP = parse_workbook(Workbook.content, options, !!Workbook.find); | ||||
| 	/* Quattro Pro 7-8 */ | ||||
| 	else if(cfb.find('PerfectOffice_MAIN')) WorkbookP = WK_.to_workbook(cfb.find('PerfectOffice_MAIN').content, options); | ||||
| 	/* Quattro Pro 9 */ | ||||
| 	else if(cfb.find('NativeContent_MAIN')) WorkbookP = WK_.to_workbook(cfb.find('NativeContent_MAIN').content, options); | ||||
| 	else throw new Error("Cannot find Workbook stream"); | ||||
| } | ||||
| 
 | ||||
| @ -13952,7 +14277,7 @@ var XLSRecordEnum = { | ||||
| 0x08c5: { n:"ListCF", f:parsenoop }, | ||||
| 0x08c6: { n:"FMQry", f:parsenoop }, | ||||
| 0x08c7: { n:"FMSQry", f:parsenoop }, | ||||
| 0x08c8: { n:"PLV", f:parsenoop }, /* supposedly PLV for Excel 11 */ | ||||
| 0x08c8: { n:"PLV", f:parsenoop }, | ||||
| 0x08c9: { n:"LnExt", f:parsenoop }, | ||||
| 0x08ca: { n:"MkrExt", f:parsenoop }, | ||||
| 0x08cb: { n:"CrtCoopt", f:parsenoop }, | ||||
| @ -14126,6 +14451,7 @@ function parse_dom_table(table, opts) { | ||||
| 			var o = {t:'s', v:v}; | ||||
| 			if(v != null && v.length && !isNaN(Number(v))) o = {t:'n', v:Number(v)}; | ||||
| 			ws[encode_cell({c:C, r:R})] = o; | ||||
| 			if(range.e.c < C) range.e.c = C; | ||||
| 			C += CS; | ||||
| 		} | ||||
| 	} | ||||
| @ -15059,6 +15385,7 @@ function readSync(data, opts) { | ||||
| 		case 0x50: if(n[1] == 0x4B && n[2] < 0x20 && n[3] < 0x20) return read_zip(d, o); break; | ||||
| 		case 0xEF: return parse_xlml(d, o); | ||||
| 		case 0xFF: if(n[1] == 0xFE){ return read_utf16(d, o); } break; | ||||
| 		case 0x00: if(n[1] == 0x00 && n[2] >= 0x02 && n[3] == 0x00) return WK_.to_workbook(d, o); break; | ||||
| 		case 0x03: case 0x83: case 0x8B: return DBF.to_workbook(d, o); | ||||
| 	} | ||||
| 	if(n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user