@quario/layout 0.5.0 → 0.6.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 +45 -0
- package/README.md +28 -23
- package/lib/fonts.js +7 -3
- package/lib/index.d.ts +5 -1
- package/lib/index.js +15 -3
- package/lib/page.js +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# @quario/layout
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- **Every target factory now refuses an option it does not understand.** An unknown key, a key with
|
|
8
|
+
a value of the wrong type, or an `options` that is not an object throws a `TypeError` at the
|
|
9
|
+
factory call, naming the option path — `options.meta.title`, `options.page.size`. `@quario/docx`
|
|
10
|
+
already behaved this way. `@quario/pdf`, `@quario/html`, `@quario/xlsx` and `@quario/layout` read
|
|
11
|
+
the keys they knew and ignored the rest, so a typo cost you the option you meant to set with no
|
|
12
|
+
signal at any point. `csv()` takes no options, and it refuses an argument rather than discarding
|
|
13
|
+
one.
|
|
14
|
+
|
|
15
|
+
The check itself is one thing, so the engine now exports it: `hostOptions` closes a key set and
|
|
16
|
+
`hostMeta` validates the `{ title, author, subject }` contract the document targets share. A
|
|
17
|
+
fourth document property is one edit rather than three.
|
|
18
|
+
|
|
19
|
+
**What this changes for you.** One options object spread across several targets stops working if
|
|
20
|
+
any target does not know one of its keys — `const o = { page, fonts, meta }` passed to `pdf(o)`,
|
|
21
|
+
`html(o)` and `xlsx(o)` is three different key sets. An object holding only what its consumers
|
|
22
|
+
share is unaffected, so `{ page, fonts }` into both `pdf()` and `layout()` still works.
|
|
23
|
+
TypeScript does not warn about this: excess-property checking fires on an object literal and not
|
|
24
|
+
on a variable, so a bag held in a `const` compiles clean and throws when you call the factory.
|
|
25
|
+
|
|
26
|
+
Nullish is absence everywhere, at both levels, so `{ meta: config.meta ?? null }` and
|
|
27
|
+
`{ meta: { title: config.title } }` over a config that carries neither are both fine.
|
|
28
|
+
`html({ paths })` now takes `true` or `false` rather than anything truthy, so `paths: "no"` throws
|
|
29
|
+
instead of turning path stamping on. A `fonts` mapping given as an array is refused by
|
|
30
|
+
`@quario/html` and `@quario/layout` rather than read as families named `0`, `1`, `2`.
|
|
31
|
+
|
|
32
|
+
Every host-option failure is now a `TypeError` rather than a plain `Error`. A `catch` that tests
|
|
33
|
+
`instanceof Error` is unaffected. One that compares the constructor is not.
|
|
34
|
+
|
|
35
|
+
`@quario/docx` is relaxed in three places, each of them now accepting what it refused: `meta: null`
|
|
36
|
+
and `page: null` are absence rather than errors, a property written as `meta: { title: undefined }`
|
|
37
|
+
is a property you did not write rather than one of the wrong type, and an inherited enumerable key
|
|
38
|
+
is no longer reported as an option you wrote.
|
|
39
|
+
|
|
40
|
+
`@quario/pdf`, `@quario/xlsx` and `@quario/docx` now copy `meta` at the factory call. Mutating the
|
|
41
|
+
object you passed no longer changes what a configured target writes.
|
|
42
|
+
|
|
43
|
+
### Patch Changes
|
|
44
|
+
|
|
45
|
+
- Updated dependencies
|
|
46
|
+
- quario@0.9.0
|
|
47
|
+
|
|
3
48
|
## 0.5.0
|
|
4
49
|
|
|
5
50
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
**The paged layout of a [quario](https://www.npmjs.com/package/quario) report, as a display
|
|
4
4
|
list.** `layout(options)` is a render target whose output is pages of boxes, text runs, rules
|
|
5
|
-
and images in points
|
|
6
|
-
so the preview breaks its pages exactly where the document does.
|
|
5
|
+
and images in points. The PDF target writes that layout, and the viewer and editor paint it on
|
|
6
|
+
screen, so the preview breaks its pages exactly where the document does.
|
|
7
7
|
|
|
8
8
|
You install this package directly only to consume the list yourself. `@quario/pdf`, `@quario/viewer`
|
|
9
9
|
and `@quario/editor` depend on it and run it for you.
|
|
@@ -39,11 +39,16 @@ list.pages[0].boxes; // which schema node was drawn where
|
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
Every coordinate is in PostScript points from the page's top-left corner. `list.width` and
|
|
42
|
-
`list.height` are the paper every page is drawn on
|
|
43
|
-
own size.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
`list.height` are the paper every page is drawn on. A report has one paper, so a page does not
|
|
43
|
+
carry its own size.
|
|
44
|
+
|
|
45
|
+
A page carries:
|
|
46
|
+
|
|
47
|
+
- its `{ number, total }`,
|
|
48
|
+
- its ops: `rect`, `line`, `image`, and `text`. A `text` op holds the string to draw, the face it
|
|
49
|
+
uses, and, for a built-in family only, the per-character advances the measurer laid it out
|
|
50
|
+
with. An unlicensed render adds one `mark` op.
|
|
51
|
+
- its `boxes`, one per schema node drawn, named by the node's path.
|
|
47
52
|
|
|
48
53
|
## Options
|
|
49
54
|
|
|
@@ -52,7 +57,7 @@ named by the node's path.
|
|
|
52
57
|
| `page` | `{ size: "A4" \| "letter" \| [w, h], margin }` points | A4, 54pt |
|
|
53
58
|
| `fonts` | A font mapping: family name to TrueType bytes | none |
|
|
54
59
|
|
|
55
|
-
They are the pdf target's own `page` and `fonts
|
|
60
|
+
They are the pdf target's own `page` and `fonts`. Pass the same object to `layout()` and to
|
|
56
61
|
`pdf()` and the two agree, because the pdf target runs this layout and paints its list.
|
|
57
62
|
|
|
58
63
|
## Painting on a canvas
|
|
@@ -70,26 +75,26 @@ hit(page, x, y); // -> { path, x, y, w, h } or null
|
|
|
70
75
|
```
|
|
71
76
|
|
|
72
77
|
`paint` draws a white page and every op in order. Size the canvas to the page before you call it:
|
|
73
|
-
the ops are in the page's own coordinates, so a smaller canvas clips them, and
|
|
74
|
-
over the whole canvas.
|
|
75
|
-
|
|
78
|
+
the ops are in the page's own coordinates, so a smaller canvas clips them, and `paint` fills the
|
|
79
|
+
white over the whole canvas. `paint` decodes images from the bytes on the list. No `img-src`
|
|
80
|
+
grant, no URLs. `hit` answers which schema node was drawn at a point: the smallest box
|
|
76
81
|
containing it.
|
|
77
82
|
|
|
78
|
-
Text is drawn in the face the document will use.
|
|
79
|
-
`FontFace` from your own bytes, and
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
measured advances
|
|
83
|
-
|
|
83
|
+
Text is drawn in the face the document will use. `paint` registers a family you pass as `fonts` as
|
|
84
|
+
a `FontFace` from your own bytes, and the browser then shapes its runs. That is the same shaping
|
|
85
|
+
the PDF gets from the same file, so ligatures, joined scripts and accents look on screen the way
|
|
86
|
+
they will on paper. `paint` draws text in the built-in families character by character at the
|
|
87
|
+
measured advances. The typeface a browser has for Helvetica, Times or Courier only stands in for
|
|
88
|
+
the one the PDF writes, and without that correction a line would drift as it ran.
|
|
84
89
|
|
|
85
90
|
Inside a single run of a family you supply, the preview is not promised to place glyphs exactly
|
|
86
|
-
where the PDF does
|
|
91
|
+
where the PDF does. Both read a glyph's own advance and neither applies the font's GPOS
|
|
87
92
|
positioning, so a browser may kern by a fraction more. Where a line breaks, how wide a column is,
|
|
88
93
|
and where a page ends are the layout's — one measurer, so preview and document agree.
|
|
89
94
|
|
|
90
|
-
`PX_PER_POINT` is how big a point is on screen — 96 dpi over PostScript's 72
|
|
91
|
-
a page at 100% sizes its canvas `list.width * PX_PER_POINT` CSS pixels wide and paints
|
|
92
|
-
`PX_PER_POINT * devicePixelRatio`. Any zoom is a factor on top of that.
|
|
95
|
+
`PX_PER_POINT` is how big a point is on screen — 96 dpi over PostScript's 72. A surface that draws
|
|
96
|
+
a page at 100% therefore sizes its canvas `list.width * PX_PER_POINT` CSS pixels wide and paints
|
|
97
|
+
at `PX_PER_POINT * devicePixelRatio`. Any zoom is a factor on top of that.
|
|
93
98
|
|
|
94
99
|
## Documentation
|
|
95
100
|
|
|
@@ -100,5 +105,5 @@ specification of what a report may declare, and
|
|
|
100
105
|
|
|
101
106
|
## License
|
|
102
107
|
|
|
103
|
-
quario is commercial software
|
|
104
|
-
unlicensed. See [LICENSE](https://getquario.com/license).
|
|
108
|
+
quario is commercial software. Evaluation is free and fully featured, and quario marks its output
|
|
109
|
+
as unlicensed. See [LICENSE](https://getquario.com/license).
|
package/lib/fonts.js
CHANGED
|
@@ -165,8 +165,8 @@ let useFontkit = async () => {
|
|
|
165
165
|
let asFamily = (name, def, at) => {
|
|
166
166
|
let path = at + "." + name;
|
|
167
167
|
if (!def || typeof def !== "object")
|
|
168
|
-
throw
|
|
169
|
-
if (def.regular == null) throw
|
|
168
|
+
throw TypeError(path + ": expected { regular, bold?, italic?, boldItalic? }");
|
|
169
|
+
if (def.regular == null) throw TypeError(path + ".regular: required");
|
|
170
170
|
return path;
|
|
171
171
|
};
|
|
172
172
|
|
|
@@ -279,6 +279,10 @@ export async function loadFonts(custom) {
|
|
|
279
279
|
return { families };
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
+
// An array is a `typeof "object"` whose families would be named 0, 1, 2.
|
|
283
|
+
/** @type {(custom: any) => boolean} */
|
|
284
|
+
let record = (custom) => typeof custom === "object" && !Array.isArray(custom);
|
|
285
|
+
|
|
282
286
|
/**
|
|
283
287
|
* Check the shape of a host's font mapping without loading a parser.
|
|
284
288
|
*
|
|
@@ -295,7 +299,7 @@ export async function loadFonts(custom) {
|
|
|
295
299
|
*/
|
|
296
300
|
export let checkFonts = (custom, at = "options.fonts") => {
|
|
297
301
|
if (custom == null) return;
|
|
298
|
-
if (
|
|
302
|
+
if (!record(custom)) throw TypeError(at + ": expected a record of families");
|
|
299
303
|
for (let [name, def] of Object.entries(custom)) asFamily(name, def, at);
|
|
300
304
|
};
|
|
301
305
|
|
package/lib/index.d.ts
CHANGED
|
@@ -22,7 +22,11 @@ export interface LayoutFontFamily {
|
|
|
22
22
|
/** The font mapping: a declared `family` name to the bytes it measures against. */
|
|
23
23
|
export type LayoutFonts = Record<string, LayoutFontFamily>;
|
|
24
24
|
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Host controls, taken and validated at the factory call: an option this
|
|
27
|
+
* target does not understand, or one of the wrong type, throws a `TypeError`
|
|
28
|
+
* there rather than costing the host the option in silence.
|
|
29
|
+
*/
|
|
26
30
|
export interface LayoutOptions {
|
|
27
31
|
page?: LayoutPage;
|
|
28
32
|
/**
|
package/lib/index.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* driver dispatches to is the band flow's, next door in `layout.js`; this
|
|
21
21
|
* file adds none. The package publishes `lib/` verbatim.
|
|
22
22
|
*/
|
|
23
|
-
import { breathe, walk } from "quario";
|
|
23
|
+
import { breathe, hostOptions, walk } from "quario";
|
|
24
24
|
import { listing, stamp } from "./canvas.js";
|
|
25
25
|
import { checkFonts, loadFonts } from "./fonts.js";
|
|
26
26
|
import { flow, furniture } from "./layout.js";
|
|
@@ -58,6 +58,19 @@ let markPages = async (canvas, marking) => {
|
|
|
58
58
|
await overPages(canvas, () => canvas.watermark(mark));
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
+
// This target's whole option contract, checked at the factory call: the key
|
|
62
|
+
// sets, then the geometry and the font mapping's shape. The `page` key set
|
|
63
|
+
// closes here rather than in `pageBox`, which the two surface elements also
|
|
64
|
+
// call -- a host configures those by assigning DOM properties, and nothing can
|
|
65
|
+
// refuse an unknown one (`docs/adr/0072`).
|
|
66
|
+
/** @type {(options: any) => void} */
|
|
67
|
+
let checkOptions = (options) => {
|
|
68
|
+
hostOptions(options, ["page", "fonts"], "options");
|
|
69
|
+
hostOptions(options?.page, ["size", "margin"], "options.page");
|
|
70
|
+
geometry(options?.page);
|
|
71
|
+
checkFonts(options?.fonts);
|
|
72
|
+
};
|
|
73
|
+
|
|
61
74
|
/**
|
|
62
75
|
* The layout target:
|
|
63
76
|
* `quario().report(schema).render(layout({ page }), data)` resolves the
|
|
@@ -70,8 +83,7 @@ let markPages = async (canvas, marking) => {
|
|
|
70
83
|
* The target (see SCHEMA.md, "Instances and targets").
|
|
71
84
|
*/
|
|
72
85
|
export function layout(options) {
|
|
73
|
-
|
|
74
|
-
checkFonts(options?.fonts);
|
|
86
|
+
checkOptions(options);
|
|
75
87
|
let custom = options?.fonts;
|
|
76
88
|
/** @type {(stream: any) => (data?: any) => Promise<Layout>} */
|
|
77
89
|
let compile = (stream) => async (data) => {
|
package/lib/page.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quario/layout",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "The paged display list for quario — the layout the PDF target writes and the viewer paints — in the makings, not yet released",
|
|
5
5
|
"homepage": "https://getquario.com",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -43,13 +43,13 @@
|
|
|
43
43
|
"@size-limit/preset-small-lib": "^13.0.3",
|
|
44
44
|
"@types/fontkit": "^2.0.9",
|
|
45
45
|
"fontkit": "^2.0.4",
|
|
46
|
-
"quario": "^0.
|
|
46
|
+
"quario": "^0.9.0",
|
|
47
47
|
"size-limit": "^13.0.3",
|
|
48
48
|
"typescript": "^7.0.2"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"fontkit": "^2.0.4",
|
|
52
|
-
"quario": "^0.
|
|
52
|
+
"quario": "^0.9.0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependenciesMeta": {
|
|
55
55
|
"fontkit": {
|