@quario/layout 0.1.0 → 0.2.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/lib/page.js CHANGED
@@ -49,12 +49,6 @@ let marginOf = (margin = 54, width, height, path) => {
49
49
  return margin;
50
50
  };
51
51
 
52
- // This layout's baseline type size. Not a host option: a document's type size
53
- // is the document's own, so it is `style.size` on the report and the number
54
- // here is only what text renders at when nothing declares one. The XLSX target
55
- // carries the same 10 for the same reason (docs/adr/0014, docs/adr/0033).
56
- let BASE = 10;
57
-
58
52
  // Size is settled at the factory; margin may come from the document's
59
53
  // `report-start`, so a render peeks that event before opening a canvas.
60
54
  /** @type {(page: any) => any} */
@@ -95,5 +89,5 @@ export let pageBox = (page, at = "page") => {
95
89
  export let geometry = (page = {}, opening) => {
96
90
  let { width, height } = pageBox(page, "options.page");
97
91
  let chosen = marginChoice(opening, page, "options.page");
98
- return frame(width, height, marginOf(chosen.value, width, height, chosen.path), BASE);
92
+ return frame(width, height, marginOf(chosen.value, width, height, chosen.path));
99
93
  };
package/lib/paint.js CHANGED
@@ -183,9 +183,6 @@ let paintText = (ctx, op) => {
183
183
  }
184
184
  };
185
185
 
186
- /** @type {(ctx: CanvasRenderingContext2D, op: any, bitmap: ImageBitmap) => void} */
187
- let paintImage = (ctx, op, bitmap) => ctx.drawImage(bitmap, op.x, op.y, op.w, op.h);
188
-
189
186
  // The marking, as the PDF draws it: translucent grey along the page diagonal,
190
187
  // turned counter-clockwise by the list's angle. The canvas reads down, so the
191
188
  // turn is negative here.
@@ -207,7 +204,8 @@ let PAINTERS = { rect: paintRect, line: paintLine, text: paintText, mark: paintM
207
204
 
208
205
  /** @type {(ctx: CanvasRenderingContext2D, op: Op, bitmaps: Map<Op, ImageBitmap>) => void} */
209
206
  let paintOp = (ctx, op, bitmaps) => {
210
- if (op.kind === "image") paintImage(ctx, op, /** @type {ImageBitmap} */ (bitmaps.get(op)));
207
+ if (op.kind === "image")
208
+ ctx.drawImage(/** @type {ImageBitmap} */ (bitmaps.get(op)), op.x, op.y, op.w, op.h);
211
209
  else PAINTERS[op.kind](ctx, op);
212
210
  };
213
211
 
@@ -225,6 +223,13 @@ let decode = async (page) => {
225
223
  * the canvas, so it knows. `fonts` is the host's font mapping, the same
226
224
  * record given to `layout()`, so a TrueType family draws in its own face.
227
225
  *
226
+ * It awaits its faces and images before it draws, and it draws whatever
227
+ * happened in between: a canvas re-sized under a call still in flight is
228
+ * filled at that call's own `scale`, not the size it now has. A caller that
229
+ * repaints one canvas at changing scales owns that, by not letting a
230
+ * superseded call reach a canvas still on screen — which is what the viewer's
231
+ * stage retires a page for (docs/adr/0046).
232
+ *
228
233
  * @param {CanvasRenderingContext2D} ctx The context to paint on.
229
234
  * @param {Page} page One page of a `Layout`.
230
235
  * @param {{ scale?: number, fonts?: any }} [options]
package/lib/style.js CHANGED
@@ -44,6 +44,13 @@ let BLACK = { r: 0, g: 0, b: 0 };
44
44
  let SHIFT = { right: 1, center: 0.5 };
45
45
  /** @type {(align: any, extra: number) => number} */
46
46
  let shift = (align, extra) => (Object.hasOwn(SHIFT, align) ? SHIFT[align] : 0) * extra;
47
+ // The same for the leftover height -- the slack a box has over its content,
48
+ // which is the only place `valign` is legal (CONTEXT.md "Box"). Top keeps it,
49
+ // bottom takes all of it, middle takes half; undeclared is top.
50
+ /** @type {Record<string, number>} */
51
+ let VSHIFT = { bottom: 1, middle: 0.5 };
52
+ /** @type {(valign: any, extra: number) => number} */
53
+ let vshift = (valign, extra) => (Object.hasOwn(VSHIFT, valign) ? VSHIFT[valign] : 0) * extra;
47
54
 
48
55
  // A declared colour as a `Color`, or null when the value is not one.
49
56
  /** @type {(value: any) => Color | null} */
@@ -62,7 +69,8 @@ let sizeOf = (style, base) => (Number.isFinite(style.size) && style.size > 0 ? s
62
69
  // Style blocks layer outward-in: row under cell, split under slot. Those are
63
70
  // the two innermost of the four layers; the outer two reach a node without
64
71
  // being merged into it -- the band-role default through `roled` below, and the
65
- // report default through the canvas, as `layout.js`'s `adoptDefault` explains.
72
+ // report default through the render's settings, as `canvas.js`'s
73
+ // `adoptSettings` explains.
66
74
  /** @type {(under: any, over: any) => any} */
67
75
  let merge = (under, over) => (under ? (over ? { ...under, ...over } : under) : over || {});
68
76
 
@@ -101,4 +109,19 @@ let dressed = (style) => !!(style && (style.underline || style.strikethrough));
101
109
  /** @type {(style: any) => boolean} */
102
110
  let upper = (style) => !!(style && style.uppercase);
103
111
 
104
- export { BAND, BLACK, GUTTER, LEAD, PADX, PADY, col, dressed, merge, roled, shift, sizeOf, upper };
112
+ export {
113
+ BAND,
114
+ BLACK,
115
+ GUTTER,
116
+ LEAD,
117
+ PADX,
118
+ PADY,
119
+ col,
120
+ dressed,
121
+ merge,
122
+ roled,
123
+ shift,
124
+ sizeOf,
125
+ upper,
126
+ vshift,
127
+ };
package/lib/text.js CHANGED
@@ -7,15 +7,12 @@ 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, 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
- */
10
+ // What measuring needs and no more, and it is exactly the render's settings:
11
+ // the faces to measure against, the base size and family a style falls back to,
12
+ // and the intl three a formatted value resolves in. Declared next door, in
13
+ // `canvas.js`, where it is built and settled — nothing here can touch a page,
14
+ // and a caller hands over `canvas.settings` rather than the canvas.
15
+ /** @typedef {import('./canvas.js').Settings} Settings */
19
16
 
20
17
  /**
21
18
  * @typedef {{ text: string, font: any, size: number, color: any,
@@ -29,10 +26,10 @@ import { LEAD, col, dressed, sizeOf, upper } from "./style.js";
29
26
 
30
27
  /** @typedef {{ cur: Atom[], w: number, lines: Line[], base: number }} Wrap */
31
28
 
32
- /** @type {(token: any, style: any, metrics: Metrics) => string} */
33
- let rawOf = (token, style, metrics) => {
29
+ /** @type {(token: any, style: any, settings: Settings) => string} */
30
+ let rawOf = (token, style, settings) => {
34
31
  if ("literal" in token) return token.literal;
35
- return format(token.value, style?.format, metrics) ?? display(token.value);
32
+ return format(token.value, style?.format, settings) ?? display(token.value);
36
33
  };
37
34
 
38
35
  // This layout has no text-transform to defer to, so `uppercase` is applied to
@@ -43,12 +40,9 @@ let rawOf = (token, style, metrics) => {
43
40
  /** @type {(text: string, style: any) => string} */
44
41
  let cased = (text, style) => (upper(style) ? text.toUpperCase() : text);
45
42
 
46
- /** @type {(font: any, line: string) => string[]} */
47
- let partsOf = (font, line) => printable(font, line).split(/( +)/).filter(Boolean);
48
-
49
43
  /** @type {(out: Atom[], font: any, size: number, color: any, line: string) => void} */
50
44
  let pushParts = (out, font, size, color, line) => {
51
- for (let part of partsOf(font, line))
45
+ for (let part of printable(font, line).split(/( +)/).filter(Boolean))
52
46
  out.push({ text: part, font, size, color, space: part[0] === " ", hard: false });
53
47
  };
54
48
 
@@ -58,12 +52,12 @@ let pushLine = (out, font, size, color, i, line) => {
58
52
  pushParts(out, font, size, color, line);
59
53
  };
60
54
 
61
- /** @type {(metrics: Metrics, style: any) => { font: any, size: number, color: any }} */
62
- let look = (metrics, style) => {
55
+ /** @type {(settings: Settings, style: any) => { font: any, size: number, color: any }} */
56
+ let look = (settings, style) => {
63
57
  let resolved = style || {};
64
58
  return {
65
- font: face(metrics.fonts, resolved, metrics.family),
66
- size: sizeOf(resolved, metrics.base),
59
+ font: face(settings.fonts, resolved, settings.family),
60
+ size: sizeOf(resolved, settings.base),
67
61
  color: col(resolved.color),
68
62
  };
69
63
  };
@@ -71,12 +65,12 @@ let look = (metrics, style) => {
71
65
  // Flatten a cell's tokens to word/space atoms carrying the cell's resolved
72
66
  // typography — one face, size and colour for the whole cell. CR, LF, and
73
67
  // CRLF are one hard break each (SCHEMA.md, Cell values).
74
- /** @type {(metrics: Metrics, tokens: any[], style: any) => Atom[]} */
75
- let atoms = (metrics, tokens, style) => {
68
+ /** @type {(settings: Settings, tokens: any[], style: any) => Atom[]} */
69
+ let atoms = (settings, tokens, style) => {
76
70
  let out = /** @type {Atom[]} */ ([]);
77
- let { font, size, color } = look(metrics, style);
71
+ let { font, size, color } = look(settings, style);
78
72
  for (let token of tokens)
79
- for (let [i, line] of cased(rawOf(token, style, metrics), style)
73
+ for (let [i, line] of cased(rawOf(token, style, settings), style)
80
74
  .split(/\r\n|\r|\n/)
81
75
  .entries())
82
76
  pushLine(out, font, size, color, i, line);
@@ -228,8 +222,8 @@ let placeAtom = (state, atom, avail) => {
228
222
  // breaks by character, hard breaks always break. A cell's atoms share one
229
223
  // typography, so a line's atoms merge into a single draw piece. `size` is the
230
224
  // 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) => {
225
+ /** @type {(list: Atom[], avail: number, size: number) => Line[]} */
226
+ let wrap = (list, avail, size) => {
233
227
  /** @type {Wrap} */
234
228
  let state = { cur: [], w: 0, lines: [], base: size };
235
229
  for (let atom of list) placeAtom(state, atom, avail);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quario/layout",
3
- "version": "0.1.0",
3
+ "version": "0.2.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",
@@ -40,18 +40,19 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "@arethetypeswrong/cli": "^0.18.3",
43
- "@pdf-lib/fontkit": "^1.1.1",
44
43
  "@size-limit/preset-small-lib": "^13.0.3",
45
- "quario": "^0.4.0",
44
+ "@types/fontkit": "^2.0.9",
45
+ "fontkit": "^2.0.4",
46
+ "quario": "^0.5.0",
46
47
  "size-limit": "^13.0.3",
47
48
  "typescript": "^7.0.2"
48
49
  },
49
50
  "peerDependencies": {
50
- "@pdf-lib/fontkit": "^1.1.1",
51
- "quario": "^0.4.0"
51
+ "fontkit": "^2.0.4",
52
+ "quario": "^0.5.0"
52
53
  },
53
54
  "peerDependenciesMeta": {
54
- "@pdf-lib/fontkit": {
55
+ "fontkit": {
55
56
  "optional": true
56
57
  }
57
58
  },