1 /* -*- coding: utf-8 -*- */
2 /* Copyright (C) 2007-2012 by George Williams */
3 /*
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6 
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9 
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13 
14  * The name of the author may not be used to endorse or promote products
15  * derived from this software without specific prior written permission.
16 
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <fontforge-config.h>
30 
31 #include "lookups.h"
32 
33 #include "chardata.h"
34 #include "fontforgevw.h"
35 #include "fvfonts.h"
36 #include "macenc.h"
37 #include "splinesaveafm.h"
38 #include "splineutil.h"
39 #include "tottfgpos.h"
40 #include "ttf.h"
41 #include "ustring.h"
42 #include "utype.h"
43 
44 #include <locale.h>
45 #include <math.h>
46 #include <stdarg.h>
47 #include <stdlib.h>
48 
49 struct opentype_feature_friendlynames friendlies[] = {
50     { CHR('a','a','l','t'),	"aalt", N_("Access All Alternates"),	gsub_single_mask|gsub_alternate_mask },
51     { CHR('a','b','v','f'),	"abvf", N_("Above Base Forms"),		gsub_single_mask|gsub_ligature_mask|gsub_context_mask|gsub_contextchain_mask },
52     { CHR('a','b','v','m'),	"abvm", N_("Above Base Mark"),		gpos_mark2base_mask|gpos_mark2ligature_mask },
53     { CHR('a','b','v','s'),	"abvs", N_("Above Base Substitutions"),	gsub_ligature_mask|gsub_context_mask|gsub_contextchain_mask },
54     { CHR('a','f','r','c'),	"afrc", N_("Vertical Fractions"),	gsub_ligature_mask },
55     { CHR('a','k','h','n'),	"akhn", N_("Akhand"),			gsub_ligature_mask },
56     { CHR('a','l','i','g'),	"alig", N_("Ancient Ligatures (Obsolete)"),	gsub_ligature_mask },
57     { CHR('b','l','w','f'),	"blwf", N_("Below Base Forms"),		gsub_ligature_mask|gsub_context_mask|gsub_contextchain_mask },
58     { CHR('b','l','w','m'),	"blwm", N_("Below Base Mark"),		gpos_mark2base_mask|gpos_mark2ligature_mask },
59     { CHR('b','l','w','s'),	"blws", N_("Below Base Substitutions"),	gsub_ligature_mask },
60     { CHR('c','2','p','c'),	"c2pc", N_("Capitals to Petite Capitals"),	gsub_single_mask },
61     { CHR('c','2','s','c'),	"c2sc", N_("Capitals to Small Capitals"),	gsub_single_mask },
62     { CHR('c','a','l','t'),	"calt", N_("Contextual Alternates"),	gsub_context_mask|gsub_contextchain_mask|morx_context_mask },
63     { CHR('c','a','s','e'),	"case", N_("Case-Sensitive Forms"),	gsub_single_mask|gpos_single_mask },
64     { CHR('c','c','m','p'),	"ccmp", N_("Glyph Composition/Decomposition"),	gsub_multiple_mask|gsub_ligature_mask },
65     { CHR('c','f','a','r'),	"cfar", N_("Conjunct Form After Ro"),	gsub_single_mask },
66     { CHR('c','j','c','t'),	"cjct", N_("Conjunct Forms"),		gsub_ligature_mask },
67     { CHR('c','l','i','g'),	"clig", N_("Contextual Ligatures"),	gsub_contextchain_mask|gsub_reversecchain_mask },
68     { CHR('c','p','c','t'),	"cpct", N_("Centered CJK Punctuation"),	gpos_single_mask },
69     { CHR('c','p','s','p'),	"cpsp", N_("Capital Spacing"),		gpos_single_mask },
70     { CHR('c','s','w','h'),	"cswh", N_("Contextual Swash"),		gsub_contextchain_mask|gsub_reversecchain_mask },
71     { CHR('c','u','r','s'),	"curs", N_("Cursive Attachment"),	gpos_cursive_mask },
72     { CHR('c','v','0','1'),	"cv01", N_("Character Variants 01"),	gsub_single_mask },
73     { CHR('c','v','0','2'),	"cv02", N_("Character Variants 02"),	gsub_single_mask },
74     { CHR('c','v','0','3'),	"cv03", N_("Character Variants 03"),	gsub_single_mask },
75     { CHR('c','v','0','4'),	"cv04", N_("Character Variants 04"),	gsub_single_mask },
76     { CHR('c','v','0','5'),	"cv05", N_("Character Variants 05"),	gsub_single_mask },
77     { CHR('c','v','0','6'),	"cv06", N_("Character Variants 06"),	gsub_single_mask },
78     { CHR('c','v','0','7'),	"cv07", N_("Character Variants 07"),	gsub_single_mask },
79     { CHR('c','v','0','8'),	"cv08", N_("Character Variants 08"),	gsub_single_mask },
80     { CHR('c','v','0','9'),	"cv09", N_("Character Variants 09"),	gsub_single_mask },
81     { CHR('c','v','1','0'),	"cv10", N_("Character Variants 10"),	gsub_single_mask },
82     { CHR('c','v','1','1'),	"cv11", N_("Character Variants 11"),    gsub_single_mask },
83     { CHR('c','v','1','2'),	"cv12", N_("Character Variants 12"),    gsub_single_mask },
84     { CHR('c','v','1','3'),	"cv13", N_("Character Variants 13"),    gsub_single_mask },
85     { CHR('c','v','1','4'),	"cv14", N_("Character Variants 14"),    gsub_single_mask },
86     { CHR('c','v','1','5'),	"cv15", N_("Character Variants 15"),    gsub_single_mask },
87     { CHR('c','v','1','6'),	"cv16", N_("Character Variants 16"),    gsub_single_mask },
88     { CHR('c','v','1','7'),	"cv17", N_("Character Variants 17"),    gsub_single_mask },
89     { CHR('c','v','1','8'),	"cv18", N_("Character Variants 18"),    gsub_single_mask },
90     { CHR('c','v','1','9'),	"cv19", N_("Character Variants 19"),    gsub_single_mask },
91     { CHR('c','v','2','0'),	"cv20", N_("Character Variants 20"),    gsub_single_mask },
92     { CHR('c','v','2','1'),	"cv21", N_("Character Variants 21"),    gsub_single_mask },
93     { CHR('c','v','2','2'),	"cv22", N_("Character Variants 22"),    gsub_single_mask },
94     { CHR('c','v','2','3'),	"cv23", N_("Character Variants 23"),    gsub_single_mask },
95     { CHR('c','v','2','4'),	"cv24", N_("Character Variants 24"),    gsub_single_mask },
96     { CHR('c','v','2','5'),	"cv25", N_("Character Variants 25"),    gsub_single_mask },
97     { CHR('c','v','2','6'),	"cv26", N_("Character Variants 26"),    gsub_single_mask },
98     { CHR('c','v','2','7'),	"cv27", N_("Character Variants 27"),    gsub_single_mask },
99     { CHR('c','v','2','8'),	"cv28", N_("Character Variants 28"),    gsub_single_mask },
100     { CHR('c','v','2','9'),	"cv29", N_("Character Variants 29"),    gsub_single_mask },
101     { CHR('c','v','3','0'),	"cv30", N_("Character Variants 30"),    gsub_single_mask },
102     { CHR('c','v','3','1'),	"cv31", N_("Character Variants 31"),    gsub_single_mask },
103     { CHR('c','v','3','2'),	"cv32", N_("Character Variants 32"),    gsub_single_mask },
104     { CHR('c','v','3','3'),	"cv33", N_("Character Variants 33"),    gsub_single_mask },
105     { CHR('c','v','3','4'),	"cv34", N_("Character Variants 34"),    gsub_single_mask },
106     { CHR('c','v','3','5'),	"cv35", N_("Character Variants 35"),    gsub_single_mask },
107     { CHR('c','v','3','6'),	"cv36", N_("Character Variants 36"),    gsub_single_mask },
108     { CHR('c','v','3','7'),	"cv37", N_("Character Variants 37"),    gsub_single_mask },
109     { CHR('c','v','3','8'),	"cv38", N_("Character Variants 38"),    gsub_single_mask },
110     { CHR('c','v','3','9'),	"cv39", N_("Character Variants 39"),    gsub_single_mask },
111     { CHR('c','v','4','0'),	"cv40", N_("Character Variants 40"),    gsub_single_mask },
112     { CHR('c','v','4','1'),	"cv41", N_("Character Variants 41"),    gsub_single_mask },
113     { CHR('c','v','4','2'),	"cv42", N_("Character Variants 42"),    gsub_single_mask },
114     { CHR('c','v','4','3'),	"cv43", N_("Character Variants 43"),    gsub_single_mask },
115     { CHR('c','v','4','4'),	"cv44", N_("Character Variants 44"),    gsub_single_mask },
116     { CHR('c','v','4','5'),	"cv45", N_("Character Variants 45"),    gsub_single_mask },
117     { CHR('c','v','4','6'),	"cv46", N_("Character Variants 46"),    gsub_single_mask },
118     { CHR('c','v','4','7'),	"cv47", N_("Character Variants 47"),    gsub_single_mask },
119     { CHR('c','v','4','8'),	"cv48", N_("Character Variants 48"),    gsub_single_mask },
120     { CHR('c','v','4','9'),	"cv49", N_("Character Variants 49"),    gsub_single_mask },
121     { CHR('c','v','5','0'),	"cv50", N_("Character Variants 50"),    gsub_single_mask },
122     { CHR('c','v','5','1'),	"cv51", N_("Character Variants 51"),    gsub_single_mask },
123     { CHR('c','v','5','2'),	"cv52", N_("Character Variants 52"),    gsub_single_mask },
124     { CHR('c','v','5','3'),	"cv53", N_("Character Variants 53"),    gsub_single_mask },
125     { CHR('c','v','5','4'),	"cv54", N_("Character Variants 54"),    gsub_single_mask },
126     { CHR('c','v','5','5'),	"cv55", N_("Character Variants 55"),    gsub_single_mask },
127     { CHR('c','v','5','6'),	"cv56", N_("Character Variants 56"),    gsub_single_mask },
128     { CHR('c','v','5','7'),	"cv57", N_("Character Variants 57"),    gsub_single_mask },
129     { CHR('c','v','5','8'),	"cv58", N_("Character Variants 58"),    gsub_single_mask },
130     { CHR('c','v','5','9'),	"cv59", N_("Character Variants 59"),    gsub_single_mask },
131     { CHR('c','v','6','0'),	"cv60", N_("Character Variants 60"),    gsub_single_mask },
132     { CHR('c','v','6','1'),	"cv61", N_("Character Variants 61"),    gsub_single_mask },
133     { CHR('c','v','6','2'),	"cv62", N_("Character Variants 62"),    gsub_single_mask },
134     { CHR('c','v','6','3'),	"cv63", N_("Character Variants 63"),    gsub_single_mask },
135     { CHR('c','v','6','4'),	"cv64", N_("Character Variants 64"),    gsub_single_mask },
136     { CHR('c','v','6','5'),	"cv65", N_("Character Variants 65"),    gsub_single_mask },
137     { CHR('c','v','6','6'),	"cv66", N_("Character Variants 66"),    gsub_single_mask },
138     { CHR('c','v','6','7'),	"cv67", N_("Character Variants 67"),    gsub_single_mask },
139     { CHR('c','v','6','8'),	"cv68", N_("Character Variants 68"),    gsub_single_mask },
140     { CHR('c','v','6','9'),	"cv69", N_("Character Variants 69"),    gsub_single_mask },
141     { CHR('c','v','7','0'),	"cv70", N_("Character Variants 70"),    gsub_single_mask },
142     { CHR('c','v','7','1'),	"cv71", N_("Character Variants 71"),    gsub_single_mask },
143     { CHR('c','v','7','2'),	"cv72", N_("Character Variants 72"),    gsub_single_mask },
144     { CHR('c','v','7','3'),	"cv73", N_("Character Variants 73"),    gsub_single_mask },
145     { CHR('c','v','7','4'),	"cv74", N_("Character Variants 74"),    gsub_single_mask },
146     { CHR('c','v','7','5'),	"cv75", N_("Character Variants 75"),    gsub_single_mask },
147     { CHR('c','v','7','6'),	"cv76", N_("Character Variants 76"),    gsub_single_mask },
148     { CHR('c','v','7','7'),	"cv77", N_("Character Variants 77"),    gsub_single_mask },
149     { CHR('c','v','7','8'),	"cv78", N_("Character Variants 78"),    gsub_single_mask },
150     { CHR('c','v','7','9'),	"cv79", N_("Character Variants 79"),    gsub_single_mask },
151     { CHR('c','v','8','0'),	"cv80", N_("Character Variants 80"),    gsub_single_mask },
152     { CHR('c','v','8','1'),	"cv81", N_("Character Variants 81"),    gsub_single_mask },
153     { CHR('c','v','8','2'),	"cv82", N_("Character Variants 82"),    gsub_single_mask },
154     { CHR('c','v','8','3'),	"cv83", N_("Character Variants 83"),    gsub_single_mask },
155     { CHR('c','v','8','4'),	"cv84", N_("Character Variants 84"),    gsub_single_mask },
156     { CHR('c','v','8','5'),	"cv85", N_("Character Variants 85"),    gsub_single_mask },
157     { CHR('c','v','8','6'),	"cv86", N_("Character Variants 86"),    gsub_single_mask },
158     { CHR('c','v','8','7'),	"cv87", N_("Character Variants 87"),    gsub_single_mask },
159     { CHR('c','v','8','8'),	"cv88", N_("Character Variants 88"),    gsub_single_mask },
160     { CHR('c','v','8','9'),	"cv89", N_("Character Variants 89"),    gsub_single_mask },
161     { CHR('c','v','9','0'),	"cv90", N_("Character Variants 90"),    gsub_single_mask },
162     { CHR('c','v','9','1'),	"cv91", N_("Character Variants 91"),    gsub_single_mask },
163     { CHR('c','v','9','2'),	"cv92", N_("Character Variants 92"),    gsub_single_mask },
164     { CHR('c','v','9','3'),	"cv93", N_("Character Variants 93"),    gsub_single_mask },
165     { CHR('c','v','9','4'),	"cv94", N_("Character Variants 94"),    gsub_single_mask },
166     { CHR('c','v','9','5'),	"cv95", N_("Character Variants 95"),    gsub_single_mask },
167     { CHR('c','v','9','6'),	"cv96", N_("Character Variants 96"),    gsub_single_mask },
168     { CHR('c','v','9','7'),	"cv97", N_("Character Variants 97"),    gsub_single_mask },
169     { CHR('c','v','9','8'),	"cv98", N_("Character Variants 98"),    gsub_single_mask },
170     { CHR('c','v','9','9'),	"cv99", N_("Character Variants 99"),	gsub_single_mask },
171     { CHR('d','c','a','p'),	"dcap", N_("Drop Caps (Obsolete)"),	gsub_single_mask },
172     { CHR('d','i','s','t'),	"dist", N_("Distance"),			gpos_pair_mask },
173     { CHR('d','l','i','g'),	"dlig", N_("Discretionary Ligatures"),	gsub_ligature_mask },
174     { CHR('d','n','o','m'),	"dnom", N_("Denominators"),		gsub_single_mask },
175     { CHR('d','p','n','g'),	"dpng", N_("Diphthongs (Obsolete)"),	gsub_ligature_mask },
176     { CHR('d','t','l','s'),	"dtls", N_("Dotless Forms"),		gsub_single_mask },
177     { CHR('e','x','p','t'),	"expt", N_("Expert Forms"),		gsub_single_mask },
178     { CHR('f','a','l','t'),	"falt", N_("Final Glyph On Line"),	gsub_alternate_mask },
179     { CHR('f','i','n','2'),	"fin2", N_("Terminal Forms #2"),	gsub_single_mask },
180     { CHR('f','i','n','3'),	"fin3", N_("Terminal Forms #3"),	gsub_single_mask },
181     { CHR('f','i','n','a'),	"fina", N_("Terminal Forms"),		gsub_single_mask },
182     { CHR('f','l','a','c'),	"flac", N_("Flattened Accents over Capitals"),	gsub_single_mask|gsub_ligature_mask },
183     { CHR('f','r','a','c'),	"frac", N_("Diagonal Fractions"),	gsub_single_mask|gsub_ligature_mask },
184     { CHR('f','w','i','d'),	"fwid", N_("Full Widths"),		gsub_single_mask|gpos_single_mask },
185     { CHR('h','a','l','f'),	"half", N_("Half Forms"),		gsub_ligature_mask },
186     { CHR('h','a','l','n'),	"haln", N_("Halant Forms"),		gsub_ligature_mask },
187     { CHR('h','a','l','t'),	"halt", N_("Alternative Half Widths"),	gpos_single_mask },
188     { CHR('h','i','s','t'),	"hist", N_("Historical Forms"),		gsub_single_mask },
189     { CHR('h','k','n','a'),	"hkna", N_("Horizontal Kana Alternatives"),	gsub_single_mask },
190     { CHR('h','l','i','g'),	"hlig", N_("Historic Ligatures"),	gsub_ligature_mask },
191     { CHR('h','n','g','l'),	"hngl", N_("Hanja to Hangul (Deprecated)"),	gsub_single_mask|gsub_alternate_mask },
192     { CHR('h','o','j','o'),	"hojo", N_("Hojo (JIS X 0212-1990) Kanji Forms"),	gsub_single_mask },
193     { CHR('h','w','i','d'),	"hwid", N_("Half Widths"),		gsub_single_mask|gpos_single_mask },
194     { CHR('i','n','i','t'),	"init", N_("Initial Forms"),		gsub_single_mask },
195     { CHR('i','s','o','l'),	"isol", N_("Isolated Forms"),		gsub_single_mask },
196     { CHR('i','t','a','l'),	"ital", N_("Italics"),			gsub_single_mask },
197     { CHR('j','a','l','t'),	"jalt", N_("Justification Alternatives"),	gsub_alternate_mask },
198     { CHR('j','a','j','p'),	"jajp", N_("Japanese Forms (Obsolete)"),	gsub_single_mask|gsub_alternate_mask },
199     { CHR('j','p','0','4'),	"jp04", N_("JIS2004 Forms"),		gsub_single_mask },
200     { CHR('j','p','7','8'),	"jp78", N_("JIS78 Forms"),		gsub_single_mask|gsub_alternate_mask },
201     { CHR('j','p','8','3'),	"jp83", N_("JIS83 Forms"),		gsub_single_mask },
202     { CHR('j','p','9','0'),	"jp90", N_("JIS90 Forms"),		gsub_single_mask },
203     { CHR('k','e','r','n'),	"kern", N_("Horizontal Kerning"),	gpos_pair_mask|gpos_context_mask|gpos_contextchain_mask|kern_statemachine_mask },
204     { CHR('l','f','b','d'),	"lfbd", N_("Left Bounds"),		gpos_single_mask },
205     { CHR('l','i','g','a'),	"liga", N_("Standard Ligatures"),	gsub_ligature_mask },
206     { CHR('l','j','m','o'),	"ljmo", N_("Leading Jamo Forms"),	gsub_ligature_mask },
207     { CHR('l','n','u','m'),	"lnum", N_("Lining Figures"),		gsub_single_mask },
208     { CHR('l','o','c','l'),	"locl", N_("Localized Forms"),		gsub_single_mask },
209     { CHR('l','t','r','a'),	"ltra", N_("Left to Right Alternates"),	gsub_single_mask },
210     { CHR('l','t','r','m'),	"ltrm", N_("Left to Right mirrored forms"),	gsub_single_mask },
211     { CHR('m','a','r','k'),	"mark", N_("Mark Positioning"),		gpos_mark2base_mask|gpos_mark2ligature_mask },
212     { CHR('m','e','d','2'),	"med2", N_("Medial Forms 2"),		gsub_single_mask },
213     { CHR('m','e','d','i'),	"medi", N_("Medial Forms"),		gsub_single_mask },
214     { CHR('m','g','r','k'),	"mgrk", N_("Mathematical Greek"),	gsub_single_mask },
215     { CHR('m','k','m','k'),	"mkmk", N_("Mark to Mark"),		gpos_mark2mark_mask },
216     { CHR('m','s','e','t'),	"mset", N_("Mark Positioning via Substitution"),	gsub_context_mask|gsub_contextchain_mask|morx_context_mask },
217     { CHR('n','a','l','t'),	"nalt", N_("Alternate Annotation Forms"),	gsub_single_mask|gsub_alternate_mask },
218     { CHR('n','l','c','k'),	"nlck", N_("NLC Kanji Forms"),		gsub_single_mask },
219     { CHR('n','u','k','t'),	"nukt", N_("Nukta Forms"),		gsub_ligature_mask },
220     { CHR('n','u','m','r'),	"numr", N_("Numerators"),		gsub_single_mask },
221     { CHR('o','n','u','m'),	"onum", N_("Oldstyle Figures"),		gsub_single_mask },
222     { CHR('o','p','b','d'),	"opbd", N_("Optical Bounds"),		gpos_single_mask },
223     { CHR('o','r','d','n'),	"ordn", N_("Ordinals"),			gsub_ligature_mask|gsub_context_mask|gsub_contextchain_mask|morx_context_mask },
224     { CHR('o','r','n','m'),	"ornm", N_("Ornaments"),		gsub_single_mask|gsub_alternate_mask },
225     { CHR('p','a','l','t'),	"palt", N_("Proportional Alternate Metrics"),	gpos_single_mask },
226     { CHR('p','c','a','p'),	"pcap", N_("Lowercase to Petite Capitals"),	gsub_single_mask },
227     { CHR('p','k','n','a'),	"pkna", N_("Proportional Kana"),	gpos_single_mask },
228     { CHR('p','n','u','m'),	"pnum", N_("Proportional Numbers"),	gsub_single_mask },
229     { CHR('p','r','e','f'),	"pref", N_("Pre Base Forms"),		gpos_single_mask|gsub_ligature_mask|gsub_context_mask|gsub_contextchain_mask },
230     { CHR('p','r','e','s'),	"pres", N_("Pre Base Substitutions"),	gsub_ligature_mask|gsub_context_mask|gsub_contextchain_mask|morx_context_mask },
231     { CHR('p','s','t','f'),	"pstf", N_("Post Base Forms"),		gsub_ligature_mask|gsub_context_mask|gsub_contextchain_mask },
232     { CHR('p','s','t','s'),	"psts", N_("Post Base Substitutions"),	gsub_ligature_mask|gsub_context_mask|gsub_contextchain_mask|morx_context_mask },
233     { CHR('p','w','i','d'),	"pwid", N_("Proportional Width"),	gsub_single_mask },
234     { CHR('q','w','i','d'),	"qwid", N_("Quarter Widths"),		gsub_single_mask|gpos_single_mask },
235     { CHR('r','a','n','d'),	"rand", N_("Randomize"),		gsub_alternate_mask },
236     { CHR('r','c','l','t'),	"rclt", N_("Required Contextual Alternates"),	gsub_context_mask|gsub_contextchain_mask|morx_context_mask },
237     { CHR('r','k','r','f'),	"rkrf", N_("Rakar Forms"),		gsub_ligature_mask },
238     { CHR('r','l','i','g'),	"rlig", N_("Required Ligatures"),	gsub_ligature_mask },
239     { CHR('r','p','h','f'),	"rphf", N_("Reph Form"),		gsub_ligature_mask },
240     { CHR('r','t','b','d'),	"rtbd", N_("Right Bounds"),		gpos_single_mask },
241     { CHR('r','t','l','a'),	"rtla", N_("Right to Left Alternates"),	gsub_single_mask },
242     { CHR('r','t','l','m'),	"rtlm", N_("Right to Left mirrored forms"),	gsub_single_mask },
243     { CHR('r','u','b','y'),	"ruby", N_("Ruby Notational Forms"),	gsub_single_mask },
244     { CHR('r','v','r','n'),	"rvrn", N_("Required Variation Alternates"),	gsub_single_mask },
245     { CHR('s','a','l','t'),	"salt", N_("Stylistic Alternatives"),	gsub_single_mask|gsub_alternate_mask },
246     { CHR('s','i','n','f'),	"sinf", N_("Scientific Inferiors"),	gsub_single_mask },
247     { CHR('s','m','c','p'),	"smcp", N_("Lowercase to Small Capitals"),	gsub_single_mask },
248     { CHR('s','m','p','l'),	"smpl", N_("Simplified Forms"),		gsub_single_mask },
249     { CHR('s','s','0','1'),	"ss01", N_("Style Set 1"),		gsub_single_mask },
250     { CHR('s','s','0','2'),	"ss02", N_("Style Set 2"),		gsub_single_mask },
251     { CHR('s','s','0','3'),	"ss03", N_("Style Set 3"),		gsub_single_mask },
252     { CHR('s','s','0','4'),	"ss04", N_("Style Set 4"),		gsub_single_mask },
253     { CHR('s','s','0','5'),	"ss05", N_("Style Set 5"),		gsub_single_mask },
254     { CHR('s','s','0','6'),	"ss06", N_("Style Set 6"),		gsub_single_mask },
255     { CHR('s','s','0','7'),	"ss07", N_("Style Set 7"),		gsub_single_mask },
256     { CHR('s','s','0','8'),	"ss08", N_("Style Set 8"),		gsub_single_mask },
257     { CHR('s','s','0','9'),	"ss09", N_("Style Set 9"),		gsub_single_mask },
258     { CHR('s','s','1','0'),	"ss10", N_("Style Set 10"),		gsub_single_mask },
259     { CHR('s','s','1','1'),	"ss11", N_("Style Set 11"),		gsub_single_mask },
260     { CHR('s','s','1','2'),	"ss12", N_("Style Set 12"),		gsub_single_mask },
261     { CHR('s','s','1','3'),	"ss13", N_("Style Set 13"),		gsub_single_mask },
262     { CHR('s','s','1','4'),	"ss14", N_("Style Set 14"),		gsub_single_mask },
263     { CHR('s','s','1','5'),	"ss15", N_("Style Set 15"),		gsub_single_mask },
264     { CHR('s','s','1','6'),	"ss16", N_("Style Set 16"),		gsub_single_mask },
265     { CHR('s','s','1','7'),	"ss17", N_("Style Set 17"),		gsub_single_mask },
266     { CHR('s','s','1','8'),	"ss18", N_("Style Set 18"),		gsub_single_mask },
267     { CHR('s','s','1','9'),	"ss19", N_("Style Set 19"),		gsub_single_mask },
268     { CHR('s','s','2','0'),	"ss20", N_("Style Set 20"),		gsub_single_mask },
269     { CHR('s','s','t','y'),	"ssty", N_("Math Script Style"),	gsub_single_mask|gsub_alternate_mask },
270     { CHR('s','t','c','h'),	"stch", N_("Stretching Glyph Decomposition"),	gsub_multiple_mask },
271     { CHR('s','u','b','s'),	"subs", N_("Subscript"),		gsub_single_mask },
272     { CHR('s','u','p','s'),	"sups", N_("Superscript"),		gsub_single_mask },
273     { CHR('s','w','s','h'),	"swsh", N_("Swash"),			gsub_single_mask|gsub_alternate_mask },
274     { CHR('t','i','t','l'),	"titl", N_("Titling"),			gsub_single_mask },
275     { CHR('t','j','m','o'),	"tjmo", N_("Trailing Jamo Forms"),	gsub_ligature_mask },
276     { CHR('t','n','a','m'),	"tnam", N_("Traditional Name Forms"),	gsub_single_mask },
277     { CHR('t','n','u','m'),	"tnum", N_("Tabular Numbers"),		gsub_single_mask },
278     { CHR('t','r','a','d'),	"trad", N_("Traditional Forms"),	gsub_single_mask|gsub_alternate_mask },
279     { CHR('t','w','i','d'),	"twid", N_("Third Widths"),		gsub_single_mask|gpos_single_mask },
280     { CHR('u','n','i','c'),	"unic", N_("Unicase"),			gsub_single_mask },
281     { CHR('v','a','l','t'),	"valt", N_("Alternate Vertical Metrics"),	gpos_single_mask },
282     { CHR('v','a','t','u'),	"vatu", N_("Vattu Variants"),		gsub_ligature_mask },
283     { CHR('v','e','r','t'),	"vert", N_("Vertical Alternates"),	gsub_single_mask },
284     { CHR('v','h','a','l'),	"vhal", N_("Alternate Vertical Half Metrics"),	gpos_single_mask },
285     { CHR('v','j','m','o'),	"vjmo", N_("Vowel Jamo Forms"),		gsub_ligature_mask },
286     { CHR('v','k','n','a'),	"vkna", N_("Vertical Kana Alternates"),	gsub_single_mask },
287     { CHR('v','k','r','n'),	"vkrn", N_("Vertical Kerning"),		gpos_pair_mask|gpos_context_mask|gpos_contextchain_mask|kern_statemachine_mask },
288     { CHR('v','p','a','l'),	"vpal", N_("Proportional Alternate Vertical Metrics"),	gpos_single_mask },
289     { CHR('v','r','t','2'),	"vrt2", N_("Vertical Rotation & Alternates"),	gsub_single_mask },
290     { CHR('v','r','t','r'),	"vrtr", N_("Vertical Alternates for Rotation"),	gsub_single_mask },
291     { CHR('z','e','r','o'),	"zero", N_("Slashed Zero"),		gsub_single_mask },
292 /* This is my hack for setting the "Required feature" field of a script */
293     { CHR(' ','R','Q','D'),	" RQD", N_("Required feature"),		gsub_single_mask|gsub_multiple_mask|gsub_alternate_mask|gsub_ligature_mask|gsub_context_mask|gsub_contextchain_mask|gsub_reversecchain_mask|morx_context_mask|gpos_single_mask|gpos_pair_mask|gpos_cursive_mask|gpos_mark2base_mask|gpos_mark2ligature_mask|gpos_mark2mark_mask|gpos_context_mask|gpos_contextchain_mask },
294     OPENTYPE_FEATURE_FRIENDLYNAMES_EMPTY
295 };
296 
uint32_cmp(const void * _ui1,const void * _ui2)297 static int uint32_cmp(const void *_ui1, const void *_ui2) {
298     if ( *(uint32 *) _ui1 > *(uint32 *)_ui2 )
299 return( 1 );
300     if ( *(uint32 *) _ui1 < *(uint32 *)_ui2 )
301 return( -1 );
302 
303 return( 0 );
304 }
305 
lang_cmp(const void * _ui1,const void * _ui2)306 static int lang_cmp(const void *_ui1, const void *_ui2) {
307     /* The default language is magic, and should come first in the list even */
308     /*  if that is not true alphabetical order */
309     if ( *(uint32 *) _ui1 == DEFAULT_LANG )
310 return( -1 );
311     if ( *(uint32 *) _ui2 == DEFAULT_LANG )
312 return( 1 );
313 
314     if ( *(uint32 *) _ui1 > *(uint32 *)_ui2 )
315 return( 1 );
316     if ( *(uint32 *) _ui1 < *(uint32 *)_ui2 )
317 return( -1 );
318 
319 return( 0 );
320 }
321 
FindFeatureTagInFeatureScriptList(uint32 tag,FeatureScriptLangList * fl)322 FeatureScriptLangList *FindFeatureTagInFeatureScriptList(uint32 tag, FeatureScriptLangList *fl) {
323 
324     while ( fl!=NULL ) {
325 	if ( fl->featuretag==tag )
326 return( fl );
327 	fl = fl->next;
328     }
329 return( NULL );
330 }
331 
FeatureTagInFeatureScriptList(uint32 tag,FeatureScriptLangList * fl)332 int FeatureTagInFeatureScriptList(uint32 tag, FeatureScriptLangList *fl) {
333 
334     while ( fl!=NULL ) {
335 	if ( fl->featuretag==tag )
336 return( true );
337 	fl = fl->next;
338     }
339 return( false );
340 }
341 
ScriptInFeatureScriptList(uint32 script,FeatureScriptLangList * fl)342 int ScriptInFeatureScriptList(uint32 script, FeatureScriptLangList *fl) {
343     struct scriptlanglist *sl;
344 
345     if ( fl==NULL )		/* No features bound to lookup? (nested?) don't restrict by script */
346 return( true );
347 
348     while ( fl!=NULL ) {
349 	for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) {
350 	    if ( sl->script == script )
351 return( true );
352 	}
353 	fl = fl->next;
354     }
355 return( false );
356 }
357 
FeatureScriptTagInFeatureScriptList(uint32 feature,uint32 script,FeatureScriptLangList * fl)358 int FeatureScriptTagInFeatureScriptList(uint32 feature, uint32 script, FeatureScriptLangList *fl) {
359     struct scriptlanglist *sl;
360 
361     while ( fl!=NULL ) {
362 	if ( fl->featuretag == feature ) {
363 	    for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) {
364 		if ( sl->script == script )
365 return( true );
366 	    }
367 	}
368 	fl = fl->next;
369     }
370 return( false );
371 }
372 
DefaultLangTagInOneScriptList(struct scriptlanglist * sl)373 int DefaultLangTagInOneScriptList(struct scriptlanglist *sl) {
374     int l;
375 
376     for ( l=0; l<sl->lang_cnt; ++l ) {
377 	uint32 lang = l<MAX_LANG ? sl->langs[l] : sl->morelangs[l-MAX_LANG];
378 	if ( lang==DEFAULT_LANG )
379 return( true );
380     }
381 return( false );
382 }
383 
DefaultLangTagInScriptList(struct scriptlanglist * sl,int DFLT_ok)384 struct scriptlanglist *DefaultLangTagInScriptList(struct scriptlanglist *sl, int DFLT_ok) {
385 
386     while ( sl!=NULL ) {
387 	if ( DFLT_ok || sl->script!=DEFAULT_SCRIPT ) {
388 	    if ( DefaultLangTagInOneScriptList(sl))
389 return( sl );
390 	}
391 	sl = sl->next;
392     }
393 return( NULL );
394 }
395 
SFScriptsInLookups(SplineFont * sf,int gpos)396 uint32 *SFScriptsInLookups(SplineFont *sf,int gpos) {
397     /* Presumes that either SFFindUnusedLookups or SFFindClearUnusedLookupBits */
398     /*  has been called first */
399     /* Since MS will sometimes ignore a script if it isn't found in both */
400     /*  GPOS and GSUB we want to return the same script list no matter */
401     /*  what the setting of gpos ... so we totally ignore that argument */
402     /*  and always look at both sets of lookups */
403 
404 /* Sergey Malkin from MicroSoft tells me:
405     Each shaping engine in Uniscribe can decide on its requirements for
406     layout tables - some of them require both GSUB and GPOS, in some cases
407     any table present is enough, or it can work without any table.
408 
409     Sometimes, purpose of the check is to determine if font is supporting
410     particular script - if required tables are not there font is just
411     rejected by this shaping engine. Sometimes, shaping engine can not just
412     reject the font because there are fonts using older shaping technologies
413     we still have to support, so it uses some logic when to fallback to
414     legacy layout code.
415 
416     In your case this is Hebrew, where both tables are required to use
417     OpenType processing. Arabic requires both tables too, Latin requires
418     GSUB to execute GPOS. But in general, if you have both tables you should
419     be safe with any script to get fully featured OpenType shaping.
420 
421 In other words, if we have a Hebrew font with just GPOS features they won't work,
422 and MS will not use the font at all. We must add a GSUB table. In the unlikely
423 event that we had a hebrew font with only GSUB it would not work either.
424 
425 So if we want our lookups to have a chance of executing under Uniscribe we
426 better make sure that both tables have the same script set.
427 
428 (Sergey says we could optimize a little: A Latin GSUB table will run without
429 a GPOS, but he says the GPOS won't work without a GSUB.)
430 */
431     int cnt=0, tot=0, i;
432     uint32 *scripts = NULL;
433     OTLookup *test;
434     FeatureScriptLangList *fl;
435     struct scriptlanglist *sl;
436 
437     /* So here always give scripts for both (see comment above) no */
438     /*  matter what they asked for */
439     for ( gpos=0; gpos<2; ++gpos ) {
440 	for ( test = gpos ? sf->gpos_lookups : sf->gsub_lookups; test!=NULL; test = test->next ) {
441 	    if ( test->unused )
442 	continue;
443 	    for ( fl=test->features; fl!=NULL; fl=fl->next ) {
444 		if ( fl->ismac )
445 	    continue;
446 		for ( sl=fl->scripts ; sl!=NULL; sl=sl->next ) {
447 		    for ( i=0; i<cnt; ++i ) {
448 			if ( sl->script==scripts[i] )
449 		    break;
450 		    }
451 		    if ( i==cnt ) {
452 			if ( cnt>=tot )
453 			    scripts = realloc(scripts,(tot+=10)*sizeof(uint32));
454 			scripts[cnt++] = sl->script;
455 		    }
456 		}
457 	    }
458 	}
459     }
460 
461     if ( cnt==0 )
462 return( NULL );
463 
464     /* We want our scripts in alphabetic order */
465     qsort(scripts,cnt,sizeof(uint32),uint32_cmp);
466     /* add a 0 entry to mark the end of the list */
467     if ( cnt>=tot )
468 	scripts = realloc(scripts,(tot+1)*sizeof(uint32));
469     scripts[cnt] = 0;
470 return( scripts );
471 }
472 
SFLangsInScript(SplineFont * sf,int gpos,uint32 script)473 uint32 *SFLangsInScript(SplineFont *sf,int gpos,uint32 script) {
474     /* However, the language lists (I think) are distinct */
475     /* But giving a value of -1 for gpos will give us the set of languages in */
476     /*  both tables (for this script) */
477     int cnt=0, tot=0, i, g, l;
478     uint32 *langs = NULL;
479     OTLookup *test;
480     FeatureScriptLangList *fl;
481     struct scriptlanglist *sl;
482 
483     for ( g=0; g<2; ++g ) {
484 	if (( gpos==0 && g==1 ) || ( gpos==1 && g==0 ))
485     continue;
486 	for ( test = g ? sf->gpos_lookups : sf->gsub_lookups; test!=NULL; test = test->next ) {
487 	    if ( test->unused )
488 	continue;
489 	    for ( fl=test->features; fl!=NULL; fl=fl->next ) {
490 		for ( sl=fl->scripts ; sl!=NULL; sl=sl->next ) {
491 		    if ( sl->script==script ) {
492 			for ( l=0; l<sl->lang_cnt; ++l ) {
493 			    uint32 lang;
494 			    if ( l<MAX_LANG )
495 				lang = sl->langs[l];
496 			    else
497 				lang = sl->morelangs[l-MAX_LANG];
498 			    for ( i=0; i<cnt; ++i ) {
499 				if ( lang==langs[i] )
500 			    break;
501 			    }
502 			    if ( i==cnt ) {
503 				if ( cnt>=tot )
504 				    langs = realloc(langs,(tot+=10)*sizeof(uint32));
505 				langs[cnt++] = lang;
506 			    }
507 			}
508 		    }
509 		}
510 	    }
511 	}
512     }
513 
514     if ( cnt==0 ) {
515 	/* We add dummy script entries. Because Uniscribe will refuse to */
516 	/*  process some scripts if they don't have an entry in both GPOS */
517 	/*  an GSUB. So if a script appears in either table, force it to */
518 	/*  appear in both. That means we can get scripts with no lookups */
519 	/*  and hence no languages. It seems that Uniscribe doesn't like */
520 	/*  that either. So give each such script a dummy default language */
521 	/*  entry. This is what VOLT does */
522 	langs = calloc(2,sizeof(uint32));
523 	langs[0] = DEFAULT_LANG;
524 return( langs );
525     }
526 
527     /* We want our languages in alphabetic order */
528     qsort(langs,cnt,sizeof(uint32),lang_cmp);
529     /* add a 0 entry to mark the end of the list */
530     if ( cnt>=tot )
531 	langs = realloc(langs,(tot+1)*sizeof(uint32));
532     langs[cnt] = 0;
533 return( langs );
534 }
535 
SFFeaturesInScriptLang(SplineFont * sf,int gpos,uint32 script,uint32 lang)536 uint32 *SFFeaturesInScriptLang(SplineFont *sf,int gpos,uint32 script,uint32 lang) {
537     int cnt=0, tot=0, i, l, isg;
538     uint32 *features = NULL;
539     OTLookup *test;
540     FeatureScriptLangList *fl;
541     struct scriptlanglist *sl;
542     /* gpos==0 => GSUB, gpos==1 => GPOS, gpos==-1 => both, gpos==-2 => Both & morx & kern */
543 
544     if ( sf->cidmaster ) sf=sf->cidmaster;
545     for ( isg = 0; isg<2; ++isg ) {
546 	if ( gpos>=0 && isg!=gpos )
547     continue;
548 	for ( test = isg ? sf->gpos_lookups : sf->gsub_lookups; test!=NULL; test = test->next ) {
549 	    if ( test->unused )
550 	continue;
551 	    for ( fl=test->features; fl!=NULL; fl=fl->next ) {
552 		if ( fl->ismac && gpos!=-2 )
553 	    continue;
554 		if ( script==0xffffffff ) {
555 		    for ( i=0; i<cnt; ++i ) {
556 			if ( fl->featuretag==features[i] )
557 		    break;
558 		    }
559 		    if ( i==cnt ) {
560 			if ( cnt>=tot )
561 			    features = realloc(features,(tot+=10)*sizeof(uint32));
562 			features[cnt++] = fl->featuretag;
563 		    }
564 		} else for ( sl=fl->scripts ; sl!=NULL; sl=sl->next ) {
565 		    if ( sl->script==script ) {
566 			int matched = false;
567 			if ( fl->ismac && gpos==-2 )
568 			    matched = true;
569 			else for ( l=0; l<sl->lang_cnt; ++l ) {
570 			    uint32 testlang;
571 			    if ( l<MAX_LANG )
572 				testlang = sl->langs[l];
573 			    else
574 				testlang = sl->morelangs[l-MAX_LANG];
575 			    if ( testlang==lang ) {
576 				matched = true;
577 			break;
578 			    }
579 			}
580 			if ( matched ) {
581 			    for ( i=0; i<cnt; ++i ) {
582 				if ( fl->featuretag==features[i] )
583 			    break;
584 			    }
585 			    if ( i==cnt ) {
586 				if ( cnt>=tot )
587 				    features = realloc(features,(tot+=10)*sizeof(uint32));
588 				features[cnt++] = fl->featuretag;
589 			    }
590 			}
591 		    }
592 		}
593 	    }
594 	}
595     }
596 
597     if ( sf->design_size!=0 && gpos ) {
598 	/* The 'size' feature is like no other. It has no lookups and so */
599 	/*  we will never find it in the normal course of events. If the */
600 	/*  user has specified a design size, then every script/lang combo */
601 	/*  gets a 'size' feature which contains no lookups but feature */
602 	/*  params */
603 	if ( cnt>=tot )
604 	    features = realloc(features,(tot+=2)*sizeof(uint32));
605 	features[cnt++] = CHR('s','i','z','e');
606     }
607 
608     if ( cnt==0 )
609 return( calloc(1,sizeof(uint32)) );
610 
611     /* We don't care if our features are in alphabetical order here */
612     /*  all that matters is whether the complete list of features is */
613     /*  ordering here would be irrelevant */
614     /* qsort(features,cnt,sizeof(uint32),uint32_cmp); */
615 
616     /* add a 0 entry to mark the end of the list */
617     if ( cnt>=tot )
618 	features = realloc(features,(tot+1)*sizeof(uint32));
619     features[cnt] = 0;
620 return( features );
621 }
622 
SFLookupsInScriptLangFeature(SplineFont * sf,int gpos,uint32 script,uint32 lang,uint32 feature)623 OTLookup **SFLookupsInScriptLangFeature(SplineFont *sf,int gpos,uint32 script,uint32 lang, uint32 feature) {
624     int cnt=0, tot=0, l;
625     OTLookup **lookups = NULL;
626     OTLookup *test;
627     FeatureScriptLangList *fl;
628     struct scriptlanglist *sl;
629 
630     for ( test = gpos ? sf->gpos_lookups : sf->gsub_lookups; test!=NULL; test = test->next ) {
631 	if ( test->unused )
632     continue;
633 	for ( fl=test->features; fl!=NULL; fl=fl->next ) {
634 	    if ( fl->featuretag==feature ) {
635 		for ( sl=fl->scripts ; sl!=NULL; sl=sl->next ) {
636 		    if ( sl->script==script ) {
637 			for ( l=0; l<sl->lang_cnt; ++l ) {
638 			    uint32 testlang;
639 			    if ( l<MAX_LANG )
640 				testlang = sl->langs[l];
641 			    else
642 				testlang = sl->morelangs[l-MAX_LANG];
643 			    if ( testlang==lang ) {
644 				if ( cnt>=tot )
645 				    lookups = realloc(lookups,(tot+=10)*sizeof(OTLookup *));
646 				lookups[cnt++] = test;
647 	goto found;
648 			    }
649 			}
650 		    }
651 		}
652 	    }
653 	}
654 	found:;
655     }
656 
657     if ( cnt==0 )
658 return( NULL );
659 
660     /* lookup order is irrelevant here. might as well leave it in invocation order */
661     /* add a 0 entry to mark the end of the list */
662     if ( cnt>=tot )
663 	lookups = realloc(lookups,(tot+1)*sizeof(OTLookup *));
664     lookups[cnt] = 0;
665 return( lookups );
666 }
667 
LigaturesFirstComponentGID(SplineFont * sf,char * components)668 static int LigaturesFirstComponentGID(SplineFont *sf,char *components) {
669     int gid, ch;
670     char *pt;
671 
672     for ( pt = components; *pt!='\0' && *pt!=' '; ++pt );
673     ch = *pt; *pt = '\0';
674     gid = SFFindExistingSlot(sf,-1,components);
675     *pt = ch;
676 return( gid );
677 }
678 
PSTValid(SplineFont * sf,PST * pst)679 static int PSTValid(SplineFont *sf,PST *pst) {
680     char *start, *pt, ch;
681     int ret;
682 
683     switch ( pst->type ) {
684       case pst_position:
685 return( true );
686       case pst_pair:
687 return( SCWorthOutputting(SFGetChar(sf,-1,pst->u.pair.paired)) );
688       case pst_substitution: case pst_alternate: case pst_multiple:
689       case pst_ligature:
690 	for ( start = pst->u.mult.components; *start ; ) {
691 	    for ( pt=start; *pt && *pt!=' '; ++pt );
692 	    ch = *pt; *pt = '\0';
693 	    ret = SCWorthOutputting(SFGetChar(sf,-1,start));
694 	    if ( !ret ) {
695 		LogError(_("Lookup subtable contains unused glyph %s making the whole subtable invalid"), start);
696 		*pt = ch;
697 return( false );
698 	    }
699 	    *pt = ch;
700 	    if ( ch==0 )
701 		start = pt;
702 	    else
703 		start = pt+1;
704 	}
705       default:
706       break;
707     }
708 return( true );
709 }
710 
SFGlyphsWithPSTinSubtable(SplineFont * sf,struct lookup_subtable * subtable)711 SplineChar **SFGlyphsWithPSTinSubtable(SplineFont *sf,struct lookup_subtable *subtable) {
712     uint8 *used = calloc(sf->glyphcnt,sizeof(uint8));
713     SplineChar **glyphs, *sc;
714     int i, k, gid, cnt;
715     KernPair *kp;
716     PST *pst;
717     int ispair = subtable->lookup->lookup_type == gpos_pair;
718     int isliga = subtable->lookup->lookup_type == gsub_ligature;
719 
720     for ( i=0; i<sf->glyphcnt; ++i ) if ( SCWorthOutputting(sc = sf->glyphs[i]) ) {
721 	if ( ispair ) {
722 	    for ( k=0; k<2; ++k ) {
723 		for ( kp= k ? sc->kerns : sc->vkerns; kp!=NULL ; kp=kp->next ) {
724 		    if ( !SCWorthOutputting(kp->sc))
725 		continue;
726 		    if ( kp->subtable == subtable ) {
727 			used[i] = true;
728     goto continue_;
729 		    }
730 		}
731 	    }
732 	}
733 	for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
734 	    if ( pst->subtable == subtable && PSTValid(sf,pst)) {
735 		if ( !isliga ) {
736 		    used[i] = true;
737     goto continue_;
738 		} else {
739 		    gid = LigaturesFirstComponentGID(sf,pst->u.lig.components);
740 		    pst->u.lig.lig = sc;
741 		    if ( gid!=-1 )
742 			used[gid] = true;
743 		    /* can't continue here. ffi might be "f+f+i" and "ff+i" */
744 		    /*  and we need to mark both "f" and "ff" as used */
745 		}
746 	    }
747 	}
748     continue_: ;
749     }
750 
751     for ( i=cnt=0 ; i<sf->glyphcnt; ++i )
752 	if ( used[i] )
753 	    ++cnt;
754 
755     if ( cnt==0 ) {
756 	free(used);
757 return( NULL );
758     }
759     glyphs = malloc((cnt+1)*sizeof(SplineChar *));
760     for ( i=cnt=0 ; i<sf->glyphcnt; ++i ) {
761 	if ( used[i] )
762 	    glyphs[cnt++] = sf->glyphs[i];
763     }
764     glyphs[cnt] = NULL;
765     free(used);
766 return( glyphs );
767 }
768 
SFGlyphsWithLigatureinLookup(SplineFont * sf,struct lookup_subtable * subtable)769 SplineChar **SFGlyphsWithLigatureinLookup(SplineFont *sf,struct lookup_subtable *subtable) {
770     uint8 *used = calloc(sf->glyphcnt,sizeof(uint8));
771     SplineChar **glyphs, *sc;
772     int i, cnt;
773     PST *pst;
774 
775     for ( i=0; i<sf->glyphcnt; ++i ) if ( SCWorthOutputting(sc = sf->glyphs[i]) ) {
776 	for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
777 	    if ( pst->subtable == subtable ) {
778 		used[i] = true;
779     goto continue_;
780 	    }
781 	}
782     continue_: ;
783     }
784 
785     for ( i=cnt=0 ; i<sf->glyphcnt; ++i )
786 	if ( used[i] )
787 	    ++cnt;
788 
789     if ( cnt==0 ) {
790 	free(used);
791 return( NULL );
792     }
793 
794     glyphs = malloc((cnt+1)*sizeof(SplineChar *));
795     for ( i=cnt=0 ; i<sf->glyphcnt; ++i ) {
796 	if ( used[i] )
797 	    glyphs[cnt++] = sf->glyphs[i];
798     }
799     glyphs[cnt] = NULL;
800     free(used);
801 return( glyphs );
802 }
803 
TickLookupKids(OTLookup * otl)804 static void TickLookupKids(OTLookup *otl) {
805     struct lookup_subtable *sub;
806     int i,j;
807 
808     for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
809 	if ( sub->fpst!=NULL ) {
810 	    for ( i=0; i<sub->fpst->rule_cnt; ++i ) {
811 		struct fpst_rule *rule = &sub->fpst->rules[i];
812 		for ( j=0; j<rule->lookup_cnt; ++j ) {
813 		    if ( rule->lookups[j].lookup!=NULL )
814 			rule->lookups[j].lookup->in_gpos = true;
815 		}
816 	    }
817 	}
818     }
819 }
820 
SFFindUnusedLookups(SplineFont * sf)821 void SFFindUnusedLookups(SplineFont *sf) {
822     OTLookup *test;
823     struct lookup_subtable *sub;
824     int gpos;
825     AnchorClass *ac;
826     AnchorPoint *ap;
827     SplineChar *sc;
828     KernPair *kp;
829     PST *pst;
830     int i,k,gid,isv;
831     SplineFont *_sf = sf;
832     Justify *jscripts;
833     struct jstf_lang *jlangs;
834 
835     if ( _sf->cidmaster ) _sf = _sf->cidmaster;
836 
837     /* Some things are obvious. If a subtable consists of a kernclass or some */
838     /*  such, then obviously it is used. But more distributed info takes more */
839     /*  work. So mark anything easy as used, and anything difficult as unused */
840     /* We'll work on the difficult things later */
841     for ( gpos=0; gpos<2; ++gpos ) {
842 	for ( test = gpos ? _sf->gpos_lookups : _sf->gsub_lookups; test!=NULL; test = test->next ) {
843 	    for ( sub = test->subtables; sub!=NULL; sub=sub->next ) {
844 		if ( sub->kc!=NULL || sub->fpst!=NULL || sub->sm!=NULL ) {
845 		    sub->unused = false;
846 	    continue;
847 		}
848 		sub->unused = true;
849 		/* We'll turn the following bit back on if there turns out */
850 		/*  to be an anchor class attached to it -- that is subtly */
851 		/*  different than being unused -- unused will be set if all */
852 		/*  acs are unused, this bit will be on if there are unused */
853 		/*  classes that still refer to us. */
854 		sub->anchor_classes = false;
855 	    }
856 	}
857     }
858 
859     /* To be useful an anchor class must have both at least one base and one mark */
860     /*  (for cursive anchors that means at least one entry and at least one exit) */
861     /* Start by assuming the worst */
862     for ( ac = _sf->anchor; ac!=NULL; ac=ac->next )
863 	ac->has_mark = ac->has_base = false;
864 
865     /* Ok, for each glyph, look at all lookups (or anchor classes) it affects */
866     /*  and mark the appropriate parts of them as used */
867     k = 0;
868     do {
869 	sf = _sf->subfontcnt==0 ? _sf : _sf->subfonts[k];
870 	for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( SCWorthOutputting(sc = sf->glyphs[gid]) ) {
871 	    for ( ap=sc->anchor; ap!=NULL; ap=ap->next ) {
872 		switch ( ap->type ) {
873 		  case at_mark: case at_centry:
874 		    ap->anchor->has_mark = true;
875 		  break;
876 		  case at_basechar: case at_baselig: case at_basemark:
877 		  case at_cexit:
878 		    ap->anchor->has_base = true;
879 		  break;
880 		  default:
881 		  break;
882 		}
883 	    }
884 	    for ( isv=0; isv<2; ++isv ) {
885 		for ( kp= isv ? sc->kerns : sc->vkerns ; kp!=NULL; kp=kp->next ) {
886 		    if ( SCWorthOutputting(kp->sc))
887 			kp->subtable->unused = false;
888 		}
889 	    }
890 	    for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
891 		if ( pst->subtable==NULL )
892 	    continue;
893 		if ( !PSTValid(sf,pst))
894 	    continue;
895 		pst->subtable->unused = false;
896 	    }
897 	}
898 	++k;
899     } while ( k<_sf->subfontcnt );
900 
901     /* Finally for any anchor class that has both a mark and a base then it is */
902     /*  used, and its lookup is also used */
903     /* Also, even if unused, as long as the anchor class exists we must keep */
904     /*  the subtable around */
905     for ( ac = _sf->anchor; ac!=NULL; ac=ac->next ) {
906         if ( ac->subtable==NULL )
907     continue;
908 	ac->subtable->anchor_classes = true;
909 	if ( ac->has_mark && ac->has_base )
910 	    ac->subtable->unused = false;
911     }
912 
913     /* Now for each lookup, a lookup is unused if ALL subtables are unused */
914     for ( gpos=0; gpos<2; ++gpos ) {
915 	for ( test = gpos ? _sf->gpos_lookups : _sf->gsub_lookups; test!=NULL; test = test->next ) {
916 	    test->unused = test->empty = true;
917 	    for ( sub=test->subtables; sub!=NULL; sub=sub->next ) {
918 		if ( !sub->unused )
919 		    test->unused = false;
920 		if ( !sub->unused && !sub->anchor_classes ) {
921 		    test->empty = false;
922 	    break;
923 		}
924 	    }
925 	}
926     }
927 
928     /* I store JSTF max lookups in the gpos list because they have the same */
929     /*  format. But now I need to tease them out and learn which lookups are */
930     /*  used in GPOS and which in JSTF (and conceivably which get duplicated */
931     /*  and placed in both) */
932     for ( test = sf->gpos_lookups; test!=NULL; test = test->next ) {
933 	test->only_jstf = test->in_jstf = test->in_gpos = false;
934 	if ( test->features!=NULL )
935 	    test->in_gpos = true;
936     }
937     for ( jscripts = sf->justify; jscripts!=NULL; jscripts=jscripts->next ) {
938 	for ( jlangs=jscripts->langs; jlangs!=NULL; jlangs=jlangs->next ) {
939 	    for ( i=0; i<jlangs->cnt; ++i ) {
940 		struct jstf_prio *prio = &jlangs->prios[i];
941 		if ( prio->enableShrink!=NULL )
942 		    for ( k=0; prio->enableShrink[k]!=NULL; ++k )
943 			prio->enableShrink[k]->in_gpos = true;
944 		if ( prio->disableShrink!=NULL )
945 		    for ( k=0; prio->disableShrink[k]!=NULL; ++k )
946 			prio->disableShrink[k]->in_gpos = true;
947 		if ( prio->enableExtend!=NULL )
948 		    for ( k=0; prio->enableExtend[k]!=NULL; ++k )
949 			prio->enableExtend[k]->in_gpos = true;
950 		if ( prio->disableExtend!=NULL )
951 		    for ( k=0; prio->disableExtend[k]!=NULL; ++k )
952 			prio->disableExtend[k]->in_gpos = true;
953 		if ( prio->maxShrink!=NULL )
954 		    for ( k=0; prio->maxShrink[k]!=NULL; ++k )
955 			prio->maxShrink[k]->in_jstf = true;
956 		if ( prio->maxExtend!=NULL )
957 		    for ( k=0; prio->maxExtend[k]!=NULL; ++k )
958 			prio->maxExtend[k]->in_jstf = true;
959 	    }
960 	}
961     }
962     for ( test = sf->gpos_lookups; test!=NULL; test = test->next ) {
963 	if ( test->in_gpos && (test->lookup_type==gpos_context || test->lookup_type==gpos_contextchain))
964 	    TickLookupKids(test);
965     }
966     for ( test = sf->gpos_lookups; test!=NULL; test = test->next )
967 	test->only_jstf = test->in_jstf && !test->in_gpos;
968 }
969 
SFFindClearUnusedLookupBits(SplineFont * sf)970 void SFFindClearUnusedLookupBits(SplineFont *sf) {
971     OTLookup *test;
972     int gpos;
973 
974     for ( gpos=0; gpos<2; ++gpos ) {
975 	for ( test = gpos ? sf->gpos_lookups : sf->gsub_lookups; test!=NULL; test = test->next ) {
976 	    test->unused = false;
977 	    test->empty = false;
978 	    test->def_lang_checked = false;
979 	}
980     }
981 }
982 
SFRemoveAnchorPointsOfAC(SplineFont * sf,AnchorClass * ac)983 static void SFRemoveAnchorPointsOfAC(SplineFont *sf,AnchorClass *ac) {
984     int gid;
985     SplineChar *sc;
986     AnchorPoint *ap, *prev, *next;
987 
988     for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc = sf->glyphs[gid])!=NULL ) {
989 	for ( prev=NULL, ap=sc->anchor; ap!=NULL; ap=next ) {
990 	    next = ap->next;
991 	    if ( ap->anchor!=ac )
992 		prev = ap;
993 	    else {
994 		if ( prev==NULL )
995 		    sc->anchor = next;
996 		else
997 		    prev->next = next;
998 		ap->next = NULL;
999 		AnchorPointsFree(ap);
1000 	    }
1001 	}
1002     }
1003 }
1004 
RemoveFromList(OTLookup ** list,OTLookup * dying)1005 static OTLookup **RemoveFromList(OTLookup **list, OTLookup *dying) {
1006     int i,j;
1007 
1008     if ( list==NULL )
1009 return( NULL );
1010     for ( i=0; list[i]!=NULL; ++i ) {
1011 	if ( list[i]==dying ) {
1012 	    for ( j=i+1; ; ++j ) {
1013 		list[j-1] = list[j];
1014 		if ( list[j]==NULL )
1015 	    break;
1016 	    }
1017 	}
1018     }
1019     if ( list[0]==NULL ) {
1020 	free(list);
1021 return( NULL );
1022     }
1023 return( list );
1024 }
1025 
RemoveJSTFReferences(SplineFont * sf,OTLookup * dying)1026 static void RemoveJSTFReferences(SplineFont *sf,OTLookup *dying) {
1027     Justify *jscript;
1028     struct jstf_lang *jlang;
1029     int i;
1030 
1031     for ( jscript = sf->justify; jscript!=NULL; jscript=jscript->next ) {
1032 	for ( jlang=jscript->langs; jlang!=NULL; jlang=jlang->next ) {
1033 	    for ( i=0; i<jlang->cnt; ++i ) {
1034 		struct jstf_prio *prio = &jlang->prios[i];
1035 		prio->enableShrink = RemoveFromList(prio->enableShrink,dying);
1036 		prio->disableShrink = RemoveFromList(prio->disableShrink,dying);
1037 		prio->enableExtend = RemoveFromList(prio->enableExtend,dying);
1038 		prio->disableExtend = RemoveFromList(prio->disableExtend,dying);
1039 		prio->maxShrink = RemoveFromList(prio->maxShrink,dying);
1040 		prio->maxExtend = RemoveFromList(prio->maxExtend,dying);
1041 	    }
1042 	}
1043     }
1044 }
1045 
RemoveNestedReferences(SplineFont * sf,int isgpos)1046 static void RemoveNestedReferences(SplineFont *sf,int isgpos) {
1047     OTLookup *otl;
1048     struct lookup_subtable *sub;
1049     int i,j,k;
1050 
1051     for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl = otl->next ) {
1052 	if ( otl->lookup_type==morx_context ) {
1053 	    for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
1054 		ASM *sm = sub->sm;
1055 		if ( sm->type==asm_context ) {
1056 		    for ( i=0; i<sm->state_cnt*sm->class_cnt; ++i ) {
1057 			struct asm_state *state = &sm->state[i];
1058 			if ( state->u.context.mark_lookup == otl )
1059 			    state->u.context.mark_lookup = NULL;
1060 			if ( state->u.context.cur_lookup == otl )
1061 			    state->u.context.cur_lookup = NULL;
1062 		    }
1063 		}
1064 	    }
1065 	/* Reverse chaining tables do not reference lookups. The match pattern*/
1066 	/*  is a (exactly one) coverage table, and each glyph in that table   */
1067 	/*  as an inline replacement. There is no lookup to do the replacement*/
1068 	/* (so we ignore it here) */
1069 	} else if ( otl->lookup_type==gsub_context || otl->lookup_type==gsub_contextchain ||
1070 		otl->lookup_type==gpos_context || otl->lookup_type==gpos_contextchain ) {
1071 	    for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
1072 		FPST *fpst = sub->fpst;
1073 		if (fpst!=NULL) {
1074 		    for ( i=0; i<fpst->rule_cnt; ++i ) {
1075 			for ( j=0; j<fpst->rules[i].lookup_cnt; ++j ) {
1076 			    if ( fpst->rules[i].lookups[j].lookup == otl ) {
1077 				for ( k=j+1; k<fpst->rules[i].lookup_cnt; ++k )
1078 				    fpst->rules[i].lookups[k-1] = fpst->rules[i].lookups[k];
1079 				--fpst->rules[i].lookup_cnt;
1080 				--j;
1081 			    }
1082 			}
1083 		    }
1084 		}
1085 	    }
1086 	}
1087     }
1088 }
1089 
SFRemoveUnusedLookupSubTables(SplineFont * sf,int remove_incomplete_anchorclasses,int remove_unused_lookups)1090 void SFRemoveUnusedLookupSubTables(SplineFont *sf,
1091 	int remove_incomplete_anchorclasses,
1092 	int remove_unused_lookups) {
1093     int gpos;
1094     struct lookup_subtable *sub, *subnext, *prev;
1095     AnchorClass *ac, *acprev, *acnext;
1096     OTLookup *otl, *otlprev, *otlnext;
1097 
1098     /* Presumes someone has called SFFindUnusedLookups first */
1099 
1100     if ( remove_incomplete_anchorclasses ) {
1101 	for ( acprev=NULL, ac=sf->anchor; ac!=NULL; ac=acnext ) {
1102 	    acnext = ac->next;
1103 	    if ( ac->has_mark && ac->has_base )
1104 		acprev = ac;
1105 	    else {
1106 		SFRemoveAnchorPointsOfAC(sf,ac);
1107 		ac->next = NULL;
1108 		AnchorClassesFree(ac);
1109 		if ( acprev==NULL )
1110 		    sf->anchor = acnext;
1111 		else
1112 		    acprev = acnext;
1113 	    }
1114 	}
1115     }
1116 
1117     for ( gpos=0; gpos<2; ++gpos ) {
1118 	for ( otl = gpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl = otlnext ) {
1119 	    otlnext = otl->next;
1120 	    if ( remove_unused_lookups && (otl->empty ||
1121 		    (otl->unused && remove_incomplete_anchorclasses)) ) {
1122 		if ( otlprev!=NULL )
1123 		    otlprev->next = otlnext;
1124 		else if ( gpos )
1125 		    sf->gpos_lookups = otlnext;
1126 		else
1127 		    sf->gsub_lookups = otlnext;
1128 		RemoveNestedReferences(sf,gpos);
1129 		OTLookupFree(otl);
1130 	    } else {
1131 		for ( prev=NULL, sub=otl->subtables; sub!=NULL; sub=subnext ) {
1132 		    subnext = sub->next;
1133 		    if ( sub->unused &&
1134 			    (!sub->anchor_classes ||
1135 			      remove_incomplete_anchorclasses )) {
1136 			if ( prev==NULL )
1137 			    otl->subtables = subnext;
1138 			else
1139 			    prev->next = subnext;
1140 			free(sub->subtable_name);
1141 			chunkfree(sub,sizeof(*sub));
1142 		    } else
1143 			prev = sub;
1144 		}
1145 	    }
1146 	}
1147     }
1148 }
1149 
SFRemoveLookupSubTable(SplineFont * sf,struct lookup_subtable * sub,int remove_acs)1150 void SFRemoveLookupSubTable(SplineFont *sf,struct lookup_subtable *sub, int remove_acs) {
1151     OTLookup *otl = sub->lookup;
1152     struct lookup_subtable *subprev, *subtest;
1153 
1154     if ( sf->cidmaster!=NULL ) sf = sf->cidmaster;
1155 
1156     if ( sub->sm!=NULL ) {
1157 	ASM *prev = NULL, *test;
1158 	for ( test=sf->sm; test!=NULL && test!=sub->sm; prev=test, test=test->next );
1159 	if ( prev==NULL )
1160 	    sf->sm = sub->sm->next;
1161 	else
1162 	    prev->next = sub->sm->next;
1163 	sub->sm->next = NULL;
1164 	ASMFree(sub->sm);
1165 	sub->sm = NULL;
1166     } else if ( sub->fpst!=NULL ) {
1167 	FPST *prev = NULL, *test;
1168 	for ( test=sf->possub; test!=NULL && test!=sub->fpst; prev=test, test=test->next );
1169 	if ( prev==NULL )
1170 	    sf->possub = sub->fpst->next;
1171 	else
1172 	    prev->next = sub->fpst->next;
1173 	sub->fpst->next = NULL;
1174 	FPSTFree(sub->fpst);
1175 	sub->fpst = NULL;
1176     } else if ( sub->kc!=NULL ) {
1177 	KernClass *prev = NULL, *test;
1178 	for ( test=sf->kerns; test!=NULL && test!=sub->kc; prev=test, test=test->next );
1179 	if ( test!=NULL ) {
1180 	    if ( prev==NULL )
1181 		sf->kerns = sub->kc->next;
1182 	    else
1183 		prev->next = sub->kc->next;
1184 	} else {
1185 	    for ( prev=NULL,test=sf->vkerns; test!=NULL && test!=sub->kc; prev=test, test=test->next );
1186 	    if ( prev==NULL )
1187 		sf->vkerns = sub->kc->next;
1188 	    else
1189 		prev->next = sub->kc->next;
1190 	}
1191 	sub->kc->next = NULL;
1192 	KernClassListFree(sub->kc);
1193 	sub->kc = NULL;
1194     } else if ( otl->lookup_type==gpos_cursive || otl->lookup_type==gpos_mark2base ||
1195 	    otl->lookup_type==gpos_mark2ligature || otl->lookup_type==gpos_mark2mark ) {
1196 	AnchorClass *ac, *acnext;
1197 	for ( ac=sf->anchor; ac!=NULL; ac=acnext ) {
1198 	    acnext = ac->next;
1199 	    if ( ac->subtable==sub ) {
1200                 if ( remove_acs )
1201 		    SFRemoveAnchorClass(sf,ac);
1202                 else
1203                     ac->subtable = NULL;
1204             }
1205 	}
1206     } else {
1207 	int i,k,v;
1208 	SplineChar *sc;
1209 	SplineFont *_sf;
1210 	PST *pst, *prev, *next;
1211 	KernPair *kp, *kpprev, *kpnext;
1212 	k=0;
1213 	do {
1214 	    _sf = sf->subfontcnt==0 ? sf : sf->subfonts[k];
1215 	    for ( i=0; i<_sf->glyphcnt; ++i ) if ( (sc=_sf->glyphs[i])!=NULL ) {
1216 		for ( pst=sc->possub, prev=NULL ; pst!=NULL; pst=next ) {
1217 		    next = pst->next;
1218 		    if ( pst->subtable==sub ) {
1219 			if ( prev==NULL )
1220 			    sc->possub = next;
1221 			else
1222 			    prev->next = next;
1223 			pst->next = NULL;
1224 			PSTFree(pst);
1225 		    } else
1226 			prev = pst;
1227 		}
1228 		for ( v=0; v<2; ++v ) {
1229 		    for ( kp=v ? sc->vkerns : sc->kerns, kpprev=NULL ; kp!=NULL; kp=kpnext ) {
1230 			kpnext = kp->next;
1231 			if ( kp->subtable==sub ) {
1232 			    if ( kpprev!=NULL )
1233 				kpprev->next = kpnext;
1234 			    else if ( v )
1235 				sc->vkerns = kpnext;
1236 			    else
1237 				sc->kerns = kpnext;
1238 			    kp->next = NULL;
1239 			    KernPairsFree(kp);
1240 			} else
1241 			    kpprev = kp;
1242 		    }
1243 		}
1244 	    }
1245 	    ++k;
1246 	} while ( k<sf->subfontcnt );
1247     }
1248 
1249     subprev = NULL;
1250     for ( subtest = otl->subtables; subtest!=NULL && subtest!=sub; subprev = subtest, subtest=subtest->next );
1251     if ( subprev==NULL )
1252 	otl->subtables = sub->next;
1253     else
1254 	subprev->next = sub->next;
1255     free(sub->subtable_name);
1256     free(sub->suffix);
1257     chunkfree(sub,sizeof(struct lookup_subtable));
1258 }
1259 
SFRemoveLookup(SplineFont * sf,OTLookup * otl,int remove_acs)1260 void SFRemoveLookup(SplineFont *sf,OTLookup *otl,int remove_acs) {
1261     OTLookup *test, *prev;
1262     int isgpos;
1263     struct lookup_subtable *sub, *subnext;
1264 
1265     if ( sf->cidmaster ) sf = sf->cidmaster;
1266 
1267     for ( sub = otl->subtables; sub!=NULL; sub=subnext ) {
1268 	subnext = sub->next;
1269 	SFRemoveLookupSubTable(sf,sub,remove_acs);
1270     }
1271 
1272     for ( prev=NULL, test=sf->gpos_lookups; test!=NULL && test!=otl; prev=test, test=test->next );
1273     if ( test==NULL ) {
1274 	isgpos = false;
1275 	for ( prev=NULL, test=sf->gsub_lookups; test!=NULL && test!=otl; prev=test, test=test->next );
1276     } else
1277 	isgpos = true;
1278     if ( prev!=NULL )
1279 	prev->next = otl->next;
1280     else if ( isgpos )
1281 	sf->gpos_lookups = otl->next;
1282     else
1283 	sf->gsub_lookups = otl->next;
1284 
1285     RemoveNestedReferences(sf,isgpos);
1286     RemoveJSTFReferences(sf,otl);
1287 
1288     otl->next = NULL;
1289     OTLookupFree(otl);
1290 }
1291 
SFFindLookupSubtable(SplineFont * sf,const char * name)1292 struct lookup_subtable *SFFindLookupSubtable(SplineFont *sf,const char *name) {
1293     int isgpos;
1294     OTLookup *otl;
1295     struct lookup_subtable *sub;
1296 
1297     if ( sf->cidmaster ) sf = sf->cidmaster;
1298 
1299     if ( name==NULL )
1300 return( NULL );
1301 
1302     for ( isgpos=0; isgpos<2; ++isgpos ) {
1303 	for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups ; otl!=NULL; otl=otl->next ) {
1304 	    for ( sub = otl->subtables; sub!=NULL; sub=sub->next ) {
1305 		if ( strcmp(name,sub->subtable_name)==0 )
1306 return( sub );
1307 	    }
1308 	}
1309     }
1310 return( NULL );
1311 }
1312 
SFFindLookupSubtableAndFreeName(SplineFont * sf,char * name)1313 struct lookup_subtable *SFFindLookupSubtableAndFreeName(SplineFont *sf, char *name) {
1314     struct lookup_subtable *sub = SFFindLookupSubtable(sf,name);
1315     free(name);
1316 return( sub );
1317 }
1318 
SFFindLookup(SplineFont * sf,const char * name)1319 OTLookup *SFFindLookup(SplineFont *sf,const char *name) {
1320     int isgpos;
1321     OTLookup *otl;
1322 
1323     if ( sf->cidmaster ) sf = sf->cidmaster;
1324 
1325     if ( name==NULL )
1326 return( NULL );
1327 
1328     for ( isgpos=0; isgpos<2; ++isgpos ) {
1329 	for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups ; otl!=NULL; otl=otl->next ) {
1330 	    if ( strcmp(name,otl->lookup_name)==0 )
1331 return( otl );
1332 	}
1333     }
1334 return( NULL );
1335 }
1336 
FListAppendScriptLang(FeatureScriptLangList * fl,uint32 script_tag,uint32 lang_tag)1337 void FListAppendScriptLang(FeatureScriptLangList *fl,uint32 script_tag,uint32 lang_tag) {
1338     struct scriptlanglist *sl;
1339     int l;
1340 
1341     for ( sl = fl->scripts; sl!=NULL && sl->script!=script_tag; sl=sl->next );
1342     if ( sl==NULL ) {
1343 	sl = chunkalloc(sizeof(struct scriptlanglist));
1344 	sl->script = script_tag;
1345 	sl->next = fl->scripts;
1346 	fl->scripts = sl;
1347     }
1348     for ( l=0; l<MAX_LANG && l<sl->lang_cnt && sl->langs[l]!=lang_tag; ++l );
1349     if ( l>=MAX_LANG && l<sl->lang_cnt ) {
1350 	while ( l<sl->lang_cnt && sl->morelangs[l-MAX_LANG]!=lang_tag )
1351 	    ++l;
1352     }
1353     if ( l>=sl->lang_cnt ) {
1354 	if ( l<MAX_LANG )
1355 	    sl->langs[l] = lang_tag;
1356 	else {
1357 	    if ( l%MAX_LANG == 0 )
1358 		sl->morelangs = realloc(sl->morelangs,l*sizeof(uint32));
1359 		/* We've just allocated MAX_LANG-1 more than we need */
1360 		/*  so we don't do quite some many allocations */
1361 	    sl->morelangs[l-MAX_LANG] = lang_tag;
1362 	}
1363 	++sl->lang_cnt;
1364     }
1365 }
1366 
FListsAppendScriptLang(FeatureScriptLangList * fl,uint32 script_tag,uint32 lang_tag)1367 void FListsAppendScriptLang(FeatureScriptLangList *fl,uint32 script_tag,uint32 lang_tag) {
1368     while ( fl!=NULL ) {
1369 	FListAppendScriptLang(fl,script_tag,lang_tag);
1370 	fl=fl->next;
1371     }
1372 }
1373 
SuffixFromTags(FeatureScriptLangList * fl)1374 char *SuffixFromTags(FeatureScriptLangList *fl) {
1375     static struct { uint32 tag; const char *suffix; } tags2suffix[] = {
1376         { CHR('v','r','t','2'), "vert" },	/* Will check for vrt2 later */
1377         { CHR('o','n','u','m'), "oldstyle" },
1378         { CHR('s','u','p','s'), "superior" },
1379         { CHR('s','u','b','s'), "inferior" },
1380         { CHR('s','w','s','h'), "swash" },
1381         { CHR('f','w','i','d'), "full" },
1382         { CHR('h','w','i','d'), "hw" },
1383         { 0, NULL }
1384     };
1385     int i;
1386 
1387     while ( fl!=NULL ) {
1388 	for ( i=0; tags2suffix[i].tag!=0; ++i )
1389 	    if ( tags2suffix[i].tag==fl->featuretag )
1390 return( copy( tags2suffix[i].suffix ));
1391 	fl = fl->next;
1392     }
1393 return( NULL );
1394 }
1395 
1396 const char *lookup_type_names[2][10] = {
1397     {   N_("Undefined substitution"), N_("Single Substitution"), N_("Multiple Substitution"),
1398         N_("Alternate Substitution"), N_("Ligature Substitution"), N_("Contextual Substitution"),
1399         N_("Contextual Chaining Substitution"), N_("Extension"),
1400         N_("Reverse Contextual Chaining Substitution")
1401     },
1402     {   N_("Undefined positioning"), N_("Single Positioning"), N_("Pairwise Positioning (kerning)"),
1403         N_("Cursive attachment"), N_("Mark to base attachment"),
1404         N_("Mark to Ligature attachment"), N_("Mark to Mark attachment"),
1405         N_("Contextual Positioning"), N_("Contextual Chaining Positioning"),
1406         N_("Extension")
1407     }
1408 };
1409 
1410 /* This is a non-ui based copy of a similar list in lookupui.c */
1411 static struct {
1412     const char *text;
1413     uint32 tag;
1414 } localscripts[] = {
1415     { N_("Adlam"), CHR('a','d','l','m') },
1416     { N_("Ahom"), CHR('a','h','o','m') },
1417     { N_("Anatolian Hieroglyphs"), CHR('h','l','u','w') },
1418 /* GT: See the long comment at "Property|New" */
1419 /* GT: The msgstr should contain a translation of "Arabic", ignore "Script|" */
1420     { N_("Script|Arabic"), CHR('a','r','a','b') },
1421     { N_("Script|Aramaic"), CHR('s','a','m','r') },
1422     { N_("Script|Armenian"), CHR('a','r','m','n') },
1423     { N_("Script|Avestan"), CHR('a','v','s','t') },
1424     { N_("Script|Balinese"), CHR('b','a','l','i') },
1425     { N_("Bamum"), CHR('b','a','m','u') },
1426     { N_("Bassa Vah"), CHR('b','a','s','s') },
1427     { N_("Script|Batak"), CHR('b','a','t','k') },
1428     { N_("Script|Bengali"), CHR('b','e','n','g') },
1429     { N_("Script|Bengali2"), CHR('b','n','g','2') },
1430     { N_("Bhaiksuki"), CHR('b','h','k','s') },
1431     { N_("Bopomofo"), CHR('b','o','p','o') },
1432     { NU_("Brāhmī"), CHR('b','r','a','h') },
1433     { N_("Braille"), CHR('b','r','a','i') },
1434     { N_("Script|Buginese"), CHR('b','u','g','i') },
1435     { N_("Script|Buhid"), CHR('b','u','h','d') },
1436     { N_("Byzantine Music"), CHR('b','y','z','m') },
1437     { N_("Canadian Syllabics"), CHR('c','a','n','s') },
1438     { N_("Carian"), CHR('c','a','r','i') },
1439     { N_("Caucasian Albanian"), CHR('a','g','h','b') },
1440     { N_("Chakma"), CHR('c','a','k','m') },
1441     { N_("Script|Cham"), CHR('c','h','a','m') },
1442     { N_("Script|Cherokee"), CHR('c','h','e','r') },
1443     { N_("CJK Ideographic"), CHR('h','a','n','i') },
1444     { N_("Script|Coptic"), CHR('c','o','p','t') },
1445     { N_("Cypriot syllabary"), CHR('c','p','r','t') },
1446     { N_("Cyrillic"), CHR('c','y','r','l') },
1447     { N_("Script|Default"), CHR('D','F','L','T') },
1448     { N_("Deseret (Mormon)"), CHR('d','s','r','t') },
1449     { N_("Devanagari"), CHR('d','e','v','a') },
1450     { N_("Devanagari2"), CHR('d','e','v','2') },
1451     { N_("Dogra"), CHR('d','o','g','r') },
1452     { N_("Duployan"), CHR('d','u','p','l') },
1453     { N_("Egyptian Hieroglyphs"), CHR('e','g','y','p') },
1454     { N_("Elbasan"), CHR('e','l','b','a') },
1455     { N_("Script|Ethiopic"), CHR('e','t','h','i') },
1456     { N_("Script|Georgian"), CHR('g','e','o','r') },
1457     { N_("Glagolitic"), CHR('g','l','a','g') },
1458     { N_("Gothic"), CHR('g','o','t','h') },
1459     { N_("Grantha"), CHR('g','r','a','n') },
1460     { N_("Script|Greek"), CHR('g','r','e','k') },
1461     { N_("Script|Gujarati"), CHR('g','u','j','r') },
1462     { N_("Script|Gujarati2"), CHR('g','j','r','2') },
1463     { N_("Gunjala Gondi"), CHR('g','o','n','g') },
1464     { N_("Gurmukhi"), CHR('g','u','r','u') },
1465     { N_("Gurmukhi2"), CHR('g','u','r','2') },
1466     { N_("Hangul Jamo"), CHR('j','a','m','o') },
1467     { N_("Hangul"), CHR('h','a','n','g') },
1468     { N_("Hanifi Rohingya"), CHR('r','o','h','g') },
1469     { NU_("Script|Hanunóo"), CHR('h','a','n','o') },
1470     { N_("Hatran"), CHR('h','a','t','r') },
1471     { N_("Script|Hebrew"), CHR('h','e','b','r') },
1472     { N_("Hiragana & Katakana"), CHR('k','a','n','a') },
1473     { N_("Imperial Aramaic"), CHR('a','r','m','i') },
1474     { N_("Inscriptional Pahlavi"), CHR('p','h','l','i') },
1475     { N_("Inscriptional Parthian"), CHR('p','r','t','i') },
1476     { N_("Script|Javanese"), CHR('j','a','v','a') },
1477     { N_("Kaithi"), CHR('k','t','h','i') },
1478     { N_("Script|Kannada"), CHR('k','n','d','a') },
1479     { N_("Script|Kannada2"), CHR('k','n','d','2') },
1480     { N_("Kayah Li"), CHR('k','a','l','i') },
1481     { N_("Script|Kharosthi"), CHR('k','h','a','r') },
1482     { N_("Script|Khmer"), CHR('k','h','m','r') },
1483     { N_("Khojki"), CHR('k','h','o','j') },
1484     { N_("Khudawadi"), CHR('s','i','n','d') },
1485     { N_("Script|Lao"), CHR('l','a','o',' ') },
1486     { N_("Script|Latin"), CHR('l','a','t','n') },
1487     { NU_("Lepcha (Róng)"), CHR('l','e','p','c') },
1488     { N_("Script|Limbu"), CHR('l','i','m','b') },
1489     { N_("Linear A"), CHR('l','i','n','a') },
1490     { N_("Linear B"), CHR('l','i','n','b') },
1491     { N_("Lisu"), CHR('l','i','s','u') },
1492     { N_("Lycian"), CHR('l','y','c','i') },
1493     { N_("Lydian"), CHR('l','y','d','i') },
1494     { N_("Mahajani"), CHR('m','a','h','j') },
1495     { N_("Makasar"), CHR('m','a','k','a') },
1496     { NU_("Script|Malayālam"), CHR('m','l','y','m') },
1497     { NU_("Script|Malayālam2"), CHR('m','l','m','2') },
1498     { N_("Script|Mandaean"), CHR('m','a','n','d') },
1499     { N_("Manichaean"), CHR('m','a','n','i') },
1500     { N_("Marchen"), CHR('m','a','r','c') },
1501     { N_("Masaram Gondi"), CHR('g','o','n','m') },
1502     { N_("Mathematical Alphanumeric Symbols"), CHR('m','a','t','h') },
1503     { N_("Medefaidrin"), CHR('m','e','d','f') },
1504     { N_("Meetei Mayek"), CHR('m','t','e','i') },
1505     { N_("Mende Kikakui"), CHR('m','e','n','d') },
1506     { N_("Meroitic Cursive"), CHR('m','e','r','c') },
1507     { N_("Meroitic Hieroglyphs"), CHR('m','e','r','o') },
1508     { N_("Miao"), CHR('p','l','r','d') },
1509     { N_("Modi"), CHR('m','o','d','i') },
1510     { N_("Script|Mongolian"), CHR('m','o','n','g') },
1511     { N_("Mro"), CHR('m','r','o','o') },
1512     { N_("Multani"), CHR('m','u','l','t') },
1513     { N_("Musical"), CHR('m','u','s','c') },
1514     { N_("Script|Myanmar"), CHR('m','y','m','2') },
1515     { N_("N'Ko"), CHR('n','k','o',' ') },
1516     { N_("Nabataean"), CHR('n','b','a','t') },
1517     { N_("New Tai Lue"), CHR('t','a','l','u') },
1518     { N_("Newa"), CHR('n','e','w','a') },
1519     { N_("Nushu"), CHR('n','s','h','u') },
1520     { N_("Ogham"), CHR('o','g','a','m') },
1521     { N_("Ol Chiki"), CHR('o','l','c','k') },
1522     { N_("Old Hungarian"), CHR('h','u','n','g') },
1523     { N_("Old Italic (Etruscan, Oscan, etc.)"), CHR('i','t','a','l') },
1524     { N_("Old North Arabian"), CHR('n','a','r','b') },
1525     { N_("Script|Old Permic"), CHR('p','e','r','m') },
1526     { N_("Old Persian cuneiform"), CHR('x','p','e','o') },
1527     { N_("Old Sogdian"), CHR('s','o','g','o') },
1528     { N_("Old South Arabian"), CHR('s','a','r','b') },
1529     { N_("Old Turkic"), CHR('o','r','k','h') },
1530     { N_("Script|Oriya"), CHR('o','r','y','a') },
1531     { N_("Script|Oriya2"), CHR('o','r','y','2') },
1532     { N_("Osage"), CHR('o','s','g','e') },
1533     { N_("Osmanya"), CHR('o','s','m','a') },
1534     { N_("Pahawh Hmong"), CHR('h','m','n','g') },
1535     { N_("Palmyrene"), CHR('p','a','l','m') },
1536     { N_("Pau Cin Hau"), CHR('p','a','u','c') },
1537     { N_("Script|Phags-pa"), CHR('p','h','a','g') },
1538     { N_("Script|Phoenician"), CHR('p','h','n','x') },
1539     { N_("Pollard Phonetic"), CHR('p','l','r','d') },
1540     { N_("Psalter Pahlavi"), CHR('p','h','l','p') },
1541     { N_("Rejang"), CHR('r','j','n','g') },
1542     { N_("Runic"), CHR('r','u','n','r') },
1543     { N_("Saurashtra"), CHR('s','a','u','r') },
1544     { N_("Sharada"), CHR('s','h','r','d') },
1545     { N_("Shavian"), CHR('s','h','a','w') },
1546     { N_("Siddham"), CHR('s','i','d','d') },
1547     { N_("Sutton SignWriting"), CHR('s','g','n','w') },
1548     { N_("Script|Sinhala"), CHR('s','i','n','h') },
1549     { N_("Sogdian"), CHR('s','o','g','d') },
1550     { N_("Sora Sompeng"), CHR('s','o','r','a') },
1551     { N_("Soyombo"), CHR('s','o','y','o') },
1552     { N_("Script|Sumero-Akkadian Cuneiform"), CHR('x','s','u','x') },
1553     { N_("Script|Sundanese"), CHR('s','u','n','d') },
1554     { N_("Script|Syloti Nagri"), CHR('s','y','l','o') },
1555     { N_("Script|Syriac"), CHR('s','y','r','c') },
1556     { N_("Script|Tagalog"), CHR('t','g','l','g') },
1557     { N_("Script|Tagbanwa"), CHR('t','a','g','b') },
1558     { N_("Tai Le"), CHR('t','a','l','e') },
1559     { N_("Tai Tham"), CHR('l','a','n','a') },
1560     { N_("Tai Viet"), CHR('t','a','v','t') },
1561     { N_("Takri"), CHR('t','a','k','r') },
1562     { N_("Script|Tamil"), CHR('t','a','m','l') },
1563     { N_("Script|Tamil2"), CHR('t','m','l','2') },
1564     { N_("Tangut"), CHR('t','a','n','g') },
1565     { N_("Script|Telugu"), CHR('t','e','l','u') },
1566     { N_("Script|Telugu2"), CHR('t','e','l','2') },
1567     { N_("Thaana"), CHR('t','h','a','a') },
1568     { N_("Script|Thai"), CHR('t','h','a','i') },
1569     { N_("Script|Tibetan"), CHR('t','i','b','t') },
1570     { N_("Tifinagh (Berber)"), CHR('t','f','n','g') },
1571     { N_("Tirhuta"), CHR('t','i','r','h') },
1572     { N_("Script|Ugaritic"), CHR('u','g','a','r') },
1573     { N_("Script|Vai"), CHR('v','a','i',' ') },
1574     { N_("Warang Citi"), CHR('w','a','r','a') },
1575     { N_("Script|Yi"), CHR('y','i',' ',' ') },
1576     { N_("Zanabazar Square"), CHR('z','a','n','b') },
1577     { NULL, 0 }
1578 };
1579 
LookupInit(void)1580 void LookupInit(void) {
1581     static int done = false;
1582     int i, j;
1583 
1584     if ( done )
1585 return;
1586     done = true;
1587     for ( j=0; j<2; ++j ) {
1588 	for ( i=0; i<10; ++i )
1589 	    if ( lookup_type_names[j][i]!=NULL )
1590 		lookup_type_names[j][i] = S_((char *) lookup_type_names[j][i]);
1591     }
1592     for ( i=0; localscripts[i].text!=NULL; ++i )
1593 	localscripts[i].text = S_(localscripts[i].text);
1594     for ( i=0; friendlies[i].friendlyname!=NULL; ++i )
1595 	friendlies[i].friendlyname = S_(friendlies[i].friendlyname);
1596 }
1597 
TagFullName(SplineFont * sf,uint32 tag,int ismac,int onlyifknown)1598 char *TagFullName(SplineFont *sf,uint32 tag, int ismac, int onlyifknown) {
1599     char ubuf[200], *end = ubuf+sizeof(ubuf), *setname;
1600     int k;
1601 
1602     if ( ismac==-1 )
1603 	/* Guess */
1604 	ismac = (tag>>24)<' ' || (tag>>24)>0x7e;
1605 
1606     if ( ismac ) {
1607 	sprintf( ubuf, "<%d,%d> ", (int) (tag>>16),(int) (tag&0xffff) );
1608 	if ( (setname = PickNameFromMacName(FindMacSettingName(sf,tag>>16,tag&0xffff)))!=NULL ) {
1609 	    strcat( ubuf, setname );
1610 	    free( setname );
1611 	}
1612     } else {
1613 	uint32 stag = tag;
1614 	if ( tag==CHR('n','u','t','f') )	/* early name that was standardize later as... */
1615 	    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 */
1616 	if ( tag==REQUIRED_FEATURE ) {
1617 	    strcpy(ubuf,_("Required Feature"));
1618 	} else {
1619 	    LookupInit();
1620 	    for ( k=0; friendlies[k].tag!=0; ++k ) {
1621 		if ( friendlies[k].tag == stag )
1622 	    break;
1623 	    }
1624 	    ubuf[0] = '\'';
1625 	    ubuf[1] = tag>>24;
1626 	    ubuf[2] = (tag>>16)&0xff;
1627 	    ubuf[3] = (tag>>8)&0xff;
1628 	    ubuf[4] = tag&0xff;
1629 	    ubuf[5] = '\'';
1630 	    ubuf[6] = ' ';
1631 	    if ( friendlies[k].tag!=0 )
1632 		strncpy(ubuf+7, (char *) friendlies[k].friendlyname,end-ubuf-7);
1633 	    else if ( onlyifknown )
1634 return( NULL );
1635 	    else
1636 		ubuf[7]='\0';
1637 	}
1638     }
1639 return( copy( ubuf ));
1640 }
1641 
1642 
NameOTLookup(OTLookup * otl,SplineFont * sf)1643 void NameOTLookup(OTLookup *otl,SplineFont *sf) {
1644     char *userfriendly = NULL, *script;
1645     FeatureScriptLangList *fl;
1646     char *lookuptype;
1647     const char *format;
1648     struct lookup_subtable *subtable;
1649     int k;
1650 
1651     LookupInit();
1652 
1653     if ( otl->lookup_name==NULL ) {
1654 	for ( k=0; k<2; ++k ) {
1655 	    for ( fl=otl->features; fl!=NULL ; fl=fl->next ) {
1656 		/* look first for a feature attached to a default language */
1657 		if ( k==1 || DefaultLangTagInScriptList(fl->scripts,false)!=NULL ) {
1658 		    userfriendly = TagFullName(sf,fl->featuretag, fl->ismac, true);
1659 		    if ( userfriendly!=NULL )
1660 	    break;
1661 		}
1662 	    }
1663 	    if ( userfriendly!=NULL )
1664 	break;
1665 	}
1666 	if ( userfriendly==NULL ) {
1667 	    if ( (otl->lookup_type&0xff)>= 0xf0 )
1668 		lookuptype = _("State Machine");
1669 	    else if ( (otl->lookup_type>>8)<2 && (otl->lookup_type&0xff)<10 )
1670 		lookuptype = _(lookup_type_names[otl->lookup_type>>8][otl->lookup_type&0xff]);
1671 	    else
1672 		lookuptype = S_("LookupType|Unknown");
1673 	    for ( fl=otl->features; fl!=NULL && !fl->ismac; fl=fl->next );
1674 	    if ( fl==NULL )
1675 		userfriendly = copy(lookuptype);
1676 	    else {
1677 		userfriendly = malloc( strlen(lookuptype) + 10);
1678 		sprintf( userfriendly, "%s '%c%c%c%c'", lookuptype,
1679 		    fl->featuretag>>24,
1680 		    fl->featuretag>>16,
1681 		    fl->featuretag>>8 ,
1682 		    fl->featuretag );
1683 	    }
1684 	}
1685 	script = NULL;
1686 	if ( fl==NULL ) fl = otl->features;
1687 	if ( fl!=NULL && fl->scripts!=NULL ) {
1688 	    char buf[8];
1689 	    int j;
1690 	    struct scriptlanglist *sl, *found, *found2;
1691 	    uint32 script_tag = fl->scripts->script;
1692 	    found = found2 = NULL;
1693 	    for ( sl = fl->scripts; sl!=NULL; sl=sl->next ) {
1694 		if ( sl->script == DEFAULT_SCRIPT )
1695 		    /* Ignore it */;
1696 		else if ( DefaultLangTagInOneScriptList(sl)) {
1697 		    if ( found==NULL )
1698 			found = sl;
1699 		    else {
1700 			found = found2 = NULL;
1701 	    break;
1702 		    }
1703 		} else if ( found2 == NULL )
1704 		    found2 = sl;
1705 		else
1706 		    found2 = (struct scriptlanglist *) -1;
1707 	    }
1708 	    if ( found==NULL && found2!=NULL && found2 != (struct scriptlanglist *) -1 )
1709 		found = found2;
1710 	    if ( found!=NULL ) {
1711 		script_tag = found->script;
1712 		for ( j=0; localscripts[j].text!=NULL && script_tag!=localscripts[j].tag; ++j );
1713 		if ( localscripts[j].text!=NULL )
1714 		    script = copy( S_((char *) localscripts[j].text) );
1715 		else {
1716 		    buf[0] = '\'';
1717 		    buf[1] = fl->scripts->script>>24;
1718 		    buf[2] = (fl->scripts->script>>16)&0xff;
1719 		    buf[3] = (fl->scripts->script>>8)&0xff;
1720 		    buf[4] = fl->scripts->script&0xff;
1721 		    buf[5] = '\'';
1722 		    buf[6] = 0;
1723 		    script = copy(buf);
1724 		}
1725 	    }
1726 	}
1727 	if ( script!=NULL ) {
1728 /* GT: This string is used to generate a name for each OpenType lookup. */
1729 /* GT: The %s will be filled with the user friendly name of the feature used to invoke the lookup */
1730 /* GT: The second %s (if present) is the script */
1731 /* GT: While the %d is the index into the lookup list and is used to disambiguate it */
1732 /* GT: In case that is needed */
1733 	    format = _("%s in %s lookup %d");
1734 	    otl->lookup_name = malloc( strlen(userfriendly)+strlen(format)+strlen(script)+10 );
1735 	    sprintf( otl->lookup_name, format, userfriendly, script, otl->lookup_index );
1736 	} else {
1737 	    format = _("%s lookup %d");
1738 	    otl->lookup_name = malloc( strlen(userfriendly)+strlen(format)+10 );
1739 	    sprintf( otl->lookup_name, format, userfriendly, otl->lookup_index );
1740 	}
1741 	free(script);
1742 	free(userfriendly);
1743     }
1744 
1745     if ( otl->subtables==NULL )
1746 	/* IError( _("Lookup with no subtables"))*/;
1747     else {
1748 	int cnt = 0;
1749 	for ( subtable = otl->subtables; subtable!=NULL; subtable=subtable->next, ++cnt )
1750 		if ( subtable->subtable_name==NULL ) {
1751 	    if ( subtable==otl->subtables && subtable->next==NULL )
1752 /* GT: This string is used to generate a name for an OpenType lookup subtable. */
1753 /* GT:  %s is the lookup name */
1754 		format = _("%s subtable");
1755 	    else if ( subtable->per_glyph_pst_or_kern )
1756 /* GT: This string is used to generate a name for an OpenType lookup subtable. */
1757 /* GT:  %s is the lookup name, %d is the index of the subtable in the lookup */
1758 		format = _("%s per glyph data %d");
1759 	    else if ( subtable->kc!=NULL )
1760 		format = _("%s kerning class %d");
1761 	    else if ( subtable->fpst!=NULL )
1762 		format = _("%s contextual %d");
1763 	    else if ( subtable->anchor_classes )
1764 		format = _("%s anchor %d");
1765 	    else {
1766 		IError("Subtable status not filled in for %dth subtable of %s", cnt, otl->lookup_name );
1767 		format = "%s !!!!!!!! %d";
1768 	    }
1769 	    subtable->subtable_name = malloc( strlen(otl->lookup_name)+strlen(format)+10 );
1770 	    sprintf( subtable->subtable_name, format, otl->lookup_name, cnt );
1771 	}
1772     }
1773     if ( otl->lookup_type==gsub_ligature ) {
1774 	for ( fl=otl->features; fl!=NULL; fl=fl->next )
1775 	    if ( fl->featuretag==CHR('l','i','g','a') || fl->featuretag==CHR('r','l','i','g'))
1776 		otl->store_in_afm = true;
1777     }
1778 
1779     if ( otl->lookup_type==gsub_single )
1780 	for ( subtable = otl->subtables; subtable!=NULL; subtable=subtable->next )
1781 	    subtable->suffix = SuffixFromTags(otl->features);
1782 }
1783 
LangOrder(struct scriptlanglist * sl)1784 static void LangOrder(struct scriptlanglist *sl) {
1785     int i,j;
1786     uint32 lang, lang2;
1787 
1788     for ( i=0; i<sl->lang_cnt; ++i ) {
1789 	lang = i<MAX_LANG ? sl->langs[i] : sl->morelangs[i-MAX_LANG];
1790 	for ( j=i+1; j<sl->lang_cnt; ++j ) {
1791 	    lang2 = j<MAX_LANG ? sl->langs[j] : sl->morelangs[j-MAX_LANG];
1792 	    if ( lang>lang2 ) {
1793 		if ( i<MAX_LANG )
1794 		    sl->langs[i] = lang2;
1795 		else
1796 		    sl->morelangs[i-MAX_LANG] = lang2;
1797 		if ( j<MAX_LANG )
1798 		    sl->langs[j] = lang;
1799 		else
1800 		    sl->morelangs[j-MAX_LANG] = lang;
1801 		lang = lang2;
1802 	    }
1803 	}
1804     }
1805 }
1806 
SLOrder(struct scriptlanglist * sl)1807 static struct scriptlanglist *SLOrder(struct scriptlanglist *sl) {
1808     int i,j, cnt;
1809     struct scriptlanglist *sl2, *space[30], **allocked=NULL, **test = space;
1810 
1811     for ( sl2=sl, cnt=0; sl2!=NULL; sl2=sl2->next, ++cnt )
1812 	LangOrder(sl2);
1813     if ( cnt<=1 )
1814 return( sl );
1815     if ( cnt>30 )
1816 	test = allocked = malloc(cnt*sizeof(struct scriptlanglist *));
1817     for ( sl2=sl, cnt=0; sl2!=NULL; sl2=sl2->next, ++cnt )
1818 	test[cnt] = sl2;
1819     for ( i=0; i<cnt; ++i ) for ( j=i+1; j<cnt; ++j ) {
1820 	if ( test[i]->script > test[j]->script ) {
1821 	    struct scriptlanglist *temp;
1822 	    temp = test[i];
1823 	    test[i] = test[j];
1824 	    test[j] = temp;
1825 	}
1826     }
1827     sl = test[0];
1828     for ( i=1; i<cnt; ++i )
1829 	test[i-1]->next = test[i];
1830     test[i-1]->next = NULL;
1831     free( allocked );
1832 return( sl );
1833 }
1834 
FLOrder(FeatureScriptLangList * fl)1835 FeatureScriptLangList *FLOrder(FeatureScriptLangList *fl) {
1836     int i,j, cnt;
1837     FeatureScriptLangList *fl2, *space[30], **allocked=NULL, **test = space;
1838 
1839     for ( fl2=fl, cnt=0; fl2!=NULL; fl2=fl2->next, ++cnt )
1840 	fl2->scripts = SLOrder(fl2->scripts);
1841     if ( cnt<=1 )
1842 return( fl );
1843     if ( cnt>30 )
1844 	test = allocked = malloc(cnt*sizeof(FeatureScriptLangList *));
1845     for ( fl2=fl, cnt=0; fl2!=NULL; fl2=fl2->next, ++cnt )
1846 	test[cnt] = fl2;
1847     for ( i=0; i<cnt; ++i ) for ( j=i+1; j<cnt; ++j ) {
1848 	if ( test[i]->featuretag > test[j]->featuretag ) {
1849 	    FeatureScriptLangList *temp;
1850 	    temp = test[i];
1851 	    test[i] = test[j];
1852 	    test[j] = temp;
1853 	}
1854     }
1855     fl = test[0];
1856     for ( i=1; i<cnt; ++i )
1857 	test[i-1]->next = test[i];
1858     test[i-1]->next = NULL;
1859     free( allocked );
1860 return( fl );
1861 }
1862 
SLCopy(struct scriptlanglist * sl)1863 struct scriptlanglist *SLCopy(struct scriptlanglist *sl) {
1864     struct scriptlanglist *newsl;
1865 
1866     newsl = chunkalloc(sizeof(struct scriptlanglist));
1867     *newsl = *sl;
1868     newsl->next = NULL;
1869 
1870     if ( sl->lang_cnt>MAX_LANG ) {
1871 	newsl->morelangs = malloc((newsl->lang_cnt-MAX_LANG)*sizeof(uint32));
1872 	memcpy(newsl->morelangs,sl->morelangs,(newsl->lang_cnt-MAX_LANG)*sizeof(uint32));
1873     }
1874 return( newsl );
1875 }
1876 
SListCopy(struct scriptlanglist * sl)1877 struct scriptlanglist *SListCopy(struct scriptlanglist *sl) {
1878     struct scriptlanglist *head=NULL, *last=NULL, *cur;
1879 
1880     for ( ; sl!=NULL; sl=sl->next ) {
1881 	cur = SLCopy(sl);
1882 	if ( head==NULL )
1883 	    head = cur;
1884 	else
1885 	    last->next = cur;
1886 	last = cur;
1887     }
1888 return( head );
1889 }
1890 
FeatureListCopy(FeatureScriptLangList * fl)1891 FeatureScriptLangList *FeatureListCopy(FeatureScriptLangList *fl) {
1892     FeatureScriptLangList *newfl;
1893 
1894     if ( fl==NULL )
1895 return( NULL );
1896 
1897     newfl = chunkalloc(sizeof(FeatureScriptLangList));
1898     *newfl = *fl;
1899     newfl->next = NULL;
1900 
1901     newfl->scripts = SListCopy(fl->scripts);
1902 return( newfl );
1903 }
1904 
LangMerge(struct scriptlanglist * into,struct scriptlanglist * from)1905 static void LangMerge(struct scriptlanglist *into, struct scriptlanglist *from) {
1906     int i,j;
1907     uint32 flang, tlang;
1908 
1909     for ( i=0 ; i<from->lang_cnt; ++i ) {
1910 	flang = i<MAX_LANG ? from->langs[i] : from->morelangs[i-MAX_LANG];
1911 	for ( j=0; j<into->lang_cnt; ++j ) {
1912 	    tlang = j<MAX_LANG ? into->langs[j] : into->morelangs[j-MAX_LANG];
1913 	    if ( tlang==flang )
1914 	break;
1915 	}
1916 	if ( j==into->lang_cnt ) {
1917 	    if ( into->lang_cnt<MAX_LANG )
1918 		into->langs[into->lang_cnt++] = flang;
1919 	    else {
1920 		into->morelangs = realloc(into->morelangs,(into->lang_cnt+1-MAX_LANG)*sizeof(uint32));
1921 		into->morelangs[into->lang_cnt++-MAX_LANG] = flang;
1922 	    }
1923 	}
1924     }
1925 }
1926 
SLMerge(FeatureScriptLangList * into,struct scriptlanglist * fsl)1927 void SLMerge(FeatureScriptLangList *into, struct scriptlanglist *fsl) {
1928     struct scriptlanglist *isl;
1929 
1930     for ( ; fsl!=NULL; fsl = fsl->next ) {
1931 	for ( isl=into->scripts; isl!=NULL; isl=isl->next ) {
1932 	    if ( fsl->script==isl->script )
1933 	break;
1934 	}
1935 	if ( isl!=NULL )
1936 	    LangMerge(isl,fsl);
1937 	else {
1938 	    isl = SLCopy(fsl);
1939 	    isl->next = into->scripts;
1940 	    into->scripts = isl;
1941 	}
1942     }
1943 }
1944 
FLMerge(OTLookup * into,OTLookup * from)1945 void FLMerge(OTLookup *into, OTLookup *from) {
1946     /* Merge the feature list from "from" into "into" */
1947     FeatureScriptLangList *ifl, *ffl;
1948 
1949     /* first check for common featuretags and merge the scripts of each */
1950     for ( ffl = from->features; ffl!=NULL; ffl = ffl->next ) {
1951 	for ( ifl=into->features; ifl!=NULL; ifl=ifl->next ) {
1952 	    if ( ffl->featuretag==ifl->featuretag )
1953 	break;
1954 	}
1955 	if ( ifl!=NULL )
1956 	    SLMerge(ffl,ifl->scripts);
1957 	else {
1958 	    ifl = FeatureListCopy(ffl);
1959 	    ifl->next = into->features;
1960 	    into->features = ifl;
1961 	}
1962     }
1963     into->features = FLOrder(into->features);
1964 }
1965 
SFSubTablesMerge(SplineFont * _sf,struct lookup_subtable * subfirst,struct lookup_subtable * subsecond)1966 void SFSubTablesMerge(SplineFont *_sf,struct lookup_subtable *subfirst,
1967 	struct lookup_subtable *subsecond) {
1968     uint16 lookup_type = subfirst->lookup->lookup_type;
1969     int gid,k,isv;
1970     SplineChar *sc;
1971     SplineFont *sf = _sf;
1972     PST *pst, *fpst, *spst, *pstprev, *pstnext;
1973     KernPair *fkp, *skp, *kpprev, *kpnext;
1974     AnchorClass *ac;
1975 
1976     if ( lookup_type != subsecond->lookup->lookup_type ) {
1977 	IError("Attempt to merge lookup subtables with mismatch types");
1978 return;
1979     }
1980     if ( lookup_type != gsub_single &&
1981 	    lookup_type != gsub_multiple &&
1982 	    lookup_type != gsub_alternate &&
1983 	    lookup_type != gsub_ligature &&
1984 	    lookup_type != gpos_single &&
1985 	    lookup_type != gpos_pair &&
1986 	    lookup_type != gpos_cursive &&
1987 	    lookup_type != gpos_mark2base &&
1988 	    lookup_type != gpos_mark2ligature &&
1989 	    lookup_type != gpos_mark2mark ) {
1990 	IError("Attempt to merge lookup subtables with bad types");
1991 return;
1992     } else if ( subfirst->kc!=NULL || subsecond->kc != NULL ) {
1993 	IError("Attempt to merge lookup subtables with kerning classes");
1994 return;
1995     }
1996 
1997     if ( lookup_type==gpos_cursive || lookup_type==gpos_mark2base ||
1998 	    lookup_type==gpos_mark2ligature || lookup_type==gpos_mark2mark ) {
1999 	for ( ac = sf->anchor; ac!=NULL ; ac=ac->next )
2000 	    if ( ac->subtable == subsecond )
2001 		ac->subtable = subfirst;
2002     } else {
2003 	k=0;
2004 	do {
2005 	    sf = _sf->subfontcnt==0 ? _sf : _sf->subfonts[k];
2006 	    for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
2007 		if ( lookup_type==gsub_single || lookup_type==gsub_multiple ||
2008 			lookup_type==gsub_alternate || lookup_type==gpos_single ) {
2009 		    fpst = spst = NULL;
2010 		    for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
2011 			if ( pst->subtable == subfirst ) {
2012 			    fpst = pst;
2013 			    if ( spst!=NULL )
2014 		    break;
2015 			} else if ( pst->subtable == subsecond ) {
2016 			    spst = pst;
2017 			    if ( fpst!=NULL )
2018 		    break;
2019 			}
2020 		    }
2021 		    if ( fpst==NULL && spst!=NULL )
2022 			spst->subtable = subfirst;
2023 		    else if ( spst!=NULL ) {
2024 			LogError(_("The glyph, %s, contains a %s from %s and one from %s.\nThe one from %s will be removed.\n"),
2025 				sc->name,
2026 			        lookup_type==gpos_single ? _("positioning") : _("substitution"),
2027 			        subfirst->subtable_name, subsecond->subtable_name,
2028 			        subsecond->subtable_name );
2029 			pstprev = NULL;
2030 			for ( pst=sc->possub; pst!=NULL && pst!=spst; pstprev=pst, pst=pst->next );
2031 			if ( pstprev==NULL )
2032 			    sc->possub = spst->next;
2033 			else
2034 			    pstprev = spst->next;
2035 			spst->next = NULL;
2036 			PSTFree(spst);
2037 		    }
2038 		} else if ( lookup_type==gsub_ligature || lookup_type==gpos_pair ) {
2039 		    pstprev = NULL;
2040 		    for ( spst=sc->possub; spst!=NULL ; spst = pstnext ) {
2041 			pstnext = spst->next;
2042 			if ( spst->subtable == subsecond ) {
2043 			    for ( fpst=sc->possub; fpst!=NULL; fpst=fpst->next ) {
2044 				if ( fpst->subtable == subfirst &&
2045 					strcmp(fpst->u.lig.components,spst->u.lig.components)==0 )
2046 			    break;
2047 			    }
2048 			    if ( fpst==NULL )
2049 				spst->subtable = subfirst;
2050 			    else {
2051 				LogError(_("The glyph, %s, contains the same %s from %s and from %s.\nThe one from %s will be removed.\n"),
2052 					sc->name,
2053 			                lookup_type==gsub_ligature ? _("ligature") : _("kern pair"),
2054 					subfirst->subtable_name, subsecond->subtable_name,
2055 					subsecond->subtable_name );
2056 				if ( pstprev==NULL )
2057 				    sc->possub = pstnext;
2058 				else
2059 				    pstprev->next = pstnext;
2060 				spst->next = NULL;
2061 			        PSTFree(spst);
2062 			        spst = pstprev;
2063 			    }
2064 			}
2065 			pstprev = spst;
2066 		    }
2067 		    for ( isv=0; isv<2; ++isv ) {
2068 			kpprev = NULL;
2069 			for ( skp=isv ? sc->vkerns : sc->kerns; skp!=NULL ; skp = kpnext ) {
2070 			    kpnext = skp->next;
2071 			    if ( skp->subtable == subsecond ) {
2072 				for ( fkp=isv ? sc->vkerns : sc->kerns; fkp!=NULL; fkp=fkp->next ) {
2073 				    if ( fkp->subtable == subfirst && fkp->sc==skp->sc )
2074 				break;
2075 				}
2076 				if ( fkp==NULL )
2077 				    skp->subtable = subfirst;
2078 				else {
2079 				    LogError(_("The glyph, %s, contains the same kern pair from %s and from %s.\nThe one from %s will be removed.\n"),
2080 					    sc->name,
2081 					    subfirst->subtable_name, subsecond->subtable_name,
2082 					    subsecond->subtable_name );
2083 				    if ( kpprev!=NULL )
2084 					kpprev->next = kpnext;
2085 				    else if ( isv )
2086 					sc->vkerns = kpnext;
2087 				    else
2088 					sc->kerns = kpnext;
2089 				    skp->next = NULL;
2090 				    KernPairsFree(skp);
2091 				    skp = kpprev;
2092 				}
2093 			    }
2094 			    kpprev = skp;
2095 			}
2096 		    }
2097 		}
2098 	    }
2099 	    ++k;
2100 	} while ( k<_sf->subfontcnt );
2101     }
2102 }
2103 
2104 /* ************************************************************************** */
2105 /* ******************************* copy lookup ****************************** */
2106 /* ************************************************************************** */
2107 
ClassCopy(int class_cnt,char ** classes)2108 static char **ClassCopy(int class_cnt,char **classes) {
2109     char **newclasses;
2110     int i;
2111 
2112     if ( classes==NULL || class_cnt==0 )
2113 return( NULL );
2114     newclasses = malloc(class_cnt*sizeof(char *));
2115     for ( i=0; i<class_cnt; ++i )
2116 	newclasses[i] = copy(classes[i]);
2117 return( newclasses );
2118 }
2119 
2120 static OTLookup *_OTLookupCopyInto(struct sfmergecontext *mc,
2121 	OTLookup *from_otl, OTLookup *before, int do_contents);
OTLookupCopyNested(struct sfmergecontext * mc,OTLookup * from_otl)2122 static OTLookup *OTLookupCopyNested(struct sfmergecontext *mc,
2123 	OTLookup *from_otl) {
2124     char *newname;
2125     OTLookup *to_nested_otl;
2126     int l;
2127 
2128     if ( from_otl==NULL )
2129 return( NULL );
2130 
2131     for ( l=0; l<mc->lcnt; ++l ) {
2132 	if ( mc->lks[l].from == from_otl )
2133 return( mc->lks[l].to );
2134     }
2135 
2136     newname = strconcat(mc->prefix,from_otl->lookup_name);
2137     to_nested_otl = SFFindLookup(mc->sf_to,newname);
2138     free(newname);
2139     if ( to_nested_otl==NULL )
2140 	to_nested_otl = _OTLookupCopyInto(mc, from_otl, (OTLookup *) -1, true );
2141 return( to_nested_otl );
2142 }
2143 
SF_AddKernClass(struct sfmergecontext * mc,KernClass * kc,struct lookup_subtable * sub)2144 static KernClass *SF_AddKernClass(struct sfmergecontext *mc,KernClass *kc,
2145 	struct lookup_subtable *sub ) {
2146     KernClass *newkc;
2147 
2148     newkc = chunkalloc(sizeof(KernClass));
2149     *newkc = *kc;
2150     newkc->subtable = sub;
2151     if ( sub->vertical_kerning ) {
2152 	newkc->next = mc->sf_to->vkerns;
2153 	mc->sf_to->vkerns = newkc;
2154     } else {
2155 	newkc->next = mc->sf_to->kerns;
2156 	mc->sf_to->kerns = newkc;
2157     }
2158 
2159     newkc->firsts = ClassCopy(newkc->first_cnt,newkc->firsts);
2160     newkc->seconds = ClassCopy(newkc->second_cnt,newkc->seconds);
2161     newkc->offsets = malloc(newkc->first_cnt*newkc->second_cnt*sizeof(int16));
2162     memcpy(newkc->offsets,kc->offsets,newkc->first_cnt*newkc->second_cnt*sizeof(int16));
2163     // We support group kerning as well.
2164     if (newkc->firsts_names) newkc->firsts_names = ClassCopy(newkc->first_cnt,newkc->firsts_names);
2165     if (newkc->seconds_names) newkc->seconds_names = ClassCopy(newkc->second_cnt,newkc->seconds_names);
2166     if (newkc->firsts_flags) {
2167       newkc->firsts_flags = malloc(newkc->first_cnt*sizeof(int));
2168       memcpy(newkc->firsts_flags,kc->firsts_flags,newkc->first_cnt*sizeof(int));
2169     }
2170     if (newkc->seconds_flags) {
2171       newkc->seconds_flags = malloc(newkc->second_cnt*sizeof(int));
2172       memcpy(newkc->seconds_flags,kc->seconds_flags,newkc->second_cnt*sizeof(int));
2173     }
2174     if (newkc->offsets_flags) {
2175       newkc->offsets_flags = malloc(newkc->first_cnt*newkc->second_cnt*sizeof(int));
2176       memcpy(newkc->offsets_flags,kc->offsets_flags,newkc->first_cnt*newkc->second_cnt*sizeof(int));
2177     }
2178 return( newkc );
2179 }
2180 
SF_AddFPST(struct sfmergecontext * mc,FPST * fpst,struct lookup_subtable * sub)2181 static FPST *SF_AddFPST(struct sfmergecontext *mc,FPST *fpst,
2182 	struct lookup_subtable *sub ) {
2183     FPST *newfpst;
2184     int i, k;
2185 
2186     newfpst = chunkalloc(sizeof(FPST));
2187     *newfpst = *fpst;
2188     newfpst->subtable = sub;
2189     newfpst->next = mc->sf_to->possub;
2190     mc->sf_to->possub = newfpst;
2191 
2192     newfpst->nclass = ClassCopy(newfpst->nccnt,newfpst->nclass);
2193     newfpst->bclass = ClassCopy(newfpst->bccnt,newfpst->bclass);
2194     newfpst->fclass = ClassCopy(newfpst->fccnt,newfpst->fclass);
2195 
2196     newfpst->nclassnames = ClassCopy(newfpst->nccnt,newfpst->nclassnames);
2197     newfpst->bclassnames = ClassCopy(newfpst->bccnt,newfpst->bclassnames);
2198     newfpst->fclassnames = ClassCopy(newfpst->fccnt,newfpst->fclassnames);
2199 
2200     newfpst->rules = malloc(newfpst->rule_cnt*sizeof(struct fpst_rule));
2201     memcpy(newfpst->rules,fpst->rules,newfpst->rule_cnt*sizeof(struct fpst_rule));
2202 
2203     for ( i=0; i<newfpst->rule_cnt; ++i ) {
2204 	struct fpst_rule *r = &newfpst->rules[i], *oldr = &fpst->rules[i];
2205 
2206 	r->lookups = malloc(r->lookup_cnt*sizeof(struct seqlookup));
2207 	memcpy(r->lookups,oldr->lookups,r->lookup_cnt*sizeof(struct seqlookup));
2208 	for ( k=0; k<r->lookup_cnt; ++k ) {
2209 	    r->lookups[k].lookup = OTLookupCopyNested(mc,
2210 		r->lookups[k].lookup);
2211 	}
2212 
2213 	switch ( newfpst->format ) {
2214 	  case pst_glyphs:
2215 	    r->u.glyph.names = copy( r->u.glyph.names );
2216 	    r->u.glyph.back = copy( r->u.glyph.back );
2217 	    r->u.glyph.fore = copy( r->u.glyph.fore );
2218 	  break;
2219 	  case pst_class:
2220 	    r->u.class.nclasses = malloc( r->u.class.ncnt*sizeof(uint16));
2221 	    memcpy(r->u.class.nclasses,oldr->u.class.nclasses, r->u.class.ncnt*sizeof(uint16));
2222 	    r->u.class.bclasses = malloc( r->u.class.bcnt*sizeof(uint16));
2223 	    memcpy(r->u.class.bclasses,oldr->u.class.bclasses, r->u.class.bcnt*sizeof(uint16));
2224 	    r->u.class.fclasses = malloc( r->u.class.fcnt*sizeof(uint16));
2225 	    memcpy(r->u.class.fclasses,oldr->u.class.fclasses, r->u.class.fcnt*sizeof(uint16));
2226 	  break;
2227 	  case pst_coverage:
2228 	    r->u.coverage.ncovers = ClassCopy( r->u.coverage.ncnt, r->u.coverage.ncovers );
2229 	    r->u.coverage.bcovers = ClassCopy( r->u.coverage.bcnt, r->u.coverage.bcovers );
2230 	    r->u.coverage.fcovers = ClassCopy( r->u.coverage.fcnt, r->u.coverage.fcovers );
2231 	  break;
2232 	  case pst_reversecoverage:
2233 	    r->u.rcoverage.ncovers = ClassCopy( r->u.rcoverage.always1, r->u.rcoverage.ncovers );
2234 	    r->u.rcoverage.bcovers = ClassCopy( r->u.rcoverage.bcnt, r->u.rcoverage.bcovers );
2235 	    r->u.rcoverage.fcovers = ClassCopy( r->u.rcoverage.fcnt, r->u.rcoverage.fcovers );
2236 	    r->u.rcoverage.replacements = copy( r->u.rcoverage.replacements );
2237 	  break;
2238 	  default:
2239 	  break;
2240 	}
2241     }
2242 return( newfpst );
2243 }
2244 
SF_AddASM(struct sfmergecontext * mc,ASM * sm,struct lookup_subtable * sub)2245 static ASM *SF_AddASM(struct sfmergecontext *mc,ASM *sm, struct lookup_subtable *sub ) {
2246     ASM *newsm;
2247     int i;
2248 
2249     newsm = chunkalloc(sizeof(ASM));
2250     *newsm = *sm;
2251     newsm->subtable = sub;
2252     newsm->next = mc->sf_to->sm;
2253     mc->sf_to->sm = newsm;
2254     mc->sf_to->changed = true;
2255     newsm->classes = ClassCopy(newsm->class_cnt, newsm->classes);
2256     newsm->state = malloc(newsm->class_cnt*newsm->state_cnt*sizeof(struct asm_state));
2257     memcpy(newsm->state,sm->state,
2258 	    newsm->class_cnt*newsm->state_cnt*sizeof(struct asm_state));
2259     if ( newsm->type == asm_kern ) {
2260 	for ( i=newsm->class_cnt*newsm->state_cnt-1; i>=0; --i ) {
2261 	    newsm->state[i].u.kern.kerns = malloc(newsm->state[i].u.kern.kcnt*sizeof(int16));
2262 	    memcpy(newsm->state[i].u.kern.kerns,sm->state[i].u.kern.kerns,newsm->state[i].u.kern.kcnt*sizeof(int16));
2263 	}
2264     } else if ( newsm->type == asm_insert ) {
2265 	for ( i=0; i<newsm->class_cnt*newsm->state_cnt; ++i ) {
2266 	    struct asm_state *this = &newsm->state[i];
2267 	    this->u.insert.mark_ins = copy(this->u.insert.mark_ins);
2268 	    this->u.insert.cur_ins = copy(this->u.insert.cur_ins);
2269 	}
2270     } else if ( newsm->type == asm_context ) {
2271 	for ( i=0; i<newsm->class_cnt*newsm->state_cnt; ++i ) {
2272 	    newsm->state[i].u.context.mark_lookup = OTLookupCopyNested(mc,
2273 		    newsm->state[i].u.context.mark_lookup);
2274 	    newsm->state[i].u.context.cur_lookup = OTLookupCopyNested(mc,
2275 		    newsm->state[i].u.context.cur_lookup);
2276 	}
2277     }
2278 return( newsm );
2279 }
2280 
SCFindOrMake(SplineFont * into,SplineChar * fromsc)2281 static SplineChar *SCFindOrMake(SplineFont *into,SplineChar *fromsc) {
2282     int to_index;
2283 
2284     if ( into->cidmaster==NULL && into->fv!=NULL ) {
2285 	to_index = SFFindSlot(into,into->fv->map,fromsc->unicodeenc,fromsc->name);
2286 	if ( to_index==-1 )
2287 return( NULL );
2288 return( SFMakeChar(into,into->fv->map,to_index));
2289     }
2290 return( SFGetChar(into,fromsc->unicodeenc,fromsc->name));
2291 }
2292 
SF_SCAddAP(SplineChar * tosc,AnchorPoint * ap,AnchorClass * newac)2293 static void SF_SCAddAP(SplineChar *tosc,AnchorPoint *ap, AnchorClass *newac) {
2294     AnchorPoint *newap;
2295 
2296     newap = chunkalloc(sizeof(AnchorPoint));
2297     *newap = *ap;
2298     newap->anchor = newac;
2299     newap->next = tosc->anchor;
2300     tosc->anchor = newap;
2301 }
2302 
SF_AddAnchorClasses(struct sfmergecontext * mc,struct lookup_subtable * from_sub,struct lookup_subtable * sub)2303 static void SF_AddAnchorClasses(struct sfmergecontext *mc,
2304 	struct lookup_subtable *from_sub, struct lookup_subtable *sub ) {
2305     AnchorClass *ac, *nac;
2306     int k, gid;
2307     SplineFont *fsf;
2308     AnchorPoint *ap;
2309     SplineChar *fsc, *tsc;
2310 
2311     for ( ac=mc->sf_from->anchor; ac!=NULL; ac=ac->next ) if ( ac->subtable==from_sub ) {
2312 	nac = chunkalloc(sizeof(AnchorClass));
2313 	*nac = *ac;
2314 	nac->subtable = sub;
2315 	nac->name = strconcat(mc->prefix,nac->name);
2316 	nac->next = mc->sf_to->anchor;
2317 	mc->sf_to->anchor = nac;
2318 
2319 	k=0;
2320 	do {
2321 	    fsf = mc->sf_from->subfontcnt==0 ? mc->sf_from : mc->sf_from->subfonts[k];
2322 	    for ( gid = 0; gid<fsf->glyphcnt; ++gid ) if ( (fsc = fsf->glyphs[gid])!=NULL ) {
2323 		for ( ap=fsc->anchor; ap!=NULL; ap=ap->next ) {
2324 		    if ( ap->anchor==ac ) {
2325 			tsc = SCFindOrMake(mc->sf_to,fsc);
2326 			if ( tsc==NULL )
2327 		break;
2328 			SF_SCAddAP(tsc,ap,nac);
2329 		    }
2330 		}
2331 	    }
2332 	    ++k;
2333 	} while ( k<mc->sf_from->subfontcnt );
2334     }
2335 }
2336 
SF_SCAddPST(SplineChar * tosc,PST * pst,struct lookup_subtable * sub)2337 static int SF_SCAddPST(SplineChar *tosc,PST *pst,struct lookup_subtable *sub) {
2338     PST *newpst;
2339 
2340     newpst = chunkalloc(sizeof(PST));
2341     *newpst = *pst;
2342     newpst->subtable = sub;
2343     newpst->next = tosc->possub;
2344     tosc->possub = newpst;
2345 
2346     switch( newpst->type ) {
2347       case pst_pair:
2348 	newpst->u.pair.paired = copy(pst->u.pair.paired);
2349 	newpst->u.pair.vr = chunkalloc(sizeof(struct vr [2]));
2350 	memcpy(newpst->u.pair.vr,pst->u.pair.vr,sizeof(struct vr [2]));
2351       break;
2352       case pst_ligature:
2353 	newpst->u.lig.lig = tosc;
2354 	/* Fall through */
2355       case pst_substitution:
2356       case pst_alternate:
2357       case pst_multiple:
2358 	newpst->u.subs.variant = copy(pst->u.subs.variant);
2359       break;
2360       default:
2361       break;
2362     }
2363 return( true );
2364 }
2365 
SF_SCAddKP(SplineChar * tosc,KernPair * kp,struct lookup_subtable * sub,int isvkern,SplineFont * to_sf)2366 static int SF_SCAddKP(SplineChar *tosc,KernPair *kp,struct lookup_subtable *sub,
2367 	int isvkern, SplineFont *to_sf ) {
2368     SplineChar *tosecond;
2369     KernPair *newkp;
2370 
2371     tosecond = SFGetChar(to_sf,kp->sc->unicodeenc,kp->sc->name);
2372     if ( tosecond==NULL )
2373 return( false );
2374 
2375     newkp = chunkalloc(sizeof(KernPair));
2376     *newkp = *kp;
2377     newkp->subtable = sub;
2378     newkp->sc = tosecond;
2379     if ( isvkern ) {
2380 	newkp->next = tosc->vkerns;
2381 	tosc->vkerns = newkp;
2382     } else {
2383 	newkp->next = tosc->kerns;
2384 	tosc->kerns = newkp;
2385     }
2386 return(true);
2387 }
2388 
SF_AddPSTKern(struct sfmergecontext * mc,struct lookup_subtable * from_sub,struct lookup_subtable * sub)2389 static void SF_AddPSTKern(struct sfmergecontext *mc,struct lookup_subtable *from_sub, struct lookup_subtable *sub) {
2390     int k, gid, isv;
2391     SplineFont *fsf;
2392     SplineChar *fsc, *tsc;
2393     PST *pst;
2394     KernPair *kp;
2395     int iskern = sub->lookup->lookup_type==gpos_pair;
2396 
2397     k=0;
2398     do {
2399 	fsf = mc->sf_from->subfontcnt==0 ? mc->sf_from : mc->sf_from->subfonts[k];
2400 	for ( gid = 0; gid<fsf->glyphcnt; ++gid ) if ( (fsc = fsf->glyphs[gid])!=NULL ) {
2401 	    tsc = (SplineChar *) -1;
2402 	    for ( pst = fsc->possub; pst!=NULL; pst=pst->next ) {
2403 		if ( pst->subtable==from_sub ) {
2404 		    if ( tsc==(SplineChar *) -1 ) {
2405 			tsc = SCFindOrMake(mc->sf_to,fsc);
2406 			if ( tsc==NULL )
2407 	    break;
2408 		    }
2409 		    SF_SCAddPST(tsc,pst,sub);
2410 		}
2411 	    }
2412 	    if ( tsc!=NULL && iskern ) {
2413 		for ( isv=0; isv<2 && tsc!=NULL; ++isv ) {
2414 		    for ( kp= isv ? fsc->vkerns : fsc->kerns; kp!=NULL; kp=kp->next ) {
2415 			if ( kp->subtable==sub ) {
2416 			    /* Kerning data tend to be individualistic. Only copy if */
2417 			    /*  glyphs exist */
2418 			    if ( tsc==(SplineChar *) -1 ) {
2419 				tsc = SFGetChar(mc->sf_to,fsc->unicodeenc,fsc->name);
2420 				if ( tsc==NULL )
2421 		    break;
2422 			    }
2423 			    SF_SCAddKP(tsc,kp,sub,isv,mc->sf_to);
2424 			}
2425 		    }
2426 		}
2427 	    }
2428 	}
2429 	++k;
2430     } while ( k<mc->sf_from->subfontcnt );
2431 }
2432 
_FeatureOrderId(int isgpos,uint32 tag)2433 int _FeatureOrderId( int isgpos,uint32 tag ) {
2434     /* This is the order in which features should be executed */
2435 
2436     if ( !isgpos ) switch ( tag ) {
2437 /* GSUB ordering */
2438       case CHR('c','c','m','p'):	/* Must be first? */
2439 return( -2 );
2440       case CHR('l','o','c','l'):	/* Language dependent letter forms (serbian uses some different glyphs than russian) */
2441 return( -1 );
2442       case CHR('i','s','o','l'):
2443 return( 0 );
2444       case CHR('j','a','l','t'):		/* must come after 'isol' */
2445 return( 1 );
2446       case CHR('f','i','n','a'):
2447 return( 2 );
2448       case CHR('f','i','n','2'):
2449       case CHR('f','a','l','t'):		/* must come after 'fina' */
2450 return( 3 );
2451       case CHR('f','i','n','3'):
2452 return( 4 );
2453       case CHR('m','e','d','i'):
2454 return( 5 );
2455       case CHR('m','e','d','2'):
2456 return( 6 );
2457       case CHR('i','n','i','t'):
2458 return( 7 );
2459 
2460       case CHR('r','t','l','a'):
2461 return( 100 );
2462       case CHR('s','m','c','p'): case CHR('c','2','s','c'):
2463 return( 200 );
2464 
2465       case CHR('r','l','i','g'):
2466 return( 300 );
2467       case CHR('c','a','l','t'):
2468 return( 301 );
2469       case CHR('l','i','g','a'):
2470 return( 302 );
2471       case CHR('d','l','i','g'): case CHR('h','l','i','g'):
2472 return( 303 );
2473       case CHR('c','s','w','h'):
2474 return( 304 );
2475       case CHR('m','s','e','t'):
2476 return( 305 );
2477 
2478       case CHR('f','r','a','c'):
2479 return( 306 );
2480 
2481 /* Indic processing */
2482       case CHR('n','u','k','t'):
2483       case CHR('p','r','e','f'):
2484 return( 301 );
2485       case CHR('a','k','h','n'):
2486 return( 302 );
2487       case CHR('r','p','h','f'):
2488 return( 303 );
2489       case CHR('b','l','w','f'):
2490 return( 304 );
2491       case CHR('h','a','l','f'):
2492       case CHR('a','b','v','f'):
2493 return( 305 );
2494       case CHR('p','s','t','f'):
2495 return( 306 );
2496       case CHR('v','a','t','u'):
2497 return( 307 );
2498 
2499       case CHR('p','r','e','s'):
2500 return( 310 );
2501       case CHR('b','l','w','s'):
2502 return( 311 );
2503       case CHR('a','b','v','s'):
2504 return( 312 );
2505       case CHR('p','s','t','s'):
2506 return( 313 );
2507       case CHR('c','l','i','g'):
2508 return( 314 );
2509 
2510       case CHR('h','a','l','n'):
2511 return( 320 );
2512 /* end indic ordering */
2513 
2514       case CHR('a','f','r','c'):
2515       case CHR('l','j','m','o'):
2516       case CHR('v','j','m','o'):
2517 return( 350 );
2518       case CHR('v','r','t','2'): case CHR('v','e','r','t'):
2519 return( 1010 );		/* Documented to come last */
2520 
2521 /* Unknown things come after everything but vert/vrt2 */
2522       default:
2523 return( 1000 );
2524 
2525     } else switch ( tag ) {
2526 /* GPOS ordering */
2527       case CHR('c','u','r','s'):
2528 return( 0 );
2529       case CHR('d','i','s','t'):
2530 return( 100 );
2531       case CHR('b','l','w','m'):
2532 return( 201 );
2533       case CHR('a','b','v','m'):
2534 return( 202 );
2535       case CHR('k','e','r','n'):
2536 return( 300 );
2537       case CHR('m','a','r','k'):
2538 return( 400 );
2539       case CHR('m','k','m','k'):
2540 return( 500 );
2541 /* Unknown things come after everything  */
2542       default:
2543 return( 1000 );
2544     }
2545 }
2546 
FeatureOrderId(int isgpos,FeatureScriptLangList * fl)2547 int FeatureOrderId( int isgpos,FeatureScriptLangList *fl ) {
2548     int pos = 9999, temp;
2549 
2550     if ( fl==NULL )
2551 return( 0 );
2552 
2553     while ( fl!=NULL ) {
2554 	temp = _FeatureOrderId(isgpos,fl->featuretag );
2555 	if ( temp<pos ) pos = temp;
2556 	fl = fl->next;
2557     }
2558 return( pos );
2559 }
2560 
SortInsertLookup(SplineFont * sf,OTLookup * newotl)2561 void SortInsertLookup(SplineFont *sf, OTLookup *newotl) {
2562     int isgpos = newotl->lookup_type>=gpos_start;
2563     int pos;
2564     OTLookup *prev, *otl;
2565 
2566     pos = FeatureOrderId(isgpos,newotl->features);
2567     for ( prev=NULL, otl= isgpos ? sf->gpos_lookups : sf->gsub_lookups ;
2568 	    otl!=NULL && FeatureOrderId(isgpos,newotl->features)<pos;
2569 	    prev = otl, otl=otl->next );
2570     newotl->next = otl;
2571     if ( prev!=NULL )
2572 	prev->next = newotl;
2573     else if ( isgpos )
2574 	sf->gpos_lookups = newotl;
2575     else
2576 	sf->gsub_lookups = newotl;
2577 }
2578 
2579 /* Before may be:
2580     * A lookup in into_sf, in which case insert new lookup before it
2581     * NULL               , in which case insert new lookup at end
2582     * -1                 , in which case insert new lookup at start
2583     * -2                 , try to guess a good position
2584 */
OrderNewLookup(SplineFont * into_sf,OTLookup * otl,OTLookup * before)2585 static void OrderNewLookup(SplineFont *into_sf,OTLookup *otl,OTLookup *before) {
2586     int isgpos = otl->lookup_type>=gpos_start;
2587     OTLookup **head = isgpos ? &into_sf->gpos_lookups : &into_sf->gsub_lookups;
2588     OTLookup *prev;
2589 
2590     if ( before == (OTLookup *) -2 )
2591 	SortInsertLookup(into_sf,otl);
2592     else if ( before == (OTLookup *) -1 || *head==NULL || *head==before ) {
2593 	otl->next = *head;
2594 	*head = otl;
2595     } else {
2596 	for ( prev= *head; prev->next!=NULL && prev->next!=before ; prev=prev->next );
2597 	otl->next = prev->next;
2598 	prev->next = otl;
2599     }
2600 }
2601 
_OTLookupCopyInto(struct sfmergecontext * mc,OTLookup * from_otl,OTLookup * before,int do_contents)2602 static OTLookup *_OTLookupCopyInto(struct sfmergecontext *mc,
2603 	OTLookup *from_otl, OTLookup *before, int do_contents) {
2604     OTLookup *otl;
2605     struct lookup_subtable *sub, *last, *from_sub;
2606     int scnt, l;
2607 
2608     for ( l=0; l<mc->lcnt; ++l ) {
2609 	if ( mc->lks[l].from == from_otl ) {
2610 	    if ( mc->lks[l].old )
2611 return( mc->lks[l].to );
2612 	    else
2613     break;
2614 	}
2615     }
2616 
2617     if ( l>=mc->lmax )
2618 	mc->lks = realloc(mc->lks,(mc->lmax += 20)*sizeof(struct lookup_cvt));
2619     mc->sf_to->changed = true;
2620 
2621     if ( l>=mc->lcnt ) {
2622 	otl = chunkalloc(sizeof(OTLookup));
2623 	*otl = *from_otl;
2624 	memset(&mc->lks[l],0,sizeof(mc->lks[l]));
2625 	mc->lks[l].from = from_otl; mc->lks[l].to = otl; ++mc->lcnt;
2626 	otl->lookup_name = strconcat(mc->prefix,from_otl->lookup_name);
2627 	otl->features = FeatureListCopy(from_otl->features);
2628 	otl->next = NULL; otl->subtables = NULL;
2629 	OrderNewLookup(mc->sf_to,otl,before);
2630 	if ( !do_contents )
2631 	    FIOTLookupCopyInto(mc->sf_to,mc->sf_from, from_otl, otl, 0, before);
2632     } else
2633 	otl = mc->lks[l].to;
2634     if ( !do_contents )
2635 return( otl );
2636 
2637     last = NULL;
2638     scnt = 0;
2639     for ( from_sub = from_otl->subtables; from_sub!=NULL; from_sub=from_sub->next ) {
2640 	sub = chunkalloc(sizeof(struct lookup_subtable));
2641 	*sub = *from_sub;
2642 	sub->lookup = otl;
2643 	sub->subtable_name = strconcat(mc->prefix,from_sub->subtable_name);
2644 	sub->suffix = copy(sub->suffix);
2645 	if ( last==NULL )
2646 	    otl->subtables = sub;
2647 	else
2648 	    last->next = sub;
2649 	last = sub;
2650 	if ( from_sub->kc!=NULL )
2651 	    sub->kc = SF_AddKernClass(mc, from_sub->kc, sub);
2652 	else if ( from_sub->fpst!=NULL )
2653 	    sub->fpst = SF_AddFPST(mc, from_sub->fpst, sub);
2654 	else if ( from_sub->sm!=NULL )
2655 	    sub->sm = SF_AddASM(mc, from_sub->sm, sub);
2656 	else if ( from_sub->anchor_classes )
2657 	    SF_AddAnchorClasses(mc, from_sub, sub);
2658 	else
2659 	    SF_AddPSTKern(mc, from_sub, sub);
2660 	++scnt;
2661     }
2662     FIOTLookupCopyInto(mc->sf_to,mc->sf_from, from_otl, otl, scnt, before);
2663 return( otl );
2664 }
2665 
NeedsPrefix(SplineFont * into_sf,SplineFont * from_sf,OTLookup ** list)2666 static int NeedsPrefix(SplineFont *into_sf,SplineFont *from_sf, OTLookup **list) {
2667     struct lookup_subtable *from_sub;
2668     int i,j,k;
2669     OTLookup *sublist[2];
2670 
2671     sublist[1] = NULL;
2672 
2673     if ( list==NULL || list[0]==NULL )
2674 return( false );
2675     for ( k=0; list[k]!=NULL; ++k ) {
2676 	OTLookup *from_otl = list[k];
2677 	if ( SFFindLookup(into_sf,from_otl->lookup_name)!=NULL )
2678 return( true );
2679 	for ( from_sub = from_otl->subtables; from_sub!=NULL; from_sub=from_sub->next ) {
2680 	    if ( from_sub->fpst!=NULL ) {
2681 		for ( i=0; i<from_sub->fpst->rule_cnt; ++i ) {
2682 		    struct fpst_rule *r = &from_sub->fpst->rules[i];
2683 		    for ( j=0; j<r->lookup_cnt; ++j ) {
2684 			sublist[0] = r->lookups[j].lookup;
2685 			if ( NeedsPrefix(into_sf,from_sf, sublist))
2686 return( true );
2687 		    }
2688 		}
2689 	    } else if ( from_sub->sm!=NULL && from_sub->sm->type==asm_context ) {
2690 		for ( i=0; i<from_sub->sm->class_cnt*from_sub->sm->state_cnt; ++i ) {
2691 		    sublist[0] = from_sub->sm->state[i].u.context.mark_lookup;
2692 		    if ( NeedsPrefix(into_sf,from_sf,sublist))
2693 return( true );
2694 		    sublist[0] = from_sub->sm->state[i].u.context.cur_lookup;
2695 		    if ( NeedsPrefix(into_sf,from_sf,sublist))
2696 return( true );
2697 		}
2698 	    }
2699 	}
2700     }
2701 return( false );
2702 }
2703 
OTLookupCopyInto(SplineFont * into_sf,SplineFont * from_sf,OTLookup * from_otl)2704 OTLookup *OTLookupCopyInto(SplineFont *into_sf,SplineFont *from_sf, OTLookup *from_otl) {
2705     OTLookup *newotl, *list[2];
2706     struct sfmergecontext mc;
2707 
2708     memset(&mc,0,sizeof(mc));
2709     mc.sf_from = from_sf; mc.sf_to = into_sf;
2710 
2711     list[0] = from_otl; list[1] = NULL;
2712     mc.prefix = NeedsPrefix(into_sf,from_sf,list)
2713 	    ? strconcat(from_sf->fontname,"-") : copy("");
2714     newotl = _OTLookupCopyInto(&mc,from_otl,(OTLookup *) -2,true);
2715     free(mc.lks);
2716     free(mc.prefix);
2717 return( newotl );
2718 }
2719 
OTLookupsCopyInto(SplineFont * into_sf,SplineFont * from_sf,OTLookup ** list,OTLookup * before)2720 void OTLookupsCopyInto(SplineFont *into_sf,SplineFont *from_sf,
2721 	OTLookup **list, OTLookup *before) {
2722     int i, do_contents;
2723     struct sfmergecontext mc;
2724 
2725     memset(&mc,0,sizeof(mc));
2726     mc.sf_from = from_sf; mc.sf_to = into_sf;
2727 
2728     mc.prefix = NeedsPrefix(into_sf,from_sf,list)
2729 	    ? strconcat(from_sf->fontname,"-") : copy("");
2730     for ( i=0; list[i]!=NULL; ++i );
2731     mc.lks = malloc((mc.lmax=i+5)*sizeof(struct lookup_cvt));
2732     /* First create all the lookups and position them in the right order */
2733     /*  then create subtables (which may in turn create some new lookups */
2734     /*  for contextual lookups which invoke other lookups, don't care how */
2735     /*  those nested lookups are ordered) */
2736     for ( do_contents=0; do_contents<2; ++do_contents )
2737 	for ( i=0; list[i]!=NULL; ++i )
2738 	    (void) _OTLookupCopyInto(&mc,list[i],before,do_contents);
2739     free(mc.lks);
2740     free(mc.prefix);
2741 }
2742 
2743 /* ************************************************************************** */
2744 /* ****************************** Apply lookups ***************************** */
2745 /* ************************************************************************** */
2746 
2747 struct lookup_data {
2748     struct opentype_str *str;
2749     int cnt, max;
2750 
2751     uint32 script;
2752     SplineFont *sf;
2753 
2754     struct lookup_subtable *lig_owner;
2755     int lcnt, lmax;
2756     SplineChar ***ligs;		/* For each ligature we have an array of SplineChars that are its components preceded by the ligature glyph itself */
2757 				/*  NULL terminated */
2758     int pixelsize;
2759     double scale;
2760 };
2761 
2762 static int ApplyLookupAtPos(uint32 tag, OTLookup *otl,struct lookup_data *data,int pos);
2763 
GlyphNameInClass(const char * name,const char * class)2764 static int GlyphNameInClass(const char *name,const char *class) {
2765     const char *pt;
2766     int len = strlen(name);
2767 
2768     for (pt = class; pt && (pt=strstr(pt,name))!=NULL; pt += len) {
2769 	if ( (pt==class || pt[-1]==' ') && (pt[len]=='\0' || pt[len]==' '))
2770 return( true );
2771     }
2772 
2773 return( false );
2774 }
2775 
2776 /* ************************************************************************** */
2777 /* ************************ Apply Apple State Machines ********************** */
2778 /* ************************************************************************** */
2779 
ApplyMacIndicRearrangement(struct lookup_data * data,int verb,int first_pos,int last_pos)2780 static void ApplyMacIndicRearrangement(struct lookup_data *data,int verb,
2781 	int first_pos,int last_pos) {
2782     struct opentype_str temp, temp2, temp3, temp4;
2783     int i;
2784 
2785     if ( first_pos==-1 || last_pos==-1 || last_pos <= first_pos )
2786 return;
2787     switch ( verb ) {
2788       case 1: /* Ax => xA */
2789 	temp = data->str[first_pos];
2790 	for ( i= first_pos; i<last_pos; ++i )
2791 	    data->str[i] = data->str[i+1];
2792 	data->str[last_pos] = temp;
2793       break;
2794       case 2: /* xD => Dx */
2795 	temp = data->str[last_pos];
2796 	for ( i= last_pos; i>first_pos; --i )
2797 	    data->str[i] = data->str[i-1];
2798 	data->str[first_pos] = temp;
2799       break;
2800       case 3: /* AxD => DxA */
2801 	temp = data->str[last_pos];
2802 	data->str[last_pos] = data->str[first_pos];
2803 	data->str[first_pos] = temp;
2804       break;
2805       case 4: /* ABx => xAB */
2806 	temp = data->str[first_pos];
2807 	temp2 = data->str[first_pos+1];
2808 	for ( i= first_pos; i<last_pos-1; ++i )
2809 	    data->str[i] = data->str[i+21];
2810 	data->str[last_pos-1] = temp;
2811 	data->str[last_pos] = temp2;
2812       break;
2813       case 5: /* ABx => xBA */
2814 	temp = data->str[first_pos];
2815 	temp2 = data->str[first_pos+1];
2816 	for ( i= first_pos; i<last_pos-1; ++i )
2817 	    data->str[i] = data->str[i+21];
2818 	data->str[last_pos-1] = temp2;
2819 	data->str[last_pos] = temp;
2820       break;
2821       case 6: /* xCD => CDx */
2822 	temp = data->str[last_pos];
2823 	temp2 = data->str[last_pos-1];
2824 	for ( i= last_pos; i>first_pos+1; --i )
2825 	    data->str[i] = data->str[i-2];
2826 	data->str[first_pos+1] = temp;
2827 	data->str[first_pos] = temp2;
2828       break;
2829       case 7: /* xCD => DCx */
2830 	temp = data->str[last_pos];
2831 	temp2 = data->str[last_pos-1];
2832 	for ( i= last_pos; i>first_pos+1; --i )
2833 	    data->str[i] = data->str[i-2];
2834 	data->str[first_pos+1] = temp2;
2835 	data->str[first_pos] = temp;
2836       break;
2837       case 8: /* AxCD => CDxA */
2838 	temp = data->str[first_pos];
2839 	temp2 = data->str[last_pos-1];
2840 	temp3 = data->str[last_pos];
2841 	for ( i= last_pos-1; i>first_pos; --i )
2842 	    data->str[i] = data->str[i-1];
2843 	data->str[first_pos+1] = temp2;
2844 	data->str[first_pos] = temp3;
2845 	data->str[last_pos] = temp;
2846       break;
2847       case 9: /* AxCD => DCxA */
2848 	temp = data->str[first_pos];
2849 	temp2 = data->str[last_pos-1];
2850 	temp3 = data->str[last_pos];
2851 	for ( i= last_pos-1; i>first_pos; --i )
2852 	    data->str[i] = data->str[i-1];
2853 	data->str[first_pos+1] = temp3;
2854 	data->str[first_pos] = temp2;
2855 	data->str[last_pos] = temp;
2856       break;
2857       case 10: /* ABxD => DxAB */
2858 	temp = data->str[first_pos];
2859 	temp2 = data->str[first_pos+1];
2860 	temp3 = data->str[last_pos];
2861 	for ( i= first_pos+1; i<last_pos; ++i )
2862 	    data->str[i] = data->str[i+1];
2863 	data->str[first_pos] = temp3;
2864 	data->str[last_pos-1] = temp;
2865 	data->str[last_pos] = temp2;
2866       break;
2867       case 11: /* ABxD => DxBA */
2868 	temp = data->str[first_pos];
2869 	temp2 = data->str[first_pos+1];
2870 	temp3 = data->str[last_pos];
2871 	for ( i= first_pos+1; i<last_pos; ++i )
2872 	    data->str[i] = data->str[i+1];
2873 	data->str[first_pos] = temp3;
2874 	data->str[last_pos-1] = temp2;
2875 	data->str[last_pos] = temp;
2876       break;
2877       case 12: /* ABxCD => CDxAB */
2878 	temp = data->str[first_pos];
2879 	temp2 = data->str[first_pos+1];
2880 	temp3 = data->str[last_pos-1];
2881 	temp4 = data->str[last_pos];
2882 	data->str[last_pos] = temp2;
2883 	data->str[last_pos-1] = temp;
2884 	data->str[first_pos+1] = temp4;
2885 	data->str[first_pos] = temp3;
2886       break;
2887       case 13: /* ABxCD => CDxBA */
2888 	temp = data->str[first_pos];
2889 	temp2 = data->str[first_pos+1];
2890 	temp3 = data->str[last_pos-1];
2891 	temp4 = data->str[last_pos];
2892 	data->str[last_pos] = temp;
2893 	data->str[last_pos-1] = temp2;
2894 	data->str[first_pos+1] = temp4;
2895 	data->str[first_pos] = temp3;
2896       break;
2897       case 14: /* ABxCD => DCxAB */
2898 	temp = data->str[first_pos];
2899 	temp2 = data->str[first_pos+1];
2900 	temp3 = data->str[last_pos-1];
2901 	temp4 = data->str[last_pos];
2902 	data->str[last_pos] = temp2;
2903 	data->str[last_pos-1] = temp;
2904 	data->str[first_pos+1] = temp3;
2905 	data->str[first_pos] = temp4;
2906       break;
2907       case 15: /* ABxCD => DCxBA */
2908 	temp = data->str[first_pos];
2909 	temp2 = data->str[first_pos+1];
2910 	temp3 = data->str[last_pos-1];
2911 	temp4 = data->str[last_pos];
2912 	data->str[last_pos] = temp;
2913 	data->str[last_pos-1] = temp2;
2914 	data->str[first_pos+1] = temp3;
2915 	data->str[first_pos] = temp4;
2916       break;
2917       default:
2918       break;
2919     }
2920 }
2921 
ApplyMacInsert(struct lookup_data * data,int ipos,int cnt,char * glyphnames,int orig_index)2922 static int ApplyMacInsert(struct lookup_data *data,int ipos,int cnt,
2923 	char *glyphnames, int orig_index) {
2924     SplineChar *inserts[32];
2925     char *start, *pt;
2926     int i, ch;
2927 
2928     if ( cnt==0 || glyphnames==NULL || ipos == -1 )
2929 return( 0 );
2930 
2931     for ( i=0, start = glyphnames; i<cnt; ) {
2932 	while ( *start==' ' ) ++start;
2933 	if ( *start=='\0' )
2934     break;
2935 	for ( pt = start; *pt!=' ' && *pt!='\0'; ++pt );
2936 	ch = *pt; *pt = '\0';
2937 	inserts[i] = SFGetChar(data->sf,-1,start);
2938 	*pt = ch;
2939 	if ( inserts[i]!=NULL )
2940 	    ++i;
2941     }
2942     cnt = i;
2943     if ( i==0 )
2944 return( 0 );
2945     for ( i= data->cnt; i>=ipos; --i )
2946 	data->str[i+cnt] = data->str[i];
2947     memset(data->str+ipos,0,cnt*sizeof(struct opentype_str));
2948     for ( i=0; i<cnt; ++i ) {
2949 	data->str[ipos+i].sc = inserts[i];
2950 	data->str[ipos+i].orig_index = orig_index;
2951     }
2952 return( cnt );
2953 }
2954 
ApplyAppleStateMachine(OTLookup * otl,struct lookup_data * data)2955 static void ApplyAppleStateMachine(OTLookup *otl,struct lookup_data *data) {
2956     struct lookup_subtable *sub;
2957     int state, class, pos, mark_pos, markend_pos, i;
2958     ASM *sm;
2959     int cnt_cur, cnt_mark;
2960     struct asm_state *entry;
2961     int kern_stack[8], kcnt;		/* Kerning state machines handle at most 8 glyphs */
2962     /* Flaws: Line processing has not been done yet, so we are never in the */
2963     /*  start of line state and we never get an end of line token. We never */
2964     /*  get deleted tokens either, those glyphs are just gone */
2965     /* Class 0: End of text */
2966     /* Class 1: Glyph not in any classes */
2967     /* Class 2: Deleted (we never see) */
2968     /* Class 3: End of line (we never see) */
2969 
2970     /* Mac doesn't have the concept of subtables, but a user could create one */
2971     /*  it will get flattened out into its own "lookup" when written to a file*/
2972     /*  So if there are multiple subtables, just process them all */
2973     for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
2974 	sm = sub->sm;
2975 
2976 	state = 0;
2977 	mark_pos = markend_pos = -1;
2978 	for ( pos = 0; pos<=data->cnt; ) {
2979 	    if ( pos==data->cnt )
2980 		class = 0;
2981 	    else {
2982 		for ( class = sm->class_cnt-1; class>3; --class )
2983 		    if ( GlyphNameInClass(data->str[i].sc->name,sm->classes[class]) )
2984 		break;
2985 		if ( class==3 )
2986 		    class = 1;		/* Glyph not in any class */;
2987 	    }
2988 	    entry = &sm->state[state*sm->class_cnt+class];
2989 	    switch ( otl->lookup_type ) {
2990 	      case morx_context:
2991 		if ( entry->u.context.cur_lookup!=NULL )
2992 		    ApplyLookupAtPos(0,entry->u.context.cur_lookup,data,pos);
2993 		if ( entry->u.context.mark_lookup!=NULL && mark_pos!=-1 ) {
2994 		    ApplyLookupAtPos(0,entry->u.context.mark_lookup,data,mark_pos);
2995 		    mark_pos = -1;
2996 		}
2997 	      break;
2998 	      case morx_indic:
2999 		if ( entry->flags & 0x2000 )
3000 		    markend_pos = pos;
3001 		if ( (entry->flags&0xf)!=0 ) {
3002 		    ApplyMacIndicRearrangement(data,entry->flags&0xf,mark_pos,markend_pos);
3003 		    mark_pos = markend_pos = -1;
3004 		}
3005 	      break;
3006 	      case morx_insert:
3007 		/* I live in total ignorance of what I should do if the glyph */
3008 		/*  "is Kashida like"... so I ignore those flags. */
3009 		cnt_cur = (entry->flags>>5)&0x1f;
3010 		cnt_mark = (entry->flags&0x1f);
3011 		if ( data->cnt + cnt_cur + cnt_mark >= data->max )
3012 		    data->str = realloc(data->str,(data->max = data->cnt + cnt_cur + cnt_mark +20)*sizeof(struct opentype_str));
3013 		if ( cnt_cur!=0 )
3014 		    cnt_cur = ApplyMacInsert(data,(entry->flags& 0x0800)? pos : pos+1,
3015 			    cnt_cur,entry->u.insert.cur_ins,data->str[pos].orig_index);
3016 		if ( cnt_mark!=0 && mark_pos!=-1 ) {
3017 		    cnt_mark = ApplyMacInsert(data,(entry->flags& 0x0800)? mark_pos : mark_pos+1,
3018 			    cnt_mark,entry->u.insert.mark_ins,data->str[mark_pos].orig_index);
3019 		    mark_pos = -1;
3020 		} else
3021 		    cnt_mark = 0;
3022 		pos += cnt_cur+cnt_mark;
3023 	      break;
3024 	      case kern_statemachine:
3025 		if ( entry->u.kern.kcnt!=0 ) {
3026 		    for ( i=0; i<kcnt && i<entry->u.kern.kcnt; ++i )
3027 			data->str[kern_stack[i]].vr.h_adv_off +=
3028 				entry->u.kern.kerns[i];
3029 		    kcnt = 0;
3030 		}
3031 		if ( entry->flags & 0x8000 ) {
3032 		    for ( i=6; i>=0; --i )
3033 			kern_stack[i+1] = kern_stack[i];
3034 		    kern_stack[0] = pos;
3035 		    if ( ++kcnt>8 ) kcnt = 8;
3036 		}
3037 	      break;
3038 	      default:
3039 	      break;
3040 	    }
3041 	    if ( entry->flags & 0x8000 )
3042 		mark_pos = pos;		/* The docs do not state whether this happens before or after substitutions are applied at the mark */
3043 					/* after is more useful. So assume that */
3044 	    if ( !(entry->flags & 0x4000) )
3045 		++pos;
3046 	    state = entry->next_state;
3047 	}
3048     }
3049 }
3050 
3051 /* ************************************************************************** */
3052 /* ************************* Apply OpenType Lookups ************************* */
3053 /* ************************************************************************** */
3054 
LigatureFree(struct lookup_data * data)3055 static void LigatureFree(struct lookup_data *data) {
3056     int i;
3057 
3058     if ( data->ligs==NULL )
3059 return;
3060     for ( i=0; data->ligs[i]!=NULL; ++i )
3061 	free(data->ligs[i]);
3062 }
3063 
LigatureSearch(struct lookup_subtable * sub,struct lookup_data * data)3064 static void LigatureSearch(struct lookup_subtable *sub, struct lookup_data *data) {
3065     SplineFont *sf = data->sf;
3066     int gid, ccnt, cnt, ch, err;
3067     SplineChar *sc;
3068     PST *pst;
3069     char *pt, *start;
3070 
3071     LigatureFree(data);
3072     cnt = 0;
3073     for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
3074 	for ( pst=sc->possub; pst!=NULL; pst=pst->next ) if ( pst->subtable==sub ) {
3075 	    for ( pt = pst->u.lig.components, ccnt=0; *pt; ++pt )
3076 		if ( *pt==' ' )
3077 		    ++ccnt;
3078 	    if ( cnt>=data->lmax )
3079 		data->ligs = realloc(data->ligs,(data->lmax+=100)*sizeof(SplineChar **));
3080 	    data->ligs[cnt] = malloc((ccnt+3)*sizeof(SplineChar *));
3081 	    data->ligs[cnt][0] = sc;
3082 	    ccnt = 1;
3083 	    err = 0;
3084 	    for ( pt = pst->u.lig.components; *pt; ) {
3085 		while ( *pt==' ' ) ++pt;
3086 		if ( *pt=='\0' )
3087 	    break;
3088 		for ( start=pt; *pt!='\0' && *pt!=' '; ++pt );
3089 		ch = *pt; *pt = '\0';
3090 		data->ligs[cnt][ccnt++] = SFGetChar(sf,-1,start);
3091 		*pt = ch;
3092 		if ( data->ligs[cnt][ccnt-1]==NULL )
3093 		    err = 1;
3094 	    }
3095 	    if ( !err )
3096 		data->ligs[cnt++][ccnt] = NULL;
3097 	}
3098     }
3099     if ( cnt>=data->lmax )
3100 	data->ligs = realloc(data->ligs,(data->lmax+=1)*sizeof(SplineChar **));
3101     data->ligs[cnt] = NULL;
3102     data->lcnt = cnt;
3103 }
3104 
skipglyphs(int lookup_flags,struct lookup_data * data,int pos)3105 static int skipglyphs(int lookup_flags, struct lookup_data *data, int pos) {
3106     int mc, glyph_class, ms;
3107     /* The lookup flags tell us what glyphs to ignore. Skip over anything we */
3108     /*  should ignore */
3109 
3110     if ( lookup_flags==0 )
3111 return( pos );
3112     mc = (lookup_flags>>8);
3113     if ( mc<0 || mc>=data->sf->mark_class_cnt )
3114 	mc = 0;
3115     ms = lookup_flags>>16;
3116     if ( !(lookup_flags&pst_usemarkfilteringset) || ms>=data->sf->mark_set_cnt )
3117 	ms = -1;
3118     while ( pos<data->cnt ) {
3119 	glyph_class = gdefclass(data->str[pos].sc);
3120 	/* 1=>base, 2=>ligature, 3=>mark, 4=>component?, 0=>.notdef */
3121 	if ( (glyph_class==1 && (lookup_flags&pst_ignorebaseglyphs)) ||
3122 		(glyph_class==2 && (lookup_flags&pst_ignoreligatures)) ||
3123 		(glyph_class==3 && (lookup_flags&pst_ignorecombiningmarks)) ||
3124 		(glyph_class==3 && mc!=0 &&
3125 			!GlyphNameInClass(data->str[pos].sc->name,data->sf->mark_classes[mc])) ||
3126 		(ms>=0 &&
3127 			!GlyphNameInClass(data->str[pos].sc->name,data->sf->mark_sets[ms])) ) {
3128 	    ++pos;
3129 	} else
3130     break;
3131     }
3132 return( pos );
3133 }
3134 
bskipmarkglyphs(int lookup_flags,struct lookup_data * data,int pos)3135 static int bskipmarkglyphs(int lookup_flags, struct lookup_data *data, int pos) {
3136     int mc, glyph_class, ms;
3137     /* The lookup flags tell us what glyphs to ignore. Skip over anything we */
3138     /*  should ignore. Here we skip backward */
3139 
3140     mc = (lookup_flags>>8);
3141     if ( mc<0 || mc>=data->sf->mark_class_cnt )
3142 	mc = 0;
3143     ms = lookup_flags>>16;
3144     if ( !(lookup_flags&pst_usemarkfilteringset) || ms>=data->sf->mark_set_cnt )
3145 	ms = -1;
3146     while ( pos>=0 ) {
3147 	glyph_class = gdefclass(data->str[pos].sc);
3148 	/* 1=>base, 2=>ligature, 3=>mark, 4=>component?, 0=>.notdef */
3149 	if ( glyph_class==3 )
3150 	    --pos;
3151 	else if ( (glyph_class==1 && (lookup_flags&pst_ignorebaseglyphs)) ||
3152 		(glyph_class==2 && (lookup_flags&pst_ignoreligatures)) ||
3153 		(glyph_class==3 && (lookup_flags&pst_ignorecombiningmarks)) ||
3154 		(glyph_class==3 && mc!=0 &&
3155 			!GlyphNameInClass(data->str[pos].sc->name,data->sf->mark_classes[mc])) ||
3156 		(ms>=0 &&
3157 			!GlyphNameInClass(data->str[pos].sc->name,data->sf->mark_sets[ms])) ) {
3158 	    --pos;
3159 	} else
3160     break;
3161     }
3162 return( pos );
3163 }
3164 
bskipglyphs(int lookup_flags,struct lookup_data * data,int pos)3165 static int bskipglyphs(int lookup_flags, struct lookup_data *data, int pos) {
3166     int mc, glyph_class, ms;
3167     /* The lookup flags tell us what glyphs to ignore. Skip over anything we */
3168     /*  should ignore. Here we skip backward */
3169 
3170     if ( lookup_flags==0 )
3171 return( pos );
3172     mc = (lookup_flags>>8);
3173     if ( mc<0 || mc>=data->sf->mark_class_cnt )
3174 	mc = 0;
3175     ms = lookup_flags>>16;
3176     if ( !(lookup_flags&pst_usemarkfilteringset) || ms>=data->sf->mark_set_cnt )
3177 	ms = -1;
3178     while ( pos>=0 ) {
3179 	glyph_class = gdefclass(data->str[pos].sc);
3180 	/* 1=>base, 2=>ligature, 3=>mark, 4=>component?, 0=>.notdef */
3181 	if ( (glyph_class==1 && (lookup_flags&pst_ignorebaseglyphs)) ||
3182 		(glyph_class==2 && (lookup_flags&pst_ignoreligatures)) ||
3183 		(glyph_class==3 && (lookup_flags&pst_ignorecombiningmarks)) ||
3184 		(glyph_class==3 && mc!=0 &&
3185 			!GlyphNameInClass(data->str[pos].sc->name,data->sf->mark_classes[mc])) ||
3186 		(ms>=0 &&
3187 			!GlyphNameInClass(data->str[pos].sc->name,data->sf->mark_sets[ms])) ) {
3188 	    --pos;
3189 	} else
3190     break;
3191     }
3192 return( pos );
3193 }
3194 
ContextualMatch(struct lookup_subtable * sub,struct lookup_data * data,int pos,struct fpst_rule ** _rule)3195 static int ContextualMatch(struct lookup_subtable *sub,struct lookup_data *data,
3196 	int pos, struct fpst_rule **_rule) {
3197     int i, cpos, retpos, r;
3198     FPST *fpst = sub->fpst;
3199     int lookup_flags = sub->lookup->lookup_flags;
3200     const char *pt;
3201 
3202     if (fpst==NULL)
3203         return 0;
3204     /* If we should skip the current glyph then don't try for a match here */
3205     cpos = skipglyphs(lookup_flags,data,pos);
3206     if ( cpos!=pos )
3207 return( 0 );
3208 
3209     for ( r=0; r<fpst->rule_cnt; ++r ) {
3210 	struct fpst_rule *rule = &fpst->rules[r];
3211 	for ( i=pos; i<data->cnt; ++i )
3212 	    data->str[i].context_pos = -1;
3213 
3214 /* Handle backtrack (backtrace in the rule is stored in reverse textual order) */
3215 	if ( fpst->type == pst_chainpos || fpst->type == pst_chainsub ) {
3216 	    if ( fpst->format==pst_glyphs ) {
3217 		pt = rule->u.glyph.back;
3218 		for ( i=bskipglyphs(lookup_flags,data,pos-1), cpos=0; i>=0; i = bskipglyphs(lookup_flags,data,i-1)) {
3219 		    const char *name = data->str[i].sc->name;
3220 		    int len = strlen( name );
3221 		    if ( strncmp(name,pt,len)!=0 || (pt[len]!='\0' && pt[len]!=' '))
3222 		break;
3223 		    pt += len;
3224 		    while ( *pt==' ' ) ++pt;
3225 		}
3226 		if ( *pt!='\0' )
3227     continue;		/* didn't match */
3228 	    } else if ( fpst->format==pst_class ) {
3229 		for ( i=bskipglyphs(lookup_flags,data,pos-1), cpos=0; i>=0 && cpos<rule->u.class.bcnt; i = bskipglyphs(lookup_flags,data,i-1)) {
3230 		    if ( !GlyphNameInClass(data->str[i].sc->name,fpst->bclass[rule->u.class.bclasses[cpos]]) )
3231 		break;
3232 		    ++cpos;
3233 		}
3234 		if ( cpos!=rule->u.class.bcnt )
3235     continue;		/* didn't match */
3236 	    } else if ( fpst->format==pst_coverage ) {
3237 		for ( i=bskipglyphs(lookup_flags,data,pos-1), cpos=0; i>=0 && cpos<rule->u.coverage.bcnt; i = bskipglyphs(lookup_flags,data,i-1)) {
3238 		    if ( !GlyphNameInClass(data->str[i].sc->name,rule->u.coverage.bcovers[cpos]) )
3239 		break;
3240 		    ++cpos;
3241 		}
3242 		if ( cpos<rule->u.coverage.bcnt )
3243     continue;		/* didn't match */
3244 	    }
3245 	}
3246 /* Handle Match */
3247 	if ( fpst->format==pst_glyphs ) {
3248 	    pt = rule->u.glyph.names;
3249 	    for ( i=pos, cpos=0; i<data->cnt && *pt!='\0'; i = skipglyphs(lookup_flags,data,i+1)) {
3250 		const char *name = data->str[i].sc->name;
3251 		int len = strlen( name );
3252 		if ( strncmp(name,pt,len)!=0 || (pt[len]!='\0' && pt[len]!=' '))
3253 	    break;
3254 		data->str[i].context_pos = cpos++;
3255 		pt += len;
3256 		while ( *pt==' ' ) ++pt;
3257 	    }
3258 	    if ( *pt!='\0' )
3259     continue;		/* didn't match */
3260 	} else if ( fpst->format==pst_class ) {
3261 	    for ( i=pos, cpos=0; i<data->cnt && cpos<rule->u.class.ncnt; i = skipglyphs(lookup_flags,data,i+1)) {
3262 		int class = rule->u.class.nclasses[cpos];
3263 		if ( class!=0 ) {
3264 		    if ( !GlyphNameInClass(data->str[i].sc->name,fpst->nclass[class]) )
3265 	    break;
3266 		} else {
3267 		    int c;
3268 		    /* Ok, to match class 0 we must fail to match all other classes */
3269 		    for ( c=1; c<fpst->nccnt; ++c )
3270 			if ( !GlyphNameInClass(data->str[i].sc->name,fpst->nclass[c]) )
3271 		    break;
3272 		    if ( c!=fpst->nccnt )
3273 	    break;		/* It matched another class => not in class 0 */
3274 		}
3275 		data->str[i].context_pos = cpos++;
3276 	    }
3277 	    if ( cpos<rule->u.class.ncnt )
3278     continue;		/* didn't match */
3279 	} else if ( fpst->format==pst_coverage ) {
3280 	    for ( i=pos, cpos=0; i<data->cnt && cpos<rule->u.coverage.ncnt; i = skipglyphs(lookup_flags,data,i+1)) {
3281 		if ( !GlyphNameInClass(data->str[i].sc->name,rule->u.coverage.ncovers[cpos]) )
3282 	    break;
3283 		data->str[i].context_pos = cpos++;
3284 	    }
3285 	    if ( cpos<rule->u.coverage.ncnt )
3286     continue;		/* didn't match */
3287 	} else
3288 return( 0 );		/* Not ready to deal with reverse chainging */
3289 
3290 	retpos = i;
3291 /* Handle lookahead */
3292 	if ( fpst->type == pst_chainpos || fpst->type == pst_chainsub ) {
3293 	    if ( fpst->format==pst_glyphs ) {
3294 		pt = rule->u.glyph.fore;
3295 		for ( i=retpos; i<data->cnt && *pt!='\0'; i = skipglyphs(lookup_flags,data,i+1)) {
3296 		    const char *name = data->str[i].sc->name;
3297 		    int len = strlen( name );
3298 		    if ( strncmp(name,pt,len)!=0 || (pt[len]!='\0' && pt[len]!=' '))
3299 		break;
3300 		    pt += len;
3301 		    while ( *pt==' ' ) ++pt;
3302 		}
3303 		if ( *pt!='\0' )
3304     continue;		/* didn't match */
3305 	    } else if ( fpst->format==pst_class ) {
3306 		for ( i=retpos, cpos=0; i<data->cnt && cpos<rule->u.class.fcnt; i = skipglyphs(lookup_flags,data,i+1)) {
3307 		    if ( !GlyphNameInClass(data->str[i].sc->name,fpst->fclass[rule->u.class.fclasses[cpos]]) )
3308 		break;
3309 		    cpos++;
3310 		}
3311 		if ( cpos<rule->u.class.fcnt )
3312     continue;		/* didn't match */
3313 	    } else if ( fpst->format==pst_coverage ) {
3314 		for ( i=retpos, cpos=0; i<data->cnt && cpos<rule->u.coverage.fcnt; i = skipglyphs(lookup_flags,data,i+1)) {
3315 		    if ( !GlyphNameInClass(data->str[i].sc->name,rule->u.coverage.fcovers[cpos]) )
3316 		break;
3317 		    cpos++;
3318 		}
3319 		if ( cpos<rule->u.coverage.fcnt )
3320     continue;		/* didn't match */
3321 	    }
3322 	}
3323 	*_rule = rule;
3324 return( retpos );
3325     }
3326 return( 0 );
3327 }
3328 
ApplySingleSubsAtPos(struct lookup_subtable * sub,struct lookup_data * data,int pos)3329 static int ApplySingleSubsAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) {
3330     PST *pst;
3331     SplineChar *sc;
3332 
3333     for ( pst=data->str[pos].sc->possub; pst!=NULL && pst->subtable!=sub; pst=pst->next );
3334     if ( pst==NULL )
3335 return( 0 );
3336 
3337     sc = SFGetChar(data->sf,-1,pst->u.subs.variant);
3338     if ( sc!=NULL ) {
3339 	data->str[pos].sc = sc;
3340 return( pos+1 );
3341     } else if ( strcmp(pst->u.subs.variant,MAC_DELETED_GLYPH_NAME)==0 ) {
3342 	/* Under AAT we delete the glyph. But OpenType doesn't have that concept */
3343 	int i;
3344 	for ( i=pos+1; i<data->cnt; ++i )
3345 	    data->str[pos-1] = data->str[pos];
3346 	--data->cnt;
3347 return( pos );
3348     } else {
3349 return( 0 );
3350     }
3351 }
3352 
ApplyMultSubsAtPos(struct lookup_subtable * sub,struct lookup_data * data,int pos)3353 static int ApplyMultSubsAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) {
3354     PST *pst;
3355     SplineChar *sc;
3356     char *start, *pt;
3357     int mcnt, ch, i;
3358     SplineChar *mults[20];
3359 
3360     for ( pst=data->str[pos].sc->possub; pst!=NULL && pst->subtable!=sub; pst=pst->next );
3361     if ( pst==NULL )
3362 return( 0 );
3363 
3364     mcnt = 0;
3365     for ( start = pst->u.alt.components; *start==' '; ++start);
3366     for ( ; *start; ) {
3367 	for ( pt=start; *pt!='\0' && *pt!=' '; ++pt );
3368 	ch = *pt; *pt = '\0';
3369 	sc = SFGetChar(data->sf,-1,start);
3370 	*pt = ch;
3371 	if ( sc==NULL )
3372 return( 0 );
3373 	if ( mcnt<20 ) mults[mcnt++] = sc;
3374 	while ( *pt==' ' ) ++pt;
3375 	start = pt;
3376     }
3377 
3378     if ( mcnt==0 ) {
3379 	/* Is this legal? that is can we remove a glyph with an empty multiple? */
3380 	for ( i=pos+1; i<data->cnt; ++i )
3381 	    data->str[i-1] = data->str[i];
3382 	--data->cnt;
3383 return( pos );
3384     } else if ( mcnt==1 ) {
3385 	data->str[pos].sc = mults[0];
3386 return( pos+1 );
3387     } else {
3388 	if ( data->cnt+mcnt-1 >= data->max )
3389 	    data->str = realloc(data->str,(data->max+=mcnt) * sizeof( struct opentype_str ));
3390 	for ( i=data->cnt-1; i>pos; --i )
3391 	    data->str[i+mcnt-1] = data->str[i];
3392 	memset(data->str+pos,0,mcnt*sizeof(struct opentype_str));
3393 	for ( i=0; i<mcnt; ++i ) {
3394 	    data->str[pos+i].sc = mults[i];
3395 	    data->str[pos+i].orig_index = data->str[pos].orig_index;
3396 	}
3397 	data->cnt += (mcnt-1);
3398 return( pos+mcnt );
3399     }
3400 }
3401 
ApplyAltSubsAtPos(struct lookup_subtable * sub,struct lookup_data * data,int pos)3402 static int ApplyAltSubsAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) {
3403     PST *pst;
3404     SplineChar *sc;
3405     char *start, *pt, ch;
3406 
3407     for ( pst=data->str[pos].sc->possub; pst!=NULL && pst->subtable!=sub; pst=pst->next );
3408     if ( pst==NULL )
3409 return( 0 );
3410 
3411     for ( start = pst->u.alt.components; *start==' '; ++start);
3412     for ( ; *start; ) {
3413 	for ( pt=start; *pt!='\0' && *pt!=' '; ++pt );
3414 	ch = *pt; *pt = '\0';
3415 	sc = SFGetChar(data->sf,-1,start);
3416 	*pt = ch;
3417 	if ( sc!=NULL ) {
3418 	    data->str[pos].sc = sc;
3419 return( pos+1 );
3420 	}
3421 	while ( *pt==' ' ) ++pt;
3422 	start = pt;
3423     }
3424 return( 0 );
3425 }
3426 
ApplyLigatureSubsAtPos(struct lookup_subtable * sub,struct lookup_data * data,int pos)3427 static int ApplyLigatureSubsAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) {
3428     int i,k, lpos, npos;
3429     int lookup_flags = sub->lookup->lookup_flags;
3430     int match_found = -1, match_len=0;
3431 
3432     if ( data->lig_owner!=sub )
3433 	LigatureSearch(sub,data);
3434     for ( i=0; i<data->lcnt; ++i ) {
3435 	if ( data->ligs[i][1]==data->str[pos].sc ) {
3436 	    lpos = 0;
3437 	    npos = pos+1;
3438 	    for ( k=2; data->ligs[i][k]!=NULL; ++k ) {
3439 		npos = skipglyphs(lookup_flags,data,npos);
3440 		if ( npos>=data->cnt || data->str[npos].sc != data->ligs[i][k] )
3441 	    break;
3442 		++npos;
3443 	    }
3444 	    if ( data->ligs[i][k]==NULL ) {
3445 		if ( match_found==-1 || k>match_len ) {
3446 		    match_found = i;
3447 		    match_len = k;
3448 		}
3449 	    }
3450 	}
3451     }
3452     if ( match_found!=-1 ) {
3453 	/* Matched. Remove the component glyphs, and note which component */
3454 	/*  any intervening marks should be attached to */
3455 	data->str[pos].sc = data->ligs[match_found][0];
3456 	npos = pos+1;
3457 	for ( k=2; data->ligs[match_found][k]!=NULL; ++k ) {
3458 	    lpos = skipglyphs(lookup_flags,data,npos);
3459 	    for ( ; npos<lpos; ++npos )
3460 		data->str[npos].lig_pos = k-2;
3461 	    /* Remove this glyph (copy the final NUL too) */
3462 	    for ( ++lpos; lpos<=data->cnt; ++lpos )
3463 		data->str[lpos-1] = data->str[lpos];
3464 	    --data->cnt;
3465 	}
3466 	/* Any marks after the last component (which should be attached */
3467 	/*  to it) will not have been tagged, so do that now */
3468 	lpos = skipglyphs(lookup_flags,data,npos);
3469 	for ( ; npos<lpos; ++npos )
3470 	    data->str[npos].lig_pos = k-2;
3471 return( pos+1 );
3472     }
3473 
3474 return( 0 );
3475 }
3476 
ApplyContextual(struct lookup_subtable * sub,struct lookup_data * data,int pos)3477 static int ApplyContextual(struct lookup_subtable *sub,struct lookup_data *data,int pos) {
3478     /* On this level there is no difference between GPOS/GSUB contextuals */
3479     /*  If the contextual matches, then we apply the lookups, otherwise we */
3480     /*  don't. Now the lookups will be different, but we don't care here */
3481     struct fpst_rule *rule;
3482     int retpos, i,j;
3483 
3484     retpos = ContextualMatch(sub,data,pos,&rule);
3485     if ( retpos==0 )
3486 return( 0 );
3487     for ( i=0; i<rule->lookup_cnt; ++i ) {
3488 	for ( j=pos; j<data->cnt; ++j ) {
3489 	    if ( data->str[j].context_pos == rule->lookups[i].seq ) {
3490 		ApplyLookupAtPos(0,rule->lookups[i].lookup,data,j);
3491 	break;
3492 	    }
3493 	}
3494     }
3495 return( retpos );
3496 }
3497 
FigureDeviceTable(DeviceTable * dt,int pixelsize)3498 static int FigureDeviceTable(DeviceTable *dt,int pixelsize) {
3499 
3500     if ( dt==NULL || dt->corrections==NULL || pixelsize<dt->first_pixel_size ||
3501 	    pixelsize>dt->last_pixel_size )
3502 return( 0 );
3503 
3504 return( dt->corrections[pixelsize - dt->last_pixel_size] );
3505 }
3506 
ApplySinglePosAtPos(struct lookup_subtable * sub,struct lookup_data * data,int pos)3507 static int ApplySinglePosAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) {
3508     PST *pst;
3509 
3510     for ( pst=data->str[pos].sc->possub; pst!=NULL && pst->subtable!=sub; pst=pst->next );
3511     if ( pst==NULL )
3512 return( 0 );
3513 
3514     data->str[pos].vr.xoff += rint( pst->u.pos.xoff * data->scale );
3515     data->str[pos].vr.yoff += rint( pst->u.pos.yoff * data->scale );
3516     data->str[pos].vr.h_adv_off += rint( pst->u.pos.h_adv_off * data->scale );
3517     data->str[pos].vr.v_adv_off += rint( pst->u.pos.v_adv_off * data->scale );
3518     if ( pst->u.pos.adjust!=NULL ) {
3519 	data->str[pos].vr.xoff += FigureDeviceTable(&pst->u.pos.adjust->xadjust,data->pixelsize);
3520 	data->str[pos].vr.yoff += FigureDeviceTable(&pst->u.pos.adjust->yadjust,data->pixelsize);
3521 	data->str[pos].vr.h_adv_off += FigureDeviceTable(&pst->u.pos.adjust->xadv,data->pixelsize);
3522 	data->str[pos].vr.v_adv_off += FigureDeviceTable(&pst->u.pos.adjust->yadv,data->pixelsize);
3523     }
3524 return( pos+1 );
3525 }
3526 
ApplyPairPosAtPos(struct lookup_subtable * sub,struct lookup_data * data,int pos,int allow_class0)3527 static int ApplyPairPosAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos,int allow_class0) {
3528     PST *pst;
3529     int npos, isv, within, f, l, kcspecd;
3530     KernPair *kp;
3531 
3532     npos = skipglyphs(sub->lookup->lookup_flags,data,pos+1);
3533     if ( npos>=data->cnt )
3534 return( 0 );
3535     if ( sub->kc!=NULL ) {
3536 	kcspecd = sub->kc->firsts[0] != NULL;
3537 	f = KCFindName(data->str[pos].sc->name ,sub->kc->firsts ,sub->kc->first_cnt ,allow_class0);
3538 	l = KCFindName(data->str[npos].sc->name,sub->kc->seconds,sub->kc->second_cnt,allow_class0);
3539 	if ( f==-1 || l==-1 || ( !kcspecd && f==0 && l==0 ) )
3540 return( 0 );
3541 	data->str[pos].kc_index = within = f*sub->kc->second_cnt+l;
3542 	data->str[pos].kc = sub->kc;
3543 	data->str[pos].prev_kc0 = ( !kcspecd && f==0 );
3544 	data->str[pos].next_kc0 = ( l==0 );
3545 	if ( sub->vertical_kerning ) {
3546 	    data->str[pos].vr.v_adv_off += rint( sub->kc->offsets[within] * data->scale );
3547 	    data->str[pos].vr.v_adv_off += FigureDeviceTable(&sub->kc->adjusts[within],data->pixelsize);
3548 	} else if ( sub->lookup->lookup_flags & pst_r2l ) {
3549 	    data->str[npos].vr.h_adv_off += rint( sub->kc->offsets[within] * data->scale );
3550 	    data->str[npos].vr.h_adv_off += FigureDeviceTable(&sub->kc->adjusts[within],data->pixelsize);
3551 	} else {
3552 	    data->str[pos].vr.h_adv_off += rint( sub->kc->offsets[within] * data->scale );
3553 	    data->str[pos].vr.h_adv_off += FigureDeviceTable(&sub->kc->adjusts[within],data->pixelsize);
3554 	}
3555 	/* Return 1 if the result we have got comes from a combination with an {Everything Else} class */
3556 	/* This is acceptable, but we should continue looking for a better match */
3557 return( pos+1 );
3558     } else {
3559 	for ( pst=data->str[pos].sc->possub; pst!=NULL; pst=pst->next ) {
3560 	    if ( pst->subtable==sub && strcmp(pst->u.pair.paired,data->str[npos].sc->name)==0 ) {
3561 		data->str[pos].vr.xoff += rint( pst->u.pair.vr[0].xoff * data->scale);
3562 		data->str[pos].vr.yoff += rint( pst->u.pair.vr[0].yoff * data->scale);
3563 		data->str[pos].vr.h_adv_off += rint( pst->u.pair.vr[0].h_adv_off * data->scale);
3564 		data->str[pos].vr.v_adv_off += rint( pst->u.pair.vr[0].v_adv_off * data->scale);
3565 		data->str[npos].vr.xoff += rint( pst->u.pair.vr[1].xoff * data->scale);
3566 		data->str[npos].vr.yoff += rint( pst->u.pair.vr[1].yoff * data->scale);
3567 		data->str[npos].vr.h_adv_off += rint( pst->u.pair.vr[1].h_adv_off * data->scale);
3568 		data->str[npos].vr.v_adv_off += rint( pst->u.pair.vr[1].v_adv_off * data->scale);
3569 		/* I got bored. I should do all of them */
3570 		if ( pst->u.pair.vr[0].adjust!=NULL ) {
3571 		    data->str[pos].vr.h_adv_off += FigureDeviceTable(&pst->u.pair.vr[0].adjust->xadv,data->pixelsize);
3572 		}
3573 return( pos+1 );	/* We do NOT want to return npos+1 */
3574 	    }
3575 	}
3576 	for ( isv = 0; isv<2; ++isv ) {
3577 	    for ( kp = isv ? data->str[pos].sc->vkerns : data->str[pos].sc->kerns; kp!=NULL; kp=kp->next ) {
3578 		if ( kp->subtable == sub && kp->sc == data->str[npos].sc ) {
3579 		    data->str[pos].kp = kp;
3580 		    if ( isv ) {
3581 			data->str[pos].vr.v_adv_off += rint( kp->off * data->scale);
3582 			data->str[pos].vr.v_adv_off += FigureDeviceTable(kp->adjust,data->pixelsize);
3583 		    } else if ( sub->lookup->lookup_flags & pst_r2l ) {
3584 			data->str[npos].vr.h_adv_off += rint( kp->off * data->scale);
3585 			data->str[npos].vr.h_adv_off += FigureDeviceTable(kp->adjust,data->pixelsize);
3586 		    } else {
3587 			data->str[pos].vr.h_adv_off += rint( kp->off * data->scale);
3588 			data->str[pos].vr.h_adv_off += FigureDeviceTable(kp->adjust,data->pixelsize);
3589 		    }
3590 return( pos+1 );
3591 		}
3592 	    }
3593 	}
3594     }
3595 
3596 return( 0 );
3597 }
3598 
ApplyAnchorPosAtPos(struct lookup_subtable * sub,struct lookup_data * data,int pos)3599 static int ApplyAnchorPosAtPos(struct lookup_subtable *sub,struct lookup_data *data,int pos) {
3600     AnchorPoint *ap1, *ap2;
3601     int bpos;
3602 
3603     /* Anchors do not position the base glyph, but the mark (or second glyph */
3604     /*  of a cursive attachment). This means we don't apply the attachment when*/
3605     /*  we meet the first glyph, but wait until we meet the second, and then */
3606     /*  walk backwards */
3607     /* The backwards walk is different depending on the lookup type (I think) */
3608     /*  mark to base and mark to ligature lookups will skip all marks even if */
3609     /*  lookup flags don't specify that */
3610     /* mark to mark, and cursive attachment only skip what the lookup flags */
3611     /*  tell them to skip. */
3612     for ( ap2=data->str[pos].sc->anchor; ap2!=NULL ; ap2=ap2->next ) {
3613 	if ( ap2->anchor->subtable==sub && (ap2->type == at_mark || ap2->type == at_centry))
3614     break;
3615     }
3616     if ( ap2==NULL ) {
3617 	/* This subtable is not used by this glyph ... at least this glyph is */
3618 	/*  neither a mark nor an entry point for this subtable */
3619 return( 0 );
3620     }
3621 
3622     /* There's only going to be one mark anchor on a glyph in a given subtable*/
3623     /* And cursive attachments only allow one anchor class per subtable */
3624     /* in either case we have already found the only attachment site possible */
3625     /*  in the current glyph */
3626 
3627     if ( sub->lookup->lookup_type == gpos_mark2base ||
3628 	    sub->lookup->lookup_type == gpos_mark2ligature )
3629 	bpos = bskipmarkglyphs(sub->lookup->lookup_flags,data,pos-1);
3630     else
3631 	bpos = bskipglyphs(sub->lookup->lookup_flags,data,pos-1);
3632     if ( bpos==-1 )
3633 return( 0 );		/* No match */
3634 
3635     if ( sub->lookup->lookup_type == gpos_cursive ) {
3636 	for ( ap1=data->str[bpos].sc->anchor; ap1!=NULL ; ap1=ap1->next ) {
3637 	    if ( ap1->anchor==ap2->anchor && ap1->type==at_cexit )
3638 	break;
3639 	}
3640     } else if ( sub->lookup->lookup_type == gpos_mark2ligature ) {
3641 	for ( ap1=data->str[bpos].sc->anchor; ap1!=NULL ; ap1=ap1->next ) {
3642 	    if ( ap1->anchor==ap2->anchor && ap1->type==at_baselig &&
3643 		    ap1->lig_index == data->str[pos].lig_pos )
3644 	break;
3645 	}
3646     } else {
3647 	for ( ap1=data->str[bpos].sc->anchor; ap1!=NULL ; ap1=ap1->next ) {
3648 	    if ( ap1->anchor==ap2->anchor &&
3649 		    (ap1->type==at_basechar || ap1->type==at_basemark) )
3650 	break;
3651 	}
3652     }
3653     if ( ap1==NULL )
3654 return( 0 );		/* No match */
3655 
3656 /* This probably doesn't work for vertical text */
3657     data->str[pos].vr.yoff = data->str[bpos].vr.yoff +
3658 	    rint((ap1->me.y - ap2->me.y) * data->scale);
3659     data->str[pos].vr.yoff += FigureDeviceTable(&ap1->yadjust,data->pixelsize)-
3660 		FigureDeviceTable(&ap2->yadjust,data->pixelsize);
3661     if ( sub->lookup->lookup_flags&pst_r2l ) {
3662 	data->str[pos].vr.xoff = data->str[bpos].vr.xoff +
3663 		rint( -(ap1->me.x - ap2->me.x)*data->scale );
3664 	data->str[pos].vr.xoff -= FigureDeviceTable(&ap1->xadjust,data->pixelsize)-
3665 		    FigureDeviceTable(&ap2->xadjust,data->pixelsize);
3666     } else {
3667 	data->str[pos].vr.xoff = data->str[bpos].vr.xoff +
3668 		rint( (ap1->me.x - ap2->me.x - data->str[bpos].sc->width)*data->scale -
3669 		data->str[bpos].vr.h_adv_off);
3670 	data->str[pos].vr.xoff += FigureDeviceTable(&ap1->xadjust,data->pixelsize)-
3671 		    FigureDeviceTable(&ap2->xadjust,data->pixelsize);
3672     }
3673 
3674 return( pos+1 );
3675 }
3676 
ConditionalTagOk(uint32 tag,OTLookup * otl,struct lookup_data * data,int pos)3677 static int ConditionalTagOk(uint32 tag, OTLookup *otl,struct lookup_data *data,int pos) {
3678     int npos, bpos;
3679     uint32 script;
3680     int before_in_script, after_in_script;
3681 
3682     if ( tag==CHR('i','n','i','t') || tag==CHR('i','s','o','l') ||
3683 	    tag==CHR('f','i','n','a') || tag==CHR('m','e','d','i') ) {
3684 	npos = skipglyphs(otl->lookup_flags,data,pos+1);
3685 	bpos = bskipglyphs(otl->lookup_flags,data,pos-1);
3686 	script = SCScriptFromUnicode(data->str[pos].sc);
3687 	before_in_script = (bpos>=0 && SCScriptFromUnicode(data->str[bpos].sc)==script);
3688 	after_in_script = (npos<data->cnt && SCScriptFromUnicode(data->str[npos].sc)==script);
3689 	if ( tag==CHR('i','n','i','t') )
3690 return( !before_in_script && after_in_script );
3691 	else if ( tag==CHR('i','s','o','l') )
3692 return( !before_in_script && !after_in_script );
3693 	else if ( tag==CHR('f','i','n','a') )
3694 return( before_in_script && !after_in_script );
3695 	else
3696 return( before_in_script && after_in_script );
3697     }
3698 
3699 return( true );
3700 }
3701 
ApplyLookupAtPos(uint32 tag,OTLookup * otl,struct lookup_data * data,int pos)3702 static int ApplyLookupAtPos(uint32 tag, OTLookup *otl,struct lookup_data *data,int pos) {
3703     struct lookup_subtable *sub;
3704     int newpos;
3705     /* Need two passes for pair kerning lookups. At the second pass we accept */
3706     /* also combinations with the {Everything Else} class */
3707     int i, pcnt = otl->lookup_type==gpos_pair ? 2 : 1;
3708 
3709     /* Some tags imply a conditional check. Do that now */
3710     if ( !ConditionalTagOk(tag,otl,data,pos))
3711 return( 0 );
3712 
3713     for ( i=0; i<pcnt; i++ ) {
3714 	for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
3715 	    switch ( otl->lookup_type ) {
3716 	      case gsub_single:
3717 		newpos = ApplySingleSubsAtPos(sub,data,pos);
3718 	      break;
3719 	      case gsub_multiple:
3720 		newpos = ApplyMultSubsAtPos(sub,data,pos);
3721 	      break;
3722 	      case gsub_alternate:
3723 		newpos = ApplyAltSubsAtPos(sub,data,pos);
3724 	      break;
3725 	      case gsub_ligature:
3726 		newpos = ApplyLigatureSubsAtPos(sub,data,pos);
3727 	      break;
3728 	      case gsub_context:
3729 		newpos = ApplyContextual(sub,data,pos);
3730 	      break;
3731 	      case gsub_contextchain:
3732 		newpos = ApplyContextual(sub,data,pos);
3733 	      break;
3734 	      case gsub_reversecchain:
3735 		newpos = ApplySingleSubsAtPos(sub,data,pos);
3736 	      break;
3737 
3738 	      case gpos_single:
3739 		newpos = ApplySinglePosAtPos(sub,data,pos);
3740 	      break;
3741 	      case gpos_pair:
3742 		newpos = ApplyPairPosAtPos(sub,data,pos,i>0);
3743 	      break;
3744 	      case gpos_cursive:
3745 		newpos = ApplyAnchorPosAtPos(sub,data,pos);
3746 	      break;
3747 	      case gpos_mark2base:
3748 		newpos = ApplyAnchorPosAtPos(sub,data,pos);
3749 	      break;
3750 	      case gpos_mark2ligature:
3751 		newpos = ApplyAnchorPosAtPos(sub,data,pos);
3752 	      break;
3753 	      case gpos_mark2mark:
3754 		newpos = ApplyAnchorPosAtPos(sub,data,pos);
3755 	      break;
3756 	      case gpos_context:
3757 		newpos = ApplyContextual(sub,data,pos);
3758 	      break;
3759 	      case gpos_contextchain:
3760 		newpos = ApplyContextual(sub,data,pos);
3761 	      break;
3762 	      default:
3763 		/* apple state machines */
3764 		newpos = 0;
3765 	      break;
3766 	    }
3767 	    /* if a subtable worked, we don't try to apply the next one */
3768 	    if ( newpos!=0 )
3769     return( newpos );
3770 	}
3771     }
3772 return( 0 );
3773 }
3774 
ApplyLookup(uint32 tag,OTLookup * otl,struct lookup_data * data)3775 static void ApplyLookup(uint32 tag, OTLookup *otl,struct lookup_data *data) {
3776     int pos, npos;
3777     int lt = otl->lookup_type;
3778 
3779     if ( lt == morx_indic || lt == morx_context || lt == morx_insert ||
3780 	    lt == kern_statemachine )
3781 	ApplyAppleStateMachine(otl,data);
3782     else {
3783 	/* OpenType */
3784 	for ( pos = 0; pos<data->cnt; ) {
3785 	    npos = ApplyLookupAtPos(tag,otl,data,pos);
3786 	    if ( npos<=pos)		/* !!!!! */
3787 		npos = pos+1;
3788 	    pos = npos;
3789 	}
3790     }
3791 }
3792 
FSLLMatches(FeatureScriptLangList * fl,uint32 * flist,uint32 script,uint32 lang)3793 static uint32 FSLLMatches(FeatureScriptLangList *fl,uint32 *flist,uint32 script,uint32 lang) {
3794     int i,l;
3795     struct scriptlanglist *sl;
3796 
3797     if ( flist==NULL )
3798 return( 0 );
3799 
3800     while ( fl!=NULL ) {
3801 	for ( i=0; flist[i]!=0; ++i ) {
3802 	    if ( fl->featuretag==flist[i] )
3803 	break;
3804 	}
3805 	if ( flist[i]!=0 ) {
3806 	    for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) {
3807 		if ( sl->script == script ) {
3808 		    if ( fl->ismac )	/* Language irrelevant on macs (scripts too, but we pretend they matter) */
3809 return( fl->featuretag );
3810 		    for ( l=0; l<sl->lang_cnt; ++l )
3811 			if ( (l<MAX_LANG && sl->langs[l]==lang) ||
3812 				(l>=MAX_LANG && sl->morelangs[l-MAX_LANG]==lang))
3813 return( fl->featuretag );
3814 		}
3815 	    }
3816 	}
3817 	fl = fl->next;
3818     }
3819 return( 0 );
3820 }
3821 
3822 /* This routine takes a string of glyphs and applies the opentype transformations */
3823 /*  indicated by the features (and script and language) we are passed, it returns */
3824 /*  a transformed string with substitutions applied and containing positioning */
3825 /*  info */
ApplyTickedFeatures(SplineFont * sf,uint32 * flist,uint32 script,uint32 lang,int pixelsize,SplineChar ** glyphs)3826 struct opentype_str *ApplyTickedFeatures(SplineFont *sf,uint32 *flist, uint32 script, uint32 lang,
3827 	int pixelsize, SplineChar **glyphs) {
3828     int isgpos, cnt;
3829     OTLookup *otl;
3830     struct lookup_data data;
3831     uint32 *langs, templang;
3832     int i;
3833 
3834     memset(&data,0,sizeof(data));
3835     for ( cnt=0; glyphs[cnt]!=NULL; ++cnt );
3836     data.str = calloc(cnt+1,sizeof(struct opentype_str));
3837     data.cnt = data.max = cnt;
3838     for ( cnt=0; glyphs[cnt]!=NULL; ++cnt ) {
3839 	data.str[cnt].sc = glyphs[cnt];
3840 	data.str[cnt].orig_index = cnt;
3841 	data.str[cnt].lig_pos = data.str[cnt].context_pos = -1;
3842     }
3843     if ( sf->cidmaster!=NULL ) sf=sf->cidmaster;
3844     data.sf = sf;
3845     data.pixelsize = pixelsize;
3846     data.scale = pixelsize/(double) (sf->ascent+sf->descent);
3847 
3848     /* Indic glyph reordering???? */
3849     for ( isgpos=0; isgpos<2; ++isgpos ) {
3850 	/* Check that this table has an entry for this language */
3851 	/*  if it doesn't use the default language */
3852 	/* GPOS/GSUB may have different language sets, so we must be prepared */
3853 	templang = lang;
3854 	langs = SFLangsInScript(sf,isgpos,script);
3855 	for ( i=0; langs[i]!=0 && langs[i]!=lang; ++i );
3856 	if ( langs[i]==0 )
3857 	    templang = DEFAULT_LANG;
3858 	free(langs);
3859 
3860 	for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL ; otl = otl->next ) {
3861 	    uint32 tag;
3862 	    if ( (tag=FSLLMatches(otl->features,flist,script,templang))!=0 )
3863 		ApplyLookup(tag,otl,&data);
3864 	}
3865     }
3866     LigatureFree(&data);
3867     free(data.ligs);
3868 
3869     data.str = realloc(data.str,(data.cnt+1)*sizeof(struct opentype_str));
3870     memset(&data.str[data.cnt],0,sizeof(struct opentype_str));
3871 return( data.str );
3872 }
3873 
doreplace(char ** haystack,char * start,const char * rpl,int slen)3874 static void doreplace(char **haystack,char *start,const char *rpl,int slen) {
3875     int rlen;
3876     char *pt = start+slen;
3877 
3878     rlen = strlen(rpl);
3879     if ( slen>=rlen ) {
3880 	memcpy(start,rpl,rlen);
3881 	if ( slen>rlen ) {
3882 	    int diff = slen-rlen;
3883 	    for ( ; *pt ; ++pt )
3884 		pt[-diff] = *pt;
3885 	    pt[-diff] = '\0';
3886 	}
3887     } else {
3888 	char *base = *haystack;
3889 	char *new = malloc(pt-base+strlen(pt)+rlen-slen+1);
3890 	memcpy(new,base,start-base);
3891 	memcpy(new+(start-base),rpl,rlen);
3892 	strcpy(new+(start-base)+rlen,pt);
3893 	free( base );
3894 	*haystack = new;
3895     }
3896 }
3897 
rplstr(char ** haystack,const char * search,const char * rpl,int multipleoccurances)3898 static int rplstr(char **haystack,const char *search, const char *rpl,int multipleoccurances) {
3899     char *start, *pt, *base = *haystack;
3900     int ch, match, slen = strlen(search);
3901     int any = 0;
3902 
3903     if ( base==NULL )
3904 return( false );
3905 
3906     for ( pt=base ; ; ) {
3907 	while ( *pt==' ' ) ++pt;
3908 	if ( *pt=='\0' )
3909 return( any );
3910 	start=pt;
3911 	while ( *pt!=' ' && *pt!='\0' ) ++pt;
3912 	if ( pt-start!=slen )
3913 	    match = -1;
3914 	else {
3915 	    ch = *pt; *pt='\0';
3916 	    match = strcmp(start,search);
3917 	    *pt = ch;
3918 	}
3919 	if ( match==0 ) {
3920 	    doreplace(haystack,start,rpl,slen);
3921 	    if ( !multipleoccurances )
3922 return( true );
3923 	    any = true;
3924 	    if ( base!=*haystack ) {
3925 		pt = *haystack + (start-base)+strlen(rpl);
3926 		base = *haystack;
3927 	    } else
3928 		pt = start+strlen(rpl);
3929 	}
3930     }
3931 }
3932 
rplglyphname(char ** haystack,const char * search,const char * rpl)3933 static int rplglyphname(char **haystack,const char *search, const char *rpl) {
3934     /* If we change "f" to "uni0066" then we should also change "f.sc" to */
3935     /*  "uni0066.sc" and "f_f_l" to "uni0066_uni0066_l" */
3936     char *start, *pt, *base = *haystack;
3937     int ch, match;
3938     size_t slen = strlen(search);
3939     int any = 0;
3940 
3941     if ( slen>=strlen( base ))
3942 return( false );
3943 
3944     for ( pt=base ; ; ) {
3945 	while ( *pt=='_' ) ++pt;
3946 	if ( *pt=='\0' || *pt=='.' )
3947 return( any );
3948 	start=pt;
3949 	while ( *pt!='_' && *pt!='\0' && *pt!='.' ) ++pt;
3950 	if ( *pt=='\0' && start==base )	/* Don't change any unsegmented names */
3951 return( false );			/* In particular don't rename ourselves*/
3952 	if ( pt-start!=(ptrdiff_t)slen )
3953 	    match = -1;
3954 	else {
3955 	    ch = *pt; *pt='\0';
3956 	    match = strcmp(start,search);
3957 	    *pt = ch;
3958 	}
3959 	if ( match==0 ) {
3960 	    doreplace(haystack,start,rpl,slen);
3961 	    any = true;
3962 	    if ( base!=*haystack ) {
3963 		pt = *haystack + (start-base) + strlen(rpl);
3964 		base = *haystack;
3965 	    } else
3966 		pt = start+strlen(rpl);
3967 	}
3968     }
3969 }
3970 
glyphnameIsComponent(const char * haystack,const char * search)3971 static int glyphnameIsComponent(const char *haystack, const char *search) {
3972     /* Check for a glyph name in ligature names and dotted names */
3973     const char *start, *pt;
3974     size_t slen = strlen(search);
3975 
3976     if ( slen>=strlen( haystack ))
3977 return( false );
3978 
3979     for ( pt=haystack ; ; ) {
3980 	while ( *pt=='_' ) ++pt;
3981 	if ( *pt=='\0' || *pt=='.' )
3982 return( false );
3983 	start=pt;
3984 	while ( *pt!='_' && *pt!='\0' && *pt!='.' ) ++pt;
3985 	if ( *pt=='\0' && start==haystack )/* Don't change any unsegmented names */
3986 return( false );			/* In particular don't rename ourselves*/
3987 	if ( pt-start==(ptrdiff_t)slen && strncmp(start,search,slen)==0 )
3988 return( true );
3989     }
3990 }
3991 
gvfixup(struct glyphvariants * gv,const char * old,const char * new)3992 static int gvfixup(struct glyphvariants *gv,const char *old, const char *new) {
3993     int i;
3994     int ret=0;
3995 
3996     if ( gv==NULL )
3997 return( false );
3998     ret = rplstr(&gv->variants,old,new,false);
3999     for ( i=0; i<gv->part_cnt; ++i ) {
4000 	if ( strcmp(gv->parts[i].component,old)==0 ) {
4001 	    free( gv->parts[i].component);
4002 	    gv->parts[i].component = copy(new);
4003 	    ret = true;
4004 	}
4005     }
4006 return( ret );
4007 }
4008 
SFGlyphRenameFixup(SplineFont * sf,const char * old,const char * new,int rename_related_glyphs)4009 void SFGlyphRenameFixup(SplineFont *sf, const char *old, const char *new, int rename_related_glyphs) {
4010 /* NOTE: Existing GUI behaviour renames glyphs, rename_related_glyphs turns */
4011 /* off this behaviour for scripting - see github issue #523 */
4012     int k, gid, isv;
4013     int i,r;
4014     SplineFont *master = sf;
4015     SplineChar *sc;
4016     PST *pst;
4017     FPST *fpst;
4018     KernClass *kc;
4019     ASM *sm;
4020 
4021     CVGlyphRenameFixup(sf,old,new);
4022     if ( sf->cidmaster!=NULL )
4023 	master = sf->cidmaster;
4024 
4025     /* Look through all substitutions (and pairwise psts) stored on the glyphs*/
4026     /*  and change any occurances of the name */
4027     /* (KernPairs have a reference to the SC rather than the name, and need no fixup) */
4028     /* Also if the name is "f" then look for glyph names like "f.sc" or "f_f_l"*/
4029     /*  and be ready to change them too */
4030     k = 0;
4031     do {
4032 	sf = k<master->subfontcnt ? master->subfonts[k] : master;
4033 	for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
4034 	    if ( rename_related_glyphs && glyphnameIsComponent(sc->name,old) ) {
4035 		char *newer = copy(sc->name);
4036 		rplglyphname(&newer,old,new);
4037 		SFGlyphRenameFixup(master,sc->name,newer,true);
4038 		free(sc->name);
4039 		sc->name = newer;
4040 		sc->namechanged = sc->changed = true;
4041 	    }
4042 	    for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
4043 		if ( pst->type==pst_substitution || pst->type==pst_alternate ||
4044 			pst->type==pst_multiple || pst->type==pst_pair ||
4045 			pst->type==pst_ligature ) {
4046 		    if ( rplstr(&pst->u.mult.components,old,new,pst->type==pst_ligature))
4047 			sc->changed = true;
4048 		}
4049 	    }
4050 	    /* For once I don't want a short circuit eval of "or", so I use */
4051 	    /*  bitwise rather than boolean intentionally */
4052 	    if ( gvfixup(sc->vert_variants,old,new) |
4053 		    gvfixup(sc->horiz_variants,old,new))
4054 		sc->changed = true;
4055 	}
4056 	++k;
4057     } while ( k<master->subfontcnt );
4058 
4059     /* Now look for contextual fpsts which might use the name */
4060     for ( fpst=master->possub; fpst!=NULL; fpst=fpst->next ) {
4061 	if ( fpst->format==pst_class ) {
4062 	    for ( i=0; i<fpst->nccnt; ++i ) if ( fpst->nclass[i]!=NULL ) {
4063 		if ( rplstr(&fpst->nclass[i],old,new,false))
4064 	    break;
4065 	    }
4066 	    for ( i=0; i<fpst->bccnt; ++i ) if ( fpst->bclass[i]!=NULL ) {
4067 		if ( rplstr(&fpst->bclass[i],old,new,false))
4068 	    break;
4069 	    }
4070 	    for ( i=0; i<fpst->fccnt; ++i ) if ( fpst->fclass[i]!=NULL ) {
4071 		if ( rplstr(&fpst->fclass[i],old,new,false))
4072 	    break;
4073 	    }
4074 	}
4075 	for ( r=0; r<fpst->rule_cnt; ++r ) {
4076 	    struct fpst_rule *rule = &fpst->rules[r];
4077 	    if ( fpst->format==pst_glyphs ) {
4078 		rplstr(&rule->u.glyph.names,old,new,true);
4079 		rplstr(&rule->u.glyph.back,old,new,true);
4080 		rplstr(&rule->u.glyph.fore,old,new,true);
4081 	    } else if ( fpst->format==pst_coverage ||
4082 		    fpst->format==pst_reversecoverage ) {
4083 		for ( i=0; i<rule->u.coverage.ncnt ; ++i )
4084 		    rplstr(&rule->u.coverage.ncovers[i],old,new,false);
4085 		for ( i=0; i<rule->u.coverage.bcnt ; ++i )
4086 		    rplstr(&rule->u.coverage.bcovers[i],old,new,false);
4087 		for ( i=0; i<rule->u.coverage.fcnt ; ++i )
4088 		    rplstr(&rule->u.coverage.fcovers[i],old,new,false);
4089 		if ( fpst->format==pst_reversecoverage )
4090 		    rplstr(&rule->u.rcoverage.replacements,old,new,true);
4091 	    }
4092 	}
4093     }
4094 
4095     /* Now look for contextual apple state machines which might use the name */
4096     for ( sm = master->sm; sm!=NULL; sm=sm->next ) {
4097 	for ( i=0; i<sm->class_cnt; ++i ) if ( sm->classes[i]!=NULL ) {
4098 	    if ( rplstr(&sm->classes[i],old,new,false))
4099 	break;
4100 	}
4101     }
4102 
4103     /* Now look for contextual kerning classes which might use the name */
4104     for ( isv=0; isv<2; ++isv ) {
4105 	for ( kc=isv ? master->vkerns : master->kerns; kc!=NULL; kc=kc->next ) {
4106 	    for ( i=0; i<kc->first_cnt; ++i ) if ( kc->firsts[i]!=NULL ) {
4107 		if ( rplstr(&kc->firsts[i],old,new,false))
4108 	    break;
4109 	    }
4110 	    for ( i=0; i<kc->second_cnt; ++i ) if ( kc->seconds[i]!=NULL ) {
4111 		if ( rplstr(&kc->seconds[i],old,new,false))
4112 	    break;
4113 	    }
4114 	}
4115     }
4116 }
4117 
SFSubTableFindOrMake(SplineFont * sf,uint32 tag,uint32 script,int lookup_type)4118 struct lookup_subtable *SFSubTableFindOrMake(SplineFont *sf,uint32 tag,uint32 script,
4119 	int lookup_type ) {
4120     OTLookup **base;
4121     OTLookup *otl, *found=NULL;
4122     int isgpos = lookup_type>=gpos_start;
4123     struct lookup_subtable *sub;
4124 
4125     if ( sf->cidmaster ) sf = sf->cidmaster;
4126     base = isgpos ? &sf->gpos_lookups : &sf->gsub_lookups;
4127     for ( otl= *base; otl!=NULL; otl=otl->next ) {
4128 	if ( otl->lookup_type==lookup_type &&
4129 		FeatureScriptTagInFeatureScriptList(tag,script,otl->features) ) {
4130 	    for ( sub = otl->subtables; sub!=NULL; sub=sub->next )
4131 		if ( sub->kc==NULL )
4132 return( sub );
4133 	    found = otl;
4134 	}
4135     }
4136 
4137     if ( found==NULL ) {
4138 	found = chunkalloc(sizeof(OTLookup));
4139 	found->lookup_type = lookup_type;
4140 	found->features = chunkalloc(sizeof(FeatureScriptLangList));
4141 	found->features->featuretag = tag;
4142 	found->features->scripts = chunkalloc(sizeof(struct scriptlanglist));
4143 	found->features->scripts->script = script;
4144 	found->features->scripts->langs[0] = DEFAULT_LANG;
4145 	found->features->scripts->lang_cnt = 1;
4146 
4147 	SortInsertLookup(sf, found);
4148     }
4149 
4150     sub = chunkalloc(sizeof(struct lookup_subtable));
4151     sub->next = found->subtables;
4152     found->subtables = sub;
4153     sub->lookup = found;
4154     sub->per_glyph_pst_or_kern = true;
4155 
4156     NameOTLookup(found,sf);
4157 return( sub );
4158 }
4159 
SFSubTableMake(SplineFont * sf,uint32 tag,uint32 script,int lookup_type)4160 struct lookup_subtable *SFSubTableMake(SplineFont *sf,uint32 tag,uint32 script,
4161 	int lookup_type ) {
4162     OTLookup **base;
4163     OTLookup *otl, *found=NULL;
4164     int isgpos = lookup_type>=gpos_start;
4165     struct lookup_subtable *sub;
4166     int isnew = false;
4167 
4168     if ( sf->cidmaster ) sf = sf->cidmaster;
4169     base = isgpos ? &sf->gpos_lookups : &sf->gsub_lookups;
4170     for ( otl= *base; otl!=NULL; otl=otl->next ) {
4171 	if ( otl->lookup_type==lookup_type &&
4172 		FeatureScriptTagInFeatureScriptList(tag,script,otl->features) ) {
4173 	    found = otl;
4174 	}
4175     }
4176 
4177     if ( found==NULL ) {
4178 	found = chunkalloc(sizeof(OTLookup));
4179 	found->lookup_type = lookup_type;
4180 	found->features = chunkalloc(sizeof(FeatureScriptLangList));
4181 	found->features->featuretag = tag;
4182 	found->features->scripts = chunkalloc(sizeof(struct scriptlanglist));
4183 	found->features->scripts->script = script;
4184 	found->features->scripts->langs[0] = DEFAULT_LANG;
4185 	found->features->scripts->lang_cnt = 1;
4186 
4187 	SortInsertLookup(sf, found);
4188 	isnew = true;
4189     }
4190 
4191     sub = chunkalloc(sizeof(struct lookup_subtable));
4192     sub->next = found->subtables;
4193     found->subtables = sub;
4194     sub->lookup = found;
4195 
4196     if ( isnew )
4197 	NameOTLookup(found,sf);
4198 return( sub );
4199 }
4200 
LookupUsedNested(SplineFont * sf,OTLookup * checkme)4201 int LookupUsedNested(SplineFont *sf,OTLookup *checkme) {
4202     OTLookup *otl;
4203     struct lookup_subtable *sub;
4204     int r,c;
4205 
4206     if ( checkme->lookup_type>=gpos_start )
4207 	otl = sf->gpos_lookups;
4208     else
4209 	otl = sf->gsub_lookups;
4210     while ( otl!=NULL ) {
4211 	for ( sub = otl->subtables; sub!=NULL; sub=sub->next ) {
4212 	    if ( sub->fpst!=NULL ) {
4213 		for ( r=0; r<sub->fpst->rule_cnt; ++r ) {
4214 		    struct fpst_rule *rule = &sub->fpst->rules[r];
4215 		    for ( c=0; c<rule->lookup_cnt; ++c ) {
4216 			if ( rule->lookups[c].lookup == checkme )
4217 return( true );
4218 		    }
4219 		}
4220 	    } else if ( otl->lookup_type==morx_context ) {
4221 		for ( c = 0; c<sub->sm->class_cnt*sub->sm->state_cnt; ++c ) {
4222 		    struct asm_state *state = &sub->sm->state[c];
4223 		    if ( state->u.context.mark_lookup==checkme )
4224 return( true );
4225 		    if ( state->u.context.cur_lookup==checkme )
4226 return( true );
4227 		}
4228 	    }
4229 	}
4230 	otl = otl->next;
4231     }
4232 return( false );
4233 }
4234 
AALTRemoveOld(SplineFont * sf)4235 static void AALTRemoveOld(SplineFont *sf) {
4236     FeatureScriptLangList *fl, *prev;
4237     OTLookup *otl, *otlnext;
4238 
4239     for ( otl=sf->gsub_lookups; otl!=NULL; otl=otlnext ) {
4240 	otlnext = otl->next;
4241 	prev = NULL;
4242 	for ( fl = otl->features; fl!=NULL; prev=fl, fl=fl->next ) {
4243 	    if ( fl->featuretag==CHR('a','a','l','t') ) {
4244 		if ( fl==otl->features && fl->next==NULL && !LookupUsedNested(sf,otl))
4245 		    SFRemoveLookup(sf,otl,0);
4246 		else {
4247 		    if ( prev==NULL )
4248 			otl->features = fl->next;
4249 		    else
4250 			prev->next = fl->next;
4251 		    fl->next = NULL;
4252 		    FeatureScriptLangListFree(fl);
4253 		}
4254 	break;
4255 	    }
4256 	}
4257     }
4258 }
4259 
SllkFree(struct sllk * sllk,int sllk_cnt)4260 void SllkFree(struct sllk *sllk,int sllk_cnt) {
4261     int i;
4262 
4263     for ( i=0; i<sllk_cnt; ++i ) {
4264 	free( sllk[i].langs );
4265 	free( sllk[i].lookups );
4266     }
4267     free(sllk);
4268 }
4269 
AddOTLToSllk(struct sllk * sllk,OTLookup * otl,struct scriptlanglist * sl)4270 static void AddOTLToSllk(struct sllk *sllk, OTLookup *otl, struct scriptlanglist *sl) {
4271     int i,j,k,l;
4272 
4273     if ( otl->lookup_type==gsub_single || otl->lookup_type==gsub_alternate ) {
4274 	for ( i=0; i<sllk->cnt; ++i )
4275 	    if ( sllk->lookups[i]==otl )
4276 	break;
4277 	if ( i==sllk->cnt ) {
4278 	    if ( sllk->cnt>=sllk->max )
4279 		sllk->lookups = realloc(sllk->lookups,(sllk->max+=5)*sizeof(OTLookup *));
4280 	    sllk->lookups[sllk->cnt++] = otl;
4281 	    for ( l=0; l<sl->lang_cnt; ++l ) {
4282 		uint32 lang = l<MAX_LANG ? sl->langs[l] : sl->morelangs[l-MAX_LANG];
4283 		for ( j=0; j<sllk->lcnt; ++j )
4284 		    if ( sllk->langs[j]==lang )
4285 		break;
4286 		if ( j==sllk->lcnt ) {
4287 		    if ( sllk->lcnt>=sllk->lmax )
4288 			sllk->langs = realloc(sllk->langs,(sllk->lmax+=sl->lang_cnt+MAX_LANG)*sizeof(uint32));
4289 		    sllk->langs[sllk->lcnt++] = lang;
4290 		}
4291 	    }
4292 	}
4293     } else if ( otl->lookup_type==gsub_context || otl->lookup_type==gsub_contextchain ) {
4294 	struct lookup_subtable *sub;
4295 	for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
4296 	    FPST *fpst = sub->fpst;
4297 	    if (fpst!=NULL) {
4298 		for ( j=0; j<fpst->rule_cnt; ++j ) {
4299 		     struct fpst_rule *r = &fpst->rules[j];
4300 		     for ( k=0; k<r->lookup_cnt; ++k )
4301 			AddOTLToSllk(sllk,r->lookups[k].lookup,sl);
4302 		}
4303 	    }
4304 	}
4305     }
4306     /* reverse contextual chaining is weird and I shall ignore it. Adobe does too*/
4307 }
4308 
ComponentsFromPSTs(PST ** psts,int pcnt)4309 static char *ComponentsFromPSTs(PST **psts,int pcnt) {
4310     char **names=NULL;
4311     int ncnt=0, nmax=0;
4312     int i,j,len;
4313     char *ret;
4314 
4315     /* First find all the names */
4316     for ( i=0; i<pcnt; ++i ) {
4317 	char *nlist = psts[i]->u.alt.components;
4318 	char *start, *pt, ch;
4319 
4320 	for ( start = nlist; ; ) {
4321 	    while ( *start==' ' )
4322 		++start;
4323 	    if ( *start=='\0' )
4324 	break;
4325 	    for ( pt=start; *pt!=' ' && *pt!='\0'; ++pt );
4326 	    ch = *pt; *pt = '\0';
4327 	    for ( j=0; j<ncnt; ++j )
4328 		if ( strcmp( start,names[j])==0 )
4329 	    break;
4330 	    if ( j==ncnt ) {
4331 		if ( ncnt>=nmax )
4332 		    names = realloc(names,(nmax+=10)*sizeof(char *));
4333 		names[ncnt++] = copy(start);
4334 	    }
4335 	    *pt = ch;
4336 	    start = pt;
4337 	}
4338     }
4339 
4340     len = 0;
4341     for ( i=0; i<ncnt; ++i )
4342 	len += strlen(names[i])+1;
4343     if ( len==0 ) len=1;
4344     ret = malloc(len);
4345     len = 0;
4346     for ( i=0; i<ncnt; ++i ) {
4347 	strcpy(ret+len,names[i]);
4348 	len += strlen(names[i]);
4349 	ret[len++] = ' ';
4350     }
4351     if ( len==0 )
4352 	*ret = '\0';
4353     else
4354 	ret[len-1] = '\0';
4355 
4356     for ( i=0; i<ncnt; ++i )
4357 	free(names[i]);
4358     free(names);
4359 return( ret );
4360 }
4361 
SllkMatch(struct sllk * sllk,int s1,int s2)4362 static int SllkMatch(struct sllk *sllk,int s1,int s2) {
4363     int i;
4364 
4365     if ( sllk[s1].cnt != sllk[s2].cnt )
4366 return( false );
4367 
4368     for ( i=0; i<sllk[s1].cnt; ++i ) {
4369 	if ( sllk[s1].lookups[i] != sllk[s2].lookups[i] )
4370 return( false );
4371     }
4372 
4373 return( true );
4374 }
4375 
AddOTLToSllks(OTLookup * otl,struct sllk * sllk,int * _sllk_cnt,int * _sllk_max)4376 struct sllk *AddOTLToSllks( OTLookup *otl, struct sllk *sllk,
4377 	int *_sllk_cnt, int *_sllk_max ) {
4378     FeatureScriptLangList *fl;
4379     struct scriptlanglist *sl;
4380     int s;
4381 
4382     for ( fl=otl->features; fl!=NULL; fl=fl->next ) {
4383 	for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) {
4384 	    for ( s=0; s<*_sllk_cnt; ++s )
4385 		if ( sl->script == sllk[s].script )
4386 	    break;
4387 	    if ( s==*_sllk_cnt ) {
4388 		if ( *_sllk_cnt>=*_sllk_max )
4389 		    sllk = realloc(sllk,((*_sllk_max)+=10)*sizeof(struct sllk));
4390 		memset(&sllk[*_sllk_cnt],0,sizeof(struct sllk));
4391 		sllk[(*_sllk_cnt)++].script = sl->script;
4392 	    }
4393 	    AddOTLToSllk(&sllk[s], otl,sl);
4394 	}
4395     }
4396 return( sllk );
4397 }
4398 
NewAALTLookup(SplineFont * sf,struct sllk * sllk,int sllk_cnt,int i)4399 OTLookup *NewAALTLookup(SplineFont *sf,struct sllk *sllk, int sllk_cnt, int i) {
4400     OTLookup *otl;
4401     struct lookup_subtable *sub;
4402     FeatureScriptLangList *fl;
4403     struct scriptlanglist *sl;
4404     PST **psts, *pst;
4405     int j,k,l;
4406     int gid,pcnt;
4407     SplineFont *_sf;
4408     SplineChar *sc;
4409 
4410     /* Make the new lookup (and all its supporting data structures) */
4411     otl = chunkalloc(sizeof(OTLookup));
4412     otl->lookup_type = gsub_alternate;
4413     otl->lookup_flags = sllk[i].lookups[0]->lookup_flags & pst_r2l;
4414     otl->features = fl = chunkalloc(sizeof(FeatureScriptLangList));
4415     fl->featuretag = CHR('a','a','l','t');
4416     /* Any other scripts with the same lookup set? */
4417     for ( j=i; j<sllk_cnt; ++j ) {
4418 	if ( i==j || SllkMatch(sllk,i,j)) {
4419 	    sl = chunkalloc(sizeof(struct scriptlanglist));
4420 	    sl->next = fl->scripts;
4421 	    fl->scripts = sl;
4422 	    sl->script = sllk[j].script;
4423 	    sl->lang_cnt = sllk[j].lcnt;
4424 	    if ( sl->lang_cnt>MAX_LANG )
4425 		sl->morelangs = malloc((sl->lang_cnt-MAX_LANG)*sizeof(uint32));
4426 	    for ( l=0; l<sl->lang_cnt; ++l )
4427 		if ( l<MAX_LANG )
4428 		    sl->langs[l] = sllk[j].langs[l];
4429 		else
4430 		    sl->morelangs[l-MAX_LANG] = sllk[j].langs[l];
4431 	    if ( i!=j ) sllk[j].cnt = 0;	/* Mark as processed */
4432 	}
4433     }
4434     otl->subtables = sub = chunkalloc(sizeof(struct lookup_subtable));
4435     sub->lookup = otl;
4436     sub->per_glyph_pst_or_kern = true;
4437 
4438     /* Add it to the various lists it needs to be in */
4439     otl->next = sf->gsub_lookups;
4440     sf->gsub_lookups = otl;
4441 
4442     /* Now look at every glyph in the font, and see if it has any of the */
4443     /*  lookups we are interested in, and if it does, build a new pst */
4444     /*  containing all posibilities listed on any of them */
4445     if ( sf->cidmaster ) sf = sf->cidmaster;
4446     psts = malloc(sllk[i].cnt*sizeof(PST *));
4447     k=0;
4448     do {
4449 	_sf = k<sf->subfontcnt ? sf->subfonts[k] : sf;
4450 	for ( gid=0; gid<_sf->glyphcnt; ++gid ) if ( (sc = _sf->glyphs[gid])!=NULL ) {
4451 	    pcnt = 0;
4452 	    for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
4453 		if ( pst->subtable==NULL )
4454 	    continue;
4455 		for ( j=0; j<sllk[i].cnt; ++j )
4456 		    if ( pst->subtable->lookup == sllk[i].lookups[j] )
4457 		break;
4458 		if ( j<sllk[i].cnt )
4459 		    psts[pcnt++] = pst;
4460 	    }
4461 	    if ( pcnt==0 )
4462 	continue;
4463 	    pst = chunkalloc(sizeof(PST));
4464 	    pst->subtable = sub;
4465 	    pst->type = pst_alternate;
4466 	    pst->next = sc->possub;
4467 	    sc->possub = pst;
4468 	    pst->u.alt.components = ComponentsFromPSTs(psts,pcnt);
4469 	}
4470 	++k;
4471     } while ( k<sf->subfontcnt );
4472     free(psts);
4473     NameOTLookup(otl,sf);
4474 return( otl );
4475 }
4476 
AddNewAALTFeatures(SplineFont * sf)4477 void AddNewAALTFeatures(SplineFont *sf) {
4478     /* different script/lang combinations may need different 'aalt' lookups */
4479     /*  well, let's just say different script combinations */
4480     /* for each script/lang combo find all single/alternate subs for each */
4481     /*  glyph. Merge those choices and create new lookup with that info */
4482     struct sllk *sllk = NULL;
4483     int sllk_cnt=0, sllk_max = 0;
4484     int i;
4485     OTLookup *otl;
4486 
4487     AALTRemoveOld(sf);
4488 
4489     /* Find all scripts, and all the single/alternate lookups for each */
4490     /*  and all the languages used for these in each script */
4491     for ( otl=sf->gsub_lookups; otl!=NULL; otl=otl->next ) {
4492 	sllk = AddOTLToSllks( otl, sllk, &sllk_cnt, &sllk_max );
4493     }
4494     /* Each of these gets its own gsub_alternate lookup which gets inserted */
4495     /*  at the head of the lookup list. Each lookup has one subtable */
4496     for ( i=0; i<sllk_cnt; ++i ) {
4497 	if ( sllk[i].cnt==0 )		/* Script used, but provides no alternates */
4498     continue;
4499 	NewAALTLookup(sf,sllk,sllk_cnt,i);
4500     }
4501 
4502     SllkFree(sllk,sllk_cnt);
4503 }
4504 
4505 
VerticalKernFeature(SplineFont * sf,OTLookup * otl,int ask)4506 int VerticalKernFeature(SplineFont *sf, OTLookup *otl, int ask) {
4507     FeatureScriptLangList *fl;
4508     struct lookup_subtable *sub;
4509     KernClass *kc;
4510     char *buts[3];
4511 
4512     for ( fl=otl->features; fl!=NULL; fl=fl->next ) {
4513 	if ( fl->featuretag==CHR('k','e','r','n') )
4514 return( false );
4515 	else if ( fl->featuretag==CHR('v','k','r','n') )
4516 return( true );
4517     }
4518 
4519     for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
4520 	if ( sub->kc!=NULL ) {
4521 	    for ( kc=sf->kerns; kc!=NULL; kc=kc->next )
4522 		if ( kc==sub->kc )
4523 return( false );
4524 	    for ( kc=sf->vkerns; kc!=NULL; kc=kc->next )
4525 		if ( kc==sub->kc )
4526 return( true );
4527 	}
4528     }
4529 
4530     if ( !ask )
4531 return( -1 );
4532 
4533     buts[0] = _("_Horizontal"); buts[1] = _("_Vertical"); buts[2] = NULL;
4534 return( ff_ask(_("Kerning direction"),(const char **) buts,0,1,_("Is this horizontal or vertical kerning data?")) );
4535 }
4536 
IsAnchorClassUsed(SplineChar * sc,AnchorClass * an)4537 int IsAnchorClassUsed(SplineChar *sc,AnchorClass *an) {
4538     AnchorPoint *ap;
4539     int waslig=0, sawentry=0, sawexit=0;
4540 
4541     for ( ap=sc->anchor; ap!=NULL; ap=ap->next ) {
4542 	if ( ap->anchor==an ) {
4543 	    if ( ap->type==at_centry )
4544 		sawentry = true;
4545 	    else if ( ap->type==at_cexit )
4546 		sawexit = true;
4547 	    else if ( an->type==act_mkmk ) {
4548 		if ( ap->type==at_basemark )
4549 		    sawexit = true;
4550 		else
4551 		    sawentry = true;
4552             } else if ( an->type==act_unknown ) {
4553                 if ( ap->type==at_basechar )
4554                     sawexit = true;
4555                 else
4556                     sawentry = true;
4557 	    } else if ( ap->type!=at_baselig )
4558 return( -1 );
4559 	    else if ( waslig<ap->lig_index+1 )
4560 		waslig = ap->lig_index+1;
4561 	}
4562     }
4563     if ( sawentry && sawexit )
4564 return( -1 );
4565     else if ( sawentry )
4566 return( -2 );
4567     else if ( sawexit )
4568 return( -3 );
4569 return( waslig );
4570 }
4571 
PSTContains(const char * components,const char * name)4572 int PSTContains(const char *components,const char *name) {
4573     const char *pt;
4574     int len = strlen(name);
4575 
4576     for ( pt = strstr(components,name); pt!=NULL; pt = strstr(pt+len,name)) {
4577 	if (( pt==components || pt[-1]==' ') && (pt[len]==' ' || pt[len]=='\0'))
4578 return( true );
4579     }
4580 return( false );
4581 }
4582 
4583 
KernClassFindIndexContaining(char ** firsts_or_seconds,int firsts_or_seconds_size,const char * name)4584 int KernClassFindIndexContaining( char **firsts_or_seconds,
4585 				  int firsts_or_seconds_size,
4586 				  const char *name )
4587 {
4588     int ret = -1;
4589     int i = 0 ;
4590 
4591     for ( i=1; i < firsts_or_seconds_size; ++i )
4592     {
4593 	if ( PSTContains(firsts_or_seconds[i],name) )
4594 	{
4595 	    ret = i;
4596 	    break;
4597 	}
4598     }
4599 
4600     return ret;
4601 }
4602 
4603 
KernClassContains(KernClass * kc,const char * name1,const char * name2,int ordered)4604 int KernClassContains(KernClass *kc, const char *name1, const char *name2, int ordered ) {
4605     int infirst=0, insecond=0, scpos1, kwpos1, scpos2, kwpos2;
4606     int i;
4607 
4608     for ( i=1; i<kc->first_cnt; ++i ) {
4609 	if ( PSTContains(kc->firsts[i],name1) ) {
4610 	    scpos1 = i;
4611 	    if ( ++infirst>=3 )		/* The name occurs twice??? */
4612     break;
4613 	} else if ( PSTContains(kc->firsts[i],name2) ) {
4614 	    kwpos1 = i;
4615 	    if ( (infirst+=2)>=3 )
4616     break;
4617 	}
4618     }
4619     if ( infirst==0 || infirst>3 )
4620 return( 0 );
4621     for ( i=1; i<kc->second_cnt; ++i ) {
4622 	if ( PSTContains(kc->seconds[i],name1) ) {
4623 	    scpos2 = i;
4624 	    if ( ++insecond>=3 )
4625     break;
4626 	} else if ( PSTContains(kc->seconds[i],name2) ) {
4627 	    kwpos2 = i;
4628 	    if ( (insecond+=2)>=3 )
4629     break;
4630 	}
4631     }
4632     if ( insecond==0 || insecond>3 )
4633 return( 0 );
4634     if ( (infirst&1) && (insecond&2) ) {
4635 	if ( kc->offsets[scpos1*kc->second_cnt+kwpos2]!=0 )
4636 return( kc->offsets[scpos1*kc->second_cnt+kwpos2] );
4637     }
4638     if ( !ordered ) {
4639 	if ( (infirst&2) && (insecond&1) ) {
4640 	    if ( kc->offsets[kwpos1*kc->second_cnt+scpos2]!=0 )
4641 return( kc->offsets[kwpos1*kc->second_cnt+scpos2] );
4642 	}
4643     }
4644 return( 0 );
4645 }
4646 
KCFindName(const char * name,char ** classnames,int cnt,int allow_class0)4647 int KCFindName(const char *name, char **classnames, int cnt, int allow_class0 ) {
4648     int i;
4649     char *pt, *end, ch;
4650 
4651     for ( i=0; i<cnt; ++i ) {
4652 	if ( classnames[i]==NULL )
4653     continue;
4654 	for ( pt = classnames[i]; *pt; pt=end+1 ) {
4655 	    end = strchr(pt,' ');
4656 	    if ( end==NULL ) end = pt+strlen(pt);
4657 	    ch = *end;
4658 	    *end = '\0';
4659 	    if ( strcmp(pt,name)==0 ) {
4660 		*end = ch;
4661 return( i );
4662 	    }
4663 	    *end = ch;
4664 	    if ( ch=='\0' )
4665 	break;
4666 	}
4667     }
4668     /* If class 0 is specified, then we didn't find anything. If class 0 is */
4669     /*  unspecified then it means "anything" so we found something */
4670 return( classnames[0]!=NULL || !allow_class0 ? -1 : 0 );
4671 }
4672 
4673 /* Routines to generate human readable forms of FPST rules */
GrowBufferAddLookup(GrowBuf * gb,struct fpst_rule * rule,int seq)4674 static void GrowBufferAddLookup(GrowBuf *gb,struct fpst_rule *rule, int seq) {
4675     int i;
4676 
4677     for ( i=0; i<rule->lookup_cnt; ++i ) {
4678 	if ( seq==rule->lookups[i].seq ) {
4679 	    GrowBufferAddStr(gb,"@<");
4680 	    GrowBufferAddStr(gb,rule->lookups[i].lookup->lookup_name);
4681 	    GrowBufferAddStr(gb,"> ");
4682 	}
4683     }
4684 }
4685 
GrowBufferAddClass(GrowBuf * gb,int class_n,char ** classnames,int class_cnt)4686 static void GrowBufferAddClass(GrowBuf *gb,int class_n,char **classnames, int class_cnt) {
4687     char buffer[20], *str;
4688 
4689     if ( class_n<0 || class_n>=class_cnt ) {
4690 	IError("Bad class in FPST" );
4691 	class_n = 0;
4692     }
4693     if ( classnames==NULL || (str=classnames[class_n])==NULL ) {
4694 	sprintf( buffer,"%d", class_n );
4695 	str = buffer;
4696     }
4697     GrowBufferAddStr(gb,str);
4698     GrowBufferAdd(gb,' ');
4699 }
4700 
GlyphNameCnt(const char * pt)4701 int GlyphNameCnt(const char *pt) {
4702     int cnt = 0;
4703 
4704     while ( *pt ) {
4705 	while ( isspace( *pt )) ++pt;
4706 	if ( *pt=='\0' )
4707 return( cnt );
4708 	++cnt;
4709 	while ( !isspace(*pt) && *pt!='\0' ) ++pt;
4710     }
4711 return( cnt );
4712 }
4713 
reverseGlyphNames(char * str)4714 char *reverseGlyphNames(char *str) {
4715     char *ret;
4716     char *rpt, *pt, *start, *spt;
4717 
4718     if ( str==NULL )
4719 return( NULL );
4720 
4721     rpt = ret = malloc(strlen(str)+1);
4722     *ret = '\0';
4723     for ( pt=str+strlen(str); pt>str; pt=start ) {
4724 	for ( start = pt-1; start>=str && *start!=' '; --start );
4725 	for ( spt=start+1; spt<pt; )
4726 	    *rpt++ = *spt++;
4727 	*rpt++ = ' ';
4728     }
4729     if ( rpt>ret )
4730 	rpt[-1] = '\0';
4731 return( ret );
4732 }
4733 
4734 /* Note: My string format cannot represent contextual rules were the order */
4735 /*  of application of lookups is not the same as textual order. This rarely */
4736 /*  happens. Adobe's feature files have the same drawback. And there are */
4737 /*  rarely two or more lookups applied by one rule. But it could, so be aware!*/
FPSTRule_To_Str(SplineFont * sf,FPST * fpst,struct fpst_rule * rule)4738 char *FPSTRule_To_Str(SplineFont *sf,FPST *fpst,struct fpst_rule *rule) {
4739     int i, max=0;
4740     char *ret, *npt;
4741     int seq=0;
4742     GrowBuf gb;
4743 
4744     /* Note that nothing in the output distinquishes between back, match and forward */
4745     /*  the thought being that to all intents and purposes, match starts at */
4746     /*  the first lookup and ends at the last. Anything before is back, */
4747     /*  anything after is for */ /* Adobe uses this convention in feature files*/
4748     /* Drat. That doesn't work for classes if the back/fore classes differ */
4749     /*  from match classes */ /* Adobe doesn't support classes in feature files*/
4750     /* Nor does it work when the lookup needs more than one input characters */
4751     /*  ligature lookups */
4752     memset(&gb,0,sizeof(gb));
4753     switch ( fpst->format ) {
4754       case pst_glyphs:
4755 	max = ( rule->u.glyph.names ? strlen(rule->u.glyph.names) : 0 ) +
4756 		( rule->u.glyph.back ? strlen(rule->u.glyph.back) : 0 ) +
4757 		( rule->u.glyph.fore ? strlen(rule->u.glyph.fore) : 0 ) +
4758 		200;
4759 	gb.base = gb.pt = malloc(max+1);
4760 	gb.end = gb.base+max;
4761         if ( rule->u.glyph.back!=NULL ) {
4762 	    char *temp;
4763 	    GrowBufferAddStr(&gb,(temp = reverseGlyphNames(rule->u.glyph.back)));
4764 	    free(temp);
4765 	    GrowBufferAdd(&gb,' ');
4766 	}
4767 	if ( fpst->type!=pst_contextpos && fpst->type!=pst_contextsub )
4768 	    GrowBufferAddStr(&gb,"| ");
4769 	for ( npt=rule->u.glyph.names; isspace(*npt); ++npt)
4770 	    /* Skip over leading spaces, if any */;
4771 	for ( npt=rule->u.glyph.names, seq=0; *npt; ++seq ) {
4772 	    while ( isspace(*npt))
4773 		++npt;
4774 	    while ( *npt!='\0' && !isspace( *npt ) ) {
4775 		GrowBufferAdd(&gb, *npt++);
4776 	    }
4777 	    GrowBufferAdd(&gb,' ');
4778 	    GrowBufferAddLookup(&gb,rule,seq);
4779 	}
4780 	if ( fpst->type!=pst_contextpos && fpst->type!=pst_contextsub )
4781 	    GrowBufferAddStr(&gb,"| ");
4782         if ( rule->u.glyph.fore!=NULL ) {
4783 	    GrowBufferAddStr(&gb,rule->u.glyph.fore);
4784 	}
4785       break;
4786       case pst_class:
4787 	/* Reverse the backtrack classes */
4788 	for ( i=rule->u.class.bcnt-1; i>=0; --i )
4789 	    GrowBufferAddClass(&gb,rule->u.class.bclasses[i],fpst->bclassnames,fpst->bccnt);
4790 	if ( fpst->type!=pst_contextpos && fpst->type!=pst_contextsub )
4791 	    GrowBufferAddStr(&gb,"| ");
4792 	for ( i=0; i<rule->u.class.ncnt; ++i ) {
4793 	    GrowBufferAddClass(&gb,rule->u.class.nclasses[i],fpst->nclassnames,fpst->nccnt);
4794 	    GrowBufferAddLookup(&gb,rule,i);
4795 	}
4796 	if ( fpst->type!=pst_contextpos && fpst->type!=pst_contextsub )
4797 	    GrowBufferAddStr(&gb,"| ");
4798 	for ( i=0; i<rule->u.class.fcnt; ++i )
4799 	    GrowBufferAddClass(&gb,rule->u.class.fclasses[i],fpst->fclassnames,fpst->fccnt);
4800       break;
4801       case pst_coverage:
4802       case pst_reversecoverage:
4803         /* Reverse the backtrack tables */
4804 	for ( i=rule->u.coverage.bcnt-1; i>=0; --i ) {
4805 	    GrowBufferAdd(&gb,'[');
4806 	    GrowBufferAddStr(&gb,rule->u.coverage.bcovers[i]);
4807 	    GrowBufferAddStr(&gb,"] ");
4808 	}
4809 	if ( fpst->type!=pst_contextpos && fpst->type!=pst_contextsub )
4810 	    GrowBufferAddStr(&gb,"| ");
4811 	for ( i=0; i<rule->u.coverage.ncnt; ++i ) {
4812 	    GrowBufferAdd(&gb,'[');
4813 	    GrowBufferAddStr(&gb,rule->u.coverage.ncovers[i]);
4814 	    GrowBufferAddStr(&gb,"] ");
4815 	    if ( fpst->format==pst_reversecoverage ) {
4816 		GrowBufferAddStr(&gb,"=> [");
4817 		GrowBufferAddStr(&gb,rule->u.rcoverage.replacements);
4818 		GrowBufferAddStr(&gb,"] ");
4819 	    } else {
4820 		GrowBufferAddLookup(&gb,rule,i);
4821 	    }
4822 	}
4823 	if ( fpst->type!=pst_contextpos && fpst->type!=pst_contextsub )
4824 	    GrowBufferAddStr(&gb,"| ");
4825 	for ( i=0; i<rule->u.coverage.fcnt; ++i ) {
4826 	    GrowBufferAdd(&gb,'[');
4827 	    GrowBufferAddStr(&gb,rule->u.coverage.fcovers[i]);
4828 	    GrowBufferAddStr(&gb,"] ");
4829 	}
4830       break;
4831       default:
4832 	IError( "Bad FPST format");
4833 return( NULL );
4834     }
4835     if ( gb.pt>gb.base && gb.pt[-1]==' ' )
4836 	gb.pt[-1] = '\0';
4837     ret = copy(gb.base);
4838     free(gb.base);
4839 return( ret );
4840 }
4841 
4842 typedef struct lookuplist {
4843     OTLookup *lookup;
4844     struct lookuplist *next;
4845 } LookupList;
4846 
4847 typedef struct matchstr {
4848     char *entity;
4849     char *replacements;		/* For reverse contextual chaining */
4850     LookupList *lookups;
4851 } MatchStr;
4852 
4853 /* Returns an error message, a warning, or NULL if parsing were successful */
4854 /*  if successful rule will be filled in. Error msg must be freed */
FPSTRule_From_Str(SplineFont * sf,FPST * fpst,struct fpst_rule * rule,char * line,int * return_is_warning)4855 char *FPSTRule_From_Str(SplineFont *sf,FPST *fpst,struct fpst_rule *rule,
4856 	char *line, int *return_is_warning ) {
4857     char *lpt, *start, *end;
4858     int ch;
4859     int do_replacements=0, anylookup=0;
4860     int cnt=0, max=0;
4861     MatchStr *parsed=NULL;
4862     LookupList *ll, *llp;
4863     int i,j,first,last;
4864     char *ret;
4865     int isgpos = fpst->type==pst_contextpos || fpst->type==pst_chainpos;
4866     OTLookup *lookup;
4867 
4868     /* Parse the string into meaningful chunks. These could be:
4869 	A coverage table (a list of glyph names enclosed in []
4870 	A glyph name	(I'll accept this as a degenerate coverage table containing one glyph)
4871 	A class name
4872 	A lookup invocation
4873 	A list of replacement glyphs for a reverse contextual chaining lookup
4874 	    (This is a two step process. First parse the replacement marker "=>"
4875 	     then parse the coverage table)
4876 	We can also have a mark that we've switched from the backtracking list
4877 	    to the match list to the forward list. Might be needed for classes
4878     */
4879 
4880     first = last = -1;
4881     *return_is_warning = false;
4882     for ( lpt=line; *lpt; ) {
4883 	while ( isspace(*lpt)) ++lpt;
4884 	if ( *lpt=='\0' )
4885     break;
4886 	start = lpt;
4887 	if ( *start=='|' ) {
4888 	    if ( fpst->type==pst_contextpos || fpst->type==pst_contextsub )
4889 return( smprintf( _("Separation marks only meaningful in contextual chaining lookups, starting at: %.20s..."), lpt ));
4890 	    if ( first==-1 )
4891 		first = cnt;
4892 	    else if ( last==-1 )
4893 		last = cnt-1;
4894 	    else
4895 return( smprintf( _("Too many separation marks, starting at: %.20s..."), lpt ));
4896 	    ++lpt;
4897     continue;
4898 	} else if ( *start=='[' ) {
4899 	    /* A coverage table */
4900 	    if ( fpst->format!=pst_coverage && fpst->format!=pst_reversecoverage )
4901 return( smprintf( _("A coverage table was found in a glyph or class based contextual lookup, starting at: %.20s..."), lpt ));
4902 	    ++start;
4903 	    for ( lpt = start; *lpt!='\0' && *lpt!=']'; ++lpt );
4904 	    if ( *lpt!=']' )
4905 return( smprintf( _("Unterminated coverage table, starting at: %.20s..."), start-1 ));
4906 	    end = lpt++;
4907 	    if ( do_replacements==1 ) {
4908 		int rcnt, ecnt;
4909 		do_replacements = 2;
4910 		if ( cnt==0 )
4911 return( smprintf( _("Replacements must follow the coverage table to which they apply: %s"), start-4 ));
4912 		ch = *end; *end = '\0';
4913 		/* Must back up one! */
4914 		cnt--;
4915 		parsed[cnt].replacements = copy(start);
4916 		*end = ch;
4917 		rcnt = GlyphNameCnt(parsed[cnt].replacements);
4918 		ecnt = GlyphNameCnt(parsed[cnt].entity);
4919 		if ( ecnt==rcnt )
4920 		    /* Good */;
4921 		else if ( rcnt==1 && ecnt>1 ) {
4922 		    char *newr;
4923 		    newr = malloc(ecnt*(strlen(parsed[cnt].replacements)+1)+1);
4924 		    *newr = '\0';
4925 		    for ( i=0; i<ecnt; ++i ) {
4926 			strcat(newr,parsed[cnt].replacements);
4927 			if ( i!=ecnt-1 )
4928 			    strcat(newr," ");
4929 		    }
4930 		    free(parsed[cnt].replacements);
4931 		    parsed[cnt].replacements = newr;
4932 		} else
4933 return( smprintf( _("There must be as many replacement glyphs as there are match glyphs: %s => %s"),
4934 		  parsed[cnt].entity, parsed[cnt].replacements));
4935 		// And advance back to where we were
4936 		cnt++;
4937 	    }
4938 	} else if ( *start!='@' && *start!='<' && !(*start=='=' && start[1]=='>') ) {
4939 	    /* Just a normal glyph or class name. (If we expect a coverage table we'll treat it as a table with one glyph) */
4940 	    while ( *lpt!='\0' && !isspace(*lpt) && *lpt!='@' && *lpt!='<' && *lpt!='[' )
4941 		++lpt;
4942 	    end = lpt;
4943 	} else if ( *start=='=' && start[1]=='>' ) {
4944 	    /* A reverse contextual chaining */
4945 	    if ( fpst->format!=pst_reversecoverage )
4946 return( smprintf( _("No replacement lists may be specified in this contextual lookup, use a nested lookup instead, starting at: %.20s..."), lpt ));
4947 	    if ( do_replacements )
4948 return( smprintf( _("Only one replacement list may be specified in a reverse contextual chaining lookup, starting at: %.20s..."), lpt ));
4949 	    do_replacements = 1;
4950 	    lpt += 2;
4951 	    continue;
4952 	} else {
4953 	    /* A lookup invocation */
4954 	    if ( fpst->format==pst_reversecoverage )
4955 return( smprintf( _("No lookups may be specified in a reverse contextual lookup (use a replacement list instead), starting at: %.20s..."), lpt ));
4956 
4957 	    if ( *start=='@' ) {
4958 		for ( lpt=start+1; isspace( *lpt ); ++lpt );
4959 		if ( *lpt!='<' )
4960 return( smprintf( _("A lookup invocation must be started by the sequence '@<' and ended with '>', starting at: %.20s..." ), start ) );
4961 	    }
4962 	    start= ++lpt;
4963 	    for ( lpt = start; *lpt!='\0' && *lpt!='>'; ++lpt );
4964 	    if ( *lpt!='>' )
4965 return( smprintf( _("Unterminated lookup invocation, starting at: %.20s..."), start-1 ));
4966 	    *lpt = '\0';
4967 	    lookup = SFFindLookup(sf,start);
4968 	    if ( lookup==NULL ) {
4969 		ret = smprintf( _("Unknown lookup: %s"), start );
4970 		*lpt = '>';
4971 return( ret );
4972 	    } else if ( (isgpos && lookup->lookup_type<gpos_start) || (!isgpos && lookup->lookup_type>gpos_start)) {
4973 		ret = smprintf( isgpos ? _("GSUB lookup refered to in this GPOS contextual lookup: %s"):
4974 			    _("GPOS lookup refered to in this GSUB contextual lookup: %s"),
4975 			start );
4976 		*lpt = '>';
4977 return( ret );
4978 	    } else if ( cnt==0 ) {
4979 		ret = smprintf( _("Lookups must follow the glyph, class or coverage table to which they apply: %s"), start );
4980 		*lpt = '>';
4981 return( ret );
4982 	    }
4983 	    *lpt++ = '>';
4984 	    ll = chunkalloc(sizeof(LookupList));
4985 	    ll->lookup = lookup;
4986 	    /* Lookup order is important */
4987 	    if ( parsed[cnt-1].lookups==NULL )
4988 		parsed[cnt-1].lookups = ll;
4989 	    else {
4990 		for ( llp=parsed[cnt-1].lookups; llp->next!=NULL; llp=llp->next );
4991 		llp->next = ll;
4992 	    }
4993 	    anylookup = true;
4994 	    if ( first==-1 ) first = cnt-1;
4995     continue;
4996 	}
4997 	/* We get here on glyph/class names and coverage tables */
4998 	/*  not on lookup invocations */
4999 	/* BUT!  Don't do this if we just finished replacements! */
5000 	if (do_replacements == 2) {
5001 	    do_replacements = 3;
5002 	}
5003 	else {
5004 	    ch = *end; *end='\0';
5005 	    if ( cnt>=max )
5006 		parsed = realloc(parsed,(max+=200)*sizeof(MatchStr));
5007 	    memset(&parsed[cnt],'\0',sizeof(MatchStr));
5008 	    parsed[cnt++].entity = copy(start);
5009 	    *end = ch;
5010 	}
5011     }
5012 
5013     if ( cnt==0 )
5014 return( copy( _("Empty rule" )) );
5015 
5016     ret = NULL;
5017 
5018     if ( !do_replacements && !anylookup ) {
5019 	if ( fpst->format==pst_reversecoverage )
5020 return( copy( _("A reverse contextual chaining lookup must have a set of replacement glyphs somewhere" )) );
5021 	else {
5022 	    *return_is_warning = true;
5023 	    /* If there are no lookups then this rule matches and does nothing */
5024 	    /*  which can make it easier to write subsequent rules */
5025 	    ret = copy( _("This contextual rule applies no lookups." ));
5026 	}
5027     }
5028 
5029     /* Figure out the boundaries between backtrack, match and forward */
5030     for ( i=0; i<cnt; ++i ) {
5031 	if ( parsed[i].lookups!=NULL ) {
5032 	    if ( i>last ) last = i;
5033 	    if ( first==-1 ) first = i;
5034 	}
5035 	if ( parsed[i].replacements!=NULL ) {
5036 	    if (( first!=-1 && first!=i ) || (last!=-1 && last!=i ))
5037 return( copy( _("A reverse contextual chaining lookup can only match one coverage table directly" )) );
5038 	    first = last = i;
5039 	}
5040     }
5041     if ( fpst->type==pst_contextpos || fpst->type==pst_contextsub ) {
5042 	first = 0;
5043 	last = cnt-1;
5044     }
5045 
5046     switch ( fpst->format ) {
5047       case pst_glyphs: {
5048 	int blen=0, mlen=0, flen=0;
5049 	for ( i=0; i<cnt; ++i ) {
5050 	    if ( SFGetChar(sf,-1,parsed[i].entity)==NULL ) {
5051 		if ( ret==NULL ) {
5052 		    ret = smprintf( _("There is no glyph named \"%s\" in the font."), parsed[i].entity );
5053 		    *return_is_warning = true;
5054 		}
5055 	    }
5056 	    if ( i<first )
5057 		blen += strlen(parsed[i].entity)+1;
5058 	    else if ( i<=last )
5059 		mlen += strlen(parsed[i].entity)+1;
5060 	    else
5061 		flen += strlen(parsed[i].entity)+1;
5062 	}
5063 	rule->u.glyph.names = calloc(mlen+1,1);
5064 	if ( blen!=0 )
5065 	    rule->u.glyph.back = calloc(blen+1,1);
5066 	if ( flen!=0 )
5067 	    rule->u.glyph.fore = calloc(flen+1,1);
5068 	for ( i=0; i<cnt; ++i ) {
5069 	    if ( i<first ) {
5070 		strcat(rule->u.glyph.back,parsed[i].entity);
5071 		if ( i!=first-1)
5072 		    strcat(rule->u.glyph.back," ");
5073 	    } else if ( i<=last ) {
5074 		strcat(rule->u.glyph.names,parsed[i].entity);
5075 		if ( i!=last)
5076 		    strcat(rule->u.glyph.names," ");
5077 	    } else {
5078 		strcat(rule->u.glyph.fore,parsed[i].entity);
5079 		if ( i!=cnt-1)
5080 		    strcat(rule->u.glyph.fore," ");
5081 	    }
5082 	}
5083 	if ( blen!=0 ) {
5084 	    char *temp = reverseGlyphNames(rule->u.glyph.back);
5085 	    free(rule->u.glyph.back);
5086 	    rule->u.glyph.back = temp;
5087 	}
5088       } break;
5089       case pst_class:
5090         rule->u.class.ncnt = last+1-first;
5091 	rule->u.class.nclasses = malloc(rule->u.class.ncnt*sizeof(uint16));
5092 	rule->u.class.bcnt = first;
5093 	if ( first!=0 )
5094 	    rule->u.class.bclasses = malloc(first*sizeof(uint16));
5095 	rule->u.class.fcnt = cnt==last?0:cnt-last-1;
5096 	if ( rule->u.class.fcnt!=0 )
5097 	    rule->u.class.fclasses = malloc(rule->u.class.fcnt*sizeof(uint16));
5098 	for ( i=0; i<cnt; ++i ) {
5099 	    char **classnames, *pend;
5100 	    int class_cnt, val;
5101 	    if ( i<first ) {
5102 		classnames = fpst->bclassnames;
5103 		class_cnt = fpst->bccnt;
5104 	    } else if ( i<=last ) {
5105 		classnames = fpst->nclassnames;
5106 		class_cnt = fpst->nccnt;
5107 	    } else {
5108 		classnames = fpst->fclassnames;
5109 		class_cnt = fpst->fccnt;
5110 	    }
5111 	    val = strtol(parsed[i].entity,&pend,10);
5112 	    if ( *pend!='\0' )
5113 		val = -1;
5114 	    for ( j=0; j<class_cnt; ++j ) {
5115 		if ( classnames[j]!=NULL ) {
5116 		    if ( strcmp(parsed[i].entity,classnames[j])==0 )
5117 	    break;
5118 		} else {
5119 		    if ( val==j )
5120 	    break;
5121 		}
5122 	    }
5123 	    if ( j==class_cnt ) {
5124 		free( rule->u.class.nclasses ); rule->u.class.nclasses = NULL;
5125 		free( rule->u.class.bclasses ); rule->u.class.bclasses = NULL;
5126 		free( rule->u.class.fclasses ); rule->u.class.fclasses = NULL;
5127 		rule->u.class.bcnt = rule->u.class.fcnt = rule->u.class.ncnt = 0;
5128 		if ( i<first )
5129 return( smprintf( _("%s is not a class name for the backtracking classes." ), parsed[i].entity ) );
5130 		else if ( i<=last )
5131 return( smprintf( _("%s is not a class name for the matching classes." ), parsed[i].entity ) );
5132 		else
5133 return( smprintf( _("%s is not a class name for the forward classes." ), parsed[i].entity ) );
5134 	    }
5135 	    if ( i<first )
5136 		rule->u.class.bclasses[first-1-i] = j;	/* Reverse the backtrack classes */
5137 	    else if ( i<=last )
5138 		rule->u.class.nclasses[i-first] = j;
5139 	    else
5140 		rule->u.class.fclasses[i-last-1] = j;
5141 	}
5142       break;
5143       case pst_coverage:
5144       case pst_reversecoverage:
5145 	for ( i=0; i<cnt; ++i ) {
5146 	    for ( lpt = parsed[i].entity; *lpt ; ) {
5147 		while ( isspace(*lpt)) ++lpt;
5148 		if ( *lpt=='\0' )
5149 	    break;
5150 		start = lpt;
5151 		while ( !isspace(*lpt) && *lpt!='\0' )
5152 		    ++lpt;
5153 		ch = *lpt; *lpt='\0';
5154 		if ( SFGetChar(sf,-1,start)==NULL ) {
5155 		    if ( ret==NULL ) {
5156 			ret = smprintf( _("There is no glyph named \"%s\" in the font."), start );
5157 			*return_is_warning = true;
5158 		    }
5159 		}
5160 		*lpt = ch;
5161 	    }
5162 	}
5163         rule->u.coverage.ncnt = last+1-first;
5164 	rule->u.coverage.ncovers = malloc(rule->u.coverage.ncnt*sizeof(char *));
5165 	rule->u.coverage.bcnt = first;
5166 	if ( first!=0 )
5167 	    rule->u.coverage.bcovers = malloc(first*sizeof(char *));
5168 	rule->u.coverage.fcnt = cnt-last-1;
5169 	if ( rule->u.coverage.fcnt!=0 )
5170 	    rule->u.coverage.fcovers = malloc(rule->u.coverage.fcnt*sizeof(char *));
5171 	for ( i=0; i<cnt; ++i ) {
5172 	    if ( i<first )
5173 		rule->u.coverage.bcovers[first-1-i] = parsed[i].entity;	/* Reverse the order of backtrack coverage tables */
5174 	    else if ( i<=last ) {
5175 		rule->u.coverage.ncovers[i-first] = parsed[i].entity;
5176 		if ( fpst->format==pst_reversecoverage ) {
5177 		    rule->u.rcoverage.replacements = parsed[i].replacements;
5178 		    parsed[i].replacements = NULL;
5179 		}
5180 	    } else
5181 		rule->u.coverage.fcovers[i-last-1] = parsed[i].entity;
5182 	    parsed[i].entity = NULL;
5183 	}
5184       break;
5185       default:
5186 	  for ( i=0; i<cnt; i++ ) {
5187 	      free( parsed[i].entity );
5188 	      free( parsed[i].replacements );
5189 	  }
5190 return( copy( _("Bad FPST format")) );
5191     }
5192     if ( fpst->format!=pst_reversecoverage ) {
5193 	int tot=0;
5194 	for ( i=first; i<=last; ++i ) {
5195 	    for ( ll=parsed[i].lookups; ll!=NULL; ll=ll->next )
5196 		++tot;
5197 	}
5198 	rule->lookups = calloc(tot,sizeof(struct seqlookup));
5199 	rule->lookup_cnt = tot;
5200 	tot = 0;
5201 	for ( i=first; i<=last; ++i ) {
5202 	    for ( ll=parsed[i].lookups; ll!=NULL; ll=llp ) {
5203 		llp = ll->next;
5204 		rule->lookups[tot].seq = i-first;
5205 		rule->lookups[tot].lookup = ll->lookup;
5206 		++tot;
5207 		chunkfree(ll,sizeof(*ll));
5208 	    }
5209 	}
5210     }
5211     for ( i=0; i<cnt; ++i )
5212 	free( parsed[i].entity );
5213     free(parsed);
5214 return( ret );
5215 }
5216 
5217 /* User interface functionality when we have no UI */
NOFI_SortInsertLookup(SplineFont * sf,OTLookup * newotl)5218 static void NOFI_SortInsertLookup(SplineFont *sf, OTLookup *newotl) {
5219 }
5220 
NOFI_OTLookupCopyInto(SplineFont * into_sf,SplineFont * from_sf,OTLookup * from_otl,OTLookup * to_otl,int scnt,OTLookup * before)5221 static void NOFI_OTLookupCopyInto(SplineFont *into_sf,SplineFont *from_sf,
5222 	OTLookup *from_otl, OTLookup *to_otl, int scnt, OTLookup *before ) {
5223 }
5224 
NOFI_Destroy(SplineFont * sf)5225 static void NOFI_Destroy(SplineFont *sf) {
5226 }
5227 
5228 struct fi_interface noui_fi = {
5229     NOFI_SortInsertLookup,
5230     NOFI_OTLookupCopyInto,
5231     NOFI_Destroy
5232 };
5233 
5234 struct fi_interface *fi_interface = &noui_fi;
5235 
FF_SetFIInterface(struct fi_interface * fii)5236 void FF_SetFIInterface(struct fi_interface *fii) {
5237     fi_interface = fii;
5238 }
5239