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 } 256 return null; 257 } 258 }); 259 260 if (os == null) { 261 return null; 262 } 263 264 AttributeClass attCl[] = { 265 AttributeClass.ATTRIBUTES_CHARSET, 266 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE, 267 new AttributeClass("requested-attributes", 268 AttributeClass.TAG_URI, 269 "printer-uri") 270 }; 271 272 if (IPPPrintService.writeIPPRequest(os, 273 IPPPrintService.OP_CUPS_GET_DEFAULT, 274 attCl)) { 275 276 HashMap defaultMap = null; 277 String[] printerInfo = new String[2]; 278 InputStream is = urlConnection.getInputStream(); 279 HashMap[] responseMap = IPPPrintService.readIPPResponse( 280 is); 281 is.close(); 282 283 if (responseMap != null && responseMap.length > 0) { 284 defaultMap = responseMap[0]; 285 } 286 287 if (defaultMap == null) { 288 os.close(); 289 urlConnection.disconnect(); 290 291 /* CUPS on OS X, as initially configured, considers the 292 * default printer to be the last one used that's 293 * presently available. So if no default was 294 * reported, exec lpstat -d which has all the Apple 295 * special behaviour for this built in. 296 */ 297 if (UnixPrintServiceLookup.isMac()) { 298 printerInfo[0] = UnixPrintServiceLookup. 299 getDefaultPrinterNameSysV(); 300 printerInfo[1] = null; 301 return printerInfo.clone(); 302 } else { 303 return null; 304 } 305 } 306 307 308 AttributeClass attribClass = (AttributeClass) 309 defaultMap.get("printer-name"); 310 311 if (attribClass != null) { 312 printerInfo[0] = attribClass.getStringValue(); 313 attribClass = (AttributeClass)defaultMap.get("device-uri"); 314 if (attribClass != null) { 315 printerInfo[1] = attribClass.getStringValue(); 316 } else { 317 printerInfo[1] = null; 318 } 319 os.close(); 320 urlConnection.disconnect(); 321 return printerInfo.clone(); 322 } 323 } 324 os.close(); 325 urlConnection.disconnect(); 326 } 327 } catch (Exception e) { 328 } 329 return null; 330 } 331 332 333 /** 334 * Get list of all CUPS printers using IPP. 335 */ 336 static String[] getAllPrinters() { 337 try { 338 URL url = new URL("http", getServer(), getPort(), ""); 339 340 final HttpURLConnection urlConnection = 341 IPPPrintService.getIPPConnection(url); 342 343 if (urlConnection != null) { 344 OutputStream os = (OutputStream)java.security.AccessController. 345 doPrivileged(new java.security.PrivilegedAction() { 346 public Object run() { 347 try { 348 return urlConnection.getOutputStream(); 349 } catch (Exception e) { 350 } 351 return null; 352 } 353 }); 354 355 if (os == null) { 356 return null; 357 } 358 359 AttributeClass attCl[] = { 360 AttributeClass.ATTRIBUTES_CHARSET, 361 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE, 362 new AttributeClass("requested-attributes", 363 AttributeClass.TAG_KEYWORD, 364 "printer-uri-supported") 365 }; 366 367 if (IPPPrintService.writeIPPRequest(os, 368 IPPPrintService.OP_CUPS_GET_PRINTERS, attCl)) { 369 370 InputStream is = urlConnection.getInputStream(); 371 HashMap[] responseMap = 372 IPPPrintService.readIPPResponse(is); 373 374 is.close(); 375 os.close(); 376 urlConnection.disconnect(); 377 378 if (responseMap == null || responseMap.length == 0) { 379 return null; 380 } 381 382 ArrayList printerNames = new ArrayList(); 383 for (int i=0; i< responseMap.length; i++) { 384 AttributeClass attribClass = (AttributeClass) 385 responseMap[i].get("printer-uri-supported"); 386 387 if (attribClass != null) { 388 String nameStr = attribClass.getStringValue(); 389 printerNames.add(nameStr); 390 } 391 } 392 return (String[])printerNames.toArray(new String[] {}); 393 } else { 394 os.close(); 395 urlConnection.disconnect(); 396 } 397 } 398 399 } catch (Exception e) { 400 } 401 return null; 402 403 } 404 405 /** 406 * Returns CUPS server name. 407 */ 408 public static String getServer() { 409 return cupsServer; 410 } 411 412 /** 413 * Returns CUPS port number. 414 */ 415 public static int getPort() { 416 return cupsPort; 417 } 418 419 /** 420 * Detects if CUPS is running. 421 */ 422 public static boolean isCupsRunning() { 423 IPPPrintService.debug_println(debugPrefix+"libFound "+libFound); 424 if (libFound) { 425 IPPPrintService.debug_println(debugPrefix+"CUPS server "+getServer()+ 426 " port "+getPort()); 427 return canConnect(getServer(), getPort()); 428 } else { 429 return false; 430 } 431 } 432 433 434 }