How to print multiple header lines using MessageFormat using JTable

I have a table called table and it is filled with data, I also have a MessageFormat header that I want to use as a JTable print JTable , this is MessageFormat :

 MessageFormat header = new MessageFormat("Product: " + task.getProductName() + " Job: " + task.getJobNumber() + " Task: " + task.getTaskID() ); 

I want to print 3 headings in a heading, one for a product, a task and a task

the way to print this table like this:

 table.print(JTable.PrintMode.FIT_WIDTH, header, null); 

I can't figure out how to print the header in 3 separate lines, I tried using \n to create a new line, but this does not work.

+4
source share
5 answers

This will be a long answer (code wise), because the only solution I found was to implement custom Printable . Of course, I myself did not write the following code, I basically copied the code extracted from jdk sources and made some settings.

Here we are:

So you said that you are calling 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 brief 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 unchanging hclip.height = header.getHeight(); } this.printMode = printMode; this.headerFormat = headerFormat; this.footerFormat = footerFormat; // derive the header and footer font from the table 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 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 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 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 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 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 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 (hope you expect): JTable with multiple line header when printed

+5
source

You can 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 does not work, you will need to configure your own job on the printer and place the header exactly as you want it.

+2
source

If you use the getPrintable method without adding header / footer text, you can include / decorate the returned Printable in which you have more control over the header and where you can specify multi-line headers. See the javadoc of this method, which mentions

It is fully acceptable for this Printable to be wrapped inside another to create complex reports and documents. You can even require that different pages appear in print areas of different sizes. The implementation should be prepared to solve this problem (possibly by performing layout calculations on the fly). However, providing different heights for each page will most likely not work with PrintMode.NORMAL when it needs to distribute columns between pages.

I don’t have enough experience with Printable to help you further on how to do this.

+2
source

Basically, @aymeric's answer is correct: there is no way to implement on order. The way to do this with a little bit of c & p is to execute a custom implementation that

  • print header / footer
  • delegates to print the default print table

The trick in this approach is to trick the Printable delegate table into thinking that the page is smaller than it actually is, with a custom page

more details (and code)

+2
source

I used two MessageFormat arrays as a neat solution to the problem. Below you will find a printout of the final result: enter image description here

Below is the code.

  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 ); } 
0
source

All Articles