From 8517e019e05a8d695aa0edb2de40712b98510aa1 Mon Sep 17 00:00:00 2001 From: SheetJS Date: Tue, 14 Feb 2023 20:00:49 -0500 Subject: [PATCH] rhino --- docz/docs/03-demos/31-engines/03_rhino.md | 196 ++++++++++++++++++++++ docz/docs/03-demos/31-engines/09_rb.md | 2 +- docz/docs/03-demos/31-engines/11_perl.md | 62 +------ docz/docs/03-demos/31-engines/index.md | 67 +------- docz/docs/09-miscellany/02-errors.md | 19 +++ docz/static/perl/SheetJE.pl | 59 +++++++ 6 files changed, 280 insertions(+), 125 deletions(-) create mode 100644 docz/docs/03-demos/31-engines/03_rhino.md create mode 100644 docz/static/perl/SheetJE.pl diff --git a/docz/docs/03-demos/31-engines/03_rhino.md b/docz/docs/03-demos/31-engines/03_rhino.md new file mode 100644 index 0000000..e8ec920 --- /dev/null +++ b/docz/docs/03-demos/31-engines/03_rhino.md @@ -0,0 +1,196 @@ +--- +title: Java + Rhino +pagination_prev: demos/cli +pagination_next: demos/clipboard +--- + +Rhino is an ES3+ engine in Java. + +The [Standalone scripts](/docs/getting-started/installation/standalone) can be +parsed and evaluated in a Rhino context. + +This demo wraps workbooks and sheets into separate Java classes. The final +result is a JAR. + +:::caution + +Rhino does not support Uint8Array, so certain formats like NUMBERS cannot be +parsed or written from Rhino JS code! + +::: + +## Integration Details + +:::note + +Due to code generation errors, optimization must be turned off: + +```java +Context context = Context.enter(); +context.setOptimizationLevel(-1); +``` + +::: + +Binary strings can be passed back and forth. + +_Initialize Rhino_ + +Rhino does not provide a `global` variable. It can be created: + +```java +Context cx = Context.enter(); +Scriptable scope = cx.initStandardObjects(); + +/* Disable optimization */ +cx.setOptimizationLevel(-1); + +/* Initialize `global` variable */ +// highlight-start +String s = "var global = (function(){ return this; }).call(null);"; +cx.evaluateString(scope, s, "", 1, null); +// highlight-end +``` + +_Load SheetJS Scripts_ + +The main library can be loaded by reading the scripts from the Java Archive and +evaluating in the Rhino context: + +```java +/* pull source from JAR */ +String s = new Scanner( + SheetJS.class.getResourceAsStream("/xlsx.full.min.js") +).useDelimiter("\\Z").next(); +/* evaluate */ +cx.evaluateString(scope, s, "", 1, null); +``` + +To confirm the library is loaded, `XLSX.version` can be inspected: + +```swift +/* get handle to XLSX */ +Object XLSX = scope.get("XLSX", scope); +if(XLSX == Scriptable.NOT_FOUND) throw new Exception("XLSX not found"); + +/* get the version string */ +String version = ((NativeObject)XLSX).get("version", scope).toString(); +System.out.println("SheetJS version " + version); +``` + +### Reading Files + +A binary string can be generated from a `byte` array: + +```java + static String read_file(String file) throws IOException { + /* read file -> array of bytes */ + byte[] b = Files.readAllBytes(Paths.get(file)); + + /* construct binary string */ + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < b.length; ++i) sb.append(Character.toString((char)(b[i] < 0 ? b[i] + 256 : b[i]))); + return sb.toString(); + } +``` + +This string can be loaded into the JS engine and processed: + +```java + /* options argument */ + String os = "q = {'type':'binary', 'WTF':1};"; + NativeObject o = (NativeObject)cx.evaluateString(scope, os, "", 2, null); + + /* set up function arguments */ + String data = read_file(path_to_file); + Object args[] = {data, o}; + + /* call read -> wb workbook */ + NativeObject nXLSX = (NativeObject)XLSX; + Function readfunc = (Function)XLSX.get("read", scope); + NativeObject wb = (NativeObject)readfunc.call(cx, scope, nXLSX, args); +``` + +`wb` will be a reference to the JS workbook object. + +## Complete Example + +:::note + +This demo was tested on 2023 February 14 using Rhino 1.7.14. + +::: + +0) Ensure Java is installed. Create a folder for the project, download the +[JAR](https://github.com/mozilla/rhino/releases/download/Rhino1_7_14_Release/rhino-1.7.14.jar) +and rename to `rhino.jar`: + +```bash +mkdir sheetjs-java +cd sheetjs-java +curl -L -o rhino.jar https://github.com/mozilla/rhino/releases/download/Rhino1_7_14_Release/rhino-1.7.14.jar +``` + +1) Download the standalone script and the test file: + + + +```bash +curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js +curl -LO https://sheetjs.com/pres.xlsx +``` + +2) Download [`SheetJSRhino.zip`](pathname:///rhino/SheetJSRhino.zip) and unzip + +```bash +curl -LO https://docs.sheetjs.com/rhino/SheetJSRhino.zip +unzip SheetJSRhino.zip +``` + +3) Save the following code to `SheetJSRhino.java`: + +```java title="SheetJSRhino.java" +/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */ +/* vim: set ts=2: */ +import com.sheetjs.SheetJS; +import com.sheetjs.SheetJSFile; +import com.sheetjs.SheetJSSheet; + +public class SheetJSRhino { + public static void main(String args[]) throws Exception { + try { + SheetJS sjs = new SheetJS(); + + /* open file */ + SheetJSFile xl = sjs.read_file(args[0]); + + /* get sheetnames */ + String[] sheetnames = xl.get_sheet_names(); + System.err.println(sheetnames[0]); + + /* convert to CSV */ + SheetJSSheet sheet = xl.get_sheet(0); + String csv = sheet.get_csv(); + System.out.println(csv); + } catch(Exception e) { throw e; } finally { SheetJS.close(); } + } +} +``` + +4) Assemble `SheetJS.jar` from the demo code: + +```bash +javac -cp .:rhino.jar SheetJSRhino.java +jar -cf SheetJS.jar SheetJSRhino.class com/sheetjs/*.class xlsx.full.min.js +``` + +5) Test the program: + +```bash +java -cp .:SheetJS.jar:rhino.jar SheetJSRhino pres.xlsx +``` + +If successful, a CSV will be printed to console. diff --git a/docz/docs/03-demos/31-engines/09_rb.md b/docz/docs/03-demos/31-engines/09_rb.md index 4c1b6df..10074f0 100644 --- a/docz/docs/03-demos/31-engines/09_rb.md +++ b/docz/docs/03-demos/31-engines/09_rb.md @@ -1,5 +1,5 @@ --- -title: Ruby Bindings +title: Ruby + Bindings pagination_prev: demos/cli pagination_next: demos/clipboard --- diff --git a/docz/docs/03-demos/31-engines/11_perl.md b/docz/docs/03-demos/31-engines/11_perl.md index 88815ec..e288b58 100644 --- a/docz/docs/03-demos/31-engines/11_perl.md +++ b/docz/docs/03-demos/31-engines/11_perl.md @@ -115,66 +115,10 @@ cpan install JE curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.extendscript.js ``` -3) Save the following to `SheetJE.pl`: +3) Download the demo [`SheetJE.pl`](pathname:///perl/SheetJE.pl): -```perl title="SheetJE.pl" -use JE; -use File::Slurp; -use MIME::Base64 qw( encode_base64 ); - -## Initialize -say STDERR "Initializing Engine"; -my $je = new JE; -$je->eval("var global = (function(){ return this; }).call(null);"); -$je->eval(<<'EOF'); -Number.prototype.charCodeAt = function(n) { return this + 48; }; -var string = ""; for(var i = 0; i < 256; ++i) string += String.fromCharCode(i); -String.prototype.charCodeAt = function(n) { - var result = string.indexOf(this.charAt(n)); - if(result == -1) throw this.charAt(n); - return result; -}; -String.prototype.old_match = String.prototype.match; -String.prototype.match = function(p) { - var result = this.old_match(p); - return (Array.isArray(result) && result.length == 0) ? null : result; -}; -EOF - -## Load SheetJS source -say STDERR "Loading SheetJS Library"; -my $src = read_file('xlsx.extendscript.js', { binmode => ':raw' }); -$src =~ s/^\xEF\xBB\xBF//; -my $XLSX = $je->eval($src); - -## Set up conversion method -$je->eval(<<'EOF'); -function sheetjsparse(data) { try { - return XLSX.read(String(data), {type: "base64", WTF:1}); -} catch(e) { return String(e); } } -function sheetjscsv(wb) { try { - var ws = wb.Sheets[wb.SheetNames[0]]; - return XLSX.utils.sheet_to_csv(ws); -} catch(e) { return String(e); } } -function sheetjswrite(wb) { try { - return XLSX.write(wb, { WTF:1, bookType: "fods", type: "string" }); -} catch(e) { return String(e); } } -EOF - -## Read file -say STDERR "Parsing " . $ARGV[0]; -my $raw_data = encode_base64(read_file($ARGV[0], { binmode => ':raw' }), ""); -$workbook = $je->method(sheetjsparse => $raw_data); - -## Convert to CSV -say STDERR "Converting to CSV"; -my $csv_data = $je->method(sheetjscsv => $workbook); -print $csv_data; - -## Write to SheetJE.fods -say STDERR "Writing to SheetJE.fods"; -my $fods = $je->method(sheetjswrite => $workbook); -write_file("SheetJE.fods", $fods); +```bash +curl -LO https://docs.sheetjs.com/perl/SheetJE.pl ``` 4) Download a test file and run: diff --git a/docz/docs/03-demos/31-engines/index.md b/docz/docs/03-demos/31-engines/index.md index 73d93c2..369cbe7 100644 --- a/docz/docs/03-demos/31-engines/index.md +++ b/docz/docs/03-demos/31-engines/index.md @@ -301,72 +301,9 @@ If successful, the script will generate `SheetJSQuick.xlsx`. ### 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. +Rhino is an ES3+ engine in Java. -Due to code generation errors, optimization must be turned off: - -```java -Context context = Context.enter(); -context.setOptimizationLevel(-1); -``` - -
Complete Example (click to show) - -0) Download the appropriate Rhino build and rename to `rhino.jar` - -1) Download [`SheetJSRhino.zip`](pathname:///rhino/SheetJSRhino.zip) and unzip - -2) Save the following code to `SheetJSRhino.java`: - -```java title="SheetJSRhino.java" -/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */ -/* vim: set ts=2: */ -import com.sheetjs.SheetJS; -import com.sheetjs.SheetJSFile; -import com.sheetjs.SheetJSSheet; - -public class SheetJSRhino { - public static void main(String args[]) throws Exception { - try { - SheetJS sjs = new SheetJS(); - - /* open file */ - SheetJSFile xl = sjs.read_file(args[0]); - - /* get sheetnames */ - String[] sheetnames = xl.get_sheet_names(); - System.err.println(sheetnames[0]); - - /* convert to CSV */ - SheetJSSheet sheet = xl.get_sheet(0); - String csv = sheet.get_csv(); - - System.out.println(csv); - - } catch(Exception e) { - throw e; - } finally { - SheetJS.close(); - } - } -} -``` - -3) Assemble `SheetJS.jar` from the demo code: - -```bash -javac -cp .:rhino.jar SheetJSRhino.java -jar -cf SheetJS.jar SheetJSRhino.class com/sheetjs/*.class -``` - -4) Download and test: - -```bash -java -cp .:SheetJS.jar:rhino.jar SheetJSRhino pres.xlsx -``` - -
+This demo has been moved [to a dedicated page](/docs/demos/engines/rhino). ## Legacy Engines diff --git a/docz/docs/09-miscellany/02-errors.md b/docz/docs/09-miscellany/02-errors.md index 5aa5c94..fa39c98 100644 --- a/docz/docs/09-miscellany/02-errors.md +++ b/docz/docs/09-miscellany/02-errors.md @@ -129,6 +129,25 @@ async function fetch_workbook_and_error_on_404(url) { } ``` +#### Cloudflare Worker "Error: Script startup exceeded CPU time limit." + +This may show up in projects with many dependencies. The official workaround is +dynamic `import`. For example: + +```ts +export default { + async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { + // highlight-next-line + const XLSX = await import("xlsx"); // dynamic import + const wb = XLSX.read("abc\n123", {type: "string"}); + const buf = XLSX.write(wb, {type: "buffer", bookType: "xlsb"}); + const response = new Response(buf); + response.headers.set("Content-Disposition", 'attachment; filename="cf.xlsb"'); + return response; + }, +}; +``` + #### SCRIPT5022: DataCloneError IE10 does not properly support `Transferable`. diff --git a/docz/static/perl/SheetJE.pl b/docz/static/perl/SheetJE.pl new file mode 100644 index 0000000..e31927e --- /dev/null +++ b/docz/static/perl/SheetJE.pl @@ -0,0 +1,59 @@ +#!/usr/bin/env perl +# SheetJE.pl (c) SheetJS LLC -- https://sheetjs.com +use JE; +use File::Slurp; +use MIME::Base64 qw( encode_base64 ); + +## Initialize +say STDERR "Initializing Engine"; +my $je = new JE; +$je->eval("var global = (function(){ return this; }).call(null);"); +$je->eval(<<'EOF'); +Number.prototype.charCodeAt = function(n) { return this + 48; }; +var string = ""; for(var i = 0; i < 256; ++i) string += String.fromCharCode(i); +String.prototype.charCodeAt = function(n) { + var result = string.indexOf(this.charAt(n)); + if(result == -1) throw this.charAt(n); + return result; +}; +String.prototype.old_match = String.prototype.match; +String.prototype.match = function(p) { + var result = this.old_match(p); + return (Array.isArray(result) && result.length == 0) ? null : result; +}; +EOF + +## Load SheetJS source +say STDERR "Loading SheetJS Library"; +my $src = read_file('xlsx.extendscript.js', { binmode => ':raw' }); +$src =~ s/^\xEF\xBB\xBF//; +my $XLSX = $je->eval($src); + +## Set up conversion method +$je->eval(<<'EOF'); +function sheetjsparse(data) { try { + return XLSX.read(String(data), {type: "base64", WTF:1}); +} catch(e) { return String(e); } } +function sheetjscsv(wb) { try { + var ws = wb.Sheets[wb.SheetNames[0]]; + return XLSX.utils.sheet_to_csv(ws); +} catch(e) { return String(e); } } +function sheetjswrite(wb) { try { + return XLSX.write(wb, { WTF:1, bookType: "fods", type: "string" }); +} catch(e) { return String(e); } } +EOF + +## Read file +say STDERR "Parsing " . $ARGV[0]; +my $raw_data = encode_base64(read_file($ARGV[0], { binmode => ':raw' }), ""); +$workbook = $je->method(sheetjsparse => $raw_data); + +## Convert to CSV +say STDERR "Converting to CSV"; +my $csv_data = $je->method(sheetjscsv => $workbook); +print $csv_data; + +## Write to SheetJE.fods +say STDERR "Writing to SheetJE.fods"; +my $fods = $je->method(sheetjswrite => $workbook); +write_file("SheetJE.fods", $fods); \ No newline at end of file