001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.harmony.x.print;
018:
019: import java.awt.Color;
020: import java.awt.Component;
021: import java.awt.Frame;
022: import java.awt.Graphics;
023: import java.awt.Graphics2D;
024: import java.awt.GraphicsConfiguration;
025: import java.awt.Image;
026: import java.awt.Rectangle;
027: import java.awt.font.GlyphVector;
028: import java.awt.print.PageFormat;
029: import java.awt.print.Pageable;
030: import java.awt.print.Paper;
031: import java.awt.print.Printable;
032: import java.awt.print.PrinterException;
033: import java.io.File;
034: import java.io.IOException;
035: import java.net.URL;
036: import java.util.ArrayList;
037: import java.util.Collections;
038: import java.util.List;
039: import java.util.Map;
040: import java.util.WeakHashMap;
041:
042: import javax.imageio.ImageIO;
043: import javax.print.CancelablePrintJob;
044: import javax.print.Doc;
045: import javax.print.DocFlavor;
046: import javax.print.PrintException;
047: import javax.print.PrintService;
048: import javax.print.attribute.Attribute;
049: import javax.print.attribute.AttributeSet;
050: import javax.print.attribute.AttributeSetUtilities;
051: import javax.print.attribute.HashAttributeSet;
052: import javax.print.attribute.HashPrintJobAttributeSet;
053: import javax.print.attribute.PrintJobAttribute;
054: import javax.print.attribute.PrintJobAttributeSet;
055: import javax.print.attribute.PrintRequestAttributeSet;
056: import javax.print.attribute.Size2DSyntax;
057: import javax.print.attribute.TextSyntax;
058: import javax.print.attribute.standard.Destination;
059: import javax.print.attribute.standard.DocumentName;
060: import javax.print.attribute.standard.JobName;
061: import javax.print.attribute.standard.MediaPrintableArea;
062: import javax.print.attribute.standard.OrientationRequested;
063: import javax.print.attribute.standard.PageRanges;
064: import javax.print.event.PrintJobAttributeEvent;
065: import javax.print.event.PrintJobAttributeListener;
066: import javax.print.event.PrintJobEvent;
067: import javax.print.event.PrintJobListener;
068:
069: import org.apache.harmony.awt.gl.CommonGraphics2D;
070: import org.apache.harmony.awt.gl.windows.WinGDIPGraphics2D;
071:
072: class WinPrintJob implements CancelablePrintJob {
073:
074: final Object lock;
075: final Map<PrintJobListener, Object> jobListeners;
076: final Map<PrintJobAttributeListener, PrintJobAttributeSet> attrListeners;
077: final WinPrintService service;
078: Printer printer;
079:
080: WinPrintJob(final WinPrintService service) {
081: this .service = service;
082: lock = new Object();
083: jobListeners = Collections
084: .synchronizedMap(new WeakHashMap<PrintJobListener, Object>());
085: attrListeners = Collections
086: .synchronizedMap(new WeakHashMap<PrintJobAttributeListener, PrintJobAttributeSet>());
087: }
088:
089: public void cancel() throws PrintException {
090: synchronized (lock) {
091: if (printer == null) {
092: throw new PrintException("Job is not started"); //$NON-NLS-1$
093: }
094: printer.cancelJob();
095: }
096: }
097:
098: public void addPrintJobAttributeListener(
099: final PrintJobAttributeListener listener,
100: final PrintJobAttributeSet attributes) {
101: if (listener != null) {
102: attrListeners.put(listener, attributes);
103: }
104: }
105:
106: public void addPrintJobListener(final PrintJobListener listener) {
107: if (listener != null) {
108: jobListeners.put(listener, null);
109: }
110: }
111:
112: public PrintJobAttributeSet getAttributes() {
113: final PrintJobAttributeSet attrs = service.getPrinterProps()
114: .getAttributes(new HashPrintJobAttributeSet());
115:
116: for (Attribute attr : attrs.toArray()) {
117: if (!(attr instanceof PrintJobAttribute)) {
118: attrs.remove(attr);
119: }
120: }
121:
122: return AttributeSetUtilities.unmodifiableView(attrs);
123: }
124:
125: public PrintService getPrintService() {
126: return service;
127: }
128:
129: public void print(final Doc doc,
130: final PrintRequestAttributeSet attributes)
131: throws PrintException {
132: synchronized (lock) {
133: if (printer != null) {
134: throw new PrintException("Printer is busy"); //$NON-NLS-1$
135: } else {
136: final DocFlavor flavor = doc.getDocFlavor();
137:
138: if ((flavor == null)
139: || !service.isDocFlavorSupported(flavor)) {
140: throw new PrintException(
141: "Doc flavor is not supported"); //$NON-NLS-1$
142: }
143:
144: printer = new Printer(doc, attributes);
145: printer.print();
146: }
147: }
148: }
149:
150: public void removePrintJobAttributeListener(
151: final PrintJobAttributeListener listener) {
152: attrListeners.remove(listener);
153: }
154:
155: public void removePrintJobListener(final PrintJobListener listener) {
156: jobListeners.remove(listener);
157: }
158:
159: void notifyJobListeners(final int reason) {
160: final PrintJobEvent event = new PrintJobEvent(this , reason);
161:
162: for (PrintJobListener listener : jobListeners.keySet()) {
163: switch (reason) {
164: case PrintJobEvent.DATA_TRANSFER_COMPLETE:
165: listener.printDataTransferCompleted(event);
166: break;
167: case PrintJobEvent.JOB_CANCELED:
168: listener.printJobCanceled(event);
169: break;
170: case PrintJobEvent.JOB_COMPLETE:
171: listener.printJobCompleted(event);
172: break;
173: case PrintJobEvent.JOB_FAILED:
174: listener.printJobFailed(event);
175: break;
176: case PrintJobEvent.NO_MORE_EVENTS:
177: listener.printJobNoMoreEvents(event);
178: break;
179: case PrintJobEvent.REQUIRES_ATTENTION:
180: listener.printJobRequiresAttention(event);
181: break;
182: }
183: }
184: }
185:
186: void notifyAttrListeners(final PrintJobAttribute... attrs) {
187: final PrintJobAttributeSet attrSet = new HashPrintJobAttributeSet(
188: attrs);
189: final PrintJobAttributeEvent event = new PrintJobAttributeEvent(
190: this , attrSet);
191:
192: for (PrintJobAttribute attr : attrs) {
193: final Class<? extends Attribute> cat = attr.getCategory();
194:
195: for (Map.Entry<PrintJobAttributeListener, PrintJobAttributeSet> e : attrListeners
196: .entrySet()) {
197: if ((e.getValue() == null)
198: || (e.getValue().containsKey(cat))) {
199: e.getKey().attributeUpdate(event);
200: }
201: }
202: }
203: }
204:
205: void notifyAttrListeners(final AttributeSet... attrSets) {
206: final List<PrintJobAttribute> list = new ArrayList<PrintJobAttribute>();
207: final PrintJobAttribute[] attrs;
208:
209: for (AttributeSet attrSet : attrSets) {
210: if (attrSet != null) {
211: for (Attribute attr : attrSet.toArray()) {
212: if (attr instanceof PrintJobAttribute) {
213: list.add((PrintJobAttribute) attr);
214: }
215: }
216: }
217: }
218:
219: attrs = new PrintJobAttribute[list.size()];
220: notifyAttrListeners(list.toArray(attrs));
221: }
222:
223: private class Printer extends Thread {
224: final Doc doc;
225: final PrintRequestAttributeSet attributes;
226: int jobId;
227:
228: Printer(final Doc doc, final PrintRequestAttributeSet attributes) {
229: super (WinPrintService.DEFAULT_JOB_NAME.getValue());
230: this .doc = doc;
231: this .attributes = attributes;
232: }
233:
234: public void run() {
235: try {
236: print();
237: } catch (final PrintException ex) {
238: throw new RuntimeException(ex);
239: }
240: }
241:
242: public void print() throws PrintException {
243: final DocFlavor flavor = doc.getDocFlavor();
244: final DevmodeStructWrapper dm = service.getPrinterProps();
245:
246: dm.setAttributes(attributes);
247: dm.setAttributes(doc.getAttributes());
248: notifyAttrListeners(dm
249: .getAttributes(new HashAttributeSet()));
250:
251: try {
252: if (DocFlavor.SERVICE_FORMATTED.PRINTABLE
253: .equals(flavor)) {
254: printPrintable(doc, attributes);
255: } else if (DocFlavor.SERVICE_FORMATTED.PAGEABLE
256: .equals(flavor)) {
257: printPageable(doc, attributes);
258: } else if (DocFlavor.URL.JPEG.equals(flavor)
259: || DocFlavor.URL.GIF.equals(flavor)
260: || DocFlavor.URL.PNG.equals(flavor)) {
261: printImage(ImageIO.read(castDoc(doc, URL.class)),
262: doc, attributes);
263: } else if (DocFlavor.INPUT_STREAM.JPEG.equals(flavor)
264: || DocFlavor.INPUT_STREAM.GIF.equals(flavor)
265: || DocFlavor.INPUT_STREAM.PNG.equals(flavor)) {
266: printImage(ImageIO.read(doc.getStreamForBytes()),
267: doc, attributes);
268: } else if (DocFlavor.BYTE_ARRAY.JPEG.equals(flavor)
269: || DocFlavor.BYTE_ARRAY.GIF.equals(flavor)
270: || DocFlavor.BYTE_ARRAY.PNG.equals(flavor)) {
271: printImage(ImageIO.read(doc.getStreamForBytes()),
272: doc, attributes);
273: } else {
274: throw new PrintException(
275: "Doc flavor is not supported"); //$NON-NLS-1$
276: }
277:
278: notifyJobListeners(PrintJobEvent.DATA_TRANSFER_COMPLETE);
279: notifyJobListeners(PrintJobEvent.JOB_COMPLETE);
280: } catch (final PrintException ex) {
281: throw ex;
282: } catch (final Exception ex) {
283: synchronized (this ) {
284: if (jobId != -1) {
285: notifyJobListeners(PrintJobEvent.JOB_FAILED);
286: throw new PrintException(ex);
287: }
288: }
289: } finally {
290: synchronized (lock) {
291: printer = null;
292: }
293: }
294: }
295:
296: synchronized void cancelJob() throws PrintException {
297: if (jobId > 0) {
298: WinPrinterFactory.cancelPrinterJob(service
299: .getPrinterHandle(), jobId);
300: }
301:
302: jobId = -1;
303: notifyJobListeners(PrintJobEvent.JOB_CANCELED);
304: }
305:
306: private synchronized void startJob(final long pdc,
307: final String docName, final String filePath)
308: throws PrintException {
309: if (jobId == -1) {
310: throw new PrintException("Job has been canceled"); //$NON-NLS-1$
311: }
312:
313: jobId = WinPrinterFactory.startDoc(pdc, docName, filePath);
314: }
315:
316: private synchronized void endJob(final long pdc)
317: throws PrintException {
318: if (jobId <= 0) {
319: throw new PrintException("Job is not started");//$NON-NLS-1$
320: }
321: WinPrinterFactory.endDoc(pdc);
322: }
323:
324: private void printPrintable(final Doc doc,
325: final PrintRequestAttributeSet attributes)
326: throws PrintException {
327: final long pdc = WinPrinterFactory.getPrinterDC(
328: service.printerName, service.getPrinterProps()
329: .getStructPtr());
330: final AttributeSet docAttrs = doc.getAttributes();
331: final Printable printable = castDoc(doc, Printable.class);
332: final PageFormat format = getPageFormat(docAttrs,
333: attributes);
334: final PageRanges ranges = getAttribute(PageRanges.class,
335: docAttrs, attributes);
336: int res = Printable.PAGE_EXISTS;
337:
338: try {
339: startJob(pdc, getDocName(printable, docAttrs,
340: attributes), getDestinationPath(attributes));
341:
342: for (int i = 0; res == Printable.PAGE_EXISTS; i++) {
343: if ((ranges != null) && !ranges.contains(i)) {
344: continue;
345: }
346:
347: res = printPrintable(printable, pdc, format, i);
348: }
349:
350: endJob(pdc);
351: } finally {
352: WinPrinterFactory.releasePrinterDC(pdc);
353: }
354: }
355:
356: private void printPageable(final Doc doc,
357: final PrintRequestAttributeSet attributes)
358: throws PrintException {
359: final Pageable pageable = castDoc(doc, Pageable.class);
360: final PageFormat defaultFormat = getPageFormat(doc
361: .getAttributes(), attributes);
362: final long pdc = WinPrinterFactory.getPrinterDC(
363: service.printerName, service.getPrinterProps()
364: .getStructPtr());
365: final AttributeSet docAttrs = doc.getAttributes();
366: int pages = pageable.getNumberOfPages();
367: final PageRanges ranges = getAttribute(PageRanges.class,
368: docAttrs, attributes);
369:
370: if (pages == Pageable.UNKNOWN_NUMBER_OF_PAGES) {
371: pages = Integer.MAX_VALUE;
372: }
373:
374: try {
375: startJob(pdc,
376: getDocName(pageable, docAttrs, attributes),
377: getDestinationPath(attributes));
378:
379: for (int i = 0; i < pages; i++) {
380: if ((ranges != null) && !ranges.contains(i)) {
381: continue;
382: }
383:
384: final Printable printable = pageable
385: .getPrintable(i);
386: final PageFormat format = null;
387:
388: if (printable == null) {
389: throw new PrintException("No such page: " + i); //$NON-NLS-1$
390: }
391:
392: if (printPrintable(printable, pdc,
393: format != null ? format : defaultFormat, i) == Printable.NO_SUCH_PAGE) {
394: break;
395: }
396: }
397:
398: endJob(pdc);
399: } finally {
400: WinPrinterFactory.releasePrinterDC(pdc);
401: }
402: }
403:
404: private void printImage(final Image img, final Doc doc,
405: final PrintRequestAttributeSet attributes)
406: throws PrintException {
407: final PageFormat format = getPageFormat(attributes);
408: final long pdc = WinPrinterFactory.getPrinterDC(
409: service.printerName, service.getPrinterProps()
410: .getStructPtr());
411: final double xRes = WinPrinterFactory
412: .getPixelsPerInchX(pdc) / 72;
413: final double yRes = WinPrinterFactory
414: .getPixelsPerInchY(pdc) / 72;
415: final Graphics2D g2d = new WinGDIPGraphics2D(pdc, (char) 2,
416: (int) (format.getWidth() * xRes), (int) (format
417: .getHeight() * yRes));
418:
419: try {
420: startJob(pdc, getDocName(img, attributes),
421: getDestinationPath(attributes));
422: WinPrinterFactory.startPage(pdc);
423: g2d.drawImage(img,
424: (int) (format.getImageableX() * xRes),
425: (int) (format.getImageableY() * yRes),
426: (int) (format.getImageableWidth() * xRes),
427: (int) (format.getImageableHeight() * yRes),
428: Color.WHITE, null);
429: WinPrinterFactory.endPage(pdc);
430: endJob(pdc);
431: } finally {
432: WinPrinterFactory.releasePrinterDC(pdc);
433: }
434: }
435:
436: private int printPrintable(final Printable p, final long pdc,
437: final PageFormat format, final int pageIndex)
438: throws PrintException {
439: int result = Printable.PAGE_EXISTS;
440:
441: try {
442: // Before drawing on printer's device context trying to draw on
443: // a dummy graphics to ensure that the page exists.
444: result = p.print(new DummyGraphics2D((int) format
445: .getWidth(), (int) format.getHeight()), format,
446: pageIndex);
447: } catch (final Exception ex) {
448: // ignore
449: }
450:
451: if (result == Printable.PAGE_EXISTS) {
452: try {
453: WinPrinterFactory.startPage(pdc);
454: result = p.print(getGraphics(pdc, format), format,
455: pageIndex);
456: WinPrinterFactory.endPage(pdc);
457: } catch (final PrinterException ex) {
458: throw new PrintException(ex);
459: }
460: }
461:
462: return result;
463: }
464:
465: private <T extends Attribute> T getAttribute(final Class<T> c,
466: final AttributeSet... attrSets) {
467: for (AttributeSet attrs : attrSets) {
468: if (attrs != null) {
469: for (Attribute attr : attrs.toArray()) {
470: if (c.equals(attr.getCategory())) {
471: return c.cast(attr);
472: }
473: }
474: }
475: }
476:
477: return null;
478: }
479:
480: private String getDocName(final Object doc,
481: final AttributeSet... attrSets) {
482: Attribute name = getAttribute(DocumentName.class, attrSets);
483:
484: if (name == null) {
485: name = getAttribute(JobName.class, attrSets);
486: }
487:
488: if ((name == null) && (doc instanceof Component)) {
489: Component c = (Component) doc;
490:
491: while (c != null) {
492: if (c instanceof Frame) {
493: if (((Frame) c).getTitle().length() > 0) {
494: return ((Frame) c).getTitle();
495: }
496: }
497: c = c.getParent();
498: }
499: }
500:
501: return name != null ? ((TextSyntax) name).getValue()
502: : WinPrintService.DEFAULT_JOB_NAME.getValue();
503: }
504:
505: private String getDestinationPath(
506: final PrintRequestAttributeSet attrs)
507: throws PrintException {
508: if (attrs != null) {
509: final Destination dest = (Destination) attrs
510: .get(Destination.class);
511: return dest != null ? new File(dest.getURI())
512: .getAbsolutePath() : null;
513: }
514: return null;
515: }
516:
517: private <T> T castDoc(final Doc doc, final Class<T> c)
518: throws PrintException {
519: try {
520: return c.cast(doc.getPrintData());
521: } catch (final IOException ex) {
522: throw new PrintException(ex);
523: }
524: }
525:
526: private Graphics2D getGraphics(final long pdc,
527: final PageFormat format) throws PrintException {
528: final Graphics2D g2d = new WinGDIPGraphics2D(pdc, (char) 3,
529: (int) format.getWidth(), (int) format.getHeight());
530:
531: g2d.setColor(Color.BLACK);
532: g2d.setBackground(Color.WHITE);
533: g2d.setClip((int) format.getImageableX(), (int) format
534: .getImageableY(), (int) format.getImageableWidth(),
535: (int) format.getImageableHeight());
536:
537: return g2d;
538: }
539:
540: private PageFormat getPageFormat(final AttributeSet... attrSets) {
541: final Paper paper = new Paper();
542: final PageFormat format = new PageFormat();
543: final DevmodeStructWrapper dm = service.getPrinterProps();
544: final OrientationRequested o = dm.getOrientation();
545: final MediaPrintableArea area = getAttribute(
546: MediaPrintableArea.class, attrSets);
547: DevmodeStructWrapper.Paper p = dm.getPaper();
548:
549: if (p == null) {
550: p = (DevmodeStructWrapper.Paper) service
551: .getDefaultAttributeValue(DevmodeStructWrapper.Paper.class);
552: dm.setPaper(p);
553: }
554:
555: paper.setSize(p.getSize().getX(Size2DSyntax.INCH) * 72.0, p
556: .getSize().getY(Size2DSyntax.INCH) * 72.0);
557: format.setPaper(paper);
558:
559: if (OrientationRequested.LANDSCAPE.equals(o)
560: || OrientationRequested.REVERSE_LANDSCAPE.equals(o)) {
561: format.setOrientation(PageFormat.LANDSCAPE);
562: } else {
563: format.setOrientation(PageFormat.PORTRAIT);
564: }
565:
566: if (area != null) {
567: paper.setImageableArea(area
568: .getX(MediaPrintableArea.INCH) * 72, area
569: .getY(MediaPrintableArea.INCH) * 72, area
570: .getWidth(MediaPrintableArea.INCH) * 72, area
571: .getHeight(MediaPrintableArea.INCH) * 72);
572: } else {
573: final double x = paper.getWidth() / 10;
574: final double y = paper.getHeight() / 10;
575:
576: paper.setImageableArea(x, y,
577: (paper.getWidth() - 2 * x),
578: (paper.getHeight() - 2 * y));
579: }
580:
581: return format;
582: }
583: }
584:
585: private static class DummyGraphics2D extends CommonGraphics2D {
586: DummyGraphics2D(final int width, final int height) {
587: setClip(new Rectangle(width, height));
588: }
589:
590: public void drawGlyphVector(GlyphVector g, float x, float y) {
591: }
592:
593: public void drawString(String s, float x, float y) {
594: }
595:
596: public GraphicsConfiguration getDeviceConfiguration() {
597: return null;
598: }
599:
600: public void copyArea(int sx, int sy, int width, int height,
601: int dx, int dy) {
602: }
603:
604: public Graphics create() {
605: return this;
606: }
607: }
608: }
|