Package org.ribax.print

Source Code of org.ribax.print.TablePrinter$TextBox

/*
* RIBAX, Making Web Applications Easy
* Copyright (C) 2006 Damian Hamill and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.ribax.print;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.Font;
import java.awt.geom.Rectangle2D;
import java.awt.geom.AffineTransform;
import java.awt.Shape;
import java.awt.FontMetrics;
import java.awt.geom.Line2D;
import java.awt.Component;

import java.util.Date;
import java.util.Vector;

import javax.swing.table.TableColumnModel;

import java.util.Hashtable;
import java.text.MessageFormat;

import javax.swing.JTable;
import javax.swing.RepaintManager;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.JFrame;
import javax.swing.JScrollPane;

import org.ribax.utils.log.LOG;

/**
* some of this code originally copied from this post
* http://forum.java.sun.com/thread.jspa?threadID=625107&messageID=3572960
*
* <p>
*
* The <code>TablePrinter</code> is used to print a <code>JTable</code>
* with a header, table footer and page footer.  Headers and footers are set as
* options after creating the TablePrinter instance.  The header is 1 or more lines
* of text that are printed at the top of the first page (and optionally at the top of
* every page).  The table footer is 1 or more lines of text that are printed after all
* the table data has been printed.  The page footer is 1 or more lines of text that are
* printed at the foot of every page.
*
*
  <p>
  The following code illustrates how to use this class.
  <pre>
        PrinterJob job = PrinterJob.getPrinterJob();
     
      TablePrinter tablePrinter = new TablePrinter(table,true);
     
      // set the header using a String[]
      String[] header = new String[3];
      header[0] = "This is line 1";
      header[1] = "This is line 2 printed on {1}";
      header[2] = "This is line 3";
      tablePrinter.setOption(TablePrinter.HEADER,header);
     
      // set the table footer using a String
      String tableFooter = "This is the table footer which is printed below the table\n" +
      "lines will be seperated by the newline character.\n" +
      "So this will be printed as 3 lines - printed on page {0}";
      tablePrinter.setOption(TablePrinter.TABLE_FOOTER,tableFooter);
     
      // set the page footer using a MessageFormat Object
      MessageFormat mform = new MessageFormat("Page {0} printed on {1}.");
      tablePrinter.setOption(TablePrinter.PAGE_FOOTER,mform);
     
     
      // set drawing options on tablePrinter (these are the defaults)
      tablePrinter.setOption(TablePrinter.SHOW_HORIZONTAL_LINES,true);
      tablePrinter.setOption(TablePrinter.SHOW_VERTICAL_LINES,true);
      tablePrinter.setOption(TablePrinter.SHOW_GRID,true);
     
      job.setPrintable( tablePrinter);
     
      PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
     
      // add attributes
      //aset.add(OrientationRequested.LANDSCAPE);
      //aset.add(new Copies(2));
      //aset.add(new MediaSize.getMediaSizeForName(MediaSizeName.ISO_A4));
      //aset.add(MediaName.ISO_A4_WHITE);  //aset.add(MediaName.ISO_A4_TRANSPARENT);
      //aset.add(MediaSizeName.ISO_A4);
      //aset.add(Sides.ONE_SIDED);
           

     
      if(job.printDialog(aset)){
        try{
                setCursor( Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
          printJob.print(aset);
        }catch (Exception PrintException) {
          // output error message
        } finally {
                setCursor( Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            }
      }
     
       
  </pre>
    <p>
   
    The header and footers can be <code>MessageFormat</code> objects,
    <code>String</code> objects or <code>String[]</code> objects.  Whichever type of object they are
    they are formatted using
    <p>
    Object[] objs = new Object[]{new Integer(pageIndex + 1), new Date()};<br>
    MessageFormat.format(line,objs);
    <p>
    where line is either the <code>MessageFormat</code> object, the <code>String</code>
    object or each element of the <code>String[]</code> array.
   
*
* @version <tt>$Revision: $</tt>
* @author  <a href="mailto:damian@ribax.org">Damian Hamill</a>
*/

public class TablePrinter implements Printable {


    /** The table to print. */
    private JTable table;

    /** For quick reference to the table's header. */
    private JTableHeader columnHeader;

    /** For quick reference to the table's column model. */
    private TableColumnModel colModel;

    /** Used to store an area of the table to be printed. */
    private final Rectangle tableClip = new Rectangle(0, 0, 0, 0);

    /** Used to store an area of the table's header to be printed. */
    private final Rectangle columnHeaderClip = new Rectangle(0, 0, 0, 0);

    /** Saves the creation of multiple rectangles. */
    private final Rectangle tempRect = new Rectangle(0, 0, 0, 0);

    /** To save multiple calculations of total column width. */
    private int totalColWidth;

    /** The printing mode of this printable. */
    private JTable.PrintMode printMode;
  
    /** The most recent page index asked to print. */
    private int last = -1;

    /** The next row to print. */
    private int row = 0;

    /** The next column to print. */
    private int col = 0;

    /** Vertical space to leave between table and header/footer text. */
    private static final int H_F_SPACE = 8;

    /** default Font size for the header text. */
    private static final float HEADER_FONT_SIZE = 16.0f;

    /** default Font size for the footer text. */
    private static final float FOOTER_FONT_SIZE = 12.0f;
  
    /** used to store printing options */
    private Hashtable<String,Object> options = new Hashtable<String,Object>();
      
    /** whether to make a copy of the JTable */
    private boolean copyTable = false;
   
    // option names
    /**  identifies a <code>Boolean</code> that indicates whether the table draws horizontal lines between cells.*/
    public static final String SHOW_HORIZONTAL_LINES = "SHOW_HORIZONTAL_LINES"; //$NON-NLS-1$
    /**  identifies  a <code>Boolean</code> that indicates whether the table draws vertical lines between cells.*/
    public static final String SHOW_VERTICAL_LINES = "SHOW_VERTICAL_LINES"; //$NON-NLS-1$
    /**   identifies a <code>Boolean</code> that indicates whether the table draws grid lines around cells. */
    public static final String SHOW_GRID = "SHOW_GRID_LINES"; //$NON-NLS-1$
    /**  identifies a <code>Boolean</code> that indicates whether to draw a box around the table */
    public static final String DRAW_BORDER = "DRAW_BORDER"; //$NON-NLS-1$
    /**   identifies a <code>Boolean</code> that indicates whether to draw the table header*/
    public static final String DRAW_HEADER = "DRAW_HEADER"; //$NON-NLS-1$
    /**  identifies  a <code>Boolean</code> that indicates whether to draw a separator between the header text and the table */
    public static final String DRAW_SEPARATOR = "DRAW_SEPARATOR"; //$NON-NLS-1$

    // headers and footers are text that appear above and below the table
    // the HEADER here does not refer to the table JTableHeader
    /**  identifies a <code>Boolean</code> that indicates whether to print the header text on every page */
    public static final String HEADER_EVERY_PAGE = "HEADER_EVERY_PAGE"; //$NON-NLS-1$
      
    /**  identifies the header <code>String</code>, <code>String[]</code> or <code>MessageFormat</code>*/
    public static final String HEADER = "HEADER"; //$NON-NLS-1$
    /**  identifies the page footer <code>String</code>, <code>String[]</code> or <code>MessageFormat</code>
     *  that goes on every page */
    public static final String PAGE_FOOTER = "PAGEFOOTER"; //$NON-NLS-1$

    /**  identifies the table footer <code>String</code>,
     * <code>String[]</code> or <code>MessageFormat</code> that is printed
     * below the table data */
    public static final String TABLE_FOOTER = "TABLEFOOTER"; //$NON-NLS-1$
   
    /**  identifies the <code>Color</code> to use for header text */
    public static final String HEADER_COLOUR = "HEADER_COLOUR"; //$NON-NLS-1$
    /**  identifies a <code>Boolean</code> that indicates whether to center the header text */
    public static final String CENTER_HEADER = "CENTER_HEADER"; //$NON-NLS-1$
    /**  identifies the <code>Font</code> to use for header text */
    public static final String HEADER_FONT = "HEADER_FONT"; //$NON-NLS-1$
    /**  identifies the <code>Font</code> to use for the table text */
    public static final String TABLE_FONT = "TABLE_FONT"; //$NON-NLS-1$
   
    /**  identifies the <code>Color</code> to use for the page footer text */
    public static final String PAGEFOOTER_COLOUR = "PAGEFOOTER_COLOUR";     //$NON-NLS-1$
    /**  identifies a <code>Boolean</code> that indicates whether to center the page footer text */
    public static final String CENTER_PAGEFOOTER = "CENTER_PAGEFOOTER"; //$NON-NLS-1$
    /**  identifies the <code>Font</code> to use for the page footer text */
    public static final String PAGEFOOTER_FONT = "PAGEFOOTER_FONT"; //$NON-NLS-1$

    /**  identifies the <code>Color</code> to use for the table footer text */
    public static final String TABLEFOOTER_COLOUR = "TABLEFOOTER_COLOUR";     //$NON-NLS-1$
    /**  identifies a <code>Boolean</code> that indicates whether to center the table footer text */
    public static final String CENTER_TABLEFOOTER = "CENTER_TABLEFOOTER"; //$NON-NLS-1$
    /**  identifies the <code>Font</code> to use for the table footer text */
    public static final String TABLEFOOTER_FONT = "TABLEFOOTER_FONT"; //$NON-NLS-1$

    private final String HEADER_TEXTBOX = "HEADER_TEXTBOX"; //$NON-NLS-1$
    private final String TABLEFOOTER_TEXTBOX = "TABLEFOOTER_TEXTBOX"; //$NON-NLS-1$
    private final String PAGEFOOTER_TEXTBOX = "PAGEFOOTER_TEXTBOX"; //$NON-NLS-1$
   
    /**
     * Constructs a  <code>TablePrinter<code> initialised with table as the
     * <code>JTable</code> that will be printed
     *
     * @param  table         the table to print
     */
    public TablePrinter(JTable table) {
      this(table,false);
    }
    /**
     * Constructs a  <code>TablePrinter<code> initialised with old_table as the
     * <code>JTable</code> that will be printed and makes a copy of the table to
     * the originals display properties are not modified
     *
     * @param  old_table         the table to print
     * @param  copyTable     make a copy of the table to preserve the original
     */
    public TablePrinter(JTable old_table, boolean copyTable) {
     
      // create a new JTable to work with so the original is not modified
      // when we modify the display properties and column widths
     
      this.copyTable = copyTable;
     
      if (copyTable) {
        this.table = new JTable(old_table.getModel());
       
        // set the column widths, editor and renderer to match the old table
        TableColumn oldcol, newcol;
       
        for (int i = 0; i < table.getModel().getColumnCount(); i++) {
          oldcol = old_table.getColumnModel().getColumn(i);
          newcol = table.getColumnModel().getColumn(i);
         
          newcol.setPreferredWidth(oldcol.getPreferredWidth());
          newcol.setCellEditor(oldcol.getCellEditor());
          newcol.setCellRenderer(oldcol.getCellRenderer());
        }
       
        // set the preferred row height
        int height = old_table.getRowHeight();      
        if (height > 0)
          table.setRowHeight(height);
       
        JFrame frm = new JFrame();
        frm.setContentPane(new JScrollPane(table));
        frm.pack();
      } else
        this.table = old_table;
     
        columnHeader = table.getTableHeader();
        colModel = table.getColumnModel();
        totalColWidth = colModel.getTotalColumnWidth();
       
        if (columnHeader != null) {
          // the header clip height can be set once since it's unchanging
            columnHeaderClip.height = columnHeader.getHeight();
        }
               
        Font tableFont = table.getFont();
    
        // derive the header and footer font from the table's font
        Font headerFont = tableFont.deriveFont(Font.BOLD, HEADER_FONT_SIZE);
        Font footerFont = tableFont.deriveFont(Font.PLAIN, FOOTER_FONT_SIZE);
       
        // set the default options
        setOption(TABLE_FONT,tableFont);
        setOption(HEADER_FONT,headerFont);
        setOption(PAGEFOOTER_FONT,footerFont);
        setOption(TABLEFOOTER_FONT,footerFont);
       
         setOption(SHOW_HORIZONTAL_LINES,new Boolean(true));
      setOption(SHOW_VERTICAL_LINES,new Boolean(true));
      setOption(SHOW_GRID,new Boolean(true));
     
         setOption(HEADER_EVERY_PAGE,new Boolean(false));
        
         setOption(CENTER_HEADER,new Boolean(false));
         setOption(CENTER_PAGEFOOTER,new Boolean(true));
         setOption(CENTER_TABLEFOOTER,new Boolean(false));
        
         setOption(DRAW_BORDER,new Boolean(true));
         setOption(DRAW_HEADER,new Boolean(true));
         setOption(DRAW_SEPARATOR,new Boolean(false));

         setOption(HEADER_COLOUR,Color.BLACK);
         setOption(PAGEFOOTER_COLOUR,Color.BLACK);
         setOption(TABLEFOOTER_COLOUR,Color.BLACK);
    }
                
    /**
     * Set a table printing option.  The option should be one of the
     * public static String fields that identify the option to set. 
     * The setting object type must be appropriate for the option name.
     *
     * <code>
     *
     * @param option  the option name to set
     * @param setting the object to set for the option
     */
    public void setOption(String option, Object setting) {
      options.put(option,setting);
    }

   
    private static final String SHL = "SHL"; //$NON-NLS-1$
    private static final String SVL = "SVL"; //$NON-NLS-1$
    private static final String SG = "SG"; //$NON-NLS-1$
    private static final String WIDTHS = "WIDTHS"; //$NON-NLS-1$

    // save the current lines and grid and column widths
    private void saveOptions() {
     
      // check to see if the options have already been saved
      if (options.get(SHL) != null)
        return;
     
      options.put(SHL,new Boolean(table.getShowHorizontalLines()));
      options.put(SVL,new Boolean(table.getShowVerticalLines()));
      if (table.getShowHorizontalLines() && table.getShowVerticalLines())
        options.put(SG,new Boolean(true));
      else
        options.put(SG,new Boolean(false));
     
      // save the original column widths
     
      int colCount = colModel.getColumnCount();

      int[] colWidths = new int[colCount];

        for ( int i = 0; i < colCount; i++ ) {
          TableColumn col = colModel.getColumn(i);
          colWidths[i] = col.getWidth();
        }
       
        options.put(WIDTHS,colWidths);
    }

    // reset the tables lines and grid and column widths
    private void resetOptions() {
      table.setShowHorizontalLines( ((Boolean)options.get(SHL)).booleanValue() );
    table.setShowVerticalLines( ((Boolean)options.get(SVL)).booleanValue() );
    table.setShowGrid( ((Boolean)options.get(SG)).booleanValue() );
   
    // restore the original column widths
      int colCount = colModel.getColumnCount();
      int[] colWidths = (int[])options.get(WIDTHS);

        for ( int i = 0; i < colCount; i++ ) {
          TableColumn col = colModel.getColumn(i);
          col.setWidth(colWidths[i]);
        }
    }
   
   
    // set the lines and grid on the table
    private void setPrintOptions() {
      table.setShowHorizontalLines( ((Boolean)options.get(SHOW_HORIZONTAL_LINES)).booleanValue() );
    table.setShowVerticalLines( ((Boolean)options.get(SHOW_VERTICAL_LINES)).booleanValue() );
    table.setShowGrid( ((Boolean)options.get(SHOW_GRID)).booleanValue() );
    }
    // get the rectangle bounds of the given text with the given font
    private Rectangle2D getTextRectangle(Graphics graphics,String[] text, Font font) {
     
        graphics.setFont(font);
        FontMetrics metrics = graphics.getFontMetrics();

    // get the bounds for each line and add them together
      Rectangle2D rect = metrics.getStringBounds(text[0],graphics);
     
    for(int i = 1; i < text.length; i++) {
      Rectangle2D tRect = metrics.getStringBounds(text[i],graphics);
      rect.setRect(rect.getX(),rect.getY(),
          Math.max(rect.getWidth(),tRect.getWidth()),
          rect.getHeight() + tRect.getHeight());
    }
   
    return rect;
    }
   
    private int count = 0;
    private boolean resized = false;

    /**
     * Prints the specified page of the table into the given {@link Graphics}
     * context, in the specified format.
     *
     * @param   graphics    the context into which the page is drawn
     * @param   pageFormat  the size and orientation of the page being drawn
     * @param   pageIndex   the zero based index of the page to be drawn
     * @return  PAGE_EXISTS if the page is rendered successfully, or
     *          NO_SUCH_PAGE if a non-existent page index is specified
     * @throws  PrinterException if an error causes printing to be aborted
     */
    public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
    throws PrinterException {
     
      if (copyTable == false)
        saveOptions();
     
      setPrintOptions();
     
      calculateText(graphics,pageIndex);
     
        // Turn off double-buffering to speed up printing.
        RepaintManager.currentManager(table).setDoubleBufferingEnabled(false);

        // keep track of how many times this method is called
      count++;
     
        // for easy access to these values
        final int imgWidth = (int)pageFormat.getImageableWidth();
        final int imgHeight = (int)pageFormat.getImageableHeight();
        if (imgWidth <= 0) {
            throw new PrinterException(Messages.getString("TablePrinter.27")); //$NON-NLS-1$
        }
       
        boolean drawSeparator = ((Boolean)options.get(DRAW_SEPARATOR)).booleanValue();
   
        // if we are not drawing the table column header then reset the header clip height
        if (((Boolean)options.get(DRAW_HEADER)).booleanValue() == false) {
          columnHeader = null;
          columnHeaderClip.height = 0;
        }
       
        // the amount of vertical space available for printing the table
        int availableSpace = imgHeight;

        TextBox t;
        // get the header TextBox dimensions
        if ((t = (TextBox)options.get(HEADER_TEXTBOX)) != null)
          availableSpace -= t.getSpace() + H_F_SPACE;
       
        // get the page footer TextBox dimensions
        if ((t = (TextBox)options.get(PAGEFOOTER_TEXTBOX)) != null)
          availableSpace -= t.getSpace() + H_F_SPACE;
       
        if (drawSeparator)
          availableSpace -= H_F_SPACE * 2;
       
        if (availableSpace <= 0) {
            throw new PrinterException(Messages.getString("TablePrinter.28")); //$NON-NLS-1$
        }
       
        // 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 ( totalColWidth > imgWidth) {
             // only try resizing the columns once
          if (resized == false)
            sf = resizeColumns(imgWidth);
          else
            sf = (double)imgWidth / (double)totalColWidth;
        }
       
        Graphics2D g2d = (Graphics2D)graphics;
       
        // to save and store the transform
        AffineTransform oldTrans;
       
        // 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 (row >= table.getRowCount() && col == 0) {
              // Turn double-buffering back on to ensure display doesn't flicker.
                RepaintManager.currentManager(table).setDoubleBufferingEnabled(true);

              // something causes this method to be called many times per page ~1,000 - must be a bug
              if (sf < 1.0D && count > (pageIndex +1)) {
                // print some info
                  LOG.info(Messages.getString("TablePrinter.29")+count+Messages.getString("TablePrinter.30")+pageIndex+Messages.getString("TablePrinter.31")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                  LOG.info(Messages.getString("TablePrinter.32")+sf+Messages.getString("TablePrinter.33")); //$NON-NLS-1$ //$NON-NLS-2$
              }
             
              if (copyTable == false)
                resetOptions();
             
                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 - columnHeaderClip.height) / sf);
            findNextClip(scaledWidth, scaledHeight);
            last++;
        }
       
        // translate into the co-ordinate system of the pageFormat
        g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
       
        // if there's footer text, print it at the bottom of the imageable area
        if ((t = (TextBox)options.get(PAGEFOOTER_TEXTBOX)) != null) {
          // save the old co-ordinates
            oldTrans = g2d.getTransform();
            // go to the bottom of the page
            g2d.translate(0, imgHeight - t.getSpace());
            // print the text
            printText(g2d, t.getText(), t.getRect(),imgWidth,t.getAttributes());
            // restore old co-ordinates
            g2d.setTransform(oldTrans);
        }
       
        // if there's header text, print it at the top of the imageable area
        // and then translate downwards
        if ((t = (TextBox)options.get(HEADER_TEXTBOX)) != null) {
          // print the text
            printText(g2d, t.getText(), t.getRect(),imgWidth, t.getAttributes());
            // translate y point downwards
            g2d.translate(0, t.getSpace() + H_F_SPACE);
        }
       
        // if we need to print a table seperator draw a line at the
        // current y point and translate downwards
        if (drawSeparator) {
          Line2D.Double ld = new Line2D.Double(0.0,0.0,0.0 +imgWidth,0.0);
          g2d.setColor(Color.BLACK);
          g2d.draw(ld);
          g2d.translate(0,H_F_SPACE);
        }
               
        // print the table footer text before scaling
       
        // if this is the last page and there is a table footer then print it now
        t = (TextBox)options.get(TABLEFOOTER_TEXTBOX);
        if (row >= table.getRowCount() && t != null) {
         
            // determine the available space after painting the table and header
          int tableSpace = columnHeaderClip.height + tableClip.height + H_F_SPACE;
         
          // add the space for the seperator
          if (drawSeparator)
            tableSpace += H_F_SPACE;
         
          int tspace = availableSpace - tableSpace;
         
            if (tspace > t.getSpace()) {              
              // save the old co-ordinates
                oldTrans = g2d.getTransform();
                // go to the bottom of the table
                g2d.translate(0, tableSpace);
                // print the text
                printText(g2d, t.getText(), t.getRect(),imgWidth,t.getAttributes());
                // restore old co-ordinates
                g2d.setTransform(oldTrans);
               
                availableSpace -= t.getSpace();
            }
        }
       
        // if we need to print a table seperator draw a line underneath the table
        if (drawSeparator) {
          Line2D.Double ld = new Line2D.Double(0.0,columnHeaderClip.height +tableClip.height + H_F_SPACE,0.0 +imgWidth,
              columnHeaderClip.height +tableClip.height + H_F_SPACE);
          g2d.setColor(Color.BLACK);
          g2d.draw(ld);
        }
        // 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 - tableClip.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 (columnHeader != null) {
            columnHeaderClip.x = tableClip.x;
            columnHeaderClip.width = tableClip.width;
            g2d.translate(-columnHeaderClip.x, 0);
            g2d.clip(columnHeaderClip);
            columnHeader.print(g2d);
            // restore the original transform and clip
            g2d.setTransform(oldTrans);
            g2d.setClip(oldClip);
            // translate downwards
            g2d.translate(0, columnHeaderClip.height);
        }
       
        // print the current section of the table
        g2d.translate(-tableClip.x, -tableClip.y);
        g2d.clip(tableClip);
        table.print(g2d);
        // restore the original transform and clip
        g2d.setTransform(oldTrans);
        g2d.setClip(oldClip);
       
        // draw a box around the table
        if (((Boolean)options.get(DRAW_BORDER)).booleanValue()) {
          g2d.setColor(Color.BLACK);
          g2d.drawRect(0, 0, tableClip.width, columnHeaderClip.height + tableClip.height);
        }
     
        // Turn double-buffering back on to ensure display doesn't flicker.
        RepaintManager.currentManager(table).setDoubleBufferingEnabled(true);

        return PAGE_EXISTS;

    }
   
    /**
     * A helper method that encapsulates common code for rendering the
     * header and footer text.  Prints multiple lines of text with a defined
     * colour and font and may center the text.
     *
     * @param  g2d       the graphics to draw into
     * @param  text      the text to draw, non null
     * @param  rect      the bounding rectangle for this text,
     *                   as calculated at the given font, non null
     * @param  imgWidth  the width of the area to draw into
     * @param  attr      the printing attribute set
     */
    private void printText(Graphics2D g2d,
            String[] text,
            Rectangle2D rect,
            int imgWidth, Hashtable<String,Object> attr) {

      Color colour;
        if ((colour = (Color)attr.get("Colour")) == null) //$NON-NLS-1$
          colour = Color.BLACK;

      boolean centerText = false;    
      Boolean b = (Boolean)attr.get("Center"); //$NON-NLS-1$
      if (b != null)
        centerText = b.booleanValue();

        Font font = (Font)attr.get("Font"); //$NON-NLS-1$
     
    int ty = (int)Math.ceil(Math.abs(rect.getY()));
       
        Rectangle2D fr ;

    g2d.setColor(colour);
   
    if (font != null)
      g2d.setFont(font);

      for(int i = 0; i < text.length; i++) {
        int tx;
     
        // if the text is small enough to fit, center it
        if (centerText && (rect.getWidth() < imgWidth)) {
          tx = (int)((imgWidth - rect.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);
        }
       
        g2d.drawString(text[i], tx, ty);
        fr = g2d.getFontMetrics().getStringBounds(text[i],g2d);
        ty += fr.getHeight();
      }
    }
   
    /**
     * Calculate the area of the table to be printed for
     * the next page. This should only be called if there
     * are rows and columns left to print.
     *
     * To avoid an infinite loop in printing, this will
     * always put at least one cell on each page.
     *
     * @param  pw  the width of the area to print in
     * @param  ph  the height of the area to print in
     */
    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
                tableClip.x = 0;
            } else {
                // adjust clip to the right of the first column
                tableClip.x = totalColWidth;
            }

            // adjust clip to the top of the next set of rows
            tableClip.y += tableClip.height;
           
            // adjust clip width and height to be zero
            tableClip.width = 0;
            tableClip.height = 0;

            // fit as many rows as possible, and at least one
            int rowCount = table.getRowCount();
            int rowHeight = table.getRowHeight(row);
            do {
                tableClip.height += rowHeight;

                if (++row >= rowCount) {
                    break;
                }

                rowHeight = table.getRowHeight(row);
            } while (tableClip.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) {
            tableClip.x = 0;
            tableClip.width = totalColWidth;
            return;
        }

        if (ltr) {
            // adjust clip to the left of the next set of columns
            tableClip.x += tableClip.width;
        }

        // adjust clip width to be zero
        tableClip.width = 0;

        // fit as many columns as possible, and at least one
        int colCount = table.getColumnCount();
        int colWidth = colModel.getColumn(col).getWidth();
        do {
            tableClip.width += colWidth;
            if (!ltr) {
                tableClip.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 (tableClip.width + colWidth <= pw);

    }

   
    private int lastPageIndex = -1;
    /**
     * Calculate the stuff we need to know for the header and footers.  The print() method
     * can be called over 1,000 times for the same page so we only do the calculations once
     * for each page index.
     *
     * @param graphics the graphics context
     * @param pageIndex the current page number
     * @throws PrinterException
     */
    private void calculateText(Graphics graphics,int pageIndex) throws PrinterException {
     
        boolean printHeader = pageIndex == 0 || ((Boolean)options.get(HEADER_EVERY_PAGE)).booleanValue();
       
      // have we already calculated the text objects for this page
      if (pageIndex == lastPageIndex)
        return;
     
      lastPageIndex = pageIndex;
     
        // to pass the page number when formatting the header and footer text
        Object[] objs = new Object[]{new Integer(pageIndex + 1), new Date()};
        Object text;
       
        // start by removing any previous values
        options.remove(HEADER_TEXTBOX);
        options.remove(PAGEFOOTER_TEXTBOX);
        options.remove(TABLEFOOTER_TEXTBOX);
       
         // do the header
        // fetch the formatted header text, if any
        if ((text = options.get(HEADER))!= null && printHeader) {
           
          // get any specified font
          Font font= (Font)options.get(HEADER_FONT);
                    
            String[] lines = new String[1];
          if (text instanceof MessageFormat) {
            lines[0] = ((MessageFormat)text).format(objs);
          } else {
           
            if (text instanceof String)
              lines = ((String)text).split("\n"); //$NON-NLS-1$
            else if (text instanceof String[])
              lines = (String[])text;
            else
                    throw new PrinterException(Messages.getString("TablePrinter.38")); //$NON-NLS-1$
            
            for(int i = 0; i < lines.length; i++) {
              lines[i] = MessageFormat.format(lines[i],objs);
            }
          }
            TextBox tb = new TextBox(lines,
                getTextRectangle(graphics,lines,font),
                font,
                (Color)options.get(HEADER_COLOUR),
                ((Boolean)options.get(CENTER_HEADER)).booleanValue());
           
            setOption(HEADER_TEXTBOX,tb);
        }
     
      // do the page footer
        // fetch the formatted footer text, if any   
        if ((text = options.get(PAGE_FOOTER))!= null) {
         
          // get any specified font
          Font font = (Font)options.get(PAGEFOOTER_FONT);
                     
            String[] lines = new String[1];
          if (text instanceof MessageFormat) {
            lines[0] = ((MessageFormat)text).format(objs);
          } else {
            if (text instanceof String)
              lines = ((String)text).split("\n"); //$NON-NLS-1$
            else if (text instanceof String[])
              lines = (String[])text;
            else
                    throw new PrinterException(Messages.getString("TablePrinter.40")); //$NON-NLS-1$

            for(int i = 0; i < lines.length; i++) {
              lines[i] = MessageFormat.format(lines[i],objs);
            }
          }
           
            TextBox tb = new TextBox(lines,
                getTextRectangle(graphics,lines,font),
                font,
                (Color)options.get(PAGEFOOTER_COLOUR),
                ((Boolean)options.get(CENTER_PAGEFOOTER)).booleanValue());
           
            setOption(PAGEFOOTER_TEXTBOX,tb);
        }
      // do the table footer

        if ((text = options.get(TABLE_FOOTER)) != null) {
         
          // get any specified font
             Font font = (Font)options.get(TABLEFOOTER_FONT);

          String[] lines = new String[1];           
        if (text instanceof String)
          lines = ((String)text).split("\n"); //$NON-NLS-1$
        else if (text instanceof String[])
          lines = (String[])text;
        else if (text instanceof MessageFormat) {
          lines[0] = ((MessageFormat)text).format(objs);
          }
        else
                throw new PrinterException(Messages.getString("TablePrinter.42")); //$NON-NLS-1$
       
        for(int i = 0; i < lines.length; i++) {
          lines[i] = MessageFormat.format(lines[i],objs);
          }
            TextBox tb = new TextBox(lines,
                getTextRectangle(graphics,lines,font),
                font,
                (Color)options.get(TABLEFOOTER_COLOUR),
                ((Boolean)options.get(CENTER_TABLEFOOTER)).booleanValue());
           
            setOption(TABLEFOOTER_TEXTBOX,tb);
        }
    }
       
    /**
     * Resize the table columns to eliminate scaling to improve performance.
     *
     * Resize the smallest columns first on the basis that larger columns may need to
     * be larger
     *
     * @param imgWidth the width of the printable page
     */
    private double resizeColumns(int imgWidth)
    {    
      resized = true;
     
      int colCount = colModel.getColumnCount();
      TableModel model = table.getModel();
     
        table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );       

        Vector<Integer> indexes = new Vector<Integer>();
       
        // add each column index to the vector in column width order
        for ( int i = 0; i < colCount; i++ ) {
          TableColumn col = colModel.getColumn(i);
          int width = col.getWidth();
          int j = 0;
          for(; j < indexes.size(); j++) {
            Integer colNum = indexes.get(j);
            TableColumn tcol = colModel.getColumn(colNum.intValue());
            if (tcol.getWidth() > width) {
              indexes.insertElementAt(new Integer(i),j);
              break;
            }
          }
          // no columns were smaller than this column so add it to the end
          if (j == indexes.size())
            indexes.add(new Integer(i));
        }
                  
        for ( int i = 0; i < indexes.size(); i++ )
        {
          Integer colNum = indexes.get(i);
        TableColumn tcol = colModel.getColumn(colNum.intValue());
       
         resizeColumn(tcol,colNum.intValue(),model);
       
          // check the total width to see if we can break out of this loop
          totalColWidth = colModel.getTotalColumnWidth();

            if ( totalColWidth <= imgWidth) {
              // we have resized so scaling is no longer needed
                return 1.0D;
            }
        }
       
        return (double)imgWidth / (double)totalColWidth;
    }
    /**
     * Set the width the given column to the max of the header and each cell
     *
     * @param col the column to resize
     */
    private void resizeColumn(TableColumn col,int colNum,TableModel model) {
           
        // get the header renderer
        TableCellRenderer renderer;
       
        if ((renderer = col.getHeaderRenderer()) == null)
          renderer = table.getTableHeader().getDefaultRenderer();
      
        // Initialize the width to the width of the header.
        Component comp = renderer.getTableCellRendererComponent(null, col.getHeaderValue(),
                false, false, 0, 0 );
       
        int newWidth = comp.getPreferredSize().width;

        // get the width of each cell in this column
        for ( int row = 0; row < table.getRowCount(); row++ )
        {
          Object data = model.getValueAt( row, colNum );
         
          if ((renderer = col.getCellRenderer()) == null)
            renderer = table.getDefaultRenderer(data.getClass());
            comp = renderer.getTableCellRendererComponent(table,data,false,false,row,colNum );
           
            newWidth = Math.max( newWidth, comp.getPreferredSize().width );
        }

        // set the new width and allow for a bit of spacing
        int currWidth = col.getWidth();

        // JTable javadoc says use setPreferredWidth but that has no effect
        col.setWidth( Math.min(currWidth,newWidth + 5 ));
    }
   
    /**
     * used to store calculated information about headers and footers
     *
     * @author damian
     *
     */
    private class TextBox {
      String[] text;
      Rectangle2D rect;
      int space;
      Hashtable<String,Object> attr = new Hashtable<String,Object>();
     
      TextBox(String[] text, Rectangle2D rect, Font font, Color colour, boolean center) {
        this.text = text;
        this.rect = rect;
        this.space = (int)Math.ceil(rect.getHeight());
      
            // set up the print attributes
          attr.put("Font",font); //$NON-NLS-1$
          attr.put("Center",center); //$NON-NLS-1$
          attr.put("Colour",colour); //$NON-NLS-1$
      }
     
      String[] getText() {
        return text;
      }
      Rectangle2D getRect() {
        return rect;
      }
      int getSpace() {
        return space;
      }
      Hashtable<String,Object> getAttributes() {
        return attr;
      }
    }   
}
TOP

Related Classes of org.ribax.print.TablePrinter$TextBox

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.