@quario/pdf 0.2.0 → 0.3.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 +51 -0
- package/README.md +3 -2
- package/lib/box.js +111 -0
- package/lib/canvas.js +28 -15
- package/lib/fonts.js +69 -26
- package/lib/index.js +36 -15
- package/lib/layout.js +273 -132
- package/lib/style.js +10 -19
- package/lib/text.js +30 -19
- package/package.json +4 -4
package/lib/style.js
CHANGED
|
@@ -52,27 +52,18 @@ let col = (value) => {
|
|
|
52
52
|
/** @type {(style: any, base: number) => number} */
|
|
53
53
|
let sizeOf = (style, base) => (Number.isFinite(style.size) && style.size > 0 ? style.size : base);
|
|
54
54
|
|
|
55
|
-
// Style blocks layer outward-in: row under cell.
|
|
55
|
+
// Style blocks layer outward-in: row under cell, split under slot. Those are
|
|
56
|
+
// the two innermost of the four layers; the outer two reach a node without
|
|
57
|
+
// being merged into it -- the band-role default through `roled` below, and the
|
|
58
|
+
// report default through the canvas, as `layout.js`'s `adoptDefault` explains.
|
|
56
59
|
/** @type {(under: any, over: any) => any} */
|
|
57
60
|
let merge = (under, over) => (under ? (over ? { ...under, ...over } : under) : over || {});
|
|
58
61
|
|
|
59
|
-
//
|
|
60
|
-
// default
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
// Only `family` is load-bearing in the outer layer: `settle` has already put
|
|
65
|
-
// the report default's `size` on `canvas.base`, which `sizeOf` falls back to
|
|
66
|
-
// for every drawn atom, so the size reaches the page whether or not it is
|
|
67
|
-
// merged here.
|
|
68
|
-
/** @type {(canvas: { style?: any }, under: any, own: any) => any} */
|
|
69
|
-
let stack = (canvas, under, own) => merge(canvas.style, merge(under, own));
|
|
70
|
-
|
|
71
|
-
// Band-role omakase defaults — the outermost layer of that same stack, under
|
|
72
|
-
// the author's own style, which therefore always wins. Only the headline
|
|
73
|
-
// roles carry one; the XLSX target carries the same two, byte-identical, in
|
|
74
|
-
// `packages/xlsx/lib/index.js`, and they move together (SCHEMA.md states the
|
|
75
|
-
// pair for both). A target can only import public engine helpers, so there is
|
|
62
|
+
// Band-role omakase defaults — the third of those four layers, over the report
|
|
63
|
+
// default and under the author's own, which therefore always wins. Only the
|
|
64
|
+
// headline roles carry one; the XLSX target carries the same two,
|
|
65
|
+
// byte-identical, in `packages/xlsx/lib/index.js`, and they move together
|
|
66
|
+
// (SCHEMA.md states the pair for both). A target can only import public engine helpers, so there is
|
|
76
67
|
// nowhere to share this from and the two copies are synced by hand.
|
|
77
68
|
//
|
|
78
69
|
// `@quario/html` deliberately carries none of this, nor the leading, padding
|
|
@@ -103,4 +94,4 @@ let dressed = (style) => !!(style && (style.underline || style.strikethrough));
|
|
|
103
94
|
/** @type {(style: any) => boolean} */
|
|
104
95
|
let upper = (style) => !!(style && style.uppercase);
|
|
105
96
|
|
|
106
|
-
export { BAND, BLACK, GUTTER, LEAD, PADX, PADY, col, dressed, roled, shift, sizeOf,
|
|
97
|
+
export { BAND, BLACK, GUTTER, LEAD, PADX, PADY, col, dressed, merge, roled, shift, sizeOf, upper };
|
package/lib/text.js
CHANGED
|
@@ -3,14 +3,19 @@
|
|
|
3
3
|
* their resolved typography, and a greedy breaker turns those into lines. Pure
|
|
4
4
|
* measurement against the font registry — nothing here touches the page.
|
|
5
5
|
*/
|
|
6
|
-
import { display } from "quario";
|
|
6
|
+
import { display, format } from "quario";
|
|
7
7
|
import { ascOf, face, printable, width } from "./fonts.js";
|
|
8
8
|
import { LEAD, col, dressed, sizeOf, upper } from "./style.js";
|
|
9
9
|
|
|
10
|
-
// What measuring needs and no more: the embedded faces to measure against
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
|
|
10
|
+
// What measuring needs and no more: the embedded faces to measure against, and
|
|
11
|
+
// the base size and family a style falls back to — the report default's two
|
|
12
|
+
// declarations, which the canvas carries for the whole render. A `Canvas`
|
|
13
|
+
// satisfies it, so callers pass theirs straight in — but nothing here can
|
|
14
|
+
// touch a page.
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {{ fonts: import('./fonts.js').Fonts, base: number,
|
|
17
|
+
* family: string | null, locale?: string, currency?: string, timeZone?: string }} Metrics
|
|
18
|
+
*/
|
|
14
19
|
|
|
15
20
|
/**
|
|
16
21
|
* @typedef {{ text: string, font: any, size: number, color: any,
|
|
@@ -22,13 +27,13 @@ import { LEAD, col, dressed, sizeOf, upper } from "./style.js";
|
|
|
22
27
|
* underline?: boolean, strikethrough?: boolean }} Line
|
|
23
28
|
*/
|
|
24
29
|
|
|
25
|
-
/**
|
|
26
|
-
* @typedef {{ cur: Atom[], w: number, lines: Line[], base: number,
|
|
27
|
-
* fonts: import('./fonts.js').Fonts }} Wrap
|
|
28
|
-
*/
|
|
30
|
+
/** @typedef {{ cur: Atom[], w: number, lines: Line[], base: number }} Wrap */
|
|
29
31
|
|
|
30
|
-
/** @type {(token: any) => string} */
|
|
31
|
-
let rawOf = (token) =>
|
|
32
|
+
/** @type {(token: any, style: any, metrics: Metrics) => string} */
|
|
33
|
+
let rawOf = (token, style, metrics) => {
|
|
34
|
+
if ("literal" in token) return token.literal;
|
|
35
|
+
return format(token.value, style?.format, metrics) ?? display(token.value);
|
|
36
|
+
};
|
|
32
37
|
|
|
33
38
|
// This target has no text-transform to defer to, so `uppercase` is applied to
|
|
34
39
|
// the string before it is measured -- the widths have to be the widths of what
|
|
@@ -57,20 +62,23 @@ let pushLine = (out, font, size, color, i, line) => {
|
|
|
57
62
|
let look = (metrics, style) => {
|
|
58
63
|
let resolved = style || {};
|
|
59
64
|
return {
|
|
60
|
-
font: face(metrics.fonts, resolved),
|
|
65
|
+
font: face(metrics.fonts, resolved, metrics.family),
|
|
61
66
|
size: sizeOf(resolved, metrics.base),
|
|
62
67
|
color: col(resolved.color),
|
|
63
68
|
};
|
|
64
69
|
};
|
|
65
70
|
|
|
66
71
|
// Flatten a cell's tokens to word/space atoms carrying the cell's resolved
|
|
67
|
-
// typography — one face, size and colour for the whole cell.
|
|
72
|
+
// typography — one face, size and colour for the whole cell. CR, LF, and
|
|
73
|
+
// CRLF are one hard break each (SCHEMA.md, Cell values).
|
|
68
74
|
/** @type {(metrics: Metrics, tokens: any[], style: any) => Atom[]} */
|
|
69
75
|
let atoms = (metrics, tokens, style) => {
|
|
70
76
|
let out = /** @type {Atom[]} */ ([]);
|
|
71
77
|
let { font, size, color } = look(metrics, style);
|
|
72
78
|
for (let token of tokens)
|
|
73
|
-
for (let [i, line] of cased(rawOf(token), style)
|
|
79
|
+
for (let [i, line] of cased(rawOf(token, style, metrics), style)
|
|
80
|
+
.split(/\r\n|\r|\n/)
|
|
81
|
+
.entries())
|
|
74
82
|
pushLine(out, font, size, color, i, line);
|
|
75
83
|
return out;
|
|
76
84
|
};
|
|
@@ -112,7 +120,9 @@ let growPiece = (run, atom) => {
|
|
|
112
120
|
/** @type {(state: Wrap, run: Run) => void} */
|
|
113
121
|
let finish = (state, run) => {
|
|
114
122
|
if (!run.size) run.size = state.base;
|
|
115
|
-
|
|
123
|
+
// No `asc` fallback: a run with no atoms is a blank line, and an ascender
|
|
124
|
+
// only places pieces. `drawLine` loops over none and `decorateLine` skips a
|
|
125
|
+
// line with no width, so a measured one would be a face lookup nothing reads.
|
|
116
126
|
state.lines.push({
|
|
117
127
|
pieces: run.pieces,
|
|
118
128
|
w: run.w,
|
|
@@ -216,11 +226,12 @@ let placeAtom = (state, atom, avail) => {
|
|
|
216
226
|
|
|
217
227
|
// Greedy wrap against `avail`: spaces never start a line, an over-wide word
|
|
218
228
|
// breaks by character, hard breaks always break. A cell's atoms share one
|
|
219
|
-
// typography, so a line's atoms merge into a single draw piece.
|
|
220
|
-
|
|
221
|
-
|
|
229
|
+
// typography, so a line's atoms merge into a single draw piece. `size` is the
|
|
230
|
+
// empty-run height — a blank item, a hard-break hole.
|
|
231
|
+
/** @type {(metrics: Metrics, list: Atom[], avail: number, size: number) => Line[]} */
|
|
232
|
+
let wrap = (metrics, list, avail, size) => {
|
|
222
233
|
/** @type {Wrap} */
|
|
223
|
-
let state = { cur: [], w: 0, lines: [], base:
|
|
234
|
+
let state = { cur: [], w: 0, lines: [], base: size };
|
|
224
235
|
for (let atom of list) placeAtom(state, atom, avail);
|
|
225
236
|
emit(state);
|
|
226
237
|
return state.lines;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quario/pdf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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",
|
|
@@ -42,13 +42,13 @@
|
|
|
42
42
|
"@arethetypeswrong/cli": "^0.18.3",
|
|
43
43
|
"@pdf-lib/fontkit": "^1.1.1",
|
|
44
44
|
"@size-limit/preset-small-lib": "^13.0.3",
|
|
45
|
-
"quario": "^0.
|
|
45
|
+
"quario": "^0.3.0",
|
|
46
46
|
"size-limit": "^13.0.3",
|
|
47
47
|
"typescript": "^7.0.2"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@pdf-lib/fontkit": "^1.1.1",
|
|
51
|
-
"quario": "^0.
|
|
51
|
+
"quario": "^0.3.0"
|
|
52
52
|
},
|
|
53
53
|
"peerDependenciesMeta": {
|
|
54
54
|
"@pdf-lib/fontkit": {
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"quario",
|
|
63
63
|
"pdf-lib"
|
|
64
64
|
],
|
|
65
|
-
"limit": "
|
|
65
|
+
"limit": "10.5 kB"
|
|
66
66
|
}
|
|
67
67
|
],
|
|
68
68
|
"engines": {
|