Domanda

I am looking for a method to split wide tables so that they span across multiple pages. The goal is to make tables with large number of columns readable. I found one discussion thread where this topic is covered; however, the example referenced in there is not available. Manning's "iText in Action" (2006) doesn't cover this topic.

Can this be done in version 1.4.8, if not, to which version of iText should I upgrade to?

È stato utile?

Soluzione

Please take a look at the examples of chapter 4 of my book, more specifically at the Zhang example. In this example, I have a table with four columns: (1) year, (2) movie title in English, (3) movie title in Chinese, and (4) run length. If you look at the resulting PDF, you will see that this table is split vertically.

Achieving this requires more work then simply adding a table and allowing iText to decide how to split it in between rows. When you want to split in between columns, you need to organize the layout in your code. This is done using the writeSelectedRows()) method.

In my simple book example, I use these lines:

// draw the first two columns on one page
table.writeSelectedRows(0, 2, 0, -1, 236, 806, canvas);
document.newPage();
// draw the remaining two columns on the next page
table.writeSelectedRows(2, -1, 0, -1, 36, 806, canvas);

First I draw the columns from index 0 to index 2. The column with index 0 is the first column, the column with index 2 is the first column that isn't included, namely the third column. I draw the rows from index 0 (first row) until -1. Minus one means: draw all the remaining rows.

You also see minus one on the next page, where I draw the column with index 2 (the third column) until the column with index -1 (meaning: the rest of the columns).

The values (236, 806) and (36, 806) are coordinates: that's where you want the table to start. You can't define "end coordinates". If the table doesn't fit on the page, iText will just continue drawing the table, even if that means that some content exceeds the visible area of the page. This means that you'll have to be very careful when using this method: you'll need to calculate widths and heights of rows and columns before adding the table, otherwise you may end up with parts of the table that aren't visible.

Altri suggerimenti

This is the source code of a class that splits your table over multiple pages when your columns don't fit in a single page

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;

/**
 * Class that writes a <code>PdfPTable</code>, and spans it across multiple
 * pages if the columns won't fit on one page
 */
public class PdfPTableWriter {

    // Instance variables
    private PdfPTable table;
    private PdfWriter writer;
    private Document document;

    // List of how many columns per horizontal page
    private List numberOfColumnsPerPage;

    // List of how many rows per vertical page
    private List numberOfRowsPerPage;

    // Offsets if given
    private float widthOffset = 20;
    private float heightOffset = 70;

    /**
     * Class Constructor
     */
    public PdfPTableWriter(Document document, PdfWriter writer, PdfPTable table) {
        this.document = document;
        this.writer = writer;
        this.table = table;
        calculateColumns();
        calculateRows();
    }

    /**
     * Writes the table to the document
     */
    public void writeTable() throws DocumentException {
        // Begin at row 1 (row after the header)
        int rowBegin = 1;
        int rowEnd = 0;
        // Note the size of numberOfRowsPerPage is how many vertical
        // pages there are.
        Iterator rowsIter = numberOfRowsPerPage.iterator();
        while (rowsIter.hasNext()) {
            rowEnd = ((Integer) rowsIter.next()).intValue();
            writeSelectedRows(rowBegin, rowEnd);
            rowBegin = rowEnd;
        }
    }

    /**
     * Prints the Report's columns (splitting horizontally if necessary) and
     * subsequent rows
     * 
     * @param rowBegin
     * @param rowEnd
     * @throws DocumentException
     */
    private void writeSelectedRows(int rowBegin, int rowEnd) throws DocumentException {
        int colBegin = 0;
        int colEnd = 0;
        float pageHeight = document.getPageSize().getHeight() - heightOffset;
        PdfContentByte contentByte = writer.getDirectContent();
        Iterator columnsIter = numberOfColumnsPerPage.iterator();
        while (columnsIter.hasNext()) {
            colEnd = colBegin + ((Integer) columnsIter.next()).intValue();
            // Writer table header
            writeSelectedRows(colBegin, colEnd, 0, 1, widthOffset, pageHeight);
            // Writes selected rows to the document
            writeSelectedRows(colBegin, colEnd, rowBegin, rowEnd, widthOffset, pageHeight - table.getRowHeight(0) /*table.getHeaderHeight()*/);
            // Add a new page
            document.newPage();
            colBegin = colEnd;
        }
    }

    public int getTotalPages() {
        return numberOfColumnsPerPage.size() * numberOfRowsPerPage.size();
    }

    public void setHeightOffset(float heightOffset) {
        this.heightOffset = heightOffset;
    }

    public void setWidthOffset(float widthOffset) {
        this.widthOffset = widthOffset;
    }

    private void writeSelectedRows(int colBegin, int colEnd, int rowBegin, int rowEnd, float x, float y) {
        PdfContentByte cb = writer.getDirectContent();
        table.writeSelectedRows(colBegin, colEnd, rowBegin, rowEnd, x, y, cb);
    }

    private void calculateColumns() {
        numberOfColumnsPerPage = new ArrayList();
        float pageWidth = document.getPageSize().getWidth() - widthOffset;
        float[] widths = table.getAbsoluteWidths();
        if (table.getTotalWidth() > pageWidth) {
            // tmp variable for amount of total width thus far
            float tmp = 0f;
            // How many columns for this page
            int columnCount = 0;
            // Current page we're on
            int currentPage = 0;
            // Iterate through the column widths
            for (int i = 0; i < widths.length; i++) {
                // Add to the temporary total
                tmp += widths[i];
                // If this column will not fit on the page
                if (tmp > pageWidth) {
                    // Add the current column count to this page
                    numberOfColumnsPerPage.add(new Integer(columnCount));
                    // Since this column won't fit, the tmp variable should
                    // start off the next iteration
                    // as this column's width
                    tmp = widths[i];
                    // Set column count to 1, since we have moved this column to
                    // the next page
                    columnCount = 1;
                }
                // If this is will fit on the page
                else {
                    // Increase the column count
                    columnCount++;
                }
            }
            // Save the remaining columns
            numberOfColumnsPerPage.add(new Integer(columnCount));
        }

        // All the columns will fit on one horizontal page
        // Note: -1 means all the columns
        else {
            numberOfColumnsPerPage.add(new Integer(-1));
        }
    }

    private void calculateRows() {
        numberOfRowsPerPage = new ArrayList();
        float pageHeight = document.getPageSize().getHeight() - heightOffset - table.getRowHeight(0) /*table.getHeaderHeight()*/;
        // If the table won't fit on the first page
        if (table.getTotalHeight() > pageHeight /*table.getHeaderHeight()*/) {
            // Temp variables
            float tmp = 0f;
            // Determine the start and end rows for each page
            for (int i = 1; i < table.size(); i++) {
                // Add this row's height to the tmp total
                tmp += table.getRowHeight(i);
                if (tmp > pageHeight - (heightOffset/2)) {
                    // This row won't fit so end at previous row
                    numberOfRowsPerPage.add(new Integer(i - 1));
                    // Since this row won't fit, the tmp variable should start
                    // off the next iteration
                    // as this row's height
                    tmp = table.getRowHeight(i);
                }
            }
            // Last page always ends on totalRows
            numberOfRowsPerPage.add(new Integer(table.size()));
        }

        // All the rows will fit on one vertical page
        // Note: -1 means all the rows
        else {
            numberOfRowsPerPage.add(new Integer(-1));
        }
    }
}

Well, I'll try to give you some kind of response. First at all, as @mkl says, 1.4.8 is ancient version. Look at http://sourceforge.net/projects/itext/files/iText/ to get something better, the last version is 5.4.5. And as I know, there is no way to split wide table in two pages. If the document is "a bit" wider than the page - rotate the page, but if you have many columns that don't fit - you have to do it your way and this could be painful. There is no automatic function that can check if your columns have too much text and don't fit the page.

Hope this helps you.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top