diff --git a/docz/docs/03-demos/08-local/01-file.md b/docz/docs/03-demos/08-local/01-file.md index 5cd0bed..118b65f 100644 --- a/docz/docs/03-demos/08-local/01-file.md +++ b/docz/docs/03-demos/08-local/01-file.md @@ -130,6 +130,37 @@ _Writing Files_ XLSX.writeFile(wb, "SheetJS.xlsx"); ``` +
Implementation Details (click to show) + +Under the hood, it creates a special URL and clicks a link. The library method +includes a few workarounds for legacy browsers + +`XLSX.writeFile(wb, "SheetJS.xlsx");` is roughly equivalent to: + +```js +/* write data -- note that writeFile infers bookType from filename */ +const u8 = XLSX.write(wb, { bookType: "xlsx", type: "buffer" }); +/* create Blob */ +const blob = new Blob([u8]); +/* create object URL */ +const url = URL.createObjectURL(new Blob([u8])); + +/* create `A` DOM element */ +const a = document.createElement("a"); +/* set export file name */ +a.download = "SheetJS.xlsx"; +/* wire up the object URL to the DOM element */ +a.href = url; +/* add to the page */ +document.body.appendChild(a); +/* click the link */ +a.click(); +/* remove the element from the page */ +document.body.removeChild(a); +``` + +
+ :::caution Web Workers `XLSX.writeFile` requires DOM access and will not work in a Web Worker! @@ -378,6 +409,51 @@ var wb = readFile("sheetjs.numbers"); writeFile(wb, "sheetjs.xlsx"); ``` +
Implementation Details (click to show) + +**`XLSX.readFile(filepath)`** is equivalent to: + +_CommonJS_ + +```js +var fs = require("fs"); +var buf = fs.readFileSync(filepath); +var wb = XLSX.read(buf); +``` + +_ECMAScript Modules_ + +```js +import { read } from "xlsx"; +import { readFileSync } from "fs"; + +var buf = readFileSync(filepath); +var wb = read(buf); +``` + +**`XLSX.writeFile(wb, filepath)`** is equivalent to: + +_CommonJS_ + +```js +var fs = require("fs"), path = require("path"); +var buf = XLSX.write(wb, { bookType: path.extname(filepath).slice(1), type: "buffer" }); +fs.writeFileSync(filepath, buf); +``` + +_ECMAScript Modules_ + +```js +import { write } from "xlsx"; +import { writeFileSync } from "fs"; +import { extname } from "path"; + +var buf = write(wb, { bookType: extname(filepath).slice(1), type: "buffer" }); +writeFileSync(filepath, buf); +``` + +
+ ### ExtendScript In Photoshop and other Adobe apps, `readFile` and `writeFile` use the `File` @@ -390,6 +466,19 @@ var wb = XLSX.readFile("sheetjs.xlsx"); XLSX.writeFile(wb, "sheetjs.csv"); ``` +The [ExtendScript demo](/docs/demos/extensions/extendscript) covers the "Common +Extensibility Platform" (CEP) and "Unified Extensibility Platform" (UXP) details. + +### Chrome Extensions + +In Manifest v2 Chrome extensions, `writeFile` calls `chrome.downloads.download`. + +This approach uses `URL.createObjectURL`, an API that is not supported in a +Manifest v3 Background Service Worker. For small exports, raw Base64 URLs can be +generated and downloaded. + +The [Chromium demo](/docs/demos/extensions/chromium) covers the details. + ### Deno `readFile` uses `Deno.readFileSync` and `writeFile` uses `Deno.writeFileSync`: @@ -410,6 +499,34 @@ Any Deno script using `XLSX.writeFile` requires the `--allow-write` entitlement. ::: +
Implementation Details (click to show) + +**`XLSX.readFile(filepath)`** is equivalent to: + +_ECMAScript Modules_ + +{`\ +// @deno-types="https://cdn.sheetjs.com/xlsx-${current}/package/types/index.d.ts" +import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs'; +\n\ +const u8: Uint8Array = Deno.readFileSync(filepath); +const wb: XLSX.WorkBook = XLSX.read(u8);`} + + +**`XLSX.writeFile(wb, filepath)`** is equivalent to: + +_ECMAScript Modules_ + +{`\ +// @deno-types="https://cdn.sheetjs.com/xlsx-${current}/package/types/index.d.ts" +import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs'; +\n\ +const u8 = XLSX.write(wb, { bookType: filepath.slice(filepath.lastIndexOf(".")+1), type: "buffer" }); +Deno.writeFileSync(filepath, u8);`} + + +
+ ### Bun Bun requires the `fs` module: @@ -423,10 +540,11 @@ var wb = readFile("sheetjs.numbers"); writeFile(wb, "sheetjs.xlsx"); ``` +The implementation is identical to [NodeJS ECMAScript Modules](#nodejs). + ### Apps Desktop and mobile apps have their own specific APIs covered in separate demos: - [Electron and other desktop apps](/docs/demos/desktop) - [React Native and other mobile apps](/docs/demos/mobile) - diff --git a/docz/docs/03-demos/09-cloud/11-deno.md b/docz/docs/03-demos/09-cloud/11-deno.md new file mode 100644 index 0000000..43d1e6d --- /dev/null +++ b/docz/docs/03-demos/09-cloud/11-deno.md @@ -0,0 +1,112 @@ +--- +title: Deno Deploy +pagination_prev: demos/local/index +pagination_next: demos/extensions/index +--- + +import current from '/version.js'; +import CodeBlock from '@theme/CodeBlock'; + +Deno Deploy offers "Serverless Functions" powered by Deno. + +The [Deno installation](/docs/getting-started/installation/deno) instructions +apply to Deno Deploy scripts. + +:::warning + +Deno Deploy does not offer any sort of temporary file access in functions. + +This breaks web frameworks that use the filesystem in body parsing. + +::: + +:::caution + +When the demo was last tested, Deno Deploy required a GitHub account. + +::: + +## Supported Frameworks + +When the demo was last tested, the `drash` server framework used an in-memory +approach for parsing POST request bodies. + +### Parsing Data + +When files are submitted via HTTP POST, the `bodyParam` method can fetch data. +The `content` property of the returned object can be parsed with `XLSX.read`. + +The following example assumes the file is submitted at field name `file`: + +{`\ +// @deno-types="https://cdn.sheetjs.com/xlsx-${current}/package/types/index.d.ts" +import { read, utils } from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs'; +import * as Drash from "https://deno.land/x/drash@v2.5.4/mod.ts"; +\n\ +class SheetJSResource extends Drash.Resource { + public paths = ["/"]; +\n\ + public POST(request: Drash.Request, response: Drash.Response) { + // highlight-start + /* get data from body */ + const file = request.bodyParam("file"); + /* parse */ + var wb = read(file.content, {type: "buffer", dense: true}); + // highlight-end + /* generate HTML from first worksheet */ + return response.html(utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]])); + } +}`} + + +## Demo + +:::note + +This demo was last tested on 2023 June 05. The service +was implemented using this exact sequence. + +::: + +1) Register and Sign in. + +2) Click "New Project" to create a new Project. In the next screen, look for the +"Hello World" sample and click the corresponding "Fork" button. + +3) Download [`s2c.ts`](pathname:///deno/s2c.ts). Open with a text editor and +copy the contents into the playground editor (left pane). + +4) Click "Save and Deploy". + +### Testing + +5) Download the test file + +6) In the browser window, click "Choose File" and select the downloaded file. +Click "Submit" and the page will show the contents in a HTML TABLE. + +7) Click the "Fullscreen" icon in the top-right corner of the page window. + +8) Open a terminal window and download : + +```bash +curl -LO https://sheetjs.com/pres.numbers +``` + +9) Copy the first `curl` line from the page and run in the terminal. For +example, if the deployment is `clean-badger-69`, the command would be + +```bash +curl -X POST -F"file=@pres.numbers" https://clean-badger-69.deno.dev/ +``` + +The output will be an HTML table + +10) Copy the second `curl` line from the page and run in the terminal. For +example, if the deployment is `clean-badger-69`, the command would be + +```bash +curl -X POST -F"file=@pres.numbers" -F"type=csv" https://clean-badger-69.deno.dev/ +``` + +The output will be CSV. \ No newline at end of file diff --git a/docz/docs/03-demos/10-extensions/01-extendscript.md b/docz/docs/03-demos/10-extensions/01-extendscript.md index 7213c5d..0936ea8 100644 --- a/docz/docs/03-demos/10-extensions/01-extendscript.md +++ b/docz/docs/03-demos/10-extensions/01-extendscript.md @@ -15,9 +15,11 @@ support. Over the years there have been a few different JavaScript platforms: - "ExtendScript": This uses an old JavaScript dialect but is supported in older versions of Creative Suite and Creative Cloud. -- "CEP": This was recommended in CS6 but eventually deprecated. +- "Common Extensibility Platform" (CEP): This was introduced in Creative Suite. + App automation uses ExtendScript, but integration logic uses modern JS. -- "UXP": This is the current Adobe recommendation for new CC extensions. +- "Unified Extensibility Platform" (UXP): This is the current recommendation for + new Adobe CC extensions in supported apps (Photoshop 2021+ and InDesign 2022+) This demo intends to cover parts relevant to SheetJS. General setup as well as general Adobe considerations are not covered here. A basic familiarity with diff --git a/docz/docs/03-demos/10-extensions/02-chromium.md b/docz/docs/03-demos/10-extensions/02-chromium.md index bf578bf..ad1fbc2 100644 --- a/docz/docs/03-demos/10-extensions/02-chromium.md +++ b/docz/docs/03-demos/10-extensions/02-chromium.md @@ -4,39 +4,88 @@ pagination_prev: demos/cloud/index pagination_next: demos/bigdata/index --- -:::warning +:::note -This demo was written using the Manifest V2 extension platform. Chrome Web -Store will not accept new V2 extensions, but these can be sideloaded using the -"Load unpacked" extension option. +This demo showcases Manifest V2 and Manifest V3 extensions. Chrome Web Store +will not accept new V2 extensions, but these can be sideloaded using the +"Load unpacked" extension option in Developer mode. ::: -This library is compatible with Chrome and Chromium extensions and should just -work out of the box. Specific API support is listed in the Chrome extensions -API documentation. +The [Standalone scripts](/docs/getting-started/installation/standalone) can be +integrated in a Chromium extension. -[Right-Click and download the final CRX](pathname:///chromium/SheetJSDemo.crx) +This demo includes examples for exporting bookmarks from a popup and scraping +tables with a content script and a background script. -:::caution +[The demo](#demo) includes unpacked extensions for Manifest V2 and Manifest V3. -New releases of Chrome / Chromium will block with `CRX_REQUIRED_PROOF_MISSING`. +:::note -To try the extension: - -1) Right-click and select "Save Link As ..." to save the CRX file - -2) Open `chrome://extensions/` in the browser and enable Developer mode - -3) Click and drag the downloaded CRX file into the Extensions page to install. +This demo was last tested on 2023 June 06 against Chrome 114 ::: +## Loading SheetJS Scripts + +SheetJS libraries should be bundled in the extension. For path purposes, it is +strongly recommended to place `xlsx.full.min.js` in the root folder. + +#### Popup Pages + +In Manifest V2 and Manifest V3 extensions, popup pages can load the standalone +script using a normal ` +``` + +#### Content Scripts + +In Manifest V2 and Manifest V3 extensions, the standalone script can be loaded +through the `content_scripts` field: + +```js + /* in manifest.json v2 or v3 */ + "content_scripts": [{ + "matches": [""], + "js": ["xlsx.full.min.js", "content.js"], + "run_at": "document_end" + }], +``` + +The `XLSX` global will be visible to other content scripts. + +#### Background Scripts + +In Manifest V2 extensions, if the standalone script is added as a background +script, other background scripts will be able to access the `XLSX` global! + +```js + /* in manifest.json v2 only! */ + "background": { + "scripts": ["xlsx.full.min.js", "table.js"], + "persistent": false + }, +``` + +In Manifest V3 extensions, background service workers can load the standalone +script through `importScripts`: + +```js +/* assuming background script is in the same folder as xlsx.full.min.js */ +importScripts("./xlsx.full.min.js"); +// now XLSX will be available +``` ## Relevant Operations +The official documentation covers details including required permissions. + ### Generating Downloads +#### Manifest V2 + The `writeFile` function works in a Chrome or Chromium extension: ```js @@ -46,6 +95,21 @@ XLSX.writeFile(wb, "export.xlsx"); Under the hood, it uses the `chrome.downloads` API. `"downloads"` permission should be set in `manifest.json`. +#### Manifest V3 + +In a background service worker, `URL.createObjectURL` is unavailable. Instead, +`XLSX.write` can generate a Base64 string for a synthetic URL: + +```js +/* generate Base64 string */ +const b64 = XLSX.write(wb, {bookType: "xlsx", type: "base64"}); +chrome.downloads.download({ + /* make a base64 url manually */ + url: `data:application/octet-stream;base64,${b64}`, + filename: `SheetJSTables.xlsx` +}); +``` + ### Content Script Table Scraping `table_to_book` and `table_to_sheet` can help build workbooks from DOM tables: @@ -64,24 +128,50 @@ for(var i = 0; i < tables.length; ++i) { The demo extension includes multiple features to demonstrate sample usage. Production extensions should include proper error handling. -
Testing Unpacked Extension (click to show) +
Testing Unpacked Extension (click to hide) -1) [Right-Click and download the zip](pathname:///chromium/SheetJSChromiumUnpacked.zip) +1) Download the zip for the desired Manifest version: -2) Create a `SheetJSChromium` folder in your Downloads directory, move the zip - file into the folder, and extract the zip file. +- [Manifest V2](pathname:///chromium/SheetJSChromiumUnpackedV2.zip) +- [Manifest V3](pathname:///chromium/SheetJSChromiumUnpackedV3.zip) -3) Open `chrome://extensions/` in the browser and enable Developer mode +2) Open `chrome://extensions/` in the browser and enable Developer mode -4) Click "Load Unpacked" and select the `SheetJSChromium` folder. +3) Drag and drop the downloaded zip file into the window.
### Bookmark Exporter +
Testing (click to hide) + +0) Go to and create a bookmark in the browser. + +1) Click the Extensions icon (puzzle icon to the right of the address bar) and +select "SheetJS Demo". + +2) If a small popup is not displayed, click on the SheetJS icon + +3) Click "Export Bookmarks" and click "Save". Open the downloaded file! + +
+ +```mermaid +sequenceDiagram + actor U as User + participant P as Popup + participant A as Chromium + U->>P: click icon + P->>A: `chrome.bookmarks.getTree` + A->>P: bookmark tree + Note over P: walk tree + Note over P: make workbook + P->>U: `XLSX.writeFile` +``` + `chrome.bookmarks` API enables bookmark tree traversal. The "Export Bookmarks" button in the extension pop-up recursively walks the bookmark tree, pushes the -bookmark URLs into a data array, and exports into a simple spreadsheet: +bookmark URLs into a data array, and exports into a simple spreadsheet. ```js /* walk the bookmark tree */ @@ -108,17 +198,57 @@ chrome.bookmarks.getTree(function(res) { ### Table Exporter -The `content.js` content script converts a table in the DOM to workbook object -using the `table_to_book` utility function: +
Testing (click to hide) -```js -// event page script trigger -chrome.tabs.sendMessage(tab.id); -// content script convert -var wb = XLSX.utils.table_to_book(elt); -// event page script callback -XLSX.writeFile(wb, "export.xlsx"); +1) Go to + +2) Right-click anywhere in the page and select "SheetJS Demo" > "Export All Tables in Page" + +3) Save and open the downloaded file! + +
+ +The background script configures a context menu with the option to export data. +The flow diagrams show the data flow when the user chooses to export. They +differ in the denouement + +```mermaid +sequenceDiagram + actor U as User + participant P as Background Script + participant A as Content Script + U->>P: Context Click > "Export" + Note over P: Query for active tab + P->>A: Ask active tab for data + Note over A: `table_to_sheet` + Note over A: generate workbook + A->>P: workbook object + Note over U,A: ... different denouement for Manifest V2 / V3 extensions ... ``` -Since the workbook object is a plain JS object, the object is sent back to an -event page script which generates the file and attempts a download. +#### Manifest V2 + +For Manifest V2 extensions, `XLSX.writeFile` just works: + +```mermaid +sequenceDiagram + actor U as User + participant P as Background Script + Note over P,U: ... background script received workbook ... + P->>U: `XLSX.writeFile` download +``` + +#### Manifest V3 + +For Manifest V3 extensions, since `URL.createObjectURL` is not available in +background service workers, a synthetic URL is created: + +```mermaid +sequenceDiagram + actor U as User + participant P as Background Script + Note over P,U: ... background script received workbook ... + Note over P: `XLSX.write` Base64 + Note over P: Create Data URL + P->>U: `chrome.downloads.download` +``` diff --git a/docz/static/chromium/SheetJSChromiumUnpacked.zip b/docz/static/chromium/SheetJSChromiumUnpackedV2.zip similarity index 55% rename from docz/static/chromium/SheetJSChromiumUnpacked.zip rename to docz/static/chromium/SheetJSChromiumUnpackedV2.zip index dfd2c2a..e873bcd 100644 Binary files a/docz/static/chromium/SheetJSChromiumUnpacked.zip and b/docz/static/chromium/SheetJSChromiumUnpackedV2.zip differ diff --git a/docz/static/chromium/SheetJSChromiumUnpackedV3.zip b/docz/static/chromium/SheetJSChromiumUnpackedV3.zip new file mode 100644 index 0000000..068253a Binary files /dev/null and b/docz/static/chromium/SheetJSChromiumUnpackedV3.zip differ diff --git a/docz/static/chromium/SheetJSDemo.crx b/docz/static/chromium/SheetJSDemo.crx deleted file mode 100644 index 4f2e8c6..0000000 Binary files a/docz/static/chromium/SheetJSDemo.crx and /dev/null differ diff --git a/docz/static/deno/s2c.ts b/docz/static/deno/s2c.ts new file mode 100644 index 0000000..44b899f --- /dev/null +++ b/docz/static/deno/s2c.ts @@ -0,0 +1,118 @@ +// @deno-types="https://cdn.sheetjs.com/xlsx-0.19.3/package/types/index.d.ts" +import { read, utils, set_cptable, version } from 'https://cdn.sheetjs.com/xlsx-0.19.3/package/xlsx.mjs'; +import * as cptable from 'https://cdn.sheetjs.com/xlsx-0.19.3/package/dist/cpexcel.full.mjs'; +set_cptable(cptable); + +import * as Drash from "https://deno.land/x/drash@v2.5.4/mod.ts"; + +class SheetJSResource extends Drash.Resource { + public paths = ["/"]; + + public OPTIONS(request: Drash.Request, response: Drash.Response) { + const allHttpMethods: string[] = [ "GET", "POST", "PUT", "DELETE" ]; + response.headers.set("Allow", allHttpMethods.join()); + response.headers.set("Access-Control-Allow-Methods", allHttpMethods.join()); + response.headers.set("access-control-allow-origin", "*"); + response.status_code = 204; + return response; + } + + public POST(request: Drash.Request, response: Drash.Response) { + const file = request.bodyParam("file"); + const type = request.bodyParam("type"); + try { response.headers.set("access-control-allow-origin", "*"); } catch(e) {} + if (!file) throw new Error("File is required!"); + var wb = read(file.content, {type: "buffer", dense: true}); + return response.html( (type == "csv" ? utils.sheet_to_csv : utils.sheet_to_html)(wb.Sheets[wb.SheetNames[0]])); + } + + public GET(request: Drash.Request, response: Drash.Response): void { + try { response.headers.set("access-control-allow-origin", "*"); } catch(e) {} + return response.html(`\ + + + + SheetJS Spreadsheet to HTML Conversion Service + + + + +

SheetJS Spreadsheet Conversion Service

+
+

API

+
+Send a POST request to https://s2c.sheetjs.com with the file in the file body parameter:
+
+
+    curl -X POST -F"file=@pres.numbers" https://s2c.sheetjs.com/
+
+
+The response will be an HTML TABLE generated from the first worksheet. +

+For CSV data, pass the parameter type=csv:
+
+
+    curl -X POST -F"file=@pres.numbers" -F"type=csv" https://s2c.sheetjs.com/
+
+

+

Try it out!

+
+
+

+Use the file input element to select a file, then click "Submit"

+

+
+ +SheetJS Library Version: ${version} + + +`, + ); + } +} + +const server = new Drash.Server({ + hostname: "", + port: 3000, + protocol: "http", + resources: [ + SheetJSResource, + ], +}); + +server.run(); + +console.log(`Server running at ${server.address}.`); +