Skip to content

Commit 0099463

Browse files
committed
version bump 0.7.5: more performance
- eliminated functional constructs in hot functions - format try-catch block extracted into new function - cpexcel + codepage updated to 1.2.0 - more efficient (and correct) clean implementation of RGB/HSL/tint algorithms - xlsx binary --all option enables every extra formatting and saving option - column widths parsed and saved (requires cellStyles:true)
1 parent d882757 commit 0099463

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1040
-973
lines changed

bin/xlsx.njs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ program
2222
.option('-n, --sheet-rows <num>', 'Number of rows to process (0=all rows)')
2323
.option('--no-sst', 'do not generate sst')
2424
.option('--perf', 'do not generate output')
25+
.option('--all', 'parse everything; XLS[XMB] write as much as possible')
2526
.option('--dev', 'development mode')
2627
.option('--read', 'read but do not print out contents')
2728
.option('-q, --quiet', 'quiet mode');
@@ -62,6 +63,12 @@ if(program.xlsx || program.xlsm || program.xlsb) {
6263
else if(program.formulae);
6364
else opts.cellFormula = false;
6465

66+
if(program.all) {
67+
opts.cellFormula = true;
68+
opts.cellNF = true;
69+
opts.cellStyles = true;
70+
}
71+
6572
if(program.dev) {
6673
X.verbose = 2;
6774
opts.WTF = true;

bits/01_version.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
XLSX.version = '0.7.4';
1+
XLSX.version = '0.7.5';
File renamed without changes.
File renamed without changes.

bits/36_xmlutils.js renamed to bits/22_xmlutils.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@ function parsexmltag(tag) {
66
var words = tag.split(/\s+/);
77
var z = {'0': words[0]};
88
if(words.length === 1) return z;
9-
(tag.match(attregexg) || []).map(function(x){
10-
var y=x.match(attregex);
11-
y[1] = y[1].replace(/xmlns:/,"xmlns");
12-
z[y[1].replace(/^[a-zA-Z]*:/,"")] = y[2].substr(1,y[2].length-2);
13-
});
9+
var m = tag.match(attregexg), y, j, w, i;
10+
if(m) for(i = 0; i != m.length; ++i) {
11+
y = m[i].match(attregex);
12+
if((j=y[1].indexOf(":")) === -1) z[y[1]] = y[2].substr(1,y[2].length-2);
13+
else {
14+
if(y[1].substr(0,6) === "xmlns:") w = "xmlns"+y[1].substr(6);
15+
else w = y[1].substr(j+1);
16+
z[w] = y[2].substr(1,y[2].length-2);
17+
}
18+
}
1419
return z;
1520
}
1621

@@ -27,7 +32,7 @@ var rencstr = "&<>'\"".split("");
2732
// TODO: CP remap (need to read file version to determine OS)
2833
function unescapexml(text){
2934
var s = text + '';
30-
s = s.replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&amp;/g, "&");
35+
s = s.replace(/&[a-z]*;/g, function($$) { return encodings[$$]; });
3136
return s.replace(/_x([0-9a-fA-F]*)_/g,function(m,c) {return _chr(parseInt(c,16));});
3237
}
3338
function escapexml(text){
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

bits/45_styutils.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
var hex2RGB = function(h) {return h.substr(h[0]==="#"?1:0,6).match(/../g).map(function(x) { return parseInt(x,16); });};
2+
var rgb2Hex = function(rgb) {
3+
for(var i=0,o=1; i!=3; ++i) o = o*256 + (rgb[i]>255?255:rgb[i]<0?0:rgb[i]);
4+
return o.toString(16).toUpperCase().substr(1);
5+
};
6+
7+
var rgb2HSL = function(rgb) {
8+
var R = rgb[0]/255, G = rgb[1]/255, B=rgb[2]/255;
9+
var M = Math.max(R, G, B), m = Math.min(R, G, B), C = M - m;
10+
if(C === 0) return [0, 0, R];
11+
12+
var H6 = 0, S = 0, L2 = (M + m);
13+
S = C / (L2 > 1 ? 2 - L2 : L2);
14+
switch(M){
15+
case R: H6 = ((G - B) / C + 6)%6; break;
16+
case G: H6 = ((B - R) / C + 2); break;
17+
case B: H6 = ((R - G) / C + 4); break;
18+
}
19+
return [H6 / 6, S, L2 / 2];
20+
};
21+
22+
var hsl2RGB = function(hsl){
23+
var H = hsl[0], S = hsl[1], L = hsl[2];
24+
var C = S * 2 * (L < 0.5 ? L : 1 - L), m = L - C/2;
25+
var rgb = [m,m,m], h6 = 6*H;
26+
27+
var X;
28+
if(S !== 0) switch(h6|0) {
29+
case 0: case 6: X = C * h6; rgb[0] += C; rgb[1] += X; break;
30+
case 1: X = C * (2 - h6); rgb[0] += X; rgb[1] += C; break;
31+
case 2: X = C * (h6 - 2); rgb[1] += C; rgb[2] += X; break;
32+
case 3: X = C * (4 - h6); rgb[1] += X; rgb[2] += C; break;
33+
case 4: X = C * (h6 - 4); rgb[2] += C; rgb[0] += X; break;
34+
case 5: X = C * (6 - h6); rgb[2] += X; rgb[0] += C; break;
35+
}
36+
for(var i = 0; i != 3; ++i) rgb[i] = Math.round(rgb[i]*255);
37+
return rgb;
38+
};
39+
40+
/* 18.8.3 bgColor tint algorithm */
41+
function rgb_tint(hex, tint) {
42+
if(tint === 0) return hex;
43+
var hsl = rgb2HSL(hex2RGB(hex));
44+
if (tint < 0) hsl[2] = hsl[2] * (1 + tint);
45+
else hsl[2] = 1 - (1 - hsl[2]) * (1 - tint);
46+
return rgb2Hex(hsl2RGB(hsl));
47+
}
48+
49+
/* 18.3.1.13 width calculations */
50+
var DEF_MDW = 7, MAX_MDW = 15, MIN_MDW = 1, MDW = DEF_MDW;
51+
function width2px(width) { return (( width + ((128/MDW)|0)/256 )* MDW )|0; }
52+
function px2char(px) { return (((px - 5)/MDW * 100 + 0.5)|0)/100; }
53+
function char2width(chr) { return (((chr * MDW + 5)/MDW*256)|0)/256; }
54+
function cycle_width(collw) { return char2width(px2char(width2px(collw))); }
55+
function find_mdw(collw, coll) {
56+
if(cycle_width(collw) != collw) {
57+
for(MDW=DEF_MDW; MDW>MIN_MDW; --MDW) if(cycle_width(collw) === collw) break;
58+
if(MDW === MIN_MDW) for(MDW=DEF_MDW+1; MDW<MAX_MDW; ++MDW) if(cycle_width(collw) === collw) break;
59+
if(MDW === MAX_MDW) MDW = DEF_MDW;
60+
}
61+
}
File renamed without changes.
File renamed without changes.
File renamed without changes.

bits/59_theme.js renamed to bits/49_theme.js

Lines changed: 2 additions & 108 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

bits/66_wscommon.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
var strs = {}; // shared strings
2+
var _ssfopts = {}; // spreadsheet formatting options
3+
4+
RELS.WS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet";
5+
6+
function get_sst_id(sst, str) {
7+
for(var i = 0; i != sst.length; ++i) if(sst[i].t === str) { sst.Count ++; return i; }
8+
sst[sst.length] = {t:str}; sst.Count ++; sst.Unique ++; return sst.length-1;
9+
}
10+
11+
function get_cell_style(styles, cell, opts) {
12+
var z = opts.revssf[cell.z||"General"];
13+
for(var i = 0; i != styles.length; ++i) if(styles[i].numFmtId === z) return i;
14+
styles[styles.length] = {
15+
numFmtId:z,
16+
fontId:0,
17+
fillId:0,
18+
borderId:0,
19+
xfId:0,
20+
applyNumberFormat:1
21+
};
22+
return styles.length-1;
23+
}
24+
25+
function safe_format(p, fmtid, fillid, opts) {
26+
try {
27+
p.w = SSF.format(fmtid,p.v,_ssfopts);
28+
if(opts.cellNF) p.z = SSF._table[fmtid];
29+
} catch(e) { if(opts.WTF) throw e; }
30+
if(fillid) try {
31+
p.s = styles.Fills[fillid];
32+
if (p.s.fgColor && p.s.fgColor.theme) {
33+
p.s.fgColor.rgb = rgb_tint(themes.themeElements.clrScheme[p.s.fgColor.theme].rgb, p.s.fgColor.tint || 0);
34+
if(opts.WTF) p.s.fgColor.raw_rgb = themes.themeElements.clrScheme[p.s.fgColor.theme].rgb;
35+
}
36+
if (p.s.bgColor && p.s.bgColor.theme) {
37+
p.s.bgColor.rgb = rgb_tint(themes.themeElements.clrScheme[p.s.bgColor.theme].rgb, p.s.bgColor.tint || 0);
38+
if(opts.WTF) p.s.bgColor.raw_rgb = themes.themeElements.clrScheme[p.s.bgColor.theme].rgb;
39+
}
40+
} catch(e) { if(opts.WTF) throw e; }
41+
}

bits/72_wsxml.js renamed to bits/67_wsxml.js

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,27 @@ function parse_ws_xml(data, opts, rels) {
1717
});
1818
}
1919

20+
/* 18.3.1.17 cols CT_Cols */
21+
var columns = [];
22+
if(opts.cellStyles && data.match(/<\/cols>/)) {
23+
/* 18.3.1.13 col CT_Col */
24+
var cols = data.match(/<col[^>]*\/>/g);
25+
var seencol = false;
26+
for(var coli = 0; coli != cols.length; ++coli) {
27+
var coll = parsexmltag(cols[coli]);
28+
delete coll[0];
29+
var colm=Number(coll.min)-1, colM=Number(coll.max)-1;
30+
delete coll.min, coll.max;
31+
if(!seencol && coll.width) { seencol = true; find_mdw(+coll.width, coll); }
32+
if(coll.width) {
33+
coll.wpx = width2px(+coll.width);
34+
coll.wch = px2char(coll.wpx);
35+
coll.MDW = MDW;
36+
}
37+
while(colm <= colM) columns[colm++] = coll;
38+
}
39+
}
40+
2041
var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
2142
var sidx = 0;
2243

@@ -34,7 +55,8 @@ function parse_ws_xml(data, opts, rels) {
3455
if(refguess.e.r < row.r - 1) refguess.e.r = row.r - 1;
3556
/* 18.3.1.4 c CT_Cell */
3657
var cells = x.substr(x.indexOf('>')+1).split(/<(?:\w+:)?c /);
37-
for(var ix = 0, c=cells[0]; ix != cells.length; ++ix,c=cells[ix]) {
58+
for(var ix = 0, c=cells[0]; ix != cells.length; ++ix) {
59+
c = cells[ix];
3860
if(c === "" || c.trim() === "") continue;
3961
var cref = c.match(/r=["']([^"']*)["']/), idx = ix;
4062
c = "<c " + c;
@@ -85,21 +107,9 @@ function parse_ws_xml(data, opts, rels) {
85107
if(cf && cf.numFmtId) fmtid = cf.numFmtId;
86108
if(opts.cellStyles && cf && cf.fillId) fillid = cf.fillId;
87109
}
88-
try {
89-
p.w = SSF.format(fmtid,p.v,_ssfopts);
90-
if(opts.cellNF) p.z = SSF._table[fmtid];
91-
if(fillid) {
92-
p.s = styles.Fills[fillid];
93-
if (p.s.fgColor && p.s.fgColor.theme) {
94-
p.s.fgColor.rgb = rgb_tint(themes.themeElements.clrScheme[p.s.fgColor.theme].rgb, p.s.fgColor.tint || 0);
95-
}
96-
if (p.s.bgColor && p.s.bgColor.theme) {
97-
p.s.bgColor.rgb = rgb_tint(themes.themeElements.clrScheme[p.s.bgColor.theme].rgb, p.s.bgColor.tint || 0);
98-
}
99-
}
100-
} catch(e) { if(opts.WTF) throw e; }
110+
safe_format(p, fmtid, fillid, opts);
101111
s[cell.r] = p;
102-
};
112+
}
103113
}
104114

105115
/* 18.3.1.48 hyperlinks CT_Hyperlinks */
@@ -134,9 +144,11 @@ function parse_ws_xml(data, opts, rels) {
134144
}
135145
}
136146
if(mergecells.length > 0) s["!merges"] = mergecells;
147+
if(columns.length > 0) s["!cols"] = columns;
137148
return s;
138149
}
139150

151+
140152
var WS_XML_ROOT = writextag('worksheet', null, {
141153
'xmlns': XMLNS.main[0],
142154
'xmlns:r': XMLNS.r
@@ -176,12 +188,28 @@ var write_ws_xml_data = function(ws, opts, idx, wb) {
176188
return o.join("");
177189
};
178190

191+
var write_ws_cols = function(ws, cols) {
192+
var o = ["<cols>"], col, width;
193+
for(var i = 0; i != cols.length; ++i) {
194+
if(!(col = cols[i])) continue;
195+
var p = {min:i+1,max:i+1};
196+
/* wch (chars), wpx (pixels) */
197+
width = -1;
198+
if(col.wpx) width = px2char(col.wpx);
199+
else if(col.wch) width = col.wch;
200+
if(width > -1) { p.width = char2width(width); p.customWidth= 1; }
201+
o.push(writextag('col', null, p));
202+
}
203+
o.push("</cols>");
204+
return o.join("");
205+
};
206+
179207
var write_ws_xml = function(idx, opts, wb) {
180208
var o = [], s = wb.SheetNames[idx], ws = wb.Sheets[s] || {}, sidx = 0, rdata = "";
181209
o.push(XML_HEADER);
182210
o.push(WS_XML_ROOT);
183211
o.push(writextag('dimension', null, {'ref': ws['!ref'] || 'A1'}));
184-
212+
if((ws['!cols']||[]).length > 0) o.push(write_ws_cols(ws, ws['!cols']));
185213
sidx = o.length;
186214
o.push(writextag('sheetData', null));
187215
if(ws['!ref']) rdata = write_ws_xml_data(ws, opts, idx, wb);

bits/73_wsbin.js renamed to bits/68_wsbin.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,7 @@ var parse_ws_bin = function(data, opts, rels) {
176176
case 'str': p.v = utf8read(val[1]); break;
177177
}
178178
if(opts.cellFormula && val.length > 3) p.f = val[3];
179-
if((cf = styles.CellXf[val[0].iStyleRef])) try {
180-
p.w = SSF.format(cf.ifmt,p.v,_ssfopts);
181-
if(opts.cellNF) p.z = SSF._table[cf.ifmt];
182-
} catch(e) { if(opts.WTF) throw e; }
179+
if((cf = styles.CellXf[val[0].iStyleRef])) safe_format(p,cf.ifmt,null,opts);
183180
s[encode_cell({c:val[0].c,r:row.r})] = p;
184181
if(refguess.s.r > row.r) refguess.s.r = row.r;
185182
if(refguess.s.c > val[0].c) refguess.s.c = val[0].c;

0 commit comments

Comments
 (0)