PDF.JS: Render PDF using an ArrayBuffer or Blob instead of URL
Asked Answered
N

1

12

I know of a similar question to this one: Pdf.js: rendering a pdf file using a base64 file source instead of url. That question was awesomely answered by Codetoffel but my question is different in that my PDF is retrieved via a RESTful call to my Web API implementation. Let me explain...

First, here's a basic way to use PDF.JS to open a PDF via a URL:

PDFJS.getDocument("/api/path/to/my.pdf").then(function (pdf) {
  pdf.getPage(1).then(function (page) {
    var scale = 1;
    var viewport = page.getViewport(scale);
    var canvas = document.getElementById('the-canvas');
    var context = canvas.getContext('2d');
    canvas.height = viewport.height;
    canvas.width = viewport.width;
    page.render({canvasContext: context, viewport: viewport});
  });
});

This works great, but I am using Angular and its $resource service to make the request for the PDF via my RESTful Web API. I do know that PDF.JS allows me to replace passing the URL as a string in the PDFJS.getDocument method (above) with a DocumentInitParams object, which is defined here. Using the DocumentInitParams object works as follows:

var docInitParams = {
    url: "/api/path/to/my.pdf",
    httpHeaders: getSecurityHeaders(), //as needed
    withCredentials: true
};
PDFJS.getDocument(docInitParams).then(function (pdf) {
    ...
});

This also works, but it works around my Angular $resource by requiring me to construct the api URL. But that's OK because PDFJS allows me to give it the PDF data directly, instead of giving it a URL to the PDF, as follows:

var myPdf = myService.$getPdf({ Id: 123 });

//It's an Angular $resource, so there is a promise to be fulfilled...
myPdf.$promise.then(function() {
    var docInitParams = {
        data: myPdf
    };
    PDFJS.getDocument(docInitParams).then(function (pdf) {
        ...
    });
});

This is the one I can't seem to get to work. I can tell the myService.$gtPdf method to return the data as a blob or as an arraybuffer but neither works. I've tried to convert the arraybuffer returned data to an Uint8Array too, but to no avail.

I'm not sure what else to try and could really use a tip.

How do I get the data returned from my service to work with PDFJS?

Thanks in advance.

Nautch answered 18/6, 2014 at 14:27 Comment(0)
M
21

You're not passing the response data to PDF.js, but an instance of the resource:

var myPdf = myService.$getPdf({ Id: 123 });
myPdf.$promise.then(function() {
    var docInitParams = {
        data: myPdf

You haven't shown your code for $getPdf, but I guess that it is something equivalent to

var myService = $resource('/foo', {}, {$getPdf:{responseType: 'arraybuffer'}});
var myPdf = myService.$getPdf();

By default, an AngularJS $resource treats the response as an object (deserialized from JSON) and copies any properties from the object to the resource instance (myPdf in the previous snippet).

Obviously, since your response is an array buffer (or Blob, typed array or whatever), this is not going to work. One of the ways to get the desired response is to use transformResponse to wrap the response object in an object:

var myService = $resource('/foo', {}, {
    $getPdf: {
        responseType: 'arraybuffer',
        transformResponse: function(data, headersGetter) {
            // Stores the ArrayBuffer object in a property called "data"
            return { data : data };
        }
    }
});
var myPdf = myService.$getPdf();
myPdf.$promise.then(function() {
    var docInitParams = {
        data: myPdf.data
    };

    PDFJS.getDocument(docInitParams).then(function (pdf) {
        // ...
    });
});

Or simply the following (avoided unnecessary local variables):

myService.$getPdf().$promise.then(function(myPdf) {
    PDFJS.getDocument({
        data: myPdf.data
    }).then(function (pdf) {
        // ...
    });
});
Magruder answered 18/6, 2014 at 16:0 Comment(3)
Thanks! I thought I needed a PDF.JS expert to chime in, but someone with better Angular knowledge was really the key. I was already using transformResponse but not properly since I was treating the resource as if it was the response. Once you pointed that out, everything else fell into place.Nautch
Hello, what do you do with the pdf in the .then to load the document into the viewer.html?Rusk
If you want to use this with the standard viewer.html, then use PDFViewerApplication.open(myPdf);.Magruder

© 2022 - 2024 — McMap. All rights reserved.