Path: blob/master/src/java.desktop/share/classes/sun/print/PrintJob2D.java
41153 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.awt.Dimension;28import java.awt.Frame;29import java.awt.Graphics;30import java.awt.Graphics2D;31import java.awt.PrintJob;32import java.awt.JobAttributes;33import java.awt.JobAttributes.*;34import java.awt.PageAttributes;35import java.awt.PageAttributes.*;3637import java.awt.print.PageFormat;38import java.awt.print.Paper;39import java.awt.print.Printable;40import java.awt.print.PrinterException;41import java.awt.print.PrinterJob;4243import java.io.File;44import java.io.FilePermission;45import java.io.IOException;4647import java.net.URI;48import java.net.URISyntaxException;4950import java.util.ArrayList;51import java.util.Properties;5253import javax.print.PrintService;54import javax.print.attribute.HashPrintRequestAttributeSet;55import javax.print.attribute.PrintRequestAttributeSet;56import javax.print.attribute.ResolutionSyntax;57import javax.print.attribute.Size2DSyntax;58import javax.print.attribute.standard.Chromaticity;59import javax.print.attribute.standard.Copies;60import javax.print.attribute.standard.Destination;61import javax.print.attribute.standard.DialogTypeSelection;62import javax.print.attribute.standard.DialogOwner;63import javax.print.attribute.standard.JobName;64import javax.print.attribute.standard.MediaSize;65import javax.print.attribute.standard.PrintQuality;66import javax.print.attribute.standard.PrinterResolution;67import javax.print.attribute.standard.SheetCollate;68import javax.print.attribute.standard.Sides;69import javax.print.attribute.standard.Media;70import javax.print.attribute.standard.OrientationRequested;71import javax.print.attribute.standard.MediaSizeName;72import javax.print.attribute.standard.PageRanges;7374import sun.print.SunPageSelection;75import sun.print.SunMinMaxPage;7677/**78* A class which initiates and executes a print job using79* the underlying PrinterJob graphics conversions.80*81* @see java.awt.Toolkit#getPrintJob82*83*/84public class PrintJob2D extends PrintJob implements Printable, Runnable {8586private static final MediaType[] SIZES = {87MediaType.ISO_4A0, MediaType.ISO_2A0, MediaType.ISO_A0,88MediaType.ISO_A1, MediaType.ISO_A2, MediaType.ISO_A3,89MediaType.ISO_A4, MediaType.ISO_A5, MediaType.ISO_A6,90MediaType.ISO_A7, MediaType.ISO_A8, MediaType.ISO_A9,91MediaType.ISO_A10, MediaType.ISO_B0, MediaType.ISO_B1,92MediaType.ISO_B2, MediaType.ISO_B3, MediaType.ISO_B4,93MediaType.ISO_B5, MediaType.ISO_B6, MediaType.ISO_B7,94MediaType.ISO_B8, MediaType.ISO_B9, MediaType.ISO_B10,95MediaType.JIS_B0, MediaType.JIS_B1, MediaType.JIS_B2,96MediaType.JIS_B3, MediaType.JIS_B4, MediaType.JIS_B5,97MediaType.JIS_B6, MediaType.JIS_B7, MediaType.JIS_B8,98MediaType.JIS_B9, MediaType.JIS_B10, MediaType.ISO_C0,99MediaType.ISO_C1, MediaType.ISO_C2, MediaType.ISO_C3,100MediaType.ISO_C4, MediaType.ISO_C5, MediaType.ISO_C6,101MediaType.ISO_C7, MediaType.ISO_C8, MediaType.ISO_C9,102MediaType.ISO_C10, MediaType.ISO_DESIGNATED_LONG,103MediaType.EXECUTIVE, MediaType.FOLIO, MediaType.INVOICE,104MediaType.LEDGER, MediaType.NA_LETTER, MediaType.NA_LEGAL,105MediaType.QUARTO, MediaType.A, MediaType.B,106MediaType.C, MediaType.D, MediaType.E,107MediaType.NA_10X15_ENVELOPE, MediaType.NA_10X14_ENVELOPE,108MediaType.NA_10X13_ENVELOPE, MediaType.NA_9X12_ENVELOPE,109MediaType.NA_9X11_ENVELOPE, MediaType.NA_7X9_ENVELOPE,110MediaType.NA_6X9_ENVELOPE, MediaType.NA_NUMBER_9_ENVELOPE,111MediaType.NA_NUMBER_10_ENVELOPE, MediaType.NA_NUMBER_11_ENVELOPE,112MediaType.NA_NUMBER_12_ENVELOPE, MediaType.NA_NUMBER_14_ENVELOPE,113MediaType.INVITE_ENVELOPE, MediaType.ITALY_ENVELOPE,114MediaType.MONARCH_ENVELOPE, MediaType.PERSONAL_ENVELOPE115};116117/* This array maps the above array to the objects used by the118* javax.print APIs119*/120private static final MediaSizeName[] JAVAXSIZES = {121null, null, MediaSizeName.ISO_A0,122MediaSizeName.ISO_A1, MediaSizeName.ISO_A2, MediaSizeName.ISO_A3,123MediaSizeName.ISO_A4, MediaSizeName.ISO_A5, MediaSizeName.ISO_A6,124MediaSizeName.ISO_A7, MediaSizeName.ISO_A8, MediaSizeName.ISO_A9,125MediaSizeName.ISO_A10, MediaSizeName.ISO_B0, MediaSizeName.ISO_B1,126MediaSizeName.ISO_B2, MediaSizeName.ISO_B3, MediaSizeName.ISO_B4,127MediaSizeName.ISO_B5, MediaSizeName.ISO_B6, MediaSizeName.ISO_B7,128MediaSizeName.ISO_B8, MediaSizeName.ISO_B9, MediaSizeName.ISO_B10,129MediaSizeName.JIS_B0, MediaSizeName.JIS_B1, MediaSizeName.JIS_B2,130MediaSizeName.JIS_B3, MediaSizeName.JIS_B4, MediaSizeName.JIS_B5,131MediaSizeName.JIS_B6, MediaSizeName.JIS_B7, MediaSizeName.JIS_B8,132MediaSizeName.JIS_B9, MediaSizeName.JIS_B10, MediaSizeName.ISO_C0,133MediaSizeName.ISO_C1, MediaSizeName.ISO_C2, MediaSizeName.ISO_C3,134MediaSizeName.ISO_C4, MediaSizeName.ISO_C5, MediaSizeName.ISO_C6,135null, null, null, null,136MediaSizeName.ISO_DESIGNATED_LONG, MediaSizeName.EXECUTIVE,137MediaSizeName.FOLIO, MediaSizeName.INVOICE, MediaSizeName.LEDGER,138MediaSizeName.NA_LETTER, MediaSizeName.NA_LEGAL,139MediaSizeName.QUARTO, MediaSizeName.A, MediaSizeName.B,140MediaSizeName.C, MediaSizeName.D, MediaSizeName.E,141MediaSizeName.NA_10X15_ENVELOPE, MediaSizeName.NA_10X14_ENVELOPE,142MediaSizeName.NA_10X13_ENVELOPE, MediaSizeName.NA_9X12_ENVELOPE,143MediaSizeName.NA_9X11_ENVELOPE, MediaSizeName.NA_7X9_ENVELOPE,144MediaSizeName.NA_6X9_ENVELOPE,145MediaSizeName.NA_NUMBER_9_ENVELOPE,146MediaSizeName.NA_NUMBER_10_ENVELOPE,147MediaSizeName.NA_NUMBER_11_ENVELOPE,148MediaSizeName.NA_NUMBER_12_ENVELOPE,149MediaSizeName.NA_NUMBER_14_ENVELOPE,150null, MediaSizeName.ITALY_ENVELOPE,151MediaSizeName.MONARCH_ENVELOPE, MediaSizeName.PERSONAL_ENVELOPE,152};153154155// widths and lengths in PostScript points (1/72 in.)156private static final int[] WIDTHS = {157/*iso-4a0*/ 4768, /*iso-2a0*/ 3370, /*iso-a0*/ 2384, /*iso-a1*/ 1684,158/*iso-a2*/ 1191, /*iso-a3*/ 842, /*iso-a4*/ 595, /*iso-a5*/ 420,159/*iso-a6*/ 298, /*iso-a7*/ 210, /*iso-a8*/ 147, /*iso-a9*/ 105,160/*iso-a10*/ 74, /*iso-b0*/ 2835, /*iso-b1*/ 2004, /*iso-b2*/ 1417,161/*iso-b3*/ 1001, /*iso-b4*/ 709, /*iso-b5*/ 499, /*iso-b6*/ 354,162/*iso-b7*/ 249, /*iso-b8*/ 176, /*iso-b9*/ 125, /*iso-b10*/ 88,163/*jis-b0*/ 2920, /*jis-b1*/ 2064, /*jis-b2*/ 1460, /*jis-b3*/ 1032,164/*jis-b4*/ 729, /*jis-b5*/ 516, /*jis-b6*/ 363, /*jis-b7*/ 258,165/*jis-b8*/ 181, /*jis-b9*/ 128, /*jis-b10*/ 91, /*iso-c0*/ 2599,166/*iso-c1*/ 1837, /*iso-c2*/ 1298, /*iso-c3*/ 918, /*iso-c4*/ 649,167/*iso-c5*/ 459, /*iso-c6*/ 323, /*iso-c7*/ 230, /*iso-c8*/ 162,168/*iso-c9*/ 113, /*iso-c10*/ 79, /*iso-designated-long*/ 312,169/*executive*/ 522, /*folio*/ 612, /*invoice*/ 396, /*ledger*/ 792,170/*na-letter*/ 612, /*na-legal*/ 612, /*quarto*/ 609, /*a*/ 612,171/*b*/ 792, /*c*/ 1224, /*d*/ 1584, /*e*/ 2448,172/*na-10x15-envelope*/ 720, /*na-10x14-envelope*/ 720,173/*na-10x13-envelope*/ 720, /*na-9x12-envelope*/ 648,174/*na-9x11-envelope*/ 648, /*na-7x9-envelope*/ 504,175/*na-6x9-envelope*/ 432, /*na-number-9-envelope*/ 279,176/*na-number-10-envelope*/ 297, /*na-number-11-envelope*/ 324,177/*na-number-12-envelope*/ 342, /*na-number-14-envelope*/ 360,178/*invite-envelope*/ 624, /*italy-envelope*/ 312,179/*monarch-envelope*/ 279, /*personal-envelope*/ 261180};181private static final int[] LENGTHS = {182/*iso-4a0*/ 6741, /*iso-2a0*/ 4768, /*iso-a0*/ 3370, /*iso-a1*/ 2384,183/*iso-a2*/ 1684, /*iso-a3*/ 1191, /*iso-a4*/ 842, /*iso-a5*/ 595,184/*iso-a6*/ 420, /*iso-a7*/ 298, /*iso-a8*/ 210, /*iso-a9*/ 147,185/*iso-a10*/ 105, /*iso-b0*/ 4008, /*iso-b1*/ 2835, /*iso-b2*/ 2004,186/*iso-b3*/ 1417, /*iso-b4*/ 1001, /*iso-b5*/ 729, /*iso-b6*/ 499,187/*iso-b7*/ 354, /*iso-b8*/ 249, /*iso-b9*/ 176, /*iso-b10*/ 125,188/*jis-b0*/ 4127, /*jis-b1*/ 2920, /*jis-b2*/ 2064, /*jis-b3*/ 1460,189/*jis-b4*/ 1032, /*jis-b5*/ 729, /*jis-b6*/ 516, /*jis-b7*/ 363,190/*jis-b8*/ 258, /*jis-b9*/ 181, /*jis-b10*/ 128, /*iso-c0*/ 3677,191/*iso-c1*/ 2599, /*iso-c2*/ 1837, /*iso-c3*/ 1298, /*iso-c4*/ 918,192/*iso-c5*/ 649, /*iso-c6*/ 459, /*iso-c7*/ 323, /*iso-c8*/ 230,193/*iso-c9*/ 162, /*iso-c10*/ 113, /*iso-designated-long*/ 624,194/*executive*/ 756, /*folio*/ 936, /*invoice*/ 612, /*ledger*/ 1224,195/*na-letter*/ 792, /*na-legal*/ 1008, /*quarto*/ 780, /*a*/ 792,196/*b*/ 1224, /*c*/ 1584, /*d*/ 2448, /*e*/ 3168,197/*na-10x15-envelope*/ 1080, /*na-10x14-envelope*/ 1008,198/*na-10x13-envelope*/ 936, /*na-9x12-envelope*/ 864,199/*na-9x11-envelope*/ 792, /*na-7x9-envelope*/ 648,200/*na-6x9-envelope*/ 648, /*na-number-9-envelope*/ 639,201/*na-number-10-envelope*/ 684, /*na-number-11-envelope*/ 747,202/*na-number-12-envelope*/ 792, /*na-number-14-envelope*/ 828,203/*invite-envelope*/ 624, /*italy-envelope*/ 652,204/*monarch-envelope*/ 540, /*personal-envelope*/ 468205};206207208private Frame frame;209private String docTitle = "";210private JobAttributes jobAttributes;211private PageAttributes pageAttributes;212private PrintRequestAttributeSet attributes;213214/*215* Displays the native or cross-platform dialog and allows the216* user to update job & page attributes217*/218219/**220* The PrinterJob being uses to implement the PrintJob.221*/222private PrinterJob printerJob;223224/**225* The size of the page being used for the PrintJob.226*/227private PageFormat pageFormat;228229/**230* The PrinterJob and the application run on different231* threads and communicate through a pair of message232* queues. This queue is the list of Graphics that233* the PrinterJob has requested rendering for, but234* for which the application has not yet called getGraphics().235* In practice the length of this message queue is always236* 0 or 1.237*/238private MessageQ graphicsToBeDrawn = new MessageQ("tobedrawn");239240/**241* Used to communicate between the application's thread242* and the PrinterJob's thread this message queue holds243* the list of Graphics into which the application has244* finished drawing, but that have not yet been returned245* to the PrinterJob thread. Again, in practice, the246* length of this message queue is always 0 or 1.247*/248private MessageQ graphicsDrawn = new MessageQ("drawn");249250/**251* The last Graphics returned to the application via252* getGraphics. This is the Graphics into which the253* application is currently drawing.254*/255private Graphics2D currentGraphics;256257/**258* The zero based index of the page currently being rendered259* by the application.260*/261private int pageIndex = -1;262263// The following Strings are maintained for backward-compatibility with264// Properties based print control.265private static final String DEST_PROP = "awt.print.destination";266private static final String PRINTER = "printer";267private static final String FILE = "file";268269private static final String PRINTER_PROP = "awt.print.printer";270271private static final String FILENAME_PROP = "awt.print.fileName";272273private static final String NUMCOPIES_PROP = "awt.print.numCopies";274275private static final String OPTIONS_PROP = "awt.print.options";276277private static final String ORIENT_PROP = "awt.print.orientation";278private static final String PORTRAIT = "portrait";279private static final String LANDSCAPE = "landscape";280281private static final String PAPERSIZE_PROP = "awt.print.paperSize";282private static final String LETTER = "letter";283private static final String LEGAL = "legal";284private static final String EXECUTIVE = "executive";285private static final String A4 = "a4";286287private Properties props;288289private String options = ""; // REMIND: needs implementation290291/**292* The thread on which PrinterJob is running.293* This is different than the applications thread.294*/295private Thread printerJobThread;296297public PrintJob2D(Frame frame, String doctitle,298final Properties props) {299this.props = props;300this.jobAttributes = new JobAttributes();301this.pageAttributes = new PageAttributes();302translateInputProps();303initPrintJob2D(frame, doctitle,304this.jobAttributes, this.pageAttributes);305}306307public PrintJob2D(Frame frame, String doctitle,308JobAttributes jobAttributes,309PageAttributes pageAttributes) {310initPrintJob2D(frame, doctitle, jobAttributes, pageAttributes);311}312313private void initPrintJob2D(Frame frame, String doctitle,314JobAttributes jobAttributes,315PageAttributes pageAttributes) {316317@SuppressWarnings("removal")318SecurityManager security = System.getSecurityManager();319if (security != null) {320security.checkPrintJobAccess();321}322323if (frame == null &&324(jobAttributes == null ||325jobAttributes.getDialog() == DialogType.NATIVE)) {326throw new NullPointerException("Frame must not be null");327}328this.frame = frame;329330this.docTitle = (doctitle == null) ? "" : doctitle;331this.jobAttributes = (jobAttributes != null)332? jobAttributes : new JobAttributes();333this.pageAttributes = (pageAttributes != null)334? pageAttributes : new PageAttributes();335336// Currently, we always reduce page ranges to xxx or xxx-xxx337int[][] pageRanges = this.jobAttributes.getPageRanges();338int first = pageRanges[0][0];339int last = pageRanges[pageRanges.length - 1][1];340this.jobAttributes.setPageRanges(new int[][] {341new int[] { first, last }342});343this.jobAttributes.setToPage(last);344this.jobAttributes.setFromPage(first);345346347// Verify that the cross feed and feed resolutions are the same348int[] res = this.pageAttributes.getPrinterResolution();349if (res[0] != res[1]) {350throw new IllegalArgumentException("Differing cross feed and feed"+351" resolutions not supported.");352}353354// Verify that the app has access to the file system355DestinationType dest= this.jobAttributes.getDestination();356if (dest == DestinationType.FILE) {357throwPrintToFile();358359// check if given filename is valid360String destStr = jobAttributes.getFileName();361if ((destStr != null) &&362(jobAttributes.getDialog() == JobAttributes.DialogType.NONE)) {363364File f = new File(destStr);365try {366// check if this is a new file and if filename chars are valid367// createNewFile returns false if file exists368if (f.createNewFile()) {369f.delete();370}371} catch (IOException ioe) {372throw new IllegalArgumentException("Cannot write to file:"+373destStr);374} catch (SecurityException se) {375//There is already file read/write access so at this point376// only delete access is denied. Just ignore it because in377// most cases the file created in createNewFile gets overwritten378// anyway.379}380381File pFile = f.getParentFile();382if ((f.exists() &&383(!f.isFile() || !f.canWrite())) ||384((pFile != null) &&385(!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) {386throw new IllegalArgumentException("Cannot write to file:"+387destStr);388}389}390}391}392393public boolean printDialog() {394395boolean proceedWithPrint = false;396397printerJob = PrinterJob.getPrinterJob();398if (printerJob == null) {399return false;400}401DialogType d = this.jobAttributes.getDialog();402PrintService pServ = printerJob.getPrintService();403if ((pServ == null) && (d == DialogType.NONE)){404return false;405}406copyAttributes(pServ);407408DefaultSelectionType select =409this.jobAttributes.getDefaultSelection();410if (select == DefaultSelectionType.RANGE) {411attributes.add(SunPageSelection.RANGE);412} else if (select == DefaultSelectionType.SELECTION) {413attributes.add(SunPageSelection.SELECTION);414} else {415attributes.add(SunPageSelection.ALL);416}417418if (frame != null) {419attributes.add(new DialogOwner(frame));420}421422if ( d == DialogType.NONE) {423proceedWithPrint = true;424} else {425if (d == DialogType.NATIVE) {426attributes.add(DialogTypeSelection.NATIVE);427} else { // (d == DialogType.COMMON)428attributes.add(DialogTypeSelection.COMMON);429}430if (proceedWithPrint = printerJob.printDialog(attributes)) {431if (pServ == null) {432// Windows gives an option to install a service433// when it detects there are no printers so434// we make sure we get the updated print service.435pServ = printerJob.getPrintService();436if (pServ == null) {437return false;438}439}440updateAttributes();441translateOutputProps();442}443}444445if (proceedWithPrint) {446447JobName jname = (JobName)attributes.get(JobName.class);448if (jname != null) {449printerJob.setJobName(jname.toString());450}451452pageFormat = new PageFormat();453454Media media = (Media)attributes.get(Media.class);455MediaSize mediaSize = null;456if (media != null && media instanceof MediaSizeName) {457mediaSize = MediaSize.getMediaSizeForName((MediaSizeName)media);458}459460Paper p = pageFormat.getPaper();461if (mediaSize != null) {462p.setSize(mediaSize.getX(MediaSize.INCH)*72.0,463mediaSize.getY(MediaSize.INCH)*72.0);464}465466if (pageAttributes.getOrigin()==OriginType.PRINTABLE) {467// AWT uses 1/4" borders by default468p.setImageableArea(18.0, 18.0,469p.getWidth()-36.0,470p.getHeight()-36.0);471} else {472p.setImageableArea(0.0,0.0,p.getWidth(),p.getHeight());473}474475pageFormat.setPaper(p);476477OrientationRequested orient =478(OrientationRequested)attributes.get(OrientationRequested.class);479if (orient!= null &&480orient == OrientationRequested.REVERSE_LANDSCAPE) {481pageFormat.setOrientation(PageFormat.REVERSE_LANDSCAPE);482} else if (orient == OrientationRequested.LANDSCAPE) {483pageFormat.setOrientation(PageFormat.LANDSCAPE);484} else {485pageFormat.setOrientation(PageFormat.PORTRAIT);486}487488PageRanges pageRangesAttr489= (PageRanges) attributes.get(PageRanges.class);490if (pageRangesAttr != null) {491// Get the PageRanges from print dialog.492int[][] range = pageRangesAttr.getMembers();493494int prevFromPage = this.jobAttributes.getFromPage();495int prevToPage = this.jobAttributes.getToPage();496497int currFromPage = range[0][0];498int currToPage = range[range.length - 1][1];499500// if from < to update fromPage first followed by toPage501// else update toPage first followed by fromPage502if (currFromPage < prevToPage) {503this.jobAttributes.setFromPage(currFromPage);504this.jobAttributes.setToPage(currToPage);505} else {506this.jobAttributes.setToPage(currToPage);507this.jobAttributes.setFromPage(currFromPage);508}509}510printerJob.setPrintable(this, pageFormat);511512}513514return proceedWithPrint;515}516517private void updateAttributes() {518Copies c = (Copies)attributes.get(Copies.class);519jobAttributes.setCopies(c.getValue());520521SunPageSelection sel =522(SunPageSelection)attributes.get(SunPageSelection.class);523if (sel == SunPageSelection.RANGE) {524jobAttributes.setDefaultSelection(DefaultSelectionType.RANGE);525} else if (sel == SunPageSelection.SELECTION) {526jobAttributes.setDefaultSelection(DefaultSelectionType.SELECTION);527} else {528jobAttributes.setDefaultSelection(DefaultSelectionType.ALL);529}530531Destination dest = (Destination)attributes.get(Destination.class);532if (dest != null) {533jobAttributes.setDestination(DestinationType.FILE);534jobAttributes.setFileName(dest.getURI().getPath());535} else {536jobAttributes.setDestination(DestinationType.PRINTER);537}538539PrintService serv = printerJob.getPrintService();540if (serv != null) {541jobAttributes.setPrinter(serv.getName());542}543544PageRanges range = (PageRanges)attributes.get(PageRanges.class);545int[][] members = range.getMembers();546jobAttributes.setPageRanges(members);547548SheetCollate collation =549(SheetCollate)attributes.get(SheetCollate.class);550if (collation == SheetCollate.COLLATED) {551jobAttributes.setMultipleDocumentHandling(552MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_COLLATED_COPIES);553} else {554jobAttributes.setMultipleDocumentHandling(555MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES);556}557558Sides sides = (Sides)attributes.get(Sides.class);559if (sides == Sides.TWO_SIDED_LONG_EDGE) {560jobAttributes.setSides(SidesType.TWO_SIDED_LONG_EDGE);561} else if (sides == Sides.TWO_SIDED_SHORT_EDGE) {562jobAttributes.setSides(SidesType.TWO_SIDED_SHORT_EDGE);563} else {564jobAttributes.setSides(SidesType.ONE_SIDED);565}566567// PageAttributes568569Chromaticity color =570(Chromaticity)attributes.get(Chromaticity.class);571if (color == Chromaticity.COLOR) {572pageAttributes.setColor(ColorType.COLOR);573} else {574pageAttributes.setColor(ColorType.MONOCHROME);575}576577OrientationRequested orient =578(OrientationRequested)attributes.get(OrientationRequested.class);579if (orient == OrientationRequested.LANDSCAPE) {580pageAttributes.setOrientationRequested(581OrientationRequestedType.LANDSCAPE);582} else {583pageAttributes.setOrientationRequested(584OrientationRequestedType.PORTRAIT);585}586587PrintQuality qual = (PrintQuality)attributes.get(PrintQuality.class);588if (qual == PrintQuality.DRAFT) {589pageAttributes.setPrintQuality(PrintQualityType.DRAFT);590} else if (qual == PrintQuality.HIGH) {591pageAttributes.setPrintQuality(PrintQualityType.HIGH);592} else { // NORMAL593pageAttributes.setPrintQuality(PrintQualityType.NORMAL);594}595596Media msn = (Media)attributes.get(Media.class);597if (msn != null && msn instanceof MediaSizeName) {598MediaType mType = unMapMedia((MediaSizeName)msn);599600if (mType != null) {601pageAttributes.setMedia(mType);602}603}604debugPrintAttributes(false, false);605}606607private void debugPrintAttributes(boolean ja, boolean pa ) {608if (ja) {609System.out.println("new Attributes\ncopies = "+610jobAttributes.getCopies()+611"\nselection = "+612jobAttributes.getDefaultSelection()+613"\ndest "+jobAttributes.getDestination()+614"\nfile "+jobAttributes.getFileName()+615"\nfromPage "+jobAttributes.getFromPage()+616"\ntoPage "+jobAttributes.getToPage()+617"\ncollation "+618jobAttributes.getMultipleDocumentHandling()+619"\nPrinter "+jobAttributes.getPrinter()+620"\nSides2 "+jobAttributes.getSides()621);622}623624if (pa) {625System.out.println("new Attributes\ncolor = "+626pageAttributes.getColor()+627"\norientation = "+628pageAttributes.getOrientationRequested()+629"\nquality "+pageAttributes.getPrintQuality()+630"\nMedia2 "+pageAttributes.getMedia()631);632}633}634635636/* From JobAttributes we will copy job name and duplex printing637* and destination.638* The majority of the rest of the attributes are reflected639* attributes.640*641* From PageAttributes we copy color, media size, orientation,642* origin type, resolution and print quality.643* We use the media, orientation in creating the page format, and644* the origin type to set its imageable area.645*646* REMIND: Interpretation of resolution, additional media sizes.647*/648private void copyAttributes(PrintService printServ) {649650attributes = new HashPrintRequestAttributeSet();651attributes.add(new JobName(docTitle, null));652PrintService pServ = printServ;653654String printerName = jobAttributes.getPrinter();655if (printerName != null && printerName != ""656&& pServ != null && !printerName.equals(pServ.getName())) {657658// Search for the given printerName in the list of PrintServices659PrintService []services = PrinterJob.lookupPrintServices();660try {661for (int i=0; i<services.length; i++) {662if (printerName.equals(services[i].getName())) {663printerJob.setPrintService(services[i]);664pServ = services[i];665break;666}667}668} catch (PrinterException pe) {669}670}671672DestinationType dest = jobAttributes.getDestination();673if (dest == DestinationType.FILE && pServ != null &&674pServ.isAttributeCategorySupported(Destination.class)) {675676String fileName = jobAttributes.getFileName();677678Destination defaultDest;679if (fileName == null && (defaultDest = (Destination)pServ.680getDefaultAttributeValue(Destination.class)) != null) {681attributes.add(defaultDest);682} else {683URI uri = null;684try {685if (fileName != null) {686if (fileName.isEmpty()) {687fileName = ".";688}689} else {690// defaultDest should not be null. The following code691// is only added to safeguard against a possible692// buggy implementation of a PrintService having a693// null default Destination.694fileName = "out.prn";695}696uri = (new File(fileName)).toURI();697} catch (SecurityException se) {698try {699// '\\' file separator is illegal character in opaque700// part and causes URISyntaxException, so we replace701// it with '/'702fileName = fileName.replace('\\', '/');703uri = new URI("file:"+fileName);704} catch (URISyntaxException e) {705}706}707if (uri != null) {708attributes.add(new Destination(uri));709}710}711}712attributes.add(new SunMinMaxPage(jobAttributes.getMinPage(),713jobAttributes.getMaxPage()));714SidesType sType = jobAttributes.getSides();715if (sType == SidesType.TWO_SIDED_LONG_EDGE) {716attributes.add(Sides.TWO_SIDED_LONG_EDGE);717} else if (sType == SidesType.TWO_SIDED_SHORT_EDGE) {718attributes.add(Sides.TWO_SIDED_SHORT_EDGE);719} else if (sType == SidesType.ONE_SIDED) {720attributes.add(Sides.ONE_SIDED);721}722723MultipleDocumentHandlingType hType =724jobAttributes.getMultipleDocumentHandling();725if (hType ==726MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_COLLATED_COPIES) {727attributes.add(SheetCollate.COLLATED);728} else {729attributes.add(SheetCollate.UNCOLLATED);730}731732attributes.add(new Copies(jobAttributes.getCopies()));733734attributes.add(new PageRanges(jobAttributes.getFromPage(),735jobAttributes.getToPage()));736737if (pageAttributes.getColor() == ColorType.COLOR) {738attributes.add(Chromaticity.COLOR);739} else {740attributes.add(Chromaticity.MONOCHROME);741}742743pageFormat = printerJob.defaultPage();744if (pageAttributes.getOrientationRequested() ==745OrientationRequestedType.LANDSCAPE) {746pageFormat.setOrientation(PageFormat.LANDSCAPE);747attributes.add(OrientationRequested.LANDSCAPE);748} else {749pageFormat.setOrientation(PageFormat.PORTRAIT);750attributes.add(OrientationRequested.PORTRAIT);751}752753MediaType media = pageAttributes.getMedia();754MediaSizeName msn = mapMedia(media);755if (msn != null) {756attributes.add(msn);757}758759PrintQualityType qType =760pageAttributes.getPrintQuality();761if (qType == PrintQualityType.DRAFT) {762attributes.add(PrintQuality.DRAFT);763} else if (qType == PrintQualityType.NORMAL) {764attributes.add(PrintQuality.NORMAL);765} else if (qType == PrintQualityType.HIGH) {766attributes.add(PrintQuality.HIGH);767}768}769770/**771* Gets a Graphics object that will draw to the next page.772* The page is sent to the printer when the graphics773* object is disposed. This graphics object will also implement774* the PrintGraphics interface.775* @see java.awt.PrintGraphics776*/777public Graphics getGraphics() {778779Graphics printGraphics = null;780781synchronized (this) {782++pageIndex;783784// Thread should not be created after end has been called.785// One way to detect this is if any of the graphics queue786// has been closed.787if (pageIndex == 0 && !graphicsToBeDrawn.isClosed()) {788789/* We start a thread on which the PrinterJob will run.790* The PrinterJob will ask for pages on that thread791* and will use a message queue to fulfill the application's792* requests for a Graphics on the application's793* thread.794*/795796startPrinterJobThread();797798}799notify();800}801802/* If the application has already been handed back803* a graphics then we need to put that graphics into804* the drawn queue so that the PrinterJob thread can805* return to the print system.806*/807if (currentGraphics != null) {808graphicsDrawn.append(currentGraphics);809currentGraphics = null;810}811812/* We'll block here until a new graphics becomes813* available.814*/815816currentGraphics = graphicsToBeDrawn.pop();817818if (currentGraphics instanceof PeekGraphics) {819( (PeekGraphics) currentGraphics).setAWTDrawingOnly();820graphicsDrawn.append(currentGraphics);821currentGraphics = graphicsToBeDrawn.pop();822}823824825if (currentGraphics != null) {826827/* In the PrintJob API, the origin is at the upper-828* left of the imageable area when using the new "printable"829* origin attribute, otherwise its the physical origin (for830* backwards compatibility. We emulate this by createing831* a PageFormat which matches and then performing the832* translate to the origin. This is a no-op if physical833* origin is specified.834*/835currentGraphics.translate(pageFormat.getImageableX(),836pageFormat.getImageableY());837838/* Scale to accommodate AWT's notion of printer resolution */839double awtScale = 72.0/getPageResolutionInternal();840currentGraphics.scale(awtScale, awtScale);841842/* The caller wants a Graphics instance but we do843* not want them to make 2D calls. We can't hand844* back a Graphics2D. The returned Graphics also845* needs to implement PrintGraphics, so we wrap846* the Graphics2D instance. The PrintJob API has847* the application dispose of the Graphics so848* we create a copy of the one returned by PrinterJob.849*/850printGraphics = new ProxyPrintGraphics(currentGraphics.create(),851this);852853}854855return printGraphics;856}857858/**859* Returns the dimensions of the page in pixels.860* The resolution of the page is chosen so that it861* is similar to the screen resolution.862* Except (since 1.3) when the application specifies a resolution.863* In that case it is scaled accordingly.864*/865public Dimension getPageDimension() {866double wid, hgt, scale;867if (pageAttributes != null &&868pageAttributes.getOrigin()==OriginType.PRINTABLE) {869wid = pageFormat.getImageableWidth();870hgt = pageFormat.getImageableHeight();871} else {872wid = pageFormat.getWidth();873hgt = pageFormat.getHeight();874}875scale = getPageResolutionInternal() / 72.0;876return new Dimension((int)(wid * scale), (int)(hgt * scale));877}878879private double getPageResolutionInternal() {880if (pageAttributes != null) {881int []res = pageAttributes.getPrinterResolution();882if (res[2] == 3) {883return res[0];884} else /* if (res[2] == 4) */ {885return (res[0] * 2.54);886}887} else {888return 72.0;889}890}891892/**893* Returns the resolution of the page in pixels per inch.894* Note that this doesn't have to correspond to the physical895* resolution of the printer.896*/897public int getPageResolution() {898return (int)getPageResolutionInternal();899}900901/**902* Returns true if the last page will be printed first.903*/904public boolean lastPageFirst() {905return false;906}907908/**909* Ends the print job and does any necessary cleanup.910*/911public synchronized void end() {912913/* Prevent the PrinterJob thread from appending any more914* graphics to the to-be-drawn queue915*/916graphicsToBeDrawn.close();917918/* If we have a currentGraphics it was the last one returned to the919* PrintJob client. Append it to the drawn queue so that print()920* will return allowing the page to be flushed.921* This really ought to happen in dispose() but for whatever reason922* that isn't how the old PrintJob worked even though its spec923* said dispose() flushed the page.924*/925if (currentGraphics != null) {926graphicsDrawn.append(currentGraphics);927}928graphicsDrawn.closeWhenEmpty();929930/* Wait for the PrinterJob.print() thread to terminate, ensuring931* that RasterPrinterJob has made its end doc call, and resources932* are released, files closed etc.933*/934if( printerJobThread != null && printerJobThread.isAlive() ){935try {936printerJobThread.join();937} catch (InterruptedException e) {938}939}940}941942/**943* Ends this print job once it is no longer referenced.944* @see #end945*/946@SuppressWarnings("deprecation")947public void finalize() {948end();949}950951/**952* Prints the page at the specified index into the specified953* {@link Graphics} context in the specified954* format. A {@code PrinterJob} calls the955* {@code Printable} interface to request that a page be956* rendered into the context specified by957* {@code graphics}. The format of the page to be drawn is958* specified by {@code pageFormat}. The zero based index959* of the requested page is specified by {@code pageIndex}.960* If the requested page does not exist then this method returns961* NO_SUCH_PAGE; otherwise PAGE_EXISTS is returned.962* The {@code Graphics} class or subclass implements the963* {@link java.awt.PrintGraphics} interface to provide additional964* information. If the {@code Printable} object965* aborts the print job then it throws a {@link PrinterException}.966* @param graphics the context into which the page is drawn967* @param pageFormat the size and orientation of the page being drawn968* @param pageIndex the zero based index of the page to be drawn969* @return PAGE_EXISTS if the page is rendered successfully970* or NO_SUCH_PAGE if {@code pageIndex} specifies a971* non-existent page.972* @exception java.awt.print.PrinterException973* thrown when the print job is terminated.974*/975public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)976throws PrinterException {977978int result;979980/* This method will be called by the PrinterJob on a thread other981* that the application's thread. We hold on to the graphics982* until we can rendevous with the application's thread and983* hand over the graphics. The application then does all the984* drawing. When the application is done drawing we rendevous985* again with the PrinterJob thread and release the Graphics986* so that it knows we are done.987*/988989/* Add the graphics to the message queue of graphics to990* be rendered. This is really a one slot queue. The991* application's thread will come along and remove the992* graphics from the queue when the app asks for a graphics.993*/994graphicsToBeDrawn.append( (Graphics2D) graphics);995996/* We now wait for the app's thread to finish drawing on997* the Graphics. This thread will sleep until the application998* release the graphics by placing it in the graphics drawn999* message queue. If the application signals that it is1000* finished drawing the entire document then we'll get null1001* returned when we try and pop a finished graphic.1002*/1003if (graphicsDrawn.pop() != null) {1004result = PAGE_EXISTS;1005} else {1006result = NO_SUCH_PAGE;1007}10081009return result;1010}10111012private void startPrinterJobThread() {1013printerJobThread =1014new Thread(null, this, "printerJobThread", 0, false);1015printerJobThread.start();1016}101710181019public void run() {10201021try {1022attributes.remove(PageRanges.class);1023printerJob.print(attributes);1024} catch (PrinterException e) {1025//REMIND: need to store this away and not rethrow it.1026}10271028/* Close the message queues so that nobody is stuck1029* waiting for one.1030*/1031graphicsToBeDrawn.closeWhenEmpty();1032graphicsDrawn.close();1033}10341035private class MessageQ {10361037private String qid="noname";10381039private ArrayList<Graphics2D> queue = new ArrayList<>();10401041MessageQ(String id) {1042qid = id;1043}10441045synchronized void closeWhenEmpty() {10461047while (queue != null && queue.size() > 0) {1048try {1049wait(1000);1050} catch (InterruptedException e) {1051// do nothing.1052}1053}10541055queue = null;1056notifyAll();1057}10581059synchronized void close() {1060queue = null;1061notifyAll();1062}10631064synchronized boolean append(Graphics2D g) {10651066boolean queued = false;10671068if (queue != null) {1069queue.add(g);1070queued = true;1071notify();1072}10731074return queued;1075}10761077synchronized Graphics2D pop() {1078Graphics2D g = null;10791080while (g == null && queue != null) {10811082if (queue.size() > 0) {1083g = queue.remove(0);1084notify();10851086} else {1087try {1088wait(2000);1089} catch (InterruptedException e) {1090// do nothing.1091}1092}1093}10941095return g;1096}10971098synchronized boolean isClosed() {1099return queue == null;1100}11011102}110311041105private static int[] getSize(MediaType mType) {1106int []dim = new int[2];1107dim[0] = 612;1108dim[1] = 792;11091110for (int i=0; i < SIZES.length; i++) {1111if (SIZES[i] == mType) {1112dim[0] = WIDTHS[i];1113dim[1] = LENGTHS[i];1114break;1115}1116}1117return dim;1118}11191120public static MediaSizeName mapMedia(MediaType mType) {1121MediaSizeName media = null;11221123// JAVAXSIZES.length and SIZES.length must be equal!1124// Attempt to recover by getting the smaller size.1125int length = Math.min(SIZES.length, JAVAXSIZES.length);11261127for (int i=0; i < length; i++) {1128if (SIZES[i] == mType) {1129if ((JAVAXSIZES[i] != null) &&1130MediaSize.getMediaSizeForName(JAVAXSIZES[i]) != null) {1131media = JAVAXSIZES[i];1132break;1133} else {1134/* create Custom Media */1135media = new CustomMediaSizeName(SIZES[i].toString());11361137float w = (float)Math.rint(WIDTHS[i] / 72.0);1138float h = (float)Math.rint(LENGTHS[i] / 72.0);1139if (w > 0.0 && h > 0.0) {1140// add new created MediaSize to our static map1141// so it will be found when we call findMedia1142new MediaSize(w, h, Size2DSyntax.INCH, media);1143}11441145break;1146}1147}1148}1149return media;1150}115111521153public static MediaType unMapMedia(MediaSizeName mSize) {1154MediaType media = null;11551156// JAVAXSIZES.length and SIZES.length must be equal!1157// Attempt to recover by getting the smaller size.1158int length = Math.min(SIZES.length, JAVAXSIZES.length);11591160for (int i=0; i < length; i++) {1161if (JAVAXSIZES[i] == mSize) {1162if (SIZES[i] != null) {1163media = SIZES[i];1164break;1165}1166}1167}1168return media;1169}11701171private void translateInputProps() {1172if (props == null) {1173return;1174}11751176String str;11771178str = props.getProperty(DEST_PROP);1179if (str != null) {1180if (str.equals(PRINTER)) {1181jobAttributes.setDestination(DestinationType.PRINTER);1182} else if (str.equals(FILE)) {1183jobAttributes.setDestination(DestinationType.FILE);1184}1185}1186str = props.getProperty(PRINTER_PROP);1187if (str != null) {1188jobAttributes.setPrinter(str);1189}1190str = props.getProperty(FILENAME_PROP);1191if (str != null) {1192jobAttributes.setFileName(str);1193}1194str = props.getProperty(NUMCOPIES_PROP);1195if (str != null) {1196jobAttributes.setCopies(Integer.parseInt(str));1197}11981199this.options = props.getProperty(OPTIONS_PROP, "");12001201str = props.getProperty(ORIENT_PROP);1202if (str != null) {1203if (str.equals(PORTRAIT)) {1204pageAttributes.setOrientationRequested(1205OrientationRequestedType.PORTRAIT);1206} else if (str.equals(LANDSCAPE)) {1207pageAttributes.setOrientationRequested(1208OrientationRequestedType.LANDSCAPE);1209}1210}1211str = props.getProperty(PAPERSIZE_PROP);1212if (str != null) {1213if (str.equals(LETTER)) {1214pageAttributes.setMedia(SIZES[MediaType.LETTER.hashCode()]);1215} else if (str.equals(LEGAL)) {1216pageAttributes.setMedia(SIZES[MediaType.LEGAL.hashCode()]);1217} else if (str.equals(EXECUTIVE)) {1218pageAttributes.setMedia(SIZES[MediaType.EXECUTIVE.hashCode()]);1219} else if (str.equals(A4)) {1220pageAttributes.setMedia(SIZES[MediaType.A4.hashCode()]);1221}1222}1223}12241225private void translateOutputProps() {1226if (props == null) {1227return;1228}12291230String str;12311232props.setProperty(DEST_PROP,1233(jobAttributes.getDestination() == DestinationType.PRINTER) ?1234PRINTER : FILE);1235str = jobAttributes.getPrinter();1236if (str != null && !str.isEmpty()) {1237props.setProperty(PRINTER_PROP, str);1238}1239str = jobAttributes.getFileName();1240if (str != null && !str.isEmpty()) {1241props.setProperty(FILENAME_PROP, str);1242}1243int copies = jobAttributes.getCopies();1244if (copies > 0) {1245props.setProperty(NUMCOPIES_PROP, "" + copies);1246}1247str = this.options;1248if (str != null && !str.isEmpty()) {1249props.setProperty(OPTIONS_PROP, str);1250}1251props.setProperty(ORIENT_PROP,1252(pageAttributes.getOrientationRequested() ==1253OrientationRequestedType.PORTRAIT)1254? PORTRAIT : LANDSCAPE);1255MediaType media = SIZES[pageAttributes.getMedia().hashCode()];1256if (media == MediaType.LETTER) {1257str = LETTER;1258} else if (media == MediaType.LEGAL) {1259str = LEGAL;1260} else if (media == MediaType.EXECUTIVE) {1261str = EXECUTIVE;1262} else if (media == MediaType.A4) {1263str = A4;1264} else {1265str = media.toString();1266}1267props.setProperty(PAPERSIZE_PROP, str);1268}12691270private void throwPrintToFile() {1271@SuppressWarnings("removal")1272SecurityManager security = System.getSecurityManager();1273FilePermission printToFilePermission = null;1274if (security != null) {1275if (printToFilePermission == null) {1276printToFilePermission =1277new FilePermission("<<ALL FILES>>", "read,write");1278}1279security.checkPermission(printToFilePermission);1280}1281}12821283}128412851286