Annotations moving on downloaded pdf when opened in Abode Acrobat and BLUEBEAM

PDF.js Express Version
8.1.0

Detailed description of issue
Annotations are moving on downloaded pdf when opened in Abode Acrobat and BLUEBEAM. I have added readonly true property to the annotation list still the problem persists for viewing the pdf on these viewers. Is there any way to fix this using pdfjs functions? Any insights on this would be really helpful. Thanks!

Expected behavior
I have provided the code snippet that I have used for making the annotations read-only.
Does your issue happen with every document, or just one?
Every time

Link to document
{Provide a link to the document in question if possible}

Code snippet
const list = annotManager.getAnnotationsList();
list.forEach((item) => {
item.ReadOnly = true;
item.Locked = true;
item.LockedContents = true;
});

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:

Hey there,

Can you share the final file that you are opening in Adobe with us? This is tough to debug without the actual file.

Thanks!

Hey Logan!
I am sharing with you the pdf for reference. Pls let me know if you need anything else?

Thank youtest.pdf (3.0 MB)

Hey there!

I cannot reproduce your issue. I am using your code on my document, merging the updated annotations and then opening the resulting document in Acrobat. The annotations I added are read only.

Can you please share with me how you are merging the annotations back into the document? Are you using the merge rest API?

Thank you,
Logan

Hello Logan, thanks for the response!

For more information, here are the steps we are using to process PDF files:

  1. Users add annotations to files on the frontend client.

  2. Our backend receives the PDF file URL and the associated XFDF with the user made annotations.

  3. We use the API - ‘requests.post(url=‘https://api.pdfjs.express/xfdf/merge’,
    data=data, files=files)’ to merge the annotations to the source file.

  4. Once merged, we expect the annotations to be flattened and read only. But that does not happen. The annotations are still clickable and exist in a different layer (/Annots) in the PDF file. If they were properly flattened, they should not exist in a different layer.

  5. Our next step is that we need to resize each page of the PDF file and contract the text to make space for a header and a footer for our users. Doing so, the annotations move around in the resized PDF as they still follow their original coordinates. These original coordinates are now invalid as the PDF contents are shifted after we resize the pages.

  6. To address the above, we go through each /Rect component of each annotation in the /Annots layer of the PDF and adjust the coordinates of annotations to ensure they move to the intended location.

Ideally, we want to flatten the annotations in the PDF such that there should be no need to manually move the coordinates of each annotation ourselves. Is that possible in PDF.js? Do we have to send additional arguments in the merge REST API?

Additionally, once our users download the PDF files, they shouldn’t be able to click on the annotations and edit or delete them. This happens consistently now on Windows Adobe PDF viewers. Could you please advise us on how to combat this?

I’m happy to provide any other information as you need and also can jump on a call to show the issue if you’d like.

Thanks for the support.

Best,
Pratyush

Hey there,

Just to be clear, read only and flattening are two different things. Our rest API is working as expected, as it is setting annotations to read only.

We unfortunately do not support PDF flattening at this moment. If you need this functionality, our sister product PDFTron WebViewer supports flattening (and it can do it client side!).

Thank you,
Logan

Hey Logan,

Got it. If the annotations are read-only, we shouldn’t be able to modify or delete them when opening the file in a PDF viewer (like Adobe). Is that correct?

We are able to download the file and then modify annotations in Adobe on Windows. It looks like the annotations are not editable on Linux Adobe, so it is working as expected there. Could you please confirm?

I’ve attached a file here for your reference. The annotations in this file are editable in Adobe and therefore the read-only property is not working as expected for us. You can also see the list of all annotations in the Comments pane that pops up on the right.

DoubleTables_original.pdf (765.0 KB)

Thanks,
Pratyush

Logan, related question - could you please point me to the right resources on how to transition from PDF.js Express to PDFTron webviewer?

Thanks!

Hey there,

Unfortunately I cannot reproduce your issue in Adobe or any other reader (with my own code - I can reproduce the issue with the document you sent me.). I believe this is an issue with the way you are using the API.

How are you loading the document? Please note that getAnnotationsList will not return any annotations that already exist in the opened document (PDF.js Express cannot parse annotations out of existing documents, unless you use the extract rest API)

Here is an upgrade guide - Migrate from PDF.js Express to PDFTron webviewer | PDF.js Express SDK (the APIs are the exact same)

Thanks!
Logan

Hey Logan ,
Thanks for the feedback. For loading the document we are using this.wvInstance.docViewer.loadDocument(S3 URL ); which loads our datasheet.

For updating the readonly status we use
instance.docViewer.on(“annotationsLoaded”, () => {
const list = annotManager.getAnnotationsList();

    list.forEach((item) => {
      item.ReadOnly = true;
      item.Locked = true;
      item.LockedContents = true;
    });
  });

I hope we are doing this the right way. Pls, let me know if there is any other way we can handle the readonly tag.

Thanks!

Hey!

I do not believe that will work. Keep in mind that PDF.js Express cannot load or edit annotations that exist in the document already - you need to use the extract endpoint to support that use case.

Once you extract the XFDF, you can import it (see the example of the extract page) into actual editable annotations in PDF.js Express.

After you have imported the XFDF, now you can edit the annotations and set them to read only. The code you have above is fine, but I would take it out of the “annotationsLoaded” event and run that code either in annotationChanged, or just run it once before the user downloads the document.

Once you set ReadOnly to true on the annotations, you need to merge those annotations back into the document using the merge API. The resulting document will have ReadOnly annotations as expected.

Here is some simple code to help you get started (or you can check out this guide)

import WebViewer from '@pdftron/pdfjs-exress'
import ExpressUtils from '@pdftron/pdfjs-express-utils'

WebViewer({options}).then(instance => {
 
   const utils = new ExpressUtils();

   // When a document is loaded, extract the XFDF and import it
   instance.Core.documentViewer.addEventListener('documentLoaded', async () => {
        // Get the loaded document's data
       const data = await documentViewer.getDocument().getFileData();
       utils.setFile(data);

       // Use the API to extract the XFDF
       const { xfdf } = await utils.extract();
       // Loading the resulting XFDF into the viewer
       await annotationManager.importAnnotations(xfdf);
   })

   // Add some code to the "download" button in your app
   downloadButton.onclick = () => {
       const list = annotManager.getAnnotationsList();
       list.forEach((item) => {
         item.ReadOnly = true;
         item.Locked = true;
         item.LockedContents = true;
      });


       // Get the annotations and the documents data
       const xfdf = await annotationManager.exportAnnotations({});
       const fileData = await documentViewer.getDocument().getFileData({});

       // Set the annotations and document into the Utility SDK, then merge them together
       const resp = await utils
         .setFile(fileData)
         .setXFDF(xfdf)
         .merge();

       // Get the resulting blob from the merge operation
       const mergedBlob = await resp.getBlob();

      // Download the Blob here
    }
})

Thanks,
Logan