1 /* Copyright (C) 2005-2011 Fabio Riccardi */
2 
3 package com.lightcrafts.platform;
4 
5 import com.lightcrafts.model.ImageEditor.ImageEditorEngine;
6 import com.lightcrafts.model.PrintSettings;
7 import com.lightcrafts.utils.thread.ProgressThread;
8 import com.lightcrafts.utils.ProgressIndicator;
9 import com.lightcrafts.jai.utils.Functions;
10 import com.lightcrafts.jai.JAIContext;
11 
12 import com.lightcrafts.mediax.jai.*;
13 import com.lightcrafts.mediax.jai.operator.TransposeDescriptor;
14 import java.awt.print.*;
15 import java.awt.image.Raster;
16 import java.awt.image.BufferedImage;
17 import java.awt.image.WritableRaster;
18 import java.awt.image.renderable.ParameterBlock;
19 import java.awt.*;
20 import java.awt.geom.Point2D;
21 import java.awt.geom.AffineTransform;
22 
23 public class DefaultPrinterLayer implements PrinterLayer {
24     private PageFormat lastPageFormat;
25     private final PrinterJob printJob;
26 
DefaultPrinterLayer()27     DefaultPrinterLayer() {
28         printJob = PrinterJob.getPrinterJob();
29         lastPageFormat = printJob.defaultPage();
30     }
31 
setPageFormat(PageFormat pageFormat)32     public void setPageFormat(PageFormat pageFormat) {
33         // TODO: do we really need to validate?
34         lastPageFormat = printJob.validatePage(pageFormat);
35     }
36 
initialize()37     public void initialize() {
38     }
39 
dispose()40     public void dispose() {
41     }
42 
getPageFormat()43     public PageFormat getPageFormat() {
44         return lastPageFormat;
45     }
46 
pageDialog(PageFormat pageFormat)47     public PageFormat pageDialog(PageFormat pageFormat) {
48         return lastPageFormat = printJob.pageDialog(pageFormat);
49     }
50 
printDialog()51     public boolean printDialog() {
52         return printJob.printDialog();
53     }
54 
setJobName(String name)55     public void setJobName(String name) {
56         printJob.setJobName(name);
57     }
58 
59     private Printer printer = null;
60 
print(ImageEditorEngine engine, ProgressThread thread, PageFormat format, PrintSettings settings)61     public void print(ImageEditorEngine engine, ProgressThread thread,
62                       PageFormat format, PrintSettings settings) throws PrinterException {
63         printer = new Printer(engine, thread, format, settings, printJob);
64 
65         try {
66             printer.doPrint();
67         }
68         finally {
69             printer = null;
70         }
71     }
72 
cancelPrint()73     public void cancelPrint() {
74         Printer thePrinter = printer;
75 
76         if (thePrinter != null)
77             thePrinter.cancelPrint();
78     }
79 
fixPageFormat(PageFormat format)80     public static PageFormat fixPageFormat(PageFormat format) {
81         if (Platform.isMac()) {
82             Paper paper = format.getPaper();
83             paper.setImageableArea(0, 0, paper.getWidth(), paper.getHeight()); //no margins
84             format.setPaper(paper);
85             format = PrinterJob.getPrinterJob().validatePage(format);
86         }
87         return format;
88     }
89 
90     private static class Printer implements Printable {
91         private ImageEditorEngine engine;
92         private ProgressThread thread;
93         private PageFormat format;
94         private PrintSettings settings;
95         private PrinterJob printJob;
96         private boolean printCancelled = false;
97         private boolean fakeLandscape = false;
98 
99         private Point2D printOrigin = new Point2D.Double();
100 
101         private double PRINTER_RESOLUTION;
102 
103         private double printResolution;
104 
Printer(ImageEditorEngine engine, ProgressThread thread, PageFormat format, PrintSettings settings, PrinterJob printJob)105         Printer(ImageEditorEngine engine, ProgressThread thread, PageFormat format,
106                 PrintSettings settings, PrinterJob printJob) {
107             this.engine = engine;
108             this.thread = thread;
109             this.format = format;
110             this.settings = settings;
111             this.printJob = printJob;
112             PRINTER_RESOLUTION = settings.getPixelsPerInch() / 72.0;
113             printResolution = PRINTER_RESOLUTION;
114         }
115 
doPrint()116         void doPrint() throws PrinterException {
117             printCancelled = false;
118 
119             /**
120              * NOTE: Mac OS X has a bug in the landscape printing, this hack rotates te image and prints portrait anyway
121              */
122             int orientation = format.getOrientation();
123             if (orientation != PageFormat.PORTRAIT && Platform.isMac())
124                 fakeLandscape = true;
125 
126             createRendering(settings, thread.getProgressIndicator());
127 
128             printImage = new Functions.sRGBWrapper(printImage);
129 
130             if (!printCancelled && !thread.isCanceled()) {
131                 if (fakeLandscape)
132                     format.setOrientation(PageFormat.PORTRAIT);
133 
134                 printJob.setPrintable(this, format);
135                 printJob.print();
136 
137                 if (fakeLandscape) {
138                     format.setOrientation(orientation);
139                     fakeLandscape = false;
140                 }
141             }
142         }
143 
cancelPrint()144         void cancelPrint() {
145             if (!printCancelled) {
146                 printCancelled = true;
147                 printJob.cancel();
148             }
149         }
150 
151         private PlanarImage printImage = null;
152 
153         private static class PrintResolution {
154             final double resolution;
155             final double scale;
156 
PrintResolution(double resolution, double scale)157             PrintResolution(double resolution, double scale) {
158                 this.resolution = resolution;
159                 this.scale = scale;
160             }
161         }
162 
effectiveResolution(PrintSettings printSettings, Dimension dimension)163         public static PrintResolution effectiveResolution(PrintSettings printSettings, Dimension dimension) {
164             double printResolution = printSettings.getPixelsPerInch() / 72.0;
165 
166             double printScale = Math.min(printResolution * printSettings.getWidth() / dimension.getWidth(),
167                                          printResolution * printSettings.getHeight() / dimension.getHeight());
168 
169             if (printScale > 1) {
170                 printResolution /= printScale;
171                 printScale = 1;
172             }
173 
174             return new PrintResolution(printResolution, printScale);
175         }
176 
177         private ProgressIndicator listener = null;
178 
createRendering(PrintSettings printSettings, ProgressIndicator listener)179         public void createRendering(PrintSettings printSettings, ProgressIndicator listener) {
180             this.listener = listener;
181 
182             Dimension dimension = engine.getNaturalSize();
183 
184             PrintResolution pr = effectiveResolution(printSettings, dimension);
185 
186             printResolution = pr.resolution;
187 
188             double printX = fakeLandscape ? printSettings.getY() : printSettings.getX();
189             double printY = fakeLandscape ? printSettings.getX() : printSettings.getY();
190 
191             printOrigin.setLocation(printX, printY);
192 
193             System.out.println("print scale: " + pr.scale + ", print resolution: " + 72 * printResolution + " dpi");
194 
195             printImage = engine.getRendering(new Dimension((int) (pr.scale * dimension.width),
196                                                            (int) (pr.scale * dimension.height)),
197                                              printSettings.getColorProfile() != null
198                                              ? printSettings.getColorProfile()
199                                              : JAIContext.sRGBColorProfile,
200                                              engine.getLCMSIntent(printSettings.getRenderingIntent()),
201                                              true);
202 
203             if (printImage instanceof RenderedOp) {
204                 RenderedOp rop = (RenderedOp) printImage;
205                 rop.setRenderingHint(JAI.KEY_TILE_CACHE, JAIContext.defaultTileCache);
206             }
207 
208             if (fakeLandscape) {
209                 ParameterBlock params = new ParameterBlock();
210                 params.addSource(printImage);
211                 params.add(TransposeDescriptor.ROTATE_90);
212                 printImage = JAI.create("Transpose", params, null);
213             }
214 
215             if (printResolution != PRINTER_RESOLUTION) {
216                 double scale = PRINTER_RESOLUTION / printResolution;
217 
218                 System.out.println("Uprezzing by " + scale * 100 + '%');
219 
220                 AffineTransform xform = AffineTransform.getScaleInstance(scale, scale);
221 
222                 RenderingHints formatHints = new RenderingHints(JAI.KEY_BORDER_EXTENDER, BorderExtender.createInstance(BorderExtender.BORDER_COPY));
223 
224                 // Do not recycle these tiles, the canvas will cache them
225                 // formatHints.add(new RenderingHints(JAI.KEY_CACHED_TILE_RECYCLING_ENABLED, Boolean.FALSE));
226 
227                 Interpolation interp = Interpolation.getInstance(Interpolation.INTERP_BICUBIC_2);
228                 ParameterBlock params = new ParameterBlock();
229                 params.addSource(printImage);
230                 params.add(xform);
231                 params.add(interp);
232                 printImage = JAI.create("Affine", params, formatHints);
233             }
234 
235             if (!printCancelled) {
236                 System.out.println("print image bounds: " + printImage.getBounds());
237                 // JAIContext.defaultTileCache.flush();
238             } else
239                 System.out.println("cancelled printing");
240         }
241 
242         private boolean firstTime = true;
243 
244         // Implement the Printable interface
print(Graphics g, PageFormat pageFormat, int pageIndex)245         public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
246             if (pageIndex > 0) {
247                 listener = null;
248                 printImage.dispose();
249                 printImage = null;
250                 return NO_SUCH_PAGE;
251             }
252 
253             System.out.println("print image bounds: " + printImage.getBounds());
254 
255             Graphics2D g2d = (Graphics2D) g;
256 
257             AffineTransform at = g2d.getTransform();
258             g2d.scale(1./PRINTER_RESOLUTION, 1./PRINTER_RESOLUTION);
259             g2d.translate(PRINTER_RESOLUTION * printOrigin.getX(), PRINTER_RESOLUTION * printOrigin.getY());
260 
261             g2d.setClip(printImage.getBounds());
262 
263             System.out.println("printing...");
264 
265             if (!firstTime)
266                 listener.setMaximum(printImage.getMaxTileX() * printImage.getMaxTileY());
267 
268             AffineTransform identity = new AffineTransform();
269 
270             // To minimize memory footprint we print one tile at a time, with a one-pixel overlap to avoid scaling artifacts
271 
272             try {
273                 if (!firstTime) {
274                     for (int tileX = 0; !printCancelled && tileX < printImage.getNumXTiles(); tileX++)
275                         for (int tileY = 0; !printCancelled && tileY < printImage.getNumYTiles(); tileY++) {
276                             Raster tile = printImage.getTile(tileX, tileY);
277 
278                             BufferedImage tileImage = new BufferedImage(printImage.getColorModel(),
279                                                                         (WritableRaster) tile.createTranslatedChild(0, 0),
280                                                                         false, null);
281 
282                             g2d.drawRenderedImage(tileImage, AffineTransform.getTranslateInstance(tile.getMinX(),
283                                                                                                   tile.getMinY()));
284 
285                             listener.incrementBy(1);
286                         }
287                 } else
288                     g2d.drawRenderedImage(printImage, identity);
289             } catch (Exception e) {
290                 e.printStackTrace();
291             }
292 
293             if (!firstTime)
294                 listener.setIndeterminate(true);
295 
296             g2d.setTransform(at);
297 
298             System.out.println("...printed!");
299 
300             firstTime = false;
301 
302             return (PAGE_EXISTS);
303         }
304     }
305 }
306