diff --git a/bits/45_rtf.js b/bits/45_rtf.js
index 74d70ba..048480e 100644
--- a/bits/45_rtf.js
+++ b/bits/45_rtf.js
@@ -3,20 +3,44 @@ var RTF = (function() {
 		switch(opts.type) {
 			case 'base64': return rtf_to_sheet_str(Base64.decode(d), opts);
 			case 'binary': return rtf_to_sheet_str(d, opts);
-			case 'buffer': return rtf_to_sheet_str(d.toString('binary'), opts);
+			case 'buffer': return rtf_to_sheet_str(has_buf && Buffer.isBuffer(d) ? d.toString('binary') : a2s(d), opts);
 			case 'array':  return rtf_to_sheet_str(cc2str(d), opts);
 		}
 		throw new Error("Unrecognized type " + opts.type);
 	}
 
+	/* TODO: this is a stub */
 	function rtf_to_sheet_str(str/*:string*/, opts)/*:Worksheet*/ {
 		var o = opts || {};
 		var ws/*:Worksheet*/ = o.dense ? ([]/*:any*/) : ({}/*:any*/);
-		var range/*:Range*/ = ({s: {c:0, r:0}, e: {c:0, r:0}}/*:any*/);
-
-		// TODO: parse
-		if(!str.match(/\\trowd/)) throw new Error("RTF missing table");
 
+		var rows = str.match(/\\trowd.*?\\row\b/g);
+		if(!rows.length) throw new Error("RTF missing table");
+		var range/*:Range*/ = ({s: {c:0, r:0}, e: {c:0, r:rows.length - 1}}/*:any*/);
+		rows.forEach(function(rowtf, R) {
+			if(Array.isArray(ws)) ws[R] = [];
+			var rtfre = /\\\w+\b/g;
+			var last_index = 0;
+			var res;
+			var C = -1;
+			while((res = rtfre.exec(rowtf))) {
+				switch(res[0]) {
+					case "\\cell":
+						var data = rowtf.slice(last_index, rtfre.lastIndex - res[0].length);
+						if(data[0] == " ") data = data.slice(1);
+						++C;
+						if(data.length) {
+							// TODO: value parsing, including codepage adjustments
+							var cell = {v: data, t:"s"};
+							if(Array.isArray(ws)) ws[R][C] = cell;
+							else ws[encode_cell({r:R, c:C})] = cell;
+						}
+						break;
+				}
+				last_index = rtfre.lastIndex;
+			}
+			if(C > range.e.c) range.e.c = C;
+		});
 		ws['!ref'] = encode_range(range);
 		return ws;
 	}
diff --git a/bits/81_writeods.js b/bits/81_writeods.js
index 773b761..5a846ff 100644
--- a/bits/81_writeods.js
+++ b/bits/81_writeods.js
@@ -242,7 +242,6 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() {
 function write_ods(wb/*:any*/, opts/*:any*/) {
 	if(opts.bookType == "fods") return write_content_ods(wb, opts);
 
-	/*:: if(!jszip) throw new Error("JSZip is not available"); */
 	var zip = zip_new();
 	var f = "";
 
diff --git a/bits/86_writezip.js b/bits/86_writezip.js
index 90a7653..4d7a839 100644
--- a/bits/86_writezip.js
+++ b/bits/86_writezip.js
@@ -18,7 +18,6 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
 	var vbafmt = VBAFMTS.indexOf(opts.bookType) > -1;
 	var ct = new_ct();
 	fix_write_opts(opts = opts || {});
-	/*:: if(!jszip) throw new Error("JSZip is not available"); */
 	var zip = zip_new();
 	var f = "", rId = 0;
 
diff --git a/bits/87_read.js b/bits/87_read.js
index e866cfd..c5af312 100644
--- a/bits/87_read.js
+++ b/bits/87_read.js
@@ -16,7 +16,6 @@ function read_cfb(cfb/*:CFBContainer*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
 }
 
 function read_zip(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
-	/*:: if(!jszip) throw new Error("JSZip is not available"); */
 	var zip, d = data;
 	var o = opts||{};
 	if(!o.type) o.type = (has_buf && Buffer.isBuffer(data)) ? "buffer" : "base64";
diff --git a/bits/99_footer.js b/bits/99_footer.js
index cbfa12d..b1da0fa 100644
--- a/bits/99_footer.js
+++ b/bits/99_footer.js
@@ -5,5 +5,7 @@ if(typeof exports !== 'undefined') make_xlsx_lib(exports);
 else if(typeof module !== 'undefined' && module.exports) make_xlsx_lib(module.exports);
 else if(typeof define === 'function' && define.amd) define('xlsx-dist', function() { if(!XLSX.version) make_xlsx_lib(XLSX); return XLSX; });
 else make_xlsx_lib(XLSX);
+/* NOTE: the following extra line is needed for "Lightning Locker Service" */
+if(typeof window !== 'undefined' && !window.XLSX) window.XLSX = XLSX;
 /*exported XLS, ODS */
 var XLS = XLSX, ODS = XLSX;