0
# XFA API
1
2
XML Forms Architecture (XFA) support for dynamic PDF forms and interactive content. XFA forms provide rich, dynamic form experiences with complex layouts and data binding.
3
4
## Capabilities
5
6
### XFA Layer Rendering
7
8
Renders XFA forms as interactive HTML elements, enabling complex form interactions and dynamic content updates.
9
10
```javascript { .api }
11
class XfaLayer {
12
/**
13
* Render XFA form content
14
* @param parameters - XFA layer parameters
15
* @returns XFA layer result
16
*/
17
static render(parameters: XfaLayerParameters): XfaLayerResult;
18
19
/**
20
* Update existing XFA layer
21
* @param parameters - XFA layer parameters
22
*/
23
static update(parameters: XfaLayerParameters): void;
24
25
/**
26
* Set up XFA layer container
27
* @param div - Container element
28
* @param parameters - Setup parameters
29
*/
30
static setupStorage(div: HTMLElement, parameters: any): void;
31
}
32
```
33
34
### XFA Layer Parameters
35
36
Configuration for rendering XFA forms.
37
38
```javascript { .api }
39
interface XfaLayerParameters {
40
/** Page viewport for positioning */
41
viewport: PageViewport;
42
/** Container element for XFA content */
43
div: HTMLElement;
44
/** XFA HTML content */
45
xfaHtml: Element;
46
/** Annotation storage for form data */
47
annotationStorage?: AnnotationStorage;
48
/** Link service for navigation */
49
linkService: IPDFLinkService;
50
/** XFA HTML factory for element creation */
51
xfaHtmlFactory: any;
52
/** Optional document base URL */
53
docBaseUrl?: string;
54
/** Page index */
55
pageIndex?: number;
56
}
57
58
interface XfaLayerResult {
59
/** Root XFA element */
60
element: HTMLElement;
61
/** Child elements */
62
children: HTMLElement[];
63
/** Intent of the rendering */
64
intent: string;
65
}
66
```
67
68
**Usage Examples:**
69
70
```javascript
71
import { XfaLayer } from "pdfjs-dist";
72
73
// Check if page has XFA content
74
const xfaHtml = await page.getXfa();
75
76
if (xfaHtml) {
77
// Create XFA layer container
78
const xfaDiv = document.createElement("div");
79
xfaDiv.className = "xfaLayer";
80
document.body.appendChild(xfaDiv);
81
82
// Render XFA form
83
const xfaResult = XfaLayer.render({
84
viewport: viewport,
85
div: xfaDiv,
86
xfaHtml: xfaHtml,
87
annotationStorage: annotationStorage,
88
linkService: linkService,
89
xfaHtmlFactory: new XfaHtmlFactory()
90
});
91
92
console.log("XFA form rendered with", xfaResult.children.length, "elements");
93
}
94
```
95
96
### XFA HTML Factory
97
98
Factory for creating XFA HTML elements with proper event handling and styling.
99
100
```javascript { .api }
101
interface XfaHtmlFactory {
102
/**
103
* Create XFA element
104
* @param data - Element data
105
* @returns HTML element
106
*/
107
createElement(data: XfaElementData): HTMLElement;
108
109
/**
110
* Create XFA text node
111
* @param text - Text content
112
* @returns Text node
113
*/
114
createTextNode(text: string): Text;
115
116
/**
117
* Create XFA document fragment
118
* @returns Document fragment
119
*/
120
createDocumentFragment(): DocumentFragment;
121
}
122
123
interface XfaElementData {
124
/** Element tag name */
125
name: string;
126
/** Element attributes */
127
attributes?: { [key: string]: string };
128
/** Child elements */
129
children?: XfaElementData[];
130
/** Text content */
131
value?: string;
132
/** Element namespace */
133
namespace?: string;
134
}
135
```
136
137
### XFA Form Controls
138
139
Interfaces for different XFA form control types.
140
141
```javascript { .api }
142
interface XfaTextField {
143
/** Field name */
144
name: string;
145
/** Field value */
146
value: string;
147
/** Whether field is required */
148
required: boolean;
149
/** Whether field is read-only */
150
readOnly: boolean;
151
/** Maximum length */
152
maxLength?: number;
153
/** Input pattern */
154
pattern?: string;
155
/** Placeholder text */
156
placeholder?: string;
157
158
/**
159
* Set field value
160
* @param value - New value
161
*/
162
setValue(value: string): void;
163
164
/**
165
* Validate field value
166
* @returns Validation result
167
*/
168
validate(): boolean;
169
170
/**
171
* Focus the field
172
*/
173
focus(): void;
174
}
175
176
interface XfaCheckBox {
177
/** Field name */
178
name: string;
179
/** Whether checked */
180
checked: boolean;
181
/** Whether field is required */
182
required: boolean;
183
/** Whether field is read-only */
184
readOnly: boolean;
185
186
/**
187
* Set checked state
188
* @param checked - Whether to check
189
*/
190
setChecked(checked: boolean): void;
191
192
/**
193
* Toggle checked state
194
*/
195
toggle(): void;
196
}
197
198
interface XfaDropdownList {
199
/** Field name */
200
name: string;
201
/** Selected value */
202
value: string;
203
/** Available options */
204
options: { value: string; text: string }[];
205
/** Whether field is required */
206
required: boolean;
207
/** Whether field is read-only */
208
readOnly: boolean;
209
210
/**
211
* Set selected value
212
* @param value - Value to select
213
*/
214
setValue(value: string): void;
215
216
/**
217
* Add option
218
* @param option - Option to add
219
*/
220
addOption(option: { value: string; text: string }): void;
221
222
/**
223
* Remove option
224
* @param value - Value of option to remove
225
*/
226
removeOption(value: string): void;
227
}
228
```
229
230
### XFA Data Binding
231
232
Interfaces for XFA data binding and dynamic content.
233
234
```javascript { .api }
235
interface XfaDataConnection {
236
/** Connection name */
237
name: string;
238
/** Data source URL */
239
dataSource: string;
240
/** Connection type */
241
type: "xml" | "json" | "database";
242
243
/**
244
* Load data from source
245
* @returns Promise resolving to data
246
*/
247
loadData(): Promise<any>;
248
249
/**
250
* Save data to source
251
* @param data - Data to save
252
* @returns Promise resolving when complete
253
*/
254
saveData(data: any): Promise<void>;
255
256
/**
257
* Bind data to form
258
* @param formData - Form data object
259
*/
260
bindData(formData: any): void;
261
}
262
263
interface XfaCalculation {
264
/** Expression to evaluate */
265
expression: string;
266
/** Target field name */
267
target: string;
268
/** Dependencies */
269
dependencies: string[];
270
271
/**
272
* Execute calculation
273
* @param context - Calculation context
274
* @returns Calculated value
275
*/
276
execute(context: any): any;
277
278
/**
279
* Check if calculation is valid
280
* @returns Whether valid
281
*/
282
isValid(): boolean;
283
}
284
```
285
286
### XFA Events
287
288
Event handling for XFA form interactions.
289
290
```javascript { .api }
291
interface XfaEvent {
292
/** Event type */
293
type: "initialize" | "calculate" | "validate" | "full" | "save" | "print" | "change" | "exit" | "click";
294
/** Source element */
295
source: HTMLElement;
296
/** Event data */
297
data: any;
298
/** Whether event can be cancelled */
299
cancelable: boolean;
300
301
/**
302
* Cancel event
303
*/
304
cancel(): void;
305
306
/**
307
* Get field value
308
* @param fieldName - Name of field
309
* @returns Field value
310
*/
311
getFieldValue(fieldName: string): any;
312
313
/**
314
* Set field value
315
* @param fieldName - Name of field
316
* @param value - Value to set
317
*/
318
setFieldValue(fieldName: string, value: any): void;
319
}
320
321
interface XfaEventHandler {
322
/**
323
* Handle XFA event
324
* @param event - XFA event
325
*/
326
handleEvent(event: XfaEvent): void;
327
328
/**
329
* Register event listener
330
* @param eventType - Type of event
331
* @param handler - Event handler function
332
*/
333
addEventListener(eventType: string, handler: (event: XfaEvent) => void): void;
334
335
/**
336
* Remove event listener
337
* @param eventType - Type of event
338
* @param handler - Event handler function
339
*/
340
removeEventListener(eventType: string, handler: (event: XfaEvent) => void): void;
341
}
342
```
343
344
### XFA Utilities
345
346
Helper functions for XFA form operations.
347
348
```javascript { .api }
349
/**
350
* Get XFA page viewport with proper scaling
351
* @param xfaPage - XFA page element
352
* @param viewport - Base viewport
353
* @returns XFA-adjusted viewport
354
*/
355
function getXfaPageViewport(xfaPage: Element, viewport: PageViewport): PageViewport;
356
357
/**
358
* Extract XFA form data
359
* @param xfaElement - Root XFA element
360
* @returns Form data object
361
*/
362
function extractXfaData(xfaElement: HTMLElement): { [key: string]: any };
363
364
/**
365
* Populate XFA form with data
366
* @param xfaElement - Root XFA element
367
* @param data - Data to populate
368
*/
369
function populateXfaForm(xfaElement: HTMLElement, data: { [key: string]: any }): void;
370
371
/**
372
* Validate XFA form
373
* @param xfaElement - Root XFA element
374
* @returns Validation results
375
*/
376
function validateXfaForm(xfaElement: HTMLElement): { isValid: boolean; errors: string[] };
377
```
378
379
**Usage Examples:**
380
381
```javascript
382
// Complete XFA form handling
383
class XfaFormHandler {
384
constructor(container, annotationStorage, linkService) {
385
this.container = container;
386
this.annotationStorage = annotationStorage;
387
this.linkService = linkService;
388
this.xfaElements = new Map();
389
this.eventHandlers = new Map();
390
}
391
392
async renderXfaPage(page, viewport) {
393
const xfaHtml = await page.getXfa();
394
395
if (!xfaHtml) {
396
return null; // No XFA content
397
}
398
399
const xfaDiv = document.createElement("div");
400
xfaDiv.className = "xfaLayer";
401
this.container.appendChild(xfaDiv);
402
403
const xfaResult = XfaLayer.render({
404
viewport: viewport,
405
div: xfaDiv,
406
xfaHtml: xfaHtml,
407
annotationStorage: this.annotationStorage,
408
linkService: this.linkService,
409
xfaHtmlFactory: new XfaHtmlFactory()
410
});
411
412
this.setupXfaEventHandlers(xfaResult.element);
413
this.xfaElements.set(page.pageNumber, xfaResult.element);
414
415
return xfaResult;
416
}
417
418
setupXfaEventHandlers(xfaElement) {
419
// Handle form field changes
420
xfaElement.addEventListener('change', (event) => {
421
const field = event.target;
422
if (field.name) {
423
this.annotationStorage.setValue(field.name, this.getFieldValue(field));
424
this.triggerCalculations();
425
}
426
});
427
428
// Handle validations
429
xfaElement.addEventListener('blur', (event) => {
430
const field = event.target;
431
if (field.name && field.required && !field.value) {
432
this.showValidationError(field, "This field is required");
433
}
434
});
435
}
436
437
getFieldValue(field) {
438
switch (field.type) {
439
case 'checkbox':
440
return field.checked;
441
case 'radio':
442
return field.checked ? field.value : null;
443
case 'select-one':
444
return field.value;
445
case 'select-multiple':
446
return Array.from(field.selectedOptions).map(opt => opt.value);
447
default:
448
return field.value;
449
}
450
}
451
452
triggerCalculations() {
453
// Find all calculated fields and update them
454
this.xfaElements.forEach(element => {
455
const calculatedFields = element.querySelectorAll('[data-calculate]');
456
calculatedFields.forEach(field => {
457
const expression = field.dataset.calculate;
458
try {
459
const result = this.evaluateExpression(expression);
460
field.value = result;
461
this.annotationStorage.setValue(field.name, result);
462
} catch (error) {
463
console.warn('Calculation error:', error);
464
}
465
});
466
});
467
}
468
469
evaluateExpression(expression) {
470
// Simple expression evaluator (implement based on XFA spec)
471
// This would need a full XFA expression parser in practice
472
return expression;
473
}
474
475
showValidationError(field, message) {
476
// Show validation error UI
477
field.classList.add('error');
478
const errorDiv = document.createElement('div');
479
errorDiv.className = 'validation-error';
480
errorDiv.textContent = message;
481
field.parentNode.appendChild(errorDiv);
482
483
setTimeout(() => {
484
field.classList.remove('error');
485
errorDiv.remove();
486
}, 3000);
487
}
488
489
getFormData() {
490
const formData = {};
491
this.xfaElements.forEach(element => {
492
const fields = element.querySelectorAll('input, select, textarea');
493
fields.forEach(field => {
494
if (field.name) {
495
formData[field.name] = this.getFieldValue(field);
496
}
497
});
498
});
499
return formData;
500
}
501
502
setFormData(data) {
503
this.xfaElements.forEach(element => {
504
Object.entries(data).forEach(([name, value]) => {
505
const field = element.querySelector(`[name="${name}"]`);
506
if (field) {
507
this.setFieldValue(field, value);
508
}
509
});
510
});
511
}
512
513
setFieldValue(field, value) {
514
switch (field.type) {
515
case 'checkbox':
516
case 'radio':
517
field.checked = Boolean(value);
518
break;
519
case 'select-multiple':
520
Array.from(field.options).forEach(option => {
521
option.selected = Array.isArray(value) && value.includes(option.value);
522
});
523
break;
524
default:
525
field.value = value || '';
526
}
527
528
// Trigger change event
529
field.dispatchEvent(new Event('change', { bubbles: true }));
530
}
531
}
532
```
533
534
### CSS Styling
535
536
Required CSS for XFA form display:
537
538
```css
539
.xfaLayer {
540
position: absolute;
541
left: 0;
542
top: 0;
543
transform-origin: 0% 0%;
544
line-height: 1.2;
545
}
546
547
.xfaLayer * {
548
color: inherit;
549
font: inherit;
550
font-style: inherit;
551
font-weight: inherit;
552
font-kerning: inherit;
553
letter-spacing: -0.01px;
554
}
555
556
.xfaLayer input,
557
.xfaLayer textarea,
558
.xfaLayer select {
559
background-color: rgba(0, 54, 255, 0.13);
560
border: 1px solid transparent;
561
border-radius: 2px;
562
padding: 2px;
563
}
564
565
.xfaLayer input:focus,
566
.xfaLayer textarea:focus,
567
.xfaLayer select:focus {
568
background-color: rgba(0, 54, 255, 0.13);
569
border: 1px solid rgba(0, 54, 255, 1);
570
outline: none;
571
}
572
573
.xfaLayer .validation-error {
574
color: red;
575
font-size: 12px;
576
margin-top: 2px;
577
}
578
579
.xfaLayer input.error {
580
border-color: red;
581
background-color: rgba(255, 0, 0, 0.1);
582
}
583
```