This commit is contained in:
SheetJS 2023-02-12 23:07:25 -05:00
parent 02c42e79a5
commit 0bf5ac0fa8
10 changed files with 531 additions and 275 deletions

@ -135,13 +135,13 @@ cd sheetjs-neu
curl -L -o resources/js/xlsx.full.min.js https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js
```
3) Add the highlighted lines to `neutralino.conf.json` in `nativeAllowList`:
3) Add the highlighted line to `neutralino.conf.json` in `nativeAllowList`:
```json title="neutralino.config.json"
"nativeAllowList": [
"app.*",
// highlight-start
"os.*",
// highlight-start
"filesystem.*",
// highlight-end
"debug.log"

@ -1,5 +1,5 @@
---
title: React Native for Desktop
title: React Native
pagination_prev: demos/mobile/index
pagination_next: demos/cloud/index
sidebar_position: 6

@ -1,5 +1,6 @@
---
title: Command-Line Tools
pagination_next: demos/engines/index
---
import current from '/version.js';

@ -0,0 +1,232 @@
---
title: C + Duktape
pagination_prev: demos/cli
pagination_next: demos/clipboard
---
Duktape is an embeddable JS engine written in C. It has been ported to a number
of exotic architectures and operating systems.
The [Standalone scripts](/docs/getting-started/installation/standalone) can be
parsed and evaluated in a Duktape context.
## 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 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("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
This demo was tested with Duktape `2.7.0` (`darwin-x64`) on 2023 February 12.
:::
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)
0) Download and extract Duktape:
```bash
mkdir sheetjs-duk
cd sheetjs-duk
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} .
```
1) Download the standalone script, shim and test file:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js`}>shim.min.js</a></li>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href="https://sheetjs.com/pres.numbers">pres.numbers</a></li>
</ul>
```bash
curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js
curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js
curl -LO https://sheetjs.com/pres.numbers
```
2) Download [`sheetjs.duk.c`](pathname:///duk/sheetjs.duk.c):
```bash
curl -LO https://docs.sheetjs.com/duk/sheetjs.duk.c
```
3) Compile standalone `sheetjs.duk` binary
```bash
gcc -std=c99 -Wall -osheetjs.duk sheetjs.duk.c duktape.c -lm
```
4) Run the demo:
```bash
./sheetjs.duk 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<br/>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`<br/>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
```

@ -0,0 +1,163 @@
---
title: Swift + JavaScriptCore
pagination_prev: demos/cli
pagination_next: demos/clipboard
---
iOS and MacOS ship with the JavaScriptCore framework for running JS code from
Swift and Objective-C. Hybrid function invocation is tricky, but explicit data
passing is straightforward. The demo shows a standalone Swift sample for MacOS.
The [Standalone scripts](/docs/getting-started/installation/standalone) can be
parsed and evaluated in a JSC context.
:::warning Platform Limitations
JavaScriptCore is primarily deployed in MacOS and iOS applications. There is
some experimental support through the Bun runtime, but production applications
intending to support Windows / Linux / Android should try to embed V8.
:::
## Integration Details
Binary strings can be passed back and forth using `String.Encoding.isoLatin1`.
_Initialize JavaScriptCore_
JSC does not provide a `global` variable. It can be created in one line:
```swift
var context: JSContext!
do {
context = JSContext();
context.exceptionHandler = { _, X in if let e = X { print(e.toString()!); }; };
// highlight-next-line
context.evaluateScript("var global = (function(){ return this; }).call(null);");
} catch { print(error.localizedDescription); }
```
_Load SheetJS Scripts_
The main library can be loaded by reading the scripts from the file system and
evaluating in the JSC context:
```swift
let src = try String(contentsOfFile: "xlsx.full.min.js");
context.evaluateScript(src);
```
To confirm the library is loaded, `XLSX.version` can be inspected:
```swift
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
if let ver = XLSX.objectForKeyedSubscript("version") { print(ver.toString()); }
```
### Reading Files
`String(contentsOf:encoding:)` reads from a path and returns an encoded string:
```swift
/* read sheetjs.xls as Base64 string */
let file_path = shared_dir.appendingPathComponent("sheetjs.xls");
let data: String! = try String(contentsOf: file_path, encoding: String.Encoding.isoLatin1);
```
This string can be loaded into the JS engine and processed:
```swift
/* load data in JSC */
context.setObject(data, forKeyedSubscript: "payload" as (NSCopying & NSObjectProtocol));
/* `payload` (the "forKeyedSubscript" parameter) is a binary string */
context.evaluateScript("var wb = XLSX.read(payload, {type:'binary'});");
```
### Writing Files
When writing to binary string in JavaScriptCore, the result should be stored in
a variable and converted to string in Swift:
```swift
/* write to binary string */
context.evaluateScript("var out = XLSX.write(wb, {type:'binary', bookType:'xlsx'})");
/* `out` from the script is a binary string that can be stringified in Swift */
let outvalue: JSValue! = context.objectForKeyedSubscript("out");
var out: String! = outvalue.toString();
```
`String#write(to:atomically:encoding)` writes the string to the specified path:
```swift
/* write to sheetjsw.xlsx */
let out_path = shared_dir.appendingPathComponent("sheetjsw.xlsx");
try? out.write(to: out_path, atomically: false, encoding: String.Encoding.isoLatin1);
```
## Complete Example
:::note
This demo was tested on 2023 February 12. `swift --version` printed:
```
swift-driver version: 1.62.15 Apple Swift version 5.7.2
Target: x86_64-apple-macosx12.0
```
:::
The demo includes a sample `SheetJSCore` Wrapper class to simplify operations.
:::caution This demo only runs on MacOS
This example requires MacOS + Swift and will not work on Windows or Linux!
:::
0) Ensure Xcode is installed. Create a folder for the project:
```bash
mkdir sheetjswift
cd sheetjswift
```
1) Download the standalone script and the test file:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href="https://sheetjs.com/pres.numbers">pres.numbers</a></li>
</ul>
```bash
curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js
curl -LO https://sheetjs.com/pres.numbers
```
2) Download the Swift scripts for the demo
- [`SheetJSCore.swift`](pathname:///swift/SheetJSCore.swift) Wrapper library
- [`main.swift`](pathname:///swift/main.swift) Command-line script
```bash
curl -LO https://docs.sheetjs.com/swift/SheetJSCore.swift
curl -LO https://docs.sheetjs.com/swift/main.swift
```
3) Build the `SheetJSwift` binary:
```bash
swiftc SheetJSCore.swift main.swift -o SheetJSwift
```
4) Test the program:
```bash
./SheetJSwift pres.numbers
```
If successful, a CSV will be printed to console. The script also tries to write
to `SheetJSwift.xlsx`. That file can be verified by opening in Excel / Numbers.

@ -0,0 +1,5 @@
{
"label": "Other Languages",
"position": 31,
"collapsed": false
}

@ -1,11 +1,16 @@
---
title: JavaScript Engines
pagination_prev: demos/cli
pagination_next: demos/clipboard
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Browser vendors and other organizations have built "JavaScript engines". They
are independent software libraries that are capable of running JS scripts.
The most popular JavaScript engine is V8. Designed for embedding in software,
it powers Chrome, NodeJS, UXP, Deno and many other platforms.
@ -14,6 +19,7 @@ for low-power or low-memory environments. Others aim for interoperability with
specific programming languages or environments. Typically they support ES3 and
are capable of running SheetJS code.
This demo showcases a number of JS engines and language bindings.
## General Caveats
@ -47,186 +53,19 @@ Base64 strings are safe for passing between JS and native code, but they should
only be used when there is no safe way to pass `ArrayBuffer` or `Uint8Array`.
## Duktape
## Engines
This list is sorted in alphabetical order.
### Duktape
Duktape is an embeddable JS engine written in C. It has been ported to a number
of exotic architectures and operating systems.
**Reading data**
This demo has been moved [to a dedicated page](/docs/demos/engines/duktape).
Duktape supports `Buffer` natively but should be sliced before processing:
```c
/* parse a C char array as a workbook object */
duk_push_external_buffer(ctx);
duk_config_buffer(ctx, -1, buf, len);
duk_put_global_string(ctx, "buf");
duk_eval_string_noresult("workbook = XLSX.read(buf.slice(0, buf.length), {type:'buffer'});");
```
**Writing data**
`duk_get_buffer_data` can pull `Buffer` object data into the C code:
```c
/* write a workbook object to a C char array */
duk_eval_string(ctx, "XLSX.write(workbook, {type:'array', bookType:'xlsx'})");
duk_size_t sz;
char *buf = (char *)duk_get_buffer_data(ctx, -1, sz);
duk_pop(ctx);
```
<details><summary><b>Complete Example</b> (click to show)</summary>
:::note
This demo was tested on Intel Mac (`darwin-x64`).
:::
0) Download and extract the latest release (2.7.0 at the time of writing)
```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} .
```
1) Download the standalone script, shim and test file:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js`}>shim.min.js</a></li>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href="https://sheetjs.com/pres.numbers">pres.numbers</a></li>
</ul>
2) Save the following script to `sheetjs.duk.c`:
```c title="sheetjs.duk.c"
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "duktape.h"
#define FAIL_LOAD { \
duk_push_undefined(ctx); \
perror("Error in load_file"); \
return 1; \
}
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); fseek(f, 0, SEEK_SET); }
char *buf = (char *)malloc(fsize * sizeof(char));
*sz = fread((void *) buf, 1, fsize, f);
fclose(f);
return buf;
}
static duk_int_t eval_file(duk_context *ctx, const char *filename) {
size_t len; char *buf = read_file(filename, &len);
if(!buf) FAIL_LOAD
duk_push_lstring(ctx, (const char *)buf, (duk_size_t)len);
duk_int_t retval = duk_peval(ctx);
duk_pop(ctx);
return retval;
}
static duk_int_t load_file(duk_context *ctx, const char *filename, const char *var) {
size_t len; char *buf = read_file(filename, &len);
if(!buf) FAIL_LOAD
duk_push_external_buffer(ctx);
duk_config_buffer(ctx, -1, buf, len);
duk_put_global_string(ctx, var);
return 0;
}
static duk_int_t save_file(duk_context *ctx, const char *filename, const char *var) {
duk_get_global_string(ctx, var);
duk_size_t sz;
char *buf = (char *)duk_get_buffer_data(ctx, -1, &sz);
if(!buf) return 1;
FILE *f = fopen(filename, "wb"); fwrite(buf, 1, sz, f); fclose(f);
return 0;
}
#define FAIL(cmd) { \
printf("error in %s: %s\n", cmd, duk_safe_to_string(ctx, -1)); \
duk_destroy_heap(ctx); \
return res; \
}
#define DOIT(cmd) duk_eval_string_noresult(ctx, cmd);
int main(int argc, char *argv[]) {
duk_int_t res = 0;
/* initialize */
duk_context *ctx = duk_create_heap_default();
/* duktape does not expose a standard "global" by default */
DOIT("var global = (function(){ return this; }).call(null);");
/* load library */
res = eval_file(ctx, "shim.min.js");
if(res != 0) FAIL("shim load")
res = eval_file(ctx, "xlsx.full.min.js");
if(res != 0) FAIL("library load")
/* get version string */
duk_eval_string(ctx, "XLSX.version");
printf("SheetJS library version %s\n", duk_get_string(ctx, -1));
duk_pop(ctx);
/* read file */
res = load_file(ctx, argv[1], "buf");
if(res != 0) FAIL("file load")
printf("Loaded file %s\n", argv[1]);
/* parse workbook */
DOIT("wb = XLSX.read(buf.slice(0, buf.length), {type:'buffer'});");
DOIT("ws = wb.Sheets[wb.SheetNames[0]]");
/* print CSV */
duk_eval_string(ctx, "XLSX.utils.sheet_to_csv(ws)");
printf("%s\n", duk_get_string(ctx, -1));
duk_pop(ctx);
/* write file */
#define WRITE_TYPE(BOOKTYPE) \
DOIT("newbuf = (XLSX.write(wb, {type:'array', bookType:'" BOOKTYPE "'}));");\
res = save_file(ctx, "sheetjsw." BOOKTYPE, "newbuf");\
if(res != 0) FAIL("save sheetjsw." BOOKTYPE)
WRITE_TYPE("xlsb")
/* cleanup */
duk_destroy_heap(ctx);
return res;
}
```
3) Compile standalone `sheetjs.duk` binary
```bash
gcc -std=c99 -Wall -osheetjs.duk sheetjs.duk.c duktape.c -lm
```
4) Run the demo:
```bash
./sheetjs.duk 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.
</details>
## Goja
### Goja
Goja is a pure Go implementation of ECMAScript 5. It supports the standalone
scripts out of the box.
@ -368,7 +207,7 @@ This will print the contents as a CSV to screen AND write to `sheetjsg.csv`
</details>
## Hermes
### Hermes
Hermes is an embeddable JS engine for React Native. The library and binary
distributions include a command-line tool `hermes` for running JS scripts.
@ -439,107 +278,16 @@ hermes xlsx.hermes.js
</details>
## JavaScriptCore
:::warning Platform Limitations
JavaScriptCore is primarily deployed in MacOS and iOS applications. There is
some experimental support through the Bun runtime, but production applications
intending to support Windows / Linux / Android should try to embed V8.
:::
### JavaScriptCore
iOS and MacOS ship with the JavaScriptCore framework for running JS code from
Swift and Objective-C. Hybrid function invocation is tricky, but explicit data
passing is straightforward. The demo shows a standalone Swift sample for MacOS.
Swift and Objective-C.
Binary strings can be passed back and forth using `String.Encoding.isoLatin1`.
This demo has been moved [to a dedicated page](/docs/demos/engines/jsc).
**Reading data**
`String(contentsOf:encoding:)` reads from a path and returns an encoded string:
```swift
/* read sheetjs.xls as Base64 string */
let file_path = shared_dir.appendingPathComponent("sheetjs.xls");
let data: String! = try String(contentsOf: file_path, encoding: String.Encoding.isoLatin1);
```
This string can be loaded into the JS engine and processed:
```swift
/* load data in JSC */
context.setObject(data, forKeyedSubscript: "payload" as (NSCopying & NSObjectProtocol));
/* `payload` (the "forKeyedSubscript" parameter) is a binary string */
context.evaluateScript("var wb = XLSX.read(payload, {type:'binary'});");
```
**Writing data**
When writing to binary string in JavaScriptCore, the result should be stored in
a variable and converted to string in Swift:
```swift
/* write to binary string */
context.evaluateScript("var out = XLSX.write(wb, {type:'binary', bookType:'xlsx'})");
/* `out` from the script is a binary string that can be stringified in Swift */
let outvalue: JSValue! = context.objectForKeyedSubscript("out");
var out: String! = outvalue.toString();
```
`String#write(to:atomically:encoding)` writes the string to the specified path:
```swift
/* write to sheetjsw.xlsx */
let out_path = shared_dir.appendingPathComponent("sheetjsw.xlsx");
try? out.write(to: out_path, atomically: false, encoding: String.Encoding.isoLatin1);
```
The demo includes a sample `SheetJSCore` Wrapper class to simplify operations.
<details><summary><b>Complete Example</b> (click to show)</summary>
:::caution This demo only runs on MacOS
This example requires MacOS + Swift and will not work on Windows or Linux!
:::
0) Ensure Xcode is installed
1) Download the standalone script, the shim and the test file:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js`}>shim.min.js</a></li>
<li><a href="https://sheetjs.com/pres.numbers">pres.numbers</a></li>
</ul>
2) Download the Swift scripts for the demo
- [`SheetJSCore.swift`](pathname:///swift/SheetJSCore.swift) Wrapper library
- [`main.swift`](pathname:///swift/main.swift) Command-line script
3) Build the `SheetJSwift` binary:
```bash
swiftc SheetJSCore.swift main.swift -o SheetJSwift
```
4) Test the program:
```bash
./SheetJSwift pres.numbers
```
If successful, a CSV will be printed to console. The script also tries to write
to `SheetJSwift.xlsx`. That file can be verified by opening in Excel / Numbers.
</details>
## JerryScript
### JerryScript
JerryScript is a lightweight JavaScript engine designed for use in low-memory
environments like microcontrollers. As part of the build suite, the project
@ -627,7 +375,8 @@ build/bin/jerry xlsx.jerry.js; echo $?
</details>
## QuickJS
### QuickJS
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
@ -686,7 +435,7 @@ If successful, the script will generate `SheetJSQuick.xlsx`.
</details>
## Rhino
### Rhino
Rhino is an ES3+ engine in Java. The `SheetJSRhino` class and `com.sheetjs`
package show a complete JAR deployment, including the full XLSX source.

@ -1,5 +1,6 @@
---
title: Clipboard Data
pagination_prev: demos/engines/index
---
Spreadsheet software like Excel typically support copying and pasting cells and

@ -173,6 +173,7 @@ const config = {
{ from: '/docs/getting-started/demos/excel', to: '/docs/demos/' },
{ from: '/docs/demos/content', to: '/docs/demos/static/' },
{ from: '/docs/demos/git', to: '/docs/demos/hosting/github/' },
{ from: '/docs/demo/grid', to: '/docs/demos/grid/' },
/* frontend */
{ from: '/docs/demos/angular', to: '/docs/demos/frontend/angular/' },
{ from: '/docs/demos/react', to: '/docs/demos/frontend/react/' },

@ -0,0 +1,104 @@
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "duktape.h"
#define FAIL_LOAD { \
duk_push_undefined(ctx); \
perror("Error in load_file"); \
return 1; \
}
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); fseek(f, 0, SEEK_SET); }
char *buf = (char *)malloc(fsize * sizeof(char));
*sz = fread((void *) buf, 1, fsize, f);
fclose(f);
return buf;
}
static duk_int_t eval_file(duk_context *ctx, const char *filename) {
size_t len; char *buf = read_file(filename, &len);
if(!buf) FAIL_LOAD
duk_push_lstring(ctx, (const char *)buf, (duk_size_t)len);
duk_int_t retval = duk_peval(ctx);
duk_pop(ctx);
return retval;
}
static duk_int_t load_file(duk_context *ctx, const char *filename, const char *var) {
size_t len; char *buf = read_file(filename, &len);
if(!buf) FAIL_LOAD
duk_push_external_buffer(ctx);
duk_config_buffer(ctx, -1, buf, len);
duk_put_global_string(ctx, var);
return 0;
}
static duk_int_t save_file(duk_context *ctx, const char *filename, const char *var) {
duk_get_global_string(ctx, var);
duk_size_t sz;
char *buf = (char *)duk_get_buffer_data(ctx, -1, &sz);
if(!buf) return 1;
FILE *f = fopen(filename, "wb"); fwrite(buf, 1, sz, f); fclose(f);
return 0;
}
#define FAIL(cmd) { \
printf("error in %s: %s\n", cmd, duk_safe_to_string(ctx, -1)); \
duk_destroy_heap(ctx); \
return res; \
}
#define DOIT(cmd) duk_eval_string_noresult(ctx, cmd);
int main(int argc, char *argv[]) {
duk_int_t res = 0;
/* initialize */
duk_context *ctx = duk_create_heap_default();
/* duktape does not expose a standard "global" by default */
DOIT("var global = (function(){ return this; }).call(null);");
/* load library */
res = eval_file(ctx, "shim.min.js");
if(res != 0) FAIL("shim load")
res = eval_file(ctx, "xlsx.full.min.js");
if(res != 0) FAIL("library load")
/* get version string */
duk_eval_string(ctx, "XLSX.version");
printf("SheetJS library version %s\n", duk_get_string(ctx, -1));
duk_pop(ctx);
/* read file */
res = load_file(ctx, argv[1], "buf");
if(res != 0) FAIL("file load")
printf("Loaded file %s\n", argv[1]);
/* parse workbook */
DOIT("wb = XLSX.read(buf.slice(0, buf.length), {type:'buffer'});");
DOIT("ws = wb.Sheets[wb.SheetNames[0]]");
/* print CSV */
duk_eval_string(ctx, "XLSX.utils.sheet_to_csv(ws)");
printf("%s\n", duk_get_string(ctx, -1));
duk_pop(ctx);
/* write file */
#define WRITE_TYPE(BOOKTYPE) \
DOIT("newbuf = (XLSX.write(wb, {type:'array', bookType:'" BOOKTYPE "'}));");\
res = save_file(ctx, "sheetjsw." BOOKTYPE, "newbuf");\
if(res != 0) FAIL("save sheetjsw." BOOKTYPE)
WRITE_TYPE("xlsb")
/* cleanup */
duk_destroy_heap(ctx);
return res;
}