version bump 0.20.1

This commit is contained in:
SheetJS 2023-12-05 03:19:42 -05:00
parent 9199c2600c
commit 29d46c07a8
46 changed files with 1585 additions and 608 deletions

View File

@ -4,6 +4,18 @@ This log is intended to keep track of backwards-incompatible changes, including
but not limited to API changes and file location changes. Minor behavioral
changes may not be included if they are not expected to break existing code.
## v0.20.1
* `init` use packaged test files to work around GitHub breaking changes
* SSF date code rounding to 15 decimal digits (h/t @davidtamaki)
* `sheet_to_json` force UTC interpretation for formatted strings (h/t @Blanay)
* QPW extract result of string formula
* XLSX parse non-compliant merge cell expressions
* NUMBERS correctly handle rows omitted from official exports
* DBF parse empty logical field (h/t @Roman91)
* `dense` option added to types
* package.json add mini and core scripts to export map (h/t @stof)
## v0.20.0
* Use UTC interpretation of Date objects for date cells (potentially breaking)

View File

@ -67,10 +67,9 @@ clean-data:
.PHONY: init
init: ## Initial setup for development
git submodule init
git submodule update
#git submodule foreach git pull origin master
git submodule foreach make all
rm -rf test_files
if [ ! -e test_files.zip ]; then curl -LO https://test-files.sheetjs.com/test_files.zip; fi
unzip test_files.zip
mkdir -p tmp
DISTHDR=misc/suppress_export.js

View File

@ -9,29 +9,30 @@ Edit complex templates with ease; let out your inner Picasso with styling; make
custom sheets with images/graphs/PivotTables; evaluate formula expressions and
port calculations to web apps; automate common spreadsheet tasks, and much more!
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/sheetjs?pixel)](https://git.sheetjs.com/SheetJS/sheetjs)
[![Build Status](https://saucelabs.com/browser-matrix/sheetjs.svg)](https://saucelabs.com/u/sheetjs)
## Documentation
- [API and Usage Documentation](https://docs.sheetjs.com)
- [Downloadable Scripts and Modules](https://cdn.sheetjs.com)
## Related Projects
## Constellation
- <https://oss.sheetjs.com/notes/>: File Format Notes
- [`ssf`](packages/ssf): Format data using ECMA-376 spreadsheet format codes
- [`xlsx-cli`](packages/xlsx-cli/): NodeJS command-line tool for processing files
- [`xlsx-cli`](packages/xlsx-cli): NodeJS command-line tool for processing files
- [`test_files`](https://github.com/SheetJS/test_files): Sample spreadsheets
- [`cfb`](https://git.sheetjs.com/SheetJS/js-cfb): Container (OLE/ZIP) file
processing library
- [`cfb`](https://git.sheetjs.com/SheetJS/js-cfb): Container (OLE/ZIP) format library
- [`codepage`](https://git.sheetjs.com/SheetJS/js-codepage): Legacy text
encodings for XLS and other legacy spreadsheet formats
- [`codepage`](https://git.sheetjs.com/SheetJS/js-codepage): Legacy text encodings
- [`dta`](packages/dta): Stata DTA file processor
- [`test_files`](https://github.com/sheetjs/test_files): Test files and various
plaintext baselines.
## License

View File

@ -1 +1 @@
XLSX.version = '0.20.0';
XLSX.version = '0.20.1';

View File

@ -29,7 +29,7 @@ function make_json_row(sheet/*:Worksheet*/, r/*:Range*/, R/*:number*/, cols/*:Ar
v = numdate(v); // TODO: date1904 setting should also be stored in worksheet object
if(typeof v == "number") break;
/* falls through */
case 'd': if(!(o && o.UTC)) v = utc_to_local(v); break;
case 'd': if(!(o && (o.UTC||(o.raw === false)))) v = utc_to_local(new Date(v)); break;
default: throw new Error('unrecognized type ' + val.t);
}
if(hdr[C] != null) {

32
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

162
dist/xlsx.extendscript.js generated vendored
View File

@ -160,7 +160,7 @@ var DO_NOT_EXPORT_CODEPAGE = true;
/*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false, DataView:false, Deno:false, Float32Array:false */
var XLSX = {};
function make_xlsx_lib(XLSX){
XLSX.version = '0.20.0';
XLSX.version = '0.20.1';
var current_codepage = 1200, current_ansi = 1252;
/*global cptable:true, window */
var $cptable;
@ -620,8 +620,20 @@ function SSF_frac(x, D, mixed) {
var q = Math.floor(sgn * P/Q);
return [q, sgn*P - q*Q, Q];
}
function SSF_normalize_xl_unsafe(v) {
var s = v.toPrecision(16);
if(s.indexOf("e") > -1) {
var m = s.slice(0, s.indexOf("e"));
m = m.indexOf(".") > -1 ? m.slice(0, (m.slice(0,2) == "0." ? 17 : 16)) : (m.slice(0,15) + fill("0", m.length - 15));
return m + s.slice(s.indexOf("e"));
}
var n = s.indexOf(".") > -1 ? s.slice(0, (s.slice(0,2) == "0." ? 17 : 16)) : (s.slice(0,15) + fill("0", s.length - 15));
return Number(n);
}
function SSF_parse_date_code(v,opts,b2) {
if(v > 2958465 || v < 0) return null;
v = SSF_normalize_xl_unsafe(v);
var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0;
var dout=[];
var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0};
@ -774,7 +786,7 @@ if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100;
switch(fmt) {
case '[h]': case '[hh]': out = val.D*24+val.H; break;
case '[m]': case '[mm]': out = (val.D*24+val.H)*60+val.M; break;
case '[s]': case '[ss]': out = ((val.D*24+val.H)*60+val.M)*60+Math.round(val.S+val.u); break;
case '[s]': case '[ss]': out = ((val.D*24+val.H)*60+val.M)*60+(ss0 == 0 ? Math.round(val.S+val.u) : val.S); break;
default: throw 'bad abstime format: ' + fmt;
} outl = fmt.length === 3 ? 1 : 2; break;
case 101: /* 'e' era */
@ -1220,10 +1232,11 @@ function eval_fmt(fmt, v, opts, flen) {
switch(out[i].t) {
case 'h': case 'H': out[i].t = hr; lst='h'; if(bt < 1) bt = 1; break;
case 's':
if((ssm=out[i].v.match(/\.0+$/))) ss0=Math.max(ss0,ssm[0].length-1);
if((ssm=out[i].v.match(/\.0+$/))) { ss0=Math.max(ss0,ssm[0].length-1); bt = 4;}
if(bt < 3) bt = 3;
/* falls through */
case 'd': case 'y': case 'M': case 'e': lst=out[i].t; break;
case 'd': case 'y': case 'e': lst=out[i].t; break;
case 'M': lst=out[i].t; if(bt < 2) bt = 2; break;
case 'm': if(lst === 's') { out[i].t = 'M'; if(bt < 2) bt = 2; } break;
case 'X': /*if(out[i].v === "B2");*/
break;
@ -1233,17 +1246,29 @@ function eval_fmt(fmt, v, opts, flen) {
if(bt < 3 && out[i].v.match(/[Ss]/)) bt = 3;
}
}
/* time rounding depends on presence of minute / second / usec fields */
var _dt;
switch(bt) {
case 0: break;
case 1:
if(dt.u >= 0.5) { dt.u = 0; ++dt.S; }
case 2:
case 3:
if(dt.u >= 0.5) { dt.u = 0; ++dt.S; }
if(dt.S >= 60) { dt.S = 0; ++dt.M; }
if(dt.M >= 60) { dt.M = 0; ++dt.H; }
if(dt.H >= 24) { dt.H = 0; ++dt.D; _dt = SSF_parse_date_code(dt.D); _dt.u = dt.u; _dt.S = dt.S; _dt.M = dt.M; _dt.H = dt.H; dt = _dt; }
break;
case 2:
if(dt.u >= 0.5) { dt.u = 0; ++dt.S; }
case 4:
switch(ss0) {
case 1: dt.u = Math.round(dt.u * 10)/10; break;
case 2: dt.u = Math.round(dt.u * 100)/100; break;
case 3: dt.u = Math.round(dt.u * 1000)/1000; break;
}
if(dt.u >= 1) { dt.u = 0; ++dt.S; }
if(dt.S >= 60) { dt.S = 0; ++dt.M; }
if(dt.M >= 60) { dt.M = 0; ++dt.H; }
if(dt.H >= 24) { dt.H = 0; ++dt.D; _dt = SSF_parse_date_code(dt.D); _dt.u = dt.u; _dt.S = dt.S; _dt.M = dt.M; _dt.H = dt.H; dt = _dt; }
break;
}
@ -4503,6 +4528,13 @@ function sheet_to_workbook(sheet, opts) {
return { SheetNames: [n], Sheets: sheets };
}
function sheet_new(opts) {
var out = {};
var o = opts || {};
if(o.dense) out["!data"] = [];
return out;
}
function sheet_add_aoa(_ws, data, opts) {
var o = opts || {};
var dense = _ws ? (_ws["!data"] != null) : o.dense;
@ -8059,7 +8091,7 @@ var fields = [], field = ({});
case 'L': switch(s.trim().toUpperCase()) {
case 'Y': case 'T': out[R][C] = true; break;
case 'N': case 'F': out[R][C] = false; break;
case '': case '?': break;
case '': case '\x00': case '?': break;
default: throw new Error("DBF Unrecognized L:|" + s + "|");
} break;
case 'M': /* TODO: handle memo files */
@ -8121,6 +8153,7 @@ function dbf_to_workbook(buf, opts) {
var _RLEN = { 'B': 8, 'C': 250, 'L': 1, 'D': 8, '?': 0, '': 0 };
function sheet_to_dbf(ws, opts) {
if(!ws["!ref"]) throw new Error("Cannot export empty sheet to DBF");
var o = opts || {};
var old_cp = current_codepage;
if(+o.codepage >= 0) set_cp(+o.codepage);
@ -8278,7 +8311,10 @@ var SYLK = (function() {
"!":161, '"':162, "#":163, "(":164, "%":165, "'":167, "H ":168,
"+":171, ";":187, "<":188, "=":189, ">":190, "?":191, "{":223
});
var sylk_char_regex = new RegExp("\u001BN(" + keys(sylk_escapes).join("|").replace(/\|\|\|/, "|\\||").replace(/([?()+])/g,"\\$1") + "|\\|)", "gm");
var sylk_char_regex = new RegExp("\u001BN(" + keys(sylk_escapes).join("|").replace(/\|\|\|/, "|\\||").replace(/([?()+])/g,"\\$1").replace("{", "\\{") + "|\\|)", "gm");
try {
sylk_char_regex = new RegExp("\u001BN(" + keys(sylk_escapes).join("|").replace(/\|\|\|/, "|\\||").replace(/([?()+])/g,"\\$1") + "|\\|)", "gm");
} catch(e) {}
var sylk_char_fn = function(_, $1){ var o = sylk_escapes[$1]; return typeof o == "number" ? _getansi(o) : o; };
var decode_sylk_char = function($$, $1, $2) { var newcc = (($1.charCodeAt(0) - 0x20)<<4) | ($2.charCodeAt(0) - 0x30); return newcc == 59 ? $$ : _getansi(newcc); };
sylk_escapes["|"] = 254;
@ -8501,7 +8537,7 @@ var SYLK = (function() {
if(!opts) opts = {}; opts._formats = ["General"];
/* TODO: codepage */
var preamble = ["ID;PSheetJS;N;E"], o = [];
var r = safe_decode_range(ws['!ref']), cell;
var r = safe_decode_range(ws['!ref']||"A1"), cell;
var dense = ws["!data"] != null;
var RS = "\r\n";
var d1904 = (((wb||{}).Workbook||{}).WBProps||{}).date1904;
@ -8510,7 +8546,7 @@ var SYLK = (function() {
preamble.push("P;PGeneral");
/* Excel has been inconsistent in comment placement */
var R = r.s.r, C = r.s.c, p = [];
for(R = r.s.r; R <= r.e.r; ++R) {
if(ws["!ref"]) for(R = r.s.r; R <= r.e.r; ++R) {
if(dense && !ws["!data"][R]) continue;
p = [];
for(C = r.s.c; C <= r.e.c; ++C) {
@ -8520,7 +8556,7 @@ var SYLK = (function() {
}
if(p.length) o.push(p.join(RS));
}
for(R = r.s.r; R <= r.e.r; ++R) {
if(ws["!ref"]) for(R = r.s.r; R <= r.e.r; ++R) {
if(dense && !ws["!data"][R]) continue;
p = [];
for(C = r.s.c; C <= r.e.c; ++C) {
@ -8540,7 +8576,7 @@ var SYLK = (function() {
if(ws['!cols']) write_ws_cols_sylk(preamble, ws['!cols']);
if(ws['!rows']) write_ws_rows_sylk(preamble, ws['!rows']);
preamble.push("B;Y" + (r.e.r - r.s.r + 1) + ";X" + (r.e.c - r.s.c + 1) + ";D" + [r.s.c,r.s.r,r.e.c,r.e.r].join(" "));
if(ws["!ref"]) preamble.push("B;Y" + (r.e.r - r.s.r + 1) + ";X" + (r.e.c - r.s.c + 1) + ";D" + [r.s.c,r.s.r,r.e.c,r.e.r].join(" "));
preamble.push("O;L;D;B" + (d1904 ? ";V4" : "") + ";K47;G100 0.001");
delete opts._formats;
@ -8613,6 +8649,7 @@ var DIF = (function() {
function make_value_str(s) { return "1,0\r\n\"" + s.replace(/"/g,'""') + '"'; }
function sheet_to_dif(ws) {
var _DIF_XL = DIF_XL;
if(!ws["!ref"]) throw new Error("Cannot export empty sheet to DIF");
var r = safe_decode_range(ws['!ref']);
var dense = ws["!data"] != null;
var o = [
@ -8950,6 +8987,7 @@ var PRN = (function() {
function sheet_to_prn(ws) {
var o = [];
if(!ws["!ref"]) return "";
var r = safe_decode_range(ws['!ref']), cell;
var dense = ws["!data"] != null;
for(var R = r.s.r; R <= r.e.r; ++R) {
@ -9174,6 +9212,7 @@ var WK_ = (function() {
if(+o.codepage >= 0) set_cp(+o.codepage);
if(o.type == "string") throw new Error("Cannot write WK1 to JS string");
var ba = buf_array();
if(!ws["!ref"]) throw new Error("Cannot export empty sheet to WK1");
var range = safe_decode_range(ws["!ref"]);
var dense = ws["!data"] != null;
var cols = [];
@ -9967,13 +10006,25 @@ var WK_ = (function() {
0x0E: "dd-mmm-yyyy",
0x0F: "mmm-yyyy",
0x22: "0.00",
0x32: "0.00;[Red]0.00",
0x42: "0.00;\(0.00\)",
0x52: "0.00;[Red]\(0.00\)",
162: '"$"#,##0;\\("$"#,##0\\)' // slightly different from SSF 5
/* It is suspected that the the low nybble specifies decimal places */
0x0022: "0.00",
0x0032: "0.00;[Red]0.00",
0x0042: "0.00;\(0.00\)",
0x0052: "0.00;[Red]\(0.00\)",
0x00A2: '"$"#,##0.00;\\("$"#,##0.00\\)',
0x0120: '0%',
0x0130: '0E+00',
0x0140: '# ?/?'
};
function parse_qpw_str(p) {
var cch = p.read_shift(2);
var flags = p.read_shift(1);
/* TODO: find examples with nonzero flags */
if(flags != 0) throw "unsupported QPW string type " + flags.toString(16);
return p.read_shift(cch, "sbcs-cont");
}
/* QPW uses a different set of record types */
function qpw_to_workbook_buf(d, opts) {
prep_blob(d, 0);
@ -10084,7 +10135,11 @@ var WK_ = (function() {
case 4: cell = { t: "n", v: parse_RkNumber(p) }; break;
case 5: cell = { t: "n", v: p.read_shift(8, 'f') }; break;
case 7: cell = { t: "s", v: SST[type = p.read_shift(4) - 1] }; break;
case 8: cell = { t: "n", v: p.read_shift(8, 'f') }; p.l += 2; /* cell.f = formulae[p.read_shift(4)]; */ p.l += 4; break;
case 8:
cell = { t: "n", v: p.read_shift(8, 'f') };
p.l += 2; /* cell.f = formulae[p.read_shift(4)]; */ p.l += 4;
if(isNaN(cell.v)) cell = { t: "e", v: 0x0F }; // #VALUE!
break;
default: throw "Unrecognized QPW cell type " + (flags & 0x1F);
}
if(fmtidx != -1 && (FMTS[fmtidx - 1]||{}).z) cell.z = FMTS[fmtidx-1].z;
@ -10130,6 +10185,17 @@ var WK_ = (function() {
}
} break;
case 0x0C02: { /* String (result of string formula expression) */
C = p.read_shift(2);
R = p.read_shift(4);
var str = parse_qpw_str(p);
/* TODO: QP10 record has an additional unknown character after the string */
if(s["!data"] != null) {
if(!s["!data"][R]) s["!data"][R] = [];
s["!data"][R][C] = { t:"s", v:str };
} else s[encode_col(C) + encode_row(R)] = { t:"s", v:str };
} break;
default: break;
}
d.l += length;
@ -11281,7 +11347,7 @@ function parse_cellXfs(t, styles, opts) {
case '<cellXfs': case '<cellXfs>': case '<cellXfs/>': case '</cellXfs>': break;
/* 18.8.45 xf CT_Xf */
case '<xf': case '<xf/>':
case '<xf': case '<xf/>': case '<xf>':
xf = y;
delete xf[0];
for(i = 0; i < cellXF_uint.length; ++i) if(xf[cellXF_uint[i]])
@ -11295,7 +11361,7 @@ function parse_cellXfs(t, styles, opts) {
case '</xf>': break;
/* 18.8.1 alignment CT_CellAlignment */
case '<alignment': case '<alignment/>':
case '<alignment': case '<alignment/>': case '<alignment>':
var alignment = {};
if(y.vertical) alignment.vertical = y.vertical;
if(y.horizontal) alignment.horizontal = y.horizontal;
@ -11307,12 +11373,12 @@ function parse_cellXfs(t, styles, opts) {
case '</alignment>': break;
/* 18.8.33 protection CT_CellProtection */
case '<protection':
case '<protection': case '<protection>':
break;
case '</protection>': case '<protection/>': break;
/* note: sometimes mc:AlternateContent appears bare */
case '<AlternateContent': pass = true; break;
case '<AlternateContent': case '<AlternateContent>': pass = true; break;
case '</AlternateContent>': pass = false; break;
/* 18.2.10 extLst CT_ExtensionList ? */
@ -12951,7 +13017,10 @@ var rc_to_a1 = (function(){
};
})();
var crefregex = /(^|[^._A-Z0-9])([$]?)([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])([$]?)(10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5})(?![_.\(A-Za-z0-9])/g;
var crefregex = /(^|[^._A-Z0-9])(\$?)([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])(\$?)(\d{1,7})(?![_.\(A-Za-z0-9])/g;
try {
crefregex = /(^|[^._A-Z0-9])([$]?)([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])([$]?)(10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5})(?![_.\(A-Za-z0-9])/g;
}catch(e){}
var a1_to_rc = (function(){
return function a1_to_rc(fstr, base) {
return fstr.replace(crefregex, function($0, $1, $2, $3, $4, $5) {
@ -15664,7 +15733,7 @@ function parse_ws_xml_dim(ws, s) {
var d = safe_decode_range(s);
if(d.s.r<=d.e.r && d.s.c<=d.e.c && d.s.r>=0 && d.s.c>=0) ws["!ref"] = encode_range(d);
}
var mergecregex = /<(?:\w:)?mergeCell ref="[A-Z0-9:]+"\s*[\/]?>/g;
var mergecregex = /<(?:\w:)?mergeCell ref=["'][A-Z0-9:]+['"]\s*[\/]?>/g;
var sheetdataregex = /<(?:\w+:)?sheetData[^>]*>([\s\S]*)<\/(?:\w+:)?sheetData>/;
var hlinkregex = /<(?:\w:)?hyperlink [^>]*>/mg;
var dimregex = /"(\w*:\w*)"/;
@ -22507,9 +22576,9 @@ function sheet_to_html(ws, opts/*, wb:?Workbook*/) {
var header = o.header != null ? o.header : HTML_BEGIN;
var footer = o.footer != null ? o.footer : HTML_END;
var out = [header];
var r = decode_range(ws['!ref']);
var r = decode_range(ws['!ref'] || "A1");
out.push(make_html_preamble(ws, r, o));
for(var R = r.s.r; R <= r.e.r; ++R) out.push(make_html_row(ws, r, R, o));
if(ws["!ref"]) for(var R = r.s.r; R <= r.e.r; ++R) out.push(make_html_row(ws, r, R, o));
out.push("</table>" + footer);
return out.join("");
}
@ -23268,10 +23337,13 @@ function parse_content_xml(d, _opts, _nfm) {
case 'help-message': break; // 9.4.6 <table:
case 'error-message': break; // 9.4.7 <table:
case 'database-ranges': break; // 9.4.14 <table:database-ranges>
/* 9.5 Filters */
case 'filter': break; // 9.5.2 <table:filter>
case 'filter-and': break; // 9.5.3 <table:filter-and>
case 'filter-or': break; // 9.5.4 <table:filter-or>
case 'filter-condition': break; // 9.5.5 <table:filter-condition>
case 'filter-set-item': break; // 9.5.6 <table:filter-condition>
case 'list-level-style-bullet': break; // 16.31 <text:
case 'list-level-style-number': break; // 16.32 <text:
@ -23788,6 +23860,7 @@ var write_content_ods = /* @__PURE__ */(function() {
wb.SheetNames.map(function(n) { return wb.Sheets[n]; }).forEach(function(ws) {
if(!ws) return;
var dense = (ws["!data"] != null);
if(!ws["!ref"]) return;
var range = decode_range(ws["!ref"]);
for(var R = 0; R <= range.e.r; ++R) for(var C = 0; C <= range.e.c; ++C) {
var c = dense ? (ws["!data"][R]||[])[C] : ws[encode_cell({r:R,c:C})];
@ -25039,7 +25112,7 @@ function s5s_to_iwa_comment(s5s) {
return out;
}
function parse_TST_TableModelArchive(M, root, ws, opts) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
var pb = parse_shallow(root.data);
var range = { s: { r: 0, c: 0 }, e: { r: 0, c: 0 } };
range.e.r = (varint_to_i32(pb[6][0].data) >>> 0) - 1;
@ -25068,7 +25141,17 @@ function parse_TST_TableModelArchive(M, root, ws, opts) {
lut.nfmt = parse_TST_TableDataList(M, M[parse_TSP_Reference(store[22][0].data)][0]);
var tile = parse_shallow(store[3][0].data);
var _R = 0;
tile[1].forEach(function(t) {
if (!((_h = store[9]) == null ? void 0 : _h[0]))
throw "NUMBERS file missing row tree";
var rtt = parse_shallow(store[9][0].data)[1].map(function(p) {
return parse_shallow(p.data);
});
rtt.forEach(function(kv) {
_R = varint_to_i32(kv[1][0].data);
var tidx = varint_to_i32(kv[2][0].data);
var t = tile[1][tidx];
if (!t)
throw "NUMBERS missing tile " + tidx;
var tl = parse_shallow(t.data);
var ref2 = M[parse_TSP_Reference(tl[2][0].data)][0];
var mtype2 = varint_to_i32(ref2.meta[1][0].data);
@ -25091,12 +25174,12 @@ function parse_TST_TableModelArchive(M, root, ws, opts) {
});
_R += _tile.nrows;
});
if ((_h = store[13]) == null ? void 0 : _h[0]) {
if ((_i = store[13]) == null ? void 0 : _i[0]) {
var ref = M[parse_TSP_Reference(store[13][0].data)][0];
var mtype = varint_to_i32(ref.meta[1][0].data);
if (mtype != 6144)
throw new Error("Expected merge type 6144, found ".concat(mtype));
ws["!merges"] = (_i = parse_shallow(ref.data)) == null ? void 0 : _i[1].map(function(pi) {
ws["!merges"] = (_j = parse_shallow(ref.data)) == null ? void 0 : _j[1].map(function(pi) {
var merge = parse_shallow(pi.data);
var origin = u8_to_dataview(parse_shallow(merge[1][0].data)[1][0].data), size = u8_to_dataview(parse_shallow(merge[2][0].data)[1][0].data);
return {
@ -25758,6 +25841,8 @@ function write_numbers_ws(cfb, deps, ws, wsname, sheetidx, rootref) {
}
var USE_WIDE_ROWS = true;
function write_numbers_tma(cfb, deps, ws, tmaroot, tmafile, tmaref) {
if (!ws["!ref"])
throw new Error("Cannot export empty sheet to NUMBERS");
var range = decode_range(ws["!ref"]);
range.s.r = range.s.c = 0;
var trunc = false;
@ -27192,7 +27277,7 @@ function make_json_row(sheet, r, R, cols, header, hdr, o) {
v = numdate(v); // TODO: date1904 setting should also be stored in worksheet object
if(typeof v == "number") break;
/* falls through */
case 'd': if(!(o && o.UTC)) v = utc_to_local(v); break;
case 'd': if(!(o && (o.UTC||(o.raw === false)))) v = utc_to_local(new Date(v)); break;
default: throw new Error('unrecognized type ' + val.t);
}
if(hdr[C] != null) {
@ -27202,7 +27287,7 @@ function make_json_row(sheet, r, R, cols, header, hdr, o) {
else if(raw && v === null) row[hdr[C]] = null;
else continue;
} else {
row[hdr[C]] = raw && (val.t !== "n" || (val.t === "n" && o.rawNumbers !== false)) ? v : format_cell(val,v,o);
row[hdr[C]] = (val.t === 'n' && typeof o.rawNumbers === 'boolean' ? o.rawNumbers : raw) ? v : format_cell(val, v, o);
}
if(v != null) isempty = false;
}
@ -27460,9 +27545,11 @@ function wb_sheet_idx(wb, sh) {
} else throw new Error("Cannot find sheet |" + sh + "|");
}
/* simple blank workbook object */
function book_new() {
return { SheetNames: [], Sheets: {} };
/* simple blank or single-sheet workbook object */
function book_new(ws, wsname) {
var wb = { SheetNames: [], Sheets: {} };
if(ws) book_append_sheet(wb, ws, wsname || "Sheet1");
return wb;
}
/* add a worksheet to the end of a given workbook */
@ -27559,6 +27646,7 @@ var utils = {
decode_cell: decode_cell,
decode_range: decode_range,
format_cell: format_cell,
sheet_new: sheet_new,
sheet_add_aoa: sheet_add_aoa,
sheet_add_json: sheet_add_json,
sheet_add_dom: sheet_add_dom,

34
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

18
dist/xlsx.mini.min.js generated vendored

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"name": "xlsx",
"version": "0.20.0+csv",
"version": "0.20.1",
"author": "sheetjs",
"description": "SheetJS Spreadsheet data parser and writer",
"keywords": [
@ -167,6 +167,12 @@
]
},
"homepage": "https://sheetjs.com/",
"files": [
"CHANGELOG.md", "LICENSE", "README.md", "bower.json", "package.json", "xlsx.js", "xlsx.mjs", "xlsxworker.js",
"bin/xlsx.njs",
"dist/LICENSE", "dist/*.mjs", "dist/*.js", "dist/*.map", "dist/*.d.ts",
"types/index.d.ts", "types/tsconfig.json"
],
"bugs": {
"url": "https://git.sheetjs.com/SheetJS/sheetjs/issues"
},

1
packages/dta/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
misc/

View File

@ -1,5 +1,15 @@
.PHONY: build
build: node browser
build: node browser types
.PHONY: clean
clean:
rm dist/dta.*
## Types
.PHONY: types
types: dta.ts
tsc -d --emitDeclarationOnly --declarationDir types $<
mv types/dta.d.ts types/index.d.ts
## NodeJS target

View File

@ -6,4 +6,58 @@ compatible with the [SheetJS](https://sheetjs.com) library constellation.
DTA datasets can support millions of observations and over 32767 variables.
The codec will truncate data to 1048576 observations and 16384 variables.
<https://docs.sheetjs.com/docs/constellation/dta> includes a live demo.
<https://docs.sheetjs.com/docs/constellation/dta> includes a live demo.
## Installation
Using NodeJS package manager:
```bash
npm install --save https://cdn.sheetjs.com/dta-0.0.1/dta-0.0.1.tgz
```
The standalone script is also hosted on the SheetJS CDN:
```html
<script src="https://cdn.sheetjs.com/dta-0.0.1/package/dist/dta.min.js"></script>
```
## Usage
The `parse` method accepts a `Uint8Array` representing the file data. It returns
a ["Common Spreadsheet Format"](https://docs.sheetjs.com/docs/csf/) workbook
object.
The `set_utils` method accepts a `utils` object from SheetJS CE or a SheetJS
Pro build. `parse` will use methods from the `utils` object.
### NodeJS
```js
const XLSX = require("xlsx"), DTA = require("dta");
DTA.set_utils(XLSX.utils);
const wb = DTA.parse(fs.readFileSync("auto.dta"));
```
### Browser
`dist/dta.min.js` is a standalone build designed to be added with `<script>`.
```html
<script lang="javascript" src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
<script src="dist/dta.min.js"></script>
<div id="out"></div>
<script>
DTA.set_utils(XLSX.utils);
(async() => {
/* fetch file */
const data = await (await fetch("test.dta")).arrayBuffer();
/* parse */
const wb = DTA.parse(new Uint8Array(data));
/* wb is a SheetJS workbook object */
const html = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
out.innerHTML = html;
})();
</script>
```

View File

@ -15,5 +15,5 @@ const fs = require("fs");
const buf = fs.readFileSync(process.argv[2]);
const wb = DTA.parse(buf);
// translate stub cells to single blanks
wb.Sheets[wb.SheetNames[0]]["!data"].forEach(row => row.forEach(cell => {if(cell.t == "z") {cell.t = "s"; cell.v = " ";}}));
//wb.Sheets[wb.SheetNames[0]]["!data"].forEach(row => row.forEach(cell => {if(cell.t == "z") {cell.t = "s"; cell.v = " ";}}));
console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));

View File

@ -25,41 +25,95 @@ var __toCommonJS = /* @__PURE__ */ ((cache) => {
var dta_exports = {};
__export(dta_exports, {
parse: () => parse,
set_utils: () => set_utils
set_utils: () => set_utils,
version: () => version
});
var version = "0.0.1";
var _utils;
function set_utils(utils) {
_utils = utils;
}
function u8_to_str(u8) {
return new TextDecoder().decode(u8);
}
function u8_to_latin1(u8) {
return new TextDecoder("latin1").decode(u8);
}
function format_number_dta(value, format, t) {
if (value < 0) {
const res = format_number_dta(-value, format, t);
res.w = "-" + res.w;
return res;
}
const o = { t: "n", v: value };
switch (t) {
case 251:
case 98:
case 65530:
format = "%8.0g";
break;
case 252:
case 105:
case 65529:
format = "%8.0g";
break;
case 253:
case 108:
case 65528:
format = "%12.0g";
break;
case 254:
case 102:
case 65527:
format = "%9.0g";
break;
case 255:
case 100:
case 65526:
format = "%10.0g";
break;
default:
throw t;
}
try {
let w = +(format.match(/%(\d+)/) || [])[1] || 8;
let k = 0;
if (value < 1)
++k;
if (value < 0.1)
++k;
if (value < 0.01)
++k;
if (value < 1e-3)
++k;
const e = value.toExponential();
const exp = e.indexOf("e") == -1 ? 0 : +e.slice(e.indexOf("e") + 1);
let h = w - 2 - exp;
if (h < 0)
h = 0;
var m = format.match(/%\d+\.(\d+)/);
if (m && +m[1])
h = +m[1];
o.w = (Math.round(value * 10 ** h) / 10 ** h).toFixed(h).replace(/^([-]?)0\./, "$1.");
o.w = o.w.slice(0, w + k);
if (o.w.indexOf(".") > -1)
o.w = o.w.replace(/0+$/, "");
o.w = o.w.replace(/\.$/, "");
if (o.w == "")
o.w = "0";
} catch (e) {
}
return o;
}
function u8_to_dataview(array) {
return new DataView(array.buffer, array.byteOffset, array.byteLength);
}
function valid_inc(p, n) {
if (p.str.slice(p.ptr, p.ptr + n.length) != n)
if (u8_to_str(p.raw.slice(p.ptr, p.ptr + n.length)) != n)
return false;
p.ptr += n.length;
return true;
}
function skip_end(p, n) {
const idx = p.str.indexOf(n, p.ptr);
if (idx == -1)
throw new Error(`Expected ${n} after offset ${p.ptr}`);
p.ptr = idx + n.length;
}
function slice_end(p, n) {
const idx = p.str.indexOf(n, p.ptr);
if (idx == -1)
throw new Error(`Expected ${n} after offset ${p.ptr}`);
const raw = p.raw.slice(p.ptr, idx);
const res = {
ptr: 0,
raw,
str: p.str.slice(p.ptr, idx),
dv: u8_to_dataview(raw)
};
p.ptr = idx + n.length;
return res;
}
function read_f64(p, LE) {
p.ptr += 8;
const d = p.dv.getFloat64(p.ptr - 8, LE);
@ -98,15 +152,29 @@ function read_i8(p) {
}
var SUPPORTED_VERSIONS_TAGGED = [
"117",
"118"
"118",
"119",
"120",
"121"
];
var SUPPORTED_VERSIONS_LEGACY = [
102,
103,
104,
105,
108,
110,
111,
112,
113,
114,
115
];
function parse_tagged(raw) {
const err = "Not a DTA file";
const str = new TextDecoder("latin1").decode(raw);
const d = {
ptr: 0,
raw,
str,
dv: u8_to_dataview(raw)
};
let vers = 118;
@ -124,16 +192,22 @@ function parse_tagged(raw) {
{
if (!valid_inc(d, "<release>"))
throw err;
const res = slice_end(d, "</release>");
if (SUPPORTED_VERSIONS_TAGGED.indexOf(res.str) == -1)
throw `Unsupported DTA ${res.str} file`;
vers = +res.str;
const res = u8_to_latin1(d.raw.slice(d.ptr, d.ptr + 3));
d.ptr += 3;
if (!valid_inc(d, "</release>"))
throw err;
if (SUPPORTED_VERSIONS_TAGGED.indexOf(res) == -1)
throw `Unsupported DTA ${res} file`;
vers = +res;
}
{
if (!valid_inc(d, "<byteorder>"))
throw err;
const res = slice_end(d, "</byteorder>");
switch (res.str) {
const res = u8_to_latin1(d.raw.slice(d.ptr, d.ptr + 3));
d.ptr += 3;
if (!valid_inc(d, "</byteorder>"))
throw err;
switch (res) {
case "MSF":
LE = false;
break;
@ -141,48 +215,49 @@ function parse_tagged(raw) {
LE = true;
break;
default:
throw `Unsupported byteorder ${res.str}`;
throw `Unsupported byteorder ${res}`;
}
}
{
if (!valid_inc(d, "<K>"))
throw err;
const res = slice_end(d, "</K>");
nvar = read_u16(res, LE);
nvar = vers === 119 || vers >= 121 ? read_u32(d, LE) : read_u16(d, LE);
if (!valid_inc(d, "</K>"))
throw err;
}
{
if (!valid_inc(d, "<N>"))
throw err;
const res = slice_end(d, "</N>");
if (vers == 117)
nobs = nobs_lo = read_u32(res, LE);
nobs = nobs_lo = read_u32(d, LE);
else {
const lo = read_u32(res, LE), hi = read_u32(res, LE);
const lo = read_u32(d, LE), hi = read_u32(d, LE);
nobs = LE ? (nobs_lo = lo) + (nobs_hi = hi) * Math.pow(2, 32) : (nobs_lo = hi) + (nobs_hi = lo) * Math.pow(2, 32);
}
if (nobs > 1e6)
console.error(`More than 1 million observations -- extra rows will be dropped`);
if (!valid_inc(d, "</N>"))
throw err;
}
{
if (!valid_inc(d, "<label>"))
throw err;
const res = slice_end(d, "</label>");
const w = vers >= 118 ? 2 : 1;
const strlen = w == 1 ? read_u8(res) : read_u16(res, LE);
if (strlen + w != res.str.length)
throw `Expected string length ${strlen} but actual length was ${res.str.length - w}`;
const strlen = w == 1 ? read_u8(d) : read_u16(d, LE);
if (strlen > 0)
label = new TextDecoder().decode(res.raw.slice(w));
label = u8_to_str(d.raw.slice(d.ptr, d.ptr + w));
d.ptr += strlen;
if (!valid_inc(d, "</label>"))
throw err;
}
{
if (!valid_inc(d, "<timestamp>"))
throw err;
const res = slice_end(d, "</timestamp>");
const strlen = read_u8(res);
if (strlen + 1 != res.str.length)
throw `Expected string length ${strlen} but actual length was ${res.str.length - 1}`;
if (strlen > 0)
timestamp = res.str.slice(1);
const strlen = read_u8(d);
timestamp = u8_to_latin1(d.raw.slice(d.ptr, d.ptr + strlen));
d.ptr += strlen;
if (!valid_inc(d, "</timestamp>"))
throw err;
}
if (!valid_inc(d, "</header>"))
throw err;
@ -190,17 +265,16 @@ function parse_tagged(raw) {
{
if (!valid_inc(d, "<map>"))
throw err;
skip_end(d, "</map>");
d.ptr += 8 * 14;
if (!valid_inc(d, "</map>"))
throw err;
}
let stride = 0;
{
if (!valid_inc(d, "<variable_types>"))
throw err;
const res = slice_end(d, "</variable_types>");
if (res.raw.length != 2 * nvar)
throw `Expected variable_types length ${nvar * 2}, found ${res.raw.length}`;
while (res.ptr < res.raw.length) {
const type = read_u16(res, LE);
for (var i = 0; i < nvar; ++i) {
const type = read_u16(d, LE);
var_types.push(type);
if (type >= 1 && type <= 2045)
stride += type;
@ -209,6 +283,9 @@ function parse_tagged(raw) {
case 32768:
stride += 8;
break;
case 65525:
stride += 0;
break;
case 65526:
stride += 8;
break;
@ -228,57 +305,62 @@ function parse_tagged(raw) {
throw `Unsupported field type ${type}`;
}
}
if (!valid_inc(d, "</variable_types>"))
throw err;
}
{
if (!valid_inc(d, "<varnames>"))
throw err;
const res = slice_end(d, "</varnames>");
const w = vers >= 118 ? 129 : 33;
if (res.raw.length != w * nvar)
throw `Expected variable_types length ${nvar * w}, found ${res.raw.length}`;
while (res.ptr < res.raw.length) {
const name = new TextDecoder().decode(res.raw.slice(res.ptr, res.ptr + w));
res.ptr += w;
for (let i2 = 0; i2 < nvar; ++i2) {
const name = u8_to_str(d.raw.slice(d.ptr, d.ptr + w));
d.ptr += w;
var_names.push(name.replace(/\x00[\s\S]*/, ""));
}
if (!valid_inc(d, "</varnames>"))
throw err;
}
{
if (!valid_inc(d, "<sortlist>"))
throw err;
const res = slice_end(d, "</sortlist>");
if (res.raw.length != 2 * nvar + 2)
throw `Expected sortlist length ${nvar * 2 + 2}, found ${res.raw.length}`;
d.ptr += (2 * nvar + 2) * (vers == 119 || vers == 121 ? 2 : 1);
if (!valid_inc(d, "</sortlist>"))
throw err;
}
{
if (!valid_inc(d, "<formats>"))
throw err;
const res = slice_end(d, "</formats>");
const w = vers >= 118 ? 57 : 49;
if (res.raw.length != w * nvar)
throw `Expected formats length ${nvar * w}, found ${res.raw.length}`;
while (res.ptr < res.raw.length) {
const name = new TextDecoder().decode(res.raw.slice(res.ptr, res.ptr + w));
res.ptr += w;
for (let i2 = 0; i2 < nvar; ++i2) {
const name = u8_to_str(d.raw.slice(d.ptr, d.ptr + w));
d.ptr += w;
formats.push(name.replace(/\x00[\s\S]*/, ""));
}
if (!valid_inc(d, "</formats>"))
throw err;
}
const value_label_names = [];
{
if (!valid_inc(d, "<value_label_names>"))
throw err;
const w = vers >= 118 ? 129 : 33;
const res = slice_end(d, "</value_label_names>");
for (let i2 = 0; i2 < nvar; ++i2, d.ptr += w)
value_label_names[i2] = u8_to_latin1(d.raw.slice(d.ptr, d.ptr + w)).replace(/\x00.*$/, "");
if (!valid_inc(d, "</value_label_names>"))
throw err;
}
{
if (!valid_inc(d, "<variable_labels>"))
throw err;
const w = vers >= 118 ? 321 : 81;
const res = slice_end(d, "</variable_labels>");
d.ptr += w * nvar;
if (!valid_inc(d, "</variable_labels>"))
throw err;
}
{
if (!valid_inc(d, "<characteristics>"))
throw err;
while (d.str.slice(d.ptr, d.ptr + 4) == "<ch>") {
d.ptr += 4;
while (valid_inc(d, "<ch>")) {
const len = read_u32(d, LE);
d.ptr += len;
if (!valid_inc(d, "</ch>"))
@ -297,26 +379,29 @@ function parse_tagged(raw) {
for (let C = 0; C < nvar; ++C) {
let t = var_types[C];
if (t >= 1 && t <= 2045) {
let s = new TextDecoder().decode(d.raw.slice(d.ptr, d.ptr + t));
let s = u8_to_str(d.raw.slice(d.ptr, d.ptr + t));
s = s.replace(/\x00[\s\S]*/, "");
row[C] = s;
d.ptr += t;
} else
switch (t) {
case 65526:
row[C] = read_f64(d, LE);
case 65525:
d.ptr += 0;
break;
case 65527:
row[C] = read_f32(d, LE);
break;
case 65528:
row[C] = read_i32(d, LE);
case 65530:
row[C] = read_i8(d);
break;
case 65529:
row[C] = read_i16(d, LE);
break;
case 65530:
row[C] = read_i8(d);
case 65528:
row[C] = read_i32(d, LE);
break;
case 65527:
row[C] = read_f32(d, LE);
break;
case 65526:
row[C] = read_f64(d, LE);
break;
case 32768:
{
@ -328,6 +413,8 @@ function parse_tagged(raw) {
default:
throw `Unsupported field type ${t} for ${var_names[C]}`;
}
if (typeof row[C] == "number" && formats[C])
row[C] = format_number_dta(row[C], formats[C], t);
}
_utils.sheet_add_aoa(ws, [row], { origin: -1, sheetStubs: true });
}
@ -355,15 +442,15 @@ function parse_tagged(raw) {
const len = read_u32(d, LE);
if (!strl_tbl[o])
strl_tbl[o] = [];
let str2 = "";
let str = "";
if (t == 129) {
str2 = new TextDecoder("latin1").decode(d.raw.slice(d.ptr, d.ptr + len));
str = new TextDecoder(vers >= 118 ? "utf8" : "latin1").decode(d.raw.slice(d.ptr, d.ptr + len));
d.ptr += len;
} else {
str2 = new TextDecoder("latin1").decode(d.raw.slice(d.ptr, d.ptr + len)).replace(/\x00$/, "");
str = new TextDecoder(vers >= 118 ? "utf8" : "latin1").decode(d.raw.slice(d.ptr, d.ptr + len)).replace(/\x00$/, "");
d.ptr += len;
}
strl_tbl[o][v] = str2;
strl_tbl[o][v] = str;
}
if (!valid_inc(d, "</strls>"))
throw err;
@ -397,9 +484,41 @@ function parse_tagged(raw) {
});
}
{
const w = vers >= 118 ? 129 : 33;
if (!valid_inc(d, "<value_labels>"))
throw err;
const res = slice_end(d, "</value_labels>");
while (valid_inc(d, "<lbl>")) {
let len = read_u32(d, LE);
const labname = u8_to_latin1(d.raw.slice(d.ptr, d.ptr + w)).replace(/\x00.*$/, "");
d.ptr += w;
d.ptr += 3;
const labels = [];
{
const n = read_u32(d, LE);
const txtlen = read_u32(d, LE);
const off = [], val = [];
for (let i2 = 0; i2 < n; ++i2)
off.push(read_u32(d, LE));
for (let i2 = 0; i2 < n; ++i2)
val.push(read_u32(d, LE));
const str = u8_to_str(d.raw.slice(d.ptr, d.ptr + txtlen));
d.ptr += txtlen;
for (let i2 = 0; i2 < n; ++i2)
labels[val[i2]] = str.slice(off[i2], str.indexOf("\0", off[i2]));
}
const C = value_label_names.indexOf(labname);
if (C == -1)
throw new Error(`unexpected value label |${labname}|`);
for (let R = 1; R < ws["!data"].length; ++R) {
const cell = ws["!data"][R][C];
cell.t = "s";
cell.v = cell.w = labels[cell.v || 0];
}
if (!valid_inc(d, "</lbl>"))
throw err;
}
if (!valid_inc(d, "</value_labels>"))
throw err;
}
if (!valid_inc(d, "</stata_dta>"))
throw err;
@ -409,27 +528,11 @@ function parse_tagged(raw) {
}
function parse_legacy(raw) {
let vers = raw[0];
switch (vers) {
case 102:
case 112:
throw `Unsupported DTA ${vers} file`;
case 103:
case 104:
case 105:
case 108:
case 110:
case 111:
case 113:
case 114:
case 115:
break;
default:
throw new Error("Not a DTA file");
}
if (SUPPORTED_VERSIONS_LEGACY.indexOf(vers) == -1)
throw new Error("Not a DTA file");
const d = {
ptr: 1,
raw,
str: "",
dv: u8_to_dataview(raw)
};
let LE = true;
@ -456,31 +559,34 @@ function parse_legacy(raw) {
d.ptr++;
nvar = read_u16(d, LE);
nobs = read_u32(d, LE);
d.ptr += vers >= 108 ? 81 : 32;
d.ptr += vers >= 108 ? 81 : vers >= 103 ? 32 : 30;
if (vers >= 105)
d.ptr += 18;
}
const value_label_names = [];
{
let C = 0;
for (C = 0; C < nvar; ++C)
var_types.push(read_u8(d));
const w = vers >= 110 ? 33 : 9;
for (C = 0; C < nvar; ++C) {
var_names.push(new TextDecoder().decode(d.raw.slice(d.ptr, d.ptr + w)).replace(/\x00[\s\S]*$/, ""));
var_names.push(u8_to_str(d.raw.slice(d.ptr, d.ptr + w)).replace(/\x00[\s\S]*$/, ""));
d.ptr += w;
}
d.ptr += 2 * (nvar + 1);
const fw = vers >= 114 ? 49 : vers >= 105 ? 12 : 7;
for (C = 0; C < nvar; ++C) {