forked from sheetjs/docs.sheetjs.com
		
	perl
This commit is contained in:
		
							parent
							
								
									0bf5ac0fa8
								
							
						
					
					
						commit
						909b776235
					
				| @ -164,7 +164,7 @@ gcc -std=c99 -Wall -osheetjs.duk sheetjs.duk.c duktape.c -lm | ||||
| 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 | ||||
| ### Flow Diagram | ||||
| 
 | ||||
| ```mermaid | ||||
| sequenceDiagram | ||||
| @ -229,4 +229,55 @@ sequenceDiagram | ||||
|     C->>F: Write File | ||||
|   end | ||||
|   deactivate C | ||||
| ``` | ||||
| 
 | ||||
| ## Bindings | ||||
| 
 | ||||
| Duktape is easily embeddable.  Bindings exist for many languages. | ||||
| 
 | ||||
| ### Perl | ||||
| 
 | ||||
| The Perl binding for Duktape is available on CPAN: | ||||
| 
 | ||||
| ```bash | ||||
| cpan install JavaScript::Duktape | ||||
| ``` | ||||
| 
 | ||||
| The Perl binding does not have raw `Buffer` ops, so Base64 strings are used. | ||||
| With the [Extendscript](/docs/getting-started/installation/extendscript) build: | ||||
| 
 | ||||
| ```perl SheetJSDuk.pl | ||||
| # usage: perl SheetJSDuk.pl path/to/file | ||||
| use JavaScript::Duktape; | ||||
| use File::Slurp; | ||||
| use MIME::Base64 qw( encode_base64 decode_base64 ); | ||||
| 
 | ||||
| # Initialize | ||||
| my $js = JavaScript::Duktape->new( max_memory => 1024 * 1024 * 1024 ); | ||||
| $js->eval("var global = (function(){ return this; }).call(null);"); | ||||
| 
 | ||||
| # Load the ExtendScript build | ||||
| my $src = read_file('xlsx.extendscript.js', { binmode => ':raw' }); | ||||
| $src =~ s/^\xEF\xBB\xBF//; | ||||
| my $XLSX = $js->eval($src); | ||||
| 
 | ||||
| # Print version number | ||||
| $js->set('log' => sub { print $_[0], "\n"; }); | ||||
| $js->eval("log('SheetJS library version ' + XLSX.version);"); | ||||
| 
 | ||||
| # Parse File | ||||
| my $raw_data = encode_base64(read_file($ARGV[0], { binmode => ':raw' }), ""); | ||||
| $js->set("b64", $raw_data); | ||||
| $js->eval(qq{ | ||||
|   global.wb = XLSX.read(b64, {type: "base64"}); | ||||
|   global.ws = wb.Sheets[wb.SheetNames[0]]; | ||||
| }); | ||||
| 
 | ||||
| # Print first worksheet CSV | ||||
| my $csv = $js->eval('XLSX.utils.sheet_to_csv(global.ws)'); | ||||
| print $csv | ||||
| 
 | ||||
| # Write XLSB file | ||||
| my $xlsb = $js->eval("XLSX.write(global.wb, {type:'base64', bookType:'xlsb'})"); | ||||
| write_file("SheetJSDuk.xlsb", decode_base64($xlsb)); | ||||
| ``` | ||||
							
								
								
									
										188
									
								
								docz/docs/03-demos/31-engines/11_perl.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										188
									
								
								docz/docs/03-demos/31-engines/11_perl.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,188 @@ | ||||
| --- | ||||
| title: Perl + JE | ||||
| pagination_prev: demos/cli | ||||
| pagination_next: demos/clipboard | ||||
| --- | ||||
| 
 | ||||
| :::warning | ||||
| 
 | ||||
| In a production application, it is strongly recommended to use a binding for a | ||||
| C engine like [`JavaScript::Duktape`](/docs/demos/engines/duktape) | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| JE is a pure-Perl JavaScript engine. | ||||
| 
 | ||||
| The [Extendscript build](/docs/getting-started/installation/extendscript) can be | ||||
| parsed and evaluated in a JE context. | ||||
| 
 | ||||
| 
 | ||||
| ## Integration Details | ||||
| 
 | ||||
| The engine deviates from ES3.  Modifying prototypes can fix some behavior: | ||||
| 
 | ||||
| ```js | ||||
| /* String#charCodeAt is missing */ | ||||
| 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; | ||||
| }; | ||||
| 
 | ||||
| /* workaround for String split bug */ | ||||
| Number.prototype.charCodeAt = function(n) { return this + 48; }; | ||||
| 
 | ||||
| /* String#match bug with empty results */ | ||||
| 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; | ||||
| }; | ||||
| ``` | ||||
| 
 | ||||
| When loading the ExtendScript build, the BOM must be removed: | ||||
| 
 | ||||
| ```perl | ||||
| ## Load SheetJS source | ||||
| my $src = read_file('xlsx.extendscript.js', { binmode => ':raw' }); | ||||
| $src =~ s/^\xEF\xBB\xBF//; ## remove UTF8 BOM | ||||
| my $XLSX = $je->eval($src); | ||||
| ``` | ||||
| 
 | ||||
| ### Reading Files | ||||
| 
 | ||||
| Data should be passed as Base64 strings: | ||||
| 
 | ||||
| ```perl | ||||
| use File::Slurp; | ||||
| use MIME::Base64 qw( encode_base64 ); | ||||
| 
 | ||||
| ## 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); } } | ||||
| EOF | ||||
| 
 | ||||
| ## Read file | ||||
| my $raw_data = encode_base64(read_file($ARGV[0], { binmode => ':raw' }), ""); | ||||
| 
 | ||||
| ## Call method with data | ||||
| $return_val = $je->method(sheetjsparse => $raw_data); | ||||
| ``` | ||||
| 
 | ||||
| ### Writing Files | ||||
| 
 | ||||
| Due to bugs in data interchange, it is strongly recommended to use a simple | ||||
| format like `.fods`: | ||||
| 
 | ||||
| ```perl | ||||
| use File::Slurp; | ||||
| 
 | ||||
| ## Set up conversion method | ||||
| $je->eval(<<'EOF'); | ||||
| function sheetjswrite(wb) { try { | ||||
|   return XLSX.write(wb, { WTF:1, bookType: "fods", type: "string" }); | ||||
| } catch(e) { return String(e); } } | ||||
| EOF | ||||
| 
 | ||||
| ## Generate file | ||||
| my $fods = $je->method(sheetjswrite => $workbook); | ||||
| 
 | ||||
| ## Write to filesystem | ||||
| write_file("SheetJE.fods", $fods); | ||||
| ``` | ||||
| 
 | ||||
| ## Complete Example | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was tested on 2023 February 12 against JE 0.066 | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| 1) Install `JE` through CPAN: | ||||
| 
 | ||||
| ```bash | ||||
| cpan install JE | ||||
| ``` | ||||
| 
 | ||||
| 2) Download the [ExtendScript build](/docs/getting-started/installation/extendscript): | ||||
| 
 | ||||
| ```bash | ||||
| curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.extendscript.js | ||||
| ``` | ||||
| 
 | ||||
| 3) Save the following to `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); | ||||
| ``` | ||||
| 
 | ||||
| 4) Download a test file and run: | ||||
| 
 | ||||
| ```bash | ||||
| curl -LO https://sheetjs.com/data/cd.xls | ||||
| perl SheetJE.pl cd.xls | ||||
| ``` | ||||
| 
 | ||||
| After a short wait, the contents will be displayed in CSV form.  It will also | ||||
| write a file `SheetJE.fods` that can be opened in LibreOffice. | ||||
| @ -63,7 +63,7 @@ Duktape is an embeddable JS engine written in C. It has been ported to a number | ||||
| of exotic architectures and operating systems. | ||||
| 
 | ||||
| This demo has been moved [to a dedicated page](/docs/demos/engines/duktape). | ||||
| 
 | ||||
| The demo includes examples in C and Perl. | ||||
| 
 | ||||
| ### Goja | ||||
| 
 | ||||
|  | ||||
| @ -142,7 +142,7 @@ const config = { | ||||
|       prism: { | ||||
|         theme: lightCodeTheme, | ||||
|         darkTheme: darkCodeTheme, | ||||
|         additionalLanguages: [ "swift", "java", "csharp" ], | ||||
|         additionalLanguages: [ "swift", "java", "csharp", "perl" ], | ||||
|       }, | ||||
|       liveCodeBlock: { | ||||
|         playgroundPosition: 'top' | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user