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