Permalink
Fetching contributors…
Cannot retrieve contributors at this time
2546 lines (2336 sloc) 81.8 KB
/* Copyright (C) 2000-2012 by George Williams */
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "fontforge.h"
#include "asmfpst.h"
#include <utype.h>
#include "ttf.h"
/* This file contains routines to create some of the Apple Advanced Typography Tables */
/* (or GX fonts) */
/* ************************************************************************** */
/* ************************* The 'acnt' table ************************* */
/* ************************************************************************** */
void aat_dumpacnt(struct alltabs *at, SplineFont *sf) {
}
/* ************************************************************************** */
/* ************************* The 'kern' table ************************* */
/* ************************************************************************** */
/* Apple's docs imply that kerning info is always provided left to right, even*/
/* for right to left scripts. 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 */
static void DumpKernClass(FILE *file, uint16 *class,int cnt,int add,int mul) {
int i, first=-1, last=-1;
for ( i=0; i<cnt; ++i ) {
if ( class[i] ) last = i;
if ( class[i] && first==-1 ) first = i;
}
putshort(file,first);
putshort(file,last-first+1);
for ( i=first; i<=last; ++i )
putshort(file,class[i]*mul+add);
}
static int morx_dumpASM(FILE *temp,ASM *sm, struct alltabs *at, SplineFont *sf );
struct kerncounts {
int cnt;
int vcnt;
int mh, mv;
int kccnt;
int vkccnt;
int ksm;
int hsubs;
int *hbreaks;
int vsubs;
int *vbreaks;
};
static int CountKerns(struct alltabs *at, SplineFont *sf, struct kerncounts *kcnt) {
int i, cnt, vcnt, j, kccnt=0, vkccnt=0, ksm=0, mh, mv;
KernPair *kp;
KernClass *kc;
ASM *sm;
cnt = mh = vcnt = mv = 0;
for ( i=0; i<at->gi.gcnt; ++i ) if ( at->gi.bygid[i]!=-1 ) {
j = 0;
for ( kp = sf->glyphs[at->gi.bygid[i]]->kerns; kp!=NULL; kp=kp->next )
if ( kp->off!=0 && kp->sc->ttf_glyph!=-1 &&
LookupHasDefault(kp->subtable->lookup ))
++cnt, ++j;
if ( j>mh ) mh=j;
j=0;
for ( kp = sf->glyphs[at->gi.bygid[i]]->vkerns; kp!=NULL; kp=kp->next )
if ( kp->off!=0 && kp->sc->ttf_glyph!=-1 &&
LookupHasDefault(kp->subtable->lookup ))
++vcnt, ++j;
if ( j>mv ) mv=j;
}
kcnt->cnt = cnt;
kcnt->vcnt = vcnt;
kcnt->mh = mh;
kcnt->mv = mv;
kcnt->hbreaks = kcnt->vbreaks = NULL;
if ( cnt>=10000 ) {
/* the sub-table size is 6*cnt+14 or so and needs to be less 65535 */
/* so break it up into little bits */
/* We might not need this when applemode is set because the subtable */
/* length is a long. BUT... there's a damn binsearch header with */
/* shorts in it still */
int b=0;
kcnt->hbreaks = malloc((at->gi.gcnt+1)*sizeof(int));
cnt = 0;
for ( i=0; i<at->gi.gcnt; ++i ) if ( at->gi.bygid[i]!=-1 ) {
j = 0;
for ( kp = sf->glyphs[at->gi.bygid[i]]->kerns; kp!=NULL; kp=kp->next )
if ( kp->off!=0 && LookupHasDefault(kp->subtable->lookup ))
++j;
if ( (cnt+j)*6>64000L && cnt!=0 ) {
kcnt->hbreaks[b++] = cnt;
cnt = 0;
}
cnt += j;
}
kcnt->hbreaks[b++] = cnt;
kcnt->hsubs = b;
} else if ( cnt!=0 )
kcnt->hsubs = 1;
else
kcnt->hsubs = 0;
if ( vcnt>=10000 ) {
int b=0;
kcnt->vbreaks = malloc((at->gi.gcnt+1)*sizeof(int));
vcnt = 0;
for ( i=0; i<at->gi.gcnt; ++i ) if ( at->gi.bygid[i]!=-1 ) {
j = 0;
for ( kp = sf->glyphs[at->gi.bygid[i]]->vkerns; kp!=NULL; kp=kp->next )
if ( kp->off!=0 && LookupHasDefault(kp->subtable->lookup))
++j;
if ( (vcnt+j)*6>64000L && vcnt!=0 ) {
kcnt->vbreaks[b++] = vcnt;
vcnt = 0;
}
vcnt += j;
}
kcnt->vbreaks[b++] = vcnt;
kcnt->vsubs = b;
} else if ( vcnt!=0 )
kcnt->vsubs = 1;
else
kcnt->vsubs = 0;
if ( at->applemode ) { /* if we aren't outputting Apple's extensions to kerning (by classes, and by state machine) then don't check for those extensions */
for ( kc=sf->kerns; kc!=NULL; kc = kc->next ) if ( LookupHasDefault(kc->subtable->lookup) )
++kccnt;
for ( kc=sf->vkerns; kc!=NULL; kc = kc->next ) if ( LookupHasDefault(kc->subtable->lookup) )
++vkccnt;
for ( sm=sf->sm; sm!=NULL; sm=sm->next )
if ( sm->type == asm_kern )
++ksm;
}
kcnt->kccnt = kccnt;
kcnt->vkccnt = vkccnt;
kcnt->ksm = ksm;
return( kcnt->hsubs + kcnt->vsubs + kccnt + vkccnt + ksm );
}
static void ttf_dumpsfkerns(struct alltabs *at, SplineFont *sf, int tupleIndex, int version) {
struct kerncounts kcnt;
int i, j, k, m, c, gid, tot, km;
KernPair *kp;
KernClass *kc;
ASM *sm;
uint16 *glnum, *offsets;
int isv;
int tupleMask = tupleIndex==-1 ? 0 : 0x2000;
int b, bmax;
int *breaks;
int winfail=0;
int subtableBeginPos,subtableEndPos;
if ( CountKerns(at,sf,&kcnt)==0 )
return;
if ( tupleIndex==-1 ) tupleIndex = 0;
for ( isv=0; isv<2; ++isv ) {
c = isv ? kcnt.vcnt : kcnt.cnt;
bmax = isv ? kcnt.vsubs : kcnt.hsubs;
breaks = isv ? kcnt.vbreaks : kcnt.hbreaks;
if ( c!=0 ) {
km = isv ? kcnt.mv : kcnt.mh;
glnum = malloc(km*sizeof(uint16));
offsets = malloc(km*sizeof(uint16));
gid = 0;
for ( b=0; b<bmax; ++b ) {
c = bmax==1 ? c : breaks[b];
// skip subtable header because we don't know the number of kern pairs yet
subtableBeginPos=ftell(at->kern);
if(version==0) fseek(at->kern,7*sizeof(uint16),SEEK_CUR);
else fseek(at->kern,8*sizeof(uint16),SEEK_CUR);
for ( tot = 0; gid<at->gi.gcnt && tot<c; ++gid ) if ( at->gi.bygid[gid]!=-1 ) {
SplineChar *sc = sf->glyphs[at->gi.bygid[gid]];
// if requested, omit kern pairs with unmapped glyphs
// (required for compatibility with non-OpenType-aware Windows applications)
if( (at->gi.flags&ttf_flag_oldkernmappedonly) && (unsigned)(sc->unicodeenc)>0xFFFF ) continue;
m = 0;
for ( kp = isv ? sc->vkerns : sc->kerns; kp!=NULL; kp=kp->next ) {
// if requested, omit kern pairs with unmapped glyphs
// (required for compatibility with non-OpenType-aware Windows applications)
if( (at->gi.flags&ttf_flag_oldkernmappedonly) && (unsigned)(kp->sc->unicodeenc)>0xFFFF ) continue;
if ( kp->off!=0 && kp->sc->ttf_glyph!=-1 &&
LookupHasDefault(kp->subtable->lookup)) {
/* order the pairs */
for ( j=0; j<m; ++j )
if ( kp->sc->ttf_glyph<glnum[j] )
break;
for ( k=m; k>j; --k ) {
glnum[k] = glnum[k-1];
offsets[k] = offsets[k-1];
}
glnum[j] = kp->sc->ttf_glyph;
offsets[j] = kp->off;
++m;
/* check if a pair will cause problems on Windows */
/* If the glyph is outside BMP, so either unicode >0xffff */
/* or -1. Cast to unsigned catches both */
if( (unsigned)(sf->glyphs[at->gi.bygid[gid]]->unicodeenc)>0xFFFF ||
(unsigned)(sf->glyphs[at->gi.bygid[glnum[j]]]->unicodeenc)>0xFFFF )
winfail++;
}
}
for ( j=0; j<m; ++j ) {
putshort(at->kern,gid);
putshort(at->kern,glnum[j]);
putshort(at->kern,offsets[j]);
}
tot += m;
}
// now we can fill the subtable header
c=tot;
subtableEndPos=ftell(at->kern);
fseek(at->kern,subtableBeginPos,SEEK_SET);
if ( version==0 ) {
putshort(at->kern,0); /* subtable version */
if ( c>10920 )
ff_post_error(_("Too many kern pairs"),_("The 'kern' table supports at most 10920 kern pairs in a subtable"));
putshort(at->kern,(7+3*c)*sizeof(uint16)); /* subtable length */
putshort(at->kern,!isv); /* coverage, flags=hor/vert&format=0 */
} else {
putlong(at->kern,(8+3*c)*sizeof(uint16)); /* subtable length */
/* Apple's new format has a completely different coverage format */
putshort(at->kern,(isv?0x8000:0)| /* format 0, horizontal/vertical flags (coverage) */
tupleMask);
putshort(at->kern,tupleIndex);
}
putshort(at->kern,c);
for ( i=1,j=0; i<=c; i<<=1, ++j );
i>>=1; --j;
putshort(at->kern,i*6); /* binary search headers */
putshort(at->kern,j);
putshort(at->kern,6*(c-i));
fseek(at->kern,subtableEndPos,SEEK_SET);
}
free(offsets);
free(glnum);
}
}
free(kcnt.hbreaks); free(kcnt.vbreaks);
if( winfail > 0 )
ff_post_error(_("Kerning is likely to fail on Windows"),_(
"Note: On Windows many apps can have problems with this font's kerning, because %d of its glyph kern pairs cannot be mapped to unicode-BMP kern pairs (eg, they have a Unicode value of -1) To avoid this, go to Generate, Options, and check the \"Windows-compatible \'kern\'\" option."),
winfail);
if ( at->applemode ) for ( isv=0; isv<2; ++isv ) {
for ( kc=isv ? sf->vkerns : sf->kerns; kc!=NULL; kc=kc->next ) if ( LookupHasDefault(kc->subtable->lookup) ) {
/* If we are here, we must be using version 1 */
uint32 len_pos = ftell(at->kern), pos;
uint16 *class1, *class2;
int first_cnt = kc->first_cnt;
/* OpenType fonts can actually have a set of glyphs in class[0] of*/
/* the first class. This happens when there are glyphs in the */
/* coverage table which are not in any of the classes. Otherwise */
/* class 0 is sort of useless in opentype */
if ( kc->firsts[0]!=NULL )
++first_cnt;
putlong(at->kern,0); /* subtable length */
putshort(at->kern,(isv?0x8002:2)| /* format 2, horizontal/vertical flags (coverage) */
tupleMask);
putshort(at->kern,tupleIndex);
putshort(at->kern,sizeof(uint16)*kc->second_cnt);
putshort(at->kern,0); /* left classes */
putshort(at->kern,0); /* right classes */
putshort(at->kern,16); /* Offset to array, next byte */
if ( kc->firsts[0]!=NULL ) {
/* Create a dummy class to correspond to the mac's class 0 */
/* all entries will be 0 */
for ( i=0 ; i<kc->second_cnt; ++i )
putshort(at->kern,0);
}
for ( i=0; i<kc->first_cnt*kc->second_cnt; ++i )
putshort(at->kern,kc->offsets[i]);
pos = ftell(at->kern);
fseek(at->kern,len_pos+10,SEEK_SET);
putshort(at->kern,pos-len_pos);
fseek(at->kern,pos,SEEK_SET);
class1 = ClassesFromNames(sf,kc->firsts,kc->first_cnt,at->maxp.numGlyphs,NULL,true);
DumpKernClass(at->kern,class1,at->maxp.numGlyphs,16,sizeof(uint16)*kc->second_cnt);
free(class1);
pos = ftell(at->kern);
fseek(at->kern,len_pos+12,SEEK_SET);
putshort(at->kern,pos-len_pos);
fseek(at->kern,pos,SEEK_SET);
class2 = ClassesFromNames(sf,kc->seconds,kc->second_cnt,at->maxp.numGlyphs,NULL,true);
DumpKernClass(at->kern,class2,at->maxp.numGlyphs,0,sizeof(uint16));
free(class2);
pos = ftell(at->kern);
fseek(at->kern,len_pos,SEEK_SET);
putlong(at->kern,pos-len_pos);
fseek(at->kern,pos,SEEK_SET);
}
}
if ( at->applemode ) if ( kcnt.ksm!=0 ) {
for ( sm=sf->sm; sm!=NULL; sm=sm->next ) if ( sm->type == asm_kern ) {
uint32 len_pos = ftell(at->kern), pos;
putlong(at->kern,0); /* subtable length */
putshort(at->kern,((sm->flags&0x8000)?0x8001:1)| /* format 1, horizontal/vertical flags (coverage) */
tupleMask);
putshort(at->kern,tupleIndex);
morx_dumpASM(at->kern,sm,at,sf);
pos = ftell(at->kern);
fseek(at->kern,len_pos,SEEK_SET);
putlong(at->kern,pos-len_pos);
fseek(at->kern,pos,SEEK_SET);
}
}
}
void ttf_dumpkerns(struct alltabs *at, SplineFont *sf) {
int i, mmcnt=0, sum;
int version;
MMSet *mm = at->dovariations ? sf->mm : NULL;
struct kerncounts kcnt;
int must_use_old_style = 0;
if ( !at->applemode && (!at->opentypemode || (at->gi.flags&ttf_flag_oldkern)) ) {
must_use_old_style = true;
SFKernClassTempDecompose(sf,false);
mm = NULL;
} else {
if ( mm!=NULL ) {
for ( i=0; i<mm->instance_count; ++i ) {
mmcnt += CountKerns(at,mm->instances[i],&kcnt);
free(kcnt.hbreaks); free(kcnt.vbreaks);
}
sf = mm->normal;
}
}
sum = CountKerns(at,sf,&kcnt);
free(kcnt.hbreaks); free(kcnt.vbreaks);
if ( sum==0 && mmcnt==0 ) {
if ( must_use_old_style )
SFKernCleanup(sf,false);
return;
}
/* Old kerning format (version 0) uses 16 bit quantities */
/* Apple's new format (version 0x00010000) uses 32 bit quantities */
at->kern = tmpfile();
if ( must_use_old_style ||
( kcnt.kccnt==0 && kcnt.vkccnt==0 && kcnt.ksm==0 && mmcnt==0 )) {
/* MS does not support format 1,2,3 kern sub-tables so if we have them */
/* we might as well admit that this table is for apple only and use */
/* the new format apple recommends. Otherwise, use the old format */
/* If we might need to store tuple data, use the new format */
putshort(at->kern,0); /* version */
putshort(at->kern,sum); /* number of subtables */
version = 0;
} else {
putlong(at->kern,0x00010000); /* version */
putlong(at->kern,sum+mmcnt); /* number of subtables */
version = 1;
}
ttf_dumpsfkerns(at, sf, -1, version);
if ( mm!=NULL ) {
for ( i=0; i<mm->instance_count; ++i )
ttf_dumpsfkerns(at, mm->instances[i], i, version);
}
if ( must_use_old_style )
SFKernCleanup(sf,false);
at->kernlen = ftell(at->kern);
if ( at->kernlen&2 )
putshort(at->kern,0); /* pad it */
}
/* ************************************************************************** */
/* ************************* The 'lcar' table ************************* */
/* ************************************************************************** */
static PST *haslcaret(SplineChar *sc) {
PST *pst; int j;
for ( pst=sc->possub; pst!=NULL && pst->type!=pst_lcaret; pst=pst->next );
if ( pst!=NULL ) {
if ( !sc->lig_caret_cnt_fixed ) {
for ( j=pst->u.lcaret.cnt-1; j>=0 && pst->u.lcaret.carets[j]==0; --j );
if ( j==-1 )
pst = NULL;
} else {
if ( pst->u.lcaret.cnt==0 )
pst = NULL;
}
}
return( pst );
}
void aat_dumplcar(struct alltabs *at, SplineFont *sf) {
int i, j, k, l, seg_cnt, tot, last, offset;
PST *pst;
FILE *lcar=NULL;
SplineChar *sc;
/* We do four passes. The first just calculates how much space we will need */
/* the second provides the top-level lookup table structure */
/* the third provides the arrays of offsets needed for type 4 lookup tables */
/* the fourth provides the actual data on the ligature carets */
for ( k=0; k<4; ++k ) {
for ( i=seg_cnt=tot=0; i<at->gi.gcnt; ++i )
if ( at->gi.bygid[i]!=-1 &&
(pst = haslcaret(sc = sf->glyphs[at->gi.bygid[i]]))!=NULL ) {
if ( k==1 )
tot = 0;
else if ( k==2 ) {
putshort(lcar,offset);
offset += 2 + 2*LigCaretCnt(sc);
} else if ( k==3 ) {
putshort(lcar,LigCaretCnt(sc));
for ( l=0; l<pst->u.lcaret.cnt; ++l )
if ( pst->u.lcaret.carets[l]!=0 || sc->lig_caret_cnt_fixed )
putshort(lcar,pst->u.lcaret.carets[l]);
}
last = i;
for ( j=i+1, ++tot; j<at->gi.gcnt && at->gi.bygid[j]!=-1; ++j ) {
if ( (pst = haslcaret(sc = sf->glyphs[at->gi.bygid[j]]))== NULL )
break;
++tot;
last = j;
if ( k==2 ) {
putshort(lcar,offset);
offset += 2 + 2*LigCaretCnt(sc);
} else if ( k==3 ) {
putshort(lcar,LigCaretCnt(sc));
for ( l=0; l<pst->u.lcaret.cnt; ++l )
if ( pst->u.lcaret.carets[l]!=0 || sc->lig_caret_cnt_fixed )
putshort(lcar,pst->u.lcaret.carets[l]);
}
}
if ( k==1 ) {
putshort(lcar,last);
putshort(lcar,i);
putshort(lcar,offset);
offset += 2*tot;
}
++seg_cnt;
i = j-1;
}
if ( k==0 ) {
if ( seg_cnt==0 )
return;
lcar = tmpfile();
putlong(lcar, 0x00010000); /* version */
putshort(lcar,0); /* data are distances (not points) */
putshort(lcar,4); /* Lookup table format 4 */
/* Binary search header */
putshort(lcar,6); /* Entry size */
putshort(lcar,seg_cnt); /* Number of segments */
for ( j=0,l=1; l<=seg_cnt; l<<=1, ++j );
--j; l>>=1;
putshort(lcar,6*l);
putshort(lcar,j);
putshort(lcar,6*(seg_cnt-l));
offset = /*4+2+*/6*2 + seg_cnt*6 + 6 /* fake segment at end */;
/* Offset relative to lookup table, not to lcar_start */
/* Or, that's true while we build the lookup table. Once we */
/* start working on the data offsets they are relative to */
/* lcar_start */
} else if ( k==1 ) { /* flag entry */
putshort(lcar,0xffff);
putshort(lcar,0xffff);
putshort(lcar,0);
offset += 6; /* Now offsets are relative to lcar_start */
}
}
at->lcar = lcar;
at->lcarlen = ftell(at->lcar);
if ( at->lcarlen&2 )
putshort(at->lcar,0);
}
/* ************************************************************************** */
/* ************************* The 'morx' table ************************* */
/* ************************* (and 'feat') ************************* */
/* ************************************************************************** */
/* Each lookup gets its own subtable, so there may be multiple subtables */
/* with the same feature/setting. The subtables will be ordered the same */
/* way the lookups are, which might lead to awkwardness if there are many */
/* chains and the same feature occurs in several of them */
/* (only the default language will be used) */
struct feature {
int16 featureType, featureSetting;
MacFeat *mf, *smf;
struct macsetting *ms, *sms;
unsigned int vertOnly: 1;
unsigned int r2l: 1; /* I think this is the "descending" flag */
unsigned int needsOff: 1;
unsigned int singleMutex: 1;
unsigned int dummyOff: 1;
uint8 subtable_type;
int chain;
int32 flag, offFlags;
uint32 feature_start;
uint32 feature_len; /* Does not include header yet */
struct feature *next; /* features in output order */
struct feature *nexttype; /* features in feature/setting order */
struct feature *nextsame; /* all features with the same feature/setting */
int setting_cnt, setting_index, real_index;
};
static struct feature *featureFromSubtable(SplineFont *sf, struct lookup_subtable *sub );
static int PSTHasTag(PST *pst, uint32 tag);
static void morxfeaturesfree(struct feature *features) {
struct feature *n;
for ( ; features!=NULL; features=n ) {
n = features->next;
chunkfree( features,sizeof(*features) );
}
}
static void mort_classes(FILE *temp,SplineFont *sf,struct glyphinfo *gi) {
int first, last, i, cnt;
/* Mort tables just have a trimmed byte array for the classes */
for ( first=0; first<gi->gcnt; ++first )
if ( gi->bygid[first]!=-1 && sf->glyphs[gi->bygid[first]]->lsidebearing!=1 )
break;
for ( last=gi->gcnt-1; last>first; --last )
if ( gi->bygid[last]!=-1 && sf->glyphs[gi->bygid[last]]->lsidebearing!=1 )
break;
cnt = last-first+1;
putshort(temp,first);
putshort(temp,cnt);
for ( i=first; i<=last; ++i )
if ( gi->bygid[i]==-1 )
putc(1,temp);
else
putc(sf->glyphs[gi->bygid[i]]->lsidebearing,temp);
if ( cnt&1 )
putc(1,temp); /* Pad to a word boundary */
}
static void morx_lookupmap(FILE *temp,SplineChar **glyphs,uint16 *maps,int gcnt) {
int i, j, k, l, seg_cnt, tot, last, offset;
/* We do four passes. The first just calculates how much space we will need (if any) */
/* the second provides the top-level lookup table structure */
/* the third provides the arrays of offsets needed for type 4 lookup tables */
for ( k=0; k<3; ++k ) {
for ( i=seg_cnt=tot=0; i<gcnt; ++i ) {
if ( glyphs[i]==NULL )
continue;
if ( k==1 )
tot = 0;
else if ( k==2 ) {
putshort(temp,maps[i]);
}
last = i;
for ( j=i+1, ++tot; j<gcnt && glyphs[j]!=NULL && glyphs[j]->ttf_glyph==glyphs[i]->ttf_glyph+j-i; ++j ) {
++tot;
last = j;
if ( k==2 ) {
putshort(temp,maps[j]);
}
}
if ( k==1 ) {
putshort(temp,glyphs[last]->ttf_glyph);
putshort(temp,glyphs[i]->ttf_glyph);
putshort(temp,offset);
offset += 2*tot;
}
++seg_cnt;
i = j-1;
}
if ( k==0 ) {
putshort(temp,4); /* Lookup table format 4 */
/* Binary search header */
putshort(temp,6); /* Entry size */
putshort(temp,seg_cnt); /* Number of segments */
for ( j=0,l=1; l<=seg_cnt; l<<=1, ++j );
--j; l>>=1;
putshort(temp,6*l);
putshort(temp,j);
putshort(temp,6*(seg_cnt-l));
if ( seg_cnt==0 )
return;
offset = 6*2 + seg_cnt*6 + 6;
} else if ( k==1 ) { /* flag entry */
putshort(temp,0xffff);
putshort(temp,0xffff);
putshort(temp,0);
}
}
}
static void morx_dumpSubsFeature(FILE *temp,SplineChar **glyphs,uint16 *maps,int gcnt) {
morx_lookupmap(temp,glyphs,maps,gcnt);
}
static struct feature *aat_dumpmorx_substitutions(struct alltabs *at, SplineFont *sf,
FILE *temp, struct feature *features, struct lookup_subtable *sub) {
int i, k, gcnt;
SplineChar *sc, *msc, **glyphs;
uint16 *maps;
struct feature *cur;
PST *pst;
for ( k=0; k<2; ++k ) {
gcnt = 0;
for ( i=0; i<at->gi.gcnt; ++i ) if ( at->gi.bygid[i]!=-1 ) {
sc = sf->glyphs[at->gi.bygid[i]];
for ( pst=sc->possub; pst!=NULL && pst->subtable!=sub; pst=pst->next );
if ( pst!=NULL ) {
if ( k==1 ) {
msc = SFGetChar(sf,-1,pst->u.subs.variant);
glyphs[gcnt] = sc;
if ( msc!=NULL && msc->ttf_glyph!=-1 ) {
maps[gcnt++] = msc->ttf_glyph;
} else if ( msc==NULL &&
strcmp(pst->u.subs.variant,MAC_DELETED_GLYPH_NAME)==0 ) {
maps[gcnt++] = 0xffff;
}
} else
++gcnt;
}
}
if ( k==0 ) {
if ( gcnt==0 )
return( features );
glyphs = malloc((gcnt+1)*sizeof(SplineChar *));
maps = malloc((gcnt+1)*sizeof(uint16));
} else {
glyphs[gcnt] = NULL; maps[gcnt] = 0;
}
}
cur = featureFromSubtable(sf,sub);
cur->next = features;
cur->r2l = sub->lookup->lookup_flags&pst_r2l ? true : false;
features = cur;
cur->subtable_type = 4;
cur->feature_start = ftell(temp);
morx_dumpSubsFeature(temp,glyphs,maps,gcnt);
if ( (ftell(temp)-cur->feature_start)&1 )
putc('\0',temp);
if ( (ftell(temp)-cur->feature_start)&2 )
putshort(temp,0);
cur->feature_len = ftell(temp)-cur->feature_start;
free(glyphs); free(maps);
return( features);
}
static LigList *LigListMatchSubtable(SplineFont *sf,LigList *ligs,
struct lookup_subtable *sub) {
LigList *l;
for ( l=ligs; l!=NULL; l=l->next )
if ( l->lig->subtable==sub )
return( l );
return( NULL );
}
static int IsMarkChar( SplineChar *sc ) {
AnchorPoint *ap;
ap=sc->anchor;
while ( ap!=NULL && (ap->type==at_centry || ap->type==at_cexit) )
ap = ap->next;
if ( ap!=NULL && (ap->type==at_mark || ap->type==at_basemark) )
return( true );
return( false );
}
struct transition { uint16 next_state, dontconsume, ismark, trans_ent; LigList *l; };
struct trans_entries { uint16 next_state, flags, act_index; LigList *l; };
static void morx_dumpLigaFeature(FILE *temp,SplineChar **glyphs,int gcnt,
struct lookup_subtable *sub, struct alltabs *at, SplineFont *sf,
int ignoremarks) {
LigList *l;
struct splinecharlist *comp;
uint16 *used = calloc(at->maxp.numGlyphs,sizeof(uint16));
SplineChar **cglyphs;
uint16 *map;
int i,j,k,class, state_max, state_cnt, base, last;
uint32 start;
struct transition **states;
struct trans_entries *trans;
int trans_cnt;
int maxccnt=0;
int acnt, lcnt, charcnt;
uint32 *actions;
uint16 *components, *lig_glyphs;
uint32 here;
struct splinecharlist *scl;
int anymarks;
/* figure out the classes (one for each character used to make a lig) */
for ( i=0; i<gcnt; ++i ) {
used[glyphs[i]->ttf_glyph] = true;
for ( l=glyphs[i]->ligofme; l!=NULL; l=l->next ) if ( l->lig->subtable==sub ) {
for ( comp = l->components; comp!=NULL; comp=comp->next )
used[comp->sc->ttf_glyph] = true;
}
}
class = 4;
for ( i=0; i<at->maxp.numGlyphs; ++i ) if ( used[i] )
used[i] = class++;
anymarks = false;
charcnt = class;
if ( ignoremarks ) {
for ( i=0; i<at->gi.gcnt; ++i ) if ( at->gi.bygid[i]!=-1 ) {
if ( IsMarkChar(sf->glyphs[at->gi.bygid[i]])) {
anymarks = true;
++charcnt;
used[i] = class;
}
}
if ( anymarks )
++class;
}
cglyphs = malloc((charcnt+1)*sizeof(SplineChar *));
map = malloc((charcnt+1)*sizeof(uint16));
j=0;
for ( i=k=0; i<at->maxp.numGlyphs; ++i ) if ( used[i] ) {
j = at->gi.bygid[i];
if ( j!=-1 ) {
cglyphs[k] = sf->glyphs[j];
map[k++] = used[i];
}
}
cglyphs[k] = NULL;
start = ftell(temp);
putlong(temp,class);
putlong(temp,7*sizeof(uint32));
putlong(temp,0); /* Fill in later */
putlong(temp,0);
putlong(temp,0);
putlong(temp,0);
putlong(temp,0);
morx_lookupmap(temp,cglyphs,map,k); /* dump the class lookup table */
free( cglyphs ); free( map );
here = ftell(temp);
fseek(temp,start+2*sizeof(uint32),SEEK_SET);
putlong(temp,here-start); /* Point to start of state arrays */
fseek(temp,0,SEEK_END);
/* Now build the state machine */
/* Note: the ligofme list is so ordered that the longest ligatures come first */
/* we will depend on that in the case of "ffl", "ffi", "ff" */
state_max = 40; state_cnt = 2;
states = malloc(state_max*sizeof(struct transition *));
states[0] = calloc(class,sizeof(struct transition)); /* Initial state */
states[1] = calloc(class,sizeof(struct transition)); /* other Initial state */
for ( i=0; i<gcnt; ++i ) {
if ( state_cnt>=state_max )
states = realloc(states,(state_max += 40)*sizeof(struct transition *));
base = state_cnt;
states[0][used[glyphs[i]->ttf_glyph]].next_state = state_cnt;
states[1][used[glyphs[i]->ttf_glyph]].next_state = state_cnt;
states[state_cnt++] = calloc(class,sizeof(struct transition));
for ( l=glyphs[i]->ligofme; l!=NULL; l=l->next ) if ( l->lig->subtable==sub ) {
if ( l->ccnt > maxccnt ) maxccnt = l->ccnt;
last = base;
for ( comp = l->components; comp!=NULL; comp=comp->next ) {
if ( states[last][used[comp->sc->ttf_glyph]].next_state==0 ) {
if ( comp->next==NULL )
states[last][used[comp->sc->ttf_glyph]].l = l;
else {
states[last][used[comp->sc->ttf_glyph]].next_state = state_cnt;
if ( state_cnt>=state_max )
states = realloc(states,(state_max += 40)*sizeof(struct transition *));
last = state_cnt;
states[state_cnt++] = calloc(class,sizeof(struct transition));
}
} else {
last = states[last][used[comp->sc->ttf_glyph]].next_state;
if ( comp->next==NULL ) {
/* this is where we depend on the ordering */
for ( j=0; j<class; ++j )
if ( states[last][j].next_state==0 && states[last][j].l==NULL ) {
states[last][j].l = l;
states[last][j].dontconsume = true;
/* the next state should continue to be 0 (initial) */
}
}
}
}
}
}
if ( anymarks ) {
/* behavior for a mark is the same everywhere: stay in current state */
/* do no operations. consume the mark */
for ( i=0; i<state_cnt; ++i ) {
states[i][class-1].next_state = i;
states[i][class-1].ismark = true;
}
}
/* Ok, we've got the state machine now. Convert it into apple's wierd */
/* (space saving) format */
trans = malloc(class*state_cnt*sizeof(struct trans_entries));
trans_cnt = 0;
for ( i=0; i<state_cnt; ++i ) for ( j=0; j<class; ++j ) {
if ( states[i][j].ismark )
k = trans_cnt;
else for ( k=0; k<trans_cnt; ++k ) {
if ( trans[k].next_state==states[i][j].next_state &&
(trans[k].flags&0x4000?1:0) == states[i][j].dontconsume &&
trans[k].l ==states[i][j].l )
break;
}
states[i][j].trans_ent = k;
if ( k==trans_cnt ) {
trans[k].next_state = states[i][j].next_state;
trans[k].l = states[i][j].l;
trans[k].flags = 0;
if ( states[i][j].dontconsume )
trans[k].flags = 0x4000;
else if ( states[i][j].ismark )
/* Do nothing */;
else if ( trans[k].next_state!=0 || trans[k].l!=NULL )
trans[k].flags = 0x8000;
if ( trans[k].l!=NULL )
trans[k].flags |= 0x2000;
trans[k].act_index = 0;
++trans_cnt;
}
}
/* Oops. Bug. */
/* Suppose we have two ligatures f+l=>fl & s+t->st. */
/* Suppose we get input "fst" */
/* Now the state machine we've built so far will go to the f branch, see */
/* the "s" and go back to state 0 */
/* Obviously that's wrong, we've lost the st. So either we go back to 0 */
/* but don't advance the glyph, or we take the transition from state 0 */
/* and copy it to here. The second is easier for me just now */
for ( i=2; i<state_cnt; ++i ) for ( j=4; j<class; ++j ) {
if ( states[i][j].trans_ent == 0 && states[0][j].trans_ent != 0 )
states[i][j].trans_ent = states[0][j].trans_ent;
}
/* Dump out the state machine */
for ( i=0; i<state_cnt; ++i ) for ( j=0; j<class; ++j )
putshort( temp, states[i][j].trans_ent );
/* Now figure out the ligature actions (and all the other tables) */
actions = malloc(trans_cnt*maxccnt*sizeof(uint32));
components = malloc(trans_cnt*maxccnt*sizeof(uint16));
lig_glyphs = malloc(trans_cnt*sizeof(uint16));
acnt = lcnt = 0;
for ( i=0; i<trans_cnt; ++i ) if ( trans[i].l!=NULL ) {
lig_glyphs[lcnt] = trans[i].l->lig->u.lig.lig->ttf_glyph;
/* component Glyphs get popped off the stack in the reverse order */
/* so we must built our tables backwards */
components[acnt+trans[i].l->ccnt-1] = lcnt;
actions[acnt+trans[i].l->ccnt-1] = 0x80000000 |
((acnt+trans[i].l->ccnt-1 - trans[i].l->first->ttf_glyph)&0x3fffffff);
for ( scl=trans[i].l->components,j=trans[i].l->ccnt-2; scl!=NULL; scl=scl->next, --j ) {
components[acnt+j] = 0;
actions[acnt+j] = (acnt+j - scl->sc->ttf_glyph)&0x3fffffff;
}
trans[i].act_index = acnt;
++lcnt;
acnt += trans[i].l->ccnt;
}
/* Now we know how big all the tables will be. Dump out their locations */
here = ftell(temp);
fseek(temp,start+3*sizeof(uint32),SEEK_SET);
putlong(temp,here-start); /* Point to start of entry array */
putlong(temp,here-start+6*trans_cnt); /* Point to start of actions */
putlong(temp,here-start+6*trans_cnt+4*acnt);/* Point to start of components */
putlong(temp,here-start+6*trans_cnt+6*acnt);/* Point to start of ligatures */
fseek(temp,0,SEEK_END);
/* Now dump the transitions */
for ( i=0; i<trans_cnt; ++i ) {
putshort(temp,trans[i].next_state);
putshort(temp,trans[i].flags);
putshort(temp,trans[i].act_index);
}
/* And the actions */
for ( i=0; i<acnt; ++i )
putlong(temp,actions[i]);
/* And the components */
for ( i=0; i<acnt; ++i )
putshort(temp,components[i]);
/* Do A simple check on the validity of what we've done */
if ( here+6*trans_cnt+6*acnt != ftell(temp) )
IError( "Offset wrong in morx ligature table\n" );
/* And finally the ligature glyph indeces */
for ( i=0; i<lcnt; ++i )
putshort(temp,lig_glyphs[i]);
/* clean up */
free(actions); free(components); free(lig_glyphs);
free(trans);
for ( i=0; i<state_cnt; ++i )
free(states[i]);
free(states);
free(used);
}
static struct feature *aat_dumpmorx_ligatures(struct alltabs *at, SplineFont *sf,
FILE *temp, struct feature *features, struct lookup_subtable *sub) {
int i, k, gcnt;
SplineChar *sc, *ssc, **glyphs;
struct feature *cur;
LigList *l;
glyphs = malloc((at->maxp.numGlyphs+1)*sizeof(SplineChar *));
for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
sf->glyphs[i]->ticked = false;
for ( i=0; i<at->gi.gcnt; ++i )
if ( at->gi.bygid[i]!=-1 && !(sc=sf->glyphs[at->gi.bygid[i]])->ticked &&
(l = LigListMatchSubtable(sf,sc->ligofme,sub))!=NULL ) {
int ignoremarks = sub->lookup->lookup_flags & pst_ignorecombiningmarks ? 1 : 0 ;
for ( k=i, gcnt=0; k<at->gi.gcnt; ++k )
if ( at->gi.bygid[k]!=-1 &&
(ssc=sf->glyphs[at->gi.bygid[k]])!=NULL && !ssc->ticked &&
LigListMatchSubtable(sf,ssc->ligofme,sub)) {
glyphs[gcnt++] = ssc;
ssc->ticked = true;
}
glyphs[gcnt] = NULL;
cur = featureFromSubtable(sf,sub);
cur->next = features;
features = cur;
cur->subtable_type = 2; /* ligature */
cur->feature_start = ftell(temp);
morx_dumpLigaFeature(temp,glyphs,gcnt,sub,at,sf,ignoremarks);
if ( (ftell(temp)-cur->feature_start)&1 )
putc('\0',temp);
if ( (ftell(temp)-cur->feature_start)&2 )
putshort(temp,0);
cur->feature_len = ftell(temp)-cur->feature_start;
cur->r2l = sub->lookup->lookup_flags&pst_r2l ? true : false;
}
free(glyphs);
return( features);
}
static void morx_dumpnestedsubs(FILE *temp,SplineFont *sf,OTLookup *otl,struct glyphinfo *gi) {
int i, j, gcnt;
PST *pst;
SplineChar **glyphs, *sc;
uint16 *map;
struct lookup_subtable *sub = otl->subtables; /* Mac can't have more than one subtable/lookup */
for ( j=0; j<2; ++j ) {
gcnt = 0;
for ( i = 0; i<gi->gcnt; ++i ) if ( gi->bygid[i]!=-1 ) {
for ( pst=sf->glyphs[gi->bygid[i]]->possub;
pst!=NULL && pst->subtable!=sub; pst=pst->next );
if ( pst!=NULL && pst->type==pst_substitution &&
(sc=SFGetChar(sf,-1,pst->u.subs.variant))!=NULL &&
sc->ttf_glyph!=-1 ) {
if ( j ) {
glyphs[gcnt] = sf->glyphs[gi->bygid[i]];
map[gcnt] = sc->ttf_glyph;
}
++gcnt;
}
}
if ( !j ) {
glyphs = malloc((gcnt+1)*sizeof(SplineChar *));
map = malloc(gcnt*sizeof(uint16));
glyphs[gcnt] = NULL;
}
}
morx_lookupmap(temp,glyphs,map,gcnt);
free(glyphs);
free(map);
}
static uint16 *NamesToGlyphs(SplineFont *sf,char *names,uint16 *cnt) {
char *pt, *start;
int c, ch;
uint16 *ret;
SplineChar *sc;
for ( c=0, pt=names; *pt; ++pt )
if ( *pt==' ' ) ++c;
ret = malloc((c+1)*sizeof(uint16));
for ( c=0, pt=names; *pt; ) {
while ( *pt==' ' ) ++pt;
if ( *pt=='\0' )
break;
start = pt;
while ( *pt!=' ' && *pt!='\0' ) ++pt;
ch = *pt; *pt='\0';
sc = SFGetChar(sf,-1,start);
*pt = ch;
if ( sc!=NULL && sc->ttf_glyph!=-1 )
ret[c++] = sc->ttf_glyph;
}
*cnt = c;
return( ret );
}
static int morx_dumpASM(FILE *temp,ASM *sm, struct alltabs *at, SplineFont *sf ) {
int i, j, k, gcnt, ch;
char *pt, *end;
uint16 *map;
SplineChar **glyphs, *sc;
int stcnt, tcnt;
struct ins { char *names; uint16 len,pos; uint16 *glyphs; } *subsins=NULL;
OTLookup **subslookups=NULL;
uint32 start, here, substable_pos, state_offset;
struct transdata { uint16 transition, mark_index, cur_index; } *transdata;
struct trans { uint16 ns, flags, mi, ci; } *trans;
int ismort = sm->type == asm_kern;
FILE *kernvalues;
for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
sf->glyphs[i]->lsidebearing = 1;
gcnt = 0;
for ( i=4; i<sm->class_cnt; ++i ) {
for ( pt = sm->classes[i]; ; pt=end ) {
while ( *pt==' ' ) ++pt;
if ( *pt=='\0' )
break;
for ( end=pt; *end!='\0' && *end!=' '; ++end );
ch = *end; *end = '\0';
sc = SFGetChar(sf,-1,pt);
*end = ch;
if ( sc!=NULL ) {
sc->lsidebearing = i;
++gcnt;
}
}
}
glyphs = malloc((gcnt+1)*sizeof(SplineChar *));
map = malloc((gcnt+1)*sizeof(uint16));
gcnt = 0;
for ( i=0; i<at->gi.gcnt; ++i ) if ( at->gi.bygid[i]!=-1 && sf->glyphs[at->gi.bygid[i]]->lsidebearing!=1 ) {
glyphs[gcnt] = sf->glyphs[at->gi.bygid[i]];
map[gcnt++] = sf->glyphs[at->gi.bygid[i]]->lsidebearing;
}
glyphs[gcnt] = NULL;
/* Give each subs tab an index into the mac's substitution lookups */
transdata = calloc(sm->state_cnt*sm->class_cnt,sizeof(struct transdata));
stcnt = 0;
subslookups = NULL; subsins = NULL;
if ( sm->type==asm_context ) {
subslookups = malloc(2*sm->state_cnt*sm->class_cnt*sizeof(OTLookup));
for ( j=0; j<sm->state_cnt*sm->class_cnt; ++j ) {
struct asm_state *this = &sm->state[j];
transdata[j].mark_index = transdata[j].cur_index = 0xffff;
if ( this->u.context.mark_lookup!=NULL ) {
for ( i=0; i<stcnt; ++i )
if ( subslookups[i]==this->u.context.mark_lookup )
break;
if ( i==stcnt )
subslookups[stcnt++] = this->u.context.mark_lookup;
transdata[j].mark_index = i;
}
if ( this->u.context.cur_lookup!=NULL ) {
for ( i=0; i<stcnt; ++i )
if ( subslookups[i]==this->u.context.cur_lookup )
break;
if ( i==stcnt )
subslookups[stcnt++] = this->u.context.cur_lookup;
transdata[j].cur_index = i;
}
}
} else if ( sm->type==asm_insert ) {
subsins = malloc(2*sm->state_cnt*sm->class_cnt*sizeof(struct ins));
for ( j=0; j<sm->state_cnt*sm->class_cnt; ++j ) {
struct asm_state *this = &sm->state[j];
transdata[j].mark_index = transdata[j].cur_index = 0xffff;
if ( this->u.insert.mark_ins!=0 ) {
for ( i=0; i<stcnt; ++i )
if ( strcmp(subsins[i].names,this->u.insert.mark_ins)==0 )
break;
if ( i==stcnt ) {
subsins[stcnt].pos = stcnt==0 ? 0 : subsins[stcnt-1].pos +
subsins[stcnt-1].len;
subsins[stcnt].names = this->u.insert.mark_ins;
subsins[stcnt].glyphs = NamesToGlyphs(sf,subsins[stcnt].names,&subsins[stcnt].len);
++stcnt;
}
transdata[j].mark_index = subsins[i].pos;
}
if ( this->u.insert.cur_ins!=0 ) {
for ( i=0; i<stcnt; ++i )
if ( strcmp(subsins[i].names,this->u.insert.cur_ins)==0 )
break;
if ( i==stcnt ) {
subsins[stcnt].pos = stcnt==0 ? 0 : subsins[stcnt-1].pos +
subsins[stcnt-1].len;
subsins[stcnt].names = this->u.insert.cur_ins;
subsins[stcnt].glyphs = NamesToGlyphs(sf,subsins[stcnt].names,&subsins[stcnt].len);
++stcnt;
}
transdata[j].cur_index = subsins[i].pos;
}
}
} else if ( sm->type==asm_kern ) {
int off=0;
kernvalues = tmpfile();
for ( j=0; j<sm->state_cnt*sm->class_cnt; ++j ) {
struct asm_state *this = &sm->state[j];
transdata[j].mark_index = 0xffff;
if ( this->u.kern.kcnt!=0 ) {
for ( k=0; k<j; ++k )
if ( sm->state[k].u.kern.kcnt==this->u.kern.kcnt &&
memcmp(sm->state[k].u.kern.kerns,this->u.kern.kerns,
this->u.kern.kcnt*sizeof(int16))==0 )
break;
if ( k!=j )
transdata[j].mark_index = transdata[k].mark_index;
else {
transdata[j].mark_index = off;
off += this->u.kern.kcnt*sizeof(int16);
/* kerning values must be output backwards */
for ( k=this->u.kern.kcnt-1; k>=1; --k )
putshort(kernvalues,this->u.kern.kerns[k]&~1);
/* And the last one must be odd */
putshort(kernvalues,this->u.kern.kerns[0]|1);
}
}
}
}
trans = malloc(sm->state_cnt*sm->class_cnt*sizeof(struct trans));
tcnt = 0;
for ( j=0; j<sm->state_cnt*sm->class_cnt; ++j ) {
struct asm_state *this = &sm->state[j];
for ( i=0; i<tcnt; ++i )
if ( trans[i].ns==this->next_state && trans[i].flags==this->flags &&
trans[i].mi==transdata[j].mark_index &&
trans[i].ci==transdata[j].cur_index )
break;
if ( i==tcnt ) {
trans[tcnt].ns = this->next_state;
trans[tcnt].flags = this->flags;
trans[tcnt].mi = transdata[j].mark_index;
trans[tcnt++].ci = transdata[j].cur_index;
}
transdata[j].transition = i;
}
/* Output the header */
start = ftell(temp);
if ( ismort /* old format still used for kerning */ ) {
putshort(temp,sm->class_cnt);
putshort(temp,5*sizeof(uint16)); /* class offset */
putshort(temp,0); /* state offset */
putshort(temp,0); /* transition entry offset */
putshort(temp,0); /* kerning values offset */
mort_classes(temp,sf,&at->gi); /* dump the class table */
} else {
putlong(temp,sm->class_cnt);
if ( sm->type==asm_indic ) {
putlong(temp,4*sizeof(uint32)); /* class offset */
putlong(temp,0); /* state offset */
putlong(temp,0); /* transition entry offset */
} else {
putlong(temp,5*sizeof(uint32)); /* class offset */
putlong(temp,0); /* state offset */
putlong(temp,0); /* transition entry offset */
putlong(temp,0); /* substitution/insertion table offset */
}
morx_lookupmap(temp,glyphs,map,gcnt);/* dump the class lookup table */
}
free(glyphs); free(map);
state_offset = ftell(temp)-start;
if ( ismort ) {
fseek(temp,start+2*sizeof(uint16),SEEK_SET);
putshort(temp,state_offset); /* Point to start of state arrays */
} else {
fseek(temp,start+2*sizeof(uint32),SEEK_SET);
putlong(temp,state_offset); /* Point to start of state arrays */
}
fseek(temp,0,SEEK_END);
if ( ismort ) {
for ( j=0; j<sm->state_cnt*sm->class_cnt; ++j )
putc(transdata[j].transition,temp);
if ( ftell(temp)&1 )
putc(0,temp); /* Pad to a word boundry */
} else {
for ( j=0; j<sm->state_cnt*sm->class_cnt; ++j )
putshort(temp,transdata[j].transition);
}
free(transdata);
here = ftell(temp);
if ( ismort ) {
fseek(temp,start+3*sizeof(uint16),SEEK_SET);
putshort(temp,here-start); /* Point to start of transition arrays */
} else {
fseek(temp,start+3*sizeof(uint32),SEEK_SET);
putlong(temp,here-start); /* Point to start of transition arrays */
}
fseek(temp,0,SEEK_END);
/* Now the transitions */
if ( sm->type==asm_kern ) {
substable_pos = here+tcnt*2*sizeof(int16);
for ( i=0; i<tcnt; ++i ) {
/* mort tables use an offset rather than the state number */
putshort(temp,trans[i].ns*sm->class_cnt+state_offset);
if ( trans[i].mi!=0xffff )
trans[i].flags |= substable_pos-start+trans[i].mi;
putshort(temp,trans[i].flags);
}
} else {
for ( i=0; i<tcnt; ++i ) {
putshort(temp,trans[i].ns);
putshort(temp,trans[i].flags);
if ( sm->type!=asm_indic && sm->type!=asm_kern ) {
putshort(temp,trans[i].mi );
putshort(temp,trans[i].ci );
}
}
}
free(trans);
if ( sm->type==asm_context ) {
substable_pos = ftell(temp);
fseek(temp,start+4*sizeof(uint32),SEEK_SET);
putlong(temp,substable_pos-start); /* Point to start of substitution lookup offsets */
fseek(temp,0,SEEK_END);
/* And finally the substitutions */
for ( i=0; i<stcnt; ++i )
putlong(temp,0); /* offsets to the substitutions */
for ( i=0; i<stcnt; ++i ) {
here = ftell(temp);
fseek(temp,substable_pos+i*sizeof(uint32),SEEK_SET);
putlong(temp,here-substable_pos);
fseek(temp,0,SEEK_END);
morx_dumpnestedsubs(temp,sf,subslookups[i],&at->gi);
}
free(subslookups);
} else if ( sm->type==asm_insert ) {
substable_pos = ftell(temp);
fseek(temp,start+4*sizeof(uint32),SEEK_SET);
putlong(temp,substable_pos-start); /* Point to start of insertions */
fseek(temp,0,SEEK_END);
for ( i=0; i<stcnt; ++i ) {
for ( j=0; j<subsins[i].len; ++j )
putshort(temp,subsins[i].glyphs[j]);
free(subsins[i].glyphs);
}
free(subsins);
} else if ( sm->type==asm_kern ) {
if ( substable_pos!=ftell(temp) )
IError( "Kern Values table in wrong place.\n" );
fseek(temp,start+4*sizeof(uint16),SEEK_SET);
putshort(temp,substable_pos-start); /* Point to start of insertions */
fseek(temp,0,SEEK_END);
if ( !ttfcopyfile(temp,kernvalues,substable_pos,"kern-subtable")) at->error = true;
}
return( true );
}
static struct feature *aat_dumpmorx_asm(struct alltabs *at, SplineFont *sf,
FILE *temp, struct feature *features, ASM *sm) {
struct feature *cur;
cur = featureFromSubtable(sf,sm->subtable);
cur->vertOnly = sm->flags&0x8000?1:0;
cur->r2l = sm->flags&0x4000?1:0;
cur->subtable_type = sm->type; /* contextual glyph subs */
cur->feature_start = ftell(temp);
if ( morx_dumpASM(temp,sm,at,sf)) {
cur->next = features;
features = cur;
if ( (ftell(temp)-cur->feature_start)&1 )
putc('\0',temp);
if ( (ftell(temp)-cur->feature_start)&2 )
putshort(temp,0);
cur->feature_len = ftell(temp)-cur->feature_start;
} else
chunkfree(cur,sizeof(struct feature));
return( features);
}
static struct feature *aat_dumpmorx_cvtopentype(struct alltabs *at, SplineFont *sf,
FILE *temp, struct feature *features, struct lookup_subtable *sub) {
ASM *sm;
if ( FPSTisMacable(sf,sub->fpst)) {
sm = ASMFromFPST(sf,sub->fpst,true);
if ( sm!=NULL ) {
features = aat_dumpmorx_asm(at,sf,temp,features,sm);
ASMFree(sm);
}
}
return( features );
}
static int IsOtfArabicFormFeature(OTLookup *otl) {
FeatureScriptLangList *fl;
for ( fl=otl->features; fl!=NULL; fl=fl->next ) {
if (( fl->featuretag == CHR('i','n','i','t') ||
fl->featuretag==CHR('m','e','d','i') ||
fl->featuretag==CHR('f','i','n','a') ||
fl->featuretag==CHR('i','s','o','l') ) &&
scriptsHaveDefault(fl->scripts))
return( true );
}
return( false );
}
static int HasCursiveConnectionSM(SplineFont *sf) {
int featureType, featureSetting;
uint32 tag;
ASM *sm;
if ( OTTagToMacFeature(CHR('i','s','o','l'),&featureType,&featureSetting) ) {
tag = (featureType<<16) | featureSetting;
for ( sm = sf->sm; sm!=NULL; sm=sm->next ) {
if ( sm->subtable->lookup->features->featuretag==tag )
return( true );
}
}
for ( sm = sf->sm; sm!=NULL; sm=sm->next ) {
if ( sm->subtable->lookup->features->featuretag==CHR('i','s','o','l') )
return( true );
}
return( false );
}
static uint32 *FormedScripts(SplineFont *sf) {
OTLookup *otl;
uint32 *ret = NULL;
int scnt=0, smax=0;
FeatureScriptLangList *fl;
struct scriptlanglist *sl;
int i;
for ( otl= sf->gsub_lookups; otl!=NULL; otl=otl->next ) {
if ( otl->lookup_type == gsub_single ) {
for ( fl=otl->features; fl!=NULL; fl=fl->next ) {
if ( fl->featuretag == CHR('i','n','i','t') ||
fl->featuretag==CHR('m','e','d','i') ||
fl->featuretag==CHR('f','i','n','a') ||
fl->featuretag==CHR('i','s','o','l') ) {
for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) {
for ( i=0; i<sl->lang_cnt; ++i ) {
if ( (i<MAX_LANG ? sl->langs[i] : sl->morelangs[i-MAX_LANG])==DEFAULT_LANG ) {
if ( scnt<=smax )
ret = realloc(ret,(smax+=5)*sizeof(uint32));
ret[scnt++] = sl->script;
}
}
}
}
}
}
}
if ( scnt==0 )
return( NULL );
if ( scnt<=smax )
ret = realloc(ret,(smax+=1)*sizeof(uint32));
ret[scnt] = 0;
return( ret );
}
int Macable(SplineFont *sf, OTLookup *otl) {
int ft, fs;
FeatureScriptLangList *features;
switch ( otl->lookup_type ) {
/* These lookup types are mac only */
case kern_statemachine: case morx_indic: case morx_context: case morx_insert:
return( true );
/* These lookup types or OpenType only */
case gsub_multiple: case gsub_alternate:
case gpos_single: case gpos_cursive: case gpos_mark2base:
case gpos_mark2ligature: case gpos_mark2mark:
return( false );
/* These are OpenType only, but they might be convertable to a state */
/* machine */
case gsub_context:
case gsub_contextchain: case gsub_reversecchain:
case gpos_context: case gpos_contextchain:
if ( sf==NULL || sf->sm!=NULL )
return( false );
/* Else fall through into the test on the feature tag */;
/* These two can be expressed in both, and might be either */
case gsub_single: case gsub_ligature: case gpos_pair:
for ( features = otl->features; features!=NULL; features = features->next ) {
if ( features->ismac || OTTagToMacFeature(features->featuretag,&ft,&fs))
return( true );
}
}
return( false );
}
static struct feature *aat_dumpmorx_cvtopentypeforms(struct alltabs *at, SplineFont *sf,
FILE *temp, struct feature *features) {
ASM *sm;
uint32 *scripts;
int featureType, featureSetting;
int i;
OTLookup *otl;
if ( sf->cidmaster!=NULL )
sf = sf->cidmaster;
else if ( sf->mm!=NULL ) sf=sf->mm->normal;
for ( otl=sf->gsub_lookups; otl!=NULL; otl=otl->next )
if ( Macable(sf,otl) && otl->lookup_type==gsub_single && IsOtfArabicFormFeature(otl))
otl->ticked = true;
if ( OTTagToMacFeature(CHR('i','s','o','l'),&featureType,&featureSetting) ) {
scripts = FormedScripts(sf);
for ( i=0; scripts[i]!=0; ++i ) {
sm = ASMFromOpenTypeForms(sf,scripts[i]);
if ( sm!=NULL ) {
features = aat_dumpmorx_asm(at,sf,temp,features,sm);
ASMFree(sf->sm);
}
}
free(scripts);
}
return( features );
}
static struct feature *featuresReverse(struct feature *features) {
struct feature *p, *n;
p = NULL;
while ( features!=NULL ) {
n = features->next;
features->next = p;
p = features;
features = n;
}
return( p );
}
static struct feature *featuresOrderByType(struct feature *features) {
struct feature *f, **all;
int i, j, cnt/*, saw_default*/;
for ( cnt=0, f=features; f!=NULL; f=f->next, ++cnt );
if ( cnt==1 ) {
return( features );
}
all = malloc(cnt*sizeof(struct feature *));
for ( i=0, f=features; f!=NULL; f=f->next, ++i )
all[i] = f;
for ( i=0; i<cnt-1; ++i ) for ( j=i+1; j<cnt; ++j ) {
if ( all[i]->featureType>all[j]->featureType ||
(all[i]->featureType==all[j]->featureType && all[i]->featureSetting>all[j]->featureSetting )) {
f = all[i];
all[i] = all[j];
all[j] = f;
}
}
for ( i=0; i<cnt-1; ++i )
all[i]->nexttype = all[i+1];
all[cnt-1]->nexttype = NULL;
features = all[0];
free( all );
return( features );
}
static struct feature *AddExclusiveNoops(SplineFont *sf, struct feature *features) {
struct feature *f, *n, *def, *p, *t;
/* mutually exclusive features need to have a setting which does nothing */
for ( f=features; f!=NULL; f=n ) {
n= f->nexttype;
if ( f->mf!=NULL && f->mf->ismutex ) {
def = NULL;
for ( n=f; n!=NULL && n->featureType==f->featureType; n=n->nexttype ) {
if ( n->featureSetting==f->mf->default_setting )
def = n;
}
if ( def==NULL ) {
t = chunkalloc(sizeof(struct feature));
*t = *f;
t->feature_start = 0; t->feature_len=0; t->next = NULL;
t->featureSetting = f->mf->default_setting;
t->ms = FindMacSetting(sf,t->featureType,f->mf->default_setting,&t->sms);
t->flag = 0;
t->dummyOff = true;
if ( f==features )
p = NULL;
else
for ( p=features; p->nexttype!=f; p=p->nexttype );
n = f;
while ( n!=NULL && n->featureType==t->featureType && n->featureSetting<t->featureSetting ) {
p = n;
n = n->nexttype;
}
t->nexttype = n;
if ( p==NULL )
features = t;
else
p->nexttype = t;
while ( n!=NULL && n->featureType==t->featureType )
n=n->nexttype;
}
}
}
return( features );
}
static void SetExclusiveOffs(struct feature *features) {
struct feature *f, *n;
int offFlags;
/* mutually exclusive features need to have a setting which does nothing */
for ( f=features; f!=NULL; f=n ) {
n= f->nexttype;
if ( f->mf!=NULL && f->mf->ismutex ) {
offFlags=0;
for ( n=f; n!=NULL && n->featureType==f->featureType; n=n->nexttype ) {
offFlags |= n->flag;
}
for ( n=f; n!=NULL && n->featureType==f->featureType; n=n->nexttype )
n->offFlags = ~(offFlags&~n->flag);
}
}
return;
}
static void aat_dumpfeat(struct alltabs *at, SplineFont *sf, struct feature *feature) {
int scnt, fcnt, cnt;
struct feature *f, *n, *p;
int k;
uint32 offset;
int strid = at->next_strid;
int fn=0;
MacFeat *mf, *smf;
struct macsetting *ms, *sms;
/* Dump the 'feat' table which is a connection between morx features and */
/* the name table */
/* We do three passes. The first just calculates how much space we will need */
/* the second provides names for the feature types */
/* and the third provides names for the feature settings */
/* As we fill up the feat table we also create an array of strings */
/* (strid, char *pointer) which will be used by the 'name' table to */
/* give names to the features and their settings */
/* The mac documentation says that the features should be sorted by feature type */
/* This is a lie. Features should appear in the same order they appear */
/* in the morx table, otherwise WorldText goes blooie */
/* WorldText doesn't exist any more. Perhaps the morx table needs to be */
/* sorted by feature id too? No, it can't be. Feature 0 must come last */
if ( feature==NULL )
return;
fcnt = scnt = 0;
for ( k=0; k<3; ++k ) {
if ( k==1 ) {
/* FeatureName entry for All Typographics */
mf = FindMacFeature(sf,0,&smf);
if ( (mf!=NULL && mf->featname!=NULL) || (smf!=NULL && smf->featname!=NULL)) {
at->feat_name[fn].mn = mf!=NULL ? mf->featname : NULL;
at->feat_name[fn].smn = smf!=NULL ? smf->featname : NULL;
at->feat_name[fn++].strid = strid;
}
putshort(at->feat,0);
putshort(at->feat,1);
putlong(at->feat,offset);
putshort(at->feat,0x0000); /* non exclusive */
putshort(at->feat,strid++);
offset += 1*4; /* (1 setting, 4 bytes) All Features */
} else if ( k==2 ) {
/* Setting Name Array for All Typographic Features */
ms = FindMacSetting(sf,0,0,&sms);
if ( (ms!=NULL && ms->setname!=NULL) || (sms!=NULL && sms->setname!=NULL)) {
at->feat_name[fn].mn = ms!=NULL ? ms->setname: NULL;
at->feat_name[fn].smn = sms!=NULL ? sms->setname: NULL;
at->feat_name[fn++].strid = strid;
}
putshort(at->feat,0);
putshort(at->feat,strid++);
}
for ( f=feature; f!=NULL; f=n ) {
cnt=1;
if ( k!=2 ) {
p = f;
for ( n=f->nexttype; n!=NULL && n->featureType==f->featureType; n = n->nexttype ) {
if ( p->featureSetting!=n->featureSetting ) {
++cnt;
p = n;
}
}
} else {
p = f;
for ( n=f; n!=NULL && n->featureType==f->featureType; n = n->nexttype ) {
if ( n==f || p->featureSetting!=n->featureSetting ) {
if (( n->ms!=NULL && n->ms->setname!=NULL ) ||
( n->sms!=NULL && n->sms->setname!=NULL)) {
at->feat_name[fn].mn = n->ms!=NULL ? n->ms->setname : NULL;
at->feat_name[fn].smn = n->sms!=NULL ? n->sms->setname : NULL;
at->feat_name[fn++].strid = strid;
}
putshort(at->feat,n->featureSetting);
putshort(at->feat,strid++);
p = n;
}
}
}
if ( k==0 ) {
++fcnt;
scnt += cnt;
} else if ( k==1 ) {
if ( (f->mf!=NULL && f->mf->featname!=NULL) || (f->smf!=NULL && f->smf->featname!=NULL) ) {
at->feat_name[fn].mn = f->mf!=NULL ? f->mf->featname : NULL;
at->feat_name[fn].smn = f->smf!=NULL ? f->smf->featname : NULL;
at->feat_name[fn++].strid = strid;
}
putshort(at->feat,f->featureType);
putshort(at->feat,cnt);
putlong(at->feat,offset);
putshort(at->feat,f->mf!=NULL && f->mf->ismutex?(0xc000|f->mf->default_setting):
0);
putshort(at->feat,strid++);
offset += 4*cnt;
}
}
if ( k==0 ) {
++fcnt; /* Add one for "All Typographic Features" */
++scnt; /* Add one for All Features */
at->feat = tmpfile();
at->feat_name = malloc((fcnt+scnt+1)*sizeof(struct feat_name));
putlong(at->feat,0x00010000);
putshort(at->feat,fcnt);
putshort(at->feat,0);
putlong(at->feat,0);
offset = 12 /* header */ + fcnt*12;
}
}
memset( &at->feat_name[fn],0,sizeof(struct feat_name));
at->next_strid = strid;
at->featlen = ftell(at->feat);
if ( at->featlen&2 )
putshort(at->feat,0);
}
static int featuresAssignFlagsChains(struct feature *features, struct feature *feature_by_type) {
int bit, cnt, chain, fcnt, i, mybit;
struct feature *f, *n, *p;
uint16 chains_features[32];
uint32 chains_bitindex[32]; /* Index for bit of first setting of this feature */
if ( features==NULL )
return( 0 );
/* A feature may have several subtables which need not be contiguous in */
/* the feature list */
/* Indeed we could have a feature in several different chains */
/* Sigh */
/* we figure out how many possible settings there are for each feature */
/* and reserve that many bits for the feature in all chains in which it */
/* occurs */
/* Note that here we count dummy settings (they need turn off bits) */
/* so we use feature_by_type */
for ( f=feature_by_type; f!=NULL; f=n ) {
cnt=0;
p = NULL;
for ( n=f; n!=NULL && n->featureType==f->featureType; n=n->nexttype ) {
if ( p==NULL || n->featureSetting != p->featureSetting ) {
++cnt;
p = n;
}
n->setting_index = cnt-1;
}
for ( n=f; n!=NULL && n->featureType==f->featureType; n=n->nexttype )
n->setting_cnt = cnt;
}
/* When we counted flags we need to count the dummy features for turning */
/* things off. Those features live in features_by_type. When we put */
/* things in chains we want only the meaningful features, and we want */
/* them to be properly ordered. That we get from the "features" list */
fcnt = 0; chain = 0; bit=0;
for ( f=features; f!=NULL; f=f->next ) {
for ( i=0; i<fcnt && chains_features[i]!=f->featureType; ++i );
if ( i==fcnt ) {
if ( bit+f->setting_cnt>=32 ) {
++chain;
bit = 0;
fcnt = 0;
}
chains_features[fcnt] = f->featureType;
chains_bitindex[fcnt++] = bit;
mybit = bit;
bit += f->setting_cnt;
} else
mybit = chains_bitindex[i];
f->real_index = mybit+f->setting_index;
f->flag = 1<<f->real_index;
if ( f->mf!=NULL && f->mf->ismutex ) {
int off = (~((~0)<<f->setting_cnt))<<mybit;
off &= !f->flag;
f->offFlags = off;
} else {
if ( f->featureSetting&1 ) {
for ( n=feature_by_type; n!=NULL &&
(n->featureType!=f->featureType || n->featureSetting!=f->featureSetting+1);
n=n->next );
} else {
for ( n=feature_by_type; n!=NULL &&
(n->featureType!=f->featureType || n->featureSetting!=f->featureSetting+1);
n=n->next );
}
if ( n!=NULL )
f->offFlags = 1<<(mybit+n->setting_index);
else
f->offFlags = ~0;
}
f->chain = chain;
}
return( chain+1 );
}
static void morxDumpChain(struct alltabs *at,struct feature *features,
struct feature *features_by_type, int chain, FILE *temp) {
uint32 def_flags=0;
struct feature *f, *n;
uint32 chain_start, end;
char *buf;
int len, tot, fs_cnt, sub_cnt;
struct feature *all[32];
int i,offFlags, last_ri=-1, last_f=-1, ri;
memset(all,0,sizeof(all));
for ( f=features, fs_cnt=sub_cnt=0; f!=NULL; f=f->next ) {
if ( f->chain==chain ) {
if ( all[f->real_index]==NULL ) {
int base = f->real_index-f->setting_index;
/* Note we use features_by_type here. It will have the default*/
/* settings for features, and will be ordered nicely */
for ( n=features_by_type; n!=NULL; n=n->nexttype ) {
if ( n->featureType==f->featureType && n->chain==chain ) {
n->nextsame = all[base+n->setting_index];
all[base+n->setting_index] = n;
if ( n->ms!=NULL && n->ms->initially_enabled )
def_flags |= n->flag;
}
}
}
++sub_cnt;
}
}
/* Chain header */
chain_start = ftell(at->morx);
putlong(at->morx,def_flags);
putlong(at->morx,0); /* Fix up length later */
putlong(at->morx,0); /* fix up feature count */
putlong(at->morx,sub_cnt); /* subtable cnt */
/* Features */
fs_cnt = 0;
for ( i=0; i<32; ++i ) if ( all[i]!=NULL ) {
putshort(at->morx,all[i]->featureType);
putshort(at->morx,all[i]->featureSetting);
if ( all[i]->dummyOff ) {
putlong(at->morx,0);
if ( last_f==all[i]->featureType )
ri = last_ri;
else if ( i<31 && all[i+1]!=NULL && all[i+1]->featureType == all[i]->featureType )
ri = i+1 - all[i+1]->real_index;
else
ri = 0; /* This can't happen */
} else {
putlong(at->morx,1<<i);
ri = i-all[i]->real_index;
last_ri = ri; last_f = all[i]->featureType;
}
offFlags = all[i]->offFlags;
if ( ri>0 )
offFlags<<=(ri);
else if ( ri<0 )
offFlags>>=(-ri);
putlong(at->morx,offFlags);
++fs_cnt;
if ( all[i]->needsOff && (i==31 || all[i+1]==NULL ||
all[i+1]->featureType!=all[i]->featureType ||
all[i+1]->featureSetting!=all[i]->featureSetting+1 )) {
putshort(at->morx,all[i]->featureType);
putshort(at->morx,all[i]->featureSetting+1);
putlong(at->morx,0);
putlong(at->morx,all[i]->offFlags & ~all[i]->flag );
++fs_cnt;
}
/* I used to have code to output the default setting of a mutex */
/* but I should already have put that in the feature list */
}
/* The feature list of every chain must end with these two features */
putshort(at->morx,0); /* All Typo Features */
putshort(at->morx,0); /* All Features */
putlong(at->morx,0xffffffff); /* enable */
putlong(at->morx,0xffffffff); /* disable */
putshort(at->morx,0); /* All Typo Features */
putshort(at->morx,1); /* No Features */
putlong(at->morx,0); /* enable */
putlong(at->morx,0); /* disable */
fs_cnt += 2;
buf = malloc(16*1024);
/* Subtables */
for ( f=features; f!=NULL; f=f->next ) if ( f->chain==chain ) {
putlong(at->morx,f->feature_len+12); /* Size of header needs to be added */
putlong(at->morx,(f->vertOnly?0x80000000:f->r2l?0x40000000:0) | f->subtable_type);
putlong(at->morx,f->flag);
tot = f->feature_len;
fseek(temp, f->feature_start, SEEK_SET);
while ( tot!=0 ) {
len = tot;
if ( len>16*1024 ) len = 16*1024;
len = fread(buf,1,len,temp);
len = fwrite(buf,1,len,at->morx);
if ( len<=0 ) {
IError( "Disk error\n" );
break;
}
tot -= len;
}
}
free(buf);
/* Pad chain to a multiple of four */
if ( (ftell(at->morx)-chain_start)&1 )
putc('\0',at->morx);
if ( (ftell(at->morx)-chain_start)&2 )
putshort(at->morx,0);
end = ftell(at->morx);
fseek(at->morx,chain_start+4,SEEK_SET);
putlong(at->morx,end-chain_start);
putlong(at->morx,fs_cnt);
fseek(at->morx,0,SEEK_END);
}
void aat_dumpmorx(struct alltabs *at, SplineFont *sf) {
FILE *temp = tmpfile();
struct feature *features = NULL, *features_by_type;
int nchains, i;
OTLookup *otl;
struct lookup_subtable *sub;
/* Arabic Form features all need to be merged together and formed into */
/* a cursive connection state machine. So the first time we see one of */
/* we handle all of them. After that we ignore all of them. Note: if */
/* OpenType has them happening in different orders, that information */
/* will be lost. All will be processed at once. */
for ( otl = sf->gsub_lookups; otl!=NULL; otl=otl->next )
otl->ticked = false;
SFLigaturePrepare(sf);
/* Retain the same lookup ordering */
for ( otl = sf->gsub_lookups; otl!=NULL; otl=otl->next ) {
if ( !Macable(sf,otl))
continue;
if ( otl->lookup_type==gsub_single && IsOtfArabicFormFeature(otl) ) {
if ( otl->ticked )
/* Already processed */;
else if ( HasCursiveConnectionSM(sf) )
/* Skip the OpenType conversion and use the native state machine */;
else
features = aat_dumpmorx_cvtopentypeforms(at,sf,temp,features);
} else {
for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
switch ( otl->lookup_type ) {
case gsub_single:
features = aat_dumpmorx_substitutions(at,sf,temp,features,sub);
break;
case gsub_ligature:
features = aat_dumpmorx_ligatures(at,sf,temp,features,sub);
break;
case morx_indic: case morx_context: case morx_insert:
features = aat_dumpmorx_asm(at,sf,temp,features,sub->sm);
break;
default:
if ( sf->sm==NULL )
features = aat_dumpmorx_cvtopentype(at,sf,temp,features,sub);
}
}
}
}
SFLigatureCleanup(sf);
if ( features==NULL ) {
fclose(temp);
return;
}
/* The features are in reverse execution order */
features = featuresReverse(features);
/* But the feature table requires them in numeric order */
features_by_type = featuresOrderByType(features);
features_by_type = AddExclusiveNoops(sf,features_by_type);
aat_dumpfeat(at, sf, features_by_type);
nchains = featuresAssignFlagsChains(features,features_by_type);
SetExclusiveOffs(features_by_type);
at->morx = tmpfile();
putlong(at->morx,0x00020000);
putlong(at->morx,nchains);
for ( i=0; i<nchains; ++i )
morxDumpChain(at,features,features_by_type,i,temp);
fclose(temp);
morxfeaturesfree(features_by_type);
at->morxlen = ftell(at->morx);
if ( at->morxlen&1 )
putc('\0',at->morx);
if ( (at->morxlen+1)&2 )
putshort(at->morx,0);
}
/* ************************************************************************** */
/* ************************* The 'opbd' table ************************* */
/* ************************************************************************** */
int haslrbounds(SplineChar *sc, PST **left, PST **right) {
PST *pst;
*left = *right = NULL;
for ( pst=sc->possub; pst!=NULL ; pst=pst->next ) {
if ( pst->type == pst_position ) {
if ( PSTHasTag(pst,CHR('l','f','b','d')) ) {
*left = pst;
if ( *right )
return( true );
} else if ( PSTHasTag(pst,CHR('r','t','b','d')) ) {
*right = pst;
if ( *left )
return( true );
}
}
}
return( *left!=NULL || *right!=NULL );
}
void aat_dumpopbd(struct alltabs *at, SplineFont *_sf) {
int i, j, k, l, seg_cnt, tot, last, offset;
PST *left, *right;
FILE *opbd=NULL;
/* We do four passes. The first just calculates how much space we will need (if any) */
/* the second provides the top-level lookup table structure */
/* the third provides the arrays of offsets needed for type 4 lookup tables */
/* the fourth provides the actual data on the optical bounds */
SplineChar *sc;
for ( k=0; k<4; ++k ) {
for ( i=seg_cnt=tot=0; i<at->gi.gcnt; ++i ) if ( at->gi.bygid[i]!=-1 ) {
l = 0;
sc = _sf->glyphs[at->gi.bygid[i]];
if ( haslrbounds(sc,&left,&right) ) {
if ( k==1 )
tot = 0;
else if ( k==2 ) {
putshort(opbd,offset);
offset += 8;
} else if ( k==3 ) {
putshort(opbd,left!=NULL?-left->u.pos.xoff:0);
putshort(opbd,0); /* top */
putshort(opbd,right!=NULL?-right->u.pos.h_adv_off:0);
putshort(opbd,0); /* bottom */
}
last = i;
for ( j=i+1, ++tot; j<at->gi.gcnt; ++j ) {
if ( at->gi.bygid[i]==-1 || !haslrbounds(_sf->glyphs[at->gi.bygid[j]],&left,&right) )
break;
++tot;
last = j;
if ( k==2 ) {
putshort(opbd,offset);
offset += 8;
} else if ( k==3 ) {
putshort(opbd,left!=NULL?-left->u.pos.xoff:0);
putshort(opbd,0); /* top */
putshort(opbd,right!=NULL?-right->u.pos.h_adv_off:0);
putshort(opbd,0); /* bottom */
}
}
if ( k==1 ) {
putshort(opbd,last);
putshort(opbd,i);
putshort(opbd,offset);
offset += 2*tot;
}
++seg_cnt;
i = j-1;
}
}
if ( k==0 ) {
if ( seg_cnt==0 )
return;
opbd = tmpfile();
putlong(opbd, 0x00010000); /* version */
putshort(opbd,0); /* data are distances (not control points) */
putshort(opbd,4); /* Lookup table format 4 */
/* Binary search header */
putshort(opbd,6); /* Entry size */
putshort(opbd,seg_cnt); /* Number of segments */
for ( j=0,l=1; l<=seg_cnt; l<<=1, ++j );
--j; l>>=1;
putshort(opbd,6*l);
putshort(opbd,j);
putshort(opbd,6*(seg_cnt-l));
/* offset from start of lookup, not table */
offset = 6*2/* format, binsearch*/ + seg_cnt*6 +6 /*flag entry */;
} else if ( k==1 ) { /* flag entry */
putshort(opbd,0xffff);
putshort(opbd,0xffff);
putshort(opbd,0);
}
}
at->opbd = opbd;
at->opbdlen = ftell(at->opbd);
if ( at->opbdlen&2 )
putshort(at->opbd,0);
}
/* ************************************************************************** */
/* ************************* The 'prop' table ************************* */
/* ************************************************************************** */
uint16 *props_array(SplineFont *sf,struct glyphinfo *gi) {
uint16 *props;
int i;
SplineChar *sc, *bsc;
int dir, isfloat, isbracket, offset, doit=false;
AnchorPoint *ap;
PST *pst;
int p;
props = calloc(gi->gcnt+1,sizeof(uint16));
props[gi->gcnt] = -1;
for ( i=0; i<gi->gcnt; ++i ) if ( (p = gi->bygid==NULL ? i : gi->bygid[i])!=-1 ) {
sc = sf->glyphs[p];
if ( sc!=NULL && (gi->bygid==NULL || sc->ttf_glyph!=-1 )) {
dir = 0;
if ( sc->unicodeenc>=0x10300 && sc->unicodeenc<=0x103ff )
dir = 0;
else if ( sc->unicodeenc>=0x10800 && sc->unicodeenc<=0x10fff )
dir = 1;
else if ( sc->unicodeenc!=-1 && sc->unicodeenc<0x10fff ) {
if ( iseuronumeric(sc->unicodeenc) )
dir = 3;
else if ( iseuronumsep(sc->unicodeenc))
dir = 4;
else if ( iseuronumterm(sc->unicodeenc))
dir = 5;
else if ( isarabnumeric(sc->unicodeenc))
dir = 6;
else if ( iscommonsep(sc->unicodeenc))
dir = 7;
else if ( isspace(sc->unicodeenc))
dir = 10;
else if ( islefttoright(sc->unicodeenc) )
dir = 0;
else if ( isrighttoleft(sc->unicodeenc) )
dir = 1;
else if ( SCScriptFromUnicode(sc)==CHR('a','r','a','b') )
dir = 2;
else if ( SCScriptFromUnicode(sc)==CHR('h','e','b','r') )
dir = 1;
else
dir = 11; /* Other neutrals */
/* Not dealing with unicode 3 classes */
/* nor block seperator/ segment seperator */
} else if ( SCScriptFromUnicode(sc)==CHR('a','r','a','b') )
dir = 2;
else if ( SCScriptFromUnicode(sc)==CHR('h','e','b','r') )
dir = 1;
if ( dir==1 || dir==2 ) doit = true;
isfloat = false;
if ( sc->width==0 &&
((sc->anchor!=NULL && sc->anchor->type==at_mark) ||
(sc->unicodeenc!=-1 && sc->unicodeenc<0x10000 && iscombining(sc->unicodeenc))))
isfloat = doit = true;
isbracket = offset = 0;
if ( sc->unicodeenc!=-1 && sc->unicodeenc<0x10000 && tomirror(sc->unicodeenc)!=0 ) {
bsc = SFGetChar(sf,tomirror(sc->unicodeenc),NULL);
if ( bsc!=NULL && bsc->ttf_glyph-sc->ttf_glyph>-8 && bsc->ttf_glyph-sc->ttf_glyph<8 ) {
isbracket = true;
offset = bsc->ttf_glyph-sc->ttf_glyph;
}
}
if ( !isbracket ) {
for ( pst=sc->possub; pst!=NULL && PSTHasTag(pst,CHR('r','t','l','a')); pst=pst->next );
if ( pst!=NULL && pst->type==pst_substitution &&
(bsc=SFGetChar(sf,-1,pst->u.subs.variant))!=NULL &&
bsc->ttf_glyph!=-1 && bsc->ttf_glyph-sc->ttf_glyph>-8 && bsc->ttf_glyph-sc->ttf_glyph<8 ) {
isbracket = true;
offset = bsc->ttf_glyph-sc->ttf_glyph;
doit = true;
}
}
if ( SCRightToLeft(sc) ) {
/* Apple docs say attached right. So for r2l scripts we look for */
/* a cursive entry, and for l2r a cursive exit */
for ( ap=sc->anchor; ap!=NULL && ap->type!=at_centry; ap=ap->next );
} else {
for ( ap=sc->anchor; ap!=NULL && ap->type!=at_cexit; ap=ap->next );
}
props[sc->ttf_glyph] = dir |
(isfloat ? 0x8000 : 0 ) |
(isbracket ? 0x1000 : 0 ) |
(ap!=NULL ? 0x80 : 0 ) |
((offset&0xf)<<8);
/* not dealing with */
/* hang left 0x4000 */
/* hang right 0x2000 */
}
}
if ( !doit ) {
free(props);
return( NULL );
}
return( props );
}
void aat_dumpprop(struct alltabs *at, SplineFont *sf) {
uint16 *props = props_array(sf,&at->gi);
uint32 bin_srch_header;
int i, j, cnt;
if ( props==NULL )
return;
at->prop = tmpfile();
putlong(at->prop,0x00020000);
putshort(at->prop,1); /* Lookup data */
putshort(at->prop,0); /* default property is simple l2r */
putshort(at->prop,2); /* lookup format 2 => segment single value */
/* Binsearch header */
bin_srch_header = ftell(at->prop);
putshort(at->prop,6); /* Entry size */
putshort(at->prop,0); /* fill in later */
putshort(at->prop,0);
putshort(at->prop,0);
putshort(at->prop,0);
cnt = 0;
for ( i=0; i<at->gi.gcnt; ++i ) {
while ( i<at->gi.gcnt && props[i]==0 ) ++i; /* skip default entries */
if ( i>=at->gi.gcnt )
break;
for ( j=i+1; j<at->gi.gcnt && props[j]==props[i]; ++j );
putshort(at->prop,j-1);
putshort(at->prop,i);
putshort(at->prop,props[i]);
i = j-1;
++cnt;
}
putshort(at->prop,0xffff); /* Final eof marker */
putshort(at->prop,0xffff);
putshort(at->prop,0x0000);
fseek(at->prop,bin_srch_header,SEEK_SET);
putshort(at->prop,6); /* Entry size */
putshort(at->prop,cnt); /* Number of segments */
for ( j=0,i=1; i<=cnt; i<<=1, ++j );
--j; i>>=1;
putshort(at->prop,6*i);
putshort(at->prop,j);
putshort(at->prop,6*(cnt-i));
fseek(at->prop,0,SEEK_END);
at->proplen = ftell(at->prop);
if ( at->proplen&2 )
putshort(at->prop,0);
free(props);
}
/* ************************************************************************** */
/* ************************* The 'bsln' table ************************* */
/* ************************************************************************** */
static int BslnFromTag(uint32 tag) {
switch ( tag ) {
case CHR('r','o','m','n'):
return( 0 );
/* Apple has a centered ideographic baseline, while OT has a top ideo bsln*/
/* no way to get Apple's baseline #1 */
case CHR('i','d','e','o'):
return( 2 );
case CHR('h','a','n','g'):
return( 3 );
case CHR('m','a','t','h'):
return( 4 );
default:
return( 0xffff );
}
}
int16 *PerGlyphDefBaseline(SplineFont *sf,int *def_baseline) {
int16 *baselines = malloc(sf->glyphcnt*sizeof(int16));
int gid, bsln, i, any;
SplineChar *sc;
int counts[32]; /* Apple supports a max of 32 baselines, but only 5 are defined */
struct Base *base = sf->horiz_base;
struct basescript *bs;
int bestbsln, bestcnt;
memset(counts,0,sizeof(counts));
for ( gid = 0; gid<sf->glyphcnt; ++gid ) if ( (sc = sf->glyphs[gid])!=NULL ) {
uint32 script = SCScriptFromUnicode(sc);
for ( bs= base->scripts; bs!=NULL; bs=bs->next )
if ( bs->script==script )
break;
if ( bs==NULL )
bsln = 0xffff;
else
bsln = BslnFromTag( base->baseline_tags[bs->def_baseline] );
/* This if is duplicated (almost) in basedlg.c:Base_FinishEdit */
if ( bsln==0xffff ) {
if ( script==CHR('k','a','n','a') || script==CHR('h','a','n','g') ||
script==CHR('h','a','n','i') || script==CHR('b','o','p','o') ||
script==CHR('j','a','m','o') || script==CHR('y','i',' ',' '))
bsln = 2;
else if ( script==CHR('t','i','b','t' ) ||
script == CHR('b','e','n','g' ) || script == CHR('b','n','g','2') ||
script == CHR('d','e','v','a' ) || script == CHR('d','e','v','2') ||
script == CHR('g','u','j','r' ) || script == CHR('g','j','r','2') ||
script == CHR('g','u','r','u' ) || script == CHR('g','u','r','2') ||
script == CHR('k','n','d','a' ) || script == CHR('k','n','d','2') ||
script == CHR('m','l','y','m' ) || script == CHR('m','l','m','2') ||
script == CHR('o','r','y','a' ) || script == CHR('o','r','y','2') ||
script == CHR('t','a','m','l' ) || script == CHR('t','m','l','2') ||
script == CHR('t','e','l','u' ) || script == CHR('t','e','l','2'))
bsln = 3;
else if ( script==CHR('m','a','t','h') )
bsln = 4;
else
bsln = 0;
}
baselines[gid] = bsln;
if ( bsln!=0xffff )
++counts[bsln];
}
bestbsln = 0;
bestcnt = 0;
any = 0;
for ( i=0; i<32 ; ++i ) {
if ( counts[i]>bestcnt ) {
bestbsln = i;
bestcnt = counts[i];
++any;
}
}
*def_baseline = bestbsln | (any<=1 ? 0x100 : 0 );
return( baselines );
}
void FigureBaseOffsets(SplineFont *sf,int def_bsln,int offsets[32]) {
struct Base *base = sf->horiz_base;
struct basescript *bs = base->scripts;
int i;
memset( offsets,0xff,32*sizeof(int));
for ( i=0; i<base->baseline_cnt; ++i ) {
int bsln = BslnFromTag(base->baseline_tags[i]);
if ( bsln!=0xffff )
offsets[bsln] = bs->baseline_pos[i];
}
if ( offsets[def_bsln]!=-1 ) {
for ( i=0; i<32; ++i ) {
if ( offsets[i]!=-1 )
offsets[i] -= offsets[def_bsln];
}
}
/* I suspect baseline 1 is the standard baseline for CJK glyphs on the mac*/
/* (because baseline 2 is often the same as baseline 1, which is wrong for 2) */
/* OT doesn't have a centered ideographic baseline, so guestimate */
/* And I don't want to base it on the actual ideo baseline (go up half an em?) */
/* because in my small sample of 'bsln' tables baseline 2 has been wrong */
/* most of the time, and it is wrong in the example in the docs. */
/* (I know it is wrong because it has the same value as baseline 1, but */
/* is supposed to be below baseline 1 ) */
if ( offsets[1]==-1 ) {
if ( offsets[2]!=-1 )
offsets[1] = offsets[2]+(sf->ascent+sf->descent)/2;
else
offsets[1] = (sf->ascent+sf->descent)/2 - sf->descent;
}
for ( i=0; i<32; ++i )
if ( offsets[i]==-1 )
offsets[i] = 0;
}
void aat_dumpbsln(struct alltabs *at, SplineFont *sf) {
int def_baseline;
int offsets[32];
int16 *baselines;
int i, gid, j, bsln, cnt;
if ( sf->horiz_base==NULL || sf->horiz_base->baseline_cnt==0 ||
sf->horiz_base->scripts==NULL )
return;
baselines = PerGlyphDefBaseline(sf,&def_baseline);
at->bsln = tmpfile();
putlong(at->bsln,0x00010000); /* Version */
if ( def_baseline & 0x100 ) /* Only one baseline in the font */
putshort(at->bsln,0); /* distanced based (no control point), no per-glyph info */
else
putshort(at->bsln,1); /* distanced based (no cp info) with per-glyph info */
putshort(at->bsln,def_baseline&0x1f);/* Default baseline when no info specified for glyphs */
/* table of 32 int16 (the docs say uint16, but that must be wrong) giving */
/* the offset of the nth baseline from the default baseline. */
/* 0 => Roman, 1=> centered ideo, 2=>low ideo (same as OTF ideo) 3=>hang, 4=>Math */
/* values 5-31 undefined, set to 0 */
FigureBaseOffsets(sf,def_baseline&0x1f,offsets);
for ( i=0; i<32; ++i )
putshort(at->bsln,offsets[i]);
if ( !(def_baseline&0x100) ) {
def_baseline &= 0x1f;
putshort(at->bsln,2); /* Lookup format 2, segmented array w/ single value */
cnt = 0;
for ( i=0; i<at->gi.gcnt; ++i ) if ( (gid=at->gi.bygid[i])!=-1 ) {
if ( baselines[gid]!=-1 && baselines[gid]!=def_baseline ) {
bsln = baselines[gid];
for ( j=i; j<at->gi.gcnt && baselines[at->gi.bygid[i]]==bsln; ++j );
i = j-1;
++cnt;
}
}
/* Dump out a binary search header */
putshort(at->bsln,6); /* size of each item */
putshort(at->bsln,cnt); /* number of items */
for ( j=1, i=0; cnt<=j; j<<=1, ++i );
putshort(at->bsln,6*j/2); /* j is a power of 2 too big */
putshort(at->bsln,i-1);
putshort(at->bsln,6*(cnt-(j>>1)) );
for ( i=0; i<at->gi.gcnt; ++i ) if ( (gid=at->gi.bygid[i])!=-1 ) {
if ( baselines[gid]!=-1 && baselines[gid]!=def_baseline ) {
bsln = baselines[gid];
for ( j=i; j<at->gi.gcnt && baselines[at->gi.bygid[i]]==bsln; ++j );
putshort(at->bsln,j-1);
putshort(at->bsln,i);
putshort(at->bsln,bsln);
i = j-1;
}
}
putshort(at->bsln,0xffff); /* Final eof marker */
putshort(at->bsln,0xffff);
putshort(at->bsln,0x0000);
}
at->bslnlen = ftell(at->bsln);
/* Only contains 2 & 4 byte quantities, can't have an odd number of bytes */
if ( at->bslnlen&2 )
putshort(at->bsln,0);
free(baselines);
}
/* ************************************************************************** */
/* ************************* utility routines ************************* */
/* ************************************************************************** */
uint32 MacFeatureToOTTag(int featureType,int featureSetting) {
int i;
struct macsettingname *msn = user_macfeat_otftag ? user_macfeat_otftag : macfeat_otftag;
for ( i=0; msn[i].otf_tag!=0; ++i )
if ( msn[i].mac_feature_type == featureType &&
msn[i].mac_feature_setting == featureSetting )
return( msn[i].otf_tag );
return( 0 );
}
int OTTagToMacFeature(uint32 tag, int *featureType,int *featureSetting) {
int i;
struct macsettingname *msn = user_macfeat_otftag ? user_macfeat_otftag : macfeat_otftag;
for ( i=0; msn[i].otf_tag!=0; ++i )
if ( msn[i].otf_tag == tag ) {
*featureType = msn[i].mac_feature_type;
*featureSetting = msn[i].mac_feature_setting;
return( true );
}
*featureType = (tag >> 16);
*featureSetting = (tag & 0xFFFF);
/* Ranges taken from Apple Font Registry. An OT tag without a
corresponding mac feature should fail this test.*/
if (*featureType >= 0 && *featureType < 105 && *featureSetting < 16)
return ( true );
*featureType = 0;
*featureSetting = 0;
return( false );
}
static struct feature *featureFromTag(SplineFont *sf, uint32 tag ) {
int ft, fs;
struct feature *feat;
feat = chunkalloc(sizeof(struct feature));
if (OTTagToMacFeature(tag, &ft, &fs)) {
feat->featureType = ft;
feat->featureSetting = fs;
feat->mf = FindMacFeature(sf,feat->featureType,&feat->smf);
feat->ms = FindMacSetting(sf,feat->featureType,feat->featureSetting,&feat->sms);
feat->needsOff = feat->mf!=NULL && !feat->mf->ismutex;
feat->vertOnly = tag==CHR('v','r','t','2') || tag==CHR('v','k','n','a');
}
return( feat );
}
static struct feature *featureFromSubtable(SplineFont *sf, struct lookup_subtable *sub ) {
FeatureScriptLangList *fl;
int ft, fs;
for ( fl=sub->lookup->features; fl!=NULL; fl=fl->next ) {
if ( fl->ismac )
break;
}
if ( fl==NULL ) {
for ( fl=sub->lookup->features; fl!=NULL; fl=fl->next ) {
if ( OTTagToMacFeature(fl->featuretag,&ft,&fs) )
break;
}
if ( fl==NULL ) {
IError("Could not find a mac feature");
return NULL;
}
}
return( featureFromTag(sf,fl->featuretag));
}
static int PSTHasTag(PST *pst, uint32 tag) {
FeatureScriptLangList *fl;
if ( pst->subtable==NULL )
return( false );
for ( fl=pst->subtable->lookup->features; fl!=NULL; fl=fl->next )
if ( fl->featuretag == tag )
return( true );
return( false );
}
int scriptsHaveDefault(struct scriptlanglist *sl) {
int i;
for ( ; sl!=NULL; sl=sl->next ) {
for ( i=0; i<sl->lang_cnt; ++i ) {
if ( (i<MAX_LANG && sl->langs[i]==DEFAULT_LANG) ||
(i>=MAX_LANG && sl->morelangs[i-MAX_LANG]==DEFAULT_LANG)) {
return( true );
}
}
}
return( false );
}
int LookupHasDefault(OTLookup *otl) {
FeatureScriptLangList *feats;
if ( otl->def_lang_checked )
return( otl->def_lang_found );
otl->def_lang_checked = true;
for ( feats=otl->features; feats!=NULL; feats = feats->next ) {
if ( scriptsHaveDefault(feats->scripts) ) {
otl->def_lang_found = true;
return( true );
}
}
otl->def_lang_found = false;
return( false );
}