XLS Unicode Property Lists [ci skip]

This commit is contained in:
SheetJS 2021-10-02 21:41:36 -04:00
parent a376a6c78c
commit b98a40235f
75 changed files with 1529 additions and 312 deletions

@ -41,6 +41,18 @@ function parse_VtStringBase(blob, stringType, pad) {
function parse_VtString(blob, t/*:number*/, pad/*:?boolean*/) { return parse_VtStringBase(blob, t, pad === false ? 0: 4); }
function parse_VtUnalignedString(blob, t/*:number*/) { if(!t) throw new Error("VtUnalignedString must have positive length"); return parse_VtStringBase(blob, t, 0); }
/* [MS-OSHARED] 2.3.3.1.7 VtVecLpwstrValue */
function parse_VtVecLpwstrValue(blob)/*:Array<string>*/ {
var length = blob.read_shift(4);
var ret/*:Array<string>*/ = [];
for(var i = 0; i != length; ++i) {
var start = blob.l;
ret[i] = blob.read_shift(0, 'lpwstr').replace(chr0,'');
if((blob.l - start) & 0x02) blob.l += 2;
}
return ret;
}
/* [MS-OSHARED] 2.3.3.1.9 VtVecUnalignedLpstrValue */
function parse_VtVecUnalignedLpstrValue(blob)/*:Array<string>*/ {
var length = blob.read_shift(4);
@ -49,14 +61,12 @@ function parse_VtVecUnalignedLpstrValue(blob)/*:Array<string>*/ {
return ret;
}
/* [MS-OSHARED] 2.3.3.1.10 VtVecUnalignedLpstr */
function parse_VtVecUnalignedLpstr(blob)/*:Array<string>*/ {
return parse_VtVecUnalignedLpstrValue(blob);
}
/* [MS-OSHARED] 2.3.3.1.13 VtHeadingPair */
function parse_VtHeadingPair(blob) {
var start = blob.l;
var headingString = parse_TypedPropertyValue(blob, VT_USTR);
if(blob[blob.l] == 0x00 && blob[blob.l+1] == 0x00 && ((blob.l - start) & 0x02)) blob.l += 2;
var headerParts = parse_TypedPropertyValue(blob, VT_I4);
return [headingString, headerParts];
}
@ -65,16 +75,10 @@ function parse_VtHeadingPair(blob) {
function parse_VtVecHeadingPairValue(blob) {
var cElements = blob.read_shift(4);
var out = [];
for(var i = 0; i != cElements / 2; ++i) out.push(parse_VtHeadingPair(blob));
for(var i = 0; i < cElements / 2; ++i) out.push(parse_VtHeadingPair(blob));
return out;
}
/* [MS-OSHARED] 2.3.3.1.15 VtVecHeadingPair */
function parse_VtVecHeadingPair(blob) {
// NOTE: When invoked, wType & padding were already consumed
return parse_VtVecHeadingPairValue(blob);
}
/* [MS-OLEPS] 2.18.1 Dictionary (uses 2.17, 2.16) */
function parse_dictionary(blob,CodePage) {
var cnt = blob.read_shift(4);
@ -113,7 +117,7 @@ function parse_TypedPropertyValue(blob, type/*:number*/, _opts)/*:any*/ {
var t = blob.read_shift(2), ret, opts = _opts||{};
blob.l += 2;
if(type !== VT_VARIANT)
if(t !== type && VT_CUSTOM.indexOf(type)===-1) throw new Error('Expected type ' + type + ' saw ' + t);
if(t !== type && VT_CUSTOM.indexOf(type)===-1 && !((type & 0xFFFE) == 0x101E && (t & 0xFFFE) == 0x101E)) throw new Error('Expected type ' + type + ' saw ' + t);
switch(type === VT_VARIANT ? t : type) {
case 0x02 /*VT_I2*/: ret = blob.read_shift(2, 'i'); if(!opts.raw) blob.l += 2; return ret;
case 0x03 /*VT_I4*/: ret = blob.read_shift(4, 'i'); return ret;
@ -126,8 +130,10 @@ function parse_TypedPropertyValue(blob, type/*:number*/, _opts)/*:any*/ {
case 0x47 /*VT_CF*/: return parse_ClipboardData(blob);
case 0x50 /*VT_STRING*/: return parse_VtString(blob, t, !opts.raw).replace(chr0,'');
case 0x51 /*VT_USTR*/: return parse_VtUnalignedString(blob, t/*, 4*/).replace(chr0,'');
case 0x100C /*VT_VECTOR|VT_VARIANT*/: return parse_VtVecHeadingPair(blob);
case 0x101E /*VT_LPSTR*/: return parse_VtVecUnalignedLpstr(blob);
case 0x100C /*VT_VECTOR|VT_VARIANT*/: return parse_VtVecHeadingPairValue(blob);
case 0x101E /*VT_VECTOR|VT_LPSTR*/:
case 0x101F /*VT_VECTOR|VT_LPWSTR*/:
return t == 0x101F ? parse_VtVecLpwstrValue(blob) : parse_VtVecUnalignedLpstrValue(blob);
default: throw new Error("TypedPropertyValue unrecognized type " + type + " " + t);
}
}

@ -19,7 +19,7 @@ can be installed with Bash on Windows or with `cygwin`.
**Frameworks and APIs**
- [`angularjs`](angular/)
- [`angular 2 / 4 / 5 / 6 and ionic`](angular2/)
- [`angular and ionic`](angular2/)
- [`knockout`](knockout/)
- [`meteor`](meteor/)
- [`react and react-native`](react/)

@ -7,3 +7,4 @@ rhino
shim.min.js
xlsx.*.js
payload.js
goja

@ -27,7 +27,6 @@ swift: base ## swift demo
.PHONY: goja
goja: base ## goja demo
if [ ! -e xlsx.core.min.js ]; then cp ../../dist/xlsx.core.min.js .; fi
go build goja.go
for ext in xlsx xlsb biff8.xls xml.xls; do ./goja sheetjs.$$ext; done

@ -51,7 +51,7 @@ Binary strings can be passed back and forth using `String.Encoding.isoLatin1`:
/* parse sheetjs.xls */
let file_path = shared_dir.appendingPathComponent("sheetjs.xls");
let data: String! = try String(contentsOf: file_path, encoding: String.Encoding.isoLatin1);
context.setObject(data, forKeyedSubscript: "payload" as (NSCopying & NSObjectProtocol)!);
context.setObject(data, forKeyedSubscript: "payload" as (NSCopying & NSObjectProtocol));
src = "var wb = XLSX.read(payload, {type:'binary'});";
context.evaluateScript(src);
@ -108,14 +108,15 @@ target builds a very simple payload with the data.
## Duktape
[Duktape](http://duktape.org/) is an embeddable JS engine written in C. The
amalgamation makes integration extremely simple! It supports `Buffer` natively:
amalgamation makes integration extremely simple! It supports `Buffer` natively
but should be sliced before processing:
```C
/* parse a C char array as a workbook object */
duk_push_external_buffer(ctx);
duk_config_buffer(ctx, -1, buf, len);
duk_put_global_string(ctx, "buf");
duk_eval_string_noresult("workbook = XLSX.read(buf, {type:'buffer'});");
duk_eval_string_noresult("workbook = XLSX.read(buf.slice(0, buf.length), {type:'buffer'});");
/* write a workbook object to a C char array */
duk_eval_string(ctx, "XLSX.write(workbook, {type:'array', bookType:'xlsx'})");
@ -157,9 +158,8 @@ wh.write(out, 0, ab.byteLength); wh.close();
## Goja
Goja is a pure Go implementation of ECMAScript 5. As of this writing, there are
some issues with processing Unicode data, but the `xlsx.core.min.js` script can
be processed. `[]byte` should be transformed to a binary string in the engine:
Goja is a pure Go implementation of ECMAScript 5. `[]byte` should be converted
to a binary string in the engine:
```go
/* read file */

@ -9,7 +9,7 @@ enum SJSError: Error {
class SJSWorksheet {
var context: JSContext!;
var wb: JSValue!; var ws: JSValue!;
var wb: JSValue; var ws: JSValue;
var idx: Int32;
func toCSV() throws -> String {
@ -26,7 +26,7 @@ class SJSWorksheet {
class SJSWorkbook {
var context: JSContext!;
var wb: JSValue!; var SheetNames: JSValue!; var Sheets: JSValue!;
var wb: JSValue; var SheetNames: JSValue; var Sheets: JSValue;
func getSheetAtIndex(idx: Int32) throws -> SJSWorksheet {
let SheetName: String = SheetNames.atIndex(Int(idx)).toString();
@ -37,8 +37,8 @@ class SJSWorkbook {
func writeBStr(bookType: String = "xlsx") throws -> String {
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
context.evaluateScript(String(format: "var writeopts = {type:'binary', bookType:'%@'}", bookType));
let writeopts: JSValue! = context.objectForKeyedSubscript("writeopts");
let writefunc: JSValue! = XLSX.objectForKeyedSubscript("write");
let writeopts: JSValue = context.objectForKeyedSubscript("writeopts");
let writefunc: JSValue = XLSX.objectForKeyedSubscript("write");
return writefunc.call(withArguments: [wb, writeopts]).toString();
}
@ -58,7 +58,7 @@ class SheetJSCore {
var context: JSContext!
do {
context = JSContext();
context.exceptionHandler = { ctx, X in if let e = X { print(e.toString()); }; };
context.exceptionHandler = { _, X in if let e = X { print(e.toString()!); }; };
context.evaluateScript("var global = (function(){ return this; }).call(null);");
context.evaluateScript("if(typeof wbs == 'undefined') wbs = [];");
let src = try String(contentsOfFile: "xlsx.full.min.js");
@ -79,7 +79,7 @@ class SheetJSCore {
}
func readBStr(data: String) throws -> SJSWorkbook {
context.setObject(data, forKeyedSubscript: "payload" as (NSCopying & NSObjectProtocol)!);
context.setObject(data, forKeyedSubscript: "payload" as (NSCopying & NSObjectProtocol));
context.evaluateScript("var wb = XLSX.read(payload, {type:'binary'});");
let wb: JSValue! = context.objectForKeyedSubscript("wb");
if wb == nil { throw SJSError.badJSWorkbook; }

@ -1,5 +1,5 @@
#!/bin/bash
DUKTAPE_VER=2.2.0
DUKTAPE_VER=2.6.0
if [ ! -e duktape-$DUKTAPE_VER ]; then
if [ ! -e duktape-$DUKTAPE_VER.tar ]; then
if [ ! -e duktape-$DUKTAPE_VER.tar.xz ]; then

@ -39,7 +39,7 @@ func main() {
/* load library */
safe_run_file(vm, "shim.min.js")
safe_run_file(vm, "xlsx.core.min.js")
safe_run_file(vm, "xlsx.full.min.js")
/* get version string */
v := eval_string(vm, "XLSX.version")

@ -10,6 +10,8 @@ into web pages with script tags:
Strictly speaking, there should be no need for an Angular demo! You can proceed
as you would with any other browser-friendly library.
This demo uses AngularJS 1.5.0.
## Array of Objects
@ -87,13 +89,13 @@ function SheetJSImportDirective() {
reader.onload = function (e) {
/* read workbook */
var bstr = e.target.result;
var workbook = XLSX.read(bstr, {type:'binary'});
var ab = e.target.result;
var workbook = XLSX.read(ab);
/* DO SOMETHING WITH workbook HERE */
};
reader.readAsBinaryString(changeEvent.target.files[0]);
reader.readAsArrayBuffer(changeEvent.target.files[0]);
});
}
};
@ -133,7 +135,7 @@ directive for a HTML File Input control. It also includes a sample service for
export which adds an item to the export menu.
The demo `SheetJSImportDirective` follows the prescription from the README for
File input controls using `readAsBinaryString`, converting to a suitable
File input controls using `readAsArrayBuffer`, converting to a suitable
representation and updating the scope.
`SheetJSExportService` exposes export functions for `XLSB` and `XLSX`. Other

@ -60,8 +60,8 @@ function SheetJSImportDirective() {
reader.onload = function(e) {
/* read workbook */
var bstr = e.target.result;
var wb = XLSX.read(bstr, {type:'binary'});
var ab = e.target.result;
var wb = XLSX.read(ab);
/* grab first sheet */
var wsname = wb.SheetNames[0];
@ -78,7 +78,7 @@ function SheetJSImportDirective() {
data[r-1] = {};
for(i = 0; i < aoa[r].length; ++i) {
if(aoa[r][i] == null) continue;
data[r-1][aoa[0][i]] = aoa[r][i]
data[r-1][aoa[0][i]] = aoa[r][i];
}
}
@ -89,7 +89,7 @@ function SheetJSImportDirective() {
});
};
reader.readAsBinaryString(changeEvent.target.files[0]);
reader.readAsArrayBuffer(changeEvent.target.files[0]);
});
}
};

@ -10,8 +10,8 @@
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-animate.js"></script>
<!-- ui-grid -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.0.0/ui-grid.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.0.0/ui-grid.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.11.0/ui-grid.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.11.0/ui-grid.css"/>
<!-- SheetJS js-xlsx library -->
<script src="shim.js"></script>

@ -5,7 +5,7 @@
<head>
<title>SheetJS + AngularJS</title>
<!-- Angular -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<!-- SheetJS js-xlsx library -->
<script src="shim.js"></script>

6
demos/angular2/.eslintrc Normal file

@ -0,0 +1,6 @@
{
"parser": "@typescript-eslint/parser",
"extends": [
"eslint:recommended"
]
}

1
demos/angular2/.gitattributes vendored Normal file

@ -0,0 +1 @@
*.*-ng* linguist-generated=true binary

@ -2,3 +2,6 @@ dist
hooks
SheetJSIonic
SheetJSNS
angular.json
tsconfig.app.json
src/polyfills.ts

@ -1,22 +1,30 @@
.PHONY: all
all: angular5
.PHONY: angular2 angular4 angular5
angular2 angular4 angular5:
cp package.json-$@ package.json
.PHONY: ng2 ng4 ng5 ng6 ng7 ng8 ng9 ng10 ng11 ng12
ng2 ng4 ng5 ng6 ng7 ng8 ng9 ng10 ng11 ng12:
rm -f angular.json tsconfig.app.json src/polyfills.ts
cp versions/package.json-$@ package.json
if [ -e versions/angular.json-$@ ]; then cp versions/angular.json-$@ angular.json; fi
if [ -e versions/tsconfig.app.json-$@ ]; then cp versions/tsconfig.app.json-$@ tsconfig.app.json; fi
if [ -e versions/polyfills.ts-$@ ]; then cp versions/polyfills.ts-$@ src/polyfills.ts; fi
rm -rf node_modules
npm install
if [ ! -e node_modules ]; then mkdir node_modules; fi
if [ ! -e node_modules/xlsx ]; then cd node_modules; ln -s ../../../ xlsx; cd -; fi
npm run build
.PHONY: all
all:
for i in 2 4 5 6 7 8 9 10 11 12; do make ng$$i; done
.PHONY: ionic
ionic:
bash ./ionic.sh
.PHONY: ios android browser
ios android browser: ionic
ios browser: ionic
cd SheetJSIonic; ionic cordova emulate $@ </dev/null; cd -
android: ionic
cd SheetJSIonic; ionic cordova prepare $@ </dev/null; ionic cordova emulate $@ </dev/null; cd -
.PHONY: nativescript
nativescript:
@ -24,7 +32,7 @@ nativescript:
.PHONY: ns-ios ns-android
ns-ios: nativescript
cd SheetJSNS; tns run ios; cd -
cd SheetJSNS; ns run ios; cd -
ns-android: nativescript
cd SheetJSNS; tns run android; cd -
cd SheetJSNS; ns run android; cd -

@ -54,8 +54,8 @@ XLSX.writeFile(wb, 'SheetJS.xlsx');
const reader: FileReader = new FileReader();
reader.onload = (e: any) => {
/* read workbook */
const bstr: string = e.target.result;
const wb: XLSX.WorkBook = XLSX.read(bstr, {type: 'binary'});
const ab: ArrayBuffer = e.target.result;
const wb: XLSX.WorkBook = XLSX.read(ab);
/* grab first sheet */
const wsname: string = wb.SheetNames[0];
@ -64,7 +64,7 @@ XLSX.writeFile(wb, 'SheetJS.xlsx');
/* save data */
this.data = <AOA>(XLSX.utils.sheet_to_json(ws, {header: 1}));
};
reader.readAsBinaryString(target.files[0]);
reader.readAsArrayBuffer(target.files[0]);
}
```
@ -73,17 +73,20 @@ XLSX.writeFile(wb, 'SheetJS.xlsx');
Modules that work with Angular 2 largely work as-is with Angular 4+. Switching
between versions is mostly a matter of installing the correct version of the
core and associated modules. This demo includes `package.json-angular#` files
for Angular 2, Angular 4, and Angular 5
for every major version of Angular up to 12.
To test a particular Angular version, overwrite `package.json`:
```bash
# switch to Angular 2
$ cp package.json-angular2 package.json
$ cp package.json-ng2 package.json
$ npm install
$ ng serve
```
Note: when running the demos, Angular 2 requires Node <= 14. This is due to a
tooling issue with `ng` and does not affect browser use.
## XLSX Symbolic Link
In this tree, `node_modules/xlsx` is a link pointing back to the root. This
@ -137,14 +140,14 @@ script performs the necessary installation steps.
```
`@ionic-native/file` reads and writes files on devices. `readAsBinaryString`
returns strings that can be parsed with the `binary` type, and `array` type can
`@ionic-native/file` reads and writes files on devices. `readAsArrayBuffer`
returns `ArrayBuffer` objects suitable for `array` type, and `array` type can
be converted to blobs that can be exported with `writeFile`:
```typescript
/* read a workbook */
const bstr: string = await this.file.readAsBinaryString(url, filename);
const wb: XLSX.WorkBook = XLSX.read(bstr, {type: 'binary'});
const ab: ArrayBuffer = await this.file.readAsArrayBuffer(url, filename);
const wb: XLSX.WorkBook = XLSX.read(bstr, {type: 'array'});
/* write a workbook */
const wbout: ArrayBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
@ -164,16 +167,19 @@ const XLSX = require("./xlsx.full.min.js");
```
The `ISO_8859_1` encoding from the text module specifies `"binary"` strings.
`fs.File#readText` and `fs.File#writeText` reads and writes files:
`File#readText` and `File#writeText` reads and writes files:
```typescript
/* read a workbook */
const bstr: string = await file.readText(textModule.encoding.ISO_8859_1);
const bstr: string = await file.readText(encoding.ISO_8859_1);
const wb = XLSX.read(bstr, { type: "binary" });
/* write a workbook */
const wbout: string = XLSX.write(wb, { bookType: 'xlsx', type: 'binary' });
await file.writeText(wbout, textModule.encoding.ISO_8859_1);
await file.writeText(wbout, encoding.ISO_8859_1);
```
Note: some versions of NativeScript do not properly support typed arrays or
binary strings. See <https://github.com/NativeScript/NativeScript/issues/9586>
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)

@ -0,0 +1,22 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/* NOTE: this file exists because `File` must be added as a provider */
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { File } from '@ionic-native/file/ngx';
@NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
providers: [File, { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
bootstrap: [AppComponent],
})
export class AppModule {}

@ -1,17 +1,16 @@
#!/bin/bash
if [ ! -e SheetJSIonic ]; then
ionic start SheetJSIonic blank --cordova --no-git --no-link </dev/null
ionic start SheetJSIonic blank --type angular --cordova --quiet --no-git --no-link --confirm </dev/null
cd SheetJSIonic
ionic cordova platform add browser </dev/null
ionic cordova platform add ios </dev/null
ionic cordova platform add browser --confirm </dev/null
ionic cordova platform add ios --confirm </dev/null
ionic cordova platform add android --confirm </dev/null
ionic cordova plugin add cordova-plugin-file </dev/null
npm install --save @ionic-native/core
npm install --save @ionic-native/file
npm install --save xlsx
cp src/app/app.module.ts{,.bak}
cat src/app/app.module.ts.bak | awk 'BEGIN{p=0} !/import/ && !p { ++p; print "import { File } from '"'"'@ionic-native/file'"'"';"; } 1; /providers: \[/ {print " File,"}' > src/app/app.module.ts
cp ../ionic-app.module.ts src/app/app.module.ts
cd -
fi
cp ionic.ts SheetJSIonic/src/pages/home/home.ts
rm -f SheetJSIonic/src/pages/home/home.html
cp ionic.ts SheetJSIonic/src/app/home/home.page.ts

@ -1,19 +1,30 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
import { Component } from '@angular/core';
import { File } from '@ionic-native/file/ngx';
import * as XLSX from 'xlsx';
import { File } from '@ionic-native/file';
type AOA = any[][];
@Component({
selector: 'page-home',
selector: 'app-home',
//templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
template: `
<ion-header><ion-navbar><ion-title>SheetJS Ionic Demo</ion-title></ion-navbar></ion-header>
<ion-header>
<ion-toolbar>
<ion-title>SheetJS Ionic Demo</ion-title>
</ion-toolbar>
</ion-header>
<ion-content [fullscreen]="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title>SheetJS Demo</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<ion-grid>
<ion-row *ngFor="let row of data">
<ion-col *ngFor="let val of row">
@ -23,7 +34,7 @@ type AOA = any[][];
</ion-grid>
</ion-content>
<ion-footer>
<ion-footer padding>
<input type="file" (change)="onFileChange($event)" multiple="false" />
<button ion-button color="secondary" (click)="import()">Import Data</button>
<button ion-button color="secondary" (click)="export()">Export Data</button>
@ -33,18 +44,18 @@ type AOA = any[][];
export class HomePage {
data: any[][] = [[1,2,3],[4,5,6]];
constructor(public file: File) {};
constructor(public file: File) {}
read(bstr: string) {
read(ab: ArrayBuffer) {
/* read workbook */
const wb: XLSX.WorkBook = XLSX.read(bstr, {type: 'binary'});
const wb: XLSX.WorkBook = XLSX.read(new Uint8Array(ab), {type: 'array'});
/* grab first sheet */
const wsname: string = wb.SheetNames[0];
const ws: XLSX.WorkSheet = wb.Sheets[wsname];
/* save data */
this.data = <AOA>(XLSX.utils.sheet_to_json(ws, {header: 1}));
this.data = (XLSX.utils.sheet_to_json(ws, {header: 1}) as AOA);
};
write(): XLSX.WorkBook {
@ -61,14 +72,14 @@ export class HomePage {
/* File Input element for browser */
onFileChange(evt: any) {
/* wire up file reader */
const target: DataTransfer = <DataTransfer>(evt.target);
if (target.files.length !== 1) throw new Error('Cannot use multiple files');
const target: DataTransfer = (evt.target as DataTransfer);
if (target.files.length !== 1) { throw new Error('Cannot use multiple files'); }
const reader: FileReader = new FileReader();
reader.onload = (e: any) => {
const bstr: string = e.target.result;
this.read(bstr);
const ab: ArrayBuffer = e.target.result;
this.read(ab);
};
reader.readAsBinaryString(target.files[0]);
reader.readAsArrayBuffer(target.files[0]);
};
/* Import button for mobile */
@ -77,23 +88,22 @@ export class HomePage {
const target: string = this.file.documentsDirectory || this.file.externalDataDirectory || this.file.dataDirectory || '';
const dentry = await this.file.resolveDirectoryUrl(target);
const url: string = dentry.nativeURL || '';
alert(`Attempting to read SheetJSIonic.xlsx from ${url}`)
const bstr: string = await this.file.readAsBinaryString(url, "SheetJSIonic.xlsx");
this.read(bstr);
alert(`Attempting to read SheetJSIonic.xlsx from ${url}`);
const ab: ArrayBuffer = await this.file.readAsArrayBuffer(url, 'SheetJSIonic.xlsx');
this.read(ab);
} catch(e) {
const m: string = e.message;
alert(m.match(/It was determined/) ? "Use File Input control" : `Error: ${m}`);
alert(m.match(/It was determined/) ? 'Use File Input control' : `Error: ${m}`);
}
};
/* Export button */
async export() {
const wb: XLSX.WorkBook = this.write();
const filename: string = "SheetJSIonic.xlsx";
const filename = 'SheetJSIonic.xlsx';
try {
/* generate Blob */
const wbout: ArrayBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
const blob: Blob = new Blob([wbout], {type: 'application/octet-stream'});
/* find appropriate path for mobile */
const target: string = this.file.documentsDirectory || this.file.externalDataDirectory || this.file.dataDirectory || '';
@ -101,14 +111,15 @@ export class HomePage {
const url: string = dentry.nativeURL || '';
/* attempt to save blob to file */
await this.file.writeFile(url, filename, blob, {replace: true});
await this.file.writeFile(url, filename, wbout, {replace: true});
alert(`Wrote to SheetJSIonic.xlsx in ${url}`);
} catch(e) {
if(e.message.match(/It was determined/)) {
/* in the browser, use writeFile */
XLSX.writeFile(wb, filename);
} else {
alert(`Error: ${e.message}`);
}
else alert(`Error: ${e.message}`);
}
};
}

@ -1,15 +1,10 @@
#!/bin/bash
if [ ! -e SheetJSNS ]; then
tns create SheetJSNS --template nativescript-template-ng-tutorial
ns create SheetJSNS --ng
cd SheetJSNS
tns plugin add nativescript-nodeify
npm install xlsx
cd app
npm install xlsx
cd ../..
cd ..
fi
cp ../../dist/xlsx.full.min.js SheetJSNS/
cp ../../dist/xlsx.full.min.js SheetJSNS/app/
cp nsmain.ts SheetJSNS/app/main.ts
cp nscript.ts SheetJSNS/app/app.component.ts
<../../dist/xlsx.full.min.js sed 's/require("fs")/null/g' > SheetJSNS/src/app/xlsx.full.min.js
cp nscript.ts SheetJSNS/src/app/app.component.ts

@ -1,21 +1,19 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
import { Component } from "@angular/core";
import * as dockModule from "tns-core-modules/ui/layouts/dock-layout";
import * as buttonModule from "tns-core-modules/ui/button";
import * as textModule from "tns-core-modules/text";
import * as dialogs from "ui/dialogs";
import * as fs from "tns-core-modules/file-system";
import { Component } from '@angular/core';
import { encoding } from '@nativescript/core/text';
import { File, Folder, knownFolders, path } from '@nativescript/core/file-system';
import { Dialogs } from '@nativescript/core';
import { Page, GridLayout, WebView, DockLayout, Button } from '@nativescript/core';
/* NativeScript does not support import syntax for npm modules */
const XLSX = require("./xlsx.full.min.js");
import * as XLSX from './xlsx.full.min';
@Component({
selector: "my-app",
selector: 'ns-app',
template: `
<Page>
<GridLayout rows="auto, *, auto">
<ActionBar row="0" title="SheetJS NativeScript Demo" class="action-bar"></ActionBar>
<!-- data converted to HTML and rendered in web view -->
<WebView row="1" src="{{html}}"></WebView>
@ -25,6 +23,7 @@ const XLSX = require("./xlsx.full.min.js");
<Button text="Export File" (tap)="export()" style="padding: 10px"></Button>
</DockLayout>
</GridLayout>
</Page>
`
})
@ -37,16 +36,16 @@ export class AppComponent {
/* Import button */
async import() {
const filename: string = "SheetJSNS.xlsx";
const filename: string = "SheetJSNS.csv";
/* find appropriate path */
const target: fs.Folder = fs.knownFolders.documents() || fs.knownFolders.ios.sharedPublic();
const url: string = fs.path.normalize(target.path + "///" + filename);
const file: fs.File = fs.File.fromPath(url);
const target: Folder = knownFolders.documents() || knownFolders.ios.sharedPublic();
const url: string = path.normalize(target.path + "///" + filename);
const file: File = File.fromPath(url);
try {
/* get binary string */
const bstr: string = await file.readText(textModule.encoding.ISO_8859_1);
const bstr: string = await file.readText(encoding.ISO_8859_1);
/* read workbook */
const wb = XLSX.read(bstr, { type: "binary" });
@ -57,27 +56,27 @@ export class AppComponent {
/* update table */
this.html = XLSX.utils.sheet_to_html(ws);
dialogs.alert(`Attempting to read to SheetJSNS.xlsx in ${url}`);
Dialogs.alert(`Attempting to read to ${filename} in ${url}`);
} catch(e) {
dialogs.alert(e.message);
Dialogs.alert(e.message);
}
};
/* Export button */
async export() {
const wb = XLSX.read(this.html, { type: "string" });
const filename: string = "SheetJSNS.xlsx";
const filename: string = "SheetJSNS.csv";
/* generate binary string */
const wbout: string = XLSX.write(wb, { bookType: 'xlsx', type: 'binary' });
const wbout: string = XLSX.write(wb, { bookType: 'csv', type: 'binary' });
/* find appropriate path */
const target: fs.Folder = fs.knownFolders.documents() || fs.knownFolders.ios.sharedPublic();
const url: string = fs.path.normalize(target.path + "///" + filename);
const file: fs.File = fs.File.fromPath(url);
const target: Folder = knownFolders.documents() || knownFolders.ios.sharedPublic();
const url: string = path.normalize(target.path + "///" + filename);
const file: File = File.fromPath(url);
/* attempt to save binary string to file */
await file.writeText(wbout, textModule.encoding.ISO_8859_1);
dialogs.alert(`Wrote to SheetJSNS.xlsx in ${url}`);
await file.writeText(wbout, encoding.ISO_8859_1);
Dialogs.alert(`Wrote to ${filename} in ${url}`);
};
}

@ -1,65 +0,0 @@
import { platformNativeScriptDynamic } from "nativescript-angular/platform";
import { AppModule } from "./app.module";
/*! ****************************************************************************
Code based on @Microsoft/tslib
Original license header:
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN AS IS BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
// tslint:disable
if (!('__awaiter' in global)) {
global['__awaiter'] = function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
}
if (!('__generator' in global)) {
global['__generator'] = function (thisArg, body) {
var _: any = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [0, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
}
platformNativeScriptDynamic().bootstrapModule(AppModule);

@ -9,6 +9,7 @@ type AOA = any[][];
@Component({
selector: 'sheetjs',
template: `
<pre><b>Version: {{ver}}</b></pre>
<input type="file" (change)="onFileChange($event)" multiple="false" />
<table class="sjs-table">
<tr *ngFor="let row of data">
@ -25,6 +26,7 @@ export class SheetJSComponent {
data: AOA = [ [1, 2], [3, 4] ];
wopts: XLSX.WritingOptions = { bookType: 'xlsx', type: 'array' };
fileName: string = 'SheetJS.xlsx';
ver: string = XLSX.version;
onFileChange(evt: any) {
/* wire up file reader */
@ -33,8 +35,8 @@ export class SheetJSComponent {
const reader: FileReader = new FileReader();
reader.onload = (e: any) => {
/* read workbook */
const bstr: string = e.target.result;
const wb: XLSX.WorkBook = XLSX.read(bstr, {type: 'binary'});
const ab: ArrayBuffer = e.target.result;
const wb: XLSX.WorkBook = XLSX.read(ab);
/* grab first sheet */
const wsname: string = wb.SheetNames[0];
@ -43,7 +45,7 @@ export class SheetJSComponent {
/* save data */
this.data = <AOA>(XLSX.utils.sheet_to_json(ws, {header: 1}));
};
reader.readAsBinaryString(target.files[0]);
reader.readAsArrayBuffer(target.files[0]);
}
export(): void {

@ -0,0 +1,3 @@
export const environment = {
production: true
};

@ -0,0 +1,3 @@
export const environment = {
production: false
};

@ -0,0 +1 @@
/* You can add global styles to this file, and also import other style files */

125
demos/angular2/versions/angular.json-ng10 generated Normal file

@ -0,0 +1,125 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"sheetjs": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/sheetjs",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "sheetjs:build"
},
"configurations": {
"production": {
"browserTarget": "sheetjs:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "sheetjs:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "sheetjs:serve"
},
"configurations": {
"production": {
"devServerTarget": "sheetjs:serve:production"
}
}
}
}
}
},
"defaultProject": "sheetjs"
}

124
demos/angular2/versions/angular.json-ng11 generated Normal file

@ -0,0 +1,124 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"sheetjs": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/sheetjs",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "sheetjs:build"
},
"configurations": {
"production": {
"browserTarget": "sheetjs:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "sheetjs:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "sheetjs:serve"
},
"configurations": {
"production": {
"devServerTarget": "sheetjs:serve:production"
}
}
}
}
}
},
"defaultProject": "sheetjs"
}

106
demos/angular2/versions/angular.json-ng12 generated Normal file

@ -0,0 +1,106 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"sheetjs": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {
"@schematics/angular:application": {
"strict": true
}
},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/sheetjs",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "2mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "sheetjs:build:production"
},
"development": {
"browserTarget": "sheetjs:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "sheetjs:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
}
}
}
},
"defaultProject": "sheetjs"
}

127
demos/angular2/versions/angular.json-ng6 generated Normal file

@ -0,0 +1,127 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"sheetjs": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/sheetjs",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "sheetjs:build"
},
"configurations": {