styled-exceljs 0.21.3 → 0.21.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/xlsx.mjs CHANGED
@@ -3,7 +3,7 @@
3
3
  /*exported XLSX */
4
4
  /*global process:false, Buffer:false, ArrayBuffer:false, DataView:false, Deno:false */
5
5
  var XLSX = {};
6
- XLSX.version = '0.21.3';
6
+ XLSX.version = '0.21.4';
7
7
  var current_codepage = 1200, current_ansi = 1252;
8
8
  /*:: declare var cptable:any; */
9
9
  /*global cptable:true, window */
@@ -1725,8 +1725,12 @@ sleuth_fat(difat_start, difat_sec_cnt, sectors, ssz, fat_addrs);
1725
1725
  /** Chains */
1726
1726
  var sector_list/*:SectorList*/ = make_sector_list(sectors, dir_start, fat_addrs, ssz);
1727
1727
 
1728
- if(dir_start < sector_list.length) sector_list[dir_start].name = "!Directory";
1729
- if(nmfs > 0 && minifat_start !== ENDOFCHAIN) sector_list[minifat_start].name = "!MiniFAT";
1728
+ if(!sector_list[dir_start]) throw new Error("CFB directory chain is missing");
1729
+ sector_list[dir_start].name = "!Directory";
1730
+ /* Some producers leave stale MiniFAT locations after removing all mini streams.
1731
+ * Require the chain only when a live directory entry actually uses it. */
1732
+ if(nmfs > 0 && sector_list[minifat_start]) sector_list[minifat_start].name = "!MiniFAT";
1733
+ if(!sector_list[fat_addrs[0]]) throw new Error("CFB FAT chain is missing");
1730
1734
  sector_list[fat_addrs[0]].name = "!FAT";
1731
1735
  sector_list.fat_addrs = fat_addrs;
1732
1736
  sector_list.ssz = ssz;
@@ -1938,10 +1942,14 @@ function read_directory(dir_start/*:number*/, sector_list/*:SectorList*/, sector
1938
1942
  if(mtime !== 0) o.mt = read_date(blob, blob.l-8);
1939
1943
  o.start = blob.read_shift(4, 'i');
1940
1944
  o.size = blob.read_shift(4, 'i');
1945
+ if(o.type === 0) { o.size = 0; o.start = ENDOFCHAIN; }
1941
1946
  if(o.size < 0 && o.start < 0) { o.size = o.type = 0; o.start = ENDOFCHAIN; o.name = ""; }
1942
1947
  if(o.type === 5) { /* root */
1943
- minifat_store = o.start;
1944
- if(nmfs > 0 && minifat_store !== ENDOFCHAIN) sector_list[minifat_store].name = "!StreamData";
1948
+ minifat_store = o.size > 0 ? o.start : ENDOFCHAIN;
1949
+ if(o.size > 0 && nmfs > 0) {
1950
+ if(!sector_list[minifat_store]) throw new Error("CFB root mini stream is missing");
1951
+ sector_list[minifat_store].name = "!StreamData";
1952
+ }
1945
1953
  /*minifat_size = o.size;*/
1946
1954
  } else if(o.size >= 4096 /* MSCSZ */) {
1947
1955
  o.storage = 'fat';
@@ -1951,8 +1959,11 @@ function read_directory(dir_start/*:number*/, sector_list/*:SectorList*/, sector
1951
1959
  } else {
1952
1960
  o.storage = 'minifat';
1953
1961
  if(o.size < 0) o.size = 0;
1954
- else if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN && sector_list[minifat_store]) {
1955
- o.content = get_mfat_entry(o, sector_list[minifat_store].data, (sector_list[mini]||{}).data);
1962
+ else if(o.type === 2 && o.size > 0) {
1963
+ if(nmfs < 1 || !sector_list[mini]) throw new Error("CFB MiniFAT chain is missing for stream: " + o.name);
1964
+ if(!sector_list[minifat_store]) throw new Error("CFB root mini stream is missing");
1965
+ o.content = get_mfat_entry(o, sector_list[minifat_store].data, sector_list[mini].data);
1966
+ if(o.content.length !== o.size) throw new Error("CFB mini stream is truncated: " + o.name);
1956
1967
  }
1957
1968
  }
1958
1969
  if(o.content) prep_blob(o.content, 0);
@@ -3288,7 +3299,6 @@ exports.utils = {
3288
3299
 
3289
3300
  return exports;
3290
3301
  })();
3291
-
3292
3302
  var _fs;
3293
3303
  function set_fs(fs) { _fs = fs; }
3294
3304
 
@@ -3945,7 +3955,7 @@ function escapexmltag(text/*:string*/)/*:string*/{ return escapexml(text).replac
3945
3955
  var htmlcharegex = /[\u0000-\u001f]/g;
3946
3956
  function escapehtml(text/*:string*/)/*:string*/{
3947
3957
  var s = text + '';
3948
- return s.replace(decregex, function(y) { return rencoding[y]; }).replace(/\n/g, "<br/>").replace(htmlcharegex,function(s) { return "&#x" + ("000"+s.charCodeAt(0).toString(16)).slice(-4) + ";"; });
3958
+ return s.replace(decregex, function(y) { return rencoding[y]; }).replace(/\r\n|\r|\n/g, "<br/>").replace(htmlcharegex,function(s) { return "&#x" + ("000"+s.charCodeAt(0).toString(16)).slice(-4) + ";"; });
3949
3959
  }
3950
3960
 
3951
3961
  function escapexlml(text/*:string*/)/*:string*/{
@@ -5755,6 +5765,7 @@ var RELS = ({
5755
5765
  MS: "http://schemas.microsoft.com/office/2006/relationships/xlMacrosheet",
5756
5766
  IMG: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
5757
5767
  DRAW: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
5768
+ TABLE: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table",
5758
5769
  XLMETA: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sheetMetadata",
5759
5770
  TCMNT: "http://schemas.microsoft.com/office/2017/10/relationships/threadedComment",
5760
5771
  PEOPLE: "http://schemas.microsoft.com/office/2017/10/relationships/person",
@@ -11224,6 +11235,9 @@ function resolve_style_obj_color(obj, themes) {
11224
11235
 
11225
11236
  /* 18.3.1.13 width calculations */
11226
11237
  /* [MS-OI29500] 2.1.595 Column Width & Formatting */
11238
+ /* The file format does not store MDW directly. Seven is the Office-compatible
11239
+ * fallback when Normal-font metrics are unavailable. A single stored column
11240
+ * width is ambiguous and must not change the scale for the rest of a sheet. */
11227
11241
  var DEF_MDW = 7, MAX_MDW = 15, MIN_MDW = 1, MDW = DEF_MDW;
11228
11242
  function width2px(width) { return Math.floor(( width + (Math.round(128/MDW))/256 )* MDW ); }
11229
11243
  function px2char(px) { return (Math.floor((px - 5)/MDW * 100 + 0.5))/100; }
@@ -12057,6 +12071,47 @@ function parse_dxfs(t, styles, themes, opts) {
12057
12071
  });
12058
12072
  }
12059
12073
 
12074
+ function parse_tableStyles(t, styles, opts) {
12075
+ styles.TableStyles = {styles:[]};
12076
+ var tableStyle = null, pass = false;
12077
+ (t.match(tagregex)||[]).forEach(function(x) {
12078
+ var y = parsexmltag(x), tag = strip_ns(y[0]);
12079
+ switch(tag) {
12080
+ case '<tableStyles': case '<tableStyles>':
12081
+ if(y.defaultTableStyle) styles.TableStyles.defaultTableStyle = utf8read(unescapexml(y.defaultTableStyle));
12082
+ if(y.defaultPivotStyle) styles.TableStyles.defaultPivotStyle = utf8read(unescapexml(y.defaultPivotStyle));
12083
+ break;
12084
+ case '<tableStyles/>': case '</tableStyles>': break;
12085
+ case '<tableStyle': case '<tableStyle>': case '<tableStyle/>':
12086
+ if(styles.TableStyles.styles.length >= TABLE_STYLE_MAX_TABLES) {
12087
+ tableStyle = null;
12088
+ break;
12089
+ }
12090
+ tableStyle = {
12091
+ name:utf8read(unescapexml(y.name || "")),
12092
+ pivot:y.pivot != null ? parsexmlbool(y.pivot) : false,
12093
+ table:y.table != null ? parsexmlbool(y.table) : true,
12094
+ elements:[]
12095
+ };
12096
+ styles.TableStyles.styles.push(tableStyle);
12097
+ if(tag.slice(-2) == "/>") tableStyle = null;
12098
+ break;
12099
+ case '</tableStyle>': tableStyle = null; break;
12100
+ case '<tableStyleElement': case '<tableStyleElement>': case '<tableStyleElement/>':
12101
+ if(tableStyle && tableStyle.elements.length < 64 && table_style_element_type(y.type) && y.dxfId != null) tableStyle.elements.push({
12102
+ type:y.type,
12103
+ dxfId:parseInt(y.dxfId, 10),
12104
+ size:y.size != null ? Math.max(1, parseInt(y.size, 10) || 1) : 1
12105
+ });
12106
+ break;
12107
+ case '<extLst': case '<extLst>': case '</extLst>': break;
12108
+ case '<ext': pass = true; break;
12109
+ case '</ext>': pass = false; break;
12110
+ default: if(opts && opts.WTF && !pass) throw new Error('unrecognized ' + y[0] + ' in tableStyles');
12111
+ }
12112
+ });
12113
+ }
12114
+
12060
12115
  function parse_colors(t, styles, themes, opts) {
12061
12116
  styles.Colors = {indexedColors:[], mruColors:[], themeColors:[]};
12062
12117
  var target = null, pass = false;
@@ -12135,6 +12190,7 @@ return function parse_sty_xml(data, themes, opts) {
12135
12190
  if((t=str_match_xml_ns(data, "dxfs"))) parse_dxfs(t[0], styles, themes, opts);
12136
12191
 
12137
12192
  /* 18.8.42 tableStyles CT_TableStyles ? */
12193
+ if((t=str_match_xml_ns(data, "tableStyles"))) parse_tableStyles(t[0], styles, opts);
12138
12194
  /* 18.8.11 colors CT_Colors ? */
12139
12195
  if((t=str_match_xml_ns(data, "colors"))) parse_colors(t[0], styles, themes, opts);
12140
12196
 
@@ -16663,7 +16719,11 @@ function validate_merges(ws/*:Worksheet*/, opts/*:?any*/) {
16663
16719
  errors.push({code:"E_MERGE_OVERLAP", message:"Merge ranges overlap", index:i, other:j, range:enc, otherRange:encode_range(merges[j])});
16664
16720
  }
16665
16721
  }
16666
- if(errors.length && opts && opts.WTF) throw new Error(errors[0].message + " (" + (errors[0].range || errors[0].index) + ")");
16722
+ if(errors.length && opts && opts.WTF) {
16723
+ var err/*:any*/ = new Error(errors[0].message + " (" + (errors[0].range || errors[0].index) + ")");
16724
+ err.code = errors[0].code;
16725
+ throw err;
16726
+ }
16667
16727
  return errors;
16668
16728
  }
16669
16729
 
@@ -16787,7 +16847,7 @@ function parse_ws_xml(data/*:?string*/, opts, idx/*:number*/, rels, wb/*:WBWBPro
16787
16847
  if(columns.length > 0) s["!cols"] = columns;
16788
16848
  if(merges.length > 0) {
16789
16849
  s["!merges"] = merges;
16790
- var mergeErrors = validate_merges(s, {WTF: !!(opts && (opts.WTF || opts.validateMerges))});
16850
+ var mergeErrors = validate_merges(s, {WTF: !!(opts && opts.validateMerges)});
16791
16851
  if(mergeErrors.length) s["!mergeErrors"] = mergeErrors;
16792
16852
  }
16793
16853
  if(rels['!id'][s['!rel']]) s['!drawel'] = rels['!id'][s['!rel']];
@@ -16902,7 +16962,8 @@ function parse_ws_xml_cols(columns, cols) {
16902
16962
  var colm=parseInt(coll.min, 10)-1, colM=parseInt(coll.max,10)-1;
16903
16963
  if(coll.outlineLevel) coll.level = (+coll.outlineLevel || 0);
16904
16964
  delete coll.min; delete coll.max; coll.width = +coll.width;
16905
- /* OOXML widths share the workbook Normal-font MDW; do not infer a different scale per sheet. */
16965
+ /* OOXML widths share the workbook Normal-font MDW. Do not infer it from
16966
+ * one ambiguous stored width or leak another worksheet's scale. */
16906
16967
  if(!seencol && coll.width) { seencol = true; MDW = DEF_MDW; }
16907
16968
  process_col(coll);
16908
16969
  while(colm <= colM) columns[colm++] = dup(coll);
@@ -17325,7 +17386,7 @@ function write_ws_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ {
17325
17386
  /* customSheetViews */
17326
17387
 
17327
17388
  if(ws['!merges'] != null && ws['!merges'].length > 0) {
17328
- validate_merges(ws, {WTF:true});
17389
+ validate_merges(ws, {WTF: !!(opts && opts.validateMerges)});
17329
17390
  o[o.length] = (write_ws_xml_merges(ws['!merges']));
17330
17391
  }
17331
17392
 
@@ -17390,6 +17451,312 @@ function write_ws_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ {
17390
17451
  if(o.length>1) { o[o.length] = ('</worksheet>'); o[1]=o[1].replace("/>",">"); }
17391
17452
  return o.join("");
17392
17453
  }
17454
+ var TABLE_STYLE_MAX_TABLES = 1024;
17455
+ var TABLE_STYLE_MAX_COLUMNS = 16384;
17456
+ var TABLE_STYLE_MAX_XML_LENGTH = 5 * 1024 * 1024;
17457
+ var TABLE_STYLE_ELEMENT_TYPES = {
17458
+ wholeTable:true, headerRow:true, totalRow:true,
17459
+ firstColumn:true, lastColumn:true,
17460
+ firstRowStripe:true, secondRowStripe:true,
17461
+ firstColumnStripe:true, secondColumnStripe:true,
17462
+ firstHeaderCell:true, lastHeaderCell:true,
17463
+ firstTotalCell:true, lastTotalCell:true
17464
+ };
17465
+
17466
+ function table_style_safe_key(key) {
17467
+ return key != "__proto__" && key != "constructor" && key != "prototype";
17468
+ }
17469
+
17470
+ function table_style_element_type(type) {
17471
+ return Object.prototype.hasOwnProperty.call(TABLE_STYLE_ELEMENT_TYPES, type);
17472
+ }
17473
+
17474
+ function table_style_attr(y, name) {
17475
+ if(y[name] != null) return y[name];
17476
+ return y[name.toLowerCase()];
17477
+ }
17478
+
17479
+ function table_style_bool(y, name, fallback) {
17480
+ var value = table_style_attr(y, name);
17481
+ return value == null ? fallback : parsexmlbool(value);
17482
+ }
17483
+
17484
+ function table_style_int(y, name) {
17485
+ var value = table_style_attr(y, name);
17486
+ if(value == null || value === "") return void 0;
17487
+ value = parseInt(value, 10);
17488
+ return isFinite(value) && value >= 0 ? value : void 0;
17489
+ }
17490
+
17491
+ function table_style_merge(target, source) {
17492
+ if(!source) return target;
17493
+ if(!target) target = {};
17494
+ keys(source).forEach(function(k) {
17495
+ if(!table_style_safe_key(k)) return;
17496
+ var value = source[k];
17497
+ if(value && typeof value == "object" && !Array.isArray(value)) {
17498
+ target[k] = table_style_merge(
17499
+ target[k] && typeof target[k] == "object" && !Array.isArray(target[k]) ? table_style_merge({}, target[k]) : {},
17500
+ value
17501
+ );
17502
+ } else target[k] = value;
17503
+ });
17504
+ return target;
17505
+ }
17506
+
17507
+ function table_style_color(themes, theme, tint) {
17508
+ return style_color_from_attrs({theme:String(theme), tint:String(tint || 0)}, themes);
17509
+ }
17510
+
17511
+ function table_style_fill(color) {
17512
+ return {fill:{patternType:"solid", fgColor:color}};
17513
+ }
17514
+
17515
+ function table_style_font(color, bold) {
17516
+ var font = {};
17517
+ if(color) font.color = color;
17518
+ if(bold) font.bold = 1;
17519
+ return {font:font};
17520
+ }
17521
+
17522
+ function table_style_border(side, color, style) {
17523
+ var border = {}, out = {border:border};
17524
+ border[side] = {style:style || "thin", color:color};
17525
+ return out;
17526
+ }
17527
+
17528
+ function table_style_accent(themes, index) {
17529
+ var position = (index - 1) % 7;
17530
+ /* The first style in each seven-style family is neutral. The remaining
17531
+ * six entries use the workbook's accent1..accent6 colors. */
17532
+ return table_style_color(themes, position ? 3 + position : 1, 0);
17533
+ }
17534
+
17535
+ function built_in_table_style_rules(name, themes) {
17536
+ var match = /^TableStyle(Light|Medium|Dark)(\d+)$/i.exec(name || "");
17537
+ if(!match) return null;
17538
+ var family = match[1].toLowerCase(), index = parseInt(match[2], 10);
17539
+ var maximum = family == "light" ? 21 : family == "medium" ? 28 : 11;
17540
+ if(index < 1 || index > maximum) return null;
17541
+ var accent = table_style_accent(themes, index);
17542
+ var white = table_style_color(themes, 0, 0);
17543
+ var dark = table_style_color(themes, 1, 0);
17544
+ var rules = {};
17545
+
17546
+ if(family == "light") {
17547
+ rules.headerRow = table_style_merge(
17548
+ table_style_font(accent, true),
17549
+ table_style_border("bottom", accent, "medium")
17550
+ );
17551
+ rules.totalRow = table_style_merge(
17552
+ table_style_font(accent, true),
17553
+ table_style_border("top", accent, "double")
17554
+ );
17555
+ rules.firstRowStripe = table_style_fill(table_style_color(themes, accent.theme, 0.9));
17556
+ rules.firstColumnStripe = rules.firstRowStripe;
17557
+ } else if(family == "medium") {
17558
+ var group = Math.floor((index - 1) / 7);
17559
+ rules.headerRow = table_style_merge(table_style_fill(accent), table_style_font(white, true));
17560
+ rules.totalRow = table_style_merge(
17561
+ table_style_font(accent, true),
17562
+ table_style_border("top", accent, "double")
17563
+ );
17564
+ if(group == 1) {
17565
+ /* Medium 8..14 use two theme-derived body fills. This is the family
17566
+ * used by Excel's TableStyleMedium13. */
17567
+ rules.wholeTable = table_style_fill(table_style_color(themes, accent.theme, 0.8));
17568
+ rules.firstRowStripe = table_style_fill(table_style_color(themes, accent.theme, 0.6));
17569
+ rules.firstColumnStripe = rules.firstRowStripe;
17570
+ } else if(group == 2) {
17571
+ rules.firstRowStripe = table_style_fill(table_style_color(themes, accent.theme, 0.8));
17572
+ rules.firstColumnStripe = rules.firstRowStripe;
17573
+ rules.wholeTable = table_style_border("bottom", table_style_color(themes, accent.theme, 0.4), "thin");
17574
+ } else if(group == 3) {
17575
+ rules.wholeTable = table_style_fill(table_style_color(themes, accent.theme, 0.9));
17576
+ rules.firstRowStripe = table_style_fill(table_style_color(themes, accent.theme, 0.7));
17577
+ rules.firstColumnStripe = rules.firstRowStripe;
17578
+ } else {
17579
+ rules.firstRowStripe = table_style_fill(table_style_color(themes, accent.theme, 0.8));
17580
+ rules.firstColumnStripe = rules.firstRowStripe;
17581
+ }
17582
+ } else {
17583
+ rules.wholeTable = table_style_merge(table_style_fill(dark), table_style_font(white, false));
17584
+ rules.headerRow = table_style_merge(table_style_fill(accent), table_style_font(white, true));
17585
+ rules.firstRowStripe = table_style_fill(table_style_color(themes, accent.theme, -0.35));
17586
+ rules.firstColumnStripe = rules.firstRowStripe;
17587
+ rules.totalRow = table_style_merge(
17588
+ table_style_font(white, true),
17589
+ table_style_border("top", accent, "double")
17590
+ );
17591
+ }
17592
+
17593
+ rules.firstColumn = table_style_font(null, true);
17594
+ rules.lastColumn = table_style_font(null, true);
17595
+ return rules;
17596
+ }
17597
+
17598
+ function custom_table_style_rules(name, styles) {
17599
+ var tableStyles = styles && styles.TableStyles;
17600
+ if(!tableStyles || !tableStyles.styles) return null;
17601
+ for(var i = 0; i < tableStyles.styles.length; ++i) {
17602
+ var tableStyle = tableStyles.styles[i];
17603
+ if(tableStyle.name != name) continue;
17604
+ var rules = Object.create(null);
17605
+ (tableStyle.elements || []).forEach(function(element) {
17606
+ if(!table_style_element_type(element.type)) return;
17607
+ var dxf = styles.Dxfs && styles.Dxfs[element.dxfId];
17608
+ if(dxf) rules[element.type] = {style:dup(dxf), size:element.size || 1};
17609
+ });
17610
+ return rules;
17611
+ }
17612
+ return null;
17613
+ }
17614
+
17615
+ function parse_table_xml(data, path, themes, styles, opts) {
17616
+ if(!data || data.length > TABLE_STYLE_MAX_XML_LENGTH) return null;
17617
+ data = remove_doctype(str_remove_ng(data, "<!--", "-->"));
17618
+ var table = {columns:[]}, column = null, pass = false;
17619
+ (data.match(tagregex)||[]).forEach(function(x) {
17620
+ var y = parsexmltag(x), tag = strip_ns(y[0]);
17621
+ switch(tag) {
17622
+ case '<table': case '<table>':
17623
+ table.id = table_style_int(y, "id");
17624
+ table.name = utf8read(unescapexml(table_style_attr(y, "name") || ""));
17625
+ table.displayName = utf8read(unescapexml(table_style_attr(y, "displayName") || table.name));
17626
+ table.ref = table_style_attr(y, "ref") || "";
17627
+ table.headerRowCount = table_style_int(y, "headerRowCount");
17628
+ table.totalsRowCount = table_style_int(y, "totalsRowCount");
17629
+ table.totalsRowShown = table_style_bool(y, "totalsRowShown", false);
17630
+ table.headerRowDxfId = table_style_int(y, "headerRowDxfId");
17631
+ table.dataDxfId = table_style_int(y, "dataDxfId");
17632
+ table.totalsRowDxfId = table_style_int(y, "totalsRowDxfId");
17633
+ table.path = path;
17634
+ break;
17635
+ case '<tableColumn': case '<tableColumn>': case '<tableColumn/>':
17636
+ if(table.columns.length >= TABLE_STYLE_MAX_COLUMNS) {
17637
+ column = null;
17638
+ break;
17639
+ }
17640
+ column = {
17641
+ id:table_style_int(y, "id"),
17642
+ name:utf8read(unescapexml(table_style_attr(y, "name") || "")),
17643
+ headerRowDxfId:table_style_int(y, "headerRowDxfId"),
17644
+ dataDxfId:table_style_int(y, "dataDxfId"),
17645
+ totalsRowDxfId:table_style_int(y, "totalsRowDxfId")
17646
+ };
17647
+ table.columns.push(column);
17648
+ if(tag.slice(-2) == "/>" ) column = null;
17649
+ break;
17650
+ case '</tableColumn>': column = null; break;
17651
+ case '<tableStyleInfo': case '<tableStyleInfo>': case '<tableStyleInfo/>':
17652
+ table.styleInfo = {
17653
+ name:utf8read(unescapexml(table_style_attr(y, "name") || "")),
17654
+ showFirstColumn:table_style_bool(y, "showFirstColumn", false),
17655
+ showLastColumn:table_style_bool(y, "showLastColumn", false),
17656
+ showRowStripes:table_style_bool(y, "showRowStripes", false),
17657
+ showColumnStripes:table_style_bool(y, "showColumnStripes", false)
17658
+ };
17659
+ break;
17660
+ case '<extLst': case '<extLst>': case '</extLst>': break;
17661
+ case '<ext': pass = true; break;
17662
+ case '</ext>': pass = false; break;
17663
+ default: if(opts && opts.WTF && !pass) {
17664
+ /* Table formulas and filter metadata are intentionally preserved by
17665
+ * the raw XML parser but do not affect visual style resolution. */
17666
+ if(/^<\/?(?:tableColumns|autoFilter|sortState|calculatedColumnFormula|totalsRowFormula|xmlColumnPr)/.test(tag)) break;
17667
+ }
17668
+ }
17669
+ });
17670
+ if(!table.ref) return null;
17671
+ try {
17672
+ table.range = safe_decode_range(table.ref);
17673
+ if(table.range.s.r < 0 || table.range.s.c < 0 || table.range.e.r > 1048575 || table.range.e.c > 16383) return null;
17674
+ } catch(e) { return null; }
17675
+ var styleName = table.styleInfo && table.styleInfo.name;
17676
+ table.styleRules = custom_table_style_rules(styleName, styles) || built_in_table_style_rules(styleName, themes) || {};
17677
+ table.dxfs = styles && styles.Dxfs ? styles.Dxfs : [];
17678
+ return table;
17679
+ }
17680
+
17681
+ function parse_sheet_tables(sheet, zip, path, rels, opts, themes, styles) {
17682
+ if(!sheet || !rels || !rels['!id'] || !opts || !opts.cellStyles) return;
17683
+ var tables = [], seen = Object.create(null);
17684
+ keys(rels['!id']).forEach(function(id) {
17685
+ if(tables.length >= TABLE_STYLE_MAX_TABLES) return;
17686
+ var rel = rels['!id'][id];
17687
+ if(!rel || rel.TargetMode == "External" || typeof rel.Target != "string" || !rel.Target ||
17688
+ (rel.Type != RELS.TABLE && !/\/table$/.test(rel.Type || ""))) return;
17689
+ var tablePath = resolve_path(rel.Target, path);
17690
+ if(seen[tablePath]) return;
17691
+ seen[tablePath] = true;
17692
+ var table = parse_table_xml(getzipstr(zip, tablePath, true), tablePath, themes, styles, opts);
17693
+ if(table) tables.push(table);
17694
+ });
17695
+ if(tables.length) sheet['!tables'] = tables;
17696
+ }
17697
+
17698
+ function table_style_rule_value(rule) {
17699
+ return rule && rule.style ? rule.style : rule;
17700
+ }
17701
+
17702
+ function table_style_stripe_rule(first, second, offset) {
17703
+ var firstSize = first && first.size || 1, secondSize = second && second.size || 1;
17704
+ var period = firstSize + secondSize;
17705
+ if(!period) return null;
17706
+ return offset % period < firstSize ? first : second;
17707
+ }
17708
+
17709
+ function table_style_dxf(table, id) {
17710
+ return id == null || !table.dxfs ? null : table.dxfs[id];
17711
+ }
17712
+
17713
+ function resolve_table_cell_style(ws, row, col, baseStyle) {
17714
+ var tables = ws && ws['!tables'];
17715
+ var resolved = baseStyle ? table_style_merge({}, baseStyle) : {};
17716
+ if(!tables || !tables.length) return keys(resolved).length ? resolved : void 0;
17717
+ for(var i = 0; i < tables.length; ++i) {
17718
+ var table = tables[i], range = table.range;
17719
+ if(!range || row < range.s.r || row > range.e.r || col < range.s.c || col > range.e.c) continue;
17720
+ var rules = table.styleRules || {}, info = table.styleInfo || {};
17721
+ var rowOffset = row - range.s.r, colOffset = col - range.s.c;
17722
+ var headerRows = table.headerRowCount == null ? 1 : table.headerRowCount;
17723
+ var totalRows = table.totalsRowCount != null ? table.totalsRowCount : table.totalsRowShown ? 1 : 0;
17724
+ var dataStart = headerRows, dataEnd = range.e.r - range.s.r - totalRows;
17725
+ var isHeader = rowOffset < headerRows;
17726
+ var isTotal = totalRows > 0 && rowOffset > dataEnd;
17727
+ var dataOffset = rowOffset - dataStart;
17728
+ var column = table.columns && table.columns[colOffset];
17729
+
17730
+ resolved = table_style_merge(resolved, table_style_rule_value(rules.wholeTable));
17731
+ if(!isHeader && !isTotal && dataOffset >= 0) {
17732
+ if(info.showRowStripes) resolved = table_style_merge(resolved, table_style_rule_value(
17733
+ table_style_stripe_rule(rules.firstRowStripe, rules.secondRowStripe, dataOffset)
17734
+ ));
17735
+ if(info.showColumnStripes) resolved = table_style_merge(resolved, table_style_rule_value(
17736
+ table_style_stripe_rule(rules.firstColumnStripe, rules.secondColumnStripe, colOffset)
17737
+ ));
17738
+ }
17739
+ if(info.showFirstColumn && col == range.s.c) resolved = table_style_merge(resolved, table_style_rule_value(rules.firstColumn));
17740
+ if(info.showLastColumn && col == range.e.c) resolved = table_style_merge(resolved, table_style_rule_value(rules.lastColumn));
17741
+ if(isHeader) {
17742
+ resolved = table_style_merge(resolved, table_style_rule_value(rules.headerRow));
17743
+ if(col == range.s.c) resolved = table_style_merge(resolved, table_style_rule_value(rules.firstHeaderCell));
17744
+ if(col == range.e.c) resolved = table_style_merge(resolved, table_style_rule_value(rules.lastHeaderCell));
17745
+ resolved = table_style_merge(resolved, table_style_dxf(table, table.headerRowDxfId));
17746
+ resolved = table_style_merge(resolved, table_style_dxf(table, column && column.headerRowDxfId));
17747
+ } else if(isTotal) {
17748
+ resolved = table_style_merge(resolved, table_style_rule_value(rules.totalRow));
17749
+ if(col == range.s.c) resolved = table_style_merge(resolved, table_style_rule_value(rules.firstTotalCell));
17750
+ if(col == range.e.c) resolved = table_style_merge(resolved, table_style_rule_value(rules.lastTotalCell));
17751
+ resolved = table_style_merge(resolved, table_style_dxf(table, table.totalsRowDxfId));
17752
+ resolved = table_style_merge(resolved, table_style_dxf(table, column && column.totalsRowDxfId));
17753
+ } else {
17754
+ resolved = table_style_merge(resolved, table_style_dxf(table, table.dataDxfId));
17755
+ resolved = table_style_merge(resolved, table_style_dxf(table, column && column.dataDxfId));
17756
+ }
17757
+ }
17758
+ return keys(resolved).length ? resolved : void 0;
17759
+ }
17393
17760
 
17394
17761
  /* [MS-XLSB] 2.4.726 BrtRowHdr */
17395
17762
  function parse_BrtRowHdr(data, length) {
@@ -18812,7 +19179,6 @@ function check_wb(wb) {
18812
19179
  check_wb_names(wb.SheetNames, Sheets, !!wb.vbaraw);
18813
19180
  for(var i = 0; i < wb.SheetNames.length; ++i) {
18814
19181
  var ws = sheet_map_get(wb.Sheets, wb.SheetNames[i]);
18815
- if(!ws) throw new Error("Missing worksheet |" + wb.SheetNames[i] + "|");
18816
19182
  check_ws(ws, wb.SheetNames[i], i);
18817
19183
  }
18818
19184
  wb.SheetNames.forEach(function(n, i) {
@@ -21241,7 +21607,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
21241
21607
  };
21242
21608
  var finalize_sheet_visuals = function finalizesheetvisuals(ws) {
21243
21609
  if(ws["!merges"] && ws["!merges"].length) {
21244
- var mergeErrors = validate_merges(ws, {WTF: !!(options && (options.WTF || options.validateMerges))});
21610
+ var mergeErrors = validate_merges(ws, {WTF: !!(options && options.validateMerges)});
21245
21611
  if(mergeErrors.length) ws["!mergeErrors"] = mergeErrors;
21246
21612
  }
21247
21613
  if(drawings && (drawings.images.length || drawings.shapes.length || drawings.charts.length || drawings.raw.length)) ws["!drawings"] = drawings;
@@ -23800,7 +24166,7 @@ function write_ws_biff8(idx/*:number*/, opts, wb/*:Workbook*/) {
23800
24166
  if(b8) write_biff_rec(ba, 0x023e /* Window2 */, write_Window2((_WB.Views||[])[0]));
23801
24167
  /* ... */
23802
24168
  if(b8 && (ws['!merges']||[]).length) {
23803
- validate_merges(ws, {WTF:true});
24169
+ validate_merges(ws, {WTF: !!(opts && opts.validateMerges)});
23804
24170
  write_biff_rec(ba, 0x00e5 /* MergeCells */, write_MergeCells(ws['!merges']));
23805
24171
  }
23806
24172
  /* [LRng] *QUERYTABLE [PHONETICINFO] CONDFMTS */
@@ -24189,6 +24555,7 @@ function make_html_row(ws/*:Worksheet*/, r/*:Range*/, R/*:number*/, o/*:Sheet2HT
24189
24555
  if(cols[C] && cols[C].s) extend_style_obj(inherited, cols[C].s);
24190
24556
  if(row && row.s) extend_style_obj(inherited, row.s);
24191
24557
  if(cell && cell.s) extend_style_obj(inherited, cell.s);
24558
+ inherited = resolve_table_cell_style(ws, R, C, inherited) || inherited;
24192
24559
  if(keys(inherited).length) {
24193
24560
  stylecell = cell ? dup(cell) : {t:'z'};
24194
24561
  stylecell.s = inherited;
@@ -28315,6 +28682,7 @@ function safe_parse_sheet(zip, path/*:string*/, relsPath/*:string*/, sheet, idx/
28315
28682
  case 'dialog': _ws = parse_ds(data, path, idx, opts, sheetRels[sheet], wb, themes, styles); break;
28316
28683
  default: throw new Error("Unrecognized sheet type " + stype);
28317
28684
  }
28685
+ if(stype == "sheet") parse_sheet_tables(_ws, zip, path, sheetRels[sheet], opts, themes, styles);
28318
28686
  sheet_map_set(sheets, sheet, _ws);
28319
28687
 
28320
28688
  /* scan rels for comments and threaded comments */
@@ -28335,7 +28703,9 @@ function safe_parse_sheet(zip, path/*:string*/, relsPath/*:string*/, sheet, idx/
28335
28703
  if(tcomments && tcomments.length) sheet_insert_comments(_ws, tcomments, true, opts.people || []);
28336
28704
  if(stype == "sheet") parse_sheet_drawing(_ws, stype, zip, path, idx, opts, wb);
28337
28705
  parse_sheet_legacy_drawing(_ws, stype, zip, path, idx, opts, wb, comments);
28338
- } catch(e) { if(opts.WTF) throw e; }
28706
+ } catch(e) {
28707
+ if(opts.WTF || (opts.validateMerges && e && /^E_MERGE_/.test(e.code))) throw e;
28708
+ }
28339
28709
  }
28340
28710
 
28341
28711
  function strip_front_slash(x/*:string*/)/*:string*/ { return x.charAt(0) == '/' ? x.slice(1) : x; }
@@ -29647,6 +30017,7 @@ var utils/*:any*/ = {
29647
30017
  measure_text_width: measure_text_width,
29648
30018
  auto_fit_columns: auto_fit_columns,
29649
30019
  autofit_columns: auto_fit_columns,
30020
+ resolve_table_cell_style: resolve_table_cell_style,
29650
30021
  col_width_to_px: width2px,
29651
30022
  px_to_col_width: function(px) { return char2width(px2char(px)); },
29652
30023
  row_height_to_px: pt2px_browser,