docs.sheetjs.com/docz/docs/03-demos/31-engines/11_perl.md
2023-02-13 04:20:49 -05:00

4.7 KiB

title pagination_prev pagination_next
Perl + JE demos/cli demos/clipboard

:::warning

In a production application, it is strongly recommended to use a binding for a C engine like JavaScript::Duktape

:::

JE is a pure-Perl JavaScript engine.

The Extendscript build can be parsed and evaluated in a JE context.

Integration Details

The engine deviates from ES3. Modifying prototypes can fix some behavior:

/* 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:

## 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:

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:

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:
cpan install JE
  1. Download the ExtendScript build:
curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.extendscript.js
  1. Save the following to 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);
  1. Download a test file and run:
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.