mirror of
https://github.com/RTimothyEdwards/magic.git
synced 2026-08-29 09:30:46 +02:00
Guided by CodeQL static code analyser. FileMayNotBeClosed.ql FileMayNeverBeClosed.ql The trick with "if(fp != stdout)" is problematic (to analyser) as technically 'stdout' can be a global pointer that COULD be modified any time, so it might have changed between the fopen() and fclose() calls so the close MAY NEVER occurs (which is problem the analyzer can see). So local state is maintained as a bool which will also clarify to the compiler see the intention without concern for external stdout modification. Some items appear to be out and out leaks when certain commands are use.
1322 lines
35 KiB
C
1322 lines
35 KiB
C
/*
|
|
* plotPS.c --
|
|
*
|
|
* This file contains procedures that generate PS-format files
|
|
* to describe a section of layout.
|
|
*
|
|
* *********************************************************************
|
|
* * Copyright (C) 1985, 1990 Regents of the University of California. *
|
|
* * Permission to use, copy, modify, and distribute this *
|
|
* * software and its documentation for any purpose and without *
|
|
* * fee is hereby granted, provided that the above copyright *
|
|
* * notice appear in all copies. The University of California *
|
|
* * makes no representations about the suitability of this *
|
|
* * software for any purpose. It is provided "as is" without *
|
|
* * express or implied warranty. Export of this software outside *
|
|
* * of the United States of America may require an export license. *
|
|
* *********************************************************************
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/plot/plotPS.c,v 1.3 2010/06/24 12:37:25 tim Exp $";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "utils/magic.h"
|
|
#include "utils/geometry.h"
|
|
#include "tiles/tile.h"
|
|
#include "utils/hash.h"
|
|
#include "database/database.h"
|
|
#include "utils/tech.h"
|
|
#include "utils/malloc.h"
|
|
#include "utils/utils.h"
|
|
#include "windows/windows.h"
|
|
#include "commands/commands.h"
|
|
#include "dbwind/dbwind.h"
|
|
#include "textio/textio.h"
|
|
|
|
/* C99 compat */
|
|
#include "graphics/graphics.h"
|
|
|
|
/* Records of the following type are used to describe how to generate
|
|
* PS output for a particular set of mask layers. Each style
|
|
* describes the PS figures to draw for a particular set of
|
|
* layers. A single layer may participate in several ps styles.
|
|
*/
|
|
|
|
typedef struct psstyle
|
|
{
|
|
TileTypeBitMask grs_layers; /* Layers to plot in this style. */
|
|
int grs_stipple; /* Index of fill to use. */
|
|
int grs_color; /* Index of color to use. */
|
|
struct psstyle *grs_next; /* Next style in chain. */
|
|
} PSStyle;
|
|
|
|
typedef struct pspattern
|
|
{
|
|
int index;
|
|
unsigned long stipple[8];
|
|
struct pspattern *pat_next;
|
|
} PSPattern;
|
|
|
|
typedef struct pscolor
|
|
{
|
|
int index;
|
|
unsigned char color[4];
|
|
struct pscolor *col_next;
|
|
} PSColor;
|
|
|
|
|
|
static PSStyle *plotPSStyles = NULL;
|
|
static PSPattern *plotPSPatterns = NULL;
|
|
static PSColor *plotPSColors = NULL;
|
|
|
|
int delta, xnmargin, ynmargin, xpmargin, ypmargin;
|
|
float fscale;
|
|
|
|
/* Most of the grs_stipple values are PS stipple numbers. However,
|
|
* if a grs_stipple value is less than zero, it means something special.
|
|
* The definitions below give the possible alternatives:
|
|
*
|
|
* CROSS: Draw a thick outline around the tile with
|
|
* a cross through it (used for contacts).
|
|
* BORDER: Same as CROSS, except draw the outline with
|
|
* no cross through it.
|
|
* SOLID: This is the same as a solid stipple but renders
|
|
* much faster.
|
|
*/
|
|
|
|
#define CROSS -1
|
|
#define BORDER -2
|
|
#define SOLID -3
|
|
|
|
/* The definitions below give the integers used for various PS
|
|
* line drawing styles (brushes).
|
|
*/
|
|
|
|
#define PS_THIN 1
|
|
#define PS_MEDIUM 2
|
|
#define PS_THICK 3
|
|
|
|
/* The variables below are used to pass information from the top-level
|
|
* procedure PlotPS down to the lower-level search functions
|
|
* that are invoked for pieces of the layout.
|
|
*/
|
|
|
|
static FILE *file; /* File to use for output. */
|
|
static PSStyle *curStyle; /* Current style being output. */
|
|
static PSColor *curColor; /* Current color being output. */
|
|
static PSPattern *curPattern; /* Current pattern being output. */
|
|
static int curLineWidth; /* Current line width */
|
|
static int curFont; /* Current font */
|
|
static TileTypeBitMask curMask; /* Layers currently being searched: this
|
|
* is the AND of the mask from curStyle and
|
|
* the layers that the user specified.
|
|
*/
|
|
static Rect bbox; /* Bounding box, in root coordinates, of
|
|
* area being plotted.
|
|
*/
|
|
|
|
/* Parameters passed to the plotting process */
|
|
|
|
static char *defaultBoldFont = "/HelveticaBold";
|
|
static char *defaultFont = "/Helvetica";
|
|
char *PlotPSIdFont = NULL;
|
|
char *PlotPSNameFont = NULL;
|
|
char *PlotPSLabelFont = NULL;
|
|
int PlotPSIdSize = 8;
|
|
int PlotPSNameSize = 12;
|
|
int PlotPSLabelSize = 12;
|
|
int PlotPSBoundary = 1; /* Print boundaries around all layers */
|
|
int PlotPSHeight = 792; /* 11 inches * 72 PS units/inch */
|
|
int PlotPSWidth = 612; /* 8.5 inches */
|
|
int PlotPSMargin = 72; /* 1 inch */
|
|
|
|
int curx1, curx2, cury1, cury2; /* Last encountered line */
|
|
int curxbot, curybot, curwidth, curheight; /* Last encountered rectangle */
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* PSReset()
|
|
*
|
|
* Plot optimization: Reset buffered line and rectangle to default values
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
PSReset()
|
|
{
|
|
curxbot = curybot = curwidth = curheight = -2;
|
|
curx1 = curx2 = cury1 = cury2 = -2;
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* PlotPSTechInit --
|
|
*
|
|
* Called once at beginning of technology file read-in to initialize
|
|
* data structures.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Clears out the list of things to plot.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
PlotPSTechInit()
|
|
{
|
|
int i, j;
|
|
PSStyle *style;
|
|
PSColor *color;
|
|
PSPattern *pattern;
|
|
|
|
/* Clear out any old information */
|
|
|
|
for (style = plotPSStyles; style != NULL; style = style->grs_next)
|
|
{
|
|
freeMagic((char *) style);
|
|
}
|
|
plotPSStyles = NULL;
|
|
|
|
for (pattern = plotPSPatterns; pattern != NULL; pattern = pattern->pat_next)
|
|
{
|
|
freeMagic((char *) pattern);
|
|
}
|
|
plotPSPatterns = NULL;
|
|
|
|
for (color = plotPSColors; color != NULL; color = color->col_next)
|
|
{
|
|
freeMagic((char *) color);
|
|
}
|
|
plotPSColors = NULL;
|
|
|
|
if (!PlotPSIdFont)
|
|
StrDup(&PlotPSIdFont, defaultFont);
|
|
if (!PlotPSNameFont)
|
|
StrDup(&PlotPSNameFont, defaultBoldFont);
|
|
if (!PlotPSLabelFont)
|
|
StrDup(&PlotPSLabelFont, defaultFont);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* PlotPSTechLine --
|
|
*
|
|
* This procedure is invoked by the technology module once for
|
|
* each line in the "ps" subsection of the "plot" section
|
|
* of the technology file.
|
|
*
|
|
* Results:
|
|
* Always returns TRUE (otherwise the technology module would
|
|
* abort Magic with a fatal error).
|
|
*
|
|
* Side effects:
|
|
* Builds up the table of PS styles.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
PlotPSTechLine(sectionName, argc, argv)
|
|
char *sectionName; /* Name of this section (unused). */
|
|
int argc; /* Number of arguments on line. */
|
|
char *argv[]; /* Pointers to fields of line. */
|
|
{
|
|
PSStyle *newstyle;
|
|
PSColor *newcolor;
|
|
PSPattern *newpattern;
|
|
int i, color, stipple;
|
|
|
|
if (argc != 9 && argc != 5 && argc != 3)
|
|
{
|
|
TechError("\"ps\" lines must have either 9, 5, or 3 arguments.\n");
|
|
return TRUE;
|
|
}
|
|
|
|
if (argc == 9) /* pattern definition */
|
|
{
|
|
newpattern = (PSPattern *) mallocMagic(sizeof(PSPattern));
|
|
sscanf(argv[0], "%d", &(newpattern->index));
|
|
for(i = 0; i < 8; i++)
|
|
{
|
|
sscanf(argv[1 + i], "%08lx", &(newpattern->stipple[i]));
|
|
}
|
|
newpattern->pat_next = plotPSPatterns;
|
|
plotPSPatterns = newpattern;
|
|
}
|
|
else if (argc == 5) /* color definition */
|
|
{
|
|
int tmpint;
|
|
newcolor = (PSColor *) mallocMagic(sizeof(PSColor));
|
|
sscanf(argv[0], "%d", &(newcolor->index));
|
|
for(i = 0; i < 4; i++)
|
|
{
|
|
sscanf(argv[1 + i], "%d", &tmpint);
|
|
newcolor->color[i] = (unsigned char)(tmpint & 0xff);
|
|
}
|
|
newcolor->col_next = plotPSColors;
|
|
plotPSColors = newcolor;
|
|
}
|
|
else { /* 3 args: layer definition */
|
|
if (!StrIsInt(argv[1]))
|
|
{
|
|
TechError("2nd field must be an integer\n");
|
|
return TRUE;
|
|
}
|
|
color = atoi(argv[1]);
|
|
|
|
if (strcmp(argv[2], "X") == 0)
|
|
stipple = CROSS;
|
|
else if (strcmp(argv[2], "B") == 0)
|
|
stipple = BORDER;
|
|
else if (strcmp(argv[2], "S") == 0)
|
|
stipple = SOLID;
|
|
else
|
|
{
|
|
if (!StrIsInt(argv[2]))
|
|
{
|
|
TechError("3rd field must be an integer or \"S\", \"X\", or \"B\".\n");
|
|
return TRUE;
|
|
}
|
|
stipple = atoi(argv[2]);
|
|
}
|
|
|
|
newstyle = (PSStyle *) mallocMagic(sizeof(PSStyle));
|
|
|
|
DBTechNoisyNameMask(argv[0], &newstyle->grs_layers);
|
|
|
|
/* Replace non-primary contact images with primary images. */
|
|
|
|
for (i = TT_TECHDEPBASE; i < DBNumTypes; i++)
|
|
{
|
|
if TTMaskHasType(&newstyle->grs_layers, i)
|
|
TTMaskSetMask(&newstyle->grs_layers, &DBLayerTypeMaskTbl[i]);
|
|
}
|
|
TTMaskAndMask(&newstyle->grs_layers, &DBUserLayerBits);
|
|
newstyle->grs_stipple = stipple;
|
|
newstyle->grs_color = color;
|
|
newstyle->grs_next = plotPSStyles;
|
|
plotPSStyles = newstyle;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plotPSFlushRect()
|
|
*
|
|
* Plot optimization: Draw last buffered rectangle.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
plotPSFlushRect(style)
|
|
int style;
|
|
{
|
|
if (curwidth > 0)
|
|
{
|
|
if (style == SOLID)
|
|
fprintf(file, "%d %d %d %d ms\n", curxbot, curybot,
|
|
curwidth, curheight);
|
|
else
|
|
fprintf(file, "%d %d %d %d fb\n", curxbot, curybot,
|
|
curxbot + curwidth, curybot + curheight);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plotPSFlushLine()
|
|
*
|
|
* Plot optimization: Draw last buffered line.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
plotPSFlushLine()
|
|
{
|
|
if (cury1 == cury2)
|
|
{
|
|
if (curx1 != curx2) /* true only if nothing is buffered */
|
|
fprintf(file, "%d %d %d hl\n", curx2 - curx1, curx1, cury1);
|
|
}
|
|
else if (curx1 == curx2)
|
|
fprintf(file, "%d %d %d vl\n", cury2 - cury1, curx1, cury1);
|
|
else
|
|
fprintf(file, "%d %d %d %d ml\n", curx1, cury1, curx2, cury2);
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plotPSLine --
|
|
*
|
|
* Outputs a line into the current PS file.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* I/O.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
plotPSLine(p1, p2)
|
|
Point *p1, *p2; /* Endpoints of line, given in root
|
|
* coordinates.
|
|
*/
|
|
{
|
|
int x1, x2, y1, y2, limit, diff;
|
|
bool tmptf;
|
|
|
|
/* Clip the line to the rectangular area being output. First,
|
|
* arrange for the first x-coordinate to be the smaller, then
|
|
* clip against vertical lines at the x-boundaries.
|
|
*/
|
|
|
|
if (p1->p_x <= p2->p_x)
|
|
{
|
|
x1 = p1->p_x - bbox.r_xbot;
|
|
x2 = p2->p_x - bbox.r_xbot;
|
|
y1 = p1->p_y - bbox.r_ybot;
|
|
y2 = p2->p_y - bbox.r_ybot;
|
|
}
|
|
else
|
|
{
|
|
x1 = p2->p_x - bbox.r_xbot;
|
|
x2 = p1->p_x - bbox.r_xbot;
|
|
y1 = p2->p_y - bbox.r_ybot;
|
|
y2 = p1->p_y - bbox.r_ybot;
|
|
}
|
|
limit = bbox.r_xtop - bbox.r_xbot;
|
|
if ((x1 > limit) || (x2 < 0)) return;
|
|
|
|
/* Now clip against horizontal lines at the y-boundaries. */
|
|
|
|
if (y2 < y1)
|
|
{
|
|
float tmp;
|
|
tmp = y2; y2 = y1; y1 = tmp;
|
|
tmp = x2; x2 = x1; x1 = tmp;
|
|
}
|
|
limit = bbox.r_ytop - bbox.r_ybot;
|
|
if ((y1 > limit) || (y2 < 0)) return;
|
|
|
|
/* compare against last output line and merge if possible */
|
|
if (((x1 == x2) && (x1 == curx1) && (x2 == curx2)
|
|
&& ((tmptf = (y1 == cury2)) || (y2 == cury1))))
|
|
{
|
|
if (tmptf) cury2 = y2;
|
|
else cury1 = y1;
|
|
}
|
|
else if (((y1 == y2) && (y1 == cury1) && (y2 == cury2)
|
|
&& ((tmptf = (x1 == curx2)) || (x2 == curx1))))
|
|
{
|
|
if (tmptf) curx2 = x2;
|
|
else curx1 = x1;
|
|
}
|
|
else
|
|
{
|
|
plotPSFlushLine();
|
|
curx1 = x1;
|
|
curx2 = x2;
|
|
cury1 = y1;
|
|
cury2 = y2;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plotPSRect --
|
|
*
|
|
* Outputs PS statements to draw a rectangular area as
|
|
* an outline with a given line style.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Adds information to the current PS file.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
plotPSRect(rect, style)
|
|
Rect *rect; /* Rectangle to be drawn, in root coords. */
|
|
int style;
|
|
{
|
|
int x, y, w, h;
|
|
|
|
/* Output all boxes with any part visible. Depend on PostScript to */
|
|
/* do the clipping of any boxes crossing the plot boundary. */
|
|
|
|
x = rect->r_xbot - bbox.r_xbot;
|
|
if ((x < 0) || (rect->r_xbot > bbox.r_xtop)) return;
|
|
w = rect->r_xtop - rect->r_xbot;
|
|
y = rect->r_ybot - bbox.r_ybot;
|
|
if ((y < 0) || (rect->r_ybot > bbox.r_ytop)) return;
|
|
h = rect->r_ytop - rect->r_ybot;
|
|
|
|
fprintf(file, "%d %d %d %d m%c\n", x, y, w, h, (style == CROSS) ? 'x' :
|
|
(style == SOLID) ? 's' : 'r');
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plotPSPaint --
|
|
*
|
|
* This procedure is invoked once for each paint rectangle in
|
|
* the area being plotted.
|
|
*
|
|
* Results:
|
|
* Always returns 0 to keep the search alive.
|
|
*
|
|
* Side effects:
|
|
* Outputs information for the tile, including stipple for its
|
|
* interior, and a solid line for any portion of the boundary
|
|
* of the tile that is adjacent to a tile NOT in this style.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
plotPSPaint(tile, cxp)
|
|
Tile *tile; /* Tile that's of type to be output. */
|
|
TreeContext *cxp; /* Describes search in progress. */
|
|
{
|
|
Rect tileArea, edge, rootArea;
|
|
int xbot, width, ybot, height;
|
|
Tile *neighbor;
|
|
SearchContext *scx = cxp->tc_scx;
|
|
bool tmptf;
|
|
TileType ntype;
|
|
|
|
/* First transform tile coords to root coords */
|
|
|
|
TiToRect(tile, &tileArea);
|
|
GeoTransRect(&scx->scx_trans, &tileArea, &rootArea);
|
|
|
|
/* See if this tile gets special handling. */
|
|
|
|
if ((curStyle->grs_stipple == CROSS) || (curStyle->grs_stipple == BORDER))
|
|
{
|
|
/* Draw tile as a thick outline with a cross from corner
|
|
* to corner, and skip the rest of this procedure.
|
|
*/
|
|
|
|
Point ul, lr;
|
|
|
|
if (curLineWidth != PS_MEDIUM) {
|
|
fprintf(file, "l2\n");
|
|
curLineWidth = PS_MEDIUM;
|
|
}
|
|
|
|
plotPSRect(&rootArea, curStyle->grs_stipple);
|
|
return 0;
|
|
}
|
|
|
|
/* If this is a triangle, output the last rect and deal with this one */
|
|
/* individually. */
|
|
|
|
if (IsSplit(tile))
|
|
{
|
|
int np, i, j;
|
|
TileType dinfo;
|
|
Point polyp[5];
|
|
|
|
plotPSFlushRect(curStyle->grs_stipple);
|
|
plotPSFlushLine();
|
|
PSReset();
|
|
|
|
/* Side and direction are altered by geometric transformations */
|
|
|
|
dinfo = DBTransformDiagonal(TiGetTypeExact(tile), &scx->scx_trans);
|
|
|
|
/* Use GrClipTriangle() routine to get the n-sided polygon that */
|
|
/* results from clipping a triangle to the clip region. */
|
|
|
|
GrClipTriangle(&rootArea, &bbox, TRUE, dinfo, polyp, &np);
|
|
for (i = 0; i < np; i++)
|
|
{
|
|
polyp[i].p_x -= bbox.r_xbot;
|
|
polyp[i].p_y -= bbox.r_ybot;
|
|
fprintf(file, "%d %d ", polyp[i].p_x, polyp[i].p_y);
|
|
}
|
|
fprintf(file, "%d tb\n", np);
|
|
|
|
if (PlotPSBoundary)
|
|
{
|
|
if (curLineWidth != PS_THIN) {
|
|
fprintf(file, "l1\n");
|
|
curLineWidth = PS_THIN;
|
|
}
|
|
|
|
/* Diagonal is always drawn */
|
|
|
|
for (i = 0; i < np; i++)
|
|
{
|
|
j = (i + 1) % np;
|
|
if (polyp[i].p_x != polyp[j].p_x &&
|
|
polyp[i].p_y != polyp[j].p_y)
|
|
{
|
|
fprintf(file, "%d %d %d %d ml\n", polyp[i].p_x, polyp[i].p_y,
|
|
polyp[j].p_x, polyp[j].p_y);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/* This tile gets "normal" processing (i.e. stippling and outlining).
|
|
* Clip it to the plotting area and output.
|
|
*/
|
|
|
|
GeoClip(&rootArea, &bbox);
|
|
xbot = rootArea.r_xbot - bbox.r_xbot;
|
|
width = rootArea.r_xtop - rootArea.r_xbot;
|
|
ybot = rootArea.r_ybot - bbox.r_ybot;
|
|
height = rootArea.r_ytop - rootArea.r_ybot;
|
|
|
|
/* compare against last output rectangle and merge if possible */
|
|
if ((width == curwidth) && (xbot == curxbot) && ((tmptf = (ybot == curybot
|
|
+ curheight)) || (ybot + height == curybot)))
|
|
{
|
|
curheight += height;
|
|
if (!tmptf) curybot = ybot;
|
|
}
|
|
else if ((height == curheight) && (ybot == curybot) && ((tmptf = (xbot
|
|
== curxbot + curwidth)) || (xbot + width == curxbot)))
|
|
{
|
|
curwidth += width;
|
|
if (!tmptf) curxbot = xbot;
|
|
}
|
|
else
|
|
{
|
|
plotPSFlushRect(curStyle->grs_stipple);
|
|
curheight = height;
|
|
curwidth = width;
|
|
curxbot = xbot;
|
|
curybot = ybot;
|
|
}
|
|
|
|
if (PlotPSBoundary && (curLineWidth != PS_THIN)) {
|
|
fprintf(file, "l1\n");
|
|
curLineWidth = PS_THIN;
|
|
}
|
|
}
|
|
|
|
if (!PlotPSBoundary) return 0; /* No borders */
|
|
|
|
/* Now output lines for any edges between material of the type
|
|
* currently being drawn and material of other types. This is
|
|
* done by searching along the tile's borders for neighbors that
|
|
* have the wrong types. First, search the tile's bottom border
|
|
* (unless it is at infinity).
|
|
*
|
|
* (This code is essentially a duplicate of selRedisplayFunc())
|
|
*/
|
|
|
|
if (IsSplit(tile) && (!(SplitSide(tile) ^ SplitDirection(tile))))
|
|
goto searchleft; /* nothing on bottom of split */
|
|
|
|
if (tileArea.r_ybot > TiPlaneRect.r_ybot)
|
|
{
|
|
edge.r_ybot = edge.r_ytop = tileArea.r_ybot;
|
|
for (neighbor = LB(tile); LEFT(neighbor) < tileArea.r_xtop;
|
|
neighbor = TR(neighbor))
|
|
{
|
|
ntype = TiGetTopType(neighbor);
|
|
if (TTMaskHasType(&curMask, ntype)) continue;
|
|
edge.r_xbot = LEFT(neighbor);
|
|
edge.r_xtop = RIGHT(neighbor);
|
|
if (edge.r_xbot < tileArea.r_xbot) edge.r_xbot = tileArea.r_xbot;
|
|
if (edge.r_xtop > tileArea.r_xtop) edge.r_xtop = tileArea.r_xtop;
|
|
GeoTransRect(&scx->scx_trans, &edge, &rootArea);
|
|
plotPSLine(&rootArea.r_ll, &rootArea.r_ur);
|
|
}
|
|
}
|
|
|
|
/* Now go along the tile's left border, doing the same thing. Ignore
|
|
* edges that are at infinity.
|
|
*/
|
|
|
|
searchleft:
|
|
if (IsSplit(tile) && SplitSide(tile))
|
|
goto searchtop; /* Nothing on left side of tile */
|
|
|
|
if (tileArea.r_xbot > TiPlaneRect.r_xbot)
|
|
{
|
|
edge.r_xbot = edge.r_xtop = tileArea.r_xbot;
|
|
for (neighbor = BL(tile); BOTTOM(neighbor) < tileArea.r_ytop;
|
|
neighbor = RT(neighbor))
|
|
{
|
|
ntype = TiGetRightType(neighbor);
|
|
if (TTMaskHasType(&curMask, ntype)) continue;
|
|
edge.r_ybot = BOTTOM(neighbor);
|
|
edge.r_ytop = TOP(neighbor);
|
|
if (edge.r_ybot < tileArea.r_ybot) edge.r_ybot = tileArea.r_ybot;
|
|
if (edge.r_ytop > tileArea.r_ytop) edge.r_ytop = tileArea.r_ytop;
|
|
GeoTransRect(&scx->scx_trans, &edge, &rootArea);
|
|
plotPSLine(&rootArea.r_ll, &rootArea.r_ur);
|
|
}
|
|
}
|
|
|
|
/* Same thing for the tile's top border. */
|
|
|
|
searchtop:
|
|
if (IsSplit(tile) && (SplitSide(tile) ^ SplitDirection(tile)))
|
|
goto searchright; /* Nothing on top side of tile */
|
|
|
|
if (tileArea.r_ytop < TiPlaneRect.r_ytop)
|
|
{
|
|
edge.r_ybot = edge.r_ytop = tileArea.r_ytop;
|
|
for (neighbor = RT(tile); RIGHT(neighbor) > tileArea.r_xbot;
|
|
neighbor = BL(neighbor))
|
|
{
|
|
ntype = TiGetBottomType(neighbor);
|
|
if (TTMaskHasType(&curMask, ntype)) continue;
|
|
edge.r_xbot = LEFT(neighbor);
|
|
edge.r_xtop = RIGHT(neighbor);
|
|
if (edge.r_xbot < tileArea.r_xbot) edge.r_xbot = tileArea.r_xbot;
|
|
if (edge.r_xtop > tileArea.r_xtop) edge.r_xtop = tileArea.r_xtop;
|
|
GeoTransRect(&scx->scx_trans, &edge, &rootArea);
|
|
plotPSLine(&rootArea.r_ll, &rootArea.r_ur);
|
|
}
|
|
}
|
|
|
|
/* Finally, the right border. */
|
|
|
|
searchright:
|
|
if (IsSplit(tile) && !SplitSide(tile))
|
|
return 0; /* Nothing on right side of tile */
|
|
|
|
if (tileArea.r_xtop < TiPlaneRect.r_xtop)
|
|
{
|
|
edge.r_xbot = edge.r_xtop = tileArea.r_xtop;
|
|
for (neighbor = TR(tile); TOP(neighbor) > tileArea.r_ybot;
|
|
neighbor = LB(neighbor))
|
|
{
|
|
ntype = TiGetLeftType(neighbor);
|
|
if (TTMaskHasType(&curMask, ntype)) continue;
|
|
edge.r_ybot = BOTTOM(neighbor);
|
|
edge.r_ytop = TOP(neighbor);
|
|
if (edge.r_ybot < tileArea.r_ybot) edge.r_ybot = tileArea.r_ybot;
|
|
if (edge.r_ytop > tileArea.r_ytop) edge.r_ytop = tileArea.r_ytop;
|
|
GeoTransRect(&scx->scx_trans, &edge, &rootArea);
|
|
plotPSLine(&rootArea.r_ll, &rootArea.r_ur);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plotPSLabelPosition --
|
|
*
|
|
* Determine the label position, orientation, and approximate bounding box
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
plotPSLabelPosition(scx, label, x, y, p)
|
|
SearchContext *scx; /* Describes state of search when label
|
|
* was found.
|
|
*/
|
|
Label *label; /* Label that was found. */
|
|
int *x; /* returned x position */
|
|
int *y; /* returned y position */
|
|
int *p; /* returned orientation */
|
|
{
|
|
Rect rootArea;
|
|
int pos;
|
|
|
|
/* Mapping from our GEO_xxx positions to PS object types: */
|
|
|
|
static int psPosition[] =
|
|
{
|
|
5, /* CENTCENT */
|
|
1, /* TOPCENT */
|
|
0, /* TOPRIGHT */
|
|
4, /* CENTRIGHT */
|
|
12, /* BOTRIGHT */
|
|
13, /* BOTCENT */
|
|
15, /* BOTLEFT */
|
|
7, /* CENTLEFT */
|
|
3 /* TOPLEFT */
|
|
};
|
|
|
|
GeoTransRect(&scx->scx_trans, &label->lab_rect, &rootArea);
|
|
pos = GeoTransPos(&scx->scx_trans, label->lab_just);
|
|
switch (pos)
|
|
{
|
|
case GEO_NORTH:
|
|
case GEO_NORTHEAST:
|
|
case GEO_NORTHWEST:
|
|
*y = (rootArea.r_ytop - bbox.r_ybot);
|
|
*y += delta;
|
|
break;
|
|
|
|
case GEO_CENTER:
|
|
case GEO_WEST:
|
|
case GEO_EAST:
|
|
*y = (rootArea.r_ytop + rootArea.r_ybot) / 2 - bbox.r_ybot;
|
|
break;
|
|
|
|
case GEO_SOUTH:
|
|
case GEO_SOUTHEAST:
|
|
case GEO_SOUTHWEST:
|
|
*y = (rootArea.r_ybot - bbox.r_ybot);
|
|
*y -= delta;
|
|
break;
|
|
}
|
|
switch (pos)
|
|
{
|
|
case GEO_WEST:
|
|
case GEO_NORTHWEST:
|
|
case GEO_SOUTHWEST:
|
|
*x = (rootArea.r_xbot - bbox.r_xbot);
|
|
*x -= delta;
|
|
break;
|
|
|
|
case GEO_CENTER:
|
|
case GEO_NORTH:
|
|
case GEO_SOUTH:
|
|
*x = (rootArea.r_xtop + rootArea.r_xbot) / 2 - bbox.r_xbot;
|
|
break;
|
|
|
|
case GEO_EAST:
|
|
case GEO_NORTHEAST:
|
|
case GEO_SOUTHEAST:
|
|
*x = (rootArea.r_xtop - bbox.r_xbot);
|
|
*x += delta;
|
|
break;
|
|
}
|
|
*p = psPosition[pos];
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plotPSLabelBounds --
|
|
*
|
|
* Estimate the bounding box extension based on label strings.
|
|
* In reality, we need to know label sizes to compute the scale,
|
|
* and we need to know the scale to compute label sizes. However,
|
|
* in practice, we can only estimate the label size anyway, so we
|
|
* allow for some slop and just wing it.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
#define AVGCHARWIDTH 0.7
|
|
#define CHARHEIGHT 1.4
|
|
|
|
int
|
|
plotPSLabelBounds(scx, label)
|
|
SearchContext *scx; /* Describes state of search when label
|
|
* was found.
|
|
*/
|
|
Label *label; /* Label that was found. */
|
|
{
|
|
int pspos;
|
|
int ls, psxsize, psysize;
|
|
int llx, lly, urx, ury;
|
|
int psdelta = (int)((float)delta / fscale);
|
|
|
|
plotPSLabelPosition(scx, label, &llx, &lly, &pspos);
|
|
urx = (int)((float)(llx - bbox.r_xtop) / fscale);
|
|
ury = (int)((float)(lly - bbox.r_ytop) / fscale);
|
|
llx = (int)((float)(bbox.r_xbot - llx) / fscale);
|
|
lly = (int)((float)(bbox.r_ybot - lly) / fscale);
|
|
ls = strlen(label->lab_text);
|
|
|
|
psxsize = ls * (int)((float)PlotPSLabelSize * AVGCHARWIDTH);
|
|
psysize = (int)((float)PlotPSLabelSize * CHARHEIGHT);
|
|
|
|
switch (pspos) {
|
|
case 0:
|
|
ury += psysize + psdelta;
|
|
urx += psxsize + psdelta;
|
|
break;
|
|
case 4:
|
|
ury += psysize / 2;
|
|
lly += psysize / 2;
|
|
urx += psxsize + psdelta;
|
|
break;
|
|
case 12:
|
|
lly += psysize + psdelta;
|
|
urx += psxsize + psdelta;
|
|
break;
|
|
case 13:
|
|
lly += psysize + psdelta;
|
|
urx += psxsize / 2;
|
|
llx += psxsize / 2;
|
|
break;
|
|
case 15:
|
|
lly += psysize + psdelta;
|
|
llx += psxsize + psdelta;
|
|
break;
|
|
case 7:
|
|
ury += psysize / 2;
|
|
lly += psysize / 2;
|
|
llx += psxsize + psdelta;
|
|
break;
|
|
case 3:
|
|
ury += psysize + psdelta;
|
|
llx += psxsize + psdelta;
|
|
break;
|
|
case 1:
|
|
ury += psysize + psdelta;
|
|
urx += psxsize / 2;
|
|
llx += psxsize / 2;
|
|
break;
|
|
case 5:
|
|
ury += psysize / 2;
|
|
lly += psysize / 2;
|
|
urx += psxsize / 2;
|
|
llx += psxsize / 2;
|
|
break;
|
|
}
|
|
|
|
if (xpmargin < urx) xpmargin = urx;
|
|
if (ypmargin < ury) ypmargin = ury;
|
|
if (xnmargin < llx) xnmargin = llx;
|
|
if (ynmargin < lly) ynmargin = lly;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plotPSLabelBox --
|
|
*
|
|
* Output the box connected to a label
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
plotPSLabelBox(scx, label)
|
|
SearchContext *scx; /* Describes state of search when label
|
|
* was found.
|
|
*/
|
|
Label *label; /* Label that was found. */
|
|
{
|
|
Rect rootArea;
|
|
int x, y;
|
|
|
|
GeoTransRect(&scx->scx_trans, &label->lab_rect, &rootArea);
|
|
|
|
/* Output lines marking the label's area. Different things are
|
|
* done depending on whether the label is a point, a line, or an
|
|
* area.
|
|
*/
|
|
|
|
if (curLineWidth != PS_MEDIUM) {
|
|
fprintf(file, "l2\n");
|
|
curLineWidth = PS_MEDIUM;
|
|
}
|
|
|
|
if ((rootArea.r_xbot == rootArea.r_xtop) &&
|
|
(rootArea.r_ybot == rootArea.r_ytop))
|
|
{
|
|
/* Point label. Output a cross. */
|
|
|
|
x = (rootArea.r_xbot - bbox.r_xbot);
|
|
y = (rootArea.r_ybot - bbox.r_ybot);
|
|
fprintf(file, "%d %d %d pl\n", delta, x, y);
|
|
}
|
|
else if ((rootArea.r_xbot == rootArea.r_xtop) ||
|
|
(rootArea.r_ybot == rootArea.r_ytop))
|
|
{
|
|
/* Line label. Just draw a medium-thickness line. */
|
|
|
|
plotPSLine(&rootArea.r_ll, &rootArea.r_ur);
|
|
}
|
|
else
|
|
{
|
|
/* Rectangular. Draw lines around the boundary. */
|
|
|
|
plotPSRect(&rootArea, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plotPSLabel --
|
|
*
|
|
* This procedure is invoked once for each label overlapping the
|
|
* area being plotted. It generates PS output to describe
|
|
* the label.
|
|
*
|
|
* Results:
|
|
* Always returns 0 to keep the search from aborting.
|
|
*
|
|
* Side effects:
|
|
* PS information is output.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
plotPSLabel(scx, label)
|
|
SearchContext *scx; /* Describes state of search when label
|
|
* was found.
|
|
*/
|
|
Label *label; /* Label that was found. */
|
|
{
|
|
int x, y;
|
|
int pspos;
|
|
|
|
plotPSLabelPosition(scx, label, &x, &y, &pspos);
|
|
|
|
/* Output the text for the label, if the label is within delta
|
|
* of the area we're plotting (a large label could overlap a
|
|
* bit of the area but stick out way off-screen too).
|
|
*/
|
|
|
|
if ((x >= -delta) && (y >= -delta) &&
|
|
(x <= (bbox.r_xtop - bbox.r_xbot) + delta) &&
|
|
(y <= (bbox.r_ytop - bbox.r_ybot) + delta))
|
|
{
|
|
fprintf(file, "(%s) %d %d %d lb\n", label->lab_text, pspos, x, y);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plotPSCell --
|
|
*
|
|
* This procedure is invoked once for each unexpanded cell that
|
|
* overlaps the area being plotted.
|
|
*
|
|
* Results:
|
|
* Always returns 0 to keep the search from aborting.
|
|
*
|
|
* Side effects:
|
|
* PS information is output to describe the cell.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
plotPSCell(scx)
|
|
SearchContext *scx; /* Describes cell whose bbox is to
|
|
* be plotted.
|
|
*/
|
|
{
|
|
extern bool PlotShowCellNames;
|
|
char idName[100];
|
|
Rect rootArea;
|
|
CellDef *def;
|
|
int x, y;
|
|
|
|
/* Convert the cell's bounding box to root coordinates and then
|
|
* draw as a thick outline.
|
|
*/
|
|
|
|
def = scx->scx_use->cu_def;
|
|
GeoTransRect(&scx->scx_trans, &def->cd_bbox, &rootArea);
|
|
if (curLineWidth != PS_THICK) {
|
|
fprintf(file, "l3\n");
|
|
curLineWidth = PS_THICK;
|
|
}
|
|
plotPSRect(&rootArea, 0);
|
|
|
|
if (!PlotShowCellNames)
|
|
return 0;
|
|
|
|
/* Output the cell definition's name in the top of the bounding box.
|
|
* Use a bold font (#3), in a medium size (#2). Make sure that the
|
|
* name's positioning point is within the area we're plotting.
|
|
*/
|
|
|
|
x = (rootArea.r_xtop + rootArea.r_xbot - 2*bbox.r_xbot)/2;
|
|
y = (2*rootArea.r_ytop + rootArea.r_ybot - 3*bbox.r_ybot)/3;
|
|
if ((x >= 0) && (y >= 0) &&
|
|
(x <= (bbox.r_xtop - bbox.r_xbot)) &&
|
|
(y <= (bbox.r_ytop - bbox.r_ybot)))
|
|
{
|
|
fprintf(file, "f2 (%s) 5 %d %d lb\n", def->cd_name, x, y);
|
|
}
|
|
|
|
/* Output the cell id in the bottom of the bounding box.
|
|
* Use an italic font (#2) in a medium size (#2).
|
|
*/
|
|
|
|
x = (rootArea.r_xtop + rootArea.r_xbot - 2*bbox.r_xbot)/2;
|
|
y = (rootArea.r_ytop + 2*rootArea.r_ybot - 3*bbox.r_ybot)/3;
|
|
if ((x >= 0) && (y >= 0) &&
|
|
(x <= (bbox.r_xtop - bbox.r_xbot)) &&
|
|
(y <= (bbox.r_ytop - bbox.r_ybot)))
|
|
{
|
|
(void) DBPrintUseId(scx, idName, 100, TRUE);
|
|
fprintf(file, "f3 (%s) 5 %d %d lb\n", idName, x, y);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* PlotPS --
|
|
*
|
|
* This procedure generates a PS file to describe an area of
|
|
* a layout.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
PlotPS(fileName, scx, layers, xMask)
|
|
char *fileName; /* Name of PS file to write. */
|
|
SearchContext *scx; /* The use and area and transformation
|
|
* in this describe what to plot.
|
|
*/
|
|
TileTypeBitMask *layers; /* Tells what layers to plot. Only
|
|
* paint layers in this mask, and also
|
|
* expanded according to xMask, are
|
|
* plotted. If L_LABELS is set, then
|
|
* labels on the layers are also
|
|
* plotted, if expanded according to
|
|
* xMask. If L_CELL is set, then
|
|
* subcells that are unexpanded
|
|
* according to xMask are plotted as
|
|
* bounding boxes.
|
|
*/
|
|
int xMask; /* An expansion mask, used to indicate
|
|
* the window whose expansion status
|
|
* will be used to determine
|
|
* visibility. Zero means treat
|
|
* everything as expanded.
|
|
*/
|
|
{
|
|
int xsize, ysize;
|
|
float yscale;
|
|
int i, j;
|
|
int twidth, theight;
|
|
char *fontptr, *fptr2, *fptr3;
|
|
char line_in[100];
|
|
|
|
PSReset();
|
|
|
|
/* Compute a scale factor between our coordinates and PS
|
|
* coordinates.
|
|
*/
|
|
|
|
GeoTransRect(&scx->scx_trans, &scx->scx_area, &bbox);
|
|
xsize = bbox.r_xtop - bbox.r_xbot;
|
|
ysize = bbox.r_ytop - bbox.r_ybot;
|
|
fscale = (float)(PlotPSWidth - 2 * PlotPSMargin) / (float)xsize;
|
|
yscale = (float)(PlotPSHeight - 2 * PlotPSMargin) / (float)ysize;
|
|
if (yscale < fscale) fscale = yscale;
|
|
|
|
/* Compute a distance equal to 1/8th the size of a typical wire
|
|
* (max of thicknesses of routing layers). This is used to offset
|
|
* text from labels and to compute cross size for point labels.
|
|
*/
|
|
|
|
if (RtrMetalWidth > RtrPolyWidth)
|
|
delta = RtrMetalWidth / 8;
|
|
else delta = RtrPolyWidth / 8;
|
|
if (delta == 0) delta = 1;
|
|
|
|
/* Go through labels once to estimate the bounding box, including labels */
|
|
|
|
xnmargin = ynmargin = xpmargin = ypmargin = 0;
|
|
if (TTMaskHasType(layers, L_LABEL))
|
|
{
|
|
curMask = *layers;
|
|
TTMaskSetType(&curMask, TT_SPACE);
|
|
(void) DBTreeSrLabels(scx, &curMask, xMask, (TerminalPath *) NULL,
|
|
TF_LABEL_ATTACH, plotPSLabelBounds, (ClientData) NULL);
|
|
fscale = (float)(PlotPSWidth - 2 * PlotPSMargin - xnmargin - xpmargin)
|
|
/ (float)(xsize);
|
|
yscale = (float)(PlotPSHeight - 2 * PlotPSMargin - ynmargin - ypmargin)
|
|
/ (float)(ysize);
|
|
if (yscale < fscale) fscale = yscale;
|
|
}
|
|
twidth = (xsize * fscale) + xnmargin + xpmargin;
|
|
theight = (ysize * fscale) + ynmargin + ypmargin;
|
|
|
|
/* Open the PS file and output header information. */
|
|
|
|
file = PaOpen(fileName, "w", (char *) NULL, ".", (char *) NULL,
|
|
(char **) NULL);
|
|
if (file == NULL)
|
|
{
|
|
TxError("Couldn't write PS file \"%s\".\n", fileName);
|
|
return;
|
|
}
|
|
fprintf(file, "%%!PS-Adobe-3.0 EPSF-3.0\n");
|
|
fprintf(file, "%%%%BoundingBox: %d %d %d %d\n",
|
|
PlotPSMargin, PlotPSMargin, twidth + PlotPSMargin,
|
|
theight + PlotPSMargin);
|
|
fontptr = PlotPSIdFont;
|
|
fprintf(file, "%%%%DocumentNeededResources: font %s", fontptr);
|
|
if (!Match(fptr2 = PlotPSNameFont, fontptr))
|
|
fprintf(file, " font %s", fptr2);
|
|
if (!Match(fptr3 = PlotPSLabelFont, fontptr))
|
|
if (!Match(fptr3, fptr2))
|
|
fprintf(file, " font %s", fptr3);
|
|
fprintf(file, "\n");
|
|
fprintf(file, "%%%%EndComments\n");
|
|
|
|
/* Insert the prolog here */
|
|
|
|
{
|
|
FILE *infile = PaOpen("magicps", "r", ".pro", ".", SysLibPath, NULL);
|
|
if (infile != NULL)
|
|
{
|
|
while(fgets(line_in, 99, infile) != NULL)
|
|
fputs(line_in, file);
|
|
fclose(infile);
|
|
}
|
|
else
|
|
fprintf(file, "\npostscript_prolog_is_missing\n\n");
|
|
}
|
|
|
|
/* Insert the font definitions here. */
|
|
|
|
fprintf(file, "/f1 { %.3f %s sf } def\n", (float)PlotPSLabelSize / fscale,
|
|
PlotPSLabelFont);
|
|
fprintf(file, "/f2 { %.3f %s sf } def\n", (float)PlotPSNameSize / fscale,
|
|
PlotPSNameFont);
|
|
fprintf(file, "/f3 { %.3f %s sf } def\n", (float)PlotPSIdSize / fscale,
|
|
PlotPSIdFont);
|
|
|
|
/* Insert the color and stipple definitions here. */
|
|
|
|
for (curColor = plotPSColors; curColor != NULL;
|
|
curColor = curColor->col_next)
|
|
{
|
|
fprintf(file, "/col%d {%.3f %.3f %.3f %.3f sc} bind def\n",
|
|
curColor->index,
|
|
(float)curColor->color[0] / 255.0,
|
|
(float)curColor->color[1] / 255.0,
|
|
(float)curColor->color[2] / 255.0,
|
|
(float)curColor->color[3] / 255.0);
|
|
}
|
|
|
|
for (curPattern = plotPSPatterns; curPattern != NULL;
|
|
curPattern = curPattern->pat_next)
|
|
{
|
|
fprintf(file, "{<");
|
|
for (j = 0; j < 8; j++)
|
|
fprintf(file, "%08lx%08lx", curPattern->stipple[j], curPattern->stipple[j]);
|
|
fprintf(file, ">} %d dp\n", curPattern->index);
|
|
}
|
|
|
|
fprintf(file, "%%%%EndResource\n%%%%EndProlog\n\n");
|
|
fprintf(file, "%%%%Page: 1 1\n");
|
|
fprintf(file, "/pgsave save def bop\n");
|
|
fprintf(file, "%% 0 0 offsets\nninit\n");
|
|
fprintf(file, "%d %d translate\n", PlotPSMargin + xnmargin, PlotPSMargin
|
|
+ ynmargin);
|
|
fprintf(file, "%.3f %.3f scale\nminit\n", fscale, fscale);
|
|
fprintf(file, "0 0 %d %d gsave rectclip\n", xsize, ysize);
|
|
fprintf(file, "l2\nsp\n\n");
|
|
|
|
curLineWidth = PS_MEDIUM;
|
|
|
|
/* For each PS style, find all the paint layers that belong
|
|
* to that style and put plot information into the file.
|
|
*/
|
|
|
|
for (curStyle = plotPSStyles; curStyle != NULL;
|
|
curStyle = curStyle->grs_next)
|
|
{
|
|
fprintf(file, "col%d\n", curStyle->grs_color);
|
|
if (curStyle->grs_stipple >= 0)
|
|
fprintf(file, "%d sl\n", curStyle->grs_stipple);
|
|
TTMaskAndMask3(&curMask, layers, &curStyle->grs_layers);
|
|
(void) DBTreeSrTiles(scx, &curMask, xMask, plotPSPaint,
|
|
(ClientData) NULL);
|
|
plotPSFlushRect(curStyle->grs_stipple);
|
|
plotPSFlushLine();
|
|
PSReset();
|
|
}
|
|
|
|
/* Output subcell bounding boxes, if they are wanted. */
|
|
|
|
if (TTMaskHasType(layers, L_CELL))
|
|
{
|
|
(void) DBTreeSrCells(scx, xMask, plotPSCell, (ClientData) NULL);
|
|
plotPSFlushRect(BORDER);
|
|
plotPSFlushLine();
|
|
}
|
|
|
|
/* Output label boxes followed by labels */
|
|
|
|
if (TTMaskHasType(layers, L_LABEL))
|
|
{
|
|
curMask = *layers;
|
|
TTMaskSetType(&curMask, TT_SPACE);
|
|
(void) DBTreeSrLabels(scx, &curMask, xMask, (TerminalPath *) NULL,
|
|
TF_LABEL_ATTACH, plotPSLabelBox, (ClientData) NULL);
|
|
plotPSFlushRect(BORDER);
|
|
plotPSFlushLine();
|
|
PSReset();
|
|
fprintf(file, "grestore\n"); /* end clipping rectangle */
|
|
fprintf(file, "f1 0 setgray\n"); /* set font, set color to black */
|
|
|
|
curMask = *layers;
|
|
TTMaskSetType(&curMask, TT_SPACE);
|
|
(void) DBTreeSrLabels(scx, &curMask, xMask, (TerminalPath *) NULL,
|
|
TF_LABEL_ATTACH, plotPSLabel, (ClientData) NULL);
|
|
}
|
|
else
|
|
{
|
|
fprintf(file, "grestore\n"); /* end clipping rectangle */
|
|
}
|
|
|
|
/* Output trailer information into the file, and close it. */
|
|
|
|
fprintf(file, "pgsave restore showpage\n\n");
|
|
fprintf(file, "%%%%Trailer\nMAGICsave restore\n%%%%EOF\n");
|
|
fclose(file);
|
|
return;
|
|
}
|