Java: Setting printable width (Pageformat) - java

I am attempting to print from my java application to a receipt printer,
the width of the receipt is 58mm, it seems that the margin is incorrect and printing with a margin of 1 inch on either side. This results in only 3 letters/numbers being printed and not the full line.
I can print from notepad successfully as I have manually adjusted the margin to 1.97mm on either side which seems to do the trick.
My code is as follows;
public int print(Graphics g, PageFormat pf, int pageIndex)
throws PrinterException {
Font font = new Font("MONOSPACED", Font.PLAIN, 10);
FontMetrics metrics = g.getFontMetrics(font);
int lineHeight = metrics.getHeight();
if (pageBreaks == null) {
initTextLines();
int linesPerPage = (int)(pf.getImageableHeight()/lineHeight);
int numBreaks = (textLines.length-1)/linesPerPage;
pageBreaks = new int[numBreaks];
for (int b=0; b<numBreaks; b++) {
pageBreaks[b] = (b+1)*linesPerPage;
}
}
if (pageIndex > pageBreaks.length) {
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
* Since we are drawing text we
*/
Graphics2D g2d = (Graphics2D)g;
g2d.setFont(new Font("MONOSPACED", Font.PLAIN, 10));
g2d.translate(pf.getImageableX(), pf.getImageableY());
/* Draw each line that is on this page.
* Increment 'y' position by lineHeight for each line.
*/
int y = 0;
int start = (pageIndex == 0) ? 0 : pageBreaks[pageIndex-1];
int end = (pageIndex == pageBreaks.length)
? textLines.length : pageBreaks[pageIndex];
for (int line=start; line<end; line++) {
y += lineHeight;
g.drawString(textLines[line], 0, y);
}
/* tell the caller that this page is part of the printed document */
return PAGE_EXISTS;
}
I would also be grateful if you could help me align the text to the right hand side of the receipt to keep it uniformed with out other systems, however my main issue is the margin if that is sorted I will be over the moon :)
Thank You!
p.s. I am new to printing from java and have struggled, might have redundant code from copying online sources. I have adjusted the font so it is smaller, that did not help much.

I have figured out a workaround to get the desired results, just adding spaces before the text seems to work out well, I have also adjusted the code as follows;
public void print() throws PrintException, IOException {
String defaultPrinter =
PrintServiceLookup.lookupDefaultPrintService().getName();
System.out.println("Default printer: " + defaultPrinter);
PrintService service = PrintServiceLookup.lookupDefaultPrintService();
InputStream is = new ByteArrayInputStream(printableAmounts.getBytes("UTF8"));
PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
pras.add(new Copies(1));
DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;
Doc doc = new SimpleDoc(is, flavor, null);
DocPrintJob job = service.createPrintJob();
PrintJobWatcher pjw = new PrintJobWatcher(job);
job.print(doc, pras);
pjw.waitForDone();
is.close();
}
Seems to be a temporary solution but if nothing else comes up it will become permanent.

Related

control the size of the panel on printed page

i want to print jpanel with two tables . Two problems i faced , the first one is the Accuracy of the printed image is not very good. the second how can i control the size of the jpanel on the printed page??
here is an image of the printed page using xps viewer(the accuracy is not good)
is there away to make the printed image with this accuracy like this
and this is the code:
PrinterJob printjob = PrinterJob.getPrinterJob();
printjob.setJobName(" TESSCO CUSTOMER CARD ");
Printable printable = new Printable() {
public int print(Graphics pg, PageFormat pf, int pageNum) {
if (pageNum > 0) {
return Printable.NO_SUCH_PAGE;
}
Dimension size = jPanel1.getSize();
BufferedImage bufferedImage = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB);
jPanel1.print(bufferedImage.getGraphics());
Graphics2D g2 = (Graphics2D) pg;
g2.translate(pf.getImageableX(), pf.getImageableY());
g2.drawImage(bufferedImage, 0, 0, (int) pf.getWidth(), (int) pf.getHeight(), null);
return Printable.PAGE_EXISTS;
}
};
Paper paper = new Paper();
paper.setImageableArea(0, 0,700,890);
paper.setSize(700,890);
PageFormat format = new PageFormat();
format.setPaper(paper);
format.setOrientation(PageFormat.LANDSCAPE);
printjob.setPrintable(printable, format);
if (printjob.printDialog() == false)
return;
try {
printjob.print();
} catch (PrinterException ex) {
System.out.println("NO PAGE FOUND." + ex);
}
Firstly, don't use the pane size, you need to act as the layout manager and size the panel to fit the page.
Secondly, don't use a buffered image. This will not share the same properties as the graphics context past to you by the print engine. Also, another print method is re-entrant, meaning that it may called a number of times for each page, creating a buffered image this way is wasteful on resources
You might like to take a look at How to Print Tables
UPDATE
You could do something like...
public int print(Graphics pg, PageFormat pf, int pageNum) {
if (page > 0) {
return NO_SUCH_PAGE;
}
Graphics2D g2d = (Graphics2D)pg;
double pageWidth = pf.getImageableWidth();
double pageHeight = pf.getImageableHeight();
double pageX = pf.getImageableX();
double pageY = pf.getImageableY();
g2d.translate(pageX, pageY);
double tableHeight = pageHeight / 2d;
jPanel1.setBounds(0, 0, (int)Math.floor(pageWidth), (int)Math.floor(pageHeight));
jPanel1.printAll(g2d);
return Printable.PAGE_EXISTS;
}
Just beware, that this could have the potential of truncating your table. Also, you should not do this with a Component that is already on the screen. You should create a new "print" component.
UPDATE with working example
Okay, so the concept is sound, just needed some tweaking to get it to work ;)
public class PrintTableTest {
public static void main(String[] args) {
final JTable table1 = new JTable(new AbstractTableModel() {
#Override
public int getRowCount() {
return 3;
}
#Override
public int getColumnCount() {
return 3;
}
#Override
public String getColumnName(int column) {
String name = null;
switch (column) {
case 0:
name = "Day";
break;
case 1:
name = "FirstName";
break;
case 2:
name = "LastName";
break;
}
return name;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Object value = null;
switch (columnIndex) {
case 0:
switch (rowIndex) {
case 0:
value = "First";
break;
case 1:
value = "Second";
break;
case 2:
value = "Final";
break;
}
break;
}
return value;
}
});
int rowHeight = (int) Math.floor(((700f / 2f) - table1.getTableHeader().getPreferredSize().height) / 3f);
table1.setRowHeight(rowHeight);
PrinterJob printjob = PrinterJob.getPrinterJob();
printjob.setJobName(" TESSCO CUSTOMER CARD ");
Printable printable;
printable = new Printable() {
public int print(Graphics pg, PageFormat pf, int pageNum) {
if (pageNum > 0) {
return NO_SUCH_PAGE;
}
Graphics2D g2d = (Graphics2D) pg;
double pageWidth = pf.getImageableWidth();
double pageHeight = pf.getImageableHeight();
double pageX = pf.getImageableX();
double pageY = pf.getImageableY();
g2d.translate(pageX, pageY);
// Each table will take half the page...
double tableHeight = pageHeight / 2d;
// We need to print the header as well...
JTableHeader header = table1.getTableHeader();
int headerHeight = header.getPreferredSize().height;
int yOffset = 0;
for (int index = 0; index < 2; index++) {
// Set the bounds of the components
// The yOffset is actuall irrelevent to us, but for consitency sake
// we'll keep it.
header.setBounds(0, yOffset, (int) Math.floor(pageWidth), headerHeight);
table1.setBounds(0, yOffset + headerHeight, (int) Math.floor(pageWidth), (int) Math.floor(tableHeight));
// Force the components to update there internal layouts to match
// the new size. We need to do this because, technically, we're not
// attached to any peer, nor do we want them to be taking into account
// the dimensions of any parent any way :P
table1.doLayout();
header.doLayout();
// Translate the graphics. Components asume a position of 0x0 when
// painting. This is a side effect of the AWT/Swing painting engine
// (for which we are greatful), but we need to simulate the change
g2d.translate(0, yOffset);
header.printAll(g2d);
// Translations are relative to the last translation...
g2d.translate(0, headerHeight);
table1.printAll(g2d);
// Reset the last translation
g2d.translate(0, -(headerHeight + yOffset));
// Next table...
yOffset += table1.getHeight();
}
return Printable.PAGE_EXISTS;
}
};
Paper paper = new Paper();
paper.setImageableArea(0, 0, 700, 890);
paper.setSize(700, 890);
PageFormat format = new PageFormat();
format.setPaper(paper);
format.setOrientation(PageFormat.LANDSCAPE);
// printjob.setPrintable(printable, format);
BufferedImage img = new BufferedImage(890, 700, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fill(new Rectangle(0, 0, 890, 700));
try {
printable.print(g2d, format, 0);
} catch (Exception exp) {
exp.printStackTrace();
}
g2d.dispose();
try {
ImageIO.write(img, "png", new File("Print.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
}

How to print multiple header lines with MessageFormat using a JTable

I have a table called table and its filled with data, I also have a MessageFormat header I want to use as a header to print the JTable this is the MessageFormat:
MessageFormat header = new MessageFormat("Product: "
+ task.getProductName() + " Job: "
+ task.getJobNumber() + " Task: " + task.getTaskID()
);
I want to print 3 lines in the header, one for Product, Job and Task
the way I print this table is like so:
table.print(JTable.PrintMode.FIT_WIDTH, header, null);
I can't seem to figure out how to print the header in 3 seperate lines, I tried using the \n to make a new line but that doesn't seem to work.
It's gonna be long answer (code wise) because the only solution I found was to implement a custom Printable. Of course I didn't write the following code myself, I mostly copied the code I extracted from the jdk sources and made some adjustments.
Here we are:
This is the way you said you invoke the print method:
DefaultTableModel dtm = new DefaultTableModel(new String[] { "Column 1" }, 1);
JTable table = new JTable(dtm) {
#Override
public Printable getPrintable(PrintMode printMode, MessageFormat headerFormat, MessageFormat footerFormat) {
return new TablePrintable(this, printMode, headerFormat, footerFormat);
}
};
where TablePrintable is the following class (sorry for not being concise here):
static class TablePrintable implements Printable {
private final JTable table;
private final JTableHeader header;
private final TableColumnModel colModel;
private final int totalColWidth;
private final JTable.PrintMode printMode;
private final MessageFormat headerFormat;
private final MessageFormat footerFormat;
private int last = -1;
private int row = 0;
private int col = 0;
private final Rectangle clip = new Rectangle(0, 0, 0, 0);
private final Rectangle hclip = new Rectangle(0, 0, 0, 0);
private final Rectangle tempRect = new Rectangle(0, 0, 0, 0);
private static final int H_F_SPACE = 8;
private static final float HEADER_FONT_SIZE = 18.0f;
private static final float FOOTER_FONT_SIZE = 12.0f;
private final Font headerFont;
private final Font footerFont;
public TablePrintable(JTable table, JTable.PrintMode printMode, MessageFormat headerFormat,
MessageFormat footerFormat) {
this.table = table;
header = table.getTableHeader();
colModel = table.getColumnModel();
totalColWidth = colModel.getTotalColumnWidth();
if (header != null) {
// the header clip height can be set once since it's unchanging
hclip.height = header.getHeight();
}
this.printMode = printMode;
this.headerFormat = headerFormat;
this.footerFormat = footerFormat;
// derive the header and footer font from the table's font
headerFont = table.getFont().deriveFont(Font.BOLD, HEADER_FONT_SIZE);
footerFont = table.getFont().deriveFont(Font.PLAIN, FOOTER_FONT_SIZE);
}
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
// for easy access to these values
final int imgWidth = (int) pageFormat.getImageableWidth();
final int imgHeight = (int) pageFormat.getImageableHeight();
if (imgWidth <= 0) {
throw new PrinterException("Width of printable area is too small.");
}
// to pass the page number when formatting the header and footer
// text
Object[] pageNumber = new Object[] { Integer.valueOf(pageIndex + 1) };
// fetch the formatted header text, if any
String headerText = null;
if (headerFormat != null) {
headerText = headerFormat.format(pageNumber);
}
// fetch the formatted footer text, if any
String footerText = null;
if (footerFormat != null) {
footerText = footerFormat.format(pageNumber);
}
// to store the bounds of the header and footer text
Rectangle2D hRect = null;
Rectangle2D fRect = null;
// the amount of vertical space needed for the header and footer
// text
int headerTextSpace = 0;
int footerTextSpace = 0;
// the amount of vertical space available for printing the table
int availableSpace = imgHeight;
// if there's header text, find out how much space is needed for it
// and subtract that from the available space
if (headerText != null) {
graphics.setFont(headerFont);
int nbLines = headerText.split("\n").length;
hRect = graphics.getFontMetrics().getStringBounds(headerText, graphics);
hRect = new Rectangle2D.Double(hRect.getX(), Math.abs(hRect.getY()), hRect.getWidth(),
hRect.getHeight() * nbLines);
headerTextSpace = (int) Math.ceil(hRect.getHeight() * nbLines);
availableSpace -= headerTextSpace + H_F_SPACE;
}
// if there's footer text, find out how much space is needed for it
// and subtract that from the available space
if (footerText != null) {
graphics.setFont(footerFont);
fRect = graphics.getFontMetrics().getStringBounds(footerText, graphics);
footerTextSpace = (int) Math.ceil(fRect.getHeight());
availableSpace -= footerTextSpace + H_F_SPACE;
}
if (availableSpace <= 0) {
throw new PrinterException("Height of printable area is too small.");
}
// depending on the print mode, we may need a scale factor to
// fit the table's entire width on the page
double sf = 1.0D;
if (printMode == JTable.PrintMode.FIT_WIDTH && totalColWidth > imgWidth) {
// if not, we would have thrown an acception previously
assert imgWidth > 0;
// it must be, according to the if-condition, since imgWidth > 0
assert totalColWidth > 1;
sf = (double) imgWidth / (double) totalColWidth;
}
// dictated by the previous two assertions
assert sf > 0;
// This is in a loop for two reasons:
// First, it allows us to catch up in case we're called starting
// with a non-zero pageIndex. Second, we know that we can be called
// for the same page multiple times. The condition of this while
// loop acts as a check, ensuring that we don't attempt to do the
// calculations again when we are called subsequent times for the
// same page.
while (last < pageIndex) {
// if we are finished all columns in all rows
if (row >= table.getRowCount() && col == 0) {
return NO_SUCH_PAGE;
}
// rather than multiplying every row and column by the scale
// factor
// in findNextClip, just pass a width and height that have
// already
// been divided by it
int scaledWidth = (int) (imgWidth / sf);
int scaledHeight = (int) ((availableSpace - hclip.height) / sf);
// calculate the area of the table to be printed for this page
findNextClip(scaledWidth, scaledHeight);
last++;
}
// create a copy of the graphics so we don't affect the one given to
// us
Graphics2D g2d = (Graphics2D) graphics.create();
// translate into the co-ordinate system of the pageFormat
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
// to save and store the transform
AffineTransform oldTrans;
// if there's footer text, print it at the bottom of the imageable
// area
if (footerText != null) {
oldTrans = g2d.getTransform();
g2d.translate(0, imgHeight - footerTextSpace);
String[] lines = footerText.split("\n");
printText(g2d, lines, fRect, footerFont, imgWidth);
g2d.setTransform(oldTrans);
}
// if there's header text, print it at the top of the imageable area
// and then translate downwards
if (headerText != null) {
String[] lines = headerText.split("\n");
printText(g2d, lines, hRect, headerFont, imgWidth);
g2d.translate(0, headerTextSpace + H_F_SPACE);
}
// constrain the table output to the available space
tempRect.x = 0;
tempRect.y = 0;
tempRect.width = imgWidth;
tempRect.height = availableSpace;
g2d.clip(tempRect);
// if we have a scale factor, scale the graphics object to fit
// the entire width
if (sf != 1.0D) {
g2d.scale(sf, sf);
// otherwise, ensure that the current portion of the table is
// centered horizontally
} else {
int diff = (imgWidth - clip.width) / 2;
g2d.translate(diff, 0);
}
// store the old transform and clip for later restoration
oldTrans = g2d.getTransform();
Shape oldClip = g2d.getClip();
// if there's a table header, print the current section and
// then translate downwards
if (header != null) {
hclip.x = clip.x;
hclip.width = clip.width;
g2d.translate(-hclip.x, 0);
g2d.clip(hclip);
header.print(g2d);
// restore the original transform and clip
g2d.setTransform(oldTrans);
g2d.setClip(oldClip);
// translate downwards
g2d.translate(0, hclip.height);
}
// print the current section of the table
g2d.translate(-clip.x, -clip.y);
g2d.clip(clip);
table.print(g2d);
// restore the original transform and clip
g2d.setTransform(oldTrans);
g2d.setClip(oldClip);
// draw a box around the table
g2d.setColor(Color.BLACK);
g2d.drawRect(0, 0, clip.width, hclip.height + clip.height);
// dispose the graphics copy
g2d.dispose();
return PAGE_EXISTS;
}
private void printText(Graphics2D g2d, String[] lines, Rectangle2D rect, Font font, int imgWidth) {
g2d.setColor(Color.BLACK);
g2d.setFont(font);
for (int i = 0; i < lines.length; i++) {
int tx;
// if the text is small enough to fit, center it
if (rect.getWidth() < imgWidth) {
tx = (int) (imgWidth / 2 - g2d.getFontMetrics().getStringBounds(lines[i], g2d).getWidth() / 2);
// otherwise, if the table is LTR, ensure the left side of
// the text shows; the right can be clipped
} else if (table.getComponentOrientation().isLeftToRight()) {
tx = 0;
// otherwise, ensure the right side of the text shows
} else {
tx = -(int) (Math.ceil(rect.getWidth()) - imgWidth);
}
int ty = (int) Math.ceil(Math.abs(rect.getY() + i * rect.getHeight() / lines.length));
g2d.drawString(lines[i], tx, ty);
}
}
private void findNextClip(int pw, int ph) {
final boolean ltr = table.getComponentOrientation().isLeftToRight();
// if we're ready to start a new set of rows
if (col == 0) {
if (ltr) {
// adjust clip to the left of the first column
clip.x = 0;
} else {
// adjust clip to the right of the first column
clip.x = totalColWidth;
}
// adjust clip to the top of the next set of rows
clip.y += clip.height;
// adjust clip width and height to be zero
clip.width = 0;
clip.height = 0;
// fit as many rows as possible, and at least one
int rowCount = table.getRowCount();
int rowHeight = table.getRowHeight(row);
do {
clip.height += rowHeight;
if (++row >= rowCount) {
break;
}
rowHeight = table.getRowHeight(row);
} while (clip.height + rowHeight <= ph);
}
// we can short-circuit for JTable.PrintMode.FIT_WIDTH since
// we'll always fit all columns on the page
if (printMode == JTable.PrintMode.FIT_WIDTH) {
clip.x = 0;
clip.width = totalColWidth;
return;
}
if (ltr) {
// adjust clip to the left of the next set of columns
clip.x += clip.width;
}
// adjust clip width to be zero
clip.width = 0;
// fit as many columns as possible, and at least one
int colCount = table.getColumnCount();
int colWidth = colModel.getColumn(col).getWidth();
do {
clip.width += colWidth;
if (!ltr) {
clip.x -= colWidth;
}
if (++col >= colCount) {
// reset col to 0 to indicate we're finished all columns
col = 0;
break;
}
colWidth = colModel.getColumn(col).getWidth();
} while (clip.width + colWidth <= pw);
}
}
And here is the result (I hope that's what you expect):
You could try
StringBuilder builder = new StringBuilder();
builder.append("Product: ");
builder.append(task.getProductName());
builder.append(System.getProperty("line.separator"));
builder.append("Job: ");
builder.append(task.getJobNumber());
builder.append(System.getProperty("line.separator"));
builder.append("Task: ");
builder.append(task.getTaskID();
MessageFormat header = new MessageFormat(builder.toString());
If this doesn't work, then you're going to have to set up your own printer job, and layout the header precisely as you want it.
If you use the getPrintable method instead without adding a header/footer text, you can then include/decorate the returned Printable in one where you have more control over the header, and where you can specify multi-line headers. See the javadoc of that method which mentions
It is entirely valid for this Printable to be wrapped inside another in order to create complex reports and documents. You may even request that different pages be rendered into different sized printable areas. The implementation must be prepared to handle this (possibly by doing its layout calculations on the fly). However, providing different heights to each page will likely not work well with PrintMode.NORMAL when it has to spread columns across pages.
I have not enough experience with Printables to help you further on how to actually do this
Basically, the answer of #aymeric is correct: there's no way around a custom printable implementation. A way to do it with slightly less c&p is to have a custom implementation that
takes over header/footer printing
delegates to table printing itself to the default printable
The trick in that approach is to fool the delegate tablePrintable into believing that the page is smaller than it actually is, with a custom pageFormat
more details (and code)
I've utilized two MessageFormat arrays as a neat solution to the problem. You'll find below a printout of the end result:
The code is outlined below:
try
{
PrinterJob job = PrinterJob.getPrinterJob();
MessageFormat[] header = new MessageFormat[6];
// Assign the arrays with 6 String values for the headers
header[0] = new MessageFormat("");
header[1] = new MessageFormat(theExamSelection);
header[2] = new MessageFormat("");
header[3] = new MessageFormat("Scrud 60 - Grade Returns - Random Sample");
header[4] = new MessageFormat("");
header[5] = new MessageFormat(theSubjectSelection+" - "+theLevelSelection+" - "+thePaperSelection);
MessageFormat[] footer = new MessageFormat[4];
// Assign the 4 Strings to the footer array
footer[0] = new MessageFormat("Assistant Examiner Signature:______________ Date:___ /___ /_____ ");
footer[1] = new MessageFormat("");
footer[2] = new MessageFormat("");
footer[3] = new MessageFormat("Advising Examiner Signature:______________ Date:___ /___ /_____ ");
//here you place the JTable to print
// in this case its called randomSample_gradeBreakdown_jTable
// along with the header and footer arrays
job.setPrintable(new PrintTableMultiLine(randomSample_gradeBreakdown_jTable, JTable.PrintMode.FIT_WIDTH, header, footer ));
job.print();
}
catch (java.awt.print.PrinterException e)
{
System.err.format("Cannot print %s%n", e.getMessage());
JOptionPane.showMessageDialog(this,
"Check that your printer is working correctly","PRINT ERROR",JOptionPane.ERROR_MESSAGE
);
}

Fix text stretching when printing to a receipt printer with Java

I am printing to some Epson receipt printers by implementing the Java Printable and placing my code into the print method. To draw the text to the printer I use Graphics2D.drawString. I am also drawing a rect to the printer to see how to compares to the text size when printing to other printers. When printing to the receipt printer the text on the paper is about double the width of printing to a laser printer or the XPS writer virtual print. Is this a problem with the way Java draws text to the Graphics2D object? I have the newest version of Java installed of 6 update 20.
Any Ideas of what to look into would be helpful.
Thanks.
Here the code I am using. With this example I am seeing the letter 'c' on the right edge of the rect when sending it to a XPS writer and if I print it to my receipt printer the 6 is on the right edge of the rect and you can tell the text is much wider then it should be. The rect seems to be the correct size.
I have tried changing the page and margin sizes but it does not seem to fix my text problem. I got these paper sizes and margins from how Microsoft Word is auto detecting the printer. Word prints the text correctly to the receipt printer.
public static void main(String[] args) {
PageFormat format = new PageFormat();
Paper paper = new Paper();
double paperWidth = 3.25;
double paperHeight = 11.69;
double leftMargin = 0.19;
double rightMargin = 0.25;
double topMargin = 0;
double bottomMargin = 0.01;
paper.setSize(paperWidth * 72.0, paperHeight * 72.0);
paper.setImageableArea(leftMargin * 72.0, topMargin * 72.0,
(paperWidth - leftMargin - rightMargin) * 72.0,
(paperHeight - topMargin - bottomMargin) * 72.0);
format.setPaper(paper);
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
aset.add(OrientationRequested.PORTRAIT);
PrinterJob printerJob = PrinterJob.getPrinterJob();
Printable printable = new ReceiptPrintTest();
format = printerJob.validatePage(format);
printerJob.setPrintable(printable, format);
try {
printerJob.print(aset);
}
catch (Exception e) {
e.printStackTrace();
}
}
public class ReceiptPrintTest implements Printable {
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
if (pageIndex < 0 || pageIndex >= 1) {
return Printable.NO_SUCH_PAGE;
}
Graphics2D g2d = (Graphics2D) graphics;
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
Font font = new Font("Arial",Font.PLAIN, 14);
g2d.setFont(font);
g2d.drawString("1234567890abcdefg", 50, 70);
g2d.drawRect(50, 0, 100, 50);
return Printable.PAGE_EXISTS;
}
Have you tried setting the font using setFont?

How do I get hold of the margins of the Paper when using java printing?

I'm trying to print a JPanel with some painted graphics on it (overriding paintComponent). The graphics is so big that they wont fit on a single page and therefor I'm letting it span across multiple pages. My problem lies within the fact that if I let the user choose the pageFormat/Paper type by calling:
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
PageFormat pf = printJob.pageDialog(aset);
printJob.setPrintable(canvas, pf);
When I'm writing my print() method (implementing Printable) in my JPanel class I can't seem to get the hold of the margins? I use graphics.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); to make it start drawing in the correct topleft corner (0;0) and it takes the margins into consideration (i.e.,starting more at (80; 100) or so). But then it prints over the bottom and right margin which I don't want it to do since that negates the user's wishes.
Here is the code of my print() method as a reference, which works fine when you don't let the user set the paper (using the default instead):
Rectangle[] pageBreaks;
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
//Calculate how many pages our print will be
if(pageBreaks == null){
double pageWidth = pageFormat.getPaper().getWidth();
double pageHeight = pageFormat.getPaper().getHeight();
//Find out how many pages we need
int numberOfPagesHigh = (int) Math.ceil(size.getHeight()/pageHeight);
int numberOfPagesWide = (int) Math.ceil(size.getWidth()/pageWidth);
pageBreaks = new Rectangle[numberOfPagesHigh*numberOfPagesWide];
double x = 0;
double y = 0;
int curXPage = 0;
//Calculate what we will print on each page
for (int i = 0; i < pageBreaks.length; i++){
double xStart = x;
double yStart = y;
x += pageWidth;
pageBreaks[i] = new Rectangle((int)xStart, (int)yStart, (int)pageWidth, (int)pageHeight);
curXPage++;
if (curXPage > numberOfPagesWide){
curXPage = 0;
x = 0;
y += pageHeight;
}
}
}
if (pageIndex < pageBreaks.length){
//Cast graphics to Graphics2D for richer API
Graphics2D g2d = (Graphics2D) graphics;
//Translate into position of the paper
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
//Setup our current page
Rectangle rect = pageBreaks[pageIndex];
g2d.translate(-rect.x, -rect.y);
g2d.setClip(rect.x, rect.y, rect.width, rect.height);
//Paint the component on the graphics object
Color oldBG = this.getBackground();
this.setBackground(Color.white);
util.PrintUtilities.disableDoubleBuffering(this);
this.paintComponent(g2d);
util.PrintUtilities.enableDoubleBuffering(this);
this.setBackground(oldBG);
//Return
return PAGE_EXISTS;
}
else {
return NO_SUCH_PAGE;
}
}
After posting this question and tabbing back into my IDE I pretty easily found the answer. Instead of using
double pageWidth = pageFormat.getPaper().getWidth();
double pageHeight = pageFormat.getPaper().getHeight();
use
double pageWidth = pageFormat.getImageableWidth();
double pageHeight = pageFormat.getImageableHeight();
The getImageableWidth() returns the totalPaperWidth-totalMargins whereas getWidth() just returns totalPaperWidth. This makes the print() method not draw more that it can on each page!

Testing whether a Font is monospaced in Java

I'm trying list all of the monospaced fonts available on a user's machine. I can get all of the font families in Swing via:
String[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getAvailableFontFamilyNames();
Is there a way to figure out which of these are monospaced?
Thanks in advance.
A simpler method that doesn't require making a BufferedImage to get a Graphics object etc.:
Font fonts[] = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
List<Font> monoFonts1 = new ArrayList<>();
FontRenderContext frc = new FontRenderContext(null, RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT, RenderingHints.VALUE_FRACTIONALMETRICS_DEFAULT);
for (Font font : fonts) {
Rectangle2D iBounds = font.getStringBounds("i", frc);
Rectangle2D mBounds = font.getStringBounds("m", frc);
if (iBounds.getWidth() == mBounds.getWidth()) {
monoFonts1.add(font);
}
}
You could use the getWidths() method of the FontMetrics class. According to the JavaDoc:
Gets the advance widths of the first 256 characters in the Font. The advance is the distance from the leftmost point to the rightmost point on the character's baseline. Note that the advance of a String is not necessarily the sum of the advances of its characters.
You could use the charWidth(char) method of the FontMetrics class. For example:
Set<String> monospaceFontFamilyNames = new HashSet<String>();
GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fontFamilyNames = graphicsEnvironment.getAvailableFontFamilyNames();
BufferedImage bufferedImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics graphics = bufferedImage.createGraphics();
for (String fontFamilyName : fontFamilyNames) {
boolean isMonospaced = true;
int fontStyle = Font.PLAIN;
int fontSize = 12;
Font font = new Font(fontFamilyName, fontStyle, fontSize);
FontMetrics fontMetrics = graphics.getFontMetrics(font);
int firstCharacterWidth = 0;
boolean hasFirstCharacterWidth = false;
for (int codePoint = 0; codePoint < 128; codePoint++) {
if (Character.isValidCodePoint(codePoint) && (Character.isLetter(codePoint) || Character.isDigit(codePoint))) {
char character = (char) codePoint;
int characterWidth = fontMetrics.charWidth(character);
if (hasFirstCharacterWidth) {
if (characterWidth != firstCharacterWidth) {
isMonospaced = false;
break;
}
} else {
firstCharacterWidth = characterWidth;
hasFirstCharacterWidth = true;
}
}
}
if (isMonospaced) {
monospaceFontFamilyNames.add(fontFamilyName);
}
}
graphics.dispose();
Compare the drawn lengths of several characters (m, i, 1, . should be a good set).
For monospaced fonts they will all be equal, for variable width fonts they won't.
According to this response, Java doesn't know too much about underlying font details, so you'd have to do some comparisons of the font's dimensions.
Probably not applicable for your case, but if you simply want to set the font to a monospaced font, use the logical font name:
Font mono = new Font("Monospaced", Font.PLAIN, 12);
This will be a guaranteed monospaced font on your system.

Categories