1
forked from sheetjs/sheetjs

XLSX encoded entities (fixes )

- HTML DOM ingress support formulae (`data-f`)
- Sheet Visibility for ODS / FODS (fixes )
This commit is contained in:
SheetJS 2024-08-19 12:43:37 -04:00
parent 2669c7cf1c
commit 9368a85b5f
7 changed files with 31 additions and 12 deletions

@ -4,6 +4,10 @@ This log is intended to keep track of backwards-incompatible changes, including
but not limited to API changes and file location changes. Minor behavioral but not limited to API changes and file location changes. Minor behavioral
changes may not be included if they are not expected to break existing code. changes may not be included if they are not expected to break existing code.
* Sheet Visibility for ODS / FODS (h/t @edemaine)
* HTML DOM ingress support formulae (`data-f`)
* Proper handling of XLSX encoded entities (h/t @inreoh)
## v0.20.3 ## v0.20.3
* Correct parsing of NUMBERS and ODS merge cells (h/t @s-ashwin) * Correct parsing of NUMBERS and ODS merge cells (h/t @s-ashwin)

@ -67,7 +67,7 @@ var rencoding = /*#__PURE__*/evert(encodings);
// TODO: CP remap (need to read file version to determine OS) // TODO: CP remap (need to read file version to determine OS)
var unescapexml/*:StringConv*/ = /*#__PURE__*/(function() { var unescapexml/*:StringConv*/ = /*#__PURE__*/(function() {
/* 22.4.2.4 bstr (Basic String) */ /* 22.4.2.4 bstr (Basic String) */
var encregex = /&(?:quot|apos|gt|lt|amp|#x?([\da-fA-F]+));/ig, coderegex = /_x([\da-fA-F]{4})_/ig; var encregex = /&(?:quot|apos|gt|lt|amp|#x?([\da-fA-F]+));/ig, coderegex = /_x([\da-fA-F]{4})_/g;
function raw_unescapexml(text/*:string*/)/*:string*/ { function raw_unescapexml(text/*:string*/)/*:string*/ {
var s = text + '', i = s.indexOf("<![CDATA["); var s = text + '', i = s.indexOf("<![CDATA[");
if(i == -1) return s.replace(encregex, function($$, $1) { return encodings[$$]||String.fromCharCode(parseInt($1,$$.indexOf("x")>-1?16:10))||$$; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));}); if(i == -1) return s.replace(encregex, function($$, $1) { return encodings[$$]||String.fromCharCode(parseInt($1,$$.indexOf("x")>-1?16:10))||$$; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});

@ -92,6 +92,7 @@ function make_html_row(ws/*:Worksheet*/, r/*:Range*/, R/*:number*/, o/*:Sheet2HT
// note: data-v is unaffected by the timezone interpretation // note: data-v is unaffected by the timezone interpretation
if(cell.v != null) sp["data-v"] = escapehtml(cell.v instanceof Date ? cell.v.toISOString() : cell.v); if(cell.v != null) sp["data-v"] = escapehtml(cell.v instanceof Date ? cell.v.toISOString() : cell.v);
if(cell.z != null) sp["data-z"] = cell.z; if(cell.z != null) sp["data-z"] = cell.z;
if(cell.f != null) sp["data-f"] = escapehtml(cell.f);
if(cell.l && (cell.l.Target || "#").charAt(0) != "#") w = '<a href="' + escapehtml(cell.l.Target) +'">' + w + '</a>'; if(cell.l && (cell.l.Target || "#").charAt(0) != "#") w = '<a href="' + escapehtml(cell.l.Target) +'">' + w + '</a>';
} }
sp.id = (o.id || "sjs") + "-" + coord; sp.id = (o.id || "sjs") + "-" + coord;
@ -179,6 +180,7 @@ function sheet_add_dom(ws/*:Worksheet*/, table/*:HTMLElement*/, _opts/*:?any*/)/
if (opts.display && is_dom_element_hidden(elt)) continue; if (opts.display && is_dom_element_hidden(elt)) continue;
var v/*:?string*/ = elt.hasAttribute('data-v') ? elt.getAttribute('data-v') : elt.hasAttribute('v') ? elt.getAttribute('v') : htmldecode(elt.innerHTML); var v/*:?string*/ = elt.hasAttribute('data-v') ? elt.getAttribute('data-v') : elt.hasAttribute('v') ? elt.getAttribute('v') : htmldecode(elt.innerHTML);
var z/*:?string*/ = elt.getAttribute('data-z') || elt.getAttribute('z'); var z/*:?string*/ = elt.getAttribute('data-z') || elt.getAttribute('z');
var f/*:?string*/ = elt.hasAttribute('data-f') ? elt.getAttribute('data-f') : elt.hasAttribute('f') ? elt.getAttribute('f') : null;
for(midx = 0; midx < merges.length; ++midx) { for(midx = 0; midx < merges.length; ++midx) {
var m/*:Range*/ = merges[midx]; var m/*:Range*/ = merges[midx];
if(m.s.c == C + or_C && m.s.r < R + or_R && R + or_R <= m.e.r) { C = m.e.c+1 - or_C; midx = -1; } if(m.s.c == C + or_C && m.s.r < R + or_R && R + or_R <= m.e.r) { C = m.e.c+1 - or_C; midx = -1; }
@ -210,6 +212,7 @@ function sheet_add_dom(ws/*:Worksheet*/, table/*:HTMLElement*/, _opts/*:?any*/)/
l = Aelts[Aelti].getAttribute("href"); if(l.charAt(0) != "#") break; l = Aelts[Aelti].getAttribute("href"); if(l.charAt(0) != "#") break;
} }
if(l && l.charAt(0) != "#" && l.slice(0, 11).toLowerCase() != 'javascript:') o.l = ({ Target: l }); if(l && l.charAt(0) != "#" && l.slice(0, 11).toLowerCase() != 'javascript:') o.l = ({ Target: l });
if(f != null) o.f = f;
if(dense) { if(!ws["!data"][R + or_R]) ws["!data"][R + or_R] = []; ws["!data"][R + or_R][C + or_C] = o; } if(dense) { if(!ws["!data"][R + or_R]) ws["!data"][R + or_R] = []; ws["!data"][R + or_R][C + or_C] = o; }
else ws[encode_cell({c:C + or_C, r:R + or_R})] = o; else ws[encode_cell({c:C + or_C, r:R + or_R})] = o;
if(range.e.c < C + or_C) range.e.c = C + or_C; if(range.e.c < C + or_C) range.e.c = C + or_C;

@ -252,11 +252,11 @@ function parse_content_xml(d/*:string*/, _opts, _nfm)/*:Workbook*/ {
var textR = [], oldtextR = []; var textR = [], oldtextR = [];
var R = -1, C = -1, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}}; var R = -1, C = -1, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
var row_ol = 0; var row_ol = 0;
var number_format_map = _nfm || {}, styles = {}; var number_format_map = _nfm || {}, styles = {}, tstyles = {};
var merges/*:Array<Range>*/ = [], mrange = {}, mR = 0, mC = 0; var merges/*:Array<Range>*/ = [], mrange = {}, mR = 0, mC = 0;
var rowinfo/*:Array<RowInfo>*/ = [], rowpeat = 1, colpeat = 1; var rowinfo/*:Array<RowInfo>*/ = [], rowpeat = 1, colpeat = 1;
var arrayf/*:Array<[Range, string]>*/ = []; var arrayf/*:Array<[Range, string]>*/ = [];
var WB = {Names:[], WBProps:{}}; var WB = {Names:[], WBProps:{}, Sheets:[]};
var atag = ({}/*:any*/); var atag = ({}/*:any*/);
var _Ref/*:[string, string]*/ = ["", ""]; var _Ref/*:[string, string]*/ = ["", ""];
var comments/*:Array<Comment>*/ = [], comment/*:Comment*/ = ({}/*:any*/); var comments/*:Array<Comment>*/ = [], comment/*:Comment*/ = ({}/*:any*/);
@ -282,6 +282,10 @@ function parse_content_xml(d/*:string*/, _opts, _nfm)/*:Workbook*/ {
if(typeof JSON !== 'undefined') JSON.stringify(sheetag); if(typeof JSON !== 'undefined') JSON.stringify(sheetag);
SheetNames.push(sheetag.name); SheetNames.push(sheetag.name);
Sheets[sheetag.name] = ws; Sheets[sheetag.name] = ws;
WB.Sheets.push({
/* TODO: CodeName */
Hidden: (tstyles[sheetag["style-name"]] && tstyles[sheetag["style-name"]]["display"] ? (parsexmlbool(tstyles[sheetag["style-name"]]["display"]) ? 0 : 1) : 0)
});
intable = false; intable = false;
} }
else if(Rn[0].charAt(Rn[0].length-2) !== '/') { else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
@ -529,12 +533,16 @@ function parse_content_xml(d/*:string*/, _opts, _nfm)/*:Workbook*/ {
case 'style': { // 16.2 <style:style> case 'style': { // 16.2 <style:style>
var styletag = parsexmltag(Rn[0], false); var styletag = parsexmltag(Rn[0], false);
if(styletag["family"] == "table-cell" && number_format_map[styletag["data-style-name"]]) styles[styletag["name"]] = number_format_map[styletag["data-style-name"]]; if(styletag["family"] == "table-cell" && number_format_map[styletag["data-style-name"]]) styles[styletag["name"]] = number_format_map[styletag["data-style-name"]];
else if(styletag["family"] == "table") tstyles[styletag["name"]] = styletag;
} break; } break;
case 'map': break; // 16.3 <style:map> case 'map': break; // 16.3 <style:map>
case 'font-face': break; // 16.21 <style:font-face> case 'font-face': break; // 16.21 <style:font-face>
case 'paragraph-properties': break; // 17.6 <style:paragraph-properties> case 'paragraph-properties': break; // 17.6 <style:paragraph-properties>
case 'table-properties': break; // 17.15 <style:table-properties> case 'table-properties': { // 17.15 <style:table-properties>
var proptag = parsexmltag(Rn[0], false);
if(styletag && styletag.family == "table") styletag.display = proptag.display;
} break;
case 'table-column-properties': break; // 17.16 <style:table-column-properties> case 'table-column-properties': break; // 17.16 <style:table-column-properties>
case 'table-row-properties': break; // 17.17 <style:table-row-properties> case 'table-row-properties': break; // 17.17 <style:table-row-properties>
case 'table-cell-properties': break; // 17.18 <style:table-cell-properties> case 'table-cell-properties': break; // 17.18 <style:table-cell-properties>

@ -214,7 +214,9 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = /* @__PURE__ */(function
var write_ws = function(ws, wb/*:Workbook*/, i/*:number*/, opts, nfs, date1904)/*:string*/ { var write_ws = function(ws, wb/*:Workbook*/, i/*:number*/, opts, nfs, date1904)/*:string*/ {
/* Section 9 Tables */ /* Section 9 Tables */
var o/*:Array<string>*/ = []; var o/*:Array<string>*/ = [];
o.push(' <table:table table:name="' + escapexml(wb.SheetNames[i]) + '" table:style-name="ta1">\n'); var tstyle = "ta1";
if(((((wb||{}).Workbook||{}).Sheets||[])[i]||{}).Hidden) tstyle = "ta2";
o.push(' <table:table table:name="' + escapexml(wb.SheetNames[i]) + '" table:style-name="' + tstyle + '">\n');
var R=0,C=0, range = decode_range(ws['!ref']||"A1"); var R=0,C=0, range = decode_range(ws['!ref']||"A1");
var marr/*:Array<Range>*/ = ws['!merges'] || [], mi = 0; var marr/*:Array<Range>*/ = ws['!merges'] || [], mi = 0;
var dense = ws["!data"] != null; var dense = ws["!data"] != null;
@ -362,6 +364,9 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = /* @__PURE__ */(function
o.push(' <style:style style:name="ta1" style:family="table" style:master-page-name="mp1">\n'); o.push(' <style:style style:name="ta1" style:family="table" style:master-page-name="mp1">\n');
o.push(' <style:table-properties table:display="true" style:writing-mode="lr-tb"/>\n'); o.push(' <style:table-properties table:display="true" style:writing-mode="lr-tb"/>\n');
o.push(' </style:style>\n'); o.push(' </style:style>\n');
o.push(' <style:style style:name="ta2" style:family="table" style:master-page-name="mp1">\n');
o.push(' <style:table-properties table:display="false" style:writing-mode="lr-tb"/>\n');
o.push(' </style:style>\n');
o.push(' <number:date-style style:name="N37" number:automatic-order="true">\n'); o.push(' <number:date-style style:name="N37" number:automatic-order="true">\n');
o.push(' <number:month number:style="long"/>\n'); o.push(' <number:month number:style="long"/>\n');

@ -142,10 +142,7 @@
"url": "https://git.sheetjs.com/SheetJS/sheetjs" "url": "https://git.sheetjs.com/SheetJS/sheetjs"
}, },
"scripts": { "scripts": {
"pretest": "npm run lint", "test": "make travis",
"test": "npm run tests-only",
"pretest-only": "git submodule init && git submodule update",
"tests-only": "make travis",
"build": "make", "build": "make",
"lint": "make fullint", "lint": "make fullint",
"dtslint": "dtslint types" "dtslint": "dtslint types"

@ -1772,8 +1772,8 @@ describe('roundtrip features', function() {
['xlsx', paths.svxlsx], ['xlsx', paths.svxlsx],
['xlsb', paths.svxlsb], ['xlsb', paths.svxlsb],
['xls', paths.svxls], ['xls', paths.svxls],
['biff5', paths.svxls5] ['biff5', paths.svxls5],
// ['ods', paths.svods] ['ods', paths.svods]
].forEach(function(w) { ].forEach(function(w) {
it(w[0], function() { it(w[0], function() {
var wb1 = X.read(fs.readFileSync(w[1]), {type:TYPE}); var wb1 = X.read(fs.readFileSync(w[1]), {type:TYPE});
@ -1783,7 +1783,9 @@ describe('roundtrip features', function() {
assert.equal(wbs1.length, wbs2.length); assert.equal(wbs1.length, wbs2.length);
for(var i = 0; i < wbs1.length; ++i) { for(var i = 0; i < wbs1.length; ++i) {
assert.equal(wbs1[i].name, wbs2[i].name); assert.equal(wbs1[i].name, wbs2[i].name);
assert.equal(wbs1[i].Hidden, wbs2[i].Hidden); /* NOTE: ODS does not support the equivalent of "Very Hidden" */
if(w[0] != "ods") assert.equal(wbs1[i].Hidden, wbs2[i].Hidden);
else assert.equal(!!wbs1[i].Hidden, !!wbs2[i].Hidden);
} }
}); });
}); });