@quillmark/quillify 0.0.0-stage → 0.3.1
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 +201 -0
- package/NOTICE +85 -0
- package/README.md +56 -2
- package/assets/LiberationSans-Bold.ttf +0 -0
- package/assets/LiberationSans-BoldItalic.ttf +0 -0
- package/assets/LiberationSans-Italic.ttf +0 -0
- package/assets/LiberationSans-Regular.ttf +0 -0
- package/assets/pdf.worker.js +64908 -0
- package/assets/qpdf.wasm +0 -0
- package/assets/qpdf.worker.js +155 -0
- package/assets/xfa-layer.d.ts +2 -0
- package/assets/xfa-layer.js +1 -0
- package/dist/api.d.ts +83 -0
- package/dist/api.js +1 -0
- package/dist/assemble.d.ts +21 -0
- package/dist/assemble.js +155 -0
- package/dist/coords.d.ts +7 -0
- package/dist/coords.js +28 -0
- package/dist/display-list.d.ts +87 -0
- package/dist/display-list.js +372 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +4 -0
- package/dist/inventory.d.ts +4 -0
- package/dist/inventory.js +179 -0
- package/dist/naming.d.ts +3 -0
- package/dist/naming.js +116 -0
- package/dist/normalize.d.ts +39 -0
- package/dist/normalize.js +303 -0
- package/dist/pdfjs.d.ts +17 -0
- package/dist/pdfjs.js +96 -0
- package/dist/quillify.d.ts +13 -0
- package/dist/quillify.js +86 -0
- package/dist/refusal.d.ts +47 -0
- package/dist/refusal.js +8 -0
- package/dist/sanitize.d.ts +20 -0
- package/dist/sanitize.js +72 -0
- package/dist/strip.d.ts +82 -0
- package/dist/strip.js +533 -0
- package/dist/types.d.ts +125 -0
- package/dist/types.js +42 -0
- package/dist/xfa-layer.d.ts +40 -0
- package/dist/xfa-layer.js +143 -0
- package/dist/xfa-widgets.d.ts +36 -0
- package/dist/xfa-widgets.js +131 -0
- package/dist/xfa.d.ts +15 -0
- package/dist/xfa.js +371 -0
- package/licenses/LiberationSans-LICENSE +102 -0
- package/licenses/compiler-rt-LICENSE +311 -0
- package/licenses/emscripten-LICENSE +102 -0
- package/licenses/libcxx-LICENSE +311 -0
- package/licenses/libcxxabi-LICENSE +311 -0
- package/licenses/libjpeg-turbo-LICENSE +132 -0
- package/licenses/libjpeg-turbo-README.ijg +258 -0
- package/licenses/musl-COPYRIGHT +193 -0
- package/licenses/pdf.js-LICENSE +177 -0
- package/licenses/qpdf-LICENSE +202 -0
- package/licenses/qpdf-NOTICE +42 -0
- package/licenses/zlib-README +118 -0
- package/package.json +70 -5
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Why a PDF is not taken. A `page` is zero-based, as a `Place`'s is. */
|
|
2
|
+
export type Refusal = {
|
|
3
|
+
code: 'too_large';
|
|
4
|
+
bytes: number;
|
|
5
|
+
limit: number;
|
|
6
|
+
} | {
|
|
7
|
+
code: 'not_pdf';
|
|
8
|
+
} | {
|
|
9
|
+
code: 'password';
|
|
10
|
+
} | {
|
|
11
|
+
code: 'encryption';
|
|
12
|
+
} | {
|
|
13
|
+
code: 'damaged';
|
|
14
|
+
} | {
|
|
15
|
+
code: 'rotated';
|
|
16
|
+
page: number;
|
|
17
|
+
} | {
|
|
18
|
+
code: 'portfolio';
|
|
19
|
+
attachment?: string;
|
|
20
|
+
} | {
|
|
21
|
+
code: 'no_fields';
|
|
22
|
+
xfa: boolean;
|
|
23
|
+
} | {
|
|
24
|
+
code: 'too_many_pages';
|
|
25
|
+
pages: number;
|
|
26
|
+
limit: number;
|
|
27
|
+
} | {
|
|
28
|
+
code: 'too_many_fields';
|
|
29
|
+
fields: number;
|
|
30
|
+
limit: number;
|
|
31
|
+
} | {
|
|
32
|
+
code: 'layout';
|
|
33
|
+
detail: string;
|
|
34
|
+
} | {
|
|
35
|
+
code: 'too_slow';
|
|
36
|
+
limit: number;
|
|
37
|
+
} | {
|
|
38
|
+
code: 'unpaintable';
|
|
39
|
+
page: number;
|
|
40
|
+
construct: 'image' | 'background_image' | 'svg' | 'border' | 'transform' | 'glyph' | 'barcode' | 'password';
|
|
41
|
+
detail?: string;
|
|
42
|
+
};
|
|
43
|
+
/** Thrown where a stage declines a PDF. */
|
|
44
|
+
export declare class Refused extends Error {
|
|
45
|
+
readonly refusal: Refusal;
|
|
46
|
+
constructor(refusal: Refusal);
|
|
47
|
+
}
|
package/dist/refusal.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type Refusal } from './refusal.js';
|
|
2
|
+
import type { Form } from './api.js';
|
|
3
|
+
export type Sanitized = {
|
|
4
|
+
ok: true;
|
|
5
|
+
form: Form;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
refusal: Refusal;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* A PDF form as its fields and a background with no form layer, or the reason
|
|
12
|
+
* the file is not taken. Nothing else in the package reads the file.
|
|
13
|
+
*
|
|
14
|
+
* One signal fires `MAX_SECONDS` after the call starts. A stage that is
|
|
15
|
+
* waiting on a worker then ends the worker and throws the signal's reason,
|
|
16
|
+
* as does an open that begins after it, and that reason is the refusal
|
|
17
|
+
* `too_slow`. Work on this thread is not interrupted and not timed: the
|
|
18
|
+
* strip starts no worker, so a call that has reached it returns its form.
|
|
19
|
+
*/
|
|
20
|
+
export declare function sanitize(pdf: Uint8Array): Promise<Sanitized>;
|
package/dist/sanitize.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { inventoryPdf } from './inventory.js';
|
|
2
|
+
import { normalizePdf } from './normalize.js';
|
|
3
|
+
import { Refused } from './refusal.js';
|
|
4
|
+
import { stripAcroForm } from './strip.js';
|
|
5
|
+
import { xfaToAcroForm } from './xfa.js';
|
|
6
|
+
import { MAX_SECONDS } from './types.js';
|
|
7
|
+
function field(widget) {
|
|
8
|
+
const [place, ...more] = widget.places;
|
|
9
|
+
const { id, required, readOnly, tooltip } = widget;
|
|
10
|
+
const common = {
|
|
11
|
+
id,
|
|
12
|
+
places: [place, ...more],
|
|
13
|
+
...(tooltip === undefined ? {} : { tooltip }),
|
|
14
|
+
required,
|
|
15
|
+
readOnly
|
|
16
|
+
};
|
|
17
|
+
switch (widget.kind) {
|
|
18
|
+
case 'Tx':
|
|
19
|
+
return {
|
|
20
|
+
...common,
|
|
21
|
+
kind: 'text',
|
|
22
|
+
multiline: widget.multiLine,
|
|
23
|
+
...(widget.maxLen > 0 ? { maxLength: widget.maxLen } : {})
|
|
24
|
+
};
|
|
25
|
+
case 'Btn':
|
|
26
|
+
// The inventory keeps no push button, so a button that is no radio is a checkbox.
|
|
27
|
+
if (!widget.radioButton)
|
|
28
|
+
return { ...common, kind: 'checkbox' };
|
|
29
|
+
return {
|
|
30
|
+
...common,
|
|
31
|
+
kind: 'radio',
|
|
32
|
+
group: widget.name,
|
|
33
|
+
...(widget.buttonValue === undefined ? {} : { on: widget.buttonValue })
|
|
34
|
+
};
|
|
35
|
+
case 'Ch':
|
|
36
|
+
return { ...common, kind: 'choice', options: widget.options };
|
|
37
|
+
case 'Sig':
|
|
38
|
+
return { ...common, kind: 'signature' };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A PDF form as its fields and a background with no form layer, or the reason
|
|
43
|
+
* the file is not taken. Nothing else in the package reads the file.
|
|
44
|
+
*
|
|
45
|
+
* One signal fires `MAX_SECONDS` after the call starts. A stage that is
|
|
46
|
+
* waiting on a worker then ends the worker and throws the signal's reason,
|
|
47
|
+
* as does an open that begins after it, and that reason is the refusal
|
|
48
|
+
* `too_slow`. Work on this thread is not interrupted and not timed: the
|
|
49
|
+
* strip starts no worker, so a call that has reached it returns its form.
|
|
50
|
+
*/
|
|
51
|
+
export async function sanitize(pdf) {
|
|
52
|
+
// Handing it anything but a Uint8Array is a mistake in the call and no fault of a file, so the call throws.
|
|
53
|
+
// The tag, not instanceof, so that a Uint8Array made in another window is taken.
|
|
54
|
+
if (Object.prototype.toString.call(pdf) !== '[object Uint8Array]')
|
|
55
|
+
throw new TypeError('sanitize takes a Uint8Array.');
|
|
56
|
+
const deadline = AbortSignal.timeout(MAX_SECONDS * 1000);
|
|
57
|
+
try {
|
|
58
|
+
const bytes = await xfaToAcroForm(await normalizePdf(pdf, deadline), deadline);
|
|
59
|
+
const { pages, widgets, views } = await inventoryPdf(bytes, deadline);
|
|
60
|
+
return {
|
|
61
|
+
ok: true,
|
|
62
|
+
form: { pdf: await stripAcroForm(bytes, views), pages, fields: widgets.map(field) }
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
if (deadline.aborted && err === deadline.reason)
|
|
67
|
+
return { ok: false, refusal: { code: 'too_slow', limit: MAX_SECONDS } };
|
|
68
|
+
if (err instanceof Refused)
|
|
69
|
+
return { ok: false, refusal: err.refusal };
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
}
|
package/dist/strip.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { View } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* What a background's dictionaries keep. A kind of dictionary lists its keys,
|
|
4
|
+
* and after a colon what a key leads to:
|
|
5
|
+
*
|
|
6
|
+
* Key a number, a name, a string, a boolean, null, or an array of those
|
|
7
|
+
* Key:kind a dictionary or a stream of that kind
|
|
8
|
+
* Key:[kind] one of them or an array of them
|
|
9
|
+
* Key:{kind} a dictionary of them by name
|
|
10
|
+
* Key:^kind a reference to one the walk reaches as that kind by another key
|
|
11
|
+
* Key:[^kind] such a reference, or an array of them and of arrays of them
|
|
12
|
+
* Key:* a value no row reads yet, kept whole
|
|
13
|
+
*
|
|
14
|
+
* A reference back can be to one of two kinds, `Key:^kind|kind`. A dictionary
|
|
15
|
+
* by name can hold one of a kind or an array of them, `Key:{[kind]}`, or values
|
|
16
|
+
* of the first sort and nothing else, `Key:{}`.
|
|
17
|
+
*
|
|
18
|
+
* A value of the first sort also stays where a kind is expected: a mask can be
|
|
19
|
+
* an array of numbers and a color space a name. A kind `STREAMS` names is a
|
|
20
|
+
* stream, which keeps the keys of `STREAM` beside its own, and no other kind is
|
|
21
|
+
* a stream. A kind whose keys are numbers is an array read by position, as a
|
|
22
|
+
* color space is: it loses every part past the last its row lists, and a part
|
|
23
|
+
* before that which is not what its position leads to becomes null. A kind
|
|
24
|
+
* `PICK` names is one of several, told apart by a key of the dictionary or a
|
|
25
|
+
* position of the array, which holds a name where `PICK` writes one and a
|
|
26
|
+
* number where it writes a number. Where `PICK` names a last kind, one that is
|
|
27
|
+
* told as none of the others is of that kind.
|
|
28
|
+
*/
|
|
29
|
+
export declare const KINDS: Record<string, string>;
|
|
30
|
+
export declare const STREAMS: string[];
|
|
31
|
+
export declare const STREAM = "Length Filter DecodeParms:[parms]";
|
|
32
|
+
export declare const PICK: Record<string, [key: string, kinds: Record<string, string>, other?: string]>;
|
|
33
|
+
/**
|
|
34
|
+
* The `acroform` background: the source file's pages, and of each dictionary
|
|
35
|
+
* they hold the keys `KINDS` lists for its kind. With the rest of the catalog
|
|
36
|
+
* go the form (`/AcroForm` with its XFA packet, `/NeedsRendering`), the XMP
|
|
37
|
+
* metadata, the scripts, attachments, portfolio and bookmarks, and the
|
|
38
|
+
* usage-rights signature under `/Perms`. The signature goes here because qpdf's
|
|
39
|
+
* `--remove-restrictions`, which takes it too, also strips `/FT /Sig` from
|
|
40
|
+
* every signature field. The trailer's `/Info` and `/ID` go too.
|
|
41
|
+
*
|
|
42
|
+
* Of a page's annotations a Link stays, when it leads to an http or https
|
|
43
|
+
* address or to an explicit destination in the file, and leads there one way.
|
|
44
|
+
* A named destination resolves through the catalog's `/Dests` or `/Names`,
|
|
45
|
+
* which go, so a link to one goes too, and so does a link with an action
|
|
46
|
+
* chained after its own. The action a link keeps is a copy, so nothing the file
|
|
47
|
+
* hangs on the same object is cut with it.
|
|
48
|
+
*
|
|
49
|
+
* Each page's MediaBox is set to the box of its entry of `views`, the frame the
|
|
50
|
+
* inventory measured that page's widgets in. No box of the file is read here. A
|
|
51
|
+
* file whose pages are not the objects `views` names, in that order, is one
|
|
52
|
+
* pdf.js and pdf-lib read differently, and is refused as `damaged`.
|
|
53
|
+
*
|
|
54
|
+
* A walk from the catalog, level by level, then gives each object one kind, by
|
|
55
|
+
* the key that reaches it. A dictionary loses every key its kind does not list
|
|
56
|
+
* and every value that is not what its key leads to, and an object the walk
|
|
57
|
+
* does not reach is deleted: a saved file carries every object the context
|
|
58
|
+
* holds, reachable or not. A file in which a key reaches an object as another
|
|
59
|
+
* kind than it has is refused as `damaged`. A reference back to an object the
|
|
60
|
+
* walk did not reach as its kind is deleted, or made null in an array: a
|
|
61
|
+
* destination's page that the page tree does not hold, and a layer the default
|
|
62
|
+
* configuration names that no other key reaches.
|
|
63
|
+
*
|
|
64
|
+
* The structure tree has rows as the rest has. Of what lies outside it, it
|
|
65
|
+
* names a page of the page tree and a link that stays, each by a reference
|
|
66
|
+
* back, and nothing else: an element's `/Pg` to any other object is deleted,
|
|
67
|
+
* and an object reference whose `/Obj` is not such a link is taken out of its
|
|
68
|
+
* element's `/K`.
|
|
69
|
+
*
|
|
70
|
+
* An object that says `/Type /Annot`, `/Subtype /Widget` or `/Type /Page`, and
|
|
71
|
+
* is neither a page of `views` nor a link that stays, has no kind. A file in
|
|
72
|
+
* which a key reaches one as a kind is refused as `damaged`, the catalog itself
|
|
73
|
+
* included, and so is a file whose catalog or page tree pdf-lib cannot read. A
|
|
74
|
+
* marked-content property list may say `/Type /Page`, and a `/Rect` says
|
|
75
|
+
* nothing: a kept object loses the keys its row does not list. So no object of
|
|
76
|
+
* a background is a widget or a page outside the page tree, and the fields and
|
|
77
|
+
* appearance streams that hung off one are out of the walk's reach: a
|
|
78
|
+
* background that still carries a form layer is one the renderer would stamp a
|
|
79
|
+
* second one over. A dictionary written in place is not an object: one by name
|
|
80
|
+
* keeps the plain values a file writes in it, a widget's among them.
|
|
81
|
+
*/
|
|
82
|
+
export declare function stripAcroForm(bytes: Uint8Array, views: View[]): Promise<Uint8Array>;
|