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