From: kramm Date: Sat, 5 Jul 2003 13:16:57 +0000 (+0000) Subject: upgraded to 3.4.3. X-Git-Tag: release-0-5-0~215 X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=commitdiff_plain;h=50760be540fa6802296d7cb6eafa4d4b82a47a3b upgraded to 3.4.3. --- diff --git a/pdf2swf/ttf2pt1/Makefile.in b/pdf2swf/ttf2pt1/Makefile.in index 3e1b76a..a288fc1 100644 --- a/pdf2swf/ttf2pt1/Makefile.in +++ b/pdf2swf/ttf2pt1/Makefile.in @@ -5,7 +5,7 @@ include ../../Makefile.common all: libpdf.a -ttf2pt1_objects = ft.o ttf.o pt1.o ttf2pt1.o t1asm.o +ttf2pt1_objects = ft.o ttf.o pt1.o ttf2pt1.o t1asm.o bdf.o bitmap.o ft.o: ft.c $(C) -I./ -Wno-parentheses ft.c -o $@ @@ -17,6 +17,10 @@ ttf2pt1.o: ttf2pt1.c $(C) -I./ -Wno-parentheses ttf2pt1.c -o $@ t1asm.o: t1asm.c $(C) -I./ -Wno-parentheses t1asm.c -o $@ +bitmap.o: bitmap.c + $(C) -I./ -Wno-parentheses bitmap.c -o $@ +bdf.o: bdf.c + $(C) -I./ -Wno-parentheses bdf.c -o $@ libpdf.a: $(ttf2pt1_objects) $(AR) r ttf2pt1.a $(ttf2pt1_objects) diff --git a/pdf2swf/ttf2pt1/ft.c b/pdf2swf/ttf2pt1/ft.c index 9b65785..92ffedd 100644 --- a/pdf2swf/ttf2pt1/ft.c +++ b/pdf2swf/ttf2pt1/ft.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include "pt1.h" @@ -51,7 +51,7 @@ struct frontsw freetype_sw = { /* statics */ -static char * dupcnstring( char *s, int len); +static char * dupcnstring( unsigned char *s, int len); static FT_Library library; static FT_Face face; @@ -153,10 +153,9 @@ glnames( for(i=0; i < face->num_glyphs; i++) { if( FT_Get_Glyph_Name(face, i, bf, MAX_NAMELEN) || bf[0]==0 ) { - sprintf(bf, "_%d", i); + sprintf(bf, "_g_%d", i); WARNING_2 fprintf(stderr, - "**** Glyph No. %d has no postscript name, becomes %s ****\n", - i, bf); + "Glyph No. %d has no postscript name, becomes %s\n", i, bf); } glyph_list[i].name = strdup(bf); if(ISDBG(FT)) fprintf(stderr, "%d has name %s\n", i, bf); @@ -338,19 +337,29 @@ populate_map: /* duplicate a string with counter to a 0-terminated string */ static char * dupcnstring( - char *s, + unsigned char *s, int len ) { - char *res; + char *res, *out; + int i, c; + static int warned=0; if(( res = malloc(len+1) )==NULL) { fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); exit(255); } - memcpy(res, s, len); - res[len] = 0; + out = res; + for(i=0; i=' ' && c!=127) + *out++ = c; + else if(!warned) { + warned=1; + WARNING_1 fprintf(stderr, "Some font name strings are in Unicode, may not show properly\n"); + } + } + *out = 0; return res; } @@ -441,6 +450,9 @@ fnmetrics( } else fm->name_ps = dupcnstring(sn.string, sn.string_len); #endif /* ENABLE_SFNT */ + for(i=0; fm->name_ps[i]!=0; i++) + if(fm->name_ps[i] == ' ') + fm->name_ps[i] = '_'; /* no spaces in the Postscript name *m /* guess the boldness from the font names */ fm->force_bold=0; diff --git a/pdf2swf/ttf2pt1/global.h b/pdf2swf/ttf2pt1/global.h index d8e9b02..d131adb 100644 --- a/pdf2swf/ttf2pt1/global.h +++ b/pdf2swf/ttf2pt1/global.h @@ -26,13 +26,19 @@ extern int hints; /* enables autogeneration of hints */ extern int subhints; /* enables autogeneration of substituted hints */ extern int trybold; /* try to guess whether the font is bold */ extern int correctwidth; /* try to correct the character width */ +extern int vectorize; /* vectorize the bitmaps */ +extern int use_autotrace; /* use the autotrace library on bitmap */ +/* options - suboptions of File Generation */ +extern int gen_pfa; /* generate the font file */ +extern int gen_afm; /* generate the metrics file */ +extern int gen_dvienc; /* generate the dvips encoding file */ /* not quite options to select a particular source encoding */ extern int force_pid; /* specific platform id */ extern int force_eid; /* specific encoding id */ /* other globals */ -extern FILE *pfa_file, *afm_file; +extern FILE *null_file, *pfa_file, *afm_file, *dvienc_file; extern int numglyphs; /* warnings */ @@ -106,6 +112,10 @@ extern int numglyphs; int iscale( int val); double fscale( double val); int unicode_rev_lookup( int unival); +void bmp_outline( GLYPH *g, int scale, char *bmap, + int xsz, int ysz, int xoff, int yoff); +int isign( int x); +int fsign( double x); /* global metrics for a font */ diff --git a/pdf2swf/ttf2pt1/pt1.c b/pdf2swf/ttf2pt1/pt1.c index 1ba7984..3f2bc08 100644 --- a/pdf2swf/ttf2pt1/pt1.c +++ b/pdf2swf/ttf2pt1/pt1.c @@ -27,6 +27,86 @@ #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 */ @@ -42,8 +122,6 @@ int encoding[ENCTABSZ]; /* inverse of glyph[].char_no */ int kerning_pairs = 0; /* prototypes */ -static int isign( int x); -static int fsign( double x); static void fixcvdir( GENTRY * ge, int dir); static void fixcvends( GENTRY * ge); static int fgetcvdir( GENTRY * ge); @@ -63,14 +141,19 @@ 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); static double fcvarea( GENTRY *ge); -static int fckjoinedcv( GLYPH *g, double t, GENTRY *nge, - GENTRY *old1, GENTRY *old2, double k); 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); -static int +int isign( int x ) @@ -83,7 +166,7 @@ isign( return 0; } -static int +int fsign( double x ) @@ -354,6 +437,50 @@ fg_rmoveto( } 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, @@ -400,6 +527,50 @@ fg_rlineto( } 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, @@ -464,6 +635,67 @@ fg_rrcurveto( } 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 ) @@ -486,6 +718,9 @@ g_closepath( g->lastentry = oge->prev; if (oge->prev == 0) g->entries = 0; + else + g->lastentry->next = 0; + free(oge); } } return; @@ -646,41 +881,6 @@ fixcvends( } } -/* if we have any curves that are in fact flat but -** are not horizontal nor vertical, substitute -** them also with lines -*/ - -void -flattencurves( - GLYPH * g -) -{ - GENTRY *ge; - int x0, y0, x1, y1, x2, y2, x3, y3; - - assertisint(g, "flattencurves INT"); - - for (ge = g->entries; ge != 0; ge = ge->next) { - if (ge->type != GE_CURVE) - continue; - - 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; - - if ((x1 - x0) * (y2 - y1) == (x2 - x1) * (y1 - y0) - && (x1 - x0) * (y3 - y2) == (x3 - x2) * (y1 - y0)) { - ge->type = GE_LINE; - } - } -} - /* ** After transformations we want to make sure that the resulting ** curve is going in the same quadrant as the original one, @@ -844,29 +1044,47 @@ fgetcvdir( abort(); /* dump core */ } - a = ge->fy3 - ge->prev->fy3; - b = ge->fx3 - ge->prev->fx3; - k = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ( b / a)); - a = ge->fy1 - ge->prev->fy3; - b = ge->fx1 - ge->prev->fx3; - k1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ( b / a)); - a = ge->fy3 - ge->fy2; - b = ge->fx3 - ge->fx2; - k2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ( b / a)); + 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 (k1 < k) + if(fabs(k1-k) < 0.0001) + dir |= CVDIR_FEQUAL; + else if (k1 < k) dir |= CVDIR_FUP; - else if (k1 > k) - dir |= CVDIR_FDOWN; else - dir |= CVDIR_FEQUAL; + dir |= CVDIR_FDOWN; - if (k2 > k) + if(fabs(k2-k) < 0.0001) + dir |= CVDIR_REQUAL; + else if (k2 > k) dir |= CVDIR_RUP; - else if (k2 < k) - dir |= CVDIR_RDOWN; else - dir |= CVDIR_REQUAL; + dir |= CVDIR_RDOWN; return dir; } @@ -890,27 +1108,45 @@ igetcvdir( a = ge->iy3 - ge->prev->iy3; b = ge->ix3 - ge->prev->ix3; - k = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + k = (a == 0) ? (b == 0 ? 1. : 100000.) : fabs((double) b / (double) a); + a = ge->iy1 - ge->prev->iy3; b = ge->ix1 - ge->prev->ix3; - k1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + 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; - k2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + 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 (k1 < k) + if(fabs(k1-k) < 0.0001) + dir |= CVDIR_FEQUAL; + else if (k1 < k) dir |= CVDIR_FUP; - else if (k1 > k) - dir |= CVDIR_FDOWN; else - dir |= CVDIR_FEQUAL; + dir |= CVDIR_FDOWN; - if (k2 > k) + if(fabs(k2-k) < 0.0001) + dir |= CVDIR_REQUAL; + else if (k2 > k) dir |= CVDIR_RUP; - else if (k2 < k) - dir |= CVDIR_RDOWN; else - dir |= CVDIR_REQUAL; + dir |= CVDIR_RDOWN; return dir; } @@ -3707,7 +3943,10 @@ fstraighten( fclosegap(ge, ge, i, df, NULL); } else { /* contour consists of only one line, get rid of it */ - ige = freethisge(ge)->prev; /* keep the iterator valid */ + 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 */ @@ -3953,23 +4192,43 @@ iiszigzag( ) { double k, k1, k2; - double a, b; + int a, b; if (ge->type != GE_CURVE) return 0; a = ge->iy2 - ge->iy1; b = ge->ix2 - ge->ix1; - k = fabs(a == 0 ? (b == 0 ? 1. : FBIGVAL) : (double) b / (double) a); + 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; - k1 = fabs(a == 0 ? (b == 0 ? 1. : FBIGVAL) : (double) b / (double) a); + 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; - k2 = fabs(a == 0 ? (b == 0 ? 1. : FBIGVAL) : (double) b / (double) a); + 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 >= k && k2 <= k || k1 <= k && k2 >= k) + if (k1+0.0001 >= k && k2 <= k+0.0001 || k1 <= k+0.0001 && k2+0.0001 >= k) return 0; else return 1; @@ -3990,16 +4249,36 @@ fiszigzag( a = fabs(ge->fy2 - ge->fy1); b = fabs(ge->fx2 - ge->fx1); - k = a < FEPS ? (b fy1 - ge->prev->fy3); b = fabs(ge->fx1 - ge->prev->fx3); - k1 = a < FEPS ? (b < FEPS ? 1. : FBIGVAL) : b / a; + 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); - k2 = a < FEPS ? (b = k && k2 <= k || k1 <= k && k2 >= k) + if (k1+0.0001 >= k && k2 <= k+0.0001 || k1 <= k+0.0001 && k2+0.0001 >= k) return 0; else return 1; @@ -4025,6 +4304,16 @@ fsplitzigzags( 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); @@ -4053,6 +4342,10 @@ fsplitzigzags( ge->fy1 = (a + b) / 2.; addgeafter(ge, nge); + + if(ISDBG(FCONCISE)) { + dumppaths(g, ge, nge); + } } } @@ -4521,92 +4814,37 @@ fdelsmall( #undef TIMESLARGER } -/* normalize curves to the form where their ends - * can be safely used as derivatives - */ - -static void -fnormalizec( - GLYPH * g -) -{ - GENTRY *ge; - int midsame, frontsame, rearsame, i; - double d, b; - - assertisfloat(g, "normalizing curves"); - - for (ge = g->entries; ge != 0; ge = ge->next) { - if (ge->type != GE_CURVE) - continue; - - midsame = (fabs(ge->fx1-ge->fx2)fy1-ge->fy2)fx1-ge->prev->fx3)fy1-ge->prev->fy3)fx3-ge->fx2)fy3-ge->fy2)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; - } - } else if(frontsame) { - for(i=0; i<2; i++) { - b = ge->prev->fpoints[i][2]; - d = ge->fpoints[i][1] - b; - ge->fpoints[i][0] = b + 0.01*d; - } - } else if(rearsame) { - for(i=0; i<2; i++) { - b = ge->fpoints[i][2]; - d = ge->fpoints[i][0] - b; - ge->fpoints[i][1] = b + 0.01*d; - } - } else - continue; - - if(ISDBG(FCONCISE)) fprintf(stderr, "glyph %g, normalized entry %x\n", g->name, ge); - } -} - /* find the point where two rays continuing vectors cross - * rays are defined as beginning of curve1 and end of curve 2 * returns 1 if they cross, 0 if they don't - * If they cross returns the maximal scales for both vectors. + * 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 -fcrossrays( - GENTRY *ge1, - GENTRY *ge2, - double *max1, - double *max2 +fcrossraysxx( + double crossdot[2][2] ) { - struct ray { - double x1, y1, x2, y2; - int isvert; - double k, b; /* lines are represented as y = k*x + b */ - double *maxp; - } ray [3]; - double x, y; + double x, y, max; int i; - ray[0].x1 = ge1->prev->fx3; - ray[0].y1 = ge1->prev->fy3; - ray[0].x2 = ge1->fx1; - ray[0].y2 = ge1->fy1; - ray[0].maxp = max1; - - ray[1].x1 = ge2->fx3; - ray[1].y1 = ge2->fy3; - ray[1].x2 = ge2->fx2; - ray[1].y2 = ge2->fy2; - ray[1].maxp = max2; - for(i=0; i<2; i++) { if(ray[i].x1 == ray[i].x2) ray[i].isvert = 1; @@ -4642,41 +4880,620 @@ fcrossrays( for(i=0; i<2; i++) { if(ray[i].isvert) - *ray[i].maxp = (y - ray[i].y1) / (ray[i].y2 - ray[i].y1); + max = (y - ray[i].y1) / (ray[i].y2 - ray[i].y1); else - *ray[i].maxp = (x - ray[i].x1) / (ray[i].x2 - ray[i].x1); + max = (x - ray[i].x1) / (ray[i].x2 - ray[i].x1); /* check if wrong sides of rays cross */ - if( *ray[i].maxp < 0 ) { - if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: scale=%g @(%g,%g) (%g,%g)<-(%g,%g)\n", - *ray[i].maxp, x, y, ray[i].x2, ray[i].y2, ray[i].x1, ray[i].y1); + 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; } -/* find the area covered by the curve - * (limited by the projections to the X axis) +/* 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 */ -static double -fcvarea( - GENTRY *ge +int +fcrossrayscv( + double curve[4][2 /*X,Y*/], + double *max1, + double *max2 ) { - double Ly, My, Ny, Py, Qx, Rx, Sx; - double area; + 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; - /* 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; + 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; - /* 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); + 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; iid1+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; irtg < 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) @@ -4685,6 +5502,7 @@ fcvarea( return area; } +#endif /* find the value of point on the curve at the given parameter t, * along the given axis (0 - X, 1 - Y). @@ -4709,224 +5527,807 @@ fcvval( + ge->fpoints[axis][2]*t*t2; } -/* Check that the new curve has the point identified by the - * parameter t reasonably close to the corresponding point - * in the old pair of curves which were joined in proportion k. - * If old2 is NULL then just compare nge and old1 at the point t. - * Returns 0 if OK, 1 if it's too far. +/* + * Find ndots equally spaced dots on a curve or line and fill + * their coordinates into the dots array */ -static int -fckjoinedcv( - GLYPH *g, - double t, - GENTRY *nge, - GENTRY *old1, - GENTRY *old2, - double k +static void +fsampledots( + GENTRY *ge, + double dots[][2], /* the dots to fill */ + int ndots ) { - GENTRY *oge; - double ot; - double off; - double lim; - int i; + int i, axis; + double t, nf, dx, d[2]; + + nf = ndots+1; + if(ge->type == GE_CURVE) { + for(i=0; ifx3 - ge->prev->fx3; + d[1] = ge->fy3 - ge->prev->fy3; + for(i=0; iprev->fpoints[axis][2] + + t*d[axis]; + } + } +} - if(old2 == 0) { - oge = old1; - ot = t; - } else if(t <= k && k!=0.) { - oge = old1; - ot = t/k; - } else { - oge = old2; - ot = (t-k) / (1.-k); +/* + * 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); } +} - if(ISDBG(FCONCISE)) - fprintf(stderr, "%s: t=%g ot=%g (%x) ", g->name, t, ot, oge); +/* + * 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)fy1-ge->fy2)fx1-ge->prev->fx3)fy1-ge->prev->fy3)fx3-ge->fx2)fy3-ge->fy2)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++) { - /* permitted tolerance is 5% */ - lim = fabs(nge->fpoints[i][2] - nge->prev->fpoints[i][2])*0.05; + 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; - if(lim < 3.) - lim = 3.; /* for small curves the tolerance is higher */ - if(lim > 10.) - lim = 10.; /* for big curves the tolerance is limited anyway */ + gex = X_CON(ge); ngex = X_CON(nge); - off = fabs(fcvval(nge, i, t) - fcvval(oge, i, ot)); + /* look if they can be joined honestly */ - if(off > lim) { - if(ISDBG(FCONCISE)) - fprintf(stderr, "out of range d%c=%.2f(%.2f)\n", - (i==0 ? 'X' : 'Y'), off, lim); - return 1; + /* 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(ISDBG(FCONCISE)) - fprintf(stderr, "valid d%c=%.2f(%.2f) ", (i==0 ? 'X' : 'Y'), off, lim); + /* 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; } - if(ISDBG(FCONCISE)) - fprintf(stderr, "\n"); - return 0; } -/* force conciseness: substitute 2 or more curves going in the -** same quadrant with one curve -** in floating point -*/ +/* + * Force conciseness of one contour in the glyph, + * the contour is indicated by one entry from it. + */ -void -fforceconcise( - GLYPH * g +static void +fconcisecontour( + GLYPH *g, + GENTRY *startge ) { - GENTRY *ge, *nge; - GENTRY tge; - double firstlen, lastlen, sumlen, scale; - double dxw1, dyw1, dxw2, dyw2; - double dxb1, dyb1, dxe1, dye1; - double dxb2, dyb2, dxe2, dye2; - double maxsc1, maxsc2; - int i; +/* 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; + } - assertisfloat(g, "enforcing conciseness"); + if(startge->type != GE_CURVE && startge->type != GE_LINE) + return; /* probably a degenerate contour */ - fdelsmall(g, 0.05); - assertpath(g->entries, __FILE__, __LINE__, g->name); - fnormalizec(g); + 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); + } - for (ge = g->entries; ge != 0; ge = ge->next) { - if (ge->type != GE_CURVE) + 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; - /* the whole direction of curve */ - dxw1 = ge->fx3 - ge->prev->fx3; - dyw1 = ge->fy3 - ge->prev->fy3; + /* 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; - while (1) { - /* the whole direction of curve */ - dxw1 = ge->fx3 - ge->prev->fx3; - dyw1 = ge->fy3 - ge->prev->fy3; + pge = ge; + pgex = X_CON(pge->bkwd); - /* directions of ends of curve */ - dxb1 = ge->fx1 - ge->prev->fx3; - dyb1 = ge->fy1 - ge->prev->fy3; - dxe1 = ge->fx3 - ge->fx2; - dye1 = ge->fy3 - ge->fy2; + if(ISDBG(FCONCISE)) + fprintf(stderr, "ge %p prev -> 0x%p ", ge, pge); - nge = ge->frwd; + 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 (nge->type != GE_CURVE) + 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; + } - dxw2 = nge->fx3 - ge->fx3; - dyw2 = nge->fy3 - ge->fy3; + quad &= qq; + pge = pge->bkwd; + pgex = X_CON(pge->bkwd); + if(ISDBG(FCONCISE)) + fprintf(stderr, "0x%p ", pge); + } - dxb2 = nge->fx1 - ge->fx3; - dyb2 = nge->fy1 - ge->fy3; - dxe2 = nge->fx3 - nge->fx2; - dye2 = nge->fy3 - nge->fy2; + /* collect as many entries for joining as possible */ + nge = ge->frwd; + ngex = X_CON(nge); + nngex = X_CON(nge->frwd); - /* if curve changes direction */ - if (fsign(dxw1) != fsign(dxw2) || fsign(dyw1) != fsign(dyw2)) - break; + if(ISDBG(FCONCISE)) + fprintf(stderr, ": 0x%x\nnext -> 0x%p ", pge, nge); - /* if the arch is going in other direction */ - if (fsign(fabs(dxb1 * dyw1) - fabs(dyb1 * dxw1)) - * fsign(fabs(dxe2 * dyw2) - fabs(dye2 * dxw2)) > 0) - break; + 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; - /* get possible scale limits within which we won't cross quadrants */ - if( fcrossrays(ge, nge, &maxsc1, &maxsc2) == 0 ) { - if(ISDBG(FCONCISE)) { - fprintf(stderr, "glyph %s has curves with strange ends\n", g->name); - dumppaths(g, ge, nge); + 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; } - if(maxsc1 < 1. || maxsc2 < 1. ) /* would create a zigzag */ - break; + quad &= qq; + nge = nge->frwd; + ngex = nngex; + nngex = X_CON(nge->frwd); + if(ISDBG(FCONCISE)) + fprintf(stderr, "0x%p ", nge); + } - ge->dir = fgetcvdir(ge); - nge->dir = fgetcvdir(nge); + if(ISDBG(FCONCISE)) + fprintf(stderr, ": 0x%x\n", nge); - if( ((ge->dir&CVDIR_FRONT)-CVDIR_FEQUAL) * ((nge->dir&CVDIR_REAR)-CVDIR_REQUAL) < 0 ) - /* would create a zigzag */ + /* 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; idots[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; + } + } - firstlen = sqrt( dxe1*dxe1 + dye1*dye1 ); - lastlen = sqrt( dxb2*dxb2 + dyb2*dyb2 ); - sumlen = firstlen + lastlen; + /* join flat segments into lines */ + /* here ge==startge */ + while(1) { + gex = X_CON(ge); + if( !(gex->flags & GEXF_JLINE) ) + goto next2; - /* check the scale limits */ - if( sumlen/firstlen > maxsc1 || sumlen/lastlen > maxsc2 ) { - if(ISDBG(FCONCISE)) - fprintf(stderr, "%s: %x, %x would be crossing in forceconcise\n", - g->name, ge, nge); + 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; } - /* OK, it seems like we can attempt to join these two curves */ - tge.flags = ge->flags; - tge.prev = ge->prev; - tge.fx1 = ge->fx1; - tge.fy1 = ge->fy1; - tge.fx2 = nge->fx2; - tge.fy2 = nge->fy2; - tge.fx3 = nge->fx3; - tge.fy3 = nge->fy3; - - dxb1 = tge.fx1 - tge.prev->fx3; - dyb1 = tge.fy1 - tge.prev->fy3; - dxe1 = tge.fx3 - tge.fx2; - dye1 = tge.fy3 - tge.fy2; - - /* scale the first segment */ - scale = sumlen / firstlen; - tge.fx1 = tge.prev->fx3 + scale * dxb1; - tge.fy1 = tge.prev->fy3 + scale * dyb1; - - /* scale the last segment */ - scale = sumlen / lastlen; - tge.fx2 = tge.fx3 - scale * dxe1; - tge.fy2 = tge.fy3 - scale * dye1; - - /* now check if we got something sensible */ - - /* check if some important points is too far from original */ - scale = firstlen / sumlen; - { - double pts[4] = { 0./*will be replaced*/, 0.5, 0.25, 0.75 }; - int i, bad; - - pts[0] = scale; - bad = 0; - - for(i=0; iflags & 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 CVEPS2) break; + } + if(iflags &= ~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 */ } - if(bad) - break; - } + } + } else if(ISDBG(FCONCISE)) + fprintf(stderr, "(p=%p) ", pge); - /* OK, it looks reasonably, let's apply it */ - if(ISDBG(FCONCISE)) - dumppaths(g, ge, nge); + 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 CVEPS2) + break; + } + if(ifrwd); + ndots--; + ngex->flags &= ~GEXF_JLINE; + } else { + nge = nge->frwd; + } + } else if(ISDBG(FCONCISE)) + fprintf(stderr, "(n=%p) ", nge); + } - for(i=0; i<3; i++) { - ge->fxn[i] = tge.fxn[i]; - ge->fyn[i] = tge.fyn[i]; - } + pge = pge->frwd; /* now the limits are pge...nge inclusive */ + if(pge == nge) /* a deeply perversive contour */ + break; - freethisge(nge); + 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 @@ -4940,6 +6341,11 @@ print_glyph( 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); diff --git a/pdf2swf/ttf2pt1/pt1.h b/pdf2swf/ttf2pt1/pt1.h index 7540531..4f65f88 100644 --- a/pdf2swf/ttf2pt1/pt1.h +++ b/pdf2swf/ttf2pt1/pt1.h @@ -19,6 +19,9 @@ typedef struct gentry { #define bkwd cntr[0] #define frwd cntr[1] + /* various extended structures used at some stage of transformation */ + void *ext; + union { struct { int val[2][3]; /* integer values */ @@ -49,6 +52,7 @@ typedef struct gentry { char flags; #define GEF_FLOAT 0x02 /* entry contains floating point data */ +#define GEF_LINE 0x04 /* entry looks like a line even if it's a curve */ unsigned char dir; /* used to temporarily store the values for * the directions of the ends of curves */ @@ -73,6 +77,10 @@ typedef struct gentry { #define GE_LINE 'L' #define GE_CURVE 'C' #define GE_PATH 'P' + + /* indexes of the points to be used for calculation of the tangents */ + signed char ftg; /* front tangent */ + signed char rtg; /* rear tangent, -1 means "idx 2 of the previous entry" */ } GENTRY; /* stem structure, describes one [hv]stem */ @@ -178,6 +186,14 @@ typedef struct glyph { } GLYPH; +/* description of a dot for calculation of its distance to a curve */ + +struct dot_dist { + double p[2 /*X,Y*/]; /* coordinates of a dot */ + double dist2; /* squared distance from the dot to the curve */ + short seg; /* the closest segment of the curve */ +}; + extern int stdhw, stdvw; /* dominant stems widths */ extern int stemsnaph[12], stemsnapv[12]; /* most typical stem width */ @@ -232,3 +248,10 @@ void stemstatistics(void); void docorrectwidth(void); void addkernpair( unsigned id1, unsigned id2, int unscval); void print_kerning( FILE *afm_file); + +int fcrossrayscv( double curve[4][2], double *max1, double *max2); +int fcrossraysge( GENTRY *ge1, GENTRY *ge2, double *max1, double *max2, + double crossdot[2][2]); +double fdotsegdist2( double seg[2][2], double dot[2]); +double fdotcurvdist2( double curve[4][2], struct dot_dist *dots, int ndots, double *maxp); +void fapproxcurve( double cv[4][2], struct dot_dist *dots, int ndots); diff --git a/pdf2swf/ttf2pt1/t1asm.c b/pdf2swf/ttf2pt1/t1asm.c index d9c3061..0c60a0c 100644 --- a/pdf2swf/ttf2pt1/t1asm.c +++ b/pdf2swf/ttf2pt1/t1asm.c @@ -54,10 +54,10 @@ static char portnotice[] = #include #include -#ifdef WINDOWS +#ifdef WIN32 # ifdef STANDALONE # define WINDOWS_FUNCTIONS -# include "windows.h" +# include "win_missing.h" # endif #endif diff --git a/pdf2swf/ttf2pt1/ttf.c b/pdf2swf/ttf2pt1/ttf.c index 23dcb43..b8db0e8 100644 --- a/pdf2swf/ttf2pt1/ttf.c +++ b/pdf2swf/ttf2pt1/ttf.c @@ -21,7 +21,7 @@ # include # include #else -# include "win_missing.h" +# include "windows.h" #endif #include "ttf.h" @@ -397,10 +397,10 @@ draw_composite_glyf( } else if (flagbyte & WE_HAVE_A_TWO_BY_TWO) { matrix[0] = f2dot14(*sptr); sptr++; - matrix[1] = f2dot14(*sptr); - sptr++; matrix[2] = f2dot14(*sptr); sptr++; + matrix[1] = f2dot14(*sptr); + sptr++; matrix[3] = f2dot14(*sptr); sptr++; } else { @@ -1035,24 +1035,24 @@ glnames( } else if (n < 258 + n_ps_names) { glyph_list[i].name = ps_name_ptr[n - 258]; } else { - glyph_list[i].name = malloc(10); - sprintf(glyph_list[i].name, "_%d", n); + glyph_list[i].name = malloc(16); + sprintf(glyph_list[i].name, "_g_%d", i); WARNING_2 fprintf(stderr, - "**** Glyph No. %d has no postscript name, becomes %s ****\n", + "Glyph No. %d has no postscript name, becomes %s\n", i, glyph_list[i].name); } } /* Now fake postscript names for all those beyond the end of the table */ if (npost < ttf_nglyphs) { for (i=npost; iitalic_angle = (short) (ntohs(post_table->italicAngle.upper)) + ((short) ntohs(post_table->italicAngle.lower) / 65536.0); @@ -1395,14 +1395,15 @@ fnmetrics( fm->force_bold=0; for(i=0; !fm->force_bold && i= len || !islower(str[j+4])) ) { fm->force_bold=1; break; diff --git a/pdf2swf/ttf2pt1/ttf2pt1.c b/pdf2swf/ttf2pt1/ttf2pt1.c index 472d1ca..7a916ab 100644 --- a/pdf2swf/ttf2pt1/ttf2pt1.c +++ b/pdf2swf/ttf2pt1/ttf2pt1.c @@ -4,7 +4,7 @@ * Based on ttf2pfa by Andrew Weeks * With help from Frank M. Siegert * - * see COPYRIGHT + * see COPYRIGHT for full copyright notice * *********************************************************************** * @@ -89,11 +89,13 @@ /* table of front-ends */ extern struct frontsw ttf_sw; +extern struct frontsw bdf_sw; #if defined(USE_FREETYPE) extern struct frontsw freetype_sw; #endif struct frontsw *frontswtab[] = { + &bdf_sw, #if defined(USE_FREETYPE) && defined(PREFER_FREETYPE) &freetype_sw, #endif @@ -114,7 +116,7 @@ int wantafm=0; /* want to see .afm instead of .t1a on stdout */ int correctvsize=0; /* try to correct the vertical size of characters */ int wantuid = 0; /* user wants UniqueID entry in the font */ int allglyphs = 0; /* convert all glyphs, not only 256 of them */ -int warnlevel = -1; /* the level of permitted warnings */ +int warnlevel = 3; /* the level of permitted warnings */ int forcemap = 0; /* do mapping even on non-Unicode fonts */ /* options - maximal limits */ int max_stemdepth = 128; /* maximal depth of stem stack in interpreter (128 - limit from X11) */ @@ -129,31 +131,31 @@ int hints; /* enables autogeneration of hints */ int subhints; /* enables autogeneration of substituted hints */ int trybold; /* try to guess whether the font is bold */ int correctwidth; /* try to correct the character width */ +int vectorize; /* vectorize the bitmaps */ +int use_autotrace; /* use the autotrace library on bitmap */ +/* options - suboptions of File Generation, defaults are set in table */ +int gen_pfa; /* generate the font file */ +int gen_afm; /* generate the metrics file */ +int gen_dvienc; /* generate the dvips encoding file */ /* not quite options to select a particular source encoding */ int force_pid = -1; /* specific platform id */ int force_eid = -1; /* specific encoding id */ -/* table of Outline Processing (may think also as Optimization) options */ -static struct { +/* structure to define the sub-option lists controlled by the + * case: uppercase enables them, lowercase disables + */ +struct subo_case { char disbl; /* character to disable - enforced lowercase */ char enbl; /* character to enable - auto-set as toupper(disbl) */ int *valp; /* pointer to the actual variable containing value */ int dflt; /* default value */ char *descr; /* description */ -} opotbl[] = { - { 'b', 0/*auto-set*/, &trybold, 1, "guessing of the ForceBold hint" }, - { 'h', 0/*auto-set*/, &hints, 1, "autogeneration of hints" }, - { 'u', 0/*auto-set*/, &subhints, 1, "hint substitution technique" }, - { 'o', 0/*auto-set*/, &optimize, 1, "space optimization of font files" }, - { 's', 0/*auto-set*/, &smooth, 1, "smoothing and repair of outlines" }, - { 't', 0/*auto-set*/, &transform, 1, "auto-scaling to the standard matrix 1000x1000" }, - { 'w', 0/*auto-set*/, &correctwidth, 0, "correct the glyph widths (use only for buggy fonts)" }, }; int debug = DEBUG; /* debugging flag */ -FILE *pfa_file, *afm_file; +FILE *null_file, *pfa_file, *afm_file, *dvienc_file; int numglyphs; struct font_metrics fontm; @@ -1139,6 +1141,36 @@ unicode_prepare_buckets( } /* + * When we print errors about bad names we want to print these names in + * some decent-looking form + */ + +static char * +nametoprint( + unsigned char *s +) +{ + static char res[50]; + int c, i; + + for(i=0; ( c =* s )!=0 && i 126) { + sprintf(res+i, "\\x%02X", c); + i+=4; + } else { + res[i++] = c; + } + } + if(*s != 0) { + res[i++] = '.'; + res[i++] = '.'; + res[i++] = '.'; + } + res[i++] = 0; + return res; +} + +/* * Scale the values according to the scale_factor */ @@ -1259,8 +1291,6 @@ convert_glyf( if (smooth) { smoothjoints(g); assertpath(g->entries, __FILE__, __LINE__, g->name); - - flattencurves(g); } ncurves = 0; @@ -1269,8 +1299,8 @@ convert_glyf( for(ge = g->entries; ge; ge = ge->next) ncurves++; } - if (ncurves > 100) { - WARNING_2 fprintf(stderr, + if (ncurves > 200) { + WARNING_3 fprintf(stderr, "** Glyph %s is too long, may display incorrectly\n", g->name); } @@ -1292,14 +1322,14 @@ handle_gnames(void) for (n = 0; n < numglyphs; n++) { int c; for (i = 0; (c = glyph_list[n].name[i]) != 0; i++) { - if (!(isalnum(c) || c == '.' || c == '_' ) + if (!(isalnum(c) || c == '.' || c == '_' || c == '-') || i==0 && isdigit(c)) { /* must not start with a digit */ WARNING_3 fprintf(stderr, "Glyph %d %s (%s), ", n, isdigit(c) ? "name starts with a digit" : "has bad characters in name", - glyph_list[n].name); - glyph_list[n].name = malloc(10); - sprintf(glyph_list[n].name, "_%d", n); + nametoprint(glyph_list[n].name)); + glyph_list[n].name = malloc(16); + sprintf(glyph_list[n].name, "_b_%d", n); WARNING_3 fprintf(stderr, "changing to %s\n", glyph_list[n].name); break; } @@ -1312,13 +1342,23 @@ handle_gnames(void) found = 0; for (i = 0; i < n && !found; i++) { if (strcmp(glyph_list[i].name, glyph_list[n].name) == 0) { - glyph_list[n].name = malloc(10); - sprintf(glyph_list[n].name, "_%d", n); - WARNING_3 fprintf(stderr, - "Glyph %d has the same name as %d: (%s), changing to %s\n", - n, i, - glyph_list[i].name, - glyph_list[n].name); + if (( glyph_list[n].name = malloc(16) )==0) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + sprintf(glyph_list[n].name, "_d_%d", n); + + /* if the font has no names in it (what the native parser + * recognises as ps_fmt_3), FreeType returns all the + * names as .notdef, so don't complain in this case + */ + if(strcmp(glyph_list[i].name, ".notdef")) { + WARNING_3 fprintf(stderr, + "Glyph %d has the same name as %d: (%s), changing to %s\n", + n, i, + glyph_list[i].name, + glyph_list[n].name); + } found = 1; } } @@ -1348,7 +1388,8 @@ handle_gnames(void) unicode_map[n] = -1; uni_lang_selected->init[i](uni_lang_arg); unicode_prepare_buckets(); - if( cursw->glenc(glyph_list, encoding, unicode_map) == 0 ) + type = cursw->glenc(glyph_list, encoding, unicode_map); + if( type == 0 ) /* if we have an 8-bit encoding we don't need more tries */ break; } @@ -1361,16 +1402,42 @@ handle_gnames(void) unicode_map[n] = -1; uni_lang[i].init[0](uni_lang_arg); unicode_prepare_buckets(); - if( cursw->glenc(glyph_list, encoding, unicode_map) == 0 ) + type = cursw->glenc(glyph_list, encoding, unicode_map); + if( type == 0 ) /* if we have an 8-bit encoding we don't need more tries */ break; } } if (ps_fmt_3) { - for (i = 0; i < 256; i++) { /* here 256, not ENCTABSZ */ - if (encoding[i] > 0) { - glyph_list[encoding[i]].name = Fmt3Encoding[i]; + /* get rid of the old names, they are all "UNKNOWN" anyawy */ + for (i = 0; i < numglyphs; i++) { + glyph_list[i].name = 0; + } + if(type == 0) { + /* 8-bit - give 8859/1 names to the first 256 glyphs */ + for (i = 0; i < 256; i++) { /* here 256, not ENCTABSZ */ + if (encoding[i] > 0) { + glyph_list[encoding[i]].name = Fmt3Encoding[i]; + } + } + } else if(type == 1) { + /* Unicode - give 8859/1 names to the first 256 glyphs of Unicode */ + for (n = 0; n < 256; n++) { /* here 256, not ENCTABSZ */ + i = unicode_rev_lookup(n); + if (i>=0 && encoding[i] > 0) { + glyph_list[encoding[i]].name = Fmt3Encoding[i]; + } + } + } /* for other types of encodings just give generated names */ + /* assign unique names to the rest of the glyphs */ + for (i = 0; i < numglyphs; i++) { + if (glyph_list[i].name == 0) { + if (( glyph_list[i].name = malloc(16) )==0) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + sprintf(glyph_list[i].name, "_d_%d", i); } } } @@ -1387,8 +1454,21 @@ handle_gnames(void) /* all the encoding things are done */ for (i = 0; i < ENCTABSZ; i++) - if(encoding[i] == -1) /* defaults to .notdef */ - encoding[i] = 0; + if(encoding[i] == -1) { + /* check whether this character might be a duplicate + * (in which case it would be missed by unicode_rev_lookup()) + */ + c = unicode_map[i]; + if((type != 0 || forcemap) && c != -1) { + for(n = 0; n < i; n++) { + if(unicode_map[n] == c) { + encoding[i] = encoding[n]; + } + } + } + if(encoding[i] == -1) /* still not found, defaults to .notdef */ + encoding[i] = 0; + } for (i = 0; i < 256; i++) /* here 256, not ENCTABSZ */ glyph_list[encoding[i]].char_no = i; @@ -1428,8 +1508,6 @@ usage(void) fplop("This build supports both short and long option names,\n"); fplop("the long options are listed before corresponding short ones\n"); - fplop(" --afm\n"); - fputs(" -A - write the .afm file to STDOUT instead of the font itself\n", stderr); fplop(" --all-glyphs\n"); fputs(" -a - include all glyphs, even those not in the encoding table\n", stderr); fplop(" --pfb\n"); @@ -1440,6 +1518,8 @@ usage(void) fputs(" -e - produce a fully encoded .pfa file\n", stderr); fplop(" --force-unicode\n"); fputs(" -F - force use of Unicode encoding even if other MS encoding detected\n", stderr); + fplop(" --generate suboptions\n"); + fputs(" -G suboptions - control the file generation, run ttf2pt1 -G? for help\n", stderr); fplop(" --language language\n"); fputs(" -l language - convert Unicode to specified language, run ttf2pt1 -l? for list\n", stderr); fplop(" --language-map file\n"); @@ -1459,14 +1539,16 @@ usage(void) fputs(" -V - print ttf2pt1 version number\n", stderr); fplop(" --warning number\n"); fputs(" -W number - set the level of permitted warnings (0 - disable)\n", stderr); - fputs("Obsolete options (will be removed in future releases, use -O? instead):\n", stderr); - fputs(" -f - don't try to guess the value of the ForceBold hint\n", stderr); - fputs(" -h - disable autogeneration of hints\n", stderr); - fputs(" -H - disable hint substitution\n", stderr); - fputs(" -o - disable outline optimization\n", stderr); - fputs(" -s - disable outline smoothing\n", stderr); - fputs(" -t - disable auto-scaling to 1000x1000 standard matrix\n", stderr); - fputs(" -w - correct the glyph widths (use only for buggy fonts)\n", stderr); + fputs("Obsolete options (will be removed in future releases):\n", stderr); + fplop(" --afm\n"); + fputs(" -A - write the .afm file to STDOUT instead of the font, now -GA\n", stderr); + fputs(" -f - don't try to guess the value of the ForceBold hint, now -Ob\n", stderr); + fputs(" -h - disable autogeneration of hints, now -Oh\n", stderr); + fputs(" -H - disable hint substitution, now -Ou\n", stderr); + fputs(" -o - disable outline optimization, now -Oo\n", stderr); + fputs(" -s - disable outline smoothing, now -Os\n", stderr); + fputs(" -t - disable auto-scaling to 1000x1000 standard matrix, now -Ot\n", stderr); + fputs(" -w - correct the glyph widths (use only for buggy fonts), now -OW\n", stderr); fputs("With no , write to with suffix replaced.\n", stderr); fputs("The last '-' means 'use STDOUT'.\n", stderr); @@ -1479,6 +1561,80 @@ printversion(void) { fprintf(stderr, "ttf2pt1 %s\n", TTF2PT1_VERSION); } + +/* initialize a table of suboptions */ +static void +init_subo_tbl( + struct subo_case *tbl +) +{ + int i; + + for(i=0; tbl[i].disbl != 0; i++) { + tbl[i].disbl = tolower(tbl[i].disbl); + tbl[i].enbl = toupper(tbl[i].disbl); + *(tbl[i].valp) = tbl[i].dflt; + } +} + +/* print the default value of the suboptions */ +static void +print_subo_dflt( + FILE *f, + struct subo_case *tbl +) +{ + int i; + + for(i=0; tbl[i].disbl != 0; i++) { + if(tbl[i].dflt) + putc(tbl[i].enbl, f); + else + putc(tbl[i].disbl, f); + } +} + +/* print the usage message for the suboptions */ +static void +print_subo_usage( + FILE *f, + struct subo_case *tbl +) +{ + int i; + + fprintf(f,"The lowercase suboptions disable features, corresponding\n"); + fprintf(f,"uppercase suboptions enable them. The supported suboptions,\n"); + fprintf(f,"their default states and the features they control are:\n"); + for(i=0; tbl[i].disbl != 0; i++) { + fprintf(f," %c/%c - [%s] %s\n", tbl[i].disbl, tbl[i].enbl, + tbl[i].dflt ? "enabled" : "disabled", tbl[i].descr); + } +} + +/* find and set the entry according to suboption, + * return the found entry (or if not found return NULL) + */ +struct subo_case * +set_subo( + struct subo_case *tbl, + int subopt +) +{ + int i; + + for(i=0; tbl[i].disbl != 0; i++) { + if(subopt == tbl[i].disbl) { + *(tbl[i].valp) = 0; + return &tbl[i]; + } else if(subopt == tbl[i].enbl) { + *(tbl[i].valp) = 1; + return &tbl[i]; + } + } + return NULL; +} + int ttf2pt1_main( @@ -1488,13 +1644,14 @@ ttf2pt1_main( { int i, j; time_t now; - char filename[256]; + char filename[4096]; int c,nchars,nmetrics; int ws; int forcebold= -1; /* -1 means "don't know" */ char *lang; int oc; int subid; + char *cmdline; #ifdef _GNU_SOURCE # define ttf2pt1_getopt(a, b, c, d, e) getopt_long(a, b, c, d, e) static struct option longopts[] = { @@ -1504,6 +1661,7 @@ ttf2pt1_main( { "debug", 1, NULL, 'd' }, { "encode", 0, NULL, 'e' }, { "force-unicode", 0, NULL, 'F' }, + { "generate", 1, NULL, 'G' }, { "language", 1, NULL, 'l' }, { "language-map", 1, NULL, 'L' }, { "limit", 1, NULL, 'm' }, @@ -1518,17 +1676,65 @@ ttf2pt1_main( #else # define ttf2pt1_getopt(a, b, c, d, e) getopt(a, b, c) #endif + /* table of Outline Processing (may think also as Optimization) options */ + static struct subo_case opotbl[] = { + { 'b', 0/*auto-set*/, &trybold, 1, "guessing of the ForceBold hint" }, + { 'h', 0/*auto-set*/, &hints, 1, "autogeneration of hints" }, + { 'u', 0/*auto-set*/, &subhints, 1, "hint substitution technique" }, + { 'o', 0/*auto-set*/, &optimize, 1, "space optimization of font files" }, + { 's', 0/*auto-set*/, &smooth, 1, "smoothing and repair of outlines" }, + { 't', 0/*auto-set*/, &transform, 1, "auto-scaling to the standard matrix 1000x1000" }, + { 'w', 0/*auto-set*/, &correctwidth, 0, "correct the glyph widths (use only for buggy fonts)" }, + { 'v', 0/*auto-set*/, &vectorize, 0, "vectorize (trace) the bitmaps" }, +#ifdef USE_AUTOTRACE + { 'z', 0/*auto-set*/, &use_autotrace, 0, "use the autotrace library on bitmaps (works badly)" }, +#endif /*USE_AUTOTRACE*/ + { 0, 0, 0, 0, 0} /* terminator */ + }; + /* table of the File Generation options */ + static struct subo_case fgotbl[] = { + { 'f', 0/*auto-set*/, &gen_pfa, 1, "generate the font file (.t1a, .pfa or .pfb)" }, + { 'a', 0/*auto-set*/, &gen_afm, 1, "generate the Adobe metrics file (.afm)" }, + { 'e', 0/*auto-set*/, &gen_dvienc, 0, "generate the dvips encoding file (.enc)" }, + { 0, 0, 0, 0, 0} /* terminator */ + }; + int *genlast = NULL; + - /* initialize sub-options of -O */ - for(i=0; i< (sizeof opotbl)/(sizeof opotbl[0]); i++) { - opotbl[i].disbl = tolower(opotbl[i].disbl); - opotbl[i].enbl = toupper(opotbl[i].disbl); - *(opotbl[i].valp) = opotbl[i].dflt; + init_subo_tbl(opotbl); /* initialize sub-options of -O */ + init_subo_tbl(fgotbl); /* initialize sub-options of -G */ + + /* save the command line for the record + * (we don't bother about escaping the shell special characters) + */ + + j = 0; + for(i=1; ivalp) ) + genlast = s->valp; } break; } @@ -1842,65 +2052,83 @@ ttf2pt1_main( /* open the input file */ cursw->open(argv[1], front_arg); - /* Get base name of output file (if not specified) + /* Get base name of output file (if not specified) * by removing (known) suffixes */ - if (argc == 2) { - char *p; - argv[2] = strdup (argv[1]); + if (argc == 2) { + char *p; + argv[2] = strdup (argv[1]); p = strrchr(argv[2], '.'); - if (p != NULL) - for (j = 0; (j < MAXSUFFIX) && (cursw->suffix[j]); j++) - if (!strcmp(p+1, cursw->suffix[j])) { - *p = '\0'; - break; - } + if (p != NULL) + for (j = 0; (j < MAXSUFFIX) && (cursw->suffix[j]); j++) + if (!strcmp(p+1, cursw->suffix[j])) { + *p = '\0'; + break; + } + } + + if ((null_file = fopen(BITBUCKET, "w")) == NULL) { + fprintf(stderr, "**** Cannot open %s ****\n", + BITBUCKET); + exit(1); } if (argv[2][0] == '-' && argv[2][1] == 0) { - pfa_file = stdout; #ifdef WIN32 if(encode) { fprintf(stderr, "**** can't write encoded file to stdout ***\n"); exit(1); } -#endif /* WINDOWS */ - if ((afm_file = fopen(BITBUCKET, "w+")) == NULL) { - fprintf(stderr, "**** Cannot open %s ****\n", - BITBUCKET); - exit(1); - } - if(wantafm) { /* print .afm instead of .pfa */ - FILE *n; - n=pfa_file; - pfa_file=afm_file; - afm_file=n; +#endif /* WIN32 */ + pfa_file = afm_file = dvienc_file = null_file; + + if(wantafm || genlast == &gen_afm) { /* print .afm instead of .pfa */ + afm_file=stdout; + } else if(genlast == &gen_dvienc) { /* print .enc instead of .pfa */ + dvienc_file=stdout; + } else { + pfa_file=stdout; } } else { #ifndef WIN32 - sprintf(filename, "%s.%s", argv[2], encode ? (pfbflag ? "pfb" : "pfa") : "t1a" ); -#else /* WINDOWS */ - sprintf(filename, "%s.t1a", argv[2]); -#endif /* WINDOWS */ - if ((pfa_file = fopen(filename, "w+b")) == NULL) { - fprintf(stderr, "**** Cannot create %s ****\n", filename); - exit(1); - } else { - WARNING_2 fprintf(stderr, "Creating file %s\n", filename); - } + snprintf(filename, sizeof filename, "%s.%s", argv[2], encode ? (pfbflag ? "pfb" : "pfa") : "t1a" ); +#else /* WIN32 */ + snprintf(filename, sizeof filename, "%s.t1a", argv[2]); +#endif /* WIN32 */ + if(gen_pfa) { + if ((pfa_file = fopen(filename, "w+b")) == NULL) { + fprintf(stderr, "**** Cannot create %s ****\n", filename); + exit(1); + } else { + WARNING_2 fprintf(stderr, "Creating file %s\n", filename); + } + } else + pfa_file = null_file; - sprintf(filename, "%s.afm", argv[2]) ; - if ((afm_file = fopen(filename, "w+")) == NULL) { - fprintf(stderr, "**** Cannot create %s ****\n", filename); - exit(1); - } + if(gen_afm) { + snprintf(filename, sizeof filename, "%s.afm", argv[2]) ; + if ((afm_file = fopen(filename, "w+")) == NULL) { + fprintf(stderr, "**** Cannot create %s ****\n", filename); + exit(1); + } + } else + afm_file = null_file; + + if(gen_dvienc) { + snprintf(filename, sizeof filename, "%s.enc", argv[2]) ; + if ((dvienc_file = fopen(filename, "w+")) == NULL) { + fprintf(stderr, "**** Cannot create %s ****\n", filename); + exit(1); + } + } else + dvienc_file = null_file; } /* * Now check whether we want a fully encoded .pfa file */ #ifndef WIN32 - if (encode) { + if (encode && pfa_file != null_file) { int p[2]; extern FILE *ifp, *ofp; /* from t1asm.c */ @@ -1933,7 +2161,7 @@ ttf2pt1_main( fclose(ifp); fclose(ofp); } } -#endif /* WINDOWS */ +#endif /* WIN32 */ numglyphs = cursw->nglyphs(); @@ -2056,7 +2284,8 @@ ttf2pt1_main( fprintf(pfa_file, "%%!PS-AdobeFont-1.0: %s %s\n", fontm.name_ps, fontm.name_copyright); time(&now); fprintf(pfa_file, "%%%%CreationDate: %s", ctime(&now)); - fprintf(pfa_file, "%% Converted from TrueType font %s by ttf2pt1 %s/%s\n%%\n", argv[1], TTF2PT1_VERSION, cursw->name); + fprintf(pfa_file, "%% Converted by ttf2pt1 %s/%s\n", TTF2PT1_VERSION, cursw->name); + fprintf(pfa_file, "%% Args: %s\n", cmdline); fprintf(pfa_file, "%%%%EndComments\n"); fprintf(pfa_file, "12 dict begin\n/FontInfo 9 dict dup begin\n"); @@ -2160,12 +2389,19 @@ ttf2pt1_main( } fprintf(afm_file, "StartCharMetrics %d\n", nmetrics); + fprintf(dvienc_file, "/%s%sEncoding [\n", + fontm.name_ps, uni_font_name_suffix); + for (i = 0; i < 256; i++) { /* here 256, not ENCTABSZ */ fprintf(pfa_file, "dup %d /%s put\n", i, glyph_list[encoding[i]].name); if( glyph_list[encoding[i]].flags & GF_USED ) { print_glyph_metrics(i, encoding[i]); } + if (encoding[i]) + fprintf (dvienc_file, "/index0x%04X\n", encoding[i]); + else + fprintf (dvienc_file, "/.notdef\n"); } /* print the metrics for glyphs not in encoding table */ @@ -2188,7 +2424,8 @@ ttf2pt1_main( if(strUID) fprintf(pfa_file, "/UniqueID %s def\n", strUID); else - fprintf(pfa_file, "/UniqueID %lu def\n", numUID); + /* the range for private UIDs is 4 000 000 - 4 999 999 */ + fprintf(pfa_file, "/UniqueID %lu def\n", numUID%1000000+4000000); } if(forcebold==0) @@ -2241,21 +2478,23 @@ ttf2pt1_main( /* our sub to make the hint substitution code shorter */ fprintf(pfa_file, "dup 4 {\n\t1 3 callothersubr pop callsubr return\n\t} NP\n"); - /* print the hinting subroutines */ - subid=5; - for (i = 0; i < numglyphs; i++) { - if (glyph_list[i].flags & GF_USED) { - subid+=print_glyph_subs(i, subid); + if(pfa_file != null_file) { /* save time if the output would be wasted */ + /* print the hinting subroutines */ + subid=5; + for (i = 0; i < numglyphs; i++) { + if (glyph_list[i].flags & GF_USED) { + subid+=print_glyph_subs(i, subid); + } } - } - fprintf(pfa_file, "ND\n"); + fprintf(pfa_file, "ND\n"); - fprintf(pfa_file, "2 index /CharStrings %d dict dup begin\n", nchars); + fprintf(pfa_file, "2 index /CharStrings %d dict dup begin\n", nchars); - for (i = 0; i < numglyphs; i++) { - if (glyph_list[i].flags & GF_USED) { - print_glyph(i); + for (i = 0; i < numglyphs; i++) { + if (glyph_list[i].flags & GF_USED) { + print_glyph(i); + } } } @@ -2265,16 +2504,24 @@ ttf2pt1_main( fprintf(pfa_file, "dup/FontName get exch definefont pop\n"); fprintf(pfa_file, "mark currentfile closefile\n"); fprintf(pfa_file, "cleartomark\n"); - fclose(pfa_file); + if(pfa_file != null_file) + fclose(pfa_file); fprintf(afm_file, "EndCharMetrics\n"); - /* print the kerning data if present */ - cursw->kerning(glyph_list); - print_kerning(afm_file); + if(afm_file != null_file) { /* save time if the output would be wasted */ + /* print the kerning data if present */ + cursw->kerning(glyph_list); + print_kerning(afm_file); + } fprintf(afm_file, "EndFontMetrics\n"); - fclose(afm_file); + if(afm_file != null_file) + fclose(afm_file); + + fprintf(dvienc_file, "] def\n"); + if(dvienc_file != null_file) + fclose(dvienc_file); WARNING_1 fprintf(stderr, "Finished - font files created\n"); @@ -2284,10 +2531,10 @@ ttf2pt1_main( while (wait(&ws) > 0) { } #else - if (encode) { + if (encode && pfa_file != null_file) { extern FILE *ifp, *ofp; /* from t1asm.c */ - sprintf(filename, "%s.%s", argv[2], pfbflag ? "pfb" : "pfa" ); + snprintf(filename, sizeof filename, "%s.%s", argv[2], pfbflag ? "pfb" : "pfa" ); if ((ofp = fopen(filename, "w+b")) == NULL) { fprintf(stderr, "**** Cannot create %s ****\n", filename); @@ -2296,7 +2543,7 @@ ttf2pt1_main( WARNING_2 fprintf(stderr, "Creating file %s\n", filename); } - sprintf(filename, "%s.t1a", argv[2]); + snprintf(filename, sizeof filename, "%s.t1a", argv[2]); if ((ifp = fopen(filename, "rb")) == NULL) { fprintf(stderr, "**** Cannot read %s ****\n", filename); @@ -2311,7 +2558,8 @@ ttf2pt1_main( if(unlink(filename) < 0) WARNING_1 fprintf(stderr, "Unable to remove file %s\n", filename); } -#endif /* WINDOWS */ +#endif /* WIN32 */ + fclose(null_file); return 0; } diff --git a/pdf2swf/ttf2pt1/version.h b/pdf2swf/ttf2pt1/version.h index 70827df..a376d23 100644 --- a/pdf2swf/ttf2pt1/version.h +++ b/pdf2swf/ttf2pt1/version.h @@ -4,4 +4,4 @@ /* version number */ -#define TTF2PT1_VERSION "3.3.5" +#define TTF2PT1_VERSION "3.4.3"