@quillmark/quillify 0.0.0-stage → 0.3.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 +201 -0
- package/NOTICE +85 -0
- package/README.md +55 -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 +148 -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 +84 -0
- package/dist/display-list.js +339 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/inventory.d.ts +4 -0
- package/dist/inventory.js +175 -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 +159 -0
- package/dist/pdfjs.d.ts +17 -0
- package/dist/pdfjs.js +95 -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 +77 -0
- package/dist/strip.js +524 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.js +27 -0
- package/dist/xfa-layer.d.ts +40 -0
- package/dist/xfa-layer.js +139 -0
- package/dist/xfa-widgets.d.ts +36 -0
- package/dist/xfa-widgets.js +128 -0
- package/dist/xfa.d.ts +15 -0
- package/dist/xfa.js +345 -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 +69 -5
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { PDFPageProxy } from 'pdfjs-dist';
|
|
2
|
+
/** One page after the browser has solved its layout. */
|
|
3
|
+
export interface XfaPage {
|
|
4
|
+
layer: HTMLElement;
|
|
5
|
+
/** The page box in client coordinates. Every harvested rect is read against it. */
|
|
6
|
+
origin: DOMRect;
|
|
7
|
+
/** Page size in points. pdf.js normalizes XFA to points and emits CSS px, so 1px is 1pt. */
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Somewhere out of sight to lay XFA pages out: a frame, whose document takes no
|
|
13
|
+
* rule from the host page and gives it none. `display: none` is not an option:
|
|
14
|
+
* it zeroes every rect, and it does that silently. The frame is hidden, out of
|
|
15
|
+
* the flow and of no size instead, so the host page shows, lays out and
|
|
16
|
+
* scrolls as it did, and the form inside is laid out all the same. A host page
|
|
17
|
+
* can still take the frame's boxes away, and `lay` throws when one has. It can
|
|
18
|
+
* take the frame out before it has loaded, and `ready` then rejects. And it
|
|
19
|
+
* can zoom the frame, which the frame undoes, or `lay` throws.
|
|
20
|
+
*
|
|
21
|
+
* The frame runs no script, and its policy lets its document load only an
|
|
22
|
+
* image from `blob:` or `data:`, which is how pdf.js hands it a form's images
|
|
23
|
+
* and the stylesheet its field background. A face of the form is not the
|
|
24
|
+
* document's to load: pdf.js makes it in the host page's realm, from the
|
|
25
|
+
* file's bytes, a `local()` name or a face the package ships.
|
|
26
|
+
*/
|
|
27
|
+
export declare class XfaStage {
|
|
28
|
+
#private;
|
|
29
|
+
/** The frame's document, once the stylesheet `XfaLayer.render` counts on applies in it. */
|
|
30
|
+
readonly ready: Promise<Document>;
|
|
31
|
+
constructor();
|
|
32
|
+
/**
|
|
33
|
+
* Lay one page out. Each page gets its own relative wrapper: `.xfaLayer` is
|
|
34
|
+
* absolutely positioned, so sibling layers would stack at the initial
|
|
35
|
+
* containing block and every page after the first would measure against the
|
|
36
|
+
* wrong origin.
|
|
37
|
+
*/
|
|
38
|
+
lay(page: PDFPageProxy): Promise<XfaPage>;
|
|
39
|
+
close(): void;
|
|
40
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import css from '../assets/xfa-layer.js';
|
|
2
|
+
import { Refused } from './refusal.js';
|
|
3
|
+
/** `XfaLayer.render` reads these off a link service; XFA forms here carry no links. */
|
|
4
|
+
const NO_LINKS = {
|
|
5
|
+
addLinkAttributes() { },
|
|
6
|
+
externalLinkTarget: 0,
|
|
7
|
+
externalLinkRel: '',
|
|
8
|
+
externalLinkEnabled: true,
|
|
9
|
+
getDestinationHash: () => '',
|
|
10
|
+
getAnchorUrl: () => ''
|
|
11
|
+
};
|
|
12
|
+
const SHELL = `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src blob: data:">`;
|
|
13
|
+
/**
|
|
14
|
+
* Somewhere out of sight to lay XFA pages out: a frame, whose document takes no
|
|
15
|
+
* rule from the host page and gives it none. `display: none` is not an option:
|
|
16
|
+
* it zeroes every rect, and it does that silently. The frame is hidden, out of
|
|
17
|
+
* the flow and of no size instead, so the host page shows, lays out and
|
|
18
|
+
* scrolls as it did, and the form inside is laid out all the same. A host page
|
|
19
|
+
* can still take the frame's boxes away, and `lay` throws when one has. It can
|
|
20
|
+
* take the frame out before it has loaded, and `ready` then rejects. And it
|
|
21
|
+
* can zoom the frame, which the frame undoes, or `lay` throws.
|
|
22
|
+
*
|
|
23
|
+
* The frame runs no script, and its policy lets its document load only an
|
|
24
|
+
* image from `blob:` or `data:`, which is how pdf.js hands it a form's images
|
|
25
|
+
* and the stylesheet its field background. A face of the form is not the
|
|
26
|
+
* document's to load: pdf.js makes it in the host page's realm, from the
|
|
27
|
+
* file's bytes, a `local()` name or a face the package ships.
|
|
28
|
+
*/
|
|
29
|
+
export class XfaStage {
|
|
30
|
+
#frame = document.createElement('iframe');
|
|
31
|
+
/** The frame's document, once the stylesheet `XfaLayer.render` counts on applies in it. */
|
|
32
|
+
ready;
|
|
33
|
+
constructor() {
|
|
34
|
+
const frame = this.#frame;
|
|
35
|
+
frame.sandbox.add('allow-same-origin');
|
|
36
|
+
frame.style.cssText = 'position:fixed;width:0;height:0;border:0;visibility:hidden';
|
|
37
|
+
frame.srcdoc = SHELL;
|
|
38
|
+
this.ready = new Promise((loaded, removed) => {
|
|
39
|
+
// A frame that leaves the document before it has loaded never loads.
|
|
40
|
+
const left = () => {
|
|
41
|
+
if (document.contains(frame))
|
|
42
|
+
return;
|
|
43
|
+
watch.disconnect();
|
|
44
|
+
removed(new Error('The host page removed the frame the form is laid out in before it had loaded.'));
|
|
45
|
+
};
|
|
46
|
+
const watch = new MutationObserver(left);
|
|
47
|
+
frame.onload = () => {
|
|
48
|
+
watch.disconnect();
|
|
49
|
+
loaded();
|
|
50
|
+
};
|
|
51
|
+
document.body.append(frame);
|
|
52
|
+
// A zoom on the frame or an ancestor scales the layout inside it. The
|
|
53
|
+
// frame's own zoom undoes its ancestors', where the browser tells it.
|
|
54
|
+
const zoom = document.body.currentCSSZoom;
|
|
55
|
+
if (zoom)
|
|
56
|
+
frame.style.zoom = String(1 / zoom);
|
|
57
|
+
watch.observe(document, { childList: true, subtree: true });
|
|
58
|
+
left();
|
|
59
|
+
}).then(() => {
|
|
60
|
+
const frameDocument = frame.contentDocument;
|
|
61
|
+
const view = frameDocument.defaultView;
|
|
62
|
+
const sheet = new view.CSSStyleSheet();
|
|
63
|
+
sheet.replaceSync(css);
|
|
64
|
+
frameDocument.adoptedStyleSheets = [sheet];
|
|
65
|
+
// A sheet that did not apply leaves every field at its content size,
|
|
66
|
+
// and the rects read off it are wrong with no error.
|
|
67
|
+
const probe = frameDocument.createElement('div');
|
|
68
|
+
probe.className = 'xfaLayer';
|
|
69
|
+
frameDocument.body.append(probe);
|
|
70
|
+
const applies = view.getComputedStyle(probe).position === 'absolute';
|
|
71
|
+
probe.remove();
|
|
72
|
+
if (!applies)
|
|
73
|
+
throw new Error('The XFA stylesheet did not apply to the stage.');
|
|
74
|
+
// pdf.js names a face with its family in quotes. Chromium keeps the quotes
|
|
75
|
+
// as part of the name, and no rule of the form then asks for that face.
|
|
76
|
+
const faces = frameDocument.fonts;
|
|
77
|
+
const add = faces.add.bind(faces);
|
|
78
|
+
faces.add = (face) => {
|
|
79
|
+
if (/^".*"$/.test(face.family))
|
|
80
|
+
face.family = face.family.slice(1, -1);
|
|
81
|
+
return add(face);
|
|
82
|
+
};
|
|
83
|
+
return frameDocument;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Lay one page out. Each page gets its own relative wrapper: `.xfaLayer` is
|
|
88
|
+
* absolutely positioned, so sibling layers would stack at the initial
|
|
89
|
+
* containing block and every page after the first would measure against the
|
|
90
|
+
* wrong origin.
|
|
91
|
+
*/
|
|
92
|
+
async lay(page) {
|
|
93
|
+
const pdfjs = await import('pdfjs-dist');
|
|
94
|
+
const xfa = await page.getXfa();
|
|
95
|
+
if (!xfa)
|
|
96
|
+
throw new Error(`XFA page ${page.pageNumber} laid out to nothing.`);
|
|
97
|
+
const viewport = pdfjs.XfaLayer.getPageViewport(xfa, { scale: 1, rotation: 0 });
|
|
98
|
+
// pdf.js lays out a page area whose medium has no size, and gives the page
|
|
99
|
+
// a side of nothing, a negative one, or no number at all.
|
|
100
|
+
if (!(viewport.width > 0 && viewport.height > 0))
|
|
101
|
+
throw new Refused({ code: 'layout', detail: 'a page has no size' });
|
|
102
|
+
const frameDocument = await this.ready;
|
|
103
|
+
const host = frameDocument.createElement('div');
|
|
104
|
+
// On Linux Chromium fits text to the pixel grid and rounds each advance to
|
|
105
|
+
// a whole pixel, unless the text asks for geometric precision.
|
|
106
|
+
host.style.cssText = `position:relative;overflow:hidden;background:#fff;text-rendering:geometricPrecision;width:${viewport.width}px;height:${viewport.height}px`;
|
|
107
|
+
const layer = frameDocument.createElement('div');
|
|
108
|
+
layer.className = 'xfaLayer';
|
|
109
|
+
host.append(layer);
|
|
110
|
+
frameDocument.body.append(host);
|
|
111
|
+
pdfjs.XfaLayer.render({
|
|
112
|
+
viewport: viewport.clone({ dontFlip: true }),
|
|
113
|
+
div: layer,
|
|
114
|
+
xfaHtml: xfa,
|
|
115
|
+
linkService: NO_LINKS,
|
|
116
|
+
intent: 'display'
|
|
117
|
+
});
|
|
118
|
+
await frameDocument.fonts.ready;
|
|
119
|
+
// A picture has a size once it has loaded, and one that fails to has none.
|
|
120
|
+
await Promise.all(Array.from(layer.querySelectorAll('img'), (img) => img.complete || new Promise((done) => (img.onload = img.onerror = done))));
|
|
121
|
+
// A frame the host page hid or took out gives the wrapper no box.
|
|
122
|
+
if (!host.getClientRects().length)
|
|
123
|
+
throw new Error('The host page hid or removed the frame the form is laid out in.');
|
|
124
|
+
// The frame's zoom times its ancestors' is a 32-bit float, and for some
|
|
125
|
+
// zooms no value brings it to 1. A rule marked `!important` beats the
|
|
126
|
+
// frame's own.
|
|
127
|
+
if ((this.#frame.currentCSSZoom ?? 1) !== 1)
|
|
128
|
+
throw new Error("The host page's zoom scales the frame the form is laid out in.");
|
|
129
|
+
return {
|
|
130
|
+
layer,
|
|
131
|
+
origin: host.getBoundingClientRect(),
|
|
132
|
+
width: viewport.width,
|
|
133
|
+
height: viewport.height
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
close() {
|
|
137
|
+
this.#frame.remove();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { XfaPage } from './xfa-layer.js';
|
|
2
|
+
export type XfaKind = 'text' | 'multiline' | 'checkbox' | 'radio' | 'choice' | 'signature';
|
|
3
|
+
export interface XfaWidget {
|
|
4
|
+
/**
|
|
5
|
+
* `p<page>.<SOM path>`, with `#n` where that repeats. It is the AcroForm field
|
|
6
|
+
* name of every control but a radio, whose field is its group's.
|
|
7
|
+
*/
|
|
8
|
+
name: string;
|
|
9
|
+
kind: XfaKind;
|
|
10
|
+
page: number;
|
|
11
|
+
/** Points, top-left origin, page-relative: the rect `form.json` wants. */
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
w: number;
|
|
15
|
+
h: number;
|
|
16
|
+
/** The exclusion group this radio belongs to, as a SOM path. */
|
|
17
|
+
group?: string;
|
|
18
|
+
/** This radio's own on-state, which is how the group tells its options apart. */
|
|
19
|
+
on?: string;
|
|
20
|
+
tooltip?: string;
|
|
21
|
+
options: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Where the browser paints a check box or a radio button. pdf.js gives the
|
|
25
|
+
* control the width its template sets and the height of what is left of its
|
|
26
|
+
* row, and the browser paints the largest square centered in that box.
|
|
27
|
+
*/
|
|
28
|
+
export declare function square(box: DOMRect): Pick<DOMRect, 'left' | 'top' | 'width' | 'height'>;
|
|
29
|
+
/**
|
|
30
|
+
* Every control on a laid-out page, named after what the form called it.
|
|
31
|
+
*
|
|
32
|
+
* What the form called it is its SOM path: the `xfaname` attributes from the
|
|
33
|
+
* layer root down to the control. Its `id` or `name` is read only when there
|
|
34
|
+
* is none.
|
|
35
|
+
*/
|
|
36
|
+
export declare function harvestWidgets(page: XfaPage, pageNumber: number): XfaWidget[];
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Refused } from './refusal.js';
|
|
2
|
+
/**
|
|
3
|
+
* Where the browser paints a check box or a radio button. pdf.js gives the
|
|
4
|
+
* control the width its template sets and the height of what is left of its
|
|
5
|
+
* row, and the browser paints the largest square centered in that box.
|
|
6
|
+
*/
|
|
7
|
+
export function square(box) {
|
|
8
|
+
const side = Math.min(box.width, box.height);
|
|
9
|
+
return {
|
|
10
|
+
left: box.left + (box.width - side) / 2,
|
|
11
|
+
top: box.top + (box.height - side) / 2,
|
|
12
|
+
width: side,
|
|
13
|
+
height: side
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Every control on a laid-out page, named after what the form called it.
|
|
18
|
+
*
|
|
19
|
+
* What the form called it is its SOM path: the `xfaname` attributes from the
|
|
20
|
+
* layer root down to the control. Its `id` or `name` is read only when there
|
|
21
|
+
* is none.
|
|
22
|
+
*/
|
|
23
|
+
export function harvestWidgets(page, pageNumber) {
|
|
24
|
+
const { layer, origin } = page;
|
|
25
|
+
const view = layer.ownerDocument.defaultView;
|
|
26
|
+
const widgets = [];
|
|
27
|
+
const seen = new Map();
|
|
28
|
+
const places = new Set();
|
|
29
|
+
// No field of a quill prints a barcode of what was entered, or hides it.
|
|
30
|
+
for (const [selector, construct] of [
|
|
31
|
+
['.xfaBarcode', 'barcode'],
|
|
32
|
+
['.xfaPasswordEdit', 'password']
|
|
33
|
+
])
|
|
34
|
+
if (layer.querySelector(selector))
|
|
35
|
+
throw new Refused({ code: 'unpaintable', page: pageNumber - 1, construct });
|
|
36
|
+
// A signature field is read after every control, so a control that shares its
|
|
37
|
+
// SOM path keeps the occurrence count it has without it.
|
|
38
|
+
for (const el of [
|
|
39
|
+
...layer.querySelectorAll('input, select, textarea'),
|
|
40
|
+
...layer.querySelectorAll('.xfaSignature')
|
|
41
|
+
]) {
|
|
42
|
+
const style = view.getComputedStyle(el);
|
|
43
|
+
if (style.visibility === 'hidden' || style.display === 'none')
|
|
44
|
+
continue;
|
|
45
|
+
const box = el.getBoundingClientRect();
|
|
46
|
+
if (box.width <= 0 || box.height <= 0)
|
|
47
|
+
continue;
|
|
48
|
+
const type = el.getAttribute('type')?.toLowerCase();
|
|
49
|
+
const tag = el.tagName.toLowerCase();
|
|
50
|
+
const kind = tag === 'div'
|
|
51
|
+
? 'signature'
|
|
52
|
+
: tag === 'select'
|
|
53
|
+
? 'choice'
|
|
54
|
+
: tag === 'textarea'
|
|
55
|
+
? 'multiline'
|
|
56
|
+
: type === 'checkbox'
|
|
57
|
+
? 'checkbox'
|
|
58
|
+
: type === 'radio'
|
|
59
|
+
? 'radio'
|
|
60
|
+
: 'text';
|
|
61
|
+
const rect = kind === 'checkbox' || kind === 'radio' ? square(box) : box;
|
|
62
|
+
const path = somPath(el, layer);
|
|
63
|
+
// A SOM path can recur: nine do on DAF 1288, six of them within a page, and
|
|
64
|
+
// every field on its second page starts `Page1.`. The page and an occurrence
|
|
65
|
+
// count make the AcroForm name unique without losing what the form called it.
|
|
66
|
+
const base = `p${pageNumber}.${path}`;
|
|
67
|
+
const x = +(rect.left - origin.left).toFixed(2);
|
|
68
|
+
const y = +(rect.top - origin.top).toFixed(2);
|
|
69
|
+
const w = +rect.width.toFixed(2);
|
|
70
|
+
const h = +rect.height.toFixed(2);
|
|
71
|
+
const tooltip = tooltipOf(el);
|
|
72
|
+
const options = tag === 'select'
|
|
73
|
+
? [...el.options]
|
|
74
|
+
.map((o) => o.textContent?.trim() ?? '')
|
|
75
|
+
.filter(Boolean)
|
|
76
|
+
: [];
|
|
77
|
+
// A template can hold a field twice at one place, the second drawn over the
|
|
78
|
+
// first. Controls that read the same in everything but their order are one field.
|
|
79
|
+
const place = JSON.stringify([base, kind, x, y, w, h, tooltip, options]);
|
|
80
|
+
if (places.has(place))
|
|
81
|
+
continue;
|
|
82
|
+
places.add(place);
|
|
83
|
+
const count = (seen.get(base) ?? 0) + 1;
|
|
84
|
+
seen.set(base, count);
|
|
85
|
+
const excl = kind === 'radio' ? exclusion(el, layer) : null;
|
|
86
|
+
widgets.push({
|
|
87
|
+
name: count === 1 ? base : `${base}#${count}`,
|
|
88
|
+
kind,
|
|
89
|
+
page: pageNumber - 1,
|
|
90
|
+
x,
|
|
91
|
+
y,
|
|
92
|
+
w,
|
|
93
|
+
h,
|
|
94
|
+
group: excl ? `p${pageNumber}.${somPath(excl, layer)}` : undefined,
|
|
95
|
+
// The leaf of the SOM path is what the form calls this option. It becomes
|
|
96
|
+
// the widget's on-state, which is the only thing telling one member of an
|
|
97
|
+
// exclusion group from another.
|
|
98
|
+
on: excl ? path.slice(path.lastIndexOf('.') + 1) : undefined,
|
|
99
|
+
tooltip,
|
|
100
|
+
options
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return widgets;
|
|
104
|
+
}
|
|
105
|
+
/** `Page1.form1.page1.TypeRequest.type_of_request`, root-first. */
|
|
106
|
+
function somPath(el, layer) {
|
|
107
|
+
const parts = [];
|
|
108
|
+
for (let node = el; node && node !== layer; node = node.parentElement) {
|
|
109
|
+
const name = node.getAttribute('xfaname');
|
|
110
|
+
if (name)
|
|
111
|
+
parts.unshift(name);
|
|
112
|
+
}
|
|
113
|
+
return parts.length ? parts.join('.') : el.id || el.getAttribute('name') || 'field';
|
|
114
|
+
}
|
|
115
|
+
function exclusion(el, layer) {
|
|
116
|
+
const found = el.closest('.xfaExclgroup[xfaname]');
|
|
117
|
+
return found && layer.contains(found) ? found : null;
|
|
118
|
+
}
|
|
119
|
+
/** The hover text a filler reads. */
|
|
120
|
+
function tooltipOf(el) {
|
|
121
|
+
const field = el.closest('.xfaField');
|
|
122
|
+
const direct = el.getAttribute('aria-label') || el.getAttribute('title') || field?.getAttribute('title');
|
|
123
|
+
if (direct?.trim())
|
|
124
|
+
return direct.trim();
|
|
125
|
+
// pdf.js rewrites the caption's class for a check-button UI, which is every
|
|
126
|
+
// radio and every checkbox, so the one selector misses both.
|
|
127
|
+
return (field?.querySelector('.xfaCaption, .xfaCaptionForCheckButton')?.textContent?.trim() || undefined);
|
|
128
|
+
}
|
package/dist/xfa.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A dynamic Adobe LiveCycle form as an ordinary static AcroForm, or the input
|
|
3
|
+
* unchanged when it is not one.
|
|
4
|
+
*
|
|
5
|
+
* In a dynamic form the fields exist only as XML inside `/AcroForm /XFA`:
|
|
6
|
+
* `/Fields` is empty, and the one page in the file is a placeholder, most often
|
|
7
|
+
* a "Please wait" notice. Converting one takes an XFA layout engine, and pdf.js
|
|
8
|
+
* has one. This runs it, reads the geometry the browser solved off the DOM, and
|
|
9
|
+
* writes both the printed page and the fields back out as a PDF the later
|
|
10
|
+
* stages already take.
|
|
11
|
+
*
|
|
12
|
+
* It is a normalization: bytes in, bytes out, and every later stage reads the
|
|
13
|
+
* result as it reads a static form.
|
|
14
|
+
*/
|
|
15
|
+
export declare function xfaToAcroForm(bytes: Uint8Array, signal?: AbortSignal): Promise<Uint8Array>;
|