forked from sheetjs/docs.sheetjs.com
		
	JavaScriptCore C demo
This commit is contained in:
		
							parent
							
								
									922b84e1e3
								
							
						
					
					
						commit
						671729b289
					
				| @ -37,7 +37,7 @@ | ||||
|   </Style> | ||||
|  </Styles> | ||||
|  <Worksheet ss:Name="Engines"> | ||||
|   <Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="16" x:FullColumns="1" | ||||
|   <Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="17" x:FullColumns="1" | ||||
|    x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16"> | ||||
|    <Column ss:Index="3" ss:Width="24"/> | ||||
|    <Column ss:Width="31"/> | ||||
| @ -91,6 +91,16 @@ | ||||
|     <Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell> | ||||
|     <Cell ss:StyleID="s16"/> | ||||
|    </Row> | ||||
|    <Row> | ||||
|     <Cell><Data ss:Type="String">JSC</Data></Cell> | ||||
|     <Cell><Data ss:Type="String">C++</Data></Cell> | ||||
|     <Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell> | ||||
|     <Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell> | ||||
|     <Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell> | ||||
|     <Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell> | ||||
|     <Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell> | ||||
|     <Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell> | ||||
|    </Row> | ||||
|    <Row> | ||||
|     <Cell><Data ss:Type="String">Jint</Data></Cell> | ||||
|     <Cell><Data ss:Type="String">C#</Data></Cell> | ||||
|  | ||||
| @ -409,7 +409,7 @@ This demo was tested in the following environments: | ||||
| |:-------------|:----------|:-----------| | ||||
| | `darwin-x64` | `2.1.1`   | 2024-03-15 | | ||||
| | `win10-x64`  | `2.1.1`   | 2024-03-24 | | ||||
| | `linux-x64`  | `2.1.1`   | 2024-03-29 | | ||||
| | `linux-x64`  | `2.1.1`   | 2024-04-25 | | ||||
| ::: | ||||
| 
 | ||||
| 1) [Download and extract PhantomJS](https://phantomjs.org/download.html) | ||||
|  | ||||
| @ -5,60 +5,171 @@ pagination_next: solutions/input | ||||
| --- | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
| import Tabs from '@theme/Tabs'; | ||||
| import TabItem from '@theme/TabItem'; | ||||
| import CodeBlock from '@theme/CodeBlock'; | ||||
| 
 | ||||
| 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. | ||||
| [JavaScriptCore](https://developer.apple.com/documentation/javascriptcore) (JSC) | ||||
| is the JavaScript engine powering the Safari web browser. | ||||
| 
 | ||||
| [SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing | ||||
| data from spreadsheets. | ||||
| 
 | ||||
| This demo uses JSC and SheetJS to read and write spreadsheets. We'll explore how | ||||
| to load SheetJS in a JSC context and process spreadsheets and structured data | ||||
| from C++ and Swift programs. | ||||
| 
 | ||||
| ## Integration Details | ||||
| 
 | ||||
| The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone) | ||||
| can be parsed and evaluated in a JSC context. | ||||
| 
 | ||||
| :::danger Platform Limitations | ||||
| 
 | ||||
| JavaScriptCore is primarily deployed in MacOS and iOS applications.  There is | ||||
| some experimental support through the Bun runtime, but apps intending to support | ||||
| Windows / Linux / Android should try to embed [V8](/docs/demos/engines/v8). | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## Integration Details | ||||
| <Tabs groupId="jsclang"> | ||||
|   <TabItem value="swift" label="Swift"> | ||||
| 
 | ||||
| Binary strings can be passed back and forth using `String.Encoding.isoLatin1`. | ||||
| 
 | ||||
| _Initialize JavaScriptCore_ | ||||
| The SheetJS `read` method[^1], with the `"binary"` type, can parse binary strings. | ||||
| 
 | ||||
| JSC does not provide a `global` variable. It can be created in one line: | ||||
| The `write` method[^2], with the `"binary"` type, can create binary strings. | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem value="cpp" label="C++"> | ||||
| 
 | ||||
| JSC provides a few special methods for working with `Uint8Array` objects: | ||||
| 
 | ||||
| - `JSObjectMakeTypedArrayWithBytesNoCopy`[^3] creates a typed array from a | ||||
| pointer and size. It uses the memory address directly (no copy). | ||||
| 
 | ||||
| - `JSObjectGetTypedArrayLength`[^4] and `JSObjectGetTypedArrayBytesPtr`[^5] can | ||||
| return a pointer and size pair from a `Uint8Array` in the JSC engine. | ||||
| 
 | ||||
| The SheetJS `read` method[^6] can process `Uint8Array` objects. | ||||
| 
 | ||||
| The `write` method[^7], with the `"buffer"` type, creates `Uint8Array` data. | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| ### Initialize JSC | ||||
| 
 | ||||
| <Tabs groupId="jsclang"> | ||||
|   <TabItem value="swift" label="Swift"> | ||||
| 
 | ||||
| A JSC context can be created with the `JSContext` function: | ||||
| 
 | ||||
| ```swift | ||||
| var context: JSContext! | ||||
| do { | ||||
|   context = JSContext(); | ||||
|   context.exceptionHandler = { _, X in if let e = X { print(e.toString()!); }; }; | ||||
| } catch { print(error.localizedDescription); } | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem value="cpp" label="C++"> | ||||
| 
 | ||||
| A JSC context can be created with the `JSGlobalContextCreate` function: | ||||
| 
 | ||||
| ```cpp | ||||
| JSGlobalContextRef ctx = JSGlobalContextCreate(NULL); | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| JSC does not provide a `global` variable. It can be created in one line: | ||||
| 
 | ||||
| <Tabs groupId="jsclang"> | ||||
|   <TabItem value="swift" label="Swift"> | ||||
| 
 | ||||
| ```swift | ||||
| do { | ||||
|   // highlight-next-line | ||||
|   context.evaluateScript("var global = (function(){ return this; }).call(null);"); | ||||
| } catch { print(error.localizedDescription); } | ||||
| ``` | ||||
| 
 | ||||
| _Load SheetJS Scripts_ | ||||
|   </TabItem> | ||||
|   <TabItem value="cpp" label="C++"> | ||||
| 
 | ||||
| ```cpp | ||||
| #define DOIT(cmd) \ | ||||
|   JSStringRef script = JSStringCreateWithUTF8CString(cmd); \ | ||||
|   JSValueRef result = JSEvaluateScript(ctx, script, NULL, NULL, 0, NULL); \ | ||||
|   JSStringRelease(script); | ||||
| 
 | ||||
| { DOIT("var global = (function(){ return this; }).call(null);") } | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| ### Load SheetJS Scripts | ||||
| 
 | ||||
| The main library can be loaded by reading the scripts from the file system and | ||||
| evaluating in the JSC context: | ||||
| 
 | ||||
| <Tabs groupId="jsclang"> | ||||
|   <TabItem value="swift" label="Swift"> | ||||
| 
 | ||||
| ```swift | ||||
| let src = try String(contentsOfFile: "xlsx.full.min.js"); | ||||
| context.evaluateScript(src); | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem value="cpp" label="C++"> | ||||
| 
 | ||||
| ```cpp | ||||
| /* load library */ | ||||
| { | ||||
|   size_t sz = 0; char *file = read_file("xlsx.full.min.js", &sz); | ||||
|   DOIT(file); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| To confirm the library is loaded, `XLSX.version` can be inspected: | ||||
| 
 | ||||
| <Tabs groupId="jsclang"> | ||||
|   <TabItem value="swift" label="Swift"> | ||||
| 
 | ||||
| ```swift | ||||
| let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX"); | ||||
| if let ver = XLSX.objectForKeyedSubscript("version") { print(ver.toString()); } | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem value="cpp" label="C++"> | ||||
| 
 | ||||
| ```cpp | ||||
| #define JS_STR_TO_C \ | ||||
|   JSStringRef str = JSValueToStringCopy(ctx, result, NULL); \ | ||||
|   size_t sz = JSStringGetMaximumUTF8CStringSize(str); \ | ||||
|   char *buf = (char *)malloc(sz); \ | ||||
|   JSStringGetUTF8CString(str, buf, sz); \ | ||||
| 
 | ||||
| /* get version string */ | ||||
| { | ||||
|   DOIT("XLSX.version") | ||||
| 
 | ||||
|   JS_STR_TO_C | ||||
| 
 | ||||
|   printf("SheetJS library version %s\n", buf); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| ### Reading Files | ||||
| 
 | ||||
| <Tabs groupId="jsclang"> | ||||
|   <TabItem value="swift" label="Swift"> | ||||
| 
 | ||||
| `String(contentsOf:encoding:)` reads from a path and returns an encoded string: | ||||
| 
 | ||||
| ```swift | ||||
| @ -104,8 +215,41 @@ For broad compatibility with Swift versions, the demo uses the String method. | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem value="cpp" label="C++"> | ||||
| 
 | ||||
| There are a few steps for loading data into the JSC engine: | ||||
| 
 | ||||
| A) The file must be read into a `char*` buffer (using standard C methods) | ||||
| 
 | ||||
| ```cpp | ||||
| size_t sz; char *file = read_file(argv[1], &sz); | ||||
| ``` | ||||
| 
 | ||||
| B) The typed array must be created with `JSObjectMakeTypedArrayWithBytesNoCopy` | ||||
| 
 | ||||
| ```cpp | ||||
| JSValueRef u8 = JSObjectMakeTypedArrayWithBytesNoCopy(ctx, kJSTypedArrayTypeUint8Array, file, sz, NULL, NULL, NULL); | ||||
| ``` | ||||
| 
 | ||||
| C) The typed array must be bound to a variable in the global scope: | ||||
| 
 | ||||
| ```cpp | ||||
| /* assign to `global.buf` */ | ||||
| JSObjectRef global = JSContextGetGlobalObject(ctx); | ||||
| JSStringRef key = JSStringCreateWithUTF8CString("buf"); | ||||
| JSObjectSetProperty(ctx, global, key, u8, 0, NULL); | ||||
| JSStringRelease(key); | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| ### Writing Files | ||||
| 
 | ||||
| <Tabs groupId="jsclang"> | ||||
|   <TabItem value="swift" label="Swift"> | ||||
| 
 | ||||
| When writing to binary string in JavaScriptCore, the result should be stored in | ||||
| a variable and converted to string in Swift: | ||||
| 
 | ||||
| @ -126,8 +270,41 @@ let out_path = shared_dir.appendingPathComponent("sheetjsw.xlsx"); | ||||
| try? out.write(to: out_path, atomically: false, encoding: String.Encoding.isoLatin1); | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem value="cpp" label="C++"> | ||||
| 
 | ||||
| The SheetJS `write` method with type `"buffer"` will return a `Uint8Array` object: | ||||
| 
 | ||||
| ```cpp | ||||
| DOIT("XLSX.write(wb, {type:'buffer', bookType:'xlsb'});") | ||||
| JSObjectRef u8 = JSValueToObject(ctx, result, NULL); | ||||
| ``` | ||||
| 
 | ||||
| Given the result object, `JSObjectGetTypedArrayLength` pulls the length into C: | ||||
| 
 | ||||
| ```cpp | ||||
| size_t sz = JSObjectGetTypedArrayLength(ctx, u8, NULL); | ||||
| ``` | ||||
| 
 | ||||
| `JSObjectGetTypedArrayBytesPtr` returns a pointer to the result buffer: | ||||
| 
 | ||||
| ```cpp | ||||
| char *buf = (char *)JSObjectGetTypedArrayBytesPtr(ctx, u8, NULL); | ||||
| ``` | ||||
| 
 | ||||
| The data can be written to file using standard C methods: | ||||
| 
 | ||||
| ```cpp | ||||
| FILE *f = fopen("sheetjsw.xlsb", "wb"); fwrite(buf, 1, sz, f); fclose(f); | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| ## Complete Example | ||||
| 
 | ||||
| ### Swift | ||||
| 
 | ||||
| :::note pass | ||||
| 
 | ||||
| This demo was tested in the following environments: | ||||
| @ -200,3 +377,162 @@ swiftc SheetJSCore.swift main.swift -o SheetJSwift | ||||
| 
 | ||||
| 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. | ||||
| 
 | ||||
| ### C++ | ||||
| 
 | ||||
| :::note pass | ||||
| 
 | ||||
| This demo was tested in the following environments: | ||||
| 
 | ||||
| | Architecture | Version          | Date       | | ||||
| |:-------------|:-----------------|:-----------| | ||||
| | `darwin-x64` | `7618.1.15.14.7` | 2024-04-24 | | ||||
| | `linux-x64`  | `7618.1.15.14.7` | 2024-04-24 | | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| 0) Install dependencies | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>Installation Notes</b> (click to show)</summary> | ||||
| 
 | ||||
| On the Steam Deck, a few dependencies must be installed before building JSC: | ||||
| 
 | ||||
| ```bash | ||||
| sudo pacman -Syu base-devel cmake ruby icu glibc linux-api-headers | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| 1) Create a project folder: | ||||
| 
 | ||||
| ```bash | ||||
| mkdir sheetjs-jsc | ||||
| cd sheetjs-jsc | ||||
| ``` | ||||
| 
 | ||||
| 2) Download and extract the WebKit snapshot: | ||||
| 
 | ||||
| ```bash | ||||
| curl -LO https://codeload.github.com/WebKit/WebKit/zip/refs/tags/WebKit-7618.1.15.14.7 | ||||
| mv WebKit-7618.1.15.14.7 WebKit.zip | ||||
| unzip WebKit.zip | ||||
| ``` | ||||
| 
 | ||||
| 3) Build JavaScriptCore: | ||||
| 
 | ||||
| <Tabs groupId="triple"> | ||||
|   <TabItem value="darwin-x64" label="MacOS"> | ||||
| 
 | ||||
| ```bash | ||||
| cd WebKit-WebKit-7618.1.15.14.7 | ||||
| Tools/Scripts/build-webkit --jsc-only --cmakeargs="-DENABLE_STATIC_JSC=ON" | ||||
| cd .. | ||||
| ``` | ||||
| 
 | ||||
| :::caution pass | ||||
| 
 | ||||
| When this demo was tested on macOS, the build failed with the error message | ||||
| 
 | ||||
| ``` | ||||
| Source/WTF/wtf/text/ASCIILiteral.h:65:34: error: use of undeclared identifier 'NSString' | ||||
|     WTF_EXPORT_PRIVATE RetainPtr<NSString> createNSString() const; | ||||
|                                  ^ | ||||
| 1 error generated. | ||||
| ``` | ||||
| 
 | ||||
| The referenced header file must be patched to declare `NSString`: | ||||
| 
 | ||||
| ```objc title="Source/WTF/wtf/text/ASCIILiteral.h (add highlighted lines)" | ||||
| #include <wtf/text/SuperFastHash.h> | ||||
| 
 | ||||
| // highlight-start | ||||
| #ifdef __OBJC__ | ||||
| @class NSString; | ||||
| #endif | ||||
| // highlight-end | ||||
| 
 | ||||
| namespace WTF { | ||||
| ``` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem value="linux-x64" label="Linux"> | ||||
| 
 | ||||
| ```bash | ||||
| cd WebKit-WebKit-7618.1.15.14.7 | ||||
| env CFLAGS="-Wno-error=dangling-reference -Wno-dangling-reference" CXXFLAGS="-Wno-error=dangling-reference -Wno-dangling-reference" Tools/Scripts/build-webkit --jsc-only --cmakeargs="-Wno-error -DENABLE_STATIC_JSC=ON -DUSE_THIN_ARCHIVES=OFF -DCMAKE_C_FLAGS="-Wno-error -Wno-dangling-reference" -DCMAKE_CXX_FLAGS=-Wno-error -Wno-dangling-reference"  --make-args="-j1 -Wno-error -Wno-error=dangling-reference" -j1 | ||||
| cd .. | ||||
| ``` | ||||
| 
 | ||||
| :::danger pass | ||||
| 
 | ||||
| When this was last tested on the Steam Deck, the build ran for 24 minutes! | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| 4) Create a symbolic link to the `Release` folder in the source tree: | ||||
| 
 | ||||
| ```bash | ||||
| ln -s WebKit-WebKit-7618.1.15.14.7/WebKitBuild/JSCOnly/Release/ . | ||||
| ``` | ||||
| 
 | ||||
| 5) Download [`sheetjs-jsc.c`](pathname:///jsc/sheetjs-jsc.c): | ||||
| 
 | ||||
| ```bash | ||||
| curl -LO https://docs.sheetjs.com/jsc/sheetjs-jsc.c | ||||
| ``` | ||||
| 
 | ||||
| 6) Compile the program: | ||||
| 
 | ||||
| <Tabs groupId="triple"> | ||||
|   <TabItem value="darwin-x64" label="MacOS"> | ||||
| 
 | ||||
| ```bash | ||||
| g++ -o sheetjs-jsc sheetjs-jsc.c -IRelease/JavaScriptCore/Headers -LRelease/lib -lbmalloc -licucore -lWTF -lJavaScriptCore -IRelease/JavaScriptCore/Headers | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem value="linux-x64" label="Linux"> | ||||
| 
 | ||||
| ```bash | ||||
| g++ -o sheetjs-jsc sheetjs-jsc.c -IRelease/JavaScriptCore/Headers -LRelease/lib -lJavaScriptCore -lWTF -lbmalloc -licui18n -licuuc -latomic -IRelease/JavaScriptCore/Headers | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| 7) Download the SheetJS Standalone script and the test file. Save both files in | ||||
| the project directory: | ||||
| 
 | ||||
| <ul> | ||||
| <li><a href={`https://cdn.sheetjs.com/xlsx-${current}/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> | ||||
| 
 | ||||
| <CodeBlock language="bash">{`\ | ||||
| curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js | ||||
| curl -LO https://sheetjs.com/pres.numbers`} | ||||
| </CodeBlock> | ||||
| 
 | ||||
| 8) Run the program: | ||||
| 
 | ||||
| ```bash | ||||
| ./sheetjs-jsc pres.numbers | ||||
| ``` | ||||
| 
 | ||||
| If successful, a CSV will be printed to console. The script also tries to write | ||||
| to `sheetjsw.xlsb`, which can be opened in a spreadsheet editor. | ||||
| 
 | ||||
| [^1]: See [`read` in "Reading Files"](/docs/api/parse-options) | ||||
| [^2]: See [`writeFile` in "Writing Files"](/docs/api/write-options) | ||||
| [^3]: See [`JSObjectMakeTypedArrayWithBytesNoCopy`](https://developer.apple.com/documentation/javascriptcore/jsobjectmaketypedarraywithbytesnocopy(_:_:_:_:_:_:_:)/) in the JavaScriptCore documentation. | ||||
| [^4]: See [`JSObjectGetTypedArrayLength`](https://developer.apple.com/documentation/javascriptcore/jsobjectgettypedarraylength(_:_:_:)/) in the JavaScriptCore documentation. | ||||
| [^5]: See [`JSObjectGetTypedArrayBytesPtr`]( | ||||
| https://developer.apple.com/documentation/javascriptcore/jsobjectgettypedarraybytesptr(_:_:_:)/) in the JavaScriptCore documentation. | ||||
| [^6]: See [`read` in "Reading Files"](/docs/api/parse-options) | ||||
| [^7]: See [`writeFile` in "Writing Files"](/docs/api/write-options) | ||||
| @ -117,7 +117,7 @@ The estimate can be recovered from the array: | ||||
| var estimate = int + num / den; | ||||
| ``` | ||||
| 
 | ||||
| If `mixed` is `false`, then `int = 0` and `0` ≤ `|num|` < `den` ≤ `D` | ||||
| If `mixed` is `false`, then `int = 0` and `0` < `den` ≤ `D` | ||||
| 
 | ||||
| If `mixed` is `true`, then `0` ≤ `num` < `den` ≤ `D` | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										112
									
								
								docz/static/jsc/sheetjs-jsc.c
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										112
									
								
								docz/static/jsc/sheetjs-jsc.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,112 @@ | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <JavaScriptCore/JavaScript.h> | ||||
| 
 | ||||
| /* simple wrapper to read the entire script file */ | ||||
| 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(buf, 1, fsize, f); | ||||
|   fclose(f); | ||||
|   return buf; | ||||
| } | ||||
| 
 | ||||
| #define JS_STR_TO_C \ | ||||
|   JSStringRef str = JSValueToStringCopy(ctx, result, NULL); \ | ||||
|   size_t sz = JSStringGetMaximumUTF8CStringSize(str); \ | ||||
|   char *buf = (char *)malloc(sz); \ | ||||
|   JSStringGetUTF8CString(str, buf, sz); \ | ||||
| 
 | ||||
| #define DOIT(cmd) \ | ||||
|   JSStringRef script = JSStringCreateWithUTF8CString(cmd); \ | ||||
|   JSValueRef result = JSEvaluateScript(ctx, script, NULL, NULL, 0, NULL); \ | ||||
|   JSStringRelease(script); | ||||
| 
 | ||||
| int main(int argc, char **argv) { | ||||
|   int res = 0; | ||||
|   size_t sz = 0; | ||||
|   char *file = NULL; | ||||
| 
 | ||||
|   /* initialize */ | ||||
|   JSGlobalContextRef ctx = JSGlobalContextCreate(NULL); | ||||
|   /* JSC does not expose a standard "global" by default */ | ||||
|   { DOIT("var global = (function(){ return this; }).call(null);") } | ||||
| 
 | ||||
|   /* load library */ | ||||
|   { | ||||
|     file = read_file("xlsx.full.min.js", &sz); | ||||
|     DOIT(file); | ||||
|     free(file); | ||||
|   } | ||||
| 
 | ||||
|   /* get version string */ | ||||
|   { | ||||
|     DOIT("XLSX.version") | ||||
| 
 | ||||
|     if(!JSValueIsString(ctx, result)) { | ||||
|       printf("Could not get SheetJS version.\n"); | ||||
|       res = 1; goto cleanup; | ||||
|     } | ||||
| 
 | ||||
|     JS_STR_TO_C | ||||
| 
 | ||||
|     printf("SheetJS library version %s\n", buf); | ||||
|     free(buf); | ||||
|     JSStringRelease(str); | ||||
|   } | ||||
| 
 | ||||
|   /* read file */ | ||||
|   file = read_file(argv[1], &sz); | ||||
|   { | ||||
|     /* push data to JSC */ | ||||
|     JSValueRef u8 = JSObjectMakeTypedArrayWithBytesNoCopy(ctx, kJSTypedArrayTypeUint8Array, file, sz, NULL, NULL, NULL); | ||||
| 
 | ||||
|     /* assign to `global.buf` */ | ||||
|     JSObjectRef global = JSContextGetGlobalObject(ctx); | ||||
|     JSStringRef key = JSStringCreateWithUTF8CString("buf"); | ||||
|     JSObjectSetProperty(ctx, global, key, u8, 0, NULL); | ||||
|     JSStringRelease(key); | ||||
|   } | ||||
| 
 | ||||
|   /* parse workbook and print CSV */ | ||||
|   { | ||||
|     DOIT( | ||||
|       "var wb = XLSX.read(global.buf);" | ||||
|       "var ws = wb.Sheets[wb.SheetNames[0]];" | ||||
|       "XLSX.utils.sheet_to_csv(ws)" | ||||
|     ) | ||||
| 
 | ||||
|     if(!JSValueIsString(ctx, result)) { | ||||
|       printf("Could not generate CSV.\n"); | ||||
|       res = 2; goto cleanup; | ||||
|     } | ||||
| 
 | ||||
|     JS_STR_TO_C | ||||
| 
 | ||||
|     printf("%s\n", buf); | ||||
|     free(buf); | ||||
|     JSStringRelease(str); | ||||
|   } | ||||
| 
 | ||||
|   /* write file */ | ||||
|   { | ||||
|     DOIT("XLSX.write(wb, {type:'buffer', bookType:'xlsb'});") | ||||
| 
 | ||||
|     /* pull Uint8Array data back to C */ | ||||
|     JSObjectRef u8 = JSValueToObject(ctx, result, NULL); | ||||
|     size_t sz = JSObjectGetTypedArrayLength(ctx, u8, NULL); | ||||
|     char *buf = (char *)JSObjectGetTypedArrayBytesPtr(ctx, u8, NULL); | ||||
| 
 | ||||
|     /* save file */ | ||||
|     FILE *f = fopen("sheetjsw.xlsb", "wb"); fwrite(buf, 1, sz, f); fclose(f); | ||||
|   } | ||||
| 
 | ||||
| cleanup: | ||||
|   // Release the JavaScript context
 | ||||
|   JSGlobalContextRelease(ctx); | ||||
|   if(file) free(file); | ||||
| 
 | ||||
|   return res; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user