Path: blob/master/src/java.desktop/unix/classes/sun/print/UnixPrintJob.java
41152 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.print;2627import java.net.URI;28import java.net.URL;29import java.io.BufferedInputStream;30import java.io.BufferedOutputStream;31import java.io.BufferedReader;32import java.io.BufferedWriter;33import java.io.File;34import java.io.FileOutputStream;35import java.io.InputStream;36import java.io.InputStreamReader;37import java.io.OutputStream;38import java.io.OutputStreamWriter;39import java.io.IOException;40import java.io.PrintWriter;41import java.io.Reader;42import java.io.StringWriter;43import java.nio.file.Files;44import java.util.Vector;4546import javax.print.CancelablePrintJob;47import javax.print.Doc;48import javax.print.DocFlavor;49import javax.print.PrintService;50import javax.print.PrintException;51import javax.print.event.PrintJobEvent;52import javax.print.event.PrintJobListener;53import javax.print.event.PrintJobAttributeListener;5455import javax.print.attribute.Attribute;56import javax.print.attribute.AttributeSetUtilities;57import javax.print.attribute.DocAttributeSet;58import javax.print.attribute.HashPrintJobAttributeSet;59import javax.print.attribute.HashPrintRequestAttributeSet;60import javax.print.attribute.PrintJobAttribute;61import javax.print.attribute.PrintJobAttributeSet;62import javax.print.attribute.PrintRequestAttribute;63import javax.print.attribute.PrintRequestAttributeSet;64import javax.print.attribute.standard.Copies;65import javax.print.attribute.standard.Destination;66import javax.print.attribute.standard.DocumentName;67import javax.print.attribute.standard.Fidelity;68import javax.print.attribute.standard.JobName;69import javax.print.attribute.standard.JobOriginatingUserName;70import javax.print.attribute.standard.JobSheets;71import javax.print.attribute.standard.Media;72import javax.print.attribute.standard.MediaSize;73import javax.print.attribute.standard.MediaSizeName;74import javax.print.attribute.standard.OrientationRequested;75import javax.print.attribute.standard.RequestingUserName;76import javax.print.attribute.standard.NumberUp;77import javax.print.attribute.standard.Sides;78import javax.print.attribute.standard.PrinterIsAcceptingJobs;7980import java.awt.print.PageFormat;81import java.awt.print.PrinterJob;82import java.awt.print.Pageable;83import java.awt.print.Paper;84import java.awt.print.Printable;85import java.awt.print.PrinterException;86878889public class UnixPrintJob implements CancelablePrintJob {90private static String debugPrefix = "UnixPrintJob>> ";9192private transient Vector<PrintJobListener> jobListeners;93private transient Vector<PrintJobAttributeListener> attrListeners;94private transient Vector<PrintJobAttributeSet> listenedAttributeSets;9596private PrintService service;97private boolean fidelity;98private boolean printing = false;99private boolean printReturned = false;100private PrintRequestAttributeSet reqAttrSet = null;101private PrintJobAttributeSet jobAttrSet = null;102private PrinterJob job;103private Doc doc;104/* these variables used globally to store reference to the print105* data retrieved as a stream. On completion these are always closed106* if non-null.107*/108private InputStream instream = null;109private Reader reader = null;110111/* default values overridden by those extracted from the attributes */112private String jobName = "Java Printing";113private int copies = 1;114private MediaSizeName mediaName = MediaSizeName.NA_LETTER;115private MediaSize mediaSize = MediaSize.NA.LETTER;116private CustomMediaTray customTray = null;117private OrientationRequested orient = OrientationRequested.PORTRAIT;118private NumberUp nUp = null;119private Sides sides = null;120121UnixPrintJob(PrintService service) {122this.service = service;123mDestination = service.getName();124if (PrintServiceLookupProvider.isMac()) {125mDestination = ((IPPPrintService)service).getDest();126}127mDestType = UnixPrintJob.DESTPRINTER;128JobSheets js = (JobSheets)(service.129getDefaultAttributeValue(JobSheets.class));130if (js != null && js.equals(JobSheets.NONE)) {131mNoJobSheet = true;132}133}134135public PrintService getPrintService() {136return service;137}138139public PrintJobAttributeSet getAttributes() {140synchronized (this) {141if (jobAttrSet == null) {142/* just return an empty set until the job is submitted */143PrintJobAttributeSet jobSet = new HashPrintJobAttributeSet();144return AttributeSetUtilities.unmodifiableView(jobSet);145} else {146return jobAttrSet;147}148}149}150151public void addPrintJobListener(PrintJobListener listener) {152synchronized (this) {153if (listener == null) {154return;155}156if (jobListeners == null) {157jobListeners = new Vector<>();158}159jobListeners.add(listener);160}161}162163public void removePrintJobListener(PrintJobListener listener) {164synchronized (this) {165if (listener == null || jobListeners == null ) {166return;167}168jobListeners.remove(listener);169if (jobListeners.isEmpty()) {170jobListeners = null;171}172}173}174175176/* Closes any stream already retrieved for the data.177* We want to avoid unnecessarily asking the Doc to create a stream only178* to get a reference in order to close it because the job failed.179* If the representation class is itself a "stream", this180* closes that stream too.181*/182private void closeDataStreams() {183184if (doc == null) {185return;186}187188Object data = null;189190try {191data = doc.getPrintData();192} catch (IOException e) {193return;194}195196if (instream != null) {197try {198instream.close();199} catch (IOException e) {200} finally {201instream = null;202}203}204else if (reader != null) {205try {206reader.close();207} catch (IOException e) {208} finally {209reader = null;210}211}212else if (data instanceof InputStream) {213try {214((InputStream)data).close();215} catch (IOException e) {216}217}218else if (data instanceof Reader) {219try {220((Reader)data).close();221} catch (IOException e) {222}223}224}225226private void notifyEvent(int reason) {227228/* since this method should always get called, here's where229* we will perform the clean up of any data stream supplied.230*/231switch (reason) {232case PrintJobEvent.DATA_TRANSFER_COMPLETE:233case PrintJobEvent.JOB_CANCELED :234case PrintJobEvent.JOB_FAILED :235case PrintJobEvent.NO_MORE_EVENTS :236case PrintJobEvent.JOB_COMPLETE :237closeDataStreams();238}239240synchronized (this) {241if (jobListeners != null) {242PrintJobListener listener;243PrintJobEvent event = new PrintJobEvent(this, reason);244for (int i = 0; i < jobListeners.size(); i++) {245listener = jobListeners.elementAt(i);246switch (reason) {247248case PrintJobEvent.JOB_CANCELED :249listener.printJobCanceled(event);250break;251252case PrintJobEvent.JOB_FAILED :253listener.printJobFailed(event);254break;255256case PrintJobEvent.DATA_TRANSFER_COMPLETE :257listener.printDataTransferCompleted(event);258break;259260case PrintJobEvent.NO_MORE_EVENTS :261listener.printJobNoMoreEvents(event);262break;263264default:265break;266}267}268}269}270}271272public void addPrintJobAttributeListener(273PrintJobAttributeListener listener,274PrintJobAttributeSet attributes) {275synchronized (this) {276if (listener == null) {277return;278}279if (attrListeners == null) {280attrListeners = new Vector<>();281listenedAttributeSets = new Vector<>();282}283attrListeners.add(listener);284if (attributes == null) {285attributes = new HashPrintJobAttributeSet();286}287listenedAttributeSets.add(attributes);288}289}290291public void removePrintJobAttributeListener(292PrintJobAttributeListener listener) {293synchronized (this) {294if (listener == null || attrListeners == null ) {295return;296}297int index = attrListeners.indexOf(listener);298if (index == -1) {299return;300} else {301attrListeners.remove(index);302listenedAttributeSets.remove(index);303if (attrListeners.isEmpty()) {304attrListeners = null;305listenedAttributeSets = null;306}307}308}309}310311@SuppressWarnings("removal")312public void print(Doc doc, PrintRequestAttributeSet attributes)313throws PrintException {314315synchronized (this) {316if (printing) {317throw new PrintException("already printing");318} else {319printing = true;320}321}322323if ((service.getAttribute(PrinterIsAcceptingJobs.class)) ==324PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS) {325throw new PrintException("Printer is not accepting job.");326}327328this.doc = doc;329/* check if the parameters are valid before doing much processing */330DocFlavor flavor = doc.getDocFlavor();331332Object data;333334try {335data = doc.getPrintData();336} catch (IOException e) {337notifyEvent(PrintJobEvent.JOB_FAILED);338throw new PrintException("can't get print data: " + e.toString());339}340341if (data == null) {342throw new PrintException("Null print data.");343}344345if (flavor == null || (!service.isDocFlavorSupported(flavor))) {346notifyEvent(PrintJobEvent.JOB_FAILED);347throw new PrintJobFlavorException("invalid flavor", flavor);348}349350initializeAttributeSets(doc, attributes);351352getAttributeValues(flavor);353354// set up mOptions355if ((service instanceof IPPPrintService) &&356CUPSPrinter.isCupsRunning()) {357358IPPPrintService.debug_println(debugPrefix+359"instanceof IPPPrintService");360361if (mediaName != null) {362CustomMediaSizeName customMedia =363((IPPPrintService)service).findCustomMedia(mediaName);364if (customMedia != null) {365mOptions = " media="+ customMedia.getChoiceName();366}367}368369if (customTray != null &&370customTray instanceof CustomMediaTray) {371String choice = customTray.getChoiceName();372if (choice != null) {373mOptions += " InputSlot="+choice;374}375}376377if (nUp != null) {378mOptions += " number-up="+nUp.getValue();379}380381if (orient != OrientationRequested.PORTRAIT &&382(flavor != null) &&383!flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE)) {384mOptions += " orientation-requested="+orient.getValue();385}386387if (sides != null) {388mOptions += " sides="+sides;389}390391}392393IPPPrintService.debug_println(debugPrefix+"mOptions "+mOptions);394String repClassName = flavor.getRepresentationClassName();395String val = flavor.getParameter("charset");396String encoding = "us-ascii";397if (val != null && !val.isEmpty()) {398encoding = val;399}400401if (flavor.equals(DocFlavor.INPUT_STREAM.GIF) ||402flavor.equals(DocFlavor.INPUT_STREAM.JPEG) ||403flavor.equals(DocFlavor.INPUT_STREAM.PNG) ||404flavor.equals(DocFlavor.BYTE_ARRAY.GIF) ||405flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) ||406flavor.equals(DocFlavor.BYTE_ARRAY.PNG)) {407try {408instream = doc.getStreamForBytes();409if (instream == null) {410notifyEvent(PrintJobEvent.JOB_FAILED);411throw new PrintException("No stream for data");412}413if (!(service instanceof IPPPrintService &&414((IPPPrintService)service).isIPPSupportedImages(415flavor.getMimeType()))) {416printableJob(new ImagePrinter(instream));417if (service instanceof IPPPrintService) {418((IPPPrintService)service).wakeNotifier();419} else {420((UnixPrintService)service).wakeNotifier();421}422return;423}424} catch (ClassCastException cce) {425notifyEvent(PrintJobEvent.JOB_FAILED);426throw new PrintException(cce);427} catch (IOException ioe) {428notifyEvent(PrintJobEvent.JOB_FAILED);429throw new PrintException(ioe);430}431} else if (flavor.equals(DocFlavor.URL.GIF) ||432flavor.equals(DocFlavor.URL.JPEG) ||433flavor.equals(DocFlavor.URL.PNG)) {434try {435URL url = (URL)data;436if ((service instanceof IPPPrintService) &&437((IPPPrintService)service).isIPPSupportedImages(438flavor.getMimeType())) {439instream = url.openStream();440} else {441printableJob(new ImagePrinter(url));442if (service instanceof IPPPrintService) {443((IPPPrintService)service).wakeNotifier();444} else {445((UnixPrintService)service).wakeNotifier();446}447return;448}449} catch (ClassCastException cce) {450notifyEvent(PrintJobEvent.JOB_FAILED);451throw new PrintException(cce);452} catch (IOException e) {453notifyEvent(PrintJobEvent.JOB_FAILED);454throw new PrintException(e.toString());455}456} else if (flavor.equals(DocFlavor.CHAR_ARRAY.TEXT_PLAIN) ||457flavor.equals(DocFlavor.READER.TEXT_PLAIN) ||458flavor.equals(DocFlavor.STRING.TEXT_PLAIN)) {459try {460reader = doc.getReaderForText();461if (reader == null) {462notifyEvent(PrintJobEvent.JOB_FAILED);463throw new PrintException("No reader for data");464}465} catch (IOException ioe) {466notifyEvent(PrintJobEvent.JOB_FAILED);467throw new PrintException(ioe.toString());468}469} else if (repClassName.equals("[B") ||470repClassName.equals("java.io.InputStream")) {471try {472instream = doc.getStreamForBytes();473if (instream == null) {474notifyEvent(PrintJobEvent.JOB_FAILED);475throw new PrintException("No stream for data");476}477} catch (IOException ioe) {478notifyEvent(PrintJobEvent.JOB_FAILED);479throw new PrintException(ioe.toString());480}481} else if (repClassName.equals("java.net.URL")) {482/*483* This extracts the data from the URL and passes it the content484* directly to the print service as a file.485* This is appropriate for the current implementation where lp or486* lpr is always used to spool the data. We expect to revise the487* implementation to provide more complete IPP support (ie not just488* CUPS) and at that time the job will be spooled via IPP489* and the URL490* itself should be sent to the IPP print service not the content.491*/492URL url = (URL)data;493try {494instream = url.openStream();495} catch (IOException e) {496notifyEvent(PrintJobEvent.JOB_FAILED);497throw new PrintException(e.toString());498}499} else if (repClassName.equals("java.awt.print.Pageable")) {500try {501pageableJob((Pageable)doc.getPrintData());502if (service instanceof IPPPrintService) {503((IPPPrintService)service).wakeNotifier();504} else {505((UnixPrintService)service).wakeNotifier();506}507return;508} catch (ClassCastException cce) {509notifyEvent(PrintJobEvent.JOB_FAILED);510throw new PrintException(cce);511} catch (IOException ioe) {512notifyEvent(PrintJobEvent.JOB_FAILED);513throw new PrintException(ioe);514}515} else if (repClassName.equals("java.awt.print.Printable")) {516try {517printableJob((Printable)doc.getPrintData());518if (service instanceof IPPPrintService) {519((IPPPrintService)service).wakeNotifier();520} else {521((UnixPrintService)service).wakeNotifier();522}523return;524} catch (ClassCastException cce) {525notifyEvent(PrintJobEvent.JOB_FAILED);526throw new PrintException(cce);527} catch (IOException ioe) {528notifyEvent(PrintJobEvent.JOB_FAILED);529throw new PrintException(ioe);530}531} else {532notifyEvent(PrintJobEvent.JOB_FAILED);533throw new PrintException("unrecognized class: "+repClassName);534}535536// now spool the print data.537PrinterOpener po = new PrinterOpener();538java.security.AccessController.doPrivileged(po);539if (po.pex != null) {540throw po.pex;541}542OutputStream output = po.result;543544/* There are three cases:545* 1) Text data from a Reader, just pass through.546* 2) Text data from an input stream which we must read using the547* correct encoding548* 3) Raw byte data from an InputStream we don't interpret as text,549* just pass through: eg postscript.550*/551552BufferedWriter bw = null;553if ((instream == null && reader != null)) {554BufferedReader br = new BufferedReader(reader);555OutputStreamWriter osw = new OutputStreamWriter(output);556bw = new BufferedWriter(osw);557char []buffer = new char[1024];558int cread;559560try {561while ((cread = br.read(buffer, 0, buffer.length)) >=0) {562bw.write(buffer, 0, cread);563}564br.close();565bw.flush();566bw.close();567} catch (IOException e) {568notifyEvent(PrintJobEvent.JOB_FAILED);569throw new PrintException (e);570}571} else if (instream != null &&572flavor.getMediaType().equalsIgnoreCase("text")) {573try {574575InputStreamReader isr = new InputStreamReader(instream,576encoding);577BufferedReader br = new BufferedReader(isr);578OutputStreamWriter osw = new OutputStreamWriter(output);579bw = new BufferedWriter(osw);580char []buffer = new char[1024];581int cread;582583while ((cread = br.read(buffer, 0, buffer.length)) >=0) {584bw.write(buffer, 0, cread);585}586bw.flush();587} catch (IOException e) {588notifyEvent(PrintJobEvent.JOB_FAILED);589throw new PrintException (e);590} finally {591try {592if (bw != null) {593bw.close();594}595} catch (IOException e) {596}597}598} else if (instream != null) {599try (BufferedInputStream bin = new BufferedInputStream(instream);600BufferedOutputStream bout = new BufferedOutputStream(output)) {601bin.transferTo(bout);602} catch (IOException e) {603notifyEvent(PrintJobEvent.JOB_FAILED);604throw new PrintException(e);605}606}607notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);608609if (mDestType == UnixPrintJob.DESTPRINTER) {610PrinterSpooler spooler = new PrinterSpooler();611java.security.AccessController.doPrivileged(spooler);612if (spooler.pex != null) {613throw spooler.pex;614}615}616notifyEvent(PrintJobEvent.NO_MORE_EVENTS);617if (service instanceof IPPPrintService) {618((IPPPrintService)service).wakeNotifier();619} else {620((UnixPrintService)service).wakeNotifier();621}622}623624public void printableJob(Printable printable) throws PrintException {625try {626synchronized(this) {627if (job != null) { // shouldn't happen628throw new PrintException("already printing");629} else {630job = new PSPrinterJob();631}632}633job.setPrintService(getPrintService());634job.setCopies(copies);635job.setJobName(jobName);636PageFormat pf = new PageFormat();637if (mediaSize != null) {638Paper p = new Paper();639p.setSize(mediaSize.getX(MediaSize.INCH)*72.0,640mediaSize.getY(MediaSize.INCH)*72.0);641p.setImageableArea(72.0, 72.0, p.getWidth()-144.0,642p.getHeight()-144.0);643pf.setPaper(p);644}645if (orient == OrientationRequested.REVERSE_LANDSCAPE) {646pf.setOrientation(PageFormat.REVERSE_LANDSCAPE);647} else if (orient == OrientationRequested.LANDSCAPE) {648pf.setOrientation(PageFormat.LANDSCAPE);649}650job.setPrintable(printable, pf);651job.print(reqAttrSet);652notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);653return;654} catch (PrinterException pe) {655notifyEvent(PrintJobEvent.JOB_FAILED);656throw new PrintException(pe);657} finally {658printReturned = true;659notifyEvent(PrintJobEvent.NO_MORE_EVENTS);660}661}662663public void pageableJob(Pageable pageable) throws PrintException {664try {665synchronized(this) {666if (job != null) { // shouldn't happen667throw new PrintException("already printing");668} else {669job = new PSPrinterJob();670}671}672job.setPrintService(getPrintService());673job.setCopies(copies);674job.setJobName(jobName);675job.setPageable(pageable);676job.print(reqAttrSet);677notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);678return;679} catch (PrinterException pe) {680notifyEvent(PrintJobEvent.JOB_FAILED);681throw new PrintException(pe);682} finally {683printReturned = true;684notifyEvent(PrintJobEvent.NO_MORE_EVENTS);685}686}687/* There's some inefficiency here as the job set is created even though688* it may never be requested.689*/690private synchronized void691initializeAttributeSets(Doc doc, PrintRequestAttributeSet reqSet) {692693reqAttrSet = new HashPrintRequestAttributeSet();694jobAttrSet = new HashPrintJobAttributeSet();695696Attribute[] attrs;697if (reqSet != null) {698reqAttrSet.addAll(reqSet);699attrs = reqSet.toArray();700for (int i=0; i<attrs.length; i++) {701if (attrs[i] instanceof PrintJobAttribute) {702jobAttrSet.add(attrs[i]);703}704}705}706707DocAttributeSet docSet = doc.getAttributes();708if (docSet != null) {709attrs = docSet.toArray();710for (int i=0; i<attrs.length; i++) {711if (attrs[i] instanceof PrintRequestAttribute) {712reqAttrSet.add(attrs[i]);713}714if (attrs[i] instanceof PrintJobAttribute) {715jobAttrSet.add(attrs[i]);716}717}718}719720/* add the user name to the job */721String userName = "";722try {723userName = System.getProperty("user.name");724} catch (SecurityException se) {725}726727if (userName == null || userName.isEmpty()) {728RequestingUserName ruName =729(RequestingUserName)reqSet.get(RequestingUserName.class);730if (ruName != null) {731jobAttrSet.add(732new JobOriginatingUserName(ruName.getValue(),733ruName.getLocale()));734} else {735jobAttrSet.add(new JobOriginatingUserName("", null));736}737} else {738jobAttrSet.add(new JobOriginatingUserName(userName, null));739}740741/* if no job name supplied use doc name (if supplied), if none and742* its a URL use that, else finally anything .. */743if (jobAttrSet.get(JobName.class) == null) {744JobName jobName;745if (docSet != null && docSet.get(DocumentName.class) != null) {746DocumentName docName =747(DocumentName)docSet.get(DocumentName.class);748jobName = new JobName(docName.getValue(), docName.getLocale());749jobAttrSet.add(jobName);750} else {751String str = "JPS Job:" + doc;752try {753Object printData = doc.getPrintData();754if (printData instanceof URL) {755str = ((URL)(doc.getPrintData())).toString();756}757} catch (IOException e) {758}759jobName = new JobName(str, null);760jobAttrSet.add(jobName);761}762}763764jobAttrSet = AttributeSetUtilities.unmodifiableView(jobAttrSet);765}766767private void getAttributeValues(DocFlavor flavor) throws PrintException {768Attribute attr;769Class<? extends Attribute> category;770771if (reqAttrSet.get(Fidelity.class) == Fidelity.FIDELITY_TRUE) {772fidelity = true;773} else {774fidelity = false;775}776777Attribute []attrs = reqAttrSet.toArray();778for (int i=0; i<attrs.length; i++) {779attr = attrs[i];780category = attr.getCategory();781if (fidelity == true) {782if (!service.isAttributeCategorySupported(category)) {783notifyEvent(PrintJobEvent.JOB_FAILED);784throw new PrintJobAttributeException(785"unsupported category: " + category, category, null);786} else if787(!service.isAttributeValueSupported(attr, flavor, null)) {788notifyEvent(PrintJobEvent.JOB_FAILED);789throw new PrintJobAttributeException(790"unsupported attribute: " + attr, null, attr);791}792}793if (category == Destination.class) {794URI uri = ((Destination)attr).getURI();795if (!"file".equals(uri.getScheme())) {796notifyEvent(PrintJobEvent.JOB_FAILED);797throw new PrintException("Not a file: URI");798} else {799try {800mDestType = DESTFILE;801mDestination = (new File(uri)).getPath();802} catch (Exception e) {803throw new PrintException(e);804}805// check write access806@SuppressWarnings("removal")807SecurityManager security = System.getSecurityManager();808if (security != null) {809try {810security.checkWrite(mDestination);811} catch (SecurityException se) {812notifyEvent(PrintJobEvent.JOB_FAILED);813throw new PrintException(se);814}815}816}817} else if (category == JobSheets.class) {818if ((JobSheets)attr == JobSheets.NONE) {819mNoJobSheet = true;820}821} else if (category == JobName.class) {822jobName = ((JobName)attr).getValue();823} else if (category == Copies.class) {824copies = ((Copies)attr).getValue();825} else if (category == Media.class) {826if (attr instanceof MediaSizeName) {827mediaName = (MediaSizeName)attr;828IPPPrintService.debug_println(debugPrefix+829"mediaName "+mediaName);830if (!service.isAttributeValueSupported(attr, null, null)) {831mediaSize = MediaSize.getMediaSizeForName(mediaName);832}833} else if (attr instanceof CustomMediaTray) {834customTray = (CustomMediaTray)attr;835}836} else if (category == OrientationRequested.class) {837orient = (OrientationRequested)attr;838} else if (category == NumberUp.class) {839nUp = (NumberUp)attr;840} else if (category == Sides.class) {841sides = (Sides)attr;842}843}844}845846private String[] printExecCmd(String printer, String options,847boolean noJobSheet,848String jobTitle, int copies, String spoolFile) {849int PRINTER = 0x1;850int OPTIONS = 0x2;851int JOBTITLE = 0x4;852int COPIES = 0x8;853int NOSHEET = 0x10;854int pFlags = 0;855String[] execCmd;856int ncomps = 2; // minimum number of print args857int n = 0;858859// conveniently "lp" is the default destination for both lp and lpr.860if (printer != null && !printer.isEmpty() && !printer.equals("lp")) {861pFlags |= PRINTER;862ncomps+=1;863}864if (options != null && !options.isEmpty()) {865pFlags |= OPTIONS;866ncomps+=1;867}868if (jobTitle != null && !jobTitle.isEmpty()) {869pFlags |= JOBTITLE;870ncomps+=1;871}872if (copies > 1) {873pFlags |= COPIES;874ncomps+=1;875}876if (noJobSheet) {877pFlags |= NOSHEET;878ncomps+=1;879} else if (getPrintService().880isAttributeCategorySupported(JobSheets.class)) {881ncomps+=1;882}883execCmd = new String[ncomps];884execCmd[n++] = "/usr/bin/lpr";885if ((pFlags & PRINTER) != 0) {886execCmd[n++] = "-P" + printer;887}888if ((pFlags & JOBTITLE) != 0) {889execCmd[n++] = "-J " + jobTitle;890}891if ((pFlags & COPIES) != 0) {892execCmd[n++] = "-#" + copies;893}894if ((pFlags & NOSHEET) != 0) {895execCmd[n++] = "-h";896} else if (getPrintService().897isAttributeCategorySupported(JobSheets.class)) {898execCmd[n++] = "-o job-sheets=standard";899}900if ((pFlags & OPTIONS) != 0) {901execCmd[n++] = "-o" + options;902}903execCmd[n++] = spoolFile;904if (IPPPrintService.debugPrint) {905System.out.println("UnixPrintJob>> execCmd");906for (int i=0; i<execCmd.length; i++) {907System.out.print(" "+execCmd[i]);908}909System.out.println();910}911return execCmd;912}913914private static int DESTPRINTER = 1;915private static int DESTFILE = 2;916private int mDestType = DESTPRINTER;917918private File spoolFile;919private String mDestination, mOptions="";920private boolean mNoJobSheet = false;921922// Inner class to run "privileged" to open the printer output stream.923924private class PrinterOpener implements java.security.PrivilegedAction<OutputStream> {925PrintException pex;926OutputStream result;927928public OutputStream run() {929try {930if (mDestType == UnixPrintJob.DESTFILE) {931spoolFile = new File(mDestination);932} else {933/* Write to a temporary file which will be spooled to934* the printer then deleted. In the case that the file935* is not removed for some reason, request that it is936* removed when the VM exits.937*/938spoolFile = Files.createTempFile("javaprint", "").toFile();939spoolFile.deleteOnExit();940}941result = new FileOutputStream(spoolFile);942return result;943} catch (IOException ex) {944// If there is an IOError we subvert it to a PrinterException.945notifyEvent(PrintJobEvent.JOB_FAILED);946pex = new PrintException(ex);947}948return null;949}950}951952// Inner class to run "privileged" to invoke the system print command953954private class PrinterSpooler implements java.security.PrivilegedAction<Object> {955PrintException pex;956957private void handleProcessFailure(final Process failedProcess,958final String[] execCmd, final int result) throws IOException {959try (StringWriter sw = new StringWriter();960PrintWriter pw = new PrintWriter(sw)) {961pw.append("error=").append(Integer.toString(result));962pw.append(" running:");963for (String arg: execCmd) {964pw.append(" '").append(arg).append("'");965}966try (InputStream is = failedProcess.getErrorStream();967InputStreamReader isr = new InputStreamReader(is);968BufferedReader br = new BufferedReader(isr)) {969while (br.ready()) {970pw.println();971pw.append("\t\t").append(br.readLine());972}973} finally {974pw.flush();975}976throw new IOException(sw.toString());977}978}979980public Object run() {981if (spoolFile == null || !spoolFile.exists()) {982pex = new PrintException("No spool file");983notifyEvent(PrintJobEvent.JOB_FAILED);984return null;985}986try {987/**988* Spool to the printer.989*/990String fileName = spoolFile.getAbsolutePath();991String[] execCmd = printExecCmd(mDestination, mOptions,992mNoJobSheet, jobName, copies, fileName);993994Process process = Runtime.getRuntime().exec(execCmd);995process.waitFor();996final int result = process.exitValue();997if (0 != result) {998handleProcessFailure(process, execCmd, result);999}1000notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);1001} catch (IOException ex) {1002notifyEvent(PrintJobEvent.JOB_FAILED);1003// REMIND : 2d printing throws PrinterException1004pex = new PrintException(ex);1005} catch (InterruptedException ie) {1006notifyEvent(PrintJobEvent.JOB_FAILED);1007pex = new PrintException(ie);1008} finally {1009spoolFile.delete();1010notifyEvent(PrintJobEvent.NO_MORE_EVENTS);1011}1012return null;1013}1014}10151016public void cancel() throws PrintException {1017synchronized (this) {1018if (!printing) {1019throw new PrintException("Job is not yet submitted.");1020} else if (job != null && !printReturned) {1021job.cancel();1022notifyEvent(PrintJobEvent.JOB_CANCELED);1023return;1024} else {1025throw new PrintException("Job could not be cancelled.");1026}1027}1028}1029}103010311032