diff --git a/README.md b/README.md index 6992fa1..d7ed4ed 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,6 @@ Specific pages can load scripts using the `head` component: ``` +## Other Notes + +`src/theme/Admonition` was swizzled from 2.4.1 to address Docusaurus issue 8568 \ No newline at end of file diff --git a/docz/docs/02-getting-started/03-roadmap.md b/docz/docs/02-getting-started/03-roadmap.md index 3011b5e..a89a94b 100644 --- a/docz/docs/02-getting-started/03-roadmap.md +++ b/docz/docs/02-getting-started/03-roadmap.md @@ -22,10 +22,25 @@ Most scenarios involving spreadsheets and data can be divided into 5 parts: locally. Data can be presented to users in an HTML TABLE or data grid. A common problem involves generating a valid spreadsheet export from data stored -in an HTML table. In this example, an HTML TABLE on the page will be scraped, -a row will be added to the bottom with the date of the report, and a new file -will be generated and downloaded locally. `XLSX.writeFile` takes care of -packaging the data and attempting a local download: +in an HTML table. + +```mermaid +flowchart LR + server[(Backend\nServer)] + html{{HTML\nTABLE}} + wb(((SheetJS\nWorkbook))) + wb2(((Modified\nWorkbook))) + file[(workbook\nfile)] + server --> |"Get Table (1)\n."| html + html --> |"Parse Table (2)\n`table_to_book`"| wb + wb --> |"Add data (3)\n`sheet_add_aoa`"| wb2 + wb2 --> |"Export file (4,5)\n`writeFile`"| file +``` + +In this example, an HTML TABLE on the page will be scraped, a row will be added +to the bottom with the date of the report, and a new file will be generated and +downloaded locally. `XLSX.writeFile` takes care of packaging the data and +attempting a local download: ```js // Acquire Data (reference to the HTML table) @@ -54,6 +69,9 @@ Utility functions help with step 3. ## Highlights +["Demos"](/docs/demos) describes special deployments using SheetJS in tandem with +other tools and libraries. + ["Data Import"](/docs/solutions/input) describes solutions for common data import scenarios. diff --git a/docz/docs/03-demos/04-static/05-vitejs.md b/docz/docs/03-demos/04-static/05-vitejs.md index d64ad37..946ec8f 100644 --- a/docz/docs/03-demos/04-static/05-vitejs.md +++ b/docz/docs/03-demos/04-static/05-vitejs.md @@ -94,14 +94,22 @@ export default defineConfig({ ``` In frontend code, the loader will look for all modules with a `?sheetjs` -query string. The default export is an array of row objects: +query string. The default export is an array of row objects. + +The following example script displays the data in a table: ```js title="main.js" -import data from './data.xlsx?sheetjs'; +import data from './data/pres.xlsx?sheetjs'; -document.querySelector('#app').innerHTML = `
-  ${data.map(row => JSON.stringify(row)).join("\n")}
-
`; +document.querySelector('#app').innerHTML = ` + + + ${data.map(row => ` + + + `).join("\n")} + +
NameIndex
${row.Name}${row.Index}
`; ``` ### Base64 Plugin @@ -155,17 +163,23 @@ When importing using the `b64` query, the raw Base64 string will be exposed. ```js title="main.js" import { read, utils } from "xlsx"; -/* reference workbook */ +/* import workbook data */ import b64 from './data.xlsx?b64'; -/* parse workbook and export first sheet to CSV */ + +/* parse workbook and pull data from the first worksheet */ const wb = read(b64, { type: "base64" }); const wsname = wb.SheetNames[0]; -const csv = utils.sheet_to_csv(wb.Sheets[wsname]); +const data = utils.sheet_to_json(wb.Sheets[wsname]); -document.querySelector('#app').innerHTML = `
-${wsname}
-${csv}
-
`; +document.querySelector('#app').innerHTML = ` + + + ${data.map(row => ` + + + `).join("\n")} + +
NameIndex
${row.Name}${row.Index}
`; ``` ## Complete Demo @@ -278,7 +292,7 @@ source. The code will reference some script like `/assets/index-HASH.js`. Open that script. Searching for `Bill Clinton` reveals the following: ``` -JSON.parse('[{"Name":"Bill Clinton","Index":42} +{"Name":"Bill Clinton","Index":42} ``` Searching for `BESSELJ` should reveal no results. The SheetJS scripts are not @@ -350,9 +364,9 @@ The SheetJS library is embedded in the final site. [^1]: See ["Using Plugins"](https://vitejs.dev/guide/using-plugins.html) in the ViteJS documentation. [^2]: See ["Static Asset Handling"](https://vitejs.dev/guide/assets.html) in the ViteJS documentation. -[^3]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/#array-output) -[^4]: See [`read` in "Parsing Options"](/docs/api/parse-options) -[^5]: See [`read` in "Parsing Options"](/docs/api/parse-options) -[^6]: See [the "base64" type in "Parsing Options"](/docs/api/parse-options#input-type) +[^3]: See [`read` in "Reading Files"](/docs/api/parse-options) +[^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output) +[^5]: See [`read` in "Reading Files"](/docs/api/parse-options) +[^6]: See [the "base64" type in "Reading Files"](/docs/api/parse-options#input-type) [^7]: See [`SheetJS/sheetjs-vite`](https://git.sheetjs.com/sheetjs/sheetjs-vite/) on the SheetJS git server. [^8]: See ["Server-Side Rendering"](https://vitejs.dev/guide/ssr.html) in the ViteJS documentation. \ No newline at end of file diff --git a/docz/docs/03-demos/06-desktop/03-wails.md b/docz/docs/03-demos/06-desktop/03-wails.md index d07db88..34f2eef 100644 --- a/docz/docs/03-demos/06-desktop/03-wails.md +++ b/docz/docs/03-demos/06-desktop/03-wails.md @@ -1,5 +1,7 @@ --- title: Wails +sidebar_label: Wails +description: Build data-intensive desktop apps using Wails. Seamlessly integrate spreadsheets into your app using SheetJS. Modernize Excel-powered business processes with confidence. pagination_prev: demos/mobile/index pagination_next: demos/data/index sidebar_position: 3 @@ -7,18 +9,28 @@ sidebar_custom_props: summary: Webview + Go Backend --- +# Spreadsheet-Powered Wails Apps + import current from '/version.js'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; -The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported -from JavaScript code. +[Wails](https://wails.io/) is a modern toolkit for building desktop apps. Wails +apps pair a Go-powered backend with a JavaScript-powered frontend[^1]. -The "Complete Example" creates an app that looks like the screenshot: +[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing +data from spreadsheets. + +This demo uses Wails and SheetJS to pull data from a spreadsheet and display the +data in the app. We'll explore how to load SheetJS in a Wails app and exchange +file data between the JavaScript frontend and Go backend. + +The ["Complete Example"](#complete-example) section covers a complete desktop +app to read and write workbooks. The app will look like the screenshots below: - +
Win10Windows macOS Linux
@@ -35,49 +47,71 @@ The "Complete Example" creates an app that looks like the screenshot:
-## Integration Details +:::info -All operations must be run from Go code. This example passes Base64 strings. - -:::caution - -Wails currently does not provide the equivalent of NodeJS `fs` module. All raw -file operations must be performed in Go code. - -The HTML File Input Element does not show a file picker. This is a known bug. -The demo works around the issue by showing pickers in Go code. +This demo assumes some familiarity with JavaScript and with Go. If you would +prefer a pure JavaScript solution, the [Electron](/docs/demos/desktop/electron) +platform provides many native features out of the box. ::: +## Integration Details + +The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be +installed in the `frontend` folder and imported in frontend scripts. + +:::caution + +Wails currently does not provide the equivalent of NodeJS `fs` module. + +Reading and writing raw file data must be implemented in native Go code. + +::: + +This demo includes native Go code for showing dialogs and reading and writing +files. When sending data between Go and JavaScript code, the raw files are +encoded as Base64 strings. + ### Reading Files -The file picker and reading operations can be combined in one Go function. +When the user clicks the "Import File" button, the frontend tells the Go backend +to read data. The user will be presented with a file picker to select a file to +read. The Go backend will read the data, encode as a Base64 string, and send the +result to the frontend. + +The frontend will parse the data using the SheetJS `read` method[^2], generate +HTML tables with `sheet_to_html`[^3], and display the tables on the frontend. + +The following diagram summarizes the steps: ```mermaid sequenceDiagram - autonumber actor User - participant JS - participant Go + participant JS as Frontend (JS) + participant Go as Backend (Go) User->>JS: click button JS->>Go: ask for data Note over Go: Show Open Dialog Note over Go: Read File Bytes Note over Go: Generate Base64 Go->>JS: return data - Note over JS: Parse data - Note over JS: Display Table + Note over JS: Parse Data
`read` + Note over JS: Display Table
`sheet_to_html` JS->>User: app shows data ``` #### Go +The Wails runtime provides the cross-platform `OpenFileDialog` function[^4] to +show a file picker. The Go standard library provides methods for reading data +from the selected file[^5] and encoding in a Base64 string[^6] + ```go import ( "context" // highlight-start "encoding/base64" - "io/ioutil" + "os" "github.com/wailsapp/wails/v2/pkg/runtime" // highlight-end ) @@ -98,7 +132,7 @@ func (a *App) ReadFile() string { }) if err != nil { return "" } // The demo app shows an error message // highlight-next-line - data, err := ioutil.ReadFile(selection) + data, err := os.ReadFile(selection) if err != nil { return "" } // The demo app shows an error message // highlight-next-line return base64.StdEncoding.EncodeToString(data) @@ -107,7 +141,8 @@ func (a *App) ReadFile() string { #### JS -Wails will automatically create bindings for use in JS: +Wails will automatically create bindings for use in JS. The `App` binding module +will export the function `ReadFile`. ```js title="frontend/src/App.svelte" import { read, utils } from 'xlsx'; @@ -115,38 +150,53 @@ import { ReadFile } from '../wailsjs/go/main/App'; async function importFile(evt) { // highlight-start + /* call the native Go function and receive a base64 string */ const b64 = await ReadFile(); + /* parse the base64 string with SheetJS */ const wb = read(b64, { type: "base64" }); // highlight-end + const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet - html = utils.sheet_to_html(ws); // generate HTML and update state + return utils.sheet_to_html(ws); // generate HTML table } ``` ### Writing Files -There is a multi-part dance since the library needs the file extension. +:::info -1) Show the save file picker in Go, pass back to JS +The SheetJS `write` method[^7] can write spreadsheets in a number of formats[^8] +including XLSX, XLSB, XLS, and NUMBERS. It expects a `bookType` option. This +means the frontend needs to know the output file name before creating the file. -2) Generate the file data in JS, pass the data back to Go +::: -3) Write to file in Go +When the user clicks the "Export File" button, the frontend asks the Go backend +for the output filename and path. The user will be presented with a file picker +to select the output folder and workbook type. The backend will send the name +to the frontend. + +The frontend will generate a workbook object from the table using the SheetJS +`table_to_book` method[^9]. The SheetJS `write` method[^10] will generate a +Base64 string from the data. + +The frontend will send the Base64 string to the backend. The backend will write +the data to a file in the selected folder. ```mermaid sequenceDiagram - autonumber actor User - participant JS - participant Go + participant JS as Frontend (JS) + participant Go as Backend (Go) User->>JS: click button JS->>Go: ask for path Note over Go: Show Save Dialog Go->>JS: path to save file - Note over JS: write workbook + Note over JS: Read from Table
`table_to_book` + Note over JS: Write Workbook
`write` JS->>Go: base64-encoded bytes - Note over Go: decode data - Note over Go: write to file + Note over Go: Decode Data + Note over Go: Write to File Go->>JS: write finished JS->>User: alert ``` @@ -155,7 +205,8 @@ sequenceDiagram Two Go functions will be exposed. -- `SaveFile` will show the file picker and return the path: +- `SaveFile` will show the file picker and return the path. It will use the + cross-platform `SaveFileDialog` function[^11]. ```go import ( @@ -183,14 +234,16 @@ func (a *App) SaveFile() string { } ``` -- `WriteFile` performs the file write given a Base64 string and file path: +- `WriteFile` performs the file write given a Base64 string and file path. The + Go standard library provides methods for decoding Base64 strings[^12] and + writing data to the filesystem[^13] ```go import ( "context" // highlight-start "encoding/base64" - "io/ioutil" + "os" // highlight-end ) @@ -201,29 +254,32 @@ type App struct { func (a *App) WriteFile(b64 string, path string) { // highlight-start buf, _ := base64.StdEncoding.DecodeString(b64); - _ = ioutil.WriteFile(path, buf, 0644); + _ = os.WriteFile(path, buf, 0644); // highlight-end } ``` #### JS -Wails will automatically create bindings for use in JS: +Wails will automatically create bindings for use in JS. The `App` binding module +will export the functions `SaveFile` and `WriteFile`: -```js +```js title="frontend/src/App.svelte" import { utils, write } from 'xlsx'; import { SaveFile, WriteFile } from '../wailsjs/go/main/App'; -async function exportFile(wb) { +async function exportFile(table_element) { /* generate workbook */ - const elt = tbl.getElementsByTagName("TABLE")[0]; - const wb = utils.table_to_book(elt); + const wb = utils.table_to_book(table_element); /* show save picker and get path */ const path = await SaveFile(); - /* generate base64 string based on the path */ - const b64 = write(wb, { bookType: path.slice(path.lastIndexOf(".")+1), type: "base64" }); + /* get the file extension -> bookType */ + const bookType = path.slice(path.lastIndexOf(".")+1); + + /* generate base64 string */ + const b64 = write(wb, { bookType: bookType, type: "base64" }); /* write to file */ await WriteFile(b64, path); @@ -239,7 +295,44 @@ the Svelte TypeScript starter. ::: -0) [Read Wails "Getting Started" guide and install dependencies.](https://wails.io/docs/gettingstarted/installation) +0) Read the Wails "Getting Started" guide[^14] and install dependencies. + +
Installation Notes (click to show) + +Wails will require: + +- A recent version of [Go](https://go.dev/doc/install). +- The "LTS" version of [NodeJS](https://nodejs.org/en/download). + +After installing both, run the following command to install Wails: + +```bash +go install github.com/wailsapp/wails/v2/cmd/wails@latest +``` + +Once that finishes, run the following command in a new terminal window: + +```bash +wails doctor +``` + +The output will include a `# Diagnosis` section. It should display: + +``` +# Diagnosis + +Your system is ready for Wails development! +``` + +If a required dependency is missing, it will be displayed. + +:::note + +None of the optional packages are required for building and running this demo. + +::: + +
1) Create a new Wails app: @@ -280,3 +373,18 @@ wails build ``` At the end, it will print the path to the generated program. Run the program! + +[^1]: See ["How does it Work?"](https://wails.io/docs/howdoesitwork) in the Wails documentation. +[^2]: See [`read` in "Parsing Options"](/docs/api/parse-options) +[^3]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output) +[^4]: See [`OpenFileDialog`](https://wails.io/docs/reference/runtime/dialog#openfiledialog) in the Wails documentation. +[^5]: See [`ReadFile`](https://pkg.go.dev/os#ReadFile) in the Go documentation +[^6]: See [`EncodeToString`](https://pkg.go.dev/encoding/base64#Encoding.EncodeToString) in the Go documentation +[^7]: See [`write` in "Writing Files"](/docs/api/write-options) +[^8]: See ["Supported Output Formats" type in "Writing Files"](/docs/api/write-options#supported-output-formats) +[^9]: See ["HTML Table Input" in "Utilities"](/docs/api/utilities/html#create-new-sheet) +[^10]: See [`write` in "Writing Files"](/docs/api/write-options) +[^11]: See [`SaveFileDialog`](https://wails.io/docs/reference/runtime/dialog#savefiledialog) in the Wails documentation. +[^12]: See [`DecodeString`](https://pkg.go.dev/encoding/base64#Encoding.DecodeString) in the Go documentation +[^13]: See [`WriteFile`](https://pkg.go.dev/os#WriteFile) in the Go documentation +[^14]: See ["Installation"](https://wails.io/docs/gettingstarted/installation) in the Wails documentation. \ No newline at end of file diff --git a/docz/docs/03-demos/12-engines/08_quickjs.md b/docz/docs/03-demos/12-engines/08_quickjs.md index 33a5f9e..75240cb 100644 --- a/docz/docs/03-demos/12-engines/08_quickjs.md +++ b/docz/docs/03-demos/12-engines/08_quickjs.md @@ -1,36 +1,66 @@ --- title: C + QuickJS +sidebar_label: C + QuickJS +description: Process structured data in C programs. Seamlessly integrate spreadsheets into your program by pairing QuickJS and SheetJS. Supercharge programs with modern data tools. pagination_prev: demos/bigdata/index pagination_next: solutions/input --- +# Data Processing with QuickJS + import current from '/version.js'; import CodeBlock from '@theme/CodeBlock'; -QuickJS is an embeddable JS engine written in C. It provides a separate set of -functions for interacting with the filesystem and the global object. It can run -the standalone browser scripts. +[QuickJS](https://bellard.org/quickjs/) is an embeddable JS engine written in C. +It has built-in support for reading and writing file data stored in memory. -The [Standalone scripts](/docs/getting-started/installation/standalone) can be -parsed and evaluated in a QuickJS context. +[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing +data from spreadsheets. + +This demo uses QuickJS and SheetJS to pull data from a spreadsheet and print CSV +rows. We'll explore how to load SheetJS in a QuickJS context and process +spreadsheets from C programs. + +The ["Integration Example"](#integration-example) section includes a complete +command-line tool for reading data from files. ## Integration Details -_Initialize QuickJS_ +:::note + +Many QuickJS functions are not documented. The explanation was verified against +the latest release (version `2021-03-27`, commit `2788d71`). + +::: + +### Initialize QuickJS + +Most QuickJS API functions interact with a `JSContext` object[^1], which is +normally created with `JS_NewRuntime` and `JS_NewContext`: + +```c +#include "quickjs.h" + +/* initialize context */ +JSRuntime *rt = JS_NewRuntime(); +JSContext *ctx = JS_NewContext(rt); +``` QuickJS provides a `global` object through `JS_GetGlobalObject`: ```c -/* initialize */ -JSRuntime *rt = JS_NewRuntime(); -JSContext *ctx = JS_NewContext(rt); - /* obtain reference to global object */ JSValue global = JS_GetGlobalObject(ctx); +``` -/* DO WORK HERE */ +
Cleanup (click to show) -/* free after use */ +Once finished, programs are expected to cleanup by using `JS_FreeValue` to free +values, `JS_FreeContext` to free the context pointer, and `JS_FreeRuntime` to +free the runtime: + +```c +/* global is a JSValue */ JS_FreeValue(ctx, global); /* cleanup */ @@ -38,27 +68,23 @@ JS_FreeContext(ctx); JS_FreeRuntime(rt); ``` -:::warning +The [Integration Example](#integration-example) frees JS values after use. -All values must be freed with `JS_FreeValue` before calling `JS_FreeContext`! +
-`JS_IsException` should be used for validation. +### Load SheetJS Scripts -Cleanup and validation code is omitted from the discussion. The integration -example shows structured validation and controlled memory usage. - -::: - -_Load SheetJS Scripts_ +[SheetJS Standalone scripts](/docs/getting-started/installation/standalone) can +be loaded and executed in QuickJS. The main library can be loaded by reading the script from the file system and -evaluating in the QuickJS context: +evaluating in the QuickJS context using `JS_Eval`: ```c static char *read_file(const char *filename, size_t *sz) { FILE *f = fopen(filename, "rb"); if(!f) return NULL; - long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fsee (f, 0, SEEK_SET); } + long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); } char *buf = (char *)malloc(fsize * sizeof(char)); *sz = fread((void *) buf, 1, fsize, f); fclose(f); @@ -66,22 +92,37 @@ static char *read_file(const char *filename, size_t *sz) { } // ... - /* load library */ { + /* Read `xlsx.full.min.js` from the filesystem */ size_t len; char *buf = read_file("xlsx.full.min.js", &len); + /* evaluate from the QuickJS context */ JS_Eval(ctx, buf, len, "", 0); + /* Free the file buffer */ free(buf); } ``` -To confirm the library is loaded, `XLSX.version` can be inspected: +If the library is loaded, `XLSX.version` will be a string. This string can be +pulled into the main C program. + +1) Get the `XLSX` property of the global object using `JS_GetPropertyStr`: ```c /* obtain reference to the XLSX object */ JSValue XLSX = JS_GetPropertyStr(ctx, global, "XLSX"); +``` -/* print version */ +2) Get the `version` property of the `XLSX` object using `JS_GetPropertyStr`: + +```c +/* obtain reference to `XLSX.version` */ JSValue version = JS_GetPropertyStr(ctx, XLSX, "version"); +``` + +3) Pull the string into C code with `JS_ToCStringLen`: + +```c +/* pull the version string into C */ size_t vlen; const char *vers = JS_ToCStringLen(ctx, &vlen, version); printf("Version: %s\n", vers); ``` @@ -97,16 +138,119 @@ size_t dlen; uint8_t * dbuf = (uint8_t *)read_file("pres.numbers", &dlen); /* load data into array buffer */ JSValue ab = JS_NewArrayBuffer(ctx, dbuf, dlen, NULL, NULL, 0); +``` -/* obtain reference to the XLSX object */ +The `ArrayBuffer` will be parsed with the SheetJS `read` method[^2]. The CSV row +data will be generated with `sheet_to_csv`[^3]. + +#### Parse the ArrayBuffer + +:::note pass + +The goal is to run the equivalent of the following JavaScript code: + +```js +/* `ab` is the `ArrayBuffer` from the previous step */ +var wb = XLSX.read(ab); +``` + +::: + +1) Get the `XLSX` property of the global object and the `read` property of `XLSX`: + +```c +/* obtain reference to XLSX.read */ JSValue XLSX = JS_GetPropertyStr(ctx, global, "XLSX"); - -/* call XLSX.read(ab) */ JSValue XLSX_read = JS_GetPropertyStr(ctx, XLSX, "read"); +``` + +2) Create an array of arguments to pass to the function. In this case, the +`read` function will be called with one argument (`ArrayBuffer` data): + +```c +/* prepare arguments */ JSValue args[] = { ab }; +``` + +3) Use `JS_Call` to call the function with the arguments: + +```c +/* call XLSX.read(ab) */ JSValue wb = JS_Call(ctx, XLSX_read, XLSX, 1, args); ``` +#### Get First Worksheet + +:::note pass + +The goal is to get the first worksheet. In JavaScript, the `SheetNames` property +of the workbook is an array of strings and the `Sheets` property holds worksheet +objects[^4]. The desired action looks like: + +```js +/* `wb` is the workbook from the previous step */ +var wsname = wb.SheetNames[0]; +var ws = wb.Sheets[wsname]; +``` + +::: + +4) Pull `wb.SheetNames[0]` into a C string using `JS_GetPropertyStr`: + +```c +/* get `wb.SheetNames[0]` */ +JSValue SheetNames = JS_GetPropertyStr(ctx, wb, "SheetNames"); +JSValue Sheet1 = JS_GetPropertyStr(ctx, SheetNames, "0"); + +/* pull first sheet name into C code */ +size_t wslen; const char *wsname = JS_ToCStringLen(ctx, &wslen, Sheet1); +``` + +5) Get the worksheet object: + +```c +/* get wb.Sheets[wsname] */ +JSValue Sheets = JS_GetPropertyStr(ctx, wb, "Sheets"); +JSValue ws = JS_GetPropertyStr(ctx, Sheets, wsname); +``` + +#### Convert to CSV + +:::note pass + +The goal is to call `sheet_to_csv`[^5] and pull the result into C code: + +```js +/* `ws` is the worksheet from the previous step */ +var csv = XLSX.utils.sheet_to_csv(ws); +``` + +::: + +6) Create a references to `XLSX.utils` and `XLSX.utils.sheet_to_csv`: + +```c +/* obtain reference to XLSX.utils.sheet_to_csv */ +JSValue utils = JS_GetPropertyStr(ctx, XLSX, "utils"); +JSValue sheet_to_csv = JS_GetPropertyStr(ctx, utils, "sheet_to_csv"); +``` + +7) Create arguments array: + +```c +/* prepare arguments */ +JSValue args[] = { ws }; +``` + +8) Use `JS_Call` to call the function and use `JS_ToCStringLen` to pull the CSV: + +```c +JSValue csv = JS_Call(ctx, sheet_to_csv, utils, 1, args); +size_t csvlen; const char *csvstr = JS_ToCStringLen(ctx, &csvlen, csv); +``` + +At this point, `csvstr` is a C string that can be printed to standard output. + ## Complete Example The "Integration Example" covers a traditional integration in a C application, @@ -124,6 +268,8 @@ This demo was tested in the following deployments: | `darwin-arm` | `2788d71` | 2023-06-05 | | `linux-x64` | `2788d71` | 2023-06-02 | +Git commit `2788d71` corresponds to the latest release (`2021-03-27`) + ::: 0) Build `libquickjs.a`: @@ -171,7 +317,7 @@ curl -LO https://sheetjs.com/pres.numbers`} 5) Run the test program: -``` +```bash ./sheetjs.quick pres.numbers ``` @@ -215,3 +361,9 @@ quickjs SheetJSQuick.js If successful, the script will generate `SheetJSQuick.xlsx`. + +[^1]: See ["Runtime and Contexts"](https://bellard.org/quickjs/quickjs.html#Runtime-and-contexts) in the QuickJS documentation +[^2]: See [`read` in "Reading Files"](/docs/api/parse-options) +[^3]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output) +[^4]: See ["Workbook Object" in "SheetJS Data Model"](/docs/csf/book) +[^5]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output) diff --git a/docz/docs/03-demos/12-engines/09_hermes.md b/docz/docs/03-demos/12-engines/09_hermes.md index 9d66097..a3dd398 100644 --- a/docz/docs/03-demos/12-engines/09_hermes.md +++ b/docz/docs/03-demos/12-engines/09_hermes.md @@ -31,7 +31,22 @@ std::unique_ptr rt(facebook::hermes::makeHermesRuntime() ``` Hermes does not expose a `console` or `global` variable, but those can be -synthesized from JS code in the runtime: +synthesized from JS code in the runtime. + +:::note pass + +The JavaScript code is shown below: + +```js +/* create global object */ +var global = (function(){ return this; }).call(null); +/* create a fake `console` from the hermes `print` builtin */ +var console = { log: function(x) { print(x); } }; +``` + +::: + +The code can be stored in a C string and evaluated after creating a runtime: ```cpp auto src = std::make_shared( @@ -96,7 +111,7 @@ Hermes supports `ArrayBuffer` but has no simple helper to read raw memory. Libraries are expected to implement `MutableBuffer`: ```cpp -/* ArrayBuffer constructor expects MutableBuffer*/ +/* ArrayBuffer constructor expects MutableBuffer */ class CMutableBuffer : public facebook::jsi::MutableBuffer { public: CMutableBuffer(uint8_t *data, size_t size) : buf(data), sz(size) {} @@ -117,8 +132,10 @@ class CMutableBuffer : public facebook::jsi::MutableBuffer { It is strongly recommended to create a stub function to perform the entire workflow in JS code and pass the final result back to C++. -> _JS Stub function_ -> +:::note pass + +The stub function will be passed an `ArrayBuffer` object: + ```js function(buf) { /* `buf` will be an ArrayBuffer */ @@ -127,6 +144,8 @@ function(buf) { } ``` +::: + _C++ integration code_ ```cpp diff --git a/docz/docs/07-csf/01-general.md b/docz/docs/07-csf/01-general.md index 1265617..359c0f3 100644 --- a/docz/docs/07-csf/01-general.md +++ b/docz/docs/07-csf/01-general.md @@ -3,19 +3,102 @@ sidebar_position: 1 title: Addresses and Ranges --- +export const g = {style: {backgroundColor:"green"}}; + +Each cell in a worksheet has a unique address which specifies the row and the +column that include the cell. + +## Basic Concepts + +### Rows + +Spreadsheet applications typically display ordinal row numbers, where `1` is the +first row, `2` is the second row, etc. The numbering starts at `1`. + +SheetJS follows JavaScript counting conventions, where `0` is the first row, `1` +is the second row, etc. The numbering starts at `0`. + +The following table lists some example row labels: + +| Ordinal | Row Label | SheetJS | +|:----------|----------:|----------:| +| First | `1` | `0` | +| Second | `2` | `1` | +| 26th | `26` | `25` | +| 420th | `420` | `419` | +| 7262nd | `7262` | `7261` | +| 1048576th | `1048576` | `1048575` | + +### Columns + +Spreadsheet applications typically use letters to represent columns. + +The first column is `A`, the second column is `B`, and the 26th column is `Z`. +After `Z`, the next column is `AA` and counting continues through `AZ`. After +`AZ`, the count continues with `BA`. After `ZZ`, the count continues with `AAA`. + +Some sample values, along with SheetJS column indices, are listed below: + +| Ordinal | Column Label | SheetJS | +|:--------|:-------------|--------:| +| First | `A` | `0` | +| Second | `B` | `1` | +| 26th | `Z` | `25` | +| 27th | `AA` | `26` | +| 420th | `PD` | `419` | +| 702nd | `ZZ` | `701` | +| 703rd | `AAA` | `702` | +| 7262nd | `JSH` | `7261` | +| 16384th | `XFD` | `16383` | + ## Cell Addresses +### A1-Style + +A1-Style is the default address style in Lotus 1-2-3 and Excel. + +A cell address is the concatenation of column label and row label. + +For example, the cell in the third column and fourth row is `C4`, concatenating +the third column label (`C`) and the fourth row label (`4`) + +### SheetJS Cell Address + Cell address objects are stored as `{c:C, r:R}` where `C` and `R` are 0-indexed column and row numbers, respectively. For example, the cell address `B5` is represented by the object `{c:1, r:4}`. ## Cell Ranges +### A1-Style + +A cell range is represented as the top-left cell of the range, followed by `:`, +followed by the bottom-right cell of the range. For example, the range `"C2:D4"` +includes the 6 green cells in the following table: + + + + + + + + +
ABCDE
1
2
3
4
5
+ +A column range is represented by the left-most column, followed by `:`, followed +by the right-most column. For example, the range `C:D` represents the third and +fourth columns. + +A row range is represented by the top-most row, followed by `:`, followed by the +bottom-most column. For example, `2:4` represents the second/third/fourth rows. + +### SheetJS Range + Cell range objects are stored as `{s:S, e:E}` where `S` is the first cell and `E` is the last cell in the range. The ranges are inclusive. For example, the range `A3:B7` is represented by the object `{s:{c:0, r:2}, e:{c:1, r:6}}`. -### Column and Row Ranges +#### Column and Row Ranges A column range (spanning every row) is represented with the starting row `0` and the ending row `1048575`: @@ -33,54 +116,9 @@ the ending col `16383`: { s: { c: 0, r: 1 }, e: { c: 16383, r: 2 } } // 2:3 ``` -# Common Spreadsheet Address Styles +## Utilities -## A1-Style - -A1-Style is the default address style in Lotus 1-2-3 and Excel. - -Columns are specified with letters, counting from `A` to `Z`, then `AA` to `ZZ`, -then `AAA`. Some sample values, along with SheetJS column indices, are listed: - -| Ordinal | `A1` | SheetJS | -|:--------|:--------|--------:| -| First | `A` | `0` | -| Second | `B` | `1` | -| 26th | `Z` | `25` | -| 27th | `AA` | `26` | -| 702nd | `ZZ` | `701` | -| 703rd | `AAA` | `702` | -| 16384th | `XFD` | `16383` | - -Rows are specified with numbers, starting from `1` for the first row. SheetJS -APIs that take row indices start from `0` (ECMAScript convention). - -A cell address is the concatenation of column text and row number. For example, -the cell in the third column and fourth row is "C4". - -A cell range is represented as the top-left cell of the range, followed by `:`, -followed by the bottom-right cell of the range. For example, the range `"C2:D4"` -includes 6 cells marked with ▒ in the table below: - - - - - - - - -
ABCDE
1
2
3
4
5
- -A column range is represented by the left-most column, followed by `:`, followed -by the right-most column. For example, the range `C:D` represents the third and -fourth columns. - -A row range is represented by the top-most row, followed by `:`, followed by the -bottom-most column. For example, `2:4` represents the second/third/fourth rows. - -### Utilities - -#### Column Names +### Column Names _Get the SheetJS index from an A1-Style column_ @@ -98,7 +136,7 @@ var col_name = XLSX.utils.encode_col(3); The argument is expected to be a SheetJS column (non-negative integer). -#### Row Names +### Row Names _Get the SheetJS index from an A1-Style row_ @@ -116,7 +154,7 @@ var row_name = XLSX.utils.encode_row(3); The argument is expected to be a SheetJS column (non-negative integer). -#### Cell Addresses +### Cell Addresses _Generate a SheetJS cell address from an A1-Style address string_ @@ -134,7 +172,7 @@ var a1_addr = XLSX.utils.encode_cell({r:1, c:0}); The argument is expected to be a SheetJS cell address -#### Cell Ranges +### Cell Ranges _Generate a SheetJS cell range from an A1-Style range string_ diff --git a/docz/docs/07-csf/07-features/index.md b/docz/docs/07-csf/07-features/index.md index d292249..2d5209b 100644 --- a/docz/docs/07-csf/07-features/index.md +++ b/docz/docs/07-csf/07-features/index.md @@ -104,6 +104,7 @@ Read functions attempt to populate all three properties. Write functions will try to cycle specified values to the desired type. In order to avoid potential conflicts, manipulation should delete the other properties first. For example, when changing the pixel width, delete the `wch` and `width` properties. +
@@ -222,10 +223,6 @@ function Visibility(props) { ))}); } - ``` - -
- diff --git a/docz/docs/07-csf/index.md b/docz/docs/07-csf/index.md index 50dcaef..81a7576 100644 --- a/docz/docs/07-csf/index.md +++ b/docz/docs/07-csf/index.md @@ -7,9 +7,12 @@ title: Common Spreadsheet Format import DocCardList from '@theme/DocCardList'; import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; -The "Common Spreadsheet Format" is the object model used by SheetJS. This -section covers the JS representation of workbooks, worksheets, cells, ranges, -addresses and other features. +The "Common Spreadsheet Format" is the object model used by SheetJS. The library +[includes a number of API functions](/docs/api) for common operations, but some +features are only accessible by inspecting and modifying the objects directly. + +This section covers the JS representation of workbooks, worksheets, cells, +ranges, addresses and other features. ### Contents diff --git a/docz/docs/08-api/index.md b/docz/docs/08-api/index.md index 3b53c10..fd5ef45 100644 --- a/docz/docs/08-api/index.md +++ b/docz/docs/08-api/index.md @@ -4,15 +4,26 @@ sidebar_position: 5 title: API Reference --- -## Interface Summary +import current from '/version.js'; -`XLSX` is the exposed variable in the browser and the exported node variable +This section lists the functions defined in the library. -`XLSX.version` is the version of the library (added by the build script). +Using the ["Standalone" scripts](/docs/getting-started/installation/standalone), +`XLSX` is added to the `window` or other `global` object. -`XLSX.SSF` is an embedded version of the [format library](https://git.sheetjs.com/sheetjs/sheetjs/src/branch/master/packages/ssf). +Using the ["NodeJS" module](/docs/getting-started/installation/nodejs), the +`XLSX` variable refers to the CommonJS export: -`XLSX.CFB` is an embedded version of the [container library](https://git.sheetjs.com/sheetjs/js-cfb). +```js +var XLSX = require("xlsx"); +``` + +Using [a framework](/docs/getting-started/installation/frameworks), the `XLSX` +variable refers to the glob import: + +```js +import * as XLSX from "xlsx"; +``` ## Parsing functions @@ -127,4 +138,20 @@ for different languages in XLS or text parsing. provides NodeJS ESM support for `XLSX.readFile` and `XLSX.writeFile`. `XLSX.utils.set_readable` supplies a NodeJS `stream.Readable` constructor. This -provides NodeJS ESM support for the streaming operations. \ No newline at end of file +provides NodeJS ESM support for the streaming operations. + +ESM helper functions are described in the ["NodeJS" Installation section](/docs/getting-started/installation/nodejs) + +## Miscellaneous + +`XLSX.version` is the version of the library. + +:::note pass + +

The current version is {current}

+ +::: + +`XLSX.SSF` is an embedded version of the [format library](https://git.sheetjs.com/sheetjs/sheetjs/src/branch/master/packages/ssf). + +`XLSX.CFB` is an embedded version of the [container library](https://git.sheetjs.com/sheetjs/js-cfb). diff --git a/docz/docusaurus.config.js b/docz/docusaurus.config.js index 8142b3b..12b64cb 100644 --- a/docz/docusaurus.config.js +++ b/docz/docusaurus.config.js @@ -34,6 +34,7 @@ const config = { ({ docs: { sidebarPath: require.resolve('./sidebars.js'), + showLastUpdateTime: true, editUrl: 'https://git.sheetjs.com/sheetjs/docs.sheetjs.com/src/branch/master/docz', }, //blog: { diff --git a/docz/src/theme/Admonition/index.js b/docz/src/theme/Admonition/index.js new file mode 100644 index 0000000..02f1093 --- /dev/null +++ b/docz/src/theme/Admonition/index.js @@ -0,0 +1,180 @@ +import React from 'react'; +import clsx from 'clsx'; +import {ThemeClassNames} from '@docusaurus/theme-common'; +import Translate from '@docusaurus/Translate'; +import styles from './styles.module.css'; +function NoteIcon() { + return ( + + + + ); +} +function TipIcon() { + return ( + + + + ); +} +function DangerIcon() { + return ( + + + + ); +} +function InfoIcon() { + return ( + + + + ); +} +function CautionIcon() { + return ( + + + + ); +} +// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style +const AdmonitionConfigs = { + note: { + infimaClassName: 'secondary', + iconComponent: NoteIcon, + label: ( + + note + + ), + }, + tip: { + infimaClassName: 'success', + iconComponent: TipIcon, + label: ( + + tip + + ), + }, + danger: { + infimaClassName: 'danger', + iconComponent: DangerIcon, + label: ( + + danger + + ), + }, + info: { + infimaClassName: 'info', + iconComponent: InfoIcon, + label: ( + + info + + ), + }, + caution: { + infimaClassName: 'warning', + iconComponent: CautionIcon, + label: ( + + caution + + ), + }, +}; +// Legacy aliases, undocumented but kept for retro-compatibility +const aliases = { + secondary: 'note', + important: 'info', + success: 'tip', + warning: 'danger', +}; +function getAdmonitionConfig(unsafeType) { + const type = aliases[unsafeType] ?? unsafeType; + const config = AdmonitionConfigs[type]; + if (config) { + return config; + } + console.warn( + `No admonition config found for admonition type "${type}". Using Info as fallback.`, + ); + return AdmonitionConfigs.info; +} +// Workaround because it's difficult in MDX v1 to provide a MDX title as props +// See https://github.com/facebook/docusaurus/pull/7152#issuecomment-1145779682 +function extractMDXAdmonitionTitle(children) { + const items = React.Children.toArray(children); + const mdxAdmonitionTitle = items.find( + (item) => + React.isValidElement(item) && + item.props?.mdxType === 'mdxAdmonitionTitle', + ); + const rest = <>{items.filter((item) => item !== mdxAdmonitionTitle)}; + return { + mdxAdmonitionTitle, + rest, + }; +} +function processAdmonitionProps(props) { + const {mdxAdmonitionTitle, rest} = extractMDXAdmonitionTitle(props.children); + return { + ...props, + title: props.title ?? mdxAdmonitionTitle, + children: rest, + }; +} +export default function Admonition(props) { + const {children, type, title, icon: iconProp} = processAdmonitionProps(props); + const typeConfig = getAdmonitionConfig(type); + const titleLabel = title ?? typeConfig.label; + const {iconComponent: IconComponent} = typeConfig; + const icon = iconProp ?? ; + return ( +
+ {titleLabel == "pass" ? void 0 : ( +
+ {icon} + {titleLabel} +
+ )} +
{children}
+
+ ); +} +/* See docusaurus issue 8568 -- this was swizzled against 2.4.1 */ \ No newline at end of file diff --git a/docz/src/theme/Admonition/styles.module.css b/docz/src/theme/Admonition/styles.module.css new file mode 100644 index 0000000..634c9dc --- /dev/null +++ b/docz/src/theme/Admonition/styles.module.css @@ -0,0 +1,31 @@ +.admonition { + margin-bottom: 1em; +} + +.admonitionHeading { + font: var(--ifm-heading-font-weight) var(--ifm-h5-font-size) / + var(--ifm-heading-line-height) var(--ifm-heading-font-family); + text-transform: uppercase; + margin-bottom: 0.3rem; +} + +.admonitionHeading code { + text-transform: none; +} + +.admonitionIcon { + display: inline-block; + vertical-align: middle; + margin-right: 0.4em; +} + +.admonitionIcon svg { + display: inline-block; + height: 1.6em; + width: 1.6em; + fill: var(--ifm-alert-foreground-color); +} + +.admonitionContent > :last-child { + margin-bottom: 0; +} diff --git a/docz/static/vitejs/table.png b/docz/static/vitejs/table.png new file mode 100644 index 0000000..b29b9a6 Binary files /dev/null and b/docz/static/vitejs/table.png differ