1 /* 2 * Copyright (c) 2003, 2007, 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 urlConnection.disconnect(); 418 init = true; 419 return; 420 } catch (Exception e) { 421 IPPPrintService.debug_println(debugPrefix+ 422 "initAttributes, error creating CUPSPrinter e="+e); 423 } 424 } 425 426 // use IPP to get all media, 427 Media[] allMedia = (Media[])getSupportedMedia(); 428 ArrayList sizeList = new ArrayList(); 429 ArrayList trayList = new ArrayList(); 430 for (int i=0; i<allMedia.length; i++) { 431 if (allMedia[i] instanceof MediaSizeName) { 432 sizeList.add(allMedia[i]); 433 } else if (allMedia[i] instanceof MediaTray) { 434 trayList.add(allMedia[i]); 435 } 436 } 437 438 if (sizeList != null) { 439 mediaSizeNames = new MediaSizeName[sizeList.size()]; 440 mediaSizeNames = (MediaSizeName[])sizeList.toArray( 441 mediaSizeNames); 442 } 443 if (trayList != null) { 444 mediaTrays = new MediaTray[trayList.size()]; 445 mediaTrays = (MediaTray[])trayList.toArray( 446 mediaTrays); 447 } 448 urlConnection.disconnect(); 449 450 init = true; 451 } 452 } 453 454 455 public DocPrintJob createPrintJob() { 456 SecurityManager security = System.getSecurityManager(); 457 if (security != null) { 458 security.checkPrintJobAccess(); 459 } 460 // REMIND: create IPPPrintJob 461 return new UnixPrintJob(this); 462 } 463 464 465 public synchronized Object 466 getSupportedAttributeValues(Class<? extends Attribute> category, 467 DocFlavor flavor, 468 AttributeSet attributes) 469 { 470 if (category == null) { 471 throw new NullPointerException("null category"); 472 } 473 if (!Attribute.class.isAssignableFrom(category)) { 474 throw new IllegalArgumentException(category + 475 " does not implement Attribute"); 476 } 477 if (flavor != null) { 478 if (!isDocFlavorSupported(flavor)) { 479 throw new IllegalArgumentException(flavor + 480 " is an unsupported flavor"); 481 } else if (isAutoSense(flavor)) { 482 return null; 483 } 484 485 } 486 487 if (!isAttributeCategorySupported(category)) { 488 return null; 489 } 490 491 /* Test if the flavor is compatible with the attributes */ 492 if (!isDestinationSupported(flavor, attributes)) { 493 return null; 494 } 495 496 initAttributes(); 497 498 /* Test if the flavor is compatible with the category */ 499 if ((category == Copies.class) || 500 (category == CopiesSupported.class)) { 501 if (flavor == null || 502 !(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) || 503 flavor.equals(DocFlavor.URL.POSTSCRIPT) || 504 flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) { 505 CopiesSupported cs = new CopiesSupported(1, MAXCOPIES); 506 AttributeClass attribClass = (getAttMap != null) ? 507 (AttributeClass)getAttMap.get(cs.getName()) : null; 508 if (attribClass != null) { 509 int[] range = attribClass.getIntRangeValue(); 510 cs = new CopiesSupported(range[0], range[1]); 511 } 512 return cs; 513 } else { 514 return null; 515 } 516 } else if (category == Chromaticity.class) { 517 if (flavor == null || 518 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 519 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) || 520 !isIPPSupportedImages(flavor.getMimeType())) { 521 Chromaticity[]arr = new Chromaticity[1]; 522 arr[0] = Chromaticity.COLOR; 523 return (arr); 524 } else { 525 return null; 526 } 527 } else if (category == Destination.class) { 528 if (flavor == null || 529 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 530 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 531 try { 532 return new Destination((new File("out.ps")).toURI()); 533 } catch (SecurityException se) { 534 try { 535 return new Destination(new URI("file:out.ps")); 536 } catch (URISyntaxException e) { 537 return null; 538 } 539 } 540 } 541 return null; 542 } else if (category == Fidelity.class) { 543 Fidelity []arr = new Fidelity[2]; 544 arr[0] = Fidelity.FIDELITY_FALSE; 545 arr[1] = Fidelity.FIDELITY_TRUE; 546 return arr; 547 } else if (category == Finishings.class) { 548 AttributeClass attribClass = (getAttMap != null) ? 549 (AttributeClass)getAttMap.get("finishings-supported") 550 : null; 551 if (attribClass != null) { 552 int[] finArray = attribClass.getArrayOfIntValues(); 553 if ((finArray != null) && (finArray.length > 0)) { 554 Finishings[] finSup = new Finishings[finArray.length]; 555 for (int i=0; i<finArray.length; i++) { 556 finSup[i] = Finishings.NONE; 557 Finishings[] fAll = (Finishings[]) 558 (new ExtFinishing(100)).getAll(); 559 for (int j=0; j<fAll.length; j++) { 560 if (finArray[i] == fAll[j].getValue()) { 561 finSup[i] = fAll[j]; 562 break; 563 } 564 } 565 } 566 return finSup; 567 } 568 } 569 } else if (category == JobName.class) { 570 return new JobName("Java Printing", null); 571 } else if (category == JobSheets.class) { 572 JobSheets arr[] = new JobSheets[2]; 573 arr[0] = JobSheets.NONE; 574 arr[1] = JobSheets.STANDARD; 575 return arr; 576 577 } else if (category == Media.class) { 578 Media[] allMedia = new Media[mediaSizeNames.length+ 579 mediaTrays.length]; 580 581 for (int i=0; i<mediaSizeNames.length; i++) { 582 allMedia[i] = mediaSizeNames[i]; 583 } 584 585 for (int i=0; i<mediaTrays.length; i++) { 586 allMedia[i+mediaSizeNames.length] = mediaTrays[i]; 587 } 588 589 if (allMedia.length == 0) { 590 allMedia = new Media[1]; 591 allMedia[0] = (Media)getDefaultAttributeValue(Media.class); 592 } 593 594 return allMedia; 595 } else if (category == MediaPrintableArea.class) { 596 MediaPrintableArea[] mpas = null; 597 if (cps != null) { 598 mpas = cps.getMediaPrintableArea(); 599 } 600 601 if (mpas == null) { 602 mpas = new MediaPrintableArea[1]; 603 mpas[0] = (MediaPrintableArea) 604 getDefaultAttributeValue(MediaPrintableArea.class); 605 } 606 607 if ((attributes == null) || (attributes.size() == 0)) { 608 ArrayList<MediaPrintableArea> printableList = 609 new ArrayList<MediaPrintableArea>(); 610 611 for (int i=0; i<mpas.length; i++) { 612 if (mpas[i] != null) { 613 printableList.add(mpas[i]); 614 } 615 } 616 if (printableList.size() > 0) { 617 mpas = new MediaPrintableArea[printableList.size()]; 618 printableList.toArray(mpas); 619 } 620 return mpas; 621 } 622 623 int match = -1; 624 Media media = (Media)attributes.get(Media.class); 625 if (media != null && media instanceof MediaSizeName) { 626 MediaSizeName msn = (MediaSizeName)media; 627 628 // case when no supported mediasizenames are reported 629 // check given media against the default 630 if (mediaSizeNames.length == 0 && 631 msn.equals(getDefaultAttributeValue(Media.class))) { 632 //default printable area is that of default mediasize 633 return mpas; 634 } 635 636 for (int i=0; i<mediaSizeNames.length; i++) { 637 if (msn.equals(mediaSizeNames[i])) { 638 match = i; 639 } 640 } 641 } 642 643 if (match == -1) { 644 return null; 645 } else { 646 MediaPrintableArea []arr = new MediaPrintableArea[1]; 647 arr[0] = mpas[match]; 648 return arr; 649 } 650 } else if (category == NumberUp.class) { 651 AttributeClass attribClass = (getAttMap != null) ? 652 (AttributeClass)getAttMap.get("number-up-supported") : null; 653 if (attribClass != null) { 654 int[] values = attribClass.getArrayOfIntValues(); 655 if (values != null) { 656 NumberUp[] nUp = new NumberUp[values.length]; 657 for (int i=0; i<values.length; i++) { 658 nUp[i] = new NumberUp(values[i]); 659 } 660 return nUp; 661 } else { 662 return null; 663 } 664 } 665 } else if (category == OrientationRequested.class) { 666 if ((flavor != null) && 667 (flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) || 668 flavor.equals(DocFlavor.URL.POSTSCRIPT) || 669 flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) { 670 return null; 671 } 672 673 boolean revPort = false; 674 OrientationRequested[] orientSup = null; 675 676 AttributeClass attribClass = (getAttMap != null) ? 677 (AttributeClass)getAttMap.get("orientation-requested-supported") 678 : null; 679 if (attribClass != null) { 680 int[] orientArray = attribClass.getArrayOfIntValues(); 681 if ((orientArray != null) && (orientArray.length > 0)) { 682 orientSup = 683 new OrientationRequested[orientArray.length]; 684 for (int i=0; i<orientArray.length; i++) { 685 switch (orientArray[i]) { 686 default: 687 case 3 : 688 orientSup[i] = OrientationRequested.PORTRAIT; 689 break; 690 case 4: 691 orientSup[i] = OrientationRequested.LANDSCAPE; 692 break; 693 case 5: 694 orientSup[i] = 695 OrientationRequested.REVERSE_LANDSCAPE; 696 break; 697 case 6: 698 orientSup[i] = 699 OrientationRequested.REVERSE_PORTRAIT; 700 revPort = true; 701 break; 702 } 703 } 704 } 705 } 706 if (flavor == null || 707 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 708 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 709 710 if (revPort && flavor == null) { 711 OrientationRequested []orSup = new OrientationRequested[4]; 712 orSup[0] = OrientationRequested.PORTRAIT; 713 orSup[1] = OrientationRequested.LANDSCAPE; 714 orSup[2] = OrientationRequested.REVERSE_LANDSCAPE; 715 orSup[3] = OrientationRequested.REVERSE_PORTRAIT; 716 return orSup; 717 } else { 718 OrientationRequested []orSup = new OrientationRequested[3]; 719 orSup[0] = OrientationRequested.PORTRAIT; 720 orSup[1] = OrientationRequested.LANDSCAPE; 721 orSup[2] = OrientationRequested.REVERSE_LANDSCAPE; 722 return orSup; 723 } 724 } else { 725 return orientSup; 726 } 727 } else if (category == PageRanges.class) { 728 if (flavor == null || 729 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 730 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 731 PageRanges []arr = new PageRanges[1]; 732 arr[0] = new PageRanges(1, Integer.MAX_VALUE); 733 return arr; 734 } else { 735 // Returning null as this is not yet supported in UnixPrintJob. 736 return null; 737 } 738 } else if (category == RequestingUserName.class) { 739 String userName = ""; 740 try { 741 userName = System.getProperty("user.name", ""); 742 } catch (SecurityException se) { 743 } 744 return new RequestingUserName(userName, null); 745 } else if (category == Sides.class) { 746 // The printer takes care of Sides so if short-edge 747 // is chosen in a job, the rotation is done by the printer. 748 // Orientation is rotated by emulation if pageable 749 // or printable so if the document is in Landscape, this may 750 // result in double rotation. 751 AttributeClass attribClass = (getAttMap != null) ? 752 (AttributeClass)getAttMap.get("sides-supported") 753 : null; 754 if (attribClass != null) { 755 String[] sidesArray = attribClass.getArrayOfStringValues(); 756 if ((sidesArray != null) && (sidesArray.length > 0)) { 757 Sides[] sidesSup = new Sides[sidesArray.length]; 758 for (int i=0; i<sidesArray.length; i++) { 759 if (sidesArray[i].endsWith("long-edge")) { 760 sidesSup[i] = Sides.TWO_SIDED_LONG_EDGE; 761 } else if (sidesArray[i].endsWith("short-edge")) { 762 sidesSup[i] = Sides.TWO_SIDED_SHORT_EDGE; 763 } else { 764 sidesSup[i] = Sides.ONE_SIDED; 765 } 766 } 767 return sidesSup; 768 } 769 } 770 } 771 772 return null; 773 } 774 775 //This class is for getting all pre-defined Finishings 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 = 938 (CustomMediaSizeName)customMediaSizeNames[i]; 939 MediaSizeName msn = custom.getStandardMedia(); 940 if (media.equals(msn)) { 941 return customMediaSizeNames[i]; 942 } 943 } 944 return null; 945 } 946 947 948 /** 949 * Returns the matching standard Media using string comparison of names. 950 */ 951 private Media getIPPMedia(String mediaName) { 952 CustomMediaSizeName sampleSize = new CustomMediaSizeName("sample", "", 953 0, 0); 954 Media[] sizes = sampleSize.getSuperEnumTable(); 955 for (int i=0; i<sizes.length; i++) { 956 if (mediaName.equals(""+sizes[i])) { 957 return sizes[i]; 958 } 959 } 960 CustomMediaTray sampleTray = new CustomMediaTray("sample", ""); 961 Media[] trays = sampleTray.getSuperEnumTable(); 962 for (int i=0; i<trays.length; i++) { 963 if (mediaName.equals(""+trays[i])) { 964 return trays[i]; 965 } 966 } 967 return null; 968 } 969 970 private Media[] getSupportedMedia() { 971 if ((getAttMap != null) && 972 getAttMap.containsKey("media-supported")) { 973 974 AttributeClass attribClass = 975 (AttributeClass)getAttMap.get("media-supported"); 976 977 if (attribClass != null) { 978 String[] mediaVals = attribClass.getArrayOfStringValues(); 979 Media msn; 980 Media[] mediaNames = 981 new Media[mediaVals.length]; 982 for (int i=0; i<mediaVals.length; i++) { 983 msn = getIPPMedia(mediaVals[i]); 984 //REMIND: if null, create custom? 985 mediaNames[i] = msn; 986 } 987 return mediaNames; 988 } 989 } 990 return new Media[0]; 991 } 992 993 994 public synchronized Class[] getSupportedAttributeCategories() { 995 if (supportedCats != null) { 996 return supportedCats; 997 } 998 999 initAttributes(); 1000 1001 ArrayList catList = new ArrayList(); 1002 Class cl; 1003 1004 for (int i=0; i < printReqAttribDefault.length; i++) { 1005 PrintRequestAttribute pra = 1006 (PrintRequestAttribute)printReqAttribDefault[i]; 1007 if (getAttMap != null && 1008 getAttMap.containsKey(pra.getName()+"-supported")) { 1009 cl = pra.getCategory(); 1010 catList.add(cl); 1011 } 1012 } 1013 1014 // Some IPP printers like lexc710 do not have list of supported media 1015 // but CUPS can get the media from PPD, so we still report as 1016 // supported category. 1017 if (isCupsPrinter) { 1018 if (!catList.contains(Media.class)) { 1019 catList.add(Media.class); 1020 } 1021 1022 // Always add MediaPrintable for cups, 1023 // because we can get it from PPD. 1024 catList.add(MediaPrintableArea.class); 1025 1026 // this is already supported in UnixPrintJob 1027 catList.add(Destination.class); 1028 1029 // It is unfortunate that CUPS doesn't provide a way to query 1030 // if printer supports collation but since most printers 1031 // now supports collation and that most OS has a way 1032 // of setting it, it is a safe assumption to just always 1033 // include SheetCollate as supported attribute. 1034 1035 /* 1036 In Linux, we use Postscript for rendering but Linux still 1037 has issues in propagating Postscript-embedded setpagedevice 1038 setting like collation. Therefore, we temporarily exclude 1039 Linux. 1040 */ 1041 if (!UnixPrintServiceLookup.isLinux()) { 1042 catList.add(SheetCollate.class); 1043 } 1044 } 1045 1046 // With the assumption that Chromaticity is equivalent to 1047 // ColorSupported. 1048 if (getAttMap != null && getAttMap.containsKey("color-supported")) { 1049 catList.add(Chromaticity.class); 1050 } 1051 supportedCats = new Class[catList.size()]; 1052 catList.toArray(supportedCats); 1053 return supportedCats; 1054 } 1055 1056 1057 public boolean 1058 isAttributeCategorySupported(Class<? extends Attribute> category) 1059 { 1060 if (category == null) { 1061 throw new NullPointerException("null category"); 1062 } 1063 if (!(Attribute.class.isAssignableFrom(category))) { 1064 throw new IllegalArgumentException(category + 1065 " is not an Attribute"); 1066 } 1067 1068 if (supportedCats == null) { 1069 getSupportedAttributeCategories(); 1070 } 1071 1072 // It is safe to assume that Orientation is always supported 1073 // and even if CUPS or an IPP device reports it as not, 1074 // our renderer can do portrait, landscape and 1075 // reverse landscape. 1076 if (category == OrientationRequested.class) { 1077 return true; 1078 } 1079 1080 for (int i=0;i<supportedCats.length;i++) { 1081 if (category == supportedCats[i]) { 1082 return true; 1083 } 1084 } 1085 1086 return false; 1087 } 1088 1089 1090 public synchronized <T extends PrintServiceAttribute> 1091 T getAttribute(Class<T> category) 1092 { 1093 if (category == null) { 1094 throw new NullPointerException("category"); 1095 } 1096 if (!(PrintServiceAttribute.class.isAssignableFrom(category))) { 1097 throw new IllegalArgumentException("Not a PrintServiceAttribute"); 1098 } 1099 1100 initAttributes(); 1101 1102 if (category == PrinterName.class) { 1103 return (T)(new PrinterName(printer, null)); 1104 } else if (category == PrinterInfo.class) { 1105 PrinterInfo pInfo = new PrinterInfo(printer, null); 1106 AttributeClass ac = (getAttMap != null) ? 1107 (AttributeClass)getAttMap.get(pInfo.getName()) 1108 : null; 1109 if (ac != null) { 1110 return (T)(new PrinterInfo(ac.getStringValue(), null)); 1111 } 1112 return (T)pInfo; 1113 } else if (category == QueuedJobCount.class) { 1114 QueuedJobCount qjc = new QueuedJobCount(0); 1115 AttributeClass ac = (getAttMap != null) ? 1116 (AttributeClass)getAttMap.get(qjc.getName()) 1117 : null; 1118 if (ac != null) { 1119 qjc = new QueuedJobCount(ac.getIntValue()); 1120 } 1121 return (T)qjc; 1122 } else if (category == PrinterIsAcceptingJobs.class) { 1123 PrinterIsAcceptingJobs accJob = 1124 PrinterIsAcceptingJobs.ACCEPTING_JOBS; 1125 AttributeClass ac = (getAttMap != null) ? 1126 (AttributeClass)getAttMap.get(accJob.getName()) 1127 : null; 1128 if ((ac != null) && (ac.getByteValue() == 0)) { 1129 accJob = PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS; 1130 } 1131 return (T)accJob; 1132 } else if (category == ColorSupported.class) { 1133 ColorSupported cs = ColorSupported.SUPPORTED; 1134 AttributeClass ac = (getAttMap != null) ? 1135 (AttributeClass)getAttMap.get(cs.getName()) 1136 : null; 1137 if ((ac != null) && (ac.getByteValue() == 0)) { 1138 cs = ColorSupported.NOT_SUPPORTED; 1139 } 1140 return (T)cs; 1141 } else if (category == PDLOverrideSupported.class) { 1142 1143 if (isCupsPrinter) { 1144 // Documented: For CUPS this will always be false 1145 return (T)PDLOverrideSupported.NOT_ATTEMPTED; 1146 } else { 1147 // REMIND: check attribute values 1148 return (T)PDLOverrideSupported.NOT_ATTEMPTED; 1149 } 1150 } else if (category == PrinterURI.class) { 1151 return (T)(new PrinterURI(myURI)); 1152 } else { 1153 return null; 1154 } 1155 } 1156 1157 1158 public synchronized PrintServiceAttributeSet getAttributes() { 1159 // update getAttMap by sending again get-attributes IPP request 1160 init = false; 1161 initAttributes(); 1162 1163 HashPrintServiceAttributeSet attrs = 1164 new HashPrintServiceAttributeSet(); 1165 1166 for (int i=0; i < serviceAttributes.length; i++) { 1167 String name = (String)serviceAttributes[i][1]; 1168 if (getAttMap != null && getAttMap.containsKey(name)) { 1169 Class c = (Class)serviceAttributes[i][0]; 1170 PrintServiceAttribute psa = getAttribute(c); 1171 if (psa != null) { 1172 attrs.add(psa); 1173 } 1174 } 1175 } 1176 return AttributeSetUtilities.unmodifiableView(attrs); 1177 } 1178 1179 public boolean isIPPSupportedImages(String mimeType) { 1180 if (supportedDocFlavors == null) { 1181 getSupportedDocFlavors(); 1182 } 1183 1184 if (mimeType.equals("image/png") && pngImagesAdded) { 1185 return true; 1186 } else if (mimeType.equals("image/gif") && gifImagesAdded) { 1187 return true; 1188 } else if (mimeType.equals("image/jpeg") && jpgImagesAdded) { 1189 return true; 1190 } 1191 1192 return false; 1193 } 1194 1195 1196 private boolean isSupportedCopies(Copies copies) { 1197 CopiesSupported cs = (CopiesSupported) 1198 getSupportedAttributeValues(Copies.class, null, null); 1199 int[][] members = cs.getMembers(); 1200 int min, max; 1201 if ((members.length > 0) && (members[0].length > 0)) { 1202 min = members[0][0]; 1203 max = members[0][1]; 1204 } else { 1205 min = 1; 1206 max = MAXCOPIES; 1207 } 1208 1209 int value = copies.getValue(); 1210 return (value >= min && value <= max); 1211 } 1212 1213 private boolean isAutoSense(DocFlavor flavor) { 1214 if (flavor.equals(DocFlavor.BYTE_ARRAY.AUTOSENSE) || 1215 flavor.equals(DocFlavor.INPUT_STREAM.AUTOSENSE) || 1216 flavor.equals(DocFlavor.URL.AUTOSENSE)) { 1217 return true; 1218 } 1219 else { 1220 return false; 1221 } 1222 } 1223 1224 private synchronized boolean isSupportedMediaTray(MediaTray msn) { 1225 initAttributes(); 1226 1227 if (mediaTrays != null) { 1228 for (int i=0; i<mediaTrays.length; i++) { 1229 if (msn.equals(mediaTrays[i])) { 1230 return true; 1231 } 1232 } 1233 } 1234 return false; 1235 } 1236 1237 private synchronized boolean isSupportedMedia(MediaSizeName msn) { 1238 initAttributes(); 1239 1240 if (msn.equals((Media)getDefaultAttributeValue(Media.class))) { 1241 return true; 1242 } 1243 for (int i=0; i<mediaSizeNames.length; i++) { 1244 debug_println(debugPrefix+"isSupportedMedia, mediaSizeNames[i] "+mediaSizeNames[i]); 1245 if (msn.equals(mediaSizeNames[i])) { 1246 return true; 1247 } 1248 } 1249 return false; 1250 } 1251 1252 /* Return false if flavor is not null, pageable, nor printable and 1253 * Destination is part of attributes. 1254 */ 1255 private boolean 1256 isDestinationSupported(DocFlavor flavor, AttributeSet attributes) { 1257 1258 if ((attributes != null) && 1259 (attributes.get(Destination.class) != null) && 1260 !(flavor == null || 1261 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 1262 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) { 1263 return false; 1264 } 1265 return true; 1266 } 1267 1268 1269 public boolean isAttributeValueSupported(Attribute attr, 1270 DocFlavor flavor, 1271 AttributeSet attributes) { 1272 if (attr == null) { 1273 throw new NullPointerException("null attribute"); 1274 } 1275 if (flavor != null) { 1276 if (!isDocFlavorSupported(flavor)) { 1277 throw new IllegalArgumentException(flavor + 1278 " is an unsupported flavor"); 1279 } else if (isAutoSense(flavor)) { 1280 return false; 1281 } 1282 } 1283 Class category = attr.getCategory(); 1284 if (!isAttributeCategorySupported(category)) { 1285 return false; 1286 } 1287 1288 /* Test if the flavor is compatible with the attributes */ 1289 if (!isDestinationSupported(flavor, attributes)) { 1290 return false; 1291 } 1292 1293 /* Test if the flavor is compatible with the category */ 1294 if (attr.getCategory() == Chromaticity.class) { 1295 if ((flavor == null) || 1296 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 1297 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) || 1298 !isIPPSupportedImages(flavor.getMimeType())) { 1299 return attr == Chromaticity.COLOR; 1300 } else { 1301 return false; 1302 } 1303 } else if (attr.getCategory() == Copies.class) { 1304 return (flavor == null || 1305 !(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) || 1306 flavor.equals(DocFlavor.URL.POSTSCRIPT) || 1307 flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) && 1308 isSupportedCopies((Copies)attr); 1309 1310 } else if (attr.getCategory() == Destination.class) { 1311 if (flavor == null || 1312 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 1313 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 1314 URI uri = ((Destination)attr).getURI(); 1315 if ("file".equals(uri.getScheme()) && 1316 !(uri.getSchemeSpecificPart().equals(""))) { 1317 return true; 1318 } 1319 } 1320 return false; 1321 } else if (attr.getCategory() == Media.class) { 1322 if (attr instanceof MediaSizeName) { 1323 return isSupportedMedia((MediaSizeName)attr); 1324 } 1325 if (attr instanceof MediaTray) { 1326 return isSupportedMediaTray((MediaTray)attr); 1327 } 1328 } else if (attr.getCategory() == PageRanges.class) { 1329 if (flavor != null && 1330 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 1331 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) { 1332 return false; 1333 } 1334 } else if (attr.getCategory() == SheetCollate.class) { 1335 if (flavor != null && 1336 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 1337 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) { 1338 return false; 1339 } 1340 } else if (attr.getCategory() == Sides.class) { 1341 Sides[] sidesArray = (Sides[])getSupportedAttributeValues( 1342 Sides.class, 1343 flavor, 1344 attributes); 1345 1346 if (sidesArray != null) { 1347 for (int i=0; i<sidesArray.length; i++) { 1348 if (sidesArray[i] == (Sides)attr) { 1349 return true; 1350 } 1351 } 1352 } 1353 return false; 1354 } else if (attr.getCategory() == OrientationRequested.class) { 1355 OrientationRequested[] orientArray = 1356 (OrientationRequested[])getSupportedAttributeValues( 1357 OrientationRequested.class, 1358 flavor, 1359 attributes); 1360 1361 if (orientArray != null) { 1362 for (int i=0; i<orientArray.length; i++) { 1363 if (orientArray[i] == (OrientationRequested)attr) { 1364 return true; 1365 } 1366 } 1367 } 1368 return false; 1369 } 1370 return true; 1371 } 1372 1373 1374 public synchronized Object 1375 getDefaultAttributeValue(Class<? extends Attribute> category) 1376 { 1377 if (category == null) { 1378 throw new NullPointerException("null category"); 1379 } 1380 if (!Attribute.class.isAssignableFrom(category)) { 1381 throw new IllegalArgumentException(category + 1382 " is not an Attribute"); 1383 } 1384 if (!isAttributeCategorySupported(category)) { 1385 return null; 1386 } 1387 1388 initAttributes(); 1389 1390 String catName = null; 1391 for (int i=0; i < printReqAttribDefault.length; i++) { 1392 PrintRequestAttribute pra = 1393 (PrintRequestAttribute)printReqAttribDefault[i]; 1394 if (pra.getCategory() == category) { 1395 catName = pra.getName(); 1396 break; 1397 } 1398 } 1399 String attribName = catName+"-default"; 1400 AttributeClass attribClass = (getAttMap != null) ? 1401 (AttributeClass)getAttMap.get(attribName) : null; 1402 1403 if (category == Copies.class) { 1404 if (attribClass != null) { 1405 return new Copies(attribClass.getIntValue()); 1406 } else { 1407 return new Copies(1); 1408 } 1409 } else if (category == Chromaticity.class) { 1410 return Chromaticity.COLOR; 1411 } else if (category == Destination.class) { 1412 try { 1413 return new Destination((new File("out.ps")).toURI()); 1414 } catch (SecurityException se) { 1415 try { 1416 return new Destination(new URI("file:out.ps")); 1417 } catch (URISyntaxException e) { 1418 return null; 1419 } 1420 } 1421 } else if (category == Fidelity.class) { 1422 return Fidelity.FIDELITY_FALSE; 1423 } else if (category == Finishings.class) { 1424 return Finishings.NONE; 1425 } else if (category == JobName.class) { 1426 return new JobName("Java Printing", null); 1427 } else if (category == JobSheets.class) { 1428 if (attribClass != null && 1429 attribClass.getStringValue().equals("none")) { 1430 return JobSheets.NONE; 1431 } else { 1432 return JobSheets.STANDARD; 1433 } 1434 } else if (category == Media.class) { 1435 defaultMediaIndex = 0; 1436 if (mediaSizeNames.length == 0) { 1437 String defaultCountry = Locale.getDefault().getCountry(); 1438 if (defaultCountry != null && 1439 (defaultCountry.equals("") || 1440 defaultCountry.equals(Locale.US.getCountry()) || 1441 defaultCountry.equals(Locale.CANADA.getCountry()))) { 1442 return MediaSizeName.NA_LETTER; 1443 } else { 1444 return MediaSizeName.ISO_A4; 1445 } 1446 } 1447 1448 if (attribClass != null) { 1449 String name = attribClass.getStringValue(); 1450 if (isCupsPrinter) { 1451 for (int i=0; i< customMediaSizeNames.length; i++) { 1452 //REMIND: get default from PPD. In native _getMedia, 1453 // move default (ppd_option_t->defchoice) to index 0. 1454 // In the meantime, use indexOf because PPD name 1455 // may be different from the IPP attribute name. 1456 if (customMediaSizeNames[i].toString().indexOf(name) 1457 != -1) { 1458 defaultMediaIndex = i; 1459 return mediaSizeNames[defaultMediaIndex]; 1460 } 1461 } 1462 } else { 1463 for (int i=0; i< mediaSizeNames.length; i++) { 1464 if (mediaSizeNames[i].toString().indexOf(name) != -1) { 1465 defaultMediaIndex = i; 1466 return mediaSizeNames[defaultMediaIndex]; 1467 } 1468 } 1469 } 1470 } 1471 return mediaSizeNames[defaultMediaIndex]; 1472 1473 } else if (category == MediaPrintableArea.class) { 1474 MediaPrintableArea[] mpas; 1475 if ((cps != null) && 1476 ((mpas = cps.getMediaPrintableArea()) != null)) { 1477 if (defaultMediaIndex == -1) { 1478 // initializes value of defaultMediaIndex 1479 getDefaultAttributeValue(Media.class); 1480 } 1481 return mpas[defaultMediaIndex]; 1482 } else { 1483 String defaultCountry = Locale.getDefault().getCountry(); 1484 float iw, ih; 1485 if (defaultCountry != null && 1486 (defaultCountry.equals("") || 1487 defaultCountry.equals(Locale.US.getCountry()) || 1488 defaultCountry.equals(Locale.CANADA.getCountry()))) { 1489 iw = MediaSize.NA.LETTER.getX(Size2DSyntax.INCH) - 0.5f; 1490 ih = MediaSize.NA.LETTER.getY(Size2DSyntax.INCH) - 0.5f; 1491 } else { 1492 iw = MediaSize.ISO.A4.getX(Size2DSyntax.INCH) - 0.5f; 1493 ih = MediaSize.ISO.A4.getY(Size2DSyntax.INCH) - 0.5f; 1494 } 1495 return new MediaPrintableArea(0.25f, 0.25f, iw, ih, 1496 MediaPrintableArea.INCH); 1497 } 1498 } else if (category == NumberUp.class) { 1499 return new NumberUp(1); // for CUPS this is always 1 1500 } else if (category == OrientationRequested.class) { 1501 if (attribClass != null) { 1502 switch (attribClass.getIntValue()) { 1503 default: 1504 case 3: return OrientationRequested.PORTRAIT; 1505 case 4: return OrientationRequested.LANDSCAPE; 1506 case 5: return OrientationRequested.REVERSE_LANDSCAPE; 1507 case 6: return OrientationRequested.REVERSE_PORTRAIT; 1508 } 1509 } else { 1510 return OrientationRequested.PORTRAIT; 1511 } 1512 } else if (category == PageRanges.class) { 1513 if (attribClass != null) { 1514 int[] range = attribClass.getIntRangeValue(); 1515 return new PageRanges(range[0], range[1]); 1516 } else { 1517 return new PageRanges(1, Integer.MAX_VALUE); 1518 } 1519 } else if (category == RequestingUserName.class) { 1520 String userName = ""; 1521 try { 1522 userName = System.getProperty("user.name", ""); 1523 } catch (SecurityException se) { 1524 } 1525 return new RequestingUserName(userName, null); 1526 } else if (category == SheetCollate.class) { 1527 return SheetCollate.UNCOLLATED; 1528 } else if (category == Sides.class) { 1529 if (attribClass != null) { 1530 if (attribClass.getStringValue().endsWith("long-edge")) { 1531 return Sides.TWO_SIDED_LONG_EDGE; 1532 } else if (attribClass.getStringValue().endsWith( 1533 "short-edge")) { 1534 return Sides.TWO_SIDED_SHORT_EDGE; 1535 } 1536 } 1537 return Sides.ONE_SIDED; 1538 } 1539 1540 return null; 1541 } 1542 1543 public ServiceUIFactory getServiceUIFactory() { 1544 return null; 1545 } 1546 1547 public void wakeNotifier() { 1548 synchronized (this) { 1549 if (notifier != null) { 1550 notifier.wake(); 1551 } 1552 } 1553 } 1554 1555 public void addPrintServiceAttributeListener( 1556 PrintServiceAttributeListener listener) { 1557 synchronized (this) { 1558 if (listener == null) { 1559 return; 1560 } 1561 if (notifier == null) { 1562 notifier = new ServiceNotifier(this); 1563 } 1564 notifier.addListener(listener); 1565 } 1566 } 1567 1568 public void removePrintServiceAttributeListener( 1569 PrintServiceAttributeListener listener) { 1570 synchronized (this) { 1571 if (listener == null || notifier == null ) { 1572 return; 1573 } 1574 notifier.removeListener(listener); 1575 if (notifier.isEmpty()) { 1576 notifier.stopNotifier(); 1577 notifier = null; 1578 } 1579 } 1580 } 1581 1582 String getDest() { 1583 return printer; 1584 } 1585 1586 public String getName() { 1587 /* 1588 * Mac is using printer-info IPP attribute for its human-readable printer 1589 * name and is also the identifier used in NSPrintInfo:setPrinter. 1590 */ 1591 if (UnixPrintServiceLookup.isMac()) { 1592 PrintServiceAttributeSet psaSet = this.getAttributes(); 1593 if (psaSet != null) { 1594 PrinterInfo pName = (PrinterInfo)psaSet.get(PrinterInfo.class); 1595 if (pName != null) { 1596 return pName.toString(); 1597 } 1598 } 1599 } 1600 return printer; 1601 } 1602 1603 1604 public boolean usesClass(Class c) { 1605 return (c == sun.print.PSPrinterJob.class); 1606 } 1607 1608 1609 public static HttpURLConnection getIPPConnection(URL url) { 1610 HttpURLConnection connection; 1611 URLConnection urlc; 1612 try { 1613 urlc = url.openConnection(); 1614 } catch (java.io.IOException ioe) { 1615 return null; 1616 } 1617 if (!(urlc instanceof HttpURLConnection)) { 1618 return null; 1619 } 1620 connection = (HttpURLConnection)urlc; 1621 connection.setUseCaches(false); 1622 connection.setDefaultUseCaches(false); 1623 connection.setDoInput(true); 1624 connection.setDoOutput(true); 1625 connection.setRequestProperty("Content-type", "application/ipp"); 1626 return connection; 1627 } 1628 1629 1630 public synchronized boolean isPostscript() { 1631 if (isPS == null) { 1632 isPS = Boolean.TRUE; 1633 if (isCupsPrinter) { 1634 try { 1635 urlConnection = getIPPConnection( 1636 new URL(myURL+".ppd")); 1637 1638 InputStream is = urlConnection.getInputStream(); 1639 if (is != null) { 1640 BufferedReader d = 1641 new BufferedReader(new InputStreamReader(is, 1642 Charset.forName("ISO-8859-1"))); 1643 String lineStr; 1644 while ((lineStr = d.readLine()) != null) { 1645 if (lineStr.startsWith("*cupsFilter:")) { 1646 isPS = Boolean.FALSE; 1647 break; 1648 } 1649 } 1650 } 1651 } catch (java.io.IOException e) { 1652 debug_println(" isPostscript, e= "+e); 1653 /* if PPD is not found, this may be a raw printer 1654 and in this case it is assumed that it is a 1655 Postscript printer */ 1656 // do nothing 1657 } 1658 } 1659 } 1660 return isPS.booleanValue(); 1661 } 1662 1663 1664 private void opGetAttributes() { 1665 try { 1666 debug_println(debugPrefix+"opGetAttributes myURI "+myURI+" myURL "+myURL); 1667 1668 AttributeClass attClNoUri[] = { 1669 AttributeClass.ATTRIBUTES_CHARSET, 1670 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE}; 1671 1672 AttributeClass attCl[] = { 1673 AttributeClass.ATTRIBUTES_CHARSET, 1674 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE, 1675 new AttributeClass("printer-uri", 1676 AttributeClass.TAG_URI, 1677 ""+myURI)}; 1678 1679 OutputStream os = (OutputStream)java.security.AccessController. 1680 doPrivileged(new java.security.PrivilegedAction() { 1681 public Object run() { 1682 try { 1683 return urlConnection.getOutputStream(); 1684 } catch (Exception e) { 1685 } 1686 return null; 1687 } 1688 }); 1689 1690 if (os == null) { 1691 return; 1692 } 1693 1694 boolean success = (myURI == null) ? 1695 writeIPPRequest(os, OP_GET_ATTRIBUTES, attClNoUri) : 1696 writeIPPRequest(os, OP_GET_ATTRIBUTES, attCl); 1697 if (success) { 1698 InputStream is = null; 1699 if ((is = urlConnection.getInputStream())!=null) { 1700 HashMap[] responseMap = readIPPResponse(is); 1701 1702 if (responseMap != null && responseMap.length > 0) { 1703 getAttMap = responseMap[0]; 1704 } 1705 } else { 1706 debug_println(debugPrefix+"opGetAttributes - null input stream"); 1707 } 1708 is.close(); 1709 } 1710 os.close(); 1711 } catch (java.io.IOException e) { 1712 debug_println(debugPrefix+"opGetAttributes - input/output stream: "+e); 1713 } 1714 } 1715 1716 1717 public static boolean writeIPPRequest(OutputStream os, 1718 String operCode, 1719 AttributeClass[] attCl) { 1720 OutputStreamWriter osw; 1721 try { 1722 osw = new OutputStreamWriter(os, "UTF-8"); 1723 } catch (java.io.UnsupportedEncodingException exc) { 1724 debug_println(debugPrefix+"writeIPPRequest, UTF-8 not supported? Exception: "+exc); 1725 return false; 1726 } 1727 debug_println(debugPrefix+"writeIPPRequest, op code= "+operCode); 1728 char[] opCode = new char[2]; 1729 opCode[0] = (char)Byte.parseByte(operCode.substring(0,2), 16); 1730 opCode[1] = (char)Byte.parseByte(operCode.substring(2,4), 16); 1731 char[] bytes = {0x01, 0x01, 0x00, 0x01}; 1732 try { 1733 osw.write(bytes, 0, 2); // version number 1734 osw.write(opCode, 0, 2); // operation code 1735 bytes[0] = 0x00; bytes[1] = 0x00; 1736 osw.write(bytes, 0, 4); // request ID #1 1737 1738 bytes[0] = 0x01; // operation-group-tag 1739 osw.write(bytes[0]); 1740 1741 String valStr; 1742 char[] lenStr; 1743 1744 AttributeClass ac; 1745 for (int i=0; i < attCl.length; i++) { 1746 ac = attCl[i]; 1747 osw.write(ac.getType()); // value tag 1748 1749 lenStr = ac.getLenChars(); 1750 osw.write(lenStr, 0, 2); // length 1751 osw.write(""+ac, 0, ac.getName().length()); 1752 1753 // check if string range (0x35 -> 0x49) 1754 if (ac.getType() >= AttributeClass.TAG_TEXT_LANGUAGE && 1755 ac.getType() <= AttributeClass.TAG_MIME_MEDIATYPE){ 1756 valStr = (String)ac.getObjectValue(); 1757 bytes[0] = 0; bytes[1] = (char)valStr.length(); 1758 osw.write(bytes, 0, 2); 1759 osw.write(valStr, 0, valStr.length()); 1760 } // REMIND: need to support other value tags but for CUPS 1761 // string is all we need. 1762 } 1763 1764 osw.write(GRPTAG_END_ATTRIBUTES); 1765 osw.flush(); 1766 osw.close(); 1767 } catch (java.io.IOException ioe) { 1768 debug_println(debugPrefix+"writeIPPRequest, IPPPrintService Exception in writeIPPRequest: "+ioe); 1769 return false; 1770 } 1771 return true; 1772 } 1773 1774 1775 public static HashMap[] readIPPResponse(InputStream inputStream) { 1776 1777 if (inputStream == null) { 1778 return null; 1779 } 1780 1781 byte response[] = new byte[MAX_ATTRIBUTE_LENGTH]; 1782 try { 1783 1784 DataInputStream ois = new DataInputStream(inputStream); 1785 1786 // read status and ID 1787 if ((ois.read(response, 0, 8) > -1) && 1788 (response[2] == STATUSCODE_SUCCESS)) { 1789 1790 ByteArrayOutputStream outObj; 1791 int counter=0; 1792 short len = 0; 1793 String attribStr = null; 1794 // assign default value 1795 byte valTagByte = AttributeClass.TAG_KEYWORD; 1796 ArrayList respList = new ArrayList(); 1797 HashMap responseMap = new HashMap(); 1798 1799 response[0] = ois.readByte(); 1800 1801 // check for group tags 1802 while ((response[0] >= GRPTAG_OP_ATTRIBUTES) && 1803 (response[0] <= GRPTAG_PRINTER_ATTRIBUTES) 1804 && (response[0] != GRPTAG_END_ATTRIBUTES)) { 1805 debug_println(debugPrefix+"readIPPResponse, checking group tag, response[0]= "+ 1806 response[0]); 1807 1808 outObj = new ByteArrayOutputStream(); 1809 //make sure counter and attribStr are re-initialized 1810 counter = 0; 1811 attribStr = null; 1812 1813 // read value tag 1814 response[0] = ois.readByte(); 1815 while (response[0] >= AttributeClass.TAG_UNSUPPORTED_VALUE && 1816 response[0] <= AttributeClass.TAG_MEMBER_ATTRNAME) { 1817 // read name length 1818 len = ois.readShort(); 1819 1820 // If current value is not part of previous attribute 1821 // then close stream and add it to HashMap. 1822 // It is part of previous attribute if name length=0. 1823 if ((len != 0) && (attribStr != null)) { 1824 //last byte is the total # of values 1825 outObj.write(counter); 1826 outObj.flush(); 1827 outObj.close(); 1828 byte outArray[] = outObj.toByteArray(); 1829 1830 // if key exists, new HashMap 1831 if (responseMap.containsKey(attribStr)) { 1832 respList.add(responseMap); 1833 responseMap = new HashMap(); 1834 } 1835 1836 // exclude those that are unknown 1837 if (valTagByte >= AttributeClass.TAG_INT) { 1838 AttributeClass ac = 1839 new AttributeClass(attribStr, 1840 valTagByte, 1841 outArray); 1842 1843 responseMap.put(ac.getName(), ac); 1844 debug_println(debugPrefix+ "readIPPResponse "+ac); 1845 } 1846 1847 outObj = new ByteArrayOutputStream(); 1848 counter = 0; //reset counter 1849 } 1850 //check if this is new value tag 1851 if (counter == 0) { 1852 valTagByte = response[0]; 1853 } 1854 // read attribute name 1855 if (len != 0) { 1856 // read "len" characters 1857 // make sure it doesn't exceed the maximum 1858 if (len > MAX_ATTRIBUTE_LENGTH) { 1859 response = new byte[len]; // expand as needed 1860 } 1861 ois.read(response, 0, len); 1862 attribStr = new String(response, 0, len); 1863 } 1864 // read value length 1865 len = ois.readShort(); 1866 // write name length 1867 outObj.write(len); 1868 // read value, make sure it doesn't exceed the maximum 1869 if (len > MAX_ATTRIBUTE_LENGTH) { 1870 response = new byte[len]; // expand as needed 1871 } 1872 ois.read(response, 0, len); 1873 // write value of "len" length 1874 outObj.write(response, 0, len); 1875 counter++; 1876 // read next byte 1877 response[0] = ois.readByte(); 1878 } 1879 1880 if (attribStr != null) { 1881 outObj.write(counter); 1882 outObj.flush(); 1883 outObj.close(); 1884 1885 // if key exists in old HashMap, new HashMap 1886 if ((counter != 0) && 1887 responseMap.containsKey(attribStr)) { 1888 respList.add(responseMap); 1889 responseMap = new HashMap(); 1890 } 1891 1892 byte outArray[] = outObj.toByteArray(); 1893 1894 AttributeClass ac = 1895 new AttributeClass(attribStr, 1896 valTagByte, 1897 outArray); 1898 responseMap.put(ac.getName(), ac); 1899 } 1900 } 1901 ois.close(); 1902 if ((responseMap != null) && (responseMap.size() > 0)) { 1903 respList.add(responseMap); 1904 } 1905 return (HashMap[])respList.toArray( 1906 new HashMap[respList.size()]); 1907 } else { 1908 debug_println(debugPrefix+ 1909 "readIPPResponse client error, IPP status code-" 1910 +Integer.toHexString(response[2])+" & " 1911 +Integer.toHexString(response[3])); 1912 return null; 1913 } 1914 1915 } catch (java.io.IOException e) { 1916 debug_println(debugPrefix+"readIPPResponse: "+e); 1917 if (debugPrint) { 1918 e.printStackTrace(); 1919 } 1920 return null; 1921 } 1922 } 1923 1924 1925 public String toString() { 1926 return "IPP Printer : " + getName(); 1927 } 1928 1929 public boolean equals(Object obj) { 1930 return (obj == this || 1931 (obj instanceof IPPPrintService && 1932 ((IPPPrintService)obj).getName().equals(getName()))); 1933 } 1934 1935 public int hashCode() { 1936 return this.getClass().hashCode()+getName().hashCode(); 1937 } 1938 }