blob: 29b5a724c987fd8b6bb709544822c85b38ff73ee [file] [log] [blame]
package org.eclipse.swt.printing;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved
*/
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.win32.*;
/**
* Instances of this class are used to print to a printer.
* Applications create a GC on a printer using <code>new GC(printer)</code>
* and then draw on the printer GC using the usual graphics calls.
* <p>
* A <code>Printer</code> object may be constructed by providing
* a <code>PrinterData</code> object which identifies the printer.
* A <code>PrintDialog</code> presents a print dialog to the user
* and returns an initialized instance of <code>PrinterData</code>.
* Alternatively, calling <code>new Printer()</code> will construct a
* printer object for the user's default printer.
* </p><p>
* Application code must explicitly invoke the <code>Printer.dispose()</code>
* method to release the operating system resources managed by each instance
* when those instances are no longer required.
* </p>
*
* @see PrinterData
* @see PrintDialog
*/
public final class Printer extends Device {
/**
* the handle to the printer DC
* (Warning: This field is platform dependent)
*/
public int handle;
/**
* the printer data describing this printer
*/
PrinterData data;
/**
* whether or not a GC was created for this printer
*/
boolean isGCCreated = false;
/**
* strings used to access the Windows registry
* (Warning: These fields are platform dependent)
*/
static TCHAR profile;
static TCHAR appName;
static TCHAR keyName;
static {
profile = new TCHAR(0, "PrinterPorts", true);
appName = new TCHAR(0, "windows", true);
keyName = new TCHAR(0, "device", true);
}
/**
* Returns an array of <code>PrinterData</code> objects
* representing all available printers.
*
* @return the list of available printers
*/
public static PrinterData[] getPrinterList() {
int length = 1024;
/* Use the character encoding for the default locale */
TCHAR buf = new TCHAR(0, length);
TCHAR nullBuf = new TCHAR(0, 1);
int n = OS.GetProfileString(profile, null, nullBuf, buf, length);
if (n == 0) return new PrinterData[0];
String[] deviceNames = new String[5];
int nameCount = 0;
int index = 0;
for (int i = 0; i < n; i++) {
if (buf.tcharAt(i) == 0) {
if (nameCount == deviceNames.length) {
String[] newNames = new String[deviceNames.length + 5];
System.arraycopy(deviceNames, 0, newNames, 0, deviceNames.length);
deviceNames = newNames;
}
deviceNames[nameCount] = buf.toString(index, i - index);
nameCount++;
index = i + 1;
}
}
PrinterData printerList[] = new PrinterData[nameCount];
for (int p = 0; p < nameCount; p++) {
String device = deviceNames[p];
String driver = "";
if (OS.GetProfileString(profile, new TCHAR(0, device, true), nullBuf, buf, length) > 0) {
int commaIndex = 0;
while (buf.tcharAt(commaIndex) != ',' && commaIndex < length) commaIndex++;
if (commaIndex < length) {
driver = buf.toString(0, commaIndex);
}
}
printerList[p] = new PrinterData(driver, device);
}
return printerList;
}
/*
* Returns a <code>PrinterData</code> object representing
* the default printer.
*
* @exception SWTError <ul>
* <li>ERROR_NO_HANDLES - if an error occurred constructing the default printer data</li>
* </ul>
*
* @return the default printer data
*/
static PrinterData getDefaultPrinterData() {
String deviceName = null;
int length = 1024;
/* Use the character encoding for the default locale */
TCHAR buf = new TCHAR(0, length);
TCHAR nullBuf = new TCHAR(0, 1);
int n = OS.GetProfileString(appName, keyName, nullBuf, buf, length);
if (n == 0) SWT.error(SWT.ERROR_NO_HANDLES);
int commaIndex = 0;
while(buf.tcharAt(commaIndex) != ',' && commaIndex < length) commaIndex++;
if (commaIndex < length) {
deviceName = buf.toString(0, commaIndex);
}
String driver = "";
if (OS.GetProfileString(profile, new TCHAR(0, deviceName, true), nullBuf, buf, length) > 0) {
commaIndex = 0;
while (buf.tcharAt(commaIndex) != ',' && commaIndex < length) commaIndex++;
if (commaIndex < length) {
driver = buf.toString(0, commaIndex);
}
}
return new PrinterData(driver, deviceName);
}
/**
* Constructs a new printer representing the default printer.
* <p>
* You must dispose the printer when it is no longer required.
* </p>
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_UNSPECIFIED - if there are no valid printers
* </ul>
*
* @see #dispose
*/
public Printer() {
this(getDefaultPrinterData());
}
/**
* Constructs a new printer given a <code>PrinterData</code>
* object representing the desired printer.
* <p>
* You must dispose the printer when it is no longer required.
* </p>
*
* @param data the printer data for the specified printer
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if the specified printer data does not represent a valid printer
* <li>ERROR_UNSPECIFIED - if there are no valid printers
* </ul>
*
* @see #dispose
*/
public Printer(PrinterData data) {
super(data);
}
/**
* Creates the printer handle.
* This method is called internally by the instance creation
* mechanism of the <code>Device</code> class.
*/
protected void create(DeviceData deviceData) {
data = (PrinterData)deviceData;
/* Use the character encoding for the default locale */
TCHAR driver = new TCHAR(0, data.driver, true);
TCHAR device = new TCHAR(0, data.name, true);
int lpInitData = 0;
byte buffer [] = data.otherData;
int hHeap = OS.GetProcessHeap();
if (buffer != null && buffer.length != 0) {
lpInitData = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, buffer.length);
OS.MoveMemory(lpInitData, buffer, buffer.length);
}
handle = OS.CreateDC(driver, device, 0, lpInitData);
if (lpInitData != 0) OS.HeapFree(hHeap, 0, lpInitData);
if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES);
}
/**
* Invokes platform specific functionality to allocate a new GC handle.
* <p>
* <b>IMPORTANT:</b> This method is <em>not</em> part of the public
* API for <code>Printer</code>. It is marked public only so that it
* can be shared within the packages provided by SWT. It is not
* available on all platforms, and should never be called from
* application code.
* </p>
*
* @param data the platform specific GC data
* @return the platform specific GC handle
*
* @private
*/
public int internal_new_GC(GCData data) {
if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES);
if (data != null) {
if (isGCCreated) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
data.device = this;
data.hFont = OS.GetCurrentObject(handle, OS.OBJ_FONT);
isGCCreated = true;
}
return handle;
}
/**
* Invokes platform specific functionality to dispose a GC handle.
* <p>
* <b>IMPORTANT:</b> This method is <em>not</em> part of the public
* API for <code>Printer</code>. It is marked public only so that it
* can be shared within the packages provided by SWT. It is not
* available on all platforms, and should never be called from
* application code.
* </p>
*
* @param handle the platform specific GC handle
* @param data the platform specific GC data
*
* @private
*/
public void internal_dispose_GC(int hDC, GCData data) {
if (data != null) isGCCreated = false;
}
/**
* Starts a print job and returns true if the job started successfully
* and false otherwise.
* <p>
* This must be the first method called to initiate a print job,
* followed by any number of startPage/endPage calls, followed by
* endJob. Calling startPage, endPage, or endJob before startJob
* will result in undefined behavior.
* </p>
*
* @return true if the job started successfully and false otherwise.
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #startPage
* @see #endPage
* @see #endJob
*/
public boolean startJob(String jobName) {
checkDevice();
DOCINFO di = new DOCINFO();
di.cbSize = DOCINFO.sizeof;
int hHeap = OS.GetProcessHeap();
int lpszDocName = 0;
if (jobName != null && jobName.length() != 0) {
/* Use the character encoding for the default locale */
TCHAR buffer = new TCHAR(0, jobName, true);
int byteCount = buffer.length() * TCHAR.sizeof;
lpszDocName = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
OS.MoveMemory(lpszDocName, buffer, byteCount);
di.lpszDocName = lpszDocName;
}
int lpszOutput = 0;
if (data.printToFile && data.fileName != null) {
/* Use the character encoding for the default locale */
TCHAR buffer = new TCHAR(0, data.fileName, true);
int byteCount = buffer.length() * TCHAR.sizeof;
lpszOutput = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
OS.MoveMemory(lpszOutput, buffer, byteCount);
di.lpszOutput = lpszOutput;
}
int rc = OS.StartDoc(handle, di);
if (lpszDocName != 0) OS.HeapFree(hHeap, 0, lpszDocName);
if (lpszOutput != 0) OS.HeapFree(hHeap, 0, lpszOutput);
return rc > 0;
}
/**
* Ends the current print job.
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #startJob
* @see #startPage
* @see #endPage
*/
public void endJob() {
checkDevice();
OS.EndDoc(handle);
}
/**
* Cancels a print job in progress.
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*/
public void cancelJob() {
checkDevice();
OS.AbortDoc(handle);
}
/**
* Starts a page and returns true if the page started successfully
* and false otherwise.
* <p>
* After calling startJob, this method may be called any number of times
* along with a matching endPage.
* </p>
*
* @return true if the page started successfully and false otherwise.
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #endPage
* @see #startJob
* @see #endJob
*/
public boolean startPage() {
checkDevice();
int rc = OS.StartPage(handle);
if (rc <= 0) OS.AbortDoc(handle);
return rc > 0;
}
/**
* Ends the current page.
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #startPage
* @see #startJob
* @see #endJob
*/
public void endPage() {
checkDevice();
OS.EndPage(handle);
}
/**
* Returns a point whose x coordinate is the horizontal
* dots per inch of the printer, and whose y coordinate
* is the vertical dots per inch of the printer.
*
* @return the horizontal and vertical DPI
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*/
public Point getDPI() {
checkDevice();
int dpiX = OS.GetDeviceCaps(handle, OS.LOGPIXELSX);
int dpiY = OS.GetDeviceCaps(handle, OS.LOGPIXELSY);
return new Point(dpiX, dpiY);
}
/**
* Returns a rectangle describing the receiver's size and location.
* For a printer, this is the size of a page, in pixels.
*
* @return the bounding rectangle
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #getClientArea
* @see #computeTrim
*/
public Rectangle getBounds() {
checkDevice();
int width = OS.GetDeviceCaps(handle, OS.PHYSICALWIDTH);
int height = OS.GetDeviceCaps(handle, OS.PHYSICALHEIGHT);
return new Rectangle(0, 0, width, height);
}
/**
* Returns a rectangle which describes the area of the
* receiver which is capable of displaying data.
* For a printer, this is the size of the printable area
* of a page, in pixels.
*
* @return the client area
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #getBounds
* @see #computeTrim
*/
public Rectangle getClientArea() {
checkDevice();
int width = OS.GetDeviceCaps(handle, OS.HORZRES);
int height = OS.GetDeviceCaps(handle, OS.VERTRES);
return new Rectangle(0, 0, width, height);
}
/**
* Given a desired <em>client area</em> for the receiver
* (as described by the arguments), returns the bounding
* rectangle which would be required to produce that client
* area.
* <p>
* In other words, it returns a rectangle such that, if the
* receiver's bounds were set to that rectangle, the area
* of the receiver which is capable of displaying data
* (that is, not covered by the "trimmings") would be the
* rectangle described by the arguments (relative to the
* receiver's parent).
* </p>
* Note that there is no setBounds for a printer. This method
* is usually used by passing in the client area (the 'printable
* area') of the printer. It can also be useful to pass in 0, 0, 0, 0.
*
* @return the required bounds to produce the given client area
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #getBounds
* @see #getClientArea
*/
public Rectangle computeTrim(int x, int y, int width, int height) {
checkDevice();
int printX = -OS.GetDeviceCaps(handle, OS.PHYSICALOFFSETX);
int printY = -OS.GetDeviceCaps(handle, OS.PHYSICALOFFSETY);
int printWidth = OS.GetDeviceCaps(handle, OS.HORZRES);
int printHeight = OS.GetDeviceCaps(handle, OS.VERTRES);
int paperWidth = OS.GetDeviceCaps(handle, OS.PHYSICALWIDTH);
int paperHeight = OS.GetDeviceCaps(handle, OS.PHYSICALHEIGHT);
int hTrim = paperWidth - printWidth;
int vTrim = paperHeight - printHeight;
return new Rectangle(x + printX, y + printY, width + hTrim, height + vTrim);
}
/**
* Returns a <code>PrinterData</code> object representing the
* target printer for this print job.
*
* @return a PrinterData object describing the receiver
*/
public PrinterData getPrinterData() {
return data;
}
/**
* Checks the validity of this device.
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*/
protected void checkDevice() {
if (handle == 0) SWT.error(SWT.ERROR_DEVICE_DISPOSED);
}
/**
* Releases any internal state prior to destroying this printer.
* This method is called internally by the dispose
* mechanism of the <code>Device</code> class.
*/
protected void release() {
super.release();
data = null;
}
/**
* Destroys the printer handle.
* This method is called internally by the dispose
* mechanism of the <code>Device</code> class.
*/
protected void destroy() {
if (handle != 0) OS.DeleteDC(handle);
handle = 0;
}
}