1
forked from sheetjs/sheetjs

version bump 0.12.5: ancillary utility update

- add BOM to `stream.to_csv` (fixes  h/t @hr5959)
- `utils.format_cell` type (h/t @victorj2307)
- duktape niggles
- demo cleanup
This commit is contained in:
SheetJS 2018-03-12 22:51:54 -04:00
parent 73a5e50f21
commit 5de62a947f
66 changed files with 681 additions and 355 deletions

@ -106,6 +106,7 @@ ui-grid
angular-cli angular-cli
- demos/database/README.md - demos/database/README.md
Knex
LowDB LowDB
MariaDB MariaDB
MySQL MySQL

@ -162,6 +162,7 @@ In the browser, just add a script tag:
| `unpkg` | <https://unpkg.com/xlsx/> | | `unpkg` | <https://unpkg.com/xlsx/> |
| `jsDelivr` | <https://jsdelivr.com/package/npm/xlsx> | | `jsDelivr` | <https://jsdelivr.com/package/npm/xlsx> |
| `CDNjs` | <http://cdnjs.com/libraries/xlsx> | | `CDNjs` | <http://cdnjs.com/libraries/xlsx> |
| `packd` | <https://bundle.run/xlsx?name=XLSX> |
`unpkg` makes the latest version available at: `unpkg` makes the latest version available at:

@ -1 +1 @@
XLSX.version = '0.12.4'; XLSX.version = '0.12.5';

@ -14,7 +14,8 @@ function write_comments_vml(rId, comments) {
]; ];
while(_shapeid < rId * 1000) _shapeid += 1000; while(_shapeid < rId * 1000) _shapeid += 1000;
comments.map(function(x) { return decode_cell(x[0]); }).forEach(function(c) { o = o.concat([ comments.forEach(function(x) { var c = decode_cell(x[0]);
o = o.concat([
'<v:shape' + wxt_helper({ '<v:shape' + wxt_helper({
id:'_x0000_s' + (++_shapeid), id:'_x0000_s' + (++_shapeid),
type:"#_x0000_t202", type:"#_x0000_t202",

@ -36,13 +36,11 @@ function write_comments_xml(data/*::, opts*/) {
var iauthor/*:Array<string>*/ = []; var iauthor/*:Array<string>*/ = [];
o.push("<authors>"); o.push("<authors>");
data.map(function(x) { return x[1]; }).forEach(function(comment) { data.forEach(function(x) { x[1].forEach(function(w) { var a = escapexml(w.a);
comment.map(function(x) { return escapexml(x.a); }).forEach(function(a) { if(iauthor.indexOf(a) > -1) return;
if(iauthor.indexOf(a) > -1) return; iauthor.push(a);
iauthor.push(a); o.push("<author>" + a + "</author>");
o.push("<author>" + a + "</author>"); }); });
});
});
o.push("</authors>"); o.push("</authors>");
o.push("<commentList>"); o.push("<commentList>");
data.forEach(function(d) { data.forEach(function(d) {

@ -163,6 +163,8 @@ function xlml_clean_comment(comment/*:any*/) {
function xlml_normalize(d)/*:string*/ { function xlml_normalize(d)/*:string*/ {
if(has_buf &&/*::typeof Buffer !== "undefined" && d != null && d instanceof Buffer &&*/ Buffer.isBuffer(d)) return d.toString('utf8'); if(has_buf &&/*::typeof Buffer !== "undefined" && d != null && d instanceof Buffer &&*/ Buffer.isBuffer(d)) return d.toString('utf8');
if(typeof d === 'string') return d; if(typeof d === 'string') return d;
/* duktape */
if(typeof Uint8Array !== 'undefined' && d instanceof Uint8Array) return utf8read(a2s(ab2a(d)));
throw new Error("Bad input format: expected Buffer or string"); throw new Error("Bad input format: expected Buffer or string");
} }

@ -15,7 +15,9 @@ if(has_buf && typeof require != 'undefined') (function() {
var rowinfo/*:Array<RowInfo>*/ = o.skipHidden && sheet["!rows"] || []; var rowinfo/*:Array<RowInfo>*/ = o.skipHidden && sheet["!rows"] || [];
for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C); for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C);
var R = r.s.r; var R = r.s.r;
var BOM = false;
stream._read = function() { stream._read = function() {
if(!BOM) { BOM = true; return stream.push("\uFEFF"); }
if(R > r.e.r) return stream.push(null); if(R > r.e.r) return stream.push(null);
while(R <= r.e.r) { while(R <= r.e.r) {
++R; ++R;

@ -4,5 +4,6 @@ duk*
*.class *.class
*.jar *.jar
rhino rhino
shim.min.js
xlsx.*.js xlsx.*.js
payload.js payload.js

@ -5,12 +5,16 @@ all: duktape nashorn rhinojs swift
base: base:
if [ ! -e sheetjs.xlsx ]; then node ../../tests/write.js; fi if [ ! -e sheetjs.xlsx ]; then node ../../tests/write.js; fi
if [ ! -e xlsx.full.min.js ]; then cp ../../dist/xlsx.full.min.js .; fi if [ ! -e xlsx.full.min.js ]; then cp ../../dist/xlsx.full.min.js .; fi
if [ ! -e shim.min.js ]; then cp ../../dist/shim.min.js .; fi
.PHONY: duktape .PHONY: duk
duktape: base ## duktape demo duk: base
bash ./duktape.sh bash ./duktape.sh
gcc -std=c99 -Wall -osheetjs.duk sheetjs.duk.c duktape.c -lm gcc -std=c99 -Wall -osheetjs.duk sheetjs.duk.c duktape.c -lm
./sheetjs.duk
.PHONY: duktape
duktape: duk ## duktape demo
for ext in xlsx xlsb biff8.xls xml.xls; do ./sheetjs.duk sheetjs.$$ext; done
.PHONY: nashorn .PHONY: nashorn
nashorn: base ## nashorn demo nashorn: base ## nashorn demo

@ -2,95 +2,95 @@
import JavaScriptCore; import JavaScriptCore;
enum SJSError: Error { enum SJSError: Error {
case badJSContext; case badJSContext;
case badJSWorkbook; case badJSWorkbook;
case badJSWorksheet; case badJSWorksheet;
}; };
class SJSWorksheet { class SJSWorksheet {
var context: JSContext!; var context: JSContext!;
var wb: JSValue!; var ws: JSValue!; var wb: JSValue!; var ws: JSValue!;
var idx: Int32; var idx: Int32;
func toCSV() throws -> String { func toCSV() throws -> String {
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX"); let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
let utils: JSValue! = XLSX.objectForKeyedSubscript("utils"); let utils: JSValue! = XLSX.objectForKeyedSubscript("utils");
let sheet_to_csv: JSValue! = utils.objectForKeyedSubscript("sheet_to_csv"); let sheet_to_csv: JSValue! = utils.objectForKeyedSubscript("sheet_to_csv");
return sheet_to_csv.call(withArguments: [ws]).toString(); return sheet_to_csv.call(withArguments: [ws]).toString();
} }
init(ctx: JSContext, workbook: JSValue, worksheet: JSValue, idx: Int32) throws { init(ctx: JSContext, workbook: JSValue, worksheet: JSValue, idx: Int32) throws {
self.context = ctx; self.wb = workbook; self.ws = worksheet; self.idx = idx; self.context = ctx; self.wb = workbook; self.ws = worksheet; self.idx = idx;
} }
} }
class SJSWorkbook { class SJSWorkbook {
var context: JSContext!; 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 { func getSheetAtIndex(idx: Int32) throws -> SJSWorksheet {
let SheetName: String = SheetNames.atIndex(Int(idx)).toString(); let SheetName: String = SheetNames.atIndex(Int(idx)).toString();
let ws: JSValue! = Sheets.objectForKeyedSubscript(SheetName); let ws: JSValue! = Sheets.objectForKeyedSubscript(SheetName);
return try SJSWorksheet(ctx: context, workbook: wb, worksheet: ws, idx: idx); return try SJSWorksheet(ctx: context, workbook: wb, worksheet: ws, idx: idx);
} }
func writeBStr(bookType: String = "xlsx") throws -> String { func writeBStr(bookType: String = "xlsx") throws -> String {
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX"); let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
context.evaluateScript(String(format: "var writeopts = {type:'binary', bookType:'%@'}", bookType)); context.evaluateScript(String(format: "var writeopts = {type:'binary', bookType:'%@'}", bookType));
let writeopts: JSValue! = context.objectForKeyedSubscript("writeopts"); let writeopts: JSValue! = context.objectForKeyedSubscript("writeopts");
let writefunc: JSValue! = XLSX.objectForKeyedSubscript("write"); let writefunc: JSValue! = XLSX.objectForKeyedSubscript("write");
return writefunc.call(withArguments: [wb, writeopts]).toString(); return writefunc.call(withArguments: [wb, writeopts]).toString();
} }
init(ctx: JSContext, wb: JSValue) throws { init(ctx: JSContext, wb: JSValue) throws {
self.context = ctx; self.context = ctx;
self.wb = wb; self.wb = wb;
self.SheetNames = wb.objectForKeyedSubscript("SheetNames"); self.SheetNames = wb.objectForKeyedSubscript("SheetNames");
self.Sheets = wb.objectForKeyedSubscript("Sheets"); self.Sheets = wb.objectForKeyedSubscript("Sheets");
} }
} }
class SheetJSCore { class SheetJSCore {
var context: JSContext!; var context: JSContext!;
var XLSX: JSValue!; var XLSX: JSValue!;
func init_context() throws -> JSContext { func init_context() throws -> JSContext {
var context: JSContext! var context: JSContext!
do { do {
context = JSContext(); context = JSContext();
context.exceptionHandler = { ctx, X in if let e = X { print(e.toString()); }; }; context.exceptionHandler = { ctx, X in if let e = X { print(e.toString()); }; };
context.evaluateScript("var global = (function(){ return this; }).call(null);"); context.evaluateScript("var global = (function(){ return this; }).call(null);");
context.evaluateScript("if(typeof wbs == 'undefined') wbs = [];"); context.evaluateScript("if(typeof wbs == 'undefined') wbs = [];");
let src = try String(contentsOfFile: "xlsx.full.min.js"); let src = try String(contentsOfFile: "xlsx.full.min.js");
context.evaluateScript(src); context.evaluateScript(src);
if context != nil { return context!; } if context != nil { return context!; }
} catch { print(error.localizedDescription); } } catch { print(error.localizedDescription); }
throw SJSError.badJSContext; throw SJSError.badJSContext;
} }
func version() throws -> String { func version() throws -> String {
if let version = XLSX.objectForKeyedSubscript("version") { return version.toString(); } if let version = XLSX.objectForKeyedSubscript("version") { return version.toString(); }
throw SJSError.badJSContext; throw SJSError.badJSContext;
} }
func readFile(file: String) throws -> SJSWorkbook { func readFile(file: String) throws -> SJSWorkbook {
let data: String! = try String(contentsOfFile: file, encoding: String.Encoding.isoLatin1); let data: String! = try String(contentsOfFile: file, encoding: String.Encoding.isoLatin1);
return try readBStr(data: data); return try readBStr(data: data);
} }
func readBStr(data: String) throws -> SJSWorkbook { 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'});"); context.evaluateScript("var wb = XLSX.read(payload, {type:'binary'});");
let wb: JSValue! = context.objectForKeyedSubscript("wb"); let wb: JSValue! = context.objectForKeyedSubscript("wb");
if wb == nil { throw SJSError.badJSWorkbook; } if wb == nil { throw SJSError.badJSWorkbook; }
return try SJSWorkbook(ctx: context, wb: wb); return try SJSWorkbook(ctx: context, wb: wb);
} }
init() throws { init() throws {
do { do {
self.context = try init_context(); self.context = try init_context();
self.XLSX = self.context.objectForKeyedSubscript("XLSX"); self.XLSX = self.context.objectForKeyedSubscript("XLSX");
if self.XLSX == nil { throw SJSError.badJSContext; } if self.XLSX == nil { throw SJSError.badJSContext; }
} catch { print(error.localizedDescription); } } catch { print(error.localizedDescription); }
} }
} }

@ -5,27 +5,27 @@ import com.sheetjs.SheetJSFile;
import com.sheetjs.SheetJSSheet; import com.sheetjs.SheetJSSheet;
public class SheetJSRhino { public class SheetJSRhino {
public static void main(String args[]) throws Exception { public static void main(String args[]) throws Exception {
try { try {
SheetJS sjs = new SheetJS(); SheetJS sjs = new SheetJS();
/* open file */ /* open file */
SheetJSFile xl = sjs.read_file(args[0]); SheetJSFile xl = sjs.read_file(args[0]);
/* get sheetnames */ /* get sheetnames */
String[] sheetnames = xl.get_sheet_names(); String[] sheetnames = xl.get_sheet_names();
System.err.println(sheetnames[0]); System.err.println(sheetnames[0]);
/* convert to CSV */ /* convert to CSV */
SheetJSSheet sheet = xl.get_sheet(0); SheetJSSheet sheet = xl.get_sheet(0);
String csv = sheet.get_csv(); String csv = sheet.get_csv();
System.out.println(csv); System.out.println(csv);
} catch(Exception e) { } catch(Exception e) {
throw e; throw e;
} finally { } finally {
SheetJS.close(); SheetJS.close();
} }
} }
} }

@ -13,39 +13,39 @@ import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Scriptable;
public class JSHelper { public class JSHelper {
static String read_file(String file) throws IOException { static String read_file(String file) throws IOException {
byte[] b = Files.readAllBytes(Paths.get(file)); byte[] b = Files.readAllBytes(Paths.get(file));
System.out.println(b.length); System.out.println(b.length);
StringBuilder sb = new StringBuilder(); 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]))); for(int i = 0; i < b.length; ++i) sb.append(Character.toString((char)(b[i] < 0 ? b[i] + 256 : b[i])));
return sb.toString(); return sb.toString();
} }
static Object get_object(String path, Object base) throws ObjectNotFoundException { static Object get_object(String path, Object base) throws ObjectNotFoundException {
int idx = path.indexOf("."); int idx = path.indexOf(".");
Scriptable b = (Scriptable)base; Scriptable b = (Scriptable)base;
if(idx == -1) return b.get(path, b); if(idx == -1) return b.get(path, b);
Object o = b.get(path.substring(0,idx), b); Object o = b.get(path.substring(0,idx), b);
if(o == Scriptable.NOT_FOUND) throw new ObjectNotFoundException("not found: |" + path.substring(0,idx) + "|" + Integer.toString(idx)); if(o == Scriptable.NOT_FOUND) throw new ObjectNotFoundException("not found: |" + path.substring(0,idx) + "|" + Integer.toString(idx));
return get_object(path.substring(idx+1), (NativeObject)o); return get_object(path.substring(idx+1), (NativeObject)o);
} }
static Object[] get_array(String path, Object base) throws ObjectNotFoundException { static Object[] get_array(String path, Object base) throws ObjectNotFoundException {
NativeArray arr = (NativeArray)get_object(path, base); NativeArray arr = (NativeArray)get_object(path, base);
Object[] out = new Object[(int)arr.getLength()]; Object[] out = new Object[(int)arr.getLength()];
int idx; int idx;
for(Object o : arr.getIds()) out[idx = (Integer)o] = arr.get(idx, arr); for(Object o : arr.getIds()) out[idx = (Integer)o] = arr.get(idx, arr);
return out; return out;
} }
static String[] get_string_array(String path, Object base) throws ObjectNotFoundException { static String[] get_string_array(String path, Object base) throws ObjectNotFoundException {
NativeArray arr = (NativeArray)get_object(path, base); NativeArray arr = (NativeArray)get_object(path, base);
String[] out = new String[(int)arr.getLength()]; String[] out = new String[(int)arr.getLength()];
int idx; int idx;
for(Object o : arr.getIds()) out[idx = (Integer)o] = arr.get(idx, arr).toString(); for(Object o : arr.getIds()) out[idx = (Integer)o] = arr.get(idx, arr).toString();
return out; return out;
} }
public static void close() { Context.exit(); } public static void close() { Context.exit(); }
} }

@ -5,6 +5,6 @@ package com.sheetjs;
import java.lang.Exception; import java.lang.Exception;
public class ObjectNotFoundException extends Exception { public class ObjectNotFoundException extends Exception {
public ObjectNotFoundException() {} public ObjectNotFoundException() {}
public ObjectNotFoundException(String message) { super(message); } public ObjectNotFoundException(String message) { super(message); }
} }

@ -12,47 +12,47 @@ import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Scriptable;
public class SheetJS { public class SheetJS {
public Scriptable scope; public Scriptable scope;
public Context cx; public Context cx;
public NativeObject nXLSX; public NativeObject nXLSX;
public SheetJS() throws Exception { public SheetJS() throws Exception {
this.cx = Context.enter(); this.cx = Context.enter();
this.scope = this.cx.initStandardObjects(); this.scope = this.cx.initStandardObjects();
/* boilerplate */ /* boilerplate */
cx.setOptimizationLevel(-1); cx.setOptimizationLevel(-1);
String s = "var global = (function(){ return this; }).call(null);"; String s = "var global = (function(){ return this; }).call(null);";
cx.evaluateString(scope, s, "<cmd>", 1, null); cx.evaluateString(scope, s, "<cmd>", 1, null);
/* eval library */ /* eval library */
s = new Scanner(SheetJS.class.getResourceAsStream("/xlsx.full.min.js")).useDelimiter("\\Z").next(); s = new Scanner(SheetJS.class.getResourceAsStream("/xlsx.full.min.js")).useDelimiter("\\Z").next();
//s = new Scanner(new File("xlsx.full.min.js")).useDelimiter("\\Z").next(); //s = new Scanner(new File("xlsx.full.min.js")).useDelimiter("\\Z").next();
cx.evaluateString(scope, s, "<cmd>", 1, null); cx.evaluateString(scope, s, "<cmd>", 1, null);
/* grab XLSX variable */ /* grab XLSX variable */
Object XLSX = scope.get("XLSX", scope); Object XLSX = scope.get("XLSX", scope);
if(XLSX == Scriptable.NOT_FOUND) throw new Exception("XLSX not found"); if(XLSX == Scriptable.NOT_FOUND) throw new Exception("XLSX not found");
this.nXLSX = (NativeObject)XLSX; this.nXLSX = (NativeObject)XLSX;
} }
public SheetJSFile read_file(String filename) throws IOException, ObjectNotFoundException { public SheetJSFile read_file(String filename) throws IOException, ObjectNotFoundException {
/* open file */ /* open file */
String d = JSHelper.read_file(filename); String d = JSHelper.read_file(filename);
/* options argument */ /* options argument */
NativeObject q = (NativeObject)this.cx.evaluateString(this.scope, "q = {'type':'binary', 'WTF':1};", "<cmd>", 2, null); NativeObject q = (NativeObject)this.cx.evaluateString(this.scope, "q = {'type':'binary', 'WTF':1};", "<cmd>", 2, null);
/* set up function arguments */ /* set up function arguments */
Object args[] = {d, q}; Object args[] = {d, q};
/* call read -> wb workbook */ /* call read -> wb workbook */
Function readfunc = (Function)JSHelper.get_object("XLSX.read",this.scope); Function readfunc = (Function)JSHelper.get_object("XLSX.read",this.scope);
NativeObject wb = (NativeObject)readfunc.call(this.cx, this.scope, this.nXLSX, args); NativeObject wb = (NativeObject)readfunc.call(this.cx, this.scope, this.nXLSX, args);
return new SheetJSFile(wb, this); return new SheetJSFile(wb, this);
} }
public static void close() { JSHelper.close(); } public static void close() { JSHelper.close(); }
} }

@ -6,19 +6,19 @@ import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.Function; import org.mozilla.javascript.Function;
public class SheetJSFile { public class SheetJSFile {
public NativeObject wb; public NativeObject wb;
public SheetJS sheetjs; public SheetJS sheetjs;
public SheetJSFile() {} public SheetJSFile() {}
public SheetJSFile(NativeObject wb, SheetJS sheetjs) { this.wb = wb; this.sheetjs = sheetjs; } public SheetJSFile(NativeObject wb, SheetJS sheetjs) { this.wb = wb; this.sheetjs = sheetjs; }
public String[] get_sheet_names() { public String[] get_sheet_names() {
try { try {
return JSHelper.get_string_array("SheetNames", this.wb); return JSHelper.get_string_array("SheetNames", this.wb);
} catch(ObjectNotFoundException e) { } catch(ObjectNotFoundException e) {
return null; return null;
} }
} }
public SheetJSSheet get_sheet(int idx) throws ObjectNotFoundException { public SheetJSSheet get_sheet(int idx) throws ObjectNotFoundException {
return new SheetJSSheet(this, idx); return new SheetJSSheet(this, idx);
} }
} }

@ -6,24 +6,24 @@ import org.mozilla.javascript.Function;
import org.mozilla.javascript.NativeObject; import org.mozilla.javascript.NativeObject;
public class SheetJSSheet { public class SheetJSSheet {
public NativeObject ws; public NativeObject ws;
public SheetJSFile wb; public SheetJSFile wb;
public SheetJSSheet(SheetJSFile wb, int idx) throws ObjectNotFoundException { public SheetJSSheet(SheetJSFile wb, int idx) throws ObjectNotFoundException {
this.wb = wb; this.wb = wb;
this.ws = (NativeObject)JSHelper.get_object("Sheets." + wb.get_sheet_names()[idx],wb.wb); this.ws = (NativeObject)JSHelper.get_object("Sheets." + wb.get_sheet_names()[idx],wb.wb);
} }
public String get_range() throws ObjectNotFoundException { public String get_range() throws ObjectNotFoundException {
return JSHelper.get_object("!ref",this.ws).toString(); return JSHelper.get_object("!ref",this.ws).toString();
} }
public String get_string_value(String address) throws ObjectNotFoundException { public String get_string_value(String address) throws ObjectNotFoundException {
return JSHelper.get_object(address + ".v",this.ws).toString(); return JSHelper.get_object(address + ".v",this.ws).toString();
} }
public String get_csv() throws ObjectNotFoundException { public String get_csv() throws ObjectNotFoundException {
Function csvify = (Function)JSHelper.get_object("XLSX.utils.sheet_to_csv",this.wb.sheetjs.scope); Function csvify = (Function)JSHelper.get_object("XLSX.utils.sheet_to_csv",this.wb.sheetjs.scope);
Object csvArgs[] = {this.ws}; Object csvArgs[] = {this.ws};
Object csv = csvify.call(this.wb.sheetjs.cx, this.wb.sheetjs.scope, this.wb.sheetjs.scope, csvArgs); Object csv = csvify.call(this.wb.sheetjs.cx, this.wb.sheetjs.scope, this.wb.sheetjs.scope, csvArgs);
return csv.toString(); return csv.toString();
} }
} }

@ -5,17 +5,17 @@ let sheetjs = try SheetJSCore();
try print(sheetjs.version()); try print(sheetjs.version());
let filenames: [[String]] = [ let filenames: [[String]] = [
["xlsx", "xlsx"], ["xlsx", "xlsx"],
["xlsb", "xlsb"], ["xlsb", "xlsb"],
["biff8.xls", "xls"], ["biff8.xls", "xls"],
["xml.xls", "xlml"] ["xml.xls", "xlml"]
]; ];
for fn in filenames { for fn in filenames {
let wb: SJSWorkbook = try sheetjs.readFile(file: "sheetjs." + fn[0]); let wb: SJSWorkbook = try sheetjs.readFile(file: "sheetjs." + fn[0]);
let ws: SJSWorksheet = try wb.getSheetAtIndex(idx: 0); let ws: SJSWorksheet = try wb.getSheetAtIndex(idx: 0);
let csv: String = try ws.toCSV(); let csv: String = try ws.toCSV();
print(csv); print(csv);
let wbout: String = try wb.writeBStr(bookType: fn[1]); let wbout: String = try wb.writeBStr(bookType: fn[1]);
try wbout.write(toFile: "sheetjsswift." + fn[0], atomically: false, encoding: String.Encoding.isoLatin1); try wbout.write(toFile: "sheetjsswift." + fn[0], atomically: false, encoding: String.Encoding.isoLatin1);
} }

@ -66,6 +66,8 @@ int main(int argc, char *argv[]) {
DOIT("var global = (function(){ return this; }).call(null);"); DOIT("var global = (function(){ return this; }).call(null);");
/* load library */ /* load library */
res = eval_file(ctx, "shim.min.js");
if(res != 0) FAIL("shim load")
res = eval_file(ctx, "xlsx.full.min.js"); res = eval_file(ctx, "xlsx.full.min.js");
if(res != 0) FAIL("library load") if(res != 0) FAIL("library load")
@ -75,9 +77,9 @@ int main(int argc, char *argv[]) {
duk_pop(ctx); duk_pop(ctx);
/* read file */ /* read file */
#define INFILE "sheetjs.xlsx" res = load_file(ctx, argv[1], "buf");
res = load_file(ctx, INFILE, "buf"); if(res != 0) FAIL("file load")
if(res != 0) FAIL("load " INFILE) printf("Loaded file %s\n", argv[1]);
/* parse workbook */ /* parse workbook */
DOIT("wb = XLSX.read(buf, {type:'buffer', cellNF:true});"); DOIT("wb = XLSX.read(buf, {type:'buffer', cellNF:true});");

@ -1,7 +1,7 @@
{ {
"env": { "shared-node-browser":true }, "env": { "shared-node-browser":true },
"parserOptions": { "parserOptions": {
"ecmaVersion": 2017 "ecmaVersion": 8
}, },
"plugins": [ "html", "json" ] "plugins": [ "html", "json" ]
} }

@ -0,0 +1,73 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* eslint-env node */
/* global Promise */
const XLSX = require('xlsx');
const assert = require('assert');
const SheetJSKnex = require("./SheetJSKnex");
const Knex = require('knex');
/* Connection to both databases are passed around */
let P = Promise.resolve([
Knex({ client: 'sqlite3', connection: { filename: './knex1.db' } }),
Knex({ client: 'sqlite3', connection: { filename: './knex2.db' } })
]);
/* Sample data table */
P = P.then(async (_) => {
const [knex] = _;
await knex.schema.dropTableIfExists('pres');
await knex.schema.createTable('pres', (table) => {
table.string('name');
table.integer('idx');
});
await knex.insert([
{ name: "Barack Obama", idx: 44 },
{ name: "Donald Trump", idx: 45 }
]).into('pres');
await knex.schema.dropTableIfExists('fmts');
await knex.schema.createTable('fmts', (table) => {
table.string('ext');
table.string('ctr');
table.integer('multi');
});
await knex.insert([
{ ext: 'XLSB', ctr: 'ZIP', multi: 1 },
{ ext: 'XLS', ctr: 'CFB', multi: 1 },
{ ext: 'XLML', ctr: '', multi: 1 },
{ ext: 'CSV', ctr: 'ZIP', multi: 0 },
]).into('fmts');
return _;
});
/* Export database to XLSX */
P = P.then(async (_) => {
const [knex] = _;
const wb = XLSX.utils.book_new();
await SheetJSKnex.book_append_knex(wb, knex, "pres");
await SheetJSKnex.book_append_knex(wb, knex, "fmts");
XLSX.writeFile(wb, "knex.xlsx");
return _;
});
/* Import XLSX to database */
P = P.then(async (_) => {
const [, knex] = _;
const wb = XLSX.readFile("knex.xlsx");
await SheetJSKnex.wb_to_knex(wb, knex);
return _;
});
/* Compare databases */
P = P.then(async (_) => {
const [k1, k2] = _;
const P1 = await k1.select("*").from('pres');
const P2 = await k2.select("*").from('pres');
const F1 = await k1.select("*").from('fmts');
const F2 = await k2.select("*").from('fmts');
assert.deepEqual(P1, P2);
assert.deepEqual(F1, F2);
});
P.then(async () => { process.exit(); });

@ -27,7 +27,7 @@ var init = [
]; ];
(async () => { (async () => {
const conn1 = await mysql.createConnection({...opts, database: "sheetjs"}); const conn1 = await mysql.createConnection(Object.assign({}, opts, {database: "sheetjs"}));
for(var i = 0; i < init.length; ++i) await conn1.query(init[i]); for(var i = 0; i < init.length; ++i) await conn1.query(init[i]);
/* Export table to XLSX */ /* Export table to XLSX */
@ -50,7 +50,7 @@ var init = [
await conn1.close(); await conn1.close();
/* Import XLSX to table */ /* Import XLSX to table */
const conn2 = await mysql.createConnection({...opts, database: "sheetj5"}); const conn2 = await mysql.createConnection(Object.assign({}, opts, {database: "sheetj5"}));
var wb2 = XLSX.readFile("mysql.xlsx"); var wb2 = XLSX.readFile("mysql.xlsx");
var queries = SheetJSSQL.book_to_sql(wb2, "MYSQL"); var queries = SheetJSSQL.book_to_sql(wb2, "MYSQL");
for(i = 0; i < queries.length; ++i) await conn2.query(queries[i]); for(i = 0; i < queries.length; ++i) await conn2.query(queries[i]);

@ -26,8 +26,8 @@ var init = [
"INSERT INTO fmts VALUES ('CSV', '', 0)", "INSERT INTO fmts VALUES ('CSV', '', 0)",
]; ];
var conn1 = new Client({...opts, database: "sheetjs"}); var conn1 = new Client(Object.assign({}, opts, {database: "sheetjs"}));
var conn2 = new Client({...opts, database: "sheetj5"}); var conn2 = new Client(Object.assign({}, opts, {database: "sheetj5"}));
(async () => { (async () => {
await conn1.connect(); await conn1.connect();
for(var i = 0; i < init.length; ++i) await conn1.query(init[i]); for(var i = 0; i < init.length; ++i) await conn1.query(init[i]);

@ -49,8 +49,12 @@ scanned to determine the column "types", and there are third-party connectors
that can push arrays of JS objects to database tables. that can push arrays of JS objects to database tables.
The [`sexql`](http://sheetjs.com/sexql) browser demo uses WebSQL, which is The [`sexql`](http://sheetjs.com/sexql) browser demo uses WebSQL, which is
limited to the SQLite fundamental types. Its schema builder scans the first row limited to the SQLite fundamental types.
to find headers:
<details>
<summary><b>Implementation details</b> (click to show)</summary>
The `sexql` schema builder scans the first row to find headers:
```js ```js
if(!ws || !ws['!ref']) return; if(!ws || !ws['!ref']) return;
@ -99,8 +103,11 @@ value of a column, the column is marked as `TEXT`:
} }
``` ```
</details>
The included `SheetJSSQL.js` script demonstrates SQL statement generation. The included `SheetJSSQL.js` script demonstrates SQL statement generation.
## Objects, K/V and "Schema-less" Databases ## Objects, K/V and "Schema-less" Databases
So-called "Schema-less" databases allow for arbitrary keys and values within the So-called "Schema-less" databases allow for arbitrary keys and values within the
@ -137,6 +144,9 @@ XXX| A | B |
The included `ObjUtils.js` script demonstrates object-workbook conversion: The included `ObjUtils.js` script demonstrates object-workbook conversion:
<details>
<summary><b>Implementation details</b> (click to show)</summary>
```js ```js
function deepset(obj, path, value) { function deepset(obj, path, value) {
if(path.indexOf(".") == -1) return obj[path] = value; if(path.indexOf(".") == -1) return obj[path] = value;
@ -185,6 +195,8 @@ function object_to_workbook(obj) {
} }
``` ```
</details>
## Browser APIs ## Browser APIs
@ -229,7 +241,7 @@ differences surround API shape and supported data types.
[The `better-sqlite3` module](https://www.npmjs.com/package/better-sqlite3) [The `better-sqlite3` module](https://www.npmjs.com/package/better-sqlite3)
provides a very simple API for working with SQLite databases. `Statement#all` provides a very simple API for working with SQLite databases. `Statement#all`
runs a prepared statement and returns an array of JS objects runs a prepared statement and returns an array of JS objects.
`SQLiteTest.js` generates a simple two-table SQLite database (`SheetJS1.db`), `SQLiteTest.js` generates a simple two-table SQLite database (`SheetJS1.db`),
exports to XLSX (`sqlite.xlsx`), imports the new XLSX file to a new database exports to XLSX (`sqlite.xlsx`), imports the new XLSX file to a new database
@ -255,6 +267,15 @@ The `rows` key of the object is an array of JS objects.
tables in the `sheetjs` database, exports to XLSX, imports the new XLSX file to tables in the `sheetjs` database, exports to XLSX, imports the new XLSX file to
the `sheetj5` database and verifies the tables are preserved. the `sheetj5` database and verifies the tables are preserved.
#### Knex Query Builder
[The `knex` module](https://www.npmjs.com/package/knex) builds SQL queries. The
same exact code can be used against Oracle Database, MSSQL, and other engines.
`KnexTest.js` uses the `sqlite3` connector and follows the same procedure as the
SQLite test. The included `SheetJSKnex.js` script converts between the query
builder and the common spreadsheet format.
### Key/Value Stores ### Key/Value Stores
#### Redis #### Redis
@ -267,7 +288,7 @@ strings in a special worksheet (`_strs`), the manifest in another worksheet
`RedisTest.js` connects to a local Redis server, populates data based on the `RedisTest.js` connects to a local Redis server, populates data based on the
official Redis tutorial, exports to XLSX, flushes the server, imports the new official Redis tutorial, exports to XLSX, flushes the server, imports the new
XLSX file and verifies the data round-tripped correctly. `SheetJSRedis.js` XLSX file and verifies the data round-tripped correctly. `SheetJSRedis.js`
includes the implementation details includes the implementation details.
#### LowDB #### LowDB

@ -21,7 +21,7 @@ var init = [
]; ];
db1.exec(init.join(";")); db1.exec(init.join(";"));
/* Export table to XLSX */ /* Export database to XLSX */
var wb = XLSX.utils.book_new(); var wb = XLSX.utils.book_new();
function book_append_table(wb, db, name) { function book_append_table(wb, db, name) {
var r = db.prepare('SELECT * FROM ' + name).all(); var r = db.prepare('SELECT * FROM ' + name).all();
@ -32,7 +32,7 @@ book_append_table(wb, db1, "pres");
book_append_table(wb, db1, "fmts"); book_append_table(wb, db1, "fmts");
XLSX.writeFile(wb, "sqlite.xlsx"); XLSX.writeFile(wb, "sqlite.xlsx");
/* Import XLSX to table */ /* Import XLSX to database */
var db2 = new Database('SheetJS2.db'); var db2 = new Database('SheetJS2.db');
var wb2 = XLSX.readFile("sqlite.xlsx"); var wb2 = XLSX.readFile("sqlite.xlsx");
var queries = SheetJSSQL.book_to_sql(wb2, "SQLITE"); var queries = SheetJSSQL.book_to_sql(wb2, "SQLITE");
@ -46,7 +46,6 @@ var F2 = db2.prepare("SELECT * FROM fmts").all();
assert.deepEqual(P1, P2); assert.deepEqual(P1, P2);
assert.deepEqual(F1, F2); assert.deepEqual(F1, F2);
/* Display results */
console.log(P2); console.log(P2);
console.log(F2); console.log(F2);

@ -0,0 +1,78 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* eslint-env node */
var XLSX = require("xlsx");
async function book_append_knex(wb, knex, tbl) {
const aoo = await knex.select("*").from(tbl);
XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(aoo), tbl);
}
const TYPES = {
b: "boolean",
n: "float",
t: "string",
d: "dateTime"
};
async function ws_to_knex(ws, knex, n) {
if(!ws || !ws['!ref']) return;
var range = XLSX.utils.decode_range(ws['!ref']);
if(!range || !range.s || !range.e || range.s > range.e) return;
var R = range.s.r, C = range.s.c;
var names = new Array(range.e.c-range.s.c+1);
for(C = range.s.c; C<= range.e.c; ++C){
var addr = XLSX.utils.encode_cell({c:C,r:R});
names[C-range.s.c] = ws[addr] ? ws[addr].v : XLSX.utils.encode_col(C);
}
for(var i = 0; i < names.length; ++i) if(names.indexOf(names[i]) < i)
for(var j = 0; j < names.length; ++j) {
var _name = names[i] + "_" + (j+1);
if(names.indexOf(_name) > -1) continue;
names[i] = _name;
}
var types = new Array(range.e.c-range.s.c+1);
for(C = range.s.c; C<= range.e.c; ++C) {
var seen = {}, _type = "";
for(R = range.s.r+1; R<= range.e.r; ++R)
seen[(ws[XLSX.utils.encode_cell({c:C,r:R})]||{t:"z"}).t] = true;
if(seen.s || seen.str) _type = TYPES.t;
else if(seen.n + seen.b + seen.d + seen.e > 1) _type = TYPES.t;
else switch(true) {
case seen.b: _type = TYPES.b; break;
case seen.n: _type = TYPES.n; break;
case seen.e: _type = TYPES.t; break;
case seen.d: _type = TYPES.d; break;
}
types[C-range.s.c] = _type || TYPES.t;
}
await knex.schema.dropTableIfExists(n);
await knex.schema.createTable(n, (table) => { names.forEach((n, i) => { table[types[i] || "text"](n); }); });
for(R = range.s.r+1; R<= range.e.r; ++R) {
var row = {};
for(C = range.s.c; C<= range.e.c; ++C) {
var cell = ws[XLSX.utils.encode_cell({c:C,r:R})];
if(!cell) continue;
var key = names[C-range.s.c], val = cell.v;
if(types[C-range.s.c] == TYPES.n) if(cell.t == 'b' || typeof val == 'boolean' ) val = +val;
row[key] = val;
}
await knex.insert(row).into(n);;
}
}
async function wb_to_knex(wb, knex) {
for(var i = 0; i < wb.SheetNames.length; ++i) {
var n = wb.SheetNames[i];
var ws = wb.Sheets[n];
await ws_to_knex(ws, knex, n);
}
}
module.exports = {
book_append_knex,
wb_to_knex
};

@ -67,6 +67,7 @@ async function wb_to_redis(wb, R) {
await aoa_to_redis[M[i].type](aoa, R, M[i].key); await aoa_to_redis[M[i].type](aoa, R, M[i].key);
} }
} }
module.exports = { module.exports = {
redis_to_wb, redis_to_wb,
wb_to_redis wb_to_redis

@ -1,4 +1,5 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* global XLSX, require, module */
var SheetJSSQL = (function() { var SheetJSSQL = (function() {
var X; var X;
@ -51,7 +52,7 @@ function sheet_to_sql(ws, sname, mode) {
var BT = mode == "PGSQL" ? "" : "`"; var BT = mode == "PGSQL" ? "" : "`";
var Q = mode == "PGSQL" ? "'" : '"'; var Q = mode == "PGSQL" ? "'" : '"';
var R = mode == "PGSQL" ? /'/g : /"/g; var J = mode == "PGSQL" ? /'/g : /"/g;
out.push("DROP TABLE IF EXISTS " + BT + sname + BT ); out.push("DROP TABLE IF EXISTS " + BT + sname + BT );
out.push("CREATE TABLE " + BT + sname + BT + " (" + names.map(function(n, i) { return BT + n + BT + " " + (types[i]||"TEXT"); }).join(", ") + ");" ); out.push("CREATE TABLE " + BT + sname + BT + " (" + names.map(function(n, i) { return BT + n + BT + " " + (types[i]||"TEXT"); }).join(", ") + ");" );
@ -64,7 +65,7 @@ function sheet_to_sql(ws, sname, mode) {
var val = cell.v; var val = cell.v;
switch(types[C-range.s.c]) { switch(types[C-range.s.c]) {
case TYPES.n: if(cell.t == 'b' || typeof val == 'boolean' ) val = +val; break; case TYPES.n: if(cell.t == 'b' || typeof val == 'boolean' ) val = +val; break;
default: val = Q + val.toString().replace(R, Q + Q) + Q; default: val = Q + val.toString().replace(J, Q + Q) + Q;
} }
values.push(val); values.push(val);
} }

@ -1,14 +1,36 @@
<!DOCTYPE html> <!DOCTYPE html>
<!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html> <html>
<head> <head>
<title></title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>SheetJS FuseBox Test</title>
<style>
a { text-decoration: none }
</style>
</head> </head>
<body> <body>
<script type="text/javascript" src="/client.js"></script> <pre>
<b><a href="http://sheetjs.com">SheetJS FuseBox Demo</a></b>
<a href="https://github.com/SheetJS/js-xlsx">Source Code Repo</a>
<a href="https://github.com/SheetJS/js-xlsx/issues">Issues? Something look weird? Click here and report an issue</a>
Original script: <a href="sheetjs.ts">sheetjs.ts</a>
<b>Console Output:</b>
</pre>
<pre id="console"></pre>
<b>
<script>
if(typeof console !== "undefined") console = {};
console.__log = console.log || function(){};
console.log = function(x) {
console.__log.apply(console, arguments);
document.getElementById('console').innerText += x + "\n";
};
console.error = console.debug = console.info = console.log
</script>
<script type="text/javascript" src="/client.js"></script>
</body> </body>
</html> </html>

@ -114,6 +114,8 @@ Downloadify.create(element_id, {
}); });
``` ```
#### Upload
The demo also includes an HTML file input element for updating the data table: The demo also includes an HTML file input element for updating the data table:
```js ```js

@ -15,7 +15,7 @@ native: ## Build react-native project
.PHONY: ios .PHONY: ios
ios: native ## react-native ios sim ios: native ## react-native ios sim
cd SheetJS; react-native run-ios; cd - cd SheetJS; react-native run-ios --simulator="iPhone X"; cd -
.PHONY: android .PHONY: android
android: native ## react-native android sim android: native ## react-native android sim

@ -77,8 +77,7 @@ Reproducing the full project is straightforward:
react-native init SheetJS react-native init SheetJS
cd SheetJS cd SheetJS
npm i -S xlsx react react-native react-native-table-component react-native-fs npm i -S xlsx react react-native react-native-table-component react-native-fs
cp ../react-native.js index.ios.js cp ../react-native.js index.js
cp ../react-native.js index.android.js
react-native link react-native link
``` ```
@ -107,6 +106,9 @@ const wbout = XLSX.write(wb, {type:'binary', bookType:"xlsx"});
writeFile(file, wbout, 'ascii').then((r)=>{/* :) */}).catch((e)=>{/* :( */}); writeFile(file, wbout, 'ascii').then((r)=>{/* :) */}).catch((e)=>{/* :( */});
``` ```
Note: for real app deployments, the `UIFileSharingEnabled` flag must be manually
set in the iOS project `Info.plist` file.
## Other Demos ## Other Demos
#### Preact #### Preact

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ # xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
if [ ! -e SheetJS ]; then if [ ! -e SheetJS ]; then
react-native init SheetJS react-native init --version="0.53.3" SheetJS
cd SheetJS cd SheetJS
npm i -S xlsx react-native-table-component react-native-fs npm i -S xlsx react-native-table-component react-native-fs
cd - cd -

@ -2,8 +2,8 @@
import XLSX from 'xlsx'; import XLSX from 'xlsx';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, View, Button, Alert, Image } from 'react-native'; import { AppRegistry, StyleSheet, Text, View, Button, Alert, Image, ScrollView, TouchableWithoutFeedback } from 'react-native';
import { Table, Row, Rows } from 'react-native-table-component'; import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
// react-native-fs // react-native-fs
import { writeFile, readFile, DocumentDirectoryPath } from 'react-native-fs'; import { writeFile, readFile, DocumentDirectoryPath } from 'react-native-fs';
@ -21,12 +21,14 @@ const output = str => str.split("").map(x => x.charCodeAt(0));
*/ */
const make_cols = refstr => Array.from({length: XLSX.utils.decode_range(refstr).e.c + 1}, (x,i) => XLSX.utils.encode_col(i)); const make_cols = refstr => Array.from({length: XLSX.utils.decode_range(refstr).e.c + 1}, (x,i) => XLSX.utils.encode_col(i));
const make_width = refstr => Array.from({length: XLSX.utils.decode_range(refstr).e.c + 1}, () => 60);
export default class SheetJS extends Component { export default class SheetJS extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
data: [[1,2,3],[4,5,6]], data: [[1,2,3],[4,5,6]],
widthArr: [60, 60, 60],
cols: make_cols("A1:C2") cols: make_cols("A1:C2")
}; };
this.importFile = this.importFile.bind(this); this.importFile = this.importFile.bind(this);
@ -46,7 +48,7 @@ export default class SheetJS extends Component {
const data = XLSX.utils.sheet_to_json(ws, {header:1}); const data = XLSX.utils.sheet_to_json(ws, {header:1});
/* update state */ /* update state */
this.setState({ data: data, cols: make_cols(ws['!ref']) }); this.setState({ data: data, cols: make_cols(ws['!ref']), widthArr: make_width(ws['!ref']) });
}).catch((err) => { Alert.alert("importFile Error", "Error " + err.message); }); }).catch((err) => { Alert.alert("importFile Error", "Error " + err.message); });
}} }}
]); ]);
@ -67,7 +69,8 @@ export default class SheetJS extends Component {
}).catch((err) => { Alert.alert("exportFile Error", "Error " + err.message); }); }).catch((err) => { Alert.alert("exportFile Error", "Error " + err.message); });
}; };
render() { return ( render() { return (
<View style={styles.container}> <ScrollView contentContainerStyle={styles.container} vertical={true}>
<Text style={styles.welcome}> </Text>
<Image style={{width: 128, height: 128}} source={require('./logo.png')} /> <Image style={{width: 128, height: 128}} source={require('./logo.png')} />
<Text style={styles.welcome}>SheetJS React Native Demo</Text> <Text style={styles.welcome}>SheetJS React Native Demo</Text>
<Text style={styles.instructions}>Import Data</Text> <Text style={styles.instructions}>Import Data</Text>
@ -76,11 +79,22 @@ export default class SheetJS extends Component {
<Button disabled={!this.state.data.length} onPress={this.exportFile} title="Export data to XLSX" color="#841584" /> <Button disabled={!this.state.data.length} onPress={this.exportFile} title="Export data to XLSX" color="#841584" />
<Text style={styles.instructions}>Current Data</Text> <Text style={styles.instructions}>Current Data</Text>
<Table style={styles.table}>
<Row data={this.state.cols} style={styles.thead} textStyle={styles.text}/> <ScrollView style={styles.table} horizontal={true} >
<Rows data={this.state.data} style={styles.tr} textStyle={styles.text}/> <Table style={styles.table}>
</Table> <TableWrapper>
</View> <Row data={this.state.cols} style={styles.thead} textStyle={styles.text} widthArr={this.state.widthArr}/>
</TableWrapper>
<TouchableWithoutFeedback>
<ScrollView vertical={true}>
<TableWrapper>
<Rows data={this.state.data} style={styles.tr} textStyle={styles.text} widthArr={this.state.widthArr}/>
</TableWrapper>
</ScrollView>
</TouchableWithoutFeedback>
</Table>
</ScrollView>
</ScrollView>
); }; ); };
}; };

@ -1,4 +1,7 @@
.PHONY: test .PHONY: test ctest
test: test:
cp ../../dist/xlsx.full.min.js . cp ../../dist/xlsx.full.min.js .
node test.node.js node test.node.js
ctest:
python -mSimpleHTTPServer

@ -56,7 +56,7 @@ onmessage = function(evt) {
/* xlsxworker.js */ /* xlsxworker.js */
var XLSX = require('xlsx'); var XLSX = require('xlsx');
_cb = function (evt) { /* ... do work here ... */ }; _cb = function(evt) { /* ... do work here ... */ };
``` ```
## Node ## Node

@ -1,6 +1,6 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
var XLSX = require('xlsx'); var XLSX = require('xlsx');
console.log(XLSX); console.log(XLSX.version);
var w = XLSX.read('abc,def\nghi,jkl', {type:'binary'}); var w = XLSX.read('abc,def\nghi,jkl', {type:'binary'});
var j = XLSX.utils.sheet_to_json(w.Sheets[w.SheetNames[0]], {header:1}); var j = XLSX.utils.sheet_to_json(w.Sheets[w.SheetNames[0]], {header:1});
console.log(j); console.log(j);

@ -1,4 +1,36 @@
<!DOCTYPE html>
<!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com --> <!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>SheetJS SystemJS Test</title>
<style>
a { text-decoration: none }
</style>
</head>
<body>
<pre>
<b><a href="http://sheetjs.com">SheetJS SystemJS Demo</a></b>
<a href="https://github.com/SheetJS/js-xlsx">Source Code Repo</a>
<a href="https://github.com/SheetJS/js-xlsx/issues">Issues? Something look weird? Click here and report an issue</a>
Original script: <a href="main.simple.js">main.simple.js</a>
<b>Console Output:</b>
</pre>
<pre id="console"></pre>
<b>
<script>
if(typeof console !== "undefined") console = {};
console.__log = console.log || function(){};
console.log = function(x) {
console.__log.apply(console, arguments);
document.getElementById('console').innerText += x + "\n";
};
console.error = console.debug = console.info = console.log
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.20.16/system.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.20.16/system.js"></script>
<script> <script>
SystemJS.config({ SystemJS.config({
@ -16,3 +48,5 @@ SystemJS.config({
}); });
SystemJS.import('main.simple.js'); SystemJS.import('main.simple.js');
</script> </script>
</body>
</html>

@ -1,6 +1,10 @@
.PHONY: all .PHONY: all
all: all:
npm run build @npm run build
.PHONY: lint
lint:
@npm run lint
.PHONY: init .PHONY: init
init: init:

@ -1,2 +1,5 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/* eslint-env node */
var readFirstSheet = require("./").readFirstSheet; var readFirstSheet = require("./").readFirstSheet;
console.log(readFirstSheet("../../sheetjs.xlsb", {type:"file", cellDates:true})); console.log(readFirstSheet("../../sheetjs.xlsb", {type:"file", cellDates:true}));

@ -5,8 +5,8 @@ import * as XLSX from 'xlsx';
const { read, utils: { sheet_to_json } } = XLSX; const { read, utils: { sheet_to_json } } = XLSX;
export function readFirstSheet(data:any, options:XLSX.ParsingOptions): any[][] { export function readFirstSheet(data: any, options: XLSX.ParsingOptions): any[][] {
const wb: XLSX.WorkBook = read(data, options); const wb: XLSX.WorkBook = read(data, options);
const ws: XLSX.WorkSheet = wb.Sheets[wb.SheetNames[0]]; const ws: XLSX.WorkSheet = wb.Sheets[wb.SheetNames[0]];
return sheet_to_json(ws, {header:1, raw:true}); return sheet_to_json(ws, { header: 1, raw: true });
}; }

@ -3,10 +3,10 @@
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
"version": "0.0.0", "version": "0.0.0",
"license": "MIT", "license": "Apache-2.0",
"scripts": { "scripts": {
"build": "tsc && browserify -o dist/browser.js src/index.js", "build": "tsc && browserify -o dist/browser.js src/index.js",
"test": "echo \"Error: no test specified\" && exit 1" "lint": "tslint lib/*.ts"
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {

@ -1,2 +1,6 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/* eslint-env browser */
/* global require */
var readFirstSheet = require("../").readFirstSheet; var readFirstSheet = require("../").readFirstSheet;
console.log(readFirstSheet("a,b,c\n1,2,3\n4,5,6", {type:"binary"})); console.log(readFirstSheet("a,b,c\n1,2,3\n4,5,6", {type:"binary"}));

@ -0,0 +1,11 @@
{
"extends": "tslint-config-airbnb",
"rules": {
"whitespace": false,
"no-sparse-arrays": false,
"only-arrow-functions": false,
"no-consecutive-blank-lines": false,
"prefer-conditional-expression": false,
"one-variable-per-declaration": false
}
}

1
demos/xhr/.gitignore vendored Normal file

@ -0,0 +1 @@
files/

@ -16,14 +16,13 @@ The included demos focus on an editable table. There are two separate flows:
- When the upload button is clicked, the browser will generate a new worksheet - When the upload button is clicked, the browser will generate a new worksheet
using `table_to_book` and build up a new workbook. It will then attempt to using `table_to_book` and build up a new workbook. It will then attempt to
generate a Base64-encoded XLSX string and upload it to the server. generate a file and upload it to the server.
### Demo Server ### Demo Server
The `server.js` nodejs server serves static files on `GET` request. On a `POST` The `server.js` nodejs server serves static files on `GET` request. On a `POST`
request to `/upload`, the server processes the body and looks for the `file` and request to `/upload`, the server processes the body and looks for uploaded file.
`data` fields. It will write the Base64-decoded data from `data` to the file It will write the data for the first file to the indicated file name.
name specified in `file`.
To start the demo, run `npm start` and navigate to <http://localhost:7262/> To start the demo, run `npm start` and navigate to <http://localhost:7262/>
@ -48,16 +47,16 @@ req.onload = function(e) {
req.send(); req.send();
``` ```
For uploading data, this demo populates a `FormData` object with string data For uploading data, this demo populates a `FormData` object with an ArrayBuffer
generated with the `base64` output type: generated with the `array` output type:
```js ```js
/* generate XLSX as base64 string */ /* generate XLSX as array buffer */
var b64 = XLSX.write(workbook, {bookType:'xlsx', type:'base64'}); var data = XLSX.write(workbook, {bookType: 'xlsx', type: 'array'});
/* build FormData with the generated file */ /* build FormData with the generated file */
var fd = new FormData(); var fd = new FormData();
fd.append('data', b64); fd.append('data', new File([data], 'sheetjs.xlsx'));
/* send data */ /* send data */
var req = new XMLHttpRequest(); var req = new XMLHttpRequest();

@ -47,8 +47,9 @@ document.getElementById('ulbutton').onclick = function() {
var wb = XLSX.utils.table_to_book(document.getElementById('htmlout')); var wb = XLSX.utils.table_to_book(document.getElementById('htmlout'));
console.log(wb); console.log(wb);
var fd = new FormData(); var fd = new FormData();
fd.append('file', demo + '.' + book); var data = XLSX.write(wb, {bookType:book, type:'array'});
fd.append('data', XLSX.write(wb, {bookType:book, type:'base64'})); console.log(data);
fd.append('data', new File([data], demo + '.' + book));
axios("/upload", {method: "POST", data: fd}); axios("/upload", {method: "POST", data: fd});
}; };
</script> </script>

@ -56,8 +56,9 @@ document.getElementById('ulbutton').onclick = function() {
var wb = XLSX.utils.table_to_book(document.getElementById('htmlout')); var wb = XLSX.utils.table_to_book(document.getElementById('htmlout'));
console.log(wb); console.log(wb);
var fd = new FormData(); var fd = new FormData();
fd.append('file', demo + '.' + book); var data = XLSX.write(wb, {bookType:book, type:'array'});
fd.append('data', XLSX.write(wb, {bookType:book, type:'base64'})); console.log(data);
fd.append('data', new File([data], demo + '.' + book));
fetch("/upload", {method: "POST", body: fd}).then(function(r) { return r.text(); }).then(function(t) { console.log(t); }); fetch("/upload", {method: "POST", body: fd}).then(function(r) { return r.text(); }).then(function(t) { console.log(t); });
}; };
</script> </script>

@ -10,13 +10,19 @@ var cors = require('../server/_cors');
var port = +process.argv[2] || +process.env.PORT || 7262; var port = +process.argv[2] || +process.env.PORT || 7262;
var basepath = process.cwd(); var basepath = process.cwd();
var dir = path.join(__dirname, "files");
try { fs.mkdirSync(dir); } catch(e) {}
app.use(logit.mw); app.use(logit.mw);
app.use(cors.mw); app.use(cors.mw);
app.use(require('express-formidable')()); app.use(require('express-formidable')({uploadDir: dir}));
app.post('/upload', function(req, res) { app.post('/upload', function(req, res) {
fs.writeFile(req.fields.file, req.fields.data, 'base64', function(err, r) { console.log(req.files);
res.end("wrote to " + req.fields.file); var f = req.files[Object.keys(req.files)[0]];
}); var newpath = path.join(dir, f.name);
fs.renameSync(f.path, newpath);
console.log("moved " + f.path + " to " + newpath);
res.end("wrote to " + f.name);
}); });
app.use(express.static(path.resolve(basepath))); app.use(express.static(path.resolve(basepath)));
app.use(require('serve-index')(basepath, {'icons':true})); app.use(require('serve-index')(basepath, {'icons':true}));

@ -49,8 +49,9 @@ document.getElementById('ulbutton').onclick = function() {
var wb = XLSX.utils.table_to_book(document.getElementById('htmlout')); var wb = XLSX.utils.table_to_book(document.getElementById('htmlout'));
console.log(wb); console.log(wb);
var fd = new FormData(); var fd = new FormData();
fd.append('file', demo + '.' + book); var data = XLSX.write(wb, {bookType:book, type:'array'});
fd.append('data', XLSX.write(wb, {bookType:book, type:'base64'})); console.log(data);
fd.append('data', new File([data], demo + '.' + book));
superagent.post("/upload").send(fd).end(function(e,r) { console.log(r.text); }); superagent.post("/upload").send(fd).end(function(e,r) { console.log(r.text); });
}; };
</script> </script>

@ -55,8 +55,9 @@ document.getElementById('ulbutton').onclick = function() {
var wb = XLSX.utils.table_to_book(document.getElementById('htmlout')); var wb = XLSX.utils.table_to_book(document.getElementById('htmlout'));
console.log(wb); console.log(wb);
var fd = new FormData(); var fd = new FormData();
fd.append('file', demo + '.' + book); var data = XLSX.write(wb, {bookType:book, type:'array'});
fd.append('data', XLSX.write(wb, {bookType:book, type:'base64'})); console.log(data);
fd.append('data', new File([data], demo + '.' + book));
var req = new XMLHttpRequest(); var req = new XMLHttpRequest();
req.open("POST", "/upload", true); req.open("POST", "/upload", true);
req.send(fd); req.send(fd);

28
dist/xlsx.core.min.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.core.min.map generated vendored

File diff suppressed because one or more lines are too long

21
dist/xlsx.extendscript.js generated vendored

@ -9141,7 +9141,7 @@ module.exports = ZStream;
/*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false */ /*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false */
var XLSX = {}; var XLSX = {};
(function make_xlsx(XLSX){ (function make_xlsx(XLSX){
XLSX.version = '0.12.4'; XLSX.version = '0.12.5';
var current_codepage = 1200, current_ansi = 1252; var current_codepage = 1200, current_ansi = 1252;
/*global cptable:true */ /*global cptable:true */
if(typeof module !== "undefined" && typeof require !== 'undefined') { if(typeof module !== "undefined" && typeof require !== 'undefined') {
@ -18109,7 +18109,8 @@ function write_comments_vml(rId, comments) {
]; ];
while(_shapeid < rId * 1000) _shapeid += 1000; while(_shapeid < rId * 1000) _shapeid += 1000;
comments.map(function(x) { return decode_cell(x[0]); }).forEach(function(c) { o = o.concat([ comments.forEach(function(x) { var c = decode_cell(x[0]);
o = o.concat([
'<v:shape' + wxt_helper({ '<v:shape' + wxt_helper({
id:'_x0000_s' + (++_shapeid), id:'_x0000_s' + (++_shapeid),
type:"#_x0000_t202", type:"#_x0000_t202",
@ -18225,13 +18226,11 @@ function write_comments_xml(data) {
var iauthor = []; var iauthor = [];
o.push("<authors>"); o.push("<authors>");
data.map(function(x) { return x[1]; }).forEach(function(comment) { data.forEach(function(x) { x[1].forEach(function(w) { var a = escapexml(w.a);
comment.map(function(x) { return escapexml(x.a); }).forEach(function(a) { if(iauthor.indexOf(a) > -1) return;
if(iauthor.indexOf(a) > -1) return; iauthor.push(a);
iauthor.push(a); o.push("<author>" + a + "</author>");
o.push("<author>" + a + "</author>"); }); });
});
});
o.push("</authors>"); o.push("</authors>");
o.push("<commentList>"); o.push("<commentList>");
data.forEach(function(d) { data.forEach(function(d) {
@ -23440,6 +23439,8 @@ function xlml_clean_comment(comment) {
function xlml_normalize(d) { function xlml_normalize(d) {
if(has_buf && Buffer.isBuffer(d)) return d.toString('utf8'); if(has_buf && Buffer.isBuffer(d)) return d.toString('utf8');
if(typeof d === 'string') return d; if(typeof d === 'string') return d;
/* duktape */
if(typeof Uint8Array !== 'undefined' && d instanceof Uint8Array) return utf8read(a2s(ab2a(d)));
throw new Error("Bad input format: expected Buffer or string"); throw new Error("Bad input format: expected Buffer or string");
} }
@ -28883,7 +28884,9 @@ if(has_buf && typeof require != 'undefined') (function() {
var rowinfo = o.skipHidden && sheet["!rows"] || []; var rowinfo = o.skipHidden && sheet["!rows"] || [];
for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C); for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C);
var R = r.s.r; var R = r.s.r;
var BOM = false;
stream._read = function() { stream._read = function() {
if(!BOM) { BOM = true; return stream.push("\uFEFF"); }
if(R > r.e.r) return stream.push(null); if(R > r.e.r) return stream.push(null);
while(R <= r.e.r) { while(R <= r.e.r) {
++R; ++R;

16
dist/xlsx.full.min.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.full.min.map generated vendored

File diff suppressed because one or more lines are too long

21
dist/xlsx.js generated vendored

@ -4,7 +4,7 @@
/*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false */ /*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false */
var XLSX = {}; var XLSX = {};
(function make_xlsx(XLSX){ (function make_xlsx(XLSX){
XLSX.version = '0.12.4'; XLSX.version = '0.12.5';
var current_codepage = 1200, current_ansi = 1252; var current_codepage = 1200, current_ansi = 1252;
/*global cptable:true */ /*global cptable:true */
if(typeof module !== "undefined" && typeof require !== 'undefined') { if(typeof module !== "undefined" && typeof require !== 'undefined') {
@ -8972,7 +8972,8 @@ function write_comments_vml(rId, comments) {
]; ];
while(_shapeid < rId * 1000) _shapeid += 1000; while(_shapeid < rId * 1000) _shapeid += 1000;
comments.map(function(x) { return decode_cell(x[0]); }).forEach(function(c) { o = o.concat([ comments.forEach(function(x) { var c = decode_cell(x[0]);
o = o.concat([
'<v:shape' + wxt_helper({ '<v:shape' + wxt_helper({
id:'_x0000_s' + (++_shapeid), id:'_x0000_s' + (++_shapeid),
type:"#_x0000_t202", type:"#_x0000_t202",
@ -9088,13 +9089,11 @@ function write_comments_xml(data) {
var iauthor = []; var iauthor = [];
o.push("<authors>"); o.push("<authors>");
data.map(function(x) { return x[1]; }).forEach(function(comment) { data.forEach(function(x) { x[1].forEach(function(w) { var a = escapexml(w.a);
comment.map(function(x) { return escapexml(x.a); }).forEach(function(a) { if(iauthor.indexOf(a) > -1) return;
if(iauthor.indexOf(a) > -1) return; iauthor.push(a);
iauthor.push(a); o.push("<author>" + a + "</author>");
o.push("<author>" + a + "</author>"); }); });
});
});
o.push("</authors>"); o.push("</authors>");
o.push("<commentList>"); o.push("<commentList>");
data.forEach(function(d) { data.forEach(function(d) {
@ -14303,6 +14302,8 @@ function xlml_clean_comment(comment) {
function xlml_normalize(d) { function xlml_normalize(d) {
if(has_buf && Buffer.isBuffer(d)) return d.toString('utf8'); if(has_buf && Buffer.isBuffer(d)) return d.toString('utf8');
if(typeof d === 'string') return d; if(typeof d === 'string') return d;
/* duktape */
if(typeof Uint8Array !== 'undefined' && d instanceof Uint8Array) return utf8read(a2s(ab2a(d)));
throw new Error("Bad input format: expected Buffer or string"); throw new Error("Bad input format: expected Buffer or string");
} }
@ -19746,7 +19747,9 @@ if(has_buf && typeof require != 'undefined') (function() {
var rowinfo = o.skipHidden && sheet["!rows"] || []; var rowinfo = o.skipHidden && sheet["!rows"] || [];
for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C); for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C);
var R = r.s.r; var R = r.s.r;
var BOM = false;
stream._read = function() { stream._read = function() {
if(!BOM) { BOM = true; return stream.push("\uFEFF"); }
if(R > r.e.r) return stream.push(null); if(R > r.e.r) return stream.push(null);
while(R <= r.e.r) { while(R <= r.e.r) {
++R; ++R;

16
dist/xlsx.min.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.min.map generated vendored

File diff suppressed because one or more lines are too long

@ -14,6 +14,7 @@ In the browser, just add a script tag:
| `unpkg` | <https://unpkg.com/xlsx/> | | `unpkg` | <https://unpkg.com/xlsx/> |
| `jsDelivr` | <https://jsdelivr.com/package/npm/xlsx> | | `jsDelivr` | <https://jsdelivr.com/package/npm/xlsx> |
| `CDNjs` | <http://cdnjs.com/libraries/xlsx> | | `CDNjs` | <http://cdnjs.com/libraries/xlsx> |
| `packd` | <https://bundle.run/xlsx?name=XLSX> |
`unpkg` makes the latest version available at: `unpkg` makes the latest version available at:

@ -154,6 +154,7 @@ In the browser, just add a script tag:
| `unpkg` | <https://unpkg.com/xlsx/> | | `unpkg` | <https://unpkg.com/xlsx/> |
| `jsDelivr` | <https://jsdelivr.com/package/npm/xlsx> | | `jsDelivr` | <https://jsdelivr.com/package/npm/xlsx> |
| `CDNjs` | <http://cdnjs.com/libraries/xlsx> | | `CDNjs` | <http://cdnjs.com/libraries/xlsx> |
| `packd` | <https://bundle.run/xlsx?name=XLSX> |
`unpkg` makes the latest version available at: `unpkg` makes the latest version available at:

@ -1,6 +1,6 @@
{ {
"name": "xlsx", "name": "xlsx",
"version": "0.12.4", "version": "0.12.5",
"author": "sheetjs", "author": "sheetjs",
"description": "SheetJS Spreadsheet data parser and writer", "description": "SheetJS Spreadsheet data parser and writer",
"keywords": [ "keywords": [

@ -205,7 +205,7 @@ filenames.forEach(function(r) {
var ext = r[1] && r[1].bookType || r[0].split(".")[1]; var ext = r[1] && r[1].bookType || r[0].split(".")[1];
ext = {"htm":"html"}[ext] || ext; ext = {"htm":"html"}[ext] || ext;
OUT.forEach(function(type) { OUT.forEach(function(type) {
if(type == "string" && ["xlsx", "xlsm", "xlsb", "xlam", "biff8", "biff5", "xla", "ods", "dbf"].indexOf(ext) > -1) return; if(type == "string" && ["xlsx", "xlsm", "xlsb", "xlam", "biff8", "biff5", "biff2", "xla", "ods", "dbf"].indexOf(ext) > -1) return;
if(type == "array" && ["xlsx", "xlsm", "xlsb", "xlam", "ods"].indexOf(ext) > -1 && typeof Uint8Array === 'undefined') return; if(type == "array" && ["xlsx", "xlsm", "xlsb", "xlam", "ods"].indexOf(ext) > -1 && typeof Uint8Array === 'undefined') return;
var datout = XLSX.write(wb, {type: type, bookType: ext, sheet:r[1] && r[1].sheet || null}); var datout = XLSX.write(wb, {type: type, bookType: ext, sheet:r[1] && r[1].sheet || null});
XLSX.read(datout, {type:type}); XLSX.read(datout, {type:type});

3
types/index.d.ts vendored

@ -730,6 +730,9 @@ export interface XLSX$Utils {
/** Converts A1 range to 0-indexed form */ /** Converts A1 range to 0-indexed form */
decode_range(range: string): Range; decode_range(range: string): Range;
/** Format cell */
format_cell(cell: CellObject, v?: any, opts?: any): string;
/* --- General Utilities --- */ /* --- General Utilities --- */
/** Creates a new workbook */ /** Creates a new workbook */

@ -28,13 +28,13 @@ const formulae: string[] = XLSX.utils.sheet_to_formulae(firstworksheet);
const aoa: any[][] = XLSX.utils.sheet_to_json<any[]>(firstworksheet, {raw:true, header:1}); const aoa: any[][] = XLSX.utils.sheet_to_json<any[]>(firstworksheet, {raw:true, header:1});
const aoa2: XLSX.WorkSheet = XLSX.utils.aoa_to_sheet<number>([ const aoa2: XLSX.WorkSheet = XLSX.utils.aoa_to_sheet<number>([
[1,2,3,4,5,6,7], [1,2,3,4,5,6,7],
[2,3,4,5,6,7,8] [2,3,4,5,6,7,8]
]); ]);
const js2ws: XLSX.WorkSheet = XLSX.utils.json_to_sheet<Tester>([ const js2ws: XLSX.WorkSheet = XLSX.utils.json_to_sheet<Tester>([
{name:"Sheet", age: 12}, {name:"Sheet", age: 12},
{name:"JS", age: 24} {name:"JS", age: 24}
]); ]);
const WBProps = workbook.Workbook; const WBProps = workbook.Workbook;
@ -57,8 +57,23 @@ const wb_4: XLSX.WorkBook = XLSX.read(XLSX.write(newwb, {type: "file", bookType:
const wb_5: XLSX.WorkBook = XLSX.read(XLSX.write(newwb, {type: "array", bookType: "xlsx" }), {type: "array"}); const wb_5: XLSX.WorkBook = XLSX.read(XLSX.write(newwb, {type: "array", bookType: "xlsx" }), {type: "array"});
const wb_6: XLSX.WorkBook = XLSX.read(XLSX.write(newwb, {type: "string", bookType: "xlsx" }), {type: "string"}); const wb_6: XLSX.WorkBook = XLSX.read(XLSX.write(newwb, {type: "string", bookType: "xlsx" }), {type: "string"});
function get_header_row(sheet: XLSX.WorkSheet) {
let headers: string[] = [];
const range = XLSX.utils.decode_range(sheet['!ref']);
let C: number = 0, R: number = range.s.r;
for(C = range.s.c; C <= range.e.c; ++C) {
const cell: XLSX.CellObject = sheet[XLSX.utils.encode_cell({c:C, r:R})];
let hdr = "UNKNOWN " + C;
if(cell && cell.t) hdr = XLSX.utils.format_cell(cell);
headers.push(hdr);
}
return headers;
}
const headers: string[] = get_header_row(aoa2);
const CFB = XLSX.CFB; const CFB = XLSX.CFB;
const vbawb = XLSX.readFile("test.xlsm", {bookVBA:true}); const vbawb = XLSX.readFile("test.xlsm", {bookVBA:true});
if(vbawb.vbaraw) { if(vbawb.vbaraw) {
const cfb: XLSX.CFB.CFB$Container = CFB.read(vbawb.vbaraw, {type: "buffer"}); const cfb: XLSX.CFB.CFB$Container = CFB.read(vbawb.vbaraw, {type: "buffer"});
} }

@ -4,7 +4,7 @@
/*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false */ /*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false */
var XLSX = {}; var XLSX = {};
(function make_xlsx(XLSX){ (function make_xlsx(XLSX){
XLSX.version = '0.12.4'; XLSX.version = '0.12.5';
var current_codepage = 1200, current_ansi = 1252; var current_codepage = 1200, current_ansi = 1252;
/*:: declare var cptable:any; */ /*:: declare var cptable:any; */
/*global cptable:true */ /*global cptable:true */
@ -9066,7 +9066,8 @@ function write_comments_vml(rId, comments) {
]; ];
while(_shapeid < rId * 1000) _shapeid += 1000; while(_shapeid < rId * 1000) _shapeid += 1000;
comments.map(function(x) { return decode_cell(x[0]); }).forEach(function(c) { o = o.concat([ comments.forEach(function(x) { var c = decode_cell(x[0]);
o = o.concat([
'<v:shape' + wxt_helper({ '<v:shape' + wxt_helper({
id:'_x0000_s' + (++_shapeid), id:'_x0000_s' + (++_shapeid),
type:"#_x0000_t202", type:"#_x0000_t202",
@ -9182,13 +9183,11 @@ function write_comments_xml(data/*::, opts*/) {
var iauthor/*:Array<string>*/ = []; var iauthor/*:Array<string>*/ = [];
o.push("<authors>"); o.push("<authors>");
data.map(function(x) { return x[1]; }).forEach(function(comment) { data.forEach(function(x) { x[1].forEach(function(w) { var a = escapexml(w.a);
comment.map(function(x) { return escapexml(x.a); }).forEach(function(a) { if(iauthor.indexOf(a) > -1) return;
if(iauthor.indexOf(a) > -1) return; iauthor.push(a);
iauthor.push(a); o.push("<author>" + a + "</author>");
o.push("<author>" + a + "</author>"); }); });
});
});
o.push("</authors>"); o.push("</authors>");
o.push("<commentList>"); o.push("<commentList>");
data.forEach(function(d) { data.forEach(function(d) {
@ -14403,6 +14402,8 @@ function xlml_clean_comment(comment/*:any*/) {
function xlml_normalize(d)/*:string*/ { function xlml_normalize(d)/*:string*/ {
if(has_buf &&/*::typeof Buffer !== "undefined" && d != null && d instanceof Buffer &&*/ Buffer.isBuffer(d)) return d.toString('utf8'); if(has_buf &&/*::typeof Buffer !== "undefined" && d != null && d instanceof Buffer &&*/ Buffer.isBuffer(d)) return d.toString('utf8');
if(typeof d === 'string') return d; if(typeof d === 'string') return d;
/* duktape */
if(typeof Uint8Array !== 'undefined' && d instanceof Uint8Array) return utf8read(a2s(ab2a(d)));
throw new Error("Bad input format: expected Buffer or string"); throw new Error("Bad input format: expected Buffer or string");
} }
@ -19862,7 +19863,9 @@ if(has_buf && typeof require != 'undefined') (function() {
var rowinfo/*:Array<RowInfo>*/ = o.skipHidden && sheet["!rows"] || []; var rowinfo/*:Array<RowInfo>*/ = o.skipHidden && sheet["!rows"] || [];
for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C); for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C);
var R = r.s.r; var R = r.s.r;
var BOM = false;
stream._read = function() { stream._read = function() {
if(!BOM) { BOM = true; return stream.push("\uFEFF"); }
if(R > r.e.r) return stream.push(null); if(R > r.e.r) return stream.push(null);
while(R <= r.e.r) { while(R <= r.e.r) {
++R; ++R;

21
xlsx.js generated

@ -4,7 +4,7 @@
/*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false */ /*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false */
var XLSX = {}; var XLSX = {};
(function make_xlsx(XLSX){ (function make_xlsx(XLSX){
XLSX.version = '0.12.4'; XLSX.version = '0.12.5';
var current_codepage = 1200, current_ansi = 1252; var current_codepage = 1200, current_ansi = 1252;
/*global cptable:true */ /*global cptable:true */
if(typeof module !== "undefined" && typeof require !== 'undefined') { if(typeof module !== "undefined" && typeof require !== 'undefined') {
@ -8972,7 +8972,8 @@ function write_comments_vml(rId, comments) {
]; ];
while(_shapeid < rId * 1000) _shapeid += 1000; while(_shapeid < rId * 1000) _shapeid += 1000;
comments.map(function(x) { return decode_cell(x[0]); }).forEach(function(c) { o = o.concat([ comments.forEach(function(x) { var c = decode_cell(x[0]);
o = o.concat([
'<v:shape' + wxt_helper({ '<v:shape' + wxt_helper({
id:'_x0000_s' + (++_shapeid), id:'_x0000_s' + (++_shapeid),
type:"#_x0000_t202", type:"#_x0000_t202",
@ -9088,13 +9089,11 @@ function write_comments_xml(data) {
var iauthor = []; var iauthor = [];
o.push("<authors>"); o.push("<authors>");
data.map(function(x) { return x[1]; }).forEach(function(comment) { data.forEach(function(x) { x[1].forEach(function(w) { var a = escapexml(w.a);
comment.map(function(x) { return escapexml(x.a); }).forEach(function(a) { if(iauthor.indexOf(a) > -1) return;
if(iauthor.indexOf(a) > -1) return; iauthor.push(a);
iauthor.push(a); o.push("<author>" + a + "</author>");
o.push("<author>" + a + "</author>"); }); });
});
});
o.push("</authors>"); o.push("</authors>");
o.push("<commentList>"); o.push("<commentList>");
data.forEach(function(d) { data.forEach(function(d) {
@ -14303,6 +14302,8 @@ function xlml_clean_comment(comment) {
function xlml_normalize(d) { function xlml_normalize(d) {
if(has_buf && Buffer.isBuffer(d)) return d.toString('utf8'); if(has_buf && Buffer.isBuffer(d)) return d.toString('utf8');
if(typeof d === 'string') return d; if(typeof d === 'string') return d;
/* duktape */
if(typeof Uint8Array !== 'undefined' && d instanceof Uint8Array) return utf8read(a2s(ab2a(d)));
throw new Error("Bad input format: expected Buffer or string"); throw new Error("Bad input format: expected Buffer or string");
} }
@ -19746,7 +19747,9 @@ if(has_buf && typeof require != 'undefined') (function() {
var rowinfo = o.skipHidden && sheet["!rows"] || []; var rowinfo = o.skipHidden && sheet["!rows"] || [];
for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C); for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C);
var R = r.s.r; var R = r.s.r;
var BOM = false;
stream._read = function() { stream._read = function() {
if(!BOM) { BOM = true; return stream.push("\uFEFF"); }
if(R > r.e.r) return stream.push(null); if(R > r.e.r) return stream.push(null);
while(R <= r.e.r) { while(R <= r.e.r) {
++R; ++R;