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 }