From f90fbd32e575d11a44a111a95f19cfe8139ccdd8 Mon Sep 17 00:00:00 2001 From: Pieter Sheth-Voss Date: Sun, 19 Mar 2017 02:50:30 -0400 Subject: [PATCH] Parse XLSX Styles some changes picked from #263 --- bits/47_styxml.js | 220 ++++++++++++++++++++++++++++++++++++----- bits/48_stybin.js | 2 +- bits/74_xmlbin.js | 6 +- bits/85_parsezip.js | 4 +- test.js | 79 +++++++-------- xlsx.flow.js | 232 ++++++++++++++++++++++++++++++++++++++------ xlsx.js | 232 ++++++++++++++++++++++++++++++++++++++------ 7 files changed, 647 insertions(+), 128 deletions(-) diff --git a/bits/47_styxml.js b/bits/47_styxml.js index d6ee486..fa4434f 100644 --- a/bits/47_styxml.js +++ b/bits/47_styxml.js @@ -1,5 +1,68 @@ +/* 18.8.5 borders CT_Borders */ +function parse_borders(t, styles, themes, opts) { + styles.Borders = []; + var border = {}, sub_border = {}; + t[0].match(tagregex).forEach(function(x) { + var y = parsexmltag(x); + switch (y[0]) { + case '': case '': break; + + /* 18.8.4 border CT_Border */ + case '': + border = {}; + if (y.diagonalUp) { border.diagonalUp = y.diagonalUp; } + if (y.diagonalDown) { border.diagonalDown = y.diagonalDown; } + styles.Borders.push(border); + break; + case '': break; + + /* note: not in spec, appears to be CT_BorderPr */ + case '': break; + case '': break; + + /* note: not in spec, appears to be CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.43 top CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.6 bottom CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.13 diagonal CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.25 horizontal CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.44 vertical CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.37 start CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.16 end CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.? color CT_Color */ + case '': break; + case '': break; + + default: if(opts && opts.WTF) throw new Error('unrecognized ' + y[0] + ' in borders'); + } + }); +} + /* 18.8.21 fills CT_Fills */ -function parse_fills(t, styles, opts) { +function parse_fills(t, styles, themes, opts) { styles.Fills = []; var fill = {}; t[0].match(tagregex).forEach(function(x) { @@ -11,9 +74,12 @@ function parse_fills(t, styles, opts) { case '': break; case '': styles.Fills.push(fill); fill = {}; break; + /* 18.8.24 gradientFill CT_GradientFill */ + case '': break; + case '': styles.Fills.push(fill); fill = {}; break; + /* 18.8.32 patternFill CT_PatternFill */ - case '': + case '': if(y.patternType) fill.patternType = y.patternType; break; case '': case '': break; @@ -25,7 +91,7 @@ function parse_fills(t, styles, opts) { if(y.theme) fill.bgColor.theme = parseInt(y.theme, 10); if(y.tint) fill.bgColor.tint = parseFloat(y.tint); /* Excel uses ARGB strings */ - if(y.rgb) fill.bgColor.rgb = y.rgb.substring(y.rgb.length - 6); + if(y.rgb) fill.bgColor.rgb = y.rgb.slice(-6); break; case '': case '': break; @@ -35,15 +101,104 @@ function parse_fills(t, styles, opts) { if(y.theme) fill.fgColor.theme = parseInt(y.theme, 10); if(y.tint) fill.fgColor.tint = parseFloat(y.tint); /* Excel uses ARGB strings */ - if(y.rgb) fill.fgColor.rgb = y.rgb.substring(y.rgb.length - 6); + if(y.rgb) fill.fgColor.rgb = y.rgb.slice(-6); break; case '': case '': break; + /* 18.8.38 stop CT_GradientStop */ + case '': break; + case '': break; + + /* 18.8.? color CT_Color */ + case '': break; + case '': break; + default: if(opts && opts.WTF) throw new Error('unrecognized ' + y[0] + ' in fills'); } }); } +/* 18.8.23 fonts CT_Fonts */ +function parse_fonts(t, styles, themes, opts) { + styles.Fonts = []; + var font = {}; + t[0].match(tagregex).forEach(function(x) { + var y = parsexmltag(x); + switch (y[0]) { + case '': case '': break; + + /* 18.8.22 font CT_Font */ + case '': break; + case '': case '': + styles.Fonts.push(font); + font = {}; + break; + + /* 18.8.29 name CT_FontName */ + case '': case '': break; + + /* 18.8.2 b CT_BooleanProperty */ + case '': font.bold = true; break; + + /* 18.8.26 i CT_BooleanProperty */ + case '': font.italic = true; break; + + /* 18.4.13 u CT_UnderlineProperty */ + case '': font.underline = true; break; + + /* 18.4.10 strike CT_BooleanProperty */ + case '': font.strike = true; break; + + /* 18.4.2 outline CT_BooleanProperty */ + case '': font.outline = true; break; + + /* 18.8.36 shadow CT_BooleanProperty */ + case '': font.shadow = true; break; + + /* 18.4.11 sz CT_FontSize */ + case '': case '': break; + + /* 18.4.14 vertAlign CT_VerticalAlignFontProperty */ + case '': case '': break; + + /* 18.8.18 family CT_FontFamily */ + case '': case '': break; + + /* 18.8.35 scheme CT_FontScheme */ + case '': case '': break; + + /* 18.4.1 charset CT_IntProperty TODO */ + case '': case '': break; + + default: if(opts && opts.WTF) throw new Error('unrecognized ' + y[0] + ' in fonts'); + } + }); +} + /* 18.8.31 numFmts CT_NumFmts */ function parse_numFmts(t, styles, opts) { styles.NumberFmt = []; @@ -68,7 +223,7 @@ function parse_numFmts(t, styles, opts) { function write_numFmts(NF/*:{[n:number]:string}*/, opts) { var o = [""]; [[5,8],[23,26],[41,44],[63,66],[164,392]].forEach(function(r) { - for(var i = r[0]; i <= r[1]; ++i) if(NF[i]) o[o.length] = (writextag('numFmt',null,{numFmtId:i,formatCode:escapexml(NF[i])})); + for(var i = r[0]; i <= r[1]; ++i) if(NF[i] != null) o[o.length] = (writextag('numFmt',null,{numFmtId:i,formatCode:escapexml(NF[i])})); }); if(o.length === 1) return ""; o[o.length] = (""); @@ -79,24 +234,37 @@ function write_numFmts(NF/*:{[n:number]:string}*/, opts) { /* 18.8.10 cellXfs CT_CellXfs */ function parse_cellXfs(t, styles, opts) { styles.CellXf = []; + var xf; t[0].match(tagregex).forEach(function(x) { var y = parsexmltag(x); switch(y[0]) { case '': case '': case '': break; /* 18.8.45 xf CT_Xf */ - case '': break; /* 18.8.1 alignment CT_CellAlignment */ - case '': case '': break; + case '': + var alignment = {}; + if(y.vertical) alignment.vertical = y.vertical; + if(y.horizontal) alignment.horizontal = y.horizontal; + if(y.textRotation != null) alignment.textRotation = y.textRotation; + if(y.indent) alignment.indent = y.indent; + if(y.wrapText) alignment.wrapText = y.wrapText; + xf.alignment = alignment; + break; + case '': break; /* 18.8.33 protection CT_CellProtection */ case '': case '': break; + /* 18.2.10 extLst CT_ExtensionList ? */ case '': break; case ']*)>.*<\/numFmts>/; var cellXfRegex = /]*)>.*<\/cellXfs>/; var fillsRegex = /]*)>.*<\/fills>/; +var fontsRegex = /]*)>.*<\/fonts>/; +var bordersRegex = /]*)>.*<\/borders>/; -return function parse_sty_xml(data, opts) { +return function parse_sty_xml(data, themes, opts) { var styles = {}; if(!data) return styles; /* 18.8.39 styleSheet CT_Stylesheet */ var t; - /* numFmts CT_NumFmts ? */ + /* 18.8.31 numFmts CT_NumFmts ? */ if((t=data.match(numFmtRegex))) parse_numFmts(t, styles, opts); - /* fonts CT_Fonts ? */ - /*if((t=data.match(/]*)>.*<\/fonts>/))) parse_fonts(t, opts);*/ + /* 18.8.23 fonts CT_Fonts ? */ + if((t=data.match(fontsRegex))) parse_fonts(t, styles, themes, opts); - /* fills CT_Fills */ - if((t=data.match(fillsRegex))) parse_fills(t, styles, opts); + /* 18.8.21 fills CT_Fills */ + if((t=data.match(fillsRegex))) parse_fills(t, styles, themes, opts); - /* borders CT_Borders ? */ - /* cellStyleXfs CT_CellStyleXfs ? */ + /* 18.8.5 borders CT_Borders ? */ + if((t=data.match(bordersRegex))) parse_borders(t, styles, themes, opts); - /* cellXfs CT_CellXfs ? */ + /* 18.8.9 cellStyleXfs CT_CellStyleXfs ? */ + + /* 18.8.10 cellXfs CT_CellXfs ? */ if((t=data.match(cellXfRegex))) parse_cellXfs(t, styles, opts); - /* dxfs CT_Dxfs ? */ - /* tableStyles CT_TableStyles ? */ - /* colors CT_Colors ? */ - /* extLst CT_ExtensionList ? */ + /* 18.8.15 dxfs CT_Dxfs ? */ + /* 18.8.42 tableStyles CT_TableStyles ? */ + /* 18.8.11 colors CT_Colors ? */ + /* 18.2.10 extLst CT_ExtensionList ? */ return styles; }; diff --git a/bits/48_stybin.js b/bits/48_stybin.js index 88961b7..edbab07 100644 --- a/bits/48_stybin.js +++ b/bits/48_stybin.js @@ -41,7 +41,7 @@ function parse_BrtXF(data, length/*:number*/) { } /* [MS-XLSB] 2.1.7.50 Styles */ -function parse_sty_bin(data, opts) { +function parse_sty_bin(data, themes, opts) { var styles = {}; styles.NumberFmt = ([]/*:any*/); for(var y in SSF._table) styles.NumberFmt[y] = SSF._table[y]; diff --git a/bits/74_xmlbin.js b/bits/74_xmlbin.js index c5f5f70..04000a0 100644 --- a/bits/74_xmlbin.js +++ b/bits/74_xmlbin.js @@ -8,9 +8,9 @@ function parse_ws(data, name/*:string*/, opts, rels, wb, themes, styles)/*:Works return parse_ws_xml((data/*:any*/), opts, rels, wb, themes, styles); } -function parse_sty(data, name/*:string*/, opts) { - if(name.slice(-4)===".bin") return parse_sty_bin((data/*:any*/), opts); - return parse_sty_xml((data/*:any*/), opts); +function parse_sty(data, name/*:string*/, themes, opts) { + if(name.slice(-4)===".bin") return parse_sty_bin((data/*:any*/), themes, opts); + return parse_sty_xml((data/*:any*/), themes, opts); } function parse_theme(data/*:string*/, name/*:string*/, opts) { diff --git a/bits/85_parsezip.js b/bits/85_parsezip.js index 2954f2c..afb7c10 100644 --- a/bits/85_parsezip.js +++ b/bits/85_parsezip.js @@ -48,9 +48,9 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ { strs = []; if(dir.sst) strs=parse_sst(getzipdata(zip, dir.sst.replace(/^\//,'')), dir.sst, opts); - if(dir.style) styles = parse_sty(getzipdata(zip, dir.style.replace(/^\//,'')),dir.style, opts); - if(opts.cellStyles && dir.themes.length) themes = parse_theme(getzipstr(zip, dir.themes[0].replace(/^\//,''), true)||"",dir.themes[0], opts); + + if(dir.style) styles = parse_sty(getzipdata(zip, dir.style.replace(/^\//,'')),dir.style, themes, opts); } var wb = parse_wb(getzipdata(zip, dir.workbooks[0].replace(/^\//,'')), dir.workbooks[0], opts); diff --git a/test.js b/test.js index bd91874..23b7ceb 100644 --- a/test.js +++ b/test.js @@ -191,7 +191,10 @@ describe('should parse test files', function() { it(x + ' [' + ext + ']', function(){ var wb = wbtable[dir + x]; if(!wb) wb = X.readFile(dir + x, opts); - parsetest(x, X.read(X.write(wb, {type:"buffer", bookType:ext.replace(/\./,"")}), {WTF:opts.WTF}), ext.replace(/\./,"") !== "xlsb", ext); + + wb = X.read(X.write(wb, {type:"buffer", bookType:ext.replace(/\./,"")}), {WTF:opts.WTF, cellNF: true}); + + parsetest(x, wb, ext.replace(/\./,"") !== "xlsb", ext); }); }); }); @@ -292,7 +295,7 @@ describe('parse options', function() { }); it('should generate cell styles when requested', function() { /* TODO: XLS / XLML */ - [paths.cssxlsx, /*paths.cssxls, paths.cssxml*/].forEach(function(p) { + [paths.cssxlsx /*,paths.cssxls, paths.cssxml*/].forEach(function(p) { var wb = X.readFile(p, {cellStyles:true}); var found = false; wb.SheetNames.forEach(function(s) { @@ -774,31 +777,31 @@ describe('parse features', function() { 'H1:J4', 'H10' /* blocks */ ]; var exp = [ - { patternType: 'darkHorizontal', - fgColor: { theme: 9, raw_rgb: 'F79646' }, - bgColor: { theme: 5, raw_rgb: 'C0504D' } }, - { patternType: 'darkUp', - fgColor: { theme: 3, raw_rgb: 'EEECE1' }, - bgColor: { theme: 7, raw_rgb: '8064A2' } }, - { patternType: 'darkGray', - fgColor: { theme: 3, raw_rgb: 'EEECE1' }, - bgColor: { theme: 1, raw_rgb: 'FFFFFF' } }, - { patternType: 'lightGray', - fgColor: { theme: 6, raw_rgb: '9BBB59' }, - bgColor: { theme: 2, raw_rgb: '1F497D' } }, - { patternType: 'lightDown', - fgColor: { theme: 4, raw_rgb: '4F81BD' }, - bgColor: { theme: 7, raw_rgb: '8064A2' } }, - { patternType: 'lightGrid', - fgColor: { theme: 6, raw_rgb: '9BBB59' }, - bgColor: { theme: 9, raw_rgb: 'F79646' } }, - { patternType: 'lightGrid', - fgColor: { theme: 4, raw_rgb: '4F81BD' }, - bgColor: { theme: 2, raw_rgb: '1F497D' } }, - { patternType: 'lightVertical', - fgColor: { theme: 3, raw_rgb: 'EEECE1' }, - bgColor: { theme: 7, raw_rgb: '8064A2' } } - ]; + { patternType: 'darkHorizontal', + fgColor: { theme: 9, raw_rgb: 'F79646' }, + bgColor: { theme: 5, raw_rgb: 'C0504D' } }, + { patternType: 'darkUp', + fgColor: { theme: 3, raw_rgb: 'EEECE1' }, + bgColor: { theme: 7, raw_rgb: '8064A2' } }, + { patternType: 'darkGray', + fgColor: { theme: 3, raw_rgb: 'EEECE1' }, + bgColor: { theme: 1, raw_rgb: 'FFFFFF' } }, + { patternType: 'lightGray', + fgColor: { theme: 6, raw_rgb: '9BBB59' }, + bgColor: { theme: 2, raw_rgb: '1F497D' } }, + { patternType: 'lightDown', + fgColor: { theme: 4, raw_rgb: '4F81BD' }, + bgColor: { theme: 7, raw_rgb: '8064A2' } }, + { patternType: 'lightGrid', + fgColor: { theme: 6, raw_rgb: '9BBB59' }, + bgColor: { theme: 9, raw_rgb: 'F79646' } }, + { patternType: 'lightGrid', + fgColor: { theme: 4, raw_rgb: '4F81BD' }, + bgColor: { theme: 2, raw_rgb: '1F497D' } }, + { patternType: 'lightVertical', + fgColor: { theme: 3, raw_rgb: 'EEECE1' }, + bgColor: { theme: 7, raw_rgb: '8064A2' } } + ]; ranges.forEach(function(rng) { it('XLS | ' + rng,function(){cmparr(rn2(rng).map(function(x){ return wsxls[x].s; }));}); it('XLSX | ' + rng,function(){cmparr(rn2(rng).map(function(x){ return wsxlsx[x].s; }));}); @@ -1172,17 +1175,17 @@ describe('corner cases', function() { assert.doesNotThrow(function(x) { return X.SSF.format(f, 12345.6789);}); }); }); - it('SSF oddities', function() { - var ssfdata = require('./misc/ssf.json'); - ssfdata.forEach(function(d) { - for(var j=1;j': case '': break; + + /* 18.8.4 border CT_Border */ + case '': + border = {}; + if (y.diagonalUp) { border.diagonalUp = y.diagonalUp; } + if (y.diagonalDown) { border.diagonalDown = y.diagonalDown; } + styles.Borders.push(border); + break; + case '': break; + + /* note: not in spec, appears to be CT_BorderPr */ + case '': break; + case '': break; + + /* note: not in spec, appears to be CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.43 top CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.6 bottom CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.13 diagonal CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.25 horizontal CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.44 vertical CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.37 start CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.16 end CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.? color CT_Color */ + case '': break; + case '': break; + + default: if(opts && opts.WTF) throw new Error('unrecognized ' + y[0] + ' in borders'); + } + }); +} + /* 18.8.21 fills CT_Fills */ -function parse_fills(t, styles, opts) { +function parse_fills(t, styles, themes, opts) { styles.Fills = []; var fill = {}; t[0].match(tagregex).forEach(function(x) { @@ -5084,9 +5147,12 @@ function parse_fills(t, styles, opts) { case '': break; case '': styles.Fills.push(fill); fill = {}; break; + /* 18.8.24 gradientFill CT_GradientFill */ + case '': break; + case '': styles.Fills.push(fill); fill = {}; break; + /* 18.8.32 patternFill CT_PatternFill */ - case '': + case '': if(y.patternType) fill.patternType = y.patternType; break; case '': case '': break; @@ -5098,7 +5164,7 @@ function parse_fills(t, styles, opts) { if(y.theme) fill.bgColor.theme = parseInt(y.theme, 10); if(y.tint) fill.bgColor.tint = parseFloat(y.tint); /* Excel uses ARGB strings */ - if(y.rgb) fill.bgColor.rgb = y.rgb.substring(y.rgb.length - 6); + if(y.rgb) fill.bgColor.rgb = y.rgb.slice(-6); break; case '': case '': break; @@ -5108,15 +5174,104 @@ function parse_fills(t, styles, opts) { if(y.theme) fill.fgColor.theme = parseInt(y.theme, 10); if(y.tint) fill.fgColor.tint = parseFloat(y.tint); /* Excel uses ARGB strings */ - if(y.rgb) fill.fgColor.rgb = y.rgb.substring(y.rgb.length - 6); + if(y.rgb) fill.fgColor.rgb = y.rgb.slice(-6); break; case '': case '': break; + /* 18.8.38 stop CT_GradientStop */ + case '': break; + case '': break; + + /* 18.8.? color CT_Color */ + case '': break; + case '': break; + default: if(opts && opts.WTF) throw new Error('unrecognized ' + y[0] + ' in fills'); } }); } +/* 18.8.23 fonts CT_Fonts */ +function parse_fonts(t, styles, themes, opts) { + styles.Fonts = []; + var font = {}; + t[0].match(tagregex).forEach(function(x) { + var y = parsexmltag(x); + switch (y[0]) { + case '': case '': break; + + /* 18.8.22 font CT_Font */ + case '': break; + case '': case '': + styles.Fonts.push(font); + font = {}; + break; + + /* 18.8.29 name CT_FontName */ + case '': case '': break; + + /* 18.8.2 b CT_BooleanProperty */ + case '': font.bold = true; break; + + /* 18.8.26 i CT_BooleanProperty */ + case '': font.italic = true; break; + + /* 18.4.13 u CT_UnderlineProperty */ + case '': font.underline = true; break; + + /* 18.4.10 strike CT_BooleanProperty */ + case '': font.strike = true; break; + + /* 18.4.2 outline CT_BooleanProperty */ + case '': font.outline = true; break; + + /* 18.8.36 shadow CT_BooleanProperty */ + case '': font.shadow = true; break; + + /* 18.4.11 sz CT_FontSize */ + case '': case '': break; + + /* 18.4.14 vertAlign CT_VerticalAlignFontProperty */ + case '': case '': break; + + /* 18.8.18 family CT_FontFamily */ + case '': case '': break; + + /* 18.8.35 scheme CT_FontScheme */ + case '': case '': break; + + /* 18.4.1 charset CT_IntProperty TODO */ + case '': case '': break; + + default: if(opts && opts.WTF) throw new Error('unrecognized ' + y[0] + ' in fonts'); + } + }); +} + /* 18.8.31 numFmts CT_NumFmts */ function parse_numFmts(t, styles, opts) { styles.NumberFmt = []; @@ -5141,7 +5296,7 @@ function parse_numFmts(t, styles, opts) { function write_numFmts(NF/*:{[n:number]:string}*/, opts) { var o = [""]; [[5,8],[23,26],[41,44],[63,66],[164,392]].forEach(function(r) { - for(var i = r[0]; i <= r[1]; ++i) if(NF[i]) o[o.length] = (writextag('numFmt',null,{numFmtId:i,formatCode:escapexml(NF[i])})); + for(var i = r[0]; i <= r[1]; ++i) if(NF[i] != null) o[o.length] = (writextag('numFmt',null,{numFmtId:i,formatCode:escapexml(NF[i])})); }); if(o.length === 1) return ""; o[o.length] = (""); @@ -5152,24 +5307,37 @@ function write_numFmts(NF/*:{[n:number]:string}*/, opts) { /* 18.8.10 cellXfs CT_CellXfs */ function parse_cellXfs(t, styles, opts) { styles.CellXf = []; + var xf; t[0].match(tagregex).forEach(function(x) { var y = parsexmltag(x); switch(y[0]) { case '': case '': case '': break; /* 18.8.45 xf CT_Xf */ - case '': break; /* 18.8.1 alignment CT_CellAlignment */ - case '': case '': break; + case '': + var alignment = {}; + if(y.vertical) alignment.vertical = y.vertical; + if(y.horizontal) alignment.horizontal = y.horizontal; + if(y.textRotation != null) alignment.textRotation = y.textRotation; + if(y.indent) alignment.indent = y.indent; + if(y.wrapText) alignment.wrapText = y.wrapText; + xf.alignment = alignment; + break; + case '': break; /* 18.8.33 protection CT_CellProtection */ case '': case '': break; + /* 18.2.10 extLst CT_ExtensionList ? */ case '': break; case ']*)>.*<\/numFmts>/; var cellXfRegex = /]*)>.*<\/cellXfs>/; var fillsRegex = /]*)>.*<\/fills>/; +var fontsRegex = /]*)>.*<\/fonts>/; +var bordersRegex = /]*)>.*<\/borders>/; -return function parse_sty_xml(data, opts) { +return function parse_sty_xml(data, themes, opts) { var styles = {}; if(!data) return styles; /* 18.8.39 styleSheet CT_Stylesheet */ var t; - /* numFmts CT_NumFmts ? */ + /* 18.8.31 numFmts CT_NumFmts ? */ if((t=data.match(numFmtRegex))) parse_numFmts(t, styles, opts); - /* fonts CT_Fonts ? */ - /*if((t=data.match(/]*)>.*<\/fonts>/))) parse_fonts(t, opts);*/ + /* 18.8.23 fonts CT_Fonts ? */ + if((t=data.match(fontsRegex))) parse_fonts(t, styles, themes, opts); - /* fills CT_Fills */ - if((t=data.match(fillsRegex))) parse_fills(t, styles, opts); + /* 18.8.21 fills CT_Fills */ + if((t=data.match(fillsRegex))) parse_fills(t, styles, themes, opts); - /* borders CT_Borders ? */ - /* cellStyleXfs CT_CellStyleXfs ? */ + /* 18.8.5 borders CT_Borders ? */ + if((t=data.match(bordersRegex))) parse_borders(t, styles, themes, opts); - /* cellXfs CT_CellXfs ? */ + /* 18.8.9 cellStyleXfs CT_CellStyleXfs ? */ + + /* 18.8.10 cellXfs CT_CellXfs ? */ if((t=data.match(cellXfRegex))) parse_cellXfs(t, styles, opts); - /* dxfs CT_Dxfs ? */ - /* tableStyles CT_TableStyles ? */ - /* colors CT_Colors ? */ - /* extLst CT_ExtensionList ? */ + /* 18.8.15 dxfs CT_Dxfs ? */ + /* 18.8.42 tableStyles CT_TableStyles ? */ + /* 18.8.11 colors CT_Colors ? */ + /* 18.2.10 extLst CT_ExtensionList ? */ return styles; }; @@ -5288,7 +5460,7 @@ function parse_BrtXF(data, length/*:number*/) { } /* [MS-XLSB] 2.1.7.50 Styles */ -function parse_sty_bin(data, opts) { +function parse_sty_bin(data, themes, opts) { var styles = {}; styles.NumberFmt = ([]/*:any*/); for(var y in SSF._table) styles.NumberFmt[y] = SSF._table[y]; @@ -9707,9 +9879,9 @@ function parse_ws(data, name/*:string*/, opts, rels, wb, themes, styles)/*:Works return parse_ws_xml((data/*:any*/), opts, rels, wb, themes, styles); } -function parse_sty(data, name/*:string*/, opts) { - if(name.slice(-4)===".bin") return parse_sty_bin((data/*:any*/), opts); - return parse_sty_xml((data/*:any*/), opts); +function parse_sty(data, name/*:string*/, themes, opts) { + if(name.slice(-4)===".bin") return parse_sty_bin((data/*:any*/), themes, opts); + return parse_sty_xml((data/*:any*/), themes, opts); } function parse_theme(data/*:string*/, name/*:string*/, opts) { @@ -13337,9 +13509,9 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ { strs = []; if(dir.sst) strs=parse_sst(getzipdata(zip, dir.sst.replace(/^\//,'')), dir.sst, opts); - if(dir.style) styles = parse_sty(getzipdata(zip, dir.style.replace(/^\//,'')),dir.style, opts); - if(opts.cellStyles && dir.themes.length) themes = parse_theme(getzipstr(zip, dir.themes[0].replace(/^\//,''), true)||"",dir.themes[0], opts); + + if(dir.style) styles = parse_sty(getzipdata(zip, dir.style.replace(/^\//,'')),dir.style, themes, opts); } var wb = parse_wb(getzipdata(zip, dir.workbooks[0].replace(/^\//,'')), dir.workbooks[0], opts); diff --git a/xlsx.js b/xlsx.js index 5971cd0..ea29657 100644 --- a/xlsx.js +++ b/xlsx.js @@ -5019,8 +5019,71 @@ var XLMLPatternTypeMap = { "ThinHorzCross": "lightGrid" }; +/* 18.8.5 borders CT_Borders */ +function parse_borders(t, styles, themes, opts) { + styles.Borders = []; + var border = {}, sub_border = {}; + t[0].match(tagregex).forEach(function(x) { + var y = parsexmltag(x); + switch (y[0]) { + case '': case '': break; + + /* 18.8.4 border CT_Border */ + case '': + border = {}; + if (y.diagonalUp) { border.diagonalUp = y.diagonalUp; } + if (y.diagonalDown) { border.diagonalDown = y.diagonalDown; } + styles.Borders.push(border); + break; + case '': break; + + /* note: not in spec, appears to be CT_BorderPr */ + case '': break; + case '': break; + + /* note: not in spec, appears to be CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.43 top CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.6 bottom CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.13 diagonal CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.25 horizontal CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.44 vertical CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.37 start CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.16 end CT_BorderPr */ + case '': break; + case '': break; + + /* 18.8.? color CT_Color */ + case '': break; + case '': break; + + default: if(opts && opts.WTF) throw new Error('unrecognized ' + y[0] + ' in borders'); + } + }); +} + /* 18.8.21 fills CT_Fills */ -function parse_fills(t, styles, opts) { +function parse_fills(t, styles, themes, opts) { styles.Fills = []; var fill = {}; t[0].match(tagregex).forEach(function(x) { @@ -5032,9 +5095,12 @@ function parse_fills(t, styles, opts) { case '': break; case '': styles.Fills.push(fill); fill = {}; break; + /* 18.8.24 gradientFill CT_GradientFill */ + case '': break; + case '': styles.Fills.push(fill); fill = {}; break; + /* 18.8.32 patternFill CT_PatternFill */ - case '': + case '': if(y.patternType) fill.patternType = y.patternType; break; case '': case '': break; @@ -5046,7 +5112,7 @@ function parse_fills(t, styles, opts) { if(y.theme) fill.bgColor.theme = parseInt(y.theme, 10); if(y.tint) fill.bgColor.tint = parseFloat(y.tint); /* Excel uses ARGB strings */ - if(y.rgb) fill.bgColor.rgb = y.rgb.substring(y.rgb.length - 6); + if(y.rgb) fill.bgColor.rgb = y.rgb.slice(-6); break; case '': case '': break; @@ -5056,15 +5122,104 @@ function parse_fills(t, styles, opts) { if(y.theme) fill.fgColor.theme = parseInt(y.theme, 10); if(y.tint) fill.fgColor.tint = parseFloat(y.tint); /* Excel uses ARGB strings */ - if(y.rgb) fill.fgColor.rgb = y.rgb.substring(y.rgb.length - 6); + if(y.rgb) fill.fgColor.rgb = y.rgb.slice(-6); break; case '': case '': break; + /* 18.8.38 stop CT_GradientStop */ + case '': break; + case '': break; + + /* 18.8.? color CT_Color */ + case '': break; + case '': break; + default: if(opts && opts.WTF) throw new Error('unrecognized ' + y[0] + ' in fills'); } }); } +/* 18.8.23 fonts CT_Fonts */ +function parse_fonts(t, styles, themes, opts) { + styles.Fonts = []; + var font = {}; + t[0].match(tagregex).forEach(function(x) { + var y = parsexmltag(x); + switch (y[0]) { + case '': case '': break; + + /* 18.8.22 font CT_Font */ + case '': break; + case '': case '': + styles.Fonts.push(font); + font = {}; + break; + + /* 18.8.29 name CT_FontName */ + case '': case '': break; + + /* 18.8.2 b CT_BooleanProperty */ + case '': font.bold = true; break; + + /* 18.8.26 i CT_BooleanProperty */ + case '': font.italic = true; break; + + /* 18.4.13 u CT_UnderlineProperty */ + case '': font.underline = true; break; + + /* 18.4.10 strike CT_BooleanProperty */ + case '': font.strike = true; break; + + /* 18.4.2 outline CT_BooleanProperty */ + case '': font.outline = true; break; + + /* 18.8.36 shadow CT_BooleanProperty */ + case '': font.shadow = true; break; + + /* 18.4.11 sz CT_FontSize */ + case '': case '': break; + + /* 18.4.14 vertAlign CT_VerticalAlignFontProperty */ + case '': case '': break; + + /* 18.8.18 family CT_FontFamily */ + case '': case '': break; + + /* 18.8.35 scheme CT_FontScheme */ + case '': case '': break; + + /* 18.4.1 charset CT_IntProperty TODO */ + case '': case '': break; + + default: if(opts && opts.WTF) throw new Error('unrecognized ' + y[0] + ' in fonts'); + } + }); +} + /* 18.8.31 numFmts CT_NumFmts */ function parse_numFmts(t, styles, opts) { styles.NumberFmt = []; @@ -5089,7 +5244,7 @@ function parse_numFmts(t, styles, opts) { function write_numFmts(NF, opts) { var o = [""]; [[5,8],[23,26],[41,44],[63,66],[164,392]].forEach(function(r) { - for(var i = r[0]; i <= r[1]; ++i) if(NF[i]) o[o.length] = (writextag('numFmt',null,{numFmtId:i,formatCode:escapexml(NF[i])})); + for(var i = r[0]; i <= r[1]; ++i) if(NF[i] != null) o[o.length] = (writextag('numFmt',null,{numFmtId:i,formatCode:escapexml(NF[i])})); }); if(o.length === 1) return ""; o[o.length] = (""); @@ -5100,24 +5255,37 @@ function write_numFmts(NF, opts) { /* 18.8.10 cellXfs CT_CellXfs */ function parse_cellXfs(t, styles, opts) { styles.CellXf = []; + var xf; t[0].match(tagregex).forEach(function(x) { var y = parsexmltag(x); switch(y[0]) { case '': case '': case '': break; /* 18.8.45 xf CT_Xf */ - case '': break; /* 18.8.1 alignment CT_CellAlignment */ - case '': case '': break; + case '': + var alignment = {}; + if(y.vertical) alignment.vertical = y.vertical; + if(y.horizontal) alignment.horizontal = y.horizontal; + if(y.textRotation != null) alignment.textRotation = y.textRotation; + if(y.indent) alignment.indent = y.indent; + if(y.wrapText) alignment.wrapText = y.wrapText; + xf.alignment = alignment; + break; + case '': break; /* 18.8.33 protection CT_CellProtection */ case '': case '': break; + /* 18.2.10 extLst CT_ExtensionList ? */ case '': break; case ']*)>.*<\/numFmts>/; var cellXfRegex = /]*)>.*<\/cellXfs>/; var fillsRegex = /]*)>.*<\/fills>/; +var fontsRegex = /]*)>.*<\/fonts>/; +var bordersRegex = /]*)>.*<\/borders>/; -return function parse_sty_xml(data, opts) { +return function parse_sty_xml(data, themes, opts) { var styles = {}; if(!data) return styles; /* 18.8.39 styleSheet CT_Stylesheet */ var t; - /* numFmts CT_NumFmts ? */ + /* 18.8.31 numFmts CT_NumFmts ? */ if((t=data.match(numFmtRegex))) parse_numFmts(t, styles, opts); - /* fonts CT_Fonts ? */ - /*if((t=data.match(/]*)>.*<\/fonts>/))) parse_fonts(t, opts);*/ + /* 18.8.23 fonts CT_Fonts ? */ + if((t=data.match(fontsRegex))) parse_fonts(t, styles, themes, opts); - /* fills CT_Fills */ - if((t=data.match(fillsRegex))) parse_fills(t, styles, opts); + /* 18.8.21 fills CT_Fills */ + if((t=data.match(fillsRegex))) parse_fills(t, styles, themes, opts); - /* borders CT_Borders ? */ - /* cellStyleXfs CT_CellStyleXfs ? */ + /* 18.8.5 borders CT_Borders ? */ + if((t=data.match(bordersRegex))) parse_borders(t, styles, themes, opts); - /* cellXfs CT_CellXfs ? */ + /* 18.8.9 cellStyleXfs CT_CellStyleXfs ? */ + + /* 18.8.10 cellXfs CT_CellXfs ? */ if((t=data.match(cellXfRegex))) parse_cellXfs(t, styles, opts); - /* dxfs CT_Dxfs ? */ - /* tableStyles CT_TableStyles ? */ - /* colors CT_Colors ? */ - /* extLst CT_ExtensionList ? */ + /* 18.8.15 dxfs CT_Dxfs ? */ + /* 18.8.42 tableStyles CT_TableStyles ? */ + /* 18.8.11 colors CT_Colors ? */ + /* 18.2.10 extLst CT_ExtensionList ? */ return styles; }; @@ -5236,7 +5408,7 @@ function parse_BrtXF(data, length) { } /* [MS-XLSB] 2.1.7.50 Styles */ -function parse_sty_bin(data, opts) { +function parse_sty_bin(data, themes, opts) { var styles = {}; styles.NumberFmt = ([]); for(var y in SSF._table) styles.NumberFmt[y] = SSF._table[y]; @@ -9654,9 +9826,9 @@ function parse_ws(data, name, opts, rels, wb, themes, styles) { return parse_ws_xml((data), opts, rels, wb, themes, styles); } -function parse_sty(data, name, opts) { - if(name.slice(-4)===".bin") return parse_sty_bin((data), opts); - return parse_sty_xml((data), opts); +function parse_sty(data, name, themes, opts) { + if(name.slice(-4)===".bin") return parse_sty_bin((data), themes, opts); + return parse_sty_xml((data), themes, opts); } function parse_theme(data, name, opts) { @@ -13279,9 +13451,9 @@ function parse_zip(zip, opts) { strs = []; if(dir.sst) strs=parse_sst(getzipdata(zip, dir.sst.replace(/^\//,'')), dir.sst, opts); - if(dir.style) styles = parse_sty(getzipdata(zip, dir.style.replace(/^\//,'')),dir.style, opts); - if(opts.cellStyles && dir.themes.length) themes = parse_theme(getzipstr(zip, dir.themes[0].replace(/^\//,''), true)||"",dir.themes[0], opts); + + if(dir.style) styles = parse_sty(getzipdata(zip, dir.style.replace(/^\//,'')),dir.style, themes, opts); } var wb = parse_wb(getzipdata(zip, dir.workbooks[0].replace(/^\//,'')), dir.workbooks[0], opts);