Help with code to print HTML in Java 5

[nobr]Hi,

The following code works and runs successfully..

However, the printing in Java 1.4.2_03 is better than Java 5 (latest version).

i.e in particular the characters are not monospaced compared with compiling with Java 1.4.2_03. e.g si so ss squashed together.

This issue does not seem to occur when running the same code in Java 1.4.2_03. (I haven't tried other 1.4.2 java versions).

Any help would be appreciated. We really need this working under Java 5 or bust.

Here is the complete listing ... PrintHtml.java (it uses the DocumentRenderer)

and following this is the input file.

[code]

import javax.swing.text.html.HTMLDocument;

import java.net.URL;

import java.net.MalformedURLException;

import java.io.IOException;

import java.io.DataInputStream;

import java.io.InputStream;

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import java.io.*;

import javax.swing.*;

import javax.swing.text.*;

import javax.swing.text.html.*;

import java.lang.reflect.*;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.Rectangle;

import java.awt.Shape;

import java.awt.print.PageFormat;

import java.awt.print.Printable;

import java.awt.print.PrinterException;

import java.awt.print.PrinterJob;

import javax.swing.JEditorPane;

import javax.swing.text.Document;

import javax.swing.text.PlainDocument;

import javax.swing.text.View;

import javax.swing.text.html.HTMLDocument;

import java.awt.*;

import javax.swing.*;

import java.awt.print.*;

import java.text.ParseException;

public class PrintHtml {

/**

* Utility helper to convert HTML Text to HTML Document.

* @param baseUrl URL to be used in order

* to resolve relative HTML references, in lieu of an

* HTML BASE tag. May be null, if not required or HTML

* BASE tag is to be used.

* @see jbox.view.jfx.JboxHtmlEditor

* @see jbox.utility.JboxPrint

* @see jbox.utility.JboxPrintUtil

*/

public static HTMLDocument htmlTextToHtmlDoc(String htmlText, URL baseUrl)

{

try

{

// JboxHtmlEditorKit editorKit = new JboxHtmlEditorKit();

HTMLEditorKit editorKit = new HTMLEditorKit();

HTMLDocument doc = (HTMLDocument)editorKit.createDefaultDocument();

if (baseUrl != null)

{

try

{

doc.setBase(baseUrl);

}

catch(Exception e)

{

//JboxTraceManager.trace(e);

}

}

StringReader reader = new StringReader(htmlText);

editorKit.read(reader, doc, 0);

return doc;

}

catch(Exception e)

{

//JboxTraceManager.trace(e);

return null;

}

}

public static void main(String[] args) {

System.out.println("printing...");

HTMLDocument x = new HTMLDocument();

DocumentRenderer invoice = new DocumentRenderer();

//invoice.setScaleWidthToFit(false);

String s = "";

try {

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("mark.html"));

InputStreamReader in = new InputStreamReader(bis , "ASCII");

StringWriter sw = new StringWriter();

while (true) {

int datum = in.read();

if (datum == -1) break;

sw.write(datum);

}

in.close();

s = sw.toString();

System.out.println("s="+s);

}

catch (IOException e) {

System.err.println(e);

}

HTMLDocument htmldoc = htmlTextToHtmlDoc(s, null);

invoice.print(htmldoc);

}

}

// the good old infamous DocumentRenderer.

/* Copyright 2002

Kei G. Gauthier

Suite 301

77 Winsor Street

Ludlow, MA 01056

*/

class DocumentRenderer implements Printable {

/* DocumentRenderer prints objects of type Document. Text attributes, including

fonts, color, and small icons, will be rendered to a printed page.

DocumentRenderer computes line breaks, paginates, and performs other

formatting.

An HTMLDocument is printed by sending it as an argument to the

print(HTMLDocument) method. A PlainDocument is printed the same way. Other

types of documents must be sent in a JEditorPane as an argument to the

print(JEditorPane) method. Printing Documents in this way will automatically

display a print dialog.

As objects which implement the Printable Interface, instances of the

DocumentRenderer class can also be used as the argument in the setPrintable

method of the PrinterJob class. Instead of using the print() methods

detailed above, a programmer may gain access to the formatting capabilities

of this class without using its print dialog by creating an instance of

DocumentRenderer and setting the document to be printed with the

setDocument() or setJEditorPane(). The Document may then be printed by

setting the instance of DocumentRenderer in any PrinterJob.

*/

protected int currentPage = -1;//Used to keep track of when

//the page to print changes.

protected JEditorPane jeditorPane;//Container to hold the

//Document. This object will

//be used to lay out the

//Document for printing.

protected double pageEndY = 0;//Location of the current page

//end.

protected double pageStartY = 0; //Location of the current page

//start.

protected boolean scaleWidthToFit = true;//boolean to allow control over

//whether pages too wide to fit

//on a page will be scaled.

/*The DocumentRenderer class uses pFormat and pJob in its methods. Note

that pFormat is not the variable name used by the print method of the

DocumentRenderer. Although it would always be expected to reference the

pFormat object, the print method gets its PageFormat as an argument.

*/

protected PageFormat pFormat;

protected PrinterJob pJob;

/* The constructor initializes the pFormat and PJob variables.

*/

public DocumentRenderer() {

pFormat = new PageFormat();

pJob = PrinterJob.getPrinterJob();

}

/* Method to get the current Document

*/

public Document getDocument() {

if (jeditorPane != null) return jeditorPane.getDocument();

else return null;

}

/* Method to get the current choice the width scaling option.

*/

public boolean getScaleWidthToFit() {

return scaleWidthToFit;

}

/* pageDialog() displays a page setup dialog.

*/

public void pageDialog() {

pFormat = pJob.pageDialog(pFormat);

}

/* The print method implements the Printable interface. Although Printables

may be called to render a page more than once, each page is painted in

order. We may, therefore, keep track of changes in the page being rendered

by setting the currentPage variable to equal the pageIndex, and then

comparing these variables on subsequent calls to this method. When the two

variables match, it means that the page is being rendered for the second or

third time. When the currentPage differs from the pageIndex, a new page is

being requested.

The highlights of the process used print a page are as follows:

I.The Graphics object is cast to a Graphics2D object to allow for

scaling.

II.The JEditorPane is laid out using the width of a printable page.

This will handle line breaks. If the JEditorPane cannot be sized at

the width of the graphics clip, scaling will be allowed.

III. The root view of the JEditorPane is obtained. By examining this root

view and all of its children, printView will be able to determine

the location of each printable element of the document.

IV.If the scaleWidthToFit option is chosen, a scaling ratio is

determined, and the graphics2D object is scaled.

V.The Graphics2D object is clipped to the size of the printable page.

VI.currentPage is checked to see if this is a new page to render. If so,

pageStartY and pageEndY are reset.

VII. To match the coordinates of the printable clip of graphics2D and the

allocation rectangle which will be used to lay out the views,

graphics2D is translated to begin at the printable X and Y

coordinates of the graphics clip.

VIII. An allocation Rectangle is created to represent the layout of the

Views.

The Printable Interface always prints the area indexed by reference

to the Graphics object. For instance, with a standard 8.5 x 11 inch

page with 1 inch margins the rectangle X = 72, Y = 72, Width = 468,

and Height = 648, the area 72, 72, 468, 648 will be painted regardless

of which page is actually being printed.

To align the allocation Rectangle with the graphics2D object two

things are done. The first step is to translate the X and Y

coordinates of the graphics2D object to begin at the X and Y

coordinates of the printable clip, see step VII. Next, when printing

other than the first page, the allocation rectangle must start laying

out in coordinates represented by negative numbers. After page one,

the beginning of the allocation is started at minus the page end of

the prior page. This moves the part which has already been rendered to

before the printable clip of the graphics2D object.

X.The printView method is called to paint the page. Its return value

will indicate if a page has been rendered.

Although public, print should not ordinarily be called by programs other

than PrinterJob.

*/

public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) {

double scale = 1.0;

Graphics2D graphics2D;

View rootView;

// I

graphics2D = (Graphics2D) graphics;

disableDoubleBuffering(jeditorPane);

// II

jeditorPane.setSize((int) pageFormat.getImageableWidth(),Integer.MAX_VALUE);

jeditorPane.validate();

// III

rootView = jeditorPane.getUI().getRootView(jeditorPane);

// IV

if ((scaleWidthToFit) && (jeditorPane.getMinimumSize().getWidth() >

pageFormat.getImageableWidth())) {

scale = pageFormat.getImageableWidth()/

jeditorPane.getMinimumSize().getWidth();

graphics2D.scale(scale,scale);

}

// V

graphics2D.setClip((int) (pageFormat.getImageableX()/scale),

(int) (pageFormat.getImageableY()/scale),

(int) (pageFormat.getImageableWidth()/scale),

(int) (pageFormat.getImageableHeight()/scale));

// VI

if (pageIndex > currentPage) {

currentPage = pageIndex;

pageStartY += pageEndY;

pageEndY = graphics2D.getClipBounds().getHeight();

}

// VII

graphics2D.translate(graphics2D.getClipBounds().getX(),

graphics2D.getClipBounds().getY());

// VIII

Rectangle allocation = new Rectangle(0,

(int) -pageStartY,

(int) (jeditorPane.getMinimumSize().getWidth()),

(int) (jeditorPane.getPreferredSize().getHeight()));

// X

if (printView(graphics2D,allocation,rootView)) {

return Printable.PAGE_EXISTS;

}

else {

pageStartY = 0;

pageEndY = 0;

currentPage = -1;

return Printable.NO_SUCH_PAGE;

}

}

/** The speed and quality of printing suffers dramatically if

* any of the containers have double buffering turned on.

* So this turns if off globally.

* @see enableDoubleBuffering

*/

public static void disableDoubleBuffering(Component c) {

RepaintManager currentManager = RepaintManager.currentManager(c);

currentManager.setDoubleBufferingEnabled(false);

}

/** Re-enables double buffering globally. */

public static void enableDoubleBuffering(Component c) {

RepaintManager currentManager = RepaintManager.currentManager(c);

currentManager.setDoubleBufferingEnabled(true);

}

/* print(HTMLDocument) is called to set an HTMLDocument for printing.

*/

public void print(HTMLDocument htmlDocument) {

setDocument(htmlDocument);

printDialog();

}

/* print(JEditorPane) prints a Document contained within a JEDitorPane.

*/

public void print(JEditorPane jedPane) {

setDocument(jedPane);

printDialog();

}

/* print(PlainDocument) is called to set a PlainDocument for printing.

*/

public void print(PlainDocument plainDocument) {

setDocument(plainDocument);

printDialog();

}

/* A protected method, printDialog(), displays the print dialog and initiates

printing in response to user input.

*/

protected void printDialog() {

if (pJob.printDialog()) {

pJob.setPrintable(this,pFormat);

try {

pJob.print();

}

catch (PrinterException printerException) {

pageStartY = 0;

pageEndY = 0;

currentPage = -1;

System.out.println("Error Printing Document");

}

}

}

/* printView is a recursive method which iterates through the tree structure

of the view sent to it. If the view sent to printView is a branch view,

that is one with children, the method calls itself on each of these

children. If the view is a leaf view, that is a view without children which

represents an actual piece of text to be painted, printView attempts to

render the view to the Graphics2D object.

I.When any view starts after the beginning of the current printable

page, this means that there are pages to print and the method sets

pageExists to true.

II.When a leaf view is taller than the printable area of a page, it

cannot, of course, be broken down to fit a single page. Such a View

will be printed whenever it intersects with the Graphics2D clip.

III. If a leaf view intersects the printable area of the graphics clip and

fits vertically within the printable area, it will be rendered.

IV.If a leaf view does not exceed the printable area of a page but does

not fit vertically within the Graphics2D clip of the current page, the

method records that this page should end at the start of the view.

This information is stored in pageEndY.

*/

protected boolean printView(Graphics2D graphics2D, Shape allocation,

View view) {

boolean pageExists = false;

Rectangle clipRectangle = graphics2D.getClipBounds();

Shape childAllocation;

View childView;

if (view.getViewCount() > 0 &&

!view.getElement().getName().equalsIgnoreCase("td")) {

for (int i = 0; i < view.getViewCount(); i++) {

childAllocation = view.getChildAllocation(i,allocation);

if (childAllocation != null) {

childView = view.getView(i);

if (printView(graphics2D,childAllocation,childView)) {

pageExists = true;

}

}

}

} else {

// I

if (allocation.getBounds().getMaxY() >= clipRectangle.getY()) {

pageExists = true;

// II

if ((allocation.getBounds().getHeight() > clipRectangle.getHeight()) &&

(allocation.intersects(clipRectangle))) {

view.paint(graphics2D,allocation);

} else {

// III

if (allocation.getBounds().getY() >= clipRectangle.getY()) {

if (allocation.getBounds().getMaxY() <= clipRectangle.getMaxY()) {

view.paint(graphics2D,allocation);

} else {

// IV

if (allocation.getBounds().getY() < pageEndY) {

pageEndY = allocation.getBounds().getY();

}

}

}

}

}

}

return pageExists;

}

/* Method to set the content type the JEditorPane.

*/

protected void setContentType(String type) {

jeditorPane.setContentType(type);

}

/* Method to set an HTMLDocument as the Document to print.

*/

public void setDocument(HTMLDocument htmlDocument) {

jeditorPane = new JEditorPane();

setDocument("text/html",htmlDocument);

}

/* Method to set the Document to print as the one contained in a JEditorPane.

This method is useful when Java does not provide direct access to a

particular Document type, such as a Rich Text Format document. With this

method such a document can be sent to the DocumentRenderer class enclosed

in a JEditorPane.

*/

public void setDocument(JEditorPane jedPane) {

jeditorPane = new JEditorPane();

setDocument(jedPane.getContentType(),jedPane.getDocument());

}

/* Method to set a PlainDocument as the Document to print.

*/

public void setDocument(PlainDocument plainDocument) {

jeditorPane = new JEditorPane();

setDocument("text/plain",plainDocument);

}

/* Method to set the content type and document of the JEditorPane.

*/

protected void setDocument(String type, Document document) {

setContentType(type);

jeditorPane.setDocument(document);

}

/* Method to set the current choice of the width scaling option.

*/

public void setScaleWidthToFit(boolean scaleWidth) {

scaleWidthToFit = scaleWidth;

}

}

{/code]

The sample input file is"mark.html":::

<html>

<head>

<style type="text/css">

<!--

ol { list-style-type: decimal; margin-top: 10; margin-left: 50; margin-bottom: 10 }

u { text-decoration: underline }

s { text-decoration: line-through }

p { font-weight: normal; font-size: medium; margin-top: 15 }

dd p { margin-top: 0; margin-left: 40; margin-bottom: 0 }

ol li p { margin-top: 0; margin-bottom: 0 }

address { color: blue; font-style: italic }

i { font-style: italic }

h6 { font-weight: bold; font-size: xx-small; margin-top: 10; margin-bottom: 10 }

h5 { font-weight: bold; font-size: x-small; margin-top: 10; margin-bottom: 10 }

h4 { font-weight: bold; font-size: small; margin-top: 10; margin-bottom: 10 }

h3 { font-weight: bold; font-size: medium; margin-top: 10; margin-bottom: 10 }

dir li p { margin-top: 0; margin-bottom: 0 }

h2 { font-weight: bold; font-size: large; margin-top: 10; margin-bottom: 10 }

b { font-weight: bold }

h1 { font-weight: bold; font-size: x-large; margin-top: 10; margin-bottom: 10 }

a { color: blue; text-decoration: underline }

ul li ul li ul li { margin-right: 0; margin-top: 0; margin-left: 0; margin-bottom: 0 }

menu { margin-top: 10; margin-left: 40; margin-bottom: 10 }

menu li p { margin-top: 0; margin-bottom: 0 }

table table { border-color: Gray; margin-right: 0; border-style: outset; margin-top: 0; margin-left: 0; margin-bottom: 0 }

sup { vertical-align: sup }

body { margin-right: 0; font-size: 14pt; font-family: SansSerif; color: black; margin-left: 0 }

ul li ul li ul { list-style-type: square; margin-left: 25 }

blockquote { margin-right: 35; margin-top: 5; margin-left: 35; margin-bottom: 5 }

samp { font-size: small; font-family: Monospaced }

cite { font-style: italic }

sub { vertical-align: sub }

em { font-style: italic }

table table table { border-color: Gray; margin-right: 0; border-style: outset; margin-top: 0; margin-left: 0; margin-bottom: 0 }

ul li p { margin-top: 0; margin-bottom: 0 }

ul li ul li { margin-right: 0; margin-top: 0; margin-left: 0; margin-bottom: 0 }

var { font-weight: bold; font-style: italic }

table { border-color: Gray; margin-right: 7; border-style: outset; margin-top: 7; margin-left: 7; margin-bottom: 17 }

dfn { font-style: italic }

menu li { margin-right: 0; margin-top: 0; margin-left: 0; margin-bottom: 0 }

strong { font-weight: bold }

ul { list-style-type: disc; margin-top: 10; margin-left: 50; margin-bottom: 10 }

center { text-align: center }

ul li ul { list-style-type: circle; margin-left: 25 }

kbd { font-size: small; font-family: Monospaced }

dir li { margin-right: 0; margin-top: 0; margin-left: 0; margin-bottom: 0 }

th p { font-weight: bold; padding-left: 2; padding-bottom: 3; padding-right: 2; margin-top: 0; padding-top: 3 }

ul li menu { list-style-type: circle; margin-left: 25 }

dt { margin-top: 0; margin-bottom: 0 }

ol li { margin-right: 0; margin-top: 0; margin-left: 0; margin-bottom: 0 }

li p { margin-top: 0; margin-bottom: 0 }

strike { text-decoration: line-through }

dl { margin-top: 10; margin-left: 10; margin-bottom: 10 }

tt { font-family: Monospaced }

ul li { margin-right: 0; margin-top: 0; margin-left: 0; margin-bottom: 0 }

dir { margin-top: 10; margin-left: 40; margin-bottom: 10 }

pre p { margin-top: 0 }

th { border-color: Gray; border-style: solid; padding-left: 3; padding-bottom: 3; padding-right: 1; padding-top: 1 }

pre { font-family: Monospaced; margin-top: 5; margin-bottom: 5 }

td { border-color: Gray; border-style: inset; padding-left: 3; padding-bottom: 3; padding-right: 1; padding-top: 1 }

td p { padding-left: 2; padding-bottom: 3; padding-right: 2; margin-top: 0; padding-top: 3 }

code { font-size: small; font-family: Monospaced }

small { font-size: x-small }

big { font-size: x-large }

-->

</style>

</head>

<body>

<p style="margin-top: 0">

<table width="500" cellspacing="20" border="1">

<tr>

<td height="330" valign="top">

<table border="0">

<tr>

<td>

<font size="2">This is to certify that [[Client Name]], born

on [[Client Date of Birth]], of [[Client Residential

Address]], was the holder of motor vehicle driver

licence number [[Client Licence Number]], first issued on

[[First Issue Date of Holding]] and expired on [[Holding

Expiry Date]].

</font>

</td>

</tr>

</table>

</td>

</tr>

</table>

<table width="500" border="2">

<tr>

<td>

<table width="480" border="0">

<tr>

<td align="right">

<font size="2">

<b>Fred Flintstone

Manager</b>

Records Services Division

State

Police

An authorised person for the purposes of the

Road Act 1986</font>

</td>

</tr>

<tr>

<td align="left">

<font size="2"><b>User ID: wzvqv7

Dated: 29 November 2006</b>

</font>

</td>

</tr>

</table>

</td>

</tr>

</table>

</body>

</html>[/nobr]

[23256 byte] By [mamabilea] at [2007-10-3 11:12:41]
# 1

1. Use Code tags

2. You posted more code than I am willing to look through (maybe someone else will).

3. I am not sure how this relates to Java2D. But then again I did not read through the code

I would suggest you create a Short,Self Contained, Compilable and Executable, Example Program.

zadoka at 2007-7-15 13:36:16 > top of Java-index,Security,Cryptography...
# 2

My apologies.

But I did include

tabs around the code.

Somehow -- when I posted it.. it just removes all the spaces. (and looks ugly).

I think you need the entire piece of code to understand the problem.

Anyway, I just tried Java 1.4.2_13 with the supplied code and works Ok.

I might try Java 6 -- to see if the issue goes away in this version.

But the rendering bit does seem to work in Java 5.

Regarding Java 2D .. it uses the Graphics2D for the printer rendering -- (refer to DocumentRenderer class.) ... The PrintHtml is simple, it reads an Html file called "mark.html"(which is also included) and then invokes the DocumentRenderer class to do the printing.

mamabilea at 2007-7-15 13:36:16 > top of Java-index,Security,Cryptography...
# 3

I have finally cracked it!!!!!!!!!!!!!!!!

The issue is definitely with Java Sun. "Uneven character spacing when printing JTextComponent"

It is raised on the http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6488219

And currently in OPEN state, and raised on 31 Oct 2006 and mentions it was caused by fix 4352983.

So where do we go from here. It's not good because I have tried all later version of the JVM and the issue is still there.

Why? Because it hasn't been fixed yet. Read the bug report above, as it gives more insight -- and mentions the workaround is NOT good for existing code.

So the way forward is to use an earlier version of the JVM 5.

I download the JVM version 1.5.0 (starting version) and works Ok... I would probably think version prior to 4352983 would be Ok too.

Please vote for this.... We have a workaround (use older version of the JVM).

So I am very happy.

mamabilea at 2007-7-15 13:36:16 > top of Java-index,Security,Cryptography...
# 4
Hi all,Can anyone help Me to create report with iReport/JasperReport for HTML field?I have tried but TextField with StyledText does not support table/style sheet.thanks
hengki.sa at 2007-7-15 13:36:16 > top of Java-index,Security,Cryptography...