diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/psread.c')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/psread.c | 4123 |
1 files changed, 4123 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/psread.c b/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/psread.c new file mode 100644 index 00000000000..c5e4958d0f5 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/psread.c @@ -0,0 +1,4123 @@ +/* Copyright (C) 2000-2008 by George Williams */ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + + * The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "pfaedit.h" +#include <math.h> +#include <locale.h> +#include <ustring.h> +#include <utype.h> +#include "psfont.h" +#include "sd.h" +#include "views.h" /* For CharViewBase */ +#ifdef HAVE_IEEEFP_H +# include <ieeefp.h> /* Solaris defines isnan in ieeefp rather than math.h */ +#endif + +typedef struct _io { + char *macro, *start; + FILE *ps, *fog; + char fogbuf[60]; + int backedup, cnt, isloop, isstopped, fogns; + struct _io *prev; +} _IO; + +typedef struct io { + struct _io *top; + int endedstopped; + int advance_width; /* Can be set from a PS comment by MF2PT1 */ +} IO; + +typedef struct growbuf { + char *pt; + char *base; + char *end; +} GrowBuf; + +#define GARBAGE_MAX 64 +struct garbage { + int cnt; + struct garbage *next; + struct pskeyval *entries[GARBAGE_MAX]; + int16 cnts[GARBAGE_MAX]; +}; + +static void GrowBuffer(GrowBuf *gb,int len) { + if ( len<400 ) len = 400; + if ( gb->base==NULL ) { + gb->base = gb->pt = galloc(len); + gb->end = gb->base + len; + } else { + int off = gb->pt-gb->base; + len += (gb->end-gb->base); + gb->base = grealloc(gb->base,len); + gb->end = gb->base + len; + gb->pt = gb->base+off; + } +} + +static void AddTok(GrowBuf *gb,char *buf,int islit) { + int len = islit + strlen(buf) + 1; + + if ( gb->pt+len+1 >= gb->end ) + GrowBuffer(gb,len+1); + if ( islit ) + *(gb->pt++) = '/'; + strcpy(gb->pt,buf); + gb->pt += strlen(buf); + *gb->pt++ = ' '; +} + +static struct pskeyval *lookup(struct pskeydict *dict,char *tokbuf) { + int i; + + for ( i=0; i<dict->cnt; ++i ) + if ( strcmp(dict->entries[i].key,tokbuf)==0 ) +return( &dict->entries[i] ); + +return( NULL ); +} + +static void dictfree(struct pskeydict *dict) { + int i; + + for ( i=0; i<dict->cnt; ++i ) { + if ( dict->entries[i].type==ps_string || dict->entries[i].type==ps_instr || + dict->entries[i].type==ps_lit ) + free(dict->entries[i].u.str); + else if ( dict->entries[i].type==ps_array || dict->entries[i].type==ps_dict ) + dictfree(&dict->entries[i].u.dict); + } +} + +static void garbagefree(struct garbage *all) { + struct garbage *junk, *next; + int i,j; + + for ( junk = all; junk!=NULL; junk = next ) { + next = junk->next; + for ( j=0; j<junk->cnt; ++j ) { + for ( i=0; i<junk->cnts[j]; ++i ) { + if ( junk->entries[j][i].type==ps_string || junk->entries[j][i].type==ps_instr || + junk->entries[j][i].type==ps_lit ) + free(junk->entries[j][i].u.str); + } + free(junk->entries[j]); + } + if ( junk!=all ) + chunkfree(junk,sizeof(struct garbage)); + } +} +/**************************** Postscript Importer *****************************/ +/* It's really dumb. It ignores almost everything except linetos and curvetos */ +/* anything else, function calls, ... is thrown out, if this breaks a lineto */ +/* or curveto or moveto (if there aren't enough things on the stack) then we */ +/* ignore that too */ + +enum pstoks { pt_eof=-1, pt_moveto, pt_rmoveto, pt_curveto, pt_rcurveto, + pt_lineto, pt_rlineto, pt_arc, pt_arcn, pt_arct, pt_arcto, + pt_newpath, pt_closepath, pt_dup, pt_pop, pt_index, + pt_exch, pt_roll, pt_clear, pt_copy, pt_count, + pt_setcachedevice, pt_setcharwidth, + pt_translate, pt_scale, pt_rotate, pt_concat, pt_end, pt_exec, + pt_add, pt_sub, pt_mul, pt_div, pt_idiv, pt_mod, pt_neg, + pt_abs, pt_round, pt_ceiling, pt_floor, pt_truncate, pt_max, pt_min, + pt_ne, pt_eq, pt_gt, pt_ge, pt_lt, pt_le, pt_and, pt_or, pt_xor, pt_not, + pt_exp, pt_sqrt, pt_ln, pt_log, pt_atan, pt_sin, pt_cos, + pt_true, pt_false, + pt_if, pt_ifelse, pt_for, pt_loop, pt_repeat, pt_exit, + pt_stopped, pt_stop, + pt_def, pt_bind, pt_load, + pt_setlinecap, pt_setlinejoin, pt_setlinewidth, pt_setdash, + pt_currentlinecap, pt_currentlinejoin, pt_currentlinewidth, pt_currentdash, + pt_setgray, pt_currentgray, pt_sethsbcolor, pt_currenthsbcolor, + pt_setrgbcolor, pt_currentrgbcolor, pt_setcmykcolor, pt_currentcmykcolor, + pt_currentpoint, + pt_fill, pt_stroke, pt_clip, + + pt_imagemask, + + pt_transform, pt_itransform, pt_dtransform, pt_idtransform, + + /* things we sort of pretend to do, but actually do something wrong */ + pt_gsave, pt_grestore, pt_save, pt_restore, pt_currentmatrix, pt_setmatrix, + pt_null, + + pt_currentflat, pt_setflat, + pt_currentglobal, pt_setglobal, + pt_currentmiterlimit, pt_setmiterlimit, + pt_currentobjectformat, pt_setobjectformat, + pt_currentoverprint, pt_setoverprint, + pt_currentpacking, pt_setpacking, + pt_currentshared, + pt_currentsmoothness, pt_setsmoothness, + pt_currentstrokeadjust, pt_setstrokeadjust, + + pt_mark, pt_counttomark, pt_cleartomark, pt_array, pt_aload, pt_astore, + pt_print, pt_cvi, pt_cvlit, pt_cvn, pt_cvr, pt_cvrs, pt_cvs, pt_cvx, pt_stringop, + + pt_opencurly, pt_closecurly, pt_openarray, pt_closearray, pt_string, + pt_number, pt_unknown, pt_namelit, pt_output, pt_outputd }; + +static char *toknames[] = { "moveto", "rmoveto", "curveto", "rcurveto", + "lineto", "rlineto", "arc", "arcn", "arct", "arcto", + "newpath", "closepath", "dup", "pop", "index", + "exch", "roll", "clear", "copy", "count", + "setcachedevice", "setcharwidth", + "translate", "scale", "rotate", "concat", "end", "exec", + "add", "sub", "mul", "div", "idiv", "mod", "neg", + "abs", "round", "ceiling", "floor", "truncate", "max", "min", + "ne", "eq", "gt", "ge", "lt", "le", "and", "or", "xor", "not", + "exp", "sqrt", "ln", "log", "atan", "sin", "cos", + "true", "false", + "if", "ifelse", "for", "loop", "repeat", "exit", + "stopped", "stop", + "def", "bind", "load", + "setlinecap", "setlinejoin", "setlinewidth", "setdash", + "currentlinecap", "currentlinejoin", "currentlinewidth", "currentdash", + "setgray", "currentgray", "sethsbcolor", "currenthsbcolor", + "setrgbcolor", "currentrgbcolor", "setcmykcolor", "currentcmykcolor", + "currentpoint", + "fill", "stroke", "clip", + + "imagemask", + + "transform", "itransform", "dtransform", "idtransform", + + "gsave", "grestore", "save", "restore", "currentmatrix", "setmatrix", + "null", + + "currentflat", "setflat", + "currentglobal", "setglobal", + "currentmiterlimit", "setmiterlimit", + "currentobjectformat", "setobjectformat", + "currentoverprint", "setoverprint", + "currentpacking", "setpacking", + "currentshared", + "currentsmoothness", "setsmoothness", + "currentstrokeadjust", "setstrokeadjust", + + "mark", "counttomark", "cleartomark", "array", "aload", "astore", + "print", "cvi", "cvlit", "cvn", "cvr", "cvrs", "cvs", "cvx", "string", + + "opencurly", "closecurly", "openarray", "closearray", "string", + "number", "unknown", "namelit", "=", "==", + + NULL }; + +/* length (of string) + fill eofill stroke + gsave grestore +*/ + +static int getfoghex(_IO *io) { + int ch,val; + + while ( isspace( ch = getc(io->fog))); + if ( isdigit(ch)) + val = ch-'0'; + else if ( ch >= 'A' && ch <= 'F' ) + val = ch-'A'+10; + else if ( ch >= 'a' && ch <= 'f' ) + val = ch-'a'+10; + else +return(EOF); + + val <<= 4; + while ( isspace( ch = getc(io->fog))); + if ( isdigit(ch)) + val |= ch-'0'; + else if ( ch >= 'A' && ch <= 'F' ) + val |= ch-'A'+10; + else if ( ch >= 'a' && ch <= 'f' ) + val |= ch-'a'+10; + else +return(EOF); + +return( val ); +} + +static int nextch(IO *wrapper) { + int ch; + _IO *io = wrapper->top; +/* This works for fog 4.1. Fonts generated by 2.4 seem to use a different */ +/* vector, and a different number parsing scheme */ + static char *foguvec[]= { "moveto ", "rlineto ", "rrcurveto ", " ", " ", + "Cache ", "10 div setlinewidth ", "ShowInt ", " ", " ", " ", " ", + "FillStroke ", " ", " ", "SetWid ", "100 mul add ", "togNS_ ", + " ", "closepath ", " ", "SG " }; + + while ( io!=NULL ) { + if ( io->backedup!=EOF ) { + ch = io->backedup; + io->backedup = EOF; +return( ch ); + } else if ( io->ps!=NULL ) { + if ( (ch = getc(io->ps))!=EOF ) +return( ch ); + } else if ( io->fog!=NULL ) { + if ( io->macro!=NULL && *io->macro!='\0' ) +return( *(io->macro++) ); + ch = getfoghex(io); + if ( ch>=233 ) { + io->macro = foguvec[ch-233]; +return( *(io->macro++) ); + } else if ( ch!=EOF && ch<200 ) { + sprintf( io->fogbuf, "%d ", ch-100); + io->macro=io->fogbuf; +return( *(io->macro++) ); + } else if (ch!=EOF) { + sprintf( io->fogbuf, "%d %s ", ch-233+17, io->fogns + ? "2 exch exp 3 1 roll 100 mul add mul" + : "100 mul add" ); + io->macro=io->fogbuf; +return( *(io->macro++) ); + } + } else { + if ( (ch = *(io->macro++))!='\0' ) +return( ch ); + if ( --io->cnt>0 ) { + io->macro = io->start; +return( nextch(wrapper)); + } + } + wrapper->top = io->prev; + if ( io->isstopped ) + wrapper->endedstopped = true; + free(io->start); + free(io); + io = wrapper->top; + } +return( EOF ); +} + +static void unnextch(int ch,IO *wrapper) { + if ( ch==EOF ) +return; + if ( wrapper->top==NULL ) + LogError( _("Can't back up with nothing on stack\n") ); + else if ( wrapper->top->backedup!=EOF ) + LogError( _("Attempt to back up twice\n") ); + else if ( wrapper->top->ps!=NULL ) + ungetc(ch,wrapper->top->ps); + else + wrapper->top->backedup = ch; +} + +static void pushio(IO *wrapper, FILE *ps, char *macro, int cnt) { + _IO *io = gcalloc(1,sizeof(_IO)); + + io->prev = wrapper->top; + io->ps = ps; + io->macro = io->start = copy(macro); + io->backedup = EOF; + if ( cnt==-1 ) { + io->cnt = 1; + io->isstopped = true; + } else if ( cnt==0 ) { + io->cnt = 1; + io->isloop = false; + } else { + io->cnt = cnt; + io->isloop = true; + } + wrapper->top = io; +} + +static void pushfogio(IO *wrapper, FILE *fog) { + _IO *io = gcalloc(1,sizeof(_IO)); + + io->prev = wrapper->top; + io->fog = fog; + io->backedup = EOF; + io->cnt = 1; + io->isloop = false; + wrapper->top = io; +} + +static void ioescapeloop(IO *wrapper) { + _IO *io = wrapper->top, *iop; + int wasloop; + + while ( io->prev!=NULL && !io->isstopped ) { + iop = io->prev; + wasloop = io->isloop; + free(io->start); + free(io); + if ( wasloop ) { + wrapper->top = iop; +return; + } + io = iop; + } + +/* GT: This is part of the PostScript language. "exit" should not be translated */ +/* GT: as it is a PostScript keyword. (FF contains a small PostScript interpreter */ +/* GT: so it can understand some PostScript fonts, and can generate errors when */ +/* GT: handed bad PostScript). */ + LogError( _("Use of \"exit\" when not in a loop\n") ); + wrapper->top = io; +} + +static int ioescapestopped(IO *wrapper, struct psstack *stack, int sp) { + _IO *io = wrapper->top, *iop; + int wasstopped; + + while ( io->prev!=NULL ) { + iop = io->prev; + wasstopped = io->isstopped; + free(io->start); + free(io); + if ( wasstopped ) { + wrapper->top = iop; + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_bool; + stack[sp++].u.tf = true; + } +return(sp); + } + io = iop; + } + +/* GT: This is part of the PostScript language. Neither "stop" nor "stopped" */ +/* GT: should be translated as both are PostScript keywords. */ + LogError( _("Use of \"stop\" when not in a stopped\n") ); + wrapper->top = io; +return( sp ); +} + +static int endedstopped(IO *wrapper) { + if ( wrapper->endedstopped ) { + wrapper->endedstopped = false; +return( true ); + } +return( false ); +} + + +static int nextpstoken(IO *wrapper, real *val, char *tokbuf, int tbsize) { + int ch, r, i; + char *pt, *end; + float mf2pt_advance_width; + + pt = tokbuf; + end = pt+tbsize-1; + + /* Eat whitespace and comments. Comments last to eol (or formfeed) */ + while ( 1 ) { + while ( isspace(ch = nextch(wrapper)) ); + if ( ch!='%' ) + break; + while ( (ch=nextch(wrapper))!=EOF && ch!='\r' && ch!='\n' && ch!='\f' ) + if ( pt<end ) + *pt++ = ch; + *pt='\0'; + /* Some comments have meanings (that we care about) */ + if ( sscanf( tokbuf, " MF2PT1: bbox %*g %*g %g %*g", &mf2pt_advance_width )==1 ) + wrapper->advance_width = mf2pt_advance_width; + else if ( sscanf( tokbuf, " MF2PT1: glyph_dimensions %*g %*g %g %*g", &mf2pt_advance_width )==1 ) + wrapper->advance_width = mf2pt_advance_width; + pt = tokbuf; + } + + if ( ch==EOF ) +return( pt_eof ); + + pt = tokbuf; + end = pt+tbsize-1; + *pt++ = ch; *pt='\0'; + + if ( ch=='(' ) { + int nest=1, quote=0; + while ( (ch=nextch(wrapper))!=EOF ) { + if ( pt<end ) *pt++ = ch; + if ( quote ) + quote=0; + else if ( ch=='(' ) + ++nest; + else if ( ch==')' ) { + if ( --nest==0 ) + break; + } else if ( ch=='\\' ) + quote = 1; + } + *pt='\0'; +return( pt_string ); + } else if ( ch=='<' ) { + ch = nextch(wrapper); + if ( pt<end ) *pt++ = ch; + if ( ch=='>' ) + /* Done */; + else if ( ch!='~' ) { + while ( (ch=nextch(wrapper))!=EOF && ch!='>' ) + if ( pt<end ) *pt++ = ch; + } else { + int twiddle=0; + while ( (ch=nextch(wrapper))!=EOF ) { + if ( pt<end ) *pt++ = ch; + if ( ch=='~' ) twiddle = 1; + else if ( twiddle && ch=='>' ) + break; + else twiddle = 0; + } + } + *pt='\0'; +return( pt_string ); + } else if ( ch==')' || ch=='>' || ch=='[' || ch==']' || ch=='{' || ch=='}' ) { + if ( ch=='{' ) +return( pt_opencurly ); + else if ( ch=='}' ) +return( pt_closecurly ); + if ( ch=='[' ) +return( pt_openarray ); + else if ( ch==']' ) +return( pt_closearray ); + +return( pt_unknown ); /* single character token */ + } else if ( ch=='/' ) { + pt = tokbuf; + while ( (ch=nextch(wrapper))!=EOF && !isspace(ch) && ch!='%' && + ch!='(' && ch!=')' && ch!='<' && ch!='>' && ch!='[' && ch!=']' && + ch!='{' && ch!='}' && ch!='/' ) + if ( pt<tokbuf+tbsize-2 ) + *pt++ = ch; + *pt = '\0'; + unnextch(ch,wrapper); +return( pt_namelit ); /* name literal */ + } else { + while ( (ch=nextch(wrapper))!=EOF && !isspace(ch) && ch!='%' && + ch!='(' && ch!=')' && ch!='<' && ch!='>' && ch!='[' && ch!=']' && + ch!='{' && ch!='}' && ch!='/' ) { + if ( pt<tokbuf+tbsize-2 ) + *pt++ = ch; + } + *pt = '\0'; + unnextch(ch,wrapper); + r = strtol(tokbuf,&end,10); + pt = end; + if ( *pt=='\0' ) { /* It's a normal integer */ + *val = r; +return( pt_number ); + } else if ( *pt=='#' ) { + r = strtol(pt+1,&end,r); + if ( *end=='\0' ) { /* It's a radix integer */ + *val = r; +return( pt_number ); + } + } else { + *val = strtod(tokbuf,&end); + if ( !finite(*val) ) { +/* GT: NaN is a concept in IEEE floating point which means "Not a Number" */ +/* GT: it is used to represent errors like 0/0 or sqrt(-1). */ + LogError( _("Bad number, infinity or nan: %s\n"), tokbuf ); + *val = 0; + } + if ( *end=='\0' ) /* It's a real */ +return( pt_number ); + } + /* It's not a number */ + for ( i=0; toknames[i]!=NULL; ++i ) + if ( strcmp(tokbuf,toknames[i])==0 ) +return( i ); + +return( pt_unknown ); + } +} + +static void Transform(BasePoint *to, DBasePoint *from, real trans[6]) { + to->x = trans[0]*from->x+trans[2]*from->y+trans[4]; + to->y = trans[1]*from->x+trans[3]*from->y+trans[5]; +} + +void MatMultiply(real m1[6], real m2[6], real to[6]) { + real trans[6]; + + trans[0] = m1[0]*m2[0] + + m1[1]*m2[2]; + trans[1] = m1[0]*m2[1] + + m1[1]*m2[3]; + trans[2] = m1[2]*m2[0] + + m1[3]*m2[2]; + trans[3] = m1[2]*m2[1] + + m1[3]*m2[3]; + trans[4] = m1[4]*m2[0] + + m1[5]*m2[2] + + m2[4]; + trans[5] = m1[4]*m2[1] + + m1[5]*m2[3] + + m2[5]; + memcpy(to,trans,sizeof(trans)); +} + +void MatInverse(real into[6], real orig[6]) { + real det = orig[0]*orig[3] - orig[1]*orig[2]; + + if ( det==0 ) { + LogError( _("Attempt to invert a singular matrix\n") ); + memset(into,0,sizeof(*into)); + } else { + into[0] = orig[3]/det; + into[1] = -orig[1]/det; + into[2] = -orig[2]/det; + into[3] = orig[0]/det; + into[4] = -orig[4]*into[0] - orig[5]*into[2]; + into[5] = -orig[4]*into[1] - orig[5]*into[3]; + } +} + +static void ECCatagorizePoints( EntityChar *ec ) { + Entity *ent; + + for ( ent=ec->splines; ent!=NULL; ent=ent->next ) if ( ent->type == et_splines ) { + SPLCatagorizePoints( ent->u.splines.splines ); + SPLCatagorizePoints( ent->clippath ); + } +} + +static int AddEntry(struct pskeydict *dict,struct psstack *stack, int sp) { + int i; + + if ( dict->cnt>=dict->max ) { + if ( dict->cnt==0 ) { + dict->max = 30; + dict->entries = galloc(dict->max*sizeof(struct pskeyval)); + } else { + dict->max += 30; + dict->entries = grealloc(dict->entries,dict->max*sizeof(struct pskeyval)); + } + } + if ( sp<2 ) +return(sp); + if ( stack[sp-2].type!=ps_string && stack[sp-2].type!=ps_lit ) { +/* GT: Here "def" is a PostScript keyword, (meaning define). */ +/* GT: This "def" should not be translated as it is part of the PostScript language. */ + LogError( _("Key for a def must be a string or name literal\n") ); +return(sp-2); + } + for ( i=0; i<dict->cnt; ++i ) + if ( strcmp(dict->entries[i].key,stack[sp-2].u.str)==0 ) + break; + if ( i!=dict->cnt ) { + free(stack[sp-2].u.str); + if ( dict->entries[i].type==ps_string || dict->entries[i].type==ps_instr || + dict->entries[i].type==ps_lit ) + free(dict->entries[i].u.str); + } else { + memset(&dict->entries[i],'\0',sizeof(struct pskeyval)); + dict->entries[i].key = stack[sp-2].u.str; + ++dict->cnt; + } + dict->entries[i].type = stack[sp-1].type; + dict->entries[i].u = stack[sp-1].u; +return(sp-2); +} + +static int forgetstack(struct psstack *stack, int forgets, int sp) { + /* forget the bottom most "forgets" entries on the stack */ + /* we presume they are garbage that has accumulated because we */ + /* don't understand all of PS */ + int i; + for ( i=0; i<forgets; ++i ) { + if ( stack[i].type==ps_string || stack[i].type==ps_instr || + stack[i].type==ps_lit ) + free(stack[i].u.str); + else if ( stack[i].type==ps_array || stack[i].type==ps_dict ) + dictfree(&stack[i].u.dict); + } + for ( i=forgets; i<sp; ++i ) + stack[i-forgets] = stack[i]; +return( sp-forgets ); +} + +static int rollstack(struct psstack *stack, int sp) { + int n,j,i; + struct psstack *temp; + + if ( sp>1 ) { + n = stack[sp-2].u.val; + j = stack[sp-1].u.val; + sp-=2; + if ( sp>=n && n>0 ) { + j %= n; + if ( j<0 ) j += n; + temp = galloc(n*sizeof(struct psstack)); + for ( i=0; i<n; ++i ) + temp[i] = stack[sp-n+i]; + for ( i=0; i<n; ++i ) + stack[sp-n+(i+j)%n] = temp[i]; + free(temp); + } + } +return( sp ); +} + +static void CheckMakeB(BasePoint *test, BasePoint *good) { + if ( !finite(test->x) || test->x>100000 || test->x<-100000 ) { + LogError( _("Value out of bounds in spline.\n") ); + if ( good!=NULL ) + test->x = good->x; + else + test->x = 0; + } + if ( !finite(test->y) || test->y>100000 || test->y<-100000 ) { + LogError( _("Value out of bounds in spline.\n") ); + if ( good!=NULL ) + test->y = good->y; + else + test->y = 0; + } +} + +static void CheckMake(SplinePoint *from, SplinePoint *to) { + CheckMakeB(&from->me,NULL); + CheckMakeB(&from->nextcp,&from->me); + CheckMakeB(&to->prevcp,&from->nextcp); + CheckMakeB(&to->me,&to->prevcp); +} + +static void circlearcto(real a1, real a2, real cx, real cy, real r, + SplineSet *cur, real *transform ) { + SplinePoint *pt; + DBasePoint temp, base, cp; + real cplen; + int sign=1; + real s1, s2, c1, c2; + + if ( a1==a2 ) +return; + + cplen = (a2-a1)/90 * r * .552; + a1 *= 3.1415926535897932/180; a2 *= 3.1415926535897932/180; + s1 = sin(a1); s2 = sin(a2); c1 = cos(a1); c2 = cos(a2); + temp.x = cx+r*c2; temp.y = cy+r*s2; + base.x = cx+r*c1; base.y = cy+r*s1; + pt = chunkalloc(sizeof(SplinePoint)); + Transform(&pt->me,&temp,transform); + cp.x = temp.x-cplen*s2; cp.y = temp.y + cplen*c2; + if ( (cp.x-base.x)*(cp.x-base.x)+(cp.y-base.y)*(cp.y-base.y) > + (temp.x-base.x)*(temp.x-base.x)+(temp.y-base.y)*(temp.y-base.y) ) { + sign = -1; + cp.x = temp.x+cplen*s2; cp.y = temp.y - cplen*c2; + } + Transform(&pt->prevcp,&cp,transform); + pt->nonextcp = true; + cp.x = base.x + sign*cplen*s1; cp.y = base.y - sign*cplen*c1; + Transform(&cur->last->nextcp,&cp,transform); + cur->last->nonextcp = false; + CheckMake(cur->last,pt); + SplineMake3(cur->last,pt); + cur->last = pt; +} + +static void circlearcsto(real a1, real a2, real cx, real cy, real r, + SplineSet *cur, real *transform, int clockwise ) { + int a; + real last; + + while ( a1<0 ) { a1 += 360; a2 +=360;} while ( a2-a1<=-360 ) a2 += 360; + while ( a1>360 ) { a1 -= 360; a2 -= 360; } while ( a2-a1>360 ) a2 -= 360; + if ( !clockwise ) { + if ( a1>a2 ) + a2 += 360; + last = a1; + for ( a=((int) (a1+90)/90)*90; a<a2; a += 90 ) { + circlearcto(last,a,cx,cy,r,cur,transform); + last = a; + } + circlearcto(last,a2,cx,cy,r,cur,transform); + } else { + if ( a2>a1 ) + a1 += 360; + last = a1; + for ( a=((int) (a1-90)/90)*90+90; a>a2; a -= 90 ) { + circlearcto(last,a,cx,cy,r,cur,transform); + last = a; + } + circlearcto(last,a2,cx,cy,r,cur,transform); + } +} + +static void collectgarbage(struct garbage *tofrees,struct pskeydict *to) { + struct garbage *into; + + /* Garbage collection pointers */ + into = tofrees; + if ( tofrees->cnt>=GARBAGE_MAX && tofrees->next!=NULL ) + into = tofrees->next; + if ( into->cnt>=GARBAGE_MAX ) { + into = chunkalloc(sizeof(struct garbage)); + into->next = tofrees->next; + tofrees->next = into; + } + into->cnts[ into->cnt ] = to->cnt; + into->entries[ into->cnt++ ] = to->entries; +} + +static void copyarray(struct pskeydict *to,struct pskeydict *from, struct garbage *tofrees) { + int i; + struct pskeyval *oldent = from->entries; + + *to = *from; + to->entries = gcalloc(to->cnt,sizeof(struct pskeyval)); + for ( i=0; i<to->cnt; ++i ) { + to->entries[i] = oldent[i]; + if ( to->entries[i].type==ps_string || to->entries[i].type==ps_instr || + to->entries[i].type==ps_lit ) + to->entries[i].u.str = copy(to->entries[i].u.str); + else if ( to->entries[i].type==ps_array || to->entries[i].type==ps_dict ) + copyarray(&to->entries[i].u.dict,&oldent[i].u.dict,tofrees); + } + collectgarbage(tofrees,to); +} + +static int aload(int sp, struct psstack *stack,int stacktop, struct garbage *tofrees) { + int i; + + if ( sp>=1 && stack[sp-1].type==ps_array ) { + struct pskeydict dict; + --sp; + dict = stack[sp].u.dict; + for ( i=0; i<dict.cnt; ++i ) { + if ( sp<stacktop ) { + stack[sp].type = dict.entries[i].type; + stack[sp].u = dict.entries[i].u; + if ( stack[sp].type==ps_string || stack[sp].type==ps_instr || + stack[sp].type==ps_lit ) + stack[sp].u.str = copy(stack[sp].u.str); +/* The following is incorrect behavior, but as I don't do garbage collection */ +/* and I'm not going to implement reference counts, this will work in most cases */ + else if ( stack[sp].type==ps_array ) + copyarray(&stack[sp].u.dict,&stack[sp].u.dict,tofrees); + ++sp; + } + } + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_array; + stack[sp].u.dict = dict; + ++sp; + } + } +return( sp ); +} + +static void printarray(struct pskeydict *dict) { + int i; + + printf("[" ); + for ( i=0; i<dict->cnt; ++i ) { + switch ( dict->entries[i].type ) { + case ps_num: + printf( "%g", (double) dict->entries[i].u.val ); + break; + case ps_bool: + printf( "%s", dict->entries[i].u.tf ? "true" : "false" ); + break; + case ps_string: case ps_instr: case ps_lit: + printf( dict->entries[i].type==ps_lit ? "/" : + dict->entries[i].type==ps_string ? "(" : "{" ); + printf( "%s", dict->entries[i].u.str ); + printf( dict->entries[i].type==ps_lit ? "" : + dict->entries[i].type==ps_string ? ")" : "}" ); + break; + case ps_array: + printarray(&dict->entries[i].u.dict); + break; + case ps_void: + printf( "-- void --" ); + break; + default: + printf( "-- nostringval --" ); + break; + } + printf(" "); + } + printf( "]" ); +} + +static void freestuff(struct psstack *stack, int sp, struct pskeydict *dict, + GrowBuf *gb, struct garbage *tofrees) { + int i; + + free(gb->base); + for ( i=0; i<dict->cnt; ++i ) { + if ( dict->entries[i].type==ps_string || dict->entries[i].type==ps_instr || + dict->entries[i].type==ps_lit ) + free(dict->entries[i].u.str); + free(dict->entries[i].key); + } + free( dict->entries ); + for ( i=0; i<sp; ++i ) { + if ( stack[i].type==ps_string || stack[i].type==ps_instr || + stack[i].type==ps_lit ) + free(stack[i].u.str); +#if 0 /* Garbage collection should get these */ + else if ( stack[i].type==ps_array || stack[i].type==ps_dict ) + dictfree(&stack[i].u.dict); +#endif + } + garbagefree(tofrees); +} + +static void DoMatTransform(int tok,int sp,struct psstack *stack) { + real invt[6], t[6]; + + if ( stack[sp-1].u.dict.cnt==6 && stack[sp-1].u.dict.entries[0].type==ps_num ) { + double x = stack[sp-3].u.val, y = stack[sp-2].u.val; + --sp; + t[5] = stack[sp].u.dict.entries[5].u.val; + t[4] = stack[sp].u.dict.entries[4].u.val; + t[3] = stack[sp].u.dict.entries[3].u.val; + t[2] = stack[sp].u.dict.entries[2].u.val; + t[1] = stack[sp].u.dict.entries[1].u.val; + t[0] = stack[sp].u.dict.entries[0].u.val; + dictfree(&stack[sp].u.dict); + if ( tok==pt_itransform || tok==pt_idtransform ) { + MatInverse(invt,t); + memcpy(t,invt,sizeof(t)); + } + stack[sp-2].u.val = t[0]*x + t[1]*y; + stack[sp-1].u.val = t[2]*x + t[3]*y; + if ( tok==pt_transform || tok==pt_itransform ) { + stack[sp-2].u.val += t[4]; + stack[sp-1].u.val += t[5]; + } + } +} + +static int DoMatOp(int tok,int sp,struct psstack *stack) { + real temp[6], t[6]; + int nsp=sp; + + if ( stack[sp-1].u.dict.cnt==6 && stack[sp-1].u.dict.entries[0].type==ps_num ) { + t[5] = stack[sp-1].u.dict.entries[5].u.val; + t[4] = stack[sp-1].u.dict.entries[4].u.val; + t[3] = stack[sp-1].u.dict.entries[3].u.val; + t[2] = stack[sp-1].u.dict.entries[2].u.val; + t[1] = stack[sp-1].u.dict.entries[1].u.val; + t[0] = stack[sp-1].u.dict.entries[0].u.val; + switch ( tok ) { + case pt_translate: + if ( sp>=3 ) { + stack[sp-1].u.dict.entries[5].u.val += stack[sp-3].u.val*t[0]+stack[sp-2].u.val*t[2]; + stack[sp-1].u.dict.entries[4].u.val += stack[sp-3].u.val*t[1]+stack[sp-2].u.val*t[3]; + nsp = sp-2; + } + break; + case pt_scale: + if ( sp>=2 ) { + stack[sp-1].u.dict.entries[0].u.val *= stack[sp-3].u.val; + stack[sp-1].u.dict.entries[1].u.val *= stack[sp-3].u.val; + stack[sp-1].u.dict.entries[2].u.val *= stack[sp-2].u.val; + stack[sp-1].u.dict.entries[3].u.val *= stack[sp-2].u.val; + /* transform[4,5] are unchanged */ + nsp = sp-2; + } + break; + case pt_rotate: + if ( sp>=1 ) { + --sp; + temp[0] = temp[3] = cos(stack[sp].u.val); + temp[1] = sin(stack[sp].u.val); + temp[2] = -temp[1]; + temp[4] = temp[5] = 0; + MatMultiply(temp,t,t); + stack[sp-1].u.dict.entries[5].u.val = t[5]; + stack[sp-1].u.dict.entries[4].u.val = t[4]; + stack[sp-1].u.dict.entries[3].u.val = t[3]; + stack[sp-1].u.dict.entries[2].u.val = t[2]; + stack[sp-1].u.dict.entries[1].u.val = t[1]; + stack[sp-1].u.dict.entries[0].u.val = t[0]; + nsp = sp-1; + } + break; + } + stack[nsp-1] = stack[sp-1]; + } +return(nsp); +} + +static Entity *EntityCreate(SplinePointList *head,int linecap,int linejoin, + real linewidth, real *transform, SplineSet *clippath) { + Entity *ent = gcalloc(1,sizeof(Entity)); + ent->type = et_splines; + ent->u.splines.splines = head; + ent->u.splines.cap = linecap; + ent->u.splines.join = linejoin; + ent->u.splines.stroke_width = linewidth; + ent->u.splines.fill.col = 0xffffffff; + ent->u.splines.stroke.col = 0xffffffff; + ent->u.splines.fill.opacity = 1.0; + ent->u.splines.stroke.opacity = 1.0; + ent->clippath = SplinePointListCopy(clippath); + memcpy(ent->u.splines.transform,transform,6*sizeof(real)); +return( ent ); +} + + +static void HandleType3Reference(IO *wrapper,EntityChar *ec,real transform[6], + char *tokbuf, int toksize) { + int tok; + real dval; + char *glyphname; + RefChar *ref; + + tok = nextpstoken(wrapper,&dval,tokbuf,toksize); + if ( strcmp(tokbuf,"get")!=0 ) +return; /* Hunh. I don't understand it. I give up */ + tok = nextpstoken(wrapper,&dval,tokbuf,toksize); + if ( tok!=pt_namelit ) +return; /* Hunh. I don't understand it. I give up */ + glyphname = copy(tokbuf); + tok = nextpstoken(wrapper,&dval,tokbuf,toksize); + if ( strcmp(tokbuf,"get")!=0 ) +return; /* Hunh. I don't understand it. I give up */ + tok = nextpstoken(wrapper,&dval,tokbuf,toksize); + if ( strcmp(tokbuf,"exec")!=0 ) +return; /* Hunh. I don't understand it. I give up */ + + /* Ok, it looks very much like a reference to glyphname */ + ref = RefCharCreate(); + memcpy(ref->transform,transform,sizeof(ref->transform)); + ref->sc = (SplineChar *) glyphname; + ref->next = ec->refs; + ec->refs = ref; +} + +static void _InterpretPS(IO *wrapper, EntityChar *ec, RetStack *rs) { + SplinePointList *cur=NULL, *head=NULL; + DBasePoint current, temp; + int tok, i, j; + struct psstack stack[100]; + real dval; + int sp=0; + SplinePoint *pt; + RefChar *ref, *lastref=NULL; + real transform[6], t[6]; + struct graphicsstate { + real transform[6]; + DBasePoint current; + real linewidth; + int linecap, linejoin; + Color fore; + DashType dashes[DASH_MAX]; + SplineSet *clippath; + } gsaves[30]; + int gsp = 0; + int ccnt=0; + GrowBuf gb; + struct pskeydict dict; + struct pskeyval *kv; + Color fore=COLOR_INHERITED; + int linecap=lc_inherited, linejoin=lj_inherited; real linewidth=WIDTH_INHERITED; + DashType dashes[DASH_MAX]; + int dash_offset = 0; + Entity *ent; + char *oldloc; + int warned = 0; + struct garbage tofrees; + SplineSet *clippath = NULL; + char tokbuf[100]; + const int tokbufsize = 100; + + oldloc = setlocale(LC_NUMERIC,"C"); + + memset(&gb,'\0',sizeof(GrowBuf)); + memset(&dict,'\0',sizeof(dict)); + tofrees.cnt = 0; tofrees.next = NULL; + + transform[0] = transform[3] = 1.0; + transform[1] = transform[2] = transform[4] = transform[5] = 0; + current.x = current.y = 0; + dashes[0] = 0; dashes[1] = DASH_INHERITED; + + if ( ec->fromtype3 ) { + /* My type3 fonts have two things pushed on the stack when they */ + /* start. One is a dictionary, the other a flag (number). If the */ + /* flag is non-zero then we are a nested call (a reference char) */ + /* if 0, we're normal. We don't want to do setcachedevice for */ + /* reference chars. We can't represent a dictionary on the stack */ + /* so just push two 0s */ + stack[0].type = stack[1].type = ps_num; + stack[0].u.val = stack[1].u.val = 0; + sp = 2; + } + + while ( (tok = nextpstoken(wrapper,&dval,tokbuf,tokbufsize))!=pt_eof ) { + if ( endedstopped(wrapper)) { + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_bool; + stack[sp++].u.tf = false; + } + } + if ( sp>(int)(sizeof(stack)/sizeof(stack[0])*4/5) ) { + /* We don't interpret all of postscript */ + /* Sometimes we leave garbage on the stack that a real PS interp */ + /* would have handled. If the stack gets too deep, clean out the */ + /* oldest entries */ + sp = forgetstack(stack,sizeof(stack)/sizeof(stack[0])/3,sp ); + } + if ( ccnt>0 ) { + if ( tok==pt_closecurly ) + --ccnt; + else if ( tok==pt_opencurly ) + ++ccnt; + if ( ccnt>0 ) + AddTok(&gb,tokbuf,tok==pt_namelit); + else { + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_instr; + if ( gb.pt==NULL ) + stack[sp++].u.str = copy(""); + else { + *gb.pt = '\0'; gb.pt = gb.base; + stack[sp++].u.str = copy(gb.base); + } + } + } + } else if ( tok==pt_unknown && (kv=lookup(&dict,tokbuf))!=NULL ) { + if ( kv->type == ps_instr ) + pushio(wrapper,NULL,copy(kv->u.str),0); + else if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = kv->type; + stack[sp++].u = kv->u; + if ( kv->type==ps_instr || kv->type==ps_lit || kv->type==ps_string ) + stack[sp-1].u.str = copy(stack[sp-1].u.str); + else if ( kv->type==ps_array || kv->type==ps_dict ) { + copyarray(&stack[sp-1].u.dict,&stack[sp-1].u.dict,&tofrees); + if ( stack[sp-1].u.dict.is_executable ) + sp = aload(sp,stack,sizeof(stack)/sizeof(stack[0]),&tofrees); + } + } + } else { + if ( tok==pt_unknown ) { + if ( strcmp(tokbuf,"Cache")==0 ) /* Fontographer type3s */ + tok = pt_setcachedevice; + else if ( strcmp(tokbuf,"SetWid")==0 ) { + tok = pt_setcharwidth; + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_num; + stack[sp++].u.val = 0; + } + } else if ( strcmp(tokbuf,"rrcurveto")==0 ) { + if ( sp>=6 ) { + stack[sp-4].u.val += stack[sp-6].u.val; + stack[sp-3].u.val += stack[sp-5].u.val; + stack[sp-2].u.val += stack[sp-4].u.val; + stack[sp-1].u.val += stack[sp-3].u.val; + tok = pt_rcurveto; + } + } else if ( strcmp(tokbuf,"FillStroke")==0 ) { + if ( sp>0 ) + --sp; + tok = linewidth!=WIDTH_INHERITED ? pt_stroke : pt_fill; + if ( wrapper->top!=NULL && wrapper->top->ps!=NULL && + linewidth!=WIDTH_INHERITED ) + linewidth /= 10.0; /* bug in Fontographer's unencrypted type3 fonts */ + } else if ( strcmp(tokbuf,"SG")==0 ) { + if ( linewidth!=WIDTH_INHERITED && sp>1 ) + stack[sp-2].u.val = stack[sp-1].u.val; + if ( sp>0 ) + --sp; + if ( sp>0 ) + stack[sp-1].u.val = (stack[sp-1].u.val+99)/198.0; + tok = pt_setgray; + } else if ( strcmp(tokbuf,"ShowInt")==0 ) { + /* Fontographer reference */ + if ( (!wrapper->top->fogns && sp>0 && stack[sp-1].type == ps_num && + stack[sp-1].u.val>=0 && stack[sp-1].u.val<=255 ) || + (wrapper->top->fogns && sp>6 && stack[sp-7].type == ps_num && + stack[sp-7].u.val>=0 && stack[sp-7].u.val<=255 )) { + ref = RefCharCreate(); + memcpy(ref->transform,transform,sizeof(ref->transform)); + if ( wrapper->top->fogns ) { + sp -= 6; + t[0] = stack[sp+0].u.val; + t[1] = stack[sp+1].u.val; + t[2] = stack[sp+2].u.val; + t[3] = stack[sp+3].u.val; + t[4] = stack[sp+4].u.val; + t[5] = stack[sp+5].u.val; + MatMultiply(t,ref->transform,ref->transform); + wrapper->top->fogns = false; + } + ref->orig_pos = stack[--sp].u.val; + ref->next = ec->refs; + ec->refs = ref; + continue; + } + } else if ( strcmp(tokbuf,"togNS_")==0 ) { + wrapper->top->fogns = !wrapper->top->fogns; + continue; + } + } + switch ( tok ) { + case pt_number: + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_num; + stack[sp++].u.val = dval; + } + break; + case pt_string: + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_string; + stack[sp++].u.str = copyn(tokbuf+1,strlen(tokbuf)-2); + } + break; + case pt_true: case pt_false: + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_bool; + stack[sp++].u.tf = tok==pt_true; + } + break; + case pt_opencurly: + ++ccnt; + break; + case pt_closecurly: + --ccnt; + if ( ccnt<0 ) { + goto done; + } + break; + case pt_count: + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_num; + stack[sp].u.val = sp; + ++sp; + } + break; + case pt_pop: + if ( sp>0 ) { + --sp; + if ( stack[sp].type==ps_string || stack[sp].type==ps_instr || + stack[sp].type==ps_lit ) + free(stack[sp].u.str); + else if ( stack[sp].type==ps_array || stack[sp].type==ps_dict ) + dictfree(&stack[sp].u.dict); + } + break; + case pt_clear: + while ( sp>0 ) { + --sp; + if ( stack[sp].type==ps_string || stack[sp].type==ps_instr || + stack[sp].type==ps_lit ) + free(stack[sp].u.str); + else if ( stack[sp].type==ps_array || stack[sp].type==ps_dict ) + dictfree(&stack[sp].u.dict); + } + break; + case pt_dup: + if ( sp>0 && sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp] = stack[sp-1]; + if ( stack[sp].type==ps_string || stack[sp].type==ps_instr || + stack[sp].type==ps_lit ) + stack[sp].u.str = copy(stack[sp].u.str); + /* The following is incorrect behavior, but as I don't do garbage collection */ + /* and I'm not going to implement reference counts, this will work in most cases */ + else if ( stack[sp].type==ps_array ) + copyarray(&stack[sp].u.dict,&stack[sp].u.dict,&tofrees); + ++sp; + } + break; + case pt_copy: + if ( sp>0 ) { + int n = stack[--sp].u.val; + if ( n+sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + int i; + for ( i=0; i<n; ++i ) { + stack[sp] = stack[sp-n]; + if ( stack[sp].type==ps_string || stack[sp].type==ps_instr || + stack[sp].type==ps_lit ) + stack[sp].u.str = copy(stack[sp].u.str); + /* The following is incorrect behavior, but as I don't do garbage collection */ + /* and I'm not going to implement reference counts, this will work in most cases */ + else if ( stack[sp].type==ps_array ) + copyarray(&stack[sp].u.dict,&stack[sp].u.dict,&tofrees); + ++sp; + } + } + } + break; + case pt_exch: + if ( sp>1 ) { + struct psstack temp; + temp = stack[sp-1]; + stack[sp-1] = stack[sp-2]; + stack[sp-2] = temp; + } + break; + case pt_roll: + sp = rollstack(stack,sp); + break; + case pt_index: + if ( sp>0 ) { + i = stack[--sp].u.val; + if ( sp>i && i>=0 ) { + stack[sp] = stack[sp-i-1]; + if ( stack[sp].type==ps_string || stack[sp].type==ps_instr || + stack[sp].type==ps_lit ) + stack[sp].u.str = copy(stack[sp].u.str); + /* The following is incorrect behavior, but as I don't do garbage collection */ + /* and I'm not going to implement reference counts, this will work in most cases */ + else if ( stack[sp].type==ps_array ) + copyarray(&stack[sp].u.dict,&stack[sp].u.dict,&tofrees); + ++sp; + } + } + break; + case pt_add: + if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) { + stack[sp-2].u.val += stack[sp-1].u.val; + --sp; + } + break; + case pt_sub: + if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) { + stack[sp-2].u.val -= stack[sp-1].u.val; + --sp; + } + break; + case pt_mul: + if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) { + stack[sp-2].u.val *= stack[sp-1].u.val; + --sp; + } + break; + case pt_div: + if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) { + if ( stack[sp-1].u.val == 0 ) + LogError( _("Divide by zero in postscript code.\n" )); + else + stack[sp-2].u.val /= stack[sp-1].u.val; + --sp; + } + break; + case pt_idiv: + if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) { + if ( stack[sp-1].u.val == 0 ) + LogError( _("Divide by zero in postscript code.\n" )); + else + stack[sp-2].u.val = ((int) stack[sp-2].u.val) / ((int) stack[sp-1].u.val); + --sp; + } + break; + case pt_mod: + if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) { + if ( stack[sp-1].u.val == 0 ) + LogError( _("Divide by zero in postscript code.\n" )); + else + stack[sp-2].u.val = ((int) stack[sp-2].u.val) % ((int) stack[sp-1].u.val); + --sp; + } + break; + case pt_max: + if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) { + if ( stack[sp-2].u.val < stack[sp-1].u.val ) + stack[sp-2].u.val = stack[sp-1].u.val; + --sp; + } + break; + case pt_min: + if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) { + if ( stack[sp-2].u.val > stack[sp-1].u.val ) + stack[sp-2].u.val = stack[sp-1].u.val; + --sp; + } + break; + case pt_neg: + if ( sp>=1 ) { + if ( stack[sp-1].type == ps_num ) + stack[sp-1].u.val = -stack[sp-1].u.val; + } + break; + case pt_abs: + if ( sp>=1 ) { + if ( stack[sp-1].type == ps_num ) + if ( stack[sp-1].u.val < 0 ) + stack[sp-1].u.val = -stack[sp-1].u.val; + } + break; + case pt_round: + if ( sp>=1 ) { + if ( stack[sp-1].type == ps_num ) + stack[sp-1].u.val = rint(stack[sp-1].u.val); + /* rint isn't quite right, round will take 6.5 to 7, 5.5 to 6, etc. while rint() will take both to 6 */ + } + break; + case pt_floor: + if ( sp>=1 ) { + if ( stack[sp-1].type == ps_num ) + stack[sp-1].u.val = floor(stack[sp-1].u.val); + } + break; + case pt_ceiling: + if ( sp>=1 ) { + if ( stack[sp-1].type == ps_num ) + stack[sp-1].u.val = ceil(stack[sp-1].u.val); + } + break; + case pt_truncate: + if ( sp>=1 ) { + if ( stack[sp-1].type == ps_num ) { + if ( stack[sp-1].u.val<0 ) + stack[sp-1].u.val = ceil(stack[sp-1].u.val); + else + stack[sp-1].u.val = floor(stack[sp-1].u.val); + } + } + break; + case pt_ne: case pt_eq: + if ( sp>=2 ) { + if ( stack[sp-2].type!=stack[sp-1].type ) + stack[sp-2].u.tf = false; + else if ( stack[sp-2].type==ps_num ) + stack[sp-2].u.tf = (stack[sp-2].u.val == stack[sp-1].u.val); + else if ( stack[sp-2].type==ps_bool ) + stack[sp-2].u.tf = (stack[sp-2].u.tf == stack[sp-1].u.tf); + else + stack[sp-2].u.tf = strcmp(stack[sp-2].u.str,stack[sp-1].u.str)==0 ; + stack[sp-2].type = ps_bool; + if ( tok==pt_ne ) stack[sp-2].u.tf = !stack[sp-2].u.tf; + --sp; + } + break; + case pt_gt: case pt_le: case pt_lt: case pt_ge: + if ( sp>=2 ) { + if ( stack[sp-2].type!=stack[sp-1].type ) + stack[sp-2].u.tf = false; + else if ( stack[sp-2].type==ps_array ) + LogError( _("Can't compare arrays\n" )); + else { + int cmp; + if ( stack[sp-2].type==ps_num ) + cmp = (stack[sp-2].u.val > stack[sp-1].u.val)?1: + (stack[sp-2].u.val == stack[sp-1].u.val)?0:-1; + else if ( stack[sp-2].type==ps_bool ) + cmp = (stack[sp-2].u.tf - stack[sp-1].u.tf); + else + cmp = strcmp(stack[sp-2].u.str,stack[sp-1].u.str); + if ( tok==pt_gt ) + stack[sp-2].u.tf = cmp>0; + else if ( tok==pt_lt ) + stack[sp-2].u.tf = cmp<0; + else if ( tok==pt_le ) + stack[sp-2].u.tf = cmp<=0; + else + stack[sp-2].u.tf = cmp>=0; + } + stack[sp-2].type = ps_bool; + --sp; + } + break; + case pt_not: + if ( sp>=1 ) { + if ( stack[sp-1].type == ps_bool ) + stack[sp-1].u.tf = !stack[sp-1].u.tf; + } + break; + case pt_and: + if ( sp>=2 ) { + if ( stack[sp-2].type == ps_num ) + stack[sp-2].u.val = ((int) stack[sp-1].u.val) & (int) stack[sp-1].u.val; + else if ( stack[sp-2].type == ps_bool ) + stack[sp-2].u.tf &= stack[sp-1].u.tf; + --sp; + } + break; + case pt_or: + if ( sp>=2 ) { + if ( stack[sp-2].type == ps_num ) + stack[sp-2].u.val = ((int) stack[sp-1].u.val) | (int) stack[sp-1].u.val; + else if ( stack[sp-2].type == ps_bool ) + stack[sp-2].u.tf |= stack[sp-1].u.tf; + --sp; + } + break; + case pt_xor: + if ( sp>=2 ) { + if ( stack[sp-2].type == ps_num ) + stack[sp-2].u.val = ((int) stack[sp-1].u.val) ^ (int) stack[sp-1].u.val; + else if ( stack[sp-2].type == ps_bool ) + stack[sp-2].u.tf ^= stack[sp-1].u.tf; + --sp; + } + break; + case pt_exp: + if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) { + stack[sp-2].u.val = pow(stack[sp-2].u.val,stack[sp-1].u.val); + --sp; + } + break; + case pt_sqrt: + if ( sp>=1 && stack[sp-1].type==ps_num ) { + stack[sp-1].u.val = sqrt(stack[sp-1].u.val); + } + break; + case pt_ln: + if ( sp>=1 && stack[sp-1].type==ps_num ) { + stack[sp-1].u.val = log(stack[sp-1].u.val); + } + break; + case pt_log: + if ( sp>=1 && stack[sp-1].type==ps_num ) { + stack[sp-1].u.val = log10(stack[sp-1].u.val); + } + break; + case pt_atan: + if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) { + stack[sp-2].u.val = atan2(stack[sp-2].u.val,stack[sp-1].u.val)* + 180/3.1415926535897932; + --sp; + } + break; + case pt_sin: + if ( sp>=1 && stack[sp-1].type==ps_num ) { + stack[sp-1].u.val = sin(stack[sp-1].u.val*3.1415926535897932/180); + } + break; + case pt_cos: + if ( sp>=1 && stack[sp-1].type==ps_num ) { + stack[sp-1].u.val = cos(stack[sp-1].u.val*3.1415926535897932/180); + } + break; + case pt_if: + if ( sp>=2 ) { + if ( ((stack[sp-2].type == ps_bool && stack[sp-2].u.tf) || + (stack[sp-2].type == ps_num && strstr(stack[sp-1].u.str,"setcachedevice")!=NULL)) && + stack[sp-1].type==ps_instr ) + pushio(wrapper,NULL,stack[sp-1].u.str,0); + if ( stack[sp-1].type==ps_string || stack[sp-1].type==ps_instr || stack[sp-1].type==ps_lit ) + free(stack[sp-1].u.str); + sp -= 2; + } else if ( sp==1 && stack[sp-1].type==ps_instr ) { + /*This can happen when reading our type3 fonts, we get passed */ + /* values on the stack which the interpreter knows nothing */ + /* about, but the interp needs to learn the width of the char */ + if ( strstr(stack[sp-1].u.str,"setcachedevice")!=NULL || + strstr(stack[sp-1].u.str,"setcharwidth")!=NULL ) + pushio(wrapper,NULL,stack[sp-1].u.str,0); + free(stack[sp-1].u.str); + sp = 0; + } + break; + case pt_ifelse: + if ( sp>=3 ) { + if ( stack[sp-3].type == ps_bool && stack[sp-3].u.tf ) { + if ( stack[sp-2].type==ps_instr ) + pushio(wrapper,NULL,stack[sp-2].u.str,0); + } else { + if ( stack[sp-1].type==ps_instr ) + pushio(wrapper,NULL,stack[sp-1].u.str,0); + } + if ( stack[sp-1].type==ps_string || stack[sp-1].type==ps_instr || stack[sp-1].type==ps_lit ) + free(stack[sp-1].u.str); + if ( stack[sp-2].type==ps_string || stack[sp-2].type==ps_instr || stack[sp-2].type==ps_lit ) + free(stack[sp-2].u.str); + sp -= 3; + } + break; + case pt_for: + if ( sp>=4 ) { + real init, incr, limit; + char *func; + int cnt; + + if ( stack[sp-4].type == ps_num && stack[sp-3].type==ps_num && + stack[sp-2].type==ps_num && stack[sp-1].type==ps_instr ) { + init = stack[sp-4].u.val; + incr = stack[sp-3].u.val; + limit = stack[sp-2].u.val; + func = stack[sp-1].u.str; + sp -= 4; + cnt = 0; + if ( incr>0 ) { + while ( init<=limit ) { ++cnt; init += incr; } + } else if ( incr<0 ) { + while ( init>=limit ) { ++cnt; init += incr; } + } + pushio(wrapper,NULL,func,cnt); + free(func); + } + } + break; + case pt_loop: + if ( sp>=1 ) { + char *func; + int cnt; + + if ( stack[sp-1].type==ps_instr ) { + cnt = 0x7fffffff; /* Loop for ever */ + func = stack[sp-1].u.str; + --sp; + pushio(wrapper,NULL,func,cnt); + free(func); + } + } + break; + case pt_repeat: + if ( sp>=2 ) { + char *func; + int cnt; + + if ( stack[sp-2].type==ps_num && stack[sp-1].type==ps_instr ) { + cnt = stack[sp-2].u.val; + func = stack[sp-1].u.str; + sp -= 2; + pushio(wrapper,NULL,func,cnt); + free(func); + } + } + break; + case pt_exit: + ioescapeloop(wrapper); + break; + case pt_stopped: + if ( sp>=1 ) { + char *func; + + if ( stack[sp-1].type==ps_instr ) { + func = stack[sp-1].u.str; + --sp; + pushio(wrapper,NULL,func,-1); + free(func); + } + } + break; + case pt_stop: + sp = ioescapestopped(wrapper,stack,sp); + break; + case pt_load: + if ( sp>=1 && stack[sp-1].type==ps_lit ) { + kv = lookup(&dict,stack[sp-1].u.str); + if ( kv!=NULL ) { + free( stack[sp-1].u.str ); + stack[sp-1].type = kv->type; + stack[sp-1].u = kv->u; + if ( kv->type==ps_instr || kv->type==ps_lit ) + stack[sp-1].u.str = copy(stack[sp-1].u.str); + } else + stack[sp-1].type = ps_instr; + } + break; + case pt_def: + sp = AddEntry(&dict,stack,sp); + break; + case pt_bind: + /* a noop in this context */ + break; + case pt_setcachedevice: + if ( sp>=6 ) { + ec->width = stack[sp-6].u.val; + ec->vwidth = stack[sp-5].u.val; + /* I don't care about the bounding box */ + sp-=6; + } + break; + case pt_setcharwidth: + if ( sp>=2 ) + ec->width = stack[sp-=2].u.val; + break; + case pt_translate: + if ( sp>=1 && stack[sp-1].type==ps_array ) + sp = DoMatOp(tok,sp,stack); + else if ( sp>=2 ) { + transform[4] += stack[sp-2].u.val*transform[0]+stack[sp-1].u.val*transform[2]; + transform[5] += stack[sp-2].u.val*transform[1]+stack[sp-1].u.val*transform[3]; + sp -= 2; + } + break; + case pt_scale: + if ( sp>=1 && stack[sp-1].type==ps_array ) + sp = DoMatOp(tok,sp,stack); + else if ( sp>=2 ) { + transform[0] *= stack[sp-2].u.val; + transform[1] *= stack[sp-2].u.val; + transform[2] *= stack[sp-1].u.val; + transform[3] *= stack[sp-1].u.val; + /* transform[4,5] are unchanged */ + sp -= 2; + } + break; + case pt_rotate: + if ( sp>=1 && stack[sp-1].type==ps_array ) + sp = DoMatOp(tok,sp,stack); + else if ( sp>=1 ) { + --sp; + t[0] = t[3] = cos(stack[sp].u.val); + t[1] = sin(stack[sp].u.val); + t[2] = -t[1]; + t[4] = t[5] = 0; + MatMultiply(t,transform,transform); + } + break; + case pt_concat: + if ( sp>=1 ) { + if ( stack[sp-1].type==ps_array ) { + if ( stack[sp-1].u.dict.cnt==6 && stack[sp-1].u.dict.entries[0].type==ps_num ) { + --sp; + t[5] = stack[sp].u.dict.entries[5].u.val; + t[4] = stack[sp].u.dict.entries[4].u.val; + t[3] = stack[sp].u.dict.entries[3].u.val; + t[2] = stack[sp].u.dict.entries[2].u.val; + t[1] = stack[sp].u.dict.entries[1].u.val; + t[0] = stack[sp].u.dict.entries[0].u.val; + dictfree(&stack[sp].u.dict); + MatMultiply(t,transform,transform); + } + } + } + break; + case pt_transform: + if ( sp>=1 && stack[sp-1].type==ps_array ) { + if ( sp>=3 ) { + DoMatTransform(tok,sp,stack); + --sp; + } + } else if ( sp>=2 ) { + double x = stack[sp-2].u.val, y = stack[sp-1].u.val; + stack[sp-2].u.val = transform[0]*x + transform[1]*y + transform[4]; + stack[sp-1].u.val = transform[2]*x + transform[3]*y + transform[5]; + } + break; + case pt_itransform: + if ( sp>=1 && stack[sp-1].type==ps_array ) { + if ( sp>=3 ) { + DoMatTransform(tok,sp,stack); + --sp; + } + } else if ( sp>=2 ) { + double x = stack[sp-2].u.val, y = stack[sp-1].u.val; + MatInverse(t,transform); + stack[sp-2].u.val = t[0]*x + t[1]*y + t[4]; + stack[sp-1].u.val = t[2]*x + t[3]*y + t[5]; + } + break; + case pt_dtransform: + if ( sp>=1 && stack[sp-1].type==ps_array ) { + if ( sp>=3 ) { + DoMatTransform(tok,sp,stack); + --sp; + } + } else if ( sp>=2 ) { + double x = stack[sp-2].u.val, y = stack[sp-1].u.val; + stack[sp-2].u.val = transform[0]*x + transform[1]*y; + stack[sp-1].u.val = transform[2]*x + transform[3]*y; + } + break; + case pt_idtransform: + if ( sp>=1 && stack[sp-1].type==ps_array ) { + if ( sp>=3 ) { + DoMatTransform(tok,sp,stack); + --sp; + } + } else if ( sp>=2 ) { + double x = stack[sp-2].u.val, y = stack[sp-1].u.val; + MatInverse(t,transform); + stack[sp-2].u.val = t[0]*x + t[1]*y; + stack[sp-1].u.val = t[2]*x + t[3]*y; + } + break; + case pt_namelit: + if ( strcmp(tokbuf,"CharProcs")==0 && ec!=NULL ) { + HandleType3Reference(wrapper,ec,transform,tokbuf,tokbufsize); + } else if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_lit; + stack[sp++].u.str = copy(tokbuf); + } + break; + case pt_exec: + if ( sp>0 && stack[sp-1].type == ps_lit ) { + ref = RefCharCreate(); + ref->sc = (SplineChar *) stack[--sp].u.str; + memcpy(ref->transform,transform,sizeof(transform)); + if ( ec->refs==NULL ) + ec->refs = ref; + else + lastref->next = ref; + lastref = ref; + } + break; + case pt_newpath: + SplinePointListsFree(head); + head = NULL; + cur = NULL; + break; + case pt_lineto: case pt_rlineto: + case pt_moveto: case pt_rmoveto: + if ( sp>=2 || tok==pt_newpath ) { + if ( tok==pt_rlineto || tok==pt_rmoveto ) { + current.x += stack[sp-2].u.val; + current.y += stack[sp-1].u.val; + sp -= 2; + } else if ( tok==pt_lineto || tok == pt_moveto ) { + current.x = stack[sp-2].u.val; + current.y = stack[sp-1].u.val; + sp -= 2; + } + pt = chunkalloc(sizeof(SplinePoint)); + Transform(&pt->me,¤t,transform); + pt->noprevcp = true; pt->nonextcp = true; + if ( tok==pt_moveto || tok==pt_rmoveto ) { + SplinePointList *spl = chunkalloc(sizeof(SplinePointList)); + spl->first = spl->last = pt; + if ( cur!=NULL ) + cur->next = spl; + else + head = spl; + cur = spl; + } else { + if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) { + CheckMake(cur->last,pt); + SplineMake3(cur->last,pt); + cur->last = pt; + } + } + } else + sp = 0; + break; + case pt_curveto: case pt_rcurveto: + if ( sp>=6 ) { + if ( tok==pt_rcurveto ) { + stack[sp-1].u.val += current.y; + stack[sp-3].u.val += current.y; + stack[sp-5].u.val += current.y; + stack[sp-2].u.val += current.x; + stack[sp-4].u.val += current.x; + stack[sp-6].u.val += current.x; + } + current.x = stack[sp-2].u.val; + current.y = stack[sp-1].u.val; + if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) { + temp.x = stack[sp-6].u.val; temp.y = stack[sp-5].u.val; + Transform(&cur->last->nextcp,&temp,transform); + cur->last->nonextcp = false; + pt = chunkalloc(sizeof(SplinePoint)); + temp.x = stack[sp-4].u.val; temp.y = stack[sp-3].u.val; + Transform(&pt->prevcp,&temp,transform); + Transform(&pt->me,¤t,transform); + pt->nonextcp = true; + CheckMake(cur->last,pt); + SplineMake3(cur->last,pt); + cur->last = pt; + } + sp -= 6; + } else + sp = 0; + break; + case pt_arc: case pt_arcn: + if ( sp>=5 ) { + real cx, cy, r, a1, a2; + cx = stack[sp-5].u.val; + cy = stack[sp-4].u.val; + r = stack[sp-3].u.val; + a1 = stack[sp-2].u.val; + a2 = stack[sp-1].u.val; + sp -= 5; + temp.x = cx+r*cos(a1/180 * 3.1415926535897932); + temp.y = cy+r*sin(a1/180 * 3.1415926535897932); + if ( temp.x!=current.x || temp.y!=current.y || + !( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) )) { + pt = chunkalloc(sizeof(SplinePoint)); + Transform(&pt->me,&temp,transform); + pt->noprevcp = true; pt->nonextcp = true; + if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) { + CheckMake(cur->last,pt); + SplineMake3(cur->last,pt); + cur->last = pt; + } else { /* if no current point, then start here */ + SplinePointList *spl = chunkalloc(sizeof(SplinePointList)); + spl->first = spl->last = pt; + if ( cur!=NULL ) + cur->next = spl; + else + head = spl; + cur = spl; + } + } + circlearcsto(a1,a2,cx,cy,r,cur,transform,tok==pt_arcn); + current.x = cx+r*cos(a2/180 * 3.1415926535897932); + current.y = cy+r*sin(a2/180 * 3.1415926535897932); + } else + sp = 0; + break; + case pt_arct: case pt_arcto: + if ( sp>=5 ) { + real x1, y1, x2, y2, r; + real xt1, xt2, yt1, yt2; + x1 = stack[sp-5].u.val; + y1 = stack[sp-4].u.val; + x2 = stack[sp-3].u.val; + y2 = stack[sp-2].u.val; + r = stack[sp-1].u.val; + sp -= 5; + + xt1 = xt2 = x1; yt1 = yt2 = y1; + if ( cur==NULL || cur->first==NULL || (cur->first==cur->last && cur->first->next!=NULL) ) + /* Error */; + else if ( current.x==x1 && current.y==y1 ) + /* Error */; + else if (( x1==x2 && y1==y2 ) || + (current.x-x1)*(y2-y1) == (x2-x1)*(current.y-y1) ) { + /* Degenerate case */ + current.x = x1; current.y = y1; + pt = chunkalloc(sizeof(SplinePoint)); + Transform(&pt->me,¤t,transform); + pt->noprevcp = true; pt->nonextcp = true; + CheckMake(cur->last,pt); + SplineMake3(cur->last,pt); + cur->last = pt; + } else { + real l1 = sqrt((current.x-x1)*(current.x-x1)+(current.y-y1)*(current.y-y1)); + real l2 = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); + real dx = ((current.x-x1)/l1 + (x2-x1)/l2); + real dy = ((current.y-y1)/l1 + (y2-y1)/l2); + /* the line from (x1,y1) to (x1+dx,y1+dy) contains the center*/ + real l3 = sqrt(dx*dx+dy*dy); + real cx, cy, t, tmid; + real a1, amid, a2; + int clockwise = true; + dx /= l3; dy /= l3; + a1 = atan2(current.y-y1,current.x-x1); + a2 = atan2(y2-y1,x2-x1); + amid = atan2(dy,dx) - a1; + tmid = r/sin(amid); + t = r/tan(amid); + if ( t<0 ) { + clockwise = false; + t = -t; + tmid = -tmid; + } + cx = x1+ tmid*dx; cy = y1 + tmid*dy; + xt1 = x1 + t*(current.x-x1)/l1; yt1 = y1 + t*(current.y-y1)/l1; + xt2 = x1 + t*(x2-x1)/l2; yt2 = y1 + t*(y2-y1)/l2; + if ( xt1!=current.x || yt1!=current.y ) { + DBasePoint temp; + temp.x = xt1; temp.y = yt1; + pt = chunkalloc(sizeof(SplinePoint)); + Transform(&pt->me,&temp,transform); + pt->noprevcp = true; pt->nonextcp = true; + CheckMake(cur->last,pt); + SplineMake3(cur->last,pt); + cur->last = pt; + } + a1 = 3*3.1415926535897932/2+a1; + a2 = 3.1415926535897932/2+a2; + if ( !clockwise ) { + a1 += 3.1415926535897932; + a2 += 3.1415926535897932; + } + circlearcsto(a1*180/3.1415926535897932,a2*180/3.1415926535897932, + cx,cy,r,cur,transform,clockwise); + } + if ( tok==pt_arcto ) { + stack[sp].type = stack[sp+1].type = stack[sp+2].type = stack[sp+3].type = ps_num; + stack[sp++].u.val = xt1; + stack[sp++].u.val = yt1; + stack[sp++].u.val = xt2; + stack[sp++].u.val = yt2; + } + current.x = xt2; current.y = yt2; + } + break; + case pt_closepath: + if ( cur!=NULL && cur->first!=NULL && cur->first!=cur->last ) { + if ( RealNear(cur->first->me.x,cur->last->me.x) && RealNear(cur->first->me.y,cur->last->me.y) ) { + SplinePoint *oldlast = cur->last; + cur->first->prevcp = oldlast->prevcp; + cur->first->prevcp.x += (cur->first->me.x-oldlast->me.x); + cur->first->prevcp.y += (cur->first->me.y-oldlast->me.y); + cur->first->noprevcp = oldlast->noprevcp; + oldlast->prev->from->next = NULL; + cur->last = oldlast->prev->from; + SplineFree(oldlast->prev); + SplinePointFree(oldlast); + } + CheckMake(cur->last,cur->first); + SplineMake3(cur->last,cur->first); + cur->last = cur->first; + } + break; + case pt_setlinecap: + if ( sp>=1 ) + linecap = stack[--sp].u.val; + break; + case pt_setlinejoin: + if ( sp>=1 ) + linejoin = stack[--sp].u.val; + break; + case pt_setlinewidth: + if ( sp>=1 ) + linewidth = stack[--sp].u.val; + break; + case pt_setdash: + if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_array ) { + sp -= 2; + dash_offset = stack[sp+1].u.val; + for ( i=0; i<DASH_MAX && i<stack[sp].u.dict.cnt; ++i ) + dashes[i] = stack[sp].u.dict.entries[i].u.val; + dictfree(&stack[sp].u.dict); + } + break; + case pt_currentlinecap: case pt_currentlinejoin: + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_num; + stack[sp++].u.val = tok==pt_currentlinecap?linecap:linejoin; + } + break; + case pt_currentlinewidth: + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_num; + stack[sp++].u.val = linewidth; + } + break; + case pt_currentdash: + if ( sp+1<(int)(sizeof(stack)/sizeof(stack[0])) ) { + struct pskeydict dict; + dict.is_executable = 0; + for ( i=0; i<DASH_MAX && dashes[i]!=0; ++i ); + dict.cnt = dict.max = i; + dict.entries = gcalloc(i,sizeof(struct pskeyval)); + for ( j=0; j<i; ++j ) { + dict.entries[j].type = ps_num; + dict.entries[j].u.val = dashes[j]; + } + stack[sp].type = ps_array; + stack[sp++].u.dict = dict; + stack[sp].type = ps_num; + stack[sp++].u.val = dash_offset; + } + break; + case pt_currentgray: + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_num; + stack[sp++].u.val = (3*((fore>>16)&0xff) + 6*((fore>>8)&0xff) + (fore&0xff))/2550.; + } + break; + case pt_setgray: + if ( sp>=1 ) { + fore = stack[--sp].u.val*255; + fore *= 0x010101; + } + break; + case pt_setrgbcolor: + if ( sp>=3 ) { + fore = (((int) (stack[sp-3].u.val*255))<<16) + + (((int) (stack[sp-2].u.val*255))<<8) + + (int) (stack[sp-1].u.val*255); + sp -= 3; + } + break; + case pt_currenthsbcolor: case pt_currentrgbcolor: + if ( sp+2<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = stack[sp+1].type = stack[sp+2].type = ps_num; + if ( tok==pt_currentrgbcolor ) { + stack[sp++].u.val = ((fore>>16)&0xff)/255.; + stack[sp++].u.val = ((fore>>8)&0xff)/255.; + stack[sp++].u.val = (fore&0xff)/255.; + } else { + int r=fore>>16, g=(fore>>8)&0xff, bl=fore&0xff; + int mx, mn; + real h, s, b; + mx = mn = r; + if ( mx>g ) mn=g; else mx=g; + if ( mx<bl ) mx = bl; if ( mn>bl ) mn = bl; + b = mx/255.; + s = h = 0; + if ( mx>0 ) + s = ((real) (mx-mn))/mx; + if ( s!=0 ) { + real rdiff = ((real) (mx-r))/(mx-mn); + real gdiff = ((real) (mx-g))/(mx-mn); + real bdiff = ((real) (mx-bl))/(mx-mn); + if ( rdiff==0 ) + h = bdiff-gdiff; + else if ( gdiff==0 ) + h = 2 + rdiff-bdiff; + else + h = 4 + gdiff-rdiff; + h /= 6; + if ( h<0 ) h += 1; + } + stack[sp++].u.val = h; + stack[sp++].u.val = s; + stack[sp++].u.val = b; + } + } + break; + case pt_sethsbcolor: + if ( sp>=3 ) { + real h = stack[sp-3].u.val, s = stack[sp-2].u.val, b = stack[sp-1].u.val; + int r=0,g=0,bl=0; + if ( s==0 ) /* it's grey */ + fore = ((int) (b*255)) * 0x010101; + else { + real sextant = (h-floor(h))*6; + real mod = sextant-floor(sextant); + real p = b*(1-s), q = b*(1-s*mod), t = b*(1-s*(1-mod)); + switch( (int) sextant) { + case 0: + r = b*255.; g = t*255.; bl = p*255.; + break; + case 1: + r = q*255.; g = b*255.; bl = p*255.; + break; + case 2: + r = p*255.; g = b*255.; bl = t*255.; + break; + case 3: + r = p*255.; g = q*255.; bl = b*255.; + break; + case 4: + r = t*255.; g = p*255.; bl = b*255.; + break; + case 5: + r = b*255.; g = p*255.; bl = q*255.; + break; + } + fore = COLOR_CREATE(r,g,bl); + } + sp -= 3; + } + break; + case pt_currentcmykcolor: + if ( sp+3<(int)(sizeof(stack)/sizeof(stack[0])) ) { + real c,m,y,k; + stack[sp].type = stack[sp+1].type = stack[sp+2].type = stack[sp+3].type = ps_num; + y = 1.-(fore&0xff)/255.; + m = 1.-((fore>>8)&0xff)/255.; + c = 1.-((fore>>16)&0xff)/255.; + k = y; if ( k>m ) k=m; if ( k>c ) k=c; + if ( k!=1 ) { + y = (y-k)/(1-k); + m = (m-k)/(1-k); + c = (c-k)/(1-k); + } else + y = m = c = 0; + stack[sp++].u.val = c; + stack[sp++].u.val = m; + stack[sp++].u.val = y; + stack[sp++].u.val = k; + } + break; + case pt_setcmykcolor: + if ( sp>=4 ) { + real c=stack[sp-4].u.val,m=stack[sp-3].u.val,y=stack[sp-2].u.val,k=stack[sp-1].u.val; + sp -= 4; + if ( k==1 ) + fore = 0x000000; + else { + if (( y = (1-k)*y+k )<0 ) y=0; else if ( y>1 ) y=1; + if (( m = (1-k)*m+k )<0 ) m=0; else if ( m>1 ) m=1; + if (( c = (1-k)*c+k )<0 ) c=0; else if ( c>1 ) c=1; + fore = ((int) ((1-c)*255.)<<16) | + ((int) ((1-m)*255.)<<8) | + ((int) ((1-y)*255.)); + } + } + break; + case pt_currentpoint: + if ( sp+1<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_num; + stack[sp++].u.val = current.x; + stack[sp].type = ps_num; + stack[sp++].u.val = current.y; + } + break; + case pt_fill: case pt_stroke: + if ( head==NULL && ec->splines!=NULL ) { + /* assume they did a "gsave fill grestore stroke" (or reverse)*/ + ent = ec->splines; + if ( tok==pt_stroke ) { + ent->u.splines.cap = linecap; ent->u.splines.join = linejoin; + ent->u.splines.stroke_width = linewidth; + memcpy(ent->u.splines.transform,transform,sizeof(transform)); + } + } else { + ent = EntityCreate(head,linecap,linejoin,linewidth,transform,clippath); + ent->next = ec->splines; + ec->splines = ent; + } + if ( tok==pt_fill ) + ent->u.splines.fill.col = fore; + else + ent->u.splines.stroke.col = fore; + head = NULL; cur = NULL; + break; + case pt_clip: + /* I really should intersect the old clip path with the new, but */ + /* I don't trust my intersect routine, crashes too often */ + SplinePointListsFree(clippath); + clippath = SplinePointListCopy(head); + if ( clippath!=NULL && clippath->first!=clippath->last ) { + SplineMake3(clippath->last,clippath->first); + clippath->last = clippath->first; + } + break; + case pt_imagemask: + LogError( _("This version of FontForge does not support the imagemask operator.\nFor support configure --with-multilayer.\n") ); + if ( sp>=5 && (stack[sp-1].type==ps_instr || stack[sp-1].type==ps_string)) + sp -= 5; + break; + + /* We don't do these right, but at least we'll avoid some errors with this hack */ + case pt_save: case pt_currentmatrix: + /* push some junk on the stack */ + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_num; + stack[sp++].u.val = 0; + } + /* Fall through into gsave */; + case pt_gsave: + if ( gsp<30 ) { + memcpy(gsaves[gsp].transform,transform,sizeof(transform)); + gsaves[gsp].current = current; + gsaves[gsp].linewidth = linewidth; + gsaves[gsp].linecap = linecap; + gsaves[gsp].linejoin = linejoin; + gsaves[gsp].fore = fore; + gsaves[gsp].clippath = SplinePointListCopy(clippath); + ++gsp; + /* I should be saving the "current path" too, but that's too hard */ + } + break; + case pt_restore: case pt_setmatrix: + /* pop some junk off the stack */ + if ( sp>=1 ) + --sp; + /* Fall through into grestore */; + case pt_grestore: + if ( gsp>0 ) { + --gsp; + memcpy(transform,gsaves[gsp].transform,sizeof(transform)); + current = gsaves[gsp].current; + linewidth = gsaves[gsp].linewidth; + linecap = gsaves[gsp].linecap; + linejoin = gsaves[gsp].linejoin; + fore = gsaves[gsp].fore; + SplinePointListsFree(clippath); + clippath = gsaves[gsp].clippath; + } + break; + case pt_null: + /* push a 0. I don't handle pointers properly */ + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].u.val = 0; + stack[sp++].type = ps_num; + } + break; + case pt_currentoverprint: + /* push false. I don't handle this properly */ + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].u.val = 0; + stack[sp++].type = ps_bool; + } + break; + case pt_setoverprint: + /* pop one item on stack */ + if ( sp>=1 ) + --sp; + break; + case pt_currentflat: + /* push 1.0 (default value). I don't handle this properly */ + if ( sp<(int)(sizeof(stack)/sizeof(stack[0]) )) { + stack[sp].u.val = 1.0; + stack[sp++].type = ps_num; + } + break; + case pt_setflat: + /* pop one item on stack */ + if ( sp>=1 ) + --sp; + break; + case pt_currentmiterlimit: + /* push 10.0 (default value). I don't handle this properly */ + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].u.val = 10.0; + stack[sp++].type = ps_num; + } + break; + case pt_setmiterlimit: + /* pop one item off stack */ + if ( sp>=1 ) + --sp; + break; + case pt_currentpacking: + /* push false (default value). I don't handle this properly */ + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].u.val = 0; + stack[sp++].type = ps_bool; + } + break; + case pt_setpacking: + /* pop one item on stack */ + if ( sp>=1 ) + --sp; + break; + case pt_currentstrokeadjust: + /* push false (default value). I don't handle this properly */ + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].u.val = 0; + stack[sp++].type = ps_bool; + } + break; + case pt_setstrokeadjust: + /* pop one item on stack */ + if ( sp>=1 ) + --sp; + break; + case pt_currentsmoothness: + /* default value is installation dependant. I don't handle this properly */ + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].u.val = 1.0; + stack[sp++].type = ps_num; + } + break; + case pt_setsmoothness: + /* pop one item on stack */ + if ( sp>=1 ) + --sp; + break; + case pt_currentobjectformat: + /* default value is installation dependant. I don't handle this properly */ + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].u.val = 0.0; + stack[sp++].type = ps_num; + } + break; + case pt_setobjectformat: + /* pop one item on stack */ + if ( sp>=1 ) + --sp; + break; + case pt_currentglobal: case pt_currentshared: + /* push false (default value). I don't handle this properly */ + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].u.val = 0; + stack[sp++].type = ps_bool; + } + break; + case pt_setglobal: + /* pop one item on stack */ + if ( sp>=1 ) + --sp; + break; + + case pt_openarray: case pt_mark: + if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp++].type = ps_mark; + } + break; + case pt_counttomark: + for ( i=0; i<sp; ++i ) + if ( stack[sp-1-i].type==ps_mark ) + break; + if ( i==sp ) + LogError( _("No mark in counttomark\n") ); + else if ( sp<(int)(sizeof(stack)/sizeof(stack[0])) ) { + stack[sp].type = ps_num; + stack[sp++].u.val = i; + } + break; + case pt_cleartomark: + for ( i=0; i<sp; ++i ) + if ( stack[sp-1-i].type==ps_mark ) + break; + if ( i==sp ) + LogError( _("No mark in cleartomark\n") ); + else { + while ( sp>=i ) { + --sp; + if ( stack[sp].type==ps_string || stack[sp].type==ps_instr || + stack[sp].type==ps_lit ) + free(stack[sp].u.str); + else if ( stack[sp].type==ps_array || stack[sp].type==ps_dict ) + dictfree(&stack[sp].u.dict); + } + } + break; + case pt_closearray: + for ( i=0; i<sp; ++i ) + if ( stack[sp-1-i].type==ps_mark ) + break; + if ( i==sp ) + LogError( _("No mark in ] (close array)\n") ); + else { + struct pskeydict dict; + dict.is_executable = 0; + dict.cnt = dict.max = i; + dict.entries = gcalloc(i,sizeof(struct pskeyval)); + for ( j=0; j<i; ++j ) { + dict.entries[j].type = stack[sp-i+j].type; + dict.entries[j].u = stack[sp-i+j].u; + /* don't need to copy because the things on the stack */ + /* are being popped (don't need to free either) */ + } + collectgarbage(&tofrees,&dict); + sp = sp-i; + stack[sp-1].type = ps_array; + stack[sp-1].u.dict = dict; + } + break; + case pt_array: + if ( sp>=1 && stack[sp-1].type==ps_num ) { + struct pskeydict dict; + dict.is_executable = 0; + dict.cnt = dict.max = stack[sp-1].u.val; + dict.entries = gcalloc(dict.cnt,sizeof(struct pskeyval)); + /* all entries are inited to void */ + stack[sp-1].type = ps_array; + stack[sp-1].u.dict = dict; + } + break; + case pt_aload: + sp = aload(sp,stack,sizeof(stack)/sizeof(stack[0]),&tofrees); + break; + case pt_astore: + if ( sp>=1 && stack[sp-1].type==ps_array ) { + struct pskeydict dict; + --sp; + dict = stack[sp].u.dict; + if ( sp>=dict.cnt ) { + for ( i=dict.cnt-1; i>=0 ; --i ) { + --sp; + dict.entries[i].type = stack[sp].type; + dict.entries[i].u = stack[sp].u; + } + } + stack[sp].type = ps_array; + stack[sp].u.dict = dict; + ++sp; + } + break; + + case pt_output: case pt_outputd: case pt_print: + if ( sp>=1 ) { + --sp; + switch ( stack[sp].type ) { + case ps_num: + printf( "%g", (double) stack[sp].u.val ); + break; + case ps_bool: + printf( "%s", stack[sp].u.tf ? "true" : "false" ); + break; + case ps_string: case ps_instr: case ps_lit: + if ( tok==pt_outputd ) + printf( stack[sp].type==ps_lit ? "/" : + stack[sp].type==ps_string ? "(" : "{" ); + printf( "%s", stack[sp].u.str ); + if ( tok==pt_outputd ) + printf( stack[sp].type==ps_lit ? "" : + stack[sp].type==ps_string ? ")" : "}" ); + free(stack[sp].u.str); + break; + case ps_void: + printf( "-- void --" ); + break; + case ps_array: + if ( tok==pt_outputd ) { + printarray(&stack[sp].u.dict); + dictfree(&stack[sp].u.dict); + break; + } /* else fall through */ + dictfree(&stack[sp].u.dict); + default: + printf( "-- nostringval --" ); + break; + } + if ( tok==pt_output || tok==pt_outputd ) + printf( "\n" ); + } else + LogError( _("Nothing on stack to print\n") ); + break; + + case pt_cvi: case pt_cvr: + /* I shan't distinguish between integers and reals */ + if ( sp>=1 && stack[sp-1].type==ps_string ) { + double val = strtod(stack[sp-1].u.str,NULL); + free(stack[sp-1].u.str); + stack[sp-1].u.val = val; + stack[sp-1].type = ps_num; + } + break; + case pt_cvlit: + if ( sp>=1 ) { + if ( stack[sp-1].type==ps_array ) + stack[sp-1].u.dict.is_executable = false; + } + case pt_cvn: + if ( sp>=1 ) { + if ( stack[sp-1].type==ps_string ) + stack[sp-1].type = ps_lit; + } + case pt_cvx: + if ( sp>=1 ) { + if ( stack[sp-1].type==ps_array ) + stack[sp-1].u.dict.is_executable = true; + } + break; + case pt_cvrs: + if ( sp>=3 && stack[sp-1].type==ps_string && + stack[sp-2].type==ps_num && + stack[sp-3].type==ps_num ) { + if ( stack[sp-2].u.val==8 ) + sprintf( stack[sp-1].u.str, "%o", (int) stack[sp-3].u.val ); + else if ( stack[sp-2].u.val==16 ) + sprintf( stack[sp-1].u.str, "%X", (int) stack[sp-3].u.val ); + else /* default to radix 10 no matter what they asked for */ + sprintf( stack[sp-1].u.str, "%g", (double) stack[sp-3].u.val ); + stack[sp-3] = stack[sp-1]; + sp-=2; + } + break; + case pt_cvs: + if ( sp>=2 && stack[sp-1].type==ps_string ) { + switch ( stack[sp].type ) { + case ps_num: + sprintf( stack[sp-1].u.str, "%g", (double) stack[sp-2].u.val ); + break; + case ps_bool: + sprintf( stack[sp-1].u.str, "%s", stack[sp-2].u.tf ? "true" : "false" ); + break; + case ps_string: case ps_instr: case ps_lit: + sprintf( stack[sp-1].u.str, "%s", stack[sp-2].u.str ); + free(stack[sp].u.str); + break; + case ps_void: + printf( "-- void --" ); + break; + case ps_array: + dictfree(&stack[sp].u.dict); + default: + sprintf( stack[sp-1].u.str, "-- nostringval --" ); + break; + } + stack[sp-2] = stack[sp-1]; + --sp; + } + break; + case pt_stringop: /* the string keyword, not the () thingy */ + if ( sp>=1 && stack[sp-1].type==ps_num ) { + stack[sp-1].type = ps_string; + stack[sp-1].u.str = gcalloc(stack[sp-1].u.val+1,1); + } + break; + + case pt_unknown: + if ( !warned ) { + LogError( _("Warning: Unable to parse token %s, some features may be lost\n"), tokbuf ); + warned = true; + } + break; + + default: + break; + }} + } + done: + if ( rs!=NULL ) { + int i, cnt, j; + for ( i=sp-1; i>=0; --i ) + if ( stack[i].type!=ps_num ) + break; + cnt = sp-1-i; + if ( cnt>rs->max ) cnt = rs->max; + rs->cnt = cnt; + for ( j=i+1; j<sp; ++j ) + rs->stack[j-i-1] = stack[j].u.val; + } + freestuff(stack,sp,&dict,&gb,&tofrees); + if ( head!=NULL ) { + ent = EntityCreate(head,linecap,linejoin,linewidth,transform,clippath); + ent->next = ec->splines; + ec->splines = ent; + } + while ( gsp>0 ) { + --gsp; + SplinePointListsFree(gsaves[gsp].clippath); + } + SplinePointListsFree(clippath); + ECCatagorizePoints(ec); + if ( ec->width == UNDEFINED_WIDTH ) + ec->width = wrapper->advance_width; + setlocale(LC_NUMERIC,oldloc); +} + +static void InterpretPS(FILE *ps, char *psstr, EntityChar *ec, RetStack *rs) { + IO wrapper; + + memset(&wrapper,0,sizeof(wrapper)); + wrapper.advance_width = UNDEFINED_WIDTH; + pushio(&wrapper,ps,psstr,0); + _InterpretPS(&wrapper,ec,rs); +} + +static SplinePointList *EraseStroke(SplineChar *sc,SplinePointList *head,SplinePointList *erase) { + SplineSet *spl, *last; + SplinePoint *sp; + + if ( head==NULL ) { + /* Pointless, but legal */ + SplinePointListsFree(erase); +return( NULL ); + } + + last = NULL; + for ( spl=head; spl!=NULL; spl=spl->next ) { + for ( sp=spl->first; sp!=NULL; ) { + sp->selected = false; + if ( sp->next==NULL ) + break; + sp = sp->next->to; + if ( sp==spl->first ) + break; + } + last = spl; + } + for ( spl=erase; spl!=NULL; spl=spl->next ) { + for ( sp=spl->first; sp!=NULL; ) { + sp->selected = true; + if ( sp->next==NULL ) + break; + sp = sp->next->to; + if ( sp==spl->first ) + break; + } + } + last->next = erase; +return( SplineSetRemoveOverlap(sc,head,over_exclude) ); +} + +static Entity *EntityReverse(Entity *ent) { + Entity *next, *last = NULL; + + while ( ent!=NULL ) { + next = ent->next; + ent->next = last; + last = ent; + ent = next; + } +return( last ); +} + +void SFSplinesFromLayers(SplineFont *sf, int tostroke) { + (void)sf; + (void)tostroke; +} + +static void EntityCharCorrectDir(EntityChar *ec) { + SplineSet *ss; + Entity *ent; + int changed; + + for ( ent=ec->splines; ent!=NULL; ent = ent->next ) { + /* ignore splines which are only stoked, but not filled */ + if ( ent->type == et_splines && ent->u.splines.fill.col!=0xffffffff ) { + /* Correct the direction of each stroke or fill with respect to */ + /* the splines in it */ + SplineSetsCorrect(ent->u.splines.splines,&changed); + if ( ent->u.splines.fill.col==0xffffff ) { + /* If they are filling with white, then assume they mean */ + /* an internal area that should be drawn backwards */ + for ( ss=ent->u.splines.splines; ss!=NULL; ss=ss->next ) + SplineSetReverse(ss); + } + SplineSetsCorrect(ent->clippath,&changed); + } + } +} + +static void EntityDefaultStrokeFill(Entity *ent) { + while ( ent!=NULL ) { + if ( ent->type == et_splines && + ent->u.splines.stroke.col==0xffffffff && + ent->u.splines.fill.col==0xffffffff ) { + SplineSet *spl; + int all=1; + for ( spl=ent->u.splines.splines; spl!=NULL; spl=spl->next ) + if ( spl->first->prev!=NULL ) { + all = false; + break; + } + if ( all && ent->u.splines.splines!=NULL && + (ent->u.splines.stroke_width==0 || ent->u.splines.stroke_width==WIDTH_INHERITED)) + ent->u.splines.stroke_width=40; /* random guess */ + if (ent->u.splines.stroke_width==0 || ent->u.splines.stroke_width==WIDTH_INHERITED) + ent->u.splines.fill.col = COLOR_INHERITED; + else + ent->u.splines.stroke.col = COLOR_INHERITED; + } + ent = ent->next; + } +} + +static SplinePointList *SplinesFromEntityChar(EntityChar *ec,int *flags,int is_stroked) { + Entity *ent, *next; + SplinePointList *head=NULL, *last, *new, *nlast, *temp, *each, *transed; + StrokeInfo si; + real inversetrans[6]; + /*SplineSet *spl;*/ + int handle_eraser = false; + int ask = false; + + EntityDefaultStrokeFill(ec->splines); + + if ( !is_stroked ) { + + if ( *flags==-1 ) { + for ( ent=ec->splines; ent!=NULL; ent = ent->next ) { + if ( ent->type == et_splines && + (ent->u.splines.fill.col==0xffffff || + /*ent->u.splines.clippath!=NULL ||*/ + (ent->u.splines.stroke_width!=0 && ent->u.splines.stroke.col!=0xffffffff))) { + ask = true; + break; + } + } + if ( ask ) + *flags = PsStrokeFlagsDlg(); + } + + if ( *flags & sf_correctdir ) /* Will happen if flags still unset (-1) */ + EntityCharCorrectDir(ec); + + handle_eraser = *flags!=-1 && (*flags & sf_handle_eraser); + if ( handle_eraser ) + ec->splines = EntityReverse(ec->splines); + } + + for ( ent=ec->splines; ent!=NULL; ent = next ) { + next = ent->next; + if ( ent->type == et_splines && is_stroked ) { + if ( head==NULL ) + head = ent->u.splines.splines; + else + last->next = ent->u.splines.splines; + if ( ent->u.splines.splines!=NULL ) + for ( last = ent->u.splines.splines; last->next!=NULL; last=last->next ); + ent->u.splines.splines = NULL; + } else if ( ent->type == et_splines ) { + if ( ent->u.splines.stroke.col!=0xffffffff && + (ent->u.splines.fill.col==0xffffffff || ent->u.splines.stroke_width!=0)) { + /* What does a stroke width of 0 mean? PS Says minimal width line */ + /* How do we implement that? Special case: If filled and stroked 0, then */ + /* ignore the stroke. This idiom is used by MetaPost sometimes and means */ + /* no stroke */ + memset(&si,'\0',sizeof(si)); + si.toobigwarn = *flags & sf_toobigwarn ? 1 : 0; + si.join = ent->u.splines.join; + si.cap = ent->u.splines.cap; + si.removeoverlapifneeded = *flags & sf_removeoverlap ? 1 : 0; + si.radius = ent->u.splines.stroke_width/2; + if ( ent->u.splines.stroke_width==WIDTH_INHERITED ) + si.radius = .5; + if ( si.cap == lc_inherited ) si.cap = lc_butt; + if ( si.join == lc_inherited ) si.join = lj_miter; + new = NULL; +#if 0 + SSBisectTurners(ent->u.splines.splines); +#endif + MatInverse(inversetrans,ent->u.splines.transform); + transed = SplinePointListTransform(SplinePointListCopy( + ent->u.splines.splines),inversetrans,true); + for ( each = transed; each!=NULL; each=each->next ) { + temp = SplineSetStroke(each,&si,ec->sc); + if ( new==NULL ) + new=temp; + else + nlast->next = temp; + if ( temp!=NULL ) + for ( nlast=temp; nlast->next!=NULL; nlast=nlast->next ); + } + new = SplinePointListTransform(new,ent->u.splines.transform,true); + SplinePointListsFree(transed); + if ( handle_eraser && ent->u.splines.stroke.col==0xffffff ) { + head = EraseStroke(ec->sc,head,new); + last = head; + if ( last!=NULL ) + for ( ; last->next!=NULL; last=last->next ); + } else { + if ( head==NULL ) + head = new; + else + last->next = new; + if ( new!=NULL ) + for ( last = new; last->next!=NULL; last=last->next ); + } + if ( si.toobigwarn ) + *flags |= sf_toobigwarn; + } + /* If they have neither a stroke nor a fill, pretend they said fill */ + if ( ent->u.splines.fill.col==0xffffffff && ent->u.splines.stroke.col!=0xffffffff ) + SplinePointListsFree(ent->u.splines.splines); + else if ( handle_eraser && ent->u.splines.fill.col==0xffffff ) { + head = EraseStroke(ec->sc,head,ent->u.splines.splines); + last = head; + if ( last!=NULL ) + for ( ; last->next!=NULL; last=last->next ); + } else { + new = ent->u.splines.splines; + if ( head==NULL ) + head = new; + else + last->next = new; + if ( new!=NULL ) + for ( last = new; last->next!=NULL; last=last->next ); + } + } + SplinePointListsFree(ent->clippath); + free(ent); + } +return( head ); +} + + +static RefChar *revrefs(RefChar *cur) { + RefChar *p, *n; + + if ( cur==NULL ) +return( NULL ); + + p = NULL; + for ( ; (n=cur->next)!=NULL; cur = n ) { + cur->next = p; + p = cur; + } + cur->next = p; +return( cur ); +} + +static void SCInterpretPS(FILE *ps,SplineChar *sc, int *flags) { + EntityChar ec; + real dval; + char tokbuf[10]; + IO wrapper; + int ch; + + while ( isspace(ch = getc(ps)) ); + ungetc(ch,ps); + + memset(&wrapper,0,sizeof(wrapper)); + wrapper.advance_width = UNDEFINED_WIDTH; + if ( ch!='<' ) { + pushio(&wrapper,ps,NULL,0); + + if ( nextpstoken(&wrapper,&dval,tokbuf,sizeof(tokbuf))!=pt_opencurly ) + LogError( _("We don't understand this font\n") ); + } else { + (void) getc(ps); + pushfogio(&wrapper,ps); + } + memset(&ec,'\0',sizeof(ec)); + ec.fromtype3 = true; + ec.sc = sc; + _InterpretPS(&wrapper,&ec,NULL); + sc->width = ec.width; + sc->layers[ly_fore].splines = SplinesFromEntityChar(&ec,flags,false); + sc->layers[ly_fore].refs = revrefs(ec.refs); + free(wrapper.top); +} + +void PSFontInterpretPS(FILE *ps,struct charprocs *cp,char **encoding) { + char tokbuf[100]; + int tok,i, j; + real dval; + SplineChar *sc; EntityChar dummy; + RefChar *p, *ref, *next; + IO wrapper; + int flags = -1; + + wrapper.top = NULL; + wrapper.advance_width = UNDEFINED_WIDTH; + pushio(&wrapper,ps,NULL,0); + + while ( (tok = nextpstoken(&wrapper,&dval,tokbuf,sizeof(tokbuf)))!=pt_eof && tok!=pt_end ) { + if ( tok==pt_namelit ) { + if ( cp->next>=cp->cnt ) { + ++cp->cnt; + cp->keys = grealloc(cp->keys,cp->cnt*sizeof(char *)); + cp->values = grealloc(cp->values,cp->cnt*sizeof(char *)); + } + if ( cp->next<cp->cnt ) { + sc = SplineCharCreate(2); + cp->keys[cp->next] = copy(tokbuf); + cp->values[cp->next++] = sc; + sc->name = copy(tokbuf); + SCInterpretPS(ps,sc,&flags); + ff_progress_next(); + } else { + memset(&dummy,0,sizeof(dummy)); + dummy.fromtype3 = true; + InterpretPS(ps,NULL,&dummy,NULL); + } + } + } + free(wrapper.top); + + /* References were done by name in the postscript. we stored the names in */ + /* ref->sc (which is a hack). Now look up all those names and replace */ + /* with the appropriate splinechar. If we can't find anything then throw */ + /* out the reference */ + /* Further fixups come later, where all ps refs are fixedup */ + for ( i=0; i<cp->next; ++i ) { + for ( p=NULL, ref=cp->values[i]->layers[ly_fore].refs; ref!=NULL; ref=next ) { + char *refname = (char *) (ref->sc); + next = ref->next; + if ( ref->sc==NULL ) + refname=encoding[ref->orig_pos]; + for ( j=0; j<cp->next; ++j ) + if ( strcmp(cp->keys[j],refname)==0 ) + break; + free(ref->sc); /* a string, not a splinechar */ + if ( j!=cp->next ) { + ref->sc = cp->values[j]; + SCMakeDependent(cp->values[i],ref->sc); + ref->adobe_enc = getAdobeEnc(ref->sc->name); + ref->checked = true; + p = ref; + } else { + if ( p==NULL ) + cp->values[i]->layers[ly_fore].refs = next; + else + p->next = next; + ref->next = NULL; + RefCharFree(ref); + } + } + } +} + +static void closepath(SplinePointList *cur, int is_type2) { + if ( cur!=NULL && cur->first==cur->last && cur->first->prev==NULL && is_type2 ) +return; /* The "path" is just a single point created by a moveto */ + /* Probably we're just doing another moveto */ + if ( cur!=NULL && cur->first!=NULL && cur->first!=cur->last ) { +/* I allow for greater errors here than I do in the straight postscript code */ +/* because: 1) the rel-rel operators will accumulate more rounding errors */ +/* 2) I only output 2 decimal digits after the decimal in type1 output */ + if ( RealWithin(cur->first->me.x,cur->last->me.x,.05) && RealWithin(cur->first->me.y,cur->last->me.y,.05) ) { + SplinePoint *oldlast = cur->last; + cur->first->prevcp = oldlast->prevcp; + cur->first->prevcp.x += (cur->first->me.x-oldlast->me.x); + cur->first->prevcp.y += (cur->first->me.y-oldlast->me.y); + cur->first->noprevcp = oldlast->noprevcp; + oldlast->prev->from->next = NULL; + cur->last = oldlast->prev->from; + chunkfree(oldlast->prev,sizeof(*oldlast)); + chunkfree(oldlast->hintmask,sizeof(HintMask)); + chunkfree(oldlast,sizeof(*oldlast)); + } + CheckMake(cur->last,cur->first); + SplineMake3(cur->last,cur->first); + cur->last = cur->first; + } +} + +static void UnblendFree(StemInfo *h ) { + while ( h!=NULL ) { + chunkfree(h->u.unblended,sizeof(real [2][MmMax])); + h->u.unblended = NULL; + h = h->next; + } +} + +static StemInfo *HintsAppend(StemInfo *to,StemInfo *extra) { + StemInfo *h; + + if ( to==NULL ) +return( extra ); + if ( extra==NULL ) +return( to ); + for ( h=to; h->next!=NULL; h=h->next ); + h->next = extra; +return( to ); +} + +static StemInfo *HintNew(double start,double width) { + StemInfo *h; + + h = chunkalloc(sizeof(StemInfo)); + h->start = start; + h->width = width; +return( h ); +} + +static void RemapHintMask(HintMask *hm,int mapping[96],int max) { + HintMask rpl; + int i, mb; + + if ( hm==NULL ) +return; + + if ( max>96 ) max = 96; + mb = (max+7)>>3; + + memset(&rpl,0,mb); + for ( i=0; i<max; ++i ) if ( (*hm)[i>>3]&(0x80>>(i&0x7)) ) + rpl[mapping[i]>>3] |= (0x80>>(mapping[i]&0x7)); + memcpy(hm,&rpl,mb); +} + +static void HintsRenumber(SplineChar *sc) { + /* In a type1 font the hints may get added to our hint list in a semi- */ + /* random order. In an incorrect type2 font the same thing could happen. */ + /* Force the order to be correct, and then update all masks */ + int mapping[96]; + int i, max; + StemInfo *h; + SplineSet *spl; + SplinePoint *sp; + + for ( i=0; i<96; ++i ) mapping[i] = i; + + i = 0; + for ( h=sc->hstem; h!=NULL; h=h->next ) { + if ( h->hintnumber<96 && i<96 ) { + mapping[h->hintnumber] = i; + h->hintnumber = i++; + } + chunkfree(h->u.unblended,sizeof(real [2][MmMax])); + h->u.unblended = NULL; + } + for ( h=sc->vstem; h!=NULL; h=h->next ) { + if ( h->hintnumber<96 && i<96 ) { + mapping[h->hintnumber] = i; + h->hintnumber = i++; + } + chunkfree(h->u.unblended,sizeof(real [2][MmMax])); + h->u.unblended = NULL; + } + max = i; + for ( i=0; i<max; ++i ) + if ( mapping[i]!=i ) + break; + if ( i==max ) +return; /* Didn't change the order */ + + for ( i=0; i<sc->countermask_cnt; ++i ) + RemapHintMask(&sc->countermasks[i],mapping,max); + for ( spl = sc->layers[ly_fore].splines; spl!=NULL; spl=spl->next ) { + for ( sp = spl->first; ; ) { + RemapHintMask(sp->hintmask,mapping,max); + if ( sp->next==NULL ) + break; + sp = sp->next->to; + if ( sp==spl->first ) + break; + } + } +} + +int UnblendedCompare(real u1[MmMax], real u2[MmMax], int cnt) { + int i; + + for ( i=0; i<cnt; ++i ) { + if ( u1[i]!=u2[i] ) +return( u1[i]>u2[i]?1:-1 ); + } +return( 0 ); +} + +static StemInfo *SameH(StemInfo *old,real start, real width, + real unblended[2][MmMax], int instance_count) { + StemInfo *sameh; + + if ( instance_count==0 ) { + for ( sameh=old; sameh!=NULL; sameh=sameh->next ) + if ( sameh->start==start && sameh->width==width) + break; + } else { int j; + for ( j=1; j<instance_count; ++j ) { + unblended[0][j] += unblended[0][j-1]; + unblended[1][j] += unblended[1][j-1]; + } + for ( sameh=old; sameh!=NULL; sameh=sameh->next ) { + if ( (*sameh->u.unblended)[0] == NULL || (*sameh->u.unblended)[1]==NULL ) + continue; + if ( UnblendedCompare((*sameh->u.unblended)[0],unblended[0],instance_count)==0 && + UnblendedCompare((*sameh->u.unblended)[1],unblended[1],instance_count)==0) + break; + } + } +return( sameh ); +} + +static real Blend(real u[MmMax],struct pscontext *context) { + real sum = u[0]; + int i; + + for ( i=1; i<context->instance_count; ++i ) + sum += context->blend_values[i]*u[i]; +return( sum ); +} + +/* this handles either Type1 or Type2 charstrings. Type2 charstrings have */ +/* more operators than Type1s and the old operators have extended meanings */ +/* (ie. the rlineto operator can produce more than one line). But pretty */ +/* much it's a superset and if we parse for type2 (with a few additions) */ +/* we'll get it right */ +/* Char width is done differently. Moveto starts a newpath. 0xff starts a 16.16*/ +/* number rather than a 32 bit number */ +SplineChar *PSCharStringToSplines(uint8 *type1, int len, struct pscontext *context, + struct pschars *subrs, struct pschars *gsubrs, const char *name) { + int is_type2 = context->is_type2; + real stack[50]; int sp=0, v; /* Type1 stack is about 25 long, Type2 stack is 48 */ + real transient[32]; + SplineChar *ret = SplineCharCreate(2); + SplinePointList *cur=NULL, *oldcur=NULL; + RefChar *r1, *r2, *rlast=NULL; + DBasePoint current; + real dx, dy, dx2, dy2, dx3, dy3, dx4, dy4, dx5, dy5, dx6=0, dy6; + SplinePoint *pt; + /* subroutines may be nested to a depth of 10 */ + struct substate { unsigned char *type1; int len; int subnum; } pcstack[11]; + int pcsp=0; + StemInfo *hint, *hp; + real pops[30]; + int popsp=0; + int base, polarity; + real coord; + struct pschars *s; + int hint_cnt = 0; + StemInfo *activeh=NULL, *activev=NULL, *sameh; + HintMask *pending_hm = NULL; + HintMask *counters[96]; + int cp=0; + real unblended[2][MmMax]; + int last_was_b1=false, old_last_was_b1; + + if ( !is_type2 && context->instance_count>1 ) + memset(unblended,0,sizeof(unblended)); + + ret->name = copy( name ); + ret->unicodeenc = -1; + ret->width = (int16) 0x8000; + if ( name==NULL ) name = "unnamed"; + ret->manualhints = true; + + current.x = current.y = 0; + while ( len>0 ) { + if ( sp>48 ) { + LogError( _("Stack got too big in %s\n"), name ); + sp = 48; + } + base = 0; + --len; + if ( (v = *type1++)>=32 ) { + if ( v<=246) { + stack[sp++] = v - 139; + } else if ( v<=250 ) { + stack[sp++] = (v-247)*256 + *type1++ + 108; + --len; + } else if ( v<=254 ) { + stack[sp++] = -(v-251)*256 - *type1++ - 108; + --len; + } else { + int val = (*type1<<24) | (type1[1]<<16) | (type1[2]<<8) | type1[3]; + stack[sp++] = val; + type1 += 4; + len -= 4; + if ( is_type2 ) { +#ifndef PSFixed_Is_TTF /* The type2 spec is contradictory. It says this is a */ + /* two's complement number, but it also says it is a */ + /* Fixed, which in truetype is not two's complement */ + /* (mantisa is always unsigned) */ + stack[sp-1] /= 65536.; +#else + int mant = val&0xffff; + stack[sp-1] = (val>>16) + mant/65536.; +#endif + } + } + } else if ( v==28 ) { + stack[sp++] = (short) ((type1[0]<<8) | type1[1]); + type1 += 2; + len -= 2; + /* In the Dict tables of CFF, a 5byte fixed value is prefixed by a */ + /* 29 code. In Type2 strings the prefix is 255. */ + } else if ( v==12 ) { + old_last_was_b1 = last_was_b1; last_was_b1 = false; + v = *type1++; + --len; + switch ( v ) { + case 0: /* dotsection */ + sp = 0; + break; + case 1: /* vstem3 */ /* specifies three v hints zones at once */ + if ( sp<6 ) LogError( _("Stack underflow on vstem3 in %s\n"), name ); + /* according to the standard, if there is a vstem3 there can't */ + /* be any vstems, so there can't be any confusion about hint order */ + /* so we don't need to worry about unblended stuff */ + sameh = NULL; + if ( !is_type2 ) + sameh = SameH(ret->vstem,stack[0] + ret->lsidebearing,stack[1], + unblended,0); + hint = HintNew(stack[0] + ret->lsidebearing,stack[1]); + hint->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++; + if ( activev==NULL ) + activev = hp = hint; + else { + for ( hp=activev; hp->next!=NULL; hp = hp->next ); + hp->next = hint; + hp = hint; + } + sameh = NULL; + if ( !is_type2 ) + sameh = SameH(ret->vstem,stack[2] + ret->lsidebearing,stack[3], + unblended,0); + hp->next = HintNew(stack[2] + ret->lsidebearing,stack[3]); + hp->next->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++; + if ( !is_type2 ) + sameh = SameH(ret->vstem,stack[4] + ret->lsidebearing,stack[5], + unblended,0); + hp->next->next = HintNew(stack[4] + ret->lsidebearing,stack[5]); + hp->next->next->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++; + if ( !is_type2 && hp->next->next->hintnumber<96 ) { + if ( pending_hm==NULL ) + pending_hm = chunkalloc(sizeof(HintMask)); + (*pending_hm)[hint->hintnumber>>3] |= 0x80>>(hint->hintnumber&0x7); + (*pending_hm)[hint->next->hintnumber>>3] |= 0x80>>(hint->next->hintnumber&0x7); + (*pending_hm)[hint->next->next->hintnumber>>3] |= 0x80>>(hint->next->next->hintnumber&0x7); + } + hp = hp->next->next; + sp = 0; + break; + case 2: /* hstem3 */ /* specifies three h hints zones at once */ + if ( sp<6 ) LogError( _("Stack underflow on hstem3 in %s\n"), name ); + sameh = NULL; + if ( !is_type2 ) + sameh = SameH(ret->hstem,stack[0],stack[1], unblended,0); + hint = HintNew(stack[0],stack[1]); + hint->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++; + if ( activeh==NULL ) + activeh = hp = hint; + else { + for ( hp=activeh; hp->next!=NULL; hp = hp->next ); + hp->next = hint; + hp = hint; + } + sameh = NULL; + if ( !is_type2 ) + sameh = SameH(ret->hstem,stack[2],stack[3], unblended,0); + hp->next = HintNew(stack[2],stack[3]); + hp->next->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++; + sameh = NULL; + if ( !is_type2 ) + sameh = SameH(ret->hstem,stack[4],stack[5], unblended,0); + hp->next->next = HintNew(stack[4],stack[5]); + hp->next->next->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++; + if ( !is_type2 && hp->next->next->hintnumber<96 ) { + if ( pending_hm==NULL ) + pending_hm = chunkalloc(sizeof(HintMask)); + (*pending_hm)[hint->hintnumber>>3] |= 0x80>>(hint->hintnumber&0x7); + (*pending_hm)[hint->next->hintnumber>>3] |= 0x80>>(hint->next->hintnumber&0x7); + (*pending_hm)[hint->next->next->hintnumber>>3] |= 0x80>>(hint->next->next->hintnumber&0x7); + } + hp = hp->next->next; + sp = 0; + break; + case 6: /* seac */ /* build accented characters */ + seac: + if ( sp<5 ) LogError( _("Stack underflow on seac in %s\n"), name ); + /* stack[0] must be the lsidebearing of the accent. I'm not sure why */ + r1 = RefCharCreate(); + r2 = RefCharCreate(); + r2->transform[0] = 1; r2->transform[3]=1; + r2->transform[4] = stack[1] - (stack[0]-ret->lsidebearing); + r2->transform[5] = stack[2]; + /* the translation of the accent here is said to be relative */ + /* to the origins of the base character. I think they place */ + /* the origin at the left bearing. And they don't mean the */ + /* base char at all, they mean the current char's lbearing */ + /* (which is normally the same as the base char's, except */ + /* when I has a big accent (like diaerisis) */ + r1->transform[0] = 1; r1->transform[3]=1; + r1->adobe_enc = stack[3]; + r2->adobe_enc = stack[4]; + if ( stack[3]<0 || stack[3]>=256 || stack[4]<0 || stack[4]>=256 ) { + LogError( _("Reference encoding out of bounds in %s\n"), name ); + r1->adobe_enc = 0; + r2->adobe_enc = 0; + } + r1->next = r2; + if ( rlast!=NULL ) rlast->next = r1; + else ret->layers[ly_fore].refs = r1; + ret->changedsincelasthinted = true; /* seac glyphs contain no hints */ + rlast = r2; + sp = 0; + break; + case 7: /* sbw */ /* generalized width/sidebearing command */ + if ( sp<4 ) LogError( _("Stack underflow on sbw in %s\n"), name ); + ret->lsidebearing = stack[0]; + /* stack[1] is lsidebearing y (only for vertical writing styles, CJK) */ + ret->width = stack[2]; + /* stack[3] is height (for vertical writing styles, CJK) */ + sp = 0; + break; + case 5: case 9: case 14: case 26: + if ( sp<1 ) LogError( _("Stack underflow on unary operator in %s\n"), name ); + switch ( v ) { + case 5: stack[sp-1] = (stack[sp-1]==0); break; /* not */ + case 9: if ( stack[sp-1]<0 ) stack[sp-1]= -stack[sp-1]; break; /* abs */ + case 14: stack[sp-1] = -stack[sp-1]; break; /* neg */ + case 26: stack[sp-1] = sqrt(stack[sp-1]); break; /* sqrt */ + } + break; + case 3: case 4: case 10: case 11: case 12: case 15: case 24: + if ( sp<2 ) LogError( _("Stack underflow on binary operator in %s\n"), name ); + else switch ( v ) { + case 3: /* and */ + stack[sp-2] = (stack[sp-1]!=0 && stack[sp-2]!=0); + break; + case 4: /* and */ + stack[sp-2] = (stack[sp-1]!=0 || stack[sp-2]!=0); + break; + case 10: /* add */ + stack[sp-2] += stack[sp-1]; + break; + case 11: /* sub */ + stack[sp-2] -= stack[sp-1]; + break; + case 12: /* div */ + stack[sp-2] /= stack[sp-1]; + break; + case 24: /* mul */ + stack[sp-2] *= stack[sp-1]; + break; + case 15: /* eq */ + stack[sp-2] = (stack[sp-1]==stack[sp-2]); + break; + } + --sp; + break; + case 22: /* ifelse */ + if ( sp<4 ) LogError( _("Stack underflow on ifelse in %s\n"), name ); + else { + if ( stack[sp-2]>stack[sp-1] ) + stack[sp-4] = stack[sp-3]; + sp -= 3; + } + break; + case 23: /* random */ + /* This function returns something (0,1]. It's not clear to me*/ + /* if rand includes 0 and RAND_MAX or not, but this approach */ + /* should work no matter what */ + do { + stack[sp] = (rand()/(RAND_MAX-1)); + } while ( stack[sp]==0 || stack[sp]>1 ); + ++sp; + break; + case 16: /* callothersubr */ + /* stack[sp-1] is the number of the thing to call in the othersubr array */ + /* stack[sp-2] is the number of args to grab off our stack and put on the */ + /* real postscript stack */ + if ( is_type2 ) + LogError( _("Type2 fonts do not support the Type1 callothersubrs operator") ); + if ( sp<2 || sp < 2+stack[sp-2] ) { + LogError( _("Stack underflow on callothersubr in %s\n"), name ); + sp = 0; + } else { + int tot = stack[sp-2], i, k, j; + popsp = 0; + for ( k=sp-3; k>=sp-2-tot; --k ) + pops[popsp++] = stack[k]; + /* othersubrs 0-3 must be interpretted. 0-2 are Flex, 3 is Hint Replacement */ + /* othersubrs 12,13 are for counter hints. We don't need to */ + /* do anything to ignore them */ + /* Subroutines 14-18 are multiple master blenders. We need */ + /* to pay attention to them too */ + switch ( (int) stack[sp-1] ) { + case 3: { + /* when we weren't capabable of hint replacement we */ + /* punted by putting 3 on the stack (T1 spec page 70) */ + /* subroutine 3 is a noop */ + /*pops[popsp-1] = 3;*/ + ret->manualhints = false; + /* We can manage hint substitution from hintmask though*/ + /* well enough that we needn't clear the manualhints bit */ + ret->hstem = HintsAppend(ret->hstem,activeh); activeh=NULL; + ret->vstem = HintsAppend(ret->vstem,activev); activev=NULL; + } break; + case 1: { + /* Essentially what we want to do is draw a line from */ + /* where we are at the beginning to where we are at */ + /* the end. So we save the beginning here (this starts*/ + /* the flex sequence), we ignore all calls to othersub*/ + /* 2, and when we get to othersub 0 we put everything*/ + /* back to where it should be and free up whatever */ + /* extranious junk we created along the way and draw */ + /* our line. */ + /* Let's punt a little less, and actually figure out */ + /* the appropriate rrcurveto commands and put in a */ + /* dished serif */ + /* We should never get here in a type2 font. But we did*/ + /* this code won't work if we follow type2 conventions*/ + /* so turn off type2 until we get 0 callothersubrs */ + /* which marks the end of the flex sequence */ + is_type2 = false; + if ( cur!=NULL ) { + oldcur = cur; + cur->next = NULL; + } else + LogError( _("Bad flex subroutine in %s\n"), name ); + } break; + case 2: { + /* No op */; + } break; + case 0: if ( oldcur!=NULL ) { + SplinePointList *spl = oldcur->next; + if ( spl!=NULL && spl->next!=NULL && + spl->next->next!=NULL && + spl->next->next->next!=NULL && + spl->next->next->next->next!=NULL && + spl->next->next->next->next->next!=NULL && + spl->next->next->next->next->next->next!=NULL ) { + BasePoint old_nextcp, mid_prevcp, mid, mid_nextcp, + end_prevcp, end; + old_nextcp = spl->next->first->me; + mid_prevcp = spl->next->next->first->me; + mid = spl->next->next->next->first->me; + mid_nextcp = spl->next->next->next->next->first->me; + end_prevcp = spl->next->next->next->next->next->first->me; + end = spl->next->next->next->next->next->next->first->me; + cur = oldcur; + if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) { + cur->last->nextcp = old_nextcp; + cur->last->nonextcp = false; + pt = chunkalloc(sizeof(SplinePoint)); + pt->hintmask = pending_hm; pending_hm = NULL; + pt->prevcp = mid_prevcp; + pt->me = mid; + pt->nextcp = mid_nextcp; + /*pt->flex = pops[2];*/ + CheckMake(cur->last,pt); + SplineMake3(cur->last,pt); + cur->last = pt; + pt = chunkalloc(sizeof(SplinePoint)); + pt->prevcp = end_prevcp; + pt->me = end; + pt->nonextcp = true; + CheckMake(cur->last,pt); + SplineMake3(cur->last,pt); + cur->last = pt; + } else + LogError( _("No previous point on path in curveto from flex 0 in %s\n"), name ); + } else { + /* Um, something's wrong. Let's just draw a line */ + /* do the simple method, which consists of creating */ + /* the appropriate line */ + pt = chunkalloc(sizeof(SplinePoint)); + pt->me.x = pops[1]; pt->me.y = pops[0]; + pt->noprevcp = true; pt->nonextcp = true; + SplinePointListFree(oldcur->next); oldcur->next = NULL; spl = NULL; + cur = oldcur; + if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) { + CheckMake(cur->last,pt); + SplineMake3(cur->last,pt); + cur->last = pt; + } else + LogError( _("No previous point on path in lineto from flex 0 in %s\n"), name ); + } + --popsp; + cur->next = NULL; + SplinePointListsFree(spl); + oldcur = NULL; + } else + LogError( _("Bad flex subroutine in %s\n"), name ); + + is_type2 = context->is_type2; + /* If we found a type2 font with a type1 flex sequence */ + /* (an illegal idea, but never mind, someone gave us one)*/ + /* then we had to turn off type2 untill the end of the */ + /* flex sequence. Which is here */ + break; + case 14: /* results in 1 blended value */ + case 15: /* results in 2 blended values */ + case 16: /* results in 3 blended values */ + case 17: /* results in 4 blended values */ + case 18: { /* results in 6 blended values */ + int cnt = stack[sp-1]-13; + if ( cnt==5 ) cnt=6; + if ( context->instance_count==0 ) + LogError( _("Attempt to use a multiple master subroutine in a non-mm font in %s.\n"), name ); + else if ( tot!=cnt*context->instance_count ) + LogError( _("Multiple master subroutine called with the wrong number of arguments in %s.\n"), name ); + else { + /* Hints need to keep track of the original blends */ + if ( cnt==1 && !is_type2 ) { + if ( sp-2-tot>=1 && (!old_last_was_b1 || stack[0]!=Blend(unblended[1],context))) { + unblended[0][0] = stack[0]; + for ( i=1; i<context->instance_count; ++i ) + unblended[0][i] = 0; + } else + memcpy(unblended,unblended+1,context->instance_count*sizeof(real)); + for ( j=0; j<context->instance_count; ++j ) + unblended[1][j] = stack[sp-2-tot+j]; + } else if ( cnt==2 && !is_type2 ) { + unblended[0][0] = stack[sp-2-tot]; + unblended[1][0] = stack[sp-2-tot+1]; + for ( i=0; i<2; ++i ) + for ( j=1; j<context->instance_count; ++j ) + unblended[i][j] = stack[sp-2-tot+2+i*(context->instance_count-1)+(j-1)]; + } + popsp = 0; + for ( i=0; i<cnt; ++i ) { + double sum = stack[sp-2-tot+ i]; + for ( j=1; j<context->instance_count; ++j ) + sum += context->blend_values[j]* + stack[sp-2-tot+ cnt +i*(context->instance_count-1)+ j-1]; + pops[cnt-1-popsp++] = sum; + } + } + } break; + } + sp = k+1; + } + break; + case 20: /* put */ + if ( sp<2 ) LogError( _("Too few items on stack for put in %s\n"), name ); + else if ( stack[sp-1]<0 || stack[sp-1]>=32 ) LogError( _("Reference to transient memory out of bounds in put in %s\n"), name ); + else { + transient[(int)stack[sp-1]] = stack[sp-2]; + sp -= 2; + } + break; + case 21: /* get */ + if ( sp<1 ) LogError( _("Too few items on stack for get in %s\n"), name ); + else if ( stack[sp-1]<0 || stack[sp-1]>=32 ) LogError( _("Reference to transient memory out of bounds in put in %s\n"), name ); + else + stack[sp-1] = transient[(int)stack[sp-1]]; + break; + case 17: /* pop */ + /* pops something from the postscript stack and pushes it on ours */ + /* used to get a return value from an othersubr call */ + /* Bleah. Adobe wants the pops to return the arguments if we */ + /* don't understand the call. What use is the subroutine then?*/ + if ( popsp<=0 ) + LogError( _("Pop stack underflow on pop in %s\n"), name ); + else + stack[sp++] = pops[--popsp]; + break; + case 18: /* drop */ + if ( sp>0 ) --sp; + break; + case 27: /* dup */ + if ( sp>=1 ) { + stack[sp] = stack[sp-1]; + ++sp; + } + break; + case 28: /* exch */ + if ( sp>=2 ) { + real temp = stack[sp-1]; + stack[sp-1] = stack[sp-2]; stack[sp-2] = temp; + } + break; + case 29: /* index */ + if ( sp>=1 ) { + int index = stack[--sp]; + if ( index<0 || sp<index+1 ) + LogError( _("Index out of range in %s\n"), name ); + else { + stack[sp] = stack[sp-index-1]; + ++sp; + } + } + break; + case 30: /* roll */ + if ( sp>=2 ) { + int j = stack[sp-1], N=stack[sp-2]; + if ( N>sp || j>=N || j<0 || N<0 ) + LogError( _("roll out of range in %s\n"), name ); + else if ( j==0 || N==0 ) + /* No op */; + else { + real *temp = galloc(N*sizeof(real)); + int i; + for ( i=0; i<N; ++i ) + temp[i] = stack[sp-N+i]; + for ( i=0; i<N; ++i ) + stack[sp-N+i] = temp[(i+j)%N]; + free(temp); + } + } + break; + case 33: /* setcurrentpoint */ + if ( sp<2 ) LogError( _("Stack underflow on setcurrentpoint in %s\n"), name ); + else { + current.x = stack[0]; + current.y = stack[1]; + } + sp = 0; + break; + case 34: /* hflex */ + case 35: /* flex */ + case 36: /* hflex1 */ + case 37: /* flex1 */ + dy = dy3 = dy4 = dy5 = dy6 = 0; + dx = stack[base++]; + if ( v!=34 ) + dy = stack[base++]; + dx2 = stack[base++]; + dy2 = stack[base++]; + dx3 = stack[base++]; + if ( v!=34 && v!=36 ) + dy3 = stack[base++]; + dx4 = stack[base++]; + if ( v!=34 && v!=36 ) + dy4 = stack[base++]; + dx5 = stack[base++]; + if ( v==34 ) + dy5 = -dy2; + else + dy5 = stack[base++]; + switch ( v ) { + real xt, yt; + case 35: /* flex */ + dx6 = stack[base++]; + dy6 = stack[base++]; + break; + case 34: /* hflex */ + dx6 = stack[base++]; + break; + case 36: /* hflex1 */ + dx6 = stack[base++]; + dy6 = -dy-dy2-dy5; + break; + case 37: /* flex1 */ + xt = dx+dx2+dx3+dx4+dx5; + yt = dy+dy2+dy3+dy4+dy5; + if ( xt<0 ) xt= -xt; + if ( yt<0 ) yt= -yt; + if ( xt>yt ) { + dx6 = stack[base++]; + dy6 = -dy-dy2-dy3-dy4-dy5; + } else { + dy6 = stack[base++]; + dx6 = -dx-dx2-dx3-dx4-dx5; + } + break; + } + if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) { + current.x = rint((current.x+dx)*1024)/1024; current.y = rint((current.y+dy)*1024)/1024; + cur->last->nextcp.x = current.x; cur->last->nextcp.y = current.y; + cur->last->nonextcp = false; + current.x = rint((current.x+dx2)*1024)/1024; current.y = rint((current.y+dy2)*1024)/1024; + pt = chunkalloc(sizeof(SplinePoint)); + pt->hintmask = pending_hm; pending_hm = NULL; + pt->prevcp.x = current.x; pt->prevcp.y = current.y; + current.x = rint((current.x+dx3)*1024)/1024; current.y = rint((current.y+dy3)*1024)/1024; + pt->me.x = current.x; pt->me.y = current.y; + pt->nonextcp = true; + CheckMake(cur->last,pt); + SplineMake3(cur->last,pt); + cur->last = pt; + + current.x = rint((current.x+dx4)*1024)/1024; current.y = rint((current.y+dy4)*1024)/1024; + cur->last->nextcp.x = current.x; cur->last->nextcp.y = current.y; + cur->last->nonextcp = false; + current.x = rint((current.x+dx5)*1024)/1024; current.y = rint((current.y+dy5)*1024)/1024; + pt = chunkalloc(sizeof(SplinePoint)); + pt->prevcp.x = current.x; pt->prevcp.y = current.y; + current.x = rint((current.x+dx6)*1024)/1024; current.y = rint((current.y+dy6)*1024)/1024; + pt->me.x = current.x; pt->me.y = current.y; + pt->nonextcp = true; + CheckMake(cur->last,pt); + SplineMake3(cur->last,pt); + cur->last = pt; + } else + LogError( _("No previous point on path in flex operator in %s\n"), name ); + sp = 0; + break; + default: + LogError( _("Uninterpreted opcode 12,%d in %s\n"), v, name ); + break; + } + } else { last_was_b1 = false; switch ( v ) { + case 1: /* hstem */ + case 18: /* hstemhm */ + base = 0; + if ( (sp&1) && ret->width == (int16) 0x8000 ) + ret->width = stack[0]; + if ( sp&1 ) + base=1; + if ( sp-base<2 ) + LogError( _("Stack underflow on hstem in %s\n"), name ); + /* stack[0] is absolute y for start of horizontal hint */ + /* (actually relative to the y specified as lsidebearing y in sbw*/ + /* stack[1] is relative y for height of hint zone */ + coord = 0; + hp = NULL; + if ( activeh!=NULL ) + for ( hp=activeh; hp->next!=NULL; hp = hp->next ); + while ( sp-base>=2 ) { + sameh = NULL; + if ( !is_type2 ) + sameh = SameH(ret->hstem,stack[base]+coord,stack[base+1], + unblended,context->instance_count); + hint = HintNew(stack[base]+coord,stack[base+1]); + hint->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++; + if ( !is_type2 && context->instance_count!=0 ) { + hint->u.unblended = chunkalloc(sizeof(real [2][MmMax])); + memcpy(hint->u.unblended,unblended,sizeof(real [2][MmMax])); + } + if ( activeh==NULL ) + activeh = hint; + else + hp->next = hint; + hp = hint; + if ( !is_type2 && hint->hintnumber<96 ) { + if ( pending_hm==NULL ) + pending_hm = chunkalloc(sizeof(HintMask)); + (*pending_hm)[hint->hintnumber>>3] |= 0x80>>(hint->hintnumber&0x7); + } + base+=2; + coord = hint->start+hint->width; + } + sp = 0; + break; + case 19: /* hintmask */ + case 20: /* cntrmask */ + /* If there's anything on the stack treat it as a vstem hint */ + case 3: /* vstem */ + case 23: /* vstemhm */ + base = 0; + if ( cur==NULL || v==3 || v==23 ) { + if ( (sp&1) && is_type2 && ret->width == (int16) 0x8000 ) { + ret->width = stack[0]; + } + if ( sp&1 ) + base=1; + /* I've seen a vstemhm with no arguments. I've no idea what that */ + /* means. It came right after a hintmask */ + /* I'm confused about v/hstemhm because the manual says it needs */ + /* to be used if one uses a hintmask, but that's not what the */ + /* examples show. Or I'm not understanding. */ + if ( sp-base<2 && v!=19 && v!=20 ) + LogError( _("Stack underflow on vstem in %s\n"), name ); + /* stack[0] is absolute x for start of vertical hint */ + /* (actually relative to the x specified as lsidebearing in h/sbw*/ + /* stack[1] is relative x for height of hint zone */ + coord = ret->lsidebearing; + hp = NULL; + if ( activev!=NULL ) + for ( hp=activev; hp->next!=NULL; hp = hp->next ); + while ( sp-base>=2 ) { + sameh = NULL; + if ( !is_type2 ) + sameh = SameH(ret->vstem,stack[base]+coord,stack[base+1], + unblended,context->instance_count); + hint = HintNew(stack[base]+coord,stack[base+1]); + hint->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++; + if ( !is_type2 && context->instance_count!=0 ) { + hint->u.unblended = chunkalloc(sizeof(real [2][MmMax])); + memcpy(hint->u.unblended,unblended,sizeof(real [2][MmMax])); + } + if ( !is_type2 && hint->hintnumber<96 ) { + if ( pending_hm==NULL ) + pending_hm = chunkalloc(sizeof(HintMask)); + (*pending_hm)[hint->hintnumber>>3] |= 0x80>>(hint->hintnumber&0x7); + } + if ( activev==NULL ) + activev = hint; + else + hp->next = hint; + hp = hint; + base+=2; + coord = hint->start+hint->width; + } + sp = 0; + } + if ( v==19 || v==20 ) { /* hintmask, cntrmask */ + unsigned bytes = (hint_cnt+7)/8; + if ( bytes>sizeof(HintMask) ) bytes = sizeof(HintMask); + if ( v==19 ) { + ret->hstem = HintsAppend(ret->hstem,activeh); activeh=NULL; + ret->vstem = HintsAppend(ret->vstem,activev); activev=NULL; + if ( pending_hm==NULL ) + pending_hm = chunkalloc(sizeof(HintMask)); + memcpy(pending_hm,type1,bytes); + } else if ( cp<(int)(sizeof(counters)/sizeof(counters[0])) ) { + counters[cp] = chunkalloc(sizeof(HintMask)); + memcpy(counters[cp],type1,bytes); + ++cp; + } + if ( bytes!=(unsigned)hint_cnt/8 ) { + int mask = 0xff>>(hint_cnt&7); + if ( type1[bytes-1]&mask ) + LogError( _("Hint mask (or counter mask) with too many hints in %s\n"), name ); + } + type1 += bytes; + len -= bytes; + } + break; + case 14: /* endchar */ + /* endchar is allowed to terminate processing even within a subroutine */ + if ( (sp&1) && is_type2 && ret->width == (int16) 0x8000 ) + ret->width = stack[0]; + if ( context->painttype!=2 ) + closepath(cur,is_type2); + pcsp = 0; + if ( sp==4 ) { + /* In Type2 strings endchar has a depreciated function of doing */ + /* a seac (which doesn't exist at all). Except enchar takes */ + /* 4 args and seac takes 5. Bleah */ + stack[4] = stack[3]; stack[3] = stack[2]; stack[2] = stack[1]; stack[1] = stack[0]; + stack[0] = 0; + sp = 5; + goto seac; + } else if ( sp==5 ) { + /* same as above except also specified a width */ + stack[0] = 0; + goto seac; + } + /* the docs say that endchar must be the last command in a char */ + /* (or the last command in a subroutine which is the last in the */ + /* char) So in theory if there's anything left we should complain*/ + /* In practice though, the EuroFont has a return statement after */ + /* the endchar in a subroutine. So we won't try to catch that err*/ + /* and just stop. */ + /* Adobe says it's not an error, but I can't understand their */ + /* logic */ + goto done; + break; + case 13: /* hsbw (set left sidebearing and width) */ + if ( sp<2 ) LogError( _("Stack underflow on hsbw in %s\n"), name ); + ret->lsidebearing = stack[0]; + current.x = stack[0]; /* sets the current point too */ + ret->width = stack[1]; + sp = 0; + break; + case 9: /* closepath */ + sp = 0; + closepath(cur,is_type2); + break; + case 21: /* rmoveto */ + case 22: /* hmoveto */ + case 4: /* vmoveto */ + if ( is_type2 ) { + if (( (v==21 && sp==3) || (v!=21 && sp==2)) && ret->width == (int16) 0x8000 ) + /* Character's width may be specified on the first moveto */ + ret->width = stack[0]; + if ( v==21 && sp>2 ) { + stack[0] = stack[sp-2]; stack[1] = stack[sp-1]; + sp = 2; + } else if ( v!=21 && sp>1 ) { + stack[0] = stack[sp-1]; + sp = 1; + } + if ( context->painttype!=2 ) + closepath(cur,true); + } + case 5: /* rlineto */ + case 6: /* hlineto */ + case 7: /* vlineto */ + polarity = 0; + base = 0; + while ( base<sp ) { + dx = dy = 0; + if ( v==5 || v==21 ) { + if ( sp<base+2 ) { + LogError( _("Stack underflow on rlineto/rmoveto in %s\n"), name ); + break; + } + dx = stack[base++]; + dy = stack[base++]; + } else if ( (v==6 && !(polarity&1)) || (v==7 && (polarity&1)) || v==22 ) { + if ( sp<=base ) { + LogError( _("Stack underflow on hlineto/hmoveto in %s\n"), name ); + break; + } + dx = stack[base++]; + } else /*if ( (v==7 && !(parity&1)) || (v==6 && (parity&1) || v==4 )*/ { + if ( sp<=base ) { + LogError( _("Stack underflow on vlineto/vmoveto in %s\n"), name ); + break; + } + dy = stack[base++]; + } + ++polarity; + current.x = rint((current.x+dx)*1024)/1024; current.y = rint((current.y+dy)*1024)/1024; + pt = chunkalloc(sizeof(SplinePoint)); + pt->hintmask = pending_hm; pending_hm = NULL; + pt->me.x = current.x; pt->me.y = current.y; + pt->noprevcp = true; pt->nonextcp = true; + if ( v==4 || v==21 || v==22 ) { + if ( cur!=NULL && cur->first==cur->last && cur->first->prev==NULL && is_type2 ) { + /* Two adjacent movetos should not create single point paths */ + cur->first->me.x = current.x; cur->first->me.y = current.y; + SplinePointFree(pt); + } else { + SplinePointList *spl = chunkalloc(sizeof(SplinePointList)); + spl->first = spl->last = pt; + if ( cur!=NULL ) + cur->next = spl; + else + ret->layers[ly_fore].splines = spl; + cur = spl; + } + break; + } else { + if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) { + CheckMake(cur->last,pt); + SplineMake3(cur->last,pt); + cur->last = pt; + } else + LogError( _("No previous point on path in lineto in %s\n"), name ); + if ( !is_type2 ) + break; + } + } + sp = 0; + break; + case 25: /* rlinecurve */ + base = 0; + while ( sp>base+6 ) { + current.x = rint((current.x+stack[base++])*1024)/1024; current.y = rint((current.y+stack[base++])*1024)/1024; + if ( cur!=NULL ) { + pt = chunkalloc(sizeof(SplinePoint)); + pt->hintmask = pending_hm; pending_hm = NULL; + pt->me.x = current.x; pt->me.y = current.y; + pt->noprevcp = true; pt->nonextcp = true; + CheckMake(cur->last,pt); + SplineMake3(cur->last,pt); + cur->last = pt; + } + } + case 24: /* rcurveline */ + case 8: /* rrcurveto */ + case 31: /* hvcurveto */ + case 30: /* vhcurveto */ + case 27: /* hhcurveto */ + case 26: /* vvcurveto */ + polarity = 0; + while ( sp>base+2 ) { + dx = dy = dx2 = dy2 = dx3 = dy3 = 0; + if ( v==8 || v==25 || v==24 ) { + if ( sp<6+base ) { + LogError( _("Stack underflow on rrcurveto in %s\n"), name ); + base = sp; + } else { + dx = stack[base++]; + dy = stack[base++]; + dx2 = stack[base++]; + dy2 = stack[base++]; + dx3 = stack[base++]; + dy3 = stack[base++]; + } + } else if ( v==27 ) { /* hhcurveto */ + if ( sp<4+base ) { + LogError( _("Stack underflow on hhcurveto in %s\n"), name ); + base = sp; + } else { + if ( (sp-base)&1 ) dy = stack[base++]; + dx = stack[base++]; + dx2 = stack[base++]; + dy2 = stack[base++]; + dx3 = stack[base++]; + } + } else if ( v==26 ) { /* vvcurveto */ + if ( sp<4+base ) { + LogError( _("Stack underflow on hhcurveto in %s\n"), name ); + base = sp; + } else { + if ( (sp-base)&1 ) dx = stack[base++]; + dy = stack[base++]; + dx2 = stack[base++]; + dy2 = stack[base++]; + dy3 = stack[base++]; + } + } else if ( (v==31 && !(polarity&1)) || (v==30 && (polarity&1)) ) { + if ( sp<4+base ) { + LogError( _("Stack underflow on hvcurveto in %s\n"), name ); + base = sp; + } else { + dx = stack[base++]; + dx2 = stack[base++]; + dy2 = stack[base++]; + dy3 = stack[base++]; + if ( sp==base+1 ) + dx3 = stack[base++]; + } + } else /*if ( (v==30 && !(polarity&1)) || (v==31 && (polarity&1)) )*/ { + if ( sp<4+base ) { + LogError( _("Stack underflow on vhcurveto in %s\n"), name ); + base = sp; + } else { + dy = stack[base++]; + dx2 = stack[base++]; + dy2 = stack[base++]; + dx3 = stack[base++]; + if ( sp==base+1 ) + dy3 = stack[base++]; + } + } + ++polarity; + if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) { + current.x = rint((current.x+dx)*1024)/1024; current.y = rint((current.y+dy)*1024)/1024; + cur->last->nextcp.x = current.x; cur->last->nextcp.y = current.y; + cur->last->nonextcp = false; + current.x = rint((current.x+dx2)*1024)/1024; current.y = rint((current.y+dy2)*1024)/1024; + pt = chunkalloc(sizeof(SplinePoint)); + pt->hintmask = pending_hm; pending_hm = NULL; + pt->prevcp.x = current.x; pt->prevcp.y = current.y; + current.x = rint((current.x+dx3)*1024)/1024; current.y = rint((current.y+dy3)*1024)/1024; + pt->me.x = current.x; pt->me.y = current.y; + pt->nonextcp = true; + CheckMake(cur->last,pt); + SplineMake3(cur->last,pt); + cur->last = pt; + } else + LogError( _("No previous point on path in curveto in %s\n"), name ); + } + if ( v==24 ) { + current.x = rint((current.x+stack[base++])*1024)/1024; current.y = rint((current.y+stack[base++])*1024)/1024; + if ( cur!=NULL ) { /* In legal code, cur can't be null here, but I got something illegal... */ + pt = chunkalloc(sizeof(SplinePoint)); + pt->hintmask = pending_hm; pending_hm = NULL; + pt->me.x = current.x; pt->me.y = current.y; + pt->noprevcp = true; pt->nonextcp = true; + CheckMake(cur->last,pt); + SplineMake3(cur->last,pt); + cur->last = pt; + } + } + sp = 0; + break; + case 29: /* callgsubr */ + case 10: /* callsubr */ + /* stack[sp-1] contains the number of the subroutine to call */ + if ( sp<1 ) { + LogError( _("Stack underflow on callsubr in %s\n"), name ); + break; + } else if ( pcsp>10 ) { + LogError( _("Too many subroutine calls in %s\n"), name ); + break; + } + s=subrs; if ( v==29 ) s = gsubrs; + if ( s!=NULL ) stack[sp-1] += s->bias; + /* Type2 subrs have a bias that must be added to the subr-number */ + /* Type1 subrs do not. We set the bias on them to 0 */ + if ( s==NULL || stack[sp-1]>=s->cnt || stack[sp-1]<0 || + s->values[(int) stack[sp-1]]==NULL ) + LogError( _("Subroutine number out of bounds in %s\n"), name ); + else { + pcstack[pcsp].type1 = type1; + pcstack[pcsp].len = len; + pcstack[pcsp].subnum = stack[sp-1]; + ++pcsp; + type1 = s->values[(int) stack[sp-1]]; + len = s->lens[(int) stack[sp-1]]; + } + if ( --sp<0 ) sp = 0; + break; + case 11: /* return */ + /* return from a subr outine */ + if ( pcsp<1 ) LogError( _("return when not in subroutine in %s\n"), name ); + else { + --pcsp; + type1 = pcstack[pcsp].type1; + len = pcstack[pcsp].len; + } + break; + case 16: { /* blend -- obsolete type 2 multiple master operator */ + int cnt,i,j; + if ( context->instance_count==0 ) + LogError( _("Attempt to use a multiple master subroutine in a non-mm font.\n") ); + else if ( sp<1 || sp<context->instance_count*stack[sp-1]+1 ) + LogError( _("Too few items on stack for blend in %s\n"), name ); + else { + if ( !context->blend_warn ) { + LogError( _("Use of obsolete blend operator.\n") ); + context->blend_warn = true; + } + cnt = stack[sp-1]; + sp -= context->instance_count*stack[sp-1]+1; + for ( i=0; i<cnt; ++i ) { + for ( j=1; j<context->instance_count; ++j ) + stack[sp+i] += context->blend_values[j]*stack[sp+ + cnt+ i*(context->instance_count-1)+ j-1]; + } + /* there will always be fewer pushes than there were pops */ + /* so I don't bother to check the stack */ + sp += cnt; + } + } + break; + default: + LogError( _("Uninterpreted opcode %d in %s\n"), v, name ); + break; + }} + } + done: + if ( pcsp!=0 ) + LogError( _("end of subroutine reached with no return in %s\n"), name ); + SCCatagorizePoints(ret); + + ret->hstem = HintsAppend(ret->hstem,activeh); activeh=NULL; + ret->vstem = HintsAppend(ret->vstem,activev); activev=NULL; + + if ( cp!=0 ) { int i; + ret->countermasks = galloc(cp*sizeof(HintMask)); + ret->countermask_cnt = cp; + for ( i=0; i<cp; ++i ) { + memcpy(&ret->countermasks[i],counters[i],sizeof(HintMask)); + chunkfree(counters[i],sizeof(HintMask)); + } + } + + /* Even in type1 fonts all paths should be closed. But if we close them at*/ + /* the obvious moveto, that breaks flex hints. So we have a hack here at */ + /* the end which closes any open paths. */ + /* If we do have a PaintType 2 font, then presumably the difference between*/ + /* open and closed paths matters */ + if ( !is_type2 && !context->painttype ) + for ( cur = ret->layers[ly_fore].splines; cur!=NULL; cur = cur->next ) if ( cur->first->prev==NULL ) { + CheckMake(cur->last,cur->first); + SplineMake3(cur->last,cur->first); + cur->last = cur->first; + } + + /* Oh, I see. PS and TT disagree on which direction to use, so Fontographer*/ + /* chose the TT direction and we must reverse postscript */ + for ( cur = ret->layers[ly_fore].splines; cur!=NULL; cur = cur->next ) + SplineSetReverse(cur); + if ( ret->hstem==NULL && ret->vstem==NULL ) + ret->manualhints = false; + if ( !is_type2 && context->instance_count!=0 ) { + UnblendFree(ret->hstem); + UnblendFree(ret->vstem); + } + ret->hstem = HintCleanup(ret->hstem,true,context->instance_count); + ret->vstem = HintCleanup(ret->vstem,true,context->instance_count); + SCGuessHHintInstancesList(ret,ly_fore); + SCGuessVHintInstancesList(ret,ly_fore); + ret->hconflicts = StemListAnyConflicts(ret->hstem); + ret->vconflicts = StemListAnyConflicts(ret->vstem); + if ( context->instance_count==1 && !ret->hconflicts && !ret->vconflicts ) + SCClearHintMasks(ret,ly_fore,false); + HintsRenumber(ret); + if ( name!=NULL && strcmp(name,".notdef")!=0 ) + ret->widthset = true; +return( ret ); +} |