@jasy/pdf 1.0.0-alpha.7 → 1.0.0-alpha.8
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/README.md +7 -7
- package/dist/api/descriptor.js +23 -0
- package/dist/api/forms.d.ts +347 -0
- package/dist/api/forms.js +189 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/structure.d.ts +6 -0
- package/dist/api/structure.js +1 -0
- package/dist/crypto/legacy-handler.d.ts +43 -0
- package/dist/crypto/legacy-handler.js +145 -0
- package/dist/crypto/security-handler.d.ts +16 -0
- package/dist/crypto/security-handler.js +26 -0
- package/dist/edit/acroform-reader.d.ts +70 -0
- package/dist/edit/acroform-reader.js +126 -0
- package/dist/edit/appearance.d.ts +47 -0
- package/dist/edit/appearance.js +171 -0
- package/dist/edit/document.d.ts +135 -0
- package/dist/edit/document.js +649 -0
- package/dist/edit/fill.d.ts +48 -0
- package/dist/edit/fill.js +395 -0
- package/dist/edit/flatten.d.ts +42 -0
- package/dist/edit/flatten.js +437 -0
- package/dist/edit/index.d.ts +18 -0
- package/dist/edit/index.js +16 -0
- package/dist/edit/lexer.d.ts +30 -0
- package/dist/edit/lexer.js +285 -0
- package/dist/edit/objects.d.ts +63 -0
- package/dist/edit/objects.js +56 -0
- package/dist/edit/writer.d.ts +53 -0
- package/dist/edit/writer.js +261 -0
- package/dist/elements/forms/checkbox-element.d.ts +51 -0
- package/dist/elements/forms/checkbox-element.js +48 -0
- package/dist/elements/forms/choice-element.d.ts +53 -0
- package/dist/elements/forms/choice-element.js +59 -0
- package/dist/elements/forms/push-button-element.d.ts +56 -0
- package/dist/elements/forms/push-button-element.js +49 -0
- package/dist/elements/forms/radio-element.d.ts +52 -0
- package/dist/elements/forms/radio-element.js +49 -0
- package/dist/elements/forms/signature-element.d.ts +54 -0
- package/dist/elements/forms/signature-element.js +47 -0
- package/dist/elements/forms/text-field-element.d.ts +61 -0
- package/dist/elements/forms/text-field-element.js +54 -0
- package/dist/elements/index.d.ts +6 -0
- package/dist/elements/index.js +6 -0
- package/dist/forms/acroform.d.ts +63 -0
- package/dist/forms/acroform.js +427 -0
- package/dist/forms/appearance.d.ts +58 -0
- package/dist/forms/appearance.js +204 -0
- package/dist/forms/field.d.ts +164 -0
- package/dist/forms/field.js +1 -0
- package/dist/forms/pdf.d.ts +19 -0
- package/dist/forms/pdf.js +28 -0
- package/dist/ir/display-list.d.ts +17 -1
- package/dist/renderer/form-field-renderer.d.ts +20 -0
- package/dist/renderer/form-field-renderer.js +12 -0
- package/dist/renderer/page-renderer.js +8 -2
- package/dist/renderer/pdf-backend.js +8 -0
- package/dist/renderer/pdf-renderer.js +19 -1
- package/dist/utils/dest-registry.d.ts +5 -2
- package/dist/utils/dest-registry.js +5 -5
- package/dist/utils/outline.js +1 -2
- package/dist/utils/pdf-object-manager.d.ts +18 -0
- package/dist/utils/pdf-object-manager.js +59 -6
- package/dist/utils/struct-tree.js +2 -3
- package/dist/validators/element-validator.js +7 -3
- package/package.json +9 -2
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { PDFObjectManager, FontStyle } from "../utils/pdf-object-manager.js";
|
|
2
|
+
import { checkboxOff, checkboxOn, fieldDrawSize, listBoxFace, radioOff, radioOn, textFieldFace, wrapFieldValue, } from "../forms/appearance.js";
|
|
3
|
+
import { Color } from "../common/color.js";
|
|
4
|
+
import { get, isDict, isString, numberOf } from "./objects.js";
|
|
5
|
+
import { latin1FromBytes } from "../utils/bytes.js";
|
|
6
|
+
/**
|
|
7
|
+
* Re-drawing a field's appearance after its value changed.
|
|
8
|
+
*
|
|
9
|
+
* A widget's appearance is a picture of its value. Change the value and the picture is wrong - which is
|
|
10
|
+
* why filling used to DROP it and ask the viewer to redraw (`/NeedAppearances`). That works, but it hands
|
|
11
|
+
* control of how the value looks to whatever program opens the file, and it leaves the field invisible in
|
|
12
|
+
* anything that does not honour the flag. Baking it is what jasy does when it CREATES a field; there is
|
|
13
|
+
* no reason the edit side should be different.
|
|
14
|
+
*
|
|
15
|
+
* The face builders in `forms/appearance.ts` need nothing from the writer - plain numbers, a style and
|
|
16
|
+
* pre-measured lines - so the only pieces to bring are the font metrics and the widget's own style, and
|
|
17
|
+
* the latter is readable straight out of the file: `/DA` for size and colour, `/MK` for the border and
|
|
18
|
+
* background, `/Q` for alignment, `/Ff` for multiline.
|
|
19
|
+
*/
|
|
20
|
+
const FF_MULTILINE = 1 << 12;
|
|
21
|
+
const FF_PASSWORD = 1 << 13;
|
|
22
|
+
/**
|
|
23
|
+
* Font metrics for measuring. Created per call rather than kept in a module-level variable: jasy has no
|
|
24
|
+
* global object manager by design (a shared one caused the mixed-page-size bug), and a reader that holds
|
|
25
|
+
* mutable state at module scope would quietly reintroduce it.
|
|
26
|
+
*/
|
|
27
|
+
export function fontMetrics() {
|
|
28
|
+
const om = new PDFObjectManager();
|
|
29
|
+
om.registerFont("Helvetica", FontStyle.Normal, "Helvetica");
|
|
30
|
+
return om;
|
|
31
|
+
}
|
|
32
|
+
/** `[r g b]` in 0..1, as `/MK` writes colours. An empty array means "none". */
|
|
33
|
+
function colorOf(raw) {
|
|
34
|
+
if (!Array.isArray(raw) || raw.length === 0)
|
|
35
|
+
return undefined;
|
|
36
|
+
const n = raw.map((x) => (typeof x === "number" ? x : 0));
|
|
37
|
+
if (n.length === 1)
|
|
38
|
+
return new Color(n[0] * 255, n[0] * 255, n[0] * 255); // grey
|
|
39
|
+
if (n.length >= 3)
|
|
40
|
+
return new Color(n[0] * 255, n[1] * 255, n[2] * 255);
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The bits of `/DA` we need: the font size and the fill colour.
|
|
45
|
+
*
|
|
46
|
+
* `/DA` is a content-stream fragment, not a dictionary - `"/Helv 12 Tf 0 0 0 rg"`. Size `0` is the PDF
|
|
47
|
+
* convention for "auto, fit the box", which is what a viewer would do; we resolve it the same way the
|
|
48
|
+
* writer does when it bakes.
|
|
49
|
+
*/
|
|
50
|
+
function fromDA(da) {
|
|
51
|
+
let size = 0;
|
|
52
|
+
let color = new Color(0, 0, 0);
|
|
53
|
+
if (da) {
|
|
54
|
+
const tf = /(-?[\d.]+)\s+Tf/.exec(da);
|
|
55
|
+
if (tf)
|
|
56
|
+
size = Number(tf[1]);
|
|
57
|
+
const rg = /(-?[\d.]+)\s+(-?[\d.]+)\s+(-?[\d.]+)\s+rg/.exec(da);
|
|
58
|
+
const g = /(-?[\d.]+)\s+g\b/.exec(da);
|
|
59
|
+
if (rg)
|
|
60
|
+
color = new Color(Number(rg[1]) * 255, Number(rg[2]) * 255, Number(rg[3]) * 255);
|
|
61
|
+
else if (g)
|
|
62
|
+
color = new Color(Number(g[1]) * 255, Number(g[1]) * 255, Number(g[1]) * 255);
|
|
63
|
+
}
|
|
64
|
+
return { size, color };
|
|
65
|
+
}
|
|
66
|
+
export function readLook(doc, widget, field) {
|
|
67
|
+
const rect = doc.lookup(widget, "Rect");
|
|
68
|
+
if (!Array.isArray(rect) || rect.length < 4)
|
|
69
|
+
return undefined;
|
|
70
|
+
const n = rect.map((x) => (typeof x === "number" ? x : NaN));
|
|
71
|
+
if (n.some(Number.isNaN))
|
|
72
|
+
return undefined;
|
|
73
|
+
const width = Math.abs(n[2] - n[0]);
|
|
74
|
+
const height = Math.abs(n[3] - n[1]);
|
|
75
|
+
// /DA may be on the widget, the field, or the form - it is inheritable.
|
|
76
|
+
const daOf = (o) => {
|
|
77
|
+
const v = doc.lookup(o, "DA");
|
|
78
|
+
return isString(v) ? latin1FromBytes(v.bytes) : undefined;
|
|
79
|
+
};
|
|
80
|
+
const acro = doc.lookup(doc.catalog, "AcroForm");
|
|
81
|
+
const da = daOf(widget) ??
|
|
82
|
+
(field.objNum !== undefined ? daOf(doc.getObject(field.objNum)) : undefined) ??
|
|
83
|
+
daOf(acro);
|
|
84
|
+
const { size, color } = fromDA(da);
|
|
85
|
+
const mk = doc.resolve(get(widget, "MK"));
|
|
86
|
+
const border = isDict(mk) ? colorOf(doc.lookup(mk, "BC")) : undefined;
|
|
87
|
+
const background = isDict(mk) ? colorOf(doc.lookup(mk, "BG")) : undefined;
|
|
88
|
+
const bs = doc.resolve(get(widget, "BS"));
|
|
89
|
+
const borderWidth = border ? (numberOf(isDict(bs) ? doc.lookup(bs, "W") : undefined) ?? 1) : 0;
|
|
90
|
+
// /Q is inheritable in the same chain as /DA: widget, then field, then the form.
|
|
91
|
+
const q = numberOf(doc.lookup(widget, "Q")) ??
|
|
92
|
+
(field.objNum !== undefined
|
|
93
|
+
? numberOf(doc.lookup(doc.getObject(field.objNum), "Q"))
|
|
94
|
+
: undefined) ??
|
|
95
|
+
numberOf(doc.lookup(acro, "Q")) ??
|
|
96
|
+
0;
|
|
97
|
+
return {
|
|
98
|
+
width,
|
|
99
|
+
height,
|
|
100
|
+
style: { border, background, color, fontSize: size, borderWidth },
|
|
101
|
+
align: q === 1 ? "center" : q === 2 ? "right" : "left",
|
|
102
|
+
multiline: (field.flags & FF_MULTILINE) !== 0,
|
|
103
|
+
password: (field.flags & FF_PASSWORD) !== 0,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/** The auto-size a viewer would pick: fit the cap height into the box, clamped to something readable. */
|
|
107
|
+
function drawSize(look, capHeight) {
|
|
108
|
+
return fieldDrawSize(look.style.fontSize, look.height, look.style.borderWidth, capHeight);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The content stream for a text or choice field showing `value`, and the BBox it is drawn in.
|
|
112
|
+
* Returns `undefined` for a kind we do not draw (a button keeps its own state pictures).
|
|
113
|
+
*/
|
|
114
|
+
export function bakeAppearance(look, field, value, values, metrics) {
|
|
115
|
+
if (field.type !== "Tx" && field.type !== "Ch")
|
|
116
|
+
return undefined;
|
|
117
|
+
const om = metrics ?? fontMetrics();
|
|
118
|
+
const { capHeight } = om.getFontDecoration("Helvetica", FontStyle.Normal);
|
|
119
|
+
const size = drawSize(look, capHeight);
|
|
120
|
+
const innerWidth = Math.max(1, look.width - 2 * (2 + look.style.borderWidth));
|
|
121
|
+
const measure = (t) => ({
|
|
122
|
+
text: t,
|
|
123
|
+
width: om.getStringWidth(t, "Helvetica", size, FontStyle.Normal),
|
|
124
|
+
});
|
|
125
|
+
let content;
|
|
126
|
+
if (field.type === "Ch" && field.options && look.height > size * 2) {
|
|
127
|
+
// A list box draws every option, with the selected ones highlighted.
|
|
128
|
+
const chosen = new Set(values ?? (value !== undefined ? [value] : []));
|
|
129
|
+
const rows = field.options.map((o) => ({
|
|
130
|
+
...measure(o.label),
|
|
131
|
+
selected: chosen.has(o.value),
|
|
132
|
+
}));
|
|
133
|
+
content = listBoxFace(look.width, look.height, look.style, rows, capHeight, size, "Helv");
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// A choice stores an EXPORT value but shows its label - "DE" in /V, "Germany" on screen.
|
|
137
|
+
const display = field.type === "Ch" ? (field.options?.find((o) => o.value === value)?.label ?? value) : value;
|
|
138
|
+
const shown = look.password ? "•".repeat([...(display ?? "")].length) : (display ?? "");
|
|
139
|
+
const lines = shown
|
|
140
|
+
? wrapFieldValue(shown, "Helvetica", size, FontStyle.Normal, innerWidth, om, look.multiline).map(measure)
|
|
141
|
+
: [];
|
|
142
|
+
content = textFieldFace(look.width, look.height, look.style, lines, capHeight, size, "Helv", look.multiline, look.align);
|
|
143
|
+
}
|
|
144
|
+
return { content, bbox: [0, 0, look.width, look.height] };
|
|
145
|
+
}
|
|
146
|
+
/** The `/DR` a regenerated appearance refers to: our own Helvetica, added when the form has none. */
|
|
147
|
+
export const HELV_FONT = "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>";
|
|
148
|
+
/** True when the form's `/DR /Font` already offers a usable `/Helv`. */
|
|
149
|
+
export function hasHelv(doc) {
|
|
150
|
+
const dr = doc.lookup(doc.lookup(doc.catalog, "AcroForm"), "DR");
|
|
151
|
+
const fonts = doc.lookup(dr, "Font");
|
|
152
|
+
return isDict(fonts) && fonts.map.has("Helv");
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* The two state pictures a check box or radio button needs, for a widget that ships none at all.
|
|
156
|
+
*
|
|
157
|
+
* A button's `/AP /N` is a dictionary of STATES, not a picture of a value, so an existing one stays
|
|
158
|
+
* valid however often the value changes - this is only for the producers that write none (PDFKit,
|
|
159
|
+
* react-pdf) and leave every box empty until a viewer redraws it. `on` is the export name the widget
|
|
160
|
+
* switches to.
|
|
161
|
+
*/
|
|
162
|
+
export function bakeButtonStates(look, on, radio) {
|
|
163
|
+
const w = look.width;
|
|
164
|
+
const h = look.height;
|
|
165
|
+
return {
|
|
166
|
+
on: radio ? radioOn(w, h, look.style) : checkboxOn(w, h, look.style),
|
|
167
|
+
off: radio ? radioOff(w, h, look.style) : checkboxOff(w, h, look.style),
|
|
168
|
+
bbox: [0, 0, w, h],
|
|
169
|
+
state: on,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { type PdfDict, type PdfObject, type PdfStream } from "./objects.js";
|
|
2
|
+
declare const EMPTY: Uint8Array<ArrayBuffer>;
|
|
3
|
+
/** Thrown when a file cannot be opened because of its encryption - always saying which of the reasons it
|
|
4
|
+
* is: no password, wrong password, or a revision we do not implement. */
|
|
5
|
+
export declare class PdfEncryptedError extends Error {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class PdfDocument {
|
|
9
|
+
readonly bytes: Uint8Array;
|
|
10
|
+
private readonly xref;
|
|
11
|
+
private readonly cache;
|
|
12
|
+
/** Object streams already unpacked, by their object number. */
|
|
13
|
+
private readonly objStmCache;
|
|
14
|
+
/** Set once an encrypted file has been opened with its password. */
|
|
15
|
+
private security?;
|
|
16
|
+
/** Decrypted stream payloads, keyed by the stream's data offset (unique per stream). */
|
|
17
|
+
private readonly plainStreams;
|
|
18
|
+
/** True when the cross-reference index was unusable and had to be rebuilt by scanning. Surfaced so
|
|
19
|
+
* a caller can report it - a silently repaired file is exactly the kind of guess we avoid. */
|
|
20
|
+
recovered: boolean;
|
|
21
|
+
trailer: PdfDict;
|
|
22
|
+
/** Whether this file indexes itself with an xref STREAM rather than a classic table. An incremental
|
|
23
|
+
* update has to follow suit: mixing the two leaves readers that understand only one kind unable to
|
|
24
|
+
* walk the chain back. */
|
|
25
|
+
usesXrefStream: boolean;
|
|
26
|
+
private constructor();
|
|
27
|
+
static load(bytes: Uint8Array): PdfDocument;
|
|
28
|
+
/**
|
|
29
|
+
* Open a document, decrypting it when it is password-protected.
|
|
30
|
+
*
|
|
31
|
+
* Async because key derivation and AES are (WebCrypto), and because decryption happens ONCE here,
|
|
32
|
+
* eagerly, rather than at every read: that keeps `getObject`, `streamData` and the whole form layer
|
|
33
|
+
* above them synchronous, which is the same trade the writer makes (`structure.ts` builds the handler
|
|
34
|
+
* before the synchronous constructor).
|
|
35
|
+
*/
|
|
36
|
+
static open(bytes: Uint8Array, opts?: {
|
|
37
|
+
password?: string;
|
|
38
|
+
}): Promise<PdfDocument>;
|
|
39
|
+
/**
|
|
40
|
+
* Build the handler for this file's `/Encrypt` dictionary, or say precisely why we cannot.
|
|
41
|
+
*
|
|
42
|
+
* Every refusal names its reason: no password given, wrong password, or an encryption revision we do
|
|
43
|
+
* not implement. "Cannot open this file" would be the unhelpful answer, and guessing would be worse.
|
|
44
|
+
*/
|
|
45
|
+
private securityHandlerFor;
|
|
46
|
+
/**
|
|
47
|
+
* Which cipher and key length actually apply to the streams.
|
|
48
|
+
*
|
|
49
|
+
* For V4 the answer is NOT the top-level `/Length`: the document names a crypt filter in `/StmF` and
|
|
50
|
+
* that filter carries its own `/CFM` and `/Length` (in BYTES, where the top-level one is in bits).
|
|
51
|
+
* `/Identity` means the streams are not enciphered at all - decrypting them anyway would destroy them.
|
|
52
|
+
*/
|
|
53
|
+
private streamCipher;
|
|
54
|
+
/**
|
|
55
|
+
* Decrypt every string and stream, once, in place.
|
|
56
|
+
*
|
|
57
|
+
* Three things are deliberately NOT decrypted, because they were never encrypted (ISO 32000-1 7.6.2):
|
|
58
|
+
* the `/Encrypt` dictionary's own strings, a cross-reference stream, and strings of objects that live
|
|
59
|
+
* inside an object stream - the object stream was enciphered as a whole, so its members come out in
|
|
60
|
+
* the clear already.
|
|
61
|
+
*/
|
|
62
|
+
private decryptEverything;
|
|
63
|
+
/** The byte offset in the `startxref` trailer at the very end of the file. */
|
|
64
|
+
private findStartXref;
|
|
65
|
+
/** Read the index at `startxref`, then walk `/Prev` back through earlier revisions. Entries already
|
|
66
|
+
* known win: the NEWEST revision is read first, and an older one must not overwrite it. */
|
|
67
|
+
private readXrefChain;
|
|
68
|
+
/** One index section, either format. Returns its trailer dictionary. */
|
|
69
|
+
private readXrefSection;
|
|
70
|
+
/** The classic table: `xref`, then `start count` subsections of 20-byte entries, then `trailer`. */
|
|
71
|
+
private readXrefTable;
|
|
72
|
+
/**
|
|
73
|
+
* An xref STREAM: fixed-width binary rows, widths in `/W`, object numbers from `/Index`. Row type 1
|
|
74
|
+
* is a plain byte offset, type 2 points into an object stream, type 0 is a free slot.
|
|
75
|
+
*/
|
|
76
|
+
private readXrefStream;
|
|
77
|
+
/** Record an entry unless a NEWER revision already claimed that object number. */
|
|
78
|
+
private note;
|
|
79
|
+
/**
|
|
80
|
+
* Last resort: scan the whole file for `N G obj` headers and believe those. Used when the index is
|
|
81
|
+
* missing or does not lead anywhere - and flagged via `recovered`, never silently.
|
|
82
|
+
*/
|
|
83
|
+
private rebuildByScanning;
|
|
84
|
+
/** The object with that number, or `undefined` when the file does not have it. */
|
|
85
|
+
getObject(num: number): PdfObject | undefined;
|
|
86
|
+
/** Parse `N G obj … endobj` at a byte offset, checking that it really is object `num`. */
|
|
87
|
+
private parseObjectAt;
|
|
88
|
+
/** Unpack an object stream once, then serve its members from the cache. */
|
|
89
|
+
private objectFromStream;
|
|
90
|
+
/** Follow indirect references until a direct object is reached. */
|
|
91
|
+
resolve(o: PdfObject | undefined): PdfObject | undefined;
|
|
92
|
+
/** A dictionary entry, with the reference already followed. */
|
|
93
|
+
lookup(o: PdfObject | undefined, key: string): PdfObject | undefined;
|
|
94
|
+
/**
|
|
95
|
+
* The file is password-protected. Every string and stream in it is enciphered, so anything read out
|
|
96
|
+
* of it is ciphertext until a decryption path exists - which is why the form layer refuses rather
|
|
97
|
+
* than handing back nonsense field names.
|
|
98
|
+
*/
|
|
99
|
+
get isEncrypted(): boolean;
|
|
100
|
+
/** True once the password has been accepted and the contents decrypted, so what is read is plaintext.
|
|
101
|
+
* Always true for a document that was never encrypted. */
|
|
102
|
+
get isReadable(): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Encipher a value that is about to be written back INTO this document, or `undefined` when the
|
|
105
|
+
* document is not encrypted and the value goes in as it is.
|
|
106
|
+
*
|
|
107
|
+
* Needed because we decrypt everything on open: a string we then hand back to the writer is plaintext
|
|
108
|
+
* in memory, and writing it into an encrypted file unchanged would both leak it and produce a document
|
|
109
|
+
* that contradicts its own /Encrypt declaration.
|
|
110
|
+
*/
|
|
111
|
+
encryptForWrite(data: Uint8Array): Promise<Uint8Array | undefined>;
|
|
112
|
+
/**
|
|
113
|
+
* Whether values written back into this document can be enciphered again.
|
|
114
|
+
*
|
|
115
|
+
* False for a legacy-encrypted file: we can READ RC4 and AES-128, but writing one would downgrade the
|
|
116
|
+
* user's document to a broken cipher. Asked BEFORE any work is done - otherwise the refusal depends on
|
|
117
|
+
* a string happening to be present in whatever we rewrite, which is a guarantee by accident.
|
|
118
|
+
*/
|
|
119
|
+
get canReEncrypt(): boolean;
|
|
120
|
+
/** The generation of an object as the file records it. Members of an object stream are always 0. */
|
|
121
|
+
generationOf(num: number): number;
|
|
122
|
+
get catalog(): PdfObject | undefined;
|
|
123
|
+
/** A stream's bytes as they sit in the file: sliced by `/Length`, still filtered and still encrypted. */
|
|
124
|
+
private rawStream;
|
|
125
|
+
/** A stream's DECODED bytes: deciphered if the document is encrypted, then run through its filters. */
|
|
126
|
+
streamData(stream: PdfStream): Uint8Array;
|
|
127
|
+
/**
|
|
128
|
+
* Undo a PNG predictor (`/Predictor` 10-15), which xref streams almost always use - the IRS form in
|
|
129
|
+
* our fixtures does. Each row is prefixed with its filter type and is decoded against the row above.
|
|
130
|
+
*/
|
|
131
|
+
private undoPredictor;
|
|
132
|
+
/** Every object number the index knows, ascending - for walking a whole document. */
|
|
133
|
+
objectNumbers(): number[];
|
|
134
|
+
}
|
|
135
|
+
export { EMPTY };
|