Files
magic/database/DBlabel.c
T
Tim Edwards 9931244e1e Corrected what appears to be a long-standing error in the "extract
unique" code.  It was using DBEraseLabelsByContent() which would
erase all matching labels, and could potentially erase labels that
were still remaining on the list being processed, causing a segfault.
Also corrected minor errors identified by valgrind during debugging
the above-referenced problem.
2020-11-03 12:17:16 -05:00

1597 lines
45 KiB
C

/*
* DBlabel.c --
*
* Label manipulation primitives.
*
* *********************************************************************
* * 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/database/DBlabel.c,v 1.9 2010/08/25 17:33:55 tim Exp $";
#endif /* not lint */
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h> /* For sin(), cos(), and round() functions */
#include "utils/magic.h"
#include "utils/malloc.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "utils/utils.h"
#include "database/database.h"
#include "database/fonts.h"
#include "database/databaseInt.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "commands/commands.h"
#include "textio/textio.h"
static TileType DBPickLabelLayer(/* CellDef *def, Label *lab, int noreconnect */);
/* Globally-accessible font information */
MagicFont **DBFontList = NULL;
int DBNumFonts = 0;
/*
* ----------------------------------------------------------------------------
*
* DBIsSubcircuit --
*
* Check if any labels in a CellDef declare port attributes, indicating
* that the CellDef should be treated as a subcircuit.
*
* Results:
* TRUE if CellDef contains ports, FALSE otherwise.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
bool
DBIsSubcircuit(cellDef)
CellDef *cellDef;
{
Label *lab;
for (lab = cellDef->cd_labels; lab != NULL; lab = lab->lab_next)
if (lab->lab_flags & PORT_DIR_MASK)
return TRUE;
return FALSE;
}
/*
* ----------------------------------------------------------------------------
*
* DBPutLabel --
*
* Place a rectangular label in the database, in a particular cell.
* This is a wrapper function to the more complete DBPutFontLabel()
* (see below).
*
* ----------------------------------------------------------------------------
*/
Label *
DBPutLabel(cellDef, rect, align, text, type, flags)
CellDef *cellDef;
Rect *rect;
int align;
char *text;
TileType type;
int flags;
{
/* Draw text in a standard X11 font */
return DBPutFontLabel(cellDef, rect, -1, 0, 0, &GeoOrigin,
align, text, type, flags);
}
/*
* ----------------------------------------------------------------------------
*
* DBPutFontLabel --
*
* Place a rectangular label in the database, in a particular cell.
*
* It is the responsibility of higher-level routines to insure that
* the material to which the label is being attached really exists at
* this point in the cell, and that TT_SPACE is used if there is
* no single material covering the label's entire area. The routine
* DBAdjustLabels is useful for this.
*
* Results:
* The return value is the actual alignment position used for
* the label. This may be different from align, if align is
* defaulted.
*
* Side effects:
* Updates the label list in the CellDef to contain the label.
*
* ----------------------------------------------------------------------------
*/
Label *
DBPutFontLabel(cellDef, rect, font, size, rot, offset, align, text, type, flags)
CellDef *cellDef; /* Cell in which label is placed */
Rect *rect; /* Location of label; see above for description */
int font; /* A vector outline font to use, or -1 for X11 font */
int size; /* Scale of vector font relative to the database (x8) */
int rot; /* Rotate of the vector font in degrees */
Point *offset; /* Offset of the font from the point of origin */
int align; /* Orientation/alignment of text. If this is < 0,
* an orientation will be picked to keep the text
* inside the cell boundary.
*/
char *text; /* Pointer to actual text of label */
TileType type; /* Type of tile to be labelled */
int flags; /* Label flags */
{
Label *lab;
int len, x1, x2, y1, y2, tmp, labx, laby;
len = strlen(text) + sizeof (Label) - sizeof lab->lab_text + 1;
lab = (Label *) mallocMagic ((unsigned) len);
strcpy(lab->lab_text, text);
/* Pick a nice alignment if the caller didn't give one. If the
* label is more than BORDER units from an edge of the cell,
* use GEO_NORTH. Otherwise, put the label on the opposite side
* from the boundary, so it won't stick out past the edge of
* the cell boundary.
*/
#define BORDER 5
if (align < 0)
{
tmp = (cellDef->cd_bbox.r_xtop - cellDef->cd_bbox.r_xbot)/3;
if (tmp > BORDER) tmp = BORDER;
x1 = cellDef->cd_bbox.r_xbot + tmp;
x2 = cellDef->cd_bbox.r_xtop - tmp;
tmp = (cellDef->cd_bbox.r_ytop - cellDef->cd_bbox.r_ybot)/3;
if (tmp > BORDER) tmp = BORDER;
y1 = cellDef->cd_bbox.r_ybot + tmp;
y2 = cellDef->cd_bbox.r_ytop - tmp;
labx = (rect->r_xtop + rect->r_xbot)/2;
laby = (rect->r_ytop + rect->r_ybot)/2;
if (labx <= x1)
{
if (laby <= y1) align = GEO_NORTHEAST;
else if (laby >= y2) align = GEO_SOUTHEAST;
else align = GEO_EAST;
}
else if (labx >= x2)
{
if (laby <= y1) align = GEO_NORTHWEST;
else if (laby >= y2) align = GEO_SOUTHWEST;
else align = GEO_WEST;
}
else
{
if (laby <= y1) align = GEO_NORTH;
else if (laby >= y2) align = GEO_SOUTH;
else align = GEO_NORTH;
}
}
lab->lab_just = align;
if (font >= 0 && font < DBNumFonts)
{
lab->lab_font = font;
lab->lab_size = size;
lab->lab_rotate = (short)rot;
lab->lab_offset = *offset;
}
else
{
lab->lab_font = -1;
lab->lab_size = 0;
lab->lab_rotate = (short)0;
lab->lab_offset = GeoOrigin;
}
lab->lab_type = type;
lab->lab_flags = flags;
lab->lab_rect = *rect;
lab->lab_next = NULL;
if (cellDef->cd_labels == NULL)
cellDef->cd_labels = lab;
else
{
ASSERT(cellDef->cd_lastLabel->lab_next == NULL, "DBPutLabel");
cellDef->cd_lastLabel->lab_next = lab;
}
cellDef->cd_lastLabel = lab;
DBFontLabelSetBBox(lab);
DBUndoPutLabel(cellDef, lab);
cellDef->cd_flags |= CDMODIFIED|CDGETNEWSTAMP;
return lab;
}
/*
* ----------------------------------------------------------------------------
*
* DBEraseLabel --
*
* Delete labels attached to tiles of the indicated types that
* are in the given area (as determined by the macro GEO_LABEL_IN_AREA).
* If this procedure is called as part of a command that also modifies paint,
* then the paint modifications should be done BEFORE calling here.
*
* Results:
* TRUE if any labels were deleted, FALSE otherwise.
*
* Side effects:
* This procedure tries to be clever in order to avoid deleting
* labels whenever possible. If there's enough material on the
* label's attached layer so that the label can stay on its
* current layer, or if the label can be migrated to a layer that
* connects to its current layer, then the label is not deleted.
* Deleting up to the edge of a label won't cause the label
* to go away. There's one final exception: if the mask includes
* L_LABEL, then labels are deleted from all layers even if there's
* still enough material to keep them around. The rect pointed to
* by areaReturn is filled with the area affected by removing the
* label, for purposes of redrawing the necessary portions of the
* screen.
*
* ----------------------------------------------------------------------------
*/
bool
DBEraseLabel(cellDef, area, mask, areaReturn)
CellDef *cellDef; /* Cell being modified */
Rect *area; /* Area from which labels are to be erased.
* This may be a point; any labels touching
* or overlapping it are erased.
*/
TileTypeBitMask *mask; /* Mask of types from which labels are to
* be erased.
*/
Rect *areaReturn; /* Expand this with label bounding box */
{
Label *lab, *labPrev;
bool erasedAny = FALSE;
TileType newType;
labPrev = NULL;
lab = cellDef->cd_labels;
while (lab != NULL)
{
if (!GEO_LABEL_IN_AREA(&lab->lab_rect, area)) goto nextLab;
if (!TTMaskHasType(mask, L_LABEL))
{
if (!TTMaskHasType(mask, lab->lab_type)) goto nextLab;
/* Labels on space always get deleted at this point, since
* there's no reasonable new layer to put them on.
*/
if (!(lab->lab_type == TT_SPACE))
{
newType = DBPickLabelLayer(cellDef, lab, 0);
if (DBConnectsTo(newType, lab->lab_type)) goto nextLab;
}
}
DBWLabelChanged(cellDef, lab, DBW_ALLWINDOWS);
if (labPrev == NULL)
cellDef->cd_labels = lab->lab_next;
else labPrev->lab_next = lab->lab_next;
if (cellDef->cd_lastLabel == lab)
cellDef->cd_lastLabel = labPrev;
DBUndoEraseLabel(cellDef, lab);
if ((lab->lab_font >= 0) && areaReturn)
GeoInclude(&lab->lab_bbox, areaReturn);
freeMagic((char *) lab);
lab = lab->lab_next;
erasedAny = TRUE;
continue;
nextLab: labPrev = lab;
lab = lab->lab_next;
}
if (erasedAny)
cellDef->cd_flags |= CDMODIFIED|CDGETNEWSTAMP;
return (erasedAny);
}
#define RECTEQUAL(r1, r2) ((r1)->r_xbot == (r2)->r_xbot \
&& (r1)->r_ybot == (r2)->r_ybot \
&& (r1)->r_xtop == (r2)->r_xtop \
&& (r1)->r_ytop == (r2)->r_ytop)
/*
* ----------------------------------------------------------------------------
*
* DBCheckLabelsByContent --
*
* Return any label found on the label list for the given
* CellDef that matches the given specification.
*
* Results:
* Returns a label if a match is found, otherwise returns NULL.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
Label *
DBCheckLabelsByContent(def, rect, type, text)
CellDef *def; /* Where to look for label to delete. */
Rect *rect; /* Coordinates of label. If NULL, then
* labels are searched regardless of coords.
*/
TileType type; /* Layer label is attached to. If < 0, then
* labels are searched regardless of type.
*/
char *text; /* Text associated with label. If NULL, then
* labels are searched regardless of text.
*/
{
Label *lab;
for (lab = def->cd_labels; lab; lab = lab->lab_next)
{
if ((rect != NULL) && !(RECTEQUAL(&lab->lab_rect, rect))) continue;
if ((type >= 0) && (type != lab->lab_type)) continue;
if ((text != NULL) && (strcmp(text, lab->lab_text) != 0)) continue;
return lab;
}
return NULL;
}
/*
* ----------------------------------------------------------------------------
*
* DBEraseLabelsByContent --
*
* Erase any labels found on the label list for the given
* CellDef that match the given specification.
*
* Results:
* None.
*
* Side effects:
* Modifies the label list for the argument CellDef. The
* DBWind module is notified about any labels that were
* deleted.
*
* ----------------------------------------------------------------------------
*/
void
DBEraseLabelsByContent(def, rect, type, text)
CellDef *def; /* Where to look for label to delete. */
Rect *rect; /* Coordinates of label. If NULL, then
* labels are deleted regardless of coords.
*/
TileType type; /* Layer label is attached to. If < 0, then
* labels are deleted regardless of type.
*/
char *text; /* Text associated with label. If NULL, then
* labels are deleted regardless of text.
*/
{
Label *lab, *labPrev;
for (labPrev = NULL, lab = def->cd_labels;
lab != NULL;
labPrev = lab, lab = lab->lab_next)
{
nextCheck:
if ((rect != NULL) && !(RECTEQUAL(&lab->lab_rect, rect))) continue;
if ((type >= 0) && (type != lab->lab_type)) continue;
if ((text != NULL) && (strcmp(text, lab->lab_text) != 0)) continue;
DBUndoEraseLabel(def, lab);
DBWLabelChanged(def, lab, DBW_ALLWINDOWS);
if (labPrev == NULL)
def->cd_labels = lab->lab_next;
else labPrev->lab_next = lab->lab_next;
if (def->cd_lastLabel == lab)
def->cd_lastLabel = labPrev;
freeMagic((char *) lab);
/* Don't iterate through loop, since this will skip a label:
* just go back to top. This is tricky!
*/
lab = lab->lab_next;
if (lab == NULL) break;
else goto nextCheck;
}
}
/*
* ----------------------------------------------------------------------------
*
* DBRemoveLabel --
*
* Erase a labels by reference.
* TRUE.
*
* Results:
* None.
*
* Side effects:
* Modifies the label list for the argument CellDef. The
* DBWind module is notified about any labels that were
* deleted.
*
* ----------------------------------------------------------------------------
*/
void
DBRemoveLabel(def, refLab)
CellDef *def; /* Where to look for label to delete. */
Label *refLab;
{
Label *lab, *labPrev;
for (labPrev = NULL, lab = def->cd_labels;
lab != NULL;
labPrev = lab, lab = lab->lab_next)
{
nextCheck:
if (lab != refLab) continue;
DBUndoEraseLabel(def, lab);
DBWLabelChanged(def, lab, DBW_ALLWINDOWS);
if (labPrev == NULL)
def->cd_labels = lab->lab_next;
else labPrev->lab_next = lab->lab_next;
if (def->cd_lastLabel == lab)
def->cd_lastLabel = labPrev;
freeMagic((char *) lab);
/* Don't iterate through loop, since this will skip a label:
* just go back to top. This is tricky!
*/
lab = lab->lab_next;
if (lab == NULL) break;
else goto nextCheck;
}
}
/*
* ----------------------------------------------------------------------------
*
* DBReOrientLabel --
*
* Change the text positions of all labels underneath a given
* area in a given cell.
*
* Results:
* None.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
void
DBReOrientLabel(cellDef, area, newPos)
CellDef *cellDef; /* Cell whose labels are to be modified. */
Rect *area; /* All labels touching this area have their
* text positions changed.
*/
int newPos; /* New text positions for all labels in
* the area, for example, GEO_NORTH.
*/
{
Label *lab;
for (lab = cellDef->cd_labels; lab != NULL; lab = lab->lab_next)
{
if (GEO_TOUCH(area, &lab->lab_rect))
{
DBUndoEraseLabel(cellDef, lab);
DBWLabelChanged(cellDef, lab, DBW_ALLWINDOWS);
lab->lab_just = newPos;
DBUndoPutLabel(cellDef, lab);
DBWLabelChanged(cellDef, lab, DBW_ALLWINDOWS);
}
}
}
/*
* ----------------------------------------------------------------------------
*
* DBAdjustLabels --
*
* This procedure is called after paint has been modified
* in an area. It finds all labels overlapping that area,
* and adjusts the layers they are attached to to reflect
* the changes in paint. Thus, a layer will automatically
* migrate from poly to poly-metal-contact and back to
* poly if the contact layer is painted and then erased.
*
* Results:
* None.
*
* Side effects:
* The layer attachments of labels may change. For each
* such change, a message is output.
*
* ----------------------------------------------------------------------------
*/
void
DBAdjustLabels(def, area)
CellDef *def; /* Cell whose paint was changed. */
Rect *area; /* Area where paint was modified. */
{
Label *lab;
TileType newType;
bool modified = FALSE;
/* First, find each label that crosses the area we're
* interested in.
*/
for (lab = def->cd_labels; lab != NULL; lab = lab->lab_next)
{
if (!GEO_TOUCH(&lab->lab_rect, area)) continue;
newType = DBPickLabelLayer(def, lab, 0);
if (newType == lab->lab_type) continue;
if (lab->lab_flags & LABEL_STICKY) continue;
if (DBVerbose && ((def->cd_flags & CDINTERNAL) == 0)) {
TxPrintf("Moving label \"%s\" from %s to %s in cell %s.\n",
lab->lab_text, DBTypeLongName(lab->lab_type),
DBTypeLongName(newType), def->cd_name);
};
DBUndoEraseLabel(def, lab);
lab->lab_type = newType;
DBUndoPutLabel(def, lab);
modified = TRUE;
}
if (modified) DBCellSetModified(def, TRUE);
}
/*
* Extended version of DBAdjustLabels. If noreconnect==0,
* this is supposed to be the same as DBAdjustlabels() above.
*/
void
DBAdjustLabelsNew(def, area, noreconnect)
CellDef *def; /* Cell whose paint was changed. */
Rect *area; /* Area where paint was modified. */
int noreconnect; /* if 1, don't move label to a type that doesn't
* connect to the original type, delete instead
*/
{
Label *lab, *labPrev;
TileType newType;
bool modified = FALSE;
/* First, find each label that crosses the area we're
* interested in.
*/
labPrev = NULL;
lab = def->cd_labels;
while (lab != NULL)
{
int locnoreconnect = noreconnect;
if (!GEO_TOUCH(&lab->lab_rect, area)) {
goto nextLab;
}
if (lab->lab_type == TT_SPACE) locnoreconnect = FALSE;
newType = DBPickLabelLayer(def, lab, locnoreconnect);
if (newType == lab->lab_type) {
goto nextLab;
}
if(newType < 0 && !(lab->lab_flags & LABEL_STICKY)) {
TxPrintf("Deleting ambiguous-layer label \"%s\" from %s in cell %s.\n",
lab->lab_text, DBTypeLongName(lab->lab_type),
def->cd_name);
if (labPrev == NULL)
def->cd_labels = lab->lab_next;
else
labPrev->lab_next = lab->lab_next;
if (def->cd_lastLabel == lab)
def->cd_lastLabel = labPrev;
DBUndoEraseLabel(def, lab);
DBWLabelChanged(def, lab, DBW_ALLWINDOWS);
freeMagic((char *) lab);
lab = lab->lab_next;
modified = TRUE;
continue;
} else if (!(lab->lab_flags & LABEL_STICKY)) {
if (DBVerbose && ((def->cd_flags & CDINTERNAL) == 0)) {
TxPrintf("Moving label \"%s\" from %s to %s in cell %s.\n",
lab->lab_text, DBTypeLongName(lab->lab_type),
DBTypeLongName(newType), def->cd_name);
}
DBUndoEraseLabel(def, lab);
lab->lab_type = newType;
DBUndoPutLabel(def, lab);
modified = TRUE;
}
nextLab:
labPrev = lab;
lab = lab->lab_next;
}
if (modified) DBCellSetModified(def, TRUE);
}
/*
* ----------------------------------------------------------------------------
*
* DBPickLabelLayer --
*
* This procedure looks at the material around a label and
* picks a new layer for the label to be attached to.
*
* Results:
* Returns a tile type, which is a layer that completely
* covers the label's area. If possible, the label's current
* layer is chosen, or if that's not possible, then a layer
* that connects to the label's current layer, or else any
* other mask layer. If everything fails, TT_SPACE is returned.
*
* Side effects:
* None. The label's layer is not changed by this procedure.
*
* ----------------------------------------------------------------------------
*/
/* The following variable(s) are shared between DBPickLabelLayer and
* its search function dbPickFunc2.
*/
TileTypeBitMask *dbAdjustPlaneTypes; /* Mask of all types in current
* plane being searched.
*/
TileType
DBPickLabelLayer(def, lab, noreconnect)
CellDef *def; /* Cell definition containing label. */
Label *lab; /* Label for which a home must be found. */
int noreconnect; /* if 1, return -1 if rule 5 or 6 would succeed */
{
TileTypeBitMask types[3], types2[3];
Rect check1, check2;
int i, j, plane;
TileType choice1, choice2, choice3, choice4, choice5, choice6;
extern int dbPickFunc1(), dbPickFunc2();
/* Compute an array of three tile type masks. The first is for
* all of the types that are present everywhere underneath the
* label. The second is for all types that are components of
* tiles that completely cover the label's area. The third
* is for tile types that touch the label anywhere. To make this
* work correctly we have to consider four cases separately:
* point labels, horizontal line labels, vertical line labels,
* and rectangular labels.
*/
if ((lab->lab_rect.r_xbot == lab->lab_rect.r_xtop)
&& (lab->lab_rect.r_ybot == lab->lab_rect.r_ytop))
{
/* Point label. Find out what layers touch the label and
* use this for all three masks.
*/
GEO_EXPAND(&lab->lab_rect, 1, &check1);
types[0] = DBZeroTypeBits;
for (i = PL_SELECTBASE; i < DBNumPlanes; i += 1)
{
(void) DBSrPaintArea((Tile *) NULL, def->cd_planes[i],
&check1, &DBAllTypeBits, dbPickFunc1,
(ClientData) &types[0]);
}
types[1] = types[0];
types[2] = types[0];
}
else if (lab->lab_rect.r_xbot == lab->lab_rect.r_xtop)
{
/* Vertical line label. Search two areas, one on the
* left and one on the right. For each side, compute
* the type arrays separately. Then merge them together.
*/
check1 = lab->lab_rect;
check2 = lab->lab_rect;
check1.r_xbot -= 1;
check2.r_xtop += 1;
twoAreas:
types[0] = DBAllButSpaceAndDRCBits;
types[1] = DBAllButSpaceAndDRCBits;
TTMaskZero(&types[2]);
types2[0] = DBAllButSpaceAndDRCBits;
types2[1] = DBAllButSpaceAndDRCBits;
TTMaskZero(&types2[2]);
for (i = PL_SELECTBASE; i < DBNumPlanes; i += 1)
{
dbAdjustPlaneTypes = &DBPlaneTypes[i];
(void) DBSrPaintArea((Tile *) NULL, def->cd_planes[i],
&check1, &DBAllTypeBits, dbPickFunc2,
(ClientData) types);
(void) DBSrPaintArea((Tile *) NULL, def->cd_planes[i],
&check2, &DBAllTypeBits, dbPickFunc2,
(ClientData) types2);
}
TTMaskSetMask(&types[0], &types2[0]);
TTMaskSetMask(&types[1], &types2[1]);
TTMaskSetMask(&types[2], &types2[2]);
}
else if (lab->lab_rect.r_ybot == lab->lab_rect.r_ytop)
{
/* Horizontal line label. Search two areas, one on the
* top and one on the bottom. Use the code from above
* to handle.
*/
check1 = lab->lab_rect;
check2 = lab->lab_rect;
check1.r_ybot -= 1;
check2.r_ytop += 1;
goto twoAreas;
}
else
{
/* This is a rectangular label. Same thing as for line labels,
* except there's only one area to search.
*/
types[0] = DBAllButSpaceAndDRCBits;
types[1] = DBAllButSpaceAndDRCBits;
TTMaskZero(&types[2]);
for (i = PL_SELECTBASE; i < DBNumPlanes; i += 1)
{
dbAdjustPlaneTypes = &DBPlaneTypes[i];
(void) DBSrPaintArea((Tile *) NULL, def->cd_planes[i],
&lab->lab_rect, &DBAllTypeBits, dbPickFunc2,
(ClientData) types);
}
}
/* If the label's layer covers the label's area, use it.
* Otherwise, look for a layer in the following order:
* 1. A layer on the same plane as the original layer and that
* covers the label and connects to its original layer.
* 2. A layer on the same plane as the original layer and that
* is a component of material that covers the label and
* connects to its original layer.
* 3. A layer that covers the label and connects to the
* old layer.
* 4. A layer that is a component of material that covers
* the label and connects to the old layer.
* 5. A layer that covers the label.
* 6. A layer that is a component of material that covers the label.
* 7. Space.
*
* All searches are done from the lowest to highest plane, so that
* the label connects to material on the highest plane that matches
* the criteria above. This avoids weirdnesses caused by declaring
* types out of order in the techfile.
*/
if (TTMaskHasType(&types[0], lab->lab_type)) return lab->lab_type;
plane = DBPlane(lab->lab_type);
choice1 = choice2 = choice3 = choice4 = choice5 = choice6 = TT_SPACE;
for (j = PL_SELECTBASE; j < DBNumPlanes; j++)
{
for (i = TT_SELECTBASE; i < DBNumUserLayers; i += 1)
{
if (!TTMaskHasType(&DBPlaneTypes[j], i)) continue;
if (DBConnectsTo(i, lab->lab_type))
{
if (DBPlane(i) == plane)
{
if (TTMaskHasType(&types[0], i))
{
choice1 = i;
continue;
}
else if (TTMaskHasType(&types[1], i))
{
choice2 = i;
continue;
}
}
if (TTMaskHasType(&types[0], i))
{
choice3 = i;
continue;
}
else if (TTMaskHasType(&types[1], i))
{
choice4 = i;
continue;
}
}
if (TTMaskHasType(&types[0], i))
{
/* A type that connects to more than itself is preferred */
if (choice5 == TT_SPACE)
choice5 = i;
else
{
TileTypeBitMask ctest;
TTMaskZero(&ctest);
TTMaskSetMask(&ctest, &DBConnectTbl[i]);
TTMaskClearType(&ctest, i);
if (!TTMaskIsZero(&ctest))
choice5 = i;
else if (TTMaskHasType(&types[1], i))
choice6 = i;
}
continue;
}
else if (TTMaskHasType(&types[1], i))
{
choice6 = i;
continue;
}
}
}
if (choice1 != TT_SPACE) return choice1;
else if (choice2 != TT_SPACE) return choice2;
else if (choice3 != TT_SPACE) return choice3;
else if (choice4 != TT_SPACE) return choice4;
else if (noreconnect) {
#ifdef notdef
TxPrintf("DBPickLabelLayer \"%s\" (on %s at %d,%d) choice4=%s choice5=%s choice6=%s.\n",
lab->lab_text,
DBTypeLongName(lab->lab_type),
lab->lab_rect.r_xbot,
lab->lab_rect.r_ytop,
DBTypeLongName(choice4),
DBTypeLongName(choice5),
DBTypeLongName(choice6));
#endif
/* If the flag is set, don't cause a netlist change by moving a
the label. So unless there's only space here, delete the label */
if(choice5 == TT_SPACE && choice6 == TT_SPACE)
return TT_SPACE;
else
return -1;
}
else if (choice5 != TT_SPACE) return choice5;
else return choice6;
}
/* Search function for DBPickLabelLayer: just OR in the type of
* any tiles (except space) to the mask passed as clientdata.
* Always return 0 to keep the search alive.
*/
int
dbPickFunc1(tile, mask)
Tile *tile; /* Tile found. */
TileTypeBitMask *mask; /* Mask to be modified. */
{
TileType type;
if (IsSplit(tile))
type = (SplitSide(tile)) ? SplitRightType(tile) : SplitLeftType(tile);
else
type = TiGetType(tile);
if (type == TT_SPACE) return 0;
TTMaskSetType(mask, type);
return 0;
}
/* Another search function for DBPickLabelLayer. For the first element
* in the mask array, AND off all types on the current plane except the
* given type. For the second element, AND off all types on the current
* plane except the ones that are components of this tile's type. For
* the third element of the array, just OR in the type of the current
* tile. A space tile ruins the whole plane so return 1 to abort the
* search. Otherwise return 0.
*/
int
dbPickFunc2(tile, mask)
Tile *tile; /* Tile found. */
TileTypeBitMask *mask; /* Mask to be modified. */
{
TileType type;
TileTypeBitMask tmp, *rMask;
if (IsSplit(tile))
type = (SplitSide(tile)) ? SplitRightType(tile) : SplitLeftType(tile);
else
type = TiGetType(tile);
if (type == TT_SPACE)
{
/* Space means can't have any tile types on this plane. */
TTMaskClearMask(&mask[0], dbAdjustPlaneTypes);
TTMaskClearMask(&mask[1], dbAdjustPlaneTypes);
return 1;
}
tmp = *dbAdjustPlaneTypes;
TTMaskClearType(&tmp, type);
TTMaskClearMask(&mask[0], &tmp);
rMask = DBResidueMask(type);
TTMaskClearMask(&tmp, rMask);
TTMaskClearMask(&mask[1], &tmp);
TTMaskSetType(&mask[2], type);
return 0;
}
/*
* Linked point list, used as temporary space when generating
* font character paths.
*/
typedef struct fontpath *FontPathPtr;
typedef struct fontpath
{
Point fp_point;
FontPathPtr fp_next;
} FontPath;
#define INTSEGS 5 /* Font outline Bezier curves are deconstructed
* into (INTSEGS + 1) line segments
*/
float par[INTSEGS];
float parsq[INTSEGS];
float parcb[INTSEGS];
/*
* ----------------------------------------------------------------------------
*
* Initialize Bezier parametric settings
*
* ----------------------------------------------------------------------------
*/
void
DBFontInitCurves()
{
float t;
short idx;
for (idx = 0; idx < INTSEGS; idx++)
{
t = (float)(idx + 1) / (INTSEGS + 1);
par[idx] = t;
parsq[idx] = t * t;
parcb[idx] = parsq[idx] * t;
}
}
/*
* ----------------------------------------------------------------------------
*
* Compute line segment points from control points of a Cubic Bezier curve.
*
* Note: This routine does not take into account the curvature, and therefore
* does not optimize for the minimum number of line segments needed to
* represent the curve within a fixed margin of error.
*
* ----------------------------------------------------------------------------
*/
void
CalcBezierPoints(fp, bp)
FontPath *fp; /* Pointer to last point of closed path */
FontPath *bp; /* Pointer to 1st of 3 bezier control points */
{
FontPath *newPath, *curPath;
Point *beginPath, *ctrl1, *ctrl2, *endPath;
float ax, bx, cx, ay, by, cy;
int idx, tmpx, tmpy;
beginPath = &fp->fp_point;
ctrl1 = &fp->fp_next->fp_point;
ctrl2 = &fp->fp_next->fp_next->fp_point;
endPath = &fp->fp_next->fp_next->fp_next->fp_point;
cx = 3.0 * (float)(ctrl1->p_x - beginPath->p_x);
bx = 3.0 * (float)(ctrl2->p_x - ctrl1->p_x) - cx;
ax = (float)(endPath->p_x - beginPath->p_x) - cx - bx;
cy = 3.0 * (float)(ctrl1->p_y - beginPath->p_y);
by = 3.0 * (float)(ctrl2->p_y - ctrl1->p_y) - cy;
ay = (float)(endPath->p_y - beginPath->p_y) - cy - by;
curPath = fp;
for (idx = 0; idx < INTSEGS; idx++)
{
tmpx = (int)(ax * parcb[idx] + bx * parsq[idx] +
cx * par[idx] + beginPath->p_x);
tmpy = (int)(ay * parcb[idx] + by * parsq[idx] +
cy * par[idx] + beginPath->p_y);
if ((tmpx != curPath->fp_point.p_x) || (tmpy != curPath->fp_point.p_y))
{
newPath = (FontPath *)mallocMagic(sizeof(FontPath));
newPath->fp_point.p_x = tmpx;
newPath->fp_point.p_y = tmpy;
curPath->fp_next = newPath;
curPath = newPath;
}
}
/* Link the last point to the end and free the two control points */
curPath->fp_next = bp->fp_next->fp_next;
freeMagic(bp->fp_next);
freeMagic(bp);
}
/*
* ----------------------------------------------------------------------------
*
* dbGetToken ---
*
* Tokenize PostScript input from a Type3 font definition file.
*
* ----------------------------------------------------------------------------
*/
char *
dbGetToken(ff)
FILE *ff;
{
static char line[512];
static char *lineptr = NULL;
char *rptr;
while (lineptr == NULL)
{
if (fgets(line, 511, ff) == NULL)
return NULL;
else
{
lineptr = line;
while (isspace(*lineptr)) lineptr++;
}
if (*lineptr == '%') lineptr = NULL; /* Skip comment lines */
else if (*lineptr == '\n') lineptr = NULL; /* Skip blank lines */
}
rptr = lineptr;
while (!isspace(*lineptr) && (*lineptr != '\n')) lineptr++;
if (*lineptr == '\n')
{
*lineptr = '\0';
lineptr = NULL;
}
else
{
*lineptr = '\0';
lineptr++;
while (isspace(*lineptr)) lineptr++;
}
return rptr;
}
/*
* ----------------------------------------------------------------------------
*
* DBFontLabelSetBBox --
*
* Given a font label with corresponding size, rotation, offset, etc.,
* (1) compute the four corners of the label, and (2) compute the
* Manhattan bounding box of the label. The corners are computed in
* units of (database / 8) for a finer resolution, and measured
* relative to the label rectangle's origin so we don't run out of
* bits in computing the corner positions.
*
* This routine needs to be run whenever a font label changes
* properties (including when the font label is created).
*
* Results:
* None.
*
* Side effects:
* The label's lab_bbox record is modified to contain the bounding
* box, in database coordinates, of the given label. The label's
* lab_corners[] points are modified to contain the corner positions
* of the label, in units as described above.
*
* ----------------------------------------------------------------------------
*/
void
DBFontLabelSetBBox(label)
Label *label;
{
char *tptr;
Point *coffset, rcenter;
Rect *cbbox, locbbox, *frect;
Transform labTransform = GeoIdentityTransform;
int i, ysave, size;
double rrad, cr, sr, tmpx, tmpy;
bool expand;
if (label->lab_font < 0) return; /* No action for default font */
/* Label size is computed such that size = 1 means that the font */
/* height extent is equal to 1 database unit. The font height */
/* extent is measured from the baseline up. */
frect = &DBFontList[label->lab_font]->mf_extents;
/* Get label size from outline fonts */
locbbox = GeoNullRect;
for (tptr = label->lab_text; *tptr != '\0'; tptr++)
{
DBFontChar(label->lab_font, *tptr, NULL, &coffset, &cbbox);
if (*(tptr + 1) == '\0')
locbbox.r_xtop += cbbox->r_xtop;
else
locbbox.r_xtop += coffset->p_x;
locbbox.r_ytop = MAX(locbbox.r_ytop, cbbox->r_ytop);
locbbox.r_ybot = MIN(locbbox.r_ybot, cbbox->r_ybot);
}
/* In case the font did not declare its extents, use the actual text height */
locbbox.r_ytop = MAX(locbbox.r_ytop, frect->r_ytop);
/* Scale to font size, in database units / 8 (upshift 3 bits) */
ysave = locbbox.r_ytop;
locbbox.r_xbot *= label->lab_size;
locbbox.r_xtop *= label->lab_size;
locbbox.r_ybot *= label->lab_size;
locbbox.r_ytop *= label->lab_size;
locbbox.r_xbot /= ysave;
locbbox.r_xtop /= ysave;
locbbox.r_ybot /= ysave;
locbbox.r_ytop /= ysave;
/* Apply justification */
switch (label->lab_just)
{
case GEO_SOUTH:
case GEO_SOUTHWEST:
case GEO_SOUTHEAST:
locbbox.r_ybot -= locbbox.r_ytop;
locbbox.r_ytop = 0;
break;
case GEO_WEST:
case GEO_EAST:
case GEO_CENTER:
locbbox.r_ytop >>= 1;
locbbox.r_ybot -= locbbox.r_ytop;
break;
}
switch (label->lab_just)
{
case GEO_WEST:
case GEO_NORTHWEST:
case GEO_SOUTHWEST:
locbbox.r_xbot -= locbbox.r_xtop;
locbbox.r_xtop = 0;
break;
case GEO_NORTH:
case GEO_SOUTH:
case GEO_CENTER:
locbbox.r_xtop >>= 1;
locbbox.r_xbot -= locbbox.r_xtop;
break;
}
/* Apply offset (already in units / 8) */
locbbox.r_xbot += label->lab_offset.p_x;
locbbox.r_xtop += label->lab_offset.p_x;
locbbox.r_ybot += label->lab_offset.p_y;
locbbox.r_ytop += label->lab_offset.p_y;
/* Apply rotation */
if (label->lab_rotate < 0) label->lab_rotate += 360;
if (label->lab_rotate >= 360) label->lab_rotate -= 360;
label->lab_corners[0] = locbbox.r_ll;
label->lab_corners[1].p_x = locbbox.r_ur.p_x;
label->lab_corners[1].p_y = locbbox.r_ll.p_y;
label->lab_corners[2] = locbbox.r_ur;
label->lab_corners[3].p_x = locbbox.r_ll.p_x;
label->lab_corners[3].p_y = locbbox.r_ur.p_y;
rrad = (double)label->lab_rotate * 0.0174532925;
cr = cos(rrad);
sr = sin(rrad);
for (i = 0; i < 4; i++)
{
tmpx = label->lab_corners[i].p_x * cr - label->lab_corners[i].p_y * sr;
tmpy = label->lab_corners[i].p_x * sr + label->lab_corners[i].p_y * cr;
label->lab_corners[i].p_x = (int)round(tmpx);
label->lab_corners[i].p_y = (int)round(tmpy);
/* Initialize bounding box */
if (i == 0)
label->lab_bbox.r_ll = label->lab_bbox.r_ur = label->lab_corners[0];
else
GeoIncludePoint(&label->lab_corners[i], &label->lab_bbox);
}
/* Compute the bounding box, in (rounded) database coordinates */
rcenter.p_x = (label->lab_rect.r_xtop + label->lab_rect.r_xbot) << 2;
rcenter.p_y = (label->lab_rect.r_ytop + label->lab_rect.r_ybot) << 2;
label->lab_bbox.r_xbot += rcenter.p_x;
label->lab_bbox.r_xtop += rcenter.p_x;
label->lab_bbox.r_ybot += rcenter.p_y;
label->lab_bbox.r_ytop += rcenter.p_y;
expand = (label->lab_bbox.r_xbot & 0x7f) ? TRUE : FALSE;
label->lab_bbox.r_xbot >>= 3;
if (expand) label->lab_bbox.r_xbot--;
expand = (label->lab_bbox.r_xtop & 0x7f) ? TRUE : FALSE;
label->lab_bbox.r_xtop >>= 3;
if (expand) label->lab_bbox.r_xtop++;
expand = (label->lab_bbox.r_ybot & 0x7f) ? TRUE : FALSE;
label->lab_bbox.r_ybot >>= 3;
if (expand) label->lab_bbox.r_ybot--;
expand = (label->lab_bbox.r_ytop & 0x7f) ? TRUE : FALSE;
label->lab_bbox.r_ytop >>= 3;
if (expand) label->lab_bbox.r_ytop++;
}
/*
* ----------------------------------------------------------------------------
*
* DBFontChar ---
*
* Return information about a single character of a font
*
* Results:
* Return 0 on success, -1 on failure.
*
* Side effects:
* Fills in clist, coffset, and cbbox if any are non-NULL.
*
* ----------------------------------------------------------------------------
*/
int
DBFontChar(font, ccode, clist, coffset, cbbox)
int font; /* Index of font */
char ccode; /* ASCII character code */
FontChar **clist; /* Return vector list here */
Point **coffset; /* Return position offset here */
Rect **cbbox; /* Return bounding box here */
{
if (font < 0 || font >= DBNumFonts) return - 1;
if (DBFontList[font] == NULL) return -1;
if (ccode < 32) ccode = 127; /* out-of-bounds code */
if (clist) *clist = DBFontList[font]->mf_vectors[ccode - 32];
if (coffset) *coffset = &DBFontList[font]->mf_offset[ccode - 32];
if (cbbox) *cbbox = &DBFontList[font]->mf_bbox[ccode - 32];
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* DBNameToFont ---
*
* Given a font name, return the index into DBFontList.
*
* Results:
* -1 if the name is "default", and -2 if the name is not found.
* Otherwise, return the index into the DBFontList.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
int
DBNameToFont(name)
char *name;
{
int i;
for (i = 0; i < DBNumFonts; i++)
if (!strcasecmp(name, DBFontList[i]->mf_name))
return i;
if (!strcasecmp(name, "default"))
return -1;
return -2;
}
/*
* ----------------------------------------------------------------------------
*
* DBLoadFont ---
*
* Read information from a Type3 font file, and save each character
* as a set of vectors forming closed paths.
* This routines assumes a *very* limited syntax for Type3 PostScript
* fonts, using only moveto, lineto, curveto, and closepath for each
* character. The font dictionary is assumed to be called CharProcs.
* These assumptions are valid for Type3 font output generated by
* FontForge.
*
* Results:
* 0 on success, -1 on failure.
*
* Side effects:
* Fills font data structures.
*
* ----------------------------------------------------------------------------
*/
#define NUMBUF 16
int
DBLoadFont(fontfile, scale)
char *fontfile;
float scale;
{
FILE *ff;
char *ascii_names[] = {
"space", "exclam", "quotedbl", "numbersign", "dollar",
"percent", "ampersand", "quoteright", "parenleft", "parenright",
"asterisk", "plus", "comma", "hyphen", "period", "slash", "zero",
"one", "two", "three", "four", "five", "six", "seven", "eight",
"nine", "colon", "semicolon", "less", "equal", "greater", "question",
"at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L",
"M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
"Z", "bracketleft", "backslash", "bracketright", "asciicircum",
"underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h",
"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u",
"v", "w", "x", "y", "z", "braceleft", "bar", "braceright",
"asciitilde", ".notdef", NULL
};
int i, j, x, y, asciiidx, chardef, numidx;
int numtok[NUMBUF];
char *token, *psname = NULL;
MagicFont *newFont = NULL, **newDBFontList;
FontChar *newChar;
FontPath *newPath, *pathStart = NULL, *curPath, *lastPath;
Rect extents = GeoNullRect;
ff = PaOpen(fontfile, "r", (char *)NULL, ".", SysLibPath, (char **)NULL);
if (ff == NULL) return -1;
chardef = 0;
while ((token = dbGetToken(ff)) != NULL)
{
if (token[0] == '/') psname = &token[1]; /* Hopefully this doesn't go away */
if (chardef == 0)
{
if (!strcmp(token, "/FontBBox"))
{
token = dbGetToken(ff);
if (*token == '{') token++;
while (isspace(*token)) token++;
if (StrIsNumeric(token)) extents.r_xbot = (int)round(atof(token));
token = dbGetToken(ff);
if (StrIsNumeric(token)) extents.r_ybot = (int)round(atof(token));
token = dbGetToken(ff);
if (StrIsNumeric(token)) extents.r_xtop = (int)round(atof(token));
token = dbGetToken(ff);
if (StrIsNumeric(token)) extents.r_ytop = (int)round(atof(token));
}
else if (!strcmp(token, "CharProcs"))
{
token = dbGetToken(ff);
if (!strcmp(token, "begin"))
{
chardef = 1;
newDBFontList = (MagicFont **)mallocMagic((DBNumFonts + 2)
* sizeof(MagicFont *));
newDBFontList[DBNumFonts + 1] = NULL;
for (i = 0; i < DBNumFonts; i++)
newDBFontList[i] = DBFontList[i];
if (DBNumFonts > 0) freeMagic((char *)DBFontList);
DBFontList = newDBFontList;
newFont = (MagicFont *)mallocMagic(sizeof(MagicFont));
newDBFontList[DBNumFonts] = newFont;
DBNumFonts++;
for (asciiidx = 0; asciiidx < 96; asciiidx++)
newFont->mf_vectors[asciiidx] = NULL;
newFont->mf_extents = extents;
/* This is a hack, but all these fonts declare */
/* their "height" to be the Y-value of the tallest */
/* character, from which one cannot get any useful */
/* measure of the height of a standard capital */
/* letter. I am using an ad-hoc scale passed as an */
/* argument to the load command (usually ~0.6). */
newFont->mf_extents.r_ytop =
(int)((float)newFont->mf_extents.r_ytop
* scale);
}
}
else if (!strcmp(token, "definefont"))
{
if (newFont && psname)
{
char *dotptr = strrchr(psname, '.');
if (dotptr != NULL) *dotptr = '\0';
newFont->mf_name = StrDup((char **)NULL, psname);
}
break; /* Presumably this is the end of input */
}
}
else if (chardef == 1)
{
if (!strcmp(token, "currentdict"))
{
token = dbGetToken(ff);
if (!strcmp(token, "end"))
chardef = 0;
}
/* Look for a character def start */
else if (!strcmp(token, "{"))
{
if (psname != NULL)
{
asciiidx = LookupStructFull(psname, ascii_names, sizeof(char *));
if (asciiidx >= 0)
chardef = 2;
}
numidx = 0;
}
}
else if (chardef == 2)
{
/* Look for character def end */
if (!strcmp(token, "}"))
chardef = 1;
/* Pick up values for setcachedevice */
if (StrIsNumeric(token))
{
numtok[numidx] = (int)round(atof(token));
numidx = (numidx + 1) % NUMBUF;
}
/* Look for path start */
else if (!strcmp(token, "setcachedevice"))
{
newChar = NULL;
chardef = 3;
/* Fill in bounding box and offset */
newFont->mf_offset[asciiidx].p_x = numtok[(numidx + NUMBUF - 6)
% NUMBUF];
newFont->mf_offset[asciiidx].p_y = numtok[(numidx + NUMBUF - 5)
% NUMBUF];
newFont->mf_bbox[asciiidx].r_xbot = numtok[(numidx + NUMBUF - 4)
% NUMBUF];
newFont->mf_bbox[asciiidx].r_ybot = numtok[(numidx + NUMBUF - 3)
% NUMBUF];
newFont->mf_bbox[asciiidx].r_xtop = numtok[(numidx + NUMBUF - 2)
% NUMBUF];
newFont->mf_bbox[asciiidx].r_ytop = numtok[(numidx + NUMBUF - 1)
% NUMBUF];
}
}
else if (chardef == 3)
{
if (StrIsNumeric(token))
{
numtok[numidx] = (int)round(atof(token));
numidx = (numidx + 1) % NUMBUF;
}
else if (!strcmp(token, "}")) /* End def */
chardef = 1;
else if (!strcmp(token, "closepath"))
{
if (newChar == NULL)
{
newChar = (FontChar *)mallocMagic(sizeof(FontChar));
newFont->mf_vectors[asciiidx] = newChar;
}
else
{
newChar->fc_next = (FontChar *)mallocMagic(sizeof(FontChar));
newChar = newChar->fc_next;
}
/* translate pathStart */
for (i = 0, curPath = pathStart; curPath->fp_next != NULL;
curPath = curPath->fp_next)
if (!GEO_SAMEPOINT(curPath->fp_next->fp_point, curPath->fp_point))
i++;
newChar->fc_numpoints = i;
/* If the first and last points are the same, remove the last one */
if (!GEO_SAMEPOINT(curPath->fp_point, pathStart->fp_point))
newChar->fc_numpoints++;
newChar->fc_points = (Point *)mallocMagic(i * sizeof(Point));
newChar->fc_next = NULL;
for (i = 0, curPath = pathStart; i < newChar->fc_numpoints;
curPath = curPath->fp_next)
{
if (curPath->fp_next != NULL)
if (GEO_SAMEPOINT(curPath->fp_next->fp_point, curPath->fp_point))
continue;
newChar->fc_points[i].p_x = curPath->fp_point.p_x;
newChar->fc_points[i].p_y = curPath->fp_point.p_y;
i++;
}
/* Remove the pointlist */
for (newPath = pathStart; newPath != NULL; newPath = newPath->fp_next)
freeMagic(newPath);
pathStart = NULL;
}
else
{
j = (!strcmp(token, "curveto")) ? 6 : 2;
lastPath = curPath;
for (i = j; i > 0; i -= 2)
{
newPath = (FontPath *)mallocMagic(sizeof(FontPath));
newPath->fp_next = NULL;
newPath->fp_point.p_x = numtok[(numidx + NUMBUF - i) % NUMBUF];
newPath->fp_point.p_y = numtok[(numidx + NUMBUF - i + 1) % NUMBUF];
if (pathStart == NULL)
pathStart = newPath;
else
curPath->fp_next = newPath;
curPath = newPath;
}
/* "moveto" and "lineto" require no further action. "curveto" */
/* requires that we convert the bezier curve defined by */
/* the last 4 points to a set of line segments. */
if (j == 6)
{
/* Retain the 3 curve points that will be replaced */
FontPath *curvePath = lastPath->fp_next;
CalcBezierPoints(lastPath, curvePath);
}
}
}
}
fclose(ff);
return (newFont == NULL) ? -1 : 0;
}