Skip to content

Commit

Permalink
more format parity
Browse files Browse the repository at this point in the history
- XLS/XLSX/XLSB/XML/ODS cellFormula option + test
- XLS/XLSX/XLSB/XML/ODS cellText option + test
- XML document property order
- XML write margins + protection
- removed gitbook search
  • Loading branch information
SheetJSDev committed Apr 26, 2017
1 parent 83ec606 commit 95a377c
Show file tree
Hide file tree
Showing 13 changed files with 438 additions and 101 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ enhancements and additional features by request.

[**Commercial Support**](http://sheetjs.com/support)

[**Rendered Documentation**](https://sheetjs.gitbooks.io/docs/)

[**In-Browser Demos**](http://sheetjs.com/demos)

[**Source Code**](http://git.io/xlsx)
Expand Down Expand Up @@ -634,8 +636,9 @@ In addition to the base sheet keys, worksheets also add:
the first cell (upper-left) in the range is set.

- `ws['protect']`: object of write sheet protection properties. The `password`
key specifies the password. The writer uses the XOR obfuscation method. The
following keys control the sheet protection (same as ECMA-376 18.3.1.85):
key specifies the password for formats that support password-protected sheets
(XLSX/XLSB/XLS). The writer uses the XOR obfuscation method. The following
keys control the sheet protection (same as ECMA-376 18.3.1.85):

| key | functionality disabled if value is true |
|:----------------------|:-----------------------------------------------------|
Expand All @@ -659,7 +662,7 @@ In addition to the base sheet keys, worksheets also add:

```typescript
type AutoFilter = {
ref:string; // A-1 based range representing the AutoFilter table range
ref:string; // A-1 based range representing the AutoFilter table range
}
```
Expand Down
50 changes: 31 additions & 19 deletions bits/36_xlsprops.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
/* Common Name -> XLML Name */
var XLMLDocPropsMap = {
Category: 'Category',
ContentStatus: 'ContentStatus', /* NOTE: missing from schema */
Title: 'Title',
Subject: 'Subject',
Author: 'Author',
Keywords: 'Keywords',
Comments: 'Description',
LastAuthor: 'LastAuthor',
LastPrinted: 'LastPrinted',
RevNumber: 'Revision',
Author: 'Author',
Comments: 'Description',
Identifier: 'Identifier', /* NOTE: missing from schema */
Language: 'Language', /* NOTE: missing from schema */
Subject: 'Subject',
Title: 'Title',
Application: 'AppName',
/* TotalTime: 'TotalTime', */
LastPrinted: 'LastPrinted',
CreatedDate: 'Created',
ModifiedDate: 'LastSaved',

Application: 'AppName',
AppVersion: 'Version',
TotalTime: 'TotalTime',
/* Pages */
/* Words */
/* Characters */
Category: 'Category',
/* PresentationFormat */
Manager: 'Manager',
Company: 'Company'
Company: 'Company',
/* Guid */
/* HyperlinkBase */
/* Bytes */
/* Lines */
/* Paragraphs */
/* CharactersWithSpaces */
AppVersion: 'Version',

ContentStatus: 'ContentStatus', /* NOTE: missing from schema */
Identifier: 'Identifier', /* NOTE: missing from schema */
Language: 'Language' /* NOTE: missing from schema */
};
var evert_XLMLDPM = evert(XLMLDocPropsMap);

Expand All @@ -28,19 +38,21 @@ function xlml_set_prop(Props, tag/*:string*/, val) {
Props[tag] = val;
}


/* TODO: verify */
function xlml_write_docprops(Props, opts) {
var o = [];
CORE_PROPS.concat(EXT_PROPS).forEach(function(p) {
keys(XLMLDocPropsMap).map(function(m) {
for(var i = 0; i < CORE_PROPS.length; ++i) if(CORE_PROPS[i][1] == m) return CORE_PROPS[i];
for(i = 0; i < EXT_PROPS.length; ++i) if(EXT_PROPS[i][1] == m) return EXT_PROPS[i];
throw m;
}).forEach(function(p) {
if(Props[p[1]] == null) return;
var m = opts && opts.Props && opts.Props[p[1]] != null ? opts.Props[p[1]] : Props[p[1]];
switch(p[2]) {
case 'date': m = new Date(m).toISOString(); break;
case 'date': m = new Date(m).toISOString().replace(/\.\d*Z/,"Z"); break;
}
if(typeof m == 'number') m = String(m);
else if(m === true || m === false) { m = m ? "1" : "0"; }
else if(m instanceof Date) m = new Date(m).toISOString();
else if(m instanceof Date) m = new Date(m).toISOString().replace(/\.\d*Z/,"");
o.push(writetag(XLMLDocPropsMap[p[1]] || p[1], m));
});
return writextag('DocumentProperties', o.join(""), {xmlns:XLMLNS.o });
Expand Down
7 changes: 3 additions & 4 deletions bits/67_wsxml.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
/* 18.3.1.73 row CT_Row */
for(ri = 0; ri < xlen; ++ri) if(x.charCodeAt(ri) === 62) break; ++ri;
tag = parsexmltag(x.substr(0,ri), true);
/* SpreadSheetGear uses implicit r/c */
tagr = typeof tag.r !== 'undefined' ? parseInt(tag.r, 10) : tagr+1; tagc = -1;
tagr = tag.r != null ? parseInt(tag.r, 10) : tagr+1; tagc = -1;
if(opts.sheetRows && opts.sheetRows < tagr) continue;
if(guess.s.r > tagr - 1) guess.s.r = tagr - 1;
if(guess.e.r < tagr - 1) guess.e.r = tagr - 1;
Expand Down Expand Up @@ -297,7 +296,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
p.F = arrayf[i][1];
}

if(tag.t === undefined && p.v === undefined) {
if(tag.t == null && p.v === undefined) {
if(!opts.sheetStubs) continue;
p.t = "z";
}
Expand Down Expand Up @@ -335,7 +334,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
break;
/* error string in .w, number in .v */
case 'e':
if(opts && opts.cellText === false) p.w = p.v;
if(!opts || opts.cellText !== false) p.w = p.v;
p.v = RBErr[p.v]; break;
}
/* formatting */
Expand Down
2 changes: 1 addition & 1 deletion bits/68_wsbin.js
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ {
case 'n': p.v = val[1]; break;
case 's': sstr = strs[val[1]]; p.v = sstr.t; p.r = sstr.r; break;
case 'b': p.v = val[1] ? true : false; break;
case 'e': p.v = val[1]; p.w = BErr[p.v]; break;
case 'e': p.v = val[1]; if(opts.cellText !== false) p.w = BErr[p.v]; break;
case 'str': p.t = 's'; p.v = utf8read(val[1]); break;
}
if((cf = styles.CellXf[val[0].iStyleRef])) safe_format(p,cf.ifmt,null,opts, themes, styles);
Expand Down
96 changes: 93 additions & 3 deletions bits/75_xlml.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,11 @@ function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, a
if(cell.v === undefined) cell.v=+xml;
if(!cell.t) cell.t = 'n';
break;
case 'Error': cell.t = 'e'; cell.v = RBErr[xml]; cell.w = xml; break;
case 'Error': cell.t = 'e'; cell.v = RBErr[xml]; if(o.cellText !== false) cell.w = xml; break;
default: cell.t = 's'; cell.v = xlml_fixstr(ss||xml); break;
}
safe_format_xlml(cell, nf, o);
if(o.cellFormula != null) {
if(o.cellFormula !== false) {
if(cell.Formula) {
var fstr = unescapexml(cell.Formula);
/* strictly speaking, the leading = is required but some writers omit */
Expand Down Expand Up @@ -837,17 +837,107 @@ function write_sty_xlml(wb, opts)/*:string*/ {
}
/* WorksheetOptions */
function write_ws_xlml_wsopts(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbook*/)/*:string*/ {
if(!ws) return "";
var o = [];
/* NOTE: spec technically allows any order, but stick with implied order */

/* FitToPage */
/* DoNotDisplayColHeaders */
/* DoNotDisplayRowHeaders */
/* ViewableRange */
/* Selection */
/* GridlineColor */
/* Name */
/* ExcelWorksheetType */
/* IntlMacro */
/* Unsynced */
/* Selected */
/* CodeName */

if(ws['!margins']) {
o.push("<PageSetup>");
if(ws['!margins'].header) o.push(writextag("Header", null, {'x:Margin':ws['!margins'].header}));
if(ws['!margins'].footer) o.push(writextag("Footer", null, {'x:Margin':ws['!margins'].footer}));
o.push(writextag("PageMargins", null, {
'x:Bottom': ws['!margins'].bottom || "0.75",
'x:Left': ws['!margins'].left || "0.7",
'x:Right': ws['!margins'].right || "0.7",
'x:Top': ws['!margins'].top || "0.75"
}));
o.push("</PageSetup>");
}

/* PageSetup */
/* DisplayPageBreak */
/* TransitionExpressionEvaluation */
/* TransitionFormulaEntry */
/* Print */
/* Zoom */
/* PageLayoutZoom */
/* PageBreakZoom */
/* ShowPageBreakZoom */
/* DefaultRowHeight */
/* DefaultColumnWidth */
/* StandardWidth */

if(wb && wb.Workbook && wb.Workbook.Sheets && wb.Workbook.Sheets[idx]) {
/* Visible */
if(!!wb.Workbook.Sheets[idx].Hidden) o.push("<Visible>" + (wb.Workbook.Sheets[idx].Hidden == 1 ? "SheetHidden" : "SheetVeryHidden") + "</Visible>");
if(!!wb.Workbook.Sheets[idx].Hidden) o.push(writextag("Visible", (wb.Workbook.Sheets[idx].Hidden == 1 ? "SheetHidden" : "SheetVeryHidden"), {}));
else {
/* Selected */
for(var i = 0; i < idx; ++i) if(wb.Workbook.Sheets[i] && !wb.Workbook.Sheets[i].Hidden) break;
if(i == idx) o.push("<Selected/>");
}
}

/* LeftColumnVisible */
/* DisplayRightToLeft */
/* GridlineColorIndex */
/* DisplayFormulas */
/* DoNotDisplayGridlines */
/* DoNotDisplayHeadings */
/* DoNotDisplayOutline */
/* ApplyAutomaticOutlineStyles */
/* NoSummaryRowsBelowDetail */
/* NoSummaryColumnsRightDetail */
/* DoNotDisplayZeros */
/* ActiveRow */
/* ActiveColumn */
/* FilterOn */
/* RangeSelection */
/* TopRowVisible */
/* TopRowBottomPane */
/* LeftColumnRightPane */
/* ActivePane */
/* SplitHorizontal */
/* SplitVertical */
/* FreezePanes */
/* FrozenNoSplit */
/* TabColorIndex */
/* Panes */

/* NOTE: Password not supported in XLML Format */
if(ws['!protect']) {
o.push(writetag("ProtectContents", "True"));
if(ws['!protect'].objects) o.push(writetag("ProtectObjects", "True"));
if(ws['!protect'].scenarios) o.push(writetag("ProtectScenarios", "True"));
if(ws['!protect'].selectLockedCells != null && !ws['!protect'].selectLockedCells) o.push(writetag("EnableSelection", "NoSelection"));
else if(ws['!protect'].selectUnlockedCells != null && !ws['!protect'].selectUnlockedCells) o.push(writetag("EnableSelection", "UnlockedCells"));
[
[ "formatColumns", "AllowFormatCells" ],
[ "formatRows", "AllowSizeCols" ],
[ "formatCells", "AllowSizeRows" ],
[ "insertColumns", "AllowInsertCols" ],
[ "insertRows", "AllowInsertRows" ],
[ "insertHyperlinks", "AllowInsertHyperlinks" ],
[ "deleteColumns", "AllowDeleteCols" ],
[ "deleteRows", "AllowDeleteRows" ],
[ "sort", "AllowSort" ],
[ "autoFilter", "AllowFilter" ],
[ "pivotTables", "AllowUsePivotTables" ]
].forEach(function(x) { if(ws['!protect'][x[0]]) o.push("<"+x[1]+"/>"); });
}

if(o.length == 0) return "";
return writextag("WorksheetOptions", o.join(""), {xmlns:XLMLNS.x});
}
Expand Down
5 changes: 3 additions & 2 deletions bits/76_xls.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,13 @@ function slurp(R, blob, length/*:number*/, opts) {

function safe_format_xf(p/*:any*/, opts/*:ParseOpts*/, date1904/*:?boolean*/) {
if(p.t === 'z') return;
if(p.t === 'e') { p.w = p.w || BErr[p.v]; }
if(!p.XF) return;
try {
var fmtid = p.XF.ifmt||0;
if(opts.cellNF) p.z = SSF._table[fmtid];
if(p.t === 'e'){}
} catch(e) { if(opts.WTF) throw e; }
if(!opts || opts.cellText !== false) try {
if(p.t === 'e') { p.w = p.w || BErr[p.v]; }
else if(fmtid === 0) {
if(p.t === 'n') {
if((p.v|0) === p.v) p.w = SSF._general_int(p.v);
Expand Down
2 changes: 1 addition & 1 deletion bits/80_parseods.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ var parse_content_xml = (function() {
isstub = textpidx == 0;
}
if(comments.length > 0) { q.c = comments; comments = []; }
if(textp) q.w = textp;
if(textp && opts.cellText !== false) q.w = textp;
if(!isstub || opts.sheetStubs) {
if(!(opts.sheetRows && opts.sheetRows < R)) {
if(opts.dense) {
Expand Down
2 changes: 1 addition & 1 deletion book.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"title": "SheetJS js-xlsx",
"author": "sheetjs",
"gitbook": "3.2.2",
"plugins": ["anchorjs", "ga", "sidebar-ad", "-sharing", "advanced-emoji"],
"plugins": ["anchorjs", "ga", "sidebar-ad", "-sharing", "-search", "advanced-emoji"],
"pluginsConfig": {
"anchorjs": {
"icon": "#",
Expand Down
2 changes: 2 additions & 0 deletions docbits/00_intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ enhancements and additional features by request.

[**Commercial Support**](http://sheetjs.com/support)

[**Rendered Documentation**](https://sheetjs.gitbooks.io/docs/)

[**In-Browser Demos**](http://sheetjs.com/demos)

[**Source Code**](http://git.io/xlsx)
Expand Down
7 changes: 4 additions & 3 deletions docbits/54_shobject.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ In addition to the base sheet keys, worksheets also add:
the first cell (upper-left) in the range is set.

- `ws['protect']`: object of write sheet protection properties. The `password`
key specifies the password. The writer uses the XOR obfuscation method. The
following keys control the sheet protection (same as ECMA-376 18.3.1.85):
key specifies the password for formats that support password-protected sheets
(XLSX/XLSB/XLS). The writer uses the XOR obfuscation method. The following
keys control the sheet protection (same as ECMA-376 18.3.1.85):

| key | functionality disabled if value is true |
|:----------------------|:-----------------------------------------------------|
Expand All @@ -39,7 +40,7 @@ In addition to the base sheet keys, worksheets also add:

```typescript
type AutoFilter = {
ref:string; // A-1 based range representing the AutoFilter table range
ref:string; // A-1 based range representing the AutoFilter table range
}
```
Expand Down
Loading

0 comments on commit 95a377c

Please sign in to comment.