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.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 144 /** 145 * Returns array of MediaPrintableArea derived from PPD. 146 */ 147 public MediaPrintableArea[] getMediaPrintableArea() { 148 initMedia(); 149 return cupsMediaPrintables; 150 } 151 152 /** 153 * Returns array of MediaTrays derived from PPD. 154 */ 155 public MediaTray[] getMediaTrays() { 156 initMedia(); 157 return cupsMediaTrays; 158 } 159 160 161 /** 162 * Initialize media by translating PPD info to PrintService attributes. 163 */ 164 private synchronized void initMedia() { 165 if (initialized) { 166 return; 167 } else { 168 initialized = true; 169 } 170 171 if (pageSizes == null) { 172 return; 173 } 174 175 cupsMediaPrintables = new MediaPrintableArea[nPageSizes]; 176 cupsMediaSNames = new MediaSizeName[nPageSizes]; 177 cupsCustomMediaSNames = new CustomMediaSizeName[nPageSizes]; 178 179 CustomMediaSizeName msn; 180 MediaPrintableArea mpa; 181 float length, width, x, y, w, h; 182 183 // initialize names and printables 184 for (int i=0; i<nPageSizes; i++) { 185 // media width and length 186 width = (float)(pageSizes[i*6]/PRINTER_DPI); 187 length = (float)(pageSizes[i*6+1]/PRINTER_DPI); 188 // media printable area 189 x = (float)(pageSizes[i*6+2]/PRINTER_DPI); 190 h = (float)(pageSizes[i*6+3]/PRINTER_DPI); 191 w = (float)(pageSizes[i*6+4]/PRINTER_DPI); 192 y = (float)(pageSizes[i*6+5]/PRINTER_DPI); 193 194 msn = new CustomMediaSizeName(media[i*2], media[i*2+1], 195 width, length); 196 197 // add to list of standard MediaSizeNames 198 if ((cupsMediaSNames[i] = msn.getStandardMedia()) == null) { 199 // add custom if no matching standard media 200 cupsMediaSNames[i] = msn; 201 202 // add this new custom msn to MediaSize array 203 if ((width > 0.0) && (length > 0.0)) { 204 new MediaSize(width, length, 205 Size2DSyntax.INCH, msn); 206 } 207 } 208 209 // add to list of custom MediaSizeName 210 // for internal use of IPPPrintService 211 cupsCustomMediaSNames[i] = msn; 212 213 mpa = null; 214 try { 215 mpa = new MediaPrintableArea(x, y, w, h, 216 MediaPrintableArea.INCH); 217 } catch (IllegalArgumentException e) { 218 if (width > 0 && length > 0) { 219 mpa = new MediaPrintableArea(0, 0, width, length, 220 MediaPrintableArea.INCH); 221 } 222 } 223 cupsMediaPrintables[i] = mpa; 224 } 225 226 // initialize trays 227 cupsMediaTrays = new MediaTray[nTrays]; 228 229 MediaTray mt; 230 for (int i=0; i<nTrays; i++) { 231 mt = new CustomMediaTray(media[(nPageSizes+i)*2], 232 media[(nPageSizes+i)*2+1]); 233 cupsMediaTrays[i] = mt; 234 } 235 236 } 237 238 /** 239 * Get CUPS default printer using IPP. 240 * Returns 2 values - index 0 is printer name, index 1 is the uri. 241 */ 242 static String[] getDefaultPrinter() { 243 try { 244 URL url = new URL("http", getServer(), getPort(), ""); 245 final HttpURLConnection urlConnection = 246 IPPPrintService.getIPPConnection(url); 247 248 if (urlConnection != null) { 249 OutputStream os = (OutputStream)java.security.AccessController. 250 doPrivileged(new java.security.PrivilegedAction() { 251 public Object run() { 252 try { 253 return urlConnection.getOutputStream(); 254 } catch (Exception e) { 255 IPPPrintService.debug_println(debugPrefix+e); 256 } 257 return null; 258 } 259 }); 260 261 if (os == null) { 262 return null; 263 } 264 265 AttributeClass attCl[] = { 266 AttributeClass.ATTRIBUTES_CHARSET, 267 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE, 268 new AttributeClass("requested-attributes", 269 AttributeClass.TAG_URI, 270 "printer-uri") 271 }; 272 273 if (IPPPrintService.writeIPPRequest(os, 274 IPPPrintService.OP_CUPS_GET_DEFAULT, 275 attCl)) { 276 277 HashMap defaultMap = null; 278 String[] printerInfo = new String[2]; 279 InputStream is = urlConnection.getInputStream(); 280 HashMap[] responseMap = IPPPrintService.readIPPResponse( 281 is); 282 is.close(); 283 284 if (responseMap != null && responseMap.length > 0) { 285 defaultMap = responseMap[0]; 286 } else { 287 IPPPrintService.debug_println(debugPrefix+ 288 " empty response map for GET_DEFAULT."); 289 } 290 291 if (defaultMap == null) { 292 os.close(); 293 urlConnection.disconnect(); 294 295 /* CUPS on OS X, as initially configured, considers the 296 * default printer to be the last one used that's 297 * presently available. So if no default was 298 * reported, exec lpstat -d which has all the Apple 299 * special behaviour for this built in. 300 */ 301 if (UnixPrintServiceLookup.isMac()) { 302 printerInfo[0] = UnixPrintServiceLookup. 303 getDefaultPrinterNameSysV(); 304 printerInfo[1] = null; 305 return printerInfo.clone(); 306 } else { 307 return null; 308 } 309 } 310 311 312 AttributeClass attribClass = (AttributeClass) 313 defaultMap.get("printer-name"); 314 315 if (attribClass != null) { 316 printerInfo[0] = attribClass.getStringValue(); 317 attribClass = (AttributeClass) 318 defaultMap.get("printer-uri-supported"); 319 IPPPrintService.debug_println(debugPrefix+ 320 "printer-uri-supported="+attribClass); 321 if (attribClass != null) { 322 printerInfo[1] = attribClass.getStringValue(); 323 } else { 324 printerInfo[1] = null; 325 } 326 os.close(); 327 urlConnection.disconnect(); 328 return printerInfo.clone(); 329 } 330 } 331 os.close(); 332 urlConnection.disconnect(); 333 } 334 } catch (Exception e) { 335 } 336 return null; 337 } 338 339 340 /** 341 * Get list of all CUPS printers using IPP. 342 */ 343 static String[] getAllPrinters() { 344 try { 345 URL url = new URL("http", getServer(), getPort(), ""); 346 347 final HttpURLConnection urlConnection = 348 IPPPrintService.getIPPConnection(url); 349 350 if (urlConnection != null) { 351 OutputStream os = (OutputStream)java.security.AccessController. 352 doPrivileged(new java.security.PrivilegedAction() { 353 public Object run() { 354 try { 355 return urlConnection.getOutputStream(); 356 } catch (Exception e) { 357 } 358 return null; 359 } 360 }); 361 362 if (os == null) { 363 return null; 364 } 365 366 AttributeClass attCl[] = { 367 AttributeClass.ATTRIBUTES_CHARSET, 368 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE, 369 new AttributeClass("requested-attributes", 370 AttributeClass.TAG_KEYWORD, 371 "printer-uri-supported") 372 }; 373 374 if (IPPPrintService.writeIPPRequest(os, 375 IPPPrintService.OP_CUPS_GET_PRINTERS, attCl)) { 376 377 InputStream is = urlConnection.getInputStream(); 378 HashMap[] responseMap = 379 IPPPrintService.readIPPResponse(is); 380 381 is.close(); 382 os.close(); 383 urlConnection.disconnect(); 384 385 if (responseMap == null || responseMap.length == 0) { 386 return null; 387 } 388 389 ArrayList printerNames = new ArrayList(); 390 for (int i=0; i< responseMap.length; i++) { 391 AttributeClass attribClass = (AttributeClass) 392 responseMap[i].get("printer-uri-supported"); 393 394 if (attribClass != null) { 395 String nameStr = attribClass.getStringValue(); 396 printerNames.add(nameStr); 397 } 398 } 399 return (String[])printerNames.toArray(new String[] {}); 400 } else { 401 os.close(); 402 urlConnection.disconnect(); 403 } 404 } 405 406 } catch (Exception e) { 407 } 408 return null; 409 410 } 411 412 /** 413 * Returns CUPS server name. 414 */ 415 public static String getServer() { 416 return cupsServer; 417 } 418 419 /** 420 * Returns CUPS port number. 421 */ 422 public static int getPort() { 423 return cupsPort; 424 } 425 426 /** 427 * Detects if CUPS is running. 428 */ 429 public static boolean isCupsRunning() { 430 IPPPrintService.debug_println(debugPrefix+"libFound "+libFound); 431 if (libFound) { 432 IPPPrintService.debug_println(debugPrefix+"CUPS server "+getServer()+ 433 " port "+getPort()); 434 return canConnect(getServer(), getPort()); 435 } else { 436 return false; 437 } 438 } 439 440 441 }