/* 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 #include "psfont.h" float GenerateHintWidthEqualityTolerance = 0.0; int autohint_before_generate = 1; /* Let's talk about references. */ /* If we are doing Type1 output, then the obvious way of doing them is seac */ /* but that's so limitting. It only works for exactly two characters both */ /* of which are in Adobe's Standard Enc. Only translations allowed. Only */ /* one reference may be translated and the width of the char must match */ /* that of the non-translated reference */ /* The first extension we can make is to allow a single character reference */ /* by making the other character be a space */ /* But if we want to do more than that we must use subrs. If we have two */ /* refs in subrs then we can do translations by preceding the subr calls by */ /* appropriate rmovetos. Actually the specs say that only one rmoveto should */ /* precede a path, so that means we can't allow the subroutines to position */ /* themselves, they must just assume that they are called with the current */ /* position correct for the first point. But then we need to know where the */ /* first point should be placed, so we allocate a BasePoint to hold that info*/ /* and store it into the "keys" array (which the subrs don't use). Similarly */ /* we need to know where the subr will leave us, so we actually allocate 2 */ /* BasePoints, one containing the start point, one the end point */ /* But that's still not good enough, hints are defined in such a way that */ /* they are not relocateable. So our subrs can't include any hint definitions*/ /* (or if they do then that subr can't be translated at all). So hints must */ /* be set outside the subrs, and the subrs can't be for chars that need hint */ /* substitution. Unless... The subr will never be relocated. */ /* So we generate two types of reference subrs, one containing no hints, the*/ /* other containing all the hints, stems and flexes. The first type may be */ /* translated, the second cannot */ /* Type2 doesn't allow any seacs */ /* So everything must go in subrs. We have a slightly different problem here:*/ /* hintmasks need to know exactly how many stem hints there are in the char */ /* so we can't include any hintmask operators inside a subr (unless we */ /* guarantee that all invocations of that subr are done with the same number */ /* of hints in the character). This again means that no char with hint subs- */ /* titutions may be put in a subr. UNLESS all the other references in a */ /* refering character contain no hints */ /* That's very complex. And it doesn't do a very good job. */ /* Instead let's take all strings bounded by either moveto or hintmask operators */ /* store these as potential subroutines. So a glyph becomes a sequence of */ /* potential subroutine calls preceded by the glyph header (width, hint decl,*/ /* counter declarations, etc.) and intersperced by hintmask/moveto operators */ /* Each time we get a potential subr we hash it and see if we've used that */ /* string before. If we have then we merge the two. Otherwise it's a new one.*/ /* Then at the end we see what strings get used often enough to go into subrs */ /* we create the subrs array from that. */ /* Then each glyph. We insert the preamble. We check of the potential subroutine */ /* became a real subroutine. If so we call it, else we insert the data inline*/ /* Do the same for the next hintmask/moveto and potential subroutine... */ /* Then, on top of that I tried generating some full glyph subroutines, and */ /* to my surprise, it just made things worse. */ struct potentialsubrs { uint8 *data; /* the charstring of the subr */ int len; /* the length of the charstring */ int idx; /* initially index into psubrs array */ /* then index into subrs array or -1 if none */ int cnt; /* the usage count */ int fd; /* Which sub font is it in */ /* -1 => used in more than one */ int next; int full_glyph_index; /* Into the glyphbits array */ /* for full references */ BasePoint *startstop; /* Again for full references */ }; struct bits { uint8 *data; int dlen; int psub_index; }; struct glyphbits { SplineChar *sc; int fd; /* Which subfont is it in */ int bcnt; struct bits *bits; uint8 wasseac; }; #define HSH_SIZE 511 /* In type2 charstrings we divide every character into bits where a bit is */ /* bounded by a hintmask/moveto. Each of these is a potential subroutine and */ /* is stored here */ typedef struct glyphinfo { struct potentialsubrs *psubrs; int pcnt, pmax; int hashed[HSH_SIZE]; struct glyphbits *gb, *active; SplineFont *sf; int layer; int glyphcnt; int subfontcnt; int bcnt, bmax; struct bits *bits; /* For current glyph */ const int *bygid; int justbroken; int instance_count; } GlyphInfo; struct mhlist { uint8 mask[HntMax/8]; int subr; struct mhlist *next; }; struct hintdb { uint8 mask[HntMax/8]; int cnt; /* number of hints */ struct mhlist *sublist; struct pschars *subrs; /*SplineChar *sc;*/ SplineChar **scs; int instance_count; unsigned int iscjk: 1; /* If cjk then don't do stem3 hints */ /* Will be done with counters instead */ /* actually, most of the time we can't use stem3s, only if those three*/ /* stems are always active and there are no other stems !(h/v)hasoverlap*/ unsigned int noconflicts: 1; unsigned int startset: 1; unsigned int skiphm: 1; /* Set when coming back to the start point of a contour. hintmask should be set the first time, not the second */ unsigned int donefirsthm: 1; int cursub; /* Current subr number */ DBasePoint current; GlyphInfo *gi; }; static void GIContentsFree(GlyphInfo *gi,SplineChar *dummynotdef) { int i,j; if ( gi->glyphcnt>0 && gi->gb[0].sc == dummynotdef ) { if ( dummynotdef->layers!=NULL ) { SplinePointListsFree(dummynotdef->layers[gi->layer].splines); dummynotdef->layers[gi->layer].splines = NULL; } StemInfosFree(dummynotdef->hstem); StemInfosFree(dummynotdef->vstem); dummynotdef->vstem = dummynotdef->hstem = NULL; free(dummynotdef->layers); dummynotdef->layers = NULL; } for ( i=0; ipcnt; ++i ) { free(gi->psubrs[i].data); free(gi->psubrs[i].startstop); gi->psubrs[i].data = NULL; gi->psubrs[i].startstop = NULL; } for ( i=0; iglyphcnt; ++i ) { for ( j=0; jgb[i].bcnt; ++j ) free(gi->gb[i].bits[j].data); free(gi->gb[i].bits); gi->gb[i].bits = NULL; gi->gb[i].bcnt = 0; } gi->pcnt = 0; gi->bcnt = 0; gi->justbroken = 0; } static void GIFree(GlyphInfo *gi,SplineChar *dummynotdef) { GIContentsFree(gi,dummynotdef); free(gi->gb); free(gi->psubrs); free(gi->bits); } static void StartNextSubroutine(GrowBuf *gb,struct hintdb *hdb) { GlyphInfo *gi; if ( hdb==NULL ) return; gi = hdb->gi; if ( gi==NULL ) return; /* Store everything in the grow buf into the data/dlen of the next bit */ if ( gi->bcnt==-1 ) gi->bcnt = 0; if ( gi->bcnt>=gi->bmax ) gi->bits = realloc(gi->bits,(gi->bmax+=20)*sizeof(struct bits)); gi->bits[gi->bcnt].dlen = gb->pt-gb->base; gi->bits[gi->bcnt].data = malloc(gi->bits[gi->bcnt].dlen); gi->bits[gi->bcnt].psub_index = -1; memcpy(gi->bits[gi->bcnt].data,gb->base,gi->bits[gi->bcnt].dlen); gb->pt = gb->base; gi->justbroken = false; } static int hashfunc(uint8 *data, int len) { uint8 *end = data+len; unsigned int hash = 0, r; while ( data>30)&3; hash <<= 2; hash = (hash|r)&0xffffffff; hash ^= *data++; } return( hash%HSH_SIZE ); } static void BreakSubroutine(GrowBuf *gb,struct hintdb *hdb) { GlyphInfo *gi; struct potentialsubrs *ps; int hash; int pi; if ( hdb==NULL ) return; gi = hdb->gi; if ( gi==NULL ) return; /* The stuff before the first moveto in a glyph (the header that sets */ /* the width, sets up the hints, counters, etc.) can't go into a subr */ if ( gi->bcnt==-1 ) { gi->bcnt=0; gi->justbroken = true; return; } else if ( gi->justbroken ) return; /* Otherwise stuff everything in the growbuffer into a subr */ hash = hashfunc(gb->base,gb->pt-gb->base); ps = NULL; for ( pi=gi->hashed[hash]; pi!=-1; pi=gi->psubrs[pi].next ) { ps = &gi->psubrs[pi]; if ( ps->len==gb->pt-gb->base && memcmp(ps->data,gb->base,gb->pt-gb->base)==0 ) break; } if ( pi==-1 ) { if ( gi->pcnt>=gi->pmax ) gi->psubrs = realloc(gi->psubrs,(gi->pmax+=gi->glyphcnt)*sizeof(struct potentialsubrs)); ps = &gi->psubrs[gi->pcnt]; memset(ps,0,sizeof(*ps)); /* set cnt to 0 */ ps->idx = gi->pcnt++; ps->len = gb->pt-gb->base; ps->data = malloc(ps->len); memcpy(ps->data,gb->base,ps->len); ps->next = gi->hashed[hash]; gi->hashed[hash] = ps->idx; ps->fd = gi->active->fd; ps->full_glyph_index = -1; } if ( ps->fd!=gi->active->fd ) ps->fd = -1; /* used in multiple cid sub-fonts */ gi->bits[gi->bcnt].psub_index = ps->idx; ++ps->cnt; gb->pt = gb->base; ++gi->bcnt; gi->justbroken = true; } static void MoveSubrsToChar(GlyphInfo *gi) { struct glyphbits *active; if ( gi==NULL ) return; active = gi->active; active->bcnt = gi->bcnt; active->bits = malloc(active->bcnt*sizeof(struct bits)); memcpy(active->bits,gi->bits,active->bcnt*sizeof(struct bits)); gi->bcnt = 0; } static int NumberHints(SplineChar *scs[MmMax], int instance_count) { int i,j, cnt=-1; StemInfo *s; for ( j=0; jhstem, i=0; s!=NULL; s=s->next ) { if ( ihintnumber = i++; else s->hintnumber = -1; } for ( s=scs[j]->vstem; s!=NULL; s=s->next ) { if ( ihintnumber = i++; else s->hintnumber = -1; } if ( cnt==-1 ) cnt = i; else if ( cnt!=i ) IError("MM font with different hint counts"); } return( cnt ); } static void MarkTranslationRefs(SplineFont *sf,int layer) { int i; SplineChar *sc; RefChar *r; for ( i=0; iglyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL ) { for ( r = sc->layers[layer].refs; r!=NULL; r=r->next ) r->justtranslated = (r->transform[0]==1 && r->transform[3]==1 && r->transform[1]==0 && r->transform[2]==0); } } /* ************************************************************************** */ /* ********************** Type1 PostScript CharStrings ********************** */ /* ************************************************************************** */ static void AddNumber(GrowBuf *gb, real pos, int round) { int dodiv = 0; int val; unsigned char *str; if ( gb->pt+8>=gb->end ) GrowBuffer(gb); if ( !round && pos!=floor(pos) ) { { if ( rint(pos*64)/64 == pos ) { pos *= 64; dodiv = 64; } else { pos *= 1024; dodiv = 1024; } } } pos = rint(pos); if ( dodiv>0 && floor(pos)/dodiv == floor(pos/dodiv) ) { pos = rint(pos/dodiv); dodiv = 0; } val = pos; str = gb->pt; if ( pos>=-107 && pos<=107 ) *str++ = val+139; else if ( pos>=108 && pos<=1131 ) { val -= 108; *str++ = (val>>8)+247; *str++ = val&0xff; } else if ( pos>=-1131 && pos<=-108 ) { val = -val; val -= 108; *str++ = (val>>8)+251; *str++ = val&0xff; } else { *str++ = '\377'; *str++ = (val>>24)&0xff; *str++ = (val>>16)&0xff; *str++ = (val>>8)&0xff; *str++ = val&0xff; } if ( dodiv ) { if ( dodiv<107 ) *str++ = dodiv+139; else { dodiv -= 108; *str++ = (dodiv>>8)+247; *str++ = dodiv&0xff; } *str++ = 12; /* div (byte1) */ *str++ = 12; /* div (byte2) */ } gb->pt = str; } /* When doing a multiple master font we have multiple instances of the same data */ /* which must all be added, and then a call made to the appropriate blend routine */ /* This is complicated because all the data may not fit on the stack so we */ /* may need to make multiple calls */ static void AddData(GrowBuf *gb, bigreal data[MmMax][6], int instances, int num_coords, int round) { int allsame = true, alls[6]; int i,j, chunk,min,max,subr; for ( j=0; jnum_coords ) max = num_coords; while ( max-1>min && alls[max-1] ) --max; if ( max-min==5 ) max=min+4; if ( minpt+1>=gb->end ) GrowBuffer(gb); *gb->pt++ = 10; /* callsubr */ min = j; } } } int CvtPsStem3(GrowBuf *gb, SplineChar *scs[MmMax], int instance_count, int ishstem, int round) { StemInfo *h1, *h2, *h3; StemInfo _h1, _h2, _h3; bigreal data[MmMax][6]; int i; real off; for ( i=0; ihconflicts) || (!ishstem && scs[i]->vconflicts)) return( false ); h1 = ishstem ? scs[i]->hstem : scs[i]->vstem; if ( h1==NULL || (h2 = h1->next)==NULL || (h3=h2->next)==NULL ) return( false ); if ( h3->next!=NULL ) return( false ); off = ishstem ? 0 : scs[i]->lsidebearing; if ( h1->width<0 ) { _h1 = *h1; _h1.start += _h1.width; _h1.width = -_h1.width; h1 = &_h1; } if ( h2->width<0 ) { _h2 = *h2; _h2.start += _h2.width; _h2.width = -_h2.width; h2 = &_h2; } if ( h3->width<0 ) { _h3 = *h3; _h3.start += _h3.width; _h3.width = -_h3.width; h3 = &_h3; } if ( h1->start>h2->start ) { StemInfo *ht = h1; h1 = h2; h2 = ht; } if ( h1->start>h3->start ) { StemInfo *ht = h1; h1 = h3; h3 = ht; } if ( h2->start>h3->start ) { StemInfo *ht = h2; h2 = h3; h3 = ht; } if ( h1->width != h3->width ) return( false ); if ( (h2->start+h2->width/2) - (h1->start+h1->width/2) != (h3->start+h3->width/2) - (h2->start+h2->width/2) ) return( false ); data[i][0] = h1->start-off; data[i][1] = h1->width; data[i][2] = h2->start-off; data[i][3] = h2->width; data[i][4] = h3->start-off; data[i][5] = h3->width; } if ( gb==NULL ) return( true ); AddData(gb,data,instance_count,6,round); if ( gb->pt+3>=gb->end ) GrowBuffer(gb); *(gb->pt)++ = 12; *(gb->pt)++ = ishstem?2:1; /* h/v stem3 */ return( true ); } static int _SCNeedsSubsPts(SplineChar *sc,int layer) { RefChar *ref; if ( sc->hstem==NULL && sc->vstem==NULL ) return( false ); if ( sc->layers[layer].splines!=NULL ) return( sc->layers[layer].splines->first->hintmask==NULL ); for ( ref = sc->layers[layer].refs; ref!=NULL; ref=ref->next ) if ( ref->layers[0].splines!=NULL ) return( ref->layers[0].splines->first->hintmask==NULL ); return( false ); /* It's empty. that's easy. */ } static int SCNeedsSubsPts(SplineChar *sc,enum fontformat format,int layer) { if ( (format!=ff_mma && format!=ff_mmb) || sc->parent->mm==NULL ) { if ( !sc->hconflicts && !sc->vconflicts ) return( false ); /* No conflicts, no swap-over points needed */ return( _SCNeedsSubsPts(sc,layer)); } else { MMSet *mm = sc->parent->mm; int i; for ( i=0; iinstance_count; ++i ) if ( sc->orig_posinstances[i]->glyphcnt ) { if ( _SCNeedsSubsPts(mm->instances[i]->glyphs[sc->orig_pos],layer) ) return( true ); } return( false ); } } #ifdef FONTFORGE_CONFIG_PS_REFS_GET_SUBRS static int AlwaysSeacable(SplineChar *sc,int flags) { struct splinecharlist *d; RefChar *r; if ( sc->parent->cidmaster!=NULL ) /* Can't use seac in CID fonts, no encoding */ return( false ); if ( flags&ps_flag_noseac ) return( false ); for ( d=sc->dependents; d!=NULL; d = d->next ) { if ( d->sc->layers[layer].splines!=NULL ) /* I won't deal with things with both splines and refs. */ continue; /* skip it */ for ( r=d->sc->layers[layer].refs; r!=NULL; r=r->next ) { if ( !r->justtranslated ) break; /* Can't deal with it either way */ } if ( r!=NULL ) /* Bad transform matrix */ continue; /* Can't handle either way, skip */ for ( r=d->sc->layers[layer].refs; r!=NULL; r=r->next ) { if ( r->adobe_enc==-1 ) return( false ); /* not seacable, but could go in subr */ } r = d->sc->layers[layer].refs; if ( r->next!=NULL && r->next->next!=NULL ) return( false ); /* seac only takes 2 glyphs */ if ( r->next!=NULL && ((r->transform[4]!=0 || r->transform[5]!=0 || r->sc->width!=d->sc->width) && (r->next->transform[4]!=0 || r->next->transform[5]!=0 || r->next->sc->width!=d->sc->width))) return( false ); /* seac only allows one to be translated, and the untranslated one must have the right width */ if ( r->next==NULL && (r->transform[4]!=0 || r->transform[5]!=0 || r->sc->width!=d->sc->width)) return( false ); } /* Either always can be represented by seac, or sometimes by neither */ return( true ); } /* normally we can't put a character with hint conflicts into a subroutine */ /* (because when we would have to invoke the hints within the subr and */ /* hints are expressed as absolute positions, so if the char has been */ /* translated we can't do the hints right). BUT if the character is not */ /* translated, and if it has the right lbearing, then the hints in the */ /* ref will match those in the character and we can use a subroutine for */ /* both */ /* If at least one ref fits our requirements then return true */ /* The same reasoning applies to flex hints. There are absolute expressions */ /* in them too. */ static int SpecialCaseConflicts(SplineChar *sc) { struct splinecharlist *d; RefChar *r; DBounds sb, db; SplineCharFindBounds(sc,&sb); for ( d=sc->dependents; d!=NULL; d = d->next ) { SplineCharFindBounds(d->sc,&db); if ( db.minx != sb.minx ) continue; for ( r=d->sc->layers[layer].refs; r!=NULL; r=r->next ) if ( r->sc == sc && r->justtranslated && r->transform[4]==0 && r->transform[5]==0 ) return( true ); } return( false ); } static BasePoint *FigureStartStop(SplineChar *sc, GlyphInfo *gi ) { int m, didfirst; SplineChar *msc; SplineSet *spl; RefChar *r; BasePoint *startstop; /* We need to know the location of the first point on the */ /* first path (need to rmoveto it, and the location of the */ /* last point on the last path (will need to move from it */ /* for the next component) */ startstop = calloc(2*gi->instance_count,sizeof(BasePoint)); for ( m=0; minstance_count; ++m ) { if ( gi->instance_count==1 || sc->parent->mm==NULL ) msc = sc; else msc = sc->parent->mm->instances[m]->glyphs[sc->orig_pos]; didfirst = false; spl = msc->layers[layer].splines; if ( spl!=NULL ) { startstop[0] = spl->first->me; didfirst = true; while ( spl!=NULL ) { /* Closepath does NOT set the current point */ /* Remember we reverse PostScript */ if ( spl->last==spl->first && spl->first->next!=NULL && spl->first->next->knownlinear ) startstop[1] = spl->first->next->to->me; else startstop[1] = spl->last->me; spl = spl->next; } } for ( r=msc->layers[layer].refs; r!=NULL; r=r->next ) { spl = r->layers[0].splines; if ( spl!=NULL ) { if ( !didfirst ) startstop[0] = spl->first->me; didfirst = true; } while ( spl!=NULL ) { /* Closepath does NOT set the current point */ /* Remember we reverse PostScript */ if ( spl->last==spl->first && spl->first->next!=NULL && spl->first->next->knownlinear ) startstop[1] = spl->first->next->to->me; else startstop[1] = spl->last->me; spl = spl->next; } } } return( startstop ); } #endif /* FONTFORGE_CONFIG_PS_REFS_GET_SUBRS */ int SFOneWidth(SplineFont *sf) { int width, i; width = -2; for ( i=0; iglyphcnt; ++i ) if ( SCWorthOutputting(sf->glyphs[i]) && (strcmp(sf->glyphs[i]->name,".notdef")!=0 || sf->glyphs[i]->layers[ly_fore].splines!=NULL)) { /* Only trust the width of notdef if it's got some content */ /* (at least as far as fixed pitch determination goes) */ if ( width==-2 ) width = sf->glyphs[i]->width; else if ( width!=sf->glyphs[i]->width ) { width = -1; break; } } return(width); } int CIDOneWidth(SplineFont *_sf) { int width, i; int k; SplineFont *sf; if ( _sf->cidmaster!=NULL ) _sf = _sf->cidmaster; width = -2; k=0; do { sf = _sf->subfonts==NULL? _sf : _sf->subfonts[k]; for ( i=0; iglyphcnt; ++i ) if ( SCWorthOutputting(sf->glyphs[i]) && strcmp(sf->glyphs[i]->name,".null")!=0 && strcmp(sf->glyphs[i]->name,"nonmarkingreturn")!=0 && (strcmp(sf->glyphs[i]->name,".notdef")!=0 || sf->glyphs[i]->layers[ly_fore].splines!=NULL)) { /* Only trust the width of notdef if it's got some content */ /* (at least as far as fixed pitch determination goes) */ if ( width==-2 ) width = sf->glyphs[i]->width; else if ( width!=sf->glyphs[i]->width ) { width = -1; break; } } ++k; } while ( k<_sf->subfontcnt ); return(width); } /* ************************************************************************** */ /* ********************** Type2 PostScript CharStrings ********************** */ /* ************************************************************************** */ static real myround2(real pos, int round) { if ( round ) return( rint(pos)); return( rint(65536*pos)/65536 ); } static void AddNumber2(GrowBuf *gb, real pos, int round) { int val, factor; unsigned char *str; if ( gb->pt+5>=gb->end ) GrowBuffer(gb); pos = rint(65536*pos)/65536; if ( round ) pos = rint(pos); str = gb->pt; if ( pos>32767.99 || pos<-32768 ) { /* same logic for big ints and reals */ if ( pos>0x3fffffff || pos<-0x40000000 ) { LogError( _("Number out of range: %g in type2 output (must be [-65536,65535])\n"), pos ); if ( pos>0 ) pos = 0x3fffffff; else pos = -0x40000000; } for ( factor=2; factor<32768; factor<<=2 ) if ( pos/factor<32767.99 && pos/factor>-32768 ) break; AddNumber2(gb,pos/factor,false); AddNumber2(gb,factor,false); if ( gb->pt+2>=gb->end ) GrowBuffer(gb); *(gb->pt++) = 0x0c; /* Multiply operator */ *(gb->pt++) = 0x18; } else if ( pos!=floor(pos )) { val = pos*65536; *str++ = '\377'; *str++ = (val>>24)&0xff; *str++ = (val>>16)&0xff; *str++ = (val>>8)&0xff; *str++ = val&0xff; } else { val = rint(pos); if ( pos>=-107 && pos<=107 ) *str++ = val+139; else if ( pos>=108 && pos<=1131 ) { val -= 108; *str++ = (val>>8)+247; *str++ = val&0xff; } else if ( pos>=-1131 && pos<=-108 ) { val = -val; val -= 108; *str++ = (val>>8)+251; *str++ = val&0xff; } else { *str++ = 28; *str++ = (val>>8)&0xff; *str++ = val&0xff; } } gb->pt = str; } static void AddMask2(GrowBuf *gb,uint8 mask[12],int cnt, int oper) { int i; if ( gb->pt+1+((cnt+7)>>3)>=gb->end ) GrowBuffer(gb); *gb->pt++ = oper; /* hintmask,cntrmask */ for ( i=0; i< ((cnt+7)>>3); ++i ) *gb->pt++ = mask[i]; } static void CounterHints2(GrowBuf *gb, SplineChar *sc, int hcnt) { int i; for ( i=0; icountermask_cnt; ++i ) AddMask2(gb,sc->countermasks[i],hcnt,20); /* cntrmask */ } static int HintSetup2(GrowBuf *gb,struct hintdb *hdb, SplinePoint *to, int break_subr ) { /* We might get a point with a hintmask in a glyph with no conflicts */ /* (ie. the initial point when we return to it at the end of the splineset*/ /* in that case hdb->cnt will be 0 and we should ignore it */ /* components in subroutines depend on not having any hintmasks */ if ( to->hintmask==NULL || hdb->cnt==0 || hdb->noconflicts || hdb->skiphm ) return( false ); if ( memcmp(hdb->mask,*to->hintmask,(hdb->cnt+7)/8)==0 ) return( false ); if ( break_subr ) BreakSubroutine(gb,hdb); AddMask2(gb,*to->hintmask,hdb->cnt,19); /* hintmask */ memcpy(hdb->mask,*to->hintmask,sizeof(HintMask)); hdb->donefirsthm = true; if ( break_subr ) StartNextSubroutine(gb,hdb); return( true ); } static void moveto2(GrowBuf *gb,struct hintdb *hdb,SplinePoint *to, int round) { BasePoint temp, *tom; if ( gb->pt+18 >= gb->end ) GrowBuffer(gb); BreakSubroutine(gb,hdb); HintSetup2(gb,hdb,to,false); tom = &to->me; if ( round ) { temp.x = rint(tom->x); temp.y = rint(tom->y); tom = &temp; } if ( hdb->current.x==tom->x ) { AddNumber2(gb,tom->y-hdb->current.y,round); *(gb->pt)++ = 4; /* v move to */ } else if ( hdb->current.y==tom->y ) { AddNumber2(gb,tom->x-hdb->current.x,round); *(gb->pt)++ = 22; /* h move to */ } else { AddNumber2(gb,tom->x-hdb->current.x,round); AddNumber2(gb,tom->y-hdb->current.y,round); *(gb->pt)++ = 21; /* r move to */ } hdb->current.x = rint(32768*tom->x)/32768; hdb->current.y = rint(32768*tom->y)/32768; StartNextSubroutine(gb,hdb); } static Spline *lineto2(GrowBuf *gb,struct hintdb *hdb,Spline *spline, Spline *done, int round) { int cnt, hv, hvcnt; Spline *test, *lastgood, *lasthvgood; BasePoint temp1, temp2, *tom, *fromm; int donehm; lastgood = NULL; for ( test=spline, cnt=0; test->knownlinear && cnt<15; ) { ++cnt; lastgood = test; test = test->to->next; /* it will be smaller to use a closepath operator so ignore the */ /* ultimate spline */ if ( test==done || test==NULL || test->to->next==done ) break; } HintSetup2(gb,hdb,spline->to,true); hv = -1; hvcnt=1; lasthvgood = NULL; if ( spline->from->me.x==spline->to->me.x ) hv = 1; /* Vertical */ else if ( spline->from->me.y==spline->to->me.y ) hv = 0; /* Horizontal */ donehm = true; if ( hv!=-1 ) { lasthvgood = spline; hvcnt = 1; if ( cnt!=1 ) { for ( test=spline->to->next; test!=NULL ; test = test->to->next ) { fromm = &test->from->me; if ( round ) { temp2.x = rint(fromm->x); temp2.y = rint(fromm->y); fromm = &temp2; } tom = &test->to->me; if ( round ) { temp1.x = rint(tom->x); temp1.y = rint(tom->y); tom = &temp1; } if ( hv==1 && tom->y==fromm->y ) hv = 0; else if ( hv==0 && tom->x==fromm->x ) hv = 1; else break; lasthvgood = test; ++hvcnt; if ( test==lastgood ) break; } } donehm = true; if ( hvcnt==cnt || hvcnt>=2 ) { /* It's more efficient to do some h/v linetos */ for ( test=spline; ; test = test->to->next ) { if ( !donehm && test->to->hintmask!=NULL ) break; donehm = false; fromm = &test->from->me; if ( round ) { temp2.x = rint(fromm->x); temp2.y = rint(fromm->y); fromm = &temp2; } tom = &test->to->me; if ( round ) { temp1.x = rint(tom->x); temp1.y = rint(tom->y); tom = &temp1; } if ( fromm->x==tom->x ) AddNumber2(gb,tom->y-fromm->y,round); else AddNumber2(gb,tom->x-fromm->x,round); hdb->current.x = rint(32768*tom->x)/32768; hdb->current.y = rint(32768*tom->y)/32768; if ( test==lasthvgood ) { test = test->to->next; break; } } if ( gb->pt+1 >= gb->end ) GrowBuffer(gb); *(gb->pt)++ = spline->from->me.x==spline->to->me.x? 7 : 6; return( test ); } } for ( test=spline; test!=NULL; test = test->to->next ) { if ( !donehm && test->to->hintmask!=NULL ) break; donehm = false; fromm = &test->from->me; if ( round ) { temp2.x = rint(fromm->x); temp2.y = rint(fromm->y); fromm = &temp2; } tom = &test->to->me; if ( round ) { temp1.x = rint(tom->x); temp1.y = rint(tom->y); tom = &temp1; } AddNumber2(gb,tom->x-fromm->x,round); AddNumber2(gb,tom->y-fromm->y,round); hdb->current.x = rint(32768*tom->x)/32768; hdb->current.y = rint(32768*tom->y)/32768; if ( test==lastgood ) { test = test->to->next; break; } } if ( gb->pt+1 >= gb->end ) GrowBuffer(gb); *(gb->pt)++ = 5; /* r line to */ return( test ); } static Spline *curveto2(GrowBuf *gb,struct hintdb *hdb,Spline *spline, Spline *done, int round) { int cnt=0, hv; Spline *first; DBasePoint start; int donehm; HintSetup2(gb,hdb,spline->to,true); hv = -1; if ( hdb->current.x==myround2(spline->from->nextcp.x,round) && myround2(spline->to->prevcp.y,round)==myround2(spline->to->me.y,round) ) hv = 1; else if ( hdb->current.y==myround2(spline->from->nextcp.y,round) && myround2(spline->to->prevcp.x,round)==myround2(spline->to->me.x,round) ) hv = 0; donehm = true; if ( hv!=-1 ) { first = spline; start = hdb->current; while ( (hv==1 && hdb->current.x==myround2(spline->from->nextcp.x,round) && myround2(spline->to->prevcp.y,round)==myround2(spline->to->me.y,round) ) || (hv==0 && hdb->current.y==myround2(spline->from->nextcp.y,round) && myround2(spline->to->prevcp.x,round)==myround2(spline->to->me.x,round) ) ) { if ( !donehm && spline->to->hintmask!=NULL ) break; donehm = false; if ( hv==1 ) { AddNumber2(gb,myround2(spline->from->nextcp.y,round)-hdb->current.y,round); AddNumber2(gb,myround2(spline->to->prevcp.x,round)-myround2(spline->from->nextcp.x,round),round); AddNumber2(gb,myround2(spline->to->prevcp.y,round)-myround2(spline->from->nextcp.y,round),round); AddNumber2(gb,myround2(spline->to->me.x,round)-myround2(spline->to->prevcp.x,round),round); hv = 0; } else { AddNumber2(gb,myround2(spline->from->nextcp.x,round)-hdb->current.x,round); AddNumber2(gb,myround2(spline->to->prevcp.x,round)-myround2(spline->from->nextcp.x,round),round); AddNumber2(gb,myround2(spline->to->prevcp.y,round)-myround2(spline->from->nextcp.y,round),round); AddNumber2(gb,myround2(spline->to->me.y,round)-myround2(spline->to->prevcp.y,round),round); hv = 1; } hdb->current.x = myround2(spline->to->me.x,round); hdb->current.y = myround2(spline->to->me.y,round); ++cnt; spline = spline->to->next; if ( spline==done || spline==NULL || cnt>9 || spline->knownlinear ) break; } if ( gb->pt+1 >= gb->end ) GrowBuffer(gb); *(gb->pt)++ = ( start.x==myround2(first->from->nextcp.x,round) && myround2(first->to->prevcp.y,round)==myround2(first->to->me.y,round) )? 30:31; /* vhcurveto:hvcurveto */ return( spline ); } while ( cnt<6 ) { if ( !donehm && spline->to->hintmask!=NULL ) break; donehm = false; hv = -1; if ( hdb->current.x==myround2(spline->from->nextcp.x,round) && myround2(spline->to->prevcp.y,round)==myround2(spline->to->me.y,round) && spline->to->next!=NULL && myround2(spline->to->me.y,round)==myround2(spline->to->nextcp.y,round) && myround2(spline->to->next->to->prevcp.x,round)==myround2(spline->to->next->to->me.x,round) ) break; else if ( hdb->current.y==myround2(spline->from->nextcp.y,round) && myround2(spline->to->prevcp.x,round)==myround2(spline->to->me.x,round) && spline->to->next!=NULL && myround2(spline->to->me.x,round)==myround2(spline->to->nextcp.x,round) && myround2(spline->to->next->to->prevcp.y,round)==myround2(spline->to->next->to->me.y,round) ) break; AddNumber2(gb,myround2(spline->from->nextcp.x,round)-hdb->current.x,round); AddNumber2(gb,myround2(spline->from->nextcp.y,round)-hdb->current.y,round); AddNumber2(gb,myround2(spline->to->prevcp.x,round)-myround2(spline->from->nextcp.x,round),round); AddNumber2(gb,myround2(spline->to->prevcp.y,round)-myround2(spline->from->nextcp.y,round),round); AddNumber2(gb,myround2(spline->to->me.x,round)-myround2(spline->to->prevcp.x,round),round); AddNumber2(gb,myround2(spline->to->me.y,round)-myround2(spline->to->prevcp.y,round),round); hdb->current.x = myround2(spline->to->me.x,round); hdb->current.y = myround2(spline->to->me.y,round); ++cnt; spline = spline->to->next; if ( spline==done || spline==NULL || spline->knownlinear ) break; } if ( gb->pt+1 >= gb->end ) GrowBuffer(gb); *(gb->pt)++ = 8; /* rrcurveto */ return( spline ); } static void flexto2(GrowBuf *gb,struct hintdb *hdb,Spline *pspline,int round) { BasePoint *c0, *c1, *mid, *end, *nc0, *nc1; Spline *nspline; c0 = &pspline->from->nextcp; c1 = &pspline->to->prevcp; mid = &pspline->to->me; nspline = pspline->to->next; nc0 = &nspline->from->nextcp; nc1 = &nspline->to->prevcp; end = &nspline->to->me; HintSetup2(gb,hdb,nspline->to,true); if ( myround2(c0->y,round)==hdb->current.y && myround2(nc1->y,round)==hdb->current.y && myround2(end->y,round)==hdb->current.y && myround2(c1->y,round)==myround2(mid->y,round) && myround2(nc0->y,round)==myround2(mid->y,round) ) { if ( gb->pt+7*6+2 >= gb->end ) GrowBuffer(gb); AddNumber2(gb,myround2(c0->x,round)-hdb->current.x,round); AddNumber2(gb,myround2(c1->x,round)-myround2(c0->x,round),round); AddNumber2(gb,myround2(c1->y,round)-myround2(c0->y,round),round); AddNumber2(gb,myround2(mid->x,round)-myround2(c1->x,round),round); AddNumber2(gb,myround2(nc0->x,round)-myround2(mid->x,round),round); AddNumber2(gb,myround2(nc1->x,round)-myround2(nc0->x,round),round); AddNumber2(gb,myround2(end->x,round)-myround2(nc1->x,round),round); *gb->pt++ = 12; *gb->pt++ = 34; /* hflex */ } else { if ( gb->pt+11*6+2 >= gb->end ) GrowBuffer(gb); AddNumber2(gb,myround2(c0->x,round)-hdb->current.x,round); AddNumber2(gb,myround2(c0->y,round)-hdb->current.y,round); AddNumber2(gb,myround2(c1->x,round)-myround2(c0->x,round),round); AddNumber2(gb,myround2(c1->y,round)-myround2(c0->y,round),round); AddNumber2(gb,myround2(mid->x,round)-myround2(c1->x,round),round); AddNumber2(gb,myround2(mid->y,round)-myround2(c1->y,round),round); AddNumber2(gb,myround2(nc0->x,round)-myround2(mid->x,round),round); AddNumber2(gb,myround2(nc0->y,round)-myround2(mid->y,round),round); AddNumber2(gb,myround2(nc1->x,round)-myround2(nc0->x,round),round); AddNumber2(gb,myround2(nc1->y,round)-myround2(nc0->y,round),round); if ( hdb->current.y==myround2(end->y,round) ) AddNumber2(gb,myround2(end->x,round)-myround2(nc1->x,round),round); else AddNumber2(gb,myround2(end->y,round)-myround2(nc1->y,round),round); *gb->pt++ = 12; *gb->pt++ = 37; /* flex1 */ } hdb->current.x = rint(32768*end->x)/32768; hdb->current.y = rint(32768*end->y)/32768; } static void CvtPsSplineSet2(GrowBuf *gb, SplinePointList *spl, struct hintdb *hdb, int is_order2,int round ) { Spline *spline, *first; SplinePointList temp, *freeme = NULL; int unhinted = true;; if ( is_order2 ) freeme = spl = SplineSetsPSApprox(spl); for ( ; spl!=NULL; spl = spl->next ) { first = NULL; SplineSetReverse(spl); /* PostScript and TrueType store their splines in in reverse */ /* orientations. Annoying. Oh well. I shall adopt TrueType and */ /* If I reverse the PS splinesets after reading them in, and then */ /* again when saving them out, all should be well */ if ( spl->first->flexy || spl->first->flexx ) { /* can't handle a flex (mid) point as the first point. rotate the */ /* list by one, this is possible because only closed paths have */ /* points marked as flex, and because we can't have two flex mid- */ /* points in a row */ if ( spl->first->hintmask==NULL || spl->first->next->to->hintmask!=NULL ) { /* But we can't rotate it if we expect it to provide us with */ /* a hintmask. */ temp = *spl; temp.first = temp.last = spl->first->next->to; spl = &temp; } if ( spl->first->flexy || spl->first->flexx ) { /* If we couldn't rotate, or if we rotated to something that */ /* also is flexible, then just turn off flex. That's safe */ spl->first->flexx = spl->first->flexy = false; } } if ( unhinted && hdb->cnt>0 && spl->first->hintmask!=NULL ) { hdb->mask[0] = ~(*spl->first->hintmask)[0]; /* Make it different */ unhinted = false; } moveto2(gb,hdb,spl->first,round); for ( spline = spl->first->next; spline!=NULL && spline!=first; ) { if ( first==NULL ) first = spline; else if ( first->from==spline->to ) hdb->skiphm = true; if ( spline->to->flexx || spline->to->flexy ) { flexto2(gb,hdb,spline,round); /* does two adjacent splines */ spline = spline->to->next->to->next; } else if ( spline->knownlinear && spline->to == spl->first ) /* In Type2 we don't even need a closepath to finish this off */ /* (which is good, because there isn't a close path) */ break; else if ( spline->knownlinear ) spline = lineto2(gb,hdb,spline,first,round); else spline = curveto2(gb,hdb,spline,first,round); } hdb->skiphm = false; /* No closepath oper in type2 fonts, it's implied */ SplineSetReverse(spl); /* Of course, I have to Reverse again to get back to my convention after*/ /* saving */ } SplinePointListsFree(freeme); } bool equalWithTolerence( real a, real b, real tolerence ) { // printf("equalWithTolerence(1) a:%f b:%f tol:%f\n", a, b, tolerence ); // printf("equalWithTolerence(2) a:%lf b:%lf tol:%lf\n", a, b, tolerence ); if( tolerence == 0.0 ) return a == b; return( (b - tolerence < a) && (b + tolerence > a )); } static void DumpHints(GrowBuf *gb,StemInfo *h,int oper,int midoper,int round) { real last = 0, cur; int cnt; if ( h==NULL ) return; cnt = 0; while ( h && h->hintnumber!=-1 ) { /* Type2 hints do not support negative widths except in the case of */ /* ghost (now called edge) hints */ if ( cnt>24-2 ) { /* stack max = 48 numbers, => 24 hints, leave a bit of slop for the width */ if ( gb->pt+1>=gb->end ) GrowBuffer(gb); *gb->pt++ = midoper; cnt = 0; } cur = myround2(h->start,round) + myround2(h->width,round); if ( h->width<0 ) { AddNumber2(gb,cur-last,round); AddNumber2(gb,-myround2(h->width,round),round); cur -= myround2(h->width,round); } else if ( h->ghost ) { if ( equalWithTolerence( h->width, 20, GenerateHintWidthEqualityTolerance )) { AddNumber2(gb,myround2(h->start,round)-last+20,round); AddNumber2(gb,-20,round); cur = myround2(h->start,round); } else { AddNumber2(gb,myround2(h->start+21,round)-last,round); AddNumber2(gb,-21,round); cur = myround2(h->start+21,round)-21; } } else { AddNumber2(gb,myround2(h->start,round)-last,round); AddNumber2(gb,myround2(h->width,round),round); } last = cur; h = h->next; ++cnt; } if ( oper!=-1 ) { if ( gb->pt+1>=gb->end ) GrowBuffer(gb); *gb->pt++ = oper; } } static void DumpRefsHints(GrowBuf *gb, struct hintdb *hdb,RefChar *cur,StemInfo *h,StemInfo *v, BasePoint *trans, int round,int layer) { uint8 masks[12]; int cnt, sets=0; StemInfo *rs; /* trans has already been rounded (whole char is translated by an integral amount) */ /* If we have a subroutine containing conflicts, then its hints will match*/ /* ours exactly, and we can use its hintmasks directly */ if (( cur->sc->hconflicts || cur->sc->vconflicts ) && cur->sc->layers[layer].splines!=NULL && cur->sc->layers[layer].splines->first->hintmask!=NULL ) { AddMask2(gb,*cur->sc->layers[layer].splines->first->hintmask,hdb->cnt,19); /* hintmask */ hdb->donefirsthm = true; memcpy(hdb->mask,*cur->sc->layers[layer].splines->first->hintmask,sizeof(HintMask)); return; } if ( h==NULL && v==NULL ) IError("hintmask invoked when there are no hints"); memset(masks,'\0',sizeof(masks)); cnt = 0; while ( h!=NULL && h->hintnumber>=0 ) { /* Horizontal stems are defined by vertical bounds */ real pos = (round ? rint(h->start) : h->start) - trans->y; for ( rs = cur->sc->hstem; rs!=NULL; rs=rs->next ) { real rpos = round ? rint(rs->start) : rs->start; if ( rpos==pos && (round ? (rint(rs->width)==rint(h->width)) : (rs->width==h->width)) ) { masks[h->hintnumber>>3] |= 0x80>>(h->hintnumber&7); ++sets; break; } else if ( rpos>pos ) break; } h = h->next; ++cnt; } while ( v!=NULL && v->hintnumber>=0 ) { real pos = (round ? rint(v->start) : v->start) - trans->x; for ( rs = cur->sc->vstem; rs!=NULL; rs=rs->next ) { real rpos = round ? rint(rs->start) : rs->start; if ( rpos==pos && (round ? (rint(rs->width)==rint(v->width)) : (rs->width==v->width)) ) { masks[v->hintnumber>>3] |= 0x80>>(v->hintnumber&7); ++sets; break; } else if ( rpos>pos ) break; } v = v->next; ++cnt; } BreakSubroutine(gb,hdb); hdb->donefirsthm = true; /* if ( sets!=0 ) */ /* First ref will need a hintmask even if it has no hints (if there are conflicts) */ AddMask2(gb,masks,cnt,19); /* hintmask */ } static void DummyHintmask(GrowBuf *gb,struct hintdb *hdb) { HintMask hm; memset(hm,0,sizeof(hm)); if ( hdb->cnt!=0 ) { BreakSubroutine(gb,hdb); hdb->donefirsthm = true; AddMask2(gb,hm,hdb->cnt,19); /* hintmask */ } } static void SetTransformedHintMask(GrowBuf *gb,struct hintdb *hdb, SplineChar *sc, RefChar *ref, BasePoint *trans, int round) { HintMask hm; if ( HintMaskFromTransformedRef(ref,trans,sc,&hm)!=NULL ) { BreakSubroutine(gb,hdb); hdb->donefirsthm = true; AddMask2(gb,hm,hdb->cnt,19); /* hintmask */ } else if ( !hdb->donefirsthm ) DummyHintmask(gb,hdb); } static void ExpandRef2(GrowBuf *gb, SplineChar *sc, struct hintdb *hdb, RefChar *r, BasePoint *trans, struct pschars *subrs, int round,int layer) { BasePoint *bpt; BasePoint temp, rtrans; GlyphInfo *gi; /* The only refs I deal with here have no hint conflicts within them */ rtrans.x = r->transform[4]+trans->x; rtrans.y = r->transform[5]+trans->y; if ( round ) { rtrans.x = rint(rtrans.x); rtrans.y = rint(rtrans.y); } BreakSubroutine(gb,hdb); if ( hdb->cnt>0 && !hdb->noconflicts ) DumpRefsHints(gb,hdb,r,sc->hstem,sc->vstem,&rtrans,round,layer); /* Translate from end of last character to where this one should */ /* start (we must have one moveto operator to start off, none */ /* in the subr) */ bpt = hdb->gi->psubrs[r->sc->lsidebearing].startstop; temp.x = bpt[0].x+rtrans.x; temp.y = bpt[0].y+rtrans.y; if ( hdb->current.x!=temp.x ) AddNumber2(gb,temp.x-hdb->current.x,round); if ( hdb->current.y!=temp.y || hdb->current.x==temp.x ) AddNumber2(gb,temp.y-hdb->current.y,round); if ( gb->pt+1>=gb->end ) GrowBuffer(gb); *gb->pt++ = hdb->current.x==temp.x?4: /* vmoveto */ hdb->current.y==temp.y?22: /* hmoveto */ 21; /* rmoveto */ if ( r->sc->lsidebearing==0x7fff ) IError("Attempt to reference an unreferenceable glyph %s", r->sc->name ); gi = hdb->gi; StartNextSubroutine(gb,hdb); gi->bits[gi->bcnt].psub_index = r->sc->lsidebearing; ++gi->bcnt; gi->justbroken = true; hdb->current.x = bpt[1].x+rtrans.x; hdb->current.y = bpt[1].y+rtrans.y; } static void RSC2PS2(GrowBuf *gb, SplineChar *base,SplineChar *rsc, struct hintdb *hdb, BasePoint *trans, struct pschars *subrs, int flags, int layer ) { BasePoint subtrans; int stationary = trans->x==0 && trans->y==0; RefChar *r, *unsafe=NULL; int unsafecnt=0, allwithouthints=true; int round = (flags&ps_flag_round)? true : false; StemInfo *oldh, *oldv; int hc, vc; SplineSet *freeme, *temp; int wasntconflicted = hdb->noconflicts; if ( flags&ps_flag_nohints ) { oldh = rsc->hstem; oldv = rsc->vstem; hc = rsc->hconflicts; vc = rsc->vconflicts; rsc->hstem = NULL; rsc->vstem = NULL; rsc->hconflicts = false; rsc->vconflicts = false; } else { for ( r=rsc->layers[layer].refs; r!=NULL; r=r->next ) { /* Ensure hintmask on refs are set correctly */ if (SCNeedsSubsPts(r->sc, ff_otf, layer)) SCFigureHintMasks(r->sc, layer); if ( !r->justtranslated ) continue; if ( r->sc->hconflicts || r->sc->vconflicts ) { ++unsafecnt; unsafe = r; } else if ( r->sc->hstem!=NULL || r->sc->vstem!=NULL ) allwithouthints = false; } if ( !stationary ) allwithouthints = false; if ( allwithouthints && unsafe!=NULL && hdb->cnt!=NumberHints(&unsafe->sc,1)) allwithouthints = false; /* There are other hints elsewhere in the base glyph */ } if ( unsafe && allwithouthints ) { if ( unsafe->sc->lsidebearing!=0x7fff ) { ExpandRef2(gb,base,hdb,unsafe,trans,subrs,round,layer); } else if ( unsafe->transform[4]==0 && unsafe->transform[5]==0 ) RSC2PS2(gb,base,unsafe->sc,hdb,trans,subrs,flags,layer); else unsafe = NULL; } else unsafe = NULL; /* What is the hintmask state here? It should not matter */ freeme = NULL; temp = rsc->layers[layer].splines; if ( base!=rsc ) temp = freeme = SPLCopyTranslatedHintMasks(temp,base,rsc,trans); CvtPsSplineSet2(gb,temp,hdb,rsc->layers[layer].order2,round); SplinePointListsFree(freeme); for ( r = rsc->layers[layer].refs; r!=NULL; r = r->next ) if ( r!=unsafe ) { if ( !r->justtranslated ) { if ( !r->sc->hconflicts && !r->sc->vconflicts && !hdb->noconflicts && r->transform[1]==0 && r->transform[2]==0 && r->transform[0]>0 && r->transform[3]>0 ) SetTransformedHintMask(gb,hdb,base,r,trans,round); if ( !hdb->donefirsthm ) DummyHintmask(gb,hdb); temp = SPLCopyTransformedHintMasks(r,base,trans,layer); CvtPsSplineSet2(gb,temp,hdb,rsc->layers[layer].order2,round); SplinePointListsFree(temp); } else if ( r->sc->lsidebearing!=0x7fff && ((flags&ps_flag_nohints) || (!r->sc->hconflicts && !r->sc->vconflicts)) ) { ExpandRef2(gb,base,hdb,r,trans,subrs,round,layer); } else { subtrans.x = trans->x + r->transform[4]; subtrans.y = trans->y + r->transform[5]; if ( !hdb->noconflicts && !r->sc->hconflicts && !r->sc->vconflicts) { SetTransformedHintMask(gb,hdb,base,r,trans,round); hdb->noconflicts = true; } RSC2PS2(gb,base,r->sc,hdb,&subtrans,subrs,flags,layer); hdb->noconflicts = wasntconflicted; } } if ( flags&ps_flag_nohints ) { rsc->hstem = oldh; rsc->vstem = oldv; rsc->hconflicts = hc; rsc->vconflicts = vc; } } static unsigned char *SplineChar2PS2(SplineChar *sc,int *len, int nomwid, int defwid, struct pschars *subrs, int flags, GlyphInfo *gi) { GrowBuf gb; unsigned char *ret; struct hintdb hdb; StemInfo *oldh, *oldv; int hc, vc; SplineChar *scs[MmMax]; int round = (flags&ps_flag_round)? true : false; HintMask *hm = NULL; BasePoint trans; if ( autohint_before_generate && sc->changedsincelasthinted && !sc->manualhints && !(flags&ps_flag_nohints)) SplineCharAutoHint(sc,gi->layer,NULL); if ( !(flags&ps_flag_nohints) && SCNeedsSubsPts(sc,ff_otf,gi->layer)) SCFigureHintMasks(sc,gi->layer); if ( flags&ps_flag_nohints ) { oldh = sc->hstem; oldv = sc->vstem; hc = sc->hconflicts; vc = sc->vconflicts; sc->hstem = NULL; sc->vstem = NULL; sc->hconflicts = false; sc->vconflicts = false; } else if ( sc->layers[gi->layer].splines!=NULL && !sc->vconflicts && !sc->hconflicts ) { hm = sc->layers[gi->layer].splines->first->hintmask; sc->layers[gi->layer].splines->first->hintmask = NULL; } memset(&gb,'\0',sizeof(gb)); GrowBuffer(&gb); /* store the width on the stack */ if ( sc->width==defwid ) /* Don't need to do anything for the width */; else AddNumber2(&gb,sc->width-nomwid,round); memset(&trans,'\0',sizeof(trans)); memset(&hdb,'\0',sizeof(hdb)); hdb.scs = scs; hdb.gi = gi; if ( gi!=NULL ) gi->bcnt = -1; scs[0] = sc; hdb.noconflicts = !sc->hconflicts && !sc->vconflicts; hdb.cnt = NumberHints(hdb.scs,1); DumpHints(&gb,sc->hstem,sc->hconflicts || sc->vconflicts?18:1, sc->hconflicts || sc->vconflicts?18:1,round); DumpHints(&gb,sc->vstem,sc->hconflicts || sc->vconflicts?-1:3, sc->hconflicts || sc->vconflicts?23:3,round); CounterHints2(&gb, sc, hdb.cnt ); RSC2PS2(&gb,sc,sc,&hdb,&trans,subrs,flags,gi->layer); if ( gi->bcnt==-1 ) { /* If it's whitespace */ gi->bcnt = 0; StartNextSubroutine(&gb,&hdb); } BreakSubroutine(&gb,&hdb); MoveSubrsToChar(gi); ret = NULL; free(gb.base); if ( flags&ps_flag_nohints ) { sc->hstem = oldh; sc->vstem = oldv; sc->hconflicts = hc; sc->vconflicts = vc; } else if ( hm!=NULL ) sc->layers[gi->layer].splines->first->hintmask = hm; return( ret ); } static SplinePoint *LineTo(SplinePoint *last, int x, int y) { SplinePoint *sp = SplinePointCreate(x,y); SplineMake3(last,sp); return( sp ); } static void Type2NotDefSplines(SplineFont *sf,SplineChar *sc,int layer) { /* I'd always assumed that Type2 notdefs would look like type1 notdefs */ /* but they don't, they look like truetype notdefs. And Ralf Stubner */ /* points out that the spec says they should. So make a box here */ int stem, ymax; SplineSet *inner, *ss; StemInfo *h, *hints; stem = (sf->ascent+sf->descent)/20; ymax = 2*sf->ascent/3; ss = chunkalloc(sizeof(SplineSet)); ss->first = ss->last = SplinePointCreate(stem,0); ss->last = LineTo(ss->last,stem,ymax); ss->last = LineTo(ss->last,sc->width-stem,ymax); ss->last = LineTo(ss->last,sc->width-stem,0); SplineMake3(ss->last,ss->first); ss->last = ss->first; ss->next = inner = chunkalloc(sizeof(SplineSet)); inner->first = inner->last = SplinePointCreate(2*stem,stem); inner->last = LineTo(inner->last,sc->width-2*stem,stem); inner->last = LineTo(inner->last,sc->width-2*stem,ymax-stem); inner->last = LineTo(inner->last,2*stem,ymax-stem); SplineMake3(inner->last,inner->first); inner->last = inner->first; sc->layers[layer].splines = ss; hints = chunkalloc(sizeof(StemInfo)); hints->start = stem; hints->width = stem; hints->next = h = chunkalloc(sizeof(StemInfo)); h->start = sc->width-2*stem; h->width = stem; sc->vstem = hints; hints = chunkalloc(sizeof(StemInfo)); hints->start = 0; hints->width = stem; hints->next = h = chunkalloc(sizeof(StemInfo)); h->start = ymax-stem; h->width = stem; sc->hstem = hints; } #ifdef FONTFORGE_CONFIG_PS_REFS_GET_SUBRS /* This char has hint conflicts. Check to see if we can put it into a subr */ /* in spite of that. If there is at least one dependent character which: */ /* refers to us without translating us */ /* and all its other refs contain no hints at all */ static int Type2SpecialCase(SplineChar *sc) { struct splinecharlist *d; RefChar *r; for ( d=sc->dependents; d!=NULL; d=d->next ) { for ( r=d->sc->layers[layer].refs; r!=NULL; r = r->next ) { if ( autohint_before_generate && r->sc!=NULL && r->sc->changedsincelasthinted && !r->sc->manualhints ) SplineCharAutoHint(r->sc,NULL); if ( r->transform[0]!=1 || r->transform[1]!=0 || r->transform[2]!=0 || r->transform[3]!=1 ) break; if ( r->sc!=sc && (r->sc->hstem!=NULL || r->sc->vstem!=NULL)) break; if ( r->sc==sc && (r->transform[4]!=0 || r->transform[5]!=0)) break; } if ( r==NULL ) return( true ); } return( false ); } #endif /* FONTFORGE_CONFIG_PS_REFS_GET_SUBRS */ /* Mark those glyphs which can live totally in subrs */ static void SplineFont2FullSubrs2(int flags,GlyphInfo *gi) { int i; SplineChar *sc; #ifdef FONTFORGE_CONFIG_PS_REFS_GET_SUBRS int cc; RefChar *r; struct potentialsubrs *ps; SplineSet *spl; #endif /* FONTFORGE_CONFIG_PS_REFS_GET_SUBRS */ if ( !autohint_before_generate && !(flags&ps_flag_nohints)) SplineFontAutoHintRefs(gi->sf,gi->layer); for ( i=0; iglyphcnt; ++i ) if ( (sc=gi->gb[i].sc)!=NULL ) sc->lsidebearing = 0x7fff; /* This code allows us to put whole glyphs into subroutines */ /* I found slight improvements in space on some fonts, and large increases */ /* in others. So I'm disabling it for now */ #ifdef FONTFORGE_CONFIG_PS_REFS_GET_SUBRS /* We don't allow refs to refs. It's too complex */ for ( i=0; iglyphcnt; ++i ) if ( (sc=gi->gb[i].sc)!=NULL ) { if ( SCWorthOutputting(sc) && (( sc->layers[layer].refs==NULL && sc->dependents!=NULL && ( (!sc->hconflicts && !sc->vconflicts) || Type2SpecialCase(sc)) ) )) { /* if the glyph is a single contour with no hintmasks then */ /* our single contour code will find it. If we do it here too */ /* we'll get a subr which points to another subr. Very dull and */ /* a waste of space */ cc = 0; for ( spl=sc->layers[layer].splines; spl!=NULL; spl=spl->next ) ++cc; for ( r= sc->layers[layer].refs; r!=NULL && cc<2 ; r=r->next ) { for ( spl=r->layers[0].splines; spl!=NULL; spl=spl->next ) ++cc; } if ( cc<2 ) continue; /* Put the */ /* character into a subr if it is referenced by other characters */ if ( gi->pcnt>=gi->pmax ) gi->psubrs = realloc(gi->psubrs,(gi->pmax+=gi->glyphcnt)*sizeof(struct potentialsubrs)); ps = &gi->psubrs[gi->pcnt]; memset(ps,0,sizeof(*ps)); /* set cnt to 0 */ ps->idx = gi->pcnt++; ps->full_glyph_index = i; sc->lsidebearing = gi->pcnt-1; ps->startstop = FigureStartStop(sc,gi); } } #endif /* FONTFORGE_CONFIG_PS_REFS_GET_SUBRS */ } struct pschars *SplineFont2ChrsSubrs2(SplineFont *sf, int nomwid, int defwid, const int *bygid, int cnt, int flags, struct pschars **_subrs, int layer) { struct pschars *subrs, *chrs; int i,j,k,scnt; SplineChar *sc; GlyphInfo gi; SplineChar dummynotdef; if ( !autohint_before_generate && !(flags&ps_flag_nohints)) SplineFontAutoHintRefs(sf,layer); memset(&gi,0,sizeof(gi)); memset(&gi.hashed,-1,sizeof(gi.hashed)); gi.instance_count = 1; gi.sf = sf; gi.layer = layer; gi.glyphcnt = cnt; gi.bygid = bygid; gi.gb = calloc(cnt,sizeof(struct glyphbits)); gi.pmax = 3*cnt; gi.psubrs = malloc(gi.pmax*sizeof(struct potentialsubrs)); for ( i=0; ilayer_cnt; dummynotdef.layers = calloc(sf->layer_cnt,sizeof(Layer)); dummynotdef.width = SFOneWidth(sf); if ( dummynotdef.width==-1 ) dummynotdef.width = (sf->ascent+sf->descent)/2; Type2NotDefSplines(sf,&dummynotdef,layer); } else if ( gid!=-1 ) sc = sf->glyphs[gid]; else continue; gi.gb[i].sc = sc; if ( autohint_before_generate && sc!=NULL && sc->changedsincelasthinted && !sc->manualhints && !(flags&ps_flag_nohints)) SplineCharAutoHint(sc,layer,NULL); sc->lsidebearing = 0x7fff; } MarkTranslationRefs(sf,layer); SplineFont2FullSubrs2(flags,&gi); for ( i=0; i(gi.psubrs[i].cnt*4)+gi.psubrs[i].len+1 ) gi.psubrs[i].idx = scnt++; else gi.psubrs[i].idx = -1; } subrs = calloc(1,sizeof(struct pschars)); subrs->cnt = scnt; subrs->next = scnt; subrs->lens = malloc(scnt*sizeof(int)); subrs->values = malloc(scnt*sizeof(unsigned char *)); subrs->bias = scnt<1240 ? 107 : scnt<33900 ? 1131 : 32768; for ( i=0; ilens[scnt] = gi.psubrs[i].len+1; subrs->values[scnt] = malloc(subrs->lens[scnt]); memcpy(subrs->values[scnt],gi.psubrs[i].data,gi.psubrs[i].len); subrs->values[scnt][gi.psubrs[i].len] = 11; /* Add a return to end of subr */ } } chrs = calloc(1,sizeof(struct pschars)); chrs->cnt = cnt; chrs->next = cnt; chrs->lens = malloc(cnt*sizeof(int)); chrs->values = malloc(cnt*sizeof(unsigned char *)); chrs->keys = malloc(cnt*sizeof(char *)); for ( i=0; isc==NULL ) continue; chrs->keys[i] = copy(gb->sc->name); for ( k=0; k<2; ++k ) if ( k!=0 || gb->sc->lsidebearing!=0x7fff ) { for ( j=0; jbcnt; ++j ) { if ( k!=0 || j!=0 ) len += gb->bits[j].dlen; if ( k==1 && gb->sc->lsidebearing!=0x7fff ) { int si = gi.psubrs[ gb->sc->lsidebearing ].idx; len += 1 + (si<=107 && si>=-107?1:si<=1131 && si>=-1131?2:si>=-32768 && si<32767?3:8); break; } if ( gi.psubrs[ gb->bits[j].psub_index ].idx==-1 ) len += gi.psubrs[ gb->bits[j].psub_index ].len; else { int si = gi.psubrs[ gb->bits[j].psub_index ].idx - subrs->bias; /* space for the number (subroutine index) */ if ( si>=-107 && si<=107 ) ++len; else if ( si>=-1131 && si<=1131 ) len += 2; else if ( si>=-32768 && si<=32767 ) len += 3; else len += 8; /* space for the subroutine operator */ ++len; } } if ( k==0 ) { int si = gi.psubrs[ gb->sc->lsidebearing ].idx; subrs->lens[si] = len+1; vals = subrs->values[si] = malloc(len+2); } else { chrs->lens[i] = len+1; vals = chrs->values[i] = malloc(len+2); /* space for endchar and a final NUL (which is really meaningless, but makes me feel better) */ } len = 0; for ( j=0; jbcnt; ++j ) { int si; if ( k!=0 || j!=0 ) { memcpy(vals+len,gb->bits[j].data,gb->bits[j].dlen); len += gb->bits[j].dlen; } si = 0x80000000; if ( k==1 && gb->sc->lsidebearing!=0x7fff ) si = gi.psubrs[ gb->sc->lsidebearing ].idx - subrs->bias; else if ( gi.psubrs[ gb->bits[j].psub_index ].idx==-1 ) { memcpy(vals+len,gi.psubrs[ gb->bits[j].psub_index ].data, gi.psubrs[ gb->bits[j].psub_index ].len); len += gi.psubrs[ gb->bits[j].psub_index ].len; } else si = gi.psubrs[ gb->bits[j].psub_index ].idx - subrs->bias; if ( si!=0x80000000 ) { /* space for the number (subroutine index) */ if ( si>=-107 && si<=107 ) vals[len++] = si+139; else if ( si>0 && si<=1131 ) { si-=108; vals[len++] = (si>>8)+247; vals[len++] = si&0xff; } else if ( si>=-1131 && si<0 ) { si=(-si)-108; vals[len++] = (si>>8)+251; vals[len++] = si&0xff; } else if ( si>=-32768 && si<=32767 ) { vals[len++] = 28; vals[len++] = (si>>8)&0xff; vals[len++] = si&0xff; } else { /* store as fixed point, then multiply by 64. Takes 8 bytes */ si *= (65536/64); vals[len++] = '\377'; vals[len++] = (si>>24)&0xff; vals[len++] = (si>>16)&0xff; vals[len++] = (si>>8)&0xff; vals[len++] = si&0xff; vals[len++] = 64 + 139; vals[len++] = 0xc; vals[len++] = 0x18; /* Multiply */ } /* space for the subroutine operator */ vals[len++] = 10; } if ( k==1 && gb->sc->lsidebearing!=0x7fff ) break; } if ( k==0 ) { vals[len++] = 11; /* return */ vals[len] = '\0'; } else { vals[len++] = 14; /* endchar */ vals[len] = '\0'; } } } GIFree(&gi,&dummynotdef); *_subrs = subrs; return( chrs ); } struct pschars *CID2ChrsSubrs2(SplineFont *cidmaster,struct fd2data *fds, int flags, struct pschars **_glbls, int layer) { struct pschars *chrs, *glbls; int i, j, cnt, cid, max, fd; int *scnts; SplineChar *sc; SplineFont *sf = NULL; /* In a cid-keyed font, cid 0 is defined to be .notdef so there are no */ /* special worries. If it is defined we use it. If it is not defined */ /* we add it. */ GlyphInfo gi; SplineChar dummynotdef; max = 0; for ( i=0; isubfontcnt; ++i ) { if ( maxsubfonts[i]->glyphcnt ) max = cidmaster->subfonts[i]->glyphcnt; MarkTranslationRefs(cidmaster->subfonts[i],layer); } cnt = 1; /* for .notdef */ for ( cid = 1; cidsubfontcnt; ++i ) { sf = cidmaster->subfonts[i]; if ( cidglyphcnt && (sc=sf->glyphs[cid])!=NULL ) { sc->ttf_glyph = -1; sc->lsidebearing = 0x7fff; if ( SCWorthOutputting(sc)) ++cnt; break; } } } memset(&gi,0,sizeof(gi)); memset(&gi.hashed,-1,sizeof(gi.hashed)); gi.instance_count = 1; gi.sf = sf; gi.glyphcnt = cnt; gi.bygid = NULL; gi.gb = calloc(cnt,sizeof(struct glyphbits)); gi.pmax = 3*cnt; gi.psubrs = malloc(gi.pmax*sizeof(struct potentialsubrs)); gi.layer = layer; for ( cid = cnt = 0; cidsubfontcnt; ++i ) { sf = cidmaster->subfonts[i]; if ( cidglyphcnt && SCWorthOutputting(sf->glyphs[cid]) ) break; } if ( cid!=0 && i==cidmaster->subfontcnt ) { sc=NULL; } else if ( i==cidmaster->subfontcnt ) { /* They didn't define CID 0 */ sc = &dummynotdef; /* Place it in the final subfont (which is what sf points to) */ memset(sc,0,sizeof(dummynotdef)); dummynotdef.name = ".notdef"; dummynotdef.parent = sf; dummynotdef.layer_cnt = layer+1; dummynotdef.layers = calloc(layer+1,sizeof(Layer)); dummynotdef.width = SFOneWidth(sf); if ( dummynotdef.width==-1 ) dummynotdef.width = (sf->ascent+sf->descent); Type2NotDefSplines(sf,&dummynotdef,layer); gi.gb[cnt].sc = sc; gi.gb[cnt].fd = i = cidmaster->subfontcnt-1; } else { gi.gb[cnt].sc = sc = sf->glyphs[cid]; gi.gb[cnt].fd = i; } if ( sc!=NULL ) { sc->lsidebearing = 0x7fff; gi.active = &gi.gb[cnt]; sc->ttf_glyph = cnt++; SplineChar2PS2(sc,NULL,fds[i].nomwid,fds[i].defwid,NULL,flags,&gi); } ff_progress_next(); } scnts = calloc( cidmaster->subfontcnt+1,sizeof(int)); for ( i=0; i(gi.psubrs[i].cnt*4)+gi.psubrs[i].len+1 ) gi.psubrs[i].idx = scnts[gi.psubrs[i].fd+1]++; } glbls = calloc(1,sizeof(struct pschars)); glbls->cnt = scnts[0]; glbls->next = scnts[0]; glbls->lens = malloc(scnts[0]*sizeof(int)); glbls->values = malloc(scnts[0]*sizeof(unsigned char *)); glbls->bias = scnts[0]<1240 ? 107 : scnts[0]<33900 ? 1131 : 32768; for ( fd=0; fdsubfontcnt; ++fd ) { fds[fd].subrs = calloc(1,sizeof(struct pschars)); fds[fd].subrs->cnt = scnts[fd+1]; fds[fd].subrs->next = scnts[fd+1]; fds[fd].subrs->lens = malloc(scnts[fd+1]*sizeof(int)); fds[fd].subrs->values = malloc(scnts[fd+1]*sizeof(unsigned char *)); fds[fd].subrs->bias = scnts[fd+1]<1240 ? 107 : scnts[fd+1]<33900 ? 1131 : 32768; } free( scnts); for ( i=0; ilens[scnt] = gi.psubrs[i].len+1; subrs->values[scnt] = malloc(subrs->lens[scnt]); memcpy(subrs->values[scnt],gi.psubrs[i].data,gi.psubrs[i].len); subrs->values[scnt][gi.psubrs[i].len] = 11; /* Add a return to end of subr */ } } chrs = calloc(1,sizeof(struct pschars)); chrs->cnt = cnt; chrs->next = cnt; chrs->lens = malloc(cnt*sizeof(int)); chrs->values = malloc(cnt*sizeof(unsigned char *)); chrs->keys = malloc(cnt*sizeof(char *)); for ( i=0; ikeys[i] = copy(gb->sc->name); for ( j=0; jbcnt; ++j ) { len += gb->bits[j].dlen; if ( gi.psubrs[ gb->bits[j].psub_index ].idx==-1 ) len += gi.psubrs[ gb->bits[j].psub_index ].len; else { struct pschars *subrs = gi.psubrs[gb->bits[j].psub_index].fd==-1 ? glbls : fds[gi.psubrs[gb->bits[j].psub_index].fd].subrs; int si = gi.psubrs[ gb->bits[j].psub_index ].idx - subrs->bias; /* space for the number (subroutine index) */ if ( si>=-107 && si<=107 ) ++len; else if ( si>=-1131 && si<=1131 ) len += 2; else if ( si>=-32768 && si<=32767 ) len += 3; else len += 8; /* space for the subroutine operator */ ++len; } } chrs->lens[i] = len+1; chrs->values[i] = malloc(len+2); /* space for endchar and a final NUL (which is really meaningless, but makes me feel better) */ len = 0; for ( j=0; jbcnt; ++j ) { memcpy(chrs->values[i]+len,gb->bits[j].data,gb->bits[j].dlen); len += gb->bits[j].dlen; if ( gi.psubrs[ gb->bits[j].psub_index ].idx==-1 ) { memcpy(chrs->values[i]+len,gi.psubrs[ gb->bits[j].psub_index ].data, gi.psubrs[ gb->bits[j].psub_index ].len); len += gi.psubrs[ gb->bits[j].psub_index ].len; } else { struct pschars *subrs = gi.psubrs[gb->bits[j].psub_index].fd==-1 ? glbls : fds[gi.psubrs[gb->bits[j].psub_index].fd].subrs; int si = gi.psubrs[ gb->bits[j].psub_index ].idx - subrs->bias; /* space for the number (subroutine index) */ if ( si>=-107 && si<=107 ) chrs->values[i][len++] = si+139; else if ( si>0 && si<=1131 ) { si-=108; chrs->values[i][len++] = (si>>8)+247; chrs->values[i][len++] = si&0xff; } else if ( si>=-1131 && si<0 ) { si=(-si)-108; chrs->values[i][len++] = (si>>8)+251; chrs->values[i][len++] = si&0xff; } else if ( si>=-32768 && si<=32767 ) { chrs->values[i][len++] = 28; chrs->values[i][len++] = (si>>8)&0xff; chrs->values[i][len++] = si&0xff; } else { /* store as fixed point, then multiply by 64. Takes 8 bytes */ si *= (65536/64); chrs->values[i][len++] = '\377'; chrs->values[i][len++] = (si>>24)&0xff; chrs->values[i][len++] = (si>>16)&0xff; chrs->values[i][len++] = (si>>8)&0xff; chrs->values[i][len++] = si&0xff; chrs->values[i][len++] = 64 + 139; chrs->values[i][len++] = 0xc; chrs->values[i][len++] = 0x18; /* Multiply */ } /* space for the subroutine operator */ if ( gi.psubrs[ gb->bits[j].psub_index ].fd==-1 ) { chrs->values[i][len++] = 29; } else chrs->values[i][len++] = 10; } } chrs->values[i][len++] = 14; /* endchar */ chrs->values[i][len] = '\0'; } GIFree(&gi,&dummynotdef); *_glbls = glbls; return( chrs ); }