diff options
Diffstat (limited to 'nx-X11/extras/ttf2pt1/pt1.c')
-rw-r--r-- | nx-X11/extras/ttf2pt1/pt1.c | 7374 |
1 files changed, 0 insertions, 7374 deletions
diff --git a/nx-X11/extras/ttf2pt1/pt1.c b/nx-X11/extras/ttf2pt1/pt1.c deleted file mode 100644 index b1c46d57a..000000000 --- a/nx-X11/extras/ttf2pt1/pt1.c +++ /dev/null @@ -1,7374 +0,0 @@ -/* - * see COPYRIGHT - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <time.h> -#include <ctype.h> -#include <math.h> - -#ifndef WINDOWS -# include <netinet/in.h> -# include <unistd.h> -#else -# include "windows.h" -#endif - -#include "ttf.h" -#include "pt1.h" -#include "global.h" - -/* big and small values for comparisons */ -#define FBIGVAL (1e20) -#define FEPS (100000./FBIGVAL) - -/* names of the axes */ -#define X 0 -#define Y 1 - -/* the GENTRY extension structure used in fforceconcise() */ - -struct gex_con { - double d[2 /*X, Y*/]; /* sizes of curve */ - double sin2; /* squared sinus of the angle to the next gentry */ - double len2; /* squared distance between the endpoints */ - -/* number of reference dots taken from each curve */ -#define NREFDOTS 3 - - double dots[NREFDOTS][2]; /* reference dots */ - - int flags; /* flags for gentry and tits joint to the next gentry */ -/* a vertical or horizontal line may be in 2 quadrants at once */ -#define GEXF_QUL 0x00000001 /* in up-left quadrant */ -#define GEXF_QUR 0x00000002 /* in up-right quadrant */ -#define GEXF_QDR 0x00000004 /* in down-right quadrant */ -#define GEXF_QDL 0x00000008 /* in down-left quadrant */ -#define GEXF_QMASK 0x0000000F /* quadrant mask */ - -/* if a line is nearly vertical or horizontal, we remember that idealized quartant too */ -#define GEXF_QTO_IDEAL(f) (((f)&0xF)<<4) -#define GEXF_QFROM_IDEAL(f) (((f)&0xF0)>>4) -#define GEXF_IDQ_L 0x00000090 /* left */ -#define GEXF_IDQ_R 0x00000060 /* right */ -#define GEXF_IDQ_U 0x00000030 /* up */ -#define GEXF_IDQ_D 0x000000C0 /* down */ - -/* possibly can be joined with conditions: - * (in order of increasing preference, the numeric order is important) - */ -#define GEXF_JLINE 0x00000100 /* into one line */ -#define GEXF_JIGN 0x00000200 /* if one entry's tangent angle is ignored */ -#define GEXF_JID 0x00000400 /* if one entry is idealized to hor/vert */ -#define GEXF_JFLAT 0x00000800 /* if one entry is flattened */ -#define GEXF_JGOOD 0x00001000 /* perfectly, no additional maodifications */ - -#define GEXF_JMASK 0x00001F00 /* the mask of all above */ -#define GEXF_JCVMASK 0x00001E00 /* the mask of all above except JLINE */ - -/* which entry needs to be modified for conditional joining */ -#define GEXF_JIGN1 0x00002000 -#define GEXF_JIGN2 0x00004000 -#define GEXF_JIGNDIR(dir) (GEXF_JIGN1<<(dir)) -#define GEXF_JID1 0x00008000 -#define GEXF_JID2 0x00010000 -#define GEXF_JIDDIR(dir) (GEXF_JID1<<(dir)) -#define GEXF_JFLAT1 0x00020000 -#define GEXF_JFLAT2 0x00040000 -#define GEXF_JFLATDIR(dir) (GEXF_JFLAT1<<(dir)) - -#define GEXF_VERT 0x00100000 /* is nearly vertical */ -#define GEXF_HOR 0x00200000 /* is nearly horizontal */ -#define GEXF_FLAT 0x00400000 /* is nearly flat */ - -#define GEXF_VDOTS 0x01000000 /* the dots are valid */ - - signed char isd[2 /*X,Y*/]; /* signs of the sizes */ -}; -typedef struct gex_con GEX_CON; - -/* convenience macros */ -#define X_CON(ge) ((GEX_CON *)((ge)->ext)) -#define X_CON_D(ge) (X_CON(ge)->d) -#define X_CON_DX(ge) (X_CON(ge)->d[0]) -#define X_CON_DY(ge) (X_CON(ge)->d[1]) -#define X_CON_ISD(ge) (X_CON(ge)->isd) -#define X_CON_ISDX(ge) (X_CON(ge)->isd[0]) -#define X_CON_ISDY(ge) (X_CON(ge)->isd[1]) -#define X_CON_SIN2(ge) (X_CON(ge)->sin2) -#define X_CON_LEN2(ge) (X_CON(ge)->len2) -#define X_CON_F(ge) (X_CON(ge)->flags) - -/* performance statistics about guessing the concise curves */ -static int ggoodcv=0, ggoodcvdots=0, gbadcv=0, gbadcvdots=0; - -int stdhw, stdvw; /* dominant stems widths */ -int stemsnaph[12], stemsnapv[12]; /* most typical stem width */ - -int bluevalues[14]; -int nblues; -int otherblues[10]; -int notherb; -int bbox[4]; /* the FontBBox array */ -double italic_angle; - -GLYPH *glyph_list; -int encoding[ENCTABSZ]; /* inverse of glyph[].char_no */ -int kerning_pairs = 0; - -/* prototypes */ -static void fixcvdir( GENTRY * ge, int dir); -static void fixcvends( GENTRY * ge); -static int fgetcvdir( GENTRY * ge); -static int igetcvdir( GENTRY * ge); -static int fiszigzag( GENTRY *ge); -static int iiszigzag( GENTRY *ge); -static GENTRY * freethisge( GENTRY *ge); -static void addgeafter( GENTRY *oge, GENTRY *nge ); -static GENTRY * newgentry( int flags); -static void debugstems( char *name, STEM * hstems, int nhs, STEM * vstems, int nvs); -static int addbluestems( STEM *s, int n); -static void sortstems( STEM * s, int n); -static int stemoverlap( STEM * s1, STEM * s2); -static int steminblue( STEM *s); -static void markbluestems( STEM *s, int nold); -static int joinmainstems( STEM * s, int nold, int useblues); -static void joinsubstems( STEM * s, short *pairs, int nold, int useblues); -static void fixendpath( GENTRY *ge); -static void fdelsmall( GLYPH *g, double minlen); -static void alloc_gex_con( GENTRY *ge); -static double fjointsin2( GENTRY *ge1, GENTRY *ge2); -#if 0 -static double fcvarea( GENTRY *ge); -#endif -static double fcvval( GENTRY *ge, int axis, double t); -static void fsampledots( GENTRY *ge, double dots[][2], int ndots); -static void fnormalizege( GENTRY *ge); -static void fanalyzege( GENTRY *ge); -static void fanalyzejoint( GENTRY *ge); -static void fconcisecontour( GLYPH *g, GENTRY *ge); -static double fclosegap( GENTRY *from, GENTRY *to, int axis, - double gap, double *ret); - -int -isign( - int x -) -{ - if (x > 0) - return 1; - else if (x < 0) - return -1; - else - return 0; -} - -int -fsign( - double x -) -{ - if (x > 0.0) - return 1; - else if (x < 0.0) - return -1; - else - return 0; -} - -static GENTRY * -newgentry( - int flags -) -{ - GENTRY *ge; - - ge = calloc(1, sizeof(GENTRY)); - - if (ge == 0) { - fprintf(stderr, "***** Memory allocation error *****\n"); - exit(255); - } - ge->stemid = -1; - ge->flags = flags; - /* the rest is set to 0 by calloc() */ - return ge; -} - -/* - * Routines to print out Postscript functions with optimization - */ - -void -rmoveto( - int dx, - int dy -) -{ - if (optimize && dx == 0) - fprintf(pfa_file, "%d vmoveto\n", dy); - else if (optimize && dy == 0) - fprintf(pfa_file, "%d hmoveto\n", dx); - else - fprintf(pfa_file, "%d %d rmoveto\n", dx, dy); -} - -void -rlineto( - int dx, - int dy -) -{ - if (optimize && dx == 0 && dy == 0) /* for special pathologic - * case */ - return; - else if (optimize && dx == 0) - fprintf(pfa_file, "%d vlineto\n", dy); - else if (optimize && dy == 0) - fprintf(pfa_file, "%d hlineto\n", dx); - else - fprintf(pfa_file, "%d %d rlineto\n", dx, dy); -} - -void -rrcurveto( - int dx1, - int dy1, - int dx2, - int dy2, - int dx3, - int dy3 -) -{ - /* first two ifs are for crazy cases that occur surprisingly often */ - if (optimize && dx1 == 0 && dx2 == 0 && dx3 == 0) - rlineto(0, dy1 + dy2 + dy3); - else if (optimize && dy1 == 0 && dy2 == 0 && dy3 == 0) - rlineto(dx1 + dx2 + dx3, 0); - else if (optimize && dy1 == 0 && dx3 == 0) - fprintf(pfa_file, "%d %d %d %d hvcurveto\n", - dx1, dx2, dy2, dy3); - else if (optimize && dx1 == 0 && dy3 == 0) - fprintf(pfa_file, "%d %d %d %d vhcurveto\n", - dy1, dx2, dy2, dx3); - else - fprintf(pfa_file, "%d %d %d %d %d %d rrcurveto\n", - dx1, dy1, dx2, dy2, dx3, dy3); -} - -void -closepath(void) -{ - fprintf(pfa_file, "closepath\n"); -} - -/* - * Many of the path processing routines exist (or will exist) in - * both floating-point and integer version. Fimally most of the - * processing will go in floating point and the integer processing - * will become legacy. - * The names of floating routines start with f, names of integer - * routines start with i, and those old routines existing in one - * version only have no such prefix at all. - */ - -/* -** Routine that checks integrity of the path, for debugging -*/ - -void -assertpath( - GENTRY * from, - char *file, - int line, - char *name -) -{ - GENTRY *first, *pe, *ge; - int isfloat; - - if(from==0) - return; - isfloat = (from->flags & GEF_FLOAT); - pe = from->prev; - for (ge = from; ge != 0; pe = ge, ge = ge->next) { - if( (ge->flags & GEF_FLOAT) ^ isfloat ) { - fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); - fprintf(stderr, "float flag changes from %s to %s at 0x%p (type %c, prev type %c)\n", - (isfloat ? "TRUE" : "FALSE"), (isfloat ? "FALSE" : "TRUE"), ge, ge->type, pe->type); - abort(); - } - if (pe != ge->prev) { - fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); - fprintf(stderr, "unidirectional chain 0x%x -next-> 0x%x -prev-> 0x%x \n", - pe, ge, ge->prev); - abort(); - } - - switch(ge->type) { - case GE_MOVE: - break; - case GE_PATH: - if (ge->prev == 0) { - fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); - fprintf(stderr, "empty path at 0x%x \n", ge); - abort(); - } - break; - case GE_LINE: - case GE_CURVE: - if(ge->frwd->bkwd != ge) { - fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); - fprintf(stderr, "unidirectional chain 0x%x -frwd-> 0x%x -bkwd-> 0x%x \n", - ge, ge->frwd, ge->frwd->bkwd); - abort(); - } - if(ge->prev->type == GE_MOVE) { - first = ge; - if(ge->bkwd->next->type != GE_PATH) { - fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); - fprintf(stderr, "broken first backlink 0x%x -bkwd-> 0x%x -next-> 0x%x \n", - ge, ge->bkwd, ge->bkwd->next); - abort(); - } - } - if(ge->next->type == GE_PATH) { - if(ge->frwd != first) { - fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); - fprintf(stderr, "broken loop 0x%x -...-> 0x%x -frwd-> 0x%x \n", - first, ge, ge->frwd); - abort(); - } - } - break; - } - - } -} - -void -assertisfloat( - GLYPH *g, - char *msg -) -{ - if( !(g->flags & GF_FLOAT) ) { - fprintf(stderr, "**! Glyph %s is not float: %s\n", g->name, msg); - abort(); - } - if(g->lastentry) { - if( !(g->lastentry->flags & GEF_FLOAT) ) { - fprintf(stderr, "**! Glyphs %s last entry is int: %s\n", g->name, msg); - abort(); - } - } -} - -void -assertisint( - GLYPH *g, - char *msg -) -{ - if( (g->flags & GF_FLOAT) ) { - fprintf(stderr, "**! Glyph %s is not int: %s\n", g->name, msg); - abort(); - } - if(g->lastentry) { - if( (g->lastentry->flags & GEF_FLOAT) ) { - fprintf(stderr, "**! Glyphs %s last entry is float: %s\n", g->name, msg); - abort(); - } - } -} - - -/* - * Routines to save the generated data about glyph - */ - -void -fg_rmoveto( - GLYPH * g, - double x, - double y) -{ - GENTRY *oge; - - if (ISDBG(BUILDG)) - fprintf(stderr, "%s: f rmoveto(%g, %g)\n", g->name, x, y); - - assertisfloat(g, "adding float MOVE"); - - if ((oge = g->lastentry) != 0) { - if (oge->type == GE_MOVE) { /* just eat up the first move */ - oge->fx3 = x; - oge->fy3 = y; - } else if (oge->type == GE_LINE || oge->type == GE_CURVE) { - fprintf(stderr, "Glyph %s: MOVE in middle of path\n", g->name); - } else { - GENTRY *nge; - - nge = newgentry(GEF_FLOAT); - nge->type = GE_MOVE; - nge->fx3 = x; - nge->fy3 = y; - - oge->next = nge; - nge->prev = oge; - g->lastentry = nge; - } - } else { - GENTRY *nge; - - nge = newgentry(GEF_FLOAT); - nge->type = GE_MOVE; - nge->fx3 = x; - nge->fy3 = y; - nge->bkwd = (GENTRY*)&g->entries; - g->entries = g->lastentry = nge; - } - - if (0 && ISDBG(BUILDG)) - dumppaths(g, NULL, NULL); -} - -void -ig_rmoveto( - GLYPH * g, - int x, - int y) -{ - GENTRY *oge; - - if (ISDBG(BUILDG)) - fprintf(stderr, "%s: i rmoveto(%d, %d)\n", g->name, x, y); - - assertisint(g, "adding int MOVE"); - - if ((oge = g->lastentry) != 0) { - if (oge->type == GE_MOVE) { /* just eat up the first move */ - oge->ix3 = x; - oge->iy3 = y; - } else if (oge->type == GE_LINE || oge->type == GE_CURVE) { - fprintf(stderr, "Glyph %s: MOVE in middle of path, ignored\n", g->name); - } else { - GENTRY *nge; - - nge = newgentry(0); - nge->type = GE_MOVE; - nge->ix3 = x; - nge->iy3 = y; - - oge->next = nge; - nge->prev = oge; - g->lastentry = nge; - } - } else { - GENTRY *nge; - - nge = newgentry(0); - nge->type = GE_MOVE; - nge->ix3 = x; - nge->iy3 = y; - nge->bkwd = (GENTRY*)&g->entries; - g->entries = g->lastentry = nge; - } - -} - -void -fg_rlineto( - GLYPH * g, - double x, - double y) -{ - GENTRY *oge, *nge; - - if (ISDBG(BUILDG)) - fprintf(stderr, "%s: f rlineto(%g, %g)\n", g->name, x, y); - - assertisfloat(g, "adding float LINE"); - - nge = newgentry(GEF_FLOAT); - nge->type = GE_LINE; - nge->fx3 = x; - nge->fy3 = y; - - if ((oge = g->lastentry) != 0) { - if (x == oge->fx3 && y == oge->fy3) { /* empty line */ - /* ignore it or we will get in troubles later */ - free(nge); - return; - } - if (g->path == 0) { - g->path = nge; - nge->bkwd = nge->frwd = nge; - } else { - oge->frwd = nge; - nge->bkwd = oge; - g->path->bkwd = nge; - nge->frwd = g->path; - } - - oge->next = nge; - nge->prev = oge; - g->lastentry = nge; - } else { - WARNING_1 fprintf(stderr, "Glyph %s: LINE outside of path\n", g->name); - free(nge); - } - - if (0 && ISDBG(BUILDG)) - dumppaths(g, NULL, NULL); -} - -void -ig_rlineto( - GLYPH * g, - int x, - int y) -{ - GENTRY *oge, *nge; - - if (ISDBG(BUILDG)) - fprintf(stderr, "%s: i rlineto(%d, %d)\n", g->name, x, y); - - assertisint(g, "adding int LINE"); - - nge = newgentry(0); - nge->type = GE_LINE; - nge->ix3 = x; - nge->iy3 = y; - - if ((oge = g->lastentry) != 0) { - if (x == oge->ix3 && y == oge->iy3) { /* empty line */ - /* ignore it or we will get in troubles later */ - free(nge); - return; - } - if (g->path == 0) { - g->path = nge; - nge->bkwd = nge->frwd = nge; - } else { - oge->frwd = nge; - nge->bkwd = oge; - g->path->bkwd = nge; - nge->frwd = g->path; - } - - oge->next = nge; - nge->prev = oge; - g->lastentry = nge; - } else { - WARNING_1 fprintf(stderr, "Glyph %s: LINE outside of path\n", g->name); - free(nge); - } - -} - -void -fg_rrcurveto( - GLYPH * g, - double x1, - double y1, - double x2, - double y2, - double x3, - double y3) -{ - GENTRY *oge, *nge; - - oge = g->lastentry; - - if (ISDBG(BUILDG)) - fprintf(stderr, "%s: f rrcurveto(%g, %g, %g, %g, %g, %g)\n" - ,g->name, x1, y1, x2, y2, x3, y3); - - assertisfloat(g, "adding float CURVE"); - - if (oge && oge->fx3 == x1 && x1 == x2 && x2 == x3) /* check if it's - * actually a line */ - fg_rlineto(g, x1, y3); - else if (oge && oge->fy3 == y1 && y1 == y2 && y2 == y3) - fg_rlineto(g, x3, y1); - else { - nge = newgentry(GEF_FLOAT); - nge->type = GE_CURVE; - nge->fx1 = x1; - nge->fy1 = y1; - nge->fx2 = x2; - nge->fy2 = y2; - nge->fx3 = x3; - nge->fy3 = y3; - - if (oge != 0) { - if (x3 == oge->fx3 && y3 == oge->fy3) { - free(nge); /* consider this curve empty */ - /* ignore it or we will get in troubles later */ - return; - } - if (g->path == 0) { - g->path = nge; - nge->bkwd = nge->frwd = nge; - } else { - oge->frwd = nge; - nge->bkwd = oge; - g->path->bkwd = nge; - nge->frwd = g->path; - } - - oge->next = nge; - nge->prev = oge; - g->lastentry = nge; - } else { - WARNING_1 fprintf(stderr, "Glyph %s: CURVE outside of path\n", g->name); - free(nge); - } - } - - if (0 && ISDBG(BUILDG)) - dumppaths(g, NULL, NULL); -} - -void -ig_rrcurveto( - GLYPH * g, - int x1, - int y1, - int x2, - int y2, - int x3, - int y3) -{ - GENTRY *oge, *nge; - - oge = g->lastentry; - - if (ISDBG(BUILDG)) - fprintf(stderr, "%s: i rrcurveto(%d, %d, %d, %d, %d, %d)\n" - ,g->name, x1, y1, x2, y2, x3, y3); - - assertisint(g, "adding int CURVE"); - - if (oge && oge->ix3 == x1 && x1 == x2 && x2 == x3) /* check if it's - * actually a line */ - ig_rlineto(g, x1, y3); - else if (oge && oge->iy3 == y1 && y1 == y2 && y2 == y3) - ig_rlineto(g, x3, y1); - else { - nge = newgentry(0); - nge->type = GE_CURVE; - nge->ix1 = x1; - nge->iy1 = y1; - nge->ix2 = x2; - nge->iy2 = y2; - nge->ix3 = x3; - nge->iy3 = y3; - - if (oge != 0) { - if (x3 == oge->ix3 && y3 == oge->iy3) { - free(nge); /* consider this curve empty */ - /* ignore it or we will get in troubles later */ - return; - } - if (g->path == 0) { - g->path = nge; - nge->bkwd = nge->frwd = nge; - } else { - oge->frwd = nge; - nge->bkwd = oge; - g->path->bkwd = nge; - nge->frwd = g->path; - } - - oge->next = nge; - nge->prev = oge; - g->lastentry = nge; - } else { - WARNING_1 fprintf(stderr, "Glyph %s: CURVE outside of path\n", g->name); - free(nge); - } - } -} - -void -g_closepath( - GLYPH * g -) -{ - GENTRY *oge, *nge; - - if (ISDBG(BUILDG)) - fprintf(stderr, "%s: closepath\n", g->name); - - oge = g->lastentry; - - if (g->path == 0) { - WARNING_1 fprintf(stderr, "Warning: **** closepath on empty path in glyph \"%s\" ****\n", - g->name); - if (oge == 0) { - WARNING_1 fprintf(stderr, "No previois entry\n"); - } else { - WARNING_1 fprintf(stderr, "Previous entry type: %c\n", oge->type); - if (oge->type == GE_MOVE) { - g->lastentry = oge->prev; - if (oge->prev == 0) - g->entries = 0; - else - g->lastentry->next = 0; - free(oge); - } - } - return; - } - - nge = newgentry(oge->flags & GEF_FLOAT); /* keep the same type */ - nge->type = GE_PATH; - - g->path = 0; - - oge->next = nge; - nge->prev = oge; - g->lastentry = nge; - - if (0 && ISDBG(BUILDG)) - dumppaths(g, NULL, NULL); -} - -/* - * * SB * Routines to smooth and fix the glyphs - */ - -/* -** we don't want to see the curves with coinciding middle and -** outer points -*/ - -static void -fixcvends( - GENTRY * ge -) -{ - int dx, dy; - int x0, y0, x1, y1, x2, y2, x3, y3; - - if (ge->type != GE_CURVE) - return; - - if(ge->flags & GEF_FLOAT) { - fprintf(stderr, "**! fixcvends(0x%x) on floating entry, ABORT\n", ge); - abort(); /* dump core */ - } - - x0 = ge->prev->ix3; - y0 = ge->prev->iy3; - x1 = ge->ix1; - y1 = ge->iy1; - x2 = ge->ix2; - y2 = ge->iy2; - x3 = ge->ix3; - y3 = ge->iy3; - - - /* look at the start of the curve */ - if (x1 == x0 && y1 == y0) { - dx = x2 - x1; - dy = y2 - y1; - - if ((dx == 0 && dy == 0) - || (x2 == x3 && y2 == y3)) { - /* Oops, we actually have a straight line */ - /* - * if it's small, we hope that it will get optimized - * later - */ - if (abs(x3 - x0) <= 2 || abs(y3 - y0) <= 2) { - ge->ix1 = x3; - ge->iy1 = y3; - ge->ix2 = x0; - ge->iy2 = y0; - } else {/* just make it a line */ - ge->type = GE_LINE; - } - } else { - if (abs(dx) < 4 && abs(dy) < 4) { /* consider it very - * small */ - ge->ix1 = x2; - ge->iy1 = y2; - } else if (abs(dx) < 8 && abs(dy) < 8) { /* consider it small */ - ge->ix1 += dx / 2; - ge->iy1 += dy / 2; - } else { - ge->ix1 += dx / 4; - ge->iy1 += dy / 4; - } - /* make sure that it's still on the same side */ - if (abs(x3 - x0) * abs(dy) < abs(y3 - y0) * abs(dx)) { - if (abs(x3 - x0) * abs(ge->iy1 - y0) > abs(y3 - y0) * abs(ge->ix1 - x0)) - ge->ix1 += isign(dx); - } else { - if (abs(x3 - x0) * abs(ge->iy1 - y0) < abs(y3 - y0) * abs(ge->ix1 - x0)) - ge->iy1 += isign(dy); - } - - ge->ix2 += (x3 - x2) / 8; - ge->iy2 += (y3 - y2) / 8; - /* make sure that it's still on the same side */ - if (abs(x3 - x0) * abs(y3 - y2) < abs(y3 - y0) * abs(x3 - x2)) { - if (abs(x3 - x0) * abs(y3 - ge->iy2) > abs(y3 - y0) * abs(x3 - ge->ix2)) - ge->iy1 -= isign(y3 - y2); - } else { - if (abs(x3 - x0) * abs(y3 - ge->iy2) < abs(y3 - y0) * abs(x3 - ge->ix2)) - ge->ix1 -= isign(x3 - x2); - } - - } - } else if (x2 == x3 && y2 == y3) { - dx = x1 - x2; - dy = y1 - y2; - - if (dx == 0 && dy == 0) { - /* Oops, we actually have a straight line */ - /* - * if it's small, we hope that it will get optimized - * later - */ - if (abs(x3 - x0) <= 2 || abs(y3 - y0) <= 2) { - ge->ix1 = x3; - ge->iy1 = y3; - ge->ix2 = x0; - ge->iy2 = y0; - } else {/* just make it a line */ - ge->type = GE_LINE; - } - } else { - if (abs(dx) < 4 && abs(dy) < 4) { /* consider it very - * small */ - ge->ix2 = x1; - ge->iy2 = y1; - } else if (abs(dx) < 8 && abs(dy) < 8) { /* consider it small */ - ge->ix2 += dx / 2; - ge->iy2 += dy / 2; - } else { - ge->ix2 += dx / 4; - ge->iy2 += dy / 4; - } - /* make sure that it's still on the same side */ - if (abs(x3 - x0) * abs(dy) < abs(y3 - y0) * abs(dx)) { - if (abs(x3 - x0) * abs(ge->iy2 - y3) > abs(y3 - y0) * abs(ge->ix2 - x3)) - ge->ix2 += isign(dx); - } else { - if (abs(x3 - x0) * abs(ge->iy2 - y3) < abs(y3 - y0) * abs(ge->ix2 - x3)) - ge->iy2 += isign(dy); - } - - ge->ix1 += (x0 - x1) / 8; - ge->iy1 += (y0 - y1) / 8; - /* make sure that it's still on the same side */ - if (abs(x3 - x0) * abs(y0 - y1) < abs(y3 - y0) * abs(x0 - x1)) { - if (abs(x3 - x0) * abs(y0 - ge->iy1) > abs(y3 - y0) * abs(x0 - ge->ix1)) - ge->iy1 -= isign(y0 - y1); - } else { - if (abs(x3 - x0) * abs(y0 - ge->iy1) < abs(y3 - y0) * abs(x0 - ge->ix1)) - ge->ix1 -= isign(x0 - x1); - } - - } - } -} - -/* -** After transformations we want to make sure that the resulting -** curve is going in the same quadrant as the original one, -** because rounding errors introduced during transformations -** may make the result completeley wrong. -** -** `dir' argument describes the direction of the original curve, -** it is the superposition of two values for the front and -** rear ends of curve: -** -** >EQUAL - goes over the line connecting the ends -** =EQUAL - coincides with the line connecting the ends -** <EQUAL - goes under the line connecting the ends -** -** See CVDIR_* for exact definitions. -*/ - -static void -fixcvdir( - GENTRY * ge, - int dir -) -{ - int a, b, c, d; - double kk, kk1, kk2; - int changed; - int fdir, rdir; - - if(ge->flags & GEF_FLOAT) { - fprintf(stderr, "**! fixcvdir(0x%x) on floating entry, ABORT\n", ge); - abort(); /* dump core */ - } - - fdir = (dir & CVDIR_FRONT) - CVDIR_FEQUAL; - if ((dir & CVDIR_REAR) == CVDIR_RSAME) - rdir = fdir; /* we need only isign, exact value doesn't matter */ - else - rdir = (dir & CVDIR_REAR) - CVDIR_REQUAL; - - fixcvends(ge); - - c = isign(ge->ix3 - ge->prev->ix3); /* note the direction of - * curve */ - d = isign(ge->iy3 - ge->prev->iy3); - - a = ge->iy3 - ge->prev->iy3; - b = ge->ix3 - ge->prev->ix3; - kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); - a = ge->iy1 - ge->prev->iy3; - b = ge->ix1 - ge->prev->ix3; - kk1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); - a = ge->iy3 - ge->iy2; - b = ge->ix3 - ge->ix2; - kk2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); - - changed = 1; - while (changed) { - if (ISDBG(FIXCVDIR)) { - /* for debugging */ - fprintf(stderr, "fixcvdir %d %d (%d %d %d %d %d %d) %f %f %f\n", - fdir, rdir, - ge->ix1 - ge->prev->ix3, - ge->iy1 - ge->prev->iy3, - ge->ix2 - ge->ix1, - ge->iy2 - ge->iy1, - ge->ix3 - ge->ix2, - ge->iy3 - ge->iy2, - kk1, kk, kk2); - } - changed = 0; - - if (fdir > 0) { - if (kk1 > kk) { /* the front end has problems */ - if (c * (ge->ix1 - ge->prev->ix3) > 0) { - ge->ix1 -= c; - changed = 1; - } if (d * (ge->iy2 - ge->iy1) > 0) { - ge->iy1 += d; - changed = 1; - } - /* recalculate the coefficients */ - a = ge->iy3 - ge->prev->iy3; - b = ge->ix3 - ge->prev->ix3; - kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); - a = ge->iy1 - ge->prev->iy3; - b = ge->ix1 - ge->prev->ix3; - kk1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); - } - } else if (fdir < 0) { - if (kk1 < kk) { /* the front end has problems */ - if (c * (ge->ix2 - ge->ix1) > 0) { - ge->ix1 += c; - changed = 1; - } if (d * (ge->iy1 - ge->prev->iy3) > 0) { - ge->iy1 -= d; - changed = 1; - } - /* recalculate the coefficients */ - a = ge->iy1 - ge->prev->iy3; - b = ge->ix1 - ge->prev->ix3; - kk1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); - a = ge->iy3 - ge->prev->iy3; - b = ge->ix3 - ge->prev->ix3; - kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); - } - } - if (rdir > 0) { - if (kk2 < kk) { /* the rear end has problems */ - if (c * (ge->ix2 - ge->ix1) > 0) { - ge->ix2 -= c; - changed = 1; - } if (d * (ge->iy3 - ge->iy2) > 0) { - ge->iy2 += d; - changed = 1; - } - /* recalculate the coefficients */ - a = ge->iy3 - ge->prev->iy3; - b = ge->ix3 - ge->prev->ix3; - kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); - a = ge->iy3 - ge->iy2; - b = ge->ix3 - ge->ix2; - kk2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); - } - } else if (rdir < 0) { - if (kk2 > kk) { /* the rear end has problems */ - if (c * (ge->ix3 - ge->ix2) > 0) { - ge->ix2 += c; - changed = 1; - } if (d * (ge->iy2 - ge->iy1) > 0) { - ge->iy2 -= d; - changed = 1; - } - /* recalculate the coefficients */ - a = ge->iy3 - ge->prev->iy3; - b = ge->ix3 - ge->prev->ix3; - kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); - a = ge->iy3 - ge->iy2; - b = ge->ix3 - ge->ix2; - kk2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); - } - } - } - fixcvends(ge); -} - -/* Get the directions of ends of curve for further usage */ - -/* expects that the previous element is also float */ - -static int -fgetcvdir( - GENTRY * ge -) -{ - double a, b; - double k, k1, k2; - int dir = 0; - - if( !(ge->flags & GEF_FLOAT) ) { - fprintf(stderr, "**! fgetcvdir(0x%x) on int entry, ABORT\n", ge); - abort(); /* dump core */ - } - - a = fabs(ge->fy3 - ge->prev->fy3); - b = fabs(ge->fx3 - ge->prev->fx3); - k = a < FEPS ? (b < FEPS ? 1. : 100000.) : ( b / a); - - a = fabs(ge->fy1 - ge->prev->fy3); - b = fabs(ge->fx1 - ge->prev->fx3); - if(a < FEPS) { - if(b < FEPS) { - a = fabs(ge->fy2 - ge->prev->fy3); - b = fabs(ge->fx2 - ge->prev->fx3); - k1 = a < FEPS ? (b < FEPS ? k : 100000.) : ( b / a); - } else - k1 = FBIGVAL; - } else - k1 = b / a; - - a = fabs(ge->fy3 - ge->fy2); - b = fabs(ge->fx3 - ge->fx2); - if(a < FEPS) { - if(b < FEPS) { - a = fabs(ge->fy3 - ge->fy1); - b = fabs(ge->fx3 - ge->fx1); - k2 = a < FEPS ? (b < FEPS ? k : 100000.) : ( b / a); - } else - k2 = FBIGVAL; - } else - k2 = b / a; - - if(fabs(k1-k) < 0.0001) - dir |= CVDIR_FEQUAL; - else if (k1 < k) - dir |= CVDIR_FUP; - else - dir |= CVDIR_FDOWN; - - if(fabs(k2-k) < 0.0001) - dir |= CVDIR_REQUAL; - else if (k2 > k) - dir |= CVDIR_RUP; - else - dir |= CVDIR_RDOWN; - - return dir; -} - - -/* expects that the previous element is also int */ - -static int -igetcvdir( - GENTRY * ge -) -{ - int a, b; - double k, k1, k2; - int dir = 0; - - if(ge->flags & GEF_FLOAT) { - fprintf(stderr, "**! igetcvdir(0x%x) on floating entry, ABORT\n", ge); - abort(); /* dump core */ - } - - a = ge->iy3 - ge->prev->iy3; - b = ge->ix3 - ge->prev->ix3; - k = (a == 0) ? (b == 0 ? 1. : 100000.) : fabs((double) b / (double) a); - - a = ge->iy1 - ge->prev->iy3; - b = ge->ix1 - ge->prev->ix3; - if(a == 0) { - if(b == 0) { - a = ge->iy2 - ge->prev->iy3; - b = ge->ix2 - ge->prev->ix3; - k1 = (a == 0) ? (b == 0 ? k : 100000.) : fabs((double) b / (double) a); - } else - k1 = FBIGVAL; - } else - k1 = fabs((double) b / (double) a); - - a = ge->iy3 - ge->iy2; - b = ge->ix3 - ge->ix2; - if(a == 0) { - if(b == 0) { - a = ge->iy3 - ge->iy1; - b = ge->ix3 - ge->ix1; - k2 = (a == 0) ? (b == 0 ? k : 100000.) : fabs((double) b / (double) a); - } else - k2 = FBIGVAL; - } else - k2 = fabs((double) b / (double) a); - - if(fabs(k1-k) < 0.0001) - dir |= CVDIR_FEQUAL; - else if (k1 < k) - dir |= CVDIR_FUP; - else - dir |= CVDIR_FDOWN; - - if(fabs(k2-k) < 0.0001) - dir |= CVDIR_REQUAL; - else if (k2 > k) - dir |= CVDIR_RUP; - else - dir |= CVDIR_RDOWN; - - return dir; -} - -#if 0 -/* a function just to test the work of fixcvdir() */ -static void -testfixcvdir( - GLYPH * g -) -{ - GENTRY *ge; - int dir; - - for (ge = g->entries; ge != 0; ge = ge->next) { - if (ge->type == GE_CURVE) { - dir = igetcvdir(ge); - fixcvdir(ge, dir); - } - } -} -#endif - -static int -iround( - double val -) -{ - return (int) (val > 0 ? val + 0.5 : val - 0.5); -} - -/* for debugging - dump the glyph - * mark with a star the entries from start to end inclusive - * (start == NULL means don't mark any, end == NULL means to the last) - */ - -void -dumppaths( - GLYPH *g, - GENTRY *start, - GENTRY *end -) -{ - GENTRY *ge; - int i; - char mark=' '; - - fprintf(stderr, "Glyph %s:\n", g->name); - - /* now do the conversion */ - for(ge = g->entries; ge != 0; ge = ge->next) { - if(ge == start) - mark = '*'; - fprintf(stderr, " %c %8x", mark, ge); - switch(ge->type) { - case GE_MOVE: - case GE_LINE: - if(ge->flags & GEF_FLOAT) - fprintf(stderr," %c float (%g, %g)\n", ge->type, ge->fx3, ge->fy3); - else - fprintf(stderr," %c int (%d, %d)\n", ge->type, ge->ix3, ge->iy3); - break; - case GE_CURVE: - if(ge->flags & GEF_FLOAT) { - fprintf(stderr," C float "); - for(i=0; i<3; i++) - fprintf(stderr,"(%g, %g) ", ge->fxn[i], ge->fyn[i]); - fprintf(stderr,"\n"); - } else { - fprintf(stderr," C int "); - for(i=0; i<3; i++) - fprintf(stderr,"(%d, %d) ", ge->ixn[i], ge->iyn[i]); - fprintf(stderr,"\n"); - } - break; - default: - fprintf(stderr, " %c\n", ge->type); - break; - } - if(ge == end) - mark = ' '; - } -} - -/* - * Routine that converts all entries in the path from float to int - */ - -void -pathtoint( - GLYPH *g -) -{ - GENTRY *ge; - int x[3], y[3]; - int i; - - - if(ISDBG(TOINT)) - fprintf(stderr, "TOINT: glyph %s\n", g->name); - assertisfloat(g, "converting path to int\n"); - - fdelsmall(g, 1.0); /* get rid of sub-pixel contours */ - assertpath(g->entries, __FILE__, __LINE__, g->name); - - /* 1st pass, collect the directions of the curves: have - * to do that in advance, while everyting is float - */ - for(ge = g->entries; ge != 0; ge = ge->next) { - if( !(ge->flags & GEF_FLOAT) ) { - fprintf(stderr, "**! glyphs %s has int entry, found in conversion to int\n", - g->name); - exit(1); - } - if(ge->type == GE_CURVE) { - ge->dir = fgetcvdir(ge); - } - } - - /* now do the conversion */ - for(ge = g->entries; ge != 0; ge = ge->next) { - switch(ge->type) { - case GE_MOVE: - case GE_LINE: - if(ISDBG(TOINT)) - fprintf(stderr," %c float x=%g y=%g\n", ge->type, ge->fx3, ge->fy3); - x[0] = iround(ge->fx3); - y[0] = iround(ge->fy3); - for(i=0; i<3; i++) { /* put some valid values everywhere, for convenience */ - ge->ixn[i] = x[0]; - ge->iyn[i] = y[0]; - } - if(ISDBG(TOINT)) - fprintf(stderr," int x=%d y=%d\n", ge->ix3, ge->iy3); - break; - case GE_CURVE: - if(ISDBG(TOINT)) - fprintf(stderr," %c float ", ge->type); - - for(i=0; i<3; i++) { - if(ISDBG(TOINT)) - fprintf(stderr,"(%g, %g) ", ge->fxn[i], ge->fyn[i]); - x[i] = iround(ge->fxn[i]); - y[i] = iround(ge->fyn[i]); - } - - if(ISDBG(TOINT)) - fprintf(stderr,"\n int "); - - for(i=0; i<3; i++) { - ge->ixn[i] = x[i]; - ge->iyn[i] = y[i]; - if(ISDBG(TOINT)) - fprintf(stderr,"(%d, %d) ", ge->ixn[i], ge->iyn[i]); - } - ge->flags &= ~GEF_FLOAT; /* for fixcvdir */ - fixcvdir(ge, ge->dir); - - if(ISDBG(TOINT)) { - fprintf(stderr,"\n fixed "); - for(i=0; i<3; i++) - fprintf(stderr,"(%d, %d) ", ge->ixn[i], ge->iyn[i]); - fprintf(stderr,"\n"); - } - - break; - } - ge->flags &= ~GEF_FLOAT; - } - g->flags &= ~GF_FLOAT; -} - - -/* check whether we can fix up the curve to change its size by (dx,dy) */ -/* 0 means NO, 1 means YES */ - -/* for float: if scaling would be under 10% */ - -int -fcheckcv( - GENTRY * ge, - double dx, - double dy -) -{ - if( !(ge->flags & GEF_FLOAT) ) { - fprintf(stderr, "**! fcheckcv(0x%x) on int entry, ABORT\n", ge); - abort(); /* dump core */ - } - - if (ge->type != GE_CURVE) - return 0; - - if( fabs(ge->fx3 - ge->prev->fx3) < fabs(dx) * 10 ) - return 0; - - if( fabs(ge->fy3 - ge->prev->fy3) < fabs(dy) * 10 ) - return 0; - - return 1; -} - -/* for int: if won't create new zigzags at the ends */ - -int -icheckcv( - GENTRY * ge, - int dx, - int dy -) -{ - int xdep, ydep; - - if(ge->flags & GEF_FLOAT) { - fprintf(stderr, "**! icheckcv(0x%x) on floating entry, ABORT\n", ge); - abort(); /* dump core */ - } - - if (ge->type != GE_CURVE) - return 0; - - xdep = ge->ix3 - ge->prev->ix3; - ydep = ge->iy3 - ge->prev->iy3; - - if (ge->type == GE_CURVE - && (xdep * (xdep + dx)) > 0 - && (ydep * (ydep + dy)) > 0) { - return 1; - } else - return 0; -} - -/* float connect the ends of open contours */ - -void -fclosepaths( - GLYPH * g -) -{ - GENTRY *ge, *fge, *xge, *nge; - int i; - - assertisfloat(g, "fclosepaths float\n"); - - for (xge = g->entries; xge != 0; xge = xge->next) { - if( xge->type != GE_PATH ) - continue; - - ge = xge->prev; - if(ge == 0 || (ge->type != GE_LINE && ge->type!= GE_CURVE)) { - fprintf(stderr, "**! Glyph %s got empty path\n", - g->name); - exit(1); - } - - fge = ge->frwd; - if (fge->prev == 0 || fge->prev->type != GE_MOVE) { - fprintf(stderr, "**! Glyph %s got strange beginning of path\n", - g->name); - exit(1); - } - fge = fge->prev; - if (fge->fx3 != ge->fx3 || fge->fy3 != ge->fy3) { - /* we have to fix this open path */ - - WARNING_4 fprintf(stderr, "Glyph %s got path open by dx=%g dy=%g\n", - g->name, fge->fx3 - ge->fx3, fge->fy3 - ge->fy3); - - - /* add a new line */ - nge = newgentry(GEF_FLOAT); - (*nge) = (*ge); - nge->fx3 = fge->fx3; - nge->fy3 = fge->fy3; - nge->type = GE_LINE; - - addgeafter(ge, nge); - - if (fabs(ge->fx3 - fge->fx3) <= 2 && fabs(ge->fy3 - fge->fy3) <= 2) { - /* - * small change, try to get rid of the new entry - */ - - double df[2]; - - for(i=0; i<2; i++) { - df[i] = ge->fpoints[i][2] - fge->fpoints[i][2]; - df[i] = fclosegap(nge, nge, i, df[i], NULL); - } - - if(df[0] == 0. && df[1] == 0.) { - /* closed gap successfully, remove the added entry */ - freethisge(nge); - } - } - } - } -} - -void -smoothjoints( - GLYPH * g -) -{ - GENTRY *ge, *ne; - int dx1, dy1, dx2, dy2, k; - int dir; - - return; /* this stuff seems to create problems */ - - assertisint(g, "smoothjoints int"); - - if (g->entries == 0) /* nothing to do */ - return; - - for (ge = g->entries->next; ge != 0; ge = ge->next) { - ne = ge->frwd; - - /* - * although there should be no one-line path * and any path - * must end with CLOSEPATH, * nobody can say for sure - */ - - if (ge == ne || ne == 0) - continue; - - /* now handle various joints */ - - if (ge->type == GE_LINE && ne->type == GE_LINE) { - dx1 = ge->ix3 - ge->prev->ix3; - dy1 = ge->iy3 - ge->prev->iy3; - dx2 = ne->ix3 - ge->ix3; - dy2 = ne->iy3 - ge->iy3; - - /* check whether they have the same direction */ - /* and the same slope */ - /* then we can join them into one line */ - - if (dx1 * dx2 >= 0 && dy1 * dy2 >= 0 && dx1 * dy2 == dy1 * dx2) { - /* extend the previous line */ - ge->ix3 = ne->ix3; - ge->iy3 = ne->iy3; - - /* and get rid of the next line */ - freethisge(ne); - } - } else if (ge->type == GE_LINE && ne->type == GE_CURVE) { - fixcvends(ne); - - dx1 = ge->ix3 - ge->prev->ix3; - dy1 = ge->iy3 - ge->prev->iy3; - dx2 = ne->ix1 - ge->ix3; - dy2 = ne->iy1 - ge->iy3; - - /* if the line is nearly horizontal and we can fix it */ - if (dx1 != 0 && 5 * abs(dy1) / abs(dx1) == 0 - && icheckcv(ne, 0, -dy1) - && abs(dy1) <= 4) { - dir = igetcvdir(ne); - ge->iy3 -= dy1; - ne->iy1 -= dy1; - fixcvdir(ne, dir); - if (ge->next != ne) - ne->prev->iy3 -= dy1; - dy1 = 0; - } else if (dy1 != 0 && 5 * abs(dx1) / abs(dy1) == 0 - && icheckcv(ne, -dx1, 0) - && abs(dx1) <= 4) { - /* the same but vertical */ - dir = igetcvdir(ne); - ge->ix3 -= dx1; - ne->ix1 -= dx1; - fixcvdir(ne, dir); - if (ge->next != ne) - ne->prev->ix3 -= dx1; - dx1 = 0; - } - /* - * if line is horizontal and curve begins nearly - * horizontally - */ - if (dy1 == 0 && dx2 != 0 && 5 * abs(dy2) / abs(dx2) == 0) { - dir = igetcvdir(ne); - ne->iy1 -= dy2; - fixcvdir(ne, dir); - dy2 = 0; - } else if (dx1 == 0 && dy2 != 0 && 5 * abs(dx2) / abs(dy2) == 0) { - /* the same but vertical */ - dir = igetcvdir(ne); - ne->ix1 -= dx2; - fixcvdir(ne, dir); - dx2 = 0; - } - } else if (ge->type == GE_CURVE && ne->type == GE_LINE) { - fixcvends(ge); - - dx1 = ge->ix3 - ge->ix2; - dy1 = ge->iy3 - ge->iy2; - dx2 = ne->ix3 - ge->ix3; - dy2 = ne->iy3 - ge->iy3; - - /* if the line is nearly horizontal and we can fix it */ - if (dx2 != 0 && 5 * abs(dy2) / abs(dx2) == 0 - && icheckcv(ge, 0, dy2) - && abs(dy2) <= 4) { - dir = igetcvdir(ge); - ge->iy3 += dy2; - ge->iy2 += dy2; - fixcvdir(ge, dir); - if (ge->next != ne) - ne->prev->iy3 += dy2; - dy2 = 0; - } else if (dy2 != 0 && 5 * abs(dx2) / abs(dy2) == 0 - && icheckcv(ge, dx2, 0) - && abs(dx2) <= 4) { - /* the same but vertical */ - dir = igetcvdir(ge); - ge->ix3 += dx2; - ge->ix2 += dx2; - fixcvdir(ge, dir); - if (ge->next != ne) - ne->prev->ix3 += dx2; - dx2 = 0; - } - /* - * if line is horizontal and curve ends nearly - * horizontally - */ - if (dy2 == 0 && dx1 != 0 && 5 * abs(dy1) / abs(dx1) == 0) { - dir = igetcvdir(ge); - ge->iy2 += dy1; - fixcvdir(ge, dir); - dy1 = 0; - } else if (dx2 == 0 && dy1 != 0 && 5 * abs(dx1) / abs(dy1) == 0) { - /* the same but vertical */ - dir = igetcvdir(ge); - ge->ix2 += dx1; - fixcvdir(ge, dir); - dx1 = 0; - } - } else if (ge->type == GE_CURVE && ne->type == GE_CURVE) { - fixcvends(ge); - fixcvends(ne); - - dx1 = ge->ix3 - ge->ix2; - dy1 = ge->iy3 - ge->iy2; - dx2 = ne->ix1 - ge->ix3; - dy2 = ne->iy1 - ge->iy3; - - /* - * check if we have a rather smooth joint at extremal - * point - */ - /* left or right extremal point */ - if (abs(dx1) <= 4 && abs(dx2) <= 4 - && dy1 != 0 && 5 * abs(dx1) / abs(dy1) == 0 - && dy2 != 0 && 5 * abs(dx2) / abs(dy2) == 0 - && ((ge->iy3 < ge->prev->iy3 && ne->iy3 < ge->iy3) - || (ge->iy3 > ge->prev->iy3 && ne->iy3 > ge->iy3)) - && (ge->ix3 - ge->prev->ix3) * (ne->ix3 - ge->ix3) < 0 - ) { - dir = igetcvdir(ge); - ge->ix2 += dx1; - dx1 = 0; - fixcvdir(ge, dir); - dir = igetcvdir(ne); - ne->ix1 -= dx2; - dx2 = 0; - fixcvdir(ne, dir); - } - /* top or down extremal point */ - else if (abs(dy1) <= 4 && abs(dy2) <= 4 - && dx1 != 0 && 5 * abs(dy1) / abs(dx1) == 0 - && dx2 != 0 && 5 * abs(dy2) / abs(dx2) == 0 - && ((ge->ix3 < ge->prev->ix3 && ne->ix3 < ge->ix3) - || (ge->ix3 > ge->prev->ix3 && ne->ix3 > ge->ix3)) - && (ge->iy3 - ge->prev->iy3) * (ne->iy3 - ge->iy3) < 0 - ) { - dir = igetcvdir(ge); - ge->iy2 += dy1; - dy1 = 0; - fixcvdir(ge, dir); - dir = igetcvdir(ne); - ne->iy1 -= dy2; - dy2 = 0; - fixcvdir(ne, dir); - } - /* or may be we just have a smooth junction */ - else if (dx1 * dx2 >= 0 && dy1 * dy2 >= 0 - && 10 * abs(k = abs(dx1 * dy2) - abs(dy1 * dx2)) < (abs(dx1 * dy2) + abs(dy1 * dx2))) { - int tries[6][4]; - int results[6]; - int i, b; - - /* build array of changes we are going to try */ - /* uninitalized entries are 0 */ - if (k > 0) { - static int t1[6][4] = { - {0, 0, 0, 0}, - {-1, 0, 1, 0}, - {-1, 0, 0, 1}, - {0, -1, 1, 0}, - {0, -1, 0, 1}, - {-1, -1, 1, 1}}; - memcpy(tries, t1, sizeof tries); - } else { - static int t1[6][4] = { - {0, 0, 0, 0}, - {1, 0, -1, 0}, - {1, 0, 0, -1}, - {0, 1, -1, 0}, - {0, 1, 0, -1}, - {1, 1, -1, -1}}; - memcpy(tries, t1, sizeof tries); - } - - /* now try the changes */ - results[0] = abs(k); - for (i = 1; i < 6; i++) { - results[i] = abs((abs(dx1) + tries[i][0]) * (abs(dy2) + tries[i][1]) - - (abs(dy1) + tries[i][2]) * (abs(dx2) + tries[i][3])); - } - - /* and find the best try */ - k = abs(k); - b = 0; - for (i = 1; i < 6; i++) - if (results[i] < k) { - k = results[i]; - b = i; - } - /* and finally apply it */ - if (dx1 < 0) - tries[b][0] = -tries[b][0]; - if (dy2 < 0) - tries[b][1] = -tries[b][1]; - if (dy1 < 0) - tries[b][2] = -tries[b][2]; - if (dx2 < 0) - tries[b][3] = -tries[b][3]; - - dir = igetcvdir(ge); - ge->ix2 -= tries[b][0]; - ge->iy2 -= tries[b][2]; - fixcvdir(ge, dir); - dir = igetcvdir(ne); - ne->ix1 += tries[b][3]; - ne->iy1 += tries[b][1]; - fixcvdir(ne, dir); - } - } - } -} - -/* debugging: print out stems of a glyph */ -static void -debugstems( - char *name, - STEM * hstems, - int nhs, - STEM * vstems, - int nvs -) -{ - int i; - - fprintf(pfa_file, "%% %s\n", name); - fprintf(pfa_file, "%% %d horizontal stems:\n", nhs); - for (i = 0; i < nhs; i++) - fprintf(pfa_file, "%% %3d %d (%d...%d) %c %c%c%c%c\n", i, hstems[i].value, - hstems[i].from, hstems[i].to, - ((hstems[i].flags & ST_UP) ? 'U' : 'D'), - ((hstems[i].flags & ST_END) ? 'E' : '-'), - ((hstems[i].flags & ST_FLAT) ? 'F' : '-'), - ((hstems[i].flags & ST_ZONE) ? 'Z' : ' '), - ((hstems[i].flags & ST_TOPZONE) ? 'T' : ' ')); - fprintf(pfa_file, "%% %d vertical stems:\n", nvs); - for (i = 0; i < nvs; i++) - fprintf(pfa_file, "%% %3d %d (%d...%d) %c %c%c\n", i, vstems[i].value, - vstems[i].from, vstems[i].to, - ((vstems[i].flags & ST_UP) ? 'U' : 'D'), - ((vstems[i].flags & ST_END) ? 'E' : '-'), - ((vstems[i].flags & ST_FLAT) ? 'F' : '-')); -} - -/* add pseudo-stems for the limits of the Blue zones to the stem array */ -static int -addbluestems( - STEM *s, - int n -) -{ - int i; - - for(i=0; i<nblues && i<2; i+=2) { /* baseline */ - s[n].value=bluevalues[i]; - s[n].flags=ST_UP|ST_ZONE; - /* don't overlap with anything */ - s[n].origin=s[n].from=s[n].to= -10000+i; - n++; - s[n].value=bluevalues[i+1]; - s[n].flags=ST_ZONE; - /* don't overlap with anything */ - s[n].origin=s[n].from=s[n].to= -10000+i+1; - n++; - } - for(i=2; i<nblues; i+=2) { /* top zones */ - s[n].value=bluevalues[i]; - s[n].flags=ST_UP|ST_ZONE|ST_TOPZONE; - /* don't overlap with anything */ - s[n].origin=s[n].from=s[n].to= -10000+i; - n++; - s[n].value=bluevalues[i+1]; - s[n].flags=ST_ZONE|ST_TOPZONE; - /* don't overlap with anything */ - s[n].origin=s[n].from=s[n].to= -10000+i+1; - n++; - } - for(i=0; i<notherb; i+=2) { /* bottom zones */ - s[n].value=otherblues[i]; - s[n].flags=ST_UP|ST_ZONE; - /* don't overlap with anything */ - s[n].origin=s[n].from=s[n].to= -10000+i+nblues; - n++; - s[n].value=otherblues[i+1]; - s[n].flags=ST_ZONE; - /* don't overlap with anything */ - s[n].origin=s[n].from=s[n].to= -10000+i+1+nblues; - n++; - } - return n; -} - -/* sort stems in array */ -static void -sortstems( - STEM * s, - int n -) -{ - int i, j; - STEM x; - - - /* a simple sorting */ - /* hm, the ordering criteria are not quite simple :-) - * if the values are tied - * ST_UP always goes under not ST_UP - * ST_ZONE goes on the most outer side - * ST_END goes towards inner side after ST_ZONE - * ST_FLAT goes on the inner side - */ - - for (i = 0; i < n; i++) - for (j = i + 1; j < n; j++) { - if(s[i].value < s[j].value) - continue; - if(s[i].value == s[j].value) { - if( (s[i].flags & ST_UP) < (s[j].flags & ST_UP) ) - continue; - if( (s[i].flags & ST_UP) == (s[j].flags & ST_UP) ) { - if( s[i].flags & ST_UP ) { - if( - ((s[i].flags & (ST_ZONE|ST_FLAT|ST_END)) ^ ST_FLAT) - > - ((s[j].flags & (ST_ZONE|ST_FLAT|ST_END)) ^ ST_FLAT) - ) - continue; - } else { - if( - ((s[i].flags & (ST_ZONE|ST_FLAT|ST_END)) ^ ST_FLAT) - < - ((s[j].flags & (ST_ZONE|ST_FLAT|ST_END)) ^ ST_FLAT) - ) - continue; - } - } - } - x = s[j]; - s[j] = s[i]; - s[i] = x; - } -} - -/* check whether two stem borders overlap */ - -static int -stemoverlap( - STEM * s1, - STEM * s2 -) -{ - int result; - - if ((s1->from <= s2->from && s1->to >= s2->from) - || (s2->from <= s1->from && s2->to >= s1->from)) - result = 1; - else - result = 0; - - if (ISDBG(STEMOVERLAP)) - fprintf(pfa_file, "%% overlap %d(%d..%d)x%d(%d..%d)=%d\n", - s1->value, s1->from, s1->to, s2->value, s2->from, s2->to, result); - return result; -} - -/* - * check if the stem [border] is in an appropriate blue zone - * (currently not used) - */ - -static int -steminblue( - STEM *s -) -{ - int i, val; - - val=s->value; - if(s->flags & ST_UP) { - /* painted size up, look at lower zones */ - if(nblues>=2 && val>=bluevalues[0] && val<=bluevalues[1] ) - return 1; - for(i=0; i<notherb; i++) { - if( val>=otherblues[i] && val<=otherblues[i+1] ) - return 1; - } - } else { - /* painted side down, look at upper zones */ - for(i=2; i<nblues; i++) { - if( val>=bluevalues[i] && val<=bluevalues[i+1] ) - return 1; - } - } - - return 0; -} - -/* mark the outermost stem [borders] in the blue zones */ - -static void -markbluestems( - STEM *s, - int nold -) -{ - int i, j, a, b, c; - /* - * traverse the list of Blue Values, mark the lowest upper - * stem in each bottom zone and the topmost lower stem in - * each top zone with ST_BLUE - */ - - /* top zones */ - for(i=2; i<nblues; i+=2) { - a=bluevalues[i]; b=bluevalues[i+1]; - if(ISDBG(BLUESTEMS)) - fprintf(pfa_file, "%% looking at blue zone %d...%d\n", a, b); - for(j=nold-1; j>=0; j--) { - if( s[j].flags & (ST_ZONE|ST_UP|ST_END) ) - continue; - c=s[j].value; - if(c<a) /* too low */ - break; - if(c<=b) { /* found the topmost stem border */ - /* mark all the stems with the same value */ - if(ISDBG(BLUESTEMS)) - fprintf(pfa_file, "%% found D BLUE at %d\n", s[j].value); - /* include ST_END values */ - while( s[j+1].value==c && (s[j+1].flags & ST_ZONE)==0 ) - j++; - s[j].flags |= ST_BLUE; - for(j--; j>=0 && s[j].value==c - && (s[j].flags & (ST_UP|ST_ZONE))==0 ; j--) - s[j].flags |= ST_BLUE; - break; - } - } - } - /* baseline */ - if(nblues>=2) { - a=bluevalues[0]; b=bluevalues[1]; - for(j=0; j<nold; j++) { - if( (s[j].flags & (ST_ZONE|ST_UP|ST_END))!=ST_UP ) - continue; - c=s[j].value; - if(c>b) /* too high */ - break; - if(c>=a) { /* found the lowest stem border */ - /* mark all the stems with the same value */ - if(ISDBG(BLUESTEMS)) - fprintf(pfa_file, "%% found U BLUE at %d\n", s[j].value); - /* include ST_END values */ - while( s[j-1].value==c && (s[j-1].flags & ST_ZONE)==0 ) - j--; - s[j].flags |= ST_BLUE; - for(j++; j<nold && s[j].value==c - && (s[j].flags & (ST_UP|ST_ZONE))==ST_UP ; j++) - s[j].flags |= ST_BLUE; - break; - } - } - } - /* bottom zones: the logic is the same as for baseline */ - for(i=0; i<notherb; i+=2) { - a=otherblues[i]; b=otherblues[i+1]; - for(j=0; j<nold; j++) { - if( (s[j].flags & (ST_UP|ST_ZONE|ST_END))!=ST_UP ) - continue; - c=s[j].value; - if(c>b) /* too high */ - break; - if(c>=a) { /* found the lowest stem border */ - /* mark all the stems with the same value */ - if(ISDBG(BLUESTEMS)) - fprintf(pfa_file, "%% found U BLUE at %d\n", s[j].value); - /* include ST_END values */ - while( s[j-1].value==c && (s[j-1].flags & ST_ZONE)==0 ) - j--; - s[j].flags |= ST_BLUE; - for(j++; j<nold && s[j].value==c - && (s[j].flags & (ST_UP|ST_ZONE))==ST_UP ; j++) - s[j].flags |= ST_BLUE; - break; - } - } - } -} - -/* Eliminate invalid stems, join equivalent lines and remove nested stems - * to build the main (non-substituted) set of stems. - * XXX add consideration of the italic angle - */ -static int -joinmainstems( - STEM * s, - int nold, - int useblues /* do we use the blue values ? */ -) -{ -#define MAX_STACK 1000 - STEM stack[MAX_STACK]; - int nstack = 0; - int sbottom = 0; - int nnew; - int i, j, k; - int a, b, c, w1, w2, w3; - int fw, fd; - /* - * priority of the last found stem: - * 0 - nothing found yet - * 1 - has ST_END in it (one or more) - * 2 - has no ST_END and no ST_FLAT, can override only one stem - * with priority 1 - * 3 - has no ST_END and at least one ST_FLAT, can override one - * stem with priority 2 or any number of stems with priority 1 - * 4 (handled separately) - has ST_BLUE, can override anything - */ - int readystem = 0; - int pri; - int nlps = 0; /* number of non-committed - * lowest-priority stems */ - - - for (i = 0, nnew = 0; i < nold; i++) { - if (s[i].flags & (ST_UP|ST_ZONE)) { - if(s[i].flags & ST_BLUE) { - /* we just HAVE to use this value */ - if (readystem) - nnew += 2; - readystem=0; - - /* remember the list of Blue zone stems with the same value */ - for(a=i, i++; i<nold && s[a].value==s[i].value - && (s[i].flags & ST_BLUE); i++) - {} - b=i; /* our range is a <= i < b */ - c= -1; /* index of our best guess up to now */ - pri=0; - /* try to find a match, don't cross blue zones */ - for(; i<nold && (s[i].flags & ST_BLUE)==0; i++) { - if(s[i].flags & ST_UP) { - if(s[i].flags & ST_TOPZONE) - break; - else - continue; - } - for(j=a; j<b; j++) { - if(!stemoverlap(&s[j], &s[i]) ) - continue; - /* consider priorities */ - if( ( (s[j].flags|s[i].flags) & (ST_FLAT|ST_END) )==ST_FLAT ) { - c=i; - goto bluematch; - } - if( ((s[j].flags|s[i].flags) & ST_END)==0 ) { - if(pri < 2) { - c=i; pri=2; - } - } else { - if(pri == 0) { - c=i; pri=1; - } - } - } - } - bluematch: - /* clean up the stack */ - nstack=sbottom=0; - readystem=0; - /* add this stem */ - s[nnew++]=s[a]; - if(c<0) { /* make one-dot-wide stem */ - if(nnew>=b) { /* have no free space */ - for(j=nold; j>=b; j--) /* make free space */ - s[j]=s[j-1]; - b++; - nold++; - } - s[nnew]=s[a]; - s[nnew].flags &= ~(ST_UP|ST_BLUE); - nnew++; - i=b-1; - } else { - s[nnew++]=s[c]; - i=c; /* skip up to this point */ - } - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% +stem %d...%d U BLUE\n", - s[nnew-2].value, s[nnew-1].value); - } else { - if (nstack >= MAX_STACK) { - WARNING_1 fprintf(stderr, "Warning: **** converter's stem stack overflow ****\n"); - nstack = 0; - } - stack[nstack++] = s[i]; - } - } else if(s[i].flags & ST_BLUE) { - /* again, we just HAVE to use this value */ - if (readystem) - nnew += 2; - readystem=0; - - /* remember the list of Blue zone stems with the same value */ - for(a=i, i++; i<nold && s[a].value==s[i].value - && (s[i].flags & ST_BLUE); i++) - {} - b=i; /* our range is a <= i < b */ - c= -1; /* index of our best guess up to now */ - pri=0; - /* try to find a match */ - for (i = nstack - 1; i >= 0; i--) { - if( (stack[i].flags & ST_UP)==0 ) { - if( (stack[i].flags & (ST_ZONE|ST_TOPZONE))==ST_ZONE ) - break; - else - continue; - } - for(j=a; j<b; j++) { - if(!stemoverlap(&s[j], &stack[i]) ) - continue; - /* consider priorities */ - if( ( (s[j].flags|stack[i].flags) & (ST_FLAT|ST_END) )==ST_FLAT ) { - c=i; - goto bluedownmatch; - } - if( ((s[j].flags|stack[i].flags) & ST_END)==0 ) { - if(pri < 2) { - c=i; pri=2; - } - } else { - if(pri == 0) { - c=i; pri=1; - } - } - } - } - bluedownmatch: - /* if found no match make a one-dot-wide stem */ - if(c<0) { - c=0; - stack[0]=s[b-1]; - stack[0].flags |= ST_UP; - stack[0].flags &= ~ST_BLUE; - } - /* remove all the stems conflicting with this one */ - readystem=0; - for(j=nnew-2; j>=0; j-=2) { - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% ?stem %d...%d -- %d\n", - s[j].value, s[j+1].value, stack[c].value); - if(s[j+1].value < stack[c].value) /* no conflict */ - break; - if(s[j].flags & ST_BLUE) { - /* oops, we don't want to spoil other blue zones */ - stack[c].value=s[j+1].value+1; - break; - } - if( (s[j].flags|s[j+1].flags) & ST_END ) { - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% -stem %d...%d p=1\n", - s[j].value, s[j+1].value); - continue; /* pri==1, silently discard it */ - } - /* we want to discard no nore than 2 stems of pri>=2 */ - if( ++readystem > 2 ) { - /* change our stem to not conflict */ - stack[c].value=s[j+1].value+1; - break; - } else { - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% -stem %d...%d p>=2\n", - s[j].value, s[j+1].value); - continue; - } - } - nnew=j+2; - /* add this stem */ - if(nnew>=b-1) { /* have no free space */ - for(j=nold; j>=b-1; j--) /* make free space */ - s[j]=s[j-1]; - b++; - nold++; - } - s[nnew++]=stack[c]; - s[nnew++]=s[b-1]; - /* clean up the stack */ - nstack=sbottom=0; - readystem=0; - /* set the next position to search */ - i=b-1; - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% +stem %d...%d D BLUE\n", - s[nnew-2].value, s[nnew-1].value); - } else if (nstack > 0) { - - /* - * check whether our stem overlaps with anything in - * stack - */ - for (j = nstack - 1; j >= sbottom; j--) { - if (s[i].value <= stack[j].value) - break; - if (stack[j].flags & ST_ZONE) - continue; - - if ((s[i].flags & ST_END) - || (stack[j].flags & ST_END)) - pri = 1; - else if ((s[i].flags & ST_FLAT) - || (stack[j].flags & ST_FLAT)) - pri = 3; - else - pri = 2; - - if ((pri < readystem && s[nnew + 1].value >= stack[j].value) - || !stemoverlap(&stack[j], &s[i])) - continue; - - if (readystem > 1 && s[nnew + 1].value < stack[j].value) { - nnew += 2; - readystem = 0; - nlps = 0; - } - /* - * width of the previous stem (if it's - * present) - */ - w1 = s[nnew + 1].value - s[nnew].value; - - /* width of this stem */ - w2 = s[i].value - stack[j].value; - - if (readystem == 0) { - /* nothing yet, just add a new stem */ - s[nnew] = stack[j]; - s[nnew + 1] = s[i]; - readystem = pri; - if (pri == 1) - nlps = 1; - else if (pri == 2) - sbottom = j; - else { - sbottom = j + 1; - while (sbottom < nstack - && stack[sbottom].value <= stack[j].value) - sbottom++; - } - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n", - stack[j].value, s[i].value, pri, nlps); - } else if (pri == 1) { - if (stack[j].value > s[nnew + 1].value) { - /* - * doesn't overlap with the - * previous one - */ - nnew += 2; - nlps++; - s[nnew] = stack[j]; - s[nnew + 1] = s[i]; - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n", - stack[j].value, s[i].value, pri, nlps); - } else if (w2 < w1) { - /* is narrower */ - s[nnew] = stack[j]; - s[nnew + 1] = s[i]; - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d %d->%d\n", - stack[j].value, s[i].value, pri, nlps, w1, w2); - } - } else if (pri == 2) { - if (readystem == 2) { - /* choose the narrower stem */ - if (w1 > w2) { - s[nnew] = stack[j]; - s[nnew + 1] = s[i]; - sbottom = j; - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d\n", - stack[j].value, s[i].value, pri, nlps); - } - /* else readystem==1 */ - } else if (stack[j].value > s[nnew + 1].value) { - /* - * value doesn't overlap with - * the previous one - */ - nnew += 2; - nlps = 0; - s[nnew] = stack[j]; - s[nnew + 1] = s[i]; - sbottom = j; - readystem = pri; - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n", - stack[j].value, s[i].value, pri, nlps); - } else if (nlps == 1 - || stack[j].value > s[nnew - 1].value) { - /* - * we can replace the top - * stem - */ - nlps = 0; - s[nnew] = stack[j]; - s[nnew + 1] = s[i]; - readystem = pri; - sbottom = j; - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d\n", - stack[j].value, s[i].value, pri, nlps); - } - } else if (readystem == 3) { /* that means also - * pri==3 */ - /* choose the narrower stem */ - if (w1 > w2) { - s[nnew] = stack[j]; - s[nnew + 1] = s[i]; - sbottom = j + 1; - while (sbottom < nstack - && stack[sbottom].value <= stack[j].value) - sbottom++; - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d\n", - stack[j].value, s[i].value, pri, nlps); - } - } else if (pri == 3) { - /* - * we can replace as many stems as - * neccessary - */ - nnew += 2; - while (nnew > 0 && s[nnew - 1].value >= stack[j].value) { - nnew -= 2; - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% -stem %d..%d\n", - s[nnew].value, s[nnew + 1].value); - } - nlps = 0; - s[nnew] = stack[j]; - s[nnew + 1] = s[i]; - readystem = pri; - sbottom = j + 1; - while (sbottom < nstack - && stack[sbottom].value <= stack[j].value) - sbottom++; - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n", - stack[j].value, s[i].value, pri, nlps); - } - } - } - } - if (readystem) - nnew += 2; - - /* change the 1-pixel-wide stems to 20-pixel-wide stems if possible - * the constant 20 is recommended in the Type1 manual - */ - if(useblues) { - for(i=0; i<nnew; i+=2) { - if(s[i].value != s[i+1].value) - continue; - if( ((s[i].flags ^ s[i+1].flags) & ST_BLUE)==0 ) - continue; - if( s[i].flags & ST_BLUE ) { - if(nnew>i+2 && s[i+2].value<s[i].value+22) - s[i+1].value=s[i+2].value-2; /* compensate for fuzziness */ - else - s[i+1].value+=20; - } else { - if(i>0 && s[i-1].value>s[i].value-22) - s[i].value=s[i-1].value+2; /* compensate for fuzziness */ - else - s[i].value-=20; - } - } - } - /* make sure that no stem it stretched between - * a top zone and a bottom zone - */ - if(useblues) { - for(i=0; i<nnew; i+=2) { - a=10000; /* lowest border of top zone crosing the stem */ - b= -10000; /* highest border of bottom zone crossing the stem */ - - for(j=2; j<nblues; j++) { - c=bluevalues[j]; - if( c>=s[i].value && c<=s[i+1].value && c<a ) - a=c; - } - if(nblues>=2) { - c=bluevalues[1]; - if( c>=s[i].value && c<=s[i+1].value && c>b ) - b=c; - } - for(j=1; j<notherb; j++) { - c=otherblues[j]; - if( c>=s[i].value && c<=s[i+1].value && c>b ) - b=c; - } - if( a!=10000 && b!= -10000 ) { /* it is stretched */ - /* split the stem into 2 ghost stems */ - for(j=nnew+1; j>i+1; j--) /* make free space */ - s[j]=s[j-2]; - nnew+=2; - - if(s[i].value+22 >= a) - s[i+1].value=a-2; /* leave space for fuzziness */ - else - s[i+1].value=s[i].value+20; - - if(s[i+3].value-22 <= b) - s[i+2].value=b+2; /* leave space for fuzziness */ - else - s[i+2].value=s[i+3].value-20; - - i+=2; - } - } - } - /* look for triple stems */ - for (i = 0; i < nnew; i += 2) { - if (nnew - i >= 6) { - a = s[i].value + s[i + 1].value; - b = s[i + 2].value + s[i + 3].value; - c = s[i + 4].value + s[i + 5].value; - - w1 = s[i + 1].value - s[i].value; - w2 = s[i + 3].value - s[i + 2].value; - w3 = s[i + 5].value - s[i + 4].value; - - fw = w3 - w1; /* fuzz in width */ - fd = ((c - b) - (b - a)); /* fuzz in distance - * (doubled) */ - - /* we are able to handle some fuzz */ - /* - * it doesn't hurt if the declared stem is a bit - * narrower than actual unless it's an edge in - * a blue zone - */ - if (abs(abs(fd) - abs(fw)) * 5 < w2 - && abs(fw) * 20 < (w1 + w3)) { /* width dirrerence <10% */ - - if(useblues) { /* check that we don't disturb any blue stems */ - j=c; k=a; - if (fw > 0) { - if (fd > 0) { - if( s[i+5].flags & ST_BLUE ) - continue; - j -= fw; - } else { - if( s[i+4].flags & ST_BLUE ) - continue; - j += fw; - } - } else if(fw < 0) { - if (fd > 0) { - if( s[i+1].flags & ST_BLUE ) - continue; - k -= fw; - } else { - if( s[i].flags & ST_BLUE ) - continue; - k += fw; - } - } - pri = ((j - b) - (b - k)); - - if (pri > 0) { - if( s[i+2].flags & ST_BLUE ) - continue; - } else if(pri < 0) { - if( s[i+3].flags & ST_BLUE ) - continue; - } - } - - /* - * first fix up the width of 1st and 3rd - * stems - */ - if (fw > 0) { - if (fd > 0) { - s[i + 5].value -= fw; - c -= fw; - } else { - s[i + 4].value += fw; - c += fw; - } - } else { - if (fd > 0) { - s[i + 1].value -= fw; - a -= fw; - } else { - s[i].value += fw; - a += fw; - } - } - fd = ((c - b) - (b - a)); - - if (fd > 0) { - s[i + 2].value += abs(fd) / 2; - } else { - s[i + 3].value -= abs(fd) / 2; - } - - s[i].flags |= ST_3; - i += 4; - } - } - } - - return (nnew & ~1); /* number of lines must be always even */ -} - -/* - * these macros and function allow to set the base stem, - * check that it's not empty and subtract another stem - * from the base stem (possibly dividing it into multiple parts) - */ - -/* pairs for pieces of the base stem */ -static short xbstem[MAX_STEMS*2]; -/* index of the last point */ -static int xblast= -1; - -#define setbasestem(from, to) \ - (xbstem[0]=from, xbstem[1]=to, xblast=1) -#define isbaseempty() (xblast<=0) - -/* returns 1 if was overlapping, 0 otherwise */ -static int -subfrombase( - int from, - int to -) -{ - int a, b; - int i, j; - - if(isbaseempty()) - return 0; - - /* handle the simple case simply */ - if(from > xbstem[xblast] || to < xbstem[0]) - return 0; - - /* the binary search may be more efficient */ - /* but for now the linear search is OK */ - for(b=1; from > xbstem[b]; b+=2) {} /* result: from <= xbstem[b] */ - for(a=xblast-1; to < xbstem[a]; a-=2) {} /* result: to >= xbstem[a] */ - - /* now the interesting examples are: - * (it was hard for me to understand, so I looked at the examples) - * 1 - * a|-----| |-----|b |-----| |-----| - * f|-----|t - * 2 - * a|-----|b |-----| |-----| |-----| - * f|--|t - * 3 - * a|-----|b |-----| |-----| |-----| - * f|-----|t - * 4 - * |-----|b a|-----| |-----| |-----| - * f|------------|t - * 5 - * |-----| |-----|b |-----| a|-----| - * f|-----------------------------|t - * 6 - * |-----|b |-----| |-----| a|-----| - * f|--------------------------------------------------|t - * 7 - * |-----|b |-----| a|-----| |-----| - * f|--------------------------|t - */ - - if(a < b-1) /* hits a gap - example 1 */ - return 0; - - /* now the subtraction itself */ - - if(a==b-1 && from > xbstem[a] && to < xbstem[b]) { - /* overlaps with only one subrange and splits it - example 2 */ - j=xblast; i=(xblast+=2); - while(j>=b) - xbstem[i--]=xbstem[j--]; - xbstem[b]=from-1; - xbstem[b+1]=to+1; - return 1; - /* becomes - * 2a - * a|b || |-----| |-----| |-----| - * f|--|t - */ - } - - if(xbstem[b-1] < from) { - /* cuts the back of this subrange - examples 3, 4, 7 */ - xbstem[b] = from-1; - b+=2; - /* becomes - * 3a - * a|----| |-----|b |-----| |-----| - * f|-----|t - * 4a - * |---| a|-----|b |-----| |-----| - * f|------------|t - * 7a - * |---| |-----|b a|-----| |-----| - * f|--------------------------|t - */ - } - - if(xbstem[a+1] > to) { - /* cuts the front of this subrange - examples 4a, 5, 7a */ - xbstem[a] = to+1; - a-=2; - /* becomes - * 4b - * a|---| |---|b |-----| |-----| - * f|------------|t - * 5b - * |-----| |-----|b a|-----| || - * f|-----------------------------|t - * 7b - * |---| a|-----|b || |-----| - * f|--------------------------|t - */ - } - - if(a < b-1) /* now after modification it hits a gap - examples 3a, 4b */ - return 1; /* because we have removed something */ - - /* now remove the subranges completely covered by the new stem */ - /* examples 5b, 6, 7b */ - i=b-1; j=a+2; - /* positioned as: - * 5b i j - * |-----| |-----|b a|-----| || - * f|-----------------------------|t - * 6 i xblast j - * |-----|b |-----| |-----| a|-----| - * f|--------------------------------------------------|t - * 7b i j - * |---| a|-----|b || |-----| - * f|--------------------------|t - */ - while(j <= xblast) - xbstem[i++]=xbstem[j++]; - xblast=i-1; - return 1; -} - -/* for debugging */ -static void -printbasestem(void) -{ - int i; - - printf("( "); - for(i=0; i<xblast; i+=2) - printf("%d-%d ", xbstem[i], xbstem[i+1]); - printf(") %d\n", xblast); -} - -/* - * Join the stem borders to build the sets of substituted stems - * XXX add consideration of the italic angle - */ -static void -joinsubstems( - STEM * s, - short *pairs, - int nold, - int useblues /* do we use the blue values ? */ -) -{ - int i, j, x; - static unsigned char mx[MAX_STEMS][MAX_STEMS]; - - /* we do the substituted groups of stems first - * and it looks like it's going to be REALLY SLOW - * AND PAINFUL but let's bother about it later - */ - - /* for the substituted stems we don't bother about [hv]stem3 - - * anyway the X11R6 rasterizer does not bother about hstem3 - * at all and is able to handle only one global vstem3 - * per glyph - */ - - /* clean the used part of matrix */ - for(i=0; i<nold; i++) - for(j=0; j<nold; j++) - mx[i][j]=0; - - /* build the matrix of stem pairs */ - for(i=0; i<nold; i++) { - if( s[i].flags & ST_ZONE ) - continue; - if(s[i].flags & ST_BLUE) - mx[i][i]=1; /* allow to pair with itself if no better pair */ - if(s[i].flags & ST_UP) { /* the down-stems are already matched */ - setbasestem(s[i].from, s[i].to); - for(j=i+1; j<nold; j++) { - if(s[i].value==s[j].value - || s[j].flags & ST_ZONE ) { - continue; - } - x=subfrombase(s[j].from, s[j].to); - - if(s[j].flags & ST_UP) /* match only up+down pairs */ - continue; - - mx[i][j]=mx[j][i]=x; - - if(isbaseempty()) /* nothing else to do */ - break; - } - } - } - - if(ISDBG(SUBSTEMS)) { - fprintf(pfa_file, "%% "); - for(j=0; j<nold; j++) - putc( j%10==0 ? '0'+(j/10)%10 : ' ', pfa_file); - fprintf(pfa_file, "\n%% "); - for(j=0; j<nold; j++) - putc('0'+j%10, pfa_file); - putc('\n', pfa_file); - for(i=0; i<nold; i++) { - fprintf(pfa_file, "%% %3d ",i); - for(j=0; j<nold; j++) - putc( mx[i][j] ? 'X' : '.', pfa_file); - putc('\n', pfa_file); - } - } - - /* now use the matrix to find the best pair for each stem */ - for(i=0; i<nold; i++) { - int pri, lastpri, v, f; - - x= -1; /* best pair: none */ - lastpri=0; - - v=s[i].value; - f=s[i].flags; - - if(f & ST_ZONE) { - pairs[i]= -1; - continue; - } - - if(f & ST_UP) { - for(j=i+1; j<nold; j++) { - if(mx[i][j]==0) - continue; - - if( (f | s[j].flags) & ST_END ) - pri=1; - else if( (f | s[j].flags) & ST_FLAT ) - pri=3; - else - pri=2; - - if(lastpri==0 - || ( pri > lastpri - && ( lastpri==1 || s[j].value-v<20 || (s[x].value-v)*2 >= s[j].value-v ) ) ) { - lastpri=pri; - x=j; - } - } - } else { - for(j=i-1; j>=0; j--) { - if(mx[i][j]==0) - continue; - - if( (f | s[j].flags) & ST_END ) - pri=1; - else if( (f | s[j].flags) & ST_FLAT ) - pri=3; - else - pri=2; - - if(lastpri==0 - || ( pri > lastpri - && ( lastpri==1 || v-s[j].value<20 || (v-s[x].value)*2 >= v-s[j].value ) ) ) { - lastpri=pri; - x=j; - } - } - } - if(x== -1 && mx[i][i]) - pairs[i]=i; /* a special case */ - else - pairs[i]=x; - } - - if(ISDBG(SUBSTEMS)) { - for(i=0; i<nold; i++) { - j=pairs[i]; - if(j>0) - fprintf(pfa_file, "%% %d...%d (%d x %d)\n", s[i].value, s[j].value, i, j); - } - } -} - -/* - * Make all the stems originating at the same value get the - * same width. Without this the rasterizer may move the dots - * randomly up or down by one pixel, and that looks bad. - * The prioritisation is the same as in findstemat(). - */ -static void -uniformstems( - STEM * s, - short *pairs, - int ns -) -{ - int i, j, from, to, val, dir; - int pri, prevpri[2], wd, prevwd[2], prevbest[2]; - - for(from=0; from<ns; from=to) { - prevpri[0] = prevpri[1] = 0; - prevwd[0] = prevwd[1] = 0; - prevbest[0] = prevbest[1] = -1; - val = s[from].value; - - for(to = from; to<ns && s[to].value == val; to++) { - dir = ((s[to].flags & ST_UP)!=0); - - i=pairs[to]; /* the other side of this stem */ - if(i<0 || i==to) - continue; /* oops, no other side */ - wd=abs(s[i].value-val); - if(wd == 0) - continue; - pri=1; - if( (s[to].flags | s[i].flags) & ST_END ) - pri=0; - if( prevbest[dir] == -1 || pri > prevpri[dir] || wd<prevwd[dir] ) { - prevbest[dir]=i; - prevpri[dir]=pri; - prevwd[dir]=wd; - } - } - - for(i=from; i<to; i++) { - dir = ((s[i].flags & ST_UP)!=0); - if(prevbest[dir] >= 0) { - if(ISDBG(SUBSTEMS)) { - fprintf(stderr, "at %d (%s %d) pair %d->%d(%d)\n", i, - (dir ? "UP":"DOWN"), s[i].value, pairs[i], prevbest[dir], - s[prevbest[dir]].value); - } - pairs[i] = prevbest[dir]; - } - } - } -} - -/* - * Find the best stem in the array at the specified (value, origin), - * related to the entry ge. - * Returns its index in the array sp, -1 means "none". - * prevbest is the result for the other end of the line, we must - * find something better than it or leave it as it is. - */ -static int -findstemat( - int value, - int origin, - GENTRY *ge, - STEM *sp, - short *pairs, - int ns, - int prevbest /* -1 means "none" */ -) -{ - int i, min, max; - int v, si; - int pri, prevpri; /* priority, 0 = has ST_END, 1 = no ST_END */ - int wd, prevwd; /* stem width */ - - si= -1; /* nothing yet */ - - /* stems are ordered by value, binary search */ - min=0; max=ns; /* min <= i < max */ - while( min < max ) { - i=(min+max)/2; - v=sp[i].value; - if(v<value) - min=i+1; - else if(v>value) - max=i; - else { - si=i; /* temporary value */ - break; - } - } - - if( si < 0 ) /* found nothing this time */ - return prevbest; - - /* find the priority of the prevbest */ - /* we expect that prevbest has a pair */ - if(prevbest>=0) { - i=pairs[prevbest]; - prevpri=1; - if( (sp[prevbest].flags | sp[i].flags) & ST_END ) - prevpri=0; - prevwd=abs(sp[i].value-value); - } - - /* stems are not ordered by origin, so now do the linear search */ - - while( si>0 && sp[si-1].value==value ) /* find the first one */ - si--; - - for(; si<ns && sp[si].value==value; si++) { - if(sp[si].origin != origin) - continue; - if(sp[si].ge != ge) { - if(ISDBG(SUBSTEMS)) { - fprintf(stderr, - "dbg: possible self-intersection at v=%d o=%d exp_ge=0x%x ge=0x%x\n", - value, origin, ge, sp[si].ge); - } - continue; - } - i=pairs[si]; /* the other side of this stem */ - if(i<0) - continue; /* oops, no other side */ - pri=1; - if( (sp[si].flags | sp[i].flags) & ST_END ) - pri=0; - wd=abs(sp[i].value-value); - if( prevbest == -1 || pri >prevpri - || (pri==prevpri && prevwd==0) || (wd!=0 && wd<prevwd) ) { - prevbest=si; - prevpri=pri; - prevwd=wd; - continue; - } - } - - return prevbest; -} - -/* add the substems for one glyph entry - * (called from groupsubstems()) - * returns 0 if all OK, 1 if too many groups - */ - -static int gssentry_lastgrp=0; /* reset to 0 for each new glyph */ - -static int -gssentry( /* crazy number of parameters */ - GENTRY *ge, - STEM *hs, /* horizontal stems, sorted by value */ - short *hpairs, - int nhs, - STEM *vs, /* vertical stems, sorted by value */ - short *vpairs, - int nvs, - STEMBOUNDS *s, - short *egp, - int *nextvsi, - int *nexthsi /* -2 means "check by yourself" */ -) { - enum { - SI_VP, /* vertical primary */ - SI_HP, /* horizontal primary */ - SI_SIZE /* size of the array */ - }; - int si[SI_SIZE]; /* indexes of relevant stems */ - - /* the bounds of the existing relevant stems */ - STEMBOUNDS r[ sizeof(si) / sizeof(si[0]) * 2 ]; - char rexpand; /* by how much we need to expand the group */ - int nr; /* and the number of them */ - - /* yet more temporary storage */ - short lb, hb, isvert; - int conflict, grp; - int i, j, x, y; - - - /* for each line or curve we try to find a horizontal and - * a vertical stem corresponding to its first point - * (corresponding to the last point of the previous - * glyph entry), because the directions of the lines - * will be eventually reversed and it will then become the last - * point. And the T1 rasterizer applies the hints to - * the last point. - * - */ - - /* start with the common part, the first point */ - x=ge->prev->ix3; - y=ge->prev->iy3; - - if(*nextvsi == -2) - si[SI_VP]=findstemat(x, y, ge, vs, vpairs, nvs, -1); - else { - si[SI_VP]= *nextvsi; *nextvsi= -2; - } - if(*nexthsi == -2) - si[SI_HP]=findstemat(y, x, ge, hs, hpairs, nhs, -1); - else { - si[SI_HP]= *nexthsi; *nexthsi= -2; - } - - /* - * For the horizontal lines we make sure that both - * ends of the line have the same horizontal stem, - * and the same thing for vertical lines and stems. - * In both cases we enforce the stem for the next entry. - * Otherwise unpleasant effects may arise. - */ - - if(ge->type==GE_LINE) { - if(ge->ix3==x) { /* vertical line */ - *nextvsi=si[SI_VP]=findstemat(x, ge->iy3, ge->frwd, vs, vpairs, nvs, si[SI_VP]); - } else if(ge->iy3==y) { /* horizontal line */ - *nexthsi=si[SI_HP]=findstemat(y, ge->ix3, ge->frwd, hs, hpairs, nhs, si[SI_HP]); - } - } - - if(si[SI_VP]+si[SI_HP] == -2) /* no stems, leave it alone */ - return 0; - - /* build the array of relevant bounds */ - nr=0; - for(i=0; i< sizeof(si) / sizeof(si[0]); i++) { - STEM *sp; - short *pairs; - int step; - int f; - int nzones, firstzone, binzone, einzone; - int btype, etype; - - if(si[i] < 0) - continue; - - if(i<SI_HP) { - r[nr].isvert=1; sp=vs; pairs=vpairs; - } else { - r[nr].isvert=0; sp=hs; pairs=hpairs; - } - - r[nr].low=sp[ si[i] ].value; - r[nr].high=sp[ pairs[ si[i] ] ].value; - - if(r[nr].low > r[nr].high) { - j=r[nr].low; r[nr].low=r[nr].high; r[nr].high=j; - step= -1; - } else { - step=1; - } - - /* handle the interaction with Blue Zones */ - - if(i>=SI_HP) { /* only for horizontal stems */ - if(si[i]==pairs[si[i]]) { - /* special case, the outermost stem in the - * Blue Zone without a pair, simulate it to 20-pixel - */ - if(sp[ si[i] ].flags & ST_UP) { - r[nr].high+=20; - for(j=si[i]+1; j<nhs; j++) - if( (sp[j].flags & (ST_ZONE|ST_TOPZONE)) - == (ST_ZONE|ST_TOPZONE) ) { - if(r[nr].high > sp[j].value-2) - r[nr].high=sp[j].value-2; - break; - } - } else { - r[nr].low-=20; - for(j=si[i]-1; j>=0; j--) - if( (sp[j].flags & (ST_ZONE|ST_TOPZONE)) - == (ST_ZONE) ) { - if(r[nr].low < sp[j].value+2) - r[nr].low=sp[j].value+2; - break; - } - } - } - - /* check that the stem borders don't end up in - * different Blue Zones */ - f=sp[ si[i] ].flags; - nzones=0; einzone=binzone=0; - for(j=si[i]; j!=pairs[ si[i] ]; j+=step) { - if( (sp[j].flags & ST_ZONE)==0 ) - continue; - /* if see a zone border going in the same direction */ - if( ((f ^ sp[j].flags) & ST_UP)==0 ) { - if( ++nzones == 1 ) { - firstzone=sp[j].value; /* remember the first one */ - etype=sp[j].flags & ST_TOPZONE; - } - einzone=1; - - } else { /* the opposite direction */ - if(nzones==0) { /* beginning is in a blue zone */ - binzone=1; - btype=sp[j].flags & ST_TOPZONE; - } - einzone=0; - } - } - - /* beginning and end are in Blue Zones of different types */ - if( binzone && einzone && (btype ^ etype)!=0 ) { - if( sp[si[i]].flags & ST_UP ) { - if(firstzone > r[nr].low+22) - r[nr].high=r[nr].low+20; - else - r[nr].high=firstzone-2; - } else { - if(firstzone < r[nr].high-22) - r[nr].low=r[nr].high-20; - else - r[nr].low=firstzone+2; - } - } - } - - if(ISDBG(SUBSTEMS)) - fprintf(pfa_file, "%% at(%d,%d)[%d,%d] %d..%d %c (%d x %d)\n", x, y, i, nr, - r[nr].low, r[nr].high, r[nr].isvert ? 'v' : 'h', - si[i], pairs[si[i]]); - - nr++; - } - - /* now try to find a group */ - conflict=0; /* no conflicts found yet */ - for(j=0; j<nr; j++) - r[j].already=0; - - /* check if it fits into the last group */ - grp = gssentry_lastgrp; - i = (grp==0)? 0 : egp[grp-1]; - for(; i<egp[grp]; i++) { - lb=s[i].low; hb=s[i].high; isvert=s[i].isvert; - for(j=0; j<nr; j++) - if( r[j].isvert==isvert /* intersects */ - && r[j].low <= hb && r[j].high >= lb ) { - if( r[j].low == lb && r[j].high == hb ) /* coincides */ - r[j].already=1; - else - conflict=1; - } - - if(conflict) - break; - } - - if(conflict) { /* nope, check all the groups */ - for(j=0; j<nr; j++) - r[j].already=0; - - for(i=0, grp=0; i<egp[NSTEMGRP-1]; i++) { - if(i == egp[grp]) { /* checked all stems in a group */ - if(conflict) { - grp++; conflict=0; /* check the next group */ - for(j=0; j<nr; j++) - r[j].already=0; - } else - break; /* insert into this group */ - } - - lb=s[i].low; hb=s[i].high; isvert=s[i].isvert; - for(j=0; j<nr; j++) - if( r[j].isvert==isvert /* intersects */ - && r[j].low <= hb && r[j].high >= lb ) { - if( r[j].low == lb && r[j].high == hb ) /* coincides */ - r[j].already=1; - else - conflict=1; - } - - if(conflict) - i=egp[grp]-1; /* fast forward to the next group */ - } - } - - /* do we have any empty group ? */ - if(conflict && grp < NSTEMGRP-1) { - grp++; conflict=0; - for(j=0; j<nr; j++) - r[j].already=0; - } - - if(conflict) { /* oops, can't find any group to fit */ - return 1; - } - - /* OK, add stems to this group */ - - rexpand = nr; - for(j=0; j<nr; j++) - rexpand -= r[j].already; - - if(rexpand > 0) { - for(i=egp[NSTEMGRP-1]-1; i>=egp[grp]; i--) - s[i+rexpand]=s[i]; - for(i=0; i<nr; i++) - if(!r[i].already) - s[egp[grp]++]=r[i]; - for(i=grp+1; i<NSTEMGRP; i++) - egp[i]+=rexpand; - } - - ge->stemid = gssentry_lastgrp = grp; - return 0; -} - -/* - * Create the groups of substituted stems from the list. - * Each group will be represented by a subroutine in the Subs - * array. - */ - -static void -groupsubstems( - GLYPH *g, - STEM *hs, /* horizontal stems, sorted by value */ - short *hpairs, - int nhs, - STEM *vs, /* vertical stems, sorted by value */ - short *vpairs, - int nvs -) -{ - GENTRY *ge; - int i, j; - - /* temporary storage */ - STEMBOUNDS s[MAX_STEMS*2]; - /* indexes in there, pointing past the end each stem group */ - short egp[NSTEMGRP]; - - int nextvsi, nexthsi; /* -2 means "check by yourself" */ - - for(i=0; i<NSTEMGRP; i++) - egp[i]=0; - - nextvsi=nexthsi= -2; /* processed no horiz/vert line */ - - gssentry_lastgrp = 0; /* reset the last group for new glyph */ - - for (ge = g->entries; ge != 0; ge = ge->next) { - if(ge->type!=GE_LINE && ge->type!=GE_CURVE) { - nextvsi=nexthsi= -2; /* next path is independent */ - continue; - } - - if( gssentry(ge, hs, hpairs, nhs, vs, vpairs, nvs, s, egp, &nextvsi, &nexthsi) ) { - WARNING_2 fprintf(stderr, "*** glyph %s requires over %d hint subroutines, ignored them\n", - g->name, NSTEMGRP); - /* it's better to have no substituted hints at all than have only part */ - for (ge = g->entries; ge != 0; ge = ge->next) - ge->stemid= -1; - g->nsg=0; /* just to be safe, already is 0 by initialization */ - return; - } - - /* - * handle the last vert/horiz line of the path specially, - * correct the hint for the first entry of the path - */ - if(ge->frwd != ge->next && (nextvsi != -2 || nexthsi != -2) ) { - if( gssentry(ge->frwd, hs, hpairs, nhs, vs, vpairs, nvs, s, egp, &nextvsi, &nexthsi) ) { - WARNING_2 fprintf(stderr, "*** glyph %s requires over %d hint subroutines, ignored them\n", - g->name, NSTEMGRP); - /* it's better to have no substituted hints at all than have only part */ - for (ge = g->entries; ge != 0; ge = ge->next) - ge->stemid= -1; - g->nsg=0; /* just to be safe, already is 0 by initialization */ - return; - } - } - - } - - /* find the index of the first empty group - same as the number of groups */ - if(egp[0]>0) { - for(i=1; i<NSTEMGRP && egp[i]!=egp[i-1]; i++) - {} - g->nsg=i; - } else - g->nsg=0; - - if(ISDBG(SUBSTEMS)) { - fprintf(pfa_file, "%% %d substem groups (%d %d %d)\n", g->nsg, - g->nsg>1 ? egp[g->nsg-2] : -1, - g->nsg>0 ? egp[g->nsg-1] : -1, - g->nsg<NSTEMGRP ? egp[g->nsg] : -1 ); - j=0; - for(i=0; i<g->nsg; i++) { - fprintf(pfa_file, "%% grp %3d: ", i); - for(; j<egp[i]; j++) { - fprintf(pfa_file, " %4d...%-4d %c ", s[j].low, s[j].high, - s[j].isvert ? 'v' : 'h'); - } - fprintf(pfa_file, "\n"); - } - } - - if(g->nsg==1) { /* it would be the same as the main stems */ - /* so erase it */ - for (ge = g->entries; ge != 0; ge = ge->next) - ge->stemid= -1; - g->nsg=0; - } - - if(g->nsg>0) { - if( (g->nsbs=malloc(g->nsg * sizeof (egp[0]))) == 0 ) { - fprintf(stderr, "**** not enough memory for substituted hints ****\n"); - exit(255); - } - memmove(g->nsbs, egp, g->nsg * sizeof(short)); - if( (g->sbstems=malloc(egp[g->nsg-1] * sizeof (s[0]))) == 0 ) { - fprintf(stderr, "**** not enough memory for substituted hints ****\n"); - exit(255); - } - memmove(g->sbstems, s, egp[g->nsg-1] * sizeof(s[0])); - } -} - -void -buildstems( - GLYPH * g -) -{ - STEM hs[MAX_STEMS], vs[MAX_STEMS]; /* temporary working - * storage */ - short hs_pairs[MAX_STEMS], vs_pairs[MAX_STEMS]; /* best pairs for these stems */ - STEM *sp; - GENTRY *ge, *nge, *pge; - int nx, ny; - int ovalue; - int totals, grp, lastgrp; - - assertisint(g, "buildstems int"); - - g->nhs = g->nvs = 0; - memset(hs, 0, sizeof hs); - memset(vs, 0, sizeof vs); - - /* first search the whole character for possible stem points */ - - for (ge = g->entries; ge != 0; ge = ge->next) { - if (ge->type == GE_CURVE) { - - /* - * SURPRISE! - * We consider the stems bound by the - * H/V ends of the curves as flat ones. - * - * But we don't include the point on the - * other end into the range. - */ - - /* first check the beginning of curve */ - /* if it is horizontal, add a hstem */ - if (ge->iy1 == ge->prev->iy3) { - hs[g->nhs].value = ge->iy1; - - if (ge->ix1 < ge->prev->ix3) - hs[g->nhs].flags = ST_FLAT | ST_UP; - else - hs[g->nhs].flags = ST_FLAT; - - hs[g->nhs].origin = ge->prev->ix3; - hs[g->nhs].ge = ge; - - if (ge->ix1 < ge->prev->ix3) { - hs[g->nhs].from = ge->ix1+1; - hs[g->nhs].to = ge->prev->ix3; - if(hs[g->nhs].from > hs[g->nhs].to) - hs[g->nhs].from--; - } else { - hs[g->nhs].from = ge->prev->ix3; - hs[g->nhs].to = ge->ix1-1; - if(hs[g->nhs].from > hs[g->nhs].to) - hs[g->nhs].to++; - } - if (ge->ix1 != ge->prev->ix3) - g->nhs++; - } - /* if it is vertical, add a vstem */ - else if (ge->ix1 == ge->prev->ix3) { - vs[g->nvs].value = ge->ix1; - - if (ge->iy1 > ge->prev->iy3) - vs[g->nvs].flags = ST_FLAT | ST_UP; - else - vs[g->nvs].flags = ST_FLAT; - - vs[g->nvs].origin = ge->prev->iy3; - vs[g->nvs].ge = ge; - - if (ge->iy1 < ge->prev->iy3) { - vs[g->nvs].from = ge->iy1+1; - vs[g->nvs].to = ge->prev->iy3; - if(vs[g->nvs].from > vs[g->nvs].to) - vs[g->nvs].from--; - } else { - vs[g->nvs].from = ge->prev->iy3; - vs[g->nvs].to = ge->iy1-1; - if(vs[g->nvs].from > vs[g->nvs].to) - vs[g->nvs].to++; - } - - if (ge->iy1 != ge->prev->iy3) - g->nvs++; - } - /* then check the end of curve */ - /* if it is horizontal, add a hstem */ - if (ge->iy3 == ge->iy2) { - hs[g->nhs].value = ge->iy3; - - if (ge->ix3 < ge->ix2) - hs[g->nhs].flags = ST_FLAT | ST_UP; - else - hs[g->nhs].flags = ST_FLAT; - - hs[g->nhs].origin = ge->ix3; - hs[g->nhs].ge = ge->frwd; - - if (ge->ix3 < ge->ix2) { - hs[g->nhs].from = ge->ix3; - hs[g->nhs].to = ge->ix2-1; - if( hs[g->nhs].from > hs[g->nhs].to ) - hs[g->nhs].to++; - } else { - hs[g->nhs].from = ge->ix2+1; - hs[g->nhs].to = ge->ix3; - if( hs[g->nhs].from > hs[g->nhs].to ) - hs[g->nhs].from--; - } - - if (ge->ix3 != ge->ix2) - g->nhs++; - } - /* if it is vertical, add a vstem */ - else if (ge->ix3 == ge->ix2) { - vs[g->nvs].value = ge->ix3; - - if (ge->iy3 > ge->iy2) - vs[g->nvs].flags = ST_FLAT | ST_UP; - else - vs[g->nvs].flags = ST_FLAT; - - vs[g->nvs].origin = ge->iy3; - vs[g->nvs].ge = ge->frwd; - - if (ge->iy3 < ge->iy2) { - vs[g->nvs].from = ge->iy3; - vs[g->nvs].to = ge->iy2-1; - if( vs[g->nvs].from > vs[g->nvs].to ) - vs[g->nvs].to++; - } else { - vs[g->nvs].from = ge->iy2+1; - vs[g->nvs].to = ge->iy3; - if( vs[g->nvs].from > vs[g->nvs].to ) - vs[g->nvs].from--; - } - - if (ge->iy3 != ge->iy2) - g->nvs++; - } else { - - /* - * check the end of curve for a not smooth - * local extremum - */ - nge = ge->frwd; - - if (nge == 0) - continue; - else if (nge->type == GE_LINE) { - nx = nge->ix3; - ny = nge->iy3; - } else if (nge->type == GE_CURVE) { - nx = nge->ix1; - ny = nge->iy1; - } else - continue; - - /* check for vertical extremums */ - if ((ge->iy3 > ge->iy2 && ge->iy3 > ny) - || (ge->iy3 < ge->iy2 && ge->iy3 < ny)) { - hs[g->nhs].value = ge->iy3; - hs[g->nhs].from - = hs[g->nhs].to - = hs[g->nhs].origin = ge->ix3; - hs[g->nhs].ge = ge->frwd; - - if (ge->ix3 < ge->ix2 - || nx < ge->ix3) - hs[g->nhs].flags = ST_UP; - else - hs[g->nhs].flags = 0; - - if (ge->ix3 != ge->ix2 || nx != ge->ix3) - g->nhs++; - } - /* - * the same point may be both horizontal and - * vertical extremum - */ - /* check for horizontal extremums */ - if ((ge->ix3 > ge->ix2 && ge->ix3 > nx) - || (ge->ix3 < ge->ix2 && ge->ix3 < nx)) { - vs[g->nvs].value = ge->ix3; - vs[g->nvs].from - = vs[g->nvs].to - = vs[g->nvs].origin = ge->iy3; - vs[g->nvs].ge = ge->frwd; - - if (ge->iy3 > ge->iy2 - || ny > ge->iy3) - vs[g->nvs].flags = ST_UP; - else - vs[g->nvs].flags = 0; - - if (ge->iy3 != ge->iy2 || ny != ge->iy3) - g->nvs++; - } - } - - } else if (ge->type == GE_LINE) { - nge = ge->frwd; - - /* if it is horizontal, add a hstem */ - /* and the ends as vstems if they brace the line */ - if (ge->iy3 == ge->prev->iy3 - && ge->ix3 != ge->prev->ix3) { - hs[g->nhs].value = ge->iy3; - if (ge->ix3 < ge->prev->ix3) { - hs[g->nhs].flags = ST_FLAT | ST_UP; - hs[g->nhs].from = ge->ix3; - hs[g->nhs].to = ge->prev->ix3; - } else { - hs[g->nhs].flags = ST_FLAT; - hs[g->nhs].from = ge->prev->ix3; - hs[g->nhs].to = ge->ix3; - } - hs[g->nhs].origin = ge->ix3; - hs[g->nhs].ge = ge->frwd; - - pge = ge->bkwd; - - /* add beginning as vstem */ - vs[g->nvs].value = pge->ix3; - vs[g->nvs].origin - = vs[g->nvs].from - = vs[g->nvs].to = pge->iy3; - vs[g->nvs].ge = ge; - - if(pge->type==GE_CURVE) - ovalue=pge->iy2; - else - ovalue=pge->prev->iy3; - - if (pge->iy3 > ovalue) - vs[g->nvs].flags = ST_UP | ST_END; - else if (pge->iy3 < ovalue) - vs[g->nvs].flags = ST_END; - else - vs[g->nvs].flags = 0; - - if( vs[g->nvs].flags != 0 ) - g->nvs++; - - /* add end as vstem */ - vs[g->nvs].value = ge->ix3; - vs[g->nvs].origin - = vs[g->nvs].from - = vs[g->nvs].to = ge->iy3; - vs[g->nvs].ge = ge->frwd; - - if(nge->type==GE_CURVE) - ovalue=nge->iy1; - else - ovalue=nge->iy3; - - if (ovalue > ge->iy3) - vs[g->nvs].flags = ST_UP | ST_END; - else if (ovalue < ge->iy3) - vs[g->nvs].flags = ST_END; - else - vs[g->nvs].flags = 0; - - if( vs[g->nvs].flags != 0 ) - g->nvs++; - - g->nhs++; - } - /* if it is vertical, add a vstem */ - /* and the ends as hstems if they brace the line */ - else if (ge->ix3 == ge->prev->ix3 - && ge->iy3 != ge->prev->iy3) { - vs[g->nvs].value = ge->ix3; - if (ge->iy3 > ge->prev->iy3) { - vs[g->nvs].flags = ST_FLAT | ST_UP; - vs[g->nvs].from = ge->prev->iy3; - vs[g->nvs].to = ge->iy3; - } else { - vs[g->nvs].flags = ST_FLAT; - vs[g->nvs].from = ge->iy3; - vs[g->nvs].to = ge->prev->iy3; - } - vs[g->nvs].origin = ge->iy3; - vs[g->nvs].ge = ge->frwd; - - pge = ge->bkwd; - - /* add beginning as hstem */ - hs[g->nhs].value = pge->iy3; - hs[g->nhs].origin - = hs[g->nhs].from - = hs[g->nhs].to = pge->ix3; - hs[g->nhs].ge = ge; - - if(pge->type==GE_CURVE) - ovalue=pge->ix2; - else - ovalue=pge->prev->ix3; - - if (pge->ix3 < ovalue) - hs[g->nhs].flags = ST_UP | ST_END; - else if (pge->ix3 > ovalue) - hs[g->nhs].flags = ST_END; - else - hs[g->nhs].flags = 0; - - if( hs[g->nhs].flags != 0 ) - g->nhs++; - - /* add end as hstem */ - hs[g->nhs].value = ge->iy3; - hs[g->nhs].origin - = hs[g->nhs].from - = hs[g->nhs].to = ge->ix3; - hs[g->nhs].ge = ge->frwd; - - if(nge->type==GE_CURVE) - ovalue=nge->ix1; - else - ovalue=nge->ix3; - - if (ovalue < ge->ix3) - hs[g->nhs].flags = ST_UP | ST_END; - else if (ovalue > ge->ix3) - hs[g->nhs].flags = ST_END; - else - hs[g->nhs].flags = 0; - - if( hs[g->nhs].flags != 0 ) - g->nhs++; - - g->nvs++; - } - /* - * check the end of line for a not smooth local - * extremum - */ - nge = ge->frwd; - - if (nge == 0) - continue; - else if (nge->type == GE_LINE) { - nx = nge->ix3; - ny = nge->iy3; - } else if (nge->type == GE_CURVE) { - nx = nge->ix1; - ny = nge->iy1; - } else - continue; - - /* check for vertical extremums */ - if ((ge->iy3 > ge->prev->iy3 && ge->iy3 > ny) - || (ge->iy3 < ge->prev->iy3 && ge->iy3 < ny)) { - hs[g->nhs].value = ge->iy3; - hs[g->nhs].from - = hs[g->nhs].to - = hs[g->nhs].origin = ge->ix3; - hs[g->nhs].ge = ge->frwd; - - if (ge->ix3 < ge->prev->ix3 - || nx < ge->ix3) - hs[g->nhs].flags = ST_UP; - else - hs[g->nhs].flags = 0; - - if (ge->ix3 != ge->prev->ix3 || nx != ge->ix3) - g->nhs++; - } - /* - * the same point may be both horizontal and vertical - * extremum - */ - /* check for horizontal extremums */ - if ((ge->ix3 > ge->prev->ix3 && ge->ix3 > nx) - || (ge->ix3 < ge->prev->ix3 && ge->ix3 < nx)) { - vs[g->nvs].value = ge->ix3; - vs[g->nvs].from - = vs[g->nvs].to - = vs[g->nvs].origin = ge->iy3; - vs[g->nvs].ge = ge->frwd; - - if (ge->iy3 > ge->prev->iy3 - || ny > ge->iy3) - vs[g->nvs].flags = ST_UP; - else - vs[g->nvs].flags = 0; - - if (ge->iy3 != ge->prev->iy3 || ny != ge->iy3) - g->nvs++; - } - } - } - - g->nhs=addbluestems(hs, g->nhs); - sortstems(hs, g->nhs); - sortstems(vs, g->nvs); - - if (ISDBG(STEMS)) - debugstems(g->name, hs, g->nhs, vs, g->nvs); - - /* find the stems interacting with the Blue Zones */ - markbluestems(hs, g->nhs); - - if(subhints) { - if (ISDBG(SUBSTEMS)) - fprintf(pfa_file, "%% %s: joining subst horizontal stems\n", g->name); - joinsubstems(hs, hs_pairs, g->nhs, 1); - uniformstems(hs, hs_pairs, g->nhs); - - if (ISDBG(SUBSTEMS)) - fprintf(pfa_file, "%% %s: joining subst vertical stems\n", g->name); - joinsubstems(vs, vs_pairs, g->nvs, 0); - - groupsubstems(g, hs, hs_pairs, g->nhs, vs, vs_pairs, g->nvs); - } - - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% %s: joining main horizontal stems\n", g->name); - g->nhs = joinmainstems(hs, g->nhs, 1); - if (ISDBG(MAINSTEMS)) - fprintf(pfa_file, "%% %s: joining main vertical stems\n", g->name); - g->nvs = joinmainstems(vs, g->nvs, 0); - - if (ISDBG(MAINSTEMS)) - debugstems(g->name, hs, g->nhs, vs, g->nvs); - - if(g->nhs > 0) { - if ((sp = malloc(sizeof(STEM) * g->nhs)) == 0) { - fprintf(stderr, "**** not enough memory for hints ****\n"); - exit(255); - } - g->hstems = sp; - memcpy(sp, hs, sizeof(STEM) * g->nhs); - } else - g->hstems = 0; - - if(g->nvs > 0) { - if ((sp = malloc(sizeof(STEM) * g->nvs)) == 0) { - fprintf(stderr, "**** not enough memory for hints ****\n"); - exit(255); - } - g->vstems = sp; - memcpy(sp, vs, sizeof(STEM) * g->nvs); - } else - g->vstems = 0; - - /* now check that the stems won't overflow the interpreter's stem stack: - * some interpreters (like X11) push the stems on each change into - * stack and pop them only after the whole glyphs is completed. - */ - - totals = (g->nhs+g->nvs) / 2; /* we count whole stems, not halves */ - lastgrp = -1; - - for (ge = g->entries; ge != 0; ge = ge->next) { - grp=ge->stemid; - if(grp >= 0 && grp != lastgrp) { - if(grp==0) - totals += g->nsbs[0]; - else - totals += g->nsbs[grp] - g->nsbs[grp-1]; - - lastgrp = grp; - } - } - - /* be on the safe side, check for >= , not > */ - if(totals >= max_stemdepth) { /* oops, too deep */ - WARNING_2 { - fprintf(stderr, "Warning: glyph %s needs hint stack depth %d\n", g->name, totals); - fprintf(stderr, " (limit %d): removed the substituted hints from it\n", max_stemdepth); - } - if(g->nsg > 0) { - for (ge = g->entries; ge != 0; ge = ge->next) - ge->stemid = -1; - free(g->sbstems); g->sbstems = 0; - free(g->nsbs); g->nsbs = 0; - g->nsg = 0; - } - } - - /* now check if there are too many main stems */ - totals = (g->nhs+g->nvs) / 2; /* we count whole stems, not halves */ - if(totals >= max_stemdepth) { - /* even worse, too much of non-substituted stems */ - WARNING_2 { - fprintf(stderr, "Warning: glyph %s has %d main hints\n", g->name, totals); - fprintf(stderr, " (limit %d): removed the hints from it\n", max_stemdepth); - } - if(g->vstems) { - free(g->vstems); g->vstems = 0; g->nvs = 0; - } - if(g->hstems) { - free(g->hstems); g->hstems = 0; g->nhs = 0; - } - } -} - -/* convert weird curves that are close to lines into lines. -*/ - -void -fstraighten( - GLYPH * g -) -{ - GENTRY *ge, *pge, *nge, *ige; - double df; - int dir; - double iln, oln; - int svdir, i, o; - - for (ige = g->entries; ige != 0; ige = ige->next) { - if (ige->type != GE_CURVE) - continue; - - ge = ige; - pge = ge->bkwd; - nge = ge->frwd; - - df = 0.; - - /* look for vertical then horizontal */ - for(i=0; i<2; i++) { - o = !i; /* other axis */ - - iln = fabs(ge->fpoints[i][2] - pge->fpoints[i][2]); - oln = fabs(ge->fpoints[o][2] - pge->fpoints[o][2]); - /* - * if current curve is almost a vertical line, and it - * doesn't begin or end horizontally (and the prev/next - * line doesn't join smoothly ?) - */ - if( oln < 1. - || ge->fpoints[o][2] == ge->fpoints[o][1] - || ge->fpoints[o][0] == pge->fpoints[o][2] - || iln > 2. - || (iln > 1. && iln/oln > 0.1) ) - continue; - - - if(ISDBG(STRAIGHTEN)) - fprintf(stderr,"** straighten almost %s\n", (i? "horizontal":"vertical")); - - df = ge->fpoints[i][2] - pge->fpoints[i][2]; - dir = fsign(ge->fpoints[o][2] - pge->fpoints[o][2]); - ge->type = GE_LINE; - - /* - * suck in all the sequence of such almost lines - * going in the same direction but not deviating - * too far from vertical - */ - iln = fabs(nge->fpoints[i][2] - ge->fpoints[i][2]); - oln = nge->fpoints[o][2] - ge->fpoints[o][2]; - - while (fabs(df) <= 5 && nge->type == GE_CURVE - && dir == fsign(oln) /* that also gives oln != 0 */ - && iln <= 2. - && ( iln <= 1. || iln/fabs(oln) <= 0.1 ) ) { - ge->fx3 = nge->fx3; - ge->fy3 = nge->fy3; - - if(ISDBG(STRAIGHTEN)) - fprintf(stderr,"** straighten collapsing %s\n", (i? "horizontal":"vertical")); - freethisge(nge); - fixendpath(ge); - pge = ge->bkwd; - nge = ge->frwd; - - df = ge->fpoints[i][2] - pge->fpoints[i][2]; - - iln = fabs(nge->fpoints[i][2] - ge->fpoints[i][2]); - oln = nge->fpoints[o][2] - ge->fpoints[o][2]; - } - - /* now check what do we have as previous/next line */ - - if(ge != pge) { - if( pge->type == GE_LINE && pge->fpoints[i][2] == pge->prev->fpoints[i][2] - && fabs(pge->fpoints[o][2] != pge->prev->fpoints[o][2]) ) { - if(ISDBG(STRAIGHTEN)) fprintf(stderr,"** straighten join with previous 0x%x 0x%x\n", pge, ge); - /* join the previous line with current */ - pge->fx3 = ge->fx3; - pge->fy3 = ge->fy3; - - ige = freethisge(ge)->prev; /* keep the iterator valid */ - ge = pge; - fixendpath(ge); - pge = ge->bkwd; - } - } - - if(ge != nge) { - if (nge->type == GE_LINE && nge->fpoints[i][2] == ge->fpoints[i][2] - && fabs(nge->fpoints[o][2] != ge->fpoints[o][2]) ) { - if(ISDBG(STRAIGHTEN)) fprintf(stderr,"** straighten join with next 0x%x 0x%x\n", ge, nge); - /* join the next line with current */ - ge->fx3 = nge->fx3; - ge->fy3 = nge->fy3; - - freethisge(nge); - fixendpath(ge); - pge = ge->bkwd; - nge = ge->frwd; - - } - } - - if(ge != pge) { - /* try to align the lines if neccessary */ - if(df != 0.) - fclosegap(ge, ge, i, df, NULL); - } else { - /* contour consists of only one line, get rid of it */ - ige = freethisge(ge); /* keep the iterator valid */ - if(ige == 0) /* this was the last contour */ - return; - ige = ige->prev; - } - - break; /* don't bother looking at the other axis */ - } - } -} - -/* solve a square equation, - * returns the number of solutions found, the solutions - * are stored in res which should point to array of two doubles. - * min and max limit the area for solutions - */ - -static int -fsqequation( - double a, - double b, - double c, - double *res, - double min, - double max -) -{ - double D; - int n; - - if(ISDBG(SQEQ)) fprintf(stderr, "sqeq(%g,%g,%g) [%g;%g]\n", a, b, c, min, max); - - if(fabs(a) < 0.000001) { /* if a linear equation */ - n=0; - if(fabs(b) < 0.000001) /* not an equation at all */ - return 0; - res[0] = -c/b; - if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: linear t=%g\n", res[0]); - if(res[0] >= min && res[0] <= max) - n++; - return n; - } - - D = b*b - 4.0*a*c; - if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: D=%g\n", D); - if(D<0) - return 0; - - D = sqrt(D); - - n=0; - res[0] = (-b+D) / (2*a); - if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: t1=%g\n", res[0]); - if(res[0] >= min && res[0] <= max) - n++; - - res[n] = (-b-D) / (2*a); - if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: t2=%g\n", res[n]); - if(res[n] >= min && res[n] <= max) - n++; - - /* return 2nd solution only if it's different enough */ - if(n==2 && fabs(res[0]-res[1])<0.000001) - n=1; - - return n; -} - -/* check that the curves don't cross quadrant boundary */ -/* (float) */ - -/* - Here we make sure that the curve does not continue past - horizontal or vertical extremums. The horizontal points are - explained, vertical points are by analogy. - - The horizontal points are where the derivative - dy/dx is equal to 0. But the Bezier curves are defined by - parametric formulas - x=fx(t) - y=fy(t) - so finding this derivative is complicated. - Also even if we find some point (x,y) splitting at this point - is far not obvious. Fortunately we can use dy/dt = 0 instead, - this gets to a rather simple square equation and splitting - at a known value of t is simple. - - The formulas are: - - y = A*(1-t)^3 + 3*B*(1-t)^2*t + 3*C*(1-t)*t^2 + D*t^3 - y = (-A+3*B-3*C+D)*t^3 + (3*A-6*B+3*C)*t^2 + (-3*A+3*B)*t + A - dy/dt = 3*(-A+3*B-3*C+D)*t^2 + 2*(3*A-6*B+3*C)*t + (-3*A+3*B) - */ - -void -ffixquadrants( - GLYPH *g -) -{ - GENTRY *ge, *nge; - int i, j, np, oldnp; - double sp[5]; /* split points, last one empty */ - char dir[5]; /* for debugging, direction by which split happened */ - double a, b, *pts; /* points of a curve */ - - for (ge = g->entries; ge != 0; ge = ge->next) { - if (ge->type != GE_CURVE) - continue; - - doagain: - np = 0; /* no split points yet */ - if(ISDBG(QUAD)) { - fprintf(stderr, "%s: trying 0x%x (%g %g) (%g %g) (%g %g) (%g %g)\n ", g->name, - ge, ge->prev->fx3, ge->prev->fy3, ge->fx1, ge->fy1, ge->fx2, ge->fy2, - ge->fx3, ge->fy3); - } - for(i=0; i<2; i++) { /* first for x then for y */ - /* find the cooridnates of control points */ - a = ge->prev->fpoints[i][2]; - pts = &ge->fpoints[i][0]; - - oldnp = np; - np += fsqequation( - 3.0*(-a + 3.0*pts[0] - 3.0*pts[1] + pts[2]), - 6.0*(a - 2.0*pts[0] + pts[1]), - 3.0*(-a + pts[0]), - &sp[np], - 0.0, 1.0); /* XXX range is [0;1] */ - - if(np == oldnp) - continue; - - if(ISDBG(QUAD)) - fprintf(stderr, "%s: 0x%x: %d pts(%c): ", - g->name, ge, np-oldnp, i? 'y':'x'); - - /* remove points that are too close to the ends - * because hor/vert ends are permitted, also - * if the split point is VERY close to the ends - * but not exactly then just flatten it and check again. - */ - for(j = oldnp; j<np; j++) { - dir[j] = i; - if(ISDBG(QUAD)) - fprintf(stderr, "%g ", sp[j]); - if(sp[j] < 0.03) { /* front end of curve */ - if(ge->fpoints[i][0] != ge->prev->fpoints[i][2]) { - ge->fpoints[i][0] = ge->prev->fpoints[i][2]; - if(ISDBG(QUAD)) fprintf(stderr, "flattened at front\n"); - goto doagain; - } - if( ge->fpoints[i][1] != ge->fpoints[i][0] - && fsign(ge->fpoints[i][2] - ge->fpoints[i][1]) - != fsign(ge->fpoints[i][1] - ge->fpoints[i][0]) ) { - ge->fpoints[i][1] = ge->fpoints[i][0]; - if(ISDBG(QUAD)) fprintf(stderr, "flattened zigzag at front\n"); - goto doagain; - } - sp[j] = sp[j+1]; np--; j--; - if(ISDBG(QUAD)) fprintf(stderr, "(front flat) "); - } else if(sp[j] > 0.97) { /* rear end of curve */ - if(ge->fpoints[i][1] != ge->fpoints[i][2]) { - ge->fpoints[i][1] = ge->fpoints[i][2]; - if(ISDBG(QUAD)) fprintf(stderr, "flattened at rear\n"); - goto doagain; - } - if( ge->fpoints[i][0] != ge->fpoints[i][1] - && fsign(ge->prev->fpoints[i][2] - ge->fpoints[i][0]) - != fsign(ge->fpoints[i][0] - ge->fpoints[i][1]) ) { - ge->fpoints[i][0] = ge->fpoints[i][1]; - if(ISDBG(QUAD)) fprintf(stderr, "flattened zigzag at rear\n"); - goto doagain; - } - sp[j] = sp[j+1]; np--; j--; - if(ISDBG(QUAD)) fprintf(stderr, "(rear flat) "); - } - } - if(ISDBG(QUAD)) fprintf(stderr, "\n"); - } - - if(np==0) /* no split points, leave it alone */ - continue; - - if(ISDBG(QUAD)) { - fprintf(stderr, "%s: splitting 0x%x (%g %g) (%g %g) (%g %g) (%g %g) at %d points\n ", g->name, - ge, ge->prev->fx3, ge->prev->fy3, ge->fx1, ge->fy1, ge->fx2, ge->fy2, - ge->fx3, ge->fy3, np); - for(i=0; i<np; i++) - fprintf(stderr, "%g(%c) ", sp[i], dir[i] ? 'y':'x'); - fprintf(stderr, "\n"); - } - - /* sort the points ascending */ - for(i=0; i<np; i++) - for(j=i+1; j<np; j++) - if(sp[i] > sp[j]) { - a = sp[i]; sp[i] = sp[j]; sp[j] = a; - } - - /* now finally do the split on each point */ - for(j=0; j<np; j++) { - double k1, k2, c; - - k1 = sp[j]; - k2 = 1 - k1; - - if(ISDBG(QUAD)) fprintf(stderr, " 0x%x %g/%g\n", ge, k1, k2); - - nge = newgentry(GEF_FLOAT); - (*nge) = (*ge); - -#define SPLIT(pt1, pt2) ( (pt1) + k1*((pt2)-(pt1)) ) /* order is important! */ - for(i=0; i<2; i++) { /* for x and y */ - a = ge->fpoints[i][0]; /* get the middle points */ - b = ge->fpoints[i][1]; - - /* calculate new internal points */ - c = SPLIT(a, b); - - ge->fpoints[i][0] = SPLIT(ge->prev->fpoints[i][2], a); - ge->fpoints[i][1] = SPLIT(ge->fpoints[i][0], c); - - nge->fpoints[i][1] = SPLIT(b, nge->fpoints[i][2]); - nge->fpoints[i][0] = SPLIT(c, nge->fpoints[i][1]); - - ge->fpoints[i][2] = SPLIT(ge->fpoints[i][1], - + nge->fpoints[i][0]); - } -#undef SPLIT - - addgeafter(ge, nge); - - /* go to the next part, adjust remaining points */ - ge = nge; - for(i=j+1; i<np; i++) - sp[i] = (sp[i]-k1) / k2; - } - } - -} - -/* check if a curve is a zigzag */ - -static int -iiszigzag( - GENTRY *ge -) -{ - double k, k1, k2; - int a, b; - - if (ge->type != GE_CURVE) - return 0; - - a = ge->iy2 - ge->iy1; - b = ge->ix2 - ge->ix1; - if(a == 0) { - if(b == 0) { - return 0; - } else - k = FBIGVAL; - } else - k = fabs((double) b / (double) a); - - a = ge->iy1 - ge->prev->iy3; - b = ge->ix1 - ge->prev->ix3; - if(a == 0) { - if(b == 0) { - return 0; - } else - k1 = FBIGVAL; - } else - k1 = fabs((double) b / (double) a); - - a = ge->iy3 - ge->iy2; - b = ge->ix3 - ge->ix2; - if(a == 0) { - if(b == 0) { - return 0; - } else - k2 = FBIGVAL; - } else - k2 = fabs((double) b / (double) a); - - /* if the curve is not a zigzag */ - if ((k1+0.0001 >= k && k2 <= k+0.0001) || (k1 <= k+0.0001 && k2+0.0001 >= k)) - return 0; - else - return 1; -} - -/* check if a curve is a zigzag - floating */ - -static int -fiszigzag( - GENTRY *ge -) -{ - double k, k1, k2; - double a, b; - - if (ge->type != GE_CURVE) - return 0; - - a = fabs(ge->fy2 - ge->fy1); - b = fabs(ge->fx2 - ge->fx1); - if(a < FEPS) { - if(b < FEPS) { - return 0; - } else - k = FBIGVAL; - } else - k = b / a; - - a = fabs(ge->fy1 - ge->prev->fy3); - b = fabs(ge->fx1 - ge->prev->fx3); - if(a < FEPS) { - if(b < FEPS) { - return 0; - } else - k1 = FBIGVAL; - } else - k1 = b / a; - - a = fabs(ge->fy3 - ge->fy2); - b = fabs(ge->fx3 - ge->fx2); - if(a < FEPS) { - if(b < FEPS) { - return 0; - } else - k2 = FBIGVAL; - } else - k2 = b / a; - - /* if the curve is not a zigzag */ - if ((k1+0.0001 >= k && k2 <= k+0.0001) || (k1 <= k+0.0001 && k2+0.0001 >= k)) - return 0; - else - return 1; -} - -/* split the zigzag-like curves into two parts */ - -void -fsplitzigzags( - GLYPH * g -) -{ - GENTRY *ge, *nge; - double a, b, c, d; - - assertisfloat(g, "splitting zigzags"); - for (ge = g->entries; ge != 0; ge = ge->next) { - if (ge->type != GE_CURVE) - continue; - - /* if the curve is not a zigzag */ - if ( !fiszigzag(ge) ) { - continue; - } - - if(ISDBG(FCONCISE)) { - double maxsc1, maxsc2; - fprintf(stderr, "split a zigzag "); - fnormalizege(ge); - if( fcrossraysge(ge, ge, &maxsc1, &maxsc2, NULL) ) { - fprintf(stderr, "sc1=%g sc2=%g\n", maxsc1, maxsc2); - } else { - fprintf(stderr, "(rays don't cross)\n"); - } - } - /* split the curve by t=0.5 */ - nge = newgentry(GEF_FLOAT); - (*nge) = (*ge); - nge->type = GE_CURVE; - - a = ge->prev->fx3; - b = ge->fx1; - c = ge->fx2; - d = ge->fx3; - nge->fx3 = d; - nge->fx2 = (c + d) / 2.; - nge->fx1 = (b + 2. * c + d) / 4.; - ge->fx3 = (a + b * 3. + c * 3. + d) / 8.; - ge->fx2 = (a + 2. * b + c) / 4.; - ge->fx1 = (a + b) / 2.; - - a = ge->prev->fy3; - b = ge->fy1; - c = ge->fy2; - d = ge->fy3; - nge->fy3 = d; - nge->fy2 = (c + d) / 2.; - nge->fy1 = (b + 2. * c + d) / 4.; - ge->fy3 = (a + b * 3. + c * 3. + d) / 8.; - ge->fy2 = (a + 2. * b + c) / 4.; - ge->fy1 = (a + b) / 2.; - - addgeafter(ge, nge); - - if(ISDBG(FCONCISE)) { - dumppaths(g, ge, nge); - } - } -} - -/* free this GENTRY, returns what was ge->next - * (ge must be of type GE_LINE or GE_CURVE) - * works on both float and int entries - */ - -static GENTRY * -freethisge( - GENTRY *ge -) -{ - GENTRY *xge; - - if (ge->bkwd != ge->prev) { - /* at beginning of the contour */ - - xge = ge->bkwd; - if(xge == ge) { /* was the only line in contour */ - /* remove the contour completely */ - /* prev is GE_MOVE, next is GE_PATH, remove them all */ - - /* may be the first contour, then ->bkwd points to ge->entries */ - if(ge->prev->prev == 0) - *(GENTRY **)(ge->prev->bkwd) = ge->next->next; - else - ge->prev->prev->next = ge->next->next; - - if(ge->next->next) { - ge->next->next->prev = ge->prev->prev; - ge->next->next->bkwd = ge->prev->bkwd; - } - - xge = ge->next->next; - free(ge->prev); free(ge->next); free(ge); - return xge; - } - - /* move the start point of the contour */ - if(ge->flags & GEF_FLOAT) { - ge->prev->fx3 = xge->fx3; - ge->prev->fy3 = xge->fy3; - } else { - ge->prev->ix3 = xge->ix3; - ge->prev->iy3 = xge->iy3; - } - } else if(ge->frwd != ge->next) { - /* at end of the contour */ - - xge = ge->frwd->prev; - /* move the start point of the contour */ - if(ge->flags & GEF_FLOAT) { - xge->fx3 = ge->bkwd->fx3; - xge->fy3 = ge->bkwd->fy3; - } else { - xge->ix3 = ge->bkwd->ix3; - xge->iy3 = ge->bkwd->iy3; - } - } - - ge->prev->next = ge->next; - ge->next->prev = ge->prev; - ge->bkwd->frwd = ge->frwd; - ge->frwd->bkwd = ge->bkwd; - - xge = ge->next; - free(ge); - return xge; -} - -/* inserts a new gentry (LINE or CURVE) after another (MOVE - * or LINE or CURVE) - * corrects the first GE_MOVE if neccessary - */ - -static void -addgeafter( - GENTRY *oge, /* after this */ - GENTRY *nge /* insert this */ -) -{ - if(oge->type == GE_MOVE) { - /* insert before next */ - if(oge->next->type == GE_PATH) { - /* first and only GENTRY in path */ - nge->frwd = nge->bkwd = nge; - } else { - nge->frwd = oge->next; - nge->bkwd = oge->next->bkwd; - oge->next->bkwd->frwd = nge; - oge->next->bkwd = nge; - } - } else { - nge->frwd = oge->frwd; - nge->bkwd = oge; - oge->frwd->bkwd = nge; - oge->frwd = nge; - } - - nge->next = oge->next; - nge->prev = oge; - oge->next->prev = nge; - oge->next = nge; - - if(nge->frwd->prev->type == GE_MOVE) { - /* fix up the GE_MOVE entry */ - if(nge->flags & GEF_FLOAT) { - nge->frwd->prev->fx3 = nge->fx3; - nge->frwd->prev->fy3 = nge->fy3; - } else { - nge->frwd->prev->ix3 = nge->ix3; - nge->frwd->prev->iy3 = nge->iy3; - } - } -} - -/* - * Check if this GENTRY happens to be at the end of path - * and fix the first MOVETO accordingly - * handles both int and float - */ - -static void -fixendpath( - GENTRY *ge -) -{ - GENTRY *mge; - - mge = ge->frwd->prev; - if(mge->type == GE_MOVE) { - if(ge->flags & GEF_FLOAT) { - mge->fx3 = ge->fx3; - mge->fy3 = ge->fy3; - } else { - mge->ix3 = ge->ix3; - mge->iy3 = ge->iy3; - } - } -} - -/* - * This function adjusts the rest of path (the part from...to is NOT changed) - * to cover the specified gap by the specified axis (0 - X, 1 - Y). - * Gap is counted in direction (end_of_to - beginning_of_from). - * Returns by how much the gap was not closed (0.0 if it was fully closed). - * Ret contains by how much the first and last points of [from...to] - * were moved to bring them in consistence to the rest of the path. - * If ret==NULL then this info is not returned. - */ - -static double -fclosegap( - GENTRY *from, - GENTRY *to, - int axis, - double gap, - double *ret -) -{ -#define TIMESLARGER 10. /* how many times larger must be a curve to not change too much */ - double rm[2]; - double oldpos[2]; - double times, limit, df, dx; - int j, k; - GENTRY *xge, *pge, *nge, *bge[2]; - - /* remember the old points to calculate ret */ - oldpos[0] = from->prev->fpoints[axis][2]; - oldpos[1] = to->fpoints[axis][2]; - - rm[0] = rm[1] = gap / 2. ; - - bge[0] = from; /* this is convenient for iterations */ - bge[1] = to; - - /* first try to modify large curves but if have none then settle for small */ - for(times = (TIMESLARGER-1); times > 0.1; times /= 2. ) { - - if(rm[0]+rm[1] == 0.) - break; - - /* iterate in both directions, backwards then forwards */ - for(j = 0; j<2; j++) { - - if(rm[j] == 0.) /* if this direction is exhausted */ - continue; - - limit = fabs(rm[j]) * (1.+times); - - for(xge = bge[j]->cntr[j]; xge != bge[!j]; xge = xge->cntr[j]) { - dx = xge->fpoints[axis][2] - xge->prev->fpoints[axis][2]; - df = fabs(dx) - limit; - if( df <= FEPS ) /* curve is too small to change */ - continue; - - if( df >= fabs(rm[j]) ) - df = rm[j]; - else - df *= fsign(rm[j]); /* we may cover this part of rm */ - - rm[j] -= df; - limit = fabs(rm[j]) * (1.+times); - - if(xge->type == GE_CURVE) { /* correct internal points */ - double scale = ((dx+df) / dx) - 1.; - double base; - - if(j) - base = xge->fpoints[axis][2]; - else - base = xge->prev->fpoints[axis][2]; - - for(k = 0; k<2; k++) - xge->fpoints[axis][k] += scale * - (xge->fpoints[axis][k] - base); - } - - /* move all the intermediate lines */ - if(j) { - df = -df; /* absolute direction */ - pge = bge[1]->bkwd; - nge = xge->bkwd; - } else { - xge->fpoints[axis][2] += df; - pge = bge[0]; - nge = xge->frwd; - } - while(nge != pge) { - if(nge->type == GE_CURVE) { - nge->fpoints[axis][0] +=df; - nge->fpoints[axis][1] +=df; - } - nge->fpoints[axis][2] += df; - if(nge->next != nge->frwd) { /* last entry of contour */ - nge->frwd->prev->fpoints[axis][2] += df; - } - nge = nge->cntr[!j]; - } - - if(rm[j] == 0.) - break; - } - } - } - - /* find the difference */ - oldpos[0] -= from->prev->fpoints[axis][2]; - oldpos[1] -= to->fpoints[axis][2]; - - if(ret) { - ret[0] = oldpos[0] - from->prev->fpoints[axis][2]; - ret[1] = oldpos[1] - to->fpoints[axis][2]; - } - -#if 0 - if( rm[0]+rm[1] != gap - oldpos[1] + oldpos[0]) { - fprintf(stderr, "** gap=%g rm[0]=%g rm[1]=%g o[0]=%g o[1]=%g rg=%g og=%g\n", - gap, rm[0], rm[1], oldpos[0], oldpos[1], rm[0]+rm[1], - gap - oldpos[1] + oldpos[0]); - } -#endif - - return rm[0]+rm[1]; -#undef TIMESLARGER -} - -/* remove the lines or curves smaller or equal to the size limit */ - -static void -fdelsmall( - GLYPH *g, - double minlen -) -{ - GENTRY *ge, *nge, *pge, *xge, *next; - int i, k; - double dx, dy, d2, d2m; - double minlen2; -#define TIMESLARGER 10. /* how much larger must be a curve to not change too much */ - - minlen2 = minlen*minlen; - - for (ge = g->entries; ge != 0; ge = next) { - next = ge->next; - - if (ge->type != GE_CURVE && ge->type != GE_LINE) - continue; - - d2m = 0; - for(i= (ge->type==GE_CURVE? 0: 2); i<3; i++) { - dx = ge->fxn[i] - ge->prev->fx3; - dy = ge->fyn[i] - ge->prev->fy3; - d2 = dx*dx + dy*dy; - if(d2m < d2) - d2m = d2; - } - - if( d2m > minlen2 ) { /* line is not too small */ - /* XXX add more normalization here */ - continue; - } - - /* if the line is too small */ - - /* check forwards if we have a whole sequence of them */ - nge = ge; - for(xge = ge->frwd; xge != ge; xge = xge->frwd) { - d2m = 0; - for(i= (xge->type==GE_CURVE? 0: 2); i<3; i++) { - dx = xge->fxn[i] - xge->prev->fx3; - dy = xge->fyn[i] - xge->prev->fy3; - d2 = dx*dx + dy*dy; - if(d2m < d2) - d2m = d2; - } - if( d2m > minlen2 ) /* line is not too small */ - break; - nge = xge; - if(next == nge) /* move the next step past this sequence */ - next = next->next; - } - - /* check backwards if we have a whole sequence of them */ - pge = ge; - for(xge = ge->bkwd; xge != ge; xge = xge->bkwd) { - d2m = 0; - for(i= (xge->type==GE_CURVE? 0: 2); i<3; i++) { - dx = xge->fxn[i] - xge->prev->fx3; - dy = xge->fyn[i] - xge->prev->fy3; - d2 = dx*dx + dy*dy; - if(d2m < d2) - d2m = d2; - } - if( d2m > minlen2 ) /* line is not too small */ - break; - pge = xge; - } - - /* now we have a sequence of small fragments in pge...nge (inclusive) */ - - if(ISDBG(FCONCISE)) { - fprintf(stderr, "glyph %s has very small fragments(%x..%x..%x)\n", - g->name, pge, ge, nge); - dumppaths(g, pge, nge); - } - - /* reduce whole sequence to one part and remember the middle point */ - if(pge != nge) { - while(1) { - xge = pge->frwd; - if(xge == nge) { - pge->fx1 = pge->fx2 = pge->fx3; - pge->fx3 = nge->fx3; - pge->fy1 = pge->fy2 = pge->fy3; - pge->fy3 = nge->fy3; - pge->type = GE_CURVE; - freethisge(nge); - break; - } - if(xge == nge->bkwd) { - pge->fx1 = pge->fx2 = (pge->fx3+xge->fx3)/2.; - pge->fx3 = nge->fx3; - pge->fy1 = pge->fy2 = (pge->fy3+xge->fy3)/2.; - pge->fy3 = nge->fy3; - pge->type = GE_CURVE; - freethisge(nge); - freethisge(xge); - break; - } - freethisge(pge); pge = xge; - xge = nge->bkwd; freethisge(nge); nge = xge; - } - } - ge = pge; - - /* check if the whole sequence is small */ - dx = ge->fx3 - ge->prev->fx3; - dy = ge->fy3 - ge->prev->fy3; - d2 = dx*dx + dy*dy; - - if( d2 > minlen2 ) { /* no, it is not */ - double b, d; - - WARNING_3 fprintf(stderr, "glyph %s had a sequence of fragments < %g points each, reduced to one curve\n", - g->name, minlen); - - /* check that we did not create a monstrosity spanning quadrants */ - if(fsign(ge->fx1 - ge->prev->fx1) * fsign(ge->fx3 - ge->fx1) < 0 - || fsign(ge->fy1 - ge->prev->fy1) * fsign(ge->fy3 - ge->fy1) < 0 ) { - /* yes, we did; are both parts of this thing big enough ? */ - dx = ge->fx1 - ge->prev->fx3; - dy = ge->fy1 - ge->prev->fy3; - d2 = dx*dx + dy*dy; - - dx = ge->fx3 - ge->fx1; - dy = ge->fy3 - ge->fy1; - d2m = dx*dx + dy*dy; - - if(d2 > minlen2 && d2m > minlen2) { /* make two straights */ - nge = newgentry(GEF_FLOAT); - *nge = *ge; - - for(i=0; i<2; i++) { - ge->fpoints[i][2] = ge->fpoints[i][0]; - b = nge->fpoints[i][0]; - d = nge->fpoints[i][2] - b; - nge->fpoints[i][0] = b + 0.1*d; - nge->fpoints[i][1] = b + 0.9*d; - } - } - for(i=0; i<2; i++) { /* make one straight or first of two straights */ - b = ge->prev->fpoints[i][2]; - d = ge->fpoints[i][2] - b; - ge->fpoints[i][0] = b + 0.1*d; - ge->fpoints[i][1] = b + 0.9*d; - } - } - continue; - } - - if(ge->frwd == ge) { /* points to itself, just remove the path completely */ - WARNING_3 fprintf(stderr, "glyph %s had a path made of fragments < %g points each, removed\n", - g->name, minlen); - - next = freethisge(ge); - continue; - } - - /* now close the gap by x and y */ - for(i=0; i<2; i++) { - double gap; - - gap = ge->fpoints[i][2] - ge->prev->fpoints[i][2]; - if( fclosegap(ge, ge, i, gap, NULL) != 0.0 ) { - double scale, base; - - /* not good, as the last resort just scale the next line */ - gap = ge->fpoints[i][2] - ge->prev->fpoints[i][2]; - - if(ISDBG(FCONCISE)) - fprintf(stderr, " last resort on %c: closing next by %g\n", - (i==0 ? 'x' : 'y'), gap); - - nge = ge->frwd; - base = nge->fpoints[i][2]; - dx = ge->fpoints[i][2] - base; - if(fabs(dx) < FEPS) - continue; - - scale = ((dx-gap) / dx); - - if(nge->type == GE_CURVE) - for(k = 0; k<2; k++) - nge->fpoints[i][k] = base + - scale * (nge->fpoints[i][k] - base); - - ge->fpoints[i][2] -= gap; - } - } - - /* OK, the gap is closed - remove this useless GENTRY */ - freethisge(ge); - } -#undef TIMESLARGER -} - -/* find the point where two rays continuing vectors cross - * returns 1 if they cross, 0 if they don't - * If they cross optionally (if the pointers are not NULL) returns - * the maximal scales for both vectors and also optionally the point - * where the rays cross (twice). - * Expects that the curves are normalized. - * - * For convenience there are 2 front-end functions taking - * arguments in different formats - */ - -struct ray { - double x1, y1, x2, y2; - int isvert; - double k, b; /* lines are represented as y = k*x + b */ - double *maxp; -}; -static struct ray ray[3]; - -/* the back-end doing the actual work - * the rays are defined in the static array ray[] - */ - -static int -fcrossraysxx( - double crossdot[2][2] -) -{ - double x, y, max; - int i; - - for(i=0; i<2; i++) { - if(ray[i].x1 == ray[i].x2) - ray[i].isvert = 1; - else { - ray[i].isvert = 0; - ray[i].k = (ray[i].y2 - ray[i].y1) / (ray[i].x2 - ray[i].x1); - ray[i].b = ray[i].y2 - ray[i].k * ray[i].x2; - } - } - - if(ray[0].isvert && ray[1].isvert) { - if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: both vertical\n"); - return 0; /* both vertical, don't cross */ - } - - if(ray[1].isvert) { - ray[2] = ray[0]; /* exchange them */ - ray[0] = ray[1]; - ray[1] = ray[2]; - } - - if(ray[0].isvert) { - x = ray[0].x1; - } else { - if( fabs(ray[0].k - ray[1].k) < FEPS) { - if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: parallel lines, k = %g, %g\n", - ray[0].k, ray[1].k); - return 0; /* parallel lines */ - } - x = (ray[1].b - ray[0].b) / (ray[0].k - ray[1].k) ; - } - y = ray[1].k * x + ray[1].b; - - for(i=0; i<2; i++) { - if(ray[i].isvert) - max = (y - ray[i].y1) / (ray[i].y2 - ray[i].y1); - else - max = (x - ray[i].x1) / (ray[i].x2 - ray[i].x1); - /* check if wrong sides of rays cross */ - if( max < 0 ) { - if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: %c scale=%g @(%g,%g) (%g,%g)<-(%g,%g)\n", - (i?'Y':'X'), max, x, y, ray[i].x2, ray[i].y2, ray[i].x1, ray[i].y1); - return 0; - } - if(ray[i].maxp) - *ray[i].maxp = max; - } - if(crossdot != 0) { - crossdot[0][0] = crossdot[1][0] = x; - crossdot[0][1] = crossdot[1][1] = y; - } - return 1; -} - -/* the front-end getting the arguments from 4 dots defining - * a curve in the same format as for fapproxcurve(): - * rays are defined as beginning and end of the curve, - * the crossdot is inserted as the two middle dots of the curve - */ - -int -fcrossrayscv( - double curve[4][2 /*X,Y*/], - double *max1, - double *max2 -) -{ - ray[0].x1 = curve[0][X]; - ray[0].y1 = curve[0][Y]; - ray[0].x2 = curve[1][X]; - ray[0].y2 = curve[1][Y]; - ray[0].maxp = max1; - - ray[1].x1 = curve[2][X]; - ray[1].y1 = curve[2][Y]; - ray[1].x2 = curve[3][X]; - ray[1].y2 = curve[3][Y]; - ray[1].maxp = max2; - - return fcrossraysxx(&curve[1]); -} - -/* the front-end getting the arguments from gentries: - * rays are defined as beginning of curve1 and end of curve 2 - */ - -int -fcrossraysge( - GENTRY *ge1, - GENTRY *ge2, - double *max1, - double *max2, - double crossdot[2][2] -) -{ - ray[0].x1 = ge1->prev->fx3; - ray[0].y1 = ge1->prev->fy3; - ray[0].x2 = ge1->fpoints[X][ge1->ftg]; - ray[0].y2 = ge1->fpoints[Y][ge1->ftg]; - ray[0].maxp = max1; - - ray[1].x1 = ge2->fx3; - ray[1].y1 = ge2->fy3; - if(ge2->rtg < 0) { - ray[1].x2 = ge2->prev->fx3; - ray[1].y2 = ge2->prev->fy3; - } else { - ray[1].x2 = ge2->fpoints[X][ge2->rtg]; - ray[1].y2 = ge2->fpoints[Y][ge2->rtg]; - } - ray[1].maxp = max2; - - return fcrossraysxx(crossdot); -} - -/* debugging printout functions */ - -#if defined(DEBUG_DOTSEG) || defined(DEBUG_DOTCURVE) || defined(DEBUG_APPROXCV) - -/* for debugging */ -static -printdot( - double dot[2] -) -{ - fprintf(stderr, "(%g,%g)", dot[0], dot[1]); -} - -static -printseg( - double seg[2][2] -) -{ - putc('[', stderr); - printdot(seg[0]); - putc(' ', stderr); - printdot(seg[1]); - putc(']', stderr); -} - -#endif /* DEBUG_* */ - -/* - * Find squared distance from a dot to a line segment - */ - -double -fdotsegdist2( - double seg[2][2 /*X,Y*/], - double dot[2 /*X,Y*/] -) -{ -#define x1 seg[0][X] -#define y1 seg[0][Y] -#define x2 seg[1][X] -#define y2 seg[1][Y] -#define xdot dot[X] -#define ydot dot[Y] - - double dx, dy; /* segment dimensions */ - double kline, bline; /* segment line formula is y=k*x+b */ - double kperp, bperp; /* perpendicular from the dot to the line */ - double xcross, ycross; /* where the perpendicular crosses the segment */ - -/* handle the situation where the nearest point of the segment is its end */ -#define HANDLE_LIMITS(less12, lesscr1, lesscr2) \ - if( less12 ) { \ - if( lesscr1 ) { \ - xcross = x1; \ - ycross = y1; \ - } else if( !(lesscr2) ) { \ - xcross = x2; \ - ycross = y2; \ - } \ - } else { \ - if( !(lesscr1) ) { \ - xcross = x1; \ - ycross = y1; \ - } else if( lesscr2 ) { \ - xcross = x2; \ - ycross = y2; \ - } \ - } \ - /* end of macro */ - - - dx = x2 - x1; - dy = y2 - y1; - - if(fabs(dx) < FEPS) { - /* special case - vertical line */ -#ifdef DEBUG_DOTSEG - printf("vertical line!\n"); -#endif - xcross = x1; - ycross = ydot; - HANDLE_LIMITS( y1 < y2, ycross < y1, ycross < y2); - } else if(fabs(dy) < FEPS) { - /* special case - horizontal line */ -#ifdef DEBUG_DOTSEG - printf("horizontal line!\n"); -#endif - xcross = xdot; - ycross = y1; - HANDLE_LIMITS( x1 < x2, xcross < x1, xcross < x2) - } else { - kline = dy/dx; - bline = y1 - x1*kline; - kperp = -1./kline; - bperp = ydot - xdot*kperp; - - xcross = (bline-bperp) / (kperp-kline); - ycross = kline*xcross + bline; - - HANDLE_LIMITS( x1 < x2, xcross < x1, xcross < x2) - } -#ifdef DEBUG_DOTSEG - printf("crossover at (%g,%g)\n", xcross, ycross); -#endif - - dx = xdot-xcross; - dy = ydot-ycross; - return dx*dx+dy*dy; -#undef x1 -#undef y1 -#undef x2 -#undef y2 -#undef xdot -#undef ydot -#undef HANDLE_LIMITS -} - -/* find the weighted quadratic average for the distance of a set - * of dots from the curve; also fills out the individual distances - * for each dot; if maxp!=NULL then returns the maximal squared - * distance in there - */ - -double -fdotcurvdist2( - double curve[4][2 /*X,Y*/ ], - struct dot_dist *dots, - int ndots, /* number of entries in dots */ - double *maxp -) -{ - /* a curve is approximated by this many straight segments */ -#define NAPSECT 16 - /* a curve is divided into this many sections with equal weight each */ -#define NWSECT 4 - /* table of coefficients for finding the dots on the curve */ - /* tt[0] is left unused */ - static double tt[NAPSECT][4]; - static int havett = 0; /* flag: tt is initialized */ - /* dots on the curve */ - double cvd[NAPSECT+1][2 /*X,Y*/]; - /* sums by sections */ - double sum[NWSECT]; - /* counts by sections */ - double count[NWSECT]; - int d, i, j; - int id1, id2; - double dist1, dist2, dist3, dx, dy, x, y; - double max = 0.; - - if(!havett) { - double t, nt, t2, nt2, step; - - havett++; - step = 1. / NAPSECT; - t = 0; - for(i=1; i<NAPSECT; i++) { - t += step; - nt = 1 - t; - t2 = t*t; - nt2 = nt*nt; - tt[i][0] = nt2*nt; /* (1-t)^3 */ - tt[i][1] = 3*nt2*t; /* 3*(1-t)^2*t */ - tt[i][2] = 3*nt*t2; /* 3*(1-t)*t^2 */ - tt[i][3] = t2*t; /* t^3 */ - } - } - - for(i=0; i<NWSECT; i++) { - sum[i] = 0.; - count[i] = 0; - } - - /* split the curve into segments */ - for(d=0; d<2; d++) { /* X and Y */ - cvd[0][d] = curve[0][d]; /* endpoints */ - cvd[NAPSECT][d] = curve[3][d]; - for(i=1; i<NAPSECT; i++) { - cvd[i][d] = curve[0][d] * tt[i][0] - + curve[1][d] * tt[i][1] - + curve[2][d] * tt[i][2] - + curve[3][d] * tt[i][3]; - } - } - - for(d=0; d<ndots; d++) { -#ifdef DEBUG_DOTCURVE - printf("dot %d ", d); printdot(dots[d].p); printf(":\n"); - - /* for debugging */ - for(i=0; i< NAPSECT; i++) { - dist1 = fdotsegdist2(&cvd[i], dots[d].p); - printf(" seg %d ",i); printseg(&cvd[i]); printf(" dist=%g\n", sqrt(dist1)); - } -#endif - - x = dots[d].p[X]; - y = dots[d].p[Y]; - - /* find the nearest dot on the curve - * there may be up to 2 local minimums - so we start from the - * ends of curve and go to the center - */ - - id1 = 0; - dx = x - cvd[0][X]; - dy = y - cvd[0][Y]; - dist1 = dx*dx + dy*dy; -#ifdef DEBUG_DOTCURVE - printf(" dot 0 "); printdot(cvd[id1]); printf(" dist=%g\n", sqrt(dist1)); -#endif - for(i = 1; i<=NAPSECT; i++) { - dx = x - cvd[i][X]; - dy = y - cvd[i][Y]; - dist3 = dx*dx + dy*dy; -#ifdef DEBUG_DOTCURVE - printf(" dot %d ",i); printdot(cvd[i]); printf(" dist=%g\n", sqrt(dist3)); -#endif - if(dist3 < dist1) { - dist1 = dist3; - id1 = i; - } else - break; - } - - if(id1 < NAPSECT-1) { - id2 = NAPSECT; - dx = x - cvd[NAPSECT][X]; - dy = y - cvd[NAPSECT][Y]; - dist2 = dx*dx + dy*dy; -#ifdef DEBUG_DOTCURVE - printf(" +dot %d ", id2); printdot(cvd[id2]); printf(" dist=%g\n", sqrt(dist2)); -#endif - for(i = NAPSECT-1; i>id1+1; i--) { - dx = x - cvd[i][X]; - dy = y - cvd[i][Y]; - dist3 = dx*dx + dy*dy; -#ifdef DEBUG_DOTCURVE - printf(" dot %d ",i); printdot(cvd[i]); printf(" dist=%g\n", sqrt(dist3)); -#endif - if(dist3 < dist2) { - dist2 = dist3; - id2 = i; - } else - break; - } - - /* now find which of the local minimums is smaller */ - if(dist2 < dist1) { - id1 = id2; - } - } - - /* the nearest segment must include the nearest dot */ - if(id1==0) { - dots[d].seg = 0; - dots[d].dist2 = fdotsegdist2(&cvd[0], dots[d].p); - } else if(id1==NAPSECT) { - dots[d].seg = NAPSECT-1; - dots[d].dist2 = fdotsegdist2(&cvd[NAPSECT-1], dots[d].p); - } else { - dist1 = fdotsegdist2(&cvd[id1], dots[d].p); - dist2 = fdotsegdist2(&cvd[id1-1], dots[d].p); - if(dist2 < dist1) { - dots[d].seg = id1-1; - dots[d].dist2 = dist2; - } else { - dots[d].seg = id1; - dots[d].dist2 = dist1; - } - } - - i = dots[d].seg % NWSECT; - sum[i] += dots[d].dist2; - if(dots[d].dist2 > max) - max = dots[d].dist2; - count[i]++; -#ifdef DEBUG_DOTCURVE - printf(" best seg %d sect %d dist=%g\n", dots[d].seg, i, sqrt(dots[d].dist2)); -#endif - } - - /* calculate the weighted average */ - id1=0; - dist1=0.; - for(i=0; i<NWSECT; i++) { - if(count[i]==0) - continue; - id1++; - dist1 += sum[i]/count[i]; - } - if(maxp) - *maxp = max; - if(id1==0) /* no dots, strange */ - return 0.; - else - return dist1/id1; /* to get the average distance apply sqrt() */ -} - -/* - * Approximate a curve matching the giving set of points and with - * middle reference points going along the given segments (and no farther - * than these segments). - */ - -void -fapproxcurve( - double cv[4][2 /*X,Y*/ ], /* points 0-3 are passed in, points 1,2 - out */ - struct dot_dist *dots, /* the dots to approximate - distances returned - * there may be invalid */ - int ndots -) -{ - /* b and c are the middle control points */ -#define B 0 -#define C 1 - /* maximal number of sections on each axis - used for the first step */ -#define MAXSECT 2 - /* number of sections used for the other steps */ -#define NORMSECT 2 - /* when the steps become less than this many points, it's time to stop */ -#define STEPEPS 1. - double from[2 /*B,C*/], to[2 /*B,C*/]; - double middf[2 /*B,C*/][2 /*X,Y*/], df; - double coef[2 /*B,C*/][MAXSECT]; - double res[MAXSECT][MAXSECT], thisres, bestres, goodres; - int ncoef[2 /*B,C*/], best[2 /*B,C*/], good[2 /*B,C*/]; - int i, j, k, keepsym; - char bc[]="BC"; - char xy[]="XY"; - -#ifdef DEBUG_APPROXCV - fprintf(stderr, "Curve points:"); - for(i=0; i<4; i++) { - fprintf(stderr, " "); - printdot(cv[i]); - } - fprintf(stderr, "\nDots:"); - for(i=0; i<ndots; i++) { - fprintf(stderr, " "); - printdot(dots[i].p); - } - fprintf(stderr, "\n"); -#endif - - /* load the endpoints and calculate differences */ - for(i=0; i<2; i++) { - /* i is X, Y */ - middf[B][i] = cv[1][i]-cv[0][i]; - middf[C][i] = cv[2][i]-cv[3][i]; - - /* i is B, C */ - from[i] = 0.; - to[i] = 1.; - ncoef[i] = MAXSECT; - } - - while(ncoef[B] != 1 || ncoef[C] != 1) { - /* prepare the values of coefficients */ - for(i=0; i<2; i++) { /*B,C*/ -#ifdef DEBUG_APPROXCV - fprintf(stderr, "Coefficients by %c(%g,%g):", bc[i], from[i], to[i]); -#endif - df = (to[i]-from[i]) / (ncoef[i]*2); - for(j=0; j<ncoef[i]; j++) { - coef[i][j] = from[i] + df*(2*j+1); -#ifdef DEBUG_APPROXCV - fprintf(stderr, " %g", coef[i][j]); -#endif - } -#ifdef DEBUG_APPROXCV - fprintf(stderr, "\n"); -#endif - } - bestres = FBIGVAL; - /* i iterates by ncoef[B], j iterates by ncoef[C] */ - for(i=0; i<ncoef[B]; i++) { - for(j=0; j<ncoef[C]; j++) { - for(k=0; k<2; k++) { /*X, Y*/ - cv[1][k] = cv[0][k] + middf[B][k]*coef[B][i]; - cv[2][k] = cv[3][k] + middf[C][k]*coef[C][j]; - } - res[i][j] = thisres = fdotcurvdist2(cv, dots, ndots, NULL); - if(thisres < bestres) { - goodres = bestres; - good[B] = best[B]; - good[C] = best[C]; - bestres = thisres; - best[B] = i; - best[C] = j; - } else if(thisres < goodres) { - goodres = thisres; - good[B] = i; - good[C] = j; - } -#ifdef DEBUG_APPROXCV - fprintf(stderr, " at (%g,%g) dist=%g %s\n", coef[B][i], coef[C][j], sqrt(thisres), - (best[B]==i && best[C]==j)? "(BEST)":""); -#endif - } - } -#ifdef DEBUG_APPROXCV - fprintf(stderr, " best: at (%g, %g) dist=%g\n", - coef[B][best[B]], coef[C][best[C]], sqrt(bestres)); - fprintf(stderr, " B:%d,%d C:%d,%d -- 2nd best: at (%g, %g) dist=%g\n", - best[B], good[B], best[C], good[C], coef[B][good[B]], coef[C][good[C]], sqrt(goodres)); -#endif - - if(bestres < (0.1*0.1)) { /* consider it close enough */ - /* calculate the coordinates to return */ - for(k=0; k<2; k++) { /*X, Y*/ - cv[1][k] = cv[0][k] + middf[B][k]*coef[B][best[B]]; - cv[2][k] = cv[3][k] + middf[C][k]*coef[C][best[C]]; - } -#ifdef DEBUG_APPROXCV - fprintf(stderr, "quick approximated middle points "); printdot(cv[1]); - fprintf(stderr, " "); printdot(cv[2]); fprintf(stderr, "\n"); -#endif - return; - } - keepsym = 0; - if(best[B] != best[C] && best[B]-best[C] == good[C]-good[B]) { - keepsym = 1; -#ifdef DEBUG_APPROXCV - fprintf(stderr, "keeping symmetry!\n"); -#endif - } - for(i=0; i<2; i++) { /*B,C*/ - if(ncoef[i]==1) - continue; - if(keepsym) { - /* try to keep the symmetry */ - if(best[i] < good[i]) { - from[i] = coef[i][best[i]]; - to[i] = coef[i][good[i]]; - } else { - from[i] = coef[i][good[i]]; - to[i] = coef[i][best[i]]; - } - } else { - df = (to[i]-from[i]) / ncoef[i]; - from[i] += df*best[i]; - to[i] = from[i] + df; - } - if( fabs(df*middf[i][0]) < STEPEPS && fabs(df*middf[i][1]) < STEPEPS) { - /* this side has converged */ - from[i] = to[i] = (from[i]+to[i]) / 2.; - ncoef[i] = 1; - } else - ncoef[i] = NORMSECT; - } - - } - /* calculate the coordinates to return */ - for(k=0; k<2; k++) { /*X, Y*/ - cv[1][k] = cv[0][k] + middf[B][k]*from[B]; - cv[2][k] = cv[3][k] + middf[C][k]*from[C]; - } -#ifdef DEBUG_APPROXCV - fprintf(stderr, "approximated middle points "); printdot(cv[1]); - fprintf(stderr, " "); printdot(cv[2]); fprintf(stderr, "\n"); -#endif -#undef B -#undef C -#undef MAXSECT -#undef NORMSECT -#undef STEPEPS -} - -/* - * Find the squared value of the sinus of the angle between the - * end of ge1 and the beginning of ge2 - * The curve must be normalized. - */ - -static double -fjointsin2( - GENTRY *ge1, - GENTRY *ge2 -) -{ - double d[3][2 /*X,Y*/]; - double scale1, scale2, len1, len2; - int axis; - - if(ge1->rtg < 0) { - d[1][X] = ge1->fx3 - ge1->prev->fx3; - d[1][Y] = ge1->fy3 - ge1->prev->fy3; - } else { - d[1][X] = ge1->fx3 - ge1->fpoints[X][ge1->rtg]; - d[1][Y] = ge1->fy3 - ge1->fpoints[Y][ge1->rtg]; - } - d[2][X] = ge2->fpoints[X][ge2->ftg] - ge2->prev->fx3; - d[2][Y] = ge2->fpoints[Y][ge2->ftg] - ge2->prev->fy3; - - len1 = sqrt( d[1][X]*d[1][X] + d[1][Y]*d[1][Y] ); - len2 = sqrt( d[2][X]*d[2][X] + d[2][Y]*d[2][Y] ); - /* scale the 2nd segment to the length of 1 - * and to make sure that the 1st segment is longer scale it to - * the length of 2 and extend to the same distance backwards - */ - scale1 = 2./len1; - scale2 = 1./len2; - - for(axis=0; axis <2; axis++) { - d[0][axis] = -( d[1][axis] *= scale1 ); - d[2][axis] *= scale2; - } - return fdotsegdist2(d, d[2]); -} - -#if 0 -/* find the area covered by the curve - * (limited by the projections to the X axis) - */ - -static double -fcvarea( - GENTRY *ge -) -{ - double Ly, My, Ny, Py, Qx, Rx, Sx; - double area; - - /* y = Ly*t^3 + My*t^2 + Ny*t + Py */ - Ly = -ge->prev->fy3 + 3*(ge->fy1 - ge->fy2) + ge->fy3; - My = 3*ge->prev->fy3 - 6*ge->fy1 + 3*ge->fy2; - Ny = 3*(-ge->prev->fy3 + ge->fy1); - Py = ge->prev->fy3; - - /* dx/dt = Qx*t^2 + Rx*t + Sx */ - Qx = 3*(-ge->prev->fx3 + 3*(ge->fx1 - ge->fx2) + ge->fx3); - Rx = 6*(ge->prev->fx3 - 2*ge->fx1 + ge->fx2); - Sx = 3*(-ge->prev->fx3 + ge->fx1); - - /* area is integral[from 0 to 1]( y(t) * dx(t)/dt *dt) */ - area = 1./6.*(Ly*Qx) + 1./5.*(Ly*Rx + My*Qx) - + 1./4.*(Ly*Sx + My*Rx + Ny*Qx) + 1./3.*(My*Sx + Ny*Rx + Py*Qx) - + 1./2.*(Ny*Sx + Py*Rx) + Py*Sx; - - return area; -} -#endif - -/* find the value of point on the curve at the given parameter t, - * along the given axis (0 - X, 1 - Y). - */ - -static double -fcvval( - GENTRY *ge, - int axis, - double t -) -{ - double t2, mt, mt2; - - /* val = A*(1-t)^3 + 3*B*(1-t)^2*t + 3*C*(1-t)*t^2 + D*t^3 */ - t2 = t*t; - mt = 1-t; - mt2 = mt*mt; - - return ge->prev->fpoints[axis][2]*mt2*mt - + 3*(ge->fpoints[axis][0]*mt2*t + ge->fpoints[axis][1]*mt*t2) - + ge->fpoints[axis][2]*t*t2; -} - -/* - * Find ndots equally spaced dots on a curve or line and fill - * their coordinates into the dots array - */ - -static void -fsampledots( - GENTRY *ge, - double dots[][2], /* the dots to fill */ - int ndots -) -{ - int i, axis; - double t, nf, dx, d[2]; - - nf = ndots+1; - if(ge->type == GE_CURVE) { - for(i=0; i<ndots; i++) { - t = (i+1)/nf; - for(axis=0; axis<2; axis++) - dots[i][axis] = fcvval(ge, axis, t); - } - } else { /* line */ - d[0] = ge->fx3 - ge->prev->fx3; - d[1] = ge->fy3 - ge->prev->fy3; - for(i=0; i<ndots; i++) { - t = (i+1)/nf; - for(axis=0; axis<2; axis++) - dots[i][axis] = ge->prev->fpoints[axis][2] - + t*d[axis]; - } - } -} - -/* - * Allocate a structure gex_con - */ - -static void -alloc_gex_con( - GENTRY *ge -) -{ - ge->ext = (void*)calloc(1, sizeof(GEX_CON)); - if(ge->ext == 0) { - fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); - exit(255); - } -} - -/* - * Normalize a gentry for fforceconcise() : find the points that - * can be used to calculate the tangents. - */ - -static void -fnormalizege( - GENTRY *ge -) -{ - int midsame, frontsame, rearsame; - - if(ge->type == GE_LINE) { - ge->ftg = 2; - ge->rtg = -1; - } else { /* assume it's a curve */ - midsame = (fabs(ge->fx1-ge->fx2)<FEPS && fabs(ge->fy1-ge->fy2)<FEPS); - frontsame = (fabs(ge->fx1-ge->prev->fx3)<FEPS && fabs(ge->fy1-ge->prev->fy3)<FEPS); - rearsame = (fabs(ge->fx3-ge->fx2)<FEPS && fabs(ge->fy3-ge->fy2)<FEPS); - - if(midsame && (frontsame || rearsame) ) { - /* essentially a line */ - ge->ftg = 2; - ge->rtg = -1; - } else { - if(frontsame) { - ge->ftg = 1; - } else { - ge->ftg = 0; - } - if(rearsame) { - ge->rtg = 0; - } else { - ge->rtg = 1; - } - } - } -} - -/* various definition for the processing of outlines */ - -/* maximal average quadratic distance from the original curve - * (in dots) to consider the joined curve good - */ -#define CVEPS 1.5 -#define CVEPS2 (CVEPS*CVEPS) -/* squared sinus of the maximal angle that we consider a smooth joint */ -#define SMOOTHSIN2 0.25 /* 0.25==sin(30 degrees)^2 */ -/* squared line length that we consider small */ -#define SMALL_LINE2 (15.*15.) -/* how many times a curve must be bigger than a line to join, squared */ -#define TIMES_LINE2 (3.*3.) - -/* - * Normalize and analyse a gentry for fforceconcise() and fill out the gex_con - * structure - */ - -static void -fanalyzege( - GENTRY *ge -) -{ - int i, ix, iy; - double avsd2, dots[3][2 /*X,Y*/]; - GEX_CON *gex; - - gex = X_CON(ge); - memset(gex, 0, sizeof *gex); - - gex->len2 = 0; - for(i=0; i<2; i++) { - avsd2 = gex->d[i] = ge->fpoints[i][2] - ge->prev->fpoints[i][2]; - gex->len2 += avsd2*avsd2; - } - gex->sin2 = fjointsin2(ge, ge->frwd); - if(ge->type == GE_CURVE) { - ge->dir = fgetcvdir(ge); - for(i=0; i<2; i++) { - dots[0][i] = ge->prev->fpoints[i][2]; - dots[1][i] = ge->fpoints[i][2]; - dots[2][i] = fcvval(ge, i, 0.5); - } - avsd2 = fdotsegdist2(dots, dots[2]); - if(avsd2 <= CVEPS2) { - gex->flags |= GEXF_FLAT; - } - } else { - ge->dir = CVDIR_FEQUAL|CVDIR_REQUAL; - gex->flags |= GEXF_FLAT; - } - if(gex->flags & GEXF_FLAT) { - if( fabs(gex->d[X]) > FEPS && fabs(gex->d[Y]) < 5. - && fabs(gex->d[Y] / gex->d[X]) < 0.2) - gex->flags |= GEXF_HOR; - else if( fabs(gex->d[Y]) > FEPS && fabs(gex->d[X]) < 5. - && fabs(gex->d[X] / gex->d[Y]) < 0.2) - gex->flags |= GEXF_VERT; - } - ix = gex->isd[X] = fsign(gex->d[X]); - iy = gex->isd[Y] = fsign(gex->d[Y]); - if(ix <= 0) { - if(iy <= 0) - gex->flags |= GEXF_QDL; - if(iy >= 0) - gex->flags |= GEXF_QUL; - if(gex->flags & GEXF_HOR) - gex->flags |= GEXF_IDQ_L; - } - if(ix >= 0) { - if(iy <= 0) - gex->flags |= GEXF_QDR; - if(iy >= 0) - gex->flags |= GEXF_QUR; - if(gex->flags & GEXF_HOR) - gex->flags |= GEXF_IDQ_R; - } - if(gex->flags & GEXF_VERT) { - if(iy <= 0) { - gex->flags |= GEXF_IDQ_U; - } else { /* supposedly there is no 0-sized entry */ - gex->flags |= GEXF_IDQ_D; - } - } -} - -/* - * Analyse a joint between this and following gentry for fforceconcise() - * and fill out the corresponding parts of the gex_con structure - * Bothe entries must be analyzed first. - */ - -static void -fanalyzejoint( - GENTRY *ge -) -{ - GENTRY *nge = ge->frwd; - GENTRY tge; - GEX_CON *gex, *ngex; - double avsd2, dots[3][2 /*X,Y*/]; - int i; - - gex = X_CON(ge); ngex = X_CON(nge); - - /* look if they can be joined honestly */ - - /* if any is flat, they should join smoothly */ - if( (gex->flags & GEXF_FLAT || ngex->flags & GEXF_FLAT) - && gex->sin2 > SMOOTHSIN2) - goto try_flatboth; - - if(ge->type == GE_LINE) { - if(nge->type == GE_LINE) { - if(gex->len2 > SMALL_LINE2 || ngex->len2 > SMALL_LINE2) - goto try_flatboth; - } else { - if(gex->len2*TIMES_LINE2 > ngex->len2) - goto try_flatboth; - } - } else if(nge->type == GE_LINE) { - if(ngex->len2*TIMES_LINE2 > gex->len2) - goto try_flatboth; - } - - /* if curve changes direction */ - if( gex->isd[X]*ngex->isd[X]<0 || gex->isd[Y]*ngex->isd[Y]<0) - goto try_idealone; - - /* if would create a zigzag */ - if( ((ge->dir&CVDIR_FRONT)-CVDIR_FEQUAL) * ((nge->dir&CVDIR_REAR)-CVDIR_REQUAL) < 0 ) - goto try_flatone; - - if( fcrossraysge(ge, nge, NULL, NULL, NULL) ) - gex->flags |= GEXF_JGOOD; - -try_flatone: - /* look if they can be joined by flatting out one of the entries */ - - /* at this point we know that the general direction of the - * gentries is OK - */ - - if( gex->flags & GEXF_FLAT ) { - tge = *ge; - tge.fx1 = tge.fx3; - tge.fy1 = tge.fy3; - fnormalizege(&tge); - if( fcrossraysge(&tge, nge, NULL, NULL, NULL) ) - gex->flags |= GEXF_JFLAT|GEXF_JFLAT1; - } - if( ngex->flags & GEXF_FLAT ) { - tge = *nge; - tge.fx2 = ge->fx3; - tge.fy2 = ge->fy3; - fnormalizege(&tge); - if( fcrossraysge(ge, &tge, NULL, NULL, NULL) ) - gex->flags |= GEXF_JFLAT|GEXF_JFLAT2; - } - -try_idealone: - /* look if one of the entries can be brought to an idealized - * horizontal or vertical position and then joined - */ - if( gex->flags & GEXF_HOR && gex->isd[X]*ngex->isd[X]>=0 ) { - tge = *ge; - tge.fx1 = tge.fx3; - tge.fy1 = ge->prev->fy3; /* force horizontal */ - fnormalizege(&tge); - if( fcrossraysge(&tge, nge, NULL, NULL, NULL) ) - gex->flags |= GEXF_JID|GEXF_JID1; - } else if( gex->flags & GEXF_VERT && gex->isd[Y]*ngex->isd[Y]>=0 ) { - tge = *ge; - tge.fx1 = ge->prev->fx3; /* force vertical */ - tge.fy1 = tge.fy3; - fnormalizege(&tge); - if( fcrossraysge(&tge, nge, NULL, NULL, NULL) ) - gex->flags |= GEXF_JID|GEXF_JID1; - } - if( ngex->flags & GEXF_HOR && gex->isd[X]*ngex->isd[X]>=0 ) { - tge = *nge; - tge.fx2 = ge->fx3; - tge.fy2 = nge->fy3; /* force horizontal */ - fnormalizege(&tge); - if( fcrossraysge(ge, &tge, NULL, NULL, NULL) ) - gex->flags |= GEXF_JID|GEXF_JID2; - } else if( ngex->flags & GEXF_VERT && gex->isd[Y]*ngex->isd[Y]>=0 ) { - tge = *nge; - tge.fx2 = nge->fx3; /* force vertical */ - tge.fy2 = ge->fy3; - fnormalizege(&tge); - if( fcrossraysge(ge, &tge, NULL, NULL, NULL) ) - gex->flags |= GEXF_JID|GEXF_JID2; - } - -try_flatboth: - /* look if we can change them to one line */ - if(gex->flags & GEXF_FLAT && ngex->flags & GEXF_FLAT) { - for(i=0; i<2; i++) { - dots[0][i] = ge->prev->fpoints[i][2]; - dots[1][i] = nge->fpoints[i][2]; - dots[2][i] = ge->fpoints[i][2]; - } - if( fdotsegdist2(dots, dots[2]) <= CVEPS2) - gex->flags |= GEXF_JLINE; - } -} - -/* - * Force conciseness of one contour in the glyph, - * the contour is indicated by one entry from it. - */ - -static void -fconcisecontour( - GLYPH *g, - GENTRY *startge -) -{ -/* initial maximal number of dots to be used as reference */ -#define MAXDOTS ((NREFDOTS+1)*12) - - GENTRY *ge, *pge, *nge, *ige; - GEX_CON *gex, *pgex, *ngex, *nngex; - GENTRY tpge, tnge; - int quad, qq, i, j, ndots, maxdots; - int found[2]; - int joinmask, pflag, nflag; - struct dot_dist *dots; - double avsd2, maxd2, eps2; - double apcv[4][2]; - - if(startge == 0) { - fprintf(stderr, "WARNING: assertion in %s line %d, please report it to the ttf2pt1 project\n", - __FILE__, __LINE__); - fprintf(stderr, "Strange contour in glyph %s\n", g->name); - dumppaths(g, NULL, NULL); - return; - } - - if(startge->type != GE_CURVE && startge->type != GE_LINE) - return; /* probably a degenerate contour */ - - if(ISDBG(FCONCISE)) - fprintf(stderr, "processing contour 0x%p of glyph %s\n", startge, g->name); - - maxdots = MAXDOTS; - dots = (struct dot_dist *)malloc(sizeof(*dots)*maxdots); - if(dots == NULL) { - fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); - exit(255); - } - - ge = startge; - joinmask = GEXF_JGOOD; - while(1) { - restart: - gex = X_CON(ge); - if((gex->flags & GEXF_JMASK) > ((joinmask<<1)-1)) { - if(ISDBG(FCONCISE)) - fprintf(stderr, "found higher flag (%x>%x) at 0x%p\n", - gex->flags & GEXF_JMASK, ((joinmask<<1)-1), ge); - joinmask <<= 1; - startge = ge; /* have to redo the pass */ - continue; - } - if(( gex->flags & joinmask )==0) - goto next; - - /* if we happen to be in the middle of a string of - * joinable entries, find its beginning - */ - if( gex->flags & (GEXF_JCVMASK^GEXF_JID) ) - quad = gex->flags & X_CON_F(ge->frwd) & GEXF_QMASK; - else if( gex->flags & GEXF_JID2 ) - quad = gex->flags & GEXF_QFROM_IDEAL(X_CON_F(ge->frwd)) & GEXF_QMASK; - else /* must be GEXF_JID1 */ - quad = GEXF_QFROM_IDEAL(gex->flags) & X_CON_F(ge->frwd) & GEXF_QMASK; - - pge = ge; - pgex = X_CON(pge->bkwd); - - if(ISDBG(FCONCISE)) - fprintf(stderr, "ge %p prev -> 0x%p ", ge, pge); - - while(pgex->flags & GEXF_JCVMASK) { - if( !(pgex->flags & ((GEXF_JCVMASK^GEXF_JID)|GEXF_JID2)) ) - qq = GEXF_QFROM_IDEAL(pgex->flags); - else - qq = pgex->flags & GEXF_QMASK; - - if(ISDBG(FCONCISE)) - fprintf(stderr, "(%x?%x)", quad, qq); - - if( !(quad & qq) ) { - if( !(X_CON_F(pge) & (GEXF_JCVMASK^GEXF_JID)) - && pgex->flags & (GEXF_JCVMASK^GEXF_JID) ) { - /* the previos entry is definitely a better match */ - if(pge == ge) { - if(ISDBG(FCONCISE)) - fprintf(stderr, "\nprev is a better match at %p\n", pge); - startge = ge; - goto next; - } else - pge = pge->frwd; - } - break; - } - - quad &= qq; - pge = pge->bkwd; - pgex = X_CON(pge->bkwd); - if(ISDBG(FCONCISE)) - fprintf(stderr, "0x%p ", pge); - } - - /* collect as many entries for joining as possible */ - nge = ge->frwd; - ngex = X_CON(nge); - nngex = X_CON(nge->frwd); - - if(ISDBG(FCONCISE)) - fprintf(stderr, ": 0x%x\nnext -> 0x%p ", pge, nge); - - while(ngex->flags & GEXF_JCVMASK) { - if( !(ngex->flags & ((GEXF_JCVMASK^GEXF_JID)|GEXF_JID1)) ) - qq = GEXF_QFROM_IDEAL(nngex->flags); - else - qq = nngex->flags & GEXF_QMASK; - - if(ISDBG(FCONCISE)) - fprintf(stderr, "(%x?%x)", quad, qq); - if( !(quad & qq) ) { - if( !(X_CON_F(nge->bkwd) & (GEXF_JCVMASK^GEXF_JID)) - && ngex->flags & (GEXF_JCVMASK^GEXF_JID) ) { - /* the next-next entry is definitely a better match */ - if(nge == ge->frwd) { - if(ISDBG(FCONCISE)) - fprintf(stderr, "\nnext %x is a better match than %x at %p (jmask %x)\n", - ngex->flags & GEXF_JCVMASK, gex->flags & GEXF_JCVMASK, nge, joinmask); - goto next; - } else - nge = nge->bkwd; - } - break; - } - - quad &= qq; - nge = nge->frwd; - ngex = nngex; - nngex = X_CON(nge->frwd); - if(ISDBG(FCONCISE)) - fprintf(stderr, "0x%p ", nge); - } - - if(ISDBG(FCONCISE)) - fprintf(stderr, ": 0x%x\n", nge); - - /* XXX add splitting of last entries if neccessary */ - - /* make sure that all the reference dots are valid */ - for(ige = pge; ige != nge->frwd; ige = ige->frwd) { - nngex = X_CON(ige); - if( !(nngex->flags & GEXF_VDOTS) ) { - fsampledots(ige, nngex->dots, NREFDOTS); - nngex->flags |= GEXF_VDOTS; - } - } - - /* do the actual joining */ - while(1) { - pgex = X_CON(pge); - ngex = X_CON(nge->bkwd); - /* now the segments to be joined are pge...nge */ - - ndots = 0; - for(ige = pge; ige != nge->frwd; ige = ige->frwd) { - if(maxdots < ndots+(NREFDOTS+1)) { - maxdots += MAXDOTS; - dots = (struct dot_dist *)realloc((void *)dots, sizeof(*dots)*maxdots); - if(dots == NULL) { - fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); - exit(255); - } - } - nngex = X_CON(ige); - for(i=0; i<NREFDOTS; i++) { - for(j=0; j<2; j++) - dots[ndots].p[j] = nngex->dots[i][j]; - ndots++; - } - for(j=0; j<2; j++) - dots[ndots].p[j] = ige->fpoints[j][2]; - ndots++; - } - ndots--; /* the last point is not interesting */ - - tpge = *pge; - pflag = pgex->flags; - if(pflag & (GEXF_JGOOD|GEXF_JFLAT2|GEXF_JID2)) { - /* nothing */ - } else if(pflag & GEXF_JFLAT) { - tpge.fx1 = tpge.fx3; - tpge.fy1 = tpge.fy3; - } else if(pflag & GEXF_JID) { - if(pflag & GEXF_HOR) - tpge.fy1 = tpge.bkwd->fy3; - else - tpge.fx1 = tpge.bkwd->fx3; - } - - tnge = *nge; - nflag = ngex->flags; - if(nflag & (GEXF_JGOOD|GEXF_JFLAT1|GEXF_JID) - && !(nflag & GEXF_JID2)) { - /* nothing */ - } else if(nflag & GEXF_JFLAT) { - tnge.fx2 = tnge.bkwd->fx3; - tnge.fy2 = tnge.bkwd->fy3; - } else if(nflag & GEXF_JID) { - if(X_CON_F(nge) & GEXF_HOR) - tnge.fy2 = tnge.fy3; - else - tnge.fx2 = tnge.fx3; - } - - fnormalizege(&tpge); - fnormalizege(&tnge); - if( fcrossraysge(&tpge, &tnge, NULL, NULL, &apcv[1]) ) { - apcv[0][X] = tpge.bkwd->fx3; - apcv[0][Y] = tpge.bkwd->fy3; - /* apcv[1] and apcv[2] were filled by fcrossraysge() */ - apcv[3][X] = tnge.fx3; - apcv[3][Y] = tnge.fy3; - - /* calculate the precision depending on the smaller dimension of the curve */ - maxd2 = apcv[3][X]-apcv[0][X]; - maxd2 *= maxd2; - eps2 = apcv[3][Y]-apcv[0][Y]; - eps2 *= eps2; - if(maxd2 < eps2) - eps2 = maxd2; - eps2 *= (CVEPS2*4.) / (400.*400.); - if(eps2 < CVEPS2) - eps2 = CVEPS2; - else if(eps2 > CVEPS2*4.) - eps2 = CVEPS2*4.; - - fapproxcurve(apcv, dots, ndots); - - avsd2 = fdotcurvdist2(apcv, dots, ndots, &maxd2); - if(ISDBG(FCONCISE)) - fprintf(stderr, "avsd = %g, maxd = %g, ", sqrt(avsd2), sqrt(maxd2)); - if(avsd2 <= eps2 && maxd2 <= eps2*2.) { - /* we've guessed a curve that is close enough */ - ggoodcv++; ggoodcvdots += ndots; - - if(ISDBG(FCONCISE)) { - fprintf(stderr, "in %s joined %p-%p to ", g->name, pge, nge); - for(i=0; i<4; i++) { - fprintf(stderr, " (%g, %g)", apcv[i][X], apcv[i][Y]); - } - fprintf(stderr, " from\n"); - dumppaths(g, pge, nge); - } - for(i=0; i<3; i++) { - pge->fxn[i] = apcv[i+1][X]; - pge->fyn[i] = apcv[i+1][Y]; - } - pge->type = GE_CURVE; - ge = pge; - for(ige = pge->frwd; ; ige = pge->frwd) { - if(ige == pge) { - fprintf(stderr, "WARNING: assertion in %s line %d, please report it to the ttf2pt1 project\n", - __FILE__, __LINE__); - free(dots); - return; - } - if(startge == ige) - startge = pge; - free(ige->ext); - freethisge(ige); - if(ige == nge) - break; - } - fnormalizege(ge); - if(ISDBG(FCONCISE)) { - fprintf(stderr, "normalized "); - for(i=0; i<3; i++) { - fprintf(stderr, " (%g, %g)", ge->fpoints[X][i], ge->fpoints[Y][i]); - } - fprintf(stderr, "\n"); - } - fanalyzege(ge); - fanalyzejoint(ge); - fanalyzege(ge->bkwd); - fanalyzejoint(ge->bkwd); - - /* the results of this join will have to be reconsidered */ - startge = ge = ge->frwd; - goto restart; - } else { - gbadcv++; gbadcvdots += ndots; - } - } - - /* if we're down to 2 entries then the join has failed */ - if(pge->frwd == nge) { - pgex->flags &= ~joinmask; - if(ISDBG(FCONCISE)) - fprintf(stderr, "no match\n"); - goto next; - } - - /* reduce the number of entries by dropping one at some end, - * should never drop the original ge from the range - */ - - if(nge->bkwd == ge - || (pge != ge && (pgex->flags & GEXF_JCVMASK) <= (ngex->flags & GEXF_JCVMASK)) ) { - pge = pge->frwd; - } else { - nge = nge->bkwd; - } - if(ISDBG(FCONCISE)) - fprintf(stderr, "next try: %p to %p\n", pge, nge); - } - -next: - ge = ge->frwd; - if(ge == startge) { - joinmask = (joinmask >> 1) & GEXF_JCVMASK; - if(joinmask == 0) - break; - } - } - - /* join flat segments into lines */ - /* here ge==startge */ - while(1) { - gex = X_CON(ge); - if( !(gex->flags & GEXF_JLINE) ) - goto next2; - - ndots = 0; - dots[ndots].p[X] = ge->fx3; - dots[ndots].p[Y] = ge->fy3; - ndots++; - - pge = ge->bkwd; - nge = ge->frwd; - - if(ISDBG(FCONCISE)) - fprintf(stderr, "joining LINE from %p-%p\n", ge, nge); - - while(pge!=nge) { - pgex = X_CON(pge); - ngex = X_CON(nge); - if(ISDBG(FCONCISE)) - fprintf(stderr, "(p=%p/%x n=0x%x/%x) ", pge, pgex->flags & GEXF_JLINE, - nge, ngex->flags & GEXF_JLINE); - if( !((pgex->flags | ngex->flags) & GEXF_JLINE) ) { - if(ISDBG(FCONCISE)) - fprintf(stderr, "(end p=%p n=%p) ", pge, nge); - break; - } - - if(maxdots < ndots+2) { - maxdots += MAXDOTS; - dots = (struct dot_dist *)realloc((void *)dots, sizeof(*dots)*maxdots); - if(dots == NULL) { - fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); - exit(255); - } - } - if( pgex->flags & GEXF_JLINE ) { - for(i=0; i<2; i++) { - apcv[0][i] = pge->bkwd->fpoints[i][2]; - apcv[1][i] = nge->fpoints[i][2]; - dots[ndots].p[i] = pge->fpoints[i][2]; - } - ndots++; - for(i=0; i<ndots; i++) { - avsd2 = fdotsegdist2(apcv, dots[i].p); - if(avsd2 > CVEPS2) - break; - } - if(i<ndots) { /* failed to join */ - if(ISDBG(FCONCISE)) - fprintf(stderr, "failed to join prev %p ", pge); - ndots--; - pgex->flags &= ~GEXF_JLINE; - } else { - pge = pge->bkwd; - if(pge == nge) { - if(ISDBG(FCONCISE)) - fprintf(stderr, "intersected at prev %p ", pge); - break; /* oops, tried to self-intersect */ - } - } - } else if(ISDBG(FCONCISE)) - fprintf(stderr, "(p=%p) ", pge); - - if( ngex->flags & GEXF_JLINE ) { - for(i=0; i<2; i++) { - apcv[0][i] = pge->fpoints[i][2]; /* pge points before the 1st segment */ - apcv[1][i] = nge->frwd->fpoints[i][2]; - dots[ndots].p[i] = nge->fpoints[i][2]; - } - ndots++; - for(i=0; i<ndots; i++) { - avsd2 = fdotsegdist2(apcv, dots[i].p); - if(avsd2 > CVEPS2) - break; - } - if(i<ndots) { /* failed to join */ - if(ISDBG(FCONCISE)) - fprintf(stderr, "failed to join next %p ", nge->frwd); - ndots--; - ngex->flags &= ~GEXF_JLINE; - } else { - nge = nge->frwd; - } - } else if(ISDBG(FCONCISE)) - fprintf(stderr, "(n=%p) ", nge); - } - - pge = pge->frwd; /* now the limits are pge...nge inclusive */ - if(pge == nge) /* a deeply perversive contour */ - break; - - if(ISDBG(FCONCISE)) { - fprintf(stderr, "\nin %s joined LINE %p-%p from\n", g->name, pge, nge); - dumppaths(g, pge, nge); - } - pge->type = GE_LINE; - for(i=0; i<2; i++) { - pge->fpoints[i][2] = nge->fpoints[i][2]; - } - fnormalizege(pge); - X_CON_F(pge) &= ~GEXF_JLINE; - - ge = pge; - for(ige = pge->frwd; ; ige = pge->frwd) { - if(ige == pge) { - fprintf(stderr, "WARNING: assertion in %s line %d, please report it to the ttf2pt1 project\n", - __FILE__, __LINE__); - free(dots); - return; - } - if(startge == ige) - startge = pge; - free(ige->ext); - freethisge(ige); - if(ige == nge) - break; - } -next2: - ge = ge->frwd; - if(ge == startge) - break; - } - - free(dots); -} - -/* force conciseness: substitute 2 or more curves going in the -** same quadrant with one curve -** in floating point -*/ - -void -fforceconcise( - GLYPH * g -) -{ - - GENTRY *ge, *nge, *endge, *xge; - - assertisfloat(g, "enforcing conciseness"); - - fdelsmall(g, 0.05); - assertpath(g->entries, __FILE__, __LINE__, g->name); - - if(ISDBG(FCONCISE)) - dumppaths(g, NULL, NULL); - - /* collect more information about each gentry and their joints */ - for (ge = g->entries; ge != 0; ge = ge->next) - if (ge->type == GE_CURVE || ge->type == GE_LINE) - fnormalizege(ge); - - for (ge = g->entries; ge != 0; ge = ge->next) - if (ge->type == GE_CURVE || ge->type == GE_LINE) { - alloc_gex_con(ge); - fanalyzege(ge); - } - - /* see what we can do about joining */ - for (ge = g->entries; ge != 0; ge = ge->next) - if (ge->type == GE_CURVE || ge->type == GE_LINE) - fanalyzejoint(ge); - - /* now do the joining */ - for (ge = g->entries; ge != 0; ge = ge->next) - if(ge->type == GE_MOVE) - fconcisecontour(g, ge->next); - - for (ge = g->entries; ge != 0; ge = ge->next) - if (ge->type == GE_CURVE || ge->type == GE_LINE) - free(ge->ext); -} - -void -print_glyph( - int glyphno -) -{ - GLYPH *g; - GENTRY *ge; - int x = 0, y = 0; - int i; - int grp, lastgrp= -1; - - if(ISDBG(FCONCISE) && glyphno == 0) { - fprintf(stderr, "Guessed curves: bad %d/%d good %d/%d\n", - gbadcv, gbadcvdots, ggoodcv, ggoodcvdots); - } - - g = &glyph_list[glyphno]; - - fprintf(pfa_file, "/%s { \n", g->name); - - /* consider widths >MAXLEGALWIDTH as bugs */ - if( g->scaledwidth <= MAXLEGALWIDTH ) { - fprintf(pfa_file, "0 %d hsbw\n", g->scaledwidth); - } else { - fprintf(pfa_file, "0 1000 hsbw\n"); - WARNING_2 fprintf(stderr, "glyph %s: width %d seems to be buggy, set to 1000\n", - g->name, g->scaledwidth); - } - -#if 0 - fprintf(pfa_file, "%% contours: "); - for (i = 0; i < g->ncontours; i++) - fprintf(pfa_file, "%s(%d,%d) ", (g->contours[i].direction == DIR_OUTER ? "out" : "in"), - g->contours[i].xofmin, g->contours[i].ymin); - fprintf(pfa_file, "\n"); - - if (g->rymin < 5000) - fprintf(pfa_file, "%d lower%s\n", g->rymin, (g->flatymin ? "flat" : "curve")); - if (g->rymax > -5000) - fprintf(pfa_file, "%d upper%s\n", g->rymax, (g->flatymax ? "flat" : "curve")); -#endif - - if (g->hstems) - for (i = 0; i < g->nhs; i += 2) { - if (g->hstems[i].flags & ST_3) { - fprintf(pfa_file, "%d %d %d %d %d %d hstem3\n", - g->hstems[i].value, - g->hstems[i + 1].value - g->hstems[i].value, - g->hstems[i + 2].value, - g->hstems[i + 3].value - g->hstems[i + 2].value, - g->hstems[i + 4].value, - g->hstems[i + 5].value - g->hstems[i + 4].value - ); - i += 4; - } else { - fprintf(pfa_file, "%d %d hstem\n", g->hstems[i].value, - g->hstems[i + 1].value - g->hstems[i].value); - } - } - - if (g->vstems) - for (i = 0; i < g->nvs; i += 2) { - if (g->vstems[i].flags & ST_3) { - fprintf(pfa_file, "%d %d %d %d %d %d vstem3\n", - g->vstems[i].value, - g->vstems[i + 1].value - g->vstems[i].value, - g->vstems[i + 2].value, - g->vstems[i + 3].value - g->vstems[i + 2].value, - g->vstems[i + 4].value, - g->vstems[i + 5].value - g->vstems[i + 4].value - ); - i += 4; - } else { - fprintf(pfa_file, "%d %d vstem\n", g->vstems[i].value, - g->vstems[i + 1].value - g->vstems[i].value); - } - } - - for (ge = g->entries; ge != 0; ge = ge->next) { - if(g->nsg>0) { - grp=ge->stemid; - if(grp >= 0 && grp != lastgrp) { - fprintf(pfa_file, "%d 4 callsubr\n", grp+g->firstsubr); - lastgrp=grp; - } - } - - switch (ge->type) { - case GE_MOVE: - if (absolute) - fprintf(pfa_file, "%d %d amoveto\n", ge->ix3, ge->iy3); - else - rmoveto(ge->ix3 - x, ge->iy3 - y); - if (0) - fprintf(stderr, "Glyph %s: print moveto(%d, %d)\n", - g->name, ge->ix3, ge->iy3); - x = ge->ix3; - y = ge->iy3; - break; - case GE_LINE: - if (absolute) - fprintf(pfa_file, "%d %d alineto\n", ge->ix3, ge->iy3); - else - rlineto(ge->ix3 - x, ge->iy3 - y); - x = ge->ix3; - y = ge->iy3; - break; - case GE_CURVE: - if (absolute) - fprintf(pfa_file, "%d %d %d %d %d %d arcurveto\n", - ge->ix1, ge->iy1, ge->ix2, ge->iy2, ge->ix3, ge->iy3); - else - rrcurveto(ge->ix1 - x, ge->iy1 - y, - ge->ix2 - ge->ix1, ge->iy2 - ge->iy1, - ge->ix3 - ge->ix2, ge->iy3 - ge->iy2); - x = ge->ix3; - y = ge->iy3; - break; - case GE_PATH: - closepath(); - break; - default: - WARNING_1 fprintf(stderr, "**** Glyph %s: unknown entry type '%c'\n", - g->name, ge->type); - break; - } - } - - fprintf(pfa_file, "endchar } ND\n"); -} - -/* print the subroutines for this glyph, returns the number of them */ -int -print_glyph_subs( - int glyphno, - int startid /* start numbering subroutines from this id */ -) -{ - GLYPH *g; - int i, grp; - - g = &glyph_list[glyphno]; - - if(!hints || !subhints || g->nsg<1) - return 0; - - g->firstsubr=startid; - -#if 0 - fprintf(pfa_file, "%% %s %d\n", g->name, g->nsg); -#endif - for(grp=0; grp<g->nsg; grp++) { - fprintf(pfa_file, "dup %d {\n", startid++); - for(i= (grp==0)? 0 : g->nsbs[grp-1]; i<g->nsbs[grp]; i++) - fprintf(pfa_file, "\t%d %d %cstem\n", g->sbstems[i].low, - g->sbstems[i].high-g->sbstems[i].low, - g->sbstems[i].isvert ? 'v' : 'h'); - fprintf(pfa_file, "\treturn\n\t} NP\n"); - } - - return g->nsg; -} - -void -print_glyph_metrics( - int code, - int glyphno -) -{ - GLYPH *g; - - g = &glyph_list[glyphno]; - - if(transform) - fprintf(afm_file, "C %d ; WX %d ; N %s ; B %d %d %d %d ;\n", - code, g->scaledwidth, g->name, - iscale(g->xMin), iscale(g->yMin), iscale(g->xMax), iscale(g->yMax)); - else - fprintf(afm_file, "C %d ; WX %d ; N %s ; B %d %d %d %d ;\n", - code, g->scaledwidth, g->name, - g->xMin, g->yMin, g->xMax, g->yMax); -} - -/* - SB: - An important note about the BlueValues. - - The Adobe documentation says that the maximal width of a Blue zone - is connected to the value of BlueScale, which is by default 0.039625. - The BlueScale value defines, at which point size the overshoot - suppression be disabled. - - The formula for it that is given in the manual is: - - BlueScale=point_size/240, for a 300dpi device - - that makes us wonder what is this 240 standing for. Incidentally - 240=72*1000/300, where 72 is the relation between inches and points, - 1000 is the size of the glyph matrix, and 300dpi is the resolution of - the output device. Knowing that we can recalculate the formula for - the font size in pixels rather than points: - - BlueScale=pixel_size/1000 - - That looks a lot simpler than the original formula, does not it ? - And the limitation about the maximal width of zone also looks - a lot simpler after the transformation: - - max_width < 1000/pixel_size - - that ensures that even at the maximal pixel size when the overshoot - suppression is disabled the zone width will be less than one pixel. - This is important, failure to comply to this limit will result in - really ugly fonts (been there, done that). But knowing the formula - for the pixel width, we see that in fact we can use the maximal width - of 24, not 23 as specified in the manual. - -*/ - -#define MAXBLUEWIDTH (24) - -/* - * Find the indexes of the most frequent values - * in the hystogram, sort them in ascending order, and save which one - * was the best one (if asked). - * Returns the number of values found (may be less than maximal because - * we ignore the zero values) - */ - -#define MAXHYST (2000) /* size of the hystogram */ -#define HYSTBASE 500 - -static int -besthyst( - int *hyst, /* the hystogram */ - int base, /* the base point of the hystogram */ - int *best, /* the array for indexes of best values */ - int nbest, /* its allocated size */ - int width, /* minimal difference between indexes */ - int *bestindp /* returned top point */ -) -{ - unsigned char hused[MAXHYST / 8 + 1]; - int i, max, j, w, last = 0; - int nf = 0; - - width--; - - memset(hused, 0 , sizeof hused); - - max = 1; - for (i = 0; i < nbest && max != 0; i++) { - best[i] = 0; - max = 0; - for (j = 1; j < MAXHYST - 1; j++) { - w = hyst[j]; - - if (w > max && (hused[j>>3] & (1 << (j & 0x07))) == 0) { - best[i] = j; - max = w; - } - } - if (max != 0) { - if (max < last/2) { - /* do not pick the too low values */ - break; - } - for (j = best[i] - width; j <= best[i] + width; j++) { - if (j >= 0 && j < MAXHYST) - hused[j >> 3] |= (1 << (j & 0x07)); - } - last = max; - best[i] -= base; - nf = i + 1; - } - } - - if (bestindp) - *bestindp = best[0]; - - /* sort the indexes in ascending order */ - for (i = 0; i < nf; i++) { - for (j = i + 1; j < nf; j++) - if (best[j] < best[i]) { - w = best[i]; - best[i] = best[j]; - best[j] = w; - } - } - - return nf; -} - -/* - * Find the next best Blue zone in the hystogram. - * Return the weight of the found zone. - */ - -static int -bestblue( - short *zhyst, /* the zones hystogram */ - short *physt, /* the points hystogram */ - short *ozhyst, /* the other zones hystogram */ - int *bluetab /* where to put the found zone */ -) -{ - int i, j, w, max, ind, first, last; - - /* find the highest point in the zones hystogram */ - /* if we have a plateau, take its center */ - /* if we have multiple peaks, take the first one */ - - max = -1; - first = last = -10; - for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) { - w = zhyst[i]; - if (w > max) { - first = last = i; - max = w; - } else if (w == max) { - if (last == i - 1) - last = i; - } - } - ind = (first + last) / 2; - - if (max == 0) /* no zones left */ - return 0; - - /* now we reuse `first' and `last' as inclusive borders of the zone */ - first = ind; - last = ind + (MAXBLUEWIDTH - 1); - - /* our maximal width is far too big, so we try to make it narrower */ - w = max; - j = (w & 1); /* a pseudo-random bit */ - while (1) { - while (physt[first] == 0) - first++; - while (physt[last] == 0) - last--; - if (last - first < (MAXBLUEWIDTH * 2 / 3) || (max - w) * 10 > max) - break; - - if (physt[first] < physt[last] - || (physt[first] == physt[last] && j)) { - if (physt[first] * 20 > w) /* if weight is >5%, - * stop */ - break; - w -= physt[first]; - first++; - j = 0; - } else { - if (physt[last] * 20 > w) /* if weight is >5%, - * stop */ - break; - w -= physt[last]; - last--; - j = 1; - } - } - - /* save our zone */ - bluetab[0] = first - HYSTBASE; - bluetab[1] = last - HYSTBASE; - - /* invalidate all the zones overlapping with this one */ - /* the constant of 2 is determined by the default value of BlueFuzz */ - for (i = first - (MAXBLUEWIDTH - 1) - 2; i <= last + 2; i++) - if (i >= 0 && i < MAXHYST) { - zhyst[i] = 0; - ozhyst[i] = 0; - } - return w; -} - -/* - * Try to find the Blue Values, bounding box and italic angle - */ - -void -findblues(void) -{ - /* hystograms for upper and lower zones */ - short hystl[MAXHYST]; - short hystu[MAXHYST]; - short zuhyst[MAXHYST]; - short zlhyst[MAXHYST]; - int nchars; - int i, j, k, w, max; - GENTRY *ge; - GLYPH *g; - double ang; - - /* find the lowest and highest points of glyphs */ - /* and by the way build the values for FontBBox */ - /* and build the hystogram for the ItalicAngle */ - - /* re-use hystl for the hystogram of italic angle */ - - bbox[0] = bbox[1] = 5000; - bbox[2] = bbox[3] = -5000; - - for (i = 0; i < MAXHYST; i++) - hystl[i] = 0; - - nchars = 0; - - for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { - if (g->flags & GF_USED) { - nchars++; - - g->rymin = 5000; - g->rymax = -5000; - for (ge = g->entries; ge != 0; ge = ge->next) { - if (ge->type == GE_LINE) { - - j = ge->iy3 - ge->prev->iy3; - k = ge->ix3 - ge->prev->ix3; - if (j > 0) - ang = atan2(-k, j) * 180.0 / M_PI; - else - ang = atan2(k, -j) * 180.0 / M_PI; - - k /= 100; - j /= 100; - if (ang > -45.0 && ang < 45.0) { - /* - * be careful to not overflow - * the counter - */ - hystl[HYSTBASE + (int) (ang * 10.0)] += (k * k + j * j) / 4; - } - if (ge->iy3 == ge->prev->iy3) { - if (ge->iy3 <= g->rymin) { - g->rymin = ge->iy3; - g->flatymin = 1; - } - if (ge->iy3 >= g->rymax) { - g->rymax = ge->iy3; - g->flatymax = 1; - } - } else { - if (ge->iy3 < g->rymin) { - g->rymin = ge->iy3; - g->flatymin = 0; - } - if (ge->iy3 > g->rymax) { - g->rymax = ge->iy3; - g->flatymax = 0; - } - } - } else if (ge->type == GE_CURVE) { - if (ge->iy3 < g->rymin) { - g->rymin = ge->iy3; - g->flatymin = 0; - } - if (ge->iy3 > g->rymax) { - g->rymax = ge->iy3; - g->flatymax = 0; - } - } - if (ge->type == GE_LINE || ge->type == GE_CURVE) { - if (ge->ix3 < bbox[0]) - bbox[0] = ge->ix3; - if (ge->ix3 > bbox[2]) - bbox[2] = ge->ix3; - if (ge->iy3 < bbox[1]) - bbox[1] = ge->iy3; - if (ge->iy3 > bbox[3]) - bbox[3] = ge->iy3; - } - } - } - } - - /* get the most popular angle */ - max = 0; - w = 0; - for (i = 0; i < MAXHYST; i++) { - if (hystl[i] > w) { - w = hystl[i]; - max = i; - } - } - ang = (double) (max - HYSTBASE) / 10.0; - WARNING_2 fprintf(stderr, "Guessed italic angle: %f\n", ang); - if (italic_angle == 0.0) - italic_angle = ang; - - /* build the hystogram of the lower points */ - for (i = 0; i < MAXHYST; i++) - hystl[i] = 0; - - for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { - if ((g->flags & GF_USED) - && g->rymin + HYSTBASE >= 0 && g->rymin < MAXHYST - HYSTBASE) { - hystl[g->rymin + HYSTBASE]++; - } - } - - /* build the hystogram of the upper points */ - for (i = 0; i < MAXHYST; i++) - hystu[i] = 0; - - for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { - if ((g->flags & GF_USED) - && g->rymax + HYSTBASE >= 0 && g->rymax < MAXHYST - HYSTBASE) { - hystu[g->rymax + HYSTBASE]++; - } - } - - /* build the hystogram of all the possible lower zones with max width */ - for (i = 0; i < MAXHYST; i++) - zlhyst[i] = 0; - - for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) { - for (j = 0; j < MAXBLUEWIDTH; j++) - zlhyst[i] += hystl[i + j]; - } - - /* build the hystogram of all the possible upper zones with max width */ - for (i = 0; i < MAXHYST; i++) - zuhyst[i] = 0; - - for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) { - for (j = 0; j < MAXBLUEWIDTH; j++) - zuhyst[i] += hystu[i + j]; - } - - /* find the baseline */ - w = bestblue(zlhyst, hystl, zuhyst, &bluevalues[0]); - if (0) - fprintf(stderr, "BaselineBlue zone %d%% %d...%d\n", w * 100 / nchars, - bluevalues[0], bluevalues[1]); - - if (w == 0) /* no baseline, something weird */ - return; - - /* find the upper zones */ - for (nblues = 2; nblues < 14; nblues += 2) { - w = bestblue(zuhyst, hystu, zlhyst, &bluevalues[nblues]); - - if (0) - fprintf(stderr, "Blue zone %d%% %d...%d\n", w * 100 / nchars, - bluevalues[nblues], bluevalues[nblues+1]); - - if (w * 20 < nchars) - break; /* don't save this zone */ - } - - /* find the lower zones */ - for (notherb = 0; notherb < 10; notherb += 2) { - w = bestblue(zlhyst, hystl, zuhyst, &otherblues[notherb]); - - if (0) - fprintf(stderr, "OtherBlue zone %d%% %d...%d\n", w * 100 / nchars, - otherblues[notherb], otherblues[notherb+1]); - - - if (w * 20 < nchars) - break; /* don't save this zone */ - } - -} - -/* - * Find the actual width of the glyph and modify the - * description to reflect it. Not guaranteed to do - * any good, may make character spacing too wide. - */ - -void -docorrectwidth(void) -{ - int i; - GENTRY *ge; - GLYPH *g; - int xmin, xmax; - int maxwidth, minsp; - - /* enforce this minimal spacing, - * we limit the amount of the enforced spacing to avoid - * spacing the bold wonts too widely - */ - minsp = (stdhw>60 || stdhw<10)? 60 : stdhw; - - for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { - g->oldwidth=g->scaledwidth; /* save the old width, will need for AFM */ - - if (correctwidth && g->flags & GF_USED) { - xmin = 5000; - xmax = -5000; - for (ge = g->entries; ge != 0; ge = ge->next) { - if (ge->type != GE_LINE && ge->type != GE_CURVE) - continue; - - if (ge->ix3 <= xmin) { - xmin = ge->ix3; - } - if (ge->ix3 >= xmax) { - xmax = ge->ix3; - } - } - - maxwidth=xmax+minsp; - if( g->scaledwidth < maxwidth ) { - g->scaledwidth = maxwidth; - WARNING_3 fprintf(stderr, "glyph %s: extended from %d to %d\n", - g->name, g->oldwidth, g->scaledwidth ); - } - } - } - -} - -/* - * Try to find the typical stem widths - */ - -void -stemstatistics(void) -{ -#define MINDIST 10 /* minimal distance between the widths */ - int hyst[MAXHYST+MINDIST*2]; - int best[12]; - int i, j, k, w; - int nchars; - int ns; - STEM *s; - GLYPH *g; - - /* start with typical stem width */ - - nchars=0; - - /* build the hystogram of horizontal stem widths */ - memset(hyst, 0, sizeof hyst); - - for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { - if (g->flags & GF_USED) { - nchars++; - s = g->hstems; - for (j = 0; j < g->nhs; j += 2) { - if ((s[j].flags | s[j + 1].flags) & ST_END) - continue; - w = s[j + 1].value - s[j].value+1; - if(w==20) /* split stems should not be counted */ - continue; - if (w > 0 && w < MAXHYST - 1) { - /* - * handle some fuzz present in - * converted fonts - */ - hyst[w+MINDIST] += MINDIST-1; - for(k=1; k<MINDIST-1; k++) { - hyst[w+MINDIST + k] += MINDIST-1-k; - hyst[w+MINDIST - k] += MINDIST-1-k; - } - } - } - } - } - - /* find 12 most frequent values */ - ns = besthyst(hyst+MINDIST, 0, best, 12, MINDIST, &stdhw); - - /* store data in stemsnaph */ - for (i = 0; i < ns; i++) - stemsnaph[i] = best[i]; - if (ns < 12) - stemsnaph[ns] = 0; - - /* build the hystogram of vertical stem widths */ - memset(hyst, 0, sizeof hyst); - - for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { - if (g->flags & GF_USED) { - s = g->vstems; - for (j = 0; j < g->nvs; j += 2) { - if ((s[j].flags | s[j + 1].flags) & ST_END) - continue; - w = s[j + 1].value - s[j].value+1; - if (w > 0 && w < MAXHYST - 1) { - /* - * handle some fuzz present in - * converted fonts - */ - hyst[w+MINDIST] += MINDIST-1; - for(k=1; k<MINDIST-1; k++) { - hyst[w+MINDIST + k] += MINDIST-1-k; - hyst[w+MINDIST - k] += MINDIST-1-k; - } - } - } - } - } - - /* find 12 most frequent values */ - ns = besthyst(hyst+MINDIST, 0, best, 12, MINDIST, &stdvw); - - /* store data in stemsnaph */ - for (i = 0; i < ns; i++) - stemsnapv[i] = best[i]; - if (ns < 12) - stemsnapv[ns] = 0; - -#undef MINDIST -} - -/* - * SB - * A funny thing: TTF paths are going in reverse direction compared - * to Type1. So after all (because the rest of logic uses TTF - * path directions) we have to reverse the paths. - * - * It was a big headache to discover that. - */ - -/* works on both int and float paths */ - -void -reversepathsfromto( - GENTRY * from, - GENTRY * to -) -{ - GENTRY *ge, *nge, *pge; - GENTRY *cur, *next; - int i, n, ilast[2]; - double flast[2], f; - - for (ge = from; ge != 0 && ge != to; ge = ge->next) { - if(ge->type == GE_LINE || ge->type == GE_CURVE) { - if (ISDBG(REVERSAL)) - fprintf(stderr, "reverse path 0x%x <- 0x%x, 0x%x\n", ge, ge->prev, ge->bkwd); - - /* cut out the path itself */ - pge = ge->prev; /* GE_MOVE */ - if (pge == 0) { - fprintf(stderr, "**! No MOVE before line !!! Fatal. ****\n"); - exit(1); - } - nge = ge->bkwd->next; /* GE_PATH */ - pge->next = nge; - nge->prev = pge; - ge->bkwd->next = 0; /* mark end of chain */ - - /* remember the starting point */ - if(ge->flags & GEF_FLOAT) { - flast[0] = pge->fx3; - flast[1] = pge->fy3; - } else { - ilast[0] = pge->ix3; - ilast[1] = pge->iy3; - } - - /* then reinsert them in backwards order */ - for(cur = ge; cur != 0; cur = next ) { - next = cur->next; /* or addgeafter() will screw it up */ - if(cur->flags & GEF_FLOAT) { - for(i=0; i<2; i++) { - /* reverse the direction of path element */ - f = cur->fpoints[i][0]; - cur->fpoints[i][0] = cur->fpoints[i][1]; - cur->fpoints[i][1] = f; - f = flast[i]; - flast[i] = cur->fpoints[i][2]; - cur->fpoints[i][2] = f; - } - } else { - for(i=0; i<2; i++) { - /* reverse the direction of path element */ - n = cur->ipoints[i][0]; - cur->ipoints[i][0] = cur->ipoints[i][1]; - cur->ipoints[i][1] = n; - n = ilast[i]; - ilast[i] = cur->ipoints[i][2]; - cur->ipoints[i][2] = n; - } - } - addgeafter(pge, cur); - } - - /* restore the starting point */ - if(ge->flags & GEF_FLOAT) { - pge->fx3 = flast[0]; - pge->fy3 = flast[1]; - } else { - pge->ix3 = ilast[0]; - pge->iy3 = ilast[1]; - } - - ge = nge; - } - - } -} - -void -reversepaths( - GLYPH * g -) -{ - reversepathsfromto(g->entries, NULL); -} - -/* add a kerning pair information, scales the value */ - -void -addkernpair( - unsigned id1, - unsigned id2, - int unscval -) -{ - static unsigned char *bits = 0; - static int lastid; - GLYPH *g = &glyph_list[id1]; - int i, n; - struct kern *p; - - if(unscval == 0 || id1 >= numglyphs || id2 >= numglyphs) - return; - - if( (glyph_list[id1].flags & GF_USED)==0 - || (glyph_list[id2].flags & GF_USED)==0 ) - return; - - if(bits == 0) { - bits = calloc( BITMAP_BYTES(numglyphs), 1); - if (bits == NULL) { - fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); - exit(255); - } - lastid = id1; - } - - if(lastid != id1) { - /* refill the bitmap cache */ - memset(bits, 0,BITMAP_BYTES(numglyphs)); - p = g->kern; - for(i=g->kerncount; i>0; i--) { - n = (p++)->id; - SET_BITMAP(bits, n); - } - lastid = id1; - } - - if(IS_BITMAP(bits, id2)) - return; /* duplicate */ - - if(g->kerncount <= g->kernalloc) { - g->kernalloc += 8; - p = realloc(g->kern, sizeof(struct kern) * g->kernalloc); - if(p == 0) { - fprintf (stderr, "** realloc failed, kerning data will be incomplete\n"); - } - g->kern = p; - } - - SET_BITMAP(bits, id2); - p = &g->kern[g->kerncount]; - p->id = id2; - p->val = iscale(unscval) - (g->scaledwidth - g->oldwidth); - g->kerncount++; - kerning_pairs++; -} - -/* print out the kerning information */ - -void -print_kerning( - FILE *afm_file -) -{ - int i, j, n; - GLYPH *g; - struct kern *p; - - if( kerning_pairs == 0 ) - return; - - fprintf(afm_file, "StartKernData\n"); - fprintf(afm_file, "StartKernPairs %hd\n", kerning_pairs); - - for(i=0; i<numglyphs; i++) { - g = &glyph_list[i]; - if( (g->flags & GF_USED) ==0) - continue; - p = g->kern; - for(j=g->kerncount; j>0; j--, p++) { - fprintf(afm_file, "KPX %s %s %d\n", g->name, - glyph_list[ p->id ].name, p->val ); - } - } - - fprintf(afm_file, "EndKernPairs\n"); - fprintf(afm_file, "EndKernData\n"); -} - - -#if 0 - -/* -** This function is commented out because the information -** collected by it is not used anywhere else yet. Now -** it only collects the directions of contours. And the -** direction of contours gets fixed already in draw_glyf(). -** -*********************************************** -** -** Here we expect that the paths are already closed. -** We also expect that the contours do not intersect -** and that curves doesn't cross any border of quadrant. -** -** Find which contours go inside which and what is -** their proper direction. Then fix the direction -** to make it right. -** -*/ - -#define MAXCONT 1000 - -void -fixcontours( - GLYPH * g -) -{ - CONTOUR cont[MAXCONT]; - short ymax[MAXCONT]; /* the highest point */ - short xofmax[MAXCONT]; /* X-coordinate of any point - * at ymax */ - short ymin[MAXCONT]; /* the lowest point */ - short xofmin[MAXCONT]; /* X-coordinate of any point - * at ymin */ - short count[MAXCONT]; /* count of lines */ - char dir[MAXCONT]; /* in which direction they must go */ - GENTRY *start[MAXCONT], *minptr[MAXCONT], *maxptr[MAXCONT]; - int ncont; - int i; - int dx1, dy1, dx2, dy2; - GENTRY *ge, *nge; - - /* find the contours and their most upper/lower points */ - ncont = 0; - ymax[0] = -5000; - ymin[0] = 5000; - for (ge = g->entries; ge != 0; ge = ge->next) { - if (ge->type == GE_LINE || ge->type == GE_CURVE) { - if (ge->iy3 > ymax[ncont]) { - ymax[ncont] = ge->iy3; - xofmax[ncont] = ge->ix3; - maxptr[ncont] = ge; - } - if (ge->iy3 < ymin[ncont]) { - ymin[ncont] = ge->iy3; - xofmin[ncont] = ge->ix3; - minptr[ncont] = ge; - } - } - if (ge->frwd != ge->next) { - start[ncont++] = ge->frwd; - ymax[ncont] = -5000; - ymin[ncont] = 5000; - } - } - - /* determine the directions of contours */ - for (i = 0; i < ncont; i++) { - ge = minptr[i]; - nge = ge->frwd; - - if (ge->type == GE_CURVE) { - dx1 = ge->ix3 - ge->ix2; - dy1 = ge->iy3 - ge->iy2; - - if (dx1 == 0 && dy1 == 0) { /* a pathological case */ - dx1 = ge->ix3 - ge->ix1; - dy1 = ge->iy3 - ge->iy1; - } - if (dx1 == 0 && dy1 == 0) { /* a more pathological - * case */ - dx1 = ge->ix3 - ge->prev->ix3; - dy1 = ge->iy3 - ge->prev->iy3; - } - } else { - dx1 = ge->ix3 - ge->prev->ix3; - dy1 = ge->iy3 - ge->prev->iy3; - } - if (nge->type == GE_CURVE) { - dx2 = ge->ix3 - nge->ix1; - dy2 = ge->iy3 - nge->iy1; - if (dx1 == 0 && dy1 == 0) { /* a pathological case */ - dx2 = ge->ix3 - nge->ix2; - dy2 = ge->iy3 - nge->iy2; - } - if (dx1 == 0 && dy1 == 0) { /* a more pathological - * case */ - dx2 = ge->ix3 - nge->ix3; - dy2 = ge->iy3 - nge->iy3; - } - } else { - dx2 = ge->ix3 - nge->ix3; - dy2 = ge->iy3 - nge->iy3; - } - - /* compare angles */ - cont[i].direction = DIR_INNER; - if (dy1 == 0) { - if (dx1 < 0) - cont[i].direction = DIR_OUTER; - } else if (dy2 == 0) { - if (dx2 > 0) - cont[i].direction = DIR_OUTER; - } else if (dx2 * dy1 < dx1 * dy2) - cont[i].direction = DIR_OUTER; - - cont[i].ymin = ymin[i]; - cont[i].xofmin = xofmin[i]; - } - - /* save the information that may be needed further */ - g->ncontours = ncont; - if (ncont > 0) { - g->contours = malloc(sizeof(CONTOUR) * ncont); - if (g->contours == 0) { - fprintf(stderr, "***** Memory allocation error *****\n"); - exit(255); - } - memcpy(g->contours, cont, sizeof(CONTOUR) * ncont); - } -} - -#endif - -/* - * - */ - |