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 257 switch (mType) { 258 case DEFAULT: 259 plm = (plm <= 0.75) ? 0.75 : plm; 260 prm = (prm <= 0.75) ? 0.75 : prm; 261 ptm = (ptm <= 0.75) ? 0.75 : ptm; 262 pbm = (pbm <= 0.75) ? 0.75 : pbm; 263 break; 264 case EQUAL: { 265 double maxH = (double)Math.max(plm, prm); 266 double maxV = (double)Math.max(ptm, pbm); 267 double maxM = (double)Math.max(maxH, maxV); 268 plm = prm = ptm = pbm = maxM; 269 break; 270 } 271 case EQUAL_OPPOSITES: { 272 double maxH = (double)Math.max(plm, prm); 273 double maxV = (double)Math.max(ptm, pbm); 274 plm = prm = maxH; 275 ptm = pbm = maxV; 276 break; 277 } 278 case HARDWARE_MINIMUM: 279 default: // Use hardware margins as is. 280 break; 281 } 282 283 double lm, rm, tm, bm; 284 // Now we gave the margins, they need to be adjusted into 285 // the orientation of the paper. If the orientation is not 286 // supported by the printer, then that needs to adjusted first. 287 288 // TBD: Adjust orient to a supported one first. 289 switch (orient) { 290 case LANDSCAPE: lm = pbm; rm = ptm; tm = plm; bm = prm; 291 break; 292 case REVERSE_LANDSCAPE: lm = ptm; rm = pbm; tm = prm; bm = plm; 293 break; 294 case REVERSE_PORTRAIT: lm = prm; rm = plm; tm = pbm; bm = ptm; 295 break; 296 default: lm = plm; rm = prm; tm = ptm; bm = pbm; 297 } 298 lm *= 72; 299 rm *= 72; 300 tm *= 72; 301 bm *= 72; 302 return new PageLayout(paper, orient, lm, rm, tm, bm); 303 } 304 305 /** 306 * Obtain a new PageLayout for this printer using the specified 307 * parameters. 308 * The paper should be one of the supported papers and 309 * the orientation should be a supported orientation. 310 * <p> 311 * Margin values are specified in 1/72 of an inch points. 312 * Margins will be validated against the printer supported margins, 313 * and adjusted if necessary. This method is generally useful to 314 * a client that wants margins that are different (eg wider) 315 * than the default margins, such as 1" at top and bottom and 316 * 0.5" to the left and right. 317 * <p>A client that needs to know what margin values are legal should first 318 * obtain a PageLayout using the <code>HARDWARE_MINIMUM</code> margins. 319 * <p> 320 * If the printer cannot support the layout as specified, it 321 * will adjust the returned layout to a supported configuration 322 * @param paper The paper to use 323 * @param orient The orientation to use 324 * @param lMargin the left margin to use in pts. 325 * @param rMargin the right margin to use in pts. 326 * @param tMargin the top margin to use in pts. 327 * @param bMargin the bottom margin to use in pts. 328 * @return PageLayout based on the specified parameters. 329 * @throws NullPointerException if paper or orient are null. 330 * @throws IllegalArgumentException if any of the margins values are 331 * less than zero. 332 */ 333 public PageLayout createPageLayout(Paper paper, PageOrientation orient, 334 double lMargin, double rMargin, 335 double tMargin, double bMargin) { 336 337 if (paper == null || orient == null) { 338 throw new NullPointerException("Parameters cannot be null"); 339 } 340 if (lMargin < 0 || rMargin < 0 || tMargin < 0 || bMargin < 0) { 341 throw new IllegalArgumentException("Margins must be >= 0"); 342 } 343 // TBD: Adjust paper to a supported one first. 344 Rectangle2D imgArea = impl.printableArea(paper); 345 double width = paper.getWidth() / 72.0; 346 double height = paper.getHeight() / 72.0; 347 double plm = imgArea.getMinX(); 348 double ptm = imgArea.getMinY(); 349 double prm = width - imgArea.getMaxX(); 350 double pbm = height - imgArea.getMaxY(); 351 352 lMargin /= 72.0; 353 rMargin /= 72.0; 354 tMargin /= 72.0; 355 bMargin /= 72.0; 356 357 // Check if the requested margins exceed the paper and 358 // if they do, ignore them. 359 boolean useDefault = false; 360 if (orient == PORTRAIT || orient == REVERSE_PORTRAIT) { 361 if ((lMargin + rMargin > width) || 362 (tMargin + bMargin > height)) { 363 useDefault = true; 364 } 365 } else { 366 if ((lMargin + rMargin > height) || 367 (tMargin + bMargin > width)) { 368 useDefault = true; 369 } 370 } 371 if (useDefault) { 372 return createPageLayout(paper, orient, MarginType.DEFAULT); 373 } 374 375 double lm, rm, tm, bm; 376 // TBD: Adjust orient to a supported one first. 377 switch (orient) { 378 case LANDSCAPE: lm = pbm; rm = ptm; tm = plm; bm = prm; 379 break; 380 case REVERSE_LANDSCAPE: lm = ptm; rm = pbm; tm = prm; bm = plm; 381 break; 382 case REVERSE_PORTRAIT: lm = prm; rm = plm; tm = pbm; bm = ptm; 383 break; 384 default: lm = plm; rm = prm; tm = ptm; bm = pbm; 385 } 386 387 lm = (lMargin >= lm) ? lMargin : lm; 388 rm = (rMargin >= rm) ? rMargin : rm; 389 tm = (tMargin >= tm) ? tMargin : tm; 390 bm = (bMargin >= bm) ? bMargin : bm; 391 392 lm *= 72; 393 rm *= 72; 394 tm *= 72; 395 bm *= 72; 396 397 return new PageLayout(paper, orient, lm, rm, tm, bm); 398 } 399 400 @Override public String toString() { 401 return "Printer " + getName(); 402 } 403 404 static { 405 // This is used by classes in different packages to get access to 406 // private and package private methods. 407 PrintHelper.setPrintAccessor(new PrintHelper.PrintAccessor() { 408 409 @Override 410 public PrintResolution createPrintResolution(int fr, int cfr) { 411 return new PrintResolution(fr, cfr); 412 } 413 414 @Override 415 public Paper createPaper(String paperName, 416 double paperWidth, 417 double paperHeight, 418 Units units) { 419 return new Paper(paperName, paperWidth, paperHeight, units); 420 } 421 422 @Override 423 public PaperSource createPaperSource(String name) { 424 return new PaperSource(name); 425 } 426 427 @Override 428 public JobSettings createJobSettings(Printer printer) { 429 return new JobSettings(printer); 430 } 431 432 /** 433 * PrintAccess is used so that implementation code outside this package 434 * package can construct printer instances using a non-visible API. 435 * We need this since its not valid for applications to create 436 * Printer instances, and we also need to pass in the delegate 437 * impl object which is not intended to be public. 438 */ 439 @Override 440 public Printer createPrinter(PrinterImpl impl) { 441 return new Printer(impl); 442 } 443 444 @Override 445 public PrinterImpl getPrinterImpl(Printer printer) { 446 return printer.getPrinterImpl(); 447 } 448 }); 449 } 450 }