postext-pdf 1.19.3 → 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.
- package/dist/__tests__/comic.test.d.ts +2 -0
- package/dist/__tests__/comic.test.d.ts.map +1 -0
- package/dist/__tests__/comic.test.js +420 -0
- package/dist/__tests__/comic.test.js.map +1 -0
- package/dist/__tests__/comicMarkPairs.test.d.ts +2 -0
- package/dist/__tests__/comicMarkPairs.test.d.ts.map +1 -0
- package/dist/__tests__/comicMarkPairs.test.js +59 -0
- package/dist/__tests__/comicMarkPairs.test.js.map +1 -0
- package/dist/pdf-backend/comic.d.ts +56 -0
- package/dist/pdf-backend/comic.d.ts.map +1 -0
- package/dist/pdf-backend/comic.js +345 -0
- package/dist/pdf-backend/comic.js.map +1 -0
- package/dist/pdf-backend/fontHelpers.d.ts.map +1 -1
- package/dist/pdf-backend/fontHelpers.js +35 -12
- package/dist/pdf-backend/fontHelpers.js.map +1 -1
- package/dist/pdf-backend/headerFooter.d.ts +4 -1
- package/dist/pdf-backend/headerFooter.d.ts.map +1 -1
- package/dist/pdf-backend/headerFooter.js +3 -1
- package/dist/pdf-backend/headerFooter.js.map +1 -1
- package/dist/pdf-backend/index.d.ts.map +1 -1
- package/dist/pdf-backend/index.js +27 -1
- package/dist/pdf-backend/index.js.map +1 -1
- package/dist/pdf-backend/renderResourceBlock.d.ts.map +1 -1
- package/dist/pdf-backend/renderResourceBlock.js +24 -1
- package/dist/pdf-backend/renderResourceBlock.js.map +1 -1
- package/dist/pdf-backend/structureFlow.d.ts +9 -0
- package/dist/pdf-backend/structureFlow.d.ts.map +1 -1
- package/dist/pdf-backend/structureFlow.js +21 -0
- package/dist/pdf-backend/structureFlow.js.map +1 -1
- package/package.json +3 -3
|
@@ -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 @@
|
|
|
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"}
|