2

I am stuck at last point of my application, i am supposed to display user form in PDF which works fine on desktop browsers as they has pdf viewer built in, but for Android / iOS its not working as pdf viewer is missing.

So i was trying to use PDF.js to display it, (to be honest, this is very widely used but documentation is lacking), only catch is i am getting data in base64 format. PDF.js has example on site which shows how to render the base64 data but its not PDF, for that displaying PDF as "PDF" i need to user their "viewer.html" but that does not take base64 data?

closest i have come to Pdf.js: rendering a pdf file using base64... on stack overflow, but i dont know how to use it after PDFJS.getDocument(pdfAsArray)?.

Other link that came across was other link

I dont want to rely on Google / Third party PDF viewer as i dont know how long they will support this.

Ninad Mhatre
  • 549
  • 1
  • 7
  • 17

1 Answers1

4

There are no end-to-end answers on this topic in community so here is my attempt to put something here. (maybe it will help others)

Okay, PDF.js is one way of showing PDF in browser, specially when you don't want to rely on PDF plugin to be installed. In my case, my application generates report in PDF and that can be viewed before downloading but on handheld devices it was not working because of missing PDF viewer plugin.

In my case PDF was sent to browse in base64 string, that I can use to display PDF with <object src="base64-data"...></object>. This works like charm on Chrome / FF but switch to mobile view and it stops working.

<object type="application/pdf" id="pdfbin" width="100%" height="100%" title="Report.pdf">
   <p class="text-center">Looks like there is no PDF viewer plugin installed, try one of the below approach...</p>                
</object>

In above code it will try to show the PDF or fall back to <p> and show error message. And I Was planning to add the PDF viewer at this point, PDF.js was the choice but was not able to display it. One example on PDF.js with Base64 data shows how to do this but that renders it as an Image not PDF, and I was not able to find solution for that and hence the question, here is what I did,

  1. First add the JavaScript code to convert base64 to array

  2. convert to blob and use viewer.html file packaged with PDF.js to display it as PDF

In case if you are wondering why base64 data, then answer is simple I can create the PDF, read it, send the data to client and delete the file, I don't have to run any cleaner service/cron job to delete generated PDF files

Few Things To Note

  1. Below code is using Flask + Jinja2, change the way base64 is read in html if you are using something else
  2. viewer.html needs to be changed to have required js & css files in proper location (by default their location is relative; you need them to be referred from static folder)
  3. viewer.js looks for pdf.worker.js in predefined location, change that in case its throwing error as above file not found.
  4. viewer.js might throw file origin does not match viewer error in that case as a quick fix comment the code which throws this error and see if that solves the issue (look for that error in viewer.js)
  5. I am not the author of below code, I have just put it together from different places.

Now to the code (so PDF will be displayed when user clicks on button with id="open_id")


Jquery

var pdfDataX = '{{ base64Pdf }}';
var BASE64_MARKER = ';base64,';
PDFJS.workerSrc = "{{ url_for('static', filename='js/pdf.worker.js') }}";

$('#open_id').click(function() {        
    PDFJS.disableWorker = true;
    var pdfAsDataUri = "data:application/pdf;base64," + pdfDataX ;
    PDFJS.workerSrc = "{{ url_for('static', filename='js/pdf.worker.js') }}";

    // Try to show in the viewer.html

    var blob = base64toBlob(pdfDataX, 'application/pdf');
    var url = URL.createObjectURL(blob);
    var viewerUrl = "{{ url_for('static', filename='viewer.html') }}" + '?file=' + encodeURIComponent(url);
    $('#pdfViewer').attr('src', viewerUrl);

    // Finish

    var mdObj = $('#pdfbin');

    mdObj.hide();
    mdObj.attr('data', pdfAsDataUri);
    mdObj.show();

    $('#myModal').modal();
});

var base64toBlob = function(b64Data, contentType, sliceSize) {
    contentType = contentType || '';
    sliceSize = sliceSize || 512;

    var byteCharacters = atob(b64Data);
    var byteArrays = [];

    for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
        var slice = byteCharacters.slice(offset, offset + sliceSize);

        var byteNumbers = new Array(slice.length);
        for (var i=0; i<slice.length; i++) {
            byteNumbers[i] = slice.charCodeAt(i);
        }

        var byteArray = new Uint8Array(byteNumbers);

        byteArrays.push(byteArray);
    }
    var blob = new Blob(byteArrays, {type: contentType});
    return blob;
}

$('.save').click(function(e) {
    e.preventDefault();
    var blob = base64toBlob(pdfDataX, 'application/pdf');
    saveAs(blob, 'abcd.pdf');  // requires https://github.com/eligrey/FileSaver.js/
    return false;
});

HTML

<object type="application/pdf" id="pdfbin" width="100%" height="100%" title="Resume.pdf">
    <p class="text-center">Looks like there is no PDF viewer plugin installed, try one of the below approach...</p>
    <iframe id="pdfViewer" style="width: 100%; height: 100%;" allowfullscreen="" webkitallowfullscreen=""></iframe>                  
</object>

Hope this will be useful for others in future.

Ninad Mhatre
  • 549
  • 1
  • 7
  • 17