/* Copyright 1990, 1991, 1992 Y&Y, Inc. Copyright 2007 TeX Users Group This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* Program to try and unreverse reversed subpaths * * program to try and split curveto's at extrema * program to try and recombine curveto's generated that way * mostly of use in trying to beat Toal & Raine fonts into shape * * to correct winding number problems use unwind -u * to produce outlines in proper format use unwind -x */ #include #include #include #include #include #include /* #include */ #define MAXLINE 256 /* #define MAXKNOTS 512 */ /* #define MAXKNOTS 1024 */ #define MAXKNOTS 2048 /* #define MAXSUBPATHS 8 */ /* #define MAXSUBPATHS 16 */ /* #define MAXSUBPATHS 32 */ /* #define MAXSUBPATHS 64 */ #define MAXSUBPATHS 128 #define MAXCHRS 256 #define CURRENTDIRECT 1 /* put output file in current directory */ int hits[MAXCHRS]; /* which characters were modified */ int seen[MAXCHRS]; /* which characters were seen */ FILE *fp_in, *fp_out; /* so can use in giveup */ FILE *errout=stdout; /*****************************************************************/ struct subpath { /* structure for subpaths */ int xll; int yll; int xur; int yur; int start; int end; int reverse; int outside; double turn; double area; }; struct knot { /* structure for a knot */ int x; int y; char code; }; char *ext = "unw"; /* default extension for output file */ char *task; /* for error message information output */ static struct knot knots[MAXKNOTS]; static struct subpath subpaths[MAXSUBPATHS]; static char line[MAXLINE]; int nknot, nsubpath; /* number of knots, number of subpaths */ int chr; /* character currently working on */ int debugflag=1; /* enable extra checking */ int verboseflag=0; /* show details of winding number calc */ int detailflag=0; /* show details of winding number calc */ int complainopen=0; /* complain about open paths */ int showflag=0; /* show details of merging of curveto's */ int traceflag=0; /* show details of character read and analyze */ int splitextremaflag=0; /* break at extrema */ int mergeflag=0; /* merge short curveto's generated by splitting */ int moremerges=0; /* reorder path at end and try again */ int flattenflag=0; /* turn almost flat curveto's into lineto's */ int straightenflag=1; /* turn completely flat curveto's into lineto's */ int combineflag=0; /* combine colinear lineto's */ int colinearflag=1; /* combine exactly colinear lineto's */ int complainflag=0; /* complain about bad winding number */ int crossoverflag=0; /* fix cross-overs */ int unwindflag=0; /* split path, get winding, reread and reverse */ int reverseflag=1; /* reverse paths if needed */ int nulloutput=0; /* suppress output */ int showcrosses=0; /* show details of crossovers */ int closepaths=0; /* output final lineto even if not needed */ char *path = ""; /* path for output files */ int extenflag=0; /* next arg is file name extension */ int pathflag=0; /* next arg is path for output */ int splits=0; /* how many curveto's have been split */ int merges=0; /* how many curveto's have been merged */ int flattens=0; /* how many curveto's have been linearized */ int straightens=0; /* how many curveto's have been linearized */ int combines=0; /* how many lineto's have been combined */ int colinears=0; /* how many lineto's have been combined */ int newstyle=0; /* non-zero => new style outlines */ double pi = 3.141592653; /* ---------------------------------------------------------------- */ #define ABS(x) (((x) < 0) ? (-x) : (x)) #define MAX(x, y) (((x) < (y)) ? (y) : (x)) #define MIN(x, y) (((x) < (y)) ? (x) : (y)) /* int levepsilon = 6; */ /* allowed departure from level line */ /* int lenepsilon = 6; */ /* allowed departure from straight line */ double tantiny = 0.1; /* allow 6 degrees between segments */ double tansmall = 0.3; /* allow 17 degree turning when merging */ /* int mlength = 80; */ /* longest curveto considered for merge */ /* int msplit = 16; */ /* largest movement of knot allowed in merge */ int round(double x) { if (x < 0) return (- (round (- x))); else return (int) (x + 0.5); } /* Approx sqrt of sum of squares for range x > 0, y > 0, x > y */ /* r approx a * x + b * x, a = 2/(1+sqrt(4-2*sqrt(2))), b = (sqrt(2)-1)*a */ /* return (int) sqrt ((double) x * (double) x + (double) y * (double) y); */ /* int radius(int x, int y) { int t; if (x < 0) x = -x; if (y < 0) y = -y; if (x < y) {t = x; x = y; y = t;} return (int) (((long) x * 960L + (long) y * 398l) / 1000L); } */ /* void checkintegrity(int start, int end) { assert(knots[start].code == 'M'); assert(knots[end-1].code == 'H'); assert(knots[end-1].x == knots[start].x); assert(knots[end-1].y == knots[start].y); assert(knots[end-2].code == 'L' || knots[end-2].code == 'C'); assert(knots[end-2].x == knots[start].x); assert(knots[end-2].y == knots[start].y); } */ /* move all knots down by dk starting with knot start */ /* may need to preserve code of knot (start - dk) ... */ int shiftdown(int start, int end, int dk) { /* shift knots down by dk */ int i; /* char code; */ assert (start < end); if (debugflag != 0) assert(knots[end-1].code == 'H'); assert(start >= dk); /* code = knots[start - dk].code; */ for (i = start; i < end; i++) knots[i - dk] = knots[i]; /* knots[start - dk].code = code; */ end = end - dk; return end; } /* move all knots up by dk starting with knot (k - dk) */ /* there will be two copies of knots (start - dk) through knot (start - 1) */ int shiftup(int start, int end, int dk) { int i; assert (start < end); if (debugflag != 0) assert(knots[end-1].code == 'H'); end = end + dk; assert(start >= dk); assert(start + dk < MAXKNOTS); for (i = end-1; i > start; i--) knots[i] = knots[i - dk]; return end; } double lepsilon = 8.0; /* premissible lateral offset in flatten line */ double sepsilon = 6.0; /* 4.0 premissible lateral offset in combine line */ /* check if point is near given straight line */ int nearline(int xs, int ys, int xm, int ym, int xf, int yf, double eps) { double fdx, fdy, len; double cros, rho; fdx = (double) (xf - xs); fdy = (double) (yf - ys); cros = (double) ys * (double) xf - (double) xs * (double) yf; len = ABS(fdx) + ABS(fdy); /* favour slanted lines for this */ /* len = sqrt(fdx * fdx + fdy * fdy); */ rho = fdy * (double) xm - fdx * (double) ym + cros; if (ABS(rho) < len * eps) return -1; else return 0; } /* used to avoid combining short, but sharply turning curve segments */ int smallangle(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) { int dx1, dy1, dx3, dy3; double cros, dot; double tantheta; dx1 = x1 - x0; dy1 = y1 - y0; dx3 = x3 - x2, dy3 = y3 - y2; dot = (double) dx1 * (double) dx3 + (double) dy1 * (double) dy3; if(dot < 0L) return 0; cros = (double) dx1 * (double) dy3 - (double) dx3 * (double) dy1; if (dot <= 0L) return 0; tantheta = (double) cros / (double) dot; if (ABS(tantheta) < tantiny) return -1; else return 0; } /* see if curveto is nearly flat */ int flatcurve(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) { if (x0 == x1 && x1 == x2 && x2 == x3) return -1; /* 94/Feb/8 */ if (y0 == y1 && y1 == y2 && y2 == y3) return -1; /* 94/Feb/8 */ if ((nearline(x0, y0, x1, y1, x3, y3, lepsilon) != 0) && (nearline(x0, y0, x2, y2, x3, y3, lepsilon) != 0) && (smallangle(x0, y0, x1, y1, x2, y2, x3, y3) != 0)) return -1; else return 0; } int mincurve(int za, int zb, int zc, int zd) { /* min of four numbers */ int zab, zcd; zab = MIN(za, zb); zcd = MIN(zc, zd); return MIN(zab, zcd); } int maxcurve(int za, int zb, int zc, int zd) { /* max of four numbers */ int zab, zcd; zab = MAX(za, zb); zcd = MAX(zc, zd); return MAX(zab, zcd); } int mindy=6; /* try not to flatten bowed serifs */ int mindx=2; /* try not to flatten tapered stems */ /* turn flat curveto's into lineto's */ int flattencurves(int start, int end) { int xmin, xmax, ymin, ymax; int k, dy, dx, xa, ya, xb, yb, xc, yc, xd, yd; for (k = start + 3; k < end; k++) { if (knots[k].code == 'C') { ya=knots[k-3].y; yb=knots[k-2].y; yc=knots[k-1].y; yd=knots[k].y; ymin = mincurve(ya, yb, yc, yd); ymax = maxcurve(ya, yb, yc, yd); dy = ymax - ymin; if (ABS(dy) < mindy) continue; xa=knots[k-3].x; xb=knots[k-2].x; xc=knots[k-1].x; xd=knots[k].x; xmin = mincurve(xa, xb, xc, xd); xmax = maxcurve(xa, xb, xc, xd); dx = xmax - xmin; if (ABS(dx) < mindx) continue; if (flatcurve(xa, ya, xb, yb, xc, yc, xd, yd) != 0) { /* knots[k].code = 'L'; */ end = shiftdown(k, end, 2); knots[k-2].code = 'L'; /* assert(knots[k-2].code == 'L'); */ flattens++; hits[chr]++; k--; /* not useful ? */ } } } return end; } int straightencurves(int start, int end) { /* 1994/Feb/8 */ int xmin, xmax, ymin, ymax; int k, dy, dx, xa, ya, xb, yb, xc, yc, xd, yd; for (k = start + 3; k < end; k++) { if (knots[k].code == 'C') { ya=knots[k-3].y; yb=knots[k-2].y; yc=knots[k-1].y; yd=knots[k].y; ymin = mincurve(ya, yb, yc, yd); ymax = maxcurve(ya, yb, yc, yd); dy = ymax - ymin; /* if (ABS(dy) < mindy) continue; */ xa=knots[k-3].x; xb=knots[k-2].x; xc=knots[k-1].x; xd=knots[k].x; xmin = mincurve(xa, xb, xc, xd); xmax = maxcurve(xa, xb, xc, xd); dx = xmax - xmin; /* if (ABS(dx) < mindx) continue; */ if (dx == 0 || dy == 0) { /* knots[k].code = 'L'; */ end = shiftdown(k, end, 2); knots[k-2].code = 'L'; /* assert(knots[k-2].code == 'L'); */ straightens++; hits[chr]++; k--; /* not useful ? */ } } } return end; } /* don't mess with near vertical and near horizontal lines */ int nottang(int xs, int ys, int xm, int ym, int xf, int yf) { int dx, dy; dx = xs - xm; dy = ys - ym; if (ABS(dx) <= 1 || ABS(dy) <= 1) return 0; dx = xf - xm; dy = yf - ym; if (ABS(dx) <= 1 || ABS(dy) <= 1) return 0; return -1; } int combinelines(int start, int end) { /* combine colinear linetos */ int k, xa, ya, xb, yb, xc, yc; for (k = start + 2; k < end; k++) { if (knots[k].code == 'L' && knots[k-1].code == 'L') { xa=knots[k-2].x; ya=knots[k-2].y; xb=knots[k-1].x; yb=knots[k-1].y; xc=knots[k].x; yc=knots[k].y; if (nottang(xa, ya, xb, yb, xc, yc) != 0 && nearline(xa, ya, xb, yb, xc, yc, sepsilon) != 0) { end = shiftdown(k, end, 1); combines++; hits[chr]++; k--; /* a little dangerous - accumulate errors ? */ } } } return end; } int mergelines(int start, int end) { /* combine colinear linetos */ int k, xa, ya, xb, yb, xc, yc; for (k = start + 2; k < end; k++) { if (knots[k].code == 'L' && knots[k-1].code == 'L') { xa=knots[k-2].x; ya=knots[k-2].y; xb=knots[k-1].x; yb=knots[k-1].y; xc=knots[k].x; yc=knots[k].y; if ((xa == xb && xb == xc) || (ya == yb && yb == yc)) { end = shiftdown(k, end, 1); colinears++; hits[chr]++; k--; } } } return end; } void showsubpathsub(int start, int end) { int k; printf("\n"); printf("Subpath start %d end %d\n", start, end); for (k = start; k < end; k++) { /* if (closepaths == 0 && k == end-2 && knots[k].code == 'L') { if (knots[k].x == knots[start].x && knots[k].y == knots[start].y) continue; } */ printf("%d %d %c \n", knots[k].x, knots[k].y, knots[k].code); } } void showsubpath(int k) { int start, end; start = subpaths[k].start; end = subpaths[k].end; showsubpathsub(start, end); } /* rearrange knots so something else appears in first spot */ int rearrange(int start, int end) { int k, i, code; if (knots[start].code != 'M') { showsubpathsub(start, end); fflush(stdout); (void) _getch(); } fflush(stdout); if (debugflag != 0) { assert(knots[start].code == 'M'); assert(knots[end-1].code == 'H'); assert(knots[end-1].x == knots[start].x); assert(knots[end-1].y == knots[start].y); assert(knots[end-2].code == 'L' || knots[end-2].code == 'C'); assert(knots[end-2].x == knots[start].x); assert(knots[end-2].y == knots[start].y); } k = (start + end) / 2 ; for(;;) { code = knots[k].code; if (code == 'L' || code == 'C') break; k++; if (k >= end) k = start; } assert(end + k + 1 < MAXKNOTS); for (i = start; i < k; i++) knots[i - start + end - 1] = knots[i+1]; for (i = start; i < end; i++) knots[i] = knots[i + k - start]; knots[start].code = 'M'; knots[end - 1] = knots[start]; knots[start].code = 'M'; knots[end - 1].code = 'H'; if (knots[start].code != 'M') { showsubpathsub(start, end); fflush(stdout); (void) _getch(); } fflush(stdout); if (debugflag != 0) { assert(knots[start].code == 'M'); assert(knots[end-1].code == 'H'); assert(knots[end-1].x == knots[start].x); assert(knots[end-1].y == knots[start].y); assert(knots[end-2].code == 'L' || knots[end-2].code == 'C'); assert(knots[end-2].x == knots[start].x); assert(knots[end-2].y == knots[start].y); } return end; } /* check whether Bezier curve has extremum */ double extrema(int z0, int z1, int z2, int z3) { int minz, maxz; int az, bz, cz; double discr; double rootdiscr, root, root1, root2; minz = MIN(z0, z3); maxz = MAX(z0, z3); if (z1 >= minz && z1 <= maxz && z2 >= minz && z2 <= maxz) return -1.0; az = z3 - 3 * (z2 - z1) - z0; bz = 3 * (z2 - 2 * z1 + z0); cz = 3 * (z1 - z0); if (az == 0) { if (bz == 0) return -1.0; else { root = - (double) cz / (2.0 * (double) bz); if (root > 0.0 && root < 1.0) return root; else return -1.0; } } discr = (double) bz * (double) bz - 3 * (double) az * (double) cz; if (discr < 0) return -1.0; rootdiscr = sqrt((double) discr); root1 = (- (double) bz + rootdiscr) / (3.0 * (double) az); if (root1 > 0.0 && root1 < 1.0) return root1; root2 = (- (double) bz - rootdiscr) / (3.0 * (double) az); if (root2 > 0.0 && root2 < 1.0) return root2; return -1.0; } /* split Bezier curve at t = alp */ void splitextrema(int k, double alp) { double f0, f1, f2, f3; double a, b, c, d, mal; double an, bn, cn, dn; f0 = (double) knots[k-3].x; f1 = (double) knots[k-2].x; f2 = (double) knots[k-1].x; f3 = (double) knots[k].x; a = (f3 - f0) - 3.0 * (f2 - f1); b = 3.0 * (f2 - 2.0 * f1 + f0); c = 3.0 * (f1 - f0); d = f0; an = a * alp * alp * alp; bn = b * alp * alp; cn = c * alp; dn = d; /* assert(knots[k-3].x == f0); */ /* assert(knots[k-3].x == round(dn)); */ if (knots[k-3].x != round(dn)) { fprintf(errout, "knots[%d-3].x %lg round(dn)\n", k, knots[k-3].x, round(dn));; } knots[k - 2].x = round(dn + cn / 3.0); knots[k - 1].x = round(dn + (2.0 * cn + bn) / 3.0); knots[k].x = round(dn + cn + bn + an); mal = 1.0 - alp; an = a * mal * mal * mal; bn = (3.0 * a * alp + b) * mal * mal; cn = (alp * (3.0 * a * alp + 2.0 * b) + c) * mal; dn = alp * ( alp * (alp * a + b) + c) + d; assert(knots[k].x == round(dn)); knots[k+1].x = round(dn + cn / 3.0); knots[k+2].x = round(dn + (2.0 * cn + bn) / 3.0); assert(knots[k+3].x == round(f3)); /* assert(knots[k+3].x == round(dn + cn + bn + an)); */ if (knots[k+3].x != round(dn+cn+bn+an)) { fprintf(errout, "knots[%d+3].x %lg round(dn+cn+bn+an)\n", k, knots[k+3].x, round(dn+cn+bn+an));; } f0 = (double) knots[k-3].y; f1 = (double) knots[k-2].y; f2 = (double) knots[k-1].y; f3 = (double) knots[k].y; a = (f3 - f0) - 3.0 * (f2 - f1); b = 3.0 * (f2 - 2.0 * f1 + f0); c = 3.0 * (f1 - f0); d = f0; an = a * alp * alp * alp; bn = b * alp * alp; cn = c * alp; dn = d; /* assert(knots[k-3].y == f0); */ /* assert(knots[k-3].y == round(dn)); */ if (knots[k-3].y != round(dn)) { fprintf(errout, "knots[%d-3].y %lg round(dn)\n", k, knots[k-3].y, round(dn));; } knots[k - 2].y = round(dn + cn / 3.0); knots[k - 1].y = round(dn + (2.0 * cn + bn) / 3.0); knots[k].y = round(dn + cn + bn + an); mal = 1.0 - alp; an = a * mal * mal * mal; bn = (3.0 * a * alp + b) * mal * mal; cn = (alp * (3.0 * a * alp + 2.0 * b) + c) * mal; dn = alp * ( alp * (alp * a + b) + c) + d; assert(knots[k].y == round(dn)); knots[k+1].y = round(dn + cn / 3.0); knots[k+2].y = round(dn + (2.0 * cn + bn) / 3.0); assert(knots[k+3].y == round(f3)); /* assert(knots[k+3].y == round(dn + cn + bn + an)); */ if (knots[k+3].y != round(dn+cn+bn+an)) { fprintf(errout, "knots[%d+3].y %lg round(dn+cn+bn+an)\n", k, knots[k+3].y, round(dn+cn+bn+an));; } splits++; /* count it */ hits[chr]++; } double distance (double xa, double ya, double xb, double yb) { double dx, dy; dx = xb - xa; dy = yb -ya; return sqrt (dx * dx + dy * dy); } #define POLY(a, b, c, d, ft) ((((a) * (ft) + (b)) * (ft) + (c)) * (ft) + (d)) double bezier(int z0, int z1, int z2, int z3, double ft) { double fz0, fz1, fz2, fz3; double az, bz, cz; fz0 = (double) z0; fz1 = (double) z1; fz2 = (double) z2; fz3 = (double) z3; cz = (fz1 - fz0) * 3.0; bz = ((fz2 - fz1) - (fz1 - fz0)) * 3.0; az = (fz3 - fz0) + 3.0 * (fz1 - fz2); return POLY(az, bz, cz, fz0, ft); } int nearvertical(double dx, double dy) { if (ABS(dx) < tansmall * ABS(dy)) return -1; else return 0; } int nearhorizontal(double dx, double dy) { if (ABS(dy) < tansmall * ABS(dx)) return -1; else return 0; } double acrosslim = 5.0; /* was 2.0 */ double alonglim = 12.0; /* was 5.0 */ double ftlim = 1.5; /* limit on t at tangency point */ /* new short curveto is from k-3 to k, old longer curveto from k to k+3 */ /* the vertical tangent occurs at k-3 and must be preserved */ /* see if can patch together - vertical tangent */ int trypatchxup(int k) { int xv, yv, x0, y0, x1, y1, x2, y2, x3, y3; double rbv, rbt, rbn, ft, fxv, fyv, fxn, fyn, fdx, fdy; double fx0, fx1, fx2, fx3, fy0, fy1, fy2, fy3; double ax, bx, cx, dx, ay, by, cy, dy; if (mergeflag == 0) return 0; /* if (k + 3 > end - 2) return 0; */ xv = knots[k-3].x; yv = knots[k-3].y; /* place of tangency */ if (showflag != 0) printf("\nXUP: tangent at (%d, %d) ", xv, yv); fxv = (double) xv; fyv = (double) yv; assert (knots[k-3].x == knots[k-2].x); x3 = knots[k].x; y3 = knots[k].y; x2 = knots[k+1].x; y2 = knots[k+1].y; fx3 = (double) x3; fy3 = (double) y3; fx2 = (double) x2; fy2 = (double) y2; /* check on angle of turn in short segment */ if (showflag != 0) printf("- turning (%lg %lg)", fx3 - fx2, fy3 - fy2); if (nearvertical(fx3 - fx2, fy3 - fy2) == 0) { if (showflag != 0) { printf(" FAILED\n"); /* (void) getch(); */ } return 0; } rbv = distance(fxv, fyv, fx3, fy3); rbt = distance(fx3, fy3, fx2, fy2); assert (rbt != 0.0); /* assert (rbv != 0.0); */ /* if (rbv == 0.0) combine ? */ ft = 1.0 + rbv/(3.0 * rbt); /* estimated t at xv, yv */ if (showflag != 0) printf("- estimated t = %lg ", ft); assert (ft >= 1.0); if (ft > ftlim) { if (showflag != 0) { printf("FAILED\n"); /* (void) getch(); */ } return 0; /* don't expect to win */ } /* compute coefficients of the two cubics */ x0 = knots[k+3].x; x1 = knots[k+2].x; fx0 = (double) x0; fx1 = (double) x1; dx = fx0; cx = (fx1 - fx0) * 3.0; bx = ((fx2 - fx1) - (fx1 - fx0)) * 3.0; ax = (fx3 - fx0) + 3.0 * (fx1 - fx2); fxn = POLY(ax, bx, cx, fx0, ft); assert (fxn == bezier(x0, x1, x2, x3, ft)); y0 = knots[k+3].y; y1 = knots[k+2].y; fy0 = (double) y0; fy1 = (double) y1; dy = fy0; cy = (fy1 - fy0) * 3.0; by = ((fy2 - fy1) - (fy1 - fy0)) * 3.0; ay = (fy3 - fy0) + 3.0 * (fy1 - fy2); fyn = POLY(ay, by, cy, fy0, ft); assert (fyn == bezier(y0, y1, y2, y3, ft)); if (showflag != 0) printf("\nOLD: (%d %d) (%d %d) (%d %d) (%d %d)\n", x0, y0, x1, y1, x2, y2, x3, y3); fdx = fxv - fxn; fdy = fyv - fyn; if (showflag != 0) printf("offset old (%lg, %lg) ", fdx, fdy); /* not clear the following is worth it */ /* or maybe always do this ? */ if (ABS(fdx) > acrosslim || ABS(fdy) > alonglim) { rbn = distance(fx3, fy3, fxn, fyn); ft = 1.0 + (ft - 1.0) * rbv / rbn; fxn = POLY(ax, bx, cx, fx0, ft); fyn = POLY(ay, by, cy, fy0, ft); fdx = fxv - fxn; fdy = fyv - fyn; if (showflag != 0) printf("Adjusting t = %lg\n", ft); if (showflag != 0) printf("offset old (%lg, %lg) ", fdx, fdy); } if (ABS(fdx) > acrosslim || ABS(fdy) > alonglim) { if (showflag != 0) { printf("FAILED\n"); /* (void) getch(); */ } return 0; /* not close enough */ } /* compute coefficients of extended cubics */ ax = ax * ft * ft * ft; bx = bx * ft * ft; cx = cx * ft; ay = ay * ft * ft * ft; by = by * ft * ft; cy = cy * ft; /* compute new knots */ /* x0 unchanged */ x1 = round(dx + cx / 3.0); x2 = round(dx + (2.0 * cx + bx) / 3.0); x3 = round(dx + cx + bx + ax); /* y0 unchanged */ y1 = round(dy + cy / 3.0); y2 = round(dy + (2.0 * cy + by) / 3.0); y3 = round(dy + cy + by + ay); /* snap to knots */ x3 = xv; y3 = yv; x2 = xv; /* check that new curve passes near (xb, yb) */ fxn = bezier(x0, x1, x2, x3, 1/ft); fyn = bezier(y0, y1, y2, y3, 1/ft); fdx = fxn - fx3; fdy = fyn - fy3; /* old values */ if (showflag != 0) printf("- offset new (%lg, %lg) ", fdx, fdy); if (ABS(fdx) > acrosslim || ABS(fdy) > alonglim) { if (showflag != 0) { printf("FAILED\n"); /* (void) getch(); */ } return 0; /* not close enough */ } /* insert new knots, ready for shift down */ /* worry about code at k - 3 ? */ knots[k].x = x3; knots[k].y = y3; knots[k+1].x = x2; knots[k+1].y = y2; knots[k+2].x = x1; knots[k+2].y = y1; assert(knots[k+3].x == x0); assert(knots[k+3].y == y0); assert (knots[k].x == knots[k-3].x); assert (knots[k].y == knots[k-3].y); if (showflag != 0) printf("\nNEW: (%d %d) (%d %d) (%d %d) (%d %d) ", x0, y0, x1, y1, x2, y2, x3, y3); if (showflag != 0) { printf("SUCCESS\n"); /* (void) getch(); */ } merges++; /* count it */ hits[chr]++; return -1; } /* try to patch together - vertical tangent */ int trypatchxdown(int k) { int xv, yv, x0, y0, x1, y1, x2, y2, x3, y3; double rbv, rbt, rbn, ft, fxv, fyv, fxn, fyn, fdx, fdy; double fx0, fx1, fx2, fx3, fy0, fy1, fy2, fy3; double ax, bx, cx, dx, ay, by, cy, dy; if (mergeflag == 0) return 0; /* if (k - 3 < start) return 0; */ xv = knots[k+3].x; yv = knots[k+3].y; /* place of tangency */ if (showflag != 0) printf("\nXDOWN: tangent at (%d, %d) ", xv, yv); fxv = (double) xv; fyv = (double) yv; assert (knots[k+3].x == knots[k+2].x); x3 = knots[k].x; y3 = knots[k].y; x2 = knots[k-1].x; y2 = knots[k-1].y; fx3 = (double) x3; fy3 = (double) y3; fx2 = (double) x2; fy2 = (double) y2; /* check on angle of turn in short segment */ if (showflag != 0) printf("- turning (%lg %lg)", fx3 - fx2, fy3 - fy2); if (nearvertical(fx3 - fx2, fy3 - fy2) == 0) { if (showflag != 0) { printf(" FAILED\n"); /* (void) getch(); */ } return 0; } rbv = distance(fxv, fyv, fx3, fy3); rbt = distance(fx3, fy3, fx2, fy2); assert (rbt != 0.0); /* assert (rbv != 0.0); */ /* if (rbv == 0.0) combine ? */ ft = 1.0 + rbv/(3.0 * rbt); /* estimated t at xv, yv */ if (showflag != 0) printf("- estimated t = %lg ", ft); assert (ft >= 1.0); if (ft > ftlim) { if (showflag != 0) { printf("FAILED\n"); /* (void) getch(); */ } return 0; /* don't expect to win */ } /* compute coefficients of the two cubics */ x0 = knots[k-3].x; x1 = knots[k-2].x; fx0 = (double) x0; fx1 = (double) x1; dx = fx0; cx = (fx1 - fx0) * 3.0; bx = ((fx2 - fx1) - (fx1 - fx0)) * 3.0; ax = (fx3 - fx0) + 3.0 * (fx1 - fx2); fxn = POLY(ax, bx, cx, fx0, ft); assert (fxn == bezier(x0, x1, x2, x3, ft)); y0 = knots[k-3].y; y1 = knots[k-2].y; fy0 = (double) y0; fy1 = (double) y1; dy = fy0; cy = (fy1 - fy0) * 3.0; by = ((fy2 - fy1) - (fy1 - fy0)) * 3.0; ay = (fy3 - fy0) + 3.0 * (fy1 - fy2); fyn = POLY(ay, by, cy, fy0, ft); assert (fyn == bezier(y0, y1, y2, y3, ft)); if (showflag != 0) printf("\nOLD: (%d %d) (%d %d) (%d %d) (%d %d)\n", x0, y0, x1, y1, x2, y2, x3, y3); fdx = fxv - fxn; fdy = fyv - fyn; if (showflag != 0) printf("offset old (%lg, %lg) ", fdx, fdy); /* not clear the following is worth it */ /* or maybe always do this ? */ if (ABS(fdx) > acrosslim || ABS(fdy) > alonglim) { rbn = distance(fx3, fy3, fxn, fyn); ft = 1.0 + (ft - 1.0) * rbv / rbn; fxn = POLY(ax, bx, cx, fx0, ft); fyn = POLY(ay, by, cy, fy0, ft); fdx = fxv - fxn; fdy = fyv - fyn; if (showflag != 0) printf("Adjusting t = %lg\n", ft); if (showflag != 0) printf("offset old (%lg, %lg) ", fdx, fdy); } if (ABS(fdx) > acrosslim || ABS(fdy) > alonglim) { if (showflag != 0) { printf("FAILED\n"); /* (void) getch(); */ } return 0; /* not close enough */ } /* compute coefficients of extended cubics */ ax = ax * ft * ft * ft; bx = bx * ft * ft; cx = cx * ft; ay = ay * ft * ft * ft; by = by * ft * ft; cy = cy * ft; /* compute new knots */ /* x0 unchanged */ x1 = round(dx + cx / 3.0); x2 = round(dx + (2.0 * cx + bx) / 3.0); x3 = round(dx + cx + bx + ax); /* y0 unchanged */ y1 = round(dy + cy / 3.0); y2 = round(dy + (2.0 * cy + by) / 3.0); y3 = round(dy + cy + by + ay); /* snap to knots */ x3 = xv; y3 = yv; x2 = xv; /* check that new curve passes near (xb, yb) */ fxn = bezier(x0, x1, x2, x3, 1/ft); fyn = bezier(y0, y1, y2, y3, 1/ft); fdx = fxn - fx3; fdy = fyn - fy3; /* old values */ if (showflag != 0) printf("- offset new (%lg, %lg) ", fdx, fdy); if (ABS(fdx) > acrosslim || ABS(fdy) > alonglim) { if (showflag != 0) { printf("FAILED\n"); /* (void) getch(); */ } return 0; /* not close enough */ } /* insert new knots, ready for shift down */ /* worry about code at k - 3 ? */ knots[k+3].x = x3; knots[k+3].y = y3; knots[k+2].x = x2; knots[k+2].y = y2; knots[k+1].x = x1; knots[k+1].y = y1; knots[k].x = x0; knots[k].y = y0; /* x0 = knots[k-3].x; */ /* y0 = knots[k-3].y; */ assert(knots[k-3].x == knots[k].x); assert(knots[k-3].y == knots[k].y); if (showflag != 0) printf("\nNEW: (%d %d) (%d %d) (%d %d) (%d %d) ", x0, y0, x1, y1, x2, y2, x3, y3); if (showflag != 0) { printf("SUCCESS\n"); /* (void) getch(); */ } merges++; /* count it */ hits[chr]++; return -1; } /* new short curveto is from k-3 to k, old longer curveto from k to k+3 */ /* the horizontal tangent occurs at k-3 and must be preserved */ /* try to patch together - horizontal tangent */ int trypatchyup(int k) { int xv, yv, x0, y0, x1, y1, x2, y2, x3, y3; double rbv, rbt, rbn, ft, fxv, fyv, fxn, fyn, fdx, fdy; double fx0, fx1, fx2, fx3, fy0, fy1, fy2, fy3; double ax, bx, cx, dx, ay, by, cy, dy; if (mergeflag == 0) return 0; /* if (k + 3 > end - 2) return 0; */ xv = knots[k-3].x; yv = knots[k-3].y; /* place of tangency */ if (showflag != 0) printf("\nYUP: tangent at (%d, %d) ", xv, yv); fxv = (double) xv; fyv = (double) yv; assert (knots[k-3].y == knots[k-2].y); x3 = knots[k].x; y3 = knots[k].y; x2 = knots[k+1].x; y2 = knots[k+1].y; fx3 = (double) x3; fy3 = (double) y3; fx2 = (double) x2; fy2 = (double) y2; /* check on angle of turn in short segment */ if (showflag != 0) printf("- turning (%lg %lg)", fx3 - fx2, fy3 - fy2); if (nearhorizontal(fx3 - fx2, fy3 - fy2) == 0) { if (showflag != 0) { printf(" FAILED\n"); /* (void) getch(); */ } return 0; } rbv = distance(fxv, fyv, fx3, fy3); rbt = distance(fx3, fy3, fx2, fy2); assert (rbt != 0.0); /* assert (rbv != 0.0); */ /* if (rbv == 0.0) combine ? */ ft = 1.0 + rbv/(3.0 * rbt); /* estimated t at xv, yv */ if (showflag != 0) printf("- estimated t = %lg ", ft); assert (ft >= 1.0); if (ft > ftlim) { if (showflag != 0) { printf("FAILED\n"); /* (void) getch(); */ } return 0; /* don't expect to win */ } /* compute coefficients of the two cubics */ x0 = knots[k+3].x; x1 = knots[k+2].x; fx0 = (double) x0; fx1 = (double) x1; dx = fx0; cx = (fx1 - fx0) * 3.0; bx = ((fx2 - fx1) - (fx1 - fx0)) * 3.0; ax = (fx3 - fx0) + 3.0 * (fx1 - fx2); fxn = POLY(ax, bx, cx, fx0, ft); assert (fxn == bezier(x0, x1, x2, x3, ft)); y0 = knots[k+3].y; y1 = knots[k+2].y; fy0 = (double) y0; fy1 = (double) y1; dy = fy0; cy = (fy1 - fy0) * 3.0; by = ((fy2 - fy1) - (fy1 - fy0)) * 3.0; ay = (fy3 - fy0) + 3.0 * (fy1 - fy2); fyn = POLY(ay, by, cy, fy0, ft); assert (fyn == bezier(y0, y1, y2, y3, ft)); if (showflag != 0) printf("\nOLD: (%d %d) (%d %d) (%d %d) (%d %d)\n", x0, y0, x1, y1, x2, y2, x3, y3); fdx = fxv - fxn; fdy = fyv - fyn; if (showflag != 0) printf("offset old (%lg, %lg) ", fdx, fdy); /* not clear the following is worth it */ /* or maybe always do this ? */ if (ABS(fdy) > acrosslim || ABS(fdx) > alonglim) { rbn = distance(fx3, fy3, fxn, fyn); ft = 1.0 + (ft - 1.0) * rbv / rbn; fxn = POLY(ax, bx, cx, fx0, ft); fyn = POLY(ay, by, cy, fy0, ft); fdx = fxv - fxn; fdy = fyv - fyn; if (showflag != 0) printf("Adjusting t = %lg\n", ft); if (showflag != 0) printf("offset old (%lg, %lg) ", fdx, fdy); } if (ABS(fdy) > acrosslim || ABS(fdx) > alonglim) { if (showflag != 0) { printf("FAILED\n"); /* (void) getch(); */ } return 0; /* not close enough */ } /* compute coefficients of extended cubics */ ax = ax * ft * ft * ft; bx = bx * ft * ft; cx = cx * ft; ay = ay * ft * ft * ft; by = by * ft * ft; cy = cy * ft; /* compute new knots */ /* x0 unchanged */ x1 = round(dx + cx / 3.0); x2 = round(dx + (2.0 * cx + bx) / 3.0); x3 = round(dx + cx + bx + ax); /* y0 unchanged */ y1 = round(dy + cy / 3.0); y2 = round(dy + (2.0 * cy + by) / 3.0); y3 = round(dy + cy + by + ay); /* snap to knots */ x3 = xv; y3 = yv; y2 = yv; /* check that new curve passes near (xb, yb) */ fxn = bezier(x0, x1, x2, x3, 1/ft); fyn = bezier(y0, y1, y2, y3, 1/ft); fdx = fxn - fx3; fdy = fyn - fy3; /* old values */ if (showflag != 0) printf("- offset new (%lg, %lg) ", fdx, fdy); if (ABS(fdy) > acrosslim || ABS(fdx) > alonglim) { if (showflag != 0) { printf("FAILED\n"); /* (void) getch(); */ } return 0; /* not close enough */ } /* insert new knots, ready for shift down */ /* worry about code at k - 3 ? */ knots[k].x = x3; knots[k].y = y3; knots[k+1].x = x2; knots[k+1].y = y2; knots[k+2].x = x1; knots[k+2].y = y1; assert(knots[k+3].x == x0); assert(knots[k+3].y == y0); assert (knots[k].x == knots[k-3].x); assert (knots[k].y == knots[k-3].y); if (showflag != 0) printf("\nNEW: (%d %d) (%d %d) (%d %d) (%d %d) ", x0, y0, x1, y1, x2, y2, x3, y3); if (showflag != 0) { printf("SUCCESS\n"); /* (void) getch(); */ } merges++; /* count it */ hits[chr]++; return -1; } /* try to patch together - horizontal tangent */ int trypatchydown(int k) { int xv, yv, x0, y0, x1, y1, x2, y2, x3, y3; double rbv, rbt, rbn, ft, fxv, fyv, fxn, fyn, fdx, fdy; double fx0, fx1, fx2, fx3, fy0, fy1, fy2, fy3; double ax, bx, cx, dx, ay, by, cy, dy; if (mergeflag == 0) return 0; /* if (k - 3 < start) return 0; */ xv = knots[k+3].x; yv = knots[k+3].y; /* place of tangency */ if (showflag != 0) printf("\nYDOWN: tangent at (%d, %d) ", xv, yv); fxv = (double) xv; fyv = (double) yv; assert (knots[k+3].y == knots[k+2].y); x3 = knots[k].x; y3 = knots[k].y; x2 = knots[k-1].x; y2 = knots[k-1].y; fx3 = (double) x3; fy3 = (double) y3; fx2 = (double) x2; fy2 = (double) y2; /* check on angle of turn in short segment */ if (showflag != 0) printf("- turning (%lg %lg)", fx3 - fx2, fy3 - fy2); if (nearhorizontal(fx3 - fx2, fy3 - fy2) == 0) { if (showflag != 0) { printf(" FAILED\n"); /* (void) getch(); */ } return 0; } rbv = distance(fxv, fyv, fx3, fy3); rbt = distance(fx3, fy3, fx2, fy2); assert (rbt != 0.0); /* assert (rbv != 0.0); */ /* if (rbv == 0.0) combine ? */ ft = 1.0 + rbv/(3.0 * rbt); /* estimated t at xv, yv */ if (showflag != 0) printf("- estimated t = %lg ", ft); assert (ft >= 1.0); if (ft > ftlim) { if (showflag != 0) { printf("FAILED\n"); /* (void) getch(); */ } return 0; /* don't expect to win */ } /* compute coefficients of the two cubics */ x0 = knots[k-3].x; x1 = knots[k-2].x; fx0 = (double) x0; fx1 = (double) x1; dx = fx0; cx = (fx1 - fx0) * 3.0; bx = ((fx2 - fx1) - (fx1 - fx0)) * 3.0; ax = (fx3 - fx0) + 3.0 * (fx1 - fx2); fxn = POLY(ax, bx, cx, fx0, ft); assert (fxn == bezier(x0, x1, x2, x3, ft)); y0 = knots[k-3].y; y1 = knots[k-2].y; fy0 = (double) y0; fy1 = (double) y1; dy = fy0; cy = (fy1 - fy0) * 3.0; by = ((fy2 - fy1) - (fy1 - fy0)) * 3.0; ay = (fy3 - fy0) + 3.0 * (fy1 - fy2); fyn = POLY(ay, by, cy, fy0, ft); assert (fyn == bezier(y0, y1, y2, y3, ft)); if (showflag != 0) printf("\nOLD: (%d %d) (%d %d) (%d %d) (%d %d)\n", x0, y0, x1, y1, x2, y2, x3, y3); fdx = fxv - fxn; fdy = fyv - fyn; if (showflag != 0) printf("offset old (%lg, %lg) ", fdx, fdy); /* not clear the following is worth it */ /* or maybe always do this ? */ if (ABS(fdy) > acrosslim || ABS(fdx) > alonglim) { rbn = distance(fx3, fy3, fxn, fyn); ft = 1.0 + (ft - 1.0) * rbv / rbn; fxn = POLY(ax, bx, cx, fx0, ft); fyn = POLY(ay, by, cy, fy0, ft); fdx = fxv - fxn; fdy = fyv - fyn; if (showflag != 0) printf("Adjusting t = %lg\n", ft); if (showflag != 0) printf("offset old (%lg, %lg) ", fdx, fdy); } if (ABS(fdy) > acrosslim || ABS(fdx) > alonglim) { if (showflag != 0) { printf("FAILED\n"); /* (void) getch(); */ } return 0; /* not close enough */ } /* compute coefficients of extended cubics */ ax = ax * ft * ft * ft; bx = bx * ft * ft; cx = cx * ft; ay = ay * ft * ft * ft; by = by * ft * ft; cy = cy * ft; /* compute new knots */ /* x0 unchanged */ x1 = round(dx + cx / 3.0); x2 = round(dx + (2.0 * cx + bx) / 3.0); x3 = round(dx + cx + bx + ax); /* y0 unchanged */ y1 = round(dy + cy / 3.0); y2 = round(dy + (2.0 * cy + by) / 3.0); y3 = round(dy + cy + by + ay); /* snap to knots */ x3 = xv; y3 = yv; y2 = yv; /* check that new curve passes near (xb, yb) */ fxn = bezier(x0, x1, x2, x3, 1/ft); fyn = bezier(y0, y1, y2, y3, 1/ft); fdx = fxn - fx3; fdy = fyn - fy3; /* old values */ if (showflag != 0) printf("- offset new (%lg, %lg) ", fdx, fdy); if (ABS(fdy) > acrosslim || ABS(fdx) > alonglim) { if (showflag != 0) { printf("FAILED\n"); /* (void) getch(); */ } return 0; /* not close enough */ } /* insert new knots, ready for shift down */ /* worry about code at k - 3 ? */ knots[k+3].x = x3; knots[k+3].y = y3; knots[k+2].x = x2; knots[k+2].y = y2; knots[k+1].x = x1; knots[k+1].y = y1; knots[k].x = x0; knots[k].y = y0; /* x0 = knots[k-3].x; */ /* y0 = knots[k-3].y; */ assert(knots[k-3].x == knots[k].x); assert(knots[k-3].y == knots[k].y); if (showflag != 0) printf("\nNEW: (%d %d) (%d %d) (%d %d) (%d %d) ", x0, y0, x1, y1, x2, y2, x3, y3); if (showflag != 0) { printf("SUCCESS\n"); /* (void) getch(); */ } merges++; /* count it */ hits[chr]++; return -1; } /* if you abort splits of short curveto's, can screw up winding number */ /* int shortie=20; */ /* don't try and split if this short */ /* don't split if very shallow extremum ? */ /* don't split if very short ? */ int breaktangents(int start, int end) { /* break curveto's at tangents */ int k; int dx, dy; /* not accessed */ double t; for (k = start + 3; k < end - 1; k++) { if (knots[k].code == 'C') { dx = knots[k].x - knots[k-3].x; dy = knots[k].y - knots[k-3].y; /* if (ABS(dx) < shortie && ABS(dy) < shortie) continue; */ t = extrema(knots[k-3].x, knots[k-2].x, knots[k-1].x, knots[k].x); if (t > 0.0 && t < 1.0) { end = shiftup(k, end, 3); splitextrema(k, t); /* k = k + 3; */ if (showflag != 0) printf("\nNEXT-X: t = %lg, start = %d, k = %d, end = %d ", t, start, k, end); if (showflag != 0) printf("\nc(%d) = %c, c(%d) = %c, c(%d) = %c, c(%d) = %c ", k-3, knots[k-3].code, k, knots[k].code, k+3, knots[k+3].code, k+6, knots[k+6].code); if (t > 0.5 && k + 6 < end - 1 && knots[k + 6].code == 'C') { if(trypatchxup(k + 3) != 0) end = shiftdown(k + 3 + 1, end, 3); } if (t < 0.5 && k - 6 >= start && knots[k - 3].code == 'C') { if(trypatchxdown(k - 3) != 0) end = shiftdown(k - 3 + 1, end, 3); } } t = extrema(knots[k-3].y, knots[k-2].y, knots[k-1].y, knots[k].y); if (t > 0.0 && t < 1.0) { end = shiftup(k, end, 3); splitextrema(k, t); /* k = k + 3; */ if (showflag != 0) printf("\nNEXT-Y: t = %lg, start = %d, k = %d, end = %d ", t, start, k, end); if (showflag != 0) printf("\nc(%d) = %c, c(%d) = %c, c(%d) = %c, c(%d) = %c ", k-3, knots[k-3].code, k, knots[k].code, k+3, knots[k+3].code, k+6, knots[k+6].code); if (t > 0.5 && k + 6 < end - 1 && knots[k + 6].code == 'C') { if(trypatchyup(k + 3) != 0) end = shiftdown(k + 3 + 1, end, 3); } if (t < 0.5 && k - 6 >= start && knots[k - 3].code == 'C') { if(trypatchydown(k - 3) != 0) end = shiftdown(k - 3 + 1, end, 3); } } } } return end; } int trymerges(int start, int end) { /* try combine certain short curveto's */ int k; for (k = start + 3; k < end - 3; k++) { /* look for sequence curveto's */ if (knots[k].code == 'C' && k + 3 < end - 1 && knots[k+3].code == 'C') { if (knots[k-3].x == knots[k-2].x) { if (showflag != 0) printf("\nNEXT-X: start = %d, k = %d, end = %d ", start, k, end); if(trypatchxup(k) != 0) end = shiftdown(k + 1, end, 3); } else if (knots[k+3].x == knots[k+2].x) { if (showflag != 0) printf("\nNEXT-X: start = %d, k = %d, end = %d ", start, k, end); if(trypatchxdown(k) != 0) end = shiftdown(k + 1, end, 3); } else if (knots[k-3].y == knots[k-2].y) { if (showflag != 0) printf("\nNEXT-Y: start = %d, k = %d, end = %d ", start, k, end); if(trypatchyup(k) != 0) end = shiftdown(k + 1, end, 3); } else if (knots[k+3].y == knots[k+2].y) { if (showflag != 0) printf("\nNEXT-Y: start = %d, k = %d, end = %d ", start, k, end); if(trypatchydown(k) != 0) end = shiftdown(k + 1, end, 3); } } } return end; } /* try and merge curvetos - still experimental - OLD version */ /* void mergecurves(int start, int end) { int k; int dy, dx, ds; if (end - start < 6) return; for (k = start + 6; k < end; k++) { if (knots[k].code == 'C' && knots[k-3].code == 'C') { if(smallangle(knots[k-4].x, knots[k-4].y, knots[k-3].x, knots[k-3].y, knots[k-3].x, knots[k-3].y, knots[k-2].x, knots[k-2].y) != 0) { if (knots[k-5].x == knots[k-6].x) { dy = knots[k-3].y - knots[k-6].y; dx = knots[k-3].x - knots[k-6].x; ds = knots[k-2].x - knots[k-6].x; if (ABS(dy) + ABS(dx)/2 < mlength && ABS(ds) < msplit) { knots[k-5].y = knots[k-2].y; end = shiftdown(k-1, end, 3); } } if (knots[k].x == knots[k-1].x) { dy = knots[k].y - knots[k-3].y; dx = knots[k].x - knots[k-3].x; ds = knots[k-4].x - knots[k].x; if (ABS(dy) + ABS(dx)/2 < mlength && ABS(ds) < msplit) { knots[k-4].x = knots[k].x; end = shiftdown(k, end, 3); } } if (knots[k-5].y == knots[k-6].y) { dx = knots[k-3].x - knots[k-6].x; dy = knots[k-3].y - knots[k-6].y; ds = knots[k-2].y - knots[k-6].y; if (ABS(dx) + ABS(dy)/2 < mlength && ABS(ds) < msplit) { knots[k-5].x = knots[k-2].x; end = shiftdown(k-1, end, 3); } } if (knots[k].y == knots[k-1].y) { dx = knots[k].x - knots[k-3].x; dy = knots[k].y - knots[k-3].y; ds = knots[k-4].y - knots[k].y; if (ABS(dx) + ABS(dy)/2 < mlength && ABS(ds) < msplit) { knots[k-4].y = knots[k].y; end = shiftdown(k, end, 3); } } } } } } */ int xcross, ycross; /* place to store cross-over for further process */ /* This is not fool proof - we are forced to use split at extrema first */ /* This could be done more cleanly without using double */ /* Solution of the problem might be more elegant than just the intersection */ int crossover(int xa, int ya, int xb, int yb, int xc, int yc, int xd, int yd) { long alphanumer, alphadenom, betanumer, betadenom; double alpha, beta; alphadenom = (long) (xb - xa) * (long) (yd - yc) - (long) (yb - ya) * (long) (xd - xc); if (alphadenom == 0L) return 0; alphanumer = (long) (xc - xa) * (long) (yd - yc) - (long) (yc - ya) * (long) (xd - xc); betadenom = (long) (xd - xc) * (long) (yb - ya) - (long) (yd - yc) * (long) (xb - xa); if (betadenom == 0L) return 0; betanumer = (long) (yc - ya) * (long) (xb - xa) - (long) (xc - xa) * (long) (yb - ya); assert (alphadenom != 0L); assert (betadenom != 0L); alpha = (double) alphanumer / (double) alphadenom; if (alpha <= 0.0 || alpha >= 1.0) return 0; beta = (double) betanumer / (double) betadenom; if (beta <= 0.0 || beta >= 1.0) return 0; xcross = xa + (int) (alpha * (double) (xb - xa)); ycross = ya + (int) (alpha * (double) (yb - ya)); return -1; } int checkcrossovers(int start, int end) { int k; int xa, ya, xb, yb, xc, yc, xd, yd; k = start; xa = knots[k].x, ya = knots[k].y; k++; xb = knots[k].x, yb = knots[k].y; k++; xc = knots[k].x, yc = knots[k].y; k++; while (k < end) { xd = knots[k].x, yd = knots[k].y; if (crossover(xa, ya, xb, yb, xc, yc, xd, yd) != 0) { /* printf("Char %d crossover (%d %d) (%d %d) (%d %d) (%d %d)\n", chr, xa, ya, xb, yb, xc, yc, xd, yd); */ printf("%d", chr); if (crossoverflag != 0) { if (knots[k].code == 'C') { /* split curveto into lineto's */ if (showcrosses != 0) { printf("Char %d crossover (%d %d) (%d %d) (%d %d) (%d %d) ", chr, xa, ya, xb, yb, xc, yc, xd, yd); printf("(%c %c %c %c)\n", knots[k-3].code, knots[k-2].code, knots[k-1].code, knots[k].code); /* (void) getch(); */ } assert(knots[k-1].code == ' '); assert(knots[k-2].code == ' '); knots[k-2].x = xcross; knots[k-2].y = ycross; knots[k-1].x = xcross; knots[k-1].y = ycross; end = shiftdown(k - 1, end, 1); k--; knots[k].code = 'L'; knots[k-1].code = 'L'; } else { /* not a cross-over inside a curveto */ printf("@"); /* do nothing here - does not affect winding number ? */ if (showcrosses != 0) { printf("Char %d crossover (%d %d) (%d %d) (%d %d) (%d %d) ", chr, xa, ya, xb, yb, xc, yc, xd, yd); printf("(%c %c %c %c)\n", knots[k-3].code, knots[k-2].code, knots[k-1].code, knots[k].code); /* (void) getch(); */ } } } /* (void) getch(); */ } k++; if (xc != xd || yc != yd) xa = xb; xb = xc; xc = xd; ya = yb; yb = yc; yc = yd; } return end; } /* void checkcrossover(void) { int k, start, end; for (k = 0; k < nsubpath; k++) { start = subpaths[k].start; end = subpaths[k].end; checkcrosssub(start, end); } } */ /* ----------------------------------------------------------------*/ void giveup(int code) { /* graceful exit with meaningful error message */ fprintf(stderr, " while %s", task); if (chr >= 0) fprintf(stderr, " for character %d ", chr); else fprintf(stderr, " "); /* fclose(fp_out); remove(fn_out); */ if (fgets(line, sizeof(line), fp_in) != NULL) { fprintf(stderr, "\nNext line:\n"); fputs(line, stderr); } fflush(stderr); exit(code); } void addknot(int x, int y, int c) { /* add a knot to subpath */ knots[nknot].x = x; knots[nknot].y = y; knots[nknot].code = (char) c; nknot++; assert (nknot < MAXKNOTS); } int fround(double z) { if (z > 0.0) return (int) (z+0.5); else if (z < 0.0) return - (int) (-z+0.5); else return 0; } void enterknot(int c) { /* make knot from next two numbers */ double fx, fy; int x, y; /* if (sscanf (line, " %d %d", &x, &y) < 2) { */ if (sscanf (line, " %lg %lg", &fx, &fy) < 2) { fprintf(stderr, "Expecting two numbers for lineto or moveto, not:\n"); fprintf(stderr, "%s", line); giveup(1); } x = fround(fx); y = fround(fy); addknot(x, y, c); } void enterknots(int c) { /* make knot from next six numbers */ double fxa, fya, fxb, fyb, fxc, fyc; int xa, ya, xb, yb, xc, yc; /* if( sscanf (line, " %d %d %d %d %d %d", &xa, &ya, &xb, &yb, &xc, &yc) */ if( sscanf (line, " %lg %lg %lg %lg %lg %lg", &fxa, &fya, &fxb, &fyb, &fxc, &fyc) < 6) { fprintf(stderr, "Expecting six numbers for curveto, not:\n"); fprintf(stderr, "%s", line); giveup(1); } xa = fround(fxa); ya = fround(fya); xb = fround(fxb); yb = fround(fyb); xc = fround(fxc); yc = fround(fyc); addknot(xa, ya, ' '); addknot(xb, yb, ' '); addknot(xc, yc, c); } void closeloop(void) { /* finish off subpath */ int xstart, ystart, inx; inx = subpaths[nsubpath].start; xstart = knots[inx].x; ystart = knots[inx].y; addknot(xstart, ystart, 'H'); } int checkglitch(int start, int end) { int i, xa, ya, xb, yb, xc, yc, xd, yd; if (start + 3 >= end) return 0; xa = knots[start].x; ya = knots[start].y; xb = knots[start+1].x; yb = knots[start+1].y; xc = knots[start+2].x; yc = knots[start+2].y; for (i = start + 3; i < end; i++) { xd = knots[i].x; yd = knots[i].y; if (ya == yd && yb == yc) { if (xa == xd && xb == xc) { fprintf(stdout, "Glitch %d (%d %d) (%d %d) (%d %d) (%d %d)\n", chr, xa, ya, xb, yb, xc, yc, xd, yd); /* (void) getch(); */ } } if (ya == yb && yb == yc) { if ((xb > xa && xb > xc) || (xb < xa && xb < xc)) { fprintf(stdout, "Reversal %d (%d %d) (%d %d) (%d %d)\n", chr, xa, ya, xb, yb, xc, yc); /* (void) getch(); */ } } if (xa == xb && xb == xc) { if ((yb > ya && yb > yc) || (yb < ya && yb < yc)) { fprintf(stdout, "Reversal %d (%d %d) (%d %d) (%d %d)\n", chr, xa, ya, xb, yb, xc, yc); /* (void) getch(); */ } } xa = xb; ya = yb; xb = xc; yb = yc; xc = xd; yc = yd; } return 0; } void statistics(void) { int k, count=0, total=0; if (splits > 0) printf("Split %d curveto's ", splits); if (merges > 0) printf("Merged %d curveto's ", merges); if (straightens > 0) printf("Linearized %d flat curvetos ", straightens); if (flattens > 0) printf("Linearized %d almost flat curvetos ", flattens); if (colinears > 0) printf("Combined %d colinear lineto's", colinears); if (combines > 0) printf("Combined %d lineto's", combines); if (splits > 0 || merges > 0 || straightens > 0 || flattens > 0 || colinears > 0 || combines > 0) putc('\n', stdout); for (k = 0; k < MAXCHRS; k++) if (hits[k] != 0) count++; for (k = 0; k < MAXCHRS; k++) if (seen[k] != 0) total++; printf("%d characters (out of %d) modified\n", count, total); if (count > 0) { /* 1996/June/15 */ /* if (verboseflag) { */ /* putc('\n', stdout); */ printf("CHARS: "); for (k = 0; k < MAXCHRS; k++) if (hits[k] != 0) printf("%d ", k); /* putc('\n', stdout); */ /* } */ } } int eofcount = 0; /* safety escape */ int getline(FILE *input, char *line) { /* read a line up to newline */ if (fgets(line, MAXLINE, input) == NULL) { if (newstyle == 0) fprintf(errout, "Unexpected EOF "); if (eofcount++ > 32) { statistics(); giveup(4); } return EOF; /* giveup(4); */ } return 0; } long flast; /* pointer into file at start of character */ int scaninchar(FILE *fp_in, int flag) { /* read outline for one character */ int start, end, err; int chr, width; nknot = 0; nsubpath = 0; task = "scanning in outline"; if (traceflag) printf ("TASK: %s\n", task); /* if (unwindflag != 0 && flag != 0) flast = ftell(fp_in); */ /* remember start of char */ if (newstyle != 0) { /* skip first line --- chr width */ /* if (strstr(line, " % ") != NULL) */ err = getline(fp_in, line); while (*line == '%' || *line == '\n') /* 1994/Feb/4 */ err = getline(fp_in, line); if (sscanf (line, "%d %d", &chr, &width) < 2) { /* not really an error condition ... */ /* if (traceflag) { fprintf(stderr, "Expecting char code and width, not:\n"); fprintf(stderr, "%s", line); } */ /* giveup(1); */ } } else { if (strstr(line, "} def") == NULL) err = getline(fp_in, line); } err = 0; /* while (strstr(line, "} def") == NULL && strstr(line, "]") == NULL && err != EOF) { */ for (;;) { /* if (strstr(line, "} def") != NULL || strstr(line, "]") != NULL || err == EOF) break; */ if (*line == '%' || *line == '\n') { /* ignore comment lines */ err = getline(fp_in, line); if (err == EOF) break; continue; } if (newstyle != 0 && strstr(line, " s %") != NULL) { err = getline(fp_in, line); /* skip over hint replacement calls */ if (err == EOF) break; continue; } if (strstr(line, "} def") != NULL || strstr(line, "]") != NULL || err == EOF) break; start = nknot; subpaths[nsubpath].start = nknot; /* while (strstr(line, "closepath") == NULL) { */ /* 1992/April/8 */ /* while (strstr(line, "closepath") == NULL && strstr(line, "h") == NULL && err != EOF) { */ for (;;) { /* if (strstr(line, "closepath") != NULL || strstr(line, "h") != NULL || err == EOF) break; */ if (*line == '%' || *line == '\n') { /* ignore comment lines */ err = getline(fp_in, line); if (err == EOF) break; continue; } if (newstyle != 0 && strstr(line, " s %") != NULL) { err = getline(fp_in, line); /* skip hint replacement calls */ continue; } if (strstr(line, "closepath") != NULL || strstr(line, "h") != NULL || err == EOF) break; if (strstr(line, "moveto") != NULL) enterknot('M'); else if (strstr(line, "m") != NULL) enterknot('M'); /* ??? */ else if (strstr(line, "lineto") != NULL) enterknot('L'); else if (strstr(line, "l") != NULL) enterknot('L'); /* ??? */ else if (strstr(line, "curveto") != NULL) enterknots('C'); else if (strstr(line, "c") != NULL) enterknots('C'); /* ??? */ else { fprintf(stderr, "Can't understand: %s", line); giveup(7); } err = getline(fp_in, line); } if (strstr(line, "reverse") != NULL) { subpaths[nsubpath].reverse = 1; if (flag != 0) printf("Flipping reversal of subpath %d in char %d\n", nsubpath, chr); } else subpaths[nsubpath].reverse = 0; if (traceflag != 0) printf("End of subpath \n"); if (knots[nknot-1].x != knots[start].x || knots[nknot-1].y != knots[start].y) { /* fprintf(stdout, "open in %d ", chr); */ if (complainopen != 0) printf("open "); addknot(knots[start].x, knots[start].y, 'L'); } closeloop(); end = nknot; if (knots[start].code != 'M') { showsubpathsub(start, end); fflush(stdout); (void) _getch(); } fflush(stdout); if (debugflag != 0) { assert(knots[start].code == 'M'); assert(knots[end-1].code == 'H'); assert(knots[end-1].x == knots[start].x); assert(knots[end-1].y == knots[start].y); assert(knots[end-2].code == 'L' || knots[end-2].code == 'C'); if (knots[end-2].x != knots[start].x || knots[end-2].y != knots[start].y) { fprintf(stdout, "Path not closed %d (%d %d) (%d %d)\n", chr, knots[end-2].x, knots[end-2].y, knots[start].x, knots[start].y); } /* assert(knots[end-2].x == knots[start].x); */ /* assert(knots[end-2].y == knots[start].y); */ } if (flag != 0) { (void) checkglitch(start, end); /* ? */ if (splitextremaflag != 0) end = breaktangents(start, end); if (crossoverflag != 0) end = checkcrossovers(start, end); if (moremerges != 0) { (void) rearrange(start, end); end = trymerges(start, end); (void) rearrange(start, end); } if (straightenflag != 0) end = straightencurves(start, end); if (flattenflag != 0) end = flattencurves(start, end); if (colinearflag != 0) end = mergelines(start, end); if (combineflag != 0) end = combinelines(start, end); } subpaths[nsubpath].end = end; nknot = end; nsubpath++; assert (nsubpath < MAXSUBPATHS); err = getline(fp_in, line); } return err; } /* --- following is stuff for computing winding numbers --- */ long dotproduct(int xab, int yab, int xbc, int ybc) { long dot; dot = (long) xab * (long) xbc + (long) yab * (long) ybc; return dot; /* return (double) xab * (double) xbc + (double) yab * (double) ybc; */ } long crossproduct(int xab, int yab, int xbc, int ybc) { long cross; cross = (long) xab * (long) ybc - (long) yab * (long) xbc; return cross; /* return xab * (double) ybc - (double) yab * (double) xbc; */ } double angle(int xab, int yab, int xbc, int ybc) { long lsint, lcost; /* double sint, cost; */ if ((xab == 0 && yab == 0) || (xbc == 0 && ybc == 0)) { fprintf(stderr, "Zero length vector"); giveup(5); } lsint = crossproduct(xab, yab, xbc, ybc); lcost = dotproduct(xab, yab, xbc, ybc); if (lsint == 0 && lcost < 0) { fprintf(stdout, "\nAntiparallel in %d: ", chr); return 10.0; /* an impossible value */ } assert(lsint != 0 || lcost != 0); return atan2((double) lsint, (double) lcost); } void setbbox(int k) { int x, y, code; int start, end, i; int xll, yll, xur, yur; start = subpaths[k].start; end = subpaths[k].end; x = knots[start].x; y = knots[start].y; xll = x; yll = y; xur = x, yur = y; for (i = start + 1; i < end; i++) { x = knots[i].x; y = knots[i].y; code = knots[i].code; if (code != ' ') { /* ignore guide knots */ if (x < xll) xll = x; if (y < yll) yll = y; if (x > xur) xur = x; if (y > yur) yur = y; } else if (knots[i-1].code == ' ') { /* try average ? */ x = (knots[i].x + knots[i-1].x)/2; y = (knots[i].y + knots[i-1].y)/2; if (x < xll) xll = x; if (y < yll) yll = y; if (x > xur) xur = x; if (y > yur) yur = y; } } subpaths[k].xll = xll; subpaths[k].yll = yll; subpaths[k].xur = xur; subpaths[k].yur = yur; } double getturn(int k) { int start, end, i; int xa, ya, xb, yb, xc, yc; int xab, yab, xbc, ybc; double turn=0.0, dturn, theta; start = subpaths[k].start; end = subpaths[k].end; if(end < start + 3) { fprintf(stderr, "Subpath too short (%d) in char %d\n", end - start, chr); return 0.0; } xb = knots[start].x; yb = knots[start].y; i = start; for (;;) { xc = knots[i].x; yc = knots[i].y; if (xb != xc || yb != yc) { xbc = xc - xb; ybc = yc - yb; xa = xb; ya = yb; xb = xc; yb = yc; xab = xbc; yab = ybc; break; } i++; } assert(xab != 0 || yab != 0); /* xab yab initialized ? */ while (i < end) { xc = knots[i].x; yc = knots[i].y; if (xb != xc || yb != yc) { /* ignore coincident knots */ xbc = xc - xb; ybc = yc - yb; /* printf("%lg ", turn); */ theta = angle(xab, yab, xbc, ybc); if (theta == 10.0) { /* error code */ fprintf(stdout, "(%d %d) (%d %d) (%d %d) ", xa, ya, xb, yb, xc, yc); /* putc(7, stdout); */ /* (void) getch(); */ } else turn = turn + theta; xa = xb; ya = yb; xb = xc; yb = yc; xab = xbc; yab = ybc; } i++; } i = start + 1; for(;;) { xc = knots[i].x; yc = knots[i].y; if (xb != xc || yb != yc) { /* ignore coincident knots */ xbc = xc - xb; ybc = yc - yb; theta = angle(xab, yab, xbc, ybc); if (theta == 10.0) /* error code */ fprintf(stdout, "(%d %d) (%d %d) (%d %d)", xa, ya, xb, yb, xc, yc); else turn = turn + theta; break; } i++; } /* check whether total angle turned through is + or - two pi */ dturn = turn - 2.0 * pi; if (dturn < -0.01 || dturn > 0.01) { dturn = turn + 2.0 * pi; if (dturn < -0.01 || dturn > 0.01) { printf("Char %d turn is %lg for subpath %d \n", chr, turn, k); /* (void) getch(); */ } } return turn; } double getarea(int k) { int start, end, i; int xa, ya, xb, yb; double area=0.0; long darea; start = subpaths[k].start; end = subpaths[k].end; xa = knots[start].x; ya = knots[start].y; for (i = start + 1; i < end; i++) { xb = knots[i].x; yb = knots[i].y; darea = (long) xa * (long) yb - (long) ya * (long) xb; area = area + (double) darea; xa = xb; ya = yb; } return area; } int inside(int i, int j) { int xlli, ylli, xuri, yuri; int xllj, yllj, xurj, yurj; xlli = subpaths[i].xll; ylli = subpaths[i].yll; xuri = subpaths[i].xur; yuri = subpaths[i].yur; xllj = subpaths[j].xll; yllj = subpaths[j].yll; xurj = subpaths[j].xur; yurj = subpaths[j].yur; /* printf(" (%d %d %d %d) (%d %d %d %d) ", xlli, ylli, xuri, yuri, xllj, yllj, xurj, yurj); */ if (xlli > xllj && ylli > yllj && xuri < xurj && yuri < yurj) return -1; else return 0; } int getoutside(int k) { /* see if bbox is not inside any others */ int i, flag = -1; for (i = 0; i < nsubpath; i++) { if (i == k) continue; if (inside(k, i) != 0) { flag = 0; break; } } return flag; } void analchar(void) { /* subpaths and knots of character */ int k; int outside; /* use before define ? */ double turn; double area; /* use before define ? */ for (k=0; k < nsubpath; k++) { setbbox(k); } turn = 0; for (k=0; k < nsubpath; k++) { turn = getturn(k); subpaths[k].turn = turn; area = getarea(k); subpaths[k].area = area; if (verboseflag != 0) printf(" %lg ", turn); /* if (turn > 0) printf("+"); else printf("-"); */ } outside = 0; for (k=0; k < nsubpath; k++) { outside = getoutside(k); subpaths[k].outside = outside; if (verboseflag != 0) printf(" %d ", outside); } if ((turn > 0 && outside != 0) || (turn < 0 && outside == 0)) putc('+', stdout); else { putc('-', stdout); if (complainflag != 0) { printf(" %d ", chr); /* putc(7, stdout); */ /* (void) getch(); */ } } } char *expandcode(int c) { if (newstyle != 0) { switch(c) { case 'L': return "l"; case 'C': return "c"; case 'M': return "m"; case 'H': return "h"; default: { fprintf(stderr, "Error: char %c not valid code", c); giveup(3); } } } else { switch(c) { case 'L': return "lineto"; case 'C': return "curveto"; case 'M': return "moveto"; case 'H': return "closepath"; default: { fprintf(stderr, "Error: char %c not valid code", c); giveup(3); } } } return ""; // keep compiler quiet } void dumpchar(FILE *fp_out) { int x, y, k; int start, end, i, code; for (k=0; k < nsubpath; k++) { start = subpaths[k].start; end = subpaths[k].end; i = start; while (i < end) { code = knots[i].code; if (code != 'H') { x = knots[i].x; y = knots[i].y; i++; /* if (closepaths == 0 && i >= end-2 && code == 'L') { */ if (closepaths == 0 && i >= end-1 && code == 'L') { /* printf("start %d i %d end %d\n", start, i, end); */ /* printf("x %d y %d c %c\n", x, y, code); */ /* printf("xo %d yo %d\n", knots[start].x, knots[start].y); */ if (x == knots[start].x && y == knots[start].y) continue; } if (newstyle == 0) fprintf(fp_out, " "); fprintf(fp_out, "%d %d ", x, y); if (code == ' ') { code = knots[i].code; if (code != ' ') showsubpath(k); assert(code == ' '); x = knots[i].x; y = knots[i].y; i++; fprintf(fp_out, "%d %d ", x, y); code = knots[i].code; assert(code == 'C'); x = knots[i].x; y = knots[i].y; i++; fprintf(fp_out, "%d %d ", x, y); } } else { i++; if (newstyle == 0) fprintf(fp_out, " "); } fprintf(fp_out, "%s\n", expandcode(code)); } } } int copytochars(FILE *fp_in, FILE *fp_out) { double size, scale; getline(fp_in, line); while (*line == '%' || *line == '\n') { fputs(line, fp_out); getline(fp_in, line); } if (sscanf (line, "%lg %lg", &size, &scale) == 2) { /* new style */ /* fprintf(fp_out, "%s", line); */ fputs(line, fp_out); getline(fp_in, line); /* read BBox */ while (*line == '%' || *line == '\n') { fputs(line, fp_out); getline(fp_in, line); } /* fprintf(fp_out, "%s", line); */ fputs(line, fp_out); return 1; } else { while (strstr(line, "/CharacterDefs") == NULL) { /* old style */ fprintf(fp_out, "%s", line); /* getline(fp_in, line); */ if (getline(fp_in, line) == EOF) { fprintf(stderr, "Bad OUT file\n"); /* 1996/Apr/8 */ exit(1); } } fprintf(fp_out, "%s", line); return 0; } } void reversesubpath(int k) { /* reverse subpath number k */ struct knot temp; int start, end, i, no; char lastcom, com; /* if (chr == 123) showsubpath(k); */ start = subpaths[k].start; end = subpaths[k].end; assert(knots[end-1].x == knots[end-2].x); assert(knots[end-1].y == knots[end-2].y); no = (end - 1 - start) / 2; for (i = 0; i < no; i++) { temp = knots[start + i]; knots[start + i] = knots[end - 2 - i]; knots[end - 2 - i] = temp; } lastcom = knots[start].code; knots[start].code = 'M'; for (i = start + 1; i < end - 1; i++) { com = knots[i].code; if (com != ' ') { knots[i].code = lastcom; lastcom = com; } } knots[end - 1].code = 'H'; /* if (chr == 123) { showsubpath(k); (void) getch(); } */ } void unwindifneeded(FILE *fp_in) { if (unwindflag != 0) { fseek(fp_in, flast, SEEK_SET); /* seek back to start of character */ getline(fp_in, line); /* reread outline for one character */ if (scaninchar(fp_in, 0) == EOF) { fprintf(errout, "EOF in unwindifneeded\n"); } /* reread subpaths, but leave flags intact */ } } void reverseifneeded(void) { int k; if (reverseflag != 0) { /* for (k = 0; k < nsubpath; k++) { if ((subpaths[k].outside == 0 && subpaths[k].turn > 0.0) || (subpaths[k].outside != 0 && subpaths[k].turn < 0.0)) { reversesubpath(k); } } */ for (k = 0; k < nsubpath; k++) { if (subpaths[k].reverse == 0) { if ((subpaths[k].outside == 0 && subpaths[k].area > 0.0) || (subpaths[k].outside != 0 && subpaths[k].area < 0.0)) { reversesubpath(k); } } else { if ((subpaths[k].outside != 0 && subpaths[k].area > 0.0) || (subpaths[k].outside == 0 && subpaths[k].area < 0.0)) { reversesubpath(k); } } } } } int doallchars(FILE *fp_in, FILE *fp_out) { int width, k, err; if (traceflag != 0) printf("Doing all characters now\n"); if (unwindflag != 0) flast = ftell(fp_in); err = getline(fp_in, line); if (err == EOF) return EOF; while (strstr(line, "end def") == NULL && err != EOF) { task = "scanning for next outline"; if (traceflag) printf ("TASK: %s\n", task); /* if (traceflag != 0) printf("%s", line); */ if (strstr(line, "/.notdef") != NULL) { fprintf(fp_out, "%s", line); if (traceflag != 0) printf("%s", line); if (unwindflag != 0) flast = ftell(fp_in); err = getline(fp_in, line); if (err == EOF) return EOF; /* break */ if (traceflag != 0) printf("%s", line); } if (traceflag != 0) printf("%s", line); if (strstr(line, "} def") != NULL) { err = getline(fp_in, line); if (err == EOF) return EOF; /* break */ } if (strstr(line, "end def") != NULL) break; /* ? */ if (newstyle != 0) { /* new style */ err = getline(fp_in, line); /* while (*line == '%' || *line == '\n') { */ /* 96/Apr/6 */ while (*line == '%' || *line == '\n' || *line == ']') { if (getline(fp_in,line) == EOF) break; } if (err == EOF) return EOF; /* break */ if (sscanf (line, "%d %d", &chr, &width) < 2) { fprintf(stderr, "Expecting char code and width, not:\n"); fprintf(stderr, "%s", line); giveup(1); } } else if (sscanf(line, " /char%d ", &chr) != 1) { /* old style */ fprintf(stderr, "Don't understand: %s\n", line); for(k=0; k < MAXCHRS; k++) { /* (void) getch(); */ err = getline(fp_in, line); if (err == EOF) return EOF; /* break */ fprintf(stderr, "%s", line); } giveup(5); /* was unreachable */ } if (verboseflag != 0) printf("\n(%d) ", chr); seen[chr]++; fprintf(fp_out, "%s", line); if (traceflag != 0) printf("First line: %s", line); err = scaninchar(fp_in, -1); if (err == EOF) { fprintf(errout, "EOF in doallchars\n"); } task = "analyzing subpaths"; if (traceflag != 0) printf("Analyzing subpaths\n"); analchar(); task = "rereading subpaths"; if (traceflag != 0) printf("Rereading subpaths\n"); unwindifneeded(fp_in); task = "reversing some subpaths"; if (traceflag != 0) printf("Reversing some subpaths\n"); reverseifneeded(); task = "dumping character"; if (traceflag != 0) printf("Dumping character\n"); dumpchar(fp_out); if (strstr(line, "/char") == NULL) fprintf(fp_out, "%s", line); /* ? */ if (traceflag != 0) printf("%s", line); if (unwindflag != 0) flast = ftell(fp_in); if (err == EOF) return EOF; /* break */ if (newstyle == 0) { err = getline(fp_in, line); /* ??? */ if (err == EOF) return EOF; /* break */ } } if (newstyle == 0) fprintf(fp_out, "%s", line); return err; } void copytoEOF(FILE *fp_in, FILE *fp_out) { int err; char *s; err = getline(fp_in, line); if (err == EOF) return; while(strstr(line, "definefont") == NULL) { if ((s = strstr(line, "eofill")) != NULL) strcpy(s, s+2); fprintf(fp_out, "%s", line); err = getline(fp_in, line); if (err == EOF) return; } /* while(fgets(line, MAXLINE, input) != NULL) */ fprintf(fp_out, "%s", line); } #ifdef IGNORE void extension(char *fname, char *ext) { /* supply extension if none */ if (strchr(fname, '.') == NULL) { strcat(fname, "."); strcat(fname, ext); } } void forceexten(char *fname, char *ext) { /* change extension, if present */ char *s; if ((s = strchr(fname, '.')) == NULL) { strcat(fname, "."); strcat(fname, ext); } else strcpy(s+1, ext); /* give it default extension */ } #endif void extension(char *fname, char *ext) { /* supply extension if none */ char *s, *t; if ((s = strrchr(fname, '.')) == NULL || ((t = strrchr(fname, '\\')) != NULL && s < t)) { strcat(fname, "."); strcat(fname, ext); } } void forceexten(char *fname, char *ext) { /* change extension if present */ char *s, *t; if ((s = strrchr(fname, '.')) == NULL || ((t = strrchr(fname, '\\')) != NULL && s < t)) { strcat(fname, "."); strcat(fname, ext); } else strcpy(s+1, ext); /* give it default extension */ } void showusage(char *s) { fprintf (errout, "\ Correct usage is:\n\n\ %s [-[v][s][m][c] [-e ] ...\n\n", s); fprintf (errout, "\ \tv: verbose mode\n\ \tt: tracing mode\n\ \tn: suppress output\n\ \tu: unwind only (split and uncross to get winding, then reread original)\n\ \tg: give char number when bad winding number seen\n\ \tq: suppress subpath reversals\n\ \tx: the works => s, m, c, r, f, l\n\ \ts: split curveto's at extrema\n\ \tm: merge short curveto's generated by splitting\n\ \tc: remove crossovers\n\ \tr: make additional merging pass\n\ \tf: flatten nearly flat curveto's\n\ \tl: combine colinear lineto's\n\ \to: complain about open subpaths\n\ \tz: write lineto closing path\n\ "); /* fprintf (errout, "\ \tp: next argument is path for output file\n"); */ /* fprintf (errout, "\ \te: next argument is extension to use on ouput file (default '%s')\n", ext); */ exit(1); } void decodeflag (int c) { /* printf ("FLAG: %c%n", c); */ switch(c) { case 's': splitextremaflag = 1; break; case 'm': mergeflag = 1; break; case 'r': moremerges = 1; break; case 'f': flattenflag = 1; break; case 'a': straightenflag = 0; break; case 'b': colinearflag = 0; break; case 'q': reverseflag = 0; break; case 'l': combineflag = 1; break; case 'g': complainflag = 1; break; case 'x': splitextremaflag = 1; mergeflag = 1; moremerges = 1; flattenflag = 1; combineflag = 1; crossoverflag = 1; break; case 'u': unwindflag = 1; splitextremaflag = 1; crossoverflag = 1; break; case 'c': crossoverflag = 1; break; case 't': traceflag = 1; showflag = 1; break; case 'v': verboseflag = 1; break; case '?': detailflag = 1; break; case 'o': complainopen = 1; break; case 'z': closepaths = 1; break; case 'n': nulloutput = 1; break; case 'p': pathflag = 1; break; case 'e': extenflag = 1; break; default: { fprintf(stderr, "Invalid command line flag '%c'", c); giveup(7); } } } int main(int argc, char *argv[]) { /* FILE *fp_in, *fp_out; */ char fn_in[FILENAME_MAX], fn_out[FILENAME_MAX]; unsigned int i; char *s; int c, k, m, err; int firstarg=1; chr = -1; if (argc < 2) showusage(argv[0]); task = "interpeting command line"; if (traceflag) printf ("TASK: %s\n", task); /* while (argv[firstarg][0] == '-') */ /* check for command flags */ while (firstarg < argc && argv[firstarg][0] == '-') { /* command flags */ /* printf("LENGTH = %d\n", strlen(argv[firstarg]) ); */ for(i = 1; i < strlen(argv[firstarg]); i++) { if ((c = argv[firstarg][i]) == '\0') break; else decodeflag(c); } firstarg++; if (extenflag != 0) { ext = argv[firstarg]; firstarg++; extenflag = 0; } if (pathflag != 0) { path = argv[firstarg]; firstarg++; pathflag = 0; } } if (detailflag) showusage(argv[0]); for (m = firstarg; m < argc; m++) { task = "opening files"; if (traceflag) printf ("TASK: %s\n", task); splits = 0; merges = 0; flattens = 0; straightens = 0; colinears = 0; combines = 0; for (k = 0; k < MAXCHRS; k++) hits[k] = 0; /* reset hit list */ for (k = 0; k < MAXCHRS; k++) seen[k] = 0; /* reset seen list */ strcpy(fn_in, argv[m]); /* extension(fn_in, "ps"); */ /* extension(fn_in, "out"); */ /* if (verboseflag != 0) */ printf("Processing font file %s\n", fn_in); if (strcmp(path, "") != 0) { strcpy(fn_out, path); strcat(fn_out, "\\"); if ((s=strrchr(fn_in, '\\')) != NULL) s++; else if ((s=strrchr(fn_in, ':')) != NULL) s++; else s = fn_in; strcat(fn_out, s); /* copy input file name minus path */ } else { #if CURRENTDIRECT != 0 if ((s=strrchr(fn_in, '\\')) != NULL) s++; else if ((s=strrchr(fn_in, ':')) != NULL) s++; else s = fn_in; strcpy(fn_out, s); /* copy input file name minus path */ #else strcpy(fn_out, fn_in); /* copy input file name */ #endif } forceexten(fn_out, ext); /* change extension */ if (verboseflag != 0) printf("Output file: %s\n", fn_out); if (nulloutput != 0) strcpy(fn_out, "NULL"); extension(fn_in, "out"); /* 1996/Apr/7 */ if ((fp_in = fopen(fn_in, "r")) == NULL) { forceexten(fn_in, "out"); if ((fp_in = fopen(fn_in, "r")) == NULL) { fprintf(stderr, "\n"); perror(fn_in); giveup(2); } } if ((fp_out = fopen(fn_out, "w")) == NULL) { fprintf(stderr, "\n"); perror(fn_in); giveup(2); } task = "scanning to character definitions"; if (traceflag) printf ("TASK: %s\n", task); newstyle = copytochars(fp_in, fp_out); if (traceflag != 0) { if (newstyle != 0) printf("New Style file\n"); } if (newstyle != 0) fprintf(fp_out, "]\n"); task = "processing all characters"; if (traceflag) printf ("TASK: %s\n", task); err = doallchars(fp_in, fp_out); task = "copying trailer information"; if (traceflag) printf ("TASK: %s\n", task); if (newstyle == 0 && err != EOF) copytoEOF(fp_in, fp_out); printf("\n"); task = "closing files"; if (traceflag) printf ("TASK: %s\n", task); statistics(); fclose(fp_in); if (ferror(fp_out) != 0) { fprintf(stderr, "Output error"); perror(fn_out); giveup(2); } fclose(fp_out); } task = "finishing up"; if (traceflag) printf ("TASK: %s\n", task); if (argc - firstarg > 1) printf( "Processed %d font files\n", argc - firstarg); return 0; } /* there is a lot of repetition in trypatchxup etc */ /* trade-off here between how much we want to stick close to original */ /* versus how concise we want to make the output */ /* avoid replication when "/char {} def" is seen - cminch problem */ /* fonts without complete character set yield problems /char {} def */ /* should complain about antiparallel segments */ /* should check for three knots coinciding... */ /* may print an extra `h' line at end in newstyle ... */ /* DON'T COMPILE WITH TOO HIGH OPTIMIZATION - induces bugs at run-time */