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

Posted

技术标签:

【中文标题】提供 PDF 下载后自动打开打印机对话框【英文标题】:Automatically open the printer dialog after providing PDF download 【发布时间】:2014-06-22 07:59:09 【问题描述】:

我目前正在浏览器的新选项卡中打开一个 pdf 文件,但我需要知道如何在按下命令按钮后打开打印机对话框来打印 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();

这是打开 pdf 文件的按钮:

<p:commandButton action="#sampleBB.printJasper"
    ajax="false" onclick="this.form.target='_blank'"
    value="#msg['generate.report']" />

我需要做什么?

【问题讨论】:

【参考方案1】:

使用 JasperReports

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

exporter.setParameter(JRPdfExporterParameter.PDF_javascript, "this.print();");

这基本上指示 Adob​​e Acrobat 在打开 PDF 时执行脚本 this.print()。另见Adobe Acrobat Scripting Guide 的第 79-80 页。以下是相关性摘录:

打印 PDF 文档

可以使用 Acrobat JavaScript 指定是否将 PDF 文档发送到 打印机或 PostScript 文件。无论哪种情况,要打印 PDF 文档,请调用 doc 对象的print 方法。 [...]


没有 JasperReports

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

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

虽然可能有一些 nasty hacks,但 JSF 支持 bean 根本不适合幂等地服务于非 JSF 响应的工作。为此,您最好使用“普通香草”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");
        // ...
    


通过此设置,http://localhost:8080/context/pdf?foo=abc&amp;bar=xyz 可以使用它。

一旦你让那部分工作,那么你只需要在&lt;iframe&gt; 中引用它,它使用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"
              onload="this.contentWindow.print()" />
    </h:body>
</html>

您需要在 JSF 支持 bean 中做的所有事情就是发送重定向到该页面,如有必要,请求查询字符串中的参数(最终将在 #request.queryString 中,以便 servlet 可以通过 request.getParameter(...) 获取它们)。

这是一个启动示例:

<h:form target="_blank">
    <h:commandButton value="Generate report" action="#bean.printPdf" />
</h:form>
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");

【讨论】:

【参考方案2】:

为此目的,有一个&lt;p:printer&gt; Primefaces 的组件。

类似的方法可能有效,但未经测试。

<h:form>
    <h:commandButton value="Print" type="button" icon="ui-icon-print">
        <p:printer target="pdf" />
    </h:commandButton>

    <p:media style="display:none;" id="pdf" value="/aPDF.pdf" />
</h:form>

注意:

&lt;p:media&gt; 确实有一个打印按钮来打印显示的 pdf。

编辑:

您必须将 pdf 文件嵌入 iframe 并在其上使用 JavaScript print() 函数,或者您可以在 PDF 本身中激活自动打印功能。但这绝对是可能的。

See this question on SO : Can a PDF file's print dialog be opened with Javascript?

How to Use JavaScript to Print a PDF

【讨论】:

根据documentation,它旨在打印活动 HTML 页面的部分/小部件/DIV,而不是打印不同的 URL。 错过了p:media 部分。如果我理解正确,您正在嵌入一个不可见的例如iframe 里面有 PDF。这行得通吗?【参考方案3】:

您不能直接从 JavaScript 打印 URL,您只能打开 现有页面 - article 和 print API 的打印对话框。

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

要“自动打印”(即打开一个打印对话框)一个 HTML 页面,你会得到这样的东西:

window.onload = function() 
  window.print();
;

但这不能用于 PDF,因为它不是 HTML 页面。

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

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

重量级选项

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

它必须是 Intranet 应用程序(在用户网络中自托管), 管理员用户需要注册可通过 JSP 页面从服务器访问的网络打印机,

“打印对话框”页面,您可以在其中选择已注册的打印机,然后单击“打印”按钮发送“打印”请求,例如:

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

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

@Sujan Sivagurunathan

我尝试通过将 p:printer 演示页面上的图像替换为 p:media 演示页面中的 PDF 文件来组合 p:printer 和 p:media :

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

// 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>

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

开斋节

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

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

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

【讨论】:

据我了解,OP 只想打开打印对话框,而不是立即打印。

以上是关于提供 PDF 下载后自动打开打印机对话框的主要内容,如果未能解决你的问题,请参考以下文章

直接从 JavaScript 打印 PDF

打印对话框关闭后自动关闭窗口

在线pdf怎样转换为excel文件

mPDF 自动打印问题

可以使用 Javascript 打开 PDF 文件的打印对话框吗?

cad如何批量打印?