forked from sheetjs/sheetjs
		
	Defined Names
- XLSX read/write defined names - XLSB/XLS/XLML read defined names Issues: - fixes #83 h/t @developergdd - fixes #6 , fixes #599
This commit is contained in:
		
							parent
							
								
									5187bc0b63
								
							
						
					
					
						commit
						0189bc23ca
					
				
							
								
								
									
										22
									
								
								README.md
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										22
									
								
								README.md
									
									
									
									
									
								
							| @ -45,6 +45,8 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6. | ||||
|     + [Chartsheet Object](#chartsheet-object) | ||||
|   * [Workbook Object](#workbook-object) | ||||
|     + [Workbook File Properties](#workbook-file-properties) | ||||
|   * [Workbook-Level Attributes](#workbook-level-attributes) | ||||
|     + [Defined Names](#defined-names) | ||||
|   * [Document Features](#document-features) | ||||
|     + [Formulae](#formulae) | ||||
|     + [Column Properties](#column-properties) | ||||
| @ -669,6 +671,26 @@ Writers will process the `Props` key of the options object: | ||||
| /* force the Author to be "SheetJS" */ | ||||
| XLSX.write(wb, {Props:{Author:"SheetJS"}}); | ||||
| ``` | ||||
| 
 | ||||
| ### Workbook-Level Attributes | ||||
| 
 | ||||
| `wb.Workbook` stores workbook level attributes. | ||||
| 
 | ||||
| #### Defined Names | ||||
| 
 | ||||
| `wb.Workbook.Names` is an array of defined name objects which have the keys: | ||||
| 
 | ||||
| | Key       | Description                                                      | | ||||
| |:----------|:-----------------------------------------------------------------| | ||||
| | `Sheet`   | Name scope.  Sheet Index (0 = first sheet) or `null` (Workbook)  | | ||||
| | `Name`    | Case-sensitive name.  Standard rules apply **                    | | ||||
| | `Ref`     | A1-style Reference (e.g. `"Sheet1!$A$1:$D$20"`)                  | | ||||
| | `Comment` | Comment (only applicable for XLS/XLSX/XLSB)                      | | ||||
| 
 | ||||
| Excel allows two sheet-scoped defined names to share the same name.  However, a | ||||
| sheet-scoped name cannot collide with a workbook-scope name.  Workbook writers | ||||
| may not enforce this constraint. | ||||
| 
 | ||||
| ### Document Features | ||||
| 
 | ||||
| Even for basic features like date storage, the official Excel formats store the | ||||
|  | ||||
| @ -39,7 +39,7 @@ function xlml_write_docprops(Props, opts) { | ||||
| 			case 'date': m = new Date(m).toISOString(); break; | ||||
| 		} | ||||
| 		if(typeof m == 'number') m = String(m); | ||||
| 		else if(m === true || m === false) { t = "boolean"; m = m ? "1" : "0"; } | ||||
| 		else if(m === true || m === false) { m = m ? "1" : "0"; } | ||||
| 		else if(m instanceof Date) m = new Date(m).toISOString(); | ||||
| 		o.push(writetag(XLMLDocPropsMap[p[1]] || p[1], m)); | ||||
| 	}); | ||||
|  | ||||
| @ -278,13 +278,6 @@ function parslurp(blob, length, cb) { | ||||
| 	return arr; | ||||
| } | ||||
| 
 | ||||
| function parslurp2(blob, length, cb) { | ||||
| 	var arr = [], target = blob.l + length, len = blob.read_shift(2); | ||||
| 	while(len-- !== 0) arr.push(cb(blob, target - blob.l)); | ||||
| 	if(target !== blob.l) throw new Error("Slurp error"); | ||||
| 	return arr; | ||||
| } | ||||
| 
 | ||||
| function parsebool(blob, length) { return blob.read_shift(length) === 0x1; } | ||||
| 
 | ||||
| function parseuint16(blob) { return blob.read_shift(2, 'u'); } | ||||
|  | ||||
| @ -442,6 +442,7 @@ function parse_ExternName(blob, length, opts) { | ||||
| 	if(opts.sbcch === 0x3A01) body = parse_AddinUdf(blob, length-2, opts); | ||||
| 	//else throw new Error("unsupported SupBook cch: " + opts.sbcch);
 | ||||
| 	o.body = body || blob.read_shift(length-2); | ||||
| 	if(typeof body === "string") o.Name = body; | ||||
| 	return o; | ||||
| } | ||||
| 
 | ||||
| @ -472,13 +473,21 @@ function parse_Lbl(blob, length, opts) { | ||||
| /* 2.4.106 TODO: verify supbook manipulation */ | ||||
| function parse_ExternSheet(blob, length, opts) { | ||||
| 	if(opts.biff < 8) return parse_ShortXLUnicodeString(blob, length, opts); | ||||
| 	var o = parslurp2(blob,length,parse_XTI); | ||||
| 	var o = [], target = blob.l + length, len = blob.read_shift(2); | ||||
| 	while(len-- !== 0) o.push(parse_XTI(blob, 6)); | ||||
| 		// [iSupBook, itabFirst, itabLast];
 | ||||
| 	var oo = []; | ||||
| 	if(opts.sbcch === 0x0401) { | ||||
| 		for(var i = 0; i != o.length; ++i) oo.push(opts.snames[o[i][1]]); | ||||
| 		return oo; | ||||
| 	} | ||||
| 	else return o; | ||||
| 	return o; | ||||
| } | ||||
| 
 | ||||
| /* 2.4.176 TODO: check older biff */ | ||||
| function parse_NameCmt(blob, length, opts) { | ||||
| 	if(opts.biff < 8) { blob.l += length; return; } | ||||
| 	var cchName = blob.read_shift(2); | ||||
| 	var cchComment = blob.read_shift(2); | ||||
| 	var name = parse_XLUnicodeStringNoCch(blob, cchName, opts); | ||||
| 	var comment = parse_XLUnicodeStringNoCch(blob, cchComment, opts); | ||||
| 	return [name, comment]; | ||||
| } | ||||
| 
 | ||||
| /* 2.4.260 */ | ||||
| @ -894,7 +903,6 @@ var parse_TableStyles = parsenoop; | ||||
| var parse_TableStyle = parsenoop; | ||||
| var parse_TableStyleElement = parsenoop; | ||||
| var parse_NamePublish = parsenoop; | ||||
| var parse_NameCmt = parsenoop; | ||||
| var parse_SortData = parsenoop; | ||||
| var parse_GUIDTypeLib = parsenoop; | ||||
| var parse_FnGrp12 = parsenoop; | ||||
|  | ||||
| @ -769,13 +769,13 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks, | ||||
| 				break; | ||||
| 			/* 2.5.198.88 */ | ||||
| 			case 'PtgRefN': | ||||
| 				type = f[1][0]; c = shift_cell_xls(f[1][1], cell, opts); | ||||
| 				type = f[1][0]; c = cell ? shift_cell_xls(f[1][1], cell, opts) : f[1][1]; | ||||
| 				stack.push(encode_cell_xls(c)); | ||||
| 				break; | ||||
| 			case 'PtgRef3d': // TODO: lots of stuff
 | ||||
| 				type = f[1][0]; ixti = /*::Number(*/f[1][1]/*::)*/; c = shift_cell_xls(f[1][2], _range, opts); | ||||
| 				sname = (supbooks && supbooks[1] ? supbooks[1][ixti+1] : "**MISSING**"); | ||||
| 				stack.push(sname + "!" + encode_cell(c)); | ||||
| 				sname = supbooks.SheetNames[ixti]; | ||||
| 				stack.push(sname + "!" + encode_cell_xls(c)); | ||||
| 				break; | ||||
| 
 | ||||
| 			/* 2.5.198.62 */ | ||||
| @ -831,7 +831,7 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks, | ||||
| 			case 'PtgName': | ||||
| 				/* f[1] = type, 0, nameindex */ | ||||
| 				nameidx = f[1][2]; | ||||
| 				var lbl = supbooks[0][nameidx]; | ||||
| 				var lbl = (supbooks.names||[])[nameidx-1] || (supbooks[0]||[])[nameidx]; | ||||
| 				var name = lbl ? lbl.Name : "**MISSING**" + String(nameidx); | ||||
| 				if(name in XLSXFutureFunctions) name = XLSXFutureFunctions[name]; | ||||
| 				stack.push(name); | ||||
| @ -843,15 +843,27 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks, | ||||
| 				var bookidx/*:number*/ = (f[1][1]/*:any*/); nameidx = f[1][2]; var externbook; | ||||
| 				/* TODO: Properly handle missing values */ | ||||
| 				//console.log(bookidx, supbooks);
 | ||||
| 				if(opts.biff == 5) { | ||||
| 				if(opts.biff <= 5) { | ||||
| 					if(bookidx < 0) bookidx = -bookidx; | ||||
| 					if(supbooks[bookidx]) externbook = supbooks[bookidx][nameidx]; | ||||
| 				} else { | ||||
| 					if(supbooks[bookidx+1]) externbook = supbooks[bookidx+1][nameidx]; | ||||
| 					else if(supbooks[bookidx-1]) externbook = supbooks[bookidx-1][nameidx]; | ||||
| 					var pnxname = supbooks.SheetNames[bookidx]; | ||||
| 					var o = ""; | ||||
| 					if(((supbooks[bookidx]||[])[0]||[])[0] == 0x3A01){} | ||||
| 					else if(((supbooks[bookidx]||[])[0]||[])[0] == 0x0401){ | ||||
| 						if(supbooks[bookidx][nameidx] && supbooks[bookidx][nameidx].itab > 0) { | ||||
| 							o = supbooks.SheetNames[supbooks[bookidx][nameidx].itab-1] + "!"; | ||||
| 						} | ||||
| 					} | ||||
| 					else o = supbooks.SheetNames[nameidx-1]+ "!"; | ||||
| 					if(supbooks[bookidx] && supbooks[bookidx][nameidx]) o += supbooks[bookidx][nameidx].Name; | ||||
| 					else if(supbooks[0] && supbooks[0][nameidx]) o += supbooks[0][nameidx].Name; | ||||
| 					else o += "??NAMEX??"; | ||||
| 					stack.push(o); | ||||
| 					break; | ||||
| 				} | ||||
| 				if(!externbook) externbook = {body: "??NAMEX??"}; | ||||
| 				stack.push(externbook.body); | ||||
| 				if(!externbook) externbook = {Name: "??NAMEX??"}; | ||||
| 				stack.push(externbook.Name); | ||||
| 				break; | ||||
| 
 | ||||
| 			/* 2.5.198.80 */ | ||||
| @ -937,6 +949,9 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks, | ||||
| 			/* 2.5.198.29 */ | ||||
| 			case 'PtgAreaErr': stack.push("#REF!"); break; | ||||
| 
 | ||||
| 			/* 2.5.198.30 */ | ||||
| 			case 'PtgAreaErr3d': stack.push("#REF!"); break; | ||||
| 
 | ||||
| 			/* 2.5.198.72 TODO */ | ||||
| 			case 'PtgMemFunc': break; | ||||
| 
 | ||||
|  | ||||
| @ -342,9 +342,9 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ { | ||||
| 	var supbooks = ([[]]/*:any*/); | ||||
| 	supbooks.sharedf = shared_formulae; | ||||
| 	supbooks.arrayf = array_formulae; | ||||
| 	supbooks.SheetNames = wb.SheetNames || wb.Sheets.map(function(x) { return x.name; }); | ||||
| 	opts.supbooks = supbooks; | ||||
| 
 | ||||
| 	for(var i = 0; i < wb.Names['!names'].length; ++i) supbooks[0][i+1] = wb.Names[wb.Names['!names'][i]]; | ||||
| 	for(var i = 0; i < wb.Names.length; ++i) supbooks[0][i+1] = wb.Names[i]; | ||||
| 
 | ||||
| 	var colinfo = [], rowinfo = []; | ||||
| 	var defwidth = 0, defheight = 0; // twips / MDW respectively
 | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| var wbnsregex = /<\w+:workbook/; | ||||
| function parse_wb_xml(data, opts)/*:WorkbookFile*/ { | ||||
| 	if(!data) throw new Error("Could not find file"); | ||||
| 	var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, Names:{'!names':[]}, xmlns: "" }; | ||||
| 	var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, Names:[], xmlns: "" }; | ||||
| 	var pass = false, xmlns = "xmlns"; | ||||
| 	var dname = {}, dnstart = 0; | ||||
| 	/*(data.match(tagregex)||[]).forEach */ | ||||
| @ -73,12 +73,12 @@ function parse_wb_xml(data, opts)/*:WorkbookFile*/ { | ||||
| 				dname = {}; | ||||
| 				dname.Name = y.name; | ||||
| 				if(y.comment) dname.Comment = y.comment; | ||||
| 				if(y.localSheetId) dname.Sheet = +y.localSheetId; | ||||
| 				dnstart = idx + x.length; | ||||
| 			}	break; | ||||
| 			case '</definedName>': { | ||||
| 				dname.Ref = data.slice(dnstart, idx); | ||||
| 				wb.Names[dname.Name] = dname; | ||||
| 				wb.Names['!names'].push(dname.Name); | ||||
| 				wb.Names.push(dname); | ||||
| 			} break; | ||||
| 			case '<definedName/>': break; | ||||
| 
 | ||||
| @ -184,7 +184,19 @@ function write_wb_xml(wb/*:Workbook*/, opts/*:?WriteOpts*/)/*:string*/ { | ||||
| 
 | ||||
| 	/* functionGroups */ | ||||
| 	/* externalReferences */ | ||||
| 	/* definedNames */ | ||||
| 
 | ||||
| 	if(wb.Workbook && (wb.Workbook.Names||[]).length > 0) { | ||||
| 		o[o.length] = "<definedNames>"; | ||||
| 		wb.Workbook.Names.forEach(function(n) { | ||||
| 			var d = {name:n.Name}; | ||||
| 			if(n.Comment) d.comment = n.Comment; | ||||
| 			if(n.Sheet != null) d.localSheetId = ""+n.Sheet; | ||||
| 			if(!n.Ref) return; | ||||
| 			o[o.length] = writextag('definedName', String(n.Ref), d); | ||||
| 		}); | ||||
| 		o[o.length] = "</definedNames>"; | ||||
| 	} | ||||
| 
 | ||||
| 	/* calcPr */ | ||||
| 	/* oleSize */ | ||||
| 	/* customWorkbookViews */ | ||||
|  | ||||
| @ -55,7 +55,9 @@ function parse_BrtName(data, length, opts) { | ||||
| 		// unusedstring2: XLNullableWideString
 | ||||
| 	//}
 | ||||
| 	data.l = end; | ||||
| 	return {Name:name, Ptg:formula, Comment:comment}; | ||||
| 	var out = ({Name:name, Ptg:formula, Comment:comment}/*:any*/); | ||||
| 	if(itab < 0xFFFFFFF) out.Sheet = itab; | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| /* [MS-XLSB] 2.1.7.60 Workbook */ | ||||
| @ -66,15 +68,20 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ { | ||||
| 	if(!opts) opts = {}; | ||||
| 	opts.biff = 12; | ||||
| 
 | ||||
| 	var Names = {}, NameList = []; | ||||
| 	var Names = []; | ||||
| 	var supbooks = []; | ||||
| 	supbooks.SheetNames = []; | ||||
| 
 | ||||
| 	recordhopper(data, function hopper_wb(val, R_n, RT) { | ||||
| 		switch(RT) { | ||||
| 			case 0x009C: /* 'BrtBundleSh' */ | ||||
| 				supbooks.SheetNames.push(val.name); | ||||
| 				wb.Sheets.push(val); break; | ||||
| 
 | ||||
| 			case 0x0027: /* 'BrtName' */ | ||||
| 				Names[val.Name] = val; NameList.push(val.Name); | ||||
| 				val.Ref = stringify_formula(val.Ptg, null, null, supbooks, opts); | ||||
| 				delete val.Ptg; | ||||
| 				Names.push(val); | ||||
| 				break; | ||||
| 			case 0x040C: /* 'BrtNameExt' */ break; | ||||
| 
 | ||||
| @ -133,7 +140,6 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ { | ||||
| 
 | ||||
| 	parse_wb_defaults(wb); | ||||
| 
 | ||||
| 	Names['!names'] = NameList; | ||||
| 	// $FlowIgnore
 | ||||
| 	wb.Names = Names; | ||||
| 
 | ||||
|  | ||||
| @ -312,7 +312,17 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ { | ||||
| 			for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = dup(csty); | ||||
| 			break; | ||||
| 
 | ||||
| 		case 'NamedRange': break; | ||||
| 		case 'NamedRange': | ||||
| 			if(!Workbook.Names) Workbook.Names = []; | ||||
| 			var _NamedRange = parsexmltag(Rn[0]); | ||||
| 			var _DefinedName = { | ||||
| 				Name: _NamedRange.Name, | ||||
| 				Ref: rc_to_a1(_NamedRange.RefersTo.substr(1)) | ||||
| 			}; | ||||
| 			if(Workbook.Sheets.length>0) _DefinedName.Sheet=Workbook.Sheets.length-1; | ||||
| 			Workbook.Names.push(_DefinedName); | ||||
| 			break; | ||||
| 
 | ||||
| 		case 'NamedCell': break; | ||||
| 		case 'B': break; | ||||
| 		case 'I': break; | ||||
| @ -796,7 +806,7 @@ function write_props_xlml(wb, opts) { | ||||
| 	/* DocumentProperties */ | ||||
| 	if(wb.Props) o.push(xlml_write_docprops(wb.Props, opts)); | ||||
| 	/* CustomDocumentProperties */ | ||||
| 	if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops)); | ||||
| 	if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops, opts)); | ||||
| 	return o.join(""); | ||||
| } | ||||
| /* TODO */ | ||||
|  | ||||
| @ -169,15 +169,17 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ { | ||||
| 	var colinfo = [], rowinfo = []; | ||||
| 	var defwidth = 0, defheight = 0; // twips / MDW respectively
 | ||||
| 	var seencol = false; | ||||
| 	var supbooks = ([[]]/*:any*/); // 1-indexed, will hold extern names
 | ||||
| 	var sbc = 0, sbci = 0, sbcli = 0; | ||||
| 	var supbooks = ([]/*:any*/); // 1-indexed, will hold extern names
 | ||||
| 	supbooks.SheetNames = opts.snames; | ||||
| 	supbooks.sharedf = opts.sharedf; | ||||
| 	supbooks.arrayf = opts.arrayf; | ||||
| 	supbooks.names = []; | ||||
| 	supbooks.XTI = []; | ||||
| 	var last_Rn = ''; | ||||
| 	var file_depth = 0; /* TODO: make a real stack */ | ||||
| 	var BIFF2Fmt = 0; | ||||
| 	var FilterDatabases = []; /* TODO: sort out supbooks and process elsewhere */ | ||||
| 	var last_lbl; | ||||
| 
 | ||||
| 	/* explicit override for some broken writers */ | ||||
| 	opts.codepage = 1200; | ||||
| @ -250,18 +252,35 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ { | ||||
| 				case 'RichTextStream': break; | ||||
| 				case 'BkHim': break; | ||||
| 
 | ||||
| 				case 'SupBook': supbooks[++sbc] = [val]; sbci = 0; break; | ||||
| 				case 'ExternName': supbooks[sbc][++sbci] = val; break; | ||||
| 				case 'SupBook': | ||||
| 					supbooks.push([val]); | ||||
| 					supbooks[supbooks.length-1].XTI = []; | ||||
| 					break; | ||||
| 				case 'ExternName': | ||||
| 					supbooks[supbooks.length-1].push(val); | ||||
| 					break; | ||||
| 				case 'Index': break; // TODO
 | ||||
| 				case 'Lbl': | ||||
| 					supbooks[0][++sbcli] = val; // TODO: local formula storage in stringify_formula
 | ||||
| 					if(!supbooks[val.itab]) supbooks[val.itab] = []; | ||||
| 					supbooks[val.itab].push(val); | ||||
| 					last_lbl = { | ||||
| 						Name: val.Name, | ||||
| 						Ref: stringify_formula(val.rgce,range,null,supbooks,opts) | ||||
| 					}; | ||||
| 					if(val.itab > 0) last_lbl.Sheet = val.itab - 1; | ||||
| 					supbooks.names.push(last_lbl); | ||||
| 					if(!supbooks[0]) supbooks[0] = []; | ||||
| 					supbooks[supbooks.length-1].push(val); | ||||
| 					if(val.Name == "\r" && val.itab > 0) | ||||
| 						if(val.rgce && val.rgce[0] && val.rgce[0][0] && val.rgce[0][0][0] == 'PtgArea3d') | ||||
| 							FilterDatabases[val.itab - 1] = { ref: encode_range(val.rgce[0][0][1][2]) }; | ||||
| 					break; | ||||
| 				case 'ExternSheet': supbooks[sbc] = supbooks[sbc].concat(val); sbci += val.length; break; | ||||
| 				case 'ExternSheet': | ||||
| 					if(supbooks.length == 0) { supbooks[0] = []; supbooks[0].XTI = []; } | ||||
| 					supbooks[supbooks.length - 1].XTI = supbooks[supbooks.length - 1].XTI.concat(val); supbooks.XTI = supbooks.XTI.concat(val); break; | ||||
| 				case 'NameCmt': | ||||
| 					/* TODO: search for correct name */ | ||||
| 					if(opts.biff < 8) break; | ||||
| 					last_lbl.Comment = val[1]; | ||||
| 					break; | ||||
| 
 | ||||
| 				case 'Protect': out["!protect"] = val; break; /* for sheet or book */ | ||||
| 				case 'Password': if(val !== 0 && opts.WTF) console.error("Password verifier: " + val); break; | ||||
| @ -484,7 +503,6 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ { | ||||
| 				} break; | ||||
| 				case 'Row': break; // TODO
 | ||||
| 
 | ||||
| 				case 'NameCmt': break; | ||||
| 				case 'Header': break; // TODO
 | ||||
| 				case 'Footer': break; // TODO
 | ||||
| 				case 'HCenter': break; // TODO
 | ||||
| @ -727,6 +745,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ { | ||||
| 	if(opts.enc) wb.Encryption = opts.enc; | ||||
| 	wb.Metadata = {}; | ||||
| 	if(country !== undefined) wb.Metadata.Country = country; | ||||
| 	if(supbooks.names.length > 0) Workbook.Names = supbooks.names; | ||||
| 	wb.Workbook = Workbook; | ||||
| 	return wb; | ||||
| } | ||||
|  | ||||
| @ -1103,7 +1103,7 @@ var XLSRecordEnum = { | ||||
| 	/*::[*/0x0890/*::]*/: { n:"TableStyleElement", f:parse_TableStyleElement }, | ||||
| 	/*::[*/0x0892/*::]*/: { n:"StyleExt", f:parse_StyleExt }, | ||||
| 	/*::[*/0x0893/*::]*/: { n:"NamePublish", f:parse_NamePublish }, | ||||
| 	/*::[*/0x0894/*::]*/: { n:"NameCmt", f:parse_NameCmt }, | ||||
| 	/*::[*/0x0894/*::]*/: { n:"NameCmt", f:parse_NameCmt, r:12 }, | ||||
| 	/*::[*/0x0895/*::]*/: { n:"SortData", f:parse_SortData }, | ||||
| 	/*::[*/0x0896/*::]*/: { n:"Theme", f:parse_Theme, r:12 }, | ||||
| 	/*::[*/0x0897/*::]*/: { n:"GUIDTypeLib", f:parse_GUIDTypeLib }, | ||||
|  | ||||
| @ -36,3 +36,4 @@ Writers will process the `Props` key of the options object: | ||||
| /* force the Author to be "SheetJS" */ | ||||
| XLSX.write(wb, {Props:{Author:"SheetJS"}}); | ||||
| ``` | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										19
									
								
								docbits/57_wbbook.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										19
									
								
								docbits/57_wbbook.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| ### Workbook-Level Attributes | ||||
| 
 | ||||
| `wb.Workbook` stores workbook level attributes. | ||||
| 
 | ||||
| #### Defined Names | ||||
| 
 | ||||
| `wb.Workbook.Names` is an array of defined name objects which have the keys: | ||||
| 
 | ||||
| | Key       | Description                                                      | | ||||
| |:----------|:-----------------------------------------------------------------| | ||||
| | `Sheet`   | Name scope.  Sheet Index (0 = first sheet) or `null` (Workbook)  | | ||||
| | `Name`    | Case-sensitive name.  Standard rules apply **                    | | ||||
| | `Ref`     | A1-style Reference (e.g. `"Sheet1!$A$1:$D$20"`)                  | | ||||
| | `Comment` | Comment (only applicable for XLS/XLSX/XLSB)                      | | ||||
| 
 | ||||
| Excel allows two sheet-scoped defined names to share the same name.  However, a | ||||
| sheet-scoped name cannot collide with a workbook-scope name.  Workbook writers | ||||
| may not enforce this constraint. | ||||
| 
 | ||||
| @ -22,6 +22,8 @@ | ||||
|     + [Chartsheet Object](README.md#chartsheet-object) | ||||
|   * [Workbook Object](README.md#workbook-object) | ||||
|     + [Workbook File Properties](README.md#workbook-file-properties) | ||||
|   * [Workbook-Level Attributes](README.md#workbook-level-attributes) | ||||
|     + [Defined Names](README.md#defined-names) | ||||
|   * [Document Features](README.md#document-features) | ||||
|     + [Formulae](README.md#formulae) | ||||
|     + [Column Properties](README.md#column-properties) | ||||
|  | ||||
| @ -1,7 +1,10 @@ | ||||
| # This file controls the multiformat tests | ||||
| # vim: set ts=4: | ||||
| # Format: <basename> <ext> <ext> [ext..] | ||||
| # yes-formula | ||||
| AutoFilter              	.xls .xlsb .xlsx .xml | ||||
| #BlankSheetTypes         	.xls .xlsb .xlsx .xml | ||||
| # note: XLML only supports sheets, ods does not support dialog | ||||
| BlankSheetTypes         	.xls .xlsb .xlsm | ||||
| NumberFormatCondition   	.xls .xlsb .xlsm .xml | ||||
| RkNumber                	.xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml | ||||
| #calendar_stress_test    	.xls .xlsb .xlsx .xml | ||||
| @ -10,17 +13,15 @@ cell_style_simple       	.xls .xlsb .xlsx .xml | ||||
| comments_stress_test    	.xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml | ||||
| # yes-csv | ||||
| custom_properties       	.xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml | ||||
| # no-formula (defined names) | ||||
| defined_names_simple    	.xls .xlsb .xlsx .xml | ||||
| # yes-formula | ||||
| # no-csv (randbetween) note: ODS does not support many XLSX functions  | ||||
| formula_stress_test     	.xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml | ||||
| formula_stress_test     	.xls .xlsb .xlsx .xlsb.xml | ||||
| # yes-csv | ||||
| formulae_test_simple    	.xls .xlsb .xlsx .xml | ||||
| hyperlink_stress_test_2011	.xls .xlsb .xlsx .xml | ||||
| #large_strings           	.xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml | ||||
| merge_cells             	.xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml | ||||
| # no-formula (defined names) | ||||
| # no-formula (filename-references in XLSX encoding as [0]) | ||||
| named_ranges_2011       	.xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml | ||||
| # yes-formula | ||||
| # no-csv (macro serialization in xml) | ||||
|  | ||||
							
								
								
									
										27
									
								
								test.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										27
									
								
								test.js
									
									
									
									
									
								
							| @ -50,6 +50,10 @@ var paths = { | ||||
| 	cstxlsx: dir + 'comments_stress_test.xlsx', | ||||
| 	cstxlsb: dir + 'comments_stress_test.xlsb', | ||||
| 	cstods: dir + 'comments_stress_test.ods', | ||||
| 	dnsxls: dir + 'defined_names_simple.xls', | ||||
| 	dnsxml: dir + 'defined_names_simple.xml', | ||||
| 	dnsxlsx: dir + 'defined_names_simple.xlsx', | ||||
| 	dnsxlsb: dir + 'defined_names_simple.xlsb', | ||||
| 	fstxls: dir + 'formula_stress_test.xls', | ||||
| 	fstxml: dir + 'formula_stress_test.xls.xml', | ||||
| 	fstxlsx: dir + 'formula_stress_test.xlsx', | ||||
| @ -893,6 +897,29 @@ describe('parse features', function() { | ||||
| 		}); }); | ||||
| 	}); | ||||
| 
 | ||||
| 	describe('defined names', function() { | ||||
| 		[ | ||||
| 			/* desc     path        cmnt */ | ||||
| 			['xlsx', paths.dnsxlsx,  true], | ||||
| 			['xlsb', paths.dnsxlsb,  true], | ||||
| 			['xls',  paths.dnsxls,   true], | ||||
| 			['xlml', paths.dnsxml,  false], | ||||
| 		].forEach(function(m) { it(m[0], function() { | ||||
| 			var wb = X.readFile(m[1]); | ||||
| 			var names = wb.Workbook.Names; | ||||
| 			for(var i = 0; i < names.length; ++i) if(names[i].Name == "SheetJS") break; | ||||
| 			assert(i < names.length, "Missing name"); | ||||
| 			assert.equal(names[i].Sheet, null); | ||||
| 			assert.equal(names[i].Ref, "Sheet1!$A$1"); | ||||
| 			if(m[2]) assert.equal(names[i].Comment, "defined names just suck  excel formulae are bad  MS should feel bad"); | ||||
| 
 | ||||
| 			for(i = 0; i < names.length; ++i) if(names[i].Name == "SHEETjs") break; | ||||
| 			assert(i < names.length, "Missing name"); | ||||
| 			assert.equal(names[i].Sheet, 0); | ||||
| 			assert.equal(names[i].Ref, "Sheet1!$A$2"); | ||||
| 		}); }); | ||||
| 	}); | ||||
| 
 | ||||
| 	describe('auto filter', function() { | ||||
| 		[ | ||||
| 			['xlsx', paths.afxlsx], | ||||
|  | ||||
							
								
								
									
										155
									
								
								xlsx.flow.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										155
									
								
								xlsx.flow.js
									
									
									
									
									
								
							| @ -3273,7 +3273,7 @@ function xlml_write_docprops(Props, opts) { | ||||
| 			case 'date': m = new Date(m).toISOString(); break; | ||||
| 		} | ||||
| 		if(typeof m == 'number') m = String(m); | ||||
| 		else if(m === true || m === false) { t = "boolean"; m = m ? "1" : "0"; } | ||||
| 		else if(m === true || m === false) { m = m ? "1" : "0"; } | ||||
| 		else if(m instanceof Date) m = new Date(m).toISOString(); | ||||
| 		o.push(writetag(XLMLDocPropsMap[p[1]] || p[1], m)); | ||||
| 	}); | ||||
| @ -3590,13 +3590,6 @@ function parslurp(blob, length, cb) { | ||||
| 	return arr; | ||||
| } | ||||
| 
 | ||||
| function parslurp2(blob, length, cb) { | ||||
| 	var arr = [], target = blob.l + length, len = blob.read_shift(2); | ||||
| 	while(len-- !== 0) arr.push(cb(blob, target - blob.l)); | ||||
| 	if(target !== blob.l) throw new Error("Slurp error"); | ||||
| 	return arr; | ||||
| } | ||||
| 
 | ||||
| function parsebool(blob, length) { return blob.read_shift(length) === 0x1; } | ||||
| 
 | ||||
| function parseuint16(blob) { return blob.read_shift(2, 'u'); } | ||||
| @ -4200,6 +4193,7 @@ function parse_ExternName(blob, length, opts) { | ||||
| 	if(opts.sbcch === 0x3A01) body = parse_AddinUdf(blob, length-2, opts); | ||||
| 	//else throw new Error("unsupported SupBook cch: " + opts.sbcch);
 | ||||
| 	o.body = body || blob.read_shift(length-2); | ||||
| 	if(typeof body === "string") o.Name = body; | ||||
| 	return o; | ||||
| } | ||||
| 
 | ||||
| @ -4230,13 +4224,21 @@ function parse_Lbl(blob, length, opts) { | ||||
| /* 2.4.106 TODO: verify supbook manipulation */ | ||||
| function parse_ExternSheet(blob, length, opts) { | ||||
| 	if(opts.biff < 8) return parse_ShortXLUnicodeString(blob, length, opts); | ||||
| 	var o = parslurp2(blob,length,parse_XTI); | ||||
| 	var o = [], target = blob.l + length, len = blob.read_shift(2); | ||||
| 	while(len-- !== 0) o.push(parse_XTI(blob, 6)); | ||||
| 		// [iSupBook, itabFirst, itabLast];
 | ||||
| 	var oo = []; | ||||
| 	if(opts.sbcch === 0x0401) { | ||||
| 		for(var i = 0; i != o.length; ++i) oo.push(opts.snames[o[i][1]]); | ||||
| 		return oo; | ||||
| 	} | ||||
| 	else return o; | ||||
| 	return o; | ||||
| } | ||||
| 
 | ||||
| /* 2.4.176 TODO: check older biff */ | ||||
| function parse_NameCmt(blob, length, opts) { | ||||
| 	if(opts.biff < 8) { blob.l += length; return; } | ||||
| 	var cchName = blob.read_shift(2); | ||||
| 	var cchComment = blob.read_shift(2); | ||||
| 	var name = parse_XLUnicodeStringNoCch(blob, cchName, opts); | ||||
| 	var comment = parse_XLUnicodeStringNoCch(blob, cchComment, opts); | ||||
| 	return [name, comment]; | ||||
| } | ||||
| 
 | ||||
| /* 2.4.260 */ | ||||
| @ -4652,7 +4654,6 @@ var parse_TableStyles = parsenoop; | ||||
| var parse_TableStyle = parsenoop; | ||||
| var parse_TableStyleElement = parsenoop; | ||||
| var parse_NamePublish = parsenoop; | ||||
| var parse_NameCmt = parsenoop; | ||||
| var parse_SortData = parsenoop; | ||||
| var parse_GUIDTypeLib = parsenoop; | ||||
| var parse_FnGrp12 = parsenoop; | ||||
| @ -8130,13 +8131,13 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks, | ||||
| 				break; | ||||
| 			/* 2.5.198.88 */ | ||||
| 			case 'PtgRefN': | ||||
| 				type = f[1][0]; c = shift_cell_xls(f[1][1], cell, opts); | ||||
| 				type = f[1][0]; c = cell ? shift_cell_xls(f[1][1], cell, opts) : f[1][1]; | ||||
| 				stack.push(encode_cell_xls(c)); | ||||
| 				break; | ||||
| 			case 'PtgRef3d': // TODO: lots of stuff
 | ||||
| 				type = f[1][0]; ixti = /*::Number(*/f[1][1]/*::)*/; c = shift_cell_xls(f[1][2], _range, opts); | ||||
| 				sname = (supbooks && supbooks[1] ? supbooks[1][ixti+1] : "**MISSING**"); | ||||
| 				stack.push(sname + "!" + encode_cell(c)); | ||||
| 				sname = supbooks.SheetNames[ixti]; | ||||
| 				stack.push(sname + "!" + encode_cell_xls(c)); | ||||
| 				break; | ||||
| 
 | ||||
| 			/* 2.5.198.62 */ | ||||
| @ -8192,7 +8193,7 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks, | ||||
| 			case 'PtgName': | ||||
| 				/* f[1] = type, 0, nameindex */ | ||||
| 				nameidx = f[1][2]; | ||||
| 				var lbl = supbooks[0][nameidx]; | ||||
| 				var lbl = (supbooks.names||[])[nameidx-1] || (supbooks[0]||[])[nameidx]; | ||||
| 				var name = lbl ? lbl.Name : "**MISSING**" + String(nameidx); | ||||
| 				if(name in XLSXFutureFunctions) name = XLSXFutureFunctions[name]; | ||||
| 				stack.push(name); | ||||
| @ -8204,15 +8205,27 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks, | ||||
| 				var bookidx/*:number*/ = (f[1][1]/*:any*/); nameidx = f[1][2]; var externbook; | ||||
| 				/* TODO: Properly handle missing values */ | ||||
| 				//console.log(bookidx, supbooks);
 | ||||
| 				if(opts.biff == 5) { | ||||
| 				if(opts.biff <= 5) { | ||||
| 					if(bookidx < 0) bookidx = -bookidx; | ||||
| 					if(supbooks[bookidx]) externbook = supbooks[bookidx][nameidx]; | ||||
| 				} else { | ||||
| 					if(supbooks[bookidx+1]) externbook = supbooks[bookidx+1][nameidx]; | ||||
| 					else if(supbooks[bookidx-1]) externbook = supbooks[bookidx-1][nameidx]; | ||||
| 					var pnxname = supbooks.SheetNames[bookidx]; | ||||
| 					var o = ""; | ||||
| 					if(((supbooks[bookidx]||[])[0]||[])[0] == 0x3A01){} | ||||
| 					else if(((supbooks[bookidx]||[])[0]||[])[0] == 0x0401){ | ||||
| 						if(supbooks[bookidx][nameidx] && supbooks[bookidx][nameidx].itab > 0) { | ||||
| 							o = supbooks.SheetNames[supbooks[bookidx][nameidx].itab-1] + "!"; | ||||
| 						} | ||||
| 					} | ||||
| 					else o = supbooks.SheetNames[nameidx-1]+ "!"; | ||||
| 					if(supbooks[bookidx] && supbooks[bookidx][nameidx]) o += supbooks[bookidx][nameidx].Name; | ||||
| 					else if(supbooks[0] && supbooks[0][nameidx]) o += supbooks[0][nameidx].Name; | ||||
| 					else o += "??NAMEX??"; | ||||
| 					stack.push(o); | ||||
| 					break; | ||||
| 				} | ||||
| 				if(!externbook) externbook = {body: "??NAMEX??"}; | ||||
| 				stack.push(externbook.body); | ||||
| 				if(!externbook) externbook = {Name: "??NAMEX??"}; | ||||
| 				stack.push(externbook.Name); | ||||
| 				break; | ||||
| 
 | ||||
| 			/* 2.5.198.80 */ | ||||
| @ -8298,6 +8311,9 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks, | ||||
| 			/* 2.5.198.29 */ | ||||
| 			case 'PtgAreaErr': stack.push("#REF!"); break; | ||||
| 
 | ||||
| 			/* 2.5.198.30 */ | ||||
| 			case 'PtgAreaErr3d': stack.push("#REF!"); break; | ||||
| 
 | ||||
| 			/* 2.5.198.72 TODO */ | ||||
| 			case 'PtgMemFunc': break; | ||||
| 
 | ||||
| @ -10528,9 +10544,9 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ { | ||||
| 	var supbooks = ([[]]/*:any*/); | ||||
| 	supbooks.sharedf = shared_formulae; | ||||
| 	supbooks.arrayf = array_formulae; | ||||
| 	supbooks.SheetNames = wb.SheetNames || wb.Sheets.map(function(x) { return x.name; }); | ||||
| 	opts.supbooks = supbooks; | ||||
| 
 | ||||
| 	for(var i = 0; i < wb.Names['!names'].length; ++i) supbooks[0][i+1] = wb.Names[wb.Names['!names'][i]]; | ||||
| 	for(var i = 0; i < wb.Names.length; ++i) supbooks[0][i+1] = wb.Names[i]; | ||||
| 
 | ||||
| 	var colinfo = [], rowinfo = []; | ||||
| 	var defwidth = 0, defheight = 0; // twips / MDW respectively
 | ||||
| @ -11161,7 +11177,7 @@ function check_wb(wb) { | ||||
| var wbnsregex = /<\w+:workbook/; | ||||
| function parse_wb_xml(data, opts)/*:WorkbookFile*/ { | ||||
| 	if(!data) throw new Error("Could not find file"); | ||||
| 	var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, Names:{'!names':[]}, xmlns: "" }; | ||||
| 	var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, Names:[], xmlns: "" }; | ||||
| 	var pass = false, xmlns = "xmlns"; | ||||
| 	var dname = {}, dnstart = 0; | ||||
| 	/*(data.match(tagregex)||[]).forEach */ | ||||
| @ -11232,12 +11248,12 @@ function parse_wb_xml(data, opts)/*:WorkbookFile*/ { | ||||
| 				dname = {}; | ||||
| 				dname.Name = y.name; | ||||
| 				if(y.comment) dname.Comment = y.comment; | ||||
| 				if(y.localSheetId) dname.Sheet = +y.localSheetId; | ||||
| 				dnstart = idx + x.length; | ||||
| 			}	break; | ||||
| 			case '</definedName>': { | ||||
| 				dname.Ref = data.slice(dnstart, idx); | ||||
| 				wb.Names[dname.Name] = dname; | ||||
| 				wb.Names['!names'].push(dname.Name); | ||||
| 				wb.Names.push(dname); | ||||
| 			} break; | ||||
| 			case '<definedName/>': break; | ||||
| 
 | ||||
| @ -11343,7 +11359,19 @@ function write_wb_xml(wb/*:Workbook*/, opts/*:?WriteOpts*/)/*:string*/ { | ||||
| 
 | ||||
| 	/* functionGroups */ | ||||
| 	/* externalReferences */ | ||||
| 	/* definedNames */ | ||||
| 
 | ||||
| 	if(wb.Workbook && (wb.Workbook.Names||[]).length > 0) { | ||||
| 		o[o.length] = "<definedNames>"; | ||||
| 		wb.Workbook.Names.forEach(function(n) { | ||||
| 			var d = {name:n.Name}; | ||||
| 			if(n.Comment) d.comment = n.Comment; | ||||
| 			if(n.Sheet != null) d.localSheetId = ""+n.Sheet; | ||||
| 			if(!n.Ref) return; | ||||
| 			o[o.length] = writextag('definedName', String(n.Ref), d); | ||||
| 		}); | ||||
| 		o[o.length] = "</definedNames>"; | ||||
| 	} | ||||
| 
 | ||||
| 	/* calcPr */ | ||||
| 	/* oleSize */ | ||||
| 	/* customWorkbookViews */ | ||||
| @ -11415,7 +11443,9 @@ function parse_BrtName(data, length, opts) { | ||||
| 		// unusedstring2: XLNullableWideString
 | ||||
| 	//}
 | ||||
| 	data.l = end; | ||||
| 	return {Name:name, Ptg:formula, Comment:comment}; | ||||
| 	var out = ({Name:name, Ptg:formula, Comment:comment}/*:any*/); | ||||
| 	if(itab < 0xFFFFFFF) out.Sheet = itab; | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| /* [MS-XLSB] 2.1.7.60 Workbook */ | ||||
| @ -11426,15 +11456,20 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ { | ||||
| 	if(!opts) opts = {}; | ||||
| 	opts.biff = 12; | ||||
| 
 | ||||
| 	var Names = {}, NameList = []; | ||||
| 	var Names = []; | ||||
| 	var supbooks = []; | ||||
| 	supbooks.SheetNames = []; | ||||
| 
 | ||||
| 	recordhopper(data, function hopper_wb(val, R_n, RT) { | ||||
| 		switch(RT) { | ||||
| 			case 0x009C: /* 'BrtBundleSh' */ | ||||
| 				supbooks.SheetNames.push(val.name); | ||||
| 				wb.Sheets.push(val); break; | ||||
| 
 | ||||
| 			case 0x0027: /* 'BrtName' */ | ||||
| 				Names[val.Name] = val; NameList.push(val.Name); | ||||
| 				val.Ref = stringify_formula(val.Ptg, null, null, supbooks, opts); | ||||
| 				delete val.Ptg; | ||||
| 				Names.push(val); | ||||
| 				break; | ||||
| 			case 0x040C: /* 'BrtNameExt' */ break; | ||||
| 
 | ||||
| @ -11493,7 +11528,6 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ { | ||||
| 
 | ||||
| 	parse_wb_defaults(wb); | ||||
| 
 | ||||
| 	Names['!names'] = NameList; | ||||
| 	// $FlowIgnore
 | ||||
| 	wb.Names = Names; | ||||
| 
 | ||||
| @ -11995,7 +12029,17 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ { | ||||
| 			for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = dup(csty); | ||||
| 			break; | ||||
| 
 | ||||
| 		case 'NamedRange': break; | ||||
| 		case 'NamedRange': | ||||
| 			if(!Workbook.Names) Workbook.Names = []; | ||||
| 			var _NamedRange = parsexmltag(Rn[0]); | ||||
| 			var _DefinedName = { | ||||
| 				Name: _NamedRange.Name, | ||||
| 				Ref: rc_to_a1(_NamedRange.RefersTo.substr(1)) | ||||
| 			}; | ||||
| 			if(Workbook.Sheets.length>0) _DefinedName.Sheet=Workbook.Sheets.length-1; | ||||
| 			Workbook.Names.push(_DefinedName); | ||||
| 			break; | ||||
| 
 | ||||
| 		case 'NamedCell': break; | ||||
| 		case 'B': break; | ||||
| 		case 'I': break; | ||||
| @ -12479,7 +12523,7 @@ function write_props_xlml(wb, opts) { | ||||
| 	/* DocumentProperties */ | ||||
| 	if(wb.Props) o.push(xlml_write_docprops(wb.Props, opts)); | ||||
| 	/* CustomDocumentProperties */ | ||||
| 	if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops)); | ||||
| 	if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops, opts)); | ||||
| 	return o.join(""); | ||||
| } | ||||
| /* TODO */ | ||||
| @ -12797,15 +12841,17 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ { | ||||
| 	var colinfo = [], rowinfo = []; | ||||
| 	var defwidth = 0, defheight = 0; // twips / MDW respectively
 | ||||
| 	var seencol = false; | ||||
| 	var supbooks = ([[]]/*:any*/); // 1-indexed, will hold extern names
 | ||||
| 	var sbc = 0, sbci = 0, sbcli = 0; | ||||
| 	var supbooks = ([]/*:any*/); // 1-indexed, will hold extern names
 | ||||
| 	supbooks.SheetNames = opts.snames; | ||||
| 	supbooks.sharedf = opts.sharedf; | ||||
| 	supbooks.arrayf = opts.arrayf; | ||||
| 	supbooks.names = []; | ||||
| 	supbooks.XTI = []; | ||||
| 	var last_Rn = ''; | ||||
| 	var file_depth = 0; /* TODO: make a real stack */ | ||||
| 	var BIFF2Fmt = 0; | ||||
| 	var FilterDatabases = []; /* TODO: sort out supbooks and process elsewhere */ | ||||
| 	var last_lbl; | ||||
| 
 | ||||
| 	/* explicit override for some broken writers */ | ||||
| 	opts.codepage = 1200; | ||||
| @ -12878,18 +12924,35 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ { | ||||
| 				case 'RichTextStream': break; | ||||
| 				case 'BkHim': break; | ||||
| 
 | ||||
| 				case 'SupBook': supbooks[++sbc] = [val]; sbci = 0; break; | ||||
| 				case 'ExternName': supbooks[sbc][++sbci] = val; break; | ||||
| 				case 'SupBook': | ||||
| 					supbooks.push([val]); | ||||
| 					supbooks[supbooks.length-1].XTI = []; | ||||
| 					break; | ||||
| 				case 'ExternName': | ||||
| 					supbooks[supbooks.length-1].push(val); | ||||
| 					break; | ||||
| 				case 'Index': break; // TODO
 | ||||
| 				case 'Lbl': | ||||
| 					supbooks[0][++sbcli] = val; // TODO: local formula storage in stringify_formula
 | ||||
| 					if(!supbooks[val.itab]) supbooks[val.itab] = []; | ||||
| 					supbooks[val.itab].push(val); | ||||
| 					last_lbl = { | ||||
| 						Name: val.Name, | ||||
| 						Ref: stringify_formula(val.rgce,range,null,supbooks,opts) | ||||
| 					}; | ||||
| 					if(val.itab > 0) last_lbl.Sheet = val.itab - 1; | ||||
| 					supbooks.names.push(last_lbl); | ||||
| 					if(!supbooks[0]) supbooks[0] = []; | ||||
| 					supbooks[supbooks.length-1].push(val); | ||||
| 					if(val.Name == "\r" && val.itab > 0) | ||||
| 						if(val.rgce && val.rgce[0] && val.rgce[0][0] && val.rgce[0][0][0] == 'PtgArea3d') | ||||
| 							FilterDatabases[val.itab - 1] = { ref: encode_range(val.rgce[0][0][1][2]) }; | ||||
| 					break; | ||||
| 				case 'ExternSheet': supbooks[sbc] = supbooks[sbc].concat(val); sbci += val.length; break; | ||||
| 				case 'ExternSheet': | ||||
| 					if(supbooks.length == 0) { supbooks[0] = []; supbooks[0].XTI = []; } | ||||
| 					supbooks[supbooks.length - 1].XTI = supbooks[supbooks.length - 1].XTI.concat(val); supbooks.XTI = supbooks.XTI.concat(val); break; | ||||
| 				case 'NameCmt': | ||||
| 					/* TODO: search for correct name */ | ||||
| 					if(opts.biff < 8) break; | ||||
| 					last_lbl.Comment = val[1]; | ||||
| 					break; | ||||
| 
 | ||||
| 				case 'Protect': out["!protect"] = val; break; /* for sheet or book */ | ||||
| 				case 'Password': if(val !== 0 && opts.WTF) console.error("Password verifier: " + val); break; | ||||
| @ -13112,7 +13175,6 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ { | ||||
| 				} break; | ||||
| 				case 'Row': break; // TODO
 | ||||
| 
 | ||||
| 				case 'NameCmt': break; | ||||
| 				case 'Header': break; // TODO
 | ||||
| 				case 'Footer': break; // TODO
 | ||||
| 				case 'HCenter': break; // TODO
 | ||||
| @ -13355,6 +13417,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ { | ||||
| 	if(opts.enc) wb.Encryption = opts.enc; | ||||
| 	wb.Metadata = {}; | ||||
| 	if(country !== undefined) wb.Metadata.Country = country; | ||||
| 	if(supbooks.names.length > 0) Workbook.Names = supbooks.names; | ||||
| 	wb.Workbook = Workbook; | ||||
| 	return wb; | ||||
| } | ||||
| @ -14515,7 +14578,7 @@ var XLSRecordEnum = { | ||||
| 	/*::[*/0x0890/*::]*/: { n:"TableStyleElement", f:parse_TableStyleElement }, | ||||
| 	/*::[*/0x0892/*::]*/: { n:"StyleExt", f:parse_StyleExt }, | ||||
| 	/*::[*/0x0893/*::]*/: { n:"NamePublish", f:parse_NamePublish }, | ||||
| 	/*::[*/0x0894/*::]*/: { n:"NameCmt", f:parse_NameCmt }, | ||||
| 	/*::[*/0x0894/*::]*/: { n:"NameCmt", f:parse_NameCmt, r:12 }, | ||||
| 	/*::[*/0x0895/*::]*/: { n:"SortData", f:parse_SortData }, | ||||
| 	/*::[*/0x0896/*::]*/: { n:"Theme", f:parse_Theme, r:12 }, | ||||
| 	/*::[*/0x0897/*::]*/: { n:"GUIDTypeLib", f:parse_GUIDTypeLib }, | ||||
|  | ||||
							
								
								
									
										155
									
								
								xlsx.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										155
									
								
								xlsx.js
									
									
									
									
									
								
							| @ -3219,7 +3219,7 @@ function xlml_write_docprops(Props, opts) { | ||||
| 			case 'date': m = new Date(m).toISOString(); break; | ||||
| 		} | ||||
| 		if(typeof m == 'number') m = String(m); | ||||
| 		else if(m === true || m === false) { t = "boolean"; m = m ? "1" : "0"; } | ||||
| 		else if(m === true || m === false) { m = m ? "1" : "0"; } | ||||
| 		else if(m instanceof Date) m = new Date(m).toISOString(); | ||||
| 		o.push(writetag(XLMLDocPropsMap[p[1]] || p[1], m)); | ||||
| 	}); | ||||
| @ -3534,13 +3534,6 @@ function parslurp(blob, length, cb) { | ||||
| 	return arr; | ||||
| } | ||||
| 
 | ||||
| function parslurp2(blob, length, cb) { | ||||
| 	var arr = [], target = blob.l + length, len = blob.read_shift(2); | ||||
| 	while(len-- !== 0) arr.push(cb(blob, target - blob.l)); | ||||
| 	if(target !== blob.l) throw new Error("Slurp error"); | ||||
| 	return arr; | ||||
| } | ||||
| 
 | ||||
| function parsebool(blob, length) { return blob.read_shift(length) === 0x1; } | ||||
| 
 | ||||
| function parseuint16(blob) { return blob.read_shift(2, 'u'); } | ||||
| @ -4144,6 +4137,7 @@ function parse_ExternName(blob, length, opts) { | ||||
| 	if(opts.sbcch === 0x3A01) body = parse_AddinUdf(blob, length-2, opts); | ||||
| 	//else throw new Error("unsupported SupBook cch: " + opts.sbcch);
 | ||||
| 	o.body = body || blob.read_shift(length-2); | ||||
| 	if(typeof body === "string") o.Name = body; | ||||
| 	return o; | ||||
| } | ||||
| 
 | ||||
| @ -4174,13 +4168,21 @@ function parse_Lbl(blob, length, opts) { | ||||
| /* 2.4.106 TODO: verify supbook manipulation */ | ||||
| function parse_ExternSheet(blob, length, opts) { | ||||
| 	if(opts.biff < 8) return parse_ShortXLUnicodeString(blob, length, opts); | ||||
| 	var o = parslurp2(blob,length,parse_XTI); | ||||
| 	var o = [], target = blob.l + length, len = blob.read_shift(2); | ||||
| 	while(len-- !== 0) o.push(parse_XTI(blob, 6)); | ||||
| 		// [iSupBook, itabFirst, itabLast];
 | ||||
| 	var oo = []; | ||||
| 	if(opts.sbcch === 0x0401) { | ||||
| 		for(var i = 0; i != o.length; ++i) oo.push(opts.snames[o[i][1]]); | ||||
| 		return oo; | ||||
| 	} | ||||
| 	else return o; | ||||
| 	return o; | ||||
| } | ||||
| 
 | ||||
| /* 2.4.176 TODO: check older biff */ | ||||
| function parse_NameCmt(blob, length, opts) { | ||||
| 	if(opts.biff < 8) { blob.l += length; return; } | ||||
| 	var cchName = blob.read_shift(2); | ||||
| 	var cchComment = blob.read_shift(2); | ||||
| 	var name = parse_XLUnicodeStringNoCch(blob, cchName, opts); | ||||
| 	var comment = parse_XLUnicodeStringNoCch(blob, cchComment, opts); | ||||
| 	return [name, comment]; | ||||
| } | ||||
| 
 | ||||
| /* 2.4.260 */ | ||||
| @ -4596,7 +4598,6 @@ var parse_TableStyles = parsenoop; | ||||
| var parse_TableStyle = parsenoop; | ||||
| var parse_TableStyleElement = parsenoop; | ||||
| var parse_NamePublish = parsenoop; | ||||
| var parse_NameCmt = parsenoop; | ||||
| var parse_SortData = parsenoop; | ||||
| var parse_GUIDTypeLib = parsenoop; | ||||
| var parse_FnGrp12 = parsenoop; | ||||
| @ -8073,13 +8074,13 @@ function stringify_formula(formula/*Array<any>*/, range, cell, supbooks, opts) { | ||||
| 				break; | ||||
| 			/* 2.5.198.88 */ | ||||
| 			case 'PtgRefN': | ||||
| 				type = f[1][0]; c = shift_cell_xls(f[1][1], cell, opts); | ||||
| 				type = f[1][0]; c = cell ? shift_cell_xls(f[1][1], cell, opts) : f[1][1]; | ||||
| 				stack.push(encode_cell_xls(c)); | ||||
| 				break; | ||||
| 			case 'PtgRef3d': // TODO: lots of stuff
 | ||||
| 				type = f[1][0]; ixti = f[1][1]; c = shift_cell_xls(f[1][2], _range, opts); | ||||
| 				sname = (supbooks && supbooks[1] ? supbooks[1][ixti+1] : "**MISSING**"); | ||||
| 				stack.push(sname + "!" + encode_cell(c)); | ||||
| 				sname = supbooks.SheetNames[ixti]; | ||||
| 				stack.push(sname + "!" + encode_cell_xls(c)); | ||||
| 				break; | ||||
| 
 | ||||
| 			/* 2.5.198.62 */ | ||||
| @ -8135,7 +8136,7 @@ function stringify_formula(formula/*Array<any>*/, range, cell, supbooks, opts) { | ||||
| 			case 'PtgName': | ||||
| 				/* f[1] = type, 0, nameindex */ | ||||
| 				nameidx = f[1][2]; | ||||
| 				var lbl = supbooks[0][nameidx]; | ||||
| 				var lbl = (supbooks.names||[])[nameidx-1] || (supbooks[0]||[])[nameidx]; | ||||
| 				var name = lbl ? lbl.Name : "**MISSING**" + String(nameidx); | ||||
| 				if(name in XLSXFutureFunctions) name = XLSXFutureFunctions[name]; | ||||
| 				stack.push(name); | ||||
| @ -8147,15 +8148,27 @@ function stringify_formula(formula/*Array<any>*/, range, cell, supbooks, opts) { | ||||
| 				var bookidx = (f[1][1]); nameidx = f[1][2]; var externbook; | ||||
| 				/* TODO: Properly handle missing values */ | ||||
| 				//console.log(bookidx, supbooks);
 | ||||
| 				if(opts.biff == 5) { | ||||
| 				if(opts.biff <= 5) { | ||||
| 					if(bookidx < 0) bookidx = -bookidx; | ||||
| 					if(supbooks[bookidx]) externbook = supbooks[bookidx][nameidx]; | ||||
| 				} else { | ||||
| 					if(supbooks[bookidx+1]) externbook = supbooks[bookidx+1][nameidx]; | ||||
| 					else if(supbooks[bookidx-1]) externbook = supbooks[bookidx-1][nameidx]; | ||||
| 					var pnxname = supbooks.SheetNames[bookidx]; | ||||
| 					var o = ""; | ||||
| 					if(((supbooks[bookidx]||[])[0]||[])[0] == 0x3A01){} | ||||
| 					else if(((supbooks[bookidx]||[])[0]||[])[0] == 0x0401){ | ||||
| 						if(supbooks[bookidx][nameidx] && supbooks[bookidx][nameidx].itab > 0) { | ||||
| 							o = supbooks.SheetNames[supbooks[bookidx][nameidx].itab-1] + "!"; | ||||
| 						} | ||||
| 					} | ||||
| 					else o = supbooks.SheetNames[nameidx-1]+ "!"; | ||||
| 					if(supbooks[bookidx] && supbooks[bookidx][nameidx]) o += supbooks[bookidx][nameidx].Name; | ||||
| 					else if(supbooks[0] && supbooks[0][nameidx]) o += supbooks[0][nameidx].Name; | ||||
| 					else o += "??NAMEX??"; | ||||
| 					stack.push(o); | ||||
| 					break; | ||||
| 				} | ||||
| 				if(!externbook) externbook = {body: "??NAMEX??"}; | ||||
| 				stack.push(externbook.body); | ||||
| 				if(!externbook) externbook = {Name: "??NAMEX??"}; | ||||
| 				stack.push(externbook.Name); | ||||
| 				break; | ||||
| 
 | ||||
| 			/* 2.5.198.80 */ | ||||
| @ -8241,6 +8254,9 @@ function stringify_formula(formula/*Array<any>*/, range, cell, supbooks, opts) { | ||||
| 			/* 2.5.198.29 */ | ||||
| 			case 'PtgAreaErr': stack.push("#REF!"); break; | ||||
| 
 | ||||
| 			/* 2.5.198.30 */ | ||||
| 			case 'PtgAreaErr3d': stack.push("#REF!"); break; | ||||
| 
 | ||||
| 			/* 2.5.198.72 TODO */ | ||||
| 			case 'PtgMemFunc': break; | ||||
| 
 | ||||
| @ -10471,9 +10487,9 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles) { | ||||
| 	var supbooks = ([[]]); | ||||
| 	supbooks.sharedf = shared_formulae; | ||||
| 	supbooks.arrayf = array_formulae; | ||||
| 	supbooks.SheetNames = wb.SheetNames || wb.Sheets.map(function(x) { return x.name; }); | ||||
| 	opts.supbooks = supbooks; | ||||
| 
 | ||||
| 	for(var i = 0; i < wb.Names['!names'].length; ++i) supbooks[0][i+1] = wb.Names[wb.Names['!names'][i]]; | ||||
| 	for(var i = 0; i < wb.Names.length; ++i) supbooks[0][i+1] = wb.Names[i]; | ||||
| 
 | ||||
| 	var colinfo = [], rowinfo = []; | ||||
| 	var defwidth = 0, defheight = 0; // twips / MDW respectively
 | ||||
| @ -11104,7 +11120,7 @@ function check_wb(wb) { | ||||
| var wbnsregex = /<\w+:workbook/; | ||||
| function parse_wb_xml(data, opts) { | ||||
| 	if(!data) throw new Error("Could not find file"); | ||||
| 	var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, Names:{'!names':[]}, xmlns: "" }; | ||||
| 	var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, Names:[], xmlns: "" }; | ||||
| 	var pass = false, xmlns = "xmlns"; | ||||
| 	var dname = {}, dnstart = 0; | ||||
| 	/*(data.match(tagregex)||[]).forEach */ | ||||
| @ -11175,12 +11191,12 @@ function parse_wb_xml(data, opts) { | ||||
| 				dname = {}; | ||||
| 				dname.Name = y.name; | ||||
| 				if(y.comment) dname.Comment = y.comment; | ||||
| 				if(y.localSheetId) dname.Sheet = +y.localSheetId; | ||||
| 				dnstart = idx + x.length; | ||||
| 			}	break; | ||||
| 			case '</definedName>': { | ||||
| 				dname.Ref = data.slice(dnstart, idx); | ||||
| 				wb.Names[dname.Name] = dname; | ||||
| 				wb.Names['!names'].push(dname.Name); | ||||
| 				wb.Names.push(dname); | ||||
| 			} break; | ||||
| 			case '<definedName/>': break; | ||||
| 
 | ||||
| @ -11286,7 +11302,19 @@ function write_wb_xml(wb, opts) { | ||||
| 
 | ||||
| 	/* functionGroups */ | ||||
| 	/* externalReferences */ | ||||
| 	/* definedNames */ | ||||
| 
 | ||||
| 	if(wb.Workbook && (wb.Workbook.Names||[]).length > 0) { | ||||
| 		o[o.length] = "<definedNames>"; | ||||
| 		wb.Workbook.Names.forEach(function(n) { | ||||
| 			var d = {name:n.Name}; | ||||
| 			if(n.Comment) d.comment = n.Comment; | ||||
| 			if(n.Sheet != null) d.localSheetId = ""+n.Sheet; | ||||
| 			if(!n.Ref) return; | ||||
| 			o[o.length] = writextag('definedName', String(n.Ref), d); | ||||
| 		}); | ||||
| 		o[o.length] = "</definedNames>"; | ||||
| 	} | ||||
| 
 | ||||
| 	/* calcPr */ | ||||
| 	/* oleSize */ | ||||
| 	/* customWorkbookViews */ | ||||
| @ -11358,7 +11386,9 @@ function parse_BrtName(data, length, opts) { | ||||
| 		// unusedstring2: XLNullableWideString
 | ||||
| 	//}
 | ||||
| 	data.l = end; | ||||
| 	return {Name:name, Ptg:formula, Comment:comment}; | ||||
| 	var out = ({Name:name, Ptg:formula, Comment:comment}); | ||||
| 	if(itab < 0xFFFFFFF) out.Sheet = itab; | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| /* [MS-XLSB] 2.1.7.60 Workbook */ | ||||
| @ -11369,15 +11399,20 @@ function parse_wb_bin(data, opts) { | ||||
| 	if(!opts) opts = {}; | ||||
| 	opts.biff = 12; | ||||
| 
 | ||||
| 	var Names = {}, NameList = []; | ||||
| 	var Names = []; | ||||
| 	var supbooks = []; | ||||
| 	supbooks.SheetNames = []; | ||||
| 
 | ||||
| 	recordhopper(data, function hopper_wb(val, R_n, RT) { | ||||
| 		switch(RT) { | ||||
| 			case 0x009C: /* 'BrtBundleSh' */ | ||||
| 				supbooks.SheetNames.push(val.name); | ||||
| 				wb.Sheets.push(val); break; | ||||
| 
 | ||||
| 			case 0x0027: /* 'BrtName' */ | ||||
| 				Names[val.Name] = val; NameList.push(val.Name); | ||||
| 				val.Ref = stringify_formula(val.Ptg, null, null, supbooks, opts); | ||||
| 				delete val.Ptg; | ||||
| 				Names.push(val); | ||||
| 				break; | ||||
| 			case 0x040C: /* 'BrtNameExt' */ break; | ||||
| 
 | ||||
| @ -11436,7 +11471,6 @@ function parse_wb_bin(data, opts) { | ||||
| 
 | ||||
| 	parse_wb_defaults(wb); | ||||
| 
 | ||||
| 	Names['!names'] = NameList; | ||||
| 	// $FlowIgnore
 | ||||
| 	wb.Names = Names; | ||||
| 
 | ||||
| @ -11935,7 +11969,17 @@ for(var cma = c; cma <= cc; ++cma) { | ||||
| 			for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = dup(csty); | ||||
| 			break; | ||||
| 
 | ||||
| 		case 'NamedRange': break; | ||||
| 		case 'NamedRange': | ||||
| 			if(!Workbook.Names) Workbook.Names = []; | ||||
| 			var _NamedRange = parsexmltag(Rn[0]); | ||||
| 			var _DefinedName = { | ||||
| 				Name: _NamedRange.Name, | ||||
| 				Ref: rc_to_a1(_NamedRange.RefersTo.substr(1)) | ||||
| 			}; | ||||
| 			if(Workbook.Sheets.length>0) _DefinedName.Sheet=Workbook.Sheets.length-1; | ||||
| 			Workbook.Names.push(_DefinedName); | ||||
| 			break; | ||||
| 
 | ||||
| 		case 'NamedCell': break; | ||||
| 		case 'B': break; | ||||
| 		case 'I': break; | ||||
| @ -12418,7 +12462,7 @@ function write_props_xlml(wb, opts) { | ||||
| 	/* DocumentProperties */ | ||||
| 	if(wb.Props) o.push(xlml_write_docprops(wb.Props, opts)); | ||||
| 	/* CustomDocumentProperties */ | ||||
| 	if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops)); | ||||
| 	if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops, opts)); | ||||
| 	return o.join(""); | ||||
| } | ||||
| /* TODO */ | ||||
| @ -12736,15 +12780,17 @@ function parse_workbook(blob, options) { | ||||
| 	var colinfo = [], rowinfo = []; | ||||
| 	var defwidth = 0, defheight = 0; // twips / MDW respectively
 | ||||
| 	var seencol = false; | ||||
| 	var supbooks = ([[]]); // 1-indexed, will hold extern names
 | ||||
| 	var sbc = 0, sbci = 0, sbcli = 0; | ||||
| 	var supbooks = ([]); // 1-indexed, will hold extern names
 | ||||
| 	supbooks.SheetNames = opts.snames; | ||||
| 	supbooks.sharedf = opts.sharedf; | ||||
| 	supbooks.arrayf = opts.arrayf; | ||||
| 	supbooks.names = []; | ||||
| 	supbooks.XTI = []; | ||||
| 	var last_Rn = ''; | ||||
| 	var file_depth = 0; /* TODO: make a real stack */ | ||||
| 	var BIFF2Fmt = 0; | ||||
| 	var FilterDatabases = []; /* TODO: sort out supbooks and process elsewhere */ | ||||
| 	var last_lbl; | ||||
| 
 | ||||
| 	/* explicit override for some broken writers */ | ||||
| 	opts.codepage = 1200; | ||||
| @ -12817,18 +12863,35 @@ function parse_workbook(blob, options) { | ||||
| 				case 'RichTextStream': break; | ||||
| 				case 'BkHim': break; | ||||
| 
 | ||||
| 				case 'SupBook': supbooks[++sbc] = [val]; sbci = 0; break; | ||||
| 				case 'ExternName': supbooks[sbc][++sbci] = val; break; | ||||
| 				case 'SupBook': | ||||
| 					supbooks.push([val]); | ||||
| 					supbooks[supbooks.length-1].XTI = []; | ||||
| 					break; | ||||
| 				case 'ExternName': | ||||
| 					supbooks[supbooks.length-1].push(val); | ||||
| 					break; | ||||
| 				case 'Index': break; // TODO
 | ||||
| 				case 'Lbl': | ||||
| 					supbooks[0][++sbcli] = val; // TODO: local formula storage in stringify_formula
 | ||||
| 					if(!supbooks[val.itab]) supbooks[val.itab] = []; | ||||
| 					supbooks[val.itab].push(val); | ||||
| 					last_lbl = { | ||||
| 						Name: val.Name, | ||||
| 						Ref: stringify_formula(val.rgce,range,null,supbooks,opts) | ||||
| 					}; | ||||
| 					if(val.itab > 0) last_lbl.Sheet = val.itab - 1; | ||||
| 					supbooks.names.push(last_lbl); | ||||
| 					if(!supbooks[0]) supbooks[0] = []; | ||||
| 					supbooks[supbooks.length-1].push(val); | ||||
| 					if(val.Name == "\r" && val.itab > 0) | ||||
| 						if(val.rgce && val.rgce[0] && val.rgce[0][0] && val.rgce[0][0][0] == 'PtgArea3d') | ||||
| 							FilterDatabases[val.itab - 1] = { ref: encode_range(val.rgce[0][0][1][2]) }; | ||||
| 					break; | ||||
| 				case 'ExternSheet': supbooks[sbc] = supbooks[sbc].concat(val); sbci += val.length; break; | ||||
| 				case 'ExternSheet': | ||||
| 					if(supbooks.length == 0) { supbooks[0] = []; supbooks[0].XTI = []; } | ||||
| 					supbooks[supbooks.length - 1].XTI = supbooks[supbooks.length - 1].XTI.concat(val); supbooks.XTI = supbooks.XTI.concat(val); break; | ||||
| 				case 'NameCmt': | ||||
| 					/* TODO: search for correct name */ | ||||
| 					if(opts.biff < 8) break; | ||||
| 					last_lbl.Comment = val[1]; | ||||
| 					break; | ||||
| 
 | ||||
| 				case 'Protect': out["!protect"] = val; break; /* for sheet or book */ | ||||
| 				case 'Password': if(val !== 0 && opts.WTF) console.error("Password verifier: " + val); break; | ||||
| @ -13051,7 +13114,6 @@ function parse_workbook(blob, options) { | ||||
| 				} break; | ||||
| 				case 'Row': break; // TODO
 | ||||
| 
 | ||||
| 				case 'NameCmt': break; | ||||
| 				case 'Header': break; // TODO
 | ||||
| 				case 'Footer': break; // TODO
 | ||||
| 				case 'HCenter': break; // TODO
 | ||||
| @ -13294,6 +13356,7 @@ function parse_workbook(blob, options) { | ||||
| 	if(opts.enc) wb.Encryption = opts.enc; | ||||
| 	wb.Metadata = {}; | ||||
| 	if(country !== undefined) wb.Metadata.Country = country; | ||||
| 	if(supbooks.names.length > 0) Workbook.Names = supbooks.names; | ||||
| 	wb.Workbook = Workbook; | ||||
| 	return wb; | ||||
| } | ||||
| @ -14454,7 +14517,7 @@ var XLSRecordEnum = { | ||||
| 0x0890: { n:"TableStyleElement", f:parse_TableStyleElement }, | ||||
| 0x0892: { n:"StyleExt", f:parse_StyleExt }, | ||||
| 0x0893: { n:"NamePublish", f:parse_NamePublish }, | ||||
| 0x0894: { n:"NameCmt", f:parse_NameCmt }, | ||||
| 0x0894: { n:"NameCmt", f:parse_NameCmt, r:12 }, | ||||
| 0x0895: { n:"SortData", f:parse_SortData }, | ||||
| 0x0896: { n:"Theme", f:parse_Theme, r:12 }, | ||||
| 0x0897: { n:"GUIDTypeLib", f:parse_GUIDTypeLib }, | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user