Path: blob/master/src/java.desktop/unix/classes/sun/print/CUPSPrinter.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.net.URL;28import java.net.HttpURLConnection;29import java.io.OutputStream;30import java.io.InputStream;31import java.util.ArrayList;32import java.util.HashMap;33import sun.print.IPPPrintService;34import sun.print.CustomMediaSizeName;35import sun.print.CustomMediaTray;36import javax.print.attribute.standard.Media;37import javax.print.attribute.standard.MediaSizeName;38import javax.print.attribute.standard.MediaSize;39import javax.print.attribute.standard.MediaTray;40import javax.print.attribute.standard.MediaPrintableArea;41import javax.print.attribute.standard.PrinterResolution;42import javax.print.attribute.Size2DSyntax;43import javax.print.attribute.Attribute;44import javax.print.attribute.EnumSyntax;45import javax.print.attribute.standard.PrinterName;464748@SuppressWarnings("removal")49public class CUPSPrinter {50private static final String debugPrefix = "CUPSPrinter>> ";51private static final double PRINTER_DPI = 72.0;52private boolean initialized;53private static native String getCupsServer();54private static native int getCupsPort();55private static native String getCupsDefaultPrinter();56private static native boolean canConnect(String server, int port);57private static native boolean initIDs();58// These functions need to be synchronized as59// CUPS does not support multi-threading.60private static synchronized native String[] getMedia(String printer);61private static synchronized native float[] getPageSizes(String printer);62private static synchronized native void63getResolutions(String printer, ArrayList<Integer> resolutionList);64//public static boolean useIPPMedia = false; will be used later6566private MediaPrintableArea[] cupsMediaPrintables;67private MediaSizeName[] cupsMediaSNames;68private CustomMediaSizeName[] cupsCustomMediaSNames;69private MediaTray[] cupsMediaTrays;7071public int nPageSizes = 0;72public int nTrays = 0;73private String[] media;74private float[] pageSizes;75int[] resolutionsArray;76private String printer;7778private static boolean libFound;79private static String cupsServer = null;80private static int cupsPort = 0;8182static {83// load awt library to access native code84java.security.AccessController.doPrivileged(85new java.security.PrivilegedAction<Void>() {86public Void run() {87System.loadLibrary("awt");88return null;89}90});91libFound = initIDs();92if (libFound) {93cupsServer = getCupsServer();94cupsPort = getCupsPort();95}96}979899CUPSPrinter (String printerName) {100if (printerName == null) {101throw new IllegalArgumentException("null printer name");102}103printer = printerName;104cupsMediaSNames = null;105cupsMediaPrintables = null;106cupsMediaTrays = null;107initialized = false;108109if (!libFound) {110throw new RuntimeException("cups lib not found");111} else {112// get page + tray names113media = getMedia(printer);114if (media == null) {115// either PPD file is not found or printer is unknown116throw new RuntimeException("error getting PPD");117}118119// get sizes120pageSizes = getPageSizes(printer);121if (pageSizes != null) {122nPageSizes = pageSizes.length/6;123124nTrays = media.length/2-nPageSizes;125assert (nTrays >= 0);126}127ArrayList<Integer> resolutionList = new ArrayList<>();128getResolutions(printer, resolutionList);129resolutionsArray = new int[resolutionList.size()];130for (int i=0; i < resolutionList.size(); i++) {131resolutionsArray[i] = resolutionList.get(i);132}133}134}135136137/**138* Returns array of MediaSizeNames derived from PPD.139*/140MediaSizeName[] getMediaSizeNames() {141initMedia();142return cupsMediaSNames;143}144145146/**147* Returns array of Custom MediaSizeNames derived from PPD.148*/149CustomMediaSizeName[] getCustomMediaSizeNames() {150initMedia();151return cupsCustomMediaSNames;152}153154public int getDefaultMediaIndex() {155return ((pageSizes.length >1) ? (int)(pageSizes[pageSizes.length -1]) : 0);156}157158/**159* Returns array of MediaPrintableArea derived from PPD.160*/161MediaPrintableArea[] getMediaPrintableArea() {162initMedia();163return cupsMediaPrintables;164}165166/**167* Returns array of MediaTrays derived from PPD.168*/169MediaTray[] getMediaTrays() {170initMedia();171return cupsMediaTrays;172}173174/**175* return the raw packed array of supported printer resolutions.176*/177int[] getRawResolutions() {178return resolutionsArray;179}180181/**182* Initialize media by translating PPD info to PrintService attributes.183*/184private synchronized void initMedia() {185if (initialized) {186return;187} else {188initialized = true;189}190191if (pageSizes == null) {192return;193}194195cupsMediaPrintables = new MediaPrintableArea[nPageSizes];196cupsMediaSNames = new MediaSizeName[nPageSizes];197cupsCustomMediaSNames = new CustomMediaSizeName[nPageSizes];198199CustomMediaSizeName msn;200MediaPrintableArea mpa;201float length, width, x, y, w, h;202203// initialize names and printables204for (int i=0; i<nPageSizes; i++) {205// media width and length206width = (float)(pageSizes[i*6]/PRINTER_DPI);207length = (float)(pageSizes[i*6+1]/PRINTER_DPI);208// media printable area209x = (float)(pageSizes[i*6+2]/PRINTER_DPI);210h = (float)(pageSizes[i*6+3]/PRINTER_DPI);211w = (float)(pageSizes[i*6+4]/PRINTER_DPI);212y = (float)(pageSizes[i*6+5]/PRINTER_DPI);213214msn = new CustomMediaSizeName(media[i*2], media[i*2+1],215width, length);216217// add to list of standard MediaSizeNames218if ((cupsMediaSNames[i] = msn.getStandardMedia()) == null) {219// add custom if no matching standard media220cupsMediaSNames[i] = msn;221222// add this new custom msn to MediaSize array223if ((width > 0.0) && (length > 0.0)) {224try {225new MediaSize(width, length,226Size2DSyntax.INCH, msn);227} catch (IllegalArgumentException e) {228/* PDF printer in Linux for Ledger paper causes229"IllegalArgumentException: X dimension > Y dimension".230We rotate based on IPP spec. */231new MediaSize(length, width, Size2DSyntax.INCH, msn);232}233}234}235236// add to list of custom MediaSizeName237// for internal use of IPPPrintService238cupsCustomMediaSNames[i] = msn;239240mpa = null;241try {242mpa = new MediaPrintableArea(x, y, w, h,243MediaPrintableArea.INCH);244} catch (IllegalArgumentException e) {245if (width > 0 && length > 0) {246mpa = new MediaPrintableArea(0, 0, width, length,247MediaPrintableArea.INCH);248}249}250cupsMediaPrintables[i] = mpa;251}252253// initialize trays254cupsMediaTrays = new MediaTray[nTrays];255256MediaTray mt;257for (int i=0; i<nTrays; i++) {258mt = new CustomMediaTray(media[(nPageSizes+i)*2],259media[(nPageSizes+i)*2+1]);260cupsMediaTrays[i] = mt;261}262263}264265/**266* Get CUPS default printer using IPP.267* Returns 2 values - index 0 is printer name, index 1 is the uri.268*/269static String[] getDefaultPrinter() {270// Try to get user/lpoptions-defined printer name from CUPS271// if not user-set, then go for server default destination272String[] printerInfo = new String[2];273printerInfo[0] = getCupsDefaultPrinter();274275if (printerInfo[0] != null) {276printerInfo[1] = null;277return printerInfo.clone();278}279try {280URL url = new URL("http", getServer(), getPort(), "");281final HttpURLConnection urlConnection =282IPPPrintService.getIPPConnection(url);283284if (urlConnection != null) {285OutputStream os = java.security.AccessController.286doPrivileged(new java.security.PrivilegedAction<OutputStream>() {287public OutputStream run() {288try {289return urlConnection.getOutputStream();290} catch (Exception e) {291IPPPrintService.debug_println(debugPrefix+e);292}293return null;294}295});296297if (os == null) {298return null;299}300301AttributeClass[] attCl = {302AttributeClass.ATTRIBUTES_CHARSET,303AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE,304new AttributeClass("requested-attributes",305AttributeClass.TAG_URI,306"printer-uri")307};308309if (IPPPrintService.writeIPPRequest(os,310IPPPrintService.OP_CUPS_GET_DEFAULT,311attCl)) {312313HashMap<String, AttributeClass> defaultMap = null;314315InputStream is = urlConnection.getInputStream();316HashMap<String, AttributeClass>[] responseMap = IPPPrintService.readIPPResponse(317is);318is.close();319320if (responseMap != null && responseMap.length > 0) {321defaultMap = responseMap[0];322} else {323IPPPrintService.debug_println(debugPrefix+324" empty response map for GET_DEFAULT.");325}326327if (defaultMap == null) {328os.close();329urlConnection.disconnect();330331/* CUPS on OS X, as initially configured, considers the332* default printer to be the last one used that's333* presently available. So if no default was334* reported, exec lpstat -d which has all the Apple335* special behaviour for this built in.336*/337if (PrintServiceLookupProvider.isMac()) {338printerInfo[0] = PrintServiceLookupProvider.339getDefaultPrinterNameSysV();340printerInfo[1] = null;341return printerInfo.clone();342} else {343return null;344}345}346347348AttributeClass attribClass = defaultMap.get("printer-name");349350if (attribClass != null) {351printerInfo[0] = attribClass.getStringValue();352attribClass = defaultMap.get("printer-uri-supported");353IPPPrintService.debug_println(debugPrefix+354"printer-uri-supported="+attribClass);355if (attribClass != null) {356printerInfo[1] = attribClass.getStringValue();357} else {358printerInfo[1] = null;359}360os.close();361urlConnection.disconnect();362return printerInfo.clone();363}364}365os.close();366urlConnection.disconnect();367}368} catch (Exception e) {369}370return null;371}372373374/**375* Get list of all CUPS printers using IPP.376*/377static String[] getAllPrinters() {378try {379URL url = new URL("http", getServer(), getPort(), "");380381final HttpURLConnection urlConnection =382IPPPrintService.getIPPConnection(url);383384if (urlConnection != null) {385OutputStream os = java.security.AccessController.386doPrivileged(new java.security.PrivilegedAction<OutputStream>() {387public OutputStream run() {388try {389return urlConnection.getOutputStream();390} catch (Exception e) {391}392return null;393}394});395396if (os == null) {397return null;398}399400AttributeClass[] attCl = {401AttributeClass.ATTRIBUTES_CHARSET,402AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE,403new AttributeClass("requested-attributes",404AttributeClass.TAG_KEYWORD,405"printer-uri-supported")406};407408if (IPPPrintService.writeIPPRequest(os,409IPPPrintService.OP_CUPS_GET_PRINTERS, attCl)) {410411InputStream is = urlConnection.getInputStream();412HashMap<String, AttributeClass>[] responseMap =413IPPPrintService.readIPPResponse(is);414415is.close();416os.close();417urlConnection.disconnect();418419if (responseMap == null || responseMap.length == 0) {420return null;421}422423ArrayList<String> printerNames = new ArrayList<>();424for (int i=0; i< responseMap.length; i++) {425AttributeClass attribClass =426responseMap[i].get("printer-uri-supported");427428if (attribClass != null) {429String nameStr = attribClass.getStringValue();430printerNames.add(nameStr);431}432}433return printerNames.toArray(new String[] {});434} else {435os.close();436urlConnection.disconnect();437}438}439440} catch (Exception e) {441}442return null;443444}445446/**447* Returns CUPS server name.448*/449public static String getServer() {450return cupsServer;451}452453/**454* Returns CUPS port number.455*/456public static int getPort() {457return cupsPort;458}459460/**461* Detects if CUPS is running.462*/463public static boolean isCupsRunning() {464IPPPrintService.debug_println(debugPrefix+"libFound "+libFound);465if (libFound) {466IPPPrintService.debug_println(debugPrefix+"CUPS server "+getServer()+467" port "+getPort());468return canConnect(getServer(), getPort());469} else {470return false;471}472}473474475}476477478