1 /*
   2  * Copyright (c) 2000, 2014, 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.awt.GraphicsEnvironment;
  29 import java.awt.Toolkit;
  30 import java.awt.Window;
  31 import java.awt.print.PrinterJob;
  32 import java.io.File;
  33 import java.net.URI;
  34 import java.net.URISyntaxException;
  35 import java.util.ArrayList;
  36 import java.util.HashMap;
  37 import javax.print.DocFlavor;
  38 import javax.print.DocPrintJob;
  39 import javax.print.PrintService;
  40 import javax.print.ServiceUIFactory;
  41 import javax.print.attribute.Attribute;
  42 import javax.print.attribute.AttributeSet;
  43 import javax.print.attribute.AttributeSetUtilities;
  44 import javax.print.attribute.EnumSyntax;
  45 import javax.print.attribute.HashAttributeSet;
  46 import javax.print.attribute.PrintRequestAttributeSet;
  47 import javax.print.attribute.PrintServiceAttribute;
  48 import javax.print.attribute.PrintServiceAttributeSet;
  49 import javax.print.attribute.HashPrintServiceAttributeSet;
  50 import javax.print.attribute.standard.PrinterName;
  51 import javax.print.attribute.standard.PrinterIsAcceptingJobs;
  52 import javax.print.attribute.standard.QueuedJobCount;
  53 import javax.print.attribute.standard.JobName;
  54 import javax.print.attribute.standard.RequestingUserName;
  55 import javax.print.attribute.standard.Chromaticity;
  56 import javax.print.attribute.standard.Copies;
  57 import javax.print.attribute.standard.CopiesSupported;
  58 import javax.print.attribute.standard.Destination;
  59 import javax.print.attribute.standard.DialogOwner;
  60 import javax.print.attribute.standard.DialogTypeSelection;
  61 import javax.print.attribute.standard.Fidelity;
  62 import javax.print.attribute.standard.Media;
  63 import javax.print.attribute.standard.MediaSizeName;
  64 import javax.print.attribute.standard.MediaSize;
  65 import javax.print.attribute.standard.MediaTray;
  66 import javax.print.attribute.standard.MediaPrintableArea;
  67 import javax.print.attribute.standard.OrientationRequested;
  68 import javax.print.attribute.standard.PageRanges;
  69 import javax.print.attribute.standard.PrinterState;
  70 import javax.print.attribute.standard.PrinterStateReason;
  71 import javax.print.attribute.standard.PrinterStateReasons;
  72 import javax.print.attribute.standard.Severity;
  73 import javax.print.attribute.standard.Sides;
  74 import javax.print.attribute.standard.ColorSupported;
  75 import javax.print.attribute.standard.PrintQuality;
  76 import javax.print.attribute.standard.PrinterResolution;
  77 import javax.print.attribute.standard.SheetCollate;
  78 import javax.print.event.PrintServiceAttributeListener;
  79 import sun.awt.windows.WPrinterJob;
  80 
  81 public class Win32PrintService implements PrintService, AttributeUpdater,
  82                                           SunPrinterJobService {
  83 
  84     public static MediaSize[] predefMedia = Win32MediaSize.getPredefMedia();
  85 
  86     private static final DocFlavor[] supportedFlavors = {
  87         DocFlavor.BYTE_ARRAY.GIF,
  88         DocFlavor.INPUT_STREAM.GIF,
  89         DocFlavor.URL.GIF,
  90         DocFlavor.BYTE_ARRAY.JPEG,
  91         DocFlavor.INPUT_STREAM.JPEG,
  92         DocFlavor.URL.JPEG,
  93         DocFlavor.BYTE_ARRAY.PNG,
  94         DocFlavor.INPUT_STREAM.PNG,
  95         DocFlavor.URL.PNG,
  96         DocFlavor.SERVICE_FORMATTED.PAGEABLE,
  97         DocFlavor.SERVICE_FORMATTED.PRINTABLE,
  98         DocFlavor.BYTE_ARRAY.AUTOSENSE,
  99         DocFlavor.URL.AUTOSENSE,
 100         DocFlavor.INPUT_STREAM.AUTOSENSE
 101     };
 102 
 103     /* let's try to support a few of these */
 104     private static final Class<?>[] serviceAttrCats = {
 105         PrinterName.class,
 106         PrinterIsAcceptingJobs.class,
 107         QueuedJobCount.class,
 108         ColorSupported.class,
 109     };
 110 
 111     /*  it turns out to be inconvenient to store the other categories
 112      *  separately because many attributes are in multiple categories.
 113      */
 114     private static Class<?>[] otherAttrCats = {
 115         JobName.class,
 116         RequestingUserName.class,
 117         Copies.class,
 118         Destination.class,
 119         OrientationRequested.class,
 120         PageRanges.class,
 121         Media.class,
 122         MediaPrintableArea.class,
 123         Fidelity.class,
 124         // We support collation on 2D printer jobs, even if the driver can't.
 125         SheetCollate.class,
 126         SunAlternateMedia.class,
 127         Chromaticity.class
 128     };
 129 
 130 
 131     /*
 132      * This table together with methods findWin32Media and
 133      * findMatchingMediaSizeNameMM are declared public as these are also
 134      * used in WPrinterJob.java.
 135      */
 136     public static final MediaSizeName[] dmPaperToPrintService = {
 137       MediaSizeName.NA_LETTER, MediaSizeName.NA_LETTER,
 138       MediaSizeName.TABLOID, MediaSizeName.LEDGER,
 139       MediaSizeName.NA_LEGAL, MediaSizeName.INVOICE,
 140       MediaSizeName.EXECUTIVE, MediaSizeName.ISO_A3,
 141       MediaSizeName.ISO_A4, MediaSizeName.ISO_A4,
 142       MediaSizeName.ISO_A5, MediaSizeName.JIS_B4,
 143       MediaSizeName.JIS_B5, MediaSizeName.FOLIO,
 144       MediaSizeName.QUARTO, MediaSizeName.NA_10X14_ENVELOPE,
 145       MediaSizeName.B, MediaSizeName.NA_LETTER,
 146       MediaSizeName.NA_NUMBER_9_ENVELOPE, MediaSizeName.NA_NUMBER_10_ENVELOPE,
 147       MediaSizeName.NA_NUMBER_11_ENVELOPE, MediaSizeName.NA_NUMBER_12_ENVELOPE,
 148       MediaSizeName.NA_NUMBER_14_ENVELOPE, MediaSizeName.C,
 149       MediaSizeName.D, MediaSizeName.E,
 150       MediaSizeName.ISO_DESIGNATED_LONG, MediaSizeName.ISO_C5,
 151       MediaSizeName.ISO_C3, MediaSizeName.ISO_C4,
 152       MediaSizeName.ISO_C6, MediaSizeName.ITALY_ENVELOPE,
 153       MediaSizeName.ISO_B4, MediaSizeName.ISO_B5,
 154       MediaSizeName.ISO_B6, MediaSizeName.ITALY_ENVELOPE,
 155       MediaSizeName.MONARCH_ENVELOPE, MediaSizeName.PERSONAL_ENVELOPE,
 156       MediaSizeName.NA_10X15_ENVELOPE, MediaSizeName.NA_9X12_ENVELOPE,
 157       MediaSizeName.FOLIO, MediaSizeName.ISO_B4,
 158       MediaSizeName.JAPANESE_POSTCARD, MediaSizeName.NA_9X11_ENVELOPE,
 159     };
 160 
 161     private static final MediaTray[] dmPaperBinToPrintService = {
 162       MediaTray.TOP, MediaTray.BOTTOM, MediaTray.MIDDLE,
 163       MediaTray.MANUAL, MediaTray.ENVELOPE, Win32MediaTray.ENVELOPE_MANUAL,
 164       Win32MediaTray.AUTO, Win32MediaTray.TRACTOR,
 165       Win32MediaTray.SMALL_FORMAT, Win32MediaTray.LARGE_FORMAT,
 166       MediaTray.LARGE_CAPACITY, null, null,
 167       MediaTray.MAIN, Win32MediaTray.FORMSOURCE,
 168     };
 169 
 170     // from wingdi.h
 171     private static int DM_PAPERSIZE = 0x2;
 172     private static int DM_PRINTQUALITY = 0x400;
 173     private static int DM_YRESOLUTION = 0x2000;
 174     private static final int DMRES_MEDIUM = -3;
 175     private static final int DMRES_HIGH = -4;
 176     private static final int DMORIENT_LANDSCAPE = 2;
 177     private static final int DMDUP_VERTICAL = 2;
 178     private static final int DMDUP_HORIZONTAL = 3;
 179     private static final int DMCOLLATE_TRUE = 1;
 180     private static final int DMCOLOR_MONOCHROME = 1;
 181     private static final int DMCOLOR_COLOR = 2;
 182 
 183 
 184     // media sizes with indices above dmPaperToPrintService' length
 185     private static final int DMPAPER_A2 = 66;
 186     private static final int DMPAPER_A6 = 70;
 187     private static final int DMPAPER_B6_JIS = 88;
 188 
 189 
 190     // Bit settings for getPrinterCapabilities which matches that
 191     // of native getCapabilities in WPrinterJob.cpp
 192     private static final int DEVCAP_COLOR = 0x0001;
 193     private static final int DEVCAP_DUPLEX = 0x0002;
 194     private static final int DEVCAP_COLLATE = 0x0004;
 195     private static final int DEVCAP_QUALITY = 0x0008;
 196     private static final int DEVCAP_POSTSCRIPT = 0x0010;
 197 
 198     private String printer;
 199     private PrinterName name;
 200     private String port;
 201 
 202     private transient PrintServiceAttributeSet lastSet;
 203     private transient ServiceNotifier notifier = null;
 204 
 205     private MediaSizeName[] mediaSizeNames;
 206     private MediaPrintableArea[] mediaPrintables;
 207     private MediaTray[] mediaTrays;
 208     private PrinterResolution[] printRes;
 209     private HashMap<MediaSizeName, MediaPrintableArea> mpaMap;
 210     private int nCopies;
 211     private int prnCaps;
 212     private int[] defaultSettings;
 213 
 214     private boolean gotTrays;
 215     private boolean gotCopies;
 216     private boolean mediaInitialized;
 217     private boolean mpaListInitialized;
 218 
 219     private ArrayList<Integer> idList;
 220     private MediaSize[] mediaSizes;
 221 
 222     private boolean isInvalid;
 223 
 224     Win32PrintService(String name) {
 225         if (name == null) {
 226             throw new IllegalArgumentException("null printer name");
 227         }
 228         printer = name;
 229 
 230         // initialize flags
 231         mediaInitialized = false;
 232         gotTrays = false;
 233         gotCopies = false;
 234         isInvalid = false;
 235         printRes = null;
 236         prnCaps = 0;
 237         defaultSettings = null;
 238         port = null;
 239     }
 240 
 241     public void invalidateService() {
 242         isInvalid = true;
 243     }
 244 
 245     public String getName() {
 246         return printer;
 247     }
 248 
 249     private PrinterName getPrinterName() {
 250         if (name == null) {
 251             name = new PrinterName(printer, null);
 252         }
 253         return name;
 254     }
 255 
 256     public int findPaperID(MediaSizeName msn) {
 257         if (msn instanceof Win32MediaSize) {
 258             Win32MediaSize winMedia = (Win32MediaSize)msn;
 259             return winMedia.getDMPaper();
 260         } else {
 261             for (int id=0; id<dmPaperToPrintService.length;id++) {
 262                 if (dmPaperToPrintService[id].equals(msn)) {
 263                     return id+1; // DMPAPER_LETTER == 1
 264                 }
 265             }
 266             if (msn.equals(MediaSizeName.ISO_A2)) {
 267                 return DMPAPER_A2;
 268             }
 269             else if (msn.equals(MediaSizeName.ISO_A6)) {
 270                 return DMPAPER_A6;
 271             }
 272             else if (msn.equals(MediaSizeName.JIS_B6)) {
 273                 return DMPAPER_B6_JIS;
 274             }
 275         }
 276 
 277         // If not found in predefined Windows ID, then we search through
 278         // the returned IDs of the driver because they can define their own
 279         // unique IDs.
 280         initMedia();
 281 
 282         if ((idList != null) && (mediaSizes != null) &&
 283             (idList.size() == mediaSizes.length)) {
 284             for (int i=0; i< idList.size(); i++) {
 285                 if (mediaSizes[i].getMediaSizeName() == msn) {
 286                     return idList.get(i).intValue();
 287                 }
 288             }
 289         }
 290         return 0;
 291     }
 292 
 293     public int findTrayID(MediaTray tray) {
 294 
 295         getMediaTrays(); // make sure they are initialised.
 296 
 297         if (tray instanceof Win32MediaTray) {
 298             Win32MediaTray winTray = (Win32MediaTray)tray;
 299             return winTray.getDMBinID();
 300         }
 301         for (int id=0; id<dmPaperBinToPrintService.length; id++) {
 302             if (tray.equals(dmPaperBinToPrintService[id])) {
 303                 return id+1; // DMBIN_FIRST = 1;
 304             }
 305         }
 306         return 0; // didn't find the tray
 307     }
 308 
 309     public MediaTray findMediaTray(int dmBin) {
 310         if (dmBin >= 1 && dmBin <= dmPaperBinToPrintService.length) {
 311             return dmPaperBinToPrintService[dmBin-1];
 312         }
 313         MediaTray[] trays = getMediaTrays();
 314         if (trays != null) {
 315             for (int i=0;i<trays.length;i++) {
 316                 if(trays[i] instanceof Win32MediaTray) {
 317                     Win32MediaTray win32Tray = (Win32MediaTray)trays[i];
 318                     if (win32Tray.winID == dmBin) {
 319                         return win32Tray;
 320                     }
 321                 }
 322             }
 323         }
 324         return Win32MediaTray.AUTO;
 325     }
 326 
 327     public MediaSizeName findWin32Media(int dmIndex) {
 328         if (dmIndex >= 1 && dmIndex <= dmPaperToPrintService.length) {
 329             return dmPaperToPrintService[dmIndex - 1];
 330         }
 331         switch(dmIndex) {
 332             /* matching media sizes with indices beyond
 333                dmPaperToPrintService's length */
 334             case DMPAPER_A2:
 335                 return MediaSizeName.ISO_A2;
 336             case DMPAPER_A6:
 337                 return MediaSizeName.ISO_A6;
 338             case DMPAPER_B6_JIS:
 339                 return MediaSizeName.JIS_B6;
 340             default:
 341                 return null;
 342         }
 343     }
 344 
 345     private boolean addToUniqueList(ArrayList<MediaSizeName> msnList,
 346                                     MediaSizeName mediaName) {
 347         MediaSizeName msn;
 348         for (int i=0; i< msnList.size(); i++) {
 349             msn = msnList.get(i);
 350             if (msn == mediaName) {
 351                 return false;
 352             }
 353         }
 354         msnList.add(mediaName);
 355         return true;
 356     }
 357 
 358     private synchronized void initMedia() {
 359         if (mediaInitialized == true) {
 360             return;
 361         }
 362         mediaInitialized = true;
 363         int[] media = getAllMediaIDs(printer, getPort());
 364         if (media == null) {
 365             return;
 366         }
 367 
 368         ArrayList<MediaSizeName> msnList = new ArrayList<>();
 369         ArrayList<Win32MediaSize> trailingWmsList = new ArrayList<Win32MediaSize>();
 370         MediaSizeName mediaName;
 371         boolean added;
 372         boolean queryFailure = false;
 373         float[] prnArea;
 374 
 375         // Get all mediaSizes supported by the printer.
 376         // We convert media to ArrayList idList and pass this to the
 377         // function for getting mediaSizes.
 378         // This is to ensure that mediaSizes and media IDs have 1-1 correspondence.
 379         // We remove from ID list any invalid mediaSize.  Though this is rare,
 380         // it happens in HP 4050 German driver.
 381 
 382         idList = new ArrayList<>();
 383         for (int i=0; i < media.length; i++) {
 384             idList.add(Integer.valueOf(media[i]));
 385         }
 386 
 387         ArrayList<String> dmPaperNameList = new ArrayList<String>();
 388         mediaSizes = getMediaSizes(idList, media, dmPaperNameList);
 389         for (int i = 0; i < idList.size(); i++) {
 390 
 391             // match Win ID with our predefined ID using table
 392             mediaName = findWin32Media(idList.get(i).intValue());
 393             // Verify that this standard size is the same size as that
 394             // reported by the driver. This should be the case except when
 395             // the driver is mis-using a standard windows paper ID.
 396             if (mediaName != null &&
 397                 idList.size() == mediaSizes.length) {
 398                 MediaSize win32Size = MediaSize.getMediaSizeForName(mediaName);
 399                 MediaSize driverSize = mediaSizes[i];
 400                 int error = 2540; // == 1/10"
 401                 if (Math.abs(win32Size.getX(1)-driverSize.getX(1)) > error ||
 402                     Math.abs(win32Size.getY(1)-driverSize.getY(1)) > error)
 403                 {
 404                    mediaName = null;
 405                 }
 406             }
 407             boolean dmPaperIDMatched = (mediaName != null);
 408 
 409             // No match found, then we get the MediaSizeName out of the MediaSize
 410             // This requires 1-1 correspondence, lengths must be checked.
 411             if ((mediaName == null) && (idList.size() == mediaSizes.length)) {
 412                 mediaName = mediaSizes[i].getMediaSizeName();
 413             }
 414 
 415             // Add mediaName to the msnList
 416             added = false;
 417             if (mediaName != null) {
 418                 added = addToUniqueList(msnList, mediaName);
 419             }
 420             if ((!dmPaperIDMatched || !added) && (idList.size() == dmPaperNameList.size())) {
 421                 /* The following block allows to add such media names to the list, whose sizes
 422                  * matched with media sizes predefined in JDK, while whose paper IDs did not,
 423                  * or whose sizes and paper IDs both did not match with any predefined in JDK.
 424                  */
 425                 Win32MediaSize wms = Win32MediaSize.findMediaName(dmPaperNameList.get(i));
 426                 if ((wms == null) && (idList.size() == mediaSizes.length)) {
 427                     wms = new Win32MediaSize(dmPaperNameList.get(i), idList.get(i));
 428                     mediaSizes[i] = new MediaSize(mediaSizes[i].getX(MediaSize.MM),
 429                         mediaSizes[i].getY(MediaSize.MM), MediaSize.MM, wms);
 430                 }
 431                 if ((wms != null) && (wms != mediaName)) {
 432                     if (!added) {
 433                         added = addToUniqueList(msnList, mediaName = wms);
 434                     } else {
 435                         trailingWmsList.add(wms);
 436                     }
 437                 }
 438             }
 439         }
 440         for (Win32MediaSize wms : trailingWmsList) {
 441             added = addToUniqueList(msnList, wms);
 442         }
 443 
 444         // init mediaSizeNames
 445         mediaSizeNames = new MediaSizeName[msnList.size()];
 446         msnList.toArray(mediaSizeNames);
 447     }
 448 
 449 
 450     /*
 451      * Gets a list of MediaPrintableAreas using a call to native function.
 452      *  msn is MediaSizeName used to get a specific printable area.  If null,
 453      *  it will get all the supported MediPrintableAreas.
 454      */
 455     private synchronized MediaPrintableArea[] getMediaPrintables(MediaSizeName msn)
 456     {
 457         if (msn == null)  {
 458             if (mpaListInitialized == true) {
 459                 return mediaPrintables;
 460             }
 461         } else {
 462             // get from cached mapping of MPAs
 463             if (mpaMap != null && (mpaMap.get(msn) != null)) {
 464                 MediaPrintableArea[] mpaArr = new MediaPrintableArea[1];
 465                 mpaArr[0] = mpaMap.get(msn);
 466                 return mpaArr;
 467             }
 468         }
 469 
 470         initMedia();
 471 
 472         if ((mediaSizeNames == null) || (mediaSizeNames.length == 0)) {
 473             return null;
 474         }
 475 
 476         MediaSizeName[] loopNames;
 477         if (msn != null) {
 478             loopNames = new MediaSizeName[1];
 479             loopNames[0] = msn;
 480         } else {
 481             loopNames = mediaSizeNames;
 482         }
 483 
 484         if (mpaMap == null) {
 485             mpaMap = new HashMap<>();
 486         }
 487 
 488         for (int i=0; i < loopNames.length; i++) {
 489             MediaSizeName mediaName = loopNames[i];
 490 
 491             if (mpaMap.get(mediaName) != null) {
 492                 continue;
 493              }
 494 
 495             if (mediaName != null) {
 496                 int defPaper = findPaperID(mediaName);
 497                 float[] prnArea = (defPaper != 0) ? getMediaPrintableArea(printer, defPaper) : null;
 498                 MediaPrintableArea printableArea = null;
 499                 if (prnArea != null) {
 500                     try {
 501                         printableArea = new MediaPrintableArea(prnArea[0],
 502                                                                prnArea[1],
 503                                                                prnArea[2],
 504                                                                prnArea[3],
 505                                                  MediaPrintableArea.INCH);
 506 
 507                         mpaMap.put(mediaName, printableArea);
 508                     }
 509                     catch (IllegalArgumentException e) {
 510                     }
 511                 } else {
 512                     // if getting  MPA failed, we use MediaSize
 513                     MediaSize ms = MediaSize.getMediaSizeForName(mediaName);
 514 
 515                     if (ms != null) {
 516                         try {
 517                             printableArea = new MediaPrintableArea(0, 0,
 518                                                      ms.getX(MediaSize.INCH),
 519                                                      ms.getY(MediaSize.INCH),
 520                                                      MediaPrintableArea.INCH);
 521                             mpaMap.put(mediaName, printableArea);
 522                         } catch (IllegalArgumentException e) {
 523                         }
 524                     }
 525                 }
 526             } //mediaName != null
 527         }
 528 
 529        if (mpaMap.size() == 0) {
 530            return null;
 531        }
 532 
 533        if (msn != null) {
 534            if (mpaMap.get(msn) == null) {
 535                return null;
 536            }
 537            MediaPrintableArea[] mpaArr = new MediaPrintableArea[1];
 538            // by this time, we've already gotten the desired MPA
 539            mpaArr[0] = mpaMap.get(msn);
 540            return mpaArr;
 541        } else {
 542            mediaPrintables = mpaMap.values().toArray(new MediaPrintableArea[0]);
 543            mpaListInitialized = true;
 544            return mediaPrintables;
 545        }
 546     }
 547 
 548 
 549     private synchronized MediaTray[] getMediaTrays() {
 550         if (gotTrays == true && mediaTrays != null) {
 551             return mediaTrays;
 552         }
 553         String prnPort = getPort();
 554         int[] mediaTr = getAllMediaTrays(printer, prnPort);
 555         String[] winMediaTrayNames = getAllMediaTrayNames(printer, prnPort);
 556 
 557         if ((mediaTr == null) || (winMediaTrayNames == null)){
 558             return null;
 559         }
 560 
 561         /* first count how many valid bins there are so we can allocate
 562          * an array of the correct size
 563          */
 564         int nTray = 0;
 565         for (int i=0; i < mediaTr.length ; i++) {
 566             if (mediaTr[i] > 0) nTray++;
 567         }
 568 
 569         MediaTray[] arr = new MediaTray[nTray];
 570         int dmBin;
 571 
 572         /* Some drivers in Win 7 don't have the same length for DC_BINS and
 573          * DC_BINNAMES so there is no guarantee that lengths of mediaTr and
 574          * winMediaTrayNames are equal. To avoid getting ArrayIndexOutOfBounds,
 575          * we need to make sure we get the minimum of the two.
 576          */
 577 
 578         for (int i = 0, j=0; i < Math.min(mediaTr.length, winMediaTrayNames.length); i++) {
 579             dmBin = mediaTr[i];
 580             if (dmBin > 0) {
 581                 // check for unsupported DMBINs and create new Win32MediaTray
 582                 if ((dmBin > dmPaperBinToPrintService.length)
 583                     || (dmPaperBinToPrintService[dmBin-1] == null)) {
 584                     arr[j++] = new Win32MediaTray(dmBin, winMediaTrayNames[i]);
 585                 } else {
 586                     arr[j++] = dmPaperBinToPrintService[dmBin-1];
 587                 }
 588             }
 589             // no else - For invalid ids, just ignore it because assigning a "default"
 590             // value may result in duplicate trays.
 591         }
 592         mediaTrays = arr;
 593         gotTrays = true;
 594         return mediaTrays;
 595     }
 596 
 597     private boolean isSameSize(float w1, float h1, float w2, float h2) {
 598         float diffX = w1 - w2;
 599         float diffY = h1 - h2;
 600         // Get diff of reverse dimensions
 601         // EPSON Stylus COLOR 860 reverses envelope's width & height
 602         float diffXrev = w1 - h2;
 603         float diffYrev = h1 - w2;
 604 
 605         if (((Math.abs(diffX)<=1) && (Math.abs(diffY)<=1)) ||
 606             ((Math.abs(diffXrev)<=1) && (Math.abs(diffYrev)<=1))){
 607           return true;
 608         } else {
 609           return false;
 610         }
 611     }
 612 
 613     public MediaSizeName findMatchingMediaSizeNameMM (float w, float h){
 614         if (predefMedia != null) {
 615             for (int k=0; k<predefMedia.length;k++) {
 616                 if (predefMedia[k] == null) {
 617                     continue;
 618                 }
 619 
 620                 if (isSameSize(predefMedia[k].getX(MediaSize.MM),
 621                                predefMedia[k].getY(MediaSize.MM),
 622                                w, h)) {
 623                   return predefMedia[k].getMediaSizeName();
 624                 }
 625             }
 626         }
 627         return null;
 628     }
 629 
 630 
 631     private MediaSize[] getMediaSizes(ArrayList<Integer> idList, int[] media,
 632                                       ArrayList<String> dmPaperNameList) {
 633         if (dmPaperNameList == null) {
 634             dmPaperNameList = new ArrayList<String>();
 635         }
 636 
 637         String prnPort = getPort();
 638         int[] mediaSz = getAllMediaSizes(printer, prnPort);
 639         String[] winMediaNames = getAllMediaNames(printer, prnPort);
 640         MediaSizeName msn = null;
 641         MediaSize ms = null;
 642         float wid, ht;
 643 
 644         if ((mediaSz == null) || (winMediaNames == null)) {
 645             return null;
 646         }
 647 
 648         int nMedia = mediaSz.length/2;
 649         ArrayList<MediaSize> msList = new ArrayList<>();
 650 
 651         for (int i = 0; i < nMedia; i++, ms=null) {
 652             wid = mediaSz[i*2]/10f;
 653             ht = mediaSz[i*2+1]/10f;
 654 
 655             // Make sure to validate wid & ht.
 656             // HP LJ 4050 (german) causes IAE in Sonderformat paper, wid & ht
 657             // returned is not constant.
 658             if ((wid <= 0) || (ht <= 0)) {
 659                 //Remove corresponding ID from list
 660                 if (nMedia == media.length) {
 661                     Integer remObj = Integer.valueOf(media[i]);
 662                     idList.remove(idList.indexOf(remObj));
 663                 }
 664                 continue;
 665             }
 666             // Find matching media using dimensions.
 667             // This call matches only with our own predefined sizes.
 668             msn = findMatchingMediaSizeNameMM(wid, ht);
 669             if (msn != null) {
 670                 ms = MediaSize.getMediaSizeForName(msn);
 671             }
 672 
 673             if (ms != null) {
 674                 msList.add(ms);
 675                 dmPaperNameList.add(winMediaNames[i]);
 676             } else {
 677                 Win32MediaSize wms = Win32MediaSize.findMediaName(winMediaNames[i]);
 678                 if (wms == null) {
 679                     wms = new Win32MediaSize(winMediaNames[i], media[i]);
 680                 }
 681                 try {
 682                     ms = new MediaSize(wid, ht, MediaSize.MM, wms);
 683                     msList.add(ms);
 684                     dmPaperNameList.add(winMediaNames[i]);
 685                 } catch(IllegalArgumentException e) {
 686                     if (nMedia == media.length) {
 687                         Integer remObj = Integer.valueOf(media[i]);
 688                         idList.remove(idList.indexOf(remObj));
 689                     }
 690                 }
 691             }
 692         }
 693 
 694         MediaSize[] arr2 = new MediaSize[msList.size()];
 695         msList.toArray(arr2);
 696 
 697         return arr2;
 698     }
 699 
 700     private PrinterIsAcceptingJobs getPrinterIsAcceptingJobs() {
 701         if (getJobStatus(printer, 2) != 1) {
 702             return PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS;
 703         }
 704         else {
 705             return PrinterIsAcceptingJobs.ACCEPTING_JOBS;
 706         }
 707     }
 708 
 709     private PrinterState getPrinterState() {
 710         if (isInvalid) {
 711             return PrinterState.STOPPED;
 712         } else {
 713             return null;
 714         }
 715     }
 716 
 717     private PrinterStateReasons getPrinterStateReasons() {
 718         if (isInvalid) {
 719             PrinterStateReasons psr = new PrinterStateReasons();
 720             psr.put(PrinterStateReason.SHUTDOWN, Severity.ERROR);
 721             return psr;
 722         } else {
 723             return null;
 724         }
 725     }
 726 
 727     private QueuedJobCount getQueuedJobCount() {
 728 
 729         int count = getJobStatus(printer, 1);
 730         if (count != -1) {
 731             return new QueuedJobCount(count);
 732         }
 733         else {
 734             return new QueuedJobCount(0);
 735         }
 736     }
 737 
 738     private boolean isSupportedCopies(Copies copies) {
 739         synchronized (this) {
 740             if (gotCopies == false) {
 741                 nCopies = getCopiesSupported(printer, getPort());
 742                 gotCopies = true;
 743             }
 744         }
 745         int numCopies = copies.getValue();
 746         return (numCopies > 0 && numCopies <= nCopies);
 747     }
 748 
 749     private boolean isSupportedMedia(MediaSizeName msn) {
 750 
 751         initMedia();
 752 
 753         if (mediaSizeNames != null) {
 754             for (int i=0; i<mediaSizeNames.length; i++) {
 755                 if (msn.equals(mediaSizeNames[i])) {
 756                     return true;
 757                 }
 758             }
 759         }
 760         return false;
 761     }
 762 
 763     private boolean isSupportedMediaPrintableArea(MediaPrintableArea mpa) {
 764 
 765         getMediaPrintables(null);
 766         int units = MediaPrintableArea.INCH;
 767 
 768         if (mediaPrintables != null) {
 769             for (int i=0; i<mediaPrintables.length; i++) {
 770                 if ((mpa.getX(units) >= mediaPrintables[i].getX(units)) &&
 771                     (mpa.getY(units) >= mediaPrintables[i].getY(units)) &&
 772                     (mpa.getX(units) + mpa.getWidth(units) <=
 773                             mediaPrintables[i].getX(units) +
 774                             mediaPrintables[i].getWidth(units)) &&
 775                     (mpa.getY(units) + mpa.getHeight(units) <=
 776                             mediaPrintables[i].getY(units) +
 777                             mediaPrintables[i].getHeight(units))) {
 778                     return true;
 779                 }
 780             }
 781         }
 782         return false;
 783     }
 784 
 785     private boolean isSupportedMediaTray(MediaTray msn) {
 786         MediaTray[] trays = getMediaTrays();
 787 
 788         if (trays != null) {
 789             for (int i=0; i<trays.length; i++) {
 790                 if (msn.equals(trays[i])) {
 791                     return true;
 792                 }
 793             }
 794         }
 795         return false;
 796     }
 797 
 798     private int getPrinterCapabilities() {
 799         if (prnCaps == 0) {
 800             prnCaps = getCapabilities(printer, getPort());
 801         }
 802         return prnCaps;
 803     }
 804 
 805     private String getPort() {
 806         if (port == null) {
 807             port = getPrinterPort(printer);
 808         }
 809         return port;
 810     }
 811 
 812    /*
 813     * NOTE: defaults indices must match those in WPrinterJob.cpp
 814     */
 815     private int[] getDefaultPrinterSettings() {
 816         if (defaultSettings == null) {
 817             defaultSettings = getDefaultSettings(printer, getPort());
 818         }
 819         return defaultSettings;
 820     }
 821 
 822     private PrinterResolution[] getPrintResolutions() {
 823         if (printRes == null) {
 824             int[] prnRes = getAllResolutions(printer, getPort());
 825             if (prnRes == null) {
 826                 printRes = new PrinterResolution[0];
 827             } else {
 828                 int nRes = prnRes.length/2;
 829 
 830                 ArrayList<PrinterResolution> arrList = new ArrayList<>();
 831                 PrinterResolution pr;
 832 
 833                 for (int i=0; i<nRes; i++) {
 834                   try {
 835                         pr = new PrinterResolution(prnRes[i*2],
 836                                        prnRes[i*2+1], PrinterResolution.DPI);
 837                         arrList.add(pr);
 838                     } catch (IllegalArgumentException e) {
 839                     }
 840                 }
 841 
 842                 printRes = arrList.toArray(new PrinterResolution[arrList.size()]);
 843             }
 844         }
 845         return printRes;
 846     }
 847 
 848     private boolean isSupportedResolution(PrinterResolution res) {
 849         PrinterResolution[] supportedRes = getPrintResolutions();
 850         if (supportedRes != null) {
 851             for (int i=0; i<supportedRes.length; i++) {
 852                 if (res.equals(supportedRes[i])) {
 853                     return true;
 854                 }
 855             }
 856         }
 857         return false;
 858     }
 859 
 860     public DocPrintJob createPrintJob() {
 861       SecurityManager security = System.getSecurityManager();
 862       if (security != null) {
 863         security.checkPrintJobAccess();
 864       }
 865         return new Win32PrintJob(this);
 866     }
 867 
 868     private PrintServiceAttributeSet getDynamicAttributes() {
 869         PrintServiceAttributeSet attrs = new HashPrintServiceAttributeSet();
 870         attrs.add(getPrinterIsAcceptingJobs());
 871         attrs.add(getQueuedJobCount());
 872         return attrs;
 873     }
 874 
 875     public PrintServiceAttributeSet getUpdatedAttributes() {
 876         PrintServiceAttributeSet currSet = getDynamicAttributes();
 877         if (lastSet == null) {
 878             lastSet = currSet;
 879             return AttributeSetUtilities.unmodifiableView(currSet);
 880         } else {
 881             PrintServiceAttributeSet updates =
 882                 new HashPrintServiceAttributeSet();
 883             Attribute []attrs =  currSet.toArray();
 884             for (int i=0; i<attrs.length; i++) {
 885                 Attribute attr = attrs[i];
 886                 if (!lastSet.containsValue(attr)) {
 887                     updates.add(attr);
 888                 }
 889             }
 890             lastSet = currSet;
 891             return AttributeSetUtilities.unmodifiableView(updates);
 892         }
 893     }
 894 
 895     public void wakeNotifier() {
 896         synchronized (this) {
 897             if (notifier != null) {
 898                 notifier.wake();
 899             }
 900         }
 901     }
 902 
 903     public void addPrintServiceAttributeListener(PrintServiceAttributeListener
 904                                                  listener) {
 905         synchronized (this) {
 906             if (listener == null) {
 907                 return;
 908             }
 909             if (notifier == null) {
 910                 notifier = new ServiceNotifier(this);
 911             }
 912             notifier.addListener(listener);
 913         }
 914     }
 915 
 916     public void removePrintServiceAttributeListener(
 917                                       PrintServiceAttributeListener listener) {
 918         synchronized (this) {
 919             if (listener == null || notifier == null ) {
 920                 return;
 921             }
 922             notifier.removeListener(listener);
 923             if (notifier.isEmpty()) {
 924                 notifier.stopNotifier();
 925                 notifier = null;
 926             }
 927         }
 928     }
 929 
 930     @SuppressWarnings("unchecked")
 931     public <T extends PrintServiceAttribute> T
 932         getAttribute(Class<T> category)
 933     {
 934         if (category == null) {
 935             throw new NullPointerException("category");
 936         }
 937         if (!(PrintServiceAttribute.class.isAssignableFrom(category))) {
 938             throw new IllegalArgumentException("Not a PrintServiceAttribute");
 939         }
 940         if (category == ColorSupported.class) {
 941             int caps = getPrinterCapabilities();
 942             if ((caps & DEVCAP_COLOR) != 0) {
 943                 return (T)ColorSupported.SUPPORTED;
 944             } else {
 945                 return (T)ColorSupported.NOT_SUPPORTED;
 946             }
 947         } else if (category == PrinterName.class) {
 948             return (T)getPrinterName();
 949         } else if (category == PrinterState.class) {
 950             return (T)getPrinterState();
 951         } else if (category == PrinterStateReasons.class) {
 952             return (T)getPrinterStateReasons();
 953         } else if (category == QueuedJobCount.class) {
 954             return (T)getQueuedJobCount();
 955         } else if (category == PrinterIsAcceptingJobs.class) {
 956             return (T)getPrinterIsAcceptingJobs();
 957         } else {
 958             return null;
 959         }
 960     }
 961 
 962     public PrintServiceAttributeSet getAttributes() {
 963 
 964         PrintServiceAttributeSet attrs = new  HashPrintServiceAttributeSet();
 965         attrs.add(getPrinterName());
 966         attrs.add(getPrinterIsAcceptingJobs());
 967         PrinterState prnState = getPrinterState();
 968         if (prnState != null) {
 969             attrs.add(prnState);
 970         }
 971         PrinterStateReasons prnStateReasons = getPrinterStateReasons();
 972         if (prnStateReasons != null) {
 973             attrs.add(prnStateReasons);
 974         }
 975         attrs.add(getQueuedJobCount());
 976         int caps = getPrinterCapabilities();
 977         if ((caps & DEVCAP_COLOR) != 0) {
 978             attrs.add(ColorSupported.SUPPORTED);
 979         } else {
 980             attrs.add(ColorSupported.NOT_SUPPORTED);
 981         }
 982 
 983         return AttributeSetUtilities.unmodifiableView(attrs);
 984     }
 985 
 986     public DocFlavor[] getSupportedDocFlavors() {
 987         int len = supportedFlavors.length;
 988         DocFlavor[] supportedDocFlavors;
 989         int caps = getPrinterCapabilities();
 990         // doc flavors supported
 991         // if PostScript is supported
 992         if ((caps & DEVCAP_POSTSCRIPT) != 0) {
 993             supportedDocFlavors = new DocFlavor[len+3];
 994             System.arraycopy(supportedFlavors, 0, supportedDocFlavors, 0, len);
 995             supportedDocFlavors[len] = DocFlavor.BYTE_ARRAY.POSTSCRIPT;
 996             supportedDocFlavors[len+1] = DocFlavor.INPUT_STREAM.POSTSCRIPT;
 997             supportedDocFlavors[len+2] = DocFlavor.URL.POSTSCRIPT;
 998         } else {
 999             supportedDocFlavors = new DocFlavor[len];
1000             System.arraycopy(supportedFlavors, 0, supportedDocFlavors, 0, len);
1001         }
1002         return supportedDocFlavors;
1003     }
1004 
1005     public boolean isDocFlavorSupported(DocFlavor flavor) {
1006         /* To avoid a native query which may be time-consuming
1007          * do not invoke native unless postscript support is being queried.
1008          * Instead just check the ones we 'always' support
1009          */
1010         DocFlavor[] supportedDocFlavors;
1011         if (isPostScriptFlavor(flavor)) {
1012             supportedDocFlavors = getSupportedDocFlavors();
1013         } else {
1014             supportedDocFlavors = supportedFlavors;
1015         }
1016         for (int f=0; f<supportedDocFlavors.length; f++) {
1017             if (flavor.equals(supportedDocFlavors[f])) {
1018                 return true;
1019             }
1020         }
1021         return false;
1022     }
1023 
1024     public Class<?>[] getSupportedAttributeCategories() {
1025         ArrayList<Class<?>> categList = new ArrayList<>(otherAttrCats.length+3);
1026         for (int i=0; i < otherAttrCats.length; i++) {
1027             categList.add(otherAttrCats[i]);
1028         }
1029 
1030         int caps = getPrinterCapabilities();
1031 
1032         if ((caps & DEVCAP_DUPLEX) != 0) {
1033             categList.add(Sides.class);
1034         }
1035 
1036         if ((caps & DEVCAP_QUALITY) != 0) {
1037             int[] defaults = getDefaultPrinterSettings();
1038             // Added check: if supported, we should be able to get the default.
1039             if ((defaults[3] >= DMRES_HIGH) && (defaults[3] < 0)) {
1040                 categList.add(PrintQuality.class);
1041             }
1042         }
1043 
1044         PrinterResolution[] supportedRes = getPrintResolutions();
1045         if ((supportedRes!=null) && (supportedRes.length>0)) {
1046             categList.add(PrinterResolution.class);
1047         }
1048 
1049         if (GraphicsEnvironment.isHeadless() == false) {
1050             categList.add(DialogOwner.class);
1051             categList.add(DialogTypeSelection.class);
1052         }
1053         return categList.toArray(new Class<?>[categList.size()]);
1054     }
1055 
1056     public boolean
1057         isAttributeCategorySupported(Class<? extends Attribute> category)
1058     {
1059 
1060         if (category == null) {
1061             throw new NullPointerException("null category");
1062         }
1063 
1064         if (!(Attribute.class.isAssignableFrom(category))) {
1065             throw new IllegalArgumentException(category +
1066                                                " is not an Attribute");
1067         }
1068 
1069         Class<?>[] classList = getSupportedAttributeCategories();
1070         for (int i = 0; i < classList.length; i++) {
1071             if (category.equals(classList[i])) {
1072                 return true;
1073             }
1074         }
1075 
1076         return false;
1077     }
1078 
1079     public Object
1080         getDefaultAttributeValue(Class<? extends Attribute> category)
1081     {
1082         if (category == null) {
1083             throw new NullPointerException("null category");
1084         }
1085         if (!Attribute.class.isAssignableFrom(category)) {
1086             throw new IllegalArgumentException(category +
1087                                                " is not an Attribute");
1088         }
1089 
1090         if (!isAttributeCategorySupported(category)) {
1091             return null;
1092         }
1093 
1094         int[] defaults = getDefaultPrinterSettings();
1095         // indices must match those in WPrinterJob.cpp
1096         int defPaper = defaults[0];
1097         int defYRes = defaults[2];
1098         int defQuality = defaults[3];
1099         int defCopies = defaults[4];
1100         int defOrient = defaults[5];
1101         int defSides = defaults[6];
1102         int defCollate = defaults[7];
1103         int defColor = defaults[8];
1104 
1105         if (category == Copies.class) {
1106             if (defCopies > 0) {
1107                 return new Copies(defCopies);
1108             } else {
1109                 return new Copies(1);
1110             }
1111         } else if (category == Chromaticity.class) {
1112             if (defColor == DMCOLOR_COLOR) {
1113                 return Chromaticity.COLOR;
1114             } else {
1115                 return Chromaticity.MONOCHROME;
1116             }
1117         } else if (category == JobName.class) {
1118             return new JobName("Java Printing", null);
1119         } else if (category == OrientationRequested.class) {
1120             if (defOrient == DMORIENT_LANDSCAPE) {
1121                 return OrientationRequested.LANDSCAPE;
1122             } else {
1123                 return OrientationRequested.PORTRAIT;
1124             }
1125         } else if (category == PageRanges.class) {
1126             return new PageRanges(1, Integer.MAX_VALUE);
1127         } else if (category == Media.class) {
1128             MediaSizeName msn = findWin32Media(defPaper);
1129             if (msn != null) {
1130                 if (!isSupportedMedia(msn) && mediaSizeNames != null) {
1131                     msn = mediaSizeNames[0];
1132                     defPaper = findPaperID(msn);
1133                 }
1134                 return msn;
1135              } else {
1136                  initMedia();
1137                  if ((mediaSizeNames != null) && (mediaSizeNames.length > 0)) {
1138                      // if 'mediaSizeNames' is not null, idList and mediaSizes
1139                      // cannot be null but to be safe, add a check
1140                      if ((idList != null) && (mediaSizes != null) &&
1141                          (idList.size() == mediaSizes.length)) {
1142                          Integer defIdObj = Integer.valueOf(defPaper);
1143                          int index = idList.indexOf(defIdObj);
1144                          if (index>=0 && index<mediaSizes.length) {
1145                              return mediaSizes[index].getMediaSizeName();
1146                          }
1147                      }
1148 
1149                      return mediaSizeNames[0];
1150                  }
1151              }
1152         } else if (category == MediaPrintableArea.class) {
1153             /* Verify defPaper */
1154             MediaSizeName msn = findWin32Media(defPaper);
1155             if (msn != null &&
1156                 !isSupportedMedia(msn) && mediaSizeNames != null) {
1157                 defPaper = findPaperID(mediaSizeNames[0]);
1158             }
1159             float[] prnArea = getMediaPrintableArea(printer, defPaper);
1160             if (prnArea != null) {
1161                 MediaPrintableArea printableArea = null;
1162                 try {
1163                     printableArea = new MediaPrintableArea(prnArea[0],
1164                                                            prnArea[1],
1165                                                            prnArea[2],
1166                                                            prnArea[3],
1167                                                            MediaPrintableArea.INCH);
1168                 } catch (IllegalArgumentException e) {
1169                 }
1170                 return printableArea;
1171             }
1172             return null;
1173         } else if (category == SunAlternateMedia.class) {
1174             return null;
1175         } else if (category == Destination.class) {
1176             try {
1177                 return new Destination((new File("out.prn")).toURI());
1178             } catch (SecurityException se) {
1179                 try {
1180                     return new Destination(new URI("file:out.prn"));
1181                 } catch (URISyntaxException e) {
1182                     return null;
1183                 }
1184             }
1185         } else if (category == Sides.class) {
1186             switch(defSides) {
1187             case DMDUP_VERTICAL :
1188                 return Sides.TWO_SIDED_LONG_EDGE;
1189             case DMDUP_HORIZONTAL :
1190                 return Sides.TWO_SIDED_SHORT_EDGE;
1191             default :
1192                 return Sides.ONE_SIDED;
1193             }
1194         } else if (category == PrinterResolution.class) {
1195             int yRes = defYRes;
1196             int xRes = defQuality;
1197             if ((xRes < 0) || (yRes < 0)) {
1198                 int res = (yRes > xRes) ? yRes : xRes;
1199                 if (res > 0) {
1200                  return new PrinterResolution(res, res, PrinterResolution.DPI);
1201                 }
1202             }
1203             else {
1204                return new PrinterResolution(xRes, yRes, PrinterResolution.DPI);
1205             }
1206         } else if (category == ColorSupported.class) {
1207             int caps = getPrinterCapabilities();
1208             if ((caps & DEVCAP_COLOR) != 0) {
1209                 return ColorSupported.SUPPORTED;
1210             } else {
1211                 return ColorSupported.NOT_SUPPORTED;
1212             }
1213         } else if (category == PrintQuality.class) {
1214             if ((defQuality < 0) && (defQuality >= DMRES_HIGH)) {
1215                 switch (defQuality) {
1216                 case DMRES_HIGH:
1217                     return PrintQuality.HIGH;
1218                 case DMRES_MEDIUM:
1219                     return PrintQuality.NORMAL;
1220                 default:
1221                     return PrintQuality.DRAFT;
1222                 }
1223             }
1224         } else if (category == RequestingUserName.class) {
1225             String userName = "";
1226             try {
1227               userName = System.getProperty("user.name", "");
1228             } catch (SecurityException se) {
1229             }
1230             return new RequestingUserName(userName, null);
1231         } else if (category == SheetCollate.class) {
1232             if (defCollate == DMCOLLATE_TRUE) {
1233                 return SheetCollate.COLLATED;
1234             } else {
1235                 return SheetCollate.UNCOLLATED;
1236             }
1237         } else if (category == Fidelity.class) {
1238             return Fidelity.FIDELITY_FALSE;
1239         }
1240         return null;
1241     }
1242 
1243     private boolean isPostScriptFlavor(DocFlavor flavor) {
1244         if (flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT) ||
1245             flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||
1246             flavor.equals(DocFlavor.URL.POSTSCRIPT)) {
1247             return true;
1248         }
1249         else {
1250             return false;
1251         }
1252     }
1253 
1254     private boolean isPSDocAttr(Class<?> category) {
1255         if (category == OrientationRequested.class || category == Copies.class) {
1256                 return true;
1257         }
1258         else {
1259             return false;
1260         }
1261     }
1262 
1263     private boolean isAutoSense(DocFlavor flavor) {
1264         if (flavor.equals(DocFlavor.BYTE_ARRAY.AUTOSENSE) ||
1265             flavor.equals(DocFlavor.INPUT_STREAM.AUTOSENSE) ||
1266             flavor.equals(DocFlavor.URL.AUTOSENSE)) {
1267             return true;
1268         }
1269         else {
1270             return false;
1271         }
1272     }
1273 
1274     public Object
1275         getSupportedAttributeValues(Class<? extends Attribute> category,
1276                                     DocFlavor flavor,
1277                                     AttributeSet attributes)
1278     {
1279         if (category == null) {
1280             throw new NullPointerException("null category");
1281         }
1282         if (!Attribute.class.isAssignableFrom(category)) {
1283             throw new IllegalArgumentException(category +
1284                                              " does not implement Attribute");
1285         }
1286         if (flavor != null) {
1287             if (!isDocFlavorSupported(flavor)) {
1288                 throw new IllegalArgumentException(flavor +
1289                                                   " is an unsupported flavor");
1290                 // if postscript & category is already specified within the
1291                 //  PostScript data we return null
1292             } else if (isAutoSense(flavor) ||(isPostScriptFlavor(flavor) &&
1293                        (isPSDocAttr(category)))){
1294                 return null;
1295             }
1296         }
1297         if (!isAttributeCategorySupported(category)) {
1298             return null;
1299         }
1300 
1301         if (category == JobName.class) {
1302             return new JobName("Java Printing", null);
1303         } else if (category == RequestingUserName.class) {
1304           String userName = "";
1305           try {
1306             userName = System.getProperty("user.name", "");
1307           } catch (SecurityException se) {
1308           }
1309             return new RequestingUserName(userName, null);
1310         } else if (category == ColorSupported.class) {
1311             int caps = getPrinterCapabilities();
1312             if ((caps & DEVCAP_COLOR) != 0) {
1313                 return ColorSupported.SUPPORTED;
1314             } else {
1315                 return ColorSupported.NOT_SUPPORTED;
1316             }
1317         } else if (category == Chromaticity.class) {
1318             if (flavor == null ||
1319                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
1320                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||
1321                 flavor.equals(DocFlavor.BYTE_ARRAY.GIF) ||
1322                 flavor.equals(DocFlavor.INPUT_STREAM.GIF) ||
1323                 flavor.equals(DocFlavor.URL.GIF) ||
1324                 flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) ||
1325                 flavor.equals(DocFlavor.INPUT_STREAM.JPEG) ||
1326                 flavor.equals(DocFlavor.URL.JPEG) ||
1327                 flavor.equals(DocFlavor.BYTE_ARRAY.PNG) ||
1328                 flavor.equals(DocFlavor.INPUT_STREAM.PNG) ||
1329                 flavor.equals(DocFlavor.URL.PNG)) {
1330                 int caps = getPrinterCapabilities();
1331                 if ((caps & DEVCAP_COLOR) == 0) {
1332                     Chromaticity []arr = new Chromaticity[1];
1333                     arr[0] = Chromaticity.MONOCHROME;
1334                     return (arr);
1335                 } else {
1336                     Chromaticity []arr = new Chromaticity[2];
1337                     arr[0] = Chromaticity.MONOCHROME;
1338                     arr[1] = Chromaticity.COLOR;
1339                     return (arr);
1340                 }
1341             } else {
1342                 return null;
1343             }
1344         } else if (category == Destination.class) {
1345             try {
1346                 return new Destination((new File("out.prn")).toURI());
1347             } catch (SecurityException se) {
1348                 try {
1349                     return new Destination(new URI("file:out.prn"));
1350                 } catch (URISyntaxException e) {
1351                     return null;
1352                 }
1353             }
1354         } else if (category == OrientationRequested.class) {
1355             if (flavor == null ||
1356                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
1357                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||
1358                 flavor.equals(DocFlavor.INPUT_STREAM.GIF) ||
1359                 flavor.equals(DocFlavor.INPUT_STREAM.JPEG) ||
1360                 flavor.equals(DocFlavor.INPUT_STREAM.PNG) ||
1361                 flavor.equals(DocFlavor.BYTE_ARRAY.GIF) ||
1362                 flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) ||
1363                 flavor.equals(DocFlavor.BYTE_ARRAY.PNG) ||
1364                 flavor.equals(DocFlavor.URL.GIF) ||
1365                 flavor.equals(DocFlavor.URL.JPEG) ||
1366                 flavor.equals(DocFlavor.URL.PNG)) {
1367                 OrientationRequested []arr = new OrientationRequested[3];
1368                 arr[0] = OrientationRequested.PORTRAIT;
1369                 arr[1] = OrientationRequested.LANDSCAPE;
1370                 arr[2] = OrientationRequested.REVERSE_LANDSCAPE;
1371                 return arr;
1372             } else {
1373                 return null;
1374             }
1375         } else if ((category == Copies.class) ||
1376                    (category == CopiesSupported.class)) {
1377             synchronized (this) {
1378                 if (gotCopies == false) {
1379                     nCopies = getCopiesSupported(printer, getPort());
1380                     gotCopies = true;
1381                 }
1382             }
1383             return new CopiesSupported(1, nCopies);
1384         } else if (category == Media.class) {
1385 
1386             initMedia();
1387 
1388             int len = (mediaSizeNames == null) ? 0 : mediaSizeNames.length;
1389 
1390             MediaTray[] trays = getMediaTrays();
1391 
1392             len += (trays == null) ? 0 : trays.length;
1393 
1394             Media []arr = new Media[len];
1395             if (mediaSizeNames != null) {
1396                 System.arraycopy(mediaSizeNames, 0, arr,
1397                                  0, mediaSizeNames.length);
1398             }
1399             if (trays != null) {
1400                 System.arraycopy(trays, 0, arr,
1401                                  len - trays.length, trays.length);
1402             }
1403             return arr;
1404         } else if (category == MediaPrintableArea.class) {
1405             // if getting printable area for a specific media size
1406             Media mediaName = null;
1407             if ((attributes != null) &&
1408                 ((mediaName =
1409                   (Media)attributes.get(Media.class)) != null)) {
1410 
1411                 if (!(mediaName instanceof MediaSizeName)) {
1412                     // if an instance of MediaTray, fall thru returning
1413                     // all MediaPrintableAreas
1414                     mediaName = null;
1415                 }
1416             }
1417 
1418             MediaPrintableArea[] mpas =
1419                                   getMediaPrintables((MediaSizeName)mediaName);
1420             if (mpas != null) {
1421                 MediaPrintableArea[] arr = new MediaPrintableArea[mpas.length];
1422                 System.arraycopy(mpas, 0, arr, 0, mpas.length);
1423                 return arr;
1424             } else {
1425                 return null;
1426             }
1427         } else if (category == SunAlternateMedia.class) {
1428             return new SunAlternateMedia(
1429                               (Media)getDefaultAttributeValue(Media.class));
1430         } else if (category == PageRanges.class) {
1431             if (flavor == null ||
1432                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
1433                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
1434                 PageRanges []arr = new PageRanges[1];
1435                 arr[0] = new PageRanges(1, Integer.MAX_VALUE);
1436                 return arr;
1437             } else {
1438                 return null;
1439             }
1440         } else if (category == PrinterResolution.class) {
1441             PrinterResolution[] supportedRes = getPrintResolutions();
1442             if (supportedRes == null) {
1443                 return null;
1444             }
1445             PrinterResolution []arr =
1446                 new PrinterResolution[supportedRes.length];
1447             System.arraycopy(supportedRes, 0, arr, 0, supportedRes.length);
1448             return arr;
1449         } else if (category == Sides.class) {
1450             if (flavor == null ||
1451                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
1452                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
1453                 Sides []arr = new Sides[3];
1454                 arr[0] = Sides.ONE_SIDED;
1455                 arr[1] = Sides.TWO_SIDED_LONG_EDGE;
1456                 arr[2] = Sides.TWO_SIDED_SHORT_EDGE;
1457                 return arr;
1458             } else {
1459                 return null;
1460             }
1461         } else if (category == PrintQuality.class) {
1462             PrintQuality []arr = new PrintQuality[3];
1463             arr[0] = PrintQuality.DRAFT;
1464             arr[1] = PrintQuality.HIGH;
1465             arr[2] = PrintQuality.NORMAL;
1466             return arr;
1467         } else if (category == SheetCollate.class) {
1468             if (flavor == null ||
1469                 (flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
1470                  flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
1471                 SheetCollate []arr = new SheetCollate[2];
1472                 arr[0] = SheetCollate.COLLATED;
1473                 arr[1] = SheetCollate.UNCOLLATED;
1474                 return arr;
1475             } else {
1476                 return null;
1477             }
1478         } else if (category == Fidelity.class) {
1479             Fidelity []arr = new Fidelity[2];
1480             arr[0] = Fidelity.FIDELITY_FALSE;
1481             arr[1] = Fidelity.FIDELITY_TRUE;
1482             return arr;
1483         } else {
1484             return null;
1485         }
1486     }
1487 
1488     public boolean isAttributeValueSupported(Attribute attr,
1489                                              DocFlavor flavor,
1490                                              AttributeSet attributes) {
1491 
1492         if (attr == null) {
1493             throw new NullPointerException("null attribute");
1494         }
1495         Class<? extends Attribute> category = attr.getCategory();
1496         if (flavor != null) {
1497             if (!isDocFlavorSupported(flavor)) {
1498                 throw new IllegalArgumentException(flavor +
1499                                                    " is an unsupported flavor");
1500                 // if postscript & category is already specified within the PostScript data
1501                 // we return false
1502             } else if (isAutoSense(flavor) || (isPostScriptFlavor(flavor) &&
1503                        (isPSDocAttr(category)))) {
1504                 return false;
1505             }
1506         }
1507 
1508         if (!isAttributeCategorySupported(category)) {
1509             return false;
1510         }
1511         else if (category == Chromaticity.class) {
1512             if ((flavor == null) ||
1513                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
1514                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||
1515                 flavor.equals(DocFlavor.BYTE_ARRAY.GIF) ||
1516                 flavor.equals(DocFlavor.INPUT_STREAM.GIF) ||
1517                 flavor.equals(DocFlavor.URL.GIF) ||
1518                 flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) ||
1519                 flavor.equals(DocFlavor.INPUT_STREAM.JPEG) ||
1520                 flavor.equals(DocFlavor.URL.JPEG) ||
1521                 flavor.equals(DocFlavor.BYTE_ARRAY.PNG) ||
1522                 flavor.equals(DocFlavor.INPUT_STREAM.PNG) ||
1523                 flavor.equals(DocFlavor.URL.PNG)) {
1524                 int caps = getPrinterCapabilities();
1525                 if ((caps & DEVCAP_COLOR) != 0) {
1526                     return true;
1527                 } else {
1528                     return attr == Chromaticity.MONOCHROME;
1529                 }
1530             } else {
1531                 return false;
1532             }
1533         } else if (category == Copies.class) {
1534             return isSupportedCopies((Copies)attr);
1535 
1536         } else if (category == Destination.class) {
1537             URI uri = ((Destination)attr).getURI();
1538             if ("file".equals(uri.getScheme()) &&
1539                 !(uri.getSchemeSpecificPart().equals(""))) {
1540                 return true;
1541             } else {
1542             return false;
1543             }
1544 
1545         } else if (category == Media.class) {
1546             if (attr instanceof MediaSizeName) {
1547                 return isSupportedMedia((MediaSizeName)attr);
1548             }
1549             if (attr instanceof MediaTray) {
1550                 return isSupportedMediaTray((MediaTray)attr);
1551             }
1552 
1553         } else if (category == MediaPrintableArea.class) {
1554             return isSupportedMediaPrintableArea((MediaPrintableArea)attr);
1555 
1556         } else if (category == SunAlternateMedia.class) {
1557             Media media = ((SunAlternateMedia)attr).getMedia();
1558             return isAttributeValueSupported(media, flavor, attributes);
1559 
1560         } else if (category == PageRanges.class ||
1561                    category == SheetCollate.class ||
1562                    category == Sides.class) {
1563             if (flavor != null &&
1564                 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
1565                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
1566                 return false;
1567             }
1568         } else if (category == PrinterResolution.class) {
1569             if (attr instanceof PrinterResolution) {
1570                 return isSupportedResolution((PrinterResolution)attr);
1571             }
1572         } else if (category == OrientationRequested.class) {
1573             if (attr == OrientationRequested.REVERSE_PORTRAIT ||
1574                 (flavor != null) &&
1575                 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
1576                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||
1577                 flavor.equals(DocFlavor.INPUT_STREAM.GIF) ||
1578                 flavor.equals(DocFlavor.INPUT_STREAM.JPEG) ||
1579                 flavor.equals(DocFlavor.INPUT_STREAM.PNG) ||
1580                 flavor.equals(DocFlavor.BYTE_ARRAY.GIF) ||
1581                 flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) ||
1582                 flavor.equals(DocFlavor.BYTE_ARRAY.PNG) ||
1583                 flavor.equals(DocFlavor.URL.GIF) ||
1584                 flavor.equals(DocFlavor.URL.JPEG) ||
1585                 flavor.equals(DocFlavor.URL.PNG))) {
1586                 return false;
1587             }
1588 
1589         } else if (category == ColorSupported.class) {
1590             int caps = getPrinterCapabilities();
1591             boolean isColorSup = ((caps & DEVCAP_COLOR) != 0);
1592             if  ((!isColorSup && (attr == ColorSupported.SUPPORTED)) ||
1593                 (isColorSup && (attr == ColorSupported.NOT_SUPPORTED))) {
1594                 return false;
1595             }
1596         } else if (category == DialogTypeSelection.class) {
1597             return true; // isHeadless was checked by category support
1598         } else if (category == DialogOwner.class) {
1599             DialogOwner owner = (DialogOwner)attr;
1600             DialogTypeSelection dts = (attributes == null) ? null :
1601                 (DialogTypeSelection)attributes.get(DialogTypeSelection.class);
1602             if (dts == DialogTypeSelection.NATIVE) {
1603                 return DialogOwnerAccessor.getID(owner) != 0;
1604             } else {
1605                if (DialogOwnerAccessor.getID(owner) != 0) {
1606                   return false;
1607                } else if (owner.getOwner() != null) {
1608                    return true;
1609                } else {
1610                    return Toolkit.getDefaultToolkit().isAlwaysOnTopSupported();
1611                }
1612             }
1613         }
1614         return true;
1615     }
1616 
1617     public AttributeSet getUnsupportedAttributes(DocFlavor flavor,
1618                                                  AttributeSet attributes) {
1619 
1620         if (flavor != null && !isDocFlavorSupported(flavor)) {
1621             throw new IllegalArgumentException("flavor " + flavor +
1622                                                " is not supported");
1623         }
1624 
1625         if (attributes == null) {
1626             return null;
1627         }
1628 
1629         Attribute attr;
1630         AttributeSet unsupp = new HashAttributeSet();
1631         Attribute []attrs = attributes.toArray();
1632         for (int i=0; i<attrs.length; i++) {
1633             try {
1634                 attr = attrs[i];
1635                 if (!isAttributeCategorySupported(attr.getCategory())) {
1636                     unsupp.add(attr);
1637                 }
1638                 else if (!isAttributeValueSupported(attr, flavor, attributes)) {
1639                     unsupp.add(attr);
1640                 }
1641             } catch (ClassCastException e) {
1642             }
1643         }
1644         if (unsupp.isEmpty()) {
1645             return null;
1646         } else {
1647             return unsupp;
1648         }
1649     }
1650 
1651     private Win32DocumentPropertiesUI docPropertiesUI = null;
1652 
1653     private static class Win32DocumentPropertiesUI
1654         extends DocumentPropertiesUI {
1655 
1656         Win32PrintService service;
1657 
1658         private Win32DocumentPropertiesUI(Win32PrintService s) {
1659             service = s;
1660         }
1661 
1662         public PrintRequestAttributeSet
1663             showDocumentProperties(PrinterJob job,
1664                                    Window owner,
1665                                    PrintService service,
1666                                    PrintRequestAttributeSet aset) {
1667 
1668             if (!(job instanceof WPrinterJob)) {
1669                 return null;
1670             }
1671             WPrinterJob wJob = (WPrinterJob)job;
1672             return wJob.showDocumentProperties(owner, service, aset);
1673         }
1674     }
1675 
1676     private synchronized DocumentPropertiesUI getDocumentPropertiesUI() {
1677         return new Win32DocumentPropertiesUI(this);
1678     }
1679 
1680     private static class Win32ServiceUIFactory extends ServiceUIFactory {
1681 
1682         Win32PrintService service;
1683 
1684         Win32ServiceUIFactory(Win32PrintService s) {
1685             service = s;
1686         }
1687 
1688         public Object getUI(int role, String ui) {
1689             if (role <= ServiceUIFactory.MAIN_UIROLE) {
1690                 return null;
1691             }
1692             if (role == DocumentPropertiesUI.DOCUMENTPROPERTIES_ROLE &&
1693                 DocumentPropertiesUI.DOCPROPERTIESCLASSNAME.equals(ui))
1694             {
1695                 return service.getDocumentPropertiesUI();
1696             }
1697             throw new IllegalArgumentException("Unsupported role");
1698         }
1699 
1700         public String[] getUIClassNamesForRole(int role) {
1701 
1702             if (role <= ServiceUIFactory.MAIN_UIROLE) {
1703                 return null;
1704             }
1705             if (role == DocumentPropertiesUI.DOCUMENTPROPERTIES_ROLE) {
1706                 String[] names = new String[0];
1707                 names[0] = DocumentPropertiesUI.DOCPROPERTIESCLASSNAME;
1708                 return names;
1709             }
1710             throw new IllegalArgumentException("Unsupported role");
1711         }
1712     }
1713 
1714     private Win32ServiceUIFactory uiFactory = null;
1715 
1716     public synchronized ServiceUIFactory getServiceUIFactory() {
1717         if (uiFactory == null) {
1718             uiFactory = new Win32ServiceUIFactory(this);
1719         }
1720         return uiFactory;
1721     }
1722 
1723     public String toString() {
1724         return "Win32 Printer : " + getName();
1725     }
1726 
1727     public boolean equals(Object obj) {
1728         return  (obj == this ||
1729                  (obj instanceof Win32PrintService &&
1730                   ((Win32PrintService)obj).getName().equals(getName())));
1731     }
1732 
1733    public int hashCode() {
1734         return this.getClass().hashCode()+getName().hashCode();
1735     }
1736 
1737     public boolean usesClass(Class<?> c) {
1738         return (c == sun.awt.windows.WPrinterJob.class);
1739     }
1740 
1741     private native int[] getAllMediaIDs(String printerName, String port);
1742     private native int[] getAllMediaSizes(String printerName, String port);
1743     private native int[] getAllMediaTrays(String printerName, String port);
1744     private native float[] getMediaPrintableArea(String printerName,
1745                                                  int paperSize);
1746     private native String[] getAllMediaNames(String printerName, String port);
1747     private native String[] getAllMediaTrayNames(String printerName, String port);
1748     private native int getCopiesSupported(String printerName, String port);
1749     private native int[] getAllResolutions(String printerName, String port);
1750     private native int getCapabilities(String printerName, String port);
1751 
1752     private native int[] getDefaultSettings(String printerName, String port);
1753     private native int getJobStatus(String printerName, int type);
1754     private native String getPrinterPort(String printerName);
1755 }
1756 
1757 @SuppressWarnings("serial") // JDK implementation class
1758 class Win32MediaSize extends MediaSizeName {
1759     private static ArrayList<String> winStringTable = new ArrayList<>();
1760     private static ArrayList<Win32MediaSize> winEnumTable = new ArrayList<>();
1761     private static MediaSize[] predefMedia;
1762 
1763     private int dmPaperID; // driver ID for this paper.
1764 
1765     private Win32MediaSize(int x) {
1766         super(x);
1767 
1768     }
1769 
1770     private static synchronized int nextValue(String name) {
1771       winStringTable.add(name);
1772       return (winStringTable.size()-1);
1773     }
1774 
1775     public static synchronized Win32MediaSize findMediaName(String name) {
1776         int nameIndex = winStringTable.indexOf(name);
1777         if (nameIndex != -1) {
1778             return winEnumTable.get(nameIndex);
1779         }
1780         return null;
1781     }
1782 
1783     public static MediaSize[] getPredefMedia() {
1784         return predefMedia;
1785     }
1786 
1787     public Win32MediaSize(String name, int dmPaper) {
1788         super(nextValue(name));
1789         dmPaperID = dmPaper;
1790         winEnumTable.add(this);
1791     }
1792 
1793     private MediaSizeName[] getSuperEnumTable() {
1794       return (MediaSizeName[])super.getEnumValueTable();
1795     }
1796 
1797     static {
1798          /* initialize predefMedia */
1799         {
1800             Win32MediaSize winMedia = new Win32MediaSize(-1);
1801 
1802             // cannot call getSuperEnumTable directly because of static context
1803             MediaSizeName[] enumMedia = winMedia.getSuperEnumTable();
1804             if (enumMedia != null) {
1805                 predefMedia = new MediaSize[enumMedia.length];
1806 
1807                 for (int i=0; i<enumMedia.length; i++) {
1808                     predefMedia[i] = MediaSize.getMediaSizeForName(enumMedia[i]);
1809                 }
1810             }
1811         }
1812     }
1813 
1814     int getDMPaper() {
1815         return dmPaperID;
1816     }
1817 
1818     protected String[] getStringTable() {
1819       String[] nameTable = new String[winStringTable.size()];
1820       return winStringTable.toArray(nameTable);
1821     }
1822 
1823     protected EnumSyntax[] getEnumValueTable() {
1824       MediaSizeName[] enumTable = new MediaSizeName[winEnumTable.size()];
1825       return winEnumTable.toArray(enumTable);
1826     }
1827 
1828 }