/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: PostScript.java * Input/output tool: PostScript output * Written by Steven M. Rubin, Sun Microsystems. * * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. * * Electric(tm) 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. * * Electric(tm) 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 Electric(tm); see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, Mass 02111-1307, USA. */ package com.sun.electric.tool.io.output; import com.sun.electric.database.geometry.EGraphics; import com.sun.electric.database.geometry.ERectangle; import com.sun.electric.database.geometry.Poly; import com.sun.electric.database.geometry.PolyBase; import com.sun.electric.database.hierarchy.Cell; import com.sun.electric.database.hierarchy.Export; import com.sun.electric.database.hierarchy.Library; import com.sun.electric.database.text.Version; import com.sun.electric.database.topology.ArcInst; import com.sun.electric.database.topology.Connection; import com.sun.electric.database.topology.NodeInst; import com.sun.electric.database.topology.PortInst; import com.sun.electric.database.variable.EditWindow0; import com.sun.electric.database.variable.EditWindow_; import com.sun.electric.database.variable.TextDescriptor; import com.sun.electric.database.variable.UserInterface; import com.sun.electric.database.variable.VarContext; import com.sun.electric.database.variable.Variable; import com.sun.electric.technology.Layer; import com.sun.electric.technology.PrimitiveNode; import com.sun.electric.technology.TechPool; import com.sun.electric.technology.Technology; import com.sun.electric.technology.technologies.Generic; import com.sun.electric.tool.Job; import com.sun.electric.tool.io.IOTool; import com.sun.electric.tool.user.GraphicsPreferences; import com.sun.electric.tool.user.User.ColorPrefType; import com.sun.electric.tool.user.ui.LayerVisibility; import com.sun.electric.util.TextUtils; import com.sun.electric.util.math.DBMath; import java.awt.Color; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * This class writes files in PostScript format. */ public class PostScript extends Output { /** scale factor for PostScript */ private static final int PSSCALE = 4; // /** scale factor for PostScript text */ private static final double PSTEXTSCALE = 0.45; /** scale factor for PostScript text */ private static final double PSTEXTSCALE = 0.75; /** size of text in the corner */ private static final int CORNERDATESIZE = 14; /** default text plain font */ private static final String DEFAULTFONT = "Times-Roman"; /** default text bold font */ private static final String DEFAULTFONTBOLD = "Times-Bold"; /** default text italic font */ private static final String DEFAULTFONTITALIC = "Times-Italic"; /** default text bold-italic font */ private static final String DEFAULTFONTBI = "Times-BoldItalic"; /** write macros for dot drawing */ private static final int HEADERDOT = 1; /** write macros for line drawing */ private static final int HEADERLINE = 2; /** write macros for polygon drawing */ private static final int HEADERPOLYGON = 3; /** write macros for filled polygon drawing */ private static final int HEADERFPOLYGON = 4; /** write macros for text drawing */ private static final int HEADERSTRING = 5; /** true if the "dot" header code has been written. */ private boolean putHeaderDot; /** true if the "line" header code has been written. */ private boolean putHeaderLine; /** true if the "polygon" header code has been written. */ private boolean putHeaderPolygon; /** true if the "filled polygon" header code has been written. */ private boolean putHeaderFilledPolygon; /** true if the "string" header code has been written. */ private boolean putHeaderString; /** true to generate color PostScript. */ private boolean psUseColor; /** true to generate merged color PostScript. */ private boolean psUseColorMerge; /** the Cell being written. */ private Cell cell; /** number of patterns emitted so far. */ private int psNumPatternsEmitted; /** list of patterns emitted so far. */ private Map<EGraphics,Integer> patternsEmitted; /** current layer number (-1: do all; 0: cleanup). */ private int currentLayer; /** the last color written out. */ private int lastColor; /** the normal width of lines. */ private int lineWidth; /** matrix from database units to PS units. */ private AffineTransform matrix; /** fake graphics for drawing outlines and text. */ private static EGraphics blackGraphics = new EGraphics(false, false, null, 0, 100,100,100,1.0,true, new int[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}); PostScriptPreferences localPrefs; public static class PostScriptPreferences extends OutputPreferences { /** list of Polys to use instead of cell contents. */ List<PolyBase> override; boolean plotDates = IOTool.isFactoryPlotDate(); int printColorMethod = IOTool.getFactoryPrintColorMethod(); boolean printForPlotter = IOTool.isFactoryPrintForPlotter(); boolean printEncapsulate = IOTool.isFactoryPrintEncapsulated(); double pageWidth = IOTool.getFactoryPrintWidth(); double pageHeight = IOTool.getFactoryPrintHeight(); double printMargin = IOTool.getFactoryPrintMargin(); int printRotation = IOTool.getFactoryPrintRotation(); double printPSLineWidth = IOTool.getFactoryPrintPSLineWidth(); GraphicsPreferences gp; EditWindow0.EditWindowSmall wnd; ERectangle printBounds; Set<Layer> invisibleLayers = new HashSet<Layer>(); boolean isGrid = false; double gridXSpacing, gridYSpacing; boolean showTempNames; PostScriptPreferences(boolean factory, List<PolyBase> override) { super(factory); this.override = override; gp = new GraphicsPreferences(factory); LayerVisibility lv = new LayerVisibility(factory); for (Technology tech: TechPool.getThreadTechPool().values()) { for (Iterator<Layer> it = tech.getLayers(); it.hasNext(); ) { Layer layer = it.next(); if (!lv.isVisible(layer)) invisibleLayers.add(layer); } } if (!factory) fillPrefs(); } private void fillPrefs() { plotDates = IOTool.isPlotDate(); printColorMethod = IOTool.getPrintColorMethod(); printForPlotter = IOTool.isPrintForPlotter(); printEncapsulate = IOTool.isPrintEncapsulated(); pageWidth = IOTool.getPrintWidth(); pageHeight = IOTool.getPrintHeight(); printMargin = IOTool.getPrintMargin(); printRotation = IOTool.getPrintRotation(); printPSLineWidth = IOTool.getPrintPSLineWidth(); UserInterface ui = Job.getUserInterface(); EditWindow_ localWnd = ui.getCurrentEditWindow_(); wnd = new EditWindow0.EditWindowSmall(localWnd); isGrid = localWnd.isGrid(); gridXSpacing = localWnd.getGridXSpacing(); gridYSpacing = localWnd.getGridYSpacing(); // determine the area of interest printBounds = null; if (override != null) { double lX=0, hX=0, lY=0, hY=0; boolean first = true; for(PolyBase poly : override) { Point2D [] points = poly.getPoints(); for(int i=0; i<points.length; i++) { double x = points[i].getX(); double y = points[i].getY(); if (first) { first = false; lX = hX = x; lY = hY = y; } else { if (x < lX) lX = x; if (x > hX) hX = x; if (y < lY) lY = y; if (y > hY) hY = y; } } } printBounds = ERectangle.fromLambda(lX, lY, hX-lX, hY-lY); } else { Cell cell = localWnd.getCell(); printBounds = ERectangle.fromLambda(getAreaToPrint(cell, false, localWnd)); } } public Output doOutput(Cell cell, VarContext context, String filePath) { PostScript out = new PostScript(this, cell); out.writeCellToFile(filePath); return out.finishWrite(); } } /** * PostScript constructor. */ private PostScript(PostScriptPreferences pp, Cell cell) { localPrefs = pp; this.cell = cell; } /** * Internal method for PostScript output. * @param filePath the disk file to create. */ private boolean writeCellToFile(String filePath) { if (localPrefs.printBounds == null) return true; boolean error = false; if (openTextOutputStream(filePath)) error = true; else { // write out the cell if (cell.getView().isTextView()) { // text cell printWriter.println("Library: " + cell.getLibrary().getName() + " Cell: " + cell.noLibDescribe()); if (localPrefs.includeDateAndVersionInOutput) { printWriter.println(" Created: " + TextUtils.formatDate(cell.getCreationDate()) + " Revised: " + TextUtils.formatDate(cell.getRevisionDate())); } printWriter.println("\n\n"); // print the text of the cell Variable var = cell.getVar(Cell.CELL_TEXT_KEY); if (var != null) { String [] strings = (String [])var.getObject(); for(int i=0; i<strings.length; i++) printWriter.println(strings[i]); } } else { // layout/schematics cell if (start()) { scanCircuit(); done(); } } if (closeTextOutputStream()) error = true; } if (!error) { System.out.println(filePath + " written"); } return error; } /** * Method to initialize for writing a cell. * @return false to abort the process. */ private boolean start() { // find the edit window // if (localPrefs.wnd != null && localPrefs.wnd.getCell() != cell) localPrefs.wnd = null; // clear flags that tell whether headers have been included putHeaderDot = false; putHeaderLine = false; putHeaderPolygon = false; putHeaderFilledPolygon = false; putHeaderString = false; // get control options psUseColor = psUseColorMerge = false; switch (localPrefs.printColorMethod) { case 1: // color psUseColor = true; break; case 2: // color stippled psUseColor = true; break; case 3: // color merged psUseColor = psUseColorMerge = true; break; } double pageWid = localPrefs.pageWidth * 75; double pageHei = localPrefs.pageHeight * 75; double pageMarginPS = localPrefs.printMargin * 75; double pageMargin = pageMarginPS; // not right!!! boolean rotatePlot = false; switch (localPrefs.printRotation) { case 1: // rotate 90 degrees rotatePlot = true; break; case 2: // auto-rotate if (((pageHei > pageWid || localPrefs.printForPlotter) && localPrefs.printBounds.getWidth() > localPrefs.printBounds.getHeight()) || (pageWid > pageHei && localPrefs.printBounds.getHeight() > localPrefs.printBounds.getWidth())) rotatePlot = true; break; } // if plotting, compute height from width if (localPrefs.printForPlotter) { if (rotatePlot) { pageHei = pageWid * localPrefs.printBounds.getWidth() / localPrefs.printBounds.getHeight(); } else { pageHei = pageWid * localPrefs.printBounds.getHeight() / localPrefs.printBounds.getWidth(); } } // for pure color plotting, use special merging code if (psUseColorMerge && localPrefs.override == null) { PostScriptColor.psColorPlot(this, cell, localPrefs.printEncapsulate, localPrefs.printForPlotter, pageWid, pageHei, pageMarginPS, localPrefs.wnd); return false; } // PostScript: compute the transformation matrix double cX = localPrefs.printBounds.getCenterX(); double cY = localPrefs.printBounds.getCenterY(); double unitsX = (pageWid-pageMargin*2) * PSSCALE; double unitsY = (pageHei-pageMargin*2) * PSSCALE; if (localPrefs.printEncapsulate) { double scale = IOTool.getPrintEPSScale(cell); if (scale != 0) { unitsX *= scale; unitsY *= scale; } } double i, j; if (localPrefs.printForPlotter) { i = unitsX / localPrefs.printBounds.getWidth(); j = unitsX / localPrefs.printBounds.getHeight(); } else { i = Math.min(unitsX / localPrefs.printBounds.getWidth(), unitsY / localPrefs.printBounds.getHeight()); j = Math.min(unitsX / localPrefs.printBounds.getHeight(), unitsY / localPrefs.printBounds.getWidth()); } if (rotatePlot) i = j; double matrix00 = i; double matrix01 = 0; double matrix10 = 0; double matrix11 = i; double matrix20 = - i * cX + unitsX / 2 + pageMarginPS * PSSCALE; double matrix21; if (localPrefs.printForPlotter) { matrix21 = - i * localPrefs.printBounds.getMinY() + pageMarginPS * PSSCALE; } else { matrix21 = - i * cY + unitsY / 2 + pageMarginPS * PSSCALE; } matrix = new AffineTransform(matrix00, matrix01, matrix10, matrix11, matrix20, matrix21); // write PostScript header if (localPrefs.printEncapsulate) printWriter.println("%!PS-Adobe-2.0 EPSF-2.0"); else printWriter.println("%!PS-Adobe-1.0"); printWriter.println("%%Title: " + cell.describe(false)); if (localPrefs.includeDateAndVersionInOutput) { printWriter.println("%%Creator: Electric VLSI Design System version " + Version.getVersion()); Date now = new Date(); printWriter.println("%%CreationDate: " + TextUtils.formatDate(now)); } else { printWriter.println("%%Creator: Electric VLSI Design System"); } if (localPrefs.printEncapsulate) printWriter.println("%%Pages: 0"); else printWriter.println("%%Pages: 1"); emitCopyright("% ", ""); // transform to PostScript units double bblx = localPrefs.printBounds.getMinX(); double bbhx = localPrefs.printBounds.getMaxX(); double bbly = localPrefs.printBounds.getMinY(); double bbhy = localPrefs.printBounds.getMaxY(); Point2D bbCorner1 = psXform(new Point2D.Double(bblx, bbly)); Point2D bbCorner2 = psXform(new Point2D.Double(bbhx, bbhy)); bblx = bbCorner1.getX(); bbly = bbCorner1.getY(); bbhx = bbCorner2.getX(); bbhy = bbCorner2.getY(); if (rotatePlot) { // fiddle with the bounding box if image rotated on page // (at this point, bounding box coordinates are absolute printer units) double t1 = bblx; double t2 = bbhx; bblx = -bbhy + pageHei * 300 / 75; bbhx = -bbly + pageHei * 300 / 75; bbly = t1 + pageMargin*2 * 300 / 75; // this may not work because "pageMargin" is badly defined bbhy = t2 + pageMargin*2 * 300 / 75; } if (bblx > bbhx) { double s = bblx; bblx = bbhx; bbhx = s; } if (bbly > bbhy) { double s = bbly; bbly = bbhy; bbhy = s; } bblx = bblx / (PSSCALE * 75.0) * 72.0 * (bblx>=0 ? 1 : -1); bbly = bbly / (PSSCALE * 75.0) * 72.0 * (bbly>=0 ? 1 : -1); bbhx = bbhx / (PSSCALE * 75.0) * 72.0 * (bbhx>=0 ? 1 : -1); bbhy = bbhy / (PSSCALE * 75.0) * 72.0 * (bbhy>=0 ? 1 : -1); // Increase the size of the bounding box by one "pixel" to // prevent the edges from being obscured by some drawing tools printWriter.println("%%BoundingBox: " + (int)(bblx-1) + " " + (int)(bbly-1) + " " + (int)(bbhx+1) + " " + (int)(bbhy+1)); printWriter.println("%%DocumentFonts: " + DEFAULTFONT); printWriter.println("%%EndComments"); if (!localPrefs.printEncapsulate) printWriter.println("%%Page: 1 1"); // PostScript: add some debugging info if (cell != null) { Rectangle2D bounds = cell.getBounds(); printWriter.println("% cell dimensions: " + bounds.getWidth() + " wide x " + bounds.getHeight() + " high (database units)"); printWriter.println("% origin: " + bounds.getMinX() + " " + bounds.getMinY()); } // disclaimers if (localPrefs.printEncapsulate) { printWriter.println("% The EPS header should declare a private dictionary."); } else { printWriter.println("% The non-EPS header does not claim conformance to Adobe-2.0"); printWriter.println("% because the structure may not be exactly correct."); } printWriter.println("%"); // set the page size if this is a plotter if (localPrefs.printForPlotter) { printWriter.println("<< /PageSize [" + (int)(pageWid * 72 / 75) + " " + (int)(pageHei * 72 / 75) + "] >> setpagedevice"); } // make the scale be exactly equal to one page pixel printWriter.println("72 " + PSSCALE*75 + " div 72 " + PSSCALE*75 + " div scale"); // set the proper typeface printWriter.println("/DefaultFont /" + DEFAULTFONT + " def"); printWriter.println("/scaleFont {"); printWriter.println(" DefaultFont findfont"); printWriter.println(" exch scalefont setfont} def"); // make the line width proper lineWidth = (int)(PSSCALE/2 * localPrefs.printPSLineWidth); printWriter.println(lineWidth + " setlinewidth"); // make the line ends look right printWriter.println("1 setlinecap"); // rotate the image if requested if (rotatePlot) { if (localPrefs.printForPlotter) { printWriter.println((pageWid/75) + " 300 mul " + ((pageHei-pageWid)/2/75) + " 300 mul translate 90 rotate"); } else { printWriter.println((pageHei+pageWid)/2/75 + " 300 mul " + (pageHei-pageWid)/2/75 + " 300 mul translate 90 rotate"); } } // fill background color if in color mode if (psUseColor) { // color: emit the background color PolyBase poly = new PolyBase(localPrefs.printBounds); setColor(localPrefs.gp.getColor(ColorPrefType.BACKGROUND)); Point2D[] points = poly.getPoints(); putPSHeader(HEADERPOLYGON); printWriter.print("["); for(int k=0; k<points.length; k++) { if (k != 0) printWriter.print(" "); Point2D ps = psXform(points[k]); printWriter.print(TextUtils.formatDouble(ps.getX()) + " " + TextUtils.formatDouble(ps.getY())); } printWriter.println("] Polygon fill"); } // initialize list of EGraphics modules that have been put out patternsEmitted = new HashMap<EGraphics,Integer>(); psNumPatternsEmitted = 0; return true; } /** * Method to clean-up writing a cell. */ private void done() { // draw the grid if requested if (psUseColor) printWriter.println("0 0 0 setrgbcolor"); if (localPrefs.wnd != null && localPrefs.isGrid) { int gridx = (int)localPrefs.gridXSpacing; int gridy = (int)localPrefs.gridYSpacing; int lx = (int)cell.getBounds().getMinX(); int ly = (int)cell.getBounds().getMinY(); int hx = (int)cell.getBounds().getMaxX(); int hy = (int)cell.getBounds().getMaxY(); int gridlx = lx / gridx * gridx; int gridly = ly / gridy * gridy; // adjust to ensure that the first point is inside the range if (gridlx > lx) gridlx -= (gridlx - lx) / gridx * gridx; if (gridly > ly) gridly -= (gridly - ly) / gridy * gridy; while (gridlx < lx) gridlx += gridx; while (gridly < ly) gridly += gridy; // PostScript: write the grid loop double matrix00 = matrix.getScaleX(); double matrix01 = matrix.getShearX(); double matrix10 = matrix.getShearY(); double matrix11 = matrix.getScaleY(); double matrix20 = matrix.getTranslateX(); double matrix21 = matrix.getTranslateY(); printWriter.println(gridlx + " " + gridx + " " + hx); printWriter.println("{"); printWriter.println(" " + gridly + " " + gridy + " " + hy); // x y printWriter.println(" {"); printWriter.println(" dup 3 -1 roll dup dup"); // y y x x x printWriter.println(" 5 1 roll 3 1 roll"); // x y x y x printWriter.println(" " + matrix00 + " mul exch " + matrix10 + " mul add " + matrix20 + " add"); // x y x x' printWriter.println(" 3 1 roll"); // x x' y x printWriter.println(" " + matrix01 + " mul exch " + matrix11 + " mul add " + matrix21 + " add"); // x x' y' printWriter.println(" newpath moveto 0 0 rlineto stroke"); printWriter.println(" } for"); printWriter.println("} for"); } // draw frame if it is there PostScriptFrame pf = new PostScriptFrame(cell, this); pf.renderFrame(); // put out dates if requested if (localPrefs.plotDates) { putPSHeader(HEADERSTRING); printWriter.print("0 " + (2 * CORNERDATESIZE * PSSCALE) + " "); writePSString("Cell: " + cell.describe(false)); printWriter.println(" " + (CORNERDATESIZE * PSSCALE) + " Botleftstring"); printWriter.print("0 " + (CORNERDATESIZE * PSSCALE) + " "); writePSString("Created: " + TextUtils.formatDate(cell.getCreationDate())); printWriter.println(" " + (CORNERDATESIZE * PSSCALE) + " Botleftstring"); printWriter.print("0 0 "); writePSString("Revised: " + TextUtils.formatDate(cell.getRevisionDate())); printWriter.println(" " + (CORNERDATESIZE * PSSCALE) + " Botleftstring"); } printWriter.println("showpage"); printWriter.println("%%Trailer"); } /** * Class for rendering a cell frame to the PostScript. * Extends Cell.FrameDescription and provides hooks for drawing to a Graphics. */ private static class PostScriptFrame extends Cell.FrameDescription { private PostScript writer; /** * Constructor for cell frame rendering. * @param cell the Cell that is having a frame drawn. * @param writer the PostScript object for access to field variables. */ public PostScriptFrame(Cell cell, PostScript writer) { super(cell, 0); this.writer = writer; } /** * Method to draw a line in a frame. * @param from the starting point of the line (in database units). * @param to the ending point of the line (in database units). */ public void showFrameLine(Point2D from, Point2D to) { writer.psLine(from, to, 0); } /** * Method to draw text in a frame. * @param ctr the anchor point of the text. * @param size the size of the text (in database units). * @param maxWid the maximum width of the text (ignored if zero). * @param maxHei the maximum height of the text (ignored if zero). * @param string the text to be displayed. */ public void showFrameText(Point2D ctr, double size, double maxWid, double maxHei, String string) { Poly poly = null; if (maxWid > 0 && maxHei > 0) { poly = new Poly(ctr.getX(), ctr.getY(), maxWid, maxHei); poly.setStyle(Poly.Type.TEXTBOX); } else { Point2D [] points = new Point2D[1]; points[0] = ctr; poly = new Poly(points); poly.setStyle(Poly.Type.TEXTCENT); } poly.setString(string); TextDescriptor td = TextDescriptor.getNodeTextDescriptor().withRelSize(size * 0.75); poly.setTextDescriptor(td); writer.psText(poly); } } /****************************** TRAVERSING THE HIERARCHY ******************************/ /** * Method to write the body of the PostScript. */ private void scanCircuit() { lastColor = -1; if (localPrefs.override != null) { for (PolyBase poly : localPrefs.override) { Point2D [] pts = poly.getPoints(); for(int i=0; i<pts.length; i++) poly.setPoint(i, pts[i].getX(), localPrefs.printBounds.getHeight() - pts[i].getY()); } } // figure out the size of the job for progress display Job.getUserInterface().startProgressDialog("Writing PostScript", null); Job.getUserInterface().setProgressNote("Counting PostScript objects..."); long totalObjects = recurseCircuitLevel(cell, DBMath.MATID, true, false, 0); if (psUseColor) { // color: plot layers in proper order List<Layer> layerList = cell.getTechnology().getLayersSortedByHeight(); for(Layer layer : layerList) { if (localPrefs.invisibleLayers.contains(layer)) continue; Job.getUserInterface().setProgressNote("Writing layer " + layer.getName() + " (" + totalObjects + " objects..."); currentLayer = layer.getIndex() + 1; recurseCircuitLevel(cell, DBMath.MATID, true, true, totalObjects); } currentLayer = 0; Job.getUserInterface().setProgressNote("Writing cell information (" + totalObjects + " objects..."); recurseCircuitLevel(cell, DBMath.MATID, true, true, totalObjects); } else { // gray-scale: just plot it once currentLayer = -1; Job.getUserInterface().setProgressNote("Found " + totalObjects + " PostScript objects..."); recurseCircuitLevel(cell, DBMath.MATID, true, true, totalObjects); } Job.getUserInterface().stopProgressDialog(); } /** * Method to recursively write a Cell to the PostScript file. * @param cell the Cell to write. * @param trans the transformation matrix from the Cell to the top level. * @param topLevel true if this is the top level. * @param real true to really write PostScript (false when counting layers). * @param progressTotal nonzero to display progress (in which case, this is the total). * @return the number of objects processed. */ private int recurseCircuitLevel(Cell cell, AffineTransform trans, boolean topLevel, boolean real, long progressTotal) { int numObjects = 0; if (localPrefs.override != null) { for (PolyBase poly : localPrefs.override) { if (real) psPoly(poly); numObjects++; if (progressTotal != 0 && (numObjects%100) == 0) { long pct = numObjects*100/progressTotal; Job.getUserInterface().setProgressValue((int)pct); } } return numObjects; } // write the nodes for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); ) { NodeInst ni = it.next(); AffineTransform subRot = ni.rotateOut(); subRot.preConcatenate(trans); if (!ni.isCellInstance()) { if (!topLevel) { if (ni.isVisInside()) continue; if (ni.getProto() == Generic.tech().essentialBoundsNode || ni.getProto() == Generic.tech().cellCenterNode) continue; } if (real) { PrimitiveNode prim = (PrimitiveNode)ni.getProto(); Technology tech = prim.getTechnology(); Poly [] polys = tech.getShapeOfNode(ni); for (int i=0; i<polys.length; i++) { polys[i].transform(subRot); psPoly(polys[i]); } } numObjects++; if (progressTotal != 0 && (numObjects%100) == 0) { long pct = numObjects*100/progressTotal; Job.getUserInterface().setProgressValue((int)pct); } } else { // a cell instance Cell subCell = (Cell)ni.getProto(); AffineTransform subTrans = ni.translateOut(); subTrans.preConcatenate(subRot); if (!ni.isExpanded()) { if (real) { Rectangle2D bounds = subCell.getBounds(); Poly poly = new Poly(bounds.getCenterX(), bounds.getCenterY(), ni.getXSize(), ni.getYSize()); poly.transform(subTrans); poly.setStyle(Poly.Type.CLOSED); poly.setLayer(null); poly.setGraphicsOverride(blackGraphics); // poly.setLayer(blackLayer); psPoly(poly); // Only when the instance names flag is on if (localPrefs.gp.isTextVisibilityOn(TextDescriptor.TextType.INSTANCE)) { poly.setStyle(Poly.Type.TEXTBOX); TextDescriptor td = TextDescriptor.getInstanceTextDescriptor().withAbsSize(24); poly.setTextDescriptor(td); poly.setString(ni.getProto().describe(false)); psPoly(poly); } if (topLevel) showCellPorts(ni, trans, null); } numObjects++; if (progressTotal != 0 && (numObjects%100) == 0) { long pct = numObjects*100/progressTotal; Job.getUserInterface().setProgressValue((int)pct); } } else { recurseCircuitLevel(subCell, subTrans, false, real, progressTotal); if (topLevel && real) showCellPorts(ni, trans, Color.BLACK); } } // draw any displayable variables on the node if (/* topLevel && */ real && localPrefs.gp.isTextVisibilityOn(TextDescriptor.TextType.NODE)) { Poly [] textPolys = ni.getDisplayableVariables(localPrefs.wnd, localPrefs.gp.isShowTempNames()); for (int i=0; i<textPolys.length; i++) { textPolys[i].transform(subRot); psPoly(textPolys[i]); } } // draw any exports from the node if (topLevel && localPrefs.gp.isTextVisibilityOn(TextDescriptor.TextType.EXPORT)) { for(Iterator<Export> eIt = ni.getExports(); eIt.hasNext(); ) { Export e = eIt.next(); if (real) { Poly poly = e.getNamePoly(); if (localPrefs.gp.exportDisplayLevel == 2) { // draw port as a cross drawCross(poly.getCenterX(), poly.getCenterY(), false); } else { // draw port as text if (localPrefs.gp.exportDisplayLevel == 1) { // use shorter port name String portName = e.getShortName(); poly.setString(portName); } // rotate the descriptor TextDescriptor descript = poly.getTextDescriptor(); Poly.Type style = descript.getPos().getPolyType(); style = Poly.rotateType(style, ni); poly.setStyle(style); psPoly(poly); } // draw variables on the export Rectangle2D rect = (Rectangle2D)poly.getBounds2D().clone(); Poly[] polys = e.getDisplayableVariables(rect, localPrefs.wnd, true, localPrefs.gp.isShowTempNames()); for (int i=0; i<polys.length; i++) { psPoly(polys[i]); } } numObjects++; if (progressTotal != 0 && (numObjects%100) == 0) { long pct = numObjects*100/progressTotal; Job.getUserInterface().setProgressValue((int)pct); } } } } // write the arcs for (Iterator<ArcInst> it = cell.getArcs(); it.hasNext();) { ArcInst ai = it.next(); if (real) { Technology tech = ai.getProto().getTechnology(); Poly[] polys = tech.getShapeOfArc(ai); for (int i=0; i<polys.length; i++) { polys[i].transform(trans); psPoly(polys[i]); } // draw any displayable variables on the arc if (topLevel && localPrefs.gp.isTextVisibilityOn(TextDescriptor.TextType.ARC)) { Poly[] textPolys = ai.getDisplayableVariables(localPrefs.wnd, localPrefs.gp.isShowTempNames()); for (int i=0; i<textPolys.length; i++) { textPolys[i].transform(trans); psPoly(textPolys[i]); } } } numObjects++; if (progressTotal != 0 && (numObjects%100) == 0) { long pct = numObjects*100/progressTotal; Job.getUserInterface().setProgressValue((int)pct); } } // show cell variables if at the top level if (topLevel && real && localPrefs.gp.isTextVisibilityOn(TextDescriptor.TextType.CELL)) { // show displayable variables on the instance Rectangle2D CENTERRECT = new Rectangle2D.Double(0, 0, 0, 0); Poly[] polys = cell.getDisplayableVariables(CENTERRECT, localPrefs.wnd, true, localPrefs.gp.isShowTempNames()); for (int i=0; i<polys.length; i++) psPoly(polys[i]); } return numObjects; } private void showCellPorts(NodeInst ni, AffineTransform trans, Color col) { // show the ports that are not further exported or connected int numPorts = ni.getProto().getNumPorts(); boolean[] shownPorts = new boolean[numPorts]; for(Iterator<Connection> it = ni.getConnections(); it.hasNext();) { Connection con = it.next(); PortInst pi = con.getPortInst(); shownPorts[pi.getPortIndex()] = true; } for(Iterator<Export> it = ni.getExports(); it.hasNext();) { Export exp = it.next(); PortInst pi = exp.getOriginalPort(); shownPorts[pi.getPortIndex()] = true; } for(int i = 0; i < numPorts; i++) { if (shownPorts[i]) continue; Export pp = (Export)ni.getProto().getPort(i); Poly portPoly = ni.getShapeOfPort(pp); if (portPoly == null) continue; portPoly.transform(trans); Color portColor = col; if (portColor == null) portColor = pp.getBasePort().getPortColor(localPrefs.gp); setColor(portColor); if (localPrefs.gp.portDisplayLevel == 2) { // draw port as a cross drawCross(portPoly.getCenterX(), portPoly.getCenterY(), false); } else { // draw port as text if (localPrefs.gp.isTextVisibilityOn(TextDescriptor.TextType.PORT)) { // combine all features of port text with color of the port TextDescriptor descript = portPoly.getTextDescriptor(); if (descript == null) descript = TextDescriptor.TextType.EXPORT.getFactoryTextDescriptor();//TextDescriptor.EMPTY; TextDescriptor portDescript = pp.getTextDescriptor(Export.EXPORT_NAME).withColorIndex(descript.getColorIndex()); Poly.Type type = descript.getPos().getPolyType(); portPoly.setStyle(type); String portName = pp.getName(); if (localPrefs.gp.portDisplayLevel == 1) { // use shorter port name portName = pp.getShortName(); } portPoly.setString(portName); portPoly.setTextDescriptor(portDescript); psText(portPoly); } } } } /****************************** EPS SYNCHRONIZATION ******************************/ /** * Method to synchronize all PostScript files that need it. * Examines all of the synchronization paths in all libraries. * If they exist, the user is asked if synchronization should be done. */ public static boolean syncAll() { // see if there are synchronization links boolean syncOther = false; for(Iterator<Library> lIt = Library.getLibraries(); lIt.hasNext(); ) { Library lib = lIt.next(); for(Iterator<Cell> cIt = lib.getCells(); cIt.hasNext(); ) { Cell aCell = cIt.next(); Variable var = aCell.getVar(IOTool.POSTSCRIPT_FILENAME); if (var != null) { String fileName = (String)var.getObject(); if (fileName.trim().length() > 0) { syncOther = true; break; } } } if (syncOther) break; } if (syncOther) { String [] options = {"Yes", "No"}; int ret = Job.getUserInterface().askForChoice("Would you like to synchronize all PostScript drawings?", "Synchronize EPS files", options, options[1]); if (ret == 1) syncOther = false; } if (syncOther) { synchronizeEPSFiles(); return true; } return false; } private static boolean synchronizeEPSFiles() { // synchronize all cells PostScriptPreferences psp = new PostScriptPreferences(false, null); int numSyncs = 0; for(Iterator<Library> lIt = Library.getLibraries(); lIt.hasNext(); ) { Library oLib = lIt.next(); for(Iterator<Cell> cIt = oLib.getCells(); cIt.hasNext(); ) { Cell oCell = cIt.next(); String syncFileName = IOTool.getPrintEPSSynchronizeFile(oCell); if (syncFileName.length() == 0) continue; // existing file: check the date to see if it should be overwritten Date lastSavedDate = IOTool.getPrintEPSSavedDate(oCell); if (lastSavedDate != null) { Date lastChangeDate = oCell.getRevisionDate(); if (lastSavedDate.after(lastChangeDate)) continue; } PostScript out = new PostScript(psp, oCell); boolean err = out.writeCellToFile(syncFileName); if (err) return true; // mark the synchronized date IOTool.setPrintEPSSavedDate(oCell, new Date()); numSyncs++; } } if (numSyncs == 0) System.out.println("No PostScript files needed to be written"); return false; } /****************************** POSTSCRIPT OUTPUT METHODS ******************************/ /** * Method to set the PostScript color. * @param col the color to write. */ private void setColor(Color col) { if (psUseColor) { if (col.getRGB() != lastColor) { lastColor = col.getRGB(); printWriter.println(col.getRed()/255.0f + " " + col.getGreen()/255.0f + " " + col.getBlue()/255.0f + " setrgbcolor"); } } } /** * Method to plot a polygon. * @param poly the polygon to plot. */ private void psPoly(PolyBase poly) { // ignore null layers Layer layer = poly.getLayer(); EGraphics gra = null; int index = 0; Color col = Color.BLACK; Technology tech = cell.getTechnology(); if (layer != null) { tech = layer.getTechnology(); index = layer.getIndex(); if (localPrefs.invisibleLayers.contains(layer)) return; if (poly instanceof Poly) gra = ((Poly)poly).getGraphicsOverride(); if (gra == null) gra = localPrefs.gp.getGraphics(layer); col = gra.getColor(); } // ignore layers that are not supposed to be dumped at this time if (currentLayer >= 0) { if (currentLayer == 0) { if (tech == cell.getTechnology()) return; } else { if (tech != cell.getTechnology() || currentLayer-1 != index) return; } } // set color if requested setColor(col); Poly.Type type = poly.getStyle(); Point2D [] points = poly.getPoints(); if (type == Poly.Type.FILLED) { Rectangle2D polyBox = poly.getBox(); if (polyBox != null) { if (polyBox.getWidth() == 0) { if (polyBox.getHeight() == 0) { psDot(new Point2D.Double(polyBox.getCenterX(), polyBox.getCenterY())); } else { psLine(new Point2D.Double(polyBox.getCenterX(), polyBox.getMinY()), new Point2D.Double(polyBox.getCenterX(), polyBox.getMaxY()), 0); } return; } else if (polyBox.getHeight() == 0) { psLine(new Point2D.Double(polyBox.getMinX(), polyBox.getCenterY()), new Point2D.Double(polyBox.getMaxX(), polyBox.getCenterY()), 0); return; } psPolygon(poly); return; } if (points.length == 1) { psDot(points[0]); return; } if (points.length == 2) { psLine(points[0], points[1], 0); return; } psPolygon(poly); return; } if (type == Poly.Type.CLOSED) { Point2D lastPt = points[points.length-1]; for (int k = 0; k < points.length; k++) { psLine(lastPt, points[k], 0); lastPt = points[k]; } return; } if (type == Poly.Type.OPENED || type == Poly.Type.OPENEDT1 || type == Poly.Type.OPENEDT2 || type == Poly.Type.OPENEDT3) { int lineType = 0; if (type == Poly.Type.OPENEDT1) lineType = 1; else if (type == Poly.Type.OPENEDT2) lineType = 2; else if (type == Poly.Type.OPENEDT3) lineType = 3; for (int k = 1; k < points.length; k++) psLine(points[k-1], points[k], lineType); return; } if (type == Poly.Type.VECTORS) { for(int k=0; k<points.length; k += 2) psLine(points[k], points[k+1], 0); return; } if (type == Poly.Type.CROSS || type == Poly.Type.BIGCROSS) { double x = poly.getCenterX(); double y = poly.getCenterY(); drawCross(x, y, type == Poly.Type.BIGCROSS); return; } if (type == Poly.Type.CROSSED) { Rectangle2D bounds = poly.getBounds2D(); psLine(new Point2D.Double(bounds.getMinX(), bounds.getMinY()), new Point2D.Double(bounds.getMinX(), bounds.getMaxY()), 0); psLine(new Point2D.Double(bounds.getMinX(), bounds.getMaxY()), new Point2D.Double(bounds.getMaxX(), bounds.getMaxY()), 0); psLine(new Point2D.Double(bounds.getMaxX(), bounds.getMaxY()), new Point2D.Double(bounds.getMaxX(), bounds.getMinY()), 0); psLine(new Point2D.Double(bounds.getMaxX(), bounds.getMinY()), new Point2D.Double(bounds.getMinX(), bounds.getMinY()), 0); psLine(new Point2D.Double(bounds.getMinX(), bounds.getMinY()), new Point2D.Double(bounds.getMaxX(), bounds.getMaxY()), 0); psLine(new Point2D.Double(bounds.getMinX(), bounds.getMaxY()), new Point2D.Double(bounds.getMaxX(), bounds.getMinY()), 0); return; } if (type == Poly.Type.DISC) { psDisc(points[0], points[1]); type = Poly.Type.CIRCLE; } if (type == Poly.Type.CIRCLE || type == Poly.Type.THICKCIRCLE) { psCircle(points[0], points[1]); return; } if (type == Poly.Type.CIRCLEARC || type == Poly.Type.THICKCIRCLEARC) { psArc(points[0], points[1], points[2]); return; } // text psText((Poly)poly); } /** * Method to draw a cross. * @param x X center of the cross. * @param y Y Center of the cross. * @param bigCross true for a big cross, false for a small one */ private void drawCross(double x, double y, boolean bigCross) { double amount = 0.25; if (bigCross) amount = 0.5; psLine(new Point2D.Double(x-amount, y), new Point2D.Double(x+amount, y), 0); psLine(new Point2D.Double(x, y+amount), new Point2D.Double(x, y-amount), 0); } /** * Method to draw a dot * @param pt the center of the dot. */ private void psDot(Point2D pt) { Point2D ps = psXform(pt); putPSHeader(HEADERDOT); printWriter.println(TextUtils.formatDouble(ps.getX()) + " " + TextUtils.formatDouble(ps.getY()) + " Putdot"); } /** * Method to draw a line. * @param from the starting point of the line. * @param to the ending point of the line. * @param pattern the line texture (0 for solid, positive for dot/dash patterns). */ private void psLine(Point2D from, Point2D to, int pattern) { Point2D pt1 = psXform(from); Point2D pt2 = psXform(to); putPSHeader(HEADERLINE); int i = PSSCALE / 2; switch (pattern) { case 0: printWriter.println(TextUtils.formatDouble(pt1.getX()) + " " + TextUtils.formatDouble(pt1.getY()) + " " + TextUtils.formatDouble(pt2.getX()) + " " + TextUtils.formatDouble(pt2.getY()) + " Drawline"); break; case 1: printWriter.print("[" + i + " " + i*3 + "] 0 setdash "); printWriter.println(TextUtils.formatDouble(pt1.getX()) + " " + TextUtils.formatDouble(pt1.getY()) + " " + TextUtils.formatDouble(pt2.getX()) + " " + TextUtils.formatDouble(pt2.getY()) + " Drawline"); printWriter.println(" [] 0 setdash"); break; case 2: printWriter.print("[" + i*6 + " " + i*3 + "] 0 setdash "); printWriter.println(TextUtils.formatDouble(pt1.getX()) + " " + TextUtils.formatDouble(pt1.getY()) + " " + TextUtils.formatDouble(pt2.getX()) + " " + TextUtils.formatDouble(pt2.getY()) + " Drawline"); printWriter.println(" [] 0 setdash"); break; case 3: printWriter.print((lineWidth*2) + " setlinewidth "); printWriter.println(TextUtils.formatDouble(pt1.getX()) + " " + TextUtils.formatDouble(pt1.getY()) + " " + TextUtils.formatDouble(pt2.getX()) + " " + TextUtils.formatDouble(pt2.getY()) + " Drawline"); printWriter.println(lineWidth + " setlinewidth"); break; } } /** * Method to draw an arc of a circle. * @param center the center of the arc's circle. * @param pt1 the starting point of the arc. * @param pt2 the ending point of the arc. */ private void psArc(Point2D center, Point2D pt1, Point2D pt2) { Point2D pc = psXform(center); Point2D ps1 = psXform(pt1); Point2D ps2 = psXform(pt2); double radius = pc.distance(ps1); int startAngle = (DBMath.figureAngle(pc, ps2) + 5) / 10; int endAngle = (DBMath.figureAngle(pc, ps1) + 5) / 10; printWriter.println("newpath " + TextUtils.formatDouble(pc.getX()) + " " + TextUtils.formatDouble(pc.getY()) + " " + radius + " " + startAngle + " " + endAngle + " arc stroke"); } /** * Method to draw an unfilled circle. * @param center the center of the circle. * @param pt a point on the circle. */ private void psCircle(Point2D center, Point2D pt) { Point2D pc = psXform(center); Point2D ps = psXform(pt); double radius = pc.distance(ps); printWriter.println("newpath " + TextUtils.formatDouble(pc.getX()) + " " + TextUtils.formatDouble(pc.getY()) + " " + radius + " 0 360 arc stroke"); } /** * Method to draw a filled circle. * @param center the center of the circle. * @param pt a point on the circle. */ private void psDisc(Point2D center, Point2D pt) { Point2D pc = psXform(center); Point2D ps = psXform(pt); double radius = pc.distance(ps); printWriter.println("newpath " + TextUtils.formatDouble(pc.getX()) + " " + TextUtils.formatDouble(pc.getY()) + " " + radius + " 0 360 arc fill"); } /** * Method to draw an irregular polygon. * @param poly the polygon to draw. */ private void psPolygon(PolyBase poly) { Point2D [] points = poly.getPoints(); if (points.length == 0) return; // // ignore if too small // Rectangle2D polyBounds = null; // for(int i=0; i<points.length; i++) // { // Point2D pu = psXform(points[i]); // if (polyBounds == null) polyBounds = new Rectangle2D.Double(pu.getX(), pu.getY(), 0, 0); else // polyBounds.add(pu); // } // if (polyBounds.getWidth() < 1 || polyBounds.getHeight() < 1) return; EGraphics desc = null; if (poly instanceof Poly) desc = ((Poly)poly).getGraphicsOverride(); if (desc == null) desc = localPrefs.gp.getGraphics(poly.getLayer()); // use solid color if solid pattern or no pattern boolean stipplePattern = desc.isPatternedOnPrinter(); // if stipple pattern is solid, just use solid fill if (stipplePattern) { int [] pattern = desc.getPattern(); boolean solid = true; for(int i=0; i<8; i++) if (pattern[i] != 0xFFFF) { solid = false; break; } if (solid) stipplePattern = false; } // put out solid fill if appropriate if (!stipplePattern) { putPSHeader(HEADERPOLYGON); printWriter.print("["); for(int i=0; i<points.length; i++) { if (i != 0) printWriter.print(" "); Point2D ps = psXform(points[i]); printWriter.print(TextUtils.formatDouble(ps.getX()) + " " + TextUtils.formatDouble(ps.getY())); } printWriter.println("] Polygon fill"); return; } /* * patterned fill: the hard one * Generate filled polygons by defining a stipple font and then tiling the * polygon to fill with 128x128 pixel characters, clipping to the polygon edge. */ putPSHeader(HEADERPOLYGON); putPSHeader(HEADERFPOLYGON); printWriter.print("(" + psPattern(desc) + ") ["); Point2D ps = psXform(points[0]); double lx = ps.getX(); double hx = lx; double ly = ps.getY(); double hy = ly; for(int i=0; i<points.length; i++) { if (i != 0) printWriter.print(" "); Point2D psi = psXform(points[i]); printWriter.print(psi.getX() + " " + psi.getY()); if (psi.getX() < lx) lx = psi.getX(); if (psi.getX() > hx) hx = psi.getX(); if (psi.getY() < ly) ly = psi.getY(); if (psi.getY() > hy) hy = psi.getY(); } printWriter.println("] " + TextUtils.formatDouble(hx-lx+1) + " " + TextUtils.formatDouble(hy-ly+1) + " " + TextUtils.formatDouble(lx) + " " + TextUtils.formatDouble(ly) + " Filledpolygon"); } /** * Method to draw text. * @param poly the text polygon to draw. */ private void psText(Poly poly) { Poly.Type style = poly.getStyle(); TextDescriptor td = poly.getTextDescriptor(); if (td == null) return; int size = (int)(td.getTrueSize(localPrefs.wnd) * PSTEXTSCALE * PSSCALE); Rectangle2D bounds = poly.getBounds2D(); // get the font size if (size <= 0) return; // make sure the string is valid String text = poly.getString().trim(); if (text.length() == 0) return; // write header information Point2D psL = psXform(new Point2D.Double(bounds.getMinX(), bounds.getMinY())); Point2D psH = psXform(new Point2D.Double(bounds.getMaxX(), bounds.getMaxY())); double cX = (psL.getX() + psH.getX()) / 2; double cY = (psL.getY() + psH.getY()) / 2; double sX = Math.abs(psH.getX() - psL.getX()); double sY = Math.abs(psH.getY() - psL.getY()); putPSHeader(HEADERSTRING); // set text color Color full; int index = td.getColorIndex(); if (index == 0) full = localPrefs.gp.getColor(ColorPrefType.TEXT); else { Color [] map = localPrefs.gp.getColorMap(cell.getTechnology()); full = EGraphics.getColorFromIndex(index, map); } setColor(full); boolean changedFont = false; String faceName = null; int faceNumber = td.getFace(); if (faceNumber != 0) { TextDescriptor.ActiveFont af = TextDescriptor.ActiveFont.findActiveFont(faceNumber); if (af != null) faceName = af.getName(); } if (faceName != null) { String fixedFaceName = faceName.replace(' ', '-'); printWriter.println("/DefaultFont /" + fixedFaceName + " def"); changedFont = true; } else { if (td.isItalic()) { if (td.isBold()) { printWriter.println("/DefaultFont /" + DEFAULTFONTBI + " def"); changedFont = true; } else { printWriter.println("/DefaultFont /" + DEFAULTFONTITALIC + " def"); changedFont = true; } } else if (td.isBold()) { printWriter.println("/DefaultFont /" + DEFAULTFONTBOLD + " def"); changedFont = true; } } if (poly.getStyle() == Poly.Type.TEXTBOX) { printWriter.print(TextUtils.formatDouble(cX) + " " + TextUtils.formatDouble(cY) + " " + TextUtils.formatDouble(sX) + " " + TextUtils.formatDouble(sY) + " "); writePSString(text); printWriter.println(" " + size + " Boxstring"); } else { String opName = null; double x = 0, y = 0; if (style == Poly.Type.TEXTCENT) { x = cX; y = cY; opName = "Centerstring"; } else if (style == Poly.Type.TEXTTOP) { x = cX; y = psH.getY(); opName = "Topstring"; } else if (style == Poly.Type.TEXTBOT) { x = cX; y = psL.getY(); opName = "Botstring"; } else if (style == Poly.Type.TEXTLEFT) { x = psL.getX(); y = cY; opName = "Leftstring"; } else if (style == Poly.Type.TEXTRIGHT) { x = psH.getX(); y = cY; opName = "Rightstring"; } else if (style == Poly.Type.TEXTTOPLEFT) { x = psL.getX(); y = psH.getY(); opName = "Topleftstring"; } else if (style == Poly.Type.TEXTTOPRIGHT) { x = psH.getX(); y = psH.getY(); opName = "Toprightstring"; } else if (style == Poly.Type.TEXTBOTLEFT) { x = psL.getX(); y = psL.getY(); opName = "Botleftstring"; } else if (style == Poly.Type.TEXTBOTRIGHT) { x = psH.getX(); y = psL.getY(); opName = "Botrightstring"; } int xoff = (int)x, yoff = (int)y; double descenderoffset = size / 12; TextDescriptor.Rotation rot = td.getRotation(); if (rot == TextDescriptor.Rotation.ROT0) y += descenderoffset; else if (rot == TextDescriptor.Rotation.ROT90) x -= descenderoffset; else if (rot == TextDescriptor.Rotation.ROT180) y -= descenderoffset; else if (rot == TextDescriptor.Rotation.ROT270) x += descenderoffset; if (rot != TextDescriptor.Rotation.ROT0) { if (rot == TextDescriptor.Rotation.ROT90 || rot == TextDescriptor.Rotation.ROT270) { if (style == Poly.Type.TEXTTOP) opName = "Rightstring"; else if (style == Poly.Type.TEXTBOT) opName = "Leftstring"; else if (style == Poly.Type.TEXTLEFT) opName = "Botstring"; else if (style == Poly.Type.TEXTRIGHT) opName = "Topstring"; else if (style == Poly.Type.TEXTTOPLEFT) opName = "Botrightstring"; else if (style == Poly.Type.TEXTBOTRIGHT) opName = "Topleftstring"; } x = y = 0; if (rot == TextDescriptor.Rotation.ROT90) { // 90 degrees counterclockwise printWriter.println(xoff + " " + yoff + " translate 90 rotate"); } else if (rot == TextDescriptor.Rotation.ROT180) { // 180 degrees printWriter.println(xoff + " " + yoff + " translate 180 rotate"); } else if (rot == TextDescriptor.Rotation.ROT270) { // 90 degrees clockwise printWriter.println(xoff + " " + yoff + " translate 270 rotate"); } } printWriter.print(TextUtils.formatDouble(x) + " " + TextUtils.formatDouble(y) + " "); writePSString(text); printWriter.println(" " + size + " " + opName); if (rot != TextDescriptor.Rotation.ROT0) { if (rot == TextDescriptor.Rotation.ROT90) { // 90 degrees counterclockwise printWriter.println("-90 rotate " + (-xoff) + " " + (-yoff) + " translate"); } else if (rot == TextDescriptor.Rotation.ROT180) { // 180 degrees printWriter.println("-180 rotate " + (-xoff) + " " + (-yoff) + " translate"); } else if (rot == TextDescriptor.Rotation.ROT270) { // 90 degrees clockwise printWriter.println("-270 rotate " + (-xoff) + " " + (-yoff) + " translate"); } } } if (changedFont) { printWriter.println("/DefaultFont /" + DEFAULTFONT + " def"); } } /****************************** SUPPORT ******************************/ private String [] headerDot = { "/Putdot {", // print dot at stack position " newpath moveto 0 0 rlineto stroke} def" }; private String [] headerLine = { "/Drawline {", // draw line on stack " newpath moveto lineto stroke} def" }; private String [] headerPolygon = { "/Polygon {", // put array into path " aload", " length 2 idiv /len exch def", " newpath", " moveto", " len 1 sub {lineto} repeat", " closepath", "} def" }; private String [] headerFilledPolygon = { "/BuildCharDict 10 dict def", // ref Making a User Defined (PostScript Cookbook) "/StippleFont1 7 dict def", "StippleFont1 begin", " /FontType 3 def", " /FontMatrix [1 0 0 1 0 0] def", " /FontBBox [0 0 1 1] def", " /Encoding 256 array def", " 0 1 255 {Encoding exch /.notdef put} for", " /CharacterDefs 40 dict def", " CharacterDefs /.notdef {} put", " /BuildChar", " { BuildCharDict begin", " /char exch def", " /fontdict exch def", " /charname fontdict /Encoding get", " char get def", " /charproc fontdict /CharacterDefs get", " charname get def", " 1 0 0 0 1 1 setcachedevice", " gsave charproc grestore", " end", " } def", "end", "/StippleFont StippleFont1 definefont pop", "/StippleCharYSize 128 def", "/StippleCharXSize StippleCharYSize def", "/Filledpolygon {", " gsave", " /StippleFont findfont StippleCharYSize scalefont setfont", " /LowY exch def /LowX exch def", " /HighY exch LowY add def /HighX exch LowX add def", " Polygon clip", " /Char exch def", " /LowY LowY StippleCharYSize div truncate StippleCharYSize mul def", " /LowX LowX StippleCharXSize div truncate StippleCharXSize mul def", " /HighY HighY StippleCharYSize div 1 add truncate StippleCharYSize mul def", " /HighX HighX StippleCharXSize div 1 add truncate StippleCharXSize mul def", " LowY StippleCharYSize HighY ", " { LowX exch moveto ", " LowX StippleCharXSize HighX ", " { Char show pop ", " } for ", " } for", " grestore", "} def" }; String [] headerString = { /* * Code to do super and subscripts: * * example: * "NORMAL\dSUB\} NORMAL\ uSUP\}" * * will subscript "SUB" and superscript "SUP", so "\d" starts a * subscript, "\ u" starts a superscript, "\}" returns to * normal. Sub-subscripts, and super-superscripts are not * supported. To print a "\", use "\\". * * changes: * * all calls to stringwidth were changed to calls to StringLength, * which returns the same info (assumes non-rotated text), but * takes sub- and super-scripts into account. * all calls to show were changes to calls to StringShow, which * handles sub- and super-scripts. * note that TSize is set to the text height, and is passed to * StringLength and StringShow. */ "/ComStart 92 def", // "\", enter command mode "/ComSub 100 def", // "d", start subscript "/ComSup 117 def", // "u", start superscript "/ComNorm 125 def", // "}", return to normal "/SSSize .70 def", // sub- and super-script size "/SubDy -.20 def", // Dy for sub-script "/SupDy .40 def", // Dy for super-script*/ "/StringShow {", // str size StringShow " /ComMode 0 def", // command mode flag " /TSize exch def", // text size " /TString exch def", // string to draw " /NormY currentpoint exch pop def", // save Y coordinate of string " TSize scaleFont", " TString {", // scan string char by char " /CharCode exch def", // save char " ComMode 1 eq {", " /ComMode 0 def", // command mode " CharCode ComSub eq {", // start subscript " TSize SSSize mul scaleFont", " currentpoint pop NormY TSize SubDy mul add moveto", " } if", " CharCode ComSup eq {", // start superscript " TSize SSSize mul scaleFont", " currentpoint pop NormY TSize SupDy mul add moveto", " } if", " CharCode ComNorm eq {", // end sub- or super-script " TSize scaleFont", " currentpoint pop NormY moveto", " } if", " CharCode ComStart eq {", // print a "\" " ( ) dup 0 CharCode put show", " } if", " }", " {", " CharCode ComStart eq {", " /ComMode 1 def", // enter command mode " }", " {", " ( ) dup 0 CharCode put show", // print char " } ifelse", " } ifelse", " } forall ", "} def", "/StringLength {", // str size StringLength " /ComMode 0 def", // command mode flag " /StrLen 0 def", // total string length " /TSize exch def", // text size " /TString exch def", // string to draw " TSize scaleFont", " TString {", // scan string char by char " /CharCode exch def", // save char " ComMode 1 eq {", " /ComMode 0 def", // command mode " CharCode ComSub eq {", // start subscript " TSize SSSize mul scaleFont", " } if", " CharCode ComSup eq {", // start superscript " TSize SSSize mul scaleFont", " } if", " CharCode ComNorm eq {", // end sub- or super-script " TSize scaleFont", " } if", " CharCode ComStart eq {", // add "\" to length " ( ) dup 0 CharCode put stringwidth pop StrLen add", " /StrLen exch def", " } if", " }", " {", " CharCode ComStart eq {", " /ComMode 1 def", // enter command mode " }", " {", // add char to length " ( ) dup 0 CharCode put stringwidth pop StrLen add", " /StrLen exch def", " } ifelse", " } ifelse", " } forall ", " StrLen 0", // return info like stringwidth "} def", "/Centerstring {", // x y str sca " dup /TSize exch def", // save size " dup scaleFont exch dup TSize StringLength", // x y sca str xw yw " pop 3 -1 roll .5 mul", // x y str xw sca*.5 (was .8) " exch 5 -1 roll exch 2 div sub", // y str sca*.5 x-xw/2 " exch 4 -1 roll exch 2 div sub", // str x-xw/2 y-sca*.5/2 " moveto TSize StringShow", "} def", "/Topstring {", // x y str sca " dup /TSize exch def", // save size " dup scaleFont exch dup TSize StringLength", // x y sca str xw yw " pop 3 -1 roll .5 mul", // x y str xw sca*.5 (was .8) " exch 5 -1 roll exch 2 div sub", // y str sca*.5 x-xw/2 " exch 4 -1 roll exch sub", // str x-xw/2 y-sca*.5 " moveto TSize StringShow", "} def", "/Botstring {", // x y str sca " dup /TSize exch def", // save size " scaleFont dup TSize StringLength pop", // x y str xw " 4 -1 roll exch 2 div sub", // y str x-xw/2 " 3 -1 roll moveto TSize StringShow", "} def", "/Leftstring {", // x y str sca " dup /TSize exch def", // save size " dup scaleFont .4 mul", // x y str sca*.4 " 3 -1 roll exch sub", // x str y-sca*.4 " 3 -1 roll exch", // str x y-sca*.4 " moveto TSize StringShow", "} def", "/Rightstring {", // x y str sca " dup /TSize exch def", // save size " dup scaleFont exch dup TSize StringLength", // x y sca str xw yw " pop 3 -1 roll .4 mul", // x y str xw sca*.4 " exch 5 -1 roll exch sub", // y str sca*.4 x-xw " exch 4 -1 roll exch sub", // str x-xw y-sca*.4 " moveto TSize StringShow", "} def", "/Topleftstring {", // x y str sca " dup /TSize exch def", // save size " dup scaleFont .5 mul", // x y str sca*.5 (was .8) " 3 -1 roll exch sub", // x str y-sca*.5 " 3 -1 roll exch", // str x y-sca*.5 " moveto TSize StringShow", "} def", "/Toprightstring {", // x y str sca " dup /TSize exch def", // save size " dup scaleFont exch dup TSize StringLength", // x y sca str xw yw " pop 3 -1 roll .5 mul", // x y str xw sca*.5 (was .8) " exch 5 -1 roll exch sub", // y str sca*.5 x-xw " exch 4 -1 roll exch sub", // str x-xw y-sca*.5 " moveto TSize StringShow", "} def", "/Botleftstring {", // x y str sca " dup /TSize exch def", // save size " scaleFont 3 1 roll moveto TSize StringShow", "} def", "/Botrightstring {", // x y str sca " dup /TSize exch def", // save size " scaleFont dup TSize StringLength", " pop 4 -1 roll exch", " sub 3 -1 roll", " moveto TSize StringShow", "} def", "/Min {", // leave minimum of top two " dup 3 -1 roll dup", " 3 1 roll gt", " {exch} if pop", "} def", "/Boxstring {", // x y mx my str sca " dup /TSize exch def", // save size " dup scaleFont", // x y mx my str sca " exch dup TSize StringLength pop", // x y mx my sca str xw " 3 -1 roll dup", // x y mx my str xw sca sca " 6 -1 roll mul", // x y my str xw sca sca*mx " 3 -1 roll div", // x y my str sca sca*mx/xw " 4 -1 roll", // x y str sca sca*mx/xw my " Min Min", // x y str minsca " Centerstring", "} def" }; /** * Method to write boilerplate for different types of PostScript objects. * @param which the desired object. */ private void putPSHeader(int which) { switch (which) { case HEADERDOT: if (putHeaderDot) return; putHeaderDot = true; for(int i=0; i<headerDot.length; i++) printWriter.println(headerDot[i]); break; case HEADERLINE: if (putHeaderLine) return; putHeaderLine = true; for(int i=0; i<headerLine.length; i++) printWriter.println(headerLine[i]); break; case HEADERPOLYGON: if (putHeaderPolygon) return; putHeaderPolygon = true; for(int i=0; i<headerPolygon.length; i++) printWriter.println(headerPolygon[i]); break; case HEADERFPOLYGON: if (putHeaderFilledPolygon) return; putHeaderFilledPolygon = true; for(int i=0; i<headerFilledPolygon.length; i++) printWriter.println(headerFilledPolygon[i]); break; case HEADERSTRING: if (putHeaderString) return; putHeaderString = true; for(int i=0; i<headerString.length; i++) printWriter.println(headerString[i]); break; } } /** * Method to write a pattern. * @param col the pattern to write. * @return the letter associated with the pattern. */ private char psPattern(EGraphics col) { // see if this graphics has been seen already Integer index = patternsEmitted.get(col); if (index != null) return (char)('A' + index.intValue()); // add to list patternsEmitted.put(col, new Integer(psNumPatternsEmitted)); int [] raster = col.getPattern(); char indexChar = (char)(psNumPatternsEmitted+'A'); psNumPatternsEmitted++; /* * Generate filled polygons by defining a stipple font, * and then tiling the polygon to fill with 128x128 pixel * characters, clipping to the polygon edge. * * Take Electric's 16x8 bit images, double each bit, * and then output 4 times to get 128 bit wide image. * Double vertically by outputting each row twice. * Note that full vertical size need not be generated, * as PostScript will just reuse the lines until the 128 * size is reached. * * see: "Making a User Defined Font", PostScript Cookbook */ printWriter.println("StippleFont1 begin"); printWriter.println(" Encoding (" + indexChar + ") 0 get /Stipple" + indexChar + " put"); printWriter.println(" CharacterDefs /Stipple" + indexChar + " {"); printWriter.println(" 128 128 true [128 0 0 -128 0 128]"); printWriter.println(" { <"); for(int i=0; i<8; i++) { int bl = raster[i] & 0x00FF; int bh = (raster[i] & 0xFF00) >> 8; int bld = 0, bhd = 0; for (int k=0; k<8; ++k) { bld = (bld << 1); bld |= (bl & 0x1); bld = (bld << 1); bld |= (bl & 0x1); bl = (bl >> 1); bhd = (bhd << 1); bhd |= (bh & 0x1); bhd = (bhd << 1); bhd |= (bh & 0x1); bh = (bh >> 1); } for (int k=0; k<2; k++) { printWriter.print(" "); for(int j=0; j<4; j++) printWriter.print((bhd&0xFFFF) + " " + (bld&0xFFFF) + " "); printWriter.println(); } } printWriter.println(" > } imagemask"); printWriter.println(" } put"); printWriter.println("end"); return indexChar; } /** * Method to convert coordinates for display. * @param pt the Electric coordinates. * @return the PostScript coordinates. */ private Point2D psXform(Point2D pt) { Point2D result = new Point2D.Double(); matrix.transform(pt, result); return result; } /** * Method to write PostScript text. * @param str the text to write in PostScript format. */ public void writePSString(String str) { printWriter.print("("); for(int i=0; i<str.length(); i++) { char ca = str.charAt(i); if (ca == '(' || ca == ')' || ca == '\\') printWriter.print("\\"); printWriter.print(ca); } printWriter.print(")"); } }