1   /*
2    *  Copyright (c) 1998-2005, The University of Sheffield.
3    *
4    *  This file is part of GATE (see http://gate.ac.uk/), and is free
5    *  software, licenced under the GNU Library General Public License,
6    *  Version 2, June 1991 (in the distribution as file licence.html,
7    *  and also available at http://gate.ac.uk/gate/licence.html).
8    *
9    *  Valentin Tablan 07/12/2001
10   *
11   *  $Id: JComponentPrinter.java,v 1.6 2005/01/11 13:51:36 ian Exp $
12   *
13   */
14  package gate.print;
15  
16  import java.awt.*;
17  import java.awt.print.*;
18  
19  import javax.swing.JComponent;
20  import javax.swing.text.BadLocationException;
21  import javax.swing.text.JTextComponent;
22  
23  import gate.event.StatusListener;
24  import gate.gui.MainFrame;
25  import gate.util.Err;
26  
27  
28  /**
29   * Will scale the component so it fits on a page horizontally
30   */
31  public class JComponentPrinter implements Pageable{
32  
33    public JComponentPrinter(JComponent component, PageFormat format){
34      this.component = component;
35      this.pageFormat = format;
36      //find the scale factor; we will not enlarge as it would look ugly
37      Rectangle componentBounds = component.getBounds(null);
38      scaleFactor = Math.min(format.getImageableWidth() /componentBounds.width,
39                             1);
40  
41      //calculate the pages count
42      pageCount = (int)((componentBounds.height * scaleFactor +
43                         pageFormat.getImageableHeight() - 1) /
44                         pageFormat.getImageableHeight());
45    }
46  
47    /**
48     * Returns the number of pages over which the canvas
49     * will be drawn.
50     */
51    public int getNumberOfPages() {
52      return pageCount;
53    }
54  
55  
56    /**
57     * Returns the PageFormat of the page specified by
58     * pageIndex. The PageFormat is the same for all pages.
59     *
60     * @param pageIndex the zero based index of the page whose
61     * PageFormat is being requested
62     * @return the PageFormat describing the size and
63     * orientation.
64     * @exception IndexOutOfBoundsException
65     * the Pageable  does not contain the requested
66     * page.
67     */
68    public PageFormat getPageFormat(int pageIndex)
69           throws IndexOutOfBoundsException {
70      if (pageIndex >= pageCount) throw new IndexOutOfBoundsException();
71      return pageFormat;
72    }
73  
74  
75    /**
76     * Returns the <code>Printable</code> instance responsible for
77     * rendering the page specified by <code>pageIndex</code>.
78     *
79     * @param pageIndex the zero based index of the page whose
80     * Printable is being requested
81     * @return the Printable that renders the page.
82     * @exception IndexOutOfBoundsException
83     * the Pageable does not contain the requested
84     * page.
85     */
86    public Printable getPrintable(int pageIndex)
87           throws IndexOutOfBoundsException {
88      if (pageIndex >= pageCount)throw new IndexOutOfBoundsException();
89  
90      double originY = pageIndex * pageFormat.getImageableHeight() / scaleFactor;
91      if(component instanceof JTextComponent){
92        JTextComponent tComp = (JTextComponent)component;
93        //move the origin up towards the first inter-row space
94        int location = tComp.viewToModel(new Point(0, (int)originY));
95        try{
96          Rectangle rect = tComp.modelToView(location);
97          originY = rect.y + rect.height - 1;
98        }catch(BadLocationException ble){
99          ble.printStackTrace(Err.getPrintWriter());
100       }
101     }
102 
103     return new TranslatedPrintable(originY);
104   }
105 
106 
107 /**
108    * This inner class's sole responsibility is to translate
109    * the coordinate system before invoking a canvas's
110    * painter. The coordinate system is translated in order
111    * to get the desired portion of a canvas to line up with
112    * the top of a page.
113    */
114   public class TranslatedPrintable implements Printable {
115     public TranslatedPrintable(double originY){
116       this.originY = originY;
117     }
118 
119     /**
120      * Prints the page at the specified index into the specified
121      * {@link Graphics} context in the specified
122      * format. A PrinterJob calls the
123      * Printableinterface to request that a page be
124      * rendered into the context specified by
125      * graphics. The format of the page to be drawn is
126      * specified by pageFormat. The zero based index
127      * of the requested page is specified by pageIndex.
128      * If the requested page does not exist then this method returns
129      * NO_SUCH_PAGE; otherwise PAGE_EXISTS is returned.
130      * The Graphics class or subclass implements the
131      * {@link PrinterGraphics} interface to provide additional
132      * information. If the Printable object
133      * aborts the print job then it throws a {@link PrinterException}.
134      * @param graphics the context into which the page is drawn
135      * @param pageFormat the size and orientation of the page being drawn
136      * @param pageIndex the zero based index of the page to be drawn
137      * @return PAGE_EXISTS if the page is rendered successfully
138      * or NO_SUCH_PAGE if pageIndex specifies a
139      * non-existent page.
140      * @exception java.awt.print.PrinterException
141      * thrown when the print job is terminated.
142      */
143     public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
144                throws PrinterException {
145 
146       Rectangle componentBounds = component.getBounds(null);
147       Graphics2D g2 = (Graphics2D) graphics;
148       g2.translate(pageFormat.getImageableX() - componentBounds.x,
149                    pageFormat.getImageableY() - originY - componentBounds.y);
150       g2.scale(scaleFactor, scaleFactor);
151 
152       if(component instanceof JTextComponent){
153         JTextComponent tComp = (JTextComponent)component;
154         double nextOriginY = (pageIndex + 1) * pageFormat.getImageableHeight() /
155                              scaleFactor;
156         int location = tComp.viewToModel(new Point(0, (int)nextOriginY));
157         try{
158           Rectangle rect = tComp.modelToView(location);
159           nextOriginY = rect.y;
160         }catch(BadLocationException ble){
161           ble.printStackTrace(Err.getPrintWriter());
162         }
163         Rectangle clip = g2.getClip().getBounds();
164         clip.setSize((int)clip.getWidth(), (int)(nextOriginY - originY) - 1);
165         g2.setClip(clip);
166       }
167 
168       boolean wasBuffered = component.isDoubleBuffered();
169       component.paint(g2);
170       component.setDoubleBuffered(wasBuffered);
171 
172       //fire the events
173       StatusListener sListener = (StatusListener)MainFrame.getListeners().
174                                  get("gate.event.StatusListener");
175       if(sListener != null){
176         sListener.statusChanged("Printing page " + (pageIndex + 1) +
177                                 "/" + pageCount);
178       }
179 
180       return PAGE_EXISTS;
181     }
182 
183     double originY;
184   }
185 
186 
187   JComponent component;
188   PageFormat pageFormat;
189   int pageCount;
190   double scaleFactor;
191 }