mirror of
https://github.com/2003scape/deep-c-rsc.git
synced 2024-03-22 05:49:51 -04:00
614 lines
20 KiB
Java
614 lines
20 KiB
Java
/*
|
|
* This file is modified by Ivan Maidanski <ivmai@ivmaisoft.com>
|
|
* Project name: JCGO-SUNAWT (http://www.ivmaisoft.com/jcgo/)
|
|
*/
|
|
|
|
/*
|
|
* @(#)PrintJob2D.java 1.13 03/01/23
|
|
*
|
|
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
|
|
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
* Copyright 1998, 1999, 2000 Sun Microsystems, Inc. All Rights Reserved.
|
|
*
|
|
* This software is the proprietary information of Sun Microsystems, Inc.
|
|
* Use is subject to license terms.
|
|
*/
|
|
|
|
package sun.print;
|
|
|
|
import java.awt.Dimension;
|
|
import java.awt.Frame;
|
|
import java.awt.Graphics;
|
|
import java.awt.Graphics2D;
|
|
import java.awt.JobAttributes;
|
|
import java.awt.PrintJob;
|
|
import java.awt.PageAttributes;
|
|
|
|
import java.awt.print.PageFormat;
|
|
import java.awt.print.Paper;
|
|
import java.awt.print.Printable;
|
|
import java.awt.print.PrinterException;
|
|
import java.awt.print.PrinterJob;
|
|
|
|
import java.io.File;
|
|
|
|
import java.net.URI;
|
|
import java.net.URISyntaxException;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Properties;
|
|
|
|
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.JobName;
|
|
import javax.print.attribute.standard.MediaSize;
|
|
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 sun.awt.print.PrintDialog;
|
|
import sun.awt.print.AwtPrintControl;
|
|
|
|
/**
|
|
* A class which initiates and executes a print job using
|
|
* the underlying PrinterJob graphics conversions.
|
|
*
|
|
* @see Toolkit#getPrintJob
|
|
*
|
|
*/
|
|
public class PrintJob2D extends PrintJob implements Printable, Runnable {
|
|
|
|
private Frame frame;
|
|
private String docTitle = "";
|
|
private JobAttributes jobAttributes;
|
|
private PageAttributes pageAttributes;
|
|
private PrintRequestAttributeSet attributes;
|
|
|
|
/*
|
|
* Displays the native or cross-platform dialog and allows the
|
|
* user to update job & page attributes
|
|
*/
|
|
private AwtPrintControl printControl;
|
|
|
|
/**
|
|
* The PrinterJob being uses to implement the PrintJob.
|
|
*/
|
|
private PrinterJob printerJob;
|
|
|
|
/**
|
|
* The size of the page being used for the PrintJob.
|
|
*/
|
|
private PageFormat pageFormat;
|
|
|
|
/**
|
|
* The PrinterJob and the application run on different
|
|
* threads and communicate through a pair of message
|
|
* queues. This queue is the list of Graphics that
|
|
* the PrinterJob has requested rendering for, but
|
|
* for which the application has not yet called getGraphics().
|
|
* In practice the length of this message queue is always
|
|
* 0 or 1.
|
|
*/
|
|
private MessageQ graphicsToBeDrawn = new MessageQ("tobedrawn");
|
|
|
|
/**
|
|
* Used to communicate between the application's thread
|
|
* and the PrinterJob's thread this message queue holds
|
|
* the list of Graphics into which the application has
|
|
* finished drawing, but that have not yet been returned
|
|
* to the PrinterJob thread. Again, in practice, the
|
|
* length of this message queue is always 0 or 1.
|
|
*/
|
|
private final MessageQ graphicsDrawn = new MessageQ("drawn");
|
|
|
|
/**
|
|
* The last Graphics returned to the application via
|
|
* getGraphics. This is the Graphics into which the
|
|
* application is currently drawing.
|
|
*/
|
|
private Graphics2D currentGraphics;
|
|
|
|
/**
|
|
* The zero based index of the page currently being rendered
|
|
* by the application.
|
|
*/
|
|
private int pageIndex = -1;
|
|
|
|
/**
|
|
* The thread on which PrinterJob is running.
|
|
* This is different than the applications thread.
|
|
*/
|
|
private Thread printerJobThread;
|
|
|
|
public PrintJob2D(Frame frame, String doctitle,
|
|
final Properties props) {
|
|
|
|
SecurityManager security = System.getSecurityManager();
|
|
if (security != null) {
|
|
security.checkPrintJobAccess();
|
|
}
|
|
|
|
if (frame == null) {
|
|
throw new NullPointerException("Frame must not be null");
|
|
}
|
|
this.docTitle = (doctitle == null) ? "" : docTitle;
|
|
printControl = new AwtPrintControl(frame, doctitle, props);
|
|
}
|
|
|
|
public PrintJob2D(Frame frame, String doctitle,
|
|
JobAttributes jobAttributes,
|
|
PageAttributes pageAttributes) {
|
|
|
|
SecurityManager security = System.getSecurityManager();
|
|
if (security != null) {
|
|
security.checkPrintJobAccess();
|
|
}
|
|
|
|
if (frame == null &&
|
|
(jobAttributes == null ||
|
|
jobAttributes.getDialog() == JobAttributes.DialogType.NATIVE)) {
|
|
throw new NullPointerException("Frame must not be null");
|
|
}
|
|
this.docTitle = (doctitle == null) ? "" : docTitle;
|
|
this.jobAttributes = jobAttributes;
|
|
this.pageAttributes = pageAttributes;
|
|
printControl = new AwtPrintControl(frame, doctitle,
|
|
jobAttributes, pageAttributes);
|
|
}
|
|
|
|
public boolean printDialog() {
|
|
|
|
boolean proceedWithPrint = printControl.displayDialog();
|
|
|
|
if (proceedWithPrint) {
|
|
jobAttributes = printControl.getJobAttributes();
|
|
pageAttributes = printControl.getPageAttributes();
|
|
printerJob = PrinterJob.getPrinterJob();
|
|
if (printerJob == null) {
|
|
return false;
|
|
}
|
|
copyAttributes();
|
|
if (pageFormat == null) {
|
|
pageFormat = printerJob.defaultPage();
|
|
}
|
|
printerJob.setPrintable(this, pageFormat);
|
|
}
|
|
return proceedWithPrint;
|
|
}
|
|
|
|
private PrintService findNamedPrintService(String printerName) {
|
|
|
|
PrintService service = printerJob.getPrintService();
|
|
if (service != null && printerName.equals(service.getName())) {
|
|
return service;
|
|
} else {
|
|
PrintService []services = PrinterJob.lookupPrintServices();
|
|
for (int i=0; i<services.length; i++) {
|
|
if (printerName.equals(services[i].getName())) {
|
|
return services[i];
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/* From JobAttributes we will copy job name and duplex printing
|
|
* and destination.
|
|
* The majority of the rest of the attributes are reflected
|
|
* attributes.
|
|
*
|
|
* From PageAttributes we copy color, media size, orientation,
|
|
* origin type, resolution and print quality.
|
|
* We use the media, orientation in creating the page format, and
|
|
* the origin type to set its imageable area.
|
|
*
|
|
* REMIND: Interpretation of resolution, additional media sizes.
|
|
*/
|
|
private void copyAttributes() {
|
|
|
|
attributes = new HashPrintRequestAttributeSet();
|
|
printerJob.setJobName(docTitle);
|
|
attributes.add(new JobName(docTitle, null));
|
|
|
|
JobAttributes.DestinationType dest = jobAttributes.getDestination();
|
|
if (dest == JobAttributes.DestinationType.PRINTER) {
|
|
String printerName = jobAttributes.getPrinter();
|
|
if (printerName != null && printerName != "") {
|
|
PrintService service = findNamedPrintService(printerName);
|
|
if (service != null) {
|
|
try {
|
|
printerJob.setPrintService(service);
|
|
} catch (PrinterException e) {
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
String fileName = jobAttributes.getFileName();
|
|
if (fileName == null) {
|
|
fileName = "out.prn";
|
|
}
|
|
URI uri = (new File(fileName)).toURI();
|
|
attributes.add(new Destination(uri));
|
|
}
|
|
JobAttributes.SidesType sType = jobAttributes.getSides();
|
|
if (sType == JobAttributes.SidesType.TWO_SIDED_LONG_EDGE) {
|
|
attributes.add(Sides.TWO_SIDED_LONG_EDGE);
|
|
} else if (sType == JobAttributes.SidesType.TWO_SIDED_SHORT_EDGE) {
|
|
attributes.add(Sides.TWO_SIDED_SHORT_EDGE);
|
|
} else if (sType == JobAttributes.SidesType.ONE_SIDED) {
|
|
attributes.add(Sides.ONE_SIDED);
|
|
}
|
|
|
|
JobAttributes.MultipleDocumentHandlingType hType =
|
|
jobAttributes.getMultipleDocumentHandling();
|
|
if (hType ==
|
|
JobAttributes.MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_COLLATED_COPIES) {
|
|
attributes.add(SheetCollate.COLLATED);
|
|
} else {
|
|
attributes.add(SheetCollate.UNCOLLATED);
|
|
}
|
|
|
|
attributes.add(new Copies(jobAttributes.getCopies()));
|
|
|
|
if (pageAttributes.getColor() == PageAttributes.ColorType.COLOR) {
|
|
attributes.add(Chromaticity.COLOR);
|
|
} else {
|
|
attributes.add(Chromaticity.MONOCHROME);
|
|
}
|
|
|
|
pageFormat = printerJob.defaultPage();
|
|
if (pageAttributes.getOrientationRequested() ==
|
|
PageAttributes.OrientationRequestedType.LANDSCAPE) {
|
|
pageFormat.setOrientation(PageFormat.LANDSCAPE);
|
|
}
|
|
|
|
int []mSize = AwtPrintControl.getSize(pageAttributes.getMedia());
|
|
Paper paper = new Paper();
|
|
paper.setSize(mSize[0], mSize[1]);
|
|
if (pageAttributes.getOrigin()==PageAttributes.OriginType.PRINTABLE) {
|
|
// AWT uses 1/4" borders by default
|
|
paper.setImageableArea(18.0, 18.0,
|
|
paper.getWidth()-36.0,
|
|
paper.getHeight()-36.0);
|
|
} else {
|
|
paper.setImageableArea(0.0,0.0,paper.getWidth(),paper.getHeight());
|
|
}
|
|
pageFormat.setPaper(paper);
|
|
|
|
PageAttributes.PrintQualityType qType =
|
|
pageAttributes.getPrintQuality();
|
|
if (qType == PageAttributes.PrintQualityType.DRAFT) {
|
|
attributes.add(PrintQuality.DRAFT);
|
|
} else if (qType == PageAttributes.PrintQualityType.NORMAL) {
|
|
attributes.add(PrintQuality.NORMAL);
|
|
} else if (qType == PageAttributes.PrintQualityType.HIGH) {
|
|
attributes.add(PrintQuality.HIGH);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets a Graphics object that will draw to the next page.
|
|
* The page is sent to the printer when the graphics
|
|
* object is disposed. This graphics object will also implement
|
|
* the PrintGraphics interface.
|
|
* @see PrintGraphics
|
|
*/
|
|
public Graphics getGraphics() {
|
|
|
|
Graphics printGraphics = null;
|
|
|
|
synchronized (this) {
|
|
++pageIndex;
|
|
|
|
if (pageIndex == 0) {
|
|
|
|
/* We start a thread on which the PrinterJob will run.
|
|
* The PrinterJob will ask for pages on that thread
|
|
* and will use a message queue to fulfill the application's
|
|
* requests for a Graphics on the application's
|
|
* thread.
|
|
*/
|
|
|
|
startPrinterJobThread();
|
|
|
|
}
|
|
notify();
|
|
}
|
|
|
|
/* If the application has already been handed back
|
|
* a graphics then we need to put that graphics into
|
|
* the drawn queue so that the PrinterJob thread can
|
|
* return to the print system.
|
|
*/
|
|
if (currentGraphics != null) {
|
|
graphicsDrawn.append(currentGraphics);
|
|
currentGraphics = null;
|
|
}
|
|
|
|
/* We'll block here until a new graphics becomes
|
|
* available.
|
|
*/
|
|
|
|
currentGraphics = graphicsToBeDrawn.pop();
|
|
|
|
if (currentGraphics instanceof PeekGraphics) {
|
|
( (PeekGraphics) currentGraphics).setAWTDrawingOnly();
|
|
graphicsDrawn.append(currentGraphics);
|
|
currentGraphics = graphicsToBeDrawn.pop();
|
|
}
|
|
|
|
|
|
if (currentGraphics != null) {
|
|
|
|
/* In the PrintJob API, the origin is at the upper-
|
|
* left of the imageable area when using the new "printable"
|
|
* origin attribute, otherwise its the physical origin (for
|
|
* backwards compatibility. We emulate this by createing
|
|
* a PageFormat which matches and then performing the
|
|
* translate to the origin. This is a no-op if physical
|
|
* origin is specified.
|
|
*/
|
|
currentGraphics.translate(pageFormat.getImageableX(),
|
|
pageFormat.getImageableY());
|
|
|
|
/* Scale to accomodate AWT's notion of printer resolution */
|
|
double awtScale = 72.0/getPageResolutionInternal();
|
|
currentGraphics.scale(awtScale, awtScale);
|
|
|
|
/* The caller wants a Graphics instance but we do
|
|
* not want them to make 2D calls. We can't hand
|
|
* back a Graphics2D. The returned Graphics also
|
|
* needs to implement PrintGraphics, so we wrap
|
|
* the Graphics2D instance. The PrintJob API has
|
|
* the application dispose of the Graphics so
|
|
* we create a copy of the one returned by PrinterJob.
|
|
*/
|
|
printGraphics = new ProxyPrintGraphics(currentGraphics.create(),
|
|
this);
|
|
|
|
}
|
|
|
|
return printGraphics;
|
|
}
|
|
|
|
/**
|
|
* Returns the dimensions of the page in pixels.
|
|
* The resolution of the page is chosen so that it
|
|
* is similar to the screen resolution.
|
|
* Except (since 1.3) when the application specifies a resolution.
|
|
* In that case it it scaled accordingly.
|
|
*/
|
|
public Dimension getPageDimension() {
|
|
double wid, hgt, scale;
|
|
if (pageAttributes != null &&
|
|
pageAttributes.getOrigin()==PageAttributes.OriginType.PRINTABLE) {
|
|
wid = pageFormat.getImageableWidth();
|
|
hgt = pageFormat.getImageableHeight();
|
|
} else {
|
|
wid = pageFormat.getWidth();
|
|
hgt = pageFormat.getHeight();
|
|
}
|
|
scale = getPageResolutionInternal() / 72.0;
|
|
return new Dimension((int)(wid * scale), (int)(hgt * scale));
|
|
}
|
|
|
|
private double getPageResolutionInternal() {
|
|
if (pageAttributes != null) {
|
|
int []res = pageAttributes.getPrinterResolution();
|
|
if (res[2] == 3) {
|
|
return res[0];
|
|
} else /* if (res[2] == 4) */ {
|
|
return (res[0] * 2.54);
|
|
}
|
|
} else {
|
|
return 72.0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the resolution of the page in pixels per inch.
|
|
* Note that this doesn't have to correspond to the physical
|
|
* resolution of the printer.
|
|
*/
|
|
public int getPageResolution() {
|
|
return (int)getPageResolutionInternal();
|
|
}
|
|
|
|
/**
|
|
* Returns true if the last page will be printed first.
|
|
*/
|
|
public boolean lastPageFirst() {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Ends the print job and does any necessary cleanup.
|
|
*/
|
|
public synchronized void end() {
|
|
if (currentGraphics == null)
|
|
return;
|
|
|
|
graphicsDrawn.append(currentGraphics);
|
|
|
|
/* Close the message queues so that nobody is stuck
|
|
* waiting for one.
|
|
*/
|
|
graphicsToBeDrawn.close();
|
|
graphicsDrawn.closeWhenEmpty();
|
|
|
|
if (printerJobThread != null && printerJobThread.isAlive()) {
|
|
try {
|
|
printerJobThread.join();
|
|
}
|
|
catch (InterruptedException e) {
|
|
}
|
|
}
|
|
currentGraphics = null;
|
|
}
|
|
|
|
/**
|
|
* Ends this print job once it is no longer referenced.
|
|
* @see #end
|
|
*/
|
|
public void finalize() {
|
|
end();
|
|
}
|
|
|
|
/**
|
|
* Prints the page at the specified index into the specified
|
|
* {@link Graphics} context in the specified
|
|
* format. A <code>PrinterJob</code> calls the
|
|
* <code>Printable</code> interface to request that a page be
|
|
* rendered into the context specified by
|
|
* <code>graphics</code>. The format of the page to be drawn is
|
|
* specified by <code>pageFormat</code>. The zero based index
|
|
* of the requested page is specified by <code>pageIndex</code>.
|
|
* If the requested page does not exist then this method returns
|
|
* NO_SUCH_PAGE; otherwise PAGE_EXISTS is returned.
|
|
* The <code>Graphics</code> class or subclass implements the
|
|
* {@link PrinterGraphics} interface to provide additional
|
|
* information. If the <code>Printable</code> object
|
|
* aborts the print job then it throws a {@link PrinterException}.
|
|
* @param graphics the context into which the page is drawn
|
|
* @param pageFormat the size and orientation of the page being drawn
|
|
* @param pageIndex the zero based index of the page to be drawn
|
|
* @return PAGE_EXISTS if the page is rendered successfully
|
|
* or NO_SUCH_PAGE if <code>pageIndex</code> specifies a
|
|
* non-existent page.
|
|
* @exception java.awt.print.PrinterException
|
|
* thrown when the print job is terminated.
|
|
*/
|
|
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
|
|
throws PrinterException {
|
|
|
|
int result;
|
|
|
|
/* This method will be called by the PrinterJob on a thread other
|
|
* that the application's thread. We hold on to the graphics
|
|
* until we can rendevous with the application's thread and
|
|
* hand over the graphics. The application then does all the
|
|
* drawing. When the application is done drawing we rendevous
|
|
* again with the PrinterJob thread and release the Graphics
|
|
* so that it knows we are done.
|
|
*/
|
|
|
|
/* Add the graphics to the message queue of graphics to
|
|
* be rendered. This is really a one slot queue. The
|
|
* application's thread will come along and remove the
|
|
* graphics from the queue when the app asks for a graphics.
|
|
*/
|
|
graphicsToBeDrawn.append( (Graphics2D) graphics);
|
|
|
|
/* We now wait for the app's thread to finish drawing on
|
|
* the Graphics. This thread will sleep until the application
|
|
* release the graphics by placing it in the graphics drawn
|
|
* message queue. If the application signals that it is
|
|
* finished drawing the entire document then we'll get null
|
|
* returned when we try and pop a finished graphic.
|
|
*/
|
|
if (graphicsDrawn.pop() != null) {
|
|
result = PAGE_EXISTS;
|
|
} else {
|
|
result = NO_SUCH_PAGE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private void startPrinterJobThread() {
|
|
|
|
printerJobThread = new Thread(this, "printerJobThread");
|
|
printerJobThread.start();
|
|
}
|
|
|
|
|
|
public void run() {
|
|
|
|
try {
|
|
printerJob.print(attributes);
|
|
} catch (PrinterException e) {
|
|
//REMIND: need to store this away and not rethrow it.
|
|
}
|
|
|
|
/* Close the message queues so that nobody is stuck
|
|
* waiting for one.
|
|
*/
|
|
graphicsToBeDrawn.closeWhenEmpty();
|
|
graphicsDrawn.close();
|
|
}
|
|
|
|
private class MessageQ {
|
|
|
|
private String qid="noname";
|
|
|
|
private ArrayList queue = new ArrayList();
|
|
|
|
MessageQ(String id) {
|
|
qid = id;
|
|
}
|
|
|
|
synchronized void closeWhenEmpty() {
|
|
|
|
while (queue != null && queue.size() > 0) {
|
|
try {
|
|
wait(1000);
|
|
} catch (InterruptedException e) {
|
|
// do nothing.
|
|
}
|
|
}
|
|
|
|
queue = null;
|
|
notifyAll();
|
|
}
|
|
|
|
synchronized void close() {
|
|
queue = null;
|
|
notifyAll();
|
|
}
|
|
|
|
synchronized boolean append(Graphics2D g) {
|
|
|
|
boolean queued = false;
|
|
|
|
if (queue != null) {
|
|
queue.add(g);
|
|
queued = true;
|
|
notify();
|
|
}
|
|
|
|
return queued;
|
|
}
|
|
|
|
synchronized Graphics2D pop() {
|
|
Graphics2D g = null;
|
|
|
|
while (g == null && queue != null) {
|
|
|
|
if (queue.size() > 0) {
|
|
g = (Graphics2D) queue.remove(0);
|
|
notify();
|
|
|
|
} else {
|
|
try {
|
|
wait(2000);
|
|
} catch (InterruptedException e) {
|
|
// do nothing.
|
|
}
|
|
}
|
|
}
|
|
|
|
return g;
|
|
}
|
|
|
|
}
|
|
|
|
}
|