1 /*******************************************************************************
2 * Copyright (c) 2000, 2016 IBM Corporation and others.
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.printing;
17 import org.eclipse.swt.*;
18 import org.eclipse.swt.graphics.*;
19 import org.eclipse.swt.internal.win32.*;
22 * Instances of this class are used to print to a printer.
23 * Applications create a GC on a printer using <code>new GC(printer)</code>
24 * and then draw on the printer GC using the usual graphics calls.
26 * A <code>Printer</code> object may be constructed by providing
27 * a <code>PrinterData</code> object which identifies the printer.
28 * A <code>PrintDialog</code> presents a print dialog to the user
29 * and returns an initialized instance of <code>PrinterData</code>.
30 * Alternatively, calling <code>new Printer()</code> will construct a
31 * printer object for the user's default printer.
33 * Application code must explicitly invoke the <code>Printer.dispose()</code>
34 * method to release the operating system resources managed by each instance
35 * when those instances are no longer required.
40 * @see <a href="http://www.eclipse.org/swt/snippets/#printing">Printing snippets</a>
41 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
43 public final class Printer extends Device {
45 * the handle to the printer DC
46 * (Warning: This field is platform dependent)
48 * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT
49 * public API. It is marked public only so that it can be shared
50 * within the packages provided by SWT. It is not available on all
51 * platforms and should never be accessed from application code.
54 * @noreference This field is not intended to be referenced by clients.
59 * the printer data describing this printer
64 * whether or not a GC was created for this printer
66 boolean isGCCreated = false;
69 * strings used to access the Windows registry
75 profile = new TCHAR(0, "PrinterPorts", true); //$NON-NLS-1$
76 appName = new TCHAR(0, "windows", true); //$NON-NLS-1$
77 keyName = new TCHAR(0, "device", true); //$NON-NLS-1$
81 * Returns an array of <code>PrinterData</code> objects
82 * representing all available printers. If there are no
83 * printers, the array will be empty.
85 * @return an array of PrinterData objects representing the available printers
87 public static PrinterData[] getPrinterList() {
89 /* Use the character encoding for the default locale */
90 TCHAR buf = new TCHAR(0, length);
91 TCHAR nullBuf = new TCHAR(0, 1);
92 int n = OS.GetProfileString(profile, null, nullBuf, buf, length);
93 if (n == 0) return new PrinterData[0];
94 String[] deviceNames = new String[5];
97 for (int i = 0; i < n; i++) {
98 if (buf.tcharAt(i) == 0) {
99 if (nameCount == deviceNames.length) {
100 String[] newNames = new String[deviceNames.length + 5];
101 System.arraycopy(deviceNames, 0, newNames, 0, deviceNames.length);
102 deviceNames = newNames;
104 deviceNames[nameCount] = buf.toString(index, i - index);
109 PrinterData printerList[] = new PrinterData[nameCount];
110 for (int p = 0; p < nameCount; p++) {
111 String device = deviceNames[p];
112 String driver = ""; //$NON-NLS-1$
113 if (OS.GetProfileString(profile, new TCHAR(0, device, true), nullBuf, buf, length) > 0) {
115 while (buf.tcharAt(commaIndex) != ',' && commaIndex < length) commaIndex++;
116 if (commaIndex < length) {
117 driver = buf.toString(0, commaIndex);
120 printerList[p] = new PrinterData(driver, device);
126 * Returns a <code>PrinterData</code> object representing
127 * the default printer or <code>null</code> if there is no
130 * @return the default printer data or null
134 public static PrinterData getDefaultPrinterData() {
135 String deviceName = null;
137 /* Use the character encoding for the default locale */
138 TCHAR buf = new TCHAR(0, length);
139 TCHAR nullBuf = new TCHAR(0, 1);
140 int n = OS.GetProfileString(appName, keyName, nullBuf, buf, length);
141 if (n == 0) return null;
143 while(buf.tcharAt(commaIndex) != ',' && commaIndex < length) commaIndex++;
144 if (commaIndex < length) {
145 deviceName = buf.toString(0, commaIndex);
147 if (deviceName == null) return null;
148 String driver = ""; //$NON-NLS-1$
149 if (OS.GetProfileString(profile, new TCHAR(0, deviceName, true), nullBuf, buf, length) > 0) {
151 while (buf.tcharAt(commaIndex) != ',' && commaIndex < length) commaIndex++;
152 if (commaIndex < length) {
153 driver = buf.toString(0, commaIndex);
156 return new PrinterData(driver, deviceName);
159 static DeviceData checkNull (PrinterData data) {
160 if (data == null) data = new PrinterData();
161 if (data.driver == null || data.name == null) {
162 PrinterData defaultPrinter = getDefaultPrinterData();
163 if (defaultPrinter == null) SWT.error(SWT.ERROR_NO_HANDLES);
164 data.driver = defaultPrinter.driver;
165 data.name = defaultPrinter.name;
171 * Constructs a new printer representing the default printer.
173 * Note: You must dispose the printer when it is no longer required.
176 * @exception SWTError <ul>
177 * <li>ERROR_NO_HANDLES - if there is no valid default printer
180 * @see Device#dispose
187 * Constructs a new printer given a <code>PrinterData</code>
188 * object representing the desired printer. If the argument
189 * is null, then the default printer will be used.
191 * Note: You must dispose the printer when it is no longer required.
194 * @param data the printer data for the specified printer, or null to use the default printer
196 * @exception IllegalArgumentException <ul>
197 * <li>ERROR_INVALID_ARGUMENT - if the specified printer data does not represent a valid printer
199 * @exception SWTError <ul>
200 * <li>ERROR_NO_HANDLES - if there are no valid printers
203 * @see Device#dispose
205 public Printer(PrinterData data) {
206 super(checkNull(data));
210 * Creates the printer handle.
211 * This method is called internally by the instance creation
212 * mechanism of the <code>Device</code> class.
213 * @param deviceData the device data
216 protected void create(DeviceData deviceData) {
217 data = (PrinterData)deviceData;
218 /* Use the character encoding for the default locale */
219 TCHAR driver = new TCHAR(0, data.driver, true);
220 TCHAR device = new TCHAR(0, data.name, true);
222 byte devmodeData [] = data.otherData;
223 long hHeap = OS.GetProcessHeap();
224 if (devmodeData != null && devmodeData.length != 0) {
225 /* If user setup info from a print dialog was specified, restore the DEVMODE struct. */
226 lpInitData = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, devmodeData.length);
227 OS.MoveMemory(lpInitData, devmodeData, devmodeData.length);
229 long [] hPrinter = new long [1];
230 OS.OpenPrinter(device, hPrinter, 0);
231 if (hPrinter[0] != 0) {
232 int dwNeeded = OS.DocumentProperties(0, hPrinter[0], device, 0, 0, 0);
234 lpInitData = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, dwNeeded);
235 int rc = OS.DocumentProperties(0, hPrinter[0], device, lpInitData, 0, OS.DM_OUT_BUFFER);
237 OS.HeapFree(hHeap, 0, lpInitData);
241 OS.ClosePrinter(hPrinter[0]);
245 /* Initialize DEVMODE struct fields from the printerData. */
246 if (lpInitData != 0) {
247 DEVMODE devmode = new DEVMODE ();
248 OS.MoveMemory(devmode, lpInitData, DEVMODE.sizeof);
249 devmode.dmFields |= OS.DM_ORIENTATION;
250 devmode.dmOrientation = data.orientation == PrinterData.LANDSCAPE ? OS.DMORIENT_LANDSCAPE : OS.DMORIENT_PORTRAIT;
251 if (data.copyCount != 1) {
252 devmode.dmFields |= OS.DM_COPIES;
253 devmode.dmCopies = (short)data.copyCount;
255 if (data.collate != false) {
256 devmode.dmFields |= OS.DM_COLLATE;
257 devmode.dmCollate = OS.DMCOLLATE_TRUE;
259 if (data.duplex != SWT.DEFAULT) {
260 devmode.dmFields |= OS.DM_DUPLEX;
261 switch (data.duplex) {
262 case PrinterData.DUPLEX_SHORT_EDGE: devmode.dmDuplex = OS.DMDUP_HORIZONTAL; break;
263 case PrinterData.DUPLEX_LONG_EDGE: devmode.dmDuplex = OS.DMDUP_VERTICAL; break;
264 default: devmode.dmDuplex = OS.DMDUP_SIMPLEX;
267 OS.MoveMemory(lpInitData, devmode, DEVMODE.sizeof);
269 handle = OS.CreateDC(driver, device, 0, lpInitData);
270 if (lpInitData != 0) OS.HeapFree(hHeap, 0, lpInitData);
271 if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES);
275 * Invokes platform specific functionality to allocate a new GC handle.
277 * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
278 * API for <code>Printer</code>. It is marked public only so that it
279 * can be shared within the packages provided by SWT. It is not
280 * available on all platforms, and should never be called from
284 * @param data the platform specific GC data
285 * @return the platform specific GC handle
287 * @noreference This method is not intended to be referenced by clients.
290 public long internal_new_GC(GCData data) {
291 if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES);
293 if (isGCCreated) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
294 int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
295 if ((data.style & mask) != 0) {
296 data.layout = (data.style & SWT.RIGHT_TO_LEFT) != 0 ? OS.LAYOUT_RTL : 0;
298 data.style |= SWT.LEFT_TO_RIGHT;
301 data.font = Font.win32_new(this, OS.GetCurrentObject(handle, OS.OBJ_FONT));
308 * Invokes platform specific functionality to dispose a GC handle.
310 * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
311 * API for <code>Printer</code>. It is marked public only so that it
312 * can be shared within the packages provided by SWT. It is not
313 * available on all platforms, and should never be called from
317 * @param hDC the platform specific GC handle
318 * @param data the platform specific GC data
320 * @noreference This method is not intended to be referenced by clients.
323 public void internal_dispose_GC(long hDC, GCData data) {
324 if (data != null) isGCCreated = false;
328 * @noreference This method is not intended to be referenced by clients.
331 public boolean isAutoScalable() {
336 * Starts a print job and returns true if the job started successfully
337 * and false otherwise.
339 * This must be the first method called to initiate a print job,
340 * followed by any number of startPage/endPage calls, followed by
341 * endJob. Calling startPage, endPage, or endJob before startJob
342 * will result in undefined behavior.
345 * @param jobName the name of the print job to start
346 * @return true if the job started successfully and false otherwise.
348 * @exception SWTException <ul>
349 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
356 public boolean startJob(String jobName) {
358 DOCINFO di = new DOCINFO();
359 di.cbSize = DOCINFO.sizeof;
360 long hHeap = OS.GetProcessHeap();
361 long lpszDocName = 0;
362 if (jobName != null && jobName.length() != 0) {
363 /* Use the character encoding for the default locale */
364 TCHAR buffer = new TCHAR(0, jobName, true);
365 int byteCount = buffer.length() * TCHAR.sizeof;
366 lpszDocName = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
367 OS.MoveMemory(lpszDocName, buffer, byteCount);
368 di.lpszDocName = lpszDocName;
371 if (data.printToFile) {
372 if (data.fileName == null) {
373 /* Prompt the user for a file name. */
374 data.fileName = "FILE:"; //$NON-NLS-1$
376 /* Use the character encoding for the default locale */
377 TCHAR buffer = new TCHAR(0, data.fileName, true);
378 int byteCount = buffer.length() * TCHAR.sizeof;
379 lpszOutput = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
380 OS.MoveMemory(lpszOutput, buffer, byteCount);
381 di.lpszOutput = lpszOutput;
383 int rc = OS.StartDoc(handle, di);
384 if (lpszDocName != 0) OS.HeapFree(hHeap, 0, lpszDocName);
385 if (lpszOutput != 0) OS.HeapFree(hHeap, 0, lpszOutput);
390 * Ends the current print job.
392 * @exception SWTException <ul>
393 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
400 public void endJob() {
406 * Cancels a print job in progress.
408 * @exception SWTException <ul>
409 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
412 public void cancelJob() {
418 * Starts a page and returns true if the page started successfully
419 * and false otherwise.
421 * After calling startJob, this method may be called any number of times
422 * along with a matching endPage.
425 * @return true if the page started successfully and false otherwise.
427 * @exception SWTException <ul>
428 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
435 public boolean startPage() {
437 int rc = OS.StartPage(handle);
438 if (rc <= 0) OS.AbortDoc(handle);
443 * Ends the current page.
445 * @exception SWTException <ul>
446 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
453 public void endPage() {
459 * Returns a point whose x coordinate is the horizontal
460 * dots per inch of the printer, and whose y coordinate
461 * is the vertical dots per inch of the printer.
463 * @return the horizontal and vertical DPI
465 * @exception SWTException <ul>
466 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
470 public Point getDPI() {
472 int dpiX = OS.GetDeviceCaps(handle, OS.LOGPIXELSX);
473 int dpiY = OS.GetDeviceCaps(handle, OS.LOGPIXELSY);
474 return new Point(dpiX, dpiY);
478 * Returns a rectangle describing the receiver's size and location.
480 * For a printer, this is the size of the physical page, in pixels.
483 * @return the bounding rectangle
485 * @exception SWTException <ul>
486 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
489 * @see #getClientArea
493 public Rectangle getBounds() {
495 int width = OS.GetDeviceCaps(handle, OS.PHYSICALWIDTH);
496 int height = OS.GetDeviceCaps(handle, OS.PHYSICALHEIGHT);
497 return new Rectangle(0, 0, width, height);
501 * Returns a rectangle which describes the area of the
502 * receiver which is capable of displaying data.
504 * For a printer, this is the size of the printable area
505 * of the page, in pixels.
508 * @return the client area
510 * @exception SWTException <ul>
511 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
518 public Rectangle getClientArea () {
520 int width = OS.GetDeviceCaps(handle, OS.HORZRES);
521 int height = OS.GetDeviceCaps(handle, OS.VERTRES);
522 return new Rectangle(0, 0, width, height);
526 * Given a <em>client area</em> (as described by the arguments),
527 * returns a rectangle, relative to the client area's coordinates,
528 * that is the client area expanded by the printer's trim (or minimum margins).
530 * Most printers have a minimum margin on each edge of the paper where the
531 * printer device is unable to print. This margin is known as the "trim."
532 * This method can be used to calculate the printer's minimum margins
533 * by passing in a client area of 0, 0, 0, 0 and then using the resulting
534 * x and y coordinates (which will be <= 0) to determine the minimum margins
535 * for the top and left edges of the paper, and the resulting width and height
536 * (offset by the resulting x and y) to determine the minimum margins for the
537 * bottom and right edges of the paper, as follows:
540 * <li>The left trim width is -x pixels</li>
541 * <li>The top trim height is -y pixels</li>
542 * <li>The right trim width is (x + width) pixels</li>
543 * <li>The bottom trim height is (y + height) pixels</li>
546 * @param x the x coordinate of the client area
547 * @param y the y coordinate of the client area
548 * @param width the width of the client area
549 * @param height the height of the client area
550 * @return a rectangle, relative to the client area's coordinates, that is
551 * the client area expanded by the printer's trim (or minimum margins)
553 * @exception SWTException <ul>
554 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
558 * @see #getClientArea
560 public Rectangle computeTrim(int x, int y, int width, int height) {
562 int printX = -OS.GetDeviceCaps(handle, OS.PHYSICALOFFSETX);
563 int printY = -OS.GetDeviceCaps(handle, OS.PHYSICALOFFSETY);
564 int printWidth = OS.GetDeviceCaps(handle, OS.HORZRES);
565 int printHeight = OS.GetDeviceCaps(handle, OS.VERTRES);
566 int paperWidth = OS.GetDeviceCaps(handle, OS.PHYSICALWIDTH);
567 int paperHeight = OS.GetDeviceCaps(handle, OS.PHYSICALHEIGHT);
568 int hTrim = paperWidth - printWidth;
569 int vTrim = paperHeight - printHeight;
570 return new Rectangle(x + printX, y + printY, width + hTrim, height + vTrim);
574 * Returns a <code>PrinterData</code> object representing the
575 * target printer for this print job.
577 * @return a PrinterData object describing the receiver
579 public PrinterData getPrinterData() {
584 * Checks the validity of this device.
586 * @exception SWTException <ul>
587 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
591 protected void checkDevice() {
592 if (handle == 0) SWT.error(SWT.ERROR_DEVICE_DISPOSED);
596 * Releases any internal state prior to destroying this printer.
597 * This method is called internally by the dispose
598 * mechanism of the <code>Device</code> class.
601 protected void release() {
607 * Destroys the printer handle.
608 * This method is called internally by the dispose
609 * mechanism of the <code>Device</code> class.
612 protected void destroy() {
613 if (handle != 0) OS.DeleteDC(handle);