javafx.print.Printer Maven / Gradle / Ivy
/*
* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javafx.print;
import com.sun.javafx.print.PrintHelper;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.ObservableSet;
import javafx.geometry.Rectangle2D;
import static javafx.print.PageOrientation.*;
import com.sun.javafx.tk.PrintPipeline;
import com.sun.javafx.print.PrinterImpl;
import com.sun.javafx.print.Units;
/**
* A Printer instance represents the destination for a print job.
*
* Printers may be enumerated and selected for use with a print job.
*
* The configuration of the printer default settings are then used to
* populate the initial settings for a job.
*
* Since the availability of printers may change during the
* execution of a program, due to administrative actions,
* a long running program which has cached a printer which
* has since been taken off-line, may create a job using that
* instance, but printing will fail.
*
* @since JavaFX 8.0
*/
public final class Printer {
/**
* Retrieve the installed printers.
* The set of printers may be dynamic.
* Consequently there is no guarantee that the result will be
* the same from call to call, but should change only as
* a result of the default changing in the environment of the
* application.
*
Note: since printers may be installed, but offline, then
* the application may want to query the status of a printer
* before using it.
* @return may be null if there are no printers.
* @throws SecurityException if the application does not
* have permission to browse printers.
*/
public static ObservableSet getAllPrinters() {
@SuppressWarnings("removal")
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPrintJobAccess();
}
return PrintPipeline.getPrintPipeline().getAllPrinters();
}
private static ReadOnlyObjectWrapper defaultPrinter;
private static ReadOnlyObjectWrapper defaultPrinterImpl() {
@SuppressWarnings("removal")
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPrintJobAccess();
}
if (defaultPrinter == null) {
Printer p = PrintPipeline.getPrintPipeline().getDefaultPrinter();
defaultPrinter =
new ReadOnlyObjectWrapper(null, "defaultPrinter", p);
}
return defaultPrinter;
}
/**
* A read only object property representing the current default printer.
* If there are no installed printers, the wrapped value will be null.
* @return the current default printer
* @throws SecurityException if the application does not
* have permission to browse printers.
*/
public static ReadOnlyObjectProperty defaultPrinterProperty() {
return defaultPrinterImpl().getReadOnlyProperty();
}
/**
* Retrieve the default printer.
* May return null if no printers are installed.
*
* The configuration of available printers may be dynamic.
* Consequently there is no guarantee that the result will be
* the same from call to call, but should change only as
* a result of the default changing in the environment of the
* application.
* @return default printer or null.
* @throws SecurityException if the application does not
* have permission to browse printers.
*/
public static Printer getDefaultPrinter() {
return defaultPrinterProperty().get();
}
private PrinterImpl impl;
Printer(PrinterImpl impl) {
this.impl = impl;
impl.setPrinter(this);
}
PrinterImpl getPrinterImpl() {
return impl;
}
/**
* Return the name used by the underlying system to identify
* the printer to users and/or applications.
* @return printer name.
*/
public String getName() {
return impl.getName();
}
private PrinterAttributes attributes;
/**
* Retrieves the delegate object encapsulating the printer
* attributes and capabilities.
* @return printer attributes.
*/
public PrinterAttributes getPrinterAttributes() {
if (attributes == null) {
attributes = new PrinterAttributes(impl);
}
return attributes;
}
/**
* Returns the default settings for this printer as would be
* used in setting up a job for this printer.
*
* When a job is first created and associated with a printer,
* (typically the default printer), it acquires these default
* settings for that printer.
* This method always returns a new instance.
*/
JobSettings getDefaultJobSettings() {
return impl.getDefaultJobSettings();
}
/**
* The MarginType is used to determine the printable area of a PageLayout.
* @since JavaFX 8.0
*/
public static enum MarginType {
/**
* This requests a default 0.75 inch margin on all sides.
* This is considered to be a common default and is supported
* by all known printers. However this may be adjusted if the paper is
* too small, to ensure that the margins are not more than 50% of
* the smaller dimension. Applications that do expect to deal with
* such small media should likely be specifying the required margins
* explicitly.
* In the unlikely event the hardware margin is larger than 0.75"
* it will be adjusted to that same hardware minimum on all sides.
*/
DEFAULT,
/**
* Request margins are set to be the smallest on each side that
* the hardware allows. This creates the greatest printable area
* but the margins may not be aesthetic if they are too small, or
* there is significant variation on the different sides of the
* paper.
*
* This is is also useful for an application that wants to know
* this so it can construct a new PageLayout that fits within
* these margins.
*/
HARDWARE_MINIMUM,
/**
* Choose the largest of the four hardware margins, and use that for
* all for margins, so that the margins are equal on all sides.
*/
EQUAL,
/**
* Similar to EQUAL
, but it chooses the larger of
* the left/right hardware margins and top/bottom hardware margins
* separately, so that the top and bottom margins are equal, and
* the left and right margins are equal.
*/
EQUAL_OPPOSITES,
};
private PageLayout defPageLayout;
/**
* Return the default page layout for this printer.
* @return default page layout.
*/
public PageLayout getDefaultPageLayout() {
if (defPageLayout == null) {
PrinterAttributes printerCaps = getPrinterAttributes();
defPageLayout =
createPageLayout(printerCaps.getDefaultPaper(),
printerCaps.getDefaultPageOrientation(),
MarginType.DEFAULT);
}
return defPageLayout;
}
/**
* Obtain a new PageLayout instance for this printer using the specified
* parameters.
* The paper should be one of the supported papers and
* the orientation should be a supported orientation.
* If the printer cannot support the layout as specified, it
* will adjust the returned layout to a supported configuration
* @param paper The paper to use
* @param orient The orientation to use
* @param mType the margin type to use
* @return PageLayout based on the specified parameters.
* @throws NullPointerException if any of the parameters are null.
*/
public PageLayout createPageLayout(Paper paper, PageOrientation orient,
MarginType mType) {
if (paper == null || orient == null || mType == null) {
throw new NullPointerException("Parameters cannot be null");
}
// TBD: Adjust paper to a supported one first.
Rectangle2D imgArea = impl.printableArea(paper);
double width = paper.getWidth() / 72.0;
double height = paper.getHeight() / 72.0;
double plm = imgArea.getMinX();
double ptm = imgArea.getMinY();
double prm = width - imgArea.getMaxX();
double pbm = height - imgArea.getMaxY();
// fix for FP error
if (plm < 0.01) plm = 0;
if (prm < 0.01) prm = 0;
if (ptm < 0.01) ptm = 0;
if (pbm < 0.01) pbm = 0;
switch (mType) {
case DEFAULT:
plm = (plm <= 0.75) ? 0.75 : plm;
prm = (prm <= 0.75) ? 0.75 : prm;
ptm = (ptm <= 0.75) ? 0.75 : ptm;
pbm = (pbm <= 0.75) ? 0.75 : pbm;
break;
case EQUAL: {
double maxH = (double)Math.max(plm, prm);
double maxV = (double)Math.max(ptm, pbm);
double maxM = (double)Math.max(maxH, maxV);
plm = prm = ptm = pbm = maxM;
break;
}
case EQUAL_OPPOSITES: {
double maxH = (double)Math.max(plm, prm);
double maxV = (double)Math.max(ptm, pbm);
plm = prm = maxH;
ptm = pbm = maxV;
break;
}
case HARDWARE_MINIMUM:
default: // Use hardware margins as is.
break;
}
while (plm + prm > width) {
plm /= 2.0;
prm /= 2.0;
}
while (ptm + pbm > height) {
ptm /= 2.0;
pbm /= 2.0;
}
double lm, rm, tm, bm;
// Now we gave the margins, they need to be adjusted into
// the orientation of the paper. If the orientation is not
// supported by the printer, then that needs to adjusted first.
// TBD: Adjust orient to a supported one first.
switch (orient) {
case LANDSCAPE: lm = pbm; rm = ptm; tm = plm; bm = prm;
break;
case REVERSE_LANDSCAPE: lm = ptm; rm = pbm; tm = prm; bm = plm;
break;
case REVERSE_PORTRAIT: lm = prm; rm = plm; tm = pbm; bm = ptm;
break;
default: lm = plm; rm = prm; tm = ptm; bm = pbm;
}
lm *= 72;
rm *= 72;
tm *= 72;
bm *= 72;
return new PageLayout(paper, orient, lm, rm, tm, bm);
}
/**
* Obtain a new PageLayout for this printer using the specified
* parameters.
* The paper should be one of the supported papers and
* the orientation should be a supported orientation.
*
* Margin values are specified in 1/72 of an inch points.
* Margins will be validated against the printer supported margins,
* and adjusted if necessary. This method is generally useful to
* a client that wants margins that are different (eg wider)
* than the default margins, such as 1" at top and bottom and
* 0.5" to the left and right.
*
A client that needs to know what margin values are legal should first
* obtain a PageLayout using the HARDWARE_MINIMUM
margins.
*
* If the printer cannot support the layout as specified, it
* will adjust the returned layout to a supported configuration
* @param paper The paper to use
* @param orient The orientation to use
* @param lMargin the left margin to use in pts.
* @param rMargin the right margin to use in pts.
* @param tMargin the top margin to use in pts.
* @param bMargin the bottom margin to use in pts.
* @return PageLayout based on the specified parameters.
* @throws NullPointerException if paper or orient are null.
* @throws IllegalArgumentException if any of the margins values are
* less than zero.
*/
public PageLayout createPageLayout(Paper paper, PageOrientation orient,
double lMargin, double rMargin,
double tMargin, double bMargin) {
if (paper == null || orient == null) {
throw new NullPointerException("Parameters cannot be null");
}
if (lMargin < 0 || rMargin < 0 || tMargin < 0 || bMargin < 0) {
throw new IllegalArgumentException("Margins must be >= 0");
}
// TBD: Adjust paper to a supported one first.
Rectangle2D imgArea = impl.printableArea(paper);
double width = paper.getWidth() / 72.0;
double height = paper.getHeight() / 72.0;
double plm = imgArea.getMinX();
double ptm = imgArea.getMinY();
double prm = width - imgArea.getMaxX();
double pbm = height - imgArea.getMaxY();
lMargin /= 72.0;
rMargin /= 72.0;
tMargin /= 72.0;
bMargin /= 72.0;
// Check if the requested margins exceed the paper and
// if they do, ignore them.
boolean useDefault = false;
if (orient == PORTRAIT || orient == REVERSE_PORTRAIT) {
if ((lMargin + rMargin > width) ||
(tMargin + bMargin > height)) {
useDefault = true;
}
} else {
if ((lMargin + rMargin > height) ||
(tMargin + bMargin > width)) {
useDefault = true;
}
}
if (useDefault) {
return createPageLayout(paper, orient, MarginType.DEFAULT);
}
double lm, rm, tm, bm;
// TBD: Adjust orient to a supported one first.
switch (orient) {
case LANDSCAPE: lm = pbm; rm = ptm; tm = plm; bm = prm;
break;
case REVERSE_LANDSCAPE: lm = ptm; rm = pbm; tm = prm; bm = plm;
break;
case REVERSE_PORTRAIT: lm = prm; rm = plm; tm = pbm; bm = ptm;
break;
default: lm = plm; rm = prm; tm = ptm; bm = pbm;
}
lm = (lMargin >= lm) ? lMargin : lm;
rm = (rMargin >= rm) ? rMargin : rm;
tm = (tMargin >= tm) ? tMargin : tm;
bm = (bMargin >= bm) ? bMargin : bm;
lm *= 72;
rm *= 72;
tm *= 72;
bm *= 72;
return new PageLayout(paper, orient, lm, rm, tm, bm);
}
@Override public String toString() {
return "Printer " + getName();
}
static {
// This is used by classes in different packages to get access to
// private and package private methods.
PrintHelper.setPrintAccessor(new PrintHelper.PrintAccessor() {
@Override
public PrintResolution createPrintResolution(int fr, int cfr) {
return new PrintResolution(fr, cfr);
}
@Override
public Paper createPaper(String paperName,
double paperWidth,
double paperHeight,
Units units) {
return new Paper(paperName, paperWidth, paperHeight, units);
}
@Override
public PaperSource createPaperSource(String name) {
return new PaperSource(name);
}
@Override
public JobSettings createJobSettings(Printer printer) {
return new JobSettings(printer);
}
/**
* PrintAccess is used so that implementation code outside this package
* package can construct printer instances using a non-visible API.
* We need this since its not valid for applications to create
* Printer instances, and we also need to pass in the delegate
* impl object which is not intended to be public.
*/
@Override
public Printer createPrinter(PrinterImpl impl) {
return new Printer(impl);
}
@Override
public PrinterImpl getPrinterImpl(Printer printer) {
return printer.getPrinterImpl();
}
});
}
}