Path: blob/master/src/java.desktop/unix/classes/sun/print/IPPPrintService.java
41152 views
/*1* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.print;2627import java.awt.GraphicsEnvironment;28import java.awt.Toolkit;29import javax.print.attribute.*;30import javax.print.attribute.standard.*;31import javax.print.DocFlavor;32import javax.print.DocPrintJob;33import javax.print.PrintService;34import javax.print.ServiceUIFactory;35import java.util.ArrayList;36import java.util.HashMap;37import java.util.Locale;38import java.util.Date;39import java.util.Arrays;40import java.security.AccessController;41import java.security.PrivilegedActionException;42import java.security.PrivilegedExceptionAction;43import javax.print.event.PrintServiceAttributeListener;4445import java.net.URI;46import java.net.URISyntaxException;47import java.net.URL;48import java.net.URLConnection;49import java.net.HttpURLConnection;50import java.io.File;51import java.io.InputStream;52import java.io.OutputStream;53import java.io.OutputStreamWriter;54import java.io.DataInputStream;55import java.io.ByteArrayOutputStream;56import java.io.ByteArrayInputStream;57import java.io.BufferedReader;58import java.io.InputStreamReader;59import java.nio.charset.Charset;6061import java.util.Iterator;62import java.util.HashSet;63import java.util.Map;646566public class IPPPrintService implements PrintService, SunPrinterJobService {6768public static final boolean debugPrint;69private static final String debugPrefix = "IPPPrintService>> ";70protected static void debug_println(String str) {71if (debugPrint) {72System.out.println(str);73}74}7576private static final String FORCE_PIPE_PROP = "sun.print.ippdebug";7778static {79@SuppressWarnings("removal")80String debugStr = java.security.AccessController.doPrivileged(81new sun.security.action.GetPropertyAction(FORCE_PIPE_PROP));8283debugPrint = "true".equalsIgnoreCase(debugStr);84}8586private String printer;87private URI myURI;88private URL myURL;89private transient ServiceNotifier notifier = null;9091private static int MAXCOPIES = 1000;92private static short MAX_ATTRIBUTE_LENGTH = 255;9394private CUPSPrinter cps;95private HttpURLConnection urlConnection = null;96private DocFlavor[] supportedDocFlavors;97private Class<?>[] supportedCats;98private MediaTray[] mediaTrays;99private MediaSizeName[] mediaSizeNames;100private CustomMediaSizeName[] customMediaSizeNames;101private int defaultMediaIndex;102private int[] rawResolutions = null;103private PrinterResolution[] printerResolutions = null;104private boolean isCupsPrinter;105private boolean init;106private Boolean isPS;107private HashMap<String, AttributeClass> getAttMap;108private boolean pngImagesAdded = false;109private boolean gifImagesAdded = false;110private boolean jpgImagesAdded = false;111112113/**114* IPP Status Codes115*/116private static final byte STATUSCODE_SUCCESS = 0x00;117118/**119* IPP Group Tags. Each tag is used once before the first attribute120* of that group.121*/122// operation attributes group123private static final byte GRPTAG_OP_ATTRIBUTES = 0x01;124// job attributes group125private static final byte GRPTAG_JOB_ATTRIBUTES = 0x02;126// printer attributes group127private static final byte GRPTAG_PRINTER_ATTRIBUTES = 0x04;128// used as the last tag in an IPP message.129private static final byte GRPTAG_END_ATTRIBUTES = 0x03;130131/**132* IPP Operation codes133*/134// gets the attributes for a printer135public static final String OP_GET_ATTRIBUTES = "000B";136// gets the default printer137public static final String OP_CUPS_GET_DEFAULT = "4001";138// gets the list of printers139public static final String OP_CUPS_GET_PRINTERS = "4002";140141142/**143* List of all PrintRequestAttributes. This is used144* for looping through all the IPP attribute name.145*/146private static Object[] printReqAttribDefault = {147Chromaticity.COLOR,148new Copies(1),149Fidelity.FIDELITY_FALSE,150Finishings.NONE,151//new JobHoldUntil(new Date()),152//new JobImpressions(0),153//JobImpressions,154//JobKOctets,155//JobMediaSheets,156new JobName("", Locale.getDefault()),157//JobPriority,158JobSheets.NONE,159(Media)MediaSizeName.NA_LETTER,160//MediaPrintableArea.class, // not an IPP attribute161//MultipleDocumentHandling.SINGLE_DOCUMENT,162new NumberUp(1),163OrientationRequested.PORTRAIT,164new PageRanges(1),165//PresentationDirection,166// CUPS does not supply printer-resolution attribute167//new PrinterResolution(300, 300, PrinterResolution.DPI),168//PrintQuality.NORMAL,169new RequestingUserName("", Locale.getDefault()),170//SheetCollate.UNCOLLATED, //CUPS has no sheet collate?171Sides.ONE_SIDED,172};173174175/**176* List of all PrintServiceAttributes. This is used177* for looping through all the IPP attribute name.178*/179private static Object[][] serviceAttributes = {180{ColorSupported.class, "color-supported"},181{PagesPerMinute.class, "pages-per-minute"},182{PagesPerMinuteColor.class, "pages-per-minute-color"},183{PDLOverrideSupported.class, "pdl-override-supported"},184{PrinterInfo.class, "printer-info"},185{PrinterIsAcceptingJobs.class, "printer-is-accepting-jobs"},186{PrinterLocation.class, "printer-location"},187{PrinterMakeAndModel.class, "printer-make-and-model"},188{PrinterMessageFromOperator.class, "printer-message-from-operator"},189{PrinterMoreInfo.class, "printer-more-info"},190{PrinterMoreInfoManufacturer.class, "printer-more-info-manufacturer"},191{PrinterName.class, "printer-name"},192{PrinterState.class, "printer-state"},193{PrinterStateReasons.class, "printer-state-reasons"},194{PrinterURI.class, "printer-uri"},195{QueuedJobCount.class, "queued-job-count"}196};197198199/**200* List of DocFlavors, grouped based on matching mime-type.201* NOTE: For any change in the predefined DocFlavors, it must be reflected202* here also.203*/204// PDF DocFlavors205private static DocFlavor[] appPDF = {206DocFlavor.BYTE_ARRAY.PDF,207DocFlavor.INPUT_STREAM.PDF,208DocFlavor.URL.PDF209};210211// Postscript DocFlavors212private static DocFlavor[] appPostScript = {213DocFlavor.BYTE_ARRAY.POSTSCRIPT,214DocFlavor.INPUT_STREAM.POSTSCRIPT,215DocFlavor.URL.POSTSCRIPT216};217218// Autosense DocFlavors219private static DocFlavor[] appOctetStream = {220DocFlavor.BYTE_ARRAY.AUTOSENSE,221DocFlavor.INPUT_STREAM.AUTOSENSE,222DocFlavor.URL.AUTOSENSE223};224225// Text DocFlavors226private static DocFlavor[] textPlain = {227DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_8,228DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16,229DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16BE,230DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16LE,231DocFlavor.BYTE_ARRAY.TEXT_PLAIN_US_ASCII,232DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_8,233DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16,234DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16BE,235DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16LE,236DocFlavor.INPUT_STREAM.TEXT_PLAIN_US_ASCII,237DocFlavor.URL.TEXT_PLAIN_UTF_8,238DocFlavor.URL.TEXT_PLAIN_UTF_16,239DocFlavor.URL.TEXT_PLAIN_UTF_16BE,240DocFlavor.URL.TEXT_PLAIN_UTF_16LE,241DocFlavor.URL.TEXT_PLAIN_US_ASCII,242DocFlavor.CHAR_ARRAY.TEXT_PLAIN,243DocFlavor.STRING.TEXT_PLAIN,244DocFlavor.READER.TEXT_PLAIN245};246247private static DocFlavor[] textPlainHost = {248DocFlavor.BYTE_ARRAY.TEXT_PLAIN_HOST,249DocFlavor.INPUT_STREAM.TEXT_PLAIN_HOST,250DocFlavor.URL.TEXT_PLAIN_HOST251};252253// JPG DocFlavors254private static DocFlavor[] imageJPG = {255DocFlavor.BYTE_ARRAY.JPEG,256DocFlavor.INPUT_STREAM.JPEG,257DocFlavor.URL.JPEG258};259260// GIF DocFlavors261private static DocFlavor[] imageGIF = {262DocFlavor.BYTE_ARRAY.GIF,263DocFlavor.INPUT_STREAM.GIF,264DocFlavor.URL.GIF265};266267// PNG DocFlavors268private static DocFlavor[] imagePNG = {269DocFlavor.BYTE_ARRAY.PNG,270DocFlavor.INPUT_STREAM.PNG,271DocFlavor.URL.PNG272};273274// HTML DocFlavors275private static DocFlavor[] textHtml = {276DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_8,277DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_16,278DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_16BE,279DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_16LE,280DocFlavor.BYTE_ARRAY.TEXT_HTML_US_ASCII,281DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_8,282DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_16,283DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_16BE,284DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_16LE,285DocFlavor.INPUT_STREAM.TEXT_HTML_US_ASCII,286DocFlavor.URL.TEXT_HTML_UTF_8,287DocFlavor.URL.TEXT_HTML_UTF_16,288DocFlavor.URL.TEXT_HTML_UTF_16BE,289DocFlavor.URL.TEXT_HTML_UTF_16LE,290DocFlavor.URL.TEXT_HTML_US_ASCII,291// These are not handled in UnixPrintJob so commenting these292// for now.293/*294DocFlavor.CHAR_ARRAY.TEXT_HTML,295DocFlavor.STRING.TEXT_HTML,296DocFlavor.READER.TEXT_HTML,297*/298};299300private static DocFlavor[] textHtmlHost = {301DocFlavor.BYTE_ARRAY.TEXT_HTML_HOST,302DocFlavor.INPUT_STREAM.TEXT_HTML_HOST,303DocFlavor.URL.TEXT_HTML_HOST,304};305306307// PCL DocFlavors308private static DocFlavor[] appPCL = {309DocFlavor.BYTE_ARRAY.PCL,310DocFlavor.INPUT_STREAM.PCL,311DocFlavor.URL.PCL312};313314// List of all DocFlavors, used in looping315// through all supported mime-types316private static Object[] allDocFlavors = {317appPDF, appPostScript, appOctetStream,318textPlain, imageJPG, imageGIF, imagePNG,319textHtml, appPCL,320};321322323IPPPrintService(String name, URL url) {324if ((name == null) || (url == null)){325throw new IllegalArgumentException("null uri or printer name");326}327try {328printer = java.net.URLDecoder.decode(name, "UTF-8");329} catch (java.io.UnsupportedEncodingException e) {330printer = name;331}332supportedDocFlavors = null;333supportedCats = null;334mediaSizeNames = null;335customMediaSizeNames = null;336mediaTrays = null;337myURL = url;338cps = null;339isCupsPrinter = false;340init = false;341defaultMediaIndex = -1;342343String host = myURL.getHost();344if (host!=null && host.equals(CUPSPrinter.getServer())) {345isCupsPrinter = true;346try {347myURI = new URI("ipp://"+host+348"/printers/"+printer);349debug_println(debugPrefix+"IPPPrintService myURI : "+myURI);350} catch (java.net.URISyntaxException e) {351throw new IllegalArgumentException("invalid url");352}353}354}355356357IPPPrintService(String name, String uriStr, boolean isCups) {358if ((name == null) || (uriStr == null)){359throw new IllegalArgumentException("null uri or printer name");360}361try {362printer = java.net.URLDecoder.decode(name, "UTF-8");363} catch (java.io.UnsupportedEncodingException e) {364printer = name;365}366supportedDocFlavors = null;367supportedCats = null;368mediaSizeNames = null;369customMediaSizeNames = null;370mediaTrays = null;371cps = null;372init = false;373defaultMediaIndex = -1;374try {375myURL =376new URL(uriStr.replaceFirst("ipp", "http"));377} catch (Exception e) {378IPPPrintService.debug_println(debugPrefix+379" IPPPrintService, myURL="+380myURL+" Exception= "+381e);382throw new IllegalArgumentException("invalid url");383}384385isCupsPrinter = isCups;386try {387myURI = new URI(uriStr);388debug_println(debugPrefix+"IPPPrintService myURI : "+myURI);389} catch (java.net.URISyntaxException e) {390throw new IllegalArgumentException("invalid uri");391}392}393394395/*396* Initialize mediaSizeNames, mediaTrays and other attributes.397* Media size/trays are initialized to non-null values, may be 0-length398* array.399* NOTE: Must be called from a synchronized block only.400*/401private void initAttributes() {402if (!init) {403// init customMediaSizeNames404customMediaSizeNames = new CustomMediaSizeName[0];405406if ((urlConnection = getIPPConnection(myURL)) == null) {407mediaSizeNames = new MediaSizeName[0];408mediaTrays = new MediaTray[0];409debug_println(debugPrefix+"initAttributes, NULL urlConnection ");410init = true;411return;412}413414// get all supported attributes through IPP415opGetAttributes();416417if (isCupsPrinter) {418// note, it is possible to query media in CUPS using IPP419// right now we always get it from PPD.420// maybe use "&& (usePPD)" later?421// Another reason why we use PPD is because422// IPP currently does not support it but PPD does.423424try {425cps = new CUPSPrinter(printer);426mediaSizeNames = cps.getMediaSizeNames();427mediaTrays = cps.getMediaTrays();428customMediaSizeNames = cps.getCustomMediaSizeNames();429defaultMediaIndex = cps.getDefaultMediaIndex();430rawResolutions = cps.getRawResolutions();431urlConnection.disconnect();432init = true;433return;434} catch (Exception e) {435IPPPrintService.debug_println(debugPrefix+436"initAttributes, error creating CUPSPrinter e="+e);437}438}439440// use IPP to get all media,441Media[] allMedia = getSupportedMedia();442ArrayList<Media> sizeList = new ArrayList<>();443ArrayList<Media> trayList = new ArrayList<>();444for (int i=0; i<allMedia.length; i++) {445if (allMedia[i] instanceof MediaSizeName) {446sizeList.add(allMedia[i]);447} else if (allMedia[i] instanceof MediaTray) {448trayList.add(allMedia[i]);449}450}451452if (sizeList != null) {453mediaSizeNames = new MediaSizeName[sizeList.size()];454mediaSizeNames = sizeList.toArray(mediaSizeNames);455}456if (trayList != null) {457mediaTrays = new MediaTray[trayList.size()];458mediaTrays = trayList.toArray(mediaTrays);459}460urlConnection.disconnect();461462init = true;463}464}465466467public DocPrintJob createPrintJob() {468@SuppressWarnings("removal")469SecurityManager security = System.getSecurityManager();470if (security != null) {471security.checkPrintJobAccess();472}473// REMIND: create IPPPrintJob474return new UnixPrintJob(this);475}476477478public synchronized Object479getSupportedAttributeValues(Class<? extends Attribute> category,480DocFlavor flavor,481AttributeSet attributes)482{483if (category == null) {484throw new NullPointerException("null category");485}486if (!Attribute.class.isAssignableFrom(category)) {487throw new IllegalArgumentException(category +488" does not implement Attribute");489}490if (flavor != null) {491if (!isDocFlavorSupported(flavor)) {492throw new IllegalArgumentException(flavor +493" is an unsupported flavor");494} else if (isAutoSense(flavor)) {495return null;496}497498}499500if (!isAttributeCategorySupported(category)) {501return null;502}503504/* Test if the flavor is compatible with the attributes */505if (!isDestinationSupported(flavor, attributes)) {506return null;507}508509initAttributes();510511/* Test if the flavor is compatible with the category */512if ((category == Copies.class) ||513(category == CopiesSupported.class)) {514if (flavor == null ||515!(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||516flavor.equals(DocFlavor.URL.POSTSCRIPT) ||517flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) {518CopiesSupported cs = new CopiesSupported(1, MAXCOPIES);519AttributeClass attribClass = (getAttMap != null) ?520getAttMap.get(cs.getName()) : null;521if (attribClass != null) {522int[] range = attribClass.getIntRangeValue();523cs = new CopiesSupported(range[0], range[1]);524}525return cs;526} else {527return null;528}529} else if (category == Chromaticity.class) {530if (flavor == null ||531flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||532flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||533!isIPPSupportedImages(flavor.getMimeType())) {534Chromaticity[]arr = new Chromaticity[1];535arr[0] = Chromaticity.COLOR;536return (arr);537} else {538return null;539}540} else if (category == Destination.class) {541if (flavor == null ||542flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||543flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {544try {545return new Destination((new File("out.ps")).toURI());546} catch (SecurityException se) {547try {548return new Destination(new URI("file:out.ps"));549} catch (URISyntaxException e) {550return null;551}552}553}554return null;555} else if (category == Fidelity.class) {556Fidelity []arr = new Fidelity[2];557arr[0] = Fidelity.FIDELITY_FALSE;558arr[1] = Fidelity.FIDELITY_TRUE;559return arr;560} else if (category == Finishings.class) {561AttributeClass attribClass = (getAttMap != null) ?562getAttMap.get("finishings-supported")563: null;564if (attribClass != null) {565int[] finArray = attribClass.getArrayOfIntValues();566if ((finArray != null) && (finArray.length > 0)) {567Finishings[] finSup = new Finishings[finArray.length];568for (int i=0; i<finArray.length; i++) {569finSup[i] = Finishings.NONE;570Finishings[] fAll = (Finishings[])571(new ExtFinishing(100)).getAll();572for (int j=0; j<fAll.length; j++) {573if (fAll[j] == null) {574continue;575}576if (finArray[i] == fAll[j].getValue()) {577finSup[i] = fAll[j];578break;579}580}581}582return finSup;583}584}585} else if (category == JobName.class) {586return new JobName("Java Printing", null);587} else if (category == JobSheets.class) {588JobSheets[] arr = new JobSheets[2];589arr[0] = JobSheets.NONE;590arr[1] = JobSheets.STANDARD;591return arr;592593} else if (category == Media.class) {594Media[] allMedia = new Media[mediaSizeNames.length+595mediaTrays.length];596597for (int i=0; i<mediaSizeNames.length; i++) {598allMedia[i] = mediaSizeNames[i];599}600601for (int i=0; i<mediaTrays.length; i++) {602allMedia[i+mediaSizeNames.length] = mediaTrays[i];603}604605if (allMedia.length == 0) {606allMedia = new Media[1];607allMedia[0] = (Media)getDefaultAttributeValue(Media.class);608}609610return allMedia;611} else if (category == MediaPrintableArea.class) {612MediaPrintableArea[] mpas = null;613if (cps != null) {614mpas = cps.getMediaPrintableArea();615}616617if (mpas == null) {618mpas = new MediaPrintableArea[1];619mpas[0] = (MediaPrintableArea)620getDefaultAttributeValue(MediaPrintableArea.class);621}622623if ((attributes == null) || (attributes.size() == 0)) {624ArrayList<MediaPrintableArea> printableList =625new ArrayList<MediaPrintableArea>();626627for (int i=0; i<mpas.length; i++) {628if (mpas[i] != null) {629printableList.add(mpas[i]);630}631}632if (printableList.size() > 0) {633mpas = new MediaPrintableArea[printableList.size()];634printableList.toArray(mpas);635}636return mpas;637}638639int match = -1;640Media media = (Media)attributes.get(Media.class);641if (media != null && media instanceof MediaSizeName) {642MediaSizeName msn = (MediaSizeName)media;643644// case when no supported mediasizenames are reported645// check given media against the default646if (mediaSizeNames.length == 0 &&647msn.equals(getDefaultAttributeValue(Media.class))) {648//default printable area is that of default mediasize649return mpas;650}651652for (int i=0; i<mediaSizeNames.length; i++) {653if (msn.equals(mediaSizeNames[i])) {654match = i;655}656}657}658659if (match == -1) {660return null;661} else {662MediaPrintableArea []arr = new MediaPrintableArea[1];663arr[0] = mpas[match];664return arr;665}666} else if (category == NumberUp.class) {667AttributeClass attribClass = (getAttMap != null) ?668getAttMap.get("number-up-supported") : null;669if (attribClass != null) {670int[] values = attribClass.getArrayOfIntValues();671if (values != null) {672NumberUp[] nUp = new NumberUp[values.length];673for (int i=0; i<values.length; i++) {674nUp[i] = new NumberUp(values[i]);675}676return nUp;677} else {678return null;679}680}681} else if (category == OrientationRequested.class) {682if ((flavor != null) &&683(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||684flavor.equals(DocFlavor.URL.POSTSCRIPT) ||685flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) {686return null;687}688689boolean revPort = false;690OrientationRequested[] orientSup = null;691692AttributeClass attribClass = (getAttMap != null) ?693getAttMap.get("orientation-requested-supported")694: null;695if (attribClass != null) {696int[] orientArray = attribClass.getArrayOfIntValues();697if ((orientArray != null) && (orientArray.length > 0)) {698orientSup =699new OrientationRequested[orientArray.length];700for (int i=0; i<orientArray.length; i++) {701switch (orientArray[i]) {702default:703case 3 :704orientSup[i] = OrientationRequested.PORTRAIT;705break;706case 4:707orientSup[i] = OrientationRequested.LANDSCAPE;708break;709case 5:710orientSup[i] =711OrientationRequested.REVERSE_LANDSCAPE;712break;713case 6:714orientSup[i] =715OrientationRequested.REVERSE_PORTRAIT;716revPort = true;717break;718}719}720}721}722if (flavor == null ||723flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||724flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {725726if (revPort && flavor == null) {727OrientationRequested []orSup = new OrientationRequested[4];728orSup[0] = OrientationRequested.PORTRAIT;729orSup[1] = OrientationRequested.LANDSCAPE;730orSup[2] = OrientationRequested.REVERSE_LANDSCAPE;731orSup[3] = OrientationRequested.REVERSE_PORTRAIT;732return orSup;733} else {734OrientationRequested []orSup = new OrientationRequested[3];735orSup[0] = OrientationRequested.PORTRAIT;736orSup[1] = OrientationRequested.LANDSCAPE;737orSup[2] = OrientationRequested.REVERSE_LANDSCAPE;738return orSup;739}740} else {741return orientSup;742}743} else if (category == PageRanges.class) {744if (flavor == null ||745flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||746flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {747PageRanges []arr = new PageRanges[1];748arr[0] = new PageRanges(1, Integer.MAX_VALUE);749return arr;750} else {751// Returning null as this is not yet supported in UnixPrintJob.752return null;753}754} else if (category == RequestingUserName.class) {755String userName = "";756try {757userName = System.getProperty("user.name", "");758} catch (SecurityException se) {759}760return new RequestingUserName(userName, null);761} else if (category == Sides.class) {762// The printer takes care of Sides so if short-edge763// is chosen in a job, the rotation is done by the printer.764// Orientation is rotated by emulation if pageable765// or printable so if the document is in Landscape, this may766// result in double rotation.767AttributeClass attribClass = (getAttMap != null) ?768getAttMap.get("sides-supported")769: null;770if (attribClass != null) {771String[] sidesArray = attribClass.getArrayOfStringValues();772if ((sidesArray != null) && (sidesArray.length > 0)) {773Sides[] sidesSup = new Sides[sidesArray.length];774for (int i=0; i<sidesArray.length; i++) {775if (sidesArray[i].endsWith("long-edge")) {776sidesSup[i] = Sides.TWO_SIDED_LONG_EDGE;777} else if (sidesArray[i].endsWith("short-edge")) {778sidesSup[i] = Sides.TWO_SIDED_SHORT_EDGE;779} else {780sidesSup[i] = Sides.ONE_SIDED;781}782}783return sidesSup;784}785}786} else if (category == PrinterResolution.class) {787PrinterResolution[] supportedRes = getPrintResolutions();788if (supportedRes == null) {789return null;790}791PrinterResolution []arr =792new PrinterResolution[supportedRes.length];793System.arraycopy(supportedRes, 0, arr, 0, supportedRes.length);794return arr;795}796797return null;798}799800//This class is for getting all pre-defined Finishings801@SuppressWarnings("serial") // JDK implementation class802private class ExtFinishing extends Finishings {803ExtFinishing(int value) {804super(100); // 100 to avoid any conflicts with predefined values.805}806807EnumSyntax[] getAll() {808EnumSyntax[] es = super.getEnumValueTable();809return es;810}811}812813814public AttributeSet getUnsupportedAttributes(DocFlavor flavor,815AttributeSet attributes) {816if (flavor != null && !isDocFlavorSupported(flavor)) {817throw new IllegalArgumentException("flavor " + flavor +818"is not supported");819}820821if (attributes == null) {822return null;823}824825Attribute attr;826AttributeSet unsupp = new HashAttributeSet();827Attribute []attrs = attributes.toArray();828for (int i=0; i<attrs.length; i++) {829try {830attr = attrs[i];831if (!isAttributeCategorySupported(attr.getCategory())) {832unsupp.add(attr);833} else if (!isAttributeValueSupported(attr, flavor,834attributes)) {835unsupp.add(attr);836}837} catch (ClassCastException e) {838}839}840if (unsupp.isEmpty()) {841return null;842} else {843return unsupp;844}845}846847848public synchronized DocFlavor[] getSupportedDocFlavors() {849850if (supportedDocFlavors != null) {851int len = supportedDocFlavors.length;852DocFlavor[] copyflavors = new DocFlavor[len];853System.arraycopy(supportedDocFlavors, 0, copyflavors, 0, len);854return copyflavors;855}856initAttributes();857858if ((getAttMap != null) &&859getAttMap.containsKey("document-format-supported")) {860861AttributeClass attribClass =862getAttMap.get("document-format-supported");863if (attribClass != null) {864String mimeType;865boolean psSupported = false;866String[] docFlavors = attribClass.getArrayOfStringValues();867DocFlavor[] flavors;868HashSet<Object> docList = new HashSet<>();869int j;870String hostEnc = DocFlavor.hostEncoding.871toLowerCase(Locale.ENGLISH);872boolean addHostEncoding = !hostEnc.equals("utf-8") &&873!hostEnc.equals("utf-16") && !hostEnc.equals("utf-16be") &&874!hostEnc.equals("utf-16le") && !hostEnc.equals("us-ascii");875876for (int i = 0; i < docFlavors.length; i++) {877for (j=0; j<allDocFlavors.length; j++) {878flavors = (DocFlavor[])allDocFlavors[j];879880mimeType = flavors[0].getMimeType();881if (mimeType.startsWith(docFlavors[i])) {882883docList.addAll(Arrays.asList(flavors));884885if (mimeType.equals("text/plain") &&886addHostEncoding) {887docList.add(Arrays.asList(textPlainHost));888} else if (mimeType.equals("text/html") &&889addHostEncoding) {890docList.add(Arrays.asList(textHtmlHost));891} else if (mimeType.equals("image/png")) {892pngImagesAdded = true;893} else if (mimeType.equals("image/gif")) {894gifImagesAdded = true;895} else if (mimeType.equals("image/jpeg")) {896jpgImagesAdded = true;897} else if (mimeType.indexOf("postscript") != -1) {898psSupported = true;899}900break;901}902}903904// Not added? Create new DocFlavors905if (j == allDocFlavors.length) {906// make new DocFlavors907docList.add(new DocFlavor.BYTE_ARRAY(docFlavors[i]));908docList.add(new DocFlavor.INPUT_STREAM(docFlavors[i]));909docList.add(new DocFlavor.URL(docFlavors[i]));910}911}912913// check if we need to add image DocFlavors914// and Pageable/Printable flavors915if (psSupported || isCupsPrinter) {916/*917Always add Pageable and Printable for CUPS918since it uses Filters to convert from Postscript919to device printer language.920*/921docList.add(DocFlavor.SERVICE_FORMATTED.PAGEABLE);922docList.add(DocFlavor.SERVICE_FORMATTED.PRINTABLE);923924docList.addAll(Arrays.asList(imageJPG));925docList.addAll(Arrays.asList(imagePNG));926docList.addAll(Arrays.asList(imageGIF));927}928supportedDocFlavors = new DocFlavor[docList.size()];929docList.toArray(supportedDocFlavors);930int len = supportedDocFlavors.length;931DocFlavor[] copyflavors = new DocFlavor[len];932System.arraycopy(supportedDocFlavors, 0, copyflavors, 0, len);933return copyflavors;934}935}936DocFlavor[] flavor = new DocFlavor[2];937flavor[0] = DocFlavor.SERVICE_FORMATTED.PAGEABLE;938flavor[1] = DocFlavor.SERVICE_FORMATTED.PRINTABLE;939supportedDocFlavors = flavor;940return flavor;941}942943944public boolean isDocFlavorSupported(DocFlavor flavor) {945if (supportedDocFlavors == null) {946getSupportedDocFlavors();947}948if (supportedDocFlavors != null) {949for (int f=0; f<supportedDocFlavors.length; f++) {950if (flavor.equals(supportedDocFlavors[f])) {951return true;952}953}954}955return false;956}957958959/**960* Finds matching CustomMediaSizeName of given media.961*/962public CustomMediaSizeName findCustomMedia(MediaSizeName media) {963if (customMediaSizeNames == null) {964return null;965}966for (int i=0; i< customMediaSizeNames.length; i++) {967CustomMediaSizeName custom = customMediaSizeNames[i];968MediaSizeName msn = custom.getStandardMedia();969if (media.equals(msn)) {970return customMediaSizeNames[i];971}972}973return null;974}975976977/**978* Returns the matching standard Media using string comparison of names.979*/980private Media getIPPMedia(String mediaName) {981CustomMediaSizeName sampleSize = new CustomMediaSizeName("sample", "",9820, 0);983Media[] sizes = sampleSize.getSuperEnumTable();984for (int i=0; i<sizes.length; i++) {985if (mediaName.equals(""+sizes[i])) {986return sizes[i];987}988}989CustomMediaTray sampleTray = new CustomMediaTray("sample", "");990Media[] trays = sampleTray.getSuperEnumTable();991for (int i=0; i<trays.length; i++) {992if (mediaName.equals(""+trays[i])) {993return trays[i];994}995}996return null;997}998999private Media[] getSupportedMedia() {1000if ((getAttMap != null) &&1001getAttMap.containsKey("media-supported")) {10021003AttributeClass attribClass = getAttMap.get("media-supported");10041005if (attribClass != null) {1006String[] mediaVals = attribClass.getArrayOfStringValues();1007Media msn;1008Media[] mediaNames =1009new Media[mediaVals.length];1010for (int i=0; i<mediaVals.length; i++) {1011msn = getIPPMedia(mediaVals[i]);1012//REMIND: if null, create custom?1013mediaNames[i] = msn;1014}1015return mediaNames;1016}1017}1018return new Media[0];1019}102010211022public synchronized Class<?>[] getSupportedAttributeCategories() {1023if (supportedCats != null) {1024Class<?> [] copyCats = new Class<?>[supportedCats.length];1025System.arraycopy(supportedCats, 0, copyCats, 0, copyCats.length);1026return copyCats;1027}10281029initAttributes();10301031ArrayList<Class<?>> catList = new ArrayList<>();10321033for (int i=0; i < printReqAttribDefault.length; i++) {1034PrintRequestAttribute pra =1035(PrintRequestAttribute)printReqAttribDefault[i];1036if (getAttMap != null &&1037getAttMap.containsKey(pra.getName()+"-supported")) {1038catList.add(pra.getCategory());1039}1040}10411042// Some IPP printers like lexc710 do not have list of supported media1043// but CUPS can get the media from PPD, so we still report as1044// supported category.1045if (isCupsPrinter) {1046if (!catList.contains(Media.class)) {1047catList.add(Media.class);1048}10491050// Always add MediaPrintable for cups,1051// because we can get it from PPD.1052catList.add(MediaPrintableArea.class);10531054// this is already supported in UnixPrintJob1055catList.add(Destination.class);10561057// It is unfortunate that CUPS doesn't provide a way to query1058// if printer supports collation but since most printers1059// now supports collation and that most OS has a way1060// of setting it, it is a safe assumption to just always1061// include SheetCollate as supported attribute.10621063catList.add(SheetCollate.class);10641065}10661067// With the assumption that Chromaticity is equivalent to1068// ColorSupported.1069if (getAttMap != null && getAttMap.containsKey("color-supported")) {1070catList.add(Chromaticity.class);1071}10721073// CUPS does not report printer resolution via IPP but it1074// may be gleaned from the PPD.1075PrinterResolution[] supportedRes = getPrintResolutions();1076if (supportedRes != null && (supportedRes.length > 0)) {1077catList.add(PrinterResolution.class);1078}10791080if (GraphicsEnvironment.isHeadless() == false) {1081catList.add(DialogOwner.class);1082catList.add(DialogTypeSelection.class);1083}10841085supportedCats = new Class<?>[catList.size()];1086catList.toArray(supportedCats);1087Class<?>[] copyCats = new Class<?>[supportedCats.length];1088System.arraycopy(supportedCats, 0, copyCats, 0, copyCats.length);1089return copyCats;1090}109110921093public boolean1094isAttributeCategorySupported(Class<? extends Attribute> category)1095{1096if (category == null) {1097throw new NullPointerException("null category");1098}1099if (!(Attribute.class.isAssignableFrom(category))) {1100throw new IllegalArgumentException(category +1101" is not an Attribute");1102}11031104if (supportedCats == null) {1105getSupportedAttributeCategories();1106}11071108// It is safe to assume that Orientation is always supported1109// and even if CUPS or an IPP device reports it as not,1110// our renderer can do portrait, landscape and1111// reverse landscape.1112if (category == OrientationRequested.class) {1113return true;1114}11151116for (int i=0;i<supportedCats.length;i++) {1117if (category == supportedCats[i]) {1118return true;1119}1120}11211122return false;1123}11241125@SuppressWarnings("unchecked")1126public synchronized <T extends PrintServiceAttribute>1127T getAttribute(Class<T> category)1128{1129if (category == null) {1130throw new NullPointerException("category");1131}1132if (!(PrintServiceAttribute.class.isAssignableFrom(category))) {1133throw new IllegalArgumentException("Not a PrintServiceAttribute");1134}11351136initAttributes();11371138if (category == PrinterName.class) {1139return (T)(new PrinterName(printer, null));1140} else if (category == PrinterInfo.class) {1141PrinterInfo pInfo = new PrinterInfo(printer, null);1142AttributeClass ac = (getAttMap != null) ?1143getAttMap.get(pInfo.getName())1144: null;1145if (ac != null) {1146return (T)(new PrinterInfo(ac.getStringValue(), null));1147}1148return (T)pInfo;1149} else if (category == QueuedJobCount.class) {1150QueuedJobCount qjc = new QueuedJobCount(0);1151AttributeClass ac = (getAttMap != null) ?1152getAttMap.get(qjc.getName())1153: null;1154if (ac != null) {1155qjc = new QueuedJobCount(ac.getIntValue());1156}1157return (T)qjc;1158} else if (category == PrinterIsAcceptingJobs.class) {1159PrinterIsAcceptingJobs accJob =1160PrinterIsAcceptingJobs.ACCEPTING_JOBS;1161AttributeClass ac = (getAttMap != null) ?1162getAttMap.get(accJob.getName())1163: null;1164if ((ac != null) && (ac.getByteValue() == 0)) {1165accJob = PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS;1166}1167return (T)accJob;1168} else if (category == ColorSupported.class) {1169ColorSupported cs = ColorSupported.SUPPORTED;1170AttributeClass ac = (getAttMap != null) ?1171getAttMap.get(cs.getName())1172: null;1173if ((ac != null) && (ac.getByteValue() == 0)) {1174cs = ColorSupported.NOT_SUPPORTED;1175}1176return (T)cs;1177} else if (category == PDLOverrideSupported.class) {11781179if (isCupsPrinter) {1180// Documented: For CUPS this will always be false1181return (T)PDLOverrideSupported.NOT_ATTEMPTED;1182} else {1183// REMIND: check attribute values1184return (T)PDLOverrideSupported.NOT_ATTEMPTED;1185}1186} else if (category == PrinterURI.class) {1187return (T)(new PrinterURI(myURI));1188} else {1189return null;1190}1191}119211931194public synchronized PrintServiceAttributeSet getAttributes() {1195// update getAttMap by sending again get-attributes IPP request1196init = false;1197initAttributes();11981199HashPrintServiceAttributeSet attrs =1200new HashPrintServiceAttributeSet();12011202for (int i=0; i < serviceAttributes.length; i++) {1203String name = (String)serviceAttributes[i][1];1204if (getAttMap != null && getAttMap.containsKey(name)) {1205@SuppressWarnings("unchecked")1206Class<PrintServiceAttribute> c = (Class<PrintServiceAttribute>)serviceAttributes[i][0];1207PrintServiceAttribute psa = getAttribute(c);1208if (psa != null) {1209attrs.add(psa);1210}1211}1212}1213return AttributeSetUtilities.unmodifiableView(attrs);1214}12151216public boolean isIPPSupportedImages(String mimeType) {1217if (supportedDocFlavors == null) {1218getSupportedDocFlavors();1219}12201221if (mimeType.equals("image/png") && pngImagesAdded) {1222return true;1223} else if (mimeType.equals("image/gif") && gifImagesAdded) {1224return true;1225} else if (mimeType.equals("image/jpeg") && jpgImagesAdded) {1226return true;1227}12281229return false;1230}123112321233private boolean isSupportedCopies(Copies copies) {1234CopiesSupported cs = (CopiesSupported)1235getSupportedAttributeValues(Copies.class, null, null);1236int[][] members = cs.getMembers();1237int min, max;1238if ((members.length > 0) && (members[0].length > 0)) {1239min = members[0][0];1240max = members[0][1];1241} else {1242min = 1;1243max = MAXCOPIES;1244}12451246int value = copies.getValue();1247return (value >= min && value <= max);1248}12491250private boolean isAutoSense(DocFlavor flavor) {1251if (flavor.equals(DocFlavor.BYTE_ARRAY.AUTOSENSE) ||1252flavor.equals(DocFlavor.INPUT_STREAM.AUTOSENSE) ||1253flavor.equals(DocFlavor.URL.AUTOSENSE)) {1254return true;1255}1256else {1257return false;1258}1259}12601261private synchronized boolean isSupportedMediaTray(MediaTray msn) {1262initAttributes();12631264if (mediaTrays != null) {1265for (int i=0; i<mediaTrays.length; i++) {1266if (msn.equals(mediaTrays[i])) {1267return true;1268}1269}1270}1271return false;1272}12731274private synchronized boolean isSupportedMedia(MediaSizeName msn) {1275initAttributes();12761277if (msn.equals((Media)getDefaultAttributeValue(Media.class))) {1278return true;1279}1280for (int i=0; i<mediaSizeNames.length; i++) {1281debug_println(debugPrefix+"isSupportedMedia, mediaSizeNames[i] "+mediaSizeNames[i]);1282if (msn.equals(mediaSizeNames[i])) {1283return true;1284}1285}1286return false;1287}12881289/* Return false if flavor is not null, pageable, nor printable and1290* Destination is part of attributes.1291*/1292private boolean1293isDestinationSupported(DocFlavor flavor, AttributeSet attributes) {12941295if ((attributes != null) &&1296(attributes.get(Destination.class) != null) &&1297!(flavor == null ||1298flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||1299flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {1300return false;1301}1302return true;1303}130413051306public boolean isAttributeValueSupported(Attribute attr,1307DocFlavor flavor,1308AttributeSet attributes) {1309if (attr == null) {1310throw new NullPointerException("null attribute");1311}1312if (flavor != null) {1313if (!isDocFlavorSupported(flavor)) {1314throw new IllegalArgumentException(flavor +1315" is an unsupported flavor");1316} else if (isAutoSense(flavor)) {1317return false;1318}1319}1320Class<? extends Attribute> category = attr.getCategory();1321if (!isAttributeCategorySupported(category)) {1322return false;1323}13241325/* Test if the flavor is compatible with the attributes */1326if (!isDestinationSupported(flavor, attributes)) {1327return false;1328}13291330/* Test if the flavor is compatible with the category */1331if (attr.getCategory() == Chromaticity.class) {1332if ((flavor == null) ||1333flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||1334flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||1335!isIPPSupportedImages(flavor.getMimeType())) {1336return attr == Chromaticity.COLOR;1337} else {1338return false;1339}1340} else if (attr.getCategory() == Copies.class) {1341return (flavor == null ||1342!(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||1343flavor.equals(DocFlavor.URL.POSTSCRIPT) ||1344flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) &&1345isSupportedCopies((Copies)attr);13461347} else if (attr.getCategory() == Destination.class) {1348if (flavor == null ||1349flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||1350flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {1351URI uri = ((Destination)attr).getURI();1352if ("file".equals(uri.getScheme()) &&1353!uri.getSchemeSpecificPart().isEmpty()) {1354return true;1355}1356}1357return false;1358} else if (attr.getCategory() == Media.class) {1359if (attr instanceof MediaSizeName) {1360return isSupportedMedia((MediaSizeName)attr);1361}1362if (attr instanceof MediaTray) {1363return isSupportedMediaTray((MediaTray)attr);1364}1365} else if (attr.getCategory() == PageRanges.class) {1366if (flavor != null &&1367!(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||1368flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {1369return false;1370}1371} else if (attr.getCategory() == SheetCollate.class) {1372if (flavor != null &&1373!(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||1374flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {1375return false;1376}1377} else if (attr.getCategory() == Sides.class) {1378Sides[] sidesArray = (Sides[])getSupportedAttributeValues(1379Sides.class,1380flavor,1381attributes);13821383if (sidesArray != null) {1384for (int i=0; i<sidesArray.length; i++) {1385if (sidesArray[i] == (Sides)attr) {1386return true;1387}1388}1389}1390return false;1391} else if (attr.getCategory() == OrientationRequested.class) {1392OrientationRequested[] orientArray =1393(OrientationRequested[])getSupportedAttributeValues(1394OrientationRequested.class,1395flavor,1396attributes);13971398if (orientArray != null) {1399for (int i=0; i<orientArray.length; i++) {1400if (orientArray[i] == (OrientationRequested)attr) {1401return true;1402}1403}1404}1405return false;1406} else if (attr.getCategory() == PrinterResolution.class) {1407if (attr instanceof PrinterResolution) {1408return isSupportedResolution((PrinterResolution)attr);1409}1410} else if (attr.getCategory() == DialogOwner.class) {1411DialogOwner owner = (DialogOwner)attr;1412// ID not supported on any dialog type on Unix platforms.1413if (DialogOwnerAccessor.getID(owner) != 0) {1414return false;1415}1416// On Mac we have no control over the native dialog.1417DialogTypeSelection dst = (attributes == null) ? null :1418(DialogTypeSelection)attributes.get(DialogTypeSelection.class);1419if (PrintServiceLookupProvider.isMac() &&1420dst == DialogTypeSelection.NATIVE) {1421return false;1422}1423// The other case is always a Swing dialog on all Unix platforms.1424// So we only need to check that the toolkit supports1425// always on top.1426if (owner.getOwner() != null) {1427return true;1428} else {1429return Toolkit.getDefaultToolkit().isAlwaysOnTopSupported();1430}1431} else if (attr.getCategory() == DialogTypeSelection.class) {1432if (PrintServiceLookupProvider.isMac()) {1433return true;1434} else {1435DialogTypeSelection dst = (DialogTypeSelection)attr;1436return attr == DialogTypeSelection.COMMON;1437}1438}1439return true;1440}144114421443public synchronized Object1444getDefaultAttributeValue(Class<? extends Attribute> category)1445{1446if (category == null) {1447throw new NullPointerException("null category");1448}1449if (!Attribute.class.isAssignableFrom(category)) {1450throw new IllegalArgumentException(category +1451" is not an Attribute");1452}1453if (!isAttributeCategorySupported(category)) {1454return null;1455}14561457initAttributes();14581459String catName = null;1460for (int i=0; i < printReqAttribDefault.length; i++) {1461PrintRequestAttribute pra =1462(PrintRequestAttribute)printReqAttribDefault[i];1463if (pra.getCategory() == category) {1464catName = pra.getName();1465break;1466}1467}1468String attribName = catName+"-default";1469AttributeClass attribClass = (getAttMap != null) ?1470getAttMap.get(attribName) : null;14711472if (category == Copies.class) {1473if (attribClass != null) {1474return new Copies(attribClass.getIntValue());1475} else {1476return new Copies(1);1477}1478} else if (category == Chromaticity.class) {1479return Chromaticity.COLOR;1480} else if (category == Destination.class) {1481try {1482return new Destination((new File("out.ps")).toURI());1483} catch (SecurityException se) {1484try {1485return new Destination(new URI("file:out.ps"));1486} catch (URISyntaxException e) {1487return null;1488}1489}1490} else if (category == Fidelity.class) {1491return Fidelity.FIDELITY_FALSE;1492} else if (category == Finishings.class) {1493return Finishings.NONE;1494} else if (category == JobName.class) {1495return new JobName("Java Printing", null);1496} else if (category == JobSheets.class) {1497if (attribClass != null &&1498attribClass.getStringValue().equals("none")) {1499return JobSheets.NONE;1500} else {1501return JobSheets.STANDARD;1502}1503} else if (category == Media.class) {1504if (defaultMediaIndex == -1) {1505defaultMediaIndex = 0;1506}1507if (mediaSizeNames.length == 0) {1508String defaultCountry = Locale.getDefault().getCountry();1509if (defaultCountry != null &&1510(defaultCountry.isEmpty() ||1511defaultCountry.equals(Locale.US.getCountry()) ||1512defaultCountry.equals(Locale.CANADA.getCountry()))) {1513return MediaSizeName.NA_LETTER;1514} else {1515return MediaSizeName.ISO_A4;1516}1517}15181519if (attribClass != null) {1520String name = attribClass.getStringValue();1521if (isCupsPrinter) {1522return mediaSizeNames[defaultMediaIndex];1523} else {1524for (int i=0; i< mediaSizeNames.length; i++) {1525if (mediaSizeNames[i].toString().indexOf(name) != -1) {1526defaultMediaIndex = i;1527return mediaSizeNames[defaultMediaIndex];1528}1529}1530}1531}1532return mediaSizeNames[defaultMediaIndex];15331534} else if (category == MediaPrintableArea.class) {1535MediaPrintableArea[] mpas;1536if ((cps != null) &&1537((mpas = cps.getMediaPrintableArea()) != null)) {1538if (defaultMediaIndex == -1) {1539// initializes value of defaultMediaIndex1540getDefaultAttributeValue(Media.class);1541}1542return mpas[defaultMediaIndex];1543} else {1544String defaultCountry = Locale.getDefault().getCountry();1545float iw, ih;1546if (defaultCountry != null &&1547(defaultCountry.isEmpty() ||1548defaultCountry.equals(Locale.US.getCountry()) ||1549defaultCountry.equals(Locale.CANADA.getCountry()))) {1550iw = MediaSize.NA.LETTER.getX(Size2DSyntax.INCH) - 0.5f;1551ih = MediaSize.NA.LETTER.getY(Size2DSyntax.INCH) - 0.5f;1552} else {1553iw = MediaSize.ISO.A4.getX(Size2DSyntax.INCH) - 0.5f;1554ih = MediaSize.ISO.A4.getY(Size2DSyntax.INCH) - 0.5f;1555}1556return new MediaPrintableArea(0.25f, 0.25f, iw, ih,1557MediaPrintableArea.INCH);1558}1559} else if (category == NumberUp.class) {1560return new NumberUp(1); // for CUPS this is always 11561} else if (category == OrientationRequested.class) {1562if (attribClass != null) {1563switch (attribClass.getIntValue()) {1564default:1565case 3: return OrientationRequested.PORTRAIT;1566case 4: return OrientationRequested.LANDSCAPE;1567case 5: return OrientationRequested.REVERSE_LANDSCAPE;1568case 6: return OrientationRequested.REVERSE_PORTRAIT;1569}1570} else {1571return OrientationRequested.PORTRAIT;1572}1573} else if (category == PageRanges.class) {1574if (attribClass != null) {1575int[] range = attribClass.getIntRangeValue();1576return new PageRanges(range[0], range[1]);1577} else {1578return new PageRanges(1, Integer.MAX_VALUE);1579}1580} else if (category == RequestingUserName.class) {1581String userName = "";1582try {1583userName = System.getProperty("user.name", "");1584} catch (SecurityException se) {1585}1586return new RequestingUserName(userName, null);1587} else if (category == SheetCollate.class) {1588return SheetCollate.UNCOLLATED;1589} else if (category == Sides.class) {1590if (attribClass != null) {1591if (attribClass.getStringValue().endsWith("long-edge")) {1592return Sides.TWO_SIDED_LONG_EDGE;1593} else if (attribClass.getStringValue().endsWith(1594"short-edge")) {1595return Sides.TWO_SIDED_SHORT_EDGE;1596}1597}1598return Sides.ONE_SIDED;1599} else if (category == PrinterResolution.class) {1600PrinterResolution[] supportedRes = getPrintResolutions();1601if ((supportedRes != null) && (supportedRes.length > 0)) {1602return supportedRes[0];1603} else {1604return new PrinterResolution(300, 300, PrinterResolution.DPI);1605}1606}16071608return null;1609}16101611private PrinterResolution[] getPrintResolutions() {1612if (printerResolutions == null) {1613if (rawResolutions == null) {1614printerResolutions = new PrinterResolution[0];1615} else {1616int numRes = rawResolutions.length / 2;1617PrinterResolution[] pres = new PrinterResolution[numRes];1618for (int i=0; i < numRes; i++) {1619pres[i] = new PrinterResolution(rawResolutions[i*2],1620rawResolutions[i*2+1],1621PrinterResolution.DPI);1622}1623printerResolutions = pres;1624}1625}1626return printerResolutions;1627}16281629private boolean isSupportedResolution(PrinterResolution res) {1630PrinterResolution[] supportedRes = getPrintResolutions();1631if (supportedRes != null) {1632for (int i=0; i<supportedRes.length; i++) {1633if (res.equals(supportedRes[i])) {1634return true;1635}1636}1637}1638return false;1639}16401641public ServiceUIFactory getServiceUIFactory() {1642return null;1643}16441645public void wakeNotifier() {1646synchronized (this) {1647if (notifier != null) {1648notifier.wake();1649}1650}1651}16521653public void addPrintServiceAttributeListener(1654PrintServiceAttributeListener listener) {1655synchronized (this) {1656if (listener == null) {1657return;1658}1659if (notifier == null) {1660notifier = new ServiceNotifier(this);1661}1662notifier.addListener(listener);1663}1664}16651666public void removePrintServiceAttributeListener(1667PrintServiceAttributeListener listener) {1668synchronized (this) {1669if (listener == null || notifier == null ) {1670return;1671}1672notifier.removeListener(listener);1673if (notifier.isEmpty()) {1674notifier.stopNotifier();1675notifier = null;1676}1677}1678}16791680String getDest() {1681return printer;1682}16831684public String getName() {1685/*1686* Mac is using printer-info IPP attribute for its human-readable printer1687* name and is also the identifier used in NSPrintInfo:setPrinter.1688*/1689if (PrintServiceLookupProvider.isMac()) {1690PrintServiceAttributeSet psaSet = this.getAttributes();1691if (psaSet != null) {1692PrinterInfo pName = (PrinterInfo)psaSet.get(PrinterInfo.class);1693if (pName != null) {1694return pName.toString();1695}1696}1697}1698return printer;1699}170017011702public boolean usesClass(Class<?> c) {1703return (c == sun.print.PSPrinterJob.class);1704}170517061707public static HttpURLConnection getIPPConnection(URL url) {1708HttpURLConnection connection;1709URLConnection urlc;1710try {1711urlc = url.openConnection();1712} catch (java.io.IOException ioe) {1713return null;1714}1715if (!(urlc instanceof HttpURLConnection)) {1716return null;1717}1718connection = (HttpURLConnection)urlc;1719connection.setUseCaches(false);1720connection.setDoInput(true);1721connection.setDoOutput(true);1722connection.setRequestProperty("Content-type", "application/ipp");1723return connection;1724}172517261727public synchronized boolean isPostscript() {1728if (isPS == null) {1729isPS = Boolean.TRUE;1730if (isCupsPrinter) {1731try {1732urlConnection = getIPPConnection(1733new URL(myURL+".ppd"));17341735InputStream is = urlConnection.getInputStream();1736if (is != null) {1737BufferedReader d =1738new BufferedReader(new InputStreamReader(is,1739Charset.forName("ISO-8859-1")));1740String lineStr;1741while ((lineStr = d.readLine()) != null) {1742if (lineStr.startsWith("*cupsFilter:")) {1743isPS = Boolean.FALSE;1744break;1745}1746}1747}1748} catch (java.io.IOException e) {1749debug_println(" isPostscript, e= "+e);1750/* if PPD is not found, this may be a raw printer1751and in this case it is assumed that it is a1752Postscript printer */1753// do nothing1754}1755}1756}1757return isPS.booleanValue();1758}175917601761private void opGetAttributes() {1762try {1763debug_println(debugPrefix+"opGetAttributes myURI "+myURI+" myURL "+myURL);17641765AttributeClass[] attClNoUri = {1766AttributeClass.ATTRIBUTES_CHARSET,1767AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE};17681769AttributeClass[] attCl = {1770AttributeClass.ATTRIBUTES_CHARSET,1771AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE,1772new AttributeClass("printer-uri",1773AttributeClass.TAG_URI,1774""+myURI)};17751776@SuppressWarnings("removal")1777OutputStream os = java.security.AccessController.1778doPrivileged(new java.security.PrivilegedAction<OutputStream>() {1779public OutputStream run() {1780try {1781return urlConnection.getOutputStream();1782} catch (Exception e) {1783}1784return null;1785}1786});17871788if (os == null) {1789return;1790}17911792boolean success = (myURI == null) ?1793writeIPPRequest(os, OP_GET_ATTRIBUTES, attClNoUri) :1794writeIPPRequest(os, OP_GET_ATTRIBUTES, attCl);1795if (success) {1796InputStream is = null;1797if ((is = urlConnection.getInputStream())!=null) {1798HashMap<String, AttributeClass>[] responseMap = readIPPResponse(is);17991800if (responseMap != null && responseMap.length > 0) {1801getAttMap = responseMap[0];1802// If there is extra hashmap created due to duplicate1803// key/attribute present in IPPresponse, then use that1804// map too by appending to getAttMap after removing the1805// duplicate key/value1806if (responseMap.length > 1) {1807for (int i = 1; i < responseMap.length; i++) {1808for (Map.Entry<String, AttributeClass> entry : responseMap[i].entrySet()) {1809if (!getAttMap.containsKey(entry.getValue())) {1810getAttMap.put(entry.getKey(), entry.getValue());1811}1812}1813}1814}1815}1816} else {1817debug_println(debugPrefix+"opGetAttributes - null input stream");1818}1819is.close();1820}1821os.close();1822} catch (java.io.IOException e) {1823debug_println(debugPrefix+"opGetAttributes - input/output stream: "+e);1824}1825}182618271828public static boolean writeIPPRequest(OutputStream os,1829String operCode,1830AttributeClass[] attCl) {1831OutputStreamWriter osw;1832try {1833osw = new OutputStreamWriter(os, "UTF-8");1834} catch (java.io.UnsupportedEncodingException exc) {1835debug_println(debugPrefix+"writeIPPRequest, UTF-8 not supported? Exception: "+exc);1836return false;1837}1838debug_println(debugPrefix+"writeIPPRequest, op code= "+operCode);1839char[] opCode = new char[2];1840opCode[0] = (char)Byte.parseByte(operCode.substring(0,2), 16);1841opCode[1] = (char)Byte.parseByte(operCode.substring(2,4), 16);1842char[] bytes = {0x01, 0x01, 0x00, 0x01};1843try {1844osw.write(bytes, 0, 2); // version number1845osw.write(opCode, 0, 2); // operation code1846bytes[0] = 0x00; bytes[1] = 0x00;1847osw.write(bytes, 0, 4); // request ID #118481849bytes[0] = 0x01; // operation-group-tag1850osw.write(bytes[0]);18511852String valStr;1853char[] lenStr;18541855AttributeClass ac;1856for (int i=0; i < attCl.length; i++) {1857ac = attCl[i];1858osw.write(ac.getType()); // value tag18591860lenStr = ac.getLenChars();1861osw.write(lenStr, 0, 2); // length1862osw.write(""+ac, 0, ac.getName().length());18631864// check if string range (0x35 -> 0x49)1865if (ac.getType() >= AttributeClass.TAG_TEXT_LANGUAGE &&1866ac.getType() <= AttributeClass.TAG_MIME_MEDIATYPE){1867valStr = (String)ac.getObjectValue();1868bytes[0] = 0; bytes[1] = (char)valStr.length();1869osw.write(bytes, 0, 2);1870osw.write(valStr, 0, valStr.length());1871} // REMIND: need to support other value tags but for CUPS1872// string is all we need.1873}18741875osw.write(GRPTAG_END_ATTRIBUTES);1876osw.flush();1877osw.close();1878} catch (java.io.IOException ioe) {1879debug_println(debugPrefix+"writeIPPRequest, IPPPrintService Exception in writeIPPRequest: "+ioe);1880return false;1881}1882return true;1883}188418851886public static HashMap<String, AttributeClass>[] readIPPResponse(InputStream inputStream) {18871888if (inputStream == null) {1889return null;1890}18911892byte[] response = new byte[MAX_ATTRIBUTE_LENGTH];1893try {18941895DataInputStream ois = new DataInputStream(inputStream);18961897// read status and ID1898if ((ois.read(response, 0, 8) > -1) &&1899(response[2] == STATUSCODE_SUCCESS)) {19001901ByteArrayOutputStream outObj;1902int counter=0;1903short len = 0;1904String attribStr = null;1905// assign default value1906byte valTagByte = AttributeClass.TAG_KEYWORD;1907ArrayList<HashMap<String, AttributeClass>> respList = new ArrayList<>();1908HashMap<String, AttributeClass> responseMap = new HashMap<>();19091910response[0] = ois.readByte();19111912// check for group tags1913while ((response[0] >= GRPTAG_OP_ATTRIBUTES) &&1914(response[0] <= GRPTAG_PRINTER_ATTRIBUTES)1915&& (response[0] != GRPTAG_END_ATTRIBUTES)) {1916debug_println(debugPrefix+"readIPPResponse, checking group tag, response[0]= "+1917response[0]);19181919outObj = new ByteArrayOutputStream();1920//make sure counter and attribStr are re-initialized1921counter = 0;1922attribStr = null;19231924// read value tag1925response[0] = ois.readByte();1926while (response[0] >= AttributeClass.TAG_UNSUPPORTED_VALUE &&1927response[0] <= AttributeClass.TAG_MEMBER_ATTRNAME) {1928// read name length1929len = ois.readShort();19301931// If current value is not part of previous attribute1932// then close stream and add it to HashMap.1933// It is part of previous attribute if name length=0.1934if ((len != 0) && (attribStr != null)) {1935//last byte is the total # of values1936outObj.write(counter);1937outObj.flush();1938outObj.close();1939byte[] outArray = outObj.toByteArray();19401941// if key exists, new HashMap1942if (responseMap.containsKey(attribStr)) {1943respList.add(responseMap);1944responseMap = new HashMap<>();1945}19461947// exclude those that are unknown1948if (valTagByte >= AttributeClass.TAG_INT) {1949AttributeClass ac =1950new AttributeClass(attribStr,1951valTagByte,1952outArray);19531954responseMap.put(ac.getName(), ac);1955debug_println(debugPrefix+ "readIPPResponse "+ac);1956}19571958outObj = new ByteArrayOutputStream();1959counter = 0; //reset counter1960}1961//check if this is new value tag1962if (counter == 0) {1963valTagByte = response[0];1964}1965// read attribute name1966if (len != 0) {1967// read "len" characters1968// make sure it doesn't exceed the maximum1969if (len > MAX_ATTRIBUTE_LENGTH) {1970response = new byte[len]; // expand as needed1971}1972ois.read(response, 0, len);1973attribStr = new String(response, 0, len);1974}1975// read value length1976len = ois.readShort();1977// write name length1978outObj.write(len);1979// read value, make sure it doesn't exceed the maximum1980if (len > MAX_ATTRIBUTE_LENGTH) {1981response = new byte[len]; // expand as needed1982}1983ois.read(response, 0, len);1984// write value of "len" length1985outObj.write(response, 0, len);1986counter++;1987// read next byte1988response[0] = ois.readByte();1989}19901991if (attribStr != null) {1992outObj.write(counter);1993outObj.flush();1994outObj.close();19951996// if key exists in old HashMap, new HashMap1997if ((counter != 0) &&1998responseMap.containsKey(attribStr)) {1999respList.add(responseMap);2000responseMap = new HashMap<>();2001}20022003byte[] outArray = outObj.toByteArray();20042005AttributeClass ac =2006new AttributeClass(attribStr,2007valTagByte,2008outArray);2009responseMap.put(ac.getName(), ac);2010}2011}2012ois.close();2013if ((responseMap != null) && (responseMap.size() > 0)) {2014respList.add(responseMap);2015}2016@SuppressWarnings({"unchecked", "rawtypes"})2017HashMap<String, AttributeClass>[] tmp =2018respList.toArray((HashMap<String, AttributeClass>[])new HashMap[respList.size()]);2019return tmp;2020} else {2021debug_println(debugPrefix+2022"readIPPResponse client error, IPP status code: 0x"+2023toHex(response[2]) + toHex(response[3]));2024return null;2025}20262027} catch (java.io.IOException e) {2028debug_println(debugPrefix+"readIPPResponse: "+e);2029if (debugPrint) {2030e.printStackTrace();2031}2032return null;2033}2034}20352036private static String toHex(byte v) {2037String s = Integer.toHexString(v&0xff);2038return (s.length() == 2) ? s : "0"+s;2039}20402041public String toString() {2042return "IPP Printer : " + getName();2043}20442045public boolean equals(Object obj) {2046return (obj == this ||2047(obj instanceof IPPPrintService &&2048((IPPPrintService)obj).getName().equals(getName())));2049}20502051public int hashCode() {2052return this.getClass().hashCode()+getName().hashCode();2053}2054}205520562057