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
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { PDFDocument } from 'pdf-lib';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import fontkit from '@pdf-lib/fontkit';
|
|
5
|
+
import { drawVectorDrawing, parsePathData, svgToVectorDrawing, } from '../pdf-backend/svgVector';
|
|
6
|
+
import { FontCache } from '../fontCache';
|
|
7
|
+
import { drawEmbeddedResource, inlineSvgFontsForRaster, preloadResourceImages, sniffBytes, } from '../pdf-backend/renderResourceBlock';
|
|
8
|
+
import { makeScale } from '../pdf-backend/primitives';
|
|
9
|
+
/** The page's content stream operators, before pdf-lib deflates them. */
|
|
10
|
+
function pageContent(page) {
|
|
11
|
+
const stream = page.getContentStream();
|
|
12
|
+
return new TextDecoder('latin1').decode(stream.getUnencodedContents());
|
|
13
|
+
}
|
|
14
|
+
const asPath = (item) => {
|
|
15
|
+
if (item?.kind !== 'path')
|
|
16
|
+
throw new Error('expected a path item');
|
|
17
|
+
return item;
|
|
18
|
+
};
|
|
19
|
+
const asText = (item) => {
|
|
20
|
+
if (item?.kind !== 'text')
|
|
21
|
+
throw new Error('expected a text item');
|
|
22
|
+
return item;
|
|
23
|
+
};
|
|
24
|
+
/** A resolver with a fixed 0.5 em advance per character (no PDF font). */
|
|
25
|
+
const monoFonts = (families) => families.includes('Mono') ? { widthOf: (t, size) => t.length * size * 0.5, pdfFont: null } : null;
|
|
26
|
+
/** Any real TrueType face will do: the tests only need widths and an embed. */
|
|
27
|
+
const ROBOTO = new URL('../../../../apps/web/public/fonts/Fraunces-Regular.ttf', import.meta.url);
|
|
28
|
+
async function pageCtx() {
|
|
29
|
+
const pdfDoc = await PDFDocument.create();
|
|
30
|
+
pdfDoc.registerFontkit(fontkit);
|
|
31
|
+
const page = pdfDoc.addPage([612, 792]);
|
|
32
|
+
// 72 dpi: 1 px = 1 pt, so the numbers below read directly as points.
|
|
33
|
+
const ctx = { page, pageHeightPt: 792, scale: makeScale(72), colorSpace: 'rgb' };
|
|
34
|
+
return { pdfDoc, page, ctx };
|
|
35
|
+
}
|
|
36
|
+
describe('parsePathData', () => {
|
|
37
|
+
it('normalises relative, horizontal / vertical and smooth commands to absolute M/L/C/Z', () => {
|
|
38
|
+
const segs = parsePathData('m 10 10 h 20 v 5 l -5,5 c 1 1 2 2 3 3 s 4 4 5 5 z');
|
|
39
|
+
expect(segs).toEqual([
|
|
40
|
+
['M', 10, 10],
|
|
41
|
+
['L', 30, 10],
|
|
42
|
+
['L', 30, 15],
|
|
43
|
+
['L', 25, 20],
|
|
44
|
+
['C', 26, 21, 27, 22, 28, 23],
|
|
45
|
+
// Smooth: first control point mirrors the previous second one (27,22) about (28,23).
|
|
46
|
+
['C', 29, 24, 32, 27, 33, 28],
|
|
47
|
+
['Z'],
|
|
48
|
+
]);
|
|
49
|
+
});
|
|
50
|
+
it('converts quadratics and arcs to cubics and accepts compact arc flags', () => {
|
|
51
|
+
const q = parsePathData('M0 0 Q 3 6 6 0');
|
|
52
|
+
expect(q[1]).toEqual(['C', 2, 4, 4, 4, 6, 0]);
|
|
53
|
+
// A half circle of radius 5 from (0,0) to (10,0): two cubic segments.
|
|
54
|
+
const a = parsePathData('M0 0 a5 5 0 01 10 0');
|
|
55
|
+
expect(a).toHaveLength(3);
|
|
56
|
+
expect(a[1][0]).toBe('C');
|
|
57
|
+
expect(a[2][0]).toBe('C');
|
|
58
|
+
const end = a[2];
|
|
59
|
+
expect(end[5]).toBeCloseTo(10);
|
|
60
|
+
expect(end[6]).toBeCloseTo(0);
|
|
61
|
+
});
|
|
62
|
+
it('rejects garbage', () => {
|
|
63
|
+
expect(() => parsePathData('10 10 L 20 20')).toThrow();
|
|
64
|
+
expect(() => parsePathData('M 10 10 X 5')).toThrow();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('svgToVectorDrawing', () => {
|
|
68
|
+
it('flattens shapes, transforms, use and clipPath into root-space paths', () => {
|
|
69
|
+
const svg = `<?xml version="1.0"?>
|
|
70
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
71
|
+
width="100pt" height="50pt" viewBox="0 0 200 100">
|
|
72
|
+
<defs>
|
|
73
|
+
<clipPath id="c"><rect x="0" y="0" width="100" height="100"/></clipPath>
|
|
74
|
+
<circle id="dot" cx="0" cy="0" r="10" fill="#ff0000"/>
|
|
75
|
+
</defs>
|
|
76
|
+
<g transform="translate(10 20)" clip-path="url(#c)" opacity="0.5">
|
|
77
|
+
<rect x="0" y="0" width="30" height="10" style="fill:rgb(0,0,255);stroke:black;stroke-width:2"/>
|
|
78
|
+
<use xlink:href="#dot" x="50" y="5"/>
|
|
79
|
+
<path d="M0 0 L10 0 L10 10 Z" fill="none"/>
|
|
80
|
+
<text x="0" y="0" display="none">hidden</text>
|
|
81
|
+
</g>
|
|
82
|
+
</svg>`;
|
|
83
|
+
const d = svgToVectorDrawing(svg);
|
|
84
|
+
expect(d).not.toBeNull();
|
|
85
|
+
expect(d.viewBox).toEqual([0, 0, 200, 100]);
|
|
86
|
+
expect(d.aspect).toEqual({ xAlign: 0.5, yAlign: 0.5, slice: false });
|
|
87
|
+
// The unpainted path (fill none, no stroke) is dropped.
|
|
88
|
+
expect(d.shapes).toHaveLength(2);
|
|
89
|
+
const rect = asPath(d.shapes[0]);
|
|
90
|
+
const dot = asPath(d.shapes[1]);
|
|
91
|
+
expect(rect.segs[0]).toEqual(['M', 10, 20]);
|
|
92
|
+
expect(rect.fill).toEqual({ hex: '#0000ff', alpha: 0.5 });
|
|
93
|
+
expect(rect.stroke).toMatchObject({ hex: '#000000', alpha: 0.5, width: 2 });
|
|
94
|
+
expect(rect.clips).toHaveLength(1);
|
|
95
|
+
expect(rect.clips[0].segs[0]).toEqual(['M', 10, 20]);
|
|
96
|
+
// use: translate(10,20) then x/y offset → circle centred at (60, 25).
|
|
97
|
+
expect(dot.segs[0]).toEqual(['M', 70, 25]);
|
|
98
|
+
expect(dot.fill).toEqual({ hex: '#ff0000', alpha: 0.5 });
|
|
99
|
+
});
|
|
100
|
+
it('honours named colours, currentColor, fill-rule, dashes and visibility', () => {
|
|
101
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" color="teal">
|
|
102
|
+
<polygon points="0,0 10,0 5,10" fill="currentColor" fill-rule="evenodd"
|
|
103
|
+
stroke="rebeccapurple" stroke-dasharray="1 2" stroke-linecap="round"/>
|
|
104
|
+
<line x1="0" y1="0" x2="1" y2="1" stroke="red" visibility="hidden"/>
|
|
105
|
+
</svg>`;
|
|
106
|
+
const d = svgToVectorDrawing(svg);
|
|
107
|
+
expect(d.shapes).toHaveLength(1);
|
|
108
|
+
const poly = asPath(d.shapes[0]);
|
|
109
|
+
expect(poly.fill).toEqual({ hex: '#008080', alpha: 1 });
|
|
110
|
+
expect(poly.evenOdd).toBe(true);
|
|
111
|
+
expect(poly.stroke).toMatchObject({ hex: '#663399', dash: [1, 2], cap: 1 });
|
|
112
|
+
});
|
|
113
|
+
it('falls back to null for anything outside the subset', () => {
|
|
114
|
+
const base = (body) => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">${body}</svg>`;
|
|
115
|
+
expect(svgToVectorDrawing(base('<text x="1" y="1">t</text>'))).toBeNull();
|
|
116
|
+
expect(svgToVectorDrawing(base('<image href="a.png" width="1" height="1"/>'))).toBeNull();
|
|
117
|
+
expect(svgToVectorDrawing(base('<defs><linearGradient id="g"/></defs><rect width="1" height="1" fill="url(#g)"/>'))).toBeNull();
|
|
118
|
+
expect(svgToVectorDrawing(base('<style>.a{fill:red}</style><rect class="a" width="1" height="1"/>'))).toBeNull();
|
|
119
|
+
expect(svgToVectorDrawing(base('<rect width="1" height="1" mask="url(#m)"/>'))).toBeNull();
|
|
120
|
+
expect(svgToVectorDrawing(base('<rect width="1" height="1" filter="url(#f)"/>'))).toBeNull();
|
|
121
|
+
expect(svgToVectorDrawing(base('<svg viewBox="0 0 1 1"><rect width="1" height="1"/></svg>'))).toBeNull();
|
|
122
|
+
expect(svgToVectorDrawing('<svg xmlns="http://www.w3.org/2000/svg"><rect width="1" height="1">')).toBeNull();
|
|
123
|
+
expect(svgToVectorDrawing('not svg at all')).toBeNull();
|
|
124
|
+
});
|
|
125
|
+
it('lays out text and tspans on a pen, honouring anchors, dx/dy and inherited fonts', () => {
|
|
126
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" font-family="Mono" font-size="10">
|
|
127
|
+
<text x="10" y="20" fill="#112233">ab <tspan font-weight="bold">cd</tspan></text>
|
|
128
|
+
<text x="50" y="40" text-anchor="middle">abcd</text>
|
|
129
|
+
<text x="50" y="60" text-anchor="end">ab<tspan dx="2" dy="-1">cd</tspan></text>
|
|
130
|
+
<g transform="translate(5 5)"><text x="0" y="0" style="font-style:italic">e</text></g>
|
|
131
|
+
<text x="0" y="90" fill="none">invisible</text>
|
|
132
|
+
</svg>`;
|
|
133
|
+
const d = svgToVectorDrawing(svg, { fonts: monoFonts });
|
|
134
|
+
expect(d).not.toBeNull();
|
|
135
|
+
const [t1, t2, t3, t4] = d.shapes.map(asText);
|
|
136
|
+
// "ab " then "cd": the trailing space is kept as a separator, advance 5/char.
|
|
137
|
+
expect(t1.runs.map((r) => [r.text, r.x, r.y])).toEqual([['ab', 10, 20], [' cd', 20, 20]]);
|
|
138
|
+
expect(t1.runs[0].fill).toEqual({ hex: '#112233', alpha: 1 });
|
|
139
|
+
// middle: width 20 → start at 40.
|
|
140
|
+
expect(t2.runs[0]).toMatchObject({ text: 'abcd', x: 40, y: 40 });
|
|
141
|
+
// end: chunk width = (10 + 2 + 10) → start at 50 - 22 = 28; dy shifts the tspan.
|
|
142
|
+
expect(t3.runs.map((r) => [r.text, r.x, r.y])).toEqual([['ab', 28, 60], ['cd', 40, 59]]);
|
|
143
|
+
expect(t4.runs[0].matrix).toEqual([1, 0, 0, 1, 5, 5]);
|
|
144
|
+
expect(d.shapes).toHaveLength(4);
|
|
145
|
+
});
|
|
146
|
+
it('bails out on text without a resolver, unknown fonts, or glyph-level positioning', () => {
|
|
147
|
+
const base = (body, attrs = '') => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" font-family="Mono" ${attrs}>${body}</svg>`;
|
|
148
|
+
expect(svgToVectorDrawing(base('<text x="1" y="1">t</text>'))).toBeNull();
|
|
149
|
+
expect(svgToVectorDrawing(base('<text x="1" y="1" font-family="Nope">t</text>'), { fonts: monoFonts })).toBeNull();
|
|
150
|
+
expect(svgToVectorDrawing(base('<text x="1 2" y="1">t</text>'), { fonts: monoFonts })).toBeNull();
|
|
151
|
+
expect(svgToVectorDrawing(base('<text x="1" y="1" letter-spacing="2">t</text>'), { fonts: monoFonts })).toBeNull();
|
|
152
|
+
expect(svgToVectorDrawing(base('<text x="1" y="1"><textPath href="#p">t</textPath></text>'), { fonts: monoFonts })).toBeNull();
|
|
153
|
+
expect(svgToVectorDrawing(base('<text x="1" y="1" font-size="2em">t</text>'), { fonts: monoFonts })).toBeNull();
|
|
154
|
+
// Generic-only family lists resolve to nothing → raster fallback.
|
|
155
|
+
expect(svgToVectorDrawing(base('<text x="1" y="1" font-family="sans-serif">t</text>'), { fonts: monoFonts })).toBeNull();
|
|
156
|
+
});
|
|
157
|
+
it('skips foreign-namespace and non-rendering elements', () => {
|
|
158
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
159
|
+
xmlns:sodipodi="x" viewBox="0 0 10 10">
|
|
160
|
+
<title>t</title><desc>d</desc><metadata><rdf:RDF xmlns:rdf="r"/></metadata>
|
|
161
|
+
<sodipodi:namedview id="nv"/>
|
|
162
|
+
<g inkscape:label="Layer 1" inkscape:groupmode="layer"><rect width="1" height="1"/></g>
|
|
163
|
+
</svg>`;
|
|
164
|
+
const d = svgToVectorDrawing(svg);
|
|
165
|
+
expect(d.shapes).toHaveLength(1);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
describe('drawVectorDrawing', () => {
|
|
169
|
+
it('maps the viewBox into the target box, y flipped, and paints with path operators', async () => {
|
|
170
|
+
const { page, ctx } = await pageCtx();
|
|
171
|
+
const drawing = svgToVectorDrawing(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 50">
|
|
172
|
+
<rect x="10" y="10" width="20" height="10" fill="#336699" fill-opacity="0.5" fill-rule="evenodd"/>
|
|
173
|
+
<circle cx="50" cy="25" r="5" fill="none" stroke="#000" stroke-width="2"/>
|
|
174
|
+
</svg>`);
|
|
175
|
+
// Place at (72, 144) px, 200 × 100 px → uniform scale 2.
|
|
176
|
+
drawVectorDrawing(ctx, drawing, 72, 144, 200, 100);
|
|
177
|
+
const content = pageContent(page);
|
|
178
|
+
// Viewport clip, then the user-space matrix: scale 2, flip y, origin at
|
|
179
|
+
// the box's top-left (72, 792 - 144 = 648).
|
|
180
|
+
expect(content).toContain('72 548 200 100 re');
|
|
181
|
+
expect(content).toContain('2 0 0 -2 72 648 cm');
|
|
182
|
+
// Rect path in SVG units, painted even-odd through an alpha state.
|
|
183
|
+
expect(content).toMatch(/10 10 m\n30 10 l\n30 20 l\n10 20 l\nh\nf\*/);
|
|
184
|
+
expect(content).toMatch(/\/GSa[^\s]* gs/);
|
|
185
|
+
// Stroked circle: line width in user units, cubic segments, stroke op.
|
|
186
|
+
expect(content).toContain('2 w');
|
|
187
|
+
expect(content).toMatch(/55 25 m\n55 [\d.]+ [\d.]+ 30 50 30 c/);
|
|
188
|
+
expect(content).toMatch(/c\nh\nS/);
|
|
189
|
+
// Alpha state registered on the page resources.
|
|
190
|
+
expect(page.node.Resources()?.toString()).toContain('/GSa');
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
describe('drawVectorDrawing (text)', () => {
|
|
194
|
+
it('sets text runs as real text with an embedded font and an upright text matrix', async () => {
|
|
195
|
+
const { pdfDoc, page, ctx } = await pageCtx();
|
|
196
|
+
const fontCache = new FontCache(pdfDoc, async () => new Uint8Array(fs.readFileSync(ROBOTO)));
|
|
197
|
+
await fontCache.preloadFontStrings(['400 16px Roboto Condensed']);
|
|
198
|
+
const font = fontCache.get('400 16px Roboto Condensed');
|
|
199
|
+
const resolver = (families) => families.includes('Roboto Condensed') ? { pdfFont: font, widthOf: (t, s) => font.widthOfTextAtSize(t, s) } : null;
|
|
200
|
+
const drawing = svgToVectorDrawing(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 50">
|
|
201
|
+
<text x="10" y="30" font-family="Roboto Condensed" font-size="8" fill="#1d1d1b">Salud</text>
|
|
202
|
+
</svg>`, { fonts: resolver });
|
|
203
|
+
expect(asText(drawing.shapes[0]).runs[0].text).toBe('Salud');
|
|
204
|
+
drawVectorDrawing(ctx, drawing, 72, 144, 200, 100);
|
|
205
|
+
const content = pageContent(page);
|
|
206
|
+
// Root matrix flips y; the text matrix flips it back at the pen (10, 30).
|
|
207
|
+
expect(content).toContain('2 0 0 -2 72 648 cm');
|
|
208
|
+
expect(content).toMatch(/BT\n\/[^\s]+ 8 Tf\n1 0 0 -1 10 30 Tm\n<[0-9A-Fa-f]+> Tj\nET/);
|
|
209
|
+
expect(page.node.Resources()?.toString()).toContain('/Font');
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
describe('inlineSvgFontsForRaster', () => {
|
|
213
|
+
it('embeds the provider faces as @font-face data URIs after the root tag', async () => {
|
|
214
|
+
const svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1"><text font-family="Roboto Condensed">a</text></svg>';
|
|
215
|
+
const out = await inlineSvgFontsForRaster(svg, ['italic 700 16px Roboto Condensed', '400 16px Nope'], async (family) => {
|
|
216
|
+
if (family !== 'Roboto Condensed')
|
|
217
|
+
throw new Error('unknown');
|
|
218
|
+
return new Uint8Array([0x00, 0x01, 0x00, 0x00, 0x00]);
|
|
219
|
+
});
|
|
220
|
+
expect(out).toContain('<style type="text/css"><![CDATA[@font-face{font-family:"Roboto Condensed";font-weight:700;font-style:italic;src:url(data:font/ttf;base64,AAEAAAA=)}]]></style><text');
|
|
221
|
+
expect(out).not.toContain('Nope');
|
|
222
|
+
expect(await inlineSvgFontsForRaster(svg, [], async () => new Uint8Array())).toBe(svg);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
describe('sniffBytes', () => {
|
|
226
|
+
it('recognises PDF, SVG (with BOM / declaration / comment) and bitmaps', () => {
|
|
227
|
+
const enc = (s) => new TextEncoder().encode(s);
|
|
228
|
+
expect(sniffBytes(enc('%PDF-1.6\n'))).toBe('pdf');
|
|
229
|
+
expect(sniffBytes(enc('<svg xmlns="x"/>'))).toBe('svg');
|
|
230
|
+
expect(sniffBytes(enc(' <?xml version="1.0"?><svg/>'))).toBe('svg');
|
|
231
|
+
expect(sniffBytes(enc('<!-- c --><svg/>'))).toBe('svg');
|
|
232
|
+
expect(sniffBytes(new Uint8Array([0x89, 0x50, 0x4e, 0x47]))).toBe('png');
|
|
233
|
+
expect(sniffBytes(new Uint8Array([0xff, 0xd8, 0xff]))).toBe('jpeg');
|
|
234
|
+
expect(sniffBytes(enc('GIF89a'))).toBe('gif');
|
|
235
|
+
expect(sniffBytes(enc('RIFF....WEBPVP8 '))).toBe('webp');
|
|
236
|
+
expect(sniffBytes(enc('<html></html>'))).toBe('unknown');
|
|
237
|
+
expect(sniffBytes(new Uint8Array())).toBe('unknown');
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
describe('preloadResourceImages', () => {
|
|
241
|
+
function doc(fileId, singleInk = false) {
|
|
242
|
+
return {
|
|
243
|
+
config: { page: { dpi: 72 }, diagramStyle: { singleInk, inkColor: { hex: '#295aa3' } } },
|
|
244
|
+
blocks: [{
|
|
245
|
+
bbox: { x: 0, y: 0, width: 100, height: 80 },
|
|
246
|
+
resourceBlock: { kind: 'svg', fileId, bodyRect: { x: 0, y: 0, width: 100, height: 50 } },
|
|
247
|
+
}],
|
|
248
|
+
pages: [],
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
it('embeds a PDF print master as a page and draws it as a form XObject', async () => {
|
|
252
|
+
const master = await PDFDocument.create();
|
|
253
|
+
master.addPage([240, 120]).drawRectangle({ x: 0, y: 0, width: 240, height: 120 });
|
|
254
|
+
const masterBytes = await master.save();
|
|
255
|
+
const { pdfDoc, page, ctx } = await pageCtx();
|
|
256
|
+
const images = await preloadResourceImages(pdfDoc, doc('fig'), (id) => (id === 'fig' ? masterBytes : undefined));
|
|
257
|
+
const embedded = images.get('fig');
|
|
258
|
+
expect(embedded?.kind).toBe('page');
|
|
259
|
+
if (embedded?.kind !== 'page')
|
|
260
|
+
return;
|
|
261
|
+
expect(embedded.page.width).toBe(240);
|
|
262
|
+
expect(embedded.page.height).toBe(120);
|
|
263
|
+
drawEmbeddedResource(ctx, embedded, 72, 144, 200, 100);
|
|
264
|
+
const content = pageContent(page);
|
|
265
|
+
// drawPage: translate to the box's bottom-left, scale to fit, paint the XObject.
|
|
266
|
+
expect(content).toContain('1 0 0 1 72 548 cm');
|
|
267
|
+
expect(content).toMatch(/\/EmbeddedPdfPage[^\s]* Do/);
|
|
268
|
+
});
|
|
269
|
+
it('emits supported SVG as a vector drawing and recolours it in single-ink mode', async () => {
|
|
270
|
+
const svg = new TextEncoder().encode('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"><rect width="10" height="10" fill="#000000"/></svg>');
|
|
271
|
+
const { pdfDoc } = await pageCtx();
|
|
272
|
+
const plain = await preloadResourceImages(pdfDoc, doc('a'), () => svg);
|
|
273
|
+
expect(plain.get('a')).toMatchObject({ kind: 'vector' });
|
|
274
|
+
if (plain.get('a')?.kind === 'vector') {
|
|
275
|
+
expect(plain.get('a').drawing.shapes[0].fill.hex).toBe('#000000');
|
|
276
|
+
}
|
|
277
|
+
const inked = await preloadResourceImages(pdfDoc, doc('a', true), () => svg);
|
|
278
|
+
const shape = inked.get('a').drawing.shapes[0];
|
|
279
|
+
expect(shape.fill.hex).toBe('#295aa3');
|
|
280
|
+
});
|
|
281
|
+
it('inlines the text fonts into the raster fallback even when the SVG bails out before its text', async () => {
|
|
282
|
+
// A filter (unsupported) sits before the text, as in cairo-made SVGs.
|
|
283
|
+
const svg = new TextEncoder().encode('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"><defs><filter id="f"/></defs>'
|
|
284
|
+
+ '<g filter="url(#f)"><rect width="1" height="1"/></g>'
|
|
285
|
+
+ '<text x="1" y="5" font-family="Roboto Condensed" font-size="4">hi</text></svg>');
|
|
286
|
+
const { pdfDoc } = await pageCtx();
|
|
287
|
+
const requested = [];
|
|
288
|
+
const provider = async (family) => {
|
|
289
|
+
requested.push(family);
|
|
290
|
+
return new Uint8Array(fs.readFileSync(ROBOTO));
|
|
291
|
+
};
|
|
292
|
+
const fontCache = new FontCache(pdfDoc, provider);
|
|
293
|
+
const images = await preloadResourceImages(pdfDoc, doc('t'), () => svg, fontCache, provider);
|
|
294
|
+
// No canvas in node: the raster cannot be produced, but the font was
|
|
295
|
+
// requested for the vector attempt and again for the inline pass.
|
|
296
|
+
expect(images.has('t')).toBe(false);
|
|
297
|
+
expect(requested).toEqual(['Roboto Condensed', 'Roboto Condensed']);
|
|
298
|
+
});
|
|
299
|
+
it('embeds SVG text as vector text when the font cache can provide the family', async () => {
|
|
300
|
+
const svg = new TextEncoder().encode('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"><text x="1" y="5" font-family="Roboto Condensed, sans-serif" font-size="4">hi</text></svg>');
|
|
301
|
+
const { pdfDoc } = await pageCtx();
|
|
302
|
+
const requested = [];
|
|
303
|
+
const fontCache = new FontCache(pdfDoc, async (family) => {
|
|
304
|
+
requested.push(family);
|
|
305
|
+
if (family !== 'Roboto Condensed')
|
|
306
|
+
throw new Error('unknown family');
|
|
307
|
+
return new Uint8Array(fs.readFileSync(ROBOTO));
|
|
308
|
+
});
|
|
309
|
+
const images = await preloadResourceImages(pdfDoc, doc('t'), () => svg, fontCache);
|
|
310
|
+
expect(images.get('t')).toMatchObject({ kind: 'vector' });
|
|
311
|
+
expect(asText(images.get('t').drawing.shapes[0]).runs[0].text).toBe('hi');
|
|
312
|
+
// Only the named family is requested; generic families are never fetched.
|
|
313
|
+
expect(requested).toEqual(['Roboto Condensed']);
|
|
314
|
+
});
|
|
315
|
+
it('leaves unsupported SVG absent outside the browser (no raster fallback available)', async () => {
|
|
316
|
+
const svg = new TextEncoder().encode('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"><text x="1" y="5">hi</text></svg>');
|
|
317
|
+
const { pdfDoc } = await pageCtx();
|
|
318
|
+
const images = await preloadResourceImages(pdfDoc, doc('t'), () => svg);
|
|
319
|
+
expect(images.has('t')).toBe(false);
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
//# sourceMappingURL=svgVector.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svgVector.test.js","sourceRoot":"","sources":["../../src/__tests__/svgVector.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toUnicode.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/toUnicode.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { PDFDocument, PDFDict, PDFName, PDFRawStream, decodePDFRawStream } from 'pdf-lib';
|
|
4
|
+
import fontkit from '@pdf-lib/fontkit';
|
|
5
|
+
import { coverAllGlyphUnicode, toUnicodeCmap } from '../fontCache';
|
|
6
|
+
// Cormorant Garamond shapes fi / fl / Th into glyphs its cmap never reaches
|
|
7
|
+
// (GSUB-only), like the CFF body faces of print presets.
|
|
8
|
+
const cormorant = fs.readFileSync(new URL('../../../../apps/web/public/fonts/CormorantGaramond.ttf', import.meta.url));
|
|
9
|
+
/** The ToUnicode CMap text of the first font of the first page. */
|
|
10
|
+
async function toUnicodeOf(bytes) {
|
|
11
|
+
const pdf = await PDFDocument.load(bytes);
|
|
12
|
+
const fonts = pdf.getPage(0).node.Resources().lookup(PDFName.of('Font'), PDFDict);
|
|
13
|
+
const [, ref] = fonts.entries()[0];
|
|
14
|
+
const font = pdf.context.lookup(ref, PDFDict);
|
|
15
|
+
const cmap = pdf.context.lookup(font.get(PDFName.of('ToUnicode')));
|
|
16
|
+
if (!(cmap instanceof PDFRawStream))
|
|
17
|
+
throw new Error('no ToUnicode stream');
|
|
18
|
+
return new TextDecoder('latin1').decode(decodePDFRawStream(cmap).decode());
|
|
19
|
+
}
|
|
20
|
+
describe('ToUnicode coverage of fully embedded faces', () => {
|
|
21
|
+
it('maps the ligature glyphs the shaper produces (fi, fl) to their letters', async () => {
|
|
22
|
+
const pdfDoc = await PDFDocument.create();
|
|
23
|
+
pdfDoc.registerFontkit(fontkit);
|
|
24
|
+
// The whole-font path (CFF faces go this way; see fontCache.ts).
|
|
25
|
+
const font = await pdfDoc.embedFont(cormorant, { subset: false });
|
|
26
|
+
coverAllGlyphUnicode(font);
|
|
27
|
+
pdfDoc.addPage([200, 100]).drawText('fi fl Th', { font, size: 12, x: 10, y: 50 });
|
|
28
|
+
const cmap = await toUnicodeOf(await pdfDoc.save());
|
|
29
|
+
// Cormorant Garamond: glyph 1272 = fi, 1273 = fl, 613 = Th.
|
|
30
|
+
expect(cmap).toContain('<04f8> <00660069>');
|
|
31
|
+
expect(cmap).toContain('<04f9> <0066006c>');
|
|
32
|
+
expect(cmap).toContain('<0265> <00540068>');
|
|
33
|
+
// The cmap-reachable glyphs stay mapped too (the space, U+0020).
|
|
34
|
+
expect(cmap).toMatch(/<[0-9a-f]{4}> <0020>/);
|
|
35
|
+
});
|
|
36
|
+
it('leaves the ligatures unmapped without the fix (the reason it exists)', async () => {
|
|
37
|
+
const pdfDoc = await PDFDocument.create();
|
|
38
|
+
pdfDoc.registerFontkit(fontkit);
|
|
39
|
+
const font = await pdfDoc.embedFont(cormorant, { subset: false });
|
|
40
|
+
pdfDoc.addPage([200, 100]).drawText('fi fl Th', { font, size: 12, x: 10, y: 50 });
|
|
41
|
+
const cmap = await toUnicodeOf(await pdfDoc.save());
|
|
42
|
+
expect(cmap).not.toContain('<04f8>');
|
|
43
|
+
expect(cmap).not.toContain('<0265>');
|
|
44
|
+
});
|
|
45
|
+
it('writes bfchar blocks of at most 100 entries with UTF-16 surrogates', () => {
|
|
46
|
+
const glyphs = Array.from({ length: 150 }, (_, i) => ({ id: i + 1, codePoints: [0x41 + (i % 26)] }));
|
|
47
|
+
glyphs.push({ id: 500, codePoints: [0x1f600] });
|
|
48
|
+
glyphs.push({ id: 501, codePoints: [] });
|
|
49
|
+
const cmap = toUnicodeCmap(glyphs);
|
|
50
|
+
expect(cmap.match(/beginbfchar/g)).toHaveLength(2);
|
|
51
|
+
expect(cmap).toContain('100 beginbfchar');
|
|
52
|
+
expect(cmap).toContain('51 beginbfchar');
|
|
53
|
+
expect(cmap).toContain('<01f4> <d83dde00>');
|
|
54
|
+
expect(cmap).not.toContain('<01f5>');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=toUnicode.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toUnicode.test.js","sourceRoot":"","sources":["../../src/__tests__/toUnicode.test.ts"],"names":[],"mappings":"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"}
|
package/dist/colors.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface RgbTuple {
|
|
2
|
+
r: number;
|
|
3
|
+
g: number;
|
|
4
|
+
b: number;
|
|
5
|
+
}
|
|
6
|
+
export interface CmykTuple {
|
|
7
|
+
c: number;
|
|
8
|
+
m: number;
|
|
9
|
+
y: number;
|
|
10
|
+
k: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parse a CSS hex color (3, 4, 6 or 8 hex digits with a leading `#`) into
|
|
14
|
+
* normalized 0..1 RGB components suitable for pdf-lib's `rgb()` factory.
|
|
15
|
+
* Alpha channels and malformed inputs are ignored and fall back to black.
|
|
16
|
+
*/
|
|
17
|
+
export declare function hexToRgb(hex: string): RgbTuple;
|
|
18
|
+
/** Naive RGB → CMYK conversion (no ICC profile). Adequate for forcing a
|
|
19
|
+
* 4-channel output when the source is authored in sRGB hex. */
|
|
20
|
+
export declare function rgbToCmyk({ r, g, b }: RgbTuple): CmykTuple;
|
|
21
|
+
/** Luminance-weighted RGB → grayscale (Rec. 601). */
|
|
22
|
+
export declare function rgbToGrayscale({ r, g, b }: RgbTuple): number;
|
|
23
|
+
//# sourceMappingURL=colors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../src/colors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,SAAS;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAoB9C;AAED;gEACgE;AAChE,wBAAgB,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,GAAG,SAAS,CAU1D;AAED,qDAAqD;AACrD,wBAAgB,cAAc,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAE5D"}
|
package/dist/colors.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a CSS hex color (3, 4, 6 or 8 hex digits with a leading `#`) into
|
|
3
|
+
* normalized 0..1 RGB components suitable for pdf-lib's `rgb()` factory.
|
|
4
|
+
* Alpha channels and malformed inputs are ignored and fall back to black.
|
|
5
|
+
*/
|
|
6
|
+
export function hexToRgb(hex) {
|
|
7
|
+
if (!hex || typeof hex !== 'string')
|
|
8
|
+
return { r: 0, g: 0, b: 0 };
|
|
9
|
+
let s = hex.trim();
|
|
10
|
+
if (s.startsWith('#'))
|
|
11
|
+
s = s.slice(1);
|
|
12
|
+
if (s.length === 3 || s.length === 4) {
|
|
13
|
+
const r = parseInt(s[0] + s[0], 16);
|
|
14
|
+
const g = parseInt(s[1] + s[1], 16);
|
|
15
|
+
const b = parseInt(s[2] + s[2], 16);
|
|
16
|
+
return { r: r / 255, g: g / 255, b: b / 255 };
|
|
17
|
+
}
|
|
18
|
+
if (s.length === 6 || s.length === 8) {
|
|
19
|
+
const r = parseInt(s.slice(0, 2), 16);
|
|
20
|
+
const g = parseInt(s.slice(2, 4), 16);
|
|
21
|
+
const b = parseInt(s.slice(4, 6), 16);
|
|
22
|
+
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
|
|
23
|
+
return { r: 0, g: 0, b: 0 };
|
|
24
|
+
}
|
|
25
|
+
return { r: r / 255, g: g / 255, b: b / 255 };
|
|
26
|
+
}
|
|
27
|
+
return { r: 0, g: 0, b: 0 };
|
|
28
|
+
}
|
|
29
|
+
/** Naive RGB → CMYK conversion (no ICC profile). Adequate for forcing a
|
|
30
|
+
* 4-channel output when the source is authored in sRGB hex. */
|
|
31
|
+
export function rgbToCmyk({ r, g, b }) {
|
|
32
|
+
const k = 1 - Math.max(r, g, b);
|
|
33
|
+
if (k >= 1)
|
|
34
|
+
return { c: 0, m: 0, y: 0, k: 1 };
|
|
35
|
+
const denom = 1 - k;
|
|
36
|
+
return {
|
|
37
|
+
c: (1 - r - k) / denom,
|
|
38
|
+
m: (1 - g - k) / denom,
|
|
39
|
+
y: (1 - b - k) / denom,
|
|
40
|
+
k,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** Luminance-weighted RGB → grayscale (Rec. 601). */
|
|
44
|
+
export function rgbToGrayscale({ r, g, b }) {
|
|
45
|
+
return 0.299 * r + 0.587 * g + 0.114 * b;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=colors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colors.js","sourceRoot":"","sources":["../src/colors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { PDFDocument, PDFFont } from 'pdf-lib';
|
|
2
|
+
/** pdf-lib builds a CID font's `W` (advance widths) array from the glyphs
|
|
3
|
+
* reachable through the cmap, so a face embedded whole leaves every
|
|
4
|
+
* GSUB-only glyph — the fi/fl ligatures, contextual alternates — at the
|
|
5
|
+
* default 1000-unit advance, and viewers draw "refl eja" with a gap after
|
|
6
|
+
* the ligature (or the next glyph overlapping it). Replace the width
|
|
7
|
+
* computation with one that covers every glyph in the face. */
|
|
8
|
+
export declare function coverAllGlyphWidths(font: PDFFont): void;
|
|
9
|
+
/** A ToUnicode CMap (Identity-H codes = glyph ids) for the given glyphs. */
|
|
10
|
+
export declare function toUnicodeCmap(glyphs: Array<{
|
|
11
|
+
id: number;
|
|
12
|
+
codePoints: number[];
|
|
13
|
+
}>): string;
|
|
14
|
+
/**
|
|
15
|
+
* A fully embedded face gets a ToUnicode map covering only the glyphs its
|
|
16
|
+
* cmap reaches, so a ligature the shaper substitutes (`fi`, `fl`, `ffi`)
|
|
17
|
+
* has no Unicode value: text extraction drops the letters and PDF/UA
|
|
18
|
+
* validators reject the font (ISO 14289-1 §7.21.7). Record every glyph the
|
|
19
|
+
* layout engine produces for the text actually set — its `codePoints` are
|
|
20
|
+
* the characters it stands for, ligatures included — and write the map
|
|
21
|
+
* from the cmap glyphs plus those.
|
|
22
|
+
*/
|
|
23
|
+
export declare function coverAllGlyphUnicode(font: PDFFont): void;
|
|
24
|
+
export type PdfFontProvider = (family: string, weight: number, style: 'normal' | 'italic') => Promise<Uint8Array>;
|
|
25
|
+
/**
|
|
26
|
+
* Per-document mapping from `family|weight|style` to an embedded pdf-lib
|
|
27
|
+
* font. Entries are populated up-front by `preload()` so that block-level
|
|
28
|
+
* rendering (`get()`) is a sync lookup. A fontString that fails to parse or
|
|
29
|
+
* to load is mapped to `null` and reported back via `missing()`.
|
|
30
|
+
*/
|
|
31
|
+
export declare class FontCache {
|
|
32
|
+
private pdfDoc;
|
|
33
|
+
private provider;
|
|
34
|
+
private map;
|
|
35
|
+
private failed;
|
|
36
|
+
constructor(pdfDoc: PDFDocument, provider: PdfFontProvider);
|
|
37
|
+
preloadFontStrings(fontStrings: Iterable<string | undefined>): Promise<void>;
|
|
38
|
+
get(fontString: string): PDFFont | null;
|
|
39
|
+
missing(): string[];
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=fontCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fontCache.d.ts","sourceRoot":"","sources":["../src/fontCache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAwBpD;;;;;gEAKgE;AAChE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAavD;AAgBD,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,GAAG,MAAM,CAkBzF;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAyBxD;AAED,MAAM,MAAM,eAAe,GAAG,CAC5B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,QAAQ,GAAG,QAAQ,KACvB,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzB;;;;;GAKG;AACH,qBAAa,SAAS;IAKlB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,QAAQ;IALlB,OAAO,CAAC,GAAG,CAAqC;IAChD,OAAO,CAAC,MAAM,CAAqB;gBAGzB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,eAAe;IAG7B,kBAAkB,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkClF,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;IAOvC,OAAO,IAAI,MAAM,EAAE;CAGpB"}
|