/* * BufferPrinter1_7.java - Main class that controls printing * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 2001 Slava Pestov * Portions copyright (C) 2002 Thomas Dilts * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gjt.sp.jedit.print; import java.awt.*; import java.awt.geom.*; import java.awt.image.BufferedImage; import java.awt.print.*; import java.io.*; import java.util.HashMap; import javax.print.*; import javax.print.attribute.*; import javax.print.attribute.standard.*; import javax.print.event.*; import javax.swing.JOptionPane; import org.gjt.sp.jedit.*; import org.gjt.sp.jedit.msg.PropertiesChanged; import org.gjt.sp.util.Log; import org.gjt.sp.util.ThreadUtilities; public class BufferPrinter1_7 { /** * Shows the printer dialog with the page setup tab active, other tabs inactive. * @param view The parent view for the dialog. */ public static void pageSetup( View view ) { loadPrintSpec(); PrinterDialog printerDialog = new PrinterDialog( view, format, true ); if ( !printerDialog.isCanceled() ) { format = printerDialog.getAttributes(); savePrintSpec(); EditBus.send(new PropertiesChanged(null)); } } public static void print( final View view, final Buffer buffer ) { //Log.log(Log.DEBUG, BufferPrinter1_7.class, "print buffer " + buffer.getPath()); // load any saved printing attributes, these are put into 'format' loadPrintSpec(); String jobName = MiscUtilities.abbreviateView( buffer.getPath() ); format.add( new JobName( jobName, null ) ); // show the print dialog so the user can make their printer settings PrinterDialog printerDialog = new PrinterDialog( view, format, false ); if ( printerDialog.isCanceled() ) { //Log.log(Log.DEBUG, BufferPrinter1_7.class, "print dialog canceled"); return; } // set up the print job PrintService printService = printerDialog.getPrintService(); if ( printService != null ) { //Log.log(Log.DEBUG, BufferPrinter1_7.class, "using print service: " + printService); try { job = printService.createPrintJob(); job.addPrintJobListener( new BufferPrinter1_7.JobListener( view ) ); format = printerDialog.getAttributes(); savePrintSpec(); EditBus.send(new PropertiesChanged(null)); } catch ( Exception e ) { e.printStackTrace(); JOptionPane.showMessageDialog( view, jEdit.getProperty( "print-error.message", new String[] {e.getMessage()} ), jEdit.getProperty( "print-error.title" ), JOptionPane.ERROR_MESSAGE ); return; } } else { JOptionPane.showMessageDialog( view, jEdit.getProperty( "print-error.message", new String[] {"Invalid print service."} ), jEdit.getProperty( "print-error.title" ), JOptionPane.ERROR_MESSAGE ); return; } // set up the printable. Some values need to be set directly from the print // dialog since they don't have attributes, like reverse page printing and printRangeType BufferPrintable1_7 printable = new BufferPrintable1_7( format, view, buffer ); printable.setReverse( printerDialog.getReverse() ); int printRangeType = printerDialog.getPrintRangeType(); printable.setPrintRangeType( printRangeType ); // check if printing a selection, if so, recalculate the page ranges. // TODO: I'm not taking even/odd page setting into account here, nor am // I considering any page values that may have been set in the page range. // I don't think this is important for printing a selection, which is // generally just a few lines rather than pages. I could be wrong... if ( printRangeType == PrinterDialog.SELECTION ) { // calculate the actual pages with a selection or bail if there is no selection int selectionCount = view.getTextArea().getSelectionCount(); if ( selectionCount == 0 ) { JOptionPane.showMessageDialog( view, jEdit.getProperty( "print-error.message", new String[] {"No text is selected to print."} ), jEdit.getProperty( "print-error.title" ), JOptionPane.ERROR_MESSAGE ); return; } // get the page ranges from the printable HashMap<Integer, Range> pageRanges = getPageRanges( printable, format ); if ( pageRanges == null || pageRanges.isEmpty() ) { JOptionPane.showMessageDialog( view, jEdit.getProperty( "print-error.message", new String[] {"Unable to calculate page ranges."} ), jEdit.getProperty( "print-error.title" ), JOptionPane.ERROR_MESSAGE ); return; } // find the pages that contain the selection(s) and construct a new // page range for the format int[] selectedLines = view.getTextArea().getSelectedLines(); StringBuilder pageRange = new StringBuilder(); for ( Integer i : pageRanges.keySet() ) { Range range = pageRanges.get( i ); for ( int line : selectedLines ) { if ( range.contains( line ) ) { pageRange.append( i ).append( ',' ); break; } } } pageRange.deleteCharAt( pageRange.length() - 1 ); format.add( new PageRanges( pageRange.toString() ) ); // also tell the printable exactly which lines are selected so it // doesn't have to fetch them itself printable.setSelectedLines( selectedLines ); } // copy the doc attributes from the print format attributes //Log.log(Log.DEBUG, BufferPrinter1_7.class, "--- print request attributes ---"); DocAttributeSet docAttributes = new HashDocAttributeSet(); Attribute[] attributes = format.toArray(); for (Attribute attribute : attributes) { boolean isDocAttr = attribute instanceof DocAttribute; //Log.log(Log.DEBUG, BufferPrinter1_7.class, attribute.getName() + " = " + attribute + ", is doc attr? " + isDocAttr); if (isDocAttr) { docAttributes.add(attribute); } } //Log.log(Log.DEBUG, BufferPrinter1_7.class, "--- end print request attributes ---"); final Doc doc = new SimpleDoc( printable, DocFlavor.SERVICE_FORMATTED.PRINTABLE, docAttributes ); // ready to print // run this in a background thread, it can take some time for a large buffer Runnable runner = new Runnable() { public void run() { try { //Log.log(Log.DEBUG, this, "sending print job to printer"); job.print( doc, format ); //Log.log(Log.DEBUG, this, "printing complete"); } catch ( PrintException e ) { JOptionPane.showMessageDialog( view, jEdit.getProperty( "print-error.message", new String[] {e.getMessage()} ), jEdit.getProperty( "print-error.title" ), JOptionPane.ERROR_MESSAGE ); } } }; ThreadUtilities.runInBackground( runner ); } //}}} /** * This is intended for use by the PrintPreview dialog. */ protected static void printPage( PrintPreviewModel model ) { String jobName = MiscUtilities.abbreviateView( model.getBuffer().getPath() ); model.getAttributes().add( new JobName( jobName, null ) ); // set up the print job PrintService printService = model.getPrintService(); if (printService == null) { printService = PrintServiceLookup.lookupDefaultPrintService(); } if ( printService != null ) { try { job = printService.createPrintJob(); } catch ( Exception e ) { JOptionPane.showMessageDialog( model.getView(), jEdit.getProperty( "print-error.message", new String[] {e.getMessage()} ), jEdit.getProperty( "print-error.title" ), JOptionPane.ERROR_MESSAGE ); return; } } else { JOptionPane.showMessageDialog( model.getView(), jEdit.getProperty( "print-error.message", new String[] {"Invalid print service."} ), jEdit.getProperty( "print-error.title" ), JOptionPane.ERROR_MESSAGE ); return; } // set up the printable to print just the requested page BufferPrintable1_7 printable = new BufferPrintable1_7( model.getAttributes(), model.getView(), model.getBuffer() ); printable.setPages(model.getPageRanges()); int pageNumber = model.getPageNumber(); try { printable.print(model.getGraphics(), model, pageNumber); } catch(Exception e) { e.printStackTrace(); } } /** * This is intended for use by classes that need to know the page ranges * of the buffer. */ public static HashMap<Integer, Range> getPageRanges( View view, Buffer buffer, PrintRequestAttributeSet attributes ) { if (attributes == null) { loadPrintSpec(); attributes = format; } BufferPrintable1_7 printable = new BufferPrintable1_7( attributes, view, buffer ); return BufferPrinter1_7.getPageRanges( printable, attributes ); } // have the printable calculate the pages and ranges, the map has the page // number as the key, a range containing the start and end line numbers of // that page private static HashMap<Integer, Range> getPageRanges( BufferPrintable1_7 printable, PrintRequestAttributeSet attributes ) { PageFormat pageFormat = createPageFormat( attributes ); GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); BufferedImage image = new BufferedImage(new Double(pageFormat.getImageableWidth()).intValue(), new Double(pageFormat.getImageableHeight()).intValue(), BufferedImage.TYPE_INT_RGB); Graphics graphics = ge.createGraphics(image); Paper paper = pageFormat.getPaper(); Rectangle2D.Double clipRegion = new Rectangle2D.Double(paper.getImageableX(), paper.getImageableY(), paper.getImageableWidth(), paper.getImageableHeight()); graphics.setClip(clipRegion); try { HashMap<Integer, Range> pageLineRanges = printable.calculatePages( graphics, pageFormat ); PageRanges pr = (PageRanges)attributes.get(PageRanges.class); if (pr == null) { pr = new PageRanges( 1, 1000 ); } HashMap<Integer, Range> newLineRanges = new HashMap<Integer, Range>(); for (Integer i : pageLineRanges.keySet()) { if (pr.contains(i)) { newLineRanges.put(i, pageLineRanges.get(i)); } } //return newLineRanges; return pageLineRanges; } catch(Exception e) { return null; } } public static PageFormat getDefaultPageFormat(PrintRequestAttributeSet attributes) { return BufferPrinter1_7.createPageFormat(attributes); } // create a page format using the values from the given attribute set private static PageFormat createPageFormat( PrintRequestAttributeSet attributes ) { Paper paper = new Paper(); MediaPrintableArea mpa = ( MediaPrintableArea )attributes.get( MediaPrintableArea.class ); int units = MediaPrintableArea.INCH; double dpi = 72.0; // Paper uses 72 dpi double x = ( double )mpa.getX( units ) * dpi; double y = ( double )mpa.getY( units ) * dpi; double w = ( double )mpa.getWidth( units ) * dpi; double h = ( double )mpa.getHeight( units ) * dpi; paper.setImageableArea( x, y, w, h ); int orientation = PageFormat.PORTRAIT; OrientationRequested or = ( OrientationRequested )attributes.get( OrientationRequested.class ); if ( OrientationRequested.LANDSCAPE.equals( or ) || OrientationRequested.REVERSE_LANDSCAPE.equals( or ) ) { orientation = PageFormat.LANDSCAPE; } PageFormat pageFormat = new PageFormat(); pageFormat.setPaper( paper ); pageFormat.setOrientation( orientation ); return pageFormat; } // {{{ loadPrintSpec() method // this finds a previously saved print attribute set in the settings directory, // or creates a new, empty attribute set if not found. private static void loadPrintSpec() { format = new HashPrintRequestAttributeSet(); String settings = jEdit.getSettingsDirectory(); if ( settings != null ) { String printSpecPath = MiscUtilities.constructPath( settings, "printspec" ); File filePrintSpec = new File( printSpecPath ); if ( filePrintSpec.exists() ) { FileInputStream fileIn; ObjectInputStream obIn = null; try { fileIn = new FileInputStream( filePrintSpec ); obIn = new ObjectInputStream( fileIn ); format = ( HashPrintRequestAttributeSet )obIn.readObject(); } catch ( Exception e ) { Log.log( Log.ERROR, BufferPrinter1_7.class, e ); } finally { try { if ( obIn != null ) { obIn.close(); } } catch ( IOException e ) // NOPMD { } } } } MediaPrintableArea mpa = ( MediaPrintableArea )format.get( MediaPrintableArea.class ); if (mpa == null) { // assume US Letter size - why? Because I live in the US mpa = new MediaPrintableArea(0.5f, 0.5f, 10.0f, 7.5f, MediaPrintableArea.INCH); format.add(mpa); } } private static void savePrintSpec() { String settings = jEdit.getSettingsDirectory(); if ( settings == null ) { return; } String printSpecPath = MiscUtilities.constructPath( settings, "printspec" ); File filePrintSpec = new File( printSpecPath ); FileOutputStream fileOut; ObjectOutputStream objectOut = null; try { fileOut = new FileOutputStream( filePrintSpec ); objectOut = new ObjectOutputStream( fileOut ); objectOut.writeObject( format ); } catch ( Exception e ) { e.printStackTrace(); } finally { if ( objectOut != null ) { try { objectOut.flush(); } catch ( IOException e ) // NOPMD { } try { objectOut.close(); } catch ( IOException e ) // NOPMD { } } } } // print job listener, does clean up when the print job is complete and shows // the user any errors generated by the printing system static class JobListener extends PrintJobAdapter { private View view; public JobListener( View view ) { this.view = view; } @Override public void printJobCompleted( PrintJobEvent pje ) { // if the print service is a "print to file" service, then need to // flush and close the output stream. PrintService printService = pje.getPrintJob().getPrintService(); if ( printService instanceof StreamPrintService ) { StreamPrintService streamService = ( StreamPrintService )printService; OutputStream outputStream = streamService.getOutputStream(); try { outputStream.flush(); } catch ( Exception e ) // NOPMD { } try { outputStream.close(); } catch ( Exception e ) // NOPMD { } } view.getStatus().setMessageAndClear( "Printing complete." ); } @Override public void printJobFailed( PrintJobEvent pje ) { JOptionPane.showMessageDialog( view, jEdit.getProperty( "print-error.message", new String[] {"Print job failed."} ), jEdit.getProperty( "print-error.title" ), JOptionPane.ERROR_MESSAGE ); } @Override public void printJobRequiresAttention( PrintJobEvent pje ) { JOptionPane.showMessageDialog( view, jEdit.getProperty( "print-error.message", new String[] {"Check the printer."} ), jEdit.getProperty( "print-error.title" ), JOptionPane.ERROR_MESSAGE ); } } private static PrintRequestAttributeSet format; private static DocPrintJob job; }