This commit is contained in:
SheetJS 2022-07-07 00:05:14 -04:00
parent 8f216aa501
commit 0847c803f2
16 changed files with 1099 additions and 1232 deletions

@ -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>
);
}
```

@ -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

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&#45;&gt;xls2 -->
<g id="edge25" class="edge">
<title>csf&#45;&gt;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&#45;&gt;xls3 -->
<g id="edge27" class="edge">
<title>csf&#45;&gt;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&#45;&gt;xls4 -->
<g id="edge29" class="edge">
<title>csf&#45;&gt;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&#45;&gt;xls5 -->
<g id="edge7" class="edge">
<title>csf&#45;&gt;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&#45;&gt;xls8 -->
<g id="edge9" class="edge">
<title>csf&#45;&gt;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&#45;&gt;xlml -->
<g id="edge5" class="edge">
<title>csf&#45;&gt;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&#45;&gt;xlsx -->
<g id="edge1" class="edge">
<title>csf&#45;&gt;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&#45;&gt;xlsb -->
<g id="edge3" class="edge">
<title>csf&#45;&gt;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&#45;&gt;nums -->
<g id="edge23" class="edge">
<title>csf&#45;&gt;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&#45;&gt;ods -->
<g id="edge13" class="edge">
<title>csf&#45;&gt;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&#45;&gt;fods -->
<g id="edge15" class="edge">
<title>csf&#45;&gt;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&#45;&gt;html -->
<g id="edge50" class="edge">
<title>csf&#45;&gt;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&#45;&gt;csv -->
<g id="edge44" class="edge">
<title>csf&#45;&gt;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&#45;16</text>
</g>
<!-- csf&#45;&gt;txt -->
<g id="edge46" class="edge">
<title>csf&#45;&gt;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&#45;&gt;dbf -->
<g id="edge48" class="edge">
<title>csf&#45;&gt;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&#45;&gt;dif -->
<g id="edge32" class="edge">
<title>csf&#45;&gt;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&#45;&gt;slk -->
<g id="edge30" class="edge">
<title>csf&#45;&gt;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&#45;&gt;prn -->
<g id="edge42" class="edge">
<title>csf&#45;&gt;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&#45;&gt;rtf -->
<g id="edge40" class="edge">
<title>csf&#45;&gt;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&#45;&gt;wk1 -->
<g id="edge34" class="edge">
<title>csf&#45;&gt;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>
<ellipse fill="cyan" stroke="cyan" cx="230.73" cy="-73.72" rx="30.59" ry="18"/>
<text text-anchor="middle" x="230.73" y="-70.02" font-family="Indie Flower" font-size="14.00">WK3</text>
</g>
<!-- csf&#45;&gt;wk3 -->
<g id="edge18" class="edge">
<title>csf&#45;&gt;wk3</title>
<path fill="none" stroke="blue" d="M384.33,-340.17C346.31,-266.87 281.03,-149.9 249.12,-98.87"/>
<polygon fill="blue" stroke="blue" points="251.89,-96.69 243.57,-90.13 245.98,-100.45 251.89,-96.69"/>
</g>
<!-- eth -->
<g id="node32" class="node">
<title>eth</title>
<ellipse fill="cyan" stroke="cyan" cx="291.3" cy="-46.85" rx="28.7" ry="18"/>
<text text-anchor="middle" x="291.3" y="-43.15" font-family="Indie Flower" font-size="14.00">ETH</text>
</g>
<!-- csf&#45;&gt;eth -->
<g id="edge51" class="edge">
<title>csf&#45;&gt;eth</title>
<path fill="none" stroke="#458b74" d="M395.89,-336.08C371.11,-256.68 326.85,-128.82 304.52,-73.62"/>
<polygon fill="#458b74" stroke="#458b74" points="307.67,-72.09 300.62,-64.19 301.21,-74.77 307.67,-72.09"/>
</g>
<!-- xls2&#45;&gt;csf -->
<g id="edge24" class="edge">
<title>xls2&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M753.25,-399.45C693.11,-396.65 573.17,-396.18 493.07,-398.05"/>
<polygon fill="#458b74" stroke="#458b74" points="492.92,-394.55 483.01,-398.3 493.1,-401.55 492.92,-394.55"/>
</g>
<!-- xls3&#45;&gt;csf -->
<g id="edge26" class="edge">
<title>xls3&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M747.79,-473.47C690.3,-457.8 571.89,-431.09 492.53,-415.44"/>
<polygon fill="#458b74" stroke="#458b74" points="493.05,-411.98 482.57,-413.5 491.71,-418.85 493.05,-411.98"/>
</g>
<!-- xls4&#45;&gt;csf -->
<g id="edge28" class="edge">
<title>xls4&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M726.87,-545.2C675,-517.77 563.3,-465.18 488.2,-432.32"/>
<polygon fill="#458b74" stroke="#458b74" points="489.34,-429 478.77,-428.22 486.55,-435.42 489.34,-429"/>
</g>
<!-- xls5&#45;&gt;csf -->
<g id="edge8" class="edge">
<title>xls5&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M690.61,-611.1C646.74,-573.63 547.39,-496.82 480.31,-447.87"/>
<polygon fill="blue" stroke="blue" points="482.03,-444.8 471.88,-441.75 477.91,-450.46 482.03,-444.8"/>
</g>
<!-- xls8&#45;&gt;csf -->
<g id="edge10" class="edge">
<title>xls8&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M640.37,-667.64C606.25,-622.16 524.54,-524.1 469.01,-461.12"/>
<polygon fill="blue" stroke="blue" points="471.62,-458.79 462.37,-453.62 466.38,-463.43 471.62,-458.79"/>
</g>
<!-- xlml&#45;&gt;csf -->
<g id="edge6" class="edge">
<title>xlml&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M570.33,-715.83C548.54,-663.98 492.39,-547.88 453.44,-472.8"/>
<polygon fill="blue" stroke="blue" points="456.5,-471.11 448.78,-463.86 450.3,-474.34 456.5,-471.11"/>
</g>
<!-- xlsx&#45;&gt;csf -->
<g id="edge2" class="edge">
<title>xlsx&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M489.73,-745.57C481.38,-690.48 455.03,-563.36 435.43,-480.6"/>
<polygon fill="blue" stroke="blue" points="438.8,-479.65 433.07,-470.74 431.99,-481.28 438.8,-479.65"/>
</g>
<!-- xlsb&#45;&gt;csf -->
<g id="edge4" class="edge">
<title>xlsb&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M408.15,-753.66C412.83,-697.93 416.76,-568.36 416.93,-483.47"/>
<polygon fill="blue" stroke="blue" points="420.43,-483.35 416.94,-473.35 413.43,-483.35 420.43,-483.35"/>
</g>
<!-- nums&#45;&gt;csf -->
<g id="edge22" class="edge">
<title>nums&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M308.97,-745.49C327.76,-698.09 369.92,-567 394.67,-481.62"/>
<polygon fill="blue" stroke="blue" points="398.05,-482.54 397.45,-471.96 391.32,-480.61 398.05,-482.54"/>
</g>
<!-- ods&#45;&gt;csf -->
<g id="edge12" class="edge">
<title>ods&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M229.64,-708.37C259.96,-665.94 331.65,-550.97 376.04,-475.01"/>
<polygon fill="blue" stroke="blue" points="379.31,-476.35 381.31,-465.95 373.25,-472.83 379.31,-476.35"/>
</g>
<!-- fods&#45;&gt;csf -->
<g id="edge14" class="edge">
<title>fods&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M176.15,-665.09C215.32,-627.8 305.77,-531.13 363,-466.36"/>
<polygon fill="blue" stroke="blue" points="365.83,-468.44 369.81,-458.62 360.57,-463.82 365.83,-468.44"/>
</g>
<!-- uos -->
<g id="node13" class="node">
<title>uos</title>
<ellipse fill="cyan" stroke="cyan" cx="355.64" cy="-31" rx="30.59" ry="18"/>
<text text-anchor="middle" x="355.64" y="-27.3" font-family="Indie Flower" font-size="14.00">UOS</text>
</g>
<!-- uos&#45;&gt;csf -->
<g id="edge16" class="edge">
<title>uos&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M358.42,-48.98C366,-97.91 387.33,-235.76 401.07,-324.5"/>
<polygon fill="blue" stroke="blue" points="397.63,-325.18 402.62,-334.53 404.55,-324.11 397.63,-325.18"/>
</g>
<!-- html&#45;&gt;csf -->
<g id="edge49" class="edge">
<title>html&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M134.59,-603.02C184.75,-571.6 285.28,-502.14 350.91,-454.07"/>
<polygon fill="#458b74" stroke="#458b74" points="353.15,-456.77 359.14,-448.02 349.01,-451.13 353.15,-456.77"/>
</g>
<!-- csv&#45;&gt;csf -->
<g id="edge43" class="edge">
<title>csv&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M94.2,-547.45C146.65,-528.05 265.66,-476.64 342.6,-440.79"/>
<polygon fill="#458b74" stroke="#458b74" points="344.2,-443.91 351.77,-436.5 341.23,-437.57 344.2,-443.91"/>
</g>
<!-- txt&#45;&gt;csf -->
<g id="edge45" class="edge">
<title>txt&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M87.49,-474.85C148.63,-464.96 260.72,-442.44 336.13,-425.2"/>
<polygon fill="#458b74" stroke="#458b74" points="337.1,-428.57 346.05,-422.92 335.52,-421.75 337.1,-428.57"/>
</g>
<!-- dbf&#45;&gt;csf -->
<g id="edge47" class="edge">
<title>dbf&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M64.23,-404.06C120.46,-407.82 248.93,-409.69 333.36,-408.54"/>
<polygon fill="#458b74" stroke="#458b74" points="333.48,-412.04 343.43,-408.39 333.37,-405.04 333.48,-412.04"/>
</g>
<!-- dif&#45;&gt;csf -->
<g id="edge39" class="edge">
<title>dif&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M66.36,-345.56C119.67,-358.46 248.86,-382.15 333.67,-395.22"/>
<polygon fill="#458b74" stroke="#458b74" points="333.37,-398.72 343.78,-396.76 334.43,-391.8 333.37,-398.72"/>
</g>
<!-- slk&#45;&gt;csf -->
<g id="edge31" class="edge">
<title>slk&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M83.29,-285.33C134.11,-307.67 255.79,-353.44 336.35,-381.18"/>
<polygon fill="#458b74" stroke="#458b74" points="335.37,-384.54 345.96,-384.47 337.64,-377.92 335.37,-384.54"/>
</g>
<!-- prn&#45;&gt;csf -->
<g id="edge41" class="edge">
<title>prn&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M107.26,-224.48C150.82,-254.64 265.79,-324.31 341.77,-367.42"/>
<polygon fill="#458b74" stroke="#458b74" points="340.4,-370.67 350.83,-372.54 343.85,-364.58 340.4,-370.67"/>
</g>
<!-- wk1&#45;&gt;csf -->
<g id="edge33" class="edge">
<title>wk1&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M184.31,-129.31C213.71,-170.98 300.37,-277.99 358.3,-345.42"/>
<polygon fill="#458b74" stroke="#458b74" points="355.68,-347.74 364.86,-353.03 360.98,-343.17 355.68,-347.74"/>
</g>
<!-- wksl -->
<g id="node23" class="node">
<title>wksl</title>
<ellipse fill="cyan" stroke="cyan" cx="428.51" cy="-26.87" rx="35.21" ry="26.74"/>
<text text-anchor="middle" x="428.51" y="-30.67" font-family="Indie Flower" font-size="14.00">WKS</text>
<text text-anchor="middle" x="428.51" y="-15.67" font-family="Indie Flower" font-size="14.00">Lotus</text>
</g>
<!-- wksl&#45;&gt;csf -->
<g id="edge37" class="edge">
<title>wksl&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M427.42,-53.97C425.17,-109.67 419.97,-238.9 416.55,-323.7"/>
<polygon fill="#458b74" stroke="#458b74" points="413.05,-323.68 416.14,-333.82 420.04,-323.97 413.05,-323.68"/>
</g>
<!-- wk3&#45;&gt;csf -->
<g id="edge17" class="edge">
<title>wk3&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M237.03,-91.6C257.98,-137.18 324.61,-259.29 369.76,-336.57"/>
<polygon fill="blue" stroke="blue" points="366.8,-338.45 374.88,-345.3 372.83,-334.91 366.8,-338.45"/>
</g>
<!-- wk4 -->
<g id="node25" class="node">
<title>wk4</title>
<ellipse fill="cyan" stroke="cyan" cx="500.81" cy="-36.85" rx="30.59" ry="18"/>
<text text-anchor="middle" x="500.81" y="-33.15" font-family="Indie Flower" font-size="14.00">WK4</text>
</g>
<!-- wk4&#45;&gt;csf -->
<g id="edge19" class="edge">
<title>wk4&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M496.53,-54.79C484.99,-103.2 452.67,-238.7 431.88,-325.89"/>
<polygon fill="blue" stroke="blue" points="428.44,-325.21 429.53,-335.75 435.25,-326.83 428.44,-325.21"/>
</g>
<!-- 123 -->
<g id="node26" class="node">
<title>123</title>
<ellipse fill="cyan" stroke="cyan" cx="562.2" cy="-57.19" rx="27" ry="18"/>
<text text-anchor="middle" x="562.2" y="-53.49" font-family="Indie Flower" font-size="14.00">123</text>
</g>
<!-- 123&#45;&gt;csf -->
<g id="edge20" class="edge">
<title>123&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M554.61,-74.85C534.7,-121.19 480.06,-248.36 444.86,-330.29"/>
<polygon fill="blue" stroke="blue" points="441.61,-328.98 440.88,-339.55 448.04,-331.75 441.61,-328.98"/>
</g>
<!-- wksm -->
<g id="node27" class="node">
<title>wksm</title>
<ellipse fill="cyan" stroke="cyan" cx="626.15" cy="-92.35" rx="38.78" ry="26.74"/>
<text text-anchor="middle" x="626.15" y="-96.15" font-family="Indie Flower" font-size="14.00">WKS</text>
<text text-anchor="middle" x="626.15" y="-81.15" font-family="Indie Flower" font-size="14.00">Works</text>
</g>
<!-- wksm&#45;&gt;csf -->
<g id="edge38" class="edge">
<title>wksm&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M609.45,-116.78C577.31,-163.8 505.87,-268.3 458.67,-337.35"/>
<polygon fill="#458b74" stroke="#458b74" points="455.5,-335.78 452.75,-346.01 461.28,-339.73 455.5,-335.78"/>
</g>
<!-- xlr -->
<g id="node28" class="node">
<title>xlr</title>
<ellipse fill="cyan" stroke="cyan" cx="682.79" cy="-139.85" rx="27.9" ry="18"/>
<text text-anchor="middle" x="682.79" y="-136.15" font-family="Indie Flower" font-size="14.00">XLR</text>
</g>
<!-- xlr&#45;&gt;csf -->
<g id="edge35" class="edge">
<title>xlr&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M667.14,-155.17C629.68,-191.84 533.36,-286.15 470.74,-347.47"/>
<polygon fill="#458b74" stroke="#458b74" points="467.97,-345.28 463.27,-354.78 472.87,-350.28 467.97,-345.28"/>
</g>
<!-- wq1 -->
<g id="node29" class="node">
<title>wq1</title>
<ellipse fill="cyan" stroke="cyan" cx="725.17" cy="-191.62" rx="31.7" ry="18"/>
<text text-anchor="middle" x="725.17" y="-187.92" font-family="Indie Flower" font-size="14.00">WQ1</text>
</g>
<!-- wq1&#45;&gt;csf -->
<g id="edge36" class="edge">
<title>wq1&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M704.71,-205.53C659.79,-236.08 550.87,-310.15 479.67,-358.56"/>
<polygon fill="#458b74" stroke="#458b74" points="477.48,-355.82 471.18,-364.34 481.42,-361.61 477.48,-355.82"/>
</g>
<!-- wq2 -->
<g id="node30" class="node">
<title>wq2</title>
<ellipse fill="cyan" stroke="cyan" cx="760.41" cy="-256.21" rx="34.8" ry="26.74"/>
<text text-anchor="middle" x="760.41" y="-260.01" font-family="Indie Flower" font-size="14.00">WQ2</text>
<text text-anchor="middle" x="760.41" y="-245.01" font-family="Indie Flower" font-size="14.00">WB*</text>
</g>
<!-- wq2&#45;&gt;csf -->
<g id="edge11" class="edge">
<title>wq2&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M729.92,-269.16C676.06,-292.05 562.68,-340.22 487.26,-372.27"/>
<polygon fill="blue" stroke="blue" points="485.63,-369.16 477.8,-376.29 488.37,-375.6 485.63,-369.16"/>
</g>
<!-- qpw -->
<g id="node31" class="node">
<title>qpw</title>
<ellipse fill="cyan" stroke="cyan" cx="782.58" cy="-327.05" rx="32.49" ry="18"/>
<text text-anchor="middle" x="782.58" y="-323.35" font-family="Indie Flower" font-size="14.00">QPW</text>
</g>
<!-- qpw&#45;&gt;csf -->
<g id="edge21" class="edge">
<title>qpw&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M752.05,-333.38C695.54,-345.11 573.04,-370.53 491.9,-387.38"/>
<polygon fill="blue" stroke="blue" points="490.8,-384.03 481.72,-389.49 492.22,-390.88 490.8,-384.03"/>
</g>
<!-- eth&#45;&gt;csf -->
<g id="edge52" class="edge">
<title>eth&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M294.25,-65.07C306.72,-113.21 351.17,-245.78 382.2,-330.05"/>
<polygon fill="#458b74" stroke="#458b74" points="378.97,-331.41 385.72,-339.57 385.53,-328.98 378.97,-331.41"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

After

Width:  |  Height:  |  Size: 275 KiB

@ -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

@ -4,531 +4,537 @@
<!-- 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)">
<svg width="1056pt" height="1033pt"
viewBox="0.00 0.00 1055.79 1032.90" 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 1028.9)">
<title>G</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-811.55 831.92,-811.55 831.92,4 -4,4"/>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-1028.9 1051.79,-1028.9 1051.79,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>
<ellipse fill="none" stroke="black" cx="523.61" cy="-511.58" rx="94.52" ry="94.52"/>
<ellipse fill="none" stroke="black" cx="523.61" cy="-511.58" rx="98.51" ry="98.51"/>
<text text-anchor="middle" x="523.61" y="-541.98" font-family="Indie Flower" font-size="22.00">Common</text>
<text text-anchor="middle" x="523.61" y="-517.98" font-family="Indie Flower" font-size="22.00">Spreadsheet</text>
<text text-anchor="middle" x="523.61" y="-493.98" font-family="Indie Flower" font-size="22.00">Format</text>
<text text-anchor="middle" x="523.61" y="-469.98" font-family="Indie Flower" font-size="22.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>
<ellipse fill="#00ff00" stroke="#00ff00" cx="997.58" cy="-511.58" rx="50.41" ry="39.7"/>
<text text-anchor="middle" x="997.58" y="-517.98" font-family="Indie Flower" font-size="22.00">XLS</text>
<text text-anchor="middle" x="997.58" y="-493.98" font-family="Indie Flower" font-size="22.00">BIFF2</text>
</g>
<!-- csf&#45;&gt;xls2 -->
<g id="edge25" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="#458b74" d="M622.39,-517.19C717.42,-518.98 858.03,-518.67 937.37,-516.26"/>
<polygon fill="#458b74" stroke="#458b74" points="937.8,-519.75 947.68,-515.93 937.57,-512.76 937.8,-519.75"/>
</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>
<ellipse fill="#00ff00" stroke="#00ff00" cx="985.74" cy="-616.89" rx="48.58" ry="39.7"/>
<text text-anchor="middle" x="985.74" y="-623.29" font-family="Indie Flower" font-size="22.00">XLS</text>
<text text-anchor="middle" x="985.74" y="-599.29" font-family="Indie Flower" font-size="22.00">BIFF3</text>
</g>
<!-- csf&#45;&gt;xls3 -->
<g id="edge27" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="#458b74" d="M618.68,-539C711.44,-561.98 849.59,-593.14 927.26,-608.33"/>
<polygon fill="#458b74" stroke="#458b74" points="926.87,-611.81 937.35,-610.27 928.19,-604.94 926.87,-611.81"/>
</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>
<ellipse fill="#00ff00" stroke="#00ff00" cx="951.7" cy="-715.02" rx="48.17" ry="39.7"/>
<text text-anchor="middle" x="951.7" y="-721.42" font-family="Indie Flower" font-size="22.00">XLS</text>
<text text-anchor="middle" x="951.7" y="-697.42" font-family="Indie Flower" font-size="22.00">BIFF4</text>
</g>
<!-- csf&#45;&gt;xls4 -->
<g id="edge29" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="#458b74" d="M610.42,-559.05C696.65,-602.03 826.1,-663.17 898.26,-694.7"/>
<polygon fill="#458b74" stroke="#458b74" points="897.06,-697.99 907.63,-698.76 899.85,-691.57 897.06,-697.99"/>
</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>
<ellipse fill="#00ff00" stroke="#00ff00" cx="895.8" cy="-805.05" rx="50.82" ry="39.7"/>
<text text-anchor="middle" x="895.8" y="-811.45" font-family="Indie Flower" font-size="22.00">XLS</text>
<text text-anchor="middle" x="895.8" y="-787.45" font-family="Indie Flower" font-size="22.00">BIFF5</text>
</g>
<!-- csf&#45;&gt;xls5 -->
<g id="edge7" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="blue" d="M597.71,-577.15C672.23,-638.22 784.86,-726.59 848.21,-773.36"/>
<polygon fill="blue" stroke="blue" points="846.31,-776.31 856.44,-779.4 850.45,-770.66 846.31,-776.31"/>
</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>
<ellipse fill="#00ff00" stroke="#00ff00" cx="821.3" cy="-880.41" rx="48.17" ry="39.7"/>
<text text-anchor="middle" x="821.3" y="-886.81" font-family="Indie Flower" font-size="22.00">XLS</text>
<text text-anchor="middle" x="821.3" y="-862.81" font-family="Indie Flower" font-size="22.00">BIFF8</text>
</g>
<!-- csf&#45;&gt;xls8 -->
<g id="edge9" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="blue" d="M581.29,-591.97C641.33,-669.31 733.48,-782.86 784.4,-841.8"/>
<polygon fill="blue" stroke="blue" points="781.8,-844.14 791,-849.39 787.09,-839.55 781.8,-844.14"/>
</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>
<ellipse fill="#00ff00" stroke="#00ff00" cx="721.05" cy="-942.48" rx="62.87" ry="39.7"/>
<text text-anchor="middle" x="721.05" y="-948.88" font-family="Indie Flower" font-size="22.00">SSML</text>
<text text-anchor="middle" x="721.05" y="-924.88" font-family="Indie Flower" font-size="22.00">(2003/4)</text>
</g>
<!-- csf&#45;&gt;xlml -->
<g id="edge5" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="blue" d="M559.66,-603.72C598.81,-693.6 660.19,-826.63 694.96,-896.23"/>
<polygon fill="blue" stroke="blue" points="691.86,-897.85 699.48,-905.21 698.11,-894.7 691.86,-897.85"/>
</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>
<ellipse fill="#00ff00" stroke="#00ff00" cx="612.75" cy="-977.1" rx="43.68" ry="39.7"/>
<text text-anchor="middle" x="612.75" y="-983.5" font-family="Indie Flower" font-size="22.00">XLSX</text>
<text text-anchor="middle" x="612.75" y="-959.5" font-family="Indie Flower" font-size="22.00">XLSM</text>
</g>
<!-- csf&#45;&gt;xlsx -->
<g id="edge1" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="blue" d="M536.68,-609.66C553.49,-707.36 581.86,-853.31 599,-928.49"/>
<polygon fill="blue" stroke="blue" points="595.68,-929.66 601.34,-938.61 602.5,-928.08 595.68,-929.66"/>
</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>
<ellipse fill="#00ff00" stroke="#00ff00" cx="507.8" cy="-985.3" rx="54.39" ry="39.7"/>
<text text-anchor="middle" x="507.8" y="-991.7" font-family="Indie Flower" font-size="22.00">XLSB</text>
<text text-anchor="middle" x="507.8" y="-967.7" font-family="Indie Flower" font-size="22.00">BIFF12</text>
</g>
<!-- csf&#45;&gt;xlsb -->
<g id="edge3" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="blue" d="M514.71,-610.13C509.53,-709.25 504.98,-858.13 505.12,-935.15"/>
<polygon fill="blue" stroke="blue" points="501.62,-935.54 505.17,-945.53 508.62,-935.51 501.62,-935.54"/>
</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>
<ellipse fill="cyan" stroke="cyan" cx="371.79" cy="-960.59" rx="77.15" ry="22.76"/>
<text text-anchor="middle" x="371.79" y="-954.99" font-family="Indie Flower" font-size="22.00">NUMBERS</text>
</g>
<!-- csf&#45;&gt;nums -->
<g id="edge23" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="blue" d="M486.66,-603.37C450.44,-704.18 398.22,-860.67 378.95,-927.99"/>
<polygon fill="blue" stroke="blue" points="375.58,-927.06 376.25,-937.64 382.32,-928.95 375.58,-927.06"/>
</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>
<ellipse fill="cyan" stroke="cyan" cx="258.58" cy="-904.55" rx="42.35" ry="22.76"/>
<text text-anchor="middle" x="258.58" y="-898.95" font-family="Indie Flower" font-size="22.00">ODS</text>
</g>
<!-- csf&#45;&gt;ods -->
<g id="edge13" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="blue" d="M463.73,-590.35C402.62,-677.38 311.79,-813.17 274.9,-873.69"/>
<polygon fill="blue" stroke="blue" points="271.81,-872.02 269.65,-882.39 277.81,-875.63 271.81,-872.02"/>
</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>
<ellipse fill="cyan" stroke="cyan" cx="182.28" cy="-840.45" rx="50.41" ry="22.76"/>
<text text-anchor="middle" x="182.28" y="-834.85" font-family="Indie Flower" font-size="22.00">FODS</text>
</g>
<!-- csf&#45;&gt;fods -->
<g id="edge15" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="blue" d="M448.58,-576.09C371.6,-647.51 256.35,-759.37 206.55,-811.72"/>
<polygon fill="blue" stroke="blue" points="204,-809.32 199.69,-819 209.1,-814.12 204,-809.32"/>
</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>
<ellipse fill="cyan" stroke="cyan" cx="118.85" cy="-758.21" rx="46.84" ry="39.7"/>
<text text-anchor="middle" x="118.85" y="-764.61" font-family="Indie Flower" font-size="22.00">HTML</text>
<text text-anchor="middle" x="118.85" y="-740.61" font-family="Indie Flower" font-size="22.00">Table</text>
</g>
<!-- csf&#45;&gt;html -->
<g id="edge50" class="edge">
<g id="edge51" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="#458b74" d="M436.33,-558.2C352,-607.43 227.75,-683.58 162.5,-726.37"/>
<polygon fill="#458b74" stroke="#458b74" points="160.5,-723.5 154.08,-731.93 164.35,-729.34 160.5,-723.5"/>
</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>
<ellipse fill="cyan" stroke="cyan" cx="79.84" cy="-678.11" rx="35.21" ry="22.76"/>
<text text-anchor="middle" x="79.84" y="-672.51" font-family="Indie Flower" font-size="22.00">CSV</text>
</g>
<!-- csf&#45;&gt;csv -->
<g id="edge44" class="edge">
<g id="edge45" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="#458b74" d="M429.15,-541.04C331.87,-575.47 185.16,-631.09 117.03,-659.87"/>
<polygon fill="#458b74" stroke="#458b74" points="115.39,-656.77 107.57,-663.91 118.14,-663.2 115.39,-656.77"/>
</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&#45;16</text>
<ellipse fill="cyan" stroke="cyan" cx="55.15" cy="-583.75" rx="55.31" ry="39.7"/>
<text text-anchor="middle" x="55.15" y="-590.15" font-family="Indie Flower" font-size="22.00">TXT</text>
<text text-anchor="middle" x="55.15" y="-566.15" font-family="Indie Flower" font-size="22.00">UTF&#45;16</text>
</g>
<!-- csf&#45;&gt;txt -->
<g id="edge46" class="edge">
<g id="edge47" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="#458b74" d="M425.12,-521.09C332.47,-533.58 196.55,-554.79 117.84,-569.24"/>
<polygon fill="#458b74" stroke="#458b74" points="116.78,-565.88 107.59,-571.15 118.06,-572.76 116.78,-565.88"/>
</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>
<ellipse fill="cyan" stroke="cyan" cx="50.78" cy="-478.57" rx="43.27" ry="22.76"/>
<text text-anchor="middle" x="50.78" y="-472.97" font-family="Indie Flower" font-size="22.00">DBF</text>
</g>
<!-- csf&#45;&gt;dbf -->
<g id="edge48" class="edge">
<g id="edge49" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="#458b74" d="M425.45,-499.11C327.93,-490.46 182.02,-480.65 104.19,-477.83"/>
<polygon fill="#458b74" stroke="#458b74" points="104.24,-474.33 94.12,-477.5 104,-481.33 104.24,-474.33"/>
</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>
<ellipse fill="cyan" stroke="cyan" cx="64.07" cy="-395.46" rx="33.88" ry="22.76"/>
<text text-anchor="middle" x="64.07" y="-389.86" font-family="Indie Flower" font-size="22.00">DIF</text>
</g>
<!-- csf&#45;&gt;dif -->
<g id="edge32" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="#458b74" d="M429.2,-481.95C330.69,-455.07 180.59,-417.66 107.33,-402.16"/>
<polygon fill="#458b74" stroke="#458b74" points="108.02,-398.73 97.52,-400.12 106.6,-405.58 108.02,-398.73"/>
</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>
<ellipse fill="cyan" stroke="cyan" cx="90.7" cy="-318.6" rx="40.11" ry="22.76"/>
<text text-anchor="middle" x="90.7" y="-313" font-family="Indie Flower" font-size="22.00">SYLK</text>
</g>
<!-- csf&#45;&gt;slk -->
<g id="edge30" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="#458b74" d="M435.66,-466.25C344.49,-423.52 205.65,-362.13 135.13,-333.78"/>
<polygon fill="#458b74" stroke="#458b74" points="136.23,-330.45 125.64,-330 133.64,-336.95 136.23,-330.45"/>
</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>
<ellipse fill="cyan" stroke="cyan" cx="133.25" cy="-242.74" rx="39.7" ry="22.76"/>
<text text-anchor="middle" x="133.25" y="-237.14" font-family="Indie Flower" font-size="22.00">PRN</text>
</g>
<!-- csf&#45;&gt;prn -->
<g id="edge42" class="edge">
<g id="edge43" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="#458b74" d="M445.43,-450.94C362.4,-391.42 234.63,-304.03 171.38,-264.02"/>
<polygon fill="#458b74" stroke="#458b74" points="172.86,-260.81 162.53,-258.47 169.14,-266.74 172.86,-260.81"/>
</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>
<ellipse fill="cyan" stroke="cyan" cx="186.96" cy="-177.94" rx="37.45" ry="22.76"/>
<text text-anchor="middle" x="186.96" y="-172.34" font-family="Indie Flower" font-size="22.00">RTF</text>
</g>
<!-- csf&#45;&gt;rtf -->
<g id="edge40" class="edge">
<g id="edge41" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="#458b74" d="M457.39,-438.07C384.75,-363.31 271.41,-251.77 217.44,-202.62"/>
<polygon fill="#458b74" stroke="#458b74" points="219.68,-199.94 209.92,-195.84 214.99,-205.13 219.68,-199.94"/>
</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>
<ellipse fill="cyan" stroke="cyan" cx="247.81" cy="-126.11" rx="35.21" ry="22.76"/>
<text text-anchor="middle" x="247.81" y="-120.51" font-family="Indie Flower" font-size="22.00">WK1</text>
</g>
<!-- csf&#45;&gt;wk1 -->
<g id="edge34" class="edge">
<title>csf&#45;&gt;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"/>
<path fill="none" stroke="#458b74" d="M470.68,-427.98C411.03,-341.2 316.74,-210.42 272.33,-153.8"/>
<polygon fill="#458b74" stroke="#458b74" points="274.86,-151.35 265.9,-145.69 269.37,-155.7 274.86,-151.35"/>
</g>
<!-- wk3 -->
<g id="node24" class="node">
<title>wk3</title>
<ellipse fill="cyan" stroke="cyan" cx="230.73" cy="-73.72" rx="30.59" ry="18"/>
<text text-anchor="middle" x="230.73" y="-70.02" font-family="Indie Flower" font-size="14.00">WK3</text>
<ellipse fill="cyan" stroke="cyan" cx="316.52" cy="-85.24" rx="37.45" ry="22.76"/>
<text text-anchor="middle" x="316.52" y="-79.64" font-family="Indie Flower" font-size="22.00">WK3</text>
</g>
<!-- csf&#45;&gt;wk3 -->
<g id="edge18" class="edge">
<title>csf&#45;&gt;wk3</title>
<path fill="none" stroke="blue" d="M384.33,-340.17C346.31,-266.87 281.03,-149.9 249.12,-98.87"/>
<polygon fill="blue" stroke="blue" points="251.89,-96.69 243.57,-90.13 245.98,-100.45 251.89,-96.69"/>
<path fill="none" stroke="blue" d="M485.49,-420.28C440.94,-324 369.25,-177.81 335.28,-115.28"/>
<polygon fill="blue" stroke="blue" points="338.25,-113.41 330.36,-106.34 332.11,-116.78 338.25,-113.41"/>
</g>
<!-- eth -->
<g id="node32" class="node">
<title>eth</title>
<ellipse fill="cyan" stroke="cyan" cx="291.3" cy="-46.85" rx="28.7" ry="18"/>
<text text-anchor="middle" x="291.3" y="-43.15" font-family="Indie Flower" font-size="14.00">ETH</text>
<ellipse fill="cyan" stroke="cyan" cx="392.47" cy="-56.11" rx="36.54" ry="22.76"/>
<text text-anchor="middle" x="392.47" y="-50.51" font-family="Indie Flower" font-size="22.00">ETH</text>
</g>
<!-- csf&#45;&gt;eth -->
<g id="edge51" class="edge">
<g id="edge52" class="edge">
<title>csf&#45;&gt;eth</title>
<path fill="none" stroke="#458b74" d="M395.89,-336.08C371.11,-256.68 326.85,-128.82 304.52,-73.62"/>
<polygon fill="#458b74" stroke="#458b74" points="307.67,-72.09 300.62,-64.19 301.21,-74.77 307.67,-72.09"/>
<path fill="none" stroke="#458b74" d="M501.66,-415.11C474.1,-312.13 428.15,-154.8 405.49,-87.94"/>
<polygon fill="#458b74" stroke="#458b74" points="408.76,-86.69 402.19,-78.38 402.14,-88.97 408.76,-86.69"/>
</g>
<!-- xls2&#45;&gt;csf -->
<g id="edge24" class="edge">
<title>xls2&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M753.25,-399.45C693.11,-396.65 573.17,-396.18 493.07,-398.05"/>
<polygon fill="#458b74" stroke="#458b74" points="492.92,-394.55 483.01,-398.3 493.1,-401.55 492.92,-394.55"/>
<path fill="none" stroke="#458b74" d="M947.31,-507.23C872.79,-504.62 731.21,-504.14 632.3,-505.8"/>
<polygon fill="#458b74" stroke="#458b74" points="632.21,-502.31 622.27,-505.98 632.33,-509.3 632.21,-502.31"/>
</g>
<!-- xls3&#45;&gt;csf -->
<g id="edge26" class="edge">
<title>xls3&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M747.79,-473.47C690.3,-457.8 571.89,-431.09 492.53,-415.44"/>
<polygon fill="#458b74" stroke="#458b74" points="493.05,-411.98 482.57,-413.5 491.71,-418.85 493.05,-411.98"/>
<path fill="none" stroke="#458b74" d="M940.26,-602.16C869.05,-583.17 729.27,-550.8 631.33,-530.2"/>
<polygon fill="#458b74" stroke="#458b74" points="631.89,-526.74 621.39,-528.12 630.46,-533.59 631.89,-526.74"/>
</g>
<!-- xls4&#45;&gt;csf -->
<g id="edge28" class="edge">
<title>xls4&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M726.87,-545.2C675,-517.77 563.3,-465.18 488.2,-432.32"/>
<polygon fill="#458b74" stroke="#458b74" points="489.34,-429 478.77,-428.22 486.55,-435.42 489.34,-429"/>
<path fill="none" stroke="#458b74" d="M912.45,-691.74C847.68,-657.9 716.49,-594.96 624.46,-553.11"/>
<polygon fill="#458b74" stroke="#458b74" points="625.68,-549.82 615.13,-548.88 622.79,-556.19 625.68,-549.82"/>
</g>
<!-- xls5&#45;&gt;csf -->
<g id="edge8" class="edge">
<title>xls5&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M690.61,-611.1C646.74,-573.63 547.39,-496.82 480.31,-447.87"/>
<polygon fill="blue" stroke="blue" points="482.03,-444.8 471.88,-441.75 477.91,-450.46 482.03,-444.8"/>
<path fill="none" stroke="blue" d="M863.35,-774.19C807.96,-726.96 693.45,-635.97 612.71,-574.48"/>
<polygon fill="blue" stroke="blue" points="614.59,-571.51 604.52,-568.25 610.36,-577.09 614.59,-571.51"/>
</g>
<!-- xls8&#45;&gt;csf -->
<g id="edge10" class="edge">
<title>xls8&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M640.37,-667.64C606.25,-622.16 524.54,-524.1 469.01,-461.12"/>
<polygon fill="blue" stroke="blue" points="471.62,-458.79 462.37,-453.62 466.38,-463.43 471.62,-458.79"/>
<path fill="none" stroke="blue" d="M798.28,-845.47C755.53,-787.92 662.57,-671.8 596.63,-592.85"/>
<polygon fill="blue" stroke="blue" points="599.04,-590.27 589.93,-584.85 593.67,-594.76 599.04,-590.27"/>
</g>
<!-- xlml&#45;&gt;csf -->
<g id="edge6" class="edge">
<title>xlml&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M570.33,-715.83C548.54,-663.98 492.39,-547.88 453.44,-472.8"/>
<polygon fill="blue" stroke="blue" points="456.5,-471.11 448.78,-463.86 450.3,-474.34 456.5,-471.11"/>
<path fill="none" stroke="blue" d="M707.67,-903.71C680.6,-837.59 618.76,-701.21 574.17,-608.06"/>
<polygon fill="blue" stroke="blue" points="577.26,-606.4 569.77,-598.9 570.95,-609.43 577.26,-606.4"/>
</g>
<!-- xlsx&#45;&gt;csf -->
<g id="edge2" class="edge">
<title>xlsx&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M489.73,-745.57C481.38,-690.48 455.03,-563.36 435.43,-480.6"/>
<polygon fill="blue" stroke="blue" points="438.8,-479.65 433.07,-470.74 431.99,-481.28 438.8,-479.65"/>
<path fill="none" stroke="blue" d="M609.18,-937.47C598.77,-867.34 571.02,-719.19 549.78,-617.52"/>
<polygon fill="blue" stroke="blue" points="553.16,-616.59 547.68,-607.52 546.31,-618.03 553.16,-616.59"/>
</g>
<!-- xlsb&#45;&gt;csf -->
<g id="edge4" class="edge">
<title>xlsb&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M408.15,-753.66C412.83,-697.93 416.76,-568.36 416.93,-483.47"/>
<polygon fill="blue" stroke="blue" points="420.43,-483.35 416.94,-473.35 413.43,-483.35 420.43,-483.35"/>
<path fill="none" stroke="blue" d="M513.06,-945.86C518.39,-875.15 524.03,-724.54 525.75,-620.69"/>
<polygon fill="blue" stroke="blue" points="529.26,-620.53 525.91,-610.47 522.26,-620.42 529.26,-620.53"/>
</g>
<!-- nums&#45;&gt;csf -->
<g id="edge22" class="edge">
<title>nums&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M308.97,-745.49C327.76,-698.09 369.92,-567 394.67,-481.62"/>
<polygon fill="blue" stroke="blue" points="398.05,-482.54 397.45,-471.96 391.32,-480.61 398.05,-482.54"/>
<path fill="none" stroke="blue" d="M382.8,-938.12C406.12,-880.29 460.03,-723.53 494.07,-617.04"/>
<polygon fill="blue" stroke="blue" points="497.49,-617.81 497.19,-607.22 490.82,-615.68 497.49,-617.81"/>
</g>
<!-- ods&#45;&gt;csf -->
<g id="edge12" class="edge">
<title>ods&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M229.64,-708.37C259.96,-665.94 331.65,-550.97 376.04,-475.01"/>
<polygon fill="blue" stroke="blue" points="379.31,-476.35 381.31,-465.95 373.25,-472.83 379.31,-476.35"/>
<path fill="none" stroke="blue" d="M276.57,-883.88C315.28,-832.69 407.35,-697.62 467.54,-605.05"/>
<polygon fill="blue" stroke="blue" points="470.58,-606.81 473.09,-596.51 464.71,-603 470.58,-606.81"/>
</g>
<!-- fods&#45;&gt;csf -->
<g id="edge14" class="edge">
<title>fods&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M176.15,-665.09C215.32,-627.8 305.77,-531.13 363,-466.36"/>
<polygon fill="blue" stroke="blue" points="365.83,-468.44 369.81,-458.62 360.57,-463.82 365.83,-468.44"/>
<path fill="none" stroke="blue" d="M207.98,-820.71C258.64,-776.41 372.85,-667.38 449.09,-591.41"/>
<polygon fill="blue" stroke="blue" points="451.75,-593.71 456.35,-584.17 446.8,-588.76 451.75,-593.71"/>
</g>
<!-- uos -->
<g id="node13" class="node">
<title>uos</title>
<ellipse fill="cyan" stroke="cyan" cx="355.64" cy="-31" rx="30.59" ry="18"/>
<text text-anchor="middle" x="355.64" y="-27.3" font-family="Indie Flower" font-size="14.00">UOS</text>
<ellipse fill="cyan" stroke="cyan" cx="474.39" cy="-40.17" rx="39.7" ry="22.76"/>
<text text-anchor="middle" x="474.39" y="-34.57" font-family="Indie Flower" font-size="22.00">UOS</text>
</g>
<!-- uos&#45;&gt;csf -->
<g id="edge16" class="edge">
<title>uos&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M358.42,-48.98C366,-97.91 387.33,-235.76 401.07,-324.5"/>
<polygon fill="blue" stroke="blue" points="397.63,-325.18 402.62,-334.53 404.55,-324.11 397.63,-325.18"/>
<path fill="none" stroke="blue" d="M476.76,-62.91C483.06,-123.25 500.52,-290.5 512.26,-402.87"/>
<polygon fill="blue" stroke="blue" points="508.78,-403.3 513.3,-412.89 515.74,-402.58 508.78,-403.3"/>
</g>
<!-- html&#45;&gt;csf -->
<g id="edge49" class="edge">
<g id="edge50" class="edge">
<title>html&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M134.59,-603.02C184.75,-571.6 285.28,-502.14 350.91,-454.07"/>
<polygon fill="#458b74" stroke="#458b74" points="353.15,-456.77 359.14,-448.02 349.01,-451.13 353.15,-456.77"/>
<path fill="none" stroke="#458b74" d="M159.46,-738.34C223.1,-702.82 347.81,-627.46 433.41,-573.32"/>
<polygon fill="#458b74" stroke="#458b74" points="435.51,-576.13 442.09,-567.82 431.77,-570.22 435.51,-576.13"/>
</g>
<!-- csv&#45;&gt;csf -->
<g id="edge43" class="edge">
<g id="edge44" class="edge">
<title>csv&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M94.2,-547.45C146.65,-528.05 265.66,-476.64 342.6,-440.79"/>
<polygon fill="#458b74" stroke="#458b74" points="344.2,-443.91 351.77,-436.5 341.23,-437.57 344.2,-443.91"/>
<path fill="none" stroke="#458b74" d="M112.7,-669.72C177.5,-648.8 324.08,-594.55 423.47,-555.34"/>
<polygon fill="#458b74" stroke="#458b74" points="424.92,-558.53 432.93,-551.6 422.35,-552.02 424.92,-558.53"/>
</g>
<!-- txt&#45;&gt;csf -->
<g id="edge45" class="edge">
<g id="edge46" class="edge">
<title>txt&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M87.49,-474.85C148.63,-464.96 260.72,-442.44 336.13,-425.2"/>
<polygon fill="#458b74" stroke="#458b74" points="337.1,-428.57 346.05,-422.92 335.52,-421.75 337.1,-428.57"/>
<path fill="none" stroke="#458b74" d="M110.5,-579.8C185.7,-570.71 321.69,-550.18 417.04,-533.85"/>
<polygon fill="#458b74" stroke="#458b74" points="417.75,-537.28 427.01,-532.13 416.56,-530.38 417.75,-537.28"/>
</g>
<!-- dbf&#45;&gt;csf -->
<g id="edge47" class="edge">
<g id="edge48" class="edge">
<title>dbf&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M64.23,-404.06C120.46,-407.82 248.93,-409.69 333.36,-408.54"/>
<polygon fill="#458b74" stroke="#458b74" points="333.48,-412.04 343.43,-408.39 333.37,-405.04 333.48,-412.04"/>
<path fill="none" stroke="#458b74" d="M92.04,-485.49C163.38,-493.37 311.94,-504.33 414.68,-509.78"/>
<polygon fill="#458b74" stroke="#458b74" points="414.62,-513.28 424.79,-510.3 414.98,-506.29 414.62,-513.28"/>
</g>
<!-- dif&#45;&gt;csf -->
<g id="edge39" class="edge">
<title>dif&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M66.36,-345.56C119.67,-358.46 248.86,-382.15 333.67,-395.22"/>
<polygon fill="#458b74" stroke="#458b74" points="333.37,-398.72 343.78,-396.76 334.43,-391.8 333.37,-398.72"/>
<path fill="none" stroke="#458b74" d="M93.7,-406.64C157.66,-426.19 311.34,-465.78 416.51,-490.49"/>
<polygon fill="#458b74" stroke="#458b74" points="416,-493.96 426.53,-492.83 417.59,-487.15 416,-493.96"/>
</g>
<!-- slk&#45;&gt;csf -->
<g id="edge31" class="edge">
<title>slk&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M83.29,-285.33C134.11,-307.67 255.79,-353.44 336.35,-381.18"/>
<polygon fill="#458b74" stroke="#458b74" points="335.37,-384.54 345.96,-384.47 337.64,-377.92 335.37,-384.54"/>
<path fill="none" stroke="#458b74" d="M118.64,-335.01C178.71,-365.35 322.62,-430.3 421.65,-472.47"/>
<polygon fill="#458b74" stroke="#458b74" points="420.52,-475.79 431.09,-476.48 423.26,-469.35 420.52,-475.79"/>
</g>
<!-- prn&#45;&gt;csf -->
<g id="edge41" class="edge">
<g id="edge42" class="edge">
<title>prn&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M107.26,-224.48C150.82,-254.64 265.79,-324.31 341.77,-367.42"/>
<polygon fill="#458b74" stroke="#458b74" points="340.4,-370.67 350.83,-372.54 343.85,-364.58 340.4,-370.67"/>
<path fill="none" stroke="#458b74" d="M154.84,-261.78C206.46,-301.46 339.28,-393.89 430.59,-454.55"/>
<polygon fill="#458b74" stroke="#458b74" points="428.75,-457.53 439.02,-460.13 432.62,-451.69 428.75,-457.53"/>
</g>
<!-- rtf&#45;&gt;csf -->
<g id="edge40" class="edge">
<title>rtf&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M202.98,-198.44C245.23,-245.28 361.45,-361.64 441.72,-438.59"/>
<polygon fill="#458b74" stroke="#458b74" points="439.73,-441.53 449.37,-445.91 444.57,-436.47 439.73,-441.53"/>
</g>
<!-- wk1&#45;&gt;csf -->
<g id="edge33" class="edge">
<title>wk1&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M184.31,-129.31C213.71,-170.98 300.37,-277.99 358.3,-345.42"/>
<polygon fill="#458b74" stroke="#458b74" points="355.68,-347.74 364.86,-353.03 360.98,-343.17 355.68,-347.74"/>
<path fill="none" stroke="#458b74" d="M259.28,-147.61C292.3,-199.97 388.7,-336.2 455.51,-426.34"/>
<polygon fill="#458b74" stroke="#458b74" points="452.7,-428.43 461.47,-434.37 458.32,-424.26 452.7,-428.43"/>
</g>
<!-- wksl -->
<g id="node23" class="node">
<title>wksl</title>
<ellipse fill="cyan" stroke="cyan" cx="428.51" cy="-26.87" rx="35.21" ry="26.74"/>
<text text-anchor="middle" x="428.51" y="-30.67" font-family="Indie Flower" font-size="14.00">WKS</text>
<text text-anchor="middle" x="428.51" y="-15.67" font-family="Indie Flower" font-size="14.00">Lotus</text>
<ellipse fill="cyan" stroke="cyan" cx="567" cy="-39.6" rx="45.92" ry="39.7"/>
<text text-anchor="middle" x="567" y="-46" font-family="Indie Flower" font-size="22.00">WKS</text>
<text text-anchor="middle" x="567" y="-22" font-family="Indie Flower" font-size="22.00">Lotus</text>
</g>
<!-- wksl&#45;&gt;csf -->
<g id="edge37" class="edge">
<title>wksl&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M427.42,-53.97C425.17,-109.67 419.97,-238.9 416.55,-323.7"/>
<polygon fill="#458b74" stroke="#458b74" points="413.05,-323.68 416.14,-333.82 420.04,-323.97 413.05,-323.68"/>
<path fill="none" stroke="#458b74" d="M563.34,-79.43C556.84,-150.09 543.09,-299.61 533.6,-402.83"/>
<polygon fill="#458b74" stroke="#458b74" points="530.1,-402.7 532.67,-412.98 537.07,-403.34 530.1,-402.7"/>
</g>
<!-- wk3&#45;&gt;csf -->
<g id="edge17" class="edge">
<title>wk3&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M237.03,-91.6C257.98,-137.18 324.61,-259.29 369.76,-336.57"/>
<polygon fill="blue" stroke="blue" points="366.8,-338.45 374.88,-345.3 372.83,-334.91 366.8,-338.45"/>
<path fill="none" stroke="blue" d="M323.83,-107.46C347.18,-163.91 419.76,-315.36 470.73,-415.98"/>
<polygon fill="blue" stroke="blue" points="467.64,-417.62 475.29,-424.95 473.88,-414.45 467.64,-417.62"/>
</g>
<!-- wk4 -->
<g id="node25" class="node">
<title>wk4</title>
<ellipse fill="cyan" stroke="cyan" cx="500.81" cy="-36.85" rx="30.59" ry="18"/>
<text text-anchor="middle" x="500.81" y="-33.15" font-family="Indie Flower" font-size="14.00">WK4</text>
<ellipse fill="cyan" stroke="cyan" cx="655.24" cy="-56.25" rx="36.54" ry="22.76"/>
<text text-anchor="middle" x="655.24" y="-50.65" font-family="Indie Flower" font-size="22.00">WK4</text>
</g>
<!-- wk4&#45;&gt;csf -->
<g id="edge19" class="edge">
<title>wk4&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M496.53,-54.79C484.99,-103.2 452.67,-238.7 431.88,-325.89"/>
<polygon fill="blue" stroke="blue" points="428.44,-325.21 429.53,-335.75 435.25,-326.83 428.44,-325.21"/>
<path fill="none" stroke="blue" d="M648.71,-78.83C631.69,-137.71 585.11,-298.83 553.84,-406.99"/>
<polygon fill="blue" stroke="blue" points="550.47,-406.05 551.06,-416.63 557.2,-408 550.47,-406.05"/>
</g>
<!-- 123 -->
<g id="node26" class="node">
<title>123</title>
<ellipse fill="cyan" stroke="cyan" cx="562.2" cy="-57.19" rx="27" ry="18"/>
<text text-anchor="middle" x="562.2" y="-53.49" font-family="Indie Flower" font-size="14.00">123</text>
<ellipse fill="cyan" stroke="cyan" cx="725.42" cy="-82.72" rx="31.23" ry="22.76"/>
<text text-anchor="middle" x="725.42" y="-77.12" font-family="Indie Flower" font-size="22.00">123</text>
</g>
<!-- 123&#45;&gt;csf -->
<g id="edge20" class="edge">
<title>123&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M554.61,-74.85C534.7,-121.19 480.06,-248.36 444.86,-330.29"/>
<polygon fill="blue" stroke="blue" points="441.61,-328.98 440.88,-339.55 448.04,-331.75 441.61,-328.98"/>
<path fill="none" stroke="blue" d="M715.27,-104.28C689.09,-159.93 617.94,-311.12 570.09,-412.81"/>
<polygon fill="blue" stroke="blue" points="566.91,-411.34 565.82,-421.88 573.25,-414.32 566.91,-411.34"/>
</g>
<!-- wksm -->
<g id="node27" class="node">
<title>wksm</title>
<ellipse fill="cyan" stroke="cyan" cx="626.15" cy="-92.35" rx="38.78" ry="26.74"/>
<text text-anchor="middle" x="626.15" y="-96.15" font-family="Indie Flower" font-size="14.00">WKS</text>
<text text-anchor="middle" x="626.15" y="-81.15" font-family="Indie Flower" font-size="14.00">Works</text>
<ellipse fill="cyan" stroke="cyan" cx="801.54" cy="-127.65" rx="50.41" ry="39.7"/>
<text text-anchor="middle" x="801.54" y="-134.05" font-family="Indie Flower" font-size="22.00">WKS</text>
<text text-anchor="middle" x="801.54" y="-110.05" font-family="Indie Flower" font-size="22.00">Works</text>
</g>
<!-- wksm&#45;&gt;csf -->
<g id="edge38" class="edge">
<title>wksm&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M609.45,-116.78C577.31,-163.8 505.87,-268.3 458.67,-337.35"/>
<polygon fill="#458b74" stroke="#458b74" points="455.5,-335.78 452.75,-346.01 461.28,-339.73 455.5,-335.78"/>
<path fill="none" stroke="#458b74" d="M776.63,-162.06C734.46,-220.3 647.76,-340.08 587.68,-423.07"/>
<polygon fill="#458b74" stroke="#458b74" points="584.8,-421.08 581.77,-431.23 590.47,-425.19 584.8,-421.08"/>
</g>
<!-- xlr -->
<g id="node28" class="node">
<title>xlr</title>
<ellipse fill="cyan" stroke="cyan" cx="682.79" cy="-139.85" rx="27.9" ry="18"/>
<text text-anchor="middle" x="682.79" y="-136.15" font-family="Indie Flower" font-size="14.00">XLR</text>
<ellipse fill="cyan" stroke="cyan" cx="870.41" cy="-188.5" rx="34.8" ry="22.76"/>
<text text-anchor="middle" x="870.41" y="-182.9" font-family="Indie Flower" font-size="22.00">XLR</text>
</g>
<!-- xlr&#45;&gt;csf -->
<g id="edge35" class="edge">
<title>xlr&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M667.14,-155.17C629.68,-191.84 533.36,-286.15 470.74,-347.47"/>
<polygon fill="#458b74" stroke="#458b74" points="467.97,-345.28 463.27,-354.78 472.87,-350.28 467.97,-345.28"/>
<path fill="none" stroke="#458b74" d="M850.52,-207.03C803.74,-250.61 684.62,-361.59 603.85,-436.83"/>
<polygon fill="#458b74" stroke="#458b74" points="601.08,-434.62 596.15,-444 605.86,-439.74 601.08,-434.62"/>
</g>
<!-- wq1 -->
<g id="node29" class="node">
<title>wq1</title>
<ellipse fill="cyan" stroke="cyan" cx="725.17" cy="-191.62" rx="31.7" ry="18"/>
<text text-anchor="middle" x="725.17" y="-187.92" font-family="Indie Flower" font-size="14.00">WQ1</text>
<ellipse fill="cyan" stroke="cyan" cx="919.38" cy="-250.79" rx="37.45" ry="22.76"/>
<text text-anchor="middle" x="919.38" y="-245.19" font-family="Indie Flower" font-size="22.00">WQ1</text>
</g>
<!-- wq1&#45;&gt;csf -->
<g id="edge36" class="edge">
<title>wq1&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M704.71,-205.53C659.79,-236.08 550.87,-310.15 479.67,-358.56"/>
<polygon fill="#458b74" stroke="#458b74" points="477.48,-355.82 471.18,-364.34 481.42,-361.61 477.48,-355.82"/>
<path fill="none" stroke="#458b74" d="M894.02,-267.5C838.76,-303.91 705.19,-391.93 614.49,-451.7"/>
<polygon fill="#458b74" stroke="#458b74" points="612.55,-448.78 606.13,-457.21 616.4,-454.63 612.55,-448.78"/>
</g>
<!-- wq2 -->
<g id="node30" class="node">
<title>wq2</title>
<ellipse fill="cyan" stroke="cyan" cx="760.41" cy="-256.21" rx="34.8" ry="26.74"/>
<text text-anchor="middle" x="760.41" y="-260.01" font-family="Indie Flower" font-size="14.00">WQ2</text>
<text text-anchor="middle" x="760.41" y="-245.01" font-family="Indie Flower" font-size="14.00">WB*</text>
<ellipse fill="cyan" stroke="cyan" cx="959.82" cy="-326.2" rx="41.02" ry="39.7"/>
<text text-anchor="middle" x="959.82" y="-332.6" font-family="Indie Flower" font-size="22.00">WQ2</text>
<text text-anchor="middle" x="959.82" y="-308.6" font-family="Indie Flower" font-size="22.00">WB*</text>
</g>
<!-- wq2&#45;&gt;csf -->
<g id="edge11" class="edge">
<title>wq2&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M729.92,-269.16C676.06,-292.05 562.68,-340.22 487.26,-372.27"/>
<polygon fill="blue" stroke="blue" points="485.63,-369.16 477.8,-376.29 488.37,-375.6 485.63,-369.16"/>
<path fill="none" stroke="blue" d="M922.26,-342.17C856.65,-370.05 719.17,-428.48 624.14,-468.86"/>
<polygon fill="blue" stroke="blue" points="622.63,-465.7 614.79,-472.83 625.37,-472.14 622.63,-465.7"/>
</g>
<!-- qpw -->
<g id="node31" class="node">
<title>qpw</title>
<ellipse fill="cyan" stroke="cyan" cx="782.58" cy="-327.05" rx="32.49" ry="18"/>
<text text-anchor="middle" x="782.58" y="-323.35" font-family="Indie Flower" font-size="14.00">QPW</text>
<ellipse fill="cyan" stroke="cyan" cx="987.11" cy="-412.49" rx="42.35" ry="22.76"/>
<text text-anchor="middle" x="987.11" y="-406.89" font-family="Indie Flower" font-size="22.00">QPW</text>
</g>
<!-- qpw&#45;&gt;csf -->
<g id="edge21" class="edge">
<title>qpw&#45;&gt;csf</title>
<path fill="none" stroke="blue" d="M752.05,-333.38C695.54,-345.11 573.04,-370.53 491.9,-387.38"/>
<polygon fill="blue" stroke="blue" points="490.8,-384.03 481.72,-389.49 492.22,-390.88 490.8,-384.03"/>
<path fill="none" stroke="blue" d="M947.59,-420.94C877.99,-435.82 731.36,-467.17 630.19,-488.8"/>
<polygon fill="blue" stroke="blue" points="629.29,-485.41 620.24,-490.93 630.75,-492.26 629.29,-485.41"/>
</g>
<!-- eth&#45;&gt;csf -->
<g id="edge52" class="edge">
<g id="edge53" class="edge">
<title>eth&#45;&gt;csf</title>
<path fill="none" stroke="#458b74" d="M294.25,-65.07C306.72,-113.21 351.17,-245.78 382.2,-330.05"/>
<polygon fill="#458b74" stroke="#458b74" points="378.97,-331.41 385.72,-339.57 385.53,-328.98 378.97,-331.41"/>
<path fill="none" stroke="#458b74" d="M395.8,-78.94C409.08,-138.35 454.86,-300.58 487.94,-408.62"/>
<polygon fill="#458b74" stroke="#458b74" points="484.62,-409.72 490.9,-418.25 491.31,-407.67 484.62,-409.72"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB