Permalink
Fetching contributors…
Cannot retrieve contributors at this time
4667 lines (4449 sloc) 140 KB
/* Copyright (C) 2000-2012 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 "fontforge.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 {
const char *macro;
char *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;
#define GARBAGE_MAX 64
struct garbage {
int cnt;
struct garbage *next;
struct pskeyval *entries[GARBAGE_MAX];
int16 cnts[GARBAGE_MAX];
};
static void AddTok(GrowBuf *gb,char *buf,int islit) {
if ( islit )
GrowBufferAdd(gb,'/');
GrowBufferAddStr(gb,buf);
GrowBufferAdd(gb,' ');
}
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,
pt_showpage };
static const 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", "=", "==",
"showpage",
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 const 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;
if (io->start != NULL) free(io->start); io->start = NULL;
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 = calloc(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 = calloc(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;
if (io->start != NULL) free(io->start); io->start = NULL;
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, const size_t bsize) {
_IO *io = wrapper->top, *iop;
int wasstopped;
while ( io->prev!=NULL ) {
iop = io->prev;
wasstopped = io->isstopped;
if (io->start != NULL) free(io->start); io->start = NULL;
free(io);
if ( wasstopped ) {
wrapper->top = iop;
if ( sp<(int)bsize ) {
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 CheckCodePointsComment(IO *wrapper) {
/* Check to see if this encoding includes the special comment we use */
/* to indicate that the encoding is based on unicode code points rather */
/* than glyph names */
char commentbuffer[128], *pt;
int ch;
/* Eat whitespace and comments. Comments last to eol (or formfeed) */
while ( isspace(ch = nextch(wrapper)) );
if ( ch!='%' ) {
unnextch(ch,wrapper);
return( false );
}
pt = commentbuffer;
while ( (ch=nextch(wrapper))!=EOF && ch!='\r' && ch!='\n' && ch!='\f' ) {
if ( pt-commentbuffer < (ptrdiff_t)sizeof(commentbuffer)-1 )
*pt++ = ch;
}
*pt = '\0';
if ( strcmp(commentbuffer," Use codepoints.")== 0 )
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];
}
}
int MatIsIdentity(real transform[6]) {
return( transform[0]==1 && transform[3]==1 && transform[1]==0 && transform[2]==0 &&
transform[4]==0 && transform[5]==0 );
}
static void ECCategorizePoints( EntityChar *ec ) {
Entity *ent;
for ( ent=ec->splines; ent!=NULL; ent=ent->next ) if ( ent->type == et_splines ) {
SPLCategorizePoints( ent->u.splines.splines );
SPLCategorizePoints( 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 = malloc(dict->max*sizeof(struct pskeyval));
} else {
dict->max += 30;
dict->entries = realloc(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 = malloc(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 = calloc(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(unsigned sp, struct psstack *stack,size_t 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;
}
}
}
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);
}
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;
default:
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 = calloc(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 uint8 *StringToBytes(struct psstack *stackel,int *len) {
char *pt;
uint8 *upt, *base, *ret;
int half, sofar, val, nesting;
int i,j;
pt = stackel->u.str;
if ( stackel->type==ps_instr ) {
/* imagemask operators take strings or procedures or files */
/* we support strings, or procedures containing strings */
while ( isspace(*pt)) ++pt;
if ( *pt=='{' || *pt=='[' ) ++pt;
while ( isspace(*pt)) ++pt;
} else if ( stackel->type!=ps_string )
return( NULL );
upt = base = malloc(65536+1); /* Maximum size of ps string */
if ( *pt=='(' ) {
/* A conventional string */
++pt;
nesting = 0;
while ( *pt!='\0' && (nesting!=0 || *pt!=')') ) {
if ( *pt=='(' ) {
++nesting;
*upt++ = *pt++;
} else if ( *pt==')' ) {
--nesting;
*upt++ = *pt++;
} else if ( *pt=='\r' || *pt=='\n' ) {
/* any of lf, cr, crlf gets translated to \n */
if ( *pt=='\r' && pt[1]=='\n' ) ++pt;
*upt++ = '\n';
++pt;
} else if ( *pt!='\\' ) {
*upt++ = *pt++;
} else {
++pt;
if ( *pt=='\r' || *pt=='\n' ) {
/* any of \lf, \cr, \crlf gets ignored */
if ( *pt=='\r' && pt[1]=='\n' ) ++pt;
++pt;
} else if ( *pt=='n' ) {
*upt++ = '\n';
++pt;
} else if ( *pt=='r' ) {
*upt++ = '\r';
++pt;
} else if ( *pt=='t' ) {
*upt++ = '\t';
++pt;
} else if ( *pt=='b' ) {
*upt++ = '\b';
++pt;
} else if ( *pt=='f' ) {
*upt++ = '\f';
++pt;
} else if ( *pt>='0' && *pt<='7' ) {
if ( pt[1]<'0' || pt[1]>'7' ) /* This isn't really legal postscript */
*upt++ = *pt++ - '0';
else if ( pt[2]<'0' || pt[2]>'7' ) {/* 3 octal digits are required */
*upt++ = ((*pt - '0')<<3) + (pt[1]-'0');
pt += 2;
} else {
*upt++ = ((*pt - '0')<<6) + ((pt[1]-'0')<<3) + (pt[2]-'0');
pt += 3;
}
} else if ( *pt=='(' || *pt==')' || *pt=='\\' )
*upt++ = *pt++;
else {
LogError( _("Unknown character after backslash in literal string.\n"));
*upt++ = *pt++;
}
}
}
} else if ( *pt!='<' ) {
LogError( _("Unknown string type\n" ));
free(base);
return( NULL );
} else if ( pt[1]!='~' ) {
/* A hex string. Ignore any characters which aren't hex */
half = sofar = 0;
++pt;
while ( *pt!='>' && *pt!='\0' ) {
if ( *pt>='a' && *pt<='f' )
val = *pt++-'a'+10;
else if ( *pt>='A' && *pt<='F' )
val = *pt++-'A'+10;
else if ( isdigit(*pt))
val = *pt++-'0';
else {
++pt; /* Not hex */
continue;
}
if ( !half ) {
half = true;
sofar = val<<4;
} else {
*upt++ = sofar|val;
half = false;
}
}
if ( half )
*upt++ = sofar;
} else {
/* An ASCII-85 string */
/* c1 c2 c3 c4 c5 (c>='!' && c<='u') => ((c1-'!')*85+(c2-'!'))*85... */
/* z => 32bits of 0 */
pt += 2;
while ( *pt!='\0' && *pt!='~' ) {
if ( upt-base+4 > 65536 )
break;
if ( *pt=='z' ) {
*upt++ = 0;
*upt++ = 0;
*upt++ = 0;
*upt++ = 0;
++pt;
} else if ( *pt>='!' && *pt<='u' ) {
val = 0;
for ( i=0; i<5 && *pt>='!' && *pt<='u'; ++i )
val = (val*85) + *pt++ - '!';
for ( j=i; j<5; ++j )
val *= 85;
*upt++ = val>>24 ;
if ( i>2 )
*upt++ = (val>>16)&0xff;
if ( i>3 )
*upt++ = (val>>8 )&0xff;
if ( i>4 )
*upt++ = val &0xff;
if ( i<5 )
break;
} else if ( isspace( *pt ) ) {
++pt;
} else
break;
}
}
*len = upt-base;
ret = malloc(upt-base);
memcpy(ret,base,upt-base);
free(base);
return(ret);
}
static int PSAddImagemask(EntityChar *ec,struct psstack *stack,int sp,
real transform[6],Color fillcol) {
uint8 *data;
int datalen, width, height, polarity;
real trans[6];
struct _GImage *base;
GImage *gi;
Entity *ent;
int i,j;
if ( sp<5 || (stack[sp-1].type!=ps_instr && stack[sp-1].type!=ps_string)) {
LogError( _("FontForge does not support dictionary based imagemask operators.\n" ));
return( sp-1 );
}
if ( stack[sp-2].type!=ps_array || stack[sp-2].u.dict.cnt!=6 ) {
LogError( _("Fourth argument of imagemask must be a 6-element transformation matrix.\n" ));
return( sp-5 );
}
if ( stack[sp-3].type!=ps_bool ) {
LogError( _("Third argument of imagemask must be a boolean.\n" ));
return( sp-5 );
}
polarity = stack[sp-3].u.tf;
if ( stack[sp-4].type!=ps_num || stack[sp-5].type!=ps_num ) {
LogError( _("First and second arguments of imagemask must be integers.\n" ));
return( sp-5 );
}
height = stack[sp-4].u.val;
width = stack[sp-5].u.val;
data = StringToBytes(&stack[sp-1],&datalen);
if ( width<=0 || height<=0 || ((width+7)/8)*height>datalen ) {
LogError( _("Width or height arguments to imagemask contain invalid values\n(either negative or they require more data than provided).\n" ));
free(data);
return( sp-5 );
}
trans[0] = stack[sp-2].u.dict.entries[0].u.val;
trans[1] = stack[sp-2].u.dict.entries[1].u.val;
trans[2] = stack[sp-2].u.dict.entries[2].u.val;
trans[3] = stack[sp-2].u.dict.entries[3].u.val;
trans[4] = stack[sp-2].u.dict.entries[4].u.val;
trans[5] = stack[sp-2].u.dict.entries[5].u.val;
gi = GImageCreate(it_mono,width,height);
base = gi->u.image;
base->trans = 1;
if ( polarity ) {
for ( i=0; i<datalen; ++i )
data[i] ^= 0xff;
}
if ( trans[0]>0 && trans[3]<0 )
memcpy(base->data,data,datalen);
else if ( trans[0]>0 && trans[3]>0 ) {
for ( i=0; i<height; ++i )
memcpy(base->data+i*base->bytes_per_line,data+(height-i)*base->bytes_per_line,
base->bytes_per_line);
} else if ( trans[0]<0 && trans[3]<0 ) {
for ( i=0; i<height; ++i ) for ( j=0; j<width; ++j ) {
if ( data[i*base->bytes_per_line+ (j>>3)]&(0x80>>(j&7)) )
base->data[i*base->bytes_per_line + ((width-j-1)>>3)] |=
(0x80>>((width-j-1)&7));
}
} else {
for ( i=0; i<height; ++i ) for ( j=0; j<width; ++j ) {
if ( data[i*base->bytes_per_line+ (j>>3)]&(0x80>>(j&7)) )
base->data[(height-i-1)*base->bytes_per_line + ((width-j-1)>>3)] |=
(0x80>>((width-j-1)&7));
}
}
free(data);
ent = calloc(1,sizeof(Entity));
ent->type = et_image;
ent->u.image.image = gi;
memcpy(ent->u.image.transform,transform,sizeof(real[6]));
ent->u.image.transform[0] /= width;
ent->u.image.transform[3] /= height;
ent->u.image.transform[5] += height;
ent->u.image.col = fillcol;
ent->next = ec->splines;
ec->splines = ent;
return( sp-5 );
}
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 ) {
free(glyphname);
return; /* Hunh. I don't understand it. I give up */
}
tok = nextpstoken(wrapper,&dval,tokbuf,toksize);
if ( strcmp(tokbuf,"exec")!=0 ) {
free(glyphname);
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;
unsigned 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;
int warned = 0;
struct garbage tofrees;
SplineSet *clippath = NULL;
char *tokbuf;
const int tokbufsize = 2*65536+10;
tokbuf = malloc(tokbufsize);
locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
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<sizeof(stack)/sizeof(stack[0]) ) {
stack[sp].type = ps_bool;
stack[sp++].u.tf = false;
}
}
if ( sp>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<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((char *)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<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<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<sizeof(stack)/sizeof(stack[0]) ) {
stack[sp].type = ps_num;
stack[sp++].u.val = dval;
}
break;
case pt_string:
if ( sp<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<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<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<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<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 ( i>=0 && sp>(unsigned)i ) {
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-2].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-2].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-2].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,sizeof(stack)/sizeof(stack[0]));
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<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,&current,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,&current,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,&current,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<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<sizeof(stack)/sizeof(stack[0]) ) {
stack[sp].type = ps_num;
stack[sp++].u.val = linewidth;
}
break;
case pt_currentdash:
if ( sp+1<sizeof(stack)/sizeof(stack[0]) ) {
struct pskeydict dict;
for ( i=0; i<DASH_MAX && dashes[i]!=0; ++i );
dict.cnt = dict.max = i;
dict.entries = calloc(i,sizeof(struct pskeyval));
dict.is_executable = false;
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<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<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,g,bl;
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;
default:
break;
}
fore = COLOR_CREATE(r,g,bl);
}
sp -= 3;
}
break;
case pt_currentcmykcolor:
if ( sp+3<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<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:
i = PSAddImagemask(ec,stack,sp,transform,fore);
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;
/* 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<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<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<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<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<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<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<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<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<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<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<sizeof(stack)/sizeof(stack[0]) ) {
stack[sp++].type = ps_mark;
}
break;
case pt_counttomark:
for ( i=0; (unsigned)i<sp; ++i )
if ( stack[sp-1-i].type==ps_mark )
break;
if ( (unsigned)i==sp )
LogError( _("No mark in counttomark\n") );
else if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
stack[sp].type = ps_num;
stack[sp++].u.val = i;
}
break;
case pt_cleartomark:
for ( i=0; (unsigned)i<sp; ++i )
if ( stack[sp-1-i].type==ps_mark )
break;
if ( (unsigned)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; (unsigned)i<sp; ++i )
if ( stack[sp-1-i].type==ps_mark )
break;
if ( (unsigned)i==sp )
LogError( _("No mark in ] (close array)\n") );
else {
struct pskeydict dict;
dict.cnt = dict.max = i;
dict.entries = calloc(i,sizeof(struct pskeyval));
dict.is_executable = false;
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.cnt = dict.max = stack[sp-1].u.val;
dict.entries = calloc(dict.cnt,sizeof(struct pskeyval));
dict.is_executable = false;
/* 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 = calloc(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; (unsigned)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);
ECCategorizePoints(ec);
if ( ec->width == UNDEFINED_WIDTH )
ec->width = wrapper->advance_width;
switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
free(tokbuf);
}
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 );
}
static SplinePointList *SplinesFromLayers(SplineChar *sc,int *flags, int tostroke) {
int layer;
SplinePointList *head=NULL, *last, *new, *nlast, *temp, *each, *transed;
StrokeInfo si;
/*SplineSet *spl;*/
int handle_eraser;
real inversetrans[6], transform[6];
int changed;
if ( tostroke ) {
for ( layer=ly_fore; layer<sc->layer_cnt; ++layer ) {
if ( sc->layers[layer].splines==NULL )
continue;
else if ( head==NULL )
head = sc->layers[layer].splines;
else
last->next = sc->layers[layer].splines;
for ( last = sc->layers[layer].splines; last->next!=NULL; last=last->next );
sc->layers[layer].splines = NULL;
}
return( head );
}
if ( *flags==-1 )
*flags = PsStrokeFlagsDlg();
if ( *flags & sf_correctdir ) {
for ( layer=ly_fore; layer<sc->layer_cnt; ++layer ) if ( sc->layers[layer].dofill )
SplineSetsCorrect(sc->layers[layer].splines,&changed);
}
handle_eraser = *flags & sf_handle_eraser;
for ( layer=ly_fore; layer<sc->layer_cnt; ++layer ) {
if ( sc->layers[layer].dostroke ) {
memset(&si,'\0',sizeof(si));
si.join = sc->layers[layer].stroke_pen.linejoin;
si.cap = sc->layers[layer].stroke_pen.linecap;
si.radius = sc->layers[layer].stroke_pen.width/2.0f;
if ( sc->layers[layer].stroke_pen.width==WIDTH_INHERITED )
si.radius = .5;
if ( si.cap == lc_inherited ) si.cap = lc_butt;
if ( si.join == lj_inherited ) si.join = lj_miter;
new = NULL;
memcpy(transform,sc->layers[layer].stroke_pen.trans,4*sizeof(real));
transform[4] = transform[5] = 0;
MatInverse(inversetrans,transform);
transed = SplinePointListTransform(SplinePointListCopy(
sc->layers[layer].splines),inversetrans,tpt_AllPoints);
for ( each = transed; each!=NULL; each=each->next ) {
temp = SplineSetStroke(each,&si,sc->layers[layer].order2);
if ( new==NULL )
new=temp;
else
nlast->next = temp;
if ( temp!=NULL )
for ( nlast=temp; nlast->next!=NULL; nlast=nlast->next );
}
new = SplinePointListTransform(new,transform,tpt_AllPoints);
SplinePointListsFree(transed);
if ( handle_eraser && sc->layers[layer].stroke_pen.brush.col==0xffffff ) {
head = EraseStroke(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 ( sc->layers[layer].dofill ) {
if ( handle_eraser && sc->layers[layer].fill_brush.col==0xffffff ) {
head = EraseStroke(sc,head,sc->layers[layer].splines);
last = head;
if ( last!=NULL )
for ( ; last->next!=NULL; last=last->next );
} else {
new = SplinePointListCopy(sc->layers[layer].splines);
if ( head==NULL )
head = new;
else
last->next = new;
if ( new!=NULL )
for ( last = new; last->next!=NULL; last=last->next );
}
}
}
return( head );
}
void SFSplinesFromLayers(SplineFont *sf,int tostroke) {
/* User has turned off multi-layer, flatten the font */
int i, layer;
int flags= -1;
Layer *new;
CharViewBase *cv;
for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
SplineChar *sc = sf->glyphs[i];
SplineSet *splines = SplinesFromLayers(sc,&flags,tostroke);
RefChar *head=NULL, *last=NULL;
for ( layer=ly_fore; layer<sc->layer_cnt; ++layer ) {
if ( head==NULL )
head = last = sc->layers[layer].refs;
else
last->next = sc->layers[layer].refs;
if ( last!=NULL )
while ( last->next!=NULL ) last = last->next;
sc->layers[layer].refs = NULL;
}
new = calloc(2,sizeof(Layer));
new[ly_back] = sc->layers[ly_back];
memset(&sc->layers[ly_back],0,sizeof(Layer));
LayerDefault(&new[ly_fore]);
new[ly_fore].splines = splines;
new[ly_fore].refs = head;
for ( layer=ly_fore; layer<sc->layer_cnt; ++layer ) {
SplinePointListsMDFree(sc,sc->layers[layer].splines);
RefCharsFree(sc->layers[layer].refs);
ImageListsFree(sc->layers[layer].images);
}
free(sc->layers);
sc->layers = new;
sc->layer_cnt = 2;
for ( cv=sc->views; cv!=NULL; cv=cv->next ) {
cv->layerheads[dm_back] = &sc->layers[ly_back];
cv->layerheads[dm_fore] = &sc->layers[ly_fore];
}
}
SFReinstanciateRefs(sf);
}
void SFSetLayerWidthsStroked(SplineFont *sf, real strokewidth) {
int i;
/* We changed from a stroked font to a multilayered font */
for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
SplineChar *sc = sf->glyphs[i];
sc->layers[ly_fore].dofill = false;
sc->layers[ly_fore].dostroke = true;
sc->layers[ly_fore].stroke_pen.width = strokewidth;
}
}
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);
}
}
}
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;
}
}
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.join = ent->u.splines.join;
si.cap = ent->u.splines.cap;
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 == lj_inherited ) si.join = lj_miter;
new = NULL;
MatInverse(inversetrans,ent->u.splines.transform);
transed = SplinePointListTransform(SplinePointListCopy(
ent->u.splines.splines),inversetrans,tpt_AllPoints);
for ( each = transed; each!=NULL; each=each->next ) {
temp = SplineSetStroke(each,&si,false);
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,tpt_AllPoints);
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 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 );
}
SplinePointList *SplinesFromEntities(Entity *ent,int *flags,int is_stroked) {
EntityChar ec;
memset(&ec,'\0',sizeof(ec));
ec.splines = ent;
return( SplinesFromEntityChar(&ec,flags,is_stroked));
}
SplinePointList *SplinePointListInterpretPS(FILE *ps,int flags,int is_stroked, int *width) {
EntityChar ec;
SplineChar sc;
memset(&ec,'\0',sizeof(ec));
ec.width = ec.vwidth = UNDEFINED_WIDTH;
memset(&sc,0,sizeof(sc)); sc.name = "<No particular character>";
ec.sc = &sc;
InterpretPS(ps,NULL,&ec,NULL);
if ( width!=NULL )
*width = ec.width;
return( SplinesFromEntityChar(&ec,&flags,is_stroked));
}
Entity *EntityInterpretPS(FILE *ps,int *width) {
EntityChar ec;
memset(&ec,'\0',sizeof(ec));
ec.width = ec.vwidth = UNDEFINED_WIDTH;
InterpretPS(ps,NULL,&ec,NULL);
if ( width!=NULL )
*width = ec.width;
return( ec.splines );
}
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) {
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->layer_cnt = 1;
SCAppendEntityLayers(sc,ec.splines);
if ( sc->layer_cnt==1 ) ++sc->layer_cnt;
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;
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 = realloc(cp->keys,cp->cnt*sizeof(char *));
cp->values = realloc(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);
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);
}
}
}
}
/* Slurp up an encoding in the form:
/Enc-name [
/charname
...
] def
We're not smart here no: 0 1 255 {1 index exch /.notdef put} for */
Encoding *PSSlurpEncodings(FILE *file) {
char *names[1024];
int32 encs[1024];
Encoding *item, *head = NULL, *last;
char *encname;
char tokbuf[200];
IO wrapper;
real dval;
size_t i, any;
int max, enc, codepointsonly, tok;
wrapper.top = NULL;
wrapper.advance_width = UNDEFINED_WIDTH;
pushio(&wrapper,file,NULL,0);
while ( (tok = nextpstoken(&wrapper,&dval,tokbuf,sizeof(tokbuf)))!=pt_eof ) {
encname = NULL;
if ( tok==pt_namelit ) {
encname = copy(tokbuf);
tok = nextpstoken(&wrapper,&dval,tokbuf,sizeof(tokbuf));
}
if ( tok!=pt_openarray && tok!=pt_opencurly )
return( head );
for ( i=0; i<sizeof(names)/sizeof(names[0]); ++i ) {
encs[i] = -1;
names[i]=NULL;
}
codepointsonly = CheckCodePointsComment(&wrapper);
max = -1; any = 0;
for (i = 0; (tok = nextpstoken(&wrapper,&dval,tokbuf,sizeof(tokbuf)))!=pt_eof &&
tok!=pt_closearray && tok!=pt_closecurly;
i++) {
if ( tok==pt_namelit && i<sizeof(names)/sizeof(names[0]) ) {
max = i;
if ( strcmp(tokbuf,".notdef")==0 ) {
encs[i] = -1;
} else if ( (enc=UniFromName(tokbuf,ui_none,&custom))!=-1 ) {
encs[i] = enc;
/* Used not to do this, but there are several legal names */
/* for some slots and people get unhappy (rightly) if we */
/* use the wrong one */
names[i] = copy(tokbuf);
any = 1;
} else {
names[i] = copy(tokbuf);
any = 1;
}
}
}
if ( encname!=NULL ) {
tok = nextpstoken(&wrapper,&dval,tokbuf,sizeof(tokbuf));
if ( tok==pt_def ) {
/* Good */
} else {
/* TODO! */
/* I guess it's not good... */
}
}
if ( max!=-1 ) {
if ( ++max<256 ) max = 256;
item = calloc(1,sizeof(Encoding));
item->enc_name = encname;
item->char_cnt = max;
item->unicode = malloc(max*sizeof(int32));
memcpy(item->unicode,encs,max*sizeof(int32));
if ( any && !codepointsonly ) {
item->psnames = calloc(max,sizeof(char *));
memcpy(item->psnames,names,max*sizeof(char *));
} else {
for ( i=0; i<max; ++i )
free(names[i]);
}
if ( head==NULL )
head = item;
else
last->next = item;
last = item;
}
}
return( head );
}
int EvaluatePS(char *str,real *stack,int size) {
EntityChar ec;
RetStack rs;
memset(&ec,'\0',sizeof(ec));
memset(&rs,'\0',sizeof(rs));
rs.max = size;
rs.stack = stack;
InterpretPS(NULL,str,&ec,&rs);
return( rs.cnt );
}
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, 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 */
if ( is_type2 )
LogError( _("%s\'s dotsection operator is deprecated for Type2\n"), name );
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 */
if ( is_type2 )
LogError( _("%s\'s vstem3 operator is not supported for Type2\n"), name );
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;