1 /*
   2  * Copyright (c) 2000, 2018, 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.io.OutputStream;
  29 import java.util.Iterator;
  30 import java.util.Locale;
  31 
  32 import javax.print.DocFlavor;
  33 import javax.print.DocPrintJob;
  34 import javax.print.StreamPrintService;
  35 import javax.print.StreamPrintServiceFactory;
  36 import javax.print.ServiceUIFactory;
  37 import javax.print.attribute.Attribute;
  38 import javax.print.attribute.AttributeSet;
  39 import javax.print.attribute.AttributeSetUtilities;
  40 import javax.print.attribute.HashAttributeSet;
  41 import javax.print.attribute.HashPrintServiceAttributeSet;
  42 import javax.print.attribute.PrintServiceAttribute;
  43 import javax.print.attribute.PrintServiceAttributeSet;
  44 import javax.print.attribute.Size2DSyntax;
  45 import javax.print.event.PrintServiceAttributeListener;
  46 import javax.print.attribute.standard.JobName;
  47 import javax.print.attribute.standard.RequestingUserName;
  48 import javax.print.attribute.standard.Chromaticity;
  49 import javax.print.attribute.standard.ColorSupported;
  50 import javax.print.attribute.standard.Copies;
  51 import javax.print.attribute.standard.CopiesSupported;
  52 import javax.print.attribute.standard.Fidelity;
  53 import javax.print.attribute.standard.Media;
  54 import javax.print.attribute.standard.MediaPrintableArea;
  55 import javax.print.attribute.standard.MediaSize;
  56 import javax.print.attribute.standard.MediaSizeName;
  57 import javax.print.attribute.standard.OrientationRequested;
  58 import javax.print.attribute.standard.PageRanges;
  59 import javax.print.attribute.standard.SheetCollate;
  60 import javax.print.attribute.standard.Sides;
  61 
  62 public class PSStreamPrintService extends StreamPrintService
  63     implements SunPrinterJobService {
  64 
  65     private static final Class<?>[] suppAttrCats = {
  66         Chromaticity.class,
  67         Copies.class,
  68         Fidelity.class,
  69         JobName.class,
  70         Media.class,
  71         MediaPrintableArea.class,
  72         OrientationRequested.class,
  73         PageRanges.class,
  74         RequestingUserName.class,
  75         SheetCollate.class,
  76         Sides.class,
  77     };
  78 
  79     private static int MAXCOPIES = 1000;
  80 
  81     private static final MediaSizeName[] mediaSizes = {
  82         MediaSizeName.NA_LETTER,
  83         MediaSizeName.TABLOID,
  84         MediaSizeName.LEDGER,
  85         MediaSizeName.NA_LEGAL,
  86         MediaSizeName.EXECUTIVE,
  87         MediaSizeName.ISO_A3,
  88         MediaSizeName.ISO_A4,
  89         MediaSizeName.ISO_A5,
  90         MediaSizeName.ISO_B4,
  91         MediaSizeName.ISO_B5,
  92     };
  93 
  94     public PSStreamPrintService(OutputStream out) {
  95         super(out);
  96     }
  97 
  98     public String getOutputFormat() {
  99         return PSStreamPrinterFactory.psMimeType;
 100     }
 101 
 102 
 103     public DocFlavor[] getSupportedDocFlavors() {
 104         return PSStreamPrinterFactory.getFlavors();
 105     }
 106 
 107     public DocPrintJob createPrintJob() {
 108         return new PSStreamPrintJob(this);
 109     }
 110 
 111     public boolean usesClass(Class<?> c) {
 112         return (c == sun.print.PSPrinterJob.class);
 113     }
 114 
 115     public String getName() {
 116         return "Postscript output";
 117     }
 118 
 119     public void addPrintServiceAttributeListener(
 120                          PrintServiceAttributeListener listener) {
 121         return;
 122     }
 123 
 124     public void removePrintServiceAttributeListener(
 125                             PrintServiceAttributeListener listener) {
 126         return;
 127     }
 128 
 129 
 130     public <T extends PrintServiceAttribute>
 131         T getAttribute(Class<T> category)
 132     {
 133         if (category == null) {
 134             throw new NullPointerException("category");
 135         }
 136         if (!(PrintServiceAttribute.class.isAssignableFrom(category))) {
 137             throw new IllegalArgumentException("Not a PrintServiceAttribute");
 138         }
 139         if (category == ColorSupported.class) {
 140             @SuppressWarnings("unchecked")
 141             T tmp = (T)ColorSupported.SUPPORTED;
 142             return tmp;
 143         } else {
 144             return null;
 145         }
 146     }
 147     public PrintServiceAttributeSet getAttributes() {
 148         PrintServiceAttributeSet attrs = new HashPrintServiceAttributeSet();
 149         attrs.add(ColorSupported.SUPPORTED);
 150 
 151         return AttributeSetUtilities.unmodifiableView(attrs);
 152     }
 153 
 154     public boolean isDocFlavorSupported(DocFlavor flavor) {
 155         DocFlavor [] flavors = getSupportedDocFlavors();
 156         for (int f=0; f<flavors.length; f++) {
 157             if (flavor.equals(flavors[f])) {
 158                 return true;
 159             }
 160         }
 161         return false;
 162     }
 163 
 164 
 165     public Class<?>[] getSupportedAttributeCategories() {
 166         Class<?>[] cats = new Class<?>[suppAttrCats.length];
 167         System.arraycopy(suppAttrCats, 0, cats, 0, cats.length);
 168         return cats;
 169     }
 170 
 171     public boolean
 172         isAttributeCategorySupported(Class<? extends Attribute> category)
 173     {
 174         if (category == null) {
 175             throw new NullPointerException("null category");
 176         }
 177         if (!(Attribute.class.isAssignableFrom(category))) {
 178             throw new IllegalArgumentException(category +
 179                                              " is not an Attribute");
 180         }
 181 
 182         for (int i=0;i<suppAttrCats.length;i++) {
 183             if (category == suppAttrCats[i]) {
 184                 return true;
 185             }
 186         }
 187         return false;
 188     }
 189 
 190 
 191     public Object
 192         getDefaultAttributeValue(Class<? extends Attribute> category)
 193     {
 194         if (category == null) {
 195             throw new NullPointerException("null category");
 196         }
 197         if (!Attribute.class.isAssignableFrom(category)) {
 198             throw new IllegalArgumentException(category +
 199                                              " is not an Attribute");
 200         }
 201 
 202         if (!isAttributeCategorySupported(category)) {
 203             return null;
 204         }
 205 
 206         if (category == Copies.class) {
 207             return new Copies(1);
 208         } else if (category == Chromaticity.class) {
 209             return Chromaticity.COLOR;
 210         } else if (category == Fidelity.class) {
 211             return Fidelity.FIDELITY_FALSE;
 212         } else if (category == Media.class) {
 213             String defaultCountry = Locale.getDefault().getCountry();
 214             if (defaultCountry != null &&
 215                 (defaultCountry.equals("") ||
 216                  defaultCountry.equals(Locale.US.getCountry()) ||
 217                  defaultCountry.equals(Locale.CANADA.getCountry()))) {
 218                 return MediaSizeName.NA_LETTER;
 219             } else {
 220                  return MediaSizeName.ISO_A4;
 221             }
 222         } else if (category == MediaPrintableArea.class) {
 223             String defaultCountry = Locale.getDefault().getCountry();
 224             float iw, ih;
 225             float margin = 0.5f; // both these papers > 5" in all dimensions
 226             if (defaultCountry != null &&
 227                 (defaultCountry.equals("") ||
 228                  defaultCountry.equals(Locale.US.getCountry()) ||
 229                  defaultCountry.equals(Locale.CANADA.getCountry()))) {
 230                 iw = MediaSize.NA.LETTER.getX(Size2DSyntax.INCH) - 2*margin;
 231                 ih = MediaSize.NA.LETTER.getY(Size2DSyntax.INCH) - 2*margin;
 232             } else {
 233                 iw = MediaSize.ISO.A4.getX(Size2DSyntax.INCH) - 2*margin;
 234                 ih = MediaSize.ISO.A4.getY(Size2DSyntax.INCH) - 2*margin;
 235             }
 236             return new MediaPrintableArea(margin, margin, iw, ih,
 237                                           MediaPrintableArea.INCH);
 238         } else if (category == OrientationRequested.class) {
 239             return OrientationRequested.PORTRAIT;
 240         } else if (category == PageRanges.class) {
 241             return new PageRanges(1, Integer.MAX_VALUE);
 242         } else if (category == SheetCollate.class) {
 243             return SheetCollate.UNCOLLATED;
 244         } else if (category == Sides.class) {
 245             return Sides.ONE_SIDED;
 246 
 247         } else
 248             return null;
 249     }
 250 
 251 
 252     public Object
 253         getSupportedAttributeValues(Class<? extends Attribute> category,
 254                                     DocFlavor flavor,
 255                                     AttributeSet attributes)
 256     {
 257 
 258         if (category == null) {
 259             throw new NullPointerException("null category");
 260         }
 261         if (!Attribute.class.isAssignableFrom(category)) {
 262             throw new IllegalArgumentException(category +
 263                                              " does not implement Attribute");
 264         }
 265         if (flavor != null && !isDocFlavorSupported(flavor)) {
 266             throw new IllegalArgumentException(flavor +
 267                                                " is an unsupported flavor");
 268         }
 269 
 270         if (!isAttributeCategorySupported(category)) {
 271             return null;
 272         }
 273 
 274         if (category == Chromaticity.class) {
 275             Chromaticity[]arr = new Chromaticity[1];
 276             arr[0] = Chromaticity.COLOR;
 277             //arr[1] = Chromaticity.MONOCHROME;
 278             return (arr);
 279         } else if (category == JobName.class) {
 280             return new JobName("", null);
 281         } else if (category == RequestingUserName.class) {
 282             return new RequestingUserName("", null);
 283         } else if (category == OrientationRequested.class) {
 284             if (flavor == null ||
 285                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
 286                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||
 287                 flavor.equals(DocFlavor.INPUT_STREAM.GIF) ||
 288                 flavor.equals(DocFlavor.INPUT_STREAM.JPEG) ||
 289                 flavor.equals(DocFlavor.INPUT_STREAM.PNG) ||
 290                 flavor.equals(DocFlavor.BYTE_ARRAY.GIF) ||
 291                 flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) ||
 292                 flavor.equals(DocFlavor.BYTE_ARRAY.PNG) ||
 293                 flavor.equals(DocFlavor.URL.GIF) ||
 294                 flavor.equals(DocFlavor.URL.JPEG) ||
 295                 flavor.equals(DocFlavor.URL.PNG)) {
 296                 OrientationRequested []arr = new OrientationRequested[3];
 297                 arr[0] = OrientationRequested.PORTRAIT;
 298                 arr[1] = OrientationRequested.LANDSCAPE;
 299                 arr[2] = OrientationRequested.REVERSE_LANDSCAPE;
 300                 return arr;
 301             } else {
 302                 return null;
 303             }
 304         } else if ((category == Copies.class) ||
 305                    (category == CopiesSupported.class)) {
 306             return new CopiesSupported(1, MAXCOPIES);
 307         } else if (category == Media.class) {
 308             Media []arr = new Media[mediaSizes.length];
 309             System.arraycopy(mediaSizes, 0, arr, 0, mediaSizes.length);
 310             return arr;
 311         } else if (category == Fidelity.class) {
 312             Fidelity []arr = new Fidelity[2];
 313             arr[0] = Fidelity.FIDELITY_FALSE;
 314             arr[1] = Fidelity.FIDELITY_TRUE;
 315             return arr;
 316         } else if (category == MediaPrintableArea.class) {
 317             if (attributes == null) {
 318                 return null;
 319             }
 320             MediaSize mediaSize = (MediaSize)attributes.get(MediaSize.class);
 321             if (mediaSize == null) {
 322                 Media media = (Media)attributes.get(Media.class);
 323                 if (media != null && media instanceof MediaSizeName) {
 324                     MediaSizeName msn = (MediaSizeName)media;
 325                     mediaSize = MediaSize.getMediaSizeForName(msn);
 326                 }
 327             }
 328             if (mediaSize == null) {
 329                 return null;
 330             } else {
 331                 MediaPrintableArea []arr = new MediaPrintableArea[1];
 332                 float w = mediaSize.getX(MediaSize.INCH);
 333                 float h = mediaSize.getY(MediaSize.INCH);
 334                 /* For dimensions >= 5 inches use 0.5 inch margins.
 335                  * For smaller dimensions, use 10% margins.
 336                  */
 337                 float xmargin = 0.5f;
 338                 float ymargin = 0.5f;
 339                 if (w < 5f) {
 340                     xmargin = w/10;
 341                 }
 342                 if (h < 5f) {
 343                     ymargin = h/10;
 344                 }
 345                 arr[0] = new MediaPrintableArea(xmargin, ymargin,
 346                                                 w - 2*xmargin,
 347                                                 h - 2*ymargin,
 348                                                 MediaSize.INCH);
 349                 return arr;
 350             }
 351         } else if (category == PageRanges.class) {
 352             if (flavor == null ||
 353                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
 354                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
 355                 PageRanges []arr = new PageRanges[1];
 356                 arr[0] = new PageRanges(1, Integer.MAX_VALUE);
 357                 return arr;
 358             } else {
 359                 return null;
 360             }
 361         } else if (category == SheetCollate.class) {
 362             if (flavor == null ||
 363                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
 364                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
 365                 SheetCollate []arr = new SheetCollate[2];
 366                 arr[0] = SheetCollate.UNCOLLATED;
 367                 arr[1] = SheetCollate.COLLATED;
 368                 return arr;
 369             } else {
 370                 SheetCollate []arr = new SheetCollate[1];
 371                 arr[0] = SheetCollate.UNCOLLATED;
 372                 return arr;
 373             }
 374         } else if (category == Sides.class) {
 375             if (flavor == null ||
 376                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
 377                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
 378                 Sides []arr = new Sides[3];
 379                 arr[0] = Sides.ONE_SIDED;
 380                 arr[1] = Sides.TWO_SIDED_LONG_EDGE;
 381                 arr[2] = Sides.TWO_SIDED_SHORT_EDGE;
 382                 return arr;
 383             } else {
 384                 return null;
 385             }
 386         } else {
 387             return null;
 388         }
 389     }
 390 
 391     private boolean isSupportedCopies(Copies copies) {
 392         int numCopies = copies.getValue();
 393         return (numCopies > 0 && numCopies < MAXCOPIES);
 394     }
 395 
 396     private boolean isSupportedMedia(MediaSizeName msn) {
 397         for (int i=0; i<mediaSizes.length; i++) {
 398             if (msn.equals(mediaSizes[i])) {
 399                 return true;
 400             }
 401         }
 402         return false;
 403     }
 404 
 405     public boolean isAttributeValueSupported(Attribute attr,
 406                                              DocFlavor flavor,
 407                                              AttributeSet attributes) {
 408         if (attr == null) {
 409             throw new NullPointerException("null attribute");
 410         }
 411         if (flavor != null && !isDocFlavorSupported(flavor)) {
 412             throw new IllegalArgumentException(flavor +
 413                                                " is an unsupported flavor");
 414         }
 415         Class<? extends Attribute> category = attr.getCategory();
 416         if (!isAttributeCategorySupported(category)) {
 417             return false;
 418         }
 419         else if (attr.getCategory() == Chromaticity.class) {
 420             return attr == Chromaticity.COLOR;
 421         }
 422         else if (attr.getCategory() == Copies.class) {
 423             return isSupportedCopies((Copies)attr);
 424         } else if (attr.getCategory() == Media.class &&
 425                    attr instanceof MediaSizeName) {
 426             return isSupportedMedia((MediaSizeName)attr);
 427         } else if (attr.getCategory() == OrientationRequested.class) {
 428             if (attr == OrientationRequested.REVERSE_PORTRAIT ||
 429                 (flavor != null) &&
 430                 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
 431                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
 432                 return false;
 433             }
 434         } else if (attr.getCategory() == PageRanges.class) {
 435             if (flavor != null &&
 436                 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
 437                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
 438                 return false;
 439             }
 440         } else if (attr.getCategory() == SheetCollate.class) {
 441             if (flavor != null &&
 442                 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
 443                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
 444                 return false;
 445             }
 446         } else if (attr.getCategory() == Sides.class) {
 447             if (flavor != null &&
 448                 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
 449                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
 450                 return false;
 451             }
 452         }
 453         return true;
 454     }
 455 
 456     public AttributeSet getUnsupportedAttributes(DocFlavor flavor,
 457                                                  AttributeSet attributes) {
 458 
 459         if (flavor != null && !isDocFlavorSupported(flavor)) {
 460             throw new IllegalArgumentException("flavor " + flavor +
 461                                                "is not supported");
 462         }
 463 
 464         if (attributes == null) {
 465             return null;
 466         }
 467 
 468         Attribute attr;
 469         AttributeSet unsupp = new HashAttributeSet();
 470         Attribute[] attrs = attributes.toArray();
 471         for (int i=0; i<attrs.length; i++) {
 472             try {
 473                 attr = attrs[i];
 474                 if (!isAttributeCategorySupported(attr.getCategory())) {
 475                     unsupp.add(attr);
 476                 } else if (!isAttributeValueSupported(attr, flavor,
 477                                                       attributes)) {
 478                     unsupp.add(attr);
 479                 }
 480             } catch (ClassCastException e) {
 481             }
 482         }
 483         if (unsupp.isEmpty()) {
 484             return null;
 485         } else {
 486             return unsupp;
 487         }
 488     }
 489 
 490     public ServiceUIFactory getServiceUIFactory() {
 491         return null;
 492     }
 493 
 494     public String toString() {
 495         return "PSStreamPrintService: " + getName();
 496     }
 497 
 498     /* Stream services have an output stream which cannot be shared,
 499      * so two services are equal only if they are the same object.
 500      */
 501     public boolean equals(Object obj) {
 502         return (obj == this ||
 503                  (obj instanceof PSStreamPrintService &&
 504                  ((PSStreamPrintService)obj).getName().equals(getName())));
 505     }
 506 
 507    public int hashCode() {
 508         return this.getClass().hashCode()+getName().hashCode();
 509     }
 510 
 511 }