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 | 3987 |
1 files changed, 0 insertions, 3987 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 deleted file mode 100644 index 16f540f1b0b..00000000000 --- a/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/lookups.c +++ /dev/null @@ -1,3987 +0,0 @@ -/* 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 }, - { 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 }, - { CHR('f','i','n','3'), "fin3", N_("Terminal Forms #3"), gsub_context_mask|gsub_contextchain_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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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|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 ) { - 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 & 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 ( 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; - 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_: ; */ - /* } */ - /* Issue 866 : currently #define SCWorthOutputting(a) 1 so we drop it */ - for ( i=0; i<sf->glyphcnt; ++i ) { - sc = sf->glyphs[i]; - if ( sc ) { - for ( pst=sc->possub; pst!=NULL; pst=pst->next ) { - if ( pst->subtable == subtable ) { - used[i] = true; - goto continue_; - } - } - continue_: ; - }/* end if ( sc ) */ - }/*end for */ - - 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->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 ) { - /* 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) */ - 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->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[] = { - { N_("Arabic"), CHR('a','r','a','b') }, - { N_("Aramaic"), CHR('a','r','a','m') }, /* Only in ff */ - { N_("Armenian"), CHR('a','r','m','n') }, - { N_("Avestan"), CHR('a','v','e','s') }, /* Only in ff */ - { N_("Balinese"), CHR('b','a','l','i') }, - { N_("Batak"), CHR('b','a','t','k') }, - { N_("Bengali"), CHR('b','e','n','g') }, - { N_("Bengali2"), CHR('b','n','g','2') }, - { N_("Bliss Symbolics"), CHR('b','l','i','s') }, /* Only in ff */ - { N_("Bopomofo"), CHR('b','o','p','o') }, - { N_("Brāhmī"), CHR('b','r','a','h') }, - { N_("Braille"), CHR('b','r','a','i') }, - { N_("Buginese"), CHR('b','u','g','i') }, - { N_("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_("Cham"), CHR('c','h','a','m') }, - { N_("Cherokee"), CHR('c','h','e','r') }, - { N_("Cirth"), CHR('c','i','r','t') }, /* Only in ff */ - { N_("CJK Ideographic"), CHR('h','a','n','i') }, - { N_("Coptic"), CHR('c','o','p','t') }, - { N_("Cypro-Minoan"), CHR('c','p','r','t') }, - { N_("Cypriot syllabary"), CHR('c','p','m','n') }, /* Only in ff */ - { N_("Cyrillic"), CHR('c','y','r','l') }, - { N_("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_("Ethiopic"), CHR('e','t','h','i') }, - { N_("Georgian"), CHR('g','e','o','r') }, - { N_("Glagolitic"), CHR('g','l','a','g') }, - { N_("Gothic"), CHR('g','o','t','h') }, - { N_("Greek"), CHR('g','r','e','k') }, - { N_("Gujarati"), CHR('g','u','j','r') }, - { N_("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') }, - { N_("Hanunóo"), CHR('h','a','n','o') }, - { N_("Hebrew"), CHR('h','e','b','r') }, -/* { N_("Pahawh Hmong"), CHR('h','m','n','g') },*/ -/* { N_("Indus (Harappan)"), CHR('i','n','d','s') },*/ - { N_("Javanese"), CHR('j','a','v','a') }, - { N_("Kayah Li"), CHR('k','a','l','i') }, - { N_("Hiragana & Katakana"), CHR('k','a','n','a') }, - { N_("Kharoṣṭhī"), CHR('k','h','a','r') }, - { N_("Kannada"), CHR('k','n','d','a') }, - { N_("Kannada2"), CHR('k','n','d','2') }, - { N_("Khmer"), CHR('k','h','m','r') }, - { N_("Kharosthi"), CHR('k','h','a','r') }, - { N_("Lao") , CHR('l','a','o',' ') }, - { N_("Latin"), CHR('l','a','t','n') }, - { N_("Lepcha (Róng)"), CHR('l','e','p','c') }, - { N_("Limbu"), CHR('l','i','m','b') }, /* Not in ISO 15924 !!!!!, just guessing */ - { N_("Linear A"), CHR('l','i','n','a') }, /* Only in ff */ - { N_("Linear B"), CHR('l','i','n','b') }, - { N_("Lycian"), CHR('l','y','c','i') }, - { N_("Lydian"), CHR('l','y','d','i') }, - { N_("Mandaean"), CHR('m','a','n','d') }, -/* { N_("Mayan hieroglyphs"), CHR('m','a','y','a') },*/ - { N_("Malayālam"), CHR('m','l','y','m') }, - { N_("Malayālam2"), CHR('m','l','y','2') }, /* Only in ff, should be mlm2 */ - { N_("Mathematical Alphanumeric Symbols"), CHR('m','a','t','h') }, - { N_("Mongolian"), CHR('m','o','n','g') }, - { N_("Musical"), CHR('m','u','s','i') }, /* Only in ff, should be musc */ - { N_("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_("Old Permic"), CHR('p','e','r','m') }, /* Only in ff */ - { N_("Old Persian cuneiform"), CHR('x','p','e','o') }, - { N_("Oriya"), CHR('o','r','y','a') }, - { N_("Oriya2"), CHR('o','r','y','2') }, - { N_("Osmanya"), CHR('o','s','m','a') }, - { N_("Pahlavi"), CHR('p','a','l','v') }, /* Only in ff */ - { N_("Phags-pa"), CHR('p','h','a','g') }, - { N_("Phoenician"), CHR('p','h','n','x') }, - { N_("Phaistos"), CHR('p','h','s','t') }, /* Only in ff */ - { N_("Pollard Phonetic"), CHR('p','l','r','d') }, /* Only in ff */ - { N_("Rejang"), CHR('r','j','n','g') }, - { N_("Rongorongo"), CHR('r','o','r','o') }, /* Only in ff */ - { N_("Runic"), CHR('r','u','n','r') }, - { N_("Saurashtra"), CHR('s','a','u','r') }, - { N_("Shavian"), CHR('s','h','a','w') }, - { N_("Sinhala"), CHR('s','i','n','h') }, - { N_("Sumero-Akkadian Cuneiform"), CHR('x','s','u','x') }, - { N_("Sundanese"), CHR('s','u','n','d') }, - { N_("Syloti Nagri"), CHR('s','y','l','o') }, - { N_("Syriac"), CHR('s','y','r','c') }, - { N_("Tagalog"), CHR('t','g','l','g') }, - { N_("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_("Tamil"), CHR('t','a','m','l') }, - { N_("Tamil2"), CHR('t','m','l','2') }, - { N_("Telugu"), CHR('t','e','l','u') }, - { N_("Telugu2"), CHR('t','e','l','2') }, - { N_("Tengwar"), CHR('t','e','n','g') }, /* Only in ff */ - { N_("Thaana"), CHR('t','h','a','a') }, - { N_("Thai"), CHR('t','h','a','i') }, - { N_("Tibetan"), CHR('t','i','b','t') }, - { N_("Tifinagh (Berber)"), CHR('t','f','n','g') }, - { N_("Ugaritic"), CHR('u','g','r','t') }, /* Only in ff, should be ugar */ - { N_("Vai"), CHR('v','a','i',' ') }, -/* { N_("Visible Speech"), CHR('v','i','s','p') },*/ - { N_("Cuneiform, Ugaritic"), CHR('x','u','g','a') }, /* Only in ff */ - { N_("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] = _((char *) lookup_type_names[j][i]); - } - for ( i=0; localscripts[i].text!=NULL; ++i ) - localscripts[i].text = _(localscripts[i].text); - for ( i=0; friendlies[i].friendlyname!=NULL; ++i ) - friendlies[i].friendlyname = _(friendlies[i].friendlyname); -} - -char *TagFullName(SplineFont *sf,uint32 tag, int onlyifknown) { - char ubuf[200], *end = ubuf+sizeof(ubuf); - int k; - - 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, true); - if ( userfriendly!=NULL ) - break; - } - } - if ( userfriendly!=NULL ) - break; -#endif - } - if ( userfriendly==NULL ) { - 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 = _("Unknown"); -#endif - for ( fl=otl->features; fl!=NULL; fl=fl->next ); - if ( fl==NULL ) - userfriendly = copy(lookuptype); - else { - userfriendly = galloc( strlen(lookuptype) + 40); -#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( _((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 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->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 ); - } - } - } - } - } -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 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: - 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; - /* 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 ) { - 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 ); -} - -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 ); - } - } - } - } - } -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; -} |