forked from sheetjs/sheetjs
		
	Read/Write Comments
- XLSB/XLSX/XLML write comments - BIFF2 write date cells (see #586 h/t @roccomuso) - ODS read cell comments (fixed #315 h/t @yisk) - XLSX / XLSB emit empty comments when necessary - changed node detection logic (fixes #614 h/t @mhenris) - fixes #605 h/t @ylbweb - fixes #233 h/t @hanxi @osecki - fixes #192 h/t @abarik1981 @stla - fixes #183 h/t @aravindkoneru @ryangallen
This commit is contained in:
		
							parent
							
								
									b93569badf
								
							
						
					
					
						commit
						3a310bd3a7
					
				| @ -7,6 +7,8 @@ changes may not be included if they are not expected to break existing code. | ||||
| 
 | ||||
| ## Unreleased | ||||
| 
 | ||||
| * default output format changed to XLSB | ||||
| * comment text line endings are now normalized | ||||
| 
 | ||||
| ## 0.9.7 (2017-03-28) | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										22
									
								
								README.md
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										22
									
								
								README.md
									
									
									
									
									
								
							| @ -46,6 +46,7 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6. | ||||
|     + [Formulae](#formulae) | ||||
|     + [Column Properties](#column-properties) | ||||
|     + [Hyperlinks](#hyperlinks) | ||||
|     + [Cell Comments](#cell-comments) | ||||
|     + [Sheet Visibility](#sheet-visibility) | ||||
| - [Parsing Options](#parsing-options) | ||||
|   * [Input Type](#input-type) | ||||
| @ -756,6 +757,23 @@ ws['A3'].l = { Target:"http://sheetjs.com", Tooltip:"Find us @ SheetJS.com!" }; | ||||
| Note that Excel does not automatically style hyperlinks -- they will generally | ||||
| be displayed as normal text. | ||||
| 
 | ||||
| #### Cell Comments | ||||
| 
 | ||||
| Cell comments are objects stored in the `c` array of cell objects.  The actual | ||||
| contents of the comment are split into blocks based on the comment author.  The | ||||
| `a` field of each comment object is the author of the comment and the `t` field | ||||
| is the plaintext representation. | ||||
| 
 | ||||
| For example, the following snippet appends a cell comment into cell `A1`: | ||||
| 
 | ||||
| ```js | ||||
| if(!ws.A1.c) ws.A1.c = []; | ||||
| ws.A1.c.push({a:"SheetJS", t:"I'm a little comment, short and stout!"}); | ||||
| ``` | ||||
| 
 | ||||
| Note: XLSB enforces a 54 character limit on the Author name.  Names longer than | ||||
| 54 characters may cause issues with other formats. | ||||
| 
 | ||||
| #### Sheet Visibility | ||||
| 
 | ||||
| Excel enables hiding sheets in the lower tab bar.  The sheet data is stored in | ||||
| @ -780,7 +798,7 @@ With <https://rawgit.com/SheetJS/test_files/master/sheet_visibility.xlsx>: | ||||
| ``` | ||||
| 
 | ||||
| Non-Excel formats do not support the Very Hidden state.  The best way to test | ||||
| if a sheet is visible is to check if the `Hidden` property is logical negation: | ||||
| if a sheet is visible is to check if the `Hidden` property is logical truth: | ||||
| 
 | ||||
| ```js | ||||
| > wb.Workbook.Sheets.map(function(x) { return [x.name, !x.Hidden] }) | ||||
| @ -856,7 +874,7 @@ file but Excel will know how to handle it.  This library applies similar logic: | ||||
| | `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` | UTF8 Text     | SpreadsheetML or Flat ODS or UOS1 or plaintext      | | ||||
| | `0xFE` | UTF16 Encoded | SpreadsheetML or Flat ODS or UOS1 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) | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| var has_buf = (typeof Buffer !== 'undefined'); | ||||
| var has_buf = (typeof Buffer !== 'undefined' && typeof process !== 'undefined' && typeof process.versions !== 'undefined' && process.versions.node); | ||||
| 
 | ||||
| function new_raw_buf(len/*:number*/) { | ||||
| 	/* jshint -W056 */ | ||||
|  | ||||
| @ -60,6 +60,9 @@ var xlml_fixstr/*:StringConv*/ = (function() { | ||||
| 	function entrepl($$/*:string*/,$1/*:string*/)/*:string*/ { return String.fromCharCode(parseInt($1,10)); } | ||||
| 	return function xlml_fixstr(str/*:string*/)/*:string*/ { return str.replace(entregex,entrepl); }; | ||||
| })(); | ||||
| var xlml_unfixstr/*:StringConv*/ = (function() { | ||||
| 	return function xlml_unfixstr(str/*:string*/)/*:string*/ { return str.replace(/(\r\n|[\r\n])/g,"\
"); }; | ||||
| })(); | ||||
| 
 | ||||
| function parsexmlbool(value/*:any*/, tag/*:?string*/)/*:boolean*/ { | ||||
| 	switch(value) { | ||||
| @ -184,5 +187,7 @@ var XLMLNS = ({ | ||||
| 	'x':    'urn:schemas-microsoft-com:office:excel', | ||||
| 	'ss':   'urn:schemas-microsoft-com:office:spreadsheet', | ||||
| 	'dt':   'uuid:C2F41010-65B3-11d1-A29F-00AA00C14882', | ||||
| 	'mv':   'http://macVmlSchemaUri', | ||||
| 	'v':    'urn:schemas-microsoft-com:vml', | ||||
| 	'html': 'http://www.w3.org/TR/REC-html40' | ||||
| }/*:any*/); | ||||
|  | ||||
| @ -10,7 +10,9 @@ function recordhopper(data, cb/*:RecordHopperCB*/, opts/*:?any*/) { | ||||
| 		tmpbyte = data.read_shift(1); | ||||
| 		length = tmpbyte & 0x7F; | ||||
| 		for(cntbyte = 1; cntbyte <4 && (tmpbyte & 0x80); ++cntbyte) length += ((tmpbyte = data.read_shift(1)) & 0x7F)<<(7*cntbyte); | ||||
| 		var tgt = data.l + length; | ||||
| 		var d = R.f(data, length, opts); | ||||
| 		data.l = tgt; | ||||
| 		if(cb(d, R, RT)) return; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -17,7 +17,7 @@ function parse_RichStr(data, length/*:number*/)/*:XLString*/ { | ||||
| 		for(var i = 0; i != dwSizeStrRun; ++i) rgsStrRun.push(parse_StrRun(data)); | ||||
| 		z.r = rgsStrRun; | ||||
| 	} | ||||
| 	else z.r = "<t>" + escapexml(str) + "</t>"; | ||||
| 	else z.r = [{ich:0, ifnt:0}]; | ||||
| 	//if((flags & 2) !== 0) { /* fExtStr */
 | ||||
| 	//	/* TODO: phonetic string */
 | ||||
| 	//}
 | ||||
| @ -26,10 +26,10 @@ function parse_RichStr(data, length/*:number*/)/*:XLString*/ { | ||||
| } | ||||
| function write_RichStr(str/*:XLString*/, o/*:?Block*/)/*:Block*/ { | ||||
| 	/* TODO: formatted string */ | ||||
| 	if(o == null) o = new_buf(5+2*str.t.length); | ||||
| 	var _null = false; if(o == null) { _null = true; o = new_buf(15+4*str.t.length); } | ||||
| 	o.write_shift(1,0); | ||||
| 	write_XLWideString(str.t, o); | ||||
| 	return o; | ||||
| 	return _null ? o.slice(0, o.l) : o; | ||||
| } | ||||
| 
 | ||||
| /* [MS-XLSB] 2.5.9 */ | ||||
| @ -59,10 +59,10 @@ function parse_XLNullableWideString(data)/*:string*/ { | ||||
| 	return cchCharacters === 0 || cchCharacters === 0xFFFFFFFF ? "" : data.read_shift(cchCharacters, 'dbcs'); | ||||
| } | ||||
| function write_XLNullableWideString(data/*:string*/, o) { | ||||
| 	if(!o) o = new_buf(127); | ||||
| 	var _null = false; if(o == null) { _null = true; o = new_buf(127); } | ||||
| 	o.write_shift(4, data.length > 0 ? data.length : 0xFFFFFFFF); | ||||
| 	if(data.length > 0) o.write_shift(0, data, 'dbcs'); | ||||
| 	return o; | ||||
| 	return _null ? o.slice(0, o.l) : o; | ||||
| } | ||||
| 
 | ||||
| /* [MS-XLSB] 2.5.168 */ | ||||
| @ -71,10 +71,10 @@ function parse_XLWideString(data)/*:string*/ { | ||||
| 	return cchCharacters === 0 ? "" : data.read_shift(cchCharacters, 'dbcs'); | ||||
| } | ||||
| function write_XLWideString(data/*:string*/, o) { | ||||
| 	if(o == null) o = new_buf(4+2*data.length); | ||||
| 	var _null = false; if(o == null) { _null = true; o = new_buf(4+2*data.length); } | ||||
| 	o.write_shift(4, data.length); | ||||
| 	if(data.length > 0) o.write_shift(0, data, 'dbcs'); | ||||
| 	return o; | ||||
| 	return _null ? o.slice(0, o.l) : o; | ||||
| } | ||||
| 
 | ||||
| /* [MS-XLSB] 2.5.165 */ | ||||
|  | ||||
| @ -23,10 +23,6 @@ var ct2type/*{[string]:string}*/ = ({ | ||||
| 	"application/vnd.openxmlformats-officedocument.customXmlProperties+xml": "TODO", | ||||
| 	"application/vnd.openxmlformats-officedocument.spreadsheetml.customProperty": "TODO", | ||||
| 
 | ||||
| 	/* Comments */ | ||||
| 	"application/vnd.ms-excel.comments": "comments", | ||||
| 	"application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": "comments", | ||||
| 
 | ||||
| 	/* PivotTable */ | ||||
| 	"application/vnd.ms-excel.pivotTable": "TODO", | ||||
| 	"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml": "TODO", | ||||
| @ -158,6 +154,10 @@ var CT_LIST = (function(){ | ||||
| 			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml", | ||||
| 			xlsb: "application/vnd.ms-excel.sharedStrings" | ||||
| 		}, | ||||
| 		comments: { /* Comments */ | ||||
| 			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml", | ||||
| 			xlsb: "application/vnd.ms-excel.comments" | ||||
| 		}, | ||||
| 		sheets: { /* Worksheet */ | ||||
| 			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml", | ||||
| 			xlsb: "application/vnd.ms-excel.worksheet" | ||||
| @ -226,6 +226,7 @@ var CTYPE_XML_ROOT = writextag('Types', null, { | ||||
| var CTYPE_DEFAULTS = [ | ||||
| 	['xml', 'application/xml'], | ||||
| 	['bin', 'application/vnd.ms-excel.sheet.binary.macroEnabled.main'], | ||||
| 	['vml', 'application/vnd.openxmlformats-officedocument.vmlDrawing'], | ||||
| 	/* from test files */ | ||||
| 	['bmp', 'image/bmp'], | ||||
| 	['png', 'image/png'], | ||||
| @ -276,7 +277,7 @@ function write_ct(ct, opts)/*:string*/ { | ||||
| 	['strs', 'styles'].forEach(f1); | ||||
| 	['coreprops', 'extprops', 'custprops'].forEach(f3); | ||||
| 	f3('vba'); | ||||
| 	f3('comments'); | ||||
| 	f2('comments'); | ||||
| 	if(o.length>2){ o[o.length] = ('</Types>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| } | ||||
|  | ||||
| @ -3,6 +3,7 @@ var RELS = ({ | ||||
| 	WB: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", | ||||
| 	SHEET: "http://sheetjs.openxmlformats.org/officeDocument/2006/relationships/officeDocument", | ||||
| 	HLINK: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", | ||||
| 	VML: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing", | ||||
| 	VBA: "http://schemas.microsoft.com/office/2006/relationships/vbaProject" | ||||
| }/*:any*/); | ||||
| 
 | ||||
|  | ||||
| @ -359,3 +359,56 @@ var DIF = (function() { | ||||
| 		from_sheet: sheet_to_dif | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| var PRN = (function() { | ||||
| 	function prn_to_aoa(d/*:RawData*/, opts)/*:AOA*/ { | ||||
| 		switch(opts.type) { | ||||
| 			case 'base64': return prn_to_aoa_str(Base64.decode(d), opts); | ||||
| 			case 'binary': return prn_to_aoa_str(d, opts); | ||||
| 			case 'buffer': return prn_to_aoa_str(d.toString('binary'), opts); | ||||
| 			case 'array': return prn_to_aoa_str(cc2str(d), opts); | ||||
| 		} | ||||
| 		throw new Error("Unrecognized type " + opts.type); | ||||
| 	} | ||||
| 
 | ||||
| 	function set_text_arr(data/*:string*/, arr/*:AOA*/, R/*:number*/, C/*:number*/) { | ||||
| 		if(data === 'TRUE') arr[R][C] = true; | ||||
| 		else if(data === 'FALSE') arr[R][C] = false; | ||||
| 		else if(+data == +data) arr[R][C] = +data; | ||||
| 		else if(data !== "") arr[R][C] = data; | ||||
| 	} | ||||
| 
 | ||||
| 	function prn_to_aoa_str(f/*:string*/, opts)/*:AOA*/ { | ||||
| 		var arr/*:AOA*/ = ([]/*:any*/); | ||||
| 		if(!f || f.length === 0) return arr; | ||||
| 		var lines = f.split(/[\r\n]/); | ||||
| 		var L = lines.length - 1; | ||||
| 		while(L >= 0 && lines[L].length === 0) --L; | ||||
| 		var start = 10, idx = 0; | ||||
| 		var R = 0; | ||||
| 		for(; R <= L; ++R) { | ||||
| 			idx = lines[R].indexOf(" "); | ||||
| 			if(idx == -1) idx = lines[R].length; else idx++; | ||||
| 			start = Math.max(start, idx); | ||||
| 		} | ||||
| 		for(R = 0; R <= L; ++R) { | ||||
| 			arr[R] = []; | ||||
| 			/* TODO: confirm that widths are always 10 */ | ||||
| 			var C = 0; | ||||
| 			set_text_arr(lines[R].slice(0, start).trim(), arr, R, C); | ||||
| 			for(C = 1; C <= (lines[R].length - start)/10 + 1; ++C) | ||||
| 				set_text_arr(lines[R].slice(start+(C-1)*10,start+C*10).trim(),arr,R,C); | ||||
| 		} | ||||
| 		return arr; | ||||
| 	} | ||||
| 
 | ||||
| 	function prn_to_sheet(str/*:string*/, opts)/*:Worksheet*/ { return aoa_to_sheet(prn_to_aoa(str, opts), opts); } | ||||
| 
 | ||||
| 	function prn_to_workbook(str/*:string*/, opts)/*:Workbook*/ { return sheet_to_workbook(prn_to_sheet(str, opts), opts); } | ||||
| 
 | ||||
| 	return { | ||||
| 		to_workbook: prn_to_workbook, | ||||
| 		to_sheet: prn_to_sheet | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
|  | ||||
| @ -61,7 +61,7 @@ function cycle_width(collw) { return char2width(px2char(width2px(collw))); } | ||||
| /* XLSX/XLSB/XLS specify width in units of MDW */ | ||||
| function find_mdw_colw(collw) { | ||||
| 	var delta = Infinity, _MDW = MIN_MDW; | ||||
| 	for(MDW=MIN_MDW; MDW<MAX_MDW; ++MDW) if(Math.abs(collw - cycle_width(collw)) < delta) { delta = Math.abs(collw - cycle_width(collw)); _MDW = MDW; } | ||||
| 	for(MDW=MIN_MDW; MDW<MAX_MDW; ++MDW) if(Math.abs(collw - cycle_width(collw)) <= delta) { delta = Math.abs(collw - cycle_width(collw)); _MDW = MDW; } | ||||
| 	MDW = _MDW; | ||||
| } | ||||
| /* XLML specifies width in terms of pixels */ | ||||
|  | ||||
							
								
								
									
										43
									
								
								bits/55_vml.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										43
									
								
								bits/55_vml.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| /* L.5.5.2 SpreadsheetML Comments + VML Schema */ | ||||
| var _shapeid = 1024; | ||||
| function write_comments_vml(rId, comments) { | ||||
| 	var csize = [21600, 21600]; | ||||
| 	/* L.5.2.1.2 Path Attribute */ | ||||
| 	var bbox = ["m0,0l0",csize[1],csize[0],csize[1],csize[0],"0xe"].join(","); | ||||
| 	var o = [ | ||||
| 		writextag("xml", null, { 'xmlns:v': XLMLNS.v, 'xmlns:o': XLMLNS.o, 'xmlns:x': XLMLNS.x, 'xmlns:mv': XLMLNS.mv }).replace(/\/>/,">"), | ||||
| 		writextag("o:shapelayout", writextag("o:idmap", null, {'v:ext':"edit", 'data':rId}), {'v:ext':"edit"}), | ||||
| 		writextag("v:shapetype", [ | ||||
| 			writextag("v:stroke", null, {joinstyle:"miter"}), | ||||
| 			writextag("v:path", null, {gradientshapeok:"t", 'o:connecttype':"rect"}) | ||||
| 		].join(""), {id:"_x0000_t202", 'o:spt':202, coordsize:csize.join(","),path:bbox}) | ||||
| 	]; | ||||
| 	while(_shapeid < rId * 1000) _shapeid += 1000; | ||||
| 
 | ||||
| 	comments.map(function(x) { return decode_cell(x[0]); }).forEach(function(c,i) { o = o.concat([ | ||||
| 	'<v:shape' + wxt_helper({ | ||||
| 		id:'_x0000_s' + (++_shapeid), | ||||
| 		type:"#_x0000_t202", | ||||
| 		style:"position:absolute; margin-left:80pt;margin-top:5pt;width:104pt;height:64pt;z-index:10;visibility:hidden", | ||||
| 		fillcolor:"#ECFAD4", | ||||
| 		strokecolor:"#edeaa1" | ||||
| 	}) + '>', | ||||
| 		writextag('v:fill', writextag("o:fill", null, {type:"gradientUnscaled", 'v:ext':"view"}), {'color2':"#BEFF82", 'angle':"-180", 'type':"gradient"}), | ||||
| 		writextag("v:shadow", null, {on:"t", 'obscured':"t"}), | ||||
| 		writextag("v:path", null, {'o:connecttype':"none"}), | ||||
| 		'<v:textbox><div style="text-align:left"></div></v:textbox>', | ||||
| 		'<x:ClientData ObjectType="Note">', | ||||
| 			'<x:MoveWithCells/>', | ||||
| 			'<x:SizeWithCells/>', | ||||
| 			/* Part 4 19.4.2.3 Anchor (Anchor) */ | ||||
| 			writetag('x:Anchor', [c.c, 0, c.r, 0, c.c+3, 100, c.r+5, 100].join(",")), | ||||
| 			writetag('x:AutoFill', "False"), | ||||
| 			writetag('x:Row', String(c.r)), | ||||
| 			writetag('x:Column', String(c.c)), | ||||
| 		'</x:ClientData>', | ||||
| 	'</v:shape>' | ||||
| 	]); }); | ||||
| 	o.push('</xml>'); | ||||
| 	return o.join(""); | ||||
| } | ||||
| 
 | ||||
| @ -1,3 +1,4 @@ | ||||
| RELS.CMNT = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"; | ||||
| 
 | ||||
| function parse_comments(zip, dirComments, sheets, sheetRels, opts) { | ||||
| 	for(var i = 0; i != dirComments.length; ++i) { | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| /* 18.7.3 CT_Comment */ | ||||
| /* 18.7 Comments */ | ||||
| function parse_comments_xml(data/*:string*/, opts)/*:Array<Comment>*/ { | ||||
| 	/* 18.7.6 CT_Comments */ | ||||
| 	if(data.match(/<(?:\w+:)?comments *\/>/)) return []; | ||||
| 	var authors = []; | ||||
| 	var commentList = []; | ||||
| @ -15,19 +16,43 @@ function parse_comments_xml(data/*:string*/, opts)/*:Array<Comment>*/ { | ||||
| 		var cm = x.match(/<(?:\w+:)?comment[^>]*>/); | ||||
| 		if(!cm) return; | ||||
| 		var y = parsexmltag(cm[0]); | ||||
| 		var comment/*:Comment*/ = ({ author: y.authorId && authors[y.authorId] ? authors[y.authorId] : undefined, ref: y.ref, guid: y.guid }/*:any*/); | ||||
| 		var comment/*:Comment*/ = ({ author: y.authorId && authors[y.authorId] ? authors[y.authorId] : "sheetjsghost", ref: y.ref, guid: y.guid }/*:any*/); | ||||
| 		var cell = decode_cell(y.ref); | ||||
| 		if(opts.sheetRows && opts.sheetRows <= cell.r) return; | ||||
| 		var textMatch = x.match(/<(?:\w+:)?text>([^\u2603]*)<\/(?:\w+:)?text>/); | ||||
| 		if (!textMatch || !textMatch[1]) return; // a comment may contain an empty text tag.
 | ||||
| 		var rt = parse_si(textMatch[1]); | ||||
| 		if(!rt) return; | ||||
| 		var rt = (!textMatch || !textMatch[1]) ? {r:"",t:"",h:""} : parse_si(textMatch[1]); | ||||
| 		comment.r = rt.r; | ||||
| 		comment.t = rt.t; | ||||
| 		comment.t = rt.t.replace(/\r\n/g,"\n").replace(/\r/g,"\n"); | ||||
| 		if(opts.cellHTML) comment.h = rt.h; | ||||
| 		commentList.push(comment); | ||||
| 	}); | ||||
| 	return commentList; | ||||
| } | ||||
| 
 | ||||
| function write_comments_xml(data, opts) { } | ||||
| var CMNT_XML_ROOT = writextag('comments', null, { 'xmlns': XMLNS.main[0] }); | ||||
| function write_comments_xml(data, opts) { | ||||
| 	var o = [XML_HEADER, CMNT_XML_ROOT]; | ||||
| 
 | ||||
| 	var iauthor = []; | ||||
| 	o.push("<authors>"); | ||||
| 	data.map(function(x) { return x[1]; }).forEach(function(comment) { | ||||
| 		comment.map(function(x) { return escapexml(x.a); }).forEach(function(a) { | ||||
| 			if(iauthor.indexOf(a) > -1) return; | ||||
| 			iauthor.push(a); | ||||
| 			o.push("<author>" + a + "</author>"); | ||||
| 		}); | ||||
| 	}); | ||||
| 	o.push("</authors>"); | ||||
| 	o.push("<commentList>"); | ||||
| 	data.forEach(function(d) { | ||||
| 		d[1].forEach(function(c) { | ||||
| 			/* 18.7.3 CT_Comment */ | ||||
| 			o.push('<comment ref="' + d[0] + '" authorId="' + iauthor.indexOf(escapexml(c.a)) + '"><text>'); | ||||
| 			o.push(writetag("t", c.t)); | ||||
| 			o.push('</text></comment>'); | ||||
| 		}); | ||||
| 	}); | ||||
| 	o.push("</commentList>"); | ||||
| 	if(o.length>2) { o[o.length] = ('</comments>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| } | ||||
|  | ||||
| @ -8,6 +8,16 @@ function parse_BrtBeginComment(data, length) { | ||||
| 	data.l += 16; /*var guid = parse_GUID(data); */ | ||||
| 	return out; | ||||
| } | ||||
| function write_BrtBeginComment(data, o) { | ||||
| 	if(o == null) o = new_buf(36); | ||||
| 	o.write_shift(4, data[1].iauthor); | ||||
| 	write_UncheckedRfX(data[0], o); | ||||
| 	o.write_shift(4, 0); | ||||
| 	o.write_shift(4, 0); | ||||
| 	o.write_shift(4, 0); | ||||
| 	o.write_shift(4, 0); | ||||
| 	return o; | ||||
| } | ||||
| 
 | ||||
| /* [MS-XLSB] 2.4.324 BrtCommentAuthor */ | ||||
| var parse_BrtCommentAuthor = parse_XLWideString; | ||||
| @ -30,6 +40,7 @@ function parse_comments_bin(data, opts) { | ||||
| 				c.author = authors[c.iauthor]; | ||||
| 				delete c.iauthor; | ||||
| 				if(opts.sheetRows && opts.sheetRows <= c.rfx.r) break; | ||||
| 				if(!c.t) c.t = ""; | ||||
| 				delete c.rfx; out.push(c); break; | ||||
| 			case 'BrtBeginComments': break; | ||||
| 			case 'BrtEndComments': break; | ||||
| @ -43,4 +54,35 @@ function parse_comments_bin(data, opts) { | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| function write_comments_bin(data, opts) { } | ||||
| function write_comments_bin(data, opts) { | ||||
| 	var ba = buf_array(); | ||||
| 	var iauthor = []; | ||||
| 	write_record(ba, "BrtBeginComments"); | ||||
| 	{ /* COMMENTAUTHORS */ | ||||
| 		write_record(ba, "BrtBeginCommentAuthors"); | ||||
| 		data.forEach(function(comment) { | ||||
| 			comment[1].forEach(function(c) { | ||||
| 				if(iauthor.indexOf(c.a) > -1) return; | ||||
| 				iauthor.push(c.a.substr(0,54)); | ||||
| 				write_record(ba, "BrtCommentAuthor", write_XLWideString(c.a.substr(0, 54))); | ||||
| 			}); | ||||
| 		}); | ||||
| 		write_record(ba, "BrtEndCommentAuthors"); | ||||
| 	} | ||||
| 	{ /* COMMENTLIST */ | ||||
| 		write_record(ba, "BrtBeginCommentList"); | ||||
| 		data.forEach(function(comment) { | ||||
| 			comment[1].forEach(function(c) { | ||||
| 				c.iauthor = iauthor.indexOf(c.a); | ||||
| 				var range = {s:decode_cell(comment[0])}; range.e = range.s; | ||||
| 				write_record(ba, "BrtBeginComment", write_BrtBeginComment([range, c])); | ||||
| 				if(c.t && c.t.length > 0) write_record(ba, "BrtCommentText", write_RichStr(c)); | ||||
| 				write_record(ba, "BrtEndComment"); | ||||
| 				delete c.iauthor; | ||||
| 			}); | ||||
| 		}); | ||||
| 		write_record(ba, "BrtEndCommentList"); | ||||
| 	} | ||||
| 	write_record(ba, "BrtEndComments"); | ||||
| 	return ba.end(); | ||||
| } | ||||
|  | ||||
| @ -20,6 +20,7 @@ function col_obj_w(C/*:number*/, col) { | ||||
| 	else if(col.wpx != null) width = px2char(col.wpx); | ||||
| 	else if(col.wch != null) width = col.wch; | ||||
| 	if(width > -1) { p.width = char2width(width); p.customWidth = 1; } | ||||
| 	else p.width = col.width; | ||||
| 	return p; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -156,6 +156,7 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) { | ||||
| 		v = writextag('f', escapexml(cell.f), ff) + (cell.v != null ? v : ""); | ||||
| 	} | ||||
| 	if(cell.l) ws['!links'].push([ref, cell.l]); | ||||
| 	if(cell.c) ws['!comments'].push([ref, cell.c]); | ||||
| 	return writextag('c', v, o); | ||||
| } | ||||
| 
 | ||||
| @ -330,6 +331,7 @@ function write_ws_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ { | ||||
| 	if(ws === undefined) ws = {}; | ||||
| 	var ref = ws['!ref']; if(ref === undefined) ref = 'A1'; | ||||
| 	if(!rels) rels = {}; | ||||
| 	ws['!comments'] = []; | ||||
| 
 | ||||
| 	o[o.length] = (writextag('sheetPr', null, {'codeName': escapexml(wb.SheetNames[idx])})); | ||||
| 	o[o.length] = (writextag('dimension', null, {'ref': ref})); | ||||
| @ -348,12 +350,12 @@ function write_ws_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ { | ||||
| 
 | ||||
| 	if(ws['!merges'] != null && ws['!merges'].length > 0) o[o.length] = (write_ws_xml_merges(ws['!merges'])); | ||||
| 
 | ||||
| 	var relc = -1, rel; | ||||
| 	var relc = -1, rel, rId = -1; | ||||
| 	if(ws['!links'].length > 0) { | ||||
| 		o[o.length] = "<hyperlinks>"; | ||||
| 		ws['!links'].forEach(function(l) { | ||||
| 			if(!l[1].Target) return; | ||||
| 			var rId = add_rels(rels, -1, escapexml(l[1].Target).replace(/#.*$/, ""), RELS.HLINK); | ||||
| 			rId = add_rels(rels, -1, escapexml(l[1].Target).replace(/#.*$/, ""), RELS.HLINK); | ||||
| 			rel = ({"ref":l[0], "r:id":"rId"+rId}/*:any*/); | ||||
| 			if((relc = l[1].Target.indexOf("#")) > -1) rel.location = escapexml(l[1].Target.substr(relc+1)); | ||||
| 			if(l[1].Tooltip) rel.tooltip = escapexml(l[1].Tooltip); | ||||
| @ -362,6 +364,14 @@ function write_ws_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ { | ||||
| 		o[o.length] = "</hyperlinks>"; | ||||
| 	} | ||||
| 	delete ws['!links']; | ||||
| 
 | ||||
| 	if(ws['!comments'].length > 0) { | ||||
| 		rId = add_rels(rels, -1, "../drawings/vmlDrawing" + (idx+1) + ".vml", RELS.VML); | ||||
| 		o[o.length] = writextag("legacyDrawing", null, {"r:id":"rId" + rId}); | ||||
| 		ws['!legacy'] = rId; | ||||
| 	} | ||||
| // <legacyDrawing r:id="rId1"/>
 | ||||
| 
 | ||||
| 	if(o.length>2) { o[o.length] = ('</worksheet>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| } | ||||
|  | ||||
| @ -570,6 +570,7 @@ function write_ws_bin_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:num | ||||
| 	/* TODO: cell style */ | ||||
| 	//o.s = get_cell_style(opts.cellXfs, cell, opts);
 | ||||
| 	if(cell.l) ws['!links'].push([encode_cell(o), cell.l]); | ||||
| 	if(cell.c) ws['!comments'].push([encode_cell(o), cell.c]); | ||||
| 	switch(cell.t) { | ||||
| 		case 's': case 'str': | ||||
| 			if(opts.bookSST) { | ||||
| @ -639,12 +640,22 @@ function write_HLINKS(ba, ws/*:Worksheet*/, rels) { | ||||
| 	}); | ||||
| 	delete ws['!links']; | ||||
| } | ||||
| function write_LEGACYDRAWING(ba, ws/*:Worksheet*/, idx/*:number*/, rels) { | ||||
| 	/* [BrtLegacyDrawing] */ | ||||
| 	if(ws['!comments'].length > 0) { | ||||
| 		var rId = add_rels(rels, -1, "../drawings/vmlDrawing" + (idx+1) + ".vml", RELS.VML); | ||||
| 		write_record(ba, "BrtLegacyDrawing", write_RelID("rId" + rId)); | ||||
| 		ws['!legacy'] = rId; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| function write_ws_bin(idx/*:number*/, opts, wb/*:Workbook*/, rels) { | ||||
| 	var ba = buf_array(); | ||||
| 	var s = wb.SheetNames[idx], ws = wb.Sheets[s] || {}; | ||||
| 	var r = safe_decode_range(ws['!ref'] || "A1"); | ||||
| 	ws['!links'] = []; | ||||
| 	/* passed back to write_zip and removed there */ | ||||
| 	ws['!comments'] = []; | ||||
| 	write_record(ba, "BrtBeginSheet"); | ||||
| 	write_record(ba, "BrtWsProp", write_BrtWsProp(s)); | ||||
| 	write_record(ba, "BrtWsDim", write_BrtWsDim(r)); | ||||
| @ -676,7 +687,7 @@ function write_ws_bin(idx/*:number*/, opts, wb/*:Workbook*/, rels) { | ||||
| 	/* [IGNOREECS] */ | ||||
| 	/* [SMARTTAGS] */ | ||||
| 	/* [BrtDrawing] */ | ||||
| 	/* [BrtLegacyDrawing] */ | ||||
| 	write_LEGACYDRAWING(ba, ws, idx, rels); | ||||
| 	/* [BrtLegacyDrawingHF] */ | ||||
| 	/* [BrtBkHim] */ | ||||
| 	/* [OLEOBJECTS] */ | ||||
|  | ||||
| @ -62,11 +62,11 @@ function write_sty(data, name/*:string*/, opts) { | ||||
| function write_sst(data/*:SST*/, name/*:string*/, opts) { | ||||
| 	return (name.slice(-4)===".bin" ? write_sst_bin : write_sst_xml)(data, opts); | ||||
| } | ||||
| /* | ||||
| function write_cmnt(data, name:string, opts) { | ||||
| 
 | ||||
| function write_cmnt(data/*:Array<any>*/, name/*:string*/, opts) { | ||||
| 	return (name.slice(-4)===".bin" ? write_comments_bin : write_comments_xml)(data, opts); | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| function write_cc(data, name:string, opts) { | ||||
| 	return (name.slice(-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts); | ||||
| } | ||||
|  | ||||
| @ -120,7 +120,7 @@ function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, a | ||||
| 			if(!cell.t) cell.t = 'n'; | ||||
| 			break; | ||||
| 		case 'Error': cell.t = 'e'; cell.v = RBErr[xml]; cell.w = xml; break; | ||||
| 		default: cell.t = 's'; cell.v = xlml_fixstr(ss); break; | ||||
| 		default: cell.t = 's'; cell.v = xlml_fixstr(ss||xml); break; | ||||
| 	} | ||||
| 	safe_format_xlml(cell, nf, o); | ||||
| 	if(o.cellFormula != null) { | ||||
| @ -129,7 +129,7 @@ function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, a | ||||
| 			/* strictly speaking, the leading = is required but some writers omit */ | ||||
| 			if(fstr.charCodeAt(0) == 61 /* = */) fstr = fstr.substr(1); | ||||
| 			cell.f = rc_to_a1(fstr, base); | ||||
| 			cell.Formula = undefined; | ||||
| 			delete cell.Formula; | ||||
| 			if(cell.ArrayRange == "RC") cell.F = rc_to_a1("RC:RC", base); | ||||
| 			else if(cell.ArrayRange) { | ||||
| 				cell.F = rc_to_a1(cell.ArrayRange, base); | ||||
| @ -152,7 +152,8 @@ function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, a | ||||
| } | ||||
| 
 | ||||
| function xlml_clean_comment(comment/*:any*/) { | ||||
| 	comment.t = comment.v; | ||||
| 	comment.t = comment.v || ""; | ||||
| 	comment.t = comment.t.replace(/\r\n/g,"\n").replace(/\r/g,"\n"); | ||||
| 	comment.v = comment.w = comment.ixfe = undefined; | ||||
| } | ||||
| 
 | ||||
| @ -803,7 +804,14 @@ function write_ws_xlml_wsopts(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workb | ||||
| 	if(o.length == 0) return ""; | ||||
| 	return writextag("WorksheetOptions", o.join(""), {xmlns:XLMLNS.x}); | ||||
| } | ||||
| /* TODO */ | ||||
| function write_ws_xlml_comment(comments) { | ||||
| 	return comments.map(function(c) { | ||||
| 		// TODO: formatted text
 | ||||
| 		var t = xlml_unfixstr(c.t||""); | ||||
| 		var d =writextag("ss:Data", t, {"xmlns":"http://www.w3.org/TR/REC-html40"}); | ||||
| 		return writextag("Comment", d, {"ss:Author":c.a}); | ||||
| 	}).join(""); | ||||
| } | ||||
| function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr)/*:string*/{ | ||||
| 	if(!cell || cell.v == undefined && cell.f == undefined) return "<Cell></Cell>"; | ||||
| 
 | ||||
| @ -846,6 +854,8 @@ function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr)/*:string*/{ | ||||
| 	} | ||||
| 	var m = '<Data ss:Type="' + t + '">' + _v + '</Data>'; | ||||
| 
 | ||||
| 	if((cell.c||[]).length > 0) m += write_ws_xlml_comment(cell.c); | ||||
| 
 | ||||
| 	return writextag("Cell", m, attr); | ||||
| } | ||||
| /* TODO */ | ||||
|  | ||||
| @ -58,11 +58,12 @@ function write_BIFF2LABEL(r, c, val) { | ||||
| 
 | ||||
| function write_ws_biff_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts) { | ||||
| 	if(cell.v != null) switch(cell.t) { | ||||
| 		case 'n': | ||||
| 			if((cell.v == (cell.v|0)) && (cell.v >= 0) && (cell.v < 65536)) | ||||
| 				write_biff_rec(ba, 0x0002, write_BIFF2INT(R, C, cell.v)); | ||||
| 		case 'd': case 'n': | ||||
| 			var v = cell.t == 'd' ? datenum(cell.v) : cell.v; | ||||
| 			if((v == (v|0)) && (v >= 0) && (v < 65536)) | ||||
| 				write_biff_rec(ba, 0x0002, write_BIFF2INT(R, C, v)); | ||||
| 			else | ||||
| 				write_biff_rec(ba, 0x0003, write_BIFF2NUMBER(R,C, cell.v)); | ||||
| 				write_biff_rec(ba, 0x0003, write_BIFF2NUMBER(R,C, v)); | ||||
| 			return; | ||||
| 		case 'b': case 'e': write_biff_rec(ba, 0x0005, write_BIFF2BERR(R, C, cell.v, cell.t)); return; | ||||
| 		/* TODO: codepage, sst */ | ||||
|  | ||||
| @ -32,6 +32,8 @@ var parse_content_xml = (function() { | ||||
| 		var number_format_map = {}; | ||||
| 		var merges = [], mrange = {}, mR = 0, mC = 0; | ||||
| 		var arrayf = []; | ||||
| 		var comments = [], comment = {}; | ||||
| 		var creator = "", creatoridx = 0; | ||||
| 		var rept = 1, isstub = false; | ||||
| 		var i = 0; | ||||
| 		xlmlregex.lastIndex = 0; | ||||
| @ -77,6 +79,7 @@ var parse_content_xml = (function() { | ||||
| 					if(C < range.s.c) range.s.c = C; | ||||
| 					if(R < range.s.r) range.s.r = R; | ||||
| 					ctag = parsexmltag(Rn[0], false); | ||||
| 					comments = []; comment = {}; | ||||
| 					q = ({t:ctag['数据类型'] || ctag['value-type'], v:null/*:: , z:null, w:""*/}/*:any*/); | ||||
| 					if(opts.cellFormula) { | ||||
| 						if(ctag.formula) ctag.formula = unescapexml(ctag.formula); | ||||
| @ -126,6 +129,7 @@ var parse_content_xml = (function() { | ||||
| 						q.v = textp || ''; | ||||
| 						isstub = textpidx == 0; | ||||
| 					} | ||||
| 					if(comments.length > 0) { q.c = comments; comments = []; } | ||||
| 					if(textp) q.w = textp; | ||||
| 					if(!isstub || opts.sheetStubs) { | ||||
| 						if(!(opts.sheetRows && opts.sheetRows < R)) { | ||||
| @ -150,6 +154,23 @@ var parse_content_xml = (function() { | ||||
| 				else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], true]); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 'annotation': // 14.1 <office:annotation>
 | ||||
| 				if(Rn[1]==='/'){ | ||||
| 					if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp; | ||||
| 					comment.t = textp; | ||||
| 					comment.a = creator; | ||||
| 					comments.push(comment); | ||||
| 				} | ||||
| 				else if(Rn[0].charAt(Rn[0].length-2) !== '/') {state.push([Rn[3], false]);} | ||||
| 				creator = ""; creatorpidx = 0; | ||||
| 				textp = ""; textpidx = 0; | ||||
| 				break; | ||||
| 
 | ||||
| 			case 'creator': // 4.3.2.7 <dc:creator>
 | ||||
| 				if(Rn[1]==='/') { creator = str.slice(creatoridx,Rn.index); } | ||||
| 				else creatoridx = Rn.index + Rn[0].length; | ||||
| 				break; | ||||
| 
 | ||||
| 			/* ignore state */ | ||||
| 			case 'meta': case '元数据': // TODO: <office:meta> <uof:元数据> FODS/UOF
 | ||||
| 			case 'settings': // TODO: <office:settings>
 | ||||
| @ -165,7 +186,6 @@ var parse_content_xml = (function() { | ||||
| 			case 'list-style': // 16.30 <text:list-style>
 | ||||
| 			case 'form': // 13.13 <form:form>
 | ||||
| 			case 'dde-links': // 9.8 <table:dde-links>
 | ||||
| 			case 'annotation': // 14.1 <office:annotation>
 | ||||
| 			case 'event-listeners': // TODO
 | ||||
| 				if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;} | ||||
| 				else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], false]); | ||||
| @ -272,7 +292,7 @@ var parse_content_xml = (function() { | ||||
| 			case 'span': break; // <text:span>
 | ||||
| 			case 'line-break': break; // 6.1.5 <text:line-break>
 | ||||
| 			case 'p': case '文本串': | ||||
| 				if(Rn[1]==='/') textp = parse_text_p(str.slice(textpidx,Rn.index), textptag); | ||||
| 				if(Rn[1]==='/') textp = (textp.length > 0 ? textp + "\n" : "") + parse_text_p(str.slice(textpidx,Rn.index), textptag); | ||||
| 				else { textptag = parsexmltag(Rn[0], false); textpidx = Rn.index + Rn[0].length; } | ||||
| 				break; // <text:p>
 | ||||
| 			case 's': break; // <text:s>
 | ||||
| @ -326,7 +346,6 @@ var parse_content_xml = (function() { | ||||
| 			case 'event-listener': // TODO
 | ||||
| 			/* TODO: FODS Properties */ | ||||
| 			case 'initial-creator': | ||||
| 			case 'creator': | ||||
| 			case 'creation-date': | ||||
| 			case 'generator': | ||||
| 			case 'document-statistic': | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ { | ||||
| 	_shapeid = 1024; | ||||
| 	if(opts.bookType == "ods") return write_ods(wb, opts); | ||||
| 	if(wb && !wb.SSF) { | ||||
| 		wb.SSF = SSF.get_table(); | ||||
| @ -57,8 +58,26 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ { | ||||
| 		f = "xl/worksheets/sheet" + rId + "." + wbext; | ||||
| 		var wsrels = {'!id':{}}; | ||||
| 		zip.file(f, write_ws(rId-1, f, opts, wb, wsrels)); | ||||
| 
 | ||||
| 		ct.sheets.push(f); | ||||
| 		add_rels(opts.wbrels, rId, "worksheets/sheet" + rId + "." + wbext, RELS.WS[0]); | ||||
| 		add_rels(opts.wbrels, -1, "worksheets/sheet" + rId + "." + wbext, RELS.WS[0]); | ||||
| 
 | ||||
| 		var ws = wb.Sheets[wb.SheetNames[rId-1]]; | ||||
| 		if(ws) { | ||||
| 			var comments = ws['!comments']; | ||||
| 			if(comments && comments.length > 0) { | ||||
| 				var cf = "xl/comments" + rId + "." + wbext; | ||||
| 				zip.file(cf, write_cmnt(comments, cf, opts)); | ||||
| 				ct.comments.push(cf); | ||||
| 				add_rels(wsrels, -1, "../comments" + rId + "." + wbext, RELS.CMNT); | ||||
| 			} | ||||
| 			if(ws['!legacy']) { | ||||
| 				zip.file("xl/drawings/vmlDrawing" + (rId) + ".vml", write_comments_vml(rId, ws['!comments'])); | ||||
| 			} | ||||
| 			delete ws['!comments']; | ||||
| 			delete ws['!legacy']; | ||||
| 		} | ||||
| 
 | ||||
| 		if(wsrels['!id'].rId1) zip.file(get_rels_path(f), write_rels(wsrels)); // get_rels_path('')
 | ||||
| 	} | ||||
| 
 | ||||
| @ -66,7 +85,7 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ { | ||||
| 		f = "xl/sharedStrings." + wbext; | ||||
| 		zip.file(f, write_sst(opts.Strings, f, opts)); | ||||
| 		ct.strs.push(f); | ||||
| 		add_rels(opts.wbrels, ++rId, "sharedStrings." + wbext, RELS.SST); | ||||
| 		add_rels(opts.wbrels, -1, "sharedStrings." + wbext, RELS.SST); | ||||
| 	} | ||||
| 
 | ||||
| 	/* TODO: something more intelligent with themes */ | ||||
| @ -74,20 +93,20 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ { | ||||
| 	f = "xl/theme/theme1.xml"; | ||||
| 	zip.file(f, write_theme(wb.Themes, opts)); | ||||
| 	ct.themes.push(f); | ||||
| 	add_rels(opts.wbrels, ++rId, "theme/theme1.xml", RELS.THEME); | ||||
| 	add_rels(opts.wbrels, -1, "theme/theme1.xml", RELS.THEME); | ||||
| 
 | ||||
| 	/* TODO: something more intelligent with styles */ | ||||
| 
 | ||||
| 	f = "xl/styles." + wbext; | ||||
| 	zip.file(f, write_sty(wb, f, opts)); | ||||
| 	ct.styles.push(f); | ||||
| 	add_rels(opts.wbrels, ++rId, "styles." + wbext, RELS.STY); | ||||
| 	add_rels(opts.wbrels, -1, "styles." + wbext, RELS.STY); | ||||
| 
 | ||||
| 	if(wb.vbaraw && vbafmt) { | ||||
| 		f = "xl/vbaProject.bin"; | ||||
| 		zip.file(f, wb.vbaraw); | ||||
| 		ct.vba.push(f); | ||||
| 		add_rels(opts.wbrels, ++rId, "vbaProject.bin", RELS.VBA); | ||||
| 		add_rels(opts.wbrels, -1, "vbaProject.bin", RELS.VBA); | ||||
| 	} | ||||
| 
 | ||||
| 	zip.file("[Content_Types].xml", write_ct(ct, opts)); | ||||
|  | ||||
| @ -45,7 +45,7 @@ function write_binary_type(out, opts/*:WriteOpts*/) { | ||||
| function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) { | ||||
| 	check_wb(wb); | ||||
| 	var o = opts||{}; | ||||
| 	switch(o.bookType || 'xlsx') { | ||||
| 	switch(o.bookType || 'xlsb') { | ||||
| 		case 'xml': | ||||
| 		case 'xlml': return write_string_type(write_xlml(wb, o), o); | ||||
| 		case 'slk': | ||||
| @ -63,21 +63,20 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) { | ||||
| } | ||||
| 
 | ||||
| function resolve_book_type(o/*?WriteFileOpts*/) { | ||||
| 	if(!o.bookType) switch(o.file.slice(-5).toLowerCase()) { | ||||
| 	if(!o.bookType) switch(o.file.slice(o.file.lastIndexOf(".")).toLowerCase()) { | ||||
| 		case '.xlsx': o.bookType = 'xlsx'; break; | ||||
| 		case '.xlsm': o.bookType = 'xlsm'; break; | ||||
| 		case '.xlsb': o.bookType = 'xlsb'; break; | ||||
| 		case '.fods': o.bookType = 'fods'; break; | ||||
| 		case '.xlml': o.bookType = 'xlml'; break; | ||||
| 		case '.sylk': o.bookType = 'sylk'; break; | ||||
| 	default: switch(o.file.slice(-4).toLowerCase()) { | ||||
| 		case '.xls': o.bookType = 'biff2'; break; | ||||
| 		case '.xml': o.bookType = 'xml'; break; | ||||
| 		case '.ods': o.bookType = 'ods'; break; | ||||
| 		case '.csv': o.bookType = 'csv'; break; | ||||
| 		case '.dif': o.bookType = 'dif'; break; | ||||
| 		case '.slk': o.bookType = 'sylk'; break; | ||||
| 	}} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| function writeFileSync(wb/*:Workbook*/, filename/*:string*/, opts/*:?WriteFileOpts*/) { | ||||
|  | ||||
							
								
								
									
										17
									
								
								docbits/66_comments.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										17
									
								
								docbits/66_comments.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| #### Cell Comments | ||||
| 
 | ||||
| Cell comments are objects stored in the `c` array of cell objects.  The actual | ||||
| contents of the comment are split into blocks based on the comment author.  The | ||||
| `a` field of each comment object is the author of the comment and the `t` field | ||||
| is the plaintext representation. | ||||
| 
 | ||||
| For example, the following snippet appends a cell comment into cell `A1`: | ||||
| 
 | ||||
| ```js | ||||
| if(!ws.A1.c) ws.A1.c = []; | ||||
| ws.A1.c.push({a:"SheetJS", t:"I'm a little comment, short and stout!"}); | ||||
| ``` | ||||
| 
 | ||||
| Note: XLSB enforces a 54 character limit on the Author name.  Names longer than | ||||
| 54 characters may cause issues with other formats. | ||||
| 
 | ||||
| @ -22,7 +22,7 @@ With <https://rawgit.com/SheetJS/test_files/master/sheet_visibility.xlsx>: | ||||
| ``` | ||||
| 
 | ||||
| Non-Excel formats do not support the Very Hidden state.  The best way to test | ||||
| if a sheet is visible is to check if the `Hidden` property is logical negation: | ||||
| if a sheet is visible is to check if the `Hidden` property is logical truth: | ||||
| 
 | ||||
| ```js | ||||
| > wb.Workbook.Sheets.map(function(x) { return [x.name, !x.Hidden] }) | ||||
|  | ||||
| @ -67,7 +67,7 @@ file but Excel will know how to handle it.  This library applies similar logic: | ||||
| | `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` | UTF8 Text     | SpreadsheetML or Flat ODS or UOS1 or plaintext      | | ||||
| | `0xFE` | UTF16 Encoded | SpreadsheetML or Flat ODS or UOS1 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) | ||||
|  | ||||
| @ -26,6 +26,7 @@ | ||||
|     + [Formulae](README.md#formulae) | ||||
|     + [Column Properties](README.md#column-properties) | ||||
|     + [Hyperlinks](README.md#hyperlinks) | ||||
|     + [Cell Comments](README.md#cell-comments) | ||||
|     + [Sheet Visibility](README.md#sheet-visibility) | ||||
| - [Parsing Options](README.md#parsing-options) | ||||
|   * [Input Type](README.md#input-type) | ||||
|  | ||||
							
								
								
									
										50
									
								
								test.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										50
									
								
								test.js
									
									
									
									
									
								
							| @ -44,6 +44,7 @@ var paths = { | ||||
| 	cstxml: dir + 'comments_stress_test.xls.xml', | ||||
| 	cstxlsx: dir + 'comments_stress_test.xlsx', | ||||
| 	cstxlsb: dir + 'comments_stress_test.xlsb', | ||||
| 	cstods: dir + 'comments_stress_test.ods', | ||||
| 	fstxls: dir + 'formula_stress_test.xls', | ||||
| 	fstxml: dir + 'formula_stress_test.xls.xml', | ||||
| 	fstxlsx: dir + 'formula_stress_test.xlsx', | ||||
| @ -221,6 +222,21 @@ describe('should parse test files', function() { | ||||
| 	}); | ||||
| }); | ||||
| 
 | ||||
| /* comments_stress_test family */ | ||||
| function check_comments(wb) { | ||||
| 	var ws0 = wb.Sheets.Sheet2; | ||||
| 	assert.equal(ws0.A1.c[0].a, 'Author'); | ||||
| 	assert.equal(ws0.A1.c[0].t, 'Author:\nGod thinks this is good'); | ||||
| 	assert.equal(ws0.C1.c[0].a, 'Author'); | ||||
| 	assert.equal(ws0.C1.c[0].t, 'I really hope that xlsx decides not to use magic like rPr'); | ||||
| 
 | ||||
| 	var ws3 = wb.Sheets.Sheet4; | ||||
| 	assert.equal(ws3.B1.c[0].a, 'Author'); | ||||
| 	assert.equal(ws3.B1.c[0].t, 'The next comment is empty'); | ||||
| 	assert.equal(ws3.B2.c[0].a, 'Author'); | ||||
| 	assert.equal(ws3.B2.c[0].t, ''); | ||||
| } | ||||
| 
 | ||||
| describe('parse options', function() { | ||||
| 	var html_cell_types = ['s']; | ||||
| 	var bef = (function() { | ||||
| @ -664,12 +680,27 @@ describe('parse features', function() { | ||||
| 			[wb1,wb2,wb3,wb4].map(function(wb) { return wb.Sheets[sheet]; }).forEach(function(ws, i) { | ||||
| 				assert.equal(ws.B1.c.length, 1,"must have 1 comment"); | ||||
| 				assert.equal(ws.B1.c[0].a, "Yegor Kozlov","must have the same author"); | ||||
| 				assert.equal(ws.B1.c[0].t.replace(/\r\n/g,"\n").replace(/\r/g,"\n"), "Yegor Kozlov:\nfirst cell", "must have the concatenated texts"); | ||||
| 				assert.equal(ws.B1.c[0].t, "Yegor Kozlov:\nfirst cell", "must have the concatenated texts"); | ||||
| 				if(i > 0) return; | ||||
| 				assert.equal(ws.B1.c[0].r, '<r><rPr><b/><sz val="8"/><color indexed="81"/><rFont val="Tahoma"/></rPr><t>Yegor Kozlov:</t></r><r><rPr><sz val="8"/><color indexed="81"/><rFont val="Tahoma"/></rPr><t xml:space="preserve">\r\nfirst cell</t></r>', "must have the rich text representation"); | ||||
| 				assert.equal(ws.B1.c[0].h, '<span style="font-weight: bold;">Yegor Kozlov:</span><span style=""><br/>first cell</span>', "must have the html representation"); | ||||
| 			}); | ||||
| 		}); | ||||
| 		[ | ||||
| 			['xlsx', paths.cstxlsx], | ||||
| 			['xlsb', paths.cstxlsb], | ||||
| 			['xls', paths.cstxls], | ||||
| 			['xlml', paths.cstxml], | ||||
| 			['ods', paths.cstods] | ||||
| 		].forEach(function(m) { it(m[0] + ' stress test', function() { | ||||
| 			var wb = X.readFile(m[1]); | ||||
| 			check_comments(wb); | ||||
| 			var ws0 = wb.Sheets.Sheet2; | ||||
| 			assert.equal(ws0.A1.c[0].a, 'Author'); | ||||
| 			assert.equal(ws0.A1.c[0].t, 'Author:\nGod thinks this is good'); | ||||
| 			assert.equal(ws0.C1.c[0].a, 'Author'); | ||||
| 			assert.equal(ws0.C1.c[0].t, 'I really hope that xlsx decides not to use magic like rPr'); | ||||
| 		}); }); | ||||
| 	}); | ||||
| 
 | ||||
| 	describe('should parse core properties and custom properties', function() { | ||||
| @ -1043,6 +1074,7 @@ describe('roundtrip features', function() { | ||||
| 			}); | ||||
| 		}); | ||||
| 	}); | ||||
| 
 | ||||
| 	describe('should preserve sheet visibility', function() { [ | ||||
| 			['xlml', paths.svxml], | ||||
| 			['xlsx', paths.svxlsx], | ||||
| @ -1061,6 +1093,22 @@ describe('roundtrip features', function() { | ||||
| 			}); | ||||
| 		}); | ||||
| 	}); | ||||
| 
 | ||||
| 	describe('should preserve cell comments', function() { [ | ||||
| 			['xlsx', paths.cstxlsx], | ||||
| 			['xlsb', paths.cstxlsb], | ||||
| 			//['xls', paths.cstxlsx],
 | ||||
| 			['xlml', paths.cstxml] | ||||
| 			//['ods', paths.cstods]
 | ||||
| 	].forEach(function(w) { | ||||
| 			it(w[0], function() { | ||||
| 				var wb1 = X.readFile(w[1]); | ||||
| 				var wb2 = X.read(X.write(wb1, {bookType:w[0], type:"buffer"}), {type:"buffer"}); | ||||
| 				check_comments(wb1); | ||||
| 				check_comments(wb2); | ||||
| 			}); | ||||
| 		}); | ||||
| 	}); | ||||
| }); | ||||
| 
 | ||||
| function password_file(x){return x.match(/^password.*\.xls$/); } | ||||
|  | ||||
| @ -77,9 +77,6 @@ ws['A3'].l = { Target: "http://sheetjs.com", Tooltip: "Visit us <SheetJS.com!>" | ||||
| //ws['B2'].z = "0.0"; wb.SSF[60] = "0.0"; // Custom
 | ||||
| console.log("JSON Data: "); console.log(XLSX.utils.sheet_to_json(ws, {header:1})); | ||||
| 
 | ||||
| console.log("Worksheet Model:") | ||||
| console.log(ws); | ||||
| 
 | ||||
| /* TEST: hidden sheets */ | ||||
| wb.SheetNames.push("Hidden"); | ||||
| wb.Sheets["Hidden"] = XLSX.utils.aoa_to_sheet(["Hidden".split(""), [1,2,3]]); | ||||
| @ -100,6 +97,13 @@ wb.Props = { | ||||
| 	CreatedDate: new Date(2017,1,19) | ||||
| } | ||||
| 
 | ||||
| /* TEST: comments */ | ||||
| ws['A4'].c = []; | ||||
| ws['A4'].c.push({a:"SheetJS",t:"I'm a little comment, short and stout!"}); | ||||
| 
 | ||||
| console.log("Worksheet Model:") | ||||
| console.log(ws); | ||||
| 
 | ||||
| /* write file */ | ||||
| XLSX.writeFile(wb, 'sheetjs.xlsx', {bookSST:true}); | ||||
| XLSX.writeFile(wb, 'sheetjs.xlsm'); | ||||
|  | ||||
							
								
								
									
										339
									
								
								xlsx.flow.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										339
									
								
								xlsx.flow.js
									
									
									
									
									
								
							| @ -77,7 +77,7 @@ var Base64 = (function make_b64(){ | ||||
| 		} | ||||
| 	}; | ||||
| })(); | ||||
| var has_buf = (typeof Buffer !== 'undefined'); | ||||
| var has_buf = (typeof Buffer !== 'undefined' && typeof process !== 'undefined' && typeof process.versions !== 'undefined' && process.versions.node); | ||||
| 
 | ||||
| function new_raw_buf(len/*:number*/) { | ||||
| 	/* jshint -W056 */ | ||||
| @ -1608,6 +1608,9 @@ var xlml_fixstr/*:StringConv*/ = (function() { | ||||
| 	function entrepl($$/*:string*/,$1/*:string*/)/*:string*/ { return String.fromCharCode(parseInt($1,10)); } | ||||
| 	return function xlml_fixstr(str/*:string*/)/*:string*/ { return str.replace(entregex,entrepl); }; | ||||
| })(); | ||||
| var xlml_unfixstr/*:StringConv*/ = (function() { | ||||
| 	return function xlml_unfixstr(str/*:string*/)/*:string*/ { return str.replace(/(\r\n|[\r\n])/g,"\
"); }; | ||||
| })(); | ||||
| 
 | ||||
| function parsexmlbool(value/*:any*/, tag/*:?string*/)/*:boolean*/ { | ||||
| 	switch(value) { | ||||
| @ -1732,6 +1735,8 @@ var XLMLNS = ({ | ||||
| 	'x':    'urn:schemas-microsoft-com:office:excel', | ||||
| 	'ss':   'urn:schemas-microsoft-com:office:spreadsheet', | ||||
| 	'dt':   'uuid:C2F41010-65B3-11d1-A29F-00AA00C14882', | ||||
| 	'mv':   'http://macVmlSchemaUri', | ||||
| 	'v':    'urn:schemas-microsoft-com:vml', | ||||
| 	'html': 'http://www.w3.org/TR/REC-html40' | ||||
| }/*:any*/); | ||||
| function read_double_le(b, idx/*:number*/)/*:number*/ { | ||||
| @ -1937,7 +1942,9 @@ function recordhopper(data, cb/*:RecordHopperCB*/, opts/*:?any*/) { | ||||
| 		tmpbyte = data.read_shift(1); | ||||
| 		length = tmpbyte & 0x7F; | ||||
| 		for(cntbyte = 1; cntbyte <4 && (tmpbyte & 0x80); ++cntbyte) length += ((tmpbyte = data.read_shift(1)) & 0x7F)<<(7*cntbyte); | ||||
| 		var tgt = data.l + length; | ||||
| 		var d = R.f(data, length, opts); | ||||
| 		data.l = tgt; | ||||
| 		if(cb(d, R, RT)) return; | ||||
| 	} | ||||
| } | ||||
| @ -2130,7 +2137,7 @@ function parse_RichStr(data, length/*:number*/)/*:XLString*/ { | ||||
| 		for(var i = 0; i != dwSizeStrRun; ++i) rgsStrRun.push(parse_StrRun(data)); | ||||
| 		z.r = rgsStrRun; | ||||
| 	} | ||||
| 	else z.r = "<t>" + escapexml(str) + "</t>"; | ||||
| 	else z.r = [{ich:0, ifnt:0}]; | ||||
| 	//if((flags & 2) !== 0) { /* fExtStr */
 | ||||
| 	//	/* TODO: phonetic string */
 | ||||
| 	//}
 | ||||
| @ -2139,10 +2146,10 @@ function parse_RichStr(data, length/*:number*/)/*:XLString*/ { | ||||
| } | ||||
| function write_RichStr(str/*:XLString*/, o/*:?Block*/)/*:Block*/ { | ||||
| 	/* TODO: formatted string */ | ||||
| 	if(o == null) o = new_buf(5+2*str.t.length); | ||||
| 	var _null = false; if(o == null) { _null = true; o = new_buf(15+4*str.t.length); } | ||||
| 	o.write_shift(1,0); | ||||
| 	write_XLWideString(str.t, o); | ||||
| 	return o; | ||||
| 	return _null ? o.slice(0, o.l) : o; | ||||
| } | ||||
| 
 | ||||
| /* [MS-XLSB] 2.5.9 */ | ||||
| @ -2172,10 +2179,10 @@ function parse_XLNullableWideString(data)/*:string*/ { | ||||
| 	return cchCharacters === 0 || cchCharacters === 0xFFFFFFFF ? "" : data.read_shift(cchCharacters, 'dbcs'); | ||||
| } | ||||
| function write_XLNullableWideString(data/*:string*/, o) { | ||||
| 	if(!o) o = new_buf(127); | ||||
| 	var _null = false; if(o == null) { _null = true; o = new_buf(127); } | ||||
| 	o.write_shift(4, data.length > 0 ? data.length : 0xFFFFFFFF); | ||||
| 	if(data.length > 0) o.write_shift(0, data, 'dbcs'); | ||||
| 	return o; | ||||
| 	return _null ? o.slice(0, o.l) : o; | ||||
| } | ||||
| 
 | ||||
| /* [MS-XLSB] 2.5.168 */ | ||||
| @ -2184,10 +2191,10 @@ function parse_XLWideString(data)/*:string*/ { | ||||
| 	return cchCharacters === 0 ? "" : data.read_shift(cchCharacters, 'dbcs'); | ||||
| } | ||||
| function write_XLWideString(data/*:string*/, o) { | ||||
| 	if(o == null) o = new_buf(4+2*data.length); | ||||
| 	var _null = false; if(o == null) { _null = true; o = new_buf(4+2*data.length); } | ||||
| 	o.write_shift(4, data.length); | ||||
| 	if(data.length > 0) o.write_shift(0, data, 'dbcs'); | ||||
| 	return o; | ||||
| 	return _null ? o.slice(0, o.l) : o; | ||||
| } | ||||
| 
 | ||||
| /* [MS-XLSB] 2.5.165 */ | ||||
| @ -2580,10 +2587,6 @@ var ct2type/*{[string]:string}*/ = ({ | ||||
| 	"application/vnd.openxmlformats-officedocument.customXmlProperties+xml": "TODO", | ||||
| 	"application/vnd.openxmlformats-officedocument.spreadsheetml.customProperty": "TODO", | ||||
| 
 | ||||
| 	/* Comments */ | ||||
| 	"application/vnd.ms-excel.comments": "comments", | ||||
| 	"application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": "comments", | ||||
| 
 | ||||
| 	/* PivotTable */ | ||||
| 	"application/vnd.ms-excel.pivotTable": "TODO", | ||||
| 	"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml": "TODO", | ||||
| @ -2715,6 +2718,10 @@ var CT_LIST = (function(){ | ||||
| 			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml", | ||||
| 			xlsb: "application/vnd.ms-excel.sharedStrings" | ||||
| 		}, | ||||
| 		comments: { /* Comments */ | ||||
| 			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml", | ||||
| 			xlsb: "application/vnd.ms-excel.comments" | ||||
| 		}, | ||||
| 		sheets: { /* Worksheet */ | ||||
| 			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml", | ||||
| 			xlsb: "application/vnd.ms-excel.worksheet" | ||||
| @ -2783,6 +2790,7 @@ var CTYPE_XML_ROOT = writextag('Types', null, { | ||||
| var CTYPE_DEFAULTS = [ | ||||
| 	['xml', 'application/xml'], | ||||
| 	['bin', 'application/vnd.ms-excel.sheet.binary.macroEnabled.main'], | ||||
| 	['vml', 'application/vnd.openxmlformats-officedocument.vmlDrawing'], | ||||
| 	/* from test files */ | ||||
| 	['bmp', 'image/bmp'], | ||||
| 	['png', 'image/png'], | ||||
| @ -2833,7 +2841,7 @@ function write_ct(ct, opts)/*:string*/ { | ||||
| 	['strs', 'styles'].forEach(f1); | ||||
| 	['coreprops', 'extprops', 'custprops'].forEach(f3); | ||||
| 	f3('vba'); | ||||
| 	f3('comments'); | ||||
| 	f2('comments'); | ||||
| 	if(o.length>2){ o[o.length] = ('</Types>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| } | ||||
| @ -2842,6 +2850,7 @@ var RELS = ({ | ||||
| 	WB: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", | ||||
| 	SHEET: "http://sheetjs.openxmlformats.org/officeDocument/2006/relationships/officeDocument", | ||||
| 	HLINK: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", | ||||
| 	VML: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing", | ||||
| 	VBA: "http://schemas.microsoft.com/office/2006/relationships/vbaProject" | ||||
| }/*:any*/); | ||||
| 
 | ||||
| @ -5109,6 +5118,59 @@ var DIF = (function() { | ||||
| 		from_sheet: sheet_to_dif | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| var PRN = (function() { | ||||
| 	function prn_to_aoa(d/*:RawData*/, opts)/*:AOA*/ { | ||||
| 		switch(opts.type) { | ||||
| 			case 'base64': return prn_to_aoa_str(Base64.decode(d), opts); | ||||
| 			case 'binary': return prn_to_aoa_str(d, opts); | ||||
| 			case 'buffer': return prn_to_aoa_str(d.toString('binary'), opts); | ||||
| 			case 'array': return prn_to_aoa_str(cc2str(d), opts); | ||||
| 		} | ||||
| 		throw new Error("Unrecognized type " + opts.type); | ||||
| 	} | ||||
| 
 | ||||
| 	function set_text_arr(data/*:string*/, arr/*:AOA*/, R/*:number*/, C/*:number*/) { | ||||
| 		if(data === 'TRUE') arr[R][C] = true; | ||||
| 		else if(data === 'FALSE') arr[R][C] = false; | ||||
| 		else if(+data == +data) arr[R][C] = +data; | ||||
| 		else if(data !== "") arr[R][C] = data; | ||||
| 	} | ||||
| 
 | ||||
| 	function prn_to_aoa_str(f/*:string*/, opts)/*:AOA*/ { | ||||
| 		var arr/*:AOA*/ = ([]/*:any*/); | ||||
| 		if(!f || f.length === 0) return arr; | ||||
| 		var lines = f.split(/[\r\n]/); | ||||
| 		var L = lines.length - 1; | ||||
| 		while(L >= 0 && lines[L].length === 0) --L; | ||||
| 		var start = 10, idx = 0; | ||||
| 		var R = 0; | ||||
| 		for(; R <= L; ++R) { | ||||
| 			idx = lines[R].indexOf(" "); | ||||
| 			if(idx == -1) idx = lines[R].length; else idx++; | ||||
| 			start = Math.max(start, idx); | ||||
| 		} | ||||
| 		for(R = 0; R <= L; ++R) { | ||||
| 			arr[R] = []; | ||||
| 			/* TODO: confirm that widths are always 10 */ | ||||
| 			var C = 0; | ||||
| 			set_text_arr(lines[R].slice(0, start).trim(), arr, R, C); | ||||
| 			for(C = 1; C <= (lines[R].length - start)/10 + 1; ++C) | ||||
| 				set_text_arr(lines[R].slice(start+(C-1)*10,start+C*10).trim(),arr,R,C); | ||||
| 		} | ||||
| 		return arr; | ||||
| 	} | ||||
| 
 | ||||
| 	function prn_to_sheet(str/*:string*/, opts)/*:Worksheet*/ { return aoa_to_sheet(prn_to_aoa(str, opts), opts); } | ||||
| 
 | ||||
| 	function prn_to_workbook(str/*:string*/, opts)/*:Workbook*/ { return sheet_to_workbook(prn_to_sheet(str, opts), opts); } | ||||
| 
 | ||||
| 	return { | ||||
| 		to_workbook: prn_to_workbook, | ||||
| 		to_sheet: prn_to_sheet | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| /* 18.4.1 charset to codepage mapping */ | ||||
| var CS2CP = ({ | ||||
| 	/*::[*/0/*::]*/:    1252, /* ANSI */ | ||||
| @ -5609,7 +5671,7 @@ function cycle_width(collw) { return char2width(px2char(width2px(collw))); } | ||||
| /* XLSX/XLSB/XLS specify width in units of MDW */ | ||||
| function find_mdw_colw(collw) { | ||||
| 	var delta = Infinity, _MDW = MIN_MDW; | ||||
| 	for(MDW=MIN_MDW; MDW<MAX_MDW; ++MDW) if(Math.abs(collw - cycle_width(collw)) < delta) { delta = Math.abs(collw - cycle_width(collw)); _MDW = MDW; } | ||||
| 	for(MDW=MIN_MDW; MDW<MAX_MDW; ++MDW) if(Math.abs(collw - cycle_width(collw)) <= delta) { delta = Math.abs(collw - cycle_width(collw)); _MDW = MDW; } | ||||
| 	MDW = _MDW; | ||||
| } | ||||
| /* XLML specifies width in terms of pixels */ | ||||
| @ -6571,6 +6633,50 @@ function parse_drawing(data, rels/*:any*/) { | ||||
| 
 | ||||
| 	return rels['!id'][id].Target; | ||||
| } | ||||
| /* L.5.5.2 SpreadsheetML Comments + VML Schema */ | ||||
| var _shapeid = 1024; | ||||
| function write_comments_vml(rId, comments) { | ||||
| 	var csize = [21600, 21600]; | ||||
| 	/* L.5.2.1.2 Path Attribute */ | ||||
| 	var bbox = ["m0,0l0",csize[1],csize[0],csize[1],csize[0],"0xe"].join(","); | ||||
| 	var o = [ | ||||
| 		writextag("xml", null, { 'xmlns:v': XLMLNS.v, 'xmlns:o': XLMLNS.o, 'xmlns:x': XLMLNS.x, 'xmlns:mv': XLMLNS.mv }).replace(/\/>/,">"), | ||||
| 		writextag("o:shapelayout", writextag("o:idmap", null, {'v:ext':"edit", 'data':rId}), {'v:ext':"edit"}), | ||||
| 		writextag("v:shapetype", [ | ||||
| 			writextag("v:stroke", null, {joinstyle:"miter"}), | ||||
| 			writextag("v:path", null, {gradientshapeok:"t", 'o:connecttype':"rect"}) | ||||
| 		].join(""), {id:"_x0000_t202", 'o:spt':202, coordsize:csize.join(","),path:bbox}) | ||||
| 	]; | ||||
| 	while(_shapeid < rId * 1000) _shapeid += 1000; | ||||
| 
 | ||||
| 	comments.map(function(x) { return decode_cell(x[0]); }).forEach(function(c,i) { o = o.concat([ | ||||
| 	'<v:shape' + wxt_helper({ | ||||
| 		id:'_x0000_s' + (++_shapeid), | ||||
| 		type:"#_x0000_t202", | ||||
| 		style:"position:absolute; margin-left:80pt;margin-top:5pt;width:104pt;height:64pt;z-index:10;visibility:hidden", | ||||
| 		fillcolor:"#ECFAD4", | ||||
| 		strokecolor:"#edeaa1" | ||||
| 	}) + '>', | ||||
| 		writextag('v:fill', writextag("o:fill", null, {type:"gradientUnscaled", 'v:ext':"view"}), {'color2':"#BEFF82", 'angle':"-180", 'type':"gradient"}), | ||||
| 		writextag("v:shadow", null, {on:"t", 'obscured':"t"}), | ||||
| 		writextag("v:path", null, {'o:connecttype':"none"}), | ||||
| 		'<v:textbox><div style="text-align:left"></div></v:textbox>', | ||||
| 		'<x:ClientData ObjectType="Note">', | ||||
| 			'<x:MoveWithCells/>', | ||||
| 			'<x:SizeWithCells/>', | ||||
| 			/* Part 4 19.4.2.3 Anchor (Anchor) */ | ||||
| 			writetag('x:Anchor', [c.c, 0, c.r, 0, c.c+3, 100, c.r+5, 100].join(",")), | ||||
| 			writetag('x:AutoFill', "False"), | ||||
| 			writetag('x:Row', String(c.r)), | ||||
| 			writetag('x:Column', String(c.c)), | ||||
| 		'</x:ClientData>', | ||||
| 	'</v:shape>' | ||||
| 	]); }); | ||||
| 	o.push('</xml>'); | ||||
| 	return o.join(""); | ||||
| } | ||||
| 
 | ||||
| RELS.CMNT = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"; | ||||
| 
 | ||||
| function parse_comments(zip, dirComments, sheets, sheetRels, opts) { | ||||
| 	for(var i = 0; i != dirComments.length; ++i) { | ||||
| @ -6613,8 +6719,9 @@ function insertCommentsIntoSheet(sheetName, sheet, comments) { | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| /* 18.7.3 CT_Comment */ | ||||
| /* 18.7 Comments */ | ||||
| function parse_comments_xml(data/*:string*/, opts)/*:Array<Comment>*/ { | ||||
| 	/* 18.7.6 CT_Comments */ | ||||
| 	if(data.match(/<(?:\w+:)?comments *\/>/)) return []; | ||||
| 	var authors = []; | ||||
| 	var commentList = []; | ||||
| @ -6630,22 +6737,46 @@ function parse_comments_xml(data/*:string*/, opts)/*:Array<Comment>*/ { | ||||
| 		var cm = x.match(/<(?:\w+:)?comment[^>]*>/); | ||||
| 		if(!cm) return; | ||||
| 		var y = parsexmltag(cm[0]); | ||||
| 		var comment/*:Comment*/ = ({ author: y.authorId && authors[y.authorId] ? authors[y.authorId] : undefined, ref: y.ref, guid: y.guid }/*:any*/); | ||||
| 		var comment/*:Comment*/ = ({ author: y.authorId && authors[y.authorId] ? authors[y.authorId] : "sheetjsghost", ref: y.ref, guid: y.guid }/*:any*/); | ||||
| 		var cell = decode_cell(y.ref); | ||||
| 		if(opts.sheetRows && opts.sheetRows <= cell.r) return; | ||||
| 		var textMatch = x.match(/<(?:\w+:)?text>([^\u2603]*)<\/(?:\w+:)?text>/); | ||||
| 		if (!textMatch || !textMatch[1]) return; // a comment may contain an empty text tag.
 | ||||
| 		var rt = parse_si(textMatch[1]); | ||||
| 		if(!rt) return; | ||||
| 		var rt = (!textMatch || !textMatch[1]) ? {r:"",t:"",h:""} : parse_si(textMatch[1]); | ||||
| 		comment.r = rt.r; | ||||
| 		comment.t = rt.t; | ||||
| 		comment.t = rt.t.replace(/\r\n/g,"\n").replace(/\r/g,"\n"); | ||||
| 		if(opts.cellHTML) comment.h = rt.h; | ||||
| 		commentList.push(comment); | ||||
| 	}); | ||||
| 	return commentList; | ||||
| } | ||||
| 
 | ||||
| function write_comments_xml(data, opts) { } | ||||
| var CMNT_XML_ROOT = writextag('comments', null, { 'xmlns': XMLNS.main[0] }); | ||||
| function write_comments_xml(data, opts) { | ||||
| 	var o = [XML_HEADER, CMNT_XML_ROOT]; | ||||
| 
 | ||||
| 	var iauthor = []; | ||||
| 	o.push("<authors>"); | ||||
| 	data.map(function(x) { return x[1]; }).forEach(function(comment) { | ||||
| 		comment.map(function(x) { return escapexml(x.a); }).forEach(function(a) { | ||||
| 			if(iauthor.indexOf(a) > -1) return; | ||||
| 			iauthor.push(a); | ||||
| 			o.push("<author>" + a + "</author>"); | ||||
| 		}); | ||||
| 	}); | ||||
| 	o.push("</authors>"); | ||||
| 	o.push("<commentList>"); | ||||
| 	data.forEach(function(d) { | ||||
| 		d[1].forEach(function(c) { | ||||
| 			/* 18.7.3 CT_Comment */ | ||||
| 			o.push('<comment ref="' + d[0] + '" authorId="' + iauthor.indexOf(escapexml(c.a)) + '"><text>'); | ||||
| 			o.push(writetag("t", c.t)); | ||||
| 			o.push('</text></comment>'); | ||||
| 		}); | ||||
| 	}); | ||||
| 	o.push("</commentList>"); | ||||
| 	if(o.length>2) { o[o.length] = ('</comments>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| } | ||||
| /* [MS-XLSB] 2.4.28 BrtBeginComment */ | ||||
| function parse_BrtBeginComment(data, length) { | ||||
| 	var out = {}; | ||||
| @ -6656,6 +6787,16 @@ function parse_BrtBeginComment(data, length) { | ||||
| 	data.l += 16; /*var guid = parse_GUID(data); */ | ||||
| 	return out; | ||||
| } | ||||
| function write_BrtBeginComment(data, o) { | ||||
| 	if(o == null) o = new_buf(36); | ||||
| 	o.write_shift(4, data[1].iauthor); | ||||
| 	write_UncheckedRfX(data[0], o); | ||||
| 	o.write_shift(4, 0); | ||||
| 	o.write_shift(4, 0); | ||||
| 	o.write_shift(4, 0); | ||||
| 	o.write_shift(4, 0); | ||||
| 	return o; | ||||
| } | ||||
| 
 | ||||
| /* [MS-XLSB] 2.4.324 BrtCommentAuthor */ | ||||
| var parse_BrtCommentAuthor = parse_XLWideString; | ||||
| @ -6678,6 +6819,7 @@ function parse_comments_bin(data, opts) { | ||||
| 				c.author = authors[c.iauthor]; | ||||
| 				delete c.iauthor; | ||||
| 				if(opts.sheetRows && opts.sheetRows <= c.rfx.r) break; | ||||
| 				if(!c.t) c.t = ""; | ||||
| 				delete c.rfx; out.push(c); break; | ||||
| 			case 'BrtBeginComments': break; | ||||
| 			case 'BrtEndComments': break; | ||||
| @ -6691,7 +6833,38 @@ function parse_comments_bin(data, opts) { | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| function write_comments_bin(data, opts) { } | ||||
| function write_comments_bin(data, opts) { | ||||
| 	var ba = buf_array(); | ||||
| 	var iauthor = []; | ||||
| 	write_record(ba, "BrtBeginComments"); | ||||
| 	{ /* COMMENTAUTHORS */ | ||||
| 		write_record(ba, "BrtBeginCommentAuthors"); | ||||
| 		data.forEach(function(comment) { | ||||
| 			comment[1].forEach(function(c) { | ||||
| 				if(iauthor.indexOf(c.a) > -1) return; | ||||
| 				iauthor.push(c.a.substr(0,54)); | ||||
| 				write_record(ba, "BrtCommentAuthor", write_XLWideString(c.a.substr(0, 54))); | ||||
| 			}); | ||||
| 		}); | ||||
| 		write_record(ba, "BrtEndCommentAuthors"); | ||||
| 	} | ||||
| 	{ /* COMMENTLIST */ | ||||
| 		write_record(ba, "BrtBeginCommentList"); | ||||
| 		data.forEach(function(comment) { | ||||
| 			comment[1].forEach(function(c) { | ||||
| 				c.iauthor = iauthor.indexOf(c.a); | ||||
| 				var range = {s:decode_cell(comment[0])}; range.e = range.s; | ||||
| 				write_record(ba, "BrtBeginComment", write_BrtBeginComment([range, c])); | ||||
| 				if(c.t && c.t.length > 0) write_record(ba, "BrtCommentText", write_RichStr(c)); | ||||
| 				write_record(ba, "BrtEndComment"); | ||||
| 				delete c.iauthor; | ||||
| 			}); | ||||
| 		}); | ||||
| 		write_record(ba, "BrtEndCommentList"); | ||||
| 	} | ||||
| 	write_record(ba, "BrtEndComments"); | ||||
| 	return ba.end(); | ||||
| } | ||||
| RELS.DS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet"; | ||||
| RELS.MS = "http://schemas.microsoft.com/office/2006/relationships/xlMacrosheet"; | ||||
| 
 | ||||
| @ -9031,6 +9204,7 @@ function col_obj_w(C/*:number*/, col) { | ||||
| 	else if(col.wpx != null) width = px2char(col.wpx); | ||||
| 	else if(col.wch != null) width = col.wch; | ||||
| 	if(width > -1) { p.width = char2width(width); p.customWidth = 1; } | ||||
| 	else p.width = col.width; | ||||
| 	return p; | ||||
| } | ||||
| 
 | ||||
| @ -9240,6 +9414,7 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) { | ||||
| 		v = writextag('f', escapexml(cell.f), ff) + (cell.v != null ? v : ""); | ||||
| 	} | ||||
| 	if(cell.l) ws['!links'].push([ref, cell.l]); | ||||
| 	if(cell.c) ws['!comments'].push([ref, cell.c]); | ||||
| 	return writextag('c', v, o); | ||||
| } | ||||
| 
 | ||||
| @ -9414,6 +9589,7 @@ function write_ws_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ { | ||||
| 	if(ws === undefined) ws = {}; | ||||
| 	var ref = ws['!ref']; if(ref === undefined) ref = 'A1'; | ||||
| 	if(!rels) rels = {}; | ||||
| 	ws['!comments'] = []; | ||||
| 
 | ||||
| 	o[o.length] = (writextag('sheetPr', null, {'codeName': escapexml(wb.SheetNames[idx])})); | ||||
| 	o[o.length] = (writextag('dimension', null, {'ref': ref})); | ||||
| @ -9432,12 +9608,12 @@ function write_ws_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ { | ||||
| 
 | ||||
| 	if(ws['!merges'] != null && ws['!merges'].length > 0) o[o.length] = (write_ws_xml_merges(ws['!merges'])); | ||||
| 
 | ||||
| 	var relc = -1, rel; | ||||
| 	var relc = -1, rel, rId = -1; | ||||
| 	if(ws['!links'].length > 0) { | ||||
| 		o[o.length] = "<hyperlinks>"; | ||||
| 		ws['!links'].forEach(function(l) { | ||||
| 			if(!l[1].Target) return; | ||||
| 			var rId = add_rels(rels, -1, escapexml(l[1].Target).replace(/#.*$/, ""), RELS.HLINK); | ||||
| 			rId = add_rels(rels, -1, escapexml(l[1].Target).replace(/#.*$/, ""), RELS.HLINK); | ||||
| 			rel = ({"ref":l[0], "r:id":"rId"+rId}/*:any*/); | ||||
| 			if((relc = l[1].Target.indexOf("#")) > -1) rel.location = escapexml(l[1].Target.substr(relc+1)); | ||||
| 			if(l[1].Tooltip) rel.tooltip = escapexml(l[1].Tooltip); | ||||
| @ -9446,6 +9622,14 @@ function write_ws_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ { | ||||
| 		o[o.length] = "</hyperlinks>"; | ||||
| 	} | ||||
| 	delete ws['!links']; | ||||
| 
 | ||||
| 	if(ws['!comments'].length > 0) { | ||||
| 		rId = add_rels(rels, -1, "../drawings/vmlDrawing" + (idx+1) + ".vml", RELS.VML); | ||||
| 		o[o.length] = writextag("legacyDrawing", null, {"r:id":"rId" + rId}); | ||||
| 		ws['!legacy'] = rId; | ||||
| 	} | ||||
| // <legacyDrawing r:id="rId1"/>
 | ||||
| 
 | ||||
| 	if(o.length>2) { o[o.length] = ('</worksheet>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| } | ||||
| @ -10021,6 +10205,7 @@ function write_ws_bin_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:num | ||||
| 	/* TODO: cell style */ | ||||
| 	//o.s = get_cell_style(opts.cellXfs, cell, opts);
 | ||||
| 	if(cell.l) ws['!links'].push([encode_cell(o), cell.l]); | ||||
| 	if(cell.c) ws['!comments'].push([encode_cell(o), cell.c]); | ||||
| 	switch(cell.t) { | ||||
| 		case 's': case 'str': | ||||
| 			if(opts.bookSST) { | ||||
| @ -10090,12 +10275,22 @@ function write_HLINKS(ba, ws/*:Worksheet*/, rels) { | ||||
| 	}); | ||||
| 	delete ws['!links']; | ||||
| } | ||||
| function write_LEGACYDRAWING(ba, ws/*:Worksheet*/, idx/*:number*/, rels) { | ||||
| 	/* [BrtLegacyDrawing] */ | ||||
| 	if(ws['!comments'].length > 0) { | ||||
| 		var rId = add_rels(rels, -1, "../drawings/vmlDrawing" + (idx+1) + ".vml", RELS.VML); | ||||
| 		write_record(ba, "BrtLegacyDrawing", write_RelID("rId" + rId)); | ||||
| 		ws['!legacy'] = rId; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| function write_ws_bin(idx/*:number*/, opts, wb/*:Workbook*/, rels) { | ||||
| 	var ba = buf_array(); | ||||
| 	var s = wb.SheetNames[idx], ws = wb.Sheets[s] || {}; | ||||
| 	var r = safe_decode_range(ws['!ref'] || "A1"); | ||||
| 	ws['!links'] = []; | ||||
| 	/* passed back to write_zip and removed there */ | ||||
| 	ws['!comments'] = []; | ||||
| 	write_record(ba, "BrtBeginSheet"); | ||||
| 	write_record(ba, "BrtWsProp", write_BrtWsProp(s)); | ||||
| 	write_record(ba, "BrtWsDim", write_BrtWsDim(r)); | ||||
| @ -10127,7 +10322,7 @@ function write_ws_bin(idx/*:number*/, opts, wb/*:Workbook*/, rels) { | ||||
| 	/* [IGNOREECS] */ | ||||
| 	/* [SMARTTAGS] */ | ||||
| 	/* [BrtDrawing] */ | ||||
| 	/* [BrtLegacyDrawing] */ | ||||
| 	write_LEGACYDRAWING(ba, ws, idx, rels); | ||||
| 	/* [BrtLegacyDrawingHF] */ | ||||
| 	/* [BrtBkHim] */ | ||||
| 	/* [OLEOBJECTS] */ | ||||
| @ -10815,11 +11010,11 @@ function write_sty(data, name/*:string*/, opts) { | ||||
| function write_sst(data/*:SST*/, name/*:string*/, opts) { | ||||
| 	return (name.slice(-4)===".bin" ? write_sst_bin : write_sst_xml)(data, opts); | ||||
| } | ||||
| /* | ||||
| function write_cmnt(data, name:string, opts) { | ||||
| 
 | ||||
| function write_cmnt(data/*:Array<any>*/, name/*:string*/, opts) { | ||||
| 	return (name.slice(-4)===".bin" ? write_comments_bin : write_comments_xml)(data, opts); | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| function write_cc(data, name:string, opts) { | ||||
| 	return (name.slice(-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts); | ||||
| } | ||||
| @ -10946,7 +11141,7 @@ function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, a | ||||
| 			if(!cell.t) cell.t = 'n'; | ||||
| 			break; | ||||
| 		case 'Error': cell.t = 'e'; cell.v = RBErr[xml]; cell.w = xml; break; | ||||
| 		default: cell.t = 's'; cell.v = xlml_fixstr(ss); break; | ||||
| 		default: cell.t = 's'; cell.v = xlml_fixstr(ss||xml); break; | ||||
| 	} | ||||
| 	safe_format_xlml(cell, nf, o); | ||||
| 	if(o.cellFormula != null) { | ||||
| @ -10955,7 +11150,7 @@ function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, a | ||||
| 			/* strictly speaking, the leading = is required but some writers omit */ | ||||
| 			if(fstr.charCodeAt(0) == 61 /* = */) fstr = fstr.substr(1); | ||||
| 			cell.f = rc_to_a1(fstr, base); | ||||
| 			cell.Formula = undefined; | ||||
| 			delete cell.Formula; | ||||
| 			if(cell.ArrayRange == "RC") cell.F = rc_to_a1("RC:RC", base); | ||||
| 			else if(cell.ArrayRange) { | ||||
| 				cell.F = rc_to_a1(cell.ArrayRange, base); | ||||
| @ -10978,7 +11173,8 @@ function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, a | ||||
| } | ||||
| 
 | ||||
| function xlml_clean_comment(comment/*:any*/) { | ||||
| 	comment.t = comment.v; | ||||
| 	comment.t = comment.v || ""; | ||||
| 	comment.t = comment.t.replace(/\r\n/g,"\n").replace(/\r/g,"\n"); | ||||
| 	comment.v = comment.w = comment.ixfe = undefined; | ||||
| } | ||||
| 
 | ||||
| @ -11629,7 +11825,14 @@ function write_ws_xlml_wsopts(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workb | ||||
| 	if(o.length == 0) return ""; | ||||
| 	return writextag("WorksheetOptions", o.join(""), {xmlns:XLMLNS.x}); | ||||
| } | ||||
| /* TODO */ | ||||
| function write_ws_xlml_comment(comments) { | ||||
| 	return comments.map(function(c) { | ||||
| 		// TODO: formatted text
 | ||||
| 		var t = xlml_unfixstr(c.t||""); | ||||
| 		var d =writextag("ss:Data", t, {"xmlns":"http://www.w3.org/TR/REC-html40"}); | ||||
| 		return writextag("Comment", d, {"ss:Author":c.a}); | ||||
| 	}).join(""); | ||||
| } | ||||
| function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr)/*:string*/{ | ||||
| 	if(!cell || cell.v == undefined && cell.f == undefined) return "<Cell></Cell>"; | ||||
| 
 | ||||
| @ -11672,6 +11875,8 @@ function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr)/*:string*/{ | ||||
| 	} | ||||
| 	var m = '<Data ss:Type="' + t + '">' + _v + '</Data>'; | ||||
| 
 | ||||
| 	if((cell.c||[]).length > 0) m += write_ws_xlml_comment(cell.c); | ||||
| 
 | ||||
| 	return writextag("Cell", m, attr); | ||||
| } | ||||
| /* TODO */ | ||||
| @ -13808,11 +14013,12 @@ function write_BIFF2LABEL(r, c, val) { | ||||
| 
 | ||||
| function write_ws_biff_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts) { | ||||
| 	if(cell.v != null) switch(cell.t) { | ||||
| 		case 'n': | ||||
| 			if((cell.v == (cell.v|0)) && (cell.v >= 0) && (cell.v < 65536)) | ||||
| 				write_biff_rec(ba, 0x0002, write_BIFF2INT(R, C, cell.v)); | ||||
| 		case 'd': case 'n': | ||||
| 			var v = cell.t == 'd' ? datenum(cell.v) : cell.v; | ||||
| 			if((v == (v|0)) && (v >= 0) && (v < 65536)) | ||||
| 				write_biff_rec(ba, 0x0002, write_BIFF2INT(R, C, v)); | ||||
| 			else | ||||
| 				write_biff_rec(ba, 0x0003, write_BIFF2NUMBER(R,C, cell.v)); | ||||
| 				write_biff_rec(ba, 0x0003, write_BIFF2NUMBER(R,C, v)); | ||||
| 			return; | ||||
| 		case 'b': case 'e': write_biff_rec(ba, 0x0005, write_BIFF2BERR(R, C, cell.v, cell.t)); return; | ||||
| 		/* TODO: codepage, sst */ | ||||
| @ -13953,6 +14159,8 @@ var parse_content_xml = (function() { | ||||
| 		var number_format_map = {}; | ||||
| 		var merges = [], mrange = {}, mR = 0, mC = 0; | ||||
| 		var arrayf = []; | ||||
| 		var comments = [], comment = {}; | ||||
| 		var creator = "", creatoridx = 0; | ||||
| 		var rept = 1, isstub = false; | ||||
| 		var i = 0; | ||||
| 		xlmlregex.lastIndex = 0; | ||||
| @ -13998,6 +14206,7 @@ var parse_content_xml = (function() { | ||||
| 					if(C < range.s.c) range.s.c = C; | ||||
| 					if(R < range.s.r) range.s.r = R; | ||||
| 					ctag = parsexmltag(Rn[0], false); | ||||
| 					comments = []; comment = {}; | ||||
| 					q = ({t:ctag['数据类型'] || ctag['value-type'], v:null/*:: , z:null, w:""*/}/*:any*/); | ||||
| 					if(opts.cellFormula) { | ||||
| 						if(ctag.formula) ctag.formula = unescapexml(ctag.formula); | ||||
| @ -14047,6 +14256,7 @@ var parse_content_xml = (function() { | ||||
| 						q.v = textp || ''; | ||||
| 						isstub = textpidx == 0; | ||||
| 					} | ||||
| 					if(comments.length > 0) { q.c = comments; comments = []; } | ||||
| 					if(textp) q.w = textp; | ||||
| 					if(!isstub || opts.sheetStubs) { | ||||
| 						if(!(opts.sheetRows && opts.sheetRows < R)) { | ||||
| @ -14071,6 +14281,23 @@ var parse_content_xml = (function() { | ||||
| 				else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], true]); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 'annotation': // 14.1 <office:annotation>
 | ||||
| 				if(Rn[1]==='/'){ | ||||
| 					if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp; | ||||
| 					comment.t = textp; | ||||
| 					comment.a = creator; | ||||
| 					comments.push(comment); | ||||
| 				} | ||||
| 				else if(Rn[0].charAt(Rn[0].length-2) !== '/') {state.push([Rn[3], false]);} | ||||
| 				creator = ""; creatorpidx = 0; | ||||
| 				textp = ""; textpidx = 0; | ||||
| 				break; | ||||
| 
 | ||||
| 			case 'creator': // 4.3.2.7 <dc:creator>
 | ||||
| 				if(Rn[1]==='/') { creator = str.slice(creatoridx,Rn.index); } | ||||
| 				else creatoridx = Rn.index + Rn[0].length; | ||||
| 				break; | ||||
| 
 | ||||
| 			/* ignore state */ | ||||
| 			case 'meta': case '元数据': // TODO: <office:meta> <uof:元数据> FODS/UOF
 | ||||
| 			case 'settings': // TODO: <office:settings>
 | ||||
| @ -14086,7 +14313,6 @@ var parse_content_xml = (function() { | ||||
| 			case 'list-style': // 16.30 <text:list-style>
 | ||||
| 			case 'form': // 13.13 <form:form>
 | ||||
| 			case 'dde-links': // 9.8 <table:dde-links>
 | ||||
| 			case 'annotation': // 14.1 <office:annotation>
 | ||||
| 			case 'event-listeners': // TODO
 | ||||
| 				if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;} | ||||
| 				else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], false]); | ||||
| @ -14193,7 +14419,7 @@ var parse_content_xml = (function() { | ||||
| 			case 'span': break; // <text:span>
 | ||||
| 			case 'line-break': break; // 6.1.5 <text:line-break>
 | ||||
| 			case 'p': case '文本串': | ||||
| 				if(Rn[1]==='/') textp = parse_text_p(str.slice(textpidx,Rn.index), textptag); | ||||
| 				if(Rn[1]==='/') textp = (textp.length > 0 ? textp + "\n" : "") + parse_text_p(str.slice(textpidx,Rn.index), textptag); | ||||
| 				else { textptag = parsexmltag(Rn[0], false); textpidx = Rn.index + Rn[0].length; } | ||||
| 				break; // <text:p>
 | ||||
| 			case 's': break; // <text:s>
 | ||||
| @ -14247,7 +14473,6 @@ var parse_content_xml = (function() { | ||||
| 			case 'event-listener': // TODO
 | ||||
| 			/* TODO: FODS Properties */ | ||||
| 			case 'initial-creator': | ||||
| 			case 'creator': | ||||
| 			case 'creation-date': | ||||
| 			case 'generator': | ||||
| 			case 'document-statistic': | ||||
| @ -14665,6 +14890,7 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ { | ||||
| 	return out; | ||||
| } | ||||
| function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ { | ||||
| 	_shapeid = 1024; | ||||
| 	if(opts.bookType == "ods") return write_ods(wb, opts); | ||||
| 	if(wb && !wb.SSF) { | ||||
| 		wb.SSF = SSF.get_table(); | ||||
| @ -14723,8 +14949,26 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ { | ||||
| 		f = "xl/worksheets/sheet" + rId + "." + wbext; | ||||
| 		var wsrels = {'!id':{}}; | ||||
| 		zip.file(f, write_ws(rId-1, f, opts, wb, wsrels)); | ||||
| 
 | ||||
| 		ct.sheets.push(f); | ||||
| 		add_rels(opts.wbrels, rId, "worksheets/sheet" + rId + "." + wbext, RELS.WS[0]); | ||||
| 		add_rels(opts.wbrels, -1, "worksheets/sheet" + rId + "." + wbext, RELS.WS[0]); | ||||
| 
 | ||||
| 		var ws = wb.Sheets[wb.SheetNames[rId-1]]; | ||||
| 		if(ws) { | ||||
| 			var comments = ws['!comments']; | ||||
| 			if(comments && comments.length > 0) { | ||||
| 				var cf = "xl/comments" + rId + "." + wbext; | ||||
| 				zip.file(cf, write_cmnt(comments, cf, opts)); | ||||
| 				ct.comments.push(cf); | ||||
| 				add_rels(wsrels, -1, "../comments" + rId + "." + wbext, RELS.CMNT); | ||||
| 			} | ||||
| 			if(ws['!legacy']) { | ||||
| 				zip.file("xl/drawings/vmlDrawing" + (rId) + ".vml", write_comments_vml(rId, ws['!comments'])); | ||||
| 			} | ||||
| 			delete ws['!comments']; | ||||
| 			delete ws['!legacy']; | ||||
| 		} | ||||
| 
 | ||||
| 		if(wsrels['!id'].rId1) zip.file(get_rels_path(f), write_rels(wsrels)); // get_rels_path('')
 | ||||
| 	} | ||||
| 
 | ||||
| @ -14732,7 +14976,7 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ { | ||||
| 		f = "xl/sharedStrings." + wbext; | ||||
| 		zip.file(f, write_sst(opts.Strings, f, opts)); | ||||
| 		ct.strs.push(f); | ||||
| 		add_rels(opts.wbrels, ++rId, "sharedStrings." + wbext, RELS.SST); | ||||
| 		add_rels(opts.wbrels, -1, "sharedStrings." + wbext, RELS.SST); | ||||
| 	} | ||||
| 
 | ||||
| 	/* TODO: something more intelligent with themes */ | ||||
| @ -14740,20 +14984,20 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ { | ||||
| 	f = "xl/theme/theme1.xml"; | ||||
| 	zip.file(f, write_theme(wb.Themes, opts)); | ||||
| 	ct.themes.push(f); | ||||
| 	add_rels(opts.wbrels, ++rId, "theme/theme1.xml", RELS.THEME); | ||||
| 	add_rels(opts.wbrels, -1, "theme/theme1.xml", RELS.THEME); | ||||
| 
 | ||||
| 	/* TODO: something more intelligent with styles */ | ||||
| 
 | ||||
| 	f = "xl/styles." + wbext; | ||||
| 	zip.file(f, write_sty(wb, f, opts)); | ||||
| 	ct.styles.push(f); | ||||
| 	add_rels(opts.wbrels, ++rId, "styles." + wbext, RELS.STY); | ||||
| 	add_rels(opts.wbrels, -1, "styles." + wbext, RELS.STY); | ||||
| 
 | ||||
| 	if(wb.vbaraw && vbafmt) { | ||||
| 		f = "xl/vbaProject.bin"; | ||||
| 		zip.file(f, wb.vbaraw); | ||||
| 		ct.vba.push(f); | ||||
| 		add_rels(opts.wbrels, ++rId, "vbaProject.bin", RELS.VBA); | ||||
| 		add_rels(opts.wbrels, -1, "vbaProject.bin", RELS.VBA); | ||||
| 	} | ||||
| 
 | ||||
| 	zip.file("[Content_Types].xml", write_ct(ct, opts)); | ||||
| @ -14858,7 +15102,7 @@ function write_binary_type(out, opts/*:WriteOpts*/) { | ||||
| function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) { | ||||
| 	check_wb(wb); | ||||
| 	var o = opts||{}; | ||||
| 	switch(o.bookType || 'xlsx') { | ||||
| 	switch(o.bookType || 'xlsb') { | ||||
| 		case 'xml': | ||||
| 		case 'xlml': return write_string_type(write_xlml(wb, o), o); | ||||
| 		case 'slk': | ||||
| @ -14876,21 +15120,20 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) { | ||||
| } | ||||
| 
 | ||||
| function resolve_book_type(o/*?WriteFileOpts*/) { | ||||
| 	if(!o.bookType) switch(o.file.slice(-5).toLowerCase()) { | ||||
| 	if(!o.bookType) switch(o.file.slice(o.file.lastIndexOf(".")).toLowerCase()) { | ||||
| 		case '.xlsx': o.bookType = 'xlsx'; break; | ||||
| 		case '.xlsm': o.bookType = 'xlsm'; break; | ||||
| 		case '.xlsb': o.bookType = 'xlsb'; break; | ||||
| 		case '.fods': o.bookType = 'fods'; break; | ||||
| 		case '.xlml': o.bookType = 'xlml'; break; | ||||
| 		case '.sylk': o.bookType = 'sylk'; break; | ||||
| 	default: switch(o.file.slice(-4).toLowerCase()) { | ||||
| 		case '.xls': o.bookType = 'biff2'; break; | ||||
| 		case '.xml': o.bookType = 'xml'; break; | ||||
| 		case '.ods': o.bookType = 'ods'; break; | ||||
| 		case '.csv': o.bookType = 'csv'; break; | ||||
| 		case '.dif': o.bookType = 'dif'; break; | ||||
| 		case '.slk': o.bookType = 'sylk'; break; | ||||
| 	}} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| function writeFileSync(wb/*:Workbook*/, filename/*:string*/, opts/*:?WriteFileOpts*/) { | ||||
|  | ||||
							
								
								
									
										339
									
								
								xlsx.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										339
									
								
								xlsx.js
									
									
									
									
									
								
							| @ -76,7 +76,7 @@ var Base64 = (function make_b64(){ | ||||
| 		} | ||||
| 	}; | ||||
| })(); | ||||
| var has_buf = (typeof Buffer !== 'undefined'); | ||||
| var has_buf = (typeof Buffer !== 'undefined' && typeof process !== 'undefined' && typeof process.versions !== 'undefined' && process.versions.node); | ||||
| 
 | ||||
| function new_raw_buf(len) { | ||||
| 	/* jshint -W056 */ | ||||
| @ -1559,6 +1559,9 @@ var xlml_fixstr = (function() { | ||||
| 	function entrepl($$,$1) { return String.fromCharCode(parseInt($1,10)); } | ||||
| 	return function xlml_fixstr(str) { return str.replace(entregex,entrepl); }; | ||||
| })(); | ||||
| var xlml_unfixstr = (function() { | ||||
| 	return function xlml_unfixstr(str) { return str.replace(/(\r\n|[\r\n])/g,"\
"); }; | ||||
| })(); | ||||
| 
 | ||||
| function parsexmlbool(value, tag) { | ||||
| 	switch(value) { | ||||
| @ -1683,6 +1686,8 @@ var XLMLNS = ({ | ||||
| 	'x':    'urn:schemas-microsoft-com:office:excel', | ||||
| 	'ss':   'urn:schemas-microsoft-com:office:spreadsheet', | ||||
| 	'dt':   'uuid:C2F41010-65B3-11d1-A29F-00AA00C14882', | ||||
| 	'mv':   'http://macVmlSchemaUri', | ||||
| 	'v':    'urn:schemas-microsoft-com:vml', | ||||
| 	'html': 'http://www.w3.org/TR/REC-html40' | ||||
| }); | ||||
| function read_double_le(b, idx) { | ||||
| @ -1886,7 +1891,9 @@ function recordhopper(data, cb, opts) { | ||||
| 		tmpbyte = data.read_shift(1); | ||||
| 		length = tmpbyte & 0x7F; | ||||
| 		for(cntbyte = 1; cntbyte <4 && (tmpbyte & 0x80); ++cntbyte) length += ((tmpbyte = data.read_shift(1)) & 0x7F)<<(7*cntbyte); | ||||
| 		var tgt = data.l + length; | ||||
| 		var d = R.f(data, length, opts); | ||||
| 		data.l = tgt; | ||||
| 		if(cb(d, R, RT)) return; | ||||
| 	} | ||||
| } | ||||
| @ -2078,7 +2085,7 @@ function parse_RichStr(data, length) { | ||||
| 		for(var i = 0; i != dwSizeStrRun; ++i) rgsStrRun.push(parse_StrRun(data)); | ||||
| 		z.r = rgsStrRun; | ||||
| 	} | ||||
| 	else z.r = "<t>" + escapexml(str) + "</t>"; | ||||
| 	else z.r = [{ich:0, ifnt:0}]; | ||||
| 	//if((flags & 2) !== 0) { /* fExtStr */
 | ||||
| 	//	/* TODO: phonetic string */
 | ||||
| 	//}
 | ||||
| @ -2087,10 +2094,10 @@ function parse_RichStr(data, length) { | ||||
| } | ||||
| function write_RichStr(str, o) { | ||||
| 	/* TODO: formatted string */ | ||||
| 	if(o == null) o = new_buf(5+2*str.t.length); | ||||
| 	var _null = false; if(o == null) { _null = true; o = new_buf(15+4*str.t.length); } | ||||
| 	o.write_shift(1,0); | ||||
| 	write_XLWideString(str.t, o); | ||||
| 	return o; | ||||
| 	return _null ? o.slice(0, o.l) : o; | ||||
| } | ||||
| 
 | ||||
| /* [MS-XLSB] 2.5.9 */ | ||||
| @ -2120,10 +2127,10 @@ function parse_XLNullableWideString(data) { | ||||
| 	return cchCharacters === 0 || cchCharacters === 0xFFFFFFFF ? "" : data.read_shift(cchCharacters, 'dbcs'); | ||||
| } | ||||
| function write_XLNullableWideString(data, o) { | ||||
| 	if(!o) o = new_buf(127); | ||||
| 	var _null = false; if(o == null) { _null = true; o = new_buf(127); } | ||||
| 	o.write_shift(4, data.length > 0 ? data.length : 0xFFFFFFFF); | ||||
| 	if(data.length > 0) o.write_shift(0, data, 'dbcs'); | ||||
| 	return o; | ||||
| 	return _null ? o.slice(0, o.l) : o; | ||||
| } | ||||
| 
 | ||||
| /* [MS-XLSB] 2.5.168 */ | ||||
| @ -2132,10 +2139,10 @@ function parse_XLWideString(data) { | ||||
| 	return cchCharacters === 0 ? "" : data.read_shift(cchCharacters, 'dbcs'); | ||||
| } | ||||
| function write_XLWideString(data, o) { | ||||
| 	if(o == null) o = new_buf(4+2*data.length); | ||||
| 	var _null = false; if(o == null) { _null = true; o = new_buf(4+2*data.length); } | ||||
| 	o.write_shift(4, data.length); | ||||
| 	if(data.length > 0) o.write_shift(0, data, 'dbcs'); | ||||
| 	return o; | ||||
| 	return _null ? o.slice(0, o.l) : o; | ||||
| } | ||||
| 
 | ||||
| /* [MS-XLSB] 2.5.165 */ | ||||
| @ -2528,10 +2535,6 @@ var ct2type/*{[string]:string}*/ = ({ | ||||
| 	"application/vnd.openxmlformats-officedocument.customXmlProperties+xml": "TODO", | ||||
| 	"application/vnd.openxmlformats-officedocument.spreadsheetml.customProperty": "TODO", | ||||
| 
 | ||||
| 	/* Comments */ | ||||
| 	"application/vnd.ms-excel.comments": "comments", | ||||
| 	"application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": "comments", | ||||
| 
 | ||||
| 	/* PivotTable */ | ||||
| 	"application/vnd.ms-excel.pivotTable": "TODO", | ||||
| 	"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml": "TODO", | ||||
| @ -2663,6 +2666,10 @@ var CT_LIST = (function(){ | ||||
| 			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml", | ||||
| 			xlsb: "application/vnd.ms-excel.sharedStrings" | ||||
| 		}, | ||||
| 		comments: { /* Comments */ | ||||
| 			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml", | ||||
| 			xlsb: "application/vnd.ms-excel.comments" | ||||
| 		}, | ||||
| 		sheets: { /* Worksheet */ | ||||
| 			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml", | ||||
| 			xlsb: "application/vnd.ms-excel.worksheet" | ||||
| @ -2731,6 +2738,7 @@ var CTYPE_XML_ROOT = writextag('Types', null, { | ||||
| var CTYPE_DEFAULTS = [ | ||||
| 	['xml', 'application/xml'], | ||||
| 	['bin', 'application/vnd.ms-excel.sheet.binary.macroEnabled.main'], | ||||
| 	['vml', 'application/vnd.openxmlformats-officedocument.vmlDrawing'], | ||||
| 	/* from test files */ | ||||
| 	['bmp', 'image/bmp'], | ||||
| 	['png', 'image/png'], | ||||
| @ -2781,7 +2789,7 @@ function write_ct(ct, opts) { | ||||
| 	['strs', 'styles'].forEach(f1); | ||||
| 	['coreprops', 'extprops', 'custprops'].forEach(f3); | ||||
| 	f3('vba'); | ||||
| 	f3('comments'); | ||||
| 	f2('comments'); | ||||
| 	if(o.length>2){ o[o.length] = ('</Types>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| } | ||||
| @ -2790,6 +2798,7 @@ var RELS = ({ | ||||
| 	WB: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", | ||||
| 	SHEET: "http://sheetjs.openxmlformats.org/officeDocument/2006/relationships/officeDocument", | ||||
| 	HLINK: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", | ||||
| 	VML: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing", | ||||
| 	VBA: "http://schemas.microsoft.com/office/2006/relationships/vbaProject" | ||||
| }); | ||||
| 
 | ||||
| @ -5055,6 +5064,59 @@ var DIF = (function() { | ||||
| 		from_sheet: sheet_to_dif | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| var PRN = (function() { | ||||
| 	function prn_to_aoa(d, opts) { | ||||
| 		switch(opts.type) { | ||||
| 			case 'base64': return prn_to_aoa_str(Base64.decode(d), opts); | ||||
| 			case 'binary': return prn_to_aoa_str(d, opts); | ||||
| 			case 'buffer': return prn_to_aoa_str(d.toString('binary'), opts); | ||||
| 			case 'array': return prn_to_aoa_str(cc2str(d), opts); | ||||
| 		} | ||||
| 		throw new Error("Unrecognized type " + opts.type); | ||||
| 	} | ||||
| 
 | ||||
| 	function set_text_arr(data, arr, R, C) { | ||||
| 		if(data === 'TRUE') arr[R][C] = true; | ||||
| 		else if(data === 'FALSE') arr[R][C] = false; | ||||
| 		else if(+data == +data) arr[R][C] = +data; | ||||
| 		else if(data !== "") arr[R][C] = data; | ||||
| 	} | ||||
| 
 | ||||
| 	function prn_to_aoa_str(f, opts) { | ||||
| 		var arr = ([]); | ||||
| 		if(!f || f.length === 0) return arr; | ||||
| 		var lines = f.split(/[\r\n]/); | ||||
| 		var L = lines.length - 1; | ||||
| 		while(L >= 0 && lines[L].length === 0) --L; | ||||
| 		var start = 10, idx = 0; | ||||
| 		var R = 0; | ||||
| 		for(; R <= L; ++R) { | ||||
| 			idx = lines[R].indexOf(" "); | ||||
| 			if(idx == -1) idx = lines[R].length; else idx++; | ||||
| 			start = Math.max(start, idx); | ||||
| 		} | ||||
| 		for(R = 0; R <= L; ++R) { | ||||
| 			arr[R] = []; | ||||
| 			/* TODO: confirm that widths are always 10 */ | ||||
| 			var C = 0; | ||||
| 			set_text_arr(lines[R].slice(0, start).trim(), arr, R, C); | ||||
| 			for(C = 1; C <= (lines[R].length - start)/10 + 1; ++C) | ||||
| 				set_text_arr(lines[R].slice(start+(C-1)*10,start+C*10).trim(),arr,R,C); | ||||
| 		} | ||||
| 		return arr; | ||||
| 	} | ||||
| 
 | ||||
| 	function prn_to_sheet(str, opts) { return aoa_to_sheet(prn_to_aoa(str, opts), opts); } | ||||
| 
 | ||||
| 	function prn_to_workbook(str, opts) { return sheet_to_workbook(prn_to_sheet(str, opts), opts); } | ||||
| 
 | ||||
| 	return { | ||||
| 		to_workbook: prn_to_workbook, | ||||
| 		to_sheet: prn_to_sheet | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| /* 18.4.1 charset to codepage mapping */ | ||||
| var CS2CP = ({ | ||||
| 0:    1252, /* ANSI */ | ||||
| @ -5555,7 +5617,7 @@ function cycle_width(collw) { return char2width(px2char(width2px(collw))); } | ||||
| /* XLSX/XLSB/XLS specify width in units of MDW */ | ||||
| function find_mdw_colw(collw) { | ||||
| 	var delta = Infinity, _MDW = MIN_MDW; | ||||
| 	for(MDW=MIN_MDW; MDW<MAX_MDW; ++MDW) if(Math.abs(collw - cycle_width(collw)) < delta) { delta = Math.abs(collw - cycle_width(collw)); _MDW = MDW; } | ||||
| 	for(MDW=MIN_MDW; MDW<MAX_MDW; ++MDW) if(Math.abs(collw - cycle_width(collw)) <= delta) { delta = Math.abs(collw - cycle_width(collw)); _MDW = MDW; } | ||||
| 	MDW = _MDW; | ||||
| } | ||||
| /* XLML specifies width in terms of pixels */ | ||||
| @ -6517,6 +6579,50 @@ function parse_drawing(data, rels) { | ||||
| 
 | ||||
| 	return rels['!id'][id].Target; | ||||
| } | ||||
| /* L.5.5.2 SpreadsheetML Comments + VML Schema */ | ||||
| var _shapeid = 1024; | ||||
| function write_comments_vml(rId, comments) { | ||||
| 	var csize = [21600, 21600]; | ||||
| 	/* L.5.2.1.2 Path Attribute */ | ||||
| 	var bbox = ["m0,0l0",csize[1],csize[0],csize[1],csize[0],"0xe"].join(","); | ||||
| 	var o = [ | ||||
| 		writextag("xml", null, { 'xmlns:v': XLMLNS.v, 'xmlns:o': XLMLNS.o, 'xmlns:x': XLMLNS.x, 'xmlns:mv': XLMLNS.mv }).replace(/\/>/,">"), | ||||
| 		writextag("o:shapelayout", writextag("o:idmap", null, {'v:ext':"edit", 'data':rId}), {'v:ext':"edit"}), | ||||
| 		writextag("v:shapetype", [ | ||||
| 			writextag("v:stroke", null, {joinstyle:"miter"}), | ||||
| 			writextag("v:path", null, {gradientshapeok:"t", 'o:connecttype':"rect"}) | ||||
| 		].join(""), {id:"_x0000_t202", 'o:spt':202, coordsize:csize.join(","),path:bbox}) | ||||
| 	]; | ||||
| 	while(_shapeid < rId * 1000) _shapeid += 1000; | ||||
| 
 | ||||
| 	comments.map(function(x) { return decode_cell(x[0]); }).forEach(function(c,i) { o = o.concat([ | ||||
| 	'<v:shape' + wxt_helper({ | ||||
| 		id:'_x0000_s' + (++_shapeid), | ||||
| 		type:"#_x0000_t202", | ||||
| 		style:"position:absolute; margin-left:80pt;margin-top:5pt;width:104pt;height:64pt;z-index:10;visibility:hidden", | ||||
| 		fillcolor:"#ECFAD4", | ||||
| 		strokecolor:"#edeaa1" | ||||
| 	}) + '>', | ||||
| 		writextag('v:fill', writextag("o:fill", null, {type:"gradientUnscaled", 'v:ext':"view"}), {'color2':"#BEFF82", 'angle':"-180", 'type':"gradient"}), | ||||
| 		writextag("v:shadow", null, {on:"t", 'obscured':"t"}), | ||||
| 		writextag("v:path", null, {'o:connecttype':"none"}), | ||||
| 		'<v:textbox><div style="text-align:left"></div></v:textbox>', | ||||
| 		'<x:ClientData ObjectType="Note">', | ||||
| 			'<x:MoveWithCells/>', | ||||
| 			'<x:SizeWithCells/>', | ||||
| 			/* Part 4 19.4.2.3 Anchor (Anchor) */ | ||||
| 			writetag('x:Anchor', [c.c, 0, c.r, 0, c.c+3, 100, c.r+5, 100].join(",")), | ||||
| 			writetag('x:AutoFill', "False"), | ||||
| 			writetag('x:Row', String(c.r)), | ||||
| 			writetag('x:Column', String(c.c)), | ||||
| 		'</x:ClientData>', | ||||
| 	'</v:shape>' | ||||
| 	]); }); | ||||
| 	o.push('</xml>'); | ||||
| 	return o.join(""); | ||||
| } | ||||
| 
 | ||||
| RELS.CMNT = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"; | ||||
| 
 | ||||
| function parse_comments(zip, dirComments, sheets, sheetRels, opts) { | ||||
| 	for(var i = 0; i != dirComments.length; ++i) { | ||||
| @ -6559,8 +6665,9 @@ function insertCommentsIntoSheet(sheetName, sheet, comments) { | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| /* 18.7.3 CT_Comment */ | ||||
| /* 18.7 Comments */ | ||||
| function parse_comments_xml(data, opts) { | ||||
| 	/* 18.7.6 CT_Comments */ | ||||
| 	if(data.match(/<(?:\w+:)?comments *\/>/)) return []; | ||||
| 	var authors = []; | ||||
| 	var commentList = []; | ||||
| @ -6576,22 +6683,46 @@ function parse_comments_xml(data, opts) { | ||||
| 		var cm = x.match(/<(?:\w+:)?comment[^>]*>/); | ||||
| 		if(!cm) return; | ||||
| 		var y = parsexmltag(cm[0]); | ||||
| 		var comment = ({ author: y.authorId && authors[y.authorId] ? authors[y.authorId] : undefined, ref: y.ref, guid: y.guid }); | ||||
| 		var comment = ({ author: y.authorId && authors[y.authorId] ? authors[y.authorId] : "sheetjsghost", ref: y.ref, guid: y.guid }); | ||||
| 		var cell = decode_cell(y.ref); | ||||
| 		if(opts.sheetRows && opts.sheetRows <= cell.r) return; | ||||
| 		var textMatch = x.match(/<(?:\w+:)?text>([^\u2603]*)<\/(?:\w+:)?text>/); | ||||
| 		if (!textMatch || !textMatch[1]) return; // a comment may contain an empty text tag.
 | ||||
| 		var rt = parse_si(textMatch[1]); | ||||
| 		if(!rt) return; | ||||
| 		var rt = (!textMatch || !textMatch[1]) ? {r:"",t:"",h:""} : parse_si(textMatch[1]); | ||||
| 		comment.r = rt.r; | ||||
| 		comment.t = rt.t; | ||||
| 		comment.t = rt.t.replace(/\r\n/g,"\n").replace(/\r/g,"\n"); | ||||
| 		if(opts.cellHTML) comment.h = rt.h; | ||||
| 		commentList.push(comment); | ||||
| 	}); | ||||
| 	return commentList; | ||||
| } | ||||
| 
 | ||||
| function write_comments_xml(data, opts) { } | ||||
| var CMNT_XML_ROOT = writextag('comments', null, { 'xmlns': XMLNS.main[0] }); | ||||
| function write_comments_xml(data, opts) { | ||||
| 	var o = [XML_HEADER, CMNT_XML_ROOT]; | ||||
| 
 | ||||
| 	var iauthor = []; | ||||
| 	o.push("<authors>"); | ||||
| 	data.map(function(x) { return x[1]; }).forEach(function(comment) { | ||||
| 		comment.map(function(x) { return escapexml(x.a); }).forEach(function(a) { | ||||
| 			if(iauthor.indexOf(a) > -1) return; | ||||
| 			iauthor.push(a); | ||||
| 			o.push("<author>" + a + "</author>"); | ||||
| 		}); | ||||
| 	}); | ||||
| 	o.push("</authors>"); | ||||
| 	o.push("<commentList>"); | ||||
| 	data.forEach(function(d) { | ||||
| 		d[1].forEach(function(c) { | ||||
| 			/* 18.7.3 CT_Comment */ | ||||
| 			o.push('<comment ref="' + d[0] + '" authorId="' + iauthor.indexOf(escapexml(c.a)) + '"><text>'); | ||||
| 			o.push(writetag("t", c.t)); | ||||
| 			o.push('</text></comment>'); | ||||
| 		}); | ||||
| 	}); | ||||
| 	o.push("</commentList>"); | ||||
| 	if(o.length>2) { o[o.length] = ('</comments>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| } | ||||
| /* [MS-XLSB] 2.4.28 BrtBeginComment */ | ||||
| function parse_BrtBeginComment(data, length) { | ||||
| 	var out = {}; | ||||
| @ -6602,6 +6733,16 @@ function parse_BrtBeginComment(data, length) { | ||||
| 	data.l += 16; /*var guid = parse_GUID(data); */ | ||||
| 	return out; | ||||
| } | ||||
| function write_BrtBeginComment(data, o) { | ||||
| 	if(o == null) o = new_buf(36); | ||||
| 	o.write_shift(4, data[1].iauthor); | ||||
| 	write_UncheckedRfX(data[0], o); | ||||
| 	o.write_shift(4, 0); | ||||
| 	o.write_shift(4, 0); | ||||
| 	o.write_shift(4, 0); | ||||
| 	o.write_shift(4, 0); | ||||
| 	return o; | ||||
| } | ||||
| 
 | ||||
| /* [MS-XLSB] 2.4.324 BrtCommentAuthor */ | ||||
| var parse_BrtCommentAuthor = parse_XLWideString; | ||||
| @ -6624,6 +6765,7 @@ function parse_comments_bin(data, opts) { | ||||
| 				c.author = authors[c.iauthor]; | ||||
| 				delete c.iauthor; | ||||
| 				if(opts.sheetRows && opts.sheetRows <= c.rfx.r) break; | ||||
| 				if(!c.t) c.t = ""; | ||||
| 				delete c.rfx; out.push(c); break; | ||||
| 			case 'BrtBeginComments': break; | ||||
| 			case 'BrtEndComments': break; | ||||
| @ -6637,7 +6779,38 @@ function parse_comments_bin(data, opts) { | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| function write_comments_bin(data, opts) { } | ||||
| function write_comments_bin(data, opts) { | ||||
| 	var ba = buf_array(); | ||||
| 	var iauthor = []; | ||||
| 	write_record(ba, "BrtBeginComments"); | ||||
| 	{ /* COMMENTAUTHORS */ | ||||
| 		write_record(ba, "BrtBeginCommentAuthors"); | ||||
| 		data.forEach(function(comment) { | ||||
| 			comment[1].forEach(function(c) { | ||||
| 				if(iauthor.indexOf(c.a) > -1) return; | ||||
| 				iauthor.push(c.a.substr(0,54)); | ||||
| 				write_record(ba, "BrtCommentAuthor", write_XLWideString(c.a.substr(0, 54))); | ||||
| 			}); | ||||
| 		}); | ||||
| 		write_record(ba, "BrtEndCommentAuthors"); | ||||
| 	} | ||||
| 	{ /* COMMENTLIST */ | ||||
| 		write_record(ba, "BrtBeginCommentList"); | ||||
| 		data.forEach(function(comment) { | ||||
| 			comment[1].forEach(function(c) { | ||||
| 				c.iauthor = iauthor.indexOf(c.a); | ||||
| 				var range = {s:decode_cell(comment[0])}; range.e = range.s; | ||||
| 				write_record(ba, "BrtBeginComment", write_BrtBeginComment([range, c])); | ||||
| 				if(c.t && c.t.length > 0) write_record(ba, "BrtCommentText", write_RichStr(c)); | ||||
| 				write_record(ba, "BrtEndComment"); | ||||
| 				delete c.iauthor; | ||||
| 			}); | ||||
| 		}); | ||||
| 		write_record(ba, "BrtEndCommentList"); | ||||
| 	} | ||||
| 	write_record(ba, "BrtEndComments"); | ||||
| 	return ba.end(); | ||||
| } | ||||
| RELS.DS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet"; | ||||
| RELS.MS = "http://schemas.microsoft.com/office/2006/relationships/xlMacrosheet"; | ||||
| 
 | ||||
| @ -8976,6 +9149,7 @@ function col_obj_w(C, col) { | ||||
| 	else if(col.wpx != null) width = px2char(col.wpx); | ||||
| 	else if(col.wch != null) width = col.wch; | ||||
| 	if(width > -1) { p.width = char2width(width); p.customWidth = 1; } | ||||
| 	else p.width = col.width; | ||||
| 	return p; | ||||
| } | ||||
| 
 | ||||
| @ -9185,6 +9359,7 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) { | ||||
| 		v = writextag('f', escapexml(cell.f), ff) + (cell.v != null ? v : ""); | ||||
| 	} | ||||
| 	if(cell.l) ws['!links'].push([ref, cell.l]); | ||||
| 	if(cell.c) ws['!comments'].push([ref, cell.c]); | ||||
| 	return writextag('c', v, o); | ||||
| } | ||||
| 
 | ||||
| @ -9359,6 +9534,7 @@ function write_ws_xml(idx, opts, wb, rels) { | ||||
| 	if(ws === undefined) ws = {}; | ||||
| 	var ref = ws['!ref']; if(ref === undefined) ref = 'A1'; | ||||
| 	if(!rels) rels = {}; | ||||
| 	ws['!comments'] = []; | ||||
| 
 | ||||
| 	o[o.length] = (writextag('sheetPr', null, {'codeName': escapexml(wb.SheetNames[idx])})); | ||||
| 	o[o.length] = (writextag('dimension', null, {'ref': ref})); | ||||
| @ -9377,12 +9553,12 @@ function write_ws_xml(idx, opts, wb, rels) { | ||||
| 
 | ||||
| 	if(ws['!merges'] != null && ws['!merges'].length > 0) o[o.length] = (write_ws_xml_merges(ws['!merges'])); | ||||
| 
 | ||||
| 	var relc = -1, rel; | ||||
| 	var relc = -1, rel, rId = -1; | ||||
| 	if(ws['!links'].length > 0) { | ||||
| 		o[o.length] = "<hyperlinks>"; | ||||
| 		ws['!links'].forEach(function(l) { | ||||
| 			if(!l[1].Target) return; | ||||
| 			var rId = add_rels(rels, -1, escapexml(l[1].Target).replace(/#.*$/, ""), RELS.HLINK); | ||||
| 			rId = add_rels(rels, -1, escapexml(l[1].Target).replace(/#.*$/, ""), RELS.HLINK); | ||||
| 			rel = ({"ref":l[0], "r:id":"rId"+rId}); | ||||
| 			if((relc = l[1].Target.indexOf("#")) > -1) rel.location = escapexml(l[1].Target.substr(relc+1)); | ||||
| 			if(l[1].Tooltip) rel.tooltip = escapexml(l[1].Tooltip); | ||||
| @ -9391,6 +9567,14 @@ function write_ws_xml(idx, opts, wb, rels) { | ||||
| 		o[o.length] = "</hyperlinks>"; | ||||
| 	} | ||||
| 	delete ws['!links']; | ||||
| 
 | ||||
| 	if(ws['!comments'].length > 0) { | ||||
| 		rId = add_rels(rels, -1, "../drawings/vmlDrawing" + (idx+1) + ".vml", RELS.VML); | ||||
| 		o[o.length] = writextag("legacyDrawing", null, {"r:id":"rId" + rId}); | ||||
| 		ws['!legacy'] = rId; | ||||
| 	} | ||||
| // <legacyDrawing r:id="rId1"/>
 | ||||
| 
 | ||||
| 	if(o.length>2) { o[o.length] = ('</worksheet>'); o[1]=o[1].replace("/>",">"); } | ||||
| 	return o.join(""); | ||||
| } | ||||
| @ -9966,6 +10150,7 @@ function write_ws_bin_cell(ba, cell, R, C, opts, ws) { | ||||
| 	/* TODO: cell style */ | ||||
| 	//o.s = get_cell_style(opts.cellXfs, cell, opts);
 | ||||
| 	if(cell.l) ws['!links'].push([encode_cell(o), cell.l]); | ||||
| 	if(cell.c) ws['!comments'].push([encode_cell(o), cell.c]); | ||||
| 	switch(cell.t) { | ||||
| 		case 's': case 'str': | ||||
| 			if(opts.bookSST) { | ||||
| @ -10035,12 +10220,22 @@ function write_HLINKS(ba, ws, rels) { | ||||
| 	}); | ||||
| 	delete ws['!links']; | ||||
| } | ||||
| function write_LEGACYDRAWING(ba, ws, idx, rels) { | ||||
| 	/* [BrtLegacyDrawing] */ | ||||
| 	if(ws['!comments'].length > 0) { | ||||
| 		var rId = add_rels(rels, -1, "../drawings/vmlDrawing" + (idx+1) + ".vml", RELS.VML); | ||||
| 		write_record(ba, "BrtLegacyDrawing", write_RelID("rId" + rId)); | ||||
| 		ws['!legacy'] = rId; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| function write_ws_bin(idx, opts, wb, rels) { | ||||
| 	var ba = buf_array(); | ||||
| 	var s = wb.SheetNames[idx], ws = wb.Sheets[s] || {}; | ||||
| 	var r = safe_decode_range(ws['!ref'] || "A1"); | ||||
| 	ws['!links'] = []; | ||||
| 	/* passed back to write_zip and removed there */ | ||||
| 	ws['!comments'] = []; | ||||
| 	write_record(ba, "BrtBeginSheet"); | ||||
| 	write_record(ba, "BrtWsProp", write_BrtWsProp(s)); | ||||
| 	write_record(ba, "BrtWsDim", write_BrtWsDim(r)); | ||||
| @ -10072,7 +10267,7 @@ function write_ws_bin(idx, opts, wb, rels) { | ||||
| 	/* [IGNOREECS] */ | ||||
| 	/* [SMARTTAGS] */ | ||||
| 	/* [BrtDrawing] */ | ||||
| 	/* [BrtLegacyDrawing] */ | ||||
| 	write_LEGACYDRAWING(ba, ws, idx, rels); | ||||
| 	/* [BrtLegacyDrawingHF] */ | ||||
| 	/* [BrtBkHim] */ | ||||
| 	/* [OLEOBJECTS] */ | ||||
| @ -10760,11 +10955,11 @@ function write_sty(data, name, opts) { | ||||
| function write_sst(data, name, opts) { | ||||
| 	return (name.slice(-4)===".bin" ? write_sst_bin : write_sst_xml)(data, opts); | ||||
| } | ||||
| /* | ||||
| function write_cmnt(data, name:string, opts) { | ||||
| 
 | ||||
| function write_cmnt(data, name, opts) { | ||||
| 	return (name.slice(-4)===".bin" ? write_comments_bin : write_comments_xml)(data, opts); | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| function write_cc(data, name:string, opts) { | ||||
| 	return (name.slice(-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts); | ||||
| } | ||||
| @ -10889,7 +11084,7 @@ function parse_xlml_data(xml, ss, data, cell, base, styles, csty, row, arrayf, o | ||||
| 			if(!cell.t) cell.t = 'n'; | ||||
| 			break; | ||||
| 		case 'Error': cell.t = 'e'; cell.v = RBErr[xml]; cell.w = xml; break; | ||||
| 		default: cell.t = 's'; cell.v = xlml_fixstr(ss); break; | ||||
| 		default: cell.t = 's'; cell.v = xlml_fixstr(ss||xml); break; | ||||
| 	} | ||||
| 	safe_format_xlml(cell, nf, o); | ||||
| 	if(o.cellFormula != null) { | ||||
| @ -10898,7 +11093,7 @@ function parse_xlml_data(xml, ss, data, cell, base, styles, csty, row, arrayf, o | ||||
| 			/* strictly speaking, the leading = is required but some writers omit */ | ||||
| 			if(fstr.charCodeAt(0) == 61 /* = */) fstr = fstr.substr(1); | ||||
| 			cell.f = rc_to_a1(fstr, base); | ||||
| 			cell.Formula = undefined; | ||||
| 			delete cell.Formula; | ||||
| 			if(cell.ArrayRange == "RC") cell.F = rc_to_a1("RC:RC", base); | ||||
| 			else if(cell.ArrayRange) { | ||||
| 				cell.F = rc_to_a1(cell.ArrayRange, base); | ||||
| @ -10921,7 +11116,8 @@ function parse_xlml_data(xml, ss, data, cell, base, styles, csty, row, arrayf, o | ||||
| } | ||||
| 
 | ||||
| function xlml_clean_comment(comment) { | ||||
| 	comment.t = comment.v; | ||||
| 	comment.t = comment.v || ""; | ||||
| 	comment.t = comment.t.replace(/\r\n/g,"\n").replace(/\r/g,"\n"); | ||||
| 	comment.v = comment.w = comment.ixfe = undefined; | ||||
| } | ||||
| 
 | ||||
| @ -11570,7 +11766,14 @@ function write_ws_xlml_wsopts(ws, opts, idx, wb) { | ||||
| 	if(o.length == 0) return ""; | ||||
| 	return writextag("WorksheetOptions", o.join(""), {xmlns:XLMLNS.x}); | ||||
| } | ||||
| /* TODO */ | ||||
| function write_ws_xlml_comment(comments) { | ||||
| 	return comments.map(function(c) { | ||||
| 		// TODO: formatted text
 | ||||
| 		var t = xlml_unfixstr(c.t||""); | ||||
| 		var d =writextag("ss:Data", t, {"xmlns":"http://www.w3.org/TR/REC-html40"}); | ||||
| 		return writextag("Comment", d, {"ss:Author":c.a}); | ||||
| 	}).join(""); | ||||
| } | ||||
| function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr){ | ||||
| 	if(!cell || cell.v == undefined && cell.f == undefined) return "<Cell></Cell>"; | ||||
| 
 | ||||
| @ -11613,6 +11816,8 @@ function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr){ | ||||
| 	} | ||||
| 	var m = '<Data ss:Type="' + t + '">' + _v + '</Data>'; | ||||
| 
 | ||||
| 	if((cell.c||[]).length > 0) m += write_ws_xlml_comment(cell.c); | ||||
| 
 | ||||
| 	return writextag("Cell", m, attr); | ||||
| } | ||||
| /* TODO */ | ||||
| @ -13749,11 +13954,12 @@ function write_BIFF2LABEL(r, c, val) { | ||||
| 
 | ||||
| function write_ws_biff_cell(ba, cell, R, C, opts) { | ||||
| 	if(cell.v != null) switch(cell.t) { | ||||
| 		case 'n': | ||||
| 			if((cell.v == (cell.v|0)) && (cell.v >= 0) && (cell.v < 65536)) | ||||
| 				write_biff_rec(ba, 0x0002, write_BIFF2INT(R, C, cell.v)); | ||||
| 		case 'd': case 'n': | ||||
| 			var v = cell.t == 'd' ? datenum(cell.v) : cell.v; | ||||
| 			if((v == (v|0)) && (v >= 0) && (v < 65536)) | ||||
| 				write_biff_rec(ba, 0x0002, write_BIFF2INT(R, C, v)); | ||||
| 			else | ||||
| 				write_biff_rec(ba, 0x0003, write_BIFF2NUMBER(R,C, cell.v)); | ||||
| 				write_biff_rec(ba, 0x0003, write_BIFF2NUMBER(R,C, v)); | ||||
| 			return; | ||||
| 		case 'b': case 'e': write_biff_rec(ba, 0x0005, write_BIFF2BERR(R, C, cell.v, cell.t)); return; | ||||
| 		/* TODO: codepage, sst */ | ||||
| @ -13894,6 +14100,8 @@ var parse_content_xml = (function() { | ||||
| 		var number_format_map = {}; | ||||
| 		var merges = [], mrange = {}, mR = 0, mC = 0; | ||||
| 		var arrayf = []; | ||||
| 		var comments = [], comment = {}; | ||||
| 		var creator = "", creatoridx = 0; | ||||
| 		var rept = 1, isstub = false; | ||||
| 		var i = 0; | ||||
| 		xlmlregex.lastIndex = 0; | ||||
| @ -13939,6 +14147,7 @@ var parse_content_xml = (function() { | ||||
| 					if(C < range.s.c) range.s.c = C; | ||||
| 					if(R < range.s.r) range.s.r = R; | ||||
| 					ctag = parsexmltag(Rn[0], false); | ||||
| 					comments = []; comment = {}; | ||||
| 					q = ({t:ctag['数据类型'] || ctag['value-type'], v:null}); | ||||
| 					if(opts.cellFormula) { | ||||
| 						if(ctag.formula) ctag.formula = unescapexml(ctag.formula); | ||||
| @ -13988,6 +14197,7 @@ var parse_content_xml = (function() { | ||||
| 						q.v = textp || ''; | ||||
| 						isstub = textpidx == 0; | ||||
| 					} | ||||
| 					if(comments.length > 0) { q.c = comments; comments = []; } | ||||
| 					if(textp) q.w = textp; | ||||
| 					if(!isstub || opts.sheetStubs) { | ||||
| 						if(!(opts.sheetRows && opts.sheetRows < R)) { | ||||
| @ -14012,6 +14222,23 @@ var parse_content_xml = (function() { | ||||
| 				else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], true]); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 'annotation': // 14.1 <office:annotation>
 | ||||
| 				if(Rn[1]==='/'){ | ||||
| 					if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp; | ||||
| 					comment.t = textp; | ||||
| 					comment.a = creator; | ||||
| 					comments.push(comment); | ||||
| 				} | ||||
| 				else if(Rn[0].charAt(Rn[0].length-2) !== '/') {state.push([Rn[3], false]);} | ||||
| 				creator = ""; creatorpidx = 0; | ||||
| 				textp = ""; textpidx = 0; | ||||
| 				break; | ||||
| 
 | ||||
| 			case 'creator': // 4.3.2.7 <dc:creator>
 | ||||
| 				if(Rn[1]==='/') { creator = str.slice(creatoridx,Rn.index); } | ||||
| 				else creatoridx = Rn.index + Rn[0].length; | ||||
| 				break; | ||||
| 
 | ||||
| 			/* ignore state */ | ||||
| 			case 'meta': case '元数据': // TODO: <office:meta> <uof:元数据> FODS/UOF
 | ||||
| 			case 'settings': // TODO: <office:settings>
 | ||||
| @ -14027,7 +14254,6 @@ var parse_content_xml = (function() { | ||||
| 			case 'list-style': // 16.30 <text:list-style>
 | ||||
| 			case 'form': // 13.13 <form:form>
 | ||||
| 			case 'dde-links': // 9.8 <table:dde-links>
 | ||||
| 			case 'annotation': // 14.1 <office:annotation>
 | ||||
| 			case 'event-listeners': // TODO
 | ||||
| 				if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;} | ||||
| 				else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], false]); | ||||
| @ -14134,7 +14360,7 @@ var parse_content_xml = (function() { | ||||
| 			case 'span': break; // <text:span>
 | ||||
| 			case 'line-break': break; // 6.1.5 <text:line-break>
 | ||||
| 			case 'p': case '文本串': | ||||
| 				if(Rn[1]==='/') textp = parse_text_p(str.slice(textpidx,Rn.index), textptag); | ||||
| 				if(Rn[1]==='/') textp = (textp.length > 0 ? textp + "\n" : "") + parse_text_p(str.slice(textpidx,Rn.index), textptag); | ||||
| 				else { textptag = parsexmltag(Rn[0], false); textpidx = Rn.index + Rn[0].length; } | ||||
| 				break; // <text:p>
 | ||||
| 			case 's': break; // <text:s>
 | ||||
| @ -14188,7 +14414,6 @@ var parse_content_xml = (function() { | ||||
| 			case 'event-listener': // TODO
 | ||||
| 			/* TODO: FODS Properties */ | ||||
| 			case 'initial-creator': | ||||
| 			case 'creator': | ||||
| 			case 'creation-date': | ||||
| 			case 'generator': | ||||
| 			case 'document-statistic': | ||||
| @ -14605,6 +14830,7 @@ function parse_zip(zip, opts) { | ||||
| 	return out; | ||||
| } | ||||
| function write_zip(wb, opts) { | ||||
| 	_shapeid = 1024; | ||||
| 	if(opts.bookType == "ods") return write_ods(wb, opts); | ||||
| 	if(wb && !wb.SSF) { | ||||
| 		wb.SSF = SSF.get_table(); | ||||
| @ -14661,8 +14887,26 @@ f = "docProps/app.xml"; | ||||
| 		f = "xl/worksheets/sheet" + rId + "." + wbext; | ||||
| 		var wsrels = {'!id':{}}; | ||||
| 		zip.file(f, write_ws(rId-1, f, opts, wb, wsrels)); | ||||
| 
 | ||||
| 		ct.sheets.push(f); | ||||
| 		add_rels(opts.wbrels, rId, "worksheets/sheet" + rId + "." + wbext, RELS.WS[0]); | ||||
| 		add_rels(opts.wbrels, -1, "worksheets/sheet" + rId + "." + wbext, RELS.WS[0]); | ||||
| 
 | ||||
| 		var ws = wb.Sheets[wb.SheetNames[rId-1]]; | ||||
| 		if(ws) { | ||||
| 			var comments = ws['!comments']; | ||||
| 			if(comments && comments.length > 0) { | ||||
| 				var cf = "xl/comments" + rId + "." + wbext; | ||||
| 				zip.file(cf, write_cmnt(comments, cf, opts)); | ||||
| 				ct.comments.push(cf); | ||||
| 				add_rels(wsrels, -1, "../comments" + rId + "." + wbext, RELS.CMNT); | ||||
| 			} | ||||
| 			if(ws['!legacy']) { | ||||
| 				zip.file("xl/drawings/vmlDrawing" + (rId) + ".vml", write_comments_vml(rId, ws['!comments'])); | ||||
| 			} | ||||
| 			delete ws['!comments']; | ||||
| 			delete ws['!legacy']; | ||||
| 		} | ||||
| 
 | ||||
| 		if(wsrels['!id'].rId1) zip.file(get_rels_path(f), write_rels(wsrels)); // get_rels_path('')
 | ||||
| 	} | ||||
| 
 | ||||
| @ -14670,7 +14914,7 @@ f = "docProps/app.xml"; | ||||
| 		f = "xl/sharedStrings." + wbext; | ||||
| 		zip.file(f, write_sst(opts.Strings, f, opts)); | ||||
| 		ct.strs.push(f); | ||||
| 		add_rels(opts.wbrels, ++rId, "sharedStrings." + wbext, RELS.SST); | ||||
| 		add_rels(opts.wbrels, -1, "sharedStrings." + wbext, RELS.SST); | ||||
| 	} | ||||
| 
 | ||||
| 	/* TODO: something more intelligent with themes */ | ||||
| @ -14678,20 +14922,20 @@ f = "docProps/app.xml"; | ||||
| 	f = "xl/theme/theme1.xml"; | ||||
| 	zip.file(f, write_theme(wb.Themes, opts)); | ||||
| 	ct.themes.push(f); | ||||
| 	add_rels(opts.wbrels, ++rId, "theme/theme1.xml", RELS.THEME); | ||||
| 	add_rels(opts.wbrels, -1, "theme/theme1.xml", RELS.THEME); | ||||
| 
 | ||||
| 	/* TODO: something more intelligent with styles */ | ||||
| 
 | ||||
| 	f = "xl/styles." + wbext; | ||||
| 	zip.file(f, write_sty(wb, f, opts)); | ||||
| 	ct.styles.push(f); | ||||
| 	add_rels(opts.wbrels, ++rId, "styles." + wbext, RELS.STY); | ||||
| 	add_rels(opts.wbrels, -1, "styles." + wbext, RELS.STY); | ||||
| 
 | ||||
| 	if(wb.vbaraw && vbafmt) { | ||||
| 		f = "xl/vbaProject.bin"; | ||||
| 		zip.file(f, wb.vbaraw); | ||||
| 		ct.vba.push(f); | ||||
| 		add_rels(opts.wbrels, ++rId, "vbaProject.bin", RELS.VBA); | ||||
| 		add_rels(opts.wbrels, -1, "vbaProject.bin", RELS.VBA); | ||||
| 	} | ||||
| 
 | ||||
| 	zip.file("[Content_Types].xml", write_ct(ct, opts)); | ||||
| @ -14795,7 +15039,7 @@ function write_binary_type(out, opts) { | ||||
| function writeSync(wb, opts) { | ||||
| 	check_wb(wb); | ||||
| 	var o = opts||{}; | ||||
| 	switch(o.bookType || 'xlsx') { | ||||
| 	switch(o.bookType || 'xlsb') { | ||||
| 		case 'xml': | ||||
| 		case 'xlml': return write_string_type(write_xlml(wb, o), o); | ||||
| 		case 'slk': | ||||
| @ -14813,21 +15057,20 @@ function writeSync(wb, opts) { | ||||
| } | ||||
| 
 | ||||
| function resolve_book_type(o/*?WriteFileOpts*/) { | ||||
| 	if(!o.bookType) switch(o.file.slice(-5).toLowerCase()) { | ||||
| 	if(!o.bookType) switch(o.file.slice(o.file.lastIndexOf(".")).toLowerCase()) { | ||||
| 		case '.xlsx': o.bookType = 'xlsx'; break; | ||||
| 		case '.xlsm': o.bookType = 'xlsm'; break; | ||||
| 		case '.xlsb': o.bookType = 'xlsb'; break; | ||||
| 		case '.fods': o.bookType = 'fods'; break; | ||||
| 		case '.xlml': o.bookType = 'xlml'; break; | ||||
| 		case '.sylk': o.bookType = 'sylk'; break; | ||||
| 	default: switch(o.file.slice(-4).toLowerCase()) { | ||||
| 		case '.xls': o.bookType = 'biff2'; break; | ||||
| 		case '.xml': o.bookType = 'xml'; break; | ||||
| 		case '.ods': o.bookType = 'ods'; break; | ||||
| 		case '.csv': o.bookType = 'csv'; break; | ||||
| 		case '.dif': o.bookType = 'dif'; break; | ||||
| 		case '.slk': o.bookType = 'sylk'; break; | ||||
| 	}} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| function writeFileSync(wb, filename, opts) { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user