@quario/xlsx 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -48
- package/README.md +18 -4
- package/lib/index.js +9 -11
- package/lib/style.js +1 -0
- package/package.json +3 -3
- package/lib/image.js +0 -45
package/CHANGELOG.md
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @quario/xlsx
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## 0.7.1
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
|
+
### Patch Changes
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
- **Every published README says where the documentation is.** Each package now
|
|
8
|
+
carries a Documentation section pointing at the reference, at the report schema
|
|
9
|
+
that normatively specifies what a report may declare, and at the package's own
|
|
10
|
+
API. The paragraphs that used to end on an unstated contract — the event
|
|
11
|
+
stream's field semantics, the style vocabulary, page columns, the Content
|
|
12
|
+
Security Policy a fragment with images needs, the formula mangling, and each
|
|
13
|
+
target's own contract — link the page that states it. Every link is an absolute
|
|
14
|
+
URL, so it resolves from the npm package page as readily as from an installed
|
|
15
|
+
copy.
|
|
16
|
+
- Updated dependencies
|
|
17
|
+
- quario@0.8.0
|
|
9
18
|
|
|
10
|
-
##
|
|
19
|
+
## 0.7.0
|
|
11
20
|
|
|
12
|
-
###
|
|
21
|
+
### Minor Changes
|
|
13
22
|
|
|
14
23
|
- **Styled runs become rich text.** A cell of more than one styled run is written
|
|
15
24
|
as rich text, each run's font merged over the cell's — so bold, italic,
|
|
@@ -18,14 +27,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
18
27
|
only, and filling the cell would colour text nobody asked to colour. A cell of
|
|
19
28
|
exactly one run stays a typed cell, so a single styled amount is still a
|
|
20
29
|
number.
|
|
21
|
-
|
|
22
30
|
- **A run inside a sentence presents its value.** A run holding one
|
|
23
31
|
interpolation under a `format` shows the same decimals here as on the page,
|
|
24
32
|
where a mixed cell used to show the bare value.
|
|
25
33
|
|
|
26
|
-
##
|
|
34
|
+
## 0.6.0
|
|
27
35
|
|
|
28
|
-
###
|
|
36
|
+
### Minor Changes
|
|
29
37
|
|
|
30
38
|
- **A date cell's number format follows the `form` its report declares.** The
|
|
31
39
|
four forms map to `dd/mm/yy`, `dd mmm yyyy`, `dd mmmm yyyy` and
|
|
@@ -33,26 +41,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
33
41
|
weekday name is spelled in stays the reader's, because that is what a
|
|
34
42
|
spreadsheet application supplies and pinning it would stop the file reading
|
|
35
43
|
naturally for whoever opens it.
|
|
36
|
-
|
|
37
44
|
- **A declared digit count builds the pattern.** A `number` cell asking for
|
|
38
45
|
three digits writes `#,##0.000`, and a `currency` cell asking for none writes
|
|
39
46
|
`"EUR"#,##0` — the same count the page presents, taken off the declaration
|
|
40
47
|
the engine resolved rather than computed here.
|
|
41
|
-
|
|
42
48
|
- **A cell's own `currency` code picks the number format**, ahead of the
|
|
43
49
|
instance's default: `"JPY"#,##0` where the row says `JPY`, since the code
|
|
44
50
|
decides the digits too. The cell stays a number either way. A code the engine
|
|
45
51
|
could not accept writes no number format at all, rather than labelling the
|
|
46
52
|
cell in the instance's currency.
|
|
47
|
-
|
|
48
|
-
### Changed
|
|
49
|
-
|
|
50
53
|
- **A `date` cell with no declared form now writes `dd mmm yyyy`, not
|
|
51
54
|
`yyyy-mm-dd`.** A bare `date` means the `medium` form throughout, so the grid
|
|
52
55
|
agrees with the page. A report that wants the ISO shape has no form for it;
|
|
53
56
|
the nearest is `{ "kind": "date", "form": "short" }`, which writes
|
|
54
57
|
`dd/mm/yy`. The cell is still a real typed date either way.
|
|
55
|
-
|
|
56
58
|
- **A cell's number format now comes from the engine's fraction-digit count
|
|
57
59
|
rather than from four patterns this target held of its own.** The visible
|
|
58
60
|
change is `currency`: the pattern followed the currency's own minor units,
|
|
@@ -61,7 +63,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
61
63
|
(`#,##0.00`), `percent` (`0.00%`) and `date` (`yyyy-mm-dd`) are unchanged in
|
|
62
64
|
a worksheet; what changed for those is that HTML and PDF now show the same
|
|
63
65
|
decimals the grid always did, instead of their own.
|
|
64
|
-
|
|
65
66
|
- A `currency` code that is not a readable currency no longer produces a number
|
|
66
67
|
format at all. The cell keeps its value; before, the code was pasted straight
|
|
67
68
|
into a pattern while every other target fell back to the unformatted value.
|
|
@@ -70,39 +71,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
70
71
|
position still come from the application that opens the file, which is what
|
|
71
72
|
lets one workbook read naturally wherever it is opened.
|
|
72
73
|
|
|
73
|
-
### Fixed
|
|
74
|
-
|
|
75
74
|
- A `format` kind reaching this target from an `=` expression is now looked up
|
|
76
75
|
as an own key. Before, a kind resolving to the name of a built-in object
|
|
77
76
|
member threw out of the render (`valueOf`, `hasOwnProperty`) or wrote
|
|
78
77
|
`[object Undefined]` into the cell's number format (`toString`). A literal
|
|
79
78
|
was never affected: it is checked against the four kinds.
|
|
80
|
-
|
|
81
|
-
### Fixed
|
|
82
|
-
|
|
83
79
|
- **An image whose size cannot be read now names the item that asked for it.**
|
|
84
80
|
The failure said only that the size could not be read from the bytes, so a
|
|
85
81
|
report with two pictures gave no way to tell which one was bad. The message
|
|
86
82
|
now begins with the item's `source` path, as every other render error does
|
|
87
83
|
and as it does from every other target.
|
|
88
84
|
|
|
89
|
-
##
|
|
85
|
+
## 0.5.0
|
|
90
86
|
|
|
91
|
-
###
|
|
87
|
+
### Minor Changes
|
|
92
88
|
|
|
93
89
|
- **Chrome merges, data does not.** A cell spanning several columns in the
|
|
94
90
|
header row or a total row is written into the first column it covers, the
|
|
95
91
|
rest of the range is written empty, and the range is merged. Nothing in the
|
|
96
92
|
data region merges, so sort, filter and column selection keep working over
|
|
97
93
|
it.
|
|
98
|
-
|
|
99
94
|
- **`valign`** writes the cell's vertical alignment for table cells and split
|
|
100
95
|
slots. Undeclared writes nothing, so the spreadsheet application keeps its
|
|
101
96
|
own default.
|
|
102
97
|
|
|
103
|
-
##
|
|
98
|
+
## 0.4.0
|
|
104
99
|
|
|
105
|
-
###
|
|
100
|
+
### Minor Changes
|
|
106
101
|
|
|
107
102
|
- **A date string under `format: "date"` now reaches the grid as a date.**
|
|
108
103
|
The cell carries a real date value under the `yyyy-mm-dd` number format
|
|
@@ -110,83 +105,68 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
110
105
|
cell declaring the kind is affected; a string in a form the engine does not
|
|
111
106
|
read stays text. See the `quario` changelog for the forms.
|
|
112
107
|
|
|
113
|
-
##
|
|
108
|
+
## 0.3.0
|
|
114
109
|
|
|
115
|
-
###
|
|
110
|
+
### Minor Changes
|
|
116
111
|
|
|
117
112
|
- **`format` maps to a number format; the cell stays typed.** `number` is
|
|
118
113
|
`#,##0.00`, `percent` `0.00%`, `date` `yyyy-mm-dd`, `currency` the
|
|
119
114
|
instance currency code as `"USD"#,##0.00`. A kind on the wrong type
|
|
120
115
|
contributes nothing.
|
|
121
|
-
|
|
122
116
|
- **Cell borders map; padding and flow spacing do not.** `solid` is exceljs
|
|
123
117
|
`thin`; `dashed` and `dotted` keep their names. A header-row, row, or
|
|
124
118
|
total-row box fans onto that row's cells; a cell that named any of a
|
|
125
119
|
side's three keys owns that side whole. Padding is unread: a worksheet
|
|
126
120
|
cell has no inset. Flow spacing is unread: a grid has no flow.
|
|
127
|
-
|
|
128
121
|
- **`page.margin` and report-header `height` are unread.** A grid has no
|
|
129
122
|
page top to pin from.
|
|
130
|
-
|
|
131
|
-
### Changed
|
|
132
|
-
|
|
133
123
|
- **A table total emits N rows.** Each `total-row` is one worksheet row, as
|
|
134
124
|
each data row is.
|
|
135
|
-
|
|
136
125
|
- **A cell whose display contains a newline wraps.** `wrapText` is this
|
|
137
126
|
target's mapping of a literal newline as a line break, so `"one\ntwo"` is
|
|
138
127
|
two lines in the grid. Single-line cells are unchanged.
|
|
139
128
|
|
|
140
|
-
##
|
|
129
|
+
## 0.2.0
|
|
141
130
|
|
|
142
|
-
###
|
|
131
|
+
### Minor Changes
|
|
143
132
|
|
|
144
133
|
- **The report default reaches every cell.** A report's top-level `style`
|
|
145
134
|
replaces this target's baseline, so a document declaring a face and size gets
|
|
146
135
|
them in cells that declare nothing of their own. It is the layer under the
|
|
147
136
|
band-role defaults — a report declaring `size: 12` still writes its report
|
|
148
137
|
header at 14 — and under each cell's own style.
|
|
149
|
-
|
|
150
138
|
- **`uppercase` is accepted and deliberately not read.** A spreadsheet font
|
|
151
139
|
has no text-transform, and writing capitals into the cell instead would turn
|
|
152
140
|
presentation into data — the cell would stop round-tripping and would sort
|
|
153
141
|
differently. The cell keeps the text you wrote; every other declaration on
|
|
154
142
|
it still applies. Same posture as the column widths this target withdrew.
|
|
155
|
-
|
|
156
143
|
- **A split is one row, its slots the cells across it.** The grid's own
|
|
157
144
|
reading of values placed beside each other. Slot `width` shares go unread,
|
|
158
145
|
on exactly the ground the table's column widths do — a worksheet's columns
|
|
159
146
|
are global to the sheet. The split's own style is what its slots sit under,
|
|
160
147
|
layering the way a table row's does, and a slot that renders nothing writes
|
|
161
148
|
an empty cell so the cells either side keep their columns.
|
|
162
|
-
|
|
163
|
-
### Changed
|
|
164
|
-
|
|
165
149
|
- **Every cell is written at 10 points unless something declares otherwise.**
|
|
166
150
|
The size was previously left to the writer's own default of 11. Now that a
|
|
167
151
|
report can declare its own size, this target and the PDF carrying different
|
|
168
152
|
numbers for one declaration is exactly what the targets' agreement rule
|
|
169
153
|
forbids. A worksheet's row height follows its font size, so existing unstyled
|
|
170
154
|
sheets come out slightly tighter.
|
|
171
|
-
|
|
172
155
|
- **Display-text `Date`s join as ISO 8601 UTC.** A `Date` inside a mixed
|
|
173
156
|
cell (literal text plus interpolation) joined through `String(date)`, which
|
|
174
157
|
bakes the host's timezone and locale into the worksheet. It now joins
|
|
175
158
|
through the engine's shared display rule as `toISOString()` text. Typed
|
|
176
159
|
cells are untouched: a cell that is one bare `Date` interpolation still
|
|
177
160
|
writes a native date.
|
|
178
|
-
|
|
179
|
-
### Fixed
|
|
180
|
-
|
|
181
161
|
- **Cells carry their font face explicitly.** Text declaring no `family` used
|
|
182
162
|
to inherit whatever the workbook writer defaulted to, which agreed with the
|
|
183
163
|
other targets only by coincidence. It now writes the same face that
|
|
184
164
|
`family: "sans"` resolves to, so the baseline is a rule rather than a
|
|
185
165
|
property of the library underneath.
|
|
186
166
|
|
|
187
|
-
##
|
|
167
|
+
## 0.1.0
|
|
188
168
|
|
|
189
|
-
###
|
|
169
|
+
### Minor Changes
|
|
190
170
|
|
|
191
171
|
- **A workbook from the same compiled report.** Bands become rows; a bare
|
|
192
172
|
interpolation that is a number, boolean, or Date stays that type. The first
|
package/README.md
CHANGED
|
@@ -110,8 +110,10 @@ on the wrong type, or a code the engine cannot read, writes no format.
|
|
|
110
110
|
|
|
111
111
|
An image item is not a cell: it takes one worksheet row as its anchor and floats over the sheet as a
|
|
112
112
|
drawing at its natural pixel size. Placement here is best-effort. `fit`, `alt`, and `style` are
|
|
113
|
-
unused, and all of it may change without a breaking change
|
|
114
|
-
|
|
113
|
+
unused, and all of it may change without a breaking change (see the
|
|
114
|
+
[support matrix](https://getquario.com/docs/reference/support-matrix/#output)). Bytes too short to
|
|
115
|
+
carry a size never reach this target: the engine reads every image's size and rejects them for
|
|
116
|
+
every target alike.
|
|
115
117
|
|
|
116
118
|
### Styles
|
|
117
119
|
|
|
@@ -145,8 +147,9 @@ table for a share to be a share of.
|
|
|
145
147
|
|
|
146
148
|
### Page columns are withdrawn
|
|
147
149
|
|
|
148
|
-
A page column count
|
|
149
|
-
|
|
150
|
+
A [page column](https://getquario.com/docs/reference/support-matrix/#page-and-pagination) count
|
|
151
|
+
sizes and splits nothing here, and will not change, as `break: "page"` is ignored. Page columns
|
|
152
|
+
are strips of a page, and this target has no page to strip.
|
|
150
153
|
|
|
151
154
|
## Unlicensed marking
|
|
152
155
|
|
|
@@ -161,6 +164,17 @@ The document model is deterministic: the workbook's dates are pinned rather than
|
|
|
161
164
|
`meta` is opt-in. The **bytes** are not. The writer stamps archive entries with the packing time.
|
|
162
165
|
Compare extracted content, not digests.
|
|
163
166
|
|
|
167
|
+
The full contract is [The XLSX target](https://getquario.com/docs/diving-deeper/xlsx-target/),
|
|
168
|
+
with each declaration's fate in the
|
|
169
|
+
[support matrix](https://getquario.com/docs/reference/support-matrix/).
|
|
170
|
+
|
|
171
|
+
## Documentation
|
|
172
|
+
|
|
173
|
+
[The quario documentation](https://getquario.com/docs/) is the reference.
|
|
174
|
+
The [report schema](https://getquario.com/docs/reference/report-schema/) is the normative
|
|
175
|
+
specification of what a report may declare, and
|
|
176
|
+
[`@quario/xlsx`](https://getquario.com/docs/reference/xlsx/) is this package's own API.
|
|
177
|
+
|
|
164
178
|
## License
|
|
165
179
|
|
|
166
180
|
Commercial software with readable source. Free, unlimited, watermarked evaluation; per-developer
|
package/lib/index.js
CHANGED
|
@@ -15,10 +15,9 @@
|
|
|
15
15
|
* the packing time. Byte-for-byte output is a goal for the writer that will
|
|
16
16
|
* replace `workbook.js`.
|
|
17
17
|
*/
|
|
18
|
-
import {
|
|
18
|
+
import { walk } from "quario";
|
|
19
19
|
import { field } from "./cell.js";
|
|
20
20
|
import { merge as under } from "./style.js";
|
|
21
|
-
import { pixels } from "./image.js";
|
|
22
21
|
import { append, create, embed, freeze, mark, place, save, sheet, span } from "./workbook.js";
|
|
23
22
|
|
|
24
23
|
// The options are described once, in the hand-written public declarations, and
|
|
@@ -197,15 +196,14 @@ export function xlsx(options) {
|
|
|
197
196
|
image: (event) => {
|
|
198
197
|
let known = images.get(event.bytes);
|
|
199
198
|
if (!known) {
|
|
200
|
-
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
known = { ...size, id: embed(workbook, event.bytes, event.format) };
|
|
199
|
+
// The event states the size in pixels, which is the unit a floating
|
|
200
|
+
// drawing is placed in, so this target has nothing to work out: the
|
|
201
|
+
// engine read the header once for every target (`docs/adr/0067`).
|
|
202
|
+
known = {
|
|
203
|
+
width: event.width,
|
|
204
|
+
height: event.height,
|
|
205
|
+
id: embed(workbook, event.bytes, event.format),
|
|
206
|
+
};
|
|
209
207
|
images.set(event.bytes, known);
|
|
210
208
|
}
|
|
211
209
|
if (split) {
|
package/lib/style.js
CHANGED
|
@@ -22,6 +22,7 @@ let FAMILY = { sans: "Calibri", serif: "Times New Roman", mono: "Courier New" };
|
|
|
22
22
|
|
|
23
23
|
// A declared colour as an ARGB string, or null when the value is not one.
|
|
24
24
|
/** @type {(value: any) => string | null} */
|
|
25
|
+
// fallow-ignore-next-line code-duplication -- each target reads a declared hex into its own type; neither may import a sibling, and the engine's stream is all they share
|
|
25
26
|
let argb = (value) => {
|
|
26
27
|
let match = typeof value === "string" && HEX.exec(value);
|
|
27
28
|
if (!match) return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quario/xlsx",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "The spreadsheet render target for quario — in the makings, not yet released",
|
|
5
5
|
"homepage": "https://getquario.com",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
"@arethetypeswrong/cli": "^0.18.3",
|
|
43
43
|
"@size-limit/preset-small-lib": "^13.0.3",
|
|
44
44
|
"@types/node": "^22.20.1",
|
|
45
|
-
"quario": "^0.
|
|
45
|
+
"quario": "^0.8.0",
|
|
46
46
|
"size-limit": "^13.0.3",
|
|
47
47
|
"typescript": "^7.0.2"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"quario": "^0.
|
|
50
|
+
"quario": "^0.8.0"
|
|
51
51
|
},
|
|
52
52
|
"size-limit": [
|
|
53
53
|
{
|
package/lib/image.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* An image's own size, read from its header. A floating drawing is placed in
|
|
3
|
-
* pixels, and the event carries the bytes and the format the engine sniffed
|
|
4
|
-
* but no dimensions, so the one thing this target needs to place a picture is
|
|
5
|
-
* worked out here — from the header alone, never by decoding the image.
|
|
6
|
-
*
|
|
7
|
-
* The PDF target reads the same two headers for its own placement, in points.
|
|
8
|
-
* Restated per target rather than shared, like every other coercion at a
|
|
9
|
-
* target's edge: neither package imports the other, and the engine's stream is
|
|
10
|
-
* all they have in common.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/** @type {(bytes: Uint8Array, at: number) => number} */
|
|
14
|
-
let word = (bytes, at) => (bytes[at] << 8) | bytes[at + 1];
|
|
15
|
-
|
|
16
|
-
// The dimensions live in the frame header, the first SOFn marker: every
|
|
17
|
-
// code in C0..CF except the three in that range that are not frames --
|
|
18
|
-
// DHT, the JPG extension, and DAC.
|
|
19
|
-
/** @type {Record<number, number>} */
|
|
20
|
-
let SKIP = { 0xc4: 1, 0xc8: 1, 0xcc: 1 };
|
|
21
|
-
/** @type {(code: number) => any} */
|
|
22
|
-
let sof = (code) => code >= 0xc0 && code <= 0xcf && !SKIP[code];
|
|
23
|
-
|
|
24
|
-
/** @type {(bytes: Uint8Array) => { width: number, height: number }} */
|
|
25
|
-
let jpeg = (bytes) => {
|
|
26
|
-
for (let at = 2; at + 9 < bytes.length; at += 2 + word(bytes, at + 2)) {
|
|
27
|
-
if (bytes[at] !== 0xff) break;
|
|
28
|
-
if (sof(bytes[at + 1])) return { width: word(bytes, at + 7), height: word(bytes, at + 5) };
|
|
29
|
-
}
|
|
30
|
-
return { width: 0, height: 0 };
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* The image's size in pixels, or null when the header does not carry one —
|
|
35
|
-
* bytes the engine vouched the magic numbers of, truncated before the size.
|
|
36
|
-
*
|
|
37
|
-
* @param {Uint8Array} bytes The image file.
|
|
38
|
-
* @param {string} format The engine's sniff: `"png"` or `"jpeg"`.
|
|
39
|
-
* @returns {{ width: number, height: number } | null} The size, if readable.
|
|
40
|
-
*/
|
|
41
|
-
export let pixels = (bytes, format) => {
|
|
42
|
-
// A PNG carries the two numbers in its IHDR at a fixed offset.
|
|
43
|
-
let size = format === "png" ? { width: word(bytes, 18), height: word(bytes, 22) } : jpeg(bytes);
|
|
44
|
-
return size.width > 0 && size.height > 0 ? size : null;
|
|
45
|
-
};
|