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