13

I need to print a PDF... But I get an error

Is there a workaround? I just need to print a PDF file with one click

error:

Uncaught SecurityError: Blocked a frame with origin "https://secure.domain.com" from accessing a frame with origin "https://cdn.domain.com". Protocols, domains, and ports must match.

code:

var iframe = $('<iframe src="'+url+'" style="display:none"></iframe>').appendTo($('#main')).load(function(){
    iframe.get(0).contentWindow.print();
});
Community
  • 1
  • 1
clarkk
  • 27,151
  • 72
  • 200
  • 340

6 Answers6

7

The error you are dealing with is related to cross-domain protection and the same-origin policy.

In your case, you can print an cross-domain iframe if you nest this iframe in another local iframe that we can call a proxy iframe.

Since the proxy iframe is local and have the same origin, you can print it without any issue and it'll also print the cross-domain iframe.

See below for an example:

index.html (container)

$(function() {
  var url = 'proxy.html'; // We're not loading the PDF but a proxy which will load the PDF in another iframe.

  var iframe = $('<iframe src="' + url + '"></iframe>').appendTo($('#main'));

  iframe.on('load', function(){
    iframe.get(0).contentWindow.print();
  });
});

proxy.html (proxy)

<body>
  <iframe src="http://ANOTHER_DOMAIN/PDF_NAME.pdf"></iframe>
</body>

With this solution, you no longer have cross-domain issues and you can use the print() function. The only things you need to deal with are a way to pass the PDF url from the container to the proxy and a way to detect when the iframe with the PDF is actually loaded but these depends on the solution / languages you're using.

HiDeoo
  • 10,353
  • 8
  • 47
  • 47
  • 7
    this answer doesn't work (in Chrome at least). Yes you can call `print()` on the wrapper iframe, but the print preview is blank, even if the pdf content is loaded in the inner iframe. – jtate Nov 09 '16 at 18:19
  • Here #main denotes div or iframe? – Vishnu Feb 15 '18 at 09:41
3

I needed to print a PDF embedded through a data:application/pdf;base64,… iframe, and I ran into the same cross-origin issue.

The solution was to convert the Base64 contents that I had into a blob, and then use put blob's object URL into the iframe src. After doing that I was able to print that iframe.

I know link-only answers are discouraged, but copying someone else's answers into my own didn't feel right either.

ᴍᴇʜᴏᴠ
  • 4,804
  • 4
  • 44
  • 57
  • Recently Chrome on iOS stopped being able to download files opened this way. You get a "This file could not be downloaded at this time" error. – jstaab Aug 03 '21 at 17:52
2

There is a workaround for this.

  1. Create an endpoint in your server to return the HTML content of the external url. (because you can't get external content from the browser - same-origin policy)

  2. Use $.get to fetch the external content from your URL and append it to an iframe.

Something similar to this:

HTML:

<div id="main">
    <iframe id="my-iframe" style="display:none"></iframe>
</div>

JS:

$.get('https://secure.domain.com/get-cdndomaincom-url-content', function(response) {
    var iframe = $('#my-iframe')[0],
        iframedoc = iframe.contentDocument || iframe.contentWindow.document;

    iframedoc.body.innerHTML = response;
    iframe.contentWindow.print();
});

C# implementation for get-cdndomaincom-url-content:

Easiest way to read from a URL into a string in .NET

Community
  • 1
  • 1
Safeer Hussain
  • 1,230
  • 1
  • 15
  • 27
  • But when i use a pdf url this shows encoded pdf code instead of rendering the pdf... anyone has an idea? – Miguel Dec 14 '16 at 11:29
  • @Miguel in your response endpoint (`get-cdndomaincom-url-content`) try sending the MIME type of the URL in response header. E.g. `application/pdf`. Ref: http://stackoverflow.com/a/11945256/826611 – Safeer Hussain Dec 14 '16 at 12:36
  • Or simply; If it is a PDF you may try directly using the URL for iframe's src. – Safeer Hussain Dec 14 '16 at 12:42
  • I solved the crossdomain, using a .php file as proxy readfile from url... but its sort of "ugly" – Miguel Dec 14 '16 at 13:08
2

You do not need proxy server for workaround. You can create proxy iframe and then dynamically create another iframe inside the proxy iframe. Then attach onload="print()" to it.

Something like this

  /**
   * Load iframe from cross-origin via proxy iframe
   * and then invokes the print dialog.
   * It is not possible to call window.print() on the target iframe directly
   * because of cross-origin policy.
   * 
   * Downside is that the iframe stays loaded. 
   */
  function printIframe(url) {
    var proxyIframe = document.createElement('iframe');
    var body = document.getElementsByTagName('body')[0];
    body.appendChild(proxyIframe);
    proxyIframe.style.width = '100%';
    proxyIframe.style.height = '100%';
    proxyIframe.style.display = 'none';

    var contentWindow = proxyIframe.contentWindow;
    contentWindow.document.open();
    // Set dimensions according to your needs.
    // You may need to calculate the dynamically after the content has loaded
    contentWindow.document.write('<iframe src="' + url + '" onload="print();" width="1000" height="1800" frameborder="0" marginheight="0" marginwidth="0">');
    contentWindow.document.close();
  }
David Votrubec
  • 3,968
  • 3
  • 33
  • 44
  • 2
    It can be difficult to work out `width="1000" height="1800"` when working with random content. But, still thanks for the solution. I am mutilating it now to see if I get anywhere. – Andrei Bazanov Jan 10 '20 at 15:35
0

--Issue--

HiDeo is right this is a cross-domain issue. It is a part of CORS which is a great thing for the security of the web but also a pain.

--Philosophy--

There are ways to work around CORS but I believe in finding a solution that works for most to all cases and keep using it. This creates easier code to reason about and keeps code consistent rather then changing and creating code for edge cases. This can create a harder initial solution but as you can reuse the method regardless of use case you end up saving time.

--Answer--

The way our team handles cross domain request issues is bypassing it completely. CORS is something for browsers only. So the best way to solve all cases of this issue is don't give the reasonability to the browser. We have the server fetch the document and giving it to the browser on the same domain.

(I'm an Angular guy) The client would say something like

$http.get('/pdf/x').then(function(){
    //do whatever you want with any cross-domain doument
});

The Server would have something like what you see here HTTP GET Request in Node.js Express

Community
  • 1
  • 1
Michael Warner
  • 3,879
  • 3
  • 21
  • 45
  • so how would you print the pdf content that is returned from the server? the question is how to print a pdf in the browser, not how to retrieve one. – jtate Nov 09 '16 at 18:23
0

It is a CORS issue . There is a library that acts as a CORS alternative , you can find it here Xdomain CORS alternative Github . It kind of by-passes CORS request seamlessly to render cross domain resources effectively.

It has a Javascript, AngularJS, as well as a JQuery wrapper too . I think this will provide you a more elegant solution, and is simple to integrate. Give it a try .

Saurabh Chaturvedi
  • 2,028
  • 2
  • 18
  • 39