diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/autohint.c')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/autohint.c | 2143 |
1 files changed, 2143 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/autohint.c b/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/autohint.c new file mode 100644 index 00000000000..a55a3d3d637 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/autohint.c @@ -0,0 +1,2143 @@ +/* Copyright (C) 2000-2008 by George Williams */ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + + * The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "pfaedit.h" +#include <stdio.h> +#include <math.h> +#include "splinefont.h" +#include "views.h" +#include "stemdb.h" +#include <utype.h> +#include <chardata.h> +#include "edgelist.h" + +/* to create a type 1 font we must come up with the following entries for the + private dictionary: + BlueValues -- an array of 2n entries where Blue[2i]<Blue[2i+1] max n=7, Blue[i]>0 + OtherBlues -- (optional) OtherBlue[i]<0 + (blue zones should be at least 3 units appart) + StdHW -- (o) array with one entry, standard hstem height + StdVW -- (o) ditto vstem width + StemSnapH -- (o) up to 12 numbers containing various hstem heights (includes StdHW), small widths first + StemSnapV -- (o) ditto, vstem +This file has routines to figure out at least some of these + +Also other routines to guess at good per-character hints +*/ + +static void AddBlue(real val, real array[5], int force) { + val = rint(val); + if ( !force && (val<array[0]-array[1] || val >array[0]+array[1] )) +return; /* Outside of one sd */ + if ( array[3]==0 && array[4]==0 ) + array[3] = array[4] = val; + else if ( val>array[4] ) + array[4] = val; + else if ( val<array[3] ) + array[3] = val; +} + +static void MergeZones(real zone1[5], real zone2[5]) { + if ( zone1[2]!=0 && zone2[2]!=0 && + ((zone1[4]+3>zone2[3] && zone1[3]<=zone2[3]) || + (zone2[4]+3>zone1[3] && zone2[3]<=zone1[3]) )) { + if (( zone2[0]<zone1[0]-zone1[1] || zone2[0] >zone1[0]+zone1[1] ) && + ( zone1[0]<zone2[0]-zone2[1] || zone1[0] >zone2[0]+zone2[1] )) + /* the means of the zones are too far appart, don't merge em */; + else { + if ( zone1[0]<zone2[0] ) zone2[0] = zone1[0]; + if ( zone1[1]>zone2[1] ) zone2[1] = zone1[1]; + } + zone1[2] = 0; + } +} + +/* I can deal with latin, greek and cyrillic because the they all come from */ +/* the same set of letter shapes and have all evolved together and have */ +/* various common features (ascenders, descenders, lower case, etc.). Other */ +/* scripts don't fit */ +void FindBlues( SplineFont *sf, int layer, real blues[14], real otherblues[10]) { + real caph[5], xh[5], ascenth[5], digith[5], descenth[5], base[5]; + real otherdigits[5]; + int i, j, k; + DBounds b; + + /* Go through once to get some idea of the average value so we can weed */ + /* out undesireables */ + caph[0] = caph[1] = caph[2] = xh[0] = xh[1] = xh[2] = 0; + ascenth[0] = ascenth[1] = ascenth[2] = digith[0] = digith[1] = digith[2] = 0; + descenth[0] = descenth[1] = descenth[2] = base[0] = base[1] = base[2] = 0; + otherdigits[0] = otherdigits[1] = otherdigits[2] = 0; + for ( i=0; i<sf->glyphcnt; ++i ) { + if ( sf->glyphs[i]!=NULL && sf->glyphs[i]->layers[layer].splines!=NULL ) { + int enc = sf->glyphs[i]->unicodeenc; + const unichar_t *upt; + if ( enc<0x10000 && isalnum(enc) && + ((enc>=32 && enc<128 ) || enc == 0xfe || enc==0xf0 || enc==0xdf || + enc==0x131 || + (enc>=0x391 && enc<=0x3f3 ) || + (enc>=0x400 && enc<=0x4e9 ) )) { + /* no accented characters (or ligatures) */ + if ( unicode_alternates[enc>>8]!=NULL && + (upt =unicode_alternates[enc>>8][enc&0xff])!=NULL && + upt[1]!='\0' ) + continue; + SplineCharFindBounds(sf->glyphs[i],&b); + if ( b.miny==0 && b.maxy==0 ) + continue; + if ( enc=='g' || enc=='j' || enc=='p' || enc=='q' || enc=='y' || + enc==0xfe || + enc==0x3c1 /* rho */ || + enc==0x3c6 /* phi */ || + enc==0x3c7 /* chi */ || + enc==0x3c8 /* psi */ || + enc==0x440 /* cyr er */ || + enc==0x443 /* cyr u */ || + enc==0x444 /* cyr ef */) { + descenth[0] += b.miny; + descenth[1] += b.miny*b.miny; + ++descenth[2]; + } else if ( enc=='x' || enc=='r' || enc=='o' || enc=='e' || + enc=='s' || enc=='c' || enc=='h' || enc=='k' || + enc=='l' || enc=='m' || enc=='n' || + enc==0x3b5 /* epsilon */ || + enc==0x3b9 /* iota */ || + enc==0x3ba /* kappa */ || + enc==0x3bf /* omicron */ || + enc==0x3c3 /* sigma */ || + enc==0x3c5 /* upsilon */ || + enc==0x430 /* cyr a */ || + enc==0x432 /* cyr ve */ || + enc==0x433 /* cyr ge */ || + enc==0x435 /* cyr e */ || + enc==0x436 /* cyr zhe */ || + enc==0x438 /* cyr i */ || + enc==0x43a /* cyr ka */ || + enc==0x43d /* cyr en */ || + enc==0x43e /* cyr o */ || + enc==0x441 /* cyr es */ || + enc==0x445 /* cyr ha */ || + enc==0x447 /* cyr che */ || + enc==0x448 /* cyr sha */ || + enc==0x44f /* cyr ya */ ){ + base[0] += b.miny; + base[1] += b.miny*b.miny; + ++base[2]; + } + /* careful of lowercase digits, 6 and 8 should be ascenders */ + if ( enc=='6' || enc=='8' ) { + digith[0] += b.maxy; + digith[1] += b.maxy*b.maxy; + ++digith[2]; + } else if ( enc<0x10000 && isdigit(enc) ) { + otherdigits[0] += b.maxy; + otherdigits[1] += b.maxy*b.maxy; + ++otherdigits[2]; + } else if ( enc<0x10000 && isupper(enc) && enc!=0x462 && enc!=0x490 ) { + caph[0] += b.maxy; + caph[1] += b.maxy*b.maxy; + ++caph[2]; + } else if ( enc=='b' || enc=='d' || enc=='f' || enc=='h' || enc=='k' || + enc == 'l' || enc==0xf0 || enc==0xfe || enc == 0xdf || + enc == 0x3b2 || enc==0x3b6 || enc==0x3b8 || enc==0x3bb || + enc == 0x3be || + enc == 0x431 /* cyr be */ /* || enc == 0x444 - ef may have varible height */) { + ascenth[0] += b.maxy; + ascenth[1] += b.maxy*b.maxy; + ++ascenth[2]; + } else if ( enc=='c' || enc=='e' || enc=='o' || enc=='s' || enc=='u' || + enc=='u' || enc=='v' || enc=='w' || enc=='x' || enc=='y' || + enc=='z' || + enc==0x3b5 /* epsilon */ || + enc==0x3b9 /* iota */ || + enc==0x3ba /* kappa */ || + enc==0x3bc /* mu */ || + enc==0x3bd /* nu */ || + enc==0x3bf /* omicron */ || + enc==0x3c0 /* pi */ || + enc==0x3c1 /* rho */ || + enc==0x3c5 /* upsilon */ || + enc==0x433 /* cyr ge */ || + enc==0x435 /* cyr e */ || + enc==0x436 /* cyr zhe */ || + enc==0x438 /* cyr i */ || + enc==0x43b /* cyr el */ || + enc==0x43d /* cyr en */ || + enc==0x43e /* cyr o */ || + enc==0x43f /* cyr pe */ || + enc==0x440 /* cyr er */ || + enc==0x441 /* cyr es */ || + enc==0x442 /* cyr te */ || + enc==0x443 /* cyr u */ || + enc==0x445 /* cyr ha */ || + enc==0x446 /* cyr tse */ || + enc==0x447 /* cyr che */ || + enc==0x448 /* cyr sha */ || + enc==0x449 /* cyr shcha */ || + enc==0x44a /* cyr hard sign */ || + enc==0x44b /* cyr yery */ || + enc==0x44c /* cyr soft sign */ || + enc==0x44d /* cyr reversed e */ || + enc==0x44f /* cyr ya */ ) { + xh[0] += b.maxy; + xh[1] += b.maxy*b.maxy; + ++xh[2]; + } + } + } + if ( !ff_progress_next()) + break; + } + if ( otherdigits[2]>0 && digith[2]>0 ) { + if ( otherdigits[0]/otherdigits[2] >= .95*digith[0]/digith[2] ) { + /* all digits are about the same height, not lowercase */ + digith[0] += otherdigits[0]; + digith[1] += otherdigits[1]; + digith[2] += otherdigits[2]; + } + } + + if ( xh[2]>1 ) { + xh[1] = sqrt((xh[1]-xh[0]*xh[0]/xh[2])/(xh[2]-1)); + xh[0] /= xh[2]; + } + if ( ascenth[2]>1 ) { + ascenth[1] = sqrt((ascenth[1]-ascenth[0]*ascenth[0]/ascenth[2])/(ascenth[2]-1)); + ascenth[0] /= ascenth[2]; + } + if ( caph[2]>1 ) { + caph[1] = sqrt((caph[1]-caph[0]*caph[0]/caph[2])/(caph[2]-1)); + caph[0] /= caph[2]; + } + if ( digith[2]>1 ) { + digith[1] = sqrt((digith[1]-digith[0]*digith[0]/digith[2])/(digith[2]-1)); + digith[0] /= digith[2]; + } + if ( base[2]>1 ) { + base[1] = sqrt((base[1]-base[0]*base[0]/base[2])/(base[2]-1)); + base[0] /= base[2]; + } + if ( descenth[2]>1 ) { + descenth[1] = sqrt((descenth[1]-descenth[0]*descenth[0]/descenth[2])/(descenth[2]-1)); + descenth[0] /= descenth[2]; + } + + /* we'll accept values between +/- 1sd of the mean */ + /* array[0] == mean, array[1] == sd, array[2] == cnt, array[3]=min, array[4]==max */ + if ( base[0]+base[1]<0 ) base[1] = -base[0]; /* Make sure 0 is within the base bluezone */ + caph[3] = caph[4] = 0; + xh[3] = xh[4] = 0; + ascenth[3] = ascenth[4] = 0; + digith[3] = digith[4] = 0; + descenth[3] = descenth[4] = 0; + base[3] = base[4] = 0; + for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) { + int enc = sf->glyphs[i]->unicodeenc; + const unichar_t *upt; + if ( enc<0x10000 && isalnum(enc) && + ((enc>=32 && enc<128 ) || enc == 0xfe || enc==0xf0 || enc==0xdf || + (enc>=0x391 && enc<=0x3f3 ) || + (enc>=0x400 && enc<=0x4e9 ) )) { + /* no accented characters (or ligatures) */ + if ( unicode_alternates[enc>>8]!=NULL && + (upt =unicode_alternates[enc>>8][enc&0xff])!=NULL && + upt[1]!='\0' ) + continue; + SplineCharFindBounds(sf->glyphs[i],&b); + if ( b.miny==0 && b.maxy==0 ) + continue; + if ( enc=='g' || enc=='j' || enc=='p' || enc=='q' || enc=='y' || + enc==0xfe || enc == 0x3c6 || enc==0x3c8 || + enc==0x440 || enc==0x443 || enc==0x444) { + AddBlue(b.miny,descenth,false); + } else { + /* O and o get forced into the baseline blue value even if they*/ + /* are beyond 1 sd */ + AddBlue(b.miny,base,enc=='O' || enc=='o'); + } + if ( enc<0x10000 && isdigit(enc)) { + AddBlue(b.maxy,digith,false); + } else if ( enc<0x10000 && isupper(enc)) { + AddBlue(b.maxy,caph,enc=='O'); + } else if ( enc=='b' || enc=='d' || enc=='f' || enc=='h' || enc=='k' || + enc == 'l' || enc=='t' || enc==0xf0 || enc==0xfe || enc == 0xdf || + enc == 0x3b2 || enc==0x3b6 || enc==0x3b8 || enc==0x3bb || + enc == 0x3be || + enc == 0x431 ) { + AddBlue(b.maxy,ascenth,false); + } else if ( enc=='c' || enc=='e' || enc=='o' || enc=='s' || enc=='u' || + enc=='u' || enc=='v' || enc=='w' || enc=='x' || enc=='y' || + enc=='z' || + enc==0x3b5 /* epsilon */ || + enc==0x3b9 /* iota */ || + enc==0x3ba /* kappa */ || + enc==0x3bc /* mu */ || + enc==0x3bd /* nu */ || + enc==0x3bf /* omicron */ || + enc==0x3c0 /* pi */ || + enc==0x3c1 /* rho */ || + enc==0x3c5 /* upsilon */ || + enc==0x433 /* cyr ge */ || + enc==0x435 /* cyr e */ || + enc==0x436 /* cyr zhe */ || + enc==0x438 /* cyr i */ || + enc==0x43b /* cyr el */ || + enc==0x43d /* cyr en */ || + enc==0x43e /* cyr o */ || + enc==0x43f /* cyr pe */ || + enc==0x440 /* cyr er */ || + enc==0x441 /* cyr es */ || + enc==0x442 /* cyr te */ || + enc==0x443 /* cyr u */ || + enc==0x445 /* cyr ha */ || + enc==0x446 /* cyr tse */ || + enc==0x447 /* cyr che */ || + enc==0x448 /* cyr sha */ || + enc==0x449 /* cyr shcha */ || + enc==0x44a /* cyr hard sign */ || + enc==0x44b /* cyr yery */ || + enc==0x44c /* cyr soft sign */ || + enc==0x44d /* cyr reversed e */ || + enc==0x44f /* cyr ya */ ) { + AddBlue(b.maxy,xh,enc=='o' || enc=='x'); + } + } + } + + /* the descent blue zone merges into the base zone */ + MergeZones(descenth,base); + MergeZones(xh,base); + MergeZones(ascenth,caph); + MergeZones(digith,caph); + MergeZones(xh,caph); + MergeZones(ascenth,digith); + MergeZones(xh,digith); + + if ( otherblues!=NULL ) + for ( i=0; i<10; ++i ) + otherblues[i] = 0; + for ( i=0; i<14; ++i ) + blues[i] = 0; + + if ( otherblues!=NULL && descenth[2]!=0 ) { + otherblues[0] = descenth[3]; + otherblues[1] = descenth[4]; + } + i = 0; + if ( base[2]==0 && (xh[2]!=0 || ascenth[2]!=0 || caph[2]!=0 || digith[2]!=0 )) { + /* base line blue value must be present if any other value is */ + /* make one up if we don't have one */ + blues[0] = -20; + blues[1] = 0; + i = 2; + } else if ( base[2]!=0 ) { + blues[0] = base[3]; + blues[1] = base[4]; + i = 2; + } + if ( xh[2]!=0 ) { + blues[i++] = xh[3]; + blues[i++] = xh[4]; + } + if ( caph[2]!=0 ) { + blues[i++] = caph[3]; + blues[i++] = caph[4]; + } + if ( digith[2]!=0 ) { + blues[i++] = digith[3]; + blues[i++] = digith[4]; + } + if ( ascenth[2]!=0 ) { + blues[i++] = ascenth[3]; + blues[i++] = ascenth[4]; + } + + for ( j=0; j<i; j+=2 ) for ( k=j+2; k<i; k+=2 ) { + if ( blues[j]>blues[k] ) { + real temp = blues[j]; + blues[j]=blues[k]; + blues[k] = temp; + temp = blues[j+1]; + blues[j+1] = blues[k+1]; + blues[k+1] = temp; + } + } +} + +void ElFreeEI(EIList *el) { + EI *e, *next; + + for ( e = el->edges; e!=NULL; e = next ) { + next = e->next; + free(e); + } +} + +static int EIAddEdge(Spline *spline, real tmin, real tmax, EIList *el) { + EI *new = gcalloc(1,sizeof(EI)); + real min, max, temp; + Spline1D *s; + real dxdtmin, dxdtmax, dydtmin, dydtmax; + + new->spline = spline; + new->tmin = tmin; + new->tmax = tmax; + + s = &spline->splines[1]; + if (( dydtmin = (3*s->a*tmin + 2*s->b)*tmin + s->c )<0 ) dydtmin = -dydtmin; + if (( dydtmax = (3*s->a*tmax + 2*s->b)*tmax + s->c )<0 ) dydtmax = -dydtmax; + s = &spline->splines[0]; + if (( dxdtmin = (3*s->a*tmin + 2*s->b)*tmin + s->c )<0 ) dxdtmin = -dxdtmin; + if (( dxdtmax = (3*s->a*tmax + 2*s->b)*tmax + s->c )<0 ) dxdtmax = -dxdtmax; + + /*s = &spline->splines[0];*/ + min = ((s->a * tmin + s->b)* tmin + s->c)* tmin + s->d; + max = ((s->a * tmax + s->b)* tmax + s->c)* tmax + s->d; + if ( tmax==1 ) max = spline->to->me.x; /* beware rounding errors */ + if ( !el->leavetiny && floor(min)==floor(max) ) { /* If it doesn't cross a pixel boundary then it might as well be vertical */ + if ( tmin==0 ) max = min; + else if ( tmax==1 ) min = max; + else max = min; + } + if ( min==max ) + new->vert = true; + else if ( min<max ) + new->hup = true; + else { + temp = min; min = max; max=temp; + } + if ( !el->leavetiny && min+1>max ) new->almostvert = true; + if ( 40*dxdtmin<dydtmin ) new->vertattmin = true; + if ( 40*dxdtmax<dydtmax ) new->vertattmax = true; + /*if ( new->vertattmin && new->vertattmax && s->a==0 && s->b==0 ) new->almostvert = true;*/ + new->coordmin[0] = min; new->coordmax[0] = max; + if ( el->coordmin[0]>min ) + el->coordmin[0] = min; + if ( el->coordmax[0]<max ) + el->coordmax[0] = max; + + s = &spline->splines[1]; + min = ((s->a * tmin + s->b)* tmin + s->c)* tmin + s->d; + max = ((s->a * tmax + s->b)* tmax + s->c)* tmax + s->d; + if ( tmax==1 ) max = spline->to->me.y; + if ( !el->leavetiny && floor(min)==floor(max) ) { /* If it doesn't cross a pixel boundary then it might as well be horizontal */ + if ( tmin==0 ) max = min; + else if ( tmax==1 ) min = max; + else max = min; + } + if ( min==max ) + new->hor = true; + else if ( min<max ) + new->vup = true; + else { + temp = min; min = max; max=temp; + } + if ( !el->leavetiny && min+1>max ) new->almosthor = true; + if ( 40*dydtmin<dxdtmin ) new->horattmin = true; + if ( 40*dydtmax<dxdtmax ) new->horattmax = true; + /*if ( new->horattmin && new->horattmax && s->a==0 && s->b==0 ) new->almosthor = true;*/ + new->coordmin[1] = min; new->coordmax[1] = max; + if ( el->coordmin[1]>min ) + el->coordmin[1] = min; + if ( el->coordmax[1]<max ) + el->coordmax[1] = max; + + if ( new->hor && new->vert ) { + /* This spline is too small for us to notice */ + free(new); +return( false ); + } else { + new->next = el->edges; + el->edges = new; + + if ( el->splinelast!=NULL ) + el->splinelast->splinenext = new; + el->splinelast = new; + if ( el->splinefirst==NULL ) + el->splinefirst = new; + +return( true ); + } +} + +static void EIAddSpline(Spline *spline, EIList *el) { + extended ts[6], temp; + int i, j, base, last; + + ts[0] = 0; ts[5] = 1.0; + SplineFindExtrema(&spline->splines[0],&ts[1],&ts[2]); + SplineFindExtrema(&spline->splines[1],&ts[3],&ts[4]); + /* avoid teeny tiny segments, they just confuse us */ + SplineRemoveExtremaTooClose(&spline->splines[0],&ts[1],&ts[2]); + SplineRemoveExtremaTooClose(&spline->splines[1],&ts[3],&ts[4]); + for ( i=0; i<4; ++i ) for ( j=i+1; j<5; ++j ) { + if ( ts[i]>ts[j] ) { + temp = ts[i]; + ts[i] = ts[j]; + ts[j] = temp; + } + } + for ( base=0; ts[base]==-1; ++base); + for ( i=5; i>base ; --i ) { + if ( ts[i]==ts[i-1] ) { + for ( j=i-1; j>base; --j ) + ts[j] = ts[j-1]; + ts[j] = -1; + ++base; + } + } + last = base; + for ( i=base; i<5 ; ++i ) + if ( EIAddEdge(spline,ts[last],ts[i+1],el) ) + last = i+1; +} + +void ELFindEdges(SplineChar *sc, EIList *el) { + Spline *spline, *first; + SplineSet *spl; + + el->sc = sc; + if ( sc->layers[el->layer].splines==NULL ) +return; + + el->coordmin[0] = el->coordmax[0] = sc->layers[el->layer].splines->first->me.x; + el->coordmin[1] = el->coordmax[1] = sc->layers[el->layer].splines->first->me.y; + + for ( spl = sc->layers[el->layer].splines; spl!=NULL; spl = spl->next ) if ( spl->first->prev!=NULL && spl->first->prev->from!=spl->first ) { + first = NULL; + for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) { + EIAddSpline(spline,el); + if ( first==NULL ) first = spline; + } + if ( el->splinefirst!=NULL && spl->first->prev!=NULL ) + el->splinelast->splinenext = el->splinefirst; + el->splinelast = NULL; el->splinefirst = NULL; + } +} + +static int IsBiggerSlope(EI *test, EI *base, int major) { + int other = !major; + real tdo, tdm, bdo, bdm, t; + + if (( major && test->vup ) || (!major && test->hup)) + t = test->tmin; + else + t = test->tmax; + tdm = (3*test->spline->splines[major].a*t + 2*test->spline->splines[major].b)*t + test->spline->splines[major].c; + tdo = (3*test->spline->splines[other].a*t + 2*test->spline->splines[other].b)*t + test->spline->splines[other].c; + + if (( major && base->vup ) || (!major && base->hup)) + t = base->tmin; + else + t = base->tmax; + bdm = (3*base->spline->splines[major].a*t + 2*base->spline->splines[major].b)*t + base->spline->splines[major].c; + bdo = (3*base->spline->splines[other].a*t + 2*base->spline->splines[other].b)*t + base->spline->splines[other].c; + + if ( tdm==0 && bdm==0 ) +return( tdo > bdo ); + if ( tdo==0 ) +return( tdo>0 ); + else if ( bdo==0 ) +return( bdo>0 ); + +return( tdo/tdm > bdo/bdm ); +} + +void ELOrder(EIList *el, int major ) { + int other = !major; + int pos; + EI *ei, *prev, *test; + + el->low = floor(el->coordmin[major]); el->high = ceil(el->coordmax[major]); + el->cnt = el->high-el->low+1; + el->ordered = gcalloc(el->cnt,sizeof(EI *)); + el->ends = gcalloc(el->cnt,1); + + for ( ei = el->edges; ei!=NULL ; ei=ei->next ) { + pos = ceil(ei->coordmax[major])-el->low; + el->ends[pos] = true; + pos = floor(ei->coordmin[major])-el->low; + ei->ocur = (ei->hup == ei->vup)?ei->coordmin[other]:ei->coordmax[other]; + ei->tcur = ((major && ei->vup) || (!major && ei->hup)) ? + ei->tmin: ei->tmax; + if ( major ) { + ei->up = ei->vup; + ei->hv = (ei->vert || ei->almostvert); + ei->hvbottom = ei->vup ? ei->vertattmin : ei->vertattmax; + ei->hvtop =!ei->vup ? ei->vertattmin : ei->vertattmax; + if ( ei->hor || ei->almosthor) + continue; + } else { + ei->up = ei->hup; + ei->hv = (ei->hor || ei->almosthor); + ei->hvbottom = ei->hup ? ei->horattmin : ei->horattmax; + ei->hvtop =!ei->hup ? ei->horattmin : ei->horattmax; + if ( ei->vert || ei->almostvert) + continue; + } + if ( el->ordered[pos]==NULL || ei->ocur<el->ordered[pos]->ocur ) { + ei->ordered = el->ordered[pos]; + el->ordered[pos] = ei; + } else { + for ( prev=el->ordered[pos], test = prev->ordered; test!=NULL; + prev = test, test = test->ordered ) { + if ( test->ocur>ei->ocur || + (test->ocur==ei->ocur && IsBiggerSlope(test,ei,major))) + break; + } + ei->ordered = test; + prev->ordered = ei; + } + } +} + +static HintInstance *HIMerge(HintInstance *into, HintInstance *hi) { + HintInstance *n, *first = NULL, *last = NULL; + + while ( into!=NULL && hi!=NULL ) { + if ( into->begin<hi->begin ) { + n = into; + into = into->next; + } else { + n = hi; + hi = hi->next; + } + if ( first==NULL ) + first = n; + else + last->next = n; + last = n; + } + if ( into!=NULL ) { + if ( first==NULL ) + first = into; + else + last->next = into; + } else if ( hi!=NULL ) { + if ( first==NULL ) + first = hi; + else + last->next = hi; + } +return( first ); +} + +StemInfo *HintCleanup(StemInfo *stem,int dosort,int instance_count) { + StemInfo *s, *p=NULL, *t, *pt, *sn; + int swap; + + for ( s=stem; s!=NULL; p=s, s=s->next ) { + if ( s->width<0 ) { + s->start += s->width; + s->width = -s->width; + s->ghost = true; + } + s->reordered = false; + if ( p!=NULL && p->start> s->start ) + dosort = true; + } + if ( dosort ) { + for ( p=NULL, s=stem; s!=NULL; p=s, s = sn ) { + sn = s->next; + for ( pt=s, t=sn; t!=NULL; pt=t, t=t->next ) { + if ( instance_count>1 && + t->u.unblended!=NULL && s->u.unblended!=NULL ) { + int temp = UnblendedCompare((*t->u.unblended)[0],(*s->u.unblended)[0],instance_count); + if ( temp==0 ) + swap = UnblendedCompare((*t->u.unblended)[1],(*s->u.unblended)[1],instance_count); + else + swap = temp<0; + } else if ( t->start<s->start ) + swap=true; + else if ( t->start>s->start ) + swap = false; + else + swap = (t->width<s->width); + if ( swap ) { + s->next = t->next; + if ( pt==s ) { + t->next = s; + sn = s; + } else { + t->next = sn; + pt->next = s; + } + if ( p==NULL ) + stem = t; + else + p->next = t; + pt = s; /* swap s and t */ + s = t; + t = pt; + } + } + } + /* Remove duplicates */ + if ( stem!=NULL ) for ( p=stem, s=stem->next; s!=NULL; s = sn ) { + sn = s->next; + if ( p->start==s->start && p->width==s->width && p->hintnumber==s->hintnumber ) { + p->where = HIMerge(p->where,s->where); + s->where = NULL; + p->next = sn; + StemInfoFree(s); + } else + p = s; + } + } +return( stem ); +} + +real EITOfNextMajor(EI *e, EIList *el, real sought_m ) { + /* We want to find t so that Mspline(t) = sought_m */ + /* the curve is monotonic */ + Spline1D *msp = &e->spline->splines[el->major]; + real new_t; + real found_m; + real t_mmax, t_mmin; + + if ( msp->a==0 && msp->b==0 ) { + if ( msp->c == 0 ) { + IError("Hor/Vert line when not expected"); +return( 0 ); + } + new_t = (sought_m-msp->d)/(msp->c); +return( new_t ); + } + + t_mmax = e->up?e->tmax:e->tmin; + t_mmin = e->up?e->tmin:e->tmax; + /* sought_m += el->low; */ + + while ( 1 ) { + new_t = (t_mmin+t_mmax)/2; + found_m = ( ((msp->a*new_t+msp->b)*new_t+msp->c)*new_t + msp->d ); + if ( found_m>sought_m-.001 && found_m<sought_m+.001 ) +return( new_t ); + if ( found_m > sought_m ) { + t_mmax = new_t; + } else { + t_mmin = new_t; + } + if ( t_mmax==t_mmin ) { + IError("EITOfNextMajor failed! on %s", el->sc!=NULL?el->sc->name:"Unknown" ); +return( new_t ); + } + } +} + +EI *EIActiveListReorder(EI *active,int *change) { + int any; + EI *pr, *apt; + + *change = false; + if ( active!=NULL ) { + any = true; + while ( any ) { + any = false; + for ( pr=NULL, apt=active; apt->aenext!=NULL; ) { + if ( apt->ocur <= apt->aenext->ocur ) { + /* still ordered */; + pr = apt; + apt = apt->aenext; + } else if ( pr==NULL ) { + active = apt->aenext; + apt->aenext = apt->aenext->aenext; + active->aenext = apt; + *change = true; + /* don't need to set any, since this reorder can't disorder the list */ + pr = active; + } else { + pr->aenext = apt->aenext; + apt->aenext = apt->aenext->aenext; + pr->aenext->aenext = apt; + any = *change = true; + pr = pr->aenext; + } + } + } + } +return( active ); +} + +EI *EIActiveEdgesRefigure(EIList *el, EI *active,real i,int major, int *_change) { + EI *apt, *pr, *npt; + int change = false, subchange; + int other = !major; + + /* first remove any entry which doesn't intersect the new scan line */ + /* (ie. stopped on last line) */ + for ( pr=NULL, apt=active; apt!=NULL; apt = apt->aenext ) { + if ( apt->coordmax[major]<i+el->low ) { + if ( pr==NULL ) + active = apt->aenext; + else + pr->aenext = apt->aenext; + change = true; + } else + pr = apt; + } + /* then move the active list to the next line */ + for ( apt=active; apt!=NULL; apt = apt->aenext ) { + Spline1D *osp = &apt->spline->splines[other]; + apt->tcur = EITOfNextMajor(apt,el,i+el->low); + apt->ocur = ( ((osp->a*apt->tcur+osp->b)*apt->tcur+osp->c)*apt->tcur + osp->d ); + } + /* reorder list */ + active = EIActiveListReorder(active,&subchange); + if ( subchange ) change = true; + + /* Insert new nodes */ + if ( el->ordered[(int) i]!=NULL ) change = true; + for ( pr=NULL, apt=active, npt=el->ordered[(int) i]; apt!=NULL && npt!=NULL; ) { + if ( npt->ocur<apt->ocur ) { + npt->aenext = apt; + if ( pr==NULL ) + active = npt; + else + pr->aenext = npt; + pr = npt; + npt = npt->ordered; + } else { + pr = apt; + apt = apt->aenext; + } + } + while ( npt!=NULL ) { + npt->aenext = NULL; + if ( pr==NULL ) + active = npt; + else + pr->aenext = npt; + pr = npt; + npt = npt->ordered; + } + *_change = change; +return( active ); +} + + +int EISkipExtremum(EI *e, real i, int major) { + EI *n = e->aenext, *t; + + if ( n==NULL ) +return( false ); + if ( + (ceil(e->coordmin[major])==i || floor(e->coordmin[major])==i || floor(e->coordmax[major])==i || ceil(e->coordmax[major])==i) && + (ceil(n->coordmin[major])==i || floor(n->coordmin[major])==i || floor(n->coordmax[major])==i || ceil(n->coordmax[major])==i) ) { + if ( + (n==e->splinenext && n->tmin==e->tmax && + n->tcur<n->tmin+.2 && e->tcur>e->tmax-.2 ) || + (n->splinenext==e && n->tmax==e->tmin && + n->tcur>n->tmax-.2 && e->tcur<e->tmin+.2 ) ) +return( n->up!=e->up ); + /* can be separated by a horizontal/vertical line in the other direction */ + if ( n->tmax==1 && e->tmin==0 && n->tcur>.8 && e->tcur<.2) { + t = n; + while ( (t = t->splinenext)!=e ) { + if ( t==NULL || t==n || + (major && !t->hor) || ( !major && !t->vert )) +return( false ); + } +return( n->up!=e->up ); + } else if ( n->tmin==0 && e->tmax==1 && n->tcur<.2 && e->tcur>.8) { + t = e; + while ( (t = t->splinenext)!=n ) { + if ( t==NULL || t==e || + (major && !t->hor) || ( !major && !t->vert )) +return( false ); + } +return( n->up!=e->up ); + } + } +return( false ); +} + + +real HIlen( StemInfo *stems) { + HintInstance *hi; + real len = 0; + + for ( hi=stems->where; hi!=NULL; hi = hi->next ) + len += hi->end-hi->begin; +return( len ); +} + +real HIoverlap( HintInstance *mhi, HintInstance *thi) { + HintInstance *hi; + real len = 0; + real s, e; + + for ( ; mhi!=NULL; mhi = mhi->next ) { + for ( hi = thi; hi!=NULL && hi->begin<=mhi->end; hi = hi->next ) { + if ( hi->end<mhi->begin ) { + thi = hi; + continue; + } + s = hi->begin<mhi->begin?mhi->begin:hi->begin; + e = hi->end>mhi->end?mhi->end:hi->end; + if ( e<s ) + continue; /* Shouldn't happen */ + len += e-s; + } + } +return( len ); +} + + +int StemListAnyConflicts(StemInfo *stems) { + StemInfo *s; + int any= false; + double end; + + for ( s=stems; s!=NULL ; s=s->next ) + s->hasconflicts = false; + while ( stems!=NULL ) { + end = stems->width<0 ? stems->start : stems->start+stems->width; + for ( s=stems->next; s!=NULL && (s->width>0 ? s->start : s->start+s->width)<end; s=s->next ) { + stems->hasconflicts = true; + s->hasconflicts = true; + any = true; + } + stems = stems->next; + } +return( any ); +} + +static HintInstance *SCGuessHintPoints(SplineChar *sc, int layer, StemInfo *stem,int major, int off) { + SplinePoint *starts[20], *ends[20]; + int spt=0, ept=0; + SplinePointList *spl; + SplinePoint *sp, *np; + int sm, wm, i, j, val; + real coord; + HintInstance *head, *test, *cur, *prev; + + for ( spl=sc->layers[layer].splines; spl!=NULL; spl=spl->next ) { + for ( sp=spl->first; ; sp = np ) { + coord = (major?sp->me.x:sp->me.y); + sm = coord>=stem->start-off && coord<=stem->start+off; + wm = coord>=stem->start+stem->width-off && coord<=stem->start+stem->width+off; + if ( sm && spt<20 ) + starts[spt++] = sp; + if ( wm && ept<20 ) + ends[ept++] = sp; + if ( sp->next==NULL ) + break; + np = sp->next->to; + if ( np==spl->first ) + break; + } + } + + head = NULL; + for ( i=0; i<spt; ++i ) { + val = 0x80000000; + for ( j=0; j<ept; ++j ) { + if ( major && starts[i]->me.y>=ends[j]->me.y-1 && starts[i]->me.y<=ends[j]->me.y+1 ) { + val = starts[i]->me.y; + break; + } else if ( !major && starts[i]->me.x>=ends[j]->me.x-1 && starts[i]->me.x<=ends[j]->me.x+1 ) { + val = starts[i]->me.x; + break; + } + } + if ( (unsigned)val!=0x80000000 ) { + for ( prev=NULL, test=head; test!=NULL && val>test->begin; prev=test, test=test->next ); + if ( test==NULL || val!=test->begin ) { + cur = chunkalloc(sizeof(HintInstance)); + cur->begin = cur->end = val; + cur->next = test; + if ( prev==NULL ) head = cur; + else prev->next = cur; + } + } + } +return( head ); +} + +static void SCGuessHintInstancesLight(SplineChar *sc, int layer, StemInfo *stem,int major) { + SplinePointList *spl; + SplinePoint *sp, *np; + int sm, wm, off; + real ob, oe; + HintInstance *s=NULL, *w=NULL, *cur, *p, *t, *n, *w2; + /* We've got a hint (from somewhere, old data, reading in a font, user specified etc.) */ + /* but we don't have HintInstance info. So see if we can find those data */ + /* Will get confused by stems with holes in them (for example if you make */ + /* a hint from one side of an H to the other, it will get the whole thing */ + /* not just the cross stem) */ + + for ( spl=sc->layers[layer].splines; spl!=NULL; spl=spl->next ) { + for ( sp=spl->first; ; sp = np ) { + sm = (major?sp->me.x:sp->me.y)==stem->start; + wm = (major?sp->me.x:sp->me.y)==stem->start+stem->width; + if ( sp->next==NULL ) + break; + np = sp->next->to; + if ( sm || wm ) { + if ( !major ) { + if ( np->me.y==sp->me.y ) { + ob = sp->me.x; oe = np->me.x; + } else if ( sp->nextcp.y==sp->me.y ) { + ob = sp->me.x; oe = (sp->me.x+sp->nextcp.x)/2; + if ( sp->prevcp.y==sp->me.y ) + ob = (sp->prevcp.x+sp->me.x)/2; + } else if ( sp->prevcp.y==sp->me.y ) { + ob = sp->me.x; oe = (sp->prevcp.x+sp->me.x)/2; + } else + sm = wm = false; + } else { + if ( np->me.x==sp->me.x ) { + ob = sp->me.y; oe = np->me.y; + } else if ( sp->nextcp.x==sp->me.x ) { + ob = sp->me.y; oe = (sp->nextcp.y+sp->me.y)/2; + if ( sp->prevcp.x==sp->me.x ) + ob = (sp->prevcp.y+sp->me.y)/2; + } else if ( sp->prevcp.x==sp->me.x ) { + ob = sp->me.y; oe = (sp->prevcp.y+sp->me.y)/2; + } else + sm = wm = false; + } + } + if ( sm || wm ) { + cur = chunkalloc(sizeof(HintInstance)); + if ( ob>oe ) { real temp=ob; ob=oe; oe=temp;} + cur->begin = ob; + cur->end = oe; + if ( sm ) { + if ( s==NULL || s->begin>cur->begin ) { + cur->next = s; + s = cur; + } else { + p = s; + for ( t=s->next; t!=NULL && t->begin<cur->begin; p=t, t=t->next ); + p->next = cur; cur->next = t; + } + } else { + if ( w==NULL || w->begin>cur->begin ) { + cur->next = w; + w = cur; + } else { + p = w; + for ( t=w->next; t!=NULL && t->begin<cur->begin; p=t, t=t->next ); + p->next = cur; cur->next = t; + } + } + } + if ( np==spl->first ) + break; + } + } + + /* Now we know what the right side of the stem looks like, and we know */ + /* what the left side looks like. They may not look the same (H for example) */ + /* Figure out the set where both are active */ + /* Unless it's a ghost hint */ + if ( stem->width==20 && s==NULL && w!=NULL ) { + s = w; + w = NULL; + } else if ( stem->width==21 && s!=NULL && w==NULL) { + /* Just use s */; + } else for ( p=NULL, t=s; t!=NULL; t=n ) { + n = t->next; + for ( w2=w; w2!=NULL && w2->begin<t->end ; w2=w2->next ) { + if ( w2->end<=t->begin ) + continue; + if ( w2->begin<=t->begin && w2->end>=t->end ) { + /* Perfect match */ + break; + } + if ( w2->begin>=t->begin ) + t->begin = w2->begin; + if ( w2->end<=t->end ) { + cur = chunkalloc(sizeof(HintInstance)); + cur->begin = w2->end; + cur->end = t->end; + cur->next = n; + t->next = cur; + n = cur; + t->end = w2->end; + } + break; + } + if ( w2!=NULL && w2->begin>=t->end ) + w2 = NULL; + if ( w2==NULL && w!=NULL ) { + HintInstance *best = NULL; + double best_off=1e10, off; + for ( w2=w; w2!=NULL ; w2=w2->next ) { + if ( w2->end<=t->begin ) + off = t->begin - w2->end; + else + off = w2->begin - t->end; + if ( best==NULL && off<best_off ) { + best = w2; + best_off = off; + } + } + if ( best!=NULL && best_off<stem->width ) { + w2 = best; + if( w2->begin<t->begin ) + t->begin = w2->begin; + if ( w2->end>t->end ) + t->end = w2->end; + } + } + if ( w2==NULL ) { + /* No match for t (or if there were it wasn't complete) get rid */ + /* of what's left of t */ + chunkfree(t,sizeof(*t)); + if ( p==NULL ) + s = n; + else + p->next = n; + } else + p = t; + } + while ( w!=NULL ) { + n = w->next; + chunkfree(w,sizeof(*w)); + w=n; + } + + /* If we couldn't find anything, then see if there are two points which */ + /* have the same x or y value and whose other coordinates match those of */ + /* the hint */ + /* Surprisingly many fonts have hints which don't accurately match the */ + /* points. Perhaps BlueFuzz (default 1) applies here too */ + for ( off=0; off<1 && s==NULL; ++off ) + s = SCGuessHintPoints(sc,layer, stem,major,off); + + stem->where = s; +} + +void SCGuessHHintInstancesList(SplineChar *sc,int layer) { + StemInfo *h; + for ( h= sc->hstem; h!=NULL; h=h->next ) { + if ( h->where==NULL ) { + SCGuessHintInstancesLight( sc,layer,h,false ); + } + } +} + +void SCGuessVHintInstancesList(SplineChar *sc,int layer) { + StemInfo *h; + for ( h= sc->vstem; h!=NULL; h=h->next ) { + if ( h->where==NULL ) { + SCGuessHintInstancesLight( sc,layer,h,true ); + } + } +} + + +static void _SCClearHintMasks(SplineChar *sc,int layer, int counterstoo) { + SplineSet *spl; + SplinePoint *sp; + RefChar *ref; + + if ( counterstoo ) { + free(sc->countermasks); + sc->countermasks = NULL; sc->countermask_cnt = 0; + } + + for ( spl = sc->layers[layer].splines; spl!=NULL; spl=spl->next ) { + for ( sp = spl->first ; ; ) { + chunkfree(sp->hintmask,sizeof(HintMask)); + sp->hintmask = NULL; + if ( sp->next==NULL ) + break; + sp = sp->next->to; + if ( sp==spl->first ) + break; + } + } + + for ( ref = sc->layers[layer].refs; ref!=NULL; ref=ref->next ) { + for ( spl = ref->layers[0].splines; spl!=NULL; spl=spl->next ) { + for ( sp = spl->first ; ; ) { + chunkfree(sp->hintmask,sizeof(HintMask)); + sp->hintmask = NULL; + if ( sp->next==NULL ) + break; + sp = sp->next->to; + if ( sp==spl->first ) + break; + } + } + } +} + + +static void SCFigureSimpleCounterMasks(SplineChar *sc) { + SplineChar *scs[MmMax]; + int hadh3, hadv3, i, vbase; + HintMask mask; + StemInfo *h; + + if ( sc->countermask_cnt!=0 ) +return; + + scs[0] = sc; + hadh3 = CvtPsStem3(NULL,scs,1,true,false); + hadv3 = CvtPsStem3(NULL,scs,1,false,false); + if ( hadh3 || hadv3 ) { + memset(mask,0,sizeof(mask)); + if ( hadh3 ) mask[0] = 0x80|0x40|0x20; + if ( hadv3 ) { + for ( h=sc->hstem, vbase=0; h!=NULL; h=h->next, ++vbase ); + for ( i=0; i<3 ; ++i ) { + int j = i+vbase; + mask[j>>3] |= (0x80>>(j&7)); + } + } + sc->countermask_cnt = 1; + sc->countermasks = galloc(sizeof(HintMask)); + memcpy(sc->countermasks[0],mask,sizeof(HintMask)); +return; + } +} + +void SCClearHintMasks(SplineChar *sc,int layer,int counterstoo) { + MMSet *mm = sc->parent->mm; + int i; + + if ( mm==NULL ) + _SCClearHintMasks(sc,layer,counterstoo); + else { + for ( i=0; i<mm->instance_count; ++i ) { + if ( sc->orig_pos<mm->instances[i]->glyphcnt ) + _SCClearHintMasks(mm->instances[i]->glyphs[sc->orig_pos],layer,counterstoo); + } + if ( sc->orig_pos<mm->normal->glyphcnt ) + _SCClearHintMasks(mm->normal->glyphs[sc->orig_pos],layer,counterstoo); + } +} + +static StemInfo *OnHHint(SplinePoint *sp, StemInfo *s) { + StemInfo *possible=NULL; + HintInstance *hi; + + if ( sp==NULL ) +return( NULL ); + + for ( ; s!=NULL; s=s->next ) { + if ( sp->me.y<s->start ) +return( possible ); + if ( s->start==sp->me.y || s->start+s->width==sp->me.y ) { + if ( !s->hasconflicts ) +return( s ); + for ( hi=s->where; hi!=NULL; hi=hi->next ) { + if ( hi->begin<=sp->me.x && hi->end>=sp->me.x ) +return( s ); + } + if ( !s->used ) + possible = s; + } + } +return( possible ); +} + +static StemInfo *OnVHint(SplinePoint *sp, StemInfo *s) { + StemInfo *possible=NULL; + HintInstance *hi; + + if ( sp==NULL ) +return( NULL ); + + for ( ; s!=NULL; s=s->next ) { + if ( sp->me.x<s->start ) +return( possible ); + if ( s->start==sp->me.x || s->start+s->width==sp->me.x ) { + if ( !s->hasconflicts ) +return( s ); + for ( hi=s->where; hi!=NULL; hi=hi->next ) { + if ( hi->begin<=sp->me.y && hi->end>=sp->me.y ) +return( s ); + } + if ( !s->used ) + possible = s; + } + } +return( possible ); +} + +/* Does h have a conflict with any of the stems in the list which have bits */ +/* set in the mask */ +static int ConflictsWithMask(StemInfo *stems, HintMask mask,StemInfo *h) { + while ( stems!=NULL && stems->start<h->start+h->width ) { + if ( stems->start+stems->width>=h->start && stems!=h ) { + if ( stems->hintnumber!=-1 && + (mask[stems->hintnumber>>3]&(0x80>>(stems->hintnumber&7))) ) +return( true ); + } + stems = stems->next; + } +return( false ); +} + +/* All instances of a MM set must have the same hint mask at all points */ +static void FigureHintMask(SplineChar *scs[MmMax], SplinePoint *to[MmMax], int instance_count, + HintMask mask) { + StemInfo *s; + int i; + SplinePoint *sp; + + memset(mask,'\0',sizeof(HintMask)); + + /* Install all hints that are always active */ + i=0; { + SplineChar *sc = scs[i]; + + if ( sc==NULL ) +return; + + for ( s=sc->hstem; s!=NULL; s=s->next ) + if ( s->hintnumber!=-1 && !s->hasconflicts ) + mask[s->hintnumber>>3] |= (0x80>>(s->hintnumber&7)); + for ( s=sc->vstem; s!=NULL; s=s->next ) + if ( s->hintnumber!=-1 && !s->hasconflicts ) + mask[s->hintnumber>>3] |= (0x80>>(s->hintnumber&7)); + + if ( sc->hconflicts ) { + for ( sp=to[i]; sp!=NULL; ) { + s = OnHHint(sp,sc->hstem); + if ( s!=NULL && s->hintnumber!=-1 ) { + if ( ConflictsWithMask(scs[i]->hstem,mask,s)) + break; + mask[s->hintnumber>>3] |= (0x80>>(s->hintnumber&7)); + } + if ( sp->next==NULL ) + break; + sp = sp->next->to; + if ( to[i]==sp ) + break; + } + } + if ( sc->vconflicts ) { + for ( sp=to[i]; sp!=NULL; ) { + s = OnVHint(sp,sc->vstem); + if ( s!=NULL && s->hintnumber!=-1 ) { + if ( ConflictsWithMask(scs[i]->vstem,mask,s)) + break; + mask[s->hintnumber>>3] |= (0x80>>(s->hintnumber&7)); + } + if ( sp->next==NULL ) + break; + sp = sp->next->to; + if ( to[i]==sp ) + break; + } + } + } + for ( i=0; i<instance_count; ++i ) if ( to[i]!=NULL ) { + chunkfree(to[i]->hintmask,sizeof(HintMask)); + to[i]->hintmask = chunkalloc(sizeof(HintMask)); + memcpy(to[i]->hintmask,mask,sizeof(HintMask)); + } +} + +static int TestHintMask(SplineChar *scs[MmMax], SplinePoint *to[MmMax], int instance_count, + HintMask mask) { + StemInfo *h=NULL, *v=NULL; + int i; + + for ( i=0; i<instance_count; ++i ) { + SplineChar *sc = scs[i]; + + if ( sc==NULL || (!sc->hconflicts && !sc->vconflicts )) + continue; + + /* Does this point lie on any hints? */ + if ( scs[i]->hconflicts ) + h = OnHHint(to[i],sc->hstem); + if ( scs[i]->vconflicts ) + v = OnVHint(to[i],sc->vstem); + + /* Need to set this hint */ + if ( (h!=NULL && h->hintnumber!=-1 && (mask[h->hintnumber>>3]&(0x80>>(h->hintnumber&7)))==0 ) || + (v!=NULL && v->hintnumber!=-1 && (mask[v->hintnumber>>3]&(0x80>>(v->hintnumber&7)))==0 )) + break; + } + if ( i==instance_count ) /* All hint masks were ok */ +return( false ); + + FigureHintMask(scs,to,instance_count,mask); +return( true ); +} + +static void UnnumberHints(SplineChar *sc) { + StemInfo *h; + + for ( h=sc->hstem; h!=NULL; h=h->next ) + h->hintnumber = -1; + for ( h=sc->vstem; h!=NULL; h=h->next ) + h->hintnumber = -1; +} + +static int NumberHints(SplineChar *sc) { + StemInfo *h; + int hcnt=0; + + for ( h=sc->hstem; h!=NULL; h=h->next ) + h->hintnumber = hcnt>=HntMax ? -1 : hcnt++; + for ( h=sc->vstem; h!=NULL; h=h->next ) + h->hintnumber = hcnt>=HntMax ? -1 : hcnt++; +return( hcnt ); +} + +static void UntickHints(SplineChar *sc) { + StemInfo *h; + + for ( h=sc->hstem; h!=NULL; h=h->next ) + h->used = false; + for ( h=sc->vstem; h!=NULL; h=h->next ) + h->used = false; +} + +struct coords { + real coords[MmMax]; + struct coords *next; +}; + +typedef struct mmh { + StemInfo *hints[MmMax], *map[MmMax]; + struct coords *where; + struct mmh *next; +} MMH; + +static void AddCoord(MMH *mmh,SplinePoint *sps[MmMax],int instance_count, int ish) { + struct coords *coords; + int i; + + coords = chunkalloc(sizeof(struct coords)); + coords->next = mmh->where; + mmh->where = coords; + if ( ish ) + for ( i=0; i<instance_count; ++i ) + coords->coords[i] = sps[i]->me.x; + else + for ( i=0; i<instance_count; ++i ) + coords->coords[i] = sps[i]->me.y; +} + +static MMH *AddHintSet(MMH *hints,StemInfo *h[MmMax], int instance_count, + SplinePoint *sps[MmMax], int ish) { + int i, cnt, bestc; + MMH *test, *best; + + for ( i=0; i<instance_count; ++i ) + if ( h[i]==NULL ) +return( hints ); + + best = NULL; bestc = 0; + for ( test=hints; test!=NULL; test=test->next ) { + cnt = 0; + for ( i=0; i<instance_count; ++i ) + if ( test->hints[i]==h[i] ) + ++cnt; + if ( cnt==instance_count ) { + AddCoord(test,sps,instance_count,ish); +return( hints ); + } + if ( cnt>bestc ) { + bestc = cnt; + best = test; + } + } + test = chunkalloc(sizeof(MMH)); + test->next = hints; + AddCoord(test,sps,instance_count,ish); + for ( i=0; i<instance_count; ++i ) + test->hints[i]=h[i]; + if ( bestc!=0 ) { + for ( i=0; i<instance_count; ++i ) { + if ( best->hints[i]==h[i] ) { + h[i]->hasconflicts = true; + test->map[i] = chunkalloc(sizeof(StemInfo)); + *test->map[i] = *h[i]; + test->map[i]->where = NULL; + test->map[i]->used = true; + h[i]->next = test->map[i]; + } else + test->map[i] = h[i]; + } + } else { + for ( i=0; i<instance_count; ++i ) + test->map[i]=h[i]; + } +return( test ); +} + +static int CompareMMH(MMH *mmh1,MMH *mmh2, int instance_count) { + int i; + + if ( mmh1->map[0]==NULL ) +return( 1 ); + if ( mmh2->map[0]==NULL ) +return( -1 ); + + for ( i=0; i<instance_count; ++i ) { + if ( mmh1->map[i]->start!=mmh2->map[i]->start ) { + if ( mmh1->map[i]->start > mmh2->map[i]->start ) +return( 1 ); + else +return( -1 ); + } + } + for ( i=0; i<instance_count; ++i ) { + if ( mmh1->map[i]->width!=mmh2->map[i]->width ) { + if ( mmh1->map[i]->width > mmh2->map[i]->width ) +return( 1 ); + else +return( -1 ); + } + } +return( 0 ); +} + +static MMH *SortMMH(MMH *head,int instance_count) { + MMH *mmh, *p, *smallest, *psmallest, *test, *ptest; + + for ( mmh = head, p=NULL; mmh!=NULL ; ) { + smallest = mmh; psmallest = p; + ptest = mmh; test = mmh->next; + while ( test!=NULL ) { + if ( CompareMMH(test,smallest,instance_count)<0 ) { + smallest = test; + psmallest = ptest; + } + ptest = test; + test = test->next; + } + if ( smallest!=mmh ) { + if ( p==NULL ) + head = smallest; + else + p->next = smallest; + if ( mmh->next==smallest ) { + mmh->next = smallest->next; + smallest->next = mmh; + } else { + test = mmh->next; + mmh->next = smallest->next; + smallest->next = test; + psmallest->next = mmh; + } + } + p = smallest; + mmh = smallest->next; + } +return( head ); +} + +static int NumberMMH(MMH *mmh,int hstart,int instance_count) { + int i; + HintInstance *hi, *n; + struct coords *coords; + + while ( mmh!=NULL ) { + for ( i=0; i<instance_count; ++i ) { + StemInfo *h = mmh->map[i]; + if ( h==NULL ) + continue; + + h->hintnumber = hstart; + + for ( hi=h->where; hi!=NULL; hi=n ) { + n = hi->next; + chunkfree(hi,sizeof(HintInstance)); + } + h->where = NULL; + for ( coords=mmh->where; coords!=NULL; coords = coords->next ) { + hi = chunkalloc(sizeof(HintInstance)); + hi->next = h->where; + h->where = hi; + hi->begin = coords->coords[i]-1; + hi->end = coords->coords[i]+1; + } + } + if ( mmh->map[0]!=NULL ) ++hstart; + mmh = mmh->next; + } +return( hstart ); +} + +static void SortMMH2(SplineChar *scs[MmMax],MMH *mmh,int instance_count,int ish) { + int i; + StemInfo *h, *n; + MMH *m; + + for ( i=0; i<instance_count; ++i ) { + for ( h= ish ? scs[i]->hstem : scs[i]->vstem; h!=NULL; h=n ) { + n = h->next; + if ( h->hintnumber==-1 ) + StemInfoFree(h); + } + n = NULL; + for ( m = mmh ; m!=NULL; m=m->next ) { + h = m->map[i]; + if ( n!=NULL ) + n->next = h; + else if ( ish ) + scs[i]->hstem = h; + else + scs[i]->vstem = h; + n = h; + } + if ( n!=NULL ) + n->next = NULL; + else if ( ish ) + scs[i]->hstem = NULL; + else + scs[i]->vstem = NULL; + } +} + +static void MMHFreeList(MMH *mmh) { + MMH *mn; + struct coords *c, *n; + + for ( ; mmh!=NULL; mmh = mn ) { + mn = mmh->next; + for ( c=mmh->where; c!=NULL; c=n ) { + n = c->next; + chunkfree(c,sizeof(struct coords)); + } + chunkfree(mmh,sizeof(struct coords)); + } +} + +static void SplResolveSplitHints(SplineChar *scs[MmMax], SplineSet *spl[MmMax], + int instance_count, MMH **hs, MMH **vs) { + SplinePoint *to[MmMax]; + StemInfo *h[MmMax], *v[MmMax]; + int i, anymore; + + forever { + for ( i=0; i<instance_count; ++i ) { + if ( spl[i]!=NULL ) + to[i] = spl[i]->first; + else + to[i] = NULL; + } + forever { + for ( i=0; i<instance_count; ++i ) { + h[i] = OnHHint(to[i],scs[i]->hstem); + v[i] = OnVHint(to[i],scs[i]->vstem); + } + *hs = AddHintSet(*hs,h,instance_count,to,true); + *vs = AddHintSet(*vs,v,instance_count,to,false); + anymore = false; + for ( i=0; i<instance_count; ++i ) if ( to[i]!=NULL ) { + if ( to[i]->next==NULL ) to[i] = NULL; + else { + to[i] = to[i]->next->to; + if ( to[i]==spl[i]->first ) to[i] = NULL; + } + if ( to[i]!=NULL ) anymore = true; + } + if ( !anymore ) + break; + } + anymore = false; + for ( i=0; i<instance_count; ++i ) { + if ( spl[i]!=NULL ) + spl[i] = spl[i]->next; + if ( spl[i]!=NULL ) anymore = true; + } + if ( !anymore ) + break; + } +} + +static void ResolveSplitHints(SplineChar *scs[16],int layer,int instance_count) { + /* It is possible for a single hint in one mm instance to split into two */ + /* in a different MM set. For example, we have two stems which happen */ + /* to line up in one instance but which do not in another instance. */ + /* It is even possible that there could be no instance with any conflicts */ + /* but some of the intermediate forms might conflict. */ + /* We can't deal (nor can postscript) with the case where hints change order*/ + SplinePointList *spl[MmMax]; + RefChar *ref[MmMax]; + int i, hcnt, hmax=0, anymore; + MMH *hs=NULL, *vs=NULL; + + for ( i=0; i<instance_count; ++i ) { + hcnt = NumberHints(scs[i]); + UntickHints(scs[i]); + if ( hcnt>hmax ) hmax = hcnt; + spl[i] = scs[i]->layers[layer].splines; + } + if ( hmax==0 ) +return; + + SplResolveSplitHints(scs,spl,instance_count,&hs,&vs); + anymore = false; + for ( i=0; i<instance_count; ++i ) { + ref[i] = scs[i]->layers[layer].refs; + if ( ref[i]!=NULL ) anymore = true; + } + while ( anymore ) { + for ( i=0; i<instance_count; ++i ) + spl[i] = ( ref[i]!=NULL ) ? ref[i]->layers[0].splines : NULL; + SplResolveSplitHints(scs,spl,instance_count,&hs,&vs); + anymore = false; + for ( i=0; i<instance_count; ++i ) { + if ( ref[i]!=NULL ) { + ref[i] = ref[i]->next; + if ( ref[i]!=NULL ) anymore = true; + } + } + } + + for ( i=0; i<instance_count; ++i ) + UnnumberHints(scs[i]); + hs = SortMMH(hs,instance_count); + vs = SortMMH(vs,instance_count); + hcnt = NumberMMH(hs,0,instance_count); + hcnt = NumberMMH(vs,hcnt,instance_count); + SortMMH2(scs,hs,instance_count,true); + SortMMH2(scs,vs,instance_count,false); + MMHFreeList(hs); + MMHFreeList(vs); +} + +static int SplFigureHintMasks(SplineChar *scs[MmMax], SplineSet *spl[MmMax], + int instance_count, HintMask mask, int inited) { + SplinePoint *to[MmMax]; + int i, anymore; + + anymore = false; + for ( i=0; i<instance_count; ++i ) { + if ( spl[i]!=NULL ) { + SplineSetReverse(spl[i]); + to[i] = spl[i]->first; + anymore = true; + } else + to[i] = NULL; + } + + /* Assign the initial hint mask */ + if ( anymore && !inited ) { + FigureHintMask(scs,to,instance_count,mask); + inited = true; + } + + forever { + for ( i=0; i<instance_count; ++i ) { + if ( spl[i]!=NULL ) + to[i] = spl[i]->first; + else + to[i] = NULL; + } + forever { + TestHintMask(scs,to,instance_count,mask); + anymore = false; + for ( i=0; i<instance_count; ++i ) if ( to[i]!=NULL ) { + if ( to[i]->next==NULL ) to[i] = NULL; + else { + to[i] = to[i]->next->to; + if ( to[i]==spl[i]->first ) to[i] = NULL; + } + if ( to[i]!=NULL ) anymore = true; + } + if ( !anymore ) + break; + } + anymore = false; + for ( i=0; i<instance_count; ++i ) { + if ( spl[i]!=NULL ) { + SplineSetReverse(spl[i]); + spl[i] = spl[i]->next; + } + if ( spl[i]!=NULL ) { + anymore = true; + SplineSetReverse(spl[i]); + } + } + if ( !anymore ) + break; + } +return( inited ); +} + +void SCFigureHintMasks(SplineChar *sc,int layer) { + SplineChar *scs[MmMax]; + SplinePointList *spl[MmMax]; + RefChar *ref[MmMax]; + MMSet *mm = sc->parent->mm; + int i, instance_count, conflicts, anymore, inited; + HintMask mask; + + if ( mm==NULL ) { + scs[0] = sc; + instance_count = 1; + SCClearHintMasks(sc,layer,false); + } else { + if ( mm->apple ) +return; + instance_count = mm->instance_count; + for ( i=0; i<instance_count; ++i ) + if ( sc->orig_pos < mm->instances[i]->glyphcnt ) { + scs[i] = mm->instances[i]->glyphs[sc->orig_pos]; + SCClearHintMasks(scs[i],layer,false); + } + ResolveSplitHints(scs,layer,instance_count); + } + conflicts = false; + for ( i=0; i<instance_count; ++i ) { + NumberHints(scs[i]); + if ( scs[i]->hconflicts || scs[i]->vconflicts ) + conflicts = true; + } + if ( !conflicts && instance_count==1 ) { /* All hints always active */ + SCFigureSimpleCounterMasks(sc); +return; /* In an MM font we may still need to resolve things like different numbers of hints */ + } + + for ( i=0; i<instance_count; ++i ) { + spl[i] = scs[i]->layers[layer].splines; + ref[i] = scs[i]->layers[layer].refs; + } + inited = SplFigureHintMasks(scs,spl,instance_count,mask,false); + forever { + for ( i=0; i<instance_count; ++i ) { + if ( ref[i]!=NULL ) + spl[i] = ref[i]->layers[0].splines; + } + inited = SplFigureHintMasks(scs,spl,instance_count,mask,inited); + anymore = false; + for ( i=0; i<instance_count; ++i ) { + if ( ref[i]!=NULL ) { + ref[i] = ref[i]->next; + if ( ref[i]!=NULL ) anymore = true; + } + } + if ( !anymore ) + break; + } + if ( instance_count==1 ) + SCFigureSimpleCounterMasks(sc); +} + + + +static void FigureStems( SplineFont *sf, real snaps[12], real cnts[12], + int which ) { + int i, j, k, cnt, smax=0, smin=2000; + real stemwidths[2000]; + StemInfo *stems, *test; + int len; + HintInstance *hi; + + memset(stemwidths,'\0',sizeof(stemwidths)); + + for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) { + stems = which?sf->glyphs[i]->hstem:sf->glyphs[i]->vstem; + for ( test=stems; test!=NULL; test = test->next ) if ( !test->ghost ) { + if ( (j=test->width)<0 ) j= -j; + if ( j<2000 ) { + len = 0; + for ( hi=test->where; hi!=NULL; hi=hi->next ) + len += hi->end-hi->begin; + if ( len==0 ) len = 100; + stemwidths[j] += len; + if ( smax<j ) smax=j; + if ( smin>j ) smin=j; + } + } + } + + for ( i=smin, cnt=0; i<=smax; ++i ) { + if ( stemwidths[i]!=0 ) + ++cnt; + } + + if ( cnt>12 ) { + /* Merge width windows */ + int windsize=3, j; + for ( i=smin; i<=smax; ++i ) if ( stemwidths[i]!=0 ) { + if ( (j = i-windsize)<0 ) j=0; + for ( ; j<smax && j<=i+windsize; ++j ) + if ( stemwidths[i]<stemwidths[j] ) + break; + if ( j==smax || j>i+windsize ) { + if ( (j = i-windsize)<0 ) j=0; + for ( ; j<smax && j<=i+windsize; ++j ) if ( j!=i ) { + stemwidths[i] += stemwidths[j]; + stemwidths[j] = 0; + } + } + } + /* Merge adjacent widths */ + for ( i=smin; i<=smax; ++i ) { + if ( stemwidths[i]!=0 && i<=smax-1 && stemwidths[i+1]!=0 ) { + if ( stemwidths[i]>stemwidths[i+1] ) { + stemwidths[i] += stemwidths[i+1]; + stemwidths[i+1] = 0; + } else { + if ( i<=smax-2 && stemwidths[i+2] && stemwidths[i+2]<stemwidths[i+1] ) { + stemwidths[i+1] += stemwidths[i+2]; + stemwidths[i+2] = 0; + } + stemwidths[i+1] += stemwidths[i]; + stemwidths[i] = 0; + ++i; + } + } + } + for ( i=smin, cnt=0; i<=smax; ++i ) { + if ( stemwidths[i]!=0 ) + ++cnt; + } + } + if ( cnt<=12 ) { + for ( i=smin, cnt=0; i<=smax; ++i ) { + if ( stemwidths[i]!=0 ) { + snaps[cnt] = i; + cnts[cnt++] = stemwidths[i]; + } + } + } else { real firstbiggest=0; + for ( cnt = 0; cnt<12; ++cnt ) { + int biggesti=0; + real biggest=0; + for ( i=smin; i<=smax; ++i ) { + if ( stemwidths[i]>biggest ) { biggest = stemwidths[i]; biggesti=i; } + } + /* array must be sorted */ + if ( biggest<firstbiggest/6 ) + break; + for ( j=0; j<cnt; ++j ) + if ( snaps[j]>biggesti ) + break; + for ( k=cnt-1; k>=j; --k ) { + snaps[k+1] = snaps[k]; + cnts[k+1]=cnts[k]; + } + snaps[j] = biggesti; + cnts[j] = biggest; + stemwidths[biggesti] = 0; + if ( firstbiggest==0 ) firstbiggest = biggest; + } + } + for ( ; cnt<12; ++cnt ) { + snaps[cnt] = 0; + cnts[cnt] = 0; + } +} + +void FindHStems( SplineFont *sf, real snaps[12], real cnt[12]) { + FigureStems(sf,snaps,cnt,1); +} + +void FindVStems( SplineFont *sf, real snaps[12], real cnt[12]) { + FigureStems(sf,snaps,cnt,0); +} + +static int IsFlexSmooth(SplinePoint *sp) { + BasePoint nvec, pvec; + double proj_same, proj_normal; + + if ( sp->nonextcp || sp->noprevcp ) +return( false ); /* No continuity of slopes */ + + nvec.x = sp->nextcp.x - sp->me.x; nvec.y = sp->nextcp.y - sp->me.y; + pvec.x = sp->me.x - sp->prevcp.x; pvec.y = sp->me.y - sp->prevcp.y; + + /* Avoid cases where the slopes are 180 out of phase */ + if ( (proj_same = nvec.x*pvec.x + nvec.y*pvec.y)<=0 ) +return( false ); + if ( (proj_normal = nvec.x*pvec.y - nvec.y*pvec.x)<0 ) + proj_normal = -proj_normal; + + /* Something is smooth if the normal projection is 0. Let's allow for */ + /* some rounding errors */ + if ( proj_same >= 16*proj_normal ) +return( true ); + +return( false ); +} + +static int _SplineCharIsFlexible(SplineChar *sc, int layer, int blueshift) { + /* Need two splines + outer endpoints have same x (or y) values + inner point must be less than 20 horizontal (v) units from the outer points + inner point must also be less than BlueShift units (defaults to 7=>6) + (can increase BlueShift up to 21) + the inner point must be a local extremum + the inner point's cps must be at the x (or y) value as the extremum + (I think) + */ + /* We want long, nearly straight stems. If the end-points should not have + continuous slopes, or if they do, they must be horizontal/vertical. + This is an heuristic requirement, not part of Adobe's spec. + */ + SplineSet *spl; + SplinePoint *sp, *np, *pp; + int max=0, val; + RefChar *r; + + if ( sc==NULL ) +return(false); + + for ( spl = sc->layers[layer].splines; spl!=NULL; spl=spl->next ) { + if ( spl->first->prev==NULL ) { + /* Mark everything on the open path as inflexible */ + sp=spl->first; + while ( 1 ) { + sp->flexx = sp->flexy = false; + if ( sp->next==NULL ) + break; + sp = sp->next->to; + } + continue; /* Ignore open paths */ + } + sp=spl->first; + do { + if ( sp->next==NULL || sp->prev==NULL ) + break; + np = sp->next->to; + pp = sp->prev->from; + if ( !pp->flexx && !pp->flexy ) { + sp->flexy = sp->flexx = 0; + val = 0; + if ( RealNear(sp->nextcp.x,sp->me.x) && + RealNear(sp->prevcp.x,sp->me.x) && + RealNear(np->me.x,pp->me.x) && + !RealNear(np->me.x,sp->me.x) && + (!IsFlexSmooth(pp) || RealNear(pp->nextcp.x,pp->me.x)) && + (!IsFlexSmooth(np) || RealNear(np->prevcp.x,np->me.x)) && + np->me.x-sp->me.x < blueshift && + np->me.x-sp->me.x > -blueshift ) { + if ( (np->me.x>sp->me.x && + np->prevcp.x<=np->me.x && np->prevcp.x>=sp->me.x && + pp->nextcp.x<=pp->me.x && pp->prevcp.x>=sp->me.x ) || + (np->me.x<sp->me.x && + np->prevcp.x>=np->me.x && np->prevcp.x<=sp->me.x && + pp->nextcp.x>=pp->me.x && pp->prevcp.x<=sp->me.x )) { + sp->flexx = true; + val = np->me.x-sp->me.x; + } + } + if ( RealNear(sp->nextcp.y,sp->me.y) && + RealNear(sp->prevcp.y,sp->me.y) && + RealNear(np->me.y,pp->me.y) && + !RealNear(np->me.y,sp->me.y) && + (!IsFlexSmooth(pp) || RealNear(pp->nextcp.y,pp->me.y)) && + (!IsFlexSmooth(np) || RealNear(np->prevcp.y,np->me.y)) && + np->me.y-sp->me.y < blueshift && + np->me.y-sp->me.y > -blueshift ) { + if ( (np->me.y>sp->me.y && + np->prevcp.y<=np->me.y && np->prevcp.y>=sp->me.y && + pp->nextcp.y<=pp->me.y && pp->nextcp.y>=sp->me.y ) || + (np->me.y<sp->me.y && + np->prevcp.y>=np->me.y && np->prevcp.y<=sp->me.y && + pp->nextcp.y>=pp->me.y && pp->nextcp.y<=sp->me.y )) { + sp->flexy = true; + val = np->me.y-sp->me.y; + } + } + if ( val<0 ) val = -val; + if ( val>max ) max = val; + } + sp = np; + } while ( sp!=spl->first ); + } + sc->layers[layer].anyflexes = max>0; + if ( max==0 ) + for ( r = sc->layers[layer].refs; r!=NULL ; r=r->next ) + if ( r->sc->layers[layer].anyflexes ) { + sc->layers[layer].anyflexes = true; + break; + } +return( max ); +} + + + +static void SCUnflex(SplineChar *sc, int layer) { + SplineSet *spl; + SplinePoint *sp; + + for ( spl = sc->layers[layer].splines; spl!=NULL; spl=spl->next ) { + /* Mark everything on the path as inflexible */ + sp=spl->first; + while ( 1 ) { + sp->flexx = sp->flexy = false; + if ( sp->next==NULL ) + break; + sp = sp->next->to; + if ( sp==spl->first ) + break; + } + } + sc->layers[layer].anyflexes = false; +} + +static void FlexDependents(SplineChar *sc,int layer) { + struct splinecharlist *scl; + + sc->layers[layer].anyflexes = true; + for ( scl = sc->dependents; scl!=NULL; scl=scl->next ) + FlexDependents(scl->sc,layer); +} + +int SplineFontIsFlexible(SplineFont *sf,int layer, int flags) { + int i; + int max=0, val; + char *pt; + int blueshift; + /* if the return value is bigger than 6 and we don't have a BlueShift */ + /* then we must set BlueShift to ret+1 before saving private dictionary */ + /* If the first point in a spline set is flexible, then we must rotate */ + /* the splineset */ + + if ( flags&(ps_flag_nohints|ps_flag_noflex)) { + for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) + SCUnflex(sf->glyphs[i],layer); +return( 0 ); + } + + pt = PSDictHasEntry(sf->private,"BlueShift"); + blueshift = 21; /* maximum posible flex, not default */ + if ( pt!=NULL ) { + blueshift = strtol(pt,NULL,10); + if ( blueshift>21 ) blueshift = 21; + } else if ( PSDictHasEntry(sf->private,"BlueValues")!=NULL ) + blueshift = 7; /* The BlueValues array may depend on BlueShift having its default value */ + + for ( i=0; i<sf->glyphcnt; ++i ) + if ( sf->glyphs[i]!=NULL ) if ( sf->glyphs[i]!=NULL ) { + val = _SplineCharIsFlexible(sf->glyphs[i],layer,blueshift); + if ( val>max ) max = val; + if ( sf->glyphs[i]->layers[layer].anyflexes ) + FlexDependents(sf->glyphs[i],layer); + } +return( max ); +} + + |