简体   繁体   English

无需打开即可打印 pdf

[英]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.我想要构建的是,通过单击一个按钮,我想触发 PDF 文件的打印,但不打开它。

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

The way how I first tried it is to use an iframe:我第一次尝试的方式是使用 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?).这适用于 Safari(桌面和 iOS)和 Chrome(我们可以将它推广到 webkit 吗?)。

On Firefox, PDF.contentWindow.print() ends with a permission denied error (even the pdf is loaded from the same domain).在 Firefox 上, PDF.contentWindow.print()permission denied错误结束(即使 pdf 是从同一域加载的)。

On IE (11), the onload handler is just not working.在 IE (11) 上, onload处理程序不起作用。


Now, my question is: is there another better way to print the pdf without visually opening it to the user?现在,我的问题是:是否有另一种更好的方法来打印 pdf 而无需向用户打开它?

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.我们现在是 2016 年,我觉得跨浏览器实现这仍然很痛苦。

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).适用于所有浏览器(因为浏览器将执行放置在操作部分的 JavaScript)。 Requires Adobe Acrobat Pro.需要 Adob​​e Acrobat Pro。


It seems 2016 brings no new advancements to the printing problem. 2016 年似乎没有为打印问题带来新的进展。 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).有一个类似的问题,为了使打印跨浏览器,我使用PDF.JS解决了它,但不得不对源代码进行单行添加(他们要求您以任何方式构建它)。

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.https://mozilla.github.io/pdf.js/getting_started/#download下载预先构建的稳定版本,并将“build”和“web”文件夹添加到项目中。
  • The viewer.html file is what renders out PDFs with a rich interface and contains print functionality. viewer.html文件用于呈现具有丰富界面并包含打印功能的 PDF。 I added a link in that file to my own JavaScript that simply triggers window.print() after a delay.我在该文件中添加了一个链接到我自己的 JavaScript,它只是在延迟后触发 window.print()。

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: 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.然后我可以在 iframe 的 src 中调用viewer.html?file=并隐藏它。 Had to use visibility, not display styles because of Firefox:由于 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.结果:打印对话框在短暂延迟后显示,PDF 对用户隐藏。

Tested in Chrome, IE, Firefox.在 Chrome、IE、Firefox 中测试。

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 firebeforeprint , 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.用于打印HTML5 Web API 规范指示printing steps必须在beforeprint ,一个简单事件(不可取消的事件)中触发到正在打印的文档的窗口对象(以及任何嵌套的浏览上下文,这与 iframe 相关)以允许在打印之前更改文档。 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.在此过程中,浏览器的打印对话框有时会显示文件的预览(Chrome 会这样做)……因此,如果您的目标是永远不向查看器显示文件,您可能会卡住。

The closest to achieving this I came was by creating an index.html file which has a button containing data-* attributes which provided context.我最接近实现这一点的方法是创建一个index.html文件,该文件有一个按钮,其中包含提供上下文的 data-* 属性。 Change the path/filename.ext in the data-print-resource-uri attribute to a local file of your own.data-print-resource-uri属性中的 path/filename.ext 更改为您自己的本地文件。

<!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).然后在print.js文件中,我尝试了一些东西,但从来没有让它工作(在评论中留下我玩过的不同东西)。

// 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.我认为,如果您可以使用Blob使其正常工作,那么在您想要的跨浏览器方法中可能效果最佳。

I found a few references about this topic which might be helpful:我找到了一些有关此主题的参考资料,它们可能会有所帮助:

I will post here the modified functions of the OP functional on IE 11我将在此处发布 IE 11 上 OP 功能的修改功能

    printPdf: function (url) {
        $('#mainLoading').show();
        let iframe = $('#idPdf');
        if (iframe) {
            iframe.remove();
        }

        iframe = $('<iframe>', {
            style: "display:none",
            id: "idPdf"
        });

        $("body").prepend(iframe);
        $('#idPdf').on("load", function(){
            utilities.printIframeOnLoad()
       })

        utilities.getAsyncBuffer(url, function(response){
            let path = utilities.getPdfLocalPath(response);
            $('#idPdf').attr('src', path);
        })
    },

     printIframeOnLoad: function () {
        let iframe = $('#idPdf');
        if (!iframe.attr("src")) { return; }

        var pdf = iframe.get(0);
        pdf.focus();
        $('#mainLoading').hide();
        pdf.contentWindow.print();
    },


    getPdfLocalPath: function (data) {
        var filename = "Application_" + utilities.uuidv4() + ".pdf";
        var blob = new Blob([data], { type: 'application/pdf' });
        if (window.navigator.msSaveOrOpenBlob) {
            window.navigator.msSaveBlob(blob, filename);
            return filename;
        }
        else {
            let url = window.URL || window.webkitURL;
            let href = url.createObjectURL(blob);
            return href;
        }
    },

    getAsyncBuffer: function (uriPath, callback) {

        var req = new XMLHttpRequest();
        req.open("GET", uriPath, true);
        req.responseType = "blob";
        req.onreadystatechange = function () {
            if (req.readyState === 4 && req.status === 200) {
                callback(req.response);
            }
        };
        req.send();
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM