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