comparison dwt/printing/Printer.d @ 0:380af2bdd8e5

Upload of whole dwt tree
author Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com>
date Sat, 09 Aug 2008 17:00:02 +0200
parents
children 649b8e223d5a
comparison
equal deleted inserted replaced
-1:000000000000 0:380af2bdd8e5
1 /*******************************************************************************
2 * Copyright (c) 2000, 2007 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 module dwt.printing.Printer;
12
13 import dwt.dwthelper.utils;
14
15 import dwt.DWT;
16 import dwt.DWTError;
17 import dwt.DWTException;
18 import dwt.graphics.Device;
19 import dwt.graphics.DeviceData;
20 import dwt.graphics.GCData;
21 import dwt.graphics.Point;
22 import dwt.graphics.Rectangle;
23 import dwt.internal.cocoa.NSArray;
24 import dwt.internal.cocoa.NSPrinter;
25 import dwt.internal.cocoa.NSString;
26
27 /**
28 * Instances of this class are used to print to a printer.
29 * Applications create a GC on a printer using <code>new GC(printer)</code>
30 * and then draw on the printer GC using the usual graphics calls.
31 * <p>
32 * A <code>Printer</code> object may be constructed by providing
33 * a <code>PrinterData</code> object which identifies the printer.
34 * A <code>PrintDialog</code> presents a print dialog to the user
35 * and returns an initialized instance of <code>PrinterData</code>.
36 * Alternatively, calling <code>new Printer()</code> will construct a
37 * printer object for the user's default printer.
38 * </p><p>
39 * Application code must explicitly invoke the <code>Printer.dispose()</code>
40 * method to release the operating system resources managed by each instance
41 * when those instances are no longer required.
42 * </p>
43 *
44 * @see PrinterData
45 * @see PrintDialog
46 */
47 public final class Printer extends Device {
48 PrinterData data;
49 NSPrinter printer;
50 bool inPage, isGCCreated;
51
52 static final String DRIVER = "Mac";
53 static final String PRINTER_DRIVER = "Printer";
54 static final String FILE_DRIVER = "File";
55 static final String PREVIEW_DRIVER = "Preview";
56 static final String FAX_DRIVER = "Fax";
57
58 /**
59 * Returns an array of <code>PrinterData</code> objects
60 * representing all available printers.
61 *
62 * @return the list of available printers
63 */
64 public static PrinterData[] getPrinterList() {
65 NSArray printers = NSPrinter.printerNames();
66 int count = printers.count();
67 PrinterData[] result = new PrinterData[count];
68 for (int i = 0; i < count; i++) {
69 NSString str = new NSString(printers.objectAtIndex(i));
70 char[] buffer = new char[str.length()];
71 str.getCharacters_(buffer);
72 result[i] = new PrinterData(DRIVER, new String(buffer));
73 }
74 return result;
75 }
76
77 /**
78 * Returns a <code>PrinterData</code> object representing
79 * the default printer or <code>null</code> if there is no
80 * printer available on the System.
81 *
82 * @return the default printer data or null
83 *
84 * @since 2.1
85 */
86 public static PrinterData getDefaultPrinterData() {
87 //TODO - get default
88 PrinterData[] printers = getPrinterList();
89 if (printers.length > 0) return printers[0];
90 return null;
91 }
92 //static int packData(int handle, byte[] buffer, int offset) {
93 // int length = OS.GetHandleSize (handle);
94 // buffer[offset++] = (byte)((length & 0xFF) >> 0);
95 // buffer[offset++] = (byte)((length & 0xFF00) >> 8);
96 // buffer[offset++] = (byte)((length & 0xFF0000) >> 16);
97 // buffer[offset++] = (byte)((length & 0xFF000000) >> 24);
98 // int [] ptr = new int [1];
99 // OS.HLock(handle);
100 // OS.memmove(ptr, handle, 4);
101 // byte[] buffer1 = new byte[length];
102 // OS.memmove(buffer1, ptr [0], length);
103 // OS.HUnlock(handle);
104 // System.arraycopy(buffer1, 0, buffer, offset, length);
105 // return offset + length;
106 //}
107 //static int unpackData(int[] handle, byte[] buffer, int offset) {
108 // int length =
109 // ((buffer[offset++] & 0xFF) << 0) |
110 // ((buffer[offset++] & 0xFF) << 8) |
111 // ((buffer[offset++] & 0xFF) << 16) |
112 // ((buffer[offset++] & 0xFF) << 24);
113 // handle[0] = OS.NewHandle(length);
114 // if (handle[0] is 0) DWT.error(DWT.ERROR_NO_HANDLES);
115 // int[] ptr = new int[1];
116 // OS.HLock(handle[0]);
117 // OS.memmove(ptr, handle[0], 4);
118 // byte[] buffer1 = new byte[length];
119 // System.arraycopy(buffer, offset, buffer1, 0, length);
120 // OS.memmove(ptr[0], buffer1, length);
121 // OS.HUnlock(handle[0]);
122 // return offset + length;
123 //}
124
125 /**
126 * Constructs a new printer representing the default printer.
127 * <p>
128 * You must dispose the printer when it is no longer required.
129 * </p>
130 *
131 * @exception DWTError <ul>
132 * <li>ERROR_NO_HANDLES - if there are no valid printers
133 * </ul>
134 *
135 * @see Device#dispose
136 */
137 public Printer() {
138 this(null);
139 }
140
141 /**
142 * Constructs a new printer given a <code>PrinterData</code>
143 * object representing the desired printer.
144 * <p>
145 * You must dispose the printer when it is no longer required.
146 * </p>
147 *
148 * @param data the printer data for the specified printer
149 *
150 * @exception IllegalArgumentException <ul>
151 * <li>ERROR_INVALID_ARGUMENT - if the specified printer data does not represent a valid printer
152 * </ul>
153 * @exception DWTError <ul>
154 * <li>ERROR_NO_HANDLES - if there are no valid printers
155 * </ul>
156 *
157 * @see Device#dispose
158 */
159 public Printer(PrinterData data) {
160 super (checkNull(data));
161 }
162
163 /**
164 * Given a desired <em>client area</em> for the receiver
165 * (as described by the arguments), returns the bounding
166 * rectangle which would be required to produce that client
167 * area.
168 * <p>
169 * In other words, it returns a rectangle such that, if the
170 * receiver's bounds were set to that rectangle, the area
171 * of the receiver which is capable of displaying data
172 * (that is, not covered by the "trimmings") would be the
173 * rectangle described by the arguments (relative to the
174 * receiver's parent).
175 * </p><p>
176 * Note that there is no setBounds for a printer. This method
177 * is usually used by passing in the client area (the 'printable
178 * area') of the printer. It can also be useful to pass in 0, 0, 0, 0.
179 * </p>
180 *
181 * @param x the desired x coordinate of the client area
182 * @param y the desired y coordinate of the client area
183 * @param width the desired width of the client area
184 * @param height the desired height of the client area
185 * @return the required bounds to produce the given client area
186 *
187 * @exception DWTException <ul>
188 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
189 * </ul>
190 *
191 * @see #getBounds
192 * @see #getClientArea
193 */
194 public Rectangle computeTrim(int x, int y, int width, int height) {
195 checkDevice();
196 // PMRect pageRect = new PMRect();
197 // PMRect paperRect = new PMRect();
198 // OS.PMGetAdjustedPageRect(pageFormat, pageRect);
199 // OS.PMGetAdjustedPaperRect(pageFormat, paperRect);
200 // return new Rectangle(x+(int)paperRect.left, y+(int)paperRect.top, width+(int)(paperRect.right-pageRect.right), height+(int)(paperRect.bottom-pageRect.bottom));
201 return null;
202 }
203
204 /**
205 * Creates the printer handle.
206 * This method is called internally by the instance creation
207 * mechanism of the <code>Device</code> class.
208 * @param deviceData the device data
209 */
210 protected void create(DeviceData deviceData) {
211 data = (PrinterData)deviceData;
212
213 printer = NSPrinter.static_printerWithName_(NSString.stringWith(data.name));
214 printer.retain();
215
216 // int[] buffer = new int[1];
217 // if (OS.PMCreateSession(buffer) !is OS.noErr) DWT.error(DWT.ERROR_NO_HANDLES);
218 // printSession = buffer[0];
219 // if (printSession is 0) DWT.error(DWT.ERROR_NO_HANDLES);
220 //
221 // if (data.otherData !is null) {
222 // /* Deserialize settings */
223 // int offset = 0;
224 // byte[] otherData = data.otherData;
225 // offset = unpackData(buffer, otherData, offset);
226 // int flatSettings = buffer[0];
227 // offset = unpackData(buffer, otherData, offset);
228 // int flatFormat = buffer[0];
229 // if (OS.PMUnflattenPrintSettings(flatSettings, buffer) !is OS.noErr) DWT.error(DWT.ERROR_NO_HANDLES);
230 // printSettings = buffer[0];
231 // if (printSettings is 0) DWT.error(DWT.ERROR_NO_HANDLES);
232 // if (OS.PMUnflattenPageFormat(flatFormat, buffer) !is OS.noErr) DWT.error(DWT.ERROR_NO_HANDLES);
233 // pageFormat = buffer[0];
234 // if (pageFormat is 0) DWT.error(DWT.ERROR_NO_HANDLES);
235 // OS.DisposeHandle(flatSettings);
236 // OS.DisposeHandle(flatFormat);
237 // } else {
238 // /* Create default settings */
239 // if (OS.PMCreatePrintSettings(buffer) !is OS.noErr) DWT.error(DWT.ERROR_NO_HANDLES);
240 // printSettings = buffer[0];
241 // if (printSettings is 0) DWT.error(DWT.ERROR_NO_HANDLES);
242 // OS.PMSessionDefaultPrintSettings(printSession, printSettings);
243 // if (OS.PMCreatePageFormat(buffer) !is OS.noErr) DWT.error(DWT.ERROR_NO_HANDLES);
244 // pageFormat = buffer[0];
245 // if (pageFormat is 0) DWT.error(DWT.ERROR_NO_HANDLES);
246 // OS.PMSessionDefaultPageFormat(printSession, pageFormat);
247 // }
248 //
249 // if (PREVIEW_DRIVER.equals(data.driver)) {
250 // OS.PMSessionSetDestination(printSession, printSettings, (short) OS.kPMDestinationPreview, 0, 0);
251 // }
252 // String name = data.name;
253 // char[] buffer1 = new char[name.length ()];
254 // name.getChars(0, buffer1.length, buffer1, 0);
255 // int ptr = OS.CFStringCreateWithCharacters(OS.kCFAllocatorDefault, buffer1, buffer1.length);
256 // if (ptr !is 0) {
257 // OS.PMSessionSetCurrentPrinter(printSession, ptr);
258 // OS.CFRelease(ptr);
259 // }
260 //
261 // OS.PMSessionValidatePrintSettings(printSession, printSettings, null);
262 // OS.PMSessionValidatePageFormat(printSession, pageFormat, null);
263 //
264 // int graphicsContextsArray = OS.CFArrayCreateMutable(OS.kCFAllocatorDefault, 1, 0);
265 // if (graphicsContextsArray !is 0) {
266 // OS.CFArrayAppendValue(graphicsContextsArray, OS.kPMGraphicsContextCoreGraphics());
267 // OS.PMSessionSetDocumentFormatGeneration(printSession, OS.kPMDocumentFormatPDF(), graphicsContextsArray, 0);
268 // OS.CFRelease(graphicsContextsArray);
269 // }
270 }
271
272 /**
273 * Destroys the printer handle.
274 * This method is called internally by the dispose
275 * mechanism of the <code>Device</code> class.
276 */
277 protected void destroy() {
278 if (printer !is null) printer.release();
279 printer = null;
280 }
281
282 /**
283 * Invokes platform specific functionality to allocate a new GC handle.
284 * <p>
285 * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
286 * API for <code>Printer</code>. It is marked public only so that it
287 * can be shared within the packages provided by DWT. It is not
288 * available on all platforms, and should never be called from
289 * application code.
290 * </p>
291 *
292 * @param data the platform specific GC data
293 * @return the platform specific GC handle
294 */
295 public int internal_new_GC(GCData data) {
296 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
297 setupNewPage();
298 // if (data !is null) {
299 // if (isGCCreated) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
300 // data.device = this;
301 // data.background = getSystemColor(DWT.COLOR_WHITE).handle;
302 // data.foreground = getSystemColor(DWT.COLOR_BLACK).handle;
303 // data.font = getSystemFont ();
304 // PMRect paperRect= new PMRect();
305 // OS.PMGetAdjustedPaperRect(pageFormat, paperRect);
306 // Rect portRect = new Rect();
307 // portRect.left = (short)paperRect.left;
308 // portRect.right = (short)paperRect.right;
309 // portRect.top = (short)paperRect.top;
310 // portRect.bottom = (short)paperRect.bottom;
311 // data.portRect = portRect;
312 // isGCCreated = true;
313 // }
314 // return context;
315 return 0;
316 }
317
318 protected void init () {
319 super.init();
320 }
321
322 /**
323 * Invokes platform specific functionality to dispose a GC handle.
324 * <p>
325 * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
326 * API for <code>Printer</code>. It is marked public only so that it
327 * can be shared within the packages provided by DWT. It is not
328 * available on all platforms, and should never be called from
329 * application code.
330 * </p>
331 *
332 * @param hDC the platform specific GC handle
333 * @param data the platform specific GC data
334 */
335 public void internal_dispose_GC(int context, GCData data) {
336 if (data !is null) isGCCreated = false;
337 }
338
339 /**
340 * Releases any internal state prior to destroying this printer.
341 * This method is called internally by the dispose
342 * mechanism of the <code>Device</code> class.
343 */
344 protected void release () {
345 super.release();
346 }
347
348 /**
349 * Starts a print job and returns true if the job started successfully
350 * and false otherwise.
351 * <p>
352 * This must be the first method called to initiate a print job,
353 * followed by any number of startPage/endPage calls, followed by
354 * endJob. Calling startPage, endPage, or endJob before startJob
355 * will result in undefined behavior.
356 * </p>
357 *
358 * @param jobName the name of the print job to start
359 * @return true if the job started successfully and false otherwise.
360 *
361 * @exception DWTException <ul>
362 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
363 * </ul>
364 *
365 * @see #startPage
366 * @see #endPage
367 * @see #endJob
368 */
369 public bool startJob(String jobName) {
370 checkDevice();
371 // if (jobName !is null && jobName.length() !is 0) {
372 // char[] buffer = new char[jobName.length ()];
373 // jobName.getChars(0, buffer.length, buffer, 0);
374 // int ptr = OS.CFStringCreateWithCharacters(OS.kCFAllocatorDefault, buffer, buffer.length);
375 // if (ptr !is 0) {
376 // OS.PMSetJobNameCFString(printSettings, ptr);
377 // OS.CFRelease (ptr);
378 // }
379 // }
380 // return OS.PMSessionBeginDocumentNoDialog(printSession, printSettings, pageFormat) is OS.noErr;
381 return false;
382 }
383
384 /**
385 * Ends the current print job.
386 *
387 * @exception DWTException <ul>
388 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
389 * </ul>
390 *
391 * @see #startJob
392 * @see #startPage
393 * @see #endPage
394 */
395 public void endJob() {
396 checkDevice();
397 // if (inPage) {
398 // OS.PMSessionEndPageNoDialog(printSession);
399 // inPage = false;
400 // }
401 // OS.PMSessionEndDocumentNoDialog(printSession);
402 // context = 0;
403 }
404
405 /**
406 * Cancels a print job in progress.
407 *
408 * @exception DWTException <ul>
409 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
410 * </ul>
411 */
412 public void cancelJob() {
413 checkDevice();
414 // OS.PMSessionSetError(printSession, OS.kPMCancel);
415 // if (inPage) {
416 // OS.PMSessionEndPageNoDialog(printSession);
417 // inPage = false;
418 // }
419 // OS.PMSessionEndDocumentNoDialog(printSession);
420 // context = 0;
421 }
422
423 static DeviceData checkNull (PrinterData data) {
424 if (data is null) data = new PrinterData();
425 if (data.driver is null || data.name is null) {
426 PrinterData defaultPrinter = getDefaultPrinterData();
427 if (defaultPrinter is null) DWT.error(DWT.ERROR_NO_HANDLES);
428 data.driver = defaultPrinter.driver;
429 data.name = defaultPrinter.name;
430 }
431 return data;
432 }
433
434 /**
435 * Starts a page and returns true if the page started successfully
436 * and false otherwise.
437 * <p>
438 * After calling startJob, this method may be called any number of times
439 * along with a matching endPage.
440 * </p>
441 *
442 * @return true if the page started successfully and false otherwise.
443 *
444 * @exception DWTException <ul>
445 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
446 * </ul>
447 *
448 * @see #endPage
449 * @see #startJob
450 * @see #endJob
451 */
452 public bool startPage() {
453 checkDevice();
454 // if (OS.PMSessionError(printSession) !is OS.noErr) return false;
455 // setupNewPage();
456 // return context !is 0;
457 return false;
458 }
459
460 /**
461 * Ends the current page.
462 *
463 * @exception DWTException <ul>
464 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
465 * </ul>
466 *
467 * @see #startPage
468 * @see #startJob
469 * @see #endJob
470 */
471 public void endPage() {
472 checkDevice();
473 // if (inPage) {
474 // OS.PMSessionEndPageNoDialog(printSession);
475 // inPage = false;
476 // }
477 }
478
479 /**
480 * Returns a point whose x coordinate is the horizontal
481 * dots per inch of the printer, and whose y coordinate
482 * is the vertical dots per inch of the printer.
483 *
484 * @return the horizontal and vertical DPI
485 *
486 * @exception DWTException <ul>
487 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
488 * </ul>
489 */
490 public Point getDPI() {
491 checkDevice();
492 // PMResolution resolution = new PMResolution();
493 // OS.PMGetResolution(pageFormat, resolution);
494 // return new Point((int)resolution.hRes, (int)resolution.vRes);
495 return null;
496 }
497
498 /**
499 * Returns a rectangle describing the receiver's size and location.
500 * For a printer, this is the size of a physical page, in pixels.
501 *
502 * @return the bounding rectangle
503 *
504 * @exception DWTException <ul>
505 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
506 * </ul>
507 *
508 * @see #getClientArea
509 * @see #computeTrim
510 */
511 public Rectangle getBounds() {
512 checkDevice();
513 // PMRect paperRect = new PMRect();
514 // OS.PMGetAdjustedPaperRect(pageFormat, paperRect);
515 // return new Rectangle(0, 0, (int)(paperRect.right-paperRect.left), (int)(paperRect.bottom-paperRect.top));
516 return null;
517 }
518
519 /**
520 * Returns a rectangle which describes the area of the
521 * receiver which is capable of displaying data.
522 * For a printer, this is the size of the printable area
523 * of a page, in pixels.
524 *
525 * @return the client area
526 *
527 * @exception DWTException <ul>
528 * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
529 * </ul>
530 *
531 * @see #getBounds
532 * @see #computeTrim
533 */
534 public Rectangle getClientArea() {
535 checkDevice();
536 // PMRect pageRect = new PMRect();
537 // OS.PMGetAdjustedPageRect(pageFormat, pageRect);
538 // return new Rectangle(0, 0, (int)(pageRect.right-pageRect.left), (int)(pageRect.bottom-pageRect.top));
539 return null;
540 }
541
542 /**
543 * Returns a <code>PrinterData</code> object representing the
544 * target printer for this print job.
545 *
546 * @return a PrinterData object describing the receiver
547 */
548 public PrinterData getPrinterData() {
549 checkDevice();
550 return data;
551 }
552
553 /**
554 * On the Mac the core graphics context for printing is only valid between PMSessionBeginPage and PMSessionEndPage,
555 * so printing code has to retrieve and initializes a graphic context for every page like this:
556 *
557 * <pre>
558 * PMSessionBeginDocument
559 * PMSessionBeginPage
560 * PMSessionGetGraphicsContext
561 * // ... use context
562 * PMSessionEndPage
563 * PMSessionEndDocument
564 * </pre>
565 *
566 * In DWT it is OK to create a GC once between startJob / endJob and use it for all pages in between:
567 *
568 * <pre>
569 * startJob(...);
570 * GC gc= new GC(printer);
571 * startPage();
572 * // ... use gc
573 * endPage();
574 * gc.dispose();
575 * endJob();
576 * </pre>
577 *
578 * The solution to resolve this difference is to rely on the fact that Mac OS X returns the same but
579 * reinitialized graphics context for every page. So we only have to account for the fact that DWT assumes
580 * that the graphics context keeps it settings across a page break when it actually does not.
581 * So we have to copy some settings that exist in the CGC before a PMSessionEndPage to the CGC after a PMSessionBeginPage.
582 * <p>
583 * In addition to this we have to cope with the situation that in DWT we can create a GC before a call to
584 * PMSessionBeginPage. For this we decouple the call to PMSessionBeginPage from
585 * DWT's method startPage as follows: if a new GC is created before a call to startPage, internal_new_GC
586 * does the PMSessionBeginPage and the next following startPage does nothing.
587 * </p>
588 */
589 void setupNewPage() {
590 // if (!inPage) {
591 // inPage= true;
592 // OS.PMSessionBeginPageNoDialog(printSession, pageFormat, null);
593 // int[] buffer = new int[1];
594 // OS.PMSessionGetGraphicsContext(printSession, 0, buffer);
595 // if (context is 0) {
596 // context = buffer[0];
597 // } else {
598 // if (context !is buffer[0]) DWT.error(DWT.ERROR_UNSPECIFIED);
599 // }
600 // PMRect paperRect= new PMRect();
601 // OS.PMGetAdjustedPaperRect(pageFormat, paperRect);
602 // OS.CGContextScaleCTM(context, 1, -1);
603 // OS.CGContextTranslateCTM(context, 0, -(float)(paperRect.bottom-paperRect.top));
604 // OS.CGContextSetStrokeColorSpace(context, colorspace);
605 // OS.CGContextSetFillColorSpace(context, colorspace);
606 // }
607 }
608
609 }