forked from sheetjs/sheetjs
		
	Hyperlink cleanup [ci skip]
- XLS normalize URLs and use moniker (fixes #2385) - ODS row heights and column widths (fixes #2378) - DSV parse of files with leading <, deviating from Excel (fixes #1238) - read `ArrayBuffer` with no type (fixes #2316) - removed invalid test files that throw errors due to string length - AOA/JSON null optional map to #NULL! (fixes #1196)
This commit is contained in:
		
							parent
							
								
									8658054872
								
							
						
					
					
						commit
						e59de73f99
					
				| @ -135,7 +135,12 @@ function sheet_add_aoa(_ws/*:?Worksheet*/, data/*:AOA*/, opts/*:?any*/)/*:Worksh | ||||
| 			if(data[R][C] && typeof data[R][C] === 'object' && !Array.isArray(data[R][C]) && !(data[R][C] instanceof Date)) cell = data[R][C]; | ||||
| 			else { | ||||
| 				if(Array.isArray(cell.v)) { cell.f = data[R][C][1]; cell.v = cell.v[0]; } | ||||
| 				if(cell.v === null) { if(cell.f) cell.t = 'n'; else if(!o.sheetStubs) continue; else cell.t = 'z'; } | ||||
| 				if(cell.v === null) { | ||||
| 					if(cell.f) cell.t = 'n'; | ||||
| 					else if(o.nullError) { cell.t = 'e'; cell.v = 0; } | ||||
| 					else if(!o.sheetStubs) continue; | ||||
| 					else cell.t = 'z'; | ||||
| 				} | ||||
| 				else if(typeof cell.v === 'number') cell.t = 'n'; | ||||
| 				else if(typeof cell.v === 'boolean') cell.t = 'b'; | ||||
| 				else if(cell.v instanceof Date) { | ||||
|  | ||||
| @ -57,7 +57,7 @@ function write_rdf(rdf) { | ||||
| } | ||||
| /* TODO: pull properties */ | ||||
| var write_meta_ods/*:{(wb:Workbook, opts:any):string}*/ = (function() { | ||||
| 	var payload = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink" office:version="1.2"><office:meta><meta:generator>Sheet' + 'JS ' + XLSX.version + '</meta:generator></office:meta></office:document-meta>'; | ||||
| 	var payload = '<office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink" office:version="1.2"><office:meta><meta:generator>Sheet' + 'JS ' + XLSX.version + '</meta:generator></office:meta></office:document-meta>'; | ||||
| 	return function wmo(/*:: wb: Workbook, opts: any*/)/*:string*/ { | ||||
| 		return payload; | ||||
| 	}; | ||||
|  | ||||
| @ -523,16 +523,17 @@ function parse_URLMoniker(blob/*::, length, opts*/) { | ||||
| 
 | ||||
| /* [MS-OSHARED] 2.3.7.8 FileMoniker TODO: all fields */ | ||||
| function parse_FileMoniker(blob/*::, length*/) { | ||||
| 	blob.l += 2; //var cAnti = blob.read_shift(2);
 | ||||
| 	var cAnti = blob.read_shift(2); | ||||
| 	var preamble = ""; while(cAnti-- > 0) preamble += "../"; | ||||
| 	var ansiPath = blob.read_shift(0, 'lpstr-ansi'); | ||||
| 	blob.l += 2; //var endServer = blob.read_shift(2);
 | ||||
| 	if(blob.read_shift(2) != 0xDEAD) throw new Error("Bad FileMoniker"); | ||||
| 	var sz = blob.read_shift(4); | ||||
| 	if(sz === 0) return ansiPath.replace(/\\/g,"/"); | ||||
| 	if(sz === 0) return preamble + ansiPath.replace(/\\/g,"/"); | ||||
| 	var bytes = blob.read_shift(4); | ||||
| 	if(blob.read_shift(2) != 3) throw new Error("Bad FileMoniker"); | ||||
| 	var unicodePath = blob.read_shift(bytes>>1, 'utf16le').replace(chr0,""); | ||||
| 	return unicodePath; | ||||
| 	return preamble + unicodePath; | ||||
| } | ||||
| 
 | ||||
| /* [MS-OSHARED] 2.3.7.2 HyperlinkMoniker TODO: all the monikers */ | ||||
| @ -551,6 +552,13 @@ function parse_HyperlinkString(blob/*::, length*/) { | ||||
| 	var o = len > 0 ? blob.read_shift(len, 'utf16le').replace(chr0, "") : ""; | ||||
| 	return o; | ||||
| } | ||||
| function write_HyperlinkString(str/*:string*/, o) { | ||||
| 	if(!o) o = new_buf(6 + str.length * 2); | ||||
| 	o.write_shift(4, 1 + str.length); | ||||
| 	for(var i = 0; i < str.length; ++i) o.write_shift(2, str.charCodeAt(i)); | ||||
| 	o.write_shift(2, 0); | ||||
| 	return o; | ||||
| } | ||||
| 
 | ||||
| /* [MS-OSHARED] 2.3.7.1 Hyperlink Object */ | ||||
| function parse_Hyperlink(blob, length)/*:Hyperlink*/ { | ||||
| @ -571,6 +579,7 @@ function parse_Hyperlink(blob, length)/*:Hyperlink*/ { | ||||
| 	var target = targetFrameName||moniker||oleMoniker||""; | ||||
| 	if(target && Loc) target+="#"+Loc; | ||||
| 	if(!target) target = "#" + Loc; | ||||
| 	if((flags & 0x0002) && target.charAt(0) == "/" && target.charAt(1) != "/") target = "file://" + target; | ||||
| 	var out = ({Target:target}/*:any*/); | ||||
| 	if(guid) out.guid = guid; | ||||
| 	if(fileTime) out.time = fileTime; | ||||
| @ -580,29 +589,31 @@ function parse_Hyperlink(blob, length)/*:Hyperlink*/ { | ||||
| function write_Hyperlink(hl) { | ||||
| 	var out = new_buf(512), i = 0; | ||||
| 	var Target = hl.Target; | ||||
| 	var F = Target.indexOf("#") > -1 ? 0x1f : 0x17; | ||||
| 	if(Target.slice(0,7) == "file://") Target = Target.slice(7); | ||||
| 	var hashidx = Target.indexOf("#"); | ||||
| 	var F = hashidx > -1 ? 0x1f : 0x17; | ||||
| 	switch(Target.charAt(0)) { case "#": F=0x1c; break; case ".": F&=~2; break; } | ||||
| 	out.write_shift(4,2); out.write_shift(4, F); | ||||
| 	var data = [8,6815827,6619237,4849780,83]; for(i = 0; i < data.length; ++i) out.write_shift(4, data[i]); | ||||
| 	if(F == 0x1C) { | ||||
| 		Target = Target.slice(1); | ||||
| 		out.write_shift(4, Target.length + 1); | ||||
| 		for(i = 0; i < Target.length; ++i) out.write_shift(2, Target.charCodeAt(i)); | ||||
| 		out.write_shift(2, 0); | ||||
| 		write_HyperlinkString(Target, out); | ||||
| 	} else if(F & 0x02) { | ||||
| 		data = "e0 c9 ea 79 f9 ba ce 11 8c 82 00 aa 00 4b a9 0b".split(" "); | ||||
| 		for(i = 0; i < data.length; ++i) out.write_shift(1, parseInt(data[i], 16)); | ||||
| 		out.write_shift(4, 2*(Target.length + 1)); | ||||
| 		for(i = 0; i < Target.length; ++i) out.write_shift(2, Target.charCodeAt(i)); | ||||
| 		var Pretarget = hashidx > -1 ? Target.slice(0, hashidx) : Target; | ||||
| 		out.write_shift(4, 2*(Pretarget.length + 1)); | ||||
| 		for(i = 0; i < Pretarget.length; ++i) out.write_shift(2, Pretarget.charCodeAt(i)); | ||||
| 		out.write_shift(2, 0); | ||||
| 		if(F & 0x08) write_HyperlinkString(hashidx > -1 ? Target.slice(hashidx+1): "", out); | ||||
| 	} else { | ||||
| 		data = "03 03 00 00 00 00 00 00 c0 00 00 00 00 00 00 46".split(" "); | ||||
| 		for(i = 0; i < data.length; ++i) out.write_shift(1, parseInt(data[i], 16)); | ||||
| 		var P = 0; | ||||
| 		while(Target.slice(P*3,P*3+3)=="../"||Target.slice(P*3,P*3+3)=="..\\") ++P; | ||||
| 		out.write_shift(2, P); | ||||
| 		out.write_shift(4, Target.length + 1); | ||||
| 		for(i = 0; i < Target.length; ++i) out.write_shift(1, Target.charCodeAt(i) & 0xFF); | ||||
| 		out.write_shift(4, Target.length - 3 * P + 1); | ||||
| 		for(i = 0; i < Target.length - 3 * P; ++i) out.write_shift(1, Target.charCodeAt(i + 3 * P) & 0xFF); | ||||
| 		out.write_shift(1, 0); | ||||
| 		out.write_shift(2, 0xFFFF); | ||||
| 		out.write_shift(2, 0xDEAD); | ||||
|  | ||||
| @ -352,7 +352,7 @@ var SYLK = (function() { | ||||
| 		Au:'ù', Bu:'ú', Cu:'û',         Hu:'ü', | ||||
| 		KC:'Ç', Kc:'ç', q:'æ',  z:'œ',  a:'Æ',  j:'Œ', | ||||
| 		DN:209, Dn:241, Hy:255, | ||||
| 		S:169,  c:170,  R:174,  B:180, | ||||
| 		S:169,  c:170,  R:174,  "B ":180, | ||||
| 		/*::[*/0/*::]*/:176,    /*::[*/1/*::]*/:177,  /*::[*/2/*::]*/:178, | ||||
| 		/*::[*/3/*::]*/:179,    /*::[*/5/*::]*/:181,  /*::[*/6/*::]*/:182, | ||||
| 		/*::[*/7/*::]*/:183,    Q:185,  k:186,  b:208,  i:216,  l:222,  s:240,  y:248, | ||||
|  | ||||
| @ -156,9 +156,9 @@ function parse_ws_xml_hlinks(s, data/*:Array<string>*/, rels) { | ||||
| 		var rel = ((rels || {})['!id']||[])[val.id]; | ||||
| 		if(rel) { | ||||
| 			val.Target = rel.Target; | ||||
| 			if(val.location) val.Target += "#"+val.location; | ||||
| 			if(val.location) val.Target += "#"+unescapexml(val.location); | ||||
| 		} else { | ||||
| 			val.Target = "#" + val.location; | ||||
| 			val.Target = "#" + unescapexml(val.location); | ||||
| 			rel = {Target: val.Target, TargetMode: 'Internal'}; | ||||
| 		} | ||||
| 		val.Rel = rel; | ||||
|  | ||||
| @ -183,6 +183,7 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ { | ||||
| 		else str = utf8read(str); | ||||
| 	} | ||||
| 	var opening = str.slice(0, 1024).toLowerCase(), ishtml = false; | ||||
| 	if((opening.indexOf(">") & 1023) > Math.min((opening.indexOf(",") & 1023), (opening.indexOf(";")&1023))) { var _o = dup(opts); _o.type = "string"; return PRN.to_workbook(str, _o); } | ||||
| 	if(opening.indexOf("<?xml") == -1) ["html", "table", "head", "meta", "script", "style", "div"].forEach(function(tag) { if(opening.indexOf("<" + tag) >= 0) ishtml = true; }); | ||||
| 	if(ishtml) return HTML_.to_workbook(str, opts); | ||||
| 	var Rn; | ||||
| @ -226,7 +227,7 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ { | ||||
| 					} else cursheet[encode_col(c) + encode_row(r)] = cell; | ||||
| 				} | ||||
| 				if(cell.HRef) { | ||||
| 					cell.l = ({Target:cell.HRef}/*:any*/); | ||||
| 					cell.l = ({Target:unescapexml(cell.HRef)}/*:any*/); | ||||
| 					if(cell.HRefScreenTip) cell.l.Tooltip = cell.HRefScreenTip; | ||||
| 					delete cell.HRef; delete cell.HRefScreenTip; | ||||
| 				} | ||||
|  | ||||
| @ -221,7 +221,7 @@ var parse_content_xml = (function() { | ||||
| 			case 'scripts': // 3.12 <office:scripts>
 | ||||
| 			case 'styles': // TODO <office:styles>
 | ||||
| 			case 'font-face-decls': // 3.14 <office:font-face-decls>
 | ||||
| 			case 'master-styles': //3.15.4 <office:master-styles> -- relevant for FODS
 | ||||
| 			case 'master-styles': // 3.15.4 <office:master-styles> -- relevant for FODS
 | ||||
| 				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], true]); | ||||
| 				break; | ||||
| @ -515,11 +515,11 @@ var parse_content_xml = (function() { | ||||
| 				if(Rn[1]!== '/') { | ||||
| 					atag = parsexmltag(Rn[0], false); | ||||
| 					if(!atag.href) break; | ||||
| 					atag.Target = atag.href; delete atag.href; | ||||
| 					atag.Target = unescapexml(atag.href); delete atag.href; | ||||
| 					if(atag.Target.charAt(0) == "#" && atag.Target.indexOf(".") > -1) { | ||||
| 						_Ref = ods_to_csf_3D(atag.Target.slice(1)); | ||||
| 						atag.Target = "#" + _Ref[0] + "!" + _Ref[1]; | ||||
| 					} | ||||
| 					} else if(atag.Target.match(/^\.\.[\\\/]/)) atag.Target = atag.Target.slice(3); | ||||
| 				} | ||||
| 				break; | ||||
| 
 | ||||
|  | ||||
| @ -37,9 +37,17 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 		var R=0,C=0, range = decode_range(ws['!ref']||"A1"); | ||||
| 		var marr/*:Array<Range>*/ = ws['!merges'] || [], mi = 0; | ||||
| 		var dense = Array.isArray(ws); | ||||
| 		for(R = 0; R < range.s.r; ++R) o.push('        <table:table-row></table:table-row>\n'); | ||||
| 		if(ws["!cols"]) { | ||||
| 			for(C = 0; C <= range.e.c; ++C) o.push('        <table:table-column' + (ws["!cols"][C] ? ' table:style-name="co' + ws["!cols"][C].ods + '"' : '') + '></table:table-column>\n'); | ||||
| 		} | ||||
| 		var H = "", ROWS = ws["!rows"]||[]; | ||||
| 		for(R = 0; R < range.s.r; ++R) { | ||||
| 			H = ROWS[R] ? ' table:style-name="ro' + ROWS[R].ods + '"' : ""; | ||||
| 			o.push('        <table:table-row' + H + '></table:table-row>\n'); | ||||
| 		} | ||||
| 		for(; R <= range.e.r; ++R) { | ||||
| 			o.push('        <table:table-row>\n'); | ||||
| 			H = ROWS[R] ? ' table:style-name="ro' + ROWS[R].ods + '"' : ""; | ||||
| 			o.push('        <table:table-row' + H + '>\n'); | ||||
| 			for(C=0; C < range.s.c; ++C) o.push(null_cell_xml); | ||||
| 			for(; C <= range.e.c; ++C) { | ||||
| 				var skip = false, ct = {}, textp = ""; | ||||
| @ -92,8 +100,11 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 				} | ||||
| 				var text_p = write_text_p(textp); | ||||
| 				if(cell.l && cell.l.Target) { | ||||
| 					var _tgt = cell.l.Target; _tgt = _tgt.charAt(0) == "#" ? "#" + csf_to_ods_3D(_tgt.slice(1)) : _tgt; | ||||
| 					text_p = writextag('text:a', text_p, {'xlink:href': _tgt}); | ||||
| 					var _tgt = cell.l.Target; | ||||
| 					_tgt = _tgt.charAt(0) == "#" ? "#" + csf_to_ods_3D(_tgt.slice(1)) : _tgt; | ||||
| 					// TODO: choose correct parent path format based on link delimiters
 | ||||
| 					if(_tgt.charAt(0) != "#" && !_tgt.match(/^\w+:/)) _tgt = '../' + _tgt; | ||||
| 					text_p = writextag('text:a', text_p, {'xlink:href': _tgt.replace(/&/g, "&")}); | ||||
| 				} | ||||
| 				o.push('          ' + writextag('table:table-cell', writextag('text:p', text_p, {}), ct) + '\n'); | ||||
| 			} | ||||
| @ -103,7 +114,7 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 		return o.join(""); | ||||
| 	}; | ||||
| 
 | ||||
| 	var write_automatic_styles_ods = function(o/*:Array<string>*/) { | ||||
| 	var write_automatic_styles_ods = function(o/*:Array<string>*/, wb) { | ||||
| 		o.push(' <office:automatic-styles>\n'); | ||||
| 
 | ||||
| 		o.push('  <number:date-style style:name="N37" number:automatic-order="true">\n'); | ||||
| @ -114,6 +125,41 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 		o.push('   <number:year/>\n'); | ||||
| 		o.push('  </number:date-style>\n'); | ||||
| 
 | ||||
| 		/* column styles */ | ||||
| 		var cidx = 0; | ||||
| 		wb.SheetNames.map(function(n) { return wb.Sheets[n]; }).forEach(function(ws) { | ||||
| 			if(!ws) return; | ||||
| 			if(ws["!cols"]) { | ||||
| 				for(var C = 0; C < ws["!cols"].length; ++C) if(ws["!cols"][C]) { | ||||
| 					var colobj = ws["!cols"][C]; | ||||
| 					if(colobj.width == null && colobj.wpx == null && colobj.wch == null) continue; | ||||
| 					process_col(colobj); | ||||
| 					colobj.ods = cidx; | ||||
| 					var w = ws["!cols"][C].wpx + "px"; | ||||
| 					o.push('  <style:style style:name="co' + cidx + '" style:family="table-column">\n'); | ||||
| 					o.push('   <style:table-column-properties fo:break-before="auto" style:column-width="' + w + '"/>\n'); | ||||
| 					o.push('  </style:style>\n'); | ||||
| 					++cidx; | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 
 | ||||
| 		/* row styles */ | ||||
| 		var ridx = 0; | ||||
| 		wb.SheetNames.map(function(n) { return wb.Sheets[n]; }).forEach(function(ws) { | ||||
| 			if(!ws) return; | ||||
| 			if(ws["!rows"]) { | ||||
| 				for(var R = 0; R < ws["!rows"].length; ++R) if(ws["!rows"][R]) { | ||||
| 					ws["!rows"][R].ods = ridx; | ||||
| 					var h = ws["!rows"][R].hpx + "px"; | ||||
| 					o.push('  <style:style style:name="ro' + ridx + '" style:family="table-row">\n'); | ||||
| 					o.push('   <style:table-row-properties fo:break-before="auto" style:row-height="' + h + '"/>\n'); | ||||
| 					o.push('  </style:style>\n'); | ||||
| 					++ridx; | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 
 | ||||
| 		/* table */ | ||||
| 		o.push('  <style:style style:name="ta1" style:family="table">\n'); // style:master-page-name="mp1">\n');
 | ||||
| 		o.push('   <style:table-properties table:display="true" style:writing-mode="lr-tb"/>\n'); | ||||
| @ -175,9 +221,13 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 			'office:mimetype': "application/vnd.oasis.opendocument.spreadsheet" | ||||
| 		}); | ||||
| 
 | ||||
| 		if(opts.bookType == "fods") o.push('<office:document' + attr + fods + '>\n'); | ||||
| 		else o.push('<office:document-content' + attr  + '>\n'); | ||||
| 		write_automatic_styles_ods(o); | ||||
| 		if(opts.bookType == "fods") { | ||||
| 			o.push('<office:document' + attr + fods + '>\n'); | ||||
| 			o.push(write_meta_ods().replace(/office:document-meta/g, "office:meta")); | ||||
| 			// TODO: settings (equiv of settings.xml for ODS)
 | ||||
| 		} else o.push('<office:document-content' + attr  + '>\n'); | ||||
| 		// o.push('  <office:scripts/>\n');
 | ||||
| 		write_automatic_styles_ods(o, wb); | ||||
| 		o.push('  <office:body>\n'); | ||||
| 		o.push('    <office:spreadsheet>\n'); | ||||
| 		for(var i = 0; i != wb.SheetNames.length; ++i) o.push(write_ws(wb.Sheets[wb.SheetNames[i]], wb, i, opts)); | ||||
| @ -217,7 +267,7 @@ function write_ods(wb/*:any*/, opts/*:any*/) { | ||||
| 
 | ||||
| 	/* TODO: this is hard-coded to satiate excel */ | ||||
| 	f = "meta.xml"; | ||||
| 	zip_add_file(zip, f, write_meta_ods(/*::wb, opts*/)); | ||||
| 	zip_add_file(zip, f, XML_HEADER + write_meta_ods(/*::wb, opts*/)); | ||||
| 	manifest.push([f, "text/xml"]); | ||||
| 	rdf.push([f, "MetadataFile"]); | ||||
| 
 | ||||
|  | ||||
| @ -66,9 +66,9 @@ function read_prn(data, d, o, str) { | ||||
| 
 | ||||
| function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ { | ||||
| 	reset_cp(); | ||||
| 	if(typeof ArrayBuffer !== 'undefined' && data instanceof ArrayBuffer) return readSync(new Uint8Array(data), opts); | ||||
| 	var d = data, n = [0,0,0,0], str = false; | ||||
| 	var o = opts||{}; | ||||
| 	if(typeof ArrayBuffer !== 'undefined' && data instanceof ArrayBuffer) return readSync(new Uint8Array(data), (o = dup(o), o.type = "array", o)); | ||||
| 	var d = data, n = [0,0,0,0], str = false; | ||||
| 	if(o.cellStyles) { o.cellNF = true; o.sheetStubs = true; } | ||||
| 	_ssfopts = {}; | ||||
| 	if(o.dateNF) _ssfopts.dateNF = o.dateNF; | ||||
|  | ||||
| @ -23,13 +23,14 @@ function make_json_row(sheet/*:Worksheet*/, r/*:Range*/, R/*:number*/, cols/*:Ar | ||||
| 		var v = val.v; | ||||
| 		switch(val.t){ | ||||
| 			case 'z': if(v == null) break; continue; | ||||
| 			case 'e': v = void 0; break; | ||||
| 			case 'e': v = (v == 0 ? null : void 0); break; | ||||
| 			case 's': case 'd': case 'b': case 'n': break; | ||||
| 			default: throw new Error('unrecognized type ' + val.t); | ||||
| 		} | ||||
| 		if(hdr[C] != null) { | ||||
| 			if(v == null) { | ||||
| 				if(defval !== undefined) row[hdr[C]] = defval; | ||||
| 				if(val.t == "e" && v === null) row[hdr[C]] = null; | ||||
| 				else if(defval !== undefined) row[hdr[C]] = defval; | ||||
| 				else if(raw && v === null) row[hdr[C]] = null; | ||||
| 				else continue; | ||||
| 			} else { | ||||
| @ -221,6 +222,7 @@ function sheet_add_json(_ws/*:?Worksheet*/, js/*:Array<any>*/, opts)/*:Worksheet | ||||
| 					if(!o.cellDates) { t = 'n'; v = datenum(v); } | ||||
| 					z = (o.dateNF || SSF._table[14]); | ||||
| 				} | ||||
| 				else if(v === null && o.nullError) { t = 'e'; v = 0; } | ||||
| 				if(!cell) ws[ref] = cell = ({t:t, v:v}/*:any*/); | ||||
| 				else { | ||||
| 					cell.t = t; cell.v = v; | ||||
|  | ||||
| @ -1,5 +1,14 @@ | ||||
| #### Hyperlinks | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>Format Support</b> (click to show)</summary> | ||||
| 
 | ||||
| **Cell Hyperlinks**: XLSX/M, XLSB, BIFF8 XLS, XLML, ODS | ||||
| 
 | ||||
| **Tooltips**: XLSX/M, XLSB, BIFF8 XLS, XLML | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| Hyperlinks are stored in the `l` key of cell objects.  The `Target` field of the | ||||
| hyperlink object is the target of the link, including the URI fragment. Tooltips | ||||
| are stored in the `Tooltip` field and are displayed when you move your mouse | ||||
| @ -9,16 +18,55 @@ For example, the following snippet creates a link from cell `A3` to | ||||
| <https://sheetjs.com> with the tip `"Find us @ SheetJS.com!"`: | ||||
| 
 | ||||
| ```js | ||||
| ws['A3'].l = { Target:"https://sheetjs.com", Tooltip:"Find us @ SheetJS.com!" }; | ||||
| ws['A1'].l = { Target:"https://sheetjs.com", Tooltip:"Find us @ SheetJS.com!" }; | ||||
| ``` | ||||
| 
 | ||||
| Note that Excel does not automatically style hyperlinks -- they will generally | ||||
| be displayed as normal text. | ||||
| 
 | ||||
| _Remote Links_ | ||||
| 
 | ||||
| HTTP / HTTPS links can be used directly: | ||||
| 
 | ||||
| ```js | ||||
| ws['A2'].l = { Target:"https://docs.sheetjs.com/#hyperlinks" }; | ||||
| ws['A3'].l = { Target:"http://localhost:7262/yes_localhost_works" }; | ||||
| ``` | ||||
| 
 | ||||
| Excel also supports `mailto` email links with subject line: | ||||
| 
 | ||||
| ```js | ||||
| ws['A4'].l = { Target:"mailto:ignored@dev.null" }; | ||||
| ws['A5'].l = { Target:"mailto:ignored@dev.null?subject=Test Subject" }; | ||||
| ``` | ||||
| 
 | ||||
| _Local Links_ | ||||
| 
 | ||||
| Links to absolute paths should use the `file://` URI scheme: | ||||
| 
 | ||||
| ```js | ||||
| ws['B1'].l = { Target:"file:///SheetJS/t.xlsx" }; /* Link to /SheetJS/t.xlsx */ | ||||
| ws['B2'].l = { Target:"file:///c:/SheetJS.xlsx" }; /* Link to c:\SheetJS.xlsx */ | ||||
| ``` | ||||
| 
 | ||||
| Links to relative paths can be specified without a scheme: | ||||
| 
 | ||||
| ```js | ||||
| ws['B3'].l = { Target:"SheetJS.xlsb" }; /* Link to SheetJS.xlsb */ | ||||
| ws['B4'].l = { Target:"../SheetJS.xlsm" }; /* Link to ../SheetJS.xlsm */ | ||||
| ``` | ||||
| 
 | ||||
| Relative Paths have undefined behavior in the SpreadsheetML 2003 format.  Excel | ||||
| 2019 will treat a `..\` parent mark as two levels up. | ||||
| 
 | ||||
| _Internal Links_ | ||||
| 
 | ||||
| Links where the target is a cell or range or defined name in the same workbook | ||||
| ("Internal Links") are marked with a leading hash character: | ||||
| 
 | ||||
| ```js | ||||
| ws['A2'].l = { Target:"#E2" }; /* link to cell E2 */ | ||||
| ws['C1'].l = { Target:"#E2" }; /* Link to cell E2 */ | ||||
| ws['C2'].l = { Target:"#Sheet2!E2" }; /* Link to cell E2 in sheet Sheet2 */ | ||||
| ws['C3'].l = { Target:"#SomeDefinedName" }; /* Link to Defined Name */ | ||||
| ``` | ||||
| 
 | ||||
|  | ||||
| @ -22,11 +22,12 @@ as the corresponding styles.  Dates are stored as date or numbers.  Array holes | ||||
| and explicit `undefined` values are skipped.  `null` values may be stubbed. All | ||||
| other values are stored as strings.  The function takes an options argument: | ||||
| 
 | ||||
| | Option Name |  Default | Description                                         | | ||||
| | :---------- | :------: | :-------------------------------------------------- | | ||||
| |`dateNF`     |  FMT 14  | Use specified date format in string output          | | ||||
| |`cellDates`  |  false   | Store dates as type `d` (default is `n`)            | | ||||
| |`sheetStubs` |  false   | Create cell objects of type `z` for `null` values   | | ||||
| | Option Name | Default | Description                                          | | ||||
| | :---------- | :-----: | :--------------------------------------------------- | | ||||
| |`dateNF`     |  FMT 14 | Use specified date format in string output           | | ||||
| |`cellDates`  |  false  | Store dates as type `d` (default is `n`)             | | ||||
| |`sheetStubs` |  false  | Create cell objects of type `z` for `null` values    | | ||||
| |`nullError`  |  false  | If true, emit `#NULL!` error cells for `null` values | | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>Examples</b> (click to show)</summary> | ||||
| @ -46,12 +47,13 @@ var ws = XLSX.utils.aoa_to_sheet([ | ||||
| existing worksheet object.  It follows the same process as `aoa_to_sheet` and | ||||
| accepts an options argument: | ||||
| 
 | ||||
| | Option Name |  Default | Description                                         | | ||||
| | :---------- | :------: | :-------------------------------------------------- | | ||||
| |`dateNF`     |  FMT 14  | Use specified date format in string output          | | ||||
| |`cellDates`  |  false   | Store dates as type `d` (default is `n`)            | | ||||
| |`sheetStubs` |  false   | Create cell objects of type `z` for `null` values   | | ||||
| |`origin`     |          | Use specified cell as starting point (see below)    | | ||||
| | Option Name | Default | Description                                          | | ||||
| | :---------- | :-----: | :--------------------------------------------------- | | ||||
| |`dateNF`     |  FMT 14 | Use specified date format in string output           | | ||||
| |`cellDates`  |  false  | Store dates as type `d` (default is `n`)             | | ||||
| |`sheetStubs` |  false  | Create cell objects of type `z` for `null` values    | | ||||
| |`nullError`  |  false  | If true, emit `#NULL!` error cells for `null` values | | ||||
| |`origin`     |         | Use specified cell as starting point (see below)     | | ||||
| 
 | ||||
| `origin` is expected to be one of: | ||||
| 
 | ||||
| @ -110,11 +112,14 @@ default column order is determined by the first appearance of the field using | ||||
| |`dateNF`     |  FMT 14 | Use specified date format in string output           | | ||||
| |`cellDates`  |  false  | Store dates as type `d` (default is `n`)             | | ||||
| |`skipHeader` |  false  | If true, do not include header row in output         | | ||||
| |`nullError`  |  false  | If true, emit `#NULL!` error cells for `null` values | | ||||
| 
 | ||||
| - All fields from each row will be written.  If `header` is an array and it does | ||||
|   not contain a particular field, the key will be appended to the array. | ||||
| - Cell types are deduced from the type of each value.  For example, a `Date` | ||||
|   object will generate a Date cell, while a string will generate a Text cell. | ||||
| - Null values will be skipped by default.  If `nullError` is true, an error cell | ||||
|   corresponding to `#NULL!` will be written to the worksheet. | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>Examples</b> (click to show)</summary> | ||||
| @ -145,13 +150,14 @@ var ws = XLSX.utils.json_to_sheet([ | ||||
| worksheet object.  It follows the same process as `json_to_sheet` and accepts | ||||
| an options argument: | ||||
| 
 | ||||
| | Option Name |  Default | Description                                         | | ||||
| | :---------- | :------: | :-------------------------------------------------- | | ||||
| |`header`     |          | Use specified column order (default `Object.keys`)  | | ||||
| |`dateNF`     |  FMT 14  | Use specified date format in string output          | | ||||
| |`cellDates`  |  false   | Store dates as type `d` (default is `n`)            | | ||||
| |`skipHeader` |  false   | If true, do not include header row in output        | | ||||
| |`origin`     |          | Use specified cell as starting point (see below)    | | ||||
| | Option Name | Default | Description                                          | | ||||
| | :---------- | :-----: | :--------------------------------------------------- | | ||||
| |`header`     |         | Use specified column order (default `Object.keys`)   | | ||||
| |`dateNF`     |  FMT 14 | Use specified date format in string output           | | ||||
| |`cellDates`  |  false  | Store dates as type `d` (default is `n`)             | | ||||
| |`skipHeader` |  false  | If true, do not include header row in output         | | ||||
| |`nullError`  |  false  | If true, emit `#NULL!` error cells for `null` values | | ||||
| |`origin`     |         | Use specified cell as starting point (see below)     | | ||||
| 
 | ||||
| `origin` is expected to be one of: | ||||
| 
 | ||||
|  | ||||
| @ -41,7 +41,7 @@ | ||||
| 		"fflate": "^0.3.8", | ||||
| 		"ssf": "~0.11.2", | ||||
| 		"wmf": "~1.0.1", | ||||
| 		"word": "~0.4.0" | ||||
| 		"word": "~0.3.0" | ||||
| 	}, | ||||
| 	"devDependencies": { | ||||
| 		"@sheetjs/uglify-js": "~2.7.3", | ||||
|  | ||||
| @ -277,9 +277,9 @@ apachepoi_evaluate_formula_with_structured_table_references.xlsx | ||||
| apachepoi_headerFooterTest.xlsx | ||||
| apachepoi_noSharedStringTable.xlsx | ||||
| apachepoi_picture.xlsx | ||||
| apachepoi_poc-shared-strings.xlsx | ||||
| apachepoi_poc-xmlbomb-empty.xlsx | ||||
| apachepoi_poc-xmlbomb.xlsx | ||||
| # apachepoi_poc-shared-strings.xlsx # string length exceeds 32767 chars | ||||
| # apachepoi_poc-xmlbomb-empty.xlsx # string length exceeds 32767 chars | ||||
| # apachepoi_poc-xmlbomb.xlsx # string length exceeds 32767 chars | ||||
| # apachepoi_protected_passtika.xlsx # password | ||||
| apachepoi_ref-56737.xlsx | ||||
| apachepoi_ref2-56737.xlsx | ||||
| @ -777,7 +777,7 @@ apachepoi_46136-NoWarnings.xls | ||||
| apachepoi_46136-WithWarnings.xls | ||||
| apachepoi_46137.xls | ||||
| apachepoi_46250.xls | ||||
| apachepoi_46368.xls | ||||
| # apachepoi_46368.xls # string length exceeds 32767 chars | ||||
| apachepoi_46445.xls | ||||
| apachepoi_46515.xls | ||||
| apachepoi_46670_http.xls | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user