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