Scheduled Maintenance
-
February 10, 2026, from 8:00 AM to 9:00 AM PST. Account-related operations might be temporarily unavailable.

Field Detection Showcase Demo Code Sample

Requirements
View Demo

Effortlessly detect and classify Form Fields. Transform your flat PDF into an interactive form with editable fields using AI-powered recognition.

This sample code includes Server SDK processing in JavaScript, with UI provided by WebViewer. If a viewer is not needed, or you want to work with a different language or framework for the Server SDK, please check out our Server SDK Smart Data Extraction Sample Code.

This demo allows you to:

  • Upload a flat PDF and automatically detect form elements such as:
    • Text Fields
    • Check Boxes
    • Radio Buttons
    • List Boxes
    • Combo Boxes
    • Buttons
    • Digital Signatures
  • Convert detected elements into editable form fields.
  • Edit and customize the newly created fields, then save your updated document.

Implementation steps
To add PDF form field detection capability with WebViewer:

Step 1: Follow get-started in JavaScript for Server SDK
Step 2: Follow get-started in your preferred web stack for WebViewer
Step 3: Download Data Extraction Module
Step 4: Add the ES6 JavaScript sample code provided in this guide

Once you generate your license key, it will automatically be included in your sample code below.

License Key

1// ES6 Compliant Syntax
2// Copilot: GitHub Copilot, Version: 1.0, Model: GPT-4, Version: 2024-06, Date: 2025-08-31
3// File: client/index.js
4
5
6// **Important**
7// You must get a license key from Apryse to run the WebViewer Server SDK.
8// A trial key can be obtained from:
9// https://docs.apryse.com/core/guides/get-started/trial-key
10const licenseKey = 'YOUR_LICENSE_KEY';
11const viewerElement = document.getElementById('viewer');
12const initialDoc = 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/form_fields_flattened.pdf';
13let jsonData = null;
14const formFieldMap = [
15 { type: 'formTextField', title: 'Text Field', color: { R: 51, G: 101, B: 251, A: 1 } },
16 { type: 'formCheckBox', title: 'Check Box', color: { R: 0, G: 128, B: 0, A: 1 } },
17 { type: 'formRadioButton', title: 'Radio Button', color: { R: 128, G: 0, B: 128, A: 1 } },
18 { type: 'formListBox', title: 'List Box', color: { R: 144, G: 238, B: 144, A: 1 } },
19 { type: 'formComboBox', title: 'Combo Box', color: { R: 255, G: 192, B: 203, A: 1 } },
20 { type: 'formButton', title: 'Button', color: { R: 255, G: 165, B: 0, A: 1 } },
21 { type: 'formDigitalSignature', title: 'Digital Signature', color: { R: 3, G: 219, B: 252, A: 1 } },
22 { type: 'formTextArea', title: 'Text Area', color: { R: 255, G: 0, B: 0, A: 1 } },
23];
24
25// The list of registered panels in the main webviewer
26let viewerPanels = null;
27
28// The tab panel, representing the webviewer left panel
29const tabPanel = {
30 handle: null,
31 dataElement: 'tabPanel'
32};
33
34// The custom form sub-panel to be registered
35const formPanel = {
36 handle: null,
37 dataElement: 'formPanel',
38 render: null,
39};
40
41// Customize the main webviewer left panel after the load completion
42const customizeUI = (instance) => {
43 const { UI } = instance;
44
45 // Close the tab panel (if it's open) for refreshment.
46 UI.closeElements([tabPanel.dataElement]);
47
48 // Get the list of registered panels in the main webviewer
49 viewerPanels = UI.getPanels();
50
51 // Find the Tab Panel to modify. The form sub-panel will be added to this Tab panel.
52 tabPanel.handle = viewerPanels.find((panel) => panel.dataElement === tabPanel.dataElement);
53
54 // Register the custom form sub-panel
55 RegisterFormPanel(instance);
56
57 // Add the new custom form sub-panel to list of sub-panels under the Tab Panel
58 formPanel.handle = { render: formPanel.dataElement };
59 tabPanel.handle.panelsList = [formPanel.handle, ...tabPanel.handle.panelsList];
60
61 UI.openElements([tabPanel.dataElement]);
62 UI.setPanelWidth(tabPanel.dataElement, 400);
63};
64
65// Register the custom form sub-panel
66const RegisterFormPanel = (instance) => {
67 formPanel.render = createFormPanelElements(instance);
68 instance.UI.addPanel({
69 dataElement: formPanel.dataElement,
70 location: 'left',
71 icon: '<svg width="18px" height="18px" viewBox="0 0 24 24" id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg"><defs><style>.cls-1{fill:#080808;}</style></defs><title>form</title><path class="cls-1" d="M21,.5H3a2,2,0,0,0-2,2V22a2,2,0,0,0,2,2H21a2,2,0,0,0,2-2V2.5A2,2,0,0,0,21,.5Zm0,2v2H3v-2ZM3,22V6.5H21V22Z"/><path class="cls-1" d="M12.5,4H20a.5.5,0,0,0,0-1H12.5a.5.5,0,0,0,0,1Z"/><path class="cls-1" d="M4.5,4a.43.43,0,0,0,.19,0,.35.35,0,0,0,.16-.11A.47.47,0,0,0,5,3.5a.43.43,0,0,0,0-.19.36.36,0,0,0-.11-.16.5.5,0,0,0-.7,0A.35.35,0,0,0,4,3.31.43.43,0,0,0,4,3.5a.51.51,0,0,0,.5.5Z"/><path class="cls-1" d="M5.65,3.85A.36.36,0,0,0,5.81,4,.44.44,0,0,0,6,4a.47.47,0,0,0,.35-.15.36.36,0,0,0,.11-.16.6.6,0,0,0,0-.19.51.51,0,0,0-.15-.35A.49.49,0,0,0,5.81,3a.36.36,0,0,0-.16.11.47.47,0,0,0-.15.35.4.4,0,0,0,0,.19A.35.35,0,0,0,5.65,3.85Z"/><path class="cls-1" d="M8,8H4.5a1,1,0,0,0,0,2H8A1,1,0,0,0,8,8Z"/><path class="cls-1" d="M8,11.67H4.5a1,1,0,0,0,0,2H8a1,1,0,0,0,0-2Z"/><path class="cls-1" d="M8,15.33H4.5a1,1,0,0,0,0,2H8a1,1,0,0,0,0-2Z"/><path class="cls-1" d="M8,19H4.5a1,1,0,0,0,0,2H8a1,1,0,0,0,0-2Z"/><path class="cls-1" d="M14,8H10.5a1,1,0,0,0,0,2H14a1,1,0,0,0,0-2Z"/><path class="cls-1" d="M14,11.67H10.5a1,1,0,0,0,0,2H14a1,1,0,0,0,0-2Z"/><path class="cls-1" d="M14,15.33H10.5a1,1,0,0,0,0,2H14a1,1,0,0,0,0-2Z"/><path class="cls-1" d="M14,19H10.5a1,1,0,0,0,0,2H14a1,1,0,0,0,0-2Z"/><path class="cls-1" d="M19.5,8h-3a1,1,0,0,0,0,2h3a1,1,0,0,0,0-2Z"/><path class="cls-1" d="M19.5,11.67h-3a1,1,0,0,0,0,2h3a1,1,0,0,0,0-2Z"/><path class="cls-1" d="M19.5,15.33h-3a1,1,0,0,0,0,2h3a1,1,0,0,0,0-2Z"/><path class="cls-1" d="M19.5,19h-3a1,1,0,0,0,0,2h3a1,1,0,0,0,0-2Z"/></svg>',
72 title: 'Form',
73 render: () => formPanel.render,
74 });
75};
76
77// Create the form panel elements.
78const createFormPanelElements = (instance) => {
79 let panelDiv = document.createElement('div');
80 panelDiv.id = 'form';
81 let paragraph = document.createTextNode('A demo of Apryse SDK Smart Data Extraction and the form field detection and classification. Take a flat PDF and automatically find form fields using AI.');
82 panelDiv.appendChild(paragraph);
83
84 const span = document.createElement("span");
85 span.style.color = 'orange';
86 span.appendChild(document.createTextNode('NOTE: Only the first page will be processed.'));
87 panelDiv.appendChild(document.createElement('p'));
88 panelDiv.appendChild(span);
89
90 let dividerDiv = document.createElement('div');
91 dividerDiv.style.borderTop = '1px solid #ccc';
92 dividerDiv.style.margin = '10px 0';
93 panelDiv.appendChild(dividerDiv);
94
95 // Detect form fields button
96 let detectFieldsButton = document.createElement('button');
97 detectFieldsButton.textContent = 'Detect Form Fields';
98 detectFieldsButton.id = 'detectFieldsButton';
99 detectFieldsButton.onclick = async () => {
100
101 detectFieldsButton.style.cursor = "not-allowed"; // Changes cursor for the button itself
102 formPanel.render.style.cursor = "not-allowed"; // Changes cursor for the button itself
103
104 enableButton(detectFieldsButton, false);
105 await detectFormFields(instance); // Detect form fields
106
107 detectFieldsButton.style.cursor = "default";
108 formPanel.render.style.cursor = "default";
109 }
110 enableButton(detectFieldsButton, false); // Disabled until a document is loaded
111
112 panelDiv.appendChild(detectFieldsButton);
113 panelDiv.appendChild(document.createElement('p'));
114
115 return panelDiv;
116};
117
118// Open JSON data in a dialog box with zoom in/out and close buttons
119const openJsonDataDialog = (jsonText) => {
120 let fontSize = 14;
121
122 // Create overlay
123 const overlay = document.createElement("div");
124 overlay.className = "modal-overlay";
125 overlay.onclick = (e) => {
126 if (e.target === overlay) {
127 document.body.removeChild(overlay);
128 }
129 };
130
131 // Modal box
132 const modal = document.createElement("div");
133 modal.className = "modal-box";
134
135 // Controls
136 const controls = document.createElement("div");
137 controls.className = "modal-controls";
138
139 const zoomInBtn = document.createElement("button");
140 zoomInBtn.textContent = "+";
141 zoomInBtn.onclick = () => {
142 fontSize += 2;
143 content.style.fontSize = fontSize + "px";
144 };
145
146 const zoomOutBtn = document.createElement("button");
147 zoomOutBtn.textContent = "-";
148 zoomOutBtn.onclick = () => {
149 fontSize = Math.max(10, fontSize - 2);
150 content.style.fontSize = fontSize + "px";
151 };
152
153 const closeBtn = document.createElement("button");
154 closeBtn.textContent = "Close";
155 closeBtn.className = "modal-close";
156 closeBtn.onclick = () => {
157 document.body.removeChild(overlay);
158 };
159
160 controls.appendChild(zoomInBtn);
161 controls.appendChild(zoomOutBtn);
162 controls.appendChild(closeBtn);
163
164 // Content
165 const content = document.createElement("pre");
166 content.className = "modal-content";
167 content.style.fontSize = fontSize + "px";
168 content.innerHTML = jsonText;
169
170 modal.appendChild(controls);
171 modal.appendChild(content);
172 overlay.appendChild(modal);
173 document.body.appendChild(overlay);
174}
175
176// Draw annotation rectangles on the PDF when forms fields are detected
177const drawAnnotations = (instance) => {
178 const formFieldData = JSON.parse(jsonData);
179 const { annotationManager, Annotations } = instance.Core;
180
181 formFieldData.pages[0].formElements.forEach((element) => {
182 const color = formFieldMap.find(field => field.type === element.type).color;
183 const annot = new Annotations.RectangleAnnotation({
184 PageNumber: formFieldData.pages[0].properties.pageNumber,
185 X: element.rect[0],
186 Y: element.rect[1],
187 Width: element.rect[2] - element.rect[0],
188 Height: element.rect[3] - element.rect[1],
189 StrokeColor: new Annotations.Color(color.R, color.G, color.B, color.A),
190 StrokeThickness: 2,
191 });
192 annotationManager.addAnnotation(annot);
193 annotationManager.redrawAnnotation(annot);
194 });
195};
196
197const downloadPdf = async (instance) => {
198 const options = {
199 flags: instance.Core.SaveOptions.LINEARIZED,
200 downloadType: 'pdf'
201 };
202
203 instance.UI.downloadPdf(options);
204};
205
206// Build form fields and draw annotation rectangles
207// on the PDF when the "Build Form" button is clicked
208const buildForm = (instance) => {
209 const { annotationManager, Annotations } = instance.Core;
210 const { WidgetFlags } = Annotations;
211 let flags = null;
212 let field = null;
213 let widgetAnnot = null;
214 const font = new Annotations.Font({ name: 'Helvetica', size: 12 });
215
216 annotationManager.deleteAnnotations(annotationManager.getAnnotationsList());
217
218 const formFieldData = JSON.parse(jsonData);
219
220 formFieldData.pages[0].formElements.forEach((formField, index) => {
221
222 // Sets generic flags for the widget.
223 flags = new WidgetFlags();
224 flags.set(WidgetFlags.REQUIRED, true);
225
226 switch (formField.type) {
227 // Text field
228 case 'formTextField':
229 flags.set(WidgetFlags.MULTILINE, true);
230
231 // Creates a text form field.
232 field = new Annotations.Forms.Field(`TextField ${index}`, {
233 type: 'Tx',
234 defaultValue: 'Default Value',
235 flags,
236 });
237
238 // Creates a text widget annotation.
239 widgetAnnot = new Annotations.TextWidgetAnnotation(field);
240 break;
241
242 // Radio button
243 case 'formRadioButton':
244 flags.set(WidgetFlags.RADIO, true);
245 flags.set(WidgetFlags.NO_TOGGLE_TO_OFF, true);
246
247 // Creates a radio button form field.
248 field = new Annotations.Forms.Field(`RadioField ${index}`, {
249 type: 'Btn',
250 value: 'Off',
251 flags,
252 font: font,
253 });
254
255 // Create a radio widget button.
256 widgetAnnot = new Annotations.RadioButtonWidgetAnnotation(field, {
257 appearance: 'Off',
258 appearances: {
259 Off: {},
260 First: {},
261 },
262 backgroundColor: new Color(255, 0, 0),
263 });
264 break;
265
266 // List box
267 case 'formComboBox':
268 // Sets flags for the combobox widget.
269 flags.set(WidgetFlags.COMBO, true);
270
271 // Define the available options.
272 const comboOptions = [
273 { value: '1', displayValue: 'one' },
274 { value: '2', displayValue: 'two' },
275 { value: '3', displayValue: 'three' }
276 ];
277
278 // Creates a combobox form field.
279 field = new Annotations.Forms.Field(`ComboBoxField ${index}`, {
280 flags,
281 font: font,
282 type: 'Ch',
283 options: comboOptions,
284 value: comboOptions[0].value,
285 });
286
287 // Creates a combobox widget annotation.
288 widgetAnnot = new Annotations.ChoiceWidgetAnnotation(field);
289 break;
290
291 // Check box
292 case 'formCheckBox':
293 // Creates a checkbox form field.
294 field = new Annotations.Forms.Field(`CheckBoxField ${index}`, {
295 type: 'Btn',
296 value: 'Off',
297 flags,
298 });
299
300 // Creates a checkbox widget annotation.
301 widgetAnnot = new Annotations.CheckButtonWidgetAnnotation(field, {
302 appearance: 'Off',
303 appearances: {
304 Off: {},
305 Yes: {},
306 },
307 captions: {
308 Normal: '' // Uses the check symbol for selected caption.
309 }
310 });
311 break;
312
313 // Digital signature
314 case 'formDigitalSignature':
315 // Creates a signature form field.
316 field = new Annotations.Forms.Field(`SignatureField ${index}`, {
317 type: 'Sig',
318 flags,
319 });
320
321 // Creates a signature widget annotation.
322 widgetAnnot = new Annotations.SignatureWidgetAnnotation(field, {
323 appearance: '_DEFAULT',
324 appearances: {
325 _DEFAULT: {
326 Normal: {
327 // Optionally can pass image data to appearance.
328 // data: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjEuMWMqnEsAAAANSURBVBhXY/j//z8DAAj8Av6IXwbgAAAAAElFTkSuQmCC',
329 offset: {
330 x: formField.rect[0],
331 y: formField.rect[1],
332 },
333 },
334 },
335 },
336 });
337 break;
338
339 // Button
340 case 'formButton':
341 flags.set(WidgetFlags.PUSH_BUTTON, true);
342 // Creates a button form field.
343 field = new Annotations.Forms.Field(`ButtonField ${index}`, {
344 type: 'Btn',
345 tooltipName: 'this is a button',
346 flags,
347 });
348
349 // Creates a checkbox widget annotation.
350 widgetAnnot = new Annotations.PushButtonWidgetAnnotation(field, {
351 border: new Annotations.Border({
352 color: new Annotations.Color(255, 0, 0),
353 width: 1,
354 style: 'solid',
355 }),
356 });
357 break;
358
359 default:
360 break;
361 }
362
363 // set the widget properties
364 widgetAnnot.PageNumber = formFieldData.pages[0].properties.pageNumber;
365 widgetAnnot.X = formField.rect[0];
366 widgetAnnot.Y = formField.rect[1];
367 widgetAnnot.Width = formField.rect[2] - formField.rect[0];
368 widgetAnnot.Height = formField.rect[3] - formField.rect[1];
369
370 // Add form field to field manager and widget annotation to annotation manager.
371 annotationManager.getFieldManager().addField(field);
372 annotationManager.addAnnotation(widgetAnnot);
373 annotationManager.drawAnnotationsFromList([widgetAnnot]);
374 });
375}
376
377// Detect form fields in the PDF document
378// This function will send GET message to the server,
379// to receive the detected form fields as JSON object.
380const detectFormFields = async (instance) => {
381
382 const doc = instance.Core.documentViewer.getDocument();
383
384 // Make a GET request to get the extracted JSON data of form fields of the current PDF.
385 return new Promise(function (resolve) {
386 fetch(`http://localhost:5050/server/handler.js?filename=${doc.filename}`, {
387 method: 'GET'
388 }).then(function (response) {
389 if (response.status === 200) {
390 response.text().then(function (json) {
391 jsonData = json;
392 let jsonText = JSON.stringify(jsonData, null, 2);
393 jsonText = jsonText.replace(/\\r\\n/g, '\n');
394 jsonText = jsonText.replace(/\\"/g, '"');
395
396 // Build form button
397 let buildFormButton = document.createElement('button');
398 buildFormButton.textContent = 'Build Form';
399 buildFormButton.id = 'buildFormButton';
400 buildFormButton.onclick = () => {
401 enableButton(buildFormButton, false);
402 buildForm(instance); // Build form
403 }
404 enableButton(buildFormButton, false);
405
406 formPanel.render.appendChild(buildFormButton);
407 formPanel.render.appendChild(document.createElement('p'));
408
409 // Download button
410 let downloadButton = document.createElement('button');
411 downloadButton.textContent = 'Download PDF';
412 downloadButton.id = 'downloadButton';
413 downloadButton.style.backgroundColor = 'blue';
414 downloadButton.style.color = 'white';
415 downloadButton.onclick = () => downloadPdf(instance); // Download PDF
416
417 formPanel.render.appendChild(downloadButton);
418
419 // Display the detected form fields
420 let colorsDiv = document.createElement('div');
421 colorsDiv.id = 'json';
422 colorsDiv.className = "listContainer";
423 const colorsTitle = document.createElement("h3");
424 colorsTitle.textContent = "Color Legend";
425 colorsDiv.appendChild(colorsTitle);
426 colorsDiv.appendChild(document.createElement('p'));
427
428 // Create list items
429 formFieldMap.forEach(field => {
430 const color = new instance.Core.Annotations.Color(field.color.R, field.color.G, field.color.B, field.color.A);
431 const listItem = document.createElement("div");
432 listItem.className = "listItem";
433 listItem.textContent = field.text;
434 listItem.style.setProperty("--bullet-color", color);
435 listItem.style.setProperty("color", color);
436 listItem.style.setProperty("font-weight", "bold");
437
438 // Set bullet color using ::before
439 listItem.style.setProperty("--bullet-color", color);
440 listItem.style.setProperty("position", "relative");
441 listItem.style.setProperty("padding-left", "20px");
442 listItem.style.setProperty("margin", "8px 0");
443
444 // Add custom bullet using inline style
445 listItem.style.setProperty("list-style", "none");
446 listItem.style.setProperty("display", "block");
447 listItem.style.setProperty("line-height", "1.5");
448 listItem.style.setProperty("font-size", "14px");
449
450 // Create bullet manually
451 const bullet = document.createElement("span");
452 bullet.style.width = "10px";
453 bullet.style.height = "10px";
454 bullet.style.borderRadius = "50%";
455 bullet.style.backgroundColor = color;
456 bullet.style.display = "inline-block";
457 bullet.style.marginRight = "10px";
458 bullet.style.verticalAlign = "middle";
459
460 // Insert bullet before text
461 listItem.textContent = ""; // Clear text
462 listItem.appendChild(bullet);
463 listItem.appendChild(document.createTextNode(field.title));
464
465 colorsDiv.appendChild(listItem);
466 });
467
468 formPanel.render.appendChild(colorsDiv);
469
470 // Display the detected form fields
471 let jsonDiv = document.createElement('div');
472 jsonDiv.id = 'json';
473 const jsonTitle = document.createElement("h3");
474 jsonTitle.textContent = "JSON Data";
475 jsonDiv.appendChild(jsonTitle);
476 jsonDiv.appendChild(document.createElement('p'));
477
478 const scrollBox = document.createElement("div");
479 scrollBox.style.width = "350px";
480 scrollBox.style.height = "350px";
481 scrollBox.style.border = "2px solid #444";
482 scrollBox.style.overflow = "scroll"; // Enables both vertical and horizontal scroll
483 scrollBox.style.whiteSpace = "nowrap"; // Prevents wrapping for horizontal scroll
484 scrollBox.style.padding = "10px";
485 scrollBox.style.fontFamily = "monospace";
486 scrollBox.style.backgroundColor = "black";
487 scrollBox.style.color = "white";
488
489 // Format and insert JSON data
490 const jsonContent = document.createElement("pre");
491 jsonContent.textContent = jsonText;
492 scrollBox.appendChild(jsonContent);
493 jsonDiv.appendChild(scrollBox);
494
495 // Open JSON data dialog button
496 let jsonDataDialogButton = document.createElement('button');
497 jsonDataDialogButton.textContent = 'Open in Dialog';
498 jsonDataDialogButton.id = 'jsonDataDialogButton';
499 jsonDataDialogButton.style.backgroundColor = 'blue';
500 jsonDataDialogButton.style.color = 'white';
501 jsonDataDialogButton.onclick = () => openJsonDataDialog(jsonText);
502 jsonDiv.appendChild(jsonDataDialogButton);
503 jsonDiv.appendChild(document.createElement('p'));
504
505 formPanel.render.appendChild(jsonDiv);
506 drawAnnotations(instance);
507 resolve();
508 enableButton(formPanel.render.querySelector('#buildFormButton'), true);
509 })
510 }
511 else if (response.status === 500) {
512 jsonData = null;
513 resolve();
514 }
515 });
516 });
517};
518
519// Enable or disable a button based on the state
520const enableButton = (button, state) => {
521 button.disabled = !state;
522 button.style.backgroundColor = (state) ? 'blue' : 'gray';
523 button.style.color = (state) ? 'white' : 'darkgray';
524};
525
526WebViewer({
527 path: '/lib',
528 initialDoc: initialDoc,
529 enableFilePicker: true, // Enable file picker to open files. In WebViewer -> menu icon -> Open File
530 enableMeasurement: true,
531 loadAsPDF: true,
532 licenseKey: licenseKey,
533}, viewerElement).then(instance => {
534
535 // Once the PDF document is loaded, send it to the server.
536 // The sent PDF document will be processed by the server,
537 // by extracting form fields JSON data when the user clicks the "Detect Form Fields" button.
538 instance.Core.documentViewer.addEventListener('documentLoaded', async () => {
539
540 // Customize the main webviewer left panel after the load completion
541 customizeUI(instance);
542
543 // Reset JSON data
544 jsonData = null;
545
546 // Preparation of the PDF blob to be sent to the server
547 const doc = instance.Core.documentViewer.getDocument();
548 const xfdfString = await instance.Core.annotationManager.exportAnnotations(); // obtaining annotations in the loaded document
549 const data = await doc.getFileData({ xfdfString });
550 const arr = new Uint8Array(data);
551 const blob = new Blob([arr], { type: 'application/pdf' });
552 const formData = new FormData();
553 formData.append(doc.filename, blob, doc.filename);
554
555 // Send the PDF blob to the server for processing
556 new Promise(function (resolve, reject) {
557 console.log('🚀 Sending PDF to server for initial processing...');
558
559 fetch(`http://localhost:5050/server/handler.js?filename=${doc.filename}`, {
560 method: 'POST',
561 body: formData,
562 }).then(function (response) {
563 console.log(`📡 Server response status: ${response.status}`);
564
565 if (response.status === 200) {
566 console.log('✅ PDF successfully sent to server');
567
568 // enable Detect Form Fields button
569 const detectButton = formPanel.render.querySelector('#detectFieldsButton');
570 if (detectButton) {
571 console.log('🔓 Enabling Detect Form Fields button');
572 enableButton(detectButton, true);
573 } else {
574 console.warn('⚠️ Could not find detectFieldsButton in DOM');
575 }
576 resolve();
577 } else {
578 console.error(`❌ Server responded with status: ${response.status}`);
579 reject(new Error(`Server error: ${response.status}`));
580 }
581 }).catch(function (error) {
582 console.error('❌ Failed to connect to server:', error);
583 console.error('📍 Attempted URL: http://localhost:5050/server/handler.js');
584 console.error('🔍 This likely means the field-detection server is not running on port 5050');
585 reject(error);
586 });
587 }).catch(function (error) {
588 console.error('❌ Error in PDF upload promise:', error);
589 });
590 });
591
592 console.log('✅ WebViewer loaded successfully.');
593}).catch((error) => {
594 console.error('❌ Failed to initialize WebViewer:', error);
595});
596

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales