提供PDF下载后自动打开打印机对话框

Joh*_*tts 5 printing pdf jsf jasper-reports primefaces

我目前正在浏览器的新选项卡中打开pdf文件,但我需要知道如何在按下commandButton后打开打印机对话框来打印pdf jasper报告

这是在新选项卡中打开pdf的方法:

public void printJasper() {

    JasperReport compiledTemplate = null;
    JRExporter exporter = null;
    ByteArrayOutputStream out = null;
    ByteArrayInputStream input = null;
    BufferedOutputStream output = null;

    FacesContext facesContext = FacesContext.getCurrentInstance();
    ExternalContext externalContext = facesContext.getExternalContext();
    HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();

    try {

        List<String> sampleList = new ArrayList<String>();
        sampleList.add("Fist sample string");
        sampleList.add("Second sample string");

        JRBeanCollectionDataSource beanCollectionDataSource = new JRBeanCollectionDataSource(sampleList);
        Map<String, Object> reportValues = new HashMap<String, Object>();
        reportValues.put("anyTestValue", "test value");

        facesContext = FacesContext.getCurrentInstance();
        externalContext = facesContext.getExternalContext();
        response = (HttpServletResponse) externalContext.getResponse();

        FileInputStream file = new FileInputStream("/any_dir/sample.jasper");
        compiledTemplate = (JasperReport) JRLoader.loadObject(file);

        out = new ByteArrayOutputStream();
        JasperPrint jasperPrint = JasperFillManager.fillReport(compiledTemplate, reportValues, beanCollectionDataSource);

        exporter = new JRPdfExporter();
        exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
        exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, out);
        exporter.exportReport();

        input = new ByteArrayInputStream(out.toByteArray());

        response.reset();
        response.setHeader("Content-Type", "application/pdf");
        response.setHeader("Content-Length", String.valueOf(out.toByteArray().length));
        response.setHeader("Content-Disposition", "inline; filename=\"fileName.pdf\"");
        output = new BufferedOutputStream(response.getOutputStream(), Constants.DEFAULT_BUFFER_SIZE);

        byte[] buffer = new byte[Constants.DEFAULT_BUFFER_SIZE];
        int length;
        while ((length = input.read(buffer)) > 0) {
            output.write(buffer, 0, length);
        }
        output.flush();

    } catch (Exception exception) {
        /* ... */
    } finally {
        try {
            if (output != null) {
                output.close();
            }
            if (input != null) {
                input.close();
            }
        } catch (Exception exception) {
            /* ... */
        }
    }
    facesContext.responseComplete();
}
Run Code Online (Sandbox Code Playgroud)

这是打开pdf文件的按钮:

<p:commandButton action="#{sampleBB.printJasper}"
    ajax="false" onclick="this.form.target='_blank'"
    value="#{msg['generate.report']}" />
Run Code Online (Sandbox Code Playgroud)

我需要做什么?

Bal*_*usC 11

使用JasperReports

使用JasperReports时,只需将此参数添加到JasperReports导出器:

exporter.setParameter(JRPdfExporterParameter.PDF_JAVASCRIPT, "this.print();");
Run Code Online (Sandbox Code Playgroud)

这基本上指示Adobe Acrobat this.print()在打开PDF时执行脚本.另请参阅" Adobe Acrobat脚本指南"的第79-80页.以下是相关摘录:

打印PDF文档

可以使用Acrobat JavaScript指定是将PDF文档发送到打印机还是PostScript文件.在任何一种情况下,要打印PDF文档,请调用doc对象的print方法.[...]


没有JasperReports

如果您无法控制PDF的生成,因此无法操纵它来添加上述脚本,另一种方法是相应地更改所有Java/JSF代码,以便PDF文件可以有效地使用(即PDF文件必须仅通过GET请求而不是POST请求可用.这允许您将其嵌入<iframe>到onload中,然后在onload期间可以通过JavaScript打印其内容(尽管记住CORS).

简而言之,最终用户必须能够通过在浏览器的地址栏中输入其URL来下载所需的PDF文件.您当然可以使用GET请求查询字符串来指定参数,从而允许更多动态性.如果它是"非常大"的数据,那么您总是可以让JSF将它放在HTTP会话或DB中,然后将唯一标识符作为请求参数传递,以便servlet可以从相同的HTTP会话或DB中获取它.

虽然可能存在一些令人讨厌的 黑客攻击,但JSF支持bean对于无意义地提供非JSF响应的工作来说是不可取的.你最好使用一个"普通的vanilla" servlet.你最终会得到更简单的代码.这是一个这样的servlet的启动示例:

@WebServlet("/pdf")
public class PdfServlet extends HttpServlet {

    @Override    
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String foo = request.getParameter("foo");
        String bar = request.getParameter("bar");
        // ...

        // Now just use the same code as in your original bean *without* FacesContext.
        // Note that the HttpServletResponse is readily available as method argument!
        response.setContentType("application/pdf");
        // ...
    }

}
Run Code Online (Sandbox Code Playgroud)

通过此设置,它可用http://localhost:8080/context/pdf?foo=abc&bar=xyz.

一旦你使这个部分工作,你只需要在一个<iframe>使用JavaScript在其load事件期间打印自己的内容窗口的引用它.您可以在JSF页面中执行此操作,例如/pdf.xhtml:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
>
    <h:head> 
         <style>html, body { height: 100%; margin: 0; overflow: hidden; }</style>
    </h:head>
    <h:body>
        <iframe src="#{request.contextPath}/pdf?#{request.queryString}"
            width="100%" height="100%" onload="this.contentWindow.print()" />
    </h:body>
</html>
Run Code Online (Sandbox Code Playgroud)

您需要在JSF支持bean中执行的操作是将重定向发送到该页面,如果需要,请在请求查询字符串中使用参数(最终将使#{request.queryString}servlet可以通过它获取request.getParameter(...)).

这是一个启动示例:

<h:form target="_blank">
    <h:commandButton value="Generate report" action="#{bean.printPdf}" />
</h:form>
Run Code Online (Sandbox Code Playgroud)
public String printPdf() {
    // Prepare params here if necessary.
    String foo = "abc";
    String bar = "xyz";
    // ...

    return "/pdf?faces-redirect=true" 
        + "&foo=" + URLEncoder.encode(foo, "UTF-8")
        + "&bar=" + URLEncoder.encode(bar, "UTF-8");
}
Run Code Online (Sandbox Code Playgroud)


Ceb*_*nce 0

您无法直接从 JavaScript 打印 URL,只能打开现有页面文章和打印API的打印对话框。

PDF 在服务器上生成并发送到网络浏览器(作为单独的“页面”),浏览器必须决定如何处理它 - 通常会询问用户是否要显示或保存 PDF。

要“自动打印”(即打开打印对话框)HTML 页面,您只需使用以下内容:

window.onload = function() {
  window.print();
};
Run Code Online (Sandbox Code Playgroud)

但对于 PDF 则无法做到这一点,因为它不是 HTML 页面。

要“自动打印”HTML 页面以外的内容,您需要有一个网络浏览器插件来处理来自服务器的 PDF。

另一种可能性是编写一个 GreaseMonkey 用户脚本,该脚本会*.myserver.com/**.pdf对其做出反应并打印出来。注意:GreaseMonkey 是 Mozilla Firefox 插件。

重量级选项

您可以通过向服务器应用程序添加打印支持来完成您的任务。申请要求:

  • 它必须是一个内联网应用程序(在用户网络内自托管),
  • 管理员用户需要注册可通过 JSP 页面从服务器访问的网络打印机,
  • “打印对话框”页面,您可以在其中选择已注册的打印机,然后单击“打印”按钮发送“打印”请求,例如:

    /print?printer=printer1&doc=/reports/report1

我见过一个支持此功能的 Java Web 应用程序,但正如您所见,这不是一件容易的事。

@苏扬·西瓦古鲁纳坦

我尝试将p:printerp:media结合起来,方法是将p:printer演示页面上的图像替换为p:media演示页面中的 PDF 文件:

// Replace this line:
<img id="j_idt18:image" src="/showcase/images/nature1.jpg?pfdrid_c=true" alt="">

// With this one:
<object **id="j_idt18:image"** style="display:none;" type="application/pdf" data="/showcase/resources/other/guide.pdf?pfdrid_c=true">Undefined</object>
Run Code Online (Sandbox Code Playgroud)

当您单击“打印”按钮时,您会看到一个空白页。如果您省略style="display:none;"并保留 PDF,height="300px" width="100%"您将在页面打印预览上看到一个小矩形。

艾特

谢谢 BalusC 和 Sujan。我同意,可以选择在 PDF 中嵌入 JavaScript,但出于安全原因通常会禁用该选项。

我想最浏览器兼容和用户友好的方法是有一个专用的打印预览弹出窗口,其中iframe通过 GET 请求显示给定的 PDF 和一个打印按钮来调用 IFRAME 的contentWindow.print().

只打印文档而不让用户选择打印机并配置它通常是一个坏主意。