forked from sheetjs/docs.sheetjs.com
		
	
		
			
	
	
		
			232 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			232 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|  | --- | ||
|  | 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 | ||
|  | ``` |