Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

print a pdf via iframe (cross domain)

Tags:

javascript

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();
});
like image 719
clarkk Avatar asked Jul 02 '16 20:07

clarkk


4 Answers

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.

like image 132
mehov Avatar answered Sep 20 '22 20:09

mehov


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.

like image 27
HiDeo Avatar answered Nov 13 '22 14:11

HiDeo


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

like image 2
Safeer Hussain Avatar answered Nov 13 '22 16:11

Safeer Hussain


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();
  }
like image 2
David Votrubec Avatar answered Nov 13 '22 14:11

David Votrubec