diff --git a/Makefile b/Makefile index 40fac21b..89877b81 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ .PHONY: build build: + cp formats.png docz/docs/img/formats.png cd docz; npx -y pnpm build; cd .. rm -rf docs mv docz/build/ docs @@ -7,7 +8,7 @@ build: .PHONY: serve serve: - cd docs; python -mSimpleHTTPServer; cd - + cd docs; python -mSimpleHTTPServer || python3 -mhttp.server; cd - .PHONY: index index: readme ## Rebuild site diff --git a/docz/docs/04-getting-started/03-demos/06-clipboard.md b/docz/docs/04-getting-started/03-demos/06-clipboard.md index 647143a4..ef379ca7 100644 --- a/docz/docs/04-getting-started/03-demos/06-clipboard.md +++ b/docz/docs/04-getting-started/03-demos/06-clipboard.md @@ -14,7 +14,7 @@ XLS (both '97-2004 and '95), and SpreadsheetML 2003. Not all Clipboard APIs offer access to all clipboard types. -## Browser +## Browser Reading (paste) Clipboard data can be read from a `paste` event, accessible from the event `clipboardData` property: @@ -26,7 +26,7 @@ document.onpaste = function(e) { /* parse */ var wb = XLSX.read(str, {type: "string"}); /* DO SOMETHING WITH wb HERE */ -} +}; ``` `getData` accepts one argument: the desired MIME type. Chrome 103 supports: @@ -62,7 +62,7 @@ function Clipboard() { /* generate CSV for each "first worksheet" */ var result = ws_arr.map(ws => XLSX.utils.sheet_to_csv(ws)); setCSVs(result); - } + }; }, []); return ( @@ -72,6 +72,68 @@ function Clipboard() { {csvs[2] && (
Data from clipboard RTF  (text/rtf)
{csvs[2]}
)} {csvs.every(x => !x) && Copy data in Excel, click here, and paste (Control+V)} - ) + ); +} +``` + +## Browser Writing (copy) + +Clipboard data can be written from a `copy` event, accessible from the event +`clipboardData` property: + +```js +document.oncopy = function(e) { + /* get HTML of first worksheet in workbook */ + var str = XLSX.write(wb, {type: "string", bookType: "html"}); + /* set HTML clipboard data */ + e.clipboardData.setData('text/html', str); + + /* prevent the browser from copying the normal data */ + e.preventDefault(); +}; +``` + +`setData` accepts two arguments: MIME type and new data. Chrome 103 supports: + +| MIME type | Data format | +|:-------------|:---------------------------| +| `text/plain` | TSV (tab separated values) | +| `text/html` | HTML | + +Browsers do not currently support assigning to the `text/rtf` clipboard type. + +### Live Demo + +This demo creates a simple workbook from the following HTML table: + + + + + +
SheetJSClipboardDemo
bookTypeRTF
sourceHTML Table
+ +Create a new file in Excel then come back to this window. Select the text +below and copy (Control+C for Windows, Command+C for Mac). Go back to the +excel + +```jsx live +function Clipboard() { + /* Set up copy handler */ + React.useEffect(async() => { + document.oncopy = function(e) { + /* generate workbook from table */ + var wb = XLSX.utils.table_to_book(document.getElementById("srcdata")); + /* get HTML of first worksheet in workbook */ + var str = XLSX.write(wb, {type: "string", bookType: "html"}); + /* set HTML clipboard data */ + e.clipboardData.setData('text/html', str); + /* prevent the browser from copying the normal data */ + e.preventDefault(); + }; + }, []); + + return ( + Select this text, copy (Control+C), and paste in Excel + ); } ``` diff --git a/docz/docs/04-getting-started/03-demos/07-headless.md b/docz/docs/04-getting-started/03-demos/07-headless.md new file mode 100644 index 00000000..23a04f32 --- /dev/null +++ b/docz/docs/04-getting-started/03-demos/07-headless.md @@ -0,0 +1,215 @@ +--- +sidebar_position: 7 +--- + +# Headless Automation + +Headless automation involves controlling "headless browsers" to access websites +and submit or download data. It is also possible to automate browsers using +custom browser extensions. + +The [SheetJS standalone script](../../installation/standalone) can be added to +any website by inserting a `SCRIPT` tag. Headless browsers usually provide +utility functions for running custom snippets in the browser and passing data +back to the automation script. + +## Use Case + +This demo focuses on exporting table data to a workbook. Headless browsers do +not generally support passing objects between the browser context and the +automation script, so the file data must be generated in the browser context +and sent back to the automation script for saving in the filesystem. Steps: + +1) Launch the headless browser and load the target webpage. + +2) Add the standalone SheetJS build to the page in a `SCRIPT` tag. + +3) Add a script to the page (in the browser context) that will: + +- Make a workbook object from the first table using `XLSX.utils.table_to_book` +- Generate the bytes for an XLSB file using `XLSX.write` +- Send the bytes back to the automation script + +4) When the automation context receives data, save to a file + +This demo exports data from . + +:::note + +It is also possible to parse files from the browser context, but parsing from +the automation context is more performant and strongly recommended. + +::: + +## Puppeteer + +Puppeteer enables headless Chromium automation for NodeJS. Releases ship with +an installer script. Installation is straightforward: + +```bash +npm i https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz puppeteer +``` + +Binary strings are the favored data type. They can be safely passed from the +browser context to the automation script. NodeJS provides an API to write +binary strings to file (`fs.writeFileSync` using encoding `binary`). + +To run the example, after installing the packages, save the following script to +`SheetJSPuppeteer.js` and run `node SheetJSPuppeteer.js`. Steps are commented: + +```js title="SheetJSPuppeteer.js" +const fs = require("fs"); +const puppeteer = require('puppeteer'); +(async () => { + /* (1) Load the target page */ + const browser = await puppeteer.launch(); + const page = await browser.newPage(); + page.on("console", msg => console.log("PAGE LOG:", msg.text())); + await page.setViewport({width: 1920, height: 1080}); + await page.goto('https://sheetjs.com/demos/table'); + + /* (2) Load the standalone SheetJS build from the CDN */ + await page.addScriptTag({ url: 'https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js' }); + + /* (3) Run the snippet in browser and return data */ + const bin = await page.evaluate(() => { + /* NOTE: this function will be evaluated in the browser context. + `page`, `fs` and `puppeteer` are not available. + `XLSX` will be available thanks to step 2 */ + + /* find first table */ + var table = document.body.getElementsByTagName('table')[0]; + + /* call table_to_book on first table */ + var wb = XLSX.utils.table_to_book(table); + + /* generate XLSB and return binary string */ + return XLSX.write(wb, {type: "binary", bookType: "xlsb"}); + }); + + /* (4) write data to file */ + fs.writeFileSync("SheetJSPuppeteer.xlsb", bin, { encoding: "binary" }); + + await browser.close(); +})(); +``` + +## Playwright + +Playwright presents a unified scripting framework for Chromium, WebKit, and +other browsers. It draws inspiration from Puppeteer. In fact, the example +code is almost identical! + +```bash +npm i https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz playwright +``` + +To run the example, after installing the packages, save the following script to +`SheetJSPlaywright.js` and run `node SheetJSPlaywright.js`. Import divergences +from the Puppeteer example are highlighted below: + +```js title="SheetJSPlaywright.js" +const fs = require("fs"); +// highlight-next-line +const { webkit } = require('playwright'); // import desired browser +(async () => { + /* (1) Load the target page */ + // highlight-next-line + const browser = await webkit.launch(); // launch desired browser + const page = await browser.newPage(); + page.on("console", msg => console.log("PAGE LOG:", msg.text())); + // highlight-next-line + await page.setViewportSize({width: 1920, height: 1080}); // different name :( + await page.goto('https://sheetjs.com/demos/table'); + + /* (2) Load the standalone SheetJS build from the CDN */ + await page.addScriptTag({ url: 'https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js' }); + + /* (3) Run the snippet in browser and return data */ + const bin = await page.evaluate(() => { + /* NOTE: this function will be evaluated in the browser context. + `page`, `fs` and the browser engine are not available. + `XLSX` will be available thanks to step 2 */ + + /* find first table */ + var table = document.body.getElementsByTagName('table')[0]; + + /* call table_to_book on first table */ + var wb = XLSX.utils.table_to_book(table); + + /* generate XLSB and return binary string */ + return XLSX.write(wb, {type: "binary", bookType: "xlsb"}); + }); + + /* (4) write data to file */ + fs.writeFileSync("SheetJSPlaywright.xlsb", bin, { encoding: "binary" }); + + await browser.close(); +})(); +``` + + +## PhantomJS + +PhantomJS is a headless web browser powered by WebKit. Standalone binaries are +available at + +:::warning + +This information is provided for legacy deployments. PhantomJS development has +been suspended and there are known vulnerabilities, so new projects should use +alternatives. For WebKit automation, new projects should use Playwright. + +::: + +Binary strings are the favored data type. They can be safely passed from the +browser context to the automation script. PhantomJS provides an API to write +binary strings to file (`fs.write` using mode `wb`). + +To run the example, save the following script to `SheetJSPhantom.js` in the same +folder as `phantomjs.exe` or `phantomjs` and run + +``` +./phantomjs SheetJSPhantom.js ## macOS / Linux +.\phantomjs.exe SheetJSPhantom.js ## windows +``` + +The steps are marked in the comments: + +```js title="SheetJSPhantom.js" +var page = require('webpage').create(); +page.onConsoleMessage = function(msg) { console.log(msg); }; + +/* (1) Load the target page */ +page.open('https://sheetjs.com/demos/table', function() { + + /* (2) Load the standalone SheetJS build from the CDN */ + page.includeJs("https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js", function() { + + /* (3) Run the snippet in browser and return data */ + var bin = page.evaluateJavaScript([ "function(){", + + /* find first table */ + "var table = document.body.getElementsByTagName('table')[0];", + + /* call table_to_book on first table */ + "var wb = XLSX.utils.table_to_book(table);", + + /* generate XLSB file and return binary string */ + "return XLSX.write(wb, {type: 'binary', bookType: 'xlsb'});", + "}" ].join("")); + + /* (4) write data to file */ + require("fs").write("SheetJSPhantomJS.xlsb", bin, "wb"); + + phantom.exit(); + }); +}); +``` + +:::caution + +PhantomJS is very finicky and will hang if there are script errors. It is +strongly recommended to add verbose logging and to lint scripts before use. + +::: diff --git a/docz/docs/04-getting-started/03-demos/index.md b/docz/docs/04-getting-started/03-demos/index.md index 8e9d930a..a142e573 100644 --- a/docz/docs/04-getting-started/03-demos/index.md +++ b/docz/docs/04-getting-started/03-demos/index.md @@ -41,7 +41,7 @@ The demo projects include small runnable examples and short explainers. - [`NetSuite SuiteScript`](./netsuite) - [`SalesForce Lightning Web Components`](./salesforce) - [`Excel JavaScript API`](./excel) -- [`Headless Browsers`](https://github.com/SheetJS/SheetJS/tree/master/demos/headless/) +- [`Headless Automation`](./headless) - [`Other JavaScript Engines`](https://github.com/SheetJS/SheetJS/tree/master/demos/altjs/) - [`"serverless" functions`](https://github.com/SheetJS/SheetJS/tree/master/demos/function/) - [`Databases and Key/Value Stores`](https://github.com/SheetJS/SheetJS/tree/master/demos/database/) diff --git a/docz/docs/06-solutions/01-input.md b/docz/docs/06-solutions/01-input.md index 9f397079..459f6cce 100644 --- a/docz/docs/06-solutions/01-input.md +++ b/docz/docs/06-solutions/01-input.md @@ -818,13 +818,17 @@ is missing or no options are specified, the default name `Sheet1` is used. #### Examples +The [Headless Demo](../getting-started/demos/headless) includes examples of +server-side spreadsheet generation from HTML TABLE elements using headless +Chromium ("Puppeteer") and other browsers ("Playwright") + Here are a few common scenarios (click on each subtitle to see the code):
HTML TABLE element in a webpage (click to show) ```html - + @@ -897,95 +901,6 @@ chrome.runtime.onMessage.addListener(function(msg, sender, cb) {
-
- Server-Side HTML Tables with Headless Chrome (click to show) - -The [`headless` demo](https://github.com/SheetJS/SheetJS/tree/master/demos/headless/) includes a complete demo to convert HTML -files to XLSB workbooks. The core idea is to add the script to the page, parse -the table in the page context, generate a `base64` workbook and send it back -for further processing: - -```js -const XLSX = require("xlsx"); -const { readFileSync } = require("fs"), puppeteer = require("puppeteer"); - -const url = `https://sheetjs.com/demos/table`; - -/* get the standalone build source (node_modules/xlsx/dist/xlsx.full.min.js) */ -const lib = readFileSync(require.resolve("xlsx/dist/xlsx.full.min.js"), "utf8"); - -(async() => { - /* start browser and go to web page */ - const browser = await puppeteer.launch(); - const page = await browser.newPage(); - await page.goto(url, {waitUntil: "networkidle2"}); - - /* inject library */ - await page.addScriptTag({content: lib}); - - /* this function `s5s` will be called by the script below, receiving the Base64-encoded file */ - await page.exposeFunction("s5s", async(b64) => { - const workbook = XLSX.read(b64, {type: "base64" }); - - /* DO SOMETHING WITH workbook HERE */ - }); - - /* generate XLSB file in webpage context and send back result */ - await page.addScriptTag({content: ` - /* call table_to_book on first table */ - var workbook = XLSX.utils.table_to_book(document.querySelector("TABLE")); - - /* generate XLSX file */ - var b64 = XLSX.write(workbook, {type: "base64", bookType: "xlsb"}); - - /* call "s5s" hook exposed from the node process */ - window.s5s(b64); - `}); - - /* cleanup */ - await browser.close(); -})(); -``` - -
- -
- Server-Side HTML Tables with Headless WebKit (click to show) - -The [`headless` demo](https://github.com/SheetJS/SheetJS/tree/master/demos/headless/) includes a complete demo to convert HTML -files to XLSB workbooks using [PhantomJS](https://phantomjs.org/). The core idea -is to add the script to the page, parse the table in the page context, generate -a `binary` workbook and send it back for further processing: - -```js -var XLSX = require('xlsx'); -var page = require('webpage').create(); - -/* this code will be run in the page */ -var code = [ "function(){", - /* call table_to_book on first table */ - "var wb = XLSX.utils.table_to_book(document.body.getElementsByTagName('table')[0]);", - - /* generate XLSB file and return binary string */ - "return XLSX.write(wb, {type: 'binary', bookType: 'xlsb'});", -"}" ].join(""); - -page.open('https://sheetjs.com/demos/table', function() { - /* Load the browser script from the UNPKG CDN */ - page.includeJs("https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js", function() { - /* The code will return an XLSB file encoded as binary string */ - var bin = page.evaluateJavaScript(code); - - var workbook = XLSX.read(bin, {type: "binary"}); - /* DO SOMETHING WITH workbook HERE */ - - phantom.exit(); - }); -}); -``` - -
-
NodeJS HTML Tables without a browser (click to show) diff --git a/docz/docs/06-solutions/05-output.md b/docz/docs/06-solutions/05-output.md index 80f9d1f4..523f2da9 100644 --- a/docz/docs/06-solutions/05-output.md +++ b/docz/docs/06-solutions/05-output.md @@ -245,28 +245,34 @@ The [`extendscript` demo](../getting-started/demos/extendscript) includes a more -
- PhantomJS (Headless Webkit) File Generation (click to show) +The [`headless` demo](../getting-started/demos/headless) includes complete +examples of converting HTML TABLE elements to XLSB workbooks using Puppeteer +and other headless automation tools. -The [`headless` demo](https://github.com/SheetJS/SheetJS/tree/master/demos/headless/) includes a complete demo to convert HTML -files to XLSB workbooks using [PhantomJS](https://phantomjs.org/). PhantomJS -`fs.write` supports writing files from the main process but has a different -interface from the NodeJS `fs` module: +Headless browsers may not have access to the filesystem, so `XLSX.writeFile` +may fail. It is strongly recommended to generate the file bytes in the browser +context, send the bytes to the automation context, and write from automation. + +Puppeteer and Playwright are NodeJS modules that support binary strings: ```js -var XLSX = require('xlsx'); -var fs = require('fs'); +/* from the browser context */ +var bin = XLSX.write(workbook, { type:"binary", bookType: "xlsb" }); -/* generate a binary string */ -var bin = XLSX.write(workbook, { type:"binary", bookType: "xlsx" }); -/* write to file */ -fs.write("test.xlsx", bin, "wb"); +/* from the automation context */ +fs.writeFileSync("SheetJSansHead.xlsb", bin, { encoding: "binary" }); ``` -Note: The section ["Processing HTML Tables"](./input#processing-html-tables) shows how -to generate a workbook from HTML tables in a page in "Headless WebKit". +PhantomJS `fs.write` supports writing files from the main process. The mode +`wb` supports binary strings: -
+```js +/* from the browser context */ +var bin = XLSX.write(workbook, { type:"binary", bookType: "xlsb" }); + +/* from the automation context */ +fs.write("SheetJSansHead.xlsb", bin, "wb"); +```
diff --git a/docz/docs/07-csf/07-features/index.md b/docz/docs/07-csf/07-features/index.md index a4eee48b..51c9d1e5 100644 --- a/docz/docs/07-csf/07-features/index.md +++ b/docz/docs/07-csf/07-features/index.md @@ -426,9 +426,9 @@ explicitly flagged. Combining the two checks yields a simple function: ```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'); + 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/docz/docs/08-api/09-utilities.md b/docz/docs/08-api/09-utilities.md index 5e6ada9f..0740ba32 100644 --- a/docz/docs/08-api/09-utilities.md +++ b/docz/docs/08-api/09-utilities.md @@ -9,18 +9,27 @@ The `sheet_to_*` functions accept a worksheet and an optional options object. The `*_to_sheet` functions accept a data object and an optional options object. +The `sheet_add_*` functions accept worksheet, data, and optional options. + The examples are based on the following worksheet: -``` -XXX| A | B | C | D | E | F | G | ----+---+---+---+---+---+---+---+ - 1 | S | h | e | e | t | J | S | - 2 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | - 3 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -``` + + + + + + +
SheetJS
12 567
23 678
34 789
4567890
+ ### Array of Arrays Input +**Create a worksheet from an array of arrays** + +```js +var ws = XLSX.utils.aoa_to_sheet(aoa, opts); +``` + `XLSX.utils.aoa_to_sheet` takes an array of arrays of JS values and returns a worksheet resembling the input data. Numbers, Booleans and Strings are stored as the corresponding styles. Dates are stored as date or numbers. Array holes @@ -34,19 +43,23 @@ other values are stored as strings. The function takes an options argument: |`sheetStubs` | false | Create cell objects of type `z` for `null` values | |`nullError` | false | If true, emit `#NULL!` error cells for `null` values | -
- Examples (click to show) - -To generate the example sheet: +The example worksheet can be generated with: ```js var ws = XLSX.utils.aoa_to_sheet([ - "SheetJS".split(""), - [1,2,3,4,5,6,7], - [2,3,4,5,6,7,8] + ["S", "h", "e", "e", "t", "J", "S"], + [ 1, 2, , , 5, 6, 7], + [ 2, 3, , , 6, 7, 8], + [ 3, 4, , , 7, 8, 9], + [ 4, 5, 6, 7, 8, 9, 0] ]); ``` -
+ +**Add data from an array of arrays to an existing worksheet** + +```js +XLSX.utils.sheet_add_aoa(ws, aoa, opts); +``` `XLSX.utils.sheet_add_aoa` takes an array of arrays of JS values and updates an existing worksheet object. It follows the same process as `aoa_to_sheet` and @@ -71,22 +84,7 @@ accepts an options argument: | (default) | Start from cell A1 | -
- Examples (click to show) - -Consider the worksheet: - -``` -XXX| A | B | C | D | E | F | G | ----+---+---+---+---+---+---+---+ - 1 | S | h | e | e | t | J | S | - 2 | 1 | 2 | | | 5 | 6 | 7 | - 3 | 2 | 3 | | | 6 | 7 | 8 | - 4 | 3 | 4 | | | 7 | 8 | 9 | - 5 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | -``` - -This worksheet can be built up in the order `A1:G1, A2:B4, E2:G4, A5:G5`: +The example worksheet can be built up in the order `A1:G1, A2:B4, E2:G4, A5:G5`: ```js /* Initial row */ @@ -102,10 +100,14 @@ XLSX.utils.sheet_add_aoa(ws, [[5,6,7], [6,7,8], [7,8,9]], {origin:{r:1, c:4}}); XLSX.utils.sheet_add_aoa(ws, [[4,5,6,7,8,9,0]], {origin: -1}); ``` -
- ### Array of Objects Input +**Create a worksheet from an array of objects** + +```js +var ws = XLSX.utils.json_to_sheet(aoo, opts); +``` + `XLSX.utils.json_to_sheet` takes an array of objects and returns a worksheet with automatically-generated "headers" based on the keys of the objects. The default column order is determined by the first appearance of the field using @@ -119,37 +121,69 @@ default column order is determined by the first appearance of the field using |`skipHeader` | false | If true, do not include header row in output | |`nullError` | false | If true, emit `#NULL!` error cells for `null` values | -- All fields from each row will be written. If `header` is an array and it does - not contain a particular field, the key will be appended to the array. +:::caution + +All fields from each row will be written! `header` hints at a particular order +but is not exclusive. To remove fields from the export, filter the data source. + +Some data sources have special options to filter properties. For example, +MongoDB will add the `_id` field when finding data from a collection: + +```js +const aoo_with_id = await coll.find({}).toArray(); +const ws = XLSX.utils.json_to_sheet(aoo_with_id); // includes _id column +``` + +This can be filtered out through the `projection` property: + +```js +const aoo = await coll.find({}, {projection:{_id:0}}).toArray(); // no _id ! +const ws = XLSX.utils.json_to_sheet(aoo); +``` + +If a data source does not provide a filter option, it can be filtered manually: + +```js +const aoo = data.map(obj => Object.fromEntries(Object.entries(obj).filter(r => headers.indexOf(r[0]) > -1))); +``` + +::: + +- If `header` is an array, missing keys will be added in order of first use. - Cell types are deduced from the type of each value. For example, a `Date` object will generate a Date cell, while a string will generate a Text cell. - Null values will be skipped by default. If `nullError` is true, an error cell corresponding to `#NULL!` will be written to the worksheet. -
- Examples (click to show) - -The original sheet cannot be reproduced using plain objects since JS object keys +The example sheet cannot be reproduced using plain objects since JS object keys must be unique. After replacing the second `e` and `S` with `e_1` and `S_1`: ```js var ws = XLSX.utils.json_to_sheet([ - { S:1, h:2, e:3, e_1:4, t:5, J:6, S_1:7 }, - { S:2, h:3, e:4, e_1:5, t:6, J:7, S_1:8 } + { S:1, h:2, , , t:5, J:6, S_1:7 }, + { S:2, h:3, , , t:6, J:7, S_1:8 } + { S:3, h:4, , , t:7, J:8, S_1:9 } + { S:4, h:5, e:6, e_1:7, t:8, J:9, S_1:0 } ], {header:["S","h","e","e_1","t","J","S_1"]}); ``` -Alternatively, the header row can be skipped: +Alternatively, a different set of unique headers can be used with `skipHeader`: ```js var ws = XLSX.utils.json_to_sheet([ { A:"S", B:"h", C:"e", D:"e", E:"t", F:"J", G:"S" }, - { A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7 }, - { A: 2, B: 3, C: 4, D: 5, E: 6, F: 7, G: 8 } + { A: 1, B: 2, , , E: 5, F: 6, G: 7 }, + { A: 2, B: 3, , , E: 6, F: 7, G: 8 } + { A: 3, B: 4, , , E: 7, F: 8, G: 9 }, + { A: 4, B: 5, C: 6, D: 7, E: 8, F: 9, G: 0 }, ], {header:["A","B","C","D","E","F","G"], skipHeader:true}); ``` -
+**Add data from an array of objects to an existing worksheet** + +```js +XLSX.utils.sheet_add_json(ws, aoo, opts); +``` `XLSX.utils.sheet_add_json` takes an array of objects and updates an existing worksheet object. It follows the same process as `json_to_sheet` and accepts @@ -175,22 +209,7 @@ an options argument: | (default) | Start from cell A1 | -
- Examples (click to show) - -Consider the worksheet: - -``` -XXX| A | B | C | D | E | F | G | ----+---+---+---+---+---+---+---+ - 1 | S | h | e | e | t | J | S | - 2 | 1 | 2 | | | 5 | 6 | 7 | - 3 | 2 | 3 | | | 6 | 7 | 8 | - 4 | 3 | 4 | | | 7 | 8 | 9 | - 5 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | -``` - -This worksheet can be built up in the order `A1:G1, A2:B4, E2:G4, A5:G5`: +This example worksheet can be built up in the order `A1:G1, A2:B4, E2:G4, A5:G5`: ```js /* Initial row */ @@ -214,10 +233,49 @@ XLSX.utils.sheet_add_json(ws, [ ], {header: ["A", "B", "C", "D", "E", "F", "G"], skipHeader: true, origin: -1}); ``` -
+:::note + +If the `header` option is an array, `sheet_add_json` and `sheet_to_json` will +append missing elements. + +This design enables consistent header order across calls: + +```jsx live +function SheetJSHeaderOrder() { + /* Use shared header */ + const header = []; + const ws1 = XLSX.utils.json_to_sheet([ {C: 2, D: 3}, ], {header}); + XLSX.utils.sheet_add_json(ws1, [ {D: 1, C: 4}, ], {header, origin: -1, skipHeader: true}); + + /* only use header in first call */ + const ws2 = XLSX.utils.json_to_sheet([ {C: 2, D: 3}, ], {header:[]}); + XLSX.utils.sheet_add_json(ws2, [ {D: 1, C: 4}, ], {origin: -1, skipHeader: true}); + + return (
+    Objects
+    {"\n[\n  { C: 2, D: 3 },\n  { D: 1, C: 4 } // different key order\n]\n"}
+ Worksheet when same `header` array is passed to `sheet_add_json` +
+ New contents of `header`
+ {JSON.stringify(header)}
+
+ Worksheet when no `header` property is passed to `sheet_add_json` +
+
) +} +``` + +::: ### HTML Table Input +**Create a worksheet or workbook from an HTML DOM TABLE** + +```js +var ws = XLSX.utils.table_to_sheet(elt, opts); +var wb = XLSX.utils.table_to_book(elt, opts); +``` + `XLSX.utils.table_to_sheet` takes a table DOM element and returns a worksheet resembling the input table. Numbers are parsed. All other data will be stored as strings. @@ -235,29 +293,47 @@ Both functions accept options arguments: |`display` | false | If true, hidden rows and cells will not be parsed | -
- Examples (click to show) - -To generate the example sheet, start with the HTML table: - -```html - - - - -
SheetJS
1234567
2345678
-``` - -To process the table: +To generate the example sheet, assuming the table has ID `sheetjs`: ```js var tbl = document.getElementById('sheetjs'); -var wb = XLSX.utils.table_to_book(tbl); +var ws = XLSX.utils.table_to_sheet(tbl); ``` -
-Note: `XLSX.read` can handle HTML represented as strings. +:::note +`table_to_book` and `table_to_sheet` act on HTML DOM elements. Starting from +an HTML string, there are two parsing approaches: + +A) Table Phantasm: create a DIV with the desired HTML. + +```js +/* create element from the source */ +var elt = document.createElement("div"); +elt.innerHTML = html_source; +document.body.appendChild(elt); + +/* generate worksheet */ +var ws = XLSX.utils.table_to_sheet(elt.getElementsByTagName("TABLE")[0]); + +/* remove element */ +document.body.removeChild(elt); +``` + +B) Raw HTML: use `XLSX.read` to read the text in the same manner as CSV. + +```js +var wb = XLSX.read(html_source, { type: "string" }); +var ws = wb.Sheets[wb.SheetNames[0]]; +``` + +::: + +**Add data from a HTML DOM TABLE to an existing worksheet** + +```js +XLSX.utils.sheet_add_dom(ws, elt, opts); +``` `XLSX.utils.sheet_add_dom` takes a table DOM element and updates an existing worksheet object. It follows the same process as `table_to_sheet` and accepts @@ -282,53 +358,74 @@ an options argument: | (default) | Start from cell A1 | -
- Examples (click to show) +A common use case for `sheet_add_dom` involves adding multiple tables to a +single worksheet, usually with a few blank rows in between each table: -A small helper function can create gap rows between tables: +![Multi-Table Export in Excel](pathname:///files/multitable.png) -```js -function create_gap_rows(ws, nrows) { - var ref = XLSX.utils.decode_range(ws["!ref"]); // get original range - ref.e.r += nrows; // add to ending row - ws["!ref"] = XLSX.utils.encode_range(ref); // reassign row +```jsx live +function MultiTable() { + const headers = ["Table 1", "Table2", "Table 3"]; + + /* Callback invoked when the button is clicked */ + const xport = React.useCallback(async () => { + /* This function creates gap rows */ + function create_gap_rows(ws, nrows) { + var ref = XLSX.utils.decode_range(ws["!ref"]); // get original range + ref.e.r += nrows; // add to ending row + ws["!ref"] = XLSX.utils.encode_range(ref); // reassign row + } + + /* first table */ + const ws = XLSX.utils.aoa_to_sheet([[headers[0]]]); + XLSX.utils.sheet_add_dom(ws, document.getElementById('table1'), {origin: -1}); + create_gap_rows(ws, 1); // one row gap after first table + + /* second table */ + XLSX.utils.sheet_add_aoa(ws, [[headers[1]]], {origin: -1}); + XLSX.utils.sheet_add_dom(ws, document.getElementById('table2'), {origin: -1}); + create_gap_rows(ws, 2); // two rows gap after second table + + /* third table */ + XLSX.utils.sheet_add_aoa(ws, [[headers[2]]], {origin: -1}); + XLSX.utils.sheet_add_dom(ws, document.getElementById('table3'), {origin: -1}); + + /* create workbook and export */ + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, "Export"); + XLSX.writeFile(wb, "SheetJSMultiTablexport.xlsx"); + }); + + return ( + <> +

+ {headers[0]}
+ + + +
A2B2
A3B3
+ {headers[1]}
+ + + +
A6B6C6
A7B7C7
+
+ {headers[2]}
+ + + +
A11B11
A12B12
+ + ); } - -/* first table */ -var ws = XLSX.utils.table_to_sheet(document.getElementById('table1')); -create_gap_rows(ws, 1); // one row gap after first table - -/* second table */ -XLSX.utils.sheet_add_dom(ws, document.getElementById('table2'), {origin: -1}); -create_gap_rows(ws, 3); // three rows gap after second table - -/* third table */ -XLSX.utils.sheet_add_dom(ws, document.getElementById('table3'), {origin: -1}); ``` -
- -### Formulae Output - -`XLSX.utils.sheet_to_formulae` generates an array of commands that represent -how a person would enter data into an application. Each entry is of the form -`A1-cell-address=formula-or-value`. String literals are prefixed with a `'` in -accordance with Excel. - -
- Examples (click to show) - -For the example sheet: - -```js -> var o = XLSX.utils.sheet_to_formulae(ws); -> [o[0], o[5], o[10], o[15], o[20]]; -[ 'A1=\'S', 'F1=\'J', 'D2=4', 'B3=3', 'G3=8' ] -``` -
- ### Delimiter-Separated Output +```js +var csv = XLSX.utils.sheet_to_csv(ws, opts); +``` + As an alternative to the `writeFile` CSV type, `XLSX.utils.sheet_to_csv` also produces CSV output. The function takes an options argument: @@ -351,26 +448,35 @@ produces CSV output. The function takes an options argument: Using `XLSX.write` with type `string` will also skip the mark. -
- Examples (click to show) +Starting from the example worksheet: -For the example sheet: +```jsx live +function SheetJSCSVTest() { + var ws = XLSX.utils.aoa_to_sheet([ + ["S", "h", "e", "e", "t", "J", "S"], + [ 1, 2, , , 5, 6, 7], + [ 2, 3, , , 6, 7, 8], + [ 3, 4, , , 7, 8, 9], + [ 4, 5, 6, 7, 8, 9, 0] + ]); + return (
+    Worksheet (as HTML)
+    
+ XLSX.utils.sheet_to_csv(ws)
+ {XLSX.utils.sheet_to_csv(ws)}

+ XLSX.utils.sheet_to_csv(ws, {'{'} FS: "\t" {'}'})
+ {XLSX.utils.sheet_to_csv(ws, { FS: "\t" })}

+ XLSX.utils.sheet_to_csv(ws, {'{'} FS: ":", RS: "|" {'}'})
+ {XLSX.utils.sheet_to_csv(ws, { FS: ":", RS: "|" })}
+
); +} +``` + +**UTF-16 Text Output** ```js -> console.log(XLSX.utils.sheet_to_csv(ws)); -S,h,e,e,t,J,S -1,2,3,4,5,6,7 -2,3,4,5,6,7,8 -> console.log(XLSX.utils.sheet_to_csv(ws, {FS:"\t"})); -S h e e t J S -1 2 3 4 5 6 7 -2 3 4 5 6 7 8 -> console.log(XLSX.utils.sheet_to_csv(ws,{FS:":",RS:"|"})); -S:h:e:e:t:J:S|1:2:3:4:5:6:7|2:3:4:5:6:7:8| +var txt = XLSX.utils.sheet_to_txt(ws, opts); ``` -
- -#### UTF-16 Unicode Text The `txt` output type uses the tab character as the field separator. If the `codepage` library is available (included in full distribution but not core), @@ -378,8 +484,13 @@ the output will be encoded in `CP1200` and the BOM will be prepended. `XLSX.utils.sheet_to_txt` takes the same arguments as `sheet_to_csv`. + ### HTML Output +```js +var html = XLSX.utils.sheet_to_html(ws, opts); +``` + As an alternative to the `writeFile` HTML type, `XLSX.utils.sheet_to_html` also produces HTML output. The function takes an options argument: @@ -390,18 +501,31 @@ produces HTML output. The function takes an options argument: |`header` | | Override header (default `html body`) | |`footer` | | Override footer (default `/body /html`) | -
- Examples (click to show) +Starting from the example worksheet: -For the example sheet: +```jsx live +function SheetJSHTML() { + var ws = XLSX.utils.aoa_to_sheet([ + ["S", "h", "e", "e", "t", "J", "S"], + [ 1, 2, , , 5, 6, 7], + [ 2, 3, , , 6, 7, 8], + [ 3, 4, , , 7, 8, 9], + [ 4, 5, 6, 7, 8, 9, 0] + ]); + return (
+    XLSX.utils.sheet_to_html(ws)
+    
+
); +} +``` + +### Array Output ```js -> console.log(XLSX.utils.sheet_to_html(ws)); -// ... -``` -
+var arr = XLSX.utils.sheet_to_json(ws, opts); -### JSON +var aoa = XLSX.utils.sheet_to_json(ws, {header: 1, ...other_opts}); +``` `XLSX.utils.sheet_to_json` generates different types of JS objects. The function takes an options argument: @@ -452,46 +576,110 @@ takes an options argument: - If header is an array, the keys will not be disambiguated. This can lead to unexpected results if the array values are not unique! +For the example worksheet: -
- Examples (click to show) +```jsx live +function SheetJSToJSON() { + /* original data */ + var ws = XLSX.utils.aoa_to_sheet([ + ["S", "h", "e", "e", "t", "J", "S"], + [ 1, 2, , , 5, 6, 7], + [ 2, 3, , , 6, 7, 8], + [ 3, 4, , , 7, 8, 9], + [ 4, 5, 6, 7, 8, 9, 0] + ]); -For the example sheet: + /* display JS objects with some whitespace */ + const aoo = o => o.map(r => " " + JSON.stringify(r).replace(/,"/g, ', "').replace(/:/g, ": ").replace(/"([A-Za-z_]\w*)":/g, '$1:')).join("\n"); + const aoa = o => o.map(r => " " + JSON.stringify(r).replace(/,/g, ', ').replace(/null/g, "")).join("\n"); -```js -> XLSX.utils.sheet_to_json(ws); -[ { S: 1, h: 2, e: 3, e_1: 4, t: 5, J: 6, S_1: 7 }, - { S: 2, h: 3, e: 4, e_1: 5, t: 6, J: 7, S_1: 8 } ] - -> XLSX.utils.sheet_to_json(ws, {header:"A"}); -[ { A: 'S', B: 'h', C: 'e', D: 'e', E: 't', F: 'J', G: 'S' }, - { A: '1', B: '2', C: '3', D: '4', E: '5', F: '6', G: '7' }, - { A: '2', B: '3', C: '4', D: '5', E: '6', F: '7', G: '8' } ] - -> XLSX.utils.sheet_to_json(ws, {header:["A","E","I","O","U","6","9"]}); -[ { '6': 'J', '9': 'S', A: 'S', E: 'h', I: 'e', O: 'e', U: 't' }, - { '6': '6', '9': '7', A: '1', E: '2', I: '3', O: '4', U: '5' }, - { '6': '7', '9': '8', A: '2', E: '3', I: '4', O: '5', U: '6' } ] - -> XLSX.utils.sheet_to_json(ws, {header:1}); -[ [ 'S', 'h', 'e', 'e', 't', 'J', 'S' ], - [ '1', '2', '3', '4', '5', '6', '7' ], - [ '2', '3', '4', '5', '6', '7', '8' ] ] + return (
+    Worksheet (as HTML)
+    
+ XLSX.utils.sheet_to_json(ws, {'{'} header: 1 {'}'}) [array of arrays]
+ [
{aoa(XLSX.utils.sheet_to_json(ws, { header: 1 }))}
]

+ XLSX.utils.sheet_to_json(ws) [objects with header disambiguation]
+ [
{aoo(XLSX.utils.sheet_to_json(ws))}
]

+ XLSX.utils.sheet_to_json(ws, {'{'} header: "A" {'}'}) [column names as keys]
+ [
{aoo(XLSX.utils.sheet_to_json(ws, { header: "A" }))}
]

+ XLSX.utils.sheet_to_json(ws, {'{'} header: ["A","E","I","O","U","6","9"] {'}'})
+ [
{aoo(XLSX.utils.sheet_to_json(ws, { header: ["A","E","I","O","U","6","9"] }))}
]
+
); +} ``` -Example showing the effect of `raw`: +### Formulae Output ```js -> ws['A2'].w = "3"; // set A2 formatted string value - -> XLSX.utils.sheet_to_json(ws, {header:1, raw:false}); -[ [ 'S', 'h', 'e', 'e', 't', 'J', 'S' ], - [ '3', '2', '3', '4', '5', '6', '7' ], // <-- A2 uses the formatted string - [ '2', '3', '4', '5', '6', '7', '8' ] ] - -> XLSX.utils.sheet_to_json(ws, {header:1}); -[ [ 'S', 'h', 'e', 'e', 't', 'J', 'S' ], - [ 1, 2, 3, 4, 5, 6, 7 ], // <-- A2 uses the raw value - [ 2, 3, 4, 5, 6, 7, 8 ] ] +var fmla_arr = XLSX.utils.sheet_to_formulae(ws); +``` + +`XLSX.utils.sheet_to_formulae` generates an array of commands that represent +how a person would enter data into an application. + +Cells without formulae are written as `A1-cell-address=value`: + +``` +A1=1 // A1 is the numeric value 1 +B1=TRUE // B1 is the logical value TRUE +``` + +String literals are prefixed with a `'` in accordance with Excel: + +``` +A5='A4+A3 // A5 is the string "A4+A3" +``` + +Cells with formulae are written as `A1-cell-address=formula`: + +``` +A5=A4+A3 // A5 is a cell with formula =A4+A3 +``` + +Array formulae are written as `A1-range=formula`. They do not include the +displayed curly braces: + +``` +A4:B4=A2:B2*A3:B3 // A4:B4 array formula {=A2:B2*A3:B3} +``` + +Single-cell array formulae are written with single-cell ranges: + +``` +C4:C4=SUM(A2:A3*B2:B3) // C4 array formula {=SUM(A2:A3*B2:B3)} +``` + +```jsx live +function SheetJSToJSON() { + var ws = XLSX.utils.aoa_to_sheet([ + ["A", "B", "C"], + [1, 2, { t: "n", f: "SUM(A2:B2)" }], + [3, 4, { t: "n", f: "A3+B3" }] + ]); + XLSX.utils.sheet_set_array_formula(ws, "A4:B4", "A2:B2*A3:B3"); + XLSX.utils.sheet_set_array_formula(ws, "C4", "SUM(A2:A3*B2:B3)"); + + var __html = `\ +Values +[ + ["A", "B", "C"], + [1, 2], + [3, 4] +] +Formulae +C2 =SUM(A2:B2) +C3 =A3+B3 +Array Formulae +A4:B4 {=A2:B2*A3:B3} +C4 {=SUM(A2:A3*B2:B3)} + +`; + + return (
+    Original worksheet
+    
+ XLSX.utils.sheet_to_formulae(ws).join("\n")
+
{XLSX.utils.sheet_to_formulae(ws).join("\n")} +
); +} ``` -
diff --git a/docz/docs/09-miscellany/01-formats.md b/docz/docs/09-miscellany/01-formats.md index a69d9fc0..2f0ea9db 100644 --- a/docz/docs/09-miscellany/01-formats.md +++ b/docz/docs/09-miscellany/01-formats.md @@ -1,5 +1,6 @@ --- sidebar_position: 1 +hide_table_of_contents: true --- # File Formats @@ -39,7 +40,7 @@ sidebar_position: 1 | Works 6.x-9.x Spreadsheet (XLR) | ✔ | | | **Other Common Spreadsheet Output Formats** |:-----:|:-----:| | HTML Tables | ✔ | ✔ | -| Rich Text Format tables (RTF) | | ✔ | +| Rich Text Format tables (RTF) | ✔ | ✔ | | Ethercalc Record Format (ETH) | ✔ | ✔ | Features not supported by a given file format will not be written. Formats with diff --git a/docz/docs/img/formats.png b/docz/docs/img/formats.png index dd478aa5..df08436b 100644 Binary files a/docz/docs/img/formats.png and b/docz/docs/img/formats.png differ diff --git a/docz/static/files/multitable.png b/docz/static/files/multitable.png new file mode 100644 index 00000000..c3526944 Binary files /dev/null and b/docz/static/files/multitable.png differ diff --git a/docz/static/img/formats.svg b/docz/static/img/formats.svg deleted file mode 100644 index 08f30e9c..00000000 --- a/docz/static/img/formats.svg +++ /dev/null @@ -1,534 +0,0 @@ - - - - - - -G - - - -csf - - -Common -Spreadsheet -Format -(JS Object) - - - -xls2 - -XLS -BIFF2 - - - -csf->xls2 - - - - - -xls3 - -XLS -BIFF3 - - - -csf->xls3 - - - - - -xls4 - -XLS -BIFF4 - - - -csf->xls4 - - - - - -xls5 - -XLS -BIFF5 - - - -csf->xls5 - - - - - -xls8 - -XLS -BIFF8 - - - -csf->xls8 - - - - - -xlml - -SSML -(2003/4) - - - -csf->xlml - - - - - -xlsx - -XLSX -XLSM - - - -csf->xlsx - - - - - -xlsb - -XLSB -BIFF12 - - - -csf->xlsb - - - - - -nums - -NUMBERS - - - -csf->nums - - - - - -ods - -ODS - - - -csf->ods - - - - - -fods - -FODS - - - -csf->fods - - - - - -html - -HTML -Table - - - -csf->html - - - - - -csv - -CSV - - - -csf->csv - - - - - -txt - -TXT -UTF-16 - - - -csf->txt - - - - - -dbf - -DBF - - - -csf->dbf - - - - - -dif - -DIF - - - -csf->dif - - - - - -slk - -SYLK - - - -csf->slk - - - - - -prn - -PRN - - - -csf->prn - - - - - -rtf - -RTF - - - -csf->rtf - - - - - -wk1 - -WK1 - - - -csf->wk1 - - - - - -wk3 - -WK3 - - - -csf->wk3 - - - - - -eth - -ETH - - - -csf->eth - - - - - -xls2->csf - - - - - -xls3->csf - - - - - -xls4->csf - - - - - -xls5->csf - - - - - -xls8->csf - - - - - -xlml->csf - - - - - -xlsx->csf - - - - - -xlsb->csf - - - - - -nums->csf - - - - - -ods->csf - - - - - -fods->csf - - - - - -uos - -UOS - - - -uos->csf - - - - - -html->csf - - - - - -csv->csf - - - - - -txt->csf - - - - - -dbf->csf - - - - - -dif->csf - - - - - -slk->csf - - - - - -prn->csf - - - - - -wk1->csf - - - - - -wksl - -WKS -Lotus - - - -wksl->csf - - - - - -wk3->csf - - - - - -wk4 - -WK4 - - - -wk4->csf - - - - - -123 - -123 - - - -123->csf - - - - - -wksm - -WKS -Works - - - -wksm->csf - - - - - -xlr - -XLR - - - -xlr->csf - - - - - -wq1 - -WQ1 - - - -wq1->csf - - - - - -wq2 - -WQ2 -WB* - - - -wq2->csf - - - - - -qpw - -QPW - - - -qpw->csf - - - - - -eth->csf - - - - - diff --git a/formats.png b/formats.png index dd478aa5..df08436b 100644 Binary files a/formats.png and b/formats.png differ diff --git a/misc/formats.dot b/misc/formats.dot index 806fbc6a..bf663488 100644 --- a/misc/formats.dot +++ b/misc/formats.dot @@ -1,6 +1,6 @@ digraph G { graph [mindist=0.1]; - node [fontname="Indie Flower"]; + node [fontname="Indie Flower",fontsize=22]; csf [shape=doublecircle,label="Common\nSpreadsheet\nFormat\n(JS Object)"]; subgraph XL { node [style=filled,color="#00FF00"]; @@ -85,6 +85,7 @@ digraph G { wksl -> csf wksm -> csf dif -> csf + rtf -> csf csf -> rtf prn -> csf csf -> prn diff --git a/misc/formats.svg b/misc/formats.svg index 08f30e9c..5ad42638 100644 --- a/misc/formats.svg +++ b/misc/formats.svg @@ -4,531 +4,537 @@ - - + + G - + csf - - -Common -Spreadsheet -Format -(JS Object) + + +Common +Spreadsheet +Format +(JS Object) xls2 - -XLS -BIFF2 + +XLS +BIFF2 csf->xls2 - - + + xls3 - -XLS -BIFF3 + +XLS +BIFF3 csf->xls3 - - + + xls4 - -XLS -BIFF4 + +XLS +BIFF4 csf->xls4 - - + + xls5 - -XLS -BIFF5 + +XLS +BIFF5 csf->xls5 - - + + xls8 - -XLS -BIFF8 + +XLS +BIFF8 csf->xls8 - - + + xlml - -SSML -(2003/4) + +SSML +(2003/4) csf->xlml - - + + xlsx - -XLSX -XLSM + +XLSX +XLSM csf->xlsx - - + + xlsb - -XLSB -BIFF12 + +XLSB +BIFF12 csf->xlsb - - + + nums - -NUMBERS + +NUMBERS csf->nums - - + + ods - -ODS + +ODS csf->ods - - + + fods - -FODS + +FODS csf->fods - - + + html - -HTML -Table + +HTML +Table - + csf->html - - + + csv - -CSV + +CSV - + csf->csv - - + + txt - -TXT -UTF-16 + +TXT +UTF-16 - + csf->txt - - + + dbf - -DBF + +DBF - + csf->dbf - - + + dif - -DIF + +DIF csf->dif - - + + slk - -SYLK + +SYLK csf->slk - - + + prn - -PRN + +PRN - + csf->prn - - + + rtf - -RTF + +RTF - + csf->rtf - - + + wk1 - -WK1 + +WK1 csf->wk1 - - + + wk3 - -WK3 + +WK3 csf->wk3 - - + + eth - -ETH + +ETH - + csf->eth - - + + xls2->csf - - + + xls3->csf - - + + xls4->csf - - + + xls5->csf - - + + xls8->csf - - + + xlml->csf - - + + xlsx->csf - - + + xlsb->csf - - + + nums->csf - - + + ods->csf - - + + fods->csf - - + + uos - -UOS + +UOS uos->csf - - + + - + html->csf - - + + - + csv->csf - - + + - + txt->csf - - + + - + dbf->csf - - + + dif->csf - - + + slk->csf - - + + - + prn->csf - - + + + + + +rtf->csf + + wk1->csf - - + + wksl - -WKS -Lotus + +WKS +Lotus wksl->csf - - + + wk3->csf - - + + wk4 - -WK4 + +WK4 wk4->csf - - + + 123 - -123 + +123 123->csf - - + + wksm - -WKS -Works + +WKS +Works wksm->csf - - + + xlr - -XLR + +XLR xlr->csf - - + + wq1 - -WQ1 + +WQ1 wq1->csf - - + + wq2 - -WQ2 -WB* + +WQ2 +WB* wq2->csf - - + + qpw - -QPW + +QPW qpw->csf - - + + - + eth->csf - - + + diff --git a/misc/formats.svg.svg b/misc/formats.svg.svg index 1f249127..60ce1444 100644 --- a/misc/formats.svg.svg +++ b/misc/formats.svg.svg @@ -1,527 +1,533 @@ - - -G - + + +G + csf - - -Common -Spreadsheet -Format -(JS Object) + + +Common +Spreadsheet +Format +(JS Object) xls2 - -XLS -BIFF2 + +XLS +BIFF2 csf->xls2 - - + + xls3 - -XLS -BIFF3 + +XLS +BIFF3 csf->xls3 - - + + xls4 - -XLS -BIFF4 + +XLS +BIFF4 csf->xls4 - - + + xls5 - -XLS -BIFF5 + +XLS +BIFF5 csf->xls5 - - + + xls8 - -XLS -BIFF8 + +XLS +BIFF8 csf->xls8 - - + + xlml - -SSML -(2003/4) + +SSML +(2003/4) csf->xlml - - + + xlsx - -XLSX -XLSM + +XLSX +XLSM csf->xlsx - - + + xlsb - -XLSB -BIFF12 + +XLSB +BIFF12 csf->xlsb - - + + nums - -NUMBERS + +NUMBERS csf->nums - - + + ods - -ODS + +ODS csf->ods - - + + fods - -FODS + +FODS csf->fods - - + + html - -HTML -Table + +HTML +Table - + csf->html - - + + csv - -CSV + +CSV - + csf->csv - - + + txt - -TXT -UTF-16 + +TXT +UTF-16 - + csf->txt - - + + dbf - -DBF + +DBF - + csf->dbf - - + + dif - -DIF + +DIF csf->dif - - + + slk - -SYLK + +SYLK csf->slk - - + + prn - -PRN + +PRN - + csf->prn - - + + rtf - -RTF + +RTF - + csf->rtf - - + + wk1 - -WK1 + +WK1 csf->wk1 - - + + wk3 - -WK3 + +WK3 csf->wk3 - - + + eth - -ETH + +ETH - + csf->eth - - + + xls2->csf - - + + xls3->csf - - + + xls4->csf - - + + xls5->csf - - + + xls8->csf - - + + xlml->csf - - + + xlsx->csf - - + + xlsb->csf - - + + nums->csf - - + + ods->csf - - + + fods->csf - - + + uos - -UOS + +UOS uos->csf - - + + - + html->csf - - + + - + csv->csf - - + + - + txt->csf - - + + - + dbf->csf - - + + dif->csf - - + + slk->csf - - + + - + prn->csf - - + + + + + +rtf->csf + + wk1->csf - - + + wksl - -WKS -Lotus + +WKS +Lotus wksl->csf - - + + wk3->csf - - + + wk4 - -WK4 + +WK4 wk4->csf - - + + 123 - -123 + +123 123->csf - - + + wksm - -WKS -Works + +WKS +Works wksm->csf - - + + xlr - -XLR + +XLR xlr->csf - - + + wq1 - -WQ1 + +WQ1 wq1->csf - - + + wq2 - -WQ2 -WB* + +WQ2 +WB* wq2->csf - - + + qpw - -QPW + +QPW qpw->csf - - + + - + eth->csf - - + + \ No newline at end of file