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

时间:2014-05-05 21:31:03

标签: jsf pdf primefaces printing jasper-reports

我目前正在浏览器的新标签页中打开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();
}

这是打开pdf文件的按钮:

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

我需要做什么?

3 个答案:

答案 0 :(得分:10)

使用JasperReports

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

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

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

  

打印PDF文档

     

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


没有JasperReports

如果您无法控制PDF的生成,因此无法操纵它来添加上述脚本,另一种方法是相应地更改所有Java / JSF代码,以便PDF文件可以有效地使用(即只有GET请求而不是POST请求才能提供PDF文件。这允许您将其嵌入<iframe>中,然后在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&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>

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

这是一个启动示例:

request.getParameter(...)
<h:form target="_blank">
    <h:commandButton value="Generate report" action="#{bean.printPdf}" />
</h:form>

答案 1 :(得分:0)

您无法直接从JavaScript打印网址,只能打开现有网页 - articleprint API的打印对话框。

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

To&#34;自动打印&#34; (即打开一个打印对话框)一个HTML页面你会有这样的东西:

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

但是,由于PDF不是HTML页面,因此无法完成。

To&#34;自动打印&#34;除了HTML页面之外,您还需要一个Web浏览器插件来处理来自服务器的PDF。

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

重量级选项

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

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

    /打印打印机= PRINTER1&安培; DOC = /报告/报告1

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

@Sujan Sivagurunathan

我尝试将 p:printer 演示页面上的图片替换为 p:media p:printer和p:media结合起来em>演示页面:

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

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

<强> Eidt

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

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

在不让用户选择打印机并进行配置的情况下打印文档通常是一个坏主意。

答案 2 :(得分:0)

为此目的,有一个<p:printer> 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>

注意:

<p:media>有一个打印按钮来打印显示的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