LXFree Java PDF Output uses gnujpdf licensed under the GNU Lesser General Public License. http://gnujpdf.sourceforge.net/ by Peter Mount and Eric Z. Beard.
The open source code for gnujpdf is a Java library (gnu.jpdf.*). The terms of the LGPL require that this souce code is made available when software uses the library. You can download the open source code for the library from http://gnujpdf.sourceforge.net/.
The library was modified for use with LXFree Java by the addition of methods to the PDFGraphics class that help support rotated images and text. The source code for the library used by LXFree Java can be downloaded here. The modified library is also licensed for use under the GNU Lesser General Public License.
Source to LXFree Java Modifications to gnu.jpdf
//*************** The following methods were added by Claude Heintz dev@macluxpro.com on 10/26/2009 ***************//
public void writeSaveState() { //directly writes a save graphic state to the pdf stream
pw.print("q ");
}
public void writeRestoreState() { //directly writes a restore graphic state to the pdf stream
pw.print("Q ");
}
// The following methods write commands to the stream that transform the pdf coordinate space
// According to the PDF reference http://www.adobe.com/devnet/acrobat/pdfs/PDF32000_2008.pdf
// The correct sequence of transformations is: translate, rotate scale/skew
public void writeTranslate( double x, double y ) { //directly writes an append translate to transform matrix to the pdf stream
pw.print("1 0 0 1 "+df.format(x)+" "+df.format(y)+" cm ");
}
public void writeRotate( double r ) { //directly writes an append rotate to transform matrix to the pdf stream
double sr = Math.sin(r);
double cr = Math.cos(r);
pw.print("" + df.format(cr) + " " + df.format(sr) + " " + df.format(-sr) + " " + df.format(cr) + " 0 0 cm ");
}
public void writeScale (double w, double h ) { //directly writes an append scale to transform matrix to the pdf stream
pw.print(""+df.format(w)+" 0 0 "+df.format(h)+" 0 0 cm ");
}
// coordinate conversion methods
public double flipY(double y) { //pdf coordinates have y positive at the top of the axis, in java positive is bottom
return page.getDimension().getHeight()-y;
}
public Point2D.Double originWithRotation( double r ) { //calculates a rotated origin in pdf coordinates (use flipY to get java y coord.)
return new Point2D.Double(page.getDimension().getHeight() * Math.sin(r), page.getDimension().getHeight() * Math.cos(r));
}
//*************** The following method was added to PDFFont by Claude Heintz dev@macluxpro.com on 3/22/2011 ***************//
public static String removeWhiteSpace(String s) {
StringBuffer rs = new StringBuffer();
int n;
char cc;
for (n=0; n<s.length(); n++) {
cc= s.charAt(n);
if ( ! ( ( cc == ' ') || (cc == '\t') ) ) {
rs.append(cc);
}
}
return rs.toString();
}
//*************** The method PDFFont.write(OutputStream os) was modified by Claude Heintz dev@macluxpro.com on 3/22/2011 ***************//
...
os.write("\n/BaseFont ".getBytes());
os.write(font.getBytes());
...
***was changed to***
...
os.write("\n/BaseFont ".getBytes());
os.write(PDFFont.removeWhiteSpace(font).getBytes());
...
//*************** The method PDFGraphics.closeBlock() was modified by Claude Heintz dev@macluxpro.com on 6/23/13 ***************//
//change to public to allow external close of text block CH 6/23/13
public void closeBlock() {
closeBlock("S");
}
Examples showing use of the modifications
The folowing is the paint method from an Image class that supports rotated images. The method detects if the drawing is to an instance of PDFGraphics and modifies its behavior. This method DOES NOT deal with scaled images(?) where this.width() != this.image2draw().getWidth( [null/observer] ) or this.height() != this.image2draw().getHeight( [null/observer] ). This remains a problem to be solved.
public void draw(Graphics g) {
if ( this.image() != null ) {
Rectangle cr = g.getClipBounds();
if ( (cr == null ) || cr.intersects(this.drawBounds()) ) {
if ( this.drawFill() ) {
if ( rotation() == 0 ) { //simpler if not rotated!
g.drawImage(this.image2draw(), (int)this.getX(), (int)this.getY(), (int)this.width(), (int)this.height(), null);
} else {
float rx = this.width()/2;
float ry = this.height()/2;
if ( g instanceof PDFGraphics ) { //ROTATED DRAWING TO PDF
PDFGraphics pg = (PDFGraphics) g;
pg.writeSaveState();
Point2D.Double rref = pg.originWithRotation(Math.toRadians(this.rotation())); //calculate rotated origin
double tx = -rref.getX();
double ty = pg.flipY(rref.getY());
AffineTransform at = AffineTransform.getTranslateInstance( this.getX()+ rx, this.getY()+ry ); //calculate upper left corner
AffineTransform att = AffineTransform.getRotateInstance(Math.toRadians(this.rotation()));
at.concatenate(att);
Point2D.Double ptSrc = new Point2D.Double(-rx, -ry);
Point2D.Double ptDst = new Point2D.Double();
at.transform(ptSrc, ptDst);
tx += ptDst.getX(); //translate rotated origin to corner
ty -= ptDst.getY();
pg.writeTranslate( tx, ty );
pg.writeRotate(Math.toRadians(-this.rotation()));
pg.drawImage(this.image2draw(), (int) 0, (int) 0, (int)this.width(), (int)this.height(), null);//causes scale?? before draw object
pg.writeRestoreState();
} else { //REGULAR ROTATED DRAWING
AffineTransform savt = ((Graphics2D)g).getTransform();
AffineTransform at = AffineTransform.getTranslateInstance( this.getX()+ rx, this.getY()+ry );
AffineTransform att = AffineTransform.getRotateInstance(Math.toRadians(this.rotation()));
at.concatenate(att);
((Graphics2D)g).transform(at);
g.drawImage(this.image2draw(), (int) -rx, (int) -ry, (int)this.width(), (int)this.height(), null);
((Graphics2D)g).setTransform(savt);
}
}
}
}
}
}
The folowing is the paint method from an TextField class that supports rotated text. The method detects if the drawing is to an instance of PDFGraphics and modifies its behavior.
public void draw(Graphics g) {
Rectangle cr = g.getClipBounds();
if ( (cr == null) || cr.intersects(this.drawBounds())) {
if ( this.drawFill() ) {
g.setColor(this.fillColor());
((Graphics2D)g).fill(this.path());
}
if ( this.drawStroke() ) {
String txt = this.text();
if ( (txt != null) && ( txt.length()!=0) ) {
g.setColor(this.strokeColor());
g.setFont(this.font());
if ( rotation()==0 ) { //simpler if not rotated!
g.drawString(txt, (int)this.drawPoint().getX(), (int)this.drawPoint().getY());
if ( g instanceof PDFGraphics ) {
((PDFGraphics) g).closeBlock(); //added 6/23/13
}
} else {
if ( g instanceof PDFGraphics ) {
PDFGraphics pg = (PDFGraphics) g;
pg.writeSaveState();
Point2D.Double rref = pg.originWithRotation(Math.toRadians(this.rotation())); //calculate rotated origin
double tx = -rref.getX();
double ty = pg.flipY(rref.getY());
AffineTransform at = AffineTransform.getTranslateInstance( this.drawPoint().getX(), this.drawPoint().getY() ); //calculate draw point
AffineTransform att = AffineTransform.getRotateInstance(Math.toRadians(this.rotation()));
at.concatenate(att);
Point2D.Double ptSrc = new Point2D.Double(0, 0);
Point2D.Double ptDst = new Point2D.Double();
at.transform(ptSrc, ptDst);
tx += ptDst.getX(); //translate rotated origin to point
ty -= ptDst.getY();
pg.writeTranslate( tx, ty );
pg.writeRotate(Math.toRadians(-this.rotation()));
pg.drawString(this.text(), 0, 0);
pg.writeRestoreState();
} else {
AffineTransform savt = ((Graphics2D)g).getTransform();
AffineTransform at = AffineTransform.getTranslateInstance( this.drawPoint().getX(), this.drawPoint().getY() );
((Graphics2D)g).transform(at);
at = AffineTransform.getRotateInstance(Math.toRadians(this.rotation()));
((Graphics2D)g).transform(at);
g.drawString(this.text(), 0, 0);
((Graphics2D)g).setTransform(savt);
}
}
}
}
}
}
Modification to PDFGraphics.java class
//============ Clipping operations =======================
// static method added 1/13/12 by Claude Heintz dev@macluxpro.com
// returns a clipped copy of an image suitable for drawImage methods that support destination/source
// eg. drawImage(Image img,int dx1,int dy1,int dx2, int dy2,int sx1,int sy1,int sx2,int sy2, ImageObserver obs)
public static BufferedImage cropImage(Image img, int dw, int dh, int sx1, int sy1, int sx2, int sy2) {
BufferedImage croppedimage = new BufferedImage( dw, dh, BufferedImage.TYPE_INT_ARGB );
Graphics2D rg = croppedimage.createGraphics();
rg.drawImage(img, 0, 0, dw, dh, sx1, sy1, sx2, sy2, null);
rg.dispose();
return croppedimage;
}
/**
* Draw's an image onto the page, with scaling
*
* @param img The java.awt.Image
* @param dx1 coordinate on page
* @param dy1 coordinate on page
* @param dx2 coordinate on page
* @param dy2 coordinate on page
* @param sx1 coordinate on image
* @param sy1 coordinate on image
* @param sx2 coordinate on image
* @param sy2 coordinate on image
* @param obs ImageObserver
* @return true if drawn
*/
public boolean drawImage(Image img,int dx1,int dy1,int dx2,
int dy2,int sx1,int sy1,int sx2,int sy2,
ImageObserver obs) {
Image ci = PDFGraphics.cropImage(img, dx2-dx1, dy2-dy1, sx1, sy1, sx2, sy2);
return drawImage(ci, dx1, dy1, (int) ci.getWidth(obs), (int) ci.getHeight(obs), null);
}