1 /* 2 * Copyright (c) 2003, 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 javax.print.attribute.*; 29 import javax.print.attribute.standard.*; 30 import javax.print.DocFlavor; 31 import javax.print.DocPrintJob; 32 import javax.print.PrintService; 33 import javax.print.ServiceUIFactory; 34 import java.util.ArrayList; 35 import java.util.HashMap; 36 import java.util.Locale; 37 import java.util.Date; 38 import java.util.Arrays; 39 import java.security.AccessController; 40 import java.security.PrivilegedActionException; 41 import java.security.PrivilegedExceptionAction; 42 import javax.print.event.PrintServiceAttributeListener; 43 44 import java.net.URI; 45 import java.net.URISyntaxException; 46 import java.net.URL; 47 import java.net.URLConnection; 48 import java.net.HttpURLConnection; 49 import java.io.File; 50 import java.io.InputStream; 51 import java.io.OutputStream; 52 import java.io.OutputStreamWriter; 53 import java.io.DataInputStream; 54 import java.io.ByteArrayOutputStream; 55 import java.io.ByteArrayInputStream; 56 import java.io.BufferedReader; 57 import java.io.InputStreamReader; 58 import java.nio.charset.Charset; 59 60 import java.util.Iterator; 61 import java.util.HashSet; 62 63 64 public class IPPPrintService implements PrintService, SunPrinterJobService { 65 66 public static final boolean debugPrint; 67 private static final String debugPrefix = "IPPPrintService>> "; 68 protected static void debug_println(String str) { 69 if (debugPrint) { 70 System.out.println(str); 71 } 72 } 73 74 private static final String FORCE_PIPE_PROP = "sun.print.ippdebug"; 75 76 static { 77 String debugStr = java.security.AccessController.doPrivileged( 78 new sun.security.action.GetPropertyAction(FORCE_PIPE_PROP)); 79 80 debugPrint = "true".equalsIgnoreCase(debugStr); 81 } 82 83 private String printer; 84 private URI myURI; 85 private URL myURL; 86 transient private ServiceNotifier notifier = null; 87 88 private static int MAXCOPIES = 1000; 89 private static short MAX_ATTRIBUTE_LENGTH = 255; 90 91 private CUPSPrinter cps; 92 private HttpURLConnection urlConnection = null; 93 private DocFlavor[] supportedDocFlavors; 94 private Class<?>[] supportedCats; 95 private MediaTray[] mediaTrays; 96 private MediaSizeName[] mediaSizeNames; 97 private CustomMediaSizeName[] customMediaSizeNames; 98 private int defaultMediaIndex; 99 private int[] rawResolutions = null; 100 private PrinterResolution[] printerResolutions = null; 101 private boolean isCupsPrinter; 102 private boolean init; 103 private Boolean isPS; 104 private HashMap<String, AttributeClass> getAttMap; 105 private boolean pngImagesAdded = false; 106 private boolean gifImagesAdded = false; 107 private boolean jpgImagesAdded = false; 108 109 110 /** 111 * IPP Status Codes 112 */ 113 private static final byte STATUSCODE_SUCCESS = 0x00; 114 115 /** 116 * IPP Group Tags. Each tag is used once before the first attribute 117 * of that group. 118 */ 119 // operation attributes group 120 private static final byte GRPTAG_OP_ATTRIBUTES = 0x01; 121 // job attributes group 122 private static final byte GRPTAG_JOB_ATTRIBUTES = 0x02; 123 // printer attributes group 124 private static final byte GRPTAG_PRINTER_ATTRIBUTES = 0x04; 125 // used as the last tag in an IPP message. 126 private static final byte GRPTAG_END_ATTRIBUTES = 0x03; 127 128 /** 129 * IPP Operation codes 130 */ 131 // gets the attributes for a printer 132 public static final String OP_GET_ATTRIBUTES = "000B"; 133 // gets the default printer 134 public static final String OP_CUPS_GET_DEFAULT = "4001"; 135 // gets the list of printers 136 public static final String OP_CUPS_GET_PRINTERS = "4002"; 137 138 139 /** 140 * List of all PrintRequestAttributes. This is used 141 * for looping through all the IPP attribute name. 142 */ 143 private static Object[] printReqAttribDefault = { 144 Chromaticity.COLOR, 145 new Copies(1), 146 Fidelity.FIDELITY_FALSE, 147 Finishings.NONE, 148 //new JobHoldUntil(new Date()), 149 //new JobImpressions(0), 150 //JobImpressions, 151 //JobKOctets, 152 //JobMediaSheets, 153 new JobName("", Locale.getDefault()), 154 //JobPriority, 155 JobSheets.NONE, 156 (Media)MediaSizeName.NA_LETTER, 157 //MediaPrintableArea.class, // not an IPP attribute 158 //MultipleDocumentHandling.SINGLE_DOCUMENT, 159 new NumberUp(1), 160 OrientationRequested.PORTRAIT, 161 new PageRanges(1), 162 //PresentationDirection, 163 // CUPS does not supply printer-resolution attribute 164 //new PrinterResolution(300, 300, PrinterResolution.DPI), 165 //PrintQuality.NORMAL, 166 new RequestingUserName("", Locale.getDefault()), 167 //SheetCollate.UNCOLLATED, //CUPS has no sheet collate? 168 Sides.ONE_SIDED, 169 }; 170 171 172 /** 173 * List of all PrintServiceAttributes. This is used 174 * for looping through all the IPP attribute name. 175 */ 176 private static Object[][] serviceAttributes = { 177 {ColorSupported.class, "color-supported"}, 178 {PagesPerMinute.class, "pages-per-minute"}, 179 {PagesPerMinuteColor.class, "pages-per-minute-color"}, 180 {PDLOverrideSupported.class, "pdl-override-supported"}, 181 {PrinterInfo.class, "printer-info"}, 182 {PrinterIsAcceptingJobs.class, "printer-is-accepting-jobs"}, 183 {PrinterLocation.class, "printer-location"}, 184 {PrinterMakeAndModel.class, "printer-make-and-model"}, 185 {PrinterMessageFromOperator.class, "printer-message-from-operator"}, 186 {PrinterMoreInfo.class, "printer-more-info"}, 187 {PrinterMoreInfoManufacturer.class, "printer-more-info-manufacturer"}, 188 {PrinterName.class, "printer-name"}, 189 {PrinterState.class, "printer-state"}, 190 {PrinterStateReasons.class, "printer-state-reasons"}, 191 {PrinterURI.class, "printer-uri"}, 192 {QueuedJobCount.class, "queued-job-count"} 193 }; 194 195 196 /** 197 * List of DocFlavors, grouped based on matching mime-type. 198 * NOTE: For any change in the predefined DocFlavors, it must be reflected 199 * here also. 200 */ 201 // PDF DocFlavors 202 private static DocFlavor[] appPDF = { 203 DocFlavor.BYTE_ARRAY.PDF, 204 DocFlavor.INPUT_STREAM.PDF, 205 DocFlavor.URL.PDF 206 }; 207 208 // Postscript DocFlavors 209 private static DocFlavor[] appPostScript = { 210 DocFlavor.BYTE_ARRAY.POSTSCRIPT, 211 DocFlavor.INPUT_STREAM.POSTSCRIPT, 212 DocFlavor.URL.POSTSCRIPT 213 }; 214 215 // Autosense DocFlavors 216 private static DocFlavor[] appOctetStream = { 217 DocFlavor.BYTE_ARRAY.AUTOSENSE, 218 DocFlavor.INPUT_STREAM.AUTOSENSE, 219 DocFlavor.URL.AUTOSENSE 220 }; 221 222 // Text DocFlavors 223 private static DocFlavor[] textPlain = { 224 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_8, 225 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16, 226 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16BE, 227 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16LE, 228 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_US_ASCII, 229 DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_8, 230 DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16, 231 DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16BE, 232 DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16LE, 233 DocFlavor.INPUT_STREAM.TEXT_PLAIN_US_ASCII, 234 DocFlavor.URL.TEXT_PLAIN_UTF_8, 235 DocFlavor.URL.TEXT_PLAIN_UTF_16, 236 DocFlavor.URL.TEXT_PLAIN_UTF_16BE, 237 DocFlavor.URL.TEXT_PLAIN_UTF_16LE, 238 DocFlavor.URL.TEXT_PLAIN_US_ASCII, 239 DocFlavor.CHAR_ARRAY.TEXT_PLAIN, 240 DocFlavor.STRING.TEXT_PLAIN, 241 DocFlavor.READER.TEXT_PLAIN 242 }; 243 244 private static DocFlavor[] textPlainHost = { 245 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_HOST, 246 DocFlavor.INPUT_STREAM.TEXT_PLAIN_HOST, 247 DocFlavor.URL.TEXT_PLAIN_HOST 248 }; 249 250 // JPG DocFlavors 251 private static DocFlavor[] imageJPG = { 252 DocFlavor.BYTE_ARRAY.JPEG, 253 DocFlavor.INPUT_STREAM.JPEG, 254 DocFlavor.URL.JPEG 255 }; 256 257 // GIF DocFlavors 258 private static DocFlavor[] imageGIF = { 259 DocFlavor.BYTE_ARRAY.GIF, 260 DocFlavor.INPUT_STREAM.GIF, 261 DocFlavor.URL.GIF 262 }; 263 264 // PNG DocFlavors 265 private static DocFlavor[] imagePNG = { 266 DocFlavor.BYTE_ARRAY.PNG, 267 DocFlavor.INPUT_STREAM.PNG, 268 DocFlavor.URL.PNG 269 }; 270 271 // HTML DocFlavors 272 private static DocFlavor[] textHtml = { 273 DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_8, 274 DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_16, 275 DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_16BE, 276 DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_16LE, 277 DocFlavor.BYTE_ARRAY.TEXT_HTML_US_ASCII, 278 DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_8, 279 DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_16, 280 DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_16BE, 281 DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_16LE, 282 DocFlavor.INPUT_STREAM.TEXT_HTML_US_ASCII, 283 DocFlavor.URL.TEXT_HTML_UTF_8, 284 DocFlavor.URL.TEXT_HTML_UTF_16, 285 DocFlavor.URL.TEXT_HTML_UTF_16BE, 286 DocFlavor.URL.TEXT_HTML_UTF_16LE, 287 DocFlavor.URL.TEXT_HTML_US_ASCII, 288 // These are not handled in UnixPrintJob so commenting these 289 // for now. 290 /* 291 DocFlavor.CHAR_ARRAY.TEXT_HTML, 292 DocFlavor.STRING.TEXT_HTML, 293 DocFlavor.READER.TEXT_HTML, 294 */ 295 }; 296 297 private static DocFlavor[] textHtmlHost = { 298 DocFlavor.BYTE_ARRAY.TEXT_HTML_HOST, 299 DocFlavor.INPUT_STREAM.TEXT_HTML_HOST, 300 DocFlavor.URL.TEXT_HTML_HOST, 301 }; 302 303 304 // PCL DocFlavors 305 private static DocFlavor[] appPCL = { 306 DocFlavor.BYTE_ARRAY.PCL, 307 DocFlavor.INPUT_STREAM.PCL, 308 DocFlavor.URL.PCL 309 }; 310 311 // List of all DocFlavors, used in looping 312 // through all supported mime-types 313 private static Object[] allDocFlavors = { 314 appPDF, appPostScript, appOctetStream, 315 textPlain, imageJPG, imageGIF, imagePNG, 316 textHtml, appPCL, 317 }; 318 319 320 IPPPrintService(String name, URL url) { 321 if ((name == null) || (url == null)){ 322 throw new IllegalArgumentException("null uri or printer name"); 323 } 324 printer = name; 325 supportedDocFlavors = null; 326 supportedCats = null; 327 mediaSizeNames = null; 328 customMediaSizeNames = null; 329 mediaTrays = null; 330 myURL = url; 331 cps = null; 332 isCupsPrinter = false; 333 init = false; 334 defaultMediaIndex = -1; 335 336 String host = myURL.getHost(); 337 if (host!=null && host.equals(CUPSPrinter.getServer())) { 338 isCupsPrinter = true; 339 try { 340 myURI = new URI("ipp://"+host+ 341 "/printers/"+printer); 342 debug_println(debugPrefix+"IPPPrintService myURI : "+myURI); 343 } catch (java.net.URISyntaxException e) { 344 throw new IllegalArgumentException("invalid url"); 345 } 346 } 347 } 348 349 350 IPPPrintService(String name, String uriStr, boolean isCups) { 351 if ((name == null) || (uriStr == null)){ 352 throw new IllegalArgumentException("null uri or printer name"); 353 } 354 printer = name; 355 supportedDocFlavors = null; 356 supportedCats = null; 357 mediaSizeNames = null; 358 customMediaSizeNames = null; 359 mediaTrays = null; 360 cps = null; 361 init = false; 362 defaultMediaIndex = -1; 363 try { 364 myURL = 365 new URL(uriStr.replaceFirst("ipp", "http")); 366 } catch (Exception e) { 367 IPPPrintService.debug_println(debugPrefix+ 368 " IPPPrintService, myURL="+ 369 myURL+" Exception= "+ 370 e); 371 throw new IllegalArgumentException("invalid url"); 372 } 373 374 isCupsPrinter = isCups; 375 try { 376 myURI = new URI(uriStr); 377 debug_println(debugPrefix+"IPPPrintService myURI : "+myURI); 378 } catch (java.net.URISyntaxException e) { 379 throw new IllegalArgumentException("invalid uri"); 380 } 381 } 382 383 384 /* 385 * Initialize mediaSizeNames, mediaTrays and other attributes. 386 * Media size/trays are initialized to non-null values, may be 0-length 387 * array. 388 * NOTE: Must be called from a synchronized block only. 389 */ 390 private void initAttributes() { 391 if (!init) { 392 // init customMediaSizeNames 393 customMediaSizeNames = new CustomMediaSizeName[0]; 394 395 if ((urlConnection = getIPPConnection(myURL)) == null) { 396 mediaSizeNames = new MediaSizeName[0]; 397 mediaTrays = new MediaTray[0]; 398 debug_println(debugPrefix+"initAttributes, NULL urlConnection "); 399 init = true; 400 return; 401 } 402 403 // get all supported attributes through IPP 404 opGetAttributes(); 405 406 if (isCupsPrinter) { 407 // note, it is possible to query media in CUPS using IPP 408 // right now we always get it from PPD. 409 // maybe use "&& (usePPD)" later? 410 // Another reason why we use PPD is because 411 // IPP currently does not support it but PPD does. 412 413 try { 414 cps = new CUPSPrinter(printer); 415 mediaSizeNames = cps.getMediaSizeNames(); 416 mediaTrays = cps.getMediaTrays(); 417 customMediaSizeNames = cps.getCustomMediaSizeNames(); 418 defaultMediaIndex = cps.getDefaultMediaIndex(); 419 rawResolutions = cps.getRawResolutions(); 420 urlConnection.disconnect(); 421 init = true; 422 return; 423 } catch (Exception e) { 424 IPPPrintService.debug_println(debugPrefix+ 425 "initAttributes, error creating CUPSPrinter e="+e); 426 } 427 } 428 429 // use IPP to get all media, 430 Media[] allMedia = getSupportedMedia(); 431 ArrayList<Media> sizeList = new ArrayList<>(); 432 ArrayList<Media> trayList = new ArrayList<>(); 433 for (int i=0; i<allMedia.length; i++) { 434 if (allMedia[i] instanceof MediaSizeName) { 435 sizeList.add(allMedia[i]); 436 } else if (allMedia[i] instanceof MediaTray) { 437 trayList.add(allMedia[i]); 438 } 439 } 440 441 if (sizeList != null) { 442 mediaSizeNames = new MediaSizeName[sizeList.size()]; 443 mediaSizeNames = sizeList.toArray(mediaSizeNames); 444 } 445 if (trayList != null) { 446 mediaTrays = new MediaTray[trayList.size()]; 447 mediaTrays = trayList.toArray(mediaTrays); 448 } 449 urlConnection.disconnect(); 450 451 init = true; 452 } 453 } 454 455 456 public DocPrintJob createPrintJob() { 457 SecurityManager security = System.getSecurityManager(); 458 if (security != null) { 459 security.checkPrintJobAccess(); 460 } 461 // REMIND: create IPPPrintJob 462 return new UnixPrintJob(this); 463 } 464 465 466 public synchronized Object 467 getSupportedAttributeValues(Class<? extends Attribute> category, 468 DocFlavor flavor, 469 AttributeSet attributes) 470 { 471 if (category == null) { 472 throw new NullPointerException("null category"); 473 } 474 if (!Attribute.class.isAssignableFrom(category)) { 475 throw new IllegalArgumentException(category + 476 " does not implement Attribute"); 477 } 478 if (flavor != null) { 479 if (!isDocFlavorSupported(flavor)) { 480 throw new IllegalArgumentException(flavor + 481 " is an unsupported flavor"); 482 } else if (isAutoSense(flavor)) { 483 return null; 484 } 485 486 } 487 488 if (!isAttributeCategorySupported(category)) { 489 return null; 490 } 491 492 /* Test if the flavor is compatible with the attributes */ 493 if (!isDestinationSupported(flavor, attributes)) { 494 return null; 495 } 496 497 initAttributes(); 498 499 /* Test if the flavor is compatible with the category */ 500 if ((category == Copies.class) || 501 (category == CopiesSupported.class)) { 502 if (flavor == null || 503 !(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) || 504 flavor.equals(DocFlavor.URL.POSTSCRIPT) || 505 flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) { 506 CopiesSupported cs = new CopiesSupported(1, MAXCOPIES); 507 AttributeClass attribClass = (getAttMap != null) ? 508 getAttMap.get(cs.getName()) : null; 509 if (attribClass != null) { 510 int[] range = attribClass.getIntRangeValue(); 511 cs = new CopiesSupported(range[0], range[1]); 512 } 513 return cs; 514 } else { 515 return null; 516 } 517 } else if (category == Chromaticity.class) { 518 if (flavor == null || 519 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 520 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) || 521 !isIPPSupportedImages(flavor.getMimeType())) { 522 Chromaticity[]arr = new Chromaticity[1]; 523 arr[0] = Chromaticity.COLOR; 524 return (arr); 525 } else { 526 return null; 527 } 528 } else if (category == Destination.class) { 529 if (flavor == null || 530 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 531 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 532 try { 533 return new Destination((new File("out.ps")).toURI()); 534 } catch (SecurityException se) { 535 try { 536 return new Destination(new URI("file:out.ps")); 537 } catch (URISyntaxException e) { 538 return null; 539 } 540 } 541 } 542 return null; 543 } else if (category == Fidelity.class) { 544 Fidelity []arr = new Fidelity[2]; 545 arr[0] = Fidelity.FIDELITY_FALSE; 546 arr[1] = Fidelity.FIDELITY_TRUE; 547 return arr; 548 } else if (category == Finishings.class) { 549 AttributeClass attribClass = (getAttMap != null) ? 550 getAttMap.get("finishings-supported") 551 : null; 552 if (attribClass != null) { 553 int[] finArray = attribClass.getArrayOfIntValues(); 554 if ((finArray != null) && (finArray.length > 0)) { 555 Finishings[] finSup = new Finishings[finArray.length]; 556 for (int i=0; i<finArray.length; i++) { 557 finSup[i] = Finishings.NONE; 558 Finishings[] fAll = (Finishings[]) 559 (new ExtFinishing(100)).getAll(); 560 for (int j=0; j<fAll.length; j++) { 561 if (finArray[i] == fAll[j].getValue()) { 562 finSup[i] = fAll[j]; 563 break; 564 } 565 } 566 } 567 return finSup; 568 } 569 } 570 } else if (category == JobName.class) { 571 return new JobName("Java Printing", null); 572 } else if (category == JobSheets.class) { 573 JobSheets arr[] = new JobSheets[2]; 574 arr[0] = JobSheets.NONE; 575 arr[1] = JobSheets.STANDARD; 576 return arr; 577 578 } else if (category == Media.class) { 579 Media[] allMedia = new Media[mediaSizeNames.length+ 580 mediaTrays.length]; 581 582 for (int i=0; i<mediaSizeNames.length; i++) { 583 allMedia[i] = mediaSizeNames[i]; 584 } 585 586 for (int i=0; i<mediaTrays.length; i++) { 587 allMedia[i+mediaSizeNames.length] = mediaTrays[i]; 588 } 589 590 if (allMedia.length == 0) { 591 allMedia = new Media[1]; 592 allMedia[0] = (Media)getDefaultAttributeValue(Media.class); 593 } 594 595 return allMedia; 596 } else if (category == MediaPrintableArea.class) { 597 MediaPrintableArea[] mpas = null; 598 if (cps != null) { 599 mpas = cps.getMediaPrintableArea(); 600 } 601 602 if (mpas == null) { 603 mpas = new MediaPrintableArea[1]; 604 mpas[0] = (MediaPrintableArea) 605 getDefaultAttributeValue(MediaPrintableArea.class); 606 } 607 608 if ((attributes == null) || (attributes.size() == 0)) { 609 ArrayList<MediaPrintableArea> printableList = 610 new ArrayList<MediaPrintableArea>(); 611 612 for (int i=0; i<mpas.length; i++) { 613 if (mpas[i] != null) { 614 printableList.add(mpas[i]); 615 } 616 } 617 if (printableList.size() > 0) { 618 mpas = new MediaPrintableArea[printableList.size()]; 619 printableList.toArray(mpas); 620 } 621 return mpas; 622 } 623 624 int match = -1; 625 Media media = (Media)attributes.get(Media.class); 626 if (media != null && media instanceof MediaSizeName) { 627 MediaSizeName msn = (MediaSizeName)media; 628 629 // case when no supported mediasizenames are reported 630 // check given media against the default 631 if (mediaSizeNames.length == 0 && 632 msn.equals(getDefaultAttributeValue(Media.class))) { 633 //default printable area is that of default mediasize 634 return mpas; 635 } 636 637 for (int i=0; i<mediaSizeNames.length; i++) { 638 if (msn.equals(mediaSizeNames[i])) { 639 match = i; 640 } 641 } 642 } 643 644 if (match == -1) { 645 return null; 646 } else { 647 MediaPrintableArea []arr = new MediaPrintableArea[1]; 648 arr[0] = mpas[match]; 649 return arr; 650 } 651 } else if (category == NumberUp.class) { 652 AttributeClass attribClass = (getAttMap != null) ? 653 getAttMap.get("number-up-supported") : null; 654 if (attribClass != null) { 655 int[] values = attribClass.getArrayOfIntValues(); 656 if (values != null) { 657 NumberUp[] nUp = new NumberUp[values.length]; 658 for (int i=0; i<values.length; i++) { 659 nUp[i] = new NumberUp(values[i]); 660 } 661 return nUp; 662 } else { 663 return null; 664 } 665 } 666 } else if (category == OrientationRequested.class) { 667 if ((flavor != null) && 668 (flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) || 669 flavor.equals(DocFlavor.URL.POSTSCRIPT) || 670 flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) { 671 return null; 672 } 673 674 boolean revPort = false; 675 OrientationRequested[] orientSup = null; 676 677 AttributeClass attribClass = (getAttMap != null) ? 678 getAttMap.get("orientation-requested-supported") 679 : null; 680 if (attribClass != null) { 681 int[] orientArray = attribClass.getArrayOfIntValues(); 682 if ((orientArray != null) && (orientArray.length > 0)) { 683 orientSup = 684 new OrientationRequested[orientArray.length]; 685 for (int i=0; i<orientArray.length; i++) { 686 switch (orientArray[i]) { 687 default: 688 case 3 : 689 orientSup[i] = OrientationRequested.PORTRAIT; 690 break; 691 case 4: 692 orientSup[i] = OrientationRequested.LANDSCAPE; 693 break; 694 case 5: 695 orientSup[i] = 696 OrientationRequested.REVERSE_LANDSCAPE; 697 break; 698 case 6: 699 orientSup[i] = 700 OrientationRequested.REVERSE_PORTRAIT; 701 revPort = true; 702 break; 703 } 704 } 705 } 706 } 707 if (flavor == null || 708 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 709 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 710 711 if (revPort && flavor == null) { 712 OrientationRequested []orSup = new OrientationRequested[4]; 713 orSup[0] = OrientationRequested.PORTRAIT; 714 orSup[1] = OrientationRequested.LANDSCAPE; 715 orSup[2] = OrientationRequested.REVERSE_LANDSCAPE; 716 orSup[3] = OrientationRequested.REVERSE_PORTRAIT; 717 return orSup; 718 } else { 719 OrientationRequested []orSup = new OrientationRequested[3]; 720 orSup[0] = OrientationRequested.PORTRAIT; 721 orSup[1] = OrientationRequested.LANDSCAPE; 722 orSup[2] = OrientationRequested.REVERSE_LANDSCAPE; 723 return orSup; 724 } 725 } else { 726 return orientSup; 727 } 728 } else if (category == PageRanges.class) { 729 if (flavor == null || 730 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 731 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 732 PageRanges []arr = new PageRanges[1]; 733 arr[0] = new PageRanges(1, Integer.MAX_VALUE); 734 return arr; 735 } else { 736 // Returning null as this is not yet supported in UnixPrintJob. 737 return null; 738 } 739 } else if (category == RequestingUserName.class) { 740 String userName = ""; 741 try { 742 userName = System.getProperty("user.name", ""); 743 } catch (SecurityException se) { 744 } 745 return new RequestingUserName(userName, null); 746 } else if (category == Sides.class) { 747 // The printer takes care of Sides so if short-edge 748 // is chosen in a job, the rotation is done by the printer. 749 // Orientation is rotated by emulation if pageable 750 // or printable so if the document is in Landscape, this may 751 // result in double rotation. 752 AttributeClass attribClass = (getAttMap != null) ? 753 getAttMap.get("sides-supported") 754 : null; 755 if (attribClass != null) { 756 String[] sidesArray = attribClass.getArrayOfStringValues(); 757 if ((sidesArray != null) && (sidesArray.length > 0)) { 758 Sides[] sidesSup = new Sides[sidesArray.length]; 759 for (int i=0; i<sidesArray.length; i++) { 760 if (sidesArray[i].endsWith("long-edge")) { 761 sidesSup[i] = Sides.TWO_SIDED_LONG_EDGE; 762 } else if (sidesArray[i].endsWith("short-edge")) { 763 sidesSup[i] = Sides.TWO_SIDED_SHORT_EDGE; 764 } else { 765 sidesSup[i] = Sides.ONE_SIDED; 766 } 767 } 768 return sidesSup; 769 } 770 } 771 } else if (category == PrinterResolution.class) { 772 PrinterResolution[] supportedRes = getPrintResolutions(); 773 if (supportedRes == null) { 774 return null; 775 } 776 PrinterResolution []arr = 777 new PrinterResolution[supportedRes.length]; 778 System.arraycopy(supportedRes, 0, arr, 0, supportedRes.length); 779 return arr; 780 } 781 782 return null; 783 } 784 785 //This class is for getting all pre-defined Finishings 786 @SuppressWarnings("serial") // JDK implementation class 787 private class ExtFinishing extends Finishings { 788 ExtFinishing(int value) { 789 super(100); // 100 to avoid any conflicts with predefined values. 790 } 791 792 EnumSyntax[] getAll() { 793 EnumSyntax[] es = super.getEnumValueTable(); 794 return es; 795 } 796 } 797 798 799 public AttributeSet getUnsupportedAttributes(DocFlavor flavor, 800 AttributeSet attributes) { 801 if (flavor != null && !isDocFlavorSupported(flavor)) { 802 throw new IllegalArgumentException("flavor " + flavor + 803 "is not supported"); 804 } 805 806 if (attributes == null) { 807 return null; 808 } 809 810 Attribute attr; 811 AttributeSet unsupp = new HashAttributeSet(); 812 Attribute []attrs = attributes.toArray(); 813 for (int i=0; i<attrs.length; i++) { 814 try { 815 attr = attrs[i]; 816 if (!isAttributeCategorySupported(attr.getCategory())) { 817 unsupp.add(attr); 818 } else if (!isAttributeValueSupported(attr, flavor, 819 attributes)) { 820 unsupp.add(attr); 821 } 822 } catch (ClassCastException e) { 823 } 824 } 825 if (unsupp.isEmpty()) { 826 return null; 827 } else { 828 return unsupp; 829 } 830 } 831 832 833 public synchronized DocFlavor[] getSupportedDocFlavors() { 834 835 if (supportedDocFlavors != null) { 836 int len = supportedDocFlavors.length; 837 DocFlavor[] copyflavors = new DocFlavor[len]; 838 System.arraycopy(supportedDocFlavors, 0, copyflavors, 0, len); 839 return copyflavors; 840 } 841 initAttributes(); 842 843 if ((getAttMap != null) && 844 getAttMap.containsKey("document-format-supported")) { 845 846 AttributeClass attribClass = 847 getAttMap.get("document-format-supported"); 848 if (attribClass != null) { 849 String mimeType; 850 boolean psSupported = false; 851 String[] docFlavors = attribClass.getArrayOfStringValues(); 852 DocFlavor[] flavors; 853 HashSet<Object> docList = new HashSet<>(); 854 int j; 855 String hostEnc = DocFlavor.hostEncoding. 856 toLowerCase(Locale.ENGLISH); 857 boolean addHostEncoding = !hostEnc.equals("utf-8") && 858 !hostEnc.equals("utf-16") && !hostEnc.equals("utf-16be") && 859 !hostEnc.equals("utf-16le") && !hostEnc.equals("us-ascii"); 860 861 for (int i = 0; i < docFlavors.length; i++) { 862 for (j=0; j<allDocFlavors.length; j++) { 863 flavors = (DocFlavor[])allDocFlavors[j]; 864 865 mimeType = flavors[0].getMimeType(); 866 if (mimeType.startsWith(docFlavors[i])) { 867 868 docList.addAll(Arrays.asList(flavors)); 869 870 if (mimeType.equals("text/plain") && 871 addHostEncoding) { 872 docList.add(Arrays.asList(textPlainHost)); 873 } else if (mimeType.equals("text/html") && 874 addHostEncoding) { 875 docList.add(Arrays.asList(textHtmlHost)); 876 } else if (mimeType.equals("image/png")) { 877 pngImagesAdded = true; 878 } else if (mimeType.equals("image/gif")) { 879 gifImagesAdded = true; 880 } else if (mimeType.equals("image/jpeg")) { 881 jpgImagesAdded = true; 882 } else if (mimeType.indexOf("postscript") != -1) { 883 psSupported = true; 884 } 885 break; 886 } 887 } 888 889 // Not added? Create new DocFlavors 890 if (j == allDocFlavors.length) { 891 // make new DocFlavors 892 docList.add(new DocFlavor.BYTE_ARRAY(docFlavors[i])); 893 docList.add(new DocFlavor.INPUT_STREAM(docFlavors[i])); 894 docList.add(new DocFlavor.URL(docFlavors[i])); 895 } 896 } 897 898 // check if we need to add image DocFlavors 899 // and Pageable/Printable flavors 900 if (psSupported || isCupsPrinter) { 901 /* 902 Always add Pageable and Printable for CUPS 903 since it uses Filters to convert from Postscript 904 to device printer language. 905 */ 906 docList.add(DocFlavor.SERVICE_FORMATTED.PAGEABLE); 907 docList.add(DocFlavor.SERVICE_FORMATTED.PRINTABLE); 908 909 docList.addAll(Arrays.asList(imageJPG)); 910 docList.addAll(Arrays.asList(imagePNG)); 911 docList.addAll(Arrays.asList(imageGIF)); 912 } 913 supportedDocFlavors = new DocFlavor[docList.size()]; 914 docList.toArray(supportedDocFlavors); 915 int len = supportedDocFlavors.length; 916 DocFlavor[] copyflavors = new DocFlavor[len]; 917 System.arraycopy(supportedDocFlavors, 0, copyflavors, 0, len); 918 return copyflavors; 919 } 920 } 921 return null; 922 } 923 924 925 public boolean isDocFlavorSupported(DocFlavor flavor) { 926 if (supportedDocFlavors == null) { 927 getSupportedDocFlavors(); 928 } 929 if (supportedDocFlavors != null) { 930 for (int f=0; f<supportedDocFlavors.length; f++) { 931 if (flavor.equals(supportedDocFlavors[f])) { 932 return true; 933 } 934 } 935 } 936 return false; 937 } 938 939 940 /** 941 * Finds matching CustomMediaSizeName of given media. 942 */ 943 public CustomMediaSizeName findCustomMedia(MediaSizeName media) { 944 if (customMediaSizeNames == null) { 945 return null; 946 } 947 for (int i=0; i< customMediaSizeNames.length; i++) { 948 CustomMediaSizeName custom = customMediaSizeNames[i]; 949 MediaSizeName msn = custom.getStandardMedia(); 950 if (media.equals(msn)) { 951 return customMediaSizeNames[i]; 952 } 953 } 954 return null; 955 } 956 957 958 /** 959 * Returns the matching standard Media using string comparison of names. 960 */ 961 private Media getIPPMedia(String mediaName) { 962 CustomMediaSizeName sampleSize = new CustomMediaSizeName("sample", "", 963 0, 0); 964 Media[] sizes = sampleSize.getSuperEnumTable(); 965 for (int i=0; i<sizes.length; i++) { 966 if (mediaName.equals(""+sizes[i])) { 967 return sizes[i]; 968 } 969 } 970 CustomMediaTray sampleTray = new CustomMediaTray("sample", ""); 971 Media[] trays = sampleTray.getSuperEnumTable(); 972 for (int i=0; i<trays.length; i++) { 973 if (mediaName.equals(""+trays[i])) { 974 return trays[i]; 975 } 976 } 977 return null; 978 } 979 980 private Media[] getSupportedMedia() { 981 if ((getAttMap != null) && 982 getAttMap.containsKey("media-supported")) { 983 984 AttributeClass attribClass = getAttMap.get("media-supported"); 985 986 if (attribClass != null) { 987 String[] mediaVals = attribClass.getArrayOfStringValues(); 988 Media msn; 989 Media[] mediaNames = 990 new Media[mediaVals.length]; 991 for (int i=0; i<mediaVals.length; i++) { 992 msn = getIPPMedia(mediaVals[i]); 993 //REMIND: if null, create custom? 994 mediaNames[i] = msn; 995 } 996 return mediaNames; 997 } 998 } 999 return new Media[0]; 1000 } 1001 1002 1003 public synchronized Class<?>[] getSupportedAttributeCategories() { 1004 if (supportedCats != null) { 1005 Class<?> [] copyCats = new Class<?>[supportedCats.length]; 1006 System.arraycopy(supportedCats, 0, copyCats, 0, copyCats.length); 1007 return copyCats; 1008 } 1009 1010 initAttributes(); 1011 1012 ArrayList<Class<?>> catList = new ArrayList<>(); 1013 1014 for (int i=0; i < printReqAttribDefault.length; i++) { 1015 PrintRequestAttribute pra = 1016 (PrintRequestAttribute)printReqAttribDefault[i]; 1017 if (getAttMap != null && 1018 getAttMap.containsKey(pra.getName()+"-supported")) { 1019 catList.add(pra.getCategory()); 1020 } 1021 } 1022 1023 // Some IPP printers like lexc710 do not have list of supported media 1024 // but CUPS can get the media from PPD, so we still report as 1025 // supported category. 1026 if (isCupsPrinter) { 1027 if (!catList.contains(Media.class)) { 1028 catList.add(Media.class); 1029 } 1030 1031 // Always add MediaPrintable for cups, 1032 // because we can get it from PPD. 1033 catList.add(MediaPrintableArea.class); 1034 1035 // this is already supported in UnixPrintJob 1036 catList.add(Destination.class); 1037 1038 // It is unfortunate that CUPS doesn't provide a way to query 1039 // if printer supports collation but since most printers 1040 // now supports collation and that most OS has a way 1041 // of setting it, it is a safe assumption to just always 1042 // include SheetCollate as supported attribute. 1043 1044 /* 1045 In Linux, we use Postscript for rendering but Linux still 1046 has issues in propagating Postscript-embedded setpagedevice 1047 setting like collation. Therefore, we temporarily exclude 1048 Linux. 1049 */ 1050 if (!PrintServiceLookupProvider.isLinux()) { 1051 catList.add(SheetCollate.class); 1052 } 1053 } 1054 1055 // With the assumption that Chromaticity is equivalent to 1056 // ColorSupported. 1057 if (getAttMap != null && getAttMap.containsKey("color-supported")) { 1058 catList.add(Chromaticity.class); 1059 } 1060 1061 // CUPS does not report printer resolution via IPP but it 1062 // may be gleaned from the PPD. 1063 PrinterResolution[] supportedRes = getPrintResolutions(); 1064 if (supportedRes != null && (supportedRes.length > 0)) { 1065 catList.add(PrinterResolution.class); 1066 } 1067 1068 supportedCats = new Class<?>[catList.size()]; 1069 catList.toArray(supportedCats); 1070 Class<?>[] copyCats = new Class<?>[supportedCats.length]; 1071 System.arraycopy(supportedCats, 0, copyCats, 0, copyCats.length); 1072 return copyCats; 1073 } 1074 1075 1076 public boolean 1077 isAttributeCategorySupported(Class<? extends Attribute> category) 1078 { 1079 if (category == null) { 1080 throw new NullPointerException("null category"); 1081 } 1082 if (!(Attribute.class.isAssignableFrom(category))) { 1083 throw new IllegalArgumentException(category + 1084 " is not an Attribute"); 1085 } 1086 1087 if (supportedCats == null) { 1088 getSupportedAttributeCategories(); 1089 } 1090 1091 // It is safe to assume that Orientation is always supported 1092 // and even if CUPS or an IPP device reports it as not, 1093 // our renderer can do portrait, landscape and 1094 // reverse landscape. 1095 if (category == OrientationRequested.class) { 1096 return true; 1097 } 1098 1099 for (int i=0;i<supportedCats.length;i++) { 1100 if (category == supportedCats[i]) { 1101 return true; 1102 } 1103 } 1104 1105 return false; 1106 } 1107 1108 @SuppressWarnings("unchecked") 1109 public synchronized <T extends PrintServiceAttribute> 1110 T getAttribute(Class<T> category) 1111 { 1112 if (category == null) { 1113 throw new NullPointerException("category"); 1114 } 1115 if (!(PrintServiceAttribute.class.isAssignableFrom(category))) { 1116 throw new IllegalArgumentException("Not a PrintServiceAttribute"); 1117 } 1118 1119 initAttributes(); 1120 1121 if (category == PrinterName.class) { 1122 return (T)(new PrinterName(printer, null)); 1123 } else if (category == PrinterInfo.class) { 1124 PrinterInfo pInfo = new PrinterInfo(printer, null); 1125 AttributeClass ac = (getAttMap != null) ? 1126 getAttMap.get(pInfo.getName()) 1127 : null; 1128 if (ac != null) { 1129 return (T)(new PrinterInfo(ac.getStringValue(), null)); 1130 } 1131 return (T)pInfo; 1132 } else if (category == QueuedJobCount.class) { 1133 QueuedJobCount qjc = new QueuedJobCount(0); 1134 AttributeClass ac = (getAttMap != null) ? 1135 getAttMap.get(qjc.getName()) 1136 : null; 1137 if (ac != null) { 1138 qjc = new QueuedJobCount(ac.getIntValue()); 1139 } 1140 return (T)qjc; 1141 } else if (category == PrinterIsAcceptingJobs.class) { 1142 PrinterIsAcceptingJobs accJob = 1143 PrinterIsAcceptingJobs.ACCEPTING_JOBS; 1144 AttributeClass ac = (getAttMap != null) ? 1145 getAttMap.get(accJob.getName()) 1146 : null; 1147 if ((ac != null) && (ac.getByteValue() == 0)) { 1148 accJob = PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS; 1149 } 1150 return (T)accJob; 1151 } else if (category == ColorSupported.class) { 1152 ColorSupported cs = ColorSupported.SUPPORTED; 1153 AttributeClass ac = (getAttMap != null) ? 1154 getAttMap.get(cs.getName()) 1155 : null; 1156 if ((ac != null) && (ac.getByteValue() == 0)) { 1157 cs = ColorSupported.NOT_SUPPORTED; 1158 } 1159 return (T)cs; 1160 } else if (category == PDLOverrideSupported.class) { 1161 1162 if (isCupsPrinter) { 1163 // Documented: For CUPS this will always be false 1164 return (T)PDLOverrideSupported.NOT_ATTEMPTED; 1165 } else { 1166 // REMIND: check attribute values 1167 return (T)PDLOverrideSupported.NOT_ATTEMPTED; 1168 } 1169 } else if (category == PrinterURI.class) { 1170 return (T)(new PrinterURI(myURI)); 1171 } else { 1172 return null; 1173 } 1174 } 1175 1176 1177 public synchronized PrintServiceAttributeSet getAttributes() { 1178 // update getAttMap by sending again get-attributes IPP request 1179 init = false; 1180 initAttributes(); 1181 1182 HashPrintServiceAttributeSet attrs = 1183 new HashPrintServiceAttributeSet(); 1184 1185 for (int i=0; i < serviceAttributes.length; i++) { 1186 String name = (String)serviceAttributes[i][1]; 1187 if (getAttMap != null && getAttMap.containsKey(name)) { 1188 @SuppressWarnings("unchecked") 1189 Class<PrintServiceAttribute> c = (Class<PrintServiceAttribute>)serviceAttributes[i][0]; 1190 PrintServiceAttribute psa = getAttribute(c); 1191 if (psa != null) { 1192 attrs.add(psa); 1193 } 1194 } 1195 } 1196 return AttributeSetUtilities.unmodifiableView(attrs); 1197 } 1198 1199 public boolean isIPPSupportedImages(String mimeType) { 1200 if (supportedDocFlavors == null) { 1201 getSupportedDocFlavors(); 1202 } 1203 1204 if (mimeType.equals("image/png") && pngImagesAdded) { 1205 return true; 1206 } else if (mimeType.equals("image/gif") && gifImagesAdded) { 1207 return true; 1208 } else if (mimeType.equals("image/jpeg") && jpgImagesAdded) { 1209 return true; 1210 } 1211 1212 return false; 1213 } 1214 1215 1216 private boolean isSupportedCopies(Copies copies) { 1217 CopiesSupported cs = (CopiesSupported) 1218 getSupportedAttributeValues(Copies.class, null, null); 1219 int[][] members = cs.getMembers(); 1220 int min, max; 1221 if ((members.length > 0) && (members[0].length > 0)) { 1222 min = members[0][0]; 1223 max = members[0][1]; 1224 } else { 1225 min = 1; 1226 max = MAXCOPIES; 1227 } 1228 1229 int value = copies.getValue(); 1230 return (value >= min && value <= max); 1231 } 1232 1233 private boolean isAutoSense(DocFlavor flavor) { 1234 if (flavor.equals(DocFlavor.BYTE_ARRAY.AUTOSENSE) || 1235 flavor.equals(DocFlavor.INPUT_STREAM.AUTOSENSE) || 1236 flavor.equals(DocFlavor.URL.AUTOSENSE)) { 1237 return true; 1238 } 1239 else { 1240 return false; 1241 } 1242 } 1243 1244 private synchronized boolean isSupportedMediaTray(MediaTray msn) { 1245 initAttributes(); 1246 1247 if (mediaTrays != null) { 1248 for (int i=0; i<mediaTrays.length; i++) { 1249 if (msn.equals(mediaTrays[i])) { 1250 return true; 1251 } 1252 } 1253 } 1254 return false; 1255 } 1256 1257 private synchronized boolean isSupportedMedia(MediaSizeName msn) { 1258 initAttributes(); 1259 1260 if (msn.equals((Media)getDefaultAttributeValue(Media.class))) { 1261 return true; 1262 } 1263 for (int i=0; i<mediaSizeNames.length; i++) { 1264 debug_println(debugPrefix+"isSupportedMedia, mediaSizeNames[i] "+mediaSizeNames[i]); 1265 if (msn.equals(mediaSizeNames[i])) { 1266 return true; 1267 } 1268 } 1269 return false; 1270 } 1271 1272 /* Return false if flavor is not null, pageable, nor printable and 1273 * Destination is part of attributes. 1274 */ 1275 private boolean 1276 isDestinationSupported(DocFlavor flavor, AttributeSet attributes) { 1277 1278 if ((attributes != null) && 1279 (attributes.get(Destination.class) != null) && 1280 !(flavor == null || 1281 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 1282 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) { 1283 return false; 1284 } 1285 return true; 1286 } 1287 1288 1289 public boolean isAttributeValueSupported(Attribute attr, 1290 DocFlavor flavor, 1291 AttributeSet attributes) { 1292 if (attr == null) { 1293 throw new NullPointerException("null attribute"); 1294 } 1295 if (flavor != null) { 1296 if (!isDocFlavorSupported(flavor)) { 1297 throw new IllegalArgumentException(flavor + 1298 " is an unsupported flavor"); 1299 } else if (isAutoSense(flavor)) { 1300 return false; 1301 } 1302 } 1303 Class<? extends Attribute> category = attr.getCategory(); 1304 if (!isAttributeCategorySupported(category)) { 1305 return false; 1306 } 1307 1308 /* Test if the flavor is compatible with the attributes */ 1309 if (!isDestinationSupported(flavor, attributes)) { 1310 return false; 1311 } 1312 1313 /* Test if the flavor is compatible with the category */ 1314 if (attr.getCategory() == Chromaticity.class) { 1315 if ((flavor == null) || 1316 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 1317 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) || 1318 !isIPPSupportedImages(flavor.getMimeType())) { 1319 return attr == Chromaticity.COLOR; 1320 } else { 1321 return false; 1322 } 1323 } else if (attr.getCategory() == Copies.class) { 1324 return (flavor == null || 1325 !(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) || 1326 flavor.equals(DocFlavor.URL.POSTSCRIPT) || 1327 flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) && 1328 isSupportedCopies((Copies)attr); 1329 1330 } else if (attr.getCategory() == Destination.class) { 1331 if (flavor == null || 1332 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 1333 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 1334 URI uri = ((Destination)attr).getURI(); 1335 if ("file".equals(uri.getScheme()) && 1336 !(uri.getSchemeSpecificPart().equals(""))) { 1337 return true; 1338 } 1339 } 1340 return false; 1341 } else if (attr.getCategory() == Media.class) { 1342 if (attr instanceof MediaSizeName) { 1343 return isSupportedMedia((MediaSizeName)attr); 1344 } 1345 if (attr instanceof MediaTray) { 1346 return isSupportedMediaTray((MediaTray)attr); 1347 } 1348 } else if (attr.getCategory() == PageRanges.class) { 1349 if (flavor != null && 1350 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 1351 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) { 1352 return false; 1353 } 1354 } else if (attr.getCategory() == SheetCollate.class) { 1355 if (flavor != null && 1356 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 1357 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) { 1358 return false; 1359 } 1360 } else if (attr.getCategory() == Sides.class) { 1361 Sides[] sidesArray = (Sides[])getSupportedAttributeValues( 1362 Sides.class, 1363 flavor, 1364 attributes); 1365 1366 if (sidesArray != null) { 1367 for (int i=0; i<sidesArray.length; i++) { 1368 if (sidesArray[i] == (Sides)attr) { 1369 return true; 1370 } 1371 } 1372 } 1373 return false; 1374 } else if (attr.getCategory() == OrientationRequested.class) { 1375 OrientationRequested[] orientArray = 1376 (OrientationRequested[])getSupportedAttributeValues( 1377 OrientationRequested.class, 1378 flavor, 1379 attributes); 1380 1381 if (orientArray != null) { 1382 for (int i=0; i<orientArray.length; i++) { 1383 if (orientArray[i] == (OrientationRequested)attr) { 1384 return true; 1385 } 1386 } 1387 } 1388 return false; 1389 } if (attr.getCategory() == PrinterResolution.class) { 1390 if (attr instanceof PrinterResolution) { 1391 return isSupportedResolution((PrinterResolution)attr); 1392 } 1393 } 1394 return true; 1395 } 1396 1397 1398 public synchronized Object 1399 getDefaultAttributeValue(Class<? extends Attribute> category) 1400 { 1401 if (category == null) { 1402 throw new NullPointerException("null category"); 1403 } 1404 if (!Attribute.class.isAssignableFrom(category)) { 1405 throw new IllegalArgumentException(category + 1406 " is not an Attribute"); 1407 } 1408 if (!isAttributeCategorySupported(category)) { 1409 return null; 1410 } 1411 1412 initAttributes(); 1413 1414 String catName = null; 1415 for (int i=0; i < printReqAttribDefault.length; i++) { 1416 PrintRequestAttribute pra = 1417 (PrintRequestAttribute)printReqAttribDefault[i]; 1418 if (pra.getCategory() == category) { 1419 catName = pra.getName(); 1420 break; 1421 } 1422 } 1423 String attribName = catName+"-default"; 1424 AttributeClass attribClass = (getAttMap != null) ? 1425 getAttMap.get(attribName) : null; 1426 1427 if (category == Copies.class) { 1428 if (attribClass != null) { 1429 return new Copies(attribClass.getIntValue()); 1430 } else { 1431 return new Copies(1); 1432 } 1433 } else if (category == Chromaticity.class) { 1434 return Chromaticity.COLOR; 1435 } else if (category == Destination.class) { 1436 try { 1437 return new Destination((new File("out.ps")).toURI()); 1438 } catch (SecurityException se) { 1439 try { 1440 return new Destination(new URI("file:out.ps")); 1441 } catch (URISyntaxException e) { 1442 return null; 1443 } 1444 } 1445 } else if (category == Fidelity.class) { 1446 return Fidelity.FIDELITY_FALSE; 1447 } else if (category == Finishings.class) { 1448 return Finishings.NONE; 1449 } else if (category == JobName.class) { 1450 return new JobName("Java Printing", null); 1451 } else if (category == JobSheets.class) { 1452 if (attribClass != null && 1453 attribClass.getStringValue().equals("none")) { 1454 return JobSheets.NONE; 1455 } else { 1456 return JobSheets.STANDARD; 1457 } 1458 } else if (category == Media.class) { 1459 if (defaultMediaIndex == -1) { 1460 defaultMediaIndex = 0; 1461 } 1462 if (mediaSizeNames.length == 0) { 1463 String defaultCountry = Locale.getDefault().getCountry(); 1464 if (defaultCountry != null && 1465 (defaultCountry.equals("") || 1466 defaultCountry.equals(Locale.US.getCountry()) || 1467 defaultCountry.equals(Locale.CANADA.getCountry()))) { 1468 return MediaSizeName.NA_LETTER; 1469 } else { 1470 return MediaSizeName.ISO_A4; 1471 } 1472 } 1473 1474 if (attribClass != null) { 1475 String name = attribClass.getStringValue(); 1476 if (isCupsPrinter) { 1477 return mediaSizeNames[defaultMediaIndex]; 1478 } else { 1479 for (int i=0; i< mediaSizeNames.length; i++) { 1480 if (mediaSizeNames[i].toString().indexOf(name) != -1) { 1481 defaultMediaIndex = i; 1482 return mediaSizeNames[defaultMediaIndex]; 1483 } 1484 } 1485 } 1486 } 1487 return mediaSizeNames[defaultMediaIndex]; 1488 1489 } else if (category == MediaPrintableArea.class) { 1490 MediaPrintableArea[] mpas; 1491 if ((cps != null) && 1492 ((mpas = cps.getMediaPrintableArea()) != null)) { 1493 if (defaultMediaIndex == -1) { 1494 // initializes value of defaultMediaIndex 1495 getDefaultAttributeValue(Media.class); 1496 } 1497 return mpas[defaultMediaIndex]; 1498 } else { 1499 String defaultCountry = Locale.getDefault().getCountry(); 1500 float iw, ih; 1501 if (defaultCountry != null && 1502 (defaultCountry.equals("") || 1503 defaultCountry.equals(Locale.US.getCountry()) || 1504 defaultCountry.equals(Locale.CANADA.getCountry()))) { 1505 iw = MediaSize.NA.LETTER.getX(Size2DSyntax.INCH) - 0.5f; 1506 ih = MediaSize.NA.LETTER.getY(Size2DSyntax.INCH) - 0.5f; 1507 } else { 1508 iw = MediaSize.ISO.A4.getX(Size2DSyntax.INCH) - 0.5f; 1509 ih = MediaSize.ISO.A4.getY(Size2DSyntax.INCH) - 0.5f; 1510 } 1511 return new MediaPrintableArea(0.25f, 0.25f, iw, ih, 1512 MediaPrintableArea.INCH); 1513 } 1514 } else if (category == NumberUp.class) { 1515 return new NumberUp(1); // for CUPS this is always 1 1516 } else if (category == OrientationRequested.class) { 1517 if (attribClass != null) { 1518 switch (attribClass.getIntValue()) { 1519 default: 1520 case 3: return OrientationRequested.PORTRAIT; 1521 case 4: return OrientationRequested.LANDSCAPE; 1522 case 5: return OrientationRequested.REVERSE_LANDSCAPE; 1523 case 6: return OrientationRequested.REVERSE_PORTRAIT; 1524 } 1525 } else { 1526 return OrientationRequested.PORTRAIT; 1527 } 1528 } else if (category == PageRanges.class) { 1529 if (attribClass != null) { 1530 int[] range = attribClass.getIntRangeValue(); 1531 return new PageRanges(range[0], range[1]); 1532 } else { 1533 return new PageRanges(1, Integer.MAX_VALUE); 1534 } 1535 } else if (category == RequestingUserName.class) { 1536 String userName = ""; 1537 try { 1538 userName = System.getProperty("user.name", ""); 1539 } catch (SecurityException se) { 1540 } 1541 return new RequestingUserName(userName, null); 1542 } else if (category == SheetCollate.class) { 1543 return SheetCollate.UNCOLLATED; 1544 } else if (category == Sides.class) { 1545 if (attribClass != null) { 1546 if (attribClass.getStringValue().endsWith("long-edge")) { 1547 return Sides.TWO_SIDED_LONG_EDGE; 1548 } else if (attribClass.getStringValue().endsWith( 1549 "short-edge")) { 1550 return Sides.TWO_SIDED_SHORT_EDGE; 1551 } 1552 } 1553 return Sides.ONE_SIDED; 1554 } else if (category == PrinterResolution.class) { 1555 PrinterResolution[] supportedRes = getPrintResolutions(); 1556 if ((supportedRes != null) && (supportedRes.length > 0)) { 1557 return supportedRes[0]; 1558 } else { 1559 return new PrinterResolution(300, 300, PrinterResolution.DPI); 1560 } 1561 } 1562 1563 return null; 1564 } 1565 1566 private PrinterResolution[] getPrintResolutions() { 1567 if (printerResolutions == null) { 1568 if (rawResolutions == null) { 1569 printerResolutions = new PrinterResolution[0]; 1570 } else { 1571 int numRes = rawResolutions.length / 2; 1572 PrinterResolution[] pres = new PrinterResolution[numRes]; 1573 for (int i=0; i < numRes; i++) { 1574 pres[i] = new PrinterResolution(rawResolutions[i*2], 1575 rawResolutions[i*2+1], 1576 PrinterResolution.DPI); 1577 } 1578 printerResolutions = pres; 1579 } 1580 } 1581 return printerResolutions; 1582 } 1583 1584 private boolean isSupportedResolution(PrinterResolution res) { 1585 PrinterResolution[] supportedRes = getPrintResolutions(); 1586 if (supportedRes != null) { 1587 for (int i=0; i<supportedRes.length; i++) { 1588 if (res.equals(supportedRes[i])) { 1589 return true; 1590 } 1591 } 1592 } 1593 return false; 1594 } 1595 1596 public ServiceUIFactory getServiceUIFactory() { 1597 return null; 1598 } 1599 1600 public void wakeNotifier() { 1601 synchronized (this) { 1602 if (notifier != null) { 1603 notifier.wake(); 1604 } 1605 } 1606 } 1607 1608 public void addPrintServiceAttributeListener( 1609 PrintServiceAttributeListener listener) { 1610 synchronized (this) { 1611 if (listener == null) { 1612 return; 1613 } 1614 if (notifier == null) { 1615 notifier = new ServiceNotifier(this); 1616 } 1617 notifier.addListener(listener); 1618 } 1619 } 1620 1621 public void removePrintServiceAttributeListener( 1622 PrintServiceAttributeListener listener) { 1623 synchronized (this) { 1624 if (listener == null || notifier == null ) { 1625 return; 1626 } 1627 notifier.removeListener(listener); 1628 if (notifier.isEmpty()) { 1629 notifier.stopNotifier(); 1630 notifier = null; 1631 } 1632 } 1633 } 1634 1635 String getDest() { 1636 return printer; 1637 } 1638 1639 public String getName() { 1640 /* 1641 * Mac is using printer-info IPP attribute for its human-readable printer 1642 * name and is also the identifier used in NSPrintInfo:setPrinter. 1643 */ 1644 if (PrintServiceLookupProvider.isMac()) { 1645 PrintServiceAttributeSet psaSet = this.getAttributes(); 1646 if (psaSet != null) { 1647 PrinterInfo pName = (PrinterInfo)psaSet.get(PrinterInfo.class); 1648 if (pName != null) { 1649 return pName.toString(); 1650 } 1651 } 1652 } 1653 return printer; 1654 } 1655 1656 1657 public boolean usesClass(Class<?> c) { 1658 return (c == sun.print.PSPrinterJob.class); 1659 } 1660 1661 1662 public static HttpURLConnection getIPPConnection(URL url) { 1663 HttpURLConnection connection; 1664 URLConnection urlc; 1665 try { 1666 urlc = url.openConnection(); 1667 } catch (java.io.IOException ioe) { 1668 return null; 1669 } 1670 if (!(urlc instanceof HttpURLConnection)) { 1671 return null; 1672 } 1673 connection = (HttpURLConnection)urlc; 1674 connection.setUseCaches(false); 1675 connection.setDefaultUseCaches(false); 1676 connection.setDoInput(true); 1677 connection.setDoOutput(true); 1678 connection.setRequestProperty("Content-type", "application/ipp"); 1679 return connection; 1680 } 1681 1682 1683 public synchronized boolean isPostscript() { 1684 if (isPS == null) { 1685 isPS = Boolean.TRUE; 1686 if (isCupsPrinter) { 1687 try { 1688 urlConnection = getIPPConnection( 1689 new URL(myURL+".ppd")); 1690 1691 InputStream is = urlConnection.getInputStream(); 1692 if (is != null) { 1693 BufferedReader d = 1694 new BufferedReader(new InputStreamReader(is, 1695 Charset.forName("ISO-8859-1"))); 1696 String lineStr; 1697 while ((lineStr = d.readLine()) != null) { 1698 if (lineStr.startsWith("*cupsFilter:")) { 1699 isPS = Boolean.FALSE; 1700 break; 1701 } 1702 } 1703 } 1704 } catch (java.io.IOException e) { 1705 debug_println(" isPostscript, e= "+e); 1706 /* if PPD is not found, this may be a raw printer 1707 and in this case it is assumed that it is a 1708 Postscript printer */ 1709 // do nothing 1710 } 1711 } 1712 } 1713 return isPS.booleanValue(); 1714 } 1715 1716 1717 private void opGetAttributes() { 1718 try { 1719 debug_println(debugPrefix+"opGetAttributes myURI "+myURI+" myURL "+myURL); 1720 1721 AttributeClass attClNoUri[] = { 1722 AttributeClass.ATTRIBUTES_CHARSET, 1723 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE}; 1724 1725 AttributeClass attCl[] = { 1726 AttributeClass.ATTRIBUTES_CHARSET, 1727 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE, 1728 new AttributeClass("printer-uri", 1729 AttributeClass.TAG_URI, 1730 ""+myURI)}; 1731 1732 OutputStream os = java.security.AccessController. 1733 doPrivileged(new java.security.PrivilegedAction<OutputStream>() { 1734 public OutputStream run() { 1735 try { 1736 return urlConnection.getOutputStream(); 1737 } catch (Exception e) { 1738 } 1739 return null; 1740 } 1741 }); 1742 1743 if (os == null) { 1744 return; 1745 } 1746 1747 boolean success = (myURI == null) ? 1748 writeIPPRequest(os, OP_GET_ATTRIBUTES, attClNoUri) : 1749 writeIPPRequest(os, OP_GET_ATTRIBUTES, attCl); 1750 if (success) { 1751 InputStream is = null; 1752 if ((is = urlConnection.getInputStream())!=null) { 1753 HashMap<String, AttributeClass>[] responseMap = readIPPResponse(is); 1754 1755 if (responseMap != null && responseMap.length > 0) { 1756 getAttMap = responseMap[0]; 1757 } 1758 } else { 1759 debug_println(debugPrefix+"opGetAttributes - null input stream"); 1760 } 1761 is.close(); 1762 } 1763 os.close(); 1764 } catch (java.io.IOException e) { 1765 debug_println(debugPrefix+"opGetAttributes - input/output stream: "+e); 1766 } 1767 } 1768 1769 1770 public static boolean writeIPPRequest(OutputStream os, 1771 String operCode, 1772 AttributeClass[] attCl) { 1773 OutputStreamWriter osw; 1774 try { 1775 osw = new OutputStreamWriter(os, "UTF-8"); 1776 } catch (java.io.UnsupportedEncodingException exc) { 1777 debug_println(debugPrefix+"writeIPPRequest, UTF-8 not supported? Exception: "+exc); 1778 return false; 1779 } 1780 debug_println(debugPrefix+"writeIPPRequest, op code= "+operCode); 1781 char[] opCode = new char[2]; 1782 opCode[0] = (char)Byte.parseByte(operCode.substring(0,2), 16); 1783 opCode[1] = (char)Byte.parseByte(operCode.substring(2,4), 16); 1784 char[] bytes = {0x01, 0x01, 0x00, 0x01}; 1785 try { 1786 osw.write(bytes, 0, 2); // version number 1787 osw.write(opCode, 0, 2); // operation code 1788 bytes[0] = 0x00; bytes[1] = 0x00; 1789 osw.write(bytes, 0, 4); // request ID #1 1790 1791 bytes[0] = 0x01; // operation-group-tag 1792 osw.write(bytes[0]); 1793 1794 String valStr; 1795 char[] lenStr; 1796 1797 AttributeClass ac; 1798 for (int i=0; i < attCl.length; i++) { 1799 ac = attCl[i]; 1800 osw.write(ac.getType()); // value tag 1801 1802 lenStr = ac.getLenChars(); 1803 osw.write(lenStr, 0, 2); // length 1804 osw.write(""+ac, 0, ac.getName().length()); 1805 1806 // check if string range (0x35 -> 0x49) 1807 if (ac.getType() >= AttributeClass.TAG_TEXT_LANGUAGE && 1808 ac.getType() <= AttributeClass.TAG_MIME_MEDIATYPE){ 1809 valStr = (String)ac.getObjectValue(); 1810 bytes[0] = 0; bytes[1] = (char)valStr.length(); 1811 osw.write(bytes, 0, 2); 1812 osw.write(valStr, 0, valStr.length()); 1813 } // REMIND: need to support other value tags but for CUPS 1814 // string is all we need. 1815 } 1816 1817 osw.write(GRPTAG_END_ATTRIBUTES); 1818 osw.flush(); 1819 osw.close(); 1820 } catch (java.io.IOException ioe) { 1821 debug_println(debugPrefix+"writeIPPRequest, IPPPrintService Exception in writeIPPRequest: "+ioe); 1822 return false; 1823 } 1824 return true; 1825 } 1826 1827 1828 public static HashMap<String, AttributeClass>[] readIPPResponse(InputStream inputStream) { 1829 1830 if (inputStream == null) { 1831 return null; 1832 } 1833 1834 byte response[] = new byte[MAX_ATTRIBUTE_LENGTH]; 1835 try { 1836 1837 DataInputStream ois = new DataInputStream(inputStream); 1838 1839 // read status and ID 1840 if ((ois.read(response, 0, 8) > -1) && 1841 (response[2] == STATUSCODE_SUCCESS)) { 1842 1843 ByteArrayOutputStream outObj; 1844 int counter=0; 1845 short len = 0; 1846 String attribStr = null; 1847 // assign default value 1848 byte valTagByte = AttributeClass.TAG_KEYWORD; 1849 ArrayList<HashMap<String, AttributeClass>> respList = new ArrayList<>(); 1850 HashMap<String, AttributeClass> responseMap = new HashMap<>(); 1851 1852 response[0] = ois.readByte(); 1853 1854 // check for group tags 1855 while ((response[0] >= GRPTAG_OP_ATTRIBUTES) && 1856 (response[0] <= GRPTAG_PRINTER_ATTRIBUTES) 1857 && (response[0] != GRPTAG_END_ATTRIBUTES)) { 1858 debug_println(debugPrefix+"readIPPResponse, checking group tag, response[0]= "+ 1859 response[0]); 1860 1861 outObj = new ByteArrayOutputStream(); 1862 //make sure counter and attribStr are re-initialized 1863 counter = 0; 1864 attribStr = null; 1865 1866 // read value tag 1867 response[0] = ois.readByte(); 1868 while (response[0] >= AttributeClass.TAG_UNSUPPORTED_VALUE && 1869 response[0] <= AttributeClass.TAG_MEMBER_ATTRNAME) { 1870 // read name length 1871 len = ois.readShort(); 1872 1873 // If current value is not part of previous attribute 1874 // then close stream and add it to HashMap. 1875 // It is part of previous attribute if name length=0. 1876 if ((len != 0) && (attribStr != null)) { 1877 //last byte is the total # of values 1878 outObj.write(counter); 1879 outObj.flush(); 1880 outObj.close(); 1881 byte outArray[] = outObj.toByteArray(); 1882 1883 // if key exists, new HashMap 1884 if (responseMap.containsKey(attribStr)) { 1885 respList.add(responseMap); 1886 responseMap = new HashMap<>(); 1887 } 1888 1889 // exclude those that are unknown 1890 if (valTagByte >= AttributeClass.TAG_INT) { 1891 AttributeClass ac = 1892 new AttributeClass(attribStr, 1893 valTagByte, 1894 outArray); 1895 1896 responseMap.put(ac.getName(), ac); 1897 debug_println(debugPrefix+ "readIPPResponse "+ac); 1898 } 1899 1900 outObj = new ByteArrayOutputStream(); 1901 counter = 0; //reset counter 1902 } 1903 //check if this is new value tag 1904 if (counter == 0) { 1905 valTagByte = response[0]; 1906 } 1907 // read attribute name 1908 if (len != 0) { 1909 // read "len" characters 1910 // make sure it doesn't exceed the maximum 1911 if (len > MAX_ATTRIBUTE_LENGTH) { 1912 response = new byte[len]; // expand as needed 1913 } 1914 ois.read(response, 0, len); 1915 attribStr = new String(response, 0, len); 1916 } 1917 // read value length 1918 len = ois.readShort(); 1919 // write name length 1920 outObj.write(len); 1921 // read value, make sure it doesn't exceed the maximum 1922 if (len > MAX_ATTRIBUTE_LENGTH) { 1923 response = new byte[len]; // expand as needed 1924 } 1925 ois.read(response, 0, len); 1926 // write value of "len" length 1927 outObj.write(response, 0, len); 1928 counter++; 1929 // read next byte 1930 response[0] = ois.readByte(); 1931 } 1932 1933 if (attribStr != null) { 1934 outObj.write(counter); 1935 outObj.flush(); 1936 outObj.close(); 1937 1938 // if key exists in old HashMap, new HashMap 1939 if ((counter != 0) && 1940 responseMap.containsKey(attribStr)) { 1941 respList.add(responseMap); 1942 responseMap = new HashMap<>(); 1943 } 1944 1945 byte outArray[] = outObj.toByteArray(); 1946 1947 AttributeClass ac = 1948 new AttributeClass(attribStr, 1949 valTagByte, 1950 outArray); 1951 responseMap.put(ac.getName(), ac); 1952 } 1953 } 1954 ois.close(); 1955 if ((responseMap != null) && (responseMap.size() > 0)) { 1956 respList.add(responseMap); 1957 } 1958 @SuppressWarnings({"unchecked", "rawtypes"}) 1959 HashMap<String, AttributeClass>[] tmp = 1960 respList.toArray((HashMap<String, AttributeClass>[])new HashMap[respList.size()]); 1961 return tmp; 1962 } else { 1963 debug_println(debugPrefix+ 1964 "readIPPResponse client error, IPP status code: 0x"+ 1965 toHex(response[2]) + toHex(response[3])); 1966 return null; 1967 } 1968 1969 } catch (java.io.IOException e) { 1970 debug_println(debugPrefix+"readIPPResponse: "+e); 1971 if (debugPrint) { 1972 e.printStackTrace(); 1973 } 1974 return null; 1975 } 1976 } 1977 1978 private static String toHex(byte v) { 1979 String s = Integer.toHexString(v&0xff); 1980 return (s.length() == 2) ? s : "0"+s; 1981 } 1982 1983 public String toString() { 1984 return "IPP Printer : " + getName(); 1985 } 1986 1987 public boolean equals(Object obj) { 1988 return (obj == this || 1989 (obj instanceof IPPPrintService && 1990 ((IPPPrintService)obj).getName().equals(getName()))); 1991 } 1992 1993 public int hashCode() { 1994 return this.getClass().hashCode()+getName().hashCode(); 1995 } 1996 }