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