/*
* Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.print;
import javax.print.attribute.*;
import javax.print.attribute.standard.*;
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintService;
import javax.print.ServiceUIFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.Date;
import java.util.Arrays;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import javax.print.event.PrintServiceAttributeListener;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.net.HttpURLConnection;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.DataInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.HashSet;
public class IPPPrintService implements PrintService, SunPrinterJobService {
public static final boolean debugPrint;
private static final String debugPrefix = "IPPPrintService>> ";
protected static void debug_println(String str) {
if (debugPrint) {
System.out.println(str);
}
}
private static final String FORCE_PIPE_PROP = "sun.print.ippdebug";
static {
String debugStr = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(FORCE_PIPE_PROP));
debugPrint = "true".equalsIgnoreCase(debugStr);
}
private String printer;
private URI myURI;
private URL myURL;
private transient ServiceNotifier notifier = null;
private static int MAXCOPIES = 1000;
private static short MAX_ATTRIBUTE_LENGTH = 255;
private CUPSPrinter cps;
private HttpURLConnection urlConnection = null;
private DocFlavor[] supportedDocFlavors;
private Class<?>[] supportedCats;
private MediaTray[] mediaTrays;
private MediaSizeName[] mediaSizeNames;
private CustomMediaSizeName[] customMediaSizeNames;
private int defaultMediaIndex;
private int[] rawResolutions = null;
private PrinterResolution[] printerResolutions = null;
private boolean isCupsPrinter;
private boolean init;
private Boolean isPS;
private HashMap<String, AttributeClass> getAttMap;
private boolean pngImagesAdded = false;
private boolean gifImagesAdded = false;
private boolean jpgImagesAdded = false;
/**
* IPP Status Codes
*/
private static final byte STATUSCODE_SUCCESS = 0x00;
/**
* IPP Group Tags. Each tag is used once before the first attribute
* of that group.
*/
// operation attributes group
private static final byte GRPTAG_OP_ATTRIBUTES = 0x01;
// job attributes group
private static final byte GRPTAG_JOB_ATTRIBUTES = 0x02;
// printer attributes group
private static final byte GRPTAG_PRINTER_ATTRIBUTES = 0x04;
// used as the last tag in an IPP message.
private static final byte GRPTAG_END_ATTRIBUTES = 0x03;
/**
* IPP Operation codes
*/
// gets the attributes for a printer
public static final String OP_GET_ATTRIBUTES = "000B";
// gets the default printer
public static final String OP_CUPS_GET_DEFAULT = "4001";
// gets the list of printers
public static final String OP_CUPS_GET_PRINTERS = "4002";
/**
* List of all PrintRequestAttributes. This is used
* for looping through all the IPP attribute name.
*/
private static Object[] printReqAttribDefault = {
Chromaticity.COLOR,
new Copies(1),
Fidelity.FIDELITY_FALSE,
Finishings.NONE,
//new JobHoldUntil(new Date()),
//new JobImpressions(0),
//JobImpressions,
//JobKOctets,
//JobMediaSheets,
new JobName("", Locale.getDefault()),
//JobPriority,
JobSheets.NONE,
(Media)MediaSizeName.NA_LETTER,
//MediaPrintableArea.class, // not an IPP attribute
//MultipleDocumentHandling.SINGLE_DOCUMENT,
new NumberUp(1),
OrientationRequested.PORTRAIT,
new PageRanges(1),
//PresentationDirection,
// CUPS does not supply printer-resolution attribute
//new PrinterResolution(300, 300, PrinterResolution.DPI),
//PrintQuality.NORMAL,
new RequestingUserName("", Locale.getDefault()),
//SheetCollate.UNCOLLATED, //CUPS has no sheet collate?
Sides.ONE_SIDED,
};
/**
* List of all PrintServiceAttributes. This is used
* for looping through all the IPP attribute name.
*/
private static Object[][] serviceAttributes = {
{ColorSupported.class, "color-supported"},
{PagesPerMinute.class, "pages-per-minute"},
{PagesPerMinuteColor.class, "pages-per-minute-color"},
{PDLOverrideSupported.class, "pdl-override-supported"},
{PrinterInfo.class, "printer-info"},
{PrinterIsAcceptingJobs.class, "printer-is-accepting-jobs"},
{PrinterLocation.class, "printer-location"},
{PrinterMakeAndModel.class, "printer-make-and-model"},
{PrinterMessageFromOperator.class, "printer-message-from-operator"},
{PrinterMoreInfo.class, "printer-more-info"},
{PrinterMoreInfoManufacturer.class, "printer-more-info-manufacturer"},
{PrinterName.class, "printer-name"},
{PrinterState.class, "printer-state"},
{PrinterStateReasons.class, "printer-state-reasons"},
{PrinterURI.class, "printer-uri"},
{QueuedJobCount.class, "queued-job-count"}
};
/**
* List of DocFlavors, grouped based on matching mime-type.
* NOTE: For any change in the predefined DocFlavors, it must be reflected
* here also.
*/
// PDF DocFlavors
private static DocFlavor[] appPDF = {
DocFlavor.BYTE_ARRAY.PDF,
DocFlavor.INPUT_STREAM.PDF,
DocFlavor.URL.PDF
};
// Postscript DocFlavors
private static DocFlavor[] appPostScript = {
DocFlavor.BYTE_ARRAY.POSTSCRIPT,
DocFlavor.INPUT_STREAM.POSTSCRIPT,
DocFlavor.URL.POSTSCRIPT
};
// Autosense DocFlavors
private static DocFlavor[] appOctetStream = {
DocFlavor.BYTE_ARRAY.AUTOSENSE,
DocFlavor.INPUT_STREAM.AUTOSENSE,
DocFlavor.URL.AUTOSENSE
};
// Text DocFlavors
private static DocFlavor[] textPlain = {
DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_8,
DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16,
DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16BE,
DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16LE,
DocFlavor.BYTE_ARRAY.TEXT_PLAIN_US_ASCII,
DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_8,
DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16,
DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16BE,
DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16LE,
DocFlavor.INPUT_STREAM.TEXT_PLAIN_US_ASCII,
DocFlavor.URL.TEXT_PLAIN_UTF_8,
DocFlavor.URL.TEXT_PLAIN_UTF_16,
DocFlavor.URL.TEXT_PLAIN_UTF_16BE,
DocFlavor.URL.TEXT_PLAIN_UTF_16LE,
DocFlavor.URL.TEXT_PLAIN_US_ASCII,
DocFlavor.CHAR_ARRAY.TEXT_PLAIN,
DocFlavor.STRING.TEXT_PLAIN,
DocFlavor.READER.TEXT_PLAIN
};
private static DocFlavor[] textPlainHost = {
DocFlavor.BYTE_ARRAY.TEXT_PLAIN_HOST,
DocFlavor.INPUT_STREAM.TEXT_PLAIN_HOST,
DocFlavor.URL.TEXT_PLAIN_HOST
};
// JPG DocFlavors
private static DocFlavor[] imageJPG = {
DocFlavor.BYTE_ARRAY.JPEG,
DocFlavor.INPUT_STREAM.JPEG,
DocFlavor.URL.JPEG
};
// GIF DocFlavors
private static DocFlavor[] imageGIF = {
DocFlavor.BYTE_ARRAY.GIF,
DocFlavor.INPUT_STREAM.GIF,
DocFlavor.URL.GIF
};
// PNG DocFlavors
private static DocFlavor[] imagePNG = {
DocFlavor.BYTE_ARRAY.PNG,
DocFlavor.INPUT_STREAM.PNG,
DocFlavor.URL.PNG
};
// HTML DocFlavors
private static DocFlavor[] textHtml = {
DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_8,
DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_16,
DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_16BE,
DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_16LE,
DocFlavor.BYTE_ARRAY.TEXT_HTML_US_ASCII,
DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_8,
DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_16,
DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_16BE,
DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_16LE,
DocFlavor.INPUT_STREAM.TEXT_HTML_US_ASCII,
DocFlavor.URL.TEXT_HTML_UTF_8,
DocFlavor.URL.TEXT_HTML_UTF_16,
DocFlavor.URL.TEXT_HTML_UTF_16BE,
DocFlavor.URL.TEXT_HTML_UTF_16LE,
DocFlavor.URL.TEXT_HTML_US_ASCII,
// These are not handled in UnixPrintJob so commenting these
// for now.
/*
DocFlavor.CHAR_ARRAY.TEXT_HTML,
DocFlavor.STRING.TEXT_HTML,
DocFlavor.READER.TEXT_HTML,
*/
};
private static DocFlavor[] textHtmlHost = {
DocFlavor.BYTE_ARRAY.TEXT_HTML_HOST,
DocFlavor.INPUT_STREAM.TEXT_HTML_HOST,
DocFlavor.URL.TEXT_HTML_HOST,
};
// PCL DocFlavors
private static DocFlavor[] appPCL = {
DocFlavor.BYTE_ARRAY.PCL,
DocFlavor.INPUT_STREAM.PCL,
DocFlavor.URL.PCL
};
// List of all DocFlavors, used in looping
// through all supported mime-types
private static Object[] allDocFlavors = {
appPDF, appPostScript, appOctetStream,
textPlain, imageJPG, imageGIF, imagePNG,
textHtml, appPCL,
};
IPPPrintService(String name, URL url) {
if ((name == null) || (url == null)){
throw new IllegalArgumentException("null uri or printer name");
}
try {
printer = java.net.URLDecoder.decode(name, "UTF-8");
} catch (java.io.UnsupportedEncodingException e) {
printer = name;
}
supportedDocFlavors = null;
supportedCats = null;
mediaSizeNames = null;
customMediaSizeNames = null;
mediaTrays = null;
myURL = url;
cps = null;
isCupsPrinter = false;
init = false;
defaultMediaIndex = -1;
String host = myURL.getHost();
if (host!=null && host.equals(CUPSPrinter.getServer())) {
isCupsPrinter = true;
try {
myURI = new URI("ipp://"+host+
"/printers/"+printer);
debug_println(debugPrefix+"IPPPrintService myURI : "+myURI);
} catch (java.net.URISyntaxException e) {
throw new IllegalArgumentException("invalid url");
}
}
}
IPPPrintService(String name, String uriStr, boolean isCups) {
if ((name == null) || (uriStr == null)){
throw new IllegalArgumentException("null uri or printer name");
}
try {
printer = java.net.URLDecoder.decode(name, "UTF-8");
} catch (java.io.UnsupportedEncodingException e) {
printer = name;
}
supportedDocFlavors = null;
supportedCats = null;
mediaSizeNames = null;
customMediaSizeNames = null;
mediaTrays = null;
cps = null;
init = false;
defaultMediaIndex = -1;
try {
myURL =
new URL(uriStr.replaceFirst("ipp", "http"));
} catch (Exception e) {
IPPPrintService.debug_println(debugPrefix+
" IPPPrintService, myURL="+
myURL+" Exception= "+
e);
throw new IllegalArgumentException("invalid url");
}
isCupsPrinter = isCups;
try {
myURI = new URI(uriStr);
debug_println(debugPrefix+"IPPPrintService myURI : "+myURI);
} catch (java.net.URISyntaxException e) {
throw new IllegalArgumentException("invalid uri");
}
}
/*
* Initialize mediaSizeNames, mediaTrays and other attributes.
* Media size/trays are initialized to non-null values, may be 0-length
* array.
* NOTE: Must be called from a synchronized block only.
*/
private void initAttributes() {
if (!init) {
// init customMediaSizeNames
customMediaSizeNames = new CustomMediaSizeName[0];
if ((urlConnection = getIPPConnection(myURL)) == null) {
mediaSizeNames = new MediaSizeName[0];
mediaTrays = new MediaTray[0];
debug_println(debugPrefix+"initAttributes, NULL urlConnection ");
init = true;
return;
}
// get all supported attributes through IPP
opGetAttributes();
if (isCupsPrinter) {
// note, it is possible to query media in CUPS using IPP
// right now we always get it from PPD.
// maybe use "&& (usePPD)" later?
// Another reason why we use PPD is because
// IPP currently does not support it but PPD does.
try {
cps = new CUPSPrinter(printer);
mediaSizeNames = cps.getMediaSizeNames();
mediaTrays = cps.getMediaTrays();
customMediaSizeNames = cps.getCustomMediaSizeNames();
defaultMediaIndex = cps.getDefaultMediaIndex();
rawResolutions = cps.getRawResolutions();
urlConnection.disconnect();
init = true;
return;
} catch (Exception e) {
IPPPrintService.debug_println(debugPrefix+
"initAttributes, error creating CUPSPrinter e="+e);
}
}
// use IPP to get all media,
Media[] allMedia = getSupportedMedia();
ArrayList<Media> sizeList = new ArrayList<>();
ArrayList<Media> trayList = new ArrayList<>();
for (int i=0; i<allMedia.length; i++) {
if (allMedia[i] instanceof MediaSizeName) {
sizeList.add(allMedia[i]);
} else if (allMedia[i] instanceof MediaTray) {
trayList.add(allMedia[i]);
}
}
if (sizeList != null) {
mediaSizeNames = new MediaSizeName[sizeList.size()];
mediaSizeNames = sizeList.toArray(mediaSizeNames);
}
if (trayList != null) {
mediaTrays = new MediaTray[trayList.size()];
mediaTrays = trayList.toArray(mediaTrays);
}
urlConnection.disconnect();
init = true;
}
}
public DocPrintJob createPrintJob() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPrintJobAccess();
}
// REMIND: create IPPPrintJob
return new UnixPrintJob(this);
}
public synchronized Object
getSupportedAttributeValues(Class<? extends Attribute> category,
DocFlavor flavor,
AttributeSet attributes)
{
if (category == null) {
throw new NullPointerException("null category");
}
if (!Attribute.class.isAssignableFrom(category)) {
throw new IllegalArgumentException(category +
" does not implement Attribute");
}
if (flavor != null) {
if (!isDocFlavorSupported(flavor)) {
throw new IllegalArgumentException(flavor +
" is an unsupported flavor");
} else if (isAutoSense(flavor)) {
return null;
}
}
if (!isAttributeCategorySupported(category)) {
return null;
}
/* Test if the flavor is compatible with the attributes */
if (!isDestinationSupported(flavor, attributes)) {
return null;
}
initAttributes();
/* Test if the flavor is compatible with the category */
if ((category == Copies.class) ||
(category == CopiesSupported.class)) {
if (flavor == null ||
!(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||
flavor.equals(DocFlavor.URL.POSTSCRIPT) ||
flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) {
CopiesSupported cs = new CopiesSupported(1, MAXCOPIES);
AttributeClass attribClass = (getAttMap != null) ?
getAttMap.get(cs.getName()) : null;
if (attribClass != null) {
int[] range = attribClass.getIntRangeValue();
cs = new CopiesSupported(range[0], range[1]);
}
return cs;
} else {
return null;
}
} else if (category == Chromaticity.class) {
if (flavor == null ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||
!isIPPSupportedImages(flavor.getMimeType())) {
Chromaticity[]arr = new Chromaticity[1];
arr[0] = Chromaticity.COLOR;
return (arr);
} else {
return null;
}
} else if (category == Destination.class) {
if (flavor == null ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
try {
return new Destination((new File("out.ps")).toURI());
} catch (SecurityException se) {
try {
return new Destination(new URI("file:out.ps"));
} catch (URISyntaxException e) {
return null;
}
}
}
return null;
} else if (category == Fidelity.class) {
Fidelity []arr = new Fidelity[2];
arr[0] = Fidelity.FIDELITY_FALSE;
arr[1] = Fidelity.FIDELITY_TRUE;
return arr;
} else if (category == Finishings.class) {
AttributeClass attribClass = (getAttMap != null) ?
getAttMap.get("finishings-supported")
: null;
if (attribClass != null) {
int[] finArray = attribClass.getArrayOfIntValues();
if ((finArray != null) && (finArray.length > 0)) {
Finishings[] finSup = new Finishings[finArray.length];
for (int i=0; i<finArray.length; i++) {
finSup[i] = Finishings.NONE;
Finishings[] fAll = (Finishings[])
(new ExtFinishing(100)).getAll();
for (int j=0; j<fAll.length; j++) {
if (finArray[i] == fAll[j].getValue()) {
finSup[i] = fAll[j];
break;
}
}
}
return finSup;
}
}
} else if (category == JobName.class) {
return new JobName("Java Printing", null);
} else if (category == JobSheets.class) {
JobSheets arr[] = new JobSheets[2];
arr[0] = JobSheets.NONE;
arr[1] = JobSheets.STANDARD;
return arr;
} else if (category == Media.class) {
Media[] allMedia = new Media[mediaSizeNames.length+
mediaTrays.length];
for (int i=0; i<mediaSizeNames.length; i++) {
allMedia[i] = mediaSizeNames[i];
}
for (int i=0; i<mediaTrays.length; i++) {
allMedia[i+mediaSizeNames.length] = mediaTrays[i];
}
if (allMedia.length == 0) {
allMedia = new Media[1];
allMedia[0] = (Media)getDefaultAttributeValue(Media.class);
}
return allMedia;
} else if (category == MediaPrintableArea.class) {
MediaPrintableArea[] mpas = null;
if (cps != null) {
mpas = cps.getMediaPrintableArea();
}
if (mpas == null) {
mpas = new MediaPrintableArea[1];
mpas[0] = (MediaPrintableArea)
getDefaultAttributeValue(MediaPrintableArea.class);
}
if ((attributes == null) || (attributes.size() == 0)) {
ArrayList<MediaPrintableArea> printableList =
new ArrayList<MediaPrintableArea>();
for (int i=0; i<mpas.length; i++) {
if (mpas[i] != null) {
printableList.add(mpas[i]);
}
}
if (printableList.size() > 0) {
mpas = new MediaPrintableArea[printableList.size()];
printableList.toArray(mpas);
}
return mpas;
}
int match = -1;
Media media = (Media)attributes.get(Media.class);
if (media != null && media instanceof MediaSizeName) {
MediaSizeName msn = (MediaSizeName)media;
// case when no supported mediasizenames are reported
// check given media against the default
if (mediaSizeNames.length == 0 &&
msn.equals(getDefaultAttributeValue(Media.class))) {
//default printable area is that of default mediasize
return mpas;
}
for (int i=0; i<mediaSizeNames.length; i++) {
if (msn.equals(mediaSizeNames[i])) {
match = i;
}
}
}
if (match == -1) {
return null;
} else {
MediaPrintableArea []arr = new MediaPrintableArea[1];
arr[0] = mpas[match];
return arr;
}
} else if (category == NumberUp.class) {
AttributeClass attribClass = (getAttMap != null) ?
getAttMap.get("number-up-supported") : null;
if (attribClass != null) {
int[] values = attribClass.getArrayOfIntValues();
if (values != null) {
NumberUp[] nUp = new NumberUp[values.length];
for (int i=0; i<values.length; i++) {
nUp[i] = new NumberUp(values[i]);
}
return nUp;
} else {
return null;
}
}
} else if (category == OrientationRequested.class) {
if ((flavor != null) &&
(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||
flavor.equals(DocFlavor.URL.POSTSCRIPT) ||
flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) {
return null;
}
boolean revPort = false;
OrientationRequested[] orientSup = null;
AttributeClass attribClass = (getAttMap != null) ?
getAttMap.get("orientation-requested-supported")
: null;
if (attribClass != null) {
int[] orientArray = attribClass.getArrayOfIntValues();
if ((orientArray != null) && (orientArray.length > 0)) {
orientSup =
new OrientationRequested[orientArray.length];
for (int i=0; i<orientArray.length; i++) {
switch (orientArray[i]) {
default:
case 3 :
orientSup[i] = OrientationRequested.PORTRAIT;
break;
case 4:
orientSup[i] = OrientationRequested.LANDSCAPE;
break;
case 5:
orientSup[i] =
OrientationRequested.REVERSE_LANDSCAPE;
break;
case 6:
orientSup[i] =
OrientationRequested.REVERSE_PORTRAIT;
revPort = true;
break;
}
}
}
}
if (flavor == null ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
if (revPort && flavor == null) {
OrientationRequested []orSup = new OrientationRequested[4];
orSup[0] = OrientationRequested.PORTRAIT;
orSup[1] = OrientationRequested.LANDSCAPE;
orSup[2] = OrientationRequested.REVERSE_LANDSCAPE;
orSup[3] = OrientationRequested.REVERSE_PORTRAIT;
return orSup;
} else {
OrientationRequested []orSup = new OrientationRequested[3];
orSup[0] = OrientationRequested.PORTRAIT;
orSup[1] = OrientationRequested.LANDSCAPE;
orSup[2] = OrientationRequested.REVERSE_LANDSCAPE;
return orSup;
}
} else {
return orientSup;
}
} else if (category == PageRanges.class) {
if (flavor == null ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
PageRanges []arr = new PageRanges[1];
arr[0] = new PageRanges(1, Integer.MAX_VALUE);
return arr;
} else {
// Returning null as this is not yet supported in UnixPrintJob.
return null;
}
} else if (category == RequestingUserName.class) {
String userName = "";
try {
userName = System.getProperty("user.name", "");
} catch (SecurityException se) {
}
return new RequestingUserName(userName, null);
} else if (category == Sides.class) {
// The printer takes care of Sides so if short-edge
// is chosen in a job, the rotation is done by the printer.
// Orientation is rotated by emulation if pageable
// or printable so if the document is in Landscape, this may
// result in double rotation.
AttributeClass attribClass = (getAttMap != null) ?
getAttMap.get("sides-supported")
: null;
if (attribClass != null) {
String[] sidesArray = attribClass.getArrayOfStringValues();
if ((sidesArray != null) && (sidesArray.length > 0)) {
Sides[] sidesSup = new Sides[sidesArray.length];
for (int i=0; i<sidesArray.length; i++) {
if (sidesArray[i].endsWith("long-edge")) {
sidesSup[i] = Sides.TWO_SIDED_LONG_EDGE;
} else if (sidesArray[i].endsWith("short-edge")) {
sidesSup[i] = Sides.TWO_SIDED_SHORT_EDGE;
} else {
sidesSup[i] = Sides.ONE_SIDED;
}
}
return sidesSup;
}
}
} else if (category == PrinterResolution.class) {
PrinterResolution[] supportedRes = getPrintResolutions();
if (supportedRes == null) {
return null;
}
PrinterResolution []arr =
new PrinterResolution[supportedRes.length];
System.arraycopy(supportedRes, 0, arr, 0, supportedRes.length);
return arr;
}
return null;
}
//This class is for getting all pre-defined Finishings
@SuppressWarnings("serial") // JDK implementation class
private class ExtFinishing extends Finishings {
ExtFinishing(int value) {
super(100); // 100 to avoid any conflicts with predefined values.
}
EnumSyntax[] getAll() {
EnumSyntax[] es = super.getEnumValueTable();
return es;
}
}
public AttributeSet getUnsupportedAttributes(DocFlavor flavor,
AttributeSet attributes) {
if (flavor != null && !isDocFlavorSupported(flavor)) {
throw new IllegalArgumentException("flavor " + flavor +
"is not supported");
}
if (attributes == null) {
return null;
}
Attribute attr;
AttributeSet unsupp = new HashAttributeSet();
Attribute []attrs = attributes.toArray();
for (int i=0; i<attrs.length; i++) {
try {
attr = attrs[i];
if (!isAttributeCategorySupported(attr.getCategory())) {
unsupp.add(attr);
} else if (!isAttributeValueSupported(attr, flavor,
attributes)) {
unsupp.add(attr);
}
} catch (ClassCastException e) {
}
}
if (unsupp.isEmpty()) {
return null;
} else {
return unsupp;
}
}
public synchronized DocFlavor[] getSupportedDocFlavors() {
if (supportedDocFlavors != null) {
int len = supportedDocFlavors.length;
DocFlavor[] copyflavors = new DocFlavor[len];
System.arraycopy(supportedDocFlavors, 0, copyflavors, 0, len);
return copyflavors;
}
initAttributes();
if ((getAttMap != null) &&
getAttMap.containsKey("document-format-supported")) {
AttributeClass attribClass =
getAttMap.get("document-format-supported");
if (attribClass != null) {
String mimeType;
boolean psSupported = false;
String[] docFlavors = attribClass.getArrayOfStringValues();
DocFlavor[] flavors;
HashSet<Object> docList = new HashSet<>();
int j;
String hostEnc = DocFlavor.hostEncoding.
toLowerCase(Locale.ENGLISH);
boolean addHostEncoding = !hostEnc.equals("utf-8") &&
!hostEnc.equals("utf-16") && !hostEnc.equals("utf-16be") &&
!hostEnc.equals("utf-16le") && !hostEnc.equals("us-ascii");
for (int i = 0; i < docFlavors.length; i++) {
for (j=0; j<allDocFlavors.length; j++) {
flavors = (DocFlavor[])allDocFlavors[j];
mimeType = flavors[0].getMimeType();
if (mimeType.startsWith(docFlavors[i])) {
docList.addAll(Arrays.asList(flavors));
if (mimeType.equals("text/plain") &&
addHostEncoding) {
docList.add(Arrays.asList(textPlainHost));
} else if (mimeType.equals("text/html") &&
addHostEncoding) {
docList.add(Arrays.asList(textHtmlHost));
} else if (mimeType.equals("image/png")) {
pngImagesAdded = true;
} else if (mimeType.equals("image/gif")) {
gifImagesAdded = true;
} else if (mimeType.equals("image/jpeg")) {
jpgImagesAdded = true;
} else if (mimeType.indexOf("postscript") != -1) {
psSupported = true;
}
break;
}
}
// Not added? Create new DocFlavors
if (j == allDocFlavors.length) {
// make new DocFlavors
docList.add(new DocFlavor.BYTE_ARRAY(docFlavors[i]));
docList.add(new DocFlavor.INPUT_STREAM(docFlavors[i]));
docList.add(new DocFlavor.URL(docFlavors[i]));
}
}
// check if we need to add image DocFlavors
// and Pageable/Printable flavors
if (psSupported || isCupsPrinter) {
/*
Always add Pageable and Printable for CUPS
since it uses Filters to convert from Postscript
to device printer language.
*/
docList.add(DocFlavor.SERVICE_FORMATTED.PAGEABLE);
docList.add(DocFlavor.SERVICE_FORMATTED.PRINTABLE);
docList.addAll(Arrays.asList(imageJPG));
docList.addAll(Arrays.asList(imagePNG));
docList.addAll(Arrays.asList(imageGIF));
}
supportedDocFlavors = new DocFlavor[docList.size()];
docList.toArray(supportedDocFlavors);
int len = supportedDocFlavors.length;
DocFlavor[] copyflavors = new DocFlavor[len];
System.arraycopy(supportedDocFlavors, 0, copyflavors, 0, len);
return copyflavors;
}
}
DocFlavor[] flavor = new DocFlavor[2];
flavor[0] = DocFlavor.SERVICE_FORMATTED.PAGEABLE;
flavor[1] = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
supportedDocFlavors = flavor;
return flavor;
}
public boolean isDocFlavorSupported(DocFlavor flavor) {
if (supportedDocFlavors == null) {
getSupportedDocFlavors();
}
if (supportedDocFlavors != null) {
for (int f=0; f<supportedDocFlavors.length; f++) {
if (flavor.equals(supportedDocFlavors[f])) {
return true;
}
}
}
return false;
}
/**
* Finds matching CustomMediaSizeName of given media.
*/
public CustomMediaSizeName findCustomMedia(MediaSizeName media) {
if (customMediaSizeNames == null) {
return null;
}
for (int i=0; i< customMediaSizeNames.length; i++) {
CustomMediaSizeName custom = customMediaSizeNames[i];
MediaSizeName msn = custom.getStandardMedia();
if (media.equals(msn)) {
return customMediaSizeNames[i];
}
}
return null;
}
/**
* Returns the matching standard Media using string comparison of names.
*/
private Media getIPPMedia(String mediaName) {
CustomMediaSizeName sampleSize = new CustomMediaSizeName("sample", "",
0, 0);
Media[] sizes = sampleSize.getSuperEnumTable();
for (int i=0; i<sizes.length; i++) {
if (mediaName.equals(""+sizes[i])) {
return sizes[i];
}
}
CustomMediaTray sampleTray = new CustomMediaTray("sample", "");
Media[] trays = sampleTray.getSuperEnumTable();
for (int i=0; i<trays.length; i++) {
if (mediaName.equals(""+trays[i])) {
return trays[i];
}
}
return null;
}
private Media[] getSupportedMedia() {
if ((getAttMap != null) &&
getAttMap.containsKey("media-supported")) {
AttributeClass attribClass = getAttMap.get("media-supported");
if (attribClass != null) {
String[] mediaVals = attribClass.getArrayOfStringValues();
Media msn;
Media[] mediaNames =
new Media[mediaVals.length];
for (int i=0; i<mediaVals.length; i++) {
msn = getIPPMedia(mediaVals[i]);
//REMIND: if null, create custom?
mediaNames[i] = msn;
}
return mediaNames;
}
}
return new Media[0];
}
public synchronized Class<?>[] getSupportedAttributeCategories() {
if (supportedCats != null) {
Class<?> [] copyCats = new Class<?>[supportedCats.length];
System.arraycopy(supportedCats, 0, copyCats, 0, copyCats.length);
return copyCats;
}
initAttributes();
ArrayList<Class<?>> catList = new ArrayList<>();
for (int i=0; i < printReqAttribDefault.length; i++) {
PrintRequestAttribute pra =
(PrintRequestAttribute)printReqAttribDefault[i];
if (getAttMap != null &&
getAttMap.containsKey(pra.getName()+"-supported")) {
catList.add(pra.getCategory());
}
}
// Some IPP printers like lexc710 do not have list of supported media
// but CUPS can get the media from PPD, so we still report as
// supported category.
if (isCupsPrinter) {
if (!catList.contains(Media.class)) {
catList.add(Media.class);
}
// Always add MediaPrintable for cups,
// because we can get it from PPD.
catList.add(MediaPrintableArea.class);
// this is already supported in UnixPrintJob
catList.add(Destination.class);
// It is unfortunate that CUPS doesn't provide a way to query
// if printer supports collation but since most printers
// now supports collation and that most OS has a way
// of setting it, it is a safe assumption to just always
// include SheetCollate as supported attribute.
catList.add(SheetCollate.class);
}
// With the assumption that Chromaticity is equivalent to
// ColorSupported.
if (getAttMap != null && getAttMap.containsKey("color-supported")) {
catList.add(Chromaticity.class);
}
// CUPS does not report printer resolution via IPP but it
// may be gleaned from the PPD.
PrinterResolution[] supportedRes = getPrintResolutions();
if (supportedRes != null && (supportedRes.length > 0)) {
catList.add(PrinterResolution.class);
}
supportedCats = new Class<?>[catList.size()];
catList.toArray(supportedCats);
Class<?>[] copyCats = new Class<?>[supportedCats.length];
System.arraycopy(supportedCats, 0, copyCats, 0, copyCats.length);
return copyCats;
}
public boolean
isAttributeCategorySupported(Class<? extends Attribute> category)
{
if (category == null) {
throw new NullPointerException("null category");
}
if (!(Attribute.class.isAssignableFrom(category))) {
throw new IllegalArgumentException(category +
" is not an Attribute");
}
if (supportedCats == null) {
getSupportedAttributeCategories();
}
// It is safe to assume that Orientation is always supported
// and even if CUPS or an IPP device reports it as not,
// our renderer can do portrait, landscape and
// reverse landscape.
if (category == OrientationRequested.class) {
return true;
}
for (int i=0;i<supportedCats.length;i++) {
if (category == supportedCats[i]) {
return true;
}
}
return false;
}
@SuppressWarnings("unchecked")
public synchronized <T extends PrintServiceAttribute>
T getAttribute(Class<T> category)
{
if (category == null) {
throw new NullPointerException("category");
}
if (!(PrintServiceAttribute.class.isAssignableFrom(category))) {
throw new IllegalArgumentException("Not a PrintServiceAttribute");
}
initAttributes();
if (category == PrinterName.class) {
return (T)(new PrinterName(printer, null));
} else if (category == PrinterInfo.class) {
PrinterInfo pInfo = new PrinterInfo(printer, null);
AttributeClass ac = (getAttMap != null) ?
getAttMap.get(pInfo.getName())
: null;
if (ac != null) {
return (T)(new PrinterInfo(ac.getStringValue(), null));
}
return (T)pInfo;
} else if (category == QueuedJobCount.class) {
QueuedJobCount qjc = new QueuedJobCount(0);
AttributeClass ac = (getAttMap != null) ?
getAttMap.get(qjc.getName())
: null;
if (ac != null) {
qjc = new QueuedJobCount(ac.getIntValue());
}
return (T)qjc;
} else if (category == PrinterIsAcceptingJobs.class) {
PrinterIsAcceptingJobs accJob =
PrinterIsAcceptingJobs.ACCEPTING_JOBS;
AttributeClass ac = (getAttMap != null) ?
getAttMap.get(accJob.getName())
: null;
if ((ac != null) && (ac.getByteValue() == 0)) {
accJob = PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS;
}
return (T)accJob;
} else if (category == ColorSupported.class) {
ColorSupported cs = ColorSupported.SUPPORTED;
AttributeClass ac = (getAttMap != null) ?
getAttMap.get(cs.getName())
: null;
if ((ac != null) && (ac.getByteValue() == 0)) {
cs = ColorSupported.NOT_SUPPORTED;
}
return (T)cs;
} else if (category == PDLOverrideSupported.class) {
if (isCupsPrinter) {
// Documented: For CUPS this will always be false
return (T)PDLOverrideSupported.NOT_ATTEMPTED;
} else {
// REMIND: check attribute values
return (T)PDLOverrideSupported.NOT_ATTEMPTED;
}
} else if (category == PrinterURI.class) {
return (T)(new PrinterURI(myURI));
} else {
return null;
}
}
public synchronized PrintServiceAttributeSet getAttributes() {
// update getAttMap by sending again get-attributes IPP request
init = false;
initAttributes();
HashPrintServiceAttributeSet attrs =
new HashPrintServiceAttributeSet();
for (int i=0; i < serviceAttributes.length; i++) {
String name = (String)serviceAttributes[i][1];
if (getAttMap != null && getAttMap.containsKey(name)) {
@SuppressWarnings("unchecked")
Class<PrintServiceAttribute> c = (Class<PrintServiceAttribute>)serviceAttributes[i][0];
PrintServiceAttribute psa = getAttribute(c);
if (psa != null) {
attrs.add(psa);
}
}
}
return AttributeSetUtilities.unmodifiableView(attrs);
}
public boolean isIPPSupportedImages(String mimeType) {
if (supportedDocFlavors == null) {
getSupportedDocFlavors();
}
if (mimeType.equals("image/png") && pngImagesAdded) {
return true;
} else if (mimeType.equals("image/gif") && gifImagesAdded) {
return true;
} else if (mimeType.equals("image/jpeg") && jpgImagesAdded) {
return true;
}
return false;
}
private boolean isSupportedCopies(Copies copies) {
CopiesSupported cs = (CopiesSupported)
getSupportedAttributeValues(Copies.class, null, null);
int[][] members = cs.getMembers();
int min, max;
if ((members.length > 0) && (members[0].length > 0)) {
min = members[0][0];
max = members[0][1];
} else {
min = 1;
max = MAXCOPIES;
}
int value = copies.getValue();
return (value >= min && value <= max);
}
private boolean isAutoSense(DocFlavor flavor) {
if (flavor.equals(DocFlavor.BYTE_ARRAY.AUTOSENSE) ||
flavor.equals(DocFlavor.INPUT_STREAM.AUTOSENSE) ||
flavor.equals(DocFlavor.URL.AUTOSENSE)) {
return true;
}
else {
return false;
}
}
private synchronized boolean isSupportedMediaTray(MediaTray msn) {
initAttributes();
if (mediaTrays != null) {
for (int i=0; i<mediaTrays.length; i++) {
if (msn.equals(mediaTrays[i])) {
return true;
}
}
}
return false;
}
private synchronized boolean isSupportedMedia(MediaSizeName msn) {
initAttributes();
if (msn.equals((Media)getDefaultAttributeValue(Media.class))) {
return true;
}
for (int i=0; i<mediaSizeNames.length; i++) {
debug_println(debugPrefix+"isSupportedMedia, mediaSizeNames[i] "+mediaSizeNames[i]);
if (msn.equals(mediaSizeNames[i])) {
return true;
}
}
return false;
}
/* Return false if flavor is not null, pageable, nor printable and
* Destination is part of attributes.
*/
private boolean
isDestinationSupported(DocFlavor flavor, AttributeSet attributes) {
if ((attributes != null) &&
(attributes.get(Destination.class) != null) &&
!(flavor == null ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
return false;
}
return true;
}
public boolean isAttributeValueSupported(Attribute attr,
DocFlavor flavor,
AttributeSet attributes) {
if (attr == null) {
throw new NullPointerException("null attribute");
}
if (flavor != null) {
if (!isDocFlavorSupported(flavor)) {
throw new IllegalArgumentException(flavor +
" is an unsupported flavor");
} else if (isAutoSense(flavor)) {
return false;
}
}
Class<? extends Attribute> category = attr.getCategory();
if (!isAttributeCategorySupported(category)) {
return false;
}
/* Test if the flavor is compatible with the attributes */
if (!isDestinationSupported(flavor, attributes)) {
return false;
}
/* Test if the flavor is compatible with the category */
if (attr.getCategory() == Chromaticity.class) {
if ((flavor == null) ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||
!isIPPSupportedImages(flavor.getMimeType())) {
return attr == Chromaticity.COLOR;
} else {
return false;
}
} else if (attr.getCategory() == Copies.class) {
return (flavor == null ||
!(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||
flavor.equals(DocFlavor.URL.POSTSCRIPT) ||
flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) &&
isSupportedCopies((Copies)attr);
} else if (attr.getCategory() == Destination.class) {
if (flavor == null ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
URI uri = ((Destination)attr).getURI();
if ("file".equals(uri.getScheme()) &&
!(uri.getSchemeSpecificPart().equals(""))) {
return true;
}
}
return false;
} else if (attr.getCategory() == Media.class) {
if (attr instanceof MediaSizeName) {
return isSupportedMedia((MediaSizeName)attr);
}
if (attr instanceof MediaTray) {
return isSupportedMediaTray((MediaTray)attr);
}
} else if (attr.getCategory() == PageRanges.class) {
if (flavor != null &&
!(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
return false;
}
} else if (attr.getCategory() == SheetCollate.class) {
if (flavor != null &&
!(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
return false;
}
} else if (attr.getCategory() == Sides.class) {
Sides[] sidesArray = (Sides[])getSupportedAttributeValues(
Sides.class,
flavor,
attributes);
if (sidesArray != null) {
for (int i=0; i<sidesArray.length; i++) {
if (sidesArray[i] == (Sides)attr) {
return true;
}
}
}
return false;
} else if (attr.getCategory() == OrientationRequested.class) {
OrientationRequested[] orientArray =
(OrientationRequested[])getSupportedAttributeValues(
OrientationRequested.class,
flavor,
attributes);
if (orientArray != null) {
for (int i=0; i<orientArray.length; i++) {
if (orientArray[i] == (OrientationRequested)attr) {
return true;
}
}
}
return false;
} if (attr.getCategory() == PrinterResolution.class) {
if (attr instanceof PrinterResolution) {
return isSupportedResolution((PrinterResolution)attr);
}
}
return true;
}
public synchronized Object
getDefaultAttributeValue(Class<? extends Attribute> category)
{
if (category == null) {
throw new NullPointerException("null category");
}
if (!Attribute.class.isAssignableFrom(category)) {
throw new IllegalArgumentException(category +
" is not an Attribute");
}
if (!isAttributeCategorySupported(category)) {
return null;
}
initAttributes();
String catName = null;
for (int i=0; i < printReqAttribDefault.length; i++) {
PrintRequestAttribute pra =
(PrintRequestAttribute)printReqAttribDefault[i];
if (pra.getCategory() == category) {
catName = pra.getName();
break;
}
}
String attribName = catName+"-default";
AttributeClass attribClass = (getAttMap != null) ?
getAttMap.get(attribName) : null;
if (category == Copies.class) {
if (attribClass != null) {
return new Copies(attribClass.getIntValue());
} else {
return new Copies(1);
}
} else if (category == Chromaticity.class) {
return Chromaticity.COLOR;
} else if (category == Destination.class) {
try {
return new Destination((new File("out.ps")).toURI());
} catch (SecurityException se) {
try {
return new Destination(new URI("file:out.ps"));
} catch (URISyntaxException e) {
return null;
}
}
} else if (category == Fidelity.class) {
return Fidelity.FIDELITY_FALSE;
} else if (category == Finishings.class) {
return Finishings.NONE;
} else if (category == JobName.class) {
return new JobName("Java Printing", null);
} else if (category == JobSheets.class) {
if (attribClass != null &&
attribClass.getStringValue().equals("none")) {
return JobSheets.NONE;
} else {
return JobSheets.STANDARD;
}
} else if (category == Media.class) {
if (defaultMediaIndex == -1) {
defaultMediaIndex = 0;
}
if (mediaSizeNames.length == 0) {
String defaultCountry = Locale.getDefault().getCountry();
if (defaultCountry != null &&
(defaultCountry.equals("") ||
defaultCountry.equals(Locale.US.getCountry()) ||
defaultCountry.equals(Locale.CANADA.getCountry()))) {
return MediaSizeName.NA_LETTER;
} else {
return MediaSizeName.ISO_A4;
}
}
if (attribClass != null) {
String name = attribClass.getStringValue();
if (isCupsPrinter) {
return mediaSizeNames[defaultMediaIndex];
} else {
for (int i=0; i< mediaSizeNames.length; i++) {
if (mediaSizeNames[i].toString().indexOf(name) != -1) {
defaultMediaIndex = i;
return mediaSizeNames[defaultMediaIndex];
}
}
}
}
return mediaSizeNames[defaultMediaIndex];
} else if (category == MediaPrintableArea.class) {
MediaPrintableArea[] mpas;
if ((cps != null) &&
((mpas = cps.getMediaPrintableArea()) != null)) {
if (defaultMediaIndex == -1) {
// initializes value of defaultMediaIndex
getDefaultAttributeValue(Media.class);
}
return mpas[defaultMediaIndex];
} else {
String defaultCountry = Locale.getDefault().getCountry();
float iw, ih;
if (defaultCountry != null &&
(defaultCountry.equals("") ||
defaultCountry.equals(Locale.US.getCountry()) ||
defaultCountry.equals(Locale.CANADA.getCountry()))) {
iw = MediaSize.NA.LETTER.getX(Size2DSyntax.INCH) - 0.5f;
ih = MediaSize.NA.LETTER.getY(Size2DSyntax.INCH) - 0.5f;
} else {
iw = MediaSize.ISO.A4.getX(Size2DSyntax.INCH) - 0.5f;
ih = MediaSize.ISO.A4.getY(Size2DSyntax.INCH) - 0.5f;
}
return new MediaPrintableArea(0.25f, 0.25f, iw, ih,
MediaPrintableArea.INCH);
}
} else if (category == NumberUp.class) {
return new NumberUp(1); // for CUPS this is always 1
} else if (category == OrientationRequested.class) {
if (attribClass != null) {
switch (attribClass.getIntValue()) {
default:
case 3: return OrientationRequested.PORTRAIT;
case 4: return OrientationRequested.LANDSCAPE;
case 5: return OrientationRequested.REVERSE_LANDSCAPE;
case 6: return OrientationRequested.REVERSE_PORTRAIT;
}
} else {
return OrientationRequested.PORTRAIT;
}
} else if (category == PageRanges.class) {
if (attribClass != null) {
int[] range = attribClass.getIntRangeValue();
return new PageRanges(range[0], range[1]);
} else {
return new PageRanges(1, Integer.MAX_VALUE);
}
} else if (category == RequestingUserName.class) {
String userName = "";
try {
userName = System.getProperty("user.name", "");
} catch (SecurityException se) {
}
return new RequestingUserName(userName, null);
} else if (category == SheetCollate.class) {
return SheetCollate.UNCOLLATED;
} else if (category == Sides.class) {
if (attribClass != null) {
if (attribClass.getStringValue().endsWith("long-edge")) {
return Sides.TWO_SIDED_LONG_EDGE;
} else if (attribClass.getStringValue().endsWith(
"short-edge")) {
return Sides.TWO_SIDED_SHORT_EDGE;
}
}
return Sides.ONE_SIDED;
} else if (category == PrinterResolution.class) {
PrinterResolution[] supportedRes = getPrintResolutions();
if ((supportedRes != null) && (supportedRes.length > 0)) {
return supportedRes[0];
} else {
return new PrinterResolution(300, 300, PrinterResolution.DPI);
}
}
return null;
}
private PrinterResolution[] getPrintResolutions() {
if (printerResolutions == null) {
if (rawResolutions == null) {
printerResolutions = new PrinterResolution[0];
} else {
int numRes = rawResolutions.length / 2;
PrinterResolution[] pres = new PrinterResolution[numRes];
for (int i=0; i < numRes; i++) {
pres[i] = new PrinterResolution(rawResolutions[i*2],
rawResolutions[i*2+1],
PrinterResolution.DPI);
}
printerResolutions = pres;
}
}
return printerResolutions;
}
private boolean isSupportedResolution(PrinterResolution res) {
PrinterResolution[] supportedRes = getPrintResolutions();
if (supportedRes != null) {
for (int i=0; i<supportedRes.length; i++) {
if (res.equals(supportedRes[i])) {
return true;
}
}
}
return false;
}
public ServiceUIFactory getServiceUIFactory() {
return null;
}
public void wakeNotifier() {
synchronized (this) {
if (notifier != null) {
notifier.wake();
}
}
}
public void addPrintServiceAttributeListener(
PrintServiceAttributeListener listener) {
synchronized (this) {
if (listener == null) {
return;
}
if (notifier == null) {
notifier = new ServiceNotifier(this);
}
notifier.addListener(listener);
}
}
public void removePrintServiceAttributeListener(
PrintServiceAttributeListener listener) {
synchronized (this) {
if (listener == null || notifier == null ) {
return;
}
notifier.removeListener(listener);
if (notifier.isEmpty()) {
notifier.stopNotifier();
notifier = null;
}
}
}
String getDest() {
return printer;
}
public String getName() {
/*
* Mac is using printer-info IPP attribute for its human-readable printer
* name and is also the identifier used in NSPrintInfo:setPrinter.
*/
if (PrintServiceLookupProvider.isMac()) {
PrintServiceAttributeSet psaSet = this.getAttributes();
if (psaSet != null) {
PrinterInfo pName = (PrinterInfo)psaSet.get(PrinterInfo.class);
if (pName != null) {
return pName.toString();
}
}
}
return printer;
}
public boolean usesClass(Class<?> c) {
return (c == sun.print.PSPrinterJob.class);
}
public static HttpURLConnection getIPPConnection(URL url) {
HttpURLConnection connection;
URLConnection urlc;
try {
urlc = url.openConnection();
} catch (java.io.IOException ioe) {
return null;
}
if (!(urlc instanceof HttpURLConnection)) {
return null;
}
connection = (HttpURLConnection)urlc;
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("Content-type", "application/ipp");
return connection;
}
public synchronized boolean isPostscript() {
if (isPS == null) {
isPS = Boolean.TRUE;
if (isCupsPrinter) {
try {
urlConnection = getIPPConnection(
new URL(myURL+".ppd"));
InputStream is = urlConnection.getInputStream();
if (is != null) {
BufferedReader d =
new BufferedReader(new InputStreamReader(is,
Charset.forName("ISO-8859-1")));
String lineStr;
while ((lineStr = d.readLine()) != null) {
if (lineStr.startsWith("*cupsFilter:")) {
isPS = Boolean.FALSE;
break;
}
}
}
} catch (java.io.IOException e) {
debug_println(" isPostscript, e= "+e);
/* if PPD is not found, this may be a raw printer
and in this case it is assumed that it is a
Postscript printer */
// do nothing
}
}
}
return isPS.booleanValue();
}
private void opGetAttributes() {
try {
debug_println(debugPrefix+"opGetAttributes myURI "+myURI+" myURL "+myURL);
AttributeClass attClNoUri[] = {
AttributeClass.ATTRIBUTES_CHARSET,
AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE};
AttributeClass attCl[] = {
AttributeClass.ATTRIBUTES_CHARSET,
AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE,
new AttributeClass("printer-uri",
AttributeClass.TAG_URI,
""+myURI)};
OutputStream os = java.security.AccessController.
doPrivileged(new java.security.PrivilegedAction<OutputStream>() {
public OutputStream run() {
try {
return urlConnection.getOutputStream();
} catch (Exception e) {
}
return null;
}
});
if (os == null) {
return;
}
boolean success = (myURI == null) ?
writeIPPRequest(os, OP_GET_ATTRIBUTES, attClNoUri) :
writeIPPRequest(os, OP_GET_ATTRIBUTES, attCl);
if (success) {
InputStream is = null;
if ((is = urlConnection.getInputStream())!=null) {
HashMap<String, AttributeClass>[] responseMap = readIPPResponse(is);
if (responseMap != null && responseMap.length > 0) {
getAttMap = responseMap[0];
}
} else {
debug_println(debugPrefix+"opGetAttributes - null input stream");
}
is.close();
}
os.close();
} catch (java.io.IOException e) {
debug_println(debugPrefix+"opGetAttributes - input/output stream: "+e);
}
}
public static boolean writeIPPRequest(OutputStream os,
String operCode,
AttributeClass[] attCl) {
OutputStreamWriter osw;
try {
osw = new OutputStreamWriter(os, "UTF-8");
} catch (java.io.UnsupportedEncodingException exc) {
debug_println(debugPrefix+"writeIPPRequest, UTF-8 not supported? Exception: "+exc);
return false;
}
debug_println(debugPrefix+"writeIPPRequest, op code= "+operCode);
char[] opCode = new char[2];
opCode[0] = (char)Byte.parseByte(operCode.substring(0,2), 16);
opCode[1] = (char)Byte.parseByte(operCode.substring(2,4), 16);
char[] bytes = {0x01, 0x01, 0x00, 0x01};
try {
osw.write(bytes, 0, 2); // version number
osw.write(opCode, 0, 2); // operation code
bytes[0] = 0x00; bytes[1] = 0x00;
osw.write(bytes, 0, 4); // request ID #1
bytes[0] = 0x01; // operation-group-tag
osw.write(bytes[0]);
String valStr;
char[] lenStr;
AttributeClass ac;
for (int i=0; i < attCl.length; i++) {
ac = attCl[i];
osw.write(ac.getType()); // value tag
lenStr = ac.getLenChars();
osw.write(lenStr, 0, 2); // length
osw.write(""+ac, 0, ac.getName().length());
// check if string range (0x35 -> 0x49)
if (ac.getType() >= AttributeClass.TAG_TEXT_LANGUAGE &&
ac.getType() <= AttributeClass.TAG_MIME_MEDIATYPE){
valStr = (String)ac.getObjectValue();
bytes[0] = 0; bytes[1] = (char)valStr.length();
osw.write(bytes, 0, 2);
osw.write(valStr, 0, valStr.length());
} // REMIND: need to support other value tags but for CUPS
// string is all we need.
}
osw.write(GRPTAG_END_ATTRIBUTES);
osw.flush();
osw.close();
} catch (java.io.IOException ioe) {
debug_println(debugPrefix+"writeIPPRequest, IPPPrintService Exception in writeIPPRequest: "+ioe);
return false;
}
return true;
}
public static HashMap<String, AttributeClass>[] readIPPResponse(InputStream inputStream) {
if (inputStream == null) {
return null;
}
byte response[] = new byte[MAX_ATTRIBUTE_LENGTH];
try {
DataInputStream ois = new DataInputStream(inputStream);
// read status and ID
if ((ois.read(response, 0, 8) > -1) &&
(response[2] == STATUSCODE_SUCCESS)) {
ByteArrayOutputStream outObj;
int counter=0;
short len = 0;
String attribStr = null;
// assign default value
byte valTagByte = AttributeClass.TAG_KEYWORD;
ArrayList<HashMap<String, AttributeClass>> respList = new ArrayList<>();
HashMap<String, AttributeClass> responseMap = new HashMap<>();
response[0] = ois.readByte();
// check for group tags
while ((response[0] >= GRPTAG_OP_ATTRIBUTES) &&
(response[0] <= GRPTAG_PRINTER_ATTRIBUTES)
&& (response[0] != GRPTAG_END_ATTRIBUTES)) {
debug_println(debugPrefix+"readIPPResponse, checking group tag, response[0]= "+
response[0]);
outObj = new ByteArrayOutputStream();
//make sure counter and attribStr are re-initialized
counter = 0;
attribStr = null;
// read value tag
response[0] = ois.readByte();
while (response[0] >= AttributeClass.TAG_UNSUPPORTED_VALUE &&
response[0] <= AttributeClass.TAG_MEMBER_ATTRNAME) {
// read name length
len = ois.readShort();
// If current value is not part of previous attribute
// then close stream and add it to HashMap.
// It is part of previous attribute if name length=0.
if ((len != 0) && (attribStr != null)) {
//last byte is the total # of values
outObj.write(counter);
outObj.flush();
outObj.close();
byte outArray[] = outObj.toByteArray();
// if key exists, new HashMap
if (responseMap.containsKey(attribStr)) {
respList.add(responseMap);
responseMap = new HashMap<>();
}
// exclude those that are unknown
if (valTagByte >= AttributeClass.TAG_INT) {
AttributeClass ac =
new AttributeClass(attribStr,
valTagByte,
outArray);
responseMap.put(ac.getName(), ac);
debug_println(debugPrefix+ "readIPPResponse "+ac);
}
outObj = new ByteArrayOutputStream();
counter = 0; //reset counter
}
//check if this is new value tag
if (counter == 0) {
valTagByte = response[0];
}
// read attribute name
if (len != 0) {
// read "len" characters
// make sure it doesn't exceed the maximum
if (len > MAX_ATTRIBUTE_LENGTH) {
response = new byte[len]; // expand as needed
}
ois.read(response, 0, len);
attribStr = new String(response, 0, len);
}
// read value length
len = ois.readShort();
// write name length
outObj.write(len);
// read value, make sure it doesn't exceed the maximum
if (len > MAX_ATTRIBUTE_LENGTH) {
response = new byte[len]; // expand as needed
}
ois.read(response, 0, len);
// write value of "len" length
outObj.write(response, 0, len);
counter++;
// read next byte
response[0] = ois.readByte();
}
if (attribStr != null) {
outObj.write(counter);
outObj.flush();
outObj.close();
// if key exists in old HashMap, new HashMap
if ((counter != 0) &&
responseMap.containsKey(attribStr)) {
respList.add(responseMap);
responseMap = new HashMap<>();
}
byte outArray[] = outObj.toByteArray();
AttributeClass ac =
new AttributeClass(attribStr,
valTagByte,
outArray);
responseMap.put(ac.getName(), ac);
}
}
ois.close();
if ((responseMap != null) && (responseMap.size() > 0)) {
respList.add(responseMap);
}
@SuppressWarnings({"unchecked", "rawtypes"})
HashMap<String, AttributeClass>[] tmp =
respList.toArray((HashMap<String, AttributeClass>[])new HashMap[respList.size()]);
return tmp;
} else {
debug_println(debugPrefix+
"readIPPResponse client error, IPP status code: 0x"+
toHex(response[2]) + toHex(response[3]));
return null;
}
} catch (java.io.IOException e) {
debug_println(debugPrefix+"readIPPResponse: "+e);
if (debugPrint) {
e.printStackTrace();
}
return null;
}
}
private static String toHex(byte v) {
String s = Integer.toHexString(v&0xff);
return (s.length() == 2) ? s : "0"+s;
}
public String toString() {
return "IPP Printer : " + getName();
}
public boolean equals(Object obj) {
return (obj == this ||
(obj instanceof IPPPrintService &&
((IPPPrintService)obj).getName().equals(getName())));
}
public int hashCode() {
return this.getClass().hashCode()+getName().hashCode();
}
}