XLS/XLSX/XLSB Row Outline Levels (closes #723)

note: @sheetjsdev rewrote implementation, original PR author used
This commit is contained in:
Danwakeem 2017-07-09 12:37:45 -05:00 committed by SheetJS
parent 859691b87b
commit d28cc872c7
13 changed files with 158 additions and 25 deletions

View File

@ -1204,9 +1204,14 @@ type RowInfo = {
/* row height is specified in one of the following ways: */
hpx?: number; // height in screen pixels
hpt?: number; // height in points
level?: number; // 0-indexed outline / group level
};
```
Note: Excel UI displays the base outline level as `1` and the max level as `8`.
The `level` field stores the base outline as `0` and the max level as `7`.
<details>
<summary><b>Implementation details</b> (click to show)</summary>

View File

@ -224,6 +224,8 @@ function parse_Row(blob, length) {
blob.l += 4; // reserved(2), unused(2)
var flags = blob.read_shift(1); // various flags
blob.l += 3; // reserved(8), ixfe(12), flags(4)
if(flags & 0x07) z.level = flags & 0x07;
// collapsed: flags & 0x10
if(flags & 0x20) z.hidden = true;
if(flags & 0x40) z.hpt = miyRw / 20;
return z;

View File

@ -262,6 +262,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
rowobj = {}; rowrite = false;
if(tag.ht) { rowrite = true; rowobj.hpt = parseFloat(tag.ht); rowobj.hpx = pt2px(rowobj.hpt); }
if(tag.hidden == "1") { rowrite = true; rowobj.hidden = true; }
if(tag.outlineLevel != null) { rowrite = true; rowobj.level = +tag.outlineLevel; }
if(rowrite) rows[tagr-1] = rowobj;
}
@ -391,7 +392,7 @@ function write_ws_xml_data(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbook
if(_cell === undefined) continue;
if((cell = write_ws_xml_cell(_cell, ref, ws, opts, idx, wb)) != null) r.push(cell);
}
if(r.length > 0) {
if(r.length > 0 || rows && rows[R]) {
var params = ({r:rr}/*:any*/);
if(rows && rows[R]) {
var row = rows[R];
@ -400,10 +401,24 @@ function write_ws_xml_data(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbook
if (row.hpx) height = px2pt(row.hpx);
else if (row.hpt) height = row.hpt;
if (height > -1) { params.ht = height; params.customHeight = 1; }
if (row.level) { params.outlineLevel = row.level; }
}
o[o.length] = (writextag('row', r.join(""), params));
}
}
if(rows) for(; R < rows.length; ++R) {
if(rows && rows[R]) {
var params = ({r:R+1}/*:any*/);
var row = rows[R];
if(row.hidden) params.hidden = 1;
var height = -1;
if (row.hpx) height = px2pt(row.hpx);
else if (row.hpt) height = row.hpt;
if (height > -1) { params.ht = height; params.customHeight = 1; }
if (row.level) { params.outlineLevel = row.level; }
o[o.length] = (writextag('row', "", params));
}
}
return o.join("");
}
@ -429,7 +444,11 @@ function write_ws_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ {
o[o.length] = write_ws_xml_sheetviews(ws, opts, idx, wb);
/* TODO: store in WB, process styles */
if(opts.sheetFormat) o[o.length] = (writextag('sheetFormatPr', null, {defaultRowHeight:opts.sheetFormat.defaultRowHeight||'16', baseColWidth:opts.sheetFormat.baseColWidth||'10' }));
if(opts.sheetFormat) o[o.length] = (writextag('sheetFormatPr', null, {
defaultRowHeight:opts.sheetFormat.defaultRowHeight||'16',
baseColWidth:opts.sheetFormat.baseColWidth||'10',
outlineLevelRow:opts.sheetFormat.outlineLevelRow||'7'
}));
if(ws['!cols'] != null && ws['!cols'].length > 0) o[o.length] = (write_ws_xml_cols(ws, ws['!cols']));

View File

@ -9,6 +9,7 @@ function parse_BrtRowHdr(data, length) {
data.l += 1; // TODO: top/bot padding
var flags = data.read_shift(1);
data.l = tgt;
if(flags & 0x07) z.level = flags & 0x07;
if(flags & 0x10) z.hidden = true;
if(flags & 0x20) z.hpt = miyRw / 20;
return z;
@ -28,6 +29,7 @@ function write_BrtRowHdr(R/*:number*/, range, ws) {
o.write_shift(1, 0); /* top/bot padding */
var flags = 0x0;
if(row.level) flags |= row.level;
if(row.hidden) flags |= 0x10;
if(row.hpx || row.hpt) flags |= 0x20;
o.write_shift(1, flags);
@ -62,7 +64,7 @@ function write_BrtRowHdr(R/*:number*/, range, ws) {
}
function write_row_header(ba, ws, range, R) {
var o = write_BrtRowHdr(R, range, ws);
if(o.length > 17) write_record(ba, 'BrtRowHdr', o);
if(o.length > 17 || (ws['!rows']||[])[R]) write_record(ba, 'BrtRowHdr', o);
}
/* [MS-XLSB] 2.4.812 BrtWsDim */
@ -426,7 +428,7 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ {
if(opts.sheetRows && opts.sheetRows <= row.r) end=true;
rr = encode_row(R = row.r);
opts['!row'] = row.r;
if(val.hidden || val.hpt) {
if(val.hidden || val.hpt || val.level != null) {
if(val.hpt) val.hpx = pt2px(val.hpt);
rowinfo[val.r] = val;
}
@ -684,12 +686,14 @@ function write_CELLTABLE(ba, ws/*:Worksheet*/, idx/*:number*/, opts, wb/*:Workbo
var range = safe_decode_range(ws['!ref'] || "A1"), ref, rr = "", cols = [];
write_record(ba, 'BrtBeginSheetData');
var dense = Array.isArray(ws);
for(var R = range.s.r; R <= range.e.r; ++R) {
var cap = range.e.r;
if(ws['!rows']) cap = Math.max(range.e.r, ws['!rows'].length - 1);
for(var R = range.s.r; R <= cap; ++R) {
rr = encode_row(R);
/* [ACCELLTABLE] */
/* BrtRowHdr */
write_row_header(ba, ws, range, R);
for(var C = range.s.c; C <= range.e.c; ++C) {
if(R <= range.e.r) for(var C = range.s.c; C <= range.e.c; ++C) {
/* *16384CELL */
if(R === range.s.r) cols[C] = encode_col(C);
ref = cols[C] + rr;

View File

@ -521,6 +521,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
} break;
case 'Row': {
var rowobj = {};
if(val.level != null) { rowinfo[val.r] = rowobj; rowobj.level = val.level; }
if(val.hidden) { rowinfo[val.r] = rowobj; rowobj.hidden = true; }
if(val.hpt) {
rowinfo[val.r] = rowobj;

View File

@ -289,6 +289,9 @@ var parse_content_xml = (function() {
case 'forms': break; // 12.25.2 13.2
case 'table-column': break; // 9.1.6 <table:table-column>
/* TODO: outline levels */
case 'table-row-group': break; // 9.1.9 <table:table-row-group>
case 'table-column-group': break; // 9.1.10 <table:table-column-group>
case 'null-date': break; // 9.4.2 <table:null-date> TODO: date1904

View File

@ -75,9 +75,14 @@ type RowInfo = {
/* row height is specified in one of the following ways: */
hpx?: number; // height in screen pixels
hpt?: number; // height in points
level?: number; // 0-indexed outline / group level
};
```
Note: Excel UI displays the base outline level as `1` and the max level as `8`.
The `level` field stores the base outline as `0` and the max level as `7`.
<details>
<summary><b>Implementation details</b> (click to show)</summary>

View File

@ -1105,9 +1105,14 @@ type RowInfo = {
/* row height is specified in one of the following ways: */
hpx?: number; // height in screen pixels
hpt?: number; // height in points
level?: number; // 0-indexed outline / group level
};
```
Note: Excel UI displays the base outline level as `1` and the max level as `8`.
The `level` field stores the base outline as `0` and the max level as `7`.
Excel internally stores row heights in points. The default resolution is 72 DPI
or 96 PPI, so the pixel and point size should agree. For different resolutions

33
test.js
View File

@ -97,6 +97,12 @@ var paths = {
nfxlsx: dir + 'number_format.xlsm',
nfxlsb: dir + 'number_format.xlsb',
olxls: dir + 'outline.xls',
olxls5: dir + 'outline.biff5',
olxlsx: dir + 'outline.xlsx',
olxlsb: dir + 'outline.xlsb',
olods: dir + 'outline.ods',
pmxls: dir + 'page_margins_2016.xls',
pmxls5: dir + 'page_margins_2016_5.xls',
pmxml: dir + 'page_margins_2016.xml',
@ -901,6 +907,7 @@ describe('parse features', function() {
describe('row properties', function() {
var wb1, wb2, wb3, wb4, wb5, wb6;
var ol1, ol2, ol3, ol4, ol5;
var bef = (function() {
X = require(modp);
wb1 = X.readFile(paths.rhxlsx, {cellStyles:true});
@ -909,6 +916,11 @@ describe('parse features', function() {
wb4 = X.readFile(paths.rhxls5, {cellStyles:true});
wb5 = X.readFile(paths.rhxml, {cellStyles:true});
wb6 = X.readFile(paths.rhslk, {cellStyles:true});
ol1 = X.readFile(paths.olxlsx, {cellStyles:true});
ol2 = X.readFile(paths.olxlsb, {cellStyles:true});
ol3 = X.readFile(paths.olxls, {cellStyles:true});
ol4 = X.readFile(paths.olxls5, {cellStyles:true});
ol5 = X.readFile(paths.olods, {cellStyles:true});
});
if(typeof before != 'undefined') before(bef);
else it('before', bef);
@ -930,6 +942,22 @@ describe('parse features', function() {
assert.equal(x[3].hpx, 100);
});
});
it('should have correct outline levels', function() {
/* TODO: ODS */
[ol1, ol2, ol3, ol4/*, ol5*/].map(function(x) { return x.Sheets.Sheet1; }).forEach(function(ws) {
var rows = ws['!rows'];
for(var i = 0; i < 29; ++i) {
var cell = get_cell(ws, "A" + X.utils.encode_row(i));
var lvl = (rows[i]||{}).level||0;
if(!cell || cell.t == 's') assert.equal(lvl, 0);
else if(cell.t == 'n') {
if(cell.v == 0) assert.equal(lvl, 0);
else assert.equal(lvl, cell.v);
}
}
assert.equal(rows[29].level, 7);
});
});
});
describe('merge cells',function() {
@ -1400,18 +1428,21 @@ describe('roundtrip features', function() {
}); });
});
/* TODO: ODS and BIFF5/8 */
describe('should preserve row properties', function() { [
'xlml', /*'biff2', */ 'xlsx', 'xlsb', 'slk'
].forEach(function(w) { it(w, function() {
var ws1 = X.utils.aoa_to_sheet([["hpx12"],["hpt24"],["hpx48"],["hidden"]]);
ws1['!rows'] = [{hpx:12},{hpt:24},{hpx:48},{hidden:true}];
for(var i = 0; i <= 7; ++i) ws1['!rows'].push({level:i});
var wb1 = {SheetNames:["Sheet1"], Sheets:{Sheet1:ws1}};
var wb2 = X.read(X.write(wb1, {bookType:w, type:"buffer"}), {type:"buffer", cellStyles:true});
var wb2 = X.read(X.write(wb1, {bookType:w, type:"buffer", cellStyles:true}), {type:"buffer", cellStyles:true});
var ws2 = wb2.Sheets.Sheet1;
assert.equal(ws2['!rows'][0].hpx, 12);
assert.equal(ws2['!rows'][1].hpt, 24);
assert.equal(ws2['!rows'][2].hpx, 48);
assert.equal(ws2['!rows'][3].hidden, true);
if(w == 'xlsb' || w == 'xlsx') for(i = 0; i <= 7; ++i) assert.equal((ws2['!rows'][4+i]||{}).level||0, i);
}); });
});

@ -1 +1 @@
Subproject commit 249b005fddf7cea0b2c2d1aff5a2414e47e70c0e
Subproject commit d0f58a9e4b0519a513a1e44d1b28109fd4a8e13e

View File

@ -25,7 +25,7 @@ var wsrows = [
{hpt: 12}, // "points"
{hpx: 16}, // "pixels"
,
{hpx: 24},
{hpx: 24, level:3},
{hidden: true}, // hide row
{hidden: false}
];

View File

@ -4292,6 +4292,8 @@ function parse_Row(blob, length) {
blob.l += 4; // reserved(2), unused(2)
var flags = blob.read_shift(1); // various flags
blob.l += 3; // reserved(8), ixfe(12), flags(4)
if(flags & 0x07) z.level = flags & 0x07;
// collapsed: flags & 0x10
if(flags & 0x20) z.hidden = true;
if(flags & 0x40) z.hpt = miyRw / 20;
return z;
@ -5730,7 +5732,8 @@ var PRN = (function() {
function finish_cell() {
var s = str.slice(start, end);
var cell = ({}/*:any*/);
if(s.charCodeAt(0) == 0x3D) { cell.t = 'n'; cell.f = s.substr(1); }
if(o.raw) { cell.t = 's'; cell.v = s; }
else if(s.charCodeAt(0) == 0x3D) { cell.t = 'n'; cell.f = s.substr(1); }
else if(s == "TRUE") { cell.t = 'b'; cell.v = true; }
else if(s == "FALSE") { cell.t = 'b'; cell.v = false; }
else if(!isNaN(v = +s)) { cell.t = 'n'; cell.w = s; cell.v = v; }
@ -5824,7 +5827,6 @@ function read_wb_ID(d, opts) {
return PRN.to_workbook(d, opts);
}
}
var WK_ = (function() {
function lotushopper(data, cb/*:RecordHopperCB*/, opts/*:any*/) {
if(!data) return;
@ -10999,6 +11001,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
rowobj = {}; rowrite = false;
if(tag.ht) { rowrite = true; rowobj.hpt = parseFloat(tag.ht); rowobj.hpx = pt2px(rowobj.hpt); }
if(tag.hidden == "1") { rowrite = true; rowobj.hidden = true; }
if(tag.outlineLevel != null) { rowrite = true; rowobj.level = +tag.outlineLevel; }
if(rowrite) rows[tagr-1] = rowobj;
}
@ -11128,7 +11131,7 @@ function write_ws_xml_data(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbook
if(_cell === undefined) continue;
if((cell = write_ws_xml_cell(_cell, ref, ws, opts, idx, wb)) != null) r.push(cell);
}
if(r.length > 0) {
if(r.length > 0 || rows && rows[R]) {
var params = ({r:rr}/*:any*/);
if(rows && rows[R]) {
var row = rows[R];
@ -11137,10 +11140,24 @@ function write_ws_xml_data(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbook
if (row.hpx) height = px2pt(row.hpx);
else if (row.hpt) height = row.hpt;
if (height > -1) { params.ht = height; params.customHeight = 1; }
if (row.level) { params.outlineLevel = row.level; }
}
o[o.length] = (writextag('row', r.join(""), params));
}
}
if(rows) for(; R < rows.length; ++R) {
if(rows && rows[R]) {
var params = ({r:R+1}/*:any*/);
var row = rows[R];
if(row.hidden) params.hidden = 1;
var height = -1;
if (row.hpx) height = px2pt(row.hpx);
else if (row.hpt) height = row.hpt;
if (height > -1) { params.ht = height; params.customHeight = 1; }
if (row.level) { params.outlineLevel = row.level; }
o[o.length] = (writextag('row', "", params));
}
}
return o.join("");
}
@ -11166,7 +11183,11 @@ function write_ws_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ {
o[o.length] = write_ws_xml_sheetviews(ws, opts, idx, wb);
/* TODO: store in WB, process styles */
if(opts.sheetFormat) o[o.length] = (writextag('sheetFormatPr', null, {defaultRowHeight:opts.sheetFormat.defaultRowHeight||'16', baseColWidth:opts.sheetFormat.baseColWidth||'10' }));
if(opts.sheetFormat) o[o.length] = (writextag('sheetFormatPr', null, {
defaultRowHeight:opts.sheetFormat.defaultRowHeight||'16',
baseColWidth:opts.sheetFormat.baseColWidth||'10',
outlineLevelRow:opts.sheetFormat.outlineLevelRow||'7'
}));
if(ws['!cols'] != null && ws['!cols'].length > 0) o[o.length] = (write_ws_xml_cols(ws, ws['!cols']));
@ -11260,6 +11281,7 @@ function parse_BrtRowHdr(data, length) {
data.l += 1; // TODO: top/bot padding
var flags = data.read_shift(1);
data.l = tgt;
if(flags & 0x07) z.level = flags & 0x07;
if(flags & 0x10) z.hidden = true;
if(flags & 0x20) z.hpt = miyRw / 20;
return z;
@ -11279,6 +11301,7 @@ function write_BrtRowHdr(R/*:number*/, range, ws) {
o.write_shift(1, 0); /* top/bot padding */
var flags = 0x0;
if(row.level) flags |= row.level;
if(row.hidden) flags |= 0x10;
if(row.hpx || row.hpt) flags |= 0x20;
o.write_shift(1, flags);
@ -11313,7 +11336,7 @@ function write_BrtRowHdr(R/*:number*/, range, ws) {
}
function write_row_header(ba, ws, range, R) {
var o = write_BrtRowHdr(R, range, ws);
if(o.length > 17) write_record(ba, 'BrtRowHdr', o);
if(o.length > 17 || (ws['!rows']||[])[R]) write_record(ba, 'BrtRowHdr', o);
}
/* [MS-XLSB] 2.4.812 BrtWsDim */
@ -11677,7 +11700,7 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ {
if(opts.sheetRows && opts.sheetRows <= row.r) end=true;
rr = encode_row(R = row.r);
opts['!row'] = row.r;
if(val.hidden || val.hpt) {
if(val.hidden || val.hpt || val.level != null) {
if(val.hpt) val.hpx = pt2px(val.hpt);
rowinfo[val.r] = val;
}
@ -11935,12 +11958,14 @@ function write_CELLTABLE(ba, ws/*:Worksheet*/, idx/*:number*/, opts, wb/*:Workbo
var range = safe_decode_range(ws['!ref'] || "A1"), ref, rr = "", cols = [];
write_record(ba, 'BrtBeginSheetData');
var dense = Array.isArray(ws);
for(var R = range.s.r; R <= range.e.r; ++R) {
var cap = range.e.r;
if(ws['!rows']) cap = Math.max(range.e.r, ws['!rows'].length - 1);
for(var R = range.s.r; R <= cap; ++R) {
rr = encode_row(R);
/* [ACCELLTABLE] */
/* BrtRowHdr */
write_row_header(ba, ws, range, R);
for(var C = range.s.c; C <= range.e.c; ++C) {
if(R <= range.e.r) for(var C = range.s.c; C <= range.e.c; ++C) {
/* *16384CELL */
if(R === range.s.r) cols[C] = encode_col(C);
ref = cols[C] + rr;
@ -14524,6 +14549,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
} break;
case 'Row': {
var rowobj = {};
if(val.level != null) { rowinfo[val.r] = rowobj; rowobj.level = val.level; }
if(val.hidden) { rowinfo[val.r] = rowobj; rowobj.hidden = true; }
if(val.hpt) {
rowinfo[val.r] = rowobj;
@ -16644,6 +16670,9 @@ var parse_content_xml = (function() {
case 'forms': break; // 12.25.2 13.2
case 'table-column': break; // 9.1.6 <table:table-column>
/* TODO: outline levels */
case 'table-row-group': break; // 9.1.9 <table:table-row-group>
case 'table-column-group': break; // 9.1.10 <table:table-column-group>
case 'null-date': break; // 9.4.2 <table:null-date> TODO: date1904

45
xlsx.js
View File

@ -4230,6 +4230,8 @@ function parse_Row(blob, length) {
blob.l += 4; // reserved(2), unused(2)
var flags = blob.read_shift(1); // various flags
blob.l += 3; // reserved(8), ixfe(12), flags(4)
if(flags & 0x07) z.level = flags & 0x07;
// collapsed: flags & 0x10
if(flags & 0x20) z.hidden = true;
if(flags & 0x40) z.hpt = miyRw / 20;
return z;
@ -5668,7 +5670,8 @@ var PRN = (function() {
function finish_cell() {
var s = str.slice(start, end);
var cell = ({});
if(s.charCodeAt(0) == 0x3D) { cell.t = 'n'; cell.f = s.substr(1); }
if(o.raw) { cell.t = 's'; cell.v = s; }
else if(s.charCodeAt(0) == 0x3D) { cell.t = 'n'; cell.f = s.substr(1); }
else if(s == "TRUE") { cell.t = 'b'; cell.v = true; }
else if(s == "FALSE") { cell.t = 'b'; cell.v = false; }
else if(!isNaN(v = +s)) { cell.t = 'n'; cell.w = s; cell.v = v; }
@ -5762,7 +5765,6 @@ function read_wb_ID(d, opts) {
return PRN.to_workbook(d, opts);
}
}
var WK_ = (function() {
function lotushopper(data, cb, opts) {
if(!data) return;
@ -10933,6 +10935,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
rowobj = {}; rowrite = false;
if(tag.ht) { rowrite = true; rowobj.hpt = parseFloat(tag.ht); rowobj.hpx = pt2px(rowobj.hpt); }
if(tag.hidden == "1") { rowrite = true; rowobj.hidden = true; }
if(tag.outlineLevel != null) { rowrite = true; rowobj.level = +tag.outlineLevel; }
if(rowrite) rows[tagr-1] = rowobj;
}
@ -11062,7 +11065,7 @@ function write_ws_xml_data(ws, opts, idx, wb, rels) {
if(_cell === undefined) continue;
if((cell = write_ws_xml_cell(_cell, ref, ws, opts, idx, wb)) != null) r.push(cell);
}
if(r.length > 0) {
if(r.length > 0 || rows && rows[R]) {
var params = ({r:rr});
if(rows && rows[R]) {
var row = rows[R];
@ -11071,10 +11074,24 @@ function write_ws_xml_data(ws, opts, idx, wb, rels) {
if (row.hpx) height = px2pt(row.hpx);
else if (row.hpt) height = row.hpt;
if (height > -1) { params.ht = height; params.customHeight = 1; }
if (row.level) { params.outlineLevel = row.level; }
}
o[o.length] = (writextag('row', r.join(""), params));
}
}
if(rows) for(; R < rows.length; ++R) {
if(rows && rows[R]) {
var params = ({r:R+1});
var row = rows[R];
if(row.hidden) params.hidden = 1;
var height = -1;
if (row.hpx) height = px2pt(row.hpx);
else if (row.hpt) height = row.hpt;
if (height > -1) { params.ht = height; params.customHeight = 1; }
if (row.level) { params.outlineLevel = row.level; }
o[o.length] = (writextag('row', "", params));
}
}
return o.join("");
}
@ -11100,7 +11117,11 @@ function write_ws_xml(idx, opts, wb, rels) {
o[o.length] = write_ws_xml_sheetviews(ws, opts, idx, wb);
/* TODO: store in WB, process styles */
if(opts.sheetFormat) o[o.length] = (writextag('sheetFormatPr', null, {defaultRowHeight:opts.sheetFormat.defaultRowHeight||'16', baseColWidth:opts.sheetFormat.baseColWidth||'10' }));
if(opts.sheetFormat) o[o.length] = (writextag('sheetFormatPr', null, {
defaultRowHeight:opts.sheetFormat.defaultRowHeight||'16',
baseColWidth:opts.sheetFormat.baseColWidth||'10',
outlineLevelRow:opts.sheetFormat.outlineLevelRow||'7'
}));
if(ws['!cols'] != null && ws['!cols'].length > 0) o[o.length] = (write_ws_xml_cols(ws, ws['!cols']));
@ -11194,6 +11215,7 @@ function parse_BrtRowHdr(data, length) {
data.l += 1; // TODO: top/bot padding
var flags = data.read_shift(1);
data.l = tgt;
if(flags & 0x07) z.level = flags & 0x07;
if(flags & 0x10) z.hidden = true;
if(flags & 0x20) z.hpt = miyRw / 20;
return z;
@ -11213,6 +11235,7 @@ function write_BrtRowHdr(R, range, ws) {
o.write_shift(1, 0); /* top/bot padding */
var flags = 0x0;
if(row.level) flags |= row.level;
if(row.hidden) flags |= 0x10;
if(row.hpx || row.hpt) flags |= 0x20;
o.write_shift(1, flags);
@ -11247,7 +11270,7 @@ function write_BrtRowHdr(R, range, ws) {
}
function write_row_header(ba, ws, range, R) {
var o = write_BrtRowHdr(R, range, ws);
if(o.length > 17) write_record(ba, 'BrtRowHdr', o);
if(o.length > 17 || (ws['!rows']||[])[R]) write_record(ba, 'BrtRowHdr', o);
}
/* [MS-XLSB] 2.4.812 BrtWsDim */
@ -11610,7 +11633,7 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles) {
if(opts.sheetRows && opts.sheetRows <= row.r) end=true;
rr = encode_row(R = row.r);
opts['!row'] = row.r;
if(val.hidden || val.hpt) {
if(val.hidden || val.hpt || val.level != null) {
if(val.hpt) val.hpx = pt2px(val.hpt);
rowinfo[val.r] = val;
}
@ -11868,12 +11891,14 @@ function write_CELLTABLE(ba, ws, idx, opts, wb) {
var range = safe_decode_range(ws['!ref'] || "A1"), ref, rr = "", cols = [];
write_record(ba, 'BrtBeginSheetData');
var dense = Array.isArray(ws);
for(var R = range.s.r; R <= range.e.r; ++R) {
var cap = range.e.r;
if(ws['!rows']) cap = Math.max(range.e.r, ws['!rows'].length - 1);
for(var R = range.s.r; R <= cap; ++R) {
rr = encode_row(R);
/* [ACCELLTABLE] */
/* BrtRowHdr */
write_row_header(ba, ws, range, R);
for(var C = range.s.c; C <= range.e.c; ++C) {
if(R <= range.e.r) for(var C = range.s.c; C <= range.e.c; ++C) {
/* *16384CELL */
if(R === range.s.r) cols[C] = encode_col(C);
ref = cols[C] + rr;
@ -14450,6 +14475,7 @@ wb.opts.Date1904 = Workbook.WBProps.date1904 = val; break;
} break;
case 'Row': {
var rowobj = {};
if(val.level != null) { rowinfo[val.r] = rowobj; rowobj.level = val.level; }
if(val.hidden) { rowinfo[val.r] = rowobj; rowobj.hidden = true; }
if(val.hpt) {
rowinfo[val.r] = rowobj;
@ -16570,6 +16596,9 @@ var parse_content_xml = (function() {
case 'forms': break; // 12.25.2 13.2
case 'table-column': break; // 9.1.6 <table:table-column>
/* TODO: outline levels */
case 'table-row-group': break; // 9.1.9 <table:table-row-group>
case 'table-column-group': break; // 9.1.10 <table:table-column-group>
case 'null-date': break; // 9.4.2 <table:null-date> TODO: date1904