postext-pdf 0.1.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/LICENSE +21 -0
- package/README.md +67 -0
- package/dist/__bench__/render.bench.test.d.ts +2 -0
- package/dist/__bench__/render.bench.test.d.ts.map +1 -0
- package/dist/__bench__/render.bench.test.js +86 -0
- package/dist/__bench__/render.bench.test.js.map +1 -0
- package/dist/__tests__/accessibility.test.d.ts +2 -0
- package/dist/__tests__/accessibility.test.d.ts.map +1 -0
- package/dist/__tests__/accessibility.test.js +364 -0
- package/dist/__tests__/accessibility.test.js.map +1 -0
- package/dist/__tests__/contentsLinks.test.d.ts +2 -0
- package/dist/__tests__/contentsLinks.test.d.ts.map +1 -0
- package/dist/__tests__/contentsLinks.test.js +131 -0
- package/dist/__tests__/contentsLinks.test.js.map +1 -0
- package/dist/__tests__/fontCache.test.d.ts +2 -0
- package/dist/__tests__/fontCache.test.d.ts.map +1 -0
- package/dist/__tests__/fontCache.test.js +42 -0
- package/dist/__tests__/fontCache.test.js.map +1 -0
- package/dist/__tests__/headerFooter.test.d.ts +2 -0
- package/dist/__tests__/headerFooter.test.d.ts.map +1 -0
- package/dist/__tests__/headerFooter.test.js +68 -0
- package/dist/__tests__/headerFooter.test.js.map +1 -0
- package/dist/__tests__/rotatedTable.test.d.ts +2 -0
- package/dist/__tests__/rotatedTable.test.d.ts.map +1 -0
- package/dist/__tests__/rotatedTable.test.js +112 -0
- package/dist/__tests__/rotatedTable.test.js.map +1 -0
- package/dist/__tests__/svgVector.test.d.ts +2 -0
- package/dist/__tests__/svgVector.test.d.ts.map +1 -0
- package/dist/__tests__/svgVector.test.js +322 -0
- package/dist/__tests__/svgVector.test.js.map +1 -0
- package/dist/__tests__/toUnicode.test.d.ts +2 -0
- package/dist/__tests__/toUnicode.test.d.ts.map +1 -0
- package/dist/__tests__/toUnicode.test.js +57 -0
- package/dist/__tests__/toUnicode.test.js.map +1 -0
- package/dist/colors.d.ts +23 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +47 -0
- package/dist/colors.js.map +1 -0
- package/dist/fontCache.d.ts +41 -0
- package/dist/fontCache.d.ts.map +1 -0
- package/dist/fontCache.js +154 -0
- package/dist/fontCache.js.map +1 -0
- package/dist/fontString.d.ts +14 -0
- package/dist/fontString.d.ts.map +1 -0
- package/dist/fontString.js +43 -0
- package/dist/fontString.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/pdf-backend/blockRender.d.ts +15 -0
- package/dist/pdf-backend/blockRender.d.ts.map +1 -0
- package/dist/pdf-backend/blockRender.js +244 -0
- package/dist/pdf-backend/blockRender.js.map +1 -0
- package/dist/pdf-backend/fontHelpers.d.ts +6 -0
- package/dist/pdf-backend/fontHelpers.d.ts.map +1 -0
- package/dist/pdf-backend/fontHelpers.js +81 -0
- package/dist/pdf-backend/fontHelpers.js.map +1 -0
- package/dist/pdf-backend/headerFooter.d.ts +15 -0
- package/dist/pdf-backend/headerFooter.d.ts.map +1 -0
- package/dist/pdf-backend/headerFooter.js +145 -0
- package/dist/pdf-backend/headerFooter.js.map +1 -0
- package/dist/pdf-backend/index.d.ts +47 -0
- package/dist/pdf-backend/index.d.ts.map +1 -0
- package/dist/pdf-backend/index.js +227 -0
- package/dist/pdf-backend/index.js.map +1 -0
- package/dist/pdf-backend/links.d.ts +44 -0
- package/dist/pdf-backend/links.d.ts.map +1 -0
- package/dist/pdf-backend/links.js +110 -0
- package/dist/pdf-backend/links.js.map +1 -0
- package/dist/pdf-backend/outlines.d.ts +8 -0
- package/dist/pdf-backend/outlines.d.ts.map +1 -0
- package/dist/pdf-backend/outlines.js +145 -0
- package/dist/pdf-backend/outlines.js.map +1 -0
- package/dist/pdf-backend/pageDecorations.d.ts +13 -0
- package/dist/pdf-backend/pageDecorations.d.ts.map +1 -0
- package/dist/pdf-backend/pageDecorations.js +88 -0
- package/dist/pdf-backend/pageDecorations.js.map +1 -0
- package/dist/pdf-backend/pageLabels.d.ts +8 -0
- package/dist/pdf-backend/pageLabels.d.ts.map +1 -0
- package/dist/pdf-backend/pageLabels.js +61 -0
- package/dist/pdf-backend/pageLabels.js.map +1 -0
- package/dist/pdf-backend/primitives.d.ts +46 -0
- package/dist/pdf-backend/primitives.d.ts.map +1 -0
- package/dist/pdf-backend/primitives.js +162 -0
- package/dist/pdf-backend/primitives.js.map +1 -0
- package/dist/pdf-backend/renderResourceBlock.d.ts +87 -0
- package/dist/pdf-backend/renderResourceBlock.d.ts.map +1 -0
- package/dist/pdf-backend/renderResourceBlock.js +753 -0
- package/dist/pdf-backend/renderResourceBlock.js.map +1 -0
- package/dist/pdf-backend/structureFlow.d.ts +55 -0
- package/dist/pdf-backend/structureFlow.d.ts.map +1 -0
- package/dist/pdf-backend/structureFlow.js +180 -0
- package/dist/pdf-backend/structureFlow.js.map +1 -0
- package/dist/pdf-backend/svgVector.d.ts +144 -0
- package/dist/pdf-backend/svgVector.d.ts.map +1 -0
- package/dist/pdf-backend/svgVector.js +1455 -0
- package/dist/pdf-backend/svgVector.js.map +1 -0
- package/dist/pdf-backend/tagging.d.ts +136 -0
- package/dist/pdf-backend/tagging.d.ts.map +1 -0
- package/dist/pdf-backend/tagging.js +296 -0
- package/dist/pdf-backend/tagging.js.map +1 -0
- package/dist/woff2.d.ts +11 -0
- package/dist/woff2.d.ts.map +1 -0
- package/dist/woff2.js +47 -0
- package/dist/woff2.js.map +1 -0
- package/dist/worker/client.d.ts +30 -0
- package/dist/worker/client.d.ts.map +1 -0
- package/dist/worker/client.js +112 -0
- package/dist/worker/client.js.map +1 -0
- package/dist/worker/pdf.worker.d.ts +2 -0
- package/dist/worker/pdf.worker.d.ts.map +1 -0
- package/dist/worker/pdf.worker.js +67 -0
- package/dist/worker/pdf.worker.js.map +1 -0
- package/dist/worker/protocol.d.ts +49 -0
- package/dist/worker/protocol.d.ts.map +1 -0
- package/dist/worker/protocol.js +2 -0
- package/dist/worker/protocol.js.map +1 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Postext contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# postext-pdf
|
|
2
|
+
|
|
3
|
+
**PDF backend for [postext](https://www.npmjs.com/package/postext).**
|
|
4
|
+
|
|
5
|
+
`postext-pdf` turns the document that `postext` lays out into a real PDF, in the browser or in Node. It does not re-measure anything: it consumes the same `VDTDocument` the canvas and HTML renderers use and translates its coordinates into PDF points, so the three outputs agree on every line break, column height and resource placement. The PDF embeds the actual font files, carries outline bookmarks and is tagged for accessibility (PDF/UA oriented).
|
|
6
|
+
|
|
7
|
+
> **Active development** — postext and postext-pdf change frequently. The packages are published so everyone can follow their evolution, but we do not recommend using them in production yet.
|
|
8
|
+
|
|
9
|
+
**Website:** [postext.dev](https://postext.dev/) · **Docs:** [PDF output](https://postext.dev/en/docs/configuration#generating-pdfs)
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install postext postext-pdf
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`postext` is a peer dependency. Or import both straight from a CDN:
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
import { buildDocument } from 'https://esm.sh/postext';
|
|
21
|
+
import { renderToPdf, decompressWoff2 } from 'https://esm.sh/postext-pdf';
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick example
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { buildDocument } from 'postext';
|
|
28
|
+
import { renderToPdf, decompressWoff2 } from 'postext-pdf';
|
|
29
|
+
|
|
30
|
+
// The PDF embeds real font files; resolve every family/weight/style the
|
|
31
|
+
// document uses to TTF or OTF bytes. Here: Fontsource's static WOFF2 cuts.
|
|
32
|
+
const fontProvider = async (family, weight, style) => {
|
|
33
|
+
const id = family.toLowerCase().replace(/\s+/g, '-');
|
|
34
|
+
const url = `https://cdn.jsdelivr.net/npm/@fontsource/${id}@latest/files/${id}-latin-${weight}-${style}.woff2`;
|
|
35
|
+
const res = await fetch(url);
|
|
36
|
+
return decompressWoff2(new Uint8Array(await res.arrayBuffer()));
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const doc = buildDocument({ markdown: '# Hello\n\nTypeset me.' }, {
|
|
40
|
+
page: { sizePreset: '17x24' },
|
|
41
|
+
bodyText: { fontFamily: 'EB Garamond' },
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const bytes = await renderToPdf(doc, { fontProvider }); // Uint8Array
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
In the browser, load the web fonts (`document.fonts.load(...)`) before calling `buildDocument`, so the layout is measured with the same faces the PDF embeds.
|
|
48
|
+
|
|
49
|
+
## API
|
|
50
|
+
|
|
51
|
+
- `renderToPdf(doc | doc[], options)` — renders one document or a whole book (an array of chapters) to PDF bytes. Options: `fontProvider` (required), `resourceBytes` (images, SVGs and single-page PDF masters by file id), `outlines`, `accessible`, `colorSpace`, `pageNegative`, `onProgress`, `rasterizeSvg`.
|
|
52
|
+
- `decompressWoff2(bytes)` — WOFF2 → TTF/OTF, for font providers that fetch web fonts.
|
|
53
|
+
- `postext-pdf/worker` — `createPdfWorker()` renders off the main thread; fonts and SVG rasters are still resolved on the caller's thread when the worker asks for them. `postext-pdf/worker/entry` is the worker script for bundlers that want to control the worker URL.
|
|
54
|
+
- Helpers: `svgToVectorDrawing`, `sniffBytes`, `rasterizeSvgWithDom`.
|
|
55
|
+
|
|
56
|
+
## WOFF2 decoding and bundlers
|
|
57
|
+
|
|
58
|
+
`decompressWoff2` loads [`wawoff2`](https://www.npmjs.com/package/wawoff2) lazily, a CommonJS package wrapping an emscripten WASM decoder. It works as published:
|
|
59
|
+
|
|
60
|
+
- in Node (the emscripten glue assigns `module.exports` when it detects Node), and
|
|
61
|
+
- from esm.sh, whose CommonJS → ESM conversion exposes `decompress`.
|
|
62
|
+
|
|
63
|
+
Some bundlers (Vite, webpack, Turbopack) evaluate the emscripten glue as a web module, where it never assigns `module.exports`, and `decompressWoff2` then fails with `wawoff2: no decompress export`. The fix is a one-line patch to `wawoff2/build/decompress_binding.js` and `compress_binding.js` appending `module.exports = Module;` — the [postext monorepo](https://github.com/drnachio/postext/blob/main/patches/wawoff2.patch) applies it through pnpm's `patchedDependencies`. Alternatively, supply TTF/OTF bytes to your font provider directly and never call `decompressWoff2`.
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.bench.test.d.ts","sourceRoot":"","sources":["../../src/__bench__/render.bench.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost of writing a long chapter to PDF: ms per page, and the share of
|
|
3
|
+
* the phases. Skipped by `pnpm test`; run with `pnpm bench` (BENCH=1).
|
|
4
|
+
* Profile with `node --cpu-prof ./node_modules/vitest/vitest.mjs run
|
|
5
|
+
* __bench__ --pool threads` (BENCH=1).
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it } from 'vitest';
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import fontkit from '@pdf-lib/fontkit';
|
|
10
|
+
import { buildDocument } from 'postext';
|
|
11
|
+
import { renderToPdf } from '../pdf-backend';
|
|
12
|
+
import { parseFontString } from '../fontString';
|
|
13
|
+
const fontBytes = fs.readFileSync(new URL('../../../../apps/web/public/fonts/Lora-Regular.ttf', import.meta.url));
|
|
14
|
+
const fontProvider = async () => new Uint8Array(fontBytes);
|
|
15
|
+
const face = fontkit.create(fontBytes);
|
|
16
|
+
class StubCtx {
|
|
17
|
+
font = '';
|
|
18
|
+
measureText(s) {
|
|
19
|
+
const sizePx = parseFontString(this.font)?.sizePx ?? 16;
|
|
20
|
+
const run = face.layout(s);
|
|
21
|
+
return { width: (run.advanceWidth / face.unitsPerEm) * sizePx };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
globalThis.OffscreenCanvas = class {
|
|
25
|
+
getContext() {
|
|
26
|
+
return new StubCtx();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const SENTENCES = [
|
|
30
|
+
'La terapia ocupacional interviene sobre las actividades de la vida diaria, el juego y la participación social de la persona.',
|
|
31
|
+
'El **razonamiento clínico** combina la evaluación estandarizada con la observación directa del desempeño en el entorno real.',
|
|
32
|
+
'Los marcos de referencia *biomecánico*, *rehabilitador* y *neurodesarrollo* orientan la elección de productos de apoyo.',
|
|
33
|
+
'Una férula de reposo mantiene la articulación en posición funcional y reduce el dolor durante los brotes inflamatorios.',
|
|
34
|
+
'La prevención primaria actúa antes de la aparición de la enfermedad; la secundaria busca el diagnóstico precoz.',
|
|
35
|
+
];
|
|
36
|
+
function paragraph(seed, sentences) {
|
|
37
|
+
const out = [];
|
|
38
|
+
for (let i = 0; i < sentences; i++)
|
|
39
|
+
out.push(SENTENCES[(seed + i) % SENTENCES.length]);
|
|
40
|
+
return out.join(' ');
|
|
41
|
+
}
|
|
42
|
+
function chapterMarkdown() {
|
|
43
|
+
const parts = ['# Programas de intervención en geriatría', '', paragraph(0, 5), ''];
|
|
44
|
+
for (let i = 1; i <= 40; i++) {
|
|
45
|
+
parts.push(`## Sección ${i}`, '', paragraph(i, 6), '', paragraph(i + 2, 4), '');
|
|
46
|
+
parts.push(`### Detalle ${i}`, '', paragraph(i + 1, 5), '');
|
|
47
|
+
parts.push('- Valoración inicial de la persona y de su entorno.', '- Objetivos consensuados con la persona y la familia.', '- Reevaluación periódica del plan de intervención.', '');
|
|
48
|
+
parts.push(paragraph(i + 3, 3), '');
|
|
49
|
+
}
|
|
50
|
+
return parts.join('\n');
|
|
51
|
+
}
|
|
52
|
+
const nodeProcess = globalThis.process;
|
|
53
|
+
describe.skipIf(!nodeProcess?.env?.BENCH)('pdf render bench', () => {
|
|
54
|
+
it('reports ms per page and phase shares', { timeout: 600_000 }, async () => {
|
|
55
|
+
const config = { bodyText: { fontFamily: 'Lora', hyphenation: { locale: 'es' } } };
|
|
56
|
+
const doc = buildDocument({ markdown: chapterMarkdown() }, config);
|
|
57
|
+
const rounds = 3;
|
|
58
|
+
const times = [];
|
|
59
|
+
let phases = '';
|
|
60
|
+
for (let r = 0; r < rounds; r++) {
|
|
61
|
+
let lastPhase = '';
|
|
62
|
+
let lastAt = performance.now();
|
|
63
|
+
const marks = [];
|
|
64
|
+
const t0 = performance.now();
|
|
65
|
+
const bytes = await renderToPdf(doc, {
|
|
66
|
+
fontProvider,
|
|
67
|
+
onProgress: (p) => {
|
|
68
|
+
if (p.phase !== lastPhase) {
|
|
69
|
+
const now = performance.now();
|
|
70
|
+
if (lastPhase)
|
|
71
|
+
marks.push(`${lastPhase}=${Math.round(now - lastAt)}`);
|
|
72
|
+
lastPhase = p.phase;
|
|
73
|
+
lastAt = now;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
const total = performance.now() - t0;
|
|
78
|
+
marks.push(`${lastPhase}=${Math.round(performance.now() - lastAt)}`);
|
|
79
|
+
times.push(total);
|
|
80
|
+
phases = `${marks.join(' ')} kb=${Math.round(bytes.byteLength / 1024)}`;
|
|
81
|
+
}
|
|
82
|
+
const median = times.slice().sort((a, b) => a - b)[Math.floor(rounds / 2)];
|
|
83
|
+
nodeProcess?.stdout.write(`[bench] render: ${doc.pages.length} pages, median ${Math.round(median)} ms (${Math.round(median / doc.pages.length)} ms/page); phases ${phases}\n`);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
//# sourceMappingURL=render.bench.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.bench.test.js","sourceRoot":"","sources":["../../src/__bench__/render.bench.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibility.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/accessibility.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll } from 'vitest';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { PDFArray, PDFDict, PDFDocument, PDFHexString, PDFName, PDFNumber, PDFRawStream, PDFRef, PDFString, decodePDFRawStream, } from 'pdf-lib';
|
|
4
|
+
import fontkit from '@pdf-lib/fontkit';
|
|
5
|
+
import { buildDocument } from 'postext';
|
|
6
|
+
import { renderToPdf } from '../pdf-backend';
|
|
7
|
+
import { buildXmp } from '../pdf-backend/tagging';
|
|
8
|
+
import { parseFontString } from '../fontString';
|
|
9
|
+
const fontBytes = fs.readFileSync(new URL('../../../../apps/web/public/fonts/Lora-Regular.ttf', import.meta.url));
|
|
10
|
+
const fontProvider = async () => new Uint8Array(fontBytes);
|
|
11
|
+
const face = fontkit.create(fontBytes);
|
|
12
|
+
// Text measurement stub with the real metrics of the face the PDF embeds
|
|
13
|
+
// (no DOM in the node test env), so justified lines space like real output.
|
|
14
|
+
class StubCtx {
|
|
15
|
+
font = '';
|
|
16
|
+
measureText(s) {
|
|
17
|
+
const sizePx = parseFontString(this.font)?.sizePx ?? 16;
|
|
18
|
+
const run = face.layout(s);
|
|
19
|
+
return { width: (run.advanceWidth / face.unitsPerEm) * sizePx };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
globalThis.OffscreenCanvas = class {
|
|
23
|
+
getContext() {
|
|
24
|
+
return new StubCtx();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const pt = (value) => ({ value, unit: 'pt' });
|
|
28
|
+
// 1×1 PNG.
|
|
29
|
+
const PNG = Uint8Array.from(Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==', 'base64'));
|
|
30
|
+
const resources = [
|
|
31
|
+
{
|
|
32
|
+
id: 'f1',
|
|
33
|
+
typeId: 'figure',
|
|
34
|
+
kind: 'bitmap',
|
|
35
|
+
caption: 'A square.',
|
|
36
|
+
altText: 'A red square on white',
|
|
37
|
+
createdAt: 0,
|
|
38
|
+
updatedAt: 0,
|
|
39
|
+
bitmap: { fileId: 'f1.png', format: 'png', width: 400, height: 300 },
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 't1',
|
|
43
|
+
typeId: 'table',
|
|
44
|
+
kind: 'table',
|
|
45
|
+
caption: 'Sizes.',
|
|
46
|
+
altText: 'Two sizes and their widths',
|
|
47
|
+
createdAt: 0,
|
|
48
|
+
updatedAt: 0,
|
|
49
|
+
table: {
|
|
50
|
+
model: {
|
|
51
|
+
headerRowCount: 1,
|
|
52
|
+
rows: [
|
|
53
|
+
[{ content: 'Size' }, { content: 'Width' }],
|
|
54
|
+
[{ content: 'Small' }, { content: '10' }],
|
|
55
|
+
[{ content: 'Large' }, { content: '20' }],
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
const para = 'Body text that runs on for a while so the page fills and the paragraph wraps over several lines. ';
|
|
62
|
+
const markdown = [
|
|
63
|
+
'# Opening chapter',
|
|
64
|
+
'',
|
|
65
|
+
`See :ref{id=f1} and :ref{id=t1}. ${para.repeat(3)}`,
|
|
66
|
+
'',
|
|
67
|
+
'### Skipped level',
|
|
68
|
+
'',
|
|
69
|
+
'Words with **bold** and *italic* runs.',
|
|
70
|
+
'',
|
|
71
|
+
'- First item',
|
|
72
|
+
' - Nested item',
|
|
73
|
+
'- Second item',
|
|
74
|
+
'',
|
|
75
|
+
'1. One',
|
|
76
|
+
'2. Two',
|
|
77
|
+
'',
|
|
78
|
+
'> A quoted paragraph.',
|
|
79
|
+
'',
|
|
80
|
+
para.repeat(40),
|
|
81
|
+
'',
|
|
82
|
+
'## Second section',
|
|
83
|
+
'',
|
|
84
|
+
para.repeat(4),
|
|
85
|
+
].join('\n');
|
|
86
|
+
const config = {
|
|
87
|
+
page: { width: pt(360), height: pt(480), margins: { top: pt(24), bottom: pt(24), left: pt(24), right: pt(24) } },
|
|
88
|
+
locale: 'en-us',
|
|
89
|
+
footer: {
|
|
90
|
+
elements: [{
|
|
91
|
+
kind: 'text', id: 'pn', content: '{pageNumber}', fontSize: pt(8), overflow: 'ellipsis-end',
|
|
92
|
+
placement: { anchor: { to: 'container', edge: 'bottom' }, size: { width: 'auto', height: 'auto' } },
|
|
93
|
+
}],
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
function nameOf(v) {
|
|
97
|
+
return v instanceof PDFName ? v.decodeText() : undefined;
|
|
98
|
+
}
|
|
99
|
+
function textOf(v) {
|
|
100
|
+
if (v instanceof PDFHexString || v instanceof PDFString)
|
|
101
|
+
return v.decodeText();
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
function readAttrs(pdf, a) {
|
|
105
|
+
const dicts = a instanceof PDFRef ? [pdf.context.lookup(a)] : a instanceof PDFArray ? a.asArray().map((x) => pdf.context.lookup(x)) : [a];
|
|
106
|
+
return dicts.filter((d) => d instanceof PDFDict).map((d) => {
|
|
107
|
+
const out = {};
|
|
108
|
+
for (const [k, v] of d.entries()) {
|
|
109
|
+
out[k.decodeText()] = nameOf(v) ?? textOf(v) ?? (v instanceof PDFNumber ? String(v.asNumber()) : v.toString());
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function readElem(pdf, ref) {
|
|
115
|
+
const dict = pdf.context.lookup(ref, PDFDict);
|
|
116
|
+
const elem = { type: nameOf(dict.get(PDFName.of('S'))), attrs: readAttrs(pdf, dict.get(PDFName.of('A'))), kids: [], mcids: 0, objrs: 0 };
|
|
117
|
+
const alt = textOf(dict.get(PDFName.of('Alt')));
|
|
118
|
+
if (alt !== undefined)
|
|
119
|
+
elem.alt = alt;
|
|
120
|
+
const k = dict.get(PDFName.of('K'));
|
|
121
|
+
const kids = k instanceof PDFArray ? k.asArray() : k ? [k] : [];
|
|
122
|
+
for (const kid of kids) {
|
|
123
|
+
if (kid instanceof PDFRef) {
|
|
124
|
+
elem.kids.push(readElem(pdf, kid));
|
|
125
|
+
}
|
|
126
|
+
else if (kid instanceof PDFDict) {
|
|
127
|
+
const type = nameOf(kid.get(PDFName.of('Type')));
|
|
128
|
+
if (type === 'MCR')
|
|
129
|
+
elem.mcids++;
|
|
130
|
+
else if (type === 'OBJR')
|
|
131
|
+
elem.objrs++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return elem;
|
|
135
|
+
}
|
|
136
|
+
function structRoot(pdf) {
|
|
137
|
+
const root = pdf.catalog.lookup(PDFName.of('StructTreeRoot'), PDFDict);
|
|
138
|
+
const k = root.get(PDFName.of('K'));
|
|
139
|
+
return readElem(pdf, k.get(0));
|
|
140
|
+
}
|
|
141
|
+
function walk(elem, visit, parent = null) {
|
|
142
|
+
visit(elem, parent);
|
|
143
|
+
for (const kid of elem.kids)
|
|
144
|
+
walk(kid, visit, elem);
|
|
145
|
+
}
|
|
146
|
+
function pageContent(pdf, index) {
|
|
147
|
+
const page = pdf.getPage(index);
|
|
148
|
+
const contents = page.node.Contents();
|
|
149
|
+
const refs = contents instanceof PDFArray ? contents.asArray() : [contents];
|
|
150
|
+
let out = '';
|
|
151
|
+
for (const ref of refs) {
|
|
152
|
+
const stream = pdf.context.lookup(ref);
|
|
153
|
+
if (!(stream instanceof PDFRawStream))
|
|
154
|
+
throw new Error('page content is not a raw stream');
|
|
155
|
+
out += new TextDecoder('latin1').decode(decodePDFRawStream(stream).decode());
|
|
156
|
+
}
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
/** Split a content stream into the runs outside / inside marked content,
|
|
160
|
+
* reporting whether any painting operator sits outside a sequence. */
|
|
161
|
+
function unmarkedPainting(content) {
|
|
162
|
+
const painting = /^(?:Tj|TJ|f|f\*|B|B\*|b|b\*|S|s|Do|sh|EI)$/;
|
|
163
|
+
const out = [];
|
|
164
|
+
let depth = 0;
|
|
165
|
+
for (const line of content.split('\n')) {
|
|
166
|
+
const op = line.trim().split(' ').pop() ?? '';
|
|
167
|
+
if (op === 'BDC' || op === 'BMC')
|
|
168
|
+
depth++;
|
|
169
|
+
else if (op === 'EMC')
|
|
170
|
+
depth--;
|
|
171
|
+
else if (painting.test(op) && depth === 0)
|
|
172
|
+
out.push(line.trim());
|
|
173
|
+
}
|
|
174
|
+
return out;
|
|
175
|
+
}
|
|
176
|
+
let bytes;
|
|
177
|
+
let pdf;
|
|
178
|
+
beforeAll(async () => {
|
|
179
|
+
const doc = buildDocument({ markdown, metadata: { title: 'Sample book', author: 'Ada' }, resources }, config);
|
|
180
|
+
expect(doc.pages.length).toBeGreaterThan(1);
|
|
181
|
+
bytes = await renderToPdf(doc, {
|
|
182
|
+
fontProvider,
|
|
183
|
+
resourceBytes: (fileId) => (fileId === 'f1.png' ? PNG : undefined),
|
|
184
|
+
});
|
|
185
|
+
const out = process.env.POSTEXT_A11Y_PDF_OUT;
|
|
186
|
+
if (out)
|
|
187
|
+
fs.writeFileSync(out, bytes);
|
|
188
|
+
pdf = await PDFDocument.load(bytes);
|
|
189
|
+
}, 60_000);
|
|
190
|
+
describe('accessible (tagged) PDF output', () => {
|
|
191
|
+
it('declares the document tagged, titled and in its language', () => {
|
|
192
|
+
const markInfo = pdf.catalog.lookup(PDFName.of('MarkInfo'), PDFDict);
|
|
193
|
+
expect(markInfo.get(PDFName.of('Marked'))?.toString()).toBe('true');
|
|
194
|
+
expect(pdf.catalog.has(PDFName.of('StructTreeRoot'))).toBe(true);
|
|
195
|
+
expect(textOf(pdf.catalog.get(PDFName.of('Lang')))).toBe('en-US');
|
|
196
|
+
expect(pdf.getTitle()).toBe('Sample book');
|
|
197
|
+
const prefs = pdf.catalog.lookup(PDFName.of('ViewerPreferences'), PDFDict);
|
|
198
|
+
expect(prefs.get(PDFName.of('DisplayDocTitle'))?.toString()).toBe('true');
|
|
199
|
+
const metadata = pdf.catalog.lookup(PDFName.of('Metadata'));
|
|
200
|
+
if (!(metadata instanceof PDFRawStream))
|
|
201
|
+
throw new Error('metadata is not a raw stream');
|
|
202
|
+
const xmp = new TextDecoder().decode(metadata.getContents());
|
|
203
|
+
expect(xmp).toContain('<pdfuaid:part>1</pdfuaid:part>');
|
|
204
|
+
expect(xmp).toContain('<rdf:li xml:lang="x-default">Sample book</rdf:li>');
|
|
205
|
+
expect(xmp).toContain('<rdf:li>Ada</rdf:li>');
|
|
206
|
+
expect(metadata.dict.has(PDFName.of('Filter'))).toBe(false);
|
|
207
|
+
});
|
|
208
|
+
it('gives every page a structure-parent key and structure tab order', () => {
|
|
209
|
+
for (const page of pdf.getPages()) {
|
|
210
|
+
expect(page.node.get(PDFName.of('StructParents'))).toBeInstanceOf(PDFNumber);
|
|
211
|
+
expect(nameOf(page.node.get(PDFName.of('Tabs')))).toBe('S');
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
it('marks every painting operator as content or artifact, properly nested', () => {
|
|
215
|
+
for (let i = 0; i < pdf.getPageCount(); i++) {
|
|
216
|
+
const content = pageContent(pdf, i);
|
|
217
|
+
expect(unmarkedPainting(content), `page ${i + 1}`).toEqual([]);
|
|
218
|
+
const opens = (content.match(/\b(BDC|BMC)\n/g) ?? []).length;
|
|
219
|
+
const closes = (content.match(/\bEMC\n/g) ?? []).length;
|
|
220
|
+
expect(opens).toBe(closes);
|
|
221
|
+
// Sequences never nest and never straddle the column clip: the `W n`
|
|
222
|
+
// clip is always preceded by an `EMC` (or by nothing marked yet).
|
|
223
|
+
expect(content).not.toMatch(/BDC\n(?:(?!EMC\n)[^])*?\bW\nn\n/);
|
|
224
|
+
}
|
|
225
|
+
const first = pageContent(pdf, 0);
|
|
226
|
+
expect(first).toMatch(/\/Artifact <<\n\/Type \/Background\n>> BDC/);
|
|
227
|
+
expect(first).toMatch(/\/Artifact <<\n\/Type \/Pagination\n\/Subtype \/Footer\n>> BDC/);
|
|
228
|
+
expect(first).toMatch(/\/H1 <<\n\/MCID \d+\n>> BDC/);
|
|
229
|
+
expect(first).toMatch(/\/P <<\n\/MCID \d+\n>> BDC/);
|
|
230
|
+
});
|
|
231
|
+
it('builds the reading-order structure tree', () => {
|
|
232
|
+
const root = structRoot(pdf);
|
|
233
|
+
expect(root.type).toBe('Document');
|
|
234
|
+
const types = [];
|
|
235
|
+
walk(root, (e) => types.push(e.type));
|
|
236
|
+
// Heading levels never skip: the `###` after the `#` becomes H2.
|
|
237
|
+
expect(types).toContain('H1');
|
|
238
|
+
expect(types).toContain('H2');
|
|
239
|
+
expect(types).not.toContain('H3');
|
|
240
|
+
for (const t of ['P', 'L', 'LI', 'Lbl', 'LBody', 'BlockQuote', 'Figure', 'Caption', 'Table', 'TR', 'TH', 'TD', 'Link']) {
|
|
241
|
+
expect(types, t).toContain(t);
|
|
242
|
+
}
|
|
243
|
+
// The document's top-level order: heading, paragraph(s), heading, …
|
|
244
|
+
const top = root.kids.map((k) => k.type);
|
|
245
|
+
expect(top[0]).toBe('H1');
|
|
246
|
+
expect(top.filter((t) => t === 'L')).toHaveLength(2);
|
|
247
|
+
});
|
|
248
|
+
it('nests lists by depth and labels their bullets', () => {
|
|
249
|
+
const root = structRoot(pdf);
|
|
250
|
+
const lists = [];
|
|
251
|
+
walk(root, (e) => { if (e.type === 'L')
|
|
252
|
+
lists.push(e); });
|
|
253
|
+
const outer = lists.find((l) => l.kids.length === 2 && l.attrs[0]?.ListNumbering === 'Disc');
|
|
254
|
+
expect(outer).toBeDefined();
|
|
255
|
+
const [first, second] = outer.kids;
|
|
256
|
+
expect(first.type).toBe('LI');
|
|
257
|
+
expect(first.kids.map((k) => k.type)).toEqual(['Lbl', 'LBody']);
|
|
258
|
+
expect(first.kids[0].mcids).toBeGreaterThan(0);
|
|
259
|
+
const body = first.kids[1];
|
|
260
|
+
expect(body.mcids).toBeGreaterThan(0);
|
|
261
|
+
expect(body.kids.map((k) => k.type)).toEqual(['L']);
|
|
262
|
+
expect(body.kids[0].kids[0].kids.map((k) => k.type)).toEqual(['Lbl', 'LBody']);
|
|
263
|
+
expect(second.kids.map((k) => k.type)).toEqual(['Lbl', 'LBody']);
|
|
264
|
+
const ordered = lists.find((l) => l.attrs[0]?.ListNumbering === 'Decimal');
|
|
265
|
+
expect(ordered.kids).toHaveLength(2);
|
|
266
|
+
});
|
|
267
|
+
it('describes the figure and captions it', () => {
|
|
268
|
+
const root = structRoot(pdf);
|
|
269
|
+
let figure;
|
|
270
|
+
walk(root, (e) => { if (e.type === 'Figure')
|
|
271
|
+
figure = e; });
|
|
272
|
+
expect(figure.alt).toBe('A red square on white');
|
|
273
|
+
expect(figure.mcids).toBe(1);
|
|
274
|
+
expect(figure.attrs[0]?.O).toBe('Layout');
|
|
275
|
+
expect(figure.attrs[0]?.Placement).toBe('Block');
|
|
276
|
+
expect(figure.attrs[0]?.BBox).toBeDefined();
|
|
277
|
+
const caption = figure.kids.find((k) => k.type === 'Caption');
|
|
278
|
+
expect(caption.mcids).toBeGreaterThan(0);
|
|
279
|
+
});
|
|
280
|
+
it('tags the table with header cells, scopes and a summary', () => {
|
|
281
|
+
const root = structRoot(pdf);
|
|
282
|
+
let table;
|
|
283
|
+
walk(root, (e) => { if (e.type === 'Table')
|
|
284
|
+
table = e; });
|
|
285
|
+
expect(table.attrs[0]).toEqual({ O: 'Table', Summary: 'Two sizes and their widths' });
|
|
286
|
+
expect(table.kids.map((k) => k.type)).toEqual(['TR', 'TR', 'TR', 'Caption']);
|
|
287
|
+
const [head, ...body] = table.kids.filter((k) => k.type === 'TR');
|
|
288
|
+
expect(head.kids.map((k) => k.type)).toEqual(['TH', 'TH']);
|
|
289
|
+
expect(head.kids[0].attrs[0]).toEqual({ O: 'Table', Scope: 'Column' });
|
|
290
|
+
expect(head.kids[0].mcids).toBeGreaterThan(0);
|
|
291
|
+
for (const row of body) {
|
|
292
|
+
expect(row.kids.map((k) => k.type)).toEqual(['TD', 'TD']);
|
|
293
|
+
for (const cell of row.kids)
|
|
294
|
+
expect(cell.mcids).toBeGreaterThan(0);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
it('wraps each reference in a Link element that owns its annotation', () => {
|
|
298
|
+
const root = structRoot(pdf);
|
|
299
|
+
const links = [];
|
|
300
|
+
walk(root, (e) => { if (e.type === 'Link')
|
|
301
|
+
links.push(e); });
|
|
302
|
+
expect(links).toHaveLength(2);
|
|
303
|
+
for (const link of links) {
|
|
304
|
+
expect(link.mcids).toBe(1);
|
|
305
|
+
expect(link.objrs).toBe(1);
|
|
306
|
+
}
|
|
307
|
+
const annots = pdf.getPage(0).node.Annots().asArray().map((r) => pdf.context.lookup(r, PDFDict));
|
|
308
|
+
expect(annots).toHaveLength(2);
|
|
309
|
+
for (const annot of annots) {
|
|
310
|
+
expect(annot.get(PDFName.of('StructParent'))).toBeInstanceOf(PDFNumber);
|
|
311
|
+
// The ref text itself (`Fig. 1.1`, `Table 1.1`) describes the target.
|
|
312
|
+
expect(textOf(annot.get(PDFName.of('Contents')))).toMatch(/\b1\.1$/);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
it('maps every marked-content id and annotation back through the parent tree', () => {
|
|
316
|
+
const root = pdf.catalog.lookup(PDFName.of('StructTreeRoot'), PDFDict);
|
|
317
|
+
const parentTree = root.lookup(PDFName.of('ParentTree'), PDFDict);
|
|
318
|
+
const nums = parentTree.lookup(PDFName.of('Nums'), PDFArray).asArray();
|
|
319
|
+
const keys = nums.filter((_, i) => i % 2 === 0).map((n) => n.asNumber());
|
|
320
|
+
expect(keys).toEqual([...keys].sort((a, b) => a - b));
|
|
321
|
+
expect(root.get(PDFName.of('ParentTreeNextKey'))).toEqual(PDFNumber.of(keys.length));
|
|
322
|
+
// Page 1: one owner per MCID used in its content stream.
|
|
323
|
+
const pageKey = pdf.getPage(0).node.get(PDFName.of('StructParents')).asNumber();
|
|
324
|
+
const owners = pdf.context.lookup(nums[keys.indexOf(pageKey) * 2 + 1], PDFArray);
|
|
325
|
+
const mcids = new Set([...pageContent(pdf, 0).matchAll(/\/MCID (\d+)/g)].map((m) => Number(m[1])));
|
|
326
|
+
expect(owners.size()).toBe(mcids.size);
|
|
327
|
+
for (const owner of owners.asArray())
|
|
328
|
+
expect(owner).toBeInstanceOf(PDFRef);
|
|
329
|
+
});
|
|
330
|
+
it('paints word spaces as glyphs so extraction needs no gap heuristics', () => {
|
|
331
|
+
const content = pageContent(pdf, 0);
|
|
332
|
+
// pdf-lib shows text as hex CIDs; a run made of the single space glyph
|
|
333
|
+
// appears between the words of every justified line.
|
|
334
|
+
const spaceRuns = content.match(/<[0-9a-f]{4}> Tj/g) ?? [];
|
|
335
|
+
expect(spaceRuns.length).toBeGreaterThan(10);
|
|
336
|
+
});
|
|
337
|
+
it('keeps a split paragraph in one element across pages', () => {
|
|
338
|
+
const root = structRoot(pdf);
|
|
339
|
+
// The long paragraph runs over a page break: one P, several MCIDs, no
|
|
340
|
+
// links to account for the extra sequences.
|
|
341
|
+
const multiPage = root.kids.filter((k) => k.type === 'P' && k.kids.length === 0 && k.mcids >= 2);
|
|
342
|
+
expect(multiPage.length).toBeGreaterThan(0);
|
|
343
|
+
});
|
|
344
|
+
it('can be switched off', async () => {
|
|
345
|
+
const doc = buildDocument({ markdown: '# T\n\nText.' }, config);
|
|
346
|
+
const plain = await PDFDocument.load(await renderToPdf(doc, { fontProvider, accessible: false }));
|
|
347
|
+
expect(plain.catalog.has(PDFName.of('StructTreeRoot'))).toBe(false);
|
|
348
|
+
expect(plain.catalog.has(PDFName.of('MarkInfo'))).toBe(false);
|
|
349
|
+
expect(pageContent(plain, 0)).not.toContain('BDC');
|
|
350
|
+
});
|
|
351
|
+
it('falls back to the first heading as the title', async () => {
|
|
352
|
+
const doc = buildDocument({ markdown: '# Untitled chapter\n\nText.' }, config);
|
|
353
|
+
const out = await PDFDocument.load(await renderToPdf(doc, { fontProvider }));
|
|
354
|
+
expect(out.getTitle()).toBe('Untitled chapter');
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
describe('XMP packet', () => {
|
|
358
|
+
it('escapes markup in the title', () => {
|
|
359
|
+
const xmp = buildXmp({ title: 'A <b> & "c"', producer: 'p', creatorTool: 'c' });
|
|
360
|
+
expect(xmp).toContain('<rdf:li xml:lang="x-default">A <b> & "c"</rdf:li>');
|
|
361
|
+
expect(xmp).not.toContain('<dc:creator>');
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
//# sourceMappingURL=accessibility.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibility.test.js","sourceRoot":"","sources":["../../src/__tests__/accessibility.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contentsLinks.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/contentsLinks.test.ts"],"names":[],"mappings":""}
|