postext-pdf 1.19.2 → 1.20.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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=comic.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comic.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/comic.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,420 @@
1
+ import { describe, it, expect, beforeAll } from 'vitest';
2
+ import fs from 'node:fs';
3
+ import zlib from 'node:zlib';
4
+ import { PDFArray, PDFDict, PDFDocument, PDFHexString, PDFName, PDFRawStream, PDFRef, PDFString, decodePDFRawStream, } from 'pdf-lib';
5
+ import { buildDocument } from 'postext';
6
+ import { renderToPdf } from '../pdf-backend';
7
+ import { comicBalloonGroups, comicBalloonText, rotationMatrix } from '../pdf-backend/comic';
8
+ // Comic pages in the PDF (#564): panels clipped to their outline with the
9
+ // whole picture drawn at its box, mirrored art, borders, balloons painted
10
+ // group by group, and the tagged structure in reading order.
11
+ const fontBytes = fs.readFileSync(new URL('../../../../apps/web/public/fonts/Lora-Regular.ttf', import.meta.url));
12
+ const fontProvider = async () => new Uint8Array(fontBytes);
13
+ class StubCtx {
14
+ font = '';
15
+ letterSpacing = '0px';
16
+ measureText(s) {
17
+ return { width: s.length * 7 };
18
+ }
19
+ }
20
+ globalThis.OffscreenCanvas = class {
21
+ getContext() {
22
+ return new StubCtx();
23
+ }
24
+ };
25
+ // ------------------------------------------------------------ a tiny PNG encoder
26
+ const CRC_TABLE = (() => {
27
+ const t = new Uint32Array(256);
28
+ for (let n = 0; n < 256; n++) {
29
+ let c = n;
30
+ for (let k = 0; k < 8; k++)
31
+ c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
32
+ t[n] = c >>> 0;
33
+ }
34
+ return t;
35
+ })();
36
+ function crc32(bytes) {
37
+ let c = 0xffffffff;
38
+ for (const b of bytes)
39
+ c = CRC_TABLE[(c ^ b) & 0xff] ^ (c >>> 8);
40
+ return (c ^ 0xffffffff) >>> 0;
41
+ }
42
+ function chunk(type, data) {
43
+ const len = Buffer.alloc(4);
44
+ len.writeUInt32BE(data.length);
45
+ const body = Buffer.concat([Buffer.from(type, 'latin1'), Buffer.from(data)]);
46
+ const crc = Buffer.alloc(4);
47
+ crc.writeUInt32BE(crc32(body));
48
+ return Buffer.concat([len, body, crc]);
49
+ }
50
+ /** An RGB PNG painted by `pixel`. */
51
+ function png(width, height, pixel) {
52
+ const raw = Buffer.alloc((width * 3 + 1) * height);
53
+ for (let y = 0; y < height; y++) {
54
+ raw[y * (width * 3 + 1)] = 0;
55
+ for (let x = 0; x < width; x++) {
56
+ const [r, g, b] = pixel(x, y);
57
+ const o = y * (width * 3 + 1) + 1 + x * 3;
58
+ raw[o] = r;
59
+ raw[o + 1] = g;
60
+ raw[o + 2] = b;
61
+ }
62
+ }
63
+ const ihdr = Buffer.alloc(13);
64
+ ihdr.writeUInt32BE(width, 0);
65
+ ihdr.writeUInt32BE(height, 4);
66
+ ihdr[8] = 8;
67
+ ihdr[9] = 2;
68
+ return new Uint8Array(Buffer.concat([
69
+ Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
70
+ chunk('IHDR', ihdr),
71
+ chunk('IDAT', zlib.deflateSync(raw)),
72
+ chunk('IEND', new Uint8Array(0)),
73
+ ]));
74
+ }
75
+ // A wide landscape: sky over grass, a red sun on the left (so a flip shows).
76
+ const landscape = png(300, 150, (x, y) => {
77
+ if ((x - 60) ** 2 + (y - 45) ** 2 < 22 ** 2)
78
+ return [220, 50, 40];
79
+ return y < 95 ? [120 + (y >> 1), 170 + (y >> 2), 235] : [70, 150 - ((y - 95) >> 1), 60];
80
+ });
81
+ // A tall figure: a dark stripe down the left third.
82
+ const portrait = png(120, 200, (x, y) => (x < 40 ? [40, 40, 70] : [235, 215, 160 + (y >> 2)]));
83
+ const files = { 'land.png': landscape, 'tall.png': portrait };
84
+ const picture = (id, fileId, width, height, extra = {}) => ({
85
+ id,
86
+ typeId: 'figure',
87
+ kind: 'bitmap',
88
+ createdAt: 0,
89
+ updatedAt: 0,
90
+ bitmap: { fileId, format: 'png', width, height },
91
+ ...extra,
92
+ });
93
+ const resources = [
94
+ picture('land', 'land.png', 300, 150, { altText: 'A meadow under a red sun' }),
95
+ picture('tall', 'tall.png', 120, 200),
96
+ ];
97
+ const pt = (value) => ({ value, unit: 'pt' });
98
+ const config = {
99
+ page: { width: pt(400), height: pt(560), dpi: 72, margins: { top: pt(30), bottom: pt(30), left: pt(30), right: pt(30) } },
100
+ layout: { layoutType: 'single' },
101
+ bodyText: { fontFamily: 'Lora', fontSize: pt(11), lineHeight: pt(16), hyphenation: { enabled: false } },
102
+ headings: { balancing: { enabled: false } },
103
+ comics: { panel: { borderRadius: pt(0) } },
104
+ };
105
+ const markdown = `Before the comic.
106
+
107
+ :::page{split="45 [60 | *] / *" gutter=12pt}
108
+ ::panel{art=land}
109
+ ::panel{art=tall mirror alt="A figure in the doorway"}
110
+ ::panel{art=land bg="#ffeecc" style=sketch}
111
+ :::
112
+
113
+ After the comic.
114
+ `;
115
+ // ------------------------------------------------------------ synthetic lettering
116
+ function textBlock(text, x, baseline, color = '#111111', size = 14) {
117
+ const width = text.length * 7;
118
+ return {
119
+ kind: 'text',
120
+ bbox: { x, y: baseline - size, width, height: size * 1.3 },
121
+ fontString: `${size}px Lora`,
122
+ color,
123
+ clip: false,
124
+ lines: [{ text, xOffset: 0, baselineY: baseline, width }],
125
+ };
126
+ }
127
+ /** An ellipse (px), clockwise on the sheet. */
128
+ function ellipse(cx, cy, rx, ry) {
129
+ const k = 0.5523;
130
+ return `M${cx + rx} ${cy}C${cx + rx} ${cy + ry * k} ${cx + rx * k} ${cy + ry} ${cx} ${cy + ry}C${cx - rx * k} ${cy + ry} ${cx - rx} ${cy + ry * k} ${cx - rx} ${cy}C${cx - rx} ${cy - ry * k} ${cx - rx * k} ${cy - ry} ${cx} ${cy - ry}C${cx + rx * k} ${cy - ry} ${cx + rx} ${cy - ry * k} ${cx + rx} ${cy}Z`;
131
+ }
132
+ /** An ellipse (px) with a wedge tail, as one compound path (both
133
+ * clockwise, so the nonzero fill covers their overlap). */
134
+ function ellipseWithTail(cx, cy, rx, ry, tip) {
135
+ return `${ellipse(cx, cy, rx, ry)}M${cx + 8} ${cy + ry - 4}L${tip.x} ${tip.y}L${cx - 8} ${cy + ry - 4}Z`;
136
+ }
137
+ function letter(page) {
138
+ const [p0, p1, p2] = page.comic.panels;
139
+ const c0 = { x: p0.bbox.x + p0.bbox.width * 0.62, y: p0.bbox.y + 34 };
140
+ const c1 = { x: p1.bbox.x + p1.bbox.width / 2, y: p1.bbox.y + 40 };
141
+ const c2 = { x: p2.bbox.x + p2.bbox.width * 0.7, y: p2.bbox.y + p2.bbox.height * 0.55 };
142
+ const speech = {
143
+ id: 'b1', panelIndex: 0, order: 0, kind: 'balloon', style: 'speech', speaker: 'ana', sourceStart: 0, sourceEnd: 0, group: 0,
144
+ // The body of the joined balloon below it is a subpath of the same
145
+ // outline: one stroke, one fill, so the two merge.
146
+ shape: { d: ellipseWithTail(c0.x, c0.y, 62, 22, { x: c0.x - 75, y: c0.y + 30 }) + ellipse(c0.x - 5, c0.y + 36, 48, 15), fill: '#ffffff', stroke: '#111111', strokeWidth: 1.2 },
147
+ text: [textBlock('DID YOU HEAR?', c0.x - 45, c0.y + 5)],
148
+ bbox: { x: c0.x - 62, y: c0.y - 22, width: 124, height: 44 },
149
+ tailTip: { x: c0.x - 75, y: c0.y + 30 },
150
+ };
151
+ // Joined to the first: its outline is carried by the group's first balloon.
152
+ const joined = {
153
+ id: 'b2', panelIndex: 0, order: 1, kind: 'balloon', style: 'speech', speaker: 'ana', sourceStart: 0, sourceEnd: 0, group: 0,
154
+ text: [textBlock('SOMETHING!', c0.x - 35, c0.y + 40)],
155
+ bbox: { x: c0.x - 40, y: c0.y + 28, width: 80, height: 20 },
156
+ };
157
+ const whisper = {
158
+ id: 'b3', panelIndex: 1, order: 2, kind: 'balloon', style: 'whisper', speaker: 'ben', sourceStart: 0, sourceEnd: 0, group: 1,
159
+ shape: { d: ellipseWithTail(c1.x, c1.y, 50, 20, { x: c1.x + 10, y: c1.y + 60 }), fill: '#ffffff', stroke: '#111111', strokeWidth: 1, dash: [3, 2], double: { gap: 2 } },
160
+ text: [textBlock('SHH…', c1.x - 15, c1.y + 5)],
161
+ bbox: { x: c1.x - 50, y: c1.y - 20, width: 100, height: 40 },
162
+ };
163
+ const sfx = {
164
+ id: 'b4', panelIndex: 2, order: 3, kind: 'sfx', style: 'sfx', sourceStart: 0, sourceEnd: 0, group: 2,
165
+ text: [textBlock('KRAK', c2.x - 28, c2.y + 10, '#d02020', 28)],
166
+ bbox: { x: c2.x - 30, y: c2.y - 20, width: 60, height: 40 },
167
+ rotate: -10,
168
+ halo: { width: 2.5, color: '#ffffff' },
169
+ };
170
+ return [speech, joined, whisper, sfx];
171
+ }
172
+ // ------------------------------------------------------------ PDF readers
173
+ function contentOps(pdf, pageIndex) {
174
+ const contents = pdf.getPage(pageIndex).node.Contents();
175
+ const refs = contents instanceof PDFArray ? contents.asArray() : [contents];
176
+ return refs
177
+ .map((ref) => {
178
+ const s = pdf.context.lookup(ref);
179
+ return s instanceof PDFRawStream ? new TextDecoder('latin1').decode(decodePDFRawStream(s).decode()) : '';
180
+ })
181
+ .join('\n');
182
+ }
183
+ function readElem(pdf, ref) {
184
+ const dict = pdf.context.lookup(ref, PDFDict);
185
+ const text = (v) => (v instanceof PDFHexString || v instanceof PDFString ? v.decodeText() : undefined);
186
+ const elem = { type: dict.get(PDFName.of('S')).decodeText(), kids: [], mcids: 0 };
187
+ const alt = text(dict.get(PDFName.of('Alt')));
188
+ if (alt !== undefined)
189
+ elem.alt = alt;
190
+ const actual = text(dict.get(PDFName.of('ActualText')));
191
+ if (actual !== undefined)
192
+ elem.actualText = actual;
193
+ const a = dict.get(PDFName.of('A'));
194
+ const attr = a instanceof PDFRef ? pdf.context.lookup(a) : a;
195
+ if (attr instanceof PDFDict) {
196
+ const bbox = attr.get(PDFName.of('BBox'));
197
+ const arr = bbox instanceof PDFRef ? pdf.context.lookup(bbox) : bbox;
198
+ if (arr instanceof PDFArray)
199
+ elem.bbox = arr.asArray().map((n) => Number(n.toString()));
200
+ }
201
+ const k = dict.get(PDFName.of('K'));
202
+ for (const kid of k instanceof PDFArray ? k.asArray() : k ? [k] : []) {
203
+ if (kid instanceof PDFRef)
204
+ elem.kids.push(readElem(pdf, kid));
205
+ else if (kid instanceof PDFDict)
206
+ elem.mcids++;
207
+ }
208
+ return elem;
209
+ }
210
+ function structRoot(pdf) {
211
+ const root = pdf.catalog.lookup(PDFName.of('StructTreeRoot'), PDFDict);
212
+ return readElem(pdf, root.get(PDFName.of('K')).get(0));
213
+ }
214
+ // ------------------------------------------------------------ the document
215
+ let doc;
216
+ let comicIndex;
217
+ let bytes;
218
+ let pdf;
219
+ const missing = [];
220
+ beforeAll(async () => {
221
+ doc = buildDocument({ markdown, resources }, { ...config, comics: { ...config.comics, panelStyles: [{ id: 'sketch', borderStyle: 'rough' }] } });
222
+ comicIndex = doc.pages.findIndex((p) => p.comic);
223
+ const page = doc.pages[comicIndex];
224
+ page.comic.balloons = letter(page);
225
+ bytes = await renderToPdf(doc, {
226
+ fontProvider,
227
+ resourceBytes: (id) => files[id],
228
+ onWarning: (w) => {
229
+ if (w.kind === 'missingImage')
230
+ missing.push(w.fileId);
231
+ },
232
+ });
233
+ if (process.env.COMIC_PDF_OUT)
234
+ fs.writeFileSync(process.env.COMIC_PDF_OUT, bytes);
235
+ pdf = await PDFDocument.load(bytes);
236
+ }, 60_000);
237
+ const n = (v) => +v.toFixed(2);
238
+ describe('comic pages in the PDF', () => {
239
+ it('lays the comic page out with three panels of art', () => {
240
+ expect(comicIndex).toBeGreaterThan(0);
241
+ const comic = doc.pages[comicIndex].comic;
242
+ expect(comic.panels.map((p) => p.art?.resourceId)).toEqual(['land', 'tall', 'land']);
243
+ expect(comic.panels[1].art.mirrored).toBe(true);
244
+ expect(comic.panels[2].border.style).toBe('rough');
245
+ expect(missing).toEqual([]);
246
+ });
247
+ it('clips each picture to its panel and draws it whole at its box', () => {
248
+ const ops = contentOps(pdf, comicIndex);
249
+ const H = doc.pages[comicIndex].height;
250
+ for (const panel of doc.pages[comicIndex].comic.panels) {
251
+ const [first] = panel.polygon;
252
+ // The clip path starts at the polygon's first corner and ends W n.
253
+ const clip = new RegExp(`q\\s+${n(first.x)} ${n(H - first.y)} m[^q]*?h\\s+W\\s+n`);
254
+ expect(ops).toMatch(clip);
255
+ const box = panel.art.box;
256
+ // The image's matrix is the uncropped box: larger than the panel on
257
+ // the axis the crop cuts.
258
+ expect(ops).toContain(`${n(box.width)} 0 0 ${n(box.height)} `);
259
+ expect(box.width * box.height).toBeGreaterThan(panel.bbox.width * panel.bbox.height - 1);
260
+ }
261
+ });
262
+ it('flips mirrored art inside its box', () => {
263
+ const ops = contentOps(pdf, comicIndex);
264
+ const box = doc.pages[comicIndex].comic.panels[1].art.box;
265
+ expect(ops).toContain(`-1 0 0 1 ${n(2 * box.x + box.width)} 0 cm`);
266
+ });
267
+ it('strokes the borders, a rough one as a wobbling polyline', () => {
268
+ const ops = contentOps(pdf, comicIndex);
269
+ // Two straight borders (four corners each) and a rough one with many points.
270
+ const strokes = ops.split(/\n/).reduce((acc, line) => {
271
+ if (/ m$/.test(line.trim()))
272
+ acc.push([]);
273
+ acc[acc.length - 1]?.push(line.trim());
274
+ return acc;
275
+ }, []).filter((path) => path.includes('S'));
276
+ const lengths = strokes.map((p) => p.filter((l) => / l$/.test(l)).length);
277
+ expect(lengths.some((l) => l > 20)).toBe(true);
278
+ });
279
+ it('paints a group by stroking at twice the width, filling, then lettering', () => {
280
+ const ops = contentOps(pdf, comicIndex);
281
+ const speech = doc.pages[comicIndex].comic.balloons[0];
282
+ // The outline: round joins, 2 × 1.2 pt wide, then the white fill.
283
+ const strokeAt = ops.indexOf('2.4 w\n1 j');
284
+ expect(strokeAt).toBeGreaterThan(-1);
285
+ const fillAt = ops.indexOf('1 1 1 rg', strokeAt);
286
+ expect(fillAt).toBeGreaterThan(strokeAt);
287
+ const textAt = ops.indexOf('BT', fillAt);
288
+ expect(textAt).toBeGreaterThan(fillAt);
289
+ // Both balloons of the group are lettered before the next group's outline.
290
+ const nextStroke = ops.indexOf('[6 4] 0 d', textAt);
291
+ expect(nextStroke).toBeGreaterThan(ops.indexOf('BT', textAt + 2));
292
+ expect(comicBalloonText(speech)).toBe('DID YOU HEAR?');
293
+ });
294
+ it('draws a dashed whisper over a double outline', () => {
295
+ const ops = contentOps(pdf, comicIndex);
296
+ // Double: 2 × (2·1 + 2) = 8 in ink, 2 × (1 + 2) = 6 in the fill colour,
297
+ // then the dashed 2 pt outline (dashes doubled with the stroke).
298
+ const wide = ops.indexOf('8 w');
299
+ const inner = ops.indexOf('6 w', wide);
300
+ const dashed = ops.indexOf('[6 4] 0 d', inner);
301
+ expect(wide).toBeGreaterThan(-1);
302
+ expect(inner).toBeGreaterThan(wide);
303
+ expect(dashed).toBeGreaterThan(inner);
304
+ // Under the dashes, a solid 3 × 1 band in the fill colour: the gaps read
305
+ // as the balloon's ground.
306
+ const band = ops.indexOf('3 w', inner);
307
+ expect(band).toBeGreaterThan(inner);
308
+ expect(band).toBeLessThan(dashed);
309
+ });
310
+ it('turns a sound effect about its centre and puts a halo under its letters', () => {
311
+ const ops = contentOps(pdf, comicIndex);
312
+ const sfx = doc.pages[comicIndex].comic.balloons[3];
313
+ const m = rotationMatrix(-10, sfx.bbox.x + sfx.bbox.width / 2, sfx.bbox.y + sfx.bbox.height / 2, 1, doc.pages[comicIndex].height);
314
+ const turned = ops.split('\n').filter((l) => / cm$/.test(l)).map((l) => l.split(' ').slice(0, 6).map(Number))
315
+ .find((c) => c.every((v, i) => Math.abs(v - m[i]) < 1e-6));
316
+ expect(turned).toBeDefined();
317
+ expect(m[1]).toBeGreaterThan(0);
318
+ // The halo: hollow glyphs (render mode 1) 5 pt wide, then the red fill.
319
+ const halo = ops.indexOf('1 Tr');
320
+ expect(halo).toBeGreaterThan(-1);
321
+ expect(ops.lastIndexOf('5 w', halo)).toBeGreaterThan(ops.lastIndexOf('cm', halo) - 1);
322
+ expect(ops.slice(halo)).toMatch(/ET[\s\S]*?0\.8156\d* 0\.1254\d* 0\.1254\d* rg[\s\S]*?TJ/);
323
+ // Sound effects come last.
324
+ expect(comicBalloonGroups(doc.pages[comicIndex].comic.balloons).map((g) => g.map((b) => b.id))).toEqual([['b1', 'b2'], ['b3'], ['b4']]);
325
+ });
326
+ it('tags the page as a Div of panel figures, each followed by its lettering', () => {
327
+ const root = structRoot(pdf);
328
+ const divs = root.kids.filter((k) => k.type === 'Div');
329
+ expect(divs).toHaveLength(1);
330
+ const div = divs[0];
331
+ expect(div.kids.map((k) => k.type)).toEqual(['Figure', 'P', 'P', 'Figure', 'P', 'Figure', 'P']);
332
+ const [f0, , , f1, , f2, sfx] = div.kids;
333
+ expect(f0.alt).toBe('A meadow under a red sun');
334
+ expect(f1.alt).toBe('A figure in the doorway');
335
+ expect(f2.alt).toBe('A meadow under a red sun');
336
+ // Every figure carries its panel's box and owns the picture painted.
337
+ const page = doc.pages[comicIndex];
338
+ const panel = page.comic.panels[0];
339
+ expect(f0.bbox.map(n)).toEqual([panel.bbox.x, page.height - panel.bbox.y - panel.bbox.height, panel.bbox.x + panel.bbox.width, page.height - panel.bbox.y].map(n));
340
+ for (const f of [f0, f1, f2])
341
+ expect(f.mcids).toBeGreaterThan(0);
342
+ // The sound effect: a Span with its text as ActualText.
343
+ expect(sfx.kids[0].type).toBe('Span');
344
+ expect(sfx.kids[0].actualText).toBe('KRAK');
345
+ // Reading order around the page: the text before, the Div, the text after.
346
+ const at = root.kids.indexOf(div);
347
+ expect(root.kids[at - 1].type).toBe('P');
348
+ expect(root.kids[at + 1].type).toBe('P');
349
+ });
350
+ it('leaves no painting outside marked content', () => {
351
+ const ops = contentOps(pdf, comicIndex);
352
+ const painting = /^(?:Tj|TJ|f|f\*|B|B\*|b|b\*|S|s|Do|sh|EI)$/;
353
+ let depth = 0;
354
+ const loose = [];
355
+ for (const line of ops.split('\n')) {
356
+ const op = line.trim().split(' ').pop() ?? '';
357
+ if (op === 'BDC' || op === 'BMC')
358
+ depth++;
359
+ else if (op === 'EMC')
360
+ depth--;
361
+ else if (painting.test(op) && depth === 0)
362
+ loose.push(line.trim());
363
+ }
364
+ expect(loose).toEqual([]);
365
+ });
366
+ it('embeds the pictures of comic panels at their full size', async () => {
367
+ // Untagged output paints the same page.
368
+ const plain = await PDFDocument.load(await renderToPdf(doc, { fontProvider, resourceBytes: (id) => files[id], accessible: false }));
369
+ expect(plain.catalog.has(PDFName.of('StructTreeRoot'))).toBe(false);
370
+ expect(contentOps(plain, comicIndex)).toContain('Do');
371
+ });
372
+ });
373
+ /** Where pictures `w` × `h` (px at 72 dpi = pt) are drawn: their top-left
374
+ * corners on the sheet, rounded. */
375
+ function drawnAt(ops, w, h) {
376
+ const H = 560;
377
+ const re = /1 0 0 1 (\S+) (\S+) cm\n(?:1 0 0 1 0 0 cm\n)?(\S+) 0 0 (\S+) 0 0 cm/g;
378
+ return [...ops.matchAll(re)]
379
+ .filter((m) => Math.abs(Number(m[3]) - w) < 0.01 && Math.abs(Number(m[4]) - h) < 0.01)
380
+ .map((m) => [n(Number(m[1])), n(H - Number(m[2]) - h)]);
381
+ }
382
+ describe('strips and spreads in the PDF', () => {
383
+ const figures = (e) => [...(e.type === 'Figure' ? [e] : []), ...e.kids.flatMap(figures)];
384
+ it('paints a strip on its page and reads it where it stands in the text', async () => {
385
+ const md = 'Before the strip.\n\n:::strip{split="* | *" aspect=3}\n::panel{art=land}\n::panel{art=tall}\n:::\n\nAfter the strip.';
386
+ const d = buildDocument({ markdown: md, resources }, config);
387
+ const pageIndex = d.pages.findIndex((p) => p.columns.some((c) => c.blocks.some((b) => b.comic)));
388
+ const block = d.pages[pageIndex].columns.flatMap((c) => c.blocks).find((b) => b.comic);
389
+ const out = await PDFDocument.load(await renderToPdf(d, { fontProvider, resourceBytes: (id) => files[id] }));
390
+ const ops = contentOps(out, pageIndex);
391
+ // Both pictures drawn at their boxes on the sheet (the strip's box moved
392
+ // to where the block sits).
393
+ for (const p of block.comic.panels)
394
+ expect(drawnAt(ops, p.art.box.width, p.art.box.height)).toContainEqual([n(block.bbox.x + p.art.box.x), n(block.bbox.y + p.art.box.y)]);
395
+ const root = structRoot(out);
396
+ const types = root.kids.map((k) => k.type);
397
+ const at = types.indexOf('Div');
398
+ expect(types.slice(at - 1, at + 2)).toEqual(['P', 'Div', 'P']);
399
+ expect(root.kids[at].kids.map((k) => k.type)).toEqual(['Figure', 'Figure']);
400
+ expect(root.kids[at].kids[0].alt).toBe('A meadow under a red sun');
401
+ }, 60_000);
402
+ it('paints both halves of a spread, a panel across the spine tagged once', async () => {
403
+ const md = ':::page{spread split="50 / * [* | * | *]"}\n::panel{art=land}\n::panel{art=tall}\n::panel{art=land}\n::panel{art=tall}\n:::\n\nAfter the spread.';
404
+ const d = buildDocument({ markdown: md, resources }, config);
405
+ const [l, r] = d.pages.filter((p) => p.comic);
406
+ const out = await PDFDocument.load(await renderToPdf(d, { fontProvider, resourceBytes: (id) => files[id] }));
407
+ // The wide top panel's picture on both pages.
408
+ const top = l.comic.panels[0].art.box;
409
+ expect(drawnAt(contentOps(out, l.index), top.width, top.height)).toContainEqual([n(top.x), n(top.y)]);
410
+ const topR = r.comic.panels[0].art.box;
411
+ expect(drawnAt(contentOps(out, r.index), topR.width, topR.height)).toContainEqual([n(topR.x), n(topR.y)]);
412
+ // Four panels, four figures: the halves on the right page continue the
413
+ // left page's.
414
+ const divs = structRoot(out).kids.filter((k) => k.type === 'Div');
415
+ expect(divs).toHaveLength(2);
416
+ expect(divs.flatMap(figures)).toHaveLength(4);
417
+ expect(divs[1].kids.filter((k) => k.type === 'Figure')).toHaveLength(1);
418
+ }, 60_000);
419
+ });
420
+ //# sourceMappingURL=comic.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comic.test.js","sourceRoot":"","sources":["../../src/__tests__/comic.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=comicMarkPairs.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comicMarkPairs.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/comicMarkPairs.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,59 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import fs from 'node:fs';
3
+ import { buildDocument } from 'postext';
4
+ import { renderToPdf } from '../pdf-backend';
5
+ import { collectFontText } from '../pdf-backend/fontHelpers';
6
+ // A full-width pair of marks (!!) set in one cell of a vertical balloon is
7
+ // painted with the half-width marks (`!!`, `VerticalGlyph.paintAs`): the
8
+ // font provider must be asked for those, or a face served as several files
9
+ // (the Fontsource slices of a CJK face) never hands over the file holding
10
+ // `!` and the PDF draws .notdef (#581, Nº 150 `hana{shout}: メン!!`).
11
+ const fontBytes = fs.readFileSync(new URL('../../../../apps/web/public/fonts/Lora-Regular.ttf', import.meta.url));
12
+ class StubCtx {
13
+ font = '';
14
+ letterSpacing = '0px';
15
+ measureText(s) {
16
+ return { width: [...s].length * 7 };
17
+ }
18
+ }
19
+ globalThis.OffscreenCanvas = class {
20
+ getContext() {
21
+ return new StubCtx();
22
+ }
23
+ };
24
+ const resources = [{
25
+ id: 'dojo', typeId: 'figure', kind: 'bitmap', createdAt: 0, updatedAt: 0,
26
+ bitmap: { fileId: 'file-dojo', format: 'png', width: 1200, height: 800 },
27
+ anchors: [{ id: 'hana', x: 0.3, y: 0.6 }],
28
+ }];
29
+ const markdown = `:::page{split="*"}
30
+ ::panel{art=dojo}
31
+ hana{shout}: メン!!
32
+ :::
33
+ `;
34
+ const config = { page: { sizePreset: '17x24' }, locale: 'ja' };
35
+ const shoutFace = (fontText) => {
36
+ const entry = [...fontText.entries()].find(([fs, cps]) => /\b700\b|bold/.test(fs) && cps.has(0xff01));
37
+ expect(entry, 'the shout is set in a bold face').toBeDefined();
38
+ return entry[1];
39
+ };
40
+ describe('mark pairs in vertical balloons', () => {
41
+ it('records the half-width marks a full-width pair is painted with', () => {
42
+ const doc = buildDocument({ markdown, resources }, config);
43
+ const balloon = doc.pages.find((p) => p.comic).comic.balloons[0];
44
+ expect(balloon.text[0].vertical?.region).toBe('japan');
45
+ const cps = shoutFace(collectFontText(doc));
46
+ expect(cps.has(0x21)).toBe(true);
47
+ });
48
+ it('asks the provider of the bold face for `!`', async () => {
49
+ const doc = buildDocument({ markdown, resources }, config);
50
+ const asked = [];
51
+ const fontProvider = async (_family, weight, _style, request) => {
52
+ asked.push({ weight, codePoints: [...(request?.codePoints ?? [])] });
53
+ return new Uint8Array(fontBytes);
54
+ };
55
+ await renderToPdf(doc, { fontProvider, resourceBytes: () => undefined });
56
+ expect(asked.some((a) => a.weight === 700 && a.codePoints.includes(0x21))).toBe(true);
57
+ });
58
+ });
59
+ //# sourceMappingURL=comicMarkPairs.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comicMarkPairs.test.js","sourceRoot":"","sources":["../../src/__tests__/comicMarkPairs.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * PDF painting of a comic page (`VDTPage.comic`, #564), in the order the
3
+ * canvas paints it (`canvas-backend/comic.ts`, SPEC D5):
4
+ *
5
+ * 1. per panel, in reading order: its background, then its picture
6
+ * clipped to its outline (the whole picture drawn at `art.box`,
7
+ * flipped inside it when mirrored), then its border (solid, rough or
8
+ * none), then its pop-out cut-out over the border, unclipped;
9
+ * 2. the balloons, group by group: every outline of the group stroked
10
+ * (at twice the stroke width, so that the fill drawn next covers the
11
+ * inner half and overlapping bodies and tails merge into one line;
12
+ * a double outline adds two wider strokes under it), then every
13
+ * outline filled, then the lettering (each text's halo first, then
14
+ * its glyphs); sound effects last.
15
+ *
16
+ * Everything is in sheet coordinates: call it outside any flow frame.
17
+ *
18
+ * An accessible render tags the page as a `Div` holding, in reading order,
19
+ * a `Figure` per panel with a picture (its alternative text and bounding
20
+ * box) followed by a `P` per balloon of that panel; a sound effect is a
21
+ * `Span` (inside a `P`) whose `/ActualText` is its text. Backgrounds,
22
+ * borders and balloon outlines are artifacts.
23
+ */
24
+ import type { VDTComicBalloon, VDTComicPage, VDTComicPanel } from 'postext';
25
+ import type { FontCache } from '../fontCache';
26
+ import { type PageCtx, type PdfMatrix } from './primitives';
27
+ import { type ResourceImageMap } from './renderResourceBlock';
28
+ import type { StructureFlow } from './structureFlow';
29
+ import { type PathSeg } from './svgVector';
30
+ import { type StructElem } from './tagging';
31
+ /** A panel's outline: its polygon, or a rounded rectangle when it has a
32
+ * radius (as `comicPanelPath` traces it on the canvas). */
33
+ export declare function comicPanelSegs(panel: Pick<VDTComicPanel, 'polygon' | 'bbox' | 'radius'>): PathSeg[];
34
+ /** The matrix of a turn of `deg` degrees clockwise on the sheet about the
35
+ * point (cx, cy) px, in the backend's points (y up). */
36
+ export declare function rotationMatrix(deg: number, cxPx: number, cyPx: number, scale: number, pageHeightPt: number): PdfMatrix;
37
+ /** A sound effect: a balloon of kind `sfx` (the script line's `sfx`
38
+ * key, whatever style it names). A VDT from before balloons had a kind
39
+ * falls back on the `sfx` style id. */
40
+ export declare function isComicSoundEffect(balloon: Pick<VDTComicBalloon, 'style'> & {
41
+ kind?: VDTComicBalloon['kind'];
42
+ }): boolean;
43
+ /** Plain text of a balloon's lettering (the engine's `comicBalloonText`:
44
+ * lines joined with a space, none between two CJK characters). */
45
+ export declare const comicBalloonText: (balloon: Pick<VDTComicBalloon, 'text'>) => string;
46
+ /** Paint one panel: background, clipped picture, border, pop-out. */
47
+ export declare function renderComicPanelPdf(ctx: PageCtx, panel: VDTComicPanel, images: ResourceImageMap | undefined, figure?: StructElem): void;
48
+ /** The balloons of a page as join groups in paint order: groups in the
49
+ * order their first balloon comes, sound effects after the rest. */
50
+ export declare function comicBalloonGroups(balloons: readonly VDTComicBalloon[]): VDTComicBalloon[][];
51
+ /** Paint a comic (a comic page, a half of a spread, a strip on the
52
+ * sheet): panels, then lettering. Tagged in `div` when given (a strip's,
53
+ * placed where the strip is read), else in a `Div` of its own at the end
54
+ * of the document read so far (a comic page). */
55
+ export declare function renderComicPage(ctx: PageCtx, comic: VDTComicPage, fontCache: FontCache, images: ResourceImageMap | undefined, structure?: StructureFlow, div?: StructElem): void;
56
+ //# sourceMappingURL=comic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comic.d.ts","sourceRoot":"","sources":["../../src/pdf-backend/comic.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAuBH,OAAO,KAAK,EAAe,eAAe,EAAE,YAAY,EAAE,aAAa,EAAgC,MAAM,SAAS,CAAC;AACvH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EAUf,MAAM,cAAc,CAAC;AACtB,OAAO,EAAsC,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAClG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAA2B,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAsCrE;4DAC4D;AAC5D,wBAAgB,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC,GAAG,OAAO,EAAE,CAMnG;AAmED;yDACyD;AACzD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,CAStH;AAqBD;;wCAEwC;AACxC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,GAAG;IAAE,IAAI,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,CAAA;CAAE,GAAG,OAAO,CAExH;AAED;mEACmE;AACnE,eAAO,MAAM,gBAAgB,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,KAAK,MAA0B,CAAC;AAmEtG,qEAAqE;AACrE,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,gBAAgB,GAAG,SAAS,EACpC,MAAM,CAAC,EAAE,UAAU,GAClB,IAAI,CAwBN;AA2ED;qEACqE;AACrE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,GAAG,eAAe,EAAE,EAAE,CAW5F;AAED;;;kDAGkD;AAClD,wBAAgB,eAAe,CAC7B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,gBAAgB,GAAG,SAAS,EACpC,SAAS,CAAC,EAAE,aAAa,EACzB,GAAG,CAAC,EAAE,UAAU,GACf,IAAI,CAIN"}