Chris@2
|
1 /*
|
Chris@2
|
2 Copyright (C) 2001, 2006 by Simon Dixon
|
Chris@2
|
3
|
Chris@2
|
4 This program is free software; you can redistribute it and/or modify
|
Chris@2
|
5 it under the terms of the GNU General Public License as published by
|
Chris@2
|
6 the Free Software Foundation; either version 2 of the License, or
|
Chris@2
|
7 (at your option) any later version.
|
Chris@2
|
8
|
Chris@2
|
9 This program is distributed in the hope that it will be useful,
|
Chris@2
|
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
Chris@2
|
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
Chris@2
|
12 GNU General Public License for more details.
|
Chris@2
|
13
|
Chris@2
|
14 You should have received a copy of the GNU General Public License along
|
Chris@2
|
15 with this program (the file gpl.txt); if not, download it from
|
Chris@2
|
16 http://www.gnu.org/licenses/gpl.txt or write to the
|
Chris@2
|
17 Free Software Foundation, Inc.,
|
Chris@2
|
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
Chris@2
|
19 */
|
Chris@2
|
20
|
Chris@2
|
21 package at.ofai.music.util;
|
Chris@2
|
22
|
Chris@2
|
23 import java.awt.Component;
|
Chris@2
|
24 import java.awt.Graphics;
|
Chris@2
|
25 import java.awt.Graphics2D;
|
Chris@2
|
26 import java.awt.geom.AffineTransform;
|
Chris@2
|
27 import java.awt.print.PageFormat;
|
Chris@2
|
28 import java.awt.print.Printable;
|
Chris@2
|
29 import java.awt.print.PrinterException;
|
Chris@2
|
30 import java.awt.print.PrinterJob;
|
Chris@2
|
31
|
Chris@2
|
32 import at.ofai.music.util.Format;
|
Chris@2
|
33
|
Chris@2
|
34 /** A utility class for converting graphical user interface components to
|
Chris@2
|
35 * PostScript, which can be sent directly to a printer or printed to a file.
|
Chris@2
|
36 * This gives much higher quality illustrations for articles
|
Chris@2
|
37 * than if a screenshot is used, since scaling should not reduce quality.
|
Chris@2
|
38 * The only requirement is that the component to be printed has a
|
Chris@2
|
39 * <code>paint(Graphics)</code> method.
|
Chris@2
|
40 * <p>There are some bugs in this code which require manual editing of
|
Chris@2
|
41 * the PostScript file. First, there doesn't seem to be any way to include
|
Chris@2
|
42 * the bounding box, although it is possible to calculate it. Second, the
|
Chris@2
|
43 * cliprect produced in the PostScript output is wrong.
|
Chris@2
|
44 * (Check: has this been fixed in more recent Java versions?
|
Chris@2
|
45 * Apparently not, as of 1.5.0, but if scaling is not performed, the cliprect
|
Chris@2
|
46 * is OK and the bounding box correct.)
|
Chris@2
|
47 * See {@link PSPrinter#print(Graphics, PageFormat, int)}
|
Chris@2
|
48 */
|
Chris@2
|
49 public class PSPrinter implements Printable {
|
Chris@2
|
50
|
Chris@2
|
51 /** the component to be converted */
|
Chris@2
|
52 Component component;
|
Chris@2
|
53 /** the desired graphical resolution in pixels per inch */
|
Chris@2
|
54 int resolution; // can't work out how to ask the system
|
Chris@2
|
55
|
Chris@2
|
56 /** Print a GUI component to a PostScript printer or file.
|
Chris@2
|
57 * The 2 forms of this method are the normal ways of accessing this class.
|
Chris@2
|
58 * This form has problems printing some components. It is recommended to
|
Chris@2
|
59 * use the other version.
|
Chris@2
|
60 * @param c the component to be rendered in PostScript
|
Chris@2
|
61 * @param r the resolution of the printer in pixels per inch
|
Chris@2
|
62 */
|
Chris@2
|
63 public static void print(Component c, int r) {
|
Chris@2
|
64 new PSPrinter(c, r).doPrint();
|
Chris@2
|
65 }
|
Chris@2
|
66
|
Chris@2
|
67 /** Print a GUI component to a PostScript printer or file.
|
Chris@2
|
68 * The 2 forms of this method are the normal ways of accessing this class.
|
Chris@2
|
69 * If no resolution is given, the picture is not scaled, and the cliprect
|
Chris@2
|
70 * is then correct. This is the recommended version to use.
|
Chris@2
|
71 * @param c the component to be rendered in PostScript
|
Chris@2
|
72 */
|
Chris@2
|
73 public static void print(Component c) {
|
Chris@2
|
74 new PSPrinter(c, -1).doPrint();
|
Chris@2
|
75 }
|
Chris@2
|
76
|
Chris@2
|
77 /** Constructs a PSPrinter for a given graphical component and resolution.
|
Chris@2
|
78 * @param c the component to be rendered in PostScript
|
Chris@2
|
79 * @param res the resolution of the printer in pixels per inch; set res to
|
Chris@2
|
80 * -1 for no scaling (avoids the apparently buggy cliprect)
|
Chris@2
|
81 */
|
Chris@2
|
82 public PSPrinter(Component c, int res) {
|
Chris@2
|
83 component = c;
|
Chris@2
|
84 resolution = res;
|
Chris@2
|
85 } // constructor
|
Chris@2
|
86
|
Chris@2
|
87 /** Produces a print dialog and executes the requested print job.
|
Chris@2
|
88 * The print job performs its task by callback of the
|
Chris@2
|
89 * {@link PSPrinter#print(Graphics, PageFormat, int)} method.
|
Chris@2
|
90 */
|
Chris@2
|
91 public void doPrint() {
|
Chris@2
|
92 PrinterJob printJob = PrinterJob.getPrinterJob();
|
Chris@2
|
93 printJob.setPrintable(this); // tell it where the rendering code is
|
Chris@2
|
94 if (printJob.printDialog()) {
|
Chris@2
|
95 try {
|
Chris@2
|
96 printJob.print();
|
Chris@2
|
97 } catch (Exception ex) {
|
Chris@2
|
98 ex.printStackTrace();
|
Chris@2
|
99 }
|
Chris@2
|
100 }
|
Chris@2
|
101 } // doPrint()
|
Chris@2
|
102
|
Chris@2
|
103 /** The callback method for performing the printing / Postscript conversion.
|
Chris@2
|
104 * The resulting PostScript file requires some post-editing.
|
Chris@2
|
105 * In particular, there are two problems to be dealt with:
|
Chris@2
|
106 * <p>1) The file has no bounding box. This method prints the correct
|
Chris@2
|
107 * bounding box to stardard output, and it must then be cut and pasted
|
Chris@2
|
108 * into the PostScript file. (There must be a better way!)
|
Chris@2
|
109 * <p>2) The cliprect is wrong (but only if the resolution is specified).
|
Chris@2
|
110 * This is solved by using resolution = -1 or by deleting the lines
|
Chris@2
|
111 * in the PostScript file from <code>newpath</code> to <code>clip</code>.
|
Chris@2
|
112 * (I don't know if this causes problems for components that try to draw
|
Chris@2
|
113 * outside of their area.)
|
Chris@2
|
114 * @param g the graphics object used for painting
|
Chris@2
|
115 * @param f the requested page format (e.g. A4)
|
Chris@2
|
116 * @param pg the page number (must be 0, or we report an error)
|
Chris@2
|
117 * @return the error status; if the page is successfully rendered,
|
Chris@2
|
118 * Printable.PAGE_EXISTS is returned, otherwise if a page number greater
|
Chris@2
|
119 * than 0 is requested, Printable.NO_SUCH_PAGE is returned
|
Chris@2
|
120 * @throws PrinterException thrown when the print job is terminated
|
Chris@2
|
121 */
|
Chris@2
|
122 public int print(Graphics g, PageFormat f, int pg) throws PrinterException {
|
Chris@2
|
123 if (pg >= 1)
|
Chris@2
|
124 return Printable.NO_SUCH_PAGE;
|
Chris@2
|
125 Graphics2D g2 = (Graphics2D) g;
|
Chris@2
|
126 double wd = component.getWidth();
|
Chris@2
|
127 double ht = component.getHeight();
|
Chris@2
|
128 double imwd = f.getImageableWidth();
|
Chris@2
|
129 double imht = f.getImageableHeight();
|
Chris@2
|
130 double corr = resolution / 72.0;
|
Chris@2
|
131 double scaleFactor = corr * Math.min(imwd / wd, imht / ht);
|
Chris@2
|
132 double xmin = f.getImageableX();
|
Chris@2
|
133 double ymin = f.getImageableY();
|
Chris@2
|
134 AffineTransform scale = new AffineTransform(scaleFactor, 0,
|
Chris@2
|
135 0, scaleFactor,
|
Chris@2
|
136 corr * xmin, corr * ymin);
|
Chris@2
|
137 Format.setGroupingUsed(false);
|
Chris@2
|
138 double pgHt = f.getHeight();
|
Chris@2
|
139 if (resolution > 0) {
|
Chris@2
|
140 g2.setTransform(scale);
|
Chris@2
|
141 System.out.println("%%BoundingBox: " +
|
Chris@2
|
142 Format.d(xmin, 0) + " " +
|
Chris@2
|
143 Format.d(pgHt - ymin - ht * scaleFactor / corr, 0) + " " +
|
Chris@2
|
144 Format.d(xmin + wd * scaleFactor / corr, 0) + " " +
|
Chris@2
|
145 Format.d(pgHt - ymin, 0));
|
Chris@2
|
146 } else {
|
Chris@2
|
147 g2.setClip(0, 0, (int)wd, (int)ht);
|
Chris@2
|
148 System.out.println("%%BoundingBox: " +
|
Chris@2
|
149 Format.d(0, 0) + " " +
|
Chris@2
|
150 Format.d(pgHt - ht, 0) + " " +
|
Chris@2
|
151 Format.d(wd, 0) + " " +
|
Chris@2
|
152 Format.d(pgHt, 0));
|
Chris@2
|
153 }
|
Chris@2
|
154
|
Chris@2
|
155 // System.out.println(f.getWidth() + " " + f.getHeight() + " " +
|
Chris@2
|
156 // f.getImageableX() + " " + f.getImageableY() + " " +
|
Chris@2
|
157 // f.getImageableWidth() + " " + f.getImageableHeight());
|
Chris@2
|
158 // Letter = 8.5x11" 612x792pt DEFAULT
|
Chris@2
|
159 // A4 = 210x297mm 595x842pt
|
Chris@2
|
160
|
Chris@2
|
161 // AffineTransform scale = new AffineTransform(2.5, 0, 0, 2.5, 200, 1000);
|
Chris@2
|
162 // g2.setTransform(scale); // The figures need some fiddling.
|
Chris@2
|
163 // In particular, the PostScript file has:
|
Chris@2
|
164 // 1) no bounding box (add manually)
|
Chris@2
|
165 // 2) wrong cliprect (delete lines from
|
Chris@2
|
166 // "newpath" to "clip")
|
Chris@2
|
167 component.printAll(g2);
|
Chris@2
|
168 return Printable.PAGE_EXISTS;
|
Chris@2
|
169 } // print()
|
Chris@2
|
170
|
Chris@2
|
171 } // class PSPrinter
|