1 /*
   2  * Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.print;
  27 
  28 import java.net.URL;
  29 import java.io.InputStream;
  30 import java.io.IOException;
  31 import java.io.Reader;
  32 import java.util.Vector;
  33 
  34 import javax.print.CancelablePrintJob;
  35 import javax.print.Doc;
  36 import javax.print.DocFlavor;
  37 import javax.print.DocPrintJob;
  38 import javax.print.PrintService;
  39 import javax.print.PrintException;
  40 import javax.print.event.PrintJobEvent;
  41 import javax.print.event.PrintJobListener;
  42 import javax.print.event.PrintJobAttributeListener;
  43 
  44 import javax.print.attribute.Attribute;
  45 import javax.print.attribute.AttributeSet;
  46 import javax.print.attribute.AttributeSetUtilities;
  47 import javax.print.attribute.DocAttributeSet;
  48 import javax.print.attribute.HashPrintJobAttributeSet;
  49 import javax.print.attribute.HashPrintRequestAttributeSet;
  50 import javax.print.attribute.PrintJobAttribute;
  51 import javax.print.attribute.PrintJobAttributeSet;
  52 import javax.print.attribute.PrintRequestAttribute;
  53 import javax.print.attribute.PrintRequestAttributeSet;
  54 import javax.print.attribute.standard.Copies;
  55 import javax.print.attribute.standard.DocumentName;
  56 import javax.print.attribute.standard.Fidelity;
  57 import javax.print.attribute.standard.JobName;
  58 import javax.print.attribute.standard.JobOriginatingUserName;
  59 import javax.print.attribute.standard.Media;
  60 import javax.print.attribute.standard.MediaSize;
  61 import javax.print.attribute.standard.MediaSizeName;
  62 import javax.print.attribute.standard.OrientationRequested;
  63 import javax.print.attribute.standard.RequestingUserName;
  64 
  65 import java.awt.print.*;
  66 
  67 public class PSStreamPrintJob implements CancelablePrintJob {
  68 
  69     transient private Vector jobListeners;
  70     transient private Vector attrListeners;
  71     transient private Vector listenedAttributeSets;
  72 
  73     private PSStreamPrintService service;
  74     private boolean fidelity;
  75     private boolean printing = false;
  76     private boolean printReturned = false;
  77     private PrintRequestAttributeSet reqAttrSet = null;
  78     private PrintJobAttributeSet jobAttrSet = null;
  79     private PrinterJob job;
  80     private Doc doc;
  81     /* these variables used globally to store reference to the print
  82      * data retrieved as a stream. On completion these are always closed
  83      * if non-null.
  84      */
  85     private InputStream instream = null;
  86     private Reader reader = null;
  87 
  88     /* default values overridden by those extracted from the attributes */
  89     private String jobName = "Java Printing";
  90     private int copies = 1;
  91     private MediaSize     mediaSize = MediaSize.NA.LETTER;
  92     private OrientationRequested orient = OrientationRequested.PORTRAIT;
  93 
  94     PSStreamPrintJob(PSStreamPrintService service) {
  95         this.service = service;
  96     }
  97 
  98     public PrintService getPrintService() {
  99         return service;
 100     }
 101 
 102     public PrintJobAttributeSet getAttributes() {
 103         synchronized (this) {
 104             if (jobAttrSet == null) {
 105                 /* just return an empty set until the job is submitted */
 106                 PrintJobAttributeSet jobSet = new HashPrintJobAttributeSet();
 107                 return AttributeSetUtilities.unmodifiableView(jobSet);
 108             } else {
 109                 return jobAttrSet;
 110             }
 111         }
 112     }
 113 
 114     public void addPrintJobListener(PrintJobListener listener) {
 115         synchronized (this) {
 116             if (listener == null) {
 117                 return;
 118             }
 119             if (jobListeners == null) {
 120                 jobListeners = new Vector();
 121             }
 122             jobListeners.add(listener);
 123         }
 124     }
 125 
 126     public void removePrintJobListener(PrintJobListener listener) {
 127         synchronized (this) {
 128             if (listener == null || jobListeners == null ) {
 129                 return;
 130             }
 131             jobListeners.remove(listener);
 132             if (jobListeners.isEmpty()) {
 133                 jobListeners = null;
 134             }
 135         }
 136     }
 137 
 138     /* Closes any stream already retrieved for the data.
 139      * We want to avoid unnecessarily asking the Doc to create a stream only
 140      * to get a reference in order to close it because the job failed.
 141      * If the representation class is itself a "stream", this
 142      * closes that stream too.
 143      */
 144     private void closeDataStreams() {
 145 
 146         if (doc == null) {
 147             return;
 148         }
 149 
 150         Object data = null;
 151 
 152         try {
 153             data = doc.getPrintData();
 154         } catch (IOException e) {
 155             return;
 156         }
 157 
 158         if (instream != null) {
 159             try {
 160                 instream.close();
 161             } catch (IOException e) {
 162             } finally {
 163                 instream = null;
 164             }
 165         }
 166         else if (reader != null) {
 167             try {
 168                 reader.close();
 169             } catch (IOException e) {
 170             } finally {
 171                 reader = null;
 172             }
 173         }
 174         else if (data instanceof InputStream) {
 175             try {
 176                 ((InputStream)data).close();
 177             } catch (IOException e) {
 178             }
 179         }
 180         else if (data instanceof Reader) {
 181             try {
 182                 ((Reader)data).close();
 183             } catch (IOException e) {
 184             }
 185         }
 186     }
 187 
 188     private void notifyEvent(int reason) {
 189         synchronized (this) {
 190             if (jobListeners != null) {
 191                 PrintJobListener listener;
 192                 PrintJobEvent event = new PrintJobEvent(this, reason);
 193                 for (int i = 0; i < jobListeners.size(); i++) {
 194                     listener = (PrintJobListener)(jobListeners.elementAt(i));
 195                     switch (reason) {
 196 
 197                         case PrintJobEvent.JOB_CANCELED :
 198                             listener.printJobCanceled(event);
 199                             break;
 200 
 201                         case PrintJobEvent.JOB_FAILED :
 202                             listener.printJobFailed(event);
 203                             break;
 204 
 205                         case PrintJobEvent.DATA_TRANSFER_COMPLETE :
 206                             listener.printDataTransferCompleted(event);
 207                             break;
 208 
 209                         case PrintJobEvent.NO_MORE_EVENTS :
 210                             listener.printJobNoMoreEvents(event);
 211                             break;
 212 
 213                         case PrintJobEvent.JOB_COMPLETE :
 214                             listener.printJobCompleted(event);
 215                             break;
 216 
 217                         default:
 218                             break;
 219                     }
 220                 }
 221             }
 222        }
 223     }
 224 
 225     public void addPrintJobAttributeListener(
 226                                   PrintJobAttributeListener listener,
 227                                   PrintJobAttributeSet attributes) {
 228         synchronized (this) {
 229             if (listener == null) {
 230                 return;
 231             }
 232             if (attrListeners == null) {
 233                 attrListeners = new Vector();
 234                 listenedAttributeSets = new Vector();
 235             }
 236             attrListeners.add(listener);
 237             if (attributes == null) {
 238                 attributes = new HashPrintJobAttributeSet();
 239             }
 240             listenedAttributeSets.add(attributes);
 241         }
 242     }
 243 
 244     public void removePrintJobAttributeListener(
 245                                         PrintJobAttributeListener listener) {
 246         synchronized (this) {
 247             if (listener == null || attrListeners == null ) {
 248                 return;
 249             }
 250             int index = attrListeners.indexOf(listener);
 251             if (index == -1) {
 252                 return;
 253             } else {
 254                 attrListeners.remove(index);
 255                 listenedAttributeSets.remove(index);
 256                 if (attrListeners.isEmpty()) {
 257                     attrListeners = null;
 258                     listenedAttributeSets = null;
 259                 }
 260             }
 261         }
 262     }
 263 
 264     public void print(Doc doc, PrintRequestAttributeSet attributes)
 265         throws PrintException {
 266 
 267         synchronized (this) {
 268             if (printing) {
 269                 throw new PrintException("already printing");
 270             } else {
 271                 printing = true;
 272             }
 273         }
 274 
 275         this.doc = doc;
 276         /* check if the parameters are valid before doing much processing */
 277         DocFlavor flavor = doc.getDocFlavor();
 278         Object data;
 279 
 280         try {
 281             data = doc.getPrintData();
 282         } catch (IOException e) {
 283             notifyEvent(PrintJobEvent.JOB_FAILED);
 284             throw new PrintException("can't get print data: " + e.toString());
 285         }
 286 
 287         if (flavor == null || (!service.isDocFlavorSupported(flavor))) {
 288             notifyEvent(PrintJobEvent.JOB_FAILED);
 289             throw new PrintJobFlavorException("invalid flavor", flavor);
 290         }
 291 
 292         initializeAttributeSets(doc, attributes);
 293 
 294         getAttributeValues(flavor);
 295 
 296         String repClassName = flavor.getRepresentationClassName();
 297         if (flavor.equals(DocFlavor.INPUT_STREAM.GIF) ||
 298             flavor.equals(DocFlavor.INPUT_STREAM.JPEG) ||
 299             flavor.equals(DocFlavor.INPUT_STREAM.PNG) ||
 300             flavor.equals(DocFlavor.BYTE_ARRAY.GIF) ||
 301             flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) ||
 302             flavor.equals(DocFlavor.BYTE_ARRAY.PNG)) {
 303             try {
 304                 instream = doc.getStreamForBytes();
 305                 printableJob(new ImagePrinter(instream), reqAttrSet);
 306                 return;
 307             } catch (ClassCastException cce) {
 308                 notifyEvent(PrintJobEvent.JOB_FAILED);
 309                 throw new PrintException(cce);
 310             } catch (IOException ioe) {
 311                 notifyEvent(PrintJobEvent.JOB_FAILED);
 312                 throw new PrintException(ioe);
 313             }
 314         } else if (flavor.equals(DocFlavor.URL.GIF) ||
 315                    flavor.equals(DocFlavor.URL.JPEG) ||
 316                    flavor.equals(DocFlavor.URL.PNG)) {
 317             try {
 318                 printableJob(new ImagePrinter((URL)data), reqAttrSet);
 319                 return;
 320             } catch (ClassCastException cce) {
 321                 notifyEvent(PrintJobEvent.JOB_FAILED);
 322                 throw new PrintException(cce);
 323             }
 324         } else if (repClassName.equals("java.awt.print.Pageable")) {
 325             try {
 326                 pageableJob((Pageable)doc.getPrintData(), reqAttrSet);
 327                 return;
 328             } catch (ClassCastException cce) {
 329                 notifyEvent(PrintJobEvent.JOB_FAILED);
 330                 throw new PrintException(cce);
 331             } catch (IOException ioe) {
 332                 notifyEvent(PrintJobEvent.JOB_FAILED);
 333                 throw new PrintException(ioe);
 334             }
 335         } else if (repClassName.equals("java.awt.print.Printable")) {
 336             try {
 337                 printableJob((Printable)doc.getPrintData(), reqAttrSet);
 338                 return;
 339             } catch (ClassCastException cce) {
 340                 notifyEvent(PrintJobEvent.JOB_FAILED);
 341                 throw new PrintException(cce);
 342             } catch (IOException ioe) {
 343                 notifyEvent(PrintJobEvent.JOB_FAILED);
 344                 throw new PrintException(ioe);
 345             }
 346         } else {
 347             notifyEvent(PrintJobEvent.JOB_FAILED);
 348             throw new PrintException("unrecognized class: "+repClassName);
 349         }
 350     }
 351 
 352     public void printableJob(Printable printable,
 353                              PrintRequestAttributeSet attributes)
 354         throws PrintException {
 355         try {
 356             synchronized(this) {
 357                 if (job != null) { // shouldn't happen
 358                     throw new PrintException("already printing");
 359                 } else {
 360                     job = new PSPrinterJob();
 361                 }
 362             }
 363             job.setPrintService(getPrintService());
 364             PageFormat pf = new PageFormat();
 365             if (mediaSize != null) {
 366                 Paper p = new Paper();
 367                 p.setSize(mediaSize.getX(MediaSize.INCH)*72.0,
 368                           mediaSize.getY(MediaSize.INCH)*72.0);
 369                 p.setImageableArea(72.0, 72.0, p.getWidth()-144.0,
 370                                    p.getHeight()-144.0);
 371                 pf.setPaper(p);
 372             }
 373             if (orient == OrientationRequested.REVERSE_LANDSCAPE) {
 374                 pf.setOrientation(PageFormat.REVERSE_LANDSCAPE);
 375             } else if (orient == OrientationRequested.LANDSCAPE) {
 376                 pf.setOrientation(PageFormat.LANDSCAPE);
 377             }
 378             job.setPrintable(printable, pf);
 379             job.print(attributes);
 380             notifyEvent(PrintJobEvent.JOB_COMPLETE);
 381             return;
 382         } catch (PrinterException pe) {
 383             notifyEvent(PrintJobEvent.JOB_FAILED);
 384             throw new PrintException(pe);
 385         } finally {
 386             printReturned = true;
 387         }
 388     }
 389 
 390     public void pageableJob(Pageable pageable,
 391                             PrintRequestAttributeSet attributes)
 392         throws PrintException {
 393         try {
 394             synchronized(this) {
 395                 if (job != null) { // shouldn't happen
 396                     throw new PrintException("already printing");
 397                 } else {
 398                     job = new PSPrinterJob();
 399                 }
 400             }
 401             job.setPrintService(getPrintService());
 402             job.setPageable(pageable);
 403             job.print(attributes);
 404             notifyEvent(PrintJobEvent.JOB_COMPLETE);
 405             return;
 406         } catch (PrinterException pe) {
 407             notifyEvent(PrintJobEvent.JOB_FAILED);
 408             throw new PrintException(pe);
 409         } finally {
 410             printReturned = true;
 411         }
 412     }
 413 
 414     /* There's some inefficiency here as the job set is created even though
 415      * it may never be requested.
 416      */
 417     private synchronized void
 418         initializeAttributeSets(Doc doc, PrintRequestAttributeSet reqSet) {
 419 
 420         reqAttrSet = new HashPrintRequestAttributeSet();
 421         jobAttrSet = new HashPrintJobAttributeSet();
 422 
 423         Attribute[] attrs;
 424         if (reqSet != null) {
 425             reqAttrSet.addAll(reqSet);
 426             attrs = reqSet.toArray();
 427             for (int i=0; i<attrs.length; i++) {
 428                 if (attrs[i] instanceof PrintJobAttribute) {
 429                     jobAttrSet.add(attrs[i]);
 430                 }
 431             }
 432         }
 433 
 434         DocAttributeSet docSet = doc.getAttributes();
 435         if (docSet != null) {
 436             attrs = docSet.toArray();
 437             for (int i=0; i<attrs.length; i++) {
 438                 if (attrs[i] instanceof PrintRequestAttribute) {
 439                     reqAttrSet.add(attrs[i]);
 440                 }
 441                 if (attrs[i] instanceof PrintJobAttribute) {
 442                     jobAttrSet.add(attrs[i]);
 443                 }
 444             }
 445         }
 446 
 447         /* add the user name to the job */
 448         String userName = "";
 449         try {
 450           userName = System.getProperty("user.name");
 451         } catch (SecurityException se) {
 452         }
 453 
 454         if (userName == null || userName.equals("")) {
 455             RequestingUserName ruName =
 456                 (RequestingUserName)reqSet.get(RequestingUserName.class);
 457             if (ruName != null) {
 458                 jobAttrSet.add(
 459                     new JobOriginatingUserName(ruName.getValue(),
 460                                                ruName.getLocale()));
 461             } else {
 462                 jobAttrSet.add(new JobOriginatingUserName("", null));
 463             }
 464         } else {
 465             jobAttrSet.add(new JobOriginatingUserName(userName, null));
 466         }
 467 
 468         /* if no job name supplied use doc name (if supplied), if none and
 469          * its a URL use that, else finally anything .. */
 470         if (jobAttrSet.get(JobName.class) == null) {
 471             JobName jobName;
 472             if (docSet != null && docSet.get(DocumentName.class) != null) {
 473                 DocumentName docName =
 474                     (DocumentName)docSet.get(DocumentName.class);
 475                 jobName = new JobName(docName.getValue(), docName.getLocale());
 476                 jobAttrSet.add(jobName);
 477             } else {
 478                 String str = "JPS Job:" + doc;
 479                 try {
 480                     Object printData = doc.getPrintData();
 481                     if (printData instanceof URL) {
 482                         str = ((URL)(doc.getPrintData())).toString();
 483                     }
 484                 } catch (IOException e) {
 485                 }
 486                 jobName = new JobName(str, null);
 487                 jobAttrSet.add(jobName);
 488             }
 489         }
 490 
 491         jobAttrSet = AttributeSetUtilities.unmodifiableView(jobAttrSet);
 492     }
 493 
 494     private void getAttributeValues(DocFlavor flavor) throws PrintException {
 495 
 496         Attribute attr;
 497         Class category;
 498 
 499         if (reqAttrSet.get(Fidelity.class) == Fidelity.FIDELITY_TRUE) {
 500             fidelity = true;
 501         } else {
 502             fidelity = false;
 503         }
 504 
 505         Attribute []attrs = reqAttrSet.toArray();
 506         for (int i=0; i<attrs.length; i++) {
 507             attr = attrs[i];
 508             category = attr.getCategory();
 509             if (fidelity == true) {
 510                 if (!service.isAttributeCategorySupported(category)) {
 511                     notifyEvent(PrintJobEvent.JOB_FAILED);
 512                     throw new PrintJobAttributeException(
 513                         "unsupported category: " + category, category, null);
 514                 } else if
 515                     (!service.isAttributeValueSupported(attr, flavor, null)) {
 516                     notifyEvent(PrintJobEvent.JOB_FAILED);
 517                     throw new PrintJobAttributeException(
 518                         "unsupported attribute: " + attr, null, attr);
 519                 }
 520             }
 521             if (category == JobName.class) {
 522                 jobName = ((JobName)attr).getValue();
 523             } else if (category == Copies.class) {
 524                 copies = ((Copies)attr).getValue();
 525             } else if (category == Media.class) {
 526                 if (attr instanceof MediaSizeName &&
 527                     service.isAttributeValueSupported(attr, null, null)) {
 528                     mediaSize =
 529                         MediaSize.getMediaSizeForName((MediaSizeName)attr);
 530                 }
 531             } else if (category == OrientationRequested.class) {
 532                 orient = (OrientationRequested)attr;
 533             }
 534         }
 535     }
 536 
 537     /* Cancel PrinterJob jobs that haven't yet completed. */
 538     public void cancel() throws PrintException {
 539         synchronized (this) {
 540             if (!printing) {
 541                 throw new PrintException("Job is not yet submitted.");
 542             } else if (job != null && !printReturned) {
 543                 job.cancel();
 544                 notifyEvent(PrintJobEvent.JOB_CANCELED);
 545                 return;
 546             } else {
 547                 throw new PrintException("Job could not be cancelled.");
 548             }
 549         }
 550     }
 551 
 552 }