package net.sourceforge.fidocadj; import javax.swing.*; import javax.print.attribute.*; import javax.print.attribute.standard.*; import java.io.*; import java.awt.print.*; import java.awt.*; import java.util.*; import java.awt.geom.*; import net.sourceforge.fidocadj.circuit.*; import net.sourceforge.fidocadj.dialogs.print.*; import net.sourceforge.fidocadj.globals.*; import net.sourceforge.fidocadj.geom.*; import net.sourceforge.fidocadj.graphic.*; import net.sourceforge.fidocadj.graphic.swing.*; import net.sourceforge.fidocadj.layers.*; /** PrintTools.java Class performing interface operations for launching print operations. It also reads and stores preferences. <pre> This file is part of FidoCadJ. FidoCadJ 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 3 of the License, or (at your option) any later version. FidoCadJ 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 FidoCadJ. If not, @see <a href=http://www.gnu.org/licenses/>http://www.gnu.org/licenses/</a>. Copyright 2015-2017 by Davide Bucci </pre> @author Davide Bucci */ public class PrintTools implements Printable { // Settings related to the printing modes. private boolean printMirror; // Mirror the printed output. private boolean printFitToPage; // Fit to the page. private boolean printLandscape; // Put the page in the landscape mode. private boolean printBlackWhite; // Black and white. private int currentLayerSelected; // Layer to print. If negative=all. // Margins private double topMargin=-1; private double bottomMargin=-1; private double leftMargin=-1; private double rightMargin=-1; private CircuitPanel cc; private boolean showMargins; private final static double LIMIT=1e-5; private final static double MULT=16.0; // Multiplying 72dp times MULT private final static double INCH=2.54; // in cm private final static double NATIVERES=72.0; // in dpi /** Standard constructor. */ public PrintTools() { // some standard configurations printMirror = false; printFitToPage = false; printLandscape = false; adjustMargins(PrinterJob.getPrinterJob().defaultPage()); showMargins = false; currentLayerSelected=-1; } /** If the values of the margins are negative, they will be adjusted in such a way that the printable area will be covered in the most efficient way. */ private void adjustMargins(PageFormat pf) { final double correction=0.01; // Start of the imageable region, in centimeters. if(leftMargin<0.0) { leftMargin=pf.getImageableX()/NATIVERES*INCH+correction; } if(topMargin<0.0) { topMargin=pf.getImageableY()/NATIVERES*INCH+correction; } if(rightMargin<0.0) { rightMargin=(pf.getWidth()-pf.getImageableX() -pf.getImageableWidth())/NATIVERES*INCH+correction; } if(bottomMargin<0.0) { bottomMargin=(pf.getHeight()-pf.getImageableY() -pf.getImageableHeight())/NATIVERES*INCH+correction; } } /** Determine if the margins should be shown or not. @param sm true if the margins should be shown (for example, in a print preview operation). */ public void setShowMargins(boolean sm) { showMargins=sm; } /** Set the size of the margins, in centimeters. The orientation of those margins should correspond to the page in the portrait orientation. @param tm top margin. @param bm bottom margin. @param lm left margin. @param rm right margin. */ public void setMargins(double tm, double bm, double lm, double rm) { if(tm>0.0) topMargin=tm; if(bm>0.0) bottomMargin=bm; if(lm>0.0) leftMargin=lm; if(rm>0.0) rightMargin=rm; } /** Associate to a given CircuitPanel containing the circuit to be printed. @param rCC the CircuitPanel containing the drawing to be exported. */ public void associateToCircuitPanel(CircuitPanel rCC) { cc=rCC; } /** Show a dialog for printing the current drawing. @param fff the parent frame which will be used for dialogs and message boxes. */ public void printDrawing(JFrame fff) { PrinterJob job = PrinterJob.getPrinterJob(); PageFormat pp = job.defaultPage(); DialogPrint dp=new DialogPrint(fff, cc.getDrawingModel(), pp); dp.setMirror(printMirror); dp.setFit(printFitToPage); dp.setBW(printBlackWhite); dp.setLandscape(printLandscape); dp.setMaxMargins(pp.getWidth()/NATIVERES*INCH, pp.getHeight()/NATIVERES*INCH); dp.setMargins(topMargin, bottomMargin, leftMargin, rightMargin); boolean noexit; do { // Show the (modal) dialog. dp.setVisible(true); noexit=configurePrinting(dp, pp,true); if (!dp.shouldPrint()) return; } while (noexit); try { // Launch printing. executePrinting(job); } catch (PrinterException ex) { // The job did not successfully complete JOptionPane.showMessageDialog(fff, Globals.messages.getString("Print_uncomplete")); } } /** Configure the printing object by reading the current settings of the DialogPrint employed for the user interaction. @param dp the printing dialog. @param pp the standard page format. @param checkMarginSize if true, the size of the margins is checked and an error message is issued in case of problems. @return true if the printing operation should not be done and the dialog should be shown again. */ public boolean configurePrinting(DialogPrint dp, PageFormat pp, boolean checkMarginSize) { boolean noexit=false; if (!dp.shouldPrint() && checkMarginSize) return true; // Get some information about the printing options. printMirror = dp.getMirror(); printFitToPage = dp.getFit(); printLandscape = dp.getLandscape(); printBlackWhite=dp.getBW(); try { topMargin=dp.getTMargin(); bottomMargin=dp.getBMargin(); leftMargin=dp.getLMargin(); rightMargin=dp.getRMargin(); } catch (java.lang.NumberFormatException n) { System.out.println( Globals.messages.getString("Format_invalid")); } if(checkMarginSize && (topMargin/INCH*NATIVERES<pp.getImageableY() || bottomMargin/INCH*NATIVERES<pp.getHeight() -pp.getImageableHeight()-pp.getImageableY() || leftMargin/INCH*NATIVERES<pp.getImageableX() || rightMargin/INCH*NATIVERES<pp.getWidth() -pp.getImageableWidth()-pp.getImageableX())) { int answer = JOptionPane.showConfirmDialog(dp, Globals.messages.getString("Print_outside_regions"), "",JOptionPane.YES_NO_OPTION); if(answer!= JOptionPane.YES_OPTION) { noexit=true; } } currentLayerSelected=dp.getSingleLayerToPrint(); // Deselect all elements. cc.getSelectionActions().setSelectionAll(false); return noexit; } /** Low level printing operations. @param job the current printing job. */ private void executePrinting(PrinterJob job) throws PrinterException { job.setPrintable(this); if (job.printDialog()) { PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet(); // Set the correct printing orientation. if (printLandscape) { aset.add(OrientationRequested.LANDSCAPE); } else { aset.add(OrientationRequested.PORTRAIT); } job.print(aset); } } /** The printing interface (prints one page). @param g the graphic context. @param pf the page format. @param page the page number. @return PAGE_EXISTS if the page has to be printed. @throws PrinterException if a printing error occurs. */ public int print(Graphics g, PageFormat pf, int page) throws PrinterException { // This is not a "real" margin, but just a tiny amount which ensures // that even when the calculations are rounded up, the printout does // not span erroneously over multiple pages. int security=5; // This might be explained as follows: // 1 - The Java printing system normally works with an internal // resolution which is 72 dpi (probably inspired by Postscript). // 2 - To have a sufficient resolution, this is increased by 16 times, // by using the scale method of the graphic object associated to the // printer. This gives a 72 dpi * 16=1152 dpi resolution. // 3 - The 0.127 mm pitch used in FidoCadJ corresponds to a 200 dpi // resolution. Calculating 1152 dpi / 200 dpi gives the 5.76 constant // for the zoom. double xscale = 1.0/MULT; // Set 1152 logical units for an inch double yscale = 1.0/MULT; // as the standard resolution is 72 double zoom = NATIVERES*MULT/200.0;// in a 1152 dpi resolution is 1:1 double shownWidth; // Printed region (taking into account double shownHeight; // margins). Graphics2D g2d = (Graphics2D)g; AffineTransform oldTransform = g2d.getTransform(); // Mark with a light red the unprintable area of the sheet. if(showMargins) { g2d.setColor(new Color(255,200,200)); g2d.fillRect(0,0, (int)pf.getImageableX(), (int)pf.getHeight()); g2d.fillRect(0,0, (int)pf.getWidth(), (int)pf.getImageableY()); g2d.fillRect((int)(pf.getImageableX()+pf.getImageableWidth()), 0, (int)pf.getImageableX(), (int)pf.getHeight()); g2d.fillRect(0, (int)(pf.getImageableY()+pf.getImageableHeight()), (int)pf.getWidth(), (int)(pf.getHeight()- pf.getImageableHeight()-pf.getImageableY())); } // User (0,0) is typically outside the imageable area, so we must // translate by the X and Y values in the PageFormat to avoid clipping, // taking into account the margins which are needed. if (printMirror) { g2d.translate(pf.getWidth()-rightMargin/INCH*NATIVERES, topMargin/INCH*NATIVERES); g2d.scale(-xscale,yscale); } else { g2d.translate(leftMargin/INCH*NATIVERES, topMargin/INCH*NATIVERES); g2d.scale(xscale,yscale); } shownWidth=(pf.getWidth()- (leftMargin+rightMargin)/INCH*NATIVERES)*MULT; shownHeight=(pf.getHeight()- (topMargin+bottomMargin)/INCH*NATIVERES)*MULT; // The current margins are shown with a dashed black line. Rectangle2D.Double border = new Rectangle2D.Double(0, 0, shownWidth-2*security, shownHeight-2*security); if(showMargins) { float dashBorder[] = {150.0f}; BasicStroke dashed = new BasicStroke(50.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dashBorder, 0.0f); g2d.setStroke(dashed); g2d.setColor(Color.black); g2d.draw(border); } // Clip the drawing inside the borders. g2d.clip(border); MapCoordinates m; // Perform an adjustement if we need to fit the drawing to the page. if (printFitToPage) { MapCoordinates n = DrawingSize.calculateZoomToFit( cc.getDrawingModel(), (int)((pf.getWidth()-(leftMargin+rightMargin) /INCH*NATIVERES)*MULT) -2*security, (int)((pf.getHeight()-(topMargin+bottomMargin) /INCH*NATIVERES)*MULT) -2*security, true); zoom=n.getXMagnitude(); } m=new MapCoordinates(); m.setMagnitudes(zoom, zoom); PointG o=new PointG(0,0); DimensionG dim = DrawingSize.getImageSize( cc.getDrawingModel(), zoom, true, o); int imageWidth = dim.width; int imageHeight = dim.height; // Calculate how many pages are needed in the horisontal and in the // vertical dimensions. The printout will be organized as a mosaic. int npagesx = (int)Math.ceil((imageWidth)/(double)shownWidth); int npagesy = (int)Math.ceil((imageHeight)/(double)shownHeight); // Calculate the total number of pages. int npages=npagesx*npagesy; // Current pages of the mosaic. int pagex=page % npagesx; int pagey=page / npagesx; if(printFitToPage) { g2d.translate(-o.x,-o.y); } // Check if printing is finished. if(page>=npages) { g2d.setTransform(oldTransform); return NO_SUCH_PAGE; } // Check if we need more than one page if (page>0) { g2d.translate(-(shownWidth*pagex),0); g2d.translate(0,-(shownHeight*pagey)); } Vector<LayerDesc> ol=cc.dmp.getLayers(); // Check if only one layer should be printed. if(currentLayerSelected>=0) { cc.dmp.drawOnlyLayer=currentLayerSelected; } // Check if the drawing should be black and white if(printBlackWhite) { Vector<LayerDesc> v=new Vector<LayerDesc>(); // Here we create an alternative array of layers in // which all colors are pitch black. This may be // useful for PCB's. for (int i=0; i<LayerDesc.MAX_LAYERS;++i) v.add(new LayerDesc(new ColorSwing(Color.black), ((LayerDesc)ol.get(i)).getVisible(), "B/W",((LayerDesc)ol.get(i)).getAlpha())); cc.dmp.setLayers(v); } Graphics2DSwing graphicSwing = new Graphics2DSwing(g2d); // This is important for taking into account the dashing size graphicSwing.setZoom(m.getXMagnitude()); // Now we perform our rendering cc.drawingAgent.draw(graphicSwing, m); if(currentLayerSelected>=0) { cc.dmp.setDrawOnlyPads(true); cc.drawingAgent.draw(new Graphics2DSwing(g2d), m); cc.dmp.setDrawOnlyPads(false); cc.dmp.drawOnlyLayer=-1; } cc.dmp.setLayers(ol); g2d.setTransform(oldTransform); /* tell the caller that this page is part of the printed document */ return PAGE_EXISTS; } }