Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Print a pdf without visually opening it

The thing I want to build is that by clicking a button I want to trigger the print of a PDF file, but without opening it.

+-----------+
| Print PDF |
+-----------+
     ^ Click *---------> printPdf(pdfUrl)

The way how I first tried it is to use an iframe:

var $iframe = null;

// This is supposed to fix the onload bug on IE, but it's not fired
window.printIframeOnLoad = function() {
  if (!$iframe.attr("src")) { return; }
  var PDF = $iframe.get(0);
  PDF.focus();

  try {
    // This doesn't work on IE anyways
    PDF.contentWindow.print();

    // I think on IE we can do something like this:
    // PDF.document.execCommand("print", false, null);
  } catch (e) {
    // If we can't print it, we just open it in the current window
    window.location = url;
  }
};

function printPdf(url) {

  if ($iframe) {
    $iframe.remove();
  }

  $iframe = $('<iframe>', {
    class: "hide",
    id: "idPdf",
    // Supposed to be a fix for IE
    onload: "window.printIframeOnLoad()",
    src: url
  });

  $("body").prepend($iframe);
}

This works on Safari (desktop & iOS) and Chrome (can we generalize it maybe to webkit?).

On Firefox, PDF.contentWindow.print() ends with a permission denied error (even the pdf is loaded from the same domain).

On IE (11), the onload handler is just not working.


Now, my question is: is there another better way to print the pdf without visually opening it to the user?

The cross browser thing is critical here. We should support as many browsers as possible.

What's the best way to achieve this? Is my start a good one? How to complete it?

We are now in 2016 and I feel like this is still a pain to implement across the browsers.

like image 793
Ionică Bizău Avatar asked Jul 28 '16 14:07

Ionică Bizău


People also ask

Can you print a PDF without opening it?

Instead of opening a document to print it, you can save time by setting up automatic drag-and-drop printing. Instead of opening a document to print it, you can save time by setting up automatic drag-and-drop printing.


2 Answers

UPDATE: This link details an elegant solution that involves editing the page properties for the first page and adding an action on Page Open. Works across all browsers (as browsers will execute the JavaScript placed in the actions section). Requires Adobe Acrobat Pro.


It seems 2016 brings no new advancements to the printing problem. Had a similar issue and to make the printing cross-browser I solved it using PDF.JS but had to make a one-liner addition to the source (they ask you to build upon it in anyways).

The idea:

  • Download the pre-built stable release from https://mozilla.github.io/pdf.js/getting_started/#download and add the "build" and "web" folders to the project.
  • The viewer.html file is what renders out PDFs with a rich interface and contains print functionality. I added a link in that file to my own JavaScript that simply triggers window.print() after a delay.

The link added to viewer:

    <script src="viewer.js"></script>
    <!-- this autoPrint.js was added below viewer.js -->
    <script src="autoPrint.js"></script>
</head>

The autoPrint.js javascript:

(function () {
    function printWhenReady() {
        if (PDFViewerApplication.initialized) {
            window.print();
        }
        else {
            window.setTimeout(printWhenReady, 3000);
        }
    };

    printWhenReady();
})();
  • I could then put calls to viewer.html?file= in the src of an iframe and hide it. Had to use visibility, not display styles because of Firefox:

    <iframe src="web/viewer.html?file=abcde.pdf" style="visibility: hidden">
    

The result: the print dialog showed after a short delay with the PDF being hidden from the user.

Tested in Chrome, IE, Firefox.

like image 137
Balah Avatar answered Oct 19 '22 22:10

Balah


After spending the past couple of hours trying to figure this one out and lots of searching here is what I have determined...

The HTML5 Web API spec for Printing indicates that one of the printing steps must fire beforeprint, a simple event (an event that is non-cancelable), to the window object of the Document being printed (as well as any nested browsing contexts, this relates to iframes) to allow for changes to the Document prior to printing. This step is internal to the browser and not something you'll be able to adjust. During this process, the browser's print dialog sometimes shows a preview of the file (Chrome does this)...so if your goal is to never display the file to the viewer you might be stuck.

The closest to achieving this I came was by creating an index.html file which has a button containing data-* attributes which provided context. Change the path/filename.ext in the data-print-resource-uri attribute to a local file of your own.

<!DOCTYPE html>
<html>
    <head>
        <title>Express</title>
        <link rel="stylesheet" href="/stylesheets/style.css">
    </head>
    <body>
        <h1>Express</h1>
        <p>Welcome to Express</p>
        <button name="printFile" id="printFile" data-print-resource-uri="/binary/paycheckStub.pdf" data-print-resource-type="application/pdf">Print File</button>
        <iframe name="printf" id="printf" frameborder="0"></iframe>
        <script src="/javascripts/print.js"></script>
    </body>
</html>

Then in the print.js file, I tried a few things, but never quite got it working (leaving different things I had played with in the comments).

// Reference vars
var printButton = document.getElementById('printFile');
var printFrame = document.getElementById('printf');

// onClick handler
printButton.onclick = function(evt) {
    console.log('evt: ', evt);
    printBlob('printf', printButton.getAttribute('data-print-resource-uri'), printButton.getAttribute('data-print-resource-type'));
}

// Fetch the file from the server
function getFile( fileUri, fileType, callback ) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', fileUri);
    xhr.responseType = 'blob';
    xhr.onload = function(e) {
        // Success
        if( 200 === this.status ) {
            // Store as a Blob
            var blob = new Blob([this.response], {type: fileType});
            // Hang a URL to it
            blob = URL.createObjectURL(blob);
            callback(blob);
        } else {
            console.log('Error Status: ', this.status);
        }
    };
    xhr.send();
}

function printBlob(printFrame, fileUri, fileType) {
    // Debugging
    console.log('inside of printBlob');
    console.log('file URI: ', fileUri);
    console.log('file TYPE: ', fileType);

    // Get the file
    getFile( fileUri, fileType, function(data) {
        loadAndPrint(printFrame, data, fileType);
    });
}

function loadAndPrint(printFrame, file, type) {
    // Debugging
    console.log('printFrame: ', printFrame);
    console.log('file: ', file);

    window.frames[printFrame].src = file;
    window.frames[printFrame].print();

    /*
    // Setup the print window content
    var windowContent = '<!DOCTYPE html>';
    windowContent += '<html>'
    windowContent += '<head><title>Print canvas</title></head>';
    windowContent += '<body>'
    windowContent += '<embed src="' + file + '" type="' + type + '">';
    windowContent += '</body>';
    windowContent += '</html>';

    // Setup the print window
    var printWin = window.open('','','width=340,height=260');
    printWin.document.open();
    printWin.document.write(windowContent);
    printWin.document.close();
    printWin.focus();
    printWin.print();
    printWin.close();
    */
}

I think that if you can get it working properly using the Blob might work the best in the cross-browser method you wanted.

I found a few references about this topic which might be helpful:

  • How to send a pdf file directly to the printer using JavaScript?

  • https://www.w3.org/TR/html5/webappapis.html#printing

  • https://developer.mozilla.org/en-US/docs/Web/Guide/Printing#Print_an_external_page_without_opening_it

  • Printing a web page using just url and without opening new window?

like image 33
Benjamin Dean Avatar answered Oct 19 '22 20:10

Benjamin Dean