diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/lookups.c')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/lookups.c | 4468 |
1 files changed, 4468 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/lookups.c b/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/lookups.c new file mode 100644 index 00000000000..df31efd5c16 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/lookups.c @@ -0,0 +1,4468 @@ +/* Copyright (C) 2007,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 "fontforgevw.h" +#include <chardata.h> +#include <utype.h> +#include <ustring.h> +#include <math.h> +#include <locale.h> +#include <stdlib.h> +#include "ttf.h" +#include "lookups.h" + +struct opentype_feature_friendlynames friendlies[] = { +#if 0 /* They get stuffed into the 'MATH' table now */ +/* I added these first three features to allow round-trip conversion of tfm files */ + { CHR('I','T','L','C'), "ITLC", N_("Italic Correction"), gpos_single_mask }, + { CHR('T','C','H','L'), "TCHL", N_("TeX Glyphlist"), gsub_alternate_mask }, + { CHR('T','E','X','L'), "TEXL", N_("TeX Extension List"), gsub_multiple_mask }, +#endif +/* Normal OpenType features follow */ + { CHR('a','a','l','t'), "aalt", N_("Access All Alternates"), gsub_single_mask|gsub_alternate_mask }, + { CHR('a','b','v','f'), "abvf", N_("Above Base Forms"), gsub_single_mask }, + { CHR('a','b','v','m'), "abvm", N_("Above Base Mark"), gpos_mark2base_mask|gpos_mark2ligature_mask }, + { CHR('a','b','v','s'), "abvs", N_("Above Base Substitutions"), gsub_ligature_mask }, + { CHR('a','f','r','c'), "afrc", N_("Vertical Fractions"), gsub_ligature_mask }, + { CHR('a','k','h','n'), "akhn", N_("Akhand"), gsub_ligature_mask }, + { CHR('a','l','i','g'), "alig", N_("Ancient Ligatures"), gsub_ligature_mask }, + { CHR('b','l','w','f'), "blwf", N_("Below Base Forms"), gsub_ligature_mask }, + { CHR('b','l','w','m'), "blwm", N_("Below Base Mark"), gpos_mark2base_mask|gpos_mark2ligature_mask }, + { CHR('b','l','w','s'), "blws", N_("Below Base Substitutions"), gsub_ligature_mask }, + { CHR('c','2','p','c'), "c2pc", N_("Capitals to Petite Capitals"), gsub_single_mask }, + { CHR('c','2','s','c'), "c2sc", N_("Capitals to Small Capitals"), gsub_single_mask }, + { CHR('c','a','l','t'), "calt", N_("Contextual Alternates"), gsub_context_mask|gsub_contextchain_mask|morx_context_mask }, + { CHR('c','a','s','e'), "case", N_("Case-Sensitive Forms"), gsub_single_mask|gpos_single_mask }, + { CHR('c','c','m','p'), "ccmp", N_("Glyph Composition/Decomposition"), gsub_multiple_mask|gsub_ligature_mask }, + { CHR('c','l','i','g'), "clig", N_("Contextual Ligatures"), gsub_reversecchain_mask }, + { CHR('c','p','c','t'), "cpct", N_("Centered CJK Punctuation"), gpos_single_mask }, + { CHR('c','p','s','p'), "cpsp", N_("Capital Spacing"), gpos_single_mask }, + { CHR('c','s','w','h'), "cswh", N_("Contextual Swash"), gsub_reversecchain_mask }, + { CHR('c','u','r','s'), "curs", N_("Cursive Attachment"), gpos_cursive_mask }, + { CHR('c','v','0','0'), "cv00", N_("Character Variants 00"), gsub_single_mask }, + { CHR('c','v','0','1'), "cv01", N_("Character Variants 01"), gsub_single_mask }, + { CHR('c','v','0','2'), "cv02", N_("Character Variants 02"), gsub_single_mask }, + { CHR('c','v','0','3'), "cv03", N_("Character Variants 03"), gsub_single_mask }, + { CHR('c','v','0','4'), "cv04", N_("Character Variants 04"), gsub_single_mask }, + { CHR('c','v','0','5'), "cv05", N_("Character Variants 05"), gsub_single_mask }, + { CHR('c','v','0','6'), "cv06", N_("Character Variants 06"), gsub_single_mask }, + { CHR('c','v','0','7'), "cv07", N_("Character Variants 07"), gsub_single_mask }, + { CHR('c','v','0','8'), "cv08", N_("Character Variants 08"), gsub_single_mask }, + { CHR('c','v','0','9'), "cv09", N_("Character Variants 09"), gsub_single_mask }, + { CHR('c','v','1','0'), "cv10", N_("Character Variants 10"), gsub_single_mask }, + { CHR('c','v','9','9'), "cv99", N_("Character Variants 99"), gsub_single_mask }, + { CHR('d','c','a','p'), "dcap", N_("Drop Caps"), gsub_single_mask }, + { CHR('d','i','s','t'), "dist", N_("Distance"), gpos_pair_mask }, + { CHR('d','l','i','g'), "dlig", N_("Discretionary Ligatures"), gsub_ligature_mask }, + { CHR('d','n','o','m'), "dnom", N_("Denominators"), gsub_single_mask }, + { CHR('d','p','n','g'), "dpng", N_("Dipthongs (Obsolete)"), gsub_ligature_mask }, + { CHR('e','x','p','t'), "expt", N_("Expert Forms"), gsub_single_mask }, + { CHR('f','a','l','t'), "falt", N_("Final Glyph On Line"), gsub_alternate_mask }, + { CHR('f','i','n','2'), "fin2", N_("Terminal Forms #2"), gsub_context_mask|gsub_contextchain_mask|morx_context_mask }, + { CHR('f','i','n','3'), "fin3", N_("Terminal Forms #3"), gsub_context_mask|gsub_contextchain_mask|morx_context_mask }, + { CHR('f','i','n','a'), "fina", N_("Terminal Forms"), gsub_single_mask }, + { CHR('f','l','a','c'), "flac", N_("Flattened Accents over Capitals"), gsub_single_mask|gsub_ligature_mask }, + { CHR('f','r','a','c'), "frac", N_("Diagonal Fractions"), gsub_single_mask|gsub_ligature_mask }, + { CHR('f','w','i','d'), "fwid", N_("Full Widths"), gsub_single_mask|gpos_single_mask }, + { CHR('h','a','l','f'), "half", N_("Half Forms"), gsub_ligature_mask }, + { CHR('h','a','l','n'), "haln", N_("Halant Forms"), gsub_ligature_mask }, + { CHR('h','a','l','t'), "halt", N_("Alternative Half Widths"), gpos_single_mask }, + { CHR('h','i','s','t'), "hist", N_("Historical Forms"), gsub_single_mask }, + { CHR('h','k','n','a'), "hkna", N_("Horizontal Kana Alternatives"), gsub_single_mask }, + { CHR('h','l','i','g'), "hlig", N_("Historic Ligatures"), gsub_ligature_mask }, + { CHR('h','n','g','l'), "hngl", N_("Hanja to Hangul"), gsub_single_mask|gsub_alternate_mask }, + { CHR('h','o','j','o'), "hojo", N_("Hojo (JIS X 0212-1990) Kanji Forms"), gsub_single_mask }, + { CHR('h','w','i','d'), "hwid", N_("Half Widths"), gsub_single_mask|gpos_single_mask }, + { CHR('i','n','i','t'), "init", N_("Initial Forms"), gsub_single_mask }, + { CHR('i','s','o','l'), "isol", N_("Isolated Forms"), gsub_single_mask }, + { CHR('i','t','a','l'), "ital", N_("Italics"), gsub_single_mask }, + { CHR('j','a','l','t'), "jalt", N_("Justification Alternatives"), gsub_alternate_mask }, + { CHR('j','a','j','p'), "jajp", N_("Japanese Forms (Obsolete"), gsub_single_mask|gsub_alternate_mask }, + { CHR('j','p','7','8'), "jp78", N_("JIS78 Forms"), gsub_single_mask|gsub_alternate_mask }, + { CHR('j','p','8','3'), "jp83", N_("JIS83 Forms"), gsub_single_mask }, + { CHR('j','p','9','0'), "jp90", N_("JIS90 Forms"), gsub_single_mask }, + { CHR('k','e','r','n'), "kern", N_("Horizontal Kerning"), gpos_pair_mask|gpos_context_mask|gpos_contextchain_mask|kern_statemachine_mask }, + { CHR('l','f','b','d'), "lfbd", N_("Left Bounds"), gpos_single_mask }, + { CHR('l','i','g','a'), "liga", N_("Standard Ligatures"), gsub_ligature_mask }, + { CHR('l','j','m','o'), "ljmo", N_("Leading Jamo Forms"), gsub_ligature_mask }, + { CHR('l','n','u','m'), "lnum", N_("Lining Figures"), gsub_single_mask }, + { CHR('l','o','c','l'), "locl", N_("Localized Forms"), gsub_single_mask }, + { CHR('m','a','r','k'), "mark", N_("Mark Positioning"), gpos_mark2base_mask|gpos_mark2ligature_mask }, + { CHR('m','e','d','2'), "med2", N_("Medial Forms 2"), gsub_context_mask|gsub_contextchain_mask|morx_context_mask }, + { CHR('m','e','d','i'), "medi", N_("Medial Forms"), gsub_single_mask }, + { CHR('m','g','r','k'), "mgrk", N_("Mathematical Greek"), gsub_single_mask }, + { CHR('m','k','m','k'), "mkmk", N_("Mark to Mark"), gpos_mark2mark_mask }, + { CHR('m','s','e','t'), "mset", N_("Mark Positioning via Substitution"), gsub_context_mask|gsub_contextchain_mask|morx_context_mask }, + { CHR('n','a','l','t'), "nalt", N_("Alternate Annotation Forms"), gsub_single_mask|gsub_alternate_mask }, + { CHR('n','u','k','t'), "nukt", N_("Nukta Forms"), gsub_ligature_mask }, + { CHR('n','u','m','r'), "numr", N_("Numerators"), gsub_single_mask }, + { CHR('o','n','u','m'), "onum", N_("Oldstyle Figures"), gsub_single_mask }, + { CHR('o','p','b','d'), "opbd", N_("Optical Bounds"), gpos_single_mask }, + { CHR('o','r','d','n'), "ordn", N_("Ordinals"), gsub_ligature_mask|gsub_context_mask|gsub_contextchain_mask|morx_context_mask }, + { CHR('o','r','n','m'), "ornm", N_("Ornaments"), gsub_single_mask|gsub_alternate_mask }, + { CHR('p','a','l','t'), "palt", N_("Proportional Alternate Metrics"), gpos_single_mask }, + { CHR('p','c','a','p'), "pcap", N_("Lowercase to Petite Capitals"), gsub_single_mask }, + { CHR('p','k','n','a'), "pkna", N_("Proportional Kana"), gpos_single_mask }, + { CHR('p','n','u','m'), "pnum", N_("Proportional Numbers"), gsub_single_mask }, + { CHR('p','r','e','f'), "pref", N_("Pre Base Forms"), gsub_ligature_mask }, + { CHR('p','r','e','s'), "pres", N_("Pre Base Substitutions"), gsub_ligature_mask|gsub_context_mask|gsub_contextchain_mask|morx_context_mask }, + { CHR('p','s','t','f'), "pstf", N_("Post Base Forms"), gsub_ligature_mask }, + { CHR('p','s','t','s'), "psts", N_("Post Base Substitutions"), gsub_ligature_mask }, + { CHR('p','w','i','d'), "pwid", N_("Proportional Width"), gsub_single_mask }, + { CHR('q','w','i','d'), "qwid", N_("Quarter Widths"), gsub_single_mask|gpos_single_mask }, + { CHR('r','a','n','d'), "rand", N_("Randomize"), gsub_alternate_mask }, + { CHR('r','k','r','f'), "rkrf", N_("Rakar Forms"), gsub_ligature_mask }, + { CHR('r','l','i','g'), "rlig", N_("Required Ligatures"), gsub_ligature_mask }, + { CHR('r','p','h','f'), "rphf", N_("Reph Form"), gsub_ligature_mask }, + { CHR('r','t','b','d'), "rtbd", N_("Right Bounds"), gpos_single_mask }, + { CHR('r','t','l','a'), "rtla", N_("Right to Left Alternates"), gsub_single_mask }, + { CHR('r','u','b','y'), "ruby", N_("Ruby Notational Forms"), gsub_single_mask }, + { CHR('s','a','l','t'), "salt", N_("Stylistic Alternatives"), gsub_single_mask|gsub_alternate_mask }, + { CHR('s','i','n','f'), "sinf", N_("Scientific Inferiors"), gsub_single_mask }, + { CHR('s','m','c','p'), "smcp", N_("Lowercase to Small Capitals"), gsub_single_mask }, + { CHR('s','m','p','l'), "smpl", N_("Simplified Forms"), gsub_single_mask }, + { CHR('s','s','0','1'), "ss01", N_("Style Set 1"), gsub_single_mask }, + { CHR('s','s','0','2'), "ss02", N_("Style Set 2"), gsub_single_mask }, + { CHR('s','s','0','3'), "ss03", N_("Style Set 3"), gsub_single_mask }, + { CHR('s','s','0','4'), "ss04", N_("Style Set 4"), gsub_single_mask }, + { CHR('s','s','0','5'), "ss05", N_("Style Set 5"), gsub_single_mask }, + { CHR('s','s','0','6'), "ss06", N_("Style Set 6"), gsub_single_mask }, + { CHR('s','s','0','7'), "ss07", N_("Style Set 7"), gsub_single_mask }, + { CHR('s','s','0','8'), "ss08", N_("Style Set 8"), gsub_single_mask }, + { CHR('s','s','0','9'), "ss09", N_("Style Set 9"), gsub_single_mask }, + { CHR('s','s','1','0'), "ss10", N_("Style Set 10"), gsub_single_mask }, + { CHR('s','s','1','1'), "ss11", N_("Style Set 11"), gsub_single_mask }, + { CHR('s','s','1','2'), "ss12", N_("Style Set 12"), gsub_single_mask }, + { CHR('s','s','1','3'), "ss13", N_("Style Set 13"), gsub_single_mask }, + { CHR('s','s','1','4'), "ss14", N_("Style Set 14"), gsub_single_mask }, + { CHR('s','s','1','5'), "ss15", N_("Style Set 15"), gsub_single_mask }, + { CHR('s','s','1','6'), "ss16", N_("Style Set 16"), gsub_single_mask }, + { CHR('s','s','1','7'), "ss17", N_("Style Set 17"), gsub_single_mask }, + { CHR('s','s','1','8'), "ss18", N_("Style Set 18"), gsub_single_mask }, + { CHR('s','s','1','9'), "ss19", N_("Style Set 19"), gsub_single_mask }, + { CHR('s','s','2','0'), "ss20", N_("Style Set 20"), gsub_single_mask }, + { CHR('s','s','t','y'), "ssty", N_("Script Style"), gsub_single_mask }, + { CHR('s','u','b','s'), "subs", N_("Subscript"), gsub_single_mask }, + { CHR('s','u','p','s'), "sups", N_("Superscript"), gsub_single_mask }, + { CHR('s','w','s','h'), "swsh", N_("Swash"), gsub_single_mask|gsub_alternate_mask }, + { CHR('t','i','t','l'), "titl", N_("Titling"), gsub_single_mask }, + { CHR('t','j','m','o'), "tjmo", N_("Trailing Jamo Forms"), gsub_ligature_mask }, + { CHR('t','n','a','m'), "tnam", N_("Traditional Name Forms"), gsub_single_mask }, + { CHR('t','n','u','m'), "tnum", N_("Tabular Numbers"), gsub_single_mask }, + { CHR('t','r','a','d'), "trad", N_("Traditional Forms"), gsub_single_mask|gsub_alternate_mask }, + { CHR('t','w','i','d'), "twid", N_("Third Widths"), gsub_single_mask|gpos_single_mask }, + { CHR('u','n','i','c'), "unic", N_("Unicase"), gsub_single_mask }, + { CHR('v','a','l','t'), "valt", N_("Alternate Vertical Metrics"), gpos_single_mask }, + { CHR('v','a','t','u'), "vatu", N_("Vattu Variants"), gsub_ligature_mask }, + { CHR('v','e','r','t'), "vert", N_("Vertical Alternates (obs)"), gsub_single_mask }, + { CHR('v','h','a','l'), "vhal", N_("Alternate Vertical Half Metrics"), gpos_single_mask }, + { CHR('v','j','m','o'), "vjmo", N_("Vowel Jamo Forms"), gsub_ligature_mask }, + { CHR('v','k','n','a'), "vkna", N_("Vertical Kana Alternates"), gsub_single_mask }, + { CHR('v','k','r','n'), "vkrn", N_("Vertical Kerning"), gpos_pair_mask|gpos_context_mask|gpos_contextchain_mask|kern_statemachine_mask }, + { CHR('v','p','a','l'), "vpal", N_("Proportional Alternate Vertical Metrics"), gpos_single_mask }, + { CHR('v','r','t','2'), "vrt2", N_("Vertical Rotation & Alternates"), gsub_single_mask }, + { CHR('z','e','r','o'), "zero", N_("Slashed Zero"), gsub_single_mask }, +/* This is my hack for setting the "Required feature" field of a script */ + { CHR(' ','R','Q','D'), " RQD", N_("Required feature"), gsub_single_mask|gsub_multiple_mask|gsub_alternate_mask|gsub_ligature_mask|gsub_context_mask|gsub_contextchain_mask|gsub_reversecchain_mask|morx_context_mask|gpos_single_mask|gpos_pair_mask|gpos_cursive_mask|gpos_mark2base_mask|gpos_mark2ligature_mask|gpos_mark2mark_mask|gpos_context_mask|gpos_contextchain_mask }, + { 0, NULL, 0, 0 } +}; + +static int uint32_cmp(const void *_ui1, const void *_ui2) { + if ( *(uint32 *) _ui1 > *(uint32 *)_ui2 ) +return( 1 ); + if ( *(uint32 *) _ui1 < *(uint32 *)_ui2 ) +return( -1 ); + +return( 0 ); +} + +static int lang_cmp(const void *_ui1, const void *_ui2) { + /* The default language is magic, and should come first in the list even */ + /* if that is not true alphabetical order */ + if ( *(uint32 *) _ui1 == DEFAULT_LANG ) +return( -1 ); + if ( *(uint32 *) _ui2 == DEFAULT_LANG ) +return( 1 ); + + if ( *(uint32 *) _ui1 > *(uint32 *)_ui2 ) +return( 1 ); + if ( *(uint32 *) _ui1 < *(uint32 *)_ui2 ) +return( -1 ); + +return( 0 ); +} + +FeatureScriptLangList *FindFeatureTagInFeatureScriptList(uint32 tag, FeatureScriptLangList *fl) { + + while ( fl!=NULL ) { + if ( fl->featuretag==tag ) +return( fl ); + fl = fl->next; + } +return( NULL ); +} + +int FeatureTagInFeatureScriptList(uint32 tag, FeatureScriptLangList *fl) { + + while ( fl!=NULL ) { + if ( fl->featuretag==tag ) +return( true ); + fl = fl->next; + } +return( false ); +} + +int ScriptInFeatureScriptList(uint32 script, FeatureScriptLangList *fl) { + struct scriptlanglist *sl; + + if ( fl==NULL ) /* No features bound to lookup? (nested?) don't restrict by script */ +return( true ); + + while ( fl!=NULL ) { + for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) { + if ( sl->script == script ) +return( true ); + } + fl = fl->next; + } +return( false ); +} + +int FeatureScriptTagInFeatureScriptList(uint32 feature, uint32 script, FeatureScriptLangList *fl) { + struct scriptlanglist *sl; + + while ( fl!=NULL ) { + if ( fl->featuretag == feature ) { + for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) { + if ( sl->script == script ) +return( true ); + } + } + fl = fl->next; + } +return( false ); +} + +int DefaultLangTagInOneScriptList(struct scriptlanglist *sl) { + int l; + + for ( l=0; l<sl->lang_cnt; ++l ) { + uint32 lang = l<MAX_LANG ? sl->langs[l] : sl->morelangs[l-MAX_LANG]; + if ( lang==DEFAULT_LANG ) +return( true ); + } +return( false ); +} + +struct scriptlanglist *DefaultLangTagInScriptList(struct scriptlanglist *sl, int DFLT_ok) { + + while ( sl!=NULL ) { + if ( DFLT_ok || sl->script!=DEFAULT_SCRIPT ) { + if ( DefaultLangTagInOneScriptList(sl)) +return( sl ); + } + sl = sl->next; + } +return( NULL ); +} + +uint32 *SFScriptsInLookups(SplineFont *sf,int gpos) { + /* Presumes that either SFFindUnusedLookups or SFFindClearUnusedLookupBits */ + /* has been called first */ + /* Since MS will sometimes ignore a script if it isn't found in both */ + /* GPOS and GSUB we want to return the same script list no matter */ + /* what the setting of gpos ... so we totally ignore that argument */ + /* and always look at both sets of lookups */ + +/* Sergey Malkin from MicroSoft tells me: + Each shaping engine in Uniscribe can decide on its requirements for + layout tables - some of them require both GSUB and GPOS, in some cases + any table present is enough, or it can work without any table. + + Sometimes, purpose of the check is to determine if font is supporting + particular script - if required tables are not there font is just + rejected by this shaping engine. Sometimes, shaping engine can not just + reject the font because there are fonts using older shaping technologies + we still have to support, so it uses some logic when to fallback to + legacy layout code. + + In your case this is Hebrew, where both tables are required to use + OpenType processing. Arabic requires both tables too, Latin requires + GSUB to execute GPOS. But in general, if you have both tables you should + be safe with any script to get fully featured OpenType shaping. + +In other words, if we have a Hebrew font with just GPOS features they won't work, +and MS will not use the font at all. We must add a GSUB table. In the unlikely +event that we had a hebrew font with only GSUB it would not work either. + +So if we want our lookups to have a chance of executing under Uniscribe we +better make sure that both tables have the same script set. + +(Sergey says we could optimize a little: A Latin GSUB table will run without +a GPOS, but he says the GPOS won't work without a GSUB.) +*/ + int cnt=0, tot=0, i; + uint32 *scripts = NULL; + OTLookup *test; + FeatureScriptLangList *fl; + struct scriptlanglist *sl; + + /* So here always give scripts for both (see comment above) no */ + /* matter what they asked for */ + for ( gpos=0; gpos<2; ++gpos ) { + for ( test = gpos ? sf->gpos_lookups : sf->gsub_lookups; test!=NULL; test = test->next ) { + if ( test->unused ) + continue; + for ( fl=test->features; fl!=NULL; fl=fl->next ) { + if ( fl->ismac ) + continue; + for ( sl=fl->scripts ; sl!=NULL; sl=sl->next ) { + for ( i=0; i<cnt; ++i ) { + if ( sl->script==scripts[i] ) + break; + } + if ( i==cnt ) { + if ( cnt>=tot ) + scripts = grealloc(scripts,(tot+=10)*sizeof(uint32)); + scripts[cnt++] = sl->script; + } + } + } + } + } + + if ( cnt==0 ) +return( NULL ); + + /* We want our scripts in alphabetic order */ + qsort(scripts,cnt,sizeof(uint32),uint32_cmp); + /* add a 0 entry to mark the end of the list */ + if ( cnt>=tot ) + scripts = grealloc(scripts,(tot+1)*sizeof(uint32)); + scripts[cnt] = 0; +return( scripts ); +} + +uint32 *SFLangsInScript(SplineFont *sf,int gpos,uint32 script) { + /* However, the language lists (I think) are distinct */ + /* But giving a value of -1 for gpos will give us the set of languages in */ + /* both tables (for this script) */ + int cnt=0, tot=0, i, g, l; + uint32 *langs = NULL; + OTLookup *test; + FeatureScriptLangList *fl; + struct scriptlanglist *sl; + + for ( g=0; g<2; ++g ) { + if (( gpos==0 && g==1 ) || ( gpos==1 && g==0 )) + continue; + for ( test = gpos ? sf->gpos_lookups : sf->gsub_lookups; test!=NULL; test = test->next ) { + if ( test->unused ) + continue; + for ( fl=test->features; fl!=NULL; fl=fl->next ) { + for ( sl=fl->scripts ; sl!=NULL; sl=sl->next ) { + if ( sl->script==script ) { + for ( l=0; l<sl->lang_cnt; ++l ) { + unsigned lang; + if ( l<MAX_LANG ) + lang = sl->langs[l]; + else + lang = sl->morelangs[l-MAX_LANG]; + for ( i=0; i<cnt; ++i ) { + if ( lang==langs[i] ) + break; + } + if ( i==cnt ) { + if ( cnt>=tot ) + langs = grealloc(langs,(tot+=10)*sizeof(uint32)); + langs[cnt++] = lang; + } + } + } + } + } + } + } + + if ( cnt==0 ) { + /* We add dummy script entries. Because Uniscribe will refuse to */ + /* process some scripts if they don't have an entry in both GPOS */ + /* an GSUB. So if a script appears in either table, force it to */ + /* appear in both. That means we can get scripts with no lookups */ + /* and hence no languages. It seems that Uniscribe doesn't like */ + /* that either. So give each such script a dummy default language */ + /* entry. This is what VOLT does */ + langs = gcalloc(2,sizeof(uint32)); + langs[0] = DEFAULT_LANG; +return( langs ); + } + + /* We want our languages in alphabetic order */ + qsort(langs,cnt,sizeof(uint32),lang_cmp); + /* add a 0 entry to mark the end of the list */ + if ( cnt>=tot ) + langs = grealloc(langs,(tot+1)*sizeof(uint32)); + langs[cnt] = 0; +return( langs ); +} + +uint32 *SFFeaturesInScriptLang(SplineFont *sf,int gpos,uint32 script,uint32 lang) { + int cnt=0, tot=0, i, l, isg; + uint32 *features = NULL; + OTLookup *test; + FeatureScriptLangList *fl; + struct scriptlanglist *sl; + /* gpos==0 => GSUB, gpos==1 => GPOS, gpos==-1 => both, gpos==-2 => Both & morx & kern */ + + if ( sf->cidmaster ) sf=sf->cidmaster; + for ( isg = 0; isg<2; ++isg ) { + if ( gpos>=0 && isg!=gpos ) + continue; + for ( test = isg ? sf->gpos_lookups : sf->gsub_lookups; test!=NULL; test = test->next ) { + if ( test->unused ) + continue; + for ( fl=test->features; fl!=NULL; fl=fl->next ) { + if ( fl->ismac && gpos!=-2 ) + continue; + if ( script==0xffffffff ) { + for ( i=0; i<cnt; ++i ) { + if ( fl->featuretag==features[i] ) + break; + } + if ( i==cnt ) { + if ( cnt>=tot ) + features = grealloc(features,(tot+=10)*sizeof(uint32)); + features[cnt++] = fl->featuretag; + } + } else for ( sl=fl->scripts ; sl!=NULL; sl=sl->next ) { + if ( sl->script==script ) { + int matched = false; + if ( fl->ismac && gpos==-2 ) + matched = true; + else for ( l=0; l<sl->lang_cnt; ++l ) { + unsigned testlang; + if ( l<MAX_LANG ) + testlang = sl->langs[l]; + else + testlang = sl->morelangs[l-MAX_LANG]; + if ( testlang==lang ) { + matched = true; + break; + } + } + if ( matched ) { + for ( i=0; i<cnt; ++i ) { + if ( fl->featuretag==features[i] ) + break; + } + if ( i==cnt ) { + if ( cnt>=tot ) + features = grealloc(features,(tot+=10)*sizeof(uint32)); + features[cnt++] = fl->featuretag; + } + } + } + } + } + } + } + + if ( sf->design_size!=0 && gpos ) { + /* The 'size' feature is like no other. It has no lookups and so */ + /* we will never find it in the normal course of events. If the */ + /* user has specified a design size, then every script/lang combo */ + /* gets a 'size' feature which contains no lookups but feature */ + /* params */ + if ( cnt>=tot ) + features = grealloc(features,(tot+=2)*sizeof(uint32)); + features[cnt++] = CHR('s','i','z','e'); + } + + if ( cnt==0 ) +return( gcalloc(1,sizeof(uint32)) ); + + /* We don't care if our features are in alphabetical order here */ + /* all that matters is whether the complete list of features is */ + /* ordering here would be irrelevant */ + /* qsort(features,cnt,sizeof(uint32),uint32_cmp); */ + + /* add a 0 entry to mark the end of the list */ + if ( cnt>=tot ) + features = grealloc(features,(tot+1)*sizeof(uint32)); + features[cnt] = 0; +return( features ); +} + +OTLookup **SFLookupsInScriptLangFeature(SplineFont *sf,int gpos,uint32 script,uint32 lang, uint32 feature) { + int cnt=0, tot=0, l; + OTLookup **lookups = NULL; + OTLookup *test; + FeatureScriptLangList *fl; + struct scriptlanglist *sl; + + for ( test = gpos ? sf->gpos_lookups : sf->gsub_lookups; test!=NULL; test = test->next ) { + if ( test->unused ) + continue; + for ( fl=test->features; fl!=NULL; fl=fl->next ) { + if ( fl->featuretag==feature ) { + for ( sl=fl->scripts ; sl!=NULL; sl=sl->next ) { + if ( sl->script==script ) { + for ( l=0; l<sl->lang_cnt; ++l ) { + unsigned testlang; + if ( l<MAX_LANG ) + testlang = sl->langs[l]; + else + testlang = sl->morelangs[l-MAX_LANG]; + if ( testlang==lang ) { + if ( cnt>=tot ) + lookups = grealloc(lookups,(tot+=10)*sizeof(OTLookup *)); + lookups[cnt++] = test; + goto found; + } + } + } + } + } + } + found:; + } + + if ( cnt==0 ) +return( NULL ); + + /* lookup order is irrelevant here. might as well leave it in invocation order */ + /* add a 0 entry to mark the end of the list */ + if ( cnt>=tot ) + lookups = grealloc(lookups,(tot+1)*sizeof(OTLookup *)); + lookups[cnt] = 0; +return( lookups ); +} + +static int LigaturesFirstComponentGID(SplineFont *sf,char *components) { + int gid, ch; + char *pt; + + for ( pt = components; *pt!='\0' && *pt!=' '; ++pt ); + ch = *pt; *pt = '\0'; + gid = SFFindExistingSlot(sf,-1,components); + *pt = ch; +return( gid ); +} + +static int PSTValid(SplineFont *sf,PST *pst) { + char *start, *pt, ch; + int ret; + (void)sf; + switch ( pst->type ) { + case pst_position: +return( true ); + case pst_pair: +return( SCWorthOutputting(SFGetChar(sf,-1,pst->u.pair.paired)) ); + case pst_substitution: case pst_alternate: case pst_multiple: + case pst_ligature: + for ( start = pst->u.mult.components; *start ; ) { + for ( pt=start; *pt && *pt!=' '; ++pt ); + ch = *pt; *pt = '\0'; + ret = SCWorthOutputting(SFGetChar(sf,-1,start)); + *pt = ch; + if ( !ret ) +return( false ); + if ( ch==0 ) + start = pt; + else + start = pt+1; + } + } +return( true ); +} + +SplineChar **SFGlyphsWithPSTinSubtable(SplineFont *sf,struct lookup_subtable *subtable) { + uint8 *used = gcalloc(sf->glyphcnt,sizeof(uint8)); + SplineChar **glyphs, *sc; + int i, k, gid, cnt; + KernPair *kp; + PST *pst; + int ispair = subtable->lookup->lookup_type == gpos_pair; + int isliga = subtable->lookup->lookup_type == gsub_ligature; + sc = NULL; + for ( i=0; i<sf->glyphcnt; ++i ) if ( SCWorthOutputting(sc = sf->glyphs[i]) ) { + if ( ispair ) { + for ( k=0; k<2; ++k ) { + for ( kp= k ? sc->kerns : sc->vkerns; kp!=NULL ; kp=kp->next ) { + if ( !SCWorthOutputting(kp->sc)) + continue; + if ( kp->subtable == subtable ) { + used[i] = true; + goto continue_; + } + } + } + } + for ( pst=sc->possub; pst!=NULL; pst=pst->next ) { + if ( pst->subtable == subtable && PSTValid(sf,pst)) { + if ( !isliga ) { + used[i] = true; + goto continue_; + } else { + gid = LigaturesFirstComponentGID(sf,pst->u.lig.components); + pst->u.lig.lig = sc; + if ( gid!=-1 ) + used[gid] = true; + /* can't continue here. ffi might be "f+f+i" and "ff+i" */ + /* and we need to mark both "f" and "ff" as used */ + } + } + } + continue_: ; + } + + for ( i=cnt=0 ; i<sf->glyphcnt; ++i ) + if ( used[i] ) + ++cnt; + + if ( cnt==0 ) { + free(used); +return( NULL ); + } + glyphs = galloc((cnt+1)*sizeof(SplineChar *)); + for ( i=cnt=0 ; i<sf->glyphcnt; ++i ) { + if ( used[i] ) + glyphs[cnt++] = sf->glyphs[i]; + } + glyphs[cnt] = NULL; + free(used); +return( glyphs ); +} + +SplineChar **SFGlyphsWithLigatureinLookup(SplineFont *sf,struct lookup_subtable *subtable) { + uint8 *used = gcalloc(sf->glyphcnt,sizeof(uint8)); + SplineChar **glyphs, *sc; + int i, cnt; + PST *pst; + sc=NULL; + for ( i=0; i<sf->glyphcnt; ++i ) if ( SCWorthOutputting(sc = sf->glyphs[i]) ) { + for ( pst=sc->possub; pst!=NULL; pst=pst->next ) { + if ( pst->subtable == subtable ) { + used[i] = true; + goto continue_; + } + } + continue_: ; + } + + for ( i=cnt=0 ; i<sf->glyphcnt; ++i ) + if ( used[i] ) + ++cnt; + + if ( cnt==0 ) { + free(used); +return( NULL ); + } + + glyphs = galloc((cnt+1)*sizeof(SplineChar *)); + for ( i=cnt=0 ; i<sf->glyphcnt; ++i ) { + if ( used[i] ) + glyphs[cnt++] = sf->glyphs[i]; + } + glyphs[cnt] = NULL; + free(used); +return( glyphs ); +} + +void SFFindUnusedLookups(SplineFont *sf) { + OTLookup *test; + struct lookup_subtable *sub; + int gpos; + AnchorClass *ac; + AnchorPoint *ap; + SplineChar *sc; + KernPair *kp; + PST *pst; + int k,gid,isv; + SplineFont *_sf = sf; + sc=NULL; + if ( _sf->cidmaster ) _sf = _sf->cidmaster; + + /* Some things are obvious. If a subtable consists of a kernclass or some */ + /* such, then obviously it is used. But more distributed info takes more */ + /* work. So mark anything easy as used, and anything difficult as unused */ + /* We'll work on the difficult things later */ + for ( gpos=0; gpos<2; ++gpos ) { + for ( test = gpos ? _sf->gpos_lookups : _sf->gsub_lookups; test!=NULL; test = test->next ) { + for ( sub = test->subtables; sub!=NULL; sub=sub->next ) { + if ( sub->kc!=NULL || sub->fpst!=NULL || sub->sm!=NULL ) { + sub->unused = false; + continue; + } + sub->unused = true; + /* We'll turn the following bit back on if there turns out */ + /* to be an anchor class attached to it -- that is subtly */ + /* different than being unused -- unused will be set if all */ + /* acs are unused, this bit will be on if there are unused */ + /* classes that still refer to us. */ + sub->anchor_classes = false; + } + } + } + + /* To be useful an anchor class must have both at least one base and one mark */ + /* (for cursive anchors that means at least one entry and at least one exit) */ + /* Start by assuming the worst */ + for ( ac = _sf->anchor; ac!=NULL; ac=ac->next ) + ac->has_mark = ac->has_base = false; + + /* Ok, for each glyph, look at all lookups (or anchor classes) it affects */ + /* and mark the appropriate parts of them as used */ + k = 0; + do { + sf = _sf->subfontcnt==0 ? _sf : _sf->subfonts[k]; + for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( SCWorthOutputting(sc = sf->glyphs[gid]) ) { + for ( ap=sc->anchor; ap!=NULL; ap=ap->next ) { + switch ( ap->type ) { + case at_mark: case at_centry: + ap->anchor->has_mark = true; + break; + case at_basechar: case at_baselig: case at_basemark: + case at_cexit: + ap->anchor->has_base = true; + break; + } + } + for ( isv=0; isv<2; ++isv ) { + for ( kp= isv ? sc->kerns : sc->vkerns ; kp!=NULL; kp=kp->next ) { + if ( SCWorthOutputting(kp->sc)) + kp->subtable->unused = false; + } + } + for ( pst=sc->possub; pst!=NULL; pst=pst->next ) { + if ( pst->subtable==NULL ) + continue; + if ( !PSTValid(sf,pst)) + continue; + pst->subtable->unused = false; + } + } + ++k; + } while ( k<_sf->subfontcnt ); + + /* Finally for any anchor class that has both a mark and a base then it is */ + /* used, and its lookup is also used */ + /* Also, even if unused, as long as the anchor class exists we must keep */ + /* the subtable around */ + for ( ac = _sf->anchor; ac!=NULL; ac=ac->next ) { + ac->subtable->anchor_classes = true; + if ( ac->has_mark && ac->has_base ) + ac->subtable->unused = false; + } + + /* Now for each lookup, a lookup is unused if ALL subtables are unused */ + for ( gpos=0; gpos<2; ++gpos ) { + for ( test = gpos ? _sf->gpos_lookups : _sf->gsub_lookups; test!=NULL; test = test->next ) { + test->unused = test->empty = true; + for ( sub=test->subtables; sub!=NULL; sub=sub->next ) { + if ( !sub->unused ) + test->unused = false; + if ( !sub->unused && !sub->anchor_classes ) { + test->empty = false; + break; + } + } + } + } +} + +void SFFindClearUnusedLookupBits(SplineFont *sf) { + OTLookup *test; + int gpos; + + for ( gpos=0; gpos<2; ++gpos ) { + for ( test = gpos ? sf->gpos_lookups : sf->gsub_lookups; test!=NULL; test = test->next ) { + test->unused = false; + test->empty = false; + test->def_lang_checked = false; + } + } +} + +static void SFRemoveAnchorPointsOfAC(SplineFont *sf,AnchorClass *ac) { + int gid; + SplineChar *sc; + AnchorPoint *ap, *prev, *next; + + for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc = sf->glyphs[gid])!=NULL ) { + for ( prev=NULL, ap=sc->anchor; ap!=NULL; ap=next ) { + next = ap->next; + if ( ap->anchor!=ac ) + prev = ap; + else { + if ( prev==NULL ) + sc->anchor = next; + else + prev->next = next; + ap->next = NULL; + AnchorPointsFree(ap); + } + } + } +} + +static void RemoveNestedReferences(SplineFont *sf,int isgpos,OTLookup *dying) { + OTLookup *otl; + struct lookup_subtable *sub; + int i,j,k; + (void)dying; + for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl = otl->next ) { + if ( otl->lookup_type==morx_context ) { + for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) { + ASM *sm = sub->sm; + if ( sm->type==asm_context ) { + for ( i=0; i<sm->state_cnt*sm->class_cnt; ++i ) { + struct asm_state *state = &sm->state[i]; + if ( state->u.context.mark_lookup == otl ) + state->u.context.mark_lookup = NULL; + if ( state->u.context.cur_lookup == otl ) + state->u.context.cur_lookup = NULL; + } + } + } + /* Reverse chaining tables do not reference lookups. The match pattern*/ + /* is a (exactly one) coverage table, and each glyph in that table */ + /* as an inline replacement. There is no lookup to do the replacement*/ + /* (so we ignore it here) */ + } else if ( otl->lookup_type==gsub_context || otl->lookup_type==gsub_contextchain || + otl->lookup_type==gpos_context || otl->lookup_type==gpos_contextchain ) { + for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) { + FPST *fpst = sub->fpst; + for ( i=0; i<fpst->rule_cnt; ++i ) { + for ( j=0; j<fpst->rules[i].lookup_cnt; ++j ) { + if ( fpst->rules[i].lookups[j].lookup == otl ) { + for ( k=j+1; k<fpst->rules[i].lookup_cnt; ++k ) + fpst->rules[i].lookups[k-1] = fpst->rules[i].lookups[k]; + --fpst->rules[i].lookup_cnt; + --j; + } + } + } + } + } + } +} + +void SFRemoveUnusedLookupSubTables(SplineFont *sf, + int remove_incomplete_anchorclasses, + int remove_unused_lookups) { + int gpos; + struct lookup_subtable *sub, *subnext, *prev; + AnchorClass *ac, *acprev, *acnext; + OTLookup *otl, *otlprev, *otlnext; + otlprev=NULL; + /* Presumes someone has called SFFindUnusedLookups first */ + + if ( remove_incomplete_anchorclasses ) { + for ( acprev=NULL, ac=sf->anchor; ac!=NULL; ac=acnext ) { + acnext = ac->next; + if ( ac->has_mark && ac->has_base ) + acprev = ac; + else { + SFRemoveAnchorPointsOfAC(sf,ac); + ac->next = NULL; + AnchorClassesFree(ac); + if ( acprev==NULL ) + sf->anchor = acnext; + else + acprev = acnext; + } + } + } + + for ( gpos=0; gpos<2; ++gpos ) { + for ( otl = gpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl = otlnext ) { + otlnext = otl->next; + if ( remove_unused_lookups && (otl->empty || + (otl->unused && remove_incomplete_anchorclasses)) ) { + if ( otlprev!=NULL ) + otlprev->next = otlnext; + else if ( gpos ) + sf->gpos_lookups = otlnext; + else + sf->gsub_lookups = otlnext; + RemoveNestedReferences(sf,gpos,otl); + OTLookupFree(otl); + } else { + for ( prev=NULL, sub=otl->subtables; sub!=NULL; sub=subnext ) { + subnext = sub->next; + if ( sub->unused && + (!sub->anchor_classes || + remove_incomplete_anchorclasses )) { + if ( prev==NULL ) + otl->subtables = subnext; + else + prev->next = subnext; + free(sub->subtable_name); + chunkfree(sub,sizeof(*sub)); + } else + prev = sub; + } + } + } + } +} + +void SFRemoveLookupSubTable(SplineFont *sf,struct lookup_subtable *sub) { + OTLookup *otl = sub->lookup; + struct lookup_subtable *subprev, *subtest; + + if ( sf->cidmaster!=NULL ) sf = sf->cidmaster; + + if ( sub->sm!=NULL ) { + ASM *prev = NULL, *test; + for ( test=sf->sm; test!=NULL && test!=sub->sm; prev=test, test=test->next ); + if ( prev==NULL ) + sf->sm = sub->sm->next; + else + prev->next = sub->sm->next; + sub->sm->next = NULL; + ASMFree(sub->sm); + sub->sm = NULL; + } else if ( sub->fpst!=NULL ) { + FPST *prev = NULL, *test; + for ( test=sf->possub; test!=NULL && test!=sub->fpst; prev=test, test=test->next ); + if ( prev==NULL ) + sf->possub = sub->fpst->next; + else + prev->next = sub->fpst->next; + sub->fpst->next = NULL; + FPSTFree(sub->fpst); + sub->fpst = NULL; + } else if ( sub->kc!=NULL ) { + KernClass *prev = NULL, *test; + for ( test=sf->kerns; test!=NULL && test!=sub->kc; prev=test, test=test->next ); + if ( test!=NULL ) { + if ( prev==NULL ) + sf->kerns = sub->kc->next; + else + prev->next = sub->kc->next; + } else { + for ( prev=NULL,test=sf->vkerns; test!=NULL && test!=sub->kc; prev=test, test=test->next ); + if ( prev==NULL ) + sf->vkerns = sub->kc->next; + else + prev->next = sub->kc->next; + } + sub->kc->next = NULL; + KernClassListFree(sub->kc); + sub->kc = NULL; + } else if ( otl->lookup_type==gpos_cursive || otl->lookup_type==gpos_mark2base || + otl->lookup_type==gpos_mark2ligature || otl->lookup_type==gpos_mark2mark ) { + AnchorClass *ac, *acnext; + for ( ac=sf->anchor; ac!=NULL; ac=acnext ) { + acnext = ac->next; + if ( ac->subtable==sub ) + SFRemoveAnchorClass(sf,ac); + } + } else { + int i,k,v; + SplineChar *sc; + SplineFont *_sf; + PST *pst, *prev, *next; + KernPair *kp, *kpprev, *kpnext; + k=0; i=0; + do { + _sf = sf->subfontcnt==0 ? sf : sf->subfonts[i]; + for ( i=0; i<_sf->glyphcnt; ++i ) if ( (sc=_sf->glyphs[i])!=NULL ) { + for ( pst=sc->possub, prev=NULL ; pst!=NULL; pst=next ) { + next = pst->next; + if ( pst->subtable==sub ) { + if ( prev==NULL ) + sc->possub = next; + else + prev->next = next; + pst->next = NULL; + PSTFree(pst); + } else + prev = pst; + } + for ( v=0; v<2; ++v ) { + for ( kp=v ? sc->vkerns : sc->kerns, kpprev=NULL ; kp!=NULL; kp=kpnext ) { + kpnext = kp->next; + if ( kp->subtable==sub ) { + if ( kpprev!=NULL ) + kpprev->next = kpnext; + else if ( v ) + sc->vkerns = kpnext; + else + sc->kerns = kpnext; + kp->next = NULL; + KernPairsFree(kp); + } else + kpprev = kp; + } + } + } + ++k; + } while ( k<sf->subfontcnt ); + } + + subprev = NULL; + for ( subtest = otl->subtables; subtest!=NULL && subtest!=sub; subprev = subtest, subtest=subtest->next ); + if ( subprev==NULL ) + otl->subtables = sub->next; + else + subprev->next = sub->next; + free(sub->subtable_name); + free(sub->suffix); + chunkfree(sub,sizeof(struct lookup_subtable)); +} + +void SFRemoveLookup(SplineFont *sf,OTLookup *otl) { + OTLookup *test, *prev; + int isgpos; + struct lookup_subtable *sub, *subnext; + + if ( sf->cidmaster ) sf = sf->cidmaster; + + for ( sub = otl->subtables; sub!=NULL; sub=subnext ) { + subnext = sub->next; + SFRemoveLookupSubTable(sf,sub); + } + + for ( prev=NULL, test=sf->gpos_lookups; test!=NULL && test!=otl; prev=test, test=test->next ); + if ( test==NULL ) { + isgpos = false; + for ( prev=NULL, test=sf->gsub_lookups; test!=NULL && test!=otl; prev=test, test=test->next ); + } else + isgpos = true; + if ( prev!=NULL ) + prev->next = otl->next; + else if ( isgpos ) + sf->gpos_lookups = otl->next; + else + sf->gsub_lookups = otl->next; + + RemoveNestedReferences(sf,isgpos,otl); + + otl->next = NULL; + OTLookupFree(otl); +} + +struct lookup_subtable *SFFindLookupSubtable(SplineFont *sf,char *name) { + int isgpos; + OTLookup *otl; + struct lookup_subtable *sub; + + if ( sf->cidmaster ) sf = sf->cidmaster; + + if ( name==NULL ) +return( NULL ); + + for ( isgpos=0; isgpos<2; ++isgpos ) { + for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups ; otl!=NULL; otl=otl->next ) { + for ( sub = otl->subtables; sub!=NULL; sub=sub->next ) { + if ( strcmp(name,sub->subtable_name)==0 ) +return( sub ); + } + } + } +return( NULL ); +} + +struct lookup_subtable *SFFindLookupSubtableAndFreeName(SplineFont *sf,char *name) { + struct lookup_subtable *sub = SFFindLookupSubtable(sf,name); + free(name); +return( sub ); +} + +OTLookup *SFFindLookup(SplineFont *sf,char *name) { + int isgpos; + OTLookup *otl; + + if ( sf->cidmaster ) sf = sf->cidmaster; + + if ( name==NULL ) +return( NULL ); + + for ( isgpos=0; isgpos<2; ++isgpos ) { + for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups ; otl!=NULL; otl=otl->next ) { + if ( strcmp(name,otl->lookup_name)==0 ) +return( otl ); + } + } +return( NULL ); +} + +void FListAppendScriptLang(FeatureScriptLangList *fl,uint32 script_tag,uint32 lang_tag) { + struct scriptlanglist *sl; + int l; + + for ( sl = fl->scripts; sl!=NULL && sl->script!=script_tag; sl=sl->next ); + if ( sl==NULL ) { + sl = chunkalloc(sizeof(struct scriptlanglist)); + sl->script = script_tag; + sl->next = fl->scripts; + fl->scripts = sl; + } + for ( l=0; l<MAX_LANG && l<sl->lang_cnt && sl->langs[l]!=lang_tag; ++l ); + if ( l>=MAX_LANG && l<sl->lang_cnt ) { + while ( l<sl->lang_cnt && sl->morelangs[l-MAX_LANG]!=lang_tag ) + ++l; + } + if ( l>=sl->lang_cnt ) { + if ( l<MAX_LANG ) + sl->langs[l] = lang_tag; + else { + if ( l%MAX_LANG == 0 ) + sl->morelangs = grealloc(sl->morelangs,l*sizeof(uint32)); + /* We've just allocated MAX_LANG-1 more than we need */ + /* so we don't do quite some many allocations */ + sl->morelangs[l-MAX_LANG] = lang_tag; + } + ++sl->lang_cnt; + } +} + +void FListsAppendScriptLang(FeatureScriptLangList *fl,uint32 script_tag,uint32 lang_tag) { + while ( fl!=NULL ) { + FListAppendScriptLang(fl,script_tag,lang_tag); + fl=fl->next; + } +} + +char *SuffixFromTags(FeatureScriptLangList *fl) { + static struct { uint32 tag; char *suffix; } tags2suffix[] = { + { CHR('v','r','t','2'), "vert" }, /* Will check for vrt2 later */ + { CHR('o','n','u','m'), "oldstyle" }, + { CHR('s','u','p','s'), "superior" }, + { CHR('s','u','b','s'), "inferior" }, + { CHR('s','w','s','h'), "swash" }, + { CHR('f','w','i','d'), "full" }, + { CHR('h','w','i','d'), "hw" }, + { 0 , NULL } + }; + int i; + + while ( fl!=NULL ) { + for ( i=0; tags2suffix[i].tag!=0; ++i ) + if ( tags2suffix[i].tag==fl->featuretag ) +return( copy( tags2suffix[i].suffix )); + fl = fl->next; + } +return( NULL ); +} +#ifdef LUA_FF_LIB +/* ma = State Machine, un = Unknown */ +char *lookup_type_names[2][10] = + { { "us", "ss", "ms", "as", "ls", "cs", "ks", "es", "rk" }, + { "up", "sp", "pp", "ca", "mb", "ml", "mm", "cp", "kp","ep" }}; +#else +char *lookup_type_names[2][10] = + { { N_("Undefined substitution"), N_("Single Substitution"), N_("Multiple Substitution"), + N_("Alternate Substitution"), N_("Ligature Substitution"), N_("Contextual Substitution"), + N_("Contextual Chaining Substitution"), N_("Extension"), + N_("Reverse Contextual Chaining Substitution") }, + { N_("Undefined positioning"), N_("Single Positioning"), N_("Pairwise Positioning (kerning)"), + N_("Cursive attachment"), N_("Mark to base attachment"), + N_("Mark to Ligature attachment"), N_("Mark to Mark attachment"), + N_("Contextual Positioning"), N_("Contextual Chaining Positioning"), + N_("Extension") }}; +#endif + +/* This is a non-ui based copy of a similar list in lookupui.c */ +static struct { + char *text; + uint32 tag; +} localscripts[] = { +/* GT: See the long comment at "Property|New" */ +/* GT: The msgstr should contain a translation of "Arabic", ignore "Script|" */ + { N_("Script|Arabic"), CHR('a','r','a','b') }, + { N_("Script|Aramaic"), CHR('a','r','a','m') }, + { N_("Script|Armenian"), CHR('a','r','m','n') }, + { N_("Script|Avestan"), CHR('a','v','e','s') }, + { N_("Script|Balinese"), CHR('b','a','l','i') }, + { N_("Script|Batak"), CHR('b','a','t','k') }, + { N_("Script|Bengali"), CHR('b','e','n','g') }, + { N_("Script|Bengali2"), CHR('b','n','g','2') }, + { N_("Bliss Symbolics"), CHR('b','l','i','s') }, + { N_("Bopomofo"), CHR('b','o','p','o') }, + { NU_("Brāhmī"), CHR('b','r','a','h') }, + { N_("Braille"), CHR('b','r','a','i') }, + { N_("Script|Buginese"), CHR('b','u','g','i') }, + { N_("Script|Buhid"), CHR('b','u','h','d') }, + { N_("Byzantine Music"), CHR('b','y','z','m') }, + { N_("Canadian Syllabics"), CHR('c','a','n','s') }, + { N_("Carian"), CHR('c','a','r','i') }, + { N_("Cherokee"), CHR('c','h','a','m') }, + { N_("Script|Cham"), CHR('c','h','a','m') }, + { N_("Script|Cherokee"), CHR('c','h','e','r') }, + { N_("Cirth"), CHR('c','i','r','t') }, + { N_("CJK Ideographic"), CHR('h','a','n','i') }, + { N_("Script|Coptic"), CHR('c','o','p','t') }, + { N_("Cypro-Minoan"), CHR('c','p','r','t') }, + { N_("Cypriot syllabary"), CHR('c','p','m','n') }, + { N_("Cyrillic"), CHR('c','y','r','l') }, + { N_("Script|Default"), CHR('D','F','L','T') }, + { N_("Deseret (Mormon)"), CHR('d','s','r','t') }, + { N_("Devanagari"), CHR('d','e','v','a') }, + { N_("Devanagari2"), CHR('d','e','v','2') }, +/* { N_("Egyptian demotic"), CHR('e','g','y','d') }, */ +/* { N_("Egyptian hieratic"), CHR('e','g','y','h') }, */ +/* GT: Someone asked if FontForge actually was prepared generate hieroglyph output */ +/* GT: because of this string. No. But OpenType and Unicode have placeholders for */ +/* GT: dealing with these scripts against the day someone wants to use them. So */ +/* GT: FontForge must be prepared to deal with those placeholders if nothing else. */ +/* { N_("Egyptian hieroglyphs"), CHR('e','g','y','p') }, */ + { N_("Script|Ethiopic"), CHR('e','t','h','i') }, + { N_("Script|Georgian"), CHR('g','e','o','r') }, + { N_("Glagolitic"), CHR('g','l','a','g') }, + { N_("Gothic"), CHR('g','o','t','h') }, + { N_("Script|Greek"), CHR('g','r','e','k') }, + { N_("Script|Gujarati"), CHR('g','u','j','r') }, + { N_("Script|Gujarati2"), CHR('g','j','r','2') }, + { N_("Gurmukhi"), CHR('g','u','r','u') }, + { N_("Gurmukhi2"), CHR('g','u','r','2') }, + { N_("Hangul Jamo"), CHR('j','a','m','o') }, + { N_("Hangul"), CHR('h','a','n','g') }, + { NU_("Script|Hanunóo"), CHR('h','a','n','o') }, + { N_("Script|Hebrew"), CHR('h','e','b','r') }, +/* { N_("Pahawh Hmong"), CHR('h','m','n','g') },*/ +/* { N_("Indus (Harappan)"), CHR('i','n','d','s') },*/ + { N_("Script|Javanese"), CHR('j','a','v','a') }, + { N_("Kayah Li"), CHR('k','a','l','i') }, + { N_("Hiragana & Katakana"), CHR('k','a','n','a') }, + { NU_("Kharoṣṭhī"), CHR('k','h','a','r') }, + { N_("Script|Kannada"), CHR('k','n','d','a') }, + { N_("Script|Kannada2"), CHR('k','n','d','2') }, + { N_("Script|Khmer"), CHR('k','h','m','r') }, + { N_("Script|Kharosthi"), CHR('k','h','a','r') }, + { N_("Script|Lao") , CHR('l','a','o',' ') }, + { N_("Script|Latin"), CHR('l','a','t','n') }, + { NU_("Lepcha (Róng)"), CHR('l','e','p','c') }, + { N_("Script|Limbu"), CHR('l','i','m','b') }, /* Not in ISO 15924 !!!!!, just guessing */ + { N_("Linear A"), CHR('l','i','n','a') }, + { N_("Linear B"), CHR('l','i','n','b') }, + { N_("Lycian"), CHR('l','y','c','i') }, + { N_("Lydian"), CHR('l','y','d','i') }, + { N_("Script|Mandaean"), CHR('m','a','n','d') }, +/* { N_("Mayan hieroglyphs"), CHR('m','a','y','a') },*/ + { NU_("Script|Malayālam"), CHR('m','l','y','m') }, + { NU_("Script|Malayālam2"), CHR('m','l','y','2') }, + { NU_("Mathematical Alphanumeric Symbols"), CHR('m','a','t','h') }, + { N_("Script|Mongolian"), CHR('m','o','n','g') }, + { N_("Musical"), CHR('m','u','s','i') }, + { N_("Script|Myanmar"), CHR('m','y','m','r') }, + { N_("New Tai Lue"), CHR('t','a','l','u') }, + { N_("N'Ko"), CHR('n','k','o',' ') }, + { N_("Ogham"), CHR('o','g','a','m') }, + { N_("Ol Chiki"), CHR('o','l','c','k') }, + { N_("Old Italic (Etruscan, Oscan, etc.)"), CHR('i','t','a','l') }, + { N_("Script|Old Permic"), CHR('p','e','r','m') }, + { N_("Old Persian cuneiform"), CHR('x','p','e','o') }, + { N_("Script|Oriya"), CHR('o','r','y','a') }, + { N_("Script|Oriya2"), CHR('o','r','y','2') }, + { N_("Osmanya"), CHR('o','s','m','a') }, + { N_("Script|Pahlavi"), CHR('p','a','l','v') }, + { N_("Script|Phags-pa"), CHR('p','h','a','g') }, + { N_("Script|Phoenician"), CHR('p','h','n','x') }, + { N_("Phaistos"), CHR('p','h','s','t') }, + { N_("Pollard Phonetic"), CHR('p','l','r','d') }, + { N_("Rejang"), CHR('r','j','n','g') }, + { N_("Rongorongo"), CHR('r','o','r','o') }, + { N_("Runic"), CHR('r','u','n','r') }, + { N_("Saurashtra"), CHR('s','a','u','r') }, + { N_("Shavian"), CHR('s','h','a','w') }, + { N_("Script|Sinhala"), CHR('s','i','n','h') }, + { N_("Script|Sumero-Akkadian Cuneiform"), CHR('x','s','u','x') }, + { N_("Script|Sundanese"), CHR('s','u','n','d') }, + { N_("Script|Syloti Nagri"), CHR('s','y','l','o') }, + { N_("Script|Syriac"), CHR('s','y','r','c') }, + { N_("Script|Tagalog"), CHR('t','g','l','g') }, + { N_("Script|Tagbanwa"), CHR('t','a','g','b') }, + { N_("Tai Le"), CHR('t','a','l','e') }, /* Not in ISO 15924 !!!!!, just guessing */ + { N_("Tai Lu"), CHR('t','a','l','a') }, /* Not in ISO 15924 !!!!!, just guessing */ + { N_("Script|Tamil"), CHR('t','a','m','l') }, + { N_("Script|Tamil2"), CHR('t','m','l','2') }, + { N_("Script|Telugu"), CHR('t','e','l','u') }, + { N_("Script|Telugu2"), CHR('t','e','l','2') }, + { N_("Tengwar"), CHR('t','e','n','g') }, + { N_("Thaana"), CHR('t','h','a','a') }, + { N_("Script|Thai"), CHR('t','h','a','i') }, + { N_("Script|Tibetan"), CHR('t','i','b','t') }, + { N_("Tifinagh (Berber)"), CHR('t','f','n','g') }, + { N_("Script|Ugaritic"), CHR('u','g','r','t') }, /* Not in ISO 15924 !!!!!, just guessing */ + { N_("Script|Vai"), CHR('v','a','i',' ') }, +/* { N_("Visible Speech"), CHR('v','i','s','p') },*/ + { N_("Cuneiform, Ugaritic"), CHR('x','u','g','a') }, + { N_("Script|Yi") , CHR('y','i',' ',' ') }, +/* { N_("Private Use Script 1"), CHR('q','a','a','a') },*/ +/* { N_("Private Use Script 2"), CHR('q','a','a','b') },*/ +/* { N_("Undetermined Script"), CHR('z','y','y','y') },*/ +/* { N_("Uncoded Script"), CHR('z','z','z','z') },*/ + { NULL, 0 } +}; + +void LookupInit(void) { + static int done = false; + int i, j; + + if ( done ) +return; + done = true; + for ( j=0; j<2; ++j ) { + for ( i=0; i<10; ++i ) + if ( lookup_type_names[j][i]!=NULL ) + lookup_type_names[j][i] = S_((char *) lookup_type_names[j][i]); + } + for ( i=0; localscripts[i].text!=NULL; ++i ) + localscripts[i].text = S_(localscripts[i].text); + for ( i=0; friendlies[i].friendlyname!=NULL; ++i ) + friendlies[i].friendlyname = S_(friendlies[i].friendlyname); +} + +char *TagFullName(SplineFont *sf,uint32 tag, int ismac, int onlyifknown) { + char ubuf[200], *end = ubuf+sizeof(ubuf), *setname; + int k; + + if ( ismac==-1 ) + /* Guess */ + ismac = (tag>>24)<' ' || (tag>>24)>0x7e; + + if ( ismac ) { + sprintf( ubuf, "<%d,%d> ", (int) (tag>>16),(int) (tag&0xffff) ); + if ( (setname = PickNameFromMacName(FindMacSettingName(sf,tag>>16,tag&0xffff)))!=NULL ) { + strcat( ubuf, setname ); + free( setname ); + } + } else { + unsigned stag = tag; + if ( tag==CHR('n','u','t','f') ) /* early name that was standardize later as... */ + stag = CHR('a','f','r','c'); /* Stood for nut fractions. "nut" meaning "fits in an en" in old typography-speak => vertical fractions rather than diagonal ones */ + if ( tag==REQUIRED_FEATURE ) { + strcpy(ubuf,_("Required Feature")); + } else { + LookupInit(); + for ( k=0; friendlies[k].tag!=0; ++k ) { + if ( friendlies[k].tag == stag ) + break; + } + ubuf[0] = '\''; + ubuf[1] = tag>>24; + ubuf[2] = (tag>>16)&0xff; + ubuf[3] = (tag>>8)&0xff; + ubuf[4] = tag&0xff; + ubuf[5] = '\''; + ubuf[6] = ' '; + if ( friendlies[k].tag!=0 ) + strncpy(ubuf+7, (char *) friendlies[k].friendlyname,end-ubuf-7); + else if ( onlyifknown ) +return( NULL ); + else + ubuf[7]='\0'; + } + } +return( copy( ubuf )); +} + + +void NameOTLookup(OTLookup *otl,SplineFont *sf) { + char *userfriendly = NULL, *script; + FeatureScriptLangList *fl; + char *lookuptype; + char *format; + struct lookup_subtable *subtable; + int k; + (void)sf; + + LookupInit(); + + if ( otl->lookup_name==NULL ) { + for ( k=0; k<2; ++k ) { +#ifndef LUA_FF_LIB + for ( fl=otl->features; fl!=NULL ; fl=fl->next ) { + /* look first for a feature attached to a default language */ + if ( k==1 || DefaultLangTagInScriptList(fl->scripts,false)!=NULL ) { + userfriendly = TagFullName(sf,fl->featuretag, fl->ismac, true); + if ( userfriendly!=NULL ) + break; + } + } + if ( userfriendly!=NULL ) + break; +#endif + } + if ( userfriendly==NULL ) { + if ( (otl->lookup_type&0xff)>= 0xf0 ) +#ifdef LUA_FF_LIB + lookuptype = "ma"; +#else + lookuptype = _("State Machine"); +#endif + else if ( (otl->lookup_type>>8)<2 && (otl->lookup_type&0xff)<10 ) + lookuptype = _(lookup_type_names[otl->lookup_type>>8][otl->lookup_type&0xff]); + else +#ifdef LUA_FF_LIB + lookuptype = "un"; +#else + lookuptype = S_("LookupType|Unknown"); +#endif + for ( fl=otl->features; fl!=NULL && !fl->ismac; fl=fl->next ); + if ( fl==NULL ) + userfriendly = copy(lookuptype); + else { + userfriendly = galloc( strlen(lookuptype) + 16); +#ifdef LUA_FF_LIB + if ( (otl->lookup_type&0xff)>= 0xf0 ) { + sprintf( userfriendly, "%s_<%d,%d>", lookuptype, + (fl->featuretag>>16), (fl->featuretag&0xffff)); + } else { + sprintf( userfriendly, "%s_%c%c%c%c", lookuptype, + fl->featuretag>>24, + fl->featuretag>>16, + fl->featuretag>>8 , + fl->featuretag ); + } +#else + sprintf( userfriendly, "%s '%c%c%c%c'", lookuptype, + fl->featuretag>>24, + fl->featuretag>>16, + fl->featuretag>>8 , + fl->featuretag ); +#endif + } + } + script = NULL; + if ( fl==NULL ) fl = otl->features; + if ( fl!=NULL && fl->scripts!=NULL ) { + char buf[8]; + int j; + struct scriptlanglist *sl, *found, *found2; + uint32 script_tag = fl->scripts->script; + found = found2 = NULL; + for ( sl = fl->scripts; sl!=NULL; sl=sl->next ) { + if ( sl->script == DEFAULT_SCRIPT ) + /* Ignore it */; + else if ( DefaultLangTagInOneScriptList(sl)) { + if ( found==NULL ) + found = sl; + else { + found = found2 = NULL; + break; + } + } else if ( found2 == NULL ) + found2 = sl; + else + found2 = (struct scriptlanglist *) -1; + } + if ( found==NULL && found2!=NULL && found2 != (struct scriptlanglist *) -1 ) + found = found2; + if ( found!=NULL ) { + script_tag = found->script; + for ( j=0; localscripts[j].text!=NULL && script_tag!=localscripts[j].tag; ++j ); +#ifdef LUA_FF_LIB + buf[0] = fl->scripts->script>>24; + buf[1] = (fl->scripts->script>>16)&0xff; + buf[2] = (fl->scripts->script>>8)&0xff; + buf[3] = fl->scripts->script&0xff; + buf[4] = 0; + script = copy(buf); +#else + if ( localscripts[j].text!=NULL ) + script = copy( S_((char *) localscripts[j].text) ); + else { + buf[0] = '\''; + buf[1] = fl->scripts->script>>24; + buf[2] = (fl->scripts->script>>16)&0xff; + buf[3] = (fl->scripts->script>>8)&0xff; + buf[4] = fl->scripts->script&0xff; + buf[5] = '\''; + buf[6] = 0; + script = copy(buf); + } +#endif + } + } + if ( script!=NULL ) { +/* GT: This string is used to generate a name for each OpenType lookup. */ +/* GT: The %s will be filled with the user friendly name of the feature used to invoke the lookup */ +/* GT: The second %s (if present) is the script */ +/* GT: While the %d is the index into the lookup list and is used to disambiguate it */ +/* GT: In case that is needed */ +#ifdef LUA_FF_LIB + format = "%s_%s_l_%d"; +#else + format = _("%s in %s lookup %d"); +#endif + otl->lookup_name = galloc( strlen(userfriendly)+strlen(format)+strlen(script)+10 ); + sprintf( otl->lookup_name, format, userfriendly, script, otl->lookup_index ); + } else { +#ifdef LUA_FF_LIB + format = "%s_l_%d"; +#else + format = _("%s lookup %d"); +#endif + otl->lookup_name = galloc( strlen(userfriendly)+strlen(format)+10 ); + sprintf( otl->lookup_name, format, userfriendly, otl->lookup_index ); + } + free(script); + free(userfriendly); + } + + if ( otl->subtables==NULL ) + /* IError( _("Lookup with no subtables"))*/; + else { + int cnt = 0; + for ( subtable = otl->subtables; subtable!=NULL; subtable=subtable->next, ++cnt ) + if ( subtable->subtable_name==NULL ) { + if ( subtable==otl->subtables && subtable->next==NULL ) +/* GT: This string is used to generate a name for an OpenType lookup subtable. */ +/* GT: %s is the lookup name */ +#ifdef LUA_FF_LIB + format = "%s_s"; +#else + format = _("%s subtable"); +#endif + else if ( subtable->per_glyph_pst_or_kern ) +/* GT: This string is used to generate a name for an OpenType lookup subtable. */ +/* GT: %s is the lookup name, %d is the index of the subtable in the lookup */ +#ifdef LUA_FF_LIB + format = "%s_g_%d"; +#else + format = _("%s per glyph data %d"); +#endif + else if ( subtable->kc!=NULL ) +#ifdef LUA_FF_LIB + format = "%s_k_%d"; +#else + format = _("%s kerning class %d"); +#endif + else if ( subtable->fpst!=NULL ) +#ifdef LUA_FF_LIB + format = "%s_c_%d"; +#else + format = _("%s contextual %d"); +#endif + else if ( subtable->anchor_classes ) +#ifdef LUA_FF_LIB + format = "%s_a_%d"; +#else + format = _("%s anchor %d"); +#endif + else { + IError("Subtable status not filled in for %dth subtable of %s", cnt, otl->lookup_name ); + format = "%s !!!!!!!! %d"; + } + subtable->subtable_name = galloc( strlen(otl->lookup_name)+strlen(format)+10 ); + sprintf( subtable->subtable_name, format, otl->lookup_name, cnt ); + } + } + if ( otl->lookup_type==gsub_ligature ) { + for ( fl=otl->features; fl!=NULL; fl=fl->next ) + if ( fl->featuretag==CHR('l','i','g','a') || fl->featuretag==CHR('r','l','i','g')) + otl->store_in_afm = true; + } + + if ( otl->lookup_type==gsub_single ) + for ( subtable = otl->subtables; subtable!=NULL; subtable=subtable->next ) + subtable->suffix = SuffixFromTags(otl->features); +} + +static void LangOrder(struct scriptlanglist *sl) { + int i,j; + uint32 lang, lang2; + + for ( i=0; i<sl->lang_cnt; ++i ) { + lang = i<MAX_LANG ? sl->langs[i] : sl->morelangs[i-MAX_LANG]; + for ( j=i+1; j<sl->lang_cnt; ++j ) { + lang2 = j<MAX_LANG ? sl->langs[j] : sl->morelangs[j-MAX_LANG]; + if ( lang>lang2 ) { + if ( i<MAX_LANG ) + sl->langs[i] = lang2; + else + sl->morelangs[i-MAX_LANG] = lang2; + if ( j<MAX_LANG ) + sl->langs[j] = lang; + else + sl->morelangs[j-MAX_LANG] = lang; + lang = lang2; + } + } + } +} + +static struct scriptlanglist *SLOrder(struct scriptlanglist *sl) { + int i,j, cnt; + struct scriptlanglist *sl2, *space[30], **allocked=NULL, **test = space; + + for ( sl2=sl, cnt=0; sl2!=NULL; sl2=sl2->next, ++cnt ) + LangOrder(sl2); + if ( cnt<=1 ) +return( sl ); + if ( cnt>30 ) + test = allocked = galloc(cnt*sizeof(struct scriptlanglist *)); + for ( sl2=sl, cnt=0; sl2!=NULL; sl2=sl2->next, ++cnt ) + test[cnt] = sl2; + for ( i=0; i<cnt; ++i ) for ( j=i+1; j<cnt; ++j ) { + if ( test[i]->script > test[j]->script ) { + struct scriptlanglist *temp; + temp = test[i]; + test[i] = test[j]; + test[j] = temp; + } + } + sl = test[0]; + for ( i=1; i<cnt; ++i ) + test[i-1]->next = test[i]; + test[i-1]->next = NULL; + free( allocked ); +return( sl ); +} + +FeatureScriptLangList *FLOrder(FeatureScriptLangList *fl) { + int i,j, cnt; + FeatureScriptLangList *fl2, *space[30], **allocked=NULL, **test = space; + + for ( fl2=fl, cnt=0; fl2!=NULL; fl2=fl2->next, ++cnt ) + fl2->scripts = SLOrder(fl2->scripts); + if ( cnt<=1 ) +return( fl ); + if ( cnt>30 ) + test = allocked = galloc(cnt*sizeof(FeatureScriptLangList *)); + for ( fl2=fl, cnt=0; fl2!=NULL; fl2=fl2->next, ++cnt ) + test[cnt] = fl2; + for ( i=0; i<cnt; ++i ) for ( j=i+1; j<cnt; ++j ) { + if ( test[i]->featuretag > test[j]->featuretag ) { + FeatureScriptLangList *temp; + temp = test[i]; + test[i] = test[j]; + test[j] = temp; + } + } + fl = test[0]; + for ( i=1; i<cnt; ++i ) + test[i-1]->next = test[i]; + test[i-1]->next = NULL; + free( allocked ); +return( fl ); +} + +struct scriptlanglist *SLCopy(struct scriptlanglist *sl) { + struct scriptlanglist *newsl; + + newsl = chunkalloc(sizeof(struct scriptlanglist)); + *newsl = *sl; + newsl->next = NULL; + + if ( sl->lang_cnt>MAX_LANG ) { + newsl->morelangs = galloc((newsl->lang_cnt-MAX_LANG)*sizeof(uint32)); + memcpy(newsl->morelangs,sl->morelangs,(newsl->lang_cnt-MAX_LANG)*sizeof(uint32)); + } +return( newsl ); +} + +struct scriptlanglist *SListCopy(struct scriptlanglist *sl) { + struct scriptlanglist *head=NULL, *last=NULL, *cur; + + for ( ; sl!=NULL; sl=sl->next ) { + cur = SLCopy(sl); + if ( head==NULL ) + head = cur; + else + last->next = cur; + last = cur; + } +return( head ); +} + +FeatureScriptLangList *FeatureListCopy(FeatureScriptLangList *fl) { + FeatureScriptLangList *newfl; + + if ( fl==NULL ) +return( NULL ); + + newfl = chunkalloc(sizeof(FeatureScriptLangList)); + *newfl = *fl; + newfl->next = NULL; + + newfl->scripts = SListCopy(fl->scripts); +return( newfl ); +} + +static void LangMerge(struct scriptlanglist *into, struct scriptlanglist *from) { + int i,j; + uint32 flang, tlang; + + for ( i=0 ; i<from->lang_cnt; ++i ) { + flang = i<MAX_LANG ? from->langs[i] : from->morelangs[i-MAX_LANG]; + for ( j=0; j<into->lang_cnt; ++j ) { + tlang = j<MAX_LANG ? into->langs[j] : into->morelangs[j-MAX_LANG]; + if ( tlang==flang ) + break; + } + if ( j==into->lang_cnt ) { + if ( into->lang_cnt<MAX_LANG ) + into->langs[into->lang_cnt++] = flang; + else { + into->morelangs = grealloc(into->morelangs,(into->lang_cnt+1-MAX_LANG)*sizeof(uint32)); + into->morelangs[into->lang_cnt++-MAX_LANG] = flang; + } + } + } +} + +void SLMerge(FeatureScriptLangList *into, struct scriptlanglist *fsl) { + struct scriptlanglist *isl; + + for ( ; fsl!=NULL; fsl = fsl->next ) { + for ( isl=into->scripts; isl!=NULL; isl=isl->next ) { + if ( fsl->script==isl->script ) + break; + } + if ( isl!=NULL ) + LangMerge(isl,fsl); + else { + isl = SLCopy(fsl); + isl->next = into->scripts; + into->scripts = isl; + } + } +} + +void FLMerge(OTLookup *into, OTLookup *from) { + /* Merge the feature list from "from" into "into" */ + FeatureScriptLangList *ifl, *ffl; + + /* first check for common featuretags and merge the scripts of each */ + for ( ffl = from->features; ffl!=NULL; ffl = ffl->next ) { + for ( ifl=into->features; ifl!=NULL; ifl=ifl->next ) { + if ( ffl->featuretag==ifl->featuretag ) + break; + } + if ( ifl!=NULL ) + SLMerge(ffl,ifl->scripts); + else { + ifl = FeatureListCopy(ffl); + ifl->next = into->features; + into->features = ifl; + } + } + into->features = FLOrder(into->features); +} + +void SFSubTablesMerge(SplineFont *_sf,struct lookup_subtable *subfirst, + struct lookup_subtable *subsecond) { + unsigned lookup_type = subfirst->lookup->lookup_type; + int gid,k,isv; + SplineChar *sc; + SplineFont *sf = _sf; + PST *pst, *fpst, *spst, *pstprev, *pstnext; + KernPair *fkp, *skp, *kpprev, *kpnext; + AnchorClass *ac; + + if ( lookup_type != subsecond->lookup->lookup_type ) { + IError("Attempt to merge lookup subtables with mismatch types"); +return; + } + if ( lookup_type != gsub_single && + lookup_type != gsub_multiple && + lookup_type != gsub_alternate && + lookup_type != gsub_ligature && + lookup_type != gpos_single && + lookup_type != gpos_pair && + lookup_type != gpos_cursive && + lookup_type != gpos_mark2base && + lookup_type != gpos_mark2ligature && + lookup_type != gpos_mark2mark ) { + IError("Attempt to merge lookup subtables with bad types"); +return; + } else if ( subfirst->kc!=NULL || subsecond->kc != NULL ) { + IError("Attempt to merge lookup subtables with kerning classes"); +return; + } + + if ( lookup_type==gpos_cursive || lookup_type==gpos_mark2base || + lookup_type==gpos_mark2ligature || lookup_type==gpos_mark2mark ) { + for ( ac = sf->anchor; ac!=NULL ; ac=ac->next ) + if ( ac->subtable == subsecond ) + ac->subtable = subfirst; + } else { + k=0; + do { + sf = _sf->subfontcnt==0 ? _sf : _sf->subfonts[k]; + for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) { + if ( lookup_type==gsub_single || lookup_type==gsub_multiple || + lookup_type==gsub_alternate || lookup_type==gpos_single ) { + fpst = spst = NULL; + for ( pst=sc->possub; pst!=NULL; pst=pst->next ) { + if ( pst->subtable == subfirst ) { + fpst = pst; + if ( spst!=NULL ) + break; + } else if ( pst->subtable == subsecond ) { + spst = pst; + if ( fpst!=NULL ) + break; + } + } + if ( fpst==NULL && spst!=NULL ) + spst->subtable = subfirst; + else if ( spst!=NULL ) { + LogError(_("The glyph, %s, contains a %s from %s and one from %s.\nThe one from %s will be removed.\n"), + sc->name, + lookup_type==gpos_single ? _("positioning") : _("substitution"), + subfirst->subtable_name, subsecond->subtable_name, + subsecond->subtable_name ); + pstprev = NULL; + for ( pst=sc->possub; pst!=NULL && pst!=spst; pstprev=pst, pst=pst->next ); + if ( pstprev==NULL ) + sc->possub = spst->next; + else + pstprev = spst->next; + spst->next = NULL; + PSTFree(spst); + } + } else if ( lookup_type==gsub_ligature || lookup_type==gpos_pair ) { + pstprev = NULL; + for ( spst=sc->possub; spst!=NULL ; spst = pstnext ) { + pstnext = spst->next; + if ( spst->subtable == subsecond ) { + for ( fpst=sc->possub; fpst!=NULL; fpst=fpst->next ) { + if ( fpst->subtable == subfirst && + strcmp(fpst->u.lig.components,spst->u.lig.components)==0 ) + break; + } + if ( fpst==NULL ) + spst->subtable = subfirst; + else { + LogError(_("The glyph, %s, contains the same %s from %s and from %s.\nThe one from %s will be removed.\n"), + sc->name, + lookup_type==gsub_ligature ? _("ligature") : _("kern pair"), + subfirst->subtable_name, subsecond->subtable_name, + subsecond->subtable_name ); + if ( pstprev==NULL ) + sc->possub = pstnext; + else + pstprev->next = pstnext; + spst->next = NULL; + PSTFree(spst); + spst = pstprev; + } + } + pstprev = spst; + } + for ( isv=0; isv<2; ++isv ) { + kpprev = NULL; + for ( skp=isv ? sc->vkerns : sc->kerns; skp!=NULL ; skp = kpnext ) { + kpnext = skp->next; + if ( skp->subtable == subsecond ) { + for ( fkp=isv ? sc->vkerns : sc->kerns; fkp!=NULL; fkp=fkp->next ) { + if ( fkp->subtable == subfirst && fkp->sc==skp->sc ) + break; + } + if ( fkp==NULL ) + skp->subtable = subfirst; + else { + LogError(_("The glyph, %s, contains the same kern pair from %s and from %s.\nThe one from %s will be removed.\n"), + sc->name, + subfirst->subtable_name, subsecond->subtable_name, + subsecond->subtable_name ); + if ( kpprev!=NULL ) + kpprev->next = kpnext; + else if ( isv ) + sc->vkerns = kpnext; + else + sc->kerns = kpnext; + skp->next = NULL; + KernPairsFree(skp); + skp = kpprev; + } + } + kpprev = skp; + } + } + } + } + ++k; + } while ( k<_sf->subfontcnt ); + } +} + +/* ************************************************************************** */ +/* ******************************* copy lookup ****************************** */ +/* ************************************************************************** */ + +static char **ClassCopy(int class_cnt,char **classes) { + char **newclasses; + int i; + + if ( classes==NULL || class_cnt==0 ) +return( NULL ); + newclasses = galloc(class_cnt*sizeof(char *)); + for ( i=0; i<class_cnt; ++i ) + newclasses[i] = copy(classes[i]); +return( newclasses ); +} + +static OTLookup *_OTLookupCopyInto(struct sfmergecontext *mc, + OTLookup *from_otl, OTLookup *before, int do_contents); +static OTLookup *OTLookupCopyNested(struct sfmergecontext *mc, + OTLookup *from_otl) { + char *newname; + OTLookup *to_nested_otl; + int l; + + if ( from_otl==NULL ) +return( NULL ); + + for ( l=0; l<mc->lcnt; ++l ) { + if ( mc->lks[l].from == from_otl ) +return( mc->lks[l].to ); + } + + newname = strconcat(mc->prefix,from_otl->lookup_name); + to_nested_otl = SFFindLookup(mc->sf_to,newname); + free(newname); + if ( to_nested_otl==NULL ) + to_nested_otl = _OTLookupCopyInto(mc, from_otl, (OTLookup *) -1, true ); +return( to_nested_otl ); +} + +static KernClass *SF_AddKernClass(struct sfmergecontext *mc,KernClass *kc, + struct lookup_subtable *sub ) { + KernClass *newkc; + + newkc = chunkalloc(sizeof(KernClass)); + *newkc = *kc; + newkc->subtable = sub; + if ( sub->vertical_kerning ) { + newkc->next = mc->sf_to->vkerns; + mc->sf_to->vkerns = newkc; + } else { + newkc->next = mc->sf_to->kerns; + mc->sf_to->kerns = newkc; + } + + newkc->firsts = ClassCopy(newkc->first_cnt,newkc->firsts); + newkc->seconds = ClassCopy(newkc->second_cnt,newkc->seconds); + newkc->offsets = galloc(newkc->first_cnt*newkc->second_cnt*sizeof(int16)); + memcpy(newkc->offsets,kc->offsets,newkc->first_cnt*newkc->second_cnt*sizeof(int16)); +return( newkc ); +} + +static FPST *SF_AddFPST(struct sfmergecontext *mc,FPST *fpst, + struct lookup_subtable *sub ) { + FPST *newfpst; + int i, k, cur; + + newfpst = chunkalloc(sizeof(FPST)); + *newfpst = *fpst; + newfpst->subtable = sub; + newfpst->next = mc->sf_to->possub; + mc->sf_to->possub = newfpst; + + newfpst->nclass = ClassCopy(newfpst->nccnt,newfpst->nclass); + newfpst->bclass = ClassCopy(newfpst->bccnt,newfpst->bclass); + newfpst->fclass = ClassCopy(newfpst->fccnt,newfpst->fclass); + + newfpst->rules = galloc(newfpst->rule_cnt*sizeof(struct fpst_rule)); + memcpy(newfpst->rules,fpst->rules,newfpst->rule_cnt*sizeof(struct fpst_rule)); + + cur = 0; + for ( i=0; i<newfpst->rule_cnt; ++i ) { + struct fpst_rule *r = &newfpst->rules[i], *oldr = &fpst->rules[i]; + + r->lookups = galloc(r->lookup_cnt*sizeof(struct seqlookup)); + memcpy(r->lookups,oldr->lookups,r->lookup_cnt*sizeof(struct seqlookup)); + for ( k=0; k<r->lookup_cnt; ++k ) { + r->lookups[k].lookup = OTLookupCopyNested(mc, + r->lookups[k].lookup); + } + + switch ( newfpst->format ) { + case pst_glyphs: + r->u.glyph.names = copy( r->u.glyph.names ); + r->u.glyph.back = copy( r->u.glyph.back ); + r->u.glyph.fore = copy( r->u.glyph.fore ); + break; + case pst_class: + r->u.class.nclasses = galloc( r->u.class.ncnt*sizeof(uint16)); + memcpy(r->u.class.nclasses,oldr->u.class.nclasses, r->u.class.ncnt*sizeof(uint16)); + r->u.class.bclasses = galloc( r->u.class.bcnt*sizeof(uint16)); + memcpy(r->u.class.bclasses,oldr->u.class.bclasses, r->u.class.ncnt*sizeof(uint16)); + r->u.class.fclasses = galloc( r->u.class.fcnt*sizeof(uint16)); + memcpy(r->u.class.fclasses,oldr->u.class.fclasses, r->u.class.fcnt*sizeof(uint16)); + break; + case pst_coverage: + r->u.coverage.ncovers = ClassCopy( r->u.coverage.ncnt, r->u.coverage.ncovers ); + r->u.coverage.bcovers = ClassCopy( r->u.coverage.bcnt, r->u.coverage.bcovers ); + r->u.coverage.fcovers = ClassCopy( r->u.coverage.fcnt, r->u.coverage.fcovers ); + break; + case pst_reversecoverage: + r->u.rcoverage.ncovers = ClassCopy( r->u.rcoverage.always1, r->u.rcoverage.ncovers ); + r->u.rcoverage.bcovers = ClassCopy( r->u.rcoverage.bcnt, r->u.rcoverage.bcovers ); + r->u.rcoverage.fcovers = ClassCopy( r->u.rcoverage.fcnt, r->u.rcoverage.fcovers ); + r->u.rcoverage.replacements = copy( r->u.rcoverage.replacements ); + break; + } + } +return( newfpst ); +} + +static ASM *SF_AddASM(struct sfmergecontext *mc,ASM *sm, struct lookup_subtable *sub ) { + ASM *newsm; + int i; + + newsm = chunkalloc(sizeof(ASM)); + *newsm = *sm; + newsm->subtable = sub; + newsm->next = mc->sf_to->sm; + mc->sf_to->sm = newsm; + mc->sf_to->changed = true; + newsm->classes = ClassCopy(newsm->class_cnt, newsm->classes); + newsm->state = galloc(newsm->class_cnt*newsm->state_cnt*sizeof(struct asm_state)); + memcpy(newsm->state,sm->state, + newsm->class_cnt*newsm->state_cnt*sizeof(struct asm_state)); + if ( newsm->type == asm_kern ) { + for ( i=newsm->class_cnt*newsm->state_cnt-1; i>=0; --i ) { + newsm->state[i].u.kern.kerns = galloc(newsm->state[i].u.kern.kcnt*sizeof(int16)); + memcpy(newsm->state[i].u.kern.kerns,sm->state[i].u.kern.kerns,newsm->state[i].u.kern.kcnt*sizeof(int16)); + } + } else if ( newsm->type == asm_insert ) { + for ( i=0; i<newsm->class_cnt*newsm->state_cnt; ++i ) { + struct asm_state *this = &newsm->state[i]; + this->u.insert.mark_ins = copy(this->u.insert.mark_ins); + this->u.insert.cur_ins = copy(this->u.insert.cur_ins); + } + } else if ( newsm->type == asm_context ) { + for ( i=0; i<newsm->class_cnt*newsm->state_cnt; ++i ) { + newsm->state[i].u.context.mark_lookup = OTLookupCopyNested(mc, + newsm->state[i].u.context.mark_lookup); + newsm->state[i].u.context.cur_lookup = OTLookupCopyNested(mc, + newsm->state[i].u.context.cur_lookup); + } + } +return( newsm ); +} + +static SplineChar *SCFindOrMake(SplineFont *into,SplineChar *fromsc) { + int to_index; + + if ( into->cidmaster==NULL && into->fv!=NULL ) { + to_index = SFFindSlot(into,into->fv->map,fromsc->unicodeenc,fromsc->name); + if ( to_index==-1 ) +return( NULL ); +return( SFMakeChar(into,into->fv->map,to_index)); + } +return( SFGetChar(into,fromsc->unicodeenc,fromsc->name)); +} + +static void SF_SCAddAP(SplineChar *tosc,AnchorPoint *ap, AnchorClass *newac) { + AnchorPoint *newap; + + newap = chunkalloc(sizeof(AnchorPoint)); + *newap = *ap; + newap->anchor = newac; + newap->next = tosc->anchor; + tosc->anchor = newap; +} + +static void SF_AddAnchorClasses(struct sfmergecontext *mc, + struct lookup_subtable *from_sub, struct lookup_subtable *sub ) { + AnchorClass *ac, *nac; + int k, gid; + SplineFont *fsf; + AnchorPoint *ap; + SplineChar *fsc, *tsc; + + for ( ac=mc->sf_from->anchor; ac!=NULL; ac=ac->next ) if ( ac->subtable==from_sub ) { + nac = chunkalloc(sizeof(AnchorClass)); + *nac = *ac; + nac->subtable = sub; + nac->name = strconcat(mc->prefix,nac->name); + nac->next = mc->sf_to->anchor; + mc->sf_to->anchor = nac; + + k=0; + do { + fsf = mc->sf_from->subfontcnt==0 ? mc->sf_from : mc->sf_from->subfonts[k]; + for ( gid = 0; gid<fsf->glyphcnt; ++gid ) if ( (fsc = fsf->glyphs[gid])!=NULL ) { + for ( ap=fsc->anchor; ap!=NULL; ap=ap->next ) { + if ( ap->anchor==ac ) { + tsc = SCFindOrMake(mc->sf_to,fsc); + if ( tsc==NULL ) + break; + SF_SCAddAP(tsc,ap,nac); + } + } + } + ++k; + } while ( k<mc->sf_from->subfontcnt ); + } +} + +static int SF_SCAddPST(SplineChar *tosc,PST *pst,struct lookup_subtable *sub) { + PST *newpst; + + newpst = chunkalloc(sizeof(PST)); + *newpst = *pst; + newpst->subtable = sub; + newpst->next = tosc->possub; + tosc->possub = newpst; + + switch( newpst->type ) { + case pst_pair: + newpst->u.pair.paired = copy(pst->u.pair.paired); + newpst->u.pair.vr = chunkalloc(sizeof(struct vr [2])); + memcpy(newpst->u.pair.vr,pst->u.pair.vr,sizeof(struct vr [2])); + break; + case pst_ligature: + newpst->u.lig.lig = tosc; + /* Fall through */ + case pst_substitution: + case pst_alternate: + case pst_multiple: + newpst->u.subs.variant = copy(pst->u.subs.variant); + break; + } +return( true ); +} + +static int SF_SCAddKP(SplineChar *tosc,KernPair *kp,struct lookup_subtable *sub, + int isvkern, SplineFont *to_sf ) { + SplineChar *tosecond; + KernPair *newkp; + + tosecond = SFGetChar(to_sf,kp->sc->unicodeenc,kp->sc->name); + if ( tosecond==NULL ) +return( false ); + + newkp = chunkalloc(sizeof(KernPair)); + *newkp = *kp; + newkp->subtable = sub; + newkp->sc = tosecond; + if ( isvkern ) { + newkp->next = tosc->vkerns; + tosc->vkerns = newkp; + } else { + newkp->next = tosc->kerns; + tosc->kerns = newkp; + } +return(true); +} + +static void SF_AddPSTKern(struct sfmergecontext *mc,struct lookup_subtable *from_sub, struct lookup_subtable *sub) { + int k, gid, isv; + SplineFont *fsf; + SplineChar *fsc, *tsc; + PST *pst; + KernPair *kp; + int iskern = sub->lookup->lookup_type==gpos_pair; + + k=0; + do { + fsf = mc->sf_from->subfontcnt==0 ? mc->sf_from : mc->sf_from->subfonts[k]; + for ( gid = 0; gid<fsf->glyphcnt; ++gid ) if ( (fsc = fsf->glyphs[gid])!=NULL ) { + tsc = (SplineChar *) -1; + for ( pst = fsc->possub; pst!=NULL; pst=pst->next ) { + if ( pst->subtable==from_sub ) { + if ( tsc==(SplineChar *) -1 ) { + tsc = SCFindOrMake(mc->sf_to,fsc); + if ( tsc==NULL ) + break; + } + SF_SCAddPST(tsc,pst,sub); + } + } + if ( tsc!=NULL && iskern ) { + for ( isv=0; isv<2 && tsc!=NULL; ++isv ) { + for ( kp= isv ? fsc->vkerns : fsc->kerns; kp!=NULL; kp=kp->next ) { + if ( kp->subtable==sub ) { + /* Kerning data tend to be individualistic. Only copy if */ + /* glyphs exist */ + if ( tsc==(SplineChar *) -1 ) { + tsc = SFGetChar(mc->sf_to,fsc->unicodeenc,fsc->name); + if ( tsc==NULL ) + break; + } + SF_SCAddKP(tsc,kp,sub,isv,mc->sf_to); + } + } + } + } + } + ++k; + } while ( k<mc->sf_from->subfontcnt ); +} + +int _FeatureOrderId( int isgpos,uint32 tag ) { + /* This is the order in which features should be executed */ + + if ( !isgpos ) switch ( tag ) { +/* GSUB ordering */ + case CHR('c','c','m','p'): /* Must be first? */ +return( -2 ); + case CHR('l','o','c','l'): /* Language dependent letter forms (serbian uses some different glyphs than russian) */ +return( -1 ); + case CHR('i','s','o','l'): +return( 0 ); + case CHR('j','a','l','t'): /* must come after 'isol' */ +return( 1 ); + case CHR('f','i','n','a'): +return( 2 ); + case CHR('f','i','n','2'): + case CHR('f','a','l','t'): /* must come after 'fina' */ +return( 3 ); + case CHR('f','i','n','3'): +return( 4 ); + case CHR('m','e','d','i'): +return( 5 ); + case CHR('m','e','d','2'): +return( 6 ); + case CHR('i','n','i','t'): +return( 7 ); + + case CHR('r','t','l','a'): +return( 100 ); + case CHR('s','m','c','p'): case CHR('c','2','s','c'): +return( 200 ); + + case CHR('r','l','i','g'): +return( 300 ); + case CHR('c','a','l','t'): +return( 301 ); + case CHR('l','i','g','a'): +return( 302 ); + case CHR('d','l','i','g'): case CHR('h','l','i','g'): +return( 303 ); + case CHR('c','s','w','h'): +return( 304 ); + case CHR('m','s','e','t'): +return( 305 ); + + case CHR('f','r','a','c'): +return( 306 ); + +/* Indic processing */ + case CHR('n','u','k','t'): + case CHR('p','r','e','f'): +return( 301 ); + case CHR('a','k','h','n'): +return( 302 ); + case CHR('r','p','h','f'): +return( 303 ); + case CHR('b','l','w','f'): +return( 304 ); + case CHR('h','a','l','f'): + case CHR('a','b','v','f'): +return( 305 ); + case CHR('p','s','t','f'): +return( 306 ); + case CHR('v','a','t','u'): +return( 307 ); + + case CHR('p','r','e','s'): +return( 310 ); + case CHR('b','l','w','s'): +return( 311 ); + case CHR('a','b','v','s'): +return( 312 ); + case CHR('p','s','t','s'): +return( 313 ); + case CHR('c','l','i','g'): +return( 314 ); + + case CHR('h','a','l','n'): +return( 320 ); +/* end indic ordering */ + + case CHR('a','f','r','c'): + case CHR('l','j','m','o'): + case CHR('v','j','m','o'): +return( 350 ); + case CHR('v','r','t','2'): case CHR('v','e','r','t'): +return( 1010 ); /* Documented to come last */ + +/* Unknown things come after everything but vert/vrt2 */ + default: +return( 1000 ); + + } else switch ( tag ) { +/* GPOS ordering */ + case CHR('c','u','r','s'): +return( 0 ); + case CHR('d','i','s','t'): +return( 100 ); + case CHR('b','l','w','m'): +return( 201 ); + case CHR('a','b','v','m'): +return( 202 ); + case CHR('k','e','r','n'): +return( 300 ); + case CHR('m','a','r','k'): +return( 400 ); + case CHR('m','k','m','k'): +return( 500 ); +/* Unknown things come after everything */ + default: +return( 1000 ); + } +} + +int FeatureOrderId( int isgpos,FeatureScriptLangList *fl ) { + int pos = 9999, temp; + + if ( fl==NULL ) +return( 0 ); + + while ( fl!=NULL ) { + temp = _FeatureOrderId(isgpos,fl->featuretag ); + if ( temp<pos ) pos = temp; + fl = fl->next; + } +return( pos ); +} + +void SortInsertLookup(SplineFont *sf, OTLookup *newotl) { + int isgpos = newotl->lookup_type>=gpos_start; + int pos; + OTLookup *prev, *otl; + + pos = FeatureOrderId(isgpos,newotl->features); + for ( prev=NULL, otl= isgpos ? sf->gpos_lookups : sf->gsub_lookups ; + otl!=NULL && FeatureOrderId(isgpos,newotl->features)<pos; + prev = otl, otl=otl->next ); + newotl->next = otl; + if ( prev!=NULL ) + prev->next = newotl; + else if ( isgpos ) + sf->gpos_lookups = newotl; + else + sf->gsub_lookups = newotl; +} + +/* Before may be: + * A lookup in into_sf, in which case insert new lookup before it + * NULL , in which case insert new lookup at end + * -1 , in which case insert new lookup at start + * -2 , try to guess a good position +*/ +static void OrderNewLookup(SplineFont *into_sf,OTLookup *otl,OTLookup *before) { + int isgpos = otl->lookup_type>=gpos_start; + OTLookup **head = isgpos ? &into_sf->gpos_lookups : &into_sf->gsub_lookups; + OTLookup *prev; + + if ( before == (OTLookup *) -2 ) + SortInsertLookup(into_sf,otl); + else if ( before == (OTLookup *) -1 || *head==NULL || *head==before ) { + otl->next = *head; + *head = otl; + } else { + for ( prev= *head; prev->next!=NULL && prev->next!=before ; prev=prev->next ); + otl->next = prev->next; + prev->next = otl; + } +} + +static OTLookup *_OTLookupCopyInto(struct sfmergecontext *mc, + OTLookup *from_otl, OTLookup *before, int do_contents) { + OTLookup *otl; + struct lookup_subtable *sub, *last, *from_sub; + int scnt, l; + + for ( l=0; l<mc->lcnt; ++l ) { + if ( mc->lks[l].from == from_otl ) { + if ( mc->lks[l].old ) +return( mc->lks[l].to ); + else + break; + } + } + + if ( l>=mc->lmax ) + mc->lks = grealloc(mc->lks,(mc->lmax += 20)*sizeof(struct lookup_cvt)); + mc->sf_to->changed = true; + + if ( l>=mc->lcnt ) { + otl = chunkalloc(sizeof(OTLookup)); + *otl = *from_otl; + memset(&mc->lks[l],0,sizeof(mc->lks[l])); + mc->lks[l].from = from_otl; mc->lks[l].to = otl; ++mc->lcnt; + otl->lookup_name = strconcat(mc->prefix,from_otl->lookup_name); + otl->features = FeatureListCopy(from_otl->features); + otl->next = NULL; otl->subtables = NULL; + OrderNewLookup(mc->sf_to,otl,before); + if ( !do_contents ) + FIOTLookupCopyInto(mc->sf_to,mc->sf_from, from_otl, otl, 0, before); + } else + otl = mc->lks[l].to; + if ( !do_contents ) +return( otl ); + + last = NULL; + scnt = 0; + for ( from_sub = from_otl->subtables; from_sub!=NULL; from_sub=from_sub->next ) { + sub = chunkalloc(sizeof(struct lookup_subtable)); + *sub = *from_sub; + sub->lookup = otl; + sub->subtable_name = strconcat(mc->prefix,from_sub->subtable_name); + sub->suffix = copy(sub->suffix); + if ( last==NULL ) + otl->subtables = sub; + else + last->next = sub; + last = sub; + if ( from_sub->kc!=NULL ) + sub->kc = SF_AddKernClass(mc, from_sub->kc, sub); + else if ( from_sub->fpst!=NULL ) + sub->fpst = SF_AddFPST(mc, from_sub->fpst, sub); + else if ( from_sub->sm!=NULL ) + sub->sm = SF_AddASM(mc, from_sub->sm, sub); + else if ( from_sub->anchor_classes ) + SF_AddAnchorClasses(mc, from_sub, sub); + else + SF_AddPSTKern(mc, from_sub, sub); + ++scnt; + } + FIOTLookupCopyInto(mc->sf_to,mc->sf_from, from_otl, otl, scnt, before); +return( otl ); +} + +static int NeedsPrefix(SplineFont *into_sf,SplineFont *from_sf, OTLookup **list) { + struct lookup_subtable *from_sub; + int i,j,k; + OTLookup *sublist[2]; + + sublist[1] = NULL; + + if ( list==NULL || list[0]==NULL ) +return( false ); + for ( k=0; list[k]!=NULL; ++k ) { + OTLookup *from_otl = list[k]; + if ( SFFindLookup(into_sf,from_otl->lookup_name)!=NULL ) +return( true ); + for ( from_sub = from_otl->subtables; from_sub!=NULL; from_sub=from_sub->next ) { + if ( from_sub->fpst!=NULL ) { + for ( i=0; i<from_sub->fpst->rule_cnt; ++i ) { + struct fpst_rule *r = &from_sub->fpst->rules[i]; + for ( j=0; j<r->lookup_cnt; ++j ) { + sublist[0] = r->lookups[j].lookup; + if ( NeedsPrefix(into_sf,from_sf, sublist)) +return( true ); + } + } + } else if ( from_sub->sm!=NULL && from_sub->sm->type==asm_context ) { + for ( i=0; i<from_sub->sm->class_cnt*from_sub->sm->state_cnt; ++i ) { + sublist[0] = from_sub->sm->state[i].u.context.mark_lookup; + if ( NeedsPrefix(into_sf,from_sf,sublist)) +return( true ); + sublist[0] = from_sub->sm->state[i].u.context.cur_lookup; + if ( NeedsPrefix(into_sf,from_sf,sublist)) +return( true ); + } + } + } + } +return( false ); +} + +OTLookup *OTLookupCopyInto(SplineFont *into_sf,SplineFont *from_sf, OTLookup *from_otl) { + OTLookup *newotl, *list[2]; + struct sfmergecontext mc; + + memset(&mc,0,sizeof(mc)); + mc.sf_from = from_sf; mc.sf_to = into_sf; + + list[0] = from_otl; list[1] = NULL; + mc.prefix = NeedsPrefix(into_sf,from_sf,list) + ? strconcat(from_sf->fontname,"-") : copy(""); + newotl = _OTLookupCopyInto(&mc,from_otl,(OTLookup *) -2,true); + free(mc.lks); + free(mc.prefix); +return( newotl ); +} + +void OTLookupsCopyInto(SplineFont *into_sf,SplineFont *from_sf, + OTLookup **list, OTLookup *before) { + int i, do_contents; + struct sfmergecontext mc; + + memset(&mc,0,sizeof(mc)); + mc.sf_from = from_sf; mc.sf_to = into_sf; + + mc.prefix = NeedsPrefix(into_sf,from_sf,list) + ? strconcat(from_sf->fontname,"-") : copy(""); + for ( i=0; list[i]!=NULL; ++i ); + mc.lks = galloc((mc.lmax=i+5)*sizeof(struct lookup_cvt)); + /* First create all the lookups and position them in the right order */ + /* then create subtables (which may in turn create some new lookups */ + /* for contextual lookups which invoke other lookups, don't care how */ + /* those nested lookups are ordered) */ + for ( do_contents=0; do_contents<2; ++do_contents ) + for ( i=0; list[i]!=NULL; ++i ) + (void) _OTLookupCopyInto(&mc,list[i],before,do_contents); + free(mc.lks); + free(mc.prefix); +} + +/* ************************************************************************** */ +/* ****************************** Apply lookups ***************************** */ +/* ************************************************************************** */ + +struct lookup_data { + struct opentype_str *str; + int cnt, max; + + uint32 script; + SplineFont *sf; + + struct lookup_subtable *lig_owner; + int lcnt, lmax; + SplineChar ***ligs; /* For each ligature we have an array of SplineChars that are its components preceded by the ligature glyph itself */ + /* NULL terminated */ + int pixelsize; + double scale; +}; + +static int ApplyLookupAtPos(uint32 tag, OTLookup *otl,struct lookup_data *data,int pos); + +static int GlyphNameInClass(char *name,char *class ) { + char *pt; + int len = strlen(name); + + if ( class==NULL ) +return( false ); + + pt = class; + while ( (pt=strstr(pt,name))!=NULL ) { + if ( pt==NULL ) +return( false ); + if ( (pt==class || pt[-1]==' ') && (pt[len]=='\0' || pt[len]==' ')) +return( true ); + pt+=len; + } + +return( false ); +} + +/* ************************************************************************** */ +/* ************************ Apply Apple State Machines ********************** */ +/* ************************************************************************** */ + +static void ApplyMacIndicRearrangement(struct lookup_data *data,int verb, + int first_pos,int last_pos) { + struct opentype_str temp, temp2, temp3, temp4; + int i; + + if ( first_pos==-1 || last_pos==-1 || last_pos <= first_pos ) +return; + switch ( verb ) { + case 1: /* Ax => xA */ + temp = data->str[first_pos]; + for ( i= first_pos; i<last_pos; ++i ) + data->str[i] = data->str[i+1]; + data->str[last_pos] = temp; + break; + case 2: /* xD => Dx */ + temp = data->str[last_pos]; + for ( i= last_pos; i>first_pos; --i ) + data->str[i] = data->str[i-1]; + data->str[first_pos] = temp; + break; + case 3: /* AxD => DxA */ + temp = data->str[last_pos]; + data->str[last_pos] = data->str[first_pos]; + data->str[first_pos] = temp; + break; + case 4: /* ABx => xAB */ + temp = data->str[first_pos]; + temp2 = data->str[first_pos+1]; + for ( i= first_pos; i<last_pos-1; ++i ) + data->str[i] = data->str[i+21]; + data->str[last_pos-1] = temp; + data->str[last_pos] = temp2; + break; + case 5: /* ABx => xBA */ + temp = data->str[first_pos]; + temp2 = data->str[first_pos+1]; + for ( i= first_pos; i<last_pos-1; ++i ) + data->str[i] = data->str[i+21]; + data->str[last_pos-1] = temp2; + data->str[last_pos] = temp; + break; + case 6: /* xCD => CDx */ + temp = data->str[last_pos]; + temp2 = data->str[last_pos-1]; + for ( i= last_pos; i>first_pos+1; --i ) + data->str[i] = data->str[i-2]; + data->str[first_pos+1] = temp; + data->str[first_pos] = temp2; + break; + case 7: /* xCD => DCx */ + temp = data->str[last_pos]; + temp2 = data->str[last_pos-1]; + for ( i= last_pos; i>first_pos+1; --i ) + data->str[i] = data->str[i-2]; + data->str[first_pos+1] = temp2; + data->str[first_pos] = temp; + break; + case 8: /* AxCD => CDxA */ + temp = data->str[first_pos]; + temp2 = data->str[last_pos-1]; + temp3 = data->str[last_pos]; + for ( i= last_pos-1; i>first_pos; --i ) + data->str[i] = data->str[i-1]; + data->str[first_pos+1] = temp2; + data->str[first_pos] = temp3; + data->str[last_pos] = temp; + break; + case 9: /* AxCD => DCxA */ + temp = data->str[first_pos]; + temp2 = data->str[last_pos-1]; + temp3 = data->str[last_pos]; + for ( i= last_pos-1; i>first_pos; --i ) + data->str[i] = data->str[i-1]; + data->str[first_pos+1] = temp3; + data->str[first_pos] = temp2; + data->str[last_pos] = temp; + break; + case 10: /* ABxD => DxAB */ + temp = data->str[first_pos]; + temp2 = data->str[first_pos+1]; + temp3 = data->str[last_pos]; + for ( i= first_pos+1; i<last_pos; ++i ) + data->str[i] = data->str[i+1]; + data->str[first_pos] = temp3; + data->str[last_pos-1] = temp; + data->str[last_pos] = temp2; + break; + case 11: /* ABxD => DxBA */ + temp = data->str[first_pos]; + temp2 = data->str[first_pos+1]; + temp3 = data->str[last_pos]; + for ( i= first_pos+1; i<last_pos; ++i ) + data->str[i] = data->str[i+1]; + data->str[first_pos] = temp3; + data->str[last_pos-1] = temp2; + data->str[last_pos] = temp; + break; + case 12: /* ABxCD => CDxAB */ + temp = data->str[first_pos]; + temp2 = data->str[first_pos+1]; + temp3 = data->str[last_pos-1]; + temp4 = data->str[last_pos]; + data->str[last_pos] = temp2; + data->str[last_pos-1] = temp; + data->str[first_pos+1] = temp4; + data->str[first_pos] = temp3; + break; + case 13: /* ABxCD => CDxBA */ + temp = data->str[first_pos]; + temp2 = data->str[first_pos+1]; + temp3 = data->str[last_pos-1]; + temp4 = data->str[last_pos]; + data->str[last_pos] = temp; + data->str[last_pos-1] = temp2; + data->str[first_pos+1] = temp4; + data->str[first_pos] = temp3; + break; + case 14: /* ABxCD => DCxAB */ + temp = data->str[first_pos]; + temp2 = data->str[first_pos+1]; + temp3 = data->str[last_pos-1]; + temp4 = data->str[last_pos]; + data->str[last_pos] = temp2; + data->str[last_pos-1] = temp; + data->str[first_pos+1] = temp3; + data->str[first_pos] = temp4; + break; + case 15: /* ABxCD => DCxBA */ + temp = data->str[first_pos]; + temp2 = data->str[first_pos+1]; + temp3 = data->str[last_pos-1]; + temp4 = data->str[last_pos]; + data->str[last_pos] = temp; + data->str[last_pos-1] = temp2; + data->str[first_pos+1] = temp3; + data->str[first_pos] = temp4; + break; + } +} + +static int ApplyMacInsert(struct lookup_data *data,int ipos,int cnt, + char *glyphnames, int orig_index) { + SplineChar *inserts[32]; + char *start, *pt; + int i, ch; + + if ( cnt==0 || glyphnames==NULL || ipos == -1 ) +return( 0 ); + + for ( i=0, start = glyphnames; i<cnt; ) { + while ( *start==' ' ) ++start; + if ( *start=='\0' ) + break; + for ( pt = start; *pt!=' ' && *pt!='\0'; ++pt ); + ch = *pt; *pt = '\0'; + inserts[i] = SFGetChar(data->sf,-1,start); + *pt = ch; + if ( inserts[i]!=NULL ) + ++i; + } + cnt = i; + if ( i==0 ) +return( 0 ); + for ( i= data->cnt; i>=ipos; --i ) + data->str[i+cnt] = data->str[i]; + memset(data->str+ipos,0,cnt*sizeof(struct opentype_str)); + for ( i=0; i<cnt; ++i ) { + data->str[ipos+i].sc = inserts[i]; + data->str[ipos+i].orig_index = orig_index; + } +return( cnt ); +} + +static void ApplyAppleStateMachine(uint32 tag, OTLookup *otl,struct lookup_data *data) { + struct lookup_subtable *sub; + int state, class, pos, mark_pos, markend_pos, i; + ASM *sm; + int cnt_cur, cnt_mark; + struct asm_state *entry; + int kern_stack[8], kcnt; /* Kerning state machines handle at most 8 glyphs */ + (void)tag; + /* Flaws: Line processing has not been done yet, so we are never in the */ + /* start of line state and we never get an end of line token. We never */ + /* get deleted tokens either, those glyphs are just gone */ + /* Class 0: End of text */ + /* Class 1: Glyph not in any classes */ + /* Class 2: Deleted (we never see) */ + /* Class 3: End of line (we never see) */ + /* Mac doesn't have the concept of subtables, but a user could create one */ + /* it will get flattened out into its own "lookup" when written to a file*/ + /* So if there are multiple subtables, just process them all */ + for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) { + sm = sub->sm; + + state = 0; + mark_pos = markend_pos = -1; + for ( pos = 0; pos<=data->cnt; ) { + if ( pos==data->cnt ) + class = 0; + else { + for ( class = sm->class_cnt-1; class>3; --class ) + if ( GlyphNameInClass(data->str[i].sc->name,sm->classes[class]) ) + break; + if ( class==3 ) + class = 1; /* Glyph not in any class */; + } + entry = &sm->state[state*sm->class_cnt+class]; + switch ( otl->lookup_type ) { + case morx_context: + if ( entry->u.context.cur_lookup!=NULL ) + ApplyLookupAtPos(0,entry->u.context.cur_lookup,data,pos); + if ( entry->u.context.mark_lookup!=NULL && mark_pos!=-1 ) { + ApplyLookupAtPos(0,entry->u.context.mark_lookup,data,mark_pos); + mark_pos = -1; + } + break; + case morx_indic: + if ( entry->flags & 0x2000 ) + markend_pos = pos; + if ( (entry->flags&0xf)!=0 ) { + ApplyMacIndicRearrangement(data,entry->flags&0xf,mark_pos,markend_pos); + mark_pos = markend_pos = -1; + } + break; + case morx_insert: + /* I live in total ignorance of what I should do if the glyph */ + /* "is Kashida like"... so I ignore those flags. */ + cnt_cur = (entry->flags>>5)&0x1f; + cnt_mark = (entry->flags&0x1f); + if ( data->cnt + cnt_cur + cnt_mark >= data->max ) + data->str = grealloc(data->str,(data->max = data->cnt + cnt_cur + cnt_mark +20)*sizeof(struct opentype_str)); + if ( cnt_cur!=0 ) + cnt_cur = ApplyMacInsert(data,(entry->flags& 0x0800)? pos : pos+1, + cnt_cur,entry->u.insert.cur_ins,data->str[pos].orig_index); + if ( cnt_mark!=0 && mark_pos!=-1 ) { + cnt_mark = ApplyMacInsert(data,(entry->flags& 0x0800)? mark_pos : mark_pos+1, + cnt_mark,entry->u.insert.mark_ins,data->str[mark_pos].orig_index); + mark_pos = -1; + } else + cnt_mark = 0; + pos += cnt_cur+cnt_mark; + break; + case kern_statemachine: + if ( entry->u.kern.kcnt!=0 ) { + for ( i=0; i<kcnt && i<entry->u.kern.kcnt; ++i ) + data->str[kern_stack[i]].vr.h_adv_off += + entry->u.kern.kerns[i]; + kcnt = 0; + } + if ( entry->flags & 0x8000 ) { + for ( i=6; i>=0; --i ) + kern_stack[i+1] = kern_stack[i]; + kern_stack[0] = pos; + if ( ++kcnt>8 ) kcnt = 8; + } + break; + default: /* for -W */ + break; + } + if ( entry->flags & 0x8000 ) + mark_pos = pos; /* The docs do not state whether this happens before or after substitutions are applied at the mark */ + /* after is more useful. So assume that */ + if ( !(entry->flags & 0x4000) ) + ++pos; + state = entry->next_state; + } + } +} + +/* ************************************************************************** */ +/* ************************* Apply OpenType Lookups ************************* */ +/* ************************************************************************** */ + +static void LigatureFree(struct lookup_data *data) { + int i; + + if ( data->ligs==NULL ) +return; + for ( i=0; data->ligs[i]!=NULL; ++i ) + free(data->ligs[i]); +} + +static void LigatureSearch(struct lookup_subtable *sub, struct lookup_data *data) { + SplineFont *sf = data->sf; + int gid, ccnt, cnt, ch, err; + SplineChar *sc; + PST *pst; + char *pt, *start; + + LigatureFree(data); + cnt = 0; + for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) { + for ( pst=sc->possub; pst!=NULL; pst=pst->next ) if ( pst->subtable==sub ) { + for ( pt = pst->u.lig.components, ccnt=0; *pt; ++pt ) + if ( *pt==' ' ) + ++ccnt; + if ( cnt>=data->lmax ) + data->ligs = grealloc(data->ligs,(data->lmax+=100)*sizeof(SplineChar **)); + data->ligs[cnt] = galloc((ccnt+3)*sizeof(SplineChar *)); + data->ligs[cnt][0] = sc; + ccnt = 1; + err = 0; + for ( pt = pst->u.lig.components; *pt; ) { + while ( *pt==' ' ) ++pt; + if ( *pt=='\0' ) + break; + for ( start=pt; *pt!='\0' && *pt!=' '; ++pt ); + ch = *pt; *pt = '\0'; + data->ligs[cnt][ccnt++] = SFGetChar(sf,-1,start); + *pt = ch; + if ( data->ligs[cnt][ccnt-1]==NULL ) + err = 1; + } + if ( !err ) + data->ligs[cnt++][ccnt] = NULL; + } + } + if ( cnt>=data->lmax ) + data->ligs = grealloc(data->ligs,(data->lmax+=1)*sizeof(SplineChar **)); + data->ligs[cnt] = NULL; + data->lcnt = cnt; +} + +static int skipglyphs(int lookup_flags, struct lookup_data *data, int pos) { + int mc, glyph_class; + /* The lookup flags tell us what glyphs to ignore. Skip over anything we */ + /* should ignore */ + + if ( lookup_flags==0 ) +return( pos ); + mc = (lookup_flags>>8); + if ( mc<0 || mc>=data->sf->mark_class_cnt ) + mc = 0; + while ( pos<data->cnt ) { + glyph_class = gdefclass(data->str[pos].sc); + /* 1=>base, 2=>ligature, 3=>mark, 4=>component?, 0=>.notdef */ + if ( (glyph_class==1 && (lookup_flags&pst_ignorebaseglyphs)) || + (glyph_class==2 && (lookup_flags&pst_ignoreligatures)) || + (glyph_class==3 && (lookup_flags&pst_ignorecombiningmarks)) || + (glyph_class==3 && mc!=0 && + !GlyphNameInClass(data->str[pos].sc->name,data->sf->mark_classes[mc])) ) { + ++pos; + } else + break; + } +return( pos ); +} + +static int bskipmarkglyphs(int lookup_flags, struct lookup_data *data, int pos) { + int mc, glyph_class; + /* The lookup flags tell us what glyphs to ignore. Skip over anything we */ + /* should ignore. Here we skip backward */ + + mc = (lookup_flags>>8); + if ( mc<0 || mc>=data->sf->mark_class_cnt ) + mc = 0; + while ( pos>=0 ) { + glyph_class = gdefclass(data->str[pos].sc); + /* 1=>base, 2=>ligature, 3=>mark, 4=>component?, 0=>.notdef */ + if ( glyph_class==3 ) + --pos; + else if ( (glyph_class==1 && (lookup_flags&pst_ignorebaseglyphs)) || + (glyph_class==2 && (lookup_flags&pst_ignoreligatures)) || + (glyph_class==3 && (lookup_flags&pst_ignorecombiningmarks)) || + (glyph_class==3 && mc!=0 && + !GlyphNameInClass(data->str[pos].sc->name,data->sf->mark_classes[mc])) ) { + --pos; + } else + break; + } +return( pos ); +} + +static int bskipglyphs(int lookup_flags, struct lookup_data *data, int pos) { + int mc, glyph_class; + /* The lookup flags tell us what glyphs to ignore. Skip over anything we */ + /* should ignore. Here we skip backward */ + + if ( lookup_flags==0 ) +return( pos ); + mc = (lookup_flags>>8); + if ( mc<0 || mc>=data->sf->mark_class_cnt ) + mc = 0; + while ( pos>=0 ) { + glyph_class = gdefclass(data->str[pos].sc); + /* 1=>base, 2=>ligature, 3=>mark, 4=>component?, 0=>.notdef */ + if ( (glyph_class==1 && (lookup_flags&pst_ignorebaseglyphs)) || + (glyph_class==2 && (lookup_flags&pst_ignoreligatures)) || + (glyph_class==3 && (lookup_flags&pst_ignorecombiningmarks)) || + (glyph_class==3 && mc!=0 && + !GlyphNameInClass(data->str[pos].sc->name,data->sf->mark_classes[mc])) ) { + --pos; + } else + break; + } +return( pos ); +} + +static int ContextualMatch(struct lookup_subtable *sub,struct lookup_data *data, + int pos, struct fpst_rule **_rule) { + int i, cpos, retpos, r; + FPST *fpst = sub->fpst; + int lookup_flags = sub->lookup->lookup_flags; + char *pt; + + /* If we should skip the current glyph then don't try for a match here */ + cpos = skipglyphs(lookup_flags,data,pos); + if ( cpos!=pos ) +return( 0 ); + + for ( r=0; r<fpst->rule_cnt; ++r ) { + struct fpst_rule *rule = &fpst->rules[r]; + for ( i=pos; i<data->cnt; ++i ) + data->str[i].context_pos = -1; + +/* Handle backtrack (backtrace in the rule is stored in reverse textual order) */ + if ( fpst->type == pst_chainpos || fpst->type == pst_chainsub ) { + if ( fpst->format==pst_glyphs ) { + pt = rule->u.glyph.back; + for ( i=bskipglyphs(lookup_flags,data,pos-1), cpos=0; i>=0; i = bskipglyphs(lookup_flags,data,i-1)) { + char *name = data->str[i].sc->name; + int len = strlen( name ); + if ( strncmp(name,pt,len)!=0 || (pt[len]!='\0' && pt[len]!=' ')) + break; + pt += len; + while ( *pt==' ' ) ++pt; + } + if ( *pt!='\0' ) + continue; /* didn't match */ + } else if ( fpst->format==pst_class ) { + for ( i=bskipglyphs(lookup_flags,data,pos-1), cpos=0; i>=0 && cpos<rule->u.class.bcnt; i = bskipglyphs(lookup_flags,data,i-1)) { + if ( !GlyphNameInClass(data->str[i].sc->name,fpst->bclass[rule->u.class.bclasses[cpos]]) ) + break; + ++cpos; + } + if ( cpos!=rule->u.class.bcnt ) + continue; /* didn't match */ + } else if ( fpst->format==pst_coverage ) { + for ( i=bskipglyphs(lookup_flags,data,pos-1), cpos=0; i>=0 && cpos<rule->u.coverage.bcnt; i = bskipglyphs(lookup_flags,data,i-1)) { + if ( !GlyphNameInClass(data->str[i].sc->name,rule->u.coverage.bcovers[cpos]) ) + break; + ++cpos; + } + if ( cpos<rule->u.coverage.bcnt ) + continue; /* didn't match */ + } + } +/* Handle Match */ + if ( fpst->format==pst_glyphs ) { + pt = rule->u.glyph.names; + for ( i=pos, cpos=0; i<data->cnt && *pt!='\0'; i = skipglyphs(lookup_flags,data,i+1)) { + char *name = data->str[i].sc->name; + int len = strlen( name ); + if ( strncmp(name,pt,len)!=0 || (pt[len]!='\0' && pt[len]!=' ')) + break; + data->str[i].context_pos = cpos++; + pt += len; + while ( *pt==' ' ) ++pt; + } + if ( *pt!='\0' ) + continue; /* didn't match */ + } else if ( fpst->format==pst_class ) { + for ( i=pos, cpos=0; i<data->cnt && cpos<rule->u.class.ncnt; i = skipglyphs(lookup_flags,data,i+1)) { + int class = rule->u.class.nclasses[cpos]; + if ( class!=0 ) { + if ( !GlyphNameInClass(data->str[i].sc->name,fpst->nclass[class]) ) + break; + } else { + int c; + /* Ok, to match class 0 we must fail to match all other classes */ + for ( c=1; c<fpst->nccnt; ++c ) + if ( !GlyphNameInClass(data->str[i].sc->name,fpst->nclass[c]) ) + break; + if ( c!=fpst->nccnt ) + break; /* It matched another class => not in class 0 */ + } + data->str[i].context_pos = cpos++; + } + if ( cpos<rule->u.class.ncnt ) + continue; /* didn't match */ + } else if ( fpst->format==pst_coverage ) { + for ( i=pos, cpos=0; i<data->cnt && cpos<rule->u.coverage.ncnt; i = skipglyphs(lookup_flags,data,i+1)) { + if ( !GlyphNameInClass(data->str[i].sc->name,rule->u.coverage.ncovers[cpos]) ) + break; + data->str[i].context_pos = cpos++; + } + if ( cpos<rule->u.coverage.ncnt ) + continue; /* didn't match */ + } else +return( 0 ); /* Not ready to deal with reverse chainging */ + + retpos = i; +/* Handle lookahead */ + if ( fpst->type == pst_chainpos || fpst->type == pst_chainsub ) { + if ( fpst->format==pst_glyphs ) { + pt = rule->u.glyph.fore; + for ( i=retpos; i<data->cnt && *pt!='\0'; i = skipglyphs(lookup_flags,data,i+1)) { + char *name = data->str[i].sc->name; + int len = strlen( name ); + if ( strncmp(name,pt,len)!=0 || (pt[len]!='\0' && pt[len]!=' ')) + break; + pt += len; + while ( *pt==' ' ) ++pt; + } + if ( *pt!='\0' ) + continue; /* didn't match */ + } else if ( fpst->format==pst_class ) { + for ( i=retpos, cpos=0; i<data->cnt && cpos<rule->u.class.fcnt; i = skipglyphs(lookup_flags,data,i+1)) { + if ( !GlyphNameInClass(data->str[i].sc->name,fpst->fclass[rule->u.class.fclasses[cpos]]) ) + break; + cpos++; + } + if ( cpos<rule->u.class.fcnt ) + continue; /* didn't match */ + } else if ( fpst->format==pst_coverage ) { + for ( i=retpos, cpos=0; i<data->cnt && cpos<rule->u.coverage.fcnt; i = skipglyphs(lookup_flags,data,i+1)) { + if ( !GlyphNameInClass(data->str[i].sc->name,rule->u.coverage.fcovers[cpos]) ) + break; + cpos++; + } + if ( cpos<rule->u.coverage.fcnt ) + continue; /* didn't match */ + } + } + *_rule = rule; +return( retpos ); + } +return( 0 ); +} + +static int ApplySingleSubsAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) { + PST *pst; + SplineChar *sc; + + for ( pst=data->str[pos].sc->possub; pst!=NULL && pst->subtable!=sub; pst=pst->next ); + if ( pst==NULL ) +return( 0 ); + + sc = SFGetChar(data->sf,-1,pst->u.subs.variant); + if ( sc!=NULL ) { + data->str[pos].sc = sc; +return( pos+1 ); + } else if ( strcmp(pst->u.subs.variant,MAC_DELETED_GLYPH_NAME)==0 ) { + /* Under AAT we delete the glyph. But OpenType doesn't have that concept */ + int i; + for ( i=pos+1; i<data->cnt; ++i ) + data->str[pos-1] = data->str[pos]; + --data->cnt; +return( pos ); + } else { +return( 0 ); + } +} + +static int ApplyMultSubsAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) { + PST *pst; + SplineChar *sc; + char *start, *pt; + int mcnt, ch, i; + SplineChar *mults[20]; + + for ( pst=data->str[pos].sc->possub; pst!=NULL && pst->subtable!=sub; pst=pst->next ); + if ( pst==NULL ) +return( 0 ); + + mcnt = 0; + for ( start = pst->u.alt.components; *start==' '; ++start); + for ( ; *start; ) { + for ( pt=start; *pt!='\0' && *pt!=' '; ++pt ); + ch = *pt; *pt = '\0'; + sc = SFGetChar(data->sf,-1,start); + *pt = ch; + if ( sc==NULL ) +return( 0 ); + if ( mcnt<20 ) mults[mcnt++] = sc; + while ( *pt==' ' ) ++pt; + start = pt; + } + + if ( mcnt==0 ) { + /* Is this legal? that is can we remove a glyph with an empty multiple? */ + for ( i=pos+1; i<data->cnt; ++i ) + data->str[i-1] = data->str[i]; + --data->cnt; +return( pos ); + } else if ( mcnt==1 ) { + data->str[pos].sc = mults[0]; +return( pos+1 ); + } else { + if ( data->cnt+mcnt-1 >= data->max ) + data->str = grealloc(data->str,(data->max+=mcnt) * sizeof( struct opentype_str )); + for ( i=data->cnt-1; i>pos; --i ) + data->str[i+mcnt-1] = data->str[i]; + memset(data->str+pos,0,mcnt*sizeof(struct opentype_str)); + for ( i=0; i<mcnt; ++i ) { + data->str[pos+i].sc = mults[i]; + data->str[pos+i].orig_index = data->str[pos].orig_index; + } + data->cnt += (mcnt-1); +return( pos+mcnt ); + } +} + +static int ApplyAltSubsAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) { + PST *pst; + SplineChar *sc; + char *start, *pt, ch; + + for ( pst=data->str[pos].sc->possub; pst!=NULL && pst->subtable!=sub; pst=pst->next ); + if ( pst==NULL ) +return( 0 ); + + for ( start = pst->u.alt.components; *start==' '; ++start); + for ( ; *start; ) { + for ( pt=start; *pt!='\0' && *pt!=' '; ++pt ); + ch = *pt; *pt = '\0'; + sc = SFGetChar(data->sf,-1,start); + *pt = ch; + if ( sc!=NULL ) { + data->str[pos].sc = sc; +return( pos+1 ); + } + while ( *pt==' ' ) ++pt; + start = pt; + } +return( 0 ); +} + +static int ApplyLigatureSubsAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) { + int i,k, lpos, npos; + int lookup_flags = sub->lookup->lookup_flags; + int match_found = -1, match_len=0; + + if ( data->lig_owner!=sub ) + LigatureSearch(sub,data); + for ( i=0; i<data->lcnt; ++i ) { + if ( data->ligs[i][1]==data->str[pos].sc ) { + lpos = 0; + npos = pos+1; + for ( k=2; data->ligs[i][k]!=NULL; ++k ) { + npos = skipglyphs(lookup_flags,data,npos); + if ( npos>=data->cnt || data->str[npos].sc != data->ligs[i][k] ) + break; + ++npos; + } + if ( data->ligs[i][k]==NULL ) { + if ( match_found==-1 || k>match_len ) { + match_found = i; + match_len = k; + } + } + } + } + if ( match_found!=-1 ) { + /* Matched. Remove the component glyphs, and note which component */ + /* any intervening marks should be attached to */ + data->str[pos].sc = data->ligs[match_found][0]; + npos = pos+1; + for ( k=2; data->ligs[match_found][k]!=NULL; ++k ) { + lpos = skipglyphs(lookup_flags,data,npos); + for ( ; npos<lpos; ++npos ) + data->str[npos].lig_pos = k-2; + /* Remove this glyph (copy the final NUL too) */ + for ( ++lpos; lpos<=data->cnt; ++lpos ) + data->str[lpos-1] = data->str[lpos]; + --data->cnt; + } + /* Any marks after the last component (which should be attached */ + /* to it) will not have been tagged, so do that now */ + lpos = skipglyphs(lookup_flags,data,npos); + for ( ; npos<lpos; ++npos ) + data->str[npos].lig_pos = k-2; +return( pos+1 ); + } + +return( 0 ); +} + +static int ApplyContextual(struct lookup_subtable *sub,struct lookup_data *data,int pos) { + /* On this level there is no difference between GPOS/GSUB contextuals */ + /* If the contextual matches, then we apply the lookups, otherwise we */ + /* don't. Now the lookups will be different, but we don't care here */ + struct fpst_rule *rule; + int retpos, i,j; + + retpos = ContextualMatch(sub,data,pos,&rule); + if ( retpos==0 ) +return( 0 ); + for ( i=0; i<rule->lookup_cnt; ++i ) { + for ( j=pos; j<data->cnt; ++j ) { + if ( data->str[j].context_pos == rule->lookups[i].seq ) { + ApplyLookupAtPos(0,rule->lookups[i].lookup,data,j); + break; + } + } + } +return( retpos ); +} + +#ifdef FONTFORGE_CONFIG_DEVICETABLES +static int FigureDeviceTable(DeviceTable *dt,int pixelsize) { + + if ( dt==NULL || dt->corrections==NULL || pixelsize<dt->first_pixel_size || + pixelsize>dt->last_pixel_size ) +return( 0 ); + +return( dt->corrections[pixelsize - dt->last_pixel_size] ); +} +#endif + +static int ApplySinglePosAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) { + PST *pst; + + for ( pst=data->str[pos].sc->possub; pst!=NULL && pst->subtable!=sub; pst=pst->next ); + if ( pst==NULL ) +return( 0 ); + + data->str[pos].vr.xoff += rint( pst->u.pos.xoff * data->scale ); + data->str[pos].vr.yoff += rint( pst->u.pos.yoff * data->scale ); + data->str[pos].vr.h_adv_off += rint( pst->u.pos.h_adv_off * data->scale ); + data->str[pos].vr.v_adv_off += rint( pst->u.pos.v_adv_off * data->scale ); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + if ( pst->u.pos.adjust!=NULL ) { + data->str[pos].vr.xoff += FigureDeviceTable(&pst->u.pos.adjust->xadjust,data->pixelsize); + data->str[pos].vr.yoff += FigureDeviceTable(&pst->u.pos.adjust->yadjust,data->pixelsize); + data->str[pos].vr.h_adv_off += FigureDeviceTable(&pst->u.pos.adjust->xadv,data->pixelsize); + data->str[pos].vr.v_adv_off += FigureDeviceTable(&pst->u.pos.adjust->yadv,data->pixelsize); + } +#endif +return( pos+1 ); +} + +static int ApplyPairPosAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) { + PST *pst; + int npos, isv, within; + KernPair *kp; + + npos = skipglyphs(sub->lookup->lookup_flags,data,pos+1); + if ( npos>=data->cnt ) +return( 0 ); + if ( sub->kc!=NULL ) { + within = KCFindIndex(sub->kc,data->str[pos].sc->name,data->str[npos].sc->name); + if ( within==-1 ) +return( 0 ); + data->str[pos].kc_index = within; + data->str[pos].kc = sub->kc; + if ( sub->vertical_kerning ) { + data->str[pos].vr.v_adv_off += rint( sub->kc->offsets[within] * data->scale ); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + data->str[pos].vr.v_adv_off += FigureDeviceTable(&sub->kc->adjusts[within],data->pixelsize); +#endif + } else if ( sub->lookup->lookup_flags & pst_r2l ) { + data->str[npos].vr.h_adv_off += rint( sub->kc->offsets[within] * data->scale ); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + data->str[npos].vr.h_adv_off += FigureDeviceTable(&sub->kc->adjusts[within],data->pixelsize); +#endif + } else { + data->str[pos].vr.h_adv_off += rint( sub->kc->offsets[within] * data->scale ); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + data->str[pos].vr.h_adv_off += FigureDeviceTable(&sub->kc->adjusts[within],data->pixelsize); +#endif + } +return( pos+1 ); + } else { + for ( pst=data->str[pos].sc->possub; pst!=NULL; pst=pst->next ) { + if ( pst->subtable==sub && strcmp(pst->u.pair.paired,data->str[npos].sc->name)==0 ) { + data->str[pos].vr.xoff += rint( pst->u.pair.vr[0].xoff * data->scale); + data->str[pos].vr.yoff += rint( pst->u.pair.vr[0].yoff * data->scale); + data->str[pos].vr.h_adv_off += rint( pst->u.pair.vr[0].h_adv_off * data->scale); + data->str[pos].vr.v_adv_off += rint( pst->u.pair.vr[0].v_adv_off * data->scale); + data->str[npos].vr.xoff += rint( pst->u.pair.vr[1].xoff * data->scale); + data->str[npos].vr.yoff += rint( pst->u.pair.vr[1].yoff * data->scale); + data->str[npos].vr.h_adv_off += rint( pst->u.pair.vr[1].h_adv_off * data->scale); + data->str[npos].vr.v_adv_off += rint( pst->u.pair.vr[1].v_adv_off * data->scale); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + /* I got bored. I should do all of them */ + if ( pst->u.pair.vr[0].adjust!=NULL ) { + data->str[pos].vr.h_adv_off += FigureDeviceTable(&pst->u.pair.vr[0].adjust->xadv,data->pixelsize); + } +#endif +return( pos+1 ); /* We do NOT want to return npos+1 */ + } + } + for ( isv = 0; isv<2; ++isv ) { + for ( kp = isv ? data->str[pos].sc->vkerns : data->str[pos].sc->kerns; kp!=NULL; kp=kp->next ) { + if ( kp->subtable == sub && kp->sc == data->str[npos].sc ) { + data->str[pos].kp = kp; + if ( isv ) { + data->str[pos].vr.v_adv_off += rint( kp->off * data->scale); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + data->str[pos].vr.v_adv_off += FigureDeviceTable(kp->adjust,data->pixelsize); +#endif + } else if ( sub->lookup->lookup_flags & pst_r2l ) { + data->str[npos].vr.h_adv_off += rint( kp->off * data->scale); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + data->str[npos].vr.h_adv_off += FigureDeviceTable(kp->adjust,data->pixelsize); +#endif + } else { + data->str[pos].vr.h_adv_off += rint( kp->off * data->scale); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + data->str[pos].vr.h_adv_off += FigureDeviceTable(kp->adjust,data->pixelsize); +#endif + } +return( pos+1 ); + } + } + } + } + +return( 0 ); +} + +static int ApplyAnchorPosAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) { + AnchorPoint *ap1, *ap2; + int bpos; + + /* Anchors do not position the base glyph, but the mark (or second glyph */ + /* of a cursive attachment). This means we don't apply the attachment when*/ + /* we meet the first glyph, but wait until we meet the second, and then */ + /* walk backwards */ + /* The backwards walk is different depending on the lookup type (I think) */ + /* mark to base and mark to ligature lookups will skip all marks even if */ + /* lookup flags don't specify that */ + /* mark to mark, and cursive attachment only skip what the lookup flags */ + /* tell them to skip. */ + for ( ap2=data->str[pos].sc->anchor; ap2!=NULL ; ap2=ap2->next ) { + if ( ap2->anchor->subtable==sub && (ap2->type == at_mark || ap2->type == at_centry)) + break; + } + if ( ap2==NULL ) { + /* This subtable is not used by this glyph ... at least this glyph is */ + /* neither a mark nor an entry point for this subtable */ +return( 0 ); + } + + /* There's only going to be one mark anchor on a glyph in a given subtable*/ + /* And cursive attachments only allow one anchor class per subtable */ + /* in either case we have already found the only attachment site possible */ + /* in the current glyph */ + + if ( sub->lookup->lookup_type == gpos_mark2base || + sub->lookup->lookup_type == gpos_mark2ligature ) + bpos = bskipmarkglyphs(sub->lookup->lookup_flags,data,pos-1); + else + bpos = bskipglyphs(sub->lookup->lookup_flags,data,pos-1); + if ( bpos==-1 ) +return( 0 ); /* No match */ + + if ( sub->lookup->lookup_type == gpos_cursive ) { + for ( ap1=data->str[bpos].sc->anchor; ap1!=NULL ; ap1=ap1->next ) { + if ( ap1->anchor==ap2->anchor && ap1->type==at_cexit ) + break; + } + } else if ( sub->lookup->lookup_type == gpos_mark2ligature ) { + for ( ap1=data->str[bpos].sc->anchor; ap1!=NULL ; ap1=ap1->next ) { + if ( ap1->anchor==ap2->anchor && ap1->type==at_baselig && + ap1->lig_index == data->str[pos].lig_pos ) + break; + } + } else { + for ( ap1=data->str[bpos].sc->anchor; ap1!=NULL ; ap1=ap1->next ) { + if ( ap1->anchor==ap2->anchor && + (ap1->type==at_basechar || ap1->type==at_basemark) ) + break; + } + } + if ( ap1==NULL ) +return( 0 ); /* No match */ + +/* This probably doesn't work for vertical text */ + data->str[pos].vr.yoff = data->str[bpos].vr.yoff + + rint((ap1->me.y - ap2->me.y) * data->scale); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + data->str[pos].vr.yoff += FigureDeviceTable(&ap1->yadjust,data->pixelsize)- + FigureDeviceTable(&ap2->yadjust,data->pixelsize); +#endif + if ( sub->lookup->lookup_flags&pst_r2l ) { + data->str[pos].vr.xoff = data->str[bpos].vr.xoff + + rint( -(ap1->me.x - ap2->me.x)*data->scale ); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + data->str[pos].vr.xoff -= FigureDeviceTable(&ap1->xadjust,data->pixelsize)- + FigureDeviceTable(&ap2->xadjust,data->pixelsize); +#endif + } else { + data->str[pos].vr.xoff = data->str[bpos].vr.xoff + + rint( (ap1->me.x - ap2->me.x - data->str[bpos].sc->width)*data->scale - + data->str[bpos].vr.h_adv_off); +#ifdef FONTFORGE_CONFIG_DEVICETABLES + data->str[pos].vr.xoff += FigureDeviceTable(&ap1->xadjust,data->pixelsize)- + FigureDeviceTable(&ap2->xadjust,data->pixelsize); +#endif + } + +return( pos+1 ); +} + +static int ConditionalTagOk(uint32 tag, OTLookup *otl,struct lookup_data *data,int pos) { + int npos, bpos; + uint32 script; + int before_in_script, after_in_script; + + if ( tag==CHR('i','n','i','t') || tag==CHR('i','s','o','l') || + tag==CHR('f','i','n','a') || tag==CHR('m','e','d','i') ) { + npos = skipglyphs(otl->lookup_flags,data,pos+1); + bpos = bskipglyphs(otl->lookup_flags,data,pos-1); + script = SCScriptFromUnicode(data->str[pos].sc); + before_in_script = (bpos>=0 && SCScriptFromUnicode(data->str[bpos].sc)==script); + after_in_script = (npos<data->cnt && SCScriptFromUnicode(data->str[npos].sc)==script); + if ( tag==CHR('i','n','i','t') ) +return( !before_in_script && after_in_script ); + else if ( tag==CHR('i','s','o','l') ) +return( !before_in_script && !after_in_script ); + else if ( tag==CHR('f','i','n','a') ) +return( before_in_script && !after_in_script ); + else +return( before_in_script && after_in_script ); + } + +return( true ); +} + +static int ApplyLookupAtPos(uint32 tag, OTLookup *otl,struct lookup_data *data,int pos) { + struct lookup_subtable *sub; + int newpos; + + /* Some tags imply a conditional check. Do that now */ + if ( !ConditionalTagOk(tag,otl,data,pos)) +return( 0 ); + + for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) { + switch ( otl->lookup_type ) { + case gsub_single: + newpos = ApplySingleSubsAtPos(sub,data,pos); + break; + case gsub_multiple: + newpos = ApplyMultSubsAtPos(sub,data,pos); + break; + case gsub_alternate: + newpos = ApplyAltSubsAtPos(sub,data,pos); + break; + case gsub_ligature: + newpos = ApplyLigatureSubsAtPos(sub,data,pos); + break; + case gsub_context: + newpos = ApplyContextual(sub,data,pos); + break; + case gsub_contextchain: + newpos = ApplyContextual(sub,data,pos); + break; + case gsub_reversecchain: + newpos = ApplySingleSubsAtPos(sub,data,pos); + break; + + case gpos_single: + newpos = ApplySinglePosAtPos(sub,data,pos); + break; + case gpos_pair: + newpos = ApplyPairPosAtPos(sub,data,pos); + break; + case gpos_cursive: + newpos = ApplyAnchorPosAtPos(sub,data,pos); + break; + case gpos_mark2base: + newpos = ApplyAnchorPosAtPos(sub,data,pos); + break; + case gpos_mark2ligature: + newpos = ApplyAnchorPosAtPos(sub,data,pos); + break; + case gpos_mark2mark: + newpos = ApplyAnchorPosAtPos(sub,data,pos); + break; + case gpos_context: + newpos = ApplyContextual(sub,data,pos); + break; + case gpos_contextchain: + newpos = ApplyContextual(sub,data,pos); + break; + default: + /* apple state machines */ + newpos = 0; + break; + } + /* if a subtable worked, we don't try to apply the next one */ + if ( newpos!=0 ) +return( newpos ); + } +return( 0 ); +} + +static void ApplyLookup(uint32 tag, OTLookup *otl,struct lookup_data *data) { + int pos, npos; + int lt = otl->lookup_type; + + if ( lt == morx_indic || lt == morx_context || lt == morx_insert || + lt == kern_statemachine ) + ApplyAppleStateMachine(tag,otl,data); + else { + /* OpenType */ + for ( pos = 0; pos<data->cnt; ) { + npos = ApplyLookupAtPos(tag,otl,data,pos); + if ( npos<=pos) /* !!!!! */ + npos = pos+1; + pos = npos; + } + } +} + +static uint32 FSLLMatches(FeatureScriptLangList *fl,uint32 *flist,uint32 script,uint32 lang) { + int i,l; + struct scriptlanglist *sl; + + if ( flist==NULL ) +return( 0 ); + + while ( fl!=NULL ) { + for ( i=0; flist[i]!=0; ++i ) { + if ( fl->featuretag==flist[i] ) + break; + } + if ( flist[i]!=0 ) { + for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) { + if ( sl->script == script ) { + if ( fl->ismac ) /* Language irrelevant on macs (scripts too, but we pretend they matter) */ +return( fl->featuretag ); + for ( l=0; l<sl->lang_cnt; ++l ) + if ( (l<MAX_LANG && sl->langs[l]==lang) || + (l>=MAX_LANG && sl->morelangs[l-MAX_LANG]==lang)) +return( fl->featuretag ); + } + } + } + fl = fl->next; + } +return( 0 ); +} + +/* This routine takes a string of glyphs and applies the opentype transformations */ +/* indicated by the features (and script and language) we are passed, it returns */ +/* a transformed string with substitutions applied and containing positioning */ +/* info */ +struct opentype_str *ApplyTickedFeatures(SplineFont *sf,uint32 *flist, uint32 script, uint32 lang, + int pixelsize, SplineChar **glyphs) { + int isgpos, cnt; + OTLookup *otl; + struct lookup_data data; + uint32 *langs, templang; + int i; + + memset(&data,0,sizeof(data)); + for ( cnt=0; glyphs[cnt]!=NULL; ++cnt ); + data.str = gcalloc(cnt+1,sizeof(struct opentype_str)); + data.cnt = data.max = cnt; + for ( cnt=0; glyphs[cnt]!=NULL; ++cnt ) { + data.str[cnt].sc = glyphs[cnt]; + data.str[cnt].orig_index = cnt; + data.str[cnt].lig_pos = data.str[cnt].context_pos = -1; + } + if ( sf->cidmaster!=NULL ) sf=sf->cidmaster; + data.sf = sf; + data.pixelsize = pixelsize; + data.scale = pixelsize/(double) (sf->ascent+sf->descent); + + /* Indic glyph reordering???? */ + for ( isgpos=0; isgpos<2; ++isgpos ) { + /* Check that this table has an entry for this language */ + /* if it doesn't use the default language */ + /* GPOS/GSUB may have different language sets, so we must be prepared */ + templang = lang; + langs = SFLangsInScript(sf,isgpos,script); + for ( i=0; langs[i]!=0 && langs[i]!=lang; ++i ); + if ( langs[i]==0 ) + templang = DEFAULT_LANG; + free(langs); + + for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL ; otl = otl->next ) { + uint32 tag; + if ( (tag=FSLLMatches(otl->features,flist,script,templang))!=0 ) + ApplyLookup(tag,otl,&data); + } + } + LigatureFree(&data); + free(data.ligs); + + data.str = grealloc(data.str,(data.cnt+1)*sizeof(struct opentype_str)); + memset(&data.str[data.cnt],0,sizeof(struct opentype_str)); +return( data.str ); +} + +static void doreplace(char **haystack,char *start,char *search,char *rpl,int slen) { + int rlen; + char *pt = start+slen; + (void)search; + rlen = strlen(rpl); + if ( slen>=rlen ) { + memcpy(start,rpl,rlen); + if ( slen>rlen ) { + int diff = slen-rlen; + for ( ; *pt ; ++pt ) + pt[-diff] = *pt; + pt[-diff] = '\0'; + } + } else { + char *base = *haystack; + char *new = galloc(pt-base+strlen(pt)+rlen-slen+1); + memcpy(new,base,start-base); + memcpy(new+(start-base),rpl,rlen); + strcpy(new+(start-base)+rlen,pt); + free( base ); + *haystack = new; + } +} + +static int rplstr(char **haystack,char *search, char *rpl,int multipleoccurances) { + char *start, *pt, *base = *haystack; + int ch, match, slen = strlen(search); + int any = 0; + + if ( base==NULL ) +return( false ); + + for ( pt=base ; ; ) { + while ( *pt==' ' ) ++pt; + if ( *pt=='\0' ) +return( any ); + start=pt; + while ( *pt!=' ' && *pt!='\0' ) ++pt; + if ( pt-start!=slen ) + match = -1; + else { + ch = *pt; *pt='\0'; + match = strcmp(start,search); + *pt = ch; + } + if ( match==0 ) { + doreplace(haystack,start,search,rpl,slen); + if ( !multipleoccurances ) +return( true ); + any = true; + if ( base!=*haystack ) { + pt = *haystack + (start-base)+strlen(rpl); + base = *haystack; + } else + pt = start+strlen(rpl); + } + } +} + +static int rplglyphname(char **haystack,char *search, char *rpl) { + /* If we change "f" to "uni0066" then we should also change "f.sc" to */ + /* "uni0066.sc" and "f_f_l" to "uni0066_uni0066_l" */ + char *start, *pt, *base = *haystack; + int ch, match; + unsigned slen = strlen(search); + int any = 0; + + if ( slen>=strlen( base )) +return( false ); + + for ( pt=base ; ; ) { + while ( *pt=='_' ) ++pt; + if ( *pt=='\0' || *pt=='.' ) +return( any ); + start=pt; + while ( *pt!='_' && *pt!='\0' && *pt!='.' ) ++pt; + if ( *pt=='\0' && start==base ) /* Don't change any unsegmented names */ +return( false ); /* In particular don't rename ourselves*/ + if ( pt-start!=slen ) + match = -1; + else { + ch = *pt; *pt='\0'; + match = strcmp(start,search); + *pt = ch; + } + if ( match==0 ) { + doreplace(haystack,start,search,rpl,slen); + any = true; + if ( base!=*haystack ) { + pt = *haystack + (start-base) + strlen(rpl); + base = *haystack; + } else + pt = start+strlen(rpl); + } + } +} + +static int glyphnameIsComponent(char *haystack,char *search) { + /* Check for a glyph name in ligature names and dotted names */ + char *start, *pt; + unsigned slen = strlen(search); + + if ( slen>=strlen( haystack )) +return( false ); + + for ( pt=haystack ; ; ) { + while ( *pt=='_' ) ++pt; + if ( *pt=='\0' || *pt=='.' ) +return( false ); + start=pt; + while ( *pt!='_' && *pt!='\0' && *pt!='.' ) ++pt; + if ( *pt=='\0' && start==haystack )/* Don't change any unsegmented names */ +return( false ); /* In particular don't rename ourselves*/ + if ( pt-start==slen && strncmp(start,search,slen)==0 ) +return( true ); + } +} + +static int gvfixup(struct glyphvariants *gv,char *old, char *new) { + int i; + int ret=0; + + if ( gv==NULL ) +return( false ); + ret = rplstr(&gv->variants,old,new,false); + for ( i=0; i<gv->part_cnt; ++i ) { + if ( strcmp(gv->parts[i].component,old)==0 ) { + free( gv->parts[i].component); + gv->parts[i].component = copy(new); + ret = true; + } + } +return( ret ); +} + +void SFGlyphRenameFixup(SplineFont *sf, char *old, char *new) { + int k, gid, isv; + int i,r; + SplineFont *master = sf; + SplineChar *sc; + PST *pst; + FPST *fpst; + KernClass *kc; + ASM *sm; + + CVGlyphRenameFixup(sf,old,new); + if ( sf->cidmaster!=NULL ) + master = sf->cidmaster; + + /* Look through all substitutions (and pairwise psts) stored on the glyphs*/ + /* and change any occurances of the name */ + /* (KernPairs have a reference to the SC rather than the name, and need no fixup) */ + /* Also if the name is "f" then look for glyph names like "f.sc" or "f_f_l"*/ + /* and be ready to change them too */ + k = 0; + do { + sf = k<master->subfontcnt ? master->subfonts[k] : master; + for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) { + if ( glyphnameIsComponent(sc->name,old)) { + char *newer = copy(sc->name); + rplglyphname(&newer,old,new); + SFGlyphRenameFixup(master,sc->name,newer); + free(sc->name); + sc->name = newer; + sc->namechanged = sc->changed = true; + } + for ( pst=sc->possub; pst!=NULL; pst=pst->next ) { + if ( pst->type==pst_substitution || pst->type==pst_alternate || + pst->type==pst_multiple || pst->type==pst_pair || + pst->type==pst_ligature ) { + if ( rplstr(&pst->u.mult.components,old,new,pst->type==pst_ligature)) + sc->changed = true; + } + } + /* For once I don't want a short circuit eval of "or", so I use */ + /* bitwise rather than boolean intentionally */ + if ( gvfixup(sc->vert_variants,old,new) | + gvfixup(sc->horiz_variants,old,new)) + sc->changed = true; + } + ++k; + } while ( k<master->subfontcnt ); + + /* Now look for contextual fpsts which might use the name */ + for ( fpst=master->possub; fpst!=NULL; fpst=fpst->next ) { + if ( fpst->format==pst_class ) { + for ( i=0; i<fpst->nccnt; ++i ) if ( fpst->nclass[i]!=NULL ) { + if ( rplstr(&fpst->nclass[i],old,new,false)) + break; + } + for ( i=0; i<fpst->bccnt; ++i ) if ( fpst->bclass[i]!=NULL ) { + if ( rplstr(&fpst->bclass[i],old,new,false)) + break; + } + for ( i=0; i<fpst->fccnt; ++i ) if ( fpst->fclass[i]!=NULL ) { + if ( rplstr(&fpst->fclass[i],old,new,false)) + break; + } + } + for ( r=0; r<fpst->rule_cnt; ++r ) { + struct fpst_rule *rule = &fpst->rules[r]; + if ( fpst->type==pst_glyphs ) { + rplstr(&rule->u.glyph.names,old,new,true); + rplstr(&rule->u.glyph.back,old,new,true); + rplstr(&rule->u.glyph.fore,old,new,true); + } else if ( fpst->type==pst_coverage || + fpst->type==pst_reversecoverage ) { + for ( i=0; i<rule->u.coverage.ncnt ; ++i ) + rplstr(&rule->u.coverage.ncovers[i],old,new,false); + for ( i=0; i<rule->u.coverage.bcnt ; ++i ) + rplstr(&rule->u.coverage.bcovers[i],old,new,false); + for ( i=0; i<rule->u.coverage.fcnt ; ++i ) + rplstr(&rule->u.coverage.fcovers[i],old,new,false); + if ( fpst->type==pst_reversecoverage ) + rplstr(&rule->u.rcoverage.replacements,old,new,true); + } + } + } + + /* Now look for contextual apple state machines which might use the name */ + for ( sm = master->sm; sm!=NULL; sm=sm->next ) { + for ( i=0; i<sm->class_cnt; ++i ) if ( sm->classes[i]!=NULL ) { + if ( rplstr(&sm->classes[i],old,new,false)) + break; + } + } + + /* Now look for contextual kerning classes which might use the name */ + for ( isv=0; isv<2; ++isv ) { + for ( kc=isv ? master->vkerns : master->kerns; kc!=NULL; kc=kc->next ) { + for ( i=0; i<kc->first_cnt; ++i ) if ( kc->firsts[i]!=NULL ) { + if ( rplstr(&kc->firsts[i],old,new,false)) + break; + } + for ( i=0; i<kc->second_cnt; ++i ) if ( kc->seconds[i]!=NULL ) { + if ( rplstr(&kc->seconds[i],old,new,false)) + break; + } + } + } +} + +struct lookup_subtable *SFSubTableFindOrMake(SplineFont *sf,uint32 tag,uint32 script, + int lookup_type ) { + OTLookup **base; + OTLookup *otl, *found=NULL; + int isgpos = lookup_type>=gpos_start; + struct lookup_subtable *sub; + int isnew = false; + + if ( sf->cidmaster ) sf = sf->cidmaster; + base = isgpos ? &sf->gpos_lookups : &sf->gsub_lookups; + for ( otl= *base; otl!=NULL; otl=otl->next ) { + if ( otl->lookup_type==(unsigned)lookup_type && + FeatureScriptTagInFeatureScriptList(tag,script,otl->features) ) { + for ( sub = otl->subtables; sub!=NULL; sub=sub->next ) + if ( sub->kc==NULL ) +return( sub ); + found = otl; + } + } + + if ( found==NULL ) { + found = chunkalloc(sizeof(OTLookup)); + found->lookup_type = lookup_type; + found->features = chunkalloc(sizeof(FeatureScriptLangList)); + found->features->featuretag = tag; + found->features->scripts = chunkalloc(sizeof(struct scriptlanglist)); + found->features->scripts->script = script; + found->features->scripts->langs[0] = DEFAULT_LANG; + found->features->scripts->lang_cnt = 1; + + SortInsertLookup(sf, found); + isnew = true; + } + + sub = chunkalloc(sizeof(struct lookup_subtable)); + sub->next = found->subtables; + found->subtables = sub; + sub->lookup = found; + sub->per_glyph_pst_or_kern = true; + + NameOTLookup(found,sf); +return( sub ); +} + +struct lookup_subtable *SFSubTableMake(SplineFont *sf,uint32 tag,uint32 script, + int lookup_type ) { + OTLookup **base; + OTLookup *otl, *found=NULL; + int isgpos = lookup_type>=gpos_start; + struct lookup_subtable *sub; + int isnew = false; + + if ( sf->cidmaster ) sf = sf->cidmaster; + base = isgpos ? &sf->gpos_lookups : &sf->gsub_lookups; + for ( otl= *base; otl!=NULL; otl=otl->next ) { + if ( otl->lookup_type==(unsigned)lookup_type && + FeatureScriptTagInFeatureScriptList(tag,script,otl->features) ) { + found = otl; + } + } + + if ( found==NULL ) { + found = chunkalloc(sizeof(OTLookup)); + found->lookup_type = lookup_type; + found->features = chunkalloc(sizeof(FeatureScriptLangList)); + found->features->featuretag = tag; + found->features->scripts = chunkalloc(sizeof(struct scriptlanglist)); + found->features->scripts->script = script; + found->features->scripts->langs[0] = DEFAULT_LANG; + found->features->scripts->lang_cnt = 1; + + SortInsertLookup(sf, found); + isnew = true; + } + + sub = chunkalloc(sizeof(struct lookup_subtable)); + sub->next = found->subtables; + found->subtables = sub; + sub->lookup = found; + + if ( isnew ) + NameOTLookup(found,sf); +return( sub ); +} + +int LookupUsedNested(SplineFont *sf,OTLookup *checkme) { + OTLookup *otl; + struct lookup_subtable *sub; + int r,c; + + if ( checkme->lookup_type>=gpos_start ) + otl = sf->gpos_lookups; + else + otl = sf->gsub_lookups; + while ( otl!=NULL ) { + for ( sub = otl->subtables; sub!=NULL; sub=sub->next ) { + if ( sub->fpst!=NULL ) { + for ( r=0; r<sub->fpst->rule_cnt; ++r ) { + struct fpst_rule *rule = &sub->fpst->rules[r]; + for ( c=0; c<rule->lookup_cnt; ++c ) { + if ( rule->lookups[c].lookup == checkme ) +return( true ); + } + } + } else if ( otl->lookup_type==morx_context ) { + for ( c = 0; c<sub->sm->class_cnt*sub->sm->state_cnt; ++c ) { + struct asm_state *state = &sub->sm->state[c]; + if ( state->u.context.mark_lookup==checkme ) +return( true ); + if ( state->u.context.cur_lookup==checkme ) +return( true ); + } + } + } + } +return( false ); +} + +static void AALTRemoveOld(SplineFont *sf) { + FeatureScriptLangList *fl, *prev; + OTLookup *otl, *otlnext; + + for ( otl=sf->gsub_lookups; otl!=NULL; otl=otlnext ) { + otlnext = otl->next; + prev = NULL; + for ( fl = otl->features; fl!=NULL; prev=fl, fl=fl->next ) { + if ( fl->featuretag==CHR('a','a','l','t') ) { + if ( fl==otl->features && fl->next==NULL && !LookupUsedNested(sf,otl)) + SFRemoveLookup(sf,otl); + else { + if ( prev==NULL ) + otl->features = fl->next; + else + prev->next = fl->next; + fl->next = NULL; + FeatureScriptLangListFree(fl); + } + break; + } + } + } +} + +void SllkFree(struct sllk *sllk,int sllk_cnt) { + int i; + + for ( i=0; i<sllk_cnt; ++i ) { + free( sllk[i].langs ); + free( sllk[i].lookups ); + } + free(sllk); +} + +static void AddOTLToSllk(struct sllk *sllk, OTLookup *otl, struct scriptlanglist *sl) { + int i,j,k,l; + + if ( otl->lookup_type==gsub_single || otl->lookup_type==gsub_alternate ) { + for ( i=0; i<sllk->cnt; ++i ) + if ( sllk->lookups[i]==otl ) + break; + if ( i==sllk->cnt ) { + if ( sllk->cnt>=sllk->max ) + sllk->lookups = grealloc(sllk->lookups,(sllk->max+=5)*sizeof(OTLookup *)); + sllk->lookups[sllk->cnt++] = otl; + for ( l=0; l<sl->lang_cnt; ++l ) { + uint32 lang = l<MAX_LANG ? sl->langs[l] : sl->morelangs[l-MAX_LANG]; + for ( j=0; j<sllk->lcnt; ++j ) + if ( sllk->langs[j]==lang ) + break; + if ( j==sllk->lcnt ) { + if ( sllk->lcnt>=sllk->lmax ) + sllk->langs = grealloc(sllk->langs,(sllk->lmax+=sl->lang_cnt+MAX_LANG)*sizeof(uint32)); + sllk->langs[sllk->lcnt++] = lang; + } + } + } + } else if ( otl->lookup_type==gsub_context || otl->lookup_type==gsub_contextchain ) { + struct lookup_subtable *sub; + for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) { + FPST *fpst = sub->fpst; + for ( j=0; j<fpst->rule_cnt; ++j ) { + struct fpst_rule *r = &fpst->rules[j]; + for ( k=0; k<r->lookup_cnt; ++k ) + AddOTLToSllk(sllk,r->lookups[k].lookup,sl); + } + } + } + /* reverse contextual chaining is weird and I shall ignore it. Adobe does too*/ +} + +static char *ComponentsFromPSTs(PST **psts,int pcnt) { + char **names=NULL; + int ncnt=0, nmax=0; + int i,j,len; + char *ret; + + /* First find all the names */ + for ( i=0; i<pcnt; ++i ) { + char *nlist = psts[i]->u.alt.components; + char *start, *pt, ch; + + for ( start = nlist; ; ) { + while ( *start==' ' ) + ++start; + if ( *start=='\0' ) + break; + for ( pt=start; *pt!=' ' && *pt!='\0'; ++pt ); + ch = *pt; *pt = '\0'; + for ( j=0; j<ncnt; ++j ) + if ( strcmp( start,names[j])==0 ) + break; + if ( j==ncnt ) { + if ( ncnt>=nmax ) + names = grealloc(names,(nmax+=10)*sizeof(char *)); + names[ncnt++] = copy(start); + } + *pt = ch; + start = pt; + } + } + + len = 0; + for ( i=0; i<ncnt; ++i ) + len += strlen(names[i])+1; + if ( len==0 ) len=1; + ret = galloc(len); + len = 0; + for ( i=0; i<ncnt; ++i ) { + strcpy(ret+len,names[i]); + len += strlen(names[i]); + ret[len++] = ' '; + } + if ( len==0 ) + *ret = '\0'; + else + ret[len-1] = '\0'; + + for ( i=0; i<ncnt; ++i ) + free(names[i]); + free(names); +return( ret ); +} + +static int SllkMatch(struct sllk *sllk,int s1,int s2) { + int i; + + if ( sllk[s1].cnt != sllk[s2].cnt ) +return( false ); + + for ( i=0; i<sllk[s1].cnt; ++i ) { + if ( sllk[s1].lookups[i] != sllk[s2].lookups[i] ) +return( false ); + } + +return( true ); +} + +struct sllk *AddOTLToSllks( OTLookup *otl, struct sllk *sllk, + int *_sllk_cnt, int *_sllk_max ) { + FeatureScriptLangList *fl; + struct scriptlanglist *sl; + int s; + + for ( fl=otl->features; fl!=NULL; fl=fl->next ) { + for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) { + for ( s=0; s<*_sllk_cnt; ++s ) + if ( sl->script == sllk[s].script ) + break; + if ( s==*_sllk_cnt ) { + if ( *_sllk_cnt>=*_sllk_max ) + sllk = grealloc(sllk,((*_sllk_max)+=10)*sizeof(struct sllk)); + memset(&sllk[*_sllk_cnt],0,sizeof(struct sllk)); + sllk[(*_sllk_cnt)++].script = sl->script; + } + AddOTLToSllk(&sllk[s], otl,sl); + } + } +return( sllk ); +} + +OTLookup *NewAALTLookup(SplineFont *sf,struct sllk *sllk, int sllk_cnt, int i) { + OTLookup *otl; + struct lookup_subtable *sub; + FeatureScriptLangList *fl; + struct scriptlanglist *sl; + PST **psts, *pst; + int j,k,l; + int gid,pcnt; + SplineFont *_sf; + SplineChar *sc; + + /* Make the new lookup (and all its supporting data structures) */ + otl = chunkalloc(sizeof(OTLookup)); + otl->lookup_type = gsub_alternate; + otl->lookup_flags = sllk[i].lookups[0]->lookup_flags & pst_r2l; + otl->features = fl = chunkalloc(sizeof(FeatureScriptLangList)); + fl->featuretag = CHR('a','a','l','t'); + /* Any other scripts with the same lookup set? */ + for ( j=i; j<sllk_cnt; ++j ) { + if ( i==j || SllkMatch(sllk,i,j)) { + sl = chunkalloc(sizeof(struct scriptlanglist)); + sl->next = fl->scripts; + fl->scripts = sl; + sl->script = sllk[j].script; + sl->lang_cnt = sllk[j].lcnt; + if ( sl->lang_cnt>MAX_LANG ) + sl->morelangs = galloc((sl->lang_cnt-MAX_LANG)*sizeof(uint32)); + for ( l=0; l<sl->lang_cnt; ++l ) + if ( l<MAX_LANG ) + sl->langs[l] = sllk[j].langs[l]; + else + sl->morelangs[l-MAX_LANG] = sllk[j].langs[l]; + if ( i!=j ) sllk[j].cnt = 0; /* Mark as processed */ + } + } + otl->subtables = sub = chunkalloc(sizeof(struct lookup_subtable)); + sub->lookup = otl; + sub->per_glyph_pst_or_kern = true; + + /* Add it to the various lists it needs to be in */ + otl->next = sf->gsub_lookups; + sf->gsub_lookups = otl; + + /* Now look at every glyph in the font, and see if it has any of the */ + /* lookups we are interested in, and if it does, build a new pst */ + /* containing all posibilities listed on any of them */ + if ( sf->cidmaster ) sf = sf->cidmaster; + psts = galloc(sllk[i].cnt*sizeof(PST *)); + k=0; + do { + _sf = k<sf->subfontcnt ? sf->subfonts[k] : sf; + for ( gid=0; gid<_sf->glyphcnt; ++gid ) if ( (sc = _sf->glyphs[gid])!=NULL ) { + pcnt = 0; + for ( pst=sc->possub; pst!=NULL; pst=pst->next ) { + if ( pst->subtable==NULL ) + continue; + for ( j=0; j<sllk[i].cnt; ++j ) + if ( pst->subtable->lookup == sllk[i].lookups[j] ) + break; + if ( j<sllk[i].cnt ) + psts[pcnt++] = pst; + } + if ( pcnt==0 ) + continue; + pst = chunkalloc(sizeof(PST)); + pst->subtable = sub; + pst->type = pst_alternate; + pst->next = sc->possub; + sc->possub = pst; + pst->u.alt.components = ComponentsFromPSTs(psts,pcnt); + } + ++k; + } while ( k<sf->subfontcnt ); + free(psts); + NameOTLookup(otl,sf); +return( otl ); +} + +void AddNewAALTFeatures(SplineFont *sf) { + /* different script/lang combinations may need different 'aalt' lookups */ + /* well, let's just say different script combinations */ + /* for each script/lang combo find all single/alternate subs for each */ + /* glyph. Merge those choices and create new lookup with that info */ + struct sllk *sllk = NULL; + int sllk_cnt=0, sllk_max = 0; + int i; + OTLookup *otl; + + AALTRemoveOld(sf); + + /* Find all scripts, and all the single/alternate lookups for each */ + /* and all the languages used for these in each script */ + for ( otl=sf->gsub_lookups; otl!=NULL; otl=otl->next ) { + sllk = AddOTLToSllks( otl, sllk, &sllk_cnt, &sllk_max ); + } + /* Each of these gets its own gsub_alternate lookup which gets inserted */ + /* at the head of the lookup list. Each lookup has one subtable */ + for ( i=0; i<sllk_cnt; ++i ) { + if ( sllk[i].cnt==0 ) /* Script used, but provides no alternates */ + continue; + NewAALTLookup(sf,sllk,sllk_cnt,i); + } + + SllkFree(sllk,sllk_cnt); +} + + +int VerticalKernFeature(SplineFont *sf, OTLookup *otl, int ask) { + FeatureScriptLangList *fl; + struct lookup_subtable *sub; + KernClass *kc; + char *buts[3]; + + for ( fl=otl->features; fl!=NULL; fl=fl->next ) { + if ( fl->featuretag==CHR('k','e','r','n') ) +return( false ); + else if ( fl->featuretag==CHR('v','k','r','n') ) +return( true ); + } + + for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) { + if ( sub->kc!=NULL ) { + for ( kc=sf->kerns; kc!=NULL; kc=kc->next ) + if ( kc==sub->kc ) +return( false ); + for ( kc=sf->vkerns; kc!=NULL; kc=kc->next ) + if ( kc==sub->kc ) +return( true ); + } + } + + if ( !ask ) +return( -1 ); + + buts[0] = _("_Horizontal"); buts[1] = _("_Vertical"); buts[2] = NULL; +return( ff_ask(_("Kerning direction"),(const char **) buts,0,1,_("Is this horizontal or vertical kerning data?")) ); +} + +int IsAnchorClassUsed(SplineChar *sc,AnchorClass *an) { + AnchorPoint *ap; + int waslig=0, sawentry=0, sawexit=0; + + for ( ap=sc->anchor; ap!=NULL; ap=ap->next ) { + if ( ap->anchor==an ) { + if ( ap->type==at_centry ) + sawentry = true; + else if ( ap->type==at_cexit ) + sawexit = true; + else if ( an->type==act_mkmk ) { + if ( ap->type==at_basemark ) + sawexit = true; + else + sawentry = true; + } else if ( ap->type!=at_baselig ) +return( -1 ); + else if ( waslig<ap->lig_index+1 ) + waslig = ap->lig_index+1; + } + } + if ( sawentry && sawexit ) +return( -1 ); + else if ( sawentry ) +return( -2 ); + else if ( sawexit ) +return( -3 ); +return( waslig ); +} + +int PSTContains(const char *components,const char *name) { + const char *pt; + int len = strlen(name); + + for ( pt = strstr(components,name); pt!=NULL; pt = strstr(pt+len,name)) { + if (( pt==components || pt[-1]==' ') && (pt[len]==' ' || pt[len]=='\0')) +return( true ); + } +return( false ); +} + +int KernClassContains(KernClass *kc, char *name1, char *name2, int ordered ) { + int infirst=0, insecond=0, scpos1=0, kwpos1=0, scpos2=0, kwpos2=0; + int i; + + for ( i=1; i<kc->first_cnt; ++i ) { + if ( PSTContains(kc->firsts[i],name1) ) { + scpos1 = i; + if ( ++infirst>=3 ) /* The name occurs twice??? */ + break; + } else if ( PSTContains(kc->firsts[i],name2) ) { + kwpos1 = i; + if ( (infirst+=2)>=3 ) + break; + } + } + if ( infirst==0 || infirst>3 ) +return( 0 ); + for ( i=1; i<kc->second_cnt; ++i ) { + if ( PSTContains(kc->seconds[i],name1) ) { + scpos2 = i; + if ( ++insecond>=3 ) + break; + } else if ( PSTContains(kc->seconds[i],name2) ) { + kwpos2 = i; + if ( (insecond+=2)>=3 ) + break; + } + } + if ( insecond==0 || insecond>3 ) +return( 0 ); + if ( (infirst&1) && (insecond&2) ) { + if ( kc->offsets[scpos1*kc->second_cnt+kwpos2]!=0 ) +return( kc->offsets[scpos1*kc->second_cnt+kwpos2] ); + } + if ( !ordered ) { + if ( (infirst&2) && (insecond&1) ) { + if ( kc->offsets[kwpos1*kc->second_cnt+scpos2]!=0 ) +return( kc->offsets[kwpos1*kc->second_cnt+scpos2] ); + } + } +return( 0 ); +} + +int KCFindName(char *name, char **classnames, int cnt ) { + int i; + char *pt, *end, ch; + + for ( i=0; i<cnt; ++i ) { + if ( classnames[i]==NULL ) + continue; + for ( pt = classnames[i]; *pt; pt=end+1 ) { + end = strchr(pt,' '); + if ( end==NULL ) end = pt+strlen(pt); + ch = *end; + *end = '\0'; + if ( strcmp(pt,name)==0 ) { + *end = ch; +return( i ); + } + *end = ch; + if ( ch=='\0' ) + break; + } + } +return( 0 ); +} + +int KCFindIndex(KernClass *kc,char *name1, char *name2) { + int f,l; + + f = KCFindName(name1,kc->firsts,kc->first_cnt); + l = KCFindName(name2,kc->seconds,kc->second_cnt); + if ( (f!=0 || kc->firsts[0]!=NULL) && l!=0 ) +return( f*kc->second_cnt+l ); + +return( -1 ); +} + + +static void NOFI_SortInsertLookup(SplineFont *sf, OTLookup *newotl) { + (void)sf; + (void)newotl; +} + +static void NOFI_OTLookupCopyInto(SplineFont *into_sf,SplineFont *from_sf, + OTLookup *from_otl, OTLookup *to_otl, int scnt, OTLookup *before ) { + (void)into_sf; + (void)from_sf; + (void)from_otl; + (void)to_otl; + (void)scnt; + (void)before; +} + +static void NOFI_Destroy(SplineFont *sf) { + (void)sf; +} + +struct fi_interface noui_fi = { + NOFI_SortInsertLookup, + NOFI_OTLookupCopyInto, + NOFI_Destroy +}; + +struct fi_interface *fi_interface = &noui_fi; + +void FF_SetFIInterface(struct fi_interface *fii) { + fi_interface = fii; +} |