Printing simple tabular reports with JTable

I've been putting together a little Java application that required some simple tabular reports that could be both viewed on screen and printed out, the data coming from a Derby database. I spent quite a bit of time looking a the various libraries out there for doing this. A lot of the libraries are commercial, which mean they were a non-starter, and to be honest I wasn't overy impressed with any of the free alternatives. The main ones that I looked at were Jasper Reports and the associated iReport GUI. It looks OK, but although the software is free, it costs $89.95 for the documentation, which in my book makes it a commercial product, not free. I also looked at JFreeReport and the associated Pentaho GUI, but it didn't support subreports and it needed an extensive list of additional libraries, which would have been much larger than my entire application. The last one I considered was DataVision, but the report building GUI was pretty awful - there was no way to align the baseline of fields for example, and you couldn't modify the generated SQL.

Several of the reporting tools accept a TableModel as a source of data, so I was looking at that and the documentation for the associated JTable Swing class, when I noticed that JTable has a Print() method, added in Java 1.5. This takes care of all the scaling and page break management that makes Java printing such a pain, and as the data I wanted to display and print was tabular in nature, it looked like JTable might just be OK for my (admittedly simple) requirements.

I put together a simple form in NetBeans with a JTable in it - NetBeans wraps JTable in a JScrollPane for you when you add it to a form. The next thing was to try to get the JTable to render as close as possible to a printed page. I selected the JTable and modified the properties accordingly - I deselected showHorizontalLines and showVericalLines to remove the cell borders, and disabled focusable as the table was going to be read-only I didn't want the user to be able to select cells within it. I also didn't want the column headers that JTable displays by default - there isn't a way to disable them via the NetBeans form designer, but a call to dataScrollPane.setColumnHeaderView(null) disables them. The next problem is that if the JTable doesn't fill the entire JScrollPane which encloses it, you get a grey background in the unoccupied area. Setting the background of the scroll pane doesn't work as there is another pane between the scroll pane and it's contained JTable, so what we need is dataTable.getParent().setBackground(java.awt.Color.WHITE).

The next problem is that when we display the table, all the columns are set to the default width, which means that the contents of a lot of them are truncated. Hmm. What we actually want is for the columns to be sized to fit the widest row that they contain, and for a horizontal scrollbar to be displayed. Scrolling through the properties for the JTable show an property called setAutoResizeMode that looks like it might do what we want. However it doesn't - there is no supported mode that sizes all columns to fit and enables the horizontal scrollbar if required. After a lot of digging I eventually found the answer. What is required is to set the resize mode to AUTO_RESIZE_OFF, which enables the horizontal scrollbar when required, and manually resize the columns so that they fit the contents. I used a simplified version of the code provided in the above link, taking advantage of the fact that we've already disabled the column headers:

    private void sizeColumns(JTable table) {
        TableColumnModel columns = table.getColumnModel();
        TableModel data = table.getModel();
        int margin = columns.getColumnMargin() * 2;
        int columnCount = columns.getColumnCount();
        int rowCount = data.getRowCount();
        for (int col = 0; col < columnCount; col++) {
            TableColumn column = columns.getColumn(col);
            int modelCol = column.getModelIndex();
            int width = 0;
            for (int row = 0; row < rowCount; row++) {
                TableCellRenderer r = table.getCellRenderer(row, col);
                int w = r.getTableCellRendererComponent(
                  table, data.getValueAt(row, modelCol), false, false, row, col)
                if (w > width) {
                    width = w;
            column.setPreferredWidth(width + margin);

Perfect. The resulting table looks just as I want, and printing it is a snap:

    try {
          new MessageFormat(title),
           new MessageFormat("Page {0}"));
    } catch (PrinterException e) {
        Main.handleException(this, "Printing failed", e, null, false);

The last trick is an easy way of changing text attributes in the contents of the cells. We can take advantage of the fact that text cells in a JTable are rendered with a JLabel, and that a JLabel's contents can be HTML. So, if for example we want an underlined cell we can just wrap the cell's contents in <html><u> and </u></html>. We can also do the same for bold, but taking it much further isn't advisable as the height of the cells won't adjust to fit.

Tags : , ,
Categories : Java, Tech