Imprimir JTable

   Anteriormente explicaba como "imprimir en java", y aunque es válido para cualquier componente "swing" muchos me han preguntado como imprimir un jTable, de forma rápida el código es el mismo, pero para simplificar todo:

MessageFormat headerFormat = new MessageFormat("MI CABECERA");
MessageFormat footerFormat = new MessageFormat("- Página {0} -");
Mi_Tabla.print(PrintMode.FIT_WIDTH, headerFormat, footerFormat);

   Si nos vamos a detalles podemos tomar como ejemplo el código públicado en la entrada JTable con Paginación, en la cual vemos como llenar un JTable y colocarle paginación, ahora bien.... Si queremos imprimir esta tabla simplemente debemos agregar al código:

boolean complete = tabla.print();
if (complete) {
    System.out.println("Impresión finalizada!");
} else {
    System.out.println("Error al imprimir");
}

   Pero ¿que pasa si queremos imprimir solo las filas que he seleccionado?, entonces adaptamos un poco más para crear un modelo a partir de la seleccion, enviando a imprimir los datos si existen filas seleccionadas o imprimir toda la tabla si no se ha seleccionado ningún registro:

if(tabla.getSelectedRow() > 0) {
    for (int row : tabla.getSelectedRows()) {
        modeloTemp.addRow((Vector) modeloTabla.getDataVector().get(row));
    }
    JTable tablaFinal = new JTable(modeloTemp);
    tablaFinal.setSize(tablaFinal.getPreferredSize());
    JTableHeader tableHeader = tablaFinal.getTableHeader();
    tableHeader.setSize(tableHeader.getPreferredSize());
    tablaFinal.print(JTable.PrintMode.FIT_WIDTH);
} else {
    MessageFormat headerFormat = new MessageFormat("CABECERA #{0}");
    MessageFormat footerFormat = new MessageFormat("- Página {0} -");
    tabla.print(PrintMode.FIT_WIDTH, headerFormat, footerFormat);
}

   Si quiero utilizar diferentes cabeceras al momento de imprimir entonces tenemos que extender un poco mas nuestra clase implementando la interfáz printable. Lo primero será crear la clase (gracias al usuario aymeric@GitHub):

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);

    }
}

   Una vez tenemos la clase solo tenemos que extender el modelo de la tabla para utilizarla:

tabla = new JTable(new DefaultTableModel(data, columnas)){
@Override
    public Printable getPrintable(PrintMode printMode, MessageFormat headerFormat, MessageFormat footerFormat) {
        return new TablePrintable(this, printMode, headerFormat, footerFormat);
    }
};

   Por último el ejemplo completo sería el siguiente:

import java.awt.*;
import java.awt.geom.*;
import java.awt.print.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.JTable.PrintMode;
import java.text.MessageFormat;

public class jTableConNavegacion extends JFrame {

    //Variables
    int alto;
    final int filas = 11;
    String[] columnas = {"1","2","3","4","5","6","7"};
    Integer[][] data = new Integer[1000][columnas.length];

    //Componentes
    Dimension dimension;
    JButton siguiente;
    JButton anterior;
    JButton imprimir;
    JScrollBar barra;
    JPanel botones;
    JPanel contenedor;

    //Tabla
    final JScrollPane scrollPane;
    final JTable tabla;

    public jTableConNavegacion() {

        //Definimos el contenedor
        super("Imprimiendo un JTable");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocation(100,100);
        setSize(320,262);
        contenedor = new JPanel(new BorderLayout(3,3));

        //Creamos el panel para los botones
        botones = new JPanel(new FlowLayout(FlowLayout.CENTER));
        anterior = new JButton("Anterior");
        siguiente = new JButton("Siguiente");
        imprimir = new JButton("Imprimir");

        //Creamos datos falsos para el ejemplo
        for (int xx=0; xx<data.length; xx++) {
            for (int yy=0; yy<data[0].length; yy++) {
                data[xx][yy] = new Integer((xx+1)*(yy+1));
            }
        }

        //Inicializamos la tabla
        tabla = new JTable(new DefaultTableModel(data, columnas)){
		@Override
			public Printable getPrintable(PrintMode printMode, MessageFormat headerFormat, MessageFormat footerFormat) {
			   return new TablePrintable(this, printMode, headerFormat, footerFormat);
			}
		};

        //Definimos scroll y tamano
        scrollPane = new JScrollPane(
			tabla,
			JScrollPane.VERTICAL_SCROLLBAR_NEVER,
			JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
			dimension = tabla.getPreferredSize();
			scrollPane.setPreferredSize(new Dimension(
				dimension.width,
				tabla.getRowHeight() * filas
			));

        //Cargamos listener para los botones
        siguiente.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent evt) {
                alto = tabla.getRowHeight() * (filas - 1);
                barra = scrollPane.getVerticalScrollBar();
                barra.setValue( barra.getValue() + alto );
            }
        });

        anterior.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent evt) {
                alto = tabla.getRowHeight() * (filas-1);
                barra = scrollPane.getVerticalScrollBar();
                barra.setValue( barra.getValue() - alto );
            }
        });

        imprimir.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent evt) {
                try {
					DefaultTableModel modeloTabla = (DefaultTableModel) tabla.getModel();
					DefaultTableModel modeloTemp = new DefaultTableModel(0, modeloTabla.getColumnCount());

					boolean complete = false;

					if(tabla.getSelectedRow() > 0) {
						for (int row : tabla.getSelectedRows()) {
							modeloTemp.addRow((Vector) modeloTabla.getDataVector().get(row));
						}

						JTable tablaFinal = new JTable(modeloTemp);

						tablaFinal.setSize(tablaFinal.getPreferredSize());
						JTableHeader tableHeader = tablaFinal.getTableHeader();
						tableHeader.setSize(tableHeader.getPreferredSize());

						complete = tablaFinal.print(JTable.PrintMode.FIT_WIDTH);
					} else {
						MessageFormat headerFormat = new MessageFormat("CABECERA #{0}");
						MessageFormat footerFormat = new MessageFormat("- Página {0} -");

						complete = tabla.print(PrintMode.FIT_WIDTH, headerFormat, footerFormat);
					}

                    if (complete) {
						JOptionPane.showMessageDialog(null,"Impresión finalizada!");
                    } else {
						JOptionPane.showMessageDialog(null,"Error al imprimir");
                    }
                } catch (Exception pe) {
					JOptionPane.showMessageDialog(null,"Error al conectar con impresora");
					System.out.println(pe);
                }
            }
        });

        //Preparamos la interfaz
        add(contenedor);
        botones.add(anterior);
        botones.add(siguiente);
        botones.add(imprimir);
        contenedor.add(scrollPane, BorderLayout.CENTER);
        contenedor.add(botones, BorderLayout.SOUTH);
    }

    public static void main(String[] arg) {
        JOptionPane.showMessageDialog(null,"Imprimiendo un JTable....");
        JOptionPane.showMessageDialog(null,"By Carlitox ("+
            "http://calitoxenlaweb.blogspot.com)");
        jTableConNavegacion app = new jTableConNavegacion();
        app.setVisible(true);
    }
}

   Nota: para que este ejemplo funcione hay que tener la clase TablePrintable en el mismo directorio o colocarla dentro del mismo archivo.


10 Comentarios

Escribir Comentario
H4CK-4-LIFE
AUTOR
22 de mayo de 2011, 14:50 delete

hola men que tal yo estoy con eso con la libreria itex men,pero como lo harias dinamicamente que tengas tus datos en jtable y que tome la tabla activa y te genere el pdf..no se si me podrias decir la manera en que se podria lograr algo asii...

Responder
avatar
23 de mayo de 2011, 22:14 delete

Hola, bueno, si con tabla activa te refieres a una tabla dinamica con MySQL o una BD, revisa en mi blog, encontraras varios ejemplos de como llenar una tabla con datos dinámicos, ahora, si necesitas imprimir una buena tabla con datos específicos, sub-reportes y todo lo demás, revisa la entrada "Reportes en iReport" (consta de 4 partes), encontraras como hacer una tabla cruzada y demas...

Responder
avatar
Anónimo
AUTOR
2 de enero de 2012, 22:08 delete

Por favor tutoriales de ireport java eclipse...importante

Responder
avatar
5 de enero de 2012, 13:31 delete

No entiendo a que te refieres, java es java sin importar el IDE que utilices. Me parece que debes documentarte un poco mas o rectificar tu pregunta... Saludos!

Responder
avatar
caruizj
AUTOR
4 de mayo de 2012, 4:13 delete

Buenos días,

Muchas gracias por el aporte, me ha servido de mucho.

En mi caso quiero hacer la cabecera con una imagen y varias lineas. ¿Sabes cómo se podría hacer?

He intentado lo de varías lineas con /n pero no funciona.

Un saludo y mil gracias

Responder
avatar
4 de mayo de 2012, 14:00 delete

Si usas SQL te recomiendo usar iReport, esto es para reportes sencillos, lo que buscas es algo mas avanzado, aquí el enlace: http://carlitoxenlaweb.blogspot.com/2010/09/reportes-en-java-parte-i.html.... saludos!!

Responder
avatar
Anónimo
AUTOR
17 de diciembre de 2012, 15:36 delete

Cordial Saludo,

Ese codigo me finciona pero me imprime todo el Jtable. Sabes como hacer para que imprima solo las filas que yo seleccione?.

Gracias.

Responder
avatar
Unknown
AUTOR
30 de enero de 2017, 13:42 delete

hola yo he usado el metodo que dices funciona bien..pero una consulta como haces para imprimir varias lineas de cabecera usando ese metodo??

Responder
avatar

Lamentablemente hay muchos usuarios en la red que han llegado al blog para escribir obscenidades, así que la moderación se hace necesaria. Recuerda utilizar un lenguaje correcto y espera a que sea aprobado.

Si necesitas publicar código haz click en "Conversión" para hacerlo legible.
ConversiónConversión