Search code examples
javascriptpdfdata-uri

PDF Download in HREF of a-element results in empty PDF


I use a-element as download-button for files I have had to query with AJAX before as described here, here and here. I put to put the file-data as Data-URI into a a-element to create a download-button. Unfortunately I can not just point to the file but have to do it like that. With most download-formats HTML, CSV it works like this:

var mimeType = "text/html"; // example. works also with others.
var BOM = '\ufeff';
var url = "data:"+ mimeType +";charset=UTF-8," + BOM + encodeURIComponent(response.data);
var linkElem = document.querySelector("#hiddenDownloadLink");
var link = angular.element(linkElem);
link.prop("href", url);
link.prop("download", 'myFile.' + extension);
linkElem.click();

Okay. That works. But not for PDF.

I create a PDF in my backend (java, with openhtmltopdf but it doesn't matter I guess, since the PDF is definitely correct):

httpOutputMessage.getHeaders().add(HttpHeaders.CONTENT_TYPE, "application/pdf");
httpOutputMessage.getHeaders().add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"myFile.pdf\"");
makePdf(httpOutputMessage.getBody());

If I query the backend directly, or even if I log the output into a file, everything is fine. But when I use my download-controller as described above, I get a PDF with the right number of pages but completely empty! I think there must be an encoding issue. I tried with and without BOM, also with or without encodeURIComponent.

I also tried to use the base64-decoding as. Because window.atob(response.data) fails because of line breaks and others, I tried this conversion. The result is an broken PDF. Results in broken PDFs. I am not sure if that makes any sense.

My PDF-data starts like this, so it's not compressed or encoded anyhow:

%PDF-1.4
%����
1 0 obj
<<
/Type /Catalog
/Version /1.7
/Pages 2 0 R
>>
endobj
3 0 obj
<<

I also tried to convert the bytestream to blob and generate a link as described here orhere, but that creates broken PDFs.

Any Ideas, why I get empty PDFs or what might go wrong here and how can I repait the download-link?

-- Edit 1

I also get valid, but blank PDF when I try

var blob = new Blob([response.data], {type: 'application/pdf'});
var url = window.URL.createObjectURL(blob);
link.prop("href", url);

When I pipe response.data through this function, I get a broken PDF.

var utf8_to_b64 = function(str) {
  var unescape = window.unescape || window.decodeURI;
  str = encodeURIComponent(str);
  str = unescape(str);
  str = window.btoa(str);

  return str;
};

Solution

  • So I could fully reproduce your situation. I loaded and output a 4 page PDF file through an AJAX request from backend, and with your code example got a 4 page blank PDF.

    Here's what I did after (and got the right PDF to download):

    I base64 encoded the output from the backend. In my case I was using PHP so it was something like this:

    $pdf = file_get_contents('test.pdf');
    header('Content-Type: application/pdf');
    echo base64_encode($pdf);
    

    Then in the frontend I changed only this line:

    var url = "data:"+ mimeType +";charset=UTF-8," + BOM + encodeURIComponent(response.data);
    

    to this:

    var url = "data:"+ mimeType +";base64," + encodeURIComponent(response.data);
    

    Hope this helps.