Annotations (comments ex) causing JS error: won't render

Which product are you using?
PDFJS Express

PDF.js Express Version

UI version ‘8.4.1’
Core version ‘8.4.0’

Detailed description of issue
I can load a document into the viewer and am able to update fields and save them. On save, I export and save the annotations. When I reload the page, the fields are set properly. If I annotate the page with highlight and underlines, save, and reload, the annotations appear correctly.

However, if I add a comment annotation, save and reload, the annotations fail with the following JS error:

VM265:1 Uncaught (in promise) SyntaxError: Unexpected token c in JSON at position 18
at JSON.parse ()
at h.qO (webviewer-core.min.js:2575:263)
at ja (webviewer-core.min.js:2578:224)
at h.rO (webviewer-core.min.js:2578:462)
at webviewer-core.min.js:2570:364
at webviewer-core.min.js:2570:269
at webviewer-core.min.js:2076:313
at x (webviewer-core.min.js:1845:405)
at ha (webviewer-core.min.js:1845:426)
at ka (webviewer-core.min.js:2069:480)

Expected behaviour
All annotations will be displayed properly

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

Link to document

Code snippet

$(document).ready(function() {

    WebViewer({
        path: '/Scripts/pdf-js-express/lib',
        licenseKey: 'xxx', // redacted
        initialDoc: '/FormDocument/CreateFormTemplate/8',
        disableFlattenedAnnotations: true
    },
        document.getElementById('viewer')).then(async instance => {
            const docViewer = instance.docViewer;
            const annotManager = instance.annotManager;
            // 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;


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

                    
                    var annotText = 'XXX'; // redacted full XFDF XML embedded string
                    await annotManager.importAnnotations(annotText);
                    
                });
    });
});

async function saveFile(isSubmit, docViewer, annotManager) {
    const xfdf = await annotManager.exportAnnotations();
    const fileData = await docViewer.getDocument().getFileData({});
    const blob = new Blob([fileData], { type: 'application/pdf' });

    var data = new FormData();

    data.append('xfdf', xfdf);
    data.append('file', blob);
    //data.append('license', 'XXXX'); // redacted

    // 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", 8);
    data.append("pdfAnnotations", xfdf);
    data.append("fileStream", mergedBlob);

    $.ajax({
        url: '/FormDocument/UploadFile',
        method: "POST",
        data: data,
        cache: false,
        contentType: false,
        processData: false,
        success : async data => {
            if (data.success) {
                if (isSubmit) {
                    return submitFormDocument();
                }
            }
        }
    });
}

async function submitFormDocument() {

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

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

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

                    window.location.href=url;
                }
            } else {
                if (data.message != "") {
                    window.alert(data.message);
                } else {
                    window.alert("Something went wrong 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:

Hello chris1,

It seems you have your license key commented out

    const xfdf = await annotManager.exportAnnotations();
    const fileData = await docViewer.getDocument().getFileData({});
    const blob = new Blob([fileData], { type: 'application/pdf' });

    var data = new FormData();

    data.append('xfdf', xfdf);
    data.append('file', blob);
    //data.append('license', 'XXXX'); // redacted

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

However the error that you noted is a JSON error, where is it failing in your code specifically? You have multiple fetch requests

Best regards,
Tyler Gordon
Web Development Support Engineer

Thanks for the reply Tyler. Sorry, I missed the response

The license part was removed just for the posted code. It’s all uncommented and I have both sets of keys set properly in the actual code.

The multiple fetch’s are only on save file. It’s when we reload the file and call await annotManager.importAnnotations(annotText) AFTER I’ve added the comments and saved and reloaded, so it’s the document.ready section that runs. Like I said above it’s only when I add that specific type (comments for ex) to a document when it starts to error. All other saves and reloads work fine.

Hello chris1,

Thank you for the clarification,
I would suggest starting with updating the code to v8.x namespacing, here is a migration guide: Migrating to PDF.js Express 8.0 | Documentation

  1. Does this happen with only note annotations or comments?
  2. Does this also happen with FreeText annotations?
  3. Does this happen with the rectangle annotations?

Best regards,
Tyler Gordon
Web Development Support Engineer
PDFTron

Thanks Tyler.

I’m not sure what you mean by version 8.x namespacing as this is what I’m using when pulling the Core.instance. Everything else is just variables after that so please let me know what I’m missing.

This will error out with notes and free text. It looks like rectangles do not break it.

Thanks,
Chris

Hello chris1,

With the 8.x namespacing:

const docViewer = instance.docViewer;
const annotManager = instance.annotManager;

turns into:

const docViewer = instance.Core.documentViewer;
const annotManager = instance.Core.annotationManager;

The JSON error is possibly due to the encoding of our rich text, can you provide the XFDF you are testing with?

Best regards,
Tyler Gordon
Web Development Support Engineer
PDFTron

<?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>Mortality Inc</value></field><field name="CHDate"><value>1/1/2022</value></field><field name="CHAcceptServices"><value>Yes</value></field><field name="CHRefuseParticipation"><value>Yes</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>Off</value></field><field name="CHEmergencyPlan"><value>Off</value></field><field name="CHNo"><value>Off</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>Off</value></field><field name="CHYesWill"><value>Off</value></field><field name="CHDoNotResuscitate"><value>Off</value></field><field name="CHYesResuscitate"><value>Off</value></field><field name="CHNoPowerOfAttorney"><value>Off</value></field><field name="CHYesPowerOfAttorney"><value>Off</value></field><field name="CHRatePerAssessmentVisit"><value></value></field><field name="CHRatePerTreatmentVisit"><value></value></field><field name="CHPaymentSources"><value></value></field><field name="CHFinancialResponsibilities"><value>Off</value></field><field name="CHNoInsurance"><value>Off</value></field><field name="CHInsuranceChanges"><value>Off</value></field><field name="CHParentConsent"><value>Off</value></field><field name="CHLegalGuardianConsent"><value>Off</value></field><field name="CHPCG"><value>Yes</value></field><field name="CHParent"><value>Off</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>Off</value></field><field name="CHPCGInitials"><value></value></field><field name="CHPCGInitials2"><value></value></field><field name="CHTodaysDate"><value></value></field><field name="CHPrintedNameofGuardia/Parent"><value>Yes</value></field><field name="CHRelationshipToChild"><value></value></field><field name="CHTherapistName"><value></value></field><field name="CHTherapistSignature"><value></value></field></fields><annots><ink page="0" rect="185.330,571.386,385.330,609.954" color="#000000" flags="print" name="30603e79-bb9e-2626-1558-b63dfb27f9bc" title="Guest" subject="Signature" date="D:20220728123341-05'00'" creationdate="D:20220728123341-05'00'"><trn-custom-data bytes="{&quot;trn-annot-maintain-aspect-ratio&quot;:&quot;true&quot;}"/><inklist><gesture>186.32634254004287,580.580455290705;188.21813217303568,582.4722449236979;191.37111489469035,584.9946311010216;200.1994665153235,593.1923861773238;209.02781813595658,599.4983516206331;224.1621351998991,607.6961066969354;229.2069075545466,607.6961066969354;243.71062807415817,603.9125274309497;252.5389796947913,600.1289481649641;272.0874725690503,591.3005965443309;281.54642073401436,586.2558241896834;302.98670324126624,574.9050863917266;313.0762479505612,572.3827002144028;332.62474082482026,572.3827002144028;344.6060751671081,574.9050863917266;366.0463576743599,581.8416483793668;376.135902383655,588.7782103670072;383.07246437129527,602.6513343422879;383.7030609156262,606.4349136082734;384.3336574599571,608.3267032412663;384.3336574599571,608.9572997855972</gesture></inklist></ink><text page="0" rect="398,548.330,429,579.330" color="#FFCD45" flags="print,nozoom,norotate" name="f86e4bc1-9ce7-0bf2-e9ac-fb2ed65dce8c" title="Guest" subject="Comment" date="D:20220728123918-05'00'" creationdate="D:20220728123912-05'00'" icon="Comment" statemodel="Review"><trn-custom-data bytes="{&quot;trn-mention&quot;:&quot;{\&quot;contents\&quot;:\&quot;I like bananas\&quot;,\&quot;ids\&quot;:[]}&quot;}"/><contents>I like bananas</contents></text></annots><pages><defmtx matrix="1,0,0,-1,0,792" /></pages></xfdf>

Hello chris1,

It seems like there is a weird character added to the XFDF in the custom-data:

\&quot;

in <trn-custom-data bytes="{&quot;trn-mention&quot;:&quot;{\&quot;contents\&quot;:\&quot;I like bananas\&quot;,\&quot;ids\&quot;:[]}&quot;}"/>
which is causing the JSON parsing to fail. Was this added by your code?

Best regards,
Tyler Gordon
Web Development Support Engineer
PDFTron

Thanks for the quick reply. We just take whatever is created by the exportAnnotations method. We aren’t editing the XFDF strings at all so I don’t believe it’s our code.

As a workaround, I’ve added a comment and attempted to replace in the string it to ", ", ', and ' character in the XFDF. None of them seem to work properly.

Hello chris1,

I think a workaround by doing:

xfdfString.replace('\&quot;','');

would work.
However we have just released v8.7.0 of PDFJS Express, can you test with that version to see if the error has subsided?

Best regards,
Tyler Gordon
Web Development Support Engineer
PDFTron

I changed how we were embedded the xml in the page and upgraded to 8.7. This appears to be working now! Hooray and thank you!

Hi chris1, can you elaborate how you solve the issue. I am having similar issues.

You can embed the xml directly on the page if you do this:


<script id="annotationsXml" type="text/xmldata">
    @Html.Raw(Model.Annotations)
</script>

and then retrieve it like this:


 var xfdf = $("#annotationsXml")[0].innerHTML.trim();