Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
972 views
in Technique[技术] by (71.8m points)

jsf - Automatically open the printer dialog after providing PDF download

I am currently opening a pdf file in a new tab in my browser but I need to know how to open a printer dialog to print the pdf jasper report after pressing a commandButton

This is the method that open the pdf in a new tab:

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();
}

This is the button that open the pdf file:

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

What I need to do?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

With JasperReports

When using JasperReports, simply add this parameter to JasperReports exporter:

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

This basically instructs Adobe Acrobat to execute the script this.print() when opening the PDF. See also page 79-80 of Adobe Acrobat Scripting Guide. Below is an extract of relevance:

Printing PDF Documents

It is possible to use Acrobat JavaScript to specify whether a PDF document is sent to a printer or to a PostScript file. In either case, to print a PDF document, invoke the doc object’s print method. [...]


Without JasperReports

If you don't have control over generation of PDFs and thus can't manipulate it to add the mentioned script, an alternative is to change all the Java/JSF code accordingly so that the PDF file is idempotently available (i.e. the PDF file must be available by just a GET request rather than a POST request). This allows you to embed it in an <iframe> for which it's in turn possible to print its content by JavaScript during onload (keep CORS in mind though).

Simply put, the enduser must be able to download the desired PDF file by just entering its URL in browser's address bar. You can of course make use of GET request query string to specify parameters, allowing a bit more dynamicness. If it's "very large" data, then you can always let JSF put it in the HTTP session or DB and then pass an unique identifier around as request parameter so that the servlet can in turn obtain it from the very same HTTP session or DB.

Whilst possible with some nasty hacks, a JSF backing bean is simply insuitable for the job of idempotently serving a non-JSF response. You'd better use a "plain vanilla" servlet for this. You'll end up with much simpler code. Here's a kickoff example of such a 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");
        // ...
    }

}

With this setup, it's available by http://localhost:8080/context/pdf?foo=abc&bar=xyz.

Once you get that part to work, then you just have to reference it in an <iframe> which uses JavaScript to print its own content window during its load event. You can do this in a JSF page, e.g. /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>

All you need to do in your JSF backing bean is to send a redirect to that page, if necessary with parameters in request query string (which will end up in #{request.queryString} so that the servlet can obtain them via request.getParameter(...)).

Here's a kickoff example:

<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");
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...