Java PrintJob to DocPrintJob Not Working? - java

I have a class that extends from Printable, it prints fine using the standard PrintJob, but i would like to move to DocPrintJob so i can listen to the status of the print (successful print etc).
My current code looks like this to create a print job and print
// define printer
AttributeSet attributes = new HashAttributeSet();
attributes.add(new Copies(1));
// get printerJob
PrinterJob printJob = PrinterJob.getPrinterJob();
PageFormat pageFormat = printJob.defaultPage();
// sizing (standard is 72dpi, so multiple inches by this)
Double height = 8d * 72d;
Double width = 3d * 72d;
Double margin = 0.1d * 72d;
// set page size
Paper paper = pageFormat.getPaper();
paper.setSize(width, height);
paper.setImageableArea(margin, margin, width - (margin * 2), height - (margin * 2));
// set orientation and paper
pageFormat.setOrientation(PageFormat.PORTRAIT);
pageFormat.setPaper(paper);
// create a book for printing
Book book = new Book();
book.append(new EventPrint(args.getEvent()), pageFormat);
// set book to what we are printing
printJob.setPageable(book);
// set print request attributes
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
aset.add(OrientationRequested.PORTRAIT);
// now print
try {
printJob.print();
} catch (PrinterException e) {
e.printStackTrace();
}
Now to convert this to a DocPrintJob, i followed this link which told me how to convert from PrintJob to DocPrintJob. So now my code became this
PrintService[] services = PrinterJob.lookupPrintServices(); //list of printers
PrintService printService = services[0];
DocFlavor[] flavours = printService.getSupportedDocFlavors();
// may need to determine which printer to use
DocPrintJob printJob = printService.createPrintJob();
// get out printable
EventPrint eventPrint = new EventPrint(args.getEvent());
// convert printable to doc
Doc doc = new SimpleDoc(eventPrint, DocFlavor.SERVICE_FORMATTED.PRINTABLE, null);
// add eventlistener to printJob
printJob.addPrintJobListener(new PrintJobListener() {
#Override
public void printDataTransferCompleted(PrintJobEvent pje) {
Console.Log("Print data sent successfully");
}
#Override
public void printJobCompleted(PrintJobEvent pje) {
Console.Log("Print successful");
}
#Override
public void printJobFailed(PrintJobEvent pje) {
Console.Log("Print failed");
}
#Override
public void printJobCanceled(PrintJobEvent pje) {
Console.Log("Print cancelled");
}
#Override
public void printJobNoMoreEvents(PrintJobEvent pje) {
Console.Log("No more printJob events");
}
#Override
public void printJobRequiresAttention(PrintJobEvent pje) {
Console.Log("printJob requires attention");
}
});
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
aset.add(new Copies(1));
try {
printJob.print(doc, aset);
} catch (Exception e) {
}
Now for some reason, my print just keeps executing (400+ times until i close application). I am not sure if it is because maybe i have not set the paper size, like i did when i was using PrintJob? Would that cause it? If so, how can i set the paperSize of the DocPrintJob as it doesnt have the methods the normal printJob has?
Anyone else faces this problem before?
EDIT: Here is my eventPrint class and the class it inherits from
PRINTABLEBASE.JAVA
public class PrintableBase {
public Graphics2D g2d;
public float x, y, imageHeight, imageWidth, maxY;
public enum Alignment { LEFT, RIGHT, CENTER };
public void printSetup(Graphics graphics, PageFormat pageFormat) {
// user (0,0) is typically outside the imageable area, so we must translate
// by the X and Y values in the pageFormat to avoid clipping
g2d = (Graphics2D) graphics;
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
// get starter X and Y
x = 0;//(float)pageFormat.getImageableX();
// do not offset vertical as pushes ticket down too much
y = 0;//(float)pageFormat.getImageableY();
// get height and width of the printable image
imageWidth = (float)pageFormat.getImageableWidth();
imageHeight = (float)pageFormat.getImageableHeight();
// maximum that we can go vertically
maxY = y;
Console.Log("imageWidth:" + imageWidth);
Console.Log("imageHeight: " + imageHeight);
Console.Log("X: " + x);
Console.Log("Y: " + y);
}
public void setFont(Font font) {
g2d.setFont(font);
}
public float addSeparatorLine(float y, float imageWidth) {
String fontName = g2d.getFont().getName();
int fontStyle = g2d.getFont().getStyle();
int fontSize = g2d.getFont().getSize();
g2d.setFont(new Font("Arial", Font.PLAIN, 10));
y = drawFirstLine(g2d, "---------------------------------------------------------------------------------------", imageWidth, 0, y, Alignment.LEFT);
// revery font
g2d.setFont(new Font(fontName, fontStyle, fontSize));
return y + 5;
}
public BufferedImage scaleImage(BufferedImage sbi, int dWidth, int dHeight) {
BufferedImage dbi = null;
if(sbi != null) {
// calculate ratio between standard size and scaled
double wRatio = (double)dWidth / (double)sbi.getWidth();
double hRatio = (double)dHeight / (double)sbi.getHeight();
// use wRatio by default
double sWidth = (double)sbi.getWidth() * wRatio;
double sHeight = (double)sbi.getHeight() * wRatio;
// if hRation is small, use that, as image will be reduced by more
if (hRatio < wRatio) {
sWidth = (double)sbi.getWidth() * hRatio;
sHeight = (double)sbi.getHeight() * hRatio;
}
double sRatio = (wRatio < hRatio) ? wRatio : hRatio;
// now create graphic, of new size
dbi = new BufferedImage((int)sWidth, (int)sHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = dbi.createGraphics();
AffineTransform at = AffineTransform.getScaleInstance(sRatio, sRatio);
g.drawRenderedImage(sbi, at);
}
return dbi;
}
// This function will only add the first line of text and will not wrap
// useful for adding the ticket title.
// Returns the height of the text
public float drawFirstLine(Graphics2D g2, String text, float width, float x, float y, Alignment alignment) {
AttributedString attstring = new AttributedString(text);
attstring.addAttribute(TextAttribute.FONT, g2.getFont());
AttributedCharacterIterator paragraph = attstring.getIterator();
int paragraphStart = paragraph.getBeginIndex();
int paragraphEnd = paragraph.getEndIndex();
FontRenderContext frc = g2.getFontRenderContext();
LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc);
// Set break width to width of Component.
float breakWidth = width;
float drawPosY = y;
// Set position to the index of the first character in the paragraph.
lineMeasurer.setPosition(paragraphStart);
// Get lines until the entire paragraph has been displayed.
if (lineMeasurer.getPosition() < paragraphEnd) {
// Retrieve next layout. A cleverer program would also cache
// these layouts until the component is re-sized.
TextLayout layout = lineMeasurer.nextLayout(breakWidth);
// Compute pen x position.
float drawPosX;
switch (alignment){
case RIGHT:
drawPosX = (float) x + breakWidth - layout.getAdvance();
break;
case CENTER:
drawPosX = (float) x + (breakWidth - layout.getAdvance())/2;
break;
default:
drawPosX = (float) x;
}
// Move y-coordinate by the ascent of the layout.
drawPosY += layout.getAscent();
// Draw the TextLayout at (drawPosX, drawPosY).
layout.draw(g2, drawPosX, drawPosY);
// Move y-coordinate in preparation for next layout.
drawPosY += layout.getDescent() + layout.getLeading();
}
return drawPosY;
}
/**
* Draw paragraph.
*
* #param g2 Drawing graphic.
* #param text String to draw.
* #param width Paragraph's desired width.
* #param x Start paragraph's X-Position.
* #param y Start paragraph's Y-Position.
* #param dir Paragraph's alignment.
* #return Next line Y-position to write to.
*/
protected float drawParagraph (String text, float width, float x, float y, Alignment alignment){
AttributedString attstring = new AttributedString(text);
attstring.addAttribute(TextAttribute.FONT, g2d.getFont());
AttributedCharacterIterator paragraph = attstring.getIterator();
int paragraphStart = paragraph.getBeginIndex();
int paragraphEnd = paragraph.getEndIndex();
FontRenderContext frc = g2d.getFontRenderContext();
LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc);
// Set break width to width of Component.
float breakWidth = width;
float drawPosY = y;
// Set position to the index of the first character in the paragraph.
lineMeasurer.setPosition(paragraphStart);
// Get lines until the entire paragraph has been displayed.
while (lineMeasurer.getPosition() < paragraphEnd) {
// Retrieve next layout. A cleverer program would also cache
// these layouts until the component is re-sized.
TextLayout layout = lineMeasurer.nextLayout(breakWidth);
// Compute pen x position.
float drawPosX;
switch (alignment){
case RIGHT:
drawPosX = (float) x + breakWidth - layout.getAdvance();
break;
case CENTER:
drawPosX = (float) x + (breakWidth - layout.getAdvance())/2;
break;
default:
drawPosX = (float) x;
}
// Move y-coordinate by the ascent of the layout.
drawPosY += layout.getAscent();
// Draw the TextLayout at (drawPosX, drawPosY).
layout.draw(g2d, drawPosX, drawPosY);
// Move y-coordinate in preparation for next layout.
drawPosY += layout.getDescent() + layout.getLeading();
}
return drawPosY;
}
}
EVENTPRINT.JAVA
public class EventPrint extends PrintableBase implements Printable {
private Event event;
public EventPrint(Event event) {
this.event = event;
}
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
throws PrinterException {
// setup
super.printSetup(graphics, pageFormat);
// title
super.setFont(new Font("Tahoma", Font.BOLD, 16));
y = super.drawParagraph(event.getTitle(), imageWidth, x, y, Alignment.LEFT);
RETURN PAGE_EXISTS;
}

I would say that your problem is that you never tell the API that there are no more pages....
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
throws PrinterException {
// setup
super.printSetup(graphics, pageFormat);
// title
super.setFont(new Font("Tahoma", Font.BOLD, 16));
y = super.drawParagraph(event.getTitle(), imageWidth, x, y, Alignment.LEFT);
RETURN PAGE_EXISTS;
}
So, assuming you only want to print a single page, you could use something like...
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
throws PrinterException {
int result = NO_SUCH_PAGE;
if (pageIndex == 0) {
// setup
super.printSetup(graphics, pageFormat);
// title
super.setFont(new Font("Tahoma", Font.BOLD, 16));
y = super.drawParagraph(event.getTitle(), imageWidth, x, y, Alignment.LEFT);
result = PAGE_EXISTS;
}
RETURN result;
}
Otherwise the API doesn't know when to stop printing.
Bookable uses a different approach, in that each page of the book is only printed once and it will continue until all pages are printed. Printable is different, it will continue printing until you tell it to stop
Take a look at A Basic Printing Program for more details

I also encountered some issues when switching from PrinterJob to DocPrintJob. Your code seems fine to me, so maybe you are right with the page size. You can set the page size like this:
aset.add(new MediaPrintableArea(0, 0, width, height, MediaPrintableArea.MM));
But it depends on your printable object how to set this. I have created a bytearray (PDF) with iText and set the page size there. Then, I set the width and height in the above code to Integer.MAX_VALUE.
I can post my entire code when I get home. Hope it helps!

Related

How to determine remaining space on a pdf page with itext [duplicate]

I have a pdf file where-in I am adding a stamp to all it's pages.
But, the problem is, the stamp is added to the upper-left corner of each page. If, the page has text in that part, the stamp appears on the text.
My question is, is there any method by which I can read each page and if there is no text in that part add the stamp else search for nearest available free space, just like what a density scanner does?
I am using IText and Java 1.7.
The free space fider class and the distance calculation function are the same that is there in the accepted answer.
Following is the edited code I am using:
// The resulting PDF file
String RESULT = "K:\\DCIN_TER\\DCIN_EPU2\\CIRCUIT FROM BRANCH\\RAINBOW ORDERS\\" + jtfSONo.getText().trim() + "\\PADR Release\\Final PADR Release 1.pdf";
// Create a reader
PdfReader reader = new PdfReader("K:\\DCIN_TER\\DCIN_EPU2\\CIRCUIT FROM BRANCH\\RAINBOW ORDERS\\" + jtfSONo.getText().trim() + "\\PADR Release\\Final PADR Release.pdf");
// Create a stamper
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(RESULT));
// Loop over the pages and add a footer to each page
int n = reader.getNumberOfPages();
for(int i = 1; i <= n; i++)
{
Collection<Rectangle2D> rectangles = find(reader, 300, 100, n, stamper); // minimum width & height of a rectangle
Iterator itr = rectangles.iterator();
while(itr.hasNext())
{
System.out.println(itr.next());
}
if(!(rectangles.isEmpty()) && (rectangles.size() != 0))
{
Rectangle2D best = null;
double bestDist = Double.MAX_VALUE;
Point2D.Double point = new Point2D.Double(200, 400);
float x = 0, y = 0;
for(Rectangle2D rectangle: rectangles)
{
double distance = distance(rectangle, point);
if(distance < bestDist)
{
best = rectangle;
bestDist = distance;
x = (float) best.getX();
y = (float) best.getY();
int left = (int) best.getMinX();
int right = (int) best.getMaxX();
int top = (int) best.getMaxY();
int bottom = (int) best.getMinY();
System.out.println("x : " + x);
System.out.println("y : " + y);
System.out.println("left : " + left);
System.out.println("right : " + right);
System.out.println("top : " + top);
System.out.println("bottom : " + bottom);
}
}
getFooterTable(i, n).writeSelectedRows(0, -1, x, y, stamper.getOverContent(i)); // 0, -1 indicates 1st row, 1st column upto last row and last column
}
else
getFooterTable(i, n).writeSelectedRows(0, -1, 94, 140, stamper.getOverContent(i)); // bottom left corner
}
// Close the stamper
stamper.close();
// Close the reader
reader.close();
public Collection<Rectangle2D> find(PdfReader reader, float minWidth, float minHeight, int page, PdfStamper stamper) throws IOException
{
Rectangle cropBox = reader.getCropBox(page);
Rectangle2D crop = new Rectangle2D.Float(cropBox.getLeft(), cropBox.getBottom(), cropBox.getWidth(), cropBox.getHeight());
FreeSpaceFinder finder = new FreeSpaceFinder(crop, minWidth, minHeight);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
parser.processContent(page, finder);
System.out.println("finder.freeSpaces : " + finder.freeSpaces);
return finder.freeSpaces;
}
// Create a table with page X of Y, #param x the page number, #param y the total number of pages, #return a table that can be used as footer
public static PdfPTable getFooterTable(int x, int y)
{
java.util.Date date = new java.util.Date();
SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy");
String month = sdf.format(date);
System.out.println("Month : " + month);
PdfPTable table = new PdfPTable(1);
table.setTotalWidth(120);
table.setLockedWidth(true);
table.getDefaultCell().setFixedHeight(20);
table.getDefaultCell().setBorder(Rectangle.TOP);
table.getDefaultCell().setBorder(Rectangle.LEFT);
table.getDefaultCell().setBorder(Rectangle.RIGHT);
table.getDefaultCell().setBorderColorTop(BaseColor.BLUE);
table.getDefaultCell().setBorderColorLeft(BaseColor.BLUE);
table.getDefaultCell().setBorderColorRight(BaseColor.BLUE);
table.getDefaultCell().setBorderWidthTop(1f);
table.getDefaultCell().setBorderWidthLeft(1f);
table.getDefaultCell().setBorderWidthRight(1f);
table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
Font font1 = new Font(FontFamily.HELVETICA, 10, Font.BOLD, BaseColor.BLUE);
table.addCell(new Phrase("CONTROLLED COPY", font1));
table.getDefaultCell().setFixedHeight(20);
table.getDefaultCell().setBorder(Rectangle.LEFT);
table.getDefaultCell().setBorder(Rectangle.RIGHT);
table.getDefaultCell().setBorderColorLeft(BaseColor.BLUE);
table.getDefaultCell().setBorderColorRight(BaseColor.BLUE);
table.getDefaultCell().setBorderWidthLeft(1f);
table.getDefaultCell().setBorderWidthRight(1f);
table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
Font font = new Font(FontFamily.HELVETICA, 10, Font.BOLD, BaseColor.RED);
table.addCell(new Phrase(month, font));
table.getDefaultCell().setFixedHeight(20);
table.getDefaultCell().setBorder(Rectangle.LEFT);
table.getDefaultCell().setBorder(Rectangle.RIGHT);
table.getDefaultCell().setBorder(Rectangle.BOTTOM);
table.getDefaultCell().setBorderColorLeft(BaseColor.BLUE);
table.getDefaultCell().setBorderColorRight(BaseColor.BLUE);
table.getDefaultCell().setBorderColorBottom(BaseColor.BLUE);
table.getDefaultCell().setBorderWidthLeft(1f);
table.getDefaultCell().setBorderWidthRight(1f);
table.getDefaultCell().setBorderWidthBottom(1f);
table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(new Phrase("BLR DESIGN DEPT.", font1));
return table;
}
is there any method by which I can read each page and if there is no text in that part add the stamp else search for nearest available free space, just like what a density scanner does?
iText does not offer that functionality out of the box. Depending of what kind of content you want to evade, though, you might consider either rendering the page to an image and looking for white spots in the image or doing text extraction with a strategy that tries to find locations without text.
The first alternative, analyzing a rendered version of the page, would be the focus of a separate question as an image processing library would have to be chosen first.
There are a number of situations, though, in which that first alternative is not the best way to go. E.g. if you only want to evade text but not necessarily graphics (like watermarks), or if you also want to evade invisible text (which usually can be marked in a PDF viewer and, therefore, interfere with your addition).
The second alternative (using text and image extraction abilities of iText) can be the more appropriate approach in such situations.
Here a sample RenderListener for such a task:
public class FreeSpaceFinder implements RenderListener
{
//
// constructors
//
public FreeSpaceFinder(Rectangle2D initialBox, float minWidth, float minHeight)
{
this(Collections.singleton(initialBox), minWidth, minHeight);
}
public FreeSpaceFinder(Collection<Rectangle2D> initialBoxes, float minWidth, float minHeight)
{
this.minWidth = minWidth;
this.minHeight = minHeight;
freeSpaces = initialBoxes;
}
//
// RenderListener implementation
//
#Override
public void renderText(TextRenderInfo renderInfo)
{
Rectangle2D usedSpace = renderInfo.getAscentLine().getBoundingRectange();
usedSpace.add(renderInfo.getDescentLine().getBoundingRectange());
remove(usedSpace);
}
#Override
public void renderImage(ImageRenderInfo renderInfo)
{
Matrix imageMatrix = renderInfo.getImageCTM();
Vector image00 = rect00.cross(imageMatrix);
Vector image01 = rect01.cross(imageMatrix);
Vector image10 = rect10.cross(imageMatrix);
Vector image11 = rect11.cross(imageMatrix);
Rectangle2D usedSpace = new Rectangle2D.Float(image00.get(Vector.I1), image00.get(Vector.I2), 0, 0);
usedSpace.add(image01.get(Vector.I1), image01.get(Vector.I2));
usedSpace.add(image10.get(Vector.I1), image10.get(Vector.I2));
usedSpace.add(image11.get(Vector.I1), image11.get(Vector.I2));
remove(usedSpace);
}
#Override
public void beginTextBlock() { }
#Override
public void endTextBlock() { }
//
// helpers
//
void remove(Rectangle2D usedSpace)
{
final double minX = usedSpace.getMinX();
final double maxX = usedSpace.getMaxX();
final double minY = usedSpace.getMinY();
final double maxY = usedSpace.getMaxY();
final Collection<Rectangle2D> newFreeSpaces = new ArrayList<Rectangle2D>();
for (Rectangle2D freeSpace: freeSpaces)
{
final Collection<Rectangle2D> newFragments = new ArrayList<Rectangle2D>();
if (freeSpace.intersectsLine(minX, minY, maxX, minY))
newFragments.add(new Rectangle2D.Double(freeSpace.getMinX(), freeSpace.getMinY(), freeSpace.getWidth(), minY-freeSpace.getMinY()));
if (freeSpace.intersectsLine(minX, maxY, maxX, maxY))
newFragments.add(new Rectangle2D.Double(freeSpace.getMinX(), maxY, freeSpace.getWidth(), freeSpace.getMaxY() - maxY));
if (freeSpace.intersectsLine(minX, minY, minX, maxY))
newFragments.add(new Rectangle2D.Double(freeSpace.getMinX(), freeSpace.getMinY(), minX - freeSpace.getMinX(), freeSpace.getHeight()));
if (freeSpace.intersectsLine(maxX, minY, maxX, maxY))
newFragments.add(new Rectangle2D.Double(maxX, freeSpace.getMinY(), freeSpace.getMaxX() - maxX, freeSpace.getHeight()));
if (newFragments.isEmpty())
{
add(newFreeSpaces, freeSpace);
}
else
{
for (Rectangle2D fragment: newFragments)
{
if (fragment.getHeight() >= minHeight && fragment.getWidth() >= minWidth)
{
add(newFreeSpaces, fragment);
}
}
}
}
freeSpaces = newFreeSpaces;
}
void add(Collection<Rectangle2D> rectangles, Rectangle2D addition)
{
final Collection<Rectangle2D> toRemove = new ArrayList<Rectangle2D>();
boolean isContained = false;
for (Rectangle2D rectangle: rectangles)
{
if (rectangle.contains(addition))
{
isContained = true;
break;
}
if (addition.contains(rectangle))
toRemove.add(rectangle);
}
rectangles.removeAll(toRemove);
if (!isContained)
rectangles.add(addition);
}
//
// members
//
public Collection<Rectangle2D> freeSpaces = null;
final float minWidth;
final float minHeight;
final static Vector rect00 = new Vector(0, 0, 1);
final static Vector rect01 = new Vector(0, 1, 1);
final static Vector rect10 = new Vector(1, 0, 1);
final static Vector rect11 = new Vector(1, 1, 1);
}
Using this FreeSpaceFinder you can find empty areas with given minimum dimensions in a method like this:
public Collection<Rectangle2D> find(PdfReader reader, float minWidth, float minHeight, int page) throws IOException
{
Rectangle cropBox = reader.getCropBox(page);
Rectangle2D crop = new Rectangle2D.Float(cropBox.getLeft(), cropBox.getBottom(), cropBox.getWidth(), cropBox.getHeight());
FreeSpaceFinder finder = new FreeSpaceFinder(crop, minWidth, minHeight);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
parser.processContent(page, finder);
return finder.freeSpaces;
}
For your task you now have to choose from the returned rectangles the one which suits you best.
Beware, this code still may have to be tuned to your requirements:
It ignores clip paths, rendering modes, colors, and covering objects. Thus, it considers all text and all bitmap images, whether they are actually visible or not.
It does not consider vector graphics (because the iText parser package does not consider them).
It is not very optimized.
Applied to this PDF page:
with minimum width 200 and height 50, you get these rectangles:
x y w h
000,000 000,000 595,000 056,423
000,000 074,423 595,000 168,681
000,000 267,304 314,508 088,751
000,000 503,933 351,932 068,665
164,296 583,598 430,704 082,800
220,803 583,598 374,197 096,474
220,803 583,598 234,197 107,825
000,000 700,423 455,000 102,396
000,000 700,423 267,632 141,577
361,348 782,372 233,652 059,628
or, more visually, here as rectangles on the page:
The paper plane is a vector graphic and, therefore, ignored.
Of course you could also change the PDF rendering code to not draw stuff you want to ignore and to visibly draw originally invisible stuff which you want to ignore, and then use bitmap image analysis nonetheless...
EDIT
In his comments the OP asked how to find the rectangle in the rectangle collection returned by find which is nearest to a given point.
First of all there not necessarily is the nearest rectangle, there may be multiple.
That been said, one can choose a nearest rectangle as follows:
First one needs to calculate a distance between point and rectangle, e.g.:
double distance(Rectangle2D rectangle, Point2D point)
{
double x = point.getX();
double y = point.getY();
double left = rectangle.getMinX();
double right = rectangle.getMaxX();
double top = rectangle.getMaxY();
double bottom = rectangle.getMinY();
if (x < left) // point left of rect
{
if (y < bottom) // and below
return Point2D.distance(x, y, left, bottom);
if (y > top) // and top
return Point2D.distance(x, y, left, top);
return left - x;
}
if (x > right) // point right of rect
{
if (y < bottom) // and below
return Point2D.distance(x, y, right, bottom);
if (y > top) // and top
return Point2D.distance(x, y, right, top);
return x - right;
}
if (y < bottom) // and below
return bottom - y;
if (y > top) // and top
return y - top;
return 0;
}
Using this distance measurement one can select a nearest rectangle using code like this for a Collection<Rectangle2D> rectangles and a Point2D point:
Rectangle2D best = null;
double bestDist = Double.MAX_VALUE;
for (Rectangle2D rectangle: rectangles)
{
double distance = distance(rectangle, point);
if (distance < bestDist)
{
best = rectangle;
bestDist = distance;
}
}
After this best contains a best rectangle.
For the sample document used above, this method returns the colored rectangles for the page corners and left and right centers:
EDIT TWO
Since iText 5.5.6, the RenderListener interface has been extended as ExtRenderListener to also be signaled about Path construction and path drawing operations. Thus, the FreeSpaceFinder above could also be extended to handle paths:
//
// Additional ExtRenderListener methods
//
#Override
public void modifyPath(PathConstructionRenderInfo renderInfo)
{
List<Vector> points = new ArrayList<Vector>();
if (renderInfo.getOperation() == PathConstructionRenderInfo.RECT)
{
float x = renderInfo.getSegmentData().get(0);
float y = renderInfo.getSegmentData().get(1);
float w = renderInfo.getSegmentData().get(2);
float h = renderInfo.getSegmentData().get(3);
points.add(new Vector(x, y, 1));
points.add(new Vector(x+w, y, 1));
points.add(new Vector(x, y+h, 1));
points.add(new Vector(x+w, y+h, 1));
}
else if (renderInfo.getSegmentData() != null)
{
for (int i = 0; i < renderInfo.getSegmentData().size()-1; i+=2)
{
points.add(new Vector(renderInfo.getSegmentData().get(i), renderInfo.getSegmentData().get(i+1), 1));
}
}
for (Vector point: points)
{
point = point.cross(renderInfo.getCtm());
Rectangle2D.Float pointRectangle = new Rectangle2D.Float(point.get(Vector.I1), point.get(Vector.I2), 0, 0);
if (currentPathRectangle == null)
currentPathRectangle = pointRectangle;
else
currentPathRectangle.add(pointRectangle);
}
}
#Override
public Path renderPath(PathPaintingRenderInfo renderInfo)
{
if (renderInfo.getOperation() != PathPaintingRenderInfo.NO_OP)
remove(currentPathRectangle);
currentPathRectangle = null;
return null;
}
#Override
public void clipPath(int rule)
{
// TODO Auto-generated method stub
}
Rectangle2D.Float currentPathRectangle = null;
(FreeSpaceFinderExt.java)
Using this class the result above is improved to
As you see the paper plane and the table background colorations now also are taken into account.
My other answer focuses on the original question, i.e. how to find free space with given minimum dimensions on a page.
Since that answer had been written, the OP provided code trying to make use of that original answer.
This answer deals with that code.
The code has a number of shortcoming.
The choice of free space on a page depends on the number of pages in the document.
The reason for this is to be found at the start of the loop over the pages:
for(int i = 1; i <= n; i++)
{
Collection<Rectangle2D> rectangles = find(reader, 300, 100, n, stamper);
...
The OP surely meant i, not n there. The code as is always looks for free space on the last document page.
The rectangles are lower than they should be.
The reason for this is to be found in the retrieval and use of the rectangle coordinates:
x = (float) best.getX();
y = (float) best.getY();
...
getFooterTable(i, n).writeSelectedRows(0, -1, x, y, stamper.getOverContent(i));
The Rectangle2D methods getX and getY return the coordinates of the lower left rectangle corner; the PdfPTable methods writeSelectedRows, on the other hand, require the upper left rectangle corner. Thus, getMaxY should be used instead of getY.

JavaFX Printable to Image very pix-elated, but perfect to printer

I have a printable object that prints perfectly fine to a printer, but when i print it to an Image, then it is very pixelated?
Here is my code to setup a printer for printing and to an image
private PageFormat setupPrinter() {
// sizing (standard is 72dpi, so multiple inches by this)
Double height = 4d * 72d;
Double width = 3d * 72d;
Double margin = 0.1d * 72d;
// now lets print it
AttributeSet attributes = new HashAttributeSet();
attributes.add(new Copies(1));
PrinterJob printJob = PrinterJob.getPrinterJob();
PageFormat pageFormat = printJob.defaultPage();
// if there are more than 10 items, up the paper size to 6inch.
// This will handle up to 24 different items
if (1 == 2) height = 6d * 72d;
// set page size
Paper paper = pageFormat.getPaper();
paper.setSize(width, height);
paper.setImageableArea(margin, margin, width - (margin * 2), height - (margin * 2));
// set orientation and paper
pageFormat.setOrientation(PageFormat.PORTRAIT);
pageFormat.setPaper(paper);
return pageFormat;
}
private void printToImage() {
MyPrintable myPrintable = new MyPrintable();
// setup our printer
PageFormat pageFormat = setupPrinter();
// set size of imageView, using the hieght and width values
double imageHeight = pageFormat.getHeight();
double imageWidth = pageFormat.getWidth();
// create our image/graphics
BufferedImage image = new BufferedImage((int)imageWidth, (int)imageHeight, BufferedImage.TYPE_INT_ARGB);
Graphics graphics = image.getGraphics();
// color background white
graphics.setColor(Color.WHITE);
((Graphics2D) graphics).fill(new Rectangle.Float(0, 0, (float)imageWidth, (float)imageHeight));
// directly call our print method, passing graphics, pageFormat and pageIndex
try {
myPrintable.print(graphics, pageFormat, 0);
} catch (PrinterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
graphics.dispose();
// set our image
imgReport.setImage(SwingFXUtils.toFXImage(image, null));
// now lets show the preview pane
reportPane.setVisible(true);
}
public void printToPrinter(ActionEvent event) {
PrinterJob printJob = PrinterJob.getPrinterJob();
PageFormat pageFormat = setupPrinter();
printJob.setPrintable(new MyPrintable(), pageFormat);
try {
printJob.print();
} catch (Exception e) {
e.printStackTrace();
}
}
public MyPrintable() {
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
if (pageIndex > 0) {
return NO_SUCH_PAGE;
}
// user (0,0) is typically outside the imageable area, so we must translate
// by the X and Y values in the pageFormat to avoid clipping
Graphics2D g2d = (Graphics2D) graphics;
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
// add text etc here
........
}
}
Here is an example of how pixelated it is. Bit it fits in the image perfectly....
You can use a controlling rendering quality
read this:
https://docs.oracle.com/javase/tutorial/2d/advanced/quality.html
example for your code:
Graphics2D graphics = (Graphics2D)image.getGraphics();
RenderingHints rh = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHints(rh);
graphics.setColor(Color.WHITE);
graphics.fill(new Rectangle.Float(0, 0, (float)imageWidth, (float)imageHeight));

Printing Multiple Pages with Swing

I am writing a program that takes my text area with already generated data and it needs to print it out. I got it to calculate how many pages its going to take to print the whole textarea but instead of printing page 1 and then page 2, it prints page 1 and then page 1 again. I can't seem to get it to work to save my life. I've been looking through all the forums and help on oracle but there must be something I am missing. If you could be so kind and take a minute to look at my code I will be so grateful. thanks. -Garrett
private void hardPrintActionPerformed(java.awt.event.ActionEvent evt)
{
int width = printOut.getWidth() ;
int height = printOut.getHeight();
System.out.println("width is "+ width);
System.out.println("Height is "+height);
String font = fontSize.getText();
int FontS = Integer.parseInt(font);
Font fonT = new Font("Monospaced", Font.PLAIN, 12);
printOut.setFont(new Font("Tahoma", Font.PLAIN, FontS));
PrinterJob pj = PrinterJob.getPrinterJob();
PageFormat pf = pj.defaultPage();
Book bk = new Book();
Paper paper = new Paper();
double margin = 18;
pj.setPageable(bk);
paper.setSize(612,792);
paper.setImageableArea(margin, margin
, paper.getWidth() -margin * 2
, paper.getHeight() - margin * 2);
int lines = printOut.getLineCount();
System.out.println("lines = "+lines);
pf.setPaper(paper);
int tall = (int) pf.getImageableHeight();
System.out.println("tall ="+tall);
System.out.println("orientation "+pf.getOrientation());
int pagenum =bk.getNumberOfPages();
System.out.println("pagenum = "+pagenum);
System.out.println("areah = "+height);
int numpages =1;
if (height >756) {
for (int i = 756; i < height;) {
numpages = numpages+1;
i= i +756;
}
}
System.out.println("numpages ="+numpages);
bk.append(new MyPrintable(), pf,numpages);
// pj.setPrintable(new MyPrintable(), pf);
if (pj.printDialog()) {
try {
printOut.setBounds(0, 0, printOut.getWidth(), printOut.getHeight());
pj.print();
} catch (PrinterException pp) {
System.out.println(pp);
}
}
}
/**
* #param args the command line arguments
*/
class MyPrintable implements Printable {
public int print(Graphics g, PageFormat pf, int pageIndex) {
//int pagenum =bk.getNumberOfPages();
int height = printOut.getHeight();
int numpages =1;
if (height >756) {
for (int i = 756; i < height;) {
numpages = numpages+1;
i= i +756;
}
}
if (pageIndex >3)
return NO_SUCH_PAGE;
else{
Graphics2D g2 = (Graphics2D) g;
g2.setFont(new Font("Monospaced", Font.PLAIN, 10));
printOut.setBounds(0, 0, printOut.getWidth(), printOut.getHeight());
// paper.getImageableWidth();
g2.translate(pf.getImageableX(), pf.getImageableY());
System.out.println("print " +pf.getImageableY());
Rectangle componentBounds = printOut.getBounds();
g2.translate(-componentBounds.x, -componentBounds.y);
g2.scale(1, 1);
boolean wasBuffered = printOut.isDoubleBuffered();
printOut.printAll(g2); //printOut is my text area.
printOut.setDoubleBuffered(wasBuffered);
return PAGE_EXISTS;
}
}
}
You need to play with the g2.translate(-componentBounds.x, -componentBounds.y);
Correct the Y with pageIndex*pageHeight
Do you really need to use printJob for that?, why don't use JasperReports to make your pdf's (as a plus, you get a library that make pdf, xls and many others formats), you cant design in IReports and then use it with jasper, or design the report from code, very effective tool :D

How can I print a custom paper size (cheques 8" x 4")?

I am trying to print a custom paper size from a Java applet. I have set the paper size but it is being ignored.
I have also tried using the book method as I have seen something about this helping to get it working but when I use that it just prints a blank page and still seems to be about A4 (I'm looking to print cheques which are obviously much smaller (8" x 4")).
I am trying to print HTML from a JEditorPane if that makes any difference.
If you have any ideas I would be very grateful, I'm tearing my hair out with this one.
I should also add that I am very much a beginner when it comes to Java.
Here is what I have so far:
Updated:
I have now got the page size right but can't seem to get the HTML page I'm loading to fit or line up with the page size.
Update:
Now I just can't get the applet to run in the browser. It works from eclipse just not the browser. I will also need to pass the URL from a parameter.
Here is the HTML applet tag I'm using and updated Java code:
<!DOCTYPE html>
<html>
<head><title>Printing Cheque</title></head>
<body>
<applet width=100 height=100 code="HTMLPrinter"
archive="cheque_print.jar">
</applet>
</body>
</html>
package com.yunatech.pns.chequeprint;
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.print.Book;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import javax.swing.JEditorPane;
public class HTMLPrinter extends Applet {
/**
*
*/
private static final long serialVersionUID = 8065834484717197790L;
private static JEditorPane editor;
public HTMLPrinter() {
try {
editor = new JEditorPane();
editor.setPage("http://localhost/print_test/test.html");
PrinterJob pj = PrinterJob.getPrinterJob();
if (pj.printDialog()) {
PageFormat pf = pj.defaultPage();
Paper paper = pf.getPaper();
double width = 8d * 72d;
double height = 4d * 72d;
double margin = 1d * 72d;
paper.setSize(width, height);
paper.setImageableArea(
margin,
0,
width - (margin * 2),
height);
System.out.println("Before- " + dump(paper));
pf.setOrientation(PageFormat.PORTRAIT);
pf.setPaper(paper);
System.out.println("After- " + dump(paper));
System.out.println("After- " + dump(pf));
dump(pf);
PageFormat validatePage = pj.validatePage(pf);
System.out.println("Valid- " + dump(validatePage));
Book pBook = new Book();
pBook.append(new Page(), pf);
pj.setPageable(pBook);
try {
pj.print();
} catch (PrinterException ex) {
ex.printStackTrace();
}
}
} catch (Exception exp) {
exp.printStackTrace();
}
}
protected static String dump(Paper paper) {
StringBuilder sb = new StringBuilder(64);
sb.append(paper.getWidth()).append("x").append(paper.getHeight())
.append("/").append(paper.getImageableX()).append("x").
append(paper.getImageableY()).append(" - ").append(paper
.getImageableWidth()).append("x").append(paper.getImageableHeight());
return sb.toString();
}
protected static String dump(PageFormat pf) {
Paper paper = pf.getPaper();
return dump(paper);
}
public static class Page implements Printable {
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) {
if (pageIndex >= 1) return Printable.NO_SUCH_PAGE;
Graphics2D g2d = (Graphics2D)graphics;
g2d.translate((int)pageFormat.getImageableX(), (int)pageFormat.getImageableY());
editor.setSize((int)pageFormat.getImageableWidth(), (int)pageFormat.getImageableHeight());
editor.print(g2d);
return Printable.PAGE_EXISTS;
}
}
}
Thanks in advance for any help you can offer.
Printing is designed to work in pixels per inch. The base print API uses a DPI of 72.
You need to convert your measurements accordingly...
double paperWidth = 8 * 72d;
double paperHeight = 4 * 72d;
double margin = 1 * 72d;
UPDATED with example
g2d.setClip(0, 0, (int)pageFormat.getImageableWidth(), (int)pageFormat.getImageableHeight()); is ill adviced, dangerous and generally, not required, besides which, you've used the wrong width and height values. The imageable parameters take into account the margins, but you've not translated the graphics, which will more then likely cut of the bottom, left portion of the area you do have to print to...
I'd just avoid using clipping
public class TestPrinting01 {
public static void main(String[] args) {
PrinterJob pj = PrinterJob.getPrinterJob();
if (pj.printDialog()) {
PageFormat pf = pj.defaultPage();
Paper paper = pf.getPaper();
double width = 8d * 72d;
double height = 4d * 72d;
double margin = 1d * 72d;
paper.setSize(width, height);
paper.setImageableArea(
margin,
margin,
width - (margin * 2),
height - (margin * 2));
System.out.println("Before- " + dump(paper));
pf.setOrientation(PageFormat.LANDSCAPE);
pf.setPaper(paper);
System.out.println("After- " + dump(paper));
System.out.println("After- " + dump(pf));
dump(pf);
PageFormat validatePage = pj.validatePage(pf);
System.out.println("Valid- " + dump(validatePage));
Book pBook = new Book();
pBook.append(new Page(), pf);
pj.setPageable(pBook);
try {
pj.print();
} catch (PrinterException ex) {
ex.printStackTrace();
}
}
}
protected static String dump(Paper paper) {
StringBuilder sb = new StringBuilder(64);
sb.append(paper.getWidth()).append("x").append(paper.getHeight())
.append("/").append(paper.getImageableX()).append("x").
append(paper.getImageableY()).append(" - ").append(paper
.getImageableWidth()).append("x").append(paper.getImageableHeight());
return sb.toString();
}
protected static String dump(PageFormat pf) {
Paper paper = pf.getPaper();
return dump(paper);
}
public static class Page implements Printable {
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) {
if (pageIndex >= 1) {
return Printable.NO_SUCH_PAGE;
}
Graphics2D g2d = (Graphics2D) graphics;
// Be careful of clips...
g2d.translate((int) pageFormat.getImageableX(), (int) pageFormat.getImageableY());
double width = pageFormat.getImageableWidth();
double height = pageFormat.getImageableHeight();
g2d.drawRect(0, 0, (int)pageFormat.getImageableWidth() - 1, (int)pageFormat.getImageableHeight() - 1);
FontMetrics fm = g2d.getFontMetrics();
String text = "top";
g2d.drawString(text, 0, fm.getAscent());
text = "bottom";
double x = width - fm.stringWidth(text);
double y = (height - fm.getHeight()) + fm.getAscent();
g2d.drawString(text, (int)x, (int)y);
return Printable.PAGE_EXISTS;
}
}
}
UPDATED
When printing components, you become responsible for it's layout.
public class TestPrinting01 {
private static JEditorPane editor;
public static void main(String[] args) {
try {
editor = new JEditorPane();
editor.setPage(new File("C:/hold/search.htm").toURI().toURL());
PrinterJob pj = PrinterJob.getPrinterJob();
if (pj.printDialog()) {
PageFormat pf = pj.defaultPage();
Paper paper = pf.getPaper();
double width = 8d * 72d;
double height = 4d * 72d;
double margin = 1d * 72d;
paper.setSize(width, height);
paper.setImageableArea(
margin,
margin,
width - (margin * 2),
height - (margin * 2));
System.out.println("Before- " + dump(paper));
pf.setOrientation(PageFormat.LANDSCAPE);
pf.setPaper(paper);
System.out.println("After- " + dump(paper));
System.out.println("After- " + dump(pf));
dump(pf);
PageFormat validatePage = pj.validatePage(pf);
System.out.println("Valid- " + dump(validatePage));
Book pBook = new Book();
pBook.append(new Page(), pf);
pj.setPageable(pBook);
try {
pj.print();
} catch (PrinterException ex) {
ex.printStackTrace();
}
}
} catch (Exception exp) {
exp.printStackTrace();
}
}
protected static String dump(Paper paper) {
StringBuilder sb = new StringBuilder(64);
sb.append(paper.getWidth()).append("x").append(paper.getHeight())
.append("/").append(paper.getImageableX()).append("x").
append(paper.getImageableY()).append(" - ").append(paper
.getImageableWidth()).append("x").append(paper.getImageableHeight());
return sb.toString();
}
protected static String dump(PageFormat pf) {
Paper paper = pf.getPaper();
return dump(paper);
}
public static class Page implements Printable {
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) {
if (pageIndex >= 1) {
return Printable.NO_SUCH_PAGE;
}
Graphics2D g2d = (Graphics2D) graphics;
// Be careful of clips...
// g2d.setClip(0, 0, (int) pageFormat.getWidth(), (int) pageFormat.getHeight());
g2d.translate((int) pageFormat.getImageableX(), (int) pageFormat.getImageableY());
double width = pageFormat.getImageableWidth();
double height = pageFormat.getImageableHeight();
System.out.println("width = " + width);
System.out.println("height = " + height);
editor.setLocation(0, 0);
editor.setSize((int)width, (int)height);
editor.printAll(g2d);
g2d.setColor(Color.BLACK);
g2d.draw(new Rectangle2D.Double(0, 0, width, height));
return Printable.PAGE_EXISTS;
}
}
}

How to output a String on multiple lines using Graphics

My Program overrides public void paint(Graphics g, int x, int y); in order to draw some stings using g.drawString(someString, x+10, y+30);
Now someString can be quite long and thus, it may not fit on one line.
What is the best way to write the text on multiple line. For instance, in a rectangle (x1, y1, x2, y2)?
Thanks to Epaga's hint and a couple of examples on the Net (not so obvious to find! I used mainly Break a Line for text layout), I could make a component to display wrapped text. It is incomplete, but at least it shows the intended effect.
class TextContainer extends JPanel
{
private int m_width;
private int m_height;
private String m_text;
private AttributedCharacterIterator m_iterator;
private int m_start;
private int m_end;
public TextContainer(String text, int width, int height)
{
m_text = text;
m_width = width;
m_height = height;
AttributedString styledText = new AttributedString(text);
m_iterator = styledText.getIterator();
m_start = m_iterator.getBeginIndex();
m_end = m_iterator.getEndIndex();
}
public String getText()
{
return m_text;
}
public Dimension getPreferredSize()
{
return new Dimension(m_width, m_height);
}
public void paint(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
FontRenderContext frc = g2.getFontRenderContext();
LineBreakMeasurer measurer = new LineBreakMeasurer(m_iterator, frc);
measurer.setPosition(m_start);
float x = 0, y = 0;
while (measurer.getPosition() < m_end)
{
TextLayout layout = measurer.nextLayout(m_width);
y += layout.getAscent();
float dx = layout.isLeftToRight() ?
0 : m_width - layout.getAdvance();
layout.draw(g2, x + dx, y);
y += layout.getDescent() + layout.getLeading();
}
}
}
Just for fun, I made it fitting a circle (alas, no justification, it seems):
public void paint(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
FontRenderContext frc = g2.getFontRenderContext();
LineBreakMeasurer measurer = new LineBreakMeasurer(m_iterator, frc);
measurer.setPosition(m_start);
float y = 0;
while (measurer.getPosition() < m_end)
{
double ix = Math.sqrt((m_width / 2 - y) * y);
float x = m_width / 2.0F - (float) ix;
int width = (int) ix * 2;
TextLayout layout = measurer.nextLayout(width);
y += layout.getAscent();
float dx = layout.isLeftToRight() ?
0 : width - layout.getAdvance();
layout.draw(g2, x + dx, y);
y += layout.getDescent() + layout.getLeading();
}
}
I am not too sure about dx computation, though.
java.awt.font.TextLayout might be helpful. Here's a snippet of their example code:
Graphics2D g = ...;
Point2D loc = ...;
Font font = Font.getFont("Helvetica-bold-italic");
FontRenderContext frc = g.getFontRenderContext();
TextLayout layout = new TextLayout("This is a string", font, frc);
layout.draw(g, (float)loc.getX(), (float)loc.getY());
Rectangle2D bounds = layout.getBounds();
bounds.setRect(bounds.getX()+loc.getX(),
bounds.getY()+loc.getY(),
bounds.getWidth(),
bounds.getHeight());
g.draw(bounds);
Otherwise you could always use a Swing text element to do the job for you, just pass in the Graphics you want it to paint into.
Incrementally build your string, one word at a time, using Epaga's method to find the length of your string. Once the length is longer than your rectangle, remove the last word and print. Repeat until you run out of words.
It sounds like a bad algorithm, but for each line, it's really O(screenWidth/averageCharacterWidth) => O(1).
Still, use a StringBuffer to build your string!
Had some trouble myself this is my solution:
Graphics2D g=....
FontRenderContext frc = g.getFontRenderContext();
TextLayout layout = new TextLayout(text, font, frc);
String[] outputs = text.split("\n");
for(int i=0; i<outputs.length; i++){
g.drawString(outputs[i], 15,(int) (15+i*layout.getBounds().getHeight()+0.5));
Hope that helps.... simple but it works.
You can use a JLabel and embed the text with html.
JLabel.setText("<html>"+line1+"<br>"+line2);

Categories

Resources