@quario/pdf 0.7.0 → 0.8.0

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 CHANGED
@@ -1,39 +1,51 @@
1
- # Changelog
1
+ # @quario/pdf
2
2
 
3
- All notable changes to @quario/pdf are documented in this file.
3
+ ## 0.8.0
4
4
 
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
+ ### Minor Changes
7
6
 
8
- ## [Unreleased]
7
+ - **An image's box is now as wide as the container it sits in.** A
8
+ `background` or `border*` on an image item hugged the picture and now spans
9
+ the content width, or the slot's share inside a split; the picture itself
10
+ does not move. See the `@quario/layout` changelog for the rule and for what
11
+ it costs a report that relied on a border hugging a logo.
9
12
 
10
- ## [0.7.0] - 2026-09-07
13
+ ### Patch Changes
11
14
 
12
- ### Added
15
+ - **Every published README says where the documentation is.** Each package now
16
+ carries a Documentation section pointing at the reference, at the report schema
17
+ that normatively specifies what a report may declare, and at the package's own
18
+ API. The paragraphs that used to end on an unstated contract — the event
19
+ stream's field semantics, the style vocabulary, page columns, the Content
20
+ Security Policy a fragment with images needs, the formula mangling, and each
21
+ target's own contract — link the page that states it. Every link is an absolute
22
+ URL, so it resolves from the npm package page as readily as from an installed
23
+ copy.
24
+ - Updated dependencies
25
+ - @quario/layout@0.5.0
26
+ - quario@0.8.0
27
+
28
+ ## 0.7.0
29
+
30
+ ### Minor Changes
13
31
 
14
32
  - **Styled runs reach the page.** A cell value written as a list of styled runs
15
33
  draws each run in its own face, size and colour, underlines and strikes
16
34
  through per run rather than across the line, and paints a run's `background`
17
35
  as a highlight behind its text.
18
-
19
- ### Changed
20
-
21
36
  - **A `format` inside a sentence now presents where the engine says it does.** A
22
37
  cell mixing literal text and one interpolation under a `format` draws the
23
38
  value plainly, as the spreadsheet and CSV targets already did; splitting the
24
39
  value into styled runs is how a value inside a sentence is formatted.
25
40
 
26
- ## [0.6.0] - 2026-09-07
41
+ ## 0.6.0
27
42
 
28
- ### Added
43
+ ### Minor Changes
29
44
 
30
45
  - **A cell's own `currency` code is honoured**, ahead of the instance's
31
46
  default, so a listing whose rows arrive in different currencies presents each
32
47
  in its own. A code the engine could not accept leaves the cell as plain
33
48
  display text rather than presenting it in the instance's currency.
34
-
35
- ### Changed
36
-
37
49
  - **Numbers presented through `format` now show a fixed two fraction digits,
38
50
  matching every other target.** `format: "number"` on `1000` renders
39
51
  `1,000.00` where it rendered `1,000`, and `0.12345` renders `0.12` where it
@@ -42,9 +54,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
42
54
  so a JPY amount loses the two decimals it never had. `date` is unchanged, as
43
55
  are grouping separators and symbol placement. The digits come from the
44
56
  engine, so a cell reads the same here as it does in a workbook.
45
-
46
- ### Fixed
47
-
48
57
  - **An image these bytes cannot embed now fails with the item's name on it.**
49
58
  A PNG whose pixel data is corrupt past the size in its header used to reject
50
59
  the render with `Invalid typed array length: 0` — the PDF writer's own words,
@@ -54,9 +63,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
54
63
  account after it and on `cause`. A JPEG is unchanged: its header is read but
55
64
  its pixel data never is, so a corrupt one still embeds without complaint.
56
65
 
57
- ## [0.5.0] - 2026-09-05
66
+ ## 0.5.0
58
67
 
59
- ### Added
68
+ ### Minor Changes
60
69
 
61
70
  - **A spanning cell draws as one box** across the columns it covers, through
62
71
  `@quario/layout`. It takes no part in measuring them, and a spanning row that
@@ -64,16 +73,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
64
73
  each slice lands in.
65
74
  - **`valign`** on table cells and split slots, through `@quario/layout`: middle
66
75
  and bottom place the content in the height its row or split leaves over it.
67
-
68
- ### Changed
69
-
70
76
  - **A row's box is drawn by the row's cells**, through `@quario/layout`. A box
71
77
  declared on a table row used to be one rect across the columns; each covered
72
78
  cell now draws its own. A row's `borderBottom` still reads as one continuous
73
79
  edge, a row's `borderLeft` becomes an edge on every cell rather than one at
74
80
  the row's outer left, and a bordered row is taller by its border's width,
75
81
  since a border occupies height as a cell's always has.
76
-
77
82
  - **Embedding TrueType fonts now needs `fontkit`, not `@pdf-lib/fontkit`.**
78
83
  Install `fontkit` instead; nothing else about `options.fonts` changes. The
79
84
  old package's bundle crashed with a bare `ReferenceError` on any OpenType
@@ -81,28 +86,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
81
86
  Tamil, Khmer or Myanmar webfont in practice, and some fifty further scripts
82
87
  besides. Those faces now measure, embed and draw. Which scripts a face
83
88
  supports remains the font's and the parser's to answer, not this package's.
84
-
85
89
  - **`pdf-lib` is now `@cantoo/pdf-lib`.** A maintained fork, and what
86
90
  `fontkit`'s subsetting requires. Rendered documents are unchanged in what
87
91
  they draw: every drawn string is identical and every filled area lands in
88
92
  the same place, but the file's bytes differ, so a host comparing digests
89
93
  against stored output will see them move once.
90
94
 
91
- ## [0.4.0] - 2026-09-03
95
+ ## 0.4.0
92
96
 
93
- ### Changed
97
+ ### Minor Changes
94
98
 
95
99
  - **A split slot's box now fills the split's height.** A slot's background and
96
100
  border stopped at that slot's own content and now take the split's height,
97
101
  the way a table cell's take their row's. See the `@quario/layout` changelog
98
102
  for the rule and for what it costs a report that relied on the short box.
99
-
100
103
  - **A date string under `format: "date"` now presents as a date.** The
101
104
  drawn text comes from the engine's `format()` helper, which revives the two
102
105
  read forms. See the `quario` changelog for the forms and the timezone rule.
103
-
104
- ### Changed
105
-
106
106
  - **The layout moved to `@quario/layout`; this target paints its list.**
107
107
  Measurement, wrapping, pagination, page furniture, the marking's geometry
108
108
  and the page-size table now live in the layout package, which this target
@@ -112,16 +112,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
112
112
  options are the layout's own, so the same object configures `pdf()`, the
113
113
  viewer and the editor. The font mapping's shape is now refused at the
114
114
  factory call rather than at render.
115
-
116
- ### Fixed
117
-
118
115
  - **A nested group's footer keeps the page columns.** Inside a page-column
119
116
  region, the first footer of a group nested in it ended the region: the
120
117
  strips collapsed and every band after it was laid out across the page, as
121
118
  if `columns` were absent. Only the node that declared `columns` closes its
122
119
  own region now — a nested group's header and footer are region content,
123
120
  laid out in the strips.
124
-
125
121
  - **A padded or bordered item or row that breaks across a page keeps its
126
122
  box.** Content too tall for one page is drawn as slices, and a slice now
127
123
  carries the box sides the break left it — the top on the first, the bottom
@@ -133,60 +129,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
133
129
  the horizontal padding they were wrapped for, rather than flush to the
134
130
  column. **This changes output** for reports where such content breaks.
135
131
 
136
- ## [0.3.0] - 2026-09-02
132
+ ## 0.3.0
137
133
 
138
- ### Added
134
+ ### Minor Changes
139
135
 
140
136
  - **`format` stringifies at the edge from the instance locale.** A PDF
141
137
  without a host locale still uses `en-US` / UTC, so the bytes stay
142
138
  reproducible.
143
-
144
139
  - **A height-declared report header pins from the page top.** Leftover sits
145
140
  under the items; the next band starts at the pin. The half-line group gap
146
141
  drops after it. `page.margin` on the document is the inset when the host
147
142
  omits it; both is a render error.
148
-
149
143
  - **`spaceBefore` / `spaceAfter` skip the cursor.** Adjacent gaps add.
150
144
  `spaceBefore` drops at a fresh body page or strip top; page-band items
151
145
  keep it.
152
-
153
146
  - **The box is honoured in layout.** Padding and border inset wrap width; a
154
147
  named padding `0` beats the cell omakase (`PADX` 6 / `PADY` 2) on that
155
148
  side. Incomplete sides draw nothing.
156
-
157
- ### Changed
158
-
159
149
  - **The last data row keeps the whole emitted total block.** A stack taller
160
150
  than a page degrades like a tall row.
161
-
162
151
  - **An unstyled table has no rules.** This target used to draw two 0.5 pt
163
152
  rules, under the header and above the total, that no declaration could
164
153
  ask for or refuse. Those strokes are gone. The strokes an author wants
165
154
  are the box they declared. Cell padding omakase stays on undeclared cell
166
155
  sides.
167
-
168
156
  - **A visible text item occupies a line at its own `size`, empty or not.** An
169
157
  empty or whitespace-only value used to sit at the report's base leading
170
158
  (~14 pt at the 10 pt baseline). It now occupies `1.4 ×` the item's size, the
171
159
  same as a glyph line. A literal newline is a line break; the blank line
172
160
  among `"a\n\nb"` is that size too. A table cell that is empty or only
173
161
  horizontal whitespace still has no content height.
174
-
175
- ### Fixed
176
-
177
162
  - **A missing TrueType glyph draws as `?`, not `.notdef`.** Characters
178
163
  outside WinAnsi already substituted `?`; an embedded face's cmap holes
179
164
  drew a box instead, and text extraction hid it. Same rule for every
180
165
  face: a character the face cannot draw becomes `?`.
181
-
182
166
  - **CR, LF, and CRLF are one hard line break.** Cell text already broke
183
167
  on LF; a CRLF left a CR on the previous line, and a lone CR did not
184
168
  break. All three are now one break, matching `SCHEMA.md` Cell values.
185
169
  Wrapping still applies within each line.
186
170
 
187
- ## [0.2.0] - 2026-09-01
171
+ ## 0.2.0
188
172
 
189
- ### Added
173
+ ### Minor Changes
190
174
 
191
175
  - **The report default sets the document's face and base size.** A report's
192
176
  top-level `style` replaces this target's own baseline, so its `size` scales
@@ -194,12 +178,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
194
178
  nothing is set in. It is the layer _under_ the band-role defaults: a report
195
179
  declaring `size: 12` still renders its report header at 14, and an item's own
196
180
  style wins over both.
197
-
198
181
  - **`uppercase` draws capitals.** With no text-transform to defer to, this
199
182
  target capitalises the string before measuring it, so wrapping and column
200
183
  widths are those of the text actually drawn. The mapping is Unicode default
201
184
  case, never the host's locale, so output stays byte-reproducible.
202
-
203
185
  - **Splits lay out across the content width.** Each slot is measured and
204
186
  wrapped inside its own share, all slots draw from a common top, and the
205
187
  split takes the height of its tallest slot so the band below clears them
@@ -208,27 +190,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
208
190
  any page renders in full past the bottom margin. The split's own style is
209
191
  the layer under each slot's, and its `background` fills the whole split
210
192
  behind them.
211
-
212
- ### Changed
213
-
214
193
  - **A bare `Date` draws as ISO 8601 UTC, the same on every machine.** Cell
215
194
  text and outline bookmark titles for `Date` values used `String(date)`,
216
195
  which bakes the host's timezone and locale into the document — at odds with
217
196
  this target's byte-reproducibility guarantee. Both now render through the
218
197
  engine's shared display rule, so a `Date` group key titles its bookmark
219
198
  with the same ISO text its cells draw.
220
-
221
- ### Removed
222
-
223
199
  - **`options.baseSize`.** A document's type size is the document's own, so it
224
200
  is the report's `style.size` — portable, travelling with the definition to
225
201
  every target — rather than a host option one target honoured. Text with
226
202
  nothing declared still renders at 10 points. Replace `pdf({ baseSize: 11 })`
227
203
  with `"style": { "size": 11 }` on the report.
228
204
 
229
- ## [0.1.0] - 2026-08-27
205
+ ## 0.1.0
230
206
 
231
- ### Added
207
+ ### Minor Changes
232
208
 
233
209
  - **Paginated PDFs from the same compiled report.** The band flow places
234
210
  headers, groups, and tables; pdf-lib writes the file. Page furniture and an
package/README.md CHANGED
@@ -198,6 +198,17 @@ per-page presence is normative; exact geometry is best-effort. A licensed render
198
198
  Paper size and fonts are options, not schema; the margin is an option a document may declare in
199
199
  its stead. Authored watermarks and finer break control stay out of scope. Keep-together and continuation headers are by construction, above.
200
200
 
201
+ The full contract is [The PDF target](https://getquario.com/docs/diving-deeper/pdf-target/),
202
+ with each declaration's fate in the
203
+ [support matrix](https://getquario.com/docs/reference/support-matrix/).
204
+
205
+ ## Documentation
206
+
207
+ [The quario documentation](https://getquario.com/docs/) is the reference.
208
+ The [report schema](https://getquario.com/docs/reference/report-schema/) is the normative
209
+ specification of what a report may declare, and
210
+ [`@quario/pdf`](https://getquario.com/docs/reference/pdf/) is this package's own API.
211
+
201
212
  ## License
202
213
 
203
214
  Commercial software with readable source. Free, unlimited, watermarked evaluation; per-developer
package/lib/painter.js CHANGED
@@ -80,6 +80,7 @@ let paintLine = ({ page, height }, op) =>
80
80
  });
81
81
 
82
82
  /** @type {(painter: Painter, op: any) => void} */
83
+ // fallow-ignore-next-line code-duplication -- one operator path, so a face is embedded once per page; ink, baseline, rotation and opacity all differ, and the marking's draw stays its own
83
84
  let paintText = ({ page, height, fonts, faceKey }, op) => {
84
85
  let font = fontFor(fonts, op.font);
85
86
  page.pushOperators(
@@ -100,6 +101,7 @@ let paintText = ({ page, height, fonts, faceKey }, op) => {
100
101
  // face reuses the page's `faceKey` resource instead of growing the dictionary
101
102
  // per draw.
102
103
  /** @type {(painter: Painter, op: any) => void} */
104
+ // fallow-ignore-next-line code-duplication -- one operator path, so a face is embedded once per page; ink, baseline, rotation and opacity all differ, and the marking's draw stays its own
103
105
  let paintMark = ({ page, height, fonts, faceKey }, op) => {
104
106
  let font = fontFor(fonts, op.font);
105
107
  page.pushOperators(
@@ -126,7 +128,7 @@ let paintMark = ({ page, height, fonts, faceKey }, op) => {
126
128
  // The embedders parse the bytes, and what they throw when those bytes are bad
127
129
  // says nothing an author could act on: a corrupt PNG used to reach a host as a
128
130
  // bare `RangeError: Invalid typed array length: 0`, naming no image and no
129
- // item (quario-e0bz). The engine vouched for the magic numbers and the layout
131
+ // item (quario-e0bz). The engine vouched for the magic numbers and
130
132
  // read the size out of the header, so a file corrupt past that point arrives
131
133
  // here looking like any other and this is the first place that knows. The
132
134
  // embedder's own class and words survive behind the path, which is the shape
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quario/pdf",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "The browserless, paginated PDF render target for quario — in the makings, not yet released",
5
5
  "homepage": "https://getquario.com",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -37,20 +37,20 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@cantoo/pdf-lib": "^2.9.1",
40
- "@quario/layout": "^0.4.0"
40
+ "@quario/layout": "^0.5.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@arethetypeswrong/cli": "^0.18.3",
44
44
  "@size-limit/preset-small-lib": "^13.0.3",
45
45
  "@types/fontkit": "^2.0.9",
46
46
  "fontkit": "^2.0.4",
47
- "quario": "^0.7.0",
47
+ "quario": "^0.8.0",
48
48
  "size-limit": "^13.0.3",
49
49
  "typescript": "^7.0.2"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "fontkit": "^2.0.4",
53
- "quario": "^0.7.0"
53
+ "quario": "^0.8.0"
54
54
  },
55
55
  "peerDependenciesMeta": {
56
56
  "fontkit": {