--- title: Data Processing with Duktape sidebar_label: C + Duktape description: Process structured data in C programs. Seamlessly integrate spreadsheets into your program by pairing Duktape and SheetJS. Supercharge programs with modern data tools. pagination_prev: demos/bigdata/index pagination_next: solutions/input --- import current from '/version.js'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; [Duktape](https://duktape.org) is an embeddable JS engine written in C. It has been ported to a number of exotic architectures and operating systems. [SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing data from spreadsheets. The ["Complete Example"](#complete-example) section includes a complete command-line tool for reading data from spreadsheets and exporting to Excel XLSB workbooks. ["Bindings"](#bindings) covers bindings for other ecosystems. ## Integration Details ### Initialize Duktape Duktape does not provide a `global` variable. It can be created in one line: ```c /* initialize */ duk_context *ctx = duk_create_heap_default(); /* duktape does not expose a standard "global" by default */ // highlight-next-line duk_eval_string_noresult(ctx, "var global = (function(){ return this; }).call(null);"); ``` ### Load SheetJS Scripts The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone) can be parsed and evaluated in a Duktape context. The shim and main libraries can be loaded by reading the scripts from the file system and evaluating in the Duktape context: ```c /* simple wrapper to read the entire script file */ static duk_int_t eval_file(duk_context *ctx, const char *filename) { size_t len; /* read script from filesystem */ FILE *f = fopen(filename, "rb"); if(!f) { duk_push_undefined(ctx); perror("fopen"); return 1; } long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); } char *buf = (char *)malloc(fsize * sizeof(char)); len = fread((void *) buf, 1, fsize, f); fclose(f); if(!buf) { duk_push_undefined(ctx); perror("fread"); return 1; } // highlight-start /* load script into the context */ duk_push_lstring(ctx, (const char *)buf, (duk_size_t)len); /* eval script */ duk_int_t retval = duk_peval(ctx); /* cleanup */ duk_pop(ctx); // highlight-end return retval; } // ... duk_int_t res = 0; if((res = eval_file(ctx, "shim.min.js")) != 0) { /* error handler */ } if((res = eval_file(ctx, "xlsx.full.min.js")) != 0) { /* error handler */ } ``` To confirm the library is loaded, `XLSX.version` can be inspected: ```c /* get version string */ duk_eval_string(ctx, "XLSX.version"); printf("SheetJS library version %s\n", duk_get_string(ctx, -1)); duk_pop(ctx); ``` ### Reading Files Duktape supports `Buffer` natively but should be sliced before processing. Assuming `buf` is a C byte array, with length `len`, this snippet parses data: ```c /* load C char array and save to a Buffer */ duk_push_external_buffer(ctx); duk_config_buffer(ctx, -1, buf, len); duk_put_global_string(ctx, "buf"); /* parse with SheetJS */ duk_eval_string_noresult(ctx, "workbook = XLSX.read(buf.slice(0, buf.length), {type:'buffer'});"); ``` `workbook` will be a variable in the JS environment that can be inspected using the various SheetJS API functions. ### Writing Files `duk_get_buffer_data` can pull `Buffer` object data into the C code: ```c /* write with SheetJS using type: "array" */ duk_eval_string(ctx, "XLSX.write(workbook, {type:'array', bookType:'xlsx'})"); /* pull result back to C */ duk_size_t sz; char *buf = (char *)duk_get_buffer_data(ctx, -1, sz); /* discard result in duktape */ duk_pop(ctx); ``` The resulting `buf` can be written to file with `fwrite`. ## Complete Example :::note Tested Deployments This demo was tested in the following deployments: | Architecture | Version | Date | |:-------------|:--------|:-----------| | `darwin-x64` | `2.7.0` | 2026-01-21 | | `darwin-arm` | `2.7.0` | 2025-09-03 | | `win11-x64` | `2.7.0` | 2025-04-28 | | `win11-arm` | `2.7.0` | 2025-02-23 | | `linux-x64` | `2.7.0` | 2025-04-21 | | `linux-arm` | `2.7.0` | 2025-02-15 | ::: This program parses a file and prints CSV data from the first worksheet. It also generates an XLSB file and writes to the filesystem. The [flow diagram is displayed after the example steps](#flow-diagram) :::note pass The Windows build requires Visual Studio with "Desktop development with C++". **Commands must be run in a "Native Tools Command Prompt" session.** ::: 0) Create a project folder: ```bash mkdir sheetjs-duk cd sheetjs-duk ``` 1) Download and extract Duktape: :::caution pass The Windows built-in `tar` does not support `xz` archives. **The commands must be run within WSL `bash`.** After the `mv` command, exit WSL. ::: ```bash curl -LO https://duktape.org/duktape-2.7.0.tar.xz tar -xJf duktape-2.7.0.tar.xz mv duktape-2.7.0/src/*.{c,h} . ``` 2) Download the SheetJS Standalone script, shim script and test file. Move all three files to the project directory: :::caution pass If the `curl` command fails, run the commands within WSL `bash`. ::: {`\ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js curl -LO https://docs.sheetjs.com/pres.numbers`} 3) Download [`sheetjs.duk.c`](pathname:///duk/sheetjs.duk.c): ```bash curl -LO https://docs.sheetjs.com/duk/sheetjs.duk.c ``` 4) Compile standalone `sheetjs.duk` binary ```bash gcc -std=c99 -Wall -osheetjs.duk sheetjs.duk.c duktape.c -lm ``` :::note pass GCC may generate a warning: ``` duk_js_compiler.c:5628:13: warning: variable 'num_stmts' set but not used [-Wunused-but-set-variable] duk_int_t num_stmts; ^ ``` This warning can be ignored. ::: ```powershell cl sheetjs.duk.c duktape.c /I .\ ``` 5) Run the demo: ```bash ./sheetjs.duk pres.numbers ``` ```bash .\sheetjs.duk.exe pres.numbers ``` If the program succeeded, the CSV contents will be printed to console and the file `sheetjsw.xlsb` will be created. That file can be opened with Excel. ### Flow Diagram ```mermaid sequenceDiagram participant F as Filesystem participant C as C Code participant D as Duktape activate C opt Note over F,D: ~ Prepare Duktape ~ C->>+D: Initialize deactivate C D->>-C: Done activate C C->>F: Need SheetJS F->>C: SheetJS Code C->>+D: Load SheetJS Code deactivate C D->>-C: Loaded activate C C->>+D: Execute Code deactivate C Note over D: Eval SheetJS Code D->>-C: Done activate C Note over D: XLSX
ready to rock end opt Note over F,D: ~ Parse File ~ C->>F: Read Spreadsheet F->>C: Spreadsheet File C->>+D: Load Data deactivate C D->>-C: Loaded activate C C->>+D: eval `var workbook = XLSX.read(...)` deactivate C Note over D: Parse File D->>-C: Done activate C Note over D: `workbook`
can be used later end opt Note over F,D: ~ Print CSV to screen ~ C->>+D: eval `XLSX.utils.sheet_to_csv(...)` deactivate C Note over D: Generate CSV D->>-C: CSV Data activate C Note over C: Print to standard output end opt Note over F,D: ~ Write XLSB File ~ C->>+D: eval `XLSX.write(...)` deactivate C Note over D: Generate File D->>-C: done activate C C->>+D: get file bytes deactivate C D->>-C: binary data activate C C->>F: Write File end deactivate C ``` ## Bindings Bindings exist for many languages. As these bindings require "native" code, they may not work on every platform. The Duktape source distribution includes a separate Makefile for building a shared library. This library can be loaded in other programs. #### Blingos Duktape includes a number of "blingos" (function-like macros) which will not be included in the shared library. The macros must be manually expanded. For example, `duk_create_heap_default` is defined as follows: ```c #define duk_create_heap_default() \ duk_create_heap(NULL, NULL, NULL, NULL, NULL) ``` The `duk_create_heap_default` blingo will not be defined in the shared library. Instead, `duk_create_heap` must be called directly. Using PHP FFI: ```php /* create new FFI object */ $ffi = FFI::cdef(/* ... arguments */); /* call duk_create_heap directly */ // highlight-next-line $context = $ffi->duk_create_heap(null, null, null, null, null); ``` #### Null Pointers The C `NULL` pointer must be used in some functions. Some FFI implementations have special values distinct from the language-native null value. Using Python, return type hints are specified with the `restype` property: ```py from ctypes import CDLL, c_void_p duk = CDLL("libduktape.so") # highlight-next-line duk.duk_create_heap.restype = c_void_p context = duk.duk_create_heap(None, None, None, None, None) ``` ### PHP There is no official PHP binding to the Duktape library. Instead, this demo uses the raw `FFI` interface[^1] to the Duktape shared library. The [`SheetJSDuk.php`](pathname:///duk/SheetJSDuk.php) demo script parses a file, prints CSV rows from the first worksheet, and creates a XLSB workbook. #### PHP Demo :::note Tested Deployments This demo was tested in the following deployments: | Architecture | Version | PHP | Date | |:-------------|:--------|:---------|:-----------| | `darwin-x64` | `2.7.0` | `8.4.11` | 2026-01-21 | | `darwin-arm` | `2.7.0` | `8.4.8` | 2026-01-23 | | `linux-x64` | `2.7.0` | `8.3.6` | 2025-04-21 | | `linux-arm` | `2.7.0` | `8.2.26` | 2025-02-15 | ::: 0) Ensure `php` is installed and available on the system path 1) Find the `php.ini` file: ```bash php --ini ``` The following output is from the most recent `darwin-x64` test: ```text pass Configuration File (php.ini) Path: /usr/local/etc/php/8.4 // highlight-next-line Loaded Configuration File: /usr/local/etc/php/8.4/php.ini Scan for additional .ini files in: /usr/local/etc/php/8.4/conf.d Additional .ini files parsed: /usr/local/etc/php/8.4/conf.d/ext-opcache.ini ``` 2) Edit the `php.ini` configuration file. The following line should appear in the configuration: ```ini title="php.ini (add to end)" extension=ffi ``` If this line is prefixed with a `;`, remove the semicolon. If this line does not appear in the file, add it to the end. :::note pass On Linux and macOS, the file may be owned by the `root` user. If writing the file fails with a normal user account, use `sudo` to launch the text editor. ::: 3) Build the Duktape shared library: ```bash curl -LO https://duktape.org/duktape-2.7.0.tar.xz tar -xJf duktape-2.7.0.tar.xz cd duktape-2.7.0 make -f Makefile.sharedlibrary cd .. ``` 4) Copy the shared library to the current folder. When the demo was last tested, the shared library file name differed by platform: | OS | name | |:-------|:--------------------------| | Darwin | `libduktape.207.20700.so` | | Linux | `libduktape.so.207.20700` | ```bash cp duktape-*/libduktape.* . ``` 5) Download the SheetJS Standalone script, shim script and test file. Move all three files to the project directory: {`\ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js curl -LO https://docs.sheetjs.com/pres.numbers`} 6) Download [`SheetJSDuk.php`](pathname:///duk/SheetJSDuk.php): ```bash curl -LO https://docs.sheetjs.com/duk/SheetJSDuk.php ``` 7) Edit the `SheetJSDuk.php` script. The `$sofile` variable declares the path to the library: ```php title="SheetJSDuk.php (edit highlighted line)" The name of the library is `libduktape.207.20700.so`: ```php title="SheetJSDuk.php (change highlighted line)" // highlight-next-line $sofile = './libduktape.207.20700.so'; ``` The name of the library is `libduktape.so.207.20700`: ```php title="SheetJSDuk.php (change highlighted line)" // highlight-next-line $sofile = './libduktape.so.207.20700'; ``` 8) Run the script: ```bash php SheetJSDuk.php pres.numbers ``` If the program succeeded, the CSV contents will be printed to console and the file `sheetjsw.xlsb` will be created. That file can be opened with Excel. ### Python There is no official Python binding to the Duktape library. Instead, this demo uses the raw `ctypes` interface[^2] to the Duktape shared library. #### Python Demo :::note Tested Deployments This demo was tested in the following deployments: | Architecture | Version | Python | Date | |:-------------|:--------|:---------|:-----------| | `darwin-x64` | `2.7.0` | `3.13.7` | 2026-01-21 | | `darwin-arm` | `2.7.0` | `3.12.3` | 2026-01-23 | | `win11-x64` | `2.7.0` | `3.11.9` | 2026-01-28 | | `linux-x64` | `2.7.0` | `3.12.3` | 2025-04-21 | | `linux-arm` | `2.7.0` | `3.11.2` | 2025-02-15 | ::: 0) Ensure `python3` is installed and available on the system path. 1) Build the Duktape shared library: ```bash curl -LO https://duktape.org/duktape-2.7.0.tar.xz tar -xJf duktape-2.7.0.tar.xz cd duktape-2.7.0 make -f Makefile.sharedlibrary cd .. ``` 2) Copy the shared library to the current folder. When the demo was last tested, the shared library file name differed by platform: | OS | name | |:-------|:--------------------------| | Darwin | `libduktape.207.20700.so` | | Linux | `libduktape.so.207.20700` | ```bash cp duktape-*/libduktape.* . ``` 3) Download the SheetJS Standalone script, shim script and test file. Move all three files to the project directory: {`\ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js curl -LO https://docs.sheetjs.com/pres.numbers`} 4) Download [`SheetJSDuk.py`](pathname:///duk/SheetJSDuk.py): ```bash curl -LO https://docs.sheetjs.com/duk/SheetJSDuk.py ``` 5) Edit the `SheetJSDuk.py` script. The `lib` variable declares the path to the library: ```python title="SheetJSDuk.py (edit highlighted line)" #!/usr/bin/env python3 # highlight-next-line lib = "libduktape.207.20700.so" ``` The name of the library is `libduktape.207.20700.so`: ```python title="SheetJSDuk.py (change highlighted line)" # highlight-next-line lib = "libduktape.207.20700.so" ``` The name of the library is `libduktape.so.207.20700`: ```python title="SheetJSDuk.py (change highlighted line)" # highlight-next-line lib = "libduktape.so.207.20700" ``` 6) Run the script: ```bash python3 SheetJSDuk.py pres.numbers ``` If the program succeeded, the CSV contents will be printed to console and the file `sheetjsw.xlsb` will be created. That file can be opened with Excel. :::caution pass In some tests, the command failed with an `OSError` message: ``` OSError: libduktape.so.207.20700: cannot open shared object file: No such file or directory ``` The fix is to explicitly add `./` to the `lib` variable in `SheetJSDuk.py`: The name of the library is `libduktape.207.20700.so`: ```python title="SheetJSDuk.py (change highlighted line)" # highlight-next-line lib = "./libduktape.207.20700.so" ``` The name of the library is `libduktape.so.207.20700`: ```python title="SheetJSDuk.py (change highlighted line)" # highlight-next-line lib = "./libduktape.so.207.20700" ``` ::: ### Zig :::caution Zig support is considered experimental. Great open source software grows with user tests and reports. Any issues should be reported to the Zig project for further diagnosis. ::: #### Zig Compilation The main Duktape code can be added to the Zig build pipeline. :::note pass The following explanation was verified against Zig 0.14.0. ::: Due to restrictions in the Zig C integration, the path to the Duktape `src` folder must be added to the include path list: ```zig title="build.zig" const exe = b.addExecutable(.{ // ... }); // highlight-start // this line is required to make @cInclude("duktape.h") work exe.addIncludePath(b.path("duktape-2.7.0/src")); // highlight-end ``` The `duktape.c` source file must be added to the build sequence. For Zig version 0.14.0, Duktape must be compiled with flags `-std=c99 -fno-sanitize=undefined` and linked against `libc` and `libm`: ```zig title="build.zig" const exe = b.addExecutable(.{ // ... }); // highlight-start exe.addCSourceFile(.{: .file = b.path("duktape-2.7.0/src/duktape.c"), .flags = &.{ "-std=c99", "-fno-sanitize=undefined" } }); exe.linkSystemLibrary("c"); exe.linkSystemLibrary("m"); // highlight-end ``` #### Zig Import `duktape.h` can be imported using the `@cImport` directive: ```zig title="main.zig" const duktape = @cImport({ @cInclude("duktape.h"); }); ``` Once imported, many API functions can be referenced from the `duktape` scope. For example, `duk_peval_string` in the C interface will be available to Zig code using the name `duktape.duk_peval_string`. It is strongly recommended to colocate allocations and cleanup methods using `defer`. For example, a Duktape context is created with `duk_create_heap` and destroyed with `duk_destroy_heap`. The latter call can be deferred: ```zig title="Colocating cleanup methods with defer" const ctx = duktape.duk_create_heap(null, null, null, null, null); defer _ = duktape.duk_destroy_heap(ctx); ``` #### Zig Translator Caveats The Zig translator does not properly handle blingo `void` casts. For example, `duk_eval_string_noresult` is a function-like macro defined in `duktape.h`: ```c title="duk_eval_string_noresult blingo" #define duk_eval_string_noresult(ctx,src) \ ((void) duk_eval_raw((ctx), (src), 0, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN | DUK_COMPILE_NORESULT | DUK_COMPILE_NOFILENAME)) ``` The compiler will throw an error involving `anyopaque` (C `void`): ``` error: opaque return type 'anyopaque' not allowed ``` The blingo performs a `void` cast to suppress certain C compiler warnings. The spiritual equivalent in Zig is to assign to `_`. The `duk_eval_raw` method and each compile-time constant are available in the `duktape` scope. A manual translation is shown below: ```zig title="duk_eval_string_noresult blingo in Zig" _ = duktape.duk_eval_raw(ctx, src, 0, 0 | duktape.DUK_COMPILE_EVAL | duktape.DUK_COMPILE_NOSOURCE | duktape.DUK_COMPILE_STRLEN | duktape.DUK_COMPILE_NORESULT | duktape.DUK_COMPILE_NOFILENAME); ``` #### Zig Demo :::note Tested Deployments This demo was tested in the following deployments: | Architecture | Version | Zig | Date | |:-------------|:--------|:---------|:-----------| | `darwin-x64` | `2.7.0` | `0.15.2` | 2026-01-20 | | `darwin-arm` | `2.7.0` | `0.15.2` | 2026-01-20 | | `win11-x64` | `2.7.0` | `0.14.0` | 2025-04-28 | | `win11-arm` | `2.7.0` | `0.13.0` | 2025-02-23 | | `linux-x64` | `2.7.0` | `0.14.0` | 2025-04-21 | | `linux-arm` | `2.7.0` | `0.13.0` | 2025-02-15 | On Windows, due to incompatibilities between WSL and PowerShell, some commands must be run in WSL Bash. ::: 0) Create a new project folder: ```bash mkdir sheetjs-zig cd sheetjs-zig ``` 1) Download the Zig tarball from https://ziglang.org/download/ and extract to the project folder. For X64 Mac: ```bash curl -LO https://ziglang.org/download/0.15.2/zig-x86_64-macos-0.15.2.tar.xz tar -xzf zig-*.tar.xz ``` For ARM64 Mac: ```bash curl -LO https://ziglang.org/download/0.15.2/zig-aarch64-macos-0.15.2.tar.xz tar -xzf zig-*.tar.xz ``` For X64 Linux: ```bash curl -LO https://ziglang.org/download/0.14.0/zig-linux-x86_64-0.14.0.tar.xz xz -d zig-linux-*.tar.xz tar -xf zig-linux-*.tar ``` For AArch64 Linux: ```bash curl -LO https://ziglang.org/download/0.13.0/zig-linux-aarch64-0.13.0.tar.xz xz -d zig-linux-*.tar.xz tar -xf zig-linux-*.tar ``` :::note pass The following commands should be run within WSL bash. ::: For X64 Windows: ```bash curl -LO https://ziglang.org/download/0.14.0/zig-windows-x86_64-0.14.0.zip unzip zig-windows-x86_64-0.14.0.zip ``` For ARM64 Windows: ```bash curl -LO https://ziglang.org/download/0.13.0/zig-windows-aarch64-0.13.0.zip unzip zig-windows-aarch64-0.13.0.zip ``` 2) Initialize a project: ```bash ./zig-*/zig init ``` ```bash ./zig-*/zig init ``` :::note pass The following command should be run within Powershell. ::: ```bash .\zig-windows-*\zig.exe init ``` 3) Download the Duktape source and extract in the current directory. On Windows, the commands should be run within WSL: ```bash curl -LO https://duktape.org/duktape-2.7.0.tar.xz tar -xJf duktape-2.7.0.tar.xz ``` 4) Download the SheetJS Standalone script, shim script and test file. Move all three files to the `src` subdirectory: The following commands can be run within a shell on macOS and Linux. On Windows, the commands should be run within WSL bash: {`\ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js curl -LO https://docs.sheetjs.com/pres.numbers mv *.js src`} 5) Add the highlighted lines to `build.zig` just after the `exe` definition: ```zig title="build.zig (add highlighted lines)" const exe = b.addExecutable(.{ .name = "sheetjs_zig", .root_module = exe_mod, }); // highlight-start exe.addCSourceFile(.{ .file = b.path("duktape-2.7.0/src/duktape.c"), .flags = &.{ "-std=c99", "-fno-sanitize=undefined" } }); exe.addIncludePath(b.path("duktape-2.7.0/src")); exe.linkSystemLibrary("c"); exe.linkSystemLibrary("m"); // highlight-end ``` 6) Download [`main.zig`](pathname:///duk/main.zig) and replace `src/main.zig`. The following command should be run in WSL bash or the macOS or Linux terminal: ```bash curl -L -o src/main.zig https://docs.sheetjs.com/duk/main.zig ``` 7) Build and run the program: ```bash ./zig-*/zig build run -- pres.numbers ``` ```bash ./zig-*/zig build run -- pres.numbers ``` :::caution pass On Arch Linux and HoloOS (Steam Deck), compilation may fail: ``` zig build-exe sheetjs-zig Debug native: error: error: unable to create compilation: LibCStdLibHeaderNotFound ``` `glibc` and `linux-api-headers` must be installed: ```bash sudo pacman -Syu glibc linux-api-headers ``` ::: This command should be run in PowerShell: ```bash .\zig-windows-*\zig.exe build run -- pres.numbers ``` This step builds and runs the program. The generated program will be placed in the `zig-out/bin/` subdirectory. It should display some metadata along with CSV rows from the first worksheet. It will also generate `sheetjs.zig.xlsx`, which can be opened with a spreadsheet editor such as Excel. ### Perl The Perl binding for Duktape is available as `JavaScript::Duktape::XS` on CPAN. The Perl binding does not have raw `Buffer` ops, so Base64 strings are used. #### Perl Demo :::note Tested Deployments This demo was tested in the following deployments: | Architecture | Version | Date | |:-------------|:--------|:-----------| | `darwin-x64` | `2.2.0` | 2026-01-21 | | `darwin-arm` | `2.2.0` | 2026-01-23 | | `linux-x64` | `2.2.0` | 2025-04-21 | | `linux-arm` | `2.2.0` | 2025-02-15 | ::: 0) Ensure `perl` and `cpan` are installed and available on the system path. :::caution pass On Arch Linux and HoloOS (Steam Deck), `crypt.h` may be missing. The `libxcrypt` package must be explicitly installed: ```bash sudo pacman -S libxcrypt ``` ::: 1) Install the `JavaScript::Duktape::XS` library: ```bash cpan install JavaScript::Duktape::XS ``` :::note pass On some systems, the command must be run as the root user: ```bash sudo cpan install JavaScript::Duktape::XS ``` ::: 2) Download [`SheetJSDuk.pl`](pathname:///duk/SheetJSDuk.pl): ```bash curl -LO https://docs.sheetjs.com/duk/SheetJSDuk.pl ``` 3) Download the SheetJS ExtendScript build and test file: {`\ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.extendscript.js curl -LO https://docs.sheetjs.com/pres.xlsx`} 4) Run the script: ```bash perl SheetJSDuk.pl pres.xlsx ``` If the script succeeded, the data in the test file will be printed in CSV rows. The script will also export `SheetJSDuk.xlsb`. :::note pass In some test runs, the command failed due to missing `File::Slurp`: ``` Can't locate File/Slurp.pm in @INC (you may need to install the File::Slurp module) ``` The fix is to install `File::Slurp` with `cpan`: ```bash sudo cpan install File::Slurp ``` ::: ### Rust [`ducc`](https://crates.io/crates/ducc) is a Rust binding to the Duktape engine. It provides a number of convenience methods for exchanging data with the engine. :::note pass When this demo was last tested, there were issues passing `Uint8Array` objects back to Rust. Instead, this demo generates a Base64-encoded string, passes the string back to Rust, and decodes using the `base64` crate. ::: #### Rust Demo :::note Tested Deployments This demo was tested in the following deployments: | Architecture | Version | Date | |:-------------|:--------|:-----------| | `darwin-x64` | `2.2.1` | 2026-01-21 | | `darwin-arm` | `2.2.1` | 2026-01-23 | | `win11-x64` | `2.2.1` | 2026-02-02 | | `win11-arm` | `2.2.1` | 2026-02-02 | | `linux-x64` | `2.2.1` | 2026-01-08 | | `linux-arm` | `2.2.1` | 2025-04-18 | ::: :::caution pass The `ducc` crate cannot compile Duktape from source in Windows on ARM, so the x64 Rust toolchain must be used through the X64 compatibility layer.
Windows on ARM steps (click to show) The following commands switch to the X64 toolchain: ```pwsh rustup toolchain install stable-x86_64-pc-windows-msvc --force-non-host rustup default stable-x86_64-pc-windows-msvc --force-non-host ``` --- The following command switches back to the native toolchain: ```pwsh rustup default stable ```
::: 1) Create a new project: ```bash cargo new sheetjs-duk-rs cd sheetjs-duk-rs cargo run ``` 2) Download the SheetJS Standalone script and shim to the `src` folder: {`\ curl -L -o src/shim.min.js https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js curl -L -o src/xlsx.full.min.js https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`} 3) Download the test file. Move the file to the project directory: :::caution pass If the `curl` command fails, run the commands within WSL `bash`. ::: {`\ curl -LO https://docs.sheetjs.com/pres.numbers`} 4) Download [`main.rs`](pathname:///duk/main.rs) and replace `src/main.rs`: ```bash curl -L -o src/main.rs https://docs.sheetjs.com/duk/main.rs ``` 5) Install dependencies: ```bash cargo add ducc base64 ``` 6) Build and run the app: ```bash cargo run -- pres.numbers ``` If the program succeeded, the CSV contents will be printed to console and the file `sheetjsw.xlsb` will be created. That file can be opened with a spreadsheet editor that supports XLSB spreadsheets. [^1]: See [Foreign Function Interface](https://www.php.net/manual/en/book.ffi.php) in the PHP documentation. [^2]: See [`ctypes`](https://docs.python.org/3/library/ctypes.html) in the Python documentation.