Path: blob/master/src/java.desktop/windows/classes/sun/print/Win32PrintJob.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.io.BufferedInputStream;29import java.io.File;30import java.io.InputStream;31import java.io.IOException;32import java.io.Reader;33import java.net.URL;34import java.nio.file.Files;35import java.nio.file.Path;36import java.nio.file.StandardCopyOption;37import java.util.Vector;3839import javax.print.CancelablePrintJob;40import javax.print.Doc;41import javax.print.DocFlavor;42import javax.print.PrintService;43import javax.print.PrintException;44import javax.print.event.PrintJobEvent;45import javax.print.event.PrintJobListener;46import javax.print.event.PrintJobAttributeListener;4748import javax.print.attribute.Attribute;49import javax.print.attribute.AttributeSetUtilities;50import javax.print.attribute.DocAttributeSet;51import javax.print.attribute.HashPrintJobAttributeSet;52import javax.print.attribute.HashPrintRequestAttributeSet;53import javax.print.attribute.PrintJobAttribute;54import javax.print.attribute.PrintJobAttributeSet;55import javax.print.attribute.PrintRequestAttribute;56import javax.print.attribute.PrintRequestAttributeSet;57import javax.print.attribute.standard.Copies;58import javax.print.attribute.standard.DocumentName;59import javax.print.attribute.standard.Fidelity;60import javax.print.attribute.standard.JobName;61import javax.print.attribute.standard.JobOriginatingUserName;62import javax.print.attribute.standard.Media;63import javax.print.attribute.standard.MediaSize;64import javax.print.attribute.standard.MediaSizeName;65import javax.print.attribute.standard.OrientationRequested;66import javax.print.attribute.standard.RequestingUserName;67import javax.print.attribute.standard.Destination;68import javax.print.attribute.standard.PrinterIsAcceptingJobs;69import javax.print.attribute.standard.PrinterState;70import javax.print.attribute.standard.PrinterStateReason;71import javax.print.attribute.standard.PrinterStateReasons;7273import java.awt.print.*;7475public class Win32PrintJob implements CancelablePrintJob {7677private transient Vector<PrintJobListener> jobListeners;78private transient Vector<PrintJobAttributeListener> attrListeners;79private transient Vector<PrintJobAttributeSet> listenedAttributeSets;8081private Win32PrintService service;82private boolean fidelity;83private boolean printing = false;84private boolean printReturned = false;85private PrintRequestAttributeSet reqAttrSet = null;86private PrintJobAttributeSet jobAttrSet = null;87private PrinterJob job;88private Doc doc;89private String mDestination = null;9091/* these variables used globally to store reference to the print92* data retrieved as a stream. On completion these are always closed93* if non-null.94*/95private InputStream instream = null;96private Reader reader = null;9798/* default values overridden by those extracted from the attributes */99private String jobName = "Java Printing";100private int copies = 0;101private MediaSizeName mediaName = null;102private MediaSize mediaSize = null;103private OrientationRequested orient = null;104105/* print job handle used by native code */106private long hPrintJob;107108/* buffer length for printing raw data */109private static final int PRINTBUFFERLEN = 8192;110111Win32PrintJob(Win32PrintService service) {112this.service = service;113}114115public PrintService getPrintService() {116return service;117}118119public PrintJobAttributeSet getAttributes() {120synchronized (this) {121if (jobAttrSet == null) {122/* just return an empty set until the job is submitted */123PrintJobAttributeSet jobSet = new HashPrintJobAttributeSet();124return AttributeSetUtilities.unmodifiableView(jobSet);125} else {126return jobAttrSet;127}128}129}130131public void addPrintJobListener(PrintJobListener listener) {132synchronized (this) {133if (listener == null) {134return;135}136if (jobListeners == null) {137jobListeners = new Vector<>();138}139jobListeners.add(listener);140}141}142143public void removePrintJobListener(PrintJobListener listener) {144synchronized (this) {145if (listener == null || jobListeners == null ) {146return;147}148jobListeners.remove(listener);149if (jobListeners.isEmpty()) {150jobListeners = null;151}152}153}154155156/* Closes any stream already retrieved for the data.157* We want to avoid unnecessarily asking the Doc to create a stream only158* to get a reference in order to close it because the job failed.159* If the representation class is itself a "stream", this160* closes that stream too.161*/162private void closeDataStreams() {163164if (doc == null) {165return;166}167168Object data = null;169170try {171data = doc.getPrintData();172} catch (IOException e) {173return;174}175176if (instream != null) {177try {178instream.close();179} catch (IOException e) {180} finally {181instream = null;182}183}184else if (reader != null) {185try {186reader.close();187} catch (IOException e) {188} finally {189reader = null;190}191}192else if (data instanceof InputStream) {193try {194((InputStream)data).close();195} catch (IOException e) {196}197}198else if (data instanceof Reader) {199try {200((Reader)data).close();201} catch (IOException e) {202}203}204}205206private void notifyEvent(int reason) {207208/* since this method should always get called, here's where209* we will perform the clean up of any data stream supplied.210*/211switch (reason) {212case PrintJobEvent.DATA_TRANSFER_COMPLETE:213case PrintJobEvent.JOB_CANCELED :214case PrintJobEvent.JOB_FAILED :215case PrintJobEvent.NO_MORE_EVENTS :216case PrintJobEvent.JOB_COMPLETE :217closeDataStreams();218}219220synchronized (this) {221if (jobListeners != null) {222PrintJobListener listener;223PrintJobEvent event = new PrintJobEvent(this, reason);224for (int i = 0; i < jobListeners.size(); i++) {225listener = jobListeners.elementAt(i);226switch (reason) {227228case PrintJobEvent.JOB_COMPLETE :229listener.printJobCompleted(event);230break;231232case PrintJobEvent.JOB_CANCELED :233listener.printJobCanceled(event);234break;235236case PrintJobEvent.JOB_FAILED :237listener.printJobFailed(event);238break;239240case PrintJobEvent.DATA_TRANSFER_COMPLETE :241listener.printDataTransferCompleted(event);242break;243244case PrintJobEvent.NO_MORE_EVENTS :245listener.printJobNoMoreEvents(event);246break;247248default:249break;250}251}252}253}254}255256public void addPrintJobAttributeListener(257PrintJobAttributeListener listener,258PrintJobAttributeSet attributes) {259synchronized (this) {260if (listener == null) {261return;262}263if (attrListeners == null) {264attrListeners = new Vector<>();265listenedAttributeSets = new Vector<>();266}267attrListeners.add(listener);268if (attributes == null) {269attributes = new HashPrintJobAttributeSet();270}271listenedAttributeSets.add(attributes);272}273}274275public void removePrintJobAttributeListener(276PrintJobAttributeListener listener) {277synchronized (this) {278if (listener == null || attrListeners == null ) {279return;280}281int index = attrListeners.indexOf(listener);282if (index == -1) {283return;284} else {285attrListeners.remove(index);286listenedAttributeSets.remove(index);287if (attrListeners.isEmpty()) {288attrListeners = null;289listenedAttributeSets = null;290}291}292}293}294295public void print(Doc doc, PrintRequestAttributeSet attributes)296throws PrintException {297298synchronized (this) {299if (printing) {300throw new PrintException("already printing");301} else {302printing = true;303}304}305306PrinterState prnState = service.getAttribute(PrinterState.class);307if (prnState == PrinterState.STOPPED) {308PrinterStateReasons prnStateReasons =309service.getAttribute(PrinterStateReasons.class);310if ((prnStateReasons != null) &&311(prnStateReasons.containsKey(PrinterStateReason.SHUTDOWN)))312{313throw new PrintException("PrintService is no longer available.");314}315}316317if (service.getAttribute(PrinterIsAcceptingJobs.class) ==318PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS) {319throw new PrintException("Printer is not accepting job.");320}321322323this.doc = doc;324/* check if the parameters are valid before doing much processing */325DocFlavor flavor = doc.getDocFlavor();326Object data;327328try {329data = doc.getPrintData();330} catch (IOException e) {331notifyEvent(PrintJobEvent.JOB_FAILED);332throw new PrintException("can't get print data: " + e.toString());333}334335if (data == null) {336throw new PrintException("Null print data.");337}338339if (flavor == null || (!service.isDocFlavorSupported(flavor))) {340notifyEvent(PrintJobEvent.JOB_FAILED);341throw new PrintJobFlavorException("invalid flavor", flavor);342}343344initializeAttributeSets(doc, attributes);345346getAttributeValues(flavor);347348String repClassName = flavor.getRepresentationClassName();349350if (flavor.equals(DocFlavor.INPUT_STREAM.GIF) ||351flavor.equals(DocFlavor.INPUT_STREAM.JPEG) ||352flavor.equals(DocFlavor.INPUT_STREAM.PNG) ||353flavor.equals(DocFlavor.BYTE_ARRAY.GIF) ||354flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) ||355flavor.equals(DocFlavor.BYTE_ARRAY.PNG)) {356try {357instream = doc.getStreamForBytes();358if (instream == null) {359notifyEvent(PrintJobEvent.JOB_FAILED);360throw new PrintException("No stream for data");361}362printableJob(new ImagePrinter(instream));363service.wakeNotifier();364return;365} catch (ClassCastException cce) {366notifyEvent(PrintJobEvent.JOB_FAILED);367throw new PrintException(cce);368} catch (IOException ioe) {369notifyEvent(PrintJobEvent.JOB_FAILED);370throw new PrintException(ioe);371}372} else if (flavor.equals(DocFlavor.URL.GIF) ||373flavor.equals(DocFlavor.URL.JPEG) ||374flavor.equals(DocFlavor.URL.PNG)) {375try {376printableJob(new ImagePrinter((URL)data));377service.wakeNotifier();378return;379} catch (ClassCastException cce) {380notifyEvent(PrintJobEvent.JOB_FAILED);381throw new PrintException(cce);382}383} else if (repClassName.equals("java.awt.print.Pageable")) {384try {385pageableJob((Pageable)doc.getPrintData());386service.wakeNotifier();387return;388} catch (ClassCastException cce) {389notifyEvent(PrintJobEvent.JOB_FAILED);390throw new PrintException(cce);391} catch (IOException ioe) {392notifyEvent(PrintJobEvent.JOB_FAILED);393throw new PrintException(ioe);394}395} else if (repClassName.equals("java.awt.print.Printable")) {396try {397printableJob((Printable)doc.getPrintData());398service.wakeNotifier();399return;400} catch (ClassCastException cce) {401notifyEvent(PrintJobEvent.JOB_FAILED);402throw new PrintException(cce);403} catch (IOException ioe) {404notifyEvent(PrintJobEvent.JOB_FAILED);405throw new PrintException(ioe);406}407} else if (repClassName.equals("[B") ||408repClassName.equals("java.io.InputStream") ||409repClassName.equals("java.net.URL")) {410411if (repClassName.equals("java.net.URL")) {412URL url = (URL)data;413try {414instream = url.openStream();415} catch (IOException e) {416notifyEvent(PrintJobEvent.JOB_FAILED);417throw new PrintException(e.toString());418}419} else {420try {421instream = doc.getStreamForBytes();422} catch (IOException ioe) {423notifyEvent(PrintJobEvent.JOB_FAILED);424throw new PrintException(ioe.toString());425}426}427428if (instream == null) {429notifyEvent(PrintJobEvent.JOB_FAILED);430throw new PrintException("No stream for data");431}432433if (mDestination != null) { // if destination attribute is set434try {435Files.copy(instream, Path.of(mDestination), StandardCopyOption.REPLACE_EXISTING);436} catch (IOException ioe) {437notifyEvent(PrintJobEvent.JOB_FAILED);438throw new PrintException(ioe.toString());439}440notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);441notifyEvent(PrintJobEvent.JOB_COMPLETE);442service.wakeNotifier();443return;444}445446if (!startPrintRawData(service.getName(), jobName)) {447notifyEvent(PrintJobEvent.JOB_FAILED);448throw new PrintException("Print job failed to start.");449}450BufferedInputStream bin = new BufferedInputStream(instream);451int bread = 0;452try {453byte[] buffer = new byte[PRINTBUFFERLEN];454455while ((bread = bin.read(buffer, 0, PRINTBUFFERLEN)) >=0) {456if (!printRawData(buffer, bread)) {457bin.close();458notifyEvent(PrintJobEvent.JOB_FAILED);459throw new PrintException ("Problem while spooling data");460}461}462bin.close();463if (!endPrintRawData()) {464notifyEvent(PrintJobEvent.JOB_FAILED);465throw new PrintException("Print job failed to close properly.");466}467notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);468} catch (IOException e) {469notifyEvent(PrintJobEvent.JOB_FAILED);470throw new PrintException (e.toString());471} finally {472notifyEvent(PrintJobEvent.NO_MORE_EVENTS);473}474} else {475notifyEvent(PrintJobEvent.JOB_FAILED);476throw new PrintException("unrecognized class: "+repClassName);477}478service.wakeNotifier();479}480481public void printableJob(Printable printable) throws PrintException {482try {483synchronized(this) {484if (job != null) { // shouldn't happen485throw new PrintException("already printing");486} else {487job = new sun.awt.windows.WPrinterJob();488}489}490PrintService svc = getPrintService();491job.setPrintService(svc);492if (copies == 0) {493Copies c = (Copies)svc.getDefaultAttributeValue(Copies.class);494copies = c.getValue();495}496497if (mediaName == null) {498Object media = svc.getDefaultAttributeValue(Media.class);499if (media instanceof MediaSizeName) {500mediaName = (MediaSizeName) media;501mediaSize = MediaSize.getMediaSizeForName(mediaName);502}503}504505if (orient == null) {506orient =507(OrientationRequested)svc.getDefaultAttributeValue(OrientationRequested.class);508}509510job.setCopies(copies);511job.setJobName(jobName);512PageFormat pf = new PageFormat();513if (mediaSize != null) {514Paper p = new Paper();515p.setSize(mediaSize.getX(MediaSize.INCH)*72.0,516mediaSize.getY(MediaSize.INCH)*72.0);517p.setImageableArea(72.0, 72.0, p.getWidth()-144.0,518p.getHeight()-144.0);519pf.setPaper(p);520}521if (orient == OrientationRequested.REVERSE_LANDSCAPE) {522pf.setOrientation(PageFormat.REVERSE_LANDSCAPE);523} else if (orient == OrientationRequested.LANDSCAPE) {524pf.setOrientation(PageFormat.LANDSCAPE);525}526job.setPrintable(printable, pf);527job.print(reqAttrSet);528notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);529return;530} catch (PrinterException pe) {531notifyEvent(PrintJobEvent.JOB_FAILED);532throw new PrintException(pe);533} finally {534printReturned = true;535notifyEvent(PrintJobEvent.NO_MORE_EVENTS);536}537}538539public void pageableJob(Pageable pageable) throws PrintException {540try {541synchronized(this) {542if (job != null) { // shouldn't happen543throw new PrintException("already printing");544} else {545job = new sun.awt.windows.WPrinterJob();546}547}548PrintService svc = getPrintService();549job.setPrintService(svc);550if (copies == 0) {551Copies c = (Copies)svc.getDefaultAttributeValue(Copies.class);552copies = c.getValue();553}554job.setCopies(copies);555job.setJobName(jobName);556job.setPageable(pageable);557job.print(reqAttrSet);558notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);559return;560} catch (PrinterException pe) {561notifyEvent(PrintJobEvent.JOB_FAILED);562throw new PrintException(pe);563} finally {564printReturned = true;565notifyEvent(PrintJobEvent.NO_MORE_EVENTS);566}567}568569/* There's some inefficiency here as the job set is created even though570* it may never be requested.571*/572private synchronized void573initializeAttributeSets(Doc doc, PrintRequestAttributeSet reqSet) {574575reqAttrSet = new HashPrintRequestAttributeSet();576jobAttrSet = new HashPrintJobAttributeSet();577578Attribute[] attrs;579if (reqSet != null) {580reqAttrSet.addAll(reqSet);581attrs = reqSet.toArray();582for (int i=0; i<attrs.length; i++) {583if (attrs[i] instanceof PrintJobAttribute) {584jobAttrSet.add(attrs[i]);585}586}587}588589DocAttributeSet docSet = doc.getAttributes();590if (docSet != null) {591attrs = docSet.toArray();592for (int i=0; i<attrs.length; i++) {593if (attrs[i] instanceof PrintRequestAttribute) {594reqAttrSet.add(attrs[i]);595}596if (attrs[i] instanceof PrintJobAttribute) {597jobAttrSet.add(attrs[i]);598}599}600}601602/* add the user name to the job */603String userName = "";604try {605userName = System.getProperty("user.name");606} catch (SecurityException se) {607}608609if (userName == null || userName.isEmpty()) {610RequestingUserName ruName =611(RequestingUserName)reqSet.get(RequestingUserName.class);612if (ruName != null) {613jobAttrSet.add(614new JobOriginatingUserName(ruName.getValue(),615ruName.getLocale()));616} else {617jobAttrSet.add(new JobOriginatingUserName("", null));618}619} else {620jobAttrSet.add(new JobOriginatingUserName(userName, null));621}622623/* if no job name supplied use doc name (if supplied), if none and624* its a URL use that, else finally anything .. */625if (jobAttrSet.get(JobName.class) == null) {626JobName jobName;627if (docSet != null && docSet.get(DocumentName.class) != null) {628DocumentName docName =629(DocumentName)docSet.get(DocumentName.class);630jobName = new JobName(docName.getValue(), docName.getLocale());631jobAttrSet.add(jobName);632} else {633String str = "JPS Job:" + doc;634try {635Object printData = doc.getPrintData();636if (printData instanceof URL) {637str = ((URL)(doc.getPrintData())).toString();638}639} catch (IOException e) {640}641jobName = new JobName(str, null);642jobAttrSet.add(jobName);643}644}645646jobAttrSet = AttributeSetUtilities.unmodifiableView(jobAttrSet);647}648649private void getAttributeValues(DocFlavor flavor) throws PrintException {650651if (reqAttrSet.get(Fidelity.class) == Fidelity.FIDELITY_TRUE) {652fidelity = true;653} else {654fidelity = false;655}656657Class<? extends Attribute> category;658Attribute [] attrs = reqAttrSet.toArray();659for (int i=0; i<attrs.length; i++) {660Attribute attr = attrs[i];661category = attr.getCategory();662if (fidelity == true) {663if (!service.isAttributeCategorySupported(category)) {664notifyEvent(PrintJobEvent.JOB_FAILED);665throw new PrintJobAttributeException(666"unsupported category: " + category, category, null);667} else if668(!service.isAttributeValueSupported(attr, flavor, null)) {669notifyEvent(PrintJobEvent.JOB_FAILED);670throw new PrintJobAttributeException(671"unsupported attribute: " + attr, null, attr);672}673}674if (category == Destination.class) {675URI uri = ((Destination)attr).getURI();676if (!"file".equals(uri.getScheme())) {677notifyEvent(PrintJobEvent.JOB_FAILED);678throw new PrintException("Not a file: URI");679} else {680try {681mDestination = (new File(uri)).getPath();682} catch (Exception e) {683throw new PrintException(e);684}685// check write access686@SuppressWarnings("removal")687SecurityManager security = System.getSecurityManager();688if (security != null) {689try {690security.checkWrite(mDestination);691} catch (SecurityException se) {692notifyEvent(PrintJobEvent.JOB_FAILED);693throw new PrintException(se);694}695}696}697} else if (category == JobName.class) {698jobName = ((JobName)attr).getValue();699} else if (category == Copies.class) {700copies = ((Copies)attr).getValue();701} else if (category == Media.class) {702if (attr instanceof MediaSizeName) {703mediaName = (MediaSizeName)attr;704// If requested MediaSizeName is not supported,705// get the corresponding media size - this will706// be used to create a new PageFormat.707if (!service.isAttributeValueSupported(attr, null, null)) {708mediaSize = MediaSize.getMediaSizeForName(mediaName);709}710}711} else if (category == OrientationRequested.class) {712orient = (OrientationRequested)attr;713}714}715}716717private native boolean startPrintRawData(String printerName,718String jobName);719private native boolean printRawData(byte[] data, int count);720private native boolean endPrintRawData();721722/* Cancel PrinterJob jobs that haven't yet completed. */723public void cancel() throws PrintException {724synchronized (this) {725if (!printing) {726throw new PrintException("Job is not yet submitted.");727} else if (job != null && !printReturned) {728job.cancel();729notifyEvent(PrintJobEvent.JOB_CANCELED);730return;731} else {732throw new PrintException("Job could not be cancelled.");733}734}735}736}737738739