diff --git a/.gitignore b/.gitignore
index 83bd2c5..36b818a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,7 @@ tmp
*.[wW][kKqQbB][S1234567890]
*.[qQ][pP][wW]
*.[bB][iI][fF][fF][23458]
+*.[rR][tT][fF]
*.123
*.htm
*.html
diff --git a/.npmignore b/.npmignore
index 7ce3209..4455c2f 100644
--- a/.npmignore
+++ b/.npmignore
@@ -23,6 +23,7 @@ tmp
*.[wW][kKqQbB][S1234567890]
*.[qQ][pP][wW]
*.[bB][iI][fF][fF][23458]
+*.[rR][tT][fF]
*.123
*.htm
*.html
@@ -30,6 +31,7 @@ tmp
*.exe
.gitignore
.fossaignore
+.spelling
.eslintrc
.jshintrc
CONTRIBUTING.md
diff --git a/.spelling b/.spelling
index a6a043b..9a537bb 100644
--- a/.spelling
+++ b/.spelling
@@ -18,10 +18,15 @@ PivotTable
Quattro
SpreadsheetML
Unhide
+VBA
Visicalc
chartsheet
chartsheets
+dialogsheet
+dialogsheets
dBASE
+macrosheet
+macrosheets
tooltip
tooltips
diff --git a/.travis.yml b/.travis.yml
index 278255a..fa70776 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,13 +3,6 @@ node_js:
- "8"
- "7"
- "6"
-# note: travis has been acting up on old versions of node
-# - "5"
-# - "4"
-# - "0.12"
-# - "0.10"
-# - "0.9"
-# - "0.8"
matrix:
include:
- node_js: "6"
@@ -26,6 +19,18 @@ matrix:
env: TZ="Asia/Shanghai"
- node_js: "8"
env: TZ="Asia/Seoul" FMTS=misc
+
+ - node_js: "5"
+ env: TZ="America/Anchorage" FMTS=misc
+ - node_js: "4"
+ env: TZ="America/Barbados" FMTS=misc
+ - node_js: "0.12"
+ env: TZ="America/Cayman" FMTS=misc
+ - node_js: "0.10"
+ env: TZ="Pacific/Honolulu" FMTS=misc
+ - node_js: "0.8"
+ env: TZ="America/Mexico_City" FMTS=misc
+
before_install:
- "npm install -g npm@4.3.0"
- "npm install -g mocha@2.x voc"
diff --git a/README.md b/README.md
index 2c7937e..f2616a8 100644
--- a/README.md
+++ b/README.md
@@ -78,6 +78,8 @@ enhancements, additional features by request, and dedicated support.
* [Sheet Objects](#sheet-objects)
+ [Worksheet Object](#worksheet-object)
+ [Chartsheet Object](#chartsheet-object)
+ + [Macrosheet Object](#macrosheet-object)
+ + [Dialogsheet Object](#dialogsheet-object)
* [Workbook Object](#workbook-object)
+ [Workbook File Properties](#workbook-file-properties)
* [Workbook-Level Attributes](#workbook-level-attributes)
@@ -91,6 +93,7 @@ enhancements, additional features by request, and dedicated support.
+ [Hyperlinks](#hyperlinks)
+ [Cell Comments](#cell-comments)
+ [Sheet Visibility](#sheet-visibility)
+ + [VBA and Macros](#vba-and-macros)
- [Parsing Options](#parsing-options)
* [Input Type](#input-type)
* [Guessing File Type](#guessing-file-type)
@@ -124,6 +127,7 @@ enhancements, additional features by request, and dedicated support.
+ [Lotus Formatted Text (PRN)](#lotus-formatted-text-prn)
+ [Data Interchange Format (DIF)](#data-interchange-format-dif)
+ [HTML](#html)
+ + [Rich Text Format (RTF)](#rich-text-format-rtf)
- [Testing](#testing)
* [Node](#node)
* [Browser](#browser)
@@ -983,6 +987,16 @@ Chartsheets are represented as standard sheets. They are distinguished with the
The underlying data and `!ref` refer to the cached data in the chartsheet. The
first row of the chartsheet is the underlying header.
+#### Macrosheet Object
+
+Macrosheets are represented as standard sheets. They are distinguished with the
+`!type` property set to `"macro"`.
+
+#### Dialogsheet Object
+
+Dialogsheets are represented as standard sheets. They are distinguished with the
+`!type` property set to `"dialog"`.
+
### Workbook Object
`workbook.SheetNames` is an ordered list of the sheets in the workbook
@@ -1421,6 +1435,37 @@ if a sheet is visible is to check if the `Hidden` property is logical truth:
```
+#### VBA and Macros
+
+VBA Macros are stored in a special data blob that is exposed in the `vbaraw`
+property of the workbook object when the `bookVBA` option is `true`. They are
+supported in `XLSM`, `XLSB`, and `BIFF8 XLS` formats. The `XLSM` and `XLSB`
+writers automatically insert the data blobs if it is present in the workbook.
+
+
+ Macrosheets (click to show)
+
+Older versions of Excel also supported a non-VBA "macrosheet" sheet type that
+stored automation commands. These are exposed in objects with the `!type`
+property set to `"macro"`.
+
+
+
+
+ Detecting macros in workbooks (click to show)
+
+The `vbaraw` field will only be set if macros are present, so testing is simple:
+
+```js
+function wb_has_macro(wb/*:workbook*/)/*:boolean*/ {
+ if(!!wb.vbaraw) return true;
+ const sheets = wb.SheetNames.map((n) => wb.Sheets[n]);
+ return sheets.some((ws) => !!ws && ws['!type']=='macro');
+}
+```
+
+
+
## Parsing Options
The exported `read` and `readFile` functions accept an options argument:
@@ -1459,7 +1504,9 @@ The exported `read` and `readFile` functions accept an options argument:
- `sheetRows-1` rows will be generated when looking at the JSON object output
(since the header row is counted as a row when parsing the data)
- `bookVBA` merely exposes the raw VBA CFB object. It does not parse the data.
- XLSM and XLSB store the VBA CFB object in `xl/vbaProject.bin`.
+ XLSM and XLSB store the VBA CFB object in `xl/vbaProject.bin`. BIFF8 XLS mixes
+ the VBA entries alongside the core Workbook entry, so the library generates a
+ new XLSB-compatible blob from the XLS CFB container.
- Currently only XOR encryption is supported. Unsupported error will be thrown
for files employing other encryption methods.
- WTF is mainly for development. By default, the parser will suppress read
@@ -1591,6 +1638,7 @@ output formats. The specific file type is controlled with `bookType` option:
| `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) |
| `html` | `.html` | none | single | HTML Document |
| `dif` | `.dif` | none | single | Data Interchange Format (DIF) |
+| `rtf` | `.rtf` | none | single | Rich Text Format |
| `prn` | `.prn` | none | single | Lotus Formatted Text |
- `compression` only applies to formats with ZIP containers.
@@ -1928,6 +1976,7 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | :o: | |
| **Other Common Spreadsheet Output Formats** |:-----:|:-----:|
| HTML Tables | :o: | :o: |
+| RTF Tables | | :o: |
### Excel 2007+ XML (XLSX/XLSM)
@@ -2151,6 +2200,17 @@ the metadata the output is valid HTML, although it does accept bare `&` symbols.
+#### Rich Text Format (RTF)
+
+
+ (click to show)
+
+Excel RTF worksheets are stored in clipboard when copying cells or ranges from a
+worksheet. The supported codes are a subset of the Word RTF support.
+
+
+
+
## Testing
### Node
@@ -2394,6 +2454,7 @@ granted by the Apache 2.0 License are reserved by the Original Author.
- `MS-XLSB`: Excel (.xlsb) Binary File Format
- `MS-XLSX`: Excel (.xlsx) Extensions to the Office Open XML SpreadsheetML File Format
- `XLS`: Microsoft Office Excel 97-2007 Binary File Format Specification
+ - `RTF`: Rich Text Format
diff --git a/bin/xlsx.njs b/bin/xlsx.njs
index e2f9e39..1bd8344 100755
--- a/bin/xlsx.njs
+++ b/bin/xlsx.njs
@@ -28,15 +28,16 @@ program
.option('-6, --xlml', 'emit SSML to or .xls (2003 XML)')
.option('-T, --fods', 'emit FODS to or .fods (Flat ODS)')
- .option('-S, --formulae', 'print formulae')
- .option('-j, --json', 'emit formatted JSON (all fields text)')
- .option('-J, --raw-js', 'emit raw JS object (raw numbers)')
- .option('-A, --arrays', 'emit rows as JS objects (raw numbers)')
- .option('-H, --html', 'emit HTML')
- .option('-D, --dif', 'emit data interchange format (dif)')
- .option('-K, --sylk', 'emit symbolic link (sylk)')
- .option('-P, --prn', 'emit formatted text (prn)')
- .option('-t, --txt', 'emit delimited text (txt)')
+ .option('-S, --formulae', 'emit list of values and formulae')
+ .option('-j, --json', 'emit formatted JSON (all fields text)')
+ .option('-J, --raw-js', 'emit raw JS object (raw numbers)')
+ .option('-A, --arrays', 'emit rows as JS objects (raw numbers)')
+ .option('-H, --html', 'emit HTML to or .html')
+ .option('-D, --dif', 'emit DIF to or .dif (Lotus DIF)')
+ .option('-K, --sylk', 'emit SYLK to or .slk (Excel SYLK)')
+ .option('-P, --prn', 'emit PRN to or .prn (Lotus PRN)')
+ .option('-t, --txt', 'emit TXT to or .txt (UTF-8 TSV)')
+ .option('-r, --rtf', 'emit RTF to or .txt (Table RTF)')
.option('-F, --field-sep ', 'CSV field separator', ",")
.option('-R, --row-sep ', 'CSV row separator', "\n")
@@ -62,7 +63,7 @@ var workbook_formats = [
['xlsm', 'xlsm', 'xlsm'],
['xlsb', 'xlsb', 'xlsb'],
['xls', 'xls', 'xls'],
- //['biff5', 'biff5', 'xls'],
+ ['biff5', 'biff5', 'xls'],
['ods', 'ods', 'ods'],
['fods', 'fods', 'fods']
];
@@ -180,11 +181,12 @@ if(program.readOnly) process.exit(0);
/* single worksheet formats */
[
['biff2', '.xls'],
- //['biff3', '.xls'],
- //['biff4', '.xls'],
+ ['biff3', '.xls'],
+ ['biff4', '.xls'],
['sylk', '.slk'],
['html', '.html'],
['prn', '.prn'],
+ ['rtf', '.rtf'],
['txt', '.txt'],
['dif', '.dif']
].forEach(function(m) { if(program[m[0]] || isfmt(m[1])) {
diff --git a/bits/23_binutils.js b/bits/23_binutils.js
index 5beb26a..b6f36a4 100644
--- a/bits/23_binutils.js
+++ b/bits/23_binutils.js
@@ -27,7 +27,7 @@ function write_double_le(b/*:RawBytes|CFBlob*/, v/*:number*/, idx/*:number*/) {
var __toBuffer = function(bufs) { var x = []; for(var i = 0; i < bufs[0].length; ++i) { x.push.apply(x, bufs[0][i]); } return x; };
var ___toBuffer = __toBuffer;
-var __utf16le = function(b/*:RawBytes|CFBlob*/,s/*:number*/,e/*:number*/)/*:string*/ { var ss/*:Array*/=[]; for(var i=s; i*/=[]; for(var i=s; i*/=[]; for(var i=s; i 0 ? b.toString('utf8',i+4,i+4+len-1) : "";};
__lpwstr = function lpwstr_b(b/*:RawBytes|CFBlob*/, i/*:number*/) { if(!Buffer.isBuffer(b)/*:: || !(b instanceof Buffer)*/) return ___lpwstr(b, i); var len = 2*b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len-1);};
@@ -61,7 +61,7 @@ if(has_buf/*:: && typeof Buffer !== 'undefined'*/) {
/* from js-xls */
if(typeof cptable !== 'undefined') {
- __utf16le = function(b/*:RawBytes|CFBlob*/,s/*:number*/,e/*:number*/) { return cptable.utils.decode(1200, b.slice(s,e)); };
+ __utf16le = function(b/*:RawBytes|CFBlob*/,s/*:number*/,e/*:number*/) { return cptable.utils.decode(1200, b.slice(s,e)).replace(chr0, ''); };
__utf8 = function(b/*:RawBytes|CFBlob*/,s/*:number*/,e/*:number*/) { return cptable.utils.decode(65001, b.slice(s,e)); };
__lpstr = function(b/*:RawBytes|CFBlob*/,i/*:number*/) { var len = __readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(current_codepage, b.slice(i+4, i+4+len-1)) : "";};
__lpwstr = function(b/*:RawBytes|CFBlob*/,i/*:number*/) { var len = 2*__readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(1200, b.slice(i+4,i+4+len-1)) : "";};
diff --git a/bits/45_rtf.js b/bits/45_rtf.js
index 2cdd2af..d947a10 100644
--- a/bits/45_rtf.js
+++ b/bits/45_rtf.js
@@ -10,11 +10,39 @@ var RTF = (function() {
}
function rtf_to_sheet_str(str/*:string*/, opts)/*:Worksheet*/ {
- throw new Error("Unsupported RTF");
+ var o = opts || {};
+ var ws/*:Worksheet*/ = ({}/*:any*/);
+ var range/*:Range*/ = ({s: {c:0, r:0}, e: {c:0, r:0}}/*:any*/);
+
+ // TODO: parse
+ if(!str.match(/\\trowd/)) throw new Error("RTF missing table");
+
+ ws['!ref'] = encode_range(range);
+ return ws;
}
function rtf_to_workbook(d/*:RawData*/, opts)/*:Workbook*/ { return sheet_to_workbook(rtf_to_sheet(d, opts), opts); }
- function sheet_to_rtf() { throw new Error("Unsupported"); }
+
+ /* TODO: this is a stub */
+ function sheet_to_rtf(ws/*:Worksheet*/, opts)/*:string*/ {
+ var o = ["{\\rtf1\\ansi"];
+ var r = safe_decode_range(ws['!ref']), cell/*:Cell*/;
+ var dense = Array.isArray(ws);
+ for(var R = r.s.r; R <= r.e.r; ++R) {
+ o.push("\\trowd\\trautofit1");
+ for(var C = r.s.c; C <= r.e.c; ++C) o.push("\\cellx" + (C+1));
+ o.push("\\pard\\intbl");
+ for(C = r.s.c; C <= r.e.c; ++C) {
+ var coord = encode_cell({r:R,c:C});
+ cell = dense ? (ws[R]||[])[C]: ws[coord];
+ if(!cell || cell.v == null && (!cell.f || cell.F)) continue;
+ o.push(" " + (cell.w || (format_cell(cell), cell.w)));
+ o.push("\\cell");
+ }
+ o.push("\\pard\\intbl\\row");
+ }
+ return o.join("") + "}";
+ }
return {
to_workbook: rtf_to_workbook,
diff --git a/bits/59_vba.js b/bits/59_vba.js
new file mode 100644
index 0000000..775615c
--- /dev/null
+++ b/bits/59_vba.js
@@ -0,0 +1,9 @@
+function make_vba_xls(cfb/*:CFBContainer*/) {
+ var newcfb = CFB.utils.cfb_new({root:"R"});
+ cfb.FullPaths.forEach(function(p, i) {
+ if(p.slice(-1) === "/" || !p.match(/_VBA_PROJECT_CUR/)) return;
+ var newpath = p.replace(/^[^/]*/,"R").replace(/\/_VBA_PROJECT_CUR\u0000*/, "");
+ CFB.utils.cfb_add(newcfb, newpath, cfb.FileIndex[i].content);
+ });
+ return CFB.write(newcfb);
+}
diff --git a/bits/72_wbxml.js b/bits/72_wbxml.js
index 3953617..6fb3900 100644
--- a/bits/72_wbxml.js
+++ b/bits/72_wbxml.js
@@ -136,7 +136,8 @@ function parse_wb_xml(data, opts)/*:WorkbookFile*/ {
/* Others */
case '': pass=true; break;
case '': pass=false; break;
/* TODO */
diff --git a/bits/76_xls.js b/bits/76_xls.js
index 894baae..51c178b 100644
--- a/bits/76_xls.js
+++ b/bits/76_xls.js
@@ -868,6 +868,7 @@ else/*:: if(cfb instanceof CFBContainer) */ {
/* Quattro Pro 9 */
else if((_data=CFB.find(cfb, 'NativeContent_MAIN')) && _data.content) WorkbookP = WK_.to_workbook(_data.content, (options.type = T, options));
else throw new Error("Cannot find Workbook stream");
+ if(options.bookVBA && CFB.find(cfb, '/_VBA_PROJECT_CUR/VBA/dir')) WorkbookP.vbaraw = make_vba_xls(cfb);
}
var props = {};
diff --git a/docbits/54_shobject.md b/docbits/54_shobject.md
index 3dd7700..483b760 100644
--- a/docbits/54_shobject.md
+++ b/docbits/54_shobject.md
@@ -60,3 +60,13 @@ Chartsheets are represented as standard sheets. They are distinguished with the
The underlying data and `!ref` refer to the cached data in the chartsheet. The
first row of the chartsheet is the underlying header.
+#### Macrosheet Object
+
+Macrosheets are represented as standard sheets. They are distinguished with the
+`!type` property set to `"macro"`.
+
+#### Dialogsheet Object
+
+Dialogsheets are represented as standard sheets. They are distinguished with the
+`!type` property set to `"dialog"`.
+
diff --git a/docbits/77_macrovba.md b/docbits/77_macrovba.md
new file mode 100644
index 0000000..93f3e84
--- /dev/null
+++ b/docbits/77_macrovba.md
@@ -0,0 +1,31 @@
+#### VBA and Macros
+
+VBA Macros are stored in a special data blob that is exposed in the `vbaraw`
+property of the workbook object when the `bookVBA` option is `true`. They are
+supported in `XLSM`, `XLSB`, and `BIFF8 XLS` formats. The `XLSM` and `XLSB`
+writers automatically insert the data blobs if it is present in the workbook.
+
+
+ Macrosheets (click to show)
+
+Older versions of Excel also supported a non-VBA "macrosheet" sheet type that
+stored automation commands. These are exposed in objects with the `!type`
+property set to `"macro"`.
+
+
+
+
+ Detecting macros in workbooks (click to show)
+
+The `vbaraw` field will only be set if macros are present, so testing is simple:
+
+```js
+function wb_has_macro(wb/*:workbook*/)/*:boolean*/ {
+ if(!!wb.vbaraw) return true;
+ const sheets = wb.SheetNames.map((n) => wb.Sheets[n]);
+ return sheets.some((ws) => !!ws && ws['!type']=='macro');
+}
+```
+
+
+
diff --git a/docbits/80_parseopts.md b/docbits/80_parseopts.md
index 00269d0..a0790ae 100644
--- a/docbits/80_parseopts.md
+++ b/docbits/80_parseopts.md
@@ -36,7 +36,9 @@ The exported `read` and `readFile` functions accept an options argument:
- `sheetRows-1` rows will be generated when looking at the JSON object output
(since the header row is counted as a row when parsing the data)
- `bookVBA` merely exposes the raw VBA CFB object. It does not parse the data.
- XLSM and XLSB store the VBA CFB object in `xl/vbaProject.bin`.
+ XLSM and XLSB store the VBA CFB object in `xl/vbaProject.bin`. BIFF8 XLS mixes
+ the VBA entries alongside the core Workbook entry, so the library generates a
+ new XLSB-compatible blob from the XLS CFB container.
- Currently only XOR encryption is supported. Unsupported error will be thrown
for files employing other encryption methods.
- WTF is mainly for development. By default, the parser will suppress read
diff --git a/docbits/81_writeopts.md b/docbits/81_writeopts.md
index 2454bbc..186f665 100644
--- a/docbits/81_writeopts.md
+++ b/docbits/81_writeopts.md
@@ -45,6 +45,7 @@ output formats. The specific file type is controlled with `bookType` option:
| `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) |
| `html` | `.html` | none | single | HTML Document |
| `dif` | `.dif` | none | single | Data Interchange Format (DIF) |
+| `rtf` | `.rtf` | none | single | Rich Text Format |
| `prn` | `.prn` | none | single | Lotus Formatted Text |
- `compression` only applies to formats with ZIP containers.
diff --git a/docbits/85_filetype.md b/docbits/85_filetype.md
index f7a660b..714af6d 100644
--- a/docbits/85_filetype.md
+++ b/docbits/85_filetype.md
@@ -28,6 +28,7 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | :o: | |
| **Other Common Spreadsheet Output Formats** |:-----:|:-----:|
| HTML Tables | :o: | :o: |
+| RTF Tables | | :o: |
### Excel 2007+ XML (XLSX/XLSM)
@@ -251,3 +252,14 @@ the metadata the output is valid HTML, although it does accept bare `&` symbols.
+#### Rich Text Format (RTF)
+
+
+ (click to show)
+
+Excel RTF worksheets are stored in clipboard when copying cells or ranges from a
+worksheet. The supported codes are a subset of the Word RTF support.
+
+
+
+
diff --git a/docbits/98_reference.md b/docbits/98_reference.md
index 1aaa356..c70b4e8 100644
--- a/docbits/98_reference.md
+++ b/docbits/98_reference.md
@@ -22,6 +22,7 @@
- `MS-XLSB`: Excel (.xlsb) Binary File Format
- `MS-XLSX`: Excel (.xlsx) Extensions to the Office Open XML SpreadsheetML File Format
- `XLS`: Microsoft Office Excel 97-2007 Binary File Format Specification
+ - `RTF`: Rich Text Format
diff --git a/formats.dot b/formats.dot
index e87866d..6d6ba2f 100644
--- a/formats.dot
+++ b/formats.dot
@@ -25,6 +25,7 @@ digraph G {
dif [label="DIF"];
slk [label="SYLK"];
prn [label="PRN"];
+ rtf [label="RTF"];
wk1 [label="WK1/2\n123"];
wk3 [label="WK3/4"];
wqb [label="WQ*\nWB*"];
@@ -62,6 +63,7 @@ digraph G {
wk1 -> csf
wqb -> csf
dif -> csf
+ csf -> rtf
prn -> csf
csf -> prn
csv -> csf
diff --git a/formats.png b/formats.png
index c6ff2d1..2517246 100644
Binary files a/formats.png and b/formats.png differ
diff --git a/misc/docs/README.md b/misc/docs/README.md
index ca4f616..25ea174 100644
--- a/misc/docs/README.md
+++ b/misc/docs/README.md
@@ -73,6 +73,8 @@ enhancements, additional features by request, and dedicated support.
* [Sheet Objects](#sheet-objects)
+ [Worksheet Object](#worksheet-object)
+ [Chartsheet Object](#chartsheet-object)
+ + [Macrosheet Object](#macrosheet-object)
+ + [Dialogsheet Object](#dialogsheet-object)
* [Workbook Object](#workbook-object)
+ [Workbook File Properties](#workbook-file-properties)
* [Workbook-Level Attributes](#workbook-level-attributes)
@@ -86,6 +88,7 @@ enhancements, additional features by request, and dedicated support.
+ [Hyperlinks](#hyperlinks)
+ [Cell Comments](#cell-comments)
+ [Sheet Visibility](#sheet-visibility)
+ + [VBA and Macros](#vba-and-macros)
- [Parsing Options](#parsing-options)
* [Input Type](#input-type)
* [Guessing File Type](#guessing-file-type)
@@ -119,6 +122,7 @@ enhancements, additional features by request, and dedicated support.
+ [Lotus Formatted Text (PRN)](#lotus-formatted-text-prn)
+ [Data Interchange Format (DIF)](#data-interchange-format-dif)
+ [HTML](#html)
+ + [Rich Text Format (RTF)](#rich-text-format-rtf)
- [Testing](#testing)
* [Node](#node)
* [Browser](#browser)
@@ -902,6 +906,16 @@ Chartsheets are represented as standard sheets. They are distinguished with the
The underlying data and `!ref` refer to the cached data in the chartsheet. The
first row of the chartsheet is the underlying header.
+#### Macrosheet Object
+
+Macrosheets are represented as standard sheets. They are distinguished with the
+`!type` property set to `"macro"`.
+
+#### Dialogsheet Object
+
+Dialogsheets are represented as standard sheets. They are distinguished with the
+`!type` property set to `"dialog"`.
+
### Workbook Object
`workbook.SheetNames` is an ordered list of the sheets in the workbook
@@ -1301,6 +1315,31 @@ if a sheet is visible is to check if the `Hidden` property is logical truth:
[ [ 'Visible', true ], [ 'Hidden', false ], [ 'VeryHidden', false ] ]
```
+#### VBA and Macros
+
+VBA Macros are stored in a special data blob that is exposed in the `vbaraw`
+property of the workbook object when the `bookVBA` option is `true`. They are
+supported in `XLSM`, `XLSB`, and `BIFF8 XLS` formats. The `XLSM` and `XLSB`
+writers automatically insert the data blobs if it is present in the workbook.
+
+
+Older versions of Excel also supported a non-VBA "macrosheet" sheet type that
+stored automation commands. These are exposed in objects with the `!type`
+property set to `"macro"`.
+
+
+
+The `vbaraw` field will only be set if macros are present, so testing is simple:
+
+```js
+function wb_has_macro(wb/*:workbook*/)/*:boolean*/ {
+ if(!!wb.vbaraw) return true;
+ const sheets = wb.SheetNames.map((n) => wb.Sheets[n]);
+ return sheets.some((ws) => !!ws && ws['!type']=='macro');
+}
+```
+
+
## Parsing Options
The exported `read` and `readFile` functions accept an options argument:
@@ -1339,7 +1378,9 @@ The exported `read` and `readFile` functions accept an options argument:
- `sheetRows-1` rows will be generated when looking at the JSON object output
(since the header row is counted as a row when parsing the data)
- `bookVBA` merely exposes the raw VBA CFB object. It does not parse the data.
- XLSM and XLSB store the VBA CFB object in `xl/vbaProject.bin`.
+ XLSM and XLSB store the VBA CFB object in `xl/vbaProject.bin`. BIFF8 XLS mixes
+ the VBA entries alongside the core Workbook entry, so the library generates a
+ new XLSB-compatible blob from the XLS CFB container.
- Currently only XOR encryption is supported. Unsupported error will be thrown
for files employing other encryption methods.
- WTF is mainly for development. By default, the parser will suppress read
@@ -1465,6 +1506,7 @@ output formats. The specific file type is controlled with `bookType` option:
| `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) |
| `html` | `.html` | none | single | HTML Document |
| `dif` | `.dif` | none | single | Data Interchange Format (DIF) |
+| `rtf` | `.rtf` | none | single | Rich Text Format |
| `prn` | `.prn` | none | single | Lotus Formatted Text |
- `compression` only applies to formats with ZIP containers.
@@ -1781,6 +1823,7 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | :o: | |
| **Other Common Spreadsheet Output Formats** |:-----:|:-----:|
| HTML Tables | :o: | :o: |
+| RTF Tables | | :o: |
### Excel 2007+ XML (XLSX/XLSM)
@@ -1956,6 +1999,14 @@ Excel HTML worksheets include special metadata encoded in styles. For example,
the metadata the output is valid HTML, although it does accept bare `&` symbols.
+#### Rich Text Format (RTF)
+
+
+Excel RTF worksheets are stored in clipboard when copying cells or ranges from a
+worksheet. The supported codes are a subset of the Word RTF support.
+
+
+
## Testing
### Node
@@ -2173,6 +2224,7 @@ granted by the Apache 2.0 License are reserved by the Original Author.
- `MS-XLSB`: Excel (.xlsb) Binary File Format
- `MS-XLSX`: Excel (.xlsx) Extensions to the Office Open XML SpreadsheetML File Format
- `XLS`: Microsoft Office Excel 97-2007 Binary File Format Specification
+ - `RTF`: Rich Text Format
- ISO/IEC 29500:2012(E) "Information technology — Document description and processing languages — Office Open XML File Formats"
diff --git a/misc/docs/SUMMARY.md b/misc/docs/SUMMARY.md
index 0df30e1..c43105e 100644
--- a/misc/docs/SUMMARY.md
+++ b/misc/docs/SUMMARY.md
@@ -26,6 +26,8 @@
* [Sheet Objects](README.md#sheet-objects)
+ [Worksheet Object](README.md#worksheet-object)
+ [Chartsheet Object](README.md#chartsheet-object)
+ + [Macrosheet Object](README.md#macrosheet-object)
+ + [Dialogsheet Object](README.md#dialogsheet-object)
* [Workbook Object](README.md#workbook-object)
+ [Workbook File Properties](README.md#workbook-file-properties)
* [Workbook-Level Attributes](README.md#workbook-level-attributes)
@@ -39,6 +41,7 @@
+ [Hyperlinks](README.md#hyperlinks)
+ [Cell Comments](README.md#cell-comments)
+ [Sheet Visibility](README.md#sheet-visibility)
+ + [VBA and Macros](README.md#vba-and-macros)
- [Parsing Options](README.md#parsing-options)
* [Input Type](README.md#input-type)
* [Guessing File Type](README.md#guessing-file-type)
@@ -72,6 +75,7 @@
+ [Lotus Formatted Text (PRN)](README.md#lotus-formatted-text-prn)
+ [Data Interchange Format (DIF)](README.md#data-interchange-format-dif)
+ [HTML](README.md#html)
+ + [Rich Text Format (RTF)](README.md#rich-text-format-rtf)
- [Testing](README.md#testing)
* [Node](README.md#node)
* [Browser](README.md#browser)
diff --git a/misc/flow.js b/misc/flow.js
index 1e1626c..b8d8568 100644
--- a/misc/flow.js
+++ b/misc/flow.js
@@ -21,6 +21,7 @@ type Workbook = {
SSF?: SSFTable;
cfb?: any;
+ vbaraw?: any;
};
type WBWBProps = {
diff --git a/test.js b/test.js
index 000647f..cf0f95d 100644
--- a/test.js
+++ b/test.js
@@ -576,12 +576,14 @@ describe('parse options', function() {
assert(typeof wb.vbaraw === 'undefined');
wb = X.read(fs.readFileSync(paths.nfxlsb), {type:TYPE});
assert(typeof wb.vbaraw === 'undefined');
+ wb = X.read(fs.readFileSync(paths.nfxls), {type:TYPE});
+ assert(typeof wb.vbaraw === 'undefined');
});
- it('bookVBA should generate vbaraw (XLSX/XLSB)', function() {
- var wb = X.read(fs.readFileSync(paths.nfxlsx),{type:TYPE, bookVBA:true});
- assert(wb.vbaraw);
- wb = X.read(fs.readFileSync(paths.nfxlsb),{type:TYPE, bookVBA:true});
- assert(wb.vbaraw);
+ it('bookVBA should generate vbaraw', function() {
+ var wb;
+ wb = X.read(fs.readFileSync(paths.nfxlsx),{type:TYPE, bookVBA:true}); assert(wb.vbaraw);
+ wb = X.read(fs.readFileSync(paths.nfxlsb),{type:TYPE, bookVBA:true}); assert(wb.vbaraw);
+ wb = X.read(fs.readFileSync(paths.nfxls),{type:TYPE, bookVBA:true}); assert(wb.vbaraw);
});
});
});
@@ -1068,12 +1070,9 @@ describe('parse features', function() {
assert.equal(sheet[3]['てすと'], '2/14/14');
});
it('cellDates should not affect formatted text', function() {
- var wb1, ws1, wb2, ws2;
var sheetName = 'Sheet1';
- wb1 = X.read(fs.readFileSync(paths.dtxlsx), {type:TYPE});
- ws1 = wb1.Sheets[sheetName];
- wb2 = X.read(fs.readFileSync(paths.dtxlsb), {type:TYPE});
- ws2 = wb2.Sheets[sheetName];
+ var ws1 = X.read(fs.readFileSync(paths.dtxlsx), {type:TYPE}).Sheets[sheetName];
+ var ws2 = X.read(fs.readFileSync(paths.dtxlsb), {type:TYPE}).Sheets[sheetName];
assert.equal(X.utils.sheet_to_csv(ws1),X.utils.sheet_to_csv(ws2));
});
});
diff --git a/tests.lst b/tests.lst
index 5274683..5ed63cd 100644
--- a/tests.lst
+++ b/tests.lst
@@ -12,6 +12,7 @@ apachepoi_hyperlink.xlsb
apachepoi_protected_passtika.xlsb.pending
apachepoi_sample.xlsb
apachepoi_testVarious.xlsb
+author_snowman.xlsb
calendar_stress_test.xlsb.pending
cell_style_simple.xlsb
column_width.xlsb
@@ -30,6 +31,7 @@ number_format_entities.xlsb
number_format_russian.xlsb
numfmt_1_russian.xlsb
outline.xlsb
+page_margins_2016.xlsb
phonetic_text.xlsb
pivot_table_named_range.xlsb
pivot_table_test.xlsb
@@ -142,6 +144,7 @@ apachepoi_56420.xlsx
apachepoi_56502.xlsx
apachepoi_56511.xlsx
apachepoi_56514.xlsx
+apachepoi_56557.xlsx
apachepoi_56574.xlsx
apachepoi_56644.xlsx
apachepoi_56688_1.xlsx
@@ -188,7 +191,13 @@ apachepoi_59746_NoRowNums.xlsx
apachepoi_59775.xlsx
apachepoi_60255_extra_drawingparts.xlsx
apachepoi_60289.xlsx
+#apachepoi_60384.xlsx
+apachepoi_60709.xlsx
#apachepoi_60825.xlsx # Missing worksheet xml file
+apachepoi_61034.xlsx
+apachepoi_61060-conditional-number-formatting.xlsx
+apachepoi_61063.xlsx
+apachepoi_61281.xlsx
apachepoi_AverageTaxRates.xlsx
apachepoi_Booleans.xlsx
apachepoi_BrNotClosed.xlsx
@@ -214,6 +223,7 @@ apachepoi_FormulaSheetRange.xlsx
apachepoi_GroupTest.xlsx
apachepoi_InlineStrings.xlsx
apachepoi_Intersection-52111-xssf.xlsx
+apachepoi_MatrixFormulaEvalTestData.xlsx
apachepoi_NewStyleConditionalFormattings.xlsx
apachepoi_NewlineInFormulas.xlsx
# apachepoi_NumberFormatApproxTests.xlsx # xlml
@@ -231,6 +241,7 @@ apachepoi_SimpleWithComments.xlsx
apachepoi_StructuredReferences.xlsx
apachepoi_StructuredRefs-lots-with-lookups.xlsx
# apachepoi_Tables.xlsx # xlml
+apachepoi_TablesWithDifferentHeaders.xlsx
apachepoi_TestShiftRowSharedFormula.xlsx
apachepoi_TextFormatTests.xlsx
apachepoi_Themes.xlsx
@@ -254,15 +265,20 @@ apachepoi_atp.xlsx
apachepoi_bug60858.xlsx
apachepoi_chartTitle_noTitle.xlsx
apachepoi_chartTitle_withTitle.xlsx
+apachepoi_chartTitle_withTitleFormula.xlsx
apachepoi_chart_sheet.xlsx
apachepoi_commentTest.xlsx
apachepoi_comments.xlsx
+apachepoi_conditional_formatting_with_formula_on_second_sheet.xlsx
apachepoi_craftonhills.edu_programreview_report.aspx_goalpriorityreport_0011d159-1eeb-4b63-8833-867b0926e5f3.xlsx
+apachepoi_customIndexedColors.xlsx
+apachepoi_dataValidationTableRange.xlsx
apachepoi_evaluate_formula_with_structured_table_references.xlsx
apachepoi_headerFooterTest.xlsx
apachepoi_noSharedStringTable.xlsx
apachepoi_picture.xlsx
apachepoi_poc-shared-strings.xlsx
+apachepoi_poc-xmlbomb-empty.xlsx
apachepoi_poc-xmlbomb.xlsx
# apachepoi_protected_passtika.xlsx # password
apachepoi_ref-56737.xlsx
@@ -275,7 +291,10 @@ apachepoi_sample.xlsx
apachepoi_shared_formulas.xlsx
apachepoi_sheetProtection_allLocked.xlsx
apachepoi_sheetProtection_not_protected.xlsx
+apachepoi_simple-monthly-budget.xlsx
+# apachepoi_style-alternate-content.xlsx # bad xml
apachepoi_styles.xlsx
+apachepoi_tableStyle.xlsx
apachepoi_template.xlsx
apachepoi_unicodeSheetName.xlsx
apachepoi_workbookProtection-sheet_password-2013.xlsx
@@ -287,6 +306,7 @@ apachepoi_workbookProtection_workbook_structure_protected.xlsx
apachepoi_workbookProtection_workbook_windows_protected.xlsx
apachepoi_workbookProtection_worksheet_protected.xlsx
apachepoi_xlsx-jdbc.xlsx
+author_snowman.xlsx
calendar_stress_test.xlsx.pending
cell_style_simple.xlsx
column_width.xlsx
@@ -364,6 +384,7 @@ openpyxl_r_nonstandard_workbook_name.xlsx
openpyxl_r_null_archive.xlsx.pending
openpyxl_r_null_file.xlsx.pending
outline.xlsx
+page_margins_2016.xlsx
phonetic_text.xlsx
pivot_table_named_range.xlsx
rich_text_stress.xlsx
@@ -422,6 +443,7 @@ spout-xlsx_file_with_no_sheets_in_workbook_xml.xlsx
# spout-xlsx_file_with_sheet_xml_not_matching_content_types.xlsx
spout-xlsx_one_sheet_with_inline_strings.xlsx
spout-xlsx_one_sheet_with_invalid_xml_characters.xlsx
+spout-xlsx_one_sheet_with_pre_encoded_html_entities.xlsx
spout-xlsx_one_sheet_with_shared_multiline_strings.xlsx
spout-xlsx_one_sheet_with_shared_strings.xlsx
spout-xlsx_one_sheet_with_shared_strings_containing_text_and_hyperlink_in_same_cell.xlsx
@@ -441,15 +463,18 @@ spout-xlsx_sheet_with_lots_of_shared_strings.xlsx
spout-xlsx_sheet_with_missing_cell_reference.xlsx
spout-xlsx_sheet_with_no_cells.xlsx
spout-xlsx_sheet_with_no_shared_strings_file.xlsx
+spout-xlsx_sheet_with_prefixed_shared_strings_xml.xlsx
spout-xlsx_sheet_with_prefixed_xml_files.xlsx
spout-xlsx_sheet_with_preserve_space_shared_strings.xlsx
spout-xlsx_sheet_with_pronunciation.xlsx
+spout-xlsx_sheet_with_row_not_starting_at_column_a.xlsx
spout-xlsx_sheet_with_same_numeric_value_date_formatted_differently.xlsx
spout-xlsx_sheet_with_untrimmed_inline_strings.xlsx
spout-xlsx_sheet_with_zeros_in_row.xlsx
spout-xlsx_sheet_without_dimensions_and_empty_cells.xlsx
spout-xlsx_sheet_without_dimensions_but_spans_and_empty_cells.xlsx
spout-xlsx_two_sheets_with_custom_names.xlsx
+spout-xlsx_two_sheets_with_custom_names_and_custom_active_tab.xlsx
spout-xlsx_two_sheets_with_inline_strings.xlsx
spout-xlsx_two_sheets_with_shared_strings.xlsx
spout-xlsx_two_sheets_with_sheets_definition_in_reverse_order.xlsx
@@ -551,7 +576,9 @@ roo_whitespace.xlsm
AutoFilter.ods
BlankSheetTypes.ods
apachepoi_SampleSS.ods
+author_snowman.ods
cell_style_simple.ods
+comments_stress_test.ods
formula_stress_test.ods
merge_cells.ods
number_format.ods
@@ -612,6 +639,7 @@ spout-ods_sheet_with_untrimmed_strings.ods
spout-ods_sheet_with_various_spaces.ods
spout-ods_sheet_with_zeros_in_row.ods
spout-ods_two_sheets_with_custom_names.ods
+spout-ods_two_sheets_with_no_settings_xml_file.ods
spout-ods_two_sheets_with_strings.ods
sushi.ods
biff5/NumberFormatCondition.xls
@@ -839,6 +867,9 @@ apachepoi_59830.xls
apachepoi_59858.xls
apachepoi_60273.xls
# apachepoi_60284.xls
+apachepoi_61045_govdocs1_626534.xls
+apachepoi_61287.xls
+# apachepoi_61300.xls # node 0.8 oob
apachepoi_AbnormalSharedFormulaFlag.xls
apachepoi_AreaErrPtg.xls
# apachepoi_BOOK_in_capitals.xls # note: worksheet length exceeds 31 chars
@@ -881,6 +912,7 @@ apachepoi_IrrNpvTestCaseData.xls
# apachepoi_LookupFunctionsTestCaseData.xls # xlml
apachepoi_MRExtraLines.xls
apachepoi_MatchFunctionTestCaseData.xls
+apachepoi_MatrixFormulaEvalTestData.xls
apachepoi_MissingBits.xls
apachepoi_NewStyleConditionalFormattings.xls
apachepoi_NoGutsRecords.xls
@@ -942,6 +974,7 @@ apachepoi_WithTwoHyperLinks.xls
apachepoi_WrongFormulaRecordType.xls
apachepoi_XRefCalc.xls
apachepoi_XRefCalcData.xls
+apachepoi_angelo.edu_content_files_19555-nsse-2011-multiyear-benchmark.xls
apachepoi_ar.org.apsme.www_Form%20Inscripcion%20Curso%20NO%20Socios.xls
apachepoi_at.gv.land-oberoesterreich.www_cps_rde_xbcr_SID-4A1B954F-5C07F98E_ooe_stat_download_bp10.xls
apachepoi_atp.xls
@@ -974,6 +1007,7 @@ apachepoi_ex47747-sharedFormula.xls
apachepoi_excel_with_embeded.xls
apachepoi_excelant.xls.pending
apachepoi_externalFunctionExample.xls
+apachepoi_external_image.xls
# apachepoi_finance.xls # xlml
apachepoi_florida_data.ashx.xls
apachepoi_intercept.xls
@@ -1008,6 +1042,7 @@ apachepoi_text.xls
apachepoi_unicodeNameRecord.xls
apachepoi_xor-encryption-abc.xls.pending
# apachepoi_yearfracExamples.xls # xlml
+author_snowman.xls
calendar_stress_test.xls.pending
cell_style_simple.xls
column_width.xls
@@ -1079,6 +1114,7 @@ jxls-examples_chart.xls
jxls-examples_colouring.xls
jxls-examples_department.xls
jxls-examples_dynamiccolumns.xls
+jxls-examples_dynamicolumns.xls
jxls-examples_employees.xls
jxls-examples_ex_temp.xls
jxls-examples_grouping.xls
@@ -1202,6 +1238,8 @@ number_format_entities.xls
number_format_russian.xls
numfmt_1_russian.xls
outline.xls
+page_margins_2016.xls
+page_margins_2016_5.xls
phonetic_text.xls
phpexcel_bad_cfb_dir.xls
pivot_table_named_range.xls
diff --git a/tests/core.js b/tests/core.js
index 000647f..511f860 100644
--- a/tests/core.js
+++ b/tests/core.js
@@ -576,12 +576,14 @@ describe('parse options', function() {
assert(typeof wb.vbaraw === 'undefined');
wb = X.read(fs.readFileSync(paths.nfxlsb), {type:TYPE});
assert(typeof wb.vbaraw === 'undefined');
+ wb = X.read(fs.readFileSync(paths.nfxls), {type:TYPE});
+ assert(typeof wb.vbaraw === 'undefined');
});
it('bookVBA should generate vbaraw (XLSX/XLSB)', function() {
- var wb = X.read(fs.readFileSync(paths.nfxlsx),{type:TYPE, bookVBA:true});
- assert(wb.vbaraw);
- wb = X.read(fs.readFileSync(paths.nfxlsb),{type:TYPE, bookVBA:true});
- assert(wb.vbaraw);
+ var wb;
+ wb = X.read(fs.readFileSync(paths.nfxlsx),{type:TYPE, bookVBA:true}); assert(wb.vbaraw);
+ wb = X.read(fs.readFileSync(paths.nfxlsb),{type:TYPE, bookVBA:true}); assert(wb.vbaraw);
+ wb = X.read(fs.readFileSync(paths.nfxls),{type:TYPE, bookVBA:true}); assert(wb.vbaraw);
});
});
});
@@ -1068,12 +1070,9 @@ describe('parse features', function() {
assert.equal(sheet[3]['てすと'], '2/14/14');
});
it('cellDates should not affect formatted text', function() {
- var wb1, ws1, wb2, ws2;
var sheetName = 'Sheet1';
- wb1 = X.read(fs.readFileSync(paths.dtxlsx), {type:TYPE});
- ws1 = wb1.Sheets[sheetName];
- wb2 = X.read(fs.readFileSync(paths.dtxlsb), {type:TYPE});
- ws2 = wb2.Sheets[sheetName];
+ var ws1 = X.read(fs.readFileSync(paths.dtxlsx), {type:TYPE}).Sheets[sheetName];
+ var ws2 = X.read(fs.readFileSync(paths.dtxlsb), {type:TYPE}).Sheets[sheetName];
assert.equal(X.utils.sheet_to_csv(ws1),X.utils.sheet_to_csv(ws2));
});
});
diff --git a/tests/write.js b/tests/write.js
index 9d45ad5..2f1a6ed 100644
--- a/tests/write.js
+++ b/tests/write.js
@@ -172,6 +172,7 @@ var filenames = [
['sheetjs.slk'],
['sheetjs.htm'],
['sheetjs.dif'],
+ ['sheetjs.rtf'],
['sheetjs.prn']
];
diff --git a/types/bin_xlsx.ts b/types/bin_xlsx.ts
index 8617919..4fab33f 100755
--- a/types/bin_xlsx.ts
+++ b/types/bin_xlsx.ts
@@ -20,19 +20,21 @@ program
.option('-M, --xlsm', 'emit XLSM to or .xlsm')
.option('-X, --xlsx', 'emit XLSX to or .xlsx')
.option('-Y, --ods', 'emit ODS to or .ods')
+ .option('-8, --xls', 'emit XLS to or .xls (BIFF8)')
.option('-2, --biff2','emit XLS to or .xls (BIFF2)')
.option('-6, --xlml', 'emit SSML to or .xls (2003 XML)')
.option('-T, --fods', 'emit FODS to or .fods (Flat ODS)')
- .option('-S, --formulae', 'print formulae')
- .option('-j, --json', 'emit formatted JSON (all fields text)')
- .option('-J, --raw-js', 'emit raw JS object (raw numbers)')
- .option('-A, --arrays', 'emit rows as JS objects (raw numbers)')
- .option('-H, --html', 'emit HTML')
- .option('-D, --dif', 'emit data interchange format (dif)')
- .option('-K, --sylk', 'emit symbolic link (sylk)')
- .option('-P, --prn', 'emit formatted text (prn)')
- .option('-t, --txt', 'emit delimited text (txt)')
+ .option('-S, --formulae', 'emit list of values and formulae')
+ .option('-j, --json', 'emit formatted JSON (all fields text)')
+ .option('-J, --raw-js', 'emit raw JS object (raw numbers)')
+ .option('-A, --arrays', 'emit rows as JS objects (raw numbers)')
+ .option('-H, --html', 'emit HTML to or .html')
+ .option('-D, --dif', 'emit DIF to or .dif (Lotus DIF)')
+ .option('-K, --sylk', 'emit SYLK to or .slk (Excel SYLK)')
+ .option('-P, --prn', 'emit PRN to or .prn (Lotus PRN)')
+ .option('-t, --txt', 'emit TXT to or .txt (UTF-8 TSV)')
+ .option('-r, --rtf', 'emit RTF to or .txt (Table RTF)')
.option('-F, --field-sep ', 'CSV field separator', ",")
.option('-R, --row-sep ', 'CSV row separator', "\n")
@@ -52,11 +54,18 @@ program.on('--help', function() {
console.log(' Web Demo: http://oss.sheetjs.com/js-'+n+'/');
});
-/* output formats, update list with full option name */
-const workbook_formats = ['xlsx', 'xlsm', 'xlsb', 'ods', 'fods'];
/* flag, bookType, default ext */
+const workbook_formats = [
+ ['xlsx', 'xlsx', 'xlsx'],
+ ['xlsm', 'xlsm', 'xlsm'],
+ ['xlsb', 'xlsb', 'xlsb'],
+ ['xls', 'xls', 'xls'],
+ ['biff5', 'biff5', 'xls'],
+ ['ods', 'ods', 'ods'],
+ ['fods', 'fods', 'fods']
+];
const wb_formats_2 = [
- ['xlml', 'xlml', 'xls']
+ ['xlml', 'xlml', 'xls']
];
program.parse(process.argv);
@@ -94,7 +103,7 @@ function isfmt(m: string): boolean {
const t = m.charAt(0) === "." ? m : "." + m;
return program.output.slice(-t.length) === t;
}
-workbook_formats.forEach(function(m) { if(program[m] || isfmt(m)) { wb_fmt(); } });
+workbook_formats.forEach(function(m) { if(program[m[0]] || isfmt(m[0])) { wb_fmt(); } });
wb_formats_2.forEach(function(m) { if(program[m[0]] || isfmt(m[0])) { wb_fmt(); } });
if(seen) {
} else if(program.formulae) opts.cellFormula = true;
@@ -134,8 +143,9 @@ let wopts: X.WritingOptions = ({WTF:opts.WTF, bookSST:program.sst}/*:any*/);
if(program.compress) wopts.compression = true;
/* full workbook formats */
-workbook_formats.forEach(function(m) { if(program[m] || isfmt(m)) {
- X.writeFile(wb, program.output || sheetname || ((filename || "") + "." + m), wopts);
+workbook_formats.forEach(function(m) { if(program[m[0]] || isfmt(m[0])) {
+ wopts.bookType = (m[1]);
+ X.writeFile(wb, program.output || sheetname || ((filename || "") + "." + m[2]), wopts);
process.exit(0);
} });
@@ -168,9 +178,12 @@ if(program.readOnly) process.exit(0);
/* single worksheet formats */
[
['biff2', '.xls'],
+ ['biff3', '.xls'],
+ ['biff4', '.xls'],
['sylk', '.slk'],
['html', '.html'],
['prn', '.prn'],
+ ['rtf', '.rtf'],
['txt', '.txt'],
['dif', '.dif']
].forEach(function(m) { if(program[m[0]] || isfmt(m[1])) {
diff --git a/types/index.d.ts b/types/index.d.ts
index f8611fb..da691d1 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -477,7 +477,7 @@ export type ExcelDataType = 'b' | 'n' | 'e' | 's' | 'd' | 'z';
* Type of generated workbook
* @default 'xlsx'
*/
-export type BookType = 'xlsx' | 'xlsm' | 'xlsb' | 'xls' | 'biff8' | 'biff2' | 'xlml' | 'ods' | 'fods' | 'csv' | 'txt' | 'sylk' | 'html' | 'dif' | 'prn';
+export type BookType = 'xlsx' | 'xlsm' | 'xlsb' | 'xls' | 'biff8' | 'biff2' | 'xlml' | 'ods' | 'fods' | 'csv' | 'txt' | 'sylk' | 'html' | 'dif' | 'rtf' | 'prn';
/** Comment element */
export interface Comment {
diff --git a/xlsx.flow.js b/xlsx.flow.js
index a24ace9..541a943 100644
--- a/xlsx.flow.js
+++ b/xlsx.flow.js
@@ -2274,7 +2274,7 @@ function write_double_le(b/*:RawBytes|CFBlob*/, v/*:number*/, idx/*:number*/) {
var __toBuffer = function(bufs) { var x = []; for(var i = 0; i < bufs[0].length; ++i) { x.push.apply(x, bufs[0][i]); } return x; };
var ___toBuffer = __toBuffer;
-var __utf16le = function(b/*:RawBytes|CFBlob*/,s/*:number*/,e/*:number*/)/*:string*/ { var ss/*:Array*/=[]; for(var i=s; i*/=[]; for(var i=s; i*/=[]; for(var i=s; i 0 ? b.toString('utf8',i+4,i+4+len-1) : "";};
__lpwstr = function lpwstr_b(b/*:RawBytes|CFBlob*/, i/*:number*/) { if(!Buffer.isBuffer(b)/*:: || !(b instanceof Buffer)*/) return ___lpwstr(b, i); var len = 2*b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len-1);};
@@ -2308,7 +2308,7 @@ if(has_buf/*:: && typeof Buffer !== 'undefined'*/) {
/* from js-xls */
if(typeof cptable !== 'undefined') {
- __utf16le = function(b/*:RawBytes|CFBlob*/,s/*:number*/,e/*:number*/) { return cptable.utils.decode(1200, b.slice(s,e)); };
+ __utf16le = function(b/*:RawBytes|CFBlob*/,s/*:number*/,e/*:number*/) { return cptable.utils.decode(1200, b.slice(s,e)).replace(chr0, ''); };
__utf8 = function(b/*:RawBytes|CFBlob*/,s/*:number*/,e/*:number*/) { return cptable.utils.decode(65001, b.slice(s,e)); };
__lpstr = function(b/*:RawBytes|CFBlob*/,i/*:number*/) { var len = __readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(current_codepage, b.slice(i+4, i+4+len-1)) : "";};
__lpwstr = function(b/*:RawBytes|CFBlob*/,i/*:number*/) { var len = 2*__readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(1200, b.slice(i+4,i+4+len-1)) : "";};
@@ -7030,11 +7030,39 @@ var RTF = (function() {
}
function rtf_to_sheet_str(str/*:string*/, opts)/*:Worksheet*/ {
- throw new Error("Unsupported RTF");
+ var o = opts || {};
+ var ws/*:Worksheet*/ = ({}/*:any*/);
+ var range/*:Range*/ = ({s: {c:0, r:0}, e: {c:0, r:0}}/*:any*/);
+
+ // TODO: parse
+ if(!str.match(/\\trowd/)) throw new Error("RTF missing table");
+
+ ws['!ref'] = encode_range(range);
+ return ws;
}
function rtf_to_workbook(d/*:RawData*/, opts)/*:Workbook*/ { return sheet_to_workbook(rtf_to_sheet(d, opts), opts); }
- function sheet_to_rtf() { throw new Error("Unsupported"); }
+
+ /* TODO: this is a stub */
+ function sheet_to_rtf(ws/*:Worksheet*/, opts)/*:string*/ {
+ var o = ["{\\rtf1\\ansi"];
+ var r = safe_decode_range(ws['!ref']), cell/*:Cell*/;
+ var dense = Array.isArray(ws);
+ for(var R = r.s.r; R <= r.e.r; ++R) {
+ o.push("\\trowd\\trautofit1");
+ for(var C = r.s.c; C <= r.e.c; ++C) o.push("\\cellx" + (C+1));
+ o.push("\\pard\\intbl");
+ for(C = r.s.c; C <= r.e.c; ++C) {
+ var coord = encode_cell({r:R,c:C});
+ cell = dense ? (ws[R]||[])[C]: ws[coord];
+ if(!cell || cell.v == null && (!cell.f || cell.F)) continue;
+ o.push(" " + (cell.w || (format_cell(cell), cell.w)));
+ o.push("\\cell");
+ }
+ o.push("\\pard\\intbl\\row");
+ }
+ return o.join("") + "}";
+ }
return {
to_workbook: rtf_to_workbook,
@@ -8673,6 +8701,15 @@ function write_comments_bin(data, opts) {
write_record(ba, "BrtEndComments");
return ba.end();
}
+function make_vba_xls(cfb/*:CFBContainer*/) {
+ var newcfb = CFB.utils.cfb_new({root:"R"});
+ cfb.FullPaths.forEach(function(p, i) {
+ if(p.slice(-1) === "/" || !p.match(/_VBA_PROJECT_CUR/)) return;
+ var newpath = p.replace(/^[^/]*/,"R").replace(/\/_VBA_PROJECT_CUR\u0000*/, "");
+ CFB.utils.cfb_add(newcfb, newpath, cfb.FileIndex[i].content);
+ });
+ return CFB.write(newcfb);
+}
RELS.DS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet";
RELS.MS = "http://schemas.microsoft.com/office/2006/relationships/xlMacrosheet";
@@ -12881,7 +12918,8 @@ function parse_wb_xml(data, opts)/*:WorkbookFile*/ {
/* Others */
case '': pass=true; break;
case '': pass=false; break;
/* TODO */
@@ -15291,6 +15329,7 @@ else/*:: if(cfb instanceof CFBContainer) */ {
/* Quattro Pro 9 */
else if((_data=CFB.find(cfb, 'NativeContent_MAIN')) && _data.content) WorkbookP = WK_.to_workbook(_data.content, (options.type = T, options));
else throw new Error("Cannot find Workbook stream");
+ if(options.bookVBA && CFB.find(cfb, '/_VBA_PROJECT_CUR/VBA/dir')) WorkbookP.vbaraw = make_vba_xls(cfb);
}
var props = {};
diff --git a/xlsx.js b/xlsx.js
index 8db8ffc..813122e 100644
--- a/xlsx.js
+++ b/xlsx.js
@@ -2202,7 +2202,7 @@ function write_double_le(b, v, idx) {
var __toBuffer = function(bufs) { var x = []; for(var i = 0; i < bufs[0].length; ++i) { x.push.apply(x, bufs[0][i]); } return x; };
var ___toBuffer = __toBuffer;
-var __utf16le = function(b,s,e) { var ss=[]; for(var i=s; i 0 ? b.toString('utf8',i+4,i+4+len-1) : "";};
__lpwstr = function lpwstr_b(b, i) { if(!Buffer.isBuffer(b)) return ___lpwstr(b, i); var len = 2*b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len-1);};
@@ -2236,7 +2236,7 @@ if(has_buf) {
/* from js-xls */
if(typeof cptable !== 'undefined') {
- __utf16le = function(b,s,e) { return cptable.utils.decode(1200, b.slice(s,e)); };
+ __utf16le = function(b,s,e) { return cptable.utils.decode(1200, b.slice(s,e)).replace(chr0, ''); };
__utf8 = function(b,s,e) { return cptable.utils.decode(65001, b.slice(s,e)); };
__lpstr = function(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(current_codepage, b.slice(i+4, i+4+len-1)) : "";};
__lpwstr = function(b,i) { var len = 2*__readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(1200, b.slice(i+4,i+4+len-1)) : "";};
@@ -6945,11 +6945,39 @@ var RTF = (function() {
}
function rtf_to_sheet_str(str, opts) {
- throw new Error("Unsupported RTF");
+ var o = opts || {};
+ var ws = ({});
+ var range = ({s: {c:0, r:0}, e: {c:0, r:0}});
+
+ // TODO: parse
+ if(!str.match(/\\trowd/)) throw new Error("RTF missing table");
+
+ ws['!ref'] = encode_range(range);
+ return ws;
}
function rtf_to_workbook(d, opts) { return sheet_to_workbook(rtf_to_sheet(d, opts), opts); }
- function sheet_to_rtf() { throw new Error("Unsupported"); }
+
+ /* TODO: this is a stub */
+ function sheet_to_rtf(ws, opts) {
+ var o = ["{\\rtf1\\ansi"];
+ var r = safe_decode_range(ws['!ref']), cell;
+ var dense = Array.isArray(ws);
+ for(var R = r.s.r; R <= r.e.r; ++R) {
+ o.push("\\trowd\\trautofit1");
+ for(var C = r.s.c; C <= r.e.c; ++C) o.push("\\cellx" + (C+1));
+ o.push("\\pard\\intbl");
+ for(C = r.s.c; C <= r.e.c; ++C) {
+ var coord = encode_cell({r:R,c:C});
+ cell = dense ? (ws[R]||[])[C]: ws[coord];
+ if(!cell || cell.v == null && (!cell.f || cell.F)) continue;
+ o.push(" " + (cell.w || (format_cell(cell), cell.w)));
+ o.push("\\cell");
+ }
+ o.push("\\pard\\intbl\\row");
+ }
+ return o.join("") + "}";
+ }
return {
to_workbook: rtf_to_workbook,
@@ -8586,6 +8614,15 @@ function write_comments_bin(data, opts) {
write_record(ba, "BrtEndComments");
return ba.end();
}
+function make_vba_xls(cfb) {
+ var newcfb = CFB.utils.cfb_new({root:"R"});
+ cfb.FullPaths.forEach(function(p, i) {
+ if(p.slice(-1) === "/" || !p.match(/_VBA_PROJECT_CUR/)) return;
+ var newpath = p.replace(/^[^/]*/,"R").replace(/\/_VBA_PROJECT_CUR\u0000*/, "");
+ CFB.utils.cfb_add(newcfb, newpath, cfb.FileIndex[i].content);
+ });
+ return CFB.write(newcfb);
+}
RELS.DS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet";
RELS.MS = "http://schemas.microsoft.com/office/2006/relationships/xlMacrosheet";
@@ -12792,7 +12829,8 @@ function parse_wb_xml(data, opts) {
/* Others */
case '': pass=true; break;
case '': pass=false; break;
/* TODO */
@@ -15193,6 +15231,7 @@ else {
/* Quattro Pro 9 */
else if((_data=CFB.find(cfb, 'NativeContent_MAIN')) && _data.content) WorkbookP = WK_.to_workbook(_data.content, (options.type = T, options));
else throw new Error("Cannot find Workbook stream");
+ if(options.bookVBA && CFB.find(cfb, '/_VBA_PROJECT_CUR/VBA/dir')) WorkbookP.vbaraw = make_vba_xls(cfb);
}
var props = {};