1 /* 2 * Copyright (c) 2000, 2020, 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 java.io.File; 29 import java.net.URI; 30 import java.net.URISyntaxException; 31 import java.util.ArrayList; 32 import java.util.Locale; 33 34 import java.awt.GraphicsEnvironment; 35 import java.awt.Toolkit; 36 import javax.print.DocFlavor; 37 import javax.print.DocPrintJob; 38 import javax.print.PrintService; 39 import javax.print.ServiceUIFactory; 40 import javax.print.attribute.Attribute; 41 import javax.print.attribute.AttributeSet; 42 import javax.print.attribute.AttributeSetUtilities; 43 import javax.print.attribute.HashAttributeSet; 44 import javax.print.attribute.PrintServiceAttribute; 45 import javax.print.attribute.PrintServiceAttributeSet; 46 import javax.print.attribute.HashPrintServiceAttributeSet; 47 import javax.print.attribute.Size2DSyntax; 48 import javax.print.attribute.standard.PrinterName; 49 import javax.print.attribute.standard.PrinterIsAcceptingJobs; 50 import javax.print.attribute.standard.QueuedJobCount; 51 import javax.print.attribute.standard.JobName; 52 import javax.print.attribute.standard.JobSheets; 53 import javax.print.attribute.standard.RequestingUserName; 54 import javax.print.attribute.standard.Chromaticity; 55 import javax.print.attribute.standard.ColorSupported; 56 import javax.print.attribute.standard.Copies; 57 import javax.print.attribute.standard.CopiesSupported; 58 import javax.print.attribute.standard.Destination; 59 import javax.print.attribute.standard.DialogOwner; 60 import javax.print.attribute.standard.DialogTypeSelection; 61 import javax.print.attribute.standard.Fidelity; 62 import javax.print.attribute.standard.Media; 63 import javax.print.attribute.standard.MediaPrintableArea; 64 import javax.print.attribute.standard.MediaSize; 65 import javax.print.attribute.standard.MediaSizeName; 66 import javax.print.attribute.standard.OrientationRequested; 67 import javax.print.attribute.standard.PageRanges; 68 import javax.print.attribute.standard.PrinterState; 69 import javax.print.attribute.standard.PrinterStateReason; 70 import javax.print.attribute.standard.PrinterStateReasons; 71 import javax.print.attribute.standard.Severity; 72 import javax.print.attribute.standard.SheetCollate; 73 import javax.print.attribute.standard.Sides; 74 import javax.print.event.PrintServiceAttributeListener; 75 76 77 public class UnixPrintService implements PrintService, AttributeUpdater, 78 SunPrinterJobService { 79 80 /* define doc flavors for text types in the default encoding of 81 * this platform since we can always read those. 82 */ 83 private static String encoding = "ISO8859_1"; 84 private static DocFlavor textByteFlavor; 85 86 private static DocFlavor[] supportedDocFlavors = null; 87 private static final DocFlavor[] supportedDocFlavorsInit = { 88 DocFlavor.BYTE_ARRAY.POSTSCRIPT, 89 DocFlavor.INPUT_STREAM.POSTSCRIPT, 90 DocFlavor.URL.POSTSCRIPT, 91 DocFlavor.BYTE_ARRAY.GIF, 92 DocFlavor.INPUT_STREAM.GIF, 93 DocFlavor.URL.GIF, 94 DocFlavor.BYTE_ARRAY.JPEG, 95 DocFlavor.INPUT_STREAM.JPEG, 96 DocFlavor.URL.JPEG, 97 DocFlavor.BYTE_ARRAY.PNG, 98 DocFlavor.INPUT_STREAM.PNG, 99 DocFlavor.URL.PNG, 100 101 DocFlavor.CHAR_ARRAY.TEXT_PLAIN, 102 DocFlavor.READER.TEXT_PLAIN, 103 DocFlavor.STRING.TEXT_PLAIN, 104 105 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_8, 106 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16, 107 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16BE, 108 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16LE, 109 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_US_ASCII, 110 111 112 DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_8, 113 DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16, 114 DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16BE, 115 DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16LE, 116 DocFlavor.INPUT_STREAM.TEXT_PLAIN_US_ASCII, 117 118 119 DocFlavor.URL.TEXT_PLAIN_UTF_8, 120 DocFlavor.URL.TEXT_PLAIN_UTF_16, 121 DocFlavor.URL.TEXT_PLAIN_UTF_16BE, 122 DocFlavor.URL.TEXT_PLAIN_UTF_16LE, 123 DocFlavor.URL.TEXT_PLAIN_US_ASCII, 124 125 DocFlavor.SERVICE_FORMATTED.PAGEABLE, 126 DocFlavor.SERVICE_FORMATTED.PRINTABLE, 127 128 DocFlavor.BYTE_ARRAY.AUTOSENSE, 129 DocFlavor.URL.AUTOSENSE, 130 DocFlavor.INPUT_STREAM.AUTOSENSE 131 }; 132 133 private static final DocFlavor[] supportedHostDocFlavors = { 134 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_HOST, 135 DocFlavor.INPUT_STREAM.TEXT_PLAIN_HOST, 136 DocFlavor.URL.TEXT_PLAIN_HOST 137 }; 138 139 String[] lpcStatusCom = { 140 "", 141 "| grep -E '^[ 0-9a-zA-Z_-]*@' | awk '{print $2, $3}'" 142 }; 143 144 String[] lpcQueueCom = { 145 "", 146 "| grep -E '^[ 0-9a-zA-Z_-]*@' | awk '{print $4}'" 147 }; 148 149 static { 150 encoding = java.security.AccessController.doPrivileged( 151 new sun.security.action.GetPropertyAction("file.encoding")); 152 } 153 154 /* let's try to support a few of these */ 155 private static final Class<?>[] serviceAttrCats = { 156 PrinterName.class, 157 PrinterIsAcceptingJobs.class, 158 QueuedJobCount.class, 159 }; 160 161 /* it turns out to be inconvenient to store the other categories 162 * separately because many attributes are in multiple categories. 163 */ 164 private static final Class<?>[] otherAttrCats = { 165 Chromaticity.class, 166 Copies.class, 167 Destination.class, 168 Fidelity.class, 169 JobName.class, 170 JobSheets.class, 171 Media.class, /* have to support this somehow ... */ 172 MediaPrintableArea.class, 173 OrientationRequested.class, 174 PageRanges.class, 175 RequestingUserName.class, 176 SheetCollate.class, 177 Sides.class, 178 }; 179 180 private static int MAXCOPIES = 1000; 181 182 private static final MediaSizeName[] mediaSizes = { 183 MediaSizeName.NA_LETTER, 184 MediaSizeName.TABLOID, 185 MediaSizeName.LEDGER, 186 MediaSizeName.NA_LEGAL, 187 MediaSizeName.EXECUTIVE, 188 MediaSizeName.ISO_A3, 189 MediaSizeName.ISO_A4, 190 MediaSizeName.ISO_A5, 191 MediaSizeName.ISO_B4, 192 MediaSizeName.ISO_B5, 193 }; 194 195 private String printer; 196 private PrinterName name; 197 private boolean isInvalid; 198 199 private transient PrintServiceAttributeSet lastSet; 200 private transient ServiceNotifier notifier = null; 201 202 UnixPrintService(String name) { 203 if (name == null) { 204 throw new IllegalArgumentException("null printer name"); 205 } 206 printer = name; 207 isInvalid = false; 208 } 209 210 public void invalidateService() { 211 isInvalid = true; 212 } 213 214 public String getName() { 215 return printer; 216 } 217 218 private PrinterName getPrinterName() { 219 if (name == null) { 220 name = new PrinterName(printer, null); 221 } 222 return name; 223 } 224 225 private PrinterIsAcceptingJobs getPrinterIsAcceptingJobsBSD() { 226 if (PrintServiceLookupProvider.cmdIndex == 227 PrintServiceLookupProvider.UNINITIALIZED) { 228 229 PrintServiceLookupProvider.cmdIndex = 230 PrintServiceLookupProvider.getBSDCommandIndex(); 231 } 232 233 String command = "/usr/sbin/lpc status " + printer 234 + lpcStatusCom[PrintServiceLookupProvider.cmdIndex]; 235 String[] results= PrintServiceLookupProvider.execCmd(command); 236 237 if (results != null && results.length > 0) { 238 if (PrintServiceLookupProvider.cmdIndex == 239 PrintServiceLookupProvider.BSD_LPD_NG) { 240 if (results[0].startsWith("enabled enabled")) { 241 return PrinterIsAcceptingJobs.ACCEPTING_JOBS ; 242 } 243 } else { 244 if ((results[1].trim().startsWith("queuing is enabled") && 245 results[2].trim().startsWith("printing is enabled")) || 246 (results.length >= 4 && 247 results[2].trim().startsWith("queuing is enabled") && 248 results[3].trim().startsWith("printing is enabled"))) { 249 return PrinterIsAcceptingJobs.ACCEPTING_JOBS ; 250 } 251 } 252 } 253 return PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS ; 254 } 255 256 // Filter the list of possible AIX Printers and remove header lines 257 // and extra lines which have been added for remote printers. 258 // 'protected' because this method is also used from PrintServiceLookupProvider. 259 protected static String[] filterPrinterNamesAIX(String[] posPrinters) { 260 ArrayList<String> printers = new ArrayList<>(); 261 String [] splitPart; 262 263 for(int i = 0; i < posPrinters.length; i++) { 264 // Remove the header lines 265 if (posPrinters[i].startsWith("---") || 266 posPrinters[i].startsWith("Queue") || 267 posPrinters[i].isEmpty()) continue; 268 269 // Check if there is a ":" in the end of the first colomn. 270 // This means that it is not a valid printer definition. 271 splitPart = posPrinters[i].split(" "); 272 if(splitPart.length >= 1 && !splitPart[0].trim().endsWith(":")) { 273 printers.add(posPrinters[i]); 274 } 275 } 276 277 return printers.toArray(new String[printers.size()]); 278 } 279 280 private PrinterIsAcceptingJobs getPrinterIsAcceptingJobsAIX() { 281 // On AIX there should not be a blank after '-a'. 282 String command = "/usr/bin/lpstat -a" + printer; 283 String[] results= PrintServiceLookupProvider.execCmd(command); 284 285 // Remove headers and bogus entries added by remote printers. 286 results = filterPrinterNamesAIX(results); 287 288 if (results != null && results.length > 0) { 289 for (int i = 0; i < results.length; i++) { 290 if (results[i].contains("READY") || 291 results[i].contains("RUNNING")) { 292 return PrinterIsAcceptingJobs.ACCEPTING_JOBS; 293 } 294 } 295 } 296 297 return PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS; 298 299 } 300 301 private PrinterIsAcceptingJobs getPrinterIsAcceptingJobs() { 302 if (PrintServiceLookupProvider.isBSD()) { 303 return getPrinterIsAcceptingJobsBSD(); 304 } else if (PrintServiceLookupProvider.isAIX()) { 305 return getPrinterIsAcceptingJobsAIX(); 306 } else { 307 return PrinterIsAcceptingJobs.ACCEPTING_JOBS; 308 } 309 } 310 311 private PrinterState getPrinterState() { 312 if (isInvalid) { 313 return PrinterState.STOPPED; 314 } else { 315 return null; 316 } 317 } 318 319 private PrinterStateReasons getPrinterStateReasons() { 320 if (isInvalid) { 321 PrinterStateReasons psr = new PrinterStateReasons(); 322 psr.put(PrinterStateReason.SHUTDOWN, Severity.ERROR); 323 return psr; 324 } else { 325 return null; 326 } 327 } 328 329 private QueuedJobCount getQueuedJobCountBSD() { 330 if (PrintServiceLookupProvider.cmdIndex == 331 PrintServiceLookupProvider.UNINITIALIZED) { 332 333 PrintServiceLookupProvider.cmdIndex = 334 PrintServiceLookupProvider.getBSDCommandIndex(); 335 } 336 337 int qlen = 0; 338 String command = "/usr/sbin/lpc status " + printer 339 + lpcQueueCom[PrintServiceLookupProvider.cmdIndex]; 340 String[] results = PrintServiceLookupProvider.execCmd(command); 341 342 if (results != null && results.length > 0) { 343 String queued; 344 if (PrintServiceLookupProvider.cmdIndex == 345 PrintServiceLookupProvider.BSD_LPD_NG) { 346 queued = results[0]; 347 } else { 348 queued = results[3].trim(); 349 if (queued.startsWith("no")) { 350 return new QueuedJobCount(0); 351 } else { 352 queued = queued.substring(0, queued.indexOf(' ')); 353 } 354 } 355 356 try { 357 qlen = Integer.parseInt(queued); 358 } catch (NumberFormatException e) { 359 } 360 } 361 362 return new QueuedJobCount(qlen); 363 } 364 365 private QueuedJobCount getQueuedJobCountAIX() { 366 // On AIX there should not be a blank after '-a'. 367 String command = "/usr/bin/lpstat -a" + printer; 368 String[] results= PrintServiceLookupProvider.execCmd(command); 369 370 // Remove headers and bogus entries added by remote printers. 371 results = filterPrinterNamesAIX(results); 372 373 int qlen = 0; 374 if (results != null && results.length > 0){ 375 for (int i = 0; i < results.length; i++) { 376 if (results[i].contains("QUEUED")){ 377 qlen ++; 378 } 379 } 380 } 381 return new QueuedJobCount(qlen); 382 } 383 384 private QueuedJobCount getQueuedJobCount() { 385 if (PrintServiceLookupProvider.isBSD()) { 386 return getQueuedJobCountBSD(); 387 } else if (PrintServiceLookupProvider.isAIX()) { 388 return getQueuedJobCountAIX(); 389 } else { 390 return new QueuedJobCount(0); 391 } 392 } 393 394 private PrintServiceAttributeSet getBSDServiceAttributes() { 395 PrintServiceAttributeSet attrs = new HashPrintServiceAttributeSet(); 396 attrs.add(getQueuedJobCountBSD()); 397 attrs.add(getPrinterIsAcceptingJobsBSD()); 398 return attrs; 399 } 400 401 private PrintServiceAttributeSet getAIXServiceAttributes() { 402 PrintServiceAttributeSet attrs = new HashPrintServiceAttributeSet(); 403 attrs.add(getQueuedJobCountAIX()); 404 attrs.add(getPrinterIsAcceptingJobsAIX()); 405 return attrs; 406 } 407 408 private boolean isSupportedCopies(Copies copies) { 409 int numCopies = copies.getValue(); 410 return (numCopies > 0 && numCopies < MAXCOPIES); 411 } 412 413 private boolean isSupportedMedia(MediaSizeName msn) { 414 for (int i=0; i<mediaSizes.length; i++) { 415 if (msn.equals(mediaSizes[i])) { 416 return true; 417 } 418 } 419 return false; 420 } 421 422 public DocPrintJob createPrintJob() { 423 SecurityManager security = System.getSecurityManager(); 424 if (security != null) { 425 security.checkPrintJobAccess(); 426 } 427 return new UnixPrintJob(this); 428 } 429 430 private PrintServiceAttributeSet getDynamicAttributes() { 431 if (PrintServiceLookupProvider.isAIX()) { 432 return getAIXServiceAttributes(); 433 } else { 434 return getBSDServiceAttributes(); 435 } 436 } 437 438 public PrintServiceAttributeSet getUpdatedAttributes() { 439 PrintServiceAttributeSet currSet = getDynamicAttributes(); 440 if (lastSet == null) { 441 lastSet = currSet; 442 return AttributeSetUtilities.unmodifiableView(currSet); 443 } else { 444 PrintServiceAttributeSet updates = 445 new HashPrintServiceAttributeSet(); 446 Attribute []attrs = currSet.toArray(); 447 Attribute attr; 448 for (int i=0; i<attrs.length; i++) { 449 attr = attrs[i]; 450 if (!lastSet.containsValue(attr)) { 451 updates.add(attr); 452 } 453 } 454 lastSet = currSet; 455 return AttributeSetUtilities.unmodifiableView(updates); 456 } 457 } 458 459 public void wakeNotifier() { 460 synchronized (this) { 461 if (notifier != null) { 462 notifier.wake(); 463 } 464 } 465 } 466 467 public void addPrintServiceAttributeListener( 468 PrintServiceAttributeListener listener) { 469 synchronized (this) { 470 if (listener == null) { 471 return; 472 } 473 if (notifier == null) { 474 notifier = new ServiceNotifier(this); 475 } 476 notifier.addListener(listener); 477 } 478 } 479 480 public void removePrintServiceAttributeListener( 481 PrintServiceAttributeListener listener) { 482 synchronized (this) { 483 if (listener == null || notifier == null ) { 484 return; 485 } 486 notifier.removeListener(listener); 487 if (notifier.isEmpty()) { 488 notifier.stopNotifier(); 489 notifier = null; 490 } 491 } 492 } 493 494 @SuppressWarnings("unchecked") 495 public <T extends PrintServiceAttribute> 496 T getAttribute(Class<T> category) 497 { 498 if (category == null) { 499 throw new NullPointerException("category"); 500 } 501 if (!(PrintServiceAttribute.class.isAssignableFrom(category))) { 502 throw new IllegalArgumentException("Not a PrintServiceAttribute"); 503 } 504 505 if (category == PrinterName.class) { 506 return (T)getPrinterName(); 507 } else if (category == PrinterState.class) { 508 return (T)getPrinterState(); 509 } else if (category == PrinterStateReasons.class) { 510 return (T)getPrinterStateReasons(); 511 } else if (category == QueuedJobCount.class) { 512 return (T)getQueuedJobCount(); 513 } else if (category == PrinterIsAcceptingJobs.class) { 514 return (T)getPrinterIsAcceptingJobs(); 515 } else { 516 return null; 517 } 518 } 519 520 public PrintServiceAttributeSet getAttributes() { 521 PrintServiceAttributeSet attrs = new HashPrintServiceAttributeSet(); 522 attrs.add(getPrinterName()); 523 attrs.add(getPrinterIsAcceptingJobs()); 524 PrinterState prnState = getPrinterState(); 525 if (prnState != null) { 526 attrs.add(prnState); 527 } 528 PrinterStateReasons prnStateReasons = getPrinterStateReasons(); 529 if (prnStateReasons != null) { 530 attrs.add(prnStateReasons); 531 } 532 attrs.add(getQueuedJobCount()); 533 return AttributeSetUtilities.unmodifiableView(attrs); 534 } 535 536 private void initSupportedDocFlavors() { 537 String hostEnc = DocFlavor.hostEncoding.toLowerCase(Locale.ENGLISH); 538 if (!hostEnc.equals("utf-8") && !hostEnc.equals("utf-16") && 539 !hostEnc.equals("utf-16be") && !hostEnc.equals("utf-16le") && 540 !hostEnc.equals("us-ascii")) { 541 542 int len = supportedDocFlavorsInit.length; 543 DocFlavor[] flavors = 544 new DocFlavor[len + supportedHostDocFlavors.length]; 545 // copy host encoding flavors 546 System.arraycopy(supportedHostDocFlavors, 0, flavors, 547 len, supportedHostDocFlavors.length); 548 System.arraycopy(supportedDocFlavorsInit, 0, flavors, 0, len); 549 550 supportedDocFlavors = flavors; 551 } else { 552 supportedDocFlavors = supportedDocFlavorsInit; 553 } 554 } 555 556 public DocFlavor[] getSupportedDocFlavors() { 557 if (supportedDocFlavors == null) { 558 initSupportedDocFlavors(); 559 } 560 int len = supportedDocFlavors.length; 561 DocFlavor[] flavors = new DocFlavor[len]; 562 System.arraycopy(supportedDocFlavors, 0, flavors, 0, len); 563 564 return flavors; 565 } 566 567 public boolean isDocFlavorSupported(DocFlavor flavor) { 568 if (supportedDocFlavors == null) { 569 initSupportedDocFlavors(); 570 } 571 for (int f=0; f<supportedDocFlavors.length; f++) { 572 if (flavor.equals(supportedDocFlavors[f])) { 573 return true; 574 } 575 } 576 return false; 577 } 578 579 public Class<?>[] getSupportedAttributeCategories() { 580 ArrayList<Class<?>> categList = new ArrayList<>(otherAttrCats.length); 581 for (Class<?> c : otherAttrCats) { 582 categList.add(c); 583 } 584 if (GraphicsEnvironment.isHeadless() == false) { 585 categList.add(DialogOwner.class); 586 categList.add(DialogTypeSelection.class); 587 } 588 return categList.toArray(new Class<?>[categList.size()]); 589 } 590 591 public boolean 592 isAttributeCategorySupported(Class<? extends Attribute> category) 593 { 594 if (category == null) { 595 throw new NullPointerException("null category"); 596 } 597 if (!(Attribute.class.isAssignableFrom(category))) { 598 throw new IllegalArgumentException(category + 599 " is not an Attribute"); 600 } 601 602 for (int i=0;i<otherAttrCats.length;i++) { 603 if (category == otherAttrCats[i]) { 604 return true; 605 } 606 } 607 return false; 608 } 609 610 /* return defaults for all attributes for which there is a default 611 * value 612 */ 613 public Object 614 getDefaultAttributeValue(Class<? extends Attribute> category) 615 { 616 if (category == null) { 617 throw new NullPointerException("null category"); 618 } 619 if (!Attribute.class.isAssignableFrom(category)) { 620 throw new IllegalArgumentException(category + 621 " is not an Attribute"); 622 } 623 624 if (!isAttributeCategorySupported(category)) { 625 return null; 626 } 627 628 if (category == Copies.class) { 629 return new Copies(1); 630 } else if (category == Chromaticity.class) { 631 return Chromaticity.COLOR; 632 } else if (category == Destination.class) { 633 try { 634 return new Destination((new File("out.ps")).toURI()); 635 } catch (SecurityException se) { 636 try { 637 return new Destination(new URI("file:out.ps")); 638 } catch (URISyntaxException e) { 639 return null; 640 } 641 } 642 } else if (category == Fidelity.class) { 643 return Fidelity.FIDELITY_FALSE; 644 } else if (category == JobName.class) { 645 return new JobName("Java Printing", null); 646 } else if (category == JobSheets.class) { 647 return JobSheets.STANDARD; 648 } else if (category == Media.class) { 649 String defaultCountry = Locale.getDefault().getCountry(); 650 if (defaultCountry != null && 651 (defaultCountry.isEmpty() || 652 defaultCountry.equals(Locale.US.getCountry()) || 653 defaultCountry.equals(Locale.CANADA.getCountry()))) { 654 return MediaSizeName.NA_LETTER; 655 } else { 656 return MediaSizeName.ISO_A4; 657 } 658 } else if (category == MediaPrintableArea.class) { 659 String defaultCountry = Locale.getDefault().getCountry(); 660 float iw, ih; 661 if (defaultCountry != null && 662 (defaultCountry.isEmpty() || 663 defaultCountry.equals(Locale.US.getCountry()) || 664 defaultCountry.equals(Locale.CANADA.getCountry()))) { 665 iw = MediaSize.NA.LETTER.getX(Size2DSyntax.INCH) - 0.5f; 666 ih = MediaSize.NA.LETTER.getY(Size2DSyntax.INCH) - 0.5f; 667 } else { 668 iw = MediaSize.ISO.A4.getX(Size2DSyntax.INCH) - 0.5f; 669 ih = MediaSize.ISO.A4.getY(Size2DSyntax.INCH) - 0.5f; 670 } 671 return new MediaPrintableArea(0.25f, 0.25f, iw, ih, 672 MediaPrintableArea.INCH); 673 } else if (category == OrientationRequested.class) { 674 return OrientationRequested.PORTRAIT; 675 } else if (category == PageRanges.class) { 676 return new PageRanges(1, Integer.MAX_VALUE); 677 } else if (category == RequestingUserName.class) { 678 String userName = ""; 679 try { 680 userName = System.getProperty("user.name", ""); 681 } catch (SecurityException se) { 682 } 683 return new RequestingUserName(userName, null); 684 } else if (category == SheetCollate.class) { 685 return SheetCollate.UNCOLLATED; 686 } else if (category == Sides.class) { 687 return Sides.ONE_SIDED; 688 } else 689 return null; 690 } 691 692 693 private boolean isAutoSense(DocFlavor flavor) { 694 if (flavor.equals(DocFlavor.BYTE_ARRAY.AUTOSENSE) || 695 flavor.equals(DocFlavor.INPUT_STREAM.AUTOSENSE) || 696 flavor.equals(DocFlavor.URL.AUTOSENSE)) { 697 return true; 698 } 699 else { 700 return false; 701 } 702 } 703 704 public Object 705 getSupportedAttributeValues(Class<? extends Attribute> category, 706 DocFlavor flavor, 707 AttributeSet attributes) 708 { 709 710 if (category == null) { 711 throw new NullPointerException("null category"); 712 } 713 if (!Attribute.class.isAssignableFrom(category)) { 714 throw new IllegalArgumentException(category + 715 " does not implement Attribute"); 716 } 717 if (flavor != null) { 718 if (!isDocFlavorSupported(flavor)) { 719 throw new IllegalArgumentException(flavor + 720 " is an unsupported flavor"); 721 } else if (isAutoSense(flavor)) { 722 return null; 723 } 724 } 725 726 if (!isAttributeCategorySupported(category)) { 727 return null; 728 } 729 730 if (category == Chromaticity.class) { 731 if (flavor == null || isServiceFormattedFlavor(flavor)) { 732 Chromaticity[]arr = new Chromaticity[1]; 733 arr[0] = Chromaticity.COLOR; 734 return (arr); 735 } else { 736 return null; 737 } 738 } else if (category == Destination.class) { 739 try { 740 return new Destination((new File("out.ps")).toURI()); 741 } catch (SecurityException se) { 742 try { 743 return new Destination(new URI("file:out.ps")); 744 } catch (URISyntaxException e) { 745 return null; 746 } 747 } 748 } else if (category == JobName.class) { 749 return new JobName("Java Printing", null); 750 } else if (category == JobSheets.class) { 751 JobSheets[] arr = new JobSheets[2]; 752 arr[0] = JobSheets.NONE; 753 arr[1] = JobSheets.STANDARD; 754 return arr; 755 } else if (category == RequestingUserName.class) { 756 String userName = ""; 757 try { 758 userName = System.getProperty("user.name", ""); 759 } catch (SecurityException se) { 760 } 761 return new RequestingUserName(userName, null); 762 } else if (category == OrientationRequested.class) { 763 if (flavor == null || isServiceFormattedFlavor(flavor)) { 764 OrientationRequested []arr = new OrientationRequested[3]; 765 arr[0] = OrientationRequested.PORTRAIT; 766 arr[1] = OrientationRequested.LANDSCAPE; 767 arr[2] = OrientationRequested.REVERSE_LANDSCAPE; 768 return arr; 769 } else { 770 return null; 771 } 772 } else if ((category == Copies.class) || 773 (category == CopiesSupported.class)) { 774 if (flavor == null || 775 !(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) || 776 flavor.equals(DocFlavor.URL.POSTSCRIPT) || 777 flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) { 778 return new CopiesSupported(1, MAXCOPIES); 779 } else { 780 return null; 781 } 782 } else if (category == Media.class) { 783 Media []arr = new Media[mediaSizes.length]; 784 System.arraycopy(mediaSizes, 0, arr, 0, mediaSizes.length); 785 return arr; 786 } else if (category == Fidelity.class) { 787 Fidelity []arr = new Fidelity[2]; 788 arr[0] = Fidelity.FIDELITY_FALSE; 789 arr[1] = Fidelity.FIDELITY_TRUE; 790 return arr; 791 } else if (category == MediaPrintableArea.class) { 792 /* The code below implements the behaviour that if no Media or 793 * MediaSize attribute is specified, return an array of 794 * MediaPrintableArea, one for each supported Media. 795 * If a MediaSize is specified, return a MPA consistent for that, 796 * and if a Media is specified locate its MediaSize and return 797 * its MPA, and if none is found, return an MPA for the default 798 * Media for this service. 799 */ 800 if (attributes == null) { 801 return getAllPrintableAreas(); 802 } 803 MediaSize mediaSize = (MediaSize)attributes.get(MediaSize.class); 804 Media media = (Media)attributes.get(Media.class); 805 MediaPrintableArea []arr = new MediaPrintableArea[1]; 806 if (mediaSize == null) { 807 if (media instanceof MediaSizeName) { 808 MediaSizeName msn = (MediaSizeName)media; 809 mediaSize = MediaSize.getMediaSizeForName(msn); 810 if (mediaSize == null) { 811 /* try to get a size from the default media */ 812 media = (Media)getDefaultAttributeValue(Media.class); 813 if (media instanceof MediaSizeName) { 814 msn = (MediaSizeName)media; 815 mediaSize = MediaSize.getMediaSizeForName(msn); 816 } 817 if (mediaSize == null) { 818 /* shouldn't happen, return a default */ 819 arr[0] = new MediaPrintableArea(0.25f, 0.25f, 820 8f, 10.5f, 821 MediaSize.INCH); 822 return arr; 823 } 824 } 825 } else { 826 return getAllPrintableAreas(); 827 } 828 } 829 /* If reach here MediaSize is non-null */ 830 assert mediaSize != null; 831 arr[0] = new MediaPrintableArea(0.25f, 0.25f, 832 mediaSize.getX(MediaSize.INCH)-0.5f, 833 mediaSize.getY(MediaSize.INCH)-0.5f, 834 MediaSize.INCH); 835 return arr; 836 } else if (category == PageRanges.class) { 837 if (flavor == null || 838 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 839 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 840 PageRanges []arr = new PageRanges[1]; 841 arr[0] = new PageRanges(1, Integer.MAX_VALUE); 842 return arr; 843 } else { 844 return null; 845 } 846 } else if (category == SheetCollate.class) { 847 if (flavor == null || 848 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 849 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 850 SheetCollate []arr = new SheetCollate[2]; 851 arr[0] = SheetCollate.UNCOLLATED; 852 arr[1] = SheetCollate.COLLATED; 853 return arr; 854 } else { 855 return null; 856 } 857 } else if (category == Sides.class) { 858 if (flavor == null || 859 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 860 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 861 Sides []arr = new Sides[3]; 862 arr[0] = Sides.ONE_SIDED; 863 arr[1] = Sides.TWO_SIDED_LONG_EDGE; 864 arr[2] = Sides.TWO_SIDED_SHORT_EDGE; 865 return arr; 866 } else { 867 return null; 868 } 869 } else { 870 return null; 871 } 872 } 873 874 private static MediaPrintableArea[] mpas = null; 875 private MediaPrintableArea[] getAllPrintableAreas() { 876 877 if (mpas == null) { 878 Media[] media = (Media[])getSupportedAttributeValues(Media.class, 879 null, null); 880 mpas = new MediaPrintableArea[media.length]; 881 for (int i=0; i< mpas.length; i++) { 882 if (media[i] instanceof MediaSizeName) { 883 MediaSizeName msn = (MediaSizeName)media[i]; 884 MediaSize mediaSize = MediaSize.getMediaSizeForName(msn); 885 if (mediaSize == null) { 886 mpas[i] = (MediaPrintableArea) 887 getDefaultAttributeValue(MediaPrintableArea.class); 888 } else { 889 mpas[i] = new MediaPrintableArea(0.25f, 0.25f, 890 mediaSize.getX(MediaSize.INCH)-0.5f, 891 mediaSize.getY(MediaSize.INCH)-0.5f, 892 MediaSize.INCH); 893 } 894 } 895 } 896 } 897 MediaPrintableArea[] mpasCopy = new MediaPrintableArea[mpas.length]; 898 System.arraycopy(mpas, 0, mpasCopy, 0, mpas.length); 899 return mpasCopy; 900 } 901 902 /* Is this one of the flavors that this service explicitly 903 * generates postscript for, and so can control how it is rendered? 904 */ 905 private boolean isServiceFormattedFlavor(DocFlavor flavor) { 906 return 907 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 908 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) || 909 flavor.equals(DocFlavor.BYTE_ARRAY.GIF) || 910 flavor.equals(DocFlavor.INPUT_STREAM.GIF) || 911 flavor.equals(DocFlavor.URL.GIF) || 912 flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) || 913 flavor.equals(DocFlavor.INPUT_STREAM.JPEG) || 914 flavor.equals(DocFlavor.URL.JPEG) || 915 flavor.equals(DocFlavor.BYTE_ARRAY.PNG) || 916 flavor.equals(DocFlavor.INPUT_STREAM.PNG) || 917 flavor.equals(DocFlavor.URL.PNG); 918 } 919 920 public boolean isAttributeValueSupported(Attribute attr, 921 DocFlavor flavor, 922 AttributeSet attributes) { 923 if (attr == null) { 924 throw new NullPointerException("null attribute"); 925 } 926 if (flavor != null) { 927 if (!isDocFlavorSupported(flavor)) { 928 throw new IllegalArgumentException(flavor + 929 " is an unsupported flavor"); 930 } else if (isAutoSense(flavor)) { 931 return false; 932 } 933 } 934 Class<? extends Attribute> category = attr.getCategory(); 935 if (!isAttributeCategorySupported(category)) { 936 return false; 937 } 938 else if (attr.getCategory() == Chromaticity.class) { 939 if (flavor == null || isServiceFormattedFlavor(flavor)) { 940 return attr == Chromaticity.COLOR; 941 } else { 942 return false; 943 } 944 } 945 else if (attr.getCategory() == Copies.class) { 946 return (flavor == null || 947 !(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) || 948 flavor.equals(DocFlavor.URL.POSTSCRIPT) || 949 flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) && 950 isSupportedCopies((Copies)attr); 951 } else if (attr.getCategory() == Destination.class) { 952 URI uri = ((Destination)attr).getURI(); 953 if ("file".equals(uri.getScheme()) && 954 !uri.getSchemeSpecificPart().isEmpty()) { 955 return true; 956 } else { 957 return false; 958 } 959 } else if (attr.getCategory() == Media.class) { 960 if (attr instanceof MediaSizeName) { 961 return isSupportedMedia((MediaSizeName)attr); 962 } else { 963 return false; 964 } 965 } else if (attr.getCategory() == OrientationRequested.class) { 966 if (attr == OrientationRequested.REVERSE_PORTRAIT || 967 (flavor != null) && 968 !isServiceFormattedFlavor(flavor)) { 969 return false; 970 } 971 } else if (attr.getCategory() == PageRanges.class) { 972 if (flavor != null && 973 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 974 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) { 975 return false; 976 } 977 } else if (attr.getCategory() == SheetCollate.class) { 978 if (flavor != null && 979 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 980 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) { 981 return false; 982 } 983 } else if (attr.getCategory() == Sides.class) { 984 if (flavor != null && 985 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 986 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) { 987 return false; 988 } 989 } else if (attr.getCategory() == DialogOwner.class) { 990 DialogOwner owner = (DialogOwner)attr; 991 // ID not supported on any dialog type on Unix platforms. 992 if (DialogOwnerAccessor.getID(owner) != 0) { 993 return false; 994 } 995 // UnixPrintService is not used on Mac, so this is 996 // always some Unix system that does not have CUPS/IPP 997 // Which means we always use a Swing dialog and we need 998 // only check if alwaysOnTop is supported by the toolkit. 999 if (owner.getOwner() != null) { 1000 return true; 1001 } else { 1002 return Toolkit.getDefaultToolkit().isAlwaysOnTopSupported(); 1003 } 1004 } else if (attr.getCategory() == DialogTypeSelection.class) { 1005 DialogTypeSelection dts = (DialogTypeSelection)attr; 1006 return dts == DialogTypeSelection.COMMON; 1007 } 1008 return true; 1009 } 1010 1011 public AttributeSet getUnsupportedAttributes(DocFlavor flavor, 1012 AttributeSet attributes) { 1013 1014 if (flavor != null && !isDocFlavorSupported(flavor)) { 1015 throw new IllegalArgumentException("flavor " + flavor + 1016 "is not supported"); 1017 } 1018 1019 if (attributes == null) { 1020 return null; 1021 } 1022 1023 Attribute attr; 1024 AttributeSet unsupp = new HashAttributeSet(); 1025 Attribute []attrs = attributes.toArray(); 1026 for (int i=0; i<attrs.length; i++) { 1027 try { 1028 attr = attrs[i]; 1029 if (!isAttributeCategorySupported(attr.getCategory())) { 1030 unsupp.add(attr); 1031 } else if (!isAttributeValueSupported(attr, flavor, 1032 attributes)) { 1033 unsupp.add(attr); 1034 } 1035 } catch (ClassCastException e) { 1036 } 1037 } 1038 if (unsupp.isEmpty()) { 1039 return null; 1040 } else { 1041 return unsupp; 1042 } 1043 } 1044 1045 public ServiceUIFactory getServiceUIFactory() { 1046 return null; 1047 } 1048 1049 public String toString() { 1050 return "Unix Printer : " + getName(); 1051 } 1052 1053 public boolean equals(Object obj) { 1054 return (obj == this || 1055 (obj instanceof UnixPrintService && 1056 ((UnixPrintService)obj).getName().equals(getName()))); 1057 } 1058 1059 public int hashCode() { 1060 return this.getClass().hashCode()+getName().hashCode(); 1061 } 1062 1063 public boolean usesClass(Class<?> c) { 1064 return (c == sun.print.PSPrinterJob.class); 1065 } 1066 1067 }