forked from sheetjs/sheetjs
Compare commits
76 Commits
ivan-truso
...
master
Author | SHA1 | Date | |
---|---|---|---|
29d46c07a8 | |||
9199c2600c | |||
cd5fafda32 | |||
|
c8e6d5c20f | ||
ad0fb7766b | |||
4cc980975b | |||
432ac1fda7 | |||
53283217e9 | |||
248108b667 | |||
|
766fc4f4d2 | ||
485a4f30f1 | |||
68cce77d23 | |||
c0b9e229a0 | |||
955543147d | |||
36c5b7c0f5 | |||
6e260c9185 | |||
d3a480750b | |||
5b33acfaf4 | |||
b68eaed726 | |||
333e4e40f9 | |||
af8e3d9171 | |||
|
dca90c8024 | ||
c06a32043f | |||
ffbea71bc2 | |||
16dd8a6eae | |||
0577bb7a45 | |||
b150dea21d | |||
e9cf1ad0fb | |||
5141222c24 | |||
51a8619000 | |||
e7e129e417 | |||
df48489211 | |||
050f66ce1b | |||
2f329b64e2 | |||
515d1c6f2e | |||
654d6f98c3 | |||
4ae4f0fad9 | |||
|
1ca49a50bd | ||
81b231d866 | |||
1491302aa4 | |||
bd5878e7c7 | |||
|
4dd092a076 | ||
|
04dc18e742 | ||
6a5be04e3d | |||
e90a61bf09 | |||
26cbfe37be | |||
df0e7b5f25 | |||
6c9010f9d1 | |||
0a6ddcaf44 | |||
|
0de9479053 | ||
045adba80d | |||
|
199373e918 | ||
917a69e394 | |||
838ee58a49 | |||
|
6bea47aaef | ||
|
aea2157036 | ||
fdbbf2d6bf | |||
c02eb14255 | |||
d55b7a3063 | |||
ba94ffba35 | |||
c03bc18803 | |||
|
9a36af0830 | ||
|
71b14b63da | ||
6c41339fc0 | |||
dbc30ef188 | |||
ef6d3086ac | |||
fad98cf64a | |||
ad1ce0d9b0 | |||
61262617ec | |||
|
efa36be102 | ||
ecfa614dd8 | |||
ee8b37b3a6 | |||
4a31cb9810 | |||
a373597294 | |||
08f5678c98 | |||
4cc0412154 |
@ -11,6 +11,7 @@
|
||||
"comma-dangle": [ 2, "never" ],
|
||||
"curly": 0,
|
||||
"no-bitwise": 0,
|
||||
"no-cond-assign": 1,
|
||||
"no-console": 0,
|
||||
"no-control-regex": 0,
|
||||
"no-unused-vars": 1,
|
||||
|
20
.github/workflows/bun.yml
vendored
Normal file
20
.github/workflows/bun.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
name: 'Tests: Bun'
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
# misc test
|
||||
misc:
|
||||
name: 'misc (with codepage)'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: antongolub/action-setup-bun@v1
|
||||
- uses: ljharb/actions/node/install@main
|
||||
with:
|
||||
node-version: '16.'
|
||||
- run: sudo curl -Lo /usr/bin/rooster https://github.com/SheetJS/rooster/releases/download/v0.2.0/rooster-v0.2.0-linux-amd64
|
||||
- run: sudo chmod a+x /usr/bin/rooster
|
||||
- run: make init
|
||||
- run: 'cd test_files; make all; cd -'
|
||||
- run: 'env FMTS=misc bun hotcross.mjs'
|
38
.github/workflows/deno.yml
vendored
38
.github/workflows/deno.yml
vendored
@ -3,26 +3,9 @@ name: 'Tests: deno 1.x'
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
# small test
|
||||
misc:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
FMTS: misc
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: denoland/setup-deno@main
|
||||
with:
|
||||
deno-version: v1.x
|
||||
- uses: ljharb/actions/node/install@main
|
||||
with:
|
||||
node-version: '16.'
|
||||
- run: sudo curl -Lo /usr/bin/rooster https://github.com/SheetJS/rooster/releases/download/v0.2.0/rooster-v0.2.0-linux-amd64
|
||||
- run: sudo chmod a+x /usr/bin/rooster
|
||||
- run: make init
|
||||
- run: 'cd test_files; make all; cd -'
|
||||
- run: deno test --allow-env --allow-read --allow-write test.ts
|
||||
# full test
|
||||
full:
|
||||
name: 'full (with codepage)'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -36,4 +19,21 @@ jobs:
|
||||
- run: sudo chmod a+x /usr/bin/rooster
|
||||
- run: make init
|
||||
- run: 'cd test_files; make all; cd -'
|
||||
- run: deno test --allow-env --allow-read --allow-write test.ts
|
||||
- run: deno test --allow-env --allow-read --allow-write --config misc/test.deno.jsonc test.ts
|
||||
# full test (no codepage)
|
||||
fullnocp:
|
||||
name: 'full (no codepage)'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: denoland/setup-deno@main
|
||||
with:
|
||||
deno-version: v1.x
|
||||
- uses: ljharb/actions/node/install@main
|
||||
with:
|
||||
node-version: '16.'
|
||||
- run: sudo curl -Lo /usr/bin/rooster https://github.com/SheetJS/rooster/releases/download/v0.2.0/rooster-v0.2.0-linux-amd64
|
||||
- run: sudo chmod a+x /usr/bin/rooster
|
||||
- run: make init
|
||||
- run: 'cd test_files; make all; cd -'
|
||||
- run: deno test --allow-env --allow-read --allow-write --config misc/test.deno.jsonc testnocp.ts
|
||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -14,6 +14,7 @@ tmp
|
||||
*.[pP][mM][dD]*
|
||||
*.[pP][dD][fF]
|
||||
*.[sS][lL][kK]
|
||||
*.[sS][yY][lL][kK]
|
||||
*.socialcalc
|
||||
*.[xX][lL][sSwWcCaAtTmMrR]
|
||||
*.[xX][lL][sSaAtT][xXmMbB]
|
||||
@ -22,14 +23,16 @@ tmp
|
||||
*.[xX][mM][lL]
|
||||
*.[xX][lL][mM][lL]
|
||||
*.[uU][oO][sS]
|
||||
*.[wW][kKqQbB][S1234567890]
|
||||
*.[wW][kKqQbB][sS1234567890]
|
||||
*.[qQ][pP][wW]
|
||||
*.[fF][mM][3tT]
|
||||
*.[bB][iI][fF][fF][23458]
|
||||
*.[rR][tT][fF]
|
||||
*.[eE][tT]
|
||||
*.[eE][tT][hH]
|
||||
*.[nN][uU][mM][bB][eE][rR][sS]
|
||||
*.[mM][oO][dD]
|
||||
*.[dD][tT][aA]
|
||||
*.123
|
||||
*.htm
|
||||
*.html
|
||||
|
1
.gitmodules
vendored
1
.gitmodules
vendored
@ -1,3 +1,4 @@
|
||||
[submodule "test_files"]
|
||||
path = test_files
|
||||
url = https://github.com/SheetJS/test_files
|
||||
ignore = dirty
|
||||
|
@ -11,6 +11,7 @@ node_modules
|
||||
*.jsx
|
||||
_book
|
||||
book.json
|
||||
v8.log
|
||||
tmp
|
||||
*.[tT][xX][tT]
|
||||
*.[cC][sS][vV]
|
||||
@ -19,6 +20,7 @@ tmp
|
||||
*.[pP][mM][dD]*
|
||||
*.[pP][dD][fF]
|
||||
*.[sS][lL][kK]
|
||||
*.[sS][yY][lL][kK]
|
||||
*.socialcalc
|
||||
*.[xX][lL][sSwWcCaAtTmMrR]
|
||||
*.[xX][lL][sSaAtT][xXmMbB]
|
||||
@ -32,6 +34,7 @@ tmp
|
||||
*.[fF][mM][3tT]
|
||||
*.[bB][iI][fF][fF][23458]
|
||||
*.[rR][tT][fF]
|
||||
*.[eE][tT]
|
||||
*.[eE][tT][hH]
|
||||
*.[nN][uU][mM][bB][eE][rR][sS]
|
||||
*.[mM][oO][dD]
|
||||
@ -60,6 +63,9 @@ shim.js
|
||||
test.js
|
||||
test.mjs
|
||||
test.ts
|
||||
test.mts
|
||||
testnocp.ts
|
||||
testbun.mjs
|
||||
.jscs.json
|
||||
.gitmodules
|
||||
.travis.yml
|
||||
|
@ -54,6 +54,7 @@ Rollup
|
||||
SessionStorage
|
||||
SQLite
|
||||
SystemJS
|
||||
Vite
|
||||
VueJS
|
||||
WebKit
|
||||
WebSQL
|
||||
|
70
CHANGELOG.md
70
CHANGELOG.md
@ -4,10 +4,78 @@ 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)
|
||||
* API functions support UTC and local time value interpretations
|
||||
* Export `NaN` values to `#NUM!` and infinite values to `#DIV/0!`
|
||||
|
||||
## v0.19.3
|
||||
|
||||
* XLSX Ensure comment address is valid (h/t @slonser)
|
||||
* Enforce Excel worksheet name restrictions
|
||||
* Fixed "Prototype Pollution" vulnerability (CVE-2023-30533)
|
||||
|
||||
## v0.19.2
|
||||
|
||||
* XLSX proper decoding of hyperlinks (h/t @tw-yaxu)
|
||||
* XLSX ignore unexpected attributes in rich text (h/t @colin4)
|
||||
* `sheet_to_json` type fix (h/t @chsdwn)
|
||||
|
||||
## v0.19.1
|
||||
|
||||
* Fixed types issue in strict mode (h/t @younes-io)
|
||||
* Numbers 12.2 parsing skip ActivityStream.iwa
|
||||
|
||||
## v0.19.0
|
||||
|
||||
* XLSX export hyperlinks compatible with google sheets (h/t Evan Bovie)
|
||||
* NUMBERS export multiple sheets, full worksheet range
|
||||
* formalized `dense` mode
|
||||
|
||||
## v0.18.12
|
||||
|
||||
* `package.json` added types in `exports` structure
|
||||
* uncapped NUMBERS single-sheet single-table export
|
||||
* DBF export records using supported codepages
|
||||
|
||||
## v0.18.11
|
||||
|
||||
* Base64 input ignore data URI wrapper
|
||||
* Parse ZIP files that use ZIP64 extended information field
|
||||
* More precise handling of time-only values
|
||||
* Threaded Comment fallback text for older Excel
|
||||
|
||||
## v0.18.10
|
||||
|
||||
* `exports` field in package.json to satiate ViteJS and newer tooling
|
||||
* JSC (Safari / Bun) perf, see <https://bugs.webkit.org/show_bug.cgi?id=243148>
|
||||
* workbook `bookType` property to denote the origin format when parsed from file
|
||||
* XLSX force export of stub cells with number formats when `sheetStubs` is set
|
||||
|
||||
## v0.18.9
|
||||
|
||||
* XLSX / ODS write defined names
|
||||
* sync defined names to AutoFilter setting on export
|
||||
* 1904 date system setting properly roundtripped
|
||||
* ODS read/write number formats
|
||||
|
||||
## v0.18.8
|
||||
|
||||
* Plaintext parsing of dateless meridien time values (`1:23:45 PM`)
|
||||
* Legacy format (SYLK / WK# / Multiplan) minutiae
|
||||
* Legacy format (SYLK / WK# / Multiplan) minutiae
|
||||
|
||||
## v0.18.7
|
||||
|
||||
|
56
Makefile
56
Makefile
@ -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
|
||||
@ -107,12 +106,13 @@ dist-deps: ## Copy dependencies for distribution
|
||||
.PHONY: aux
|
||||
aux: $(AUXTARGETS)
|
||||
|
||||
BYTEFILEC=dist/xlsx.{full,core,mini}.min.js
|
||||
BYTEFILER=dist/xlsx.extendscript.js xlsx.mjs
|
||||
BYTEFILEC=dist/xlsx.{full,core,mini}.min.js xlsx.mjs
|
||||
BYTEFILER=dist/xlsx.extendscript.js
|
||||
.PHONY: bytes
|
||||
bytes: ## Display minified and gzipped file sizes
|
||||
@for i in $(BYTEFILEC); do npx printj "%-30s %7d %10d" $$i $$(wc -c < $$i) $$(gzip --best --stdout $$i | wc -c); done
|
||||
@for i in $(BYTEFILER); do npx printj "%-30s %7d" $$i $$(wc -c < $$i); done
|
||||
@npx printj "%-30s %10d" "treeshake" "$$(npx -y esbuild@0.14.14 --bundle misc/import.js | wc -c)"
|
||||
|
||||
|
||||
.PHONY: git
|
||||
@ -137,31 +137,62 @@ pkg: bin/xlsx.njs xlsx.js ## Build pkg standalone executable
|
||||
test mocha: test.js ## Run test suite
|
||||
mocha -R spec -t 30000
|
||||
|
||||
#* To run tests for one format, make test_<fmt>
|
||||
#* To run the core test suite, make test_misc
|
||||
|
||||
.PHONY: testdot
|
||||
testdot: test.js ## Run test suite using dot reporter
|
||||
mocha -R dot -t 30000
|
||||
|
||||
.PHONY: test-esm
|
||||
test-esm: test.mjs ## Run Node ESM test suite
|
||||
npx -y mocha@9 -R spec -t 30000 $<
|
||||
|
||||
test.ts: test.mts
|
||||
node -pe 'var data = fs.readFileSync("'$<'", "utf8"); data.split("\n").map(function(l) { return l.replace(/^describe\((.*?)function\(\)/, "Deno.test($$1async function(t)").replace(/\b(?:it|describe)\((.*?)function\(\)/g, "await t.step($$1async function(t)").replace("assert.ok", "assert.assert"); }).join("\n")' > $@
|
||||
|
||||
.PHONY: test-bun
|
||||
test-bun: testbun.mjs ## Run Bun test suite
|
||||
bun $<
|
||||
|
||||
.PHONY: test-deno
|
||||
test-deno: test.ts ## Run Deno test suite
|
||||
deno test --allow-env --allow-read --allow-write $<
|
||||
deno test --check --allow-env --allow-read --allow-write --config misc/test.deno.jsonc $<
|
||||
|
||||
.PHONY: test-denocp
|
||||
test-denocp: testnocp.ts ## Run Deno test suite (without codepage)
|
||||
deno test --check --allow-env --allow-read --allow-write --config misc/test.deno.jsonc $<
|
||||
|
||||
#* To run tests for one format, make test_<fmt>
|
||||
#* To run the core test suite, make test_misc
|
||||
TESTFMT=$(patsubst %,test_%,$(FMT))
|
||||
.PHONY: $(TESTFMT)
|
||||
$(TESTFMT): test_%:
|
||||
FMTS=$* make test
|
||||
|
||||
TESTFMT=$(patsubst %,testdot_%,$(FMT))
|
||||
.PHONY: $(TESTFMT)
|
||||
$(TESTFMT): testdot_%:
|
||||
FMTS=$* make testdot
|
||||
|
||||
TESTESMFMT=$(patsubst %,test-esm_%,$(FMT))
|
||||
.PHONY: $(TESTESMFMT)
|
||||
$(TESTESMFMT): test-esm_%:
|
||||
FMTS=$* make test-esm
|
||||
|
||||
TESTDENOFMT=$(patsubst %,test-deno_%,$(FMT))
|
||||
.PHONY: $(TESTESMFMT)
|
||||
.PHONY: $(TESTDENOFMT)
|
||||
$(TESTDENOFMT): test-deno_%:
|
||||
FMTS=$* make test-deno
|
||||
|
||||
TESTDENOCPFMT=$(patsubst %,test-denocp_%,$(FMT))
|
||||
.PHONY: $(TESTDENOCPFMT)
|
||||
$(TESTDENOCPFMT): test-denocp_%:
|
||||
FMTS=$* make test-denocp
|
||||
|
||||
TESTBUNFMT=$(patsubst %,test-bun_%,$(FMT))
|
||||
.PHONY: $(TESTBUNFMT)
|
||||
$(TESTBUNFMT): test-bun_%:
|
||||
FMTS=$* make test-bun
|
||||
|
||||
.PHONY: travis
|
||||
travis: ## Run test suite with minimal output
|
||||
mocha -R dot -t 30000
|
||||
@ -172,7 +203,7 @@ ctest: ## Build browser test fixtures
|
||||
|
||||
.PHONY: ctestserv
|
||||
ctestserv: ## Start a test server on port 8000
|
||||
@cd tests && python -mSimpleHTTPServer
|
||||
@cd tests && python -mSimpleHTTPServer || python3 -mhttp.server || npx -y http-server -p 8000 .
|
||||
|
||||
## Code Checking
|
||||
|
||||
@ -223,8 +254,7 @@ misc/coverage.html: $(TARGET) test.js
|
||||
coveralls: ## Coverage Test + Send to coveralls.io
|
||||
mocha --require blanket --reporter mocha-lcov-reporter -t 30000 | node ./node_modules/coveralls/bin/coveralls.js
|
||||
|
||||
DEMOMDS=$(sort $(wildcard demos/*/README.md))
|
||||
MDLINT=$(DEMOMDS) README.md demos/README.md
|
||||
MDLINT=README.md
|
||||
.PHONY: mdlint
|
||||
mdlint: $(MDLINT) ## Check markdown documents
|
||||
./node_modules/.bin/alex $^
|
||||
|
25
README.md
25
README.md
@ -9,33 +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!
|
||||
|
||||
![License](https://img.shields.io/github/license/SheetJS/sheetjs)
|
||||
[![Build Status](https://img.shields.io/github/workflow/status/sheetjs/sheetjs/Tests:%20node.js)](https://github.com/SheetJS/sheetjs/actions)
|
||||
[![Snyk Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/SheetJS/sheetjs)](https://snyk.io/test/github/SheetJS/sheetjs)
|
||||
[![npm Downloads](https://img.shields.io/npm/dm/xlsx.svg)](https://npmjs.org/package/xlsx)
|
||||
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/sheetjs?pixel)](https://github.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
|
||||
- <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): test files repo
|
||||
- [`cfb`](https://git.sheetjs.com/SheetJS/js-cfb): Container (OLE/ZIP) file
|
||||
processing library
|
||||
|
||||
- [`cfb`](https://github.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://github.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
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*! xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* vim: set ts=2: */
|
||||
/*exported XLSX */
|
||||
/*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false, DataView:false, Deno:false */
|
||||
/*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){
|
||||
|
@ -1 +1 @@
|
||||
XLSX.version = '0.18.8';
|
||||
XLSX.version = '0.20.1';
|
||||
|
@ -41,6 +41,11 @@ function utf16leread(data/*:string*/)/*:string*/ {
|
||||
for(var i = 0; i < (data.length>>1); ++i) o[i] = String.fromCharCode(data.charCodeAt(2*i) + (data.charCodeAt(2*i+1)<<8));
|
||||
return o.join("");
|
||||
}
|
||||
function utf16lereadu(data/*:Uint8Array*/)/*:string*/ {
|
||||
var o/*:Array<string>*/ = [];
|
||||
for(var i = 0; i < (data.length>>1); ++i) o[i] = String.fromCharCode(data[2*i] + (data[2*i+1]<<8));
|
||||
return o.join("");
|
||||
}
|
||||
function utf16beread(data/*:string*/)/*:string*/ {
|
||||
var o/*:Array<string>*/ = [];
|
||||
for(var i = 0; i < (data.length>>1); ++i) o[i] = String.fromCharCode(data.charCodeAt(2*i+1) + (data.charCodeAt(2*i)<<8));
|
||||
|
@ -19,10 +19,56 @@ function Base64_encode(input) {
|
||||
}
|
||||
return o;
|
||||
}
|
||||
function Base64_encode_pass(input) {
|
||||
var o = "";
|
||||
var c1 = 0, c2 = 0, c3 = 0, e1 = 0, e2 = 0, e3 = 0, e4 = 0;
|
||||
for (var i = 0; i < input.length; ) {
|
||||
c1 = input.charCodeAt(i++);
|
||||
if (c1 > 255)
|
||||
c1 = 95;
|
||||
e1 = c1 >> 2;
|
||||
c2 = input.charCodeAt(i++);
|
||||
if (c2 > 255)
|
||||
c2 = 95;
|
||||
e2 = (c1 & 3) << 4 | c2 >> 4;
|
||||
c3 = input.charCodeAt(i++);
|
||||
if (c3 > 255)
|
||||
c3 = 95;
|
||||
e3 = (c2 & 15) << 2 | c3 >> 6;
|
||||
e4 = c3 & 63;
|
||||
if (isNaN(c2)) {
|
||||
e3 = e4 = 64;
|
||||
} else if (isNaN(c3)) {
|
||||
e4 = 64;
|
||||
}
|
||||
o += Base64_map.charAt(e1) + Base64_map.charAt(e2) + Base64_map.charAt(e3) + Base64_map.charAt(e4);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
function Base64_encode_arr(input) {
|
||||
var o = "";
|
||||
var c1 = 0, c2 = 0, c3 = 0, e1 = 0, e2 = 0, e3 = 0, e4 = 0;
|
||||
for (var i = 0; i < input.length; ) {
|
||||
c1 = input[i++];
|
||||
e1 = c1 >> 2;
|
||||
c2 = input[i++];
|
||||
e2 = (c1 & 3) << 4 | c2 >> 4;
|
||||
c3 = input[i++];
|
||||
e3 = (c2 & 15) << 2 | c3 >> 6;
|
||||
e4 = c3 & 63;
|
||||
if (isNaN(c2)) {
|
||||
e3 = e4 = 64;
|
||||
} else if (isNaN(c3)) {
|
||||
e4 = 64;
|
||||
}
|
||||
o += Base64_map.charAt(e1) + Base64_map.charAt(e2) + Base64_map.charAt(e3) + Base64_map.charAt(e4);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
function Base64_decode(input) {
|
||||
var o = "";
|
||||
var c1 = 0, c2 = 0, c3 = 0, e1 = 0, e2 = 0, e3 = 0, e4 = 0;
|
||||
input = input.replace(/[^\w\+\/\=]/g, "");
|
||||
input = input.replace(/^data:([^\/]+\/[^\/]+)?;base64\,/, "").replace(/[^\w\+\/\=]/g, "");
|
||||
for (var i = 0; i < input.length; ) {
|
||||
e1 = Base64_map.indexOf(input.charAt(i++));
|
||||
e2 = Base64_map.indexOf(input.charAt(i++));
|
||||
|
@ -8,6 +8,13 @@ var Buffer_from = /*#__PURE__*/(function() {
|
||||
}
|
||||
return function() {};
|
||||
})();
|
||||
var buf_utf16le = /*#__PURE__*/(function() {
|
||||
if(typeof Buffer === 'undefined') return false;
|
||||
var x = Buffer_from([65,0]);
|
||||
if(!x) return false;
|
||||
var o = x.toString("utf16le");
|
||||
return o.length == 1;
|
||||
})();
|
||||
|
||||
|
||||
function new_raw_buf(len/*:number*/) {
|
||||
@ -64,7 +71,7 @@ var bconcat = has_buf ? function(bufs) { return Buffer.concat(bufs.map(function(
|
||||
for(i = 0, maxlen = 0; i < bufs.length; maxlen += len, ++i) {
|
||||
len = bufs[i].length;
|
||||
if(bufs[i] instanceof Uint8Array) o.set(bufs[i], maxlen);
|
||||
else if(typeof bufs[i] == "string") { throw "wtf"; }
|
||||
else if(typeof bufs[i] == "string") o.set(new Uint8Array(s2a(bufs[i])), maxlen);
|
||||
else o.set(new Uint8Array(bufs[i]), maxlen);
|
||||
}
|
||||
return o;
|
||||
|
@ -173,8 +173,20 @@ function SSF_frac(x/*:number*/, D/*:number*/, mixed/*:?boolean*/)/*:Array<number
|
||||
var q = Math.floor(sgn * P/Q);
|
||||
return [q, sgn*P - q*Q, Q];
|
||||
}
|
||||
function SSF_normalize_xl_unsafe(v/*:number*/)/*:number*/ {
|
||||
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/*:number*/,opts/*:?any*/,b2/*:?boolean*/) {
|
||||
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};
|
||||
@ -203,15 +215,6 @@ function SSF_parse_date_code(v/*:number*/,opts/*:?any*/,b2/*:?boolean*/) {
|
||||
out.q = dow;
|
||||
return out;
|
||||
}
|
||||
var SSFbasedate = /*#__PURE__*/new Date(1899, 11, 31, 0, 0, 0);
|
||||
var SSFdnthresh = /*#__PURE__*/SSFbasedate.getTime();
|
||||
var SSFbase1904 = /*#__PURE__*/new Date(1900, 2, 1, 0, 0, 0);
|
||||
function datenum_local(v/*:Date*/, date1904/*:?boolean*/)/*:number*/ {
|
||||
var epoch = /*#__PURE__*/v.getTime();
|
||||
if(date1904) epoch -= 1461*24*60*60*1000;
|
||||
else if(v >= SSFbase1904) epoch += 24*60*60*1000;
|
||||
return (epoch - (SSFdnthresh + (/*#__PURE__*/v.getTimezoneOffset() - /*#__PURE__*/SSFbasedate.getTimezoneOffset()) * 60000)) / (24 * 60 * 60 * 1000);
|
||||
}
|
||||
/* ECMA-376 18.8.30 numFmt*/
|
||||
/* Note: `toPrecision` uses standard form when prec > E and E >= -6 */
|
||||
/* exponent >= -9 and <= 9 */
|
||||
@ -269,7 +272,7 @@ function SSF_general(v/*:any*/, opts/*:any*/) {
|
||||
case 'undefined': return "";
|
||||
case 'object':
|
||||
if(v == null) return "";
|
||||
if(v instanceof Date) return SSF_format(14, datenum_local(v, opts && opts.date1904), opts);
|
||||
if(v instanceof Date) return SSF_format(14, datenum(v, opts && opts.date1904), opts);
|
||||
}
|
||||
throw new Error("unsupported value in General format: " + v);
|
||||
}
|
||||
@ -337,7 +340,7 @@ function SSF_write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*
|
||||
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 */
|
||||
@ -785,10 +788,11 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
|
||||
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;
|
||||
@ -798,19 +802,29 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
|
||||
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) break;*/
|
||||
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) break;*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -957,7 +971,7 @@ function SSF_format(fmt/*:string|number*/,v/*:any*/,o/*:?any*/) {
|
||||
break;
|
||||
}
|
||||
if(SSF_isgeneral(sfmt,0)) return SSF_general(v, o);
|
||||
if(v instanceof Date) v = datenum_local(v, o.date1904);
|
||||
if(v instanceof Date) v = datenum(v, o.date1904);
|
||||
var f = choose_fmt(sfmt, v);
|
||||
if(SSF_isgeneral(f[1])) return SSF_general(v, o);
|
||||
if(v === true) v = "TRUE"; else if(v === false) v = "FALSE";
|
||||
|
@ -43,6 +43,7 @@ var dateNFregex = /[dD]+|[mM]+|[yYeE]+|[Hh]+|[Ss]+/g;
|
||||
function dateNF_regex(dateNF/*:string|number*/)/*:RegExp*/ {
|
||||
var fmt = typeof dateNF == "number" ? table_fmt[dateNF] : dateNF;
|
||||
fmt = fmt.replace(dateNFregex, "(\\d+)");
|
||||
dateNFregex.lastIndex = 0;
|
||||
return new RegExp("^" + fmt + "$");
|
||||
}
|
||||
function dateNF_fix(str/*:string*/, dateNF/*:string*/, match/*:Array<string>*/)/*:string*/ {
|
||||
@ -55,6 +56,7 @@ function dateNF_fix(str/*:string*/, dateNF/*:string*/, match/*:Array<string>*/)/
|
||||
case 'm': if(H >= 0) M = v; else m = v; break;
|
||||
}
|
||||
});
|
||||
dateNFregex.lastIndex = 0;
|
||||
if(S >= 0 && M == -1 && m >= 0) { M = m; m = -1; }
|
||||
var datestr = (("" + (Y>=0?Y: new Date().getFullYear())).slice(-4) + "-" + ("00" + (m>=1?m:1)).slice(-2) + "-" + ("00" + (d>=1?d:1)).slice(-2));
|
||||
if(datestr.length == 7) datestr = "0" + datestr;
|
||||
|
@ -73,7 +73,7 @@ function slice_by_16_tables(T) {
|
||||
for(c = 256 + n; c < 4096; c += 256) v = table[c] = (v >>> 8) ^ T[v & 0xFF];
|
||||
}
|
||||
var out = [];
|
||||
for(n = 1; n != 16; ++n) out[n - 1] = typeof Int32Array !== 'undefined' ? table.subarray(n * 256, n * 256 + 256) : table.slice(n * 256, n * 256 + 256);
|
||||
for(n = 1; n != 16; ++n) out[n - 1] = typeof Int32Array !== 'undefined' && typeof table.subarray == "function" ? table.subarray(n * 256, n * 256 + 256) : table.slice(n * 256, n * 256 + 256);
|
||||
return out;
|
||||
}
|
||||
var TT = slice_by_16_tables(T0);
|
||||
@ -211,8 +211,15 @@ function parse_extra_field(blob/*:CFBlob*/)/*:any*/ {
|
||||
if(flags & 4) p.ctime = blob.read_shift(4);
|
||||
}
|
||||
if(p.mtime) p.mt = new Date(p.mtime*1000);
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
/* ZIP64 Extended Information Field */
|
||||
case 0x0001: {
|
||||
var sz1 = blob.read_shift(4), sz2 = blob.read_shift(4);
|
||||
p.usz = (sz2 * Math.pow(2,32) + sz1);
|
||||
sz1 = blob.read_shift(4); sz2 = blob.read_shift(4);
|
||||
p.csz = (sz2 * Math.pow(2,32) + sz1);
|
||||
// NOTE: volume fields are skipped
|
||||
} break;
|
||||
}
|
||||
blob.l = tgt;
|
||||
o[type] = p;
|
||||
@ -299,7 +306,7 @@ sleuth_fat(difat_start, difat_sec_cnt, sectors, ssz, fat_addrs);
|
||||
/** Chains */
|
||||
var sector_list/*:SectorList*/ = make_sector_list(sectors, dir_start, fat_addrs, ssz);
|
||||
|
||||
sector_list[dir_start].name = "!Directory";
|
||||
if(dir_start < sector_list.length) sector_list[dir_start].name = "!Directory";
|
||||
if(nmfs > 0 && minifat_start !== ENDOFCHAIN) sector_list[minifat_start].name = "!MiniFAT";
|
||||
sector_list[fat_addrs[0]].name = "!FAT";
|
||||
sector_list.fat_addrs = fat_addrs;
|
||||
@ -1401,6 +1408,11 @@ function parse_zip(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/
|
||||
|
||||
var L = blob.l;
|
||||
blob.l = offset + 4;
|
||||
/* ZIP64 lengths */
|
||||
if(EF && EF[0x0001]) {
|
||||
if((EF[0x0001]||{}).usz) usz = EF[0x0001].usz;
|
||||
if((EF[0x0001]||{}).csz) csz = EF[0x0001].csz;
|
||||
}
|
||||
parse_local_file(blob, csz, usz, o, EF);
|
||||
blob.l = L;
|
||||
}
|
||||
@ -1430,7 +1442,13 @@ function parse_local_file(blob/*:CFBlob*/, csz/*:number*/, usz/*:number*/, o/*:C
|
||||
if(efsz) {
|
||||
var ef = parse_extra_field(/*::(*/blob.slice(blob.l, blob.l + efsz)/*:: :any)*/);
|
||||
if((ef[0x5455]||{}).mt) date = ef[0x5455].mt;
|
||||
if(((EF||{})[0x5455]||{}).mt) date = EF[0x5455].mt;
|
||||
if((ef[0x0001]||{}).usz) _usz = ef[0x0001].usz;
|
||||
if((ef[0x0001]||{}).csz) _csz = ef[0x0001].csz;
|
||||
if(EF) {
|
||||
if((EF[0x5455]||{}).mt) date = EF[0x5455].mt;
|
||||
if((EF[0x0001]||{}).usz) _usz = ef[0x0001].usz;
|
||||
if((EF[0x0001]||{}).csz) _csz = ef[0x0001].csz;
|
||||
}
|
||||
}
|
||||
blob.l += efsz;
|
||||
|
||||
@ -1482,9 +1500,9 @@ function write_zip(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/
|
||||
var namebuf = new_buf(fp.length);
|
||||
for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F);
|
||||
namebuf = namebuf.slice(0, namebuf.l);
|
||||
crcs[fcnt] = CRC32.buf(/*::((*/fi.content/*::||[]):any)*/, 0);
|
||||
crcs[fcnt] = typeof fi.content == "string" ? CRC32.bstr(fi.content, 0) : CRC32.buf(/*::((*/fi.content/*::||[]):any)*/, 0);
|
||||
|
||||
var outbuf = fi.content/*::||[]*/;
|
||||
var outbuf = typeof fi.content == "string" ? s2a(fi.content) : fi.content/*::||[]*/;
|
||||
if(method == 8) outbuf = _deflateRawSync(outbuf);
|
||||
|
||||
/* local file header */
|
||||
|
@ -35,7 +35,7 @@ function write_dl(fname/*:string*/, payload/*:any*/, enc/*:?string*/) {
|
||||
/*:: declare var chrome: any; */
|
||||
if(typeof chrome === 'object' && typeof (chrome.downloads||{}).download == "function") {
|
||||
if(URL.revokeObjectURL && typeof setTimeout !== 'undefined') setTimeout(function() { URL.revokeObjectURL(url); }, 60000);
|
||||
return chrome.downloads.download({ url: url, filename: fname, saveAs: true});
|
||||
return chrome.downloads.download({ url: url, filename: fname, saveAs: true });
|
||||
}
|
||||
var a = document.createElement("a");
|
||||
if(a.download != null) {
|
||||
@ -45,6 +45,10 @@ function write_dl(fname/*:string*/, payload/*:any*/, enc/*:?string*/) {
|
||||
if(URL.revokeObjectURL && typeof setTimeout !== 'undefined') setTimeout(function() { URL.revokeObjectURL(url); }, 60000);
|
||||
return url;
|
||||
}
|
||||
} else if(typeof URL !== 'undefined' && !URL.createObjectURL && typeof chrome === 'object') {
|
||||
/* manifest v3 extensions -- no URL.createObjectURL */
|
||||
var b64 = "data:application/octet-stream;base64," + Base64_encode_arr(new Uint8Array(blobify(data)));
|
||||
return chrome.downloads.download({ url: b64, filename: fname, saveAs: true });
|
||||
}
|
||||
}
|
||||
// $FlowIgnore
|
||||
|
@ -31,22 +31,19 @@ function evert_arr(obj/*:any*/)/*:EvertArrType*/ {
|
||||
return o;
|
||||
}
|
||||
|
||||
var basedate = /*#__PURE__*/new Date(1899, 11, 30, 0, 0, 0); // 2209161600000
|
||||
var dnthresh = /*#__PURE__*/Date.UTC(1899, 11, 30, 0, 0, 0); // -2209161600000
|
||||
var dnthresh1 = /*#__PURE__*/Date.UTC(1899, 11, 31, 0, 0, 0); // -2209075200000
|
||||
var dnthresh2 = /*#__PURE__*/Date.UTC(1904, 0, 1, 0, 0, 0); // -2209075200000
|
||||
function datenum(v/*:Date*/, date1904/*:?boolean*/)/*:number*/ {
|
||||
var epoch = /*#__PURE__*/v.getTime();
|
||||
if(date1904) epoch -= 1462*24*60*60*1000;
|
||||
var dnthresh = /*#__PURE__*/basedate.getTime() + (/*#__PURE__*/v.getTimezoneOffset() - /*#__PURE__*/basedate.getTimezoneOffset()) * 60000;
|
||||
return (epoch - dnthresh) / (24 * 60 * 60 * 1000);
|
||||
var res = (epoch - dnthresh) / (24 * 60 * 60 * 1000);
|
||||
if(date1904) { res -= 1462; return res < -1402 ? res - 1 : res; }
|
||||
return res < 60 ? res - 1 : res;
|
||||
}
|
||||
var refdate = /*#__PURE__*/new Date();
|
||||
var dnthresh = /*#__PURE__*/basedate.getTime() + (/*#__PURE__*/refdate.getTimezoneOffset() - /*#__PURE__*/basedate.getTimezoneOffset()) * 60000;
|
||||
var refoffset = /*#__PURE__*/refdate.getTimezoneOffset();
|
||||
function numdate(v/*:number*/)/*:Date*/ {
|
||||
function numdate(v/*:number*/)/*:Date|number*/ {
|
||||
if(v >= 60 && v < 61) return v;
|
||||
var out = new Date();
|
||||
out.setTime(v * 24 * 60 * 60 * 1000 + dnthresh);
|
||||
if (out.getTimezoneOffset() !== refoffset) {
|
||||
out.setTime(out.getTime() + (out.getTimezoneOffset() - refoffset) * 60000);
|
||||
}
|
||||
out.setTime((v>60 ? v : (v+1)) * 24 * 60 * 60 * 1000 + dnthresh);
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -77,33 +74,28 @@ function parse_isodur(s) {
|
||||
return sec;
|
||||
}
|
||||
|
||||
var good_pd_date_1 = /*#__PURE__*/new Date('2017-02-19T19:06:09.000Z');
|
||||
var good_pd_date = /*#__PURE__*/isNaN(/*#__PURE__*/good_pd_date_1.getFullYear()) ? /*#__PURE__*/new Date('2/19/17') : good_pd_date_1;
|
||||
var good_pd = /*#__PURE__*/good_pd_date.getFullYear() == 2017;
|
||||
/* parses a date as a local date */
|
||||
function parseDate(str/*:string|Date*/, fixdate/*:?number*/)/*:Date*/ {
|
||||
var d = new Date(str);
|
||||
if(good_pd) {
|
||||
/*:: if(fixdate == null) fixdate = 0; */
|
||||
if(fixdate > 0) d.setTime(d.getTime() + d.getTimezoneOffset() * 60 * 1000);
|
||||
else if(fixdate < 0) d.setTime(d.getTime() - d.getTimezoneOffset() * 60 * 1000);
|
||||
return d;
|
||||
}
|
||||
/* Blame https://bugs.chromium.org/p/v8/issues/detail?id=7863 for the regexide */
|
||||
var pdre1 = /^(\d+):(\d+)(:\d+)?(\.\d+)?$/; // HH:MM[:SS[.UUU]]
|
||||
var pdre2 = /^(\d+)-(\d+)-(\d+)$/; // YYYY-mm-dd
|
||||
var pdre3 = /^(\d+)-(\d+)-(\d+)[T ](\d+):(\d+)(:\d+)?(\.\d+)?$/; // YYYY-mm-dd(T or space)HH:MM[:SS[.UUU]], sans "Z"
|
||||
/* parses a date string as a UTC date */
|
||||
function parseDate(str/*:string*/, date1904/*:boolean*/)/*:Date*/ {
|
||||
if(str instanceof Date) return str;
|
||||
if(good_pd_date.getFullYear() == 1917 && !isNaN(d.getFullYear())) {
|
||||
var s = d.getFullYear();
|
||||
if(str.indexOf("" + s) > -1) return d;
|
||||
d.setFullYear(d.getFullYear() + 100); return d;
|
||||
}
|
||||
var n = str.match(/\d+/g)||["2017","2","19","0","0","0"];
|
||||
var out = new Date(+n[0], +n[1] - 1, +n[2], (+n[3]||0), (+n[4]||0), (+n[5]||0));
|
||||
if(str.indexOf("Z") > -1) out = new Date(out.getTime() - out.getTimezoneOffset() * 60 * 1000);
|
||||
return out;
|
||||
var m = str.match(pdre1);
|
||||
if(m) return new Date((date1904 ? dnthresh2 : dnthresh1) + ((parseInt(m[1], 10)*60 + parseInt(m[2], 10))*60 + (m[3] ? parseInt(m[3].slice(1), 10) : 0))*1000 + (m[4] ? parseInt((m[4]+"000").slice(1,4), 10) : 0));
|
||||
m = str.match(pdre2);
|
||||
if(m) return new Date(Date.UTC(+m[1], +m[2]-1, +m[3], 0, 0, 0, 0));
|
||||
/* TODO: 1900-02-29T00:00:00.000 should return a flag to treat as a date code (affects xlml) */
|
||||
m = str.match(pdre3);
|
||||
if(m) return new Date(Date.UTC(+m[1], +m[2]-1, +m[3], +m[4], +m[5], ((m[6] && parseInt(m[6].slice(1), 10))|| 0), ((m[7] && parseInt(m[7].slice(1), 10))||0)));
|
||||
var d = new Date(str);
|
||||
return d;
|
||||
}
|
||||
|
||||
function cc2str(arr/*:Array<number>*/, debomit)/*:string*/ {
|
||||
if(has_buf && Buffer.isBuffer(arr)) {
|
||||
if(debomit) {
|
||||
if(debomit && buf_utf16le) {
|
||||
// TODO: temporary patch
|
||||
if(arr[0] == 0xFF && arr[1] == 0xFE) return utf8write(arr.slice(2).toString("utf16le"));
|
||||
if(arr[1] == 0xFE && arr[2] == 0xFF) return utf8write(utf16beread(arr.slice(2).toString("binary")));
|
||||
}
|
||||
@ -128,8 +120,18 @@ function cc2str(arr/*:Array<number>*/, debomit)/*:string*/ {
|
||||
return new TextDecoder("latin1").decode(arr).replace(/[€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ]/g, function(c) { return rev[c] || c; });
|
||||
} catch(e) {}
|
||||
|
||||
var o = [];
|
||||
for(var i = 0; i != arr.length; ++i) o.push(String.fromCharCode(arr[i]));
|
||||
var o = [], i = 0;
|
||||
// this cascade is for the browsers and runtimes of antiquity (and for modern runtimes that lack TextEncoder)
|
||||
try {
|
||||
for(i = 0; i < arr.length - 65536; i+=65536) o.push(String.fromCharCode.apply(0, arr.slice(i, i + 65536)));
|
||||
o.push(String.fromCharCode.apply(0, arr.slice(i)));
|
||||
} catch(e) { try {
|
||||
for(; i < arr.length - 16384; i+=16384) o.push(String.fromCharCode.apply(0, arr.slice(i, i + 16384)));
|
||||
o.push(String.fromCharCode.apply(0, arr.slice(i)));
|
||||
} catch(e) {
|
||||
for(; i != arr.length; ++i) o.push(String.fromCharCode(arr[i]));
|
||||
}
|
||||
}
|
||||
return o.join("");
|
||||
}
|
||||
|
||||
@ -158,35 +160,50 @@ function fuzzynum(s/*:string*/)/*:number*/ {
|
||||
}
|
||||
|
||||
/* NOTE: Chrome rejects bare times like 1:23 PM */
|
||||
var FDRE1 = /^(0?\d|1[0-2])(?:|:([0-5]?\d)(?:|(\.\d+)(?:|:([0-5]?\d))|:([0-5]?\d)(|\.\d+)))([ap])m?/;
|
||||
var FDRE1 = /^(0?\d|1[0-2])(?:|:([0-5]?\d)(?:|(\.\d+)(?:|:([0-5]?\d))|:([0-5]?\d)(|\.\d+)))\s+([ap])m?$/;
|
||||
var FDRE2 = /^([01]?\d|2[0-3])(?:|:([0-5]?\d)(?:|(\.\d+)(?:|:([0-5]?\d))|:([0-5]?\d)(|\.\d+)))$/;
|
||||
var FDISO = /^(\d+)-(\d+)-(\d+)[T ](\d+):(\d+)(:\d+)(\.\d+)?[Z]?$/; // YYYY-mm-dd(T or space)HH:MM:SS[.UUU][Z]
|
||||
|
||||
/* TODO: 1904 adjustment */
|
||||
var utc_append_works = new Date("6/9/69 00:00 UTC").valueOf() == -17798400000;
|
||||
function fuzzytime1(M) /*:Date*/ {
|
||||
/* TODO: 1904 adjustment */
|
||||
if(!M[2]) return new Date(1900,0,0,(+M[1]%12) + (M[7] == "p" ? 12 : 0), 0, 0, 0);
|
||||
if(M[3]) {
|
||||
if(M[4]) return new Date(1900,0,0,(+M[1]%12) + (M[7] == "p" ? 12 : 0), +M[2], +M[4], parseFloat(M[3])*1000);
|
||||
else return new Date(1900,0,0,(M[7] == "p" ? 12 : 0), +M[1], +M[2], parseFloat(M[3])*1000);
|
||||
}
|
||||
else if(M[5]) return new Date(1900, 0, 0, (+M[1]%12) + (M[7] == "p" ? 12 : 0), +M[2], +M[5], M[6] ? parseFloat(M[6]) * 1000 : 0);
|
||||
else return new Date(1900,0,0,(+M[1]%12) + (M[7] == "p" ? 12 : 0), +M[2], 0, 0);
|
||||
if(!M[2]) return new Date(Date.UTC(1899,11,31,(+M[1]%12) + (M[7] == "p" ? 12 : 0), 0, 0, 0));
|
||||
if(M[3]) {
|
||||
if(M[4]) return new Date(Date.UTC(1899,11,31,(+M[1]%12) + (M[7] == "p" ? 12 : 0), +M[2], +M[4], parseFloat(M[3])*1000));
|
||||
else return new Date(Date.UTC(1899,11,31,(M[7] == "p" ? 12 : 0), +M[1], +M[2], parseFloat(M[3])*1000));
|
||||
}
|
||||
else if(M[5]) return new Date(Date.UTC(1899,11,31, (+M[1]%12) + (M[7] == "p" ? 12 : 0), +M[2], +M[5], M[6] ? parseFloat(M[6]) * 1000 : 0));
|
||||
else return new Date(Date.UTC(1899,11,31,(+M[1]%12) + (M[7] == "p" ? 12 : 0), +M[2], 0, 0));
|
||||
}
|
||||
function fuzzytime2(M) /*:Date*/ {
|
||||
if(!M[2]) return new Date(Date.UTC(1899,11,31,+M[1], 0, 0, 0));
|
||||
if(M[3]) {
|
||||
if(M[4]) return new Date(Date.UTC(1899,11,31,+M[1], +M[2], +M[4], parseFloat(M[3])*1000));
|
||||
else return new Date(Date.UTC(1899,11,31,0, +M[1], +M[2], parseFloat(M[3])*1000));
|
||||
}
|
||||
else if(M[5]) return new Date(Date.UTC(1899,11,31, +M[1], +M[2], +M[5], M[6] ? parseFloat(M[6]) * 1000 : 0));
|
||||
else return new Date(Date.UTC(1899,11,31,+M[1], +M[2], 0, 0));
|
||||
}
|
||||
var lower_months = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'];
|
||||
function fuzzydate(s/*:string*/)/*:Date*/ {
|
||||
// See issue 2863 -- this is technically not supported in Excel but is otherwise useful
|
||||
if(FDISO.test(s)) return s.indexOf("Z") == -1 ? local_to_utc(new Date(s)) : new Date(s);
|
||||
var lower = s.toLowerCase();
|
||||
var lnos = lower.replace(/\s+/g, "");
|
||||
var lnos = lower.replace(/\s+/g, " ").trim();
|
||||
var M = lnos.match(FDRE1);
|
||||
if(M) return fuzzytime1(M);
|
||||
|
||||
var o = new Date(s), n = new Date(NaN);
|
||||
M = lnos.match(FDRE2);
|
||||
if(M) return fuzzytime2(M);
|
||||
M = lnos.match(pdre3);
|
||||
if(M) return new Date(Date.UTC(+M[1], +M[2]-1, +M[3], +M[4], +M[5], ((M[6] && parseInt(M[6].slice(1), 10))|| 0), ((M[7] && parseInt(M[7].slice(1), 10))||0)));
|
||||
var o = new Date(utc_append_works && s.indexOf("UTC") == -1 ? s + " UTC": s), n = new Date(NaN);
|
||||
var y = o.getYear(), m = o.getMonth(), d = o.getDate();
|
||||
if(isNaN(d)) return n;
|
||||
if(lower.match(/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/)) {
|
||||
lower = lower.replace(/[^a-z]/g,"").replace(/([^a-z]|^)[ap]m?([^a-z]|$)/,"");
|
||||
if(lower.length > 3 && lower_months.indexOf(lower) == -1) return n;
|
||||
} else if(lower.replace(/[ap]m?/, "").match(/[a-z]/)) return n;
|
||||
if(y < 0 || y > 8099) return n;
|
||||
if((m > 0 || d > 1) && y != 101) return o;
|
||||
if(s.match(/[^-0-9:,\/\\]/)) return n;
|
||||
if(y < 0 || y > 8099 || s.match(/[^-0-9:,\/\\\ ]/)) return n;
|
||||
return o;
|
||||
}
|
||||
|
||||
@ -199,3 +216,10 @@ var split_regex = /*#__PURE__*/(function() {
|
||||
return o;
|
||||
};
|
||||
})();
|
||||
|
||||
function utc_to_local(utc) {
|
||||
return new Date(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate(), utc.getUTCHours(), utc.getUTCMinutes(), utc.getUTCSeconds(), utc.getUTCMilliseconds());
|
||||
}
|
||||
function local_to_utc(local) {
|
||||
return new Date(Date.UTC(local.getFullYear(), local.getMonth(), local.getDate(), local.getHours(), local.getMinutes(), local.getSeconds(), local.getMilliseconds()));
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ function xlml_normalize(d)/*:string*/ {
|
||||
throw new Error("Bad input format: expected Buffer or string");
|
||||
}
|
||||
/* UOS uses CJK in tags */
|
||||
var xlmlregex = /<(\/?)([^\s?><!\/:]*:|)([^\s?<>:\/]+)(?:[\s?:\/][^>]*)?>/mg;
|
||||
var xlmlregex = /<(\/?)([^\s?><!\/:]*:|)([^\s?<>:\/]+)(?:[\s?:\/](?:[^>=]|="[^"]*?")*)?>/mg;
|
||||
//var xlmlregex = /<(\/?)([a-z0-9]*:|)(\w+)[^>]*>/mg;
|
||||
|
||||
var XMLNS = ({
|
||||
|
@ -29,7 +29,7 @@ var ___toBuffer = function(bufs/*:Array<Array<RawBytes> >*/)/*:RawBytes*/ { var
|
||||
var __toBuffer = has_buf ? function(bufs) { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat(bufs[0].map(function(x) { return Buffer.isBuffer(x) ? x : Buffer_from(x); })) : ___toBuffer(bufs);} : ___toBuffer;
|
||||
|
||||
var ___utf16le = function(b/*:RawBytes|CFBlob*/,s/*:number*/,e/*:number*/)/*:string*/ { var ss/*:Array<string>*/=[]; for(var i=s; i<e; i+=2) ss.push(String.fromCharCode(__readUInt16LE(b,i))); return ss.join("").replace(chr0,''); };
|
||||
var __utf16le = has_buf ? function(b/*:RawBytes|CFBlob*/,s/*:number*/,e/*:number*/)/*:string*/ { if(!Buffer.isBuffer(b)/*:: || !(b instanceof Buffer)*/) return ___utf16le(b,s,e); return b.toString('utf16le',s,e).replace(chr0,'')/*.replace(chr1,'!')*/; } : ___utf16le;
|
||||
var __utf16le = has_buf ? function(b/*:RawBytes|CFBlob*/,s/*:number*/,e/*:number*/)/*:string*/ { if(!Buffer.isBuffer(b)/*:: || !(b instanceof Buffer)*/ || !buf_utf16le) return ___utf16le(b,s,e); return b.toString('utf16le',s,e).replace(chr0,'')/*.replace(chr1,'!')*/; } : ___utf16le;
|
||||
|
||||
var ___hexlify = function(b/*:RawBytes|CFBlob*/,s/*:number*/,l/*:number*/)/*:string*/ { var ss/*:Array<string>*/=[]; for(var i=s; i<s+l; ++i) ss.push(("0" + b[i].toString(16)).slice(-2)); return ss.join(""); };
|
||||
var __hexlify = has_buf ? function(b/*:RawBytes|CFBlob*/,s/*:number*/,l/*:number*/)/*:string*/ { return Buffer.isBuffer(b)/*:: && b instanceof Buffer*/ ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); } : ___hexlify;
|
||||
@ -60,8 +60,8 @@ var is_buf = function is_buf_a(a) { return Array.isArray(a) || (typeof Uint8Arra
|
||||
if(has_buf/*:: && typeof Buffer !== 'undefined'*/) {
|
||||
__lpstr = function lpstr_b(b/*:RawBytes|CFBlob*/, i/*:number*/) { if(!Buffer.isBuffer(b)/*:: || !(b instanceof Buffer)*/) return ___lpstr(b, i); var len = b.readUInt32LE(i); return len > 0 ? b.toString('utf8',i+4,i+4+len-1) : "";};
|
||||
__cpstr = function cpstr_b(b/*:RawBytes|CFBlob*/, i/*:number*/) { if(!Buffer.isBuffer(b)/*:: || !(b instanceof Buffer)*/) return ___cpstr(b, i); var len = b.readUInt32LE(i); return len > 0 ? b.toString('utf8',i+4,i+4+len-1) : "";};
|
||||
__lpwstr = function lpwstr_b(b/*:RawBytes|CFBlob*/, i/*:number*/) { if(!Buffer.isBuffer(b)/*:: || !(b instanceof Buffer)*/) return ___lpwstr(b, i); var len = 2*b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len-1);};
|
||||
__lpp4 = function lpp4_b(b/*:RawBytes|CFBlob*/, i/*:number*/) { if(!Buffer.isBuffer(b)/*:: || !(b instanceof Buffer)*/) return ___lpp4(b, i); var len = b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len);};
|
||||
__lpwstr = function lpwstr_b(b/*:RawBytes|CFBlob*/, i/*:number*/) { if(!Buffer.isBuffer(b)/*:: || !(b instanceof Buffer)*/ || !buf_utf16le) return ___lpwstr(b, i); var len = 2*b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len-1);};
|
||||
__lpp4 = function lpp4_b(b/*:RawBytes|CFBlob*/, i/*:number*/) { if(!Buffer.isBuffer(b)/*:: || !(b instanceof Buffer)*/ || !buf_utf16le) return ___lpp4(b, i); var len = b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len);};
|
||||
__8lpp4 = function lpp4_8b(b/*:RawBytes|CFBlob*/, i/*:number*/) { if(!Buffer.isBuffer(b)/*:: || !(b instanceof Buffer)*/) return ___8lpp4(b, i); var len = b.readUInt32LE(i); return b.toString('utf8',i+4,i+4+len);};
|
||||
__double = function double_(b/*:RawBytes|CFBlob*/, i/*:number*/) { if(Buffer.isBuffer(b)/*::&& b instanceof Buffer*/) return b.readDoubleLE(i); return ___double(b,i); };
|
||||
is_buf = function is_buf_b(a) { return Buffer.isBuffer(a) || Array.isArray(a) || (typeof Uint8Array !== "undefined" && a instanceof Uint8Array); };
|
||||
@ -91,7 +91,7 @@ function ReadShift(size/*:number*/, t/*:?string*/)/*:number|string*/ {
|
||||
switch(t) {
|
||||
case 'dbcs':
|
||||
loc = this.l;
|
||||
if(has_buf && Buffer.isBuffer(this)) o = this.slice(this.l, this.l+2*size).toString("utf16le");
|
||||
if(has_buf && Buffer.isBuffer(this) && buf_utf16le) o = this.slice(this.l, this.l+2*size).toString("utf16le");
|
||||
else for(i = 0; i < size; ++i) { o+=String.fromCharCode(__readUInt16LE(this, loc)); loc+=2; }
|
||||
size *= 2;
|
||||
break;
|
||||
@ -181,21 +181,29 @@ function WriteShift(t/*:number*/, val/*:string|number*/, f/*:?string*/)/*:any*/
|
||||
/*:: if(typeof val !== 'string') throw new Error("unreachable"); */
|
||||
for(i = 0; i != val.length; ++i) __writeUInt16LE(this, val.charCodeAt(i), this.l + 2 * i);
|
||||
size = 2 * val.length;
|
||||
} else if(f === 'sbcs') {
|
||||
} else if(f === 'sbcs' || f == 'cpstr') {
|
||||
if(typeof $cptable !== 'undefined' && current_ansi == 874) {
|
||||
/* TODO: use tables directly, don't encode */
|
||||
/*:: if(typeof val !== "string") throw new Error("unreachable"); */
|
||||
for(i = 0; i != val.length; ++i) {
|
||||
var cppayload = $cptable.utils.encode(current_ansi, val.charAt(i));
|
||||
this[this.l + i] = cppayload[0];
|
||||
var cpp = $cptable.utils.encode(current_ansi, val.charAt(i));
|
||||
this[this.l + i] = cpp[0];
|
||||
}
|
||||
size = val.length;
|
||||
} else if(typeof $cptable !== 'undefined' && f == 'cpstr') {
|
||||
cpp = $cptable.utils.encode(current_codepage, val);
|
||||
/* replace null bytes with _ when relevant */
|
||||
if(cpp.length == val.length) for(i = 0; i < val.length; ++i) if(cpp[i] == 0 && val.charCodeAt(i) != 0) cpp[i] = 0x5F;
|
||||
if(cpp.length == 2 * val.length) for(i = 0; i < val.length; ++i) if(cpp[2*i] == 0 && cpp[2*i+1] == 0 && val.charCodeAt(i) != 0) cpp[2*i] = 0x5F;
|
||||
for(i = 0; i < cpp.length; ++i) this[this.l + i] = cpp[i];
|
||||
size = cpp.length;
|
||||
} else {
|
||||
/*:: if(typeof val !== 'string') throw new Error("unreachable"); */
|
||||
val = val.replace(/[^\x00-\x7F]/g, "_");
|
||||
/*:: if(typeof val !== 'string') throw new Error("unreachable"); */
|
||||
for(i = 0; i != val.length; ++i) this[this.l + i] = (val.charCodeAt(i) & 0xFF);
|
||||
size = val.length;
|
||||
}
|
||||
size = val.length;
|
||||
} else if(f === 'hex') {
|
||||
for(; i < t; ++i) {
|
||||
/*:: if(typeof val !== "string") throw new Error("unreachable"); */
|
||||
|
@ -31,8 +31,11 @@ function buf_array()/*:BufArray*/ {
|
||||
|
||||
var endbuf = function ba_endbuf() {
|
||||
if(!curbuf) return;
|
||||
if(curbuf.length > curbuf.l) { curbuf = curbuf.slice(0, curbuf.l); curbuf.l = curbuf.length; }
|
||||
if(curbuf.length > 0) bufs.push(curbuf);
|
||||
// workaround for new Buffer(3).slice(0,0) bug in bun 0.1.3
|
||||
if(curbuf.l) {
|
||||
if(curbuf.length > curbuf.l) { curbuf = curbuf.slice(0, curbuf.l); curbuf.l = curbuf.length; }
|
||||
if(curbuf.length > 0) bufs.push(curbuf);
|
||||
}
|
||||
curbuf = null;
|
||||
};
|
||||
|
||||
|
@ -9,7 +9,6 @@ function fix_col(cstr/*:string*/)/*:string*/ { return cstr.replace(/^([A-Z])/,"$
|
||||
function unfix_col(cstr/*:string*/)/*:string*/ { return cstr.replace(/^\$([A-Z])/,"$1"); }
|
||||
|
||||
function split_cell(cstr/*:string*/)/*:Array<string>*/ { return cstr.replace(/(\$?[A-Z]*)(\$?\d*)/,"$1,$2").split(","); }
|
||||
//function decode_cell(cstr/*:string*/)/*:CellAddress*/ { var splt = split_cell(cstr); return { c:decode_col(splt[0]), r:decode_row(splt[1]) }; }
|
||||
function decode_cell(cstr/*:string*/)/*:CellAddress*/ {
|
||||
var R = 0, C = 0;
|
||||
for(var i = 0; i < cstr.length; ++i) {
|
||||
@ -19,7 +18,6 @@ function decode_cell(cstr/*:string*/)/*:CellAddress*/ {
|
||||
}
|
||||
return { c: C - 1, r:R - 1 };
|
||||
}
|
||||
//function encode_cell(cell/*:CellAddress*/)/*:string*/ { return encode_col(cell.c) + encode_row(cell.r); }
|
||||
function encode_cell(cell/*:CellAddress*/)/*:string*/ {
|
||||
var col = cell.c + 1;
|
||||
var s="";
|
||||
@ -110,11 +108,19 @@ function sheet_to_workbook(sheet/*:Worksheet*/, opts)/*:Workbook*/ {
|
||||
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/*:?Worksheet*/, data/*:AOA*/, opts/*:?any*/)/*:Worksheet*/ {
|
||||
var o = opts || {};
|
||||
var dense = _ws ? Array.isArray(_ws) : o.dense;
|
||||
var dense = _ws ? (_ws["!data"] != null) : o.dense;
|
||||
if(DENSE != null && dense == null) dense = DENSE;
|
||||
var ws/*:Worksheet*/ = _ws || (dense ? ([]/*:any*/) : ({}/*:any*/));
|
||||
var ws/*:Worksheet*/ = _ws || ({}/*:any*/);
|
||||
if(dense && !ws["!data"]) ws["!data"] = [];
|
||||
var _R = 0, _C = 0;
|
||||
if(ws && o.origin != null) {
|
||||
if(typeof o.origin == 'number') _R = o.origin;
|
||||
@ -133,13 +139,19 @@ function sheet_add_aoa(_ws/*:?Worksheet*/, data/*:AOA*/, opts/*:?any*/)/*:Worksh
|
||||
range.e.r = Math.max(range.e.r, _range.e.r);
|
||||
if(_R == -1) range.e.r = _R = _range.e.r + 1;
|
||||
}
|
||||
var row = [];
|
||||
for(var R = 0; R != data.length; ++R) {
|
||||
if(!data[R]) continue;
|
||||
if(!Array.isArray(data[R])) throw new Error("aoa_to_sheet expects an array of arrays");
|
||||
var __R = _R + R, __Rstr = "" + (__R + 1);
|
||||
if(dense) {
|
||||
if(!ws["!data"][__R]) ws["!data"][__R] = [];
|
||||
row = ws["!data"][__R];
|
||||
}
|
||||
for(var C = 0; C != data[R].length; ++C) {
|
||||
if(typeof data[R][C] === 'undefined') continue;
|
||||
var cell/*:Cell*/ = ({v: data[R][C] }/*:any*/);
|
||||
var __R = _R + R, __C = _C + C;
|
||||
var __C = _C + C;
|
||||
if(range.s.r > __R) range.s.r = __R;
|
||||
if(range.s.c > __C) range.s.c = __C;
|
||||
if(range.e.r < __R) range.e.r = __R;
|
||||
@ -157,17 +169,17 @@ function sheet_add_aoa(_ws/*:?Worksheet*/, data/*:AOA*/, opts/*:?any*/)/*:Worksh
|
||||
else if(typeof cell.v === 'boolean') cell.t = 'b';
|
||||
else if(cell.v instanceof Date) {
|
||||
cell.z = o.dateNF || table_fmt[14];
|
||||
if(o.cellDates) { cell.t = 'd'; cell.w = SSF_format(cell.z, datenum(cell.v)); }
|
||||
else { cell.t = 'n'; cell.v = datenum(cell.v); cell.w = SSF_format(cell.z, cell.v); }
|
||||
if(!o.UTC) cell.v = local_to_utc(cell.v);
|
||||
if(o.cellDates) { cell.t = 'd'; cell.w = SSF_format(cell.z, datenum(cell.v, o.date1904)); }
|
||||
else { cell.t = 'n'; cell.v = datenum(cell.v, o.date1904); cell.w = SSF_format(cell.z, cell.v); }
|
||||
}
|
||||
else cell.t = 's';
|
||||
}
|
||||
if(dense) {
|
||||
if(!ws[__R]) ws[__R] = [];
|
||||
if(ws[__R][__C] && ws[__R][__C].z) cell.z = ws[__R][__C].z;
|
||||
ws[__R][__C] = cell;
|
||||
if(row[__C] && row[__C].z) cell.z = row[__C].z;
|
||||
row[__C] = cell;
|
||||
} else {
|
||||
var cell_ref = encode_cell(({c:__C,r:__R}/*:any*/));
|
||||
var cell_ref = encode_col(__C) + __Rstr/*:any*/;
|
||||
if(ws[cell_ref] && ws[cell_ref].z) cell.z = ws[cell_ref].z;
|
||||
ws[cell_ref] = cell;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ function parse_rels(data/*:?string*/, currentFilePath/*:string*/) {
|
||||
var y = parsexmltag(x);
|
||||
/* 9.3.2.2 OPC_Relationships */
|
||||
if (y[0] === '<Relationship') {
|
||||
var rel = {}; rel.Type = y.Type; rel.Target = y.Target; rel.Id = y.Id; if(y.TargetMode) rel.TargetMode = y.TargetMode;
|
||||
var rel = {}; rel.Type = y.Type; rel.Target = unescapexml(y.Target); rel.Id = y.Id; if(y.TargetMode) rel.TargetMode = y.TargetMode;
|
||||
var canonictarget = y.TargetMode === 'External' ? y.Target : resolve_path(y.Target, currentFilePath);
|
||||
rels[canonictarget] = rel;
|
||||
hash[y.Id] = rel;
|
||||
|
@ -1,62 +1,61 @@
|
||||
/* Open Document Format for Office Applications (OpenDocument) Version 1.2 */
|
||||
/* Part 3 Section 4 Manifest File */
|
||||
var CT_ODS = "application/vnd.oasis.opendocument.spreadsheet";
|
||||
function parse_manifest(d, opts) {
|
||||
var str = xlml_normalize(d);
|
||||
var Rn;
|
||||
var FEtag;
|
||||
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
|
||||
case 'manifest': break; // 4.2 <manifest:manifest>
|
||||
case 'file-entry': // 4.3 <manifest:file-entry>
|
||||
FEtag = parsexmltag(Rn[0], false);
|
||||
if(FEtag.path == '/' && FEtag.type !== CT_ODS) throw new Error("This OpenDocument is not a spreadsheet");
|
||||
break;
|
||||
case 'encryption-data': // 4.4 <manifest:encryption-data>
|
||||
case 'algorithm': // 4.5 <manifest:algorithm>
|
||||
case 'start-key-generation': // 4.6 <manifest:start-key-generation>
|
||||
case 'key-derivation': // 4.7 <manifest:key-derivation>
|
||||
throw new Error("Unsupported ODS Encryption");
|
||||
default: if(opts && opts.WTF) throw Rn;
|
||||
}
|
||||
var str = xlml_normalize(d);
|
||||
var Rn;
|
||||
var FEtag;
|
||||
while (Rn = xlmlregex.exec(str))
|
||||
switch (Rn[3]) {
|
||||
case "manifest":
|
||||
break;
|
||||
case "file-entry":
|
||||
FEtag = parsexmltag(Rn[0], false);
|
||||
if (FEtag.path == "/" && FEtag.type !== CT_ODS)
|
||||
throw new Error("This OpenDocument is not a spreadsheet");
|
||||
break;
|
||||
case "encryption-data":
|
||||
case "algorithm":
|
||||
case "start-key-generation":
|
||||
case "key-derivation":
|
||||
throw new Error("Unsupported ODS Encryption");
|
||||
default:
|
||||
if (opts && opts.WTF)
|
||||
throw Rn;
|
||||
}
|
||||
}
|
||||
|
||||
function write_manifest(manifest/*:Array<Array<string> >*/)/*:string*/ {
|
||||
var o = [XML_HEADER];
|
||||
o.push('<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.2">\n');
|
||||
o.push(' <manifest:file-entry manifest:full-path="/" manifest:version="1.2" manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/>\n');
|
||||
for(var i = 0; i < manifest.length; ++i) o.push(' <manifest:file-entry manifest:full-path="' + manifest[i][0] + '" manifest:media-type="' + manifest[i][1] + '"/>\n');
|
||||
o.push('</manifest:manifest>');
|
||||
return o.join("");
|
||||
function write_manifest(manifest) {
|
||||
var o = [XML_HEADER];
|
||||
o.push('<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.2">\n');
|
||||
o.push(' <manifest:file-entry manifest:full-path="/" manifest:version="1.2" manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/>\n');
|
||||
for (var i = 0; i < manifest.length; ++i)
|
||||
o.push(' <manifest:file-entry manifest:full-path="' + manifest[i][0] + '" manifest:media-type="' + manifest[i][1] + '"/>\n');
|
||||
o.push("</manifest:manifest>");
|
||||
return o.join("");
|
||||
}
|
||||
|
||||
/* Part 3 Section 6 Metadata Manifest File */
|
||||
function write_rdf_type(file/*:string*/, res/*:string*/, tag/*:?string*/) {
|
||||
return [
|
||||
' <rdf:Description rdf:about="' + file + '">\n',
|
||||
' <rdf:type rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/' + (tag || "odf") + '#' + res + '"/>\n',
|
||||
' </rdf:Description>\n'
|
||||
].join("");
|
||||
function write_rdf_type(file, res, tag) {
|
||||
return [
|
||||
' <rdf:Description rdf:about="' + file + '">\n',
|
||||
' <rdf:type rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/' + (tag || "odf") + "#" + res + '"/>\n',
|
||||
" </rdf:Description>\n"
|
||||
].join("");
|
||||
}
|
||||
function write_rdf_has(base/*:string*/, file/*:string*/) {
|
||||
return [
|
||||
' <rdf:Description rdf:about="' + base + '">\n',
|
||||
' <ns0:hasPart xmlns:ns0="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#" rdf:resource="' + file + '"/>\n',
|
||||
' </rdf:Description>\n'
|
||||
].join("");
|
||||
function write_rdf_has(base, file) {
|
||||
return [
|
||||
' <rdf:Description rdf:about="' + base + '">\n',
|
||||
' <ns0:hasPart xmlns:ns0="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#" rdf:resource="' + file + '"/>\n',
|
||||
" </rdf:Description>\n"
|
||||
].join("");
|
||||
}
|
||||
function write_rdf(rdf) {
|
||||
var o = [XML_HEADER];
|
||||
o.push('<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">\n');
|
||||
for(var i = 0; i != rdf.length; ++i) {
|
||||
o.push(write_rdf_type(rdf[i][0], rdf[i][1]));
|
||||
o.push(write_rdf_has("",rdf[i][0]));
|
||||
}
|
||||
o.push(write_rdf_type("","Document", "pkg"));
|
||||
o.push('</rdf:RDF>');
|
||||
return o.join("");
|
||||
var o = [XML_HEADER];
|
||||
o.push('<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">\n');
|
||||
for (var i = 0; i != rdf.length; ++i) {
|
||||
o.push(write_rdf_type(rdf[i][0], rdf[i][1]));
|
||||
o.push(write_rdf_has("", rdf[i][0]));
|
||||
}
|
||||
o.push(write_rdf_type("", "Document", "pkg"));
|
||||
o.push("</rdf:RDF>");
|
||||
return o.join("");
|
||||
}
|
||||
/* TODO: pull properties */
|
||||
function write_meta_ods(/*:: wb: Workbook, opts: any*/)/*:string*/ {
|
||||
return '<office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink" office:version="1.2"><office:meta><meta:generator>Sheet' + 'JS ' + XLSX.version + '</meta:generator></office:meta></office:document-meta>';
|
||||
function write_meta_ods(wb, opts) {
|
||||
return '<office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink" office:version="1.2"><office:meta><meta:generator>SheetJS ' + XLSX.version + "</meta:generator></office:meta></office:document-meta>";
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ function parse_cust_props(data/*:string*/, opts) {
|
||||
var m = data.match(custregex);
|
||||
if(m) for(var i = 0; i != m.length; ++i) {
|
||||
var x = m[i], y = parsexmltag(x);
|
||||
switch(y[0]) {
|
||||
switch(strip_ns(y[0])) {
|
||||
case '<?xml': break;
|
||||
case '<Properties': break;
|
||||
case '<property': name = unescapexml(y.name); break;
|
||||
|
@ -1,9 +1,20 @@
|
||||
/* [MS-XLS] 2.5.19 */
|
||||
function parse_XLSCell(blob/*::, length*/)/*:Cell*/ {
|
||||
function parse_XLSCell(blob, length, opts)/*:Cell*/ {
|
||||
var rw = blob.read_shift(2); // 0-indexed
|
||||
var col = blob.read_shift(2);
|
||||
var ixfe = blob.read_shift(2);
|
||||
return ({r:rw, c:col, ixfe:ixfe}/*:any*/);
|
||||
var ret = ({r:rw, c:col, ixfe:0}/*:any*/);
|
||||
if(opts && opts.biff == 2 || length == 7) {
|
||||
/* TODO: pass back flags */
|
||||
var flags = blob.read_shift(1);
|
||||
ret.ixfe = flags & 0x3F;
|
||||
blob.l += 2;
|
||||
/*
|
||||
var ifntifmt = blob.read_shift(1);
|
||||
var ifmt = ifntifmt & 0x3f, ifnt = ifntifmt >> 6;
|
||||
var flags3 = blob.read_shift(1);
|
||||
*/
|
||||
} else ret.ixfe = blob.read_shift(2);
|
||||
return ret;
|
||||
}
|
||||
function write_XLSCell(R/*:number*/, C/*:number*/, ixfe/*:?number*/, o) {
|
||||
if(!o) o = new_buf(6);
|
||||
@ -138,7 +149,7 @@ function parse_FtArray(blob, length/*::, ot*/) {
|
||||
var ft = blob.read_shift(2);
|
||||
blob.l-=2;
|
||||
try {
|
||||
fts.push(FtTab[ft](blob, tgt - blob.l));
|
||||
fts[ft] = FtTab[ft](blob, tgt - blob.l);
|
||||
} catch(e) { blob.l = tgt; return fts; }
|
||||
}
|
||||
if(blob.l != tgt) blob.l = tgt; //throw new Error("bad Object Ft-sequence");
|
||||
@ -228,6 +239,12 @@ function parse_WsBool(blob, length, opts) {
|
||||
|
||||
/* [MS-XLS] 2.4.28 */
|
||||
function parse_BoundSheet8(blob, length, opts) {
|
||||
var name = "";
|
||||
if(opts.biff == 4) {
|
||||
name = parse_ShortXLUnicodeString(blob, 0, opts);
|
||||
if(name.length === 0) name = "Sheet1";
|
||||
return { name:name };
|
||||
}
|
||||
var pos = blob.read_shift(4);
|
||||
var hidden = blob.read_shift(1) & 0x03;
|
||||
var dt = blob.read_shift(1);
|
||||
@ -237,7 +254,7 @@ function parse_BoundSheet8(blob, length, opts) {
|
||||
case 2: dt = 'Chartsheet'; break;
|
||||
case 6: dt = 'VBAModule'; break;
|
||||
}
|
||||
var name = parse_ShortXLUnicodeString(blob, 0, opts);
|
||||
name = parse_ShortXLUnicodeString(blob, 0, opts);
|
||||
if(name.length === 0) name = "Sheet1";
|
||||
return { pos:pos, hs:hidden, dt:dt, name:name };
|
||||
}
|
||||
@ -408,8 +425,8 @@ function write_Font(data, opts) {
|
||||
}
|
||||
|
||||
/* [MS-XLS] 2.4.149 */
|
||||
function parse_LabelSst(blob) {
|
||||
var cell = parse_XLSCell(blob);
|
||||
function parse_LabelSst(blob, length, opts) {
|
||||
var cell = parse_XLSCell(blob, length, opts);
|
||||
cell.isst = blob.read_shift(4);
|
||||
return cell;
|
||||
}
|
||||
@ -424,8 +441,7 @@ function write_LabelSst(R/*:number*/, C/*:number*/, v/*:number*/, os/*:number*/
|
||||
function parse_Label(blob, length, opts) {
|
||||
if(opts.biffguess && opts.biff == 2) opts.biff = 5;
|
||||
var target = blob.l + length;
|
||||
var cell = parse_XLSCell(blob, 6);
|
||||
if(opts.biff == 2) blob.l++;
|
||||
var cell = parse_XLSCell(blob, length, opts);
|
||||
var str = parse_XLUnicodeString(blob, target - blob.l, opts);
|
||||
cell.val = str;
|
||||
return cell;
|
||||
@ -459,6 +475,19 @@ function write_Format(i/*:number*/, f/*:string*/, opts, o) {
|
||||
return out;
|
||||
}
|
||||
var parse_BIFF2Format = parse_XLUnicodeString2;
|
||||
function write_BIFF2Format(f/*:string*/) {
|
||||
var o = new_buf(1 + f.length);
|
||||
o.write_shift(1, f.length);
|
||||
o.write_shift(f.length, f, "sbcs");
|
||||
return o;
|
||||
}
|
||||
function write_BIFF4Format(f/*:string*/) {
|
||||
var o = new_buf(3 + f.length);
|
||||
o.l += 2;
|
||||
o.write_shift(1, f.length);
|
||||
o.write_shift(f.length, f, "sbcs");
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-XLS] 2.4.90 */
|
||||
function parse_Dimensions(blob, length, opts) {
|
||||
@ -582,6 +611,44 @@ function write_XF(data, ixfeP, opts, o) {
|
||||
o.write_shift(2, 0);
|
||||
return o;
|
||||
}
|
||||
function parse_BIFF2XF(blob/*::, length, opts*/) {
|
||||
var o = {};
|
||||
o.ifnt = blob.read_shift(1); blob.l++; o.flags = blob.read_shift(1);
|
||||
o.numFmtId = o.flags & 0x3F; o.flags>>=6;
|
||||
o.fStyle = 0;
|
||||
o.data = {}; // TODO
|
||||
return o;
|
||||
}
|
||||
function write_BIFF2XF(xf) {
|
||||
var o = new_buf(4);
|
||||
o.l+=2;
|
||||
o.write_shift(1, xf.numFmtId);
|
||||
o.l++;
|
||||
return o;
|
||||
}
|
||||
function write_BIFF3XF(xf) {
|
||||
var o = new_buf(12);
|
||||
o.l++;
|
||||
o.write_shift(1, xf.numFmtId);
|
||||
o.l += 10;
|
||||
return o;
|
||||
}
|
||||
/* TODO: check other fields */
|
||||
var write_BIFF4XF = write_BIFF3XF;
|
||||
function parse_BIFF3XF(blob/*::, length, opts*/) {
|
||||
var o = {};
|
||||
o.ifnt = blob.read_shift(1); o.numFmtId = blob.read_shift(1); o.flags = blob.read_shift(2);
|
||||
o.fStyle = (o.flags >> 2) & 0x01;
|
||||
o.data = {}; // TODO
|
||||
return o;
|
||||
}
|
||||
function parse_BIFF4XF(blob/*::, length, opts*/) {
|
||||
var o = {};
|
||||
o.ifnt = blob.read_shift(1); o.numFmtId = blob.read_shift(1); o.flags = blob.read_shift(2);
|
||||
o.fStyle = (o.flags >> 2) & 0x01;
|
||||
o.data = {}; // TODO
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-XLS] 2.4.134 */
|
||||
function parse_Guts(blob) {
|
||||
@ -602,8 +669,7 @@ function write_Guts(guts/*:Array<number>*/) {
|
||||
|
||||
/* [MS-XLS] 2.4.24 */
|
||||
function parse_BoolErr(blob, length, opts) {
|
||||
var cell = parse_XLSCell(blob, 6);
|
||||
if(opts.biff == 2 || length == 9) ++blob.l;
|
||||
var cell = parse_XLSCell(blob, 6, opts);
|
||||
var val = parse_Bes(blob, 2);
|
||||
cell.val = val;
|
||||
cell.t = (val === true || val === false) ? 'b' : 'e';
|
||||
@ -619,7 +685,7 @@ function write_BoolErr(R/*:number*/, C/*:number*/, v, os/*:number*/, opts, t/*:s
|
||||
/* [MS-XLS] 2.4.180 Number */
|
||||
function parse_Number(blob, length, opts) {
|
||||
if(opts.biffguess && opts.biff == 2) opts.biff = 5;
|
||||
var cell = parse_XLSCell(blob, 6);
|
||||
var cell = parse_XLSCell(blob, 6, opts);
|
||||
var xnum = parse_Xnum(blob, 8);
|
||||
cell.val = xnum;
|
||||
return cell;
|
||||
@ -696,9 +762,11 @@ function parse_Lbl(blob, length, opts) {
|
||||
};
|
||||
}
|
||||
|
||||
/* [MS-XLS] 2.4.106 TODO: verify filename encoding */
|
||||
/* [MS-XLS] 2.4.106 TODO: legacy record filename encoding */
|
||||
function parse_ExternSheet(blob, length, opts) {
|
||||
if(opts.biff < 8) return parse_BIFF5ExternSheet(blob, length, opts);
|
||||
/* see issue 2907 */
|
||||
if(!(opts.biff > 8) && (length == blob[blob.l] + (blob[blob.l+1] == 0x03 ? 1 : 0) + 1)) return parse_BIFF5ExternSheet(blob, length, opts);
|
||||
var o = [], target = blob.l + length, len = blob.read_shift(opts.biff > 8 ? 4 : 2);
|
||||
while(len-- !== 0) o.push(parse_XTI(blob, opts.biff > 8 ? 12 : 6, opts));
|
||||
// [iSupBook, itabFirst, itabLast];
|
||||
@ -750,21 +818,34 @@ function parse_MTRSettings(blob) {
|
||||
return [fMTREnabled, fUserSetThreadCount, cUserThreadCount];
|
||||
}
|
||||
|
||||
/* [MS-XLS] 2.5.186 TODO: BIFF5 */
|
||||
/* [MS-XLS] 2.5.186 */
|
||||
function parse_NoteSh(blob, length, opts) {
|
||||
if(opts.biff < 8) return;
|
||||
var row = blob.read_shift(2), col = blob.read_shift(2);
|
||||
var flags = blob.read_shift(2), idObj = blob.read_shift(2);
|
||||
var stAuthor = parse_XLUnicodeString2(blob, 0, opts);
|
||||
if(opts.biff < 8) blob.read_shift(1);
|
||||
return [{r:row,c:col}, stAuthor, idObj, flags];
|
||||
}
|
||||
|
||||
/* [MS-XLS] 2.4.179 */
|
||||
function parse_Note(blob, length, opts) {
|
||||
if(opts && (opts.biff < 8)) {
|
||||
var row = blob.read_shift(2), col = blob.read_shift(2);
|
||||
if(row == 0xFFFF || row == -1) return; // TODO: test continuation
|
||||
var cch = blob.read_shift(2);
|
||||
var cmnt = blob.read_shift(Math.min(cch,2048), 'cpstr');
|
||||
return [{r:row, c:col}, cmnt];
|
||||
}
|
||||
/* TODO: Support revisions */
|
||||
return parse_NoteSh(blob, length, opts);
|
||||
}
|
||||
function write_NOTE_BIFF2(text/*:string*/, R/*:number*/, C/*:number*/, len/*?:number*/) {
|
||||
var o = new_buf(6 + (len || text.length));
|
||||
o.write_shift(2, R);
|
||||
o.write_shift(2, C);
|
||||
o.write_shift(2, len || text.length);
|
||||
o.write_shift(text.length, text, "sbcs");
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-XLS] 2.4.168 */
|
||||
function parse_MergeCells(blob, length)/*:Array<Range>*/ {
|
||||
@ -1014,43 +1095,50 @@ function parse_ImData(blob) {
|
||||
return o;
|
||||
}
|
||||
|
||||
function write_BIFF2Cell(out, r/*:number*/, c/*:number*/, ixfe/*:number*/, ifmt/*:number*/) {
|
||||
if(!out) out = new_buf(7);
|
||||
out.write_shift(2, r);
|
||||
out.write_shift(2, c);
|
||||
out.write_shift(1, ixfe||0/* & 0x3F */);
|
||||
out.write_shift(1, ifmt||0/* & 0x3F */);
|
||||
out.write_shift(1, 0);
|
||||
return out;
|
||||
}
|
||||
|
||||
/* BIFF2_??? where ??? is the name from [XLS] */
|
||||
function parse_BIFF2STR(blob, length, opts) {
|
||||
if(opts.biffguess && opts.biff == 5) opts.biff = 2;
|
||||
var cell = parse_XLSCell(blob, 6);
|
||||
++blob.l;
|
||||
var cell = parse_XLSCell(blob, 7, opts);
|
||||
var str = parse_XLUnicodeString2(blob, length-7, opts);
|
||||
cell.t = 'str';
|
||||
cell.val = str;
|
||||
return cell;
|
||||
}
|
||||
|
||||
function parse_BIFF2NUM(blob/*::, length*/) {
|
||||
var cell = parse_XLSCell(blob, 6);
|
||||
++blob.l;
|
||||
function parse_BIFF2NUM(blob, length, opts) {
|
||||
var cell = parse_XLSCell(blob, 7, opts);
|
||||
var num = parse_Xnum(blob, 8);
|
||||
cell.t = 'n';
|
||||
cell.val = num;
|
||||
return cell;
|
||||
}
|
||||
function write_BIFF2NUM(r/*:number*/, c/*:number*/, val/*:number*/) {
|
||||
function write_BIFF2NUM(r/*:number*/, c/*:number*/, val/*:number*/, ixfe, ifmt) {
|
||||
var out = new_buf(15);
|
||||
write_BIFF2Cell(out, r, c);
|
||||
write_BIFF2Cell(out, r, c, ixfe||0, ifmt||0);
|
||||
out.write_shift(8, val, 'f');
|
||||
return out;
|
||||
}
|
||||
|
||||
function parse_BIFF2INT(blob) {
|
||||
var cell = parse_XLSCell(blob, 6);
|
||||
++blob.l;
|
||||
function parse_BIFF2INT(blob, length, opts) {
|
||||
var cell = parse_XLSCell(blob, 7, opts);
|
||||
var num = blob.read_shift(2);
|
||||
cell.t = 'n';
|
||||
cell.val = num;
|
||||
return cell;
|
||||
}
|
||||
function write_BIFF2INT(r/*:number*/, c/*:number*/, val/*:number*/) {
|
||||
function write_BIFF2INT(r/*:number*/, c/*:number*/, val/*:number*/, ixfe/*:number*/, ifmt/*:number*/) {
|
||||
var out = new_buf(9);
|
||||
write_BIFF2Cell(out, r, c);
|
||||
write_BIFF2Cell(out, r, c, ixfe||0, ifmt||0);
|
||||
out.write_shift(2, val);
|
||||
return out;
|
||||
}
|
||||
@ -1061,6 +1149,16 @@ function parse_BIFF2STRING(blob) {
|
||||
return blob.read_shift(cch, 'sbcs-cont');
|
||||
}
|
||||
|
||||
function parse_BIFF2BOOLERR(blob, length, opts) {
|
||||
var bestart = blob.l + 7;
|
||||
var cell = parse_XLSCell(blob, 6, opts);
|
||||
blob.l = bestart;
|
||||
var val = parse_Bes(blob, 2);
|
||||
cell.val = val;
|
||||
cell.t = (val === true || val === false) ? 'b' : 'e';
|
||||
return cell;
|
||||
}
|
||||
|
||||
/* TODO: convert to BIFF8 font struct */
|
||||
function parse_BIFF2FONTXTRA(blob, length) {
|
||||
blob.l += 6; // unknown
|
||||
@ -1074,7 +1172,7 @@ function parse_BIFF2FONTXTRA(blob, length) {
|
||||
/* TODO: parse rich text runs */
|
||||
function parse_RString(blob, length, opts) {
|
||||
var end = blob.l + length;
|
||||
var cell = parse_XLSCell(blob, 6);
|
||||
var cell = parse_XLSCell(blob, 6, opts);
|
||||
var cch = blob.read_shift(2);
|
||||
var str = parse_XLUnicodeStringNoCch(blob, cch, opts);
|
||||
blob.l = end;
|
||||
@ -1082,3 +1180,10 @@ function parse_RString(blob, length, opts) {
|
||||
cell.val = str;
|
||||
return cell;
|
||||
}
|
||||
|
||||
function parse_BIFF4SheetInfo(blob/*::, length, opts*/) {
|
||||
var flags = blob.read_shift(4);
|
||||
var cch = blob.read_shift(1), name = blob.read_shift(cch, "sbcs");
|
||||
if(name.length === 0) name = "Sheet1";
|
||||
return { flags: flags, name:name };
|
||||
}
|
311
bits/40_harb.js
311
bits/40_harb.js
@ -1,4 +1,3 @@
|
||||
/* from js-harb (C) 2014-present SheetJS */
|
||||
var DBF_SUPPORTED_VERSIONS = [0x02, 0x03, 0x30, 0x31, 0x83, 0x8B, 0x8C, 0xF5];
|
||||
var DBF = /*#__PURE__*/(function() {
|
||||
var dbf_codepage_map = {
|
||||
@ -119,7 +118,7 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
|
||||
var ww = l7 ? 32 : 11;
|
||||
while(d.l < hend && d[d.l] != 0x0d) {
|
||||
field = ({}/*:any*/);
|
||||
field.name = $cptable.utils.decode(current_cp, d.slice(d.l, d.l+ww)).replace(/[\u0000\r\n].*$/g,"");
|
||||
field.name = (typeof $cptable !== "undefined" ? $cptable.utils.decode(current_cp, d.slice(d.l, d.l+ww)) : a2s(d.slice(d.l, d.l + ww))).replace(/[\u0000\r\n].*$/g,"");
|
||||
d.l += ww;
|
||||
field.type = String.fromCharCode(d.read_shift(1));
|
||||
if(ft != 0x02 && !l7) field.offset = d.read_shift(4);
|
||||
@ -173,14 +172,17 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
|
||||
for(C = 0; C != fields.length; ++C) {
|
||||
var dd = d.slice(d.l, d.l+fields[C].len); d.l+=fields[C].len;
|
||||
prep_blob(dd, 0);
|
||||
var s = $cptable.utils.decode(current_cp, dd);
|
||||
var s = typeof $cptable !== "undefined" ? $cptable.utils.decode(current_cp, dd) : a2s(dd);
|
||||
switch(fields[C].type) {
|
||||
case 'C':
|
||||
// NOTE: it is conventional to write ' / / ' for empty dates
|
||||
if(s.trim().length) out[R][C] = s.replace(/\s+$/,"");
|
||||
break;
|
||||
case 'D':
|
||||
if(s.length === 8) out[R][C] = new Date(+s.slice(0,4), +s.slice(4,6)-1, +s.slice(6,8));
|
||||
if(s.length === 8) {
|
||||
out[R][C] = new Date(Date.UTC(+s.slice(0,4), +s.slice(4,6)-1, +s.slice(6,8), 0, 0, 0, 0));
|
||||
if(!(opts && opts.UTC)) { out[R][C] = utc_to_local(out[R][C]); }
|
||||
}
|
||||
else out[R][C] = s;
|
||||
break;
|
||||
case 'F': out[R][C] = parseFloat(s.trim()); break;
|
||||
@ -188,7 +190,7 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
|
||||
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 */
|
||||
@ -203,7 +205,12 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
|
||||
// NOTE: dBASE specs appear to be incorrect
|
||||
out[R][C] = new Date(dd.read_shift(-8, 'f') - 0x388317533400);
|
||||
break;
|
||||
case 'T': out[R][C] = new Date((dd.read_shift(4) - 0x253D8C) * 0x5265C00 + dd.read_shift(4)); break;
|
||||
case 'T': {
|
||||
var hi = dd.read_shift(4), lo = dd.read_shift(4);
|
||||
if(hi == 0 && lo == 0) break;
|
||||
out[R][C] = new Date((hi - 0x253D8C) * 0x5265C00 + lo);
|
||||
if(!(opts && opts.UTC)) out[R][C] = utc_to_local(out[R][C]);
|
||||
} break;
|
||||
case 'Y': out[R][C] = dd.read_shift(4,'i')/1e4 + (dd.read_shift(4, 'i')/1e4)*Math.pow(2,32); break;
|
||||
case 'O': out[R][C] = -dd.read_shift(-8, 'f'); break;
|
||||
case 'B': if(vfp && fields[C].len == 8) { out[R][C] = dd.read_shift(8,'f'); break; }
|
||||
@ -235,14 +242,19 @@ function dbf_to_sheet(buf, opts)/*:Worksheet*/ {
|
||||
}
|
||||
|
||||
function dbf_to_workbook(buf, opts)/*:Workbook*/ {
|
||||
try { return sheet_to_workbook(dbf_to_sheet(buf, opts), opts); }
|
||||
catch(e) { if(opts && opts.WTF) throw e; }
|
||||
try {
|
||||
var o = sheet_to_workbook(dbf_to_sheet(buf, opts), opts);
|
||||
o.bookType = "dbf";
|
||||
return o;
|
||||
} catch(e) { if(opts && opts.WTF) throw e; }
|
||||
return ({SheetNames:[],Sheets:{}});
|
||||
}
|
||||
|
||||
var _RLEN = { 'B': 8, 'C': 250, 'L': 1, 'D': 8, '?': 0, '': 0 };
|
||||
function sheet_to_dbf(ws/*:Worksheet*/, opts/*:WriteOpts*/) {
|
||||
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);
|
||||
if(o.type == "string") throw new Error("Cannot write DBF to JS string");
|
||||
var ba = buf_array();
|
||||
@ -278,7 +290,8 @@ function sheet_to_dbf(ws/*:Worksheet*/, opts/*:WriteOpts*/) {
|
||||
case 'object': _guess = col[j] instanceof Date ? 'D' : 'C'; break;
|
||||
default: _guess = 'C';
|
||||
}
|
||||
maxlen = Math.max(maxlen, String(col[j]).length);
|
||||
/* TODO: cache the values instead of encoding twice */
|
||||
maxlen = Math.max(maxlen, (typeof $cptable !== "undefined" && typeof col[j] == "string" ? $cptable.utils.encode(current_ansi, col[j]): String(col[j])).length);
|
||||
guess = guess && guess != _guess ? 'C' : _guess;
|
||||
//if(guess == 'C') break;
|
||||
}
|
||||
@ -304,11 +317,17 @@ function sheet_to_dbf(ws/*:Worksheet*/, opts/*:WriteOpts*/) {
|
||||
h.write_shift(2, 296 + 32 * hcnt);
|
||||
h.write_shift(2, rlen);
|
||||
for(i=0; i < 4; ++i) h.write_shift(4, 0);
|
||||
h.write_shift(4, 0x00000000 | ((+dbf_reverse_map[/*::String(*/current_ansi/*::)*/] || 0x03)<<8));
|
||||
var cp = +dbf_reverse_map[/*::String(*/current_codepage/*::)*/] || 0x03;
|
||||
h.write_shift(4, 0x00000000 | (cp<<8));
|
||||
if(dbf_codepage_map[cp] != +o.codepage) {
|
||||
if(o.codepage) console.error("DBF Unsupported codepage " + current_codepage + ", using 1252");
|
||||
current_codepage = 1252;
|
||||
}
|
||||
|
||||
for(i = 0, j = 0; i < headers.length; ++i) {
|
||||
if(headers[i] == null) continue;
|
||||
var hf = ba.next(32);
|
||||
/* TODO: test how applications handle non-ASCII field names */
|
||||
var _f = (headers[i].slice(-10) + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00").slice(0, 11);
|
||||
hf.write_shift(1, _f, "sbcs");
|
||||
hf.write_shift(1, coltypes[i] == '?' ? 'C' : coltypes[i], "sbcs");
|
||||
@ -337,6 +356,7 @@ function sheet_to_dbf(ws/*:Worksheet*/, opts/*:WriteOpts*/) {
|
||||
case 'N':
|
||||
var _n = "0";
|
||||
if(typeof data[i][j] == "number") _n = data[i][j].toFixed(coldecimals[j]||0);
|
||||
if(_n.length > colwidths[j]) _n = _n.slice(0, colwidths[j]); // addresses decimal > width
|
||||
for(hcnt=0; hcnt < colwidths[j]-_n.length; ++hcnt) rout.write_shift(1, 0x20);
|
||||
rout.write_shift(1, _n, "sbcs");
|
||||
break;
|
||||
@ -348,13 +368,16 @@ function sheet_to_dbf(ws/*:Worksheet*/, opts/*:WriteOpts*/) {
|
||||
rout.write_shift(2, ("00"+data[i][j].getDate()).slice(-2), "sbcs");
|
||||
} break;
|
||||
case 'C':
|
||||
var _l = rout.l;
|
||||
var _s = String(data[i][j] != null ? data[i][j] : "").slice(0, colwidths[j]);
|
||||
rout.write_shift(1, _s, "sbcs");
|
||||
for(hcnt=0; hcnt < colwidths[j]-_s.length; ++hcnt) rout.write_shift(1, 0x20); break;
|
||||
rout.write_shift(1, _s, "cpstr");
|
||||
_l += colwidths[j] - rout.l;
|
||||
for(hcnt=0; hcnt < _l; ++hcnt) rout.write_shift(1, 0x20); break;
|
||||
}
|
||||
}
|
||||
// data
|
||||
}
|
||||
current_codepage = old_cp;
|
||||
ba.next(1).write_shift(1, 0x1A);
|
||||
return ba.end();
|
||||
}
|
||||
@ -387,10 +410,15 @@ var SYLK = /*#__PURE__*/(function() {
|
||||
"!":161, '"':162, "#":163, "(":164, "%":165, "'":167, "H ":168,
|
||||
"+":171, ";":187, "<":188, "=":189, ">":190, "?":191, "{":223
|
||||
}/*:any*/);
|
||||
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;
|
||||
/* TODO: evert the escape map */
|
||||
var encode_sylk_str = function($$) { return $$.replace(/\n/g, "\x1b :").replace(/\r/g, "\x1b ="); };
|
||||
/* https://oss.sheetjs.com/notes/sylk/ for more details */
|
||||
function sylk_to_aoa(d/*:RawData*/, opts)/*:[AOA, Worksheet]*/ {
|
||||
switch(opts.type) {
|
||||
@ -439,10 +467,13 @@ var SYLK = /*#__PURE__*/(function() {
|
||||
}
|
||||
wb.Workbook.Names.push(nn);
|
||||
} break;
|
||||
// case 'NE': // ??
|
||||
// case 'NU': // ??
|
||||
case 'C': /* cell */
|
||||
var C_seen_K = false, C_seen_X = false, C_seen_S = false, C_seen_E = false, _R = -1, _C = -1;
|
||||
var C_seen_K = false, C_seen_X = false, C_seen_S = false, C_seen_E = false, _R = -1, _C = -1, formula = "", cell_t = "z";
|
||||
var cmnt = "";
|
||||
for(rj=1; rj<record.length; ++rj) switch(record[rj].charAt(0)) {
|
||||
case 'A': break; // TODO: comment
|
||||
case 'A': cmnt = record[rj].slice(1); break; // TODO: comment
|
||||
case 'X': C = parseInt(record[rj].slice(1), 10)-1; C_seen_X = true; break;
|
||||
case 'Y':
|
||||
R = parseInt(record[rj].slice(1), 10)-1; if(!C_seen_X) C = 0;
|
||||
@ -450,42 +481,51 @@ var SYLK = /*#__PURE__*/(function() {
|
||||
break;
|
||||
case 'K':
|
||||
val = record[rj].slice(1);
|
||||
if(val.charAt(0) === '"') val = val.slice(1,val.length - 1);
|
||||
else if(val === 'TRUE') val = true;
|
||||
else if(val === 'FALSE') val = false;
|
||||
if(val.charAt(0) === '"') { val = val.slice(1,val.length - 1); cell_t = "s"; }
|
||||
else if(val === 'TRUE' || val === 'FALSE') { val = val === 'TRUE'; cell_t = "b"; }
|
||||
else if(!isNaN(fuzzynum(val))) {
|
||||
val = fuzzynum(val);
|
||||
if(next_cell_format !== null && fmt_is_date(next_cell_format)) val = numdate(wb.Workbook.WBProps.date1904 ? val + 1462 : val);
|
||||
} else if(!isNaN(fuzzydate(val).getDate())) {
|
||||
val = parseDate(val);
|
||||
val = fuzzynum(val); cell_t = "n";
|
||||
if(next_cell_format !== null && fmt_is_date(next_cell_format) && opts.cellDates) {
|
||||
val = numdate(wb.Workbook.WBProps.date1904 ? val + 1462 : val); cell_t = typeof val == "number" ? "n" : "d";
|
||||
}
|
||||
}
|
||||
if(typeof $cptable !== 'undefined' && typeof val == "string" && ((opts||{}).type != "string") && (opts||{}).codepage) val = $cptable.utils.decode(opts.codepage, val);
|
||||
C_seen_K = true;
|
||||
break;
|
||||
case 'E':
|
||||
C_seen_E = true;
|
||||
var formula = rc_to_a1(record[rj].slice(1), {r:R,c:C});
|
||||
arr[R][C] = [arr[R][C], formula];
|
||||
formula = rc_to_a1(record[rj].slice(1), {r:R,c:C});
|
||||
break;
|
||||
case 'S':
|
||||
C_seen_S = true;
|
||||
arr[R][C] = [arr[R][C], "S5S"];
|
||||
break;
|
||||
case 'G': break; // unknown
|
||||
case 'R': _R = parseInt(record[rj].slice(1), 10)-1; break;
|
||||
case 'C': _C = parseInt(record[rj].slice(1), 10)-1; break;
|
||||
// case 'P': // ??
|
||||
// case 'D': // ??
|
||||
default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr);
|
||||
}
|
||||
if(C_seen_K) {
|
||||
if(arr[R][C] && arr[R][C].length == 2) arr[R][C][0] = val;
|
||||
else arr[R][C] = val;
|
||||
if(!arr[R][C]) arr[R][C] = { t: cell_t, v: val };
|
||||
else { arr[R][C].t = cell_t; arr[R][C].v = val; }
|
||||
if(next_cell_format) arr[R][C].z = next_cell_format;
|
||||
if(opts.cellText !== false && next_cell_format) arr[R][C].w = SSF_format(arr[R][C].z, arr[R][C].v, { date1904: wb.Workbook.WBProps.date1904 });
|
||||
next_cell_format = null;
|
||||
}
|
||||
if(C_seen_S) {
|
||||
if(C_seen_E) throw new Error("SYLK shared formula cannot have own formula");
|
||||
var shrbase = _R > -1 && arr[_R][_C];
|
||||
if(!shrbase || !shrbase[1]) throw new Error("SYLK shared formula cannot find base");
|
||||
arr[R][C][1] = shift_formula_str(shrbase[1], {r: R - _R, c: C - _C});
|
||||
formula = shift_formula_str(shrbase[1], {r: R - _R, c: C - _C});
|
||||
}
|
||||
if(formula) {
|
||||
if(!arr[R][C]) arr[R][C] = { t: 'n', f: formula };
|
||||
else arr[R][C].f = formula;
|
||||
}
|
||||
if(cmnt) {
|
||||
if(!arr[R][C]) arr[R][C] = { t: 'z' };
|
||||
arr[R][C].c = [{a:"SheetJSYLK", t: cmnt}];
|
||||
}
|
||||
break;
|
||||
case 'F': /* Format */
|
||||
@ -521,6 +561,8 @@ var SYLK = /*#__PURE__*/(function() {
|
||||
if(Mval > 0) { rowinfo[R].hpt = Mval; rowinfo[R].hpx = pt2px(Mval); }
|
||||
else if(Mval === 0) rowinfo[R].hidden = true;
|
||||
break;
|
||||
// case 'K': // ??
|
||||
// case 'E': // ??
|
||||
default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr);
|
||||
}
|
||||
if(F_seen < 1) next_cell_format = null; break;
|
||||
@ -537,14 +579,16 @@ var SYLK = /*#__PURE__*/(function() {
|
||||
function sylk_to_workbook(d/*:RawData*/, opts)/*:Workbook*/ {
|
||||
var aoasht = sylk_to_aoa(d, opts);
|
||||
var aoa = aoasht[0], ws = aoasht[1], wb = aoasht[2];
|
||||
var o = aoa_to_sheet(aoa, opts);
|
||||
var _opts = dup(opts); _opts.date1904 = (((wb||{}).Workbook || {}).WBProps || {}).date1904;
|
||||
var o = aoa_to_sheet(aoa, _opts);
|
||||
keys(ws).forEach(function(k) { o[k] = ws[k]; });
|
||||
var outwb = sheet_to_workbook(o, opts);
|
||||
keys(wb).forEach(function(k) { outwb[k] = wb[k]; });
|
||||
outwb.bookType = "sylk";
|
||||
return outwb;
|
||||
}
|
||||
|
||||
function write_ws_cell_sylk(cell/*:Cell*/, ws/*:Worksheet*/, R/*:number*/, C/*:number*//*::, opts*/)/*:string*/ {
|
||||
function write_ws_cell_sylk(cell/*:Cell*/, ws/*:Worksheet*/, R/*:number*/, C/*:number*/, opts, date1904/*:boolean*/)/*:string*/ {
|
||||
var o = "C;Y" + (R+1) + ";X" + (C+1) + ";K";
|
||||
switch(cell.t) {
|
||||
case 'n':
|
||||
@ -552,12 +596,19 @@ var SYLK = /*#__PURE__*/(function() {
|
||||
if(cell.f && !cell.F) o += ";E" + a1_to_rc(cell.f, {r:R, c:C}); break;
|
||||
case 'b': o += cell.v ? "TRUE" : "FALSE"; break;
|
||||
case 'e': o += cell.w || cell.v; break;
|
||||
case 'd': o += '"' + (cell.w || cell.v) + '"'; break;
|
||||
case 's': o += '"' + cell.v.replace(/"/g,"").replace(/;/g, ";;") + '"'; break;
|
||||
case 'd': o += datenum(parseDate(cell.v, date1904), date1904); break;
|
||||
case 's': o += '"' + (cell.v == null ? "" : String(cell.v)).replace(/"/g,"").replace(/;/g, ";;") + '"'; break;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
function write_ws_cmnt_sylk(cmnt/*:Comment*/, R/*:number*/, C/*:number*/)/*:string*/ {
|
||||
var o = "C;Y" + (R+1) + ";X" + (C+1) + ";A";
|
||||
/* TODO: max length? */
|
||||
o += encode_sylk_str(cmnt.map(function(c) { return c.t; }).join(""));
|
||||
return o;
|
||||
}
|
||||
|
||||
function write_ws_cols_sylk(out, cols) {
|
||||
cols.forEach(function(col, i) {
|
||||
var rec = "F;W" + (i+1) + " " + (i+1) + " ";
|
||||
@ -581,26 +632,53 @@ var SYLK = /*#__PURE__*/(function() {
|
||||
});
|
||||
}
|
||||
|
||||
function sheet_to_sylk(ws/*:Worksheet*/, opts/*:?any*/)/*:string*/ {
|
||||
function sheet_to_sylk(ws/*:Worksheet*/, opts/*:?any*/, wb/*:?WorkBook*/)/*:string*/ {
|
||||
if(!opts) opts = {}; opts._formats = ["General"];
|
||||
/* TODO: codepage */
|
||||
var preamble/*:Array<string>*/ = ["ID;PSheetJS;N;E"], o/*:Array<string>*/ = [];
|
||||
var r = safe_decode_range(ws['!ref']), cell/*:Cell*/;
|
||||
var dense = Array.isArray(ws);
|
||||
var r = safe_decode_range(ws['!ref']||"A1"), cell/*:Cell*/;
|
||||
var dense = ws["!data"] != null;
|
||||
var RS = "\r\n";
|
||||
var d1904 = (((wb||{}).Workbook||{}).WBProps||{}).date1904;
|
||||
|
||||
var _lastfmt = "General";
|
||||
preamble.push("P;PGeneral");
|
||||
/* Excel has been inconsistent in comment placement */
|
||||
var R = r.s.r, C = r.s.c, p = [];
|
||||
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) {
|
||||
cell = dense ? ws["!data"][R][C] : ws[encode_col(C) + encode_row(R)];
|
||||
if(!cell || !cell.c) continue;
|
||||
p.push(write_ws_cmnt_sylk(cell.c, R, C));
|
||||
}
|
||||
if(p.length) o.push(p.join(RS));
|
||||
}
|
||||
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) {
|
||||
cell = dense ? ws["!data"][R][C] : ws[encode_col(C) + encode_row(R)];
|
||||
if(!cell || (cell.v == null && (!cell.f || cell.F))) continue;
|
||||
if((cell.z||(cell.t == "d" ? table_fmt[14] : "General")) != _lastfmt) {
|
||||
var ifmt = opts._formats.indexOf(cell.z);
|
||||
if(ifmt == -1) { opts._formats.push(cell.z); ifmt = opts._formats.length - 1; preamble.push("P;P" + cell.z.replace(/;/g, ";;")); }
|
||||
p.push("F;P" + ifmt + ";Y" + (R+1) + ";X" + (C+1));
|
||||
}
|
||||
p.push(write_ws_cell_sylk(cell, ws, R, C, opts, d1904));
|
||||
}
|
||||
o.push(p.join(RS));
|
||||
}
|
||||
|
||||
preamble.push("F;P0;DG0G8;M255");
|
||||
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(" "));
|
||||
for(var R = r.s.r; R <= r.e.r; ++R) {
|
||||
for(var C = r.s.c; C <= r.e.c; ++C) {
|
||||
var coord = encode_cell({r:R,c:C});
|
||||
cell = dense ? (ws[R]||[])[C]: ws[coord];
|
||||
if(!cell || (cell.v == null && (!cell.f || cell.F))) continue;
|
||||
o.push(write_ws_cell_sylk(cell, ws, R, C, opts));
|
||||
}
|
||||
}
|
||||
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;
|
||||
return preamble.join(RS) + RS + o.join(RS) + RS + "E" + RS;
|
||||
}
|
||||
|
||||
@ -640,7 +718,10 @@ var DIF = /*#__PURE__*/(function() {
|
||||
if(data === 'TRUE') arr[R][C] = true;
|
||||
else if(data === 'FALSE') arr[R][C] = false;
|
||||
else if(!isNaN(fuzzynum(value))) arr[R][C] = fuzzynum(value);
|
||||
else if(!isNaN(fuzzydate(value).getDate())) arr[R][C] = parseDate(value);
|
||||
else if(!isNaN(fuzzydate(value).getDate())) {
|
||||
arr[R][C] = parseDate(value);
|
||||
if(!(opts && opts.UTC)) { arr[R][C] = utc_to_local(arr[R][C]); }
|
||||
}
|
||||
else arr[R][C] = value;
|
||||
++C; break;
|
||||
case 1:
|
||||
@ -657,64 +738,62 @@ var DIF = /*#__PURE__*/(function() {
|
||||
}
|
||||
|
||||
function dif_to_sheet(str/*:string*/, opts)/*:Worksheet*/ { return aoa_to_sheet(dif_to_aoa(str, opts), opts); }
|
||||
function dif_to_workbook(str/*:string*/, opts)/*:Workbook*/ { return sheet_to_workbook(dif_to_sheet(str, opts), opts); }
|
||||
function dif_to_workbook(str/*:string*/, opts)/*:Workbook*/ {
|
||||
var o = sheet_to_workbook(dif_to_sheet(str, opts), opts);
|
||||
o.bookType = "dif";
|
||||
return o;
|
||||
}
|
||||
|
||||
var sheet_to_dif = /*#__PURE__*/(function() {
|
||||
var push_field = function pf(o/*:Array<string>*/, topic/*:string*/, v/*:number*/, n/*:number*/, s/*:string*/) {
|
||||
o.push(topic);
|
||||
o.push(v + "," + n);
|
||||
o.push('"' + s.replace(/"/g,'""') + '"');
|
||||
};
|
||||
var push_value = function po(o/*:Array<string>*/, type/*:number*/, v/*:any*/, s/*:string*/) {
|
||||
o.push(type + "," + v);
|
||||
o.push(type == 1 ? '"' + s.replace(/"/g,'""') + '"' : s);
|
||||
};
|
||||
return function sheet_to_dif(ws/*:Worksheet*//*::, opts:?any*/)/*:string*/ {
|
||||
var o/*:Array<string>*/ = [];
|
||||
var r = safe_decode_range(ws['!ref']), cell/*:Cell*/;
|
||||
var dense = Array.isArray(ws);
|
||||
push_field(o, "TABLE", 0, 1, "sheetjs");
|
||||
push_field(o, "VECTORS", 0, r.e.r - r.s.r + 1,"");
|
||||
push_field(o, "TUPLES", 0, r.e.c - r.s.c + 1,"");
|
||||
push_field(o, "DATA", 0, 0,"");
|
||||
for(var R = r.s.r; R <= r.e.r; ++R) {
|
||||
push_value(o, -1, 0, "BOT");
|
||||
for(var C = r.s.c; C <= r.e.c; ++C) {
|
||||
var coord = encode_cell({r:R,c:C});
|
||||
cell = dense ? (ws[R]||[])[C] : ws[coord];
|
||||
if(!cell) { push_value(o, 1, 0, ""); continue;}
|
||||
switch(cell.t) {
|
||||
case 'n':
|
||||
var val = DIF_XL ? cell.w : cell.v;
|
||||
if(!val && cell.v != null) val = cell.v;
|
||||
if(val == null) {
|
||||
if(DIF_XL && cell.f && !cell.F) push_value(o, 1, 0, "=" + cell.f);
|
||||
else push_value(o, 1, 0, "");
|
||||
}
|
||||
else push_value(o, 0, val, "V");
|
||||
break;
|
||||
case 'b':
|
||||
push_value(o, 0, cell.v ? 1 : 0, cell.v ? "TRUE" : "FALSE");
|
||||
break;
|
||||
case 's':
|
||||
push_value(o, 1, 0, (!DIF_XL || isNaN(cell.v)) ? cell.v : '="' + cell.v + '"');
|
||||
break;
|
||||
case 'd':
|
||||
if(!cell.w) cell.w = SSF_format(cell.z || table_fmt[14], datenum(parseDate(cell.v)));
|
||||
if(DIF_XL) push_value(o, 0, cell.w, "V");
|
||||
else push_value(o, 1, 0, cell.w);
|
||||
break;
|
||||
default: push_value(o, 1, 0, "");
|
||||
}
|
||||
function make_value(v/*:number*/, s/*:string*/)/*:string*/ { return "0," + String(v) + "\r\n" + s; }
|
||||
function make_value_str(s/*:string*/)/*:string*/ { return "1,0\r\n\"" + s.replace(/"/g,'""') + '"'; }
|
||||
function sheet_to_dif(ws/*:Worksheet*//*::, opts:?any*/)/*:string*/ {
|
||||
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/*:Array<string>*/ = [
|
||||
"TABLE\r\n0,1\r\n\"sheetjs\"\r\n",
|
||||
"VECTORS\r\n0," + (r.e.r - r.s.r + 1) + "\r\n\"\"\r\n",
|
||||
"TUPLES\r\n0," + (r.e.c - r.s.c + 1) + "\r\n\"\"\r\n",
|
||||
"DATA\r\n0,0\r\n\"\"\r\n"
|
||||
];
|
||||
for(var R = r.s.r; R <= r.e.r; ++R) {
|
||||
var row = dense ? ws["!data"][R] : [];
|
||||
var p = "-1,0\r\nBOT\r\n";
|
||||
for(var C = r.s.c; C <= r.e.c; ++C) {
|
||||
var cell/*:Cell*/ = dense ? (row && row[C]) : ws[encode_cell({r:R,c:C})];
|
||||
if(cell == null) { p +=("1,0\r\n\"\"\r\n"); continue;}
|
||||
switch(cell.t) {
|
||||
case 'n':
|
||||
if(_DIF_XL) {
|
||||
if(cell.w != null) p +=("0," + cell.w + "\r\nV");
|
||||
else if(cell.v != null) p +=(make_value(cell.v, "V")); // TODO: should this call SSF_format?
|
||||
else if(cell.f != null && !cell.F) p +=(make_value_str("=" + cell.f));
|
||||
else p +=("1,0\r\n\"\"");
|
||||
} else {
|
||||
if(cell.v == null) p +=("1,0\r\n\"\"");
|
||||
else p +=(make_value(cell.v, "V"));
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
p +=(cell.v ? make_value(1, "TRUE") : make_value(0, "FALSE"));
|
||||
break;
|
||||
case 's':
|
||||
p +=(make_value_str((!_DIF_XL || isNaN(+cell.v)) ? cell.v : '="' + cell.v + '"'));
|
||||
break;
|
||||
case 'd':
|
||||
if(!cell.w) cell.w = SSF_format(cell.z || table_fmt[14], datenum(parseDate(cell.v)));
|
||||
if(_DIF_XL) p +=(make_value(cell.w, "V"));
|
||||
else p +=(make_value_str(cell.w));
|
||||
break;
|
||||
default: p +=("1,0\r\n\"\"");
|
||||
}
|
||||
p += "\r\n";
|
||||
}
|
||||
push_value(o, -1, 0, "EOD");
|
||||
var RS = "\r\n";
|
||||
var oo = o.join(RS);
|
||||
//while((oo.length & 0x7F) != 0) oo += "\0";
|
||||
return oo;
|
||||
};
|
||||
})();
|
||||
o.push(p);
|
||||
}
|
||||
return o.join("") + "-1,0\r\nEOD";
|
||||
}
|
||||
return {
|
||||
to_workbook: dif_to_workbook,
|
||||
to_sheet: dif_to_sheet,
|
||||
@ -777,11 +856,11 @@ var ETH = /*#__PURE__*/(function() {
|
||||
if(!ws || !ws['!ref']) return "";
|
||||
var o/*:Array<string>*/ = [], oo/*:Array<string>*/ = [], cell, coord = "";
|
||||
var r = decode_range(ws['!ref']);
|
||||
var dense = Array.isArray(ws);
|
||||
var dense = ws["!data"] != null;
|
||||
for(var R = r.s.r; R <= r.e.r; ++R) {
|
||||
for(var C = r.s.c; C <= r.e.c; ++C) {
|
||||
coord = encode_cell({r:R,c:C});
|
||||
cell = dense ? (ws[R]||[])[C] : ws[coord];
|
||||
cell = dense ? (ws["!data"][R]||[])[C] : ws[coord];
|
||||
if(!cell || cell.v == null || cell.t === 'z') continue;
|
||||
oo = ["cell", coord, 't'];
|
||||
switch(cell.t) {
|
||||
@ -903,7 +982,8 @@ var PRN = /*#__PURE__*/(function() {
|
||||
var o = opts || {};
|
||||
var sep = "";
|
||||
if(DENSE != null && o.dense == null) o.dense = DENSE;
|
||||
var ws/*:Worksheet*/ = o.dense ? ([]/*:any*/) : ({}/*:any*/);
|
||||
var ws/*:Worksheet*/ = ({}/*:any*/);
|
||||
if(o.dense) ws["!data"] = [];
|
||||
var range/*:Range*/ = ({s: {c:0, r:0}, e: {c:0, r:0}}/*:any*/);
|
||||
|
||||
if(str.slice(0,4) == "sep=") {
|
||||
@ -923,33 +1003,35 @@ var PRN = /*#__PURE__*/(function() {
|
||||
var start = 0, end = 0, sepcc = sep.charCodeAt(0), instr = false, cc=0, startcc=str.charCodeAt(0);
|
||||
var _re/*:?RegExp*/ = o.dateNF != null ? dateNF_regex(o.dateNF) : null;
|
||||
function finish_cell() {
|
||||
/* TODO: fuzzy parsers should pass back assumed number format */
|
||||
var s = str.slice(start, end); if(s.slice(-1) == "\r") s = s.slice(0, -1);
|
||||
var cell = ({}/*:any*/);
|
||||
if(s.charAt(0) == '"' && s.charAt(s.length - 1) == '"') s = s.slice(1,-1).replace(/""/g,'"');
|
||||
if(o.cellText !== false) cell.w = s;
|
||||
if(s.length === 0) cell.t = 'z';
|
||||
else if(o.raw) { cell.t = 's'; cell.v = s; }
|
||||
else if(s.trim().length === 0) { cell.t = 's'; cell.v = s; }
|
||||
else if(s.charCodeAt(0) == 0x3D) {
|
||||
if(s.charCodeAt(1) == 0x22 && s.charCodeAt(s.length - 1) == 0x22) { cell.t = 's'; cell.v = s.slice(2,-1).replace(/""/g,'"'); }
|
||||
else if(fuzzyfmla(s)) { cell.t = 'n'; cell.f = s.slice(1); }
|
||||
else if(fuzzyfmla(s)) { cell.t = 's'; cell.f = s.slice(1); cell.v = s; }
|
||||
else { cell.t = 's'; cell.v = s; } }
|
||||
else if(s == "TRUE") { cell.t = 'b'; cell.v = true; }
|
||||
else if(s == "FALSE") { cell.t = 'b'; cell.v = false; }
|
||||
else if(!isNaN(v = fuzzynum(s))) { cell.t = 'n'; if(o.cellText !== false) cell.w = s; cell.v = v; }
|
||||
else if(!isNaN(fuzzydate(s).getDate()) || _re && s.match(_re)) {
|
||||
else if(!isNaN(v = fuzzynum(s))) { cell.t = 'n'; cell.v = v; }
|
||||
else if(!isNaN((v = fuzzydate(s)).getDate()) || _re && s.match(_re)) {
|
||||
cell.z = o.dateNF || table_fmt[14];
|
||||
var k = 0;
|
||||
if(_re && s.match(_re)){ s=dateNF_fix(s, o.dateNF, (s.match(_re)||[])); k=1; }
|
||||
if(o.cellDates) { cell.t = 'd'; cell.v = parseDate(s, k); }
|
||||
else { cell.t = 'n'; cell.v = datenum(parseDate(s, k)); }
|
||||
if(o.cellText !== false) cell.w = SSF_format(cell.z, cell.v instanceof Date ? datenum(cell.v):cell.v);
|
||||
if(_re && s.match(_re)){ var news=dateNF_fix(s, o.dateNF, (s.match(_re)||[])); v = parseDate(news); if(o && o.UTC === false) v = utc_to_local(v); }
|
||||
else if(o && o.UTC === false) v = utc_to_local(v);
|
||||
else if(o.cellText !== false && o.dateNF) cell.w = SSF_format(cell.z, v);
|
||||
if(o.cellDates) { cell.t = 'd'; cell.v = v; }
|
||||
else { cell.t = 'n'; cell.v = datenum(v); }
|
||||
if(!o.cellNF) delete cell.z;
|
||||
} else {
|
||||
cell.t = 's';
|
||||
cell.v = s;
|
||||
}
|
||||
if(cell.t == 'z'){}
|
||||
else if(o.dense) { if(!ws[R]) ws[R] = []; ws[R][C] = cell; }
|
||||
else if(o.dense) { if(!ws["!data"][R]) ws["!data"][R] = []; ws["!data"][R][C] = cell; }
|
||||
else ws[encode_cell({c:C,r:R})] = cell;
|
||||
start = end+1; startcc = str.charCodeAt(start);
|
||||
if(range.e.c < C) range.e.c = C;
|
||||
@ -1004,13 +1086,14 @@ var PRN = /*#__PURE__*/(function() {
|
||||
|
||||
function sheet_to_prn(ws/*:Worksheet*//*::, opts:?any*/)/*:string*/ {
|
||||
var o/*:Array<string>*/ = [];
|
||||
if(!ws["!ref"]) return "";
|
||||
var r = safe_decode_range(ws['!ref']), cell/*:Cell*/;
|
||||
var dense = Array.isArray(ws);
|
||||
var dense = ws["!data"] != null;
|
||||
for(var R = r.s.r; R <= r.e.r; ++R) {
|
||||
var oo/*:Array<string>*/ = [];
|
||||
for(var C = r.s.c; C <= r.e.c; ++C) {
|
||||
var coord = encode_cell({r:R,c:C});
|
||||
cell = dense ? (ws[R]||[])[C] : ws[coord];
|
||||
cell = dense ? (ws["!data"][R]||[])[C] : ws[coord];
|
||||
if(!cell || cell.v == null) { oo.push(" "); continue; }
|
||||
var w = (cell.w || (format_cell(cell), cell.w) || "").slice(0,10);
|
||||
while(w.length < 10) w += " ";
|
||||
|
216
bits/41_lotus.js
216
bits/41_lotus.js
@ -24,15 +24,33 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
throw "Unsupported type " + opts.type;
|
||||
}
|
||||
|
||||
/* NOTE: this list intentionally starts at 1 */
|
||||
var LOTUS_DATE_FMTS = [
|
||||
"mmmm",
|
||||
"dd-mmm-yyyy",
|
||||
"dd-mmm",
|
||||
"mmm-yyyy",
|
||||
"@", // "text"?
|
||||
"mm/dd",
|
||||
"hh:mm:ss AM/PM", // 7
|
||||
"hh:mm AM/PM",
|
||||
"mm/dd/yyyy",
|
||||
"mm/dd",
|
||||
"hh:mm:ss",
|
||||
"hh:mm" // 12
|
||||
];
|
||||
|
||||
function lotus_to_workbook_buf(d, opts)/*:Workbook*/ {
|
||||
if(!d) return d;
|
||||
var o = opts || {};
|
||||
if(DENSE != null && o.dense == null) o.dense = DENSE;
|
||||
var s/*:Worksheet*/ = ((o.dense ? [] : {})/*:any*/), n = "Sheet1", next_n = "", sidx = 0;
|
||||
var sheets = {}, snames = [], realnames = [];
|
||||
var s/*:Worksheet*/ = ({}/*:any*/), n = "Sheet1", next_n = "", sidx = 0;
|
||||
var sheets = {}, snames = [], realnames = [], sdata = [];
|
||||
if(o.dense) sdata = s["!data"] = [];
|
||||
|
||||
var refguess = {s: {r:0, c:0}, e: {r:0, c:0} };
|
||||
var sheetRows = o.sheetRows || 0;
|
||||
var lastcell = {};
|
||||
|
||||
if(d[4] == 0x51 && d[5] == 0x50 && d[6] == 0x57) return qpw_to_workbook_buf(d, opts);
|
||||
if(d[2] == 0x00) {
|
||||
@ -64,9 +82,9 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
case 0x0E: /* NUMBER */
|
||||
case 0x10: /* FORMULA */
|
||||
/* TODO: actual translation of the format code */
|
||||
if(RT == 0x0E && (val[2] & 0x70) == 0x70 && (val[2] & 0x0F) > 1 && (val[2] & 0x0F) < 15) {
|
||||
val[1].z = o.dateNF || table_fmt[14];
|
||||
if(o.cellDates) { val[1].t = 'd'; val[1].v = numdate(val[1].v); }
|
||||
if((val[2] & 0x70) == 0x70 && (val[2] & 0x0F) > 1 && (val[2] & 0x0F) < 15) {
|
||||
val[1].z = o.dateNF || LOTUS_DATE_FMTS[(val[2] & 0x0F)-1] || table_fmt[14];
|
||||
if(o.cellDates) { val[1].v = numdate(val[1].v); val[1].t = typeof val[1].v == "number" ? 'n' : 'd'; }
|
||||
}
|
||||
|
||||
if(o.qpro) {
|
||||
@ -74,26 +92,36 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
s["!ref"] = encode_range(refguess);
|
||||
sheets[n] = s;
|
||||
snames.push(n);
|
||||
s = (o.dense ? [] : {});
|
||||
s = ({}/*:any*/); if(o.dense) sdata = s["!data"] = [];
|
||||
refguess = {s: {r:0, c:0}, e: {r:0, c:0} };
|
||||
sidx = val[3]; n = next_n || "Sheet" + (sidx + 1); next_n = "";
|
||||
}
|
||||
}
|
||||
|
||||
var tmpcell = o.dense ? (s[val[0].r]||[])[val[0].c] : s[encode_cell(val[0])];
|
||||
var tmpcell = o.dense ? (sdata[val[0].r]||[])[val[0].c] : s[encode_cell(val[0])];
|
||||
if(tmpcell) {
|
||||
tmpcell.t = val[1].t; tmpcell.v = val[1].v;
|
||||
if(val[1].z != null) tmpcell.z = val[1].z;
|
||||
if(val[1].f != null) tmpcell.f = val[1].f;
|
||||
lastcell = tmpcell;
|
||||
break;
|
||||
}
|
||||
if(o.dense) {
|
||||
if(!s[val[0].r]) s[val[0].r] = [];
|
||||
s[val[0].r][val[0].c] = val[1];
|
||||
if(!sdata[val[0].r]) sdata[val[0].r] = [];
|
||||
sdata[val[0].r][val[0].c] = val[1];
|
||||
} else s[encode_cell(val[0])] = val[1];
|
||||
lastcell = val[1];
|
||||
break;
|
||||
case 0x5405: o.works2 = true; break;
|
||||
default:
|
||||
case 0x5402: {
|
||||
/* TODO: enumerate all extended number formats */
|
||||
if(val == 0x14a1) {
|
||||
lastcell.z = "hh:mm:ss";
|
||||
if(o.cellDates && lastcell.t == "n") {
|
||||
lastcell.v = numdate(lastcell.v); lastcell.t = typeof lastcell.v == "number" ? 'n' : 'd';
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}}, o);
|
||||
} else if(d[2] == 0x1A || d[2] == 0x0E) {
|
||||
o.Enum = WK3Enum;
|
||||
@ -115,14 +143,14 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
s["!ref"] = encode_range(refguess);
|
||||
sheets[n] = s;
|
||||
snames.push(n);
|
||||
s = (o.dense ? [] : {});
|
||||
s = ({}/*:any*/); if(o.dense) sdata = s["!data"] = [];
|
||||
refguess = {s: {r:0, c:0}, e: {r:0, c:0} };
|
||||
sidx = val[3]; n = "Sheet" + (sidx + 1);
|
||||
}
|
||||
if(sheetRows > 0 && val[0].r >= sheetRows) break;
|
||||
if(o.dense) {
|
||||
if(!s[val[0].r]) s[val[0].r] = [];
|
||||
s[val[0].r][val[0].c] = val[1];
|
||||
if(!sdata[val[0].r]) sdata[val[0].r] = [];
|
||||
sdata[val[0].r][val[0].c] = val[1];
|
||||
} else s[encode_cell(val[0])] = val[1];
|
||||
if(refguess.e.c < val[0].c) refguess.e.c = val[0].c;
|
||||
if(refguess.e.r < val[0].r) refguess.e.r = val[0].r;
|
||||
@ -156,25 +184,32 @@ var WK_ = /*#__PURE__*/(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 = Array.isArray(ws);
|
||||
var dense = ws["!data"] != null;
|
||||
var cols = [];
|
||||
|
||||
write_biff_rec(ba, 0x00, write_BOF_WK1(0x0406));
|
||||
write_biff_rec(ba, 0x06, write_RANGE(range));
|
||||
var max_R = Math.min(range.e.r, 8191);
|
||||
for(var C = range.s.c; C <= range.e.c; ++C) cols[C] = encode_col(C);
|
||||
for(var R = range.s.r; R <= max_R; ++R) {
|
||||
var rr = encode_row(R);
|
||||
for(var C = range.s.c; C <= range.e.c; ++C) {
|
||||
if(R === range.s.r) cols[C] = encode_col(C);
|
||||
var ref = cols[C] + rr;
|
||||
var cell = dense ? (ws[R]||[])[C] : ws[ref];
|
||||
for(C = range.s.c; C <= range.e.c; ++C) {
|
||||
var cell = dense ? (ws["!data"][R]||[])[C] : ws[cols[C] + rr];
|
||||
if(!cell || cell.t == "z") continue;
|
||||
/* TODO: formula records */
|
||||
if(cell.t == "n") {
|
||||
if((cell.v|0)==cell.v && cell.v >= -32768 && cell.v <= 32767) write_biff_rec(ba, 0x0d, write_INTEGER(R, C, cell.v));
|
||||
else write_biff_rec(ba, 0x0e, write_NUMBER(R, C, cell.v));
|
||||
} else {
|
||||
switch(cell.t) {
|
||||
case "n":
|
||||
if((cell.v|0)==cell.v && cell.v >= -32768 && cell.v <= 32767) write_biff_rec(ba, 0x0d, write_INTEGER(R, C, cell));
|
||||
else write_biff_rec(ba, 0x0e, write_NUMBER(R, C, cell));
|
||||
break;
|
||||
case "d":
|
||||
var dc = datenum(cell.v);
|
||||
if((dc|0)==dc && dc >= -32768 && dc <= 32767) write_biff_rec(ba, 0x0d, write_INTEGER(R, C, {t:"n", v:dc, z:cell.z || table_fmt[14]}));
|
||||
else write_biff_rec(ba, 0x0e, write_NUMBER(R, C, {t:"n", v:dc, z:cell.z || table_fmt[14]}));
|
||||
break;
|
||||
default:
|
||||
var str = format_cell(cell);
|
||||
write_biff_rec(ba, 0x0F, write_LABEL(R, C, str.slice(0, 239)));
|
||||
}
|
||||
@ -200,7 +235,7 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
var ws = wb.Sheets[wb.SheetNames[i]];
|
||||
if(!ws || !ws["!ref"]) continue;
|
||||
var range = safe_decode_range(ws["!ref"]);
|
||||
var dense = Array.isArray(ws);
|
||||
var dense = ws["!data"] != null;
|
||||
var cols = [];
|
||||
var max_R = Math.min(range.e.r, 8191);
|
||||
for(var R = range.s.r; R <= max_R; ++R) {
|
||||
@ -208,7 +243,7 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
for(var C = range.s.c; C <= range.e.c; ++C) {
|
||||
if(R === range.s.r) cols[C] = encode_col(C);
|
||||
var ref = cols[C] + rr;
|
||||
var cell = dense ? (ws[R]||[])[C] : ws[ref];
|
||||
var cell = dense ? (ws["!data"][R]||[])[C] : ws[ref];
|
||||
if(!cell || cell.t == "z") continue;
|
||||
/* TODO: FORMULA19 NUMBER18 records */
|
||||
if(cell.t == "n") {
|
||||
@ -308,11 +343,18 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
return o;
|
||||
}
|
||||
|
||||
function get_wk1_fmt(cell)/*:number*/ {
|
||||
/* TODO: some fuzzy matching on the number format */
|
||||
if(cell.z && fmt_is_date(cell.z)) {
|
||||
return 0xf0 | (LOTUS_DATE_FMTS.indexOf(cell.z) + 1 || 2);
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
function parse_LABEL(blob, length, opts) {
|
||||
var tgt = blob.l + length;
|
||||
var o = parse_cell(blob, length, opts);
|
||||
o[1].t = 's';
|
||||
if(opts.vers == 0x5120) {
|
||||
if((opts.vers & 0xFFFE) == 0x5120) { // WQ1 / WQ2
|
||||
blob.l++;
|
||||
var len = blob.read_shift(1);
|
||||
o[1].v = blob.read_shift(len, 'utf8');
|
||||
@ -354,12 +396,12 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
o[1].v = blob.read_shift(2, 'i');
|
||||
return o;
|
||||
}
|
||||
function write_INTEGER(R, C, v) {
|
||||
function write_INTEGER(R, C, cell) {
|
||||
var o = new_buf(7);
|
||||
o.write_shift(1, 0xFF);
|
||||
o.write_shift(1, get_wk1_fmt(cell));
|
||||
o.write_shift(2, C);
|
||||
o.write_shift(2, R);
|
||||
o.write_shift(2, v, 'i');
|
||||
o.write_shift(2, cell.v, 'i');
|
||||
return o;
|
||||
}
|
||||
|
||||
@ -368,12 +410,12 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
o[1].v = blob.read_shift(8, 'f');
|
||||
return o;
|
||||
}
|
||||
function write_NUMBER(R, C, v) {
|
||||
function write_NUMBER(R, C, cell) {
|
||||
var o = new_buf(13);
|
||||
o.write_shift(1, 0xFF);
|
||||
o.write_shift(1, get_wk1_fmt(cell));
|
||||
o.write_shift(2, C);
|
||||
o.write_shift(2, R);
|
||||
o.write_shift(8, v, 'f');
|
||||
o.write_shift(8, cell.v, 'f');
|
||||
return o;
|
||||
}
|
||||
|
||||
@ -426,7 +468,7 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
0x33: ["FALSE", 0],
|
||||
0x34: ["TRUE", 0],
|
||||
0x35: ["RAND", 0],
|
||||
// 0x36 DATE
|
||||
0x36: ["DATE", 3],
|
||||
// 0x37 NOW
|
||||
// 0x38 PMT
|
||||
// 0x39 PV
|
||||
@ -436,7 +478,7 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
// 0x3D MONTH
|
||||
// 0x3E YEAR
|
||||
0x3F: ["ROUND", 2],
|
||||
// 0x40 TIME
|
||||
0x40: ["TIME", 3],
|
||||
// 0x41 HOUR
|
||||
// 0x42 MINUTE
|
||||
// 0x43 SECOND
|
||||
@ -778,13 +820,24 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
/*::[*/0x0048/*::]*/: { n:"ACOMM" },
|
||||
/*::[*/0x0049/*::]*/: { n:"AMACRO" },
|
||||
/*::[*/0x004A/*::]*/: { n:"PARSE" },
|
||||
// 0x0064
|
||||
/*::[*/0x0066/*::]*/: { n:"PRANGES??" },
|
||||
/*::[*/0x0067/*::]*/: { n:"RRANGES??" },
|
||||
/*::[*/0x0068/*::]*/: { n:"FNAME??" },
|
||||
/*::[*/0x0069/*::]*/: { n:"MRANGES??" },
|
||||
// 0x0096
|
||||
// 0x0099
|
||||
// 0x009A
|
||||
// 0x009B
|
||||
// 0x009C
|
||||
// 0x00C0
|
||||
// 0x00C7
|
||||
// 0x00C9
|
||||
/*::[*/0x00CC/*::]*/: { n:"SHEETNAMECS", f:parse_SHEETNAMECS },
|
||||
// 0x00CD
|
||||
/*::[*/0x00DE/*::]*/: { n:"SHEETNAMELP", f:parse_SHEETNAMELP },
|
||||
/*::[*/0x00FF/*::]*/: { n:"BOF", f:parseuint16 },
|
||||
/*::[*/0x5402/*::]*/: { n:"WKSNF", f:parseuint16 },
|
||||
/*::[*/0xFFFF/*::]*/: { n:"" }
|
||||
};
|
||||
|
||||
@ -914,16 +967,47 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
/*::[*/0xFFFF/*::]*/: { n:"" }
|
||||
};
|
||||
|
||||
/* TODO: fill out and verify this table across QP versions */
|
||||
var QPWNFTable = {
|
||||
/*::[*/0x05/*::*/: "dd-mmm-yy",
|
||||
/*::[*/0x06/*::*/: "dd-mmm",
|
||||
/*::[*/0x07/*::*/: "mmm-yy",
|
||||
/*::[*/0x08/*::*/: "mm/dd/yy", // Long Date Intl
|
||||
/*::[*/0x0A/*::*/: "hh:mm:ss AM/PM",
|
||||
/*::[*/0x0B/*::*/: "hh:mm AM/PM",
|
||||
/*::[*/0x0E/*::*/: "dd-mmm-yyyy",
|
||||
/*::[*/0x0F/*::*/: "mmm-yyyy",
|
||||
|
||||
/* 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)/*:Workbook*/ {
|
||||
prep_blob(d, 0);
|
||||
var o = opts || {};
|
||||
if(DENSE != null && o.dense == null) o.dense = DENSE;
|
||||
var s/*:Worksheet*/ = ((o.dense ? [] : {})/*:any*/);
|
||||
var s/*:Worksheet*/ = ({}/*:any*/); if(o.dense) s["!data"] = [];
|
||||
var SST = [], sname = "", formulae = [];
|
||||
var range = {s:{r:-1,c:-1}, e:{r:-1,c:-1}};
|
||||
var cnt = 0, type = 0, C = 0, R = 0;
|
||||
var wb = { SheetNames: [], Sheets: {} };
|
||||
var FMTS = [];
|
||||
outer: while(d.l < d.length) {
|
||||
var RT = d.read_shift(2), length = d.read_shift(2);
|
||||
var p = d.slice(d.l, d.l + length);
|
||||
@ -934,6 +1018,22 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
break;
|
||||
case 0x02: /* EOF */ break outer;
|
||||
|
||||
case 0x08: /* NF */ break; // TODO: this is tied to custom number formats
|
||||
|
||||
case 0x0A: /* FORMATS */ {
|
||||
var fcnt = p.read_shift(4);
|
||||
var step = ((p.length - p.l)/ fcnt)|0;
|
||||
for(var ifmt = 0; ifmt < fcnt; ++ifmt) {
|
||||
var end = p.l + step;
|
||||
var fmt = {};
|
||||
p.l += 2;
|
||||
fmt.numFmtId = p.read_shift(2);
|
||||
if(QPWNFTable[fmt.numFmtId]) fmt.z = QPWNFTable[fmt.numFmtId];
|
||||
p.l = end;
|
||||
FMTS.push(fmt);
|
||||
}
|
||||
} break;
|
||||
|
||||
/* TODO: The behavior here should be consistent with Numbers: QP Notebook ~ .TN.SheetArchive, QP Sheet ~ .TST.TableModelArchive */
|
||||
case 0x0401: /* BON */ break;
|
||||
case 0x0402: /* EON */ /* TODO: backfill missing sheets based on BON cnt */ break;
|
||||
@ -956,7 +1056,7 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
|
||||
case 0x0601: { /* BOS */
|
||||
var sidx = p.read_shift(2);
|
||||
s = ((o.dense ? [] : {})/*:any*/);
|
||||
s = ({}/*:any*/); if(o.dense) s["!data"] = [];
|
||||
range.s.c = p.read_shift(2);
|
||||
range.e.c = p.read_shift(2);
|
||||
range.s.r = p.read_shift(4);
|
||||
@ -993,20 +1093,28 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
R = p.read_shift(4), cnt = p.read_shift(4);
|
||||
if(range.s.r > R) range.s.r = R;
|
||||
if(range.e.r < R + cnt - 1) range.e.r = R + cnt - 1;
|
||||
var CC = encode_col(C);
|
||||
while(p.l < p.length) {
|
||||
var cell = { t: "z" };
|
||||
var flags = p.read_shift(1);
|
||||
if(flags & 0x80) p.l += 2;
|
||||
var flags = p.read_shift(1), fmtidx = -1;
|
||||
if(flags & 0x80) fmtidx = p.read_shift(2);
|
||||
var mul = (flags & 0x40) ? p.read_shift(2) - 1: 0;
|
||||
switch(flags & 0x1F) {
|
||||
case 0: break;
|
||||
case 1: break;
|
||||
case 2: cell = { t: "n", v: p.read_shift(2) }; break;
|
||||
case 3: cell = { t: "n", v: p.read_shift(2, 'i') }; break;
|
||||
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;
|
||||
var delta = 0;
|
||||
if(flags & 0x20) switch(flags & 0x1F) {
|
||||
case 2: delta = p.read_shift(2); break;
|
||||
@ -1015,10 +1123,14 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
default: throw "Unsupported delta for QPW cell type " + (flags & 0x1F);
|
||||
}
|
||||
if(!(!o.sheetStubs && cell.t == "z")) {
|
||||
if(Array.isArray(s)) {
|
||||
if(!s[R]) s[R] = [];
|
||||
s[R][C] = cell;
|
||||
} else s[encode_cell({r:R, c:C})] = cell;
|
||||
var newcell = dup(cell);
|
||||
if(cell.t == "n" && cell.z && fmt_is_date(cell.z) && o.cellDates) {
|
||||
newcell.v = numdate(cell.v); newcell.t = typeof newcell.v == "number" ? 'n' : 'd';
|
||||
}
|
||||
if(s["!data"] != null) {
|
||||
if(!s["!data"][R]) s["!data"][R] = [];
|
||||
s["!data"][R][C] = newcell;
|
||||
} else s[CC + encode_row(R)] = newcell;
|
||||
}
|
||||
++R; --cnt;
|
||||
while(mul-- > 0 && cnt >= 0) {
|
||||
@ -1033,17 +1145,29 @@ var WK_ = /*#__PURE__*/(function() {
|
||||
case 7: cell = { t: "s", v: SST[type = p.read_shift(4) - 1] }; break;
|
||||
default: throw "Cannot apply repeat for QPW cell type " + (flags & 0x1F);
|
||||
}
|
||||
if(fmtidx != -1);
|
||||
if(!(!o.sheetStubs && cell.t == "z")) {
|
||||
if(Array.isArray(s)) {
|
||||
if(!s[R]) s[R] = [];
|
||||
s[R][C] = cell;
|
||||
} else s[encode_cell({r:R, c:C})] = cell;
|
||||
if(s["!data"] != null) {
|
||||
if(!s["!data"][R]) s["!data"][R] = [];
|
||||
s["!data"][R][C] = cell;
|
||||
} else s[CC + encode_row(R)] = cell;
|
||||
}
|
||||
++R; --cnt;
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
|
@ -168,7 +168,7 @@ var rs_to_html = /*#__PURE__*/(function parse_rs_factory() {
|
||||
})();
|
||||
|
||||
/* 18.4.8 si CT_Rst */
|
||||
var sitregex = /<(?:\w+:)?t[^>]*>([^<]*)<\/(?:\w+:)?t>/g, sirregex = /<(?:\w+:)?r>/;
|
||||
var sitregex = /<(?:\w+:)?t[^>]*>([^<]*)<\/(?:\w+:)?t>/g, sirregex = /<(?:\w+:)?r\b[^>]*>/;
|
||||
var sirphregex = /<(?:\w+:)?rPh.*?>([\s\S]*?)<\/(?:\w+:)?rPh>/g;
|
||||
function parse_si(x, opts) {
|
||||
var html = opts ? opts.cellHTML : true;
|
||||
@ -229,6 +229,7 @@ function write_sst_xml(sst/*:SST*/, opts)/*:string*/ {
|
||||
else {
|
||||
sitag += "<t";
|
||||
if(!s.t) s.t = "";
|
||||
if(typeof s.t !== "string") s.t = String(s.t);
|
||||
if(s.t.match(straywsregex)) sitag += ' xml:space="preserve"';
|
||||
sitag += ">" + escapexml(s.t) + "</t>";
|
||||
}
|
||||
|
185
bits/45_rtf.js
185
bits/45_rtf.js
@ -1,82 +1,103 @@
|
||||
var RTF = /*#__PURE__*/(function() {
|
||||
function rtf_to_sheet(d/*:RawData*/, opts)/*:Worksheet*/ {
|
||||
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(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 rows = str.match(/\\trowd[\s\S]*?\\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;
|
||||
var payload = [];
|
||||
while((res = rtfre.exec(rowtf))) {
|
||||
var data = rowtf.slice(last_index, rtfre.lastIndex - res[0].length);
|
||||
if(data.charCodeAt(0) == 0x20) data = data.slice(1);
|
||||
if(data.length) payload.push(data);
|
||||
switch(res[0]) {
|
||||
case "\\cell":
|
||||
++C;
|
||||
if(payload.length) {
|
||||
// TODO: value parsing, including codepage adjustments
|
||||
var cell = {v: payload.join(""), t:"s"};
|
||||
if(Array.isArray(ws)) ws[R][C] = cell;
|
||||
else ws[encode_cell({r:R, c:C})] = cell;
|
||||
}
|
||||
payload = [];
|
||||
break;
|
||||
case "\\par": // NOTE: Excel serializes both "\r" and "\n" as "\\par"
|
||||
payload.push("\n");
|
||||
break;
|
||||
}
|
||||
last_index = rtfre.lastIndex;
|
||||
}
|
||||
if(C > range.e.c) range.e.c = C;
|
||||
});
|
||||
ws['!ref'] = encode_range(range);
|
||||
return ws;
|
||||
}
|
||||
|
||||
function rtf_to_workbook(d/*:RawData*/, opts)/*:Workbook*/ { return sheet_to_workbook(rtf_to_sheet(d, opts), opts); }
|
||||
|
||||
/* TODO: this is a stub */
|
||||
function sheet_to_rtf(ws/*:Worksheet*//*::, opts*/)/*:string*/ {
|
||||
var o = ["{\\rtf1\\ansi"];
|
||||
var r = safe_decode_range(ws['!ref']), cell/*:Cell*/;
|
||||
var dense = Array.isArray(ws);
|
||||
for(var R = r.s.r; R <= r.e.r; ++R) {
|
||||
o.push("\\trowd\\trautofit1");
|
||||
for(var C = r.s.c; C <= r.e.c; ++C) o.push("\\cellx" + (C+1));
|
||||
o.push("\\pard\\intbl");
|
||||
for(C = r.s.c; C <= r.e.c; ++C) {
|
||||
var coord = encode_cell({r:R,c:C});
|
||||
cell = dense ? (ws[R]||[])[C]: ws[coord];
|
||||
if(!cell || cell.v == null && (!cell.f || cell.F)) continue;
|
||||
o.push(" " + (cell.w || (format_cell(cell), cell.w)).replace(/[\r\n]/g, "\\par "));
|
||||
o.push("\\cell");
|
||||
}
|
||||
o.push("\\pard\\intbl\\row");
|
||||
}
|
||||
return o.join("") + "}";
|
||||
}
|
||||
|
||||
return {
|
||||
to_workbook: rtf_to_workbook,
|
||||
to_sheet: rtf_to_sheet,
|
||||
from_sheet: sheet_to_rtf
|
||||
};
|
||||
})();
|
||||
function rtf_to_sheet(d, opts) {
|
||||
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(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);
|
||||
}
|
||||
function rtf_to_sheet_str(str, opts) {
|
||||
var o = opts || {};
|
||||
var ws = {};
|
||||
var dense = o.dense;
|
||||
if (dense)
|
||||
ws["!data"] = [];
|
||||
var rows = str.match(/\\trowd[\s\S]*?\\row\b/g);
|
||||
if (!rows)
|
||||
throw new Error("RTF missing table");
|
||||
var range = { s: { c: 0, r: 0 }, e: { c: 0, r: rows.length - 1 } };
|
||||
var row = [];
|
||||
rows.forEach(function(rowtf, R) {
|
||||
if (dense)
|
||||
row = ws["!data"][R] = [];
|
||||
var rtfre = /\\[\w\-]+\b/g;
|
||||
var last_index = 0;
|
||||
var res;
|
||||
var C = -1;
|
||||
var payload = [];
|
||||
while ((res = rtfre.exec(rowtf)) != null) {
|
||||
var data = rowtf.slice(last_index, rtfre.lastIndex - res[0].length);
|
||||
if (data.charCodeAt(0) == 32)
|
||||
data = data.slice(1);
|
||||
if (data.length)
|
||||
payload.push(data);
|
||||
switch (res[0]) {
|
||||
case "\\cell":
|
||||
++C;
|
||||
if (payload.length) {
|
||||
var cell = { v: payload.join(""), t: "s" };
|
||||
if (cell.v == "TRUE" || cell.v == "FALSE") {
|
||||
cell.v = cell.v == "TRUE";
|
||||
cell.t = "b";
|
||||
} else if (!isNaN(fuzzynum(cell.v))) {
|
||||
cell.t = "n";
|
||||
if (o.cellText !== false)
|
||||
cell.w = cell.v;
|
||||
cell.v = fuzzynum(cell.v);
|
||||
}
|
||||
if (dense)
|
||||
row[C] = cell;
|
||||
else
|
||||
ws[encode_cell({ r: R, c: C })] = cell;
|
||||
}
|
||||
payload = [];
|
||||
break;
|
||||
case "\\par":
|
||||
payload.push("\n");
|
||||
break;
|
||||
}
|
||||
last_index = rtfre.lastIndex;
|
||||
}
|
||||
if (C > range.e.c)
|
||||
range.e.c = C;
|
||||
});
|
||||
ws["!ref"] = encode_range(range);
|
||||
return ws;
|
||||
}
|
||||
function rtf_to_workbook(d, opts) {
|
||||
var wb = sheet_to_workbook(rtf_to_sheet(d, opts), opts);
|
||||
wb.bookType = "rtf";
|
||||
return wb;
|
||||
}
|
||||
function sheet_to_rtf(ws, opts) {
|
||||
var o = ["{\\rtf1\\ansi"];
|
||||
if (!ws["!ref"])
|
||||
return o[0] + "}";
|
||||
var r = safe_decode_range(ws["!ref"]), cell;
|
||||
var dense = ws["!data"] != null, row = [];
|
||||
for (var R = r.s.r; R <= r.e.r; ++R) {
|
||||
o.push("\\trowd\\trautofit1");
|
||||
for (var C = r.s.c; C <= r.e.c; ++C)
|
||||
o.push("\\cellx" + (C + 1));
|
||||
o.push("\\pard\\intbl");
|
||||
if (dense)
|
||||
row = ws["!data"][R] || [];
|
||||
for (C = r.s.c; C <= r.e.c; ++C) {
|
||||
var coord = encode_cell({ r: R, c: C });
|
||||
cell = dense ? row[C] : ws[coord];
|
||||
if (!cell || cell.v == null && (!cell.f || cell.F)) {
|
||||
o.push(" \\cell");
|
||||
continue;
|
||||
}
|
||||
o.push(" " + (cell.w || (format_cell(cell), cell.w) || "").replace(/[\r\n]/g, "\\par "));
|
||||
o.push("\\cell");
|
||||
}
|
||||
o.push("\\pard\\intbl\\row");
|
||||
}
|
||||
return o.join("") + "}";
|
||||
}
|
||||
|
@ -311,7 +311,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]])
|
||||
@ -325,7 +325,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;
|
||||
@ -337,12 +337,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 ? */
|
||||
|
@ -71,7 +71,7 @@ function parse_xlmeta_xml(data, name, opts) {
|
||||
lastmeta.offsets.push(+y.i);
|
||||
break;
|
||||
default:
|
||||
if (!pass && opts.WTF)
|
||||
if (!pass && (opts == null ? void 0 : opts.WTF))
|
||||
throw new Error("unrecognized " + y[0] + " in metadata");
|
||||
}
|
||||
return x;
|
||||
|
@ -1,5 +1,43 @@
|
||||
/* L.5.5.2 SpreadsheetML Comments + VML Schema */
|
||||
function write_vml(rId/*:number*/, comments) {
|
||||
var shapevmlregex = /<(?:\w+:)?shape(?:[^\w][^>]*)?>([\s\S]*?)<\/(?:\w+:)?shape>/g;
|
||||
function parse_vml(data/*:string*/, sheet, comments) {
|
||||
var cidx = 0;
|
||||
(data.match(shapevmlregex)||[]).forEach(function(m) {
|
||||
var type = "";
|
||||
var hidden = true;
|
||||
var aidx = -1;
|
||||
var R = -1, C = -1;
|
||||
m.replace(tagregex, function(x/*:string*/, idx/*:number*/) {
|
||||
var y = parsexmltag(x);
|
||||
switch(strip_ns(y[0])) {
|
||||
case '<ClientData': if(y.ObjectType) type = y.ObjectType; break;
|
||||
|
||||
case '<Visible': case '<Visible/>': hidden = false; break;
|
||||
|
||||
case '<Row': case '<Row>': aidx = idx + x.length; break;
|
||||
case '</Row>': R = +m.slice(aidx, idx).trim(); break;
|
||||
|
||||
case '<Column': case '<Column>': aidx = idx + x.length; break;
|
||||
case '</Column>': C = +m.slice(aidx, idx).trim(); break;
|
||||
}
|
||||
return "";
|
||||
});
|
||||
switch(type) {
|
||||
case 'Note':
|
||||
var cell = ws_get_cell_stub(sheet, ((R>=0 && C>=0) ? encode_cell({r:R,c:C}) : comments[cidx].ref));
|
||||
if(cell.c) {
|
||||
cell.c.hidden = hidden;
|
||||
}
|
||||
++cidx;
|
||||
break;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* comment boxes */
|
||||
function write_vml(rId/*:number*/, comments, ws) {
|
||||
var csize = [21600, 21600];
|
||||
/* L.5.2.1.2 Path Attribute */
|
||||
var bbox = ["m0,0l0",csize[1],csize[0],csize[1],csize[0],"0xe"].join(",");
|
||||
@ -21,7 +59,7 @@ function write_vml(rId/*:number*/, comments) {
|
||||
return o.join("");
|
||||
}
|
||||
|
||||
function write_vml_comment(x, _shapeid)/*:string*/ {
|
||||
function write_vml_comment(x, _shapeid, ws)/*:string*/ {
|
||||
var c = decode_cell(x[0]);
|
||||
var fillopts = /*::(*/{'color2':"#BEFF82", 'type':"gradient"}/*:: :any)*/;
|
||||
if(fillopts.type == "gradient") fillopts.angle = "-180";
|
||||
|
@ -1,15 +1,16 @@
|
||||
function sheet_insert_comments(sheet, comments/*:Array<RawComment>*/, threaded/*:boolean*/, people/*:?Array<any>*/) {
|
||||
var dense = Array.isArray(sheet);
|
||||
function sheet_insert_comments(sheet/*:WorkSheet*/, comments/*:Array<RawComment>*/, threaded/*:boolean*/, people/*:?Array<any>*/) {
|
||||
var dense = sheet["!data"] != null;
|
||||
var cell/*:Cell*/;
|
||||
comments.forEach(function(comment) {
|
||||
var r = decode_cell(comment.ref);
|
||||
if(r.r < 0 || r.c < 0) return;
|
||||
if(dense) {
|
||||
if(!sheet[r.r]) sheet[r.r] = [];
|
||||
cell = sheet[r.r][r.c];
|
||||
if(!sheet["!data"][r.r]) sheet["!data"][r.r] = [];
|
||||
cell = sheet["!data"][r.r][r.c];
|
||||
} else cell = sheet[comment.ref];
|
||||
if (!cell) {
|
||||
cell = ({t:"z"}/*:any*/);
|
||||
if(dense) sheet[r.r][r.c] = cell;
|
||||
if(dense) sheet["!data"][r.r][r.c] = cell;
|
||||
else sheet[comment.ref] = cell;
|
||||
var range = safe_decode_range(sheet["!ref"]||"BDWGO1000001:A1");
|
||||
if(range.s.r > r.r) range.s.r = r.r;
|
||||
@ -17,7 +18,7 @@ function sheet_insert_comments(sheet, comments/*:Array<RawComment>*/, threaded/*
|
||||
if(range.s.c > r.c) range.s.c = r.c;
|
||||
if(range.e.c < r.c) range.e.c = r.c;
|
||||
var encoded = encode_range(range);
|
||||
if (encoded !== sheet["!ref"]) sheet["!ref"] = encoded;
|
||||
sheet["!ref"] = encoded;
|
||||
}
|
||||
|
||||
if (!cell.c) cell.c = [];
|
||||
@ -35,4 +36,3 @@ function sheet_insert_comments(sheet, comments/*:Array<RawComment>*/, threaded/*
|
||||
cell.c.push(o);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -50,21 +50,28 @@ function write_comments_xml(data/*::, opts*/) {
|
||||
o.push("<commentList>");
|
||||
data.forEach(function(d) {
|
||||
/* 18.7.3 CT_Comment */
|
||||
var lastauthor = 0, ts = [];
|
||||
var lastauthor = 0, ts = [], tcnt = 0;
|
||||
if(d[1][0] && d[1][0].T && d[1][0].ID) lastauthor = iauthor.indexOf("tc=" + d[1][0].ID);
|
||||
else d[1].forEach(function(c) {
|
||||
d[1].forEach(function(c) {
|
||||
if(c.a) lastauthor = iauthor.indexOf(escapexml(c.a));
|
||||
ts.push(c.t||"");
|
||||
if(c.T) ++tcnt;
|
||||
ts.push(c.t == null ? "" : escapexml(c.t));
|
||||
});
|
||||
o.push('<comment ref="' + d[0] + '" authorId="' + lastauthor + '"><text>');
|
||||
if(ts.length <= 1) o.push(writetag("t", escapexml(ts[0]||"")));
|
||||
else {
|
||||
if(tcnt === 0) {
|
||||
d[1].forEach(function(c) {
|
||||
o.push('<comment ref="' + d[0] + '" authorId="' + iauthor.indexOf(escapexml(c.a)) + '"><text>');
|
||||
o.push(writetag("t", c.t == null ? "" : escapexml(c.t)));
|
||||
o.push('</text></comment>');
|
||||
});
|
||||
} else {
|
||||
if(d[1][0] && d[1][0].T && d[1][0].ID) lastauthor = iauthor.indexOf("tc=" + d[1][0].ID);
|
||||
/* based on Threaded Comments -> Comments projection */
|
||||
o.push('<comment ref="' + d[0] + '" authorId="' + lastauthor + '"><text>');
|
||||
var t = "Comment:\n " + (ts[0]) + "\n";
|
||||
for(var i = 1; i < ts.length; ++i) t += "Reply:\n " + ts[i] + "\n";
|
||||
o.push(writetag("t", escapexml(t)));
|
||||
o.push('</text></comment>');
|
||||
}
|
||||
o.push('</text></comment>');
|
||||
});
|
||||
o.push("</commentList>");
|
||||
if(o.length>2) { o[o.length] = ('</comments>'); o[1]=o[1].replace("/>",">"); }
|
||||
|
@ -74,6 +74,10 @@ function write_comments_bin(data/*::, opts*/) {
|
||||
if(iauthor.indexOf(c.a) > -1) return;
|
||||
iauthor.push(c.a.slice(0,54));
|
||||
write_record(ba, 0x0278 /* BrtCommentAuthor */, write_BrtCommentAuthor(c.a));
|
||||
if(c.T && c.ID && iauthor.indexOf("tc=" + c.ID) == -1) {
|
||||
iauthor.push("tc=" + c.ID);
|
||||
write_record(ba, 0x0278 /* BrtCommentAuthor */, write_BrtCommentAuthor("tc=" + c.ID));
|
||||
}
|
||||
});
|
||||
});
|
||||
write_record(ba, 0x0277 /* BrtEndCommentAuthors */);
|
||||
@ -81,7 +85,11 @@ function write_comments_bin(data/*::, opts*/) {
|
||||
write_record(ba, 0x0279 /* BrtBeginCommentList */);
|
||||
data.forEach(function(comment) {
|
||||
comment[1].forEach(function(c) {
|
||||
c.iauthor = iauthor.indexOf(c.a);
|
||||
var _ia = -1;
|
||||
if(c.ID) _ia = iauthor.indexOf("tc=" + c.ID);
|
||||
if(_ia == -1 && comment[1][0].T && comment[1][0].ID) _ia = iauthor.indexOf("tc=" + comment[1][0].ID);
|
||||
if(_ia == -1) _ia = iauthor.indexOf(c.a);
|
||||
c.iauthor = _ia;
|
||||
var range = {s:decode_cell(comment[0]),e:decode_cell(comment[0])};
|
||||
write_record(ba, 0x027B /* BrtBeginComment */, write_BrtBeginComment([range, c]));
|
||||
if(c.t && c.t.length > 0) write_record(ba, 0x027D /* BrtCommentText */, write_BrtCommentText(c));
|
||||
|
@ -23,7 +23,10 @@ var rc_to_a1 = /*#__PURE__*/(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 = /*#__PURE__*/(function(){
|
||||
return function a1_to_rc(fstr/*:string*/, base/*:CellAddress*/) {
|
||||
return fstr.replace(crefregex, function($0, $1, $2, $3, $4, $5) {
|
||||
|
@ -1041,6 +1041,7 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
|
||||
}
|
||||
}
|
||||
if(stack.length > 1 && opts.WTF) throw new Error("bad formula stack");
|
||||
if(stack[0] == "TRUE") return true; if(stack[0] == "FALSE") return false;
|
||||
return stack[0];
|
||||
}
|
||||
|
||||
|
@ -68,8 +68,7 @@ function write_FormulaValue(value) {
|
||||
/* [MS-XLS] 2.4.127 TODO */
|
||||
function parse_Formula(blob, length, opts) {
|
||||
var end = blob.l + length;
|
||||
var cell = parse_XLSCell(blob, 6);
|
||||
if(opts.biff == 2) ++blob.l;
|
||||
var cell = parse_XLSCell(blob, 6, opts);
|
||||
var val = parse_FormulaValue(blob,8);
|
||||
var flags = blob.read_shift(1);
|
||||
if(opts.biff != 2) {
|
||||
@ -152,6 +151,15 @@ function write_XLSBFormulaErr(val/*:number*/) {
|
||||
oint.write_shift(4, 0);
|
||||
return oint;
|
||||
}
|
||||
/* Writes a PtgBool */
|
||||
function write_XLSBFormulaBool(val/*:boolean*/) {
|
||||
var oint = new_buf(10);
|
||||
oint.write_shift(4, 2);
|
||||
oint.write_shift(1, 0x1D);
|
||||
oint.write_shift(1, val?1:0);
|
||||
oint.write_shift(4, 0);
|
||||
return oint;
|
||||
}
|
||||
|
||||
/* Writes a PtgStr */
|
||||
function write_XLSBFormulaStr(val/*:string*/) {
|
||||
@ -254,7 +262,7 @@ function write_XLSBFormulaRangeWS(_str, wb) {
|
||||
var sname = _str.slice(0, lastbang);
|
||||
_str = _str.slice(lastbang+1);
|
||||
if(sname.charAt(0) == "'") sname = sname.slice(1, -1).replace(/''/g, "'");
|
||||
var parts = _str.split(":"); str = parts[0];
|
||||
var parts = _str.split(":");
|
||||
|
||||
var out = new_buf(27);
|
||||
out.write_shift(4, 19);
|
||||
@ -306,7 +314,9 @@ function write_XLSBFormulaArea3D(_str, wb) {
|
||||
|
||||
|
||||
/* General Formula */
|
||||
function write_XLSBFormula(val/*:string*/, wb) {
|
||||
function write_XLSBFormula(val/*:string|number*/, wb) {
|
||||
if(typeof val == "number") return write_XLSBFormulaNum(val);
|
||||
if(typeof val == "boolean") return write_XLSBFormulaBool(val);
|
||||
if(/^#(DIV\/0!|GETTING_DATA|N\/A|NAME\?|NULL!|NUM!|REF!|VALUE!)$/.test(val)) return write_XLSBFormulaErr(+RBErr[val]);
|
||||
if(val.match(/^\$?(?:[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D]|[A-Z]{1,2})\$?(?: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})$/)) return write_XLSBFormulaRef(val);
|
||||
if(val.match(/^\$?(?:[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D]|[A-Z]{1,2})\$?(?: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-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D]|[A-Z]{1,2})\$?(?: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})$/)) return write_XLSBFormulaRange(val);
|
||||
|
@ -31,6 +31,6 @@ function ods_to_csf_3D(r/*:string*/)/*:[string, string]*/ {
|
||||
}
|
||||
|
||||
function csf_to_ods_3D(r/*:string*/)/*:string*/ {
|
||||
return r.replace(/!/,".");
|
||||
return r.replace(/!/,".").replace(/:/, ":.");
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ function get_cell_style(styles/*:Array<any>*/, cell/*:Cell*/, opts) {
|
||||
return len;
|
||||
}
|
||||
|
||||
function safe_format(p/*:Cell*/, fmtid/*:number*/, fillid/*:?number*/, opts, themes, styles) {
|
||||
function safe_format(p/*:Cell*/, fmtid/*:number*/, fillid/*:?number*/, opts, themes, styles, date1904) {
|
||||
try {
|
||||
if(opts.cellNF) p.z = table_fmt[fmtid];
|
||||
} catch(e) { if(opts.WTF) throw e; }
|
||||
@ -96,14 +96,14 @@ function safe_format(p/*:Cell*/, fmtid/*:number*/, fillid/*:?number*/, opts, the
|
||||
else p.w = SSF_general_num(p.v);
|
||||
}
|
||||
else if(p.t === 'd') {
|
||||
var dd = datenum(p.v);
|
||||
var dd = datenum(p.v, !!date1904);
|
||||
if((dd|0) === dd) p.w = dd.toString(10);
|
||||
else p.w = SSF_general_num(dd);
|
||||
}
|
||||
else if(p.v === undefined) return "";
|
||||
else p.w = SSF_general(p.v,_ssfopts);
|
||||
}
|
||||
else if(p.t === 'd') p.w = SSF_format(fmtid,datenum(p.v),_ssfopts);
|
||||
else if(p.t === 'd') p.w = SSF_format(fmtid,datenum(p.v, !!date1904),_ssfopts);
|
||||
else p.w = SSF_format(fmtid,p.v,_ssfopts);
|
||||
} catch(e) { if(opts.WTF) throw e; }
|
||||
if(!opts.cellStyles) return;
|
||||
|
120
bits/67_wsxml.js
120
bits/67_wsxml.js
@ -2,7 +2,7 @@ function parse_ws_xml_dim(ws/*:Worksheet*/, s/*:string*/) {
|
||||
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*)"/;
|
||||
@ -20,7 +20,7 @@ function parse_ws_xml(data/*:?string*/, opts, idx/*:number*/, rels, wb/*:WBWBPro
|
||||
if(DENSE != null && opts.dense == null) opts.dense = DENSE;
|
||||
|
||||
/* 18.3.1.99 worksheet CT_Worksheet */
|
||||
var s = opts.dense ? ([]/*:any*/) : ({}/*:any*/);
|
||||
var s = ({}/*:any*/); if(opts.dense) s["!data"] = [];
|
||||
var refguess/*:Range*/ = ({s: {r:2000000, c:2000000}, e: {r:0, c:0} }/*:any*/);
|
||||
|
||||
var data1 = "", data2 = "";
|
||||
@ -39,7 +39,7 @@ function parse_ws_xml(data/*:?string*/, opts, idx/*:number*/, rels, wb/*:WBWBPro
|
||||
var ridx = (data1.match(/<(?:\w*:)?dimension/)||{index:-1}).index;
|
||||
if(ridx > 0) {
|
||||
var ref = data1.slice(ridx,ridx+50).match(dimregex);
|
||||
if(ref) parse_ws_xml_dim(s, ref[1]);
|
||||
if(ref && !(opts && opts.nodim)) parse_ws_xml_dim(s, ref[1]);
|
||||
}
|
||||
|
||||
/* 18.3.1.88 sheetViews CT_SheetViews */
|
||||
@ -55,7 +55,7 @@ function parse_ws_xml(data/*:?string*/, opts, idx/*:number*/, rels, wb/*:WBWBPro
|
||||
}
|
||||
|
||||
/* 18.3.1.80 sheetData CT_SheetData ? */
|
||||
if(mtch) parse_ws_xml_data(mtch[1], s, opts, refguess, themes, styles);
|
||||
if(mtch) parse_ws_xml_data(mtch[1], s, opts, refguess, themes, styles, wb);
|
||||
|
||||
/* 18.3.1.2 autoFilter CT_AutoFilter */
|
||||
var afilter = data2.match(afregex);
|
||||
@ -75,6 +75,11 @@ function parse_ws_xml(data/*:?string*/, opts, idx/*:number*/, rels, wb/*:WBWBPro
|
||||
var margins = data2.match(marginregex);
|
||||
if(margins) s['!margins'] = parse_ws_xml_margins(parsexmltag(margins[0]));
|
||||
|
||||
/* legacyDrawing */
|
||||
var m;
|
||||
if((m = data2.match(/legacyDrawing r:id="(.*?)"/))) s['!legrel'] = m[1];
|
||||
|
||||
if(opts && opts.nodim) refguess.s.c = refguess.s.r = 0;
|
||||
if(!s["!ref"] && refguess.e.c >= refguess.s.c && refguess.e.r >= refguess.s.r) s["!ref"] = encode_range(refguess);
|
||||
if(opts.sheetRows > 0 && s["!ref"]) {
|
||||
var tmpref = safe_decode_range(s["!ref"]);
|
||||
@ -90,6 +95,7 @@ function parse_ws_xml(data/*:?string*/, opts, idx/*:number*/, rels, wb/*:WBWBPro
|
||||
}
|
||||
if(columns.length > 0) s["!cols"] = columns;
|
||||
if(merges.length > 0) s["!merges"] = merges;
|
||||
if(rels['!id'][s['!legrel']]) s['!legdrawel'] = rels['!id'][s['!legrel']];
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -149,7 +155,7 @@ function write_ws_xml_protection(sp)/*:string*/ {
|
||||
}
|
||||
|
||||
function parse_ws_xml_hlinks(s, data/*:Array<string>*/, rels) {
|
||||
var dense = Array.isArray(s);
|
||||
var dense = s["!data"] != null;
|
||||
for(var i = 0; i != data.length; ++i) {
|
||||
var val = parsexmltag(utf8read(data[i]), true);
|
||||
if(!val.ref) return;
|
||||
@ -165,11 +171,11 @@ function parse_ws_xml_hlinks(s, data/*:Array<string>*/, rels) {
|
||||
if(val.tooltip) { val.Tooltip = val.tooltip; delete val.tooltip; }
|
||||
var rng = safe_decode_range(val.ref);
|
||||
for(var R=rng.s.r;R<=rng.e.r;++R) for(var C=rng.s.c;C<=rng.e.c;++C) {
|
||||
var addr = encode_cell({c:C,r:R});
|
||||
var addr = encode_col(C) + encode_row(R);
|
||||
if(dense) {
|
||||
if(!s[R]) s[R] = [];
|
||||
if(!s[R][C]) s[R][C] = {t:"z",v:undefined};
|
||||
s[R][C].l = val;
|
||||
if(!s["!data"][R]) s["!data"][R] = [];
|
||||
if(!s["!data"][R][C]) s["!data"][R][C] = {t:"z",v:undefined};
|
||||
s["!data"][R][C].l = val;
|
||||
} else {
|
||||
if(!s[addr]) s[addr] = {t:"z",v:undefined};
|
||||
s[addr].l = val;
|
||||
@ -256,21 +262,27 @@ function write_ws_xml_sheetviews(ws, opts, idx, wb)/*:string*/ {
|
||||
return writextag("sheetViews", writextag("sheetView", null, sview), {});
|
||||
}
|
||||
|
||||
function write_ws_xml_cell(cell/*:Cell*/, ref, ws, opts/*::, idx, wb*/)/*:string*/ {
|
||||
function write_ws_xml_cell(cell/*:Cell*/, ref, ws, opts, idx, wb, date1904)/*:string*/ {
|
||||
if(cell.c) ws['!comments'].push([ref, cell.c]);
|
||||
if(cell.v === undefined && typeof cell.f !== "string" || cell.t === 'z' && !cell.f) return "";
|
||||
if((cell.v === undefined || cell.t === "z" && !(opts||{}).sheetStubs) && typeof cell.f !== "string" && typeof cell.z == "undefined") return "";
|
||||
var vv = "";
|
||||
var oldt = cell.t, oldv = cell.v;
|
||||
if(cell.t !== "z") switch(cell.t) {
|
||||
case 'b': vv = cell.v ? "1" : "0"; break;
|
||||
case 'n': vv = ''+cell.v; break;
|
||||
case 'n':
|
||||
if(isNaN(cell.v)) { cell.t = "e"; vv = BErr[cell.v = 0x24]; } // #NUM!
|
||||
else if(!isFinite(cell.v)) { cell.t = "e"; vv = BErr[cell.v = 0x07]; } // #DIV/0!
|
||||
else vv = ''+cell.v; break;
|
||||
case 'e': vv = BErr[cell.v]; break;
|
||||
case 'd':
|
||||
if(opts && opts.cellDates) vv = parseDate(cell.v, -1).toISOString();
|
||||
else {
|
||||
if(opts && opts.cellDates) {
|
||||
var _vv = parseDate(cell.v, date1904);
|
||||
vv = _vv.toISOString();
|
||||
if(_vv.getUTCFullYear() < 1900) vv = vv.slice(vv.indexOf("T") + 1).replace("Z","");
|
||||
} else {
|
||||
cell = dup(cell);
|
||||
cell.t = 'n';
|
||||
vv = ''+(cell.v = datenum(parseDate(cell.v)));
|
||||
vv = ''+(cell.v = datenum(parseDate(cell.v, date1904), date1904));
|
||||
}
|
||||
if(typeof cell.z === 'undefined') cell.z = table_fmt[14];
|
||||
break;
|
||||
@ -299,7 +311,10 @@ function write_ws_xml_cell(cell/*:Cell*/, ref, ws, opts/*::, idx, wb*/)/*:string
|
||||
var ff = cell.F && cell.F.slice(0, ref.length) == ref ? {t:"array", ref:cell.F} : null;
|
||||
v = writextag('f', escapexml(cell.f), ff) + (cell.v != null ? v : "");
|
||||
}
|
||||
if(cell.l) ws['!links'].push([ref, cell.l]);
|
||||
if(cell.l) {
|
||||
cell.l.display = escapexml(vv);
|
||||
ws['!links'].push([ref, cell.l]);
|
||||
}
|
||||
if(cell.D) o.cm = 1;
|
||||
return writextag('c', v, o);
|
||||
}
|
||||
@ -310,7 +325,7 @@ var parse_ws_xml_data = /*#__PURE__*/(function() {
|
||||
var refregex = /ref=["']([^"']*)["']/;
|
||||
var match_v = matchtag("v"), match_f = matchtag("f");
|
||||
|
||||
return function parse_ws_xml_data(sdata/*:string*/, s, opts, guess/*:Range*/, themes, styles) {
|
||||
return function parse_ws_xml_data(sdata/*:string*/, s, opts, guess/*:Range*/, themes, styles, wb) {
|
||||
var ri = 0, x = "", cells/*:Array<string>*/ = [], cref/*:?Array<string>*/ = [], idx=0, i=0, cc=0, d="", p/*:any*/;
|
||||
var tag, tagr = 0, tagc = 0;
|
||||
var sstr, ftag;
|
||||
@ -318,9 +333,10 @@ return function parse_ws_xml_data(sdata/*:string*/, s, opts, guess/*:Range*/, th
|
||||
var do_format = Array.isArray(styles.CellXf), cf;
|
||||
var arrayf/*:Array<[Range, string]>*/ = [];
|
||||
var sharedf = [];
|
||||
var dense = Array.isArray(s);
|
||||
var dense = s["!data"] != null;
|
||||
var rows/*:Array<RowInfo>*/ = [], rowobj = {}, rowrite = false;
|
||||
var sheetStubs = !!opts.sheetStubs;
|
||||
var date1904 = !!((wb||{}).WBProps||{}).date1904;
|
||||
for(var marr = sdata.split(rowregex), mt = 0, marrlen = marr.length; mt != marrlen; ++mt) {
|
||||
x = marr[mt].trim();
|
||||
var xlen = x.length;
|
||||
@ -349,8 +365,10 @@ return function parse_ws_xml_data(sdata/*:string*/, s, opts, guess/*:Range*/, th
|
||||
tag = parsexmltag(x.slice(rstarti,ri), true);
|
||||
tagr = tag.r != null ? parseInt(tag.r, 10) : tagr+1; tagc = -1;
|
||||
if(opts.sheetRows && opts.sheetRows < tagr) continue;
|
||||
if(guess.s.r > tagr - 1) guess.s.r = tagr - 1;
|
||||
if(guess.e.r < tagr - 1) guess.e.r = tagr - 1;
|
||||
if(!opts.nodim) {
|
||||
if(guess.s.r > tagr - 1) guess.s.r = tagr - 1;
|
||||
if(guess.e.r < tagr - 1) guess.e.r = tagr - 1;
|
||||
}
|
||||
|
||||
if(opts && opts.cellStyles) {
|
||||
rowobj = {}; rowrite = false;
|
||||
@ -386,19 +404,27 @@ return function parse_ws_xml_data(sdata/*:string*/, s, opts, guess/*:Range*/, th
|
||||
|
||||
if((cref=d.match(match_v))!= null && /*::cref != null && */cref[1] !== '') p.v=unescapexml(cref[1]);
|
||||
if(opts.cellFormula) {
|
||||
if((cref=d.match(match_f))!= null && /*::cref != null && */cref[1] !== '') {
|
||||
/* TODO: match against XLSXFutureFunctions */
|
||||
p.f=unescapexml(utf8read(cref[1]), true);
|
||||
if(!opts.xlfn) p.f = _xlfn(p.f);
|
||||
if(/*::cref != null && cref[0] != null && */cref[0].indexOf('t="array"') > -1) {
|
||||
p.F = (d.match(refregex)||[])[1];
|
||||
if(p.F.indexOf(":") > -1) arrayf.push([safe_decode_range(p.F), p.F]);
|
||||
} else if(/*::cref != null && cref[0] != null && */cref[0].indexOf('t="shared"') > -1) {
|
||||
// TODO: parse formula
|
||||
ftag = parsexmltag(cref[0]);
|
||||
var ___f = unescapexml(utf8read(cref[1]));
|
||||
if(!opts.xlfn) ___f = _xlfn(___f);
|
||||
sharedf[parseInt(ftag.si, 10)] = [ftag, ___f, tag.r];
|
||||
if((cref=d.match(match_f))!= null /*:: && cref != null*/) {
|
||||
if(cref[1] == "") {
|
||||
if(/*::cref != null && cref[0] != null && */cref[0].indexOf('t="shared"') > -1) {
|
||||
// TODO: parse formula
|
||||
ftag = parsexmltag(cref[0]);
|
||||
if(sharedf[ftag.si]) p.f = shift_formula_xlsx(sharedf[ftag.si][1], sharedf[ftag.si][2]/*[0].ref*/, tag.r);
|
||||
}
|
||||
} else {
|
||||
/* TODO: match against XLSXFutureFunctions */
|
||||
p.f=unescapexml(utf8read(cref[1]), true);
|
||||
if(!opts.xlfn) p.f = _xlfn(p.f);
|
||||
if(/*::cref != null && cref[0] != null && */cref[0].indexOf('t="array"') > -1) {
|
||||
p.F = (d.match(refregex)||[])[1];
|
||||
if(p.F.indexOf(":") > -1) arrayf.push([safe_decode_range(p.F), p.F]);
|
||||
} else if(/*::cref != null && cref[0] != null && */cref[0].indexOf('t="shared"') > -1) {
|
||||
// TODO: parse formula
|
||||
ftag = parsexmltag(cref[0]);
|
||||
var ___f = unescapexml(utf8read(cref[1]));
|
||||
if(!opts.xlfn) ___f = _xlfn(___f);
|
||||
sharedf[parseInt(ftag.si, 10)] = [ftag, ___f, tag.r];
|
||||
}
|
||||
}
|
||||
} else if((cref=d.match(/<f[^>]*\/>/))) {
|
||||
ftag = parsexmltag(cref[0]);
|
||||
@ -455,8 +481,8 @@ return function parse_ws_xml_data(sdata/*:string*/, s, opts, guess/*:Range*/, th
|
||||
break;
|
||||
case 'b': p.v = parsexmlbool(p.v); break;
|
||||
case 'd':
|
||||
if(opts.cellDates) p.v = parseDate(p.v, 1);
|
||||
else { p.v = datenum(parseDate(p.v, 1)); p.t = 'n'; }
|
||||
if(opts.cellDates) p.v = parseDate(p.v, date1904);
|
||||
else { p.v = datenum(parseDate(p.v, date1904), date1904); p.t = 'n'; }
|
||||
break;
|
||||
/* error string in .w, number in .v */
|
||||
case 'e':
|
||||
@ -475,16 +501,22 @@ return function parse_ws_xml_data(sdata/*:string*/, s, opts, guess/*:Range*/, th
|
||||
}
|
||||
}
|
||||
}
|
||||
safe_format(p, fmtid, fillid, opts, themes, styles);
|
||||
if(opts.cellDates && do_format && p.t == 'n' && fmt_is_date(table_fmt[fmtid])) { p.t = 'd'; p.v = numdate(p.v); }
|
||||
safe_format(p, fmtid, fillid, opts, themes, styles, date1904);
|
||||
if(opts.cellDates && do_format && p.t == 'n' && fmt_is_date(table_fmt[fmtid])) { p.v = numdate(p.v + (date1904 ? 1462 : 0)); p.t = typeof p.v == "number" ? 'n' : 'd'; }
|
||||
if(tag.cm && opts.xlmeta) {
|
||||
var cm = (opts.xlmeta.Cell||[])[+tag.cm-1];
|
||||
if(cm && cm.type == 'XLDAPR') p.D = true;
|
||||
}
|
||||
var _r;
|
||||
if(opts.nodim) {
|
||||
_r = decode_cell(tag.r);
|
||||
if(guess.s.r > _r.r) guess.s.r = _r.r;
|
||||
if(guess.e.r < _r.r) guess.e.r = _r.r;
|
||||
}
|
||||
if(dense) {
|
||||
var _r = decode_cell(tag.r);
|
||||
if(!s[_r.r]) s[_r.r] = [];
|
||||
s[_r.r][_r.c] = p;
|
||||
_r = decode_cell(tag.r);
|
||||
if(!s["!data"][_r.r]) s["!data"][_r.r] = [];
|
||||
s["!data"][_r.r][_r.c] = p;
|
||||
} else s[tag.r] = p;
|
||||
}
|
||||
}
|
||||
@ -493,17 +525,18 @@ return function parse_ws_xml_data(sdata/*:string*/, s, opts, guess/*:Range*/, th
|
||||
|
||||
function write_ws_xml_data(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbook*//*::, rels*/)/*:string*/ {
|
||||
var o/*:Array<string>*/ = [], r/*:Array<string>*/ = [], range = safe_decode_range(ws['!ref']), cell="", ref, rr = "", cols/*:Array<string>*/ = [], R=0, C=0, rows = ws['!rows'];
|
||||
var dense = Array.isArray(ws);
|
||||
var dense = ws["!data"] != null;
|
||||
var params = ({r:rr}/*:any*/), row/*:RowInfo*/, height = -1;
|
||||
var date1904 = (((wb||{}).Workbook||{}).WBProps||{}).date1904;
|
||||
for(C = range.s.c; C <= range.e.c; ++C) cols[C] = encode_col(C);
|
||||
for(R = range.s.r; R <= range.e.r; ++R) {
|
||||
r = [];
|
||||
rr = encode_row(R);
|
||||
for(C = range.s.c; C <= range.e.c; ++C) {
|
||||
ref = cols[C] + rr;
|
||||
var _cell = dense ? (ws[R]||[])[C]: ws[ref];
|
||||
var _cell = dense ? (ws["!data"][R]||[])[C]: ws[ref];
|
||||
if(_cell === undefined) continue;
|
||||
if((cell = write_ws_xml_cell(_cell, ref, ws, opts, idx, wb)) != null) r.push(cell);
|
||||
if((cell = write_ws_xml_cell(_cell, ref, ws, opts, idx, wb, date1904)) != null) r.push(cell);
|
||||
}
|
||||
if(r.length > 0 || (rows && rows[R])) {
|
||||
params = ({r:rr}/*:any*/);
|
||||
@ -609,6 +642,7 @@ function write_ws_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ {
|
||||
}
|
||||
if((relc = l[1].Target.indexOf("#")) > -1) rel.location = escapexml(l[1].Target.slice(relc+1));
|
||||
if(l[1].Tooltip) rel.tooltip = escapexml(l[1].Tooltip);
|
||||
rel.display = l[1].display;
|
||||
o[o.length] = writextag("hyperlink",null,rel);
|
||||
});
|
||||
o[o.length] = "</hyperlinks>";
|
||||
|
@ -41,12 +41,13 @@ function write_BrtRowHdr(R/*:number*/, range, ws) {
|
||||
o.l += 4;
|
||||
|
||||
var caddr = {r:R, c:0};
|
||||
var dense = ws["!data"] != null;
|
||||
for(var i = 0; i < 16; ++i) {
|
||||
if((range.s.c > ((i+1) << 10)) || (range.e.c < (i << 10))) continue;
|
||||
var first = -1, last = -1;
|
||||
for(var j = (i<<10); j < ((i+1)<<10); ++j) {
|
||||
caddr.c = j;
|
||||
var cell = Array.isArray(ws) ? (ws[caddr.r]||[])[caddr.c] : ws[encode_cell(caddr)];
|
||||
var cell = dense ? (ws["!data"][caddr.r]||[])[caddr.c] : ws[encode_cell(caddr)];
|
||||
if(cell) { if(first < 0) first = j; last = j; }
|
||||
}
|
||||
if(first < 0) continue;
|
||||
@ -258,9 +259,10 @@ function parse_BrtCellSt(data) {
|
||||
return [cell, value, 'str'];
|
||||
}
|
||||
function write_BrtCellSt(cell, ncell, o) {
|
||||
var data = cell.v == null ? "" : String(cell.v);
|
||||
if(o == null) o = new_buf(12 + 4 * cell.v.length);
|
||||
write_XLSBCell(ncell, o);
|
||||
write_XLWideString(cell.v, o);
|
||||
write_XLWideString(data, o);
|
||||
return o.length > o.l ? o.slice(0, o.l) : o;
|
||||
}
|
||||
function parse_BrtShortSt(data) {
|
||||
@ -269,9 +271,10 @@ function parse_BrtShortSt(data) {
|
||||
return [cell, value, 'str'];
|
||||
}
|
||||
function write_BrtShortSt(cell, ncell, o) {
|
||||
if(o == null) o = new_buf(8 + 4 * cell.v.length);
|
||||
var data = cell.v == null ? "" : String(cell.v);
|
||||
if(o == null) o = new_buf(8 + 4 * data.length);
|
||||
write_XLSBShortCell(ncell, o);
|
||||
write_XLWideString(cell.v, o);
|
||||
write_XLWideString(data, o);
|
||||
return o.length > o.l ? o.slice(0, o.l) : o;
|
||||
}
|
||||
|
||||
@ -508,7 +511,7 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
|
||||
var opts = _opts || {};
|
||||
if(!rels) rels = {'!id':{}};
|
||||
if(DENSE != null && opts.dense == null) opts.dense = DENSE;
|
||||
var s/*:Worksheet*/ = (opts.dense ? [] : {});
|
||||
var s/*:Worksheet*/ = ({}); if(opts.dense) s["!data"] = [];
|
||||
|
||||
var ref;
|
||||
var refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
|
||||
@ -539,6 +542,7 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
|
||||
XLSBRecordEnum[0x0010] = { n:"BrtShortReal", f:parse_BrtShortReal };
|
||||
|
||||
var cm, vm;
|
||||
var date1904 = 1462 * +!!((wb||{}).WBProps||{}).date1904;
|
||||
|
||||
recordhopper(data, function ws_parse(val, RR, RT) {
|
||||
if(end) return;
|
||||
@ -582,9 +586,9 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
|
||||
case 'str': p.t = 's'; p.v = val[1]; break;
|
||||
case 'is': p.t = 's'; p.v = val[1].t; break;
|
||||
}
|
||||
if((cf = styles.CellXf[val[0].iStyleRef])) safe_format(p,cf.numFmtId,null,opts, themes, styles);
|
||||
if((cf = styles.CellXf[val[0].iStyleRef])) safe_format(p,cf.numFmtId,null,opts, themes, styles, date1904>0);
|
||||
C = val[0].c == -1 ? C + 1 : val[0].c;
|
||||
if(opts.dense) { if(!s[R]) s[R] = []; s[R][C] = p; }
|
||||
if(opts.dense) { if(!s["!data"][R]) s["!data"][R] = []; s["!data"][R][C] = p; }
|
||||
else s[encode_col(C) + rr] = p;
|
||||
if(opts.cellFormula) {
|
||||
af = false;
|
||||
@ -603,7 +607,7 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
|
||||
if(refguess.e.r < row.r) refguess.e.r = row.r;
|
||||
if(refguess.e.c < C) refguess.e.c = C;
|
||||
if(opts.cellDates && cf && p.t == 'n' && fmt_is_date(table_fmt[cf.numFmtId])) {
|
||||
var _d = SSF_parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
|
||||
var _d = SSF_parse_date_code(p.v + date1904); if(_d) { p.t = 'd'; p.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
}
|
||||
if(cm) {
|
||||
if(cm.type == 'XLDAPR') p.D = true;
|
||||
@ -617,7 +621,7 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
|
||||
if(!opts.sheetStubs || pass) break;
|
||||
p = ({t:'z',v:void 0}/*:any*/);
|
||||
C = val[0].c == -1 ? C + 1 : val[0].c;
|
||||
if(opts.dense) { if(!s[R]) s[R] = []; s[R][C] = p; }
|
||||
if(opts.dense) { if(!s["!data"][R]) s["!data"][R] = []; s["!data"][R][C] = p; }
|
||||
else s[encode_col(C) + rr] = p;
|
||||
if(refguess.s.r > row.r) refguess.s.r = row.r;
|
||||
if(refguess.s.c > C) refguess.s.c = C;
|
||||
@ -648,11 +652,11 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
|
||||
}
|
||||
for(R=val.rfx.s.r;R<=val.rfx.e.r;++R) for(C=val.rfx.s.c;C<=val.rfx.e.c;++C) {
|
||||
if(opts.dense) {
|
||||
if(!s[R]) s[R] = [];
|
||||
if(!s[R][C]) s[R][C] = {t:'z',v:undefined};
|
||||
s[R][C].l = val;
|
||||
if(!s["!data"][R]) s["!data"][R] = [];
|
||||
if(!s["!data"][R][C]) s["!data"][R][C] = {t:'z',v:undefined};
|
||||
s["!data"][R][C].l = val;
|
||||
} else {
|
||||
addr = encode_cell({c:C,r:R});
|
||||
addr = encode_col(C) + encode_row(R);
|
||||
if(!s[addr]) s[addr] = {t:'z',v:undefined};
|
||||
s[addr].l = val;
|
||||
}
|
||||
@ -662,14 +666,14 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
|
||||
case 0x01AA: /* 'BrtArrFmla' */
|
||||
if(!opts.cellFormula) break;
|
||||
arrayf.push(val);
|
||||
cell = ((opts.dense ? s[R][C] : s[encode_col(C) + rr])/*:any*/);
|
||||
cell = ((opts.dense ? s["!data"][R][C] : s[encode_col(C) + rr])/*:any*/);
|
||||
cell.f = stringify_formula(val[1], refguess, {r:row.r, c:C}, supbooks, opts);
|
||||
cell.F = encode_range(val[0]);
|
||||
break;
|
||||
case 0x01AB: /* 'BrtShrFmla' */
|
||||
if(!opts.cellFormula) break;
|
||||
sharedf[encode_cell(val[0].s)] = val[1];
|
||||
cell = (opts.dense ? s[R][C] : s[encode_col(C) + rr]);
|
||||
cell = (opts.dense ? s["!data"][R][C] : s[encode_col(C) + rr]);
|
||||
cell.f = stringify_formula(val[1], refguess, {r:row.r, c:C}, supbooks, opts);
|
||||
break;
|
||||
|
||||
@ -683,6 +687,10 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0227: /* 'BrtLegacyDrawing' */
|
||||
if(val) s["!legrel"] = val;
|
||||
break;
|
||||
|
||||
case 0x00A1: /* 'BrtBeginAFilter' */
|
||||
s['!autofilter'] = { ref:encode_range(val) };
|
||||
break;
|
||||
@ -739,7 +747,6 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
|
||||
case 0x0499: /* 'BrtFilter14' */
|
||||
case 0x00A9: /* 'BrtIconFilter' */
|
||||
case 0x049D: /* 'BrtIconFilter14' */
|
||||
case 0x0227: /* 'BrtLegacyDrawing' */
|
||||
case 0x0228: /* 'BrtLegacyDrawingHF' */
|
||||
case 0x0295: /* 'BrtListPart' */
|
||||
case 0x027F: /* 'BrtOleObject' */
|
||||
@ -798,11 +805,12 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
|
||||
if(merges.length > 0) s["!merges"] = merges;
|
||||
if(colinfo.length > 0) s["!cols"] = colinfo;
|
||||
if(rowinfo.length > 0) s["!rows"] = rowinfo;
|
||||
if(rels['!id'][s['!legrel']]) s['!legdrawel'] = rels['!id'][s['!legrel']];
|
||||
return s;
|
||||
}
|
||||
|
||||
/* TODO: something useful -- this is a stub */
|
||||
function write_ws_bin_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts, ws/*:Worksheet*/, last_seen/*:boolean*/)/*:boolean*/ {
|
||||
function write_ws_bin_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts, ws/*:Worksheet*/, last_seen/*:boolean*/, date1904/*:boolean*/)/*:boolean*/ {
|
||||
var o/*:any*/ = ({r:R, c:C}/*:any*/);
|
||||
if(cell.c) ws['!comments'].push([encode_cell(o), cell.c]);
|
||||
if(cell.v === undefined) return false;
|
||||
@ -812,7 +820,7 @@ function write_ws_bin_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:num
|
||||
case 'd': // no BrtCellDate :(
|
||||
cell = dup(cell);
|
||||
cell.z = cell.z || table_fmt[14];
|
||||
cell.v = datenum(parseDate(cell.v)); cell.t = 'n';
|
||||
cell.v = datenum(parseDate(cell.v, date1904), date1904); cell.t = 'n';
|
||||
break;
|
||||
/* falls through */
|
||||
case 'n': case 'e': vv = ''+cell.v; break;
|
||||
@ -824,7 +832,7 @@ function write_ws_bin_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:num
|
||||
switch(cell.t) {
|
||||
case 's': case 'str':
|
||||
if(opts.bookSST) {
|
||||
vv = get_sst_id(opts.Strings, (cell.v/*:any*/), opts.revStrings);
|
||||
vv = get_sst_id(opts.Strings, (cell.v == null ? "" : String(cell.v)/*:any*/), opts.revStrings);
|
||||
o.t = "s"; o.v = vv;
|
||||
if(last_seen) write_record(ba, 0x0012 /* BrtShortIsst */, write_BrtShortIsst(cell, o));
|
||||
else write_record(ba, 0x0007 /* BrtCellIsst */, write_BrtCellIsst(cell, o));
|
||||
@ -839,6 +847,12 @@ function write_ws_bin_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:num
|
||||
if(cell.v == (cell.v | 0) && cell.v > -1000 && cell.v < 1000) {
|
||||
if(last_seen) write_record(ba, 0x000D /* BrtShortRk */, write_BrtShortRk(cell, o));
|
||||
else write_record(ba, 0x0002 /* BrtCellRk */, write_BrtCellRk(cell, o));
|
||||
} else if(isNaN(cell.v)) {
|
||||
if(last_seen) write_record(ba, 0x000E /* BrtShortError */, write_BrtShortError({t:"e", v: 0x24}, o)); // #NUM!
|
||||
else write_record(ba, 0x0003 /* BrtCellError */, write_BrtCellError({t:"e", v: 0x24}, o)); // #NUM!
|
||||
} else if(!isFinite(cell.v)) {
|
||||
if(last_seen) write_record(ba, 0x000E /* BrtShortError */, write_BrtShortError({t:"e", v: 0x07}, o)); // #DIV/0!
|
||||
else write_record(ba, 0x0003 /* BrtCellError */, write_BrtCellError({t:"e", v: 0x07}, o)); // #DIV/0!
|
||||
} else {
|
||||
if(last_seen) write_record(ba, 0x0010 /* BrtShortReal */, write_BrtShortReal(cell, o));
|
||||
else write_record(ba, 0x0005 /* BrtCellReal */, write_BrtCellReal(cell, o));
|
||||
@ -859,10 +873,11 @@ function write_ws_bin_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:num
|
||||
return true;
|
||||
}
|
||||
|
||||
function write_CELLTABLE(ba, ws/*:Worksheet*/, idx/*:number*/, opts/*::, wb:Workbook*/) {
|
||||
function write_CELLTABLE(ba, ws/*:Worksheet*/, idx/*:number*/, opts, wb/*:Workbook*/) {
|
||||
var range = safe_decode_range(ws['!ref'] || "A1"), ref, rr = "", cols/*:Array<string>*/ = [];
|
||||
var date1904 = (((wb||{}).Workbook||{}).WBProps||{}).date1904;
|
||||
write_record(ba, 0x0091 /* BrtBeginSheetData */);
|
||||
var dense = Array.isArray(ws);
|
||||
var dense = ws["!data"] != null;
|
||||
var cap = range.e.r;
|
||||
if(ws['!rows']) cap = Math.max(range.e.r, ws['!rows'].length - 1);
|
||||
for(var R = range.s.r; R <= cap; ++R) {
|
||||
@ -875,10 +890,10 @@ function write_CELLTABLE(ba, ws/*:Worksheet*/, idx/*:number*/, opts/*::, wb:Work
|
||||
/* *16384CELL */
|
||||
if(R === range.s.r) cols[C] = encode_col(C);
|
||||
ref = cols[C] + rr;
|
||||
var cell = dense ? (ws[R]||[])[C] : ws[ref];
|
||||
var cell = dense ? (ws["!data"][R]||[])[C] : ws[ref];
|
||||
if(!cell) { last_seen = false; continue; }
|
||||
/* write cell */
|
||||
last_seen = write_ws_bin_cell(ba, cell, R, C, opts, ws, last_seen);
|
||||
last_seen = write_ws_bin_cell(ba, cell, R, C, opts, ws, last_seen, date1904);
|
||||
}
|
||||
}
|
||||
write_record(ba, 0x0092 /* BrtEndSheetData */);
|
||||
|
@ -34,7 +34,10 @@ function parse_chart(data/*:?string*/, name/*:string*/, opts, rels, wb, csheet)
|
||||
refguess.e.c = C;
|
||||
col = encode_col(C);
|
||||
cache[0].forEach(function(n,i) {
|
||||
cs[col + encode_row(i)] = {t:'n', v:n, z:cache[1] };
|
||||
if(cs["!data"]) {
|
||||
if(!cs["!data"][i]) cs["!data"][i] = [];
|
||||
cs["!data"][i][C] = {t:'n', v:n, z:cache[1] };
|
||||
} else cs[col + encode_row(i)] = {t:'n', v:n, z:cache[1] };
|
||||
R = i;
|
||||
});
|
||||
if(refguess.e.r < R) refguess.e.r = R;
|
||||
|
@ -113,16 +113,19 @@ function safe1904(wb/*:Workbook*/)/*:string*/ {
|
||||
return parsexmlbool(wb.Workbook.WBProps.date1904) ? "true" : "false";
|
||||
}
|
||||
|
||||
var badchars = /*#__PURE__*/"][*?\/\\".split("");
|
||||
var badchars = /*#__PURE__*/":][*?\/\\".split("");
|
||||
function check_ws_name(n/*:string*/, safe/*:?boolean*/)/*:boolean*/ {
|
||||
if(n.length > 31) { if(safe) return false; throw new Error("Sheet names cannot exceed 31 chars"); }
|
||||
var _good = true;
|
||||
badchars.forEach(function(c) {
|
||||
if(n.indexOf(c) == -1) return;
|
||||
if(!safe) throw new Error("Sheet name cannot contain : \\ / ? * [ ]");
|
||||
_good = false;
|
||||
});
|
||||
return _good;
|
||||
try {
|
||||
if(n == "") throw new Error("Sheet name cannot be blank");
|
||||
if(n.length > 31) throw new Error("Sheet name cannot exceed 31 chars");
|
||||
if(n.charCodeAt(0) == 0x27 || n.charCodeAt(n.length - 1) == 0x27) throw new Error("Sheet name cannot start or end with apostrophe (')");
|
||||
if(n.toLowerCase() == "history") throw new Error("Sheet name cannot be 'History'");
|
||||
badchars.forEach(function(c) {
|
||||
if(n.indexOf(c) == -1) return;
|
||||
throw new Error("Sheet name cannot contain : \\ / ? * [ ]");
|
||||
});
|
||||
} catch(e) { if(safe) return false; throw e; }
|
||||
return true;
|
||||
}
|
||||
function check_wb_names(N, S, codes) {
|
||||
N.forEach(function(n,i) {
|
||||
|
@ -45,6 +45,7 @@ function write_BrtWbProp(data/*:?WBProps*/, o) {
|
||||
var flags = 0;
|
||||
if(data) {
|
||||
/* TODO: mirror parse_BrtWbProp fields */
|
||||
if(data.date1904) flags |= 0x01;
|
||||
if(data.filterPrivacy) flags |= 0x08;
|
||||
}
|
||||
o.write_shift(4, flags);
|
||||
|
@ -40,10 +40,10 @@ function xlml_parsexmltagobj(tag/*:string*/) {
|
||||
/* map from xlml named formats to SSF TODO: localize */
|
||||
var XLMLFormatMap/*: {[string]:string}*/;
|
||||
|
||||
function xlml_format(format, value)/*:string*/ {
|
||||
function xlml_format(format, value, date1904)/*:string*/ {
|
||||
var fmt = XLMLFormatMap[format] || unescapexml(format);
|
||||
if(fmt === "General") return SSF_general(value);
|
||||
return SSF_format(fmt, value);
|
||||
return SSF_format(fmt, value, {date1904: !!date1904});
|
||||
}
|
||||
|
||||
function xlml_set_custprop(Custprops, key, cp, val/*:string*/) {
|
||||
@ -59,7 +59,7 @@ function xlml_set_custprop(Custprops, key, cp, val/*:string*/) {
|
||||
Custprops[unescapexml(key)] = oval;
|
||||
}
|
||||
|
||||
function safe_format_xlml(cell/*:Cell*/, nf, o) {
|
||||
function safe_format_xlml(cell/*:Cell*/, nf, o, date1904) {
|
||||
if(cell.t === 'z') return;
|
||||
if(!o || o.cellText !== false) try {
|
||||
if(cell.t === 'e') { cell.w = cell.w || BErr[cell.v]; }
|
||||
@ -70,13 +70,13 @@ function safe_format_xlml(cell/*:Cell*/, nf, o) {
|
||||
}
|
||||
else cell.w = SSF_general(cell.v);
|
||||
}
|
||||
else cell.w = xlml_format(nf||"General", cell.v);
|
||||
else cell.w = xlml_format(nf||"General", cell.v, date1904);
|
||||
} catch(e) { if(o.WTF) throw e; }
|
||||
try {
|
||||
var z = XLMLFormatMap[nf]||nf||"General";
|
||||
if(o.cellNF) cell.z = z;
|
||||
if(o.cellDates && cell.t == 'n' && fmt_is_date(z)) {
|
||||
var _d = SSF_parse_date_code(cell.v); if(_d) { cell.t = 'd'; cell.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
|
||||
var _d = SSF_parse_date_code(cell.v + (date1904 ? 1462 : 0)); if(_d) { cell.t = 'd'; cell.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
}
|
||||
} catch(e) { if(o.WTF) throw e; }
|
||||
}
|
||||
@ -92,7 +92,7 @@ function process_style_xlml(styles, stag, opts) {
|
||||
}
|
||||
|
||||
/* TODO: there must exist some form of OSP-blessed spec */
|
||||
function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, arrayf, o) {
|
||||
function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, arrayf, o, date1904) {
|
||||
var nf = "General", sid = cell.StyleID, S = {}; o = o || {};
|
||||
var interiors = [];
|
||||
var i = 0;
|
||||
@ -115,9 +115,8 @@ function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, a
|
||||
break;
|
||||
case 'DateTime':
|
||||
if(xml.slice(-1) != "Z") xml += "Z";
|
||||
cell.v = (parseDate(xml) - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
|
||||
cell.v = datenum(parseDate(xml, date1904), date1904);
|
||||
if(cell.v !== cell.v) cell.v = unescapexml(xml);
|
||||
else if(cell.v<60) cell.v = cell.v -1;
|
||||
if(!nf || nf == "General") nf = "yyyy-mm-dd";
|
||||
/* falls through */
|
||||
case 'Number':
|
||||
@ -130,7 +129,7 @@ function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, a
|
||||
else { cell.t = 's'; cell.v = xlml_fixstr(ss||xml); }
|
||||
break;
|
||||
}
|
||||
safe_format_xlml(cell, nf, o);
|
||||
safe_format_xlml(cell, nf, o, date1904);
|
||||
if(o.cellFormula !== false) {
|
||||
if(cell.Formula) {
|
||||
var fstr = unescapexml(cell.Formula);
|
||||
@ -207,7 +206,7 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ {
|
||||
var Rn;
|
||||
var state = [], tmp;
|
||||
if(DENSE != null && opts.dense == null) opts.dense = DENSE;
|
||||
var sheets = {}, sheetnames/*:Array<string>*/ = [], cursheet/*:Worksheet*/ = (opts.dense ? [] : {}), sheetname = "";
|
||||
var sheets = {}, sheetnames/*:Array<string>*/ = [], cursheet/*:Worksheet*/ = ({}), sheetname = ""; if(opts.dense) cursheet["!data"] = [];
|
||||
var cell = ({}/*:any*/), row = {};// eslint-disable-line no-unused-vars
|
||||
var dtag = xlml_parsexmltag('<Data ss:Type="String">'), didx = 0;
|
||||
var c = 0, r = 0;
|
||||
@ -232,7 +231,7 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ {
|
||||
break;
|
||||
}
|
||||
if(state[state.length-1][1]) break;
|
||||
if(Rn[1]==='/') parse_xlml_data(str.slice(didx, Rn.index), ss, dtag, state[state.length-1][0]==/*"Comment"*/"comment"?comment:cell, {c:c,r:r}, styles, cstys[c], row, arrayf, opts);
|
||||
if(Rn[1]==='/') parse_xlml_data(str.slice(didx, Rn.index), ss, dtag, state[state.length-1][0]==/*"Comment"*/"comment"?comment:cell, {c:c,r:r}, styles, cstys[c], row, arrayf, opts, Workbook.WBProps.date1904);
|
||||
else { ss = ""; dtag = xlml_parsexmltag(Rn[0]); didx = Rn.index + Rn[0].length; }
|
||||
break;
|
||||
case 'cell' /*case 'Cell'*/:
|
||||
@ -240,8 +239,8 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ {
|
||||
if(comments.length > 0) cell.c = comments;
|
||||
if((!opts.sheetRows || opts.sheetRows > r) && cell.v !== void 0) {
|
||||
if(opts.dense) {
|
||||
if(!cursheet[r]) cursheet[r] = [];
|
||||
cursheet[r][c] = cell;
|
||||
if(!cursheet["!data"][r]) cursheet["!data"][r] = [];
|
||||
cursheet["!data"][r][c] = cell;
|
||||
} else cursheet[encode_col(c) + encode_row(r)] = cell;
|
||||
}
|
||||
if(cell.HRef) {
|
||||
@ -261,8 +260,8 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ {
|
||||
for(var cmd = r; cmd <= rr; ++cmd) {
|
||||
if(cma > c || cmd > r) {
|
||||
if(opts.dense) {
|
||||
if(!cursheet[cmd]) cursheet[cmd] = [];
|
||||
cursheet[cmd][cma] = {t:'z'};
|
||||
if(!cursheet["!data"][cmd]) cursheet["!data"][cmd] = [];
|
||||
cursheet["!data"][cmd][cma] = {t:'z'};
|
||||
} else cursheet[encode_col(cma) + encode_row(cmd)] = {t:'z'};
|
||||
}
|
||||
}
|
||||
@ -321,7 +320,7 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ {
|
||||
state.push([Rn[3], false]);
|
||||
tmp = xlml_parsexmltag(Rn[0]);
|
||||
sheetname = unescapexml(tmp.Name);
|
||||
cursheet = (opts.dense ? [] : {});
|
||||
cursheet = ({}); if(opts.dense) cursheet["!data"] = [];
|
||||
merges = [];
|
||||
arrayf = [];
|
||||
rowinfo = [];
|
||||
@ -443,6 +442,7 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ {
|
||||
} else {
|
||||
state.push([Rn[3], false]);
|
||||
tmp = xlml_parsexmltag(Rn[0]);
|
||||
if(!parsexmlbool(tmp["ShowAlways"]||"0")) comments.hidden = true;
|
||||
comment = ({a:tmp.Author}/*:any*/);
|
||||
}
|
||||
break;
|
||||
@ -534,6 +534,9 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ {
|
||||
/*:: if(!Workbook.WBProps) Workbook.WBProps = {}; */
|
||||
Workbook.WBProps.date1904 = true;
|
||||
break;
|
||||
case 'hidehorizontalscrollbar' /*case 'HideHorizontalScrollBar'*/: break;
|
||||
case 'hideverticalscrollbar' /*case 'HideVerticalScrollBar'*/: break;
|
||||
case 'hideworkbooktabs' /*case 'HideWorkbookTabs'*/: break;
|
||||
case 'windowheight' /*case 'WindowHeight'*/: break;
|
||||
case 'windowwidth' /*case 'WindowWidth'*/: break;
|
||||
case 'windowtopx' /*case 'WindowTopX'*/: break;
|
||||
@ -919,6 +922,7 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ {
|
||||
out.SSF = dup(table_fmt);
|
||||
out.Props = Props;
|
||||
out.Custprops = Custprops;
|
||||
out.bookType = "xlml";
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -942,9 +946,10 @@ function write_props_xlml(wb/*:Workbook*/, opts)/*:string*/ {
|
||||
return o.join("");
|
||||
}
|
||||
/* TODO */
|
||||
function write_wb_xlml(/*::wb, opts*/)/*:string*/ {
|
||||
function write_wb_xlml(wb/*::, opts*/)/*:string*/ {
|
||||
/* OfficeDocumentSettings */
|
||||
/* ExcelWorkbook */
|
||||
if((((wb||{}).Workbook||{}).WBProps||{}).date1904) return '<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel"><Date1904/></ExcelWorkbook>';
|
||||
return "";
|
||||
}
|
||||
/* TODO */
|
||||
@ -1100,11 +1105,15 @@ function write_ws_xlml_wsopts(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workb
|
||||
return writextag("WorksheetOptions", o.join(""), {xmlns:XLMLNS.x});
|
||||
}
|
||||
function write_ws_xlml_comment(comments/*:Array<any>*/)/*:string*/ {
|
||||
/* TODO: test multiple comments */
|
||||
return comments.map(function(c) {
|
||||
// TODO: formatted text
|
||||
var t = xlml_unfixstr(c.t||"");
|
||||
var d =writextag("ss:Data", t, {"xmlns":"http://www.w3.org/TR/REC-html40"});
|
||||
return writextag("Comment", d, {"ss:Author":c.a});
|
||||
var p = {};
|
||||
if(c.a) p["ss:Author"] = c.a;
|
||||
if(!comments.hidden) p["ss:ShowAlways"] = "1";
|
||||
return writextag("Comment", d, p);
|
||||
}).join("");
|
||||
}
|
||||
function write_ws_xlml_cell(cell, ref/*:string*/, ws, opts, idx/*:number*/, wb, addr)/*:string*/{
|
||||
@ -1175,7 +1184,7 @@ function write_ws_xlml_table(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbo
|
||||
if(n.hidden) k['ss:Hidden']="1";
|
||||
o.push(writextag("Column",null,k));
|
||||
});
|
||||
var dense = Array.isArray(ws);
|
||||
var dense = ws["!data"] != null;
|
||||
for(var R = range.s.r; R <= range.e.r; ++R) {
|
||||
var row = [write_ws_xlml_row(R, (ws['!rows']||[])[R])];
|
||||
for(var C = range.s.c; C <= range.e.c; ++C) {
|
||||
@ -1190,7 +1199,7 @@ function write_ws_xlml_table(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbo
|
||||
}
|
||||
if(skip) continue;
|
||||
var addr = {r:R,c:C};
|
||||
var ref = encode_cell(addr), cell = dense ? (ws[R]||[])[C] : ws[ref];
|
||||
var ref = encode_col(C) + encode_row(R), cell = dense ? (ws["!data"][R]||[])[C] : ws[ref];
|
||||
row.push(write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr));
|
||||
}
|
||||
row.push("</Row>");
|
||||
|
@ -77,7 +77,7 @@ function safe_format_xf(p/*:any*/, opts/*:ParseOpts*/, date1904/*:?boolean*/) {
|
||||
var fmtid = 0;
|
||||
try {
|
||||
fmtid = p.z || p.XF.numFmtId || 0;
|
||||
if(opts.cellNF) p.z = table_fmt[fmtid];
|
||||
if(opts.cellNF && p.z == null) p.z = table_fmt[fmtid];
|
||||
} catch(e) { if(opts.WTF) throw e; }
|
||||
if(!opts || opts.cellText !== false) try {
|
||||
if(p.t === 'e') { p.w = p.w || BErr[p.v]; }
|
||||
@ -91,7 +91,7 @@ function safe_format_xf(p/*:any*/, opts/*:ParseOpts*/, date1904/*:?boolean*/) {
|
||||
else p.w = SSF_format(fmtid,p.v, {date1904:!!date1904, dateNF: opts && opts.dateNF});
|
||||
} catch(e) { if(opts.WTF) throw e; }
|
||||
if(opts.cellDates && fmtid && p.t == 'n' && fmt_is_date(table_fmt[fmtid] || String(fmtid))) {
|
||||
var _d = SSF_parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
|
||||
var _d = SSF_parse_date_code(p.v + (date1904 ? 1462 : 0)); if(_d) { p.t = 'd'; p.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
var wb = ({opts:{}}/*:any*/);
|
||||
var Sheets = {};
|
||||
if(DENSE != null && options.dense == null) options.dense = DENSE;
|
||||
var out/*:Worksheet*/ = ((options.dense ? [] : {})/*:any*/);
|
||||
var out/*:Worksheet*/ = ({}/*:any*/); if(options.dense) out["!data"] = [];
|
||||
var Directory = {};
|
||||
var range/*:Range*/ = ({}/*:any*/);
|
||||
var last_formula = null;
|
||||
@ -119,6 +119,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
var XFs = []; /* XF records */
|
||||
var palette/*:Array<[number, number, number]>*/ = [];
|
||||
var Workbook/*:WBWBProps*/ = ({ Sheets:[], WBProps:{date1904:false}, Views:[{}] }/*:any*/), wsprops = {};
|
||||
var biff4w = false;
|
||||
var get_rgb = function getrgb(icv/*:number*/)/*:[number, number, number]*/ {
|
||||
if(icv < 8) return XLSIcv[icv];
|
||||
if(icv < 64) return palette[icv-8] || XLSIcv[icv];
|
||||
@ -134,7 +135,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
if((t = rgb2Hex(get_rgb(xfd.icvBack)))) { line.s.bgColor = {rgb:t}; }
|
||||
};
|
||||
var addcell = function addcell(cell/*:any*/, line/*:any*/, options/*:any*/) {
|
||||
if(file_depth > 1) return;
|
||||
if(!biff4w && file_depth > 1) return;
|
||||
if(options.sheetRows && cell.r >= options.sheetRows) return;
|
||||
if(options.cellStyles && line.XF && line.XF.data) process_cell_style(cell, line, options);
|
||||
delete line.ixfe; delete line.XF;
|
||||
@ -157,8 +158,8 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
}
|
||||
{
|
||||
if(options.dense) {
|
||||
if(!out[cell.r]) out[cell.r] = [];
|
||||
out[cell.r][cell.c] = line;
|
||||
if(!out["!data"][cell.r]) out["!data"][cell.r] = [];
|
||||
out["!data"][cell.r][cell.c] = line;
|
||||
} else out[last_cell] = line;
|
||||
}
|
||||
};
|
||||
@ -204,6 +205,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
if(RecordType === 0 && last_RT === 0x000a /* EOF */) break;
|
||||
var length = (blob.l === blob.length ? 0 : blob.read_shift(2));
|
||||
var R = XLSRecordEnum[RecordType];
|
||||
if(file_depth == 0 && [0x0009, 0x0209, 0x0409, 0x0809].indexOf(RecordType) == -1 /* BOF */) break;
|
||||
//console.log(RecordType.toString(16), RecordType, R, blob.l, length, blob.length);
|
||||
//if(!R) console.log(blob.slice(blob.l, blob.l + length));
|
||||
if(R && R.f) {
|
||||
@ -264,6 +266,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
if(!val.fBelow) (out["!outline"] || (out["!outline"] = {})).above = true;
|
||||
if(!val.fRight) (out["!outline"] || (out["!outline"] = {})).left = true;
|
||||
break; // TODO
|
||||
case 0x0043: /* BIFF2XF */ case 0x0243: /* BIFF3XF */ case 0x0443: /* BIFF4XF */
|
||||
case 0x00e0 /* XF */:
|
||||
XFs.push(val); break;
|
||||
case 0x01ae /* SupBook */:
|
||||
@ -298,11 +301,11 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
case 0x0012 /* Protect */: out["!protect"] = val; break; /* for sheet or book */
|
||||
case 0x0013 /* Password */: if(val !== 0 && opts.WTF) console.error("Password verifier: " + val); break;
|
||||
case 0x0085 /* BoundSheet8 */: {
|
||||
Directory[val.pos] = val;
|
||||
Directory[opts.biff == 4 ? opts.snames.length : val.pos] = val;
|
||||
opts.snames.push(val.name);
|
||||
} break;
|
||||
case 0x000a /* EOF */: {
|
||||
if(--file_depth) break;
|
||||
if(--file_depth ? !biff4w : biff4w) break;
|
||||
if(range.e) {
|
||||
if(range.e.r > 0 && range.e.c > 0) {
|
||||
range.e.r--; range.e.c--;
|
||||
@ -323,7 +326,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
Workbook.Sheets.push(wsprops);
|
||||
}
|
||||
if(cur_sheet === "") Preamble = out; else Sheets[cur_sheet] = out;
|
||||
out = ((options.dense ? [] : {})/*:any*/);
|
||||
out = ({}/*:any*/); if(options.dense) out["!data"] = [];
|
||||
} break;
|
||||
case 0x0009: case 0x0209: case 0x0409: case 0x0809 /* BOF */: {
|
||||
if(opts.biff === 8) opts.biff = {
|
||||
@ -341,13 +344,15 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
}[val.BIFFVer] || 8;
|
||||
opts.biffguess = val.BIFFVer == 0;
|
||||
if(val.BIFFVer == 0 && val.dt == 0x1000) { opts.biff = 5; seen_codepage = true; set_cp(opts.codepage = 28591); }
|
||||
if(opts.biff == 4 && val.dt & 0x100) biff4w = true;
|
||||
if(opts.biff == 8 && val.BIFFVer == 0 && val.dt == 16) opts.biff = 2;
|
||||
if(file_depth++) break;
|
||||
out = ((options.dense ? [] : {})/*:any*/);
|
||||
if(file_depth++ && !biff4w) break;
|
||||
out = ({}/*:any*/); if(options.dense) out["!data"] = [];
|
||||
|
||||
if(opts.biff < 8 && !seen_codepage) { seen_codepage = true; set_cp(opts.codepage = options.codepage || 1252); }
|
||||
|
||||
if(opts.biff < 5 || val.BIFFVer == 0 && val.dt == 0x1000) {
|
||||
if(opts.biff == 4 && biff4w) {
|
||||
cur_sheet = (Directory[opts.snames.indexOf(cur_sheet)+1] || {name:""}).name;
|
||||
} else if(opts.biff < 5 || val.BIFFVer == 0 && val.dt == 0x1000) {
|
||||
if(cur_sheet === "") cur_sheet = "Sheet1";
|
||||
range = {s:{r:0,c:0},e:{r:0,c:0}};
|
||||
/* fake BoundSheet8 */
|
||||
@ -366,21 +371,21 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
wsprops = {Hidden:(Directory[s]||{hs:0}).hs, name:cur_sheet };
|
||||
} break;
|
||||
case 0x0203 /* Number */: case 0x0003 /* BIFF2NUM */: case 0x0002 /* BIFF2INT */: {
|
||||
if(out["!type"] == "chart") if(options.dense ? (out[val.r]||[])[val.c]: out[encode_cell({c:val.c, r:val.r})]) ++val.c;
|
||||
if(out["!type"] == "chart") if(options.dense ? (out["!data"][val.r]||[])[val.c]: out[encode_col(val.c) + encode_row(val.r)]) ++val.c;
|
||||
temp_val = ({ixfe: val.ixfe, XF: XFs[val.ixfe]||{}, v:val.val, t:'n'}/*:any*/);
|
||||
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
if(BIFF2Fmt > 0) temp_val.z = (temp_val.XF && temp_val.XF.numFmtId) && BIFF2FmtTable[temp_val.XF.numFmtId] || BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
safe_format_xf(temp_val, options, wb.opts.Date1904);
|
||||
addcell({c:val.c, r:val.r}, temp_val, options);
|
||||
} break;
|
||||
case 0x0005: case 0x0205 /* BoolErr */: {
|
||||
temp_val = ({ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.val, t:val.t}/*:any*/);
|
||||
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
if(BIFF2Fmt > 0) temp_val.z = (temp_val.XF && temp_val.XF.numFmtId) && BIFF2FmtTable[temp_val.XF.numFmtId] || BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
safe_format_xf(temp_val, options, wb.opts.Date1904);
|
||||
addcell({c:val.c, r:val.r}, temp_val, options);
|
||||
} break;
|
||||
case 0x027e /* RK */: {
|
||||
temp_val = ({ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.rknum, t:'n'}/*:any*/);
|
||||
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
if(BIFF2Fmt > 0) temp_val.z = (temp_val.XF && temp_val.XF.numFmtId) && BIFF2FmtTable[temp_val.XF.numFmtId] || BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
safe_format_xf(temp_val, options, wb.opts.Date1904);
|
||||
addcell({c:val.c, r:val.r}, temp_val, options);
|
||||
} break;
|
||||
@ -388,7 +393,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
for(var j = val.c; j <= val.C; ++j) {
|
||||
var ixfe = val.rkrec[j-val.c][0];
|
||||
temp_val= ({ixfe:ixfe, XF:XFs[ixfe], v:val.rkrec[j-val.c][1], t:'n'}/*:any*/);
|
||||
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
if(BIFF2Fmt > 0) temp_val.z = (temp_val.XF && temp_val.XF.numFmtId) && BIFF2FmtTable[temp_val.XF.numFmtId] || BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
safe_format_xf(temp_val, options, wb.opts.Date1904);
|
||||
addcell({c:j, r:val.r}, temp_val, options);
|
||||
}
|
||||
@ -403,10 +408,10 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
var _fr = _f[0][0][1][0], _fc = _f[0][0][1][1];
|
||||
var _fe = encode_cell({r:_fr, c:_fc});
|
||||
if(sharedf[_fe]) temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
|
||||
else temp_val.F = ((options.dense ? (out[_fr]||[])[_fc]: out[_fe]) || {}).F;
|
||||
else temp_val.F = ((options.dense ? (out["!data"][_fr]||[])[_fc]: out[_fe]) || {}).F;
|
||||
} else temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
|
||||
}
|
||||
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
if(BIFF2Fmt > 0) temp_val.z = (temp_val.XF && temp_val.XF.numFmtId) && BIFF2FmtTable[temp_val.XF.numFmtId] || BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
safe_format_xf(temp_val, options, wb.opts.Date1904);
|
||||
addcell(val.cell, temp_val, options);
|
||||
last_formula = val;
|
||||
@ -419,7 +424,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
if(options.cellFormula) {
|
||||
temp_val.f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
|
||||
}
|
||||
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
if(BIFF2Fmt > 0) temp_val.z = (temp_val.XF && temp_val.XF.numFmtId) && BIFF2FmtTable[temp_val.XF.numFmtId] || BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
safe_format_xf(temp_val, options, wb.opts.Date1904);
|
||||
addcell(last_formula.cell, temp_val, options);
|
||||
last_formula = null;
|
||||
@ -428,7 +433,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
case 0x0021: case 0x0221 /* Array */: {
|
||||
arrayf.push(val);
|
||||
var _arraystart = encode_cell(val[0].s);
|
||||
cc = options.dense ? (out[val[0].s.r]||[])[val[0].s.c] : out[_arraystart];
|
||||
cc = options.dense ? (out["!data"][val[0].s.r]||[])[val[0].s.c] : out[_arraystart];
|
||||
if(options.cellFormula && cc) {
|
||||
if(!last_formula) break; /* technically unreachable */
|
||||
if(!_arraystart || !cc) break;
|
||||
@ -442,7 +447,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
/* TODO: capture range */
|
||||
if(!last_formula) break; /* technically unreachable */
|
||||
sharedf[encode_cell(last_formula.cell)]= val[0];
|
||||
cc = options.dense ? (out[last_formula.cell.r]||[])[last_formula.cell.c] : out[encode_cell(last_formula.cell)];
|
||||
cc = options.dense ? (out["!data"][last_formula.cell.r]||[])[last_formula.cell.c] : out[encode_cell(last_formula.cell)];
|
||||
(cc||{}).f = ""+stringify_formula(val[0], range, lastcell, supbooks, opts);
|
||||
}
|
||||
} break;
|
||||
@ -450,13 +455,13 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
temp_val=make_cell(sst[val.isst].t, val.ixfe, 's');
|
||||
if(sst[val.isst].h) temp_val.h = sst[val.isst].h;
|
||||
temp_val.XF = XFs[temp_val.ixfe];
|
||||
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
if(BIFF2Fmt > 0) temp_val.z = (temp_val.XF && temp_val.XF.numFmtId) && BIFF2FmtTable[temp_val.XF.numFmtId] || BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
safe_format_xf(temp_val, options, wb.opts.Date1904);
|
||||
addcell({c:val.c, r:val.r}, temp_val, options);
|
||||
break;
|
||||
case 0x0201 /* Blank */: if(options.sheetStubs) {
|
||||
temp_val = ({ixfe: val.ixfe, XF: XFs[val.ixfe], t:'z'}/*:any*/);
|
||||
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
if(BIFF2Fmt > 0) temp_val.z = (temp_val.XF && temp_val.XF.numFmtId) && BIFF2FmtTable[temp_val.XF.numFmtId] || BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
safe_format_xf(temp_val, options, wb.opts.Date1904);
|
||||
addcell({c:val.c, r:val.r}, temp_val, options);
|
||||
} break;
|
||||
@ -464,7 +469,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
for(var _j = val.c; _j <= val.C; ++_j) {
|
||||
var _ixfe = val.ixfe[_j-val.c];
|
||||
temp_val= ({ixfe:_ixfe, XF:XFs[_ixfe], t:'z'}/*:any*/);
|
||||
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
if(BIFF2Fmt > 0) temp_val.z = (temp_val.XF && temp_val.XF.numFmtId) && BIFF2FmtTable[temp_val.XF.numFmtId] || BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
safe_format_xf(temp_val, options, wb.opts.Date1904);
|
||||
addcell({c:_j, r:val.r}, temp_val, options);
|
||||
}
|
||||
@ -473,7 +478,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
case 0x0204 /* Label */: case 0x0004 /* BIFF2STR */:
|
||||
temp_val=make_cell(val.val, val.ixfe, 's');
|
||||
temp_val.XF = XFs[temp_val.ixfe];
|
||||
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
if(BIFF2Fmt > 0) temp_val.z = (temp_val.XF && temp_val.XF.numFmtId) && BIFF2FmtTable[temp_val.XF.numFmtId] || BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
|
||||
safe_format_xf(temp_val, options, wb.opts.Date1904);
|
||||
addcell({c:val.c, r:val.r}, temp_val, options);
|
||||
break;
|
||||
@ -485,7 +490,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
sst = val;
|
||||
} break;
|
||||
case 0x041e /* Format */: { /* val = [id, fmt] */
|
||||
if(opts.biff == 4) {
|
||||
if(opts.biff >= 3 && opts.biff <= 4) {
|
||||
BIFF2FmtTable[BIFF2Fmt++] = val[1];
|
||||
for(var b4idx = 0; b4idx < BIFF2Fmt + 163; ++b4idx) if(table_fmt[b4idx] == val[1]) break;
|
||||
if(b4idx >= 163) SSF__load(val[1], BIFF2Fmt + 163);
|
||||
@ -507,25 +512,24 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
case 0x01b8 /* HLink */: {
|
||||
for(rngR = val[0].s.r; rngR <= val[0].e.r; ++rngR)
|
||||
for(rngC = val[0].s.c; rngC <= val[0].e.c; ++rngC) {
|
||||
cc = options.dense ? (out[rngR]||[])[rngC] : out[encode_cell({c:rngC,r:rngR})];
|
||||
cc = options.dense ? (out["!data"][rngR]||[])[rngC] : out[encode_cell({c:rngC,r:rngR})];
|
||||
if(cc) cc.l = val[1];
|
||||
}
|
||||
} break;
|
||||
case 0x0800 /* HLinkTooltip */: {
|
||||
for(rngR = val[0].s.r; rngR <= val[0].e.r; ++rngR)
|
||||
for(rngC = val[0].s.c; rngC <= val[0].e.c; ++rngC) {
|
||||
cc = options.dense ? (out[rngR]||[])[rngC] : out[encode_cell({c:rngC,r:rngR})];
|
||||
cc = options.dense ? (out["!data"][rngR]||[])[rngC] : out[encode_cell({c:rngC,r:rngR})];
|
||||
if(cc && cc.l) cc.l.Tooltip = val[1];
|
||||
}
|
||||
} break;
|
||||
case 0x001c /* Note */: {
|
||||
if(opts.biff <= 5 && opts.biff >= 2) break; /* TODO: BIFF5 */
|
||||
cc = options.dense ? (out[val[0].r]||[])[val[0].c] : out[encode_cell(val[0])];
|
||||
var noteobj = objects[val[2]];
|
||||
/* TODO: comment continuation (row == -1 / 0xFFFF) */
|
||||
cc = options.dense ? (out["!data"][val[0].r]||[])[val[0].c] : out[encode_cell(val[0])];
|
||||
if(!cc) {
|
||||
if(options.dense) {
|
||||
if(!out[val[0].r]) out[val[0].r] = [];
|
||||
cc = out[val[0].r][val[0].c] = ({t:"z"}/*:any*/);
|
||||
if(!out["!data"][val[0].r]) out["!data"][val[0].r] = [];
|
||||
cc = out["!data"][val[0].r][val[0].c] = ({t:"z"}/*:any*/);
|
||||
} else {
|
||||
cc = out[encode_cell(val[0])] = ({t:"z"}/*:any*/);
|
||||
}
|
||||
@ -535,7 +539,12 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
range.s.c = Math.min(range.s.c, val[0].c);
|
||||
}
|
||||
if(!cc.c) cc.c = [];
|
||||
cmnt = {a:val[1],t:noteobj.TxO.t};
|
||||
if(opts.biff <= 5 && opts.biff >= 2) cmnt = {a:"SheetJ5", t:val[1]};
|
||||
else {
|
||||
var noteobj = objects[val[2]];
|
||||
cmnt = {a:val[1],t:noteobj.TxO.t};
|
||||
if(val[3] != null && !(val[3] & 0x02)) cc.c.hidden = true;
|
||||
}
|
||||
cc.c.push(cmnt);
|
||||
} break;
|
||||
case 0x087d /* XFExt */: update_xfext(XFs[val.ixfe], val.ext); break;
|
||||
|
@ -482,7 +482,7 @@ var XLSBRecordEnum = {
|
||||
/*::[*/0x0224/*::]*/: { /* n:"BrtFileSharing" */ },
|
||||
/*::[*/0x0225/*::]*/: { /* n:"BrtOleSize" */ },
|
||||
/*::[*/0x0226/*::]*/: { /* n:"BrtDrawing", */ f:parse_RelID },
|
||||
/*::[*/0x0227/*::]*/: { /* n:"BrtLegacyDrawing" */ },
|
||||
/*::[*/0x0227/*::]*/: { /* n:"BrtLegacyDrawing", */ f:parse_XLNullableWideString },
|
||||
/*::[*/0x0228/*::]*/: { /* n:"BrtLegacyDrawingHF" */ },
|
||||
/*::[*/0x0229/*::]*/: { /* n:"BrtWebOpt" */ },
|
||||
/*::[*/0x022A/*::]*/: { /* n:"BrtBeginWebPubItems", */ T:1 },
|
||||
@ -1221,7 +1221,7 @@ var XLSRecordEnum = {
|
||||
/*::[*/0x0002/*::]*/: { /* n:"BIFF2INT", */ f:parse_BIFF2INT },
|
||||
/*::[*/0x0003/*::]*/: { /* n:"BIFF2NUM", */ f:parse_BIFF2NUM },
|
||||
/*::[*/0x0004/*::]*/: { /* n:"BIFF2STR", */ f:parse_BIFF2STR },
|
||||
/*::[*/0x0005/*::]*/: { /* n:"BoolErr", */ f:parse_BoolErr },
|
||||
/*::[*/0x0005/*::]*/: { /* n:"BIFF2BOOLERR", */ f:parse_BIFF2BOOLERR },
|
||||
/*::[*/0x0007/*::]*/: { /* n:"String", */ f:parse_BIFF2STRING },
|
||||
/*::[*/0x0008/*::]*/: { /* n:"BIFF2ROW", */ },
|
||||
/*::[*/0x0009/*::]*/: { /* n:"BOF", */ f:parse_BOF },
|
||||
@ -1233,34 +1233,34 @@ var XLSRecordEnum = {
|
||||
/*::[*/0x0021/*::]*/: { /* n:"Array", */ f:parse_Array },
|
||||
/*::[*/0x0024/*::]*/: { /* n:"COLWIDTH", */ },
|
||||
/*::[*/0x0025/*::]*/: { /* n:"DefaultRowHeight", */ f:parse_DefaultRowHeight },
|
||||
// 0x2c ??
|
||||
// 0x2d ??
|
||||
// 0x2e ??
|
||||
// 0x30 FONTCOUNT: number of fonts
|
||||
// 0x002c ??
|
||||
// 0x002d ??
|
||||
// 0x002e ??
|
||||
// 0x0030 FONTCOUNT: number of fonts
|
||||
/*::[*/0x0032/*::]*/: { /* n:"BIFF2FONTXTRA", */ f:parse_BIFF2FONTXTRA },
|
||||
// 0x35: INFOOPTS
|
||||
// 0x36: TABLE (BIFF2 only)
|
||||
// 0x37: TABLE2 (BIFF2 only)
|
||||
// 0x38: WNDESK
|
||||
// 0x39 ??
|
||||
// 0x3a: BEGINPREF
|
||||
// 0x3b: ENDPREF
|
||||
// 0x0035: INFOOPTS
|
||||
// 0x0036: TABLE (BIFF2 only)
|
||||
// 0x0037: TABLE2 (BIFF2 only)
|
||||
// 0x0038: WNDESK
|
||||
// 0x0039 ??
|
||||
// 0x003a: BEGINPREF
|
||||
// 0x003b: ENDPREF
|
||||
/*::[*/0x003e/*::]*/: { /* n:"BIFF2WINDOW2", */ },
|
||||
// 0x3f ??
|
||||
// 0x46: SHOWSCROLL
|
||||
// 0x47: SHOWFORMULA
|
||||
// 0x48: STATUSBAR
|
||||
// 0x49: SHORTMENUS
|
||||
// 0x4A:
|
||||
// 0x4B:
|
||||
// 0x4C:
|
||||
// 0x4E:
|
||||
// 0x4F:
|
||||
// 0x58: TOOLBAR (BIFF3)
|
||||
// 0x003f ??
|
||||
// 0x0046: SHOWSCROLL
|
||||
// 0x0047: SHOWFORMULA
|
||||
// 0x0048: STATUSBAR
|
||||
// 0x0049: SHORTMENUS
|
||||
// 0x004A:
|
||||
// 0x004B:
|
||||
// 0x004C:
|
||||
// 0x004E:
|
||||
// 0x004F:
|
||||
// 0x0058: TOOLBAR (BIFF3)
|
||||
|
||||
/* - - - */
|
||||
/*::[*/0x0034/*::]*/: { /* n:"DDEObjName", */ },
|
||||
/*::[*/0x0043/*::]*/: { /* n:"BIFF2XF", */ },
|
||||
/*::[*/0x0043/*::]*/: { /* n:"BIFF2XF", */ f:parse_BIFF2XF },
|
||||
/*::[*/0x0044/*::]*/: { /* n:"BIFF2XFINDEX", */ f:parseuint16 },
|
||||
/*::[*/0x0045/*::]*/: { /* n:"BIFF2FONTCLR", */ },
|
||||
/*::[*/0x0056/*::]*/: { /* n:"BIFF4FMTCNT", */ }, /* 16-bit cnt, similar to BIFF2 */
|
||||
@ -1272,7 +1272,7 @@ var XLSRecordEnum = {
|
||||
// 0x8A
|
||||
// 0x8B LH: alternate menu key flag (BIFF3/4)
|
||||
// 0x8E
|
||||
// 0x8F
|
||||
/*::[*/0x008F/*::]*/: { /* n:"BIFF4SheetInfo", */ f:parse_BIFF4SheetInfo },
|
||||
/*::[*/0x0091/*::]*/: { /* n:"Sub", */ },
|
||||
// 0x93 STYLE
|
||||
/*::[*/0x0094/*::]*/: { /* n:"LHRecord", */ },
|
||||
@ -1298,10 +1298,10 @@ var XLSRecordEnum = {
|
||||
/*::[*/0x0218/*::]*/: { /* n:"Lbl", */ f:parse_Lbl },
|
||||
/*::[*/0x0223/*::]*/: { /* n:"ExternName", */ f:parse_ExternName },
|
||||
/*::[*/0x0231/*::]*/: { /* n:"Font", */ },
|
||||
/*::[*/0x0243/*::]*/: { /* n:"BIFF3XF", */ },
|
||||
/*::[*/0x0243/*::]*/: { /* n:"BIFF3XF", */ f:parse_BIFF3XF },
|
||||
/*::[*/0x0406/*::]*/: { /* n:"Formula", */ f:parse_Formula },
|
||||
/*::[*/0x0409/*::]*/: { /* n:"BOF", */ f:parse_BOF },
|
||||
/*::[*/0x0443/*::]*/: { /* n:"BIFF4XF", */ },
|
||||
/*::[*/0x0443/*::]*/: { /* n:"BIFF4XF", */ f:parse_BIFF4XF },
|
||||
/*::[*/0x086d/*::]*/: { /* n:"FeatInfo", */ },
|
||||
/*::[*/0x0873/*::]*/: { /* n:"FeatInfo11", */ },
|
||||
/*::[*/0x0881/*::]*/: { /* n:"SXAddl12", */ },
|
||||
|
@ -31,16 +31,7 @@ function write_biff_continue(ba/*:BufArray*/, type/*:number*/, payload, length/*
|
||||
}
|
||||
}
|
||||
|
||||
function write_BIFF2Cell(out, r/*:number*/, c/*:number*/) {
|
||||
if(!out) out = new_buf(7);
|
||||
out.write_shift(2, r);
|
||||
out.write_shift(2, c);
|
||||
out.write_shift(2, 0);
|
||||
out.write_shift(1, 0);
|
||||
return out;
|
||||
}
|
||||
|
||||
function write_BIFF2BERR(r/*:number*/, c/*:number*/, val, t/*:?string*/) {
|
||||
function write_BIFF2BERR(r/*:number*/, c/*:number*/, val, t/*:string*/) {
|
||||
var out = new_buf(9);
|
||||
write_BIFF2Cell(out, r, c);
|
||||
write_Bes(val, t || 'b', out);
|
||||
@ -56,62 +47,371 @@ function write_BIFF2LABEL(r/*:number*/, c/*:number*/, val) {
|
||||
return out.l < out.length ? out.slice(0, out.l) : out;
|
||||
}
|
||||
|
||||
function write_ws_biff2_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*//*::, opts*/) {
|
||||
function write_comments_biff2(ba/*:BufArray*/, comments/*:Array<[Comment[], number, number]>*/) {
|
||||
comments.forEach(function(data) {
|
||||
var text = data[0].map(function(cc) { return cc.t; }).join("");
|
||||
// TODO: should '\n' be translated to '\r' to correct for Excel 5.0 bug when exporting to BIFF2/3 ?
|
||||
if(text.length <= 2048) return write_biff_rec(ba, 0x001C, write_NOTE_BIFF2(text, data[1], data[2]));
|
||||
write_biff_rec(ba, 0x001C, write_NOTE_BIFF2(text.slice(0, 2048), data[1], data[2], text.length));
|
||||
for(var i = 2048; i < text.length; i += 2048)
|
||||
write_biff_rec(ba, 0x001C, write_NOTE_BIFF2(text.slice(i, Math.min(i+2048, text.length)), -1, -1, Math.min(2048, text.length - i)));
|
||||
});
|
||||
}
|
||||
|
||||
/* TODO: BIFF3/4 use different records -- see comments*/
|
||||
function write_ws_biff2_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts, date1904/*:boolean*/) {
|
||||
var ifmt = 0;
|
||||
if(cell.z != null) {
|
||||
ifmt = opts._BIFF2FmtTable.indexOf(cell.z);
|
||||
if(ifmt == -1) { opts._BIFF2FmtTable.push(cell.z); ifmt = opts._BIFF2FmtTable.length - 1; }
|
||||
}
|
||||
var ixfe = 0;
|
||||
if(cell.z != null) {
|
||||
for(; ixfe < opts.cellXfs.length; ++ixfe) if(opts.cellXfs[ixfe].numFmtId == ifmt) break;
|
||||
if(ixfe == opts.cellXfs.length) opts.cellXfs.push({numFmtId: ifmt});
|
||||
}
|
||||
if(cell.v != null) switch(cell.t) {
|
||||
case 'd': case 'n':
|
||||
var v = cell.t == 'd' ? datenum(parseDate(cell.v)) : cell.v;
|
||||
if((v == (v|0)) && (v >= 0) && (v < 65536))
|
||||
write_biff_rec(ba, 0x0002, write_BIFF2INT(R, C, v));
|
||||
var v = cell.t == 'd' ? datenum(parseDate(cell.v, date1904), date1904) : cell.v;
|
||||
if(opts.biff == 2 && (v == (v|0)) && (v >= 0) && (v < 65536))
|
||||
// 0x027E (RK) in BIFF3/4
|
||||
write_biff_rec(ba, 0x0002, write_BIFF2INT(R, C, v, ixfe, ifmt));
|
||||
else if(isNaN(v))
|
||||
// 0x0205 in BIFF3/4
|
||||
write_biff_rec(ba, 0x0005, write_BIFF2BERR(R, C, 0x24, "e")); // #NUM!
|
||||
else if(!isFinite(v))
|
||||
// 0x0205 in BIFF3/4
|
||||
write_biff_rec(ba, 0x0005, write_BIFF2BERR(R, C, 0x07, "e")); // #DIV/0!
|
||||
else
|
||||
write_biff_rec(ba, 0x0003, write_BIFF2NUM(R,C, v));
|
||||
// 0x0203 in BIFF3/4
|
||||
write_biff_rec(ba, 0x0003, write_BIFF2NUM(R,C, v, ixfe, ifmt));
|
||||
return;
|
||||
case 'b': case 'e': write_biff_rec(ba, 0x0005, write_BIFF2BERR(R, C, cell.v, cell.t)); return;
|
||||
case 'b': case 'e':
|
||||
// 0x0205 in BIFF3/4
|
||||
write_biff_rec(ba, 0x0005, write_BIFF2BERR(R, C, cell.v, cell.t)); return;
|
||||
/* TODO: codepage, sst */
|
||||
case 's': case 'str':
|
||||
write_biff_rec(ba, 0x0004, write_BIFF2LABEL(R, C, (cell.v||"").slice(0,255)));
|
||||
// 0x0204 in BIFF3/4
|
||||
write_biff_rec(ba, 0x0004, write_BIFF2LABEL(R, C, cell.v == null ? "" : String(cell.v).slice(0,255)));
|
||||
return;
|
||||
}
|
||||
// 0x0201 in BIFF3/4
|
||||
write_biff_rec(ba, 0x0001, write_BIFF2Cell(null, R, C));
|
||||
}
|
||||
|
||||
function write_ws_biff2(ba/*:BufArray*/, ws/*:Worksheet*/, idx/*:number*/, opts/*::, wb:Workbook*/) {
|
||||
var dense = Array.isArray(ws);
|
||||
function write_ws_biff2(ba/*:BufArray*/, ws/*:Worksheet*/, idx/*:number*/, opts, wb/*:Workbook*/) {
|
||||
var dense = ws["!data"] != null;
|
||||
var range = safe_decode_range(ws['!ref'] || "A1"), ref/*:string*/, rr = "", cols/*:Array<string>*/ = [];
|
||||
if(range.e.c > 0xFF || range.e.r > 0x3FFF) {
|
||||
if(opts.WTF) throw new Error("Range " + (ws['!ref'] || "A1") + " exceeds format limit A1:IV16384");
|
||||
range.e.c = Math.min(range.e.c, 0xFF);
|
||||
range.e.r = Math.min(range.e.c, 0x3FFF);
|
||||
ref = encode_range(range);
|
||||
}
|
||||
var date1904 = (((wb||{}).Workbook||{}).WBProps||{}).date1904;
|
||||
var row = [], comments = [];
|
||||
/* TODO: 0x0000 / 0x0200 dimensions? */
|
||||
for(var C = range.s.c; C <= range.e.c; ++C) cols[C] = encode_col(C);
|
||||
for(var R = range.s.r; R <= range.e.r; ++R) {
|
||||
if(dense) row = ws["!data"][R] || [];
|
||||
rr = encode_row(R);
|
||||
for(var C = range.s.c; C <= range.e.c; ++C) {
|
||||
if(R === range.s.r) cols[C] = encode_col(C);
|
||||
ref = cols[C] + rr;
|
||||
var cell = dense ? (ws[R]||[])[C] : ws[ref];
|
||||
for(C = range.s.c; C <= range.e.c; ++C) {
|
||||
var cell = dense ? row[C] : ws[cols[C] + rr];
|
||||
if(!cell) continue;
|
||||
/* write cell */
|
||||
write_ws_biff2_cell(ba, cell, R, C, opts);
|
||||
write_ws_biff2_cell(ba, cell, R, C, opts, date1904);
|
||||
if(cell.c) comments.push([cell.c, R, C]);
|
||||
}
|
||||
}
|
||||
|
||||
/* ... 0x12 0x19 0x13 (Password) */
|
||||
write_comments_biff2(ba, comments);
|
||||
/* 0x3d (Window1) ... */
|
||||
}
|
||||
|
||||
/* Based on test files */
|
||||
function write_biff2_buf(wb/*:Workbook*/, opts/*:WriteOpts*/) {
|
||||
var o = opts || {};
|
||||
if(DENSE != null && o.dense == null) o.dense = DENSE;
|
||||
|
||||
var ba = buf_array();
|
||||
var idx = 0;
|
||||
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
|
||||
if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
|
||||
write_biff_rec(ba, (o.biff == 4 ? 0x0409 : (o.biff == 3 ? 0x0209 : 0x0009)), write_BOF(wb, 0x10, o));
|
||||
/* ... */
|
||||
write_ws_biff2(ba, wb.Sheets[wb.SheetNames[idx]], idx, o, wb);
|
||||
/* ... */
|
||||
if(((wb.Workbook||{}).WBProps||{}).date1904) write_biff_rec(ba, 0x0022, writebool(true));
|
||||
o.cellXfs = [{numFmtId: 0}];
|
||||
o._BIFF2FmtTable/*:Array<string>*/ = ["General"]; o._Fonts = [];
|
||||
var body = buf_array();
|
||||
write_ws_biff2(body, wb.Sheets[wb.SheetNames[idx]], idx, o, wb);
|
||||
|
||||
o._BIFF2FmtTable.forEach(function(f) {
|
||||
if(o.biff <= 3) write_biff_rec(ba, 0x001E, write_BIFF2Format(f));
|
||||
else write_biff_rec(ba, 0x041E, write_BIFF4Format(f));
|
||||
});
|
||||
o.cellXfs.forEach(function(xf) {
|
||||
switch(o.biff) {
|
||||
case 2: write_biff_rec(ba, 0x0043, write_BIFF2XF(xf)); break;
|
||||
case 3: write_biff_rec(ba, 0x0243, write_BIFF3XF(xf)); break;
|
||||
case 4: write_biff_rec(ba, 0x0443, write_BIFF4XF(xf)); break;
|
||||
}
|
||||
});
|
||||
delete o._BIFF2FmtTable; delete o.cellXfs; delete o._Fonts;
|
||||
|
||||
ba.push(body.end());
|
||||
write_biff_rec(ba, 0x000A);
|
||||
return ba.end();
|
||||
}
|
||||
|
||||
var b8oid = 1, b8ocnts/*:Array<[number, number, number]>*/ = [];
|
||||
function write_MsoDrawingGroup() {
|
||||
var buf = new_buf(82 + 8 * b8ocnts.length);
|
||||
/* [MS-ODRAW] 2.2.12 OfficeArtDggContainer */
|
||||
buf.write_shift(2, 0x0F);
|
||||
buf.write_shift(2, 0xF000);
|
||||
buf.write_shift(4, 74 + 8 * b8ocnts.length);
|
||||
/* 2.2.48 OfficeArtFDGGBlock */
|
||||
{
|
||||
buf.write_shift(2, 0);
|
||||
buf.write_shift(2, 0xF006);
|
||||
buf.write_shift(4, 16 + 8 * b8ocnts.length);
|
||||
/* 2.2.47 OfficeArtFDGG */
|
||||
{
|
||||
buf.write_shift(4, b8oid);
|
||||
buf.write_shift(4, b8ocnts.length+1);
|
||||
var acc = 0; for(var i = 0; i < b8ocnts.length; ++i) acc += (b8ocnts[i] && b8ocnts[i][1] || 0); buf.write_shift(4, acc);
|
||||
buf.write_shift(4, b8ocnts.length);
|
||||
}
|
||||
/* 2.2.46 OfficeArtIDCL + */
|
||||
b8ocnts.forEach(function(b8) {
|
||||
buf.write_shift(4, b8[0]);
|
||||
buf.write_shift(4, b8[2]);
|
||||
});
|
||||
}
|
||||
/* 2.2.9 OfficeArtFOPT */
|
||||
{
|
||||
buf.write_shift(2, 0x33); // 0x03 | (3 << 4)
|
||||
buf.write_shift(2, 0xF00B);
|
||||
buf.write_shift(4, 0x12); // 3 * 6
|
||||
/* 2.3.21.15 Text Boolean Properties */
|
||||
buf.write_shift(2, 0xBF); buf.write_shift(4, 0x00080008);
|
||||
/* 2.3.7.2 fillColor */
|
||||
buf.write_shift(2, 0x0181); buf.write_shift(4, 0x08000041);
|
||||
/* 2.3.8.1 lineColor */
|
||||
buf.write_shift(2, 0x01C0); buf.write_shift(4, 0x08000040);
|
||||
}
|
||||
/* 2.2.45 OfficeArtSplitMenuColorContainer */
|
||||
{
|
||||
buf.write_shift(2, 0x40);
|
||||
buf.write_shift(2, 0xF11E);
|
||||
buf.write_shift(4, 16);
|
||||
buf.write_shift(4, 0x0800000D);
|
||||
buf.write_shift(4, 0x0800000C);
|
||||
buf.write_shift(4, 0x08000017);
|
||||
buf.write_shift(4, 0x100000F7);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
function write_comments_biff8(ba/*:BufArray*/, comments/*:Array<[Comment[], number, number]>*/) {
|
||||
var notes/*:Array<RawData>*/ = [], sz = 0, pl = buf_array(), baseid = b8oid;
|
||||
var _oasc;
|
||||
comments.forEach(function(c, ci) {
|
||||
var author = "";
|
||||
var text = c[0].map(function(t) { if(t.a && !author) author = t.a; return t.t; }).join("");
|
||||
++b8oid;
|
||||
|
||||
/* 2.2.14 OfficeArtSpContainer */
|
||||
{
|
||||
var oasc = new_buf(0x96);
|
||||
oasc.write_shift(2, 0x0F);
|
||||
oasc.write_shift(2, 0xF004);
|
||||
oasc.write_shift(4, 0x96);
|
||||
/* 2.2.40 OfficeArtFSP */
|
||||
{
|
||||
oasc.write_shift(2, 0xca2); // 0x02 | (0xca << 4)
|
||||
oasc.write_shift(2, 0xF00A);
|
||||
oasc.write_shift(4, 8);
|
||||
oasc.write_shift(4, b8oid);
|
||||
oasc.write_shift(4, 0xA00);
|
||||
}
|
||||
/* 2.2.9 OfficeArtFOPT */
|
||||
{
|
||||
oasc.write_shift(2, 0xE3); // 0x03 | (14 << 4)
|
||||
oasc.write_shift(2, 0xF00B);
|
||||
oasc.write_shift(4, 0x54); // 14 * 6
|
||||
/* 2.3.21.1 ITxid */
|
||||
oasc.write_shift(2, 0x80); oasc.write_shift(4, 0);
|
||||
/* 2.3.21.12 txdir */
|
||||
oasc.write_shift(2, 0x8B); oasc.write_shift(4, 0x02);
|
||||
/* 2.3.21.15 Text Boolean Properties */
|
||||
oasc.write_shift(2, 0xBF); oasc.write_shift(4, 0x00080008);
|
||||
/* 2.3.6.30 cxk */
|
||||
oasc.write_shift(2, 0x0158); oasc.l += 4;
|
||||
/* 2.3.7.2 fillColor */
|
||||
oasc.write_shift(2, 0x0181); oasc.write_shift(4, 0x08000050);
|
||||
/* 2.3.7.4 fillBackColor */
|
||||
oasc.write_shift(2, 0x0183); oasc.write_shift(4, 0x08000050);
|
||||
/* 2.3.7.6 fillCrMod */
|
||||
oasc.write_shift(2, 0x0185); oasc.write_shift(4, 0x100000F4);
|
||||
/* 2.3.7.43 Fill Style Boolean Properties */
|
||||
oasc.write_shift(2, 0x01BF); oasc.write_shift(4, 0x00100010);
|
||||
/* 2.3.8.1 lineColor */
|
||||
oasc.write_shift(2, 0x01C0); oasc.write_shift(4, 0x08000051);
|
||||
/* 2.3.8.4 lineCrMod */
|
||||
oasc.write_shift(2, 0x01C3); oasc.write_shift(4, 0x100000F4);
|
||||
/* 2.3.13.2 shadowColor */
|
||||
oasc.write_shift(2, 0x0201); oasc.write_shift(4, 0x08000051);
|
||||
/* 2.3.13.4 shadowCrMod */
|
||||
oasc.write_shift(2, 0x0203); oasc.write_shift(4, 0x100000F4);
|
||||
/* 2.3.13.23 Shadow Style Boolean Properties */
|
||||
oasc.write_shift(2, 0x023F); oasc.write_shift(4, 0x00030001);
|
||||
/* 2.3.4.44 Group Shape Boolean Properties */
|
||||
oasc.write_shift(2, 0x03BF); oasc.write_shift(4, 0x00020000 | (c[0].hidden ? 2 : 0));
|
||||
}
|
||||
/* [MS-XLS] 2.5.193 OfficeArtClientAnchorSheet */
|
||||
{
|
||||
oasc.l += 2;
|
||||
oasc.write_shift(2, 0xF010);
|
||||
oasc.write_shift(4, 0x12);
|
||||
oasc.write_shift(2, 0x3); // do not move or size with cells
|
||||
oasc.write_shift(2, c[2] + 2); oasc.l += 2;
|
||||
oasc.write_shift(2, c[1] + 1); oasc.l += 2;
|
||||
oasc.write_shift(2, c[2] + 4); oasc.l += 2;
|
||||
oasc.write_shift(2, c[1] + 5); oasc.l += 2;
|
||||
}
|
||||
/* [MS-XLS] 2.5.194 OfficeArtClientData */
|
||||
{
|
||||
oasc.l += 2;
|
||||
oasc.write_shift(2, 0xF011);
|
||||
oasc.l += 4;
|
||||
}
|
||||
oasc.l = 0x96;
|
||||
if(ci == 0) /* write_biff_rec(pl, 0x003C, oasc); */ _oasc = oasc;
|
||||
else write_biff_rec(pl, 0x00EC, oasc);
|
||||
}
|
||||
sz += 0x96;
|
||||
|
||||
/* [MS-XLS] 2.4.181 Obj */
|
||||
{
|
||||
var obj = new_buf(52); // 22 + 26 + 4
|
||||
/* [MS-XLS] 2.5.143 FtCmo */
|
||||
obj.write_shift(2, 0x15);
|
||||
obj.write_shift(2, 0x12);
|
||||
obj.write_shift(2, 0x19);
|
||||
obj.write_shift(2, b8oid);
|
||||
obj.write_shift(2, 0);
|
||||
obj.l = 22;
|
||||
/* [MS-XLS] 2.5.149 FtNts */
|
||||
obj.write_shift(2, 0x0D);
|
||||
obj.write_shift(2, 0x16);
|
||||
obj.write_shift(4, 0x62726272);
|
||||
obj.write_shift(4, 0x95374305);
|
||||
obj.write_shift(4, 0x80301328);
|
||||
obj.write_shift(4, 0x69696904 + b8oid*256);
|
||||
obj.write_shift(2,0);
|
||||
obj.write_shift(4,0);
|
||||
// reserved
|
||||
obj.l += 4;
|
||||
write_biff_rec(pl, 0x005D, obj);
|
||||
}
|
||||
|
||||
/* [MS-XLS] 2.5.195 OfficeArtClientTextbox */
|
||||
{
|
||||
var oact = new_buf(8);
|
||||
oact.l += 2;
|
||||
oact.write_shift(2, 0xF00D);
|
||||
oact.l += 4;
|
||||
write_biff_rec(pl, 0x00EC, oact);
|
||||
}
|
||||
sz += 8;
|
||||
|
||||
/* [MS-XLS] 2.4.329 TxO */
|
||||
{
|
||||
var txo = new_buf(18);
|
||||
txo.write_shift(2, 0x12);
|
||||
txo.l += 8;
|
||||
txo.write_shift(2, text.length);
|
||||
txo.write_shift(2, 0x10);
|
||||
txo.l += 4;
|
||||
write_biff_rec(pl, 0x01b6, txo);
|
||||
/* text continue record TODO: switch to wide strings */
|
||||
{
|
||||
var cont = new_buf(1 + text.length);
|
||||
cont.write_shift(1, 0);
|
||||
cont.write_shift(text.length, text, "sbcs");
|
||||
write_biff_rec(pl, 0x003C, cont);
|
||||
}
|
||||
/* formatting continue records */
|
||||
{
|
||||
var conf = new_buf(0x10);
|
||||
conf.l += 8;
|
||||
conf.write_shift(2, text.length);
|
||||
conf.l += 6;
|
||||
write_biff_rec(pl, 0x003C, conf);
|
||||
}
|
||||
}
|
||||
|
||||
/* 2.4.179 Note */
|
||||
{
|
||||
var notesh = new_buf(12 + author.length);
|
||||
notesh.write_shift(2, c[1]);
|
||||
notesh.write_shift(2, c[2]);
|
||||
notesh.write_shift(2, 0 | (c[0].hidden ? 0 : 2));
|
||||
notesh.write_shift(2, b8oid);
|
||||
notesh.write_shift(2, author.length);
|
||||
notesh.write_shift(1, 0);
|
||||
notesh.write_shift(author.length, author, "sbcs");
|
||||
notesh.l ++;
|
||||
notes.push(notesh);
|
||||
}
|
||||
});
|
||||
/* [MS-ODRAW] 2.2.13 OfficeArtDgContainer */
|
||||
{
|
||||
var hdr = new_buf(80);
|
||||
hdr.write_shift(2, 0x0F);
|
||||
hdr.write_shift(2, 0xF002);
|
||||
hdr.write_shift(4, sz + hdr.length - 8);
|
||||
/* [MS-ODRAW] 2.2.49 OfficeArtFDG */
|
||||
{
|
||||
hdr.write_shift(2, 0x10);
|
||||
hdr.write_shift(2, 0xF008);
|
||||
hdr.write_shift(4, 0x08);
|
||||
hdr.write_shift(4, comments.length + 1);
|
||||
hdr.write_shift(4, b8oid);
|
||||
}
|
||||
/* [MS-ODRAW] 2.2.16 OfficeArtSpgrContainer */
|
||||
{
|
||||
hdr.write_shift(2, 0x0f);
|
||||
hdr.write_shift(2, 0xF003);
|
||||
hdr.write_shift(4, sz + 0x30);
|
||||
/* [MS-ODRAW] 2.2.14 OfficeArtSpContainer */
|
||||
{
|
||||
hdr.write_shift(2, 0x0f);
|
||||
hdr.write_shift(2, 0xF004);
|
||||
hdr.write_shift(4, 0x28);
|
||||
/* [MS-ODRAW] 2.2.38 OfficeArtFSPGR */
|
||||
{
|
||||
hdr.write_shift(2, 0x01);
|
||||
hdr.write_shift(2, 0xF009);
|
||||
hdr.write_shift(4, 0x10);
|
||||
hdr.l += 16;
|
||||
}
|
||||
/* [MS-ODRAW] 2.2.40 OfficeArtFSP */
|
||||
{
|
||||
hdr.write_shift(2, 0x02);
|
||||
hdr.write_shift(2, 0xF00A);
|
||||
hdr.write_shift(4, 0x08);
|
||||
hdr.write_shift(4, baseid);
|
||||
hdr.write_shift(4, 0x05);
|
||||
}
|
||||
}
|
||||
}
|
||||
write_biff_rec(ba, 0x00EC, /* hdr */ _oasc ? bconcat([hdr, _oasc]) : hdr);
|
||||
}
|
||||
ba.push(pl.end());
|
||||
notes.forEach(function(n) { write_biff_rec(ba, 0x001C, n); });
|
||||
b8ocnts.push([baseid, comments.length + 1, b8oid]);
|
||||
++b8oid;
|
||||
}
|
||||
|
||||
function write_FONTS_biff8(ba, data, opts) {
|
||||
write_biff_rec(ba, 0x0031 /* Font */, write_Font({
|
||||
sz:12,
|
||||
@ -173,7 +473,7 @@ function write_ws_cols_biff8(ba, cols) {
|
||||
});
|
||||
}
|
||||
|
||||
function write_ws_biff8_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts) {
|
||||
function write_ws_biff8_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts, date1904/*:boolean*/) {
|
||||
var os = 16 + get_cell_style(opts.cellXfs, cell, opts);
|
||||
if(cell.v == null && !cell.bf) {
|
||||
write_biff_rec(ba, 0x0201 /* Blank */, write_XLSCell(R, C, os));
|
||||
@ -182,9 +482,11 @@ function write_ws_biff8_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:n
|
||||
if(cell.bf) write_biff_rec(ba, 0x0006 /* Formula */, write_Formula(cell, R, C, opts, os));
|
||||
else switch(cell.t) {
|
||||
case 'd': case 'n':
|
||||
var v = cell.t == 'd' ? datenum(parseDate(cell.v)) : cell.v;
|
||||
var v = cell.t == 'd' ? datenum(parseDate(cell.v, date1904), date1904) : cell.v;
|
||||
if(isNaN(v)) write_biff_rec(ba, 0x0205 /* BoolErr */, write_BoolErr(R, C, 0x24, os, opts, "e")); // #NUM!
|
||||
else if(!isFinite(v)) write_biff_rec(ba, 0x0205 /* BoolErr */, write_BoolErr(R, C, 0x07, os, opts, "e")); // #DIV/0!
|
||||
/* TODO: emit RK as appropriate */
|
||||
write_biff_rec(ba, 0x0203 /* Number */, write_Number(R, C, v, os, opts));
|
||||
else write_biff_rec(ba, 0x0203 /* Number */, write_Number(R, C, v, os, opts));
|
||||
break;
|
||||
case 'b': case 'e':
|
||||
write_biff_rec(ba, 0x0205 /* BoolErr */, write_BoolErr(R, C, cell.v, os, opts, cell.t));
|
||||
@ -192,9 +494,9 @@ function write_ws_biff8_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:n
|
||||
/* TODO: codepage, sst */
|
||||
case 's': case 'str':
|
||||
if(opts.bookSST) {
|
||||
var isst = get_sst_id(opts.Strings, cell.v, opts.revStrings);
|
||||
var isst = get_sst_id(opts.Strings, cell.v == null ? "" : String(cell.v), opts.revStrings);
|
||||
write_biff_rec(ba, 0x00fd /* LabelSst */, write_LabelSst(R, C, isst, os, opts));
|
||||
} else write_biff_rec(ba, 0x0204 /* Label */, write_Label(R, C, (cell.v||"").slice(0,255), os, opts));
|
||||
} else write_biff_rec(ba, 0x0204 /* Label */, write_Label(R, C, (cell.v == null ? "" : String(cell.v)).slice(0,255), os, opts));
|
||||
break;
|
||||
default:
|
||||
write_biff_rec(ba, 0x0201 /* Blank */, write_XLSCell(R, C, os));
|
||||
@ -207,7 +509,7 @@ function write_ws_biff8(idx/*:number*/, opts, wb/*:Workbook*/) {
|
||||
var s = wb.SheetNames[idx], ws = wb.Sheets[s] || {};
|
||||
var _WB/*:WBWBProps*/ = ((wb||{}).Workbook||{}/*:any*/);
|
||||
var _sheet/*:WBWSProp*/ = ((_WB.Sheets||[])[idx]||{}/*:any*/);
|
||||
var dense = Array.isArray(ws);
|
||||
var dense = ws["!data"] != null;
|
||||
var b8 = opts.biff == 8;
|
||||
var ref/*:string*/, rr = "", cols/*:Array<string>*/ = [];
|
||||
var range = safe_decode_range(ws['!ref'] || "A1");
|
||||
@ -241,24 +543,27 @@ function write_ws_biff8(idx/*:number*/, opts, wb/*:Workbook*/) {
|
||||
write_biff_rec(ba, 0x0200 /* Dimensions */, write_Dimensions(range, opts));
|
||||
/* ... */
|
||||
|
||||
var date1904 = (((wb||{}).Workbook||{}).WBProps||{}).date1904;
|
||||
if(b8) ws['!links'] = [];
|
||||
var comments = [];
|
||||
var row = [];
|
||||
for(var C = range.s.c; C <= range.e.c; ++C) cols[C] = encode_col(C);
|
||||
for(var R = range.s.r; R <= range.e.r; ++R) {
|
||||
if(dense) row = ws["!data"][R] || [];
|
||||
rr = encode_row(R);
|
||||
for(var C = range.s.c; C <= range.e.c; ++C) {
|
||||
if(R === range.s.r) cols[C] = encode_col(C);
|
||||
for(C = range.s.c; C <= range.e.c; ++C) {
|
||||
ref = cols[C] + rr;
|
||||
var cell = dense ? (ws[R]||[])[C] : ws[ref];
|
||||
var cell = dense ? row[C] : ws[ref];
|
||||
if(!cell) continue;
|
||||
/* write cell */
|
||||
write_ws_biff8_cell(ba, cell, R, C, opts);
|
||||
write_ws_biff8_cell(ba, cell, R, C, opts, date1904);
|
||||
if(b8 && cell.l) ws['!links'].push([ref, cell.l]);
|
||||
if(b8 && cell.c) comments.push([ref, cell.c]);
|
||||
if(cell.c) comments.push([cell.c, R, C]);
|
||||
}
|
||||
}
|
||||
var cname/*:string*/ = _sheet.CodeName || _sheet.name || s;
|
||||
/* ... */
|
||||
// if(b8) comments.forEach(function(comment) { write_biff_rec(ba, 0x001c /* Note */, write_NoteSh(comment)); });
|
||||
if(b8) write_comments_biff8(ba, comments); else write_comments_biff2(ba, comments);
|
||||
/* ... */
|
||||
if(b8) write_biff_rec(ba, 0x023e /* Window2 */, write_Window2((_WB.Views||[])[0]));
|
||||
/* ... */
|
||||
@ -328,8 +633,10 @@ function write_biff8_global(wb/*:Workbook*/, bufs, opts/*:WriteOpts*/) {
|
||||
var C = buf_array();
|
||||
/* METADATA [MTRSettings] [ForceFullCalculation] */
|
||||
if(b8) write_biff_rec(C, 0x008C /* Country */, write_Country());
|
||||
/* *SUPBOOK *LBL *RTD [RecalcId] *HFPicture *MSODRAWINGGROUP */
|
||||
/* *SUPBOOK *LBL *RTD [RecalcId] *HFPicture */
|
||||
|
||||
/* BIFF8: MsoDrawingGroup [*Continue] */
|
||||
if(b8 && b8ocnts.length) write_biff_rec(C, 0x00EB /* MsoDrawingGroup */, write_MsoDrawingGroup());
|
||||
/* BIFF8: [SST *Continue] ExtSST */
|
||||
if(b8 && opts.Strings) write_biff_continue(C, 0x00FC /* SST */, write_SST(opts.Strings, opts));
|
||||
|
||||
@ -372,6 +679,7 @@ function write_biff8_buf(wb/*:Workbook*/, opts/*:WriteOpts*/) {
|
||||
o.ssf = wb.SSF;
|
||||
}
|
||||
|
||||
b8oid = 1; b8ocnts = [];
|
||||
o.Strings = /*::((*/[]/*:: :any):SST)*/; o.Strings.Count = 0; o.Strings.Unique = 0;
|
||||
fix_write_opts(o);
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
/* note: browser DOM element cannot see mso- style attrs, must parse */
|
||||
function html_to_sheet(str/*:string*/, _opts)/*:Workbook*/ {
|
||||
var opts = _opts || {};
|
||||
if(DENSE != null && opts.dense == null) opts.dense = DENSE;
|
||||
var ws/*:Worksheet*/ = opts.dense ? ([]/*:any*/) : ({}/*:any*/);
|
||||
var dense = (opts.dense != null) ? opts.dense : DENSE;
|
||||
var ws/*:Worksheet*/ = ({}/*:any*/); if(dense) ws["!data"] = [];
|
||||
str = str.replace(/<!--.*?-->/g, "");
|
||||
var mtch/*:any*/ = str.match(/<table/i);
|
||||
if(!mtch) throw new Error("Invalid HTML: could not find <table>");
|
||||
@ -45,10 +45,12 @@ function html_to_sheet(str/*:string*/, _opts)/*:Workbook*/ {
|
||||
else if(!isNaN(fuzzynum(m))) o = {t:'n', v:fuzzynum(m)};
|
||||
else if(!isNaN(fuzzydate(m).getDate())) {
|
||||
o = ({t:'d', v:parseDate(m)}/*:any*/);
|
||||
if(opts.UTC === false) o.v = utc_to_local(o.v);
|
||||
if(!opts.cellDates) o = ({t:'n', v:datenum(o.v)}/*:any*/);
|
||||
o.z = opts.dateNF || table_fmt[14];
|
||||
}
|
||||
if(opts.dense) { if(!ws[R]) ws[R] = []; ws[R][C] = o; }
|
||||
if(o.cellText !== false) o.w = m;
|
||||
if(dense) { if(!ws["!data"][R]) ws["!data"][R] = []; ws["!data"][R][C] = o; }
|
||||
else ws[encode_cell({r:R, c:C})] = o;
|
||||
C += CS;
|
||||
}
|
||||
@ -61,6 +63,7 @@ function make_html_row(ws/*:Worksheet*/, r/*:Range*/, R/*:number*/, o/*:Sheet2HT
|
||||
var M/*:Array<Range>*/ = (ws['!merges'] ||[]);
|
||||
var oo/*:Array<string>*/ = [];
|
||||
var sp = ({}/*:any*/);
|
||||
var dense = ws["!data"] != null;
|
||||
for(var C = r.s.c; C <= r.e.c; ++C) {
|
||||
var RS = 0, CS = 0;
|
||||
for(var j = 0; j < M.length; ++j) {
|
||||
@ -70,8 +73,8 @@ function make_html_row(ws/*:Worksheet*/, r/*:Range*/, R/*:number*/, o/*:Sheet2HT
|
||||
RS = M[j].e.r - M[j].s.r + 1; CS = M[j].e.c - M[j].s.c + 1; break;
|
||||
}
|
||||
if(RS < 0) continue;
|
||||
var coord = encode_cell({r:R,c:C});
|
||||
var cell = o.dense ? (ws[R]||[])[C] : ws[coord];
|
||||
var coord = encode_col(C) + encode_row(R);
|
||||
var cell = dense ? (ws["!data"][R]||[])[C] : ws[coord];
|
||||
/* TODO: html entities */
|
||||
var w = (cell && cell.v != null) && (cell.h || escapehtml(cell.w || (format_cell(cell), cell.w) || "")) || "";
|
||||
sp = ({}/*:any*/);
|
||||
@ -80,9 +83,10 @@ function make_html_row(ws/*:Worksheet*/, r/*:Range*/, R/*:number*/, o/*:Sheet2HT
|
||||
if(o.editable) w = '<span contenteditable="true">' + w + '</span>';
|
||||
else if(cell) {
|
||||
sp["data-t"] = cell && cell.t || 'z';
|
||||
if(cell.v != null) sp["data-v"] = cell.v;
|
||||
// note: data-v is unaffected by the timezone interpretation
|
||||
if(cell.v != null) sp["data-v"] = cell.v instanceof Date ? cell.v.toISOString() : cell.v;
|
||||
if(cell.z != null) sp["data-z"] = cell.z;
|
||||
if(cell.l && (cell.l.Target || "#").charAt(0) != "#") w = '<a href="' + cell.l.Target +'">' + w + '</a>';
|
||||
if(cell.l && (cell.l.Target || "#").charAt(0) != "#") w = '<a href="' + escapehtml(cell.l.Target) +'">' + w + '</a>';
|
||||
}
|
||||
sp.id = (o.id || "sjs") + "-" + coord;
|
||||
oo.push(writextag('td', w, sp));
|
||||
@ -97,9 +101,14 @@ var HTML_END = '</body></html>';
|
||||
function html_to_workbook(str/*:string*/, opts)/*:Workbook*/ {
|
||||
var mtch = str.match(/<table[\s\S]*?>[\s\S]*?<\/table>/gi);
|
||||
if(!mtch || mtch.length == 0) throw new Error("Invalid HTML: could not find <table>");
|
||||
if(mtch.length == 1) return sheet_to_workbook(html_to_sheet(mtch[0], opts), opts);
|
||||
if(mtch.length == 1) {
|
||||
var w = sheet_to_workbook(html_to_sheet(mtch[0], opts), opts);
|
||||
w.bookType = "html";
|
||||
return w;
|
||||
}
|
||||
var wb = book_new();
|
||||
mtch.forEach(function(s, idx) { book_append_sheet(wb, html_to_sheet(s, opts), "Sheet" + (idx+1)); });
|
||||
wb.bookType = "html";
|
||||
return wb;
|
||||
}
|
||||
|
||||
@ -113,10 +122,9 @@ function sheet_to_html(ws/*:Worksheet*/, opts/*:?Sheet2HTMLOpts*//*, wb:?Workboo
|
||||
var header = o.header != null ? o.header : HTML_BEGIN;
|
||||
var footer = o.footer != null ? o.footer : HTML_END;
|
||||
var out/*:Array<string>*/ = [header];
|
||||
var r = decode_range(ws['!ref']);
|
||||
o.dense = Array.isArray(ws);
|
||||
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("");
|
||||
}
|
||||
@ -129,7 +137,7 @@ function sheet_add_dom(ws/*:Worksheet*/, table/*:HTMLElement*/, _opts/*:?any*/)/
|
||||
}
|
||||
|
||||
var opts = _opts || {};
|
||||
if(DENSE != null) opts.dense = DENSE;
|
||||
var dense = ws["!data"] != null;
|
||||
var or_R = 0, or_C = 0;
|
||||
if(opts.origin != null) {
|
||||
if(typeof opts.origin == 'number') or_R = opts.origin;
|
||||
@ -182,6 +190,7 @@ function sheet_add_dom(ws/*:Worksheet*/, table/*:HTMLElement*/, _opts/*:?any*/)/
|
||||
else if(!isNaN(fuzzynum(v))) o = {t:'n', v:fuzzynum(v)};
|
||||
else if(!isNaN(fuzzydate(v).getDate())) {
|
||||
o = ({t:'d', v:parseDate(v)}/*:any*/);
|
||||
if(opts.UTC) o.v = local_to_utc(o.v);
|
||||
if(!opts.cellDates) o = ({t:'n', v:datenum(o.v)}/*:any*/);
|
||||
o.z = opts.dateNF || table_fmt[14];
|
||||
}
|
||||
@ -194,7 +203,7 @@ function sheet_add_dom(ws/*:Worksheet*/, table/*:HTMLElement*/, _opts/*:?any*/)/
|
||||
l = Aelts[Aelti].getAttribute("href"); if(l.charAt(0) != "#") break;
|
||||
}
|
||||
if(l && l.charAt(0) != "#" && l.slice(0, 11).toLowerCase() != 'javascript:') o.l = ({ Target: l });
|
||||
if(opts.dense) { if(!ws[R + or_R]) ws[R + or_R] = []; ws[R + or_R][C + or_C] = o; }
|
||||
if(dense) { if(!ws["!data"][R + or_R]) ws["!data"][R + or_R] = []; ws["!data"][R + or_R][C + or_C] = o; }
|
||||
else ws[encode_cell({c:C + or_C, r:R + or_R})] = o;
|
||||
if(range.e.c < C + or_C) range.e.c = C + or_C;
|
||||
C += CS;
|
||||
@ -210,12 +219,14 @@ function sheet_add_dom(ws/*:Worksheet*/, table/*:HTMLElement*/, _opts/*:?any*/)/
|
||||
|
||||
function parse_dom_table(table/*:HTMLElement*/, _opts/*:?any*/)/*:Worksheet*/ {
|
||||
var opts = _opts || {};
|
||||
var ws/*:Worksheet*/ = opts.dense ? ([]/*:any*/) : ({}/*:any*/);
|
||||
var ws/*:Worksheet*/ = ({}/*:any*/); if(opts.dense) ws["!data"] = [];
|
||||
return sheet_add_dom(ws, table, _opts);
|
||||
}
|
||||
|
||||
function table_to_book(table/*:HTMLElement*/, opts/*:?any*/)/*:Workbook*/ {
|
||||
return sheet_to_workbook(parse_dom_table(table, opts), opts);
|
||||
var o = sheet_to_workbook(parse_dom_table(table, opts), opts);
|
||||
//o.bookType = "dom"; // TODO: define a type for this
|
||||
return o;
|
||||
}
|
||||
|
||||
function is_dom_element_hidden(element/*:HTMLElement*/)/*:boolean*/ {
|
||||
|
@ -12,44 +12,251 @@ function parse_text_p(text/*:string*//*::, tag*/)/*:Array<any>*/ {
|
||||
return [v];
|
||||
}
|
||||
|
||||
var number_formats_ods = {
|
||||
/* ods name: [short ssf fmt, long ssf fmt] */
|
||||
day: ["d", "dd"],
|
||||
month: ["m", "mm"],
|
||||
year: ["y", "yy"],
|
||||
hours: ["h", "hh"],
|
||||
minutes: ["m", "mm"],
|
||||
seconds: ["s", "ss"],
|
||||
"am-pm": ["A/P", "AM/PM"],
|
||||
"day-of-week": ["ddd", "dddd"],
|
||||
era: ["e", "ee"],
|
||||
/* there is no native representation of LO "Q" format */
|
||||
quarter: ["\\Qm", "m\\\"th quarter\""]
|
||||
};
|
||||
/* Note: ODS can stick styles in content.xml or styles.xml, FODS blurs lines */
|
||||
function parse_ods_styles(d/*:string*/, _opts, _nfm) {
|
||||
var number_format_map = _nfm || {};
|
||||
var str = xlml_normalize(d);
|
||||
xlmlregex.lastIndex = 0;
|
||||
str = str.replace(/<!--([\s\S]*?)-->/mg,"").replace(/<!DOCTYPE[^\[]*\[[^\]]*\]>/gm,"");
|
||||
var Rn, NFtag, NF = "", tNF = "", y, etpos = 0, tidx = -1, infmt = false, payload = "";
|
||||
while((Rn = xlmlregex.exec(str))) {
|
||||
switch((Rn[3]=Rn[3].replace(/_.*$/,""))) {
|
||||
/* Number Format Definitions */
|
||||
case 'number-style': // <number:number-style> 16.29.2
|
||||
case 'currency-style': // <number:currency-style> 16.29.8
|
||||
case 'percentage-style': // <number:percentage-style> 16.29.10
|
||||
case 'date-style': // <number:date-style> 16.29.11
|
||||
case 'time-style': // <number:time-style> 16.29.19
|
||||
case 'text-style': // <number:text-style> 16.29.26
|
||||
if(Rn[1]==='/') {
|
||||
infmt = false;
|
||||
if(NFtag['truncate-on-overflow'] == "false") {
|
||||
if(NF.match(/h/)) NF = NF.replace(/h+/, "[$&]");
|
||||
else if(NF.match(/m/)) NF = NF.replace(/m+/, "[$&]");
|
||||
else if(NF.match(/s/)) NF = NF.replace(/s+/, "[$&]");
|
||||
}
|
||||
number_format_map[NFtag.name] = NF;
|
||||
NF = "";
|
||||
} else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
|
||||
infmt = true;
|
||||
NF = "";
|
||||
NFtag = parsexmltag(Rn[0], false);
|
||||
} break;
|
||||
|
||||
// LibreOffice bug https://bugs.documentfoundation.org/show_bug.cgi?id=149484
|
||||
case 'boolean-style': // <number:boolean-style> 16.29.24
|
||||
if(Rn[1]==='/') {
|
||||
infmt = false;
|
||||
number_format_map[NFtag.name] = "General";
|
||||
NF = "";
|
||||
} else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
|
||||
infmt = true;
|
||||
NF = "";
|
||||
NFtag = parsexmltag(Rn[0], false);
|
||||
} break;
|
||||
|
||||
/* Number Format Elements */
|
||||
case 'boolean': // <number:boolean> 16.29.25
|
||||
NF += "General"; // ODF spec is unfortunately underspecified here
|
||||
break;
|
||||
|
||||
case 'text': // <number:text> 16.29.27
|
||||
if(Rn[1]==='/') {
|
||||
payload = str.slice(tidx, xlmlregex.lastIndex - Rn[0].length);
|
||||
// NOTE: Excel has a different interpretation of "%%" and friends
|
||||
if(payload == "%" && NFtag[0] == '<number:percentage-style') NF += "%";
|
||||
else NF += '"' + payload.replace(/"/g, '""') + '"';
|
||||
} else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
|
||||
tidx = xlmlregex.lastIndex;
|
||||
} break;
|
||||
|
||||
|
||||
function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
case 'day': { // <number:day> 16.29.12
|
||||
y = parsexmltag(Rn[0], false);
|
||||
switch(y["style"]) {
|
||||
case "short": NF += "d"; break;
|
||||
case "long": NF += "dd"; break;
|
||||
default: NF += "dd"; break; // TODO: error condition
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'day-of-week': { // <number:day-of-week> 16.29.16
|
||||
y = parsexmltag(Rn[0], false);
|
||||
switch(y["style"]) {
|
||||
case "short": NF += "ddd"; break;
|
||||
case "long": NF += "dddd"; break;
|
||||
default: NF += "ddd"; break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'era': { // <number:era> 16.29.15 TODO: proper mapping
|
||||
y = parsexmltag(Rn[0], false);
|
||||
switch(y["style"]) {
|
||||
case "short": NF += "ee"; break;
|
||||
case "long": NF += "eeee"; break;
|
||||
default: NF += "eeee"; break; // TODO: error condition
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'hours': { // <number:hours> 16.29.20
|
||||
y = parsexmltag(Rn[0], false);
|
||||
switch(y["style"]) {
|
||||
case "short": NF += "h"; break;
|
||||
case "long": NF += "hh"; break;
|
||||
default: NF += "hh"; break; // TODO: error condition
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'minutes': { // <number:minutes> 16.29.21
|
||||
y = parsexmltag(Rn[0], false);
|
||||
switch(y["style"]) {
|
||||
case "short": NF += "m"; break;
|
||||
case "long": NF += "mm"; break;
|
||||
default: NF += "mm"; break; // TODO: error condition
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'month': { // <number:month> 16.29.13
|
||||
y = parsexmltag(Rn[0], false);
|
||||
if(y["textual"]) NF += "mm";
|
||||
switch(y["style"]) {
|
||||
case "short": NF += "m"; break;
|
||||
case "long": NF += "mm"; break;
|
||||
default: NF += "m"; break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'seconds': { // <number:seconds> 16.29.22
|
||||
y = parsexmltag(Rn[0], false);
|
||||
switch(y["style"]) {
|
||||
case "short": NF += "s"; break;
|
||||
case "long": NF += "ss"; break;
|
||||
default: NF += "ss"; break; // TODO: error condition
|
||||
}
|
||||
if(y["decimal-places"]) NF += "." + fill("0", +y["decimal-places"]);
|
||||
} break;
|
||||
|
||||
case 'year': { // <number:year> 16.29.14
|
||||
y = parsexmltag(Rn[0], false);
|
||||
switch(y["style"]) {
|
||||
case "short": NF += "yy"; break;
|
||||
case "long": NF += "yyyy"; break;
|
||||
default: NF += "yy"; break; // TODO: error condition
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'am-pm': // <number:am-pm> 16.29.23
|
||||
NF += "AM/PM"; // LO autocorrects A/P -> AM/PM
|
||||
break;
|
||||
|
||||
case 'week-of-year': // <number:week-of-year> 16.29.17
|
||||
case 'quarter': // <number:quarter> 16.29.18
|
||||
console.error("Excel does not support ODS format token " + Rn[3]);
|
||||
break;
|
||||
|
||||
case 'fill-character': // <number:fill-character> 16.29.5
|
||||
if(Rn[1]==='/') {
|
||||
payload = str.slice(tidx, xlmlregex.lastIndex - Rn[0].length);
|
||||
// NOTE: Excel has a different interpretation of "%%" and friends
|
||||
NF += '"' + payload.replace(/"/g, '""') + '"*';
|
||||
} else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
|
||||
tidx = xlmlregex.lastIndex;
|
||||
} break;
|
||||
|
||||
case 'scientific-number': // <number:scientific-number> 16.29.6
|
||||
// TODO: find a mapping for all parameters
|
||||
y = parsexmltag(Rn[0], false);
|
||||
NF += "0." + fill("0", +y["min-decimal-places"] || +y["decimal-places"] || 2) + fill("?", +y["decimal-places"] - +y["min-decimal-places"] || 0) + "E" + (parsexmlbool(y["forced-exponent-sign"]) ? "+" : "") + fill("0", +y["min-exponent-digits"] || 2);
|
||||
break;
|
||||
|
||||
case 'fraction': // <number:fraction> 16.29.7
|
||||
// TODO: find a mapping for all parameters
|
||||
y = parsexmltag(Rn[0], false);
|
||||
if(!+y["min-integer-digits"]) NF += "#";
|
||||
else NF += fill("0", +y["min-integer-digits"]);
|
||||
NF += " ";
|
||||
NF += fill("?", +y["min-numerator-digits"] || 1);
|
||||
NF += "/";
|
||||
if(+y["denominator-value"]) NF += y["denominator-value"];
|
||||
else NF += fill("?", +y["min-denominator-digits"] || 1);
|
||||
break;
|
||||
|
||||
case 'currency-symbol': // <number:currency-symbol> 16.29.9
|
||||
// TODO: localization with [$-...]
|
||||
if(Rn[1]==='/') {
|
||||
NF += '"' + str.slice(tidx, xlmlregex.lastIndex - Rn[0].length).replace(/"/g, '""') + '"';
|
||||
} else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
|
||||
tidx = xlmlregex.lastIndex;
|
||||
} else NF += "$";
|
||||
break;
|
||||
|
||||
case 'text-properties': // <style:text-properties> 16.29.29
|
||||
y = parsexmltag(Rn[0], false);
|
||||
switch((y["color"]||"").toLowerCase().replace("#", "")) {
|
||||
case "ff0000": case "red": NF = "[Red]" + NF; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'text-content': // <number:text-content> 16.29.28
|
||||
NF += "@";
|
||||
break;
|
||||
|
||||
case 'map': // <style:map> 16.3
|
||||
// TODO: handle more complex maps
|
||||
y = parsexmltag(Rn[0], false);
|
||||
if(unescapexml(y["condition"]) == "value()>=0") NF = number_format_map[y["apply-style-name"]] + ";" + NF;
|
||||
else console.error("ODS number format may be incorrect: " + y["condition"]);
|
||||
break;
|
||||
|
||||
case 'number': // <number:number> 16.29.3
|
||||
// TODO: handle all the attributes
|
||||
if(Rn[1]==='/') break;
|
||||
y = parsexmltag(Rn[0], false);
|
||||
tNF = "";
|
||||
tNF += fill("0", +y["min-integer-digits"] || 1);
|
||||
if(parsexmlbool(y["grouping"])) tNF = commaify(fill("#", Math.max(0, 4 - tNF.length)) + tNF);
|
||||
if(+y["min-decimal-places"] || +y["decimal-places"]) tNF += ".";
|
||||
if(+y["min-decimal-places"]) tNF += fill("0", +y["min-decimal-places"] || 1);
|
||||
if(+y["decimal-places"] - (+y["min-decimal-places"]||0)) tNF += fill("0", +y["decimal-places"] - (+y["min-decimal-places"]||0)); // TODO: should this be "#" ?
|
||||
NF += tNF;
|
||||
break;
|
||||
|
||||
case 'embedded-text': // <number:embedded-text> 16.29.4
|
||||
// TODO: verify interplay with grouping et al
|
||||
if(Rn[1]==='/') {
|
||||
if(etpos == 0) NF += '"' + str.slice(tidx, xlmlregex.lastIndex - Rn[0].length).replace(/"/g, '""') + '"';
|
||||
else NF = NF.slice(0, etpos) + '"' + str.slice(tidx, xlmlregex.lastIndex - Rn[0].length).replace(/"/g, '""') + '"' + NF.slice(etpos);
|
||||
} else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
|
||||
tidx = xlmlregex.lastIndex;
|
||||
etpos = -+parsexmltag(Rn[0], false)["position"] || 0;
|
||||
} break;
|
||||
|
||||
}}
|
||||
return number_format_map;
|
||||
}
|
||||
|
||||
function parse_content_xml(d/*:string*/, _opts, _nfm)/*:Workbook*/ {
|
||||
var opts = _opts || {};
|
||||
if(DENSE != null && opts.dense == null) opts.dense = DENSE;
|
||||
var str = xlml_normalize(d);
|
||||
var state/*:Array<any>*/ = [], tmp;
|
||||
var tag/*:: = {}*/;
|
||||
var NFtag = {name:""}, NF = "", pidx = 0;
|
||||
var nfidx, NF = "", pidx = 0;
|
||||
var sheetag/*:: = {name:"", '名称':""}*/;
|
||||
var rowtag/*:: = {'行号':""}*/;
|
||||
var Sheets = {}, SheetNames/*:Array<string>*/ = [];
|
||||
var ws = opts.dense ? ([]/*:any*/) : ({}/*:any*/);
|
||||
var ws = ({}/*:any*/); if(opts.dense) ws["!data"] = [];
|
||||
var Rn, q/*:: :any = ({t:"", v:null, z:null, w:"",c:[],}:any)*/;
|
||||
var ctag = ({value:""}/*:any*/);
|
||||
var textp = "", textpidx = 0, textptag/*:: = {}*/;
|
||||
var textR = [];
|
||||
var textp = "", textpidx = 0, textptag/*:: = {}*/, oldtextp = "", oldtextpidx = 0;
|
||||
var textR = [], oldtextR = [];
|
||||
var R = -1, C = -1, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
|
||||
var row_ol = 0;
|
||||
var number_format_map = {};
|
||||
var number_format_map = _nfm || {}, styles = {};
|
||||
var merges/*:Array<Range>*/ = [], mrange = {}, mR = 0, mC = 0;
|
||||
var rowinfo/*:Array<RowInfo>*/ = [], rowpeat = 1, colpeat = 1;
|
||||
var arrayf/*:Array<[Range, string]>*/ = [];
|
||||
var WB = {Names:[]};
|
||||
var WB = {Names:[], WBProps:{}};
|
||||
var atag = ({}/*:any*/);
|
||||
var _Ref/*:[string, string]*/ = ["", ""];
|
||||
var comments/*:Array<Comment>*/ = [], comment/*:Comment*/ = ({}/*:any*/);
|
||||
@ -81,7 +288,7 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
sheetag = parsexmltag(Rn[0], false);
|
||||
R = C = -1;
|
||||
range.s.r = range.s.c = 10000000; range.e.r = range.e.c = 0;
|
||||
ws = opts.dense ? ([]/*:any*/) : ({}/*:any*/); merges = [];
|
||||
ws = ({}/*:any*/); if(opts.dense) ws["!data"] = []; merges = [];
|
||||
rowinfo = [];
|
||||
intable = true;
|
||||
}
|
||||
@ -101,7 +308,7 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
case 'covered-table-cell': // 9.1.5 <table:covered-table-cell>
|
||||
if(Rn[1] !== '/') ++C;
|
||||
if(opts.sheetStubs) {
|
||||
if(opts.dense) { if(!ws[R]) ws[R] = []; ws[R][C] = {t:'z'}; }
|
||||
if(opts.dense) { if(!ws["!data"][R]) ws["!data"][R] = []; ws["!data"][R][C] = {t:'z'}; }
|
||||
else ws[encode_cell({r:R,c:C})] = {t:'z'};
|
||||
}
|
||||
textp = ""; textR = [];
|
||||
@ -113,19 +320,20 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
colpeat = parseInt(ctag['number-columns-repeated']||"1", 10);
|
||||
q = ({t:'z', v:null/*:: , z:null, w:"",c:[]*/}/*:any*/);
|
||||
if(ctag.formula && opts.cellFormula != false) q.f = ods_to_csf_formula(unescapexml(ctag.formula));
|
||||
if(ctag["style-name"] && styles[ctag["style-name"]]) q.z = styles[ctag["style-name"]];
|
||||
if((ctag['数据类型'] || ctag['value-type']) == "string") {
|
||||
q.t = "s"; q.v = unescapexml(ctag['string-value'] || "");
|
||||
if(opts.dense) {
|
||||
if(!ws[R]) ws[R] = [];
|
||||
ws[R][C] = q;
|
||||
if(!ws["!data"][R]) ws["!data"][R] = [];
|
||||
ws["!data"][R][C] = q;
|
||||
} else {
|
||||
ws[encode_cell({r:R,c:C})] = q;
|
||||
ws[encode_col(C) + encode_row(R)] = q;
|
||||
}
|
||||
}
|
||||
C+= colpeat-1;
|
||||
} else if(Rn[1]!=='/') {
|
||||
++C;
|
||||
textp = ""; textpidx = 0; textR = [];
|
||||
textp = oldtextp = ""; textpidx = oldtextpidx = 0; textR = []; oldtextR = [];
|
||||
colpeat = 1;
|
||||
var rptR = rowpeat ? R + rowpeat - 1 : R;
|
||||
if(C > range.e.c) range.e.c = C;
|
||||
@ -135,6 +343,7 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
ctag = parsexmltag(Rn[0], false);
|
||||
comments = []; comment = ({}/*:any*/);
|
||||
q = ({t:ctag['数据类型'] || ctag['value-type'], v:null/*:: , z:null, w:"",c:[]*/}/*:any*/);
|
||||
if(ctag["style-name"] && styles[ctag["style-name"]]) q.z = styles[ctag["style-name"]];
|
||||
if(opts.cellFormula) {
|
||||
if(ctag.formula) ctag.formula = unescapexml(ctag.formula);
|
||||
if(ctag['number-matrix-columns-spanned'] && ctag['number-matrix-rows-spanned']) {
|
||||
@ -160,19 +369,23 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
/* 19.675.2 table:number-columns-repeated */
|
||||
if(ctag['number-columns-repeated']) colpeat = parseInt(ctag['number-columns-repeated'], 10);
|
||||
|
||||
/* 19.385 office:value-type */
|
||||
/* 19.385 office:value-type TODO: verify ODS and UOS */
|
||||
switch(q.t) {
|
||||
case 'boolean': q.t = 'b'; q.v = parsexmlbool(ctag['boolean-value']); break;
|
||||
case 'float': q.t = 'n'; q.v = parseFloat(ctag.value); break;
|
||||
case 'boolean': q.t = 'b'; q.v = parsexmlbool(ctag['boolean-value']) || (+ctag['boolean-value'] >= 1); break;
|
||||
case 'float': q.t = 'n'; q.v = parseFloat(ctag.value);
|
||||
if(opts.cellDates && q.z && fmt_is_date(q.z)) { q.v = numdate(q.v + (WB.WBProps.date1904 ? 1462 : 0)); q.t = typeof q.v == "number" ? 'n' : 'd'; }
|
||||
break;
|
||||
case 'percentage': q.t = 'n'; q.v = parseFloat(ctag.value); break;
|
||||
case 'currency': q.t = 'n'; q.v = parseFloat(ctag.value); break;
|
||||
case 'date': q.t = 'd'; q.v = parseDate(ctag['date-value']);
|
||||
if(!opts.cellDates) { q.t = 'n'; q.v = datenum(q.v); }
|
||||
q.z = 'm/d/yy'; break;
|
||||
case 'date': q.t = 'd'; q.v = parseDate(ctag['date-value'], WB.WBProps.date1904);
|
||||
if(!opts.cellDates) { q.t = 'n'; q.v = datenum(q.v, WB.WBProps.date1904); }
|
||||
if(!q.z) q.z = 'm/d/yy'; break;
|
||||
/* NOTE: for `time`, Excel ODS export incorrectly uses durations relative to 1900 epoch even if 1904 is specified */
|
||||
case 'time': q.t = 'n'; q.v = parse_isodur(ctag['time-value'])/86400;
|
||||
if(opts.cellDates) { q.t = 'd'; q.v = numdate(q.v); }
|
||||
q.z = 'HH:MM:SS'; break;
|
||||
case 'number': q.t = 'n'; q.v = parseFloat(ctag['数据数值']); break;
|
||||
if(opts.cellDates) { q.v = numdate(q.v); q.t = typeof q.v == "number" ? 'n' : 'd'; }
|
||||
if(!q.z) q.z = 'HH:MM:SS'; break;
|
||||
case 'number': q.t = 'n'; q.v = parseFloat(ctag['数据数值']);
|
||||
break;
|
||||
default:
|
||||
if(q.t === 'string' || q.t === 'text' || !q.t) {
|
||||
q.t = 's';
|
||||
@ -195,9 +408,9 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
for(var rpt = 0; rpt < rowpeat; ++rpt) {
|
||||
colpeat = parseInt(ctag['number-columns-repeated']||"1", 10);
|
||||
if(opts.dense) {
|
||||
if(!ws[R + rpt]) ws[R + rpt] = [];
|
||||
ws[R + rpt][C] = rpt == 0 ? q : dup(q);
|
||||
while(--colpeat > 0) ws[R + rpt][C + colpeat] = dup(q);
|
||||
if(!ws["!data"][R + rpt]) ws["!data"][R + rpt] = [];
|
||||
ws["!data"][R + rpt][C] = rpt == 0 ? q : dup(q);
|
||||
while(--colpeat > 0) ws["!data"][R + rpt][C + colpeat] = dup(q);
|
||||
} else {
|
||||
ws[encode_cell({r:R + rpt,c:C})] = q;
|
||||
while(--colpeat > 0) ws[encode_cell({r:R + rpt,c:C + colpeat})] = dup(q);
|
||||
@ -233,10 +446,17 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
if(textR.length) /*::(*/comment/*:: :any)*/.R = textR;
|
||||
comment.a = creator;
|
||||
comments.push(comment);
|
||||
textp = oldtextp; textpidx = oldtextpidx; textR = oldtextR;
|
||||
}
|
||||
else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
|
||||
state.push([Rn[3], false]);
|
||||
var annotag = parsexmltag(Rn[0], true);
|
||||
/* office:display TODO: check if there is a global override */
|
||||
if(!(annotag["display"] && parsexmlbool(annotag["display"]))) comments.hidden = true;
|
||||
oldtextp = textp; oldtextpidx = textpidx; oldtextR = textR;
|
||||
textp = ""; textpidx = 0; textR = [];
|
||||
}
|
||||
else if(Rn[0].charAt(Rn[0].length-2) !== '/') {state.push([Rn[3], false]);}
|
||||
creator = ""; creatoridx = 0;
|
||||
textp = ""; textpidx = 0; textR = [];
|
||||
break;
|
||||
|
||||
case 'creator': // 4.3.2.7 <dc:creator>
|
||||
@ -266,23 +486,24 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
textp = ""; textpidx = 0; textR = [];
|
||||
break;
|
||||
|
||||
case 'scientific-number': // TODO: <number:scientific-number>
|
||||
break;
|
||||
case 'currency-symbol': // TODO: <number:currency-symbol>
|
||||
break;
|
||||
case 'currency-style': // TODO: <number:currency-style>
|
||||
case 'scientific-number': // <number:scientific-number>
|
||||
case 'currency-symbol': // <number:currency-symbol>
|
||||
case 'fill-character': // 16.29.5 <number:fill-character>
|
||||
break;
|
||||
|
||||
case 'text-style': // 16.27.25 <number:text-style>
|
||||
case 'boolean-style': // 16.27.23 <number:boolean-style>
|
||||
case 'number-style': // 16.27.2 <number:number-style>
|
||||
case 'currency-style': // 16.29.8 <number:currency-style>
|
||||
case 'percentage-style': // 16.27.9 <number:percentage-style>
|
||||
case 'date-style': // 16.27.10 <number:date-style>
|
||||
case 'time-style': // 16.27.18 <number:time-style>
|
||||
if(Rn[1]==='/'){
|
||||
number_format_map[NFtag.name] = NF;
|
||||
if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;
|
||||
var xlmlidx = xlmlregex.lastIndex;
|
||||
parse_ods_styles(str.slice(nfidx, xlmlregex.lastIndex), _opts, number_format_map);
|
||||
xlmlregex.lastIndex = xlmlidx;
|
||||
} else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
|
||||
NF = "";
|
||||
NFtag = parsexmltag(Rn[0], false);
|
||||
state.push([Rn[3], true]);
|
||||
nfidx = xlmlregex.lastIndex - Rn[0].length;
|
||||
} break;
|
||||
|
||||
case 'script': break; // 3.13 <office:script>
|
||||
@ -291,8 +512,10 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
|
||||
case 'default-style': // TODO: <style:default-style>
|
||||
case 'page-layout': break; // TODO: <style:page-layout>
|
||||
case 'style': // 16.2 <style:style>
|
||||
break;
|
||||
case 'style': { // 16.2 <style:style>
|
||||
var styletag = parsexmltag(Rn[0], false);
|
||||
if(styletag["family"] == "table-cell" && number_format_map[styletag["data-style-name"]]) styles[styletag["name"]] = number_format_map[styletag["data-style-name"]];
|
||||
} break;
|
||||
case 'map': break; // 16.3 <style:map>
|
||||
case 'font-face': break; // 16.21 <style:font-face>
|
||||
|
||||
@ -303,12 +526,7 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
case 'table-cell-properties': break; // 17.18 <style:table-cell-properties>
|
||||
|
||||
case 'number': // 16.27.3 <number:number>
|
||||
switch(state[state.length-1][0]) {
|
||||
case 'time-style':
|
||||
case 'date-style':
|
||||
tag = parsexmltag(Rn[0], false);
|
||||
NF += number_formats_ods[Rn[3]][tag.style==='long'?1:0]; break;
|
||||
} break;
|
||||
break;
|
||||
|
||||
case 'fraction': break; // TODO 16.27.6 <number:fraction>
|
||||
|
||||
@ -323,16 +541,9 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
case 'minutes': // 16.27.20 <number:minutes>
|
||||
case 'seconds': // 16.27.21 <number:seconds>
|
||||
case 'am-pm': // 16.27.22 <number:am-pm>
|
||||
switch(state[state.length-1][0]) {
|
||||
case 'time-style':
|
||||
case 'date-style':
|
||||
tag = parsexmltag(Rn[0], false);
|
||||
NF += number_formats_ods[Rn[3]][tag.style==='long'?1:0]; break;
|
||||
} break;
|
||||
break;
|
||||
|
||||
case 'boolean-style': break; // 16.27.23 <number:boolean-style>
|
||||
case 'boolean': break; // 16.27.24 <number:boolean>
|
||||
case 'text-style': break; // 16.27.25 <number:text-style>
|
||||
case 'text': // 16.27.26 <number:text>
|
||||
if(Rn[0].slice(-2) === "/>") break;
|
||||
else if(Rn[1]==="/") switch(state[state.length-1][0]) {
|
||||
@ -368,7 +579,12 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
case 'table-header-columns': break; // 9.1.11 <table:table-header-columns>
|
||||
case 'table-columns': break; // 9.1.12 <table:table-columns>
|
||||
|
||||
case 'null-date': break; // 9.4.2 <table:null-date> TODO: date1904
|
||||
case 'null-date': // 9.4.2 <table:null-date>
|
||||
tag = parsexmltag(Rn[0], false);
|
||||
switch(tag["date-value"]) {
|
||||
case "1904-01-01": WB.WBProps.date1904 = true; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'graphic-properties': break; // 17.21 <style:graphic-properties>
|
||||
case 'calculation-settings': break; // 9.4.1 <table:calculation-settings>
|
||||
@ -388,6 +604,9 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
if(Rn[1]==='/' && (!ctag || !ctag['string-value'])) {
|
||||
var ptp = parse_text_p(str.slice(textpidx,Rn.index), textptag);
|
||||
textp = (textp.length > 0 ? textp + "\n" : "") + ptp[0];
|
||||
} else if(Rn[0].slice(-2) == "/>") {
|
||||
/* TODO: is self-closing 文本串 valid? */
|
||||
textp += "\n";
|
||||
} else { textptag = parsexmltag(Rn[0], false); textpidx = Rn.index + Rn[0].length; }
|
||||
break; // <text:p>
|
||||
case 's': break; // <text:s>
|
||||
@ -417,10 +636,13 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
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:
|
||||
@ -556,13 +778,18 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
function parse_ods(zip/*:ZIPFile*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
opts = opts || ({}/*:any*/);
|
||||
if(safegetzipfile(zip, 'META-INF/manifest.xml')) parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts);
|
||||
var styles = getzipstr(zip, 'styles.xml');
|
||||
var Styles = styles && parse_ods_styles(utf8read(styles), opts);
|
||||
var content = getzipstr(zip, 'content.xml');
|
||||
if(!content) throw new Error("Missing content.xml in ODS / UOF file");
|
||||
var wb = parse_content_xml(utf8read(content), opts);
|
||||
var wb = parse_content_xml(utf8read(content), opts, Styles);
|
||||
if(safegetzipfile(zip, 'meta.xml')) wb.Props = parse_core_props(getzipdata(zip, 'meta.xml'));
|
||||
wb.bookType = "ods";
|
||||
return wb;
|
||||
}
|
||||
function parse_fods(data/*:string*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
return parse_content_xml(data, opts);
|
||||
var wb = parse_content_xml(data, opts);
|
||||
wb.bookType = "fods";
|
||||
return wb;
|
||||
}
|
||||
|
||||
|
@ -30,11 +30,169 @@ var write_styles_ods/*:{(wb:any, opts:any):string}*/ = /* @__PURE__ */(function(
|
||||
return XML_HEADER + payload;
|
||||
};
|
||||
})();
|
||||
|
||||
// TODO: find out if anyone actually read the spec. LO has some wild errors
|
||||
function write_number_format_ods(nf/*:string*/, nfidx/*:string*/)/*:string*/ {
|
||||
var type = "number", payload = "", nopts = { "style:name": nfidx }, c = "", i = 0;
|
||||
nf = nf.replace(/"[$]"/g, "$");
|
||||
/* TODO: replace with an actual parser based on a real grammar */
|
||||
j: {
|
||||
// TODO: support style maps
|
||||
if(nf.indexOf(";") > -1) {
|
||||
console.error("Unsupported ODS Style Map exported. Using first branch of " + nf);
|
||||
nf = nf.slice(0, nf.indexOf(";"));
|
||||
}
|
||||
|
||||
if(nf == "@") { type = "text"; payload = "<number:text-content/>"; break j; }
|
||||
|
||||
/* currency flag */
|
||||
if(nf.indexOf(/\$/) > -1) { type = "currency"; }
|
||||
|
||||
/* opening string literal */
|
||||
if(nf[i] == '"') {
|
||||
c = "";
|
||||
while(nf[++i] != '"' || nf[++i] == '"') c += nf[i]; --i;
|
||||
if(nf[i+1] == "*") {
|
||||
i++;
|
||||
payload += '<number:fill-character>' + escapexml(c.replace(/""/g, '"')) + '</number:fill-character>';
|
||||
} else {
|
||||
payload += '<number:text>' + escapexml(c.replace(/""/g, '"')) + '</number:text>';
|
||||
}
|
||||
nf = nf.slice(i+1); i = 0;
|
||||
}
|
||||
|
||||
/* fractions */
|
||||
var t = nf.match(/# (\?+)\/(\?+)/);
|
||||
if(t) { payload += writextag("number:fraction", null, {"number:min-integer-digits":0, "number:min-numerator-digits": t[1].length, "number:max-denominator-value": Math.max(+(t[1].replace(/./g, "9")), +(t[2].replace(/./g, "9"))) }); break j; }
|
||||
if((t=nf.match(/# (\?+)\/(\d+)/))) { payload += writextag("number:fraction", null, {"number:min-integer-digits":0, "number:min-numerator-digits": t[1].length, "number:denominator-value": +t[2]}); break j; }
|
||||
|
||||
/* percentages */
|
||||
if((t=nf.match(/(\d+)(|\.\d+)%/))) { type = "percentage"; payload += writextag("number:number", null, {"number:decimal-places": t[2] && t.length - 1 || 0, "number:min-decimal-places": t[2] && t.length - 1 || 0, "number:min-integer-digits": t[1].length }) + "<number:text>%</number:text>"; break j; }
|
||||
|
||||
/* datetime */
|
||||
var has_time = false;
|
||||
if(["y","m","d"].indexOf(nf[0]) > -1) {
|
||||
type = "date";
|
||||
k: for(; i < nf.length; ++i) switch((c = nf[i].toLowerCase())) {
|
||||
case "h": case "s": has_time = true; --i; break k;
|
||||
case "m":
|
||||
l: for(var h = i+1; h < nf.length; ++h) switch(nf[h]) {
|
||||
case "y": case "d": break l;
|
||||
case "h": case "s": has_time = true; --i; break k;
|
||||
}
|
||||
/* falls through */
|
||||
case "y": case "d":
|
||||
while((nf[++i]||"").toLowerCase() == c[0]) c += c[0]; --i;
|
||||
switch(c) {
|
||||
case "y": case "yy": payload += "<number:year/>"; break;
|
||||
case "yyy": case "yyyy": payload += '<number:year number:style="long"/>'; break;
|
||||
case "mmmmm": console.error("ODS has no equivalent of format |mmmmm|");
|
||||
/* falls through */
|
||||
case "m": case "mm": case "mmm": case "mmmm":
|
||||
payload += '<number:month number:style="' + (c.length % 2 ? "short" : "long") + '" number:textual="' + (c.length >= 3 ? "true" : "false") + '"/>';
|
||||
break;
|
||||
case "d": case "dd": payload += '<number:day number:style="' + (c.length % 2 ? "short" : "long") + '"/>'; break;
|
||||
case "ddd": case "dddd": payload += '<number:day-of-week number:style="' + (c.length % 2 ? "short" : "long") + '"/>'; break;
|
||||
}
|
||||
break;
|
||||
case '"':
|
||||
while(nf[++i] != '"' || nf[++i] == '"') c += nf[i]; --i;
|
||||
payload += '<number:text>' + escapexml(c.slice(1).replace(/""/g, '"')) + '</number:text>';
|
||||
break;
|
||||
case '\\': c = nf[++i];
|
||||
payload += '<number:text>' + escapexml(c) + '</number:text>'; break;
|
||||
case '/': case ':': payload += '<number:text>' + escapexml(c) + '</number:text>'; break;
|
||||
default: console.error("unrecognized character " + c + " in ODF format " + nf);
|
||||
}
|
||||
if(!has_time) break j;
|
||||
nf = nf.slice(i+1); i = 0;
|
||||
}
|
||||
if(nf.match(/^\[?[hms]/)) {
|
||||
if(type == "number") type = "time";
|
||||
if(nf.match(/\[/)) {
|
||||
nf = nf.replace(/[\[\]]/g, "");
|
||||
nopts['number:truncate-on-overflow'] = "false";
|
||||
}
|
||||
for(; i < nf.length; ++i) switch((c = nf[i].toLowerCase())) {
|
||||
case "h": case "m": case "s":
|
||||
while((nf[++i]||"").toLowerCase() == c[0]) c += c[0]; --i;
|
||||
switch(c) {
|
||||
case "h": case "hh": payload += '<number:hours number:style="' + (c.length % 2 ? "short" : "long") + '"/>'; break;
|
||||
case "m": case "mm": payload += '<number:minutes number:style="' + (c.length % 2 ? "short" : "long") + '"/>'; break;
|
||||
case "s": case "ss":
|
||||
if(nf[i+1] == ".") do { c += nf[i+1]; ++i; } while(nf[i+1] == "0");
|
||||
payload += '<number:seconds number:style="' + (c.match("ss") ? "long" : "short") + '"' + (c.match(/\./) ? ' number:decimal-places="' + (c.match(/0+/)||[""])[0].length + '"' : "")+ '/>'; break;
|
||||
}
|
||||
break;
|
||||
case '"':
|
||||
while(nf[++i] != '"' || nf[++i] == '"') c += nf[i]; --i;
|
||||
payload += '<number:text>' + escapexml(c.slice(1).replace(/""/g, '"')) + '</number:text>';
|
||||
break;
|
||||
case '/': case ':': payload += '<number:text>' + escapexml(c) + '</number:text>'; break;
|
||||
case "a":
|
||||
if(nf.slice(i, i+3).toLowerCase() == "a/p") { payload += '<number:am-pm/>'; i += 2; break; } // Note: ODF does not support A/P
|
||||
if(nf.slice(i, i+5).toLowerCase() == "am/pm") { payload += '<number:am-pm/>'; i += 4; break; }
|
||||
/* falls through */
|
||||
default: console.error("unrecognized character " + c + " in ODF format " + nf);
|
||||
}
|
||||
break j;
|
||||
}
|
||||
|
||||
/* currency flag */
|
||||
if(nf.indexOf(/\$/) > -1) { type = "currency"; }
|
||||
|
||||
/* should be in a char loop */
|
||||
if(nf[0] == "$") { payload += '<number:currency-symbol number:language="en" number:country="US">$</number:currency-symbol>'; nf = nf.slice(1); i = 0; }
|
||||
i = 0; if(nf[i] == '"') {
|
||||
while(nf[++i] != '"' || nf[++i] == '"') c += nf[i]; --i;
|
||||
if(nf[i+1] == "*") {
|
||||
i++;
|
||||
payload += '<number:fill-character>' + escapexml(c.replace(/""/g, '"')) + '</number:fill-character>';
|
||||
} else {
|
||||
payload += '<number:text>' + escapexml(c.replace(/""/g, '"')) + '</number:text>';
|
||||
}
|
||||
nf = nf.slice(i+1); i = 0;
|
||||
}
|
||||
|
||||
/* number TODO: interstitial text e.g. 000)000-0000 */
|
||||
var np = nf.match(/([#0][0#,]*)(\.[0#]*|)(E[+]?0*|)/i);
|
||||
if(!np || !np[0]) console.error("Could not find numeric part of " + nf);
|
||||
else {
|
||||
var base = np[1].replace(/,/g, "");
|
||||
payload += '<number:' + (np[3] ? "scientific-" : "")+ 'number' +
|
||||
' number:min-integer-digits="' + (base.indexOf("0") == -1 ? "0" : base.length - base.indexOf("0")) + '"' +
|
||||
(np[0].indexOf(",") > -1 ? ' number:grouping="true"' : "") +
|
||||
(np[2] && ' number:decimal-places="' + (np[2].length - 1) + '"' || ' number:decimal-places="0"') +
|
||||
(np[3] && np[3].indexOf("+") > -1 ? ' number:forced-exponent-sign="true"' : "" ) +
|
||||
(np[3] ? ' number:min-exponent-digits="' + np[3].match(/0+/)[0].length + '"' : "" ) +
|
||||
'>' +
|
||||
/* TODO: interstitial text placeholders */
|
||||
'</number:' + (np[3] ? "scientific-" : "") + 'number>';
|
||||
i = np.index + np[0].length;
|
||||
}
|
||||
|
||||
/* residual text */
|
||||
if(nf[i] == '"') {
|
||||
c = "";
|
||||
while(nf[++i] != '"' || nf[++i] == '"') c += nf[i]; --i;
|
||||
payload += '<number:text>' + escapexml(c.replace(/""/g, '"')) + '</number:text>';
|
||||
}
|
||||
}
|
||||
|
||||
if(!payload) { console.error("Could not generate ODS number format for |" + nf + "|"); return ""; }
|
||||
return writextag("number:" + type + "-style", payload, nopts);
|
||||
}
|
||||
|
||||
function write_names_ods(Names, SheetNames, idx) {
|
||||
var scoped = Names.filter(function(name) { return name.Sheet == (idx == -1 ? null : idx); });
|
||||
//var scoped = Names.filter(function(name) { return name.Sheet == (idx == -1 ? null : idx); });
|
||||
var scoped = []; for(var namei = 0; namei < Names.length; ++namei) {
|
||||
var name = Names[namei];
|
||||
if(!name) continue;
|
||||
if(name.Sheet == (idx == -1 ? null : idx)) scoped.push(name);
|
||||
}
|
||||
if(!scoped.length) return "";
|
||||
return " <table:named-expressions>\n" + scoped.map(function(name) {
|
||||
var odsref = csf_to_ods_3D(name.Ref);
|
||||
var odsref = (idx == -1 ? "$" : "") + csf_to_ods_3D(name.Ref);
|
||||
return " " + writextag("table:named-range", null, {
|
||||
"table:name": name.Name,
|
||||
"table:cell-range-address": odsref,
|
||||
@ -53,14 +211,13 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = /* @__PURE__ */(function
|
||||
};
|
||||
|
||||
var null_cell_xml = ' <table:table-cell />\n';
|
||||
var covered_cell_xml = ' <table:covered-table-cell/>\n';
|
||||
var write_ws = function(ws, wb/*:Workbook*/, i/*:number*//*::, opts*/)/*:string*/ {
|
||||
var write_ws = function(ws, wb/*:Workbook*/, i/*:number*/, opts, nfs, date1904)/*:string*/ {
|
||||
/* Section 9 Tables */
|
||||
var o/*:Array<string>*/ = [];
|
||||
o.push(' <table:table table:name="' + escapexml(wb.SheetNames[i]) + '" table:style-name="ta1">\n');
|
||||
var R=0,C=0, range = decode_range(ws['!ref']||"A1");
|
||||
var marr/*:Array<Range>*/ = ws['!merges'] || [], mi = 0;
|
||||
var dense = Array.isArray(ws);
|
||||
var dense = ws["!data"] != null;
|
||||
if(ws["!cols"]) {
|
||||
for(C = 0; C <= range.e.c; ++C) o.push(' <table:table-column' + (ws["!cols"][C] ? ' table:style-name="co' + ws["!cols"][C].ods + '"' : '') + '></table:table-column>\n');
|
||||
}
|
||||
@ -85,8 +242,8 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = /* @__PURE__ */(function
|
||||
ct['table:number-rows-spanned'] = (marr[mi].e.r - marr[mi].s.r + 1);
|
||||
break;
|
||||
}
|
||||
if(skip) { o.push(covered_cell_xml); continue; }
|
||||
var ref = encode_cell({r:R, c:C}), cell = dense ? (ws[R]||[])[C]: ws[ref];
|
||||
if(skip) { o.push(' <table:covered-table-cell/>\n'); continue; }
|
||||
var ref = encode_cell({r:R, c:C}), cell = dense ? (ws["!data"][R]||[])[C]: ws[ref];
|
||||
if(cell && cell.f) {
|
||||
ct['table:formula'] = escapexml(csf_to_ods_formula(cell.f));
|
||||
if(cell.F) {
|
||||
@ -114,13 +271,13 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = /* @__PURE__ */(function
|
||||
ct['office:value-type'] = "string";
|
||||
break;
|
||||
case 'd':
|
||||
textp = (cell.w||(parseDate(cell.v).toISOString()));
|
||||
textp = (cell.w||(parseDate(cell.v, date1904).toISOString()));
|
||||
ct['office:value-type'] = "date";
|
||||
ct['office:date-value'] = (parseDate(cell.v).toISOString());
|
||||
ct['office:date-value'] = (parseDate(cell.v, date1904).toISOString());
|
||||
ct['table:style-name'] = "ce1";
|
||||
break;
|
||||
//case 'e':
|
||||
default: o.push(null_cell_xml); continue;
|
||||
//case 'e': // TODO: translate to ODS errors
|
||||
default: o.push(null_cell_xml); continue; // TODO: empty cell with comments
|
||||
}
|
||||
var text_p = write_text_p(textp);
|
||||
if(cell.l && cell.l.Target) {
|
||||
@ -130,7 +287,18 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = /* @__PURE__ */(function
|
||||
if(_tgt.charAt(0) != "#" && !_tgt.match(/^\w+:/)) _tgt = '../' + _tgt;
|
||||
text_p = writextag('text:a', text_p, {'xlink:href': _tgt.replace(/&/g, "&")});
|
||||
}
|
||||
o.push(' ' + writextag('table:table-cell', writextag('text:p', text_p, {}), ct) + '\n');
|
||||
if(nfs[cell.z]) ct["table:style-name"] = "ce" + nfs[cell.z].slice(1);
|
||||
var payload = writextag('text:p', text_p, {});
|
||||
if(cell.c) {
|
||||
var acreator = "", apayload = "", aprops = {};
|
||||
for(var ci = 0; ci < cell.c.length; ++ci) {
|
||||
if(!acreator && cell.c[ci].a) acreator = cell.c[ci].a;
|
||||
apayload += "<text:p>" + write_text_p(cell.c[ci].t) + "</text:p>";
|
||||
}
|
||||
if(!cell.c.hidden) aprops["office:display"] = true;
|
||||
payload = writextag('office:annotation', apayload, aprops) + payload;
|
||||
}
|
||||
o.push(' ' + writextag('table:table-cell', payload, ct) + '\n');
|
||||
}
|
||||
o.push(' </table:table-row>\n');
|
||||
}
|
||||
@ -142,14 +310,6 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = /* @__PURE__ */(function
|
||||
var write_automatic_styles_ods = function(o/*:Array<string>*/, wb) {
|
||||
o.push(' <office:automatic-styles>\n');
|
||||
|
||||
o.push(' <number:date-style style:name="N37" number:automatic-order="true">\n');
|
||||
o.push(' <number:month number:style="long"/>\n');
|
||||
o.push(' <number:text>/</number:text>\n');
|
||||
o.push(' <number:day number:style="long"/>\n');
|
||||
o.push(' <number:text>/</number:text>\n');
|
||||
o.push(' <number:year/>\n');
|
||||
o.push(' </number:date-style>\n');
|
||||
|
||||
/* column styles */
|
||||
var cidx = 0;
|
||||
wb.SheetNames.map(function(n) { return wb.Sheets[n]; }).forEach(function(ws) {
|
||||
@ -190,12 +350,40 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = /* @__PURE__ */(function
|
||||
o.push(' <style:table-properties table:display="true" style:writing-mode="lr-tb"/>\n');
|
||||
o.push(' </style:style>\n');
|
||||
|
||||
/* table cells, text */
|
||||
o.push(' <number:date-style style:name="N37" number:automatic-order="true">\n');
|
||||
o.push(' <number:month number:style="long"/>\n');
|
||||
o.push(' <number:text>/</number:text>\n');
|
||||
o.push(' <number:day number:style="long"/>\n');
|
||||
o.push(' <number:text>/</number:text>\n');
|
||||
o.push(' <number:year/>\n');
|
||||
o.push(' </number:date-style>\n');
|
||||
|
||||
/* number formats, table cells, text */
|
||||
var nfs = {};
|
||||
var nfi = 69;
|
||||
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})];
|
||||
if(!c || !c.z || c.z.toLowerCase() == "general") continue;
|
||||
if(!nfs[c.z]) {
|
||||
var out = write_number_format_ods(c.z, "N" + nfi);
|
||||
if(out) { nfs[c.z] = "N" + nfi; ++nfi; o.push(out + "\n"); }
|
||||
}
|
||||
}
|
||||
});
|
||||
o.push(' <style:style style:name="ce1" style:family="table-cell" style:parent-style-name="Default" style:data-style-name="N37"/>\n');
|
||||
keys(nfs).forEach(function(nf) {
|
||||
o.push('<style:style style:name="ce' + nfs[nf].slice(1) + '" style:family="table-cell" style:parent-style-name="Default" style:data-style-name="' + nfs[nf] + '"/>\n');
|
||||
});
|
||||
|
||||
/* page-layout */
|
||||
|
||||
o.push(' </office:automatic-styles>\n');
|
||||
return nfs;
|
||||
};
|
||||
|
||||
return function wcx(wb, opts) {
|
||||
@ -248,14 +436,15 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = /* @__PURE__ */(function
|
||||
|
||||
if(opts.bookType == "fods") {
|
||||
o.push('<office:document' + attr + fods + '>\n');
|
||||
o.push(write_meta_ods().replace(/office:document-meta/g, "office:meta"));
|
||||
o.push(write_meta_ods().replace(/<office:document-meta.*?>/, "").replace(/<\/office:document-meta>/, "") + "\n");
|
||||
// TODO: settings (equiv of settings.xml for ODS)
|
||||
} else o.push('<office:document-content' + attr + '>\n');
|
||||
// o.push(' <office:scripts/>\n');
|
||||
write_automatic_styles_ods(o, wb);
|
||||
var nfs = write_automatic_styles_ods(o, wb);
|
||||
o.push(' <office:body>\n');
|
||||
o.push(' <office:spreadsheet>\n');
|
||||
for(var i = 0; i != wb.SheetNames.length; ++i) o.push(write_ws(wb.Sheets[wb.SheetNames[i]], wb, i, opts));
|
||||
if(((wb.Workbook||{}).WBProps||{}).date1904) o.push(' <table:calculation-settings table:case-sensitive="false" table:search-criteria-must-apply-to-whole-cell="true" table:use-wildcards="true" table:use-regular-expressions="false" table:automatic-find-labels="false">\n <table:null-date table:date-value="1904-01-01"/>\n </table:calculation-settings>\n');
|
||||
for(var i = 0; i != wb.SheetNames.length; ++i) o.push(write_ws(wb.Sheets[wb.SheetNames[i]], wb, i, opts, nfs, ((wb.Workbook||{}).WBProps||{}).date1904));
|
||||
if((wb.Workbook||{}).Names) o.push(write_names_ods(wb.Workbook.Names, wb.SheetNames, -1));
|
||||
o.push(' </office:spreadsheet>\n');
|
||||
o.push(' </office:body>\n');
|
||||
|
2030
bits/83_numbers.js
2030
bits/83_numbers.js
File diff suppressed because it is too large
Load Diff
@ -13,6 +13,13 @@ function safe_parse_wbrels(wbrels, sheets) {
|
||||
return !wbrels || wbrels.length === 0 ? null : wbrels;
|
||||
}
|
||||
|
||||
function parse_sheet_legacy_drawing(sheet, type, zip, path, idx, opts, wb, comments) {
|
||||
if(!sheet || !sheet['!legdrawel']) return;
|
||||
var dfile = resolve_path(sheet['!legdrawel'].Target, path);
|
||||
var draw = getzipstr(zip, dfile, true);
|
||||
if(draw) parse_vml(utf8read(draw), sheet, comments||[]);
|
||||
}
|
||||
|
||||
function safe_parse_sheet(zip, path/*:string*/, relsPath/*:string*/, sheet, idx/*:number*/, sheetRels, sheets, stype/*:string*/, opts, wb, themes, styles) {
|
||||
try {
|
||||
sheetRels[sheet]=parse_rels(getzipstr(zip, relsPath, true), path);
|
||||
@ -36,12 +43,12 @@ function safe_parse_sheet(zip, path/*:string*/, relsPath/*:string*/, sheet, idx/
|
||||
sheets[sheet] = _ws;
|
||||
|
||||
/* scan rels for comments and threaded comments */
|
||||
var tcomments = [];
|
||||
var comments = [], tcomments = [];
|
||||
if(sheetRels && sheetRels[sheet]) keys(sheetRels[sheet]).forEach(function(n) {
|
||||
var dfile = "";
|
||||
if(sheetRels[sheet][n].Type == RELS.CMNT) {
|
||||
dfile = resolve_path(sheetRels[sheet][n].Target, path);
|
||||
var comments = parse_cmnt(getzipdata(zip, dfile, true), dfile, opts);
|
||||
comments = parse_cmnt(getzipdata(zip, dfile, true), dfile, opts);
|
||||
if(!comments || !comments.length) return;
|
||||
sheet_insert_comments(_ws, comments, false);
|
||||
}
|
||||
@ -51,6 +58,7 @@ function safe_parse_sheet(zip, path/*:string*/, relsPath/*:string*/, sheet, idx/
|
||||
}
|
||||
});
|
||||
if(tcomments && tcomments.length) sheet_insert_comments(_ws, tcomments, true, opts.people || []);
|
||||
parse_sheet_legacy_drawing(_ws, stype, zip, path, idx, opts, wb, comments);
|
||||
} catch(e) { if(opts.WTF) throw e; }
|
||||
}
|
||||
|
||||
@ -69,16 +77,25 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
if(safegetzipfile(zip, 'Index/Document.iwa')) {
|
||||
if(typeof Uint8Array == "undefined") throw new Error('NUMBERS file parsing requires Uint8Array support');
|
||||
if(typeof parse_numbers_iwa != "undefined") {
|
||||
if(zip.FileIndex) return parse_numbers_iwa(zip);
|
||||
if(zip.FileIndex) return parse_numbers_iwa(zip, opts);
|
||||
var _zip = CFB.utils.cfb_new();
|
||||
zipentries(zip).forEach(function(e) { zip_add_file(_zip, e, getzipbin(zip, e)); });
|
||||
return parse_numbers_iwa(_zip);
|
||||
return parse_numbers_iwa(_zip, opts);
|
||||
}
|
||||
throw new Error('Unsupported NUMBERS file');
|
||||
}
|
||||
if(!safegetzipfile(zip, '[Content_Types].xml')) {
|
||||
if(safegetzipfile(zip, 'index.xml.gz')) throw new Error('Unsupported NUMBERS 08 file');
|
||||
if(safegetzipfile(zip, 'index.xml')) throw new Error('Unsupported NUMBERS 09 file');
|
||||
var index_zip = CFB.find(zip, 'Index.zip');
|
||||
if(index_zip) {
|
||||
opts = dup(opts);
|
||||
delete opts.type;
|
||||
if(typeof index_zip.content == "string") opts.type = "binary";
|
||||
// TODO: Bun buffer bug
|
||||
if(typeof Bun !== "undefined" && Buffer.isBuffer(index_zip.content)) return readSync(new Uint8Array(index_zip.content), opts);
|
||||
return readSync(index_zip.content, opts);
|
||||
}
|
||||
throw new Error('Unsupported ZIP file');
|
||||
}
|
||||
|
||||
@ -240,6 +257,8 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
if(dir.vba.length > 0) out.vbaraw = getzipdata(zip,strip_front_slash(dir.vba[0]),true);
|
||||
else if(dir.defaults && dir.defaults.bin === CT_VBA) out.vbaraw = getzipdata(zip, 'xl/vbaProject.bin',true);
|
||||
}
|
||||
// TODO: pass back content types metdata for xlsm/xlsx resolution
|
||||
out.bookType = xlsb ? "xlsb" : "xlsx";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,9 @@ function write_zip_xlsb(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
|
||||
add_rels(opts.rels, 4, f, RELS.CUST_PROPS);
|
||||
}
|
||||
|
||||
var people = ["SheetJ5"];
|
||||
opts.tcid = 0;
|
||||
|
||||
for(rId=1;rId <= wb.SheetNames.length; ++rId) {
|
||||
var wsrels = {'!id':{}};
|
||||
var ws = wb.Sheets[wb.SheetNames[rId-1]];
|
||||
@ -70,6 +73,17 @@ function write_zip_xlsb(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
|
||||
var need_vml = false;
|
||||
var cf = "";
|
||||
if(comments && comments.length > 0) {
|
||||
var needtc = false;
|
||||
comments.forEach(function(carr) {
|
||||
carr[1].forEach(function(c) { if(c.T == true) needtc = true; });
|
||||
});
|
||||
if(needtc) {
|
||||
cf = "xl/threadedComments/threadedComment" + rId + ".xml";
|
||||
zip_add_file(zip, cf, write_tcmnt_xml(comments, people, opts));
|
||||
ct.threadedcomments.push(cf);
|
||||
add_rels(wsrels, -1, "../threadedComments/threadedComment" + rId + ".xml", RELS.TCMNT);
|
||||
}
|
||||
|
||||
cf = "xl/comments" + rId + "." + wbext;
|
||||
zip_add_file(zip, cf, write_comments_bin(comments, opts));
|
||||
ct.comments.push(cf);
|
||||
@ -101,7 +115,8 @@ function write_zip_xlsb(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
|
||||
/* TODO: something more intelligent with themes */
|
||||
|
||||
f = "xl/theme/theme1.xml";
|
||||
zip_add_file(zip, f, write_theme(wb.Themes, opts));
|
||||
var ww = write_theme(wb.Themes, opts);
|
||||
zip_add_file(zip, f, ww);
|
||||
ct.themes.push(f);
|
||||
add_rels(opts.wbrels, -1, "theme/theme1.xml", RELS.THEME);
|
||||
|
||||
@ -124,6 +139,13 @@ function write_zip_xlsb(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
|
||||
ct.metadata.push(f);
|
||||
add_rels(opts.wbrels, -1, "metadata." + wbext, RELS.XLMETA);
|
||||
|
||||
if(people.length > 1) {
|
||||
f = "xl/persons/person.xml";
|
||||
zip_add_file(zip, f, write_people_xml(people, opts));
|
||||
ct.people.push(f);
|
||||
add_rels(opts.wbrels, -1, "persons/person.xml", RELS.PEOPLE);
|
||||
}
|
||||
|
||||
zip_add_file(zip, "[Content_Types].xml", write_ct(ct, opts));
|
||||
zip_add_file(zip, '_rels/.rels', write_rels(opts.rels));
|
||||
zip_add_file(zip, 'xl/_rels/workbook.' + wbext + '.rels', write_rels(opts.wbrels));
|
||||
|
@ -50,7 +50,13 @@ function read_plaintext_raw(data/*:RawData*/, o/*:ParseOpts*/)/*:Workbook*/ {
|
||||
function read_utf16(data/*:RawData*/, o/*:ParseOpts*/)/*:Workbook*/ {
|
||||
var d = data;
|
||||
if(o.type == 'base64') d = Base64_decode(d);
|
||||
d = $cptable.utils.decode(1200, d.slice(2), 'str');
|
||||
if(typeof ArrayBuffer !== "undefined" && data instanceof ArrayBuffer) d = new Uint8Array(data);
|
||||
d = typeof $cptable !== "undefined" ? $cptable.utils.decode(1200, d.slice(2), 'str') : (
|
||||
(has_buf && Buffer.isBuffer(data)) ? data.slice(2).toString("utf16le") :
|
||||
(typeof Uint8Array !== "undefined" && d instanceof Uint8Array) ? (
|
||||
typeof TextDecoder !== "undefined" ? new TextDecoder("utf-16le").decode(d.slice(2)) : utf16lereadu(d.slice(2))
|
||||
) : utf16leread(d.slice(2))
|
||||
);
|
||||
o.type = "binary";
|
||||
return read_plaintext(d, o);
|
||||
}
|
||||
@ -67,6 +73,7 @@ function read_prn(data, d, o, str) {
|
||||
function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
reset_cp();
|
||||
var o = opts||{};
|
||||
if(o.codepage && typeof $cptable === "undefined") console.error("Codepage tables are not loaded. Non-ASCII characters may not give expected results");
|
||||
if(typeof ArrayBuffer !== 'undefined' && data instanceof ArrayBuffer) return readSync(new Uint8Array(data), (o = dup(o), o.type = "array", o));
|
||||
if(typeof Uint8Array !== 'undefined' && data instanceof Uint8Array && !o.type) o.type = typeof Deno !== "undefined" ? "buffer" : "array";
|
||||
var d = data, n = [0,0,0,0], str = false;
|
||||
@ -104,7 +111,7 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
}
|
||||
break;
|
||||
case 0x03: case 0x83: case 0x8B: case 0x8C: return DBF.to_workbook(d, o);
|
||||
case 0x7B: if(n[1] === 0x5C && n[2] === 0x72 && n[3] === 0x74) return RTF.to_workbook(d, o); break;
|
||||
case 0x7B: if(n[1] === 0x5C && n[2] === 0x72 && n[3] === 0x74) return rtf_to_workbook(d, o); break;
|
||||
case 0x0A: case 0x0D: case 0x20: return read_plaintext_raw(d, o);
|
||||
case 0x89: if(n[1] === 0x50 && n[2] === 0x4E && n[3] === 0x47) throw new Error("PNG Image File is not a spreadsheet"); break;
|
||||
case 0x08: if(n[1] === 0xE7) throw new Error("Unsupported Multiplan 1.x file!"); break;
|
||||
|
@ -81,7 +81,7 @@ function write_string_type(out/*:string*/, opts/*:WriteOpts*/, bom/*:?string*/)/
|
||||
|
||||
function write_stxt_type(out/*:string*/, opts/*:WriteOpts*/)/*:any*/ {
|
||||
switch(opts.type) {
|
||||
case "base64": return Base64_encode(out);
|
||||
case "base64": return Base64_encode_pass(out);
|
||||
case "binary": return out;
|
||||
case "string": return out; /* override in sheet_to_txt */
|
||||
case "file": return write_dl(opts.file, out, 'binary');
|
||||
@ -134,7 +134,7 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
|
||||
case 'xml':
|
||||
case 'xlml': return write_string_type(write_xlml(wb, o), o);
|
||||
case 'slk':
|
||||
case 'sylk': return write_string_type(SYLK.from_sheet(wb.Sheets[wb.SheetNames[idx]], o), o);
|
||||
case 'sylk': return write_string_type(SYLK.from_sheet(wb.Sheets[wb.SheetNames[idx]], o, wb), o);
|
||||
case 'htm':
|
||||
case 'html': return write_string_type(sheet_to_html(wb.Sheets[wb.SheetNames[idx]], o), o);
|
||||
case 'txt': return write_stxt_type(sheet_to_txt(wb.Sheets[wb.SheetNames[idx]], o), o);
|
||||
@ -142,7 +142,7 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
|
||||
case 'dif': return write_string_type(DIF.from_sheet(wb.Sheets[wb.SheetNames[idx]], o), o);
|
||||
case 'dbf': return write_binary_type(DBF.from_sheet(wb.Sheets[wb.SheetNames[idx]], o), o);
|
||||
case 'prn': return write_string_type(PRN.from_sheet(wb.Sheets[wb.SheetNames[idx]], o), o);
|
||||
case 'rtf': return write_string_type(RTF.from_sheet(wb.Sheets[wb.SheetNames[idx]], o), o);
|
||||
case 'rtf': return write_string_type(sheet_to_rtf(wb.Sheets[wb.SheetNames[idx]], o), o);
|
||||
case 'eth': return write_string_type(ETH.from_sheet(wb.Sheets[wb.SheetNames[idx]], o), o);
|
||||
case 'fods': return write_string_type(write_ods(wb, o), o);
|
||||
case 'wk1': return write_binary_type(WK_.sheet_to_wk1(wb.Sheets[wb.SheetNames[idx]], o), o);
|
||||
|
@ -4,18 +4,18 @@ type MJRObject = {
|
||||
isempty: boolean;
|
||||
};
|
||||
*/
|
||||
function make_json_row(sheet/*:Worksheet*/, r/*:Range*/, R/*:number*/, cols/*:Array<string>*/, header/*:number*/, hdr/*:Array<any>*/, dense/*:boolean*/, o/*:Sheet2JSONOpts*/)/*:MJRObject*/ {
|
||||
function make_json_row(sheet/*:Worksheet*/, r/*:Range*/, R/*:number*/, cols/*:Array<string>*/, header/*:number*/, hdr/*:Array<any>*/, o/*:Sheet2JSONOpts*/)/*:MJRObject*/ {
|
||||
var rr = encode_row(R);
|
||||
var defval = o.defval, raw = o.raw || !Object.prototype.hasOwnProperty.call(o, "raw");
|
||||
var isempty = true;
|
||||
var isempty = true, dense = (sheet["!data"] != null);
|
||||
var row/*:any*/ = (header === 1) ? [] : {};
|
||||
if(header !== 1) {
|
||||
if(Object.defineProperty) try { Object.defineProperty(row, '__rowNum__', {value:R, enumerable:false}); } catch(e) { row.__rowNum__ = R; }
|
||||
else row.__rowNum__ = R;
|
||||
}
|
||||
if(!dense || sheet[R]) for (var C = r.s.c; C <= r.e.c; ++C) {
|
||||
var val = dense ? sheet[R][C] : sheet[cols[C] + rr];
|
||||
if(val === undefined || val.t === undefined) {
|
||||
if(!dense || sheet["!data"][R]) for (var C = r.s.c; C <= r.e.c; ++C) {
|
||||
var val = dense ? (sheet["!data"][R]||[])[C] : sheet[cols[C] + rr];
|
||||
if(val == null || val.t === undefined) {
|
||||
if(defval === undefined) continue;
|
||||
if(hdr[C] != null) { row[hdr[C]] = defval; }
|
||||
continue;
|
||||
@ -24,7 +24,12 @@ function make_json_row(sheet/*:Worksheet*/, r/*:Range*/, R/*:number*/, cols/*:Ar
|
||||
switch(val.t){
|
||||
case 'z': if(v == null) break; continue;
|
||||
case 'e': v = (v == 0 ? null : void 0); break;
|
||||
case 's': case 'd': case 'b': case 'n': break;
|
||||
case 's': case 'b':
|
||||
case 'n': if(!val.z || !fmt_is_date(val.z)) break;
|
||||
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||(o.raw === false)))) v = utc_to_local(new Date(v)); break;
|
||||
default: throw new Error('unrecognized type ' + val.t);
|
||||
}
|
||||
if(hdr[C] != null) {
|
||||
@ -34,7 +39,7 @@ function make_json_row(sheet/*:Worksheet*/, r/*:Range*/, R/*:number*/, cols/*:Ar
|
||||
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;
|
||||
}
|
||||
@ -63,16 +68,16 @@ function sheet_to_json(sheet/*:Worksheet*/, opts/*:?Sheet2JSONOpts*/) {
|
||||
var cols/*:Array<string>*/ = [];
|
||||
var out/*:Array<any>*/ = [];
|
||||
var outi = 0, counter = 0;
|
||||
var dense = Array.isArray(sheet);
|
||||
var dense = sheet["!data"] != null;
|
||||
var R = r.s.r, C = 0;
|
||||
var header_cnt = {};
|
||||
if(dense && !sheet[R]) sheet[R] = [];
|
||||
if(dense && !sheet["!data"][R]) sheet["!data"][R] = [];
|
||||
var colinfo/*:Array<ColInfo>*/ = o.skipHidden && sheet["!cols"] || [];
|
||||
var rowinfo/*:Array<ColInfo>*/ = o.skipHidden && sheet["!rows"] || [];
|
||||
for(C = r.s.c; C <= r.e.c; ++C) {
|
||||
if(((colinfo[C]||{}).hidden)) continue;
|
||||
cols[C] = encode_col(C);
|
||||
val = dense ? sheet[R][C] : sheet[cols[C] + rr];
|
||||
val = dense ? sheet["!data"][R][C] : sheet[cols[C] + rr];
|
||||
switch(header) {
|
||||
case 1: hdr[C] = C - r.s.c; break;
|
||||
case 2: hdr[C] = cols[C]; break;
|
||||
@ -91,7 +96,7 @@ function sheet_to_json(sheet/*:Worksheet*/, opts/*:?Sheet2JSONOpts*/) {
|
||||
}
|
||||
for (R = r.s.r + offset; R <= r.e.r; ++R) {
|
||||
if ((rowinfo[R]||{}).hidden) continue;
|
||||
var row = make_json_row(sheet, r, R, cols, header, hdr, dense, o);
|
||||
var row = make_json_row(sheet, r, R, cols, header, hdr, o);
|
||||
if((row.isempty === false) || (header === 1 ? o.blankrows !== false : !!o.blankrows)) out[outi++] = row.row;
|
||||
}
|
||||
out.length = outi;
|
||||
@ -102,9 +107,11 @@ var qreg = /"/g;
|
||||
function make_csv_row(sheet/*:Worksheet*/, r/*:Range*/, R/*:number*/, cols/*:Array<string>*/, fs/*:number*/, rs/*:number*/, FS/*:string*/, o/*:Sheet2CSVOpts*/)/*:?string*/ {
|
||||
var isempty = true;
|
||||
var row/*:Array<string>*/ = [], txt = "", rr = encode_row(R);
|
||||
var dense = sheet["!data"] != null;
|
||||
var datarow = dense && sheet["!data"][R] || [];
|
||||
for(var C = r.s.c; C <= r.e.c; ++C) {
|
||||
if (!cols[C]) continue;
|
||||
var val = o.dense ? (sheet[R]||[])[C]: sheet[cols[C] + rr];
|
||||
var val = dense ? datarow[C]: sheet[cols[C] + rr];
|
||||
if(val == null) txt = "";
|
||||
else if(val.v != null) {
|
||||
isempty = false;
|
||||
@ -131,7 +138,6 @@ function sheet_to_csv(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/)/*:string*/ {
|
||||
var RS = o.RS !== undefined ? o.RS : "\n", rs = RS.charCodeAt(0);
|
||||
var endregex = new RegExp((FS=="|" ? "\\|" : FS)+"+$");
|
||||
var row = "", cols/*:Array<string>*/ = [];
|
||||
o.dense = Array.isArray(sheet);
|
||||
var colinfo/*:Array<ColInfo>*/ = o.skipHidden && sheet["!cols"] || [];
|
||||
var rowinfo/*:Array<ColInfo>*/ = o.skipHidden && sheet["!rows"] || [];
|
||||
for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C);
|
||||
@ -143,7 +149,6 @@ function sheet_to_csv(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/)/*:string*/ {
|
||||
if(o.strip) row = row.replace(endregex,"");
|
||||
if(row || (o.blankrows !== false)) out.push((w++ ? RS : "") + row);
|
||||
}
|
||||
delete o.dense;
|
||||
return out.join("");
|
||||
}
|
||||
|
||||
@ -160,13 +165,13 @@ function sheet_to_formulae(sheet/*:Worksheet*/)/*:Array<string>*/ {
|
||||
if(sheet == null || sheet["!ref"] == null) return [];
|
||||
var r = safe_decode_range(sheet['!ref']), rr = "", cols/*:Array<string>*/ = [], C;
|
||||
var cmds/*:Array<string>*/ = [];
|
||||
var dense = Array.isArray(sheet);
|
||||
var dense = sheet["!data"] != null;
|
||||
for(C = r.s.c; C <= r.e.c; ++C) cols[C] = encode_col(C);
|
||||
for(var R = r.s.r; R <= r.e.r; ++R) {
|
||||
rr = encode_row(R);
|
||||
for(C = r.s.c; C <= r.e.c; ++C) {
|
||||
y = cols[C] + rr;
|
||||
x = dense ? (sheet[R]||[])[C] : sheet[y];
|
||||
x = dense ? (sheet["!data"][R]||[])[C] : sheet[y];
|
||||
val = "";
|
||||
if(x === undefined) continue;
|
||||
else if(x.F != null) {
|
||||
@ -191,8 +196,11 @@ function sheet_to_formulae(sheet/*:Worksheet*/)/*:Array<string>*/ {
|
||||
|
||||
function sheet_add_json(_ws/*:?Worksheet*/, js/*:Array<any>*/, opts)/*:Worksheet*/ {
|
||||
var o = opts || {};
|
||||
var dense = _ws ? (_ws["!data"] != null) : o.dense;
|
||||
if(DENSE != null && dense == null) dense = DENSE;
|
||||
var offset = +!o.skipHeader;
|
||||
var ws/*:Worksheet*/ = _ws || ({}/*:any*/);
|
||||
var ws/*:Worksheet*/ = _ws || ({});
|
||||
if(!_ws && dense) ws["!data"] = [];
|
||||
var _R = 0, _C = 0;
|
||||
if(ws && o.origin != null) {
|
||||
if(typeof o.origin == 'number') _R = o.origin;
|
||||
@ -201,7 +209,6 @@ function sheet_add_json(_ws/*:?Worksheet*/, js/*:Array<any>*/, opts)/*:Worksheet
|
||||
_R = _origin.r; _C = _origin.c;
|
||||
}
|
||||
}
|
||||
var cell/*:Cell*/;
|
||||
var range/*:Range*/ = ({s: {c:0, r:0}, e: {c:_C, r:_R + js.length - 1 + offset}}/*:any*/);
|
||||
if(ws['!ref']) {
|
||||
var _range = safe_decode_range(ws['!ref']);
|
||||
@ -212,29 +219,35 @@ function sheet_add_json(_ws/*:?Worksheet*/, js/*:Array<any>*/, opts)/*:Worksheet
|
||||
if(_R == -1) { _R = 0; range.e.r = js.length - 1 + offset; }
|
||||
}
|
||||
var hdr/*:Array<string>*/ = o.header || [], C = 0;
|
||||
|
||||
var ROW = [];
|
||||
js.forEach(function (JS, R/*:number*/) {
|
||||
if(dense && !ws["!data"][_R + R + offset]) ws["!data"][_R + R + offset] = [];
|
||||
if(dense) ROW = ws["!data"][_R + R + offset];
|
||||
keys(JS).forEach(function(k) {
|
||||
if((C=hdr.indexOf(k)) == -1) hdr[C=hdr.length] = k;
|
||||
var v = JS[k];
|
||||
var t = 'z';
|
||||
var z = "";
|
||||
var ref = encode_cell({c:_C + C,r:_R + R + offset});
|
||||
cell = ws_get_cell_stub(ws, ref);
|
||||
var ref = dense ? "" : (encode_col(_C + C) + encode_row(_R + R + offset));
|
||||
var cell/*:Cell*/ = dense ? ROW[_C + C] : ws[ref];
|
||||
if(v && typeof v === 'object' && !(v instanceof Date)){
|
||||
ws[ref] = v;
|
||||
if(dense) ROW[_C + C] = v;
|
||||
else ws[ref] = v;
|
||||
} else {
|
||||
if(typeof v == 'number') t = 'n';
|
||||
else if(typeof v == 'boolean') t = 'b';
|
||||
else if(typeof v == 'string') t = 's';
|
||||
else if(v instanceof Date) {
|
||||
t = 'd';
|
||||
if(!o.UTC) v = local_to_utc(v);
|
||||
if(!o.cellDates) { t = 'n'; v = datenum(v); }
|
||||
z = (cell.z && fmt_is_date(cell.z)) ? cell.z : (o.dateNF || table_fmt[14]);
|
||||
z = (cell != null && cell.z && fmt_is_date(cell.z)) ? cell.z : (o.dateNF || table_fmt[14]);
|
||||
}
|
||||
else if(v === null && o.nullError) { t = 'e'; v = 0; }
|
||||
if(!cell) ws[ref] = cell = ({t:t, v:v}/*:any*/);
|
||||
else {
|
||||
if(!cell) {
|
||||
if(!dense) ws[ref] = cell = ({t:t, v:v}/*:any*/);
|
||||
else ROW[_C + C] = cell = ({t:t, v:v}/*:any*/);
|
||||
} else {
|
||||
cell.t = t; cell.v = v;
|
||||
delete cell.w; delete cell.R;
|
||||
if(z) cell.z = z;
|
||||
@ -245,7 +258,11 @@ function sheet_add_json(_ws/*:?Worksheet*/, js/*:Array<any>*/, opts)/*:Worksheet
|
||||
});
|
||||
range.e.c = Math.max(range.e.c, _C + hdr.length - 1);
|
||||
var __R = encode_row(_R);
|
||||
if(offset) for(C = 0; C < hdr.length; ++C) ws[encode_col(C + _C) + __R] = {t:'s', v:hdr[C]};
|
||||
if(dense && !ws["!data"][_R]) ws["!data"][_R] = [];
|
||||
if(offset) for(C = 0; C < hdr.length; ++C) {
|
||||
if(dense) ws["!data"][_R][C + _C] = {t:'s', v:hdr[C]};
|
||||
else ws[encode_col(C + _C) + __R] = {t:'s', v:hdr[C]};
|
||||
}
|
||||
ws['!ref'] = encode_range(range);
|
||||
return ws;
|
||||
}
|
||||
@ -255,18 +272,17 @@ function json_to_sheet(js/*:Array<any>*/, opts)/*:Worksheet*/ { return sheet_add
|
||||
function ws_get_cell_stub(ws/*:Worksheet*/, R, C/*:?number*/)/*:Cell*/ {
|
||||
/* A1 cell address */
|
||||
if(typeof R == "string") {
|
||||
/* dense */
|
||||
if(Array.isArray(ws)) {
|
||||
if(ws["!data"] != null) {
|
||||
var RC = decode_cell(R);
|
||||
if(!ws[RC.r]) ws[RC.r] = [];
|
||||
return ws[RC.r][RC.c] || (ws[RC.r][RC.c] = {t:'z'});
|
||||
if(!ws["!data"][RC.r]) ws["!data"][RC.r] = [];
|
||||
return ws["!data"][RC.r][RC.c] || (ws["!data"][RC.r][RC.c] = {t:'z'});
|
||||
}
|
||||
return ws[R] || (ws[R] = {t:'z'});
|
||||
}
|
||||
/* cell address object */
|
||||
if(typeof R != "number") return ws_get_cell_stub(ws, encode_cell(R));
|
||||
/* R and C are 0-based indices */
|
||||
return ws_get_cell_stub(ws, encode_cell({r:R,c:C||0}));
|
||||
return ws_get_cell_stub(ws, encode_col(C||0) + encode_row(R));
|
||||
}
|
||||
|
||||
/* find sheet index for given name / validate index */
|
||||
@ -281,9 +297,11 @@ function wb_sheet_idx(wb/*:Workbook*/, sh/*:number|string*/) {
|
||||
} else throw new Error("Cannot find sheet |" + sh + "|");
|
||||
}
|
||||
|
||||
/* simple blank workbook object */
|
||||
function book_new()/*:Workbook*/ {
|
||||
return { SheetNames: [], Sheets: {} };
|
||||
/* simple blank or single-sheet workbook object */
|
||||
function book_new(ws/*:?Worksheet*/, wsname/*:?string*/)/*:Workbook*/ {
|
||||
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 */
|
||||
|
@ -9,6 +9,7 @@ var utils/*:any*/ = {
|
||||
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,
|
||||
|
@ -10,7 +10,6 @@ function write_csv_stream(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
|
||||
var RS = o.RS !== undefined ? o.RS : "\n", rs = RS.charCodeAt(0);
|
||||
var endregex = new RegExp((FS=="|" ? "\\|" : FS)+"+$");
|
||||
var row/*:?string*/ = "", cols/*:Array<string>*/ = [];
|
||||
o.dense = Array.isArray(sheet);
|
||||
var colinfo/*:Array<ColInfo>*/ = o.skipHidden && sheet["!cols"] || [];
|
||||
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);
|
||||
@ -40,7 +39,6 @@ function write_html_stream(ws/*:Worksheet*/, opts/*:?Sheet2HTMLOpts*/) {
|
||||
var footer = o.footer != null ? o.footer : HTML_END;
|
||||
stream.push(header);
|
||||
var r = decode_range(ws['!ref']);
|
||||
o.dense = Array.isArray(ws);
|
||||
stream.push(make_html_preamble(ws, r, o));
|
||||
var R = r.s.r;
|
||||
var end = false;
|
||||
@ -78,16 +76,16 @@ function write_json_stream(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
|
||||
var rr = encode_row(r.s.r);
|
||||
var cols/*:Array<string>*/ = [];
|
||||
var counter = 0;
|
||||
var dense = Array.isArray(sheet);
|
||||
var dense = sheet["!data"] != null;
|
||||
var R = r.s.r, C = 0;
|
||||
var header_cnt = {};
|
||||
if(dense && !sheet[R]) sheet[R] = [];
|
||||
if(dense && !sheet["!data"][R]) sheet["!data"][R] = [];
|
||||
var colinfo/*:Array<ColInfo>*/ = o.skipHidden && sheet["!cols"] || [];
|
||||
var rowinfo/*:Array<RowInfo>*/ = o.skipHidden && sheet["!rows"] || [];
|
||||
for(C = r.s.c; C <= r.e.c; ++C) {
|
||||
if(((colinfo[C]||{}).hidden)) continue;
|
||||
cols[C] = encode_col(C);
|
||||
val = dense ? sheet[R][C] : sheet[cols[C] + rr];
|
||||
val = dense ? sheet["!data"][R][C] : sheet[cols[C] + rr];
|
||||
switch(header) {
|
||||
case 1: hdr[C] = C - r.s.c; break;
|
||||
case 2: hdr[C] = cols[C]; break;
|
||||
@ -108,7 +106,7 @@ function write_json_stream(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
|
||||
stream._read = function() {
|
||||
while(R <= r.e.r) {
|
||||
if ((rowinfo[R-1]||{}).hidden) continue;
|
||||
var row = make_json_row(sheet, r, R, cols, header, hdr, dense, o);
|
||||
var row = make_json_row(sheet, r, R, cols, header, hdr, o);
|
||||
++R;
|
||||
if((row.isempty === false) || (header === 1 ? o.blankrows !== false : !!o.blankrows)) {
|
||||
stream.push(row.row);
|
||||
|
@ -10,6 +10,8 @@ XLSX.writeFileAsync = writeFileAsync;
|
||||
XLSX.utils = utils;
|
||||
XLSX.writeXLSX = writeSyncXLSX;
|
||||
XLSX.writeFileXLSX = writeFileSyncXLSX;
|
||||
XLSX.set_fs = set_fs;
|
||||
XLSX.set_cptable = set_cptable;
|
||||
XLSX.SSF = SSF;
|
||||
if(typeof __stream !== "undefined") XLSX.stream = __stream;
|
||||
if(typeof CFB !== "undefined") XLSX.CFB = CFB;
|
||||
|
@ -1,59 +0,0 @@
|
||||
# Demos
|
||||
|
||||
These demos are intended to demonstrate how to load this library in various
|
||||
ecosystems. The library is designed to be used in the web browser and in node
|
||||
contexts, using dynamic feature tests to pull in features when necessary. This
|
||||
works extremely well in common use cases: script tag insertion and node require.
|
||||
|
||||
Systems like webpack try to be clever by performing simple static analysis to
|
||||
pull in code. However, they do not support dynamic type tests, breaking
|
||||
compatibility with traditional scripts. Configuration is required. The demos
|
||||
cover basic configuration steps for various systems and should "just work".
|
||||
|
||||
Mobile app and other larger demos do not include the full build structure. The
|
||||
demos have `Makefile` scripts that show how to reproduce the full projects. The
|
||||
scripts have been tested against iOS and OSX. For Windows platforms, GNU make
|
||||
can be installed with Bash on Windows or with `cygwin`.
|
||||
|
||||
### Included Demos
|
||||
|
||||
**Frameworks and APIs**
|
||||
- [`angularjs`](angular/)
|
||||
- [`angular and ionic`](angular2/)
|
||||
- [`knockout`](knockout/)
|
||||
- [`meteor`](meteor/)
|
||||
- [`react, react-native, next`](react/)
|
||||
- [`vue 2.x, weex, nuxt`](vue/)
|
||||
- [`XMLHttpRequest and fetch`](xhr/)
|
||||
- [`nodejs server`](server/)
|
||||
- [`databases and key/value stores`](database/)
|
||||
- [`typed arrays and math`](array/)
|
||||
|
||||
**Bundlers and Tooling**
|
||||
- [`browserify`](browserify/)
|
||||
- [`parcel`](parcel/)
|
||||
- [`requirejs`](requirejs/)
|
||||
- [`rollup`](rollup/)
|
||||
- [`systemjs`](systemjs/)
|
||||
- [`typescript`](typescript/)
|
||||
- [`webpack 2.x`](webpack/)
|
||||
|
||||
**Platforms and Integrations**
|
||||
- [`deno`](deno/)
|
||||
- [`electron application`](electron/)
|
||||
- [`nw.js application`](nwjs/)
|
||||
- [`Chrome / Chromium extensions`](chrome/)
|
||||
- [`Google Sheets API`](https://docs.sheetjs.com/docs/getting-started/demos/gsheet)
|
||||
- [`Adobe Apps`](https://docs.sheetjs.com/docs/getting-started/demos/extendscript)
|
||||
- [`Headless Browsers`](headless/)
|
||||
- [`canvas-datagrid`](datagrid/)
|
||||
- [`x-spreadsheet`](xspreadsheet/)
|
||||
- [`react-data-grid`](react/modify/)
|
||||
- [`vue3-table-light`](/vue/modify/)
|
||||
- [`Swift JSC and other engines`](altjs/)
|
||||
- [`"serverless" functions`](function/)
|
||||
- [`internet explorer`](oldie/)
|
||||
|
||||
Other examples are included in the [showcase](demos/showcase/).
|
||||
|
||||
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
|
10
demos/altjs/.gitignore
vendored
10
demos/altjs/.gitignore
vendored
@ -1,10 +0,0 @@
|
||||
sheetjs.*
|
||||
SheetJSSwift
|
||||
duk*
|
||||
*.class
|
||||
*.jar
|
||||
rhino
|
||||
shim.min.js
|
||||
xlsx.*.js
|
||||
payload.js
|
||||
goja
|
@ -1,3 +0,0 @@
|
||||
disabled_rules:
|
||||
- trailing_semicolon
|
||||
- identifier_name
|
@ -1,60 +0,0 @@
|
||||
.PHONY: all
|
||||
all: duktape nashorn rhinojs swift goja
|
||||
|
||||
.PHONY: base
|
||||
base:
|
||||
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 shim.min.js ]; then cp ../../dist/shim.min.js .; fi
|
||||
|
||||
.PHONY: duk
|
||||
duk: base
|
||||
bash ./duktape.sh
|
||||
gcc -std=c99 -Wall -osheetjs.duk sheetjs.duk.c duktape.c -lm
|
||||
|
||||
.PHONY: duktape
|
||||
duktape: duk ## duktape demo
|
||||
for ext in xlsx xlsb biff8.xls xml.xls; do ./sheetjs.duk sheetjs.$$ext; done
|
||||
|
||||
.PHONY: nashorn
|
||||
nashorn: base ## nashorn demo
|
||||
jjs nashorn.js
|
||||
|
||||
.PHONY: swift
|
||||
swift: base ## swift demo
|
||||
swiftc SheetJSCore.swift main.swift -o SheetJSSwift
|
||||
./SheetJSSwift
|
||||
|
||||
.PHONY: goja
|
||||
goja: base ## goja demo
|
||||
go build goja.go
|
||||
for ext in xlsx xlsb biff8.xls xml.xls; do ./goja sheetjs.$$ext; done
|
||||
|
||||
.PHONY: chakra
|
||||
chakra: base ## Chakra demo
|
||||
node -pe "fs.writeFileSync('payload.js', 'var payload = \"' + fs.readFileSync('sheetjs.xlsx').toString('base64') + '\";')"
|
||||
cat global.js xlsx.full.min.js payload.js chakra.js > xlsx.chakra.js
|
||||
chakra ./xlsx.chakra.js
|
||||
|
||||
.PHONY: rhinojs ## rhino demo
|
||||
rhinojs: base SheetJSRhino.class
|
||||
for ext in xlsx xlsb biff8.xls xml.xls; do java -cp .:SheetJS.jar:rhino.jar SheetJSRhino sheetjs.$$ext; done
|
||||
|
||||
RHDEPS=$(filter-out SheetJSRhino.class,$(patsubst %.java,%.class,$(wildcard com/sheetjs/*.java)))
|
||||
$(RHDEPS): %.class: %.java rhino.jar
|
||||
javac -cp .:SheetJS.jar:rhino.jar $*.java
|
||||
|
||||
SheetJSRhino.class: $(RHDEPS)
|
||||
jar -cf SheetJS.jar $^ xlsx.full.min.js
|
||||
javac -cp .:SheetJS.jar:rhino.jar SheetJSRhino.java
|
||||
|
||||
rhino.jar:
|
||||
if [ ! -e rhino ]; then git clone --depth=1 https://github.com/mozilla/rhino; fi
|
||||
#if [ ! -e rhino/build/rhino*/js.jar ]; then cd rhino; ant jar; fi
|
||||
#cp rhino/build/rhino*/js.jar rhino.jar
|
||||
if [ ! -e rhino/buildGradle/libs/rhino-[0-1]*.jar ]; then cd rhino; ./gradlew jar; fi
|
||||
cp rhino/buildGradle/libs/rhino-[0-9]*.jar rhino.jar
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm SheetJS.jar *.class com/sheetjs/*.class
|
@ -1,186 +0,0 @@
|
||||
# Other JS Engines and Deployments
|
||||
|
||||
There are many JS engines and deployments outside of web browsers. NodeJS is the
|
||||
most popular deployment, but there are many others for special use cases. Some
|
||||
optimize for low overhead and others optimize for ease of embedding within other
|
||||
applications. Since it was designed for ES3 engines, the library can be used in
|
||||
those settings! This demo tries to demonstrate a few alternative deployments.
|
||||
|
||||
Some engines provide no default global object. To create a global reference:
|
||||
|
||||
```js
|
||||
var global = (function(){ return this; }).call(null);
|
||||
```
|
||||
|
||||
|
||||
## Swift + JavaScriptCore
|
||||
|
||||
iOS and OSX ship with the JavaScriptCore framework for running JS scripts from
|
||||
Swift and Objective-C. Hybrid function invocation is tricky, but explicit data
|
||||
passing is straightforward. The demo shows a standalone example for OSX. For
|
||||
playgrounds, the library should be copied to shared playground data directory
|
||||
(usually `~/Documents/Shared Playground Data`):
|
||||
|
||||
```swift
|
||||
/* This only works in a playground, see SheetJSCore.swift for standalone use */
|
||||
import JavaScriptCore;
|
||||
import PlaygroundSupport;
|
||||
|
||||
/* build path variable for the library */
|
||||
let shared_dir = PlaygroundSupport.playgroundSharedDataDirectory;
|
||||
let lib_path = shared_dir.appendingPathComponent("xlsx.full.min.js");
|
||||
|
||||
/* prepare JS context */
|
||||
var context: JSContext! = JSContext();
|
||||
var src = "var global = (function(){ return this; }).call(null);";
|
||||
context.evaluateScript(src);
|
||||
|
||||
/* load library */
|
||||
var lib = try? String(contentsOf: lib_path);
|
||||
context.evaluateScript(lib);
|
||||
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
|
||||
|
||||
/* to verify the library was loaded, get the version string */
|
||||
let XLSXversion: JSValue! = XLSX.objectForKeyedSubscript("version")
|
||||
var version = XLSXversion.toString();
|
||||
```
|
||||
|
||||
Binary strings can be passed back and forth using `String.Encoding.isoLatin1`:
|
||||
|
||||
```swift
|
||||
/* parse sheetjs.xls */
|
||||
let file_path = shared_dir.appendingPathComponent("sheetjs.xls");
|
||||
let data: String! = try String(contentsOf: file_path, encoding: String.Encoding.isoLatin1);
|
||||
context.setObject(data, forKeyedSubscript: "payload" as (NSCopying & NSObjectProtocol));
|
||||
src = "var wb = XLSX.read(payload, {type:'binary'});";
|
||||
context.evaluateScript(src);
|
||||
|
||||
/* write to sheetjsw.xlsx */
|
||||
let out_path = shared_dir.appendingPathComponent("sheetjsw.xlsx");
|
||||
src = "var out = XLSX.write(wb, {type:'binary', bookType:'xlsx'})";
|
||||
context.evaluateScript(src);
|
||||
let outvalue: JSValue! = context.objectForKeyedSubscript("out");
|
||||
var out: String! = outvalue.toString();
|
||||
try? out.write(to: out_path, atomically: false, encoding: String.Encoding.isoLatin1);
|
||||
```
|
||||
|
||||
|
||||
## Nashorn
|
||||
|
||||
Nashorn ships with Java 8. It includes a command-line tool `jjs` for running JS
|
||||
scripts. It is somewhat limited but does offer access to the full Java runtime.
|
||||
|
||||
The `load` function in `jjs` can load the minified source directly:
|
||||
|
||||
```js
|
||||
var global = (function(){ return this; }).call(null);
|
||||
load('xlsx.full.min.js');
|
||||
```
|
||||
|
||||
The Java `nio` API provides the `Files.readAllBytes` method to read a file into
|
||||
a byte array. To use in `XLSX.read`, the demo copies the bytes into a plain JS
|
||||
array and calls `XLSX.read` with type `"array"`.
|
||||
|
||||
|
||||
## Rhino
|
||||
|
||||
[Rhino](http://www.mozilla.org/rhino) is an ES3+ engine written in Java. The
|
||||
`SheetJSRhino` class and `com.sheetjs` package show a complete JAR deployment,
|
||||
including the full XLSX source.
|
||||
|
||||
Due to code generation errors, optimization must be turned off:
|
||||
|
||||
```java
|
||||
Context context = Context.enter();
|
||||
context.setOptimizationLevel(-1);
|
||||
```
|
||||
|
||||
|
||||
## ChakraCore
|
||||
|
||||
ChakraCore is an embeddable JS engine written in C++. The library and binary
|
||||
distributions include a command-line tool `chakra` for running JS scripts.
|
||||
|
||||
The simplest way to interact with the engine is to pass Base64 strings. The make
|
||||
target builds a very simple payload with the data.
|
||||
|
||||
|
||||
## Duktape
|
||||
|
||||
[Duktape](http://duktape.org/) is an embeddable JS engine written in C. The
|
||||
amalgamation makes integration extremely simple! It supports `Buffer` natively
|
||||
but should be sliced before processing:
|
||||
|
||||
```C
|
||||
/* parse a C char array as a workbook object */
|
||||
duk_push_external_buffer(ctx);
|
||||
duk_config_buffer(ctx, -1, buf, len);
|
||||
duk_put_global_string(ctx, "buf");
|
||||
duk_eval_string_noresult("workbook = XLSX.read(buf.slice(0, buf.length), {type:'buffer'});");
|
||||
|
||||
/* write a workbook object to a C char array */
|
||||
duk_eval_string(ctx, "XLSX.write(workbook, {type:'array', bookType:'xlsx'})");
|
||||
duk_size_t sz;
|
||||
char *buf = (char *)duk_get_buffer_data(ctx, -1, sz);
|
||||
duk_pop(ctx);
|
||||
```
|
||||
|
||||
|
||||
## QuickJS
|
||||
|
||||
QuickJS is an embeddable JS engine written in C. It provides a separate set of
|
||||
functions for interacting with the filesystem and the global object. It can run
|
||||
the browser dist build.
|
||||
|
||||
The `global` object is available as `std.global`. To make it visible to the
|
||||
loader, create a reference to itself:
|
||||
|
||||
```js
|
||||
std.global.global = std.global;
|
||||
std.loadScript("xlsx.full.min.js");
|
||||
```
|
||||
|
||||
The filesystem interaction mirrors POSIX, including separate allocations:
|
||||
|
||||
```js
|
||||
/* read file */
|
||||
var rh = std.open(filename, "rb"); rh.seek(0, std.SEEK_END);
|
||||
var sz = rh.tell(); rh.seek();
|
||||
var ab = new ArrayBuffer(sz); rh.read(ab, 0, sz); rh.close();
|
||||
var wb = XLSX.read(ab, {type: 'array'});
|
||||
|
||||
/* write file */
|
||||
var ab = XLSX.write(wb, {type: 'array'});
|
||||
var wh = std.open("sheetjs.qjs.xlsx", "wb");
|
||||
wh.write(out, 0, ab.byteLength); wh.close();
|
||||
```
|
||||
|
||||
|
||||
## Goja
|
||||
|
||||
Goja is a pure Go implementation of ECMAScript 5. `[]byte` should be converted
|
||||
to a binary string in the engine:
|
||||
|
||||
```go
|
||||
/* read file */
|
||||
data, _ := ioutil.ReadFile("sheetjs.xlsx")
|
||||
|
||||
/* load into engine */
|
||||
vm.Set("buf", data)
|
||||
|
||||
/* convert to binary string */
|
||||
_, _ = vm.RunString("var bstr = ''; for(var i = 0; i < buf.length; ++i) bstr += String.fromCharCode(buf[i]);")
|
||||
|
||||
/* parse */
|
||||
wb, _ = vm.RunString("wb = XLSX.read(bstr, {type:'binary', cellNF:true});")
|
||||
```
|
||||
|
||||
On the write side, `"base64"` strings can be decoded in Go:
|
||||
|
||||
```go
|
||||
b64str, _ := vm.RunString("XLSX.write(wb, {type:'base64', bookType:'xlsx'})")
|
||||
buf, _ := base64.StdEncoding.DecodeString(b64str.String())
|
||||
_ = ioutil.WriteFile("sheetjs.xlsx", buf, 0644)
|
||||
```
|
||||
|
||||
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
|
@ -1,37 +0,0 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* This only works in a playground, see SheetJSCore.swift for standalone use */
|
||||
import JavaScriptCore;
|
||||
import PlaygroundSupport;
|
||||
|
||||
/* build path variable for the library */
|
||||
let shared_dir = PlaygroundSupport.playgroundSharedDataDirectory;
|
||||
let lib_path = shared_dir.appendingPathComponent("xlsx.full.min.js");
|
||||
|
||||
/* prepare JS context */
|
||||
var context: JSContext! = JSContext();
|
||||
var src = "var global = (function(){ return this; }).call(null);";
|
||||
context.evaluateScript(src);
|
||||
|
||||
/* load library */
|
||||
var lib = try? String(contentsOf: lib_path);
|
||||
context.evaluateScript(lib);
|
||||
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
|
||||
|
||||
/* to verify the library was loaded, get the version string */
|
||||
let XLSXversion: JSValue! = XLSX.objectForKeyedSubscript("version")
|
||||
var version = XLSXversion.toString();
|
||||
|
||||
/* parse sheetjs.xls */
|
||||
let file_path = shared_dir.appendingPathComponent("sheetjs.xls");
|
||||
let data: String! = try String(contentsOf: file_path, encoding: String.Encoding.isoLatin1);
|
||||
context.setObject(data, forKeyedSubscript: "payload" as (NSCopying & NSObjectProtocol)!);
|
||||
src = "var wb = XLSX.read(payload, {type:'binary'});";
|
||||
context.evaluateScript(src);
|
||||
|
||||
/* write to sheetjsw.xlsx */
|
||||
let out_path = shared_dir.appendingPathComponent("sheetjsw.xlsx");
|
||||
src = "var out = XLSX.write(wb, {type:'binary', bookType:'xlsx'})";
|
||||
context.evaluateScript(src);
|
||||
let outvalue: JSValue! = context.objectForKeyedSubscript("out");
|
||||
var out: String! = outvalue.toString();
|
||||
try? out.write(to: out_path, atomically: false, encoding: String.Encoding.isoLatin1);
|
@ -1,96 +0,0 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
import JavaScriptCore;
|
||||
|
||||
enum SJSError: Error {
|
||||
case badJSContext;
|
||||
case badJSWorkbook;
|
||||
case badJSWorksheet;
|
||||
};
|
||||
|
||||
class SJSWorksheet {
|
||||
var context: JSContext!;
|
||||
var wb: JSValue; var ws: JSValue;
|
||||
var idx: Int32;
|
||||
|
||||
func toCSV() throws -> String {
|
||||
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
|
||||
let utils: JSValue! = XLSX.objectForKeyedSubscript("utils");
|
||||
let sheet_to_csv: JSValue! = utils.objectForKeyedSubscript("sheet_to_csv");
|
||||
return sheet_to_csv.call(withArguments: [ws]).toString();
|
||||
}
|
||||
|
||||
init(ctx: JSContext, workbook: JSValue, worksheet: JSValue, idx: Int32) throws {
|
||||
self.context = ctx; self.wb = workbook; self.ws = worksheet; self.idx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
class SJSWorkbook {
|
||||
var context: JSContext!;
|
||||
var wb: JSValue; var SheetNames: JSValue; var Sheets: JSValue;
|
||||
|
||||
func getSheetAtIndex(idx: Int32) throws -> SJSWorksheet {
|
||||
let SheetName: String = SheetNames.atIndex(Int(idx)).toString();
|
||||
let ws: JSValue! = Sheets.objectForKeyedSubscript(SheetName);
|
||||
return try SJSWorksheet(ctx: context, workbook: wb, worksheet: ws, idx: idx);
|
||||
}
|
||||
|
||||
func writeBStr(bookType: String = "xlsx") throws -> String {
|
||||
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
|
||||
context.evaluateScript(String(format: "var writeopts = {type:'binary', bookType:'%@'}", bookType));
|
||||
let writeopts: JSValue = context.objectForKeyedSubscript("writeopts");
|
||||
let writefunc: JSValue = XLSX.objectForKeyedSubscript("write");
|
||||
return writefunc.call(withArguments: [wb, writeopts]).toString();
|
||||
}
|
||||
|
||||
init(ctx: JSContext, wb: JSValue) throws {
|
||||
self.context = ctx;
|
||||
self.wb = wb;
|
||||
self.SheetNames = wb.objectForKeyedSubscript("SheetNames");
|
||||
self.Sheets = wb.objectForKeyedSubscript("Sheets");
|
||||
}
|
||||
}
|
||||
|
||||
class SheetJSCore {
|
||||
var context: JSContext!;
|
||||
var XLSX: JSValue!;
|
||||
|
||||
func init_context() throws -> JSContext {
|
||||
var context: JSContext!
|
||||
do {
|
||||
context = JSContext();
|
||||
context.exceptionHandler = { _, X in if let e = X { print(e.toString()!); }; };
|
||||
context.evaluateScript("var global = (function(){ return this; }).call(null);");
|
||||
context.evaluateScript("if(typeof wbs == 'undefined') wbs = [];");
|
||||
let src = try String(contentsOfFile: "xlsx.full.min.js");
|
||||
context.evaluateScript(src);
|
||||
if context != nil { return context!; }
|
||||
} catch { print(error.localizedDescription); }
|
||||
throw SJSError.badJSContext;
|
||||
}
|
||||
|
||||
func version() throws -> String {
|
||||
if let version = XLSX.objectForKeyedSubscript("version") { return version.toString(); }
|
||||
throw SJSError.badJSContext;
|
||||
}
|
||||
|
||||
func readFile(file: String) throws -> SJSWorkbook {
|
||||
let data: String! = try String(contentsOfFile: file, encoding: String.Encoding.isoLatin1);
|
||||
return try readBStr(data: data);
|
||||
}
|
||||
|
||||
func readBStr(data: String) throws -> SJSWorkbook {
|
||||
context.setObject(data, forKeyedSubscript: "payload" as (NSCopying & NSObjectProtocol));
|
||||
context.evaluateScript("var wb = XLSX.read(payload, {type:'binary'});");
|
||||
let wb: JSValue! = context.objectForKeyedSubscript("wb");
|
||||
if wb == nil { throw SJSError.badJSWorkbook; }
|
||||
return try SJSWorkbook(ctx: context, wb: wb);
|
||||
}
|
||||
|
||||
init() throws {
|
||||
do {
|
||||
self.context = try init_context();
|
||||
self.XLSX = self.context.objectForKeyedSubscript("XLSX");
|
||||
if self.XLSX == nil { throw SJSError.badJSContext; }
|
||||
} catch { print(error.localizedDescription); }
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* vim: set ts=2: */
|
||||
import com.sheetjs.SheetJS;
|
||||
import com.sheetjs.SheetJSFile;
|
||||
import com.sheetjs.SheetJSSheet;
|
||||
|
||||
public class SheetJSRhino {
|
||||
public static void main(String args[]) throws Exception {
|
||||
try {
|
||||
SheetJS sjs = new SheetJS();
|
||||
|
||||
/* open file */
|
||||
SheetJSFile xl = sjs.read_file(args[0]);
|
||||
|
||||
/* get sheetnames */
|
||||
String[] sheetnames = xl.get_sheet_names();
|
||||
System.err.println(sheetnames[0]);
|
||||
|
||||
/* convert to CSV */
|
||||
SheetJSSheet sheet = xl.get_sheet(0);
|
||||
String csv = sheet.get_csv();
|
||||
|
||||
System.out.println(csv);
|
||||
|
||||
} catch(Exception e) {
|
||||
throw e;
|
||||
} finally {
|
||||
SheetJS.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
var wb = XLSX.read(payload, {type:'base64'});
|
||||
console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
|
@ -1,51 +0,0 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* vim: set ts=2: */
|
||||
package com.sheetjs;
|
||||
|
||||
import java.lang.Integer;
|
||||
import java.lang.StringBuilder;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.NativeArray;
|
||||
import org.mozilla.javascript.NativeObject;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
|
||||
public class JSHelper {
|
||||
static String read_file(String file) throws IOException {
|
||||
byte[] b = Files.readAllBytes(Paths.get(file));
|
||||
System.out.println(b.length);
|
||||
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])));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static Object get_object(String path, Object base) throws ObjectNotFoundException {
|
||||
int idx = path.indexOf(".");
|
||||
Scriptable b = (Scriptable)base;
|
||||
if(idx == -1) return b.get(path, 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));
|
||||
return get_object(path.substring(idx+1), (NativeObject)o);
|
||||
}
|
||||
|
||||
static Object[] get_array(String path, Object base) throws ObjectNotFoundException {
|
||||
NativeArray arr = (NativeArray)get_object(path, base);
|
||||
Object[] out = new Object[(int)arr.getLength()];
|
||||
int idx;
|
||||
for(Object o : arr.getIds()) out[idx = (Integer)o] = arr.get(idx, arr);
|
||||
return out;
|
||||
}
|
||||
|
||||
static String[] get_string_array(String path, Object base) throws ObjectNotFoundException {
|
||||
NativeArray arr = (NativeArray)get_object(path, base);
|
||||
String[] out = new String[(int)arr.getLength()];
|
||||
int idx;
|
||||
for(Object o : arr.getIds()) out[idx = (Integer)o] = arr.get(idx, arr).toString();
|
||||
return out;
|
||||
}
|
||||
|
||||
public static void close() { Context.exit(); }
|
||||
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* vim: set ts=2: */
|
||||
package com.sheetjs;
|
||||
|
||||
import java.lang.Exception;
|
||||
|
||||
public class ObjectNotFoundException extends Exception {
|
||||
public ObjectNotFoundException() {}
|
||||
public ObjectNotFoundException(String message) { super(message); }
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* vim: set ts=2: */
|
||||
package com.sheetjs;
|
||||
|
||||
import java.lang.Integer;
|
||||
import java.util.Scanner;
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Function;
|
||||
import org.mozilla.javascript.NativeObject;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
|
||||
public class SheetJS {
|
||||
public Scriptable scope;
|
||||
public Context cx;
|
||||
public NativeObject nXLSX;
|
||||
|
||||
public SheetJS() throws Exception {
|
||||
this.cx = Context.enter();
|
||||
this.scope = this.cx.initStandardObjects();
|
||||
|
||||
/* boilerplate */
|
||||
cx.setOptimizationLevel(-1);
|
||||
String s = "var global = (function(){ return this; }).call(null);";
|
||||
cx.evaluateString(scope, s, "<cmd>", 1, null);
|
||||
|
||||
/* eval library */
|
||||
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();
|
||||
cx.evaluateString(scope, s, "<cmd>", 1, null);
|
||||
|
||||
/* grab XLSX variable */
|
||||
Object XLSX = scope.get("XLSX", scope);
|
||||
if(XLSX == Scriptable.NOT_FOUND) throw new Exception("XLSX not found");
|
||||
this.nXLSX = (NativeObject)XLSX;
|
||||
}
|
||||
|
||||
public SheetJSFile read_file(String filename) throws IOException, ObjectNotFoundException {
|
||||
/* open file */
|
||||
String d = JSHelper.read_file(filename);
|
||||
|
||||
/* options argument */
|
||||
NativeObject q = (NativeObject)this.cx.evaluateString(this.scope, "q = {'type':'binary', 'WTF':1};", "<cmd>", 2, null);
|
||||
|
||||
/* set up function arguments */
|
||||
Object args[] = {d, q};
|
||||
|
||||
/* call read -> wb workbook */
|
||||
Function readfunc = (Function)JSHelper.get_object("XLSX.read",this.scope);
|
||||
NativeObject wb = (NativeObject)readfunc.call(this.cx, this.scope, this.nXLSX, args);
|
||||
|
||||
return new SheetJSFile(wb, this);
|
||||
}
|
||||
|
||||
public static void close() { JSHelper.close(); }
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* vim: set ts=2: */
|
||||
package com.sheetjs;
|
||||
|
||||
import org.mozilla.javascript.NativeObject;
|
||||
import org.mozilla.javascript.Function;
|
||||
|
||||
public class SheetJSFile {
|
||||
public NativeObject wb;
|
||||
public SheetJS sheetjs;
|
||||
public SheetJSFile() {}
|
||||
public SheetJSFile(NativeObject wb, SheetJS sheetjs) { this.wb = wb; this.sheetjs = sheetjs; }
|
||||
public String[] get_sheet_names() {
|
||||
try {
|
||||
return JSHelper.get_string_array("SheetNames", this.wb);
|
||||
} catch(ObjectNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public SheetJSSheet get_sheet(int idx) throws ObjectNotFoundException {
|
||||
return new SheetJSSheet(this, idx);
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* vim: set ts=2: */
|
||||
package com.sheetjs;
|
||||
|
||||
import org.mozilla.javascript.Function;
|
||||
import org.mozilla.javascript.NativeObject;
|
||||
|
||||
public class SheetJSSheet {
|
||||
public NativeObject ws;
|
||||
public SheetJSFile wb;
|
||||
public SheetJSSheet(SheetJSFile wb, int idx) throws ObjectNotFoundException {
|
||||
this.wb = wb;
|
||||
this.ws = (NativeObject)JSHelper.get_object("Sheets." + wb.get_sheet_names()[idx],wb.wb);
|
||||
}
|
||||
public String get_range() throws ObjectNotFoundException {
|
||||
return JSHelper.get_object("!ref",this.ws).toString();
|
||||
}
|
||||
public String get_string_value(String address) throws ObjectNotFoundException {
|
||||
return JSHelper.get_object(address + ".v",this.ws).toString();
|
||||
}
|
||||
|
||||
public String get_csv() throws ObjectNotFoundException {
|
||||
Function csvify = (Function)JSHelper.get_object("XLSX.utils.sheet_to_csv",this.wb.sheetjs.scope);
|
||||
Object csvArgs[] = {this.ws};
|
||||
Object csv = csvify.call(this.wb.sheetjs.cx, this.wb.sheetjs.scope, this.wb.sheetjs.scope, csvArgs);
|
||||
return csv.toString();
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
DUKTAPE_VER=2.6.0
|
||||
if [ ! -e duktape-$DUKTAPE_VER ]; then
|
||||
if [ ! -e duktape-$DUKTAPE_VER.tar ]; then
|
||||
if [ ! -e duktape-$DUKTAPE_VER.tar.xz ]; then
|
||||
curl -O https://duktape.org/duktape-$DUKTAPE_VER.tar.xz
|
||||
fi
|
||||
xz -d duktape-$DUKTAPE_VER.tar.xz
|
||||
fi
|
||||
tar -xf duktape-$DUKTAPE_VER.tar
|
||||
fi
|
||||
|
||||
for f in duktape.{c,h} duk_config.h; do
|
||||
cp duktape-$DUKTAPE_VER/src/$f .
|
||||
done
|
||||
|
@ -1,3 +0,0 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
var global = (function(){ return this; }).call(null);
|
||||
|
@ -1,71 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
b64 "encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
"io/ioutil"
|
||||
"github.com/dop251/goja"
|
||||
)
|
||||
|
||||
func safe_run_file(vm *goja.Runtime, file string) {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err != nil { panic(err) }
|
||||
src := string(data)
|
||||
_, err = vm.RunString(src)
|
||||
if err != nil { panic(err) }
|
||||
}
|
||||
|
||||
func eval_string(vm *goja.Runtime, cmd string) goja.Value {
|
||||
v, err := vm.RunString(cmd)
|
||||
if err != nil { panic(err) }
|
||||
return v
|
||||
}
|
||||
|
||||
func write_type(vm *goja.Runtime, t string) {
|
||||
/* due to some wonkiness with array passing, use base64 */
|
||||
b64str := eval_string(vm, "XLSX.write(wb, {type:'base64', bookType:'" + t + "'})")
|
||||
buf, err := b64.StdEncoding.DecodeString(b64str.String());
|
||||
if err != nil { panic(err) }
|
||||
err = ioutil.WriteFile("sheetjsg." + t, buf, 0644)
|
||||
if err != nil { panic(err) }
|
||||
}
|
||||
|
||||
func main() {
|
||||
vm := goja.New()
|
||||
|
||||
/* initialize */
|
||||
eval_string(vm, "if(typeof global == 'undefined') global = (function(){ return this; }).call(null);")
|
||||
|
||||
/* load library */
|
||||
safe_run_file(vm, "shim.min.js")
|
||||
safe_run_file(vm, "xlsx.full.min.js")
|
||||
|
||||
/* get version string */
|
||||
v := eval_string(vm, "XLSX.version")
|
||||
fmt.Printf("SheetJS library version %s\n", v)
|
||||
|
||||
/* read file */
|
||||
data, err := ioutil.ReadFile(os.Args[1])
|
||||
if err != nil { panic(err) }
|
||||
vm.Set("buf", data)
|
||||
fmt.Printf("Loaded file %s\n", os.Args[1])
|
||||
|
||||
/* parse workbook */
|
||||
eval_string(vm, "var bstr = ''; for(var i = 0; i < buf.length; ++i) bstr += String.fromCharCode(buf[i]);")
|
||||
eval_string(vm, "wb = XLSX.read(bstr, {type:'binary', cellNF:true});")
|
||||
eval_string(vm, "ws = wb.Sheets[wb.SheetNames[0]]")
|
||||
|
||||
/* print CSV */
|
||||
csv := eval_string(vm, "XLSX.utils.sheet_to_csv(ws)")
|
||||
fmt.Printf("%s\n", csv)
|
||||
|
||||
/* change cell A1 to 3 */
|
||||
eval_string(vm, "ws['A1'].v = 3; delete ws['A1'].w;")
|
||||
|
||||
/* write file */
|
||||
//write_type(vm, "xlsb")
|
||||
//write_type(vm, "xlsx")
|
||||
write_type(vm, "xls")
|
||||
write_type(vm, "csv")
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
|
||||
let sheetjs = try SheetJSCore();
|
||||
|
||||
try print(sheetjs.version());
|
||||
|
||||
let filenames: [[String]] = [
|
||||
["xlsx", "xlsx"],
|
||||
["xlsb", "xlsb"],
|
||||
["biff8.xls", "xls"],
|
||||
["xml.xls", "xlml"]
|
||||
];
|
||||
|
||||
for fn in filenames {
|
||||
let wb: SJSWorkbook = try sheetjs.readFile(file: "sheetjs." + fn[0]);
|
||||
let ws: SJSWorksheet = try wb.getSheetAtIndex(idx: 0);
|
||||
let csv: String = try ws.toCSV();
|
||||
print(csv);
|
||||
let wbout: String = try wb.writeBStr(bookType: fn[1]);
|
||||
try wbout.write(toFile: "sheetjsswift." + fn[0], atomically: false, encoding: String.Encoding.isoLatin1);
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env jjs
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
|
||||
/* load module */
|
||||
var global = (function(){ return this; }).call(null);
|
||||
load('xlsx.full.min.js');
|
||||
|
||||
/* helper to convert byte array to plain JS array */
|
||||
function b2a(b) {
|
||||
var out = new Array(b.length);
|
||||
for(var i = 0; i < out.length; i++) out[i] = (b[i] < 0 ? b[i] + 256 : b[i]);
|
||||
return out;
|
||||
}
|
||||
|
||||
function process_file(path) {
|
||||
java.lang.System.out.println(path);
|
||||
|
||||
/* read file */
|
||||
var path = java.nio.file.Paths.get(path);
|
||||
var bytes = java.nio.file.Files.readAllBytes(path);
|
||||
var u8a = b2a(bytes);
|
||||
|
||||
/* read data */
|
||||
var wb = XLSX.read(u8a, {type:"array"});
|
||||
|
||||
/* get first worksheet as an array of arrays */
|
||||
var ws = wb.Sheets[wb.SheetNames[0]];
|
||||
var js = XLSX.utils.sheet_to_json(ws, {header:1});
|
||||
|
||||
/* print out every line */
|
||||
js.forEach(function(l) { java.lang.System.out.println(JSON.stringify(l)); });
|
||||
}
|
||||
|
||||
process_file('sheetjs.xlsx');
|
||||
process_file('sheetjs.xlsb');
|
||||
process_file('sheetjs.biff8.xls');
|
@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env qjs
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* load XLSX */
|
||||
std.global.global = std.global;
|
||||
std.loadScript("xlsx.full.min.js");
|
||||
|
||||
/* read contents of file */
|
||||
var rh = std.open("sheetjs.xlsx", "rb");
|
||||
rh.seek(0, std.SEEK_END);
|
||||
var sz = rh.tell();
|
||||
var ab = new ArrayBuffer(sz);
|
||||
rh.seek();
|
||||
rh.read(ab, 0, sz);
|
||||
rh.close();
|
||||
|
||||
/* parse file */
|
||||
var wb = XLSX.read(ab, {type: 'array'});
|
||||
|
||||
/* write array */
|
||||
var out = XLSX.write(wb, {type: 'array'});
|
||||
|
||||
/* write contents to file */
|
||||
var wh = std.open("sheetjs.qjs.xlsx", "wb");
|
||||
wh.write(out, 0, out.byteLength);
|
||||
wh.close();
|
@ -1,110 +0,0 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "duktape.h"
|
||||
|
||||
#define FAIL_LOAD { \
|
||||
duk_push_undefined(ctx); \
|
||||
perror("Error in load_file"); \
|
||||
return 1; \
|
||||
}
|
||||
|
||||
static char *read_file(const char *filename, size_t *sz) {
|
||||
FILE *f = fopen(filename, "rb");
|
||||
if(!f) return NULL;
|
||||
long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); }
|
||||
char *buf = (char *)malloc(fsize * sizeof(char));
|
||||
*sz = fread((void *) buf, 1, fsize, f);
|
||||
fclose(f);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static duk_int_t eval_file(duk_context *ctx, const char *filename) {
|
||||
size_t len; char *buf = read_file(filename, &len);
|
||||
if(!buf) FAIL_LOAD
|
||||
|
||||
duk_push_lstring(ctx, (const char *)buf, (duk_size_t)len);
|
||||
duk_int_t retval = duk_peval(ctx);
|
||||
duk_pop(ctx);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static duk_int_t load_file(duk_context *ctx, const char *filename, const char *var) {
|
||||
size_t len; char *buf = read_file(filename, &len);
|
||||
if(!buf) FAIL_LOAD
|
||||
|
||||
duk_push_external_buffer(ctx);
|
||||
duk_config_buffer(ctx, -1, buf, len);
|
||||
duk_put_global_string(ctx, var);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_int_t save_file(duk_context *ctx, const char *filename, const char *var) {
|
||||
duk_get_global_string(ctx, var);
|
||||
duk_size_t sz;
|
||||
char *buf = (char *)duk_get_buffer_data(ctx, -1, &sz);
|
||||
|
||||
if(!buf) return 1;
|
||||
FILE *f = fopen(filename, "wb"); fwrite(buf, 1, sz, f); fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FAIL(cmd) { \
|
||||
printf("error in %s: %s\n", cmd, duk_safe_to_string(ctx, -1)); \
|
||||
duk_destroy_heap(ctx); \
|
||||
return res; \
|
||||
}
|
||||
|
||||
#define DOIT(cmd) duk_eval_string_noresult(ctx, cmd);
|
||||
int main(int argc, char *argv[]) {
|
||||
duk_int_t res = 0;
|
||||
|
||||
/* initialize */
|
||||
duk_context *ctx = duk_create_heap_default();
|
||||
/* duktape does not expose a standard "global" by default */
|
||||
DOIT("var global = (function(){ return this; }).call(null);");
|
||||
|
||||
/* load library */
|
||||
res = eval_file(ctx, "shim.min.js");
|
||||
if(res != 0) FAIL("shim load")
|
||||
res = eval_file(ctx, "xlsx.full.min.js");
|
||||
if(res != 0) FAIL("library load")
|
||||
|
||||
/* get version string */
|
||||
duk_eval_string(ctx, "XLSX.version");
|
||||
printf("SheetJS library version %s\n", duk_get_string(ctx, -1));
|
||||
duk_pop(ctx);
|
||||
|
||||
/* read file */
|
||||
res = load_file(ctx, argv[1], "buf");
|
||||
if(res != 0) FAIL("file load")
|
||||
printf("Loaded file %s\n", argv[1]);
|
||||
|
||||
/* parse workbook */
|
||||
DOIT("wb = XLSX.read(buf, {type:'buffer', cellNF:true});");
|
||||
DOIT("ws = wb.Sheets[wb.SheetNames[0]]");
|
||||
|
||||
/* print CSV */
|
||||
duk_eval_string(ctx, "XLSX.utils.sheet_to_csv(ws)");
|
||||
printf("%s\n", duk_get_string(ctx, -1));
|
||||
duk_pop(ctx);
|
||||
|
||||
/* change cell A1 to 3 */
|
||||
DOIT("ws['A1'].v = 3; delete ws['A1'].w;");
|
||||
|
||||
/* write file */
|
||||
#define WRITE_TYPE(BOOKTYPE) \
|
||||
DOIT("newbuf = (XLSX.write(wb, {type:'array', bookType:'" BOOKTYPE "'}));");\
|
||||
res = save_file(ctx, "sheetjsw." BOOKTYPE, "newbuf");\
|
||||
if(res != 0) FAIL("save sheetjsw." BOOKTYPE)
|
||||
|
||||
WRITE_TYPE("xlsb")
|
||||
WRITE_TYPE("xlsx")
|
||||
WRITE_TYPE("xls")
|
||||
WRITE_TYPE("csv")
|
||||
|
||||
/* cleanup */
|
||||
duk_destroy_heap(ctx);
|
||||
return res;
|
||||
}
|
@ -1,148 +0,0 @@
|
||||
# AngularJS
|
||||
|
||||
The `xlsx.core.min.js` and `xlsx.full.min.js` scripts are designed to be dropped
|
||||
into web pages with script tags:
|
||||
|
||||
```html
|
||||
<script src="xlsx.full.min.js"></script>
|
||||
```
|
||||
|
||||
Strictly speaking, there should be no need for an Angular demo! You can proceed
|
||||
as you would with any other browser-friendly library.
|
||||
|
||||
This demo uses AngularJS 1.5.0.
|
||||
|
||||
|
||||
## Array of Objects
|
||||
|
||||
A common data table is often stored as an array of objects:
|
||||
|
||||
```js
|
||||
$scope.data = [
|
||||
{ Name: "Bill Clinton", Index: 42 },
|
||||
{ Name: "GeorgeW Bush", Index: 43 },
|
||||
{ Name: "Barack Obama", Index: 44 },
|
||||
{ Name: "Donald Trump", Index: 45 }
|
||||
];
|
||||
```
|
||||
|
||||
This neatly maps to a table with `ng-repeat`:
|
||||
|
||||
```html
|
||||
<table id="sjs-table">
|
||||
<tr><th>Name</th><th>Index</th></tr>
|
||||
<tr ng-repeat="row in data">
|
||||
<td>{{row.Name}}</td>
|
||||
<td>{{row.Index}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
```
|
||||
|
||||
The `$http` service can request binary data using the `"arraybuffer"` response
|
||||
type coupled with `XLSX.read` with type `"array"`:
|
||||
|
||||
```js
|
||||
$http({
|
||||
method:'GET',
|
||||
url:'https://sheetjs.com/pres.xlsx',
|
||||
responseType:'arraybuffer'
|
||||
}).then(function(data) {
|
||||
var wb = XLSX.read(data.data, {type:"array"});
|
||||
var d = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
$scope.data = d;
|
||||
}, function(err) { console.log(err); });
|
||||
```
|
||||
|
||||
The HTML table can be directly exported with `XLSX.utils.table_to_book`:
|
||||
|
||||
```js
|
||||
var wb = XLSX.utils.table_to_book(document.getElementById('sjs-table'));
|
||||
XLSX.writeFile(wb, "export.xlsx");
|
||||
```
|
||||
|
||||
|
||||
## Import Directive
|
||||
|
||||
A general import directive is fairly straightforward:
|
||||
|
||||
- Define the `importSheetJs` directive in the app:
|
||||
|
||||
```js
|
||||
app.directive("importSheetJs", [SheetJSImportDirective]);
|
||||
```
|
||||
|
||||
- Add the attribute `import-sheet-js=""` to the file input element:
|
||||
|
||||
```html
|
||||
<input type="file" import-sheet-js="" multiple="false" />
|
||||
```
|
||||
|
||||
- Define the directive:
|
||||
|
||||
```js
|
||||
function SheetJSImportDirective() {
|
||||
return {
|
||||
scope: { opts: '=' },
|
||||
link: function ($scope, $elm) {
|
||||
$elm.on('change', function (changeEvent) {
|
||||
var reader = new FileReader();
|
||||
|
||||
reader.onload = function (e) {
|
||||
/* read workbook */
|
||||
var ab = e.target.result;
|
||||
var workbook = XLSX.read(ab);
|
||||
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
};
|
||||
|
||||
reader.readAsArrayBuffer(changeEvent.target.files[0]);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Export Service
|
||||
|
||||
An export can be triggered at any point! Depending on how data is represented,
|
||||
a workbook object can be built using the utility functions. For example, using
|
||||
an array of objects:
|
||||
|
||||
```js
|
||||
/* starting from this data */
|
||||
var data = [
|
||||
{ name: "Barack Obama", pres: 44 },
|
||||
{ name: "Donald Trump", pres: 45 }
|
||||
];
|
||||
|
||||
/* generate a worksheet */
|
||||
var ws = XLSX.utils.json_to_sheet(data);
|
||||
|
||||
/* add to workbook */
|
||||
var wb = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(wb, ws, "Presidents");
|
||||
|
||||
/* write workbook and force a download */
|
||||
XLSX.writeFile(wb, "sheetjs.xlsx");
|
||||
```
|
||||
|
||||
## Demo
|
||||
|
||||
`grid.html` uses `angular-ui-grid` to display a table. The library does not
|
||||
provide any way to modify the import button, so the demo includes a simple
|
||||
directive for a HTML File Input control. It also includes a sample service for
|
||||
export which adds an item to the export menu.
|
||||
|
||||
The demo `SheetJSImportDirective` follows the prescription from the README for
|
||||
File input controls using `readAsArrayBuffer`, converting to a suitable
|
||||
representation and updating the scope.
|
||||
|
||||
`SheetJSExportService` exposes export functions for `XLSB` and `XLSX`. Other
|
||||
file formats can be exported by changing the `bookType` variable. It grabs
|
||||
values from the grid, builds an array of arrays, generates a workbook and forces
|
||||
a download. By setting the `filename` and `sheetname` options in the `ui-grid`
|
||||
options, the output can be controlled.
|
||||
|
||||
|
||||
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
|
96
demos/angular/SheetJS-angular.js
vendored
96
demos/angular/SheetJS-angular.js
vendored
@ -1,96 +0,0 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env browser */
|
||||
/* global XLSX */
|
||||
/* exported SheetJSExportService, SheetJSImportDirective */
|
||||
function SheetJSExportService(uiGridExporterService) {
|
||||
|
||||
function exportSheetJS(gridApi, wopts) {
|
||||
var columns = uiGridExporterService.getColumnHeaders(gridApi.grid, 'all');
|
||||
var data = uiGridExporterService.getData(gridApi.grid, 'all', 'all');
|
||||
|
||||
var fileName = gridApi.grid.options.filename || 'SheetJS';
|
||||
fileName += wopts.bookType ? "." + wopts.bookType : '.xlsx';
|
||||
|
||||
var sheetName = gridApi.grid.options.sheetname || 'Sheet1';
|
||||
|
||||
var wb = XLSX.utils.book_new(), ws = uigrid_to_sheet(data, columns);
|
||||
XLSX.utils.book_append_sheet(wb, ws, sheetName);
|
||||
XLSX.writeFile(wb, fileName);
|
||||
}
|
||||
|
||||
var service = {};
|
||||
service.exportXLSB = function exportXLSB(gridApi) { return exportSheetJS(gridApi, { bookType: 'xlsb', bookSST: true, type: 'array' }); };
|
||||
service.exportXLSX = function exportXLSX(gridApi) { return exportSheetJS(gridApi, { bookType: 'xlsx', bookSST: true, type: 'array' }); }
|
||||
|
||||
return service;
|
||||
|
||||
/* utilities */
|
||||
function uigrid_to_sheet(data, columns) {
|
||||
var o = [], oo = [], i = 0, j = 0;
|
||||
|
||||
/* column headers */
|
||||
for(j = 0; j < columns.length; ++j) oo.push(get_value(columns[j]));
|
||||
o.push(oo);
|
||||
|
||||
/* table data */
|
||||
for(i = 0; i < data.length; ++i) {
|
||||
oo = [];
|
||||
for(j = 0; j < data[i].length; ++j) oo.push(get_value(data[i][j]));
|
||||
o.push(oo);
|
||||
}
|
||||
/* aoa_to_sheet converts an array of arrays into a worksheet object */
|
||||
return XLSX.utils.aoa_to_sheet(o);
|
||||
}
|
||||
|
||||
function get_value(col) {
|
||||
if(!col) return col;
|
||||
if(col.value) return col.value;
|
||||
if(col.displayName) return col.displayName;
|
||||
if(col.name) return col.name;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function SheetJSImportDirective() {
|
||||
return {
|
||||
scope: { opts: '=' },
|
||||
link: function($scope, $elm) {
|
||||
$elm.on('change', function(changeEvent) {
|
||||
var reader = new FileReader();
|
||||
|
||||
reader.onload = function(e) {
|
||||
/* read workbook */
|
||||
var ab = e.target.result;
|
||||
var wb = XLSX.read(ab);
|
||||
|
||||
/* grab first sheet */
|
||||
var wsname = wb.SheetNames[0];
|
||||
var ws = wb.Sheets[wsname];
|
||||
|
||||
/* grab first row and generate column headers */
|
||||
var aoa = XLSX.utils.sheet_to_json(ws, {header:1, raw:false});
|
||||
var cols = [];
|
||||
for(var i = 0; i < aoa[0].length; ++i) cols[i] = { field: aoa[0][i] };
|
||||
|
||||
/* generate rest of the data */
|
||||
var data = [];
|
||||
for(var r = 1; r < aoa.length; ++r) {
|
||||
data[r-1] = {};
|
||||
for(i = 0; i < aoa[r].length; ++i) {
|
||||
if(aoa[r][i] == null) continue;
|
||||
data[r-1][aoa[0][i]] = aoa[r][i];
|
||||
}
|
||||
}
|
||||
|
||||
/* update scope */
|
||||
$scope.$apply(function() {
|
||||
$scope.opts.columnDefs = cols;
|
||||
$scope.opts.data = data;
|
||||
});
|
||||
};
|
||||
|
||||
reader.readAsArrayBuffer(changeEvent.target.files[0]);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
45
demos/angular/app.js
vendored
45
demos/angular/app.js
vendored
@ -1,45 +0,0 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env browser */
|
||||
/* global angular, SheetJSExportService, SheetJSImportDirective */
|
||||
var app = angular.module('app', ['ngAnimate', 'ngTouch', 'ui.grid', 'ui.grid.selection', 'ui.grid.exporter']);
|
||||
|
||||
/* Inject SheetJSExportService */
|
||||
app.factory('SheetJSExportService', SheetJSExportService);
|
||||
SheetJSExportService.inject = ['uiGridExporterService'];
|
||||
|
||||
app.controller('MainCtrl', ['$scope', '$http','SheetJSExportService', function($scope, $http, SheetJSExportService) {
|
||||
$scope.gridOptions = {
|
||||
columnDefs: [
|
||||
{ field: 'name' },
|
||||
{ field: 'gender', visible: false},
|
||||
{ field: 'company' }
|
||||
],
|
||||
enableGridMenu: true,
|
||||
enableSelectAll: true,
|
||||
exporterMenuPdf: false,
|
||||
exporterMenuCsv: false,
|
||||
showHeader: true,
|
||||
onRegisterApi: function(gridApi){
|
||||
$scope.gridApi = gridApi;
|
||||
},
|
||||
/* SheetJS Service setup */
|
||||
filename: "SheetJSAngular",
|
||||
sheetname: "ng-SheetJS",
|
||||
gridMenuCustomItems: [
|
||||
{
|
||||
title: 'Export all data as XLSX',
|
||||
action: function() { SheetJSExportService.exportXLSX($scope.gridApi); },
|
||||
order: 200
|
||||
},
|
||||
{
|
||||
title: 'Export all data as XLSB',
|
||||
action: function() { SheetJSExportService.exportXLSB($scope.gridApi); },
|
||||
order: 201
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
$http.get('https://cdn.rawgit.com/angular-ui/ui-grid.info/gh-pages/data/100.json').success(function(data) { $scope.gridOptions.data = data; });
|
||||
|
||||
}]);
|
||||
app.directive("importSheetJs", [SheetJSImportDirective]);
|
@ -1,64 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com -->
|
||||
<!-- vim: set ts=2: -->
|
||||
<html ng-app="app">
|
||||
<head>
|
||||
<title>SheetJS + AngularJS + ui-grid</title>
|
||||
<!-- Angular -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.js"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-touch.js"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-animate.js"></script>
|
||||
|
||||
<!-- ui-grid -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.11.0/ui-grid.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.11.0/ui-grid.css"/>
|
||||
|
||||
<!-- SheetJS js-xlsx library -->
|
||||
<script src="shim.js"></script>
|
||||
<script src="xlsx.full.min.js"></script>
|
||||
|
||||
<!-- SheetJS Service -->
|
||||
<script src="SheetJS-angular.js"></script>
|
||||
|
||||
<style>
|
||||
.grid1 {
|
||||
width: 500px;
|
||||
height: 400px;
|
||||
};
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<pre>
|
||||
<b><a href="http://sheetjs.com">SheetJS + AngularJS demo</a></b>
|
||||
|
||||
The core library can be used as-is in AngularJS applications.
|
||||
The <a href="https://github.com/sheetjs/js-xlsx">Community Edition README</a> details some common use cases.
|
||||
We also have some <a href="http://sheetjs.com/demos/">more public demos</a>
|
||||
|
||||
This demo shows:
|
||||
- SheetJSExportService: a service for exporting data from a ui-grid
|
||||
- SheetJSImportDirective: a directive providing a file input button for import
|
||||
|
||||
<a href="https://obamawhitehouse.archives.gov/sites/default/files/omb/budget/fy2014/assets/receipts.xls">Sample Spreadsheet</a>
|
||||
</pre>
|
||||
|
||||
<div ng-controller="MainCtrl">
|
||||
<input type="file" import-sheet-js="" opts="gridOptions" multiple="false" />
|
||||
<div id="grid1" ui-grid="gridOptions" ui-grid-selection ui-grid-exporter class="grid"></div>
|
||||
</div>
|
||||
|
||||
<script src="app.js"></script>
|
||||
<script type="text/javascript">
|
||||
/* eslint no-use-before-define:0 */
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-36810333-1']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1,75 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com -->
|
||||
<!-- vim: set ts=2: -->
|
||||
<html ng-app="sjs">
|
||||
<head>
|
||||
<title>SheetJS + AngularJS</title>
|
||||
<!-- Angular -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||
|
||||
<!-- SheetJS js-xlsx library -->
|
||||
<script src="shim.js"></script>
|
||||
<script src="xlsx.full.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre>
|
||||
<b><a href="http://sheetjs.com">SheetJS + AngularJS demo</a></b>
|
||||
|
||||
The core library can be used as-is in AngularJS applications.
|
||||
The <a href="https://github.com/sheetjs/js-xlsx">Community Edition README</a> details some common use cases.
|
||||
We also have some <a href="http://sheetjs.com/demos/">more public demos</a>
|
||||
|
||||
This demo shows:
|
||||
- $http request for XLSX file and scope update with data
|
||||
- HTML table using ng-repeat
|
||||
- XLSX table export using `XLSX.utils.table_to_book`
|
||||
|
||||
<a href="https://sheetjs.com/pres.xlsx">Sample Spreadsheet</a>
|
||||
</pre>
|
||||
|
||||
<div ng-controller="sheetjs">
|
||||
|
||||
<table id="sjs-table">
|
||||
<tr><th>Name</th><th>Index</th></tr>
|
||||
<tr ng-repeat="row in data">
|
||||
<td>{{row.Name}}</td>
|
||||
<td>{{row.Index}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<button id="exportbtn">Export Table</button>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var app = angular.module('sjs', []);
|
||||
app.controller('sheetjs', function($scope, $http) {
|
||||
$http({
|
||||
method:'GET',
|
||||
url:'https://sheetjs.com/pres.xlsx',
|
||||
responseType:'arraybuffer'
|
||||
}).then(function(data) {
|
||||
var wb = XLSX.read(data.data, {type:"array"});
|
||||
var d = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
$scope.data = d;
|
||||
}, function(err) { console.log(err); });
|
||||
});
|
||||
exportbtn.addEventListener('click', function() {
|
||||
var wb = XLSX.utils.table_to_book(document.getElementById('sjs-table'));
|
||||
XLSX.writeFile(wb, "export.xlsx");
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
/* eslint no-use-before-define:0 */
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-36810333-1']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
1
demos/angular/shim.js
vendored
1
demos/angular/shim.js
vendored
@ -1 +0,0 @@
|
||||
../../shim.js
|
1
demos/angular/xlsx.core.min.js
vendored
1
demos/angular/xlsx.core.min.js
vendored
@ -1 +0,0 @@
|
||||
../../dist/xlsx.core.min.js
|
1
demos/angular/xlsx.full.min.js
vendored
1
demos/angular/xlsx.full.min.js
vendored
@ -1 +0,0 @@
|
||||
../../dist/xlsx.full.min.js
|
@ -1,23 +0,0 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"project": {
|
||||
"name": "angular2"
|
||||
},
|
||||
"apps": [
|
||||
{
|
||||
"root": "src",
|
||||
"outDir": "dist",
|
||||
"index": "index.html",
|
||||
"main": "main.ts",
|
||||
"polyfills": "polyfills.ts",
|
||||
"test": "test.ts",
|
||||
"tsconfig": "tsconfig.app.json",
|
||||
"prefix": "app",
|
||||
"scripts": []
|
||||
}
|
||||
],
|
||||
"defaults": {
|
||||
"styleExt": "css",
|
||||
"component": {}
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"extends": [
|
||||
"eslint:recommended"
|
||||
]
|
||||
}
|
1
demos/angular2/.gitattributes
vendored
1
demos/angular2/.gitattributes
vendored
@ -1 +0,0 @@
|
||||
*.*-ng* linguist-generated=true binary
|
8
demos/angular2/.gitignore
vendored
8
demos/angular2/.gitignore
vendored
@ -1,8 +0,0 @@
|
||||
dist
|
||||
hooks
|
||||
SheetJSIonic
|
||||
SheetJSNS
|
||||
angular.json
|
||||
tsconfig.app.json
|
||||
src/polyfills.ts
|
||||
.angular
|
@ -1,45 +0,0 @@
|
||||
.PHONY: ng2 ng4 ng5 ng6 ng7 ng8 ng9 ng10 ng11 ng12 ng13
|
||||
ng2 ng4 ng5 ng6 ng7 ng8 ng9 ng10 ng11 ng12 ng13:
|
||||
rm -f angular.json tsconfig.app.json src/polyfills.ts
|
||||
cp versions/package.json-$@ package.json
|
||||
if [ -e versions/angular.json-$@ ]; then cp versions/angular.json-$@ angular.json; fi
|
||||
if [ -e versions/tsconfig.app.json-$@ ]; then cp versions/tsconfig.app.json-$@ tsconfig.app.json; fi
|
||||
if [ -e versions/polyfills.ts-$@ ]; then cp versions/polyfills.ts-$@ src/polyfills.ts; fi
|
||||
rm -rf node_modules
|
||||
if [ ! -e node_modules ]; then mkdir node_modules; fi
|
||||
npm install
|
||||
if [ ! -e node_modules/xlsx ]; then cd node_modules; ln -s ../../../ xlsx; cd -; fi
|
||||
npm run build
|
||||
|
||||
.PHONY: refresh
|
||||
refresh: ## refresh the `xlsx` symlink to force angular to rebuild
|
||||
rm -rf .angular/
|
||||
rm -f node_modules/xlsx
|
||||
cd node_modules; ln -s ../../../ xlsx; cd -
|
||||
touch node_modules/xlsx
|
||||
|
||||
.PHONY: all
|
||||
all:
|
||||
for i in 2 4 5 6 7 8 9 10 11 12 13; do make ng$$i; done
|
||||
|
||||
.PHONY: ionic
|
||||
ionic:
|
||||
bash ./ionic.sh
|
||||
|
||||
.PHONY: ios android browser
|
||||
ios browser: ionic
|
||||
cd SheetJSIonic; ionic cordova emulate $@ </dev/null; cd -
|
||||
android: ionic
|
||||
cd SheetJSIonic; ionic cordova prepare $@ </dev/null; ionic cordova emulate $@ </dev/null; cd -
|
||||
|
||||
|
||||
.PHONY: nativescript
|
||||
nativescript:
|
||||
bash ./nscript.sh
|
||||
|
||||
.PHONY: ns-ios ns-android
|
||||
ns-ios: nativescript
|
||||
cd SheetJSNS; ns run ios; cd -
|
||||
ns-android: nativescript
|
||||
cd SheetJSNS; ns run android; cd -
|
||||
|
@ -1,185 +0,0 @@
|
||||
# Angular 2+
|
||||
|
||||
The ESM build can be imported directly from TS code with:
|
||||
|
||||
```typescript
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
```
|
||||
|
||||
This demo uses an array of arrays (type `Array<Array<any>>`) as the core state.
|
||||
The component template includes a file input element, a table that updates with
|
||||
the data, and a button to export the data.
|
||||
|
||||
Other scripts in this demo show:
|
||||
- `ionic` deployment for iOS, android, and browser
|
||||
- `nativescript` deployment for iOS and android
|
||||
|
||||
## Array of Arrays
|
||||
|
||||
`Array<Array<any>>` neatly maps to a table with `ngFor`:
|
||||
|
||||
```html
|
||||
<table class="sjs-table">
|
||||
<tr *ngFor="let row of data">
|
||||
<td *ngFor="let val of row">
|
||||
{{val}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
```
|
||||
|
||||
The `aoa_to_sheet` utility function returns a worksheet. Exporting is simple:
|
||||
|
||||
```typescript
|
||||
/* generate worksheet */
|
||||
const ws: XLSX.WorkSheet = XLSX.utils.aoa_to_sheet(this.data);
|
||||
|
||||
/* generate workbook and add the worksheet */
|
||||
const wb: XLSX.WorkBook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
|
||||
|
||||
/* save to file */
|
||||
XLSX.writeFile(wb, 'SheetJS.xlsx');
|
||||
```
|
||||
|
||||
`sheet_to_json` with the option `header:1` makes importing simple:
|
||||
|
||||
```typescript
|
||||
/* <input type="file" (change)="onFileChange($event)" multiple="false" /> */
|
||||
/* ... (within the component class definition) ... */
|
||||
onFileChange(evt: any) {
|
||||
/* wire up file reader */
|
||||
const target: DataTransfer = <DataTransfer>(evt.target);
|
||||
if (target.files.length !== 1) throw new Error('Cannot use multiple files');
|
||||
const reader: FileReader = new FileReader();
|
||||
reader.onload = (e: any) => {
|
||||
/* read workbook */
|
||||
const ab: ArrayBuffer = e.target.result;
|
||||
const wb: XLSX.WorkBook = XLSX.read(ab);
|
||||
|
||||
/* grab first sheet */
|
||||
const wsname: string = wb.SheetNames[0];
|
||||
const ws: XLSX.WorkSheet = wb.Sheets[wsname];
|
||||
|
||||
/* save data */
|
||||
this.data = <AOA>(XLSX.utils.sheet_to_json(ws, {header: 1}));
|
||||
};
|
||||
reader.readAsArrayBuffer(target.files[0]);
|
||||
}
|
||||
```
|
||||
|
||||
## Switching between Angular versions
|
||||
|
||||
Modules that work with Angular 2 largely work as-is with Angular 4+. Switching
|
||||
between versions is mostly a matter of installing the correct version of the
|
||||
core and associated modules. This demo includes `package.json-angular#` files
|
||||
for every major version of Angular up to 12.
|
||||
|
||||
To test a particular Angular version, overwrite `package.json`:
|
||||
|
||||
```bash
|
||||
# switch to Angular 2
|
||||
$ cp package.json-ng2 package.json
|
||||
$ npm install
|
||||
$ ng serve
|
||||
```
|
||||
|
||||
Note: when running the demos, Angular 2 requires Node <= 14. This is due to a
|
||||
tooling issue with `ng` and does not affect browser use.
|
||||
|
||||
## XLSX Symbolic Link
|
||||
|
||||
In this tree, `node_modules/xlsx` is a link pointing back to the root. This
|
||||
enables testing the development version of the library. In order to use this
|
||||
demo in other applications, add the `xlsx` dependency:
|
||||
|
||||
```bash
|
||||
$ npm install --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
|
||||
```
|
||||
|
||||
## SystemJS Configuration
|
||||
|
||||
The default angular-cli configuration requires no additional configuration.
|
||||
|
||||
Some deployments use the SystemJS loader, which does require configuration. The
|
||||
SystemJS example shows the required meta and map settings:
|
||||
|
||||
```js
|
||||
SystemJS.config({
|
||||
meta: {
|
||||
'xlsx': {
|
||||
exports: 'XLSX' // <-- tell SystemJS to expose the XLSX variable
|
||||
}
|
||||
},
|
||||
map: {
|
||||
'xlsx': 'xlsx.full.min.js', // <-- make sure xlsx.full.min.js is in same dir
|
||||
'fs': '', // <--|
|
||||
'crypto': '', // <--| suppress native node modules
|
||||
'stream': '' // <--|
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Ionic
|
||||
|
||||
<img src="screen.png" width="400px"/>
|
||||
|
||||
Reproducing the full project is a little bit tricky. The included `ionic.sh`
|
||||
script performs the necessary installation steps.
|
||||
|
||||
`Array<Array<any>>` neatly maps to a table with `ngFor`:
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row *ngFor="let row of data">
|
||||
<ion-col *ngFor="let val of row">
|
||||
{{val}}
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
```
|
||||
|
||||
|
||||
`@ionic-native/file` reads and writes files on devices. `readAsArrayBuffer`
|
||||
returns `ArrayBuffer` objects suitable for `array` type, and `array` type can
|
||||
be converted to blobs that can be exported with `writeFile`:
|
||||
|
||||
```typescript
|
||||
/* read a workbook */
|
||||
const ab: ArrayBuffer = await this.file.readAsArrayBuffer(url, filename);
|
||||
const wb: XLSX.WorkBook = XLSX.read(bstr, {type: 'array'});
|
||||
|
||||
/* write a workbook */
|
||||
const wbout: ArrayBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
|
||||
let blob = new Blob([wbout], {type: 'application/octet-stream'});
|
||||
this.file.writeFile(url, filename, blob, {replace: true});
|
||||
```
|
||||
|
||||
## NativeScript
|
||||
|
||||
Reproducing the full project is a little bit tricky. The included `nscript.sh`
|
||||
script performs the necessary installation steps and adds the necessary shims
|
||||
for `async` support. Due to incompatibilities with NativeScript and TypeScript
|
||||
definitions, apps should require the `xlsx.full.min.js` file directly:
|
||||
|
||||
```typescript
|
||||
const XLSX = require("./xlsx.full.min.js");
|
||||
```
|
||||
|
||||
The `ISO_8859_1` encoding from the text module specifies `"binary"` strings.
|
||||
`File#readText` and `File#writeText` reads and writes files:
|
||||
|
||||
```typescript
|
||||
/* read a workbook */
|
||||
const bstr: string = await file.readText(encoding.ISO_8859_1);
|
||||
const wb = XLSX.read(bstr, { type: "binary" });
|
||||
|
||||
/* write a workbook */
|
||||
const wbout: string = XLSX.write(wb, { bookType: 'xlsx', type: 'binary' });
|
||||
await file.writeText(wbout, encoding.ISO_8859_1);
|
||||
```
|
||||
|
||||
Note: some versions of NativeScript do not properly support typed arrays or
|
||||
binary strings. See <https://github.com/NativeScript/NativeScript/issues/9586>
|
||||
|
||||
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
|
@ -1,22 +0,0 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* vim: set ts=2: */
|
||||
/* NOTE: this file exists because `File` must be added as a provider */
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { RouteReuseStrategy } from '@angular/router';
|
||||
|
||||
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
|
||||
import { File } from '@ionic-native/file/ngx';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
entryComponents: [],
|
||||
imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
|
||||
providers: [File, { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user