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