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(×tamp);
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("<")-strlen("<"))) +
451 (count_occurrence(value, ">") * (strlen(">")-strlen("<"))) +
452 (count_occurrence(value, "&") * (strlen("&")-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, "<");
458 pos1 += strlen("<");
459 } else if ( *value=='>' ) {
460 strcat(tmpstring, ">");
461 pos1 += strlen(">");
462 } else if ( *value == '&' ) {
463 strcat(tmpstring, "&");
464 pos1 += strlen("&");
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, ¤t_groupkern_index, ¤t_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, ¤t_groupkern_index, ¤t_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, ¤t_groupkern_index, ¤t_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, ¤t_groupkern_index, ¤t_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