phasing out patterns with side effects

- add ssf test to actions
- support for threaded comments (fixes #2542)
This commit is contained in:
SheetJS 2022-03-14 02:51:33 -04:00
parent f443aa8475
commit 9a3294c955
61 changed files with 2585 additions and 1749 deletions

@ -82,6 +82,7 @@ jobs:
- run: make init
- run: 'cd test_files; make all; cd -'
- run: npm run tests-only
- run: 'cd packages/ssf; npm install; npm run tests-only; cd -'
node:
name: 'node 4+'

@ -35,6 +35,7 @@ jobs:
- run: make init
- run: 'cd test_files; make all; cd -'
- run: npm run tests-only
#- run: 'cd packages/ssf; npm run tests-only; cd -'
node:
name: 'io.js'

@ -51,6 +51,7 @@ jobs:
- run: make init
- run: 'cd test_files; make all; cd -'
- run: npm run tests-only
#- run: 'cd packages/ssf; npm run tests-only; cd -'
# unstable:
# needs: [matrix, stable]

@ -3082,6 +3082,26 @@ ws.A2.c.hidden = true;
ws.A2.c.push({a:"SheetJS", t:"This comment will be hidden"});
```
_Threaded Comments_
Introduced in Excel 365, threaded comments are plain text comment snippets with
author metadata and parent references. They are supported in XLSX and XLSB.
To mark a comment as threaded, each comment part must have a true `T` property:
```js
if(!ws.A1.c) ws.A1.c = [];
ws.A1.c.push({a:"SheetJS", t:"This is not threaded"});
if(!ws.A2.c) ws.A2.c = [];
ws.A2.c.hidden = true;
ws.A2.c.push({a:"SheetJS", t:"This is threaded", T: true});
ws.A2.c.push({a:"JSSheet", t:"This is also threaded", T: true});
```
There is no Active Directory or Office 365 metadata associated with authors in a thread.
#### Sheet Visibility
Excel enables hiding sheets in the lower tab bar. The sheet data is stored in

@ -154,6 +154,7 @@ if(program.all) {
opts.cellStyles = true;
opts.sheetStubs = true;
opts.cellDates = true;
wopts.cellFormula = true;
wopts.cellStyles = true;
wopts.sheetStubs = true;
wopts.bookVBA = true;

@ -253,6 +253,7 @@ var XMLNS = ({
EXT_PROPS: "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties",
CT: 'http://schemas.openxmlformats.org/package/2006/content-types',
RELS: 'http://schemas.openxmlformats.org/package/2006/relationships',
TCMNT: 'http://schemas.microsoft.com/office/spreadsheetml/2018/threadedcomments',
'dc': 'http://purl.org/dc/elements/1.1/',
'dcterms': 'http://purl.org/dc/terms/',
'dcmitype': 'http://purl.org/dc/dcmitype/',

@ -101,9 +101,6 @@ var SummaryPIDSI = {
/*::[*/0x72627262/*::]*/: {}
};
var DocSummaryRE/*:{[key:string]:string}*/ = evert_key(DocSummaryPIDDSI, "n");
var SummaryRE/*:{[key:string]:string}*/ = evert_key(SummaryPIDSI, "n");
/* [MS-XLS] 2.4.63 Country/Region codes */
var CountryEnum = {
/*::[*/0x0001/*::]*/: "US", // United States

@ -49,6 +49,8 @@ var ct2type/*{[string]:string}*/ = ({
/* Comments */
"application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": "comments",
"application/vnd.ms-excel.comments": "comments",
"application/vnd.ms-excel.threadedcomments+xml": "threadedcomments",
"application/vnd.ms-excel.person+xml": "people",
/* Metadata (Stock/Geography and Dynamic Array) */
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml": "metadata",
@ -223,9 +225,9 @@ var CT_LIST = {
function new_ct()/*:any*/ {
return ({
workbooks:[], sheets:[], charts:[], dialogs:[], macros:[],
rels:[], strs:[], comments:[], links:[],
rels:[], strs:[], comments:[], threadedcomments:[], links:[],
coreprops:[], extprops:[], custprops:[], themes:[], styles:[],
calcchains:[], vba: [], drawings: [], metadata: [],
calcchains:[], vba: [], drawings: [], metadata: [], people:[],
TODO:[], xmlns: "" }/*:any*/);
}
@ -259,32 +261,30 @@ var CTYPE_XML_ROOT = writextag('Types', null, {
'xmlns:xsi': XMLNS.xsi
});
var CTYPE_DEFAULTS = [
['xml', 'application/xml'],
['bin', 'application/vnd.ms-excel.sheet.binary.macroEnabled.main'],
['vml', 'application/vnd.openxmlformats-officedocument.vmlDrawing'],
['data', 'application/vnd.openxmlformats-officedocument.model+data'],
/* from test files */
['bmp', 'image/bmp'],
['png', 'image/png'],
['gif', 'image/gif'],
['emf', 'image/x-emf'],
['wmf', 'image/x-wmf'],
['jpg', 'image/jpeg'], ['jpeg', 'image/jpeg'],
['tif', 'image/tiff'], ['tiff', 'image/tiff'],
['pdf', 'application/pdf'],
['rels', 'application/vnd.openxmlformats-package.relationships+xml']
].map(function(x) {
return writextag('Default', null, {'Extension':x[0], 'ContentType': x[1]});
});
function write_ct(ct, opts)/*:string*/ {
var type2ct/*{[string]:Array<string>}*/ = evert_arr(ct2type);
var o/*:Array<string>*/ = [], v;
o[o.length] = (XML_HEADER);
o[o.length] = (CTYPE_XML_ROOT);
o = o.concat(CTYPE_DEFAULTS);
o = o.concat([
['xml', 'application/xml'],
['bin', 'application/vnd.ms-excel.sheet.binary.macroEnabled.main'],
['vml', 'application/vnd.openxmlformats-officedocument.vmlDrawing'],
['data', 'application/vnd.openxmlformats-officedocument.model+data'],
/* from test files */
['bmp', 'image/bmp'],
['png', 'image/png'],
['gif', 'image/gif'],
['emf', 'image/x-emf'],
['wmf', 'image/x-wmf'],
['jpg', 'image/jpeg'], ['jpeg', 'image/jpeg'],
['tif', 'image/tiff'], ['tiff', 'image/tiff'],
['pdf', 'application/pdf'],
['rels', 'application/vnd.openxmlformats-package.relationships+xml']
].map(function(x) {
return writextag('Default', null, {'Extension':x[0], 'ContentType': x[1]});
}));
/* only write first instance */
var f1 = function(w) {
@ -325,8 +325,10 @@ function write_ct(ct, opts)/*:string*/ {
['coreprops', 'extprops', 'custprops'].forEach(f3);
f3('vba');
f3('comments');
f3('threadedcomments');
f3('drawings');
f2('metadata');
f3('people');
if(o.length>2){ o[o.length] = ('</Types>'); o[1]=o[1].replace("/>",">"); }
return o.join("");
}

@ -9,9 +9,31 @@ var RELS = ({
XLINK: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLink",
CXML: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml",
CXMLP: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXmlProps",
CMNT: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments",
CORE_PROPS: "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties",
EXT_PROPS: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties',
CUST_PROPS: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties',
SST: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings",
STY: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
THEME: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme",
CHART: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart",
CHARTEX: "http://schemas.microsoft.com/office/2014/relationships/chartEx",
CS: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet",
WS: [
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet",
"http://purl.oclc.org/ooxml/officeDocument/relationships/worksheet"
],
DS: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet",
MS: "http://schemas.microsoft.com/office/2006/relationships/xlMacrosheet",
IMG: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
DRAW: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
XLMETA: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sheetMetadata",
TCMNT: "http://schemas.microsoft.com/office/2017/10/relationships/threadedComment",
PEOPLE: "http://schemas.microsoft.com/office/2017/10/relationships/person",
VBA: "http://schemas.microsoft.com/office/2006/relationships/vbaProject"
}/*:any*/);
/* 9.3.3 Representing Relationships */
function get_rels_path(file/*:string*/)/*:string*/ {
var n = file.lastIndexOf("/");

@ -18,8 +18,6 @@ var CORE_PROPS/*:Array<Array<string> >*/ = [
["dcterms:modified", "ModifiedDate", 'date']
];
RELS.CORE_PROPS = 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties';
var CORE_PROPS_REGEX/*:Array<RegExp>*/ = /*#__PURE__*/(function() {
var r = new Array(CORE_PROPS.length);
for(var i = 0; i < CORE_PROPS.length; ++i) {
@ -43,15 +41,6 @@ function parse_core_props(data) {
return p;
}
var CORE_PROPS_XML_ROOT = writextag('cp:coreProperties', null, {
//'xmlns': XMLNS.CORE_PROPS,
'xmlns:cp': XMLNS.CORE_PROPS,
'xmlns:dc': XMLNS.dc,
'xmlns:dcterms': XMLNS.dcterms,
'xmlns:dcmitype': XMLNS.dcmitype,
'xmlns:xsi': XMLNS.xsi
});
function cp_doit(f, g, h, o, p) {
if(p[f] != null || g == null || g === "") return;
p[f] = g;
@ -61,7 +50,14 @@ function cp_doit(f, g, h, o, p) {
function write_core_props(cp, _opts) {
var opts = _opts || {};
var o = [XML_HEADER, CORE_PROPS_XML_ROOT], p = {};
var o = [XML_HEADER, writextag('cp:coreProperties', null, {
//'xmlns': XMLNS.CORE_PROPS,
'xmlns:cp': XMLNS.CORE_PROPS,
'xmlns:dc': XMLNS.dc,
'xmlns:dcterms': XMLNS.dcterms,
'xmlns:dcmitype': XMLNS.dcmitype,
'xmlns:xsi': XMLNS.xsi
})], p = {};
if(!cp && !opts.Props) return o.join("");
if(cp) {

@ -14,8 +14,6 @@ var EXT_PROPS/*:Array<Array<string> >*/ = [
["TitlesOfParts", "TitlesOfParts", "raw"]
];
RELS.EXT_PROPS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties';
var PseudoPropsPairs = [
"Worksheets", "SheetNames",
"NamedRanges", "DefinedNames",
@ -89,17 +87,15 @@ function parse_ext_props(data, p, opts) {
return p;
}
var EXT_PROPS_XML_ROOT = writextag('Properties', null, {
'xmlns': XMLNS.EXT_PROPS,
'xmlns:vt': XMLNS.vt
});
function write_ext_props(cp/*::, opts*/)/*:string*/ {
var o/*:Array<string>*/ = [], W = writextag;
if(!cp) cp = {};
cp.Application = "SheetJS";
o[o.length] = (XML_HEADER);
o[o.length] = (EXT_PROPS_XML_ROOT);
o[o.length] = (writextag('Properties', null, {
'xmlns': XMLNS.EXT_PROPS,
'xmlns:vt': XMLNS.vt
}));
EXT_PROPS.forEach(function(f) {
if(cp[f[1]] === undefined) return;

@ -1,6 +1,4 @@
/* 15.2.12.2 Custom File Properties Part */
RELS.CUST_PROPS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties';
var custregex = /<[^>]+>[^<]*/g;
function parse_cust_props(data/*:string*/, opts) {
var p = {}, name = "";
@ -46,13 +44,11 @@ function parse_cust_props(data/*:string*/, opts) {
return p;
}
var CUST_PROPS_XML_ROOT = writextag('Properties', null, {
'xmlns': XMLNS.CUST_PROPS,
'xmlns:vt': XMLNS.vt
});
function write_cust_props(cp/*::, opts*/)/*:string*/ {
var o = [XML_HEADER, CUST_PROPS_XML_ROOT];
var o = [XML_HEADER, writextag('Properties', null, {
'xmlns': XMLNS.CUST_PROPS,
'xmlns:vt': XMLNS.vt
})];
if(!cp) return o.join("");
var pid = 1;
keys(cp).forEach(function custprop(k) { ++pid;

@ -31,9 +31,10 @@ var XLMLDocPropsMap = {
Identifier: 'Identifier', /* NOTE: missing from schema */
Language: 'Language' /* NOTE: missing from schema */
};
var evert_XLMLDPM = evert(XLMLDocPropsMap);
var evert_XLMLDPM;
function xlml_set_prop(Props, tag/*:string*/, val) {
if(!evert_XLMLDPM) evert_XLMLDPM = evert(XLMLDocPropsMap);
tag = evert_XLMLDPM[tag] || tag;
Props[tag] = val;
}

@ -248,7 +248,7 @@ function parse_PropertySet(blob, PIDSI) {
blob.l = start_addr + size; /* step ahead to skip padding */
return PropH;
}
var XLSPSSkip = [ "CodePage", "Thumbnail", "_PID_LINKBASE", "_PID_HLINKS", "SystemIdentifier", "FMTID" ].concat(PseudoPropsPairs);
var XLSPSSkip = [ "CodePage", "Thumbnail", "_PID_LINKBASE", "_PID_HLINKS", "SystemIdentifier", "FMTID" ]; //.concat(PseudoPropsPairs);
function guess_property_type(val/*:any*/)/*:number*/ {
switch(typeof val) {
case "boolean": return 0x0B;
@ -292,7 +292,7 @@ function write_PropertySet(entries, RE, PIDSI) {
for(i = 0; i < entries.length; ++i) {
if(RE && !RE[entries[i][0]]) continue;
if(XLSPSSkip.indexOf(entries[i][0]) > -1) continue;
if(XLSPSSkip.indexOf(entries[i][0]) > -1 || PseudoPropsPairs.indexOf(entries[i][0]) > -1) continue;
if(entries[i][1] == null) continue;
var val = entries[i][1], idx = 0;

@ -213,7 +213,6 @@ function parse_sst_xml(data/*:string*/, opts)/*:SST*/ {
return s;
}
RELS.SST = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings";
var straywsregex = /^\s|\s$|[\t\n\r]/;
function write_sst_xml(sst/*:SST*/, opts)/*:string*/ {
if(!opts.bookSST) return "";

@ -410,8 +410,6 @@ return function parse_sty_xml(data, themes, opts) {
};
})();
RELS.STY = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
function write_sty_xml(wb/*:Workbook*/, opts)/*:string*/ {
var o = [XML_HEADER, writextag('styleSheet', null, {
'xmlns': XMLNS_main[0],

@ -102,11 +102,12 @@ var XLSBFillPTNames = [
"gray125",
"gray0625"
];
var rev_XLSBFillPTNames/*:EvertNumType*/ = (evert(XLSBFillPTNames)/*:any*/);
var rev_XLSBFillPTNames/*:EvertNumType*/
/* TODO: gradient fill representation */
var parse_BrtFill = parsenoop;
function write_BrtFill(fill, o) {
if(!o) o = new_buf(4*3 + 8*7 + 16*1);
if(!rev_XLSBFillPTNames) rev_XLSBFillPTNames = (evert(XLSBFillPTNames)/*:any*/);
var fls/*:number*/ = rev_XLSBFillPTNames[fill.patternType];
if(fls == null) fls = 0x28;
o.write_shift(4, fls);

@ -1,5 +1,3 @@
RELS.THEME = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
/* Even though theme layout is dk1 lt1 dk2 lt2, true order is lt1 dk1 lt2 dk2 */
var XLSXThemeClrScheme = [
'</a:lt1>', '</a:dk1>', '</a:lt2>', '</a:dk2>',

@ -1,150 +0,0 @@
RELS.XLMETA = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sheetMetadata";
function parse_xlmeta_xml(data, name, opts) {
var out = { Types: [] };
if (!data)
return out;
var pass = false;
data.replace(tagregex, function(x, idx) {
var y = parsexmltag(x);
switch (strip_ns(y[0])) {
case "<?xml":
break;
case "<metadata":
case "</metadata>":
break;
case "<metadataTypes":
case "</metadataTypes>":
break;
case "<metadataType":
out.Types.push({ name: y.name });
break;
case "<futureMetadata":
break;
case "</futureMetadata>":
break;
case "<bk>":
break;
case "</bk>":
break;
case "<rc":
break;
case "</rc>":
break;
case "<cellMetadata":
case "</cellMetadata>":
break;
case "<valueMetadata":
break;
case "</valueMetadata>":
break;
case "<extLst":
case "<extLst>":
case "</extLst>":
case "<extLst/>":
break;
case "<ext":
pass = true;
break;
case "</ext>":
pass = false;
break;
default:
if (!pass && opts.WTF)
throw new Error("unrecognized " + y[0] + " in metadata");
}
return x;
});
return out;
}
function write_xlmeta_xml() {
var o = [XML_HEADER];
o.push('<metadata xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:xlrd="http://schemas.microsoft.com/office/spreadsheetml/2017/richdata" xmlns:xda="http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray">\n <metadataTypes count="1">\n <metadataType name="XLDAPR" minSupportedVersion="120000" copy="1" pasteAll="1" pasteValues="1" merge="1" splitFirst="1" rowColShift="1" clearFormats="1" clearComments="1" assign="1" coerce="1" cellMeta="1"/>\n </metadataTypes>\n <futureMetadata name="XLDAPR" count="1">\n <bk>\n <extLst>\n <ext uri="{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}">\n <xda:dynamicArrayProperties fDynamic="1" fCollapsed="0"/>\n </ext>\n </extLst>\n </bk>\n </futureMetadata>\n <cellMetadata count="1">\n <bk>\n <rc t="1" v="0"/>\n </bk>\n </cellMetadata>\n</metadata>');
return o.join("");
}
function parse_BrtMdtinfo(data, length) {
return {
flags: data.read_shift(4),
version: data.read_shift(4),
name: parse_XLWideString(data, length - 8)
};
}
function write_BrtMdtinfo(data) {
var o = new_buf(12 + 2 * data.name.length);
o.write_shift(4, data.flags);
o.write_shift(4, data.version);
write_XLWideString(data.name, o);
return o.slice(0, o.l);
}
function write_BrtMdb(mdb) {
var o = new_buf(4 + 8 * mdb.length);
o.write_shift(4, mdb.length);
for (var i = 0; i < mdb.length; ++i) {
o.write_shift(4, mdb[i][0]);
o.write_shift(4, mdb[i][1]);
}
return o;
}
function write_BrtBeginEsfmd(cnt, name) {
var o = new_buf(8 + 2 * name.length);
o.write_shift(4, cnt);
write_XLWideString(name, o);
return o.slice(0, o.l);
}
function write_BrtBeginEsmdb(cnt, cm) {
var o = new_buf(8);
o.write_shift(4, cnt);
o.write_shift(4, cm ? 1 : 0);
return o;
}
function parse_xlmeta_bin(data, name, _opts) {
var out = { Types: [] };
var opts = _opts || {};
var state = [];
var pass = false;
recordhopper(data, function(val, R, RT) {
switch (RT) {
case 335:
out.Types.push({ name: val.name });
break;
case 51:
break;
case 35:
state.push(RT);
pass = true;
break;
case 36:
state.pop();
pass = false;
break;
default:
if (R.T) {
} else if (!pass || opts.WTF && state[state.length - 1] != 35)
throw new Error("Unexpected record 0x" + RT.toString(16));
}
});
return out;
}
function write_xlmeta_bin() {
var ba = buf_array();
write_record(ba, 332);
write_record(ba, 334, write_UInt32LE(1));
write_record(ba, 335, write_BrtMdtinfo({
name: "XLDAPR",
version: 12e4,
flags: 3496657072
}));
write_record(ba, 336);
write_record(ba, 339, write_BrtBeginEsfmd(1, "XLDAPR"));
write_record(ba, 52);
write_record(ba, 35, write_UInt32LE(514));
write_record(ba, 4096, write_UInt32LE(0));
write_record(ba, 4097, writeuint16(1));
write_record(ba, 36);
write_record(ba, 53);
write_record(ba, 340);
write_record(ba, 337, write_BrtBeginEsmdb(1, true));
write_record(ba, 51, write_BrtMdb([[1, 0]]));
write_record(ba, 338);
write_record(ba, 333);
return ba.end();
}

105
bits/51_xlsbmeta.js Normal file

@ -0,0 +1,105 @@
function parse_BrtMdtinfo(data, length) {
return {
flags: data.read_shift(4),
version: data.read_shift(4),
name: parse_XLWideString(data, length - 8)
};
}
function write_BrtMdtinfo(data) {
var o = new_buf(12 + 2 * data.name.length);
o.write_shift(4, data.flags);
o.write_shift(4, data.version);
write_XLWideString(data.name, o);
return o.slice(0, o.l);
}
function parse_BrtMdb(data, length) {
var out = [];
var cnt = data.read_shift(4);
while (cnt-- > 0)
out.push([data.read_shift(4), data.read_shift(4)]);
return out;
}
function write_BrtMdb(mdb) {
var o = new_buf(4 + 8 * mdb.length);
o.write_shift(4, mdb.length);
for (var i = 0; i < mdb.length; ++i) {
o.write_shift(4, mdb[i][0]);
o.write_shift(4, mdb[i][1]);
}
return o;
}
function write_BrtBeginEsfmd(cnt, name) {
var o = new_buf(8 + 2 * name.length);
o.write_shift(4, cnt);
write_XLWideString(name, o);
return o.slice(0, o.l);
}
function parse_BrtBeginEsmdb(data, length) {
data.l += 4;
return data.read_shift(4) != 0;
}
function write_BrtBeginEsmdb(cnt, cm) {
var o = new_buf(8);
o.write_shift(4, cnt);
o.write_shift(4, cm ? 1 : 0);
return o;
}
function parse_xlmeta_bin(data, name, _opts) {
var out = { Types: [], Cell: [], Value: [] };
var opts = _opts || {};
var state = [];
var pass = false;
var esmdb = 0;
recordhopper(data, function(val, R, RT) {
switch (RT) {
case 335:
out.Types.push({ name: val.name });
break;
case 51:
val.forEach(function(r) {
(esmdb == 1 ? out.Cell : out.Value).push({ type: out.Types[r[0] - 1].name, index: r[1] });
});
break;
case 337:
esmdb = val ? 1 : 0;
break;
case 35:
state.push(RT);
pass = true;
break;
case 36:
state.pop();
pass = false;
break;
default:
if (R.T) {
} else if (!pass || opts.WTF && state[state.length - 1] != 35)
throw new Error("Unexpected record 0x" + RT.toString(16));
}
});
return out;
}
function write_xlmeta_bin() {
var ba = buf_array();
write_record(ba, 332);
write_record(ba, 334, write_UInt32LE(1));
write_record(ba, 335, write_BrtMdtinfo({
name: "XLDAPR",
version: 12e4,
flags: 3496657072
}));
write_record(ba, 336);
write_record(ba, 339, write_BrtBeginEsfmd(1, "XLDAPR"));
write_record(ba, 52);
write_record(ba, 35, write_UInt32LE(514));
write_record(ba, 4096, write_UInt32LE(0));
write_record(ba, 4097, writeuint16(1));
write_record(ba, 36);
write_record(ba, 53);
write_record(ba, 340);
write_record(ba, 337, write_BrtBeginEsmdb(1, true));
write_record(ba, 51, write_BrtMdb([[1, 0]]));
write_record(ba, 338);
write_record(ba, 333);
return ba.end();
}

74
bits/51_xlsxmeta.js Normal file

@ -0,0 +1,74 @@
function parse_xlmeta_xml(data, name, opts) {
var out = { Types: [], Cell: [], Value: [] };
if (!data)
return out;
var pass = false;
var metatype = "";
data.replace(tagregex, function(x, idx) {
var y = parsexmltag(x);
switch (strip_ns(y[0])) {
case "<?xml":
break;
case "<metadata":
case "</metadata>":
break;
case "<metadataTypes":
case "</metadataTypes>":
break;
case "<metadataType":
out.Types.push({ name: y.name });
break;
case "</metadataType>":
break;
case "<futureMetadata":
break;
case "</futureMetadata>":
break;
case "<bk>":
break;
case "</bk>":
break;
case "<rc":
if (metatype == "cell")
out.Cell.push({ type: out.Types[y.t - 1].name, index: +y.v });
else if (metatype == "value")
out.Value.push({ type: out.Types[y.t - 1].name, index: +y.v });
break;
case "</rc>":
break;
case "<cellMetadata":
metatype = "cell";
break;
case "</cellMetadata>":
metatype = "";
break;
case "<valueMetadata":
metatype = "value";
break;
case "</valueMetadata>":
metatype = "";
break;
case "<extLst":
case "<extLst>":
case "</extLst>":
case "<extLst/>":
break;
case "<ext":
pass = true;
break;
case "</ext>":
pass = false;
break;
default:
if (!pass && opts.WTF)
throw new Error("unrecognized " + y[0] + " in metadata");
}
return x;
});
return out;
}
function write_xlmeta_xml() {
var o = [XML_HEADER];
o.push('<metadata xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:xlrd="http://schemas.microsoft.com/office/spreadsheetml/2017/richdata" xmlns:xda="http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray">\n <metadataTypes count="1">\n <metadataType name="XLDAPR" minSupportedVersion="120000" copy="1" pasteAll="1" pasteValues="1" merge="1" splitFirst="1" rowColShift="1" clearFormats="1" clearComments="1" assign="1" coerce="1" cellMeta="1"/>\n </metadataTypes>\n <futureMetadata name="XLDAPR" count="1">\n <bk>\n <extLst>\n <ext uri="{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}">\n <xda:dynamicArrayProperties fDynamic="1" fCollapsed="0"/>\n </ext>\n </extLst>\n </bk>\n </futureMetadata>\n <cellMetadata count="1">\n <bk>\n <rc t="1" v="0"/>\n </bk>\n </cellMetadata>\n</metadata>');
return o.join("");
}

@ -1,7 +1,4 @@
/* 20.5 DrawingML - SpreadsheetML Drawing */
RELS.IMG = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
RELS.DRAW = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing";
/* 20.5.2.35 wsDr CT_Drawing */
function parse_drawing(data, rels/*:any*/) {
if(!data) return "??";

@ -1,6 +1,4 @@
RELS.CMNT = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments";
function sheet_insert_comments(sheet, comments/*:Array<RawComment>*/) {
function sheet_insert_comments(sheet, comments/*:Array<RawComment>*/, threaded/*:boolean*/, people/*:?Array<any>*/) {
var dense = Array.isArray(sheet);
var cell/*:Cell*/;
comments.forEach(function(comment) {
@ -23,8 +21,17 @@ function sheet_insert_comments(sheet, comments/*:Array<RawComment>*/) {
}
if (!cell.c) cell.c = [];
var o/*:Comment*/ = ({a: comment.author, t: comment.t, r: comment.r});
var o/*:Comment*/ = ({a: comment.author, t: comment.t, r: comment.r, T: threaded});
if(comment.h) o.h = comment.h;
/* threaded comments always override */
for(var i = cell.c.length - 1; i >= 0; --i) {
if(!threaded && cell.c[i].T) return;
if(threaded && !cell.c[i].T) cell.c.splice(i, 1);
}
if(threaded && people) for(var i = 0; i < people.length; ++i) {
if(o.a == people[i].id) { o.a = people[i].name || o.a; break; }
}
cell.c.push(o);
});
}

@ -36,21 +36,144 @@ function write_comments_xml(data/*::, opts*/) {
var iauthor/*:Array<string>*/ = [];
o.push("<authors>");
data.forEach(function(x) { x[1].forEach(function(w) { var a = escapexml(w.a);
if(iauthor.indexOf(a) > -1) return;
iauthor.push(a);
o.push("<author>" + a + "</author>");
if(iauthor.indexOf(a) == -1) {
iauthor.push(a);
o.push("<author>" + a + "</author>");
}
if(w.T && w.ID && iauthor.indexOf("tc=" + w.ID) == -1) {
iauthor.push("tc=" + w.ID);
o.push("<author>" + "tc=" + w.ID + "</author>");
}
}); });
if(iauthor.length == 0) { iauthor.push("SheetJ5"); o.push("<author>SheetJ5</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 == null ? "" : escapexml(c.t)));
o.push('</text></comment>');
/* 18.7.3 CT_Comment */
var lastauthor = 0, ts = [];
if(d[1][0] && d[1][0].T && d[1][0].ID) lastauthor = iauthor.indexOf("tc=" + d[1][0].ID);
else d[1].forEach(function(c) {
if(c.a) lastauthor = iauthor.indexOf(escapexml(c.a));
ts.push(c.t||"");
});
o.push('<comment ref="' + d[0] + '" authorId="' + lastauthor + '"><text>');
if(ts.length <= 1) o.push(writetag("t", escapexml(ts[0]||"")));
else {
/* based on Threaded Comments -> Comments projection */
var t = "Comment:\n " + (ts[0]) + "\n";
for(var i = 1; i < ts.length; ++i) t += "Reply:\n " + ts[i] + "\n";
o.push(writetag("t", escapexml(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-XLSX] 2.1.17 */
function parse_tcmnt_xml(data/*:string*/, opts)/*:Array<RawComment>*/ {
var out = [];
var pass = false, comment = {}, tidx = 0;
data.replace(tagregex, function xml_tcmnt(x, idx) {
var y/*:any*/ = parsexmltag(x);
switch(strip_ns(y[0])) {
case '<?xml': break;
/* 2.6.207 ThreadedComments CT_ThreadedComments */
case '<ThreadedComments': break;
case '</ThreadedComments>': break;
/* 2.6.205 threadedComment CT_ThreadedComment */
case '<threadedComment': comment = {author: y.personId, guid: y.id, ref: y.ref, T: 1}; break;
case '</threadedComment>': if(comment.t != null) out.push(comment); break;
case '<text>': case '<text': tidx = idx + x.length; break;
case '</text>': comment.t = data.slice(tidx, idx).replace(/\r\n/g, "\n").replace(/\r/g, "\n"); break;
/* 2.6.206 mentions CT_ThreadedCommentMentions TODO */
case '<mentions': case '<mentions>': pass = true; break;
case '</mentions>': pass = false; break;
/* 2.6.202 mention CT_Mention TODO */
/* 18.2.10 extLst CT_ExtensionList ? */
case '<extLst': case '<extLst>': case '</extLst>': case '<extLst/>': break;
/* 18.2.7 ext CT_Extension + */
case '<ext': pass=true; break;
case '</ext>': pass=false; break;
default: if(!pass && opts.WTF) throw new Error('unrecognized ' + y[0] + ' in threaded comments');
}
return x;
});
return out;
}
function write_tcmnt_xml(comments, people, opts) {
var o = [XML_HEADER, writextag('ThreadedComments', null, { 'xmlns': XMLNS.TCMNT }).replace(/[\/]>/, ">")];
comments.forEach(function(carr) {
var rootid = "";
(carr[1] || []).forEach(function(c, idx) {
if(!c.T) { delete c.ID; return; }
if(c.a && people.indexOf(c.a) == -1) people.push(c.a);
var tcopts = {
ref: carr[0],
id: "{54EE7951-7262-4200-6969-" + ("000000000000" + opts.tcid++).slice(-12) + "}"
};
if(idx == 0) rootid = tcopts.id;
else tcopts.parentId = rootid;
c.ID = tcopts.id;
if(c.a) tcopts.personId = "{54EE7950-7262-4200-6969-" + ("000000000000" + people.indexOf(c.a)).slice(-12) + "}";
o.push(writextag('threadedComment', writetag('text', c.t||""), tcopts));
});
});
o.push('</ThreadedComments>');
return o.join("");
}
/* [MS-XLSX] 2.1.18 */
function parse_people_xml(data/*:string*/, opts) {
var out = [];
var pass = false, tidx = 0;
data.replace(tagregex, function xml_tcmnt(x, idx) {
var y/*:any*/ = parsexmltag(x);
switch(strip_ns(y[0])) {
case '<?xml': break;
/* 2.4.85 personList CT_PersonList */
case '<personList': break;
case '</personList>': break;
/* 2.6.203 person CT_Person TODO: providers */
case '<person': out.push({name: y.displayname, id: y.id }); break;
case '</person>': break;
/* 18.2.10 extLst CT_ExtensionList ? */
case '<extLst': case '<extLst>': case '</extLst>': case '<extLst/>': break;
/* 18.2.7 ext CT_Extension + */
case '<ext': pass=true; break;
case '</ext>': pass=false; break;
default: if(!pass && opts.WTF) throw new Error('unrecognized ' + y[0] + ' in threaded comments');
}
return x;
});
return out;
}
function write_people_xml(people, opts) {
var o = [XML_HEADER, writextag('personList', null, {
'xmlns': XMLNS.TCMNT,
'xmlns:x': XMLNS_main[0]
}).replace(/[\/]>/, ">")];
people.forEach(function(person, idx) {
o.push(writextag('person', null, {
displayName: person,
id: "{54EE7950-7262-4200-6969-" + ("000000000000" + idx).slice(-12) + "}",
userId: person,
providerId: "None"
}));
});
o.push("</personList>");
return o.join("");
}

@ -1,6 +1,3 @@
RELS.DS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet";
RELS.MS = "http://schemas.microsoft.com/office/2006/relationships/xlMacrosheet";
/* macro and dialog sheet stubs */
function parse_ds_bin(/*::data:any, opts, idx:number, rels, wb, themes, styles*/)/*:Worksheet*/ { return {'!type':'dialog'}; }
function parse_ds_xml(/*::data:any, opts, idx:number, rels, wb, themes, styles*/)/*:Worksheet*/ { return {'!type':'dialog'}; }

@ -596,12 +596,12 @@ var Ptg19 = {
/*::[*/0x08/*::]*/: { n:'PtgAttrGoto', f:parse_PtgAttrGoto },
/*::[*/0x10/*::]*/: { n:'PtgAttrSum', f:parse_PtgAttrSum },
/*::[*/0x20/*::]*/: { n:'PtgAttrBaxcel', f:parse_PtgAttrBaxcel },
/*::[*/0x21/*::]*/: { n:'PtgAttrBaxcel', f:parse_PtgAttrBaxcel },
/*::[*/0x40/*::]*/: { n:'PtgAttrSpace', f:parse_PtgAttrSpace },
/*::[*/0x41/*::]*/: { n:'PtgAttrSpaceSemi', f:parse_PtgAttrSpaceSemi },
/*::[*/0x80/*::]*/: { n:'PtgAttrIfError', f:parse_PtgAttrIfError },
/*::[*/0xFF/*::]*/: {}
};
Ptg19[0x21] = Ptg19[0x20];
/* [MS-XLS] 2.5.198.103 ; [MS-XLSB] 2.5.97.87 */
function parse_RgbExtra(blob, length, rgce, opts) {

@ -1,10 +1,6 @@
var strs = {}; // shared strings
var _ssfopts = {}; // spreadsheet formatting options
RELS.WS = [
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet",
"http://purl.oclc.org/ooxml/officeDocument/relationships/worksheet"
];
/*global Map */
var browser_has_Map = typeof Map !== 'undefined';

@ -257,7 +257,8 @@ function write_ws_xml_sheetviews(ws, opts, idx, wb)/*:string*/ {
}
function write_ws_xml_cell(cell/*:Cell*/, ref, ws, opts/*::, idx, wb*/)/*:string*/ {
if(cell.v === undefined && typeof cell.f !== "string" || cell.t === 'z') return "";
if(cell.c) ws['!comments'].push([ref, cell.c]);
if(cell.v === undefined && typeof cell.f !== "string" || cell.t === 'z' && !cell.f) return "";
var vv = "";
var oldt = cell.t, oldv = cell.v;
if(cell.t !== "z") switch(cell.t) {
@ -299,7 +300,6 @@ function write_ws_xml_cell(cell/*:Cell*/, ref, ws, opts/*::, idx, wb*/)/*:string
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]);
if(cell.D) o.cm = 1;
return writextag('c', v, o);
}
@ -478,8 +478,8 @@ return function parse_ws_xml_data(sdata/*:string*/, s, opts, guess/*:Range*/, th
safe_format(p, fmtid, fillid, opts, themes, styles);
if(opts.cellDates && do_format && p.t == 'n' && SSF.is_date(SSF._table[fmtid])) { p.t = 'd'; p.v = numdate(p.v); }
if(tag.cm && opts.xlmeta) {
var cm = (opts.xlmeta.Types||[])[+tag.cm-1];
if(cm && cm.name == 'XLDAPR') p.D = true;
var cm = (opts.xlmeta.Cell||[])[+tag.cm-1];
if(cm && cm.type == 'XLDAPR') p.D = true;
}
if(dense) {
var _r = decode_cell(tag.r);

@ -540,7 +540,7 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
var cm, vm;
recordhopper(data, function ws_parse(val, R, RT) {
recordhopper(data, function ws_parse(val, RR, RT) {
if(end) return;
switch(RT) {
case 0x0094: /* 'BrtWsDim' */
@ -606,7 +606,7 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
}
if(cm) {
if(cm.name == 'XLDAPR') p.D = true;
if(cm.type == 'XLDAPR') p.D = true;
cm = void 0;
}
if(vm) vm = void 0;
@ -624,7 +624,7 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
if(refguess.e.r < row.r) refguess.e.r = row.r;
if(refguess.e.c < C) refguess.e.c = C;
if(cm) {
if(cm.name == 'XLDAPR') p.D = true;
if(cm.type == 'XLDAPR') p.D = true;
cm = void 0;
}
if(vm) vm = void 0;
@ -634,7 +634,7 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
merges.push(val); break;
case 0x0031: { /* 'BrtCellMeta' */
cm = ((opts.xlmeta||{}).Types||[])[val-1];
cm = ((opts.xlmeta||{}).Cell||[])[val-1];
} break;
case 0x01EE: /* 'BrtHLink' */
@ -774,7 +774,7 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
state.pop(); pass = false; break;
default:
if(R.T){/* empty */}
if(RR.T){/* empty */}
else if(!pass || opts.WTF) throw new Error("Unexpected record 0x" + RT.toString(16));
}
}, opts);

@ -1,6 +1,3 @@
RELS.CHART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart";
RELS.CHARTEX = "http://schemas.microsoft.com/office/2014/relationships/chartEx";
function parse_Cache(data/*:string*/)/*:[Array<number|string>, string, ?string]*/ {
var col/*:Array<number|string>*/ = [];
var num = data.match(/^<c:numCache>/);

@ -1,5 +1,3 @@
RELS.CS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet";
/* 18.3 Worksheets also covers Chartsheets */
function parse_cs_xml(data/*:?string*/, opts, idx/*:number*/, rels, wb/*::, themes, styles*/)/*:Worksheet*/ {
if(!data) return data;

@ -891,6 +891,8 @@ function parse_xls_props(cfb/*:CFBContainer*/, props, o) {
function write_xls_props(wb/*:Workbook*/, cfb/*:CFBContainer*/) {
var DSEntries = [], SEntries = [], CEntries = [];
var i = 0, Keys;
var DocSummaryRE/*:{[key:string]:string}*/ = evert_key(DocSummaryPIDDSI, "n");
var SummaryRE/*:{[key:string]:string}*/ = evert_key(SummaryPIDSI, "n");
if(wb.Props) {
Keys = keys(wb.Props);
// $FlowIgnore
@ -903,7 +905,7 @@ function write_xls_props(wb/*:Workbook*/, cfb/*:CFBContainer*/) {
}
var CEntries2 = [];
for(i = 0; i < CEntries.length; ++i) {
if(XLSPSSkip.indexOf(CEntries[i][0]) > -1) continue;
if(XLSPSSkip.indexOf(CEntries[i][0]) > -1 || PseudoPropsPairs.indexOf(CEntries[i][0]) > -1) continue;
if(CEntries[i][1] == null) continue;
CEntries2.push(CEntries[i]);
}

@ -50,7 +50,7 @@ var XLSBRecordEnum = {
/*::[*/0x0030/*::]*/: { /* n:"BrtStyle" */ },
/*::[*/0x0031/*::]*/: { /* n:"BrtCellMeta", */ f:parse_Int32LE },
/*::[*/0x0032/*::]*/: { /* n:"BrtValueMeta" */ },
/*::[*/0x0033/*::]*/: { /* n:"BrtMdb" */ },
/*::[*/0x0033/*::]*/: { /* n:"BrtMdb" */ f:parse_BrtMdb },
/*::[*/0x0034/*::]*/: { /* n:"BrtBeginFmd", */ T:1 },
/*::[*/0x0035/*::]*/: { /* n:"BrtEndFmd", */ T:-1 },
/*::[*/0x0036/*::]*/: { /* n:"BrtBeginMdx", */ T:1 },
@ -276,7 +276,7 @@ var XLSBRecordEnum = {
/*::[*/0x014E/*::]*/: { /* n:"BrtBeginEsmdtinfo", */ T:1 },
/*::[*/0x014F/*::]*/: { /* n:"BrtMdtinfo", */ f:parse_BrtMdtinfo },
/*::[*/0x0150/*::]*/: { /* n:"BrtEndEsmdtinfo", */ T:-1 },
/*::[*/0x0151/*::]*/: { /* n:"BrtBeginEsmdb", */ T:1 },
/*::[*/0x0151/*::]*/: { /* n:"BrtBeginEsmdb", */ f:parse_BrtBeginEsmdb, T:1 },
/*::[*/0x0152/*::]*/: { /* n:"BrtEndEsmdb", */ T:-1 },
/*::[*/0x0153/*::]*/: { /* n:"BrtBeginEsfmd", */ T:1 },
/*::[*/0x0154/*::]*/: { /* n:"BrtEndEsfmd", */ T:-1 },

File diff suppressed because it is too large Load Diff

@ -8,7 +8,7 @@ function fix_opts_func(defaults/*:Array<Array<any> >*/)/*:{(o:any):void}*/ {
};
}
var fix_read_opts = function(opts) {
function fix_read_opts(opts) {
fix_opts_func([
['cellNF', false], /* emit cell number format string as .z */
['cellHTML', true], /* emit html string as .h */
@ -29,9 +29,10 @@ fix_opts_func([
['password',''], /* password */
['WTF', false] /* WTF mode (throws errors) */
])(opts);
};
}
var fix_write_opts = fix_opts_func([
function fix_write_opts(opts) {
fix_opts_func([
['cellDates', false], /* write date cells with type `d` */
['bookSST', false], /* Generate Shared String Table */
@ -41,4 +42,5 @@ var fix_write_opts = fix_opts_func([
['compression', false], /* Use file compression */
['WTF', false] /* WTF mode (throws errors) */
]);
])(opts);
}

@ -13,7 +13,7 @@ function safe_parse_wbrels(wbrels, sheets) {
return !wbrels || wbrels.length === 0 ? null : wbrels;
}
function safe_parse_sheet(zip, path/*:string*/, relsPath/*:string*/, sheet, idx/*:number*/, sheetRels, sheets, stype/*:string*/, opts, wb, themes, styles) {
function safe_parse_sheet(zip, path/*:string*/, relsPath/*:string*/, sheet, idx/*:number*/, sheetRels, sheets, stype/*:string*/, opts, wb, themes, styles, people) {
try {
sheetRels[sheet]=parse_rels(getzipstr(zip, relsPath, true), path);
var data = getzipdata(zip, path);
@ -35,16 +35,22 @@ function safe_parse_sheet(zip, path/*:string*/, relsPath/*:string*/, sheet, idx/
}
sheets[sheet] = _ws;
/* scan rels for comments */
var comments = [];
/* scan rels for comments and threaded comments */
var tcomments = [], tauthors = [];
if(sheetRels && sheetRels[sheet]) keys(sheetRels[sheet]).forEach(function(n) {
var dfile = "";
if(sheetRels[sheet][n].Type == RELS.CMNT) {
var dfile = resolve_path(sheetRels[sheet][n].Target, path);
comments = parse_cmnt(getzipdata(zip, dfile, true), dfile, opts);
dfile = resolve_path(sheetRels[sheet][n].Target, path);
var comments = parse_cmnt(getzipdata(zip, dfile, true), dfile, opts);
if(!comments || !comments.length) return;
sheet_insert_comments(_ws, comments);
sheet_insert_comments(_ws, comments, false);
}
if(sheetRels[sheet][n].Type == RELS.TCMNT) {
dfile = resolve_path(sheetRels[sheet][n].Target, path);
tcomments = tcomments.concat(parse_tcmnt_xml(getzipdata(zip, dfile, true), opts));