Search This Blog

Friday, September 3, 2010

Printing in Vaadin

In one of my recent projects for one of our clients, I have used the Vaadin framework to develop the application. One of the requirements of this application was to print certain content. I basically evaluated three possible solutions to implement this functionality:

  1. Create a new window with content specifically designed for printing and use the JavaScript print() method to print the contents of this window;
  2. Create a PDF with the printable contents and open this PDF in a new browser window;
  3. Create a PDF with the printable contents and use the Embedded and Window components to display the PDF.

I found option 1 not compatible and consistent enough between browsers. To create a consistent look&feel of the printable contents I used PDF. One way to display this PDF to the end-user is to open a new browser window. Unfortunately this was hindered by popup blockers which is not acceptable to end-users of the application.

Option 3 involved using an Embedded component to display the PDF inside a native Vaadin popup window (which is a regular div). This way, the printable contents are always consistent (because PDF is used) and the popup with the PDF contents is not hindered by browser popup blockers because the popup is just a regular div. By using this method, the print window also integrated nicely with the visual appearance of the rest of the application. The following code demonstrates how to implement this functionality:
/**
* This class creates a PDF with the iText library. This class implements
* the StreamSource interface which defines the getStream method.
*/
public class Pdf implements StreamSource {
private final ByteArrayOutputStream os = new ByteArrayOutputStream();

public Pdf() {
Document document = null;

try {
document = new Document(PageSize.A4, 50, 50, 50, 50);
PdfWriter.getInstance(document, os);
document.open();

document.add(new Paragraph("This is some content for the sample PDF!"));
} catch (Exception e) {
e.printStackTrace();
} finally {
if (document != null) {
document.close();
}
}
}

@Override
public InputStream getStream() {
// Here we return the pdf contents as a byte-array
return new ByteArrayInputStream(os.toByteArray());
}
}


And the code for displaying a popup with the actual PDF viewer:
Window window = new Window();
((VerticalLayout) window.getContent()).setSizeFull();
window.setResizable(true);
window.setWidth("800");
window.setHeight("600");
window.center();
Embedded e = new Embedded();
e.setSizeFull();
e.setType(Embedded.TYPE_BROWSER);

// Here we create a new StreamResource which downloads our StreamSource,
// which is our pdf.
StreamResource resource = new StreamResource(new Pdf(), "test.pdf?" + System.currentTimeMillis(), this);
// Set the right mime type
resource.setMIMEType("application/pdf");

e.setSource(resource);
window.addComponent(e);
getMainWindow().addWindow(window);


A full, working example including PDF generation, can be found here. The example project demonstrates displaying the PDF with and without a popup.

What is your experience with printable content in a Vaadin application?

29 comments:

mstahv said...

Probably the best cross plaftform approach atm, especially if you have strict typography requirements or multipage documents. CSS Print Profile has been there for ages, but only Firefox supports it somehow. We used Vaadin layouts, Lables and SVG built by JFreeChart succesfully in one project, but there we could be sure the the system was only used with Firefox.

BostonQuad said...

This looks promising, but the full source code doesn't seem to be at the link you provide. Can you make sure it's posted, please?

Jamie Craane said...

The full link to the provided example is http://code.google.com/p/jc-examples/source/browse/#svn%2Ftrunk%2FVaadinSamples

Hope this helps.

BostonQuad said...

Yes, wonderful! The code works, and the example clears a lot up. Thanks! :-)

shadi card said...

This is very informative and examples clear a lot.

André Kampert said...
This comment has been removed by the author.
André Kampert said...

Good post Jamie, just what I needed.

Rodrigo said...

Hi, Jamie! I´ve tried with your sample and it´s works fully! However, I need to use my reports .jasper. Is It possible? Could you show us a little example using .jasper?

Thank you so much!

Rodrigo

Jamie Craane said...

Hi Rodrigo,

You should be able to use Jasper reports also. You just have to provide an OutputStream for the report. I think you can use the following code for this taken from http://www.theregister.co.uk/2006/10/24/jasperreports_tutorial/.

ByteArrayOutputStream output
= new ByteArrayOutputStream();
JasperExportManager.exportReportToPdfStream(print, output);

and then create an inputstream from this outputstream, the same as in my example when using iText.

Let me know if this helps.

Hidra said...

Does it work with Google App Engine?

Jamie Craane said...

I think this should also work on AppEngine. By looking at the JRE whitelist (http://code.google.com/appengine/docs/java/jrewhitelist.html) you can see that the ByteArrayIn* and OutputStreams are supported.

Just give it a try and let me know!

Rodrigo said...

OK, Jamie,
I´ll try to run your tip. Until now, I just managed to appear a blank window. I´ll keep trying. For now... Thank you so much (again) !

Rodrigo said...

Hi, Jamie!

It couldn't be the best way, but I did something like your example.
First, I generate a .PDF file from my .JASPER.

...
JasperPrint jp = JasperFillManager.fillReport(file, parameterMap, cx);
JasperExportManager.exportReportToPdfFile(jp, "C:/themes/repy.pdf");
...

After this, I used an instance of Embedded class to open the .PDF file.

public void imprimirExemplo() {

Window window = new Window();
((VerticalLayout) window.getContent()).setSizeFull();
window.setResizable(true);
window.setCaption("Exemplo PDF");
window.setWidth("800");
window.setHeight("600");
window.center();
StreamSource s = new StreamResource.StreamSource() {

@Override
public InputStream getStream() {
try {
File f = new File("C:/themes/repy.pdf");
FileInputStream fis = new FileInputStream(f);
return fis;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
};

StreamResource r = new StreamResource(s, "repy.pdf", mainLayout.getApplication());
Embedded e = new Embedded();
e.setSizeFull();
e.setType(Embedded.TYPE_BROWSER);
r.setMIMEType("application/pdf");

e.setSource(r);
window.addComponent(e);
getMainWindow().addWindow(window);
}


So (for now), it´s seems to be working like I want. Maybe, It´s could be help for someone!

Best regards!

Rodrigo

Shantanu said...

Wonderful and easy solution .
Thanks a lot.

Sergio said...
This comment has been removed by the author.
Sergio said...

Internet explorer bug: main window buttons disappear after closing popup window.. Is there any walk around this issue?

Thanks!

Jamie Craane said...

Hi Sergio,

What version of Internet Explorer and Vaadin are you using?

Regards,
Jamie

Sergio said...

IE - v9, vaadin - v6.7.1
After the closing popup window instead of main window you see white empty page. After refreshing the window appears...

Jamie Craane said...

Hi Sergio,

I tried to reproduce the issue but I am unable to. I used the following configuration:
- Windows 7 (Within VMWare Fusion on OSX Lion)
- IE9 (build: 9.0.8112.16421
- Vaadin 6.7.3 (Also tried with Vaadin 6.4).

Just to make sure: open the page, click on the 'PDF in popup' button and then close the popup? Then you see a blank page? In my case the main window (including the two buttons) remains visible.

Regards,
Jamie

Sergio said...

Hi Jamie,

Problem solved. It was something to do with IE version on my laptop. Works perfectly on other machines. Thank you for your help!

Regards,
Sergio

diyam said...

when add Arabic Paragraph does not appear in PDF ,
as example :
document.add(new Paragraph("لماذا"));
does need any extra code??

Jamie Craane said...

Hi diyam,

I think, in order for Arabic characters to work, you need to use unicode. See the following links for more information:

http://support.itextpdf.com/node/74
http://www.java2s.com/Code/Java/PDF-RTF/ArabicTextinPDF.htm

Hope this helps.

Regards,
Jamie

Basil.Bourque said...

I'm considering a different solution for my Vaadin app:

Use the Java implementation of the API for OpenOffice/LibreOffice to create or modify an ODF document. And use their feature to "Export to PDF" (or on a Mac use the Print-to-PDF that every app has).

Hugo said...

Very good post Jamie, thanks a lot....Do you know how can I print a complete layout (and all of its components) in pdf??...some sort of picture of the layout....

regards,

Hugo

Jamie Craane said...

Hi Hugo,

You may use something like html2canvas although I have never used it myself. The demo look promosing though. Be sure to test it in every browser you need to support. The link is this: http://html2canvas.hertzen.com/screenshots.html

If you come across other options please le me know.

Regards,
Jamie

Hugo said...

thank you for your answer Jamie...If I succeed I will let you know....

A.Tamás said...

Hi!

It was very helpful, but can you help me how create an UTF-8 PDF?

Thanks!

Jamie Craane said...

Hi,

Take a look here http://www.google.nl/url?sa=t&source=web&cd=1&ved=0CFAQFjAA&url=http%3A%2F%2Fitextpdf.com%2Fexamples%2Fiia.php%3Fid%3D198&ei=E0gpUIbjCcTssga33oHgAw&usg=AFQjCNG7TeYiLS6ufEIIW3UE-DrKg61neQ&sig2=CJN8LSCBIB522hJtmoWBhg

try specifying utf-8 in the createFont method.

Regards,
Jamie

Jet said...

this blog might be old but it just save my life... Thanks, especially to rodrigo.