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