forked from sheetjs/docs.sheetjs.com
		
	c+swift
This commit is contained in:
		
							parent
							
								
									02c42e79a5
								
							
						
					
					
						commit
						0bf5ac0fa8
					
				| @ -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'; | ||||
|  | ||||
							
								
								
									
										232
									
								
								docz/docs/03-demos/31-engines/01_duktape.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										232
									
								
								docz/docs/03-demos/31-engines/01_duktape.md
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||
| ``` | ||||
							
								
								
									
										163
									
								
								docz/docs/03-demos/31-engines/02_jsc.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										163
									
								
								docz/docs/03-demos/31-engines/02_jsc.md
									
									
									
									
									
										Normal file
									
								
							| @ -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. | ||||
							
								
								
									
										5
									
								
								docz/docs/03-demos/31-engines/_category_.json
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										5
									
								
								docz/docs/03-demos/31-engines/_category_.json
									
									
									
									
									
										Normal file
									
								
							| @ -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/' }, | ||||
|  | ||||
							
								
								
									
										104
									
								
								docz/static/duk/sheetjs.duk.c
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										104
									
								
								docz/static/duk/sheetjs.duk.c
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user