1 /* 2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.print; 27 28 import com.sun.javafx.print.PrintHelper; 29 import javafx.beans.property.ReadOnlyObjectProperty; 30 import javafx.beans.property.ReadOnlyObjectWrapper; 31 import javafx.collections.ObservableSet; 32 import javafx.geometry.Rectangle2D; 33 34 import static javafx.print.PageOrientation.*; 35 36 import com.sun.javafx.tk.PrintPipeline; 37 import com.sun.javafx.print.PrinterImpl; 38 import com.sun.javafx.print.Units; 39 40 /** 41 * A Printer instance represents the destination for a print job. 42 * <p> 43 * Printers may be enumerated and selected for use with a print job. 44 * <p> 45 * The configuration of the printer default settings are then used to 46 * populate the initial settings for a job. 47 * <p> 48 * Since the availability of printers may change during the 49 * execution of a program, due to administrative actions, 50 * a long running program which has cached a printer which 51 * has since been taken off-line, may create a job using that 52 * instance, but printing will fail. 53 * 54 * @since JavaFX 8.0 55 */ 56 public final class Printer { 57 58 /** 59 * Retrieve the installed printers. 60 * The set of printers may be dynamic. 61 * Consequently there is no guarantee that the result will be 62 * the same from call to call, but should change only as 63 * a result of the default changing in the environment of the 64 * application. 65 * <p>Note: since printers may be installed, but offline, then 66 * the application may want to query the status of a printer 67 * before using it. 68 * @return may be null if there are no printers. 69 * @throws SecurityException if the application does not 70 * have permission to browse printers. 71 */ 72 public static ObservableSet<Printer> getAllPrinters() { 73 SecurityManager security = System.getSecurityManager(); 74 if (security != null) { 75 security.checkPrintJobAccess(); 76 } 77 return PrintPipeline.getPrintPipeline().getAllPrinters(); 78 } 79 80 private static ReadOnlyObjectWrapper<Printer> defaultPrinter; 81 82 private static ReadOnlyObjectWrapper<Printer> defaultPrinterImpl() { 83 SecurityManager security = System.getSecurityManager(); 84 if (security != null) { 85 security.checkPrintJobAccess(); 86 } 87 if (defaultPrinter == null) { 88 Printer p = PrintPipeline.getPrintPipeline().getDefaultPrinter(); 89 defaultPrinter = 90 new ReadOnlyObjectWrapper<Printer>(null, "defaultPrinter", p); 91 } 92 return defaultPrinter; 93 } 94 95 /** 96 * A read only object property representing the current default printer. 97 * If there are no installed printers, the wrapped value will be null. 98 * @throws SecurityException if the application does not 99 * have permission to browse printers. 100 */ 101 public static ReadOnlyObjectProperty<Printer> defaultPrinterProperty() { 102 return defaultPrinterImpl().getReadOnlyProperty(); 103 } 104 105 /** 106 * Retrieve the default printer. 107 * May return null if no printers are installed. 108 * <p> 109 * The configuration of available printers may be dynamic. 110 * Consequently there is no guarantee that the result will be 111 * the same from call to call, but should change only as 112 * a result of the default changing in the environment of the 113 * application. 114 * @return default printer or null. 115 * @throws SecurityException if the application does not 116 * have permission to browse printers. 117 */ 118 public static Printer getDefaultPrinter() { 119 return defaultPrinterProperty().get(); 120 } 121 122 private PrinterImpl impl; 123 124 Printer(PrinterImpl impl) { 125 this.impl = impl; 126 impl.setPrinter(this); 127 } 128 129 PrinterImpl getPrinterImpl() { 130 return impl; 131 } 132 133 /** 134 * Return the name used by the underlying system to identify 135 * the printer to users and/or applications. 136 * @return printer name. 137 */ 138 public String getName() { 139 return impl.getName(); 140 } 141 142 private PrinterAttributes attributes; 143 /** 144 * Retrieves the delegate object encapsulating the printer 145 * attributes and capabilities. 146 * @return printer attributes. 147 */ 148 public PrinterAttributes getPrinterAttributes() { 149 if (attributes == null) { 150 attributes = new PrinterAttributes(impl); 151 } 152 return attributes; 153 } 154 155 /** 156 * Returns the default settings for this printer as would be 157 * used in setting up a job for this printer. 158 * <p> 159 * When a job is first created and associated with a printer, 160 * (typically the default printer), it acquires these default 161 * settings for that printer. 162 * This method always returns a new instance. 163 */ 164 JobSettings getDefaultJobSettings() { 165 return impl.getDefaultJobSettings(); 166 } 167 168 /** 169 * The MarginType is used to determine the printable area of a PageLayout. 170 * @since JavaFX 8.0 171 */ 172 public static enum MarginType { 173 /** 174 * This requests a default 0.75 inch margin on all sides. 175 * This is considered to be a common default and is supported 176 * by all known printers. However this may be adjusted if the paper is 177 * too small, to ensure that the margins are not more than 50% of 178 * the smaller dimension. Applications that do expect to deal with 179 * such small media should likely be specifying the required margins 180 * explicitly. 181 * In the unlikely event the hardware margin is larger than 0.75" 182 * it will be adjusted to that same hardware minimum on all sides. 183 */ 184 DEFAULT, 185 /** 186 * Request margins are set to be the smallest on each side that 187 * the hardware allows. This creates the greatest printable area 188 * but the margins may not be aesthetic if they are too small, or 189 * there is significant variation on the different sides of the 190 * paper. 191 * <p> 192 * This is is also useful for an application that wants to know 193 * this so it can construct a new PageLayout that fits within 194 * these margins. 195 */ 196 HARDWARE_MINIMUM, 197 /** 198 * Choose the largest of the four hardware margins, and use that for 199 * all for margins, so that the margins are equal on all sides. 200 */ 201 EQUAL, 202 /** 203 * Similar to <code>EQUAL</code>, but it chooses the larger of 204 * the left/right hardware margins and top/bottom hardware margins 205 * separately, so that the top and bottom margins are equal, and 206 * the left and right margins are equal. 207 */ 208 EQUAL_OPPOSITES, 209 210 }; 211 212 private PageLayout defPageLayout; 213 /** 214 * Return the default page layout for this printer. 215 * @return default page layout. 216 */ 217 public PageLayout getDefaultPageLayout() { 218 if (defPageLayout == null) { 219 PrinterAttributes printerCaps = getPrinterAttributes(); 220 defPageLayout = 221 createPageLayout(printerCaps.getDefaultPaper(), 222 printerCaps.getDefaultPageOrientation(), 223 MarginType.DEFAULT); 224 } 225 return defPageLayout; 226 } 227 228 /** 229 * Obtain a new PageLayout instance for this printer using the specified 230 * parameters. 231 * The paper should be one of the supported papers and 232 * the orientation should be a supported orientation. 233 * If the printer cannot support the layout as specified, it 234 * will adjust the returned layout to a supported configuration 235 * @param paper The paper to use 236 * @param orient The orientation to use 237 * @param mType the margin type to use 238 * @return PageLayout based on the specified parameters. 239 * @throws NullPointerException if any of the parameters are null. 240 */ 241 public PageLayout createPageLayout(Paper paper, PageOrientation orient, 242 MarginType mType) { 243 244 if (paper == null || orient == null || mType == null) { 245 throw new NullPointerException("Parameters cannot be null"); 246 } 247 248 // TBD: Adjust paper to a supported one first. 249 Rectangle2D imgArea = impl.printableArea(paper); 250 double width = paper.getWidth() / 72.0; 251 double height = paper.getHeight() / 72.0; 252 double plm = imgArea.getMinX(); 253 double ptm = imgArea.getMinY(); 254 double prm = width - imgArea.getMaxX(); 255 double pbm = height - imgArea.getMaxY(); 256 // fix for FP error 257 if (Math.abs(plm) < 0.01) plm = 0; 258 if (Math.abs(prm) < 0.01) prm = 0; 259 if (Math.abs(ptm) < 0.01) ptm = 0; 260 if (Math.abs(pbm) < 0.01) pbm = 0; 261 262 switch (mType) { 263 case DEFAULT: 264 plm = (plm <= 0.75) ? 0.75 : plm; 265 prm = (prm <= 0.75) ? 0.75 : prm; 266 ptm = (ptm <= 0.75) ? 0.75 : ptm; 267 pbm = (pbm <= 0.75) ? 0.75 : pbm; 268 break; 269 case EQUAL: { 270 double maxH = (double)Math.max(plm, prm); 271 double maxV = (double)Math.max(ptm, pbm); 272 double maxM = (double)Math.max(maxH, maxV); 273 plm = prm = ptm = pbm = maxM; 274 break; 275 } 276 case EQUAL_OPPOSITES: { 277 double maxH = (double)Math.max(plm, prm); 278 double maxV = (double)Math.max(ptm, pbm); 279 plm = prm = maxH; 280 ptm = pbm = maxV; 281 break; 282 } 283 case HARDWARE_MINIMUM: 284 default: // Use hardware margins as is. 285 break; 286 } 287 288 double lm, rm, tm, bm; 289 // Now we gave the margins, they need to be adjusted into 290 // the orientation of the paper. If the orientation is not 291 // supported by the printer, then that needs to adjusted first. 292 293 // TBD: Adjust orient to a supported one first. 294 switch (orient) { 295 case LANDSCAPE: lm = pbm; rm = ptm; tm = plm; bm = prm; 296 break; 297 case REVERSE_LANDSCAPE: lm = ptm; rm = pbm; tm = prm; bm = plm; 298 break; 299 case REVERSE_PORTRAIT: lm = prm; rm = plm; tm = pbm; bm = ptm; 300 break; 301 default: lm = plm; rm = prm; tm = ptm; bm = pbm; 302 } 303 lm *= 72; 304 rm *= 72; 305 tm *= 72; 306 bm *= 72; 307 return new PageLayout(paper, orient, lm, rm, tm, bm); 308 } 309 310 /** 311 * Obtain a new PageLayout for this printer using the specified 312 * parameters. 313 * The paper should be one of the supported papers and 314 * the orientation should be a supported orientation. 315 * <p> 316 * Margin values are specified in 1/72 of an inch points. 317 * Margins will be validated against the printer supported margins, 318 * and adjusted if necessary. This method is generally useful to 319 * a client that wants margins that are different (eg wider) 320 * than the default margins, such as 1" at top and bottom and 321 * 0.5" to the left and right. 322 * <p>A client that needs to know what margin values are legal should first 323 * obtain a PageLayout using the <code>HARDWARE_MINIMUM</code> margins. 324 * <p> 325 * If the printer cannot support the layout as specified, it 326 * will adjust the returned layout to a supported configuration 327 * @param paper The paper to use 328 * @param orient The orientation to use 329 * @param lMargin the left margin to use in pts. 330 * @param rMargin the right margin to use in pts. 331 * @param tMargin the top margin to use in pts. 332 * @param bMargin the bottom margin to use in pts. 333 * @return PageLayout based on the specified parameters. 334 * @throws NullPointerException if paper or orient are null. 335 * @throws IllegalArgumentException if any of the margins values are 336 * less than zero. 337 */ 338 public PageLayout createPageLayout(Paper paper, PageOrientation orient, 339 double lMargin, double rMargin, 340 double tMargin, double bMargin) { 341 342 if (paper == null || orient == null) { 343 throw new NullPointerException("Parameters cannot be null"); 344 } 345 if (lMargin < 0 || rMargin < 0 || tMargin < 0 || bMargin < 0) { 346 throw new IllegalArgumentException("Margins must be >= 0"); 347 } 348 // TBD: Adjust paper to a supported one first. 349 Rectangle2D imgArea = impl.printableArea(paper); 350 double width = paper.getWidth() / 72.0; 351 double height = paper.getHeight() / 72.0; 352 double plm = imgArea.getMinX(); 353 double ptm = imgArea.getMinY(); 354 double prm = width - imgArea.getMaxX(); 355 double pbm = height - imgArea.getMaxY(); 356 357 lMargin /= 72.0; 358 rMargin /= 72.0; 359 tMargin /= 72.0; 360 bMargin /= 72.0; 361 362 // Check if the requested margins exceed the paper and 363 // if they do, ignore them. 364 boolean useDefault = false; 365 if (orient == PORTRAIT || orient == REVERSE_PORTRAIT) { 366 if ((lMargin + rMargin > width) || 367 (tMargin + bMargin > height)) { 368 useDefault = true; 369 } 370 } else { 371 if ((lMargin + rMargin > height) || 372 (tMargin + bMargin > width)) { 373 useDefault = true; 374 } 375 } 376 if (useDefault) { 377 return createPageLayout(paper, orient, MarginType.DEFAULT); 378 } 379 380 double lm, rm, tm, bm; 381 // TBD: Adjust orient to a supported one first. 382 switch (orient) { 383 case LANDSCAPE: lm = pbm; rm = ptm; tm = plm; bm = prm; 384 break; 385 case REVERSE_LANDSCAPE: lm = ptm; rm = pbm; tm = prm; bm = plm; 386 break; 387 case REVERSE_PORTRAIT: lm = prm; rm = plm; tm = pbm; bm = ptm; 388 break; 389 default: lm = plm; rm = prm; tm = ptm; bm = pbm; 390 } 391 392 lm = (lMargin >= lm) ? lMargin : lm; 393 rm = (rMargin >= rm) ? rMargin : rm; 394 tm = (tMargin >= tm) ? tMargin : tm; 395 bm = (bMargin >= bm) ? bMargin : bm; 396 397 lm *= 72; 398 rm *= 72; 399 tm *= 72; 400 bm *= 72; 401 402 return new PageLayout(paper, orient, lm, rm, tm, bm); 403 } 404 405 @Override public String toString() { 406 return "Printer " + getName(); 407 } 408 409 static { 410 // This is used by classes in different packages to get access to 411 // private and package private methods. 412 PrintHelper.setPrintAccessor(new PrintHelper.PrintAccessor() { 413 414 @Override 415 public PrintResolution createPrintResolution(int fr, int cfr) { 416 return new PrintResolution(fr, cfr); 417 } 418 419 @Override 420 public Paper createPaper(String paperName, 421 double paperWidth, 422 double paperHeight, 423 Units units) { 424 return new Paper(paperName, paperWidth, paperHeight, units); 425 } 426 427 @Override 428 public PaperSource createPaperSource(String name) { 429 return new PaperSource(name); 430 } 431 432 @Override 433 public JobSettings createJobSettings(Printer printer) { 434 return new JobSettings(printer); 435 } 436 437 /** 438 * PrintAccess is used so that implementation code outside this package 439 * package can construct printer instances using a non-visible API. 440 * We need this since its not valid for applications to create 441 * Printer instances, and we also need to pass in the delegate 442 * impl object which is not intended to be public. 443 */ 444 @Override 445 public Printer createPrinter(PrinterImpl impl) { 446 return new Printer(impl); 447 } 448 449 @Override 450 public PrinterImpl getPrinterImpl(Printer printer) { 451 return printer.getPrinterImpl(); 452 } 453 }); 454 } 455 }