0001: /*
0002: * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package sun.print;
0027:
0028: import javax.print.attribute.*;
0029: import javax.print.attribute.standard.*;
0030: import javax.print.DocFlavor;
0031: import javax.print.DocPrintJob;
0032: import javax.print.PrintService;
0033: import javax.print.ServiceUIFactory;
0034: import java.util.ArrayList;
0035: import java.util.HashMap;
0036: import java.util.Locale;
0037: import java.util.Date;
0038: import java.util.Arrays;
0039: import java.security.AccessController;
0040: import java.security.PrivilegedActionException;
0041: import java.security.PrivilegedExceptionAction;
0042: import javax.print.event.PrintServiceAttributeListener;
0043:
0044: import java.net.URI;
0045: import java.net.URISyntaxException;
0046: import java.net.URL;
0047: import java.net.HttpURLConnection;
0048: import java.io.File;
0049: import java.io.InputStream;
0050: import java.io.OutputStream;
0051: import java.io.OutputStreamWriter;
0052: import java.io.DataInputStream;
0053: import java.io.ByteArrayOutputStream;
0054: import java.io.ByteArrayInputStream;
0055: import java.io.BufferedReader;
0056: import java.io.InputStreamReader;
0057: import java.nio.charset.Charset;
0058:
0059: import java.util.Iterator;
0060:
0061: public class IPPPrintService implements PrintService,
0062: SunPrinterJobService {
0063:
0064: public static boolean debugPrint = false;
0065: private static String debugPrefix = "IPPPrintService>> ";
0066:
0067: protected static void debug_println(String str) {
0068: if (debugPrint) {
0069: System.out.println(str);
0070: }
0071: }
0072:
0073: private String printer;
0074: private URI myURI;
0075: private URL myURL;
0076: transient private ServiceNotifier notifier = null;
0077:
0078: private static int MAXCOPIES = 1000;
0079: private static short MAX_ATTRIBUTE_LENGTH = 255;
0080:
0081: private CUPSPrinter cps;
0082: private HttpURLConnection urlConnection = null;
0083: private DocFlavor[] supportedDocFlavors;
0084: private Class[] supportedCats;
0085: private MediaTray[] mediaTrays;
0086: private MediaSizeName[] mediaSizeNames;
0087: private CustomMediaSizeName[] customMediaSizeNames;
0088: private int defaultMediaIndex;
0089: private boolean isCupsPrinter;
0090: private boolean init;
0091: private Boolean isPS;
0092: private HashMap getAttMap;
0093: private boolean pngImagesAdded = false;
0094: private boolean gifImagesAdded = false;
0095: private boolean jpgImagesAdded = false;
0096:
0097: /**
0098: * IPP Status Codes
0099: */
0100: private static final byte STATUSCODE_SUCCESS = 0x00;
0101:
0102: /**
0103: * IPP Group Tags. Each tag is used once before the first attribute
0104: * of that group.
0105: */
0106: // operation attributes group
0107: private static final byte GRPTAG_OP_ATTRIBUTES = 0x01;
0108: // job attributes group
0109: private static final byte GRPTAG_JOB_ATTRIBUTES = 0x02;
0110: // printer attributes group
0111: private static final byte GRPTAG_PRINTER_ATTRIBUTES = 0x04;
0112: // used as the last tag in an IPP message.
0113: private static final byte GRPTAG_END_ATTRIBUTES = 0x03;
0114:
0115: /**
0116: * IPP Operation codes
0117: */
0118: // gets the attributes for a printer
0119: public static final String OP_GET_ATTRIBUTES = "000B";
0120: // gets the default printer
0121: public static final String OP_CUPS_GET_DEFAULT = "4001";
0122: // gets the list of printers
0123: public static final String OP_CUPS_GET_PRINTERS = "4002";
0124:
0125: /**
0126: * List of all PrintRequestAttributes. This is used
0127: * for looping through all the IPP attribute name.
0128: */
0129: private static Object[] printReqAttribDefault = {
0130: Chromaticity.COLOR,
0131: new Copies(1),
0132: Fidelity.FIDELITY_FALSE,
0133: Finishings.NONE,
0134: //new JobHoldUntil(new Date()),
0135: //new JobImpressions(0),
0136: //JobImpressions,
0137: //JobKOctets,
0138: //JobMediaSheets,
0139: new JobName("", Locale.getDefault()),
0140: //JobPriority,
0141: JobSheets.NONE,
0142: (Media) MediaSizeName.NA_LETTER,
0143: //MediaPrintableArea.class, // not an IPP attribute
0144: //MultipleDocumentHandling.SINGLE_DOCUMENT,
0145: new NumberUp(1), OrientationRequested.PORTRAIT,
0146: new PageRanges(1),
0147: //PresentationDirection,
0148: // CUPS does not supply printer-resolution attribute
0149: //new PrinterResolution(300, 300, PrinterResolution.DPI),
0150: //PrintQuality.NORMAL,
0151: new RequestingUserName("", Locale.getDefault()),
0152: //SheetCollate.UNCOLLATED, //CUPS has no sheet collate?
0153: Sides.ONE_SIDED, };
0154:
0155: /**
0156: * List of all PrintServiceAttributes. This is used
0157: * for looping through all the IPP attribute name.
0158: */
0159: private static Object[][] serviceAttributes = {
0160: { ColorSupported.class, "color-supported" },
0161: { PagesPerMinute.class, "pages-per-minute" },
0162: { PagesPerMinuteColor.class, "pages-per-minute-color" },
0163: { PDLOverrideSupported.class, "pdl-override-supported" },
0164: { PrinterInfo.class, "printer-info" },
0165: { PrinterIsAcceptingJobs.class, "printer-is-accepting-jobs" },
0166: { PrinterLocation.class, "printer-location" },
0167: { PrinterMakeAndModel.class, "printer-make-and-model" },
0168: { PrinterMessageFromOperator.class,
0169: "printer-message-from-operator" },
0170: { PrinterMoreInfo.class, "printer-more-info" },
0171: { PrinterMoreInfoManufacturer.class,
0172: "printer-more-info-manufacturer" },
0173: { PrinterName.class, "printer-name" },
0174: { PrinterState.class, "printer-state" },
0175: { PrinterStateReasons.class, "printer-state-reasons" },
0176: { PrinterURI.class, "printer-uri" },
0177: { QueuedJobCount.class, "queued-job-count" } };
0178:
0179: /**
0180: * List of DocFlavors, grouped based on matching mime-type.
0181: * NOTE: For any change in the predefined DocFlavors, it must be reflected
0182: * here also.
0183: */
0184: // PDF DocFlavors
0185: private static DocFlavor[] appPDF = { DocFlavor.BYTE_ARRAY.PDF,
0186: DocFlavor.INPUT_STREAM.PDF, DocFlavor.URL.PDF };
0187:
0188: // Postscript DocFlavors
0189: private static DocFlavor[] appPostScript = {
0190: DocFlavor.BYTE_ARRAY.POSTSCRIPT,
0191: DocFlavor.INPUT_STREAM.POSTSCRIPT, DocFlavor.URL.POSTSCRIPT };
0192:
0193: // Autosense DocFlavors
0194: private static DocFlavor[] appOctetStream = {
0195: DocFlavor.BYTE_ARRAY.AUTOSENSE,
0196: DocFlavor.INPUT_STREAM.AUTOSENSE, DocFlavor.URL.AUTOSENSE };
0197:
0198: // Text DocFlavors
0199: private static DocFlavor[] textPlain = {
0200: DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_8,
0201: DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16,
0202: DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16BE,
0203: DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16LE,
0204: DocFlavor.BYTE_ARRAY.TEXT_PLAIN_US_ASCII,
0205: DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_8,
0206: DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16,
0207: DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16BE,
0208: DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16LE,
0209: DocFlavor.INPUT_STREAM.TEXT_PLAIN_US_ASCII,
0210: DocFlavor.URL.TEXT_PLAIN_UTF_8,
0211: DocFlavor.URL.TEXT_PLAIN_UTF_16,
0212: DocFlavor.URL.TEXT_PLAIN_UTF_16BE,
0213: DocFlavor.URL.TEXT_PLAIN_UTF_16LE,
0214: DocFlavor.URL.TEXT_PLAIN_US_ASCII,
0215: DocFlavor.CHAR_ARRAY.TEXT_PLAIN,
0216: DocFlavor.STRING.TEXT_PLAIN, DocFlavor.READER.TEXT_PLAIN };
0217:
0218: private static DocFlavor[] textPlainHost = {
0219: DocFlavor.BYTE_ARRAY.TEXT_PLAIN_HOST,
0220: DocFlavor.INPUT_STREAM.TEXT_PLAIN_HOST,
0221: DocFlavor.URL.TEXT_PLAIN_HOST };
0222:
0223: // JPG DocFlavors
0224: private static DocFlavor[] imageJPG = { DocFlavor.BYTE_ARRAY.JPEG,
0225: DocFlavor.INPUT_STREAM.JPEG, DocFlavor.URL.JPEG };
0226:
0227: // GIF DocFlavors
0228: private static DocFlavor[] imageGIF = { DocFlavor.BYTE_ARRAY.GIF,
0229: DocFlavor.INPUT_STREAM.GIF, DocFlavor.URL.GIF };
0230:
0231: // PNG DocFlavors
0232: private static DocFlavor[] imagePNG = { DocFlavor.BYTE_ARRAY.PNG,
0233: DocFlavor.INPUT_STREAM.PNG, DocFlavor.URL.PNG };
0234:
0235: // HTML DocFlavors
0236: private static DocFlavor[] textHtml = {
0237: DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_8,
0238: DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_16,
0239: DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_16BE,
0240: DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_16LE,
0241: DocFlavor.BYTE_ARRAY.TEXT_HTML_US_ASCII,
0242: DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_8,
0243: DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_16,
0244: DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_16BE,
0245: DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_16LE,
0246: DocFlavor.INPUT_STREAM.TEXT_HTML_US_ASCII,
0247: DocFlavor.URL.TEXT_HTML_UTF_8,
0248: DocFlavor.URL.TEXT_HTML_UTF_16,
0249: DocFlavor.URL.TEXT_HTML_UTF_16BE,
0250: DocFlavor.URL.TEXT_HTML_UTF_16LE,
0251: DocFlavor.URL.TEXT_HTML_US_ASCII,
0252: // These are not handled in UnixPrintJob so commenting these
0253: // for now.
0254: /*
0255: DocFlavor.CHAR_ARRAY.TEXT_HTML,
0256: DocFlavor.STRING.TEXT_HTML,
0257: DocFlavor.READER.TEXT_HTML,
0258: */
0259: };
0260:
0261: private static DocFlavor[] textHtmlHost = {
0262: DocFlavor.BYTE_ARRAY.TEXT_HTML_HOST,
0263: DocFlavor.INPUT_STREAM.TEXT_HTML_HOST,
0264: DocFlavor.URL.TEXT_HTML_HOST, };
0265:
0266: // PCL DocFlavors
0267: private static DocFlavor[] appPCL = { DocFlavor.BYTE_ARRAY.PCL,
0268: DocFlavor.INPUT_STREAM.PCL, DocFlavor.URL.PCL };
0269:
0270: // List of all DocFlavors, used in looping
0271: // through all supported mime-types
0272: private static Object[] allDocFlavors = { appPDF, appPostScript,
0273: appOctetStream, textPlain, imageJPG, imageGIF, imagePNG,
0274: textHtml, appPCL, };
0275:
0276: IPPPrintService(String name, URL url) {
0277: if ((name == null) || (url == null)) {
0278: throw new IllegalArgumentException(
0279: "null uri or printer name");
0280: }
0281: printer = name;
0282: supportedDocFlavors = null;
0283: supportedCats = null;
0284: mediaSizeNames = null;
0285: customMediaSizeNames = null;
0286: mediaTrays = null;
0287: myURL = url;
0288: cps = null;
0289: isCupsPrinter = false;
0290: init = false;
0291: defaultMediaIndex = -1;
0292:
0293: String host = myURL.getHost();
0294: if (host != null && host.equals(CUPSPrinter.getServer())) {
0295: isCupsPrinter = true;
0296: try {
0297: myURI = new URI("ipp://" + host + "/printers/"
0298: + printer);
0299: debug_println(debugPrefix + "IPPPrintService myURI : "
0300: + myURI);
0301: } catch (java.net.URISyntaxException e) {
0302: throw new IllegalArgumentException("invalid url");
0303: }
0304: }
0305: }
0306:
0307: /*
0308: * Initialize mediaSizeNames, mediaTrays and other attributes.
0309: * Media size/trays are initialized to non-null values, may be 0-length
0310: * array.
0311: * NOTE: Must be called from a synchronized block only.
0312: */
0313: private void initAttributes() {
0314: if (!init) {
0315: // init customMediaSizeNames
0316: customMediaSizeNames = new CustomMediaSizeName[0];
0317:
0318: if ((urlConnection = getIPPConnection(myURL)) == null) {
0319: mediaSizeNames = new MediaSizeName[0];
0320: mediaTrays = new MediaTray[0];
0321: debug_println("NULL urlConnection ");
0322: init = true;
0323: return;
0324: }
0325:
0326: // get all supported attributes through IPP
0327: opGetAttributes();
0328:
0329: if (isCupsPrinter) {
0330: // note, it is possible to query media in CUPS using IPP
0331: // right now we always get it from PPD.
0332: // maybe use "&& (usePPD)" later?
0333: // Another reason why we use PPD is because
0334: // IPP currently does not support it but PPD does.
0335:
0336: try {
0337: cps = new CUPSPrinter(printer);
0338: mediaSizeNames = cps.getMediaSizeNames();
0339: mediaTrays = cps.getMediaTrays();
0340: customMediaSizeNames = cps
0341: .getCustomMediaSizeNames();
0342: urlConnection.disconnect();
0343: init = true;
0344: return;
0345: } catch (Exception e) {
0346: IPPPrintService.debug_println(debugPrefix
0347: + " error creating CUPSPrinter");
0348: }
0349: }
0350:
0351: // use IPP to get all media,
0352: Media[] allMedia = (Media[]) getSupportedMedia();
0353: ArrayList sizeList = new ArrayList();
0354: ArrayList trayList = new ArrayList();
0355: for (int i = 0; i < allMedia.length; i++) {
0356: if (allMedia[i] instanceof MediaSizeName) {
0357: sizeList.add(allMedia[i]);
0358: } else if (allMedia[i] instanceof MediaTray) {
0359: trayList.add(allMedia[i]);
0360: }
0361: }
0362:
0363: if (sizeList != null) {
0364: mediaSizeNames = new MediaSizeName[sizeList.size()];
0365: mediaSizeNames = (MediaSizeName[]) sizeList
0366: .toArray(mediaSizeNames);
0367: }
0368: if (trayList != null) {
0369: mediaTrays = new MediaTray[trayList.size()];
0370: mediaTrays = (MediaTray[]) trayList.toArray(mediaTrays);
0371: }
0372: urlConnection.disconnect();
0373:
0374: init = true;
0375: }
0376: }
0377:
0378: public DocPrintJob createPrintJob() {
0379: SecurityManager security = System.getSecurityManager();
0380: if (security != null) {
0381: security.checkPrintJobAccess();
0382: }
0383: // REMIND: create IPPPrintJob
0384: return new UnixPrintJob(this );
0385: }
0386:
0387: public synchronized Object getSupportedAttributeValues(
0388: Class<? extends Attribute> category, DocFlavor flavor,
0389: AttributeSet attributes) {
0390: if (category == null) {
0391: throw new NullPointerException("null category");
0392: }
0393: if (!Attribute.class.isAssignableFrom(category)) {
0394: throw new IllegalArgumentException(category
0395: + " does not implement Attribute");
0396: }
0397: if (flavor != null) {
0398: if (!isDocFlavorSupported(flavor)) {
0399: throw new IllegalArgumentException(flavor
0400: + " is an unsupported flavor");
0401: } else if (isAutoSense(flavor)) {
0402: return null;
0403: }
0404:
0405: }
0406:
0407: if (!isAttributeCategorySupported(category)) {
0408: return null;
0409: }
0410:
0411: /* Test if the flavor is compatible with the attributes */
0412: if (!isDestinationSupported(flavor, attributes)) {
0413: return null;
0414: }
0415:
0416: initAttributes();
0417:
0418: /* Test if the flavor is compatible with the category */
0419: if ((category == Copies.class)
0420: || (category == CopiesSupported.class)) {
0421: CopiesSupported cs = new CopiesSupported(1, MAXCOPIES);
0422: AttributeClass attribClass = (getAttMap != null) ? (AttributeClass) getAttMap
0423: .get(cs.getName())
0424: : null;
0425: if (attribClass != null) {
0426: int[] range = attribClass.getIntRangeValue();
0427: cs = new CopiesSupported(range[0], range[1]);
0428: }
0429: return cs;
0430: } else if (category == Chromaticity.class) {
0431: if (flavor == null
0432: || flavor
0433: .equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE)
0434: || flavor
0435: .equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)
0436: || flavor.equals(DocFlavor.BYTE_ARRAY.GIF)
0437: || flavor.equals(DocFlavor.INPUT_STREAM.GIF)
0438: || flavor.equals(DocFlavor.URL.GIF)
0439: || flavor.equals(DocFlavor.BYTE_ARRAY.JPEG)
0440: || flavor.equals(DocFlavor.INPUT_STREAM.JPEG)
0441: || flavor.equals(DocFlavor.URL.JPEG)
0442: || flavor.equals(DocFlavor.BYTE_ARRAY.PNG)
0443: || flavor.equals(DocFlavor.INPUT_STREAM.PNG)
0444: || flavor.equals(DocFlavor.URL.PNG)) {
0445:
0446: Chromaticity[] arr = new Chromaticity[1];
0447: arr[0] = Chromaticity.COLOR;
0448: return (arr);
0449: } else {
0450: return null;
0451: }
0452: } else if (category == Destination.class) {
0453: if (flavor == null
0454: || flavor
0455: .equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE)
0456: || flavor
0457: .equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
0458: try {
0459: return new Destination((new File("out.ps")).toURI());
0460: } catch (SecurityException se) {
0461: try {
0462: return new Destination(new URI("file:out.ps"));
0463: } catch (URISyntaxException e) {
0464: return null;
0465: }
0466: }
0467: }
0468: return null;
0469: } else if (category == Fidelity.class) {
0470: Fidelity[] arr = new Fidelity[2];
0471: arr[0] = Fidelity.FIDELITY_FALSE;
0472: arr[1] = Fidelity.FIDELITY_TRUE;
0473: return arr;
0474: } else if (category == Finishings.class) {
0475: AttributeClass attribClass = (getAttMap != null) ? (AttributeClass) getAttMap
0476: .get("finishings-supported")
0477: : null;
0478: if (attribClass != null) {
0479: int[] finArray = attribClass.getArrayOfIntValues();
0480: if ((finArray != null) && (finArray.length > 0)) {
0481: Finishings[] finSup = new Finishings[finArray.length];
0482: for (int i = 0; i < finArray.length; i++) {
0483: finSup[i] = Finishings.NONE;
0484: Finishings[] fAll = (Finishings[]) (new ExtFinishing(
0485: 100)).getAll();
0486: for (int j = 0; j < fAll.length; j++) {
0487: if (finArray[i] == fAll[j].getValue()) {
0488: finSup[i] = fAll[j];
0489: break;
0490: }
0491: }
0492: }
0493: return finSup;
0494: }
0495: }
0496: } else if (category == JobName.class) {
0497: return new JobName("Java Printing", null);
0498: } else if (category == JobSheets.class) {
0499: JobSheets arr[] = new JobSheets[2];
0500: arr[0] = JobSheets.NONE;
0501: arr[1] = JobSheets.STANDARD;
0502: return arr;
0503:
0504: } else if (category == Media.class) {
0505: Media[] allMedia = new Media[mediaSizeNames.length
0506: + mediaTrays.length];
0507:
0508: for (int i = 0; i < mediaSizeNames.length; i++) {
0509: allMedia[i] = mediaSizeNames[i];
0510: }
0511:
0512: for (int i = 0; i < mediaTrays.length; i++) {
0513: allMedia[i + mediaSizeNames.length] = mediaTrays[i];
0514: }
0515:
0516: if (allMedia.length == 0) {
0517: allMedia = new Media[1];
0518: allMedia[0] = (Media) getDefaultAttributeValue(Media.class);
0519: }
0520:
0521: return allMedia;
0522: } else if (category == MediaPrintableArea.class) {
0523: MediaPrintableArea[] mpas = null;
0524: if (cps != null) {
0525: mpas = cps.getMediaPrintableArea();
0526: }
0527:
0528: if (mpas == null) {
0529: mpas = new MediaPrintableArea[1];
0530: mpas[0] = (MediaPrintableArea) getDefaultAttributeValue(MediaPrintableArea.class);
0531: }
0532:
0533: if ((attributes == null) || (attributes.size() == 0)) {
0534: ArrayList<MediaPrintableArea> printableList = new ArrayList<MediaPrintableArea>();
0535:
0536: for (int i = 0; i < mpas.length; i++) {
0537: if (mpas[i] != null) {
0538: printableList.add(mpas[i]);
0539: }
0540: }
0541: if (printableList.size() > 0) {
0542: mpas = new MediaPrintableArea[printableList.size()];
0543: printableList.toArray(mpas);
0544: }
0545: return mpas;
0546: }
0547:
0548: int match = -1;
0549: Media media = (Media) attributes.get(Media.class);
0550: if (media != null && media instanceof MediaSizeName) {
0551: MediaSizeName msn = (MediaSizeName) media;
0552:
0553: // case when no supported mediasizenames are reported
0554: // check given media against the default
0555: if (mediaSizeNames.length == 0
0556: && msn
0557: .equals(getDefaultAttributeValue(Media.class))) {
0558: //default printable area is that of default mediasize
0559: return mpas;
0560: }
0561:
0562: for (int i = 0; i < mediaSizeNames.length; i++) {
0563: if (msn.equals(mediaSizeNames[i])) {
0564: match = i;
0565: }
0566: }
0567: }
0568:
0569: if (match == -1) {
0570: return null;
0571: } else {
0572: MediaPrintableArea[] arr = new MediaPrintableArea[1];
0573: arr[0] = mpas[match];
0574: return arr;
0575: }
0576: } else if (category == NumberUp.class) {
0577: AttributeClass attribClass = (getAttMap != null) ? (AttributeClass) getAttMap
0578: .get("number-up-supported")
0579: : null;
0580: if (attribClass != null) {
0581: int[] values = attribClass.getArrayOfIntValues();
0582: if (values != null) {
0583: NumberUp[] nUp = new NumberUp[values.length];
0584: for (int i = 0; i < values.length; i++) {
0585: nUp[i] = new NumberUp(values[i]);
0586: }
0587: return nUp;
0588: } else {
0589: return null;
0590: }
0591: }
0592: } else if (category == OrientationRequested.class) {
0593: if (flavor == null
0594: || flavor
0595: .equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE)
0596: || flavor
0597: .equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
0598: // Orientation is emulated in Pageable/Printable flavors
0599: // so we report the 3 orientations as supported.
0600: OrientationRequested[] orientSup = new OrientationRequested[3];
0601: orientSup[0] = OrientationRequested.PORTRAIT;
0602: orientSup[1] = OrientationRequested.LANDSCAPE;
0603: orientSup[2] = OrientationRequested.REVERSE_LANDSCAPE;
0604: return orientSup;
0605: }
0606:
0607: AttributeClass attribClass = (getAttMap != null) ? (AttributeClass) getAttMap
0608: .get("orientation-requested-supported")
0609: : null;
0610: if (attribClass != null) {
0611: int[] orientArray = attribClass.getArrayOfIntValues();
0612: if ((orientArray != null) && (orientArray.length > 0)) {
0613: OrientationRequested[] orientSup = new OrientationRequested[orientArray.length];
0614: for (int i = 0; i < orientArray.length; i++) {
0615: switch (orientArray[i]) {
0616: default:
0617: case 3:
0618: orientSup[i] = OrientationRequested.PORTRAIT;
0619: break;
0620: case 4:
0621: orientSup[i] = OrientationRequested.LANDSCAPE;
0622: break;
0623: case 5:
0624: orientSup[i] = OrientationRequested.REVERSE_LANDSCAPE;
0625: break;
0626: case 6:
0627: orientSup[i] = OrientationRequested.REVERSE_PORTRAIT;
0628: break;
0629: }
0630: }
0631: return orientSup;
0632: }
0633: }
0634: } else if (category == PageRanges.class) {
0635: if (flavor == null
0636: || flavor
0637: .equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE)
0638: || flavor
0639: .equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
0640: PageRanges[] arr = new PageRanges[1];
0641: arr[0] = new PageRanges(1, Integer.MAX_VALUE);
0642: return arr;
0643: } else {
0644: // Returning null as this is not yet supported in UnixPrintJob.
0645: return null;
0646: }
0647: } else if (category == RequestingUserName.class) {
0648: String userName = "";
0649: try {
0650: userName = System.getProperty("user.name", "");
0651: } catch (SecurityException se) {
0652: }
0653: return new RequestingUserName(userName, null);
0654: } else if (category == Sides.class) {
0655: // The printer takes care of Sides so if short-edge
0656: // is chosen in a job, the rotation is done by the printer.
0657: // Orientation is rotated by emulation if pageable
0658: // or printable so if the document is in Landscape, this may
0659: // result in double rotation.
0660: AttributeClass attribClass = (getAttMap != null) ? (AttributeClass) getAttMap
0661: .get("sides-supported")
0662: : null;
0663: if (attribClass != null) {
0664: String[] sidesArray = attribClass
0665: .getArrayOfStringValues();
0666: if ((sidesArray != null) && (sidesArray.length > 0)) {
0667: Sides[] sidesSup = new Sides[sidesArray.length];
0668: for (int i = 0; i < sidesArray.length; i++) {
0669: if (sidesArray[i].endsWith("long-edge")) {
0670: sidesSup[i] = Sides.TWO_SIDED_LONG_EDGE;
0671: } else if (sidesArray[i].endsWith("short-edge")) {
0672: sidesSup[i] = Sides.TWO_SIDED_SHORT_EDGE;
0673: } else {
0674: sidesSup[i] = Sides.ONE_SIDED;
0675: }
0676: }
0677: return sidesSup;
0678: }
0679: }
0680: }
0681:
0682: return null;
0683: }
0684:
0685: //This class is for getting all pre-defined Finishings
0686: private class ExtFinishing extends Finishings {
0687: ExtFinishing(int value) {
0688: super (100); // 100 to avoid any conflicts with predefined values.
0689: }
0690:
0691: EnumSyntax[] getAll() {
0692: EnumSyntax[] es = super .getEnumValueTable();
0693: return es;
0694: }
0695: }
0696:
0697: public AttributeSet getUnsupportedAttributes(DocFlavor flavor,
0698: AttributeSet attributes) {
0699: if (flavor != null && !isDocFlavorSupported(flavor)) {
0700: throw new IllegalArgumentException("flavor " + flavor
0701: + "is not supported");
0702: }
0703:
0704: if (attributes == null) {
0705: return null;
0706: }
0707:
0708: Attribute attr;
0709: AttributeSet unsupp = new HashAttributeSet();
0710: Attribute[] attrs = attributes.toArray();
0711: for (int i = 0; i < attrs.length; i++) {
0712: try {
0713: attr = attrs[i];
0714: if (!isAttributeCategorySupported(attr.getCategory())) {
0715: unsupp.add(attr);
0716: } else if (!isAttributeValueSupported(attr, flavor,
0717: attributes)) {
0718: unsupp.add(attr);
0719: }
0720: } catch (ClassCastException e) {
0721: }
0722: }
0723: if (unsupp.isEmpty()) {
0724: return null;
0725: } else {
0726: return unsupp;
0727: }
0728: }
0729:
0730: public synchronized DocFlavor[] getSupportedDocFlavors() {
0731:
0732: if (supportedDocFlavors != null) {
0733: int len = supportedDocFlavors.length;
0734: DocFlavor[] copyflavors = new DocFlavor[len];
0735: System.arraycopy(supportedDocFlavors, 0, copyflavors, 0,
0736: len);
0737: return copyflavors;
0738: }
0739: initAttributes();
0740:
0741: if ((getAttMap != null)
0742: && getAttMap.containsKey("document-format-supported")) {
0743:
0744: AttributeClass attribClass = (AttributeClass) getAttMap
0745: .get("document-format-supported");
0746: if (attribClass != null) {
0747: String mimeType;
0748: boolean psSupported = false;
0749: String[] docFlavors = attribClass
0750: .getArrayOfStringValues();
0751: DocFlavor[] flavors;
0752: ArrayList docList = new ArrayList();
0753: int j;
0754: String hostEnc = DocFlavor.hostEncoding
0755: .toLowerCase(Locale.ENGLISH);
0756: boolean addHostEncoding = !hostEnc.equals("utf-8")
0757: && !hostEnc.equals("utf-16")
0758: && !hostEnc.equals("utf-16be")
0759: && !hostEnc.equals("utf-16le")
0760: && !hostEnc.equals("us-ascii");
0761:
0762: for (int i = 0; i < docFlavors.length; i++) {
0763: for (j = 0; j < allDocFlavors.length; j++) {
0764: flavors = (DocFlavor[]) allDocFlavors[j];
0765:
0766: mimeType = flavors[0].getMimeType();
0767: if (mimeType.startsWith(docFlavors[i])) {
0768:
0769: docList.addAll(Arrays.asList(flavors));
0770:
0771: if (mimeType.equals("text/plain")
0772: && addHostEncoding) {
0773: docList.add(Arrays
0774: .asList(textPlainHost));
0775: } else if (mimeType.equals("text/html")
0776: && addHostEncoding) {
0777: docList
0778: .add(Arrays
0779: .asList(textHtmlHost));
0780: } else if (mimeType.equals("image/png")) {
0781: pngImagesAdded = true;
0782: } else if (mimeType.equals("image/gif")) {
0783: gifImagesAdded = true;
0784: } else if (mimeType.equals("image/jpeg")) {
0785: jpgImagesAdded = true;
0786: } else if (mimeType.indexOf("postscript") != -1) {
0787: docList
0788: .add(DocFlavor.SERVICE_FORMATTED.PAGEABLE);
0789: docList
0790: .add(DocFlavor.SERVICE_FORMATTED.PRINTABLE);
0791:
0792: psSupported = true;
0793: }
0794: break;
0795: }
0796: }
0797:
0798: // Not added? Create new DocFlavors
0799: if (j == allDocFlavors.length) {
0800: // make new DocFlavors
0801: docList.add(new DocFlavor.BYTE_ARRAY(
0802: docFlavors[i]));
0803: docList.add(new DocFlavor.INPUT_STREAM(
0804: docFlavors[i]));
0805: docList.add(new DocFlavor.URL(docFlavors[i]));
0806: }
0807: }
0808:
0809: // check if we need to add image DocFlavors
0810: if (psSupported) {
0811: if (!jpgImagesAdded) {
0812: docList.addAll(Arrays.asList(imageJPG));
0813: }
0814: if (!pngImagesAdded) {
0815: docList.addAll(Arrays.asList(imagePNG));
0816: }
0817: if (!gifImagesAdded) {
0818: docList.addAll(Arrays.asList(imageGIF));
0819: }
0820: }
0821: supportedDocFlavors = new DocFlavor[docList.size()];
0822: docList.toArray(supportedDocFlavors);
0823: int len = supportedDocFlavors.length;
0824: DocFlavor[] copyflavors = new DocFlavor[len];
0825: System.arraycopy(supportedDocFlavors, 0, copyflavors,
0826: 0, len);
0827: return copyflavors;
0828: }
0829: }
0830: return null;
0831: }
0832:
0833: public boolean isDocFlavorSupported(DocFlavor flavor) {
0834: if (supportedDocFlavors == null) {
0835: getSupportedDocFlavors();
0836: }
0837: if (supportedDocFlavors != null) {
0838: for (int f = 0; f < supportedDocFlavors.length; f++) {
0839: if (flavor.equals(supportedDocFlavors[f])) {
0840: return true;
0841: }
0842: }
0843: }
0844: return false;
0845: }
0846:
0847: /**
0848: * Finds matching CustomMediaSizeName of given media.
0849: */
0850: public CustomMediaSizeName findCustomMedia(MediaSizeName media) {
0851: for (int i = 0; i < customMediaSizeNames.length; i++) {
0852: CustomMediaSizeName custom = (CustomMediaSizeName) customMediaSizeNames[i];
0853: MediaSizeName msn = custom.getStandardMedia();
0854: if (media.equals(msn)) {
0855: return customMediaSizeNames[i];
0856: }
0857: }
0858: return null;
0859: }
0860:
0861: /**
0862: * Returns the matching standard Media using string comparison of names.
0863: */
0864: private Media getIPPMedia(String mediaName) {
0865: CustomMediaSizeName sampleSize = new CustomMediaSizeName(
0866: "sample", "", 0, 0);
0867: Media[] sizes = sampleSize.getSuperEnumTable();
0868: for (int i = 0; i < sizes.length; i++) {
0869: if (mediaName.equals("" + sizes[i])) {
0870: return sizes[i];
0871: }
0872: }
0873: CustomMediaTray sampleTray = new CustomMediaTray("sample", "");
0874: Media[] trays = sampleTray.getSuperEnumTable();
0875: for (int i = 0; i < trays.length; i++) {
0876: if (mediaName.equals("" + trays[i])) {
0877: return trays[i];
0878: }
0879: }
0880: return null;
0881: }
0882:
0883: private Media[] getSupportedMedia() {
0884: if ((getAttMap != null)
0885: && getAttMap.containsKey("media-supported")) {
0886:
0887: AttributeClass attribClass = (AttributeClass) getAttMap
0888: .get("media-supported");
0889:
0890: if (attribClass != null) {
0891: String[] mediaVals = attribClass
0892: .getArrayOfStringValues();
0893: Media msn;
0894: Media[] mediaNames = new Media[mediaVals.length];
0895: for (int i = 0; i < mediaVals.length; i++) {
0896: msn = getIPPMedia(mediaVals[i]);
0897: //REMIND: if null, create custom?
0898: mediaNames[i] = msn;
0899: }
0900: return mediaNames;
0901: }
0902: }
0903: return new Media[0];
0904: }
0905:
0906: public synchronized Class[] getSupportedAttributeCategories() {
0907: if (supportedCats != null) {
0908: return supportedCats;
0909: }
0910:
0911: initAttributes();
0912:
0913: ArrayList catList = new ArrayList();
0914: Class cl;
0915:
0916: for (int i = 0; i < printReqAttribDefault.length; i++) {
0917: PrintRequestAttribute pra = (PrintRequestAttribute) printReqAttribDefault[i];
0918: if (getAttMap != null
0919: && getAttMap.containsKey(pra.getName()
0920: + "-supported")) {
0921: cl = pra.getCategory();
0922: catList.add(cl);
0923: }
0924: }
0925:
0926: // Some IPP printers like lexc710 do not have list of supported media
0927: // but CUPS can get the media from PPD, so we still report as
0928: // supported category.
0929: if (isCupsPrinter) {
0930: if (!catList.contains(Media.class)) {
0931: catList.add(Media.class);
0932: }
0933:
0934: // Always add MediaPrintable for cups,
0935: // because we can get it from PPD.
0936: catList.add(MediaPrintableArea.class);
0937:
0938: // this is already supported in UnixPrintJob
0939: catList.add(Destination.class);
0940: }
0941:
0942: // With the assumption that Chromaticity is equivalent to
0943: // ColorSupported.
0944: if (getAttMap != null
0945: && getAttMap.containsKey("color-supported")) {
0946: catList.add(Chromaticity.class);
0947: }
0948: supportedCats = new Class[catList.size()];
0949: catList.toArray(supportedCats);
0950: return supportedCats;
0951: }
0952:
0953: public boolean isAttributeCategorySupported(
0954: Class<? extends Attribute> category) {
0955: if (category == null) {
0956: throw new NullPointerException("null category");
0957: }
0958: if (!(Attribute.class.isAssignableFrom(category))) {
0959: throw new IllegalArgumentException(category
0960: + " is not an Attribute");
0961: }
0962:
0963: if (supportedCats == null) {
0964: getSupportedAttributeCategories();
0965: }
0966:
0967: for (int i = 0; i < supportedCats.length; i++) {
0968: if (category == supportedCats[i]) {
0969: return true;
0970: }
0971: }
0972:
0973: return false;
0974: }
0975:
0976: public synchronized <T extends PrintServiceAttribute> T getAttribute(
0977: Class<T> category) {
0978: if (category == null) {
0979: throw new NullPointerException("category");
0980: }
0981: if (!(PrintServiceAttribute.class.isAssignableFrom(category))) {
0982: throw new IllegalArgumentException(
0983: "Not a PrintServiceAttribute");
0984: }
0985:
0986: initAttributes();
0987:
0988: if (category == PrinterName.class) {
0989: return (T) (new PrinterName(printer, null));
0990: } else if (category == QueuedJobCount.class) {
0991: QueuedJobCount qjc = new QueuedJobCount(0);
0992: AttributeClass ac = (getAttMap != null) ? (AttributeClass) getAttMap
0993: .get(qjc.getName())
0994: : null;
0995: if (ac != null) {
0996: qjc = new QueuedJobCount(ac.getIntValue());
0997: }
0998: return (T) qjc;
0999: } else if (category == PrinterIsAcceptingJobs.class) {
1000: PrinterIsAcceptingJobs accJob = PrinterIsAcceptingJobs.ACCEPTING_JOBS;
1001: AttributeClass ac = (getAttMap != null) ? (AttributeClass) getAttMap
1002: .get(accJob.getName())
1003: : null;
1004: if ((ac != null) && (ac.getByteValue() == 0)) {
1005: accJob = PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS;
1006: }
1007: return (T) accJob;
1008: } else if (category == ColorSupported.class) {
1009: ColorSupported cs = ColorSupported.SUPPORTED;
1010: AttributeClass ac = (getAttMap != null) ? (AttributeClass) getAttMap
1011: .get(cs.getName())
1012: : null;
1013: if ((ac != null) && (ac.getByteValue() == 0)) {
1014: cs = ColorSupported.NOT_SUPPORTED;
1015: }
1016: return (T) cs;
1017: } else if (category == PDLOverrideSupported.class) {
1018:
1019: if (isCupsPrinter) {
1020: // Documented: For CUPS this will always be false
1021: return (T) PDLOverrideSupported.NOT_ATTEMPTED;
1022: } else {
1023: // REMIND: check attribute values
1024: return (T) PDLOverrideSupported.NOT_ATTEMPTED;
1025: }
1026: } else {
1027: return null;
1028: }
1029: }
1030:
1031: public synchronized PrintServiceAttributeSet getAttributes() {
1032: // update getAttMap by sending again get-attributes IPP request
1033: init = false;
1034: initAttributes();
1035:
1036: HashPrintServiceAttributeSet attrs = new HashPrintServiceAttributeSet();
1037:
1038: for (int i = 0; i < serviceAttributes.length; i++) {
1039: String name = (String) serviceAttributes[i][1];
1040: if (getAttMap != null && getAttMap.containsKey(name)) {
1041: Class c = (Class) serviceAttributes[i][0];
1042: PrintServiceAttribute psa = getAttribute(c);
1043: if (psa != null) {
1044: attrs.add(psa);
1045: }
1046: }
1047: }
1048: return AttributeSetUtilities.unmodifiableView(attrs);
1049: }
1050:
1051: public boolean isIPPSupportedImages(String mimeType) {
1052: if (supportedDocFlavors == null) {
1053: getSupportedDocFlavors();
1054: }
1055:
1056: if (mimeType.equals("image/png") && pngImagesAdded) {
1057: return true;
1058: } else if (mimeType.equals("image/gif") && gifImagesAdded) {
1059: return true;
1060: } else if (mimeType.equals("image/jpeg") && jpgImagesAdded) {
1061: return true;
1062: }
1063:
1064: return false;
1065: }
1066:
1067: private boolean isSupportedCopies(Copies copies) {
1068: CopiesSupported cs = (CopiesSupported) getSupportedAttributeValues(
1069: Copies.class, null, null);
1070: int[][] members = cs.getMembers();
1071: int min, max;
1072: if ((members.length > 0) && (members[0].length > 0)) {
1073: min = members[0][0];
1074: max = members[0][1];
1075: } else {
1076: min = 1;
1077: max = MAXCOPIES;
1078: }
1079:
1080: int value = copies.getValue();
1081: return (value >= min && value <= max);
1082: }
1083:
1084: private boolean isAutoSense(DocFlavor flavor) {
1085: if (flavor.equals(DocFlavor.BYTE_ARRAY.AUTOSENSE)
1086: || flavor.equals(DocFlavor.INPUT_STREAM.AUTOSENSE)
1087: || flavor.equals(DocFlavor.URL.AUTOSENSE)) {
1088: return true;
1089: } else {
1090: return false;
1091: }
1092: }
1093:
1094: private synchronized boolean isSupportedMediaTray(MediaTray msn) {
1095: initAttributes();
1096:
1097: if (mediaTrays != null) {
1098: for (int i = 0; i < mediaTrays.length; i++) {
1099: if (msn.equals(mediaTrays[i])) {
1100: return true;
1101: }
1102: }
1103: }
1104: return false;
1105: }
1106:
1107: private synchronized boolean isSupportedMedia(MediaSizeName msn) {
1108: initAttributes();
1109:
1110: if (msn.equals((Media) getDefaultAttributeValue(Media.class))) {
1111: return true;
1112: }
1113: for (int i = 0; i < mediaSizeNames.length; i++) {
1114: debug_println("mediaSizeNames[i] " + mediaSizeNames[i]);
1115: if (msn.equals(mediaSizeNames[i])) {
1116: return true;
1117: }
1118: }
1119: return false;
1120: }
1121:
1122: /* Return false if flavor is not null, pageable, nor printable and
1123: * Destination is part of attributes.
1124: */
1125: private boolean isDestinationSupported(DocFlavor flavor,
1126: AttributeSet attributes) {
1127:
1128: if ((attributes != null)
1129: && (attributes.get(Destination.class) != null)
1130: && !(flavor == null
1131: || flavor
1132: .equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || flavor
1133: .equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
1134: return false;
1135: }
1136: return true;
1137: }
1138:
1139: public boolean isAttributeValueSupported(Attribute attr,
1140: DocFlavor flavor, AttributeSet attributes) {
1141: if (attr == null) {
1142: throw new NullPointerException("null attribute");
1143: }
1144: if (flavor != null) {
1145: if (!isDocFlavorSupported(flavor)) {
1146: throw new IllegalArgumentException(flavor
1147: + " is an unsupported flavor");
1148: } else if (isAutoSense(flavor)) {
1149: return false;
1150: }
1151: }
1152: Class category = attr.getCategory();
1153: if (!isAttributeCategorySupported(category)) {
1154: return false;
1155: }
1156:
1157: /* Test if the flavor is compatible with the attributes */
1158: if (!isDestinationSupported(flavor, attributes)) {
1159: return false;
1160: }
1161:
1162: /* Test if the flavor is compatible with the category */
1163: if (attr.getCategory() == Chromaticity.class) {
1164: if ((flavor == null)
1165: || flavor
1166: .equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE)
1167: || flavor
1168: .equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)
1169: || flavor.equals(DocFlavor.BYTE_ARRAY.GIF)
1170: || flavor.equals(DocFlavor.INPUT_STREAM.GIF)
1171: || flavor.equals(DocFlavor.URL.GIF)
1172: || flavor.equals(DocFlavor.BYTE_ARRAY.JPEG)
1173: || flavor.equals(DocFlavor.INPUT_STREAM.JPEG)
1174: || flavor.equals(DocFlavor.URL.JPEG)
1175: || flavor.equals(DocFlavor.BYTE_ARRAY.PNG)
1176: || flavor.equals(DocFlavor.INPUT_STREAM.PNG)
1177: || flavor.equals(DocFlavor.URL.PNG)) {
1178: return attr == Chromaticity.COLOR;
1179: } else {
1180: return false;
1181: }
1182: } else if (attr.getCategory() == Copies.class) {
1183: return isSupportedCopies((Copies) attr);
1184: } else if (attr.getCategory() == Destination.class) {
1185: if (flavor == null
1186: || flavor
1187: .equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE)
1188: || flavor
1189: .equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
1190: URI uri = ((Destination) attr).getURI();
1191: if ("file".equals(uri.getScheme())
1192: && !(uri.getSchemeSpecificPart().equals(""))) {
1193: return true;
1194: }
1195: }
1196: return false;
1197: } else if (attr.getCategory() == Media.class) {
1198: if (attr instanceof MediaSizeName) {
1199: return isSupportedMedia((MediaSizeName) attr);
1200: }
1201: if (attr instanceof MediaTray) {
1202: return isSupportedMediaTray((MediaTray) attr);
1203: }
1204: } else if (attr.getCategory() == PageRanges.class) {
1205: if (flavor != null
1206: && !(flavor
1207: .equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || flavor
1208: .equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
1209: return false;
1210: }
1211: } else if (attr.getCategory() == SheetCollate.class) {
1212: if (flavor != null
1213: && !(flavor
1214: .equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || flavor
1215: .equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
1216: return false;
1217: }
1218: } else if (attr.getCategory() == Sides.class) {
1219: Sides[] sidesArray = (Sides[]) getSupportedAttributeValues(
1220: Sides.class, flavor, attributes);
1221:
1222: if (sidesArray != null) {
1223: for (int i = 0; i < sidesArray.length; i++) {
1224: if (sidesArray[i] == (Sides) attr) {
1225: return true;
1226: }
1227: }
1228: }
1229: return false;
1230: } else if (attr.getCategory() == OrientationRequested.class) {
1231: OrientationRequested[] orientArray = (OrientationRequested[]) getSupportedAttributeValues(
1232: OrientationRequested.class, flavor, attributes);
1233:
1234: if (orientArray != null) {
1235: for (int i = 0; i < orientArray.length; i++) {
1236: if (orientArray[i] == (OrientationRequested) attr) {
1237: return true;
1238: }
1239: }
1240: }
1241: return false;
1242: }
1243: return true;
1244: }
1245:
1246: public synchronized Object getDefaultAttributeValue(
1247: Class<? extends Attribute> category) {
1248: if (category == null) {
1249: throw new NullPointerException("null category");
1250: }
1251: if (!Attribute.class.isAssignableFrom(category)) {
1252: throw new IllegalArgumentException(category
1253: + " is not an Attribute");
1254: }
1255: if (!isAttributeCategorySupported(category)) {
1256: return null;
1257: }
1258:
1259: initAttributes();
1260:
1261: String catName = null;
1262: for (int i = 0; i < printReqAttribDefault.length; i++) {
1263: PrintRequestAttribute pra = (PrintRequestAttribute) printReqAttribDefault[i];
1264: if (pra.getCategory() == category) {
1265: catName = pra.getName();
1266: break;
1267: }
1268: }
1269: String attribName = catName + "-default";
1270: AttributeClass attribClass = (getAttMap != null) ? (AttributeClass) getAttMap
1271: .get(attribName)
1272: : null;
1273:
1274: if (category == Copies.class) {
1275: if (attribClass != null) {
1276: return new Copies(attribClass.getIntValue());
1277: } else {
1278: return new Copies(1);
1279: }
1280: } else if (category == Chromaticity.class) {
1281: return Chromaticity.COLOR;
1282: } else if (category == Destination.class) {
1283: try {
1284: return new Destination((new File("out.ps")).toURI());
1285: } catch (SecurityException se) {
1286: try {
1287: return new Destination(new URI("file:out.ps"));
1288: } catch (URISyntaxException e) {
1289: return null;
1290: }
1291: }
1292: } else if (category == Fidelity.class) {
1293: return Fidelity.FIDELITY_FALSE;
1294: } else if (category == Finishings.class) {
1295: return Finishings.NONE;
1296: } else if (category == JobName.class) {
1297: return new JobName("Java Printing", null);
1298: } else if (category == JobSheets.class) {
1299: if (attribClass != null
1300: && attribClass.getStringValue().equals("none")) {
1301: return JobSheets.NONE;
1302: } else {
1303: return JobSheets.STANDARD;
1304: }
1305: } else if (category == Media.class) {
1306: defaultMediaIndex = 0;
1307: if (mediaSizeNames.length == 0) {
1308: String defaultCountry = Locale.getDefault()
1309: .getCountry();
1310: if (defaultCountry != null
1311: && (defaultCountry.equals("")
1312: || defaultCountry.equals(Locale.US
1313: .getCountry()) || defaultCountry
1314: .equals(Locale.CANADA.getCountry()))) {
1315: return MediaSizeName.NA_LETTER;
1316: } else {
1317: return MediaSizeName.ISO_A4;
1318: }
1319: }
1320:
1321: if (attribClass != null) {
1322: String name = attribClass.getStringValue();
1323: if (isCupsPrinter) {
1324: for (int i = 0; i < customMediaSizeNames.length; i++) {
1325: //REMIND: get default from PPD. In native _getMedia,
1326: // move default (ppd_option_t->defchoice) to index 0.
1327: // In the meantime, use indexOf because PPD name
1328: // may be different from the IPP attribute name.
1329: if (customMediaSizeNames[i].toString().indexOf(
1330: name) != -1) {
1331: defaultMediaIndex = i;
1332: return mediaSizeNames[defaultMediaIndex];
1333: }
1334: }
1335: } else {
1336: for (int i = 0; i < mediaSizeNames.length; i++) {
1337: if (mediaSizeNames[i].toString().indexOf(name) != -1) {
1338: defaultMediaIndex = i;
1339: return mediaSizeNames[defaultMediaIndex];
1340: }
1341: }
1342: }
1343: }
1344: return mediaSizeNames[defaultMediaIndex];
1345:
1346: } else if (category == MediaPrintableArea.class) {
1347: MediaPrintableArea[] mpas;
1348: if ((cps != null)
1349: && ((mpas = cps.getMediaPrintableArea()) != null)) {
1350: if (defaultMediaIndex == -1) {
1351: // initializes value of defaultMediaIndex
1352: getDefaultAttributeValue(Media.class);
1353: }
1354: return mpas[defaultMediaIndex];
1355: } else {
1356: String defaultCountry = Locale.getDefault()
1357: .getCountry();
1358: float iw, ih;
1359: if (defaultCountry != null
1360: && (defaultCountry.equals("")
1361: || defaultCountry.equals(Locale.US
1362: .getCountry()) || defaultCountry
1363: .equals(Locale.CANADA.getCountry()))) {
1364: iw = MediaSize.NA.LETTER.getX(Size2DSyntax.INCH) - 0.5f;
1365: ih = MediaSize.NA.LETTER.getY(Size2DSyntax.INCH) - 0.5f;
1366: } else {
1367: iw = MediaSize.ISO.A4.getX(Size2DSyntax.INCH) - 0.5f;
1368: ih = MediaSize.ISO.A4.getY(Size2DSyntax.INCH) - 0.5f;
1369: }
1370: return new MediaPrintableArea(0.25f, 0.25f, iw, ih,
1371: MediaPrintableArea.INCH);
1372: }
1373: } else if (category == NumberUp.class) {
1374: return new NumberUp(1); // for CUPS this is always 1
1375: } else if (category == OrientationRequested.class) {
1376: if (attribClass != null) {
1377: switch (attribClass.getIntValue()) {
1378: default:
1379: case 3:
1380: return OrientationRequested.PORTRAIT;
1381: case 4:
1382: return OrientationRequested.LANDSCAPE;
1383: case 5:
1384: return OrientationRequested.REVERSE_LANDSCAPE;
1385: case 6:
1386: return OrientationRequested.REVERSE_PORTRAIT;
1387: }
1388: } else {
1389: return OrientationRequested.PORTRAIT;
1390: }
1391: } else if (category == PageRanges.class) {
1392: if (attribClass != null) {
1393: int[] range = attribClass.getIntRangeValue();
1394: return new PageRanges(range[0], range[1]);
1395: } else {
1396: return new PageRanges(1, Integer.MAX_VALUE);
1397: }
1398: } else if (category == RequestingUserName.class) {
1399: String userName = "";
1400: try {
1401: userName = System.getProperty("user.name", "");
1402: } catch (SecurityException se) {
1403: }
1404: return new RequestingUserName(userName, null);
1405: } else if (category == SheetCollate.class) {
1406: return SheetCollate.UNCOLLATED;
1407: } else if (category == Sides.class) {
1408: if (attribClass != null) {
1409: if (attribClass.getStringValue().endsWith("long-edge")) {
1410: return Sides.TWO_SIDED_LONG_EDGE;
1411: } else if (attribClass.getStringValue().endsWith(
1412: "short-edge")) {
1413: return Sides.TWO_SIDED_SHORT_EDGE;
1414: }
1415: }
1416: return Sides.ONE_SIDED;
1417: }
1418:
1419: return null;
1420: }
1421:
1422: public ServiceUIFactory getServiceUIFactory() {
1423: return null;
1424: }
1425:
1426: public void wakeNotifier() {
1427: synchronized (this ) {
1428: if (notifier != null) {
1429: notifier.wake();
1430: }
1431: }
1432: }
1433:
1434: public void addPrintServiceAttributeListener(
1435: PrintServiceAttributeListener listener) {
1436: synchronized (this ) {
1437: if (listener == null) {
1438: return;
1439: }
1440: if (notifier == null) {
1441: notifier = new ServiceNotifier(this );
1442: }
1443: notifier.addListener(listener);
1444: }
1445: }
1446:
1447: public void removePrintServiceAttributeListener(
1448: PrintServiceAttributeListener listener) {
1449: synchronized (this ) {
1450: if (listener == null || notifier == null) {
1451: return;
1452: }
1453: notifier.removeListener(listener);
1454: if (notifier.isEmpty()) {
1455: notifier.stopNotifier();
1456: notifier = null;
1457: }
1458: }
1459: }
1460:
1461: public String getName() {
1462: return printer;
1463: }
1464:
1465: public boolean usesClass(Class c) {
1466: return (c == sun.print.PSPrinterJob.class);
1467: }
1468:
1469: public static HttpURLConnection getIPPConnection(URL url) {
1470: HttpURLConnection connection;
1471: try {
1472: connection = (HttpURLConnection) url.openConnection();
1473: } catch (java.io.IOException ioe) {
1474: return null;
1475: }
1476: if (!(connection instanceof HttpURLConnection)) {
1477: return null;
1478: }
1479: connection.setUseCaches(false);
1480: connection.setDefaultUseCaches(false);
1481: connection.setDoInput(true);
1482: connection.setDoOutput(true);
1483: connection
1484: .setRequestProperty("Content-type", "application/ipp");
1485: return connection;
1486: }
1487:
1488: public synchronized boolean isPostscript() {
1489: if (isPS == null) {
1490: isPS = Boolean.TRUE;
1491: if (isCupsPrinter) {
1492: try {
1493: urlConnection = getIPPConnection(new URL("http://"
1494: + CUPSPrinter.getServer() + ":"
1495: + CUPSPrinter.getPort() + "/printers/"
1496: + printer + ".ppd"));
1497:
1498: InputStream is = urlConnection.getInputStream();
1499: if (is != null) {
1500: BufferedReader d = new BufferedReader(
1501: new InputStreamReader(is, Charset
1502: .forName("ISO-8859-1")));
1503: String lineStr;
1504: while ((lineStr = d.readLine()) != null) {
1505: if (lineStr.startsWith("*cupsFilter:")) {
1506: isPS = Boolean.FALSE;
1507: break;
1508: }
1509: }
1510: }
1511: } catch (java.io.IOException e) {
1512: }
1513: }
1514: }
1515: return isPS.booleanValue();
1516: }
1517:
1518: private void opGetAttributes() {
1519: try {
1520: debug_println(debugPrefix + "opGetAttributes myURI "
1521: + myURI + " myURL " + myURL);
1522:
1523: AttributeClass attClNoUri[] = {
1524: AttributeClass.ATTRIBUTES_CHARSET,
1525: AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE };
1526:
1527: AttributeClass attCl[] = {
1528: AttributeClass.ATTRIBUTES_CHARSET,
1529: AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE,
1530: new AttributeClass("printer-uri",
1531: AttributeClass.TAG_URI, "" + myURI) };
1532:
1533: OutputStream os = (OutputStream) java.security.AccessController
1534: .doPrivileged(new java.security.PrivilegedAction() {
1535: public Object run() {
1536: try {
1537: return urlConnection.getOutputStream();
1538: } catch (Exception e) {
1539: }
1540: return null;
1541: }
1542: });
1543:
1544: if (os == null) {
1545: return;
1546: }
1547:
1548: boolean success = (myURI == null) ? writeIPPRequest(os,
1549: OP_GET_ATTRIBUTES, attClNoUri) : writeIPPRequest(
1550: os, OP_GET_ATTRIBUTES, attCl);
1551: if (success) {
1552: InputStream is = null;
1553: if ((is = urlConnection.getInputStream()) != null) {
1554: HashMap[] responseMap = readIPPResponse(is);
1555:
1556: if (responseMap != null && responseMap.length > 0) {
1557: getAttMap = responseMap[0];
1558: }
1559: } else {
1560: debug_println(debugPrefix
1561: + "opGetAttributes - null input stream");
1562: }
1563: is.close();
1564: }
1565: os.close();
1566: } catch (java.io.IOException e) {
1567: debug_println(debugPrefix
1568: + "opGetAttributes - input/output stream: " + e);
1569: }
1570: }
1571:
1572: public static boolean writeIPPRequest(OutputStream os,
1573: String operCode, AttributeClass[] attCl) {
1574: OutputStreamWriter osw = new OutputStreamWriter(os);
1575: char[] opCode = new char[2];
1576: opCode[0] = (char) Byte.parseByte(operCode.substring(0, 2), 16);
1577: opCode[1] = (char) Byte.parseByte(operCode.substring(2, 4), 16);
1578: char[] bytes = { 0x01, 0x01, 0x00, 0x01 };
1579: try {
1580: osw.write(bytes, 0, 2); // version number
1581: osw.write(opCode, 0, 2); // operation code
1582: bytes[0] = 0x00;
1583: bytes[1] = 0x00;
1584: osw.write(bytes, 0, 4); // request ID #1
1585:
1586: bytes[0] = 0x01; // operation-group-tag
1587: osw.write(bytes[0]);
1588:
1589: String valStr;
1590: char[] lenStr;
1591:
1592: AttributeClass ac;
1593: for (int i = 0; i < attCl.length; i++) {
1594: ac = attCl[i];
1595: osw.write(ac.getType()); // value tag
1596:
1597: lenStr = ac.getLenChars();
1598: osw.write(lenStr, 0, 2); // length
1599: osw.write("" + ac, 0, ac.getName().length());
1600:
1601: // check if string range (0x35 -> 0x49)
1602: if (ac.getType() >= AttributeClass.TAG_TEXT_LANGUAGE
1603: && ac.getType() <= AttributeClass.TAG_MIME_MEDIATYPE) {
1604: valStr = (String) ac.getObjectValue();
1605: bytes[0] = 0;
1606: bytes[1] = (char) valStr.length();
1607: osw.write(bytes, 0, 2);
1608: osw.write(valStr, 0, valStr.length());
1609: } // REMIND: need to support other value tags but for CUPS
1610: // string is all we need.
1611: }
1612:
1613: osw.write(GRPTAG_END_ATTRIBUTES);
1614: osw.flush();
1615: osw.close();
1616: } catch (java.io.IOException ioe) {
1617: debug_println(debugPrefix
1618: + "IPPPrintService Exception in writeIPPRequest: "
1619: + ioe);
1620: return false;
1621: }
1622: return true;
1623: }
1624:
1625: public static HashMap[] readIPPResponse(InputStream inputStream) {
1626:
1627: if (inputStream == null) {
1628: return null;
1629: }
1630:
1631: byte response[] = new byte[MAX_ATTRIBUTE_LENGTH];
1632: try {
1633:
1634: DataInputStream ois = new DataInputStream(inputStream);
1635:
1636: // read status and ID
1637: if ((ois.read(response, 0, 8) > -1)
1638: && (response[2] == STATUSCODE_SUCCESS)) {
1639:
1640: ByteArrayOutputStream outObj;
1641: int counter = 0;
1642: short len = 0;
1643: String attribStr = null;
1644: // assign default value
1645: byte valTagByte = AttributeClass.TAG_KEYWORD;
1646: ArrayList respList = new ArrayList();
1647: HashMap responseMap = new HashMap();
1648:
1649: response[0] = ois.readByte();
1650:
1651: // check for group tags
1652: while ((response[0] >= GRPTAG_OP_ATTRIBUTES)
1653: && (response[0] <= GRPTAG_PRINTER_ATTRIBUTES)
1654: && (response[0] != GRPTAG_END_ATTRIBUTES)) {
1655: debug_println(debugPrefix
1656: + "checking group tag, response[0]= "
1657: + response[0]);
1658:
1659: outObj = new ByteArrayOutputStream();
1660: //make sure counter and attribStr are re-initialized
1661: counter = 0;
1662: attribStr = null;
1663:
1664: // read value tag
1665: response[0] = ois.readByte();
1666: while (response[0] >= AttributeClass.TAG_INT
1667: && response[0] <= AttributeClass.TAG_MEMBER_ATTRNAME) {
1668: // read name length
1669: len = ois.readShort();
1670:
1671: // If current value is not part of previous attribute
1672: // then close stream and add it to HashMap.
1673: // It is part of previous attribute if name length=0.
1674: if ((len != 0) && (attribStr != null)) {
1675: //last byte is the total # of values
1676: outObj.write(counter);
1677: outObj.flush();
1678: outObj.close();
1679: byte outArray[] = outObj.toByteArray();
1680:
1681: // if key exists, new HashMap
1682: if (responseMap.containsKey(attribStr)) {
1683: respList.add(responseMap);
1684: responseMap = new HashMap();
1685: }
1686: AttributeClass ac = new AttributeClass(
1687: attribStr, valTagByte, outArray);
1688:
1689: responseMap.put(ac.getName(), ac);
1690:
1691: outObj = new ByteArrayOutputStream();
1692: counter = 0; //reset counter
1693: }
1694: //check if this is new value tag
1695: if (counter == 0) {
1696: valTagByte = response[0];
1697: }
1698: // read attribute name
1699: if (len != 0) {
1700: // read "len" characters
1701: // make sure it doesn't exceed the maximum
1702: if (len > MAX_ATTRIBUTE_LENGTH) {
1703: response = new byte[len]; // expand as needed
1704: }
1705: ois.read(response, 0, len);
1706: attribStr = new String(response, 0, len);
1707: }
1708: // read value length
1709: len = ois.readShort();
1710: // write name length
1711: outObj.write(len);
1712: // read value, make sure it doesn't exceed the maximum
1713: if (len > MAX_ATTRIBUTE_LENGTH) {
1714: response = new byte[len]; // expand as needed
1715: }
1716: ois.read(response, 0, len);
1717: // write value of "len" length
1718: outObj.write(response, 0, len);
1719: counter++;
1720: // read next byte
1721: response[0] = ois.readByte();
1722: }
1723:
1724: if (attribStr != null) {
1725: outObj.write(counter);
1726: outObj.flush();
1727: outObj.close();
1728:
1729: // if key exists in old HashMap, new HashMap
1730: if ((counter != 0)
1731: && responseMap.containsKey(attribStr)) {
1732: respList.add(responseMap);
1733: responseMap = new HashMap();
1734: }
1735:
1736: byte outArray[] = outObj.toByteArray();
1737:
1738: AttributeClass ac = new AttributeClass(
1739: attribStr, valTagByte, outArray);
1740: responseMap.put(ac.getName(), ac);
1741: }
1742: }
1743: ois.close();
1744: if ((responseMap != null) && (responseMap.size() > 0)) {
1745: respList.add(responseMap);
1746: }
1747: return (HashMap[]) respList
1748: .toArray(new HashMap[respList.size()]);
1749: } else {
1750: debug_println(debugPrefix
1751: + "readIPPResponse client error, IPP status code-"
1752: + Integer.toHexString(response[2]) + " & "
1753: + Integer.toHexString(response[3]));
1754: return null;
1755: }
1756:
1757: } catch (java.io.IOException e) {
1758: debug_println(debugPrefix + "readIPPResponse: " + e);
1759: return null;
1760: }
1761: }
1762:
1763: public String toString() {
1764: return "IPP Printer : " + getName();
1765: }
1766:
1767: public boolean equals(Object obj) {
1768: return (obj == this || (obj instanceof IPPPrintService && ((IPPPrintService) obj)
1769: .getName().equals(getName())));
1770: }
1771: }
|