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