diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/parsettfatt.c')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/parsettfatt.c | 5594 |
1 files changed, 5594 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/parsettfatt.c b/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/parsettfatt.c new file mode 100644 index 00000000000..8b86752fc20 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/parsettfatt.c @@ -0,0 +1,5594 @@ +/* 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 <chardata.h> +#include <utype.h> +#include <ustring.h> +#include <math.h> +#include <locale.h> +#include <stdlib.h> +#include <ggadget.h> /* For GTextInfo */ +#include "ttf.h" + +static uint16 *getAppleClassTable(FILE *ttf, int classdef_offset, int cnt, int sub, int div, struct ttfinfo *info) { + uint16 *class = gcalloc(cnt,sizeof(uint16)); + int first, i, n; + /* Apple stores its class tables as containing offsets. I find it hard to */ + /* think that way and convert them to indeces (by subtracting off a base */ + /* offset and dividing by the item's size) before doing anything else */ + + fseek(ttf,classdef_offset,SEEK_SET); + first = getushort(ttf); + n = getushort(ttf); + if ( first+n-1>=cnt ) { + LogError( _("Bad Apple Kern Class\n") ); + info->bad_gx = true; + } + for ( i=0; i<n && i+first<cnt; ++i ) + class[first+i] = (getushort(ttf)-sub)/div; +return( class ); +} + +static char **ClassToNames(struct ttfinfo *info,int class_cnt,uint16 *class,int glyph_cnt) { + char **ret = galloc(class_cnt*sizeof(char *)); + int *lens = gcalloc(class_cnt,sizeof(int)); + int i; + + ret[0] = NULL; + for ( i=0 ; i<glyph_cnt; ++i ) if ( class[i]!=0 && info->chars[i]!=NULL && class[i]<class_cnt ) + lens[class[i]] += strlen(info->chars[i]->name)+1; + for ( i=1; i<class_cnt ; ++i ) + ret[i] = galloc(lens[i]+1); + memset(lens,0,class_cnt*sizeof(int)); + for ( i=0 ; i<glyph_cnt; ++i ) if ( class[i]!=0 && info->chars[i]!=NULL ) { + if ( class[i]<class_cnt ) { + strcpy(ret[class[i]]+lens[class[i]], info->chars[i]->name ); + lens[class[i]] += strlen(info->chars[i]->name)+1; + ret[class[i]][lens[class[i]]-1] = ' '; + } else { + LogError( _("Class index out of range %d (must be <%d)\n"),class[i], class_cnt ); + info->bad_ot = true; + } + } + for ( i=1; i<class_cnt ; ++i ) + if ( lens[i]==0 ) + ret[i][0] = '\0'; + else + ret[i][lens[i]-1] = '\0'; + free(lens); +return( ret ); +} + +static char *CoverageMinusClasses(uint16 *coverageglyphs,uint16 *classed, + struct ttfinfo *info ) { + int i, j, len; + uint8 *glyphs = gcalloc(info->glyph_cnt,1); + char *ret; + + for ( i=0; coverageglyphs[i]!=0xffff; ++i ) + glyphs[coverageglyphs[i]] = 1; + for ( i=0; i<info->glyph_cnt; ++i ) + if ( classed[i]!=0 ) + glyphs[i] = 0; + for ( i=0; i<info->glyph_cnt; ++i ) + if ( glyphs[i]!=0 ) + break; + /* coverage table matches glyphs in classes. No need for special treatment*/ + if ( i==info->glyph_cnt ) { + free(glyphs); +return( NULL ); + } + /* Otherwise we need to generate a class string of glyph names in the coverage */ + /* table but not in any class. These become the glyphs in class 0 */ + ret = NULL; + for ( j=0; j<2; ++j ) { + len = 0; + for ( i=0; i<info->glyph_cnt; ++i ) { + if ( glyphs[i]!=0 ) { + if ( j ) { + strcpy( ret+len, info->chars[i]->name ); + strcat( ret+len, " "); + } + len += strlen(info->chars[i]->name)+1; + } + } + if ( j==0 ) + ret = galloc(len+1); + else + ret[len-1] = '\0'; + } + free(glyphs); +return( ret ); +} + +static int ClassFindCnt(uint16 *class,int tot) { + int i, max=0; + + for ( i=0; i<tot; ++i ) + if ( class[i]>max ) max = class[i]; +return( max+1 ); +} + +static int cmpuint16(const void *u1, const void *u2) { +return( ((int) *((const uint16 *) u1)) - ((int) *((const uint16 *) u2)) ); +} + +static char *GlyphsToNames(struct ttfinfo *info,uint16 *glyphs,int make_uniq) { + int i, j, len, off; + char *ret, *pt; + + if ( glyphs==NULL ) +return( copy("")); + + /* Adobe produces coverage tables containing duplicate glyphs in */ + /* GaramondPremrPro.otf. We want unique glyphs, so enforce that */ + if ( make_uniq ) { + for ( i=0 ; glyphs[i]!=0xffff; ++i ); + qsort(glyphs,i,sizeof(uint16),cmpuint16); + for ( i=0; glyphs[i]!=0xffff; ++i ) { + if ( glyphs[i+1]==glyphs[i] ) { + for ( j=i+1; glyphs[j]==glyphs[i]; ++j ); + off = j-i -1; + for ( j=i+1; ; ++j ) { + glyphs[j] = glyphs[j+off]; + if ( glyphs[j]==0xffff ) + break; + } + } + } + } + + for ( i=len=0 ; glyphs[i]!=0xffff; ++i ) + if ( info->chars[glyphs[i]]!=NULL ) + len += strlen(info->chars[glyphs[i]]->name)+1; + ret = pt = galloc(len+1); *pt = '\0'; + for ( i=0 ; glyphs[i]!=0xffff; ++i ) if ( info->chars[glyphs[i]]!=NULL ) { + strcpy(pt,info->chars[glyphs[i]]->name); + pt += strlen(pt); + *pt++ = ' '; + } + if ( pt>ret ) pt[-1] = '\0'; +return( ret ); +} + +struct scripts { + uint32 offset; + uint32 tag; + int langcnt; /* the default language is included as a */ + struct language { /* normal entry with lang tag 'dflt' */ + uint32 tag; + uint32 offset; + uint16 req; /* required feature index. 0xffff for null */ + int fcnt; + uint16 *features; + } *languages; +}; + +struct feature { + uint32 offset; + uint32 tag; + int lcnt; + uint16 *lookups; +}; + +struct lookup { + uint16 type; + uint16 flags; + /* uint16 lookup; */ /* ???? can't imagine what this is*/ + uint32 offset; + int subtabcnt; + int32 *subtab_offsets; + OTLookup *otlookup; +}; + +static uint16 *getCoverageTable(FILE *ttf, int coverage_offset, struct ttfinfo *info) { + int format, cnt, i,j, rcnt; + uint16 *glyphs=NULL; + int start, end, ind, max; + + fseek(ttf,coverage_offset,SEEK_SET); + format = getushort(ttf); + if ( format==1 ) { + cnt = getushort(ttf); + glyphs = galloc((cnt+1)*sizeof(uint16)); + if ( ftell(ttf)+2*cnt > info->g_bounds ) { + LogError( _("coverage table extends beyond end of table\n") ); + info->bad_ot = true; + if ( ftell(ttf)>info->g_bounds ) +return( NULL ); + cnt = (info->g_bounds-ftell(ttf))/2; + } + for ( i=0; i<cnt; ++i ) { + if ( cnt&0xffff0000 ) { + LogError( _("Bad count.\n")); + info->bad_ot = true; + } + glyphs[i] = getushort(ttf); + if ( feof(ttf) ) { + LogError( _("End of file found in coverage table.\n") ); + info->bad_ot = true; + free(glyphs); +return( NULL ); + } + if ( glyphs[i]>=info->glyph_cnt ) { + LogError( _("Bad coverage table. Glyph %d out of range [0,%d)\n"), glyphs[i], info->glyph_cnt ); + info->bad_ot = true; + glyphs[i] = 0; + } + } + } else if ( format==2 ) { + glyphs = gcalloc((max=256),sizeof(uint16)); + rcnt = getushort(ttf); cnt = 0; + if ( ftell(ttf)+6*rcnt > info->g_bounds ) { + LogError( _("coverage table extends beyond end of table\n") ); + info->bad_ot = true; + rcnt = (info->g_bounds-ftell(ttf))/6; + } + + for ( i=0; i<rcnt; ++i ) { + start = getushort(ttf); + end = getushort(ttf); + ind = getushort(ttf); + if ( feof(ttf) ) { + LogError( _("End of file found in coverage table.\n") ); + info->bad_ot = true; + free(glyphs); +return( NULL ); + } + if ( start>end || end>=info->glyph_cnt ) { + LogError( _("Bad coverage table. Glyph range %d-%d out of range [0,%d)\n"), start, end, info->glyph_cnt ); + info->bad_ot = true; + start = end = 0; + } + if ( ind+end-start+2 >= max ) { + int oldmax = max; + max = ind+end-start+2; + glyphs = grealloc(glyphs,max*sizeof(uint16)); + memset(glyphs+oldmax,0,(max-oldmax)*sizeof(uint16)); + } + for ( j=start; j<=end; ++j ) { + glyphs[j-start+ind] = j; + if ( j>=info->glyph_cnt ) + glyphs[j-start+ind] = 0; + } + if ( ind+end-start+1>cnt ) + cnt = ind+end-start+1; + } + } else { + LogError( _("Bad format for coverage table %d\n"), format ); + info->bad_ot = true; +return( NULL ); + } + glyphs[cnt] = 0xffff; + +return( glyphs ); +} + +struct valuerecord { + int16 xplacement, yplacement; + int16 xadvance, yadvance; + uint16 offXplaceDev, offYplaceDev; + uint16 offXadvanceDev, offYadvanceDev; +}; + +static uint16 *getClassDefTable(FILE *ttf, int classdef_offset, struct ttfinfo *info) { + int format, i, j; + uint16 start, glyphcnt, rangecnt, end, class; + uint16 *glist=NULL; + int warned = false; + int cnt = info->glyph_cnt; + uint32 g_bounds = info->g_bounds; + + fseek(ttf, classdef_offset, SEEK_SET); + glist = gcalloc(cnt,sizeof(uint16)); /* Class 0 is default */ + format = getushort(ttf); + if ( format==1 ) { + start = getushort(ttf); + glyphcnt = getushort(ttf); + if ( start+(int) glyphcnt>cnt ) { + LogError( _("Bad class def table. start=%d cnt=%d, max glyph=%d\n"), start, glyphcnt, cnt ); + info->bad_ot = true; + glyphcnt = cnt-start; + } else if ( ftell(ttf)+2*glyphcnt > g_bounds ) { + LogError( _("Class definition sub-table extends beyond end of table\n") ); + info->bad_ot = true; + glyphcnt = (g_bounds-ftell(ttf))/2; + } + for ( i=0; i<glyphcnt; ++i ) + glist[start+i] = getushort(ttf); + } else if ( format==2 ) { + rangecnt = getushort(ttf); + if ( ftell(ttf)+6*rangecnt > g_bounds ) { + LogError( _("Class definition sub-table extends beyond end of table\n") ); + info->bad_ot = true; + rangecnt = (g_bounds-ftell(ttf))/6; + } + for ( i=0; i<rangecnt; ++i ) { + start = getushort(ttf); + end = getushort(ttf); + if ( start>end || end>=cnt ) { + LogError( _("Bad class def table. Glyph range %d-%d out of range [0,%d)\n"), start, end, cnt ); + info->bad_ot = true; + } + class = getushort(ttf); + for ( j=start; j<=end; ++j ) if ( j<cnt ) + glist[j] = class; + } + } else { + LogError( _("Unknown class table format: %d\n"), format ); + info->bad_ot = true; + } + + /* Do another validity test */ + for ( i=0; i<cnt; ++i ) { + if ( glist[i]>=cnt+1 ) { + if ( !warned ) { + LogError( _("Nonsensical class assigned to a glyph-- class=%d is too big. Glyph=%d\n"), + glist[i], i ); + info->bad_ot = true; + warned = true; + } + glist[i] = 0; + } + } + +return glist; +} + +static void readvaluerecord(struct valuerecord *vr,int vf,FILE *ttf) { + memset(vr,'\0',sizeof(struct valuerecord)); + if ( vf&1 ) + vr->xplacement = getushort(ttf); + if ( vf&2 ) + vr->yplacement = getushort(ttf); + if ( vf&4 ) + vr->xadvance = getushort(ttf); + if ( vf&8 ) + vr->yadvance = getushort(ttf); + if ( vf&0x10 ) + vr->offXplaceDev = getushort(ttf); + if ( vf&0x20 ) + vr->offYplaceDev = getushort(ttf); + if ( vf&0x40 ) + vr->offXadvanceDev = getushort(ttf); + if ( vf&0x80 ) + vr->offYadvanceDev = getushort(ttf); +} + +#ifdef FONTFORGE_CONFIG_DEVICETABLES +static void ReadDeviceTable(FILE *ttf,DeviceTable *adjust,uint32 devtab, + struct ttfinfo *info) { + long here; + int pack; + int w,b,i,c; + + if ( devtab==0 ) +return; + here = ftell(ttf); + fseek(ttf,devtab,SEEK_SET); + adjust->first_pixel_size = getushort(ttf); + adjust->last_pixel_size = getushort(ttf); + pack = getushort(ttf); + if ( adjust->first_pixel_size>adjust->last_pixel_size || pack==0 || pack>3 ) { + LogError(_("Bad device table\n" )); + info->bad_ot = true; + adjust->first_pixel_size = adjust->last_pixel_size = 0; + } else { + c = adjust->last_pixel_size-adjust->first_pixel_size+1; + adjust->corrections = galloc(c); + if ( pack==1 ) { + for ( i=0; i<c; i+=8 ) { + w = getushort(ttf); + for ( b=0; b<8 && i+b<c; ++b ) + adjust->corrections[i+b] = ((int16) ((w<<(b*2))&0xc000))>>14; + } + } else if ( pack==2 ) { + for ( i=0; i<c; i+=4 ) { + w = getushort(ttf); + for ( b=0; b<4 && i+b<c; ++b ) + adjust->corrections[i+b] = ((int16) ((w<<(b*4))&0xf000))>>12; + } + } else { + for ( i=0; i<c; ++i ) + adjust->corrections[i] = (int8) getc(ttf); + } + } + fseek(ttf,here,SEEK_SET); +} + +static ValDevTab *readValDevTab(FILE *ttf,struct valuerecord *vr,uint32 base, + struct ttfinfo *info) { + ValDevTab *ret; + + if ( vr->offXplaceDev==0 && vr->offYplaceDev==0 && + vr->offXadvanceDev==0 && vr->offYadvanceDev==0 ) +return( NULL ); + ret = chunkalloc(sizeof(ValDevTab)); + if ( vr->offXplaceDev!=0 ) + ReadDeviceTable(ttf,&ret->xadjust,base + vr->offXplaceDev,info); + if ( vr->offYplaceDev!=0 ) + ReadDeviceTable(ttf,&ret->yadjust,base + vr->offYplaceDev,info); + if ( vr->offXadvanceDev!=0 ) + ReadDeviceTable(ttf,&ret->xadv,base + vr->offXadvanceDev,info); + if ( vr->offYadvanceDev!=0 ) + ReadDeviceTable(ttf,&ret->yadv,base + vr->offYadvanceDev,info); +return( ret ); +} +#endif + +static void addPairPos(struct ttfinfo *info, int glyph1, int glyph2, + struct lookup *l, struct lookup_subtable *subtable, struct valuerecord *vr1,struct valuerecord *vr2, + uint32 base,FILE *ttf) { + (void)ttf; /* for -Wall */ + (void)l; /* for -Wall */ + (void)base; /* for -Wall */ + if ( glyph1<info->glyph_cnt && glyph2<info->glyph_cnt ) { + PST *pos = chunkalloc(sizeof(PST)); + pos->type = pst_pair; + pos->subtable = subtable; + pos->next = info->chars[glyph1]->possub; + info->chars[glyph1]->possub = pos; + pos->u.pair.vr = chunkalloc(sizeof(struct vr [2])); + pos->u.pair.paired = copy(info->chars[glyph2]->name); + pos->u.pair.vr[0].xoff = vr1->xplacement; + pos->u.pair.vr[0].yoff = vr1->yplacement; + pos->u.pair.vr[0].h_adv_off = vr1->xadvance; + pos->u.pair.vr[0].v_adv_off = vr1->yadvance; + pos->u.pair.vr[1].xoff = vr2->xplacement; + pos->u.pair.vr[1].yoff = vr2->yplacement; + pos->u.pair.vr[1].h_adv_off = vr2->xadvance; + pos->u.pair.vr[1].v_adv_off = vr2->yadvance; +#ifdef FONTFORGE_CONFIG_DEVICETABLES + pos->u.pair.vr[0].adjust = readValDevTab(ttf,vr1,base,info); + pos->u.pair.vr[1].adjust = readValDevTab(ttf,vr2,base,info); +#endif + } else { + LogError( _("Bad pair position: glyphs %d & %d should have been < %d\n"), + glyph1, glyph2, info->glyph_cnt ); + info->bad_ot = true; + } +} + +static int addKernPair(struct ttfinfo *info, int glyph1, int glyph2, + int16 offset, uint32 devtab, struct lookup *l, struct lookup_subtable *subtable,int isv, + FILE *ttf) { + KernPair *kp; + (void)ttf; /* for -Wall */ + (void)l; /* for -Wall */ + (void)devtab; /* for -Wall */ + if ( glyph1<info->glyph_cnt && glyph2<info->glyph_cnt && + info->chars[glyph1]!=NULL && info->chars[glyph2]!=NULL ) { + for ( kp=isv ? info->chars[glyph1]->vkerns : info->chars[glyph1]->kerns; + kp!=NULL; kp=kp->next ) { + if ( kp->sc == info->chars[glyph2] ) + break; + } + if ( kp==NULL ) { + kp = chunkalloc(sizeof(KernPair)); + kp->sc = info->chars[glyph2]; + kp->off = offset; + kp->subtable = subtable; +#ifdef FONTFORGE_CONFIG_DEVICETABLES + if ( devtab!=0 ) { + kp->adjust = chunkalloc(sizeof(DeviceTable)); + ReadDeviceTable(ttf,kp->adjust,devtab,info); + } +#endif + if ( isv ) { + kp->next = info->chars[glyph1]->vkerns; + info->chars[glyph1]->vkerns = kp; + } else { + kp->next = info->chars[glyph1]->kerns; + info->chars[glyph1]->kerns = kp; + } + } else if ( kp->subtable!=subtable ) +return( true ); + } else if ( glyph1>=info->glyph_cnt || glyph2>=info->glyph_cnt ) { + /* Might be NULL in a ttc file where we omit glyphs */ + LogError( _("Bad kern pair: glyphs %d & %d should have been < %d\n"), + glyph1, glyph2, info->glyph_cnt ); + info->bad_ot = true; + } +return( false ); +} + +static void gposKernSubTable(FILE *ttf, int stoffset, struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable) { + int coverage, cnt, i, j, pair_cnt, vf1, vf2, glyph2; + int cd1, cd2, c1_cnt, c2_cnt; + uint16 format; + uint16 *ps_offsets; + uint16 *glyphs, *class1, *class2; + struct valuerecord vr1, vr2; + long foffset; + KernClass *kc; + int isv, r2l; + + format=getushort(ttf); + if ( format!=1 && format!=2 ) /* Unknown subtable format */ +return; + coverage = getushort(ttf); + vf1 = getushort(ttf); + vf2 = getushort(ttf); + r2l = 0; + + /* Accept forms both with and without device tables */ + if ( (vf1==0x0008 || vf1==0x0088) && vf2==0x0000 ) + isv = 1; /* Top to bottom */ + else if ( vf1==0x0000 && (vf2==0x0004 || vf2==0x0044) && (l->flags&pst_r2l)) { + isv = 0; /* Right to left */ + r2l = 1; + } else if ( (vf1==0x0004 || vf1==0x0044) && vf2==0x0000 && !(l->flags&pst_r2l) ) + isv = 0; /* Left to right */ + else + isv = 2; /* Can't optimize, store all 8 settings */ + if ( format==1 ) { + subtable->per_glyph_pst_or_kern = true; + cnt = getushort(ttf); + ps_offsets = galloc(cnt*sizeof(uint16)); + for ( i=0; i<cnt; ++i ) + ps_offsets[i]=getushort(ttf); + glyphs = getCoverageTable(ttf,stoffset+coverage,info); + if ( glyphs==NULL ) +return; + for ( i=0; i<cnt; ++i ) if ( glyphs[i]<info->glyph_cnt ) { + fseek(ttf,stoffset+ps_offsets[i],SEEK_SET); + pair_cnt = getushort(ttf); + for ( j=0; j<pair_cnt; ++j ) { + glyph2 = getushort(ttf); + readvaluerecord(&vr1,vf1,ttf); + readvaluerecord(&vr2,vf2,ttf); + if ( isv==2 ) + addPairPos(info, glyphs[i], glyph2,l,subtable,&vr1,&vr2, stoffset,ttf); + else if ( isv ) { + if ( addKernPair(info, glyphs[i], glyph2, vr1.yadvance, + vr1.offYadvanceDev==0?0:stoffset+vr1.offYadvanceDev, + l,subtable,isv,ttf)) + addPairPos(info, glyphs[i], glyph2,l,subtable,&vr1,&vr2, stoffset,ttf); + /* If we've already got kern data for this pair of */ + /* glyphs, then we can't make it be a true KernPair */ + /* but we can save the info as a pst_pair */ + } else if ( r2l ) { /* R2L */ + if ( addKernPair(info, glyphs[i], glyph2, vr2.xadvance, + vr2.offXadvanceDev==0?0:stoffset+vr2.offXadvanceDev, + l,subtable,isv,ttf)) + addPairPos(info, glyphs[i], glyph2,l,subtable,&vr1,&vr2,stoffset,ttf); + } else { + if ( addKernPair(info, glyphs[i], glyph2, vr1.xadvance, + vr1.offXadvanceDev==0?0:stoffset+vr1.offXadvanceDev, + l,subtable,isv,ttf)) + addPairPos(info, glyphs[i], glyph2,l,subtable,&vr1,&vr2,stoffset,ttf); + } + } + } + free(ps_offsets); free(glyphs); + } else if ( format==2 ) { /* Class-based kerning */ + cd1 = getushort(ttf); + cd2 = getushort(ttf); + foffset = ftell(ttf); + class1 = getClassDefTable(ttf, stoffset+cd1, info); + class2 = getClassDefTable(ttf, stoffset+cd2, info); + glyphs = getCoverageTable(ttf,stoffset+coverage,info); + fseek(ttf, foffset, SEEK_SET); /* come back */ + c1_cnt = getushort(ttf); + c2_cnt = getushort(ttf); + if ( isv!=2 ) { + if ( isv ) { + if ( info->vkhead==NULL ) + info->vkhead = kc = chunkalloc(sizeof(KernClass)); + else + kc = info->vklast->next = chunkalloc(sizeof(KernClass)); + info->vklast = kc; + } else { + if ( info->khead==NULL ) + info->khead = kc = chunkalloc(sizeof(KernClass)); + else + kc = info->klast->next = chunkalloc(sizeof(KernClass)); + info->klast = kc; + } + subtable->vertical_kerning = isv; + subtable->kc = kc; + kc->first_cnt = c1_cnt; kc->second_cnt = c2_cnt; + kc->subtable = subtable; + kc->offsets = galloc(c1_cnt*c2_cnt*sizeof(int16)); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + kc->adjusts = gcalloc(c1_cnt*c2_cnt,sizeof(DeviceTable)); +#endif + kc->firsts = ClassToNames(info,c1_cnt,class1,info->glyph_cnt); + kc->seconds = ClassToNames(info,c2_cnt,class2,info->glyph_cnt); + /* Now if the coverage table contains entries which are not in */ + /* the list of first classes, then those glyphs are the real */ + /* values for kc->firsts[0] */ + kc->firsts[0] = CoverageMinusClasses(glyphs,class1,info); + for ( i=0; i<c1_cnt; ++i) { + for ( j=0; j<c2_cnt; ++j) { + readvaluerecord(&vr1,vf1,ttf); + readvaluerecord(&vr2,vf2,ttf); + if ( isv ) + kc->offsets[i*c2_cnt+j] = vr1.yadvance; + else if ( r2l ) + kc->offsets[i*c2_cnt+j] = vr2.xadvance; + else + kc->offsets[i*c2_cnt+j] = vr1.xadvance; +#ifdef FONTFORGE_CONFIG_DEVICETABLES + if ( isv ) { + if ( vr1.offYadvanceDev!=0 ) + ReadDeviceTable(ttf,&kc->adjusts[i*c2_cnt+j],stoffset+vr1.offYadvanceDev,info); + } else if ( r2l ) { + if ( vr2.offXadvanceDev!=0 ) + ReadDeviceTable(ttf,&kc->adjusts[i*c2_cnt+j],stoffset+vr2.offXadvanceDev,info); + } else { + if ( vr1.offXadvanceDev!=0 ) + ReadDeviceTable(ttf,&kc->adjusts[i*c2_cnt+j],stoffset+vr1.offXadvanceDev,info); + } +#endif + } + } + } else { /* This happens when we have a feature which is neither 'kern' nor 'vkrn' we don't know what to do with it so we make it into kern pairs */ + int k,m; + subtable->per_glyph_pst_or_kern = true; + for ( i=0; i<c1_cnt; ++i) { + for ( j=0; j<c2_cnt; ++j) { + readvaluerecord(&vr1,vf1,ttf); + readvaluerecord(&vr2,vf2,ttf); + if ( vr1.xadvance!=0 || vr1.xplacement!=0 || vr1.yadvance!=0 || vr1.yplacement!=0 || + vr2.xadvance!=0 || vr2.xplacement!=0 || vr2.yadvance!=0 || vr2.yplacement!=0 ) + for ( k=0; k<info->glyph_cnt; ++k ) + if ( class1[k]==i ) + for ( m=0; m<info->glyph_cnt; ++m ) + if ( class2[m]==j ) + addPairPos(info, k,m,l,subtable,&vr1,&vr2,stoffset,ttf); + } + } + } + free(class1); free(class2); + free(glyphs); + } +} + +static AnchorPoint *readAnchorPoint(FILE *ttf,uint32 base,AnchorClass *class, + enum anchor_type type,AnchorPoint *last, struct ttfinfo *info) { + AnchorPoint *ap; + int format; + (void)info; /* for -Wall */ + fseek(ttf,base,SEEK_SET); + + ap = chunkalloc(sizeof(AnchorPoint)); + ap->anchor = class; + /* All anchor types have the same initial 3 entries, format */ + /* x,y pos. format 2 contains a truetype positioning point, and */ + /* format==3 may also have device tables */ + format = getushort(ttf); + ap->me.x = (int16) getushort(ttf); + ap->me.y = (int16) getushort(ttf); + ap->type = type; + if ( format==2 ) { + ap->ttf_pt_index = getushort(ttf); + ap->has_ttf_pt = true; + } +#ifdef FONTFORGE_CONFIG_DEVICETABLES + else if ( format==3 ) { + int devoff; + devoff = getushort(ttf); + if ( devoff!=0 ) + ReadDeviceTable(ttf,&ap->xadjust,base+devoff,info); + devoff = getushort(ttf); + if ( devoff!=0 ) + ReadDeviceTable(ttf,&ap->yadjust,base+devoff,info); + } +#endif + ap->next = last; +return( ap ); +} + +static void gposCursiveSubTable(FILE *ttf, int stoffset, struct ttfinfo *info,struct lookup *l, struct lookup_subtable *subtable) { + int coverage, cnt, format, i; + struct ee_offsets { int entry, exit; } *offsets; + uint16 *glyphs; + AnchorClass *class; + SplineChar *sc; + char buf[50]; + (void)l; /* for -Wall */ + format=getushort(ttf); + if ( format!=1 ) /* Unknown subtable format */ +return; + coverage = getushort(ttf); + cnt = getushort(ttf); + if ( cnt==0 ) +return; + offsets = galloc(cnt*sizeof(struct ee_offsets)); + for ( i=0; i<cnt; ++i ) { + offsets[i].entry = getushort(ttf); + offsets[i].exit = getushort(ttf); + } + glyphs = getCoverageTable(ttf,stoffset+coverage,info); + + class = chunkalloc(sizeof(AnchorClass)); + snprintf(buf,sizeof(buf),_("Cursive-%d"), + info->anchor_class_cnt++ ); + class->name = copy(buf); + subtable->anchor_classes = true; + class->subtable = subtable; + class->type = act_curs; + if ( info->ahead==NULL ) + info->ahead = class; + else + info->alast->next = class; + info->alast = class; + + for ( i=0; i<cnt; ++i ) { + sc = info->chars[glyphs[i]]; + if ( offsets[i].entry!=0 ) { + sc->anchor = readAnchorPoint(ttf,stoffset+offsets[i].entry,class, + at_centry,sc->anchor,info); + } + if ( offsets[i].exit!=0 ) { + sc->anchor = readAnchorPoint(ttf,stoffset+offsets[i].exit,class, + at_cexit,sc->anchor,info); + } + } + free(offsets); + free(glyphs); +} + +static AnchorClass **MarkGlyphsProcessMarks(FILE *ttf,int markoffset, + struct ttfinfo *info,struct lookup *l, struct lookup_subtable *subtable,uint16 *markglyphs, + int classcnt) { + AnchorClass **classes = gcalloc(classcnt,sizeof(AnchorClass *)), *ac; + char buf[50]; + int i, cnt; + struct mr { uint16 class, offset; } *at_offsets; + SplineChar *sc; + + for ( i=0; i<classcnt; ++i ) { + snprintf(buf,sizeof(buf),_("Anchor-%d"), + info->anchor_class_cnt+i ); + classes[i] = ac = chunkalloc(sizeof(AnchorClass)); + ac->name = copy(buf); + subtable->anchor_classes = true; + ac->subtable = subtable; + /*ac->merge_with = info->anchor_merge_cnt+1;*/ + ac->type = l->otlookup->lookup_type==gpos_mark2mark ? act_mkmk : act_mark; + /* I don't distinguish between mark to base and mark to lig */ + if ( info->ahead==NULL ) + info->ahead = ac; + else + info->alast->next = ac; + info->alast = ac; + } + + fseek(ttf,markoffset,SEEK_SET); + cnt = getushort(ttf); + if ( feof(ttf) ) { + LogError( _("Bad mark table.\n") ); + info->bad_ot = true; +return( NULL ); + } + at_offsets = galloc(cnt*sizeof(struct mr)); + for ( i=0; i<cnt; ++i ) { + at_offsets[i].class = getushort(ttf); + at_offsets[i].offset = getushort(ttf); + if ( at_offsets[i].class>=classcnt ) { + at_offsets[i].class = 0; + if ( markglyphs[i]>=info->glyph_cnt ) + LogError( _("Class out of bounds in GPOS mark sub-table\n") ); + else + LogError( _("Class out of bounds in GPOS mark sub-table for mark %.30s\n"), info->chars[markglyphs[i]]->name); + info->bad_ot = true; + } + } + for ( i=0; i<cnt; ++i ) { + if ( markglyphs[i]>=info->glyph_cnt ) + continue; + sc = info->chars[markglyphs[i]]; + if ( sc==NULL || at_offsets[i].offset==0 ) + continue; + sc->anchor = readAnchorPoint(ttf,markoffset+at_offsets[i].offset, + classes[at_offsets[i].class],at_mark,sc->anchor,info); + } + free(at_offsets); +return( classes ); +} + +static void MarkGlyphsProcessBases(FILE *ttf,int baseoffset, + struct ttfinfo *info,struct lookup *l, struct lookup_subtable *subtable,uint16 *baseglyphs,int classcnt, + AnchorClass **classes,enum anchor_type at) { + int basecnt,i, j, ibase; + uint16 *offsets; + SplineChar *sc; + (void)subtable; /* for -Wall */ + (void)l; /* for -Wall */ + fseek(ttf,baseoffset,SEEK_SET); + basecnt = getushort(ttf); + if ( feof(ttf) ) { + LogError( _("Bad base table.\n") ); + info->bad_ot = true; +return; + } + offsets = galloc(basecnt*classcnt*sizeof(uint16)); + for ( i=0; i<basecnt*classcnt; ++i ) + offsets[i] = getushort(ttf); + for ( i=ibase=0; i<basecnt; ++i, ibase+= classcnt ) { + if ( baseglyphs[i]>=info->glyph_cnt ) + continue; + sc = info->chars[baseglyphs[i]]; + if ( sc==NULL ) + continue; + for ( j=0; j<classcnt; ++j ) if ( offsets[ibase+j]!=0 ) { + sc->anchor = readAnchorPoint(ttf,baseoffset+offsets[ibase+j], + classes[j], at,sc->anchor,info); + } + } + free(offsets); +} + +static void MarkGlyphsProcessLigs(FILE *ttf,int baseoffset, + struct ttfinfo *info,struct lookup *l, struct lookup_subtable *subtable,uint16 *baseglyphs,int classcnt, + AnchorClass **classes) { + int basecnt,compcnt, i, j, k, kbase; + uint16 *loffsets, *aoffsets; + SplineChar *sc; + (void)subtable; /* for -Wall */ + (void)l; /* for -Wall */ + fseek(ttf,baseoffset,SEEK_SET); + basecnt = getushort(ttf); + if ( feof(ttf) ) { + LogError( _("Bad ligature base table.\n") ); + info->bad_ot = true; +return; + } + loffsets = galloc(basecnt*sizeof(uint16)); + for ( i=0; i<basecnt; ++i ) + loffsets[i] = getushort(ttf); + for ( i=0; i<basecnt; ++i ) { + sc = info->chars[baseglyphs[i]]; + if ( baseglyphs[i]>=info->glyph_cnt || sc==NULL ) + continue; + fseek(ttf,baseoffset+loffsets[i],SEEK_SET); + compcnt = getushort(ttf); + if ( feof(ttf)) { + LogError(_("Bad ligature anchor count.\n")); + info->bad_ot = true; + continue; + } + aoffsets = galloc(compcnt*classcnt*sizeof(uint16)); + for ( k=0; k<compcnt*classcnt; ++k ) + aoffsets[k] = getushort(ttf); + for ( k=kbase=0; k<compcnt; ++k, kbase+=classcnt ) { + for ( j=0; j<classcnt; ++j ) if ( aoffsets[kbase+j]!=0 ) { + sc->anchor = readAnchorPoint(ttf,baseoffset+loffsets[i]+aoffsets[kbase+j], + classes[j], at_baselig,sc->anchor,info); + sc->anchor->lig_index = k; + } + } + free(aoffsets); + } + free(loffsets); +} + +static void gposMarkSubTable(FILE *ttf, uint32 stoffset, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable) { + int markcoverage, basecoverage, classcnt, markoffset, baseoffset; + uint16 *markglyphs, *baseglyphs; + AnchorClass **classes; + + /* The header for the three different mark tables is the same */ + /* Type = */ getushort(ttf); + markcoverage = getushort(ttf); + basecoverage = getushort(ttf); + classcnt = getushort(ttf); + markoffset = getushort(ttf); + baseoffset = getushort(ttf); + markglyphs = getCoverageTable(ttf,stoffset+markcoverage,info); + baseglyphs = getCoverageTable(ttf,stoffset+basecoverage,info); + if ( baseglyphs==NULL || markglyphs==NULL ) { + free(baseglyphs); free(markglyphs); +return; + } + /* as is the (first) mark table */ + classes = MarkGlyphsProcessMarks(ttf,stoffset+markoffset, + info,l,subtable,markglyphs,classcnt); + if ( classes==NULL ) +return; + switch ( l->otlookup->lookup_type ) { + case gpos_mark2base: + case gpos_mark2mark: + MarkGlyphsProcessBases(ttf,stoffset+baseoffset, + info,l,subtable,baseglyphs,classcnt,classes, + l->otlookup->lookup_type==gpos_mark2base?at_basechar:at_basemark); + break; + case gpos_mark2ligature: + MarkGlyphsProcessLigs(ttf,stoffset+baseoffset, + info,l,subtable,baseglyphs,classcnt,classes); + break; + default: + break; + } + info->anchor_class_cnt += classcnt; + ++ info->anchor_merge_cnt; + free(markglyphs); free(baseglyphs); + free(classes); +} + +static void gposSimplePos(FILE *ttf, int stoffset, struct ttfinfo *info, + struct lookup *l, struct lookup_subtable *subtable) { + int coverage, cnt, i, vf; + uint16 format; + uint16 *glyphs; + struct valuerecord *vr=NULL, _vr, *which; + (void)l; /* for -Wall */ + format=getushort(ttf); + if ( format!=1 && format!=2 ) /* Unknown subtable format */ +return; + coverage = getushort(ttf); + vf = getushort(ttf); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + if ( vf==0 ) +return; +#else + if ( (vf&0xf)==0 ) /* Not interested in things whose data just live in device tables */ +return; +#endif + if ( format==1 ) { + memset(&_vr,0,sizeof(_vr)); + readvaluerecord(&_vr,vf,ttf); + } else { + cnt = getushort(ttf); + vr = gcalloc(cnt,sizeof(struct valuerecord)); + for ( i=0; i<cnt; ++i ) + readvaluerecord(&vr[i],vf,ttf); + } + glyphs = getCoverageTable(ttf,stoffset+coverage,info); + if ( glyphs==NULL ) { + free(vr); +return; + } + for ( i=0; glyphs[i]!=0xffff; ++i ) if ( glyphs[i]<info->glyph_cnt ) { + PST *pos = chunkalloc(sizeof(PST)); + pos->type = pst_position; + pos->subtable = subtable; + pos->next = info->chars[glyphs[i]]->possub; + info->chars[glyphs[i]]->possub = pos; + which = format==1 ? &_vr : &vr[i]; + pos->u.pos.xoff = which->xplacement; + pos->u.pos.yoff = which->yplacement; + pos->u.pos.h_adv_off = which->xadvance; + pos->u.pos.v_adv_off = which->yadvance; +#ifdef FONTFORGE_CONFIG_DEVICETABLES + pos->u.pos.adjust = readValDevTab(ttf,which,stoffset,info); +#endif + } + subtable->per_glyph_pst_or_kern = true; + free(vr); + free(glyphs); +} + +static void ProcessSubLookups(FILE *ttf,struct ttfinfo *info,int gpos, + struct lookup *alllooks,struct seqlookup *sl) { + int i; + (void)ttf; /* for -Wall */ + i = (intpt) sl->lookup; + if ( i<0 || i>=info->lookup_cnt ) { + LogError( _("Attempt to reference lookup %d (within a contextual lookup), but there are\n only %d lookups in %s\n"), + i, info->lookup_cnt, gpos ? "'GPOS'" : "'GSUB'" ); + info->bad_ot = true; + sl->lookup = NULL; +return; + } + sl->lookup = alllooks[i].otlookup; +} + +static void g___ContextSubTable1(FILE *ttf, int stoffset, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, int justinuse, + struct lookup *alllooks, int gpos) { + int i, j, k, rcnt, cnt; + uint16 coverage; + uint16 *glyphs; + struct subrule { + uint32 offset; + int gcnt; + int scnt; + uint16 *glyphs; + struct seqlookup *sl; + }; + struct rule { + uint32 offsets; + int scnt; + struct subrule *subrules; + } *rules; + FPST *fpst; + struct fpst_rule *rule; + int warned = false, warned2 = false; + (void)l; /* for -Wall */ + coverage = getushort(ttf); + rcnt = getushort(ttf); /* glyph count in coverage table */ + rules = galloc(rcnt*sizeof(struct rule)); + for ( i=0; i<rcnt; ++i ) + rules[i].offsets = getushort(ttf)+stoffset; + glyphs = getCoverageTable(ttf,stoffset+coverage,info); + cnt = 0; + for ( i=0; i<rcnt; ++i ) { + fseek(ttf,rules[i].offsets,SEEK_SET); + rules[i].scnt = getushort(ttf); + cnt += rules[i].scnt; + rules[i].subrules = galloc(rules[i].scnt*sizeof(struct subrule)); + for ( j=0; j<rules[i].scnt; ++j ) + rules[i].subrules[j].offset = getushort(ttf)+rules[i].offsets; + for ( j=0; j<rules[i].scnt; ++j ) { + fseek(ttf,rules[i].subrules[j].offset,SEEK_SET); + rules[i].subrules[j].gcnt = getushort(ttf); + rules[i].subrules[j].scnt = getushort(ttf); + rules[i].subrules[j].glyphs = galloc((rules[i].subrules[j].gcnt+1)*sizeof(uint16)); + rules[i].subrules[j].glyphs[0] = glyphs[i]; + for ( k=1; k<rules[i].subrules[j].gcnt; ++k ) { + rules[i].subrules[j].glyphs[k] = getushort(ttf); + if ( rules[i].subrules[j].glyphs[k]>=info->glyph_cnt ) { + if ( !warned ) + LogError( _("Bad contextual or chaining sub table. Glyph %d out of range [0,%d)\n"), + rules[i].subrules[j].glyphs[k], info->glyph_cnt ); + info->bad_ot = true; + warned = true; + rules[i].subrules[j].glyphs[k] = 0; + } + } + rules[i].subrules[j].glyphs[k] = 0xffff; + rules[i].subrules[j].sl = galloc(rules[i].subrules[j].scnt*sizeof(struct seqlookup)); + for ( k=0; k<rules[i].subrules[j].scnt; ++k ) { + rules[i].subrules[j].sl[k].seq = getushort(ttf); + if ( rules[i].subrules[j].sl[k].seq >= rules[i].subrules[j].gcnt+1 ) + if ( !warned2 ) { + LogError( _("Attempt to apply a lookup to a location out of the range of this contextual\n lookup seq=%d max=%d\n"), + rules[i].subrules[j].sl[k].seq, rules[i].subrules[j].gcnt ); + info->bad_ot = true; + warned2 = true; + } + rules[i].subrules[j].sl[k].lookup = (void *) (intpt) getushort(ttf); + } + } + } + + if ( justinuse==git_justinuse ) { + /* Nothing to do. This lookup doesn't really reference any glyphs */ + /* any lookups it invokes will be processed on their own */ + } else { + fpst = chunkalloc(sizeof(FPST)); + fpst->type = gpos ? pst_contextpos : pst_contextsub; + fpst->format = pst_glyphs; + fpst->subtable = subtable; + fpst->next = info->possub; + info->possub = fpst; + subtable->fpst = fpst; + + fpst->rules = rule = gcalloc(cnt,sizeof(struct fpst_rule)); + fpst->rule_cnt = cnt; + + cnt = 0; + for ( i=0; i<rcnt; ++i ) for ( j=0; j<rules[i].scnt; ++j ) { + rule[cnt].u.glyph.names = GlyphsToNames(info,rules[i].subrules[j].glyphs,false); + rule[cnt].lookup_cnt = rules[i].subrules[j].scnt; + rule[cnt].lookups = rules[i].subrules[j].sl; + rules[i].subrules[j].sl = NULL; + for ( k=0; k<rule[cnt].lookup_cnt; ++k ) + ProcessSubLookups(ttf,info,gpos,alllooks,&rule[cnt].lookups[k]); + ++cnt; + } + } + + for ( i=0; i<rcnt; ++i ) { + for ( j=0; j<rules[i].scnt; ++j ) { + free(rules[i].subrules[j].glyphs); + free(rules[i].subrules[j].sl); + } + free(rules[i].subrules); + } + free(rules); + free(glyphs); +} + +static void g___ChainingSubTable1(FILE *ttf, int stoffset, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, int justinuse, + struct lookup *alllooks, int gpos) { + int i, j, k, rcnt, cnt, which; + uint16 coverage; + uint16 *glyphs; + struct subrule { + uint32 offset; + int gcnt, bcnt, fcnt; + int scnt; + uint16 *glyphs, *bglyphs, *fglyphs; + struct seqlookup *sl; + }; + struct rule { + uint32 offsets; + int scnt; + struct subrule *subrules; + } *rules; + FPST *fpst; + struct fpst_rule *rule; + int warned = false, warned2 = false; + (void)l; /* for -Wall */ + coverage = getushort(ttf); + rcnt = getushort(ttf); /* glyph count in coverage table */ + rules = galloc(rcnt*sizeof(struct rule)); + for ( i=0; i<rcnt; ++i ) + rules[i].offsets = getushort(ttf)+stoffset; + glyphs = getCoverageTable(ttf,stoffset+coverage,info); + if ( glyphs==NULL ) { + free(rules); +return; + } + cnt = 0; + for ( i=0; i<rcnt; ++i ) { + fseek(ttf,rules[i].offsets,SEEK_SET); + rules[i].scnt = getushort(ttf); + cnt += rules[i].scnt; + rules[i].subrules = galloc(rules[i].scnt*sizeof(struct subrule)); + for ( j=0; j<rules[i].scnt; ++j ) + rules[i].subrules[j].offset = getushort(ttf)+rules[i].offsets; + for ( j=0; j<rules[i].scnt; ++j ) { + fseek(ttf,rules[i].subrules[j].offset,SEEK_SET); + rules[i].subrules[j].bcnt = getushort(ttf); + if ( feof(ttf)) { + LogError( _("Unexpected end of file in contextual chaining subtable.\n") ); + info->bad_ot = true; +return; + } + rules[i].subrules[j].bglyphs = galloc((rules[i].subrules[j].bcnt+1)*sizeof(uint16)); + for ( k=0; k<rules[i].subrules[j].bcnt; ++k ) + rules[i].subrules[j].bglyphs[k] = getushort(ttf); + rules[i].subrules[j].bglyphs[k] = 0xffff; + + rules[i].subrules[j].gcnt = getushort(ttf); + if ( feof(ttf)) { + LogError( _("Unexpected end of file in contextual chaining subtable.\n") ); + info->bad_ot = true; +return; + } + rules[i].subrules[j].glyphs = galloc((rules[i].subrules[j].gcnt+1)*sizeof(uint16)); + rules[i].subrules[j].glyphs[0] = glyphs[i]; + for ( k=1; k<rules[i].subrules[j].gcnt; ++k ) + rules[i].subrules[j].glyphs[k] = getushort(ttf); + rules[i].subrules[j].glyphs[k] = 0xffff; + + rules[i].subrules[j].fcnt = getushort(ttf); + if ( feof(ttf)) { + LogError( _("Unexpected end of file in contextual chaining subtable.\n") ); + info->bad_ot = true; +return; + } + rules[i].subrules[j].fglyphs = galloc((rules[i].subrules[j].fcnt+1)*sizeof(uint16)); + for ( k=0; k<rules[i].subrules[j].fcnt; ++k ) + rules[i].subrules[j].fglyphs[k] = getushort(ttf); + rules[i].subrules[j].fglyphs[k] = 0xffff; + + for ( which = 0; which<3; ++which ) { + for ( k=0; k<(&rules[i].subrules[j].gcnt)[which]; ++k ) { + if ( (&rules[i].subrules[j].glyphs)[which][k]>=info->glyph_cnt ) { + if ( !warned ) + LogError( _("Bad contextual or chaining sub table. Glyph %d out of range [0,%d)\n"), + (&rules[i].subrules[j].glyphs)[which][k], info->glyph_cnt ); + info->bad_ot = true; + warned = true; + (&rules[i].subrules[j].glyphs)[which][k] = 0; + } + } + } + + rules[i].subrules[j].scnt = getushort(ttf); + if ( feof(ttf)) { + LogError( _("Unexpected end of file in contextual chaining subtable.\n") ); + info->bad_ot = true; +return; + } + rules[i].subrules[j].sl = galloc(rules[i].subrules[j].scnt*sizeof(struct seqlookup)); + for ( k=0; k<rules[i].subrules[j].scnt; ++k ) { + rules[i].subrules[j].sl[k].seq = getushort(ttf); + if ( rules[i].subrules[j].sl[k].seq >= rules[i].subrules[j].gcnt+1 ) + if ( !warned2 ) { + LogError( _("Attempt to apply a lookup to a location out of the range of this contextual\n lookup seq=%d max=%d\n"), + rules[i].subrules[j].sl[k].seq, rules[i].subrules[j].gcnt ); + info->bad_ot = true; + warned2 = true; + } + rules[i].subrules[j].sl[k].lookup = (void *) (intpt) getushort(ttf); + } + } + } + + if ( justinuse==git_justinuse ) { + /* Nothing to do. This lookup doesn't really reference any glyphs */ + /* any lookups it invokes will be processed on their own */ + } else { + fpst = chunkalloc(sizeof(FPST)); + fpst->type = gpos ? pst_chainpos : pst_chainsub; + fpst->format = pst_glyphs; + fpst->subtable = subtable; + fpst->next = info->possub; + info->possub = fpst; + subtable->fpst = fpst; + + fpst->rules = rule = gcalloc(cnt,sizeof(struct fpst_rule)); + fpst->rule_cnt = cnt; + + cnt = 0; + for ( i=0; i<rcnt; ++i ) for ( j=0; j<rules[i].scnt; ++j ) { + rule[cnt].u.glyph.back = GlyphsToNames(info,rules[i].subrules[j].bglyphs,false); + rule[cnt].u.glyph.names = GlyphsToNames(info,rules[i].subrules[j].glyphs,false); + rule[cnt].u.glyph.fore = GlyphsToNames(info,rules[i].subrules[j].fglyphs,false); + rule[cnt].lookup_cnt = rules[i].subrules[j].scnt; + rule[cnt].lookups = rules[i].subrules[j].sl; + rules[i].subrules[j].sl = NULL; + for ( k=0; k<rule[cnt].lookup_cnt; ++k ) + ProcessSubLookups(ttf,info,gpos,alllooks,&rule[cnt].lookups[k]); + ++cnt; + } + } + + for ( i=0; i<rcnt; ++i ) { + for ( j=0; j<rules[i].scnt; ++j ) { + free(rules[i].subrules[j].bglyphs); + free(rules[i].subrules[j].glyphs); + free(rules[i].subrules[j].fglyphs); + free(rules[i].subrules[j].sl); + } + free(rules[i].subrules); + } + free(rules); + free(glyphs); +} + +static void g___ContextSubTable2(FILE *ttf, int stoffset, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, int justinuse, + struct lookup *alllooks, int gpos) { + int i, j, k, rcnt, cnt; + uint16 coverage; + uint16 classoff; + struct subrule { + uint32 offset; + int ccnt; + int scnt; + uint16 *classindeces; + struct seqlookup *sl; + }; + struct rule { + uint32 offsets; + int scnt; + struct subrule *subrules; + } *rules; + FPST *fpst; + struct fpst_rule *rule; + uint16 *glyphs, *class; + int warned2 = false; + (void)l; /* for -Wall */ + coverage = getushort(ttf); + classoff = getushort(ttf); + rcnt = getushort(ttf); /* class count in coverage table *//* == number of top level rules */ + rules = gcalloc(rcnt,sizeof(struct rule)); + for ( i=0; i<rcnt; ++i ) + rules[i].offsets = getushort(ttf)+stoffset; + cnt = 0; + for ( i=0; i<rcnt; ++i ) if ( rules[i].offsets!=(unsigned)stoffset ) { /* some classes might be unused */ + fseek(ttf,rules[i].offsets,SEEK_SET); + rules[i].scnt = getushort(ttf); + if ( rules[i].scnt<0 ) { + LogError( _("Bad count in context chaining sub-table.\n") ); + info->bad_ot = true; +return; + } + cnt += rules[i].scnt; + rules[i].subrules = galloc(rules[i].scnt*sizeof(struct subrule)); + for ( j=0; j<rules[i].scnt; ++j ) + rules[i].subrules[j].offset = getushort(ttf)+rules[i].offsets; + for ( j=0; j<rules[i].scnt; ++j ) { + fseek(ttf,rules[i].subrules[j].offset,SEEK_SET); + rules[i].subrules[j].ccnt = getushort(ttf); + rules[i].subrules[j].scnt = getushort(ttf); + if ( rules[i].subrules[j].ccnt<0 ) { + LogError( _("Bad class count in contextual chaining sub-table.\n") ); + info->bad_ot = true; + free(rules); +return; + } + rules[i].subrules[j].classindeces = galloc(rules[i].subrules[j].ccnt*sizeof(uint16)); + rules[i].subrules[j].classindeces[0] = i; + for ( k=1; k<rules[i].subrules[j].ccnt; ++k ) + rules[i].subrules[j].classindeces[k] = getushort(ttf); + if ( rules[i].subrules[j].scnt<0 ) { + LogError( _("Bad count in contextual chaining sub-table.\n") ); + info->bad_ot = true; + free(rules); +return; + } + rules[i].subrules[j].sl = galloc(rules[i].subrules[j].scnt*sizeof(struct seqlookup)); + for ( k=0; k<rules[i].subrules[j].scnt; ++k ) { + rules[i].subrules[j].sl[k].seq = getushort(ttf); + if ( rules[i].subrules[j].sl[k].seq >= rules[i].subrules[j].ccnt ) + if ( !warned2 ) { + LogError( _("Attempt to apply a lookup to a location out of the range of this contextual\n lookup seq=%d max=%d\n"), + rules[i].subrules[j].sl[k].seq, rules[i].subrules[j].ccnt-1); + info->bad_ot = true; + warned2 = true; + } + rules[i].subrules[j].sl[k].lookup = (void *) (intpt) getushort(ttf); + } + } + } + + if ( justinuse==git_justinuse ) { + /* Nothing to do. This lookup doesn't really reference any glyphs */ + /* any lookups it invokes will be processed on their own */ + } else { + fpst = chunkalloc(sizeof(FPST)); + fpst->type = gpos ? pst_contextpos : pst_contextsub; + fpst->format = pst_class; + fpst->subtable = subtable; + subtable->fpst = fpst; + fpst->next = info->possub; + info->possub = fpst; + + fpst->rules = rule = gcalloc(cnt,sizeof(struct fpst_rule)); + fpst->rule_cnt = cnt; + class = getClassDefTable(ttf, stoffset+classoff, info); + fpst->nccnt = ClassFindCnt(class,info->glyph_cnt); + fpst->nclass = ClassToNames(info,fpst->nccnt,class,info->glyph_cnt); + + /* Just in case they used the coverage table to redefine class 0 */ + glyphs = getCoverageTable(ttf,stoffset+coverage,info); + fpst->nclass[0] = CoverageMinusClasses(glyphs,class,info); + free(glyphs); free(class); class = NULL; + + cnt = 0; + for ( i=0; i<rcnt; ++i ) for ( j=0; j<rules[i].scnt; ++j ) { + rule[cnt].u.class.nclasses = rules[i].subrules[j].classindeces; + rule[cnt].u.class.ncnt = rules[i].subrules[j].ccnt; + rules[i].subrules[j].classindeces = NULL; + rule[cnt].lookup_cnt = rules[i].subrules[j].scnt; + rule[cnt].lookups = rules[i].subrules[j].sl; + rules[i].subrules[j].sl = NULL; + for ( k=0; k<rule[cnt].lookup_cnt; ++k ) + ProcessSubLookups(ttf,info,gpos,alllooks,&rule[cnt].lookups[k]); + ++cnt; + } + } + + for ( i=0; i<rcnt; ++i ) { + for ( j=0; j<rules[i].scnt; ++j ) { + free(rules[i].subrules[j].classindeces); + free(rules[i].subrules[j].sl); + } + free(rules[i].subrules); + } + free(rules); +} + +static void g___ChainingSubTable2(FILE *ttf, int stoffset, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, int justinuse, + struct lookup *alllooks, int gpos) { + int i, j, k, rcnt, cnt; + uint16 coverage, offset; + uint16 bclassoff, classoff, fclassoff; + struct subrule { + uint32 offset; + int ccnt, bccnt, fccnt; + int scnt; + uint16 *classindeces, *bci, *fci; + struct seqlookup *sl; + }; + struct rule { + uint32 offsets; + int scnt; + struct subrule *subrules; + } *rules; + FPST *fpst; + struct fpst_rule *rule; + uint16 *glyphs, *class; + int warned2 = false; + (void)l; /* for -Wall */ + coverage = getushort(ttf); + bclassoff = getushort(ttf); + classoff = getushort(ttf); + fclassoff = getushort(ttf); + rcnt = getushort(ttf); /* class count *//* == max number of top level rules */ + rules = gcalloc(rcnt,sizeof(struct rule)); + for ( i=0; i<rcnt; ++i ) { + offset = getushort(ttf); + rules[i].offsets = offset==0 ? 0 : offset+stoffset; + } + cnt = 0; + for ( i=0; i<rcnt; ++i ) if ( rules[i].offsets!=0 ) { /* some classes might be unused */ + fseek(ttf,rules[i].offsets,SEEK_SET); + rules[i].scnt = getushort(ttf); + if ( rules[i].scnt<0 ) { + LogError( _("Bad count in context chaining sub-table.\n") ); + info->bad_ot = true; +return; + } + cnt += rules[i].scnt; + rules[i].subrules = galloc(rules[i].scnt*sizeof(struct subrule)); + for ( j=0; j<rules[i].scnt; ++j ) + rules[i].subrules[j].offset = getushort(ttf)+rules[i].offsets; + for ( j=0; j<rules[i].scnt; ++j ) { + fseek(ttf,rules[i].subrules[j].offset,SEEK_SET); + rules[i].subrules[j].bccnt = getushort(ttf); + if ( rules[i].subrules[j].bccnt<0 ) { + LogError( _("Bad class count in contextual chaining sub-table.\n") ); + info->bad_ot = true; + free(rules); +return; + } + rules[i].subrules[j].bci = galloc(rules[i].subrules[j].bccnt*sizeof(uint16)); + for ( k=0; k<rules[i].subrules[j].bccnt; ++k ) + rules[i].subrules[j].bci[k] = getushort(ttf); + rules[i].subrules[j].ccnt = getushort(ttf); + if ( rules[i].subrules[j].ccnt<0 ) { + LogError( _("Bad class count in contextual chaining sub-table.\n") ); + info->bad_ot = true; + free(rules); +return; + } + rules[i].subrules[j].classindeces = galloc(rules[i].subrules[j].ccnt*sizeof(uint16)); + rules[i].subrules[j].classindeces[0] = i; + for ( k=1; k<rules[i].subrules[j].ccnt; ++k ) + rules[i].subrules[j].classindeces[k] = getushort(ttf); + rules[i].subrules[j].fccnt = getushort(ttf); + if ( rules[i].subrules[j].fccnt<0 ) { + LogError( _("Bad class count in contextual chaining sub-table.\n") ); + info->bad_ot = true; + free(rules); +return; + } + rules[i].subrules[j].fci = galloc(rules[i].subrules[j].fccnt*sizeof(uint16)); + for ( k=0; k<rules[i].subrules[j].fccnt; ++k ) + rules[i].subrules[j].fci[k] = getushort(ttf); + rules[i].subrules[j].scnt = getushort(ttf); + if ( rules[i].subrules[j].scnt<0 ) { + LogError( _("Bad count in contextual chaining sub-table.\n") ); + info->bad_ot = true; + free(rules); +return; + } + rules[i].subrules[j].sl = galloc(rules[i].subrules[j].scnt*sizeof(struct seqlookup)); + for ( k=0; k<rules[i].subrules[j].scnt; ++k ) { + rules[i].subrules[j].sl[k].seq = getushort(ttf); + if ( rules[i].subrules[j].sl[k].seq >= rules[i].subrules[j].ccnt ) + if ( !warned2 ) { + LogError( _("Attempt to apply a lookup to a location out of the range of this contextual\n lookup seq=%d max=%d\n"), + rules[i].subrules[j].sl[k].seq, rules[i].subrules[j].ccnt-1); + info->bad_ot = true; + warned2 = true; + } + rules[i].subrules[j].sl[k].lookup = (void *) (intpt) getushort(ttf); + } + } + } + + if ( justinuse==git_justinuse ) { + /* Nothing to do. This lookup doesn't really reference any glyphs */ + /* any lookups it invokes will be processed on their own */ + } else { + fpst = chunkalloc(sizeof(FPST)); + fpst->type = gpos ? pst_chainpos : pst_chainsub; + fpst->format = pst_class; + fpst->subtable = subtable; + subtable->fpst = fpst; + fpst->next = info->possub; + info->possub = fpst; + + fpst->rules = rule = gcalloc(cnt,sizeof(struct fpst_rule)); + fpst->rule_cnt = cnt; + + class = getClassDefTable(ttf, stoffset+classoff, info); + fpst->nccnt = ClassFindCnt(class,info->glyph_cnt); + fpst->nclass = ClassToNames(info,fpst->nccnt,class,info->glyph_cnt); + + /* Just in case they used the coverage table to redefine class 0 */ + glyphs = getCoverageTable(ttf,stoffset+coverage,info); + fpst->nclass[0] = CoverageMinusClasses(glyphs,class,info); + free(glyphs); free(class); class = NULL; + + /* The docs don't mention this, but in mangal.ttf fclassoff==0 NULL */ + if ( bclassoff!=0 ) + class = getClassDefTable(ttf, stoffset+bclassoff, info); + else + class = gcalloc(info->glyph_cnt,sizeof(uint16)); + fpst->bccnt = ClassFindCnt(class,info->glyph_cnt); + fpst->bclass = ClassToNames(info,fpst->bccnt,class,info->glyph_cnt); + free(class); + if ( fclassoff!=0 ) + class = getClassDefTable(ttf, stoffset+fclassoff, info); + else + class = gcalloc(info->glyph_cnt,sizeof(uint16)); + fpst->fccnt = ClassFindCnt(class,info->glyph_cnt); + fpst->fclass = ClassToNames(info,fpst->fccnt,class,info->glyph_cnt); + free(class); + + cnt = 0; + for ( i=0; i<rcnt; ++i ) for ( j=0; j<rules[i].scnt; ++j ) { + rule[cnt].u.class.nclasses = rules[i].subrules[j].classindeces; + rule[cnt].u.class.ncnt = rules[i].subrules[j].ccnt; + rules[i].subrules[j].classindeces = NULL; + rule[cnt].u.class.bclasses = rules[i].subrules[j].bci; + rule[cnt].u.class.bcnt = rules[i].subrules[j].bccnt; + rules[i].subrules[j].bci = NULL; + rule[cnt].u.class.fclasses = rules[i].subrules[j].fci; + rule[cnt].u.class.fcnt = rules[i].subrules[j].fccnt; + rules[i].subrules[j].fci = NULL; + rule[cnt].lookup_cnt = rules[i].subrules[j].scnt; + rule[cnt].lookups = rules[i].subrules[j].sl; + rules[i].subrules[j].sl = NULL; + for ( k=0; k<rule[cnt].lookup_cnt; ++k ) + ProcessSubLookups(ttf,info,gpos,alllooks,&rule[cnt].lookups[k]); + ++cnt; + } + } + + for ( i=0; i<rcnt; ++i ) { + for ( j=0; j<rules[i].scnt; ++j ) { + free(rules[i].subrules[j].classindeces); + free(rules[i].subrules[j].sl); + } + free(rules[i].subrules); + } + free(rules); +} + +static void g___ContextSubTable3(FILE *ttf, int stoffset, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, int justinuse, + struct lookup *alllooks, int gpos) { + int i, k, scnt, gcnt; + uint16 *coverage; + struct seqlookup *sl; + uint16 *glyphs; + FPST *fpst; + struct fpst_rule *rule; + int warned2 = false; + (void)l; /* for -Wall */ + gcnt = getushort(ttf); + scnt = getushort(ttf); + if ( feof(ttf) ) { + LogError( _("End of file in context chaining sub-table.\n") ); + info->bad_ot = true; +return; + } + coverage = galloc(gcnt*sizeof(uint16)); + for ( i=0; i<gcnt; ++i ) + coverage[i] = getushort(ttf); + sl = galloc(scnt*sizeof(struct seqlookup)); + for ( k=0; k<scnt; ++k ) { + sl[k].seq = getushort(ttf); + if ( sl[k].seq >= gcnt && !warned2 ) { + LogError( _("Attempt to apply a lookup to a location out of the range of this contextual\n lookup seq=%d, max=%d\n"), + sl[k].seq, gcnt-1 ); + info->bad_ot = true; + warned2 = true; + } + sl[k].lookup = (void *) (intpt) getushort(ttf); + } + + if ( justinuse==git_justinuse ) { + /* Nothing to do. This lookup doesn't really reference any glyphs */ + /* any lookups it invokes will be processed on their own */ + } else { + fpst = chunkalloc(sizeof(FPST)); + fpst->type = gpos ? pst_contextpos : pst_contextsub; + fpst->format = pst_coverage; + fpst->subtable = subtable; + subtable->fpst = fpst; + fpst->next = info->possub; + info->possub = fpst; + + fpst->rules = rule = gcalloc(1,sizeof(struct fpst_rule)); + fpst->rule_cnt = 1; + rule->u.coverage.ncnt = gcnt; + rule->u.coverage.ncovers = galloc(gcnt*sizeof(char **)); + for ( i=0; i<gcnt; ++i ) { + glyphs = getCoverageTable(ttf,stoffset+coverage[i],info); + rule->u.coverage.ncovers[i] = GlyphsToNames(info,glyphs,true); + free(glyphs); + } + rule->lookup_cnt = scnt; + rule->lookups = sl; + for ( k=0; k<scnt; ++k ) + ProcessSubLookups(ttf,info,gpos,alllooks,&sl[k]); + } + + free(coverage); +} + +static void g___ChainingSubTable3(FILE *ttf, int stoffset, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, int justinuse, + struct lookup *alllooks, int gpos) { + int i, k, scnt, gcnt, bcnt, fcnt; + uint16 *coverage, *bcoverage, *fcoverage; + struct seqlookup *sl; + uint16 *glyphs; + FPST *fpst; + struct fpst_rule *rule; + int warned2 = false; + (void)l; /* for -Wall */ + bcnt = getushort(ttf); + if ( feof(ttf)) { + LogError( _("End of file in context chaining subtable.\n") ); + info->bad_ot = true; +return; + } + bcoverage = galloc(bcnt*sizeof(uint16)); + for ( i=0; i<bcnt; ++i ) + bcoverage[i] = getushort(ttf); + gcnt = getushort(ttf); + if ( feof(ttf)) { + LogError( _("End of file in context chaining subtable.\n") ); + info->bad_ot = true; +return; + } + coverage = galloc(gcnt*sizeof(uint16)); + for ( i=0; i<gcnt; ++i ) + coverage[i] = getushort(ttf); + fcnt = getushort(ttf); + if ( feof(ttf)) { + LogError( _("End of file in context chaining subtable.\n") ); + info->bad_ot = true; +return; + } + fcoverage = galloc(fcnt*sizeof(uint16)); + for ( i=0; i<fcnt; ++i ) + fcoverage[i] = getushort(ttf); + scnt = getushort(ttf); + if ( feof(ttf)) { + LogError( _("End of file in context chaining subtable.\n") ); + info->bad_ot = true; +return; + } + sl = galloc(scnt*sizeof(struct seqlookup)); + for ( k=0; k<scnt; ++k ) { + sl[k].seq = getushort(ttf); + if ( sl[k].seq >= gcnt && !warned2 ) { + LogError( _("Attempt to apply a lookup to a location out of the range of this contextual\n lookup seq=%d, max=%d\n"), + sl[k].seq, gcnt-1 ); + info->bad_ot = true; + warned2 = true; + } + sl[k].lookup = (void *) (intpt) getushort(ttf); + } + + if ( justinuse==git_justinuse ) { + /* Nothing to do. This lookup doesn't really reference any glyphs */ + /* any lookups it invokes will be processed on their own */ + } else { + fpst = chunkalloc(sizeof(FPST)); + fpst->type = gpos ? pst_chainpos : pst_chainsub; + fpst->format = pst_coverage; + fpst->subtable = subtable; + subtable->fpst = fpst; + fpst->next = info->possub; + info->possub = fpst; + + fpst->rules = rule = gcalloc(1,sizeof(struct fpst_rule)); + fpst->rule_cnt = 1; + + rule->u.coverage.bcnt = bcnt; + rule->u.coverage.bcovers = galloc(bcnt*sizeof(char **)); + for ( i=0; i<bcnt; ++i ) { + glyphs = getCoverageTable(ttf,stoffset+bcoverage[i],info); + rule->u.coverage.bcovers[i] = GlyphsToNames(info,glyphs,true); + free(glyphs); + } + + rule->u.coverage.ncnt = gcnt; + rule->u.coverage.ncovers = galloc(gcnt*sizeof(char **)); + for ( i=0; i<gcnt; ++i ) { + glyphs = getCoverageTable(ttf,stoffset+coverage[i],info); + rule->u.coverage.ncovers[i] = GlyphsToNames(info,glyphs,true); + free(glyphs); + } + + rule->u.coverage.fcnt = fcnt; + rule->u.coverage.fcovers = galloc(fcnt*sizeof(char **)); + for ( i=0; i<fcnt; ++i ) { + glyphs = getCoverageTable(ttf,stoffset+fcoverage[i],info); + rule->u.coverage.fcovers[i] = GlyphsToNames(info,glyphs,true); + free(glyphs); + } + + rule->lookup_cnt = scnt; + rule->lookups = sl; + for ( k=0; k<scnt; ++k ) + ProcessSubLookups(ttf,info,gpos,alllooks,&sl[k]); + } + + free(bcoverage); + free(coverage); + free(fcoverage); +} + +static void gposContextSubTable(FILE *ttf, int stoffset, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, + struct lookup *alllooks) { + switch( getushort(ttf)) { + case 1: + g___ContextSubTable1(ttf,stoffset,info,l,subtable,git_normal,alllooks,true); + break; + case 2: + g___ContextSubTable2(ttf,stoffset,info,l,subtable,git_normal,alllooks,true); + break; + case 3: + g___ContextSubTable3(ttf,stoffset,info,l,subtable,git_normal,alllooks,true); + break; + } +} + +static void gposChainingSubTable(FILE *ttf, int stoffset, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, + struct lookup *alllooks) { + switch( getushort(ttf)) { + case 1: + g___ChainingSubTable1(ttf,stoffset,info,l,subtable,git_normal,alllooks,true); + break; + case 2: + g___ChainingSubTable2(ttf,stoffset,info,l,subtable,git_normal,alllooks,true); + break; + case 3: + g___ChainingSubTable3(ttf,stoffset,info,l,subtable,git_normal,alllooks,true); + break; + } +} + +static struct { uint32 tag; char *str; } tagstr[] = { + { CHR('v','r','t','2'), "vert" }, + { CHR('s','m','c','p'), "sc" }, + { CHR('s','m','c','p'), "small" }, + { CHR('o','n','u','m'), "oldstyle" }, + { CHR('s','u','p','s'), "superior" }, + { CHR('s','u','b','s'), "inferior" }, + { CHR('s','w','s','h'), "swash" }, + { 0, NULL } +}; + +static void gsubSimpleSubTable(FILE *ttf, int stoffset, struct ttfinfo *info, + struct lookup *l, struct lookup_subtable *subtable, int justinuse) { + int coverage, cnt, i, j, which; + uint16 format; + uint16 *glyphs, *glyph2s=NULL; + int delta=0; + + format=getushort(ttf); + if ( format!=1 && format!=2 ) /* Unknown subtable format */ +return; + coverage = getushort(ttf); + if ( format==1 ) { + delta = getushort(ttf); + } else { + cnt = getushort(ttf); + glyph2s = galloc(cnt*sizeof(uint16)); + for ( i=0; i<cnt; ++i ) + glyph2s[i] = getushort(ttf); + /* in range check comes later */ + } + glyphs = getCoverageTable(ttf,stoffset+coverage,info); + if ( glyphs==NULL ) { + free(glyph2s); +return; + } + if ( justinuse==git_findnames ) { + FeatureScriptLangList *fl; + fl = l->otlookup->features; + /* Unnamed glyphs get a name built of the base name and the feature tag */ + /* assuming this lookup is tagged with a feature... */ + if ( fl!=NULL ) + for ( i=0; glyphs[i]!=0xffff; ++i ) if ( glyphs[i]<info->glyph_cnt ) { + if ( info->chars[glyphs[i]]->name!=NULL ) { + which = format==1 ? (uint16) (glyphs[i]+delta) : glyph2s[i]; + if ( which<info->glyph_cnt && which>=0 && info->chars[which]!=NULL && + info->chars[which]->name==NULL ) { + char *basename = info->chars[glyphs[i]]->name; + char *str; + char tag[5], *pt=tag; + for ( j=0; tagstr[j].tag!=0 && tagstr[j].tag!=fl->featuretag; ++j ); + if ( tagstr[j].tag!=0 ) + pt = tagstr[j].str; + else { + tag[0] = fl->featuretag>>24; + if ( (tag[1] = (fl->featuretag>>16)&0xff)==' ' ) tag[1] = '\0'; + if ( (tag[2] = (fl->featuretag>>8)&0xff)==' ' ) tag[2] = '\0'; + if ( (tag[3] = (fl->featuretag)&0xff)==' ' ) tag[3] = '\0'; + tag[4] = '\0'; + pt = tag; + } + str = galloc(strlen(basename)+strlen(pt)+2); + sprintf(str,"%s.%s", basename, pt ); + info->chars[which]->name = str; + } + } + } + } else if ( justinuse==git_justinuse ) { + for ( i=0; glyphs[i]!=0xffff; ++i ) if ( glyphs[i]<info->glyph_cnt ) { + info->inuse[glyphs[i]]= true; + which = format==1 ? (uint16) (glyphs[i]+delta) : glyph2s[i]; + info->inuse[which]= true; + } + } else if ( justinuse==git_normal ) { + for ( i=0; glyphs[i]!=0xffff; ++i ) if ( glyphs[i]<info->glyph_cnt && info->chars[glyphs[i]]!=NULL ) { + which = format==1 ? (uint16) (glyphs[i]+delta) : glyph2s[i]; + if ( which>=info->glyph_cnt ) { + LogError( _("Bad substitution glyph: GID %d not less than %d\n"), + which, info->glyph_cnt); + info->bad_ot = true; + which = 0; + } + if ( info->chars[which]!=NULL ) { /* Might be in a ttc file */ + PST *pos = chunkalloc(sizeof(PST)); + pos->type = pst_substitution; + pos->subtable = subtable; + pos->next = info->chars[glyphs[i]]->possub; + info->chars[glyphs[i]]->possub = pos; + pos->u.subs.variant = copy(info->chars[which]->name); + } + } + } + subtable->per_glyph_pst_or_kern = true; + free(glyph2s); + free(glyphs); +} + +/* Multiple and alternate substitution lookups have the same format */ +static void gsubMultipleSubTable(FILE *ttf, int stoffset, struct ttfinfo *info, + struct lookup *l, struct lookup_subtable *subtable, int justinuse) { + int coverage, cnt, i, j, len, max; + uint16 format; + uint16 *offsets; + uint16 *glyphs, *glyph2s; + char *pt; + int bad; + int badcnt = 0; + + if ( justinuse==git_findnames ) +return; + + format=getushort(ttf); + if ( format!=1 ) /* Unknown subtable format */ +return; + coverage = getushort(ttf); + cnt = getushort(ttf); + if ( feof(ttf)) { + LogError( _("Unexpected end of file in GSUB sub-table.\n")); + info->bad_ot = true; +return; + } + offsets = galloc(cnt*sizeof(uint16)); + for ( i=0; i<cnt; ++i ) + offsets[i] = getushort(ttf); + glyphs = getCoverageTable(ttf,stoffset+coverage,info); + if ( glyphs==NULL ) { + free(offsets); +return; + } + for ( i=0; glyphs[i]!=0xffff; ++i ); + if ( i!=cnt ) { + LogError( _("Coverage table specifies a different number of glyphs than the sub-table expects.\n" )); + info->bad_ot = true; + if ( cnt<i ) + glyphs[cnt] = 0xffff; + else + cnt = i; + } + max = 20; + glyph2s = galloc(max*sizeof(uint16)); + for ( i=0; glyphs[i]!=0xffff; ++i ) { + PST *alt; + fseek(ttf,stoffset+offsets[i],SEEK_SET); + cnt = getushort(ttf); + if ( feof(ttf)) { + LogError( _("Unexpected end of file in GSUB sub-table.\n")); + info->bad_ot = true; +return; + } + if ( cnt>max ) { + max = cnt+30; + glyph2s = grealloc(glyph2s,max*sizeof(uint16)); + } + len = 0; bad = false; + for ( j=0; j<cnt; ++j ) { + glyph2s[j] = getushort(ttf); + if ( feof(ttf)) { + LogError( _("Unexpected end of file in GSUB sub-table.\n" )); + info->bad_ot = true; +return; + } + if ( glyph2s[j]>=info->glyph_cnt ) { + if ( !justinuse ) + LogError( _("Bad Multiple/Alternate substitution glyph. GID %d not less than %d\n"), + glyph2s[j], info->glyph_cnt ); + info->bad_ot = true; + if ( ++badcnt>20 ) +return; + glyph2s[j] = 0; + } + if ( justinuse==git_justinuse ) + /* Do Nothing */; + else if ( info->chars[glyph2s[j]]==NULL ) + bad = true; + else + len += strlen( info->chars[glyph2s[j]]->name) +1; + } + if ( justinuse==git_justinuse ) { + info->inuse[glyphs[i]] = 1; + for ( j=0; j<cnt; ++j ) + info->inuse[glyph2s[j]] = 1; + } else if ( info->chars[glyphs[i]]!=NULL && !bad ) { + alt = chunkalloc(sizeof(PST)); + alt->type = l->otlookup->lookup_type==gsub_multiple?pst_multiple:pst_alternate; + alt->subtable = subtable; + alt->next = info->chars[glyphs[i]]->possub; + info->chars[glyphs[i]]->possub = alt; + pt = alt->u.subs.variant = galloc(len+1); + *pt = '\0'; + for ( j=0; j<cnt; ++j ) { + strcat(pt,info->chars[glyph2s[j]]->name); + strcat(pt," "); + } + if ( *pt!='\0' && pt[strlen(pt)-1]==' ' ) + pt[strlen(pt)-1] = '\0'; + } + } + subtable->per_glyph_pst_or_kern = true; + free(glyphs); + free(glyph2s); + free(offsets); +} + +static void gsubLigatureSubTable(FILE *ttf, int stoffset, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, int justinuse) { + int coverage, cnt, i, j, k, lig_cnt, cc, len; + uint16 *ls_offsets, *lig_offsets; + uint16 *glyphs, *lig_glyphs, lig; + char *pt; + PST *liga; + + /* Format = */ getushort(ttf); + coverage = getushort(ttf); + cnt = getushort(ttf); + if ( feof(ttf)) { + LogError( _("Unexpected end of file in GSUB ligature sub-table.\n" )); + info->bad_ot = true; +return; + } + ls_offsets = galloc(cnt*sizeof(uint16)); + for ( i=0; i<cnt; ++i ) + ls_offsets[i]=getushort(ttf); + glyphs = getCoverageTable(ttf,stoffset+coverage,info); + if ( glyphs==NULL ) +return; + for ( i=0; i<cnt; ++i ) { + fseek(ttf,stoffset+ls_offsets[i],SEEK_SET); + lig_cnt = getushort(ttf); + if ( feof(ttf)) { + LogError( _("Unexpected end of file in GSUB ligature sub-table.\n" )); + info->bad_ot = true; +return; + } + lig_offsets = galloc(lig_cnt*sizeof(uint16)); + for ( j=0; j<lig_cnt; ++j ) + lig_offsets[j] = getushort(ttf); + if ( feof(ttf)) { + LogError( _("Unexpected end of file in GSUB ligature sub-table.\n" )); + info->bad_ot = true; +return; + } + for ( j=0; j<lig_cnt; ++j ) { + fseek(ttf,stoffset+ls_offsets[i]+lig_offsets[j],SEEK_SET); + lig = getushort(ttf); + if ( lig>=info->glyph_cnt ) { + LogError( _("Bad ligature glyph. GID %d not less than %d\n"), + lig, info->glyph_cnt ); + info->bad_ot = true; + lig = 0; + } + cc = getushort(ttf); + if ( cc<0 || cc>100 ) { + LogError( _("Unlikely count of ligature components (%d), I suspect this ligature sub-\n table is garbage, I'm giving up on it.\n"), cc ); + info->bad_ot = true; + free(glyphs); free(lig_offsets); +return; + } + lig_glyphs = galloc(cc*sizeof(uint16)); + lig_glyphs[0] = glyphs[i]; + for ( k=1; k<cc; ++k ) { + lig_glyphs[k] = getushort(ttf); + if ( lig_glyphs[k]>=info->glyph_cnt ) { + if ( justinuse==git_normal ) + LogError( _("Bad ligature component glyph. GID %d not less than %d (in ligature %d)\n"), + lig_glyphs[k], info->glyph_cnt, lig ); + info->bad_ot = true; + lig_glyphs[k] = 0; + } + } + if ( justinuse==git_justinuse ) { + info->inuse[lig] = 1; + for ( k=0; k<cc; ++k ) + info->inuse[lig_glyphs[k]] = 1; + } else if ( justinuse==git_findnames ) { + FeatureScriptLangList *fl = l->otlookup->features; + /* If our ligature glyph has no name (and its components do) */ + /* give it a name by concatenating components with underscores */ + /* between them, and appending the tag */ + if ( fl!=NULL && info->chars[lig]!=NULL && info->chars[lig]->name==NULL ) { + int len=0; + for ( k=0; k<cc; ++k ) { + if ( info->chars[lig_glyphs[k]]==NULL || info->chars[lig_glyphs[k]]->name==NULL ) + break; + len += strlen(info->chars[lig_glyphs[k]]->name)+1; + } + if ( k==cc ) { + char *str = galloc(len+6), *pt; + char tag[5]; + tag[0] = fl->featuretag>>24; + if ( (tag[1] = (fl->featuretag>>16)&0xff)==' ' ) tag[1] = '\0'; + if ( (tag[2] = (fl->featuretag>>8)&0xff)==' ' ) tag[2] = '\0'; + if ( (tag[3] = (fl->featuretag)&0xff)==' ' ) tag[3] = '\0'; + tag[4] = '\0'; + *str='\0'; + for ( k=0; k<cc; ++k ) { + strcat(str,info->chars[lig_glyphs[k]]->name); + strcat(str,"_"); + } + pt = str+strlen(str); + pt[-1] = '.'; + strcpy(pt,tag); + info->chars[lig]->name = str; + } + } + } else if ( info->chars[lig]!=NULL ) { + for ( k=len=0; k<cc; ++k ) + if ( lig_glyphs[k]<info->glyph_cnt && + info->chars[lig_glyphs[k]]!=NULL ) + len += strlen(info->chars[lig_glyphs[k]]->name)+1; + liga = chunkalloc(sizeof(PST)); + liga->type = pst_ligature; + liga->subtable = subtable; + liga->next = info->chars[lig]->possub; + info->chars[lig]->possub = liga; + liga->u.lig.lig = info->chars[lig]; + liga->u.lig.components = pt = galloc(len); + for ( k=0; k<cc; ++k ) { + if ( lig_glyphs[k]<info->glyph_cnt && + info->chars[lig_glyphs[k]]!=NULL ) { + strcpy(pt,info->chars[lig_glyphs[k]]->name); + pt += strlen(pt); + *pt++ = ' '; + } + } + pt[-1] = '\0'; + } + free(lig_glyphs); + } + free(lig_offsets); + } + subtable->per_glyph_pst_or_kern = true; + free(ls_offsets); free(glyphs); +} + +static void gsubContextSubTable(FILE *ttf, int stoffset, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, int justinuse, + struct lookup *alllooks) { + if ( justinuse==git_findnames ) +return; /* Don't give names to these guys, they might not be unique */ + /* ie. because these are context based there is not a one to one */ + /* mapping between input glyphs and output glyphs. One input glyph */ + /* may go to several output glyphs (depending on context) and so */ + /* <input-glyph-name>"."<tag-name> would be used for several glyphs */ + switch( getushort(ttf)) { + case 1: + g___ContextSubTable1(ttf,stoffset,info,l,subtable,justinuse,alllooks,false); + break; + case 2: + g___ContextSubTable2(ttf,stoffset,info,l,subtable,justinuse,alllooks,false); + break; + case 3: + g___ContextSubTable3(ttf,stoffset,info,l,subtable,justinuse,alllooks,false); + break; + } +} + +static void gsubChainingSubTable(FILE *ttf, int stoffset, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, int justinuse, + struct lookup *alllooks) { + if ( justinuse==git_findnames ) +return; /* Don't give names to these guys, the names might not be unique */ + switch( getushort(ttf)) { + case 1: + g___ChainingSubTable1(ttf,stoffset,info,l,subtable,justinuse,alllooks,false); + break; + case 2: + g___ChainingSubTable2(ttf,stoffset,info,l,subtable,justinuse,alllooks,false); + break; + case 3: + g___ChainingSubTable3(ttf,stoffset,info,l,subtable,justinuse,alllooks,false); + break; + } +} + +static void gsubReverseChainSubTable(FILE *ttf, int stoffset, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, int justinuse) { + int scnt, bcnt, fcnt, i; + uint16 coverage, *bcoverage, *fcoverage, *sglyphs, *glyphs; + FPST *fpst; + struct fpst_rule *rule; + (void)l; /* for -Wall */ + if ( justinuse==git_findnames ) +return; /* Don't give names to these guys, they might not be unique */ + if ( getushort(ttf)!=1 ) +return; /* Don't understand this format type */ + + coverage = getushort(ttf); + bcnt = getushort(ttf); + bcoverage = galloc(bcnt*sizeof(uint16)); + for ( i = 0 ; i<bcnt; ++i ) + bcoverage[i] = getushort(ttf); + fcnt = getushort(ttf); + fcoverage = galloc(fcnt*sizeof(uint16)); + for ( i = 0 ; i<fcnt; ++i ) + fcoverage[i] = getushort(ttf); + scnt = getushort(ttf); + sglyphs = galloc((scnt+1)*sizeof(uint16)); + for ( i = 0 ; i<scnt; ++i ) + if (( sglyphs[i] = getushort(ttf))>=info->glyph_cnt ) { + LogError( _("Bad reverse contextual chaining substitution glyph: %d is not less than %d\n"), + sglyphs[i], info->glyph_cnt ); + info->bad_ot = true; + sglyphs[i] = 0; + } + sglyphs[i] = 0xffff; + + if ( justinuse==git_justinuse ) { + for ( i = 0 ; i<scnt; ++i ) + info->inuse[sglyphs[i]] = 1; + } else { + fpst = chunkalloc(sizeof(FPST)); + fpst->type = pst_reversesub; + fpst->format = pst_reversecoverage; + fpst->subtable = subtable; + fpst->next = info->possub; + info->possub = fpst; + subtable->fpst = fpst; + + fpst->rules = rule = gcalloc(1,sizeof(struct fpst_rule)); + fpst->rule_cnt = 1; + + rule->u.rcoverage.always1 = 1; + rule->u.rcoverage.bcnt = bcnt; + rule->u.rcoverage.fcnt = fcnt; + rule->u.rcoverage.ncovers = galloc(sizeof(char *)); + rule->u.rcoverage.bcovers = galloc(bcnt*sizeof(char *)); + rule->u.rcoverage.fcovers = galloc(fcnt*sizeof(char *)); + rule->u.rcoverage.replacements = GlyphsToNames(info,sglyphs,false); + glyphs = getCoverageTable(ttf,stoffset+coverage,info); + rule->u.rcoverage.ncovers[0] = GlyphsToNames(info,glyphs,false); + free(glyphs); + for ( i=0; i<bcnt; ++i ) { + glyphs = getCoverageTable(ttf,stoffset+bcoverage[i],info); + rule->u.rcoverage.bcovers[i] = GlyphsToNames(info,glyphs,true); + free(glyphs); + } + for ( i=0; i<fcnt; ++i ) { + glyphs = getCoverageTable(ttf,stoffset+fcoverage[i],info); + rule->u.rcoverage.fcovers[i] = GlyphsToNames(info,glyphs,true); + free(glyphs); + } + rule->lookup_cnt = 0; /* substitution lookups needed for reverse chaining */ + } + free(sglyphs); + free(fcoverage); + free(bcoverage); +} + +static void readttfsizeparameters(FILE *ttf,int32 broken_pos,int32 correct_pos, + struct ttfinfo *info) { + int32 here; + /* Both of the two fonts I've seen that contain a 'size' feature */ + /* have multiple features all of which point to the same parameter */ + /* area. Odd. */ + /* When Adobe first released fonts containing the 'size' feature */ + /* they did not follow the spec, and the offset to the size parameters */ + /* was relative to the wrong location. They claim (Aug 2006) that */ + /* this has been fixed. Be prepared to read either style of 'size' */ + /* following the heuristics Adobe provides */ + int32 test[2]; + int i, nid; + + if ( info->last_size_pos==broken_pos || info->last_size_pos==correct_pos ) +return; + + if ( info->last_size_pos!=0 ) { + LogError( _("This font, %s, has multiple GPOS 'size' features. I'm not sure how to interpret that. I shall pick one arbitrarily.\n"), + info->fontname==NULL? _("<Untitled>") : info->fontname ); + info->bad_ot = true; +return; + } + + test[0] = correct_pos; test[1] = broken_pos; + here = ftell(ttf); + for ( i=0; i<2; ++i ) { + fseek(ttf,test[i],SEEK_SET); + info->last_size_pos = test[i]; + info->design_size = getushort(ttf); + if ( info->design_size==0 ) + continue; + info->fontstyle_id = getushort(ttf); + nid = getushort(ttf); + info->design_range_bottom = getushort(ttf); + info->design_range_top = getushort(ttf); + if ( info->fontstyle_id == 0 && nid==0 && + info->design_range_bottom==0 && info->design_range_top==0 ) { + /* Reasonable spec, only design size provided */ + info->fontstyle_name = NULL; + break; + } + if ( info->design_size < info->design_range_bottom || + info->design_size > info->design_range_top || + info->design_range_bottom > info->design_range_top || + nid<256 || nid>32767 ) + continue; + info->fontstyle_name = FindAllLangEntries(ttf,info,nid); + if ( info->fontstyle_name==NULL ) + continue; + else + break; + } + if ( i==2 ) { + LogError(_("The 'size' feature does not seem to follow the standard,\nnor does it conform to Adobe's early misinterpretation of\nthe standard. I cannot parse it.\n") ); + info->bad_ot = true; + info->design_size = info->design_range_bottom = info->design_range_top = info->fontstyle_id = 0; + info->fontstyle_name = NULL; + } else if ( i==1 ) { + LogError(_("The 'size' feature of this font conforms to Adobe's early misinterpretation of the otf standard.\n") ); + } + fseek(ttf,here,SEEK_SET); + +#if 0 + printf( "pos=%d size=%g, range=(%g,%g] id=%d name=%d\n", pos, + info->design_size/10.0, info->design_range_bottom/10.0, info->design_range_top/10.0, + info->fontstyle_id, info->fontstyle_name ); +#endif +} + +static struct scripts *readttfscripts(FILE *ttf,int32 pos, struct ttfinfo *info, int isgpos) { + int i,j,k,cnt; + int deflang, lcnt; + struct scripts *scripts; + + if ( pos>=(int32)info->g_bounds ) { + LogError(_("Attempt to read script data beyond end of %s table"), isgpos ? "GPOS" : "GSUB" ); + info->bad_ot = true; +return( NULL ); + } + fseek(ttf,pos,SEEK_SET); + cnt = getushort(ttf); + if ( cnt<=0 ) +return( NULL ); + else if ( cnt>1000 ) { + LogError( _("Too many scripts %d\n"), cnt ); + info->bad_ot = true; +return( NULL ); + } + + scripts = gcalloc(cnt+1,sizeof(struct scripts)); + for ( i=0; i<cnt; ++i ) { + scripts[i].tag = getlong(ttf); + scripts[i].offset = getushort(ttf); + } + for ( i=0; i<cnt; ++i ) { + fseek(ttf,pos+scripts[i].offset,SEEK_SET); + deflang = getushort(ttf); + lcnt = getushort(ttf); + lcnt += (deflang!=0); + scripts[i].langcnt = lcnt; + scripts[i].languages = gcalloc(lcnt+1,sizeof(struct language)); + j = 0; + if ( deflang!=0 ) { + scripts[i].languages[0].tag = CHR('d','f','l','t'); + scripts[i].languages[0].offset = deflang+scripts[i].offset; + ++j; + } + for ( ; j<lcnt; ++j ) { + scripts[i].languages[j].tag = getlong(ttf); + scripts[i].languages[j].offset = scripts[i].offset+getushort(ttf); + } + for ( j=0; j<lcnt; ++j ) { + if ( pos+scripts[i].languages[j].offset>=info->g_bounds ) { + LogError(_("Attempt to read script data beyond end of %s table"), isgpos ? "GPOS" : "GSUB" ); + info->bad_ot = true; +return( NULL ); + } + fseek(ttf,pos+scripts[i].languages[j].offset,SEEK_SET); + (void) getushort(ttf); /* lookup ordering table undefined */ + scripts[i].languages[j].req = getushort(ttf); + scripts[i].languages[j].fcnt = getushort(ttf); + if ( feof(ttf)) { + LogError(_("End of file when reading scripts in %s table"), isgpos ? "GPOS" : "GSUB" ); + info->bad_ot = true; +return( NULL ); + } + scripts[i].languages[j].features = galloc(scripts[i].languages[j].fcnt*sizeof(uint16)); + for ( k=0; k<scripts[i].languages[j].fcnt; ++k ) + scripts[i].languages[j].features[k] = getushort(ttf); + } + } + + if ( feof(ttf)) { + LogError(_("End of file in %s table"), isgpos ? "GPOS" : "GSUB" ); + info->bad_ot = true; +return( NULL ); + } + +return( scripts ); +} + +static struct feature *readttffeatures(FILE *ttf,int32 pos,int isgpos, struct ttfinfo *info) { + /* read the features table returning an array containing all interesting */ + /* features */ + int cnt; + int i,j; + struct feature *features; + int parameters; + + if ( pos>=(int32)info->g_bounds ) { + LogError(_("Attempt to read feature data beyond end of %s table"), isgpos ? "GPOS" : "GSUB" ); + info->bad_ot = true; +return( NULL ); + } + fseek(ttf,pos,SEEK_SET); + info->feature_cnt = cnt = getushort(ttf); + if ( cnt<=0 ) +return( NULL ); + else if ( cnt>1000 ) { + LogError( _("Too many features %d\n"), cnt ); + info->bad_ot = true; +return( NULL ); + } + + features = gcalloc(cnt+1,sizeof(struct feature)); + for ( i=0; i<cnt; ++i ) { + features[i].tag = getlong(ttf); + features[i].offset = getushort(ttf); + } + + for ( i=0; i<cnt; ++i ) { + if ( pos+features[i].offset>=info->g_bounds ) { + LogError(_("Attempt to read feature data beyond end of %s table"), isgpos ? "GPOS" : "GSUB" ); + info->bad_ot = true; +return( NULL ); + } + fseek(ttf,pos+features[i].offset,SEEK_SET); + parameters = getushort(ttf); + if ( features[i].tag==CHR('s','i','z','e') && parameters!=0 && !feof(ttf)) + readttfsizeparameters(ttf,pos+parameters, + pos+parameters+features[i].offset,info); + features[i].lcnt = getushort(ttf); + if ( feof(ttf) ) { + LogError(_("End of file when reading features in %s table"), isgpos ? "GPOS" : "GSUB" ); + info->bad_ot = true; +return( NULL ); + } + features[i].lookups = galloc(features[i].lcnt*sizeof(uint16)); + for ( j=0; j<features[i].lcnt; ++j ) + features[i].lookups[j] = getushort(ttf); + } + +return( features ); +} + +static struct lookup *readttflookups(FILE *ttf,int32 pos, struct ttfinfo *info, int isgpos) { + int cnt,i,j; + struct lookup *lookups; + OTLookup *otlookup, *last=NULL; + struct lookup_subtable *st; + + if ( pos>=(int32)info->g_bounds ) { + LogError(_("Attempt to read lookup data beyond end of %s table"), isgpos ? "GPOS" : "GSUB" ); + info->bad_ot = true; +return( NULL ); + } + + fseek(ttf,pos,SEEK_SET); + info->lookup_cnt = cnt = getushort(ttf); + info->cur_lookups = NULL; + if ( cnt<=0 ) +return( NULL ); + else if ( cnt>1000 ) { + LogError( _("Too many lookups %d\n"), cnt ); + info->bad_ot = true; +return( NULL ); + } + + lookups = gcalloc(cnt+1,sizeof(struct lookup)); + for ( i=0; i<cnt; ++i ) + lookups[i].offset = getushort(ttf); + for ( i=0; i<cnt; ++i ) { + if ( pos+lookups[i].offset>=info->g_bounds ) { + LogError(_("Attempt to read lookup data beyond end of %s table"), isgpos ? "GPOS" : "GSUB" ); + info->bad_ot = true; +return( NULL ); + } + fseek(ttf,pos+lookups[i].offset,SEEK_SET); + lookups[i].type = getushort(ttf); + lookups[i].flags = getushort(ttf); + lookups[i].subtabcnt = getushort(ttf); + lookups[i].subtab_offsets = galloc(lookups[i].subtabcnt*sizeof(int32)); + for ( j=0; j<lookups[i].subtabcnt; ++j ) + lookups[i].subtab_offsets[j] = pos+lookups[i].offset+getushort(ttf); + + lookups[i].otlookup = otlookup = chunkalloc(sizeof(OTLookup)); + otlookup->lookup_index = i; + if ( last==NULL ) + info->cur_lookups = otlookup; + else + last->next = otlookup; + last = otlookup; + otlookup->lookup_type = (isgpos<<8) | lookups[i].type; + otlookup->lookup_flags = lookups[i].flags; + otlookup->lookup_index = i; + if ( feof(ttf) ) { + LogError(_("End of file when reading lookups in %s table"), isgpos ? "GPOS" : "GSUB" ); + info->bad_ot = true; +return( NULL ); + } + for ( j=0; j<lookups[i].subtabcnt; ++j ) { + st = chunkalloc(sizeof(struct lookup_subtable)); + st->next = otlookup->subtables; + st->lookup = otlookup; + otlookup->subtables = st; + } + } + if ( isgpos ) + info->gpos_lookups = info->cur_lookups; + else + info->gsub_lookups = info->cur_lookups; +return( lookups ); +} + +static void tagLookupsWithFeature(uint32 script_tag,uint32 lang_tag, + int required_feature, struct feature *feature, struct lookup *lookups, + struct ttfinfo *info) { + uint32 feature_tag = required_feature ? REQUIRED_FEATURE : feature->tag; + int i; + OTLookup *otlookup; + FeatureScriptLangList *fl; + + /* The otf docs are ambiguous as to the capitalization of the default */ + /* script. The capitalized version is correct (uncapitalized is used for languages) */ + if ( script_tag == DEFAULT_LANG ) + script_tag = DEFAULT_SCRIPT; + + for ( i=0; i < feature->lcnt; ++i ) { + if ( feature->lookups[i]>=info->lookup_cnt ) { + LogError( _("Lookup out of bounds in feature table.\n") ); + info->bad_ot = true; + } else { + otlookup = lookups[feature->lookups[i]].otlookup; + for ( fl = otlookup->features; fl!=NULL && fl->featuretag!=feature_tag; fl=fl->next ); + if ( fl==NULL ) { + fl = chunkalloc(sizeof(FeatureScriptLangList)); + fl->featuretag = feature_tag; + fl->next = otlookup->features; + otlookup->features = fl; + } + FListAppendScriptLang(fl,script_tag,lang_tag); + } + } +} + +static void tagLookupsWithScript(struct scripts *scripts, + struct feature *features, struct lookup *lookups,struct ttfinfo *info ) { + int i,j; + struct scripts *s; + struct language *lang; + struct lookup *l; + + if ( scripts==NULL || features==NULL ) +return; /* Legal, I'd guess, but not very interesting. Perhaps all lookups are controlled by the JSTF table or something */ + + /* First tag every lookup with all script, lang, feature combinations that*/ + /* invoke it */ + for ( s=scripts; s->tag!=0; ++s ) { + for ( lang=s->languages, i=0; i<s->langcnt; ++i, ++lang ) { + if ( lang->req==0xffff ) + /* Do Nothing */; + else if ( lang->req>= info->feature_cnt ) { + LogError( _("Required feature out of bounds in script table.\n") ); + info->bad_ot = true; + } else + tagLookupsWithFeature(s->tag,lang->tag,true,&features[lang->req], + lookups,info); + for ( j=0; j<lang->fcnt; ++j ) { + if ( lang->features[j]>=info->feature_cnt ) { + LogError( _("Feature out of bounds in script table.\n") ); + info->bad_ot = true; + } else + tagLookupsWithFeature(s->tag,lang->tag,false,&features[lang->features[j]], + lookups,info); + } + } + } + + /* The scripts got added backwards so reverse to put them in */ + /* alphabetic order again */ + for ( l=lookups, i=0; l->offset!=0; ++l, ++i ) { + OTLookup *otl = l->otlookup; + FeatureScriptLangList *fl; + struct scriptlanglist *sl, *next, *prev; + for ( fl=otl->features; fl!=NULL; fl=fl->next ) { + prev = NULL; + for ( sl=fl->scripts; sl!=NULL; sl = next ) { + next = sl->next; + sl->next = prev; + prev = sl; + } + fl->scripts = prev; + } + } +} + +static void gposExtensionSubTable(FILE *ttf, int stoffset, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, + struct lookup *alllooks) { + uint32 base = ftell(ttf), st, offset; + int lu_type; + (void)stoffset; /* for -Wall */ + /* Format = */ getushort(ttf); + lu_type = getushort(ttf); + offset = getlong(ttf); + + l->otlookup->lookup_type = 0x100|lu_type; + + fseek(ttf,st = base+offset,SEEK_SET); + switch ( lu_type ) { + case 1: + gposSimplePos(ttf,st,info,l,subtable); + break; + case 2: + gposKernSubTable(ttf,st,info,l,subtable); + break; + case 3: + gposCursiveSubTable(ttf,st,info,l,subtable); + break; + case 4: case 5: case 6: + gposMarkSubTable(ttf,st,info,l,subtable); + break; + case 7: + gposContextSubTable(ttf,st,info,l,subtable,alllooks); + break; + case 8: + gposChainingSubTable(ttf,st,info,l,subtable,alllooks); + break; + case 9: + LogError( _("This font is erroneous: it has a GPOS extension subtable that points to\nanother extension sub-table.\n") ); + info->bad_ot = true; + break; +/* Any cases added here also need to go in the gposLookupSwitch */ + default: + LogError( _("Unknown GPOS sub-table type: %d\n"), lu_type ); + info->bad_ot = true; + break; + } + if ( ftell(ttf)>info->gpos_start+info->gpos_length ) { + LogError( _("Subtable extends beyond end of GPOS table\n") ); + info->bad_ot = true; + } +} + +static void gsubExtensionSubTable(FILE *ttf, int stoffset, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, int justinuse, + struct lookup *alllooks) { + uint32 base = ftell(ttf), st, offset; + int lu_type; + (void)stoffset; /* for -Wall */ + /* Format = */ getushort(ttf); + lu_type = getushort(ttf); + offset = getlong(ttf); + + l->otlookup->lookup_type = lu_type; + + fseek(ttf,st = base+offset,SEEK_SET); + switch ( lu_type ) { + case 1: + gsubSimpleSubTable(ttf,st,info,l,subtable,justinuse); + break; + case 2: case 3: /* Multiple and alternate have same format, different semantics */ + gsubMultipleSubTable(ttf,st,info,l,subtable,justinuse); + break; + case 4: + gsubLigatureSubTable(ttf,st,info,l,subtable,justinuse); + break; + case 5: + gsubContextSubTable(ttf,st,info,l,subtable,justinuse,alllooks); + break; + case 6: + gsubChainingSubTable(ttf,st,info,l,subtable,justinuse,alllooks); + break; + case 7: + LogError( _("This font is erroneous: it has a GSUB extension subtable that points to\nanother extension sub-table.\n") ); + info->bad_ot = true; + break; + case 8: + gsubReverseChainSubTable(ttf,st,info,l,subtable,justinuse); + break; +/* Any cases added here also need to go in the gsubLookupSwitch */ + default: + LogError( _("Unknown GSUB sub-table type: %d\n"), lu_type ); + info->bad_ot = true; + break; + } + if ( ftell(ttf)>info->gsub_start+info->gsub_length ) { + LogError( _("Subtable extends beyond end of GSUB table\n") ); + info->bad_ot = true; + } +} + +static void gposLookupSwitch(FILE *ttf, int st, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, + struct lookup *alllooks) { + + switch ( l->type | 0x100 ) { + case gpos_single: + gposSimplePos(ttf,st,info,l,subtable); + break; + case gpos_pair: + gposKernSubTable(ttf,st,info,l,subtable); + break; + case gpos_cursive: + gposCursiveSubTable(ttf,st,info,l,subtable); + break; + case gpos_mark2base: case gpos_mark2ligature: case gpos_mark2mark: + gposMarkSubTable(ttf,st,info,l,subtable); + break; + case gpos_context: + gposContextSubTable(ttf,st,info,l,subtable,alllooks); + break; + case gpos_contextchain: + gposChainingSubTable(ttf,st,info,l,subtable,alllooks); + break; + case 0x109: + gposExtensionSubTable(ttf,st,info,l,subtable,alllooks); + break; +/* Any cases added here also need to go in the gposExtensionSubTable */ + default: + LogError( _("Unknown GPOS sub-table type: %d\n"), l->otlookup->lookup_type ); + info->bad_ot = true; + break; + } + if ( ftell(ttf)>info->gpos_start+info->gpos_length ) { + LogError( _("Subtable extends beyond end of GPOS table\n") ); + info->bad_ot = true; + } +} + +static void gsubLookupSwitch(FILE *ttf, int st, + struct ttfinfo *info, struct lookup *l, struct lookup_subtable *subtable, int justinuse, + struct lookup *alllooks) { + + switch ( l->type ) { + case gsub_single: + gsubSimpleSubTable(ttf,st,info,l,subtable,justinuse); + break; + case gsub_multiple: case gsub_alternate: /* Multiple and alternate have same format, different semantics */ + gsubMultipleSubTable(ttf,st,info,l,subtable,justinuse); + break; + case gsub_ligature: + gsubLigatureSubTable(ttf,st,info,l,subtable,justinuse); + break; + case gsub_context: + gsubContextSubTable(ttf,st,info,l,subtable,justinuse,alllooks); + break; + case gsub_contextchain: + gsubChainingSubTable(ttf,st,info,l,subtable,justinuse,alllooks); + break; + case 7: + gsubExtensionSubTable(ttf,st,info,l,subtable,justinuse,alllooks); + break; + case gsub_reversecchain: + gsubReverseChainSubTable(ttf,st,info,l,subtable,justinuse); + break; +/* Any cases added here also need to go in the gsubExtensionSubTable */ + default: + LogError( _("Unknown GSUB sub-table type: %d\n"), l->otlookup->lookup_type ); + info->bad_ot = true; + break; + } + if ( ftell(ttf)>info->g_bounds ) { + LogError( _("Subtable extends beyond end of GSUB table\n" )); + info->bad_ot = true; + } +} + +static void ScriptsFree(struct scripts *scripts) { + int i,j; + + if ( scripts==NULL ) +return; + for ( i=0; scripts[i].offset!=0 ; ++i ) { + for ( j=0; j<scripts[i].langcnt; ++j ) + free( scripts[i].languages[j].features); + free(scripts[i].languages); + } + free(scripts); +} + +static void FeaturesFree(struct feature *features) { + int i; + + if ( features==NULL ) +return; + for ( i=0; features[i].offset!=0 ; ++i ) + free(features[i].lookups); + free(features); +} + +static void LookupsFree(struct lookup *lookups) { + int i; + + for ( i=0; lookups[i].offset!=0 ; ++i ) { + free( lookups[i].subtab_offsets ); + } + free(lookups); +} + +static void ProcessGPOSGSUB(FILE *ttf,struct ttfinfo *info,int gpos,int inusetype) { + int k; + int32 base, lookup_start, st; + int32 script_off, feature_off; + struct scripts *scripts; + struct feature *features; + struct lookup *lookups, *l; + struct lookup_subtable *subtable; + + if ( gpos ) { + base = info->gpos_start; + info->g_bounds = base + info->gpos_length; + } else { + base = info->gsub_start; + info->g_bounds = base + info->gsub_length; + } + fseek(ttf,base,SEEK_SET); + /* version = */ getlong(ttf); + script_off = getushort(ttf); + feature_off = getushort(ttf); + lookup_start = base+getushort(ttf); + + scripts = readttfscripts(ttf,base+script_off,info,gpos); + features = readttffeatures(ttf,base+feature_off,gpos,info); + /* It is legal to have lookups with no features or scripts */ + /* For example if all the lookups were controlled by the JSTF table */ + lookups = readttflookups(ttf,lookup_start,info,gpos); + if ( lookups==NULL ) { + ScriptsFree(scripts); + FeaturesFree(features); +return; + } + tagLookupsWithScript(scripts,features,lookups,info); + ScriptsFree(scripts); scripts = NULL; + FeaturesFree(features); features = NULL; + + for ( l = lookups; l->offset!=0; ++l ) { + for ( k=0, subtable=l->otlookup->subtables; k<l->subtabcnt; ++k, subtable=subtable->next ) { + st = l->subtab_offsets[k]; + fseek(ttf,st,SEEK_SET); + if ( gpos ) { + gposLookupSwitch(ttf,st,info,l,subtable,lookups); + } else { + gsubLookupSwitch(ttf,st,info,l,subtable,inusetype,lookups); + } + } + } + + /* Then generate some user-friendly names for the all the lookups */ + if ( inusetype==git_normal ) + for ( l=lookups; l->offset!=0; ++l ) + NameOTLookup(l->otlookup,NULL); + + LookupsFree(lookups); + if ( inusetype!=git_normal && !gpos ) { + OTLookupListFree(info->gsub_lookups); + info->gsub_lookups = info->cur_lookups = NULL; + } +} + +void readttfgsubUsed(FILE *ttf,struct ttfinfo *info) { + ProcessGPOSGSUB(ttf,info,false,git_justinuse); + info->g_bounds = 0; +} + +void GuessNamesFromGSUB(FILE *ttf,struct ttfinfo *info) { + ProcessGPOSGSUB(ttf,info,false,git_findnames); + info->g_bounds = 0; +} + +void readttfgpossub(FILE *ttf,struct ttfinfo *info,int gpos) { + ProcessGPOSGSUB(ttf,info,gpos,git_normal); + info->g_bounds = 0; +} + +void readttfgdef(FILE *ttf,struct ttfinfo *info) { + int lclo, gclass, mac; + int coverage, cnt, i,j, format; + uint16 *glyphs, *lc_offsets, *offsets; + uint32 caret_base; + PST *pst; + SplineChar *sc; + + fseek(ttf,info->gdef_start,SEEK_SET); + if ( getlong(ttf)!=0x00010000 ) +return; + info->g_bounds = info->gdef_start + info->gdef_length; + gclass = getushort(ttf); + /* attach list = */ getushort(ttf); + lclo = getushort(ttf); /* ligature caret list */ + mac = getushort(ttf); /* mark attach class */ + + if ( gclass!=0 ) { + uint16 *gclasses = getClassDefTable(ttf,info->gdef_start+gclass, info); + for ( i=0; i<info->glyph_cnt; ++i ) + if ( info->chars[i]!=NULL && gclasses[i]!=0 ) + info->chars[i]->glyph_class = gclasses[i]+1; + free(gclasses); + } + + if ( mac!=0 ) { + uint16 *mclasses = getClassDefTable(ttf,info->gdef_start+mac, info); + const char *format_spec = _("MarkClass-%d"); + info->mark_class_cnt = ClassFindCnt(mclasses,info->glyph_cnt); + info->mark_classes = ClassToNames(info,info->mark_class_cnt,mclasses,info->glyph_cnt); + info->mark_class_names = galloc(info->mark_class_cnt*sizeof(char *)); + info->mark_class_names[0] = NULL; + for ( i=1; i<info->mark_class_cnt; ++i ) { + info->mark_class_names[i] = galloc((strlen(format_spec)+10)); + sprintf( info->mark_class_names[i], format_spec, i ); + } + free(mclasses); + } + + if ( lclo!=0 ) { + lclo += info->gdef_start; + fseek(ttf,lclo,SEEK_SET); + coverage = getushort(ttf); + cnt = getushort(ttf); + if ( cnt==0 ) +return; + lc_offsets = galloc(cnt*sizeof(uint16)); + for ( i=0; i<cnt; ++i ) + lc_offsets[i]=getushort(ttf); + glyphs = getCoverageTable(ttf,lclo+coverage,info); + if ( glyphs==NULL ) +return; + for ( i=0; i<cnt; ++i ) if ( glyphs[i]<info->glyph_cnt ) { + fseek(ttf,lclo+lc_offsets[i],SEEK_SET); + sc = info->chars[glyphs[i]]; + for ( pst=sc->possub; pst!=NULL && pst->type!=pst_lcaret; pst=pst->next ); + if ( pst==NULL ) { + pst = chunkalloc(sizeof(PST)); + pst->next = sc->possub; + sc->possub = pst; + pst->type = pst_lcaret; + pst->subtable = NULL; + sc->lig_caret_cnt_fixed = true; + } + caret_base = ftell(ttf); + pst->u.lcaret.cnt = getushort(ttf); + if ( pst->u.lcaret.carets!=NULL ) free(pst->u.lcaret.carets); + offsets = galloc(pst->u.lcaret.cnt*sizeof(uint16)); + for ( j=0; j<pst->u.lcaret.cnt; ++j ) + offsets[j] = getushort(ttf); + pst->u.lcaret.carets = galloc(pst->u.lcaret.cnt*sizeof(int16)); + for ( j=0; j<pst->u.lcaret.cnt; ++j ) { + fseek(ttf,caret_base+offsets[j],SEEK_SET); + format=getushort(ttf); + if ( format==1 ) { + pst->u.lcaret.carets[j] = getushort(ttf); + } else if ( format==2 ) { + pst->u.lcaret.carets[j] = 0; + /* point = */ getushort(ttf); + } else if ( format==3 ) { + pst->u.lcaret.carets[j] = getushort(ttf); + /* in device table = */ getushort(ttf); + } else { + LogError( _("!!!! Unknown caret format %d !!!!\n"), format ); + info->bad_ot = true; + } + } + free(offsets); + } + free(lc_offsets); + free(glyphs); + } + info->g_bounds = 0; +} + +static void readttf_applelookup(FILE *ttf,struct ttfinfo *info, + void (*apply_values)(struct ttfinfo *info, int gfirst, int glast,FILE *ttf), + void (*apply_value)(struct ttfinfo *info, int gfirst, int glast,FILE *ttf), + void (*apply_default)(struct ttfinfo *info, int gfirst, int glast,void *def), + void *def, int allow_out_of_bounds) { + int format, i, first, last, data_off, cnt, prev; + uint32 here; + uint32 base = ftell(ttf); + + format = getushort(ttf); + switch ( format ) { + case 0: /* Simple array */ + apply_values(info,0,info->glyph_cnt-1,ttf); + break; + case 2: /* Segment Single */ + /* Entry size */ getushort(ttf); + cnt = getushort(ttf); + /* search range */ getushort(ttf); + /* log2(cnt) */ getushort(ttf); + /* range shift */ getushort(ttf); + prev = 0; + for ( i=0; i<cnt; ++i ) { + last = getushort(ttf); + first = getushort(ttf); + if ( last<first || last>=0xffff || + (!allow_out_of_bounds && last>=info->glyph_cnt )) { + LogError( _("Bad lookup table: format=2 (%d/%d), first=%d last=%d total glyphs in font=%d\n"), + i,cnt,first,last,info->glyph_cnt ); + info->bad_gx = true; + } else { + if ( apply_default!=NULL ) + apply_default(info,prev,first-1,def); + apply_value(info,first,last,ttf); + prev = last+1; + } + } + break; + case 4: /* Segment multiple */ + /* Entry size */ getushort(ttf); + cnt = getushort(ttf); + /* search range */ getushort(ttf); + /* log2(cnt) */ getushort(ttf); + /* range shift */ getushort(ttf); + prev = 0; + for ( i=0; i<cnt; ++i ) { + last = getushort(ttf); + first = getushort(ttf); + data_off = getushort(ttf); + if ( last<first || last>=0xffff || + (!allow_out_of_bounds && last>=info->glyph_cnt )) { + LogError( _("Bad lookup table: format=4 (%d/%d), first=%d last=%d total glyphs in font=%d\n"), + i,cnt,first,last,info->glyph_cnt ); + info->bad_gx = true; + } else { + here = ftell(ttf); + if ( apply_default!=NULL ) + apply_default(info,prev,first-1,def); + fseek(ttf,base+data_off,SEEK_SET); + apply_values(info,first,last,ttf); + fseek(ttf,here,SEEK_SET); + prev = last+1; + } + } + break; + case 6: /* Single table */ + /* Entry size */ getushort(ttf); + cnt = getushort(ttf); + /* search range */ getushort(ttf); + /* log2(cnt) */ getushort(ttf); + /* range shift */ getushort(ttf); + prev = 0; + for ( i=0; i<cnt; ++i ) { + first = getushort(ttf); + if ( first>=0xffff || (!allow_out_of_bounds && first>=info->glyph_cnt )) { + LogError( _("Bad lookup table: format=6, first=%d total glyphs in font=%d\n"), + first,info->glyph_cnt ); + info->bad_gx = true; + } else { + if ( apply_default!=NULL ) + apply_default(info,prev,first-1,def); + apply_value(info,first,first,ttf); + prev = first+1; + } + } + break; + case 8: /* Simple array */ + first = getushort(ttf); + cnt = getushort(ttf); + if ( first+cnt>=0xffff || (!allow_out_of_bounds && first+cnt>=info->glyph_cnt )) { + LogError( _("Bad lookup table: format=8, first=%d cnt=%d total glyphs in font=%d\n"), + first,cnt,info->glyph_cnt ); + info->bad_gx = true; + } else { + if ( apply_default!=NULL ) { + apply_default(info,0,first-1,def); + apply_default(info,first+cnt,info->glyph_cnt-1,def); + } + apply_values(info,first,first+cnt-1,ttf); + } + break; + default: + LogError( _("Invalid lookup table format. %d\n"), format ); + info->bad_gx = true; + break; + } +} + +static void OTLAppend(struct ttfinfo *info,OTLookup *otl,int gpos) { + OTLookup *prev; + int pos=0; + + if ( gpos && info->gpos_lookups == NULL ) + info->gpos_lookups = otl; + else if ( !gpos && info->gsub_lookups == NULL ) + info->gsub_lookups = otl; + else { + prev = gpos ? info->gpos_lookups : info->gsub_lookups; + pos = 1; + while ( prev->next!=NULL ) { + prev = prev->next; + ++pos; + } + prev->next = otl; + } + otl->lookup_index = pos; +} + +static void OTLRemove(struct ttfinfo *info,OTLookup *otl,int gpos) { + /* Remove the most recent lookup. We got bad data and can't use it */ + OTLookup *prev, **base; + + base = gpos ? &info->gpos_lookups : &info->gsub_lookups; + if ( *base==otl ) + *base = NULL; + else if ( *base!=NULL ) { + for ( prev = *base; prev->next!=NULL && prev->next!=otl; prev = prev->next ); + prev->next = NULL; + } + OTLookupFree(otl); +} + +static OTLookup *NewMacLookup(struct ttfinfo *info,int gpos) { + OTLookup *otl; + + otl = chunkalloc(sizeof(OTLookup)); + otl->lookup_type = gpos ? kern_statemachine : morx_context; + otl->subtables = chunkalloc(sizeof(struct lookup_subtable)); + otl->subtables->lookup = otl; + otl->features = chunkalloc(sizeof(FeatureScriptLangList)); + if ( gpos ) + otl->features->featuretag = CHR('k','e','r','n'); + else { + otl->features->featuretag = (info->mort_feat<<16) | (info->mort_setting); + otl->features->ismac = true; + } + OTLAppend(info,otl,gpos); +return( otl ); +} + +static OTLookup *NewMacSubsLookup(struct ttfinfo *info,OTLookup *parent, + int nest_index, OTLookup **subs) { + OTLookup *otl; + char *name, *format; + + if ( subs[nest_index]!=NULL ) +return( subs[nest_index] ); + + /* These are nested lookups, only to be activated by a state machine */ + /* as such they have no feature tags nor scripts of their own. */ + otl = chunkalloc(sizeof(OTLookup)); + otl->lookup_type = gsub_single; + otl->subtables = chunkalloc(sizeof(struct lookup_subtable)); + otl->subtables->lookup = otl; +/* GT: This is to give the name to a nested substitution lookup invoked by */ +/* GT: a statemachine. The %s is the name of the statemachine('s lookup) */ +/* GT: and the %d is n, where this lookup is the n'th defined for this state */ +/* GT: machine */ +#ifdef LUA_FF_LIB + format = _("%s_ns_%d"); +#else + format = _("%s nested-substitutions %d"); +#endif + name = galloc(strlen(parent->lookup_name)+strlen(format)+10); + sprintf( name, format, parent->lookup_name, nest_index ); + otl->lookup_name = name; +#ifdef LUA_FF_LIB + otl->subtables->subtable_name = strconcat(name,"_s"); +#else + otl->subtables->subtable_name = strconcat3(name," ",_("subtable")); +#endif + OTLAppend(info,otl,false); + subs[nest_index] = otl; +return( otl ); +} + +static void InfoNameOTLookup(OTLookup *otl,struct ttfinfo *info) { + SplineFont sf; + + memset(&sf,0,sizeof(sf)); + sf.features = info->features; + NameOTLookup(otl,&sf); +} + +static void TTF_SetProp(struct ttfinfo *info,int gnum, int prop) { + int offset; + PST *pst; + + if ( gnum<0 || gnum>=info->glyph_cnt ) { + LogError( _("Glyph out of bounds in 'prop' table %d\n"), gnum ); + info->bad_gx = true; +return; + } + + if ( prop&0x1000 ) { /* Mirror */ + offset = (prop<<20)>>28; + if ( gnum+offset>=0 && gnum+offset<info->glyph_cnt && + info->chars[gnum+offset]->name!=NULL ) { + pst = chunkalloc(sizeof(PST)); + pst->type = pst_substitution; + pst->subtable = info->mort_subs_lookup->subtables; + FListAppendScriptLang(info->mort_subs_lookup->features,SCScriptFromUnicode(info->chars[gnum]), + DEFAULT_LANG); + pst->next = info->chars[gnum]->possub; + info->chars[gnum]->possub = pst; + pst->u.subs.variant = copy(info->chars[gnum+offset]->name); + } + } +} + +static void prop_apply_values(struct ttfinfo *info, int gfirst, int glast,FILE *ttf) { + int i; + + for ( i=gfirst; i<=glast; ++i ) + TTF_SetProp(info,i, getushort(ttf)); +} + +static void prop_apply_value(struct ttfinfo *info, int gfirst, int glast,FILE *ttf) { + int i; + int prop; + + prop = getushort(ttf); + for ( i=gfirst; i<=glast; ++i ) + TTF_SetProp(info,i, prop); +} + +static void prop_apply_default(struct ttfinfo *info, int gfirst, int glast,void *def) { + int def_prop, i; + + def_prop = (intpt) def; + for ( i=gfirst; i<=glast; ++i ) + TTF_SetProp(info,i, def_prop); +} + +void readttfprop(FILE *ttf,struct ttfinfo *info) { + int def; + + fseek(ttf,info->prop_start,SEEK_SET); + /* The one example that I've got has a wierd version, so I don't check it */ + /* the three versions that I know about are all pretty much the same, just a few extra flags */ + /* version = */ getlong(ttf); + /* format = */ getushort(ttf); + def = getushort(ttf); + info->mort_subs_lookup = NewMacLookup(info,false); + info->mort_subs_lookup->lookup_type = gsub_single; + info->mort_subs_lookup->features->featuretag = CHR('r','t','l','a'); + info->mort_subs_lookup->features->ismac = false; + info->mort_subs_lookup->subtables->per_glyph_pst_or_kern = true; + readttf_applelookup(ttf,info, + prop_apply_values,prop_apply_value, + prop_apply_default,(void *) (intpt) def, false); + InfoNameOTLookup(info->mort_subs_lookup,info); +} + +static void TTF_SetLcaret(struct ttfinfo *info, int gnum, int offset, FILE *ttf) { + uint32 here = ftell(ttf); + PST *pst; + SplineChar *sc; + int cnt, i; + + if ( gnum<0 || gnum>=info->glyph_cnt ) { + LogError( _("Glyph out of bounds in 'lcar' table %d\n"), gnum ); + info->bad_gx = true; +return; + } else if ( (sc=info->chars[gnum])==NULL ) +return; + + fseek(ttf,info->lcar_start+offset,SEEK_SET); + cnt = getushort(ttf); + pst = chunkalloc(sizeof(PST)); + pst->type = pst_lcaret; + pst->subtable = NULL; + pst->next = sc->possub; + sc->possub = pst; + sc->lig_caret_cnt_fixed = true; + pst->u.lcaret.cnt = cnt; + pst->u.lcaret.carets = galloc(cnt*sizeof(uint16)); + for ( i=0; i<cnt; ++i ) + pst->u.lcaret.carets[i] = getushort(ttf); + fseek(ttf,here,SEEK_SET); +} + +static void lcar_apply_values(struct ttfinfo *info, int gfirst, int glast,FILE *ttf) { + int i; + + for ( i=gfirst; i<=glast; ++i ) + TTF_SetLcaret(info,i, getushort(ttf), ttf); +} + +static void lcar_apply_value(struct ttfinfo *info, int gfirst, int glast,FILE *ttf) { + int i; + int offset; + + offset = getushort(ttf); + for ( i=gfirst; i<=glast; ++i ) + TTF_SetLcaret(info,i, offset, ttf); +} + +void readttflcar(FILE *ttf,struct ttfinfo *info) { + + fseek(ttf,info->lcar_start,SEEK_SET); + /* version = */ getlong(ttf); + if ( getushort(ttf)!=0 ) /* A format type of 1 has the caret locations */ +return; /* indicated by points */ + readttf_applelookup(ttf,info, + lcar_apply_values,lcar_apply_value,NULL,NULL,false); +} + +static void TTF_SetOpticalBounds(struct ttfinfo *info, int gnum, int left, int right) { + PST *pst; + SplineChar *sc; + + if ( left==0 && right==0 ) +return; + + if ( gnum<0 || gnum>=info->glyph_cnt ) { + LogError( _("Glyph out of bounds in 'opbd' table %d\n"), gnum ); + info->bad_gx = true; +return; + } else if ( (sc=info->chars[gnum])==NULL ) +return; + + if ( left!=0 ) { + pst = chunkalloc(sizeof(PST)); + pst->type = pst_position; + pst->subtable = info->mort_subs_lookup->subtables; + FListAppendScriptLang(info->mort_subs_lookup->features,SCScriptFromUnicode(sc), + DEFAULT_LANG); + pst->next = sc->possub; + sc->possub = pst; + pst->u.pos.xoff = left; + pst->u.pos.h_adv_off = left; + } + if ( right!=0 ) { + pst = chunkalloc(sizeof(PST)); + pst->type = pst_position; + pst->subtable = info->mort_pos_lookup2->subtables; + FListAppendScriptLang(info->mort_pos_lookup2->features,SCScriptFromUnicode(sc), + DEFAULT_LANG); + pst->next = sc->possub; + sc->possub = pst; + pst->u.pos.h_adv_off = -right; + } +} + +static void opbd_apply_values(struct ttfinfo *info, int gfirst, int glast,FILE *ttf) { + int i, left, right, offset; + uint32 here; + + for ( i=gfirst; i<=glast; ++i ) { + offset = getushort(ttf); + here = ftell(ttf); + fseek(ttf,info->opbd_start+6/*opbd header*/+offset,SEEK_SET); + left = (int16) getushort(ttf); + /* top = (int16) */ getushort(ttf); + right = (int16) getushort(ttf); + /* bottom = (int16) */ getushort(ttf); + fseek(ttf,here,SEEK_SET); + TTF_SetOpticalBounds(info,i, left, right); + } +} + +static void opbd_apply_value(struct ttfinfo *info, int gfirst, int glast,FILE *ttf) { + int i, left, right, offset; + uint32 here; + + offset = getushort(ttf); + here = ftell(ttf); + fseek(ttf,info->opbd_start+offset,SEEK_SET); + left = (int16) getushort(ttf); + /* top = (int16) */ getushort(ttf); + right = (int16) getushort(ttf); + /* bottom = (int16) */ getushort(ttf); + fseek(ttf,here,SEEK_SET); + + for ( i=gfirst; i<=glast; ++i ) + TTF_SetOpticalBounds(info,i, left, right); +} + +void readttfopbd(FILE *ttf,struct ttfinfo *info) { + + fseek(ttf,info->opbd_start,SEEK_SET); + /* version = */ getlong(ttf); + if ( getushort(ttf)!=0 ) /* A format type of 1 has the bounds */ +return; /* indicated by points */ + info->mort_subs_lookup = NewMacLookup(info,true); + info->mort_subs_lookup->lookup_type = gpos_single; + info->mort_subs_lookup->features->featuretag = CHR('l','f','b','d'); + info->mort_subs_lookup->features->ismac = false; + info->mort_subs_lookup->subtables->per_glyph_pst_or_kern = true; + + info->mort_pos_lookup2 = NewMacLookup(info,true); + info->mort_pos_lookup2->lookup_type = gpos_single; + info->mort_pos_lookup2->features->featuretag = CHR('r','t','b','d'); + info->mort_pos_lookup2->features->ismac = false; + info->mort_pos_lookup2->subtables->per_glyph_pst_or_kern = true; + + readttf_applelookup(ttf,info, + opbd_apply_values,opbd_apply_value,NULL,NULL,false); + InfoNameOTLookup(info->mort_subs_lookup,info); + InfoNameOTLookup(info->mort_pos_lookup2,info); +} + +/* Interesting. The mac allows the creation of temporary gids beyond the */ +/* range specified by the font, as long as the user never sees them. So */ +/* it seems perfectly legal for one substitution to use a gid of 1111 */ +/* if that gid never reaches output but will be converted into a real gid */ +/* by a subsequent substitution. I saw this used in a conditional situation */ +/* to provide a temporary context for a later match. */ +static SplineChar *CreateBadGid(struct ttfinfo *info,int badgid) { + int i; + SplineChar *fake; + char name[60]; + + if ( badgid<0 || badgid>=0xffff ) /* <0 should never happen, 0xffff is the special "deleted" glyph, >0xffff should never happen */ +return( NULL ); + + for ( i=0; i<info->badgid_cnt; ++i ) + if ( info->badgids[i]->orig_pos == badgid ) +return( info->badgids[i] ); + + if ( info->badgid_cnt>=info->badgid_max ) + info->badgids = grealloc(info->badgids,(info->badgid_max += 20)*sizeof(SplineChar *)); + fake = SplineCharCreate(2); + fake->orig_pos = badgid; + sprintf( name, "Out-Of-Range-GID-%d", badgid ); + fake->name = copy(name); + fake->widthset = true; /* So it doesn't just vanish on us */ + fake->width = fake->vwidth = info->emsize; + info->badgids[info->badgid_cnt++] = fake; +return( fake ); +} + +static void TTF_SetMortSubs(struct ttfinfo *info, int gnum, int gsubs) { + PST *pst; + SplineChar *sc, *ssc; + + if ( gsubs==0 ) +return; + + if ( gnum<0 || gnum>=info->glyph_cnt ) { + if ( !info->warned_morx_out_of_bounds_glyph ) { + LogError( _("Glyph out of bounds in 'mort'/'morx' table %d\n"), gnum ); + info->bad_gx = true; + info->warned_morx_out_of_bounds_glyph = true; + } + sc = CreateBadGid(info,gnum); + } else + sc = info->chars[gnum]; + ssc = NULL; + if ( gsubs<0 || (gsubs>=info->glyph_cnt && gsubs!=0xffff)) { + if ( !info->warned_morx_out_of_bounds_glyph ) { + LogError( _("Substitute glyph out of bounds in 'mort'/'morx' table %d\n"), gsubs ); + info->bad_gx = true; + info->warned_morx_out_of_bounds_glyph = true; + } + ssc = CreateBadGid(info,gsubs); + } else if ( gsubs!=0xffff ) + ssc=info->chars[gsubs]; + if ( sc==NULL || (gsubs!=0xffff && ssc==NULL) ) +return; + + pst = chunkalloc(sizeof(PST)); + pst->type = pst_substitution; + pst->subtable = info->mort_subs_lookup->subtables; + if ( info->mort_subs_lookup->features!=NULL ) + FListsAppendScriptLang(info->mort_subs_lookup->features,SCScriptFromUnicode(sc), + DEFAULT_LANG); + pst->next = sc->possub; + sc->possub = pst; + pst->u.subs.variant = gsubs!=0xffff ? copy(ssc->name) : copy(MAC_DELETED_GLYPH_NAME); +} + +static void mort_apply_values(struct ttfinfo *info, int gfirst, int glast,FILE *ttf) { + uint16 gnum; + int i; + + for ( i=gfirst; i<=glast; ++i ) { + gnum = getushort(ttf); + TTF_SetMortSubs(info,i, gnum); + } +} + +static void mort_apply_value(struct ttfinfo *info, int gfirst, int glast,FILE *ttf) { + uint16 gnum; + int i; + + gnum = getushort(ttf); + + for ( i=gfirst; i<=glast; ++i ) + TTF_SetMortSubs(info,i, gnum ); +} + +static void mortclass_apply_values(struct ttfinfo *info, int gfirst, int glast,FILE *ttf) { + int i; + + for ( i=gfirst; i<=glast; ++i ) + info->morx_classes[i] = getushort(ttf); +} + +static void mortclass_apply_value(struct ttfinfo *info, int gfirst, int glast,FILE *ttf) { + uint16 class; + int i; + + class = getushort(ttf); + + for ( i=gfirst; i<=glast; ++i ) + info->morx_classes[i] = class; +} + +int32 memlong(uint8 *data,int len, int offset) { + if ( offset>=0 && offset+3<len ) { + int ch1 = data[offset], ch2 = data[offset+1], ch3 = data[offset+2], ch4 = data[offset+3]; +return( (ch1<<24)|(ch2<<16)|(ch3<<8)|ch4 ); + } else { + LogError( _("Bad font, offset out of bounds.\n") ); +return( 0 ); + } +} + +int memushort(uint8 *data,int len, int offset) { + if ( offset>=0 && offset+1<len ) { + int ch1 = data[offset], ch2 = data[offset+1]; +return( (ch1<<8)|ch2 ); + } else { + LogError( _("Bad font, offset out of bounds.\n") ); +return( 0 ); + } +} + +void memputshort(uint8 *data,int offset,uint16 val) { + data[offset] = (val>>8); + data[offset+1] = val&0xff; +} + +#define MAX_LIG_COMP 16 +struct statemachine { + uint8 *data; + int length; + uint32 nClasses; + uint32 classOffset, stateOffset, entryOffset, ligActOff, compOff, ligOff; + uint16 *classes; + uint16 lig_comp_classes[MAX_LIG_COMP]; + uint16 lig_comp_glyphs[MAX_LIG_COMP]; + int lcp; + uint8 *states_in_use; + int smax; + struct ttfinfo *info; + int cnt; +}; + +static void mort_figure_ligatures(struct statemachine *sm, int lcp, int off, int32 lig_offset, + struct ttfinfo *info) { + uint32 lig; + int i, j, lig_glyph; + PST *pst; + int len; + + if ( lcp<0 || off+3>sm->length ) +return; + + lig = memlong(sm->data,sm->length, off); + off += sizeof(int32); + + for ( i=0; i<sm->info->glyph_cnt; ++i ) if ( sm->classes[i]==sm->lig_comp_classes[lcp] ) { + sm->lig_comp_glyphs[lcp] = i; + lig_offset += memushort(sm->data,sm->length,2*( ((((int32) lig)<<2)>>2) + i ) ); + if ( lig&0xc0000000 ) { + if ( lig_offset+1 > sm->length ) { + LogError( _("Invalid ligature offset\n") ); + info->bad_gx = true; + break; + } + lig_glyph = memushort(sm->data,sm->length,lig_offset); + if ( lig_glyph>=sm->info->glyph_cnt ) { + LogError( _("Attempt to make a ligature for glyph %d out of "), + lig_glyph ); + for ( j=lcp; j<sm->lcp; ++j ) + LogError("%d ",sm->lig_comp_glyphs[j]); + LogError("\n"); + info->bad_gx = true; + } else { + char *comp; + for ( len=0, j=lcp; j<sm->lcp; ++j ) + if ( sm->lig_comp_glyphs[j]<sm->info->glyph_cnt && + sm->info->chars[sm->lig_comp_glyphs[j]]!=NULL ) + len += strlen(sm->info->chars[sm->lig_comp_glyphs[j]]->name)+1; + comp = galloc(len+1); + *comp = '\0'; + for ( j=lcp; j<sm->lcp; ++j ) { + if ( sm->lig_comp_glyphs[j]<sm->info->glyph_cnt && + sm->info->chars[sm->lig_comp_glyphs[j]]!=NULL ) { + if ( *comp!='\0' ) + strcat(comp," "); + strcat(comp,sm->info->chars[sm->lig_comp_glyphs[j]]->name); + } + } + if ( lig_glyph<sm->info->glyph_cnt && sm->info->chars[lig_glyph]!=NULL ) { + for ( pst=sm->info->chars[lig_glyph]->possub; pst!=NULL; pst=pst->next ) + if ( pst->type==pst_ligature && pst->subtable==sm->info->mort_subs_lookup->subtables && + strcmp(comp,pst->u.lig.components)==0 ) + break; + /* There are cases where there will be multiple entries for */ + /* the same lig. ie. if we have "ff" and "ffl" then there */ + /* will be multiple entries for "ff" */ + if ( pst == NULL ) { + pst = chunkalloc(sizeof(PST)); + pst->type = pst_ligature; + pst->subtable = sm->info->mort_subs_lookup->subtables; + if ( sm->info->mort_subs_lookup->features!=NULL ) + FListsAppendScriptLang(sm->info->mort_subs_lookup->features, + SCScriptFromUnicode(sm->info->chars[lig_glyph]), + DEFAULT_LANG); + pst->u.lig.components = comp; + pst->u.lig.lig = sm->info->chars[lig_glyph]; + pst->next = sm->info->chars[lig_glyph]->possub; + sm->info->chars[lig_glyph]->possub = pst; + } else + free(comp); + } else { + LogError( _("Bad font: Ligature glyph %d is missing\n"), lig_glyph ); + info->bad_gx = true; + } + } + } else + mort_figure_ligatures(sm,lcp-1,off,lig_offset,info); + lig_offset -= memushort(sm->data,sm->length,2*( ((((int32) lig)<<2)>>2) + i ) ); + } +} + +static void follow_mort_state(struct statemachine *sm,int offset,int class, + struct ttfinfo *info) { + int state = (offset-sm->stateOffset)/sm->nClasses; + int class_top, class_bottom; + + if ( state<0 || state>=sm->smax || sm->states_in_use[state] || sm->lcp>=MAX_LIG_COMP ) +return; + ++ sm->cnt; + if ( sm->cnt>=10000 ) { + if ( sm->cnt==10000 ) +/* GT: This is a reference to "Much Ado About Nothing". The string should read */ +/* GT: "A ligature sub-table in Apple's 'mort'/'morx' table is too\ncomplex for me to understand. I shall give up on it.\nYour ligatures may not be complete." */ + LogError(_("In an attempt to process the ligatures of this font, I've concluded\nthat the state machine in Apple's mort/morx table is\n(like the learned constable) too cunning to be understood.\nI shall give up on it. Your ligatures may be incomplete.\n") ); + info->bad_gx = true; +return; + } + sm->states_in_use[state] = true; + + if ( class==-1 ) { class_bottom = 0; class_top = sm->nClasses; } + else { class_bottom = class; class_top = class+1; } + for ( class=class_bottom; class<class_top; ++class ) { + int ent = sm->data[offset+class]; + int newState = memushort(sm->data,sm->length,sm->entryOffset+4*ent); + int flags = memushort(sm->data,sm->length,sm->entryOffset+4*ent+2); + /* If we have the same entry as state 0, then presumably we are */ + /* ignoring the components read so far and starting over with a new */ + /* lig (similarly for state 1) */ + if (( state!=0 && sm->data[sm->stateOffset+class] == ent ) || + (state>1 && sm->data[sm->stateOffset+sm->nClasses+class]==ent )) + continue; + if ( flags&0x8000 ) /* Set component */ + sm->lig_comp_classes[sm->lcp++] = class; + if ( flags&0x3fff ) { + mort_figure_ligatures(sm, sm->lcp-1, flags & 0x3fff, 0,info); + } else if ( flags&0x8000 ) + follow_mort_state(sm,newState,(flags&0x4000)?class:-1,info); + if ( flags&0x8000 ) + --sm->lcp; + } + sm->states_in_use[state] = false; +} + +static void morx_figure_ligatures(struct statemachine *sm, int lcp, int ligindex, int32 lig_offset, + struct ttfinfo *info) { + uint32 lig; + int i, j, lig_glyph; + PST *pst; + int len; + + if ( lcp<0 || sm->ligActOff+4*ligindex+3>(unsigned)sm->length ) +return; + + lig = memlong(sm->data,sm->length, sm->ligActOff+4*ligindex); + ++ligindex; + + for ( i=0; i<sm->info->glyph_cnt; ++i ) if ( sm->classes[i]==sm->lig_comp_classes[lcp] ) { + sm->lig_comp_glyphs[lcp] = i; + lig_offset += memushort(sm->data,sm->length,sm->compOff + 2*( ((((int32) lig)<<2)>>2) + i ) ); + if ( lig&0xc0000000 ) { + if ( sm->ligOff+2*lig_offset+1 > (unsigned)sm->length ) { + LogError( _("Invalid ligature offset\n") ); + info->bad_gx = true; + break; + } + lig_glyph = memushort(sm->data,sm->length,sm->ligOff+2*lig_offset); + if ( lig_glyph>=sm->info->glyph_cnt || sm->info->chars[lig_glyph]==NULL ) { + LogError( _("Attempt to make a ligature for (non-existent) glyph %d out of "), + lig_glyph ); + info->bad_gx = true; + for ( j=lcp; j<sm->lcp; ++j ) + LogError("%d ",sm->lig_comp_glyphs[j]); + LogError("\n"); + } else { + char *comp; + for ( len=0, j=lcp; j<sm->lcp; ++j ) + len += strlen(sm->info->chars[sm->lig_comp_glyphs[j]]->name)+1; + comp = galloc(len); + *comp = '\0'; + for ( j=lcp; j<sm->lcp; ++j ) { + if ( *comp!='\0' ) + strcat(comp," "); + strcat(comp,sm->info->chars[sm->lig_comp_glyphs[j]]->name); + } + for ( pst=sm->info->chars[lig_glyph]->possub; pst!=NULL; pst=pst->next ) + if ( pst->type==pst_ligature && pst->subtable==sm->info->mort_subs_lookup->subtables && + strcmp(comp,pst->u.lig.components)==0 ) + break; + /* There are cases where there will be multiple entries for */ + /* the same lig. ie. if we have "ff" and "ffl" then there */ + /* will be multiple entries for "ff" */ + if ( pst == NULL ) { + pst = chunkalloc(sizeof(PST)); + pst->type = pst_ligature; + pst->subtable = sm->info->mort_subs_lookup->subtables; + if ( sm->info->mort_subs_lookup->features!=NULL ) + FListsAppendScriptLang(sm->info->mort_subs_lookup->features, + SCScriptFromUnicode(sm->info->chars[lig_glyph]), + DEFAULT_LANG); + pst->u.lig.components = comp; + pst->u.lig.lig = sm->info->chars[lig_glyph]; + pst->next = sm->info->chars[lig_glyph]->possub; + sm->info->chars[lig_glyph]->possub = pst; + } else + free(comp); + } + } else + morx_figure_ligatures(sm,lcp-1,ligindex,lig_offset,info); + lig_offset -= memushort(sm->data,sm->length,sm->compOff + 2*( ((((int32) lig)<<2)>>2) + i ) ); + } +} + +static void follow_morx_state(struct statemachine *sm,int state,int class, + struct ttfinfo *info) { + int class_top, class_bottom; + + if ( state<0 || state>=sm->smax || sm->states_in_use[state] || sm->lcp>=MAX_LIG_COMP ) +return; + ++ sm->cnt; + if ( sm->cnt>=10000 ) { + if ( sm->cnt==10000 ) + LogError(_("In an attempt to process the ligatures of this font, I've concluded\nthat the state machine in Apple's mort/morx table is\n(like the learned constable) too cunning to be understood.\nI shall give up on it. Your ligatures may be incomplete.\n") ); + info->bad_gx = true; +return; + } + sm->states_in_use[state] = true; + + if ( class==-1 ) { class_bottom = 0; class_top = sm->nClasses; } + else { class_bottom = class; class_top = class+1; } + for ( class=class_bottom; class<class_top; ++class ) { + int ent = memushort(sm->data, sm->length,sm->stateOffset + 2*(state*sm->nClasses+class) ); + int newState = memushort(sm->data,sm->length,sm->entryOffset+6*ent); + int flags = memushort(sm->data,sm->length,sm->entryOffset+6*ent+2); + int ligindex = memushort(sm->data,sm->length,sm->entryOffset+6*ent+4); + /* If we have the same entry as state 0, then presumably we are */ + /* ignoring the components read so far and starting over with a new */ + /* lig (similarly for state 1) */ + if (( state!=0 && memushort(sm->data, sm->length,sm->stateOffset + 2*class) == ent ) || + (state>1 && memushort(sm->data,sm->length, sm->stateOffset + 2*(sm->nClasses+class))==ent )) + continue; + if ( flags&0x8000 ) /* Set component */ + sm->lig_comp_classes[sm->lcp++] = class; + if ( flags&0x2000 ) { + morx_figure_ligatures(sm, sm->lcp-1, ligindex, 0,info); + } else if ( flags&0x8000 ) + follow_morx_state(sm,newState,(flags&0x4000)?class:-1,info); + if ( flags&0x8000 ) + --sm->lcp; + } + sm->states_in_use[state] = false; +} + +static void readttf_mortx_lig(FILE *ttf,struct ttfinfo *info,int ismorx,uint32 base,uint32 length) { + uint32 here; + struct statemachine sm; + int first, cnt, i; + + memset(&sm,0,sizeof(sm)); + sm.info = info; + here = ftell(ttf); + length -= here-base; + sm.data = galloc(length); + sm.length = length; + if ( fread(sm.data,1,length,ttf)!=length ) { + free(sm.data); + LogError( _("Bad mort ligature table. Not long enough\n")); + info->bad_gx = true; +return; + } + fseek(ttf,here,SEEK_SET); + if ( ismorx ) { + sm.nClasses = memlong(sm.data,sm.length, 0); + sm.classOffset = memlong(sm.data,sm.length, sizeof(int32)); + sm.stateOffset = memlong(sm.data,sm.length, 2*sizeof(int32)); + sm.entryOffset = memlong(sm.data,sm.length, 3*sizeof(int32)); + sm.ligActOff = memlong(sm.data,sm.length, 4*sizeof(int32)); + sm.compOff = memlong(sm.data,sm.length, 5*sizeof(int32)); + sm.ligOff = memlong(sm.data,sm.length, 6*sizeof(int32)); + fseek(ttf,here+sm.classOffset,SEEK_SET); + /* I used only to allocate space for info->glyph_cnt entries */ + /* but some fonts use out of bounds gids as flags to contextual */ + /* morx subtables, so allocate a full 65536 */ + sm.classes = info->morx_classes = galloc(65536*sizeof(uint16)); + for ( i=0; i<65536; ++i ) + sm.classes[i] = 1; /* Out of bounds */ + readttf_applelookup(ttf,info, + mortclass_apply_values,mortclass_apply_value,NULL,NULL,true); + sm.smax = length/(2*sm.nClasses); + sm.states_in_use = gcalloc(sm.smax,sizeof(uint8)); + follow_morx_state(&sm,0,-1,info); + } else { + sm.nClasses = memushort(sm.data,sm.length, 0); + sm.classOffset = memushort(sm.data,sm.length, sizeof(uint16)); + sm.stateOffset = memushort(sm.data,sm.length, 2*sizeof(uint16)); + sm.entryOffset = memushort(sm.data,sm.length, 3*sizeof(uint16)); + sm.ligActOff = memushort(sm.data,sm.length, 4*sizeof(uint16)); + sm.compOff = memushort(sm.data,sm.length, 5*sizeof(uint16)); + sm.ligOff = memushort(sm.data,sm.length, 6*sizeof(uint16)); + sm.classes = galloc(info->glyph_cnt*sizeof(uint16)); + for ( i=0; i<info->glyph_cnt; ++i ) + sm.classes[i] = 1; /* Out of bounds */ + first = memushort(sm.data,sm.length, sm.classOffset); + cnt = memushort(sm.data,sm.length, sm.classOffset+sizeof(uint16)); + for ( i=0; i<cnt; ++i ) + sm.classes[first+i] = sm.data[sm.classOffset+2*sizeof(uint16)+i]; + sm.smax = length/sm.nClasses; + sm.states_in_use = gcalloc(sm.smax,sizeof(uint8)); + follow_mort_state(&sm,sm.stateOffset,-1,info); + } + free(sm.data); + free(sm.states_in_use); + free(sm.classes); +} + +struct statetable { + uint32 state_start; + int nclasses; + int nstates; + int nentries; + int state_offset; + int entry_size; /* size of individual entry */ + int entry_extras; /* Number of extra glyph offsets */ + int first_glyph; /* that's classifyable */ + int nglyphs; + uint8 *classes; + uint8 *state_table; /* state_table[nstates][nclasses], each entry is an */ + /* index into the following array */ + uint16 *state_table2; /* morx version. States are have 2 byte entries */ + uint16 *classes2; + uint8 *transitions; + uint32 extra_offsets[3]; +}; + +static struct statetable *read_statetable(FILE *ttf, int ent_extras, int ismorx, struct ttfinfo *info) { + struct statetable *st = gcalloc(1,sizeof(struct statetable)); + uint32 here = ftell(ttf); + int nclasses, class_off, state_off, entry_off; + int state_max, ent_max, old_state_max, old_ent_max; + int i, j, ent, new_state, ent_size; + int error; + + st->state_start = here; + + if ( ismorx ) { + nclasses = getlong(ttf); + class_off = getlong(ttf); + state_off = getlong(ttf); + entry_off = getlong(ttf); + st->extra_offsets[0] = getlong(ttf); + st->extra_offsets[1] = getlong(ttf); + st->extra_offsets[2] = getlong(ttf); + } else { + nclasses = getushort(ttf); /* Number of bytes per state in state subtable, equal to number of classes */ + class_off = getushort(ttf); + state_off = getushort(ttf); + entry_off = getushort(ttf); + st->extra_offsets[0] = getushort(ttf); + st->extra_offsets[1] = getushort(ttf); + st->extra_offsets[2] = getushort(ttf); + } + st->nclasses = nclasses; + st->state_offset = state_off; + + /* parse class subtable */ + fseek(ttf,here+class_off,SEEK_SET); + error = 0; + if ( ismorx ) { + /* I used only to allocate space for info->glyph_cnt entries */ + /* but some fonts use out of bounds gids as flags to contextual */ + /* morx subtables, so allocate a full 65536 */ + st->classes2 = info->morx_classes = galloc(65536*sizeof(uint16)); + for ( i=0; i<65536; ++i ) + st->classes2[i] = 1; /* Out of bounds */ + readttf_applelookup(ttf,info, + mortclass_apply_values,mortclass_apply_value,NULL,NULL,true); + for ( i=0; i<65536; ++i ) { + if ( /*st->classes2[i]<0 ||*/ st->classes2[i]>=st->nclasses ) { + if ( !error ) + LogError( _("Bad class in state machine.\n" )); + info->bad_gx = true; + error = true; + st->classes2[i] = 1; /* Out of bounds */ + } + } + } else { + st->first_glyph = getushort(ttf); + st->nglyphs = getushort(ttf); + if ( feof(ttf)) { + LogError(_("Bad glyph count in mort table.\n")); + info->bad_gx = true; + st->nglyphs = 0; + } + st->classes = galloc(st->nglyphs); + fread(st->classes,1,st->nglyphs,ttf); + for ( i=0; i<st->nglyphs; ++i ) { + if ( /*st->classes[i]<0 ||*/ st->classes[i]>=st->nclasses ) { + if ( !error ) + LogError( _("Bad class in state machine.\n" )); + info->bad_gx = true; + error = true; + st->classes[i] = 1; /* Out of bounds */ + } + } + } + + + /* The size of an entry is variable. There are 2 uint16 fields at the begin-*/ + /* ning of all entries. There may be some number of shorts following these*/ + /* used for indexing special tables. */ + ent_size = 4 + 2*ent_extras; + st->entry_size = ent_size; + st->entry_extras = ent_extras; + + /* Apple does not provide a way of figuring out the size of either of the */ + /* state or entry tables, so we must parse both as we go and try to work */ + /* out the maximum values... */ + /* There are always at least 2 states defined. Parse them and find what */ + /* is the biggest entry they use, then parse those entries and find what */ + /* is the biggest state they use, and then repeat until we don't find any*/ + /* more states or entries */ + old_state_max = 0; old_ent_max = 0; + state_max = 2; ent_max = 0; + while ( old_state_max!=state_max ) { + i = old_state_max*nclasses; + fseek(ttf,here+state_off+(ismorx?(int)(i*sizeof(uint16)):i),SEEK_SET); + old_state_max = state_max; + for ( ; i<state_max*nclasses; ++i ) { + ent = ismorx ? getushort(ttf) : getc(ttf); + if ( ent+1 > ent_max ) + ent_max = ent+1; + } + if ( ent_max==old_ent_max ) /* Nothing more */ + break; + if ( ent_max>1000 ) { + LogError( _("It looks to me as though there's a morx sub-table with more than 1000\n transitions. Which makes me think there's probably an error\n" )); + info->bad_gx = true; + free(st); +return( NULL ); + } + fseek(ttf,here+entry_off+old_ent_max*ent_size,SEEK_SET); + i = old_ent_max; + old_ent_max = ent_max; + for ( ; i<ent_max; ++i ) { + new_state = getushort(ttf); + if ( !ismorx ) + new_state = (new_state-state_off)/nclasses; + /* flags = */ getushort(ttf); + for ( j=0; j<ent_extras; ++j ) + /* glyphOffsets[j] = */ getushort(ttf); + if ( new_state+1>state_max ) + state_max = new_state+1; + } + if ( state_max>1000 ) { + LogError( _("It looks to me as though there's a morx sub-table with more than 1000\n states. Which makes me think there's probably an error\n" )); + info->bad_gx = true; + free(st); +return( NULL ); + } + } + + st->nstates = state_max; + st->nentries = ent_max; + + fseek(ttf,here+state_off,SEEK_SET); + /* an array of arrays of state transitions, each represented by one byte */ + /* which is an index into the Entry subtable, which comes next. */ + /* One dimension is the number of states, and the other the */ + /* number of classes (classes vary faster than states) */ + /* The first two states are predefined, 0 is start of text, 1 start of line*/ + if ( ismorx ) { + st->state_table2 = galloc(st->nstates*st->nclasses*sizeof(uint16)); + for ( i=0; i<st->nstates*st->nclasses; ++i ) + st->state_table2[i] = getushort(ttf); + } else { + st->state_table = galloc(st->nstates*st->nclasses); + fread(st->state_table,1,st->nstates*st->nclasses,ttf); + } + + /* parse the entry subtable */ + fseek(ttf,here+entry_off,SEEK_SET); + st->transitions = galloc(st->nentries*st->entry_size); + fread(st->transitions,1,st->nentries*st->entry_size,ttf); +return( st ); +} + +static void statetablefree(struct statetable *st) { + free( st->classes ); + free( st->state_table ); + free( st->classes2 ); + free( st->state_table2 ); + free( st->transitions ); + free( st ); +} + +static void tagSMWithScriptLang(FeatureScriptLangList *fl, + struct statetable *st,int ismorx,struct ttfinfo *info) { + int i; + + if ( ismorx ) { + for ( i=0; i<info->glyph_cnt; ++i ) + if ( st->classes2[i]>=4 && info->chars[i]!=NULL ) + FListsAppendScriptLang(fl,SCScriptFromUnicode(info->chars[i]), + DEFAULT_LANG); + } else { + for ( i=st->first_glyph; i<st->first_glyph+st->nglyphs && i<info->glyph_cnt; ++i ) + if ( info->chars[i]!=NULL ) + FListsAppendScriptLang(fl,SCScriptFromUnicode(info->chars[i]), + DEFAULT_LANG); + } +} + +static char **ClassesFromStateTable(struct statetable *st,int ismorx,struct ttfinfo *info) { + /* On the mac the first four classes should be left blank. only class 1 */ + /* (out of bounds) is supposed to be used in the class array anyway */ + char **classes = galloc(st->nclasses*sizeof(char *)); + int *lens = gcalloc(st->nclasses,sizeof(int)); + int i; + + + if ( ismorx ) { + for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL ) + lens[st->classes2[i]] += strlen( info->chars[i]->name )+1; + if ( info->badgids!=NULL ) + for ( i=0; i<info->badgid_cnt; ++i ) if ( info->badgids[i]!=NULL ) + lens[st->classes2[info->badgids[i]->orig_pos]] += strlen( info->badgids[i]->name )+1; + } else { + for ( i=st->first_glyph; i<st->first_glyph+st->nglyphs && i<info->glyph_cnt; ++i ) + if ( info->chars[i]!=NULL ) + lens[st->classes[i-st->first_glyph]] += strlen( info->chars[i]->name )+1; + } + classes[0] = classes[1] = classes[2] = classes[3] = NULL; + for ( i=4; i<st->nclasses; ++i ) { + classes[i] = galloc(lens[i]+1); + *classes[i] = '\0'; + } + if ( ismorx ) { + for ( i=0; i<info->glyph_cnt; ++i ) if ( st->classes2[i]>=4 && info->chars[i]!=NULL ) { + strcat(classes[st->classes2[i]],info->chars[i]->name ); + strcat(classes[st->classes2[i]]," "); + } + if ( info->badgids!=NULL ) + for ( i=0; i<info->badgid_cnt; ++i ) if ( info->badgids[i]!=NULL && st->classes2[info->badgids[i]->orig_pos]>=4) { + strcat(classes[st->classes2[info->badgids[i]->orig_pos]],info->badgids[i]->name ); + strcat(classes[st->classes2[info->badgids[i]->orig_pos]]," "); + } + } else { + for ( i=st->first_glyph; i<st->first_glyph+st->nglyphs && i<info->glyph_cnt; ++i ) if ( st->classes[i-st->first_glyph]>=4 && info->chars[i]!=NULL ) { + strcat(classes[st->classes[i-st->first_glyph]],info->chars[i]->name ); + strcat(classes[st->classes[i-st->first_glyph]]," " ); + } + } + for ( i=4; i<st->nclasses; ++i ) { + int len = strlen(classes[i]); + if ( len!=0 ) + classes[i][len-1] = '\0'; /* Remove trailing space */ + } + free(lens); +return( classes ); +} + +static char *NamesOfList(uint32 pos,int cnt, FILE *ttf, struct ttfinfo *info) { + int i, len, glyph; + char *str; + + if ( cnt==0 ) +return(NULL); + + fseek(ttf,pos,SEEK_SET); + for ( i=len=0; i<cnt; ++i ) { + glyph = getushort(ttf); + if ( glyph<info->glyph_cnt ) + len += strlen(info->chars[glyph]->name)+1; + } + if ( len==0 ) +return( NULL ); + str = galloc(len+1); + fseek(ttf,pos,SEEK_SET); + for ( i=len=0; i<cnt; ++i ) { + glyph = getushort(ttf); + if ( glyph<info->glyph_cnt ) { + strcpy(str+len,info->chars[glyph]->name); + len += strlen(info->chars[glyph]->name); + str[len++] = ' '; + } + } + str[len-1] = '\0'; +return( str ); +} + +#if 0 +static void RunStateFindKernDepth_(ASM *as,int state,int kdepth,uint8 *used) { + int j, kd; + + if ( used[state] ) +return; + used[state] = true; + + for ( j=0; j<as->class_cnt; ++j ) { + kd = kdepth; + flags = as->state[state*as->class_cnt+j]; + if ( flags&0x8000 ) + ++kd; + if ( (flags&0x3fff)!=0 ) { + as->state[state*as->class_cnt+j].u.kern.kcnt = kd; + kd = 0; + } + RunStateFindKernDepth_(as,as->state[state*as->class_cnt+j].next_state,kd,used); + } +} + +static void RunStateFindKernDepth(ASM *as) { + uint8 *used = gcalloc(as->class_cnt); + int i; + + for ( i=0; i<as->class_cnt*as->state_cnt; ++i ) { + as->state[i].u.kern.kerns = NULL; + as->state[i].u.kern.kcnt = (as->state[i].flags&0x3fff)==0 ? 0 : -1; + } + RunStateFindKernDepth_(as,0,0,used); + RunStateFindKernDepth_(as,1,0,used); +} +#endif + +static void KernReadKernList(FILE *ttf,uint32 pos, struct asm_state *trans) { +/* Apple does not document how to detect the end of the list */ +/* They say "an odd value that depends on coverage" */ +/* They should say "an odd value". Any odd value terminates the list. */ +/* coverage is irrelevant */ +/* Note: List is backwards (glyphs are popped of LIFO so last glyph on */ +/* in stack gets first kern value) */ +/* There are at most 8 glyphs */ + int i,j,k; + int16 buffer[8]; /* At most 8 kerns are supported */ + + fseek(ttf,pos,SEEK_SET); + for ( i=0; i<8; ++i ) { + buffer[i]=(int16) getushort(ttf); + if ( buffer[i]&1 ) { + buffer[i] &= ~1; + ++i; + break; + } + } + if ( i==0 ) { + trans->u.kern.kerns = NULL; + } else { + trans->u.kern.kerns = galloc(i*sizeof(int16)); + for ( j=i-1, k=0; k<i; ++k, --j ) + trans->u.kern.kerns[k] = buffer[j]; + } + trans->u.kern.kcnt = i; +} + +static void read_perglyph_subs(FILE *ttf,struct ttfinfo *info, + int subs_base,int subs_end,struct statetable *st, + uint8 *classes_subbed, int evermarked, uint8 *used) { + /* The file is positioned at the start of a per-glyph substitution table */ + /* Sadly great chunks of this table have been omitted. We are where glyph */ + /* 0 would be if it were present. We've no idea what has been omitted */ + /* Simple checks: if the file pointer is outside of the area devoted to */ + /* substitutions then we know it is ignorable. */ + /* If the current glyph is not in the list of glyphs which could ever */ + /* be substituted then we know it is ignorable. */ + /* Note: the above list is easily figured for substitutions on the current*/ + /* glyph, but if a substitution ever happens to a marked glyph then we */ + /* can't guess. We could check for all classes that get marked, but that*/ + /* doesn't work if there is a current substitution before the class is */ + /* marked, after that we don't know what class the glyph might have */ + /* Instead, for marked subs, we keep track of all locations which were */ + /* used in a current-only sub, and assume that they aren't valid for us */ + /* If the putative substitution glyph is not a valid glyph then we know */ + /* it is ignorable */ + int i, subs, was = info->mort_tag_mac; + uint32 here; + + info->mort_tag_mac = false; + for ( i=0; i<info->glyph_cnt; ++i ) { + here = ftell(ttf); + subs = getushort(ttf); + if ( subs>=info->glyph_cnt && subs!=0xffff ) /* 0xffff means delete the substituted glyph */ + continue; + if ( subs==0 ) /* A little risky, one could substitute notdef */ + continue; /* but they shouldn't */ + if ( here<(unsigned)subs_base ) + continue; + if ( here>=(unsigned)subs_end ) + break; + if ( evermarked ) { + if ( used[(here-subs_base)/2] ) + continue; + } else if ( i<st->first_glyph || i>=st->first_glyph+st->nglyphs ) { + if ( !classes_subbed[1]) { /* Out of bounds class */ + if ( i>=st->first_glyph+st->nglyphs ) + break; + continue; + } + } else { + if ( !classes_subbed[st->classes[i-st->first_glyph]] ) + continue; + } + + if ( !evermarked ) + used[(here-subs_base)/2] = true; + TTF_SetMortSubs(info, i, subs); + } + info->mort_tag_mac = was; +} + +static int sm_lookupfind(int32 *lookups,int *_lm,int off) { + int lm = *_lm, i; + for ( i=0; i<=lm; ++i ) + if ( lookups[i]==off ) +return( i ); + (*_lm)++; + lookups[i] = off; +return( i ); +} + +static ASM *readttf_mortx_asm(FILE *ttf,struct ttfinfo *info,int ismorx, + uint32 subtab_len,enum asm_type type,int extras, + uint32 coverage, OTLookup *otl) { + struct statetable *st; + ASM *as; + int i,j; + uint32 here = ftell(ttf); + + st = read_statetable(ttf,extras,ismorx,info); + if ( st==NULL ) +return(NULL); + + as = chunkalloc(sizeof(ASM)); + as->type = type; + as->flags = coverage>>16; + as->class_cnt = st->nclasses; + as->state_cnt = st->nstates; + as->classes = ClassesFromStateTable(st,ismorx,info); + as->state = galloc(st->nclasses*st->nstates*sizeof(struct asm_state)); + if ( otl==NULL ) + otl = NewMacLookup(info,false); + otl->lookup_type = type==asm_indic ? morx_indic : type==asm_context ? morx_context : morx_insert; + as->subtable = otl->subtables; otl->subtables->sm = as; + tagSMWithScriptLang(otl->features,st,ismorx,info); + InfoNameOTLookup(otl,info); + for ( i=0; i<st->nclasses*st->nstates; ++i ) { + int trans; + if ( ismorx ) { + trans = st->state_table2[i]; + as->state[i].next_state = memushort(st->transitions,st->nentries*st->entry_size,trans*st->entry_size); + } else { + trans = st->state_table[i]; + as->state[i].next_state = (memushort(st->transitions,st->nentries*st->entry_size,trans*st->entry_size)-st->state_offset)/st->nclasses; + } + as->state[i].flags = memushort(st->transitions,st->nentries*st->entry_size,trans*st->entry_size+2); + if ( extras>0 ) + as->state[i].u.context.mark_lookup = (void *) (intpt) memushort(st->transitions,st->nentries*st->entry_size, trans*st->entry_size+2+2); + if ( extras>1 ) + as->state[i].u.context.cur_lookup = (void *) (intpt) memushort(st->transitions,st->nentries*st->entry_size, trans*st->entry_size+2+2+2); + } + /* Indic tables have no attached subtables, just a verb in the flag field */ + /* so for them we are done. For the others... */ + if ( !ismorx && type==asm_insert ) { + for ( i=0; i<st->nclasses*st->nstates; ++i ) { + char *cur=NULL, *mark=NULL; + if ( (as->state[i].flags&0x3e0)!=0 && as->state[i].u.context.mark_lookup!=NULL ) { + cur = NamesOfList(here+(intpt) as->state[i].u.context.mark_lookup, + (as->state[i].flags&0x3e0)>>5,ttf,info); + } + if ( (as->state[i].flags&0x01f)!=0 && as->state[i].u.context.cur_lookup!=NULL ) { + mark = NamesOfList(here+(intpt) as->state[i].u.context.cur_lookup, + as->state[i].flags&0x01f,ttf,info); + } + as->state[i].u.insert.cur_ins=cur; + as->state[i].u.insert.mark_ins=mark; + } + } else if ( ismorx && type == asm_insert ) { + for ( i=0; i<st->nclasses*st->nstates; ++i ) { + char *cur=NULL, *mark=NULL; + if ( (as->state[i].flags&0x3e0)!=0 && (intpt) as->state[i].u.context.mark_lookup!=0xffff ) { + cur = NamesOfList(here+st->extra_offsets[0]+((intpt) as->state[i].u.context.mark_lookup)*2, + (as->state[i].flags&0x3e0)>>5,ttf,info); + } + if ( (as->state[i].flags&0x01f)!=0 && ((intpt) as->state[i].u.context.cur_lookup)!=0xffff ) { + mark = NamesOfList(here+st->extra_offsets[0]+((intpt) as->state[i].u.context.cur_lookup)*2, + as->state[i].flags&0x01f,ttf,info); + } + as->state[i].u.insert.cur_ins=cur; + as->state[i].u.insert.mark_ins=mark; + } + } else if ( !ismorx && type == asm_context ) { + /* I don't see any good way to parse a per-glyph substitution table */ + /* the problem being that most of the per-glyph table is missing */ + /* but I don't know which bits. The offsets I'm given point to */ + /* where glyph 0 would be if it were present in the table, but */ + /* mostly only the bit used is present... */ + /* I could walk though the state machine and find all classes that */ + /* go to a specific substitution (which would tell me what glyphs */ + /* were active). That's not hard for substitutions of the current */ + /* glyph, but it is intractibable for marked glyphs. And I can't */ + /* do one without the other. So I do neither. */ + /* One thing I could test fairly easily would be to see whether */ + /* class 1 (out of bounds) is ever available for a substitution */ + /* (if it ever has a mark set on it or has a current substitution)*/ + /* if not, then I can ignore any putative substitutions for class */ + /* 1 glyphs (actually I should do this for all classes)*/ + /* Damn. That doesn't work for marks. Because a current substitution*/ + /* may be applied, and then the glyph gets marked. So we've no idea*/ + /* what class the newly marked glyph might live in */ + /* Apple's docs say the substitutions are offset from the "state */ + /* subtable", but it seems much more likely that they are offset */ + /* from the substitution table (given that Apple's docs are often */ + /* wrong */ /* Apple's docs are right. not clear why that offset */ + /* is there */ + uint8 *classes_subbed = gcalloc(st->nclasses,1); + int lookup_max = -1, index, index_max; + int32 *lookups = galloc(st->nclasses*st->nstates*sizeof(int32)); + uint8 *evermarked = gcalloc(st->nclasses*st->nstates,sizeof(uint8)); + uint8 *used; + OTLookup **subs; + + index_max = 0; + for ( i=0; i<st->nclasses*st->nstates; ++i ) { + if ( as->state[i].u.context.mark_lookup!=NULL ) { + index = sm_lookupfind(lookups,&lookup_max,(int16) (intpt) as->state[i].u.context.mark_lookup); + if ( index>index_max ) index_max = index; + } + if ( as->state[i].u.context.cur_lookup!=NULL ) { + index = sm_lookupfind(lookups,&lookup_max,(int16) (intpt) as->state[i].u.context.cur_lookup); + if ( index>index_max ) index_max = index; + } + } + subs = gcalloc(index_max+1,sizeof(OTLookup *)); + + for ( i=0; i<st->nclasses*st->nstates; ++i ) { + if ( as->state[i].u.context.mark_lookup!=NULL ) { + index = sm_lookupfind(lookups,&lookup_max,(int16) (intpt) as->state[i].u.context.mark_lookup); + evermarked[index] = true; + as->state[i].u.context.mark_lookup = NewMacSubsLookup(info,otl,index,subs); + } + if ( as->state[i].u.context.cur_lookup!=NULL ) { + index = sm_lookupfind(lookups,&lookup_max,(int16) (intpt) as->state[i].u.context.cur_lookup); + as->state[i].u.context.cur_lookup = NewMacSubsLookup(info,otl,index,subs); + } + } + used = gcalloc((subtab_len-st->extra_offsets[0]+1)/2,sizeof(uint8)); + /* first figure things that only appear in current subs */ + /* then go back and work on things that apply to things which are also in marked subs */ + for ( j=0; j<2; ++j ) for ( i=0; i<=lookup_max; ++i ) if ( evermarked[i]==j ) { + info->mort_subs_lookup = NewMacSubsLookup(info,otl,i,subs); + info->mort_is_nested = true; + if ( !evermarked[i] ) { int k,l; + memset(classes_subbed,0,st->nclasses); + for ( k=0; k<st->nstates; ++k ) for ( l=0; l<st->nclasses; ++l ) { + if ( as->state[k*st->nclasses+l].u.context.cur_lookup == info->mort_subs_lookup ) + classes_subbed[l] = true; + } + } + fseek(ttf,here/*+st->extra_offsets[0]*/+lookups[i]*2,SEEK_SET); + read_perglyph_subs(ttf,info,here+st->extra_offsets[0],here+subtab_len, + st,classes_subbed,evermarked[i],used); + } + info->mort_is_nested = false; + free(classes_subbed); + free(lookups); + free(used); + free(evermarked); + free(subs); + } else if ( ismorx && type == asm_context ) { + int lookup_max= -1; + uint32 *lookups; + OTLookup **subs; + + for ( i=0; i<st->nclasses*st->nstates; ++i ) { + if ( (intpt) as->state[i].u.context.mark_lookup!=0xffff ) { + if ( ((int) (intpt) as->state[i].u.context.mark_lookup)>lookup_max ) + lookup_max = (intpt) as->state[i].u.context.mark_lookup; + } + if ( (intpt) as->state[i].u.context.cur_lookup!=0xffff ) { + if ( ((int) (intpt) as->state[i].u.context.cur_lookup)>lookup_max ) + lookup_max = (intpt) as->state[i].u.context.cur_lookup; + } + } + ++lookup_max; + subs = gcalloc(lookup_max,sizeof(OTLookup *)); + for ( i=0; i<st->nclasses*st->nstates; ++i ) { + if ( (intpt) as->state[i].u.context.mark_lookup!=0xffff ) { + as->state[i].u.context.mark_lookup = NewMacSubsLookup(info,otl,(intpt) as->state[i].u.context.mark_lookup,subs); + } else + as->state[i].u.context.mark_lookup = NULL; + if ( (intpt) as->state[i].u.context.cur_lookup!=0xffff ) { + as->state[i].u.context.cur_lookup = NewMacSubsLookup(info,otl,(intpt) as->state[i].u.context.cur_lookup,subs); + } else + as->state[i].u.context.cur_lookup = NULL; + } + lookups = galloc(lookup_max*sizeof(uint32)); + fseek(ttf,here+st->extra_offsets[0],SEEK_SET); + for ( i=0; i<lookup_max; ++i ) + lookups[i] = getlong(ttf) + here+st->extra_offsets[0]; + for ( i=0; i<lookup_max; ++i ) { + fseek(ttf,lookups[i],SEEK_SET); + info->mort_subs_lookup = NewMacSubsLookup(info,otl,i,subs); + info->mort_is_nested = true; + readttf_applelookup(ttf,info, + mort_apply_values,mort_apply_value,NULL,NULL,true); + } + info->mort_is_nested = false; + free(subs); + free(lookups); + } else if ( type == asm_kern ) { + for ( i=0; i<st->nclasses*st->nstates; ++i ) { + if ( (as->state[i].flags&0x3fff)!=0 ) { + KernReadKernList(ttf,here+(as->state[i].flags&0x3fff), + &as->state[i]); + as->state[i].flags &= ~0x3fff; + } else { + as->state[i].u.kern.kcnt = 0; + as->state[i].u.kern.kerns = NULL; + } + } + } + as->next = info->sm; + info->sm = as; + statetablefree(st); +return( as ); +} + +static int InfoHasGSUBTag(struct ttfinfo *info, uint32 tag, int apple_lookup_type) { + OTLookup *otl; + FeatureScriptLangList *feat; + + if ( apple_lookup_type==0 || /* Indic rearrangement */ + apple_lookup_type==1 || /* Contextual substitution */ + apple_lookup_type==5 ) /* Contextual insertion */ +return( false ); /* These types can either not be represented in OT */ + /* or not converted to AAT -- so we'd better read them */ + /* We can't really do contextual ligatures either, but we parse that table*/ + /* for the non-contextual ligs (which is most of them) */ + + for ( otl = info->gsub_lookups; otl!=NULL; otl=otl->next ) { + for ( feat = otl->features; feat!=NULL; feat=feat->next ) { + if ( feat->featuretag == tag && + Macable(NULL,otl)) +return( true ); + } + } +return( false ); +} + +static void FeatMarkAsEnabled(struct ttfinfo *info,int featureType, + int featureSetting); + +static uint32 readmortchain(FILE *ttf,struct ttfinfo *info, uint32 base, int ismorx) { + uint32 chain_len, nfeatures, nsubtables, default_flags; + uint32 enable_flags, disable_flags, flags; + int featureType, featureSetting; + int i,j,k; + uint32 length, coverage; + uint32 here; + uint32 tag; + struct tagmaskfeature { uint32 tag, enable_flags; uint16 ismac, feat, set; } tmf[32]; + int r2l; + (void)base; /* for -Wall */ + default_flags = getlong(ttf); + chain_len = getlong(ttf); + if ( ismorx ) { + nfeatures = getlong(ttf); + nsubtables = getlong(ttf); + } else { + nfeatures = getushort(ttf); + nsubtables = getushort(ttf); + } + + k = 0; + for ( i=0; i<(int)nfeatures; ++i ) { + featureType = getushort(ttf); + featureSetting = getushort(ttf); + enable_flags = getlong(ttf); + disable_flags = getlong(ttf); + if ( feof(ttf)) +return( chain_len ); + if ( enable_flags & default_flags ) + FeatMarkAsEnabled(info,featureType,featureSetting); + tag = MacFeatureToOTTag(featureType,featureSetting); + if ( enable_flags!=0 && k<32 ) { + if ( tag==0 ) { + tmf[k].tag = (featureType<<16) | featureSetting; + tmf[k].ismac = true; + } else { + tmf[k].tag = tag; + tmf[k].ismac = false; + } + tmf[k].feat = featureType; + tmf[k].set = featureSetting; + tmf[k++].enable_flags = enable_flags; + } + } + if ( k==0 ) +return( chain_len ); + + for ( i=0; i<(int)nsubtables; ++i ) { + here = ftell(ttf); + if ( ismorx ) { + length = getlong(ttf); + coverage = getlong(ttf); + } else { + length = getushort(ttf); + coverage = getushort(ttf); + coverage = ((coverage&0xe000)<<16) | (coverage&7); /* convert to morx format */ + } + r2l = (coverage & 0x40000000)? 1 : 0; + flags = getlong(ttf); + for ( j=k-1; j>=0 && (!(flags&tmf[j].enable_flags) || (tmf[j].enable_flags&~flags)!=0); --j ); + if ( j==-1 ) + for ( j=k-1; j>=0 && (!(flags&tmf[j].enable_flags) || tmf[j].feat==0); --j ); + if ( j>=0 ) { + if ( !tmf[j].ismac && + ((coverage&0xff)==0 || + (coverage&0xff)==1 || + (coverage&0xff)==5 )) { + /* Only do the opentype tag conversion if we've got a format */ + /* we can convert to opentype. Otherwise it is useless and */ + /* confusing */ + tmf[j].ismac = true; + tmf[j].tag = (tmf[j].feat<<16) | tmf[j].set; + } + info->mort_r2l = r2l; + info->mort_tag_mac = tmf[j].ismac; + info->mort_feat = tmf[j].feat; info->mort_setting = tmf[j].set; + /* If we've already read gsub. And this feature setting matches */ + /* and opentype feature tag. And we've got an OpenType lookup */ + /* attached to that feature tag. And it's a lookup type we can */ + /* convert... Then don't parse this sub-table, we shall assume */ + /* it is simply a duplicate of the OT version */ + if ( info->gsub_start!=0 && + (tag = MacFeatureToOTTag(tmf[j].feat,tmf[j].set))!=0 && + InfoHasGSUBTag(info,tag,coverage&0xff)) + /* Skip it */; + else switch( coverage&0xff ) { + case 0: /* Indic rearangement */ + readttf_mortx_asm(ttf,info,ismorx,length,asm_indic,0, + coverage,NULL); + break; + case 1: /* contextual glyph substitution */ + readttf_mortx_asm(ttf,info,ismorx,length,asm_context,2, + coverage,NULL); + break; + case 2: /* ligature substitution */ + /* Apple's ligature state machines are too weird to be */ + /* represented easily, but I can parse them into a set */ + /* of ligatures -- assuming they are unconditional */ + info->mort_subs_lookup = NewMacLookup(info,false); + info->mort_subs_lookup->lookup_type = gsub_ligature; + if ( !tmf[j].ismac ) { + info->mort_subs_lookup->features->next = chunkalloc(sizeof(FeatureScriptLangList)); + info->mort_subs_lookup->features->next->featuretag = tmf[j].tag; + info->mort_subs_lookup->features->next->ismac = false; + } + info->mort_subs_lookup->subtables->per_glyph_pst_or_kern = true; + readttf_mortx_lig(ttf,info,ismorx,here,length); + InfoNameOTLookup(info->mort_subs_lookup,info); + /* We can give the lookup a better name after we've made a */ + /* guess at what scripts it involves => substitutions first */ + break; + case 4: /* non-contextual glyph substitutions */ + info->mort_subs_lookup = NewMacLookup(info,false); + info->mort_subs_lookup->lookup_type = gsub_single; + if ( !tmf[j].ismac ) { + info->mort_subs_lookup->features->next = chunkalloc(sizeof(FeatureScriptLangList)); + info->mort_subs_lookup->features->next->featuretag = tmf[j].tag; + info->mort_subs_lookup->features->next->ismac = false; + } + info->mort_subs_lookup->subtables->per_glyph_pst_or_kern = true; + readttf_applelookup(ttf,info, + mort_apply_values,mort_apply_value,NULL,NULL,true); + InfoNameOTLookup(info->mort_subs_lookup,info); + break; + case 5: /* contextual glyph insertion */ + readttf_mortx_asm(ttf,info,ismorx,length,asm_insert,2, + coverage,NULL); + break; + } + } + fseek(ttf, here+length, SEEK_SET ); + } + +return( chain_len ); +} + +void readttfmort(FILE *ttf,struct ttfinfo *info) { + uint32 base = info->morx_start!=0 ? info->morx_start : info->mort_start; + uint32 here, len; + int ismorx; + int32 version; + int i, nchains; + + fseek(ttf,base,SEEK_SET); + version = getlong(ttf); + ismorx = version == 0x00020000; + if ( version!=0x00010000 && version != 0x00020000 ) +return; + nchains = getlong(ttf); + if ( feof(ttf)) { + LogError( _("Unexpected end of file found in morx chain.\n" )); + info->bad_gx = true; +return; + } + info->mort_max = nchains*33; /* Maximum of one feature per bit ? */ + for ( i=0; i<nchains; ++i ) { + here = ftell(ttf); + len = readmortchain(ttf,info,base,ismorx); + if ( feof(ttf)) { + LogError( _("Unexpected end of file found in morx chain.\n")); + info->bad_gx = true; + break; + } + fseek(ttf,here+len,SEEK_SET); + } + /* Some Apple fonts use out of range gids as flags in conditional substitutions */ + /* generally to pass information from one sub-table to another which then */ + /* removes the flag */ + if ( info->badgid_cnt!=0 ) { + /* Merge the fake glyphs in with the real ones */ + info->chars = grealloc(info->chars,(info->glyph_cnt+info->badgid_cnt)*sizeof(SplineChar *)); + for ( i=0; i<info->badgid_cnt; ++i ) { + info->chars[info->glyph_cnt+i] = info->badgids[i]; + info->badgids[i]->orig_pos = info->glyph_cnt+i; + } + info->glyph_cnt += info->badgid_cnt; + free(info->badgids); + } +} + +/* Apple's docs imply that kerning info is always provided left to right, even*/ +/* for right to left scripts. My guess is that their docs are wrong, as they */ +/* often are, but if that be so then we need code in here to reverse */ +/* the order of the characters for right to left since pfaedit's convention */ +/* is to follow writing order rather than to go left to right */ +void readttfkerns(FILE *ttf,struct ttfinfo *info) { + int tabcnt, len, coverage,i,j, npairs, version, format, flags_good, tab; + int left, right, offset, array, rowWidth; + int header_size; + KernPair *kp; + KernClass *kc; + uint32 begin_table; + uint16 *class1, *class2; + int tupleIndex; + int isv; + SplineChar **chars; + OTLookup *otl; + + fseek(ttf,info->kern_start,SEEK_SET); + version = getushort(ttf); + tabcnt = getushort(ttf); + if ( version!=0 ) { + fseek(ttf,info->kern_start,SEEK_SET); + version = getlong(ttf); + tabcnt = getlong(ttf); + } + for ( tab=0; tab<tabcnt; ++tab ) { + begin_table = ftell(ttf); + if ( version==0 ) { + /* version = */ getushort(ttf); + len = getushort(ttf); + coverage = getushort(ttf); + format = coverage>>8; + flags_good = ((coverage&7)<=1); + isv = !(coverage&1); + tupleIndex = -1; + header_size = 6; + } else { + len = getlong(ttf); + coverage = getushort(ttf); + /* Apple has reordered the bits */ + format = (coverage&0xff); + flags_good = ((coverage&0xdf00)==0 || (coverage&0xdf00)==0x8000); + isv = coverage&0x8000? 1 : 0; + tupleIndex = getushort(ttf); + if ( coverage&0x2000 ) { + if ( info->variations==NULL ) + flags_good = false; /* Ignore if we failed to load the tuple data */ + else if ( tupleIndex>=info->variations->tuple_count ) + flags_good = false; /* Bad tuple */ + } else + tupleIndex = -1; + header_size = 8; + } + otl = NULL; + if ( flags_good ) { + otl = NewMacLookup(info,true); + otl->lookup_type = gpos_pair; + if ( isv ) { + otl->features->featuretag = CHR('v','k','r','n'); + otl->features->ismac = false; + } + otl->subtables->per_glyph_pst_or_kern = true; + otl->subtables->vertical_kerning = isv; + } + if ( flags_good && format==0 ) { + /* format 0, horizontal kerning data (as pairs) not perpendicular */ + chars = tupleIndex==-1 ? info->chars : info->variations->tuples[tupleIndex].chars; + npairs = getushort(ttf); + if ( version==0 && (len-14 != 6*npairs || npairs>10920 )) { + LogError( _("In the 'kern' table, a subtable's length does not match the number of kerning pairs.") ); + info->bad_gx = true; + } + /* searchRange = */ getushort(ttf); + /* entrySelector = */ getushort(ttf); + /* rangeShift = */ getushort(ttf); + otl->subtables[0].per_glyph_pst_or_kern = true; + for ( j=0; j<npairs; ++j ) { + left = getushort(ttf); + right = getushort(ttf); + offset = (short) getushort(ttf); + if ( left<info->glyph_cnt && right<info->glyph_cnt && + chars[left]!=NULL && chars[right]!=NULL ) { + kp = chunkalloc(sizeof(KernPair)); + kp->sc = chars[right]; + kp->off = offset; + kp->subtable = otl->subtables; + FListsAppendScriptLang(otl->features,SCScriptFromUnicode(chars[left]), + DEFAULT_LANG); + if ( isv ) { + kp->next = chars[left]->vkerns; + chars[left]->vkerns = kp; + } else { + kp->next = chars[left]->kerns; + chars[left]->kerns = kp; + } + } else if ( left>=info->glyph_cnt || right>=info->glyph_cnt ) { + /* Holes happen when reading ttc files. They are probably ok */ + LogError( _("Bad kern pair: glyphs %d & %d must be less than %d\n"), + left, right, info->glyph_cnt ); + info->bad_gx = true; + } + } + InfoNameOTLookup(otl,info); + } else if ( flags_good && format==1 ) { + /* format 1 is an apple state machine which can handle weird cases */ + /* OpenType's spec doesn't document this */ + /* Apple's docs are wrong about this table, they claim */ + /* there is a special value which marks the end of the kerning */ + /* lists. In fact there is no such value, the list is as long */ + /* as there are things on the kern stack */ + otl->lookup_type = kern_statemachine; + readttf_mortx_asm(ttf,info,false,len-header_size,asm_kern,0, + isv ? 0x80000000 : 0 /* coverage doesn't really apply */,otl); + fseek(ttf,begin_table+len,SEEK_SET); + } else if ( flags_good && (format==2 || format==3 )) { + /* two class based formats */ + KernClass **khead, **klast; + if ( isv && tupleIndex==-1 ) { + khead = &info->vkhead; + klast = &info->vklast; + } else if ( tupleIndex==-1 ) { + khead = &info->khead; + klast = &info->klast; + } else if ( isv ) { + khead = &info->variations->tuples[tupleIndex].vkhead; + klast = &info->variations->tuples[tupleIndex].vklast; + } else { + khead = &info->variations->tuples[tupleIndex].khead; + klast = &info->variations->tuples[tupleIndex].klast; + } + if ( *khead==NULL ) + *khead = kc = chunkalloc(sizeof(KernClass)); + else + kc = (*klast)->next = chunkalloc(sizeof(KernClass)); + *klast = kc; + if ( format==2 ) { + rowWidth = getushort(ttf); + left = getushort(ttf); + right = getushort(ttf); + array = getushort(ttf); + kc->second_cnt = rowWidth/sizeof(uint16); + class1 = getAppleClassTable(ttf, begin_table+left, info->glyph_cnt, array, rowWidth, info ); + class2 = getAppleClassTable(ttf, begin_table+right, info->glyph_cnt, 0, sizeof(uint16), info ); + for ( i=0; i<info->glyph_cnt; ++i ) + if ( class1[i]>kc->first_cnt ) + kc->first_cnt = class1[i]; + ++ kc->first_cnt; + kc->offsets = galloc(kc->first_cnt*kc->second_cnt*sizeof(int16)); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + kc->adjusts = gcalloc(kc->first_cnt*kc->second_cnt,sizeof(DeviceTable)); +#endif + fseek(ttf,begin_table+array,SEEK_SET); + for ( i=0; i<kc->first_cnt*kc->second_cnt; ++i ) + kc->offsets[i] = getushort(ttf); + } else { + /* format 3, horizontal kerning data (as classes limited to 256 entries) */ + /* OpenType's spec doesn't document this */ + int gc, kv, flags; + int16 *kvs; + gc = getushort(ttf); + kv = getc(ttf); + kc->first_cnt = getc(ttf); + kc->second_cnt = getc(ttf); + flags = getc(ttf); + if ( gc>info->glyph_cnt ) { + LogError( _("Kerning subtable 3 says the glyph count is %d, but maxp says %d\n"), + gc, info->glyph_cnt ); + info->bad_gx = true; + } + class1 = gcalloc(gc>info->glyph_cnt?gc:info->glyph_cnt,sizeof(uint16)); + class2 = gcalloc(gc>info->glyph_cnt?gc:info->glyph_cnt,sizeof(uint16)); + kvs = galloc(kv*sizeof(int16)); + kc->offsets = galloc(kc->first_cnt*kc->second_cnt*sizeof(int16)); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + kc->adjusts = gcalloc(kc->first_cnt*kc->second_cnt,sizeof(DeviceTable)); +#endif + for ( i=0; i<kv; ++i ) + kvs[i] = (int16) getushort(ttf); + for ( i=0; i<gc; ++i ) + class1[i] = getc(ttf); + for ( i=0; i<gc; ++i ) + class2[i] = getc(ttf); + for ( i=0; i<kc->first_cnt*kc->second_cnt; ++i ) + kc->offsets[i] = kvs[getc(ttf)]; + free(kvs); + } + kc->firsts = ClassToNames(info,kc->first_cnt,class1,info->glyph_cnt); + kc->seconds = ClassToNames(info,kc->second_cnt,class2,info->glyph_cnt); + for ( i=0; i<info->glyph_cnt; ++i ) { + if ( class1[i]>=4 && info->chars[i]!=NULL ) + FListsAppendScriptLang(otl->features, + SCScriptFromUnicode(info->chars[i]), + DEFAULT_LANG); + } + free(class1); free(class2); + fseek(ttf,begin_table+len,SEEK_SET); + otl->subtables[0].kc = kc; + kc->subtable = otl->subtables; + InfoNameOTLookup(otl,info); + } else { + LogError(_("Invalid or unsupported format (%d) for subtable of 'kern' table"), format ); + info->bad_gx = true; + fseek(ttf,len-header_size,SEEK_CUR); + if ( otl!=NULL ) + OTLRemove(info,otl,true); + } + } +} + +void readmacfeaturemap(FILE *ttf,struct ttfinfo *info) { + MacFeat *last=NULL, *cur; + struct macsetting *slast, *scur; + struct fs { int n; int off; } *fs; + int featcnt, i, j, flags; + + fseek(ttf,info->feat_start,SEEK_SET); + /* version =*/ getfixed(ttf); + featcnt = getushort(ttf); + /* reserved */ getushort(ttf); + /* reserved */ getlong(ttf); + if ( feof(ttf)) { + LogError( _("End of file in feat table.\n" )); + info->bad_gx = true; +return; + } + + fs = galloc(featcnt*sizeof(struct fs)); + for ( i=0; i<featcnt; ++i ) { + cur = chunkalloc(sizeof(MacFeat)); + if ( last==NULL ) + info->features = cur; + else + last->next = cur; + last = cur; + + cur->feature = getushort(ttf); + fs[i].n = getushort(ttf); + fs[i].off = getlong(ttf); + flags = getushort(ttf); + cur->strid = getushort(ttf); + if ( flags&0x8000 ) cur->ismutex = true; + if ( flags&0x4000 ) + cur->default_setting = flags&0xff; + if ( feof(ttf)) { + free(fs); + LogError( _("End of file in feat table.\n" )); + info->bad_gx = true; +return; + } + } + + for ( i=0, cur=info->features; i<featcnt; ++i, cur = cur->next ) { + fseek(ttf,info->feat_start+fs[i].off,SEEK_SET); + slast = NULL; + for ( j=0; j<fs[i].n; ++j ) { + scur = chunkalloc(sizeof(struct macsetting)); + if ( slast==NULL ) + cur->settings = scur; + else + slast->next = scur; + slast = scur; + + scur->setting = getushort(ttf); + scur->strid = getushort(ttf); + if ( feof(ttf)) { + free(fs); + LogError( _("End of file in feat table.\n") ); + info->bad_gx = true; +return; + } + } + } + free(fs); +} + +static void FeatMarkAsEnabled(struct ttfinfo *info,int featureType, + int featureSetting) { + MacFeat *f; + struct macsetting *s; + + for ( f = info->features; f!=NULL && f->feature!=featureType; f=f->next ); + if ( f==NULL ) +return; + if ( f->ismutex ) { + for ( s=f->settings ; s!=NULL; s=s->next ) + s->initially_enabled = ( s->setting==featureSetting ); + f->default_setting = featureSetting; + } else { + for ( s=f->settings ; s!=NULL && s->setting!=featureSetting; s=s->next ); + if ( s!=NULL ) + s->initially_enabled = true; + } +return; +} + +/******************************************************************************/ +/* ******************************* MATH Table ******************************* */ +/* ********************** (Not strictly OpenType yet) *********************** */ +/******************************************************************************/ + +/* ******************************** Read MATH ******************************* */ + +static void ttf_math_read_constants(FILE *ttf,struct ttfinfo *info, uint32 start) { + struct MATH *math; + int i; +#ifdef FONTFORGE_CONFIG_DEVICETABLES + uint16 off; +#endif + + fseek(ttf,start,SEEK_SET); + info->math = math = gcalloc(1,sizeof(struct MATH)); + + for ( i=0; math_constants_descriptor[i].script_name!=NULL; ++i ) { + int16 *pos = (int16 *) (((char *) (math)) + math_constants_descriptor[i].offset ); + if ( pos == (int16 *) &math->MinConnectorOverlap ) + continue; /* Actually lives in the Variant table, not here */ + *pos = getushort(ttf); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + if ( math_constants_descriptor[i].devtab_offset >= 0 ) { + DeviceTable **devtab = (DeviceTable **) (((char *) (math)) + math_constants_descriptor[i].devtab_offset ); + off = getushort(ttf); + if ( off!=0 ) { + *devtab = chunkalloc(sizeof(DeviceTable)); + ReadDeviceTable(ttf,*devtab,start+off,info); + } + } +#else + /* No support for device tables, skip it */ + if ( math_constants_descriptor[i].devtab_offset != -1 ) + (void) getushort(ttf); +#endif + } +} + +static void ttf_math_read_icta(FILE *ttf,struct ttfinfo *info, uint32 start, int is_ic) { + /* The italic correction and top accent sub-tables have the same format */ + int coverage, cnt, i, val, offset; + uint16 *glyphs; + + fseek(ttf,start,SEEK_SET); + coverage = getushort(ttf); + cnt = getushort(ttf); + glyphs = getCoverageTable(ttf,start+coverage,info); + if ( glyphs==NULL ) +return; + fseek(ttf,start+4,SEEK_SET); + for ( i=0; i<cnt; ++i ) if ( glyphs[i]<info->glyph_cnt && info->chars[ glyphs[i]]!=NULL ) { + val = (int16) getushort(ttf); + if ( is_ic ) + info->chars[ glyphs[i] ]->italic_correction = val; + else + info->chars[ glyphs[i] ]->top_accent_horiz = val; + offset = getushort(ttf); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + if ( offset!=0 ) { + DeviceTable *dv = chunkalloc(sizeof(DeviceTable)); + ReadDeviceTable(ttf,dv,start+offset,info); + if ( is_ic ) + info->chars[ glyphs[i] ]->italic_adjusts = dv; + else + info->chars[ glyphs[i] ]->top_accent_adjusts = dv; + } +#endif + } + free(glyphs); +} + +static void ttf_math_read_extended(FILE *ttf,struct ttfinfo *info, uint32 start) { + int i; + uint16 *glyphs; + + glyphs = getCoverageTable(ttf,start,info); + if ( glyphs==NULL ) +return; + for ( i=0; glyphs[i]!=0xffff; ++i ) if ( glyphs[i]<info->glyph_cnt && info->chars[ glyphs[i]]!=NULL ) + info->chars[ glyphs[i] ]->is_extended_shape = true; + free(glyphs); +} + +static void ttf_math_read_mathkernv(FILE *ttf, uint32 start,struct mathkernvertex *mkv, + SplineChar *sc, int istop, struct ttfinfo *info) { + int cnt, i; + (void)info; /* for -Wall */ + fseek(ttf,start,SEEK_SET); + /* There is one more width than height. I store the width count */ + /* and guess a dummy height later */ + mkv->cnt = cnt = getushort(ttf)+1; + mkv->mkd = gcalloc(cnt,sizeof(struct mathkerndata)); + + for ( i=0; i<cnt-1; ++i ) { + mkv->mkd[i].height = getushort(ttf); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + mkv->mkd[i].height_adjusts = (void *) (intpt) getushort(ttf); +#else + (void) getushort(ttf); +#endif + } + + for ( i=0; i<cnt; ++i ) { + mkv->mkd[i].kern = getushort(ttf); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + mkv->mkd[i].kern_adjusts = (void *) (intpt) getushort(ttf); +#else + (void) getushort(ttf); +#endif + } + +#ifdef FONTFORGE_CONFIG_DEVICETABLES + for ( i=0; i<cnt; ++i ) { + DeviceTable *dv; + uint32 offset; + if ( mkv->mkd[i].height_adjusts!=NULL ) { + offset = start + (intpt) mkv->mkd[i].height_adjusts; + mkv->mkd[i].height_adjusts = dv = chunkalloc(sizeof(DeviceTable)); + ReadDeviceTable(ttf,dv,offset,info); + } + if ( mkv->mkd[i].kern_adjusts!=NULL ) { + offset = start + (intpt) mkv->mkd[i].kern_adjusts; + mkv->mkd[i].kern_adjusts = dv = chunkalloc(sizeof(DeviceTable)); + ReadDeviceTable(ttf,dv,offset,info); + } + } +#endif + + if ( cnt>=3 ) + mkv->mkd[cnt-1].height = 2*mkv->mkd[cnt-2].height - mkv->mkd[cnt-3].height; + else if ( cnt>=2 ) + mkv->mkd[cnt-1].height = mkv->mkd[cnt-2].height + 100; + else if ( cnt==1 ) { + if ( istop ) { + DBounds b; + SplineCharQuickBounds(sc,&b); + mkv->mkd[cnt-1].height = b.maxy; + } else + mkv->mkd[cnt-1].height = 0; + } +} + +static void ttf_math_read_mathkern(FILE *ttf,struct ttfinfo *info, uint32 start) { + int coverage, cnt, i; + uint16 *glyphs; + struct koff { uint16 tr, tl, br, bl; } *koff; + + fseek(ttf,start,SEEK_SET); + coverage = getushort(ttf); + cnt = getushort(ttf); + koff = galloc(cnt*sizeof(struct koff)); + for ( i=0; i<cnt; ++i ) { + koff[i].tr = getushort(ttf); + koff[i].tl = getushort(ttf); + koff[i].br = getushort(ttf); + koff[i].bl = getushort(ttf); + } + glyphs = getCoverageTable(ttf,start+coverage,info); + if ( glyphs==NULL ) { + free(koff); +return; + } + for ( i=0; i<cnt; ++i ) if ( glyphs[i]<info->glyph_cnt && info->chars[ glyphs[i]]!=NULL ) { + SplineChar *sc = info->chars[ glyphs[i]]; + sc->mathkern = chunkalloc(sizeof(struct mathkern)); + if ( koff[i].tr!=0 ) + ttf_math_read_mathkernv(ttf,start+koff[i].tr,&sc->mathkern->top_right,sc,true,info); + if ( koff[i].tl!=0 ) + ttf_math_read_mathkernv(ttf,start+koff[i].tl,&sc->mathkern->top_left,sc,true,info); + if ( koff[i].br!=0 ) + ttf_math_read_mathkernv(ttf,start+koff[i].br,&sc->mathkern->bottom_right,sc,false,info); + if ( koff[i].bl!=0 ) + ttf_math_read_mathkernv(ttf,start+koff[i].bl,&sc->mathkern->bottom_left,sc,false,info); + } + free(koff); + free(glyphs); +} + +static void ttf_math_read_glyphinfo(FILE *ttf,struct ttfinfo *info, uint32 start) { + int icoff,taoff,esoff,kioff; + + fseek(ttf,start,SEEK_SET); + icoff = getushort(ttf); + taoff = getushort(ttf); + esoff = getushort(ttf); + kioff = getushort(ttf); + + if ( icoff!=0 ) + ttf_math_read_icta(ttf,info,start+icoff,true); + if ( taoff!=0 ) + ttf_math_read_icta(ttf,info,start+taoff,false); + if ( esoff!=0 ) + ttf_math_read_extended(ttf,info,start+esoff); + if ( kioff!=0 ) + ttf_math_read_mathkern(ttf,info,start+kioff); +} + +static struct glyphvariants *ttf_math_read_gvtable(FILE *ttf,struct ttfinfo *info, uint32 start, + enum gsub_inusetype justinuse, SplineChar *basesc, int isv ) { + struct glyphvariants *gv = chunkalloc(sizeof(struct glyphvariants)); + int ga_offset; + int vcnt; + uint16 *glyphs; + int i, j, len; + char *pt; + int ic_offset, pcnt; + SplineChar *sc; + char ebuf[10], buffer[50], *ext; + + fseek(ttf,start,SEEK_SET); + ga_offset = getushort(ttf); + vcnt = getushort(ttf); + if ( vcnt!=0 ) { + if ( justinuse==git_justinuse ) { + for ( i=0; i<vcnt; ++i ) { + int gid = getushort(ttf); + /* sizes[i] = */ getushort(ttf); + if ( gid>=0 && gid<info->glyph_cnt ) + info->inuse[gid] = true; + } + } else if ( justinuse==git_findnames ) { + for ( i=0; i<vcnt; ++i ) { + int gid = getushort(ttf); + /* sizes[i] = */ getushort(ttf); + if ( basesc!=NULL && basesc->name!=NULL && + gid>=0 && gid<info->glyph_cnt && + (sc = info->chars[gid])!=NULL && sc->name==NULL ) { + snprintf(buffer,sizeof(buffer),"%.30s.%csize%d", + basesc->name, isv?'v':'h', i); + sc->name = copy(buffer); + } + } + } else { + glyphs = galloc(vcnt*sizeof(uint16)); + len = 0; + for ( i=0; i<vcnt; ++i ) { + glyphs[i] = getushort(ttf); + /* sizes[i] = */ getushort(ttf); + if ( glyphs[i]<info->glyph_cnt && (sc = info->chars[ glyphs[i]])!=NULL ) + len += strlen(sc->name)+1; + } + if ( len!=0 ) { + gv->variants = pt = galloc(len); + for ( i=len=0; i<vcnt; ++i ) { + if ( glyphs[i]<info->glyph_cnt && (sc = info->chars[ glyphs[i]])!=NULL ) { + strcpy(pt+len,sc->name); + len += strlen(sc->name); + pt[len++] = ' '; + } + } + pt[len-1] = '\0'; + } + free(glyphs); + } + } + if ( ga_offset!=0 ) { + start += ga_offset; + fseek(ttf,start,SEEK_SET); + gv->italic_correction = getushort(ttf); + ic_offset = getushort(ttf); + gv->part_cnt = pcnt = getushort(ttf); + if ( justinuse==git_normal ) + gv->parts = gcalloc(pcnt,sizeof(struct gv_part)); + for ( i=j=0; i<pcnt; ++i ) { + int gid, start, end, full, flags; + gid = getushort(ttf); + start = getushort(ttf); + end = getushort(ttf); + full = getushort(ttf); + flags = getushort(ttf); + if ( feof(ttf)) { + LogError( _("Bad glyph variant subtable of MATH table.\n") ); + info->bad_ot = true; + chunkfree(gv,sizeof(*gv)); +return( NULL ); + } + if ( justinuse==git_justinuse ) { + if ( gid<info->glyph_cnt ) + info->inuse[gid] = true; + } else if ( justinuse==git_findnames ) { + if ( basesc!=NULL && basesc->name!=NULL && + gid>=0 && gid<info->glyph_cnt && + (sc = info->chars[gid])!=NULL && sc->name==NULL ) { + if ( pcnt==1 ) + ext = "repeat"; + if ( i==0 ) + ext = isv ? "bot" : "left"; + else if ( i==pcnt-1 ) + ext = isv ? "top" : "right"; + else if ( i==1 && pcnt==3 ) + ext = "mid"; + else { + sprintf( ebuf, "%cpart%d", isv?'v':'h', i ); + ext = ebuf; + } + snprintf(buffer,sizeof(buffer),"%.30s.%s", + basesc->name, ext ); + sc->name = copy(buffer); + } + } else { + if ( gid<info->glyph_cnt && (sc = info->chars[gid])!=NULL ) { + gv->parts[j].component = copy( sc->name ); + gv->parts[j].startConnectorLength = start; + gv->parts[j].endConnectorLength = end; + gv->parts[j].fullAdvance = full; + gv->parts[j++].is_extender = flags&1; + } + } + } + gv->part_cnt = j; +#ifdef FONTFORGE_CONFIG_DEVICETABLES + if ( ic_offset!=0 && justinuse==git_normal ) { + gv->italic_adjusts = chunkalloc(sizeof(DeviceTable)); + ReadDeviceTable(ttf,gv->italic_adjusts,start+ic_offset,info); + } +#endif + } + if ( justinuse==git_justinuse ) { + chunkfree(gv,sizeof(*gv)); +return( NULL ); + } +return( gv ); +} + +static void ttf_math_read_variants(FILE *ttf,struct ttfinfo *info, uint32 start, + enum gsub_inusetype justinuse) { + int vcoverage, hcoverage, vcnt, hcnt; + int *hoffs, *voffs; + uint16 *hglyphs, *vglyphs; + int i; + + fseek(ttf,start,SEEK_SET); + if ( info->math==NULL ) + info->math = gcalloc(1,sizeof(struct MATH)); + info->math->MinConnectorOverlap = getushort(ttf); + vcoverage = getushort(ttf); + hcoverage = getushort(ttf); + vcnt = getushort(ttf); + hcnt = getushort(ttf); + hoffs = galloc(hcnt*sizeof(int)); + voffs = galloc(vcnt*sizeof(int)); + + for ( i=0; i<vcnt; ++i ) + voffs[i] = getushort(ttf); + for ( i=0; i<hcnt; ++i ) + hoffs[i] = getushort(ttf); + vglyphs = hglyphs = NULL; + if ( vcoverage!=0 ) + vglyphs = getCoverageTable(ttf,start+vcoverage,info); + if ( hcoverage!=0 ) + hglyphs = getCoverageTable(ttf,start+hcoverage,info); + + if ( vglyphs!=NULL ) { + for ( i=0; i<vcnt; ++i ) if ( vglyphs[i]<info->glyph_cnt && voffs[i]!=0) { + if ( justinuse == git_normal || justinuse == git_findnames ) { + SplineChar *sc = info->chars[ vglyphs[i]]; + if ( sc!=NULL ) + sc->vert_variants = ttf_math_read_gvtable(ttf,info,start+voffs[i],justinuse,sc,true); + } else if ( info->inuse[ vglyphs[i]]) + ttf_math_read_gvtable(ttf,info,start+voffs[i],justinuse,NULL,true); + } + } + if ( hglyphs!=NULL ) { + for ( i=0; i<hcnt; ++i ) if ( hglyphs[i]<info->glyph_cnt && hoffs[i]!=0) { + if ( justinuse == git_normal || justinuse == git_findnames ) { + SplineChar *sc = info->chars[ hglyphs[i]]; + if ( sc!=NULL ) + sc->horiz_variants = ttf_math_read_gvtable(ttf,info,start+hoffs[i],justinuse,sc,false); + } else if ( info->inuse[ hglyphs[i]]) + ttf_math_read_gvtable(ttf,info,start+hoffs[i],justinuse,NULL,false); + } + } + + free(vglyphs); free(voffs); + free(hglyphs); free(hoffs); +} + +static void _otf_read_math(FILE *ttf,struct ttfinfo *info, + enum gsub_inusetype justinuse) { + int constants, glyphinfo, variants; + if ( info->math_start==0 ) +return; + fseek(ttf,info->math_start,SEEK_SET); + + info->g_bounds = info->math_start+info->math_length; + + if ( getlong(ttf)!=0x00010000 ) +return; + constants = getushort(ttf); + glyphinfo = getushort(ttf); + variants = getushort(ttf); + + if ( justinuse == git_normal ) { + if ( constants!=0 ) + ttf_math_read_constants(ttf,info,info->math_start+constants); + if ( glyphinfo!=0 ) + ttf_math_read_glyphinfo(ttf,info,info->math_start+glyphinfo); + } + if ( variants!=0 ) + ttf_math_read_variants(ttf,info,info->math_start+variants,justinuse); + if ( ftell(ttf)>info->g_bounds ) { + LogError("MATH table extends beyond table bounds"); + info->bad_ot = true; + } + info->g_bounds = 0; +} + +void otf_read_math(FILE *ttf,struct ttfinfo *info) { + _otf_read_math(ttf,info,git_normal); +} + +void otf_read_math_used(FILE *ttf,struct ttfinfo *info) { + _otf_read_math(ttf,info,git_justinuse); +} + +void GuessNamesFromMATH(FILE *ttf,struct ttfinfo *info) { + _otf_read_math(ttf,info,git_findnames); +} + +static struct baselangextent *readttfbaseminmax(FILE *ttf,uint32 offset,struct ttfinfo *info, + uint32 script_tag,uint32 lang_tag) { + int j,feat_cnt; + struct baselangextent *lang, *cur, *last; + (void)info; /* for -Wall */ + (void)script_tag; /* for -Wall */ + fseek(ttf,offset,SEEK_SET); + lang = chunkalloc(sizeof(struct baselangextent)); + lang->lang = lang_tag; + lang->descent = (short) getushort(ttf); + lang->ascent = (short) getushort(ttf); + + feat_cnt = getushort(ttf); + last = NULL; + for ( j=0; j<feat_cnt; ++j ) { + cur = chunkalloc(sizeof(struct baselangextent)); + if ( last==NULL ) + lang->features = cur; + else + last->next = cur; + last = cur; + cur->lang = getlong(ttf); /* Actually feature tag here */ + cur->descent = (short) getushort(ttf); + cur->ascent = (short) getushort(ttf); + } +return( lang ); +} + +void readttfbase(FILE *ttf,struct ttfinfo *info) { + int version; + uint32 axes[2]; + uint32 basetags, basescripts; + int basescriptcnt; + struct tagoff { uint32 tag; uint32 offset; } *bs; + int axis,i,j, tot; + struct Base *curBase; + struct basescript *curScript, *last; + struct baselangextent *cur, *lastLang; + + if ( info->base_start==0 ) +return; + fseek(ttf,info->base_start,SEEK_SET); + + version = getlong(ttf); + if ( version!=0x00010000 ) +return; + axes[0] = getushort(ttf); /* Horizontal */ + axes[1] = getushort(ttf); /* Vertical */ + + for ( axis=0; axis<2; ++axis ) { + if ( axes[axis]==0 ) + continue; + fseek(ttf,info->base_start+axes[axis],SEEK_SET); + curBase = chunkalloc(sizeof(struct Base)); + if ( axis==0 ) info->horiz_base = curBase; else info->vert_base = curBase; + basetags = getushort(ttf); + basescripts = getushort(ttf); + if ( basetags==0 ) { + curBase->baseline_cnt = 0; + curBase->baseline_tags = NULL; + } else { + fseek(ttf,info->base_start+axes[axis]+basetags,SEEK_SET); + curBase->baseline_cnt = getushort(ttf); + curBase->baseline_tags = gcalloc(curBase->baseline_cnt,sizeof(uint32)); + for ( i=0; i<curBase->baseline_cnt; ++i ) + curBase->baseline_tags[i] = getlong(ttf); + } + if ( basescripts!=0 ) { + fseek(ttf,info->base_start+axes[axis]+basescripts,SEEK_SET); + basescriptcnt = getushort(ttf); + bs = gcalloc(basescriptcnt,sizeof(struct tagoff)); + for ( i=0; i<basescriptcnt; ++i ) { + bs[i].tag = getlong(ttf); + bs[i].offset = getushort(ttf); + if ( bs[i].offset != 0 ) + bs[i].offset += info->base_start+axes[axis]+basescripts; + } + last = NULL; + for ( i=0; i<basescriptcnt; ++i ) if ( bs[i].offset!=0 ) { + int basevalues, defminmax; + int langsyscnt; + struct tagoff *ls; + fseek(ttf,bs[i].offset,SEEK_SET); + basevalues = getushort(ttf); + defminmax = getushort(ttf); + langsyscnt = getushort(ttf); + ls = gcalloc(langsyscnt,sizeof(struct tagoff)); + for ( j=0; j<langsyscnt; ++j ) { + ls[j].tag = getlong(ttf); + ls[j].offset = getushort(ttf); + } + curScript = chunkalloc(sizeof(struct basescript)); + if ( last==NULL ) + curBase->scripts = curScript; + else + last->next = curScript; + last = curScript; + curScript->script = bs[i].tag; + if ( basevalues!=0 ) { + int coordcnt; + int *coords; + + fseek( ttf,bs[i].offset+basevalues,SEEK_SET); + curScript->def_baseline = getushort(ttf); + tot = coordcnt = getushort(ttf); + if ( coordcnt!=curBase->baseline_cnt ) { + info->bad_ot = true; + LogError( "!!!!! Coord count (%d) for '%c%c%c%c' script does not match base tag count (%d) in 'BASE' table\n", + coordcnt, + bs[i].tag>>24, bs[i].tag>>16, bs[i].tag>>8, bs[i].tag, + curBase->baseline_cnt ); + if ( tot<curBase->baseline_cnt ) + tot = curBase->baseline_cnt; + } + coords = gcalloc(coordcnt,sizeof(int)); + curScript->baseline_pos = gcalloc(tot,sizeof(int16)); + for ( j=0; j<coordcnt; ++j ) + coords[j] = getushort(ttf); + for ( j=0; j<coordcnt; ++j ) if ( coords[j]!=0 ) { + int format; + fseek( ttf,bs[i].offset+basevalues+coords[j],SEEK_SET); + format = getushort(ttf); + curScript->baseline_pos[j] = (short) getushort(ttf); + if ( format!=1 && format!=2 && format!=3 ) { + info->bad_ot = true; + LogError("!!!!! Bad Base Coord format (%d) for '%c%c%c%c' in '%c%c%c%c' script in 'BASE' table\n", + format, + curBase->baseline_tags[j]>>24, curBase->baseline_tags[j]>>16, curBase->baseline_tags[j]>>8, curBase->baseline_tags[j], + bs[i].tag>>24, bs[i].tag>>16, bs[i].tag>>8, bs[i].tag ); + } + } + free(coords); + } + lastLang = NULL; + if ( defminmax!=0 ) + curScript->langs = lastLang = readttfbaseminmax(ttf,bs[i].offset+defminmax,info,bs[i].tag,DEFAULT_LANG); + if ( langsyscnt!=0 ) { + for ( j=0; j<langsyscnt; ++j ) if ( ls[j].offset!=0 ) { + cur = readttfbaseminmax(ttf,bs[i].offset+ls[j].offset,info,bs[i].tag,ls[j].tag); + if ( last==NULL ) + curScript->langs = cur; + else + lastLang->next = cur; + lastLang = cur; + } + } + free(ls); + } + free(bs); + } + } +} + +static void bsln_apply_values(struct ttfinfo *info, int gfirst, int glast,FILE *ttf) { + int i; + + for ( i=gfirst; i<=glast; ++i ) + info->bsln_values[i]= getushort(ttf); +} + +static void bsln_apply_value(struct ttfinfo *info, int gfirst, int glast,FILE *ttf) { + int i; + int bsln; + + bsln = getushort(ttf); + for ( i=gfirst; i<=glast; ++i ) + info->bsln_values[i]= bsln; +} + +static void bsln_apply_default(struct ttfinfo *info, int gfirst, int glast,void *def) { + int def_bsln, i; + + def_bsln = (intpt) def; + for ( i=gfirst; i<=glast; ++i ) + info->bsln_values[i]= def_bsln; +} + +void readttfbsln(FILE *ttf,struct ttfinfo *info) { + int def, ap_def, version, format; + uint16 *values; + int offsets[32]; + SplineChar *sc; + BasePoint pos; + int mapping[32]; + int i; + struct Base *base; + struct basescript *bs; + + fseek(ttf,info->bsln_start,SEEK_SET); + version = getlong(ttf); + if ( version!=0x00010000 ) +return; + format = getushort(ttf); + def = getushort(ttf); + if ( format==0 || format==1 ) { + for ( i=0; i<32; ++i ) + offsets[i] = (int16) getushort(ttf); + } else if ( format==2 || format==3 ) { + int stdGID = getushort(ttf); + int ptnum; + if ( stdGID>=info->glyph_cnt || (sc = info->chars[stdGID])==NULL ) +return; + for ( i=0; i<32; ++i ) { + ptnum = getushort(ttf); + if ( ttfFindPointInSC(sc,ly_fore,ptnum,&pos,NULL)!=-1 ) +return; + offsets[i] = pos.y; + } + } + + if ( format&1 ) { + info->bsln_values = values = gcalloc(info->glyph_cnt,sizeof(uint16)); + readttf_applelookup(ttf,info, + bsln_apply_values,bsln_apply_value, + bsln_apply_default,(void *) (intpt) def, false); + } else + values = NULL; + + for ( i=1; i<32; ++i ) mapping[i] = 3; /* Roman */ + + info->horiz_base = base = chunkalloc(sizeof(struct Base)); + base->baseline_cnt = 4; + base->baseline_tags = galloc(4*sizeof(uint32)); + base->baseline_tags[0] = CHR('h','a','n','g'); /* Apple 3 */ + if ( offsets[1]!= offsets[2] ) { + base->baseline_tags[1] = CHR('i','d','e','o'); /* Apple 2 */ + base->baseline_tags[2] = CHR('m','a','t','h'); /* Apple 4 */ + base->baseline_tags[3] = CHR('r','o','m','n'); /* Apple 0 */ + + /* Map from Apple's baseline indeces, to OT tag positions */ + mapping[3] = 0; + mapping[2] = 1; + mapping[4] = 2; + mapping[0] = 3; + /* Apple baseline 1 does not map to an OT tag */ + /* I assume baseline 1 is the normal baseline for CJK on Macs */ + /* (because baseline 2 often (and wrongly) contains the same value */ + } else { + /* baseline 1 (centered ideographic) and baseline 2 (low ideographic) */ + /* are documented to be different. Yet most of the fonts I have looked*/ + /* at (and I've only got about 4 test cases) have the low ideographic*/ + /* baseline set to the centered baseline. If I were to copy that to */ + /* OT it would be really bad. So even though baseline data is provided*/ + /* I shall ignore it if it looks WRONG! */ + base->baseline_cnt = 3; + base->baseline_tags[1] = CHR('m','a','t','h'); /* Apple 4 */ + base->baseline_tags[2] = CHR('r','o','m','n'); /* Apple 0 */ + mapping[3] = 0; + mapping[4] = 1; + mapping[0] = 2; + } + + for ( i=0; i<info->glyph_cnt; ++i ) if ( (sc=info->chars[i])!=NULL ) { + uint32 script = SCScriptFromUnicode(sc); + if ( script==DEFAULT_SCRIPT ) + continue; + for ( bs=base->scripts; bs!=NULL && bs->script!=script; bs=bs->next ); + if ( bs!=NULL ) + continue; + bs = chunkalloc(sizeof(struct basescript)); + bs->script = script; + ap_def = def; + if ( values!=NULL ) + ap_def = values[i]; + bs->def_baseline = mapping[ap_def]; + bs->baseline_pos = galloc(4*sizeof(int16)); + for ( i=0; i<5; ++i ) if ( i!=1 ) + bs->baseline_pos[mapping[i]] = offsets[i] - offsets[ap_def]; + bs->next = base->scripts; + base->scripts = bs; + } +} |