1 /* Copyright (C) 2003-2012 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <fontforge-config.h>
29 
30 #include "autohint.h"
31 #include "chardata.h"
32 #include "dumppfa.h"
33 #include "featurefile.h"
34 #include "fontforgevw.h"
35 #include "fvfonts.h"
36 #include "gfile.h"
37 #include "glif_name_hash.h"
38 #include "lookups.h"
39 #include "splinesaveafm.h"
40 #include "splineutil.h"
41 #include "splineutil2.h"
42 #include "svg.h"
43 #include "tottf.h"
44 #include "tottfgpos.h"
45 #include "ustring.h"
46 #include "utype.h"
47 
48 #ifndef _NO_PYTHON
49 # include "ffpython.h"
50 #endif
51 
52 #include <locale.h>
53 #include <math.h>
54 #include <sys/stat.h>
55 #include <sys/types.h>
56 #include <time.h>
57 #include <unistd.h>
58 #include <assert.h>
59 #include <stdarg.h>
60 
61 #undef extended			/* used in xlink.h */
62 #include <libxml/tree.h>
63 
64 #ifndef HAVE_ICONV_H
65 # undef iconv
66 # undef iconv_t
67 # undef iconv_open
68 # undef iconv_close
69 #endif
70 #include <libxml/parser.h>
71 
72 /* The UFO (Unified Font Object) format ( http://unifiedfontobject.org/ ) */
73 /* is a directory containing a bunch of (mac style) property lists and another*/
74 /* directory containing glif files (and contents.plist). */
75 
76 /* Property lists contain one <dict> element which contains <key> elements */
77 /*  each followed by an <integer>, <real>, <true/>, <false/> or <string> element, */
78 /*  or another <dict> */
79 
80 /* UFO format 2.0 includes an adobe feature file "features.fea" and slightly */
81 /*  different/more tags in fontinfo.plist */
82 
buildname(const char * basedir,const char * sub)83 static char *buildname(const char *basedir, const char *sub) {
84     char *fname = malloc(strlen(basedir)+strlen(sub)+2);
85 
86     strcpy(fname, basedir);
87     if ( fname[strlen(fname)-1]!='/' )
88 	strcat(fname,"/");
89     strcat(fname,sub);
90 return( fname );
91 }
92 
extractNumericVersion(const char * textVersion,int * versionMajor,int * versionMinor)93 static void extractNumericVersion(const char * textVersion, int * versionMajor, int * versionMinor) {
94   // We extract integer values for major and minor versions from a version string.
95   *versionMajor = -1; *versionMinor = -1;
96   if (textVersion == NULL) return;
97   char *nextText1 = NULL;
98   char *nextText2 = NULL;
99   int tempVersion = -1;
100   tempVersion = strtol(textVersion, &nextText1, 10);
101   if (tempVersion != -1 && nextText1 != NULL && (nextText1[0] == '\0' || nextText1[0] == ' ' || nextText1[0] == '.')) {
102     *versionMajor = tempVersion;
103   } else return;
104   if (nextText1[0] == '\0') return;
105   tempVersion = strtol(nextText1+1, &nextText2, 10);
106   if (tempVersion != -1 && nextText2 != NULL && (nextText2[0] == '\0' || nextText2[0] == ' ' || nextText2[0] == '.')) {
107     *versionMinor = tempVersion;
108   } else return;
109   return;
110 }
111 
formatNumericVersion(int versionMajor,int versionMinor)112 static char* formatNumericVersion(int versionMajor, int versionMinor) {
113   // We generate a version string from numeric values if available.
114   assert(versionMajor != -1);
115   if (versionMinor == -1) {
116       return smprintf("%d", versionMajor);
117   }
118   return smprintf("%d.%d", versionMajor, versionMinor);
119 }
120 
121 /* The spec does not really require padding the version str but it clears */
122 /* 1.8 vs. 1.008 ambiguities and makes us inline with the AFDKO. */
paddedVersionStr(const char * textVersion,char * buffer)123 char* paddedVersionStr(const char * textVersion, char * buffer) {
124     int major = -1;
125     int minor = -1;
126     extractNumericVersion(textVersion, &major, &minor);
127     if (major < 0 || minor < 0) return (char *) textVersion;
128     snprintf(buffer, 6, "%d.%03d", major, minor);
129     return buffer;
130 }
131 
132 const char * DOS_reserved[12] = {"CON", "PRN", "AUX", "CLOCK$", "NUL", "COM1", "COM2", "COM3", "COM4", "LPT1", "LPT2", "LPT3"};
133 const int DOS_reserved_count = 12;
134 
polyMatch(const char * input,int reference_count,const char ** references)135 int polyMatch(const char * input, int reference_count, const char ** references) {
136   for (off_t pos = 0; pos < reference_count; pos++) {
137     if (strcmp(references[pos], input) == 0) return 1;
138   }
139   return 0;
140 }
141 
is_DOS_drive(char * input)142 int is_DOS_drive(char * input) {
143   if ((input != NULL) &&
144      (strlen(input) == 2) &&
145      (((input[0] >= 'A') && (input[0] <= 'Z')) || ((input[0] >= 'a') && (input[0] <= 'z'))) &&
146      (input[1] == ':'))
147        return 1;
148   return 0;
149 }
150 
ufo_name_mangle(const char * input,const char * prefix,const char * suffix,int flags)151 char * ufo_name_mangle(const char * input, const char * prefix, const char * suffix, int flags) {
152   // This does not append the prefix or the suffix.
153   // flags & 1 determines whether to post-pad caps (something implemented in the standard).
154   // flags & 2 determines whether to replace a leading '.' (standard).
155   // flags & 4 determines whether to restrict DOS names (standard).
156   // flags & 8 determines whether to implement additional character restrictions.
157   // The specification lists '"' '*' '+' '/' ':' '<' '>' '?' '[' '\\' ']' '|'
158   // and also anything in the range 0x00-0x1F and 0x7F.
159   // Our additional restriction list includes '\'' '&' '%' '$' '#' '`' '=' '!' ';'
160   // Standard behavior comes from passing a flags value of 7.
161   const char * standard_restrict = "\"*+/:<>?[]\\]|";
162   const char * extended_restrict = "\'&%$#`=!;";
163   size_t prefix_length = strlen(prefix);
164   size_t max_length = 255 - prefix_length - strlen(suffix);
165   size_t input_length = strlen(input);
166   size_t stop_pos = ((max_length < input_length) ? max_length : input_length); // Minimum.
167   size_t output_length_1 = input_length;
168   if (flags & 1) output_length_1 += count_caps(input); // Add space for underscore pads on caps.
169   off_t output_pos = 0;
170   char * output = malloc(output_length_1 + 1);
171   for (int i = 0; i < input_length; i++) {
172     if (strchr(standard_restrict, input[i]) || (input[i] <= 0x1F) || (input[i] >= 0x7F)) {
173       output[output_pos++] = '_'; // If the character is restricted, place an underscore.
174     } else if ((flags & 8) && strchr(extended_restrict, input[i])) {
175       output[output_pos++] = '_'; // If the extended restriction list is enabled and matches, ....
176     } else if ((flags & 1) && (input[i] >= 'A') && (input[i] <= 'Z')) {
177       output[output_pos++] = input[i];
178       output[output_pos++] = '_'; // If we have a capital letter, we post-pad if desired.
179     } else if ((flags & 2) && (i == 0) && (prefix_length == 0) && (input[i] == '.')) {
180       output[output_pos++] = '_'; // If we have a leading '.', we convert to an underscore.
181     } else {
182       output[output_pos++] = input[i];
183     }
184   }
185   output[output_pos] = '\0';
186   if (output_pos > max_length) {
187     output[max_length] = '\0';
188   }
189   char * output2 = NULL;
190   off_t output2_pos = 0;
191   {
192     char * disposable = malloc(output_length_1 + 1);
193     strcpy(disposable, output); // strtok rewrites the input string, so we make a copy.
194     output2 = malloc((2 * output_length_1) + 1); // It's easier to pad than to calculate.
195     output2_pos = 0;
196     char * saveptr = NULL;
197     char * current = strtok_r(disposable, ".", &saveptr); // We get the first name part.
198     while (current != NULL) {
199       char * uppered = upper_case(output);
200       if (polyMatch(uppered, DOS_reserved_count, DOS_reserved) || is_DOS_drive(uppered)) {
201         output2[output2_pos++] = '_'; // Prefix an underscore if it's a reserved name.
202       }
203       free(uppered); uppered = NULL;
204       for (off_t parti = 0; current[parti] != '\0'; parti++) {
205         output2[output2_pos++] = current[parti];
206       }
207       current = strtok_r(NULL, ".", &saveptr);
208       if (current != NULL) output2[output2_pos++] = '.';
209     }
210     output2[output2_pos] = '\0';
211     output2 = realloc(output2, output2_pos + 1);
212     free(disposable); disposable = NULL;
213   }
214   free(output); output = NULL;
215   return output2;
216 }
217 
ufo_name_number(struct glif_name_index * glif_name_hash,int index,const char * input,const char * prefix,const char * suffix,int flags)218 char * ufo_name_number(
219 struct glif_name_index * glif_name_hash,
220 int index, const char * input, const char * prefix, const char * suffix, int flags) {
221         // This does not append the prefix or the suffix.
222         // The specification deals with name collisions by appending a 15-digit decimal number to the name.
223         // But the name length cannot exceed 255 characters, so it is necessary to crop the base name if it is too long.
224         // Name exclusions are case insensitive, so we uppercase.
225         // flags & 16 forces appending a number.
226         char * name_numbered = upper_case(input);
227         char * full_name_base = same_case(input); // This is in case we do not need a number added.
228         if (strlen(input) > (255 - strlen(prefix) - strlen(suffix))) {
229           // If the numbered name base is too long, we crop it, even if we are not numbering.
230           full_name_base[(255 - strlen(suffix))] = '\0';
231           full_name_base = realloc(full_name_base, ((255 - strlen(prefix) - strlen(suffix)) + 1));
232         }
233         char * name_base = same_case(input); // This is in case we need a number added.
234         long int name_number = 0;
235 
236         if (glif_name_hash != NULL) {
237           if (strlen(input) > (255 - 15 - strlen(prefix) - strlen(suffix))) {
238             // If the numbered name base is too long, we crop it.
239             name_base[(255 - 15 - strlen(suffix))] = '\0';
240             name_base = realloc(name_base, ((255 - 15 - strlen(prefix) - strlen(suffix)) + 1));
241           }
242           int number_once = ((flags & 16) ? 1 : 0);
243           // Check the resulting name against a hash table of names.
244           if (glif_name_search_glif_name(glif_name_hash, name_numbered) != NULL || number_once) {
245             // If the name is taken, we must make space for a 15-digit number.
246             char * name_base_upper = upper_case(name_base);
247             while (glif_name_search_glif_name(glif_name_hash, name_numbered) != NULL || number_once) {
248               name_number++; // Remangle the name until we have no more matches.
249               free(name_numbered); name_numbered = NULL;
250               name_numbered = smprintf("%s%015ld", name_base_upper, name_number);
251               number_once = 0;
252             }
253             free(name_base_upper); name_base_upper = NULL;
254           }
255           // Insert the result into the hash table.
256           glif_name_track_new(glif_name_hash, index, name_numbered);
257         }
258 
259         // Now we want the correct capitalization.
260         free(name_numbered); name_numbered = NULL;
261         if (name_number > 0) {
262           name_numbered = smprintf("%s%015ld", name_base, name_number);
263         } else {
264           name_numbered = smprintf("%s", full_name_base);
265         }
266         free(name_base); name_base = NULL;
267         free(full_name_base); full_name_base = NULL;
268         return name_numbered;
269 }
270 
xmlNewChildInteger(xmlNodePtr parent,xmlNsPtr ns,const xmlChar * name,long int value)271 static xmlNodePtr xmlNewChildInteger(xmlNodePtr parent, xmlNsPtr ns, const xmlChar * name, long int value) {
272   char * valtmp = smprintf("%ld", value);
273   // Textify the value to be enclosed.
274   if (valtmp != NULL) {
275     xmlNodePtr childtmp = xmlNewChild(parent, NULL, BAD_CAST name, BAD_CAST valtmp); // Make a text node for the value.
276     free(valtmp); valtmp = NULL; // Free the temporary text store.
277     return childtmp;
278   }
279   return NULL;
280 }
xmlNewNodeInteger(xmlNsPtr ns,const xmlChar * name,long int value)281 static xmlNodePtr xmlNewNodeInteger(xmlNsPtr ns, const xmlChar * name, long int value) {
282   char * valtmp = smprintf("%ld", value);
283   xmlNodePtr childtmp = xmlNewNode(NULL, BAD_CAST name); // Create a named node.
284   // Textify the value to be enclosed.
285   if (valtmp != NULL) {
286     xmlNodePtr valtmpxml = xmlNewText(BAD_CAST valtmp); // Make a text node for the value.
287     xmlAddChild(childtmp, valtmpxml); // Attach the text node as content of the named node.
288     free(valtmp); valtmp = NULL; // Free the temporary text store.
289   } else {
290     xmlFreeNode(childtmp); childtmp = NULL;
291   }
292   return childtmp;
293 }
xmlNewChildFloat(xmlNodePtr parent,xmlNsPtr ns,const xmlChar * name,double value)294 static xmlNodePtr xmlNewChildFloat(xmlNodePtr parent, xmlNsPtr ns, const xmlChar * name, double value) {
295   char * valtmp = smprintf("%g", value);
296   // Textify the value to be enclosed.
297   if (valtmp != NULL) {
298     xmlNodePtr childtmp = xmlNewChild(parent, NULL, BAD_CAST name, BAD_CAST valtmp); // Make a text node for the value.
299     free(valtmp); valtmp = NULL; // Free the temporary text store.
300     return childtmp;
301   }
302   return NULL;
303 }
xmlNewNodeFloat(xmlNsPtr ns,const xmlChar * name,double value)304 static xmlNodePtr xmlNewNodeFloat(xmlNsPtr ns, const xmlChar * name, double value) {
305   char * valtmp = smprintf("%g", value);
306   xmlNodePtr childtmp = xmlNewNode(NULL, BAD_CAST name); // Create a named node.
307   // Textify the value to be enclosed.
308   if (valtmp != NULL) {
309     xmlNodePtr valtmpxml = xmlNewText(BAD_CAST valtmp); // Make a text node for the value.
310     xmlAddChild(childtmp, valtmpxml); // Attach the text node as content of the named node.
311     free(valtmp); valtmp = NULL; // Free the temporary text store.
312   } else {
313     xmlFreeNode(childtmp); childtmp = NULL;
314   }
315   return NULL;
316 }
xmlNewChildString(xmlNodePtr parent,xmlNsPtr ns,const xmlChar * name,char * value)317 static xmlNodePtr xmlNewChildString(xmlNodePtr parent, xmlNsPtr ns, const xmlChar * name, char * value) {
318   xmlNodePtr childtmp = xmlNewTextChild(parent, ns, BAD_CAST name, BAD_CAST value); // Make a text node for the value.
319   return childtmp;
320 }
xmlNewNodeString(xmlNsPtr ns,const xmlChar * name,char * value)321 static xmlNodePtr xmlNewNodeString(xmlNsPtr ns, const xmlChar * name, char * value) {
322   xmlNodePtr childtmp = xmlNewNode(NULL, BAD_CAST name); // Create a named node.
323   xmlNodePtr valtmpxml = xmlNewText(BAD_CAST value); // Make a text node for the value.
324   xmlAddChild(childtmp, valtmpxml); // Attach the text node as content of the named node.
325   return childtmp;
326 }
xmlNewNodeVPrintf(xmlNsPtr ns,const xmlChar * name,char * format,va_list arguments)327 static xmlNodePtr xmlNewNodeVPrintf(xmlNsPtr ns, const xmlChar * name, char * format, va_list arguments) {
328   char * valtmp = vsmprintf(format, arguments);
329   if (valtmp == NULL) {
330     return NULL;
331   }
332   xmlNodePtr childtmp = xmlNewNode(NULL, BAD_CAST name); // Create a named node.
333   xmlNodePtr valtmpxml = xmlNewText(BAD_CAST valtmp); // Make a text node for the value.
334   xmlAddChild(childtmp, valtmpxml); // Attach the text node as content of the named node.
335   free(valtmp); valtmp = NULL; // Free the temporary text store.
336   return childtmp;
337 }
xmlNewNodePrintf(xmlNsPtr ns,const xmlChar * name,char * format,...)338 static xmlNodePtr xmlNewNodePrintf(xmlNsPtr ns, const xmlChar * name, char * format, ...) {
339   va_list arguments;
340   va_start(arguments, format);
341   xmlNodePtr output = xmlNewNodeVPrintf(ns, name, format, arguments);
342   va_end(arguments);
343   return output;
344 }
xmlNewChildVPrintf(xmlNodePtr parent,xmlNsPtr ns,const xmlChar * name,char * format,va_list arguments)345 static xmlNodePtr xmlNewChildVPrintf(xmlNodePtr parent, xmlNsPtr ns, const xmlChar * name, char * format, va_list arguments) {
346   xmlNodePtr output = xmlNewNodeVPrintf(ns, name, format, arguments);
347   xmlAddChild(parent, output);
348   return output;
349 }
xmlNewChildPrintf(xmlNodePtr parent,xmlNsPtr ns,const xmlChar * name,char * format,...)350 static xmlNodePtr xmlNewChildPrintf(xmlNodePtr parent, xmlNsPtr ns, const xmlChar * name, char * format, ...) {
351   va_list arguments;
352   va_start(arguments, format);
353   xmlNodePtr output = xmlNewChildVPrintf(parent, ns, name, format, arguments);
354   va_end(arguments);
355   return output;
356 }
xmlSetPropVPrintf(xmlNodePtr target,const xmlChar * name,char * format,va_list arguments)357 static void xmlSetPropVPrintf(xmlNodePtr target, const xmlChar * name, char * format, va_list arguments) {
358   char * valtmp = vsmprintf(format, arguments);
359   // Generate the value.
360   if (valtmp == NULL) {
361     return;
362   }
363   xmlSetProp(target, name, valtmp); // Set the property.
364   free(valtmp); valtmp = NULL; // Free the temporary text store.
365   return;
366 }
xmlSetPropPrintf(xmlNodePtr target,const xmlChar * name,char * format,...)367 static void xmlSetPropPrintf(xmlNodePtr target, const xmlChar * name, char * format, ...) {
368   va_list arguments;
369   va_start(arguments, format);
370   xmlSetPropVPrintf(target, name, format, arguments);
371   va_end(arguments);
372   return;
373 }
374 
375 /* ************************************************************************** */
376 /* ****************************    PList Output    ************************** */
377 /* ************************************************************************** */
378 
count_occurrence(const char * big,const char * little)379 int count_occurrence(const char* big, const char* little) {
380     const char * tmp = big;
381     int output = 0;
382     while ((tmp = strstr(tmp, little))) { output ++; tmp ++; }
383     return output;
384 }
385 
normalizeToASCII(char * str)386 static char* normalizeToASCII(char *str) {
387     if ( str!=NULL && !AllAscii(str))
388         return StripToASCII(str);
389     else
390         return copy(str);
391 }
392 
PlistInit()393 xmlDocPtr PlistInit() {
394     // Some of this code is pasted from libxml2 samples.
395     xmlDocPtr doc = NULL;
396     xmlNodePtr root_node = NULL, dict_node = NULL;
397     xmlDtdPtr dtd = NULL;
398 
399     char buff[256];
400     int i, j;
401 
402     LIBXML_TEST_VERSION;
403 
404     doc = xmlNewDoc(BAD_CAST "1.0");
405     dtd = xmlCreateIntSubset(doc, BAD_CAST "plist", BAD_CAST "-//Apple Computer//DTD PLIST 1.0//EN", BAD_CAST "http://www.apple.com/DTDs/PropertyList-1.0.dtd");
406     root_node = xmlNewNode(NULL, BAD_CAST "plist");
407     xmlSetProp(root_node, BAD_CAST "version", BAD_CAST "1.0");
408     xmlDocSetRootElement(doc, root_node);
409     return doc;
410 }
411 
PListAddInteger(xmlNodePtr parent,const char * key,int value)412 static void PListAddInteger(xmlNodePtr parent, const char *key, int value) {
413     xmlNewChild(parent, NULL, BAD_CAST "key", BAD_CAST key);
414     xmlNewChildPrintf(parent, NULL, BAD_CAST "integer", "%d", value);
415 }
416 
PListAddReal(xmlNodePtr parent,const char * key,double value)417 static void PListAddReal(xmlNodePtr parent, const char *key, double value) {
418     xmlNewChild(parent, NULL, BAD_CAST "key", BAD_CAST key);
419     xmlNewChildPrintf(parent, NULL, BAD_CAST "real", "%g", value);
420 }
421 
PListAddIntegerOrReal(xmlNodePtr parent,const char * key,double value)422 static void PListAddIntegerOrReal(xmlNodePtr parent, const char *key, double value) {
423     if (value == floor(value)) PListAddInteger(parent, key, (int)value);
424     else PListAddReal(parent, key, value);
425 }
426 
PListAddBoolean(xmlNodePtr parent,const char * key,int value)427 static void PListAddBoolean(xmlNodePtr parent, const char *key, int value) {
428     xmlNewChild(parent, NULL, BAD_CAST "key", BAD_CAST key);
429     xmlNewChild(parent, NULL, BAD_CAST (value ? "true": "false"), NULL);
430 }
431 
PListAddDate(xmlNodePtr parent,const char * key,time_t timestamp)432 static void PListAddDate(xmlNodePtr parent, const char *key, time_t timestamp) {
433 /* openTypeHeadCreated = string format as \"YYYY/MM/DD HH:MM:SS\".	*/
434 /* \"YYYY/MM/DD\" is year/month/day. The month is in the range 1-12 and	*/
435 /* the day is in the range 1-end of month.				*/
436 /*  \"HH:MM:SS\" is hour:minute:second. The hour is in the range 0:23.	*/
437 /* Minutes and seconds are in the range 0-59.				*/
438     struct tm *tm = gmtime(&timestamp);
439     xmlNewChild(parent, NULL, BAD_CAST "key", BAD_CAST key);
440     xmlNewChildPrintf(parent, NULL, BAD_CAST "string",
441             "%4d/%02d/%02d %02d:%02d:%02d", tm->tm_year+1900, tm->tm_mon+1,
442 	    tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
443 }
444 
PListAddString(xmlNodePtr parent,const char * key,const char * value)445 void PListAddString(xmlNodePtr parent, const char *key, const char *value) {
446     if ( value==NULL ) value = "";
447     xmlNodePtr keynode = xmlNewChild(parent, NULL, BAD_CAST "key", BAD_CAST key); // "<key>%s</key>" key
448 #ifdef ESCAPE_LIBXML_STRINGS
449     size_t main_size = strlen(value) +
450                        (count_occurrence(value, "<") * (strlen("&lt")-strlen("<"))) +
451                        (count_occurrence(value, ">") * (strlen("&gt")-strlen("<"))) +
452                        (count_occurrence(value, "&") * (strlen("&amp")-strlen("<")));
453     char *tmpstring = malloc(main_size + 1); tmpstring[0] = '\0';
454     off_t pos1 = 0;
455     while ( *value ) {
456 	if ( *value=='<' ) {
457 	    strcat(tmpstring, "&lt;");
458             pos1 += strlen("&lt;");
459 	} else if ( *value=='>' ) {
460 	    strcat(tmpstring, "&gt;");
461             pos1 += strlen("&gt;");
462 	} else if ( *value == '&' ) {
463 	    strcat(tmpstring, "&amp;");
464 	    pos1 += strlen("&amp;");
465 	} else {
466 	    tmpstring[pos1++] = *value;
467             tmpstring[pos1] = '\0';
468         }
469 	++value;
470     }
471     xmlNodePtr valnode = xmlNewChild(parent, NULL, BAD_CAST "string", tmpstring); // "<string>%s</string>" tmpstring
472 #else
473     xmlNodePtr valnode = xmlNewTextChild(parent, NULL, BAD_CAST "string", value); // "<string>%s</string>" tmpstring
474 #endif
475 }
476 
PListAddNameString(xmlNodePtr parent,const char * key,const SplineFont * sf,int strid)477 static void PListAddNameString(xmlNodePtr parent, const char *key, const SplineFont *sf, int strid) {
478     char *value=NULL, *nonenglish=NULL, *freeme=NULL;
479     struct ttflangname *nm;
480 
481     for ( nm=sf->names; nm!=NULL; nm=nm->next ) {
482 	if ( nm->names[strid]!=NULL ) {
483 	    nonenglish = nm->names[strid];
484 	    if ( nm->lang == 0x409 ) {
485 		value = nm->names[strid];
486     break;
487 	    }
488 	}
489     }
490     if ( value==NULL && strid==ttf_version && sf->version!=NULL ) {
491 	char versionStr[6];
492 	paddedVersionStr(sf->version, versionStr);
493 	value = freeme = strconcat("Version ", versionStr);
494     }
495     if ( value==NULL && strid==ttf_copyright && sf->copyright!=NULL )
496 	value = sf->copyright;
497     if ( value==NULL )
498 	value=nonenglish;
499     if ( value!=NULL ) {
500 	PListAddString(parent,key,value);
501     }
502     free(freeme);
503 }
504 
PListAddIntArray(xmlNodePtr parent,const char * key,const char * entries,int len)505 static void PListAddIntArray(xmlNodePtr parent, const char *key, const char *entries, int len) {
506     int i;
507     xmlNewChild(parent, NULL, BAD_CAST "key", BAD_CAST key);
508     xmlNodePtr arrayxml = xmlNewChild(parent, NULL, BAD_CAST "array", NULL);
509     for ( i=0; i<len; ++i ) {
510       xmlNewChildInteger(arrayxml, NULL, BAD_CAST "integer", entries[i]);
511     }
512 }
513 
PListAddPrivateArray(xmlNodePtr parent,const char * key,struct psdict * private)514 static void PListAddPrivateArray(xmlNodePtr parent, const char *key, struct psdict *private) {
515     char *value;
516     if ( private==NULL )
517 return;
518     value = PSDictHasEntry(private,key);
519     if ( value==NULL )
520 return;
521     xmlNewChildPrintf(parent, NULL, BAD_CAST "key", "postscript%s", key); // "<key>postscript%s</key>" key
522     xmlNodePtr arrayxml = xmlNewChild(parent, NULL, BAD_CAST "array", NULL); // "<array>"
523     while ( *value==' ' || *value=='[' ) ++value;
524     for (;;) {
525 	int havedot=0;
526 	int skipping=0;
527         size_t tmpsize = 8;
528         char * tmp = malloc(tmpsize);
529         off_t tmppos = 0;
530 	while ( *value!=']' && *value!='\0' && *value!=' ' && tmp!=NULL) {
531             // We now deal with non-integers as necessary.
532             if (*value=='.') {
533               if (havedot) skipping = true;
534               else havedot = 1;
535             }
536 	    if (skipping)
537 		++value;
538 	    else
539                 tmp[tmppos++] = *value++;
540             if (tmppos == tmpsize) { tmpsize *= 2; tmp = realloc(tmp, tmpsize); }
541 	}
542         tmp[tmppos] = '\0';
543         if (tmp != NULL) {
544           if (havedot)
545             xmlNewChildString(arrayxml, NULL, BAD_CAST "real", BAD_CAST tmp); // "<real>%s</real>" tmp
546           else
547             xmlNewChildString(arrayxml, NULL, BAD_CAST "integer", BAD_CAST tmp); // "<integer>%s</integer>" tmp
548           free(tmp); tmp = NULL;
549         }
550 	while ( *value==' ' ) ++value;
551 	if ( *value==']' || *value=='\0' ) break;
552     }
553     // "</array>"
554 }
555 
PListAddPrivateThing(xmlNodePtr parent,const char * key,struct psdict * private,char * type)556 static void PListAddPrivateThing(xmlNodePtr parent, const char *key, struct psdict *private, char *type) {
557     char *value;
558 
559     if ( private==NULL ) return;
560     value = PSDictHasEntry(private,key);
561     if ( value==NULL ) return;
562 
563     while ( *value==' ' || *value=='[' ) ++value;
564 
565     xmlNewChildPrintf(parent, NULL, BAD_CAST "key", "postscript%s", key); // "<key>postscript%s</key>" key
566     while ( *value==' ' || *value=='[' ) ++value;
567     {
568 	int havedot=0;
569 	int skipping=0;
570         size_t tmpsize = 8;
571         char * tmp = malloc(tmpsize);
572         off_t tmppos = 0;
573 	while ( *value!=']' && *value!='\0' && *value!=' ' && tmp!=NULL) {
574             // We now deal with non-integers as necessary.
575             if (*value=='.') {
576               if (havedot) skipping = true;
577               else havedot = 1;
578             }
579 	    if (skipping)
580 		++value;
581 	    else
582                 tmp[tmppos++] = *value++;
583             if (tmppos == tmpsize) { tmpsize *= 2; tmp = realloc(tmp, tmpsize); }
584 	}
585         tmp[tmppos] = '\0';
586         if (tmp != NULL) {
587           if (havedot)
588             xmlNewChildString(parent, NULL, BAD_CAST "real", BAD_CAST tmp); // "<real>%s</real>" tmp
589           else
590             xmlNewChildString(parent, NULL, BAD_CAST "integer", BAD_CAST tmp); // "<integer>%s</integer>" tmp
591           free(tmp); tmp = NULL;
592         }
593 	while ( *value==' ' ) ++value;
594     }
595 }
596 
597 /* ************************************************************************** */
598 /* *************************   Python lib Output    ************************* */
599 /* ************************************************************************** */
600 #ifndef _NO_PYTHON
601 static int PyObjDumpable(PyObject *value, int has_lists);
602 xmlNodePtr PyObjectToXML( PyObject *value, int has_lists );
603 xmlNodePtr PythonDictToXML(PyObject *dict, xmlNodePtr target, const char **exclusions, int has_lists);
604 #endif
605 
stringInStrings(const char * target,const char ** reference)606 int stringInStrings(const char *target, const char **reference) {
607 	if (reference == NULL) return false;
608 	off_t pos;
609 	for (pos = 0; reference[pos] != NULL; pos++)
610 		if (strcmp(target, reference[pos]) == 0)
611 			return true;
612 	return false;
613 }
614 
PythonLibToXML(void * python_persistent,const SplineChar * sc,int has_lists)615 xmlNodePtr PythonLibToXML(void *python_persistent, const SplineChar *sc, int has_lists) {
616     int has_hints = (sc!=NULL && (sc->hstem!=NULL || sc->vstem!=NULL ));
617     xmlNodePtr retval = NULL, dictnode = NULL, keynode = NULL, valnode = NULL;
618     // retval = xmlNewNode(NULL, BAD_CAST "lib"); //     "<lib>"
619     dictnode = xmlNewNode(NULL, BAD_CAST "dict"); //     "  <dict>"
620     if ( has_hints
621 #ifndef _NO_PYTHON
622          || (python_persistent!=NULL && PyMapping_Check((PyObject *)python_persistent))
623 #endif
624        ) {
625 
626         xmlAddChild(retval, dictnode);
627         /* Not officially part of the UFO/glif spec, but used by robofab */
628 	if ( has_hints ) {
629             // Remember that the value of the plist key is in the node that follows it in the dict (not an x.m.l. child).
630             xmlNewChild(dictnode, NULL, BAD_CAST "key", BAD_CAST "com.fontlab.hintData"); // Label the hint data block.
631 	    //                                           "    <key>com.fontlab.hintData</key>\n"
632 	    //                                           "    <dict>"
633             xmlNodePtr hintdict = xmlNewChild(dictnode, NULL, BAD_CAST "dict", NULL);
634             if ( sc != NULL ) {
635 	        if ( sc->hstem!=NULL ) {
636                     StemInfo *h;
637                     xmlNewChild(hintdict, NULL, BAD_CAST "key", BAD_CAST "hhints");
638                     //                                   "      <key>hhints</key>"
639                     //                                   "      <array>"
640                     xmlNodePtr hintarray = xmlNewChild(hintdict, NULL, BAD_CAST "array", NULL);
641                     for ( h = sc->hstem; h!=NULL; h=h->next ) {
642                         char * valtmp = NULL;
643                         xmlNodePtr stemdict = xmlNewChild(hintarray, NULL, BAD_CAST "dict", NULL);
644 		        //                               "        <dict>"
645                         xmlNewChild(stemdict, NULL, BAD_CAST "key", "position");
646 		        //                               "          <key>position</key>"
647                         xmlNewChildInteger(stemdict, NULL, BAD_CAST "integer", (int) rint(h->start));
648 		        //                               "          <integer>%d</integer>\n" ((int) rint(h->start))
649                         xmlNewChild(stemdict, NULL, BAD_CAST "key", "width");
650 		        //                               "          <key>width</key>"
651                         xmlNewChildInteger(stemdict, NULL, BAD_CAST "integer", (int) rint(h->width));
652 		        //                               "          <integer>%d</integer>\n" ((int) rint(h->width))
653 		        //                               "        </dict>\n"
654 		    }
655 		    //                                   "      </array>\n"
656 	        }
657 	        if ( sc->vstem!=NULL ) {
658                     StemInfo *h;
659                     xmlNewChild(hintdict, NULL, BAD_CAST "key", BAD_CAST "vhints");
660                     //                                   "      <key>vhints</key>"
661                     //                                   "      <array>"
662                     xmlNodePtr hintarray = xmlNewChild(hintdict, NULL, BAD_CAST "array", NULL);
663                     for ( h = sc->vstem; h!=NULL; h=h->next ) {
664                         char * valtmp = NULL;
665                         xmlNodePtr stemdict = xmlNewChild(hintarray, NULL, BAD_CAST "dict", NULL);
666                         //                               "        <dict>"
667                         xmlNewChild(stemdict, NULL, BAD_CAST "key", "position");
668                         //                               "          <key>position</key>"
669                         xmlNewChildInteger(stemdict, NULL, BAD_CAST "integer", (int) rint(h->start));
670                         //                               "          <integer>%d</integer>\n" ((int) rint(h->start))
671                         xmlNewChild(stemdict, NULL, BAD_CAST "key", "width");
672                         //                               "          <key>width</key>"
673                         xmlNewChildInteger(stemdict, NULL, BAD_CAST "integer", (int) rint(h->width));
674                         //                               "          <integer>%d</integer>\n" ((int) rint(h->width))
675                         //                               "        </dict>\n"
676                     }
677                     //                                   "      </array>\n"
678 	        }
679 	    }
680 	    //                                           "    </dict>"
681 	}
682 #ifndef _NO_PYTHON
683 	/* Ok, look at the persistent data and output it (all except for a */
684 	/*  hint entry -- we've already handled that with the real hints, */
685 	/*  no point in retaining out of date hints too */
686 	const char *sc_exclusions[] = {"com.fontlab.hintData", NULL};
687 	const char *no_exclusions[] = {NULL};
688 	if ( python_persistent != NULL ) {
689           if (!PyMapping_Check((PyObject *)python_persistent)) fprintf(stderr, "python_persistent is not a mapping.\n");
690           else {
691 		PythonDictToXML((PyObject *)python_persistent, dictnode, (sc ? sc_exclusions : no_exclusions), has_lists);
692 	  }
693 	}
694 #endif
695     }
696     //                                                 "  </dict>"
697     // //                                                 "</lib>"
698     return dictnode;
699 }
700 
701 #ifndef _NO_PYTHON
PyObjDumpable(PyObject * value,int has_lists)702 static int PyObjDumpable(PyObject *value, int has_lists) {
703     if ( PyTuple_Check(value))
704 return( true ); // Note that this means two different things depending upon has_lists.
705     if ( PyList_Check(value))
706 return( true );
707     if ( PyLong_Check(value))
708 return( true );
709     if ( PyFloat_Check(value))
710 return( true );
711 	if ( PyDict_Check(value))
712 return( true );
713     if ( PyBytes_Check(value))
714 return( true );
715     if ( has_lists && PyList_Check(value))
716 return( true );
717     if ( PyMapping_Check(value))
718 return( true );
719     if ( PyBool_Check(value))
720 return( true );
721     if ( value == Py_None )
722 return( true );
723 
724 return( false );
725 }
726 
PyObjectToXML(PyObject * value,int has_lists)727 xmlNodePtr PyObjectToXML( PyObject *value, int has_lists ) {
728     xmlNodePtr childtmp = NULL;
729     xmlNodePtr valtmpxml = NULL;
730     char * valtmp = NULL;
731     if (has_lists && PyTuple_Check(value) && (PyTuple_Size(value) == 3) &&
732        PyBytes_Check(PyTuple_GetItem(value,0)) && PyBytes_Check(PyTuple_GetItem(value,1))) {
733       // This is a chunk of unrecognized data.
734       // Since there's no equivalent in X. M. L., we can use the Tuple for this special case.
735       // But we can only do this if the arrays are being mapped as lists rather than as tuples.
736       // So we miss foreign data in old S. F. D. versions.
737       // childtmp = xmlNewNode(NULL, (xmlChar*)(PyBytes_AsString(PyTuple_GetItem(value,0)))); // Set name.
738       // xmlNodeSetContent(childtmp, (xmlChar*)(PyBytes_AsString(PyTuple_GetItem(value,1)))); // Set contents.
739       xmlDocPtr innerdoc = xmlReadMemory((xmlChar*)(PyBytes_AsString(PyTuple_GetItem(value,1))),
740                                          PyBytes_Size(PyTuple_GetItem(value,1)), "noname.xml", NULL, 0);
741       childtmp = xmlCopyNode(xmlDocGetRootElement(innerdoc), 1);
742       xmlFreeDoc(innerdoc); innerdoc = NULL;
743     } else if (PyDict_Check(value)) {
744       childtmp = PythonLibToXML(value,NULL,has_lists);
745     } else if ( PyMapping_Check(value)) {
746       childtmp = PythonLibToXML(value,NULL,has_lists);
747     } else if ( PyBytes_Check(value)) {		/* Must precede the sequence check */
748       char *str = PyBytes_AsString(value);
749       if (str != NULL) {
750         childtmp = xmlNewNodeString(NULL, BAD_CAST "string", str); // Create a string node.
751           // "<string>%s</string>" str
752       }
753     } else if ( value==Py_True )
754 	childtmp = xmlNewNode(NULL, BAD_CAST "true"); // "<true/>"
755     else if ( value==Py_False )
756         childtmp = xmlNewNode(NULL, BAD_CAST "false"); // "<false/>"
757     else if ( value==Py_None )
758         childtmp = xmlNewNode(NULL, BAD_CAST "none");  // "<none/>"
759     else if (PyLong_Check(value)) {
760         childtmp = xmlNewNodeInteger(NULL, BAD_CAST "integer", PyLong_AsLong(value)); // Create an integer node.
761         // "<integer>%ld</integer>"
762     } else if (PyFloat_Check(value)) {
763         childtmp = xmlNewNode(NULL, BAD_CAST "real");
764         valtmp = smprintf("%g", PyFloat_AsDouble(value));
765         if (valtmp != NULL) {
766           valtmpxml = xmlNewText(BAD_CAST valtmp);
767           xmlAddChild(childtmp, valtmpxml);
768           free(valtmp); valtmp = NULL;
769         } else {
770           xmlFreeNode(childtmp); childtmp = NULL;
771         }
772         // "<real>%g</real>"
773     } else if ((!has_lists && PyTuple_Check(value)) || (has_lists && PyList_Check(value))) {
774         // Note that PyList is an extension of PySequence, so the original PySequence code would work either way.
775         // But we want to be able to detect mismatches.
776 	int i, len = ( has_lists ? PyList_Size(value) : PyTuple_Size(value) );
777         xmlNodePtr itemtmp = NULL;
778         childtmp = xmlNewNode(NULL, BAD_CAST "array");
779         // "<array>"
780 	for ( i=0; i<len; ++i ) {
781 	    PyObject *obj = ( has_lists ? PyList_GetItem(value,i) : PyTuple_GetItem(value,i) );
782 	    if (obj != NULL) {
783 	      if ( PyObjDumpable(obj, has_lists)) {
784 	        itemtmp = PyObjectToXML(obj, has_lists);
785                 xmlAddChild(childtmp, itemtmp);
786 	      }
787               obj = NULL; // PyTuple_GetItem and PyList_GetItem both return borrowed references.
788 	    }
789 	}
790         // "</array>"
791     }
792     return childtmp;
793 }
794 
PythonDictToXML(PyObject * dict,xmlNodePtr target,const char ** exclusions,int has_lists)795 xmlNodePtr PythonDictToXML(PyObject *dict, xmlNodePtr target, const char **exclusions, int has_lists) {
796 	// dict is the Python dictionary from which to extract data.
797 	// target is the xml node to which to add the contents of that dict.
798 	// exclusions is a NULL-terminated array of strings naming keys to exclude.
799 	PyObject *items, *key, *value;
800 	int i, len;
801 	char *str;
802 	items = PyMapping_Items(dict);
803 	len = PySequence_Size(items);
804 	for ( i=0; i<len; ++i ) {
805 		// According to the Python reference manual,
806 		// PySequence_GetItem returns a reference that we must release,
807 		// but PyTuple_GetItem returns a borrowed reference.
808 		PyObject *item = PySequence_GetItem(items,i);
809 		key = PyTuple_GetItem(item,0);
810 		if ( !PyBytes_Check(key))		/* Keys need not be strings */
811 			{ Py_DECREF(item); item = NULL; continue; }
812 		str = PyBytes_AsString(key);
813 		if ( !str || (stringInStrings(str, exclusions)) )	/* Already done */ // TODO: Fix this!
814 			{ Py_DECREF(item); item = NULL; continue; }
815 		value = PyTuple_GetItem(item,1);
816 		if ( !value || !PyObjDumpable(value, has_lists))
817 			{ Py_DECREF(item); item = NULL; continue; }
818 		// "<key>%s</key>" str
819 		xmlNewChild(target, NULL, BAD_CAST "key", str);
820 		xmlNodePtr tmpNode = PyObjectToXML(value, has_lists);
821 		xmlAddChild(target, tmpNode);
822 		// "<...>...</...>"
823 		Py_DECREF(item); item = NULL;
824 	}
825 	return target;
826 }
827 #endif
828 
829 /* ************************************************************************** */
830 /* ****************************   GLIF Output    **************************** */
831 /* ************************************************************************** */
832 
refcomp(const void * _r1,const void * _r2)833 static int refcomp(const void *_r1, const void *_r2) {
834     const RefChar *ref1 = *(RefChar * const *)_r1;
835     const RefChar *ref2 = *(RefChar * const *)_r2;
836 return( strcmp( ref1->sc->name, ref2->sc->name) );
837 }
838 
_GlifToXML(const SplineChar * sc,int layer,int version)839 xmlNodePtr _GlifToXML(const SplineChar *sc, int layer, int version) {
840     if (layer > sc->layer_cnt) return NULL;
841     const struct altuni *altuni;
842     int isquad = sc->layers[layer].order2;
843     const SplineSet *spl;
844     const SplinePoint *sp;
845     const AnchorPoint *ap;
846     const RefChar *ref;
847     int err;
848     char * stringtmp = NULL;
849     char numstring[32];
850     memset(numstring, 0, sizeof(numstring));
851 
852     // "<?xml version=\"1.0\" encoding=\"UTF-8\""
853     /* No DTD for these guys??? */
854     // Is there a DTD for glif data? (asks Frank)
855     // Perhaps we need to make one.
856 
857     xmlNodePtr topglyphxml = xmlNewNode(NULL, BAD_CAST "glyph"); // Create the glyph node.
858     xmlSetProp(topglyphxml, "name", sc->name); // Set the name for the glyph.
859     char *formatString = "1";
860     // If UFO is version 1 or 2, use "1" for format. Otherwise, use "2".
861     if (version >= 3) formatString = "2";
862     xmlSetProp(topglyphxml, "format", formatString); // Set the format of the glyph.
863     // "<glyph name=\"%s\" format=\"1\">" sc->name
864 
865     xmlNodePtr tmpxml2 = xmlNewChild(topglyphxml, NULL, BAD_CAST "advance", NULL); // Create the advance node.
866     xmlSetPropPrintf(tmpxml2, BAD_CAST "width", "%d", sc->width);
867     if ( sc->parent->hasvmetrics ) {
868       stringtmp = smprintf("%d", sc->width);
869       if (stringtmp != NULL) {
870         xmlSetProp(tmpxml2, BAD_CAST "height", stringtmp);
871         free(stringtmp); stringtmp = NULL;
872       }
873     }
874     // "<advance width=\"%d\" height=\"%d\"/>" sc->width sc->vwidth
875 
876     if ( sc->unicodeenc!=-1 ) {
877       xmlNodePtr unicodexml = xmlNewChild(topglyphxml, NULL, BAD_CAST "unicode", NULL);
878       xmlSetPropPrintf(unicodexml, BAD_CAST "hex", "%04X", sc->unicodeenc);
879     }
880     // "<unicode hex=\"%04X\"/>\n" sc->unicodeenc
881 
882     for ( altuni = sc->altuni; altuni!=NULL; altuni = altuni->next )
883 	if ( altuni->vs==-1 && altuni->fid==0 ) {
884           xmlNodePtr unicodexml = xmlNewChild(topglyphxml, NULL, BAD_CAST "unicode", NULL);
885           xmlSetPropPrintf(unicodexml, BAD_CAST "hex", "%04X", altuni->unienc);
886         }
887         // "<unicode hex=\"%04X\"/>" altuni->unienc
888 
889 	if (version >= 3) {
890 		// Handle the guidelines.
891 		GuidelineSet *gl;
892 		for ( gl=sc->layers[layer].guidelines; gl != NULL; gl = gl->next ) {
893 		    xmlNodePtr guidelinexml = xmlNewChild(topglyphxml, NULL, BAD_CAST "guideline", NULL);
894 		    // gl->flags & 0x10 indicates whether to use the abbreviated format in the UFO specification if possible.
895 		    if (fmod(gl->angle, 180) || !(gl->flags & 0x10))
896 		        xmlSetPropPrintf(guidelinexml, BAD_CAST "x", "%g", gl->point.x);
897 		    if (fmod(gl->angle + 90, 180) || !(gl->flags & 0x10))
898 		        xmlSetPropPrintf(guidelinexml, BAD_CAST "y", "%g", gl->point.y);
899 		    if (fmod(gl->angle, 90) || !(gl->flags & 0x10))
900 		        xmlSetPropPrintf(guidelinexml, BAD_CAST "angle", "%g", fmod(gl->angle + 360, 360));
901 		    if (gl->name != NULL)
902 		        xmlSetPropPrintf(guidelinexml, BAD_CAST "name", "%s", gl->name);
903 		    if (gl->flags & 0x20) // color is set. Repack RGBA from a uint32 to a string with 0-1 scaled values comma-joined.
904 		        xmlSetPropPrintf(guidelinexml, BAD_CAST "color", "%g,%g,%g,%g",
905 		        (((double)((gl->color >> 24) & 0xFF)) / 255),
906 		        (((double)((gl->color >> 16) & 0xFF)) / 255),
907 		        (((double)((gl->color >> 8) & 0xFF)) / 255),
908 		        (((double)((gl->color >> 0) & 0xFF)) / 255)
909 		        );
910 		    if (gl->identifier != NULL)
911 		        xmlSetPropPrintf(guidelinexml, BAD_CAST "identifier", "%s", gl->identifier);
912 		    // "<guideline/>\n"
913 
914 		}
915 		// Handle the anchors. Put global anchors only in the foreground layer.
916 		if (layer == ly_fore)
917 			for ( ap=sc->anchor; ap!=NULL; ap=ap->next ) {
918 			    int ismark = (ap->type==at_mark || ap->type==at_centry);
919 			    xmlNodePtr anchorxml = xmlNewChild(topglyphxml, NULL, BAD_CAST "anchor", NULL);
920 			    xmlSetPropPrintf(anchorxml, BAD_CAST "x", "%g", ap->me.x);
921 			    xmlSetPropPrintf(anchorxml, BAD_CAST "y", "%g", ap->me.y);
922 			    xmlSetPropPrintf(anchorxml, BAD_CAST "name", "%s%s", ismark ? "_" : "", ap->anchor->name);
923 			    // "<anchor x=\"%g\" y=\"%g\" name=\"%s%s\"/>" ap->me.x ap->me.y ap->anchor->name
924 			}
925 	}
926     if (sc->comment)
927 	xmlNewChild(topglyphxml, NULL, BAD_CAST "note", BAD_CAST sc->comment);
928     if ( sc->layers[layer].refs!=NULL || sc->layers[layer].splines!=NULL ) {
929       xmlNodePtr outlinexml = xmlNewChild(topglyphxml, NULL, BAD_CAST "outline", NULL);
930 	// "<outline>"
931 	// Distinguish UFO 3 from UFO 2.
932 	if (version < 3) {
933 		if (layer == ly_fore)
934 			for ( ap=sc->anchor; ap!=NULL; ap=ap->next ) {
935 			    int ismark = (ap->type==at_mark || ap->type==at_centry);
936 			    xmlNodePtr contourxml = xmlNewChild(outlinexml, NULL, BAD_CAST "contour", NULL);
937 			    // "<contour>"
938 			    xmlNodePtr pointxml = xmlNewChild(contourxml, NULL, BAD_CAST "point", NULL);
939 			    xmlSetPropPrintf(pointxml, BAD_CAST "x", "%g", ap->me.x);
940 			    xmlSetPropPrintf(pointxml, BAD_CAST "y", "%g", ap->me.y);
941 			    xmlSetPropPrintf(pointxml, BAD_CAST "type", "move");
942 			    xmlSetPropPrintf(pointxml, BAD_CAST "name", "%s%s", ismark ? "_" : "", ap->anchor->name);
943 			    // "<point x=\"%g\" y=\"%g\" type=\"move\" name=\"%s%s\"/>" ap->me.x ap->me.y (ismark ? "_" : "") ap->anchor->name
944 			    // "</contour>"
945 			}
946 	}
947 	for ( spl=sc->layers[layer].splines; spl!=NULL; spl=spl->next ) {
948             xmlNodePtr contourxml = xmlNewChild(outlinexml, NULL, BAD_CAST "contour", NULL);
949 	    // "<contour>"
950 	    // We write any leading control points.
951 	    if (spl->start_offset == -2) {
952 		if (spl->first && spl->first->prev && spl->first->prev->from && !spl->first->prev->from->nonextcp && !spl->first->prev->order2) {
953                           xmlNodePtr pointxml = xmlNewChild(contourxml, NULL, BAD_CAST "point", NULL);
954                           xmlSetPropPrintf(pointxml, BAD_CAST "x", "%g", (double)spl->first->prev->from->nextcp.x);
955                           xmlSetPropPrintf(pointxml, BAD_CAST "y", "%g", (double)spl->first->prev->from->nextcp.y);
956                           // "<point x=\"%g\" y=\"%g\"/>\n" (double)sp->prevcp.x (double)sp->prevcp.y
957 		}
958 	    }
959 	    if (spl->start_offset <= -1) {
960 		if (spl->first && !spl->first->noprevcp) {
961                           xmlNodePtr pointxml = xmlNewChild(contourxml, NULL, BAD_CAST "point", NULL);
962                           xmlSetPropPrintf(pointxml, BAD_CAST "x", "%g", (double)spl->first->prevcp.x);
963                           xmlSetPropPrintf(pointxml, BAD_CAST "y", "%g", (double)spl->first->prevcp.y);
964                           // "<point x=\"%g\" y=\"%g\"/>\n" (double)sp->prevcp.x (double)sp->prevcp.y
965 		}
966 	    }
967 	    for ( sp=spl->first; sp!=NULL; ) {
968 		/* Undocumented fact: If a contour contains a series of off-curve points with no on-curve then treat as quadratic even if no qcurve */
969 		// We write the next on-curve point.
970 		if (!isquad || sp->ttfindex != 0xffff || !SPInterpolate(sp) || sp->pointtype!=pt_curve || sp->name != NULL) {
971 		  xmlNodePtr pointxml = xmlNewChild(contourxml, NULL, BAD_CAST "point", NULL);
972 		  xmlSetPropPrintf(pointxml, BAD_CAST "x", "%g", (double)sp->me.x);
973 		  xmlSetPropPrintf(pointxml, BAD_CAST "y", "%g", (double)sp->me.y);
974 		  xmlSetPropPrintf(pointxml, BAD_CAST "type", BAD_CAST (
975 		  sp->prev==NULL        ? "move"   :
976 					sp->noprevcp ? "line"   :
977 					isquad 		      ? "qcurve" :
978 					"curve"));
979 		  if (sp->pointtype != pt_corner) xmlSetProp(pointxml, BAD_CAST "smooth", BAD_CAST "yes");
980 		  if (sp->name !=NULL) xmlSetProp(pointxml, BAD_CAST "name", BAD_CAST sp->name);
981                   // "<point x=\"%g\" y=\"%g\" type=\"%s\"%s%s%s%s/>\n" (double)sp->me.x (double)sp->me.y
982 		  // (sp->prev==NULL ? "move" : sp->prev->knownlinear ? "line" : isquad ? "qcurve" : "curve")
983 		  // (sp->pointtype!=pt_corner?" smooth=\"yes\"":"")
984 		  // (sp->name?" name=\"":"") (sp->name?sp->name:"") (sp->name?"\"":"")
985 		}
986 		if ( sp->next==NULL )
987 	    	  break;
988 		// We write control points.
989 		// The conditionals regarding the start offset avoid duplicating points previously written.
990 		if (sp && !sp->nonextcp && sp->next && (sp->next->to != spl->first || spl->start_offset > -2) && sp->next && !sp->next->order2) {
991                           xmlNodePtr pointxml = xmlNewChild(contourxml, NULL, BAD_CAST "point", NULL);
992                           xmlSetPropPrintf(pointxml, BAD_CAST "x", "%g", (double)sp->nextcp.x);
993                           xmlSetPropPrintf(pointxml, BAD_CAST "y", "%g", (double)sp->nextcp.y);
994 		    	  // "<point x=\"%g\" y=\"%g\"/>\n" (double)sp->nextcp.x (double)sp->nextcp.y
995 		}
996 		sp = sp->next->to;
997 		if (sp && !sp->noprevcp && (sp != spl->first || spl->start_offset > -1)) {
998                           xmlNodePtr pointxml = xmlNewChild(contourxml, NULL, BAD_CAST "point", NULL);
999                           xmlSetPropPrintf(pointxml, BAD_CAST "x", "%g", (double)sp->prevcp.x);
1000                           xmlSetPropPrintf(pointxml, BAD_CAST "y", "%g", (double)sp->prevcp.y);
1001                           // "<point x=\"%g\" y=\"%g\"/>\n" (double)sp->prevcp.x (double)sp->prevcp.y
1002 		}
1003 		if ( sp==spl->first )
1004 	    		break;
1005 	    }
1006 	    // "</contour>"
1007 	}
1008 	/* RoboFab outputs components in alphabetic (case sensitive) order. */
1009 	/* Somebody asked George to do that too (as in the disabled code below). */
1010 	/* But it seems important to leave the ordering as it is. */
1011 	/* And David Raymond advises that tampering with the ordering can break things. */
1012 	if ( sc->layers[layer].refs!=NULL ) {
1013 	    const RefChar **refs;
1014 	    int i, cnt;
1015 	    for ( cnt=0, ref = sc->layers[layer].refs; ref!=NULL; ref=ref->next ) if ((SCWorthOutputting(ref->sc) || SCHasData(ref->sc) || ref->sc->glif_name != NULL))
1016 		++cnt;
1017 	    refs = malloc(cnt*sizeof(RefChar *));
1018 	    for ( cnt=0, ref = sc->layers[layer].refs; ref!=NULL; ref=ref->next ) if ((SCWorthOutputting(ref->sc) || SCHasData(ref->sc) || ref->sc->glif_name != NULL))
1019 		refs[cnt++] = ref;
1020 	    // It seems that sorting these breaks something.
1021 #if 0
1022 	    if ( cnt>1 )
1023 		qsort(refs,cnt,sizeof(RefChar *),refcomp);
1024 #endif // 0
1025 	    for ( i=0; i<cnt; ++i ) {
1026 		ref = refs[i];
1027     xmlNodePtr componentxml = xmlNewChild(outlinexml, NULL, BAD_CAST "component", NULL);
1028     xmlSetPropPrintf(componentxml, BAD_CAST "base", "%s", ref->sc->name);
1029 		// "<component base=\"%s\"" ref->sc->name
1030     char *floattmp = NULL;
1031 		if ( ref->transform[0]!=1 ) {
1032                   xmlSetPropPrintf(componentxml, BAD_CAST "xScale", "%g", (double) ref->transform[0]);
1033 		    // "xScale=\"%g\"" (double)ref->transform[0]
1034                 }
1035 		if ( ref->transform[3]!=1 ) {
1036                   xmlSetPropPrintf(componentxml, BAD_CAST "yScale", "%g", (double) ref->transform[3]);
1037 		    // "yScale=\"%g\"" (double)ref->transform[3]
1038                 }
1039 		if ( ref->transform[1]!=0 ) {
1040                   xmlSetPropPrintf(componentxml, BAD_CAST "xyScale", "%g", (double) ref->transform[1]);
1041 		    // "xyScale=\"%g\"" (double)ref->transform[1]
1042                 }
1043 		if ( ref->transform[2]!=0 ) {
1044                   xmlSetPropPrintf(componentxml, BAD_CAST "yxScale", "%g", (double) ref->transform[2]);
1045 		    // "yxScale=\"%g\"" (double)ref->transform[2]
1046                 }
1047 		if ( ref->transform[4]!=0 ) {
1048                   xmlSetPropPrintf(componentxml, BAD_CAST "xOffset", "%g", (double) ref->transform[4]);
1049 		    // "xOffset=\"%g\"" (double)ref->transform[4]
1050                 }
1051 		if ( ref->transform[5]!=0 ) {
1052                   xmlSetPropPrintf(componentxml, BAD_CAST "yOffset", "%g", (double) ref->transform[5]);
1053 		    // "yOffset=\"%g\"" (double)ref->transform[5]
1054                 }
1055 		// "/>"
1056 	    }
1057 	    free(refs);
1058 	}
1059 
1060 	// "</outline>"
1061     }
1062     if (sc->layers[layer].python_persistent != NULL || (layer == ly_fore && (sc->hstem!=NULL || sc->vstem!=NULL ))) {
1063       // If the layer has lib data or if this is the foreground and the glyph has hints, we output lib data.
1064       xmlNodePtr libxml = xmlNewChild(topglyphxml, NULL, BAD_CAST "lib", NULL);
1065       xmlNodePtr pythonblob = PythonLibToXML(sc->layers[layer].python_persistent, (layer == ly_fore ? sc : NULL), sc->layers[layer].python_persistent_has_lists);
1066       xmlAddChild(libxml, pythonblob);
1067     }
1068     return topglyphxml;
1069 }
1070 
GlifDump(const char * glyphdir,const char * gfname,const SplineChar * sc,int layer,int version)1071 static int GlifDump(const char *glyphdir, const char *gfname, const SplineChar *sc, int layer, int version) {
1072     char *gn = buildname(glyphdir,gfname);
1073     xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
1074     if (doc == NULL) {
1075         free(gn);
1076         return 0;
1077     }
1078     xmlNodePtr root_node = _GlifToXML(sc, layer, version);
1079     if (root_node == NULL) {xmlFreeDoc(doc); doc = NULL; free(gn); return 0;}
1080     xmlDocSetRootElement(doc, root_node);
1081     int ret = (xmlSaveFormatFileEnc(gn, doc, "UTF-8", 1) != -1);
1082     xmlFreeDoc(doc); doc = NULL;
1083     free(gn);
1084     return ret;
1085 }
1086 
_ExportGlif(FILE * glif,SplineChar * sc,int layer,int version)1087 int _ExportGlif(FILE *glif,SplineChar *sc, int layer, int version) {
1088     xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
1089     xmlNodePtr root_node = _GlifToXML(sc, layer, version);
1090     xmlDocSetRootElement(doc, root_node);
1091     int output_status = xmlDocFormatDump(glif, doc, 1);
1092     xmlFreeDoc(doc); doc = NULL;
1093     return ( output_status != -1 );
1094 }
1095 
1096 /* ************************************************************************** */
1097 /* ****************************    UFO Output    **************************** */
1098 /* ************************************************************************** */
1099 
clear_cached_ufo_paths(SplineFont * sf)1100 void clear_cached_ufo_paths(SplineFont * sf) {
1101   // We cache the glif names and the layer paths.
1102   // This is helpful for preserving the structure of a U. F. O. to be edited.
1103   // But it may be desirable to purge that data on final output for consistency.
1104   // This function does that.
1105   int i;
1106   // First we clear the glif names.
1107   for (i = 0; i < sf->glyphcnt; i++) {
1108     struct splinechar * sc = sf->glyphs[i];
1109     if (sc->glif_name != NULL) { free(sc->glif_name); sc->glif_name = NULL; }
1110   }
1111   // Then we clear the layer names.
1112   for (i = 0; i < sf->layer_cnt; i++) {
1113     struct layerinfo * ly = &(sf->layers[i]);
1114     if (ly->ufo_path != NULL) { free(ly->ufo_path); ly->ufo_path = NULL; }
1115   }
1116 }
1117 
clear_cached_ufo_point_starts(SplineFont * sf)1118 void clear_cached_ufo_point_starts(SplineFont * sf) {
1119   // We store the offset from the leading spline point at which to start output
1120   // so as to be able to start curves on control points as some incoming U. F. O. files do.
1121   // But we may want to clear these sometimes.
1122   int splinechar_index;
1123   for (splinechar_index = 0; splinechar_index < sf->glyphcnt; splinechar_index ++) {
1124     struct splinechar *sc = sf->glyphs[splinechar_index];
1125     if (sc != NULL) {
1126       int layer_index;
1127       for (layer_index = 0; layer_index < sc->layer_cnt; layer_index ++) {
1128         // We look at the actual shapes for this layer.
1129         {
1130           struct splinepointlist *spl;
1131           for (spl = sc->layers[layer_index].splines; spl != NULL; spl = spl->next) {
1132             spl->start_offset = 0;
1133           }
1134         }
1135         // And then we go hunting for shapes in the refchars.
1136         {
1137           struct refchar *rc;
1138           for (rc = sc->layers[layer_index].refs; rc != NULL; rc = rc->next) {
1139             int reflayer_index;
1140             for (reflayer_index = 0; reflayer_index < rc->layer_cnt; reflayer_index ++) {
1141               struct splinepointlist *spl;
1142               for (spl = rc->layers[reflayer_index].splines; spl != NULL; spl = spl->next) {
1143                 spl->start_offset = 0;
1144               }
1145             }
1146           }
1147         }
1148       }
1149     }
1150   }
1151   // The SplineFont also has a grid.
1152   {
1153     struct splinepointlist *spl;
1154     for (spl = sf->grid.splines; spl != NULL; spl = spl->next) {
1155       spl->start_offset = 0;
1156     }
1157   }
1158 }
1159 
fetchTTFAttribute(const SplineFont * sf,int strid)1160 static char* fetchTTFAttribute(const SplineFont *sf, int strid) {
1161     char *value=NULL, *nonenglish=NULL;
1162     struct ttflangname *nm;
1163 
1164     for ( nm=sf->names; nm!=NULL; nm=nm->next ) {
1165 		if ( nm->names[strid]!=NULL ) {
1166 	    	nonenglish = nm->names[strid];
1167 	    	if ( nm->lang == 0x409 ) {
1168 				value = nm->names[strid];
1169     			break;
1170 	    	}
1171 		}
1172     }
1173     if ( value==NULL ) value=nonenglish;
1174     return value;
1175 }
1176 
UFOOutputMetaInfo(const char * basedir,SplineFont * sf,int version)1177 static int UFOOutputMetaInfo(const char *basedir, SplineFont *sf, int version) {
1178     xmlDocPtr plistdoc = PlistInit(); if (plistdoc == NULL) return false; // Make the document.
1179     xmlNodePtr rootnode = xmlDocGetRootElement(plistdoc); if (rootnode == NULL) { xmlFreeDoc(plistdoc); return false; } // Find the root node.
1180     xmlNodePtr dictnode = xmlNewChild(rootnode, NULL, BAD_CAST "dict", NULL); if (dictnode == NULL) { xmlFreeDoc(plistdoc); return false; } // Add the dict.
1181     PListAddString(dictnode,"creator","net.GitHub.FontForge");
1182     PListAddInteger(dictnode,"formatVersion", version);
1183     char *fname = buildname(basedir, "metainfo.plist"); // Build the file name.
1184     xmlSaveFormatFileEnc(fname, plistdoc, "UTF-8", 1); // Store the document.
1185     free(fname); fname = NULL;
1186     xmlFreeDoc(plistdoc); // Free the memory.
1187     xmlCleanupParser();
1188     return true;
1189 }
1190 
PointsDistance(BasePoint p0,BasePoint p1)1191 static real PointsDistance(BasePoint p0, BasePoint p1) {
1192 	return sqrt(pow((p1.x - p0.x), 2) + pow((p1.y - p0.y), 2));
1193 }
1194 
AngleFromPoints(BasePoint p0,BasePoint p1)1195 static real AngleFromPoints(BasePoint p0, BasePoint p1) {
1196 	return atan2(p1.y - p0.y, p1.x - p0.x);
1197 }
1198 
SplineToGuideline(SplineFont * sf,SplineSet * ss)1199 static GuidelineSet *SplineToGuideline(SplineFont *sf, SplineSet *ss) {
1200 	SplinePoint *sp1, *sp2;
1201 	if (ss == NULL) return NULL;
1202 	if (ss->first == NULL || ss->last == NULL || ss->first == ss->last)
1203 	return NULL;
1204 	GuidelineSet *gl = chunkalloc(sizeof(GuidelineSet));
1205 	gl->point.x = (ss->first->me.x + ss->last->me.x) / 2;
1206 	gl->point.y = (ss->first->me.y + ss->last->me.y) / 2;
1207 	real angle_radians = AngleFromPoints(ss->first->me, ss->last->me);
1208 	real angle_degrees = 180.0*angle_radians/acos(-1);
1209 	gl->angle = fmod(angle_degrees, 360);
1210 	if (ss->first->name != NULL)
1211 		gl->name = copy(ss->first->name);
1212 	return gl;
1213 }
1214 
UFOOutputFontInfo(const char * basedir,SplineFont * sf,int layer,int version)1215 static int UFOOutputFontInfo(const char *basedir, SplineFont *sf, int layer, int version) {
1216     xmlDocPtr plistdoc = PlistInit(); if (plistdoc == NULL) return false; // Make the document.
1217     xmlNodePtr rootnode = xmlDocGetRootElement(plistdoc); if (rootnode == NULL) { xmlFreeDoc(plistdoc); return false; } // Find the root node.
1218     xmlNodePtr dictnode = xmlNewChild(rootnode, NULL, BAD_CAST "dict", NULL); if (dictnode == NULL) { xmlFreeDoc(plistdoc); return false; } // Add the dict.
1219 
1220     DBounds bb;
1221     double test;
1222 
1223 /* Same keys in both formats */
1224     PListAddString(dictnode,"familyName",sf->familyname_with_timestamp ? sf->familyname_with_timestamp : sf->familyname);
1225     const char *styleNameSynthetic = NULL;
1226     if (sf->fontname) {
1227         styleNameSynthetic = SFGetModifiers(sf);
1228 	char *lastdash = strrchr(sf->fontname, '-');
1229 	if (lastdash && strlen(lastdash) > 2)
1230 	    styleNameSynthetic = lastdash + 1;
1231     }
1232     if (styleNameSynthetic)
1233 	    PListAddString(dictnode,"styleName",styleNameSynthetic);
1234     {
1235         char* preferredFamilyName = fetchTTFAttribute(sf,ttf_preffamilyname);
1236         char* preferredSubfamilyName = fetchTTFAttribute(sf,ttf_prefmodifiers);
1237         char* styleMapFamily = NULL;
1238         if (sf->styleMapFamilyName != NULL) {
1239             /* Empty styleMapStyleName means we imported a UFO that does not have this field. Bypass the fallback. */
1240             if (sf->styleMapFamilyName[0]!='\0')
1241                 styleMapFamily = sf->styleMapFamilyName;
1242         } else if (preferredFamilyName != NULL && preferredSubfamilyName != NULL) {
1243             styleMapFamily = malloc(strlen(preferredFamilyName)+strlen(preferredSubfamilyName)+2);
1244             strcpy(styleMapFamily, preferredFamilyName);
1245             strcat(styleMapFamily, " ");
1246             strcat(styleMapFamily, preferredSubfamilyName);
1247         } else if (sf->fullname != NULL) styleMapFamily = sf->fullname;
1248         if (styleMapFamily != NULL) PListAddString(dictnode,"styleMapFamilyName", styleMapFamily);
1249     }
1250     {
1251         char* styleMapName = NULL;
1252         if (sf->pfminfo.stylemap != -1) {
1253             if (sf->pfminfo.stylemap == 0x21) styleMapName = "bold italic";
1254             else if (sf->pfminfo.stylemap == 0x20) styleMapName = "bold";
1255             else if (sf->pfminfo.stylemap == 0x01) styleMapName = "italic";
1256             else if (sf->pfminfo.stylemap == 0x40) styleMapName = "regular";
1257         } else {
1258             /* Figure out styleMapStyleName automatically. */
1259             if (sf->pfminfo.weight == 700 && sf->italicangle < 0) styleMapName = "bold italic";
1260             else if (sf->italicangle < 0) styleMapName = "italic";
1261             else if (sf->pfminfo.weight == 700) styleMapName = "bold";
1262             else if (sf->pfminfo.weight == 400) styleMapName = "regular";
1263         }
1264         if (styleMapName != NULL) PListAddString(dictnode,"styleMapStyleName", styleMapName);
1265     }
1266     {
1267       // We attempt to get numeric major and minor versions for U. F. O. out of the FontForge version string.
1268       int versionMajor = -1;
1269       int versionMinor = -1;
1270       if (sf->version != NULL) extractNumericVersion(sf->version, &versionMajor, &versionMinor);
1271       if (versionMajor >= 0) PListAddInteger(dictnode,"versionMajor", versionMajor);
1272       if (versionMinor >= 0) PListAddInteger(dictnode,"versionMinor", versionMinor);
1273     }
1274     PListAddNameString(dictnode,"copyright",sf,ttf_copyright);
1275     PListAddNameString(dictnode,"trademark",sf,ttf_trademark);
1276     PListAddInteger(dictnode,"unitsPerEm",sf->ascent+sf->descent);
1277 // We decided that it would be more helpful to round-trip the U. F. O. data.
1278 #if 0
1279     test = SFXHeight(sf,layer,true);
1280     if ( test>0 )
1281 	PListAddInteger(dictnode,"xHeight",(int) rint(test));
1282     test = SFCapHeight(sf,layer,true);
1283     if ( test>0 )
1284 	PListAddInteger(dictnode,"capHeight",(int) rint(test));
1285 #else
1286     if (sf->pfminfo.os2_capheight) PListAddInteger(dictnode,"capHeight",sf->pfminfo.os2_capheight);
1287     if (sf->pfminfo.os2_xheight) PListAddInteger(dictnode,"xHeight",sf->pfminfo.os2_xheight);
1288 #endif // 0
1289     if ( sf->invalidem ) {
1290 	PListAddIntegerOrReal(dictnode,"ascender",sf->ufo_ascent);
1291 	PListAddIntegerOrReal(dictnode,"descender",sf->ufo_descent);
1292     } else {
1293 	PListAddIntegerOrReal(dictnode,"ascender",sf->ascent);
1294 	PListAddIntegerOrReal(dictnode,"descender",-sf->descent);
1295     }
1296     PListAddReal(dictnode,"italicAngle",sf->italicangle);
1297     if (sf->comments) PListAddString(dictnode,"note",sf->comments);
1298     PListAddDate(dictnode,"openTypeHeadCreated",sf->creationtime);
1299     SplineFontFindBounds(sf,&bb);
1300 
1301     if ( sf->pfminfo.hheadascent_add )
1302 	PListAddInteger(dictnode,"openTypeHheaAscender",bb.maxy+sf->pfminfo.hhead_ascent);
1303     else
1304 	PListAddInteger(dictnode,"openTypeHheaAscender",sf->pfminfo.hhead_ascent);
1305     if ( sf->pfminfo.hheaddescent_add )
1306 	PListAddInteger(dictnode,"openTypeHheaDescender",bb.miny+sf->pfminfo.hhead_descent);
1307     else
1308 	PListAddInteger(dictnode,"openTypeHheaDescender",sf->pfminfo.hhead_descent);
1309     PListAddInteger(dictnode,"openTypeHheaLineGap",sf->pfminfo.linegap);
1310 
1311     PListAddNameString(dictnode,"openTypeNameDesigner",sf,ttf_designer);
1312     PListAddNameString(dictnode,"openTypeNameDesignerURL",sf,ttf_designerurl);
1313     PListAddNameString(dictnode,"openTypeNameManufacturer",sf,ttf_manufacturer);
1314     PListAddNameString(dictnode,"openTypeNameManufacturerURL",sf,ttf_venderurl);
1315     PListAddNameString(dictnode,"openTypeNameLicense",sf,ttf_license);
1316     PListAddNameString(dictnode,"openTypeNameLicenseURL",sf,ttf_licenseurl);
1317     PListAddNameString(dictnode,"openTypeNameVersion",sf,ttf_version);
1318     PListAddNameString(dictnode,"openTypeNameUniqueID",sf,ttf_uniqueid);
1319     PListAddNameString(dictnode,"openTypeNameDescription",sf,ttf_descriptor);
1320     PListAddNameString(dictnode,"openTypeNamePreferredFamilyName",sf,ttf_preffamilyname);
1321     PListAddNameString(dictnode,"openTypeNamePreferredSubfamilyName",sf,ttf_prefmodifiers);
1322     PListAddNameString(dictnode,"openTypeNameCompatibleFullName",sf,ttf_compatfull);
1323     PListAddNameString(dictnode,"openTypeNameSampleText",sf,ttf_sampletext);
1324     PListAddNameString(dictnode,"openTypeWWSFamilyName",sf,ttf_wwsfamily);
1325     PListAddNameString(dictnode,"openTypeWWSSubfamilyName",sf,ttf_wwssubfamily);
1326     if ( sf->pfminfo.panose_set )
1327 	PListAddIntArray(dictnode,"openTypeOS2Panose",sf->pfminfo.panose,10);
1328     if ( sf->pfminfo.pfmset ) {
1329 	char vendor[8], fc[2];
1330 	PListAddInteger(dictnode,"openTypeOS2WidthClass",sf->pfminfo.width);
1331 	PListAddInteger(dictnode,"openTypeOS2WeightClass",sf->pfminfo.weight);
1332 	memcpy(vendor,sf->pfminfo.os2_vendor,4);
1333 	vendor[4] = 0;
1334 	PListAddString(dictnode,"openTypeOS2VendorID",vendor);
1335 	fc[0] = sf->pfminfo.os2_family_class>>8; fc[1] = sf->pfminfo.os2_family_class&0xff;
1336 	PListAddIntArray(dictnode,"openTypeOS2FamilyClass",fc,2);
1337 	if ( sf->pfminfo.fstype!=-1 ) {
1338 	    int fscnt,i;
1339 	    char fstype[16];
1340 	    for ( i=fscnt=0; i<16; ++i )
1341 		if ( sf->pfminfo.fstype&(1<<i) )
1342 		    fstype[fscnt++] = i;
1343 	    /*
1344 	     * Note that value 0x0 is represented by an empty bit, so in that case
1345 	     * we output an empty array, otherwise compilers will fallback to their
1346 	     * default fsType value.
1347 	     */
1348 	    PListAddIntArray(dictnode,"openTypeOS2Type",fstype,fscnt);
1349 	}
1350 	if ( sf->pfminfo.typoascent_add )
1351 	    PListAddInteger(dictnode,"openTypeOS2TypoAscender",sf->ascent+sf->pfminfo.os2_typoascent);
1352 	else
1353 	    PListAddInteger(dictnode,"openTypeOS2TypoAscender",sf->pfminfo.os2_typoascent);
1354 	if ( sf->pfminfo.typodescent_add )
1355 	    PListAddInteger(dictnode,"openTypeOS2TypoDescender",-sf->descent+sf->pfminfo.os2_typodescent);
1356 	else
1357 	    PListAddInteger(dictnode,"openTypeOS2TypoDescender",sf->pfminfo.os2_typodescent);
1358 	PListAddInteger(dictnode,"openTypeOS2TypoLineGap",sf->pfminfo.os2_typolinegap);
1359 	if ( sf->pfminfo.winascent_add )
1360 	    PListAddInteger(dictnode,"openTypeOS2WinAscent",bb.maxy+sf->pfminfo.os2_winascent);
1361 	else
1362 	    PListAddInteger(dictnode,"openTypeOS2WinAscent",sf->pfminfo.os2_winascent);
1363 	if ( sf->pfminfo.windescent_add )
1364 	    PListAddInteger(dictnode,"openTypeOS2WinDescent",-bb.miny+sf->pfminfo.os2_windescent);
1365 	else
1366 	    PListAddInteger(dictnode,"openTypeOS2WinDescent",sf->pfminfo.os2_windescent);
1367     }
1368     if ( sf->pfminfo.subsuper_set ) {
1369 	PListAddInteger(dictnode,"openTypeOS2SubscriptXSize",sf->pfminfo.os2_subxsize);
1370 	PListAddInteger(dictnode,"openTypeOS2SubscriptYSize",sf->pfminfo.os2_subysize);
1371 	PListAddInteger(dictnode,"openTypeOS2SubscriptXOffset",sf->pfminfo.os2_subxoff);
1372 	PListAddInteger(dictnode,"openTypeOS2SubscriptYOffset",sf->pfminfo.os2_subyoff);
1373 	PListAddInteger(dictnode,"openTypeOS2SuperscriptXSize",sf->pfminfo.os2_supxsize);
1374 	PListAddInteger(dictnode,"openTypeOS2SuperscriptYSize",sf->pfminfo.os2_supysize);
1375 	PListAddInteger(dictnode,"openTypeOS2SuperscriptXOffset",sf->pfminfo.os2_supxoff);
1376 	PListAddInteger(dictnode,"openTypeOS2SuperscriptYOffset",sf->pfminfo.os2_supyoff);
1377 	PListAddInteger(dictnode,"openTypeOS2StrikeoutSize",sf->pfminfo.os2_strikeysize);
1378 	PListAddInteger(dictnode,"openTypeOS2StrikeoutPosition",sf->pfminfo.os2_strikeypos);
1379     }
1380     if ( sf->pfminfo.vheadset )
1381 	PListAddInteger(dictnode,"openTypeVheaTypoLineGap",sf->pfminfo.vlinegap);
1382     if ( sf->pfminfo.hasunicoderanges ) {
1383 	char ranges[128];
1384 	int i, j, c = 0;
1385 
1386 	for ( i = 0; i<4; i++ )
1387 	    for ( j = 0; j<32; j++ )
1388 		if ( sf->pfminfo.unicoderanges[i] & (1 << j) )
1389 		    ranges[c++] = i*32+j;
1390 	if ( c!=0 )
1391 	    PListAddIntArray(dictnode,"openTypeOS2UnicodeRanges",ranges,c);
1392     }
1393     if ( sf->pfminfo.hascodepages ) {
1394 	char pages[64];
1395 	int i, j, c = 0;
1396 
1397 	for ( i = 0; i<2; i++)
1398 	    for ( j=0; j<32; j++ )
1399 		if ( sf->pfminfo.codepages[i] & (1 << j) )
1400 		    pages[c++] = i*32+j;
1401 	if ( c!=0 )
1402 	    PListAddIntArray(dictnode,"openTypeOS2CodePageRanges",pages,c);
1403     }
1404     if (sf->fontname)
1405         PListAddString(dictnode,"postscriptFontName",sf->fontname);
1406     if (sf->fullname)
1407         PListAddString(dictnode,"postscriptFullName",sf->fullname);
1408     if (sf->weight)
1409         PListAddString(dictnode,"postscriptWeightName",sf->weight);
1410     /* Spec defines a "postscriptSlantAngle" but I don't think postscript does*/
1411     /* PS does define an italicAngle, but presumably that's the general italic*/
1412     /* angle we output earlier */
1413     /* UniqueID is obsolete */
1414     PListAddInteger(dictnode,"postscriptUnderlineThickness",sf->uwidth);
1415     PListAddInteger(dictnode,"postscriptUnderlinePosition",sf->upos);
1416     if ( sf->private!=NULL ) {
1417 	char *pt;
1418 	PListAddPrivateArray(dictnode, "BlueValues", sf->private);
1419 	PListAddPrivateArray(dictnode, "OtherBlues", sf->private);
1420 	PListAddPrivateArray(dictnode, "FamilyBlues", sf->private);
1421 	PListAddPrivateArray(dictnode, "FamilyOtherBlues", sf->private);
1422 	PListAddPrivateArray(dictnode, "StemSnapH", sf->private);
1423 	PListAddPrivateArray(dictnode, "StemSnapV", sf->private);
1424 	PListAddPrivateThing(dictnode, "BlueFuzz", sf->private, "integer");
1425 	PListAddPrivateThing(dictnode, "BlueShift", sf->private, "integer");
1426 	PListAddPrivateThing(dictnode, "BlueScale", sf->private, "real");
1427 	if ( (pt=PSDictHasEntry(sf->private,"ForceBold"))!=NULL )
1428 	    PListAddBoolean(dictnode, "postscriptForceBold", strstr(pt,"true")!=NULL ? true : false );
1429     }
1430     if ( sf->fondname!=NULL )
1431     PListAddString(dictnode,"macintoshFONDName",sf->fondname);
1432     // If the version is 3 and the grid layer exists, emit a guidelines group.
1433     if (version > 2) {
1434         xmlNodePtr glkeynode = xmlNewChild(dictnode, NULL, BAD_CAST "key", "guidelines");
1435         xmlNodePtr gllistnode = xmlNewChild(dictnode, NULL, BAD_CAST "array", NULL);
1436         SplineSet *ss = NULL;
1437         for (ss = sf->grid.splines; ss != NULL; ss = ss->next) {
1438             // Convert to a standard guideline.
1439             GuidelineSet *gl = SplineToGuideline(sf, ss);
1440             if (gl) {
1441                 xmlNodePtr gldictnode = xmlNewChild(gllistnode, NULL, BAD_CAST "dict", NULL);
1442                 {
1443 		    // These are figured from splines, so we have a synthetic set of flags.
1444 		    int glflags = 0x10; // This indicates that we want to omit unnecessary values, which seems to be standard.
1445 		    // We also output all coordinates if there is a non-zero coordinate.
1446 		    if (fmod(gl->angle, 180) || !(glflags & 0x10) || gl->point.x != 0)
1447 		        PListAddReal(gldictnode, "x", gl->point.x);
1448 		    if (fmod(gl->angle + 90, 180) || !(glflags & 0x10) || gl->point.y != 0)
1449 		        PListAddReal(gldictnode, "y", gl->point.y);
1450 		    // If x and y are both present, we must add angle.
1451 		    if (fmod(gl->angle, 90) || !(glflags & 0x10) || (gl->point.x != 0 && gl->point.y != 0))
1452 		        PListAddReal(gldictnode, "angle", fmod(gl->angle + 360, 360));
1453 		    if (gl->name != NULL)
1454 		        PListAddString(gldictnode, "name", gl->name);
1455                 }
1456                 free(gl);
1457                 gl = NULL;
1458             }
1459         }
1460     }
1461     // TODO: Output unrecognized data.
1462     char *fname = buildname(basedir, "fontinfo.plist"); // Build the file name.
1463     xmlSaveFormatFileEnc(fname, plistdoc, "UTF-8", 1); // Store the document.
1464     free(fname); fname = NULL;
1465     xmlFreeDoc(plistdoc); // Free the memory.
1466     xmlCleanupParser();
1467     return true;
1468 }
1469 
kernclass_for_groups_plist(struct splinefont * sf,struct kernclass * kc,int flags)1470 int kernclass_for_groups_plist(struct splinefont *sf, struct kernclass *kc, int flags) {
1471   // Note that this is not a complete logical inverse of sister function kernclass_for_feature_file.
1472   return ((flags & FF_KERNCLASS_FLAG_NATIVE) ||
1473   (!(flags & FF_KERNCLASS_FLAG_FEATURE) && !kc->feature && (sf->preferred_kerning & 1)));
1474 }
1475 
ClassKerningAddExtensions(struct kernclass * target)1476 void ClassKerningAddExtensions(struct kernclass * target) {
1477   if (target->firsts_names == NULL && target->first_cnt) target->firsts_names = calloc(target->first_cnt, sizeof(char *));
1478   if (target->seconds_names == NULL && target->second_cnt) target->seconds_names = calloc(target->second_cnt, sizeof(char *));
1479   if (target->firsts_flags == NULL && target->first_cnt) target->firsts_flags = calloc(target->first_cnt, sizeof(int));
1480   if (target->seconds_flags == NULL && target->second_cnt) target->seconds_flags = calloc(target->second_cnt, sizeof(int));
1481   if (target->offsets_flags == NULL && (target->first_cnt * target->second_cnt)) target->offsets_flags = calloc(target->first_cnt * target->second_cnt, sizeof(int));
1482 }
1483 
UFONameKerningClasses(SplineFont * sf,int version)1484 void UFONameKerningClasses(SplineFont *sf, int version) {
1485     struct glif_name_index * class_name_hash = glif_name_index_new(); // Open the hash table.
1486     struct kernclass *current_kernclass;
1487     int isv;
1488     int isr;
1489     int i;
1490     int absolute_index = 0; // This gives us a unique index for each kerning class.
1491     // First we catch the existing names.
1492     HashKerningClassNamesCaps(sf, class_name_hash); // Note that we use the all-caps hasher for compatibility with the official naming scheme and the following code.
1493     // Next we create names for the unnamed. Note that we currently avoid naming anything that might go into the feature file (since that handler currently creates its own names).
1494     absolute_index = 0;
1495     for (isv = 0; isv < 2; isv++)
1496     for ( current_kernclass = (isv ? sf->vkerns : sf->kerns); current_kernclass != NULL; current_kernclass = current_kernclass->next )
1497     for (isr = 0; isr < 2; isr++) {
1498       // If the special class kerning storage blocks are unallocated, we allocate them if using native U. F. O. class kerning or skip the naming otherwise.
1499       if ( (isr ? current_kernclass->seconds_names : current_kernclass->firsts_names) == NULL ) {
1500         if ( !(current_kernclass->feature) && (sf->preferred_kerning & 1) ) {
1501           ClassKerningAddExtensions(current_kernclass);
1502         } else {
1503           continue;
1504         }
1505       }
1506       for ( i=0; i< (isr ? current_kernclass->second_cnt : current_kernclass->first_cnt); i++ )
1507       if ( ((isr ? current_kernclass->seconds_names[i] : current_kernclass->firsts_names[i]) == NULL) &&
1508         kernclass_for_groups_plist(sf, current_kernclass, (isr ? current_kernclass->seconds_flags[i] : current_kernclass->firsts_flags[i]))
1509         ) {
1510         int classflags = isr ? current_kernclass->seconds_flags[i] : current_kernclass->firsts_flags[i];
1511         // If the splinefont is not forcing a default group type and the group is already flagged for a feature file or to have a feature-compatible name, we give it a feature-compatible name.
1512         // Otherwise, we give it a native U. F. O. name.
1513 	// TODO: Use the UFO version.
1514         char * startname =
1515           (
1516             (
1517               (
1518                 (sf->preferred_kerning == 0) &&
1519                 (classflags & (FF_KERNCLASS_FLAG_FEATURE | FF_KERNCLASS_FLAG_NAMETYPE))
1520               ) ||
1521               (sf->preferred_kerning & 2) || (sf->preferred_kerning & 4)
1522             ) ?
1523             (
1524               isv ?
1525               (isr ? "@MMK_B_FF" : "@MMK_A_FF") :
1526               (isr ? "@MMK_R_FF" : "@MMK_L_FF")
1527             ) :
1528             (
1529               isv ?
1530               (isr ? "public.vkern2.FF" : "public.vkern1.FF") :
1531               (isr ? "public.kern2.FF" : "public.kern1.FF")
1532             )
1533           );
1534         // We must flag the group as being destined for the native file if it has a non-feature-compatible name. Otherwise, we might corrupt the feature file if it ever starts using the names.
1535         if (startname[0] != '@') {
1536           if (isr) { current_kernclass->seconds_flags[i] |= FF_KERNCLASS_FLAG_NATIVE; current_kernclass->seconds_flags[i] &= ~FF_KERNCLASS_FLAG_FEATURE; }
1537           else { current_kernclass->firsts_flags[i] |= FF_KERNCLASS_FLAG_NATIVE; current_kernclass->firsts_flags[i] &= ~FF_KERNCLASS_FLAG_FEATURE; }
1538         }
1539         // Number the name uniquely. (Note the number-forcing flag.)
1540         // And add it to the hash table with its index.
1541         char * numberedname = ufo_name_number(class_name_hash, absolute_index + i, startname, "", "", 23);
1542         if (isr) current_kernclass->seconds_names[i] = numberedname;
1543         else current_kernclass->firsts_names[i] = numberedname;
1544 	numberedname = NULL;
1545       }
1546       absolute_index +=i;
1547     }
1548     glif_name_index_destroy(class_name_hash); // Close the hash table.
1549 }
1550 
UFOOutputGroups(const char * basedir,SplineFont * sf,int version)1551 static int UFOOutputGroups(const char *basedir, SplineFont *sf, int version) {
1552     SplineChar *sc;
1553     int has_content = 0;
1554 
1555     xmlDocPtr plistdoc = PlistInit(); if (plistdoc == NULL) return false; // Make the document.
1556     xmlNodePtr rootnode = xmlDocGetRootElement(plistdoc); if (rootnode == NULL) { xmlFreeDoc(plistdoc); return false; } // Find the root node.
1557     xmlNodePtr dictnode = xmlNewChild(rootnode, NULL, BAD_CAST "dict", NULL); if (dictnode == NULL) { xmlFreeDoc(plistdoc); return false; } // Add the dict.
1558 
1559     // In order to preserve ordering, we do some very icky and inefficient things.
1560     // First, we assign names to any unnamed kerning groups using the public.kern syntax if the splinefont is set to use native U. F. O. kerning.
1561     // We assign @MMK names otherwise.
1562     // In assigning names, we check collisions both with other kerning classes and with natively listed groups.
1563     UFONameKerningClasses(sf, version);
1564 
1565     // Once names are assigned, we start outputting the native groups, with some exceptions.
1566     // Since we consider kerning groups to be natively handled, any group with a kerning-style name gets checked against kerning classes.
1567     // If it exists, we output it and flag it as output in a temporary array. If it does not exist, we skip it.
1568     // When this is complete, we go through the left and right kerning classes and output any that have not been output, adding them to the native list as we do so.
1569 
1570     int kerning_class_count = CountKerningClasses(sf);
1571     char *output_done = kerning_class_count ? calloc(kerning_class_count, sizeof(char)) : NULL;
1572 
1573     struct glif_name_index * class_name_hash = glif_name_index_new(); // Open the hash table.
1574     HashKerningClassNames(sf, class_name_hash);
1575     struct ff_glyphclasses *current_group;
1576     for (current_group = sf->groups; current_group != NULL; current_group = current_group->next) {
1577       if (current_group->classname != NULL) {
1578         int grouptype = GroupNameType(current_group->classname);
1579         if (grouptype > 0) {
1580           // We skip the group if it has a corrupt feature-like name.
1581           if (grouptype & (GROUP_NAME_KERNING_UFO | GROUP_NAME_KERNING_FEATURE)) {
1582             // If the group is a kerning group, we defer to the native kerning data for existence and content.
1583             struct glif_name *class_name_record = glif_name_search_glif_name(class_name_hash, current_group->classname);
1584             if (class_name_record != NULL) {
1585               int absolute_index = class_name_record->gid; // This gives us a unique index for the kerning class.
1586               struct kernclass *kc;
1587               int isv;
1588               int isr;
1589               int i;
1590               int offset;
1591               if (KerningClassSeekByAbsoluteIndex(sf, absolute_index, &kc, &isv, &isr, &offset)) {
1592                 // The group still exists.
1593                 xmlNewChild(dictnode, NULL, BAD_CAST "key", current_group->classname);
1594                 xmlNodePtr grouparray = xmlNewChild(dictnode, NULL, BAD_CAST "array", NULL);
1595                 // We use the results of the preceding search in order to get the list.
1596                 char *rawglyphlist = (
1597                   (isr ? kc->seconds[offset] : kc->firsts[offset])
1598                 );
1599                 // We need to convert from the space-delimited string to something more easily accessed on a per-item basis.
1600                 char **glyphlist = StringExplode(rawglyphlist, ' ');
1601                 // We will then output only those entries for which SFGetChar succeeds.
1602                 int index = 0;
1603                 while (glyphlist[index] != NULL) {
1604                   if (SFGetChar(sf, -1, glyphlist[index]))
1605                     xmlNewChild(grouparray, NULL, BAD_CAST "string", glyphlist[index]);
1606                   index++;
1607                 }
1608                 ExplodedStringFree(glyphlist);
1609                 // We flag the output of this kerning class as complete.
1610                 output_done[absolute_index] |= 1;
1611                 has_content = 1;
1612               }
1613             }
1614           }
1615         } else {
1616           // If the group is not a kerning group, we just output it raw (for now).
1617           xmlNewChild(dictnode, NULL, BAD_CAST "key", current_group->classname);
1618           xmlNodePtr grouparray = xmlNewChild(dictnode, NULL, BAD_CAST "array", NULL);
1619           // We need to convert from the space-delimited string to something more easily accessed on a per-item basis.
1620           char **glyphlist = StringExplode(current_group->glyphs, ' ');
1621           // We will then output only those entries for which SFGetChar succeeds.
1622           int index = 0;
1623           while (glyphlist[index] != NULL) {
1624             if (SFGetChar(sf, -1, glyphlist[index]))
1625               xmlNewChild(grouparray, NULL, BAD_CAST "string", glyphlist[index]);
1626             index++;
1627           }
1628           ExplodedStringFree(glyphlist);
1629           has_content = 1;
1630         }
1631       }
1632     }
1633     {
1634       // Oh. But we've not finished yet. Some kerning classes may not be in the groups listing.
1635       struct kernclass *current_kernclass;
1636       int isv;
1637       int isr;
1638       int i;
1639       int absolute_index = 0;
1640       for (isv = 0; isv < 2; isv++)
1641       for (current_kernclass = (isv ? sf->vkerns : sf->kerns); current_kernclass != NULL; current_kernclass = current_kernclass->next)
1642       for (isr = 0; isr < 2; isr++)
1643       if (isr ? current_kernclass->seconds_names : current_kernclass->firsts_names) {
1644         for (i=0; i < (isr ? current_kernclass->second_cnt : current_kernclass->first_cnt); ++i) {
1645           const char *classname = (isr ? current_kernclass->seconds_names[i] : current_kernclass->firsts_names[i]);
1646           const char *rawglyphlist = (isr ? current_kernclass->seconds[i] : current_kernclass->firsts[i]);
1647           int classflags = (isr ? current_kernclass->seconds_flags[i] : current_kernclass->firsts_flags[i]);
1648           // Note that we only output if the kernclass is destined for U. F. O. and has not already met said fate.
1649           if (classname != NULL && rawglyphlist != NULL &&
1650               !(output_done[absolute_index + i]) && kernclass_for_groups_plist(sf, current_kernclass, classflags)) {
1651                 xmlNewChild(dictnode, NULL, BAD_CAST "key", classname);
1652                 xmlNodePtr grouparray = xmlNewChild(dictnode, NULL, BAD_CAST "array", NULL);
1653                 // We need to convert from the space-delimited string to something more easily accessed on a per-item basis.
1654                 char **glyphlist = StringExplode(rawglyphlist, ' ');
1655                 // We will then output only those entries for which SFGetChar succeeds.
1656                 int index = 0;
1657                 while (glyphlist[index] != NULL) {
1658                   if (SFGetChar(sf, -1, glyphlist[index]))
1659                     xmlNewChild(grouparray, NULL, BAD_CAST "string", glyphlist[index]);
1660                   index++;
1661                 }
1662                 ExplodedStringFree(glyphlist);
1663                 // We flag the output of this kerning class as complete.
1664                 output_done[absolute_index + i] |= 1;
1665                                 has_content = 1;
1666           }
1667         }
1668         absolute_index +=i;
1669       }
1670     }
1671 
1672     glif_name_index_destroy(class_name_hash); // Close the hash table.
1673 
1674     if (output_done != NULL) { free(output_done); output_done = NULL; }
1675 
1676     char *fname = buildname(basedir, "groups.plist"); // Build the file name.
1677     if (has_content) xmlSaveFormatFileEnc(fname, plistdoc, "UTF-8", 1); // Store the document.
1678     free(fname); fname = NULL;
1679     xmlFreeDoc(plistdoc); // Free the memory.
1680     xmlCleanupParser();
1681     return true;
1682 }
1683 
KerningPListAddGlyph(xmlNodePtr parent,const char * key,const KernPair * kp)1684 static void KerningPListAddGlyph(xmlNodePtr parent, const char *key, const KernPair *kp) {
1685     xmlNewChild(parent, NULL, BAD_CAST "key", BAD_CAST key); // "<key>%s</key>" key
1686     xmlNodePtr dictxml = xmlNewChild(parent, NULL, BAD_CAST "dict", NULL); // "<dict>"
1687     while ( kp!=NULL ) {
1688       PListAddInteger(dictxml, kp->sc->name, kp->off); // "<key>%s</key><integer>%d</integer>" kp->sc->name kp->off
1689       kp = kp->next;
1690     }
1691 }
1692 
1693 struct ufo_kerning_tree_left;
1694 struct ufo_kerning_tree_right;
1695 struct ufo_kerning_tree_left {
1696   char *name;
1697   struct ufo_kerning_tree_right *first_right;
1698   struct ufo_kerning_tree_right *last_right;
1699   struct ufo_kerning_tree_left *next;
1700 };
1701 
1702 struct ufo_kerning_tree_right {
1703   char *name;
1704   int value;
1705   struct ufo_kerning_tree_right *next;
1706 };
1707 
1708 struct ufo_kerning_tree_session {
1709   struct ufo_kerning_tree_left *first_left;
1710   struct ufo_kerning_tree_left *last_left;
1711   int left_group_count;
1712   int class_pair_count;
1713   struct glif_name_index *class_pair_hash;
1714 };
1715 
ufo_kerning_tree_destroy_contents(struct ufo_kerning_tree_session * session)1716 void ufo_kerning_tree_destroy_contents(struct ufo_kerning_tree_session *session) {
1717   struct ufo_kerning_tree_left *current_left;
1718   struct ufo_kerning_tree_left *next_left;
1719   for (current_left = session->first_left; current_left != NULL; current_left = next_left) {
1720     next_left = current_left->next;
1721     struct ufo_kerning_tree_right *current_right;
1722     struct ufo_kerning_tree_right *next_right;
1723     for (current_right = current_left->first_right; current_right != NULL; current_right = next_right) {
1724       next_right = current_right->next;
1725       if (current_right->name != NULL) free(current_right->name);
1726       free(current_right);
1727     }
1728     if (current_left->name != NULL) free(current_left->name);
1729     free(current_left);
1730   }
1731   glif_name_index_destroy(session->class_pair_hash);
1732   memset(session, 0, sizeof(struct ufo_kerning_tree_session));
1733 }
1734 
ufo_kerning_tree_attempt_insert(struct ufo_kerning_tree_session * session,const char * left_name,const char * right_name,int value)1735 int ufo_kerning_tree_attempt_insert(struct ufo_kerning_tree_session *session, const char *left_name, const char *right_name, int value) {
1736   char *tmppairname = smprintf("%s %s", left_name, right_name);
1737   struct ufo_kerning_tree_left *first_left = NULL;
1738   struct ufo_kerning_tree_left *last_left = NULL;
1739   if (!glif_name_search_glif_name(session->class_pair_hash, tmppairname)) {
1740     struct ufo_kerning_tree_left *current_left;
1741     // We look for a tree node matching the left side of the pair.
1742     for (current_left = session->first_left; current_left != NULL &&
1743       (current_left->name == NULL || strcmp(current_left->name, left_name));
1744       current_left = current_left->next);
1745     // If the search fails, we make a new node.
1746     if (current_left == NULL) {
1747       current_left = calloc(1, sizeof(struct ufo_kerning_tree_left));
1748       current_left->name = copy(left_name);
1749       if (session->last_left != NULL) session->last_left->next = current_left;
1750       else session->first_left = current_left;
1751       session->last_left = current_left;
1752     }
1753     {
1754       // We already know from the pair hash search that this pair does not exist.
1755       // So we go right to the end.
1756       struct ufo_kerning_tree_right *current_right = calloc(1, sizeof(struct ufo_kerning_tree_right));
1757       current_right->name = copy(right_name);
1758       current_right->value = value;
1759       if (current_left->last_right != NULL) current_left->last_right->next = current_right;
1760       else current_left->first_right = current_right;
1761       current_left->last_right = current_right;
1762       char *newpairname = smprintf("%s %s", left_name, right_name);
1763       glif_name_track_new(session->class_pair_hash, session->class_pair_count++, newpairname);
1764       free(newpairname); newpairname = NULL;
1765     }
1766   }
1767   free(tmppairname); tmppairname = NULL;
1768   return 0;
1769 }
1770 
1771 
UFOOutputKerning2(const char * basedir,SplineFont * sf,int isv,int version)1772 static int UFOOutputKerning2(const char *basedir, SplineFont *sf, int isv, int version) {
1773     int i, j;
1774     int has_content = 0;
1775 
1776     if (!(sf->preferred_kerning & 1)) return true; // This goes into the feature file by default now.
1777 
1778     xmlDocPtr plistdoc = PlistInit(); if (plistdoc == NULL) return false; // Make the document.
1779     xmlNodePtr rootnode = xmlDocGetRootElement(plistdoc); if (rootnode == NULL) { xmlFreeDoc(plistdoc); return false; } // Find the root node.
1780     xmlNodePtr dictnode = xmlNewChild(rootnode, NULL, BAD_CAST "dict", NULL); if (dictnode == NULL) { xmlFreeDoc(plistdoc); return false; } // Add the dict.
1781 
1782     // In order to preserve ordering, we do some very icky and inefficient things.
1783     // First, we assign names to any unnamed kerning groups using the public.kern syntax if the splinefont is set to use native U. F. O. kerning.
1784     // We assign @MMK names otherwise.
1785     // In assigning names, we check collisions both with other kerning classes and with natively listed groups.
1786     UFONameKerningClasses(sf, version);
1787 
1788     // Once names are assigned, we start outputting the native groups, with some exceptions.
1789     // Since we consider kerning groups to be natively handled, any group with a kerning-style name gets checked against kerning classes.
1790     // If it exists, we output it and flag it as output in a temporary array. If it does not exist, we skip it.
1791     // When this is complete, we go through the left and right kerning classes and output any that have not been output, adding them to the native list as we do so.
1792 
1793     int kerning_class_count = CountKerningClasses(sf);
1794     int kerning_class_pair_count = kerning_class_count * kerning_class_count;
1795     char *output_done = kerning_class_pair_count ? calloc(kerning_class_pair_count, sizeof(char)) : NULL;
1796 
1797     struct ufo_kerning_tree_session _session;
1798     memset(&_session, 0, sizeof(struct ufo_kerning_tree_session));
1799     struct ufo_kerning_tree_session *session = &_session;
1800     session->class_pair_hash = glif_name_index_new(); // Open the hash table.
1801 
1802     struct glif_name_index * class_name_hash = glif_name_index_new(); // Open the hash table.
1803     HashKerningClassNames(sf, class_name_hash);
1804 
1805     // We process the raw kerning list first in order to give preference to the original ordering.
1806     struct ff_rawoffsets *current_groupkern;
1807     for (current_groupkern = (isv ? sf->groupvkerns : sf->groupkerns); current_groupkern != NULL; current_groupkern = current_groupkern->next) {
1808       if (current_groupkern->left != NULL && current_groupkern->right != NULL) {
1809         int left_grouptype = GroupNameType(current_groupkern->left);
1810         int right_grouptype = GroupNameType(current_groupkern->right);
1811         int offset = 0;
1812         int valid = 0;
1813         if (left_grouptype > 0 && right_grouptype > 0) {
1814           // It's a pure class look-up.
1815           struct glif_name *left_class_name_record = glif_name_search_glif_name(class_name_hash, current_groupkern->left);
1816           struct glif_name *right_class_name_record = glif_name_search_glif_name(class_name_hash, current_groupkern->right);
1817           if ((left_grouptype & right_grouptype & (GROUP_NAME_KERNING_UFO | GROUP_NAME_KERNING_FEATURE)) &&
1818             !(left_grouptype & GROUP_NAME_RIGHT) && (right_grouptype & GROUP_NAME_RIGHT) &&
1819             ((left_grouptype & GROUP_NAME_VERTICAL) == (isv * GROUP_NAME_VERTICAL)) &&
1820             ((right_grouptype & GROUP_NAME_VERTICAL) == (isv * GROUP_NAME_VERTICAL)) &&
1821             left_class_name_record != NULL && right_class_name_record != NULL) {
1822             // If the group is a kerning group, we defer to the native kerning data for existence and content.
1823             {
1824               int left_absolute_index = left_class_name_record->gid; // This gives us a unique index for the kerning class.
1825               int right_absolute_index = right_class_name_record->gid; // This gives us a unique index for the kerning class.
1826               struct kernclass *left_kc, *right_kc;
1827               int left_isv, right_isv;
1828               int left_isr, right_isr;
1829               int left_offset, right_offset;
1830               if (KerningClassSeekByAbsoluteIndex(sf, left_absolute_index, &left_kc, &left_isv, &left_isr, &left_offset) &&
1831                 KerningClassSeekByAbsoluteIndex(sf, right_absolute_index, &right_kc, &right_isv, &right_isr, &right_offset) &&
1832                 left_kc == right_kc) {
1833                 offset = left_kc->offsets[left_offset*left_kc->second_cnt+right_offset];
1834                 valid = 1;
1835               }
1836             }
1837           }
1838         } else if (left_grouptype == 0 && right_grouptype == 0) {
1839           // It's a plain kerning pair.
1840           struct splinechar *sc = SFGetChar(sf, -1, current_groupkern->left);
1841           struct splinechar *ssc = SFGetChar(sf, -1, current_groupkern->right);
1842           if (sc && ssc) {
1843             struct kernpair *current_kernpair = (isv ? sc->vkerns : sc->kerns);
1844             while (current_kernpair != NULL && current_kernpair->sc != ssc) current_kernpair = current_kernpair->next;
1845             if (current_kernpair != NULL && current_kernpair->sc == ssc) {
1846               offset = current_kernpair->off;
1847               valid = 1;
1848             }
1849           }
1850         } else if (left_grouptype == 0 && right_grouptype > 0) {
1851           // It's a mixed pair. FontForge does not handle these natively right now, so, if the two references are valid, we output the raw value.
1852           struct splinechar *sc = SFGetChar(sf, -1, current_groupkern->left);
1853           struct glif_name *right_class_name_record = glif_name_search_glif_name(class_name_hash, current_groupkern->right);
1854           if ((right_grouptype & (GROUP_NAME_KERNING_UFO | GROUP_NAME_KERNING_FEATURE)) &&
1855             (right_grouptype & GROUP_NAME_RIGHT) &&
1856             ((right_grouptype & GROUP_NAME_VERTICAL) == (isv * GROUP_NAME_VERTICAL)) &&
1857             right_class_name_record != NULL &&
1858             sc != NULL) {
1859             offset = current_groupkern->offset;
1860             valid = 1;
1861           }
1862         } else if (left_grouptype > 0 && right_grouptype == 0) {
1863           // It's a mixed pair. FontForge does not handle these natively right now, so, if the two references are valid, we output the raw value.
1864           struct splinechar *ssc = SFGetChar(sf, -1, current_groupkern->right);
1865           struct glif_name *left_class_name_record = glif_name_search_glif_name(class_name_hash, current_groupkern->left);
1866           if ((left_grouptype & (GROUP_NAME_KERNING_UFO | GROUP_NAME_KERNING_FEATURE)) &&
1867             !(left_grouptype & GROUP_NAME_RIGHT) &&
1868             ((left_grouptype & GROUP_NAME_VERTICAL) == (isv * GROUP_NAME_VERTICAL)) &&
1869             left_class_name_record != NULL &&
1870             ssc != NULL) {
1871             offset = current_groupkern->offset;
1872             valid = 1;
1873           }
1874         } else {
1875           // Something is wrong.
1876         }
1877         if (valid) {
1878           ufo_kerning_tree_attempt_insert(session, current_groupkern->left, current_groupkern->right, offset);
1879         }
1880       }
1881     }
1882     {
1883       // Oh. But we've not finished yet. New class kerns may not be in the original list.
1884       struct kernclass *kc;
1885       char *left_name;
1886       char *right_name;
1887       for (kc = isv ? sf->vkerns : sf->kerns; kc != NULL; kc = kc->next)
1888       if (kc->firsts_names && kc->seconds_names && kc->firsts_flags && kc->seconds_flags &&
1889         kc->offsets_flags)
1890       for ( i=0; i<kc->first_cnt; ++i ) if ( kc->firsts[i]!=NULL && kc->firsts_names[i]!=NULL && kernclass_for_groups_plist(sf, kc, kc->firsts_flags[i]))
1891         for ( j=0; j<kc->second_cnt; ++j ) if ( kc->seconds[j]!=NULL && kc->seconds_names[j]!=NULL && kernclass_for_groups_plist(sf, kc, kc->firsts_flags[j]))
1892           if (kernclass_for_groups_plist(sf, kc, kc->offsets_flags[i*kc->second_cnt+j]) && kc->offsets[i*kc->second_cnt+j] != 0)
1893             ufo_kerning_tree_attempt_insert(session, kc->firsts_names[i], kc->seconds_names[j], kc->offsets[i*kc->second_cnt+j]);
1894     }
1895     {
1896       // And don't forget about pair kerns.
1897       for ( i=0; i<sf->glyphcnt; ++i ) {
1898         struct splinechar *sc = sf->glyphs[i];
1899         struct kernpair *kp;
1900         if ( (SCWorthOutputting(sc) || SCHasData(sc) || sc->glif_name != NULL) && (isv ? sc->vkerns : sc->kerns ) !=NULL )
1901           for (kp = (isv ? sc->vkerns : sc->kerns); kp != NULL; kp = kp->next)
1902             if (kp->sc != NULL && sc->name != NULL && kp->sc->name != NULL)
1903               ufo_kerning_tree_attempt_insert(session, sc->name, kp->sc->name, kp->off);
1904       }
1905     }
1906 
1907     {
1908       // Output time has arrived.
1909       struct ufo_kerning_tree_left *current_left;
1910       for (current_left = session->first_left; current_left != NULL; current_left = current_left->next) {
1911         if (current_left->name != NULL) {
1912           xmlNewChild(dictnode, NULL, BAD_CAST "key", BAD_CAST current_left->name); // "<key>%s</key>" key
1913           xmlNodePtr dictxml = xmlNewChild(dictnode, NULL, BAD_CAST "dict", NULL); // "<dict>"
1914           struct ufo_kerning_tree_right *current_right;
1915           for (current_right = current_left->first_right; current_right != NULL; current_right = current_right->next)
1916             if (current_right->name != NULL) PListAddInteger(dictxml, current_right->name, current_right->value);
1917           has_content = 1;
1918         }
1919       }
1920     }
1921 
1922     glif_name_index_destroy(class_name_hash); // Close the hash table.
1923     ufo_kerning_tree_destroy_contents(session);
1924 
1925     if (output_done != NULL) { free(output_done); output_done = NULL; }
1926 
1927     char *fname = buildname(basedir, (isv ? "vkerning.plist" : "kerning.plist")); // Build the file name.
1928     if (has_content) xmlSaveFormatFileEnc(fname, plistdoc, "UTF-8", 1); // Store the document.
1929     free(fname); fname = NULL;
1930     xmlFreeDoc(plistdoc); // Free the memory.
1931     xmlCleanupParser();
1932     return true;
1933 }
1934 
UFOOutputLib(const char * basedir,const SplineFont * sf,int version)1935 static int UFOOutputLib(const char *basedir, const SplineFont *sf, int version) {
1936 #ifndef _NO_PYTHON
1937     if ( sf->python_persistent==NULL || PyMapping_Check(sf->python_persistent) == 0) return true;
1938 
1939     xmlDocPtr plistdoc = PlistInit(); if (plistdoc == NULL) return false; // Make the document.
1940     xmlNodePtr rootnode = xmlDocGetRootElement(plistdoc); if (rootnode == NULL) return false; // Find the root node.
1941 
1942     xmlNodePtr dictnode = PythonLibToXML(sf->python_persistent,NULL,sf->python_persistent_has_lists);
1943     xmlAddChild(rootnode, dictnode);
1944 
1945     char *fname = buildname(basedir, "lib.plist"); // Build the file name.
1946     xmlSaveFormatFileEnc(fname, plistdoc, "UTF-8", 1); // Store the document.
1947     free(fname); fname = NULL;
1948     xmlFreeDoc(plistdoc); // Free the memory.
1949     xmlCleanupParser();
1950 #endif
1951 return( true );
1952 }
1953 
UFOOutputFeatures(const char * basedir,SplineFont * sf,int version)1954 static int UFOOutputFeatures(const char *basedir, SplineFont *sf, int version) {
1955     char *fname = buildname(basedir,"features.fea");
1956     FILE *feats = fopen( fname, "w" );
1957     int err;
1958 
1959     free(fname);
1960     if ( feats==NULL )
1961 return( false );
1962     FeatDumpFontLookups(feats,sf);
1963     err = ferror(feats);
1964     fclose(feats);
1965 return( !err );
1966 }
1967 
WriteUFOLayer(const char * glyphdir,SplineFont * sf,int layer,int version)1968 int WriteUFOLayer(const char * glyphdir, SplineFont * sf, int layer, int version) {
1969     xmlDocPtr plistdoc = PlistInit(); if (plistdoc == NULL) return false; // Make the document.
1970     xmlNodePtr rootnode = xmlDocGetRootElement(plistdoc); if (rootnode == NULL) { xmlFreeDoc(plistdoc); return false; } // Find the root node.
1971     xmlNodePtr dictnode = xmlNewChild(rootnode, NULL, BAD_CAST "dict", NULL); if (dictnode == NULL) { xmlFreeDoc(plistdoc); return false; } // Make the dict.
1972 
1973     GFileMkDir( glyphdir, 0755 );
1974     int i;
1975     SplineChar * sc;
1976     int err = 0;
1977     for ( i=0; i<sf->glyphcnt; ++i ) if ( SCLWorthOutputtingOrHasData(sc=sf->glyphs[i], layer) ||
1978       ( layer == ly_fore && (SCWorthOutputting(sc) || SCHasData(sc) || (sc != NULL && sc->glif_name != NULL)) ) ) {
1979         // TODO: Optionally skip rewriting an untouched glyph.
1980         // Do we track modified glyphs carefully enough for this?
1981         char * final_name = smprintf("%s%s%s", "", sc->glif_name, ".glif");
1982         if (final_name != NULL) { // Generate the final name with prefix and suffix.
1983 		PListAddString(dictnode,sc->name,final_name); // Add the glyph to the table of contents.
1984 		err |= !GlifDump(glyphdir,final_name,sc,layer,version);
1985         	free(final_name); final_name = NULL;
1986 	} else {
1987 		err |= 1;
1988 	}
1989     }
1990 
1991     char *fname = buildname(glyphdir, "contents.plist"); // Build the file name for the contents.
1992     xmlSaveFormatFileEnc(fname, plistdoc, "UTF-8", 1); // Store the document.
1993     free(fname); fname = NULL;
1994     xmlFreeDoc(plistdoc); // Free the memory.
1995     xmlCleanupParser();
1996     if (err) {
1997 	LogError(_("Error in WriteUFOLayer."));
1998     }
1999     return err;
2000 }
2001 
WriteUFOFontFlex(const char * basedir,SplineFont * sf,enum fontformat ff,int flags,const EncMap * map,int layer,int all_layers,int version)2002 int WriteUFOFontFlex(const char *basedir, SplineFont *sf, enum fontformat ff, int flags,
2003 	const EncMap *map, int layer, int all_layers, int version) {
2004     char *glyphdir, *gfname;
2005     int err;
2006     FILE *plist;
2007     int i;
2008     SplineChar *sc;
2009 
2010     /* Clean it out, if it exists */
2011     if (!GFileRemove(basedir, true)) {
2012         LogError(_("Error clearing %s."), basedir);
2013     }
2014 
2015     /* Create it */
2016     if (GFileMkDir( basedir, 0755 ) == -1) return false;
2017 
2018     locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
2019     switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
2020 
2021 
2022     err  = !UFOOutputMetaInfo(basedir,sf,version);
2023     err |= !UFOOutputFontInfo(basedir,sf,layer,version);
2024     err |= !UFOOutputGroups(basedir,sf,version);
2025     err |= !UFOOutputKerning2(basedir,sf,0,version); // Horizontal.
2026     err |= !UFOOutputKerning2(basedir,sf,1,version); // Vertical.
2027     err |= !UFOOutputLib(basedir,sf,version);
2028     err |= !UFOOutputFeatures(basedir,sf,version);
2029 
2030     if ( err ) {
2031         switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
2032         return false;
2033     }
2034 
2035     struct glif_name_index * glif_name_hash = glif_name_index_new();
2036 
2037     // First we generate glif names.
2038     for ( i=0; i<sf->glyphcnt; ++i ) if ( SCWorthOutputting(sc=sf->glyphs[i]) || SCHasData(sf->glyphs[i]) ) {
2039         char * startname = NULL;
2040         if (sc->glif_name != NULL)
2041           startname = strdup(sc->glif_name); // If the splinechar has a glif name, try to use that.
2042         else
2043           startname = ufo_name_mangle(sc->name, "", ".glif", 7); // If not, call the mangler.
2044         // Number the name (as the specification requires) if there is a collision.
2045         // And add it to the hash table with its index.
2046         char * numberedname = ufo_name_number(glif_name_hash, i, startname, "", ".glif", 7);
2047         free(startname); startname = NULL;
2048         // We update the saved glif_name only if it is different (so as to minimize churn).
2049         if ((sc->glif_name != NULL) && (strcmp(sc->glif_name, numberedname) != 0)) {
2050           free(sc->glif_name); sc->glif_name = NULL;
2051 	}
2052         if (sc->glif_name == NULL) {
2053           sc->glif_name = numberedname;
2054         } else {
2055 	  free(numberedname);
2056 	}
2057 	numberedname = NULL;
2058     }
2059     glif_name_index_destroy(glif_name_hash); // Close the hash table.
2060 
2061     struct glif_name_index * layer_name_hash = glif_name_index_new(); // Open the hash table.
2062     struct glif_name_index * layer_path_hash = glif_name_index_new(); // Open the hash table.
2063 
2064     switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
2065     xmlDocPtr plistdoc = PlistInit(); if (plistdoc == NULL) return false; // Make the document.
2066     xmlNodePtr rootnode = xmlDocGetRootElement(plistdoc); if (rootnode == NULL) { xmlFreeDoc(plistdoc); return false; } // Find the root node.
2067     xmlNodePtr arraynode = xmlNewChild(rootnode, NULL, BAD_CAST "array", NULL); if (arraynode == NULL) { xmlFreeDoc(plistdoc); return false; } // Make the dict.
2068     switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
2069     int layer_pos;
2070     for (layer_pos = (all_layers ? 0 : ly_fore); layer_pos < (all_layers ? sf->layer_cnt : ly_fore+1); layer_pos++) {
2071       // We don't want to emit the default background layer unless it has stuff in it or was in the input U. F. O..
2072       if (layer_pos == ly_back && !LayerWorthOutputting(sf, layer_pos) && sf->layers[layer_pos].ufo_path == NULL) continue;
2073       // We start building the layer contents entry.
2074       xmlNodePtr layernode = xmlNewChild(arraynode, NULL, BAD_CAST "array", NULL);
2075       // We make the layer name.
2076       char * layer_name_start = NULL;
2077       if (layer_pos == ly_fore) layer_name_start = "public.default";
2078       else if (layer_pos == ly_back) layer_name_start = "public.background";
2079       else layer_name_start = sf->layers[layer_pos].name;
2080       if (layer_name_start == NULL) layer_name_start = "unnamed"; // The remangle step adds any needed numbers.
2081       char * numberedlayername = ufo_name_number(layer_name_hash, layer_pos, layer_name_start, "", "", 7);
2082       // We make the layer path.
2083       char * layer_path_start = NULL;
2084       char * numberedlayerpath = NULL;
2085       char * numberedlayerpathwithglyphs = NULL;
2086       int name_err = 0;
2087       if (layer_pos == ly_fore) {
2088         numberedlayerpath = strdup("glyphs");
2089         numberedlayerpathwithglyphs = copy(numberedlayerpath);
2090       } else if (sf->layers[layer_pos].ufo_path != NULL) {
2091         layer_path_start = strdup(sf->layers[layer_pos].ufo_path);
2092         numberedlayerpath = ufo_name_number(layer_path_hash, layer_pos, layer_path_start, "", "", 7);
2093         numberedlayerpathwithglyphs = copy(numberedlayerpath);
2094       } else {
2095         layer_path_start = ufo_name_mangle(sf->layers[layer_pos].name, "glyphs.", "", 7);
2096         numberedlayerpath = ufo_name_number(layer_path_hash, layer_pos, layer_path_start, "glyphs.", "", 7);
2097         numberedlayerpathwithglyphs = smprintf("glyphs.%s", numberedlayerpath);
2098       }
2099       if (layer_path_start != NULL) { free(layer_path_start); layer_path_start = NULL; }
2100       if (name_err) {
2101         err |= name_err;
2102       } else {
2103         // We write to the layer contents.
2104         xmlNewTextChild(layernode, NULL, BAD_CAST "string", numberedlayername);
2105         xmlNewTextChild(layernode, NULL, BAD_CAST "string", numberedlayerpathwithglyphs);
2106         glyphdir = buildname(basedir, numberedlayerpathwithglyphs);
2107         // We write the glyph directory.
2108         err |= WriteUFOLayer(glyphdir, sf, layer_pos, version);
2109       }
2110       free(numberedlayername); numberedlayername = NULL;
2111       free(numberedlayerpath); numberedlayerpath = NULL;
2112       free(numberedlayerpathwithglyphs); numberedlayerpathwithglyphs = NULL;
2113       free(glyphdir); glyphdir = NULL;
2114     }
2115     char *fname = buildname(basedir, "layercontents.plist"); // Build the file name for the contents.
2116     if (version >= 3)
2117       xmlSaveFormatFileEnc(fname, plistdoc, "UTF-8", 1); // Store the document.
2118     free(fname); fname = NULL;
2119     xmlFreeDoc(plistdoc); // Free the memory.
2120     xmlCleanupParser();
2121     glif_name_index_destroy(layer_name_hash); // Close the hash table.
2122     glif_name_index_destroy(layer_path_hash); // Close the hash table.
2123 
2124     switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
2125     return !err;
2126 }
2127 
SplineFontHasUFOLayerNames(SplineFont * sf)2128 int SplineFontHasUFOLayerNames(SplineFont *sf) {
2129   if (sf == NULL || sf->layers == NULL) return 0;
2130   int layer_pos = 0;
2131   for (layer_pos = 0; layer_pos < sf->layer_cnt; layer_pos++) {
2132     if (sf->layers[layer_pos].ufo_path != NULL) return 1;
2133   }
2134   return 0;
2135 }
2136 
WriteUFOFont(const char * basedir,SplineFont * sf,enum fontformat ff,int flags,const EncMap * map,int layer,int version)2137 int WriteUFOFont(const char *basedir, SplineFont *sf, enum fontformat ff, int flags,
2138 	const EncMap *map, int layer, int version) {
2139   if (SplineFontHasUFOLayerNames(sf))
2140     return WriteUFOFontFlex(basedir, sf, ff, flags, map, layer, 1, version);
2141   else
2142     return WriteUFOFontFlex(basedir, sf, ff, flags, map, layer, 0, version);
2143 }
2144 
2145 /* ************************************************************************** */
2146 /* *****************************    UFO Input    **************************** */
2147 /* ************************************************************************** */
2148 
get_thingy(FILE * file,char * buffer,char * tag)2149 static char *get_thingy(FILE *file,char *buffer,char *tag) {
2150     int ch;
2151     char *pt;
2152 
2153     for (;;) {
2154 	while ( (ch=getc(file))!='<' && ch!=EOF );
2155 	if ( ch==EOF )
2156 return( NULL );
2157 	while ( (ch=getc(file))!=EOF && isspace(ch) );
2158 	pt = tag;
2159 	while ( ch==*pt || tolower(ch)==*pt ) {
2160 	    ++pt;
2161 	    ch = getc(file);
2162 	}
2163 	if ( *pt=='\0' )
2164     continue;
2165 	if ( ch==EOF )
2166 return( NULL );
2167 	while ( isspace(ch)) ch=getc(file);
2168 	if ( ch!='>' )
2169     continue;
2170 	pt = buffer;
2171 	while ( (ch=getc(file))!='<' && ch!=EOF && pt<buffer+1000)
2172 	    *pt++ = ch;
2173 	*pt = '\0';
2174 return( buffer );
2175     }
2176 }
2177 
NamesReadUFO(char * filename)2178 char **NamesReadUFO(char *filename) {
2179     char *fn = buildname(filename,"fontinfo.plist");
2180     FILE *info = fopen(fn,"r");
2181     char buffer[1024];
2182     char **ret;
2183 
2184     free(fn);
2185     if ( info==NULL )
2186 return( NULL );
2187     while ( get_thingy(info,buffer,"key")!=NULL ) {
2188 	if ( strcmp(buffer,"fontName")!=0 ) {
2189 	    if ( get_thingy(info,buffer,"string")!=NULL ) {
2190 		ret = calloc(2,sizeof(char *));
2191 		ret[0] = copy(buffer);
2192 		fclose(info);
2193 return( ret );
2194 	    }
2195 	    fclose(info);
2196 return( NULL );
2197 	}
2198     }
2199     fclose(info);
2200 return( NULL );
2201 }
2202 
FindNode(xmlNodePtr kids,char * name)2203 static xmlNodePtr FindNode(xmlNodePtr kids,char *name) {
2204     while ( kids!=NULL ) {
2205 	if ( xmlStrcmp(kids->name,(const xmlChar *) name)== 0 )
2206 return( kids );
2207 	kids = kids->next;
2208     }
2209 return( NULL );
2210 }
2211 
2212 #ifndef _NO_PYTHON
2213 static PyObject *XMLEntryToPython(xmlDocPtr doc, xmlNodePtr entry, int has_lists);
2214 
LibToPython(xmlDocPtr doc,xmlNodePtr dict,int has_lists)2215 static PyObject *LibToPython(xmlDocPtr doc, xmlNodePtr dict, int has_lists) {
2216 	// This function is responsible for parsing keys in dicts.
2217     PyObject *pydict = PyDict_New();
2218     PyObject *item = NULL;
2219     xmlNodePtr keys, temp;
2220 
2221 	// Get the first item, then iterate through all items in the dict.
2222     for ( keys=dict->children; keys!=NULL; keys=keys->next ) {
2223 		// See that the item is in fact a key.
2224 		if ( xmlStrcmp(keys->name,(const xmlChar *) "key")== 0 ) {
2225 			// Fetch the key name, which, according to the libxml specification, is the first child of the key entry.
2226 			char *keyname = (char *) xmlNodeListGetString(doc,keys->children,true);
2227 			// In a property list, the value entry is a sibling of the key entry. The value itself is a child.
2228 			// Iterate through the following siblings (including keys (!)) until we find a text entry.
2229 			for ( temp=keys->next; temp!=NULL; temp=temp->next ) {
2230 				if ( xmlStrcmp(temp->name,(const xmlChar *) "text")!=0 ) break;
2231 			}
2232 			// Convert the X.M.L. entry into a Python object.
2233 			item = NULL;
2234 			if ( temp!=NULL) item = XMLEntryToPython(doc,temp,has_lists);
2235 			if ( item!=NULL ) PyDict_SetItemString(pydict, keyname, item );
2236 			if ( temp==NULL ) break;
2237 			else if ( xmlStrcmp(temp->name,(const xmlChar *) "key")!=0 ) keys = temp;
2238 			// If and only if the parsing succeeds, jump over any entries we read when searching for a text block.
2239 			free(keyname);
2240 		}
2241     }
2242 return( pydict );
2243 }
2244 
XMLEntryToPython(xmlDocPtr doc,xmlNodePtr entry,int has_lists)2245 static PyObject *XMLEntryToPython(xmlDocPtr doc,xmlNodePtr entry, int has_lists) {
2246     char *contents;
2247 
2248     if ( xmlStrcmp(entry->name,(const xmlChar *) "true")==0 ) {
2249 	Py_INCREF(Py_True);
2250 return( Py_True );
2251     }
2252     if ( xmlStrcmp(entry->name,(const xmlChar *) "false")==0 ) {
2253 	Py_INCREF(Py_False);
2254 return( Py_False );
2255     }
2256     if ( xmlStrcmp(entry->name,(const xmlChar *) "none")==0 ) {
2257 	Py_INCREF(Py_None);
2258 return( Py_None );
2259     }
2260 
2261     if ( xmlStrcmp(entry->name,(const xmlChar *) "dict")==0 )
2262 return( LibToPython(doc,entry, has_lists));
2263     if ( xmlStrcmp(entry->name,(const xmlChar *) "array")==0 ) {
2264 	xmlNodePtr sub;
2265 	int cnt;
2266 	PyObject *ret, *item;
2267 	/* I'm translating "Arrays" as tuples... not sure how to deal with */
2268 	/*  actual python arrays. But these look more like tuples than arrays*/
2269 	/*  since each item gets its own type */
2270 
2271 	for ( cnt=0, sub=entry->children; sub!=NULL; sub=sub->next ) {
2272 	    if ( xmlStrcmp(sub->name,(const xmlChar *) "text")==0 )
2273 	continue;
2274 	    ++cnt;
2275 	}
2276 	ret = ( has_lists ? PyList_New(cnt) : PyTuple_New(cnt) );
2277 	for ( cnt=0, sub=entry->children; sub!=NULL; sub=sub->next ) {
2278 	    if ( xmlStrcmp(sub->name,(const xmlChar *) "text")==0 )
2279 	continue;
2280 	    item = XMLEntryToPython(doc,sub,has_lists);
2281 	    if ( item==NULL ) {
2282 		item = Py_None;
2283 		Py_INCREF(item);
2284 	    }
2285 	    if (has_lists) PyList_SetItem(ret,cnt,item);
2286 	    else PyTuple_SetItem(ret,cnt,item);
2287 	    ++cnt;
2288 	}
2289 return( ret );
2290     }
2291     if ((entry->children != NULL) && ((contents = (char *) xmlNodeListGetString(doc,entry->children,true)) != NULL) &&
2292        (( xmlStrcmp(entry->name,(const xmlChar *) "integer")==0 ) || ( xmlStrcmp(entry->name,(const xmlChar *) "real")==0 ) ||
2293        ( xmlStrcmp(entry->name,(const xmlChar *) "string")==0 ))) {
2294       contents = (char *) xmlNodeListGetString(doc,entry->children,true);
2295       if ( xmlStrcmp(entry->name,(const xmlChar *) "integer")==0 ) {
2296 	long val = strtol(contents,NULL,0);
2297 	free(contents);
2298 return( Py_BuildValue("i",val));
2299       }
2300       if ( xmlStrcmp(entry->name,(const xmlChar *) "real")==0 ) {
2301 	double val = strtod(contents,NULL);
2302 	free(contents);
2303 return( Py_BuildValue("d",val));
2304       }
2305       if ( xmlStrcmp(entry->name,(const xmlChar *) "string")==0 ) {
2306 	PyObject *ret = Py_BuildValue("s",contents);
2307 	free(contents);
2308 return( ret );
2309       }
2310 
2311       free( contents );
2312     }
2313     if (has_lists) {
2314       // This special handler for unrecognized content depends
2315       // on the fact that there's no X. M. L. equivalent for the Python tuple.
2316       xmlBufferPtr buf = xmlBufferCreate(); // Create a buffer for dumping this portion of the tree.
2317       xmlNodeDump(buf, doc, entry, 0, 0); // Dump the tree into the buffer.
2318       const xmlChar* tmpcontent = xmlBufferContent(buf); // Get the string from the buffer.
2319       PyObject* ret = PyTuple_New(3); // Make a tuple in the Python tree.
2320       PyTuple_SetItem(ret, 0, PyBytes_FromString((const char*)entry->name)); // Store the node name.
2321       PyTuple_SetItem(ret, 1, PyBytes_FromString((const char*)tmpcontent)); // Store the node content.
2322       PyTuple_SetItem(ret, 2, Py_None);
2323       Py_INCREF(Py_None);
2324       xmlBufferFree(buf);
2325       return ret;
2326     }
2327     LogError(_("Unknown python type <%s> when reading UFO/GLIF lib data."), (char *) entry->name);
2328 return( NULL );
2329 }
2330 #endif
2331 
GlifParseHints(xmlDocPtr doc,xmlNodePtr dict,char * hinttype)2332 static StemInfo *GlifParseHints(xmlDocPtr doc,xmlNodePtr dict,char *hinttype) {
2333     StemInfo *head=NULL, *last=NULL, *h;
2334     xmlNodePtr keys, array, kids, poswidth,temp;
2335     double pos, width;
2336 
2337     for ( keys=dict->children; keys!=NULL; keys=keys->next ) {
2338 	if ( xmlStrcmp(keys->name,(const xmlChar *) "key")== 0 ) {
2339 	    char *keyname = (char *) xmlNodeListGetString(doc,keys->children,true);
2340 	    int found = strcmp(keyname,hinttype)==0;
2341 	    free(keyname);
2342 	    if ( found ) {
2343 		for ( array=keys->next; array!=NULL; array=array->next ) {
2344 		    if ( xmlStrcmp(array->name,(const xmlChar *) "array")==0 )
2345 		break;
2346 		}
2347 		if ( array!=NULL ) {
2348 		    for ( kids = array->children; kids!=NULL; kids=kids->next ) {
2349 			if ( xmlStrcmp(kids->name,(const xmlChar *) "dict")==0 ) {
2350 			    pos = -88888888; width = 0;
2351 			    for ( poswidth=kids->children; poswidth!=NULL; poswidth=poswidth->next ) {
2352 				if ( xmlStrcmp(poswidth->name,(const xmlChar *) "key")==0 ) {
2353 				    char *keyname2 = (char *) xmlNodeListGetString(doc,poswidth->children,true);
2354 				    int ispos = strcmp(keyname2,"position")==0, iswidth = strcmp(keyname2,"width")==0;
2355 				    double value;
2356 				    free(keyname2);
2357 				    for ( temp=poswidth->next; temp!=NULL; temp=temp->next ) {
2358 					if ( xmlStrcmp(temp->name,(const xmlChar *) "text")!=0 )
2359 				    break;
2360 				    }
2361 				    if ( temp!=NULL ) {
2362 					char *valname = (char *) xmlNodeListGetString(doc,temp->children,true);
2363 					if ( xmlStrcmp(temp->name,(const xmlChar *) "integer")==0 )
2364 					    value = strtol(valname,NULL,10);
2365 					else if ( xmlStrcmp(temp->name,(const xmlChar *) "real")==0 )
2366 					    value = strtod(valname,NULL);
2367 					else
2368 					    ispos = iswidth = false;
2369 					free(valname);
2370 					if ( ispos )
2371 					    pos = value;
2372 					else if ( iswidth )
2373 					    width = value;
2374 					poswidth = temp;
2375 				    }
2376 				}
2377 			    }
2378 			    if ( pos!=-88888888 && width!=0 ) {
2379 				h = chunkalloc(sizeof(StemInfo));
2380 			        h->start = pos;
2381 			        h->width = width;
2382 			        if ( width==-20 || width==-21 )
2383 				    h->ghost = true;
2384 				if ( head==NULL )
2385 				    head = last = h;
2386 				else {
2387 				    last->next = h;
2388 			            last = h;
2389 				}
2390 			    }
2391 			}
2392 		    }
2393 		}
2394 	    }
2395 	}
2396     }
2397 return( head );
2398 }
2399 
UFOLoadAnchor(SplineFont * sf,SplineChar * sc,xmlNodePtr xmlAnchor,AnchorPoint ** lastap)2400 static AnchorPoint *UFOLoadAnchor(SplineFont *sf, SplineChar *sc, xmlNodePtr xmlAnchor, AnchorPoint **lastap) {
2401         xmlNodePtr points = xmlAnchor;
2402         char *sname = (char *) xmlGetProp(points, (xmlChar *) "name");
2403         if ( sname!=NULL) {
2404 
2405             /* make an AP and if necessary an AC */
2406             AnchorPoint *ap = chunkalloc(sizeof(AnchorPoint));
2407             AnchorClass *ac;
2408             char *namep = *sname=='_' ? sname + 1 : sname;
2409             char *xs = (char *) xmlGetProp(points, (xmlChar *) "x");
2410             char *ys = (char *) xmlGetProp(points, (xmlChar *) "y");
2411             if (xs) { ap->me.x = strtod(xs,NULL); free(xs); }
2412             if (ys) { ap->me.y = strtod(ys,NULL); free(ys); }
2413 
2414             ac = SFFindOrAddAnchorClass(sf,namep,NULL);
2415             if (*sname=='_')
2416                 ap->type = ac->type==act_curs ? at_centry : at_mark;
2417             else
2418                 ap->type = ac->type==act_mkmk   ? at_basemark :
2419                             ac->type==act_curs  ? at_cexit :
2420                             ac->type==act_mklg  ? at_baselig :
2421                                                   at_basechar;
2422             ap->anchor = ac;
2423 	    if ( *lastap==NULL ) {
2424 			// If there are no existing anchors, we point the main spline reference to this one.
2425 			sc->anchor = ap;
2426 	    } else {
2427 			// If there are existing anchors, we attach to the last one.
2428 			(*lastap)->next = ap;
2429 	    }
2430 	    *lastap = ap;
2431 
2432             free(sname);
2433             return ap;
2434         }
2435         return NULL;
2436 }
2437 
PointsSame(BasePoint p0,BasePoint p1)2438 static real PointsSame(BasePoint p0, BasePoint p1) {
2439 	if (p0.x == p1.x && p0.y == p1.y)
2440 		return 1;
2441 	return 0;
2442 }
2443 
GuidelineToSpline(SplineFont * sf,GuidelineSet * gl)2444 static SplineSet *GuidelineToSpline(SplineFont *sf, GuidelineSet *gl) {
2445 	SplinePoint *sp1, *sp2;
2446 	SplineSet *ss;
2447 	real emsize = sf->ascent+sf->descent;
2448 	// fprintf(stderr, "Em: %g.\n", emsize);
2449 	real angle_radians = acos(-1)*gl->angle/180;
2450 	real x_off = emsize*cos(angle_radians);
2451 	real y_off = emsize*sin(angle_radians);
2452 	// fprintf(stderr, "Offsets: %g, %g.\n", x_off, y_off);
2453 	sp1 = SplinePointCreate(gl->point.x-x_off,gl->point.y-y_off);
2454 	if (gl->name != NULL)
2455 		sp1->name = copy(gl->name);
2456 	sp2 = SplinePointCreate(gl->point.x+x_off,gl->point.y+y_off);
2457 	SplineMake(sp1,sp2,sf->grid.order2);
2458 	ss = chunkalloc(sizeof(SplineSet));
2459 	ss->first = sp1; ss->last = sp2;
2460 	return ss;
2461 }
2462 
UFOLoadGuideline(SplineFont * sf,SplineChar * sc,int layer,xmlDocPtr doc,xmlNodePtr xmlGuideline,GuidelineSet ** lastgl,SplinePointList ** lastspl)2463 static void *UFOLoadGuideline(SplineFont *sf, SplineChar *sc, int layer, xmlDocPtr doc, xmlNodePtr xmlGuideline, GuidelineSet **lastgl, SplinePointList **lastspl) {
2464 	// It is easier to use the free mechanism for the guideline to clean up than to do it manually.
2465 	// So we create one speculatively and then check whether it is valid.
2466 	GuidelineSet *gl = chunkalloc(sizeof(GuidelineSet));
2467 	char *xs = NULL;
2468 	char *ys = NULL;
2469 	char *as = NULL;
2470 	char *colors = NULL;
2471 	if (xmlStrcmp(xmlGuideline->name, (const xmlChar *)"guideline") == 0) {
2472 		// Guidelines in the glif have attributes.
2473 		gl->name = (char *) xmlGetProp(xmlGuideline, (xmlChar *) "name");
2474 		gl->identifier = (char *) xmlGetProp(xmlGuideline, (xmlChar *) "identifier");
2475 		xs = (char *) xmlGetProp(xmlGuideline, (xmlChar *) "x");
2476 		ys = (char *) xmlGetProp(xmlGuideline, (xmlChar *) "y");
2477 		as = (char *) xmlGetProp(xmlGuideline, (xmlChar *) "angle");
2478 		colors = (char *) xmlGetProp(xmlGuideline, (xmlChar *) "color");
2479 	} else if (xmlStrcmp(xmlGuideline->name, (const xmlChar *)"dict") == 0) {
2480 		// fprintf(stderr, "Got global guideline definition.\n");
2481 		// Guidelines in fontinfo.plist are in a dictionary format.
2482 		xmlNodePtr dictnode = xmlGuideline;
2483 		{
2484 		    xmlNodePtr keynode = NULL;
2485 		    for (keynode=dictnode->children; keynode!=NULL; keynode=keynode->next ) {
2486 			if (xmlStrcmp(keynode->name, (const xmlChar *)"key") == 0) {
2487 			    // fprintf(stderr, "Got key.\n");
2488 			    char *keyname2 = (char *) xmlNodeListGetString(doc,keynode->children,true);
2489 			    if (keyname2 != NULL) {
2490 				    // Skip unstructured data.
2491 				    xmlNodePtr valnode = NULL;
2492 				    for ( valnode=keynode->next; valnode!=NULL; valnode=valnode->next ) {
2493 					if ( xmlStrcmp(valnode->name,(const xmlChar *) "text")!=0 )
2494 				    break;
2495 				    }
2496 				    char *valtext = valnode->children ?
2497 					(char *) xmlNodeListGetString(doc,valnode->children,true) : NULL;
2498 				    if (valtext != NULL) {
2499 					if (xmlStrcmp(valnode->name, (const xmlChar *)"string") == 0) {
2500 						// Parse strings.
2501 						if (gl->name == NULL && strcmp(keyname2,"name") == 0)
2502 						    gl->name = valtext;
2503 						else if (gl->identifier == NULL && strcmp(keyname2,"identifier") == 0)
2504 						    gl->identifier = valtext;
2505 						else if (colors == NULL && strcmp(keyname2,"color") == 0)
2506 						    colors = valtext;
2507 						else {
2508 						    // Free the temporary value if not assigned to a field.
2509 						    free(valtext);
2510 						    valtext = NULL;
2511 						}
2512 					} else if (xmlStrcmp(valnode->name, (const xmlChar *)"integer") == 0 ||
2513 							xmlStrcmp(valnode->name, (const xmlChar *)"real") == 0 ||
2514 							xmlStrcmp(valnode->name, (const xmlChar *)"float") == 0) {
2515 						// Parse numbers.
2516 						if (xs == NULL && strcmp(keyname2,"x") == 0)
2517 						    xs = valtext;
2518 						else if (ys == NULL && strcmp(keyname2,"y") == 0)
2519 						    ys = valtext;
2520 						else if (as == NULL && strcmp(keyname2,"angle") == 0)
2521 						    as = valtext;
2522 						else {
2523 						    // Free the temporary value if not assigned to a field.
2524 						    free(valtext);
2525 						    valtext = NULL;
2526 						}
2527 					} else {
2528 					    // Free the temporary value if not assigned to a field.
2529 					    free(valtext);
2530 					    valtext = NULL;
2531 					}
2532 				    }
2533 				    free(keyname2);
2534 				    keyname2 = NULL;
2535 			    }
2536 			}
2537 		    }
2538 		}
2539 	}
2540 	int what_is_defined = 0x0;
2541 	if (xs) { gl->point.x = strtod(xs,NULL); what_is_defined |= 0x1; xmlFree(xs); xs = NULL; }
2542 	if (ys) { gl->point.y = strtod(ys,NULL); what_is_defined |= 0x2; xmlFree(ys); ys = NULL; }
2543 	if (as) { gl->angle = strtod(as,NULL); what_is_defined |= 0x4; xmlFree(as); as = NULL; }
2544 	if (colors) {
2545 		// fprintf(stderr, "Checking color.\n");
2546 		// The color arrives as a set of 0-1-range values for RGBA in a string.
2547 		// We need to repack those into a single 32-bit word.
2548 		what_is_defined |= 0x20;
2549 		gl->flags |= 0x20;
2550 		int colori;
2551 		off_t colorp, colorps;
2552 		double colorv;
2553 		for (colori = 0; colori < 4; colori++) {
2554 			while (colors[colorp] == ' ' || colors[colorp] == ',') colorp++;
2555 			colorps = colorp;
2556 			while (colors[colorp] >= '0' && colors[colorp] <= '9') colorp++;
2557 			if (colorp > colorps) {
2558 				char *after_color = NULL;
2559 				colorv = strtod(colors + colorps, &after_color);
2560 				if (after_color != colors + colorp)
2561 					LogError(_("Error parsing color component.\n"));
2562 				gl->color |= (((uint32)(colorv * 255.0)) << (8 * (4 - colori)));
2563 			} else {
2564 				LogError(_("Missing color component.\n"));
2565 			}
2566 		}
2567 		xmlFree(colors);
2568 		colors = NULL;
2569 	}
2570 	// fprintf(stderr, "Checking validity.\n");
2571 	// fprintf(stderr, "Definition flags: %x.\n", what_is_defined);
2572 	// fprintf(stderr, "x: %g, y: %f, angle: %f.\n", gl->point.x, gl->point.y, gl->angle);
2573 	if (
2574 		(fmod(gl->angle, 180) && !(what_is_defined & 0x1)) || // Non-horizontal guideline without x.
2575 		(fmod(gl->angle + 90, 180) && !(what_is_defined & 0x2)) || // Non-vertical guideline without y.
2576 		isnan(gl->point.x) || isnan(gl->point.y) || isnan(gl->angle) ||
2577 		isinf(gl->point.x) || isinf(gl->point.y) || isinf(gl->angle)
2578 	) {
2579 		// Invalid data; abort.
2580 		// fprintf(stderr, "Invalid guideline.\n");
2581 		LogError(_("Invalid guideline.\n"));
2582 		GuidelineSetFree(gl);
2583 		gl = NULL;
2584 		return NULL;
2585 	}
2586 	// If the guideline is valid but not all values are defined, we flag it as using abbreviated syntax.
2587 	if ((what_is_defined & 7) != 7)
2588 		gl->flags |= 0x10;
2589 	// fprintf(stderr, "Setting reference.\n");
2590 	if (sc) {
2591 		if ( *lastgl==NULL ) {
2592 			// If there are no existing guidelines, we point the main reference to this one.
2593 			sc->layers[layer].guidelines = gl;
2594 		} else {
2595 			// If there are existing anchors, we attach to the last one.
2596 			(*lastgl)->next = gl;
2597 		}
2598 		*lastgl = gl;
2599 		return (void *)gl;
2600 	} else {
2601 		// fprintf(stderr, "Creating spline for guideline.");
2602 		// Convert from a guideline to a spline.
2603 		SplineSet *spl = GuidelineToSpline(sf, gl);
2604 		// Free the guideline.
2605 		GuidelineSetFree(gl);
2606 		// Attach the spline.
2607 		if ( *lastspl==NULL ) {
2608 			// fprintf(stderr, "First guideline spline.");
2609 			// If there are no existing guidelines, we point the main reference to this one.
2610 			sf->grid.splines = spl;
2611 		} else {
2612 			// fprintf(stderr, "Not first guideline spline.");
2613 			// If there are existing anchors, we attach to the last one.
2614 			(*lastspl)->next = spl;
2615 		}
2616 		*lastspl = spl;
2617 		return (void *)spl;
2618 	}
2619 }
2620 
UFOLoadGuidelines(SplineFont * sf,SplineChar * sc,int layer,xmlDocPtr doc,xmlNodePtr xmlGuidelines,GuidelineSet ** lastgl,SplinePointList ** lastspl)2621 static void UFOLoadGuidelines(SplineFont *sf, SplineChar *sc, int layer, xmlDocPtr doc, xmlNodePtr xmlGuidelines, GuidelineSet **lastgl, SplinePointList **lastspl) {
2622 	if (xmlStrcmp(xmlGuidelines->name, (const xmlChar *)"array") == 0) {
2623 		// fprintf(stderr, "Got global guidelines array.\n");
2624 		// Guidelines in fontinfo.plist are in a dictionary format.
2625 		xmlNodePtr array = xmlGuidelines;
2626 		if ( array!=NULL ) {
2627 			xmlNodePtr dictnode = NULL;
2628 			for ( dictnode = array->children; dictnode != NULL; dictnode = dictnode->next )
2629 				if (xmlStrcmp(dictnode->name, (const xmlChar *)"dict") == 0)
2630 					if (!UFOLoadGuideline(sf, sc, layer, doc, dictnode, lastgl, lastspl))
2631 						LogError(_("Failed to read guideline."));
2632 		}
2633 	}
2634 }
2635 
_UFOLoadGlyph(SplineFont * sf,xmlDocPtr doc,char * glifname,char * glyphname,SplineChar * existingglyph,int layerdest)2636 static SplineChar *_UFOLoadGlyph(SplineFont *sf, xmlDocPtr doc, char *glifname, char* glyphname, SplineChar* existingglyph, int layerdest) {
2637     xmlNodePtr glyph, kids, contour, points;
2638     SplineChar *sc;
2639     xmlChar *format, *width, *height, *u;
2640     char *name, *tmpname;
2641     int uni;
2642     char *cpt;
2643     int newsc = 0;
2644 
2645     glyph = xmlDocGetRootElement(doc);
2646     format = xmlGetProp(glyph,(xmlChar *) "format");
2647     if ( xmlStrcmp(glyph->name,(const xmlChar *) "glyph")!=0 ||
2648 	    (format!=NULL && xmlStrcmp(format,(xmlChar *) "1")!=0 && xmlStrcmp(format,(xmlChar *) "2")!=0)) {
2649 		LogError(_("Expected glyph file with format==1 or 2"));
2650 		xmlFreeDoc(doc);
2651 		free(format);
2652 		return( NULL );
2653     }
2654 	free(format);
2655 	tmpname = (char *) xmlGetProp(glyph,(xmlChar *) "name");
2656 	if (glyphname != NULL) {
2657 		// We use the provided name from the glyph listing since the specification says to trust that one more.
2658 		name = copy(glyphname);
2659 		// But we still fetch the internally listed name for verification and fail on a mismatch.
2660 		if ((name == NULL) || ((name != NULL) && (tmpname != NULL) && (strcmp(glyphname, name) != 0))) {
2661 			LogError(_("Bad glyph name."));
2662 			if ( tmpname != NULL ) { free(tmpname); tmpname = NULL; }
2663 			if ( name != NULL ) { free(name); name = NULL; }
2664 			xmlFreeDoc(doc);
2665 			return NULL;
2666 		}
2667 		if ( tmpname != NULL ) { free(tmpname); tmpname = NULL; }
2668 	} else {
2669 		name = tmpname;
2670 	}
2671     if ( name==NULL && glifname!=NULL ) {
2672 		char *pt = strrchr(glifname,'/');
2673 		name = copy(pt+1);
2674 		for ( pt=cpt=name; *cpt!='\0'; ++cpt ) {
2675 			if ( *cpt!='_' )
2676 			*pt++ = *cpt;
2677 			else if ( islower(*name))
2678 			*name = toupper(*name);
2679 		}
2680 		*pt = '\0';
2681     } else if ( name==NULL )
2682 		name = copy("nameless");
2683 	// We assign a placeholder name if no name exists.
2684 	// We create a new SplineChar
2685 	if (existingglyph != NULL) {
2686 		sc = existingglyph;
2687 		free(name); name = NULL;
2688 	} else {
2689     	sc = SplineCharCreate(2);
2690     	sc->name = name;
2691 		newsc = 1;
2692 	}
2693 	if (sc == NULL) {
2694 		xmlFreeDoc(doc);
2695 		return NULL;
2696 	}
2697 
2698 	// Check layer availability here.
2699 	if ( layerdest>=sc->layer_cnt ) {
2700 		sc->layers = realloc(sc->layers,(layerdest+1)*sizeof(Layer));
2701 		memset(sc->layers+sc->layer_cnt,0,(layerdest+1-sc->layer_cnt)*sizeof(Layer));
2702 		sc->layer_cnt = layerdest + 1;
2703 	}
2704 	if (sc->layers == NULL) {
2705 		if ((newsc == 1) && (sc != NULL)) {
2706 			SplineCharFree(sc);
2707 		}
2708 		xmlFreeDoc(doc);
2709 		return NULL;
2710 	}
2711 
2712     // We track the last splineset so that we can add to the end of the chain.
2713     SplineSet *last = sc->layers[layerdest].splines;
2714     while (last != NULL && last->next != NULL) last = last->next;
2715     // We track the last anchor point.
2716     AnchorPoint *lastap = sc->anchor;
2717     while (lastap != NULL && lastap->next != NULL) lastap = lastap->next;
2718     // We track the last guideline.
2719     GuidelineSet *lastgl = sc->layers[layerdest].guidelines;
2720     while (lastgl != NULL && lastgl->next != NULL) lastgl = lastgl->next;
2721     // We track the last reference.
2722     RefChar *lastref = sc->layers[layerdest].refs;
2723     while (lastref != NULL && lastref->next != NULL) lastref = lastref->next;
2724     for ( kids = glyph->children; kids!=NULL; kids=kids->next ) {
2725 	if ( xmlStrcmp(kids->name,(const xmlChar *) "advance")==0 ) {
2726 		if ((layerdest == ly_fore) || newsc) {
2727 			width = xmlGetProp(kids,(xmlChar *) "width");
2728 			height = xmlGetProp(kids,(xmlChar *) "height");
2729 			if ( width!=NULL )
2730 			sc->width = strtol((char *) width,NULL,10);
2731 			if ( height!=NULL )
2732 			sc->vwidth = strtol((char *) height,NULL,10);
2733 			sc->widthset = true;
2734 			free(width); free(height);
2735 		}
2736 	} else if ( xmlStrcmp(kids->name,(const xmlChar *) "unicode")==0 ) {
2737 		if ((layerdest == ly_fore) || newsc) {
2738 			u = xmlGetProp(kids,(xmlChar *) "hex");
2739 			uni = strtol((char *) u,NULL,16);
2740 			if ( sc->unicodeenc == -1 )
2741 			sc->unicodeenc = uni;
2742 			else
2743 			AltUniAdd(sc,uni);
2744 			free(u);
2745 		}
2746 	} else if ( xmlStrcmp(kids->name,(const xmlChar *) "note")==0 ) {
2747 		char *tval = xmlNodeListGetString(doc,kids->children,true);
2748 		if (tval != NULL) {
2749 			sc->comment = copy(tval);
2750 			free(tval);
2751 			tval = NULL;
2752 		}
2753 	} else if ( xmlStrcmp(kids->name,(const xmlChar *) "anchor")==0 ){
2754 		if (UFOLoadAnchor(sf, sc, kids, &lastap))
2755 			continue;
2756 	} else if ( xmlStrcmp(kids->name,(const xmlChar *) "guideline")==0 ){
2757 		if (UFOLoadGuideline(sf, sc, layerdest, doc, kids, &lastgl, NULL))
2758 			continue;
2759 	} else if ( xmlStrcmp(kids->name,(const xmlChar *) "outline")==0 ) {
2760 	    for ( contour = kids->children; contour!=NULL; contour=contour->next ) {
2761 		if ( xmlStrcmp(contour->name,(const xmlChar *) "component")==0 ) {
2762 		    // We have a reference.
2763 		    char *base = (char *) xmlGetProp(contour,(xmlChar *) "base"),
2764 			*xs = (char *) xmlGetProp(contour,(xmlChar *) "xScale"),
2765 			*ys = (char *) xmlGetProp(contour,(xmlChar *) "yScale"),
2766 			*xys = (char *) xmlGetProp(contour,(xmlChar *) "xyScale"),
2767 			*yxs = (char *) xmlGetProp(contour,(xmlChar *) "yxScale"),
2768 			*xo = (char *) xmlGetProp(contour,(xmlChar *) "xOffset"),
2769 			*yo = (char *) xmlGetProp(contour,(xmlChar *) "yOffset");
2770 		    RefChar *r;
2771 		    if ( base==NULL || strcmp(base,"") == 0 )
2772 				LogError(_("component with no base glyph"));
2773 		    else {
2774 				r = RefCharCreate();
2775 				r->sc = SplineCharCreate(0);
2776 				r->sc->name = base;
2777 				r->transform[0] = r->transform[3] = 1;
2778 				if ( xs!=NULL )
2779 					r->transform[0] = strtod(xs,NULL);
2780 				if ( ys!=NULL )
2781 					r->transform[3] = strtod(ys,NULL);
2782 				if ( xys!=NULL )
2783 					r->transform[1] = strtod(xys,NULL);
2784 				if ( yxs!=NULL )
2785 					r->transform[2] = strtod(yxs,NULL);
2786 				if ( xo!=NULL )
2787 					r->transform[4] = strtod(xo,NULL);
2788 				if ( yo!=NULL )
2789 					r->transform[5] = strtod(yo,NULL);
2790 
2791 				if ( lastref==NULL ) {
2792 				  // If there are no existing references, we point the main spline reference to this one.
2793 				  sc->layers[layerdest].refs = r;
2794 				} else {
2795 				  // If there are existing references, we attach to the last one.
2796 				  lastref->next = r;
2797 				}
2798 				lastref = r;
2799 		    }
2800 		    if (xs) free(xs); if (ys) free(ys); if (xys) free(xys); if (yxs) free(yxs); if (xo) free(xo); if (yo) free(yo);
2801 		} else if ( xmlStrcmp(contour->name,(const xmlChar *) "contour")==0 ) {
2802 		    xmlNodePtr npoints;
2803 
2804 			// We now look for anchor points.
2805             char *sname;
2806 
2807             for ( points=contour->children; points!=NULL; points=points->next )
2808                 if ( xmlStrcmp(points->name,(const xmlChar *) "point")==0 )
2809             break;
2810             for ( npoints=points->next; npoints!=NULL; npoints=npoints->next )
2811                 if ( xmlStrcmp(npoints->name,(const xmlChar *) "point")==0 )
2812             break;
2813 			// If the contour has a single point without another point after it, we assume it to be an anchor point.
2814             if ( points!=NULL && npoints==NULL ) {
2815               if (UFOLoadAnchor(sf, sc, points, &lastap))
2816                 continue; // We stop processing the contour at this point.
2817             }
2818 
2819 			// If we have not identified the contour as holding an anchor point, we continue processing it as a rendered shape.
2820 			SplineSet *ss;
2821 			SplinePoint *sp;
2822 			SplinePoint *sp2;
2823 			BasePoint pre[2], init[4];
2824 			int precnt=0, initcnt=0, open=0;
2825 			// precnt seems to count control points leading into the next on-curve point. pre stores those points.
2826 			// initcnt counts the control points that appear before the first on-curve point. This can get updated at the beginning and/or the end of the list.
2827 			// This is important for determining the order of the closing curve.
2828 			// A further improvement would be to prefetch the entire list so as to know the declared order of a curve before processing the point.
2829 
2830 			int wasquad = -1; // This tracks whether we identified the previous curve as quadratic. (-1 means undefined.)
2831 			int firstpointsaidquad = -1; // This tracks the declared order of the curve leading into the first on-curve point.
2832 
2833 		    ss = chunkalloc(sizeof(SplineSet));
2834 			ss->first = NULL;
2835 
2836 		    for ( points = contour->children; points!=NULL; points=points->next ) {
2837 			char *xs, *ys, *type, *pname, *smooths;
2838 			double x,y;
2839 			int smooth = 0;
2840 			// We discard any entities in the splineset that are not points.
2841 			if ( xmlStrcmp(points->name,(const xmlChar *) "point")!=0 )
2842 		    continue;
2843 			// Read as strings from xml.
2844 			xs = (char *) xmlGetProp(points,(xmlChar *) "x");
2845 			ys = (char *) xmlGetProp(points,(xmlChar *) "y");
2846 			type = (char *) xmlGetProp(points,(xmlChar *) "type");
2847 			pname = (char *) xmlGetProp(points,(xmlChar *) "name");
2848 			smooths = (char *) xmlGetProp(points,(xmlChar *) "smooth");
2849 			if (smooths != NULL) {
2850 				if (strcmp(smooths,"yes") == 0) smooth = 1;
2851 				free(smooths); smooths=NULL;
2852 			}
2853 			if ( xs==NULL || ys == NULL ) {
2854 				if (xs != NULL) { free(xs); xs = NULL; }
2855 				if (ys != NULL) { free(ys); ys = NULL; }
2856 				if (type != NULL) { free(type); type = NULL; }
2857 				if (pname != NULL) { free(pname); pname = NULL; }
2858 		    	continue;
2859 			}
2860 			x = strtod(xs,NULL); y = strtod(ys,NULL);
2861 			if ( type!=NULL && (strcmp(type,"move")==0 ||
2862 					    strcmp(type,"line")==0 ||
2863 					    strcmp(type,"curve")==0 ||
2864 					    strcmp(type,"qcurve")==0 )) {
2865 				// This handles only actual points.
2866 				// We create and label the point.
2867 			    sp = SplinePointCreate(x,y);
2868 				sp->dontinterpolate = 1;
2869 				if (pname != NULL) {
2870 					sp->name = copy(pname);
2871 				}
2872 				if (smooth == 1) sp->pointtype = pt_curve;
2873 				else sp->pointtype = pt_corner;
2874 
2875 			    if ( ss->first==NULL ) {
2876 			        // So this is the first real point!
2877 			        ss->first = ss->last = sp;
2878 			        // We move the lead-in points to the init buffer as we may need them for the final curve.
2879 			        memcpy(init,pre,sizeof(pre));
2880 			        initcnt = precnt;
2881 			        if ( strcmp(type,"move")==0 ) {
2882 			          open = true;
2883 			          if (initcnt != 0) LogError(_("We cannot have lead-in points for an open curve.\n"));
2884 			        }
2885 			    }
2886 
2887 			    if ( strcmp(type,"move")==0 ) {
2888 			        if (ss->first != sp) {
2889 			          LogError(_("The move point must be at the beginning of the contour.\n"));
2890 			          SplinePointFree(sp); sp = NULL;
2891 			        }
2892 			    } else if ( strcmp(type,"line")==0 ) {
2893 				SplineMake(ss->last,sp,false);
2894 			        ss->last = sp;
2895 			    } else if ( strcmp(type,"curve")==0 ) {
2896 				wasquad = false;
2897 				if (ss->first == sp) {
2898 				  firstpointsaidquad = false;
2899 				}
2900 				if ( precnt==2 && ss->first != sp ) {
2901 				    ss->last->nextcp = pre[0];
2902 				    ss->last->nonextcp = false;
2903 				    sp->prevcp = pre[1];
2904 				    sp->noprevcp = false;
2905 				    SplineMake(ss->last,sp,false);
2906 				}
2907 			        ss->last = sp;
2908 			    } else if ( strcmp(type,"qcurve")==0 ) {
2909 					wasquad = true;
2910 				if (ss->first == sp) {
2911 				  firstpointsaidquad = true;
2912 				}
2913 					if ( precnt>0 && precnt<=2 ) {
2914 						if ( precnt==2 ) {
2915 							// If we have two cached control points and the end point is quadratic, we need an implied point between the two control points.
2916 							sp2 = SplinePointCreate((pre[1].x+pre[0].x)/2,(pre[1].y+pre[0].y)/2);
2917 							sp2->prevcp = ss->last->nextcp = pre[0];
2918 							sp2->noprevcp = ss->last->nonextcp = false;
2919 							sp2->ttfindex = 0xffff;
2920 							SplineMake(ss->last,sp2,true);
2921 							ss->last = sp2;
2922 						}
2923 						// Now we connect the real point.
2924 						sp->prevcp = ss->last->nextcp = pre[precnt-1];
2925 						sp->noprevcp = ss->last->nonextcp = false;
2926 					}
2927 					SplineMake(ss->last,sp,true);
2928 					ss->last = sp;
2929 			    } else {
2930 			        SplinePointFree(sp); sp = NULL;
2931 			    }
2932 			    precnt = 0;
2933 			} else {
2934 				// This handles off-curve points (control points).
2935 			    if ((wasquad == true || wasquad==-1) && precnt==2 ) {
2936 				// We don't know whether the current curve is quadratic or cubic, but, if we're hitting three off-curve points in a row, something is off.
2937 				// As mentioned below, we assume in this case that we're dealing with a quadratic TrueType curve that needs implied points.
2938 				// We create those points since they are adjustable in Fontforge.
2939 				// There is not a valid case as far as Frank knows in which a cubic curve would have implied points.
2940 				/* Undocumented fact: If there are no on-curve points (and therefore no indication of quadratic/cubic), assume truetype implied points */
2941 					// We make the point between the two already cached control points.
2942 					sp = SplinePointCreate((pre[1].x+pre[0].x)/2,(pre[1].y+pre[0].y)/2);
2943 					sp->ttfindex = 0xffff;
2944 					if (pname != NULL) {
2945 						sp->name = copy(pname);
2946 					}
2947 			        sp->nextcp = pre[1];
2948 			        sp->nonextcp = false;
2949 			        if ( ss->first==NULL ) {
2950 				    // This is indeed possible if the first three points are control points.
2951 				    ss->first = sp;
2952 				    memcpy(init,pre,sizeof(pre));
2953 				    initcnt = 1;
2954 				} else {
2955 				    ss->last->nextcp = sp->prevcp = pre[0];
2956 				    ss->last->nonextcp = sp->noprevcp = false;
2957 				    initcnt = 0;
2958 				    SplineMake(ss->last,sp,true);
2959 				}
2960 			        ss->last = sp;
2961 #if 1
2962 			        // We make the point between the previously cached control point and the new control point.
2963 			        // We have decided that the curve is quadratic, so we can make the next implied point as well.
2964 			        sp = SplinePointCreate((x+pre[1].x)/2,(y+pre[1].y)/2);
2965 			        sp->prevcp = pre[1];
2966 			        sp->noprevcp = false;
2967 					sp->ttfindex = 0xffff;
2968 			        SplineMake(ss->last,sp,true);
2969 			        ss->last = sp;
2970 			        pre[0].x = x; pre[0].y = y;
2971 			        precnt = 1;
2972 #else
2973 			        // Let us instead save the second implied point for later.
2974 			        pre[0].x = pre[1].x; pre[0].y = pre[1].y;
2975 			        pre[1].x = x; pre[1].y = y;
2976 			        precnt = 2;
2977 #endif
2978 					wasquad = true;
2979 			    } else if ( wasquad==true && precnt==1) {
2980 					// Frank thinks that this might generate false positives for qcurves.
2981 					// This seems not to be the best way to handle it, but mixed-order spline sets are rare.
2982 					sp = SplinePointCreate((x+pre[0].x)/2,(y+pre[0].y)/2);
2983 					if (pname != NULL) {
2984 						sp->name = copy(pname);
2985 					}
2986 			        sp->prevcp = pre[0];
2987 			        sp->noprevcp = false;
2988 					sp->ttfindex = 0xffff;
2989 			        if ( ss->first==NULL ) {
2990 				    	ss->first = sp;
2991 			            memcpy(init,pre,sizeof(pre));
2992 			            initcnt = 1;
2993 					} else {
2994 					    ss->last->nextcp = sp->prevcp;
2995 			            ss->last->nonextcp = false;
2996 				    	SplineMake(ss->last,sp,true);
2997 					}
2998 					ss->last = sp;
2999 			        pre[0].x = x; pre[0].y = y;
3000 			    } else if ( precnt<2 ) {
3001 					pre[precnt].x = x;
3002 			        pre[precnt].y = y;
3003 			        ++precnt;
3004 			    }
3005 			}
3006                         if (xs != NULL) { free(xs); xs = NULL; }
3007                         if (ys != NULL) { free(ys); ys = NULL; }
3008                         if (type != NULL) { free(type); type = NULL; }
3009                         if (pname != NULL) { free(pname); pname = NULL; }
3010 		    }
3011 		    // We are finished looping, so it's time to close the curve if it is to be closed.
3012 		    if ( !open && ss->first != NULL ) {
3013 			ss->start_offset = -initcnt;
3014 			// init has a list of control points leading into the first point. pre has a list of control points trailing the last processed on-curve point.
3015 			// We merge pre into init and use init as the list of control points between the last processed on-curve point and the first on-curve point.
3016 			if ( precnt!=0 ) {
3017 			    BasePoint temp[2];
3018 			    memcpy(temp,init,sizeof(temp));
3019 			    memcpy(init,pre,sizeof(pre));
3020 			    memcpy(init+precnt,temp,sizeof(temp));
3021 			    initcnt += precnt;
3022 			}
3023 			if ( ((firstpointsaidquad==true || (firstpointsaidquad == -1 && wasquad == true)) && initcnt>0) || initcnt==1 ) {
3024 				// If the final curve is declared quadratic or is assumed to be by control point count, we proceed accordingly.
3025 			    int i;
3026 			    for ( i=0; i<initcnt-1; ++i ) {
3027 					// If the final curve is declared quadratic but has more than one control point, we add implied points.
3028 					sp = SplinePointCreate((init[i+1].x+init[i].x)/2,(init[i+1].y+init[i].y)/2);
3029 			        sp->prevcp = ss->last->nextcp = init[i];
3030 			        sp->noprevcp = ss->last->nonextcp = false;
3031 					sp->ttfindex = 0xffff;
3032 			        SplineMake(ss->last,sp,true);
3033 			        ss->last = sp;
3034 			    }
3035 			    ss->last->nextcp = ss->first->prevcp = init[initcnt-1];
3036 			    ss->last->nonextcp = ss->first->noprevcp = false;
3037 			    wasquad = true;
3038 			} else if ( initcnt==2 ) {
3039 			    ss->last->nextcp = init[0];
3040 			    ss->first->prevcp = init[1];
3041 			    ss->last->nonextcp = ss->first->noprevcp = false;
3042 				wasquad = false;
3043 			}
3044 			SplineMake(ss->last, ss->first, (firstpointsaidquad==true || (firstpointsaidquad == -1 && wasquad == true)));
3045 			ss->last = ss->first;
3046 		    }
3047 		    if (ss->first == NULL) {
3048 				LogError(_("This spline set has no points.\n"));
3049 				SplinePointListFree(ss); ss = NULL;
3050 		    } else {
3051 		        if ( last==NULL ) {
3052 				// If there are no existing spline sets, we point the main spline reference to this set.
3053 				sc->layers[layerdest].splines = ss;
3054 		        } else {
3055 				// If there are existing spline sets, we attach to the last one.
3056 				last->next = ss;
3057 		        }
3058 				last = ss;
3059 		    }
3060 		    }
3061 	    }
3062 	} else if ( xmlStrcmp(kids->name,(const xmlChar *) "lib")==0 ) {
3063 	    xmlNodePtr keys, temp, dict = FindNode(kids->children,"dict");
3064 	    if ( dict!=NULL ) {
3065 		for ( keys=dict->children; keys!=NULL; keys=keys->next ) {
3066 		    if ( xmlStrcmp(keys->name,(const xmlChar *) "key")== 0 ) {
3067 				char *keyname = (char *) xmlNodeListGetString(doc,keys->children,true);
3068 				if ( strcmp(keyname,"com.fontlab.hintData")==0 ) {
3069 			    	for ( temp=keys->next; temp!=NULL; temp=temp->next ) {
3070 						if ( xmlStrcmp(temp->name,(const xmlChar *) "dict")==0 )
3071 						    break;
3072 			    	}
3073 			    	if ( temp!=NULL ) {
3074 						if (layerdest == ly_fore) {
3075 							if (sc->hstem == NULL) {
3076 								sc->hstem = GlifParseHints(doc,temp,"hhints");
3077 								SCGuessHHintInstancesList(sc,ly_fore);
3078 							}
3079 							if (sc->vstem == NULL) {
3080 								sc->vstem = GlifParseHints(doc,temp,"vhints");
3081 			        			SCGuessVHintInstancesList(sc,ly_fore);
3082 			        		}
3083 						}
3084 			    	}
3085 					break;
3086 				}
3087 				free(keyname);
3088 		    }
3089 		}
3090 #ifndef _NO_PYTHON
3091 		if (sc->layers[layerdest].python_persistent == NULL) {
3092 		  sc->layers[layerdest].python_persistent = LibToPython(doc,dict,1);
3093 		  sc->layers[layerdest].python_persistent_has_lists = 1;
3094 		} else LogError(_("Duplicate lib data.\n"));
3095 #endif
3096 	    }
3097 	}
3098     }
3099     xmlFreeDoc(doc);
3100     _SPLCategorizePoints(sc->layers[layerdest].splines, pconvert_flag_smooth|pconvert_flag_by_geom);
3101 return( sc );
3102 }
3103 
UFOLoadGlyph(SplineFont * sf,char * glifname,char * glyphname,SplineChar * existingglyph,int layerdest)3104 static SplineChar *UFOLoadGlyph(SplineFont *sf,char *glifname, char* glyphname, SplineChar* existingglyph, int layerdest) {
3105     xmlDocPtr doc;
3106 
3107     doc = xmlParseFile(glifname);
3108     if ( doc==NULL ) {
3109 	LogError(_("Bad glif file %s"), glifname);
3110 return( NULL );
3111     }
3112 return( _UFOLoadGlyph(sf,doc,glifname,glyphname,existingglyph,layerdest));
3113 }
3114 
3115 
UFORefFixup(SplineFont * sf,SplineChar * sc,int layer)3116 static void UFORefFixup(SplineFont *sf, SplineChar *sc, int layer ) {
3117     RefChar *r, *prev;
3118     SplineChar *rsc;
3119 
3120     if ( sc==NULL || sc->ticked )
3121 		return;
3122     sc->ticked = true;
3123     prev = NULL;
3124 	// For each reference, attempt to locate the real splinechar matching the name stored in the fake splinechar.
3125 	// Free the fake splinechar afterwards.
3126     r=sc->layers[layer].refs;
3127     while ( r!=NULL ) {
3128 		if (r->sc->name == NULL || strcmp(r->sc->name, "") == 0) {
3129 			LogError(_("There's a reference to a glyph with no name."));
3130 			prev = r; r = r->next; continue;
3131 		}
3132 		if (r->sc->ticked) {
3133 		  // We've already fixed this one.
3134 		  prev = r; r = r->next; continue;
3135 		}
3136 		rsc = SFGetChar(sf,-1, r->sc->name);
3137 		if ( rsc==NULL || rsc->name == NULL || strcmp(rsc->name,"") == 0 ) {
3138 			if (rsc != NULL) {
3139 			  LogError(_("Invalid glyph for %s when fixing up references."), r->sc->name);
3140 			} else
3141 			LogError(_("Failed to find glyph %s when fixing up references."), r->sc->name);
3142 			SplineCharFree(r->sc); // Delete the fake glyph.
3143 			r->sc = NULL;
3144 			// Elide r from the list and free it.
3145 			if ( prev==NULL ) sc->layers[layer].refs = r->next;
3146 			else prev->next = r->next;
3147 			RefCharFree(r);
3148 			if ( prev==NULL ) r = sc->layers[layer].refs;
3149 			else r = prev->next;
3150 		} else {
3151 			UFORefFixup(sf,rsc, layer);
3152 			if (r->sc->layer_cnt > 0) {
3153 			  fprintf(stderr, "Danger!\n");
3154 			}
3155 			SplineCharFree(r->sc);
3156 			r->sc = rsc;
3157 			SCReinstanciateRefChar(sc,r,layer);
3158 			prev = r; r = r->next;
3159 		}
3160     }
3161 }
3162 
UFOLoadGlyphs(SplineFont * sf,char * glyphdir,int layerdest)3163 static void UFOLoadGlyphs(SplineFont *sf,char *glyphdir, int layerdest) {
3164     char *glyphlist = buildname(glyphdir,"contents.plist");
3165     xmlDocPtr doc;
3166     xmlNodePtr plist, dict, keys, value;
3167     char *valname, *glyphfname;
3168     int i;
3169     SplineChar *sc;
3170     int tot;
3171 
3172     doc = xmlParseFile(glyphlist);
3173     free(glyphlist);
3174     if ( doc==NULL ) {
3175 	LogError(_("Bad contents.plist"));
3176 return;
3177     }
3178     plist = xmlDocGetRootElement(doc);
3179     dict = FindNode(plist->children,"dict");
3180     if ( xmlStrcmp(plist->name,(const xmlChar *) "plist")!=0 || dict == NULL ) {
3181 	LogError(_("Expected property list file"));
3182 	xmlFreeDoc(doc);
3183 return;
3184     }
3185 	// Count glyphs for the benefit of measuring progress.
3186     for ( tot=0, keys=dict->children; keys!=NULL; keys=keys->next ) {
3187 		if ( xmlStrcmp(keys->name,(const xmlChar *) "key")==0 )
3188 		    ++tot;
3189     }
3190     ff_progress_change_total(tot);
3191 	// Start reading in glyph name to file name mappings.
3192     for ( keys=dict->children; keys!=NULL; keys=keys->next ) {
3193 		for ( value = keys->next; value!=NULL && xmlStrcmp(value->name,(const xmlChar *) "text")==0;
3194 			value = value->next );
3195 		if ( value==NULL )
3196 			break;
3197 		if ( xmlStrcmp(keys->name,(const xmlChar *) "key")==0 ) {
3198 			char * glyphname = (char *) xmlNodeListGetString(doc,keys->children,true);
3199 			int newsc = 0;
3200 			SplineChar* existingglyph = NULL;
3201 			if (glyphname != NULL) {
3202 				existingglyph = SFGetChar(sf,-1,glyphname);
3203 				if (existingglyph == NULL) newsc = 1;
3204 				valname = (char *) xmlNodeListGetString(doc,value->children,true);
3205 				glyphfname = buildname(glyphdir,valname);
3206 				sc = UFOLoadGlyph(sf, glyphfname, glyphname, existingglyph, layerdest);
3207 				// We want to stash the glif name (minus the extension) for future use.
3208 				if (sc != NULL && sc->glif_name == NULL && valname != NULL) {
3209 				  char * tmppos = strrchr(valname, '.'); if (tmppos) *tmppos = '\0';
3210 				  sc->glif_name = copy(valname);
3211 				  if (tmppos) *tmppos = '.';
3212 				}
3213 				free(valname);
3214 				if ( ( sc!=NULL ) && newsc ) {
3215 					sc->parent = sf;
3216 					if ( sf->glyphcnt>=sf->glyphmax )
3217 						sf->glyphs = realloc(sf->glyphs,(sf->glyphmax+=100)*sizeof(SplineChar *));
3218 					sc->orig_pos = sf->glyphcnt;
3219 					sf->glyphs[sf->glyphcnt++] = sc;
3220 				}
3221 			}
3222 			keys = value;
3223 			ff_progress_next();
3224 		}
3225     }
3226     xmlFreeDoc(doc);
3227 
3228     GlyphHashFree(sf);
3229     for ( i=0; i<sf->glyphcnt; ++i )
3230 	UFORefFixup(sf,sf->glyphs[i], layerdest);
3231 }
3232 
GlyphGroupDeduplicate(struct ff_glyphclasses * group_base,struct splinefont * sf,int check_kerns)3233 static struct ff_glyphclasses *GlyphGroupDeduplicate(struct ff_glyphclasses *group_base, struct splinefont *sf, int check_kerns) {
3234   // This removes internal duplicates from the specified group and also, if desired, the groups duplicating entities already named as kerning classes.
3235   // It takes the list head as its argument and returns the new list head (which may be the same unless the first item duplicates a kerning class).
3236   int temp_index = 0;
3237   struct glif_name_index * group_name_hash = glif_name_index_new(); // Open the group hash table.
3238   struct glif_name_index * class_name_hash = glif_name_index_new(); // Open the class hash table.
3239 
3240   if (check_kerns && sf) HashKerningClassNames(sf, class_name_hash);
3241   struct ff_glyphclasses *group_current = group_base;
3242   struct ff_glyphclasses *group_prev = NULL;
3243   while (group_current != NULL) {
3244     if (group_current->classname == NULL || group_current->classname[0] == '\0' ||
3245       glif_name_search_glif_name(group_name_hash, group_current->classname) ||
3246       (check_kerns && sf && glif_name_search_glif_name(class_name_hash, group_current->classname))) {
3247       if (group_prev != NULL) group_prev->next = group_current->next;
3248       else group_base = group_current->next;
3249       GlyphGroupFree(group_current);
3250       if (group_prev != NULL) group_current = group_prev->next;
3251       else group_current = group_base;
3252     } else {
3253       glif_name_track_new(group_name_hash, temp_index++, group_current->classname);
3254       group_prev = group_current; group_current = group_current->next;
3255     }
3256   }
3257   glif_name_index_destroy(class_name_hash);
3258   glif_name_index_destroy(group_name_hash);
3259 
3260   return group_base;
3261 }
3262 
script_from_glyph_list(SplineFont * sf,const char * glyph_names)3263 static uint32 script_from_glyph_list(SplineFont *sf, const char *glyph_names) {
3264   uint32 script = DEFAULT_SCRIPT;
3265   char *delimited_names;
3266   off_t name_char_pos;
3267   name_char_pos = 0;
3268   delimited_names = delimit_null(glyph_names, ' ');
3269   while (script == DEFAULT_SCRIPT && glyph_names[name_char_pos] != '\0') {
3270     SplineChar *sc = SFGetChar(sf, -1, delimited_names + name_char_pos);
3271     script = SCScriptFromUnicode(sc);
3272     name_char_pos += strlen(delimited_names + name_char_pos);
3273     if (glyph_names[name_char_pos] != '\0') name_char_pos ++;
3274   }
3275   free(delimited_names); delimited_names = NULL;
3276   return script;
3277 }
3278 
3279 #define GROUP_NAME_KERNING_UFO 1
3280 #define GROUP_NAME_KERNING_FEATURE 2
3281 #define GROUP_NAME_VERTICAL 4 // Otherwise horizontal.
3282 #define GROUP_NAME_RIGHT 8 // Otherwise left (or above).
3283 
MakeKerningClasses(SplineFont * sf,struct ff_glyphclasses * group_base)3284 static void MakeKerningClasses(SplineFont *sf, struct ff_glyphclasses *group_base) {
3285   int left_count = 0, right_count = 0, above_count = 0, below_count = 0;
3286   int left_start = 0, right_start = 0, above_start = 0, below_start = 0;
3287 
3288   // It is very difficult to create speculative indices for the unmerged group members during the size calculation.
3289   // So we expect that the incoming group list has no duplicates (as after a run through GlyphGroupDeduplicate).
3290   struct ff_glyphclasses *current_group;
3291   for (current_group = group_base; current_group != NULL; current_group = current_group->next) {
3292     int group_type = GroupNameType(current_group->classname);
3293     if ((group_type != -1) && (group_type & (GROUP_NAME_KERNING_UFO|GROUP_NAME_KERNING_FEATURE))) {
3294       if (group_type & GROUP_NAME_VERTICAL) {
3295         if (group_type & GROUP_NAME_RIGHT) {
3296           below_count++;
3297         } else {
3298           above_count++;
3299         }
3300       } else {
3301         if (group_type & GROUP_NAME_RIGHT) {
3302           right_count++;
3303         } else {
3304           left_count++;
3305         }
3306       }
3307     }
3308   }
3309   // Allocate lookups if needed.
3310   if (sf->kerns == NULL && (left_count || right_count)) {
3311     sf->kerns = calloc(1, sizeof(struct kernclass));
3312     sf->kerns->subtable = SFSubTableFindOrMake(sf, CHR('k','e','r','n'), DEFAULT_SCRIPT, gpos_pair);
3313     sf->kerns->firsts = calloc(1, sizeof(char *));
3314     sf->kerns->firsts_names = calloc(1, sizeof(char *));
3315     sf->kerns->firsts_flags = calloc(1, sizeof(int));
3316     sf->kerns->seconds = calloc(1, sizeof(char *));
3317     sf->kerns->seconds_names = calloc(1, sizeof(char *));
3318     sf->kerns->seconds_flags = calloc(1, sizeof(int));
3319     sf->kerns->offsets = calloc(1, sizeof(int16));
3320     sf->kerns->offsets_flags = calloc(1, sizeof(int));
3321     sf->kerns->first_cnt = 1;
3322     sf->kerns->second_cnt = 1;
3323   }
3324   if (sf->vkerns == NULL && (above_count || below_count)) {
3325     sf->vkerns = calloc(1, sizeof(struct kernclass));
3326     sf->vkerns->subtable = SFSubTableFindOrMake(sf, CHR('v','k','r','n'), DEFAULT_SCRIPT, gpos_pair);
3327     sf->vkerns->firsts = calloc(1, sizeof(char *));
3328     sf->vkerns->firsts_names = calloc(1, sizeof(char *));
3329     sf->vkerns->firsts_flags = calloc(1, sizeof(int));
3330     sf->vkerns->seconds = calloc(1, sizeof(char *));
3331     sf->vkerns->seconds_names = calloc(1, sizeof(char *));
3332     sf->vkerns->seconds_flags = calloc(1, sizeof(int));
3333     sf->vkerns->offsets = calloc(1, sizeof(int16));
3334     sf->vkerns->offsets_flags = calloc(1, sizeof(int));
3335     sf->vkerns->first_cnt = 1;
3336     sf->vkerns->second_cnt = 1;
3337   }
3338   // Set starts.
3339   if (sf->kerns != NULL) { left_start = sf->kerns->first_cnt; right_start = sf->kerns->second_cnt; }
3340   if (sf->vkerns != NULL) { above_start = sf->vkerns->first_cnt; below_start = sf->vkerns->second_cnt; }
3341   // Make space for the new entries.
3342   // We start by allocating space for the offsets and offsets flags. We then copy the old contents, row-by-row.
3343   if ((left_count > 0 || right_count > 0) && ((sf->kerns->first_cnt + left_count) * (sf->kerns->second_cnt + right_count) > 0)) {
3344     // Offsets.
3345     int16 *tmp_offsets = calloc((sf->kerns->first_cnt + left_count) * (sf->kerns->second_cnt + right_count), sizeof(int16));
3346     if (sf->kerns->offsets) {
3347       int rowpos;
3348       for (rowpos = 0; rowpos < sf->kerns->first_cnt; rowpos ++) {
3349         memcpy((void *)tmp_offsets + (rowpos * (sf->kerns->second_cnt + right_count)) * sizeof(int16), (void *)(sf->kerns->offsets) + (rowpos * sf->kerns->second_cnt) * sizeof(int16), sf->kerns->second_cnt * sizeof(int16));
3350       }
3351       free(sf->kerns->offsets);
3352     }
3353     sf->kerns->offsets = tmp_offsets;
3354     // Offset flags.
3355     int *tmp_offsets_flags = calloc((sf->kerns->first_cnt + left_count) * (sf->kerns->second_cnt + right_count), sizeof(int));
3356     if (sf->kerns->offsets_flags) {
3357       int rowpos;
3358       for (rowpos = 0; rowpos < sf->kerns->first_cnt; rowpos ++) {
3359         memcpy((void *)tmp_offsets_flags + (rowpos * (sf->kerns->second_cnt + right_count)) * sizeof(int), (void *)(sf->kerns->offsets_flags) + (rowpos * sf->kerns->second_cnt) * sizeof(int), sf->kerns->second_cnt * sizeof(int));
3360       }
3361       free(sf->kerns->offsets_flags);
3362     }
3363     sf->kerns->offsets_flags = tmp_offsets_flags;
3364     // Adjusts.
3365     DeviceTable *tmp_adjusts = calloc((sf->kerns->first_cnt + left_count) * (sf->kerns->second_cnt + right_count), sizeof(DeviceTable));
3366     if (sf->kerns->adjusts) {
3367       int rowpos;
3368       for (rowpos = 0; rowpos < sf->kerns->first_cnt; rowpos ++) {
3369         memcpy((void *)tmp_adjusts + (rowpos * (sf->kerns->second_cnt + right_count)) * sizeof(DeviceTable), (void *)(sf->kerns->adjusts) + (rowpos * sf->kerns->second_cnt) * sizeof(DeviceTable), sf->kerns->second_cnt * sizeof(DeviceTable));
3370       }
3371       free(sf->kerns->adjusts);
3372     }
3373     sf->kerns->adjusts = tmp_adjusts;
3374   }
3375   if ((above_count > 0 || below_count > 0) && ((sf->vkerns->first_cnt + above_count) * (sf->vkerns->second_cnt + below_count) > 0)) {
3376     // Offsets.
3377     int16 *tmp_offsets = calloc((sf->vkerns->first_cnt + above_count) * (sf->vkerns->second_cnt + below_count), sizeof(int16));
3378     if (sf->vkerns->offsets) {
3379       int rowpos;
3380       for (rowpos = 0; rowpos < sf->vkerns->first_cnt; rowpos ++) {
3381         memcpy((void *)tmp_offsets + (rowpos * (sf->vkerns->second_cnt + below_count)) * sizeof(int16), (void *)(sf->vkerns->offsets) + (rowpos * sf->vkerns->second_cnt) * sizeof(int16), sf->vkerns->second_cnt * sizeof(int16));
3382       }
3383       free(sf->vkerns->offsets);
3384     }
3385     sf->vkerns->offsets = tmp_offsets;
3386     // Offset flags.
3387     int *tmp_offsets_flags = calloc((sf->vkerns->first_cnt + above_count) * (sf->vkerns->second_cnt + below_count), sizeof(int));
3388     if (sf->vkerns->offsets_flags) {
3389       int rowpos;
3390       for (rowpos = 0; rowpos < sf->vkerns->first_cnt; rowpos ++) {
3391         memcpy((void *)tmp_offsets_flags + (rowpos * (sf->vkerns->second_cnt + below_count)) * sizeof(int), (void *)(sf->vkerns->offsets_flags) + (rowpos * sf->vkerns->second_cnt) * sizeof(int), sf->vkerns->second_cnt * sizeof(int));
3392       }
3393       free(sf->vkerns->offsets_flags);
3394     }
3395     sf->vkerns->offsets_flags = tmp_offsets_flags;
3396     // Adjusts.
3397     DeviceTable *tmp_adjusts = calloc((sf->vkerns->first_cnt + above_count) * (sf->vkerns->second_cnt + below_count), sizeof(DeviceTable));
3398     if (sf->vkerns->adjusts) {
3399       int rowpos;
3400       for (rowpos = 0; rowpos < sf->vkerns->first_cnt; rowpos ++) {
3401         memcpy((void *)tmp_adjusts + (rowpos * (sf->vkerns->second_cnt + above_count)) * sizeof(DeviceTable), (void *)(sf->vkerns->adjusts) + (rowpos * sf->vkerns->second_cnt) * sizeof(DeviceTable), sf->vkerns->second_cnt * sizeof(DeviceTable));
3402       }
3403       free(sf->vkerns->adjusts);
3404     }
3405     sf->vkerns->adjusts = tmp_adjusts;
3406   }
3407   // Since the linear data need no repositioning, we can just use realloc. But it's important that we zero the new space in case it does not get filled.
3408   if (left_count > 0) {
3409     sf->kerns->firsts = realloc(sf->kerns->firsts, sizeof(char *) * (sf->kerns->first_cnt + left_count));
3410     memset((void*)sf->kerns->firsts + sf->kerns->first_cnt * sizeof(char *), 0, left_count * sizeof(char *));
3411     sf->kerns->firsts_names = realloc(sf->kerns->firsts_names, sizeof(char *) * (sf->kerns->first_cnt + left_count));
3412     memset((void*)sf->kerns->firsts_names + sf->kerns->first_cnt * sizeof(char *), 0, left_count * sizeof(char *));
3413     sf->kerns->firsts_flags = realloc(sf->kerns->firsts_flags, sizeof(int) * (sf->kerns->first_cnt + left_count));
3414     memset((void*)sf->kerns->firsts_flags + sf->kerns->first_cnt * sizeof(int), 0, left_count * sizeof(int));
3415     sf->kerns->first_cnt += left_count;
3416   }
3417   if (right_count > 0) {
3418     sf->kerns->seconds = realloc(sf->kerns->seconds, sizeof(char *) * (sf->kerns->second_cnt + right_count));
3419     memset((void*)sf->kerns->seconds + sf->kerns->second_cnt * sizeof(char *), 0, right_count * sizeof(char *));
3420     sf->kerns->seconds_names = realloc(sf->kerns->seconds_names, sizeof(char *) * (sf->kerns->second_cnt + right_count));
3421     memset((void*)sf->kerns->seconds_names + sf->kerns->second_cnt * sizeof(char *), 0, right_count * sizeof(char *));
3422     sf->kerns->seconds_flags = realloc(sf->kerns->seconds_flags, sizeof(int) * (sf->kerns->second_cnt + right_count));
3423     memset((void*)sf->kerns->seconds_flags + sf->kerns->second_cnt * sizeof(int), 0, right_count * sizeof(int));
3424     sf->kerns->second_cnt += right_count;
3425   }
3426   if (above_count > 0) {
3427     sf->vkerns->firsts = realloc(sf->vkerns->firsts, sizeof(char *) * (sf->vkerns->first_cnt + above_count));
3428     memset((void*)sf->vkerns->firsts + sf->vkerns->first_cnt * sizeof(char *), 0, above_count * sizeof(char *));
3429     sf->vkerns->firsts_names = realloc(sf->vkerns->firsts_names, sizeof(char *) * (sf->vkerns->first_cnt + above_count));
3430     memset((void*)sf->vkerns->firsts_names + sf->vkerns->first_cnt * sizeof(char *), 0, above_count * sizeof(char *));
3431     sf->vkerns->firsts_flags = realloc(sf->vkerns->firsts_flags, sizeof(int) * (sf->vkerns->first_cnt + above_count));
3432     memset((void*)sf->vkerns->firsts_flags + sf->vkerns->first_cnt * sizeof(int), 0, above_count * sizeof(int));
3433     sf->vkerns->first_cnt += above_count;
3434   }
3435   if (below_count > 0) {
3436     sf->vkerns->seconds = realloc(sf->vkerns->seconds, sizeof(char *) * (sf->vkerns->second_cnt + below_count));
3437     memset((void*)sf->vkerns->seconds + sf->vkerns->second_cnt * sizeof(char *), 0, below_count * sizeof(char *));
3438     sf->vkerns->seconds_names = realloc(sf->vkerns->seconds_names, sizeof(char *) * (sf->vkerns->second_cnt + below_count));
3439     memset((void*)sf->vkerns->seconds_names + sf->vkerns->second_cnt * sizeof(char *), 0, below_count * sizeof(char *));
3440     sf->vkerns->seconds_flags = realloc(sf->vkerns->seconds_flags, sizeof(int) * (sf->vkerns->second_cnt + below_count));
3441     memset((void*)sf->vkerns->seconds_flags + sf->vkerns->second_cnt * sizeof(char *), 0, below_count * sizeof(int));
3442     sf->vkerns->second_cnt += below_count;
3443   }
3444   // Start copying.
3445   left_count = 0; right_count = 0; above_count = 0; below_count = 0;
3446   for (current_group = group_base; current_group != NULL; current_group = current_group->next) {
3447     int group_type = GroupNameType(current_group->classname);
3448     // This function only gets used in processing groups.plist right now, so it assumes that the groups are native as below.
3449     if ((group_type != -1) && (group_type & (GROUP_NAME_KERNING_UFO|GROUP_NAME_KERNING_FEATURE))) {
3450       if (group_type & GROUP_NAME_VERTICAL) {
3451         if (group_type & GROUP_NAME_RIGHT) {
3452           sf->vkerns->seconds[below_start + below_count] = copy(current_group->glyphs);
3453           sf->vkerns->seconds_names[below_start + below_count] = copy(current_group->classname);
3454           sf->vkerns->seconds_flags[below_start + below_count] = FF_KERNCLASS_FLAG_NATIVE | ((group_type & GROUP_NAME_KERNING_FEATURE) ? FF_KERNCLASS_FLAG_NAMETYPE : 0);
3455           below_count++;
3456         } else {
3457           sf->vkerns->firsts[above_start + above_count] = copy(current_group->glyphs);
3458           sf->vkerns->firsts_names[above_start + above_count] = copy(current_group->classname);
3459           sf->vkerns->firsts_flags[above_start + above_count] = FF_KERNCLASS_FLAG_NATIVE | ((group_type & GROUP_NAME_KERNING_FEATURE) ? FF_KERNCLASS_FLAG_NAMETYPE : 0);
3460           above_count++;
3461         }
3462       } else {
3463         if (group_type & GROUP_NAME_RIGHT) {
3464           sf->kerns->seconds[right_start + right_count] = copy(current_group->glyphs);
3465           sf->kerns->seconds_names[right_start + right_count] = copy(current_group->classname);
3466           sf->kerns->seconds_flags[right_start + right_count] = FF_KERNCLASS_FLAG_NATIVE | ((group_type & GROUP_NAME_KERNING_FEATURE) ? FF_KERNCLASS_FLAG_NAMETYPE : 0);
3467           right_count++;
3468         } else {
3469           sf->kerns->firsts[left_start + left_count] = copy(current_group->glyphs);
3470           sf->kerns->firsts_names[left_start + left_count] = copy(current_group->classname);
3471           sf->kerns->firsts_flags[left_start + left_count] = FF_KERNCLASS_FLAG_NATIVE | ((group_type & GROUP_NAME_KERNING_FEATURE) ? FF_KERNCLASS_FLAG_NAMETYPE : 0);
3472           left_count++;
3473         }
3474       }
3475     }
3476   }
3477 #ifdef UFO_GUESS_SCRIPTS
3478   // Check the script in each element of each group (for each polarity) until a character is of a script other than DFLT.
3479   if (sf->kerns != NULL) {
3480     uint32 script = DEFAULT_SCRIPT;
3481     int class_index;
3482     class_index = 0;
3483     while (script == DEFAULT_SCRIPT && class_index < sf->kerns->first_cnt) {
3484       if (sf->kerns->firsts[class_index] != NULL) script = script_from_glyph_list(sf, sf->kerns->firsts[class_index]);
3485       class_index++;
3486     }
3487     class_index = 0;
3488     while (script == DEFAULT_SCRIPT && class_index < sf->kerns->second_cnt) {
3489       if (sf->kerns->seconds[class_index] != NULL) script = script_from_glyph_list(sf, sf->kerns->seconds[class_index]);
3490       class_index++;
3491     }
3492     sf->kerns->subtable = SFSubTableFindOrMake(sf, CHR('k','e','r','n'), script, gpos_pair);
3493   }
3494   if (sf->vkerns != NULL) {
3495     uint32 script = DEFAULT_SCRIPT;
3496     int class_index;
3497     class_index = 0;
3498     while (script == DEFAULT_SCRIPT && class_index < sf->vkerns->first_cnt) {
3499       if (sf->vkerns->firsts[class_index] != NULL) script = script_from_glyph_list(sf, sf->vkerns->firsts[class_index]);
3500       class_index++;
3501     }
3502     class_index = 0;
3503     while (script == DEFAULT_SCRIPT && class_index < sf->vkerns->second_cnt) {
3504       if (sf->vkerns->seconds[class_index] != NULL) script = script_from_glyph_list(sf, sf->vkerns->seconds[class_index]);
3505       class_index++;
3506     }
3507     sf->vkerns->subtable = SFSubTableFindOrMake(sf, CHR('v','k','r','n'), script, gpos_pair);
3508   }
3509 #else
3510   // Some test cases have proven that FontForge would do best to avoid classifying these.
3511   uint32 script = DEFAULT_SCRIPT;
3512   if (sf->kerns != NULL) {
3513     sf->kerns->subtable = SFSubTableFindOrMake(sf, CHR('k','e','r','n'), script, gpos_pair);
3514   }
3515   if (sf->vkerns != NULL) {
3516     sf->vkerns->subtable = SFSubTableFindOrMake(sf, CHR('v','k','r','n'), script, gpos_pair);
3517   }
3518 #endif // UFO_GUESS_SCRIPTS
3519 
3520 }
3521 
UFOHandleGroups(SplineFont * sf,char * basedir)3522 static void UFOHandleGroups(SplineFont *sf, char *basedir) {
3523     char *fname = buildname(basedir, "groups.plist");
3524     xmlDocPtr doc=NULL;
3525     xmlNodePtr plist,dict,keys,value,subkeys;
3526     char *keyname, *valname;
3527     struct ff_glyphclasses *current_group = NULL;
3528     // We want to start at the end of the list of groups already in the SplineFont (probably not any right now).
3529     for (current_group = sf->groups; current_group != NULL && current_group->next != NULL; current_group = current_group->next);
3530     int group_count;
3531 
3532     if ( GFileExists(fname))
3533 	doc = xmlParseFile(fname);
3534     free(fname);
3535     if ( doc==NULL )
3536 return;
3537 
3538     plist = xmlDocGetRootElement(doc);
3539     dict = FindNode(plist->children,"dict");
3540     if ( xmlStrcmp(plist->name,(const xmlChar *) "plist")!=0 || dict==NULL ) {
3541 	LogError(_("Expected property list file"));
3542 	xmlFreeDoc(doc);
3543 return;
3544     }
3545     for ( keys=dict->children; keys!=NULL; keys=keys->next ) {
3546 	for ( value = keys->next; value!=NULL && xmlStrcmp(value->name,(const xmlChar *) "text")==0;
3547 		value = value->next );
3548 	if ( value==NULL )
3549     break;
3550 	if ( xmlStrcmp(keys->name,(const xmlChar *) "key")==0 ) {
3551 	    keyname = (char *) xmlNodeListGetString(doc,keys->children,true);
3552 	    SplineChar *sc = SFGetChar(sf,-1,keyname);
3553 	    if ( sc!=NULL ) { LogError(_("Skipping group %s with same name as a glyph.\n"), keyname); free(keyname); keyname = NULL; continue; }
3554             struct ff_glyphclasses *sfg = SFGetGroup(sf,-1,keyname);
3555 	    if ( sfg!=NULL ) { LogError(_("Skipping duplicate group %s.\n"), keyname); free(keyname); keyname = NULL; continue; }
3556 	    sfg = calloc(1, sizeof(struct ff_glyphclasses)); // We allocate space for the new group.
3557 	    sfg->classname = keyname; keyname = NULL; // We name it.
3558 	    if (current_group == NULL) sf->groups = sfg;
3559 	    else current_group->next = sfg;
3560 	    current_group = sfg; // And we put it at the end of the list.
3561 	    // We prepare to populate it. We will match to native glyphs first (in order to validate) and then convert back to strings later.
3562 	    RefChar *members_native = NULL;
3563 	    RefChar *member_native_current = NULL;
3564 	    int member_count = 0;
3565 	    int member_list_length = 0; // This makes it easy to allocate a string at the end.
3566 	    // We fetch the contents now. They are in an array, but we do not verify that.
3567 	    keys = value;
3568 	    for ( subkeys = keys->children; subkeys!=NULL; subkeys = subkeys->next ) {
3569 		if ( xmlStrcmp(subkeys->name,(const xmlChar *) "string")==0 ) {
3570 		    keyname = (char *) xmlNodeListGetString(doc,subkeys->children,true); // Get the member name.
3571 		    SplineChar *ssc = SFGetChar(sf,-1,keyname); // Try to match an existing glyph.
3572 		    if ( ssc==NULL ) { LogError(_("Skipping non-existent glyph %s in group %s.\n"), keyname, current_group->classname); free(keyname); keyname = NULL; continue; }
3573 		    member_list_length += strlen(keyname) + 1; member_count++; // Make space for its name.
3574 		    free(keyname); // Free the name for now. (We get it directly from the SplineChar later.)
3575 		    RefChar *member_native_temp = calloc(1, sizeof(RefChar)); // Make an entry in the list for the native reference.
3576 		    member_native_temp->sc = ssc; ssc = NULL;
3577 		    if (member_native_current == NULL) members_native = member_native_temp;
3578 		    else member_native_current->next = member_native_temp;
3579 		    member_native_current = member_native_temp; // Add it to the end of the list.
3580 		}
3581 	    }
3582 	    if (member_list_length == 0) member_list_length++; // We must have space for a zero-terminator even if the list is empty. A non-empty list has space for a space at the end that we can use.
3583 	    current_group->glyphs = malloc(member_list_length); // We allocate space for the list.
3584 	    current_group->glyphs[0] = '\0';
3585 	    for (member_native_current = members_native; member_native_current != NULL; member_native_current = member_native_current->next) {
3586                 if (member_native_current != members_native) strcat(current_group->glyphs, " ");
3587 	        strcat(current_group->glyphs, member_native_current->sc->name);
3588 	    }
3589 	    RefCharsFree(members_native); members_native = NULL;
3590 	}
3591     }
3592     // The preceding routine was sufficiently complicated that it seemed like a good idea to perform the deduplication in a separate block.
3593     sf->groups = GlyphGroupDeduplicate(sf->groups, sf, 1);
3594     // We now add kerning classes for any groups that are named like kerning classes.
3595     MakeKerningClasses(sf, sf->groups);
3596     xmlFreeDoc(doc);
3597 }
3598 
UFOHandleKern(SplineFont * sf,char * basedir,int isv)3599 static void UFOHandleKern(SplineFont *sf,char *basedir,int isv) {
3600     char *fname = buildname(basedir,isv ? "vkerning.plist" : "kerning.plist");
3601     xmlDocPtr doc=NULL;
3602     xmlNodePtr plist,dict,keys,value,subkeys;
3603     char *keyname, *valname;
3604     int offset;
3605     SplineChar *sc, *ssc;
3606     KernPair *kp;
3607     char *end;
3608     uint32 script;
3609 
3610     if ( GFileExists(fname))
3611 	doc = xmlParseFile(fname);
3612     free(fname);
3613     if ( doc==NULL )
3614 return;
3615 
3616     // If there is native kerning (as we would expect if the function has not returned), set the SplineFont flag to prefer it on output.
3617     sf->preferred_kerning = 1;
3618 
3619     plist = xmlDocGetRootElement(doc);
3620     dict = FindNode(plist->children,"dict");
3621     if ( xmlStrcmp(plist->name,(const xmlChar *) "plist")!=0 || dict==NULL ) {
3622 	LogError(_("Expected property list file"));
3623 	xmlFreeDoc(doc);
3624 return;
3625     }
3626     for ( keys=dict->children; keys!=NULL; keys=keys->next ) {
3627 	for ( value = keys->next; value!=NULL && xmlStrcmp(value->name,(const xmlChar *) "text")==0;
3628 		value = value->next );
3629 	if ( value==NULL )
3630     break;
3631 	if ( xmlStrcmp(keys->name,(const xmlChar *) "key")==0 ) {
3632 	    keyname = (char *) xmlNodeListGetString(doc,keys->children,true);
3633 	    sc = SFGetChar(sf,-1,keyname);
3634 	    free(keyname);
3635 	    if ( sc==NULL )
3636 	continue;
3637 	    keys = value;
3638 	    for ( subkeys = value->children; subkeys!=NULL; subkeys = subkeys->next ) {
3639 		for ( value = subkeys->next; value!=NULL && xmlStrcmp(value->name,(const xmlChar *) "text")==0;
3640 			value = value->next );
3641 		if ( value==NULL )
3642 	    break;
3643 		if ( xmlStrcmp(subkeys->name,(const xmlChar *) "key")==0 ) {
3644 		    keyname = (char *) xmlNodeListGetString(doc,subkeys->children,true);
3645 		    ssc = SFGetChar(sf,-1,keyname);
3646 		    free(keyname);
3647 		    if ( ssc==NULL )
3648 		continue;
3649 		    for ( kp=isv?sc->vkerns:sc->kerns; kp!=NULL && kp->sc!=ssc; kp=kp->next );
3650 		    if ( kp!=NULL )
3651 		continue;
3652 		    subkeys = value;
3653 		    valname = (char *) xmlNodeListGetString(doc,value->children,true);
3654 		    offset = strtol(valname,&end,10);
3655 		    if ( *end=='\0' ) {
3656 			kp = chunkalloc(sizeof(KernPair));
3657 			kp->off = offset;
3658 			kp->sc = ssc;
3659 			if ( isv ) {
3660 			    kp->next = sc->vkerns;
3661 			    sc->vkerns = kp;
3662 			} else {
3663 			    kp->next = sc->kerns;
3664 			    sc->kerns = kp;
3665 			}
3666 #ifdef UFO_GUESS_SCRIPTS
3667 			script = SCScriptFromUnicode(sc);
3668 			if ( script==DEFAULT_SCRIPT )
3669 			    script = SCScriptFromUnicode(ssc);
3670 #else
3671 			// Some test cases have proven that FontForge would do best to avoid classifying these.
3672 			script = DEFAULT_SCRIPT;
3673 #endif // UFO_GUESS_SCRIPTS
3674 			kp->subtable = SFSubTableFindOrMake(sf,
3675 				isv?CHR('v','k','r','n'):CHR('k','e','r','n'),
3676 				script, gpos_pair);
3677 		    }
3678 		    free(valname);
3679 		}
3680 	    }
3681 	}
3682     }
3683     xmlFreeDoc(doc);
3684 }
3685 
TryAddRawGroupKern(struct splinefont * sf,int isv,struct glif_name_index * class_name_pair_hash,int * current_groupkern_index_p,struct ff_rawoffsets ** current_groupkern_p,const char * left,const char * right,int offset)3686 int TryAddRawGroupKern(struct splinefont *sf, int isv, struct glif_name_index *class_name_pair_hash, int *current_groupkern_index_p, struct ff_rawoffsets **current_groupkern_p, const char *left, const char *right, int offset) {
3687   char *pairtext;
3688   int success = 0;
3689   if (left && right && ((pairtext = smprintf("%s %s", left, right)) != NULL)) {
3690     if (!glif_name_search_glif_name(class_name_pair_hash, pairtext)) {
3691       glif_name_track_new(class_name_pair_hash, (*current_groupkern_index_p)++, pairtext);
3692       struct ff_rawoffsets *tmp_groupkern = calloc(1, sizeof(struct ff_rawoffsets));
3693       tmp_groupkern->left = copy(left);
3694       tmp_groupkern->right = copy(right);
3695       tmp_groupkern->offset = offset;
3696       if (*current_groupkern_p == NULL) {
3697         if (isv) sf->groupvkerns = tmp_groupkern;
3698         else sf->groupkerns = tmp_groupkern;
3699       } else (*current_groupkern_p)->next = tmp_groupkern;
3700       *current_groupkern_p = tmp_groupkern;
3701       success = 1;
3702     }
3703     free(pairtext); pairtext = NULL;
3704   }
3705   return success;
3706 }
3707 
UFOHandleKern3(SplineFont * sf,char * basedir,int isv)3708 static void UFOHandleKern3(SplineFont *sf,char *basedir,int isv) {
3709     char *fname = buildname(basedir,isv ? "vkerning.plist" : "kerning.plist");
3710     xmlDocPtr doc=NULL;
3711     xmlNodePtr plist,dict,keys,value,subkeys;
3712     char *keyname, *valname;
3713     int offset;
3714     SplineChar *sc, *ssc;
3715     KernPair *kp;
3716     char *end;
3717     uint32 script;
3718 
3719     if ( GFileExists(fname))
3720 	doc = xmlParseFile(fname);
3721     free(fname);
3722     if ( doc==NULL )
3723 return;
3724 
3725     // If there is native kerning (as we would expect if the function has not returned), set the SplineFont flag to prefer it on output.
3726     sf->preferred_kerning = 1;
3727 
3728     plist = xmlDocGetRootElement(doc);
3729     dict = FindNode(plist->children,"dict");
3730     if ( xmlStrcmp(plist->name,(const xmlChar *) "plist")!=0 || dict==NULL ) {
3731 	LogError(_("Expected property list file"));
3732 	xmlFreeDoc(doc);
3733 return;
3734     }
3735 
3736     // We want a hash table of group names for reference.
3737     struct glif_name_index * group_name_hash = glif_name_index_new(); // Open the group hash table.
3738     struct ff_glyphclasses *current_group = NULL;
3739     int current_group_index = 0;
3740     for (current_group = sf->groups, current_group_index = 0; current_group != NULL; current_group = current_group->next, current_group_index++)
3741       if (current_group->classname != NULL) glif_name_track_new(group_name_hash, current_group_index, current_group->classname);
3742     // We also want a hash table of kerning class names. (We'll probably standardize on one approach or the other later.)
3743     struct glif_name_index * class_name_hash = glif_name_index_new(); // Open the group hash table.
3744     HashKerningClassNames(sf, class_name_hash);
3745     // We also want a hash table of the lookup pairs.
3746     struct glif_name_index * class_name_pair_hash = glif_name_index_new(); // Open the group hash table.
3747     // We need to track the head of the group kerns.
3748     struct ff_rawoffsets *current_groupkern = NULL;
3749     int current_groupkern_index = 0;
3750     // We want to start at the end of the list of kerns already in the SplineFont (probably not any right now).
3751     for (current_groupkern = (isv ? sf->groupvkerns : sf->groupkerns); current_groupkern != NULL && current_groupkern->next != NULL; current_groupkern = current_groupkern->next);
3752 
3753     // Read the left node. Set sc if it matches a character or isgroup and the associated values if it matches a group.
3754     // Read the right node. Set ssc if it matches a character or isgroup and the associated values if it matches a group.
3755     // If sc and ssc, add a kerning pair to sc for ssc.
3756     // If left_isgroup and right_isgroup, use the processed values in order to offset.
3757     for ( keys=dict->children; keys!=NULL; keys=keys->next ) {
3758 	for ( value = keys->next; value!=NULL && xmlStrcmp(value->name,(const xmlChar *) "text")==0;
3759 		value = value->next );
3760 	if ( value==NULL )
3761     break;
3762 	if ( xmlStrcmp(keys->name,(const xmlChar *) "key")==0 ) {
3763 	    keyname = (char *) xmlNodeListGetString(doc,keys->children,true);
3764             // Search for a glyph first.
3765 	    sc = SFGetChar(sf,-1,keyname);
3766             // Search for a group.
3767             struct glif_name *left_class_name_record = glif_name_search_glif_name(class_name_hash, keyname);
3768 	    free(keyname);
3769             if (sc == NULL && left_class_name_record == NULL) { LogError(_("kerning.plist references an entity that is neither a glyph nor a group.")); continue; }
3770 	    keys = value; // Set the offset for the next round.
3771             // This key represents the left/above side of the pair. The child keys represent its right/below complements.
3772 	    for ( subkeys = value->children; subkeys!=NULL; subkeys = subkeys->next ) {
3773 		for ( value = subkeys->next; value!=NULL && xmlStrcmp(value->name,(const xmlChar *) "text")==0;
3774 			value = value->next );
3775 		if ( value==NULL )
3776 	    break;
3777 		if ( xmlStrcmp(subkeys->name,(const xmlChar *) "key")==0 ) {
3778 		    // Get the second name of the pair.
3779 		    keyname = (char *) xmlNodeListGetString(doc,subkeys->children,true);
3780 		    // Get the offset in numeric form.
3781 		    valname = (char *) xmlNodeListGetString(doc,value->children,true);
3782 		    offset = strtol(valname,&end,10);
3783 		    if (*end != '\0') { LogError(_("kerning.plist has a non-numeric offset.")); continue; }
3784 		    free(valname); valname = NULL;
3785 		    subkeys = value; // Set the iterator for the next use.
3786 		    // Search for a character.
3787 		    ssc = SFGetChar(sf,-1,keyname);
3788 		    // Search for a group.
3789 		    struct glif_name *right_class_name_record = glif_name_search_glif_name(class_name_hash, keyname);
3790 		    free(keyname);
3791 		    if (ssc == NULL && right_class_name_record == NULL) { LogError(_("kerning.plist references an entity that is neither a glyph nor a group.")); continue; }
3792 
3793 		  if (sc && ssc) {
3794 		    KernPair *lastkp = NULL;
3795 		    for ( kp=(isv?sc->vkerns:sc->kerns); kp!=NULL && kp->sc!=ssc; lastkp = kp, kp=kp->next );
3796 		    if ( kp!=NULL ) { LogError(_("kerning.plist defines kerning between two glyphs that are already kerned.")); continue; }
3797 		    // We do not want to add the virtual entry until we have confirmed the possibility of adding the real entry as precedes this.
3798 		    if (!TryAddRawGroupKern(sf, isv, class_name_pair_hash, &current_groupkern_index, &current_groupkern, sc->name, ssc->name, offset)) {
3799 		      LogError(_("kerning.plist defines kerning between two glyphs that are already partially kerned.")); continue;
3800 		    }
3801 		    {
3802 			kp = chunkalloc(sizeof(KernPair));
3803 			kp->off = offset;
3804 			kp->sc = ssc;
3805 			if ( isv ) {
3806 			    if (lastkp != NULL) lastkp->next = kp;
3807 			    else sc->vkerns = kp;
3808 			    lastkp = kp;
3809 			    // kp->next = sc->vkerns;
3810 			    // sc->vkerns = kp;
3811 			} else {
3812 			    if (lastkp != NULL) lastkp->next = kp;
3813 			    else sc->kerns = kp;
3814 			    lastkp = kp;
3815 			    // kp->next = sc->kerns;
3816 			    // sc->kerns = kp;
3817 			}
3818 #ifdef UFO_GUESS_SCRIPTS
3819 			script = SCScriptFromUnicode(sc);
3820 			if ( script==DEFAULT_SCRIPT )
3821 			    script = SCScriptFromUnicode(ssc);
3822 #else
3823 			// Some test cases have proven that FontForge would do best to avoid classifying these.
3824 			script = DEFAULT_SCRIPT;
3825 #endif // UFO_GUESS_SCRIPTS
3826 			kp->subtable = SFSubTableFindOrMake(sf,
3827 				isv?CHR('v','k','r','n'):CHR('k','e','r','n'),
3828 				script, gpos_pair);
3829 		    }
3830 		  } else if (sc && right_class_name_record) {
3831 		    if (!TryAddRawGroupKern(sf, isv, class_name_pair_hash, &current_groupkern_index, &current_groupkern, sc->name, right_class_name_record->glif_name, offset)) {
3832 		      LogError(_("kerning.plist defines kerning between two glyphs that are already partially kerned.")); continue;
3833 		    }
3834 		  } else if (ssc && left_class_name_record) {
3835 		    if (!TryAddRawGroupKern(sf, isv, class_name_pair_hash, &current_groupkern_index, &current_groupkern, left_class_name_record->glif_name, ssc->name, offset)) {
3836 		      LogError(_("kerning.plist defines kerning between two glyphs that are already partially kerned.")); continue;
3837 		    }
3838 		  } else if (left_class_name_record && right_class_name_record) {
3839 		    struct kernclass *left_kc, *right_kc;
3840 		    int left_isv, right_isv, left_isr, right_isr, left_offset, right_offset;
3841 		    int left_exists = KerningClassSeekByAbsoluteIndex(sf, left_class_name_record->gid, &left_kc, &left_isv, &left_isr, &left_offset);
3842 		    int right_exists = KerningClassSeekByAbsoluteIndex(sf, right_class_name_record->gid, &right_kc, &right_isv, &right_isr, &right_offset);
3843 		    if ((left_kc == NULL) || (right_kc == NULL)) { LogError(_("kerning.plist references a missing kerning class.")); continue; } // I don't know how this would happen, at least as the code is now, but we do need to throw an error.
3844 		    if ((left_kc != right_kc)) { LogError(_("kerning.plist defines an offset between classes in different lookups.")); continue; }
3845 		    if ((left_offset > left_kc->first_cnt) || (right_offset > right_kc->second_cnt)) { LogError(_("There is a kerning class index error.")); continue; }
3846 		    if (left_kc->offsets_flags[left_offset*left_kc->second_cnt+right_offset]) { LogError(_("kerning.plist attempts to redefine a class kerning offset.")); continue; }
3847 		    // All checks pass. We add the virtual pair and then the real one.
3848 		    if (!TryAddRawGroupKern(sf, isv, class_name_pair_hash, &current_groupkern_index, &current_groupkern, left_class_name_record->glif_name, right_class_name_record->glif_name, offset)) {
3849 		      LogError(_("kerning.plist defines kerning between two glyphs that are already partially kerned.")); continue;
3850 		    }
3851 		    left_kc->offsets[left_offset*left_kc->second_cnt+right_offset] = offset;
3852 		    left_kc->offsets_flags[left_offset*left_kc->second_cnt+right_offset] |= FF_KERNCLASS_FLAG_NATIVE;
3853 		  }
3854 		}
3855 	    }
3856 	}
3857     }
3858     glif_name_index_destroy(group_name_hash);
3859     glif_name_index_destroy(class_name_hash);
3860     glif_name_index_destroy(class_name_pair_hash);
3861     xmlFreeDoc(doc);
3862 }
3863 
UFOAddName(SplineFont * sf,char * value,int strid)3864 static void UFOAddName(SplineFont *sf,char *value,int strid) {
3865     /* We simply assume that the entries in the name table are in English */
3866     /* The format doesn't say -- which bothers me */
3867     struct ttflangname *names;
3868 
3869     for ( names=sf->names; names!=NULL && names->lang!=0x409; names=names->next );
3870     if ( names==NULL ) {
3871 	names = chunkalloc(sizeof(struct ttflangname));
3872 	names->next = sf->names;
3873 	names->lang = 0x409;
3874 	sf->names = names;
3875     }
3876     names->names[strid] = value;
3877 }
3878 
UFOAddPrivate(SplineFont * sf,char * key,char * value)3879 static void UFOAddPrivate(SplineFont *sf,char *key,char *value) {
3880     char *pt;
3881 
3882     if ( sf->private==NULL )
3883 	sf->private = chunkalloc(sizeof(struct psdict));
3884     for ( pt=value; *pt!='\0'; ++pt ) {	/* Value might contain white space. turn into spaces */
3885 	if ( *pt=='\n' || *pt=='\r' || *pt=='\t' )
3886 	    *pt = ' ';
3887     }
3888     PSDictChangeEntry(sf->private, key, value);
3889 }
3890 
UFOAddPrivateArray(SplineFont * sf,char * key,xmlDocPtr doc,xmlNodePtr value)3891 static void UFOAddPrivateArray(SplineFont *sf,char *key,xmlDocPtr doc,xmlNodePtr value) {
3892     char space[400], *pt, *end;
3893     xmlNodePtr kid;
3894 
3895     if ( xmlStrcmp(value->name,(const xmlChar *) "array")!=0 )
3896 return;
3897     pt = space; end = pt+sizeof(space)-10;
3898     *pt++ = '[';
3899     for ( kid = value->children; kid!=NULL; kid=kid->next ) {
3900 	if ( xmlStrcmp(kid->name,(const xmlChar *) "integer")==0 ||
3901 		xmlStrcmp(kid->name,(const xmlChar *) "real")==0 ) {
3902 	    char *valName = (char *) xmlNodeListGetString(doc,kid->children,true);
3903 	    if ( pt+1+strlen(valName)<end ) {
3904 		if ( pt!=space+1 )
3905 		    *pt++=' ';
3906 		strcpy(pt,valName);
3907 		pt += strlen(pt);
3908 	    }
3909 	    free(valName);
3910 	}
3911     }
3912     if ( pt!=space+1 ) {
3913 	*pt++ = ']';
3914 	*pt++ = '\0';
3915 	UFOAddPrivate(sf,key,space);
3916     }
3917 }
3918 
UFOGetByteArray(char * array,int cnt,xmlDocPtr doc,xmlNodePtr value)3919 static void UFOGetByteArray(char *array,int cnt,xmlDocPtr doc,xmlNodePtr value) {
3920     xmlNodePtr kid;
3921     int i;
3922 
3923     memset(array,0,cnt);
3924 
3925     if ( xmlStrcmp(value->name,(const xmlChar *) "array")!=0 )
3926 return;
3927     i=0;
3928     for ( kid = value->children; kid!=NULL; kid=kid->next ) {
3929 	if ( xmlStrcmp(kid->name,(const xmlChar *) "integer")==0 ) {
3930 	    char *valName = (char *) xmlNodeListGetString(doc,kid->children,true);
3931 	    if ( i<cnt )
3932 		array[i++] = strtol(valName,NULL,10);
3933 	    free(valName);
3934 	}
3935     }
3936 }
3937 
UFOGetBits(xmlDocPtr doc,xmlNodePtr value)3938 static long UFOGetBits(xmlDocPtr doc,xmlNodePtr value) {
3939     xmlNodePtr kid;
3940     long mask=0;
3941 
3942     if ( xmlStrcmp(value->name,(const xmlChar *) "array")!=0 )
3943 return( 0 );
3944     for ( kid = value->children; kid!=NULL; kid=kid->next ) {
3945 	if ( xmlStrcmp(kid->name,(const xmlChar *) "integer")==0 ) {
3946 	    char *valName = (char *) xmlNodeListGetString(doc,kid->children,true);
3947 	    mask |= 1<<strtol(valName,NULL,10);
3948 	    free(valName);
3949 	}
3950     }
3951 return( mask );
3952 }
3953 
UFOGetBitArray(xmlDocPtr doc,xmlNodePtr value,uint32 * res,int len)3954 static void UFOGetBitArray(xmlDocPtr doc,xmlNodePtr value,uint32 *res,int len) {
3955     xmlNodePtr kid;
3956     int index;
3957 
3958     if ( xmlStrcmp(value->name,(const xmlChar *) "array")!=0 )
3959 return;
3960     for ( kid = value->children; kid!=NULL; kid=kid->next ) {
3961 	if ( xmlStrcmp(kid->name,(const xmlChar *) "integer")==0 ) {
3962 	    char *valName = (char *) xmlNodeListGetString(doc,kid->children,true);
3963 	    index = strtol(valName,NULL,10);
3964 	    if ( index < len<<5 )
3965 		res[index>>5] |= 1<<(index&31);
3966 	    free(valName);
3967 	}
3968     }
3969 }
3970 
SFReadUFO(char * basedir,int flags)3971 SplineFont *SFReadUFO(char *basedir, int flags) {
3972     xmlNodePtr plist, dict, keys, value;
3973     xmlNodePtr guidelineNode = NULL;
3974     xmlDocPtr doc;
3975     SplineFont *sf;
3976     SplineSet *lastglspl = NULL;
3977     xmlChar *keyname, *valname;
3978     char *stylename=NULL;
3979     char *temp, *glyphlist, *glyphdir;
3980     char *end;
3981     int as = -1, ds= -1, em= -1;
3982 
3983     sf = SplineFontEmpty();
3984     SFDefaultOS2Info(&sf->pfminfo, sf, ""); // We set the default pfm values.
3985     sf->pfminfo.pfmset = 1; // We flag the pfminfo as present since we expect the U. F. O. to set any desired values.
3986     int versionMajor = -1; // These are not native SplineFont values.
3987     int versionMinor = -1; // We store the U. F. O. values and then process them at the end.
3988     sf->pfminfo.stylemap = 0x0;
3989 
3990     temp = buildname(basedir,"fontinfo.plist");
3991     doc = xmlParseFile(temp);
3992     free(temp);
3993     locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
3994     switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
3995     if ( doc!=NULL ) {
3996       plist = xmlDocGetRootElement(doc);
3997       dict = FindNode(plist->children,"dict");
3998       if ( xmlStrcmp(plist->name,(const xmlChar *) "plist")!=0 || dict==NULL ) {
3999 	LogError(_("Expected property list file"));
4000 	xmlFreeDoc(doc);
4001       return( NULL );
4002       }
4003       for ( keys=dict->children; keys!=NULL; keys=keys->next ) {
4004 	for ( value = keys->next; value!=NULL && xmlStrcmp(value->name,(const xmlChar *) "text")==0;
4005 		value = value->next );
4006 	if ( value==NULL )
4007           break;
4008 	if ( xmlStrcmp(keys->name,(const xmlChar *) "key")==0 ) {
4009 	    keyname = xmlNodeListGetString(doc,keys->children,true);
4010 	    valname = xmlNodeListGetString(doc,value->children,true);
4011 	    keys = value;
4012 	    if ( xmlStrcmp(keyname,(xmlChar *) "familyName")==0 ) {
4013 		if (sf->familyname == NULL) sf->familyname = (char *) valname;
4014 		else free(valname);
4015 	    }
4016 	    else if ( xmlStrcmp(keyname,(xmlChar *) "styleName")==0 ) {
4017 		if (stylename == NULL) stylename = (char *) valname;
4018 		else free(valname);
4019 	    }
4020 	    else if ( xmlStrcmp(keyname,(xmlChar *) "styleMapFamilyName")==0 ) {
4021 		if (sf->styleMapFamilyName == NULL) sf->styleMapFamilyName = (char *) valname;
4022 		else free(valname);
4023 	    }
4024 	    else if ( xmlStrcmp(keyname,(xmlChar *) "styleMapStyleName")==0 ) {
4025 		if (strcmp((char *) valname, "regular")==0) sf->pfminfo.stylemap = 0x40;
4026         else if (strcmp((char *) valname, "italic")==0) sf->pfminfo.stylemap = 0x01;
4027         else if (strcmp((char *) valname, "bold")==0) sf->pfminfo.stylemap = 0x20;
4028         else if (strcmp((char *) valname, "bold italic")==0) sf->pfminfo.stylemap = 0x21;
4029 		free(valname);
4030 	    }
4031 	    else if ( xmlStrcmp(keyname,(xmlChar *) "fullName")==0 ||
4032 		    xmlStrcmp(keyname,(xmlChar *) "postscriptFullName")==0 ) {
4033 		if (sf->fullname == NULL) sf->fullname = (char *) valname;
4034 		else free(valname);
4035 	    }
4036 	    else if ( xmlStrcmp(keyname,(xmlChar *) "fontName")==0 ||
4037 		    xmlStrcmp(keyname,(xmlChar *) "postscriptFontName")==0 ) {
4038 		if (sf->fontname == NULL) sf->fontname = (char *) valname;
4039 		else free(valname);
4040 	    }
4041 	    else if ( xmlStrcmp(keyname,(xmlChar *) "weightName")==0 ||
4042 		    xmlStrcmp(keyname,(xmlChar *) "postscriptWeightName")==0 ) {
4043 		if (sf->weight == NULL) sf->weight = (char *) valname;
4044 		else free(valname);
4045 	    }
4046 	    else if ( xmlStrcmp(keyname,(xmlChar *) "note")==0 ) {
4047 		if (sf->comments == NULL) sf->comments = (char *) valname;
4048 		else free(valname);
4049 	    }
4050 	    else if ( xmlStrcmp(keyname,(xmlChar *) "copyright")==0 ) {
4051 		UFOAddName(sf,(char *) valname,ttf_copyright);
4052         /* sf->copyright hosts the old ASCII-only PS attribute */
4053         if (sf->copyright == NULL) sf->copyright = normalizeToASCII((char *) valname);
4054 		else free(valname);
4055 	    }
4056 	    else if ( xmlStrcmp(keyname,(xmlChar *) "trademark")==0 )
4057 		UFOAddName(sf,(char *) valname,ttf_trademark);
4058 	    else if ( strncmp((char *) keyname,"openTypeName",12)==0 ) {
4059 		if ( xmlStrcmp(keyname+12,(xmlChar *) "Designer")==0 )
4060 		    UFOAddName(sf,(char *) valname,ttf_designer);
4061 		else if ( xmlStrcmp(keyname+12,(xmlChar *) "DesignerURL")==0 )
4062 		    UFOAddName(sf,(char *) valname,ttf_designerurl);
4063 		else if ( xmlStrcmp(keyname+12,(xmlChar *) "Manufacturer")==0 )
4064 		    UFOAddName(sf,(char *) valname,ttf_manufacturer);
4065 		else if ( xmlStrcmp(keyname+12,(xmlChar *) "ManufacturerURL")==0 )
4066 		    UFOAddName(sf,(char *) valname,ttf_venderurl);
4067 		else if ( xmlStrcmp(keyname+12,(xmlChar *) "License")==0 )
4068 		    UFOAddName(sf,(char *) valname,ttf_license);
4069 		else if ( xmlStrcmp(keyname+12,(xmlChar *) "LicenseURL")==0 )
4070 		    UFOAddName(sf,(char *) valname,ttf_licenseurl);
4071 		else if ( xmlStrcmp(keyname+12,(xmlChar *) "Version")==0 )
4072 		    UFOAddName(sf,(char *) valname,ttf_version);
4073 		else if ( xmlStrcmp(keyname+12,(xmlChar *) "UniqueID")==0 )
4074 		    UFOAddName(sf,(char *) valname,ttf_uniqueid);
4075 		else if ( xmlStrcmp(keyname+12,(xmlChar *) "Description")==0 )
4076 		    UFOAddName(sf,(char *) valname,ttf_descriptor);
4077 		else if ( xmlStrcmp(keyname+12,(xmlChar *) "PreferredFamilyName")==0 )
4078 		    UFOAddName(sf,(char *) valname,ttf_preffamilyname);
4079 		else if ( xmlStrcmp(keyname+12,(xmlChar *) "PreferredSubfamilyName")==0 )
4080 		    UFOAddName(sf,(char *) valname,ttf_prefmodifiers);
4081 		else if ( xmlStrcmp(keyname+12,(xmlChar *) "CompatibleFullName")==0 )
4082 		    UFOAddName(sf,(char *) valname,ttf_compatfull);
4083 		else if ( xmlStrcmp(keyname+12,(xmlChar *) "SampleText")==0 )
4084 		    UFOAddName(sf,(char *) valname,ttf_sampletext);
4085 		else if ( xmlStrcmp(keyname+12,(xmlChar *) "WWSFamilyName")==0 )
4086 		    UFOAddName(sf,(char *) valname,ttf_wwsfamily);
4087 		else if ( xmlStrcmp(keyname+12,(xmlChar *) "WWSSubfamilyName")==0 )
4088 		    UFOAddName(sf,(char *) valname,ttf_wwssubfamily);
4089 		else
4090 		    free(valname);
4091 	    } else if ( strncmp((char *) keyname, "openTypeHhea",12)==0 ) {
4092 		if ( xmlStrcmp(keyname+12,(xmlChar *) "Ascender")==0 ) {
4093 		    sf->pfminfo.hhead_ascent = strtol((char *) valname,&end,10);
4094 		    sf->pfminfo.hheadascent_add = false;
4095 		} else if ( xmlStrcmp(keyname+12,(xmlChar *) "Descender")==0 ) {
4096 		    sf->pfminfo.hhead_descent = strtol((char *) valname,&end,10);
4097 		    sf->pfminfo.hheaddescent_add = false;
4098 		} else if ( xmlStrcmp(keyname+12,(xmlChar *) "LineGap")==0 )
4099 		    sf->pfminfo.linegap = strtol((char *) valname,&end,10);
4100 		free(valname);
4101 		sf->pfminfo.hheadset = true;
4102 	    } else if ( strncmp((char *) keyname,"openTypeVhea",12)==0 ) {
4103 		if ( xmlStrcmp(keyname+12,(xmlChar *) "LineGap")==0 )
4104 		    sf->pfminfo.vlinegap = strtol((char *) valname,&end,10);
4105 		sf->pfminfo.vheadset = true;
4106 		free(valname);
4107 	    } else if ( strncmp((char *) keyname,"openTypeOS2",11)==0 ) {
4108 		sf->pfminfo.pfmset = true;
4109 		if ( xmlStrcmp(keyname+11,(xmlChar *) "Panose")==0 ) {
4110 		    UFOGetByteArray(sf->pfminfo.panose,sizeof(sf->pfminfo.panose),doc,value);
4111 		    sf->pfminfo.panose_set = true;
4112 		} else if ( xmlStrcmp(keyname+11,(xmlChar *) "Type")==0 ) {
4113 		    sf->pfminfo.fstype = UFOGetBits(doc,value);
4114 		    if ( sf->pfminfo.fstype<0 ) {
4115 			/* all bits are set, but this is wrong, OpenType spec says */
4116 			/* bits 0, 4-7 and 10-15 must be unset, go see		   */
4117 			/* http://www.microsoft.com/typography/otspec/os2.htm#fst  */
4118 			LogError(_("Bad openTypeOS2type key: all bits are set. It will be ignored"));
4119 			sf->pfminfo.fstype = 0;
4120 		    }
4121 		} else if ( xmlStrcmp(keyname+11,(xmlChar *) "FamilyClass")==0 ) {
4122 		    char fc[2];
4123 		    UFOGetByteArray(fc,sizeof(fc),doc,value);
4124 		    sf->pfminfo.os2_family_class = (fc[0]<<8)|fc[1];
4125 		} else if ( xmlStrcmp(keyname+11,(xmlChar *) "WidthClass")==0 )
4126 		    sf->pfminfo.width = strtol((char *) valname,&end,10);
4127 		else if ( xmlStrcmp(keyname+11,(xmlChar *) "WeightClass")==0 )
4128 		    sf->pfminfo.weight = strtol((char *) valname,&end,10);
4129 		else if ( xmlStrcmp(keyname+11,(xmlChar *) "VendorID")==0 )
4130 		{
4131 		    const int os2_vendor_sz = sizeof(sf->pfminfo.os2_vendor);
4132 		    const int valname_len = c_strlen(valname);
4133 
4134 		    if( valname && valname_len <= os2_vendor_sz )
4135 			strncpy(sf->pfminfo.os2_vendor,valname,valname_len);
4136 
4137 		    char *temp = sf->pfminfo.os2_vendor + os2_vendor_sz - 1;
4138 		    while ( *temp == 0 && temp >= sf->pfminfo.os2_vendor )
4139 			*temp-- = ' ';
4140 		}
4141 		else if ( xmlStrcmp(keyname+11,(xmlChar *) "TypoAscender")==0 ) {
4142 		    sf->pfminfo.typoascent_add = false;
4143 		    sf->pfminfo.os2_typoascent = strtol((char *) valname,&end,10);
4144 		} else if ( xmlStrcmp(keyname+11,(xmlChar *) "TypoDescender")==0 ) {
4145 		    sf->pfminfo.typodescent_add = false;
4146 		    sf->pfminfo.os2_typodescent = strtol((char *) valname,&end,10);
4147 		} else if ( xmlStrcmp(keyname+11,(xmlChar *) "TypoLineGap")==0 )
4148 		    sf->pfminfo.os2_typolinegap = strtol((char *) valname,&end,10);
4149 		else if ( xmlStrcmp(keyname+11,(xmlChar *) "WinAscent")==0 ) {
4150 		    sf->pfminfo.winascent_add = false;
4151 		    sf->pfminfo.os2_winascent = strtol((char *) valname,&end,10);
4152 		} else if ( xmlStrcmp(keyname+11,(xmlChar *) "WinDescent")==0 ) {
4153 		    sf->pfminfo.windescent_add = false;
4154 		    sf->pfminfo.os2_windescent = strtol((char *) valname,&end,10);
4155 		} else if ( strncmp((char *) keyname+11,"Subscript",9)==0 ) {
4156 		    sf->pfminfo.subsuper_set = true;
4157 		    if ( xmlStrcmp(keyname+20,(xmlChar *) "XSize")==0 )
4158 			sf->pfminfo.os2_subxsize = strtol((char *) valname,&end,10);
4159 		    else if ( xmlStrcmp(keyname+20,(xmlChar *) "YSize")==0 )
4160 			sf->pfminfo.os2_subysize = strtol((char *) valname,&end,10);
4161 		    else if ( xmlStrcmp(keyname+20,(xmlChar *) "XOffset")==0 )
4162 			sf->pfminfo.os2_subxoff = strtol((char *) valname,&end,10);
4163 		    else if ( xmlStrcmp(keyname+20,(xmlChar *) "YOffset")==0 )
4164 			sf->pfminfo.os2_subyoff = strtol((char *) valname,&end,10);
4165 		} else if ( strncmp((char *) keyname+11, "Superscript",11)==0 ) {
4166 		    sf->pfminfo.subsuper_set = true;
4167 		    if ( xmlStrcmp(keyname+22,(xmlChar *) "XSize")==0 )
4168 			sf->pfminfo.os2_supxsize = strtol((char *) valname,&end,10);
4169 		    else if ( xmlStrcmp(keyname+22,(xmlChar *) "YSize")==0 )
4170 			sf->pfminfo.os2_supysize = strtol((char *) valname,&end,10);
4171 		    else if ( xmlStrcmp(keyname+22,(xmlChar *) "XOffset")==0 )
4172 			sf->pfminfo.os2_supxoff = strtol((char *) valname,&end,10);
4173 		    else if ( xmlStrcmp(keyname+22,(xmlChar *) "YOffset")==0 )
4174 			sf->pfminfo.os2_supyoff = strtol((char *) valname,&end,10);
4175 		} else if ( strncmp((char *) keyname+11, "Strikeout",9)==0 ) {
4176 		    sf->pfminfo.subsuper_set = true;
4177 		    if ( xmlStrcmp(keyname+20,(xmlChar *) "Size")==0 )
4178 			sf->pfminfo.os2_strikeysize = strtol((char *) valname,&end,10);
4179 		    else if ( xmlStrcmp(keyname+20,(xmlChar *) "Position")==0 )
4180 			sf->pfminfo.os2_strikeypos = strtol((char *) valname,&end,10);
4181 		} else if ( strncmp((char *) keyname+11, "CodePageRanges",14)==0 ) {
4182 		    UFOGetBitArray(doc,value,sf->pfminfo.codepages,2);
4183 		    sf->pfminfo.hascodepages = true;
4184 		} else if ( strncmp((char *) keyname+11, "UnicodeRanges",13)==0 ) {
4185 		    UFOGetBitArray(doc,value,sf->pfminfo.unicoderanges,4);
4186 		    sf->pfminfo.hasunicoderanges = true;
4187 		}
4188 		free(valname);
4189 	    } else if ( strncmp((char *) keyname, "postscript",10)==0 ) {
4190 		if ( xmlStrcmp(keyname+10,(xmlChar *) "UnderlineThickness")==0 )
4191 		    sf->uwidth = strtol((char *) valname,&end,10);
4192 		else if ( xmlStrcmp(keyname+10,(xmlChar *) "UnderlinePosition")==0 )
4193 		    sf->upos = strtol((char *) valname,&end,10);
4194 		else if ( xmlStrcmp(keyname+10,(xmlChar *) "BlueFuzz")==0 )
4195 		    UFOAddPrivate(sf,"BlueFuzz",(char *) valname);
4196 		else if ( xmlStrcmp(keyname+10,(xmlChar *) "BlueScale")==0 )
4197 		    UFOAddPrivate(sf,"BlueScale",(char *) valname);
4198 		else if ( xmlStrcmp(keyname+10,(xmlChar *) "BlueShift")==0 )
4199 		    UFOAddPrivate(sf,"BlueShift",(char *) valname);
4200 		else if ( xmlStrcmp(keyname+10,(xmlChar *) "BlueValues")==0 )
4201 		    UFOAddPrivateArray(sf,"BlueValues",doc,value);
4202 		else if ( xmlStrcmp(keyname+10,(xmlChar *) "OtherBlues")==0 )
4203 		    UFOAddPrivateArray(sf,"OtherBlues",doc,value);
4204 		else if ( xmlStrcmp(keyname+10,(xmlChar *) "FamilyBlues")==0 )
4205 		    UFOAddPrivateArray(sf,"FamilyBlues",doc,value);
4206 		else if ( xmlStrcmp(keyname+10,(xmlChar *) "FamilyOtherBlues")==0 )
4207 		    UFOAddPrivateArray(sf,"FamilyOtherBlues",doc,value);
4208 		else if ( xmlStrcmp(keyname+10,(xmlChar *) "StemSnapH")==0 )
4209 		    UFOAddPrivateArray(sf,"StemSnapH",doc,value);
4210 		else if ( xmlStrcmp(keyname+10,(xmlChar *) "StemSnapV")==0 )
4211 		    UFOAddPrivateArray(sf,"StemSnapV",doc,value);
4212 		else if ( xmlStrcmp(keyname+10,(xmlChar *) "ForceBold")==0 )
4213 		    UFOAddPrivate(sf,"ForceBold",(char *) value->name);
4214 			/* value->name is either true or false */
4215 		free(valname);
4216 	    } else if ( strncmp((char *)keyname,"macintosh",9)==0 ) {
4217 		if ( xmlStrcmp(keyname+9,(xmlChar *) "FONDName")==0 )
4218 		    sf->fondname = (char *) valname;
4219 		else
4220 		    free(valname);
4221 	    } else if ( xmlStrcmp(keyname,(xmlChar *) "unitsPerEm")==0 ) {
4222 		em = strtol((char *) valname,&end,10);
4223 		if ( *end!='\0' || em < 0 ) em = -1;
4224 		free(valname);
4225 	    } else if ( xmlStrcmp(keyname,(xmlChar *) "ascender")==0 ) {
4226 		as = strtod((char *) valname,&end);
4227 		if ( *end!='\0' ) as = -1;
4228 		else sf->ufo_ascent = as;
4229 		free(valname);
4230 	    } else if ( xmlStrcmp(keyname,(xmlChar *) "descender")==0 ) {
4231 		ds = -strtod((char *) valname,&end);
4232 		if ( *end!='\0' ) ds = -1;
4233 		else sf->ufo_descent = -ds;
4234 		free(valname);
4235 	    } else if ( xmlStrcmp(keyname,(xmlChar *) "xHeight")==0 ) {
4236 		sf->pfminfo.os2_xheight = strtol((char *) valname,&end,10); free(valname);
4237 	    } else if ( xmlStrcmp(keyname,(xmlChar *) "capHeight")==0 ) {
4238 		sf->pfminfo.os2_capheight = strtol((char *) valname,&end,10); free(valname);
4239 	    } else if ( xmlStrcmp(keyname,(xmlChar *) "italicAngle")==0 ||
4240 		    xmlStrcmp(keyname,(xmlChar *) "postscriptSlantAngle")==0 ) {
4241 		sf->italicangle = strtod((char *) valname,&end);
4242 		if ( *end!='\0' ) sf->italicangle = 0;
4243 		free(valname);
4244 	    } else if ( xmlStrcmp(keyname,(xmlChar *) "versionMajor")==0 ) {
4245 		versionMajor = strtol((char *) valname,&end, 10);
4246 		if ( *end!='\0' ) versionMajor = -1;
4247 		free(valname);
4248 	    } else if ( xmlStrcmp(keyname,(xmlChar *) "versionMinor")==0 ) {
4249 		versionMinor = strtol((char *) valname,&end, 10);
4250 		if ( *end!='\0' ) versionMinor = -1;
4251 		free(valname);
4252 	    } else if ( xmlStrcmp(keyname,(xmlChar *) "guidelines")==0 ) {
4253 		guidelineNode = value; // Guideline figuring needs ascent and descent, so we must parse this later.
4254 	    } else
4255 		free(valname);
4256 	    free(keyname);
4257 	}
4258       }
4259     }
4260     if ( em==-1 && as>=0 && ds>=0 )
4261 	em = as + ds;
4262     if ( em==as+ds ) {
4263 	/* Yay! They follow my conventions */;
4264     } else if ( em!=-1 ) {
4265 	as = 800*em/1000;
4266 	ds = em-as;
4267 	sf->invalidem = 1;
4268     }
4269     if ( em==-1 ) {
4270 	LogError(_("This font does not specify unitsPerEm, so we guess 1000."));
4271 	em = 1000;
4272     }
4273     sf->ascent = as; sf->descent = ds;
4274     // Ascent and descent are set, so we can parse the guidelines now.
4275     if (guidelineNode)
4276 	UFOLoadGuidelines(sf, NULL, 0, doc, guidelineNode, NULL, &lastglspl);
4277     if ( sf->fontname==NULL ) {
4278 	if ( stylename!=NULL && sf->familyname!=NULL )
4279 	    sf->fontname = strconcat3(sf->familyname,"-",stylename);
4280 	else
4281 	    sf->fontname = copy("Untitled");
4282     }
4283     if ( sf->fullname==NULL ) {
4284 	if ( stylename!=NULL && sf->familyname!=NULL )
4285 	    sf->fullname = strconcat3(sf->familyname," ",stylename);
4286 	else
4287 	    sf->fullname = copy(sf->fontname);
4288     }
4289     if ( sf->familyname==NULL )
4290 	sf->familyname = copy(sf->fontname);
4291     free(stylename); stylename = NULL;
4292     if (sf->styleMapFamilyName == NULL)
4293         sf->styleMapFamilyName = ""; // Empty default to disable fallback at export (not user-accessible anyway as of now).
4294     if ( sf->weight==NULL )
4295 	sf->weight = copy("Regular");
4296     // We can now free the document.
4297     if ( doc!=NULL )
4298         xmlFreeDoc(doc);
4299     // We first try to set the SplineFont version by using the native numeric U. F. O. values.
4300     if ( sf->version==NULL && versionMajor != -1 )
4301       sf->version = formatNumericVersion(versionMajor, versionMinor);
4302     // If that fails, we attempt to use the TrueType values.
4303     if ( sf->version==NULL && sf->names!=NULL &&
4304 	    sf->names->names[ttf_version]!=NULL &&
4305 	    strncmp(sf->names->names[ttf_version],"Version ",8)==0 )
4306 	sf->version = copy(sf->names->names[ttf_version]+8);
4307 
4308 	char * layercontentsname = buildname(basedir,"layercontents.plist");
4309 	char ** layernames = NULL;
4310 	if (layercontentsname == NULL) {
4311 		switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
4312 		return( NULL );
4313 	} else if ( GFileExists(layercontentsname)) {
4314 		xmlDocPtr layercontentsdoc = NULL;
4315 		xmlNodePtr layercontentsplist = NULL;
4316 		xmlNodePtr layercontentsdict = NULL;
4317 		xmlNodePtr layercontentslayer = NULL;
4318 		xmlNodePtr layercontentsvalue = NULL;
4319 		int layercontentslayercount = 0;
4320 		int layernamesbuffersize = 0;
4321 		int layercontentsvaluecount = 0;
4322 		if ( (layercontentsdoc = xmlParseFile(layercontentsname)) ) {
4323 			// The layercontents plist contains an array of double-element arrays. There is no top-level dict. Note that the indices in the layercontents array may not match those in the Fontforge layers array due to reserved spaces.
4324 			if ( ( layercontentsplist = xmlDocGetRootElement(layercontentsdoc) ) && ( layercontentsdict = FindNode(layercontentsplist->children,"array") ) ) {
4325 				layercontentslayercount = 0;
4326 				layernamesbuffersize = 2;
4327 				layernames = malloc(2*sizeof(char*)*layernamesbuffersize);
4328 				// Look through the children of the top-level array. Stop if one of them is not an array. (Ignore text objects since these probably just have whitespace.)
4329 				for ( layercontentslayer = layercontentsdict->children ;
4330 				( layercontentslayer != NULL ) && ( ( xmlStrcmp(layercontentslayer->name,(const xmlChar *) "array")==0 ) || ( xmlStrcmp(layercontentslayer->name,(const xmlChar *) "text")==0 ) ) ;
4331 				layercontentslayer = layercontentslayer->next ) {
4332 					if ( xmlStrcmp(layercontentslayer->name,(const xmlChar *) "array")==0 ) {
4333 						xmlChar * layerlabel = NULL;
4334 						xmlChar * layerglyphdirname = NULL;
4335 						layercontentsvaluecount = 0;
4336 						// Look through the children (effectively columns) of the layer array (the row). Reject non-string values.
4337 						for ( layercontentsvalue = layercontentslayer->children ;
4338 						( layercontentsvalue != NULL ) && ( ( xmlStrcmp(layercontentsvalue->name,(const xmlChar *) "string")==0 ) || ( xmlStrcmp(layercontentsvalue->name,(const xmlChar *) "text")==0 ) ) ;
4339 						layercontentsvalue = layercontentsvalue->next ) {
4340 							if ( xmlStrcmp(layercontentsvalue->name,(const xmlChar *) "string")==0 ) {
4341 								if (layercontentsvaluecount == 0) layerlabel = xmlNodeListGetString(layercontentsdoc, layercontentsvalue->xmlChildrenNode, true);
4342 								if (layercontentsvaluecount == 1) layerglyphdirname = xmlNodeListGetString(layercontentsdoc, layercontentsvalue->xmlChildrenNode, true);
4343 								layercontentsvaluecount++;
4344 								}
4345 						}
4346 						// We need two values (as noted above) per layer entry and ignore any layer lacking those.
4347 						if ((layercontentsvaluecount > 1) && (layernamesbuffersize < INT_MAX/2)) {
4348 							// Resize the layer names array as necessary.
4349 							if (layercontentslayercount >= layernamesbuffersize) {
4350 								layernamesbuffersize *= 2;
4351 								layernames = realloc(layernames, 2*sizeof(char*)*layernamesbuffersize);
4352 							}
4353 							// Fail silently on allocation failure; it's highly unlikely.
4354 							if (layernames != NULL) {
4355 								layernames[2*layercontentslayercount] = copy((char*)(layerlabel));
4356 								if (layernames[2*layercontentslayercount]) {
4357 									layernames[(2*layercontentslayercount)+1] = copy((char*)(layerglyphdirname));
4358 									if (layernames[(2*layercontentslayercount)+1])
4359 										layercontentslayercount++; // We increment only if both pointers are valid so as to avoid read problems later.
4360 									else
4361 										free(layernames[2*layercontentslayercount]);
4362 								}
4363 							}
4364 						}
4365 						if (layerlabel != NULL) { xmlFree(layerlabel); layerlabel = NULL; }
4366 						if (layerglyphdirname != NULL) { xmlFree(layerglyphdirname); layerglyphdirname = NULL; }
4367 					}
4368 				}
4369 				{
4370 					// Some typefaces (from very reputable shops) identify as following version 2 of the U. F. O. specification
4371 					// but have multiple layers and a layercontents.plist and omit the foreground layer from layercontents.plist.
4372 					// So, if the layercontents.plist includes no foreground layer and makes no other use of the directory glyphs
4373 					// and if that directory exists within the typeface, we map it to the foreground.
4374 					// Note that FontForge cannot round-trip this anomaly at present and shall include the foreground in
4375 					// layercontents.plist in any exported U. F. O..
4376 					int tmply = 0; // Temporary layer index.
4377 					while (tmply < layercontentslayercount && strcmp(layernames[2*tmply], "public.default") &&
4378 					  strcmp(layernames[2*tmply+1], "glyphs")) tmply ++;
4379 					// If tmply == layercontentslayercount then we know that no layer was named public.default and that no layer
4380 					// used the glyphs directory.
4381 					char * layerpath = buildname(basedir, "glyphs");
4382 					if (tmply == layercontentslayercount && layerpath != NULL && GFileExists(layerpath)) {
4383 						layercontentsvaluecount = 2;
4384 						// Note the copying here.
4385 						xmlChar * layerlabel = (xmlChar*)"public.default";
4386 						xmlChar * layerglyphdirname = (xmlChar*)"glyphs";
4387 						// We need two values (as noted above) per layer entry and ignore any layer lacking those.
4388 						if ((layercontentsvaluecount > 1) && (layernamesbuffersize < INT_MAX/2)) {
4389 							// Resize the layer names array as necessary.
4390 							if (layercontentslayercount >= layernamesbuffersize) {
4391 								layernamesbuffersize *= 2;
4392 								layernames = realloc(layernames, 2*sizeof(char*)*layernamesbuffersize);
4393 							}
4394 							// Fail silently on allocation failure; it's highly unlikely.
4395 							if (layernames != NULL) {
4396 								layernames[2*layercontentslayercount] = copy((char*)(layerlabel));
4397 								if (layernames[2*layercontentslayercount]) {
4398 									layernames[(2*layercontentslayercount)+1] = copy((char*)(layerglyphdirname));
4399 									if (layernames[(2*layercontentslayercount)+1])
4400 										layercontentslayercount++; // We increment only if both pointers are valid so as to avoid read problems later.
4401 									else
4402 										free(layernames[2*layercontentslayercount]);
4403 								}
4404 							}
4405 						}
4406 					}
4407 					if (layerpath != NULL) { free(layerpath); layerpath = NULL; }
4408 				}
4409 
4410 				if (layernames != NULL) {
4411 					int lcount = 0;
4412 					int auxpos = 2;
4413 					int layerdest = 0;
4414 					int bg = 1;
4415 					if (layercontentslayercount > 0) {
4416 						// Start reading layers.
4417 						for (lcount = 0; lcount < layercontentslayercount; lcount++) {
4418 							// We refuse to load a layer with an incorrect prefix.
4419                                                 	if (
4420 							(((strcmp(layernames[2*lcount],"public.default")==0) &&
4421 							(strcmp(layernames[2*lcount+1],"glyphs") == 0)) ||
4422 							(strstr(layernames[2*lcount+1],"glyphs.") == layernames[2*lcount+1])) &&
4423 							(glyphdir = buildname(basedir,layernames[2*lcount+1]))) {
4424                                                         	if ((glyphlist = buildname(glyphdir,"contents.plist"))) {
4425 									if ( !GFileExists(glyphlist)) {
4426 										LogError(_("No glyphs directory or no contents file"));
4427 									} else {
4428 										// Only public.default gets mapped as a foreground layer.
4429 										bg = 1;
4430 										// public.default and public.background have fixed mappings. Other layers start at 2.
4431 										if (strcmp(layernames[2*lcount],"public.default")==0) {
4432 											layerdest = ly_fore;
4433 											bg = 0;
4434 										} else if (strcmp(layernames[2*lcount],"public.background")==0) {
4435 											layerdest = ly_back;
4436 										} else {
4437 											layerdest = auxpos++;
4438 										}
4439 
4440 										// We ensure that the splinefont layer list has sufficient space.
4441 										if ( layerdest+1>sf->layer_cnt ) {
4442  										    sf->layers = realloc(sf->layers,(layerdest+1)*sizeof(LayerInfo));
4443 										    memset(sf->layers+sf->layer_cnt,0,((layerdest+1)-sf->layer_cnt)*sizeof(LayerInfo));
4444 										    sf->layer_cnt = layerdest+1;
4445 										}
4446 
4447 										// The check is redundant, but it allows us to copy from sfd.c.
4448 										if (( layerdest<sf->layer_cnt ) && sf->layers) {
4449 											if (sf->layers[layerdest].name)
4450 												free(sf->layers[layerdest].name);
4451 											sf->layers[layerdest].name = strdup(layernames[2*lcount]);
4452 											if (sf->layers[layerdest].ufo_path)
4453 												free(sf->layers[layerdest].ufo_path);
4454 											sf->layers[layerdest].ufo_path = strdup(layernames[2*lcount+1]);
4455 											sf->layers[layerdest].background = bg;
4456 											// Fetch glyphs.
4457 											UFOLoadGlyphs(sf,glyphdir,layerdest);
4458 											// Determine layer spline order.
4459 											sf->layers[layerdest].order2 = SFLFindOrder(sf,layerdest);
4460 											// Conform layer spline order (reworking control points if necessary).
4461 											SFLSetOrder(sf,layerdest,sf->layers[layerdest].order2);
4462 											// Set the grid order to the foreground order if appropriate.
4463 											if (layerdest == ly_fore) sf->grid.order2 = sf->layers[layerdest].order2;
4464 										}
4465 									}
4466 									free(glyphlist);
4467 								}
4468 								free(glyphdir);
4469 							}
4470 						}
4471 					} else {
4472 						LogError(_("layercontents.plist lists no valid layers."));
4473 					}
4474 					// Free layer names.
4475 					for (lcount = 0; lcount < layercontentslayercount; lcount++) {
4476 						if (layernames[2*lcount]) free(layernames[2*lcount]);
4477 						if (layernames[2*lcount+1]) free(layernames[2*lcount+1]);
4478 					}
4479 					free(layernames);
4480 				}
4481 			}
4482 			xmlFreeDoc(layercontentsdoc);
4483 		}
4484 	} else {
4485 		glyphdir = buildname(basedir,"glyphs");
4486     	glyphlist = buildname(glyphdir,"contents.plist");
4487     	if ( !GFileExists(glyphlist)) {
4488 			LogError(_("No glyphs directory or no contents file"));
4489     	} else {
4490 			UFOLoadGlyphs(sf,glyphdir,ly_fore);
4491 			sf->layers[ly_fore].order2 = sf->layers[ly_back].order2 = sf->grid.order2 =
4492 		    SFFindOrder(sf);
4493    	    	SFSetOrder(sf,sf->layers[ly_fore].order2);
4494 		}
4495 	    free(glyphlist);
4496 		free(glyphdir);
4497 	}
4498 	free(layercontentsname);
4499 
4500     sf->map = EncMap1to1(sf->glyphcnt);
4501 
4502     UFOHandleGroups(sf, basedir);
4503 
4504     UFOHandleKern3(sf,basedir,0);
4505     UFOHandleKern3(sf,basedir,1);
4506 
4507     /* Might as well check for feature files even if version 1 */
4508     temp = buildname(basedir,"features.fea");
4509     if ( GFileExists(temp))
4510 	SFApplyFeatureFilename(sf,temp);
4511     free(temp);
4512 
4513 #ifndef _NO_PYTHON
4514     temp = buildname(basedir,"lib.plist");
4515     doc = NULL;
4516     if ( GFileExists(temp))
4517 	doc = xmlParseFile(temp);
4518     free(temp);
4519     if ( doc!=NULL ) {
4520 		plist = xmlDocGetRootElement(doc);
4521 		dict = NULL;
4522 		if ( plist!=NULL )
4523 			dict = FindNode(plist->children,"dict");
4524 		if ( plist==NULL ||
4525 			xmlStrcmp(plist->name,(const xmlChar *) "plist")!=0 ||
4526 			dict==NULL ) {
4527 			LogError(_("Expected property list file"));
4528 		} else {
4529 			sf->python_persistent = LibToPython(doc,dict,1);
4530 			sf->python_persistent_has_lists = 1;
4531 		}
4532 		xmlFreeDoc(doc);
4533     }
4534 #endif
4535     switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
4536 return( sf );
4537 }
4538 
SplinePointListInterpretGlif(SplineFont * sf,char * filename,char * memory,int memlen,int em_size,int ascent,int is_stroked)4539 SplineSet *SplinePointListInterpretGlif(SplineFont *sf,char *filename,char *memory, int memlen,
4540 	int em_size,int ascent,int is_stroked) {
4541     xmlDocPtr doc;
4542     SplineChar *sc;
4543     SplineSet *ss;
4544 
4545     if ( filename!=NULL )
4546 	doc = xmlParseFile(filename);
4547     else
4548 	doc = xmlParseMemory(memory,memlen);
4549     if ( doc==NULL )
4550 return( NULL );
4551 
4552     locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
4553     switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
4554     setlocale(LC_NUMERIC,"C");
4555     sc = _UFOLoadGlyph(sf,doc,filename,NULL,NULL,ly_fore);
4556     switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
4557 
4558     if ( sc==NULL )
4559 return( NULL );
4560 
4561     ss = sc->layers[ly_fore].splines;
4562     sc->layers[ly_fore].splines = NULL;
4563     SplineCharFree(sc);
4564 return( ss );
4565 }
4566