Download PDF with annotations

PDF.js Express Version

Detailed description of issue
I seem to be missing some pieces to make this work. I’ve added a download button and I want to let the user download their copy with annotations. Pretty much exactly what your main demo does.

My questions are:

  • How do I get my button to call the onSave?
  • Do you have an example of what the save(mergedFileBlob) function should look like to just download without making the user click another button?
  • Do you have a working demo that has this functionality?

Below is my code

Expected behaviour
I want the user to download the PDF with their notes

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

Link to document
It’s not really document specific

Code snippet

 openDoc() {
   // clearing previous loaded doc
    this.viewer.nativeElement.innerHTML = '';
    if (this.wvInstance != null) {
      try {
        this.wvInstance.dispose();
      }
      catch { }
    }
    if (this.selectedDoc != "") {
      WebViewer({
        path: '/lib',
        initialDoc: this.selectedDoc,
        disabledElements: ['leftPanel', 'leftPanelButton', 'signatureToolGroupButton', 'miscToolGroupButton']
      }, this.viewer.nativeElement).then(instance => {
        this.wvInstance = instance;

        const { docViewer, annotManager } = instance;

        // a callback function to some "download" button
        const onSave = async () => {
          const xfdf = await annotManager.exportAnnotations({ links: false, widgets: false });
          //const fileData = await docViewer.getDocument().getFileData({});
          //const blob = new Blob([fileData], { type: 'application/pdf' });

          const data = new FormData();
          data.append('xfdf', xfdf);
          // using the URL
          data.append('file', this.selectedDoc);
          //data.append('file', blob);
          //data.append('license', my_license_key);

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

          console.log(response);

          const { url, key, id } = response;
          // Download the file
          const mergedFileBlob = await fetch(url, {
            headers: {
              Authorization: key
            }
          }).then(resp => resp.blob())

          // Do something with blob...
          // save(mergedFileBlob)
        }

      });
    }
  }

  downloadDoc() {

    // how do I call the onSave from above?

  }

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:

Hey there,

These questions are not really related to PDF.js Express and are instead related to just general JS knowledge, however I’ll try and help you out a bit.

To call the onSave function you just need to add an onclick listener to your button. There are a million different ways to do this and it depends on what framework you are using, etc.

For the save function I usually just use this npm library.

Thanks!
Logan

That wasn’t really the issue I was having, but I was able to piece together something. More just confused why you wouldn’t have a working demo of any kind. For anyone else having issues making this work, this is basically what I did in Angular, but you could make it work for anything.

  downloadDoc() {
    var fileName = this.documents.find(d => d.path == this.selectedDoc).documentName;
    if (fileName == null) {
      fileName = "doc.pdf";
    }
    const { docViewer, annotManager } = this.wvInstance;

    const onSave = async () => {
      const xfdf = await annotManager.exportAnnotations({ links: false, widgets: false });
      console.log(annotManager);
      console.log(xfdf.length);
      // using url instead of sending blob
      //const fileData = await docViewer.getDocument().getFileData({});
      //const blob = new Blob([fileData], { type: 'application/pdf' });


      // check to see if there are annotations. Just download the file if not.
      if (xfdf.length < 200) {
        console.log("no annotations. just download");
        this.wvInstance.downloadPdf({ filename: fileName});
        return;
      }
      const data = new FormData();
      data.append('xfdf', xfdf);
      data.append('file', this.selectedDoc);
      //data.append('file', blob);
      //data.append('license', my_license_key);

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

      console.log(response);

      const { url, key, id } = response;
      // Download the file
      const mergedFileBlob = await fetch(url, {
        headers: {
          Authorization: key
        }
      }).then(resp => resp.blob())

      // this won't work on IE, but should on others
      let downloadurl = window.URL.createObjectURL(mergedFileBlob);
      var link = document.createElement('a');
      link.href = downloadurl;
      link.download = fileName;
      link.click();
      setTimeout(function () {
        // For Firefox it is necessary to delay revoking the ObjectURL
        window.URL.revokeObjectURL(downloadurl);
      }, 100);
    }
    // call async from above
    onSave();

  }

Hey there!

For future reference, please wrap code snippets in triple backticks so that they get formatted properly (this forum supports markdown syntax).

I’m unsure what you’re confused about here. Our demo at https://pdfjs.express/demo uses this exact flow, just like you mentioned in your original post. The code samples we provide for this flow are also straight forward and give you everything you need. The reason we don’t provide code for the ‘onSave’ event handler is because there are so many different ways to do this, and it is straight forward JS. This is the same case for the save functionality (there are many different ways to do this and is up to the users discretion).

Could you provide a recommendation on how we can improve?

Thanks!
Logan

I understand… I was after that exact functionality, but had to dig around quite a bit to make it work.

Perhaps just add a “view source” link to the demo (PDF.js Viewer Demo | PDF.js Express) that has all the working pieces and an external download button. It may be overkill in most cases, but would let people see how to do this as well as add / remove features on the fly.

Thanks Joe!

We do have a guide that explains all this here, but maybe it’s not visible enough. I’ll shuffle some things around to try and make this more clear.

Thanks for your feedback!

Logan