headless
This commit is contained in:
parent
8f216aa501
commit
0847c803f2
3
Makefile
3
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
|
||||
|
@ -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] && (<pre><b>Data from clipboard RTF (text/rtf)</b><br/>{csvs[2]}</pre>)}
|
||||
{csvs.every(x => !x) && <b>Copy data in Excel, click here, and paste (Control+V)</b>}
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 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:
|
||||
|
||||
<table id="srcdata">
|
||||
<tr><td>SheetJS</td><td>Clipboard</td><td>Demo</td></tr>
|
||||
<tr><td>bookType</td><td>RTF</td></tr>
|
||||
<tr><td>source</td><td>HTML Table</td></tr>
|
||||
</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 (
|
||||
<b>Select this text, copy (Control+C), and paste in Excel</b>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
215
docz/docs/04-getting-started/03-demos/07-headless.md
Normal file
215
docz/docs/04-getting-started/03-demos/07-headless.md
Normal file
@ -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 <https://sheetjs.com/demos/table>.
|
||||
|
||||
:::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 <https://phantomjs.org/download.html>
|
||||
|
||||
:::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.
|
||||
|
||||
:::
|
@ -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/)
|
||||
|
@ -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):
|
||||
|
||||
<details>
|
||||
<summary><b>HTML TABLE element in a webpage</b> (click to show)</summary>
|
||||
|
||||
```html
|
||||
<!-- include the standalone script and shim. this uses the UNPKG CDN -->
|
||||
<!-- include the standalone script and shim -->
|
||||
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js"></script>
|
||||
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
|
||||
|
||||
@ -897,95 +901,6 @@ chrome.runtime.onMessage.addListener(function(msg, sender, cb) {
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Server-Side HTML Tables with Headless Chrome</b> (click to show)</summary>
|
||||
|
||||
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();
|
||||
})();
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Server-Side HTML Tables with Headless WebKit</b> (click to show)</summary>
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>NodeJS HTML Tables without a browser</b> (click to show)</summary>
|
||||
|
||||
|
@ -245,28 +245,34 @@ The [`extendscript` demo](../getting-started/demos/extendscript) includes a more
|
||||
</TabItem>
|
||||
<TabItem value="headless" label="Headless">
|
||||
|
||||
<details>
|
||||
<summary><b>PhantomJS (Headless Webkit) File Generation</b> (click to show)</summary>
|
||||
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:
|
||||
|
||||
</details>
|
||||
```js
|
||||
/* from the browser context */
|
||||
var bin = XLSX.write(workbook, { type:"binary", bookType: "xlsb" });
|
||||
|
||||
/* from the automation context */
|
||||
fs.write("SheetJSansHead.xlsb", bin, "wb");
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
@ -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');
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -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 |
|
||||
```
|
||||
<table>
|
||||
<tr><td>S</td><td>h</td><td>e</td><td>e</td><td>t</td><td>J</td><td>S</td></tr>
|
||||
<tr><td>1</td><td>2</td><td> </td><td> </td><td>5</td><td>6</td><td>7</td></tr>
|
||||
<tr><td>2</td><td>3</td><td> </td><td> </td><td>6</td><td>7</td><td>8</td></tr>
|
||||
<tr><td>3</td><td>4</td><td> </td><td> </td><td>7</td><td>8</td><td>9</td></tr>
|
||||
<tr><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td><td>0</td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
### 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 |
|
||||
|
||||
<details>
|
||||
<summary><b>Examples</b> (click to show)</summary>
|
||||
|
||||
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]
|
||||
]);
|
||||
```
|
||||
</details>
|
||||
|
||||
**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 |
|
||||
|
||||
|
||||
<details>
|
||||
<summary><b>Examples</b> (click to show)</summary>
|
||||
|
||||
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});
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### 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.
|
||||
|
||||
<details>
|
||||
<summary><b>Examples</b> (click to show)</summary>
|
||||
|
||||
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});
|
||||
```
|
||||
|
||||
</details>
|
||||
**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 |
|
||||
|
||||
|
||||
<details>
|
||||
<summary><b>Examples</b> (click to show)</summary>
|
||||
|
||||
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});
|
||||
```
|
||||
|
||||
</details>
|
||||
:::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 (<pre>
|
||||
<b>Objects</b>
|
||||
{"\n[\n { C: 2, D: 3 },\n { D: 1, C: 4 } // different key order\n]\n"}<br/>
|
||||
<b>Worksheet when same `header` array is passed to `sheet_add_json`</b>
|
||||
<div dangerouslySetInnerHTML={{__html:XLSX.utils.sheet_to_html(ws1)}}/>
|
||||
<i>New contents of `header`</i><br/>
|
||||
{JSON.stringify(header)}<br/>
|
||||
<br/>
|
||||
<b>Worksheet when no `header` property is passed to `sheet_add_json`</b>
|
||||
<div dangerouslySetInnerHTML={{__html:XLSX.utils.sheet_to_html(ws2)}}/>
|
||||
</pre>)
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### 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 |
|
||||
|
||||
|
||||
<details>
|
||||
<summary><b>Examples</b> (click to show)</summary>
|
||||
|
||||
To generate the example sheet, start with the HTML table:
|
||||
|
||||
```html
|
||||
<table id="sheetjs">
|
||||
<tr><td>S</td><td>h</td><td>e</td><td>e</td><td>t</td><td>J</td><td>S</td></tr>
|
||||
<tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td></tr>
|
||||
<tr><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td></tr>
|
||||
</table>
|
||||
```
|
||||
|
||||
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);
|
||||
```
|
||||
</details>
|
||||
|
||||
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 |
|
||||
|
||||
|
||||
<details>
|
||||
<summary><b>Examples</b> (click to show)</summary>
|
||||
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 (
|
||||
<>
|
||||
<button onClick={xport}><b>Export XLSX!</b></button><br/><br/>
|
||||
<b>{headers[0]}</b><br/>
|
||||
<table id="table1">
|
||||
<tr><td>A2</td><td>B2</td></tr>
|
||||
<tr><td>A3</td><td>B3</td></tr>
|
||||
</table>
|
||||
<b>{headers[1]}</b><br/>
|
||||
<table id="table2">
|
||||
<tr><td>A6</td><td>B6</td><td>C6</td></tr>
|
||||
<tr><td>A7</td><td>B7</td><td>C7</td></tr>
|
||||
</table>
|
||||
<br/>
|
||||
<b>{headers[2]}</b><br/>
|
||||
<table id="table3">
|
||||
<tr><td>A11</td><td>B11</td></tr>
|
||||
<tr><td>A12</td><td>B12</td></tr>
|
||||
</table>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/* 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});
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### 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.
|
||||
|
||||
<details>
|
||||
<summary><b>Examples</b> (click to show)</summary>
|
||||
|
||||
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' ]
|
||||
```
|
||||
</details>
|
||||
|
||||
### 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.
|
||||
|
||||
|
||||
<details>
|
||||
<summary><b>Examples</b> (click to show)</summary>
|
||||
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 ( <pre>
|
||||
<b>Worksheet (as HTML)</b>
|
||||
<div dangerouslySetInnerHTML={{__html: XLSX.utils.sheet_to_html(ws)}}/>
|
||||
<b>XLSX.utils.sheet_to_csv(ws)</b><br/>
|
||||
{XLSX.utils.sheet_to_csv(ws)}<br/><br/>
|
||||
<b>XLSX.utils.sheet_to_csv(ws, {'{'} FS: "\t" {'}'})</b><br/>
|
||||
{XLSX.utils.sheet_to_csv(ws, { FS: "\t" })}<br/><br/>
|
||||
<b>XLSX.utils.sheet_to_csv(ws, {'{'} FS: ":", RS: "|" {'}'})</b><br/>
|
||||
{XLSX.utils.sheet_to_csv(ws, { FS: ":", RS: "|" })}<br/>
|
||||
</pre> );
|
||||
}
|
||||
```
|
||||
|
||||
**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);
|
||||
```
|
||||
</details>
|
||||
|
||||
#### 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`) |
|
||||
|
||||
<details>
|
||||
<summary><b>Examples</b> (click to show)</summary>
|
||||
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 ( <pre>
|
||||
<b>XLSX.utils.sheet_to_html(ws)</b>
|
||||
<div dangerouslySetInnerHTML={{__html: XLSX.utils.sheet_to_html(ws)}}/>
|
||||
</pre> );
|
||||
}
|
||||
```
|
||||
|
||||
### Array Output
|
||||
|
||||
```js
|
||||
> console.log(XLSX.utils.sheet_to_html(ws));
|
||||
// ...
|
||||
```
|
||||
</details>
|
||||
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:
|
||||
|
||||
<details>
|
||||
<summary><b>Examples</b> (click to show)</summary>
|
||||
```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 ( <pre>
|
||||
<b>Worksheet (as HTML)</b>
|
||||
<div dangerouslySetInnerHTML={{__html: XLSX.utils.sheet_to_html(ws)}}/>
|
||||
<b>XLSX.utils.sheet_to_json(ws, {'{'} header: 1 {'}'}) [array of arrays]</b><br/>
|
||||
[<br/>{aoa(XLSX.utils.sheet_to_json(ws, { header: 1 }))}<br/>]<br/><br/>
|
||||
<b>XLSX.utils.sheet_to_json(ws) [objects with header disambiguation]</b><br/>
|
||||
[<br/>{aoo(XLSX.utils.sheet_to_json(ws))}<br/>]<br/><br/>
|
||||
<b>XLSX.utils.sheet_to_json(ws, {'{'} header: "A" {'}'}) [column names as keys]</b><br/>
|
||||
[<br/>{aoo(XLSX.utils.sheet_to_json(ws, { header: "A" }))}<br/>]<br/><br/>
|
||||
<b>XLSX.utils.sheet_to_json(ws, {'{'} header: ["A","E","I","O","U","6","9"] {'}'})</b><br/>
|
||||
[<br/>{aoo(XLSX.utils.sheet_to_json(ws, { header: ["A","E","I","O","U","6","9"] }))}<br/>]<br/>
|
||||
</pre> );
|
||||
}
|
||||
```
|
||||
|
||||
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 = `\
|
||||
<i>Values</i>
|
||||
[
|
||||
["A", "B", "C"],
|
||||
[1, 2],
|
||||
[3, 4]
|
||||
]
|
||||
<i>Formulae</i>
|
||||
C2 =SUM(A2:B2)
|
||||
C3 =A3+B3
|
||||
<i>Array Formulae</i>
|
||||
A4:B4 {=A2:B2*A3:B3}
|
||||
C4 {=SUM(A2:A3*B2:B3)}
|
||||
|
||||
`;
|
||||
|
||||
return ( <pre>
|
||||
<b>Original worksheet</b>
|
||||
<div dangerouslySetInnerHTML={{__html}}/>
|
||||
<b>XLSX.utils.sheet_to_formulae(ws).join("\n")</b><br/>
|
||||
<br/>{XLSX.utils.sheet_to_formulae(ws).join("\n")}
|
||||
</pre> );
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
@ -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
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 204 KiB After Width: | Height: | Size: 275 KiB |
BIN
docz/static/files/multitable.png
Normal file
BIN
docz/static/files/multitable.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
@ -1,534 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 3.0.0 (20220226.1711)
|
||||
-->
|
||||
<!-- Title: G Pages: 1 -->
|
||||
<svg width="836pt" height="816pt"
|
||||
viewBox="0.00 0.00 835.92 815.55" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 811.55)">
|
||||
<title>G</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-811.55 831.92,-811.55 831.92,4 -4,4"/>
|
||||
<!-- csf -->
|
||||
<g id="node1" class="node">
|
||||
<title>csf</title>
|
||||
<ellipse fill="none" stroke="black" cx="413.33" cy="-403.68" rx="65.54" ry="65.54"/>
|
||||
<ellipse fill="none" stroke="black" cx="413.33" cy="-403.68" rx="69.52" ry="69.52"/>
|
||||
<text text-anchor="middle" x="413.33" y="-422.48" font-family="Indie Flower" font-size="14.00">Common</text>
|
||||
<text text-anchor="middle" x="413.33" y="-407.48" font-family="Indie Flower" font-size="14.00">Spreadsheet</text>
|
||||
<text text-anchor="middle" x="413.33" y="-392.48" font-family="Indie Flower" font-size="14.00">Format</text>
|
||||
<text text-anchor="middle" x="413.33" y="-377.48" font-family="Indie Flower" font-size="14.00">(JS Object)</text>
|
||||
</g>
|
||||
<!-- xls2 -->
|
||||
<g id="node2" class="node">
|
||||
<title>xls2</title>
|
||||
<ellipse fill="#00ff00" stroke="#00ff00" cx="790.45" cy="-403.68" rx="37.45" ry="26.74"/>
|
||||
<text text-anchor="middle" x="790.45" y="-407.48" font-family="Indie Flower" font-size="14.00">XLS</text>
|
||||
<text text-anchor="middle" x="790.45" y="-392.48" font-family="Indie Flower" font-size="14.00">BIFF2</text>
|
||||
</g>
|
||||
<!-- csf->xls2 -->
|
||||
<g id="edge25" class="edge">
|
||||
<title>csf->xls2</title>
|
||||
<path fill="none" stroke="#458b74" d="M482.94,-409.06C558.69,-411.11 677.75,-410.86 743.28,-408.33"/>
|
||||
<polygon fill="#458b74" stroke="#458b74" points="743.48,-411.83 753.32,-407.91 743.18,-404.83 743.48,-411.83"/>
|
||||
</g>
|
||||
<!-- xls3 -->
|
||||
<g id="node3" class="node">
|
||||
<title>xls3</title>
|
||||
<ellipse fill="#00ff00" stroke="#00ff00" cx="781.53" cy="-485.19" rx="37.45" ry="26.74"/>
|
||||
<text text-anchor="middle" x="781.53" y="-488.99" font-family="Indie Flower" font-size="14.00">XLS</text>
|
||||
<text text-anchor="middle" x="781.53" y="-473.99" font-family="Indie Flower" font-size="14.00">BIFF3</text>
|
||||
</g>
|
||||
<!-- csf->xls3 -->
|
||||
<g id="edge27" class="edge">
|
||||
<title>csf->xls3</title>
|
||||
<path fill="none" stroke="#458b74" d="M480.13,-423.98C553.65,-442.35 669.94,-467.84 734.47,-479.53"/>
|
||||
<polygon fill="#458b74" stroke="#458b74" points="733.91,-482.99 744.37,-481.29 735.14,-476.1 733.91,-482.99"/>
|
||||
</g>
|
||||
<!-- xls4 -->
|
||||
<g id="node4" class="node">
|
||||
<title>xls4</title>
|
||||
<ellipse fill="#00ff00" stroke="#00ff00" cx="755.21" cy="-562.84" rx="37.45" ry="26.74"/>
|
||||
<text text-anchor="middle" x="755.21" y="-566.64" font-family="Indie Flower" font-size="14.00">XLS</text>
|
||||
<text text-anchor="middle" x="755.21" y="-551.64" font-family="Indie Flower" font-size="14.00">BIFF4</text>
|
||||
</g>
|
||||
<!-- csf->xls4 -->
|
||||
<g id="edge29" class="edge">
|
||||
<title>csf->xls4</title>
|
||||
<path fill="none" stroke="#458b74" d="M474.16,-437.93C542.38,-471.96 651.29,-522.38 711.56,-547.6"/>
|
||||
<polygon fill="#458b74" stroke="#458b74" points="710.58,-550.98 721.16,-551.57 713.25,-544.51 710.58,-550.98"/>
|
||||
</g>
|
||||
<!-- xls5 -->
|
||||
<g id="node5" class="node">
|
||||
<title>xls5</title>
|
||||
<ellipse fill="#00ff00" stroke="#00ff00" cx="712.74" cy="-632.97" rx="37.45" ry="26.74"/>
|
||||
<text text-anchor="middle" x="712.74" y="-636.77" font-family="Indie Flower" font-size="14.00">XLS</text>
|
||||
<text text-anchor="middle" x="712.74" y="-621.77" font-family="Indie Flower" font-size="14.00">BIFF5</text>
|
||||
</g>
|
||||
<!-- csf->xls5 -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>csf->xls5</title>
|
||||
<path fill="none" stroke="blue" d="M465.32,-450.27C525.2,-498.75 622.04,-572.55 675.09,-609.83"/>
|
||||
<polygon fill="blue" stroke="blue" points="673.32,-612.87 683.53,-615.71 677.32,-607.12 673.32,-612.87"/>
|
||||
</g>
|
||||
<!-- xls8 -->
|
||||
<g id="node6" class="node">
|
||||
<title>xls8</title>
|
||||
<ellipse fill="#00ff00" stroke="#00ff00" cx="656.1" cy="-692.26" rx="37.45" ry="26.74"/>
|
||||
<text text-anchor="middle" x="656.1" y="-696.06" font-family="Indie Flower" font-size="14.00">XLS</text>
|
||||
<text text-anchor="middle" x="656.1" y="-681.06" font-family="Indie Flower" font-size="14.00">BIFF8</text>
|
||||
</g>
|
||||
<!-- csf->xls8 -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>csf->xls8</title>
|
||||
<path fill="none" stroke="blue" d="M454.03,-460.41C502.64,-521.47 582.67,-616.11 626.05,-663.39"/>
|
||||
<polygon fill="blue" stroke="blue" points="623.57,-665.87 632.94,-670.83 628.71,-661.12 623.57,-665.87"/>
|
||||
</g>
|
||||
<!-- xlml -->
|
||||
<g id="node7" class="node">
|
||||
<title>xlml</title>
|
||||
<ellipse fill="#00ff00" stroke="#00ff00" cx="579.16" cy="-742.38" rx="47.25" ry="26.74"/>
|
||||
<text text-anchor="middle" x="579.16" y="-746.18" font-family="Indie Flower" font-size="14.00">SSML</text>
|
||||
<text text-anchor="middle" x="579.16" y="-731.18" font-family="Indie Flower" font-size="14.00">(2003/4)</text>
|
||||
</g>
|
||||
<!-- csf->xlml -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>csf->xlml</title>
|
||||
<path fill="none" stroke="blue" d="M439.11,-468.56C471.78,-540.11 527.17,-652.46 557.68,-708.4"/>
|
||||
<polygon fill="blue" stroke="blue" points="554.64,-710.14 562.54,-717.2 560.77,-706.76 554.64,-710.14"/>
|
||||
</g>
|
||||
<!-- xlsx -->
|
||||
<g id="node8" class="node">
|
||||
<title>xlsx</title>
|
||||
<ellipse fill="#00ff00" stroke="#00ff00" cx="491.7" cy="-772.57" rx="38.37" ry="26.74"/>
|
||||
<text text-anchor="middle" x="491.7" y="-776.37" font-family="Indie Flower" font-size="14.00">XLSX</text>
|
||||
<text text-anchor="middle" x="491.7" y="-761.37" font-family="Indie Flower" font-size="14.00">XLSM</text>
|
||||
</g>
|
||||
<!-- csf->xlsx -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>csf->xlsx</title>
|
||||
<path fill="none" stroke="blue" d="M422.54,-472.89C436.95,-551.07 463.73,-675.33 479.65,-736.35"/>
|
||||
<polygon fill="blue" stroke="blue" points="476.34,-737.52 482.3,-746.28 483.1,-735.72 476.34,-737.52"/>
|
||||
</g>
|
||||
<!-- xlsb -->
|
||||
<g id="node9" class="node">
|
||||
<title>xlsb</title>
|
||||
<ellipse fill="#00ff00" stroke="#00ff00" cx="403.76" cy="-780.68" rx="43.27" ry="26.74"/>
|
||||
<text text-anchor="middle" x="403.76" y="-784.48" font-family="Indie Flower" font-size="14.00">XLSB</text>
|
||||
<text text-anchor="middle" x="403.76" y="-769.48" font-family="Indie Flower" font-size="14.00">BIFF12</text>
|
||||
</g>
|
||||
<!-- csf->xlsb -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>csf->xlsb</title>
|
||||
<path fill="none" stroke="blue" d="M406.19,-473.13C402,-552.92 399.14,-680.89 400.48,-743.6"/>
|
||||
<polygon fill="blue" stroke="blue" points="396.99,-743.91 400.76,-753.81 403.99,-743.72 396.99,-743.91"/>
|
||||
</g>
|
||||
<!-- nums -->
|
||||
<g id="node10" class="node">
|
||||
<title>nums</title>
|
||||
<ellipse fill="cyan" stroke="cyan" cx="299.96" cy="-763.36" rx="55.49" ry="18"/>
|
||||
<text text-anchor="middle" x="299.96" y="-759.66" font-family="Indie Flower" font-size="14.00">NUMBERS</text>
|
||||
</g>
|
||||
<!-- csf->nums -->
|
||||
<g id="edge23" class="edge">
|
||||
<title>csf->nums</title>
|
||||
<path fill="none" stroke="blue" d="M387.28,-468.45C359.87,-547.84 319.17,-678.77 304.78,-735.61"/>
|
||||
<polygon fill="blue" stroke="blue" points="301.37,-734.79 302.4,-745.33 308.18,-736.45 301.37,-734.79"/>
|
||||
</g>
|
||||
<!-- ods -->
|
||||
<g id="node11" class="node">
|
||||
<title>ods</title>
|
||||
<ellipse fill="cyan" stroke="cyan" cx="215.6" cy="-724.8" rx="30.59" ry="18"/>
|
||||
<text text-anchor="middle" x="215.6" y="-721.1" font-family="Indie Flower" font-size="14.00">ODS</text>
|
||||
</g>
|
||||
<!-- csf->ods -->
|
||||
<g id="edge13" class="edge">
|
||||
<title>csf->ods</title>
|
||||
<path fill="none" stroke="blue" d="M372.25,-460.14C326.52,-530.09 255.55,-646.35 227.44,-698.33"/>
|
||||
<polygon fill="blue" stroke="blue" points="224.3,-696.79 222.7,-707.26 230.48,-700.07 224.3,-696.79"/>
|
||||
</g>
|
||||
<!-- fods -->
|
||||
<g id="node12" class="node">
|
||||
<title>fods</title>
|
||||
<ellipse fill="cyan" stroke="cyan" cx="157.15" cy="-680.43" rx="36" ry="18"/>
|
||||
<text text-anchor="middle" x="157.15" y="-676.73" font-family="Indie Flower" font-size="14.00">FODS</text>
|
||||
</g>
|
||||
<!-- csf->fods -->
|
||||
<g id="edge15" class="edge">
|
||||
<title>csf->fods</title>
|
||||
<path fill="none" stroke="blue" d="M362.1,-451.11C304.39,-510.17 213.75,-608.81 175.28,-655.09"/>
|
||||
<polygon fill="blue" stroke="blue" points="172.34,-653.15 168.72,-663.1 177.76,-657.58 172.34,-653.15"/>
|
||||
</g>
|
||||
<!-- html -->
|
||||
<g id="node14" class="node">
|
||||
<title>html</title>
|
||||
<ellipse fill="cyan" stroke="cyan" cx="103.98" cy="-619.37" rx="38.37" ry="26.74"/>
|
||||
<text text-anchor="middle" x="103.98" y="-623.17" font-family="Indie Flower" font-size="14.00">HTML</text>
|
||||
<text text-anchor="middle" x="103.98" y="-608.17" font-family="Indie Flower" font-size="14.00">Table</text>
|
||||
</g>
|
||||
<!-- csf->html -->
|
||||
<g id="edge50" class="edge">
|
||||
<title>csf->html</title>
|
||||
<path fill="none" stroke="#458b74" d="M353.15,-439.08C288.12,-481.86 186.98,-552.76 135.84,-591.74"/>
|
||||
<polygon fill="#458b74" stroke="#458b74" points="133.57,-589.07 127.78,-597.94 137.84,-594.62 133.57,-589.07"/>
|
||||
</g>
|
||||
<!-- csv -->
|
||||
<g id="node15" class="node">
|
||||
<title>csv</title>
|
||||
<ellipse fill="cyan" stroke="cyan" cx="67.87" cy="-554.93" rx="28.7" ry="18"/>
|
||||
<text text-anchor="middle" x="67.87" y="-551.23" font-family="Indie Flower" font-size="14.00">CSV</text>
|
||||
</g>
|
||||
<!-- csf->csv -->
|
||||
<g id="edge44" class="edge">
|
||||
<title>csf->csv</title>
|
||||
<path fill="none" stroke="#458b74" d="M347.41,-426.68C272.23,-457.2 152.63,-510.03 97.48,-537.51"/>
|
||||
<polygon fill="#458b74" stroke="#458b74" points="95.61,-534.54 88.27,-542.18 98.77,-540.79 95.61,-534.54"/>
|
||||
</g>
|
||||
<!-- txt -->
|
||||
<g id="node16" class="node">
|
||||
<title>txt</title>
|
||||
<ellipse fill="cyan" stroke="cyan" cx="43.84" cy="-479.16" rx="43.68" ry="26.74"/>
|
||||
<text text-anchor="middle" x="43.84" y="-482.96" font-family="Indie Flower" font-size="14.00">TXT</text>
|
||||
<text text-anchor="middle" x="43.84" y="-467.96" font-family="Indie Flower" font-size="14.00">UTF-16</text>
|
||||
</g>
|
||||
<!-- csf->txt -->
|
||||
<g id="edge46" class="edge">
|
||||
<title>csf->txt</title>
|
||||
<path fill="none" stroke="#458b74" d="M344.05,-412.35C270.81,-425.26 157.16,-448.67 92.75,-464.28"/>
|
||||
<polygon fill="#458b74" stroke="#458b74" points="91.72,-460.93 82.84,-466.72 93.39,-467.73 91.72,-460.93"/>
|
||||
</g>
|
||||
<!-- dbf -->
|
||||
<g id="node17" class="node">
|
||||
<title>dbf</title>
|
||||
<ellipse fill="cyan" stroke="cyan" cx="36.23" cy="-400.04" rx="28.7" ry="18"/>
|
||||
<text text-anchor="middle" x="36.23" y="-396.34" font-family="Indie Flower" font-size="14.00">DBF</text>
|
||||
</g>
|
||||
<!-- csf->dbf -->
|
||||
<g id="edge48" class="edge">
|
||||
<title>csf->dbf</title>
|
||||
<path fill="none" stroke="#458b74" d="M343.77,-397.63C264.53,-394.72 137.86,-393.86 74.68,-396.12"/>
|
||||
<polygon fill="#458b74" stroke="#458b74" points="74.22,-392.64 64.37,-396.55 74.51,-399.63 74.22,-392.64"/>
|
||||
</g>
|
||||
<!-- dif -->
|
||||
<g id="node18" class="node">
|
||||
<title>dif</title>
|
||||
<ellipse fill="cyan" stroke="cyan" cx="42.05" cy="-337.58" rx="27" ry="18"/>
|
||||
<text text-anchor="middle" x="42.05" y="-333.88" font-family="Indie Flower" font-size="14.00">DIF</text>
|
||||
</g>
|
||||
<!-- csf->dif -->
|
||||
<g id="edge32" class="edge">
|
||||
<title>csf->dif</title>
|
||||
<path fill="none" stroke="#458b74" d="M345.74,-386.19C267.48,-370.06 141.4,-348.01 79.2,-339.91"/>
|
||||
<polygon fill="#458b74" stroke="#458b74" points="79.42,-336.41 69.06,-338.65 78.55,-343.36 79.42,-336.41"/>
|
||||
</g>
|
||||
<!-- slk -->
|
||||
<g id="node19" class="node">
|
||||
<title>slk</title>
|
||||
<ellipse fill="cyan" stroke="cyan" cx="59.69" cy="-272.68" rx="33.29" ry="18"/>
|
||||
<text text-anchor="middle" x="59.69" y="-268.98" font-family="Indie Flower" font-size="14.00">SYLK</text>
|
||||
</g>
|
||||
<!-- csf->slk -->
|
||||
<g id="edge30" class="edge">
|
||||
<title>csf->slk</title>
|
||||
<path fill="none" stroke="#458b74" d="M349.92,-374.46C277.49,-345.38 161.16,-302.63 100.09,-282.93"/>
|
||||
<polygon fill="#458b74" stroke="#458b74" points="101.01,-279.55 90.42,-279.87 98.89,-286.23 101.01,-279.55"/>
|
||||
</g>
|
||||
<!-- prn -->
|
||||
<g id="node20" class="node">
|
||||
<title>prn</title>
|
||||
<ellipse fill="cyan" stroke="cyan" cx="89.74" cy="-210.01" rx="29.5" ry="18"/>
|
||||
<text text-anchor="middle" x="89.74" y="-206.31" font-family="Indie Flower" font-size="14.00">PRN</text>
|
||||
</g>
|
||||
<!-- csf->prn -->
|
||||
<g id="edge42" class="edge">
|
||||
<title>csf->prn</title>
|
||||
<path fill="none" stroke="#458b74" d="M356.36,-363.32C288.77,-320.34 178.35,-254.72 123.22,-225.16"/>
|
||||
<polygon fill="#458b74" stroke="#458b74" points="124.7,-221.99 114.22,-220.41 121.43,-228.18 124.7,-221.99"/>
|
||||
</g>
|
||||
<!-- rtf -->
|
||||
<g id="node21" class="node">
|
||||
<title>rtf</title>
|
||||
<ellipse fill="cyan" stroke="cyan" cx="127.35" cy="-157.84" rx="27.9" ry="18"/>
|
||||
<text text-anchor="middle" x="127.35" y="-154.14" font-family="Indie Flower" font-size="14.00">RTF</text>
|
||||
</g>
|
||||
<!-- csf->rtf -->
|
||||
<g id="edge40" class="edge">
|
||||
<title>csf->rtf</title>
|
||||
<path fill="none" stroke="#458b74" d="M360.21,-358.02C298.04,-304.57 197.75,-218.36 151.79,-178.85"/>
|
||||
<polygon fill="#458b74" stroke="#458b74" points="153.98,-176.12 144.12,-172.25 149.42,-181.43 153.98,-176.12"/>
|
||||
</g>
|
||||
<!-- wk1 -->
|
||||
<g id="node22" class="node">
|
||||
<title>wk1</title>
|
||||
<ellipse fill="cyan" stroke="cyan" cx="174.29" cy="-112" rx="30.59" ry="18"/>
|
||||
<text text-anchor="middle" x="174.29" y="-108.3" font-family="Indie Flower" font-size="14.00">WK1</text>
|
||||
</g>
|
||||
<!-- csf->wk1 -->
|
||||
<g id="edge34" class="edge">
|
||||
<title>csf->wk1</title>
|
||||
<path fill="none" stroke="#458b74" d="M373.36,-346.44C323.28,-281.83 239.2,-179.96 198.15,-134.79"/>
|
||||
<polygon fill="#458b74" stroke="#458b74" points="200.59,-132.27 191.24,-127.28 195.44,-137.01 200.59,-132.27"/>
|
||||
</g>
|
||||
<!-- wk3 -->
|
||||
<g id="node24" class="node">
|
||||
<title>wk3</title>
|
||||