1 /* 2 * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.print; 27 28 import java.net.URL; 29 import java.net.HttpURLConnection; 30 import java.io.OutputStream; 31 import java.io.InputStream; 32 import java.util.ArrayList; 33 import java.util.HashMap; 34 import sun.print.IPPPrintService; 35 import sun.print.CustomMediaSizeName; 36 import sun.print.CustomMediaTray; 37 import javax.print.attribute.standard.Media; 38 import javax.print.attribute.standard.MediaSizeName; 39 import javax.print.attribute.standard.MediaSize; 40 import javax.print.attribute.standard.MediaTray; 41 import javax.print.attribute.standard.MediaPrintableArea; 42 import javax.print.attribute.standard.PrinterResolution; 43 import javax.print.attribute.Size2DSyntax; 44 import javax.print.attribute.Attribute; 45 import javax.print.attribute.EnumSyntax; 46 import javax.print.attribute.standard.PrinterName; 47 48 49 public class CUPSPrinter { 50 private static final String debugPrefix = "CUPSPrinter>> "; 51 private static final double PRINTER_DPI = 72.0; 52 private boolean initialized; 53 private static native String getCupsServer(); 54 private static native int getCupsPort(); 55 private static native boolean canConnect(String server, int port); 56 private static native boolean initIDs(); 57 // These functions need to be synchronized as 58 // CUPS does not support multi-threading. 59 private static synchronized native String[] getMedia(String printer); 60 private static synchronized native float[] getPageSizes(String printer); 61 private static synchronized native void 62 getResolutions(String printer, ArrayList<Integer> resolutionList); 63 //public static boolean useIPPMedia = false; will be used later 64 65 private MediaPrintableArea[] cupsMediaPrintables; 66 private MediaSizeName[] cupsMediaSNames; 67 private CustomMediaSizeName[] cupsCustomMediaSNames; 68 private MediaTray[] cupsMediaTrays; 69 70 public int nPageSizes = 0; 71 public int nTrays = 0; 72 private String[] media; 73 private float[] pageSizes; 74 int[] resolutionsArray; 75 private String printer; 76 77 private static boolean libFound; 78 private static String cupsServer = null; 79 private static int cupsPort = 0; 80 81 static { 82 // load awt library to access native code 83 java.security.AccessController.doPrivileged( 84 new java.security.PrivilegedAction<Void>() { 85 public Void run() { 86 System.loadLibrary("awt"); 87 return null; 88 } 89 }); 90 libFound = initIDs(); 91 if (libFound) { 92 cupsServer = getCupsServer(); 93 cupsPort = getCupsPort(); 94 } 95 } 96 97 98 CUPSPrinter (String printerName) { 99 if (printerName == null) { 100 throw new IllegalArgumentException("null printer name"); 101 } 102 printer = printerName; 103 cupsMediaSNames = null; 104 cupsMediaPrintables = null; 105 cupsMediaTrays = null; 106 initialized = false; 107 108 if (!libFound) { 109 throw new RuntimeException("cups lib not found"); 110 } else { 111 // get page + tray names 112 media = getMedia(printer); 113 if (media == null) { 114 // either PPD file is not found or printer is unknown 115 throw new RuntimeException("error getting PPD"); 116 } 117 118 // get sizes 119 pageSizes = getPageSizes(printer); 120 if (pageSizes != null) { 121 nPageSizes = pageSizes.length/6; 122 123 nTrays = media.length/2-nPageSizes; 124 assert (nTrays >= 0); 125 } 126 ArrayList<Integer> resolutionList = new ArrayList<>(); 127 getResolutions(printer, resolutionList); 128 resolutionsArray = new int[resolutionList.size()]; 129 for (int i=0; i < resolutionList.size(); i++) { 130 resolutionsArray[i] = resolutionList.get(i); 131 } 132 } 133 } 134 135 136 /** 137 * Returns array of MediaSizeNames derived from PPD. 138 */ 139 MediaSizeName[] getMediaSizeNames() { 140 initMedia(); 141 return cupsMediaSNames; 142 } 143 144 145 /** 146 * Returns array of Custom MediaSizeNames derived from PPD. 147 */ 148 CustomMediaSizeName[] getCustomMediaSizeNames() { 149 initMedia(); 150 return cupsCustomMediaSNames; 151 } 152 153 public int getDefaultMediaIndex() { 154 return ((pageSizes.length >1) ? (int)(pageSizes[pageSizes.length -1]) : 0); 155 } 156 157 /** 158 * Returns array of MediaPrintableArea derived from PPD. 159 */ 160 MediaPrintableArea[] getMediaPrintableArea() { 161 initMedia(); 162 return cupsMediaPrintables; 163 } 164 165 /** 166 * Returns array of MediaTrays derived from PPD. 167 */ 168 MediaTray[] getMediaTrays() { 169 initMedia(); 170 return cupsMediaTrays; 171 } 172 173 /** 174 * return the raw packed array of supported printer resolutions. 175 */ 176 int[] getRawResolutions() { 177 return resolutionsArray; 178 } 179 180 /** 181 * Initialize media by translating PPD info to PrintService attributes. 182 */ 183 private synchronized void initMedia() { 184 if (initialized) { 185 return; 186 } else { 187 initialized = true; 188 } 189 190 if (pageSizes == null) { 191 return; 192 } 193 194 cupsMediaPrintables = new MediaPrintableArea[nPageSizes]; 195 cupsMediaSNames = new MediaSizeName[nPageSizes]; 196 cupsCustomMediaSNames = new CustomMediaSizeName[nPageSizes]; 197 198 CustomMediaSizeName msn; 199 MediaPrintableArea mpa; 200 float length, width, x, y, w, h; 201 202 // initialize names and printables 203 for (int i=0; i<nPageSizes; i++) { 204 // media width and length 205 width = (float)(pageSizes[i*6]/PRINTER_DPI); 206 length = (float)(pageSizes[i*6+1]/PRINTER_DPI); 207 // media printable area 208 x = (float)(pageSizes[i*6+2]/PRINTER_DPI); 209 h = (float)(pageSizes[i*6+3]/PRINTER_DPI); 210 w = (float)(pageSizes[i*6+4]/PRINTER_DPI); 211 y = (float)(pageSizes[i*6+5]/PRINTER_DPI); 212 213 msn = new CustomMediaSizeName(media[i*2], media[i*2+1], 214 width, length); 215 216 // add to list of standard MediaSizeNames 217 if ((cupsMediaSNames[i] = msn.getStandardMedia()) == null) { 218 // add custom if no matching standard media 219 cupsMediaSNames[i] = msn; 220 221 // add this new custom msn to MediaSize array 222 if ((width > 0.0) && (length > 0.0)) { 223 try { 224 new MediaSize(width, length, 225 Size2DSyntax.INCH, msn); 226 } catch (IllegalArgumentException e) { 227 /* PDF printer in Linux for Ledger paper causes 228 "IllegalArgumentException: X dimension > Y dimension". 229 We rotate based on IPP spec. */ 230 new MediaSize(length, width, Size2DSyntax.INCH, msn); 231 } 232 } 233 } 234 235 // add to list of custom MediaSizeName 236 // for internal use of IPPPrintService 237 cupsCustomMediaSNames[i] = msn; 238 239 mpa = null; 240 try { 241 mpa = new MediaPrintableArea(x, y, w, h, 242 MediaPrintableArea.INCH); 243 } catch (IllegalArgumentException e) { 244 if (width > 0 && length > 0) { 245 mpa = new MediaPrintableArea(0, 0, width, length, 246 MediaPrintableArea.INCH); 247 } 248 } 249 cupsMediaPrintables[i] = mpa; 250 } 251 252 // initialize trays 253 cupsMediaTrays = new MediaTray[nTrays]; 254 255 MediaTray mt; 256 for (int i=0; i<nTrays; i++) { 257 mt = new CustomMediaTray(media[(nPageSizes+i)*2], 258 media[(nPageSizes+i)*2+1]); 259 cupsMediaTrays[i] = mt; 260 } 261 262 } 263 264 /** 265 * Get CUPS default printer using IPP. 266 * Returns 2 values - index 0 is printer name, index 1 is the uri. 267 */ 268 static String[] getDefaultPrinter() { 269 try { 270 URL url = new URL("http", getServer(), getPort(), ""); 271 final HttpURLConnection urlConnection = 272 IPPPrintService.getIPPConnection(url); 273 274 if (urlConnection != null) { 275 OutputStream os = java.security.AccessController. 276 doPrivileged(new java.security.PrivilegedAction<OutputStream>() { 277 public OutputStream run() { 278 try { 279 return urlConnection.getOutputStream(); 280 } catch (Exception e) { 281 IPPPrintService.debug_println(debugPrefix+e); 282 } 283 return null; 284 } 285 }); 286 287 if (os == null) { 288 return null; 289 } 290 291 AttributeClass attCl[] = { 292 AttributeClass.ATTRIBUTES_CHARSET, 293 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE, 294 new AttributeClass("requested-attributes", 295 AttributeClass.TAG_URI, 296 "printer-uri") 297 }; 298 299 if (IPPPrintService.writeIPPRequest(os, 300 IPPPrintService.OP_CUPS_GET_DEFAULT, 301 attCl)) { 302 303 HashMap<String, AttributeClass> defaultMap = null; 304 String[] printerInfo = new String[2]; 305 InputStream is = urlConnection.getInputStream(); 306 HashMap<String, AttributeClass>[] responseMap = IPPPrintService.readIPPResponse( 307 is); 308 is.close(); 309 310 if (responseMap != null && responseMap.length > 0) { 311 defaultMap = responseMap[0]; 312 } else { 313 IPPPrintService.debug_println(debugPrefix+ 314 " empty response map for GET_DEFAULT."); 315 } 316 317 if (defaultMap == null) { 318 os.close(); 319 urlConnection.disconnect(); 320 321 /* CUPS on OS X, as initially configured, considers the 322 * default printer to be the last one used that's 323 * presently available. So if no default was 324 * reported, exec lpstat -d which has all the Apple 325 * special behaviour for this built in. 326 */ 327 if (PrintServiceLookupProvider.isMac()) { 328 printerInfo[0] = PrintServiceLookupProvider. 329 getDefaultPrinterNameSysV(); 330 printerInfo[1] = null; 331 return printerInfo.clone(); 332 } else { 333 return null; 334 } 335 } 336 337 338 AttributeClass attribClass = defaultMap.get("printer-name"); 339 340 if (attribClass != null) { 341 printerInfo[0] = attribClass.getStringValue(); 342 attribClass = defaultMap.get("printer-uri-supported"); 343 IPPPrintService.debug_println(debugPrefix+ 344 "printer-uri-supported="+attribClass); 345 if (attribClass != null) { 346 printerInfo[1] = attribClass.getStringValue(); 347 } else { 348 printerInfo[1] = null; 349 } 350 os.close(); 351 urlConnection.disconnect(); 352 return printerInfo.clone(); 353 } 354 } 355 os.close(); 356 urlConnection.disconnect(); 357 } 358 } catch (Exception e) { 359 } 360 return null; 361 } 362 363 364 /** 365 * Get list of all CUPS printers using IPP. 366 */ 367 static String[] getAllPrinters() { 368 try { 369 URL url = new URL("http", getServer(), getPort(), ""); 370 371 final HttpURLConnection urlConnection = 372 IPPPrintService.getIPPConnection(url); 373 374 if (urlConnection != null) { 375 OutputStream os = java.security.AccessController. 376 doPrivileged(new java.security.PrivilegedAction<OutputStream>() { 377 public OutputStream run() { 378 try { 379 return urlConnection.getOutputStream(); 380 } catch (Exception e) { 381 } 382 return null; 383 } 384 }); 385 386 if (os == null) { 387 return null; 388 } 389 390 AttributeClass attCl[] = { 391 AttributeClass.ATTRIBUTES_CHARSET, 392 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE, 393 new AttributeClass("requested-attributes", 394 AttributeClass.TAG_KEYWORD, 395 "printer-uri-supported") 396 }; 397 398 if (IPPPrintService.writeIPPRequest(os, 399 IPPPrintService.OP_CUPS_GET_PRINTERS, attCl)) { 400 401 InputStream is = urlConnection.getInputStream(); 402 HashMap<String, AttributeClass>[] responseMap = 403 IPPPrintService.readIPPResponse(is); 404 405 is.close(); 406 os.close(); 407 urlConnection.disconnect(); 408 409 if (responseMap == null || responseMap.length == 0) { 410 return null; 411 } 412 413 ArrayList<String> printerNames = new ArrayList<>(); 414 for (int i=0; i< responseMap.length; i++) { 415 AttributeClass attribClass = 416 responseMap[i].get("printer-uri-supported"); 417 418 if (attribClass != null) { 419 String nameStr = attribClass.getStringValue(); 420 printerNames.add(nameStr); 421 } 422 } 423 return printerNames.toArray(new String[] {}); 424 } else { 425 os.close(); 426 urlConnection.disconnect(); 427 } 428 } 429 430 } catch (Exception e) { 431 } 432 return null; 433 434 } 435 436 /** 437 * Returns CUPS server name. 438 */ 439 public static String getServer() { 440 return cupsServer; 441 } 442 443 /** 444 * Returns CUPS port number. 445 */ 446 public static int getPort() { 447 return cupsPort; 448 } 449 450 /** 451 * Detects if CUPS is running. 452 */ 453 public static boolean isCupsRunning() { 454 IPPPrintService.debug_println(debugPrefix+"libFound "+libFound); 455 if (libFound) { 456 IPPPrintService.debug_println(debugPrefix+"CUPS server "+getServer()+ 457 " port "+getPort()); 458 return canConnect(getServer(), getPort()); 459 } else { 460 return false; 461 } 462 } 463 464 465 }