JTable Printing. How to set page breaks programmatically?

1.1k views Asked by At

I am trying to find a way to programmatically set page breaks for printing out a JTable.

E.g., I have a table with approx 150 rows like this:


Line Number Data1 Data2 Data3 …etc
1                    a         b        c        d
1                    a         b        c        d
1                    a         b        c        d
2                    a         b        c        d
2                    a         b        c        d
3                    a         b        c        d
3                    a         b        c        d
3                    a         b        c        d
3                    a         b        c        d
4                    a         b        c        d
5                    a         b        c        d
5                    a         b        c        d
5                    a         b        c        d
…etc              …etc …etc …etc …etc

I need to find a way to start printing a new page when the line number changes.

I found a method for printing selected rows, and so far have modified it to loop through my table adding rows to a temporary print model and then calling print() method to print before resetting the temporary variables. However, this means that I am calling print() maybe 10 times, once for each line number, which is an unacceptable solution. The code that accomplishes current flawed solution is as follows:

btnPrint.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
            try {
                int wrappingLineNumber = (Integer) table.getValueAt(0, 0);
                WrappingSheetsTableModel printModel = new WrappingSheetsTableModel();
                for (int i = 0; i < table.getRowCount(); i++) {
                    if ((Integer)table.getValueAt(i, 0) == wrappingLineNumber) {
                        System.out.println(table.getValueAt(i, -1));
                        printModel.addRow((WrappingSheets) table.getValueAt(i, -1));
                    } else {
                        wrappingLineNumber = (Integer) table.getValueAt(i, 0);
                        // if not the same, i.e., value changed
                        JTable toPrint = new JTable(printModel);
                        toPrint.setSize(toPrint.getPreferredSize());
                        JTableHeader tableHeader = toPrint.getTableHeader();
                        tableHeader.setSize(tableHeader.getPreferredSize());
                        toPrint.print(JTable.PrintMode.FIT_WIDTH);
                        printModel.removeAll();
                        printModel.addRow((WrappingSheets) table.getValueAt(i, -1));
                    }
                }
                System.out.println("success printing");
            } catch (PrinterException pe) {
                System.out.println("printing failed");
                pe.printStackTrace();
            }
        };
    });

Can anyone provide a better solution that will start printing on a new page whenever the value in the line number column changes?

Many thanks in advance for any help provided!

1

There are 1 answers

0
Andrew On BEST ANSWER

After further research and thought I realized that it is possible to create sub-tables/temporary tables holding only the information relevant to each wrapping line. The question then is how to print multiple JTables as one print job. Variations of this question have been asked and answered here and particularly here. Following the information provided in the second of those links, the solution I came up with is as follows:

Solution utilizes unaltered PrintableWrapper provided by Durandal at link 2 above, included here for information purposes "to spare us the hassle of iterating over each page":

public class PrintableWrapper implements Printable {
private Printable delegate;
private int offset;

public PrintableWrapper(Printable delegate, int offset) {
    this.offset = offset;
    this.delegate = delegate;
}

@Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
    return delegate.print(graphics, pageFormat, pageIndex-offset);
}}

Solution also utilizes unaltered kleopatra / Durandal's code from same link for getting number of pages required for each table:

public int getNumberOfPages(Printable delegate, PageFormat pageFormat) throws PrinterException {
    Graphics g = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB).createGraphics();
    int numPages = 0;
    while (true) {
        int result = delegate.print(g, pageFormat, numPages);
        if (result == Printable.PAGE_EXISTS) {
            ++numPages;
        } else {
            break;
        }
    }

    return numPages;
}

From this, the solution was to create a PrinterJob, Book and Temporary JTables. Then add the temporary tables to the book, using the above class and methods. And lastly to print the book. Current code for this solution can be found below, with explanatory comments included in the code:

btnPrint.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent arg0) {
                    try {
                        //Code revised from https://stackoverflow.com/questions/14775753/printing-multiple-jtables-as-one-job-book-object-only-prints-1st-table?rq=1
                        // \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
                        PrinterJob printerJob = PrinterJob.getPrinterJob();

                        //Set 1/4 " margins and orientation
                        PageFormat pf = printerJob.defaultPage();
                        pf.setOrientation(PageFormat.LANDSCAPE);
                        Paper paper = new Paper();
                        //double margin = 36; // half inch
                        double margin = 18; // quarter inch
                        paper.setImageableArea(margin, margin, paper.getWidth() - margin * 2, paper.getHeight() - margin * 2);
                        pf.setPaper(paper);

                        Book bookToPrint = new Book();
                        // /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
                        //Code revised from https://stackoverflow.com/questions/14775753/printing-multiple-jtables-as-one-job-book-object-only-prints-1st-table?rq=1

                        // List to store temporary tables to print
                        List<JTable> tablesToPrint = new ArrayList<JTable>();

                        // create temporary print tables by wrapping line number
                        int wrappingLineNumber = (Integer) table.getValueAt(0, 0);
                        WrappingSheetsTableModel printModel = new WrappingSheetsTableModel();
                        for (int i = 0; i < table.getRowCount(); i++) {
                            if ((Integer)table.getValueAt(i, 0) == wrappingLineNumber) {
                                // add wrapping sheet row to print model
                                printModel.addRow((WrappingSheets) table.getValueAt(i, -1));
                            } else {
                                // if not the same, i.e., value changed
                                // create a new table
                                JTable toPrint = new JTable(printModel);

                                // set size of table as if size isn't set the table contents aren't printed
                                toPrint.setSize(toPrint.getPreferredSize());
                                // reduce row height for printing
                                toPrint.setRowHeight(16);
                                // add table to print to list of tables to be printed
                                tablesToPrint.add(toPrint);

                                // create a new print model for next table to be printed
                                printModel = new WrappingSheetsTableModel();
                                // add first new wrapping sheet row to print model
                                printModel.addRow((WrappingSheets) table.getValueAt(i, -1));
                                // increment the wrapping line number
                                wrappingLineNumber = (Integer) table.getValueAt(i, 0);
                            }
                        }
                        // add in last table to print
                        JTable toPrint = new JTable(printModel);
                        // set size of table as if size isn't set the table contents aren't printed
                        toPrint.setSize(toPrint.getPreferredSize());
                        // reduce row height for printing
                        toPrint.setRowHeight(16);

                        // add last table to print to list of tables to be printed
                        tablesToPrint.add(toPrint);

                        // add each table to be printed to book for printing
                        int totalPages = 0;
                        for (JTable table : tablesToPrint) {
                            // get the table header information from table
                            JTableHeader tableHeader = table.getTableHeader();
                            // set size so that table header will be visible
                            tableHeader.setSize(tableHeader.getPreferredSize());

                            // get number of pages in printable table
                            int pages = getNumberOfPages(table.getPrintable(PrintMode.FIT_WIDTH, null, null), pf);

                            // create temporary printable to add to book
                            Printable p = table.getPrintable(PrintMode.FIT_WIDTH, null, null);
                            // add printable to book using printable wrapper
                            // p - printable, totalPages - total pages, pf - page format, pages - num pages to add
                            bookToPrint.append(new PrintableWrapper(p, totalPages), pf, pages);

                            // increase total pages count
                            totalPages += pages;
                        }

                        //Checks for page count:
                        //System.out.println(totalPages);
                        //System.out.println(bookToPrint.getNumberOfPages());

                        // Queries document for the number of pages 
                        // and the PageFormat 
                        // and Printable 
                        // for each page held in the Pageable instance, document.
                        printerJob.setPageable(bookToPrint);

                        // show print dialog
                        // - printer selection and pages to print work
                        // - changing from e.g. landscape to portrait does not work currently
                        if(printerJob.printDialog()) printerJob.print();

                        System.out.println("success printing X");
                    } catch (PrinterException pe) {
                        System.out.println("printing failed");
                        pe.printStackTrace();
                    }
                };
            });

Each page now contains only those rows with the same wrapping line number and a new page is started when the wrapping line number changes. (More research is still needed in relation to the PageFormat and PrintDialog, as changes to page orientation in the print dialog do not currently affect the final print layout).