com.sun.prism.j2d.print.J2DPrinterJob Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openjfx-78-backport Show documentation
Show all versions of openjfx-78-backport Show documentation
This is a backport of OpenJFX 8 to run on Java 7.
The newest version!
/*
* Copyright (c) 2013, 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 com.sun.prism.j2d.print;
import java.util.ArrayList;
import java.util.Set;
import javafx.scene.*;
import javafx.scene.shape.*;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.print.Pageable;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import javax.print.PrintService;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.ResolutionSyntax;
import javax.print.attribute.Size2DSyntax;
import javax.print.attribute.standard.Chromaticity;
import javax.print.attribute.standard.Copies;
import javax.print.attribute.standard.Destination;
import javax.print.attribute.standard.DialogTypeSelection;
import javax.print.attribute.standard.Media;
import javax.print.attribute.standard.MediaPrintableArea;
import javax.print.attribute.standard.MediaSize;
import javax.print.attribute.standard.MediaSizeName;
import javax.print.attribute.standard.MediaTray;
import javax.print.attribute.standard.OrientationRequested;
import javax.print.attribute.standard.PageRanges;
import javax.print.attribute.standard.PrintQuality;
import javax.print.attribute.standard.PrinterResolution;
import javax.print.attribute.standard.SheetCollate;
import javax.print.attribute.standard.Sides;
import javafx.stage.Window;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import com.sun.javafx.scene.NodeHelper;
import com.sun.javafx.tk.Toolkit;
import com.sun.glass.ui.Application;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javafx.print.JobSettings;
import javafx.print.PageLayout;
import javafx.print.PageOrientation;
import javafx.print.Paper;
import javafx.print.Printer;
import javafx.print.Printer.MarginType;
import javafx.print.PrinterAttributes;
import javafx.print.Collation;
import javafx.print.PageOrientation;
import static javafx.print.PageOrientation.*;
import javafx.print.PageRange;
import javafx.print.PaperSource;
import javafx.print.PrintColor;
import javafx.print.PrintResolution;
import javafx.print.PrintSides;
import com.sun.javafx.print.PrintHelper;
import com.sun.javafx.print.PrinterImpl;
import com.sun.javafx.print.PrinterJobImpl;
import javafx.collections.ObservableList;
import com.sun.javafx.sg.BaseNode;
import com.sun.javafx.sg.PGNode;
import com.sun.prism.j2d.PrismPrintGraphics;
public class J2DPrinterJob implements PrinterJobImpl {
javafx.print.PrinterJob fxPrinterJob;
java.awt.print.PrinterJob pJob2D;
javafx.print.Printer fxPrinter;
J2DPrinter j2dPrinter;
private JobSettings settings;
private PrintRequestAttributeSet printReqAttrSet;
public J2DPrinterJob(javafx.print.PrinterJob fxJob) {
fxPrinterJob = fxJob;
fxPrinter = fxPrinterJob.getPrinter();
j2dPrinter = getJ2DPrinter(fxPrinter);
settings = fxPrinterJob.getJobSettings();
pJob2D = java.awt.print.PrinterJob.getPrinterJob();
printReqAttrSet = new HashPrintRequestAttributeSet();
// dialog selection is a JDK 1.7 attribute.
// We expect to run on 1.8 and above so this should be fine.
printReqAttrSet.add(DialogTypeSelection.NATIVE);
}
public boolean showPrintDialog(Window owner) {
if (jobRunning || jobDone) {
return false;
}
if (GraphicsEnvironment.isHeadless()) {
return true;
}
boolean rv = false;
syncSettingsToAttributes();
if (!Toolkit.getToolkit().isFxUserThread()) {
rv = pJob2D.printDialog(printReqAttrSet);
} else {
rv = showPrintDialogWithNestedLoop(owner);
}
if (rv) {
updateSettingsFromDialog();
}
return rv;
}
private class PrintDialogRunnable implements Runnable {
volatile boolean inNestedLoop = true;
public void run() {
boolean rv = false;
try {
rv = pJob2D.printDialog(printReqAttrSet);
} catch (Exception e) {
} finally {
synchronized (this) {
inNestedLoop = false;
Application.invokeLater(new ExitLoopRunnable(this, rv));
this.notify();
}
}
}
}
private boolean showPrintDialogWithNestedLoop(Window owner) {
PrintDialogRunnable dr = new PrintDialogRunnable();
Thread prtThread = new Thread(dr, "FX Print Dialog Thread");
prtThread.start();
// the nested event loop will return after the runnable exits.
Object rv = null;
synchronized (dr) {
if (dr.inNestedLoop) {
try {
dr.wait();
} catch (InterruptedException e) {
}
rv = Toolkit.getToolkit().enterNestedEventLoop(dr);
}
}
boolean rvbool = false;
try {
rvbool = ((Boolean)rv).booleanValue();
} catch (Exception e) {
}
return rvbool;
}
public boolean showPageDialog(Window owner) {
if (jobRunning || jobDone) {
return false;
}
if (GraphicsEnvironment.isHeadless()) {
return true;
}
boolean rv = false;
syncSettingsToAttributes();
if (!Toolkit.getToolkit().isFxUserThread()) {
PageFormat pf = pJob2D.pageDialog(printReqAttrSet);
rv = pf != null;
} else {
rv = showPageDialogFromNestedLoop(owner);
}
if (rv) {
updateSettingsFromDialog();
}
return rv;
}
private class PageDialogRunnable implements Runnable {
volatile boolean inNestedLoop = true;
public void run() {
PageFormat pf = null;
try {
pf = pJob2D.pageDialog(printReqAttrSet);
} catch (Exception e) {
} finally {
synchronized (this) {
inNestedLoop = false;
Boolean rv = Boolean.valueOf(pf != null);
Application.invokeLater(new ExitLoopRunnable(this, rv));
this.notify();
}
}
}
}
private boolean showPageDialogFromNestedLoop(Window owner) {
PageDialogRunnable dr = new PageDialogRunnable();
Thread prtThread = new Thread(dr, "FX Page Setup Dialog Thread");
prtThread.start();
// the nested event loop will return after the runnable exits.
Object rv = null;
synchronized (dr) {
if (dr.inNestedLoop) {
try {
dr.wait();
} catch (InterruptedException e) {
}
rv = Toolkit.getToolkit().enterNestedEventLoop(dr);
}
}
boolean rvbool = false;
try {
rvbool = ((Boolean)rv).booleanValue();
} catch (Exception e) {
}
return rvbool;
}
/*
* The update-Foo methods here are only used to update the
* FX JobSettings as a result of changes by user interaction
* with a print dialog. The new values are stored in the
* PrintRequestAttributeSet and pulled from there in to the
* equivalent FX public API JobSettings.
*/
private void updateJobName() {
String name = pJob2D.getJobName();
if (!name.equals(settings.getJobName())) {
settings.setJobName(name);
}
}
private void updateCopies() {
int nCopies = pJob2D.getCopies();
if (settings.getCopies() != nCopies) {
settings.setCopies(nCopies);
}
}
private void updatePageRanges() {
PageRanges ranges = (PageRanges)printReqAttrSet.get(PageRanges.class);
// JDK sets default to 1,Integer.MAX_VALUE
// So in this case I think we can just check for non-null and
// only set if its non-null.
if (ranges != null) {
int[][] members = ranges.getMembers();
if (members.length == 1) {
PageRange range = new PageRange(members[0][0], members[0][1]);
settings.setPageRanges(range);
} else if (members.length > 0) {
try {
ArrayList prList = new ArrayList();
int last = 0;
for (int i=0; i printerSet = Printer.getAllPrinters();
for (Printer p : printerSet) {
J2DPrinter p2d = (J2DPrinter)PrintHelper.getPrinterImpl(p);
PrintService s = p2d.getService();
if (s.equals(service)) {
return p;
}
}
return fxPrinter; // current printer.
}
public void setPrinterImpl(PrinterImpl impl) {
j2dPrinter = (J2DPrinter)impl;
fxPrinter = j2dPrinter.getPrinter();
}
public PrinterImpl getPrinterImpl() {
return j2dPrinter;
}
private J2DPrinter getJ2DPrinter(Printer printer) {
return (J2DPrinter)PrintHelper.getPrinterImpl(printer);
}
public Printer getPrinter() {
return fxPrinter;
}
public void setPrinter(Printer printer) {
fxPrinter = printer;
j2dPrinter = getJ2DPrinter(printer);
}
private void updatePrinter() {
PrintService currService = j2dPrinter.getService();
PrintService jobService = pJob2D.getPrintService();
if (currService.equals(jobService)) {
return; // no change
}
Printer newFXPrinter = getFXPrinterForService(jobService);
// The public setPrinter call also updates the job to be valid for
// the new printer. Any old values not supported will be updated
// to supported values. If we do that, then apply the new user
// settings, any listener will see both sets of changes.
// Its best to just see the single transition.
fxPrinterJob.setPrinter(newFXPrinter);
}
private void updateSettingsFromDialog() {
updatePrinter();
updateJobName();
updateCopies();
updatePageRanges();
updateSides();
updateCollation();
updatePageLayout();
updatePaperSource();
updateColor();
updatePrintQuality();
updatePrintResolution();
}
private void syncSettingsToAttributes() {
syncJobName();
syncCopies();
syncPageRanges();
syncSides();
syncCollation();
syncPageLayout();
syncPaperSource();
syncColor();
syncPrintQuality();
syncPrintResolution();
}
private void syncJobName() {
pJob2D.setJobName(settings.getJobName());
}
private void syncCopies() {
pJob2D.setCopies(settings.getCopies());
printReqAttrSet.add(new Copies(settings.getCopies()));
}
private void syncPageRanges() {
printReqAttrSet.remove(PageRanges.class);
PageRange[] prArr = settings.getPageRanges();
if (prArr != null && prArr.length>0) {
int len = prArr.length;
int[][] ranges = new int[len][2];
for (int i=0;i currPageIndex) {
nextPage = waitForNextPage(pageIndex);
} else if (pageIndex < currPageIndex) {
System.err.println("PAGE INDEX DECREASED");
}
return nextPage;
}
public int print(Graphics g, PageFormat pf, int pageIndex) {
if (jobError || jobDone || !getPage(pageIndex)) {
return Printable.NO_SUCH_PAGE;
}
int x = (int)pf.getImageableX();
int y = (int)pf.getImageableY();
int w = (int)pf.getImageableWidth();
int h = (int)pf.getImageableHeight();
Node appNode = currPageInfo.getNode();
g.translate(x, y);
printNode(appNode, g, w, h);
return Printable.PAGE_EXISTS;
}
private void printNode(Node node, Graphics g, int w, int h) {
PrismPrintGraphics ppg =
new PrismPrintGraphics((Graphics2D)g, w, h);
PGNode pgNode = node.impl_getPGNode();
((BaseNode)pgNode).render(ppg);
}
public Printable getPrintable(int pageIndex) {
getPage(pageIndex);
return this;
}
public PageFormat getPageFormat(int pageIndex) {
getPage(pageIndex);
return currPageFormat;
}
/*
* Since we return unknown number of pages, then
* the behaviour must be that we can only signal
* end of the job by returning NO_SUCH_PAGE from
* the print(..) method.
*/
public int getNumberOfPages() {
return Pageable.UNKNOWN_NUMBER_OF_PAGES;
}
private volatile Object elo = null;
/*
* Executed on the application's thread.
* Messages over to the printing thread.
*/
private void implPrintPage(PageLayout pageLayout, Node node) {
/* The public API printPage() is synchronized, so we know
* that the app can't call it from 2 threads at the same
* time, not that this is encouraged either.
* Therefore when we are in this code, we know that any
* previous page rendering has completed.
* We also know that this means the app can't have 'queued up'
* pages.
* So, when we are in here, we know that the app is providing
* the info for the next page.
*/
pageDone = false;
synchronized (monitor) {
newPageInfo = new PageInfo(pageLayout, node);
monitor.notify();
}
if (Toolkit.getToolkit().isFxUserThread()) {
elo = new Object();
Toolkit.getToolkit().enterNestedEventLoop(elo);
elo = null;
} else {
while (!pageDone) {
synchronized (monitor) {
try {
monitor.wait(1000);
} catch (InterruptedException e) {
}
}
}
}
}
} /* END J2DPageable class */
public boolean endJob() {
if (jobRunning && !jobDone &&!jobError) {
jobDone = true;
try {
synchronized (monitor) {
monitor.notify();
return jobDone;
}
} catch (IllegalStateException e) {
System.err.println("Internal Error " + e);
}
} else {
return false;
}
return jobDone;
}
public void cancelJob() {
if (!pJob2D.isCancelled()) {
pJob2D.cancel();
}
jobDone = true;
if (jobRunning) {
jobRunning = false;
try {
synchronized (monitor) {
monitor.notify();
}
} catch (IllegalStateException e) {
System.err.println("Internal Error " + e);
}
}
}
}