89

I have a formatted PDF string that looks like

%PDF-1.73 0 obj<<< /Type /Group /S /Transparency /CS /DeviceRGB >> /Resources 2 0 R/Contents 4 0 R>> endobj4 0 obj<> streamx��R=o�0��+��=|vL�R���l�-��ځ,���Ge�JK����{���Y5�����Z˯k�vf�a��`G֢ۢ��Asf�z�ͼ��`%��aI#�!;�t���GD?!���<�����B�b��

...

00000 n 0000000703 00000 n 0000000820 00000 n 0000000926 00000 n 0000001206 00000 n 0000001649 00000 n trailer << /Size 11 /Root 10 0 R /Info 9 0 R >>startxref2015%%EOF

I am trying to open up this string in a new window as a PDF file. Whenever I use window.open() and write the string to the new tab it thinks that the text should be the contents of an HTML document. I want it to recognize that this is a PDF file.

Any help is much appreciated

DaveC
  • 1,039
  • 2
  • 9
  • 6
  • How are you "writing" this string? Do you mean it's returned by the backend (if so, what platform?). If the PDF data is being generated by the backend, make sure you set the correct Content-Type header. – Ates Goral May 10 '10 at 18:37
  • Best answer: You should not be using AJAX in this situation. – Josh Stodola May 10 '10 at 18:43
  • @Josh - He never mentioned AJAX. It could be a standalone HTML page on a CD dynamically generating customised strings for all we know. I doubt that this is possible though. – Martin Smith May 10 '10 at 18:52
  • 1
    It is being created on the backend but it will not be possible/elegant to save on the server. Furthermore, content is already being written to the page before the pdf is being generated.... thus setting a header to pdf will not work. – DaveC May 10 '10 at 18:53
  • I'm 99% sure that there is no way of doing what you are trying to do and that it will need to be 2 separate requests but will be interested if someone proves me wrong! – Martin Smith May 10 '10 at 19:05
  • 2
    @Josh Stodola why one should not be using AJAX in this situation – user1451111 Jun 29 '17 at 15:22
  • i have a same issue : https://stackoverflow.com/questions/55935927/file-get-contents-method-return-some-value-with-strange-symbols-while-calling-a Did someone answered @DaveC ?? – danish farhaj May 01 '19 at 14:21
  • @DaveC did u find the solution for this? – Zeeshan Chaudhary Jan 16 '23 at 22:05

17 Answers17

127

Just for information, the below

window.open("data:application/pdf," + encodeURI(pdfString)); 

does not work anymore in Chrome. Yesterday, I came across with the same issue and tried this solution, but did not work (it is 'Not allowed to navigate top frame to data URL'). You cannot open the data URL directly in a new window anymore. But, you can wrap it in iframe and make it open in a new window like below. =)

let pdfWindow = window.open("")
pdfWindow.document.write(
    "<iframe width='100%' height='100%' src='data:application/pdf;base64, " +
    encodeURI(yourDocumentBase64VarHere) + "'></iframe>"
)
CroMagnon
  • 1,218
  • 7
  • 20
  • 32
Noby Fujioka
  • 1,744
  • 1
  • 12
  • 15
  • 7
    Works in Chrome but once loaded it consumes CPU and GPU and the tab says it is still loading. Tried with `iframe`, `embed` and `object`. – Esamo Oct 05 '18 at 10:21
  • @Robin Try using width and height as pixels rather than percentages. try – Siva Kiran Jul 03 '19 at 10:01
  • 4
    @SivaKiran setTimeout-0 solve my problem like this `setTimeout(() => { pdfWindow.document.write(``); pdfWindow.document.title = `${title}`; }, 0); ` – Robin Jul 03 '19 at 10:40
  • 4
    this work great, the document opened in new tab. But, when I click the download icon on the right corner, nothing happen. How to make it downloadable? – Akza Aug 12 '19 at 06:53
  • is this downloadable? – JChao Oct 22 '19 at 21:30
  • 2
    The tab still loading issue is very odd. 4 of our team members used the same button to open the pdf, and for 2 people it shows loading in the favicon, but for 2 of us it doesnt show the loading animation.... everyone is using the same version of chrome... – Ray May 06 '20 at 20:21
  • After integrating this code my opened page is loading all time and download button also not working.Any solution? – Ajas Aju Mar 30 '21 at 08:36
  • 1
    ```setTimeout(function(){ pdfWindow.stop() }, 1000);``` at the end stops the loading issue – Peter Apr 08 '21 at 20:39
68
var byteCharacters = atob(response.data);
var byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
  byteNumbers[i] = byteCharacters.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
var file = new Blob([byteArray], { type: 'application/pdf;base64' });
var fileURL = URL.createObjectURL(file);
window.open(fileURL);

You return a base64 string from the API or another source. You can also download it.

Roy Scheffers
  • 3,832
  • 11
  • 31
  • 36
Himanshu Chawla
  • 987
  • 7
  • 8
  • 2
    This seems like by far the best solution. Ever other solution seemed to work sporadically at best. I kept trying to encode the string responses from the server, but this seemed to be the only sure fire way. Thanks! – Drew Mar 23 '19 at 03:10
  • I m getting error "failed to execute atob to window.any help – Nagnath Mungade Jan 17 '20 at 05:04
  • 1
    Is there a way to obfuscate the url. I want to be the name of the file. Right now it shows as encoded base 64 value? – Learn AspNet Mar 06 '20 at 20:22
  • Wish I could upvote this a hundred times! I've been struggling to get larger files to open for days. Thank you ever so much! – phunder Sep 17 '20 at 08:08
  • This should be the accepted answer, as it doesn't use the suboptimal iframe solution! Grats! – jimmious Oct 07 '20 at 10:11
  • Recently faced this issue, and I can confirm that the code above is tested on chrome Version 87.0.4280.88 and it works – Bug Hunter Zoro Jan 04 '21 at 17:41
  • Thx you. This saved my week. In the end it was sufficient to download the pdf as blob and use that as the byteArray in your example. `const blob = new Blob([assetData], { type: mimeType }); resolve(URL.createObjectURL(blob));` – mgamsjager Jan 20 '21 at 10:01
  • Thanks mate,how to change the Browser title in this solution? – Ajas Aju Mar 30 '21 at 09:02
  • 2
    @himanshu-chawla awseome answer! As a side question - is there a way to define the default file name when clicking download button, so it doesn't look like `6a5e4cd0-2db4-4c8c-80b8-15b77f911700.pdf` but `myFile.pdf`? – neggenbe Jul 07 '21 at 08:09
  • This is working well, but it is just downloading the pdf file, and I should open manually. Is there any way to open it on new browser automatically? – Ping Zhao Apr 25 '22 at 10:10
  • This solutions works if the base 64 string is so long – Ignacio Marín Reyes Aug 04 '22 at 11:01
26

You might want to explore using the data URI. It would look something like.

window.open("data:application/pdf," + escape(pdfString));

I wasn't immediately able to get this to work, possible because formating of the binary string provided. I also usually use base64 encoded data when using the data URI. If you are able to pass the content from the backend encoded you can use..

window.open("data:application/pdf;base64, " + base64EncodedPDF);

Hopefully this is the right direction for what you need. Also note this will not work at all in IE6/7 because they do not support Data URIs.

Morgan ARR Allen
  • 10,556
  • 3
  • 35
  • 33
  • 2
    won't work in IE 8+ either, because the URL size will most likely be > 2048 character limit that IE imposes on URLs – Jeff Sep 19 '14 at 19:31
  • I believe this restriction does not apply to Data URIs, only URLs. IE8 has a 32KB Data URI limit that was removed in IE9. – Morgan ARR Allen Sep 20 '14 at 20:02
  • 2
    That's great but window.open(url) takes a URL, not a URI. IE chops off anything after 2048 chars, even if you use a data URI. Try this jsfiddle in IE, you'll see it doesn't work: http://jsfiddle.net/bpdj7ksv/ – Jeff Sep 21 '14 at 13:33
  • This doesn't work for me window.open('data:application/pdf;base64,' + btoa(unescape(encodeURIComponent(resp))), "_blank", "toolbar=yes, scrollbars=yes, resizable=yes"); – vini Jul 08 '15 at 14:21
20

This one worked for me.

window.open("data:application/octet-stream;charset=utf-16le;base64,"+data64);

This one worked too

 let a = document.createElement("a");
 a.href = "data:application/octet-stream;base64,"+data64;
 a.download = "documentName.pdf"
 a.click();
Felipe Bejarano
  • 516
  • 5
  • 8
  • I put the second part of code in a javascript function and called it onclick function of buttom and get answer very well. – Hojjat Jun 30 '23 at 20:17
18
window.open("data:application/pdf," + escape(pdfString)); 

The above one pasting the encoded content in URL. That makes restriction of the content length in URL and hence PDF file loading failed (because of incomplete content).

dandan78
  • 13,328
  • 13
  • 64
  • 78
  • 5
    warning: chrome detects this as pop-up and blocks – aldo.roman.nurena Nov 19 '13 at 21:49
  • 2
    This will not work in MSIE due to the 2048 max character URL length MS decided to impose. – Jeff Sep 19 '14 at 19:29
  • 2
    This limit does not apply to Data URIs, only normal navigational URLs. Reference: http://msdn.microsoft.com/en-us/library/cc848897(v=vs.85).aspx – Morgan ARR Allen Sep 20 '14 at 20:10
  • does anyone know why you wouldn't be able to interact with this window after opening it? like this `var wdw = window.open("data:application/pdf," + escape(pdfString)); wdw.print();` The print function doesn't do anything. And I've even tried using a `setTimeout` to delay the print, but that doesn't work either. It's like it loses the reference to the window object. If I do the same thing but pass in an actual web url it works. – jtate Nov 08 '16 at 13:58
  • how you solved such issue when the length of the content is too large ? – Hala Apr 01 '18 at 09:51
17

I realize this is a pretty old question, but I had the same thing come up today and came up with the following solution:

doSomethingToRequestData().then(function(downloadedFile) {
  // create a download anchor tag
  var downloadLink      = document.createElement('a');
  downloadLink.target   = '_blank';
  downloadLink.download = 'name_to_give_saved_file.pdf';

  // convert downloaded data to a Blob
  var blob = new Blob([downloadedFile.data], { type: 'application/pdf' });

  // create an object URL from the Blob
  var URL = window.URL || window.webkitURL;
  var downloadUrl = URL.createObjectURL(blob);

  // set object URL as the anchor's href
  downloadLink.href = downloadUrl;

  // append the anchor to document body
  document.body.appendChild(downloadLink);

  // fire a click event on the anchor
  downloadLink.click();

  // cleanup: remove element and revoke object URL
  document.body.removeChild(downloadLink);
  URL.revokeObjectURL(downloadUrl);
});
Chris Cashwell
  • 22,308
  • 13
  • 63
  • 94
  • 1
    This is a good solution! Works only in IE10+ though, according to [MDN](https://developer.mozilla.org/de/docs/Web/API/URL/createObjectURL) – Van Coding Sep 21 '16 at 13:58
  • on chrome I get document.body.append is not a function and so I used: document.getElementsByTagName('body')[0].appendChild(downloadLink); – manuel-84 Oct 05 '16 at 10:24
  • Thanks @manuel-84, you found a typo. It should have been `appendChild` from the start. – Chris Cashwell Oct 05 '16 at 18:44
  • Note that adding download property (as in the example) will cause the file to download instead to open in a new window/tab. And it's not supported in Safari. – Zemljoradnik Dec 27 '16 at 10:25
  • Hi @ChrisCashwell, I have the same problem. I want to know what is parameter `downloadedFile`. Why does it contains `data` property? Is `data` string or `blob`? – Ricky Jiao Feb 08 '17 at 13:31
  • @RickyJiao `downloadedFile` is a raw file returned from an AJAX request (like `fetch`), so `downloadedFile.data` is binary in the case of a PDF. – Chris Cashwell Feb 12 '17 at 22:59
  • `var blob = new Blob([downloadedFile.data], { type: 'application/pdf' });` great suggestion! I had to change it to `application/octet-stream` in order to get the download window. Otherwise the text stream would be sent to the browser. – Jan_V Jul 28 '17 at 11:50
12

An updated version of answer by @Noby Fujioka:

function showPdfInNewTab(base64Data, fileName) {  
  let pdfWindow = window.open("");
  pdfWindow.document.write("<html<head><title>"+fileName+"</title><style>body{margin: 0px;}iframe{border-width: 0px;}</style></head>");
  pdfWindow.document.write("<body><embed width='100%' height='100%' src='data:application/pdf;base64, " + encodeURI(base64Data)+"#toolbar=0&navpanes=0&scrollbar=0'></embed></body></html>");
}
Sastrija
  • 3,284
  • 6
  • 47
  • 64
4

I just want to add with @Noby Fujioka's response, Edge will not support following

window.open("data:application/pdf," + encodeURI(pdfString));

For Edge we need to convert it to blob and this is something like following

//If Browser is Edge
            if (window.navigator && window.navigator.msSaveOrOpenBlob) {
                var byteCharacters = atob(<Your_base64_Report Data>);
                var byteNumbers = new Array(byteCharacters.length);
                for (var i = 0; i < byteCharacters.length; i++) {
                    byteNumbers[i] = byteCharacters.charCodeAt(i);
                }
                var byteArray = new Uint8Array(byteNumbers);
                var blob = new Blob([byteArray], {
                    type: 'application/pdf'
                });
                window.navigator.msSaveOrOpenBlob(blob, "myreport.pdf");
            } else {
                var pdfWindow = window.open("", '_blank');
                pdfWindow.document.write("<iframe width='100%' style='margin: -8px;border: none;' height='100%' src='data:application/pdf;base64, " + encodeURI(<Your_base64_Report Data>) + "'></iframe>");
            }
Hasibul
  • 569
  • 2
  • 6
  • 21
3

Based off other old answers:

escape() function is now deprecated,

Use encodeURI() or encodeURIComponent() instead.

Example that worked in my situation:

window.open("data:application/pdf," + encodeURI(pdfString)); 

Happy Coding!

EaziLuizi
  • 1,557
  • 1
  • 16
  • 25
  • Because this open a new tab where in the url are the PDF content, there is a PDF limit size? I did tried many solusions and only this works but... if i have a PDF that is 2 MB ? – Mistre83 Feb 04 '17 at 22:05
  • 1
    @Mistre83 - Not entirely sure I understand your question, but I have used it numerous times on some rather large PDF's and it worked, can't test right now, try it on a large PDF and see, it should work though, if there is a size limit I would assume it to be quite large like 2-4GB (pure guesstimates) – EaziLuizi Feb 06 '17 at 19:58
3

I had this problem working with a FedEx shipment request. I perform the request with AJAX. The response includes tracking #, cost, as well as pdf string containing the shipping label.

Here's what I did:

Add a form:

<form id='getlabel' name='getlabel' action='getlabel.php' method='post' target='_blank'>
<input type='hidden' id='pdf' name='pdf'>
</form>

Use javascript to populate the hidden field's value with the pdf string and post the form.

Where getlabel.php:

<?
header('Content-Type: application/pdf');
header('Content-Length: '.strlen($_POST["pdf"]));
header('Content-Disposition: inline;');
header('Cache-Control: private, max-age=0, must-revalidate');
header('Pragma: public');
print $_POST["pdf"];
?>
anothermh
  • 9,815
  • 3
  • 33
  • 52
Dan Ross
  • 87
  • 6
0

//for pdf view

let pdfWindow = window.open("");
pdfWindow.document.write("<iframe width='100%' height='100%' src='data:application/pdf;base64," + data.data +"'></iframe>");
0

I know this is too late to answer this but I just got stuck in 'opening pdf in new page on clicking the name of pdfFile' and this worked for me:

Js-

fileName = '';
    fileUrl = '';

    handleFileUpload(event) {
        const file = event.target.files[0];
        this.fileName = file.name;
        this.fileUrl = URL.createObjectURL(file);
    }

Html-

<div class="slds-form-element">
        <lightning-input
            type="file"
            label="Upload PDF File"
            onchange={handleFileUpload}>
        </lightning-input>
    </div>
    <div class="slds-form-element">
        <a href={fileUrl}  target="_blank">{fileName}</a>
    </div>
0

In my case, I was receiving a pdf string from the backend and I had to set the responseType in Axios

{
    responseType: 'blob',
} 

and then like this, you can open a pdf file in a new tab

const file = new Blob([file], { type: 'application/pdf' });
const url = URL.createObjectURL(file);
window.open(url, '_blank');
Basir Baig
  • 111
  • 1
  • 8
-1

One suggestion is that use a pdf library like PDFJS.

Yo have to append, the following "data:application/pdf;base64" + your pdf String, and set the src of your element to that.

Try with this example:

var pdfsrc = "data:application/pdf;base64" + "67987yiujkhkyktgiyuyhjhgkhgyi...n"

<pdf-element id="pdfOpen" elevation="5" downloadable src="pdfsrc" ></pdf-element>

Hope it helps :)

DaFois
  • 2,197
  • 8
  • 26
  • 43
Sehul Viras
  • 587
  • 5
  • 9
  • Life saver, my client has an aversion to pdfs automatically being downloaded to the pc, they just want to be able to print from browser. – Sean T Jan 24 '18 at 17:07
-2

Just encode your formatted PDF string in base 64. Then you should do:

$pdf = 'data:application/pdf;base64,'.$base64EncodedString;

return this to javascript and open in a new window:

window.open(return);
-2

for the latest Chrome version, this works for me :

var win = window.open("", "Title", "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=780,height=200,top="+(screen.height-400)+",left="+(screen.width-840));
win.document.body.innerHTML = 'iframe width="100%" height="100%" src="data:application/pdf;base64,"+base64+"></iframe>';

Thanks

Rai Vu
  • 1,595
  • 1
  • 20
  • 30
-2

use function "printPreview(binaryPDFData)" to get print preview dialog of binary pdf data.

printPreview = (data, type = 'application/pdf') => {
    let blob = null;
    blob = this.b64toBlob(data, type);
    const blobURL = URL.createObjectURL(blob);
    const theWindow = window.open(blobURL);
    const theDoc = theWindow.document;
    const theScript = document.createElement('script');
    function injectThis() {
        window.print();
    }
    theScript.innerHTML = `window.onload = ${injectThis.toString()};`;
    theDoc.body.appendChild(theScript);
};

b64toBlob = (content, contentType) => {
    contentType = contentType || '';
    const sliceSize = 512;
     // method which converts base64 to binary
    const byteCharacters = window.atob(content); 

    const byteArrays = [];
    for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
        const slice = byteCharacters.slice(offset, offset + sliceSize);
        const byteNumbers = new Array(slice.length);
        for (let i = 0; i < slice.length; i++) {
            byteNumbers[i] = slice.charCodeAt(i);
        }
        const byteArray = new Uint8Array(byteNumbers);
        byteArrays.push(byteArray);
    }
    const blob = new Blob(byteArrays, {
        type: contentType
    }); // statement which creates the blob
    return blob;
};
Manoj
  • 57
  • 1
  • 3