Viewer displays PDF correctly / Merged PDF file missing form fields

Which product are you using?
PDF JS Express

PDF.js Express Version

UI version ‘8.7.0’
Core version ‘8.7.0’
webviewer.min.js ‘8.7.0’
WebViewer Server false

Detailed description of issue
We are able to load an empty pdf, retrieve annotations from our db, merge them using the api, and view it in the Viewer. However, when we attempt to save the PDF from the merge URL, several but not all of the text boxes are missing and all checkboxes are missing. This previews perfectly in the Viewer, so it’s just the combined PDF that isn’t working.

Expected behaviour
Merged PDF should look exactly like the previewed PDF.

Does your issue happen with every document, or just one?
Yes

Link to document
Preview

Resulting PDF

Code snippet
$(document).ready(function() {

    var config = {
        path: '/Scripts/pdf-js-express/lib',
        licenseKey: 'VALID KEY',
        initialDoc: '/FormDocument/CreateFormTemplate/15',
        disableFlattenedAnnotations: true
    };

    var viewerElement = document.getElementById('viewer');

    WebViewer(config, viewerElement)
        .then(async instance => {
            const { documentViewer, annotationManager } = instance.Core;
            // call methods from instance, docViewer and annotManager as needed

            // you can also access major namespaces from the instance as follows:
            // const Tools = instance.Tools;
            // const Annotations = instance.Annotations;

            documentViewer.addEventListener('documentLoaded', async () => {
                $("#btnSave").click(function() {
                    saveFile(false, documentViewer, annotationManager);
                });
                $("#submitDocumentBtn").click(function() {
                    saveFile(true, documentViewer, annotationManager);
                });

                documentViewer.zoomTo(1.5, 0, 0);
            })
                .addEventListener('annotationsLoaded', async () => {
                    
                        var xfdf = '<?xml version="1.0" encoding="UTF-8" ?><xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve"><fields><field name="CHClient"><value>Katherine Asher</value></field><field name="CHGender"><value></value></field><field name="CHDateOfBirth"><value>11/02/1999</value></field><field name="CHInsurance"><value>Horizon NJ</value></field><field name="CHDate"><value>7/28/22</value></field><field name="CHAcceptServices"><value>Yes</value></field><field name="CHRefuseParticipation"><value>Off</value></field><field name="CHOccupationalTherapy"><value>Yes</value></field><field name="CHPhysicalTherapy"><value>Yes</value></field><field name="CHSpeechTherapy"><value>Yes</value></field><field name="CHHippa--AdmissionStandards"><value>Yes</value></field><field name="CHEmergencyPlan"><value>Yes</value></field><field name="CHNo"><value>Yes</value></field><field name="CHYes"><value>Off</value></field><field name="CHSafetyChangesDescription"><value></value></field><field name="CHSafteyAgreement"><value>Off</value></field><field name="CHNoWill"><value>Yes</value></field><field name="CHYesWill"><value>Off</value></field><field name="CHDoNotResuscitate"><value>Yes</value></field><field name="CHYesResuscitate"><value>Off</value></field><field name="CHNoPowerOfAttorney"><value>Yes</value></field><field name="CHYesPowerOfAttorney"><value>Off</value></field><field name="CHRatePerAssessmentVisit"><value>275.00</value></field><field name="CHRatePerTreatmentVisit"><value>195.00</value></field><field name="CHPaymentSources"><value></value></field><field name="CHFinancialResponsibilities"><value>Yes</value></field><field name="CHNoInsurance"><value>Yes</value></field><field name="CHInsuranceChanges"><value>Yes</value></field><field name="CHParentConsent"><value>Yes</value></field><field name="CHLegalGuardianConsent"><value>Off</value></field><field name="CHPCG"><value>LISA NELSON</value></field><field name="CHParent"><value>Yes</value></field><field name="CHLegalGuardian"><value>Off</value></field><field name="CHLastDayofService"><value></value></field><field name="CHAPartirDeFecha"><value></value></field><field name="CHPhysicalTherapyCancelAuthorizationDate"><value></value></field><field name="CHOccupationalTherapyCancelDate"><value></value></field><field name="CHSpeechCancelAuthDate"><value></value></field><field name="CHReasonForProviderChange"><value></value></field><field name="CHNoTreatmentInLast6Months"><value>Yes</value></field><field name="CHPCGInitials"><value>LMN</value></field><field name="CHPCGInitials2"><value>LMN</value></field><field name="CHTodaysDate"><value>7/28/22</value></field><field name="CHPrintedNameofGuardia/Parent"><value>Lisa Nelson</value></field><field name="CHRelationshipToChild"><value>caregiver</value></field><field name="CHTherapistName"><value>Trish G. </value></field><field name="CHTherapistSignature"><value></value></field></fields><pages><defmtx matrix="1,0,0,-1,0,792" /></pages></xfdf>';
                        await annotationManager.importAnnotations(xfdf);
                    
                });
        });
});

async function saveFile(isSubmit, docViewer, annotManager) {

    try {
        window.showLoadingDiv();

        const xfdf = await annotManager.exportAnnotations({links: false, widgets: false});
        const templateFileData = await fetch("/FormDocument/CreateFormTemplate/15").then(resp => resp.blob());

        var data = new FormData();

        data.append('xfdf', xfdf);
        data.append('file', templateFileData);
        data.append('license', 'VALID KEY');

        // Process the file
        const response = await fetch('https://api.pdfjs.express/xfdf/merge',
            {
                method: 'post',
                body: data
            }).then(resp => resp.json());

        const { url, key, id } = response;

        // Download the file
        var mergedBlob = await fetch(url,
            {
                headers: {
                    Authorization: key
                }
            }).then(resp => resp.blob());

        var data = new FormData();
        data.append("id", 15);
        data.append("pdfAnnotations", xfdf);
        data.append("fileStream", mergedBlob);

        return $.ajax({
            url: '/FormDocument/UploadFile',
            method: "POST",
            data: data,
            cache: false,
            contentType: false,
            processData: false,
            success: async data => {
                if (data.success) {
                    if (isSubmit) {
                        return submitFormDocument();
                    }
                }
            },
            error: function() {
                bootbox.alert("Sorry, an error has occurred and your changes may not have saved properly. Please try again.")
            }
        });
    } catch (ex) {
        logError(ex);
        bootbox.alert("Sorry, an error has occurred and your changes may not have saved properly. Please try again.");
    }
    finally {
        window.hideLoadingDiv();
    }
}

async function submitFormDocument() {

    return $.ajax({
        url: "/FormDocument/Submit",
        type: "POST",
        data: {
            formDocumentId: "15",
            timeZone: getClientSideTimeZone(),
            signatureDate: getClientSideDateString()
        },
        success: function (data) {
            if (data.success) {
                var hasOpener = !isNullOrWhiteSpace(window.opener);

                if (hasOpener) {
                    window.close();
                }
                else {
                    var isPatient = true;
                    var isClinician = false;
                    var id = 3700;
                    var url;

                    if (isPatient) {
                        url = "/Patient/Chart/3700?patientPage=OtherDocuments";
                    } else if (isClinician) {
                        url = "/Clinician/Edit/3700?page=Documents";
                    } else {
                        url = "/";
                    }

                    window.location.href=url;
                }
            } else {
                if (data.message != "") {
                    alert(data.message);
                } else {
                    alert("Something went wrong please try again");
                }
            }
        },
        error: function() {
            bootbox.alert("Sorry, an error has occurred and your changes may not have submitted properly. Please try again.")
        }
    });
}

Hello, I’m Ron, an automated tech support bot :robot:

While you wait for one of our customer support representatives to get back to you, please check out some of these documentation pages:

Guides:APIs:Forums:

This is still an issue in the latest 8.7.

Hello chris1,

Can you provide the document shown in the photos? this would help reproducing the issue reported.

Best regards,
Tyler Gordon
Web Development Support Engineer
PDFTron

Sure. Here you go!

T2K_EngAdmit_CubHub.pdf (402.7 KB)

Hello chris1,

Thank you for the file, reviewing your code I see you have widgets: false in the exportAnnotations, this will not include any widget annotations (such as text boxes and check boxes) in the export.

Best regards,
Tyler Gordon
Web Development Support Engineer
PDFTron

Hey Tyler,

These aren’t new textboxes that have been added by the user via the PDFJS. They are in the original document. If it were the widget exporting option, I would think that all of the boxes would fail. As you can see in the screenshot though, some of the boxes do work and some do not.

Thanks!
Chris

Hello chris1,

I see instead of getting the file directly from the loaded viewer, you get:
const templateFileData = await fetch("/FormDocument/CreateFormTemplate/15").then(resp => resp.blob())
What is the result of this fetch? The requirements are it is the file to be used for the merge, in Blob format.

For example in the documentation:

    const fileData = await documentViewer.getDocument().getFileData({});
    const blob = new Blob([fileData], {type: 'application/pdf'});
    const data = new FormData();
    data.append('file', blob);

It gets the document and converts it to blob, then adds it to the form data.

Best regards,
Tyler Gordon
Web Development Support Engineer

Hello again,

Yes that response is an empty template file that is sent from our servers and as you can see is then turned into a blob. In fact, we copy pasted that same sample you posted and changed out our data. The fact that it is partially working indicates that it’s a valid file in blob format since we do receive something back from calling merge. It’s just missing some of the fields which would seem to indicate an issue in merging on the api side, unless I’m missing something.

Thanks,
Chris

Hello Chris1,

Ive attached my code I used to this reply (its a slightly modified version, as I dont have access to your Template endpoint)
Can you try with this code and see if it works for you?
index.js.zip (1.7 KB)

Best regards,
Tyler Gordon
Web Development Support Engineer
PDFTron

Okay that worked by saving the widgets and links and getting the file data directly from the viewer! We’re so close! I can refresh and it loads the annotations properly as well as rendering a PDF. Hooray!

Unfortunately now I’m getting overlaps on the text both in the PDF viewer on refresh and on the rendered PDF. On load, I’m getting the empty template and applying XFDF to it. It’s possibly a default value or watermark on the field which I’d like to turn off if possible? Or it’s something else?

So close!

Before hitting backspace:

After hitting backspace one time on the insurance field:

Hi Chris1,

It looks like its because of the default value, you can just modify the defaultValue of the form field on load if that works for you:
widgetAnntotation.defaultValue = "";

Best regards,
Tyler Gordon
Web Development Support Engineer
PDFTron


Share how you are using PDF.js Express in your organization you could win a $500 Amazon gift card. All participants will receive 6 months of PDF.js Express+ for free. Learn more here

Ok. We don’t set that value anywhere and it’s only happening after the merge api call. Is there a way to configure it globally to never set default values? Otherwise we’ll have to do some kind of loop through all annotations and remove it post merge?

On save of the annotations on the server, I’ve tried removing the default values so that when the page reloads it uses the new default-less options applied to an empty template file. The issue is still happening. Also a second issue is happening in the previewer that may be related:

  • remove (backspace) a character it still shows the previous value (as seen in the “insurance” box above)
  • click to another box the character finally clears
  • type a new character it doesn’t show as bold
  • click away, the character is suddenly in bold and exhibits the behavior above

Hello chris1,

We have identified that this duplicate text issue is because there are 2 annotations of the same ID on the document. As noted on this page:

Duplicate prevention
To prevent duplicates, remove all the annotations expected to be present in the PDF document from XFDF before using FDFMerge API.

You can remove all annotations on the document before merging which will remove the duplicates.
You can do that with: instance.Core.annotationManager.deleteAnnotations(instance.Core.annotationManager.getAnnotationsList());

Best regards,
Tyler Gordon
Web Development Support Engineer
PDFTron


Share how you are using PDF.js Express in your organization you could win a $500 Amazon gift card. All participants will receive 6 months of PDF.js Express+ for free. Learn more here

We’re still facing this issue and its severe enough that this product is becoming unusable. The delete method above removes all annotations and causes the entire document to empty out when rendered. Is there a way to set up a screen share to review this? We have what would seem to be a simple and straight forward use case and unfortunately we’re coming up on two months with this being unresolved.

Hello chris1,

We have two options to make this work:

  1. Do not export the widgets, instead merging the new field values values. This can be done with: exportAnnotations({ widgets: false })
  2. Use the set XFDF API, this will overwrite any annotations on the document, thus not creating duplicate annotations

Best regards,
Tyler Gordon
Web Development Support Engineer
PDFTron


Share how you are using PDF.js Express in your organization you could win a $500 Amazon gift card. All participants will receive 6 months of PDF.js Express+ for free. Learn more here