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