1 /* Copyright (C) 2007,2008 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 #include "fontforgevw.h"
28 #include <chardata.h>
29 #include <utype.h>
30 #include <ustring.h>
31 #include <math.h>
32 #include <locale.h>
33 #include <stdlib.h>
34 #include "ttf.h"
35 #include "lookups.h"
36 
37 struct opentype_feature_friendlynames friendlies[] = {
38 #if 0		/* They get stuffed into the 'MATH' table now */
39 /* I added these first three features to allow round-trip conversion of tfm files */
40     { CHR('I','T','L','C'),	"ITLC", N_("Italic Correction"),	gpos_single_mask },
41     { CHR('T','C','H','L'),	"TCHL", N_("TeX Glyphlist"),		gsub_alternate_mask },
42     { CHR('T','E','X','L'),	"TEXL", N_("TeX Extension List"),	gsub_multiple_mask },
43 #endif
44 /* Normal OpenType features follow */
45     { CHR('a','a','l','t'),	"aalt", N_("Access All Alternates"),	gsub_single_mask|gsub_alternate_mask },
46     { CHR('a','b','v','f'),	"abvf", N_("Above Base Forms"),		gsub_single_mask },
47     { CHR('a','b','v','m'),	"abvm", N_("Above Base Mark"),		gpos_mark2base_mask|gpos_mark2ligature_mask },
48     { CHR('a','b','v','s'),	"abvs", N_("Above Base Substitutions"),	gsub_ligature_mask },
49     { CHR('a','f','r','c'),	"afrc", N_("Vertical Fractions"),	gsub_ligature_mask },
50     { CHR('a','k','h','n'),	"akhn", N_("Akhand"),			gsub_ligature_mask },
51     { CHR('a','l','i','g'),	"alig", N_("Ancient Ligatures"),	gsub_ligature_mask },
52     { CHR('b','l','w','f'),	"blwf", N_("Below Base Forms"),		gsub_ligature_mask },
53     { CHR('b','l','w','m'),	"blwm", N_("Below Base Mark"),		gpos_mark2base_mask|gpos_mark2ligature_mask },
54     { CHR('b','l','w','s'),	"blws", N_("Below Base Substitutions"),	gsub_ligature_mask },
55     { CHR('c','2','p','c'),	"c2pc", N_("Capitals to Petite Capitals"),	gsub_single_mask },
56     { CHR('c','2','s','c'),	"c2sc", N_("Capitals to Small Capitals"),	gsub_single_mask },
57     { CHR('c','a','l','t'),	"calt", N_("Contextual Alternates"),	gsub_context_mask|gsub_contextchain_mask },
58     { CHR('c','a','s','e'),	"case", N_("Case-Sensitive Forms"),	gsub_single_mask|gpos_single_mask },
59     { CHR('c','c','m','p'),	"ccmp", N_("Glyph Composition/Decomposition"),	gsub_multiple_mask|gsub_ligature_mask },
60     { CHR('c','l','i','g'),	"clig", N_("Contextual Ligatures"),	gsub_reversecchain_mask },
61     { CHR('c','p','c','t'),	"cpct", N_("Centered CJK Punctuation"),	gpos_single_mask },
62     { CHR('c','p','s','p'),	"cpsp", N_("Capital Spacing"),		gpos_single_mask },
63     { CHR('c','s','w','h'),	"cswh", N_("Contextual Swash"),		gsub_reversecchain_mask },
64     { CHR('c','u','r','s'),	"curs", N_("Cursive Attachment"),	gpos_cursive_mask },
65     { CHR('c','v','0','0'),	"cv00", N_("Character Variants 00"),	gsub_single_mask },
66     { CHR('c','v','0','1'),	"cv01", N_("Character Variants 01"),	gsub_single_mask },
67     { CHR('c','v','0','2'),	"cv02", N_("Character Variants 02"),	gsub_single_mask },
68     { CHR('c','v','0','3'),	"cv03", N_("Character Variants 03"),	gsub_single_mask },
69     { CHR('c','v','0','4'),	"cv04", N_("Character Variants 04"),	gsub_single_mask },
70     { CHR('c','v','0','5'),	"cv05", N_("Character Variants 05"),	gsub_single_mask },
71     { CHR('c','v','0','6'),	"cv06", N_("Character Variants 06"),	gsub_single_mask },
72     { CHR('c','v','0','7'),	"cv07", N_("Character Variants 07"),	gsub_single_mask },
73     { CHR('c','v','0','8'),	"cv08", N_("Character Variants 08"),	gsub_single_mask },
74     { CHR('c','v','0','9'),	"cv09", N_("Character Variants 09"),	gsub_single_mask },
75     { CHR('c','v','1','0'),	"cv10", N_("Character Variants 10"),	gsub_single_mask },
76     { CHR('c','v','9','9'),	"cv99", N_("Character Variants 99"),	gsub_single_mask },
77     { CHR('d','c','a','p'),	"dcap", N_("Drop Caps"),		gsub_single_mask },
78     { CHR('d','i','s','t'),	"dist", N_("Distance"),			gpos_pair_mask },
79     { CHR('d','l','i','g'),	"dlig", N_("Discretionary Ligatures"),	gsub_ligature_mask },
80     { CHR('d','n','o','m'),	"dnom", N_("Denominators"),		gsub_single_mask },
81     { CHR('d','p','n','g'),	"dpng", N_("Dipthongs (Obsolete)"),	gsub_ligature_mask },
82     { CHR('e','x','p','t'),	"expt", N_("Expert Forms"),		gsub_single_mask },
83     { CHR('f','a','l','t'),	"falt", N_("Final Glyph On Line"),	gsub_alternate_mask },
84     { CHR('f','i','n','2'),	"fin2", N_("Terminal Forms #2"),	gsub_context_mask|gsub_contextchain_mask },
85     { CHR('f','i','n','3'),	"fin3", N_("Terminal Forms #3"),	gsub_context_mask|gsub_contextchain_mask },
86     { CHR('f','i','n','a'),	"fina", N_("Terminal Forms"),		gsub_single_mask },
87     { CHR('f','l','a','c'),	"flac", N_("Flattened Accents over Capitals"),	gsub_single_mask|gsub_ligature_mask },
88     { CHR('f','r','a','c'),	"frac", N_("Diagonal Fractions"),	gsub_single_mask|gsub_ligature_mask },
89     { CHR('f','w','i','d'),	"fwid", N_("Full Widths"),		gsub_single_mask|gpos_single_mask },
90     { CHR('h','a','l','f'),	"half", N_("Half Forms"),		gsub_ligature_mask },
91     { CHR('h','a','l','n'),	"haln", N_("Halant Forms"),		gsub_ligature_mask },
92     { CHR('h','a','l','t'),	"halt", N_("Alternative Half Widths"),	gpos_single_mask },
93     { CHR('h','i','s','t'),	"hist", N_("Historical Forms"),		gsub_single_mask },
94     { CHR('h','k','n','a'),	"hkna", N_("Horizontal Kana Alternatives"),	gsub_single_mask },
95     { CHR('h','l','i','g'),	"hlig", N_("Historic Ligatures"),	gsub_ligature_mask },
96     { CHR('h','n','g','l'),	"hngl", N_("Hanja to Hangul"),		gsub_single_mask|gsub_alternate_mask },
97     { CHR('h','o','j','o'),	"hojo", N_("Hojo (JIS X 0212-1990) Kanji Forms"),	gsub_single_mask },
98     { CHR('h','w','i','d'),	"hwid", N_("Half Widths"),		gsub_single_mask|gpos_single_mask },
99     { CHR('i','n','i','t'),	"init", N_("Initial Forms"),		gsub_single_mask },
100     { CHR('i','s','o','l'),	"isol", N_("Isolated Forms"),		gsub_single_mask },
101     { CHR('i','t','a','l'),	"ital", N_("Italics"),			gsub_single_mask },
102     { CHR('j','a','l','t'),	"jalt", N_("Justification Alternatives"),	gsub_alternate_mask },
103     { CHR('j','a','j','p'),	"jajp", N_("Japanese Forms (Obsolete"),	gsub_single_mask|gsub_alternate_mask },
104     { CHR('j','p','7','8'),	"jp78", N_("JIS78 Forms"),		gsub_single_mask|gsub_alternate_mask },
105     { CHR('j','p','8','3'),	"jp83", N_("JIS83 Forms"),		gsub_single_mask },
106     { CHR('j','p','9','0'),	"jp90", N_("JIS90 Forms"),		gsub_single_mask },
107     { CHR('k','e','r','n'),	"kern", N_("Horizontal Kerning"),	gpos_pair_mask|gpos_context_mask|gpos_contextchain_mask },
108     { CHR('l','f','b','d'),	"lfbd", N_("Left Bounds"),		gpos_single_mask },
109     { CHR('l','i','g','a'),	"liga", N_("Standard Ligatures"),	gsub_ligature_mask },
110     { CHR('l','j','m','o'),	"ljmo", N_("Leading Jamo Forms"),	gsub_ligature_mask },
111     { CHR('l','n','u','m'),	"lnum", N_("Lining Figures"),		gsub_single_mask },
112     { CHR('l','o','c','l'),	"locl", N_("Localized Forms"),		gsub_single_mask },
113     { CHR('m','a','r','k'),	"mark", N_("Mark Positioning"),		gpos_mark2base_mask|gpos_mark2ligature_mask },
114     { CHR('m','e','d','2'),	"med2", N_("Medial Forms 2"),		gsub_context_mask|gsub_contextchain_mask },
115     { CHR('m','e','d','i'),	"medi", N_("Medial Forms"),		gsub_single_mask },
116     { CHR('m','g','r','k'),	"mgrk", N_("Mathematical Greek"),	gsub_single_mask },
117     { CHR('m','k','m','k'),	"mkmk", N_("Mark to Mark"),		gpos_mark2mark_mask },
118     { CHR('m','s','e','t'),	"mset", N_("Mark Positioning via Substitution"),	gsub_context_mask|gsub_contextchain_mask },
119     { CHR('n','a','l','t'),	"nalt", N_("Alternate Annotation Forms"),	gsub_single_mask|gsub_alternate_mask },
120     { CHR('n','u','k','t'),	"nukt", N_("Nukta Forms"),		gsub_ligature_mask },
121     { CHR('n','u','m','r'),	"numr", N_("Numerators"),		gsub_single_mask },
122     { CHR('o','n','u','m'),	"onum", N_("Oldstyle Figures"),		gsub_single_mask },
123     { CHR('o','p','b','d'),	"opbd", N_("Optical Bounds"),		gpos_single_mask },
124     { CHR('o','r','d','n'),	"ordn", N_("Ordinals"),			gsub_ligature_mask|gsub_context_mask|gsub_contextchain_mask },
125     { CHR('o','r','n','m'),	"ornm", N_("Ornaments"),		gsub_single_mask|gsub_alternate_mask },
126     { CHR('p','a','l','t'),	"palt", N_("Proportional Alternate Metrics"),	gpos_single_mask },
127     { CHR('p','c','a','p'),	"pcap", N_("Lowercase to Petite Capitals"),	gsub_single_mask },
128     { CHR('p','k','n','a'),	"pkna", N_("Proportional Kana"),	gpos_single_mask },
129     { CHR('p','n','u','m'),	"pnum", N_("Proportional Numbers"),	gsub_single_mask },
130     { CHR('p','r','e','f'),	"pref", N_("Pre Base Forms"),		gsub_ligature_mask },
131     { CHR('p','r','e','s'),	"pres", N_("Pre Base Substitutions"),	gsub_ligature_mask|gsub_context_mask|gsub_contextchain_mask },
132     { CHR('p','s','t','f'),	"pstf", N_("Post Base Forms"),		gsub_ligature_mask },
133     { CHR('p','s','t','s'),	"psts", N_("Post Base Substitutions"),	gsub_ligature_mask },
134     { CHR('p','w','i','d'),	"pwid", N_("Proportional Width"),	gsub_single_mask },
135     { CHR('q','w','i','d'),	"qwid", N_("Quarter Widths"),		gsub_single_mask|gpos_single_mask },
136     { CHR('r','a','n','d'),	"rand", N_("Randomize"),		gsub_alternate_mask },
137     { CHR('r','k','r','f'),	"rkrf", N_("Rakar Forms"),		gsub_ligature_mask },
138     { CHR('r','l','i','g'),	"rlig", N_("Required Ligatures"),	gsub_ligature_mask },
139     { CHR('r','p','h','f'),	"rphf", N_("Reph Form"),		gsub_ligature_mask },
140     { CHR('r','t','b','d'),	"rtbd", N_("Right Bounds"),		gpos_single_mask },
141     { CHR('r','t','l','a'),	"rtla", N_("Right to Left Alternates"),	gsub_single_mask },
142     { CHR('r','u','b','y'),	"ruby", N_("Ruby Notational Forms"),	gsub_single_mask },
143     { CHR('s','a','l','t'),	"salt", N_("Stylistic Alternatives"),	gsub_single_mask|gsub_alternate_mask },
144     { CHR('s','i','n','f'),	"sinf", N_("Scientific Inferiors"),	gsub_single_mask },
145     { CHR('s','m','c','p'),	"smcp", N_("Lowercase to Small Capitals"),	gsub_single_mask },
146     { CHR('s','m','p','l'),	"smpl", N_("Simplified Forms"),		gsub_single_mask },
147     { CHR('s','s','0','1'),	"ss01", N_("Style Set 1"),		gsub_single_mask },
148     { CHR('s','s','0','2'),	"ss02", N_("Style Set 2"),		gsub_single_mask },
149     { CHR('s','s','0','3'),	"ss03", N_("Style Set 3"),		gsub_single_mask },
150     { CHR('s','s','0','4'),	"ss04", N_("Style Set 4"),		gsub_single_mask },
151     { CHR('s','s','0','5'),	"ss05", N_("Style Set 5"),		gsub_single_mask },
152     { CHR('s','s','0','6'),	"ss06", N_("Style Set 6"),		gsub_single_mask },
153     { CHR('s','s','0','7'),	"ss07", N_("Style Set 7"),		gsub_single_mask },
154     { CHR('s','s','0','8'),	"ss08", N_("Style Set 8"),		gsub_single_mask },
155     { CHR('s','s','0','9'),	"ss09", N_("Style Set 9"),		gsub_single_mask },
156     { CHR('s','s','1','0'),	"ss10", N_("Style Set 10"),		gsub_single_mask },
157     { CHR('s','s','1','1'),	"ss11", N_("Style Set 11"),		gsub_single_mask },
158     { CHR('s','s','1','2'),	"ss12", N_("Style Set 12"),		gsub_single_mask },
159     { CHR('s','s','1','3'),	"ss13", N_("Style Set 13"),		gsub_single_mask },
160     { CHR('s','s','1','4'),	"ss14", N_("Style Set 14"),		gsub_single_mask },
161     { CHR('s','s','1','5'),	"ss15", N_("Style Set 15"),		gsub_single_mask },
162     { CHR('s','s','1','6'),	"ss16", N_("Style Set 16"),		gsub_single_mask },
163     { CHR('s','s','1','7'),	"ss17", N_("Style Set 17"),		gsub_single_mask },
164     { CHR('s','s','1','8'),	"ss18", N_("Style Set 18"),		gsub_single_mask },
165     { CHR('s','s','1','9'),	"ss19", N_("Style Set 19"),		gsub_single_mask },
166     { CHR('s','s','2','0'),	"ss20", N_("Style Set 20"),		gsub_single_mask },
167     { CHR('s','s','t','y'),	"ssty", N_("Script Style"),		gsub_single_mask },
168     { CHR('s','u','b','s'),	"subs", N_("Subscript"),		gsub_single_mask },
169     { CHR('s','u','p','s'),	"sups", N_("Superscript"),		gsub_single_mask },
170     { CHR('s','w','s','h'),	"swsh", N_("Swash"),			gsub_single_mask|gsub_alternate_mask },
171     { CHR('t','i','t','l'),	"titl", N_("Titling"),			gsub_single_mask },
172     { CHR('t','j','m','o'),	"tjmo", N_("Trailing Jamo Forms"),	gsub_ligature_mask },
173     { CHR('t','n','a','m'),	"tnam", N_("Traditional Name Forms"),	gsub_single_mask },
174     { CHR('t','n','u','m'),	"tnum", N_("Tabular Numbers"),		gsub_single_mask },
175     { CHR('t','r','a','d'),	"trad", N_("Traditional Forms"),	gsub_single_mask|gsub_alternate_mask },
176     { CHR('t','w','i','d'),	"twid", N_("Third Widths"),		gsub_single_mask|gpos_single_mask },
177     { CHR('u','n','i','c'),	"unic", N_("Unicase"),			gsub_single_mask },
178     { CHR('v','a','l','t'),	"valt", N_("Alternate Vertical Metrics"),	gpos_single_mask },
179     { CHR('v','a','t','u'),	"vatu", N_("Vattu Variants"),		gsub_ligature_mask },
180     { CHR('v','e','r','t'),	"vert", N_("Vertical Alternates (obs)"),	gsub_single_mask },
181     { CHR('v','h','a','l'),	"vhal", N_("Alternate Vertical Half Metrics"),	gpos_single_mask },
182     { CHR('v','j','m','o'),	"vjmo", N_("Vowel Jamo Forms"),		gsub_ligature_mask },
183     { CHR('v','k','n','a'),	"vkna", N_("Vertical Kana Alternates"),	gsub_single_mask },
184     { CHR('v','k','r','n'),	"vkrn", N_("Vertical Kerning"),		gpos_pair_mask|gpos_context_mask|gpos_contextchain_mask },
185     { CHR('v','p','a','l'),	"vpal", N_("Proportional Alternate Vertical Metrics"),	gpos_single_mask },
186     { CHR('v','r','t','2'),	"vrt2", N_("Vertical Rotation & Alternates"),	gsub_single_mask },
187     { CHR('z','e','r','o'),	"zero", N_("Slashed Zero"),		gsub_single_mask },
188 /* This is my hack for setting the "Required feature" field of a script */
189     { 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 },
190     { 0, NULL, 0, 0 }
191 };
192 
uint32_cmp(const void * _ui1,const void * _ui2)193 static int uint32_cmp(const void *_ui1, const void *_ui2) {
194     if ( *(uint32 *) _ui1 > *(uint32 *)_ui2 )
195 return( 1 );
196     if ( *(uint32 *) _ui1 < *(uint32 *)_ui2 )
197 return( -1 );
198 
199 return( 0 );
200 }
201 
lang_cmp(const void * _ui1,const void * _ui2)202 static int lang_cmp(const void *_ui1, const void *_ui2) {
203     /* The default language is magic, and should come first in the list even */
204     /*  if that is not true alphabetical order */
205     if ( *(uint32 *) _ui1 == DEFAULT_LANG )
206 return( -1 );
207     if ( *(uint32 *) _ui2 == DEFAULT_LANG )
208 return( 1 );
209 
210     if ( *(uint32 *) _ui1 > *(uint32 *)_ui2 )
211 return( 1 );
212     if ( *(uint32 *) _ui1 < *(uint32 *)_ui2 )
213 return( -1 );
214 
215 return( 0 );
216 }
217 
FindFeatureTagInFeatureScriptList(uint32 tag,FeatureScriptLangList * fl)218 FeatureScriptLangList *FindFeatureTagInFeatureScriptList(uint32 tag, FeatureScriptLangList *fl) {
219 
220     while ( fl!=NULL ) {
221 	if ( fl->featuretag==tag )
222 return( fl );
223 	fl = fl->next;
224     }
225 return( NULL );
226 }
227 
FeatureTagInFeatureScriptList(uint32 tag,FeatureScriptLangList * fl)228 int FeatureTagInFeatureScriptList(uint32 tag, FeatureScriptLangList *fl) {
229 
230     while ( fl!=NULL ) {
231 	if ( fl->featuretag==tag )
232 return( true );
233 	fl = fl->next;
234     }
235 return( false );
236 }
237 
ScriptInFeatureScriptList(uint32 script,FeatureScriptLangList * fl)238 int ScriptInFeatureScriptList(uint32 script, FeatureScriptLangList *fl) {
239     struct scriptlanglist *sl;
240 
241     if ( fl==NULL )		/* No features bound to lookup? (nested?) don't restrict by script */
242 return( true );
243 
244     while ( fl!=NULL ) {
245 	for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) {
246 	    if ( sl->script == script )
247 return( true );
248 	}
249 	fl = fl->next;
250     }
251 return( false );
252 }
253 
FeatureScriptTagInFeatureScriptList(uint32 feature,uint32 script,FeatureScriptLangList * fl)254 int FeatureScriptTagInFeatureScriptList(uint32 feature, uint32 script, FeatureScriptLangList *fl) {
255     struct scriptlanglist *sl;
256 
257     while ( fl!=NULL ) {
258 	if ( fl->featuretag == feature ) {
259 	    for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) {
260 		if ( sl->script == script )
261 return( true );
262 	    }
263 	}
264 	fl = fl->next;
265     }
266 return( false );
267 }
268 
DefaultLangTagInOneScriptList(struct scriptlanglist * sl)269 int DefaultLangTagInOneScriptList(struct scriptlanglist *sl) {
270     int l;
271 
272     for ( l=0; l<sl->lang_cnt; ++l ) {
273 	uint32 lang = l<MAX_LANG ? sl->langs[l] : sl->morelangs[l-MAX_LANG];
274 	if ( lang==DEFAULT_LANG )
275 return( true );
276     }
277 return( false );
278 }
279 
DefaultLangTagInScriptList(struct scriptlanglist * sl,int DFLT_ok)280 struct scriptlanglist *DefaultLangTagInScriptList(struct scriptlanglist *sl, int DFLT_ok) {
281 
282     while ( sl!=NULL ) {
283 	if ( DFLT_ok || sl->script!=DEFAULT_SCRIPT ) {
284 	    if ( DefaultLangTagInOneScriptList(sl))
285 return( sl );
286 	}
287 	sl = sl->next;
288     }
289 return( NULL );
290 }
291 
SFScriptsInLookups(SplineFont * sf,int gpos)292 uint32 *SFScriptsInLookups(SplineFont *sf,int gpos) {
293     /* Presumes that either SFFindUnusedLookups or SFFindClearUnusedLookupBits */
294     /*  has been called first */
295     /* Since MS will sometimes ignore a script if it isn't found in both */
296     /*  GPOS and GSUB we want to return the same script list no matter */
297     /*  what the setting of gpos ... so we totally ignore that argument */
298     /*  and always look at both sets of lookups */
299 
300 /* Sergey Malkin from MicroSoft tells me:
301     Each shaping engine in Uniscribe can decide on its requirements for
302     layout tables - some of them require both GSUB and GPOS, in some cases
303     any table present is enough, or it can work without any table.
304 
305     Sometimes, purpose of the check is to determine if font is supporting
306     particular script - if required tables are not there font is just
307     rejected by this shaping engine. Sometimes, shaping engine can not just
308     reject the font because there are fonts using older shaping technologies
309     we still have to support, so it uses some logic when to fallback to
310     legacy layout code.
311 
312     In your case this is Hebrew, where both tables are required to use
313     OpenType processing. Arabic requires both tables too, Latin requires
314     GSUB to execute GPOS. But in general, if you have both tables you should
315     be safe with any script to get fully featured OpenType shaping.
316 
317 In other words, if we have a Hebrew font with just GPOS features they won't work,
318 and MS will not use the font at all. We must add a GSUB table. In the unlikely
319 event that we had a hebrew font with only GSUB it would not work either.
320 
321 So if we want our lookups to have a chance of executing under Uniscribe we
322 better make sure that both tables have the same script set.
323 
324 (Sergey says we could optimize a little: A Latin GSUB table will run without
325 a GPOS, but he says the GPOS won't work without a GSUB.)
326 */
327     int cnt=0, tot=0, i;
328     uint32 *scripts = NULL;
329     OTLookup *test;
330     FeatureScriptLangList *fl;
331     struct scriptlanglist *sl;
332 
333     /* So here always give scripts for both (see comment above) no */
334     /*  matter what they asked for */
335     for ( gpos=0; gpos<2; ++gpos ) {
336 	for ( test = gpos ? sf->gpos_lookups : sf->gsub_lookups; test!=NULL; test = test->next ) {
337 	    if ( test->unused )
338 	continue;
339 	    for ( fl=test->features; fl!=NULL; fl=fl->next ) {
340 		for ( sl=fl->scripts ; sl!=NULL; sl=sl->next ) {
341 		    for ( i=0; i<cnt; ++i ) {
342 			if ( sl->script==scripts[i] )
343 		    break;
344 		    }
345 		    if ( i==cnt ) {
346 			if ( cnt>=tot )
347 			    scripts = grealloc(scripts,(tot+=10)*sizeof(uint32));
348 			scripts[cnt++] = sl->script;
349 		    }
350 		}
351 	    }
352 	}
353     }
354 
355     if ( cnt==0 )
356 return( NULL );
357 
358     /* We want our scripts in alphabetic order */
359     qsort(scripts,cnt,sizeof(uint32),uint32_cmp);
360     /* add a 0 entry to mark the end of the list */
361     if ( cnt>=tot )
362 	scripts = grealloc(scripts,(tot+1)*sizeof(uint32));
363     scripts[cnt] = 0;
364 return( scripts );
365 }
366 
SFLangsInScript(SplineFont * sf,int gpos,uint32 script)367 uint32 *SFLangsInScript(SplineFont *sf,int gpos,uint32 script) {
368     /* However, the language lists (I think) are distinct */
369     /* But giving a value of -1 for gpos will give us the set of languages in */
370     /*  both tables (for this script) */
371     int cnt=0, tot=0, i, g, l;
372     uint32 *langs = NULL;
373     OTLookup *test;
374     FeatureScriptLangList *fl;
375     struct scriptlanglist *sl;
376 
377     for ( g=0; g<2; ++g ) {
378 	if (( gpos==0 && g==1 ) || ( gpos==1 && g==0 ))
379     continue;
380 	for ( test = gpos ? sf->gpos_lookups : sf->gsub_lookups; test!=NULL; test = test->next ) {
381 	    if ( test->unused )
382 	continue;
383 	    for ( fl=test->features; fl!=NULL; fl=fl->next ) {
384 		for ( sl=fl->scripts ; sl!=NULL; sl=sl->next ) {
385 		    if ( sl->script==script ) {
386 			for ( l=0; l<sl->lang_cnt; ++l ) {
387 			    unsigned lang;
388 			    if ( l<MAX_LANG )
389 				lang = sl->langs[l];
390 			    else
391 				lang = sl->morelangs[l-MAX_LANG];
392 			    for ( i=0; i<cnt; ++i ) {
393 				if ( lang==langs[i] )
394 			    break;
395 			    }
396 			    if ( i==cnt ) {
397 				if ( cnt>=tot )
398 				    langs = grealloc(langs,(tot+=10)*sizeof(uint32));
399 				langs[cnt++] = lang;
400 			    }
401 			}
402 		    }
403 		}
404 	    }
405 	}
406     }
407 
408     if ( cnt==0 ) {
409 	/* We add dummy script entries. Because Uniscribe will refuse to */
410 	/*  process some scripts if they don't have an entry in both GPOS */
411 	/*  an GSUB. So if a script appears in either table, force it to */
412 	/*  appear in both. That means we can get scripts with no lookups */
413 	/*  and hence no languages. It seems that Uniscribe doesn't like */
414 	/*  that either. So give each such script a dummy default language */
415 	/*  entry. This is what VOLT does */
416 	langs = gcalloc(2,sizeof(uint32));
417 	langs[0] = DEFAULT_LANG;
418 return( langs );
419     }
420 
421     /* We want our languages in alphabetic order */
422     qsort(langs,cnt,sizeof(uint32),lang_cmp);
423     /* add a 0 entry to mark the end of the list */
424     if ( cnt>=tot )
425 	langs = grealloc(langs,(tot+1)*sizeof(uint32));
426     langs[cnt] = 0;
427 return( langs );
428 }
429 
SFFeaturesInScriptLang(SplineFont * sf,int gpos,uint32 script,uint32 lang)430 uint32 *SFFeaturesInScriptLang(SplineFont *sf,int gpos,uint32 script,uint32 lang) {
431     int cnt=0, tot=0, i, l, isg;
432     uint32 *features = NULL;
433     OTLookup *test;
434     FeatureScriptLangList *fl;
435     struct scriptlanglist *sl;
436     /* gpos==0 => GSUB, gpos==1 => GPOS, gpos==-1 => both, gpos==-2 => Both & kern */
437 
438     if ( sf->cidmaster ) sf=sf->cidmaster;
439     for ( isg = 0; isg<2; ++isg ) {
440 	if ( gpos>=0 && isg!=gpos )
441     continue;
442 	for ( test = isg ? sf->gpos_lookups : sf->gsub_lookups; test!=NULL; test = test->next ) {
443 	    if ( test->unused )
444 	continue;
445 	    for ( fl=test->features; fl!=NULL; fl=fl->next ) {
446 		if ( script==0xffffffff ) {
447 		    for ( i=0; i<cnt; ++i ) {
448 			if ( fl->featuretag==features[i] )
449 		    break;
450 		    }
451 		    if ( i==cnt ) {
452 			if ( cnt>=tot )
453 			    features = grealloc(features,(tot+=10)*sizeof(uint32));
454 			features[cnt++] = fl->featuretag;
455 		    }
456 		} else for ( sl=fl->scripts ; sl!=NULL; sl=sl->next ) {
457 		    if ( sl->script==script ) {
458 			int matched = false;
459 			for ( l=0; l<sl->lang_cnt; ++l ) {
460 			    unsigned testlang;
461 			    if ( l<MAX_LANG )
462 				testlang = sl->langs[l];
463 			    else
464 				testlang = sl->morelangs[l-MAX_LANG];
465 			    if ( testlang==lang ) {
466 				matched = true;
467 			break;
468 			    }
469 			}
470 			if ( matched ) {
471 			    for ( i=0; i<cnt; ++i ) {
472 				if ( fl->featuretag==features[i] )
473 			    break;
474 			    }
475 			    if ( i==cnt ) {
476 				if ( cnt>=tot )
477 				    features = grealloc(features,(tot+=10)*sizeof(uint32));
478 				features[cnt++] = fl->featuretag;
479 			    }
480 			}
481 		    }
482 		}
483 	    }
484 	}
485     }
486 
487     if ( sf->design_size!=0 && gpos ) {
488 	/* The 'size' feature is like no other. It has no lookups and so */
489 	/*  we will never find it in the normal course of events. If the */
490 	/*  user has specified a design size, then every script/lang combo */
491 	/*  gets a 'size' feature which contains no lookups but feature */
492 	/*  params */
493 	if ( cnt>=tot )
494 	    features = grealloc(features,(tot+=2)*sizeof(uint32));
495 	features[cnt++] = CHR('s','i','z','e');
496     }
497 
498     if ( cnt==0 )
499 return( gcalloc(1,sizeof(uint32)) );
500 
501     /* We don't care if our features are in alphabetical order here */
502     /*  all that matters is whether the complete list of features is */
503     /*  ordering here would be irrelevant */
504     /* qsort(features,cnt,sizeof(uint32),uint32_cmp); */
505 
506     /* add a 0 entry to mark the end of the list */
507     if ( cnt>=tot )
508 	features = grealloc(features,(tot+1)*sizeof(uint32));
509     features[cnt] = 0;
510 return( features );
511 }
512 
SFLookupsInScriptLangFeature(SplineFont * sf,int gpos,uint32 script,uint32 lang,uint32 feature)513 OTLookup **SFLookupsInScriptLangFeature(SplineFont *sf,int gpos,uint32 script,uint32 lang, uint32 feature) {
514     int cnt=0, tot=0, l;
515     OTLookup **lookups = NULL;
516     OTLookup *test;
517     FeatureScriptLangList *fl;
518     struct scriptlanglist *sl;
519 
520     for ( test = gpos ? sf->gpos_lookups : sf->gsub_lookups; test!=NULL; test = test->next ) {
521 	if ( test->unused )
522     continue;
523 	for ( fl=test->features; fl!=NULL; fl=fl->next ) {
524 	    if ( fl->featuretag==feature ) {
525 		for ( sl=fl->scripts ; sl!=NULL; sl=sl->next ) {
526 		    if ( sl->script==script ) {
527 			for ( l=0; l<sl->lang_cnt; ++l ) {
528 			    unsigned testlang;
529 			    if ( l<MAX_LANG )
530 				testlang = sl->langs[l];
531 			    else
532 				testlang = sl->morelangs[l-MAX_LANG];
533 			    if ( testlang==lang ) {
534 				if ( cnt>=tot )
535 				    lookups = grealloc(lookups,(tot+=10)*sizeof(OTLookup *));
536 				lookups[cnt++] = test;
537 	goto found;
538 			    }
539 			}
540 		    }
541 		}
542 	    }
543 	}
544 	found:;
545     }
546 
547     if ( cnt==0 )
548 return( NULL );
549 
550     /* lookup order is irrelevant here. might as well leave it in invocation order */
551     /* add a 0 entry to mark the end of the list */
552     if ( cnt>=tot )
553 	lookups = grealloc(lookups,(tot+1)*sizeof(OTLookup *));
554     lookups[cnt] = 0;
555 return( lookups );
556 }
557 
LigaturesFirstComponentGID(SplineFont * sf,char * components)558 static int LigaturesFirstComponentGID(SplineFont *sf,char *components) {
559     int gid, ch;
560     char *pt;
561 
562     for ( pt = components; *pt!='\0' && *pt!=' '; ++pt );
563     ch = *pt; *pt = '\0';
564     gid = SFFindExistingSlot(sf,-1,components);
565     *pt = ch;
566 return( gid );
567 }
568 
PSTValid(SplineFont * sf,PST * pst)569 static int PSTValid(SplineFont *sf,PST *pst) {
570     char *start, *pt, ch;
571     int ret;
572     (void)sf;
573     switch ( pst->type ) {
574       case pst_position:
575 return( true );
576       case pst_pair:
577 return( SCWorthOutputting(SFGetChar(sf,-1,pst->u.pair.paired)) );
578       case pst_substitution: case pst_alternate: case pst_multiple:
579       case pst_ligature:
580 	for ( start = pst->u.mult.components; *start ; ) {
581 	    for ( pt=start; *pt && *pt!=' '; ++pt );
582 	    ch = *pt; *pt = '\0';
583 	    ret = SCWorthOutputting(SFGetChar(sf,-1,start));
584 	    *pt = ch;
585 	    if ( !ret )
586 return( false );
587 	    if ( ch==0 )
588 		start = pt;
589 	    else
590 		start = pt+1;
591 	}
592     }
593 return( true );
594 }
595 
SFGlyphsWithPSTinSubtable(SplineFont * sf,struct lookup_subtable * subtable)596 SplineChar **SFGlyphsWithPSTinSubtable(SplineFont *sf,struct lookup_subtable *subtable) {
597     uint8 *used = gcalloc(sf->glyphcnt,sizeof(uint8));
598     SplineChar **glyphs, *sc;
599     int i, k, gid, cnt;
600     KernPair *kp;
601     PST *pst;
602     int ispair = subtable->lookup->lookup_type == gpos_pair;
603     int isliga = subtable->lookup->lookup_type == gsub_ligature;
604     sc = NULL;
605     for ( i=0; i<sf->glyphcnt; ++i ) if ( SCWorthOutputting(sc = sf->glyphs[i]) ) {
606 	if ( ispair ) {
607 	    for ( k=0; k<2; ++k ) {
608 		for ( kp= k ? sc->kerns : sc->vkerns; kp!=NULL ; kp=kp->next ) {
609 		    if ( !SCWorthOutputting(kp->sc))
610 		continue;
611 		    if ( kp->subtable == subtable ) {
612 			used[i] = true;
613     goto continue_;
614 		    }
615 		}
616 	    }
617 	}
618 	for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
619 	    if ( pst->subtable == subtable && PSTValid(sf,pst)) {
620 		if ( !isliga ) {
621 		    used[i] = true;
622     goto continue_;
623 		} else {
624 		    gid = LigaturesFirstComponentGID(sf,pst->u.lig.components);
625 		    pst->u.lig.lig = sc;
626 		    if ( gid!=-1 )
627 			used[gid] = true;
628 		    /* can't continue here. ffi might be "f+f+i" and "ff+i" */
629 		    /*  and we need to mark both "f" and "ff" as used */
630 		}
631 	    }
632 	}
633     continue_: ;
634     }
635 
636     for ( i=cnt=0 ; i<sf->glyphcnt; ++i )
637 	if ( used[i] )
638 	    ++cnt;
639 
640     if ( cnt==0 ) {
641 	free(used);
642 return( NULL );
643     }
644     glyphs = galloc((cnt+1)*sizeof(SplineChar *));
645     for ( i=cnt=0 ; i<sf->glyphcnt; ++i ) {
646 	if ( used[i] )
647 	    glyphs[cnt++] = sf->glyphs[i];
648     }
649     glyphs[cnt] = NULL;
650     free(used);
651 return( glyphs );
652 }
653 
SFGlyphsWithLigatureinLookup(SplineFont * sf,struct lookup_subtable * subtable)654 SplineChar **SFGlyphsWithLigatureinLookup(SplineFont *sf,struct lookup_subtable *subtable) {
655     uint8 *used = gcalloc(sf->glyphcnt,sizeof(uint8));
656     SplineChar **glyphs, *sc;
657     int i, cnt;
658     PST *pst;
659     sc=NULL;
660     /* for ( i=0; i<sf->glyphcnt; ++i ) if ( SCWorthOutputting(sc = sf->glyphs[i]) ) { */
661     /* 	for ( pst=sc->possub; pst!=NULL; pst=pst->next ) { */
662     /* 	    if ( pst->subtable == subtable ) { */
663     /* 		used[i] = true; */
664     /* goto continue_; */
665     /* 	    } */
666     /* 	} */
667     /* continue_: ; */
668     /* } */
669     /* Issue 866 : currently #define SCWorthOutputting(a) 1 so we drop it */
670     for ( i=0; i<sf->glyphcnt; ++i ) {
671       sc = sf->glyphs[i];
672       if ( sc ) {
673     	for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
674 	  if ( pst->subtable == subtable ) {
675 	    used[i] = true;
676       goto continue_;
677 	  }
678     	}
679       continue_: ;
680       }/* end if ( sc ) */
681     }/*end for */
682 
683     for ( i=cnt=0 ; i<sf->glyphcnt; ++i )
684 	if ( used[i] )
685 	    ++cnt;
686 
687     if ( cnt==0 ) {
688 	free(used);
689 return( NULL );
690     }
691 
692     glyphs = galloc((cnt+1)*sizeof(SplineChar *));
693     for ( i=cnt=0 ; i<sf->glyphcnt; ++i ) {
694 	if ( used[i] )
695 	    glyphs[cnt++] = sf->glyphs[i];
696     }
697     glyphs[cnt] = NULL;
698     free(used);
699 return( glyphs );
700 }
701 
SFFindUnusedLookups(SplineFont * sf)702 void SFFindUnusedLookups(SplineFont *sf) {
703     OTLookup *test;
704     struct lookup_subtable *sub;
705     int gpos;
706     AnchorClass *ac;
707     AnchorPoint *ap;
708     SplineChar *sc;
709     KernPair *kp;
710     PST *pst;
711     int k,gid,isv;
712     SplineFont *_sf = sf;
713     sc=NULL;
714     if ( _sf->cidmaster ) _sf = _sf->cidmaster;
715 
716     /* Some things are obvious. If a subtable consists of a kernclass or some */
717     /*  such, then obviously it is used. But more distributed info takes more */
718     /*  work. So mark anything easy as used, and anything difficult as unused */
719     /* We'll work on the difficult things later */
720     for ( gpos=0; gpos<2; ++gpos ) {
721 	for ( test = gpos ? _sf->gpos_lookups : _sf->gsub_lookups; test!=NULL; test = test->next ) {
722 	    for ( sub = test->subtables; sub!=NULL; sub=sub->next ) {
723 		if ( sub->kc!=NULL || sub->fpst!=NULL ) {
724 		    sub->unused = false;
725 	    continue;
726 		}
727 		sub->unused = true;
728 		/* We'll turn the following bit back on if there turns out */
729 		/*  to be an anchor class attached to it -- that is subtly */
730 		/*  different than being unused -- unused will be set if all */
731 		/*  acs are unused, this bit will be on if there are unused */
732 		/*  classes that still refer to us. */
733 		sub->anchor_classes = false;
734 	    }
735 	}
736     }
737 
738     /* To be useful an anchor class must have both at least one base and one mark */
739     /*  (for cursive anchors that means at least one entry and at least one exit) */
740     /* Start by assuming the worst */
741     for ( ac = _sf->anchor; ac!=NULL; ac=ac->next )
742 	ac->has_mark = ac->has_base = false;
743 
744     /* Ok, for each glyph, look at all lookups (or anchor classes) it affects */
745     /*  and mark the appropriate parts of them as used */
746     k = 0;
747     do {
748 	sf = _sf->subfontcnt==0 ? _sf : _sf->subfonts[k];
749 	for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( SCWorthOutputting(sc = sf->glyphs[gid]) ) {
750 	    for ( ap=sc->anchor; ap!=NULL; ap=ap->next ) {
751 		switch ( ap->type ) {
752 		  case at_mark: case at_centry:
753 		    ap->anchor->has_mark = true;
754 		  break;
755 		  case at_basechar: case at_baselig: case at_basemark:
756 		  case at_cexit:
757 		    ap->anchor->has_base = true;
758 		  break;
759 		}
760 	    }
761 	    for ( isv=0; isv<2; ++isv ) {
762 		for ( kp= isv ? sc->kerns : sc->vkerns ; kp!=NULL; kp=kp->next ) {
763 		    if ( SCWorthOutputting(kp->sc))
764 			kp->subtable->unused = false;
765 		}
766 	    }
767 	    for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
768 		if ( pst->subtable==NULL )
769 	    continue;
770 		if ( !PSTValid(sf,pst))
771 	    continue;
772 		pst->subtable->unused = false;
773 	    }
774 	}
775 	++k;
776     } while ( k<_sf->subfontcnt );
777 
778     /* Finally for any anchor class that has both a mark and a base then it is */
779     /*  used, and its lookup is also used */
780     /* Also, even if unused, as long as the anchor class exists we must keep */
781     /*  the subtable around */
782     for ( ac = _sf->anchor; ac!=NULL; ac=ac->next ) {
783 	ac->subtable->anchor_classes = true;
784 	if ( ac->has_mark && ac->has_base )
785 	    ac->subtable->unused = false;
786     }
787 
788     /* Now for each lookup, a lookup is unused if ALL subtables are unused */
789     for ( gpos=0; gpos<2; ++gpos ) {
790 	for ( test = gpos ? _sf->gpos_lookups : _sf->gsub_lookups; test!=NULL; test = test->next ) {
791 	    test->unused = test->empty = true;
792 	    for ( sub=test->subtables; sub!=NULL; sub=sub->next ) {
793 		if ( !sub->unused )
794 		    test->unused = false;
795 		if ( !sub->unused && !sub->anchor_classes ) {
796 		    test->empty = false;
797 	    break;
798 		}
799 	    }
800 	}
801     }
802 }
803 
SFFindClearUnusedLookupBits(SplineFont * sf)804 void SFFindClearUnusedLookupBits(SplineFont *sf) {
805     OTLookup *test;
806     int gpos;
807 
808     for ( gpos=0; gpos<2; ++gpos ) {
809 	for ( test = gpos ? sf->gpos_lookups : sf->gsub_lookups; test!=NULL; test = test->next ) {
810 	    test->unused = false;
811 	    test->empty = false;
812 	    test->def_lang_checked = false;
813 	}
814     }
815 }
816 
SFRemoveAnchorPointsOfAC(SplineFont * sf,AnchorClass * ac)817 static void SFRemoveAnchorPointsOfAC(SplineFont *sf,AnchorClass *ac) {
818     int gid;
819     SplineChar *sc;
820     AnchorPoint *ap, *prev, *next;
821 
822     for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc = sf->glyphs[gid])!=NULL ) {
823 	for ( prev=NULL, ap=sc->anchor; ap!=NULL; ap=next ) {
824 	    next = ap->next;
825 	    if ( ap->anchor!=ac )
826 		prev = ap;
827 	    else {
828 		if ( prev==NULL )
829 		    sc->anchor = next;
830 		else
831 		    prev->next = next;
832 		ap->next = NULL;
833 		AnchorPointsFree(ap);
834 	    }
835 	}
836     }
837 }
838 
RemoveNestedReferences(SplineFont * sf,int isgpos,OTLookup * dying)839 static void RemoveNestedReferences(SplineFont *sf,int isgpos,OTLookup *dying) {
840     OTLookup *otl;
841     struct lookup_subtable *sub;
842     int i,j,k;
843     (void)dying;
844     for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl = otl->next ) {
845 	/* Reverse chaining tables do not reference lookups. The match pattern*/
846 	/*  is a (exactly one) coverage table, and each glyph in that table   */
847 	/*  as an inline replacement. There is no lookup to do the replacement*/
848 	/* (so we ignore it here) */
849 	if ( otl->lookup_type==gsub_context || otl->lookup_type==gsub_contextchain ||
850 		otl->lookup_type==gpos_context || otl->lookup_type==gpos_contextchain ) {
851 	    for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
852 		FPST *fpst = sub->fpst;
853 		for ( i=0; i<fpst->rule_cnt; ++i ) {
854 		    for ( j=0; j<fpst->rules[i].lookup_cnt; ++j ) {
855 			if ( fpst->rules[i].lookups[j].lookup == otl ) {
856 			    for ( k=j+1; k<fpst->rules[i].lookup_cnt; ++k )
857 				fpst->rules[i].lookups[k-1] = fpst->rules[i].lookups[k];
858 			    --fpst->rules[i].lookup_cnt;
859 			    --j;
860 			}
861 		    }
862 		}
863 	    }
864 	}
865     }
866 }
867 
SFRemoveUnusedLookupSubTables(SplineFont * sf,int remove_incomplete_anchorclasses,int remove_unused_lookups)868 void SFRemoveUnusedLookupSubTables(SplineFont *sf,
869 	int remove_incomplete_anchorclasses,
870 	int remove_unused_lookups) {
871     int gpos;
872     struct lookup_subtable *sub, *subnext, *prev;
873     AnchorClass *ac, *acprev, *acnext;
874     OTLookup *otl, *otlprev, *otlnext;
875     otlprev=NULL;
876     /* Presumes someone has called SFFindUnusedLookups first */
877 
878     if ( remove_incomplete_anchorclasses ) {
879 	for ( acprev=NULL, ac=sf->anchor; ac!=NULL; ac=acnext ) {
880 	    acnext = ac->next;
881 	    if ( ac->has_mark && ac->has_base )
882 		acprev = ac;
883 	    else {
884 		SFRemoveAnchorPointsOfAC(sf,ac);
885 		ac->next = NULL;
886 		AnchorClassesFree(ac);
887 		if ( acprev==NULL )
888 		    sf->anchor = acnext;
889 		else
890 		    acprev = acnext;
891 	    }
892 	}
893     }
894 
895     for ( gpos=0; gpos<2; ++gpos ) {
896 	for ( otl = gpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl = otlnext ) {
897 	    otlnext = otl->next;
898 	    if ( remove_unused_lookups && (otl->empty ||
899 		    (otl->unused && remove_incomplete_anchorclasses)) ) {
900 		if ( otlprev!=NULL )
901 		    otlprev->next = otlnext;
902 		else if ( gpos )
903 		    sf->gpos_lookups = otlnext;
904 		else
905 		    sf->gsub_lookups = otlnext;
906 		RemoveNestedReferences(sf,gpos,otl);
907 		OTLookupFree(otl);
908 	    } else {
909 		for ( prev=NULL, sub=otl->subtables; sub!=NULL; sub=subnext ) {
910 		    subnext = sub->next;
911 		    if ( sub->unused &&
912 			    (!sub->anchor_classes ||
913 			      remove_incomplete_anchorclasses )) {
914 			if ( prev==NULL )
915 			    otl->subtables = subnext;
916 			else
917 			    prev->next = subnext;
918 			free(sub->subtable_name);
919 			chunkfree(sub,sizeof(*sub));
920 		    } else
921 			prev = sub;
922 		}
923 	    }
924 	}
925     }
926 }
927 
SFRemoveLookupSubTable(SplineFont * sf,struct lookup_subtable * sub)928 void SFRemoveLookupSubTable(SplineFont *sf,struct lookup_subtable *sub) {
929     OTLookup *otl = sub->lookup;
930     struct lookup_subtable *subprev, *subtest;
931 
932     if ( sf->cidmaster!=NULL ) sf = sf->cidmaster;
933 
934     if ( sub->fpst!=NULL ) {
935 	FPST *prev = NULL, *test;
936 	for ( test=sf->possub; test!=NULL && test!=sub->fpst; prev=test, test=test->next );
937 	if ( prev==NULL )
938 	    sf->possub = sub->fpst->next;
939 	else
940 	    prev->next = sub->fpst->next;
941 	sub->fpst->next = NULL;
942 	FPSTFree(sub->fpst);
943 	sub->fpst = NULL;
944     } else if ( sub->kc!=NULL ) {
945 	KernClass *prev = NULL, *test;
946 	for ( test=sf->kerns; test!=NULL && test!=sub->kc; prev=test, test=test->next );
947 	if ( test!=NULL ) {
948 	    if ( prev==NULL )
949 		sf->kerns = sub->kc->next;
950 	    else
951 		prev->next = sub->kc->next;
952 	} else {
953 	    for ( prev=NULL,test=sf->vkerns; test!=NULL && test!=sub->kc; prev=test, test=test->next );
954 	    if ( prev==NULL )
955 		sf->vkerns = sub->kc->next;
956 	    else
957 		prev->next = sub->kc->next;
958 	}
959 	sub->kc->next = NULL;
960 	KernClassListFree(sub->kc);
961 	sub->kc = NULL;
962     } else if ( otl->lookup_type==gpos_cursive || otl->lookup_type==gpos_mark2base ||
963 	    otl->lookup_type==gpos_mark2ligature || otl->lookup_type==gpos_mark2mark ) {
964 	AnchorClass *ac, *acnext;
965 	for ( ac=sf->anchor; ac!=NULL; ac=acnext ) {
966 	    acnext = ac->next;
967 	    if ( ac->subtable==sub )
968 		SFRemoveAnchorClass(sf,ac);
969 	}
970     } else {
971 	int i,k,v;
972 	SplineChar *sc;
973 	SplineFont *_sf;
974 	PST *pst, *prev, *next;
975 	KernPair *kp, *kpprev, *kpnext;
976 	k=0; i=0;
977 	do {
978 	    _sf = sf->subfontcnt==0 ? sf : sf->subfonts[i];
979 	    for ( i=0; i<_sf->glyphcnt; ++i ) if ( (sc=_sf->glyphs[i])!=NULL ) {
980 		for ( pst=sc->possub, prev=NULL ; pst!=NULL; pst=next ) {
981 		    next = pst->next;
982 		    if ( pst->subtable==sub ) {
983 			if ( prev==NULL )
984 			    sc->possub = next;
985 			else
986 			    prev->next = next;
987 			pst->next = NULL;
988 			PSTFree(pst);
989 		    } else
990 			prev = pst;
991 		}
992 		for ( v=0; v<2; ++v ) {
993 		    for ( kp=v ? sc->vkerns : sc->kerns, kpprev=NULL ; kp!=NULL; kp=kpnext ) {
994 			kpnext = kp->next;
995 			if ( kp->subtable==sub ) {
996 			    if ( kpprev!=NULL )
997 				kpprev->next = kpnext;
998 			    else if ( v )
999 				sc->vkerns = kpnext;
1000 			    else
1001 				sc->kerns = kpnext;
1002 			    kp->next = NULL;
1003 			    KernPairsFree(kp);
1004 			} else
1005 			    kpprev = kp;
1006 		    }
1007 		}
1008 	    }
1009 	    ++k;
1010 	} while ( k<sf->subfontcnt );
1011     }
1012 
1013     subprev = NULL;
1014     for ( subtest = otl->subtables; subtest!=NULL && subtest!=sub; subprev = subtest, subtest=subtest->next );
1015     if ( subprev==NULL )
1016 	otl->subtables = sub->next;
1017     else
1018 	subprev->next = sub->next;
1019     free(sub->subtable_name);
1020     free(sub->suffix);
1021     chunkfree(sub,sizeof(struct lookup_subtable));
1022 }
1023 
SFRemoveLookup(SplineFont * sf,OTLookup * otl)1024 void SFRemoveLookup(SplineFont *sf,OTLookup *otl) {
1025     OTLookup *test, *prev;
1026     int isgpos;
1027     struct lookup_subtable *sub, *subnext;
1028 
1029     if ( sf->cidmaster ) sf = sf->cidmaster;
1030 
1031     for ( sub = otl->subtables; sub!=NULL; sub=subnext ) {
1032 	subnext = sub->next;
1033 	SFRemoveLookupSubTable(sf,sub);
1034     }
1035 
1036     for ( prev=NULL, test=sf->gpos_lookups; test!=NULL && test!=otl; prev=test, test=test->next );
1037     if ( test==NULL ) {
1038 	isgpos = false;
1039 	for ( prev=NULL, test=sf->gsub_lookups; test!=NULL && test!=otl; prev=test, test=test->next );
1040     } else
1041 	isgpos = true;
1042     if ( prev!=NULL )
1043 	prev->next = otl->next;
1044     else if ( isgpos )
1045 	sf->gpos_lookups = otl->next;
1046     else
1047 	sf->gsub_lookups = otl->next;
1048 
1049     RemoveNestedReferences(sf,isgpos,otl);
1050 
1051     otl->next = NULL;
1052     OTLookupFree(otl);
1053 }
1054 
SFFindLookupSubtable(SplineFont * sf,char * name)1055 struct lookup_subtable *SFFindLookupSubtable(SplineFont *sf,char *name) {
1056     int isgpos;
1057     OTLookup *otl;
1058     struct lookup_subtable *sub;
1059 
1060     if ( sf->cidmaster ) sf = sf->cidmaster;
1061 
1062     if ( name==NULL )
1063 return( NULL );
1064 
1065     for ( isgpos=0; isgpos<2; ++isgpos ) {
1066 	for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups ; otl!=NULL; otl=otl->next ) {
1067 	    for ( sub = otl->subtables; sub!=NULL; sub=sub->next ) {
1068 		if ( strcmp(name,sub->subtable_name)==0 )
1069 return( sub );
1070 	    }
1071 	}
1072     }
1073 return( NULL );
1074 }
1075 
SFFindLookupSubtableAndFreeName(SplineFont * sf,char * name)1076 struct lookup_subtable *SFFindLookupSubtableAndFreeName(SplineFont *sf,char *name) {
1077     struct lookup_subtable *sub = SFFindLookupSubtable(sf,name);
1078     free(name);
1079 return( sub );
1080 }
1081 
SFFindLookup(SplineFont * sf,char * name)1082 OTLookup *SFFindLookup(SplineFont *sf,char *name) {
1083     int isgpos;
1084     OTLookup *otl;
1085 
1086     if ( sf->cidmaster ) sf = sf->cidmaster;
1087 
1088     if ( name==NULL )
1089 return( NULL );
1090 
1091     for ( isgpos=0; isgpos<2; ++isgpos ) {
1092 	for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups ; otl!=NULL; otl=otl->next ) {
1093 	    if ( strcmp(name,otl->lookup_name)==0 )
1094 return( otl );
1095 	}
1096     }
1097 return( NULL );
1098 }
1099 
FListAppendScriptLang(FeatureScriptLangList * fl,uint32 script_tag,uint32 lang_tag)1100 void FListAppendScriptLang(FeatureScriptLangList *fl,uint32 script_tag,uint32 lang_tag) {
1101     struct scriptlanglist *sl;
1102     int l;
1103 
1104     for ( sl = fl->scripts; sl!=NULL && sl->script!=script_tag; sl=sl->next );
1105     if ( sl==NULL ) {
1106 	sl = chunkalloc(sizeof(struct scriptlanglist));
1107 	sl->script = script_tag;
1108 	sl->next = fl->scripts;
1109 	fl->scripts = sl;
1110     }
1111     for ( l=0; l<MAX_LANG && l<sl->lang_cnt && sl->langs[l]!=lang_tag; ++l );
1112     if ( l>=MAX_LANG && l<sl->lang_cnt ) {
1113 	while ( l<sl->lang_cnt && sl->morelangs[l-MAX_LANG]!=lang_tag )
1114 	    ++l;
1115     }
1116     if ( l>=sl->lang_cnt ) {
1117 	if ( l<MAX_LANG )
1118 	    sl->langs[l] = lang_tag;
1119 	else {
1120 	    if ( l%MAX_LANG == 0 )
1121 		sl->morelangs = grealloc(sl->morelangs,l*sizeof(uint32));
1122 		/* We've just allocated MAX_LANG-1 more than we need */
1123 		/*  so we don't do quite some many allocations */
1124 	    sl->morelangs[l-MAX_LANG] = lang_tag;
1125 	}
1126 	++sl->lang_cnt;
1127     }
1128 }
1129 
FListsAppendScriptLang(FeatureScriptLangList * fl,uint32 script_tag,uint32 lang_tag)1130 void FListsAppendScriptLang(FeatureScriptLangList *fl,uint32 script_tag,uint32 lang_tag) {
1131     while ( fl!=NULL ) {
1132 	FListAppendScriptLang(fl,script_tag,lang_tag);
1133 	fl=fl->next;
1134     }
1135 }
1136 
SuffixFromTags(FeatureScriptLangList * fl)1137 char *SuffixFromTags(FeatureScriptLangList *fl) {
1138     static struct { uint32 tag; char *suffix; } tags2suffix[] = {
1139 	{ CHR('v','r','t','2'), "vert" },	/* Will check for vrt2 later */
1140 	{ CHR('o','n','u','m'), "oldstyle" },
1141 	{ CHR('s','u','p','s'), "superior" },
1142 	{ CHR('s','u','b','s'), "inferior" },
1143 	{ CHR('s','w','s','h'), "swash" },
1144 	{ CHR('f','w','i','d'), "full" },
1145 	{ CHR('h','w','i','d'), "hw" },
1146 	{ 0 , NULL }
1147 	};
1148     int i;
1149 
1150     while ( fl!=NULL ) {
1151 	for ( i=0; tags2suffix[i].tag!=0; ++i )
1152 	    if ( tags2suffix[i].tag==fl->featuretag )
1153 return( copy( tags2suffix[i].suffix ));
1154 	fl = fl->next;
1155     }
1156 return( NULL );
1157 }
1158 #ifdef LUA_FF_LIB
1159 /* ma = State Machine, un = Unknown */
1160 char *lookup_type_names[2][10] =
1161             { { "us", "ss", "ms", "as", "ls", "cs", "ks", "es", "rk" },
1162               { "up", "sp", "pp", "ca", "mb", "ml", "mm", "cp", "kp","ep" }};
1163 #else
1164 char *lookup_type_names[2][10] =
1165 	    { { N_("Undefined substitution"), N_("Single Substitution"), N_("Multiple Substitution"),
1166 		N_("Alternate Substitution"), N_("Ligature Substitution"), N_("Contextual Substitution"),
1167 		N_("Contextual Chaining Substitution"), N_("Extension"),
1168 		N_("Reverse Contextual Chaining Substitution") },
1169 	      { N_("Undefined positioning"), N_("Single Positioning"), N_("Pairwise Positioning (kerning)"),
1170 		N_("Cursive attachment"), N_("Mark to base attachment"),
1171 		N_("Mark to Ligature attachment"), N_("Mark to Mark attachment"),
1172 		N_("Contextual Positioning"), N_("Contextual Chaining Positioning"),
1173 		N_("Extension") }};
1174 #endif
1175 
1176 /* This is a non-ui based copy of a similar list in lookupui.c */
1177 static struct {
1178     char *text;
1179     uint32 tag;
1180 } localscripts[] = {
1181     { N_("Arabic"), CHR('a','r','a','b') },
1182     { N_("Aramaic"), CHR('a','r','a','m') },
1183     { N_("Armenian"), CHR('a','r','m','n') },
1184     { N_("Avestan"), CHR('a','v','e','s') },
1185     { N_("Balinese"), CHR('b','a','l','i') },
1186     { N_("Batak"), CHR('b','a','t','k') },
1187     { N_("Bengali"), CHR('b','e','n','g') },
1188     { N_("Bengali2"), CHR('b','n','g','2') },
1189     { N_("Bliss Symbolics"), CHR('b','l','i','s') },
1190     { N_("Bopomofo"), CHR('b','o','p','o') },
1191     { N_("Brāhmī"), CHR('b','r','a','h') },
1192     { N_("Braille"), CHR('b','r','a','i') },
1193     { N_("Buginese"), CHR('b','u','g','i') },
1194     { N_("Buhid"), CHR('b','u','h','d') },
1195     { N_("Byzantine Music"), CHR('b','y','z','m') },
1196     { N_("Canadian Syllabics"), CHR('c','a','n','s') },
1197     { N_("Carian"), CHR('c','a','r','i') },
1198     { N_("Cherokee"), CHR('c','h','a','m') },
1199     { N_("Cham"), CHR('c','h','a','m') },
1200     { N_("Cherokee"), CHR('c','h','e','r') },
1201     { N_("Cirth"), CHR('c','i','r','t') },
1202     { N_("CJK Ideographic"), CHR('h','a','n','i') },
1203     { N_("Coptic"), CHR('c','o','p','t') },
1204     { N_("Cypro-Minoan"), CHR('c','p','r','t') },
1205     { N_("Cypriot syllabary"), CHR('c','p','m','n') },
1206     { N_("Cyrillic"), CHR('c','y','r','l') },
1207     { N_("Default"), CHR('D','F','L','T') },
1208     { N_("Deseret (Mormon)"), CHR('d','s','r','t') },
1209     { N_("Devanagari"), CHR('d','e','v','a') },
1210     { N_("Devanagari2"), CHR('d','e','v','2') },
1211 /*  { N_("Egyptian demotic"), CHR('e','g','y','d') }, */
1212 /*  { N_("Egyptian hieratic"), CHR('e','g','y','h') }, */
1213 /* GT: Someone asked if FontForge actually was prepared generate hieroglyph output */
1214 /* GT: because of this string. No. But OpenType and Unicode have placeholders for */
1215 /* GT: dealing with these scripts against the day someone wants to use them. So */
1216 /* GT: FontForge must be prepared to deal with those placeholders if nothing else. */
1217 /*  { N_("Egyptian hieroglyphs"), CHR('e','g','y','p') }, */
1218     { N_("Ethiopic"), CHR('e','t','h','i') },
1219     { N_("Georgian"), CHR('g','e','o','r') },
1220     { N_("Glagolitic"), CHR('g','l','a','g') },
1221     { N_("Gothic"), CHR('g','o','t','h') },
1222     { N_("Greek"), CHR('g','r','e','k') },
1223     { N_("Gujarati"), CHR('g','u','j','r') },
1224     { N_("Gujarati2"), CHR('g','j','r','2') },
1225     { N_("Gurmukhi"), CHR('g','u','r','u') },
1226     { N_("Gurmukhi2"), CHR('g','u','r','2') },
1227     { N_("Hangul Jamo"), CHR('j','a','m','o') },
1228     { N_("Hangul"), CHR('h','a','n','g') },
1229     { N_("Hanunóo"), CHR('h','a','n','o') },
1230     { N_("Hebrew"), CHR('h','e','b','r') },
1231 /*  { N_("Pahawh Hmong"), CHR('h','m','n','g') },*/
1232 /*  { N_("Indus (Harappan)"), CHR('i','n','d','s') },*/
1233     { N_("Javanese"), CHR('j','a','v','a') },
1234     { N_("Kayah Li"), CHR('k','a','l','i') },
1235     { N_("Hiragana & Katakana"), CHR('k','a','n','a') },
1236     { N_("Kharoṣṭhī"), CHR('k','h','a','r') },
1237     { N_("Kannada"), CHR('k','n','d','a') },
1238     { N_("Kannada2"), CHR('k','n','d','2') },
1239     { N_("Khmer"), CHR('k','h','m','r') },
1240     { N_("Kharosthi"), CHR('k','h','a','r') },
1241     { N_("Lao") , CHR('l','a','o',' ') },
1242     { N_("Latin"), CHR('l','a','t','n') },
1243     { N_("Lepcha (Róng)"), CHR('l','e','p','c') },
1244     { N_("Limbu"), CHR('l','i','m','b') },	/* Not in ISO 15924 !!!!!, just guessing */
1245     { N_("Linear A"), CHR('l','i','n','a') },
1246     { N_("Linear B"), CHR('l','i','n','b') },
1247     { N_("Lycian"), CHR('l','y','c','i') },
1248     { N_("Lydian"), CHR('l','y','d','i') },
1249     { N_("Mandaean"), CHR('m','a','n','d') },
1250 /*  { N_("Mayan hieroglyphs"), CHR('m','a','y','a') },*/
1251     { N_("Malayālam"), CHR('m','l','y','m') },
1252     { N_("Malayālam2"), CHR('m','l','y','2') },
1253     { N_("Mathematical Alphanumeric Symbols"), CHR('m','a','t','h') },
1254     { N_("Mongolian"), CHR('m','o','n','g') },
1255     { N_("Musical"), CHR('m','u','s','i') },
1256     { N_("Myanmar"), CHR('m','y','m','r') },
1257     { N_("New Tai Lue"), CHR('t','a','l','u') },
1258     { N_("N'Ko"), CHR('n','k','o',' ') },
1259     { N_("Ogham"), CHR('o','g','a','m') },
1260     { N_("Ol Chiki"), CHR('o','l','c','k') },
1261     { N_("Old Italic (Etruscan, Oscan, etc.)"), CHR('i','t','a','l') },
1262     { N_("Old Permic"), CHR('p','e','r','m') },
1263     { N_("Old Persian cuneiform"), CHR('x','p','e','o') },
1264     { N_("Oriya"), CHR('o','r','y','a') },
1265     { N_("Oriya2"), CHR('o','r','y','2') },
1266     { N_("Osmanya"), CHR('o','s','m','a') },
1267     { N_("Pahlavi"), CHR('p','a','l','v') },
1268     { N_("Phags-pa"), CHR('p','h','a','g') },
1269     { N_("Phoenician"), CHR('p','h','n','x') },
1270     { N_("Phaistos"), CHR('p','h','s','t') },
1271     { N_("Pollard Phonetic"), CHR('p','l','r','d') },
1272     { N_("Rejang"), CHR('r','j','n','g') },
1273     { N_("Rongorongo"), CHR('r','o','r','o') },
1274     { N_("Runic"), CHR('r','u','n','r') },
1275     { N_("Saurashtra"), CHR('s','a','u','r') },
1276     { N_("Shavian"), CHR('s','h','a','w') },
1277     { N_("Sinhala"), CHR('s','i','n','h') },
1278     { N_("Sumero-Akkadian Cuneiform"), CHR('x','s','u','x') },
1279     { N_("Sundanese"), CHR('s','u','n','d') },
1280     { N_("Syloti Nagri"), CHR('s','y','l','o') },
1281     { N_("Syriac"), CHR('s','y','r','c') },
1282     { N_("Tagalog"), CHR('t','g','l','g') },
1283     { N_("Tagbanwa"), CHR('t','a','g','b') },
1284     { N_("Tai Le"), CHR('t','a','l','e') },	/* Not in ISO 15924 !!!!!, just guessing */
1285     { N_("Tai Lu"), CHR('t','a','l','a') },	/* Not in ISO 15924 !!!!!, just guessing */
1286     { N_("Tamil"), CHR('t','a','m','l') },
1287     { N_("Tamil2"), CHR('t','m','l','2') },
1288     { N_("Telugu"), CHR('t','e','l','u') },
1289     { N_("Telugu2"), CHR('t','e','l','2') },
1290     { N_("Tengwar"), CHR('t','e','n','g') },
1291     { N_("Thaana"), CHR('t','h','a','a') },
1292     { N_("Thai"), CHR('t','h','a','i') },
1293     { N_("Tibetan"), CHR('t','i','b','t') },
1294     { N_("Tifinagh (Berber)"), CHR('t','f','n','g') },
1295     { N_("Ugaritic"), CHR('u','g','r','t') },	/* Not in ISO 15924 !!!!!, just guessing */
1296     { N_("Vai"), CHR('v','a','i',' ') },
1297 /*  { N_("Visible Speech"), CHR('v','i','s','p') },*/
1298     { N_("Cuneiform, Ugaritic"), CHR('x','u','g','a') },
1299     { N_("Yi")  , CHR('y','i',' ',' ') },
1300 /*  { N_("Private Use Script 1"), CHR('q','a','a','a') },*/
1301 /*  { N_("Private Use Script 2"), CHR('q','a','a','b') },*/
1302 /*  { N_("Undetermined Script"), CHR('z','y','y','y') },*/
1303 /*  { N_("Uncoded Script"), CHR('z','z','z','z') },*/
1304     { NULL, 0 }
1305 };
1306 
LookupInit(void)1307 void LookupInit(void) {
1308     static int done = false;
1309     int i, j;
1310 
1311     if ( done )
1312 return;
1313     done = true;
1314     for ( j=0; j<2; ++j ) {
1315 	for ( i=0; i<10; ++i )
1316 	    if ( lookup_type_names[j][i]!=NULL )
1317 		lookup_type_names[j][i] = _((char *) lookup_type_names[j][i]);
1318     }
1319     for ( i=0; localscripts[i].text!=NULL; ++i )
1320 	localscripts[i].text = _(localscripts[i].text);
1321     for ( i=0; friendlies[i].friendlyname!=NULL; ++i )
1322 	friendlies[i].friendlyname = _(friendlies[i].friendlyname);
1323 }
1324 
TagFullName(SplineFont * sf,uint32 tag,int onlyifknown)1325 char *TagFullName(SplineFont *sf,uint32 tag, int onlyifknown) {
1326     char ubuf[200], *end = ubuf+sizeof(ubuf);
1327     int k;
1328 
1329 	unsigned stag = tag;
1330 	if ( tag==CHR('n','u','t','f') )	/* early name that was standardize later as... */
1331 	    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 */
1332 	if ( tag==REQUIRED_FEATURE ) {
1333 	    strcpy(ubuf,_("Required Feature"));
1334 	} else {
1335 	    LookupInit();
1336 	    for ( k=0; friendlies[k].tag!=0; ++k ) {
1337 		if ( friendlies[k].tag == stag )
1338 	    break;
1339 	    }
1340 	    ubuf[0] = '\'';
1341 	    ubuf[1] = tag>>24;
1342 	    ubuf[2] = (tag>>16)&0xff;
1343 	    ubuf[3] = (tag>>8)&0xff;
1344 	    ubuf[4] = tag&0xff;
1345 	    ubuf[5] = '\'';
1346 	    ubuf[6] = ' ';
1347 	    if ( friendlies[k].tag!=0 )
1348 		strncpy(ubuf+7, (char *) friendlies[k].friendlyname,end-ubuf-7);
1349 	    else if ( onlyifknown )
1350 return( NULL );
1351 	    else
1352 		ubuf[7]='\0';
1353 	}
1354 return( copy( ubuf ));
1355 }
1356 
1357 
NameOTLookup(OTLookup * otl,SplineFont * sf)1358 void NameOTLookup(OTLookup *otl,SplineFont *sf) {
1359     char *userfriendly = NULL, *script;
1360     FeatureScriptLangList *fl;
1361     char *lookuptype;
1362     char *format;
1363     struct lookup_subtable *subtable;
1364     int k;
1365     (void)sf;
1366 
1367     LookupInit();
1368 
1369     if ( otl->lookup_name==NULL ) {
1370 	for ( k=0; k<2; ++k ) {
1371 #ifndef LUA_FF_LIB
1372 	    for ( fl=otl->features; fl!=NULL ; fl=fl->next ) {
1373 		/* look first for a feature attached to a default language */
1374 		if ( k==1 || DefaultLangTagInScriptList(fl->scripts,false)!=NULL ) {
1375 		    userfriendly = TagFullName(sf,fl->featuretag, true);
1376 		    if ( userfriendly!=NULL )
1377 	    break;
1378 		}
1379 	    }
1380 	    if ( userfriendly!=NULL )
1381 	break;
1382 #endif
1383 	}
1384 	if ( userfriendly==NULL ) {
1385 	    if ( (otl->lookup_type>>8)<2 && (otl->lookup_type&0xff)<10 )
1386 		lookuptype = _(lookup_type_names[otl->lookup_type>>8][otl->lookup_type&0xff]);
1387 	    else
1388 #ifdef LUA_FF_LIB
1389         lookuptype = "un";
1390 #else
1391 		lookuptype = _("Unknown");
1392 #endif
1393 	    for ( fl=otl->features; fl!=NULL; fl=fl->next );
1394 	    if ( fl==NULL )
1395 		userfriendly = copy(lookuptype);
1396 	    else {
1397 		userfriendly = galloc( strlen(lookuptype) + 40);
1398 #ifdef LUA_FF_LIB
1399         if ( (otl->lookup_type&0xff)>= 0xf0 ) {
1400           sprintf( userfriendly, "%s_<%d,%d>", lookuptype,
1401                  (fl->featuretag>>16), (fl->featuretag&0xffff));
1402         } else {
1403           sprintf( userfriendly, "%s_%c%c%c%c", lookuptype,
1404 		    fl->featuretag>>24,
1405 		    fl->featuretag>>16,
1406 		    fl->featuretag>>8 ,
1407 		    fl->featuretag );
1408         }
1409 #else
1410 		sprintf( userfriendly, "%s '%c%c%c%c'", lookuptype,
1411 		    fl->featuretag>>24,
1412 		    fl->featuretag>>16,
1413 		    fl->featuretag>>8 ,
1414 		    fl->featuretag );
1415 #endif
1416 	    }
1417 	}
1418 	script = NULL;
1419 	if ( fl==NULL ) fl = otl->features;
1420 	if ( fl!=NULL && fl->scripts!=NULL ) {
1421 	    char buf[8];
1422 	    int j;
1423 	    struct scriptlanglist *sl, *found, *found2;
1424 	    uint32 script_tag = fl->scripts->script;
1425 	    found = found2 = NULL;
1426 	    for ( sl = fl->scripts; sl!=NULL; sl=sl->next ) {
1427 		if ( sl->script == DEFAULT_SCRIPT )
1428 		    /* Ignore it */;
1429 		else if ( DefaultLangTagInOneScriptList(sl)) {
1430 		    if ( found==NULL )
1431 			found = sl;
1432 		    else {
1433 			found = found2 = NULL;
1434 	    break;
1435 		    }
1436 		} else if ( found2 == NULL )
1437 		    found2 = sl;
1438 		else
1439 		    found2 = (struct scriptlanglist *) -1;
1440 	    }
1441 	    if ( found==NULL && found2!=NULL && found2 != (struct scriptlanglist *) -1 )
1442 		found = found2;
1443 	    if ( found!=NULL ) {
1444 		script_tag = found->script;
1445 		for ( j=0; localscripts[j].text!=NULL && script_tag!=localscripts[j].tag; ++j );
1446 #ifdef LUA_FF_LIB
1447             buf[0] = fl->scripts->script>>24;
1448             buf[1] = (fl->scripts->script>>16)&0xff;
1449             buf[2] = (fl->scripts->script>>8)&0xff;
1450             buf[3] = fl->scripts->script&0xff;
1451             buf[4] = 0;
1452 		    script = copy(buf);
1453 #else
1454 		if ( localscripts[j].text!=NULL )
1455 		    script = copy( _((char *) localscripts[j].text) );
1456 		else {
1457 		    buf[0] = '\'';
1458 		    buf[1] = fl->scripts->script>>24;
1459 		    buf[2] = (fl->scripts->script>>16)&0xff;
1460 		    buf[3] = (fl->scripts->script>>8)&0xff;
1461 		    buf[4] = fl->scripts->script&0xff;
1462 		    buf[5] = '\'';
1463 		    buf[6] = 0;
1464 		    script = copy(buf);
1465 		}
1466 #endif
1467 	    }
1468 	}
1469 	if ( script!=NULL ) {
1470 /* GT: This string is used to generate a name for each OpenType lookup. */
1471 /* GT: The %s will be filled with the user friendly name of the feature used to invoke the lookup */
1472 /* GT: The second %s (if present) is the script */
1473 /* GT: While the %d is the index into the lookup list and is used to disambiguate it */
1474 /* GT: In case that is needed */
1475 #ifdef LUA_FF_LIB
1476         format = "%s_%s_l_%d";
1477 #else
1478 	    format = _("%s in %s lookup %d");
1479 #endif
1480 	    otl->lookup_name = galloc( strlen(userfriendly)+strlen(format)+strlen(script)+10 );
1481 	    sprintf( otl->lookup_name, format, userfriendly, script, otl->lookup_index );
1482 	} else {
1483 #ifdef LUA_FF_LIB
1484         format = "%s_l_%d";
1485 #else
1486 	    format = _("%s lookup %d");
1487 #endif
1488 	    otl->lookup_name = galloc( strlen(userfriendly)+strlen(format)+10 );
1489 	    sprintf( otl->lookup_name, format, userfriendly, otl->lookup_index );
1490 	}
1491 	free(script);
1492 	free(userfriendly);
1493     }
1494 
1495     if ( otl->subtables==NULL )
1496 	/* IError( _("Lookup with no subtables"))*/;
1497     else {
1498 	int cnt = 0;
1499 	for ( subtable = otl->subtables; subtable!=NULL; subtable=subtable->next, ++cnt )
1500 		if ( subtable->subtable_name==NULL ) {
1501 	    if ( subtable==otl->subtables && subtable->next==NULL )
1502 /* GT: This string is used to generate a name for an OpenType lookup subtable. */
1503 /* GT:  %s is the lookup name */
1504 #ifdef LUA_FF_LIB
1505         format = "%s_s";
1506 #else
1507 		format = _("%s subtable");
1508 #endif
1509 	    else if ( subtable->per_glyph_pst_or_kern )
1510 /* GT: This string is used to generate a name for an OpenType lookup subtable. */
1511 /* GT:  %s is the lookup name, %d is the index of the subtable in the lookup */
1512 #ifdef LUA_FF_LIB
1513         format = "%s_g_%d";
1514 #else
1515 		format = _("%s per glyph data %d");
1516 #endif
1517 	    else if ( subtable->kc!=NULL )
1518 #ifdef LUA_FF_LIB
1519         format = "%s_k_%d";
1520 #else
1521 		format = _("%s kerning class %d");
1522 #endif
1523 	    else if ( subtable->fpst!=NULL )
1524 #ifdef LUA_FF_LIB
1525         format = "%s_c_%d";
1526 #else
1527 		format = _("%s contextual %d");
1528 #endif
1529 	    else if ( subtable->anchor_classes )
1530 #ifdef LUA_FF_LIB
1531         format = "%s_a_%d";
1532 #else
1533 		format = _("%s anchor %d");
1534 #endif
1535 	    else {
1536 		IError("Subtable status not filled in for %dth subtable of %s", cnt, otl->lookup_name );
1537 		format = "%s !!!!!!!! %d";
1538 	    }
1539 	    subtable->subtable_name = galloc( strlen(otl->lookup_name)+strlen(format)+10 );
1540 	    sprintf( subtable->subtable_name, format, otl->lookup_name, cnt );
1541 	}
1542     }
1543     if ( otl->lookup_type==gsub_ligature ) {
1544 	for ( fl=otl->features; fl!=NULL; fl=fl->next )
1545 	    if ( fl->featuretag==CHR('l','i','g','a') || fl->featuretag==CHR('r','l','i','g'))
1546 		otl->store_in_afm = true;
1547     }
1548 
1549     if ( otl->lookup_type==gsub_single )
1550 	for ( subtable = otl->subtables; subtable!=NULL; subtable=subtable->next )
1551 	    subtable->suffix = SuffixFromTags(otl->features);
1552 }
1553 
LangOrder(struct scriptlanglist * sl)1554 static void LangOrder(struct scriptlanglist *sl) {
1555     int i,j;
1556     uint32 lang, lang2;
1557 
1558     for ( i=0; i<sl->lang_cnt; ++i ) {
1559 	lang = i<MAX_LANG ? sl->langs[i] : sl->morelangs[i-MAX_LANG];
1560 	for ( j=i+1; j<sl->lang_cnt; ++j ) {
1561 	    lang2 = j<MAX_LANG ? sl->langs[j] : sl->morelangs[j-MAX_LANG];
1562 	    if ( lang>lang2 ) {
1563 		if ( i<MAX_LANG )
1564 		    sl->langs[i] = lang2;
1565 		else
1566 		    sl->morelangs[i-MAX_LANG] = lang2;
1567 		if ( j<MAX_LANG )
1568 		    sl->langs[j] = lang;
1569 		else
1570 		    sl->morelangs[j-MAX_LANG] = lang;
1571 		lang = lang2;
1572 	    }
1573 	}
1574     }
1575 }
1576 
SLOrder(struct scriptlanglist * sl)1577 static struct scriptlanglist *SLOrder(struct scriptlanglist *sl) {
1578     int i,j, cnt;
1579     struct scriptlanglist *sl2, *space[30], **allocked=NULL, **test = space;
1580 
1581     for ( sl2=sl, cnt=0; sl2!=NULL; sl2=sl2->next, ++cnt )
1582 	LangOrder(sl2);
1583     if ( cnt<=1 )
1584 return( sl );
1585     if ( cnt>30 )
1586 	test = allocked = galloc(cnt*sizeof(struct scriptlanglist *));
1587     for ( sl2=sl, cnt=0; sl2!=NULL; sl2=sl2->next, ++cnt )
1588 	test[cnt] = sl2;
1589     for ( i=0; i<cnt; ++i ) for ( j=i+1; j<cnt; ++j ) {
1590 	if ( test[i]->script > test[j]->script ) {
1591 	    struct scriptlanglist *temp;
1592 	    temp = test[i];
1593 	    test[i] = test[j];
1594 	    test[j] = temp;
1595 	}
1596     }
1597     sl = test[0];
1598     for ( i=1; i<cnt; ++i )
1599 	test[i-1]->next = test[i];
1600     test[i-1]->next = NULL;
1601     free( allocked );
1602 return( sl );
1603 }
1604 
FLOrder(FeatureScriptLangList * fl)1605 FeatureScriptLangList *FLOrder(FeatureScriptLangList *fl) {
1606     int i,j, cnt;
1607     FeatureScriptLangList *fl2, *space[30], **allocked=NULL, **test = space;
1608 
1609     for ( fl2=fl, cnt=0; fl2!=NULL; fl2=fl2->next, ++cnt )
1610 	fl2->scripts = SLOrder(fl2->scripts);
1611     if ( cnt<=1 )
1612 return( fl );
1613     if ( cnt>30 )
1614 	test = allocked = galloc(cnt*sizeof(FeatureScriptLangList *));
1615     for ( fl2=fl, cnt=0; fl2!=NULL; fl2=fl2->next, ++cnt )
1616 	test[cnt] = fl2;
1617     for ( i=0; i<cnt; ++i ) for ( j=i+1; j<cnt; ++j ) {
1618 	if ( test[i]->featuretag > test[j]->featuretag ) {
1619 	    FeatureScriptLangList *temp;
1620 	    temp = test[i];
1621 	    test[i] = test[j];
1622 	    test[j] = temp;
1623 	}
1624     }
1625     fl = test[0];
1626     for ( i=1; i<cnt; ++i )
1627 	test[i-1]->next = test[i];
1628     test[i-1]->next = NULL;
1629     free( allocked );
1630 return( fl );
1631 }
1632 
SLCopy(struct scriptlanglist * sl)1633 struct scriptlanglist *SLCopy(struct scriptlanglist *sl) {
1634     struct scriptlanglist *newsl;
1635 
1636     newsl = chunkalloc(sizeof(struct scriptlanglist));
1637     *newsl = *sl;
1638     newsl->next = NULL;
1639 
1640     if ( sl->lang_cnt>MAX_LANG ) {
1641 	newsl->morelangs = galloc((newsl->lang_cnt-MAX_LANG)*sizeof(uint32));
1642 	memcpy(newsl->morelangs,sl->morelangs,(newsl->lang_cnt-MAX_LANG)*sizeof(uint32));
1643     }
1644 return( newsl );
1645 }
1646 
SListCopy(struct scriptlanglist * sl)1647 struct scriptlanglist *SListCopy(struct scriptlanglist *sl) {
1648     struct scriptlanglist *head=NULL, *last=NULL, *cur;
1649 
1650     for ( ; sl!=NULL; sl=sl->next ) {
1651 	cur = SLCopy(sl);
1652 	if ( head==NULL )
1653 	    head = cur;
1654 	else
1655 	    last->next = cur;
1656 	last = cur;
1657     }
1658 return( head );
1659 }
1660 
FeatureListCopy(FeatureScriptLangList * fl)1661 FeatureScriptLangList *FeatureListCopy(FeatureScriptLangList *fl) {
1662     FeatureScriptLangList *newfl;
1663 
1664     if ( fl==NULL )
1665 return( NULL );
1666 
1667     newfl = chunkalloc(sizeof(FeatureScriptLangList));
1668     *newfl = *fl;
1669     newfl->next = NULL;
1670 
1671     newfl->scripts = SListCopy(fl->scripts);
1672 return( newfl );
1673 }
1674 
LangMerge(struct scriptlanglist * into,struct scriptlanglist * from)1675 static void LangMerge(struct scriptlanglist *into, struct scriptlanglist *from) {
1676     int i,j;
1677     uint32 flang, tlang;
1678 
1679     for ( i=0 ; i<from->lang_cnt; ++i ) {
1680 	flang = i<MAX_LANG ? from->langs[i] : from->morelangs[i-MAX_LANG];
1681 	for ( j=0; j<into->lang_cnt; ++j ) {
1682 	    tlang = j<MAX_LANG ? into->langs[j] : into->morelangs[j-MAX_LANG];
1683 	    if ( tlang==flang )
1684 	break;
1685 	}
1686 	if ( j==into->lang_cnt ) {
1687 	    if ( into->lang_cnt<MAX_LANG )
1688 		into->langs[into->lang_cnt++] = flang;
1689 	    else {
1690 		into->morelangs = grealloc(into->morelangs,(into->lang_cnt+1-MAX_LANG)*sizeof(uint32));
1691 		into->morelangs[into->lang_cnt++-MAX_LANG] = flang;
1692 	    }
1693 	}
1694     }
1695 }
1696 
SLMerge(FeatureScriptLangList * into,struct scriptlanglist * fsl)1697 void SLMerge(FeatureScriptLangList *into, struct scriptlanglist *fsl) {
1698     struct scriptlanglist *isl;
1699 
1700     for ( ; fsl!=NULL; fsl = fsl->next ) {
1701 	for ( isl=into->scripts; isl!=NULL; isl=isl->next ) {
1702 	    if ( fsl->script==isl->script )
1703 	break;
1704 	}
1705 	if ( isl!=NULL )
1706 	    LangMerge(isl,fsl);
1707 	else {
1708 	    isl = SLCopy(fsl);
1709 	    isl->next = into->scripts;
1710 	    into->scripts = isl;
1711 	}
1712     }
1713 }
1714 
FLMerge(OTLookup * into,OTLookup * from)1715 void FLMerge(OTLookup *into, OTLookup *from) {
1716     /* Merge the feature list from "from" into "into" */
1717     FeatureScriptLangList *ifl, *ffl;
1718 
1719     /* first check for common featuretags and merge the scripts of each */
1720     for ( ffl = from->features; ffl!=NULL; ffl = ffl->next ) {
1721 	for ( ifl=into->features; ifl!=NULL; ifl=ifl->next ) {
1722 	    if ( ffl->featuretag==ifl->featuretag )
1723 	break;
1724 	}
1725 	if ( ifl!=NULL )
1726 	    SLMerge(ffl,ifl->scripts);
1727 	else {
1728 	    ifl = FeatureListCopy(ffl);
1729 	    ifl->next = into->features;
1730 	    into->features = ifl;
1731 	}
1732     }
1733     into->features = FLOrder(into->features);
1734 }
1735 
SFSubTablesMerge(SplineFont * _sf,struct lookup_subtable * subfirst,struct lookup_subtable * subsecond)1736 void SFSubTablesMerge(SplineFont *_sf,struct lookup_subtable *subfirst,
1737 	struct lookup_subtable *subsecond) {
1738     unsigned lookup_type = subfirst->lookup->lookup_type;
1739     int gid,k,isv;
1740     SplineChar *sc;
1741     SplineFont *sf = _sf;
1742     PST *pst, *fpst, *spst, *pstprev, *pstnext;
1743     KernPair *fkp, *skp, *kpprev, *kpnext;
1744     AnchorClass *ac;
1745 
1746     if ( lookup_type != subsecond->lookup->lookup_type ) {
1747 	IError("Attempt to merge lookup subtables with mismatch types");
1748 return;
1749     }
1750     if ( lookup_type != gsub_single &&
1751 	    lookup_type != gsub_multiple &&
1752 	    lookup_type != gsub_alternate &&
1753 	    lookup_type != gsub_ligature &&
1754 	    lookup_type != gpos_single &&
1755 	    lookup_type != gpos_pair &&
1756 	    lookup_type != gpos_cursive &&
1757 	    lookup_type != gpos_mark2base &&
1758 	    lookup_type != gpos_mark2ligature &&
1759 	    lookup_type != gpos_mark2mark ) {
1760 	IError("Attempt to merge lookup subtables with bad types");
1761 return;
1762     } else if ( subfirst->kc!=NULL || subsecond->kc != NULL ) {
1763 	IError("Attempt to merge lookup subtables with kerning classes");
1764 return;
1765     }
1766 
1767     if ( lookup_type==gpos_cursive || lookup_type==gpos_mark2base ||
1768 	    lookup_type==gpos_mark2ligature || lookup_type==gpos_mark2mark ) {
1769 	for ( ac = sf->anchor; ac!=NULL ; ac=ac->next )
1770 	    if ( ac->subtable == subsecond )
1771 		ac->subtable = subfirst;
1772     } else {
1773 	k=0;
1774 	do {
1775 	    sf = _sf->subfontcnt==0 ? _sf : _sf->subfonts[k];
1776 	    for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
1777 		if ( lookup_type==gsub_single || lookup_type==gsub_multiple ||
1778 			lookup_type==gsub_alternate || lookup_type==gpos_single ) {
1779 		    fpst = spst = NULL;
1780 		    for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
1781 			if ( pst->subtable == subfirst ) {
1782 			    fpst = pst;
1783 			    if ( spst!=NULL )
1784 		    break;
1785 			} else if ( pst->subtable == subsecond ) {
1786 			    spst = pst;
1787 			    if ( fpst!=NULL )
1788 		    break;
1789 			}
1790 		    }
1791 		    if ( fpst==NULL && spst!=NULL )
1792 			spst->subtable = subfirst;
1793 		    else if ( spst!=NULL ) {
1794 			LogError(_("The glyph, %s, contains a %s from %s and one from %s.\nThe one from %s will be removed.\n"),
1795 				sc->name,
1796 			        lookup_type==gpos_single ? _("positioning") : _("substitution"),
1797 			        subfirst->subtable_name, subsecond->subtable_name,
1798 			        subsecond->subtable_name );
1799 			pstprev = NULL;
1800 			for ( pst=sc->possub; pst!=NULL && pst!=spst; pstprev=pst, pst=pst->next );
1801 			if ( pstprev==NULL )
1802 			    sc->possub = spst->next;
1803 			else
1804 			    pstprev = spst->next;
1805 			spst->next = NULL;
1806 			PSTFree(spst);
1807 		    }
1808 		} else if ( lookup_type==gsub_ligature || lookup_type==gpos_pair ) {
1809 		    pstprev = NULL;
1810 		    for ( spst=sc->possub; spst!=NULL ; spst = pstnext ) {
1811 			pstnext = spst->next;
1812 			if ( spst->subtable == subsecond ) {
1813 			    for ( fpst=sc->possub; fpst!=NULL; fpst=fpst->next ) {
1814 				if ( fpst->subtable == subfirst &&
1815 					strcmp(fpst->u.lig.components,spst->u.lig.components)==0 )
1816 			    break;
1817 			    }
1818 			    if ( fpst==NULL )
1819 				spst->subtable = subfirst;
1820 			    else {
1821 				LogError(_("The glyph, %s, contains the same %s from %s and from %s.\nThe one from %s will be removed.\n"),
1822 					sc->name,
1823 			                lookup_type==gsub_ligature ? _("ligature") : _("kern pair"),
1824 					subfirst->subtable_name, subsecond->subtable_name,
1825 					subsecond->subtable_name );
1826 				if ( pstprev==NULL )
1827 				    sc->possub = pstnext;
1828 				else
1829 				    pstprev->next = pstnext;
1830 				spst->next = NULL;
1831 			        PSTFree(spst);
1832 			        spst = pstprev;
1833 			    }
1834 			}
1835 			pstprev = spst;
1836 		    }
1837 		    for ( isv=0; isv<2; ++isv ) {
1838 			kpprev = NULL;
1839 			for ( skp=isv ? sc->vkerns : sc->kerns; skp!=NULL ; skp = kpnext ) {
1840 			    kpnext = skp->next;
1841 			    if ( skp->subtable == subsecond ) {
1842 				for ( fkp=isv ? sc->vkerns : sc->kerns; fkp!=NULL; fkp=fkp->next ) {
1843 				    if ( fkp->subtable == subfirst && fkp->sc==skp->sc )
1844 				break;
1845 				}
1846 				if ( fkp==NULL )
1847 				    skp->subtable = subfirst;
1848 				else {
1849 				    LogError(_("The glyph, %s, contains the same kern pair from %s and from %s.\nThe one from %s will be removed.\n"),
1850 					    sc->name,
1851 					    subfirst->subtable_name, subsecond->subtable_name,
1852 					    subsecond->subtable_name );
1853 				    if ( kpprev!=NULL )
1854 					kpprev->next = kpnext;
1855 				    else if ( isv )
1856 					sc->vkerns = kpnext;
1857 				    else
1858 					sc->kerns = kpnext;
1859 				    skp->next = NULL;
1860 				    KernPairsFree(skp);
1861 				    skp = kpprev;
1862 				}
1863 			    }
1864 			    kpprev = skp;
1865 			}
1866 		    }
1867 		}
1868 	    }
1869 	    ++k;
1870 	} while ( k<_sf->subfontcnt );
1871     }
1872 }
1873 
1874 /* ************************************************************************** */
1875 /* ******************************* copy lookup ****************************** */
1876 /* ************************************************************************** */
1877 
ClassCopy(int class_cnt,char ** classes)1878 static char **ClassCopy(int class_cnt,char **classes) {
1879     char **newclasses;
1880     int i;
1881 
1882     if ( classes==NULL || class_cnt==0 )
1883 return( NULL );
1884     newclasses = galloc(class_cnt*sizeof(char *));
1885     for ( i=0; i<class_cnt; ++i )
1886 	newclasses[i] = copy(classes[i]);
1887 return( newclasses );
1888 }
1889 
1890 static OTLookup *_OTLookupCopyInto(struct sfmergecontext *mc,
1891 	OTLookup *from_otl, OTLookup *before, int do_contents);
OTLookupCopyNested(struct sfmergecontext * mc,OTLookup * from_otl)1892 static OTLookup *OTLookupCopyNested(struct sfmergecontext *mc,
1893 	OTLookup *from_otl) {
1894     char *newname;
1895     OTLookup *to_nested_otl;
1896     int l;
1897 
1898     if ( from_otl==NULL )
1899 return( NULL );
1900 
1901     for ( l=0; l<mc->lcnt; ++l ) {
1902 	if ( mc->lks[l].from == from_otl )
1903 return( mc->lks[l].to );
1904     }
1905 
1906     newname = strconcat(mc->prefix,from_otl->lookup_name);
1907     to_nested_otl = SFFindLookup(mc->sf_to,newname);
1908     free(newname);
1909     if ( to_nested_otl==NULL )
1910 	to_nested_otl = _OTLookupCopyInto(mc, from_otl, (OTLookup *) -1, true );
1911 return( to_nested_otl );
1912 }
1913 
SF_AddKernClass(struct sfmergecontext * mc,KernClass * kc,struct lookup_subtable * sub)1914 static KernClass *SF_AddKernClass(struct sfmergecontext *mc,KernClass *kc,
1915 	struct lookup_subtable *sub ) {
1916     KernClass *newkc;
1917 
1918     newkc = chunkalloc(sizeof(KernClass));
1919     *newkc = *kc;
1920     newkc->subtable = sub;
1921     if ( sub->vertical_kerning ) {
1922 	newkc->next = mc->sf_to->vkerns;
1923 	mc->sf_to->vkerns = newkc;
1924     } else {
1925 	newkc->next = mc->sf_to->kerns;
1926 	mc->sf_to->kerns = newkc;
1927     }
1928 
1929     newkc->firsts = ClassCopy(newkc->first_cnt,newkc->firsts);
1930     newkc->seconds = ClassCopy(newkc->second_cnt,newkc->seconds);
1931     newkc->offsets = galloc(newkc->first_cnt*newkc->second_cnt*sizeof(int16));
1932     memcpy(newkc->offsets,kc->offsets,newkc->first_cnt*newkc->second_cnt*sizeof(int16));
1933 return( newkc );
1934 }
1935 
SF_AddFPST(struct sfmergecontext * mc,FPST * fpst,struct lookup_subtable * sub)1936 static FPST *SF_AddFPST(struct sfmergecontext *mc,FPST *fpst,
1937 	struct lookup_subtable *sub ) {
1938     FPST *newfpst;
1939     int i, k, cur;
1940 
1941     newfpst = chunkalloc(sizeof(FPST));
1942     *newfpst = *fpst;
1943     newfpst->subtable = sub;
1944     newfpst->next = mc->sf_to->possub;
1945     mc->sf_to->possub = newfpst;
1946 
1947     newfpst->nclass = ClassCopy(newfpst->nccnt,newfpst->nclass);
1948     newfpst->bclass = ClassCopy(newfpst->bccnt,newfpst->bclass);
1949     newfpst->fclass = ClassCopy(newfpst->fccnt,newfpst->fclass);
1950 
1951     newfpst->rules = galloc(newfpst->rule_cnt*sizeof(struct fpst_rule));
1952     memcpy(newfpst->rules,fpst->rules,newfpst->rule_cnt*sizeof(struct fpst_rule));
1953 
1954     cur = 0;
1955     for ( i=0; i<newfpst->rule_cnt; ++i ) {
1956 	struct fpst_rule *r = &newfpst->rules[i], *oldr = &fpst->rules[i];
1957 
1958 	r->lookups = galloc(r->lookup_cnt*sizeof(struct seqlookup));
1959 	memcpy(r->lookups,oldr->lookups,r->lookup_cnt*sizeof(struct seqlookup));
1960 	for ( k=0; k<r->lookup_cnt; ++k ) {
1961 	    r->lookups[k].lookup = OTLookupCopyNested(mc,
1962 		r->lookups[k].lookup);
1963 	}
1964 
1965 	switch ( newfpst->format ) {
1966 	  case pst_glyphs:
1967 	    r->u.glyph.names = copy( r->u.glyph.names );
1968 	    r->u.glyph.back = copy( r->u.glyph.back );
1969 	    r->u.glyph.fore = copy( r->u.glyph.fore );
1970 	  break;
1971 	  case pst_class:
1972 	    r->u.class.nclasses = galloc( r->u.class.ncnt*sizeof(uint16));
1973 	    memcpy(r->u.class.nclasses,oldr->u.class.nclasses, r->u.class.ncnt*sizeof(uint16));
1974 	    r->u.class.bclasses = galloc( r->u.class.bcnt*sizeof(uint16));
1975 	    memcpy(r->u.class.bclasses,oldr->u.class.bclasses, r->u.class.ncnt*sizeof(uint16));
1976 	    r->u.class.fclasses = galloc( r->u.class.fcnt*sizeof(uint16));
1977 	    memcpy(r->u.class.fclasses,oldr->u.class.fclasses, r->u.class.fcnt*sizeof(uint16));
1978 	  break;
1979 	  case pst_coverage:
1980 	    r->u.coverage.ncovers = ClassCopy( r->u.coverage.ncnt, r->u.coverage.ncovers );
1981 	    r->u.coverage.bcovers = ClassCopy( r->u.coverage.bcnt, r->u.coverage.bcovers );
1982 	    r->u.coverage.fcovers = ClassCopy( r->u.coverage.fcnt, r->u.coverage.fcovers );
1983 	  break;
1984 	  case pst_reversecoverage:
1985 	    r->u.rcoverage.ncovers = ClassCopy( r->u.rcoverage.always1, r->u.rcoverage.ncovers );
1986 	    r->u.rcoverage.bcovers = ClassCopy( r->u.rcoverage.bcnt, r->u.rcoverage.bcovers );
1987 	    r->u.rcoverage.fcovers = ClassCopy( r->u.rcoverage.fcnt, r->u.rcoverage.fcovers );
1988 	    r->u.rcoverage.replacements = copy( r->u.rcoverage.replacements );
1989 	  break;
1990 	}
1991     }
1992 return( newfpst );
1993 }
1994 
SCFindOrMake(SplineFont * into,SplineChar * fromsc)1995 static SplineChar *SCFindOrMake(SplineFont *into,SplineChar *fromsc) {
1996     int to_index;
1997 
1998     if ( into->cidmaster==NULL && into->fv!=NULL ) {
1999 	to_index = SFFindSlot(into,into->fv->map,fromsc->unicodeenc,fromsc->name);
2000 	if ( to_index==-1 )
2001 return( NULL );
2002 return( SFMakeChar(into,into->fv->map,to_index));
2003     }
2004 return( SFGetChar(into,fromsc->unicodeenc,fromsc->name));
2005 }
2006 
SF_SCAddAP(SplineChar * tosc,AnchorPoint * ap,AnchorClass * newac)2007 static void SF_SCAddAP(SplineChar *tosc,AnchorPoint *ap, AnchorClass *newac) {
2008     AnchorPoint *newap;
2009 
2010     newap = chunkalloc(sizeof(AnchorPoint));
2011     *newap = *ap;
2012     newap->anchor = newac;
2013     newap->next = tosc->anchor;
2014     tosc->anchor = newap;
2015 }
2016 
SF_AddAnchorClasses(struct sfmergecontext * mc,struct lookup_subtable * from_sub,struct lookup_subtable * sub)2017 static void SF_AddAnchorClasses(struct sfmergecontext *mc,
2018 	struct lookup_subtable *from_sub, struct lookup_subtable *sub ) {
2019     AnchorClass *ac, *nac;
2020     int k, gid;
2021     SplineFont *fsf;
2022     AnchorPoint *ap;
2023     SplineChar *fsc, *tsc;
2024 
2025     for ( ac=mc->sf_from->anchor; ac!=NULL; ac=ac->next ) if ( ac->subtable==from_sub ) {
2026 	nac = chunkalloc(sizeof(AnchorClass));
2027 	*nac = *ac;
2028 	nac->subtable = sub;
2029 	nac->name = strconcat(mc->prefix,nac->name);
2030 	nac->next = mc->sf_to->anchor;
2031 	mc->sf_to->anchor = nac;
2032 
2033 	k=0;
2034 	do {
2035 	    fsf = mc->sf_from->subfontcnt==0 ? mc->sf_from : mc->sf_from->subfonts[k];
2036 	    for ( gid = 0; gid<fsf->glyphcnt; ++gid ) if ( (fsc = fsf->glyphs[gid])!=NULL ) {
2037 		for ( ap=fsc->anchor; ap!=NULL; ap=ap->next ) {
2038 		    if ( ap->anchor==ac ) {
2039 			tsc = SCFindOrMake(mc->sf_to,fsc);
2040 			if ( tsc==NULL )
2041 		break;
2042 			SF_SCAddAP(tsc,ap,nac);
2043 		    }
2044 		}
2045 	    }
2046 	    ++k;
2047 	} while ( k<mc->sf_from->subfontcnt );
2048     }
2049 }
2050 
SF_SCAddPST(SplineChar * tosc,PST * pst,struct lookup_subtable * sub)2051 static int SF_SCAddPST(SplineChar *tosc,PST *pst,struct lookup_subtable *sub) {
2052     PST *newpst;
2053 
2054     newpst = chunkalloc(sizeof(PST));
2055     *newpst = *pst;
2056     newpst->subtable = sub;
2057     newpst->next = tosc->possub;
2058     tosc->possub = newpst;
2059 
2060     switch( newpst->type ) {
2061       case pst_pair:
2062 	newpst->u.pair.paired = copy(pst->u.pair.paired);
2063 	newpst->u.pair.vr = chunkalloc(sizeof(struct vr [2]));
2064 	memcpy(newpst->u.pair.vr,pst->u.pair.vr,sizeof(struct vr [2]));
2065       break;
2066       case pst_ligature:
2067 	newpst->u.lig.lig = tosc;
2068 	/* Fall through */
2069       case pst_substitution:
2070       case pst_alternate:
2071       case pst_multiple:
2072 	newpst->u.subs.variant = copy(pst->u.subs.variant);
2073       break;
2074     }
2075 return( true );
2076 }
2077 
SF_SCAddKP(SplineChar * tosc,KernPair * kp,struct lookup_subtable * sub,int isvkern,SplineFont * to_sf)2078 static int SF_SCAddKP(SplineChar *tosc,KernPair *kp,struct lookup_subtable *sub,
2079 	int isvkern, SplineFont *to_sf ) {
2080     SplineChar *tosecond;
2081     KernPair *newkp;
2082 
2083     tosecond = SFGetChar(to_sf,kp->sc->unicodeenc,kp->sc->name);
2084     if ( tosecond==NULL )
2085 return( false );
2086 
2087     newkp = chunkalloc(sizeof(KernPair));
2088     *newkp = *kp;
2089     newkp->subtable = sub;
2090     newkp->sc = tosecond;
2091     if ( isvkern ) {
2092 	newkp->next = tosc->vkerns;
2093 	tosc->vkerns = newkp;
2094     } else {
2095 	newkp->next = tosc->kerns;
2096 	tosc->kerns = newkp;
2097     }
2098 return(true);
2099 }
2100 
SF_AddPSTKern(struct sfmergecontext * mc,struct lookup_subtable * from_sub,struct lookup_subtable * sub)2101 static void SF_AddPSTKern(struct sfmergecontext *mc,struct lookup_subtable *from_sub, struct lookup_subtable *sub) {
2102     int k, gid, isv;
2103     SplineFont *fsf;
2104     SplineChar *fsc, *tsc;
2105     PST *pst;
2106     KernPair *kp;
2107     int iskern = sub->lookup->lookup_type==gpos_pair;
2108 
2109     k=0;
2110     do {
2111 	fsf = mc->sf_from->subfontcnt==0 ? mc->sf_from : mc->sf_from->subfonts[k];
2112 	for ( gid = 0; gid<fsf->glyphcnt; ++gid ) if ( (fsc = fsf->glyphs[gid])!=NULL ) {
2113 	    tsc = (SplineChar *) -1;
2114 	    for ( pst = fsc->possub; pst!=NULL; pst=pst->next ) {
2115 		if ( pst->subtable==from_sub ) {
2116 		    if ( tsc==(SplineChar *) -1 ) {
2117 			tsc = SCFindOrMake(mc->sf_to,fsc);
2118 			if ( tsc==NULL )
2119 	    break;
2120 		    }
2121 		    SF_SCAddPST(tsc,pst,sub);
2122 		}
2123 	    }
2124 	    if ( tsc!=NULL && iskern ) {
2125 		for ( isv=0; isv<2 && tsc!=NULL; ++isv ) {
2126 		    for ( kp= isv ? fsc->vkerns : fsc->kerns; kp!=NULL; kp=kp->next ) {
2127 			if ( kp->subtable==sub ) {
2128 			    /* Kerning data tend to be individualistic. Only copy if */
2129 			    /*  glyphs exist */
2130 			    if ( tsc==(SplineChar *) -1 ) {
2131 				tsc = SFGetChar(mc->sf_to,fsc->unicodeenc,fsc->name);
2132 				if ( tsc==NULL )
2133 		    break;
2134 			    }
2135 			    SF_SCAddKP(tsc,kp,sub,isv,mc->sf_to);
2136 			}
2137 		    }
2138 		}
2139 	    }
2140 	}
2141 	++k;
2142     } while ( k<mc->sf_from->subfontcnt );
2143 }
2144 
_FeatureOrderId(int isgpos,uint32 tag)2145 int _FeatureOrderId( int isgpos,uint32 tag ) {
2146     /* This is the order in which features should be executed */
2147 
2148     if ( !isgpos ) switch ( tag ) {
2149 /* GSUB ordering */
2150       case CHR('c','c','m','p'):	/* Must be first? */
2151 return( -2 );
2152       case CHR('l','o','c','l'):	/* Language dependent letter forms (serbian uses some different glyphs than russian) */
2153 return( -1 );
2154       case CHR('i','s','o','l'):
2155 return( 0 );
2156       case CHR('j','a','l','t'):		/* must come after 'isol' */
2157 return( 1 );
2158       case CHR('f','i','n','a'):
2159 return( 2 );
2160       case CHR('f','i','n','2'):
2161       case CHR('f','a','l','t'):		/* must come after 'fina' */
2162 return( 3 );
2163       case CHR('f','i','n','3'):
2164 return( 4 );
2165       case CHR('m','e','d','i'):
2166 return( 5 );
2167       case CHR('m','e','d','2'):
2168 return( 6 );
2169       case CHR('i','n','i','t'):
2170 return( 7 );
2171 
2172       case CHR('r','t','l','a'):
2173 return( 100 );
2174       case CHR('s','m','c','p'): case CHR('c','2','s','c'):
2175 return( 200 );
2176 
2177       case CHR('r','l','i','g'):
2178 return( 300 );
2179       case CHR('c','a','l','t'):
2180 return( 301 );
2181       case CHR('l','i','g','a'):
2182 return( 302 );
2183       case CHR('d','l','i','g'): case CHR('h','l','i','g'):
2184 return( 303 );
2185       case CHR('c','s','w','h'):
2186 return( 304 );
2187       case CHR('m','s','e','t'):
2188 return( 305 );
2189 
2190       case CHR('f','r','a','c'):
2191 return( 306 );
2192 
2193 /* Indic processing */
2194       case CHR('n','u','k','t'):
2195       case CHR('p','r','e','f'):
2196 return( 301 );
2197       case CHR('a','k','h','n'):
2198 return( 302 );
2199       case CHR('r','p','h','f'):
2200 return( 303 );
2201       case CHR('b','l','w','f'):
2202 return( 304 );
2203       case CHR('h','a','l','f'):
2204       case CHR('a','b','v','f'):
2205 return( 305 );
2206       case CHR('p','s','t','f'):
2207 return( 306 );
2208       case CHR('v','a','t','u'):
2209 return( 307 );
2210 
2211       case CHR('p','r','e','s'):
2212 return( 310 );
2213       case CHR('b','l','w','s'):
2214 return( 311 );
2215       case CHR('a','b','v','s'):
2216 return( 312 );
2217       case CHR('p','s','t','s'):
2218 return( 313 );
2219       case CHR('c','l','i','g'):
2220 return( 314 );
2221 
2222       case CHR('h','a','l','n'):
2223 return( 320 );
2224 /* end indic ordering */
2225 
2226       case CHR('a','f','r','c'):
2227       case CHR('l','j','m','o'):
2228       case CHR('v','j','m','o'):
2229 return( 350 );
2230       case CHR('v','r','t','2'): case CHR('v','e','r','t'):
2231 return( 1010 );		/* Documented to come last */
2232 
2233 /* Unknown things come after everything but vert/vrt2 */
2234       default:
2235 return( 1000 );
2236 
2237     } else switch ( tag ) {
2238 /* GPOS ordering */
2239       case CHR('c','u','r','s'):
2240 return( 0 );
2241       case CHR('d','i','s','t'):
2242 return( 100 );
2243       case CHR('b','l','w','m'):
2244 return( 201 );
2245       case CHR('a','b','v','m'):
2246 return( 202 );
2247       case CHR('k','e','r','n'):
2248 return( 300 );
2249       case CHR('m','a','r','k'):
2250 return( 400 );
2251       case CHR('m','k','m','k'):
2252 return( 500 );
2253 /* Unknown things come after everything  */
2254       default:
2255 return( 1000 );
2256     }
2257 }
2258 
FeatureOrderId(int isgpos,FeatureScriptLangList * fl)2259 int FeatureOrderId( int isgpos,FeatureScriptLangList *fl ) {
2260     int pos = 9999, temp;
2261 
2262     if ( fl==NULL )
2263 return( 0 );
2264 
2265     while ( fl!=NULL ) {
2266 	temp = _FeatureOrderId(isgpos,fl->featuretag );
2267 	if ( temp<pos ) pos = temp;
2268 	fl = fl->next;
2269     }
2270 return( pos );
2271 }
2272 
SortInsertLookup(SplineFont * sf,OTLookup * newotl)2273 void SortInsertLookup(SplineFont *sf, OTLookup *newotl) {
2274     int isgpos = newotl->lookup_type>=gpos_start;
2275     int pos;
2276     OTLookup *prev, *otl;
2277 
2278     pos = FeatureOrderId(isgpos,newotl->features);
2279     for ( prev=NULL, otl= isgpos ? sf->gpos_lookups : sf->gsub_lookups ;
2280 	    otl!=NULL && FeatureOrderId(isgpos,newotl->features)<pos;
2281 	    prev = otl, otl=otl->next );
2282     newotl->next = otl;
2283     if ( prev!=NULL )
2284 	prev->next = newotl;
2285     else if ( isgpos )
2286 	sf->gpos_lookups = newotl;
2287     else
2288 	sf->gsub_lookups = newotl;
2289 }
2290 
2291 /* Before may be:
2292     * A lookup in into_sf, in which case insert new lookup before it
2293     * NULL               , in which case insert new lookup at end
2294     * -1                 , in which case insert new lookup at start
2295     * -2                 , try to guess a good position
2296 */
OrderNewLookup(SplineFont * into_sf,OTLookup * otl,OTLookup * before)2297 static void OrderNewLookup(SplineFont *into_sf,OTLookup *otl,OTLookup *before) {
2298     int isgpos = otl->lookup_type>=gpos_start;
2299     OTLookup **head = isgpos ? &into_sf->gpos_lookups : &into_sf->gsub_lookups;
2300     OTLookup *prev;
2301 
2302     if ( before == (OTLookup *) -2 )
2303 	SortInsertLookup(into_sf,otl);
2304     else if ( before == (OTLookup *) -1 || *head==NULL || *head==before ) {
2305 	otl->next = *head;
2306 	*head = otl;
2307     } else {
2308 	for ( prev= *head; prev->next!=NULL && prev->next!=before ; prev=prev->next );
2309 	otl->next = prev->next;
2310 	prev->next = otl;
2311     }
2312 }
2313 
_OTLookupCopyInto(struct sfmergecontext * mc,OTLookup * from_otl,OTLookup * before,int do_contents)2314 static OTLookup *_OTLookupCopyInto(struct sfmergecontext *mc,
2315 	OTLookup *from_otl, OTLookup *before, int do_contents) {
2316     OTLookup *otl;
2317     struct lookup_subtable *sub, *last, *from_sub;
2318     int scnt, l;
2319 
2320     for ( l=0; l<mc->lcnt; ++l ) {
2321 	if ( mc->lks[l].from == from_otl ) {
2322 	    if ( mc->lks[l].old )
2323 return( mc->lks[l].to );
2324 	    else
2325     break;
2326 	}
2327     }
2328 
2329     if ( l>=mc->lmax )
2330 	mc->lks = grealloc(mc->lks,(mc->lmax += 20)*sizeof(struct lookup_cvt));
2331     mc->sf_to->changed = true;
2332 
2333     if ( l>=mc->lcnt ) {
2334 	otl = chunkalloc(sizeof(OTLookup));
2335 	*otl = *from_otl;
2336 	memset(&mc->lks[l],0,sizeof(mc->lks[l]));
2337 	mc->lks[l].from = from_otl; mc->lks[l].to = otl; ++mc->lcnt;
2338 	otl->lookup_name = strconcat(mc->prefix,from_otl->lookup_name);
2339 	otl->features = FeatureListCopy(from_otl->features);
2340 	otl->next = NULL; otl->subtables = NULL;
2341 	OrderNewLookup(mc->sf_to,otl,before);
2342 	if ( !do_contents )
2343 	    FIOTLookupCopyInto(mc->sf_to,mc->sf_from, from_otl, otl, 0, before);
2344     } else
2345 	otl = mc->lks[l].to;
2346     if ( !do_contents )
2347 return( otl );
2348 
2349     last = NULL;
2350     scnt = 0;
2351     for ( from_sub = from_otl->subtables; from_sub!=NULL; from_sub=from_sub->next ) {
2352 	sub = chunkalloc(sizeof(struct lookup_subtable));
2353 	*sub = *from_sub;
2354 	sub->lookup = otl;
2355 	sub->subtable_name = strconcat(mc->prefix,from_sub->subtable_name);
2356 	sub->suffix = copy(sub->suffix);
2357 	if ( last==NULL )
2358 	    otl->subtables = sub;
2359 	else
2360 	    last->next = sub;
2361 	last = sub;
2362 	if ( from_sub->kc!=NULL )
2363 	    sub->kc = SF_AddKernClass(mc, from_sub->kc, sub);
2364 	else if ( from_sub->fpst!=NULL )
2365 	    sub->fpst = SF_AddFPST(mc, from_sub->fpst, sub);
2366 	else if ( from_sub->anchor_classes )
2367 	    SF_AddAnchorClasses(mc, from_sub, sub);
2368 	else
2369 	    SF_AddPSTKern(mc, from_sub, sub);
2370 	++scnt;
2371     }
2372     FIOTLookupCopyInto(mc->sf_to,mc->sf_from, from_otl, otl, scnt, before);
2373 return( otl );
2374 }
2375 
NeedsPrefix(SplineFont * into_sf,SplineFont * from_sf,OTLookup ** list)2376 static int NeedsPrefix(SplineFont *into_sf,SplineFont *from_sf, OTLookup **list) {
2377     struct lookup_subtable *from_sub;
2378     int i,j,k;
2379     OTLookup *sublist[2];
2380 
2381     sublist[1] = NULL;
2382 
2383     if ( list==NULL || list[0]==NULL )
2384 return( false );
2385     for ( k=0; list[k]!=NULL; ++k ) {
2386 	OTLookup *from_otl = list[k];
2387 	if ( SFFindLookup(into_sf,from_otl->lookup_name)!=NULL )
2388 return( true );
2389 	for ( from_sub = from_otl->subtables; from_sub!=NULL; from_sub=from_sub->next ) {
2390 	    if ( from_sub->fpst!=NULL ) {
2391 		for ( i=0; i<from_sub->fpst->rule_cnt; ++i ) {
2392 		    struct fpst_rule *r = &from_sub->fpst->rules[i];
2393 		    for ( j=0; j<r->lookup_cnt; ++j ) {
2394 			sublist[0] = r->lookups[j].lookup;
2395 			if ( NeedsPrefix(into_sf,from_sf, sublist))
2396 return( true );
2397 		    }
2398 		}
2399 	    }
2400 	}
2401     }
2402 return( false );
2403 }
2404 
OTLookupCopyInto(SplineFont * into_sf,SplineFont * from_sf,OTLookup * from_otl)2405 OTLookup *OTLookupCopyInto(SplineFont *into_sf,SplineFont *from_sf, OTLookup *from_otl) {
2406     OTLookup *newotl, *list[2];
2407     struct sfmergecontext mc;
2408 
2409     memset(&mc,0,sizeof(mc));
2410     mc.sf_from = from_sf; mc.sf_to = into_sf;
2411 
2412     list[0] = from_otl; list[1] = NULL;
2413     mc.prefix = NeedsPrefix(into_sf,from_sf,list)
2414 	    ? strconcat(from_sf->fontname,"-") : copy("");
2415     newotl = _OTLookupCopyInto(&mc,from_otl,(OTLookup *) -2,true);
2416     free(mc.lks);
2417     free(mc.prefix);
2418 return( newotl );
2419 }
2420 
OTLookupsCopyInto(SplineFont * into_sf,SplineFont * from_sf,OTLookup ** list,OTLookup * before)2421 void OTLookupsCopyInto(SplineFont *into_sf,SplineFont *from_sf,
2422 	OTLookup **list, OTLookup *before) {
2423     int i, do_contents;
2424     struct sfmergecontext mc;
2425 
2426     memset(&mc,0,sizeof(mc));
2427     mc.sf_from = from_sf; mc.sf_to = into_sf;
2428 
2429     mc.prefix = NeedsPrefix(into_sf,from_sf,list)
2430 	    ? strconcat(from_sf->fontname,"-") : copy("");
2431     for ( i=0; list[i]!=NULL; ++i );
2432     mc.lks = galloc((mc.lmax=i+5)*sizeof(struct lookup_cvt));
2433     /* First create all the lookups and position them in the right order */
2434     /*  then create subtables (which may in turn create some new lookups */
2435     /*  for contextual lookups which invoke other lookups, don't care how */
2436     /*  those nested lookups are ordered) */
2437     for ( do_contents=0; do_contents<2; ++do_contents )
2438 	for ( i=0; list[i]!=NULL; ++i )
2439 	    (void) _OTLookupCopyInto(&mc,list[i],before,do_contents);
2440     free(mc.lks);
2441     free(mc.prefix);
2442 }
2443 
2444 /* ************************************************************************** */
2445 /* ****************************** Apply lookups ***************************** */
2446 /* ************************************************************************** */
2447 
2448 struct lookup_data {
2449     struct opentype_str *str;
2450     int cnt, max;
2451 
2452     uint32 script;
2453     SplineFont *sf;
2454 
2455     struct lookup_subtable *lig_owner;
2456     int lcnt, lmax;
2457     SplineChar ***ligs;		/* For each ligature we have an array of SplineChars that are its components preceded by the ligature glyph itself */
2458 				/*  NULL terminated */
2459     int pixelsize;
2460     double scale;
2461 };
2462 
2463 static int ApplyLookupAtPos(uint32 tag, OTLookup *otl,struct lookup_data *data,int pos);
2464 
GlyphNameInClass(char * name,char * class)2465 static int GlyphNameInClass(char *name,char *class ) {
2466     char *pt;
2467     int len = strlen(name);
2468 
2469     if ( class==NULL )
2470 return( false );
2471 
2472     pt = class;
2473     while ( (pt=strstr(pt,name))!=NULL ) {
2474 	if ( pt==NULL )
2475 return( false );
2476 	if ( (pt==class || pt[-1]==' ') && (pt[len]=='\0' || pt[len]==' '))
2477 return( true );
2478 	pt+=len;
2479     }
2480 
2481 return( false );
2482 }
2483 
2484 /* ************************************************************************** */
2485 /* ************************* Apply OpenType Lookups ************************* */
2486 /* ************************************************************************** */
2487 
LigatureFree(struct lookup_data * data)2488 static void LigatureFree(struct lookup_data *data) {
2489     int i;
2490 
2491     if ( data->ligs==NULL )
2492 return;
2493     for ( i=0; data->ligs[i]!=NULL; ++i )
2494 	free(data->ligs[i]);
2495 }
2496 
LigatureSearch(struct lookup_subtable * sub,struct lookup_data * data)2497 static void LigatureSearch(struct lookup_subtable *sub, struct lookup_data *data) {
2498     SplineFont *sf = data->sf;
2499     int gid, ccnt, cnt, ch, err;
2500     SplineChar *sc;
2501     PST *pst;
2502     char *pt, *start;
2503 
2504     LigatureFree(data);
2505     cnt = 0;
2506     for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
2507 	for ( pst=sc->possub; pst!=NULL; pst=pst->next ) if ( pst->subtable==sub ) {
2508 	    for ( pt = pst->u.lig.components, ccnt=0; *pt; ++pt )
2509 		if ( *pt==' ' )
2510 		    ++ccnt;
2511 	    if ( cnt>=data->lmax )
2512 		data->ligs = grealloc(data->ligs,(data->lmax+=100)*sizeof(SplineChar **));
2513 	    data->ligs[cnt] = galloc((ccnt+3)*sizeof(SplineChar *));
2514 	    data->ligs[cnt][0] = sc;
2515 	    ccnt = 1;
2516 	    err = 0;
2517 	    for ( pt = pst->u.lig.components; *pt; ) {
2518 		while ( *pt==' ' ) ++pt;
2519 		if ( *pt=='\0' )
2520 	    break;
2521 		for ( start=pt; *pt!='\0' && *pt!=' '; ++pt );
2522 		ch = *pt; *pt = '\0';
2523 		data->ligs[cnt][ccnt++] = SFGetChar(sf,-1,start);
2524 		*pt = ch;
2525 		if ( data->ligs[cnt][ccnt-1]==NULL )
2526 		    err = 1;
2527 	    }
2528 	    if ( !err )
2529 		data->ligs[cnt++][ccnt] = NULL;
2530 	}
2531     }
2532     if ( cnt>=data->lmax )
2533 	data->ligs = grealloc(data->ligs,(data->lmax+=1)*sizeof(SplineChar **));
2534     data->ligs[cnt] = NULL;
2535     data->lcnt = cnt;
2536 }
2537 
skipglyphs(int lookup_flags,struct lookup_data * data,int pos)2538 static int skipglyphs(int lookup_flags, struct lookup_data *data, int pos) {
2539     int mc, glyph_class;
2540     /* The lookup flags tell us what glyphs to ignore. Skip over anything we */
2541     /*  should ignore */
2542 
2543     if ( lookup_flags==0 )
2544 return( pos );
2545     mc = (lookup_flags>>8);
2546     if ( mc<0 || mc>=data->sf->mark_class_cnt )
2547 	mc = 0;
2548     while ( pos<data->cnt ) {
2549 	glyph_class = gdefclass(data->str[pos].sc);
2550 	/* 1=>base, 2=>ligature, 3=>mark, 4=>component?, 0=>.notdef */
2551 	if ( (glyph_class==1 && (lookup_flags&pst_ignorebaseglyphs)) ||
2552 		(glyph_class==2 && (lookup_flags&pst_ignoreligatures)) ||
2553 		(glyph_class==3 && (lookup_flags&pst_ignorecombiningmarks)) ||
2554 		(glyph_class==3 && mc!=0 &&
2555 			!GlyphNameInClass(data->str[pos].sc->name,data->sf->mark_classes[mc])) ) {
2556 	    ++pos;
2557 	} else
2558     break;
2559     }
2560 return( pos );
2561 }
2562 
bskipmarkglyphs(int lookup_flags,struct lookup_data * data,int pos)2563 static int bskipmarkglyphs(int lookup_flags, struct lookup_data *data, int pos) {
2564     int mc, glyph_class;
2565     /* The lookup flags tell us what glyphs to ignore. Skip over anything we */
2566     /*  should ignore. Here we skip backward */
2567 
2568     mc = (lookup_flags>>8);
2569     if ( mc<0 || mc>=data->sf->mark_class_cnt )
2570 	mc = 0;
2571     while ( pos>=0 ) {
2572 	glyph_class = gdefclass(data->str[pos].sc);
2573 	/* 1=>base, 2=>ligature, 3=>mark, 4=>component?, 0=>.notdef */
2574 	if ( glyph_class==3 )
2575 	    --pos;
2576 	else if ( (glyph_class==1 && (lookup_flags&pst_ignorebaseglyphs)) ||
2577 		(glyph_class==2 && (lookup_flags&pst_ignoreligatures)) ||
2578 		(glyph_class==3 && (lookup_flags&pst_ignorecombiningmarks)) ||
2579 		(glyph_class==3 && mc!=0 &&
2580 			!GlyphNameInClass(data->str[pos].sc->name,data->sf->mark_classes[mc])) ) {
2581 	    --pos;
2582 	} else
2583     break;
2584     }
2585 return( pos );
2586 }
2587 
bskipglyphs(int lookup_flags,struct lookup_data * data,int pos)2588 static int bskipglyphs(int lookup_flags, struct lookup_data *data, int pos) {
2589     int mc, glyph_class;
2590     /* The lookup flags tell us what glyphs to ignore. Skip over anything we */
2591     /*  should ignore. Here we skip backward */
2592 
2593     if ( lookup_flags==0 )
2594 return( pos );
2595     mc = (lookup_flags>>8);
2596     if ( mc<0 || mc>=data->sf->mark_class_cnt )
2597 	mc = 0;
2598     while ( pos>=0 ) {
2599 	glyph_class = gdefclass(data->str[pos].sc);
2600 	/* 1=>base, 2=>ligature, 3=>mark, 4=>component?, 0=>.notdef */
2601 	if ( (glyph_class==1 && (lookup_flags&pst_ignorebaseglyphs)) ||
2602 		(glyph_class==2 && (lookup_flags&pst_ignoreligatures)) ||
2603 		(glyph_class==3 && (lookup_flags&pst_ignorecombiningmarks)) ||
2604 		(glyph_class==3 && mc!=0 &&
2605 			!GlyphNameInClass(data->str[pos].sc->name,data->sf->mark_classes[mc])) ) {
2606 	    --pos;
2607 	} else
2608     break;
2609     }
2610 return( pos );
2611 }
2612 
ContextualMatch(struct lookup_subtable * sub,struct lookup_data * data,int pos,struct fpst_rule ** _rule)2613 static int ContextualMatch(struct lookup_subtable *sub,struct lookup_data *data,
2614 	int pos, struct fpst_rule **_rule) {
2615     int i, cpos, retpos, r;
2616     FPST *fpst = sub->fpst;
2617     int lookup_flags = sub->lookup->lookup_flags;
2618     char *pt;
2619 
2620     /* If we should skip the current glyph then don't try for a match here */
2621     cpos = skipglyphs(lookup_flags,data,pos);
2622     if ( cpos!=pos )
2623 return( 0 );
2624 
2625     for ( r=0; r<fpst->rule_cnt; ++r ) {
2626 	struct fpst_rule *rule = &fpst->rules[r];
2627 	for ( i=pos; i<data->cnt; ++i )
2628 	    data->str[i].context_pos = -1;
2629 
2630 /* Handle backtrack (backtrace in the rule is stored in reverse textual order) */
2631 	if ( fpst->type == pst_chainpos || fpst->type == pst_chainsub ) {
2632 	    if ( fpst->format==pst_glyphs ) {
2633 		pt = rule->u.glyph.back;
2634 		for ( i=bskipglyphs(lookup_flags,data,pos-1), cpos=0; i>=0; i = bskipglyphs(lookup_flags,data,i-1)) {
2635 		    char *name = data->str[i].sc->name;
2636 		    int len = strlen( name );
2637 		    if ( strncmp(name,pt,len)!=0 || (pt[len]!='\0' && pt[len]!=' '))
2638 		break;
2639 		    pt += len;
2640 		    while ( *pt==' ' ) ++pt;
2641 		}
2642 		if ( *pt!='\0' )
2643     continue;		/* didn't match */
2644 	    } else if ( fpst->format==pst_class ) {
2645 		for ( i=bskipglyphs(lookup_flags,data,pos-1), cpos=0; i>=0 && cpos<rule->u.class.bcnt; i = bskipglyphs(lookup_flags,data,i-1)) {
2646 		    if ( !GlyphNameInClass(data->str[i].sc->name,fpst->bclass[rule->u.class.bclasses[cpos]]) )
2647 		break;
2648 		    ++cpos;
2649 		}
2650 		if ( cpos!=rule->u.class.bcnt )
2651     continue;		/* didn't match */
2652 	    } else if ( fpst->format==pst_coverage ) {
2653 		for ( i=bskipglyphs(lookup_flags,data,pos-1), cpos=0; i>=0 && cpos<rule->u.coverage.bcnt; i = bskipglyphs(lookup_flags,data,i-1)) {
2654 		    if ( !GlyphNameInClass(data->str[i].sc->name,rule->u.coverage.bcovers[cpos]) )
2655 		break;
2656 		    ++cpos;
2657 		}
2658 		if ( cpos<rule->u.coverage.bcnt )
2659     continue;		/* didn't match */
2660 	    }
2661 	}
2662 /* Handle Match */
2663 	if ( fpst->format==pst_glyphs ) {
2664 	    pt = rule->u.glyph.names;
2665 	    for ( i=pos, cpos=0; i<data->cnt && *pt!='\0'; i = skipglyphs(lookup_flags,data,i+1)) {
2666 		char *name = data->str[i].sc->name;
2667 		int len = strlen( name );
2668 		if ( strncmp(name,pt,len)!=0 || (pt[len]!='\0' && pt[len]!=' '))
2669 	    break;
2670 		data->str[i].context_pos = cpos++;
2671 		pt += len;
2672 		while ( *pt==' ' ) ++pt;
2673 	    }
2674 	    if ( *pt!='\0' )
2675     continue;		/* didn't match */
2676 	} else if ( fpst->format==pst_class ) {
2677 	    for ( i=pos, cpos=0; i<data->cnt && cpos<rule->u.class.ncnt; i = skipglyphs(lookup_flags,data,i+1)) {
2678 		int class = rule->u.class.nclasses[cpos];
2679 		if ( class!=0 ) {
2680 		    if ( !GlyphNameInClass(data->str[i].sc->name,fpst->nclass[class]) )
2681 	    break;
2682 		} else {
2683 		    int c;
2684 		    /* Ok, to match class 0 we must fail to match all other classes */
2685 		    for ( c=1; c<fpst->nccnt; ++c )
2686 			if ( !GlyphNameInClass(data->str[i].sc->name,fpst->nclass[c]) )
2687 		    break;
2688 		    if ( c!=fpst->nccnt )
2689 	    break;		/* It matched another class => not in class 0 */
2690 		}
2691 		data->str[i].context_pos = cpos++;
2692 	    }
2693 	    if ( cpos<rule->u.class.ncnt )
2694     continue;		/* didn't match */
2695 	} else if ( fpst->format==pst_coverage ) {
2696 	    for ( i=pos, cpos=0; i<data->cnt && cpos<rule->u.coverage.ncnt; i = skipglyphs(lookup_flags,data,i+1)) {
2697 		if ( !GlyphNameInClass(data->str[i].sc->name,rule->u.coverage.ncovers[cpos]) )
2698 	    break;
2699 		data->str[i].context_pos = cpos++;
2700 	    }
2701 	    if ( cpos<rule->u.coverage.ncnt )
2702     continue;		/* didn't match */
2703 	} else
2704 return( 0 );		/* Not ready to deal with reverse chainging */
2705 
2706 	retpos = i;
2707 /* Handle lookahead */
2708 	if ( fpst->type == pst_chainpos || fpst->type == pst_chainsub ) {
2709 	    if ( fpst->format==pst_glyphs ) {
2710 		pt = rule->u.glyph.fore;
2711 		for ( i=retpos; i<data->cnt && *pt!='\0'; i = skipglyphs(lookup_flags,data,i+1)) {
2712 		    char *name = data->str[i].sc->name;
2713 		    int len = strlen( name );
2714 		    if ( strncmp(name,pt,len)!=0 || (pt[len]!='\0' && pt[len]!=' '))
2715 		break;
2716 		    pt += len;
2717 		    while ( *pt==' ' ) ++pt;
2718 		}
2719 		if ( *pt!='\0' )
2720     continue;		/* didn't match */
2721 	    } else if ( fpst->format==pst_class ) {
2722 		for ( i=retpos, cpos=0; i<data->cnt && cpos<rule->u.class.fcnt; i = skipglyphs(lookup_flags,data,i+1)) {
2723 		    if ( !GlyphNameInClass(data->str[i].sc->name,fpst->fclass[rule->u.class.fclasses[cpos]]) )
2724 		break;
2725 		    cpos++;
2726 		}
2727 		if ( cpos<rule->u.class.fcnt )
2728     continue;		/* didn't match */
2729 	    } else if ( fpst->format==pst_coverage ) {
2730 		for ( i=retpos, cpos=0; i<data->cnt && cpos<rule->u.coverage.fcnt; i = skipglyphs(lookup_flags,data,i+1)) {
2731 		    if ( !GlyphNameInClass(data->str[i].sc->name,rule->u.coverage.fcovers[cpos]) )
2732 		break;
2733 		    cpos++;
2734 		}
2735 		if ( cpos<rule->u.coverage.fcnt )
2736     continue;		/* didn't match */
2737 	    }
2738 	}
2739 	*_rule = rule;
2740 return( retpos );
2741     }
2742 return( 0 );
2743 }
2744 
ApplySingleSubsAtPos(struct lookup_subtable * sub,struct lookup_data * data,int pos)2745 static int ApplySingleSubsAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) {
2746     PST *pst;
2747     SplineChar *sc;
2748 
2749     for ( pst=data->str[pos].sc->possub; pst!=NULL && pst->subtable!=sub; pst=pst->next );
2750     if ( pst==NULL )
2751 return( 0 );
2752 
2753     sc = SFGetChar(data->sf,-1,pst->u.subs.variant);
2754     if ( sc!=NULL ) {
2755 	data->str[pos].sc = sc;
2756 return( pos+1 );
2757     } else if ( strcmp(pst->u.subs.variant,MAC_DELETED_GLYPH_NAME)==0 ) {
2758 	/* Under AAT we delete the glyph. But OpenType doesn't have that concept */
2759 	int i;
2760 	for ( i=pos+1; i<data->cnt; ++i )
2761 	    data->str[pos-1] = data->str[pos];
2762 	--data->cnt;
2763 return( pos );
2764     } else {
2765 return( 0 );
2766     }
2767 }
2768 
ApplyMultSubsAtPos(struct lookup_subtable * sub,struct lookup_data * data,int pos)2769 static int ApplyMultSubsAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) {
2770     PST *pst;
2771     SplineChar *sc;
2772     char *start, *pt;
2773     int mcnt, ch, i;
2774     SplineChar *mults[20];
2775 
2776     for ( pst=data->str[pos].sc->possub; pst!=NULL && pst->subtable!=sub; pst=pst->next );
2777     if ( pst==NULL )
2778 return( 0 );
2779 
2780     mcnt = 0;
2781     for ( start = pst->u.alt.components; *start==' '; ++start);
2782     for ( ; *start; ) {
2783 	for ( pt=start; *pt!='\0' && *pt!=' '; ++pt );
2784 	ch = *pt; *pt = '\0';
2785 	sc = SFGetChar(data->sf,-1,start);
2786 	*pt = ch;
2787 	if ( sc==NULL )
2788 return( 0 );
2789 	if ( mcnt<20 ) mults[mcnt++] = sc;
2790 	while ( *pt==' ' ) ++pt;
2791 	start = pt;
2792     }
2793 
2794     if ( mcnt==0 ) {
2795 	/* Is this legal? that is can we remove a glyph with an empty multiple? */
2796 	for ( i=pos+1; i<data->cnt; ++i )
2797 	    data->str[i-1] = data->str[i];
2798 	--data->cnt;
2799 return( pos );
2800     } else if ( mcnt==1 ) {
2801 	data->str[pos].sc = mults[0];
2802 return( pos+1 );
2803     } else {
2804 	if ( data->cnt+mcnt-1 >= data->max )
2805 	    data->str = grealloc(data->str,(data->max+=mcnt) * sizeof( struct opentype_str ));
2806 	for ( i=data->cnt-1; i>pos; --i )
2807 	    data->str[i+mcnt-1] = data->str[i];
2808 	memset(data->str+pos,0,mcnt*sizeof(struct opentype_str));
2809 	for ( i=0; i<mcnt; ++i ) {
2810 	    data->str[pos+i].sc = mults[i];
2811 	    data->str[pos+i].orig_index = data->str[pos].orig_index;
2812 	}
2813 	data->cnt += (mcnt-1);
2814 return( pos+mcnt );
2815     }
2816 }
2817 
ApplyAltSubsAtPos(struct lookup_subtable * sub,struct lookup_data * data,int pos)2818 static int ApplyAltSubsAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) {
2819     PST *pst;
2820     SplineChar *sc;
2821     char *start, *pt, ch;
2822 
2823     for ( pst=data->str[pos].sc->possub; pst!=NULL && pst->subtable!=sub; pst=pst->next );
2824     if ( pst==NULL )
2825 return( 0 );
2826 
2827     for ( start = pst->u.alt.components; *start==' '; ++start);
2828     for ( ; *start; ) {
2829 	for ( pt=start; *pt!='\0' && *pt!=' '; ++pt );
2830 	ch = *pt; *pt = '\0';
2831 	sc = SFGetChar(data->sf,-1,start);
2832 	*pt = ch;
2833 	if ( sc!=NULL ) {
2834 	    data->str[pos].sc = sc;
2835 return( pos+1 );
2836 	}
2837 	while ( *pt==' ' ) ++pt;
2838 	start = pt;
2839     }
2840 return( 0 );
2841 }
2842 
ApplyLigatureSubsAtPos(struct lookup_subtable * sub,struct lookup_data * data,int pos)2843 static int ApplyLigatureSubsAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) {
2844     int i,k, lpos, npos;
2845     int lookup_flags = sub->lookup->lookup_flags;
2846     int match_found = -1, match_len=0;
2847 
2848     if ( data->lig_owner!=sub )
2849 	LigatureSearch(sub,data);
2850     for ( i=0; i<data->lcnt; ++i ) {
2851 	if ( data->ligs[i][1]==data->str[pos].sc ) {
2852 	    lpos = 0;
2853 	    npos = pos+1;
2854 	    for ( k=2; data->ligs[i][k]!=NULL; ++k ) {
2855 		npos = skipglyphs(lookup_flags,data,npos);
2856 		if ( npos>=data->cnt || data->str[npos].sc != data->ligs[i][k] )
2857 	    break;
2858 		++npos;
2859 	    }
2860 	    if ( data->ligs[i][k]==NULL ) {
2861 		if ( match_found==-1 || k>match_len ) {
2862 		    match_found = i;
2863 		    match_len = k;
2864 		}
2865 	    }
2866 	}
2867     }
2868     if ( match_found!=-1 ) {
2869 	/* Matched. Remove the component glyphs, and note which component */
2870 	/*  any intervening marks should be attached to */
2871 	data->str[pos].sc = data->ligs[match_found][0];
2872 	npos = pos+1;
2873 	for ( k=2; data->ligs[match_found][k]!=NULL; ++k ) {
2874 	    lpos = skipglyphs(lookup_flags,data,npos);
2875 	    for ( ; npos<lpos; ++npos )
2876 		data->str[npos].lig_pos = k-2;
2877 	    /* Remove this glyph (copy the final NUL too) */
2878 	    for ( ++lpos; lpos<=data->cnt; ++lpos )
2879 		data->str[lpos-1] = data->str[lpos];
2880 	    --data->cnt;
2881 	}
2882 	/* Any marks after the last component (which should be attached */
2883 	/*  to it) will not have been tagged, so do that now */
2884 	lpos = skipglyphs(lookup_flags,data,npos);
2885 	for ( ; npos<lpos; ++npos )
2886 	    data->str[npos].lig_pos = k-2;
2887 return( pos+1 );
2888     }
2889 
2890 return( 0 );
2891 }
2892 
ApplyContextual(struct lookup_subtable * sub,struct lookup_data * data,int pos)2893 static int ApplyContextual(struct lookup_subtable *sub,struct lookup_data *data,int pos) {
2894     /* On this level there is no difference between GPOS/GSUB contextuals */
2895     /*  If the contextual matches, then we apply the lookups, otherwise we */
2896     /*  don't. Now the lookups will be different, but we don't care here */
2897     struct fpst_rule *rule;
2898     int retpos, i,j;
2899 
2900     retpos = ContextualMatch(sub,data,pos,&rule);
2901     if ( retpos==0 )
2902 return( 0 );
2903     for ( i=0; i<rule->lookup_cnt; ++i ) {
2904 	for ( j=pos; j<data->cnt; ++j ) {
2905 	    if ( data->str[j].context_pos == rule->lookups[i].seq ) {
2906 		ApplyLookupAtPos(0,rule->lookups[i].lookup,data,j);
2907 	break;
2908 	    }
2909 	}
2910     }
2911 return( retpos );
2912 }
2913 
2914 #ifdef FONTFORGE_CONFIG_DEVICETABLES
FigureDeviceTable(DeviceTable * dt,int pixelsize)2915 static int FigureDeviceTable(DeviceTable *dt,int pixelsize) {
2916 
2917     if ( dt==NULL || dt->corrections==NULL || pixelsize<dt->first_pixel_size ||
2918 	    pixelsize>dt->last_pixel_size )
2919 return( 0 );
2920 
2921 return( dt->corrections[pixelsize - dt->last_pixel_size] );
2922 }
2923 #endif
2924 
ApplySinglePosAtPos(struct lookup_subtable * sub,struct lookup_data * data,int pos)2925 static int ApplySinglePosAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) {
2926     PST *pst;
2927 
2928     for ( pst=data->str[pos].sc->possub; pst!=NULL && pst->subtable!=sub; pst=pst->next );
2929     if ( pst==NULL )
2930 return( 0 );
2931 
2932     data->str[pos].vr.xoff += rint( pst->u.pos.xoff * data->scale );
2933     data->str[pos].vr.yoff += rint( pst->u.pos.yoff * data->scale );
2934     data->str[pos].vr.h_adv_off += rint( pst->u.pos.h_adv_off * data->scale );
2935     data->str[pos].vr.v_adv_off += rint( pst->u.pos.v_adv_off * data->scale );
2936 #ifdef FONTFORGE_CONFIG_DEVICETABLES
2937     if ( pst->u.pos.adjust!=NULL ) {
2938 	data->str[pos].vr.xoff += FigureDeviceTable(&pst->u.pos.adjust->xadjust,data->pixelsize);
2939 	data->str[pos].vr.yoff += FigureDeviceTable(&pst->u.pos.adjust->yadjust,data->pixelsize);
2940 	data->str[pos].vr.h_adv_off += FigureDeviceTable(&pst->u.pos.adjust->xadv,data->pixelsize);
2941 	data->str[pos].vr.v_adv_off += FigureDeviceTable(&pst->u.pos.adjust->yadv,data->pixelsize);
2942     }
2943 #endif
2944 return( pos+1 );
2945 }
2946 
ApplyPairPosAtPos(struct lookup_subtable * sub,struct lookup_data * data,int pos)2947 static int ApplyPairPosAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) {
2948     PST *pst;
2949     int npos, isv, within;
2950     KernPair *kp;
2951 
2952     npos = skipglyphs(sub->lookup->lookup_flags,data,pos+1);
2953     if ( npos>=data->cnt )
2954 return( 0 );
2955     if ( sub->kc!=NULL ) {
2956 	within = KCFindIndex(sub->kc,data->str[pos].sc->name,data->str[npos].sc->name);
2957 	if ( within==-1 )
2958 return( 0 );
2959 	data->str[pos].kc_index = within;
2960 	data->str[pos].kc = sub->kc;
2961 	if ( sub->vertical_kerning ) {
2962 	    data->str[pos].vr.v_adv_off += rint( sub->kc->offsets[within] * data->scale );
2963 #ifdef FONTFORGE_CONFIG_DEVICETABLES
2964 	    data->str[pos].vr.v_adv_off += FigureDeviceTable(&sub->kc->adjusts[within],data->pixelsize);
2965 #endif
2966 	} else if ( sub->lookup->lookup_flags & pst_r2l ) {
2967 	    data->str[npos].vr.h_adv_off += rint( sub->kc->offsets[within] * data->scale );
2968 #ifdef FONTFORGE_CONFIG_DEVICETABLES
2969 	    data->str[npos].vr.h_adv_off += FigureDeviceTable(&sub->kc->adjusts[within],data->pixelsize);
2970 #endif
2971 	} else {
2972 	    data->str[pos].vr.h_adv_off += rint( sub->kc->offsets[within] * data->scale );
2973 #ifdef FONTFORGE_CONFIG_DEVICETABLES
2974 	    data->str[pos].vr.h_adv_off += FigureDeviceTable(&sub->kc->adjusts[within],data->pixelsize);
2975 #endif
2976 	}
2977 return( pos+1 );
2978     } else {
2979 	for ( pst=data->str[pos].sc->possub; pst!=NULL; pst=pst->next ) {
2980 	    if ( pst->subtable==sub && strcmp(pst->u.pair.paired,data->str[npos].sc->name)==0 ) {
2981 		data->str[pos].vr.xoff += rint( pst->u.pair.vr[0].xoff * data->scale);
2982 		data->str[pos].vr.yoff += rint( pst->u.pair.vr[0].yoff * data->scale);
2983 		data->str[pos].vr.h_adv_off += rint( pst->u.pair.vr[0].h_adv_off * data->scale);
2984 		data->str[pos].vr.v_adv_off += rint( pst->u.pair.vr[0].v_adv_off * data->scale);
2985 		data->str[npos].vr.xoff += rint( pst->u.pair.vr[1].xoff * data->scale);
2986 		data->str[npos].vr.yoff += rint( pst->u.pair.vr[1].yoff * data->scale);
2987 		data->str[npos].vr.h_adv_off += rint( pst->u.pair.vr[1].h_adv_off * data->scale);
2988 		data->str[npos].vr.v_adv_off += rint( pst->u.pair.vr[1].v_adv_off * data->scale);
2989 #ifdef FONTFORGE_CONFIG_DEVICETABLES
2990 		/* I got bored. I should do all of them */
2991 		if ( pst->u.pair.vr[0].adjust!=NULL ) {
2992 		    data->str[pos].vr.h_adv_off += FigureDeviceTable(&pst->u.pair.vr[0].adjust->xadv,data->pixelsize);
2993 		}
2994 #endif
2995 return( pos+1 );	/* We do NOT want to return npos+1 */
2996 	    }
2997 	}
2998 	for ( isv = 0; isv<2; ++isv ) {
2999 	    for ( kp = isv ? data->str[pos].sc->vkerns : data->str[pos].sc->kerns; kp!=NULL; kp=kp->next ) {
3000 		if ( kp->subtable == sub && kp->sc == data->str[npos].sc ) {
3001 		    data->str[pos].kp = kp;
3002 		    if ( isv ) {
3003 			data->str[pos].vr.v_adv_off += rint( kp->off * data->scale);
3004 #ifdef FONTFORGE_CONFIG_DEVICETABLES
3005 			data->str[pos].vr.v_adv_off += FigureDeviceTable(kp->adjust,data->pixelsize);
3006 #endif
3007 		    } else if ( sub->lookup->lookup_flags & pst_r2l ) {
3008 			data->str[npos].vr.h_adv_off += rint( kp->off * data->scale);
3009 #ifdef FONTFORGE_CONFIG_DEVICETABLES
3010 			data->str[npos].vr.h_adv_off += FigureDeviceTable(kp->adjust,data->pixelsize);
3011 #endif
3012 		    } else {
3013 			data->str[pos].vr.h_adv_off += rint( kp->off * data->scale);
3014 #ifdef FONTFORGE_CONFIG_DEVICETABLES
3015 			data->str[pos].vr.h_adv_off += FigureDeviceTable(kp->adjust,data->pixelsize);
3016 #endif
3017 		    }
3018 return( pos+1 );
3019 		}
3020 	    }
3021 	}
3022     }
3023 
3024 return( 0 );
3025 }
3026 
ApplyAnchorPosAtPos(struct lookup_subtable * sub,struct lookup_data * data,int pos)3027 static int ApplyAnchorPosAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) {
3028     AnchorPoint *ap1, *ap2;
3029     int bpos;
3030 
3031     /* Anchors do not position the base glyph, but the mark (or second glyph */
3032     /*  of a cursive attachment). This means we don't apply the attachment when*/
3033     /*  we meet the first glyph, but wait until we meet the second, and then */
3034     /*  walk backwards */
3035     /* The backwards walk is different depending on the lookup type (I think) */
3036     /*  mark to base and mark to ligature lookups will skip all marks even if */
3037     /*  lookup flags don't specify that */
3038     /* mark to mark, and cursive attachment only skip what the lookup flags */
3039     /*  tell them to skip. */
3040     for ( ap2=data->str[pos].sc->anchor; ap2!=NULL ; ap2=ap2->next ) {
3041 	if ( ap2->anchor->subtable==sub && (ap2->type == at_mark || ap2->type == at_centry))
3042     break;
3043     }
3044     if ( ap2==NULL ) {
3045 	/* This subtable is not used by this glyph ... at least this glyph is */
3046 	/*  neither a mark nor an entry point for this subtable */
3047 return( 0 );
3048     }
3049 
3050     /* There's only going to be one mark anchor on a glyph in a given subtable*/
3051     /* And cursive attachments only allow one anchor class per subtable */
3052     /* in either case we have already found the only attachment site possible */
3053     /*  in the current glyph */
3054 
3055     if ( sub->lookup->lookup_type == gpos_mark2base ||
3056 	    sub->lookup->lookup_type == gpos_mark2ligature )
3057 	bpos = bskipmarkglyphs(sub->lookup->lookup_flags,data,pos-1);
3058     else
3059 	bpos = bskipglyphs(sub->lookup->lookup_flags,data,pos-1);
3060     if ( bpos==-1 )
3061 return( 0 );		/* No match */
3062 
3063     if ( sub->lookup->lookup_type == gpos_cursive ) {
3064 	for ( ap1=data->str[bpos].sc->anchor; ap1!=NULL ; ap1=ap1->next ) {
3065 	    if ( ap1->anchor==ap2->anchor && ap1->type==at_cexit )
3066 	break;
3067 	}
3068     } else if ( sub->lookup->lookup_type == gpos_mark2ligature ) {
3069 	for ( ap1=data->str[bpos].sc->anchor; ap1!=NULL ; ap1=ap1->next ) {
3070 	    if ( ap1->anchor==ap2->anchor && ap1->type==at_baselig &&
3071 		    ap1->lig_index == data->str[pos].lig_pos )
3072 	break;
3073 	}
3074     } else {
3075 	for ( ap1=data->str[bpos].sc->anchor; ap1!=NULL ; ap1=ap1->next ) {
3076 	    if ( ap1->anchor==ap2->anchor &&
3077 		    (ap1->type==at_basechar || ap1->type==at_basemark) )
3078 	break;
3079 	}
3080     }
3081     if ( ap1==NULL )
3082 return( 0 );		/* No match */
3083 
3084 /* This probably doesn't work for vertical text */
3085     data->str[pos].vr.yoff = data->str[bpos].vr.yoff +
3086 	    rint((ap1->me.y - ap2->me.y) * data->scale);
3087 #ifdef FONTFORGE_CONFIG_DEVICETABLES
3088     data->str[pos].vr.yoff += FigureDeviceTable(&ap1->yadjust,data->pixelsize)-
3089 		FigureDeviceTable(&ap2->yadjust,data->pixelsize);
3090 #endif
3091     if ( sub->lookup->lookup_flags&pst_r2l ) {
3092 	data->str[pos].vr.xoff = data->str[bpos].vr.xoff +
3093 		rint( -(ap1->me.x - ap2->me.x)*data->scale );
3094 #ifdef FONTFORGE_CONFIG_DEVICETABLES
3095 	data->str[pos].vr.xoff -= FigureDeviceTable(&ap1->xadjust,data->pixelsize)-
3096 		    FigureDeviceTable(&ap2->xadjust,data->pixelsize);
3097 #endif
3098     } else {
3099 	data->str[pos].vr.xoff = data->str[bpos].vr.xoff +
3100 		rint( (ap1->me.x - ap2->me.x - data->str[bpos].sc->width)*data->scale -
3101 		data->str[bpos].vr.h_adv_off);
3102 #ifdef FONTFORGE_CONFIG_DEVICETABLES
3103 	data->str[pos].vr.xoff += FigureDeviceTable(&ap1->xadjust,data->pixelsize)-
3104 		    FigureDeviceTable(&ap2->xadjust,data->pixelsize);
3105 #endif
3106     }
3107 
3108 return( pos+1 );
3109 }
3110 
ConditionalTagOk(uint32 tag,OTLookup * otl,struct lookup_data * data,int pos)3111 static int ConditionalTagOk(uint32 tag, OTLookup *otl,struct lookup_data *data,int pos) {
3112     int npos, bpos;
3113     uint32 script;
3114     int before_in_script, after_in_script;
3115 
3116     if ( tag==CHR('i','n','i','t') || tag==CHR('i','s','o','l') ||
3117 	    tag==CHR('f','i','n','a') || tag==CHR('m','e','d','i') ) {
3118 	npos = skipglyphs(otl->lookup_flags,data,pos+1);
3119 	bpos = bskipglyphs(otl->lookup_flags,data,pos-1);
3120 	script = SCScriptFromUnicode(data->str[pos].sc);
3121 	before_in_script = (bpos>=0 && SCScriptFromUnicode(data->str[bpos].sc)==script);
3122 	after_in_script = (npos<data->cnt && SCScriptFromUnicode(data->str[npos].sc)==script);
3123 	if ( tag==CHR('i','n','i','t') )
3124 return( !before_in_script && after_in_script );
3125 	else if ( tag==CHR('i','s','o','l') )
3126 return( !before_in_script && !after_in_script );
3127 	else if ( tag==CHR('f','i','n','a') )
3128 return( before_in_script && !after_in_script );
3129 	else
3130 return( before_in_script && after_in_script );
3131     }
3132 
3133 return( true );
3134 }
3135 
ApplyLookupAtPos(uint32 tag,OTLookup * otl,struct lookup_data * data,int pos)3136 static int ApplyLookupAtPos(uint32 tag, OTLookup *otl,struct lookup_data *data,int pos) {
3137     struct lookup_subtable *sub;
3138     int newpos;
3139 
3140     /* Some tags imply a conditional check. Do that now */
3141     if ( !ConditionalTagOk(tag,otl,data,pos))
3142 return( 0 );
3143 
3144     for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
3145 	switch ( otl->lookup_type ) {
3146 	  case gsub_single:
3147 	    newpos = ApplySingleSubsAtPos(sub,data,pos);
3148 	  break;
3149 	  case gsub_multiple:
3150 	    newpos = ApplyMultSubsAtPos(sub,data,pos);
3151 	  break;
3152 	  case gsub_alternate:
3153 	    newpos = ApplyAltSubsAtPos(sub,data,pos);
3154 	  break;
3155 	  case gsub_ligature:
3156 	    newpos = ApplyLigatureSubsAtPos(sub,data,pos);
3157 	  break;
3158 	  case gsub_context:
3159 	    newpos = ApplyContextual(sub,data,pos);
3160 	  break;
3161 	  case gsub_contextchain:
3162 	    newpos = ApplyContextual(sub,data,pos);
3163 	  break;
3164 	  case gsub_reversecchain:
3165 	    newpos = ApplySingleSubsAtPos(sub,data,pos);
3166 	  break;
3167 
3168 	  case gpos_single:
3169 	    newpos = ApplySinglePosAtPos(sub,data,pos);
3170 	  break;
3171 	  case gpos_pair:
3172 	    newpos = ApplyPairPosAtPos(sub,data,pos);
3173 	  break;
3174 	  case gpos_cursive:
3175 	    newpos = ApplyAnchorPosAtPos(sub,data,pos);
3176 	  break;
3177 	  case gpos_mark2base:
3178 	    newpos = ApplyAnchorPosAtPos(sub,data,pos);
3179 	  break;
3180 	  case gpos_mark2ligature:
3181 	    newpos = ApplyAnchorPosAtPos(sub,data,pos);
3182 	  break;
3183 	  case gpos_mark2mark:
3184 	    newpos = ApplyAnchorPosAtPos(sub,data,pos);
3185 	  break;
3186 	  case gpos_context:
3187 	    newpos = ApplyContextual(sub,data,pos);
3188 	  break;
3189 	  case gpos_contextchain:
3190 	    newpos = ApplyContextual(sub,data,pos);
3191 	  break;
3192 	  default:
3193 	    newpos = 0;
3194 	  break;
3195 	}
3196 	/* if a subtable worked, we don't try to apply the next one */
3197 	if ( newpos!=0 )
3198 return( newpos );
3199     }
3200 return( 0 );
3201 }
3202 
ApplyLookup(uint32 tag,OTLookup * otl,struct lookup_data * data)3203 static void ApplyLookup(uint32 tag, OTLookup *otl,struct lookup_data *data) {
3204     int pos, npos;
3205 	/* OpenType */
3206 	for ( pos = 0; pos<data->cnt; ) {
3207 	    npos = ApplyLookupAtPos(tag,otl,data,pos);
3208 	    if ( npos<=pos)		/* !!!!! */
3209 		npos = pos+1;
3210 	    pos = npos;
3211 	}
3212 }
3213 
FSLLMatches(FeatureScriptLangList * fl,uint32 * flist,uint32 script,uint32 lang)3214 static uint32 FSLLMatches(FeatureScriptLangList *fl,uint32 *flist,uint32 script,uint32 lang) {
3215     int i,l;
3216     struct scriptlanglist *sl;
3217 
3218     if ( flist==NULL )
3219 return( 0 );
3220 
3221     while ( fl!=NULL ) {
3222 	for ( i=0; flist[i]!=0; ++i ) {
3223 	    if ( fl->featuretag==flist[i] )
3224 	break;
3225 	}
3226 	if ( flist[i]!=0 ) {
3227 	    for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) {
3228 		if ( sl->script == script ) {
3229 		    for ( l=0; l<sl->lang_cnt; ++l )
3230 			if ( (l<MAX_LANG && sl->langs[l]==lang) ||
3231 				(l>=MAX_LANG && sl->morelangs[l-MAX_LANG]==lang))
3232 return( fl->featuretag );
3233 		}
3234 	    }
3235 	}
3236 	fl = fl->next;
3237     }
3238 return( 0 );
3239 }
3240 
3241 /* This routine takes a string of glyphs and applies the opentype transformations */
3242 /*  indicated by the features (and script and language) we are passed, it returns */
3243 /*  a transformed string with substitutions applied and containing positioning */
3244 /*  info */
ApplyTickedFeatures(SplineFont * sf,uint32 * flist,uint32 script,uint32 lang,int pixelsize,SplineChar ** glyphs)3245 struct opentype_str *ApplyTickedFeatures(SplineFont *sf,uint32 *flist, uint32 script, uint32 lang,
3246 	int pixelsize, SplineChar **glyphs) {
3247     int isgpos, cnt;
3248     OTLookup *otl;
3249     struct lookup_data data;
3250     uint32 *langs, templang;
3251     int i;
3252 
3253     memset(&data,0,sizeof(data));
3254     for ( cnt=0; glyphs[cnt]!=NULL; ++cnt );
3255     data.str = gcalloc(cnt+1,sizeof(struct opentype_str));
3256     data.cnt = data.max = cnt;
3257     for ( cnt=0; glyphs[cnt]!=NULL; ++cnt ) {
3258 	data.str[cnt].sc = glyphs[cnt];
3259 	data.str[cnt].orig_index = cnt;
3260 	data.str[cnt].lig_pos = data.str[cnt].context_pos = -1;
3261     }
3262     if ( sf->cidmaster!=NULL ) sf=sf->cidmaster;
3263     data.sf = sf;
3264     data.pixelsize = pixelsize;
3265     data.scale = pixelsize/(double) (sf->ascent+sf->descent);
3266 
3267     /* Indic glyph reordering???? */
3268     for ( isgpos=0; isgpos<2; ++isgpos ) {
3269 	/* Check that this table has an entry for this language */
3270 	/*  if it doesn't use the default language */
3271 	/* GPOS/GSUB may have different language sets, so we must be prepared */
3272 	templang = lang;
3273 	langs = SFLangsInScript(sf,isgpos,script);
3274 	for ( i=0; langs[i]!=0 && langs[i]!=lang; ++i );
3275 	if ( langs[i]==0 )
3276 	    templang = DEFAULT_LANG;
3277 	free(langs);
3278 
3279 	for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL ; otl = otl->next ) {
3280 	    uint32 tag;
3281 	    if ( (tag=FSLLMatches(otl->features,flist,script,templang))!=0 )
3282 		ApplyLookup(tag,otl,&data);
3283 	}
3284     }
3285     LigatureFree(&data);
3286     free(data.ligs);
3287 
3288     data.str = grealloc(data.str,(data.cnt+1)*sizeof(struct opentype_str));
3289     memset(&data.str[data.cnt],0,sizeof(struct opentype_str));
3290 return( data.str );
3291 }
3292 
doreplace(char ** haystack,char * start,char * search,char * rpl,int slen)3293 static void doreplace(char **haystack,char *start,char *search,char *rpl,int slen) {
3294     int rlen;
3295     char *pt = start+slen;
3296     (void)search;
3297     rlen = strlen(rpl);
3298     if ( slen>=rlen ) {
3299 	memcpy(start,rpl,rlen);
3300 	if ( slen>rlen ) {
3301 	    int diff = slen-rlen;
3302 	    for ( ; *pt ; ++pt )
3303 		pt[-diff] = *pt;
3304 	    pt[-diff] = '\0';
3305 	}
3306     } else {
3307 	char *base = *haystack;
3308 	char *new = galloc(pt-base+strlen(pt)+rlen-slen+1);
3309 	memcpy(new,base,start-base);
3310 	memcpy(new+(start-base),rpl,rlen);
3311 	strcpy(new+(start-base)+rlen,pt);
3312 	free( base );
3313 	*haystack = new;
3314     }
3315 }
3316 
rplstr(char ** haystack,char * search,char * rpl,int multipleoccurances)3317 static int rplstr(char **haystack,char *search, char *rpl,int multipleoccurances) {
3318     char *start, *pt, *base = *haystack;
3319     int ch, match, slen = strlen(search);
3320     int any = 0;
3321 
3322     if ( base==NULL )
3323 return( false );
3324 
3325     for ( pt=base ; ; ) {
3326 	while ( *pt==' ' ) ++pt;
3327 	if ( *pt=='\0' )
3328 return( any );
3329 	start=pt;
3330 	while ( *pt!=' ' && *pt!='\0' ) ++pt;
3331 	if ( pt-start!=slen )
3332 	    match = -1;
3333 	else {
3334 	    ch = *pt; *pt='\0';
3335 	    match = strcmp(start,search);
3336 	    *pt = ch;
3337 	}
3338 	if ( match==0 ) {
3339 	    doreplace(haystack,start,search,rpl,slen);
3340 	    if ( !multipleoccurances )
3341 return( true );
3342 	    any = true;
3343 	    if ( base!=*haystack ) {
3344 		pt = *haystack + (start-base)+strlen(rpl);
3345 		base = *haystack;
3346 	    } else
3347 		pt = start+strlen(rpl);
3348 	}
3349     }
3350 }
3351 
rplglyphname(char ** haystack,char * search,char * rpl)3352 static int rplglyphname(char **haystack,char *search, char *rpl) {
3353     /* If we change "f" to "uni0066" then we should also change "f.sc" to */
3354     /*  "uni0066.sc" and "f_f_l" to "uni0066_uni0066_l" */
3355     char *start, *pt, *base = *haystack;
3356     int ch, match;
3357     unsigned slen = strlen(search);
3358     int any = 0;
3359 
3360     if ( slen>=strlen( base ))
3361 return( false );
3362 
3363     for ( pt=base ; ; ) {
3364 	while ( *pt=='_' ) ++pt;
3365 	if ( *pt=='\0' || *pt=='.' )
3366 return( any );
3367 	start=pt;
3368 	while ( *pt!='_' && *pt!='\0' && *pt!='.' ) ++pt;
3369 	if ( *pt=='\0' && start==base )	/* Don't change any unsegmented names */
3370 return( false );			/* In particular don't rename ourselves*/
3371 	if ( pt-start!=slen )
3372 	    match = -1;
3373 	else {
3374 	    ch = *pt; *pt='\0';
3375 	    match = strcmp(start,search);
3376 	    *pt = ch;
3377 	}
3378 	if ( match==0 ) {
3379 	    doreplace(haystack,start,search,rpl,slen);
3380 	    any = true;
3381 	    if ( base!=*haystack ) {
3382 		pt = *haystack + (start-base) + strlen(rpl);
3383 		base = *haystack;
3384 	    } else
3385 		pt = start+strlen(rpl);
3386 	}
3387     }
3388 }
3389 
glyphnameIsComponent(char * haystack,char * search)3390 static int glyphnameIsComponent(char *haystack,char *search) {
3391     /* Check for a glyph name in ligature names and dotted names */
3392     char *start, *pt;
3393     unsigned slen = strlen(search);
3394 
3395     if ( slen>=strlen( haystack ))
3396 return( false );
3397 
3398     for ( pt=haystack ; ; ) {
3399 	while ( *pt=='_' ) ++pt;
3400 	if ( *pt=='\0' || *pt=='.' )
3401 return( false );
3402 	start=pt;
3403 	while ( *pt!='_' && *pt!='\0' && *pt!='.' ) ++pt;
3404 	if ( *pt=='\0' && start==haystack )/* Don't change any unsegmented names */
3405 return( false );			/* In particular don't rename ourselves*/
3406 	if ( pt-start==slen && strncmp(start,search,slen)==0 )
3407 return( true );
3408     }
3409 }
3410 
gvfixup(struct glyphvariants * gv,char * old,char * new)3411 static int gvfixup(struct glyphvariants *gv,char *old, char *new) {
3412     int i;
3413     int ret=0;
3414 
3415     if ( gv==NULL )
3416 return( false );
3417     ret = rplstr(&gv->variants,old,new,false);
3418     for ( i=0; i<gv->part_cnt; ++i ) {
3419 	if ( strcmp(gv->parts[i].component,old)==0 ) {
3420 	    free( gv->parts[i].component);
3421 	    gv->parts[i].component = copy(new);
3422 	    ret = true;
3423 	}
3424     }
3425 return( ret );
3426 }
3427 
SFSubTableFindOrMake(SplineFont * sf,uint32 tag,uint32 script,int lookup_type)3428 struct lookup_subtable *SFSubTableFindOrMake(SplineFont *sf,uint32 tag,uint32 script,
3429 	int lookup_type ) {
3430     OTLookup **base;
3431     OTLookup *otl, *found=NULL;
3432     int isgpos = lookup_type>=gpos_start;
3433     struct lookup_subtable *sub;
3434     int isnew = false;
3435 
3436     if ( sf->cidmaster ) sf = sf->cidmaster;
3437     base = isgpos ? &sf->gpos_lookups : &sf->gsub_lookups;
3438     for ( otl= *base; otl!=NULL; otl=otl->next ) {
3439       if ( otl->lookup_type==(unsigned)lookup_type &&
3440 		FeatureScriptTagInFeatureScriptList(tag,script,otl->features) ) {
3441 	    for ( sub = otl->subtables; sub!=NULL; sub=sub->next )
3442 		if ( sub->kc==NULL )
3443 return( sub );
3444 	    found = otl;
3445 	}
3446     }
3447 
3448     if ( found==NULL ) {
3449 	found = chunkalloc(sizeof(OTLookup));
3450 	found->lookup_type = lookup_type;
3451 	found->features = chunkalloc(sizeof(FeatureScriptLangList));
3452 	found->features->featuretag = tag;
3453 	found->features->scripts = chunkalloc(sizeof(struct scriptlanglist));
3454 	found->features->scripts->script = script;
3455 	found->features->scripts->langs[0] = DEFAULT_LANG;
3456 	found->features->scripts->lang_cnt = 1;
3457 
3458 	SortInsertLookup(sf, found);
3459 	isnew = true;
3460     }
3461 
3462     sub = chunkalloc(sizeof(struct lookup_subtable));
3463     sub->next = found->subtables;
3464     found->subtables = sub;
3465     sub->lookup = found;
3466     sub->per_glyph_pst_or_kern = true;
3467 
3468     NameOTLookup(found,sf);
3469 return( sub );
3470 }
3471 
SFSubTableMake(SplineFont * sf,uint32 tag,uint32 script,int lookup_type)3472 struct lookup_subtable *SFSubTableMake(SplineFont *sf,uint32 tag,uint32 script,
3473 	int lookup_type ) {
3474     OTLookup **base;
3475     OTLookup *otl, *found=NULL;
3476     int isgpos = lookup_type>=gpos_start;
3477     struct lookup_subtable *sub;
3478     int isnew = false;
3479 
3480     if ( sf->cidmaster ) sf = sf->cidmaster;
3481     base = isgpos ? &sf->gpos_lookups : &sf->gsub_lookups;
3482     for ( otl= *base; otl!=NULL; otl=otl->next ) {
3483       if ( otl->lookup_type==(unsigned)lookup_type &&
3484 		FeatureScriptTagInFeatureScriptList(tag,script,otl->features) ) {
3485 	    found = otl;
3486 	}
3487     }
3488 
3489     if ( found==NULL ) {
3490 	found = chunkalloc(sizeof(OTLookup));
3491 	found->lookup_type = lookup_type;
3492 	found->features = chunkalloc(sizeof(FeatureScriptLangList));
3493 	found->features->featuretag = tag;
3494 	found->features->scripts = chunkalloc(sizeof(struct scriptlanglist));
3495 	found->features->scripts->script = script;
3496 	found->features->scripts->langs[0] = DEFAULT_LANG;
3497 	found->features->scripts->lang_cnt = 1;
3498 
3499 	SortInsertLookup(sf, found);
3500 	isnew = true;
3501     }
3502 
3503     sub = chunkalloc(sizeof(struct lookup_subtable));
3504     sub->next = found->subtables;
3505     found->subtables = sub;
3506     sub->lookup = found;
3507 
3508     if ( isnew )
3509 	NameOTLookup(found,sf);
3510 return( sub );
3511 }
3512 
LookupUsedNested(SplineFont * sf,OTLookup * checkme)3513 int LookupUsedNested(SplineFont *sf,OTLookup *checkme) {
3514     OTLookup *otl;
3515     struct lookup_subtable *sub;
3516     int r,c;
3517 
3518     if ( checkme->lookup_type>=gpos_start )
3519 	otl = sf->gpos_lookups;
3520     else
3521 	otl = sf->gsub_lookups;
3522     while ( otl!=NULL ) {
3523 	for ( sub = otl->subtables; sub!=NULL; sub=sub->next ) {
3524 	    if ( sub->fpst!=NULL ) {
3525 		for ( r=0; r<sub->fpst->rule_cnt; ++r ) {
3526 		    struct fpst_rule *rule = &sub->fpst->rules[r];
3527 		    for ( c=0; c<rule->lookup_cnt; ++c ) {
3528 			if ( rule->lookups[c].lookup == checkme )
3529 return( true );
3530 		    }
3531 		}
3532 	    }
3533 	}
3534     }
3535 return( false );
3536 }
3537 
AALTRemoveOld(SplineFont * sf)3538 static void AALTRemoveOld(SplineFont *sf) {
3539     FeatureScriptLangList *fl, *prev;
3540     OTLookup *otl, *otlnext;
3541 
3542     for ( otl=sf->gsub_lookups; otl!=NULL; otl=otlnext ) {
3543 	otlnext = otl->next;
3544 	prev = NULL;
3545 	for ( fl = otl->features; fl!=NULL; prev=fl, fl=fl->next ) {
3546 	    if ( fl->featuretag==CHR('a','a','l','t') ) {
3547 		if ( fl==otl->features && fl->next==NULL && !LookupUsedNested(sf,otl))
3548 		    SFRemoveLookup(sf,otl);
3549 		else {
3550 		    if ( prev==NULL )
3551 			otl->features = fl->next;
3552 		    else
3553 			prev->next = fl->next;
3554 		    fl->next = NULL;
3555 		    FeatureScriptLangListFree(fl);
3556 		}
3557 	break;
3558 	    }
3559 	}
3560     }
3561 }
3562 
SllkFree(struct sllk * sllk,int sllk_cnt)3563 void SllkFree(struct sllk *sllk,int sllk_cnt) {
3564     int i;
3565 
3566     for ( i=0; i<sllk_cnt; ++i ) {
3567 	free( sllk[i].langs );
3568 	free( sllk[i].lookups );
3569     }
3570     free(sllk);
3571 }
3572 
AddOTLToSllk(struct sllk * sllk,OTLookup * otl,struct scriptlanglist * sl)3573 static void AddOTLToSllk(struct sllk *sllk, OTLookup *otl, struct scriptlanglist *sl) {
3574     int i,j,k,l;
3575 
3576     if ( otl->lookup_type==gsub_single || otl->lookup_type==gsub_alternate ) {
3577 	for ( i=0; i<sllk->cnt; ++i )
3578 	    if ( sllk->lookups[i]==otl )
3579 	break;
3580 	if ( i==sllk->cnt ) {
3581 	    if ( sllk->cnt>=sllk->max )
3582 		sllk->lookups = grealloc(sllk->lookups,(sllk->max+=5)*sizeof(OTLookup *));
3583 	    sllk->lookups[sllk->cnt++] = otl;
3584 	    for ( l=0; l<sl->lang_cnt; ++l ) {
3585 		uint32 lang = l<MAX_LANG ? sl->langs[l] : sl->morelangs[l-MAX_LANG];
3586 		for ( j=0; j<sllk->lcnt; ++j )
3587 		    if ( sllk->langs[j]==lang )
3588 		break;
3589 		if ( j==sllk->lcnt ) {
3590 		    if ( sllk->lcnt>=sllk->lmax )
3591 			sllk->langs = grealloc(sllk->langs,(sllk->lmax+=sl->lang_cnt+MAX_LANG)*sizeof(uint32));
3592 		    sllk->langs[sllk->lcnt++] = lang;
3593 		}
3594 	    }
3595 	}
3596     } else if ( otl->lookup_type==gsub_context || otl->lookup_type==gsub_contextchain ) {
3597 	struct lookup_subtable *sub;
3598 	for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
3599 	    FPST *fpst = sub->fpst;
3600 	    for ( j=0; j<fpst->rule_cnt; ++j ) {
3601 		struct fpst_rule *r = &fpst->rules[j];
3602 		for ( k=0; k<r->lookup_cnt; ++k )
3603 		    AddOTLToSllk(sllk,r->lookups[k].lookup,sl);
3604 	    }
3605 	}
3606     }
3607     /* reverse contextual chaining is weird and I shall ignore it. Adobe does too*/
3608 }
3609 
ComponentsFromPSTs(PST ** psts,int pcnt)3610 static char *ComponentsFromPSTs(PST **psts,int pcnt) {
3611     char **names=NULL;
3612     int ncnt=0, nmax=0;
3613     int i,j,len;
3614     char *ret;
3615 
3616     /* First find all the names */
3617     for ( i=0; i<pcnt; ++i ) {
3618 	char *nlist = psts[i]->u.alt.components;
3619 	char *start, *pt, ch;
3620 
3621 	for ( start = nlist; ; ) {
3622 	    while ( *start==' ' )
3623 		++start;
3624 	    if ( *start=='\0' )
3625 	break;
3626 	    for ( pt=start; *pt!=' ' && *pt!='\0'; ++pt );
3627 	    ch = *pt; *pt = '\0';
3628 	    for ( j=0; j<ncnt; ++j )
3629 		if ( strcmp( start,names[j])==0 )
3630 	    break;
3631 	    if ( j==ncnt ) {
3632 		if ( ncnt>=nmax )
3633 		    names = grealloc(names,(nmax+=10)*sizeof(char *));
3634 		names[ncnt++] = copy(start);
3635 	    }
3636 	    *pt = ch;
3637 	    start = pt;
3638 	}
3639     }
3640 
3641     len = 0;
3642     for ( i=0; i<ncnt; ++i )
3643 	len += strlen(names[i])+1;
3644     if ( len==0 ) len=1;
3645     ret = galloc(len);
3646     len = 0;
3647     for ( i=0; i<ncnt; ++i ) {
3648 	strcpy(ret+len,names[i]);
3649 	len += strlen(names[i]);
3650 	ret[len++] = ' ';
3651     }
3652     if ( len==0 )
3653 	*ret = '\0';
3654     else
3655 	ret[len-1] = '\0';
3656 
3657     for ( i=0; i<ncnt; ++i )
3658 	free(names[i]);
3659     free(names);
3660 return( ret );
3661 }
3662 
SllkMatch(struct sllk * sllk,int s1,int s2)3663 static int SllkMatch(struct sllk *sllk,int s1,int s2) {
3664     int i;
3665 
3666     if ( sllk[s1].cnt != sllk[s2].cnt )
3667 return( false );
3668 
3669     for ( i=0; i<sllk[s1].cnt; ++i ) {
3670 	if ( sllk[s1].lookups[i] != sllk[s2].lookups[i] )
3671 return( false );
3672     }
3673 
3674 return( true );
3675 }
3676 
AddOTLToSllks(OTLookup * otl,struct sllk * sllk,int * _sllk_cnt,int * _sllk_max)3677 struct sllk *AddOTLToSllks( OTLookup *otl, struct sllk *sllk,
3678 	int *_sllk_cnt, int *_sllk_max ) {
3679     FeatureScriptLangList *fl;
3680     struct scriptlanglist *sl;
3681     int s;
3682 
3683     for ( fl=otl->features; fl!=NULL; fl=fl->next ) {
3684 	for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) {
3685 	    for ( s=0; s<*_sllk_cnt; ++s )
3686 		if ( sl->script == sllk[s].script )
3687 	    break;
3688 	    if ( s==*_sllk_cnt ) {
3689 		if ( *_sllk_cnt>=*_sllk_max )
3690 		    sllk = grealloc(sllk,((*_sllk_max)+=10)*sizeof(struct sllk));
3691 		memset(&sllk[*_sllk_cnt],0,sizeof(struct sllk));
3692 		sllk[(*_sllk_cnt)++].script = sl->script;
3693 	    }
3694 	    AddOTLToSllk(&sllk[s], otl,sl);
3695 	}
3696     }
3697 return( sllk );
3698 }
3699 
NewAALTLookup(SplineFont * sf,struct sllk * sllk,int sllk_cnt,int i)3700 OTLookup *NewAALTLookup(SplineFont *sf,struct sllk *sllk, int sllk_cnt, int i) {
3701     OTLookup *otl;
3702     struct lookup_subtable *sub;
3703     FeatureScriptLangList *fl;
3704     struct scriptlanglist *sl;
3705     PST **psts, *pst;
3706     int j,k,l;
3707     int gid,pcnt;
3708     SplineFont *_sf;
3709     SplineChar *sc;
3710 
3711     /* Make the new lookup (and all its supporting data structures) */
3712     otl = chunkalloc(sizeof(OTLookup));
3713     otl->lookup_type = gsub_alternate;
3714     otl->lookup_flags = sllk[i].lookups[0]->lookup_flags & pst_r2l;
3715     otl->features = fl = chunkalloc(sizeof(FeatureScriptLangList));
3716     fl->featuretag = CHR('a','a','l','t');
3717     /* Any other scripts with the same lookup set? */
3718     for ( j=i; j<sllk_cnt; ++j ) {
3719 	if ( i==j || SllkMatch(sllk,i,j)) {
3720 	    sl = chunkalloc(sizeof(struct scriptlanglist));
3721 	    sl->next = fl->scripts;
3722 	    fl->scripts = sl;
3723 	    sl->script = sllk[j].script;
3724 	    sl->lang_cnt = sllk[j].lcnt;
3725 	    if ( sl->lang_cnt>MAX_LANG )
3726 		sl->morelangs = galloc((sl->lang_cnt-MAX_LANG)*sizeof(uint32));
3727 	    for ( l=0; l<sl->lang_cnt; ++l )
3728 		if ( l<MAX_LANG )
3729 		    sl->langs[l] = sllk[j].langs[l];
3730 		else
3731 		    sl->morelangs[l-MAX_LANG] = sllk[j].langs[l];
3732 	    if ( i!=j ) sllk[j].cnt = 0;	/* Mark as processed */
3733 	}
3734     }
3735     otl->subtables = sub = chunkalloc(sizeof(struct lookup_subtable));
3736     sub->lookup = otl;
3737     sub->per_glyph_pst_or_kern = true;
3738 
3739     /* Add it to the various lists it needs to be in */
3740     otl->next = sf->gsub_lookups;
3741     sf->gsub_lookups = otl;
3742 
3743     /* Now look at every glyph in the font, and see if it has any of the */
3744     /*  lookups we are interested in, and if it does, build a new pst */
3745     /*  containing all posibilities listed on any of them */
3746     if ( sf->cidmaster ) sf = sf->cidmaster;
3747     psts = galloc(sllk[i].cnt*sizeof(PST *));
3748     k=0;
3749     do {
3750 	_sf = k<sf->subfontcnt ? sf->subfonts[k] : sf;
3751 	for ( gid=0; gid<_sf->glyphcnt; ++gid ) if ( (sc = _sf->glyphs[gid])!=NULL ) {
3752 	    pcnt = 0;
3753 	    for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
3754 		if ( pst->subtable==NULL )
3755 	    continue;
3756 		for ( j=0; j<sllk[i].cnt; ++j )
3757 		    if ( pst->subtable->lookup == sllk[i].lookups[j] )
3758 		break;
3759 		if ( j<sllk[i].cnt )
3760 		    psts[pcnt++] = pst;
3761 	    }
3762 	    if ( pcnt==0 )
3763 	continue;
3764 	    pst = chunkalloc(sizeof(PST));
3765 	    pst->subtable = sub;
3766 	    pst->type = pst_alternate;
3767 	    pst->next = sc->possub;
3768 	    sc->possub = pst;
3769 	    pst->u.alt.components = ComponentsFromPSTs(psts,pcnt);
3770 	}
3771 	++k;
3772     } while ( k<sf->subfontcnt );
3773     free(psts);
3774     NameOTLookup(otl,sf);
3775 return( otl );
3776 }
3777 
AddNewAALTFeatures(SplineFont * sf)3778 void AddNewAALTFeatures(SplineFont *sf) {
3779     /* different script/lang combinations may need different 'aalt' lookups */
3780     /*  well, let's just say different script combinations */
3781     /* for each script/lang combo find all single/alternate subs for each */
3782     /*  glyph. Merge those choices and create new lookup with that info */
3783     struct sllk *sllk = NULL;
3784     int sllk_cnt=0, sllk_max = 0;
3785     int i;
3786     OTLookup *otl;
3787 
3788     AALTRemoveOld(sf);
3789 
3790     /* Find all scripts, and all the single/alternate lookups for each */
3791     /*  and all the languages used for these in each script */
3792     for ( otl=sf->gsub_lookups; otl!=NULL; otl=otl->next ) {
3793 	sllk = AddOTLToSllks( otl, sllk, &sllk_cnt, &sllk_max );
3794     }
3795     /* Each of these gets its own gsub_alternate lookup which gets inserted */
3796     /*  at the head of the lookup list. Each lookup has one subtable */
3797     for ( i=0; i<sllk_cnt; ++i ) {
3798 	if ( sllk[i].cnt==0 )		/* Script used, but provides no alternates */
3799     continue;
3800 	NewAALTLookup(sf,sllk,sllk_cnt,i);
3801     }
3802 
3803     SllkFree(sllk,sllk_cnt);
3804 }
3805 
3806 
VerticalKernFeature(SplineFont * sf,OTLookup * otl,int ask)3807 int VerticalKernFeature(SplineFont *sf, OTLookup *otl, int ask) {
3808     FeatureScriptLangList *fl;
3809     struct lookup_subtable *sub;
3810     KernClass *kc;
3811     char *buts[3];
3812 
3813     for ( fl=otl->features; fl!=NULL; fl=fl->next ) {
3814 	if ( fl->featuretag==CHR('k','e','r','n') )
3815 return( false );
3816 	else if ( fl->featuretag==CHR('v','k','r','n') )
3817 return( true );
3818     }
3819 
3820     for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
3821 	if ( sub->kc!=NULL ) {
3822 	    for ( kc=sf->kerns; kc!=NULL; kc=kc->next )
3823 		if ( kc==sub->kc )
3824 return( false );
3825 	    for ( kc=sf->vkerns; kc!=NULL; kc=kc->next )
3826 		if ( kc==sub->kc )
3827 return( true );
3828 	}
3829     }
3830 
3831     if ( !ask )
3832 return( -1 );
3833 
3834     buts[0] = _("_Horizontal"); buts[1] = _("_Vertical"); buts[2] = NULL;
3835 return( ff_ask(_("Kerning direction"),(const char **) buts,0,1,_("Is this horizontal or vertical kerning data?")) );
3836 }
3837 
IsAnchorClassUsed(SplineChar * sc,AnchorClass * an)3838 int IsAnchorClassUsed(SplineChar *sc,AnchorClass *an) {
3839     AnchorPoint *ap;
3840     int waslig=0, sawentry=0, sawexit=0;
3841 
3842     for ( ap=sc->anchor; ap!=NULL; ap=ap->next ) {
3843 	if ( ap->anchor==an ) {
3844 	    if ( ap->type==at_centry )
3845 		sawentry = true;
3846 	    else if ( ap->type==at_cexit )
3847 		sawexit = true;
3848 	    else if ( an->type==act_mkmk ) {
3849 		if ( ap->type==at_basemark )
3850 		    sawexit = true;
3851 		else
3852 		    sawentry = true;
3853 	    } else if ( ap->type!=at_baselig )
3854 return( -1 );
3855 	    else if ( waslig<ap->lig_index+1 )
3856 		waslig = ap->lig_index+1;
3857 	}
3858     }
3859     if ( sawentry && sawexit )
3860 return( -1 );
3861     else if ( sawentry )
3862 return( -2 );
3863     else if ( sawexit )
3864 return( -3 );
3865 return( waslig );
3866 }
3867 
PSTContains(const char * components,const char * name)3868 int PSTContains(const char *components,const char *name) {
3869     const char *pt;
3870     int len = strlen(name);
3871 
3872     for ( pt = strstr(components,name); pt!=NULL; pt = strstr(pt+len,name)) {
3873 	if (( pt==components || pt[-1]==' ') && (pt[len]==' ' || pt[len]=='\0'))
3874 return( true );
3875     }
3876 return( false );
3877 }
3878 
KernClassContains(KernClass * kc,char * name1,char * name2,int ordered)3879 int KernClassContains(KernClass *kc, char *name1, char *name2, int ordered ) {
3880     int infirst=0, insecond=0, scpos1=0, kwpos1=0, scpos2=0, kwpos2=0;
3881     int i;
3882 
3883     for ( i=1; i<kc->first_cnt; ++i ) {
3884 	if ( PSTContains(kc->firsts[i],name1) ) {
3885 	    scpos1 = i;
3886 	    if ( ++infirst>=3 )		/* The name occurs twice??? */
3887     break;
3888 	} else if ( PSTContains(kc->firsts[i],name2) ) {
3889 	    kwpos1 = i;
3890 	    if ( (infirst+=2)>=3 )
3891     break;
3892 	}
3893     }
3894     if ( infirst==0 || infirst>3 )
3895 return( 0 );
3896     for ( i=1; i<kc->second_cnt; ++i ) {
3897 	if ( PSTContains(kc->seconds[i],name1) ) {
3898 	    scpos2 = i;
3899 	    if ( ++insecond>=3 )
3900     break;
3901 	} else if ( PSTContains(kc->seconds[i],name2) ) {
3902 	    kwpos2 = i;
3903 	    if ( (insecond+=2)>=3 )
3904     break;
3905 	}
3906     }
3907     if ( insecond==0 || insecond>3 )
3908 return( 0 );
3909     if ( (infirst&1) && (insecond&2) ) {
3910 	if ( kc->offsets[scpos1*kc->second_cnt+kwpos2]!=0 )
3911 return( kc->offsets[scpos1*kc->second_cnt+kwpos2] );
3912     }
3913     if ( !ordered ) {
3914 	if ( (infirst&2) && (insecond&1) ) {
3915 	    if ( kc->offsets[kwpos1*kc->second_cnt+scpos2]!=0 )
3916 return( kc->offsets[kwpos1*kc->second_cnt+scpos2] );
3917 	}
3918     }
3919 return( 0 );
3920 }
3921 
KCFindName(char * name,char ** classnames,int cnt)3922 int KCFindName(char *name, char **classnames, int cnt ) {
3923     int i;
3924     char *pt, *end, ch;
3925 
3926     for ( i=0; i<cnt; ++i ) {
3927 	if ( classnames[i]==NULL )
3928     continue;
3929 	for ( pt = classnames[i]; *pt; pt=end+1 ) {
3930 	    end = strchr(pt,' ');
3931 	    if ( end==NULL ) end = pt+strlen(pt);
3932 	    ch = *end;
3933 	    *end = '\0';
3934 	    if ( strcmp(pt,name)==0 ) {
3935 		*end = ch;
3936 return( i );
3937 	    }
3938 	    *end = ch;
3939 	    if ( ch=='\0' )
3940 	break;
3941 	}
3942     }
3943 return( 0 );
3944 }
3945 
KCFindIndex(KernClass * kc,char * name1,char * name2)3946 int KCFindIndex(KernClass *kc,char *name1, char *name2) {
3947     int f,l;
3948 
3949     f = KCFindName(name1,kc->firsts,kc->first_cnt);
3950     l = KCFindName(name2,kc->seconds,kc->second_cnt);
3951     if ( (f!=0 || kc->firsts[0]!=NULL) && l!=0 )
3952 return( f*kc->second_cnt+l );
3953 
3954 return( -1 );
3955 }
3956 
3957 
NOFI_SortInsertLookup(SplineFont * sf,OTLookup * newotl)3958 static void NOFI_SortInsertLookup(SplineFont *sf, OTLookup *newotl) {
3959   (void)sf;
3960   (void)newotl;
3961 }
3962 
NOFI_OTLookupCopyInto(SplineFont * into_sf,SplineFont * from_sf,OTLookup * from_otl,OTLookup * to_otl,int scnt,OTLookup * before)3963 static void NOFI_OTLookupCopyInto(SplineFont *into_sf,SplineFont *from_sf,
3964 	OTLookup *from_otl, OTLookup *to_otl, int scnt, OTLookup *before ) {
3965   (void)into_sf;
3966   (void)from_sf;
3967   (void)from_otl;
3968   (void)to_otl;
3969   (void)scnt;
3970   (void)before;
3971 }
3972 
NOFI_Destroy(SplineFont * sf)3973 static void NOFI_Destroy(SplineFont *sf) {
3974   (void)sf;
3975 }
3976 
3977 struct fi_interface noui_fi = {
3978     NOFI_SortInsertLookup,
3979     NOFI_OTLookupCopyInto,
3980     NOFI_Destroy
3981 };
3982 
3983 struct fi_interface *fi_interface = &noui_fi;
3984 
FF_SetFIInterface(struct fi_interface * fii)3985 void FF_SetFIInterface(struct fi_interface *fii) {
3986     fi_interface = fii;
3987 }
3988