1 /* Copyright (C) 2000-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 "sfd.h"
31 
32 #include "autohint.h"
33 #include "baseviews.h"
34 #include "bvedit.h"
35 #include "cvimages.h"
36 #include "cvundoes.h"
37 #include "encoding.h"
38 #include "ffglib.h"
39 #include "fontforge.h"
40 #include "fvfonts.h"
41 #include "gfile.h"
42 #include "gutils.h"
43 #include "gwidget.h"
44 #include "lookups.h"
45 #include "mem.h"
46 #include "namelist.h"
47 #include "parsettf.h"
48 #include "psread.h"
49 #include "sfd1.h"
50 #include "splinefill.h"
51 #include "splinefont.h"
52 #include "splineorder2.h"
53 #include "splinesaveafm.h"
54 #include "splineutil.h"
55 #include "splineutil2.h"
56 #include "tottfgpos.h"
57 #include "ttfinstrs.h"
58 #include "ustring.h"
59 #include "utype.h"
60 #include "views.h"
61 
62 #include <dirent.h>
63 #include <limits.h>		/* For NAME_MAX or _POSIX_NAME_MAX */
64 #include <locale.h>
65 #include <math.h>
66 #include <sys/stat.h>
67 #include <sys/types.h>
68 #include <unistd.h>
69 #include <stdio.h>
70 
71 #ifdef _WIN32
72 #include <windows.h>
73 #endif
74 
75 #ifndef NAME_MAX
76 # ifndef  _POSIX_NAME_MAX
77 #  define _POSIX_NAME_MAX 512
78 # endif
79 # define NAME_MAX _POSIX_NAME_MAX
80 #endif
81 
82 int UndoRedoLimitToSave = 0;
83 int UndoRedoLimitToLoad = 0;
84 
85 static const char *joins[] = { "miter", "round", "bevel", "inher", NULL };
86 static const char *caps[] = { "butt", "round", "square", "inher", NULL };
87 static const char *spreads[] = { "pad", "reflect", "repeat", NULL };
88 
89 int prefRevisionsToRetain = 32;
90 #ifndef _NO_LIBPNG
91 int WritePNGInSFD = true;
92 #endif
93 
94 #define SFD_PTFLAG_TYPE_MASK          0x3
95 #define SFD_PTFLAG_IS_SELECTED        0x4
96 #define SFD_PTFLAG_NEXTCP_IS_DEFAULT  0x8
97 #define SFD_PTFLAG_PREVCP_IS_DEFAULT  0x10
98 #define SFD_PTFLAG_ROUND_IN_X         0x20
99 #define SFD_PTFLAG_ROUND_IN_Y         0x40
100 #define SFD_PTFLAG_INTERPOLATE        0x80
101 #define SFD_PTFLAG_INTERPOLATE_NEVER  0x100
102 #define SFD_PTFLAG_PREV_EXTREMA_MARKED_ACCEPTABLE  0x200
103 #define SFD_PTFLAG_FORCE_OPEN_PATH    0x400
104 
105 
106 
107 
108 /* I will retain this list in case there are still some really old sfd files */
109 /*  including numeric encodings.  This table maps them to string encodings */
110 static const char *charset_names[] = {
111     "custom",
112     "iso8859-1", "iso8859-2", "iso8859-3", "iso8859-4", "iso8859-5",
113     "iso8859-6", "iso8859-7", "iso8859-8", "iso8859-9", "iso8859-10",
114     "iso8859-11", "iso8859-13", "iso8859-14", "iso8859-15", "iso8859-16",
115     "koi8-r",
116     "jis201",
117     "win", "mac", "symbol", "zapfding", "adobestandard",
118     "jis208", "jis212", "ksc5601", "gb2312", "big5", "big5hkscs", "johab",
119     "unicode", "unicode4", "sjis", "wansung", "gb2312pk", NULL};
120 
121 static const char *unicode_interp_names[] = { "none", "adobe", "greek",
122     "japanese", "tradchinese", "simpchinese", "korean", "ams", NULL };
123 
124 /* sfdir files and extensions */
125 #define FONT_PROPS	"font.props"
126 #define STRIKE_PROPS	"strike.props"
127 #define EXT_CHAR	'.'
128 #define GLYPH_EXT	".glyph"
129 #define BITMAP_EXT	".bitmap"
130 #define STRIKE_EXT	".strike"
131 #define SUBFONT_EXT	".subfont"
132 #define INSTANCE_EXT	".instance"
133 
134 signed char inbase64[256] = {
135         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
136         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
137         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
138         52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
139         -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
140         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
141         -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
142         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
143         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
144         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
145         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
146         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
147         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
148         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
149         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
150         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
151 };
152 static char base64[64] = {
153  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
154  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
155  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
156  'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
157 
158 static const char *end_tt_instrs = "EndTTInstrs";
159 static void SFDConsumeUntil( FILE *sfd, const char** terminators );
160 static void SFDDumpRefs(FILE *sfd,RefChar *refs, int *newgids);
161 static void SFDDumpGuidelines(FILE *sfd,GuidelineSet *gl);
162 static RefChar *SFDGetRef(FILE *sfd, int was_enc);
163 static void SFDDumpImage(FILE *sfd,ImageList *img);
164 #ifndef _NO_LIBPNG
165 static void SFDDumpImagePNG(FILE *sfd,ImageList *img);
166 #endif
167 static AnchorPoint *SFDReadAnchorPoints(FILE *sfd,SplineChar *sc,AnchorPoint** alist, AnchorPoint *lastap);
168 static GuidelineSet *SFDReadGuideline(FILE *sfd, GuidelineSet **gll, GuidelineSet *lastgl);
169 static void SFDDumpTtfInstrs(FILE *sfd,SplineChar *sc);
170 static void SFDDumpTtfInstrsExplicit(FILE *sfd,uint8 *ttf_instrs, int16 ttf_instrs_len );
171 static void SFDDumpHintList(FILE *sfd,const char *key, StemInfo *h);
172 static void SFDDumpDHintList( FILE *sfd,const char *key, DStemInfo *d );
173 static StemInfo *SFDReadHints(FILE *sfd);
174 static DStemInfo *SFDReadDHints( SplineFont *sf,FILE *sfd,int old );
175 
PeekMatch(FILE * stream,const char * target)176 static int PeekMatch(FILE *stream, const char * target) {
177   // This returns 1 if target matches the next characters in the stream.
178   int pos1 = 0;
179   int lastread = getc(stream);
180   while (target[pos1] != '\0' && lastread != EOF && lastread == target[pos1]) {
181     pos1 ++; lastread = getc(stream);
182   }
183 
184   int rewind_amount = pos1 + ((lastread == EOF) ? 0 : 1);
185   fseek(stream, -rewind_amount, SEEK_CUR);
186   return (target[pos1] == '\0');
187 }
188 
utf7_encode(FILE * sfd,long ch)189 static void utf7_encode(FILE *sfd,long ch) {
190 
191     putc(base64[(ch>>18)&0x3f],sfd);
192     putc(base64[(ch>>12)&0x3f],sfd);
193     putc(base64[(ch>>6)&0x3f],sfd);
194     putc(base64[ch&0x3f],sfd);
195 }
196 
base64_encode(char * ostr,long ch)197 static char *base64_encode(char *ostr, long ch) {
198 
199     *ostr++ = base64[(ch>>18)&0x3f];
200     *ostr++ = base64[(ch>>12)&0x3f];
201     *ostr++ = base64[(ch>> 6)&0x3f];
202     *ostr++ = base64[(ch    )&0x3f];
203 return( ostr );
204 }
205 
SFDDumpUTF7Str(FILE * sfd,const char * _str)206 static void SFDDumpUTF7Str(FILE *sfd, const char *_str) {
207     int ch, prev_cnt=0, prev=0, in=0;
208     const unsigned char *str = (const unsigned char *) _str;
209 
210     putc('"',sfd);
211     if ( str!=NULL ) while ( (ch = *str++)!='\0' ) {
212 	/* Convert from utf8 to ucs4 */
213 	if ( ch<=127 )
214 	    /* Done */;
215 	else if ( ch<=0xdf && *str!='\0' ) {
216 	    ch = ((ch&0x1f)<<6) | (*str++&0x3f);
217 	} else if ( ch<=0xef && *str!='\0' && str[1]!='\0' ) {
218 	    ch = ((ch&0xf)<<12) | ((str[0]&0x3f)<<6) | (str[1]&0x3f);
219 	    str += 2;
220 	} else if ( *str!='\0' && str[1]!='\0' && str[2]!='\0' ) {
221 	    int w = ( ((ch&0x7)<<2) | ((str[0]&0x30)>>4) )-1;
222 	    int s1, s2;
223 	    s1 = (w<<6) | ((str[0]&0xf)<<2) | ((str[1]&0x30)>>4);
224 	    s2 = ((str[1]&0xf)<<6) | (str[2]&0x3f);
225 	    ch = (s1*0x400)+s2 + 0x10000;
226 	    str += 3;
227 	} else {
228 	    /* illegal */
229 	}
230 	if ( ch<127 && ch!='\n' && ch!='\r' && ch!='\\' && ch!='~' &&
231 		ch!='+' && ch!='=' && ch!='"' ) {
232 	    if ( prev_cnt!=0 ) {
233 		prev<<= (prev_cnt==1?16:8);
234 		utf7_encode(sfd,prev);
235 		prev_cnt=prev=0;
236 	    }
237 	    if ( in ) {
238 		if ( inbase64[ch]!=-1 || ch=='-' )
239 		    putc('-',sfd);
240 		in = 0;
241 	    }
242 	    putc(ch,sfd);
243 	} else if ( ch=='+' && !in ) {
244 	    putc('+',sfd);
245 	    putc('-',sfd);
246 	} else if ( prev_cnt== 0 ) {
247 	    if ( !in ) {
248 		putc('+',sfd);
249 		in = 1;
250 	    }
251 	    prev = ch;
252 	    prev_cnt = 2;		/* 2 bytes */
253 	} else if ( prev_cnt==2 ) {
254 	    prev<<=8;
255 	    prev += (ch>>8)&0xff;
256 	    utf7_encode(sfd,prev);
257 	    prev = (ch&0xff);
258 	    prev_cnt=1;
259 	} else {
260 	    prev<<=16;
261 	    prev |= ch;
262 	    utf7_encode(sfd,prev);
263 	    prev_cnt = prev = 0;
264 	}
265     }
266     if ( prev_cnt==2 ) {
267 	prev<<=8;
268 	utf7_encode(sfd,prev);
269     } else if ( prev_cnt==1 ) {
270 	prev<<=16;
271 	utf7_encode(sfd,prev);
272     }
273     putc('"',sfd);
274 }
275 
276 
utf8toutf7_copy(const char * _str)277 char *utf8toutf7_copy(const char *_str) {
278     int ch, prev_cnt=0, prev=0, in=0;
279     const unsigned char *str = (const unsigned char *) _str;
280     int i, len;
281     char *ret=NULL, *ostr=NULL;
282 
283     if ( str==NULL )
284 return( NULL );
285     for ( i=0; i<2; ++i ) {
286 	str = (const unsigned char *) _str;
287 	len= prev_cnt= prev= in=0;
288 	while ( (ch = *str++)!='\0' ) {
289 	    /* Convert from utf8 to ucs2 */
290 	    if ( ch<=127 )
291 		/* Done */;
292 	    else if ( ch<=0xdf && *str!='\0' ) {
293 		ch = ((ch&0x1f)<<6) | (*str++&0x3f);
294 	    } else if ( ch<=0xef && *str!='\0' && str[1]!='\0' ) {
295 		ch = ((ch&0xf)<<12) | ((str[0]&0x3f)<<6) | (str[1]&0x3f);
296 		str += 2;
297 	    } else if ( *str!='\0' && str[1]!='\0' && str[2]!='\0' ) {
298 		int w = ( ((ch&0x7)<<2) | ((str[0]&0x30)>>4) )-1;
299 		int s1, s2;
300 		s1 = (w<<6) | ((str[0]&0xf)<<2) | ((str[1]&0x30)>>4);
301 		s2 = ((str[1]&0xf)<<6) | (str[2]&0x3f);
302 		ch = (s1*0x400)+s2 + 0x10000;
303 		str += 3;
304 	    } else {
305 		/* illegal */
306 	    }
307 	    if ( ch<127 && ch!='\n' && ch!='\r' && ch!='\\' && ch!='~' &&
308 		    ch!='+' && ch!='=' && ch!='"' ) {
309 		if ( prev_cnt!=0 ) {
310 		    if ( i ) {
311 			prev<<= (prev_cnt==1?16:8);
312 			ostr = base64_encode(ostr,prev);
313 			prev_cnt=prev=0;
314 		    } else
315 			len += 4;
316 		}
317 		if ( in ) {
318 		    if ( inbase64[ch]!=-1 || ch=='-' ) {
319 			if ( i )
320 			    *ostr++ = '-';
321 			else
322 			    ++len;
323 		    }
324 		    in = 0;
325 		}
326 		if ( i )
327 		    *ostr++ = ch;
328 		else
329 		    ++len;
330 	    } else if ( ch=='+' && !in ) {
331 		if ( i ) {
332 		    *ostr++ = '+';
333 		    *ostr++ = '-';
334 		} else
335 		    len += 2;
336 	    } else if ( prev_cnt== 0 ) {
337 		if ( !in ) {
338 		    if ( i )
339 			*ostr++ = '+';
340 		    else
341 			++len;
342 		    in = 1;
343 		}
344 		prev = ch;
345 		prev_cnt = 2;		/* 2 bytes */
346 	    } else if ( prev_cnt==2 ) {
347 		prev<<=8;
348 		prev += (ch>>8)&0xff;
349 		if ( i ) {
350 		    ostr = base64_encode(ostr,prev);
351 		    prev_cnt=prev=0;
352 		} else
353 		    len += 4;
354 		prev = (ch&0xff);
355 		prev_cnt=1;
356 	    } else {
357 		prev<<=16;
358 		prev |= ch;
359 		if ( i ) {
360 		    ostr = base64_encode(ostr,prev);
361 		    prev_cnt=prev=0;
362 		} else
363 		    len += 4;
364 		prev_cnt = prev = 0;
365 	    }
366 	}
367 	if ( prev_cnt==2 ) {
368 	    prev<<=8;
369 	    if ( i ) {
370 		ostr = base64_encode(ostr,prev);
371 		prev_cnt=prev=0;
372 	    } else
373 		len += 4;
374 	} else if ( prev_cnt==1 ) {
375 	    prev<<=16;
376 	    if ( i ) {
377 		ostr = base64_encode(ostr,prev);
378 		prev_cnt=prev=0;
379 	    } else
380 		len += 4;
381 	}
382 	if ( in ) {
383 	    if ( i )
384 		*ostr++ = '-';
385 	    else
386 		++len;
387 	}
388 	if ( i==0 )
389 	    ostr = ret = malloc(len+1);
390     }
391     *ostr = '\0';
392 return( ret );
393 }
394 
395 /* Long lines can be broken by inserting \\\n (backslash newline) */
396 /*  into the line. I don't think this is ever ambiguous as I don't */
397 /*  think a line can end with backslash */
398 /* UPDATE: it can... that's handled in getquotedeol() below. */
nlgetc(FILE * sfd)399 static int nlgetc(FILE *sfd) {
400     int ch, ch2;
401 
402     ch=getc(sfd);
403     if ( ch!='\\' )
404 return( ch );
405     ch2 = getc(sfd);
406     if ( ch2=='\n' )
407 return( nlgetc(sfd));
408     ungetc(ch2,sfd);
409 return( ch );
410 }
411 
SFDReadUTF7Str(FILE * sfd)412 static char *SFDReadUTF7Str(FILE *sfd) {
413     char *buffer = NULL, *pt, *end = NULL;
414     int ch1, ch2, ch3, ch4, done, c;
415     int prev_cnt=0, prev=0, in=0;
416 
417     ch1 = nlgetc(sfd);
418     while ( isspace(ch1) && ch1!='\n' && ch1!='\r') ch1 = nlgetc(sfd);
419     if ( ch1=='\n' || ch1=='\r' )
420 	ungetc(ch1,sfd);
421     if ( ch1!='"' )
422 return( NULL );
423     pt = 0;
424     while ( (ch1=nlgetc(sfd))!=EOF && ch1!='"' ) {
425 	done = 0;
426 	if ( !done && !in ) {
427 	    if ( ch1=='+' ) {
428 		ch1 = nlgetc(sfd);
429 		if ( ch1=='-' ) {
430 		    ch1 = '+';
431 		    done = true;
432 		} else {
433 		    in = true;
434 		    prev_cnt = 0;
435 		}
436 	    } else
437 		done = true;
438 	}
439 	if ( !done ) {
440 	    if ( ch1=='-' ) {
441 		in = false;
442 	    } else if ( inbase64[ch1]==-1 ) {
443 		in = false;
444 		done = true;
445 	    } else {
446 		ch1 = inbase64[ch1];
447 		ch2 = inbase64[c = nlgetc(sfd)];
448 		if ( ch2==-1 ) {
449 		    ungetc(c, sfd);
450 		    ch2 = ch3 = ch4 = 0;
451 		} else {
452 		    ch3 = inbase64[c = nlgetc(sfd)];
453 		    if ( ch3==-1 ) {
454 			ungetc(c, sfd);
455 			ch3 = ch4 = 0;
456 		    } else {
457 			ch4 = inbase64[c = nlgetc(sfd)];
458 			if ( ch4==-1 ) {
459 			    ungetc(c, sfd);
460 			    ch4 = 0;
461 			}
462 		    }
463 		}
464 		ch1 = (ch1<<18) | (ch2<<12) | (ch3<<6) | ch4;
465 		if ( prev_cnt==0 ) {
466 		    prev = ch1&0xff;
467 		    ch1 >>= 8;
468 		    prev_cnt = 1;
469 		} else /* if ( prev_cnt == 1 ) */ {
470 		    ch1 |= (prev<<24);
471 		    prev = (ch1&0xffff);
472 		    ch1 = (ch1>>16)&0xffff;
473 		    prev_cnt = 2;
474 		}
475 		done = true;
476 	    }
477 	}
478 	if ( pt+10>=end ) {
479 	    if ( buffer==NULL ) {
480 		pt = buffer = malloc(400);
481 		end = buffer+400;
482 	    } else if (pt) {
483 		char *temp = realloc(buffer,end-buffer+400);
484 		pt = temp+(pt-buffer);
485 		end = temp+(end-buffer+400);
486 		buffer = temp;
487 	    }
488 	}
489 	if ( pt && done )
490 	    pt = utf8_idpb(pt,ch1,0);
491 	if ( prev_cnt==2 ) {
492 	    prev_cnt = 0;
493 	    if ( pt && prev!=0 )
494 		pt = utf8_idpb(pt,prev,0);
495 	}
496 	if ( pt==0 ) {
497 	    free(buffer);
498 	    return( NULL );
499 	}
500     }
501     if ( buffer==NULL )
502 return( NULL );
503     *pt = '\0';
504     pt = copy(buffer);
505     free(buffer );
506 return( pt );
507 }
508 
utf7toutf8_copy(const char * _str)509 char *utf7toutf8_copy(const char *_str) {
510     char *buffer = NULL, *pt, *end = NULL;
511     int ch1, ch2, ch3, ch4, done;
512     int prev_cnt=0, prev=0, in=0;
513     const char *str = _str;
514 
515     if ( str==NULL )
516 return( NULL );
517     buffer = pt = malloc(400);
518     end = pt+400;
519     while ( (ch1=*str++)!='\0' ) {
520 	done = 0;
521 	if ( !done && !in ) {
522 	    if ( ch1=='+' ) {
523 		ch1=*str++;
524 		if ( ch1=='-' ) {
525 		    ch1 = '+';
526 		    done = true;
527 		} else {
528 		    in = true;
529 		    prev_cnt = 0;
530 		}
531 	    } else
532 		done = true;
533 	}
534 	if ( !done ) {
535 	    if ( ch1=='-' ) {
536 		in = false;
537 	    } else if ( inbase64[ch1]==-1 ) {
538 		in = false;
539 		done = true;
540 	    } else {
541 		ch1 = inbase64[ch1];
542 		ch2 = inbase64[(unsigned char) *str++];
543 		if ( ch2==1 ) {
544 		    --str;
545 		    ch2 = ch3 = ch4 = 0;
546 		} else {
547 		    ch3 = inbase64[(unsigned char) *str++];
548 		    if ( ch3==-1 ) {
549 			--str;
550 			ch3 = ch4 = 0;
551 		    } else {
552 			ch4 = inbase64[(unsigned char) *str++];
553 			if ( ch4==-1 ) {
554 			    --str;
555 			    ch4 = 0;
556 			}
557 		    }
558 		}
559 		ch1 = (ch1<<18) | (ch2<<12) | (ch3<<6) | ch4;
560 		if ( prev_cnt==0 ) {
561 		    prev = ch1&0xff;
562 		    ch1 >>= 8;
563 		    prev_cnt = 1;
564 		} else /* if ( prev_cnt == 1 ) */ {
565 		    ch1 |= (prev<<24);
566 		    prev = (ch1&0xffff);
567 		    ch1 = (ch1>>16)&0xffff;
568 		    prev_cnt = 2;
569 		}
570 		done = true;
571 	    }
572 	}
573 	if ( pt+10>=end ) {
574 	    char *temp = realloc(buffer,end-buffer+400);
575 	    pt = temp+(pt-buffer);
576 	    end = temp+(end-buffer+400);
577 	    buffer = temp;
578 	}
579 	if ( pt && done )
580 	    pt = utf8_idpb(pt,ch1,0);
581 	if ( prev_cnt==2 ) {
582 	    prev_cnt = 0;
583 	    if ( pt && prev!=0 )
584 		pt = utf8_idpb(pt,prev,0);
585 	}
586 	if ( pt==0 ) {
587 	    free(buffer);
588 	    return( NULL );
589 	}
590     }
591     *pt = '\0';
592     pt = copy(buffer);
593     free(buffer );
594 return( pt );
595 }
596 
597 struct enc85 {
598     FILE *sfd;
599     unsigned char sofar[4];
600     int pos;
601     int ccnt;
602 };
603 
SFDEnc85(struct enc85 * enc,int ch)604 static void SFDEnc85(struct enc85 *enc,int ch) {
605     enc->sofar[enc->pos++] = ch;
606     if ( enc->pos==4 ) {
607 	unsigned int val = (enc->sofar[0]<<24)|(enc->sofar[1]<<16)|(enc->sofar[2]<<8)|enc->sofar[3];
608 	if ( val==0 ) {
609 	    fputc('z',enc->sfd);
610 	    ++enc->ccnt;
611 	} else {
612 	    int ch2, ch3, ch4, ch5;
613 	    ch5 = val%85;
614 	    val /= 85;
615 	    ch4 = val%85;
616 	    val /= 85;
617 	    ch3 = val%85;
618 	    val /= 85;
619 	    ch2 = val%85;
620 	    val /= 85;
621 	    fputc('!'+val,enc->sfd);
622 	    fputc('!'+ch2,enc->sfd);
623 	    fputc('!'+ch3,enc->sfd);
624 	    fputc('!'+ch4,enc->sfd);
625 	    fputc('!'+ch5,enc->sfd);
626 	    enc->ccnt += 5;
627 	    if ( enc->ccnt > 70 ) { fputc('\n',enc->sfd); enc->ccnt=0; }
628 	}
629 	enc->pos = 0;
630     }
631 }
632 
SFDEnc85EndEnc(struct enc85 * enc)633 static void SFDEnc85EndEnc(struct enc85 *enc) {
634     int i;
635     int ch2, ch3, ch4, ch5;
636     unsigned val;
637     if ( enc->pos==0 )
638 return;
639     for ( i=enc->pos; i<4; ++i )
640 	enc->sofar[i] = 0;
641     val = (enc->sofar[0]<<24)|(enc->sofar[1]<<16)|(enc->sofar[2]<<8)|enc->sofar[3];
642     if ( val==0 ) {
643 	fputc('z',enc->sfd);
644     } else {
645 	ch5 = val%85;
646 	val /= 85;
647 	ch4 = val%85;
648 	val /= 85;
649 	ch3 = val%85;
650 	val /= 85;
651 	ch2 = val%85;
652 	val /= 85;
653 	fputc('!'+val,enc->sfd);
654 	fputc('!'+ch2,enc->sfd);
655 	fputc('!'+ch3,enc->sfd);
656 	fputc('!'+ch4,enc->sfd);
657 	fputc('!'+ch5,enc->sfd);
658     }
659     enc->pos = 0;
660 }
661 
SFDDumpHintMask(FILE * sfd,HintMask * hintmask)662 static void SFDDumpHintMask(FILE *sfd,HintMask *hintmask) {
663     unsigned i, j;
664 
665     for ( i=HntMax/8-1; i>0; --i )
666 	if ( (*hintmask)[i]!=0 )
667     break;
668     for ( j=0; /* j <= i, but that might never be true, so we test j == i at end of loop */ ; ++j ) {
669 	if ( ((*hintmask)[j]>>4)<10 )
670 	    putc('0'+((*hintmask)[j]>>4),sfd);
671 	else
672 	    putc('a'-10+((*hintmask)[j]>>4),sfd);
673 	if ( ((*hintmask)[j]&0xf)<10 )
674 	    putc('0'+((*hintmask)[j]&0xf),sfd);
675 	else
676 	    putc('a'-10+((*hintmask)[j]&0xf),sfd);
677         if (j == i) break;
678     }
679 }
680 
SFDDumpSplineSet(FILE * sfd,SplineSet * spl,int want_order2)681 static void SFDDumpSplineSet(FILE *sfd, SplineSet *spl, int want_order2) {
682     SplinePoint *first, *sp;
683     // If there's no spline structure there should just be a single point,
684     // which is compatible with either order and therefore want_order2
685     int order2 = spl->first->next!=NULL ? spl->first->next->order2 : want_order2;
686     int reduce = (want_order2 && !order2);
687     if (order2 && !want_order2)
688 	IError("Asked for cubic when had quadratic");
689     SplineSet *nspl;
690 
691     for ( ; spl!=NULL; spl=spl->next ) {
692 	if (reduce) {
693 	    nspl = SSttfApprox(spl);
694 	    order2 = true;
695 	} else {
696 	    nspl = spl;
697 	}
698 	first = NULL;
699 	for ( sp = nspl->first; ; sp=sp->next->to ) {
700 #ifndef FONTFORGE_CONFIG_USE_DOUBLE
701 	    if ( first==NULL )
702 		fprintf( sfd, "%g %g m ", (double) sp->me.x, (double) sp->me.y );
703 	    else if ( sp->prev->islinear && sp->noprevcp )		/* Don't use known linear here. save control points if there are any */
704 		fprintf( sfd, " %g %g l ", (double) sp->me.x, (double) sp->me.y );
705 	    else
706 		fprintf( sfd, " %g %g %g %g %g %g c ",
707 			(double) sp->prev->from->nextcp.x, (double) sp->prev->from->nextcp.y,
708 			(double) sp->prevcp.x, (double) sp->prevcp.y,
709 			(double) sp->me.x, (double) sp->me.y );
710 #else
711 	    if ( first==NULL )
712 		fprintf( sfd, "%.12g %.12g m ", (double) sp->me.x, (double) sp->me.y );
713 	    else if ( sp->prev->islinear && sp->noprevcp )		/* Don't use known linear here. save control points if there are any */
714 		fprintf( sfd, " %.12g %.12g l ", (double) sp->me.x, (double) sp->me.y );
715 	    else
716 		fprintf( sfd, " %.12g %.12g %.12g %.12g %.12g %.12g c ",
717 			(double) sp->prev->from->nextcp.x, (double) sp->prev->from->nextcp.y,
718 			(double) sp->prevcp.x, (double) sp->prevcp.y,
719 			(double) sp->me.x, (double) sp->me.y );
720 #endif
721 	    int ptflags = 0;
722 	    ptflags = sp->pointtype|(sp->selected<<2)|
723 		(sp->nextcpdef<<3)|(sp->prevcpdef<<4)|
724 		(sp->roundx<<5)|(sp->roundy<<6)|
725 		(sp->ttfindex==0xffff?(1<<7):0)|
726 		(sp->dontinterpolate<<8)|
727 		((sp->prev && sp->prev->acceptableextrema)<<9);
728 
729 	    // Last point in the splineset, and we are an open path.
730 	    if( !sp->next
731 		&& spl->first && !spl->first->prev )
732 	    {
733 		ptflags |= SFD_PTFLAG_FORCE_OPEN_PATH;
734 	    }
735 
736 
737 	    fprintf(sfd, "%d", ptflags );
738 	    if ( order2 ) {
739 		if ( sp->ttfindex!=0xfffe && sp->nextcpindex!=0xfffe ) {
740 		    putc(',',sfd);
741 		    if ( sp->ttfindex==0xffff )
742 			fprintf(sfd,"-1");
743 		    else if ( sp->ttfindex!=0xfffe )
744 			fprintf(sfd,"%d",sp->ttfindex);
745 		    if ( sp->nextcpindex==0xffff )
746 			fprintf(sfd,",-1");
747 		    else if ( sp->nextcpindex!=0xfffe )
748 			fprintf(sfd,",%d",sp->nextcpindex);
749 		}
750 	    } else {
751 		if ( sp->hintmask!=NULL ) {
752 		    putc('x',sfd);
753 		    SFDDumpHintMask(sfd, sp->hintmask);
754 		}
755 	    }
756 	    putc('\n',sfd);
757 	    if (sp->name != NULL) {
758 		fputs("NamedP: ", sfd);
759 		SFDDumpUTF7Str(sfd, sp->name);
760 		putc('\n', sfd);
761 	    }
762 	    if ( sp==first )
763 	break;
764 	    if ( first==NULL ) first = sp;
765 	    if ( sp->next==NULL )
766 	break;
767 	}
768 	if ( spl->spiro_cnt!=0 ) {
769 	    int i;
770 	    fprintf( sfd, "  Spiro\n" );
771 	    for ( i=0; i<spl->spiro_cnt; ++i ) {
772 		fprintf( sfd, "    %g %g %c\n", spl->spiros[i].x, spl->spiros[i].y,
773 			    spl->spiros[i].ty&0x7f);
774 	    }
775 	    fprintf( sfd, "  EndSpiro\n" );
776 	}
777 	if ( spl->contour_name!=NULL ) {
778 	    fprintf( sfd, "  Named: " );
779 	    SFDDumpUTF7Str(sfd,spl->contour_name);
780 	    putc('\n',sfd);
781 	}
782 	if ( spl->is_clip_path ) {
783 	    fprintf( sfd, "  PathFlags: %d\n", spl->is_clip_path );
784 	}
785 	if ( spl->start_offset ) {
786 	    fprintf( sfd, "  PathStart: %d\n", spl->start_offset );
787 	}
788     if (reduce) SplinePointListFree(nspl);
789     }
790     fprintf( sfd, "EndSplineSet\n" );
791 }
792 
SFDDumpDeviceTable(FILE * sfd,DeviceTable * adjust)793 static void SFDDumpDeviceTable(FILE *sfd,DeviceTable *adjust) {
794     int i;
795 
796     if ( adjust==NULL )
797 return;
798     fprintf( sfd, "{" );
799     if ( adjust->corrections!=NULL ) {
800 	fprintf( sfd, "%d-%d ", adjust->first_pixel_size, adjust->last_pixel_size );
801 	for ( i=0; i<=adjust->last_pixel_size-adjust->first_pixel_size; ++i )
802 	    fprintf( sfd, "%s%d", i==0?"":",", adjust->corrections[i]);
803     }
804     fprintf( sfd, "}" );
805 }
806 
SFDDumpValDevTab(FILE * sfd,ValDevTab * adjust)807 static void SFDDumpValDevTab(FILE *sfd,ValDevTab *adjust) {
808     if ( adjust==NULL )
809 return;
810     fprintf( sfd, " [ddx=" ); SFDDumpDeviceTable(sfd,&adjust->xadjust);
811     fprintf( sfd, " ddy=" ); SFDDumpDeviceTable(sfd,&adjust->yadjust);
812     fprintf( sfd, " ddh=" ); SFDDumpDeviceTable(sfd,&adjust->xadv);
813     fprintf( sfd, " ddv=" ); SFDDumpDeviceTable(sfd,&adjust->yadv);
814     putc(']',sfd);
815 }
816 
SFDDumpAnchorPoints(FILE * sfd,AnchorPoint * ap)817 static void SFDDumpAnchorPoints(FILE *sfd,AnchorPoint *ap) {
818     if (ap==NULL) {
819 	return;
820     }
821 
822     for ( ; ap!=NULL; ap=ap->next )
823     {
824 	fprintf( sfd, "AnchorPoint: " );
825 	SFDDumpUTF7Str(sfd,ap->anchor->name);
826 	putc(' ',sfd);
827 	fprintf( sfd, "%g %g %s %d",
828 		(double) ap->me.x, (double) ap->me.y,
829 		ap->type==at_centry ? "entry" :
830 		ap->type==at_cexit ? "exit" :
831 		ap->type==at_mark ? "mark" :
832 		ap->type==at_basechar ? "basechar" :
833 		ap->type==at_baselig ? "baselig" : "basemark",
834 		ap->lig_index );
835 	if ( ap->xadjust.corrections!=NULL || ap->yadjust.corrections!=NULL ) {
836 	    putc(' ',sfd);
837 	    SFDDumpDeviceTable(sfd,&ap->xadjust);
838 	    putc(' ',sfd);
839 	    SFDDumpDeviceTable(sfd,&ap->yadjust);
840 	} else
841 	if ( ap->has_ttf_pt )
842 	    fprintf( sfd, " %d", ap->ttf_pt_index );
843 	putc('\n',sfd);
844     }
845 }
846 
847 /* Run length encoding */
848 /* We always start with a background pixel(1), each line is a series of counts */
849 /*  we alternate background/foreground. If we can't represent an entire run */
850 /*  as one count, then we can split it up into several smaller runs and put */
851 /*  0 counts in between */
852 /* counts 0-254 mean 0-254 pixels of the current color */
853 /* count 255 means that the next two bytes (bigendian) provide a two byte count */
854 /* count 255 0 n (n<255) means that the previous line should be repeated n+1 times */
855 /* count 255 0 255 means 255 pixels of the current color */
image2rle(struct _GImage * img,int * len)856 static uint8 *image2rle(struct _GImage *img, int *len) {
857     int max = img->height*img->bytes_per_line;
858     uint8 *rle, *pt, *end;
859     int cnt, set;
860     int i,j,k;
861 
862     *len = 0;
863     if ( img->image_type!=it_mono || img->bytes_per_line<5 )
864 return( NULL );
865     rle = calloc(max,sizeof(uint8)), pt = rle, end=rle+max-3;
866 
867     for ( i=0; i<img->height; ++i ) {
868 	if ( i!=0 ) {
869 	    if ( memcmp(img->data+i*img->bytes_per_line,
870 			img->data+(i-1)*img->bytes_per_line, img->bytes_per_line)== 0 ) {
871 		for ( k=1; k<img->height-i; ++k ) {
872 		    if ( memcmp(img->data+(i+k)*img->bytes_per_line,
873 				img->data+i*img->bytes_per_line, img->bytes_per_line)!= 0 )
874 		break;
875 		}
876 		i+=k;
877 		while ( k>0 ) {
878 		    if ( pt>end ) {
879 			free(rle);
880 return( NULL );
881 		    }
882 		    *pt++ = 255;
883 		    *pt++ = 0;
884 		    *pt++ = k>254 ? 254 : k;
885 		    k -= 254;
886 		}
887 		if ( i>=img->height )
888     break;
889 	    }
890 	}
891 
892 	set=1; cnt=0; j=0;
893 	while ( j<img->width ) {
894 	    for ( k=j; k<img->width; ++k ) {
895 		if (( set && !(img->data[i*img->bytes_per_line+(k>>3)]&(0x80>>(k&7))) ) ||
896 		    ( !set && (img->data[i*img->bytes_per_line+(k>>3)]&(0x80>>(k&7))) ))
897 	    break;
898 	    }
899 	    cnt = k-j;
900 	    j=k;
901 	    do {
902 		if ( pt>=end ) {
903 		    free(rle);
904 return( NULL );
905 		}
906 		if ( cnt<=254 )
907 		    *pt++ = cnt;
908 		else {
909 		    *pt++ = 255;
910 		    if ( cnt>65535 ) {
911 			*pt++ = 255;
912 			*pt++ = 255;
913 			*pt++ = 0;		/* nothing of the other color, we've still got more of this one */
914 		    } else {
915 			*pt++ = cnt>>8;
916 			*pt++ = cnt&0xff;
917 		    }
918 		}
919 		cnt -= 65535;
920 	    } while ( cnt>0 );
921 	    set = 1-set;
922 	}
923     }
924     *len = pt-rle;
925 return( rle );
926 }
927 
SFDDumpUndo(FILE * sfd,SplineChar * sc,Undoes * u,const char * keyPrefix,int idx)928 void SFDDumpUndo(FILE *sfd,SplineChar *sc,Undoes *u, const char* keyPrefix, int idx ) {
929     fprintf(sfd, "%sOperation\n",      keyPrefix );
930     fprintf(sfd, "Index: %d\n",        idx );
931     fprintf(sfd, "Type: %d\n",         u->undotype );
932     fprintf(sfd, "WasModified: %d\n",  u->was_modified );
933     fprintf(sfd, "WasOrder2: %d\n",    u->was_order2 );
934     if( u->layer != UNDO_LAYER_UNKNOWN )
935     {
936 	fprintf(sfd, "Layer: %d\n",    u->layer );
937     }
938 
939     switch( u->undotype )
940     {
941         case ut_tstate:
942         case ut_state:
943             fprintf(sfd, "Width: %d\n",           u->u.state.width );
944             fprintf(sfd, "VWidth: %d\n",          u->u.state.vwidth );
945             fprintf(sfd, "LBearingChange: %d\n",  u->u.state.lbearingchange );
946             fprintf(sfd, "UnicodeEnc: %d\n",      u->u.state.unicodeenc );
947             if( u->u.state.charname )
948                 fprintf(sfd, "Charname: \"%s\"\n", u->u.state.charname );
949             if( u->u.state.comment )
950                 fprintf(sfd, "Comment: \"%s\"\n", u->u.state.comment );
951             if( u->u.state.refs ) {
952                 SFDDumpRefs( sfd, u->u.state.refs, 0 );
953             }
954 	    if( u->u.state.images ) {
955 #ifndef _NO_LIBPNG
956                 if (WritePNGInSFD)
957                     SFDDumpImagePNG( sfd, u->u.state.images );
958                 else
959 #endif
960                     SFDDumpImage( sfd, u->u.state.images );
961             }
962             fprintf(sfd, "InstructionsLength: %d\n", u->u.state.instrs_len );
963             if( u->u.state.anchor ) {
964                 SFDDumpAnchorPoints( sfd, u->u.state.anchor );
965             }
966 	    if( u->u.state.splines ) {
967                 fprintf(sfd, "SplineSet\n" );
968                 SFDDumpSplineSet( sfd, u->u.state.splines, u->was_order2 );
969             }
970             break;
971 
972         case ut_statehint:
973         {
974             SplineChar* tsc = 0;
975             tsc = SplineCharCopy( sc, 0, 0 );
976             ExtractHints( tsc, u->u.state.hints, 1 );
977             SFDDumpHintList(  sfd, "HStem: ",  tsc->hstem);
978             SFDDumpHintList(  sfd, "VStem: ",  tsc->vstem);
979             SFDDumpDHintList( sfd, "DStem2: ", tsc->dstem);
980             SplineCharFree( tsc );
981 
982 	    if( u->u.state.instrs_len )
983                 SFDDumpTtfInstrsExplicit( sfd, u->u.state.instrs, u->u.state.instrs_len );
984             break;
985         }
986 
987         case ut_hints:
988         {
989             SplineChar* tsc = 0;
990             tsc = SplineCharCopy( sc, 0, 0 );
991             tsc->ttf_instrs = 0;
992             ExtractHints( tsc, u->u.state.hints, 1 );
993             SFDDumpHintList(  sfd, "HStem: ",  tsc->hstem);
994             SFDDumpHintList(  sfd, "VStem: ",  tsc->vstem);
995             SFDDumpDHintList( sfd, "DStem2: ", tsc->dstem);
996             SplineCharFree( tsc );
997 
998             if( u->u.state.instrs_len )
999                 SFDDumpTtfInstrsExplicit( sfd, u->u.state.instrs, u->u.state.instrs_len );
1000             if( u->copied_from && u->copied_from->fullname )
1001                 fprintf(sfd, "CopiedFrom: %s\n", u->copied_from->fullname );
1002             break;
1003         }
1004 
1005         case ut_width:
1006         case ut_vwidth:
1007         {
1008             fprintf(sfd, "Width: %d\n", u->u.width );
1009             break;
1010         }
1011 
1012         default:
1013         break;
1014     }
1015 
1016     fprintf(sfd, "End%sOperation\n", keyPrefix );
1017 }
1018 
SFDDumpImage(FILE * sfd,ImageList * img)1019 static void SFDDumpImage(FILE *sfd,ImageList *img) {
1020     GImage *image = img->image;
1021     struct _GImage *base = image->list_len==0?image->u.image:image->u.images[0];
1022     struct enc85 enc;
1023     int rlelen;
1024     uint8 *rle;
1025     int i;
1026 
1027     rle = image2rle(base,&rlelen);
1028     fprintf(sfd, "Image: %d %d %d %d %d %x %g %g %g %g %d\n",
1029 	    (int) base->width, (int) base->height, base->image_type,
1030 	    (int) (base->image_type==it_true?3*base->width:base->bytes_per_line),
1031 	    base->clut==NULL?0:base->clut->clut_len,(int) base->trans,
1032 	    (double) img->xoff, (double) img->yoff, (double) img->xscale, (double) img->yscale, rlelen );
1033     memset(&enc,'\0',sizeof(enc));
1034     enc.sfd = sfd;
1035     if ( base->clut!=NULL ) {
1036 	for ( i=0; i<base->clut->clut_len; ++i ) {
1037 	    SFDEnc85(&enc,base->clut->clut[i]>>16);
1038 	    SFDEnc85(&enc,(base->clut->clut[i]>>8)&0xff);
1039 	    SFDEnc85(&enc,base->clut->clut[i]&0xff);
1040 	}
1041     }
1042     if ( rle!=NULL ) {
1043 	uint8 *pt=rle, *end=rle+rlelen;
1044 	while ( pt<end )
1045 	    SFDEnc85(&enc,*pt++);
1046 	free( rle );
1047     } else {
1048 	for ( i=0; i<base->height; ++i ) {
1049 	    if ( base->image_type==it_rgba ) {
1050 		uint32 *ipt = (uint32 *) (base->data + i*base->bytes_per_line);
1051 		uint32 *iend = (uint32 *) (base->data + (i+1)*base->bytes_per_line);
1052 		while ( ipt<iend ) {
1053 		    SFDEnc85(&enc,*ipt>>24);
1054 		    SFDEnc85(&enc,(*ipt>>16)&0xff);
1055 		    SFDEnc85(&enc,(*ipt>>8)&0xff);
1056 		    SFDEnc85(&enc,*ipt&0xff);
1057 		    ++ipt;
1058 		}
1059 	    } else if ( base->image_type==it_true ) {
1060 		int *ipt = (int *) (base->data + i*base->bytes_per_line);
1061 		int *iend = (int *) (base->data + (i+1)*base->bytes_per_line);
1062 		while ( ipt<iend ) {
1063 		    SFDEnc85(&enc,*ipt>>16);
1064 		    SFDEnc85(&enc,(*ipt>>8)&0xff);
1065 		    SFDEnc85(&enc,*ipt&0xff);
1066 		    ++ipt;
1067 		}
1068 	    } else {
1069 		uint8 *pt = (uint8 *) (base->data + i*base->bytes_per_line);
1070 		uint8 *end = (uint8 *) (base->data + (i+1)*base->bytes_per_line);
1071 		while ( pt<end ) {
1072 		    SFDEnc85(&enc,*pt);
1073 		    ++pt;
1074 		}
1075 	    }
1076 	}
1077     }
1078     SFDEnc85EndEnc(&enc);
1079     fprintf(sfd,"\nEndImage\n" );
1080 }
1081 
1082 #ifndef _NO_LIBPNG
SFDDumpImagePNG(FILE * sfd,ImageList * img)1083 static void SFDDumpImagePNG(FILE *sfd,ImageList *img) {
1084     struct enc85 enc = {0};
1085     char* pngbuf;
1086     size_t pnglen, i;
1087 
1088     if (!GImageWritePngBuf(img->image, &pngbuf, &pnglen, 1, false)) {
1089         IError("Failed to serialise PNG image");
1090         return;
1091     }
1092 
1093     fprintf(sfd, "Image2: image/png %d %g %g %g %g\n",
1094         (int)pnglen, (double) img->xoff, (double) img->yoff, (double) img->xscale, (double) img->yscale );
1095 
1096     enc.sfd = sfd;
1097     for (i = 0; i<pnglen; ++i) {
1098         SFDEnc85(&enc, pngbuf[i]);
1099     }
1100     free(pngbuf);
1101 
1102     SFDEnc85EndEnc(&enc);
1103     fprintf(sfd,"\nEndImage2\n" );
1104 }
1105 #endif
1106 
SFDDumpHintList(FILE * sfd,const char * key,StemInfo * h)1107 static void SFDDumpHintList(FILE *sfd,const char *key, StemInfo *h) {
1108     HintInstance *hi;
1109 
1110     if ( h==NULL )
1111 return;
1112     fprintf(sfd, "%s", key );
1113     for ( ; h!=NULL; h=h->next ) {
1114 	fprintf(sfd, "%g %g", (double) h->start,(double) h->width );
1115 	if ( h->ghost ) putc('G',sfd);
1116 	if ( h->where!=NULL ) {
1117 	    putc('<',sfd);
1118 	    for ( hi=h->where; hi!=NULL; hi=hi->next )
1119 		fprintf(sfd, "%g %g%c", (double) hi->begin, (double) hi->end, hi->next?' ':'>');
1120 	}
1121 	putc(h->next?' ':'\n',sfd);
1122     }
1123 }
1124 
SFDDumpDHintList(FILE * sfd,const char * key,DStemInfo * d)1125 static void SFDDumpDHintList( FILE *sfd,const char *key, DStemInfo *d ) {
1126     HintInstance *hi;
1127 
1128     if ( d==NULL )
1129 return;
1130     fprintf(sfd, "%s", key );
1131     for ( ; d!=NULL; d=d->next ) {
1132 	fprintf(sfd, "%g %g %g %g %g %g",
1133 		(double) d->left.x, (double) d->left.y,
1134 		(double) d->right.x, (double) d->right.y,
1135 		(double) d->unit.x, (double) d->unit.y );
1136 	if ( d->where!=NULL ) {
1137 	    putc('<',sfd);
1138 	    for ( hi=d->where; hi!=NULL; hi=hi->next )
1139 		fprintf(sfd, "%g %g%c", (double) hi->begin, (double) hi->end, hi->next?' ':'>');
1140 	}
1141 	putc(d->next?' ':'\n',sfd);
1142     }
1143 }
1144 
SFDDumpTtfInstrsExplicit(FILE * sfd,uint8 * ttf_instrs,int16 ttf_instrs_len)1145 static void SFDDumpTtfInstrsExplicit(FILE *sfd,uint8 *ttf_instrs, int16 ttf_instrs_len )
1146 {
1147     char *instrs = _IVUnParseInstrs( ttf_instrs, ttf_instrs_len );
1148     char *pt;
1149     fprintf( sfd, "TtInstrs:\n" );
1150     for ( pt=instrs; *pt!='\0'; ++pt )
1151 	putc(*pt,sfd);
1152     if ( pt[-1]!='\n' )
1153 	putc('\n',sfd);
1154     free(instrs);
1155     fprintf( sfd, "%s\n", end_tt_instrs );
1156 }
SFDDumpTtfInstrs(FILE * sfd,SplineChar * sc)1157 static void SFDDumpTtfInstrs(FILE *sfd,SplineChar *sc)
1158 {
1159     SFDDumpTtfInstrsExplicit( sfd, sc->ttf_instrs,sc->ttf_instrs_len );
1160 }
1161 
SFDDumpTtfTable(FILE * sfd,struct ttf_table * tab,SplineFont * sf)1162 static void SFDDumpTtfTable(FILE *sfd,struct ttf_table *tab,SplineFont *sf) {
1163     if ( tab->tag == CHR('p','r','e','p') || tab->tag == CHR('f','p','g','m') ) {
1164 	/* These are tables of instructions and should be dumped as such */
1165 	char *instrs;
1166 	char *pt;
1167 	fprintf( sfd, "TtTable: %c%c%c%c\n",
1168 		(int) (tab->tag>>24), (int) ((tab->tag>>16)&0xff), (int) ((tab->tag>>8)&0xff), (int) (tab->tag&0xff) );
1169 	instrs = _IVUnParseInstrs( tab->data,tab->len );
1170 	for ( pt=instrs; *pt!='\0'; ++pt )
1171 	    putc(*pt,sfd);
1172 	if ( pt[-1]!='\n' )
1173 	    putc('\n',sfd);
1174 	free(instrs);
1175 	fprintf( sfd, "%s\n", end_tt_instrs );
1176     } else if ( (tab->tag == CHR('c','v','t',' ') || tab->tag == CHR('m','a','x','p')) &&
1177 	    (tab->len&1)==0 ) {
1178 	int i, ended;
1179 	uint8 *pt;
1180 	fprintf( sfd, "ShortTable: %c%c%c%c %d\n",
1181 		(int) (tab->tag>>24), (int) ((tab->tag>>16)&0xff), (int) ((tab->tag>>8)&0xff), (int) (tab->tag&0xff),
1182 		(int) (tab->len>>1) );
1183 	pt = (uint8*) tab->data;
1184 	ended = tab->tag!=CHR('c','v','t',' ') || sf->cvt_names==NULL;
1185 	for ( i=0; i<(tab->len>>1); ++i ) {
1186 	    int num = (int16) ((pt[0]<<8) | pt[1]);
1187 	    fprintf( sfd, "  %d", num );
1188 	    if ( !ended ) {
1189 		if ( sf->cvt_names[i]==END_CVT_NAMES )
1190 		    ended=true;
1191 		else if ( sf->cvt_names[i]!=NULL ) {
1192 		    putc(' ',sfd);
1193 		    SFDDumpUTF7Str(sfd,sf->cvt_names[i]);
1194 		    putc(' ',sfd);
1195 		}
1196 	    }
1197 	    putc('\n',sfd);
1198 	    pt += 2;
1199 	}
1200 	fprintf( sfd, "EndShort\n");
1201     } else {
1202 	/* maxp, who knows what. Dump 'em as binary for now */
1203 	struct enc85 enc;
1204 	int i;
1205 
1206 	memset(&enc,'\0',sizeof(enc));
1207 	enc.sfd = sfd;
1208 
1209 	fprintf( sfd, "TtfTable: %c%c%c%c %d\n",
1210 		(int) (tab->tag>>24), (int) ((tab->tag>>16)&0xff), (int) ((tab->tag>>8)&0xff), (int) (tab->tag&0xff),
1211 		(int) tab->len );
1212 	for ( i=0; i<tab->len; ++i )
1213 	    SFDEnc85(&enc,tab->data[i]);
1214 	SFDEnc85EndEnc(&enc);
1215 	fprintf(sfd,"\nEndTtf\n" );
1216     }
1217 }
1218 
SFDOmit(SplineChar * sc)1219 static int SFDOmit(SplineChar *sc) {
1220     int layer;
1221     BDFFont *bdf;
1222 
1223     if ( sc==NULL )
1224 return( true );
1225     if ( sc->comment!=NULL || sc->color!=COLOR_DEFAULT )
1226 return( false );
1227     for ( layer = ly_back; layer<sc->layer_cnt; ++layer ) {
1228 	if ( sc->layers[layer].splines!=NULL ||
1229 		sc->layers[layer].refs!=NULL ||
1230 		sc->layers[layer].images!=NULL )
1231 return( false );
1232     }
1233     if ( sc->parent->onlybitmaps ) {
1234 	for ( bdf = sc->parent->bitmaps; bdf!=NULL; bdf=bdf->next ) {
1235 	    if ( sc->orig_pos<bdf->glyphcnt && bdf->glyphs[sc->orig_pos]!=NULL )
1236 return( false );
1237 	}
1238     }
1239     if ( !sc->widthset )
1240 return(true);
1241 
1242 return( false );
1243 }
1244 
SFDDumpRefs(FILE * sfd,RefChar * refs,int * newgids)1245 static void SFDDumpRefs(FILE *sfd,RefChar *refs, int *newgids) {
1246     RefChar *ref;
1247 
1248     for ( ref=refs; ref!=NULL; ref=ref->next ) if ( ref->sc!=NULL ) {
1249 	fprintf(sfd, "Refer: %d %d %c %g %g %g %g %g %g %d",
1250 		    newgids!=NULL ? newgids[ref->sc->orig_pos]:ref->sc->orig_pos,
1251 		    ref->sc->unicodeenc,
1252 		    ref->selected?'S':'N',
1253 		    (double) ref->transform[0], (double) ref->transform[1], (double) ref->transform[2],
1254 		    (double) ref->transform[3], (double) ref->transform[4], (double) ref->transform[5],
1255 		    ref->use_my_metrics|(ref->round_translation_to_grid<<1)|
1256 		     (ref->point_match<<2));
1257 	if ( ref->point_match ) {
1258 	    fprintf(sfd, " %d %d", ref->match_pt_base, ref->match_pt_ref );
1259 	    if ( ref->point_match_out_of_date )
1260 		fprintf( sfd, " O" );
1261 	}
1262 	putc('\n',sfd);
1263     }
1264 }
1265 
SFDDumpGuidelines(FILE * sfd,GuidelineSet * gl)1266 static void SFDDumpGuidelines(FILE *sfd, GuidelineSet *gl) {
1267     if (gl==NULL) {
1268 	return;
1269     }
1270 
1271     for ( ; gl!=NULL; gl=gl->next )
1272     {
1273 	fprintf( sfd, "Guideline: " );
1274 	SFDDumpUTF7Str(sfd,gl->name);
1275 	putc(' ',sfd);
1276 	SFDDumpUTF7Str(sfd,gl->identifier);
1277 	putc(' ',sfd);
1278 	fprintf( sfd, "%g %g %g %u %d",
1279 		(double) gl->point.x, (double) gl->point.y,
1280 		(double) gl->angle, gl->color, gl->flags);
1281 	putc('\n',sfd);
1282     }
1283 }
1284 
SFDDumpMathVertex(FILE * sfd,struct mathkernvertex * vert,const char * name)1285 static void SFDDumpMathVertex(FILE *sfd,struct mathkernvertex *vert,const char *name) {
1286     int i;
1287 
1288     if ( vert==NULL || vert->cnt==0 )
1289 return;
1290 
1291     fprintf( sfd, "%s %d ", name, vert->cnt );
1292     for ( i=0; i<vert->cnt; ++i ) {
1293 	fprintf( sfd, " %d", vert->mkd[i].height );
1294 	SFDDumpDeviceTable(sfd,vert->mkd[i].height_adjusts );
1295 	fprintf( sfd, ",%d", vert->mkd[i].kern );
1296 	SFDDumpDeviceTable(sfd,vert->mkd[i].kern_adjusts );
1297     }
1298     putc('\n',sfd );
1299 }
1300 
SFDDumpGlyphVariants(FILE * sfd,struct glyphvariants * gv,const char * name)1301 static void SFDDumpGlyphVariants(FILE *sfd,struct glyphvariants *gv,const char *name) {
1302     int i;
1303 
1304     if ( gv==NULL )
1305 return;
1306 
1307     if ( gv->variants!=NULL )
1308 	fprintf( sfd, "GlyphVariants%s: %s\n", name, gv->variants );
1309     if ( gv->part_cnt!=0 ) {
1310 	if ( gv->italic_correction!=0 ) {
1311 	    fprintf( sfd, "GlyphComposition%sIC: %d", name, gv->italic_correction );
1312 	    if ( gv->italic_adjusts!=NULL ) {
1313 		putc(' ',sfd);
1314 		SFDDumpDeviceTable(sfd,gv->italic_adjusts);
1315 	    }
1316 	    putc('\n',sfd);
1317 	}
1318 	fprintf( sfd, "GlyphComposition%s: %d ", name, gv->part_cnt );
1319 	for ( i=0; i<gv->part_cnt; ++i ) {
1320 	    fprintf( sfd, " %s%%%d,%d,%d,%d", gv->parts[i].component,
1321 		    gv->parts[i].is_extender,
1322 		    gv->parts[i].startConnectorLength,
1323 		    gv->parts[i].endConnectorLength,
1324 		    gv->parts[i].fullAdvance);
1325 	}
1326 	putc('\n',sfd);
1327     }
1328 }
1329 
SFDDumpCharMath(FILE * sfd,SplineChar * sc)1330 static void SFDDumpCharMath(FILE *sfd,SplineChar *sc) {
1331     if ( sc->italic_correction!=TEX_UNDEF && sc->italic_correction!=0 ) {
1332 	fprintf( sfd, "ItalicCorrection: %d", sc->italic_correction );
1333 	if ( sc->italic_adjusts!=NULL ) {
1334 	    putc(' ',sfd);
1335 	    SFDDumpDeviceTable(sfd,sc->italic_adjusts);
1336 	}
1337 	putc('\n',sfd);
1338     }
1339     if ( sc->top_accent_horiz!=TEX_UNDEF ) {
1340 	fprintf( sfd, "TopAccentHorizontal: %d", sc->top_accent_horiz );
1341 	if ( sc->top_accent_adjusts!=NULL ) {
1342 	    putc(' ',sfd);
1343 	    SFDDumpDeviceTable(sfd,sc->top_accent_adjusts);
1344 	}
1345 	putc('\n',sfd);
1346     }
1347     if ( sc->is_extended_shape )
1348 	fprintf( sfd, "IsExtendedShape: %d\n", sc->is_extended_shape );
1349     SFDDumpGlyphVariants(sfd,sc->vert_variants,"Vertical");
1350     SFDDumpGlyphVariants(sfd,sc->horiz_variants,"Horizontal");
1351     if ( sc->mathkern!=NULL ) {
1352 	SFDDumpMathVertex(sfd,&sc->mathkern->top_right,"TopRightVertex:");
1353 	SFDDumpMathVertex(sfd,&sc->mathkern->top_left,"TopLeftVertex:");
1354 	SFDDumpMathVertex(sfd,&sc->mathkern->bottom_right,"BottomRightVertex:");
1355 	SFDDumpMathVertex(sfd,&sc->mathkern->bottom_left,"BottomLeftVertex:");
1356     }
1357 }
1358 
SFDPickleMe(FILE * sfd,void * python_data,int python_data_has_lists)1359 static void SFDPickleMe(FILE *sfd,void *python_data, int python_data_has_lists) {
1360     char *string, *pt;
1361 
1362 #ifdef _NO_PYTHON
1363     string = (char *) python_data;
1364 #else
1365     string = PyFF_PickleMeToString(python_data);
1366 #endif
1367     if ( string==NULL )
1368 return;
1369     if (python_data_has_lists)
1370     fprintf( sfd, "PickledDataWithLists: \"" );
1371     else
1372     fprintf( sfd, "PickledData: \"" );
1373     for ( pt=string; *pt; ++pt ) {
1374 	if ( *pt=='\\' || *pt=='"' )
1375 	    putc('\\',sfd);
1376 	putc(*pt,sfd);
1377     }
1378     fprintf( sfd, "\"\n");
1379 #ifndef _NO_PYTHON
1380     free(string);
1381 #endif
1382 }
1383 
SFDUnPickle(FILE * sfd,int python_data_has_lists)1384 static void *SFDUnPickle(FILE *sfd, int python_data_has_lists) {
1385     int ch, quoted;
1386     static int max = 0;
1387     static char *buf = NULL;
1388     char *pt, *end;
1389     int cnt;
1390 
1391     pt = buf; end = buf+max;
1392     while ( (ch=nlgetc(sfd))!='"' && ch!='\n' && ch!=EOF );
1393     if ( ch!='"' )
1394 return( NULL );
1395 
1396     quoted = false;
1397     while ( ((ch=nlgetc(sfd))!='"' || quoted) && ch!=EOF ) {
1398 	if ( !quoted && ch=='\\' )
1399 	    quoted = true;
1400 	else {
1401 	    if ( pt>=end ) {
1402 		cnt = pt-buf;
1403 		buf = realloc(buf,(max+=200)+1);
1404 		pt = buf+cnt;
1405 		end = buf+max;
1406 	    }
1407 	    *pt++ = ch;
1408 	    quoted = false;
1409 	}
1410     }
1411     if ( pt==buf )
1412 return( NULL );
1413     *pt='\0';
1414 #ifdef _NO_PYTHON
1415 return( copy(buf));
1416 #else
1417 return( PyFF_UnPickleMeToObjects(buf));
1418 #endif
1419     /* buf is a static buffer, I don't free it, I'll reuse it next time */
1420 }
1421 
1422 
SFDDumpGradient(FILE * sfd,const char * keyword,struct gradient * gradient)1423 static void SFDDumpGradient(FILE *sfd, const char *keyword, struct gradient *gradient) {
1424     int i;
1425 
1426     /* Use ";" as a coord separator because we treat "," as a potential decimal point */
1427     fprintf( sfd, "%s %g;%g %g;%g %g %s %d ", keyword,
1428 	    (double) gradient->start.x, (double) gradient->start.y,
1429 	    (double) gradient->stop.x, (double) gradient->stop.y,
1430 	    (double) gradient->radius,
1431 	    spreads[gradient->sm],
1432 	    gradient->stop_cnt );
1433     for ( i=0 ; i<gradient->stop_cnt; ++i ) {
1434 	fprintf( sfd, "{%g #%06x %g} ", (double) gradient->grad_stops[i].offset,
1435 		gradient->grad_stops[i].col, (double) gradient->grad_stops[i].opacity );
1436     }
1437     putc('\n',sfd);
1438 }
1439 
SFDDumpPattern(FILE * sfd,const char * keyword,struct pattern * pattern)1440 static void SFDDumpPattern(FILE *sfd, const char *keyword, struct pattern *pattern) {
1441 
1442     fprintf( sfd, "%s %s %g;%g [%g %g %g %g %g %g]\n", keyword,
1443 	    pattern->pattern,
1444 	    (double) pattern->width, (double) pattern->height,
1445 	    (double) pattern->transform[0], (double) pattern->transform[1],
1446 	    (double) pattern->transform[2], (double) pattern->transform[3],
1447 	    (double) pattern->transform[4], (double) pattern->transform[5] );
1448 }
1449 
SFD_DumpPST(FILE * sfd,SplineChar * sc)1450 void SFD_DumpPST( FILE *sfd, SplineChar *sc ) {
1451     PST *pst;
1452 
1453     for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
1454 	if (( pst->subtable==NULL && pst->type!=pst_lcaret) || pst->type==pst_null )
1455 	    /* Skip it */;
1456 	else {
1457 	    static const char *keywords[] = { "Null:", "Position2:", "PairPos2:",
1458 		    "Substitution2:",
1459 		    "AlternateSubs2:", "MultipleSubs2:", "Ligature2:",
1460 		    "LCarets2:", NULL };
1461 	    fprintf( sfd, "%s ", keywords[pst->type] );
1462 	    if ( pst->subtable!=NULL ) {
1463 		SFDDumpUTF7Str(sfd,pst->subtable->subtable_name);
1464 		putc(' ',sfd);
1465 	    }
1466 	    if ( pst->type==pst_position ) {
1467 		fprintf( sfd, "dx=%d dy=%d dh=%d dv=%d",
1468 			pst->u.pos.xoff, pst->u.pos.yoff,
1469 			pst->u.pos.h_adv_off, pst->u.pos.v_adv_off);
1470 		SFDDumpValDevTab(sfd,pst->u.pos.adjust);
1471 		putc('\n',sfd);
1472 	    } else if ( pst->type==pst_pair ) {
1473 		fprintf( sfd, "%s dx=%d dy=%d dh=%d dv=%d",
1474 			pst->u.pair.paired,
1475 			pst->u.pair.vr[0].xoff, pst->u.pair.vr[0].yoff,
1476 			pst->u.pair.vr[0].h_adv_off, pst->u.pair.vr[0].v_adv_off );
1477 		SFDDumpValDevTab(sfd,pst->u.pair.vr[0].adjust);
1478 		fprintf( sfd, " dx=%d dy=%d dh=%d dv=%d",
1479 			pst->u.pair.vr[1].xoff, pst->u.pair.vr[1].yoff,
1480 			pst->u.pair.vr[1].h_adv_off, pst->u.pair.vr[1].v_adv_off);
1481 		SFDDumpValDevTab(sfd,pst->u.pair.vr[1].adjust);
1482 		putc('\n',sfd);
1483 	    } else if ( pst->type==pst_lcaret ) {
1484 		int i;
1485 		fprintf( sfd, "%d ", pst->u.lcaret.cnt );
1486 		for ( i=0; i<pst->u.lcaret.cnt; ++i ) {
1487 		    fprintf( sfd, "%d", pst->u.lcaret.carets[i] );
1488                     if ( i<pst->u.lcaret.cnt-1 ) putc(' ',sfd);
1489                 }
1490 		fprintf( sfd, "\n" );
1491 	    } else
1492 		fprintf( sfd, "%s\n", pst->u.lig.components );
1493 	}
1494     }
1495 }
1496 
1497 
1498 
SFD_DumpKerns(FILE * sfd,SplineChar * sc,int * newgids)1499 void SFD_DumpKerns( FILE *sfd, SplineChar *sc, int *newgids ) {
1500     KernPair *kp;
1501     int v;
1502 
1503     for ( v=0; v<2; ++v ) {
1504 	kp = v ? sc->vkerns : sc->kerns;
1505 	if ( kp!=NULL ) {
1506 	    fprintf( sfd, v ? "VKerns2:" : "Kerns2:" );
1507 	    for ( ; kp!=NULL; kp=kp->next )
1508 		if ( !SFDOmit(kp->sc)) {
1509 		    fprintf( sfd, " %d %d ",
1510 			    newgids!=NULL?newgids[kp->sc->orig_pos]:kp->sc->orig_pos,
1511 			    kp->off );
1512 		    SFDDumpUTF7Str(sfd,kp->subtable->subtable_name);
1513 		    if ( kp->adjust!=NULL ) putc(' ',sfd);
1514 		    SFDDumpDeviceTable(sfd,kp->adjust);
1515 		}
1516 	    fprintf(sfd, "\n" );
1517 	}
1518     }
1519 }
1520 
SFDDumpCharStartingMarker(FILE * sfd,SplineChar * sc)1521 void SFDDumpCharStartingMarker(FILE *sfd,SplineChar *sc) {
1522     if ( AllAscii(sc->name))
1523 	fprintf(sfd, "StartChar: %s\n", sc->name );
1524     else {
1525 	fprintf(sfd, "StartChar: " );
1526 	SFDDumpUTF7Str(sfd,sc->name);
1527 	putc('\n',sfd);
1528     }
1529 }
1530 
1531 
SFDDumpChar(FILE * sfd,SplineChar * sc,EncMap * map,int * newgids,int todir,int saveUndoes)1532 static void SFDDumpChar(FILE *sfd,SplineChar *sc,EncMap *map,int *newgids,int todir,int saveUndoes) {
1533     // TODO: Output the U. F. O. glif name.
1534     ImageList *img;
1535     KernPair *kp;
1536     PST *pst;
1537     int i, v, enc;
1538     struct altuni *altuni;
1539 
1540     if (!todir)
1541 	putc('\n',sfd);
1542 
1543     SFDDumpCharStartingMarker( sfd, sc );
1544     if ( (enc = map->backmap[sc->orig_pos])>=map->enccount ) {
1545 	if ( sc->parent->cidmaster==NULL )
1546 	    IError("Bad reverse encoding");
1547 	enc = -1;
1548     }
1549     if ( sc->unicodeenc!=-1 &&
1550 	    ((map->enc->is_unicodebmp && sc->unicodeenc<0x10000) ||
1551 	     (map->enc->is_unicodefull && sc->unicodeenc < (int)unicode4_size)) )
1552 	/* If we have altunis, then the backmap may not give the primary */
1553 	/*  unicode code point, which is what we need here */
1554 	fprintf(sfd, "Encoding: %d %d %d\n", sc->unicodeenc, sc->unicodeenc,
1555 		newgids!=NULL?newgids[sc->orig_pos]:sc->orig_pos);
1556     else
1557 	fprintf(sfd, "Encoding: %d %d %d\n", enc, sc->unicodeenc,
1558 		newgids!=NULL?newgids[sc->orig_pos]:sc->orig_pos);
1559     if ( sc->altuni ) {
1560 	fprintf( sfd, "AltUni2:" );
1561 	for ( altuni = sc->altuni; altuni!=NULL; altuni=altuni->next )
1562 	    fprintf( sfd, " %06x.%06x.%x", altuni->unienc, altuni->vs, altuni->fid );
1563 	putc( '\n', sfd);
1564     }
1565     if ( sc->glif_name ) {
1566         fprintf(sfd, "GlifName: ");
1567         if ( AllAscii(sc->glif_name))
1568 	    fprintf(sfd, "%s", sc->glif_name );
1569         else {
1570 	    SFDDumpUTF7Str(sfd,sc->glif_name);
1571         }
1572         putc('\n',sfd);
1573     }
1574     fprintf(sfd, "Width: %d\n", sc->width );
1575     if ( sc->vwidth!=sc->parent->ascent+sc->parent->descent )
1576 	fprintf(sfd, "VWidth: %d\n", sc->vwidth );
1577     if ( sc->glyph_class!=0 )
1578 	fprintf(sfd, "GlyphClass: %d\n", sc->glyph_class );
1579     if ( sc->unlink_rm_ovrlp_save_undo )
1580 	fprintf(sfd, "UnlinkRmOvrlpSave: %d\n", sc->unlink_rm_ovrlp_save_undo );
1581     if ( sc->inspiro )
1582 	fprintf(sfd, "InSpiro: %d\n", sc->inspiro );
1583     if ( sc->lig_caret_cnt_fixed )
1584 	fprintf(sfd, "LigCaretCntFixed: %d\n", sc->lig_caret_cnt_fixed );
1585     if ( sc->changedsincelasthinted|| sc->manualhints || sc->widthset )
1586 	fprintf(sfd, "Flags: %s%s%s%s%s\n",
1587 		sc->changedsincelasthinted?"H":"",
1588 		sc->manualhints?"M":"",
1589 		sc->widthset?"W":"",
1590 		sc->views!=NULL?"O":"",
1591 		sc->instructions_out_of_date?"I":"");
1592     if ( sc->tex_height!=TEX_UNDEF || sc->tex_depth!=TEX_UNDEF )
1593 	fprintf( sfd, "TeX: %d %d\n", sc->tex_height, sc->tex_depth );
1594     if ( sc->is_extended_shape || sc->italic_correction!=TEX_UNDEF ||
1595 	    sc->top_accent_horiz!=TEX_UNDEF || sc->vert_variants!=NULL ||
1596 	    sc->horiz_variants!=NULL || sc->mathkern!=NULL )
1597 	SFDDumpCharMath(sfd,sc);
1598 #if HANYANG
1599     if ( sc->compositionunit )
1600 	fprintf( sfd, "CompositionUnit: %d %d\n", sc->jamo, sc->varient );
1601 #endif
1602     SFDDumpHintList(sfd,"HStem: ", sc->hstem);
1603     SFDDumpHintList(sfd,"VStem: ", sc->vstem);
1604     SFDDumpDHintList(sfd,"DStem2: ", sc->dstem);
1605     if ( sc->countermask_cnt!=0 ) {
1606 	fprintf( sfd, "CounterMasks: %d", sc->countermask_cnt );
1607 	for ( i=0; i<sc->countermask_cnt; ++i ) {
1608 	    putc(' ',sfd);
1609 	    SFDDumpHintMask(sfd,&sc->countermasks[i]);
1610 	}
1611 	putc('\n',sfd);
1612     }
1613     if ( sc->ttf_instrs_len!=0 )
1614 	SFDDumpTtfInstrs(sfd,sc);
1615     SFDDumpAnchorPoints(sfd,sc->anchor);
1616     fprintf( sfd, "LayerCount: %d\n", sc->layer_cnt );
1617     for ( i=0; i<sc->layer_cnt; ++i ) {
1618         if( saveUndoes && UndoRedoLimitToSave > 0) {
1619             if( sc->layers[i].undoes || sc->layers[i].redoes ) {
1620                 fprintf(sfd, "UndoRedoHistory\n" );
1621                 fprintf(sfd, "Layer: %d\n", i );
1622                 Undoes *undo = 0;
1623                 int idx   = 0;
1624                 int limit = 0;
1625 
1626                 fprintf(sfd, "Undoes\n" );
1627                 idx = 0;
1628                 undo = sc->layers[i].undoes;
1629                 for( limit = UndoRedoLimitToSave;
1630                      undo && (limit==-1 || limit>0);
1631                      undo = undo->next, idx++ ) {
1632                     SFDDumpUndo( sfd, sc, undo, "Undo", idx );
1633                     if( limit > 0 )
1634                         limit--;
1635                 }
1636                 fprintf(sfd, "EndUndoes\n" );
1637 
1638 		fprintf(sfd, "Redoes\n" );
1639                 idx = 0;
1640                 limit = UndoRedoLimitToSave;
1641                 undo = sc->layers[i].redoes;
1642                 for( limit = UndoRedoLimitToSave;
1643                      undo && (limit==-1 || limit>0);
1644                      undo = undo->next, idx++ ) {
1645                     SFDDumpUndo( sfd, sc, undo, "Redo", idx );
1646                     if( limit > 0 )
1647                         limit--;
1648                 }
1649                 fprintf(sfd, "EndRedoes\n" );
1650                 fprintf(sfd, "EndUndoRedoHistory\n" );
1651             }
1652         }
1653 
1654 	if ( sc->parent->multilayer ) {
1655 	    fprintf(sfd, "Layer: %d  %d %d %d  #%06x %g  #%06x %g %g %s %s [%g %g %g %g] [",
1656 		    i, sc->layers[i].dofill, sc->layers[i].dostroke, sc->layers[i].fillfirst,
1657 		    sc->layers[i].fill_brush.col, (double) sc->layers[i].fill_brush.opacity,
1658 		    sc->layers[i].stroke_pen.brush.col, (double) sc->layers[i].stroke_pen.brush.opacity,
1659 		    (double) sc->layers[i].stroke_pen.width, joins[sc->layers[i].stroke_pen.linejoin], caps[sc->layers[i].stroke_pen.linecap],
1660 		    (double) sc->layers[i].stroke_pen.trans[0], (double) sc->layers[i].stroke_pen.trans[1],
1661 		    (double) sc->layers[i].stroke_pen.trans[2], (double) sc->layers[i].stroke_pen.trans[3] );
1662 	    if ( sc->layers[i].stroke_pen.dashes[0]==0 && sc->layers[i].stroke_pen.dashes[1]==DASH_INHERITED )
1663 		fprintf(sfd,"0 %d]\n", DASH_INHERITED);
1664 	    else { int j;
1665 		for ( j=0; j<DASH_MAX && sc->layers[i].stroke_pen.dashes[j]!=0; ++j )
1666 		    fprintf( sfd,"%d ", sc->layers[i].stroke_pen.dashes[j]);
1667 		fprintf(sfd,"]\n");
1668 	    }
1669 	    if ( sc->layers[i].fill_brush.gradient!=NULL )
1670 		SFDDumpGradient(sfd,"FillGradient:", sc->layers[i].fill_brush.gradient );
1671 	    else if ( sc->layers[i].fill_brush.pattern!=NULL )
1672 		SFDDumpPattern(sfd,"FillPattern:", sc->layers[i].fill_brush.pattern );
1673 	    if ( sc->layers[i].stroke_pen.brush.gradient!=NULL )
1674 		SFDDumpGradient(sfd,"StrokeGradient:", sc->layers[i].stroke_pen.brush.gradient );
1675 	    else if ( sc->layers[i].stroke_pen.brush.pattern!=NULL )
1676 		SFDDumpPattern(sfd,"StrokePattern:", sc->layers[i].stroke_pen.brush.pattern );
1677 	} else {
1678 	    if ( sc->layers[i].images==NULL && sc->layers[i].splines==NULL &&
1679 		    sc->layers[i].refs==NULL && (sc->layers[i].validation_state&vs_known) == 0 &&
1680 		    sc->layers[i].python_persistent == NULL)
1681     continue;
1682 	    if ( i==ly_back )
1683 		fprintf( sfd, "Back\n" );
1684 	    else if ( i==ly_fore )
1685 		fprintf( sfd, "Fore\n" );
1686 	    else
1687 		fprintf(sfd, "Layer: %d\n", i );
1688 	}
1689 	for ( img=sc->layers[i].images; img!=NULL; img=img->next )
1690 #ifndef _NO_LIBPNG
1691         if (WritePNGInSFD)
1692 	    SFDDumpImagePNG(sfd,img);
1693         else
1694 #endif
1695 	    SFDDumpImage(sfd,img);
1696 	if ( sc->layers[i].splines!=NULL ) {
1697 	    fprintf(sfd, "SplineSet\n" );
1698 	    SFDDumpSplineSet(sfd,sc->layers[i].splines,sc->layers[i].order2);
1699 	}
1700 	SFDDumpRefs(sfd,sc->layers[i].refs,newgids);
1701 	SFDDumpGuidelines(sfd, sc->layers[i].guidelines);
1702 	if ( sc->layers[i].validation_state&vs_known )
1703 	    fprintf( sfd, "Validated: %d\n", sc->layers[i].validation_state );
1704         if ( sc->layers[i].python_persistent!=NULL )
1705 	  SFDPickleMe(sfd,sc->layers[i].python_persistent,sc->layers[i].python_persistent_has_lists);
1706     }
1707     for ( v=0; v<2; ++v ) {
1708 	kp = v ? sc->vkerns : sc->kerns;
1709 	if ( kp!=NULL ) {
1710 	    fprintf( sfd, v ? "VKerns2:" : "Kerns2:" );
1711 	    for ( ; kp!=NULL; kp=kp->next ) {
1712             if ( !SFDOmit(kp->sc)) {
1713                 fprintf( sfd, " %d %d ",
1714                          newgids!=NULL?newgids[kp->sc->orig_pos]:kp->sc->orig_pos,
1715                          kp->off );
1716                 SFDDumpUTF7Str(sfd,kp->subtable->subtable_name);
1717                 if ( kp->adjust!=NULL ) putc(' ',sfd);
1718                 SFDDumpDeviceTable(sfd,kp->adjust);
1719             }
1720         }
1721 	    fprintf(sfd, "\n" );
1722 	}
1723     }
1724     for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
1725 	if (( pst->subtable==NULL && pst->type!=pst_lcaret) || pst->type==pst_null )
1726 	    /* Skip it */;
1727 	else {
1728 	    static const char *keywords[] = { "Null:", "Position2:", "PairPos2:",
1729 		    "Substitution2:",
1730 		    "AlternateSubs2:", "MultipleSubs2:", "Ligature2:",
1731 		    "LCarets2:", NULL };
1732 	    fprintf( sfd, "%s ", keywords[pst->type] );
1733 	    if ( pst->subtable!=NULL ) {
1734 		SFDDumpUTF7Str(sfd,pst->subtable->subtable_name);
1735 		putc(' ',sfd);
1736 	    }
1737 	    if ( pst->type==pst_position ) {
1738 		fprintf( sfd, "dx=%d dy=%d dh=%d dv=%d",
1739 			pst->u.pos.xoff, pst->u.pos.yoff,
1740 			pst->u.pos.h_adv_off, pst->u.pos.v_adv_off);
1741 		SFDDumpValDevTab(sfd,pst->u.pos.adjust);
1742 		putc('\n',sfd);
1743 	    } else if ( pst->type==pst_pair ) {
1744 		fprintf( sfd, "%s dx=%d dy=%d dh=%d dv=%d",
1745 			pst->u.pair.paired,
1746 			pst->u.pair.vr[0].xoff, pst->u.pair.vr[0].yoff,
1747 			pst->u.pair.vr[0].h_adv_off, pst->u.pair.vr[0].v_adv_off );
1748 		SFDDumpValDevTab(sfd,pst->u.pair.vr[0].adjust);
1749 		fprintf( sfd, " dx=%d dy=%d dh=%d dv=%d",
1750 			pst->u.pair.vr[1].xoff, pst->u.pair.vr[1].yoff,
1751 			pst->u.pair.vr[1].h_adv_off, pst->u.pair.vr[1].v_adv_off);
1752 		SFDDumpValDevTab(sfd,pst->u.pair.vr[1].adjust);
1753 		putc('\n',sfd);
1754 	    } else if ( pst->type==pst_lcaret ) {
1755 		int i;
1756 		fprintf( sfd, "%d ", pst->u.lcaret.cnt );
1757 		for ( i=0; i<pst->u.lcaret.cnt; ++i ) {
1758 		    fprintf( sfd, "%d", pst->u.lcaret.carets[i] );
1759                     if ( i<pst->u.lcaret.cnt-1 ) putc(' ',sfd);
1760                 }
1761 		fprintf( sfd, "\n" );
1762 	    } else
1763 		fprintf( sfd, "%s\n", pst->u.lig.components );
1764 	}
1765     }
1766     if ( sc->comment!=NULL ) {
1767 	fprintf( sfd, "Comment: " );
1768 	SFDDumpUTF7Str(sfd,sc->comment);
1769 	putc('\n',sfd);
1770     }
1771     if ( sc->user_decomp != NULL ) {
1772 	fprintf( sfd, "Decomposition: " );
1773 	char* temp_ud = u2utf8_copy(sc->user_decomp);
1774 	SFDDumpUTF7Str(sfd, temp_ud);
1775 	free(temp_ud);
1776 	putc('\n',sfd);
1777     }
1778     if ( sc->color!=COLOR_DEFAULT )
1779 	fprintf( sfd, "Colour: %x\n", (int) sc->color );
1780     if ( sc->parent->multilayer ) {
1781 	if ( sc->tile_margin!=0 )
1782 	    fprintf( sfd, "TileMargin: %g\n", (double) sc->tile_margin );
1783 	else if ( sc->tile_bounds.minx!=0 || sc->tile_bounds.maxx!=0 )
1784 	    fprintf( sfd, "TileBounds: %g %g %g %g\n", (double) sc->tile_bounds.minx, (double) sc->tile_bounds.miny, (double) sc->tile_bounds.maxx, (double) sc->tile_bounds.maxy );
1785     }
1786     fprintf(sfd,"EndChar\n" );
1787 }
1788 
SFDDumpBitmapChar(FILE * sfd,BDFChar * bfc,int enc,int * newgids)1789 static void SFDDumpBitmapChar(FILE *sfd,BDFChar *bfc, int enc,int *newgids) {
1790     struct enc85 encrypt;
1791     int i;
1792 
1793     fprintf(sfd, "BDFChar: %d %d %d %d %d %d %d",
1794 	    newgids!=NULL ? newgids[bfc->orig_pos] : bfc->orig_pos, enc,
1795 	    bfc->width, bfc->xmin, bfc->xmax, bfc->ymin, bfc->ymax );
1796     if ( bfc->sc->parent->hasvmetrics )
1797 	fprintf(sfd, " %d", bfc->vwidth);
1798     putc('\n',sfd);
1799     memset(&encrypt,'\0',sizeof(encrypt));
1800     encrypt.sfd = sfd;
1801     for ( i=0; i<=bfc->ymax-bfc->ymin; ++i ) {
1802 	uint8 *pt = (uint8 *) (bfc->bitmap + i*bfc->bytes_per_line);
1803 	uint8 *end = pt + bfc->bytes_per_line;
1804 	while ( pt<end ) {
1805 	    SFDEnc85(&encrypt,*pt);
1806 	    ++pt;
1807 	}
1808     }
1809     SFDEnc85EndEnc(&encrypt);
1810     fputc('\n',sfd);
1811 }
1812 
appendnames(char * dest,char * dir,const char * dir_char,char * name,const char * ext)1813 static void appendnames(char *dest,char *dir,const char *dir_char,char *name,const char *ext ) {
1814     strcpy(dest,dir);
1815     dest += strlen(dest);
1816     strcpy(dest,dir_char);
1817     dest += strlen(dest);
1818     /* Some file systems are case-insensitive, so we can't just */
1819     /* copy the glyph name blindly (else "A" and "a" would map to the same file */
1820     for (;;) {
1821 	if ( strncmp(name,"uni",3)==0 && ishexdigit(name[3]) && ishexdigit(name[4]) &&
1822 		ishexdigit(name[5]) && ishexdigit(name[6])) {
1823 	    /* but in a name like uni00AD case is irrelevant. Even under unix its */
1824 	    /*  the same as uni00ad -- and it looks ugly */
1825 	    strncpy(dest,name,7);
1826 	    dest += 7; name += 7;
1827 	    while ( ishexdigit(name[0]) && ishexdigit(name[1]) &&
1828 		    ishexdigit(name[2]) && ishexdigit(name[3]) ) {
1829 		strncpy(dest,name,4);
1830 		dest += 4; name += 4;
1831 	    }
1832 	} else if ( name[0]=='u' && ishexdigit(name[1]) && ishexdigit(name[2]) &&
1833 		ishexdigit(name[3]) && ishexdigit(name[4]) &&
1834 		ishexdigit(name[5]) ) {
1835 	    strncpy(dest,name,5);
1836 	    dest += 5; name += 5;
1837 	} else
1838     break;
1839 	if ( *name!='_' )
1840     break;
1841 	*dest++ = '_';
1842 	++name;
1843     }
1844     while ( *name ) {
1845 	if ( isupper(*name)) {
1846 	    *dest++ = '_';
1847 	    *dest++ = *name;
1848 	} else
1849 	    *dest++ = *name;
1850 	++name;
1851     }
1852     strcpy(dest,ext);
1853 }
1854 
SFDDumpBitmapFont(FILE * sfd,BDFFont * bdf,EncMap * encm,int * newgids,int todir,char * dirname)1855 static int SFDDumpBitmapFont(FILE *sfd,BDFFont *bdf,EncMap *encm,int *newgids,
1856 	int todir, char *dirname) {
1857     int i;
1858     int err = false;
1859     BDFChar *bc;
1860     BDFRefChar *ref;
1861 
1862     ff_progress_next_stage();
1863     if (bdf->foundry)
1864         fprintf( sfd, "BitmapFont: %d %d %d %d %d %s\n", bdf->pixelsize, bdf->glyphcnt,
1865                  bdf->ascent, bdf->descent, BDFDepth(bdf), bdf->foundry );
1866     else
1867         fprintf( sfd, "BitmapFont: %d %d %d %d %d\n", bdf->pixelsize, bdf->glyphcnt,
1868                  bdf->ascent, bdf->descent, BDFDepth(bdf) );
1869     if ( bdf->prop_cnt>0 ) {
1870 	fprintf( sfd, "BDFStartProperties: %d\n", bdf->prop_cnt );
1871 	for ( i=0; i<bdf->prop_cnt; ++i ) {
1872 	    fprintf(sfd,"%s %d ", bdf->props[i].name, bdf->props[i].type );
1873 	    switch ( bdf->props[i].type&~prt_property ) {
1874 	      case prt_int: case prt_uint:
1875 		fprintf(sfd, "%d\n", bdf->props[i].u.val );
1876 	      break;
1877 	      case prt_string: case prt_atom:
1878 		fprintf(sfd, "\"%s\"\n", bdf->props[i].u.str );
1879 	      break;
1880 	      default:
1881 	      break;
1882 	    }
1883 	}
1884 	fprintf( sfd, "BDFEndProperties\n" );
1885     }
1886     if ( bdf->res>20 )
1887 	fprintf( sfd, "Resolution: %d\n", bdf->res );
1888     for ( i=0; i<bdf->glyphcnt; ++i ) {
1889 	if ( bdf->glyphs[i]!=NULL ) {
1890 	    if ( todir ) {
1891 		char *glyphfile = malloc(strlen(dirname)+2*strlen(bdf->glyphs[i]->sc->name)+20);
1892 		FILE *gsfd;
1893 		appendnames(glyphfile,dirname,"/",bdf->glyphs[i]->sc->name,BITMAP_EXT );
1894 		gsfd = fopen(glyphfile,"w");
1895 		if ( gsfd!=NULL ) {
1896 		    SFDDumpBitmapChar(gsfd,bdf->glyphs[i],encm->backmap[i],newgids);
1897 		    if ( ferror(gsfd)) err = true;
1898 		    if ( fclose(gsfd)) err = true;
1899 		} else
1900 		    err = true;
1901 		free(glyphfile);
1902 	    } else
1903 		SFDDumpBitmapChar(sfd,bdf->glyphs[i],encm->backmap[i],newgids);
1904 	}
1905 	ff_progress_next();
1906     }
1907     for ( i=0; i<bdf->glyphcnt; ++i ) if (( bc = bdf->glyphs[i] ) != NULL ) {
1908     	for ( ref=bc->refs; ref!=NULL; ref=ref->next )
1909 	    fprintf(sfd, "BDFRefChar: %d %d %d %d %c\n",
1910 		newgids!=NULL ? newgids[bc->orig_pos] : bc->orig_pos,
1911 		newgids!=NULL ? newgids[ref->bdfc->orig_pos] : ref->bdfc->orig_pos,
1912 		ref->xoff,ref->yoff,ref->selected?'S':'N' );
1913     }
1914     fprintf( sfd, "EndBitmapFont\n" );
1915 return( err );
1916 }
1917 
SFDDumpPrivate(FILE * sfd,struct psdict * private)1918 static void SFDDumpPrivate(FILE *sfd,struct psdict *private) {
1919     int i;
1920     char *pt;
1921     /* These guys should all be ascii text */
1922     fprintf( sfd, "BeginPrivate: %d\n", private->next );
1923     for ( i=0; i<private->next ; ++i ) {
1924       fprintf( sfd, "%s %d ", private->keys[i],
1925 	       (int)strlen(private->values[i]));
1926 	for ( pt = private->values[i]; *pt; ++pt )
1927 	    putc(*pt,sfd);
1928 	putc('\n',sfd);
1929     }
1930     fprintf( sfd, "EndPrivate\n" );
1931 }
1932 
SFDDumpLangName(FILE * sfd,struct ttflangname * ln)1933 static void SFDDumpLangName(FILE *sfd, struct ttflangname *ln) {
1934     int i, end;
1935     fprintf( sfd, "LangName: %d", ln->lang );
1936     for ( end = ttf_namemax; end>0 && ln->names[end-1]==NULL; --end );
1937     for ( i=0; i<end; ++i ) {
1938         putc(' ',sfd);
1939         SFDDumpUTF7Str(sfd,ln->names[i]);
1940     }
1941     putc('\n',sfd);
1942 }
1943 
SFDDumpGasp(FILE * sfd,SplineFont * sf)1944 static void SFDDumpGasp(FILE *sfd, SplineFont *sf) {
1945     int i;
1946 
1947     if ( sf->gasp_cnt==0 )
1948 return;
1949 
1950     fprintf( sfd, "GaspTable: %d", sf->gasp_cnt );
1951     for ( i=0; i<sf->gasp_cnt; ++i )
1952 	fprintf( sfd, " %d %d", sf->gasp[i].ppem, sf->gasp[i].flags );
1953     fprintf( sfd, " %d", sf->gasp_version);
1954     putc('\n',sfd);
1955 }
1956 
SFDDumpDesignSize(FILE * sfd,SplineFont * sf)1957 static void SFDDumpDesignSize(FILE *sfd, SplineFont *sf) {
1958     struct otfname *on;
1959 
1960     if ( sf->design_size==0 )
1961 return;
1962 
1963     fprintf( sfd, "DesignSize: %d", sf->design_size );
1964     if ( sf->fontstyle_id!=0 || sf->fontstyle_name!=NULL ||
1965 	    sf->design_range_bottom!=0 || sf->design_range_top!=0 ) {
1966 	fprintf( sfd, " %d-%d %d ",
1967 		sf->design_range_bottom, sf->design_range_top,
1968 		sf->fontstyle_id );
1969 	for ( on=sf->fontstyle_name; on!=NULL; on=on->next ) {
1970 	    fprintf( sfd, "%d ", on->lang );
1971 	    SFDDumpUTF7Str(sfd, on->name);
1972 	    if ( on->next!=NULL ) putc(' ',sfd);
1973 	}
1974     }
1975     putc('\n',sfd);
1976 }
1977 
SFDDumpOtfFeatNames(FILE * sfd,SplineFont * sf)1978 static void SFDDumpOtfFeatNames(FILE *sfd, SplineFont *sf) {
1979     struct otffeatname *fn;
1980     struct otfname *on;
1981 
1982     for ( fn=sf->feat_names; fn!=NULL; fn=fn->next ) {
1983 	fprintf( sfd, "OtfFeatName: '%c%c%c%c' ",
1984 		fn->tag>>24, fn->tag>>16, fn->tag>>8, fn->tag );
1985 	for ( on=fn->names; on!=NULL; on=on->next ) {
1986 	    fprintf( sfd, "%d ", on->lang );
1987 	    SFDDumpUTF7Str(sfd, on->name);
1988 	    if ( on->next!=NULL ) putc(' ',sfd);
1989 	}
1990 	putc('\n',sfd);
1991     }
1992 }
1993 
putstring(FILE * sfd,const char * header,char * body)1994 static void putstring(FILE *sfd, const char *header, char *body) {
1995     fprintf( sfd, "%s", header );
1996     while ( *body ) {
1997 	if ( *body=='\n' || *body == '\\' || *body=='\r' ) {
1998 	    putc('\\',sfd);
1999 	    if ( *body=='\\' )
2000 		putc('\\',sfd);
2001 	    else {
2002 		putc('n',sfd);
2003 		if ( *body=='\r' && body[1]=='\n' )
2004 		    ++body;
2005 	    }
2006 	} else
2007 	    putc(*body,sfd);
2008 	++body;
2009     }
2010     putc('\n',sfd);
2011 }
2012 
EncName(Encoding * encname)2013 const char *EncName(Encoding *encname) {
2014 return( encname->enc_name );
2015 }
2016 
SFDDumpEncoding(FILE * sfd,Encoding * encname,const char * keyword)2017 static void SFDDumpEncoding(FILE *sfd,Encoding *encname,const char *keyword) {
2018     fprintf(sfd, "%s: %s\n", keyword, encname->enc_name );
2019 }
2020 
SFDDumpMacName(FILE * sfd,struct macname * mn)2021 static void SFDDumpMacName(FILE *sfd,struct macname *mn) {
2022     char *pt;
2023 
2024     while ( mn!=NULL ) {
2025       fprintf( sfd, "MacName: %d %d %d \"", mn->enc, mn->lang,
2026 	       (int)strlen(mn->name) );
2027 	for ( pt=mn->name; *pt; ++pt ) {
2028 	    if ( *pt<' ' || *pt>=0x7f || *pt=='\\' || *pt=='"' )
2029 		fprintf( sfd, "\\%03o", *(uint8 *) pt );
2030 	    else
2031 		putc(*pt,sfd);
2032 	}
2033 	fprintf( sfd, "\"\n" );
2034 	mn = mn->next;
2035     }
2036 }
2037 
SFDDumpMacFeat(FILE * sfd,MacFeat * mf)2038 void SFDDumpMacFeat(FILE *sfd,MacFeat *mf) {
2039     struct macsetting *ms;
2040 
2041     if ( mf==NULL )
2042 return;
2043 
2044     while ( mf!=NULL ) {
2045 	if ( mf->featname!=NULL ) {
2046 	    fprintf( sfd, "MacFeat: %d %d %d\n", mf->feature, mf->ismutex, mf->default_setting );
2047 	    SFDDumpMacName(sfd,mf->featname);
2048 	    for ( ms=mf->settings; ms!=NULL; ms=ms->next ) {
2049 		if ( ms->setname!=NULL ) {
2050 		    fprintf( sfd, "MacSetting: %d\n", ms->setting );
2051 		    SFDDumpMacName(sfd,ms->setname);
2052 		}
2053 	    }
2054 	}
2055 	mf = mf->next;
2056     }
2057     fprintf( sfd,"EndMacFeatures\n" );
2058 }
2059 
SFDDumpBaseLang(FILE * sfd,struct baselangextent * bl)2060 static void SFDDumpBaseLang(FILE *sfd,struct baselangextent *bl) {
2061 
2062     if ( bl->lang==0 )
2063 	fprintf( sfd, " { %d %d", bl->descent, bl->ascent );
2064     else
2065 	fprintf( sfd, " { '%c%c%c%c' %d %d",
2066 		bl->lang>>24, bl->lang>>16, bl->lang>>8, bl->lang,
2067 		bl->descent, bl->ascent );
2068     for ( bl=bl->features; bl!=NULL; bl=bl->next )
2069 	SFDDumpBaseLang(sfd,bl);
2070     putc('}',sfd);
2071 }
2072 
SFDDumpBase(FILE * sfd,const char * keyword,struct Base * base)2073 static void SFDDumpBase(FILE *sfd,const char *keyword,struct Base *base) {
2074     int i;
2075     struct basescript *bs;
2076     struct baselangextent *bl;
2077 
2078     fprintf( sfd, "%s %d", keyword, base->baseline_cnt );
2079     for ( i=0; i<base->baseline_cnt; ++i ) {
2080 	fprintf( sfd, " '%c%c%c%c'",
2081 		base->baseline_tags[i]>>24,
2082 		base->baseline_tags[i]>>16,
2083 		base->baseline_tags[i]>>8,
2084 		base->baseline_tags[i]);
2085     }
2086     putc('\n',sfd);
2087 
2088     for ( bs=base->scripts; bs!=NULL; bs=bs->next ) {
2089 	fprintf( sfd, "BaseScript: '%c%c%c%c' %d ",
2090 		bs->script>>24, bs->script>>16, bs->script>>8, bs->script,
2091 		bs->def_baseline );
2092 	for ( i=0; i<base->baseline_cnt; ++i )
2093 	    fprintf( sfd, " %d", bs->baseline_pos[i]);
2094 	for ( bl=bs->langs; bl!=NULL; bl=bl->next )
2095 	    SFDDumpBaseLang(sfd,bl);
2096 	putc('\n',sfd);
2097     }
2098 }
2099 
SFDDumpJSTFLookups(FILE * sfd,const char * keyword,OTLookup ** list)2100 static void SFDDumpJSTFLookups(FILE *sfd,const char *keyword, OTLookup **list ) {
2101     int i;
2102 
2103     if ( list==NULL || list[0]==NULL )
2104 return;
2105 
2106     fprintf( sfd, "%s ", keyword );
2107     for ( i=0; list[i]!=NULL; ++i ) {
2108 	SFDDumpUTF7Str(sfd,list[i]->lookup_name);
2109 	if ( list[i+1]!=NULL ) putc(' ',sfd);
2110     }
2111     putc('\n',sfd);
2112 }
2113 
SFDDumpJustify(FILE * sfd,SplineFont * sf)2114 static void SFDDumpJustify(FILE *sfd,SplineFont *sf) {
2115     Justify *jscript;
2116     struct jstf_lang *jlang;
2117     int i;
2118 
2119     for ( jscript = sf->justify; jscript!=NULL; jscript=jscript->next ) {
2120 	fprintf( sfd, "Justify: '%c%c%c%c'\n",
2121 		jscript->script>>24,
2122 		jscript->script>>16,
2123 		jscript->script>>8,
2124 		jscript->script);
2125 	if ( jscript->extenders!=NULL )
2126 	    fprintf( sfd, "JstfExtender: %s\n", jscript->extenders );
2127 	for ( jlang = jscript->langs; jlang!=NULL; jlang = jlang->next ) {
2128 	    fprintf( sfd, "JstfLang: '%c%c%c%c' %d\n",
2129 		jlang->lang>>24,
2130 		jlang->lang>>16,
2131 		jlang->lang>>8,
2132 		jlang->lang, jlang->cnt );
2133 	    for ( i=0; i<jlang->cnt; ++i ) {
2134 		fprintf( sfd, "JstfPrio:\n" );
2135 		SFDDumpJSTFLookups(sfd,"JstfEnableShrink:", jlang->prios[i].enableShrink );
2136 		SFDDumpJSTFLookups(sfd,"JstfDisableShrink:", jlang->prios[i].disableShrink );
2137 		SFDDumpJSTFLookups(sfd,"JstfMaxShrink:", jlang->prios[i].maxShrink );
2138 		SFDDumpJSTFLookups(sfd,"JstfEnableExtend:", jlang->prios[i].enableExtend );
2139 		SFDDumpJSTFLookups(sfd,"JstfDisableExtend:", jlang->prios[i].disableExtend );
2140 		SFDDumpJSTFLookups(sfd,"JstfMaxExtend:", jlang->prios[i].maxExtend );
2141 	    }
2142 	}
2143     }
2144     if ( sf->justify!=NULL )
2145 	fprintf( sfd, "EndJustify\n" );
2146 }
2147 
SFDFpstClassNamesOut(FILE * sfd,int class_cnt,char ** classnames,const char * keyword)2148 static void SFDFpstClassNamesOut(FILE *sfd,int class_cnt,char **classnames,const char *keyword) {
2149     char buffer[20];
2150     int i;
2151 
2152     if ( class_cnt>0 && classnames!=NULL ) {
2153 	fprintf( sfd, "  %s: ", keyword );
2154 	for ( i=0; i<class_cnt; ++i ) {
2155 	    if ( classnames[i]==NULL ) {
2156 		sprintf( buffer,"%d", i );
2157 		SFDDumpUTF7Str(sfd,buffer);
2158 	    } else
2159 		SFDDumpUTF7Str(sfd,classnames[i]);
2160 	    if ( i<class_cnt-1 ) putc(' ',sfd);
2161 	}
2162 	putc('\n',sfd);
2163     }
2164 }
2165 
MakeTemporaryFile(void)2166 FILE* MakeTemporaryFile(void) {
2167     FILE *ret = NULL;
2168 
2169 #ifndef __MINGW32__
2170     gchar *loc;
2171     int fd = g_file_open_tmp("fontforge-XXXXXX", &loc, NULL);
2172 
2173     if (fd != -1) {
2174         ret = fdopen(fd, "w+");
2175         g_unlink(loc);
2176         g_free(loc);
2177     }
2178 #else
2179     HANDLE hFile = INVALID_HANDLE_VALUE;
2180     for (int retries = 0; hFile == INVALID_HANDLE_VALUE && retries < 10; retries++) {
2181         wchar_t *temp = _wtempnam(NULL, L"FontForge");
2182         hFile = CreateFileW(temp, GENERIC_READ|GENERIC_WRITE, 0, NULL,
2183             CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, NULL);
2184         free(temp);
2185     }
2186     ret = _fdopen(_open_osfhandle((intptr_t)hFile, 0), "wb+");
2187     if (ret == NULL && hFile != INVALID_HANDLE_VALUE) {
2188         CloseHandle(hFile);
2189     }
2190 #endif
2191     return ret;
2192 }
2193 
2194 
2195 /**
2196  * Read an entire file from the given open file handle and return that data
2197  * as an allocated string that the caller must free.
2198  * If any read or memory error occurs, then free string and return 0.
2199  * FIXME: Use a better method than fseek() and ftell() since this does not
2200  * play well with stdin, streaming input type files, or files with NULLs
2201  */
FileToAllocatedString(FILE * f)2202 char* FileToAllocatedString( FILE *f ) {
2203     char *ret, *buf;
2204     long fsize = 0;
2205     size_t bread = 0;
2206 
2207     /* get approximate file size, and allocate some memory */
2208     if ( fseek(f,0,SEEK_END)==0 && \
2209 	 (fsize=ftell(f))!=-1   && \
2210 	 fseek(f,0,SEEK_SET)==0 && \
2211 	 (buf=calloc(fsize+30001,1))!=NULL ) {
2212 	/* fread in file, size=non-exact, then resize memory smaller */
2213 	bread=fread(buf,1,fsize+30000,f);
2214 	if ( bread<=0 || bread >=(size_t)fsize+30000 || (ret=realloc(buf,bread+1))==NULL ) {
2215 	    free( buf );
2216 	} else {
2217 	    ret[bread] = '\0';
2218 	    return( ret );
2219 	}
2220     }
2221 
2222     /* error occurred reading in file */
2223     fprintf(stderr,_("Failed to read a file. Bytes read:%ld file size:%ld\n"),(long)(bread),fsize );
2224     return( 0 );
2225 }
2226 
2227 
SFD_DumpLookup(FILE * sfd,SplineFont * sf)2228 void SFD_DumpLookup( FILE *sfd, SplineFont *sf ) {
2229     int isgpos;
2230     OTLookup *otl;
2231     struct lookup_subtable *sub;
2232     FeatureScriptLangList *fl;
2233     struct scriptlanglist *sl;
2234     int i;
2235 
2236     for ( isgpos=0; isgpos<2; ++isgpos ) {
2237 	for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl = otl->next ) {
2238 	    fprintf( sfd, "Lookup: %d %d %d ", otl->lookup_type, otl->lookup_flags, otl->store_in_afm );
2239 	    SFDDumpUTF7Str(sfd,otl->lookup_name);
2240 	    fprintf( sfd, " { " );
2241 	    for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
2242 		SFDDumpUTF7Str(sfd,sub->subtable_name);
2243 		putc(' ',sfd);
2244 		if ( otl->lookup_type==gsub_single && sub->suffix!=NULL ) {
2245 		    putc('(',sfd);
2246 		    SFDDumpUTF7Str(sfd,sub->suffix);
2247 		    putc(')',sfd);
2248 		} else if ( otl->lookup_type==gpos_pair && sub->vertical_kerning )
2249 		    fprintf(sfd,"(1)");
2250 		if ( otl->lookup_type==gpos_pair && (sub->separation!=0 || sub->kerning_by_touch))
2251 		    fprintf(sfd,"[%d,%d,%d]", sub->separation, sub->minkern, sub->kerning_by_touch+2*sub->onlyCloser+4*sub->dontautokern );
2252 		putc(' ',sfd);
2253 	    }
2254 	    fprintf( sfd, "} [" );
2255 	    for ( fl=otl->features; fl!=NULL; fl=fl->next ) {
2256 		if ( fl->ismac )
2257 		    fprintf( sfd, "<%d,%d> (",
2258 			    (int) (fl->featuretag>>16),
2259 			    (int) (fl->featuretag&0xffff));
2260 		else
2261 		    fprintf( sfd, "'%c%c%c%c' (",
2262 			    (int) (fl->featuretag>>24), (int) ((fl->featuretag>>16)&0xff),
2263 			    (int) ((fl->featuretag>>8)&0xff), (int) (fl->featuretag&0xff) );
2264 		for ( sl= fl->scripts; sl!=NULL; sl = sl->next ) {
2265 		    fprintf( sfd, "'%c%c%c%c' <",
2266 			    (int) (sl->script>>24), (int) ((sl->script>>16)&0xff),
2267 			    (int) ((sl->script>>8)&0xff), (int) (sl->script&0xff) );
2268 		    for ( i=0; i<sl->lang_cnt; ++i ) {
2269 			uint32 lang = i<MAX_LANG ? sl->langs[i] : sl->morelangs[i-MAX_LANG];
2270 			fprintf( sfd, "'%c%c%c%c' ",
2271 				(int) (lang>>24), (int) ((lang>>16)&0xff),
2272 				(int) ((lang>>8)&0xff), (int) (lang&0xff) );
2273 		    }
2274 		    fprintf( sfd, "> " );
2275 		}
2276 		fprintf( sfd, ") " );
2277 	    }
2278 	    fprintf( sfd, "]\n" );
2279 	}
2280     }
2281 }
2282 
SFD_DumpSplineFontMetadata(FILE * sfd,SplineFont * sf)2283 int SFD_DumpSplineFontMetadata( FILE *sfd, SplineFont *sf )
2284 {
2285     int i, j;
2286     struct ttflangname *ln;
2287     struct ttf_table *tab;
2288     KernClass *kc;
2289     FPST *fpst;
2290     ASM *sm;
2291     int isv;
2292     int err = false;
2293     int isgpos;
2294     OTLookup *otl;
2295     struct lookup_subtable *sub;
2296     FeatureScriptLangList *fl;
2297     struct scriptlanglist *sl;
2298 
2299     fprintf(sfd, "FontName: %s\n", sf->fontname );
2300     if ( sf->fullname!=NULL )
2301 	fprintf(sfd, "FullName: %s\n", sf->fullname );
2302     if ( sf->familyname!=NULL )
2303 	fprintf(sfd, "FamilyName: %s\n", sf->familyname );
2304     if ( sf->weight!=NULL )
2305 	fprintf(sfd, "Weight: %s\n", sf->weight );
2306     if ( sf->copyright!=NULL )
2307 	putstring(sfd, "Copyright: ", sf->copyright );
2308     if ( sf->comments!=NULL ) {
2309 	fprintf( sfd, "UComments: " );
2310 	SFDDumpUTF7Str(sfd,sf->comments);
2311 	putc('\n',sfd);
2312     }
2313     if ( sf->fontlog!=NULL ) {
2314 	fprintf( sfd, "FontLog: " );
2315 	SFDDumpUTF7Str(sfd,sf->fontlog);
2316 	putc('\n',sfd);
2317     }
2318 
2319     if ( sf->version!=NULL )
2320 	fprintf(sfd, "Version: %s\n", sf->version );
2321     if ( sf->styleMapFamilyName!=NULL )
2322 	fprintf(sfd, "StyleMapFamilyName: %s\n", sf->styleMapFamilyName );
2323     if ( sf->fondname!=NULL )
2324 	fprintf(sfd, "FONDName: %s\n", sf->fondname );
2325     if ( sf->defbasefilename!=NULL )
2326 	fprintf(sfd, "DefaultBaseFilename: %s\n", sf->defbasefilename );
2327     if ( sf->strokewidth!=0 )
2328 	fprintf(sfd, "StrokeWidth: %g\n", (double) sf->strokewidth );
2329     fprintf(sfd, "ItalicAngle: %g\n", (double) sf->italicangle );
2330     fprintf(sfd, "UnderlinePosition: %g\n", (double) sf->upos );
2331     fprintf(sfd, "UnderlineWidth: %g\n", (double) sf->uwidth );
2332     fprintf(sfd, "Ascent: %d\n", sf->ascent );
2333     fprintf(sfd, "Descent: %d\n", sf->descent );
2334     fprintf(sfd, "InvalidEm: %d\n", sf->invalidem );
2335     if ( sf->sfntRevision!=sfntRevisionUnset )
2336 	fprintf(sfd, "sfntRevision: 0x%08x\n", sf->sfntRevision );
2337     if ( sf->woffMajor!=woffUnset ) {
2338 	fprintf(sfd, "woffMajor: %d\n", sf->woffMajor );
2339 	fprintf(sfd, "woffMinor: %d\n", sf->woffMinor );
2340     }
2341     if ( sf->woffMetadata!=NULL ) {
2342 	fprintf( sfd, "woffMetadata: " );
2343 	SFDDumpUTF7Str(sfd,sf->woffMetadata);
2344 	putc('\n',sfd);
2345     }
2346     if ( sf->ufo_ascent!=0 )
2347 	fprintf(sfd, "UFOAscent: %g\n", (double) sf->ufo_ascent );
2348     if ( sf->ufo_descent!=0 )
2349 	fprintf(sfd, "UFODescent: %g\n", (double) sf->ufo_descent );
2350     fprintf(sfd, "LayerCount: %d\n", sf->layer_cnt );
2351     for ( i=0; i<sf->layer_cnt; ++i ) {
2352 	fprintf( sfd, "Layer: %d %d ", i, sf->layers[i].order2/*, sf->layers[i].background*/ );
2353 	SFDDumpUTF7Str(sfd,sf->layers[i].name);
2354 	fprintf( sfd, " %d", sf->layers[i].background );
2355 	if (sf->layers[i].ufo_path != NULL) { putc(' ',sfd); SFDDumpUTF7Str(sfd,sf->layers[i].ufo_path); }
2356 	putc('\n',sfd);
2357     }
2358     // TODO: Output U. F. O. layer path.
2359     if (sf->preferred_kerning != 0) fprintf(sfd, "PreferredKerning: %d\n", sf->preferred_kerning);
2360     if ( sf->strokedfont )
2361 	fprintf(sfd, "StrokedFont: %d\n", sf->strokedfont );
2362     else if ( sf->multilayer )
2363 	fprintf(sfd, "MultiLayer: %d\n", sf->multilayer );
2364     if ( sf->hasvmetrics )
2365 	fprintf(sfd, "HasVMetrics: %d\n", sf->hasvmetrics );
2366     if ( sf->use_xuid && sf->changed_since_xuidchanged )
2367 	fprintf(sfd, "NeedsXUIDChange: 1\n" );
2368     if ( sf->xuid!=NULL )
2369 	fprintf(sfd, "XUID: %s\n", sf->xuid );
2370     if ( sf->uniqueid!=0 )
2371 	fprintf(sfd, "UniqueID: %d\n", sf->uniqueid );
2372     if ( sf->use_xuid )
2373 	fprintf(sfd, "UseXUID: 1\n" );
2374     if ( sf->use_uniqueid )
2375 	fprintf(sfd, "UseUniqueID: 1\n" );
2376     if ( sf->horiz_base!=NULL )
2377 	SFDDumpBase(sfd,"BaseHoriz:",sf->horiz_base);
2378     if ( sf->vert_base!=NULL )
2379 	SFDDumpBase(sfd,"BaseVert:",sf->vert_base);
2380     if ( sf->pfminfo.stylemap!=-1 )
2381 	fprintf(sfd, "StyleMap: 0x%04x\n", sf->pfminfo.stylemap );
2382     if ( sf->pfminfo.fstype!=-1 )
2383 	fprintf(sfd, "FSType: %d\n", sf->pfminfo.fstype );
2384     fprintf(sfd, "OS2Version: %d\n", sf->os2_version );
2385     fprintf(sfd, "OS2_WeightWidthSlopeOnly: %d\n", sf->weight_width_slope_only );
2386     fprintf(sfd, "OS2_UseTypoMetrics: %d\n", sf->use_typo_metrics );
2387     fprintf(sfd, "CreationTime: %lld\n", sf->creationtime );
2388     fprintf(sfd, "ModificationTime: %lld\n", sf->modificationtime );
2389     if ( sf->pfminfo.pfmset ) {
2390 	fprintf(sfd, "PfmFamily: %d\n", sf->pfminfo.pfmfamily );
2391 	fprintf(sfd, "TTFWeight: %d\n", sf->pfminfo.weight );
2392 	fprintf(sfd, "TTFWidth: %d\n", sf->pfminfo.width );
2393 	fprintf(sfd, "LineGap: %d\n", sf->pfminfo.linegap );
2394 	fprintf(sfd, "VLineGap: %d\n", sf->pfminfo.vlinegap );
2395 	/*putc('\n',sfd);*/
2396     }
2397     if ( sf->pfminfo.panose_set ) {
2398 	fprintf(sfd, "Panose:" );
2399 	for ( i=0; i<10; ++i )
2400 	    fprintf( sfd, " %d", sf->pfminfo.panose[i]);
2401 	putc('\n',sfd);
2402     }
2403     fprintf(sfd, "OS2TypoAscent: %d\n", sf->pfminfo.os2_typoascent );
2404     fprintf(sfd, "OS2TypoAOffset: %d\n", sf->pfminfo.typoascent_add );
2405     fprintf(sfd, "OS2TypoDescent: %d\n", sf->pfminfo.os2_typodescent );
2406     fprintf(sfd, "OS2TypoDOffset: %d\n", sf->pfminfo.typodescent_add );
2407     fprintf(sfd, "OS2TypoLinegap: %d\n", sf->pfminfo.os2_typolinegap );
2408     fprintf(sfd, "OS2WinAscent: %d\n", sf->pfminfo.os2_winascent );
2409     fprintf(sfd, "OS2WinAOffset: %d\n", sf->pfminfo.winascent_add );
2410     fprintf(sfd, "OS2WinDescent: %d\n", sf->pfminfo.os2_windescent );
2411     fprintf(sfd, "OS2WinDOffset: %d\n", sf->pfminfo.windescent_add );
2412     fprintf(sfd, "HheadAscent: %d\n", sf->pfminfo.hhead_ascent );
2413     fprintf(sfd, "HheadAOffset: %d\n", sf->pfminfo.hheadascent_add );
2414     fprintf(sfd, "HheadDescent: %d\n", sf->pfminfo.hhead_descent );
2415     fprintf(sfd, "HheadDOffset: %d\n", sf->pfminfo.hheaddescent_add );
2416     if ( sf->pfminfo.subsuper_set ) {
2417 	fprintf(sfd, "OS2SubXSize: %d\n", sf->pfminfo.os2_subxsize );
2418 	fprintf(sfd, "OS2SubYSize: %d\n", sf->pfminfo.os2_subysize );
2419 	fprintf(sfd, "OS2SubXOff: %d\n", sf->pfminfo.os2_subxoff );
2420 	fprintf(sfd, "OS2SubYOff: %d\n", sf->pfminfo.os2_subyoff );
2421 	fprintf(sfd, "OS2SupXSize: %d\n", sf->pfminfo.os2_supxsize );
2422 	fprintf(sfd, "OS2SupYSize: %d\n", sf->pfminfo.os2_supysize );
2423 	fprintf(sfd, "OS2SupXOff: %d\n", sf->pfminfo.os2_supxoff );
2424 	fprintf(sfd, "OS2SupYOff: %d\n", sf->pfminfo.os2_supyoff );
2425 	fprintf(sfd, "OS2StrikeYSize: %d\n", sf->pfminfo.os2_strikeysize );
2426 	fprintf(sfd, "OS2StrikeYPos: %d\n", sf->pfminfo.os2_strikeypos );
2427     }
2428     if ( sf->pfminfo.os2_capheight!=0 )
2429     fprintf(sfd, "OS2CapHeight: %d\n", sf->pfminfo.os2_capheight );
2430     if ( sf->pfminfo.os2_xheight!=0 )
2431     fprintf(sfd, "OS2XHeight: %d\n", sf->pfminfo.os2_xheight );
2432     if ( sf->pfminfo.os2_family_class!=0 )
2433 	fprintf(sfd, "OS2FamilyClass: %d\n", sf->pfminfo.os2_family_class );
2434     if ( sf->pfminfo.os2_vendor[0]!='\0' ) {
2435 	fprintf(sfd, "OS2Vendor: '%c%c%c%c'\n",
2436 		sf->pfminfo.os2_vendor[0], sf->pfminfo.os2_vendor[1],
2437 		sf->pfminfo.os2_vendor[2], sf->pfminfo.os2_vendor[3] );
2438     }
2439     if ( sf->pfminfo.hascodepages )
2440 	fprintf(sfd, "OS2CodePages: %08x.%08x\n", sf->pfminfo.codepages[0], sf->pfminfo.codepages[1]);
2441     if ( sf->pfminfo.hasunicoderanges )
2442 	fprintf(sfd, "OS2UnicodeRanges: %08x.%08x.%08x.%08x\n",
2443 		sf->pfminfo.unicoderanges[0], sf->pfminfo.unicoderanges[1],
2444 		sf->pfminfo.unicoderanges[2], sf->pfminfo.unicoderanges[3] );
2445     if ( sf->macstyle!=-1 )
2446 	fprintf(sfd, "MacStyle: %d\n", sf->macstyle );
2447     /* Must come before any kerning classes, anchor classes, conditional psts */
2448     /* state machines, psts, kerning pairs, etc. */
2449     for ( isgpos=0; isgpos<2; ++isgpos ) {
2450 	for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl = otl->next ) {
2451 	    fprintf( sfd, "Lookup: %d %d %d ", otl->lookup_type, otl->lookup_flags, otl->store_in_afm );
2452 	    SFDDumpUTF7Str(sfd,otl->lookup_name);
2453 	    fprintf( sfd, " { " );
2454 	    for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
2455 		SFDDumpUTF7Str(sfd,sub->subtable_name);
2456 		putc(' ',sfd);
2457 		if ( otl->lookup_type==gsub_single && sub->suffix!=NULL ) {
2458 		    putc('(',sfd);
2459 		    SFDDumpUTF7Str(sfd,sub->suffix);
2460 		    putc(')',sfd);
2461 		} else if ( otl->lookup_type==gpos_pair && sub->vertical_kerning )
2462 		    fprintf(sfd,"(1)");
2463 		if ( otl->lookup_type==gpos_pair && (sub->separation!=0 || sub->kerning_by_touch))
2464 		    fprintf(sfd,"[%d,%d,%d]", sub->separation, sub->minkern, sub->kerning_by_touch+2*sub->onlyCloser+4*sub->dontautokern );
2465 		putc(' ',sfd);
2466 	    }
2467 	    fprintf( sfd, "} [" );
2468 	    for ( fl=otl->features; fl!=NULL; fl=fl->next ) {
2469 		if ( fl->ismac )
2470 		    fprintf( sfd, "<%d,%d> (",
2471 			    (int) (fl->featuretag>>16),
2472 			    (int) (fl->featuretag&0xffff));
2473 		else
2474 		    fprintf( sfd, "'%c%c%c%c' (",
2475 			    (int) (fl->featuretag>>24), (int) ((fl->featuretag>>16)&0xff),
2476 			    (int) ((fl->featuretag>>8)&0xff), (int) (fl->featuretag&0xff) );
2477 		for ( sl= fl->scripts; sl!=NULL; sl = sl->next ) {
2478 		    fprintf( sfd, "'%c%c%c%c' <",
2479 			    (int) (sl->script>>24), (int) ((sl->script>>16)&0xff),
2480 			    (int) ((sl->script>>8)&0xff), (int) (sl->script&0xff) );
2481 		    for ( i=0; i<sl->lang_cnt; ++i ) {
2482 			uint32 lang = i<MAX_LANG ? sl->langs[i] : sl->morelangs[i-MAX_LANG];
2483 			fprintf( sfd, "'%c%c%c%c' ",
2484 				(int) (lang>>24), (int) ((lang>>16)&0xff),
2485 				(int) ((lang>>8)&0xff), (int) (lang&0xff) );
2486 		    }
2487 		    fprintf( sfd, "> " );
2488 		}
2489 		fprintf( sfd, ") " );
2490 	    }
2491 	    fprintf( sfd, "]\n" );
2492 	}
2493     }
2494 
2495     if ( sf->mark_class_cnt!=0 ) {
2496 	fprintf( sfd, "MarkAttachClasses: %d\n", sf->mark_class_cnt );
2497 	for ( i=1; i<sf->mark_class_cnt; ++i ) {	/* Class 0 is unused */
2498 	    SFDDumpUTF7Str(sfd, sf->mark_class_names[i]);
2499 	    putc(' ',sfd);
2500 	    if ( sf->mark_classes[i]!=NULL )
2501 		fprintf( sfd, "%d %s\n", (int) strlen(sf->mark_classes[i]),
2502 			sf->mark_classes[i] );
2503 	    else
2504 		fprintf( sfd, "0 \n" );
2505 	}
2506     }
2507     if ( sf->mark_set_cnt!=0 ) {
2508 	fprintf( sfd, "MarkAttachSets: %d\n", sf->mark_set_cnt );
2509 	for ( i=0; i<sf->mark_set_cnt; ++i ) {	/* Set 0 is used */
2510 	    SFDDumpUTF7Str(sfd, sf->mark_set_names[i]);
2511 	    putc(' ',sfd);
2512 	    if ( sf->mark_sets[i]!=NULL )
2513 		fprintf( sfd, "%d %s\n", (int) strlen(sf->mark_sets[i]),
2514 			sf->mark_sets[i] );
2515 	    else
2516 		fprintf( sfd, "0 \n" );
2517 	}
2518     }
2519 
2520     fprintf( sfd, "DEI: 91125\n" );
2521     for ( isv=0; isv<2; ++isv ) {
2522 	for ( kc=isv ? sf->vkerns : sf->kerns; kc!=NULL; kc = kc->next ) {
2523 	  if (kc->firsts_names == NULL && kc->seconds_names == NULL && kc->firsts_flags == NULL && kc->seconds_flags == NULL) {
2524 	    fprintf( sfd, "%s: %d%s %d ", isv ? "VKernClass2" : "KernClass2",
2525 		    kc->first_cnt, kc->firsts[0]!=NULL?"+":"",
2526 		    kc->second_cnt );
2527 	    SFDDumpUTF7Str(sfd,kc->subtable->subtable_name);
2528 	    putc('\n',sfd);
2529 	    if ( kc->firsts[0]!=NULL )
2530 	      fprintf( sfd, " %d %s\n", (int)strlen(kc->firsts[0]),
2531 		       kc->firsts[0]);
2532 	    for ( i=1; i<kc->first_cnt; ++i )
2533 	      fprintf( sfd, " %d %s\n", (int)strlen(kc->firsts[i]),
2534 		       kc->firsts[i]);
2535 	    for ( i=1; i<kc->second_cnt; ++i )
2536 	      fprintf( sfd, " %d %s\n", (int)strlen(kc->seconds[i]),
2537 		       kc->seconds[i]);
2538 	    for ( i=0; i<kc->first_cnt*kc->second_cnt; ++i ) {
2539 		fprintf( sfd, " %d", kc->offsets[i]);
2540 		putc(' ',sfd);
2541 		SFDDumpDeviceTable(sfd,&kc->adjusts[i]);
2542 	    }
2543 	    fprintf( sfd, "\n" );
2544 	  } else {
2545 	    fprintf( sfd, "%s: %d%s %d ", isv ? "VKernClass3" : "KernClass3",
2546 		    kc->first_cnt, kc->firsts[0]!=NULL?"+":"",
2547 		    kc->second_cnt );
2548 	    SFDDumpUTF7Str(sfd,kc->subtable->subtable_name);
2549 	    putc('\n',sfd);
2550 	    if ( kc->firsts[0]!=NULL ) {
2551 	      fprintf( sfd, " %d ", ((kc->firsts_flags && kc->firsts_flags[0]) ? kc->firsts_flags[0] : 0));
2552 	      SFDDumpUTF7Str(sfd, ((kc->firsts_names && kc->firsts_names[0]) ? kc->firsts_names[0] : ""));
2553 	      fprintf( sfd, " " );
2554 	      SFDDumpUTF7Str(sfd,kc->firsts[0]);
2555 	      fprintf( sfd, "\n" );
2556 	    }
2557 	    for ( i=1; i<kc->first_cnt; ++i ) {
2558 	      fprintf( sfd, " %d ", ((kc->firsts_flags && kc->firsts_flags[i]) ? kc->firsts_flags[i] : 0));
2559 	      SFDDumpUTF7Str(sfd, ((kc->firsts_names && kc->firsts_names[i]) ? kc->firsts_names[i] : ""));
2560 	      fprintf( sfd, " " );
2561 	      SFDDumpUTF7Str(sfd,kc->firsts[i]);
2562 	      fprintf( sfd, "\n" );
2563 	    }
2564 	    for ( i=1; i<kc->second_cnt; ++i ) {
2565 	      fprintf( sfd, " %d ", ((kc->seconds_flags && kc->seconds_flags[i]) ? kc->seconds_flags[i] : 0));
2566 	      SFDDumpUTF7Str(sfd, ((kc->seconds_names && kc->seconds_names[i]) ? kc->seconds_names[i] : ""));
2567 	      fprintf( sfd, " " );
2568 	      SFDDumpUTF7Str(sfd,kc->seconds[i]);
2569 	      fprintf( sfd, "\n" );
2570 	    }
2571 	    for ( i=0; i<kc->first_cnt*kc->second_cnt; ++i ) {
2572 		fprintf( sfd, " %d %d", ((kc->offsets_flags && kc->offsets_flags[i]) ? kc->offsets_flags[i] : 0), kc->offsets[i]);
2573 		putc(' ',sfd);
2574 		SFDDumpDeviceTable(sfd,&kc->adjusts[i]);
2575 	    }
2576 	    fprintf( sfd, "\n" );
2577 	  }
2578 	}
2579     }
2580     for ( fpst=sf->possub; fpst!=NULL; fpst=fpst->next ) {
2581 	static const char *keywords[] = { "ContextPos2:", "ContextSub2:", "ChainPos2:", "ChainSub2:", "ReverseChain2:", NULL };
2582 	static const char *formatkeys[] = { "glyph", "class", "coverage", "revcov", NULL };
2583 	fprintf( sfd, "%s %s ", keywords[fpst->type-pst_contextpos],
2584 		formatkeys[fpst->format] );
2585 	SFDDumpUTF7Str(sfd,fpst->subtable->subtable_name);
2586 	fprintf( sfd, " %d %d %d %d\n",
2587 		fpst->nccnt, fpst->bccnt, fpst->fccnt, fpst->rule_cnt );
2588 	if ( fpst->nccnt>0 && fpst->nclass[0]!=NULL )
2589 	  fprintf( sfd, "  Class0: %d %s\n", (int)strlen(fpst->nclass[0]),
2590 		   fpst->nclass[0]);
2591 	for ( i=1; i<fpst->nccnt; ++i )
2592 	  fprintf( sfd, "  Class: %d %s\n", (int)strlen(fpst->nclass[i]),
2593 		   fpst->nclass[i]);
2594 	for ( i=1; i<fpst->bccnt; ++i )
2595 	  fprintf( sfd, "  BClass: %d %s\n", (int)strlen(fpst->bclass[i]),
2596 		   fpst->bclass[i]);
2597 	for ( i=1; i<fpst->fccnt; ++i )
2598 	  fprintf( sfd, "  FClass: %d %s\n", (int)strlen(fpst->fclass[i]),
2599 		   fpst->fclass[i]);
2600 	for ( i=0; i<fpst->rule_cnt; ++i ) {
2601 	    switch ( fpst->format ) {
2602 	      case pst_glyphs:
2603 		fprintf( sfd, " String: %d %s\n",
2604 			 (int)strlen(fpst->rules[i].u.glyph.names),
2605 			 fpst->rules[i].u.glyph.names);
2606 		if ( fpst->rules[i].u.glyph.back!=NULL )
2607 		  fprintf( sfd, " BString: %d %s\n",
2608 			   (int)strlen(fpst->rules[i].u.glyph.back),
2609 			   fpst->rules[i].u.glyph.back);
2610 		else
2611 		    fprintf( sfd, " BString: 0\n");
2612 		if ( fpst->rules[i].u.glyph.fore!=NULL )
2613 		  fprintf( sfd, " FString: %d %s\n",
2614 			   (int)strlen(fpst->rules[i].u.glyph.fore),
2615 			   fpst->rules[i].u.glyph.fore);
2616 		else
2617 		    fprintf( sfd, " FString: 0\n");
2618 	      break;
2619 	      case pst_class:
2620 		fprintf( sfd, " %d %d %d\n  ClsList:", fpst->rules[i].u.class.ncnt, fpst->rules[i].u.class.bcnt, fpst->rules[i].u.class.fcnt );
2621 		for ( j=0; j<fpst->rules[i].u.class.ncnt; ++j )
2622 		    fprintf( sfd, " %d", fpst->rules[i].u.class.nclasses[j]);
2623 		fprintf( sfd, "\n  BClsList:" );
2624 		for ( j=0; j<fpst->rules[i].u.class.bcnt; ++j )
2625 		    fprintf( sfd, " %d", fpst->rules[i].u.class.bclasses[j]);
2626 		fprintf( sfd, "\n  FClsList:" );
2627 		for ( j=0; j<fpst->rules[i].u.class.fcnt; ++j )
2628 		    fprintf( sfd, " %d", fpst->rules[i].u.class.fclasses[j]);
2629 		fprintf( sfd, "\n" );
2630 	      break;
2631 	      case pst_coverage:
2632 	      case pst_reversecoverage:
2633 		fprintf( sfd, " %d %d %d\n", fpst->rules[i].u.coverage.ncnt, fpst->rules[i].u.coverage.bcnt, fpst->rules[i].u.coverage.fcnt );
2634 		for ( j=0; j<fpst->rules[i].u.coverage.ncnt; ++j )
2635 		  fprintf( sfd, "  Coverage: %d %s\n",
2636 			   (int)strlen(fpst->rules[i].u.coverage.ncovers[j]),
2637 			   fpst->rules[i].u.coverage.ncovers[j]);
2638 		for ( j=0; j<fpst->rules[i].u.coverage.bcnt; ++j )
2639 		  fprintf( sfd, "  BCoverage: %d %s\n",
2640 			   (int)strlen(fpst->rules[i].u.coverage.bcovers[j]),
2641 			   fpst->rules[i].u.coverage.bcovers[j]);
2642 		for ( j=0; j<fpst->rules[i].u.coverage.fcnt; ++j )
2643 		  fprintf( sfd, "  FCoverage: %d %s\n",
2644 			   (int)strlen(fpst->rules[i].u.coverage.fcovers[j]),
2645 			   fpst->rules[i].u.coverage.fcovers[j]);
2646 	      break;
2647 	      default:
2648 	      break;
2649 	    }
2650 	    switch ( fpst->format ) {
2651 	      case pst_glyphs:
2652 	      case pst_class:
2653 	      case pst_coverage:
2654 		fprintf( sfd, " %d\n", fpst->rules[i].lookup_cnt );
2655 		for ( j=0; j<fpst->rules[i].lookup_cnt; ++j ) {
2656 		    fprintf( sfd, "  SeqLookup: %d ",
2657 			    fpst->rules[i].lookups[j].seq );
2658 		    SFDDumpUTF7Str(sfd,fpst->rules[i].lookups[j].lookup->lookup_name);
2659 		    putc('\n',sfd);
2660 		}
2661 	      break;
2662 	      case pst_reversecoverage:
2663 		fprintf( sfd, "  Replace: %d %s\n",
2664 			 (int)strlen(fpst->rules[i].u.rcoverage.replacements),
2665 			 fpst->rules[i].u.rcoverage.replacements);
2666 	      break;
2667 	      default:
2668 	      break;
2669 	    }
2670 	}
2671 	/* It would make more sense to output these up near the classes */
2672 	/*  but that would break backwards compatibility (old parsers will */
2673 	/*  ignore these entries if they are at the end, new parsers will */
2674 	/*  read them */
2675 	SFDFpstClassNamesOut(sfd,fpst->nccnt,fpst->nclassnames,"ClassNames");
2676 	SFDFpstClassNamesOut(sfd,fpst->bccnt,fpst->bclassnames,"BClassNames");
2677 	SFDFpstClassNamesOut(sfd,fpst->fccnt,fpst->fclassnames,"FClassNames");
2678 	fprintf( sfd, "EndFPST\n" );
2679     }
2680     struct ff_glyphclasses *grouptmp;
2681     for ( grouptmp = sf->groups; grouptmp != NULL; grouptmp = grouptmp->next ) {
2682       fprintf(sfd, "Group: ");
2683       SFDDumpUTF7Str(sfd, grouptmp->classname); fprintf(sfd, " ");
2684       SFDDumpUTF7Str(sfd, grouptmp->glyphs); fprintf(sfd, "\n");
2685     }
2686     struct ff_rawoffsets *groupkerntmp;
2687     for ( groupkerntmp = sf->groupkerns; groupkerntmp != NULL; groupkerntmp = groupkerntmp->next ) {
2688       fprintf(sfd, "GroupKern: ");
2689       SFDDumpUTF7Str(sfd, groupkerntmp->left); fprintf(sfd, " ");
2690       SFDDumpUTF7Str(sfd, groupkerntmp->right); fprintf(sfd, " ");
2691       fprintf(sfd, "%d\n", groupkerntmp->offset);
2692     }
2693     for ( groupkerntmp = sf->groupvkerns; groupkerntmp != NULL; groupkerntmp = groupkerntmp->next ) {
2694       fprintf(sfd, "GroupVKern: ");
2695       SFDDumpUTF7Str(sfd, groupkerntmp->left); fprintf(sfd, " ");
2696       SFDDumpUTF7Str(sfd, groupkerntmp->right); fprintf(sfd, " ");
2697       fprintf(sfd, "%d\n", groupkerntmp->offset);
2698     }
2699     for ( sm=sf->sm; sm!=NULL; sm=sm->next ) {
2700 	static const char *keywords[] = { "MacIndic2:", "MacContext2:", "MacLigature2:", "unused", "MacSimple2:", "MacInsert2:",
2701 	    "unused", "unused", "unused", "unused", "unused", "unused",
2702 	    "unused", "unused", "unused", "unused", "unused", "MacKern2:",
2703 	    NULL };
2704 	fprintf( sfd, "%s ", keywords[sm->type-asm_indic] );
2705 	SFDDumpUTF7Str(sfd,sm->subtable->subtable_name);
2706 	fprintf( sfd, " %d %d %d\n", sm->flags, sm->class_cnt, sm->state_cnt );
2707 	for ( i=4; i<sm->class_cnt; ++i )
2708 	  fprintf( sfd, "  Class: %d %s\n", (int)strlen(sm->classes[i]),
2709 		   sm->classes[i]);
2710 	for ( i=0; i<sm->class_cnt*sm->state_cnt; ++i ) {
2711 	    fprintf( sfd, " %d %d ", sm->state[i].next_state, sm->state[i].flags );
2712 	    if ( sm->type==asm_context ) {
2713 		if ( sm->state[i].u.context.mark_lookup==NULL )
2714 		    putc('~',sfd);
2715 		else
2716 		    SFDDumpUTF7Str(sfd,sm->state[i].u.context.mark_lookup->lookup_name);
2717 		putc(' ',sfd);
2718 		if ( sm->state[i].u.context.cur_lookup==0 )
2719 		    putc('~',sfd);
2720 		else
2721 		    SFDDumpUTF7Str(sfd,sm->state[i].u.context.cur_lookup->lookup_name);
2722 		putc(' ',sfd);
2723 	    } else if ( sm->type == asm_insert ) {
2724 		if ( sm->state[i].u.insert.mark_ins==NULL )
2725 		    fprintf( sfd, "0 ");
2726 		else
2727 		  fprintf( sfd, "%d %s ",
2728 			   (int)strlen(sm->state[i].u.insert.mark_ins),
2729 			   sm->state[i].u.insert.mark_ins );
2730 		if ( sm->state[i].u.insert.cur_ins==NULL )
2731 		    fprintf( sfd, "0 ");
2732 		else
2733 		  fprintf( sfd, "%d %s ",
2734 			   (int)strlen(sm->state[i].u.insert.cur_ins),
2735 			   sm->state[i].u.insert.cur_ins );
2736 	    } else if ( sm->type == asm_kern ) {
2737 		fprintf( sfd, "%d ", sm->state[i].u.kern.kcnt );
2738 		for ( j=0; j<sm->state[i].u.kern.kcnt; ++j )
2739 		    fprintf( sfd, "%d ", sm->state[i].u.kern.kerns[j]);
2740 	    }
2741 	    putc('\n',sfd);
2742 	}
2743 	fprintf( sfd, "EndASM\n" );
2744     }
2745     SFDDumpMacFeat(sfd,sf->features);
2746     SFDDumpJustify(sfd,sf);
2747     for ( tab = sf->ttf_tables; tab!=NULL ; tab = tab->next )
2748 	SFDDumpTtfTable(sfd,tab,sf);
2749     for ( tab = sf->ttf_tab_saved; tab!=NULL ; tab = tab->next )
2750 	SFDDumpTtfTable(sfd,tab,sf);
2751     for ( ln = sf->names; ln!=NULL; ln=ln->next )
2752 	SFDDumpLangName(sfd,ln);
2753     if ( sf->gasp_cnt!=0 )
2754 	SFDDumpGasp(sfd,sf);
2755     if ( sf->design_size!=0 )
2756 	SFDDumpDesignSize(sfd,sf);
2757     if ( sf->feat_names!=NULL )
2758 	SFDDumpOtfFeatNames(sfd,sf);
2759 
2760 return( err );
2761 }
2762 
2763 
SFD_Dump(FILE * sfd,SplineFont * sf,EncMap * map,EncMap * normal,int todir,char * dirname)2764 static int SFD_Dump( FILE *sfd, SplineFont *sf, EncMap *map, EncMap *normal,
2765 		     int todir, char *dirname)
2766 {
2767     int i, realcnt;
2768     BDFFont *bdf;
2769     int *newgids = NULL;
2770     int err = false;
2771 
2772     if ( normal!=NULL )
2773 	map = normal;
2774 
2775     SFD_DumpSplineFontMetadata( sfd, sf ); //, map, normal, todir, dirname );
2776 
2777     if ( sf->MATH!=NULL ) {
2778 	struct MATH *math = sf->MATH;
2779 	for ( i=0; math_constants_descriptor[i].script_name!=NULL; ++i ) {
2780 	    fprintf( sfd, "MATH:%s: %d", math_constants_descriptor[i].script_name,
2781 		    *((int16 *) (((char *) (math)) + math_constants_descriptor[i].offset)) );
2782 	    if ( math_constants_descriptor[i].devtab_offset>=0 ) {
2783 		DeviceTable **devtab = (DeviceTable **) (((char *) (math)) + math_constants_descriptor[i].devtab_offset );
2784 		putc(' ',sfd);
2785 		SFDDumpDeviceTable(sfd,*devtab);
2786 	    }
2787 	    putc('\n',sfd);
2788 	}
2789     }
2790     if ( sf->python_persistent!=NULL )
2791 	SFDPickleMe(sfd,sf->python_persistent, sf->python_persistent_has_lists);
2792     if ( sf->subfontcnt!=0 ) {
2793 	/* CID fonts have no encodings, they have registry info instead */
2794 	fprintf(sfd, "Registry: %s\n", sf->cidregistry );
2795 	fprintf(sfd, "Ordering: %s\n", sf->ordering );
2796 	fprintf(sfd, "Supplement: %d\n", sf->supplement );
2797 	fprintf(sfd, "CIDVersion: %g\n", (double) sf->cidversion );	/* This is a number whereas "version" is a string */
2798     } else
2799 	SFDDumpEncoding(sfd,map->enc,"Encoding");
2800     if ( normal!=NULL )
2801 	fprintf(sfd, "Compacted: 1\n" );
2802     fprintf( sfd, "UnicodeInterp: %s\n", unicode_interp_names[sf->uni_interp]);
2803     fprintf( sfd, "NameList: %s\n", sf->for_new_glyphs->title );
2804 
2805     if ( map->remap!=NULL ) {
2806 	struct remap *remap;
2807 	int n;
2808 	for ( n=0,remap = map->remap; remap->infont!=-1; ++n, ++remap );
2809 	fprintf( sfd, "RemapN: %d\n", n );
2810 	for ( remap = map->remap; remap->infont!=-1; ++remap )
2811 	    fprintf(sfd, "Remap: %x %x %d\n", (int) remap->firstenc, (int) remap->lastenc, (int) remap->infont );
2812     }
2813     if ( sf->display_size!=0 )
2814 	fprintf( sfd, "DisplaySize: %d\n", sf->display_size );
2815     if ( sf->display_layer!=ly_fore )
2816 	fprintf( sfd, "DisplayLayer: %d\n", sf->display_layer );
2817     fprintf( sfd, "AntiAlias: %d\n", sf->display_antialias );
2818     fprintf( sfd, "FitToEm: %d\n", sf->display_bbsized );
2819     if ( sf->extrema_bound!=0 )
2820 	fprintf( sfd, "ExtremaBound: %d\n", sf->extrema_bound );
2821     if ( sf->width_separation!=0 )
2822 	fprintf( sfd, "WidthSeparation: %d\n", sf->width_separation );
2823     {
2824 	int rc, cc, te;
2825 	if ( (te = FVWinInfo(sf->fv,&cc,&rc))!= -1 )
2826 	    fprintf( sfd, "WinInfo: %d %d %d\n", te, cc, rc );
2827 	else if ( sf->top_enc!=-1 )
2828 	    fprintf( sfd, "WinInfo: %d %d %d\n", sf->top_enc, sf->desired_col_cnt,
2829 		sf->desired_row_cnt);
2830     }
2831     if ( sf->onlybitmaps!=0 )
2832 	fprintf( sfd, "OnlyBitmaps: %d\n", sf->onlybitmaps );
2833     if ( sf->private!=NULL )
2834 	SFDDumpPrivate(sfd,sf->private);
2835 #if HANYANG
2836     if ( sf->rules!=NULL )
2837 	SFDDumpCompositionRules(sfd,sf->rules);
2838 #endif
2839     if ( sf->grid.splines!=NULL ) {
2840 	if ( sf->grid.order2 )
2841 	    fprintf(sfd, "GridOrder2: %d\n", sf->grid.order2 );
2842 	fprintf(sfd, "Grid\n" );
2843 	SFDDumpSplineSet(sfd,sf->grid.splines,sf->grid.order2);
2844     }
2845     if ( sf->texdata.type!=tex_unset ) {
2846 	fprintf(sfd, "TeXData: %d %d", (int) sf->texdata.type, (int) ((sf->design_size<<19)+2)/5 );
2847 	for ( i=0; i<22; ++i )
2848 	    fprintf(sfd, " %d", (int) sf->texdata.params[i]);
2849 	putc('\n',sfd);
2850     }
2851     if ( sf->anchor!=NULL ) {
2852 	AnchorClass *an;
2853 	fprintf(sfd, "AnchorClass2:");
2854 	for ( an=sf->anchor; an!=NULL; an=an->next ) {
2855 	    putc(' ',sfd);
2856 	    SFDDumpUTF7Str(sfd,an->name);
2857             if ( an->subtable!=NULL ) {
2858 	        putc(' ',sfd);
2859 	        SFDDumpUTF7Str(sfd,an->subtable->subtable_name);
2860             }
2861             else
2862                 fprintf(sfd, "\"\" ");
2863 	}
2864 	putc('\n',sfd);
2865     }
2866 
2867     if ( sf->subfontcnt!=0 ) {
2868 	if ( todir ) {
2869 	    for ( i=0; i<sf->subfontcnt; ++i ) {
2870 		char *subfont = malloc(strlen(dirname)+1+strlen(sf->subfonts[i]->fontname)+20);
2871 		char *fontprops;
2872 		FILE *ssfd;
2873 		sprintf( subfont,"%s/%s" SUBFONT_EXT, dirname, sf->subfonts[i]->fontname );
2874 		GFileMkDir(subfont, 0755);
2875 		fontprops = malloc(strlen(subfont)+strlen("/" FONT_PROPS)+1);
2876 		strcpy(fontprops,subfont); strcat(fontprops,"/" FONT_PROPS);
2877 		ssfd = fopen( fontprops,"w");
2878 		if ( ssfd!=NULL ) {
2879 		    err |= SFD_Dump(ssfd,sf->subfonts[i],map,NULL,todir,subfont);
2880 		    if ( ferror(ssfd) ) err = true;
2881 		    if ( fclose(ssfd)) err = true;
2882 		} else
2883 		    err = true;
2884 		free(fontprops);
2885 		free(subfont);
2886 	    }
2887 	} else {
2888 	    int max;
2889 	    for ( i=max=0; i<sf->subfontcnt; ++i )
2890 		if ( max<sf->subfonts[i]->glyphcnt )
2891 		    max = sf->subfonts[i]->glyphcnt;
2892 	    fprintf(sfd, "BeginSubFonts: %d %d\n", sf->subfontcnt, max );
2893 	    for ( i=0; i<sf->subfontcnt; ++i )
2894 		SFD_Dump(sfd,sf->subfonts[i],map,NULL,false, NULL);
2895 	    fprintf(sfd, "EndSubFonts\n" );
2896 	}
2897     } else {
2898 	int enccount = map->enccount;
2899 	if ( sf->cidmaster!=NULL ) {
2900 	    realcnt = -1;
2901 	    enccount = sf->glyphcnt;
2902 	} else {
2903 	    realcnt = 0;
2904 	    for ( i=0; i<sf->glyphcnt; ++i ) if ( !SFDOmit(sf->glyphs[i]) )
2905 		++realcnt;
2906 	    if ( realcnt!=sf->glyphcnt ) {
2907 		newgids = malloc(sf->glyphcnt*sizeof(int));
2908 		realcnt = 0;
2909 		for ( i=0; i<sf->glyphcnt; ++i )
2910 		    if ( SFDOmit(sf->glyphs[i]) )
2911 			newgids[i] = -1;
2912 		    else
2913 			newgids[i] = realcnt++;
2914 	    }
2915 	}
2916 	if ( !todir )
2917 	    fprintf(sfd, "BeginChars: %d %d\n",
2918 	        enccount<map->enc->char_cnt? map->enc->char_cnt : enccount,
2919 	        realcnt );
2920 	for ( i=0; i<sf->glyphcnt; ++i ) {
2921 	    if ( !SFDOmit(sf->glyphs[i]) ) {
2922 		if ( !todir )
2923 		SFDDumpChar(sfd,sf->glyphs[i],map,newgids,todir,1);
2924 		else {
2925 		    char *glyphfile = malloc(strlen(dirname)+2*strlen(sf->glyphs[i]->name)+20);
2926 		    FILE *gsfd;
2927 		    appendnames(glyphfile,dirname,"/",sf->glyphs[i]->name,GLYPH_EXT );
2928 		    gsfd = fopen(glyphfile,"w");
2929 		    if ( gsfd!=NULL ) {
2930 			SFDDumpChar(gsfd,sf->glyphs[i],map,newgids,todir,1);
2931 			if ( ferror(gsfd)) err = true;
2932 			if ( fclose(gsfd)) err = true;
2933 		    } else
2934 			err = true;
2935 		    free(glyphfile);
2936 		}
2937 	    }
2938 	    ff_progress_next();
2939 	}
2940 	if ( !todir )
2941 	    fprintf(sfd, "EndChars\n" );
2942     }
2943 
2944     if ( sf->bitmaps!=NULL )
2945 	ff_progress_change_line2(_("Saving Bitmaps"));
2946     for ( bdf = sf->bitmaps; bdf!=NULL; bdf=bdf->next ) {
2947 	if ( todir ) {
2948 	    char *strike = malloc(strlen(dirname)+1+20+20);
2949 	    char *strikeprops;
2950 	    FILE *ssfd;
2951 	    sprintf( strike,"%s/%d" STRIKE_EXT, dirname, bdf->pixelsize );
2952 	    GFileMkDir(strike, 0755);
2953 	    strikeprops = malloc(strlen(strike)+strlen("/" STRIKE_PROPS)+1);
2954 	    strcpy(strikeprops,strike); strcat(strikeprops,"/" STRIKE_PROPS);
2955 	    ssfd = fopen( strikeprops,"w");
2956 	    if ( ssfd!=NULL ) {
2957 		err |= SFDDumpBitmapFont(ssfd,bdf,map,newgids,todir,strike);
2958 		if ( ferror(ssfd) ) err = true;
2959 		if ( fclose(ssfd)) err = true;
2960 	    } else
2961 		err = true;
2962 	    free(strikeprops);
2963 	    free(strike);
2964 	} else
2965 	    SFDDumpBitmapFont(sfd,bdf,map,newgids,todir,dirname);
2966     }
2967     fprintf(sfd, sf->cidmaster==NULL?"EndSplineFont\n":"EndSubSplineFont\n" );
2968     free(newgids);
2969 return( err );
2970 }
2971 
SFD_MIDump(SplineFont * sf,EncMap * map,char * dirname,int mm_pos)2972 static int SFD_MIDump(SplineFont *sf,EncMap *map,char *dirname,	int mm_pos) {
2973     char *instance = malloc(strlen(dirname)+1+10+20);
2974     char *fontprops;
2975     FILE *ssfd;
2976     int err = false;
2977 
2978     /* I'd like to use the font name, but the order of the instances is */
2979     /*  crucial and I must enforce an ordering on them */
2980     sprintf( instance,"%s/mm%d" INSTANCE_EXT, dirname, mm_pos );
2981     GFileMkDir(instance, 0755);
2982     fontprops = malloc(strlen(instance)+strlen("/" FONT_PROPS)+1);
2983     strcpy(fontprops,instance); strcat(fontprops,"/" FONT_PROPS);
2984     ssfd = fopen( fontprops,"w");
2985     if ( ssfd!=NULL ) {
2986 	err |= SFD_Dump(ssfd,sf,map,NULL,true,instance);
2987 	if ( ferror(ssfd) ) err = true;
2988 	if ( fclose(ssfd)) err = true;
2989     } else
2990 	err = true;
2991     free(fontprops);
2992     free(instance);
2993 return( err );
2994 }
2995 
SFD_MMDump(FILE * sfd,SplineFont * sf,EncMap * map,EncMap * normal,int todir,char * dirname)2996 static int SFD_MMDump(FILE *sfd,SplineFont *sf,EncMap *map,EncMap *normal,
2997 	int todir, char *dirname) {
2998     MMSet *mm = sf->mm;
2999     int max, i, j;
3000     int err = false;
3001 
3002     fprintf( sfd, "MMCounts: %d %d %d %d\n", mm->instance_count, mm->axis_count,
3003 	    mm->apple, mm->named_instance_count );
3004     fprintf( sfd, "MMAxis:" );
3005     for ( i=0; i<mm->axis_count; ++i )
3006 	fprintf( sfd, " %s", mm->axes[i]);
3007     putc('\n',sfd);
3008     fprintf( sfd, "MMPositions:" );
3009     for ( i=0; i<mm->axis_count*mm->instance_count; ++i )
3010 	fprintf( sfd, " %g", (double) mm->positions[i]);
3011     putc('\n',sfd);
3012     fprintf( sfd, "MMWeights:" );
3013     for ( i=0; i<mm->instance_count; ++i )
3014 	fprintf( sfd, " %g", (double) mm->defweights[i]);
3015     putc('\n',sfd);
3016     for ( i=0; i<mm->axis_count; ++i ) {
3017 	fprintf( sfd, "MMAxisMap: %d %d", i, mm->axismaps[i].points );
3018 	for ( j=0; j<mm->axismaps[i].points; ++j )
3019 	    fprintf( sfd, " %g=>%g", (double) mm->axismaps[i].blends[j], (double) mm->axismaps[i].designs[j]);
3020 	fputc('\n',sfd);
3021 	SFDDumpMacName(sfd,mm->axismaps[i].axisnames);
3022     }
3023     if ( mm->cdv!=NULL ) {
3024 	fprintf( sfd, "MMCDV:\n" );
3025 	fputs(mm->cdv,sfd);
3026 	fprintf( sfd, "\nEndMMSubroutine\n" );
3027     }
3028     if ( mm->ndv!=NULL ) {
3029 	fprintf( sfd, "MMNDV:\n" );
3030 	fputs(mm->ndv,sfd);
3031 	fprintf( sfd, "\nEndMMSubroutine\n" );
3032     }
3033     for ( i=0; i<mm->named_instance_count; ++i ) {
3034 	fprintf( sfd, "MMNamedInstance: %d ", i );
3035 	for ( j=0; j<mm->axis_count; ++j )
3036 	    fprintf( sfd, " %g", (double) mm->named_instances[i].coords[j]);
3037 	fputc('\n',sfd);
3038 	SFDDumpMacName(sfd,mm->named_instances[i].names);
3039     }
3040 
3041     if ( todir ) {
3042 	for ( i=0; i<mm->instance_count; ++i )
3043 	    err |= SFD_MIDump(mm->instances[i],map,dirname,i+1);
3044 	err |= SFD_MIDump(mm->normal,map,dirname,0);
3045     } else {
3046 	for ( i=max=0; i<mm->instance_count; ++i )
3047 	    if ( max<mm->instances[i]->glyphcnt )
3048 		max = mm->instances[i]->glyphcnt;
3049 	fprintf(sfd, "BeginMMFonts: %d %d\n", mm->instance_count+1, max );
3050 	for ( i=0; i<mm->instance_count; ++i )
3051 	    SFD_Dump(sfd,mm->instances[i],map,normal,todir,dirname);
3052 	SFD_Dump(sfd,mm->normal,map,normal,todir,dirname);
3053     }
3054     fprintf(sfd, "EndMMFonts\n" );
3055 return( err );
3056 }
3057 
SFDDump(FILE * sfd,SplineFont * sf,EncMap * map,EncMap * normal,int todir,char * dirname)3058 static int SFDDump(FILE *sfd,SplineFont *sf,EncMap *map,EncMap *normal,
3059 	int todir, char *dirname) {
3060     int i, realcnt;
3061     BDFFont *bdf;
3062     int err = false;
3063 
3064     realcnt = sf->glyphcnt;
3065     if ( sf->subfontcnt!=0 ) {
3066 	for ( i=0; i<sf->subfontcnt; ++i )
3067 	    if ( realcnt<sf->subfonts[i]->glyphcnt )
3068 		realcnt = sf->subfonts[i]->glyphcnt;
3069     }
3070     for ( i=0, bdf = sf->bitmaps; bdf!=NULL; bdf=bdf->next, ++i );
3071     ff_progress_start_indicator(10,_("Saving..."),_("Saving Spline Font Database"),_("Saving Outlines"),
3072 	    realcnt,i+1);
3073     ff_progress_enable_stop(false);
3074 #ifndef _NO_LIBPNG
3075     double version = 3.2;
3076     if (!WritePNGInSFD) version = 3.1;
3077 #else
3078     double version = 3.1;
3079 #endif
3080     if (!UndoRedoLimitToSave && version == 3.1) {
3081         version = 3.0;
3082     }
3083     fprintf(sfd, "SplineFontDB: %.1f\n", version );
3084     if ( sf->mm != NULL )
3085 	err = SFD_MMDump(sfd,sf->mm->normal,map,normal,todir,dirname);
3086     else
3087 	err = SFD_Dump(sfd,sf,map,normal,todir,dirname);
3088     ff_progress_end_indicator();
3089 return( err );
3090 }
3091 
SFDirClean(char * filename)3092 static void SFDirClean(char *filename) {
3093     DIR *dir;
3094     struct dirent *ent;
3095     char *buffer, *pt;
3096 
3097     unlink(filename);		/* Just in case it's a normal file, it shouldn't be, but just in case... */
3098     dir = opendir(filename);
3099     if ( dir==NULL )
3100 return;
3101     buffer = malloc(strlen(filename)+1+NAME_MAX+1);
3102     while ( (ent = readdir(dir))!=NULL ) {
3103 	if ( strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0 )
3104     continue;
3105 	pt = strrchr(ent->d_name,EXT_CHAR);
3106 	if ( pt==NULL )
3107     continue;
3108 	sprintf( buffer,"%s/%s", filename, ent->d_name );
3109 	if ( strcmp(pt,".props")==0 ||
3110 		strcmp(pt,GLYPH_EXT)==0 ||
3111 		strcmp(pt,BITMAP_EXT)==0 )
3112 	    unlink( buffer );
3113 	else if ( strcmp(pt,STRIKE_EXT)==0 ||
3114 		strcmp(pt,SUBFONT_EXT)==0 ||
3115 		strcmp(pt,INSTANCE_EXT)==0 )
3116 	    SFDirClean(buffer);
3117 	/* If there are filenames we don't recognize, leave them. They might contain version control info */
3118     }
3119     free(buffer);
3120     closedir(dir);
3121 }
3122 
SFFinalDirClean(char * filename)3123 static void SFFinalDirClean(char *filename) {
3124     DIR *dir;
3125     struct dirent *ent;
3126     char *buffer, *markerfile, *pt;
3127 
3128     /* we did not unlink sub-directories in case they contained version control */
3129     /*  files. We did remove all our files from them, however.  If the user */
3130     /*  removed a bitmap strike or a cid-subfont those sub-dirs will now be */
3131     /*  empty. If the user didn't remove them then they will contain our marker */
3132     /*  files. So if we find a subdir with no marker files in it, remove it */
3133     dir = opendir(filename);
3134     if ( dir==NULL )
3135 return;
3136     buffer = malloc(strlen(filename)+1+NAME_MAX+1);
3137     markerfile = malloc(strlen(filename)+2+2*NAME_MAX+1);
3138     while ( (ent = readdir(dir))!=NULL ) {
3139 	if ( strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0 )
3140     continue;
3141 	pt = strrchr(ent->d_name,EXT_CHAR);
3142 	if ( pt==NULL )
3143     continue;
3144 	sprintf( buffer,"%s/%s", filename, ent->d_name );
3145 	if ( strcmp(pt,".strike")==0 ||
3146 		strcmp(pt,SUBFONT_EXT)==0 ||
3147 		strcmp(pt,INSTANCE_EXT)==0 ) {
3148 	    if ( strcmp(pt,".strike")==0 )
3149 		sprintf( markerfile,"%s/" STRIKE_PROPS, buffer );
3150 	    else
3151 		sprintf( markerfile,"%s/" FONT_PROPS, buffer );
3152 	    if ( !GFileExists(markerfile)) {
3153 		GFileRemove(buffer, false);
3154 	    }
3155 	}
3156     }
3157     free(buffer);
3158     free(markerfile);
3159     closedir(dir);
3160 }
3161 
SFDWrite(char * filename,SplineFont * sf,EncMap * map,EncMap * normal,int todir)3162 int SFDWrite(char *filename,SplineFont *sf,EncMap *map,EncMap *normal,int todir) {
3163     FILE *sfd;
3164     int i, gc;
3165     char *tempfilename = filename;
3166     int err = false;
3167 
3168     if ( todir ) {
3169 	SFDirClean(filename);
3170 	GFileMkDir(filename, 0755);		/* this will fail if directory already exists. That's ok */
3171 	tempfilename = malloc(strlen(filename)+strlen("/" FONT_PROPS)+1);
3172 	strcpy(tempfilename,filename); strcat(tempfilename,"/" FONT_PROPS);
3173     }
3174 
3175     sfd = fopen(tempfilename,"w");
3176     if ( tempfilename!=filename ) free(tempfilename);
3177     if ( sfd==NULL )
3178 return( 0 );
3179 
3180     locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
3181     switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
3182     if ( sf->cidmaster!=NULL ) {
3183 	sf=sf->cidmaster;
3184 	gc = 1;
3185 	for ( i=0; i<sf->subfontcnt; ++i )
3186 	    if ( sf->subfonts[i]->glyphcnt > gc )
3187 		gc = sf->subfonts[i]->glyphcnt;
3188 	map = EncMap1to1(gc);
3189 	err = SFDDump(sfd,sf,map,NULL,todir,filename);
3190 	EncMapFree(map);
3191     } else
3192 	err = SFDDump(sfd,sf,map,normal,todir,filename);
3193     switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
3194     if ( ferror(sfd) ) err = true;
3195     if ( fclose(sfd) ) err = true;
3196     if ( todir )
3197 	SFFinalDirClean(filename);
3198 return( !err );
3199 }
3200 
SFDDoesAnyBackupExist(char * filename)3201 int SFDDoesAnyBackupExist(char* filename)
3202 {
3203     char path[PATH_MAX];
3204     int idx = 1;
3205 
3206     snprintf( path, PATH_MAX, "%s-%02d", filename, idx );
3207     return GFileExists(path);
3208 }
3209 
3210 /**
3211  * Handle creation of potential implicit revisions when saving.
3212  *
3213  * If s2d is set then we are saving to an sfdir and no revisions are
3214  * created.
3215  *
3216  * If localRevisionsToRetain == 0 then no revisions are made.
3217  *
3218  * If localRevisionsToRetain > 0 then it is taken as an explict number
3219  * of revisions to make, and revisions are made
3220  *
3221  * If localRevisionsToRetain == -1 then it is "not set".
3222  * In that case, revisions are only made if there are already revisions
3223  * for the locfilename.
3224  *
3225  */
SFDWriteBakExtended(char * locfilename,SplineFont * sf,EncMap * map,EncMap * normal,int s2d,int localRevisionsToRetain)3226 int SFDWriteBakExtended(char* locfilename,
3227 			SplineFont *sf,EncMap *map,EncMap *normal,
3228 			int s2d,
3229 			int localRevisionsToRetain )
3230 {
3231     int rc = 0;
3232 
3233     if( s2d )
3234     {
3235 	rc = SFDWrite(locfilename,sf,map,normal,s2d);
3236 	return rc;
3237     }
3238 
3239 
3240     int cacheRevisionsToRetain = prefRevisionsToRetain;
3241 
3242     sf->save_to_dir = s2d;
3243 
3244     if( localRevisionsToRetain < 0 )
3245     {
3246 	// If there are no backups, then don't start creating any
3247 	if( !SFDDoesAnyBackupExist(sf->filename))
3248 	    prefRevisionsToRetain = 0;
3249     }
3250     else
3251     {
3252 	prefRevisionsToRetain = localRevisionsToRetain;
3253     }
3254 
3255     rc = SFDWriteBak( locfilename, sf, map, normal );
3256 
3257     prefRevisionsToRetain = cacheRevisionsToRetain;
3258 
3259     return rc;
3260 }
3261 
3262 
SFDWriteBak(char * filename,SplineFont * sf,EncMap * map,EncMap * normal)3263 int SFDWriteBak(char *filename,SplineFont *sf,EncMap *map,EncMap *normal) {
3264     char *buf=0, *buf2=NULL;
3265     int ret;
3266 
3267     if ( sf->save_to_dir )
3268     {
3269 	ret = SFDWrite(filename,sf,map,normal,true);
3270 	return(ret);
3271     }
3272 
3273     if ( sf->cidmaster!=NULL )
3274 	sf=sf->cidmaster;
3275     buf = malloc(strlen(filename)+10);
3276     if ( sf->compression!=0 )
3277     {
3278 	buf2 = malloc(strlen(filename)+10);
3279 	strcpy(buf2,filename);
3280 	strcat(buf2,compressors[sf->compression-1].ext);
3281 	strcpy(buf,buf2);
3282 	strcat(buf,"~");
3283 	if ( rename(buf2,buf)==0 )
3284 	    sf->backedup = bs_backedup;
3285     }
3286     else
3287     {
3288 	sf->backedup = bs_dontknow;
3289 
3290 	if( prefRevisionsToRetain )
3291 	{
3292 	    char path[PATH_MAX];
3293 	    char pathnew[PATH_MAX];
3294 	    int idx = 0;
3295 
3296 	    snprintf( path,    PATH_MAX, "%s", filename );
3297 	    snprintf( pathnew, PATH_MAX, "%s-%02d", filename, idx );
3298 	    (void)rename( path, pathnew );
3299 
3300 	    for( idx=prefRevisionsToRetain; idx > 0; idx-- )
3301 	    {
3302 		snprintf( path, PATH_MAX, "%s-%02d", filename, idx-1 );
3303 		snprintf( pathnew, PATH_MAX, "%s-%02d", filename, idx );
3304 
3305 		int rc = rename( path, pathnew );
3306 		if( !idx && !rc )
3307 		    sf->backedup = bs_backedup;
3308 	    }
3309 	    idx = prefRevisionsToRetain+1;
3310 	    snprintf( path, PATH_MAX, "%s-%02d", filename, idx );
3311 	    unlink(path);
3312 	}
3313 
3314     }
3315     free(buf);
3316 
3317     ret = SFDWrite(filename,sf,map,normal,false);
3318     if ( ret && sf->compression!=0 ) {
3319 	unlink(buf2);
3320 	buf = malloc(strlen(filename)+40);
3321 	sprintf( buf, "%s %s", compressors[sf->compression-1].recomp, filename );
3322 	if ( system( buf )!=0 )
3323 	    sf->compression = 0;
3324 	free(buf);
3325     }
3326     free(buf2);
3327     return( ret );
3328 }
3329 
3330 /* ********************************* INPUT ********************************** */
3331 
getquotedeol(FILE * sfd)3332 char *getquotedeol(FILE *sfd) {
3333     char *pt, *str, *end;
3334     int ch;
3335 
3336     pt = str = malloc(101); end = str+100;
3337     while ( isspace(ch = nlgetc(sfd)) && ch!='\r' && ch!='\n' );
3338     while ( ch!='\n' && ch!='\r' && ch!=EOF ) {
3339 	if ( ch=='\\' ) {
3340 	    /* We can't use nlgetc() here, because it would misinterpret */
3341 	    /* double backslash at the end of line. Multiline strings,   */
3342 	    /* broken with backslash + newline, are just handled above.  */
3343 	    ch = getc(sfd);
3344 	    if ( ch=='n' ) ch='\n';
3345 	    /* else if ( ch=='\\' ) ch=='\\'; */ /* second backslash of '\\' */
3346 
3347 	    /* FontForge doesn't write other escape sequences in this context. */
3348 	    /* So any other value of ch is assumed impossible. */
3349 	}
3350 	if ( pt>=end ) {
3351 	    pt = realloc(str,end-str+101);
3352 	    end = pt+(end-str)+100;
3353 	    str = pt;
3354 	    pt = end-100;
3355 	}
3356 	*pt++ = ch;
3357 	ch = nlgetc(sfd);
3358     }
3359     *pt='\0';
3360     /* these strings should be in utf8 now, but some old sfd files might have */
3361     /* latin1. Not a severe problems because they SHOULD be in ASCII. So any */
3362     /* non-ascii strings are erroneous anyway */
3363     if ( !utf8_valid(str) ) {
3364 	pt = latin1_2_utf8_copy(str);
3365 	free(str);
3366 	str = pt;
3367     }
3368 return( str );
3369 }
3370 
geteol(FILE * sfd,char * tokbuf)3371 static int geteol(FILE *sfd, char *tokbuf) {
3372     char *pt=tokbuf, *end = tokbuf+2000-2; int ch;
3373 
3374     while ( isspace(ch = nlgetc(sfd)) && ch!='\r' && ch!='\n' );
3375     while ( ch!='\n' && ch!='\r' && ch!=EOF ) {
3376 	if ( pt<end ) *pt++ = ch;
3377 	ch = nlgetc(sfd);
3378     }
3379     *pt='\0';
3380 return( pt!=tokbuf?1:ch==EOF?-1: 0 );
3381 }
visitSFDFragment(FILE * sfd,SplineFont * sf,visitSFDFragmentFunc ufunc,void * udata)3382 void visitSFDFragment( FILE *sfd, SplineFont *sf,
3383 		       visitSFDFragmentFunc ufunc, void* udata )
3384 {
3385     int eof;
3386     char tok[2000];
3387     while ( 1 ) {
3388 	if ( (eof = getname(sfd,tok))!=1 ) {
3389 	    if ( eof==-1 )
3390 		break;
3391 	    geteol(sfd,tok);
3392 	    continue;
3393 	}
3394 
3395 	ufunc( sfd, tok, sf, udata );
3396     }
3397 }
3398 
3399 
getprotectedname(FILE * sfd,char * tokbuf)3400 static int getprotectedname(FILE *sfd, char *tokbuf) {
3401     char *pt=tokbuf, *end = tokbuf+100-2; int ch;
3402 
3403     while ( (ch = nlgetc(sfd))==' ' || ch=='\t' );
3404     while ( ch!=EOF && !isspace(ch) && ch!='[' && ch!=']' && ch!='{' && ch!='}' && ch!='<' && ch!='%' ) {
3405 	if ( pt<end ) *pt++ = ch;
3406 	ch = nlgetc(sfd);
3407     }
3408     if ( pt==tokbuf && ch!=EOF )
3409 	*pt++ = ch;
3410     else
3411 	ungetc(ch,sfd);
3412     *pt='\0';
3413 return( pt!=tokbuf?1:ch==EOF?-1: 0 );
3414 }
3415 
getname(FILE * sfd,char * tokbuf)3416 int getname(FILE *sfd, char *tokbuf) {
3417     int ch;
3418 
3419     while ( isspace(ch = nlgetc(sfd)));
3420     ungetc(ch,sfd);
3421 return( getprotectedname(sfd,tokbuf));
3422 }
3423 
gettag(FILE * sfd)3424 static uint32 gettag(FILE *sfd) {
3425     int ch, quoted;
3426     uint32 tag;
3427 
3428     while ( (ch=nlgetc(sfd))==' ' );
3429     if ( (quoted = (ch=='\'')) ) ch = nlgetc(sfd);
3430     tag = (ch<<24)|(nlgetc(sfd)<<16);
3431     tag |= nlgetc(sfd)<<8;
3432     tag |= nlgetc(sfd);
3433     if ( quoted ) (void) nlgetc(sfd);
3434 return( tag );
3435 }
3436 
getint(FILE * sfd,int * val)3437 static int getint(FILE *sfd, int *val) {
3438     char tokbuf[100]; int ch;
3439     char *pt=tokbuf, *end = tokbuf+100-2;
3440 
3441     while ( isspace(ch = nlgetc(sfd)));
3442     if ( ch=='-' || ch=='+' ) {
3443 	*pt++ = ch;
3444 	ch = nlgetc(sfd);
3445     }
3446     while ( isdigit(ch)) {
3447 	if ( pt<end ) *pt++ = ch;
3448 	ch = nlgetc(sfd);
3449     }
3450     *pt='\0';
3451     ungetc(ch,sfd);
3452     *val = strtol(tokbuf,NULL,10);
3453 return( pt!=tokbuf?1:ch==EOF?-1: 0 );
3454 }
3455 
getlonglong(FILE * sfd,long long * val)3456 static int getlonglong(FILE *sfd, long long *val) {
3457     char tokbuf[100]; int ch;
3458     char *pt=tokbuf, *end = tokbuf+100-2;
3459 
3460     while ( isspace(ch = nlgetc(sfd)));
3461     if ( ch=='-' || ch=='+' ) {
3462 	*pt++ = ch;
3463 	ch = nlgetc(sfd);
3464     }
3465     while ( isdigit(ch)) {
3466 	if ( pt<end ) *pt++ = ch;
3467 	ch = nlgetc(sfd);
3468     }
3469     *pt='\0';
3470     ungetc(ch,sfd);
3471     *val = strtoll(tokbuf,NULL,10);
3472 return( pt!=tokbuf?1:ch==EOF?-1: 0 );
3473 }
3474 
gethex(FILE * sfd,uint32 * val)3475 static int gethex(FILE *sfd, uint32 *val) {
3476     char tokbuf[100]; int ch;
3477     char *pt=tokbuf, *end = tokbuf+100-2;
3478 
3479     while ( isspace(ch = nlgetc(sfd)));
3480     if ( ch=='#' )
3481 	ch = nlgetc(sfd);
3482     if ( ch=='-' || ch=='+' ) {
3483 	*pt++ = ch;
3484 	ch = nlgetc(sfd);
3485     }
3486     if ( ch=='0' ) {
3487 	ch = nlgetc(sfd);
3488 	if ( ch=='x' || ch=='X' )
3489 	    ch = nlgetc(sfd);
3490 	else {
3491 	    ungetc(ch,sfd);
3492 	    ch = '0';
3493 	}
3494     }
3495     while ( isdigit(ch) || (ch>='a' && ch<='f') || (ch>='A' && ch<='F')) {
3496 	if ( pt<end ) *pt++ = ch;
3497 	ch = nlgetc(sfd);
3498     }
3499     *pt='\0';
3500     ungetc(ch,sfd);
3501     *val = strtoul(tokbuf,NULL,16);
3502 return( pt!=tokbuf?1:ch==EOF?-1: 0 );
3503 }
3504 
gethexints(FILE * sfd,uint32 * val,int cnt)3505 static int gethexints(FILE *sfd, uint32 *val, int cnt) {
3506     int i, ch;
3507 
3508     for ( i=0; i<cnt; ++i ) {
3509 	if ( i!=0 ) {
3510 	    ch = nlgetc(sfd);
3511 	    if ( ch!='.' ) ungetc(ch,sfd);
3512 	}
3513 	if ( !gethex(sfd,&val[i]))
3514 return( false );
3515     }
3516 return( true );
3517 }
3518 
getsint(FILE * sfd,int16 * val)3519 static int getsint(FILE *sfd, int16 *val) {
3520     int val2;
3521     int ret = getint(sfd,&val2);
3522     *val = val2;
3523 return( ret );
3524 }
3525 
getusint(FILE * sfd,uint16 * val)3526 static int getusint(FILE *sfd, uint16 *val) {
3527     int val2;
3528     int ret = getint(sfd,&val2);
3529     *val = val2;
3530 return( ret );
3531 }
3532 
getreal(FILE * sfd,real * val)3533 static int getreal(FILE *sfd, real *val) {
3534     char tokbuf[100];
3535     int ch;
3536     char *pt=tokbuf, *end = tokbuf+100-2, *nend;
3537 
3538     while ( isspace(ch = nlgetc(sfd)));
3539     if ( ch!='e' && ch!='E' )		/* real's can't begin with exponants */
3540 	while ( isdigit(ch) || ch=='-' || ch=='+' || ch=='e' || ch=='E' || ch=='.' || ch==',' ) {
3541 	    if ( pt<end ) *pt++ = ch;
3542 	    ch = nlgetc(sfd);
3543 	}
3544     *pt='\0';
3545     ungetc(ch,sfd);
3546     *val = strtod(tokbuf,&nend);
3547     /* Beware of different locals! */
3548     if ( *nend!='\0' ) {
3549 	if ( *nend=='.' )
3550 	    *nend = ',';
3551 	else if ( *nend==',' )
3552 	    *nend = '.';
3553 	*val = strtod(tokbuf,&nend);
3554     }
3555 return( pt!=tokbuf && *nend=='\0'?1:ch==EOF?-1: 0 );
3556 }
3557 
3558 /* Don't use nlgetc here. We carefully control newlines when dumping in 85 */
3559 /*  but backslashes can occur at end of line. */
Dec85(struct enc85 * dec)3560 static int Dec85(struct enc85 *dec) {
3561     int ch1, ch2, ch3, ch4, ch5;
3562     unsigned int val;
3563 
3564     if ( dec->pos<0 ) {
3565 	while ( isspace(ch1=getc(dec->sfd)));
3566 	if ( ch1=='z' ) {
3567 	    dec->sofar[0] = dec->sofar[1] = dec->sofar[2] = dec->sofar[3] = 0;
3568 	    dec->pos = 3;
3569 	} else {
3570 	    while ( isspace(ch2=getc(dec->sfd)));
3571 	    while ( isspace(ch3=getc(dec->sfd)));
3572 	    while ( isspace(ch4=getc(dec->sfd)));
3573 	    while ( isspace(ch5=getc(dec->sfd)));
3574 	    val = ((((ch1-'!')*85+ ch2-'!')*85 + ch3-'!')*85 + ch4-'!')*85 + ch5-'!';
3575 	    dec->sofar[3] = val>>24;
3576 	    dec->sofar[2] = val>>16;
3577 	    dec->sofar[1] = val>>8;
3578 	    dec->sofar[0] = val;
3579 	    dec->pos = 3;
3580 	}
3581     }
3582 return( dec->sofar[dec->pos--] );
3583 }
3584 
rle2image(struct enc85 * dec,int rlelen,struct _GImage * base)3585 static void rle2image(struct enc85 *dec,int rlelen,struct _GImage *base) {
3586     uint8 *pt, *end;
3587     int r,c,set, cnt, ch, ch2;
3588     int i;
3589 
3590     r = c = 0; set = 1; pt = base->data; end = pt + base->bytes_per_line*base->height;
3591     memset(base->data,0xff,end-pt);
3592     while ( rlelen>0 ) {
3593 	if ( pt>=end ) {
3594 	    IError( "RLE failure\n" );
3595 	    while ( rlelen>0 ) { Dec85(dec); --rlelen; }
3596     break;
3597 	}
3598 	ch = Dec85(dec);
3599 	--rlelen;
3600 	if ( ch==255 ) {
3601 	    ch2 = Dec85(dec);
3602 	    cnt = (ch2<<8) + Dec85(dec);
3603 	    rlelen -= 2;
3604 	} else
3605 	    cnt = ch;
3606 	if ( ch==255 && ch2==0 && cnt<255 ) {
3607 	    /* Line duplication */
3608 	    for ( i=0; i<cnt && pt<end; ++i ) {
3609 		memcpy(pt,base->data+(r-1)*base->bytes_per_line,base->bytes_per_line);
3610 		++r;
3611 		pt += base->bytes_per_line;
3612 	    }
3613 	    set = 1;
3614 	} else {
3615 	    if ( pt + ((c+cnt)>>3) > end ) {
3616 		IError( "Run length encoded image has been corrupted.\n" );
3617     break;
3618 	    }
3619 	    if ( !set ) {
3620 		for ( i=0; i<cnt; ++i )
3621 		    pt[(c+i)>>3] &= ((~0x80)>>((c+i)&7));
3622 	    }
3623 	    c += cnt;
3624 	    set = 1-set;
3625 	    if ( c>=base->width ) {
3626 		++r;
3627 		pt += base->bytes_per_line;
3628 		c = 0; set = 1;
3629 	    }
3630 	}
3631     }
3632 }
3633 
3634 #ifndef _NO_LIBPNG
3635 
3636 enum MIME { UNKNOWN, PNG }; // We only understand PNG for now.
3637 
SFDGetImage2MIME(FILE * sfd)3638 enum MIME SFDGetImage2MIME(FILE *sfd) {
3639     char mime[128];
3640 
3641     if ( !getname(sfd, mime) ) {
3642         IError("Failed to get a MIME type, file corrupt");
3643         return UNKNOWN;
3644     }
3645 
3646     if ( !(strmatch(mime, "image/png")==0) ) {
3647         IError("MIME type received—%s—is not recognized", mime);
3648         return UNKNOWN;
3649     }
3650 
3651     return PNG;
3652 }
3653 
SFDGetImagePNG(FILE * sfd)3654 static ImageList *SFDGetImagePNG(FILE *sfd) {
3655     int pnglen;
3656     ImageList *img;
3657     struct enc85 dec = {0};
3658     int i, ch;
3659 
3660     img = calloc(1,sizeof(ImageList));
3661     dec.pos = -1;
3662     dec.sfd = sfd;
3663 
3664     getint(sfd,&pnglen);
3665     getreal(sfd,&img->xoff);
3666     getreal(sfd,&img->yoff);
3667     getreal(sfd,&img->xscale);
3668     getreal(sfd,&img->yscale);
3669 
3670     while ( (ch=nlgetc(sfd))==' ' || ch=='\t' )
3671         /* skip */;
3672 
3673     char* pngbuf = malloc(pnglen * sizeof(char));
3674     if (pngbuf == NULL) {
3675         IError("Failed to allocate buffer to read PNG in SFD file");
3676         return NULL;
3677     }
3678 
3679     for (i = 0; i<pnglen; ++i) {
3680         pngbuf[i] = Dec85(&dec);
3681     }
3682 
3683     img->image = GImageReadPngBuf(pngbuf, pnglen);
3684     free(pngbuf);
3685 
3686     if (img->image == NULL) {
3687         IError("Failed to read PNG in SFD file, skipping it.");
3688         free(img);
3689         return NULL;
3690     }
3691 
3692     img->bb.minx = img->xoff; img->bb.maxy = img->yoff;
3693     img->bb.maxx = img->xoff + GImageGetWidth(img->image)*img->xscale;
3694     img->bb.miny = img->yoff - GImageGetHeight(img->image)*img->yscale;
3695     return img;
3696 }
3697 #endif
3698 
SFDGetImage(FILE * sfd)3699 static ImageList *SFDGetImage(FILE *sfd) {
3700     /* We've read the image token */
3701     int width, height, image_type, bpl, clutlen, rlelen;
3702     uint32 trans;
3703     struct _GImage *base;
3704     GImage *image;
3705     ImageList *img;
3706     struct enc85 dec;
3707     int i, ch;
3708 
3709     memset(&dec,'\0', sizeof(dec)); dec.pos = -1;
3710     dec.sfd = sfd;
3711 
3712     getint(sfd,&width);
3713     getint(sfd,&height);
3714     getint(sfd,&image_type);
3715     getint(sfd,&bpl);
3716     getint(sfd,&clutlen);
3717     gethex(sfd,&trans);
3718     image = GImageCreate(image_type,width,height);
3719     base = image->list_len==0?image->u.image:image->u.images[0];
3720     img = calloc(1,sizeof(ImageList));
3721     img->image = image;
3722     getreal(sfd,&img->xoff);
3723     getreal(sfd,&img->yoff);
3724     getreal(sfd,&img->xscale);
3725     getreal(sfd,&img->yscale);
3726     while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
3727     ungetc(ch,sfd);
3728     rlelen = 0;
3729     if ( isdigit(ch))
3730 	getint(sfd,&rlelen);
3731     base->trans = trans;
3732     if ( clutlen!=0 ) {
3733 	if ( base->clut==NULL )
3734 	    base->clut = calloc(1,sizeof(GClut));
3735 	base->clut->clut_len = clutlen;
3736 	base->clut->trans_index = trans;
3737 	for ( i=0;i<clutlen; ++i ) {
3738 	    int r,g,b;
3739 	    r = Dec85(&dec);
3740 	    g = Dec85(&dec);
3741 	    b = Dec85(&dec);
3742 	    base->clut->clut[i] = (r<<16)|(g<<8)|b;
3743 	}
3744     }
3745     if ( rlelen!=0 ) {
3746 	rle2image(&dec,rlelen,base);
3747     } else {
3748 	for ( i=0; i<height; ++i ) {
3749 	    if ( image_type==it_rgba ) {
3750 		uint32 *ipt = (uint32 *) (base->data + i*base->bytes_per_line);
3751 		uint32 *iend = (uint32 *) (base->data + (i+1)*base->bytes_per_line);
3752 		int r,g,b, a;
3753 		while ( ipt<iend ) {
3754 		    a = Dec85(&dec);
3755 		    r = Dec85(&dec);
3756 		    g = Dec85(&dec);
3757 		    b = Dec85(&dec);
3758 		    *ipt++ = (a<<24)|(r<<16)|(g<<8)|b;
3759 		}
3760 	    } else if ( image_type==it_true ) {
3761 		int *ipt = (int *) (base->data + i*base->bytes_per_line);
3762 		int *iend = (int *) (base->data + (i+1)*base->bytes_per_line);
3763 		int r,g,b;
3764 		while ( ipt<iend ) {
3765 		    r = Dec85(&dec);
3766 		    g = Dec85(&dec);
3767 		    b = Dec85(&dec);
3768 		    *ipt++ = (r<<16)|(g<<8)|b;
3769 		}
3770 	    } else {
3771 		uint8 *pt = (uint8 *) (base->data + i*base->bytes_per_line);
3772 		uint8 *end = (uint8 *) (base->data + (i+1)*base->bytes_per_line);
3773 		while ( pt<end ) {
3774 		    *pt++ = Dec85(&dec);
3775 		}
3776 	    }
3777 	}
3778     }
3779     img->bb.minx = img->xoff; img->bb.maxy = img->yoff;
3780     img->bb.maxx = img->xoff + GImageGetWidth(img->image)*img->xscale;
3781     img->bb.miny = img->yoff - GImageGetHeight(img->image)*img->yscale;
3782     /* In old sfd files I failed to recognize bitmap pngs as bitmap, so put */
3783     /*  in a little check here that converts things which should be bitmap to */
3784     /*  bitmap */ /* Eventually it can be removed as all old sfd files get */
3785     /*  converted. 22/10/2002 */
3786     if ( base->image_type==it_index && base->clut!=NULL && base->clut->clut_len==2 )
3787 	img->image = ImageAlterClut(img->image);
3788 return( img );
3789 }
3790 
SFDGetType1(FILE * sfd)3791 static void SFDGetType1(FILE *sfd) {
3792     /* We've read the OrigType1 token (this is now obselete, but parse it in case there are any old sfds) */
3793     int len;
3794     struct enc85 dec;
3795 
3796     memset(&dec,'\0', sizeof(dec)); dec.pos = -1;
3797     dec.sfd = sfd;
3798 
3799     getint(sfd,&len);
3800     while ( --len >= 0 )
3801 	Dec85(&dec);
3802 }
3803 
SFDGetTtfInstrs(FILE * sfd,SplineChar * sc)3804 static void SFDGetTtfInstrs(FILE *sfd, SplineChar *sc) {
3805     /* We've read the TtfInstr token, it is followed by a byte count */
3806     /* and then the instructions in enc85 format */
3807     int i,len;
3808     struct enc85 dec;
3809 
3810     memset(&dec,'\0', sizeof(dec)); dec.pos = -1;
3811     dec.sfd = sfd;
3812 
3813     getint(sfd,&len);
3814     sc->ttf_instrs = malloc(len);
3815     sc->ttf_instrs_len = len;
3816     for ( i=0; i<len; ++i )
3817 	sc->ttf_instrs[i] = Dec85(&dec);
3818 }
3819 
tterr(void * UNUSED (rubbish),char * message,int UNUSED (pos))3820 static void tterr(void *UNUSED(rubbish), char *message, int UNUSED(pos)) {
3821     LogError(_("When loading tt instrs from sfd: %s\n"), message );
3822 }
3823 
SFDGetTtInstrs(FILE * sfd,SplineChar * sc)3824 static void SFDGetTtInstrs(FILE *sfd, SplineChar *sc) {
3825     /* We've read the TtInstr token, it is followed by text versions of */
3826     /*  the instructions, slurp it all into a big buffer, and then parse that */
3827     char *buf=NULL, *pt=buf, *end=buf;
3828     int ch;
3829     int backlen = strlen(end_tt_instrs);
3830     int instr_len;
3831 
3832     while ( (ch=nlgetc(sfd))!=EOF ) {
3833 	if ( pt>=end ) {
3834 	    char *newbuf = realloc(buf,(end-buf+200));
3835 	    pt = newbuf+(pt-buf);
3836 	    end = newbuf+(end+200-buf);
3837 	    buf = newbuf;
3838 	}
3839 	*pt++ = ch;
3840 	if ( pt-buf>backlen && strncmp(pt-backlen,end_tt_instrs,backlen)==0 ) {
3841 	    pt -= backlen;
3842     break;
3843 	}
3844     }
3845     *pt = '\0';
3846 
3847     sc->ttf_instrs = _IVParse(sc->parent,buf,&instr_len,tterr,NULL);
3848     sc->ttf_instrs_len = instr_len;
3849 
3850     free(buf);
3851 }
3852 
SFDGetTtfTable(FILE * sfd,SplineFont * sf,struct ttf_table * lasttab[2])3853 static struct ttf_table *SFDGetTtfTable(FILE *sfd, SplineFont *sf,struct ttf_table *lasttab[2]) {
3854     /* We've read the TtfTable token, it is followed by a tag and a byte count */
3855     /* and then the instructions in enc85 format */
3856     int i,len;
3857     int which;
3858     struct enc85 dec;
3859     struct ttf_table *tab = chunkalloc(sizeof(struct ttf_table));
3860 
3861     memset(&dec,'\0', sizeof(dec)); dec.pos = -1;
3862     dec.sfd = sfd;
3863 
3864     tab->tag = gettag(sfd);
3865 
3866     if ( tab->tag==CHR('f','p','g','m') || tab->tag==CHR('p','r','e','p') ||
3867 	    tab->tag==CHR('c','v','t',' ') || tab->tag==CHR('m','a','x','p'))
3868 	which = 0;
3869     else
3870 	which = 1;
3871 
3872     getint(sfd,&len);
3873     tab->data = malloc(len);
3874     tab->len = len;
3875     for ( i=0; i<len; ++i )
3876 	tab->data[i] = Dec85(&dec);
3877 
3878     if ( lasttab[which]!=NULL )
3879 	lasttab[which]->next = tab;
3880     else if ( which==0 )
3881 	sf->ttf_tables = tab;
3882     else
3883 	sf->ttf_tab_saved = tab;
3884     lasttab[which] = tab;
3885 return( tab );
3886 }
3887 
SFDGetShortTable(FILE * sfd,SplineFont * sf,struct ttf_table * lasttab[2])3888 static struct ttf_table *SFDGetShortTable(FILE *sfd, SplineFont *sf,struct ttf_table *lasttab[2]) {
3889     /* We've read the ShortTable token, it is followed by a tag and a word count */
3890     /* and then the (text) values of the words that make up the cvt table */
3891     int i,len, ch;
3892     uint8 *pt;
3893     int which, iscvt, started;
3894     struct ttf_table *tab = chunkalloc(sizeof(struct ttf_table));
3895 
3896     tab->tag = gettag(sfd);
3897 
3898     if ( tab->tag==CHR('f','p','g','m') || tab->tag==CHR('p','r','e','p') ||
3899 	    tab->tag==CHR('c','v','t',' ') || tab->tag==CHR('m','a','x','p'))
3900 	which = 0;
3901     else
3902 	which = 1;
3903     iscvt = tab->tag==CHR('c','v','t',' ');
3904 
3905     getint(sfd,&len);
3906     pt = tab->data = malloc(2*len);
3907     tab->len = 2*len;
3908     started = false;
3909     for ( i=0; i<len; ++i ) {
3910 	int num;
3911 	getint(sfd,&num);
3912 	*pt++ = num>>8;
3913 	*pt++ = num&0xff;
3914 	if ( iscvt ) {
3915 	    ch = nlgetc(sfd);
3916 	    if ( ch==' ' ) {
3917 		if ( !started ) {
3918 		    sf->cvt_names = calloc(len+1,sizeof(char *));
3919 		    sf->cvt_names[len] = END_CVT_NAMES;
3920 		    started = true;
3921 		}
3922 		sf->cvt_names[i] = SFDReadUTF7Str(sfd);
3923 	    } else
3924 		ungetc(ch,sfd);
3925 	}
3926     }
3927 
3928     if ( lasttab[which]!=NULL )
3929 	lasttab[which]->next = tab;
3930     else if ( which==0 )
3931 	sf->ttf_tables = tab;
3932     else
3933 	sf->ttf_tab_saved = tab;
3934     lasttab[which] = tab;
3935 return( tab );
3936 }
3937 
SFDGetTtTable(FILE * sfd,SplineFont * sf,struct ttf_table * lasttab[2])3938 static struct ttf_table *SFDGetTtTable(FILE *sfd, SplineFont *sf,struct ttf_table *lasttab[2]) {
3939     /* We've read the TtTable token, it is followed by a tag */
3940     /* and then the instructions in text format */
3941     int ch;
3942     int which;
3943     struct ttf_table *tab = chunkalloc(sizeof(struct ttf_table));
3944     char *buf=NULL, *pt=buf, *end=buf;
3945     int backlen = strlen(end_tt_instrs);
3946 
3947     tab->tag = gettag(sfd);
3948 
3949     if ( tab->tag==CHR('f','p','g','m') || tab->tag==CHR('p','r','e','p') ||
3950 	    tab->tag==CHR('c','v','t',' ') || tab->tag==CHR('m','a','x','p'))
3951 	which = 0;
3952     else
3953 	which = 1;
3954 
3955     while ( (ch=nlgetc(sfd))!=EOF ) {
3956 	if ( pt>=end ) {
3957 	    char *newbuf = realloc(buf,(end-buf+200));
3958 	    pt = newbuf+(pt-buf);
3959 	    end = newbuf+(end+200-buf);
3960 	    buf = newbuf;
3961 	}
3962 	*pt++ = ch;
3963 	if ( pt-buf>backlen && strncmp(pt-backlen,end_tt_instrs,backlen)==0 ) {
3964 	    pt -= backlen;
3965     break;
3966 	}
3967     }
3968     *pt = '\0';
3969     tab->data = _IVParse(sf,buf,&tab->len,tterr,NULL);
3970     free(buf);
3971 
3972     if ( lasttab[which]!=NULL )
3973 	lasttab[which]->next = tab;
3974     else if ( which==0 )
3975 	sf->ttf_tables = tab;
3976     else
3977 	sf->ttf_tab_saved = tab;
3978     lasttab[which] = tab;
3979 return( tab );
3980 }
3981 
SFDCloseCheck(SplinePointList * spl,int order2)3982 static int SFDCloseCheck(SplinePointList *spl,int order2) {
3983     if ( spl->first!=spl->last &&
3984 	    RealNear(spl->first->me.x,spl->last->me.x) &&
3985 	    RealNear(spl->first->me.y,spl->last->me.y)) {
3986 	SplinePoint *oldlast = spl->last;
3987 	spl->first->prevcp = oldlast->prevcp;
3988 	spl->first->noprevcp = oldlast->noprevcp;
3989 	oldlast->prev->from->next = NULL;
3990 	spl->last = oldlast->prev->from;
3991 	chunkfree(oldlast->prev,sizeof(*oldlast));
3992 	chunkfree(oldlast->hintmask,sizeof(HintMask));
3993 	chunkfree(oldlast,sizeof(*oldlast));
3994 	SplineMake(spl->last,spl->first,order2);
3995 	spl->last = spl->first;
3996 return( true );
3997     }
3998 return( false );
3999 }
4000 
SFDGetHintMask(FILE * sfd,HintMask * hintmask)4001 static void SFDGetHintMask(FILE *sfd,HintMask *hintmask) {
4002     int nibble = 0, ch;
4003 
4004     memset(hintmask,0,sizeof(HintMask));
4005     for (;;) {
4006 	ch = nlgetc(sfd);
4007 	if ( isdigit(ch))
4008 	    ch -= '0';
4009 	else if ( ch>='a' && ch<='f' )
4010 	    ch -= 'a'-10;
4011 	else if ( ch>='A' && ch<='F' )
4012 	    ch -= 'A'-10;
4013 	else {
4014 	    ungetc(ch,sfd);
4015     break;
4016 	}
4017 	if ( nibble<2*HntMax/8 )
4018 	    (*hintmask)[nibble>>1] |= ch<<(4*(1-(nibble&1)));
4019 	++nibble;
4020     }
4021 }
4022 
SFDGetSpiros(FILE * sfd,SplineSet * cur)4023 static void SFDGetSpiros(FILE *sfd,SplineSet *cur) {
4024     int ch;
4025     spiro_cp cp;
4026 
4027     ch = nlgetc(sfd);		/* S */
4028     ch = nlgetc(sfd);		/* p */
4029     ch = nlgetc(sfd);		/* i */
4030     ch = nlgetc(sfd);		/* r */
4031     ch = nlgetc(sfd);		/* o */
4032     while ( fscanf(sfd,"%lg %lg %c", &cp.x, &cp.y, &cp.ty )==3 ) {
4033 	if ( cur!=NULL ) {
4034 	    if ( cur->spiro_cnt>=cur->spiro_max )
4035 		cur->spiros = realloc(cur->spiros,
4036 		                      (cur->spiro_max+=10)*sizeof(spiro_cp));
4037 	    cur->spiros[cur->spiro_cnt++] = cp;
4038 	}
4039     }
4040     if (    cur!=NULL && cur->spiro_cnt>0
4041          && (cur->spiros[cur->spiro_cnt-1].ty&0x7f)!=SPIRO_END ) {
4042 	if ( cur->spiro_cnt>=cur->spiro_max )
4043 	    cur->spiros = realloc(cur->spiros,
4044 	                          (cur->spiro_max+=1)*sizeof(spiro_cp));
4045 	memset(&cur->spiros[cur->spiro_cnt],0,sizeof(spiro_cp));
4046 	cur->spiros[cur->spiro_cnt++].ty = SPIRO_END;
4047     }
4048     ch = nlgetc(sfd);
4049     if ( ch=='E' ) {
4050 	ch = nlgetc(sfd);		/* n */
4051 	ch = nlgetc(sfd);		/* d */
4052 	ch = nlgetc(sfd);		/* S */
4053 	ch = nlgetc(sfd);		/* p */
4054 	ch = nlgetc(sfd);		/* i */
4055 	ch = nlgetc(sfd);		/* r */
4056 	ch = nlgetc(sfd);		/* o */
4057     } else
4058 	ungetc(ch,sfd);
4059 }
4060 
SFDGetSplineSet(FILE * sfd,int order2)4061 static SplineSet *SFDGetSplineSet(FILE *sfd,int order2) {
4062     SplinePointList *cur=NULL, *head=NULL;
4063     BasePoint current;
4064     real stack[100];
4065     int sp=0;
4066     SplinePoint *pt = NULL;
4067     int ch;
4068 	int ch2;
4069     char tok[100];
4070     int ttfindex = 0;
4071     int lastacceptable;
4072     int flags = 0, tmp;
4073 
4074     current.x = current.y = 0;
4075     lastacceptable = 0;
4076     while ( 1 ) {
4077 
4078 	while ( getreal(sfd,&stack[sp])==1 )
4079 	    if ( sp<99 )
4080 		++sp;
4081 	while ( isspace(ch=nlgetc(sfd)));
4082 	if ( ch=='E' || ch=='e' || ch==EOF )
4083     break;
4084 	if ( ch=='S' ) {
4085 	    ungetc(ch,sfd);
4086 	    SFDGetSpiros(sfd,cur);
4087     continue;
4088 	} else if (( ch=='N' ) &&
4089 	    nlgetc(sfd)=='a' &&	/* a */
4090 	    nlgetc(sfd)=='m' &&	/* m */
4091 	    nlgetc(sfd)=='e' &&	/* e */
4092 	    nlgetc(sfd)=='d' ) /* d */ {
4093 	    ch2 = nlgetc(sfd);		/* : */
4094 		// We are either fetching a splineset name (Named:) or a point name (NamedP:).
4095 		if (ch2=='P') { if ((nlgetc(sfd)==':') && (pt!=NULL)) { if (pt->name!=NULL) {free(pt->name);} pt->name = SFDReadUTF7Str(sfd); } }
4096 		else if (ch2==':') { if (cur != NULL) cur->contour_name = SFDReadUTF7Str(sfd); else { char * freetmp = SFDReadUTF7Str(sfd); free(freetmp); freetmp = NULL; } }
4097         continue;
4098 	} else if ( ch=='P' && PeekMatch(sfd,"ath") ) {
4099 	    int flags;
4100 	    nlgetc(sfd);		/* a */
4101 	    nlgetc(sfd);		/* t */
4102 	    nlgetc(sfd);		/* h */
4103 	    if (PeekMatch(sfd,"Flags:")) {
4104 	      nlgetc(sfd);		/* F */
4105 	      nlgetc(sfd);		/* l */
4106 	      nlgetc(sfd);		/* a */
4107 	      nlgetc(sfd);		/* g */
4108 	      nlgetc(sfd);		/* s */
4109 	      nlgetc(sfd);		/* : */
4110 	      getint(sfd,&flags);
4111 	      if (cur != NULL) cur->is_clip_path = flags&1;
4112 	    } else if (PeekMatch(sfd,"Start:")) {
4113 	      nlgetc(sfd);		/* S */
4114 	      nlgetc(sfd);		/* t */
4115 	      nlgetc(sfd);		/* a */
4116 	      nlgetc(sfd);		/* r */
4117 	      nlgetc(sfd);		/* t */
4118 	      nlgetc(sfd);		/* : */
4119 	      getint(sfd,&flags);
4120 	      if (cur != NULL) cur->start_offset = flags;
4121 	    }
4122 	}
4123 	pt = NULL;
4124 	if ( ch=='l' || ch=='m' ) {
4125 	    if ( sp>=2 ) {
4126 		current.x = stack[sp-2];
4127 		current.y = stack[sp-1];
4128 		sp -= 2;
4129 		pt = chunkalloc(sizeof(SplinePoint));
4130 		pt->me = current;
4131 		pt->noprevcp = true; pt->nonextcp = true;
4132 		if ( ch=='m' ) {
4133 		    SplinePointList *spl = chunkalloc(sizeof(SplinePointList));
4134 		    spl->first = spl->last = pt;
4135 		    spl->start_offset = 0;
4136 		    if ( cur!=NULL ) {
4137 			if ( !(flags & SFD_PTFLAG_FORCE_OPEN_PATH) && SFDCloseCheck(cur,order2) )
4138 			    --ttfindex;
4139 			cur->next = spl;
4140 		    } else
4141 			head = spl;
4142 		    cur = spl;
4143 		} else {
4144 		    if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) {
4145 			if ( cur->last->nextcpindex==0xfffe )
4146 			    cur->last->nextcpindex = 0xffff;
4147 			SplineMake(cur->last,pt,order2);
4148 			cur->last->nonextcp = 1;
4149 			pt->noprevcp = 1;
4150 			cur->last = pt;
4151 		    }
4152 		}
4153 	    } else
4154 		sp = 0;
4155 	} else if ( ch=='c' ) {
4156 	    if ( sp>=6 ) {
4157 		current.x = stack[sp-2];
4158 		current.y = stack[sp-1];
4159 
4160 		if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) {
4161 		    cur->last->nextcp.x = stack[sp-6];
4162 		    cur->last->nextcp.y = stack[sp-5];
4163 		    cur->last->nonextcp = false;
4164 		    pt = chunkalloc(sizeof(SplinePoint));
4165 		    pt->prevcp.x = stack[sp-4];
4166 		    pt->prevcp.y = stack[sp-3];
4167 		    pt->me = current;
4168 		    pt->nonextcp = true;
4169 		    if ( cur->last->nextcpindex==0xfffe )
4170 			cur->last->nextcpindex = ttfindex++;
4171 		    else if ( cur->last->nextcpindex!=0xffff )
4172 			ttfindex = cur->last->nextcpindex+1;
4173 		    SplineMake(cur->last,pt,order2);
4174 		    cur->last = pt;
4175 		}
4176 
4177 		sp -= 6;
4178 	    } else
4179 		sp = 0;
4180 	}
4181 	if ( pt!=NULL ) {
4182 	    getint(sfd,&flags);
4183 
4184 	    pt->pointtype = (flags & SFD_PTFLAG_TYPE_MASK);
4185 	    pt->selected  = (flags & SFD_PTFLAG_IS_SELECTED) > 0;
4186 	    pt->nextcpdef = (flags & SFD_PTFLAG_NEXTCP_IS_DEFAULT) > 0;
4187 	    pt->prevcpdef = (flags & SFD_PTFLAG_PREVCP_IS_DEFAULT) > 0;
4188 	    pt->roundx    = (flags & SFD_PTFLAG_ROUND_IN_X) > 0;
4189 	    pt->roundy    = (flags & SFD_PTFLAG_ROUND_IN_Y) > 0;
4190 	    pt->dontinterpolate = (flags & SFD_PTFLAG_INTERPOLATE_NEVER) > 0;
4191 	    if ( pt->prev!=NULL )
4192 		pt->prev->acceptableextrema = (flags & SFD_PTFLAG_PREV_EXTREMA_MARKED_ACCEPTABLE) > 0;
4193 	    else
4194 		lastacceptable = (flags & SFD_PTFLAG_PREV_EXTREMA_MARKED_ACCEPTABLE) > 0;
4195 	    if ( flags&0x80 )
4196 		pt->ttfindex = 0xffff;
4197 	    else
4198 		pt->ttfindex = ttfindex++;
4199 	    pt->nextcpindex = 0xfffe;
4200 	    ch = nlgetc(sfd);
4201 	    if ( ch=='x' ) {
4202 		pt->hintmask = chunkalloc(sizeof(HintMask));
4203 		SFDGetHintMask(sfd,pt->hintmask);
4204 	    } else if ( ch!=',' )
4205 		ungetc(ch,sfd);
4206 	    else {
4207 		ch = nlgetc(sfd);
4208 		if ( ch==',' )
4209 		    pt->ttfindex = 0xfffe;
4210 		else {
4211 		    ungetc(ch,sfd);
4212 		    getint(sfd,&tmp);
4213 		    pt->ttfindex = tmp;
4214 		    nlgetc(sfd);	/* skip comma */
4215 		    if ( tmp!=-1 )
4216 			ttfindex = tmp+1;
4217 		}
4218 		ch = nlgetc(sfd);
4219 		if ( ch=='\r' || ch=='\n' )
4220 		    ungetc(ch,sfd);
4221 		else {
4222 		    ungetc(ch,sfd);
4223 		    getint(sfd,&tmp);
4224 		    pt->nextcpindex = tmp;
4225 		    if ( tmp!=-1 )
4226 			ttfindex = tmp+1;
4227 		}
4228 	    }
4229 	} else
4230 	    flags = 0;
4231     }
4232     if ( cur!=NULL && !(flags & SFD_PTFLAG_FORCE_OPEN_PATH) )
4233 	SFDCloseCheck(cur,order2);
4234     if ( lastacceptable && cur->last->prev!=NULL )
4235 	cur->last->prev->acceptableextrema = true;
4236     getname(sfd,tok);
4237 return( head );
4238 }
4239 
SFDGetUndo(FILE * sfd,SplineChar * sc,const char * startTag,int current_layer)4240 Undoes *SFDGetUndo( FILE *sfd, SplineChar *sc,
4241 		    const char* startTag,
4242 		    int current_layer )
4243 {
4244     Undoes *u = 0;
4245     char tok[2000];
4246     int i;
4247     RefChar *lastr=NULL;
4248     ImageList *lasti=NULL;
4249     AnchorPoint *lastap = NULL;
4250     GuidelineSet *lastgl = NULL;
4251     SplineChar* tsc = 0;
4252 
4253     if ( getname(sfd,tok)!=1 )
4254         return( NULL );
4255     if ( strcmp(tok, startTag) )
4256         return( NULL );
4257 
4258     u = chunkalloc(sizeof(Undoes));
4259     u->undotype = ut_state;
4260     u->layer = UNDO_LAYER_UNKNOWN;
4261 
4262     while ( 1 )
4263     {
4264         if ( getname(sfd,tok)!=1 ) {
4265             chunkfree(u,sizeof(Undoes));
4266             return( NULL );
4267         }
4268 
4269         if ( !strmatch(tok,"EndUndoOperation")
4270             || !strmatch(tok,"EndRedoOperation"))
4271         {
4272             if( u->undotype == ut_hints ) {
4273                 if( tsc ) {
4274                     u->u.state.hints = UHintCopy(tsc,1);
4275                     SplineCharFree( tsc );
4276                 }
4277             }
4278 
4279             return u;
4280         }
4281 	if ( !strmatch(tok,"Index:")) {
4282             getint(sfd,&i);
4283         }
4284 	if ( !strmatch(tok,"Type:")) {
4285             getint(sfd,&i);
4286             u->undotype = i;
4287             if( u->undotype == ut_hints ) {
4288                 tsc = SplineCharCopy( sc, 0, 0 );
4289                 tsc->hstem = 0;
4290                 tsc->vstem = 0;
4291                 tsc->dstem = 0;
4292             }
4293         }
4294         if ( !strmatch(tok,"WasModified:")) {
4295             getint(sfd,&i);
4296             u->was_modified = i;
4297         }
4298         if ( !strmatch(tok,"WasOrder2:")) {
4299             getint(sfd,&i);
4300             u->was_order2 = i;
4301         }
4302         if ( !strmatch(tok,"Layer:")) {
4303             getint(sfd,&i);
4304             u->layer = i;
4305         }
4306 
4307         switch( u->undotype )
4308         {
4309 	case ut_tstate:
4310 	case ut_state:
4311 	    if ( !strmatch(tok,"Width:"))          { getint(sfd,&i); u->u.state.width = i; }
4312 	    if ( !strmatch(tok,"VWidth:"))         { getint(sfd,&i); u->u.state.vwidth = i; }
4313 	    if ( !strmatch(tok,"LBearingChange:")) { getint(sfd,&i); u->u.state.lbearingchange = i; }
4314 	    if ( !strmatch(tok,"UnicodeEnc:"))     { getint(sfd,&i); u->u.state.unicodeenc = i; }
4315 	    if ( !strmatch(tok,"Charname:"))       { u->u.state.charname = getquotedeol(sfd); }
4316 	    if ( !strmatch(tok,"Comment:"))        { u->u.state.comment  = getquotedeol(sfd); }
4317 
4318 	    if( !strmatch(tok,"Refer:"))
4319 	    {
4320 		RefChar *ref = SFDGetRef(sfd,strmatch(tok,"Ref:")==0);
4321 		int i=0;
4322 		for( i=0; i< ref->layer_cnt; i++ ) {
4323 		    ref->layers[i].splines = 0;
4324 		}
4325 		if ( !u->u.state.refs )
4326 		    u->u.state.refs = ref;
4327 		else
4328 		    lastr->next = ref;
4329 		lastr = ref;
4330 	    }
4331 
4332 	    if( !strmatch(tok,"Image:"))
4333 	    {
4334 		ImageList *img = SFDGetImage(sfd);
4335 		if (img != NULL) {
4336 		if ( !u->u.state.images )
4337 		    u->u.state.images = img;
4338 		else
4339 		    lasti->next = img;
4340 		lasti = img;
4341 		}
4342 	    }
4343 
4344 	    if( !strmatch(tok,"Image2:"))
4345 	    {
4346 #ifndef _NO_LIBPNG
4347 		enum MIME mime = SFDGetImage2MIME(sfd);
4348 		if (mime == PNG) {
4349 		    ImageList *img = SFDGetImagePNG(sfd);
4350 		    if (img != NULL) {
4351 			if ( !u->u.state.images )
4352 			    u->u.state.images = img;
4353 			else
4354 			    lasti->next = img;
4355 			lasti = img;
4356 		    }
4357 		} else
4358 #endif
4359 	    {
4360 		LogError(_("Image2 skipped as it uses an unsupported image type"));
4361 		const char* im2_terminator[] = { "EndImage2", 0 };
4362 		SFDConsumeUntil(sfd, im2_terminator);
4363 	    }
4364 	    }
4365 
4366 	    if( !strmatch(tok,"Comment:")) {
4367 		u->u.state.comment  = getquotedeol(sfd);
4368 	    }
4369 	    if( !strmatch(tok,"InstructionsLength:")) {
4370 		getint(sfd,&i); u->u.state.instrs_len = i;
4371 	    }
4372 	    if( !strmatch(tok,"AnchorPoint:") ) {
4373 		lastap = SFDReadAnchorPoints( sfd, sc, &(u->u.state.anchor), lastap );
4374 	    }
4375 	    if ( !strmatch(tok,"SplineSet")) {
4376 		u->u.state.splines = SFDGetSplineSet(sfd,sc->layers[current_layer].order2);
4377 	    }
4378 	    break;
4379 	case ut_hints:
4380 	{
4381 	    if ( !strmatch(tok,"HStem:") ) {
4382 		tsc->hstem = SFDReadHints(sfd);
4383 		tsc->hconflicts = StemListAnyConflicts(tsc->hstem);
4384 	    }
4385 	    else if ( !strmatch(tok,"VStem:") ) {
4386 		tsc->vstem = SFDReadHints(sfd);
4387 		tsc->vconflicts = StemListAnyConflicts(tsc->vstem);
4388 	    }
4389 	    else if( !strmatch(tok,"DStem2:"))
4390 		tsc->dstem = SFDReadDHints( sc->parent,sfd,false );
4391 	    else if( !strmatch(tok,"TtInstrs:")) {
4392 		SFDGetTtInstrs(sfd,tsc);
4393 		u->u.state.instrs = tsc->ttf_instrs;
4394 		u->u.state.instrs_len = tsc->ttf_instrs_len;
4395 		tsc->ttf_instrs = 0;
4396 		tsc->ttf_instrs_len = 0;
4397 	    }
4398 	    break;
4399 	}
4400 
4401 	case ut_width:
4402 	case ut_vwidth:
4403 	    if( !strmatch(tok,"Width:")) {
4404 		getint(sfd,&i); u->u.width = i;
4405 	    }
4406 	    break;
4407 	default:
4408 	break;
4409         }
4410     }
4411 
4412     return u;
4413 }
4414 
SFDGetMinimumDistances(FILE * sfd,SplineChar * sc)4415 static void SFDGetMinimumDistances(FILE *sfd, SplineChar *sc) {
4416     SplineSet *ss;
4417     SplinePoint *sp;
4418     int pt,i, val, err;
4419     int ch;
4420     SplinePoint **mapping=NULL;
4421     MinimumDistance *last, *md, *mdhead=NULL;
4422 
4423     for ( i=0; i<2; ++i ) {
4424 	pt = 0;
4425 	for ( ss = sc->layers[ly_fore].splines; ss!=NULL; ss=ss->next ) {
4426 	    for ( sp=ss->first; ; ) {
4427 		if ( mapping!=NULL ) mapping[pt] = sp;
4428 		pt++;
4429 		if ( sp->next == NULL )
4430 	    break;
4431 		sp = sp->next->to;
4432 		if ( sp==ss->first )
4433 	    break;
4434 	    }
4435 	}
4436 	if ( mapping==NULL )
4437 	    mapping = calloc(pt,sizeof(SplinePoint *));
4438     }
4439 
4440     last = NULL;
4441     for ( ch=nlgetc(sfd); ch!=EOF && ch!='\n'; ch=nlgetc(sfd)) {
4442 	err = false;
4443 	while ( isspace(ch) && ch!='\n' ) ch=nlgetc(sfd);
4444 	if ( ch=='\n' )
4445     break;
4446 	md = chunkalloc(sizeof(MinimumDistance));
4447 	if ( ch=='x' ) md->x = true;
4448 	getint(sfd,&val);
4449 	if ( val<-1 || val>=pt ) {
4450 	    IError( "Minimum Distance specifies bad point (%d) in sfd file\n", val );
4451 	    err = true;
4452 	} else if ( val!=-1 ) {
4453 	    md->sp1 = mapping[val];
4454 	    md->sp1->dontinterpolate = true;
4455 	}
4456 	ch = nlgetc(sfd);
4457 	if ( ch!=',' ) {
4458 	    IError( "Minimum Distance lacks a comma where expected\n" );
4459 	    err = true;
4460 	}
4461 	getint(sfd,&val);
4462 	if ( val<-1 || val>=pt ) {
4463 	    IError( "Minimum Distance specifies bad point (%d) in sfd file\n", val );
4464 	    err = true;
4465 	} else if ( val!=-1 ) {
4466 	    md->sp2 = mapping[val];
4467 	    md->sp2->dontinterpolate = true;
4468 	}
4469 	if ( !err ) {
4470 	    if ( last!=NULL )
4471 		last->next = md;
4472 	    last = md;
4473 	} else
4474 	    chunkfree(md,sizeof(MinimumDistance));
4475     }
4476     free(mapping);
4477 
4478     /* Obsolete concept */
4479     MinimumDistancesFree(mdhead);
4480 }
4481 
SFDReadHintInstances(FILE * sfd,StemInfo * stem)4482 static HintInstance *SFDReadHintInstances(FILE *sfd, StemInfo *stem) {
4483     HintInstance *head=NULL, *last=NULL, *cur;
4484     real begin, end;
4485     int ch;
4486 
4487     while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
4488     if ( ch=='G' && stem != NULL ) {
4489 	stem->ghost = true;
4490 	while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
4491     }
4492     if ( ch!='<' ) {
4493 	ungetc(ch,sfd);
4494 return(NULL);
4495     }
4496     while ( getreal(sfd,&begin)==1 && getreal(sfd,&end)) {
4497 	cur = chunkalloc(sizeof(HintInstance));
4498 	cur->begin = begin;
4499 	cur->end = end;
4500 	if ( head == NULL )
4501 	    head = cur;
4502 	else
4503 	    last->next = cur;
4504 	last = cur;
4505     }
4506     while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
4507     if ( ch!='>' )
4508 	ungetc(ch,sfd);
4509 return( head );
4510 }
4511 
SFDReadHints(FILE * sfd)4512 static StemInfo *SFDReadHints(FILE *sfd) {
4513     StemInfo *head=NULL, *last=NULL, *cur;
4514     real start, width;
4515 
4516     while ( getreal(sfd,&start)==1 && getreal(sfd,&width)) {
4517 	cur = chunkalloc(sizeof(StemInfo));
4518 	cur->start = start;
4519 	cur->width = width;
4520 	cur->where = SFDReadHintInstances(sfd,cur);
4521 	if ( head == NULL )
4522 	    head = cur;
4523 	else
4524 	    last->next = cur;
4525 	last = cur;
4526     }
4527 return( head );
4528 }
4529 
SFDReadDHints(SplineFont * sf,FILE * sfd,int old)4530 static DStemInfo *SFDReadDHints( SplineFont *sf,FILE *sfd,int old ) {
4531     DStemInfo *head=NULL, *last=NULL, *cur;
4532     int i;
4533     BasePoint bp[4], *bpref[4], left, right, unit;
4534     double rstartoff, rendoff, lendoff;
4535 
4536     if ( old ) {
4537         for ( i=0 ; i<4 ; i++ ) bpref[i] = &bp[i];
4538 
4539         while ( getreal( sfd,&bp[0].x ) && getreal( sfd,&bp[0].y ) &&
4540 	        getreal( sfd,&bp[1].x ) && getreal( sfd,&bp[1].y ) &&
4541 	        getreal( sfd,&bp[2].x ) && getreal( sfd,&bp[2].y ) &&
4542 	        getreal( sfd,&bp[3].x ) && getreal( sfd,&bp[3].y )) {
4543 
4544             /* Ensure point coordinates specified in the sfd file do */
4545             /* form a diagonal line */
4546             if ( PointsDiagonalable( sf,bpref,&unit )) {
4547 	        cur = chunkalloc( sizeof( DStemInfo ));
4548 	        cur->left = *bpref[0];
4549 	        cur->right = *bpref[1];
4550                 cur->unit = unit;
4551                 /* Generate a temporary hint instance, so that the hint can */
4552                 /* be visible in charview even if subsequent rebuilding instances */
4553                 /* fails (e. g. for composite characters) */
4554                 cur->where = chunkalloc( sizeof( HintInstance ));
4555                 rstartoff = ( cur->right.x - cur->left.x ) * cur->unit.x +
4556                             ( cur->right.y - cur->left.y ) * cur->unit.y;
4557                 rendoff =   ( bpref[2]->x - cur->left.x ) * cur->unit.x +
4558                             ( bpref[2]->y - cur->left.y ) * cur->unit.y;
4559                 lendoff =   ( bpref[3]->x - cur->left.x ) * cur->unit.x +
4560                             ( bpref[3]->y - cur->left.y ) * cur->unit.y;
4561                 cur->where->begin = ( rstartoff > 0 ) ? rstartoff : 0;
4562                 cur->where->end   = ( rendoff > lendoff ) ? lendoff : rendoff;
4563                 MergeDStemInfo( sf,&head,cur );
4564             }
4565         }
4566     } else {
4567         while ( getreal( sfd,&left.x ) && getreal( sfd,&left.y ) &&
4568                 getreal( sfd,&right.x ) && getreal( sfd,&right.y ) &&
4569                 getreal( sfd,&unit.x ) && getreal( sfd,&unit.y )) {
4570 	    cur = chunkalloc( sizeof( DStemInfo ));
4571 	    cur->left = left;
4572 	    cur->right = right;
4573             cur->unit = unit;
4574 	    cur->where = SFDReadHintInstances( sfd,NULL );
4575 	    if ( head == NULL )
4576 	        head = cur;
4577 	    else
4578 	        last->next = cur;
4579 	    last = cur;
4580         }
4581     }
4582 return( head );
4583 }
4584 
SFDReadDeviceTable(FILE * sfd,DeviceTable * adjust)4585 static DeviceTable *SFDReadDeviceTable(FILE *sfd,DeviceTable *adjust) {
4586     int i, junk, first, last, ch, len;
4587 
4588     while ( (ch=nlgetc(sfd))==' ' );
4589     if ( ch=='{' ) {
4590 	while ( (ch=nlgetc(sfd))==' ' );
4591 	if ( ch=='}' )
4592 return(NULL);
4593 	else
4594 	    ungetc(ch,sfd);
4595 	getint(sfd,&first);
4596 	ch = nlgetc(sfd);		/* Should be '-' */
4597 	getint(sfd,&last);
4598 	len = last-first+1;
4599 	if ( len<=0 ) {
4600 	    IError( "Bad device table, invalid length.\n" );
4601 return(NULL);
4602 	}
4603 	if ( adjust==NULL )
4604 	    adjust = chunkalloc(sizeof(DeviceTable));
4605 	adjust->first_pixel_size = first;
4606 	adjust->last_pixel_size = last;
4607 	adjust->corrections = malloc(len);
4608 	for ( i=0; i<len; ++i ) {
4609 	    while ( (ch=nlgetc(sfd))==' ' );
4610 	    if ( ch!=',' ) ungetc(ch,sfd);
4611 	    getint(sfd,&junk);
4612 	    adjust->corrections[i] = junk;
4613 	}
4614 	while ( (ch=nlgetc(sfd))==' ' );
4615 	if ( ch!='}' ) ungetc(ch,sfd);
4616     } else
4617 	ungetc(ch,sfd);
4618 return( adjust );
4619 }
4620 
SFDReadValDevTab(FILE * sfd)4621 static ValDevTab *SFDReadValDevTab(FILE *sfd) {
4622     int i, j, ch;
4623     ValDevTab vdt;
4624     char buf[4];
4625 
4626     memset(&vdt,0,sizeof(vdt));
4627     buf[3] = '\0';
4628     while ( (ch=nlgetc(sfd))==' ' );
4629     if ( ch=='[' ) {
4630 	for ( i=0; i<4; ++i ) {
4631 	    while ( (ch=nlgetc(sfd))==' ' );
4632 	    if ( ch==']' )
4633 	break;
4634 	    buf[0]=ch;
4635 	    for ( j=1; j<3; ++j ) buf[j]=nlgetc(sfd);
4636 	    while ( (ch=nlgetc(sfd))==' ' );
4637 	    if ( ch!='=' ) ungetc(ch,sfd);
4638 	    SFDReadDeviceTable(sfd,
4639 		    strcmp(buf,"ddx")==0 ? &vdt.xadjust :
4640 		    strcmp(buf,"ddy")==0 ? &vdt.yadjust :
4641 		    strcmp(buf,"ddh")==0 ? &vdt.xadv :
4642 		    strcmp(buf,"ddv")==0 ? &vdt.yadv :
4643 			(&vdt.xadjust) + i );
4644 	    while ( (ch=nlgetc(sfd))==' ' );
4645 	    if ( ch!=']' ) ungetc(ch,sfd);
4646 	    else
4647 	break;
4648 	}
4649 	if ( vdt.xadjust.corrections!=NULL || vdt.yadjust.corrections!=NULL ||
4650 		vdt.xadv.corrections!=NULL || vdt.yadv.corrections!=NULL ) {
4651 	    ValDevTab *v = chunkalloc(sizeof(ValDevTab));
4652 	    *v = vdt;
4653 return( v );
4654 	}
4655     } else
4656 	ungetc(ch,sfd);
4657 return( NULL );
4658 }
4659 
SFDReadAnchorPoints(FILE * sfd,SplineChar * sc,AnchorPoint ** alist,AnchorPoint * lastap)4660 static AnchorPoint *SFDReadAnchorPoints(FILE *sfd,SplineChar *sc,AnchorPoint** alist, AnchorPoint *lastap)
4661 {
4662     AnchorPoint *ap = chunkalloc(sizeof(AnchorPoint));
4663     AnchorClass *an;
4664     char *name;
4665     char tok[200];
4666     int ch;
4667 
4668     name = SFDReadUTF7Str(sfd);
4669     if ( name==NULL ) {
4670 	LogError(_("Anchor Point with no class name: %s"), sc->name );
4671 	AnchorPointsFree(ap);
4672 return( lastap );
4673     }
4674     for ( an=sc->parent->anchor; an!=NULL && strcmp(an->name,name)!=0; an=an->next );
4675     free(name);
4676     ap->anchor = an;
4677     getreal(sfd,&ap->me.x);
4678     getreal(sfd,&ap->me.y);
4679     ap->type = -1;
4680     if ( getname(sfd,tok)==1 ) {
4681 	if ( strcmp(tok,"mark")==0 )
4682 	    ap->type = at_mark;
4683 	else if ( strcmp(tok,"basechar")==0 )
4684 	    ap->type = at_basechar;
4685 	else if ( strcmp(tok,"baselig")==0 )
4686 	    ap->type = at_baselig;
4687 	else if ( strcmp(tok,"basemark")==0 )
4688 	    ap->type = at_basemark;
4689 	else if ( strcmp(tok,"entry")==0 )
4690 	    ap->type = at_centry;
4691 	else if ( strcmp(tok,"exit")==0 )
4692 	    ap->type = at_cexit;
4693     }
4694     getsint(sfd,&ap->lig_index);
4695     ch = nlgetc(sfd);
4696     ungetc(ch,sfd);
4697     if ( ch==' ' ) {
4698 	SFDReadDeviceTable(sfd,&ap->xadjust);
4699 	SFDReadDeviceTable(sfd,&ap->yadjust);
4700 	ch = nlgetc(sfd);
4701 	ungetc(ch,sfd);
4702 	if ( isdigit(ch)) {
4703 	    getsint(sfd,(int16 *) &ap->ttf_pt_index);
4704 	    ap->has_ttf_pt = true;
4705 	}
4706     }
4707     if ( ap->anchor==NULL || ap->type==-1 ) {
4708 	LogError(_("Bad Anchor Point: %s"), sc->name );
4709 	AnchorPointsFree(ap);
4710 return( lastap );
4711     }
4712     if ( lastap==NULL )
4713 	(*alist) = ap;
4714     else
4715 	lastap->next = ap;
4716 
4717     return( ap );
4718 }
4719 
SFDReadGuideline(FILE * sfd,GuidelineSet ** gll,GuidelineSet * lastgl)4720 static GuidelineSet *SFDReadGuideline(FILE *sfd, GuidelineSet **gll, GuidelineSet *lastgl)
4721 {
4722     GuidelineSet *gl = chunkalloc(sizeof(GuidelineSet));
4723     gl->name = SFDReadUTF7Str(sfd);
4724     gl->identifier = SFDReadUTF7Str(sfd);
4725     getreal(sfd,&gl->point.x);
4726     getreal(sfd,&gl->point.y);
4727     getreal(sfd,&gl->angle);
4728     getint(sfd,&gl->color);
4729     getint(sfd,&gl->flags);
4730     if ( lastgl!=NULL )
4731 	lastgl->next = gl;
4732     else if (gll)
4733         *gll = gl;
4734     return( gl );
4735 }
4736 
SFDGetRef(FILE * sfd,int was_enc)4737 static RefChar *SFDGetRef(FILE *sfd, int was_enc) {
4738     RefChar *rf;
4739     int temp=0, ch;
4740 
4741     rf = RefCharCreate();
4742     getint(sfd,&rf->orig_pos);
4743     rf->encoded = was_enc;
4744     if ( getint(sfd,&temp))
4745 	rf->unicode_enc = temp;
4746     while ( isspace(ch=nlgetc(sfd)));
4747     if ( ch=='S' ) rf->selected = true;
4748     getreal(sfd,&rf->transform[0]);
4749     getreal(sfd,&rf->transform[1]);
4750     getreal(sfd,&rf->transform[2]);
4751     getreal(sfd,&rf->transform[3]);
4752     getreal(sfd,&rf->transform[4]);
4753     getreal(sfd,&rf->transform[5]);
4754     while ( (ch=nlgetc(sfd))==' ');
4755     ungetc(ch,sfd);
4756     if ( isdigit(ch) ) {
4757 	getint(sfd,&temp);
4758 	rf->use_my_metrics = temp&1;
4759 	rf->round_translation_to_grid = (temp&2)?1:0;
4760 	rf->point_match = (temp&4)?1:0;
4761 	if ( rf->point_match ) {
4762 	    getsint(sfd,(int16 *) &rf->match_pt_base);
4763 	    getsint(sfd,(int16 *) &rf->match_pt_ref);
4764 	    while ( (ch=nlgetc(sfd))==' ');
4765 	    if ( ch=='O' )
4766 		rf->point_match_out_of_date = true;
4767 	    else
4768 		ungetc(ch,sfd);
4769 	}
4770     }
4771 return( rf );
4772 }
4773 
4774 /* I used to create multiple ligatures by putting ";" between them */
4775 /* that is the component string for "ffi" was "ff i ; f f i" */
4776 /* Now I want to have separate ligature structures for each */
LigaCreateFromOldStyleMultiple(PST1 * liga)4777 static PST1 *LigaCreateFromOldStyleMultiple(PST1 *liga) {
4778     char *pt;
4779     PST1 *new, *last=liga;
4780     while ( (pt = strrchr(liga->pst.u.lig.components,';'))!=NULL ) {
4781 	new = chunkalloc(sizeof( PST1 ));
4782 	*new = *liga;
4783 	new->pst.u.lig.components = copy(pt+1);
4784 	last->pst.next = (PST *) new;
4785 	last = new;
4786 	*pt = '\0';
4787     }
4788 return( last );
4789 }
4790 
4791 #ifdef FONTFORGE_CONFIG_CVT_OLD_MAC_FEATURES
4792 static struct { int feature, setting; uint32 tag; } formertags[] = {
4793     { 1, 6, CHR('M','L','O','G') },
4794     { 1, 8, CHR('M','R','E','B') },
4795     { 1, 10, CHR('M','D','L','G') },
4796     { 1, 12, CHR('M','S','L','G') },
4797     { 1, 14, CHR('M','A','L','G') },
4798     { 8, 0, CHR('M','S','W','I') },
4799     { 8, 2, CHR('M','S','W','F') },
4800     { 8, 4, CHR('M','S','L','I') },
4801     { 8, 6, CHR('M','S','L','F') },
4802     { 8, 8, CHR('M','S','N','F') },
4803     { 22, 1, CHR('M','W','I','D') },
4804     { 27, 1, CHR('M','U','C','M') },
4805     { 103, 2, CHR('M','W','I','D') },
4806     { -1, -1, 0xffffffff },
4807 };
4808 
CvtOldMacFeature(PST1 * pst)4809 static void CvtOldMacFeature(PST1 *pst) {
4810     int i;
4811 
4812     if ( pst->macfeature )
4813 return;
4814     for ( i=0; formertags[i].feature!=-1 ; ++i ) {
4815 	if ( pst->tag == formertags[i].tag ) {
4816 	    pst->macfeature = true;
4817 	    pst->tag = (formertags[i].feature<<16) | formertags[i].setting;
4818 return;
4819 	}
4820     }
4821 }
4822 #endif
4823 
SFDSetEncMap(SplineFont * sf,int orig_pos,int enc)4824 static void SFDSetEncMap(SplineFont *sf,int orig_pos,int enc) {
4825     EncMap *map = sf->map;
4826 
4827     if ( map==NULL )
4828 return;
4829 
4830     if ( orig_pos>=map->backmax ) {
4831 	int old = map->backmax;
4832 	map->backmax = orig_pos+10;
4833 	map->backmap = realloc(map->backmap,map->backmax*sizeof(int));
4834 	memset(map->backmap+old,-1,(map->backmax-old)*sizeof(int));
4835     }
4836     if ( map->backmap[orig_pos] == -1 )		/* backmap will not be unique if multiple encodings come from same glyph */
4837 	map->backmap[orig_pos] = enc;
4838     if ( enc>=map->encmax ) {
4839 	int old = map->encmax;
4840 	map->encmax = enc+10;
4841 	map->map = realloc(map->map,map->encmax*sizeof(int));
4842 	memset(map->map+old,-1,(map->encmax-old)*sizeof(int));
4843     }
4844     if ( enc>=map->enccount )
4845 	map->enccount = enc+1;
4846     if ( enc>-1 )
4847 	map->map[enc] = orig_pos;
4848 }
4849 
SCDefaultInterpolation(SplineChar * sc)4850 static void SCDefaultInterpolation(SplineChar *sc) {
4851     SplineSet *cur;
4852     SplinePoint *sp;
4853     /* We used not to store the dontinterpolate bit. We used to use the */
4854     /* presence or absence of instructions as that flag */
4855 
4856     if ( sc->ttf_instrs_len!=0 ) {
4857 	for ( cur=sc->layers[ly_fore].splines; cur!=NULL; cur=cur->next ) {
4858 	    for ( sp=cur->first; ; ) {
4859 		if ( sp->ttfindex!=0xffff && SPInterpolate(sp))
4860 		    sp->dontinterpolate = true;
4861 		if ( sp->next==NULL )
4862 	    break;
4863 		sp=sp->next->to;
4864 		if ( sp==cur->first )
4865 	    break;
4866 	    }
4867 	}
4868     }
4869 }
4870 
SFDParseMathValueRecord(FILE * sfd,int16 * value,DeviceTable ** devtab)4871 static void SFDParseMathValueRecord(FILE *sfd,int16 *value,DeviceTable **devtab) {
4872     getsint(sfd,value);
4873     *devtab = SFDReadDeviceTable(sfd,NULL);
4874 }
4875 
SFDParseGlyphComposition(FILE * sfd,struct glyphvariants * gv,char * tok)4876 static struct glyphvariants *SFDParseGlyphComposition(FILE *sfd,
4877 	struct glyphvariants *gv, char *tok) {
4878     int i;
4879 
4880     if ( gv==NULL )
4881 	gv = chunkalloc(sizeof(struct glyphvariants));
4882     getint(sfd,&gv->part_cnt);
4883     gv->parts = calloc(gv->part_cnt,sizeof(struct gv_part));
4884     for ( i=0; i<gv->part_cnt; ++i ) {
4885 	int temp, ch;
4886 	getname(sfd,tok);
4887 	gv->parts[i].component = copy(tok);
4888 	while ( (ch=nlgetc(sfd))==' ' );
4889 	if ( ch!='%' ) ungetc(ch,sfd);
4890 	getint(sfd,&temp);
4891 	gv->parts[i].is_extender = temp;
4892 	while ( (ch=nlgetc(sfd))==' ' );
4893 	if ( ch!=',' ) ungetc(ch,sfd);
4894 	getint(sfd,&temp);
4895 	gv->parts[i].startConnectorLength=temp;
4896 	while ( (ch=nlgetc(sfd))==' ' );
4897 	if ( ch!=',' ) ungetc(ch,sfd);
4898 	getint(sfd,&temp);
4899 	gv->parts[i].endConnectorLength = temp;
4900 	while ( (ch=nlgetc(sfd))==' ' );
4901 	if ( ch!=',' ) ungetc(ch,sfd);
4902 	getint(sfd,&temp);
4903 	gv->parts[i].fullAdvance = temp;
4904     }
4905 return( gv );
4906 }
4907 
SFDParseVertexKern(FILE * sfd,struct mathkernvertex * vertex)4908 static void SFDParseVertexKern(FILE *sfd, struct mathkernvertex *vertex) {
4909     int i,ch;
4910 
4911     getint(sfd,&vertex->cnt);
4912     vertex->mkd = calloc(vertex->cnt,sizeof(struct mathkerndata));
4913     for ( i=0; i<vertex->cnt; ++i ) {
4914 	SFDParseMathValueRecord(sfd,&vertex->mkd[i].height,&vertex->mkd[i].height_adjusts);
4915 	while ( (ch=nlgetc(sfd))==' ' );
4916 	if ( ch!=EOF && ch!=',' )
4917 	    ungetc(ch,sfd);
4918 	SFDParseMathValueRecord(sfd,&vertex->mkd[i].kern,&vertex->mkd[i].kern_adjusts);
4919     }
4920 }
4921 
SFDParseGradient(FILE * sfd,char * tok)4922 static struct gradient *SFDParseGradient(FILE *sfd,char *tok) {
4923     struct gradient *grad = chunkalloc(sizeof(struct gradient));
4924     int ch, i;
4925 
4926     getreal(sfd,&grad->start.x);
4927     while ( isspace(ch=nlgetc(sfd)));
4928     if ( ch!=';' ) ungetc(ch,sfd);
4929     getreal(sfd,&grad->start.y);
4930 
4931     getreal(sfd,&grad->stop.x);
4932     while ( isspace(ch=nlgetc(sfd)));
4933     if ( ch!=';' ) ungetc(ch,sfd);
4934     getreal(sfd,&grad->stop.y);
4935 
4936     getreal(sfd,&grad->radius);
4937 
4938     getname(sfd,tok);
4939     for ( i=0; spreads[i]!=NULL; ++i )
4940 	if ( strmatch(spreads[i],tok)==0 )
4941     break;
4942     if ( spreads[i]==NULL ) i=0;
4943     grad->sm = i;
4944 
4945     getint(sfd,&grad->stop_cnt);
4946     grad->grad_stops = calloc(grad->stop_cnt,sizeof(struct grad_stops));
4947     for ( i=0; i<grad->stop_cnt; ++i ) {
4948 	while ( isspace(ch=nlgetc(sfd)));
4949 	if ( ch!='{' ) ungetc(ch,sfd);
4950 	getreal( sfd, &grad->grad_stops[i].offset );
4951 	gethex( sfd, &grad->grad_stops[i].col );
4952 	getreal( sfd, &grad->grad_stops[i].opacity );
4953 	while ( isspace(ch=nlgetc(sfd)));
4954 	if ( ch!='}' ) ungetc(ch,sfd);
4955     }
4956 return( grad );
4957 }
4958 
SFDParsePattern(FILE * sfd,char * tok)4959 static struct pattern *SFDParsePattern(FILE *sfd,char *tok) {
4960     struct pattern *pat = chunkalloc(sizeof(struct pattern));
4961     int ch;
4962 
4963     getname(sfd,tok);
4964     pat->pattern = copy(tok);
4965 
4966     getreal(sfd,&pat->width);
4967     while ( isspace(ch=nlgetc(sfd)));
4968     if ( ch!=';' ) ungetc(ch,sfd);
4969     getreal(sfd,&pat->height);
4970 
4971     while ( isspace(ch=nlgetc(sfd)));
4972     if ( ch!='[' ) ungetc(ch,sfd);
4973     getreal(sfd,&pat->transform[0]);
4974     getreal(sfd,&pat->transform[1]);
4975     getreal(sfd,&pat->transform[2]);
4976     getreal(sfd,&pat->transform[3]);
4977     getreal(sfd,&pat->transform[4]);
4978     getreal(sfd,&pat->transform[5]);
4979     while ( isspace(ch=nlgetc(sfd)));
4980     if ( ch!=']' ) ungetc(ch,sfd);
4981 return( pat );
4982 }
4983 
4984 
SFDConsumeUntil(FILE * sfd,const char ** terminators)4985 static void SFDConsumeUntil( FILE *sfd, const char** terminators ) {
4986 
4987     char* line = 0;
4988     while((line = getquotedeol( sfd ))) {
4989         const char** tp = terminators;
4990         for( ; tp && *tp; ++tp ) {
4991             if( !strnmatch( line, *tp, strlen( *tp ))) {
4992                 free(line);
4993                 return;
4994             }
4995         }
4996         free(line);
4997     }
4998 }
4999 
5000 static int orig_pos;
5001 
SFDGetKerns(FILE * sfd,SplineChar * sc,char * ttok)5002 void SFDGetKerns( FILE *sfd, SplineChar *sc, char* ttok ) {
5003     struct splinefont * sf = sc->parent;
5004     char tok[2001], ch;
5005     uint32 script = 0;
5006     SplineFont *sli_sf = sf->cidmaster ? sf->cidmaster : sf;
5007 
5008     strncpy(tok,ttok,sizeof(tok)-1);
5009     tok[2000]=0;
5010 
5011     if( strmatch(tok,"Kerns2:")==0 ||
5012 	strmatch(tok,"VKerns2:")==0 ) {
5013 	    KernPair *kp, *last=NULL;
5014 	    int isv = *tok=='V';
5015 	    int off, index;
5016 	    struct lookup_subtable *sub;
5017 	    int kernCount = 0;
5018 	    if ( sf->sfd_version<2 )
5019 		LogError(_("Found an new style kerning pair inside a version 1 (or lower) sfd file.\n") );
5020 	    while ( fscanf(sfd,"%d %d", &index, &off )==2 ) {
5021 		sub = SFFindLookupSubtableAndFreeName(sf,SFDReadUTF7Str(sfd));
5022 		if ( sub==NULL ) {
5023 		    LogError(_("KernPair with no subtable name.\n"));
5024 	    	    break;
5025 		}
5026 		kernCount++;
5027 		kp = chunkalloc(sizeof(KernPair1));
5028 		kp->sc = (SplineChar *) (intpt) index;
5029 		kp->kcid = true;
5030 		kp->off = off;
5031 		kp->subtable = sub;
5032 		kp->next = NULL;
5033 		while ( (ch=nlgetc(sfd))==' ' );
5034 		ungetc(ch,sfd);
5035 		if ( ch=='{' ) {
5036 		    kp->adjust = SFDReadDeviceTable(sfd, NULL);
5037 		}
5038 		if ( last != NULL )
5039 		    last->next = kp;
5040 		else if ( isv )
5041 		    sc->vkerns = kp;
5042 		else
5043 		    sc->kerns = kp;
5044 		last = kp;
5045 	    }
5046 	    if( !kernCount ) {
5047 //		printf("SFDGetKerns() have a BLANK KERN\n");
5048 		sc->kerns = 0;
5049 	    }
5050     } else if ( strmatch(tok,"Kerns:")==0 ||
5051 		strmatch(tok,"KernsSLI:")==0 ||
5052 		strmatch(tok,"KernsSLIF:")==0 ||
5053 		strmatch(tok,"VKernsSLIF:")==0 ||
5054 		strmatch(tok,"KernsSLIFO:")==0 ||
5055 		strmatch(tok,"VKernsSLIFO:")==0 ) {
5056 	    KernPair1 *kp, *last=NULL;
5057 	    int index, off, sli, flags=0;
5058 	    int hassli = (strmatch(tok,"KernsSLI:")==0);
5059 	    int isv = *tok=='V';
5060 	    int has_orig = strstr(tok,"SLIFO:")!=NULL;
5061 	    if ( sf->sfd_version>=2 ) {
5062 		IError( "Found an old style kerning pair inside a version 2 (or higher) sfd file." );
5063 exit(1);
5064 	    }
5065 	    if ( strmatch(tok,"KernsSLIF:")==0 || strmatch(tok,"KernsSLIFO:")==0 ||
5066 		    strmatch(tok,"VKernsSLIF:")==0 || strmatch(tok,"VKernsSLIFO:")==0 )
5067 		hassli=2;
5068 	    while ( (hassli==1 && fscanf(sfd,"%d %d %d", &index, &off, &sli )==3) ||
5069 		    (hassli==2 && fscanf(sfd,"%d %d %d %d", &index, &off, &sli, &flags )==4) ||
5070 		    (hassli==0 && fscanf(sfd,"%d %d", &index, &off )==2) ) {
5071 		if ( !hassli )
5072 		    sli = SFFindBiggestScriptLangIndex(sli_sf,
5073 			    script!=0?script:SCScriptFromUnicode(sc),DEFAULT_LANG);
5074 		if ( sli>=((SplineFont1 *) sli_sf)->sli_cnt && sli!=SLI_NESTED) {
5075 		    static int complained=false;
5076 		    if ( !complained )
5077 			IError("'%s' in %s has a script index out of bounds: %d",
5078 				isv ? "vkrn" : "kern",
5079 				sc->name, sli );
5080 		    sli = SFFindBiggestScriptLangIndex(sli_sf,
5081 			    SCScriptFromUnicode(sc),DEFAULT_LANG);
5082 		    complained = true;
5083 		}
5084 		kp = chunkalloc(sizeof(KernPair1));
5085 		kp->kp.sc = (SplineChar *) (intpt) index;
5086 		kp->kp.kcid = has_orig;
5087 		kp->kp.off = off;
5088 		kp->sli = sli;
5089 		kp->flags = flags;
5090 		kp->kp.next = NULL;
5091 		while ( (ch=nlgetc(sfd))==' ' );
5092 		ungetc(ch,sfd);
5093 		if ( ch=='{' ) {
5094 		    kp->kp.adjust = SFDReadDeviceTable(sfd, NULL);
5095 		}
5096 		if ( last != NULL )
5097 		    last->kp.next = (KernPair *) kp;
5098 		else if ( isv )
5099 		    sc->vkerns = (KernPair *) kp;
5100 		else
5101 		    sc->kerns = (KernPair *) kp;
5102 		last = kp;
5103 	    }
5104     } else {
5105 	return;
5106     }
5107 
5108     // we matched something, grab the next top level token to ttok
5109     getname( sfd, ttok );
5110 }
5111 
5112 
SFDGetPSTs(FILE * sfd,SplineChar * sc,char * ttok)5113 void SFDGetPSTs( FILE *sfd, SplineChar *sc, char* ttok ) {
5114     struct splinefont * sf = sc->parent;
5115     char tok[2001], ch;
5116     int isliga = 0, ispos, issubs, ismult, islcar, ispair, temp;
5117     PST *last = NULL;
5118     uint32 script = 0;
5119     SplineFont *sli_sf = sf->cidmaster ? sf->cidmaster : sf;
5120 
5121     strncpy(tok,ttok,sizeof(tok)-1);
5122 
5123     if ( strmatch(tok,"Script:")==0 ) {
5124 	/* Obsolete. But still used for parsing obsolete ligature/subs tags */
5125 	while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
5126 	if ( ch=='\n' || ch=='\r' )
5127 	    script = 0;
5128 	else {
5129 	    ungetc(ch,sfd);
5130 	    script = gettag(sfd);
5131 	}
5132     } else if ( (ispos = (strmatch(tok,"Position:")==0)) ||
5133 		( ispos  = (strmatch(tok,"Position2:")==0)) ||
5134 		( ispair = (strmatch(tok,"PairPos:")==0)) ||
5135 		( ispair = (strmatch(tok,"PairPos2:")==0)) ||
5136 		( islcar = (strmatch(tok,"LCarets:")==0)) ||
5137 		( islcar = (strmatch(tok,"LCarets2:")==0)) ||
5138 		( isliga = (strmatch(tok,"Ligature:")==0)) ||
5139 		( isliga = (strmatch(tok,"Ligature2:")==0)) ||
5140 		( issubs = (strmatch(tok,"Substitution:")==0)) ||
5141 		( issubs = (strmatch(tok,"Substitution2:")==0)) ||
5142 		( ismult = (strmatch(tok,"MultipleSubs:")==0)) ||
5143 		( ismult = (strmatch(tok,"MultipleSubs2:")==0)) ||
5144 		strmatch(tok,"AlternateSubs:")==0 ||
5145 		strmatch(tok,"AlternateSubs2:")==0 ) {
5146 	    PST *pst;
5147 	    int old, type;
5148 	    type = ispos ? pst_position :
5149 			 ispair ? pst_pair :
5150 			 islcar ? pst_lcaret :
5151 			 isliga ? pst_ligature :
5152 			 issubs ? pst_substitution :
5153 			 ismult ? pst_multiple :
5154 			 pst_alternate;
5155 	    if ( strchr(tok,'2')!=NULL ) {
5156 		old = false;
5157 		pst = chunkalloc(sizeof(PST));
5158 		if ( type!=pst_lcaret )
5159 		    pst->subtable = SFFindLookupSubtableAndFreeName(sf,SFDReadUTF7Str(sfd));
5160 	    } else {
5161 		old = true;
5162 		pst = chunkalloc(sizeof(PST1));
5163 		((PST1 *) pst)->tag = CHR('l','i','g','a');
5164 		((PST1 *) pst)->script_lang_index = 0xffff;
5165 		while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
5166 		if ( isdigit(ch)) {
5167 		    int temp;
5168 		    ungetc(ch,sfd);
5169 		    getint(sfd,&temp);
5170 		    ((PST1 *) pst)->flags = temp;
5171 		    while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
5172 		} else
5173 		    ((PST1 *) pst)->flags = 0 /*PSTDefaultFlags(type,sc)*/;
5174 		if ( isdigit(ch)) {
5175 		    ungetc(ch,sfd);
5176 		    getusint(sfd,&((PST1 *) pst)->script_lang_index);
5177 		    while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
5178 		} else
5179 		    ((PST1 *) pst)->script_lang_index = SFFindBiggestScriptLangIndex(sf,
5180 			    script!=0?script:SCScriptFromUnicode(sc),DEFAULT_LANG);
5181 		if ( ch=='\'' ) {
5182 		    ungetc(ch,sfd);
5183 		    ((PST1 *) pst)->tag = gettag(sfd);
5184 		} else if ( ch=='<' ) {
5185 		    getint(sfd,&temp);
5186 		    ((PST1 *) pst)->tag = temp<<16;
5187 		    nlgetc(sfd);	/* comma */
5188 		    getint(sfd,&temp);
5189 		    ((PST1 *) pst)->tag |= temp;
5190 		    nlgetc(sfd);	/* close '>' */
5191 		    ((PST1 *) pst)->macfeature = true;
5192 		} else
5193 		    ungetc(ch,sfd);
5194 		if ( type==pst_lcaret ) {
5195 		/* These are meaningless for lcarets, set them to innocuous values */
5196 		    ((PST1 *) pst)->script_lang_index = SLI_UNKNOWN;
5197 		    ((PST1 *) pst)->tag = CHR(' ',' ',' ',' ');
5198 		} else if ( ((PST1 *) pst)->script_lang_index>=((SplineFont1 *) sli_sf)->sli_cnt && ((PST1 *) pst)->script_lang_index!=SLI_NESTED ) {
5199 		    static int complained=false;
5200 		    if ( !complained )
5201 			IError("'%c%c%c%c' in %s has a script index out of bounds: %d",
5202 				(((PST1 *) pst)->tag>>24), (((PST1 *) pst)->tag>>16)&0xff, (((PST1 *) pst)->tag>>8)&0xff, ((PST1 *) pst)->tag&0xff,
5203 				sc->name, ((PST1 *) pst)->script_lang_index );
5204 		    else
5205 			IError( "'%c%c%c%c' in %s has a script index out of bounds: %d\n",
5206 				(((PST1 *) pst)->tag>>24), (((PST1 *) pst)->tag>>16)&0xff, (((PST1 *) pst)->tag>>8)&0xff, ((PST1 *) pst)->tag&0xff,
5207 				sc->name, ((PST1 *) pst)->script_lang_index );
5208 		    ((PST1 *) pst)->script_lang_index = SFFindBiggestScriptLangIndex(sli_sf,
5209 			    SCScriptFromUnicode(sc),DEFAULT_LANG);
5210 		    complained = true;
5211 		}
5212 	    }
5213 	    if ( (sf->sfd_version<2)!=old ) {
5214 		IError( "Version mixup in PST of sfd file." );
5215 exit(1);
5216 	    }
5217 	    if ( last==NULL )
5218 		sc->possub = pst;
5219 	    else
5220 		last->next = pst;
5221 	    last = pst;
5222 	    pst->type = type;
5223 	    if ( pst->type==pst_position ) {
5224 		fscanf( sfd, " dx=%hd dy=%hd dh=%hd dv=%hd",
5225 			&pst->u.pos.xoff, &pst->u.pos.yoff,
5226 			&pst->u.pos.h_adv_off, &pst->u.pos.v_adv_off);
5227 		pst->u.pos.adjust = SFDReadValDevTab(sfd);
5228 		ch = nlgetc(sfd);		/* Eat new line */
5229 	    } else if ( pst->type==pst_pair ) {
5230 		getname(sfd,tok);
5231 		pst->u.pair.paired = copy(tok);
5232 		pst->u.pair.vr = chunkalloc(sizeof(struct vr [2]));
5233 		fscanf( sfd, " dx=%hd dy=%hd dh=%hd dv=%hd",
5234 			&pst->u.pair.vr[0].xoff, &pst->u.pair.vr[0].yoff,
5235 			&pst->u.pair.vr[0].h_adv_off, &pst->u.pair.vr[0].v_adv_off);
5236 		pst->u.pair.vr[0].adjust = SFDReadValDevTab(sfd);
5237 		fscanf( sfd, " dx=%hd dy=%hd dh=%hd dv=%hd",
5238 			&pst->u.pair.vr[1].xoff, &pst->u.pair.vr[1].yoff,
5239 			&pst->u.pair.vr[1].h_adv_off, &pst->u.pair.vr[1].v_adv_off);
5240 		pst->u.pair.vr[0].adjust = SFDReadValDevTab(sfd);
5241 		ch = nlgetc(sfd);
5242 	    } else if ( pst->type==pst_lcaret ) {
5243 		int i;
5244 		fscanf( sfd, " %d", &pst->u.lcaret.cnt );
5245 		pst->u.lcaret.carets = malloc(pst->u.lcaret.cnt*sizeof(int16));
5246 		for ( i=0; i<pst->u.lcaret.cnt; ++i )
5247 		    fscanf( sfd, " %hd", &pst->u.lcaret.carets[i]);
5248 		geteol(sfd,tok);
5249 	    } else {
5250 		geteol(sfd,tok);
5251 		pst->u.lig.components = copy(tok);	/* it's in the same place for all formats */
5252 		if ( isliga ) {
5253 		    pst->u.lig.lig = sc;
5254 		    if ( old )
5255 			last = (PST *) LigaCreateFromOldStyleMultiple((PST1 *) pst);
5256 		}
5257 	    }
5258 #ifdef FONTFORGE_CONFIG_CVT_OLD_MAC_FEATURES
5259 	    if ( old )
5260 		CvtOldMacFeature((PST1 *) pst);
5261 #endif
5262     } else {
5263 	return;
5264     }
5265 
5266     // we matched something, grab the next top level token to ttok
5267     getname( sfd, ttok );
5268 }
5269 
5270 
SFDMoveToNextStartChar(FILE * sfd)5271 char* SFDMoveToNextStartChar( FILE* sfd ) {
5272     char ret[2000];
5273 
5274     memset( ret, '\0', 2000 );
5275     char* line = 0;
5276     while((line = getquotedeol( sfd ))) {
5277 	if( !strnmatch( line, "StartChar:", strlen( "StartChar:" ))) {
5278 	    // FIXME: use the getname()/SFDReadUTF7Str() combo
5279 	    // from SFDGetChar
5280 	    int len = strlen("StartChar:");
5281 	    while( line[len] && line[len] == ' ' )
5282 		len++;
5283 	    strcpy( ret, line+len );
5284 	    free(line);
5285 	    return copy(ret);
5286 	}
5287 	free(line);
5288 	if(feof( sfd ))
5289 	    break;
5290 
5291     }
5292     return 0;
5293 }
5294 
SFDGetChar(FILE * sfd,SplineFont * sf,int had_sf_layer_cnt)5295 static SplineChar *SFDGetChar(FILE *sfd,SplineFont *sf, int had_sf_layer_cnt) {
5296     SplineChar *sc;
5297     char tok[2000], ch;
5298     RefChar *lastr=NULL, *ref;
5299     ImageList *lasti=NULL, *img;
5300     AnchorPoint *lastap = NULL;
5301     GuidelineSet *lastgl = NULL;
5302     int isliga = 0, ispos, issubs, ismult, islcar, ispair, temp, i;
5303     PST *last = NULL;
5304     uint32 script = 0;
5305     int current_layer = ly_fore;
5306     int multilayer = sf->multilayer;
5307     int had_old_dstems = false;
5308     SplineFont *sli_sf = sf->cidmaster ? sf->cidmaster : sf;
5309     struct altuni *altuni;
5310     int oldback = false;
5311 
5312     if ( getname(sfd,tok)!=1 )
5313 return( NULL );
5314     if ( strcmp(tok,"StartChar:")!=0 )
5315 return( NULL );
5316     while ( isspace(ch=nlgetc(sfd)));
5317     ungetc(ch,sfd);
5318     sc = SFSplineCharCreate(sf);
5319     if ( ch!='"' ) {
5320 	if ( getname(sfd,tok)!=1 ) {
5321 	    SplineCharFree(sc);
5322 return( NULL );
5323 	}
5324 	sc->name = copy(tok);
5325     } else {
5326 	sc->name = SFDReadUTF7Str(sfd);
5327 	if ( sc->name==NULL ) {
5328 	    SplineCharFree(sc);
5329 return( NULL );
5330 	}
5331     }
5332     sc->vwidth = sf->ascent+sf->descent;
5333     sc->parent = sf;
5334     while ( 1 ) {
5335 	if ( getname(sfd,tok)!=1 ) {
5336 	    SplineCharFree(sc);
5337 return( NULL );
5338 	}
5339 	if ( strmatch(tok,"Encoding:")==0 ) {
5340 	    int enc;
5341 	    getint(sfd,&enc);
5342 	    getint(sfd,&sc->unicodeenc);
5343 	    while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
5344 	    ungetc(ch,sfd);
5345 	    if ( ch!='\n' && ch!='\r' ) {
5346 		getint(sfd,&sc->orig_pos);
5347 		if ( sc->orig_pos==65535 )
5348 		    sc->orig_pos = orig_pos++;
5349 		    /* An old mark meaning: "I don't know" */
5350 		if ( sc->orig_pos<sf->glyphcnt && sf->glyphs[sc->orig_pos]!=NULL )
5351 		    sc->orig_pos = sf->glyphcnt;
5352 		if ( sc->orig_pos>=sf->glyphcnt ) {
5353 		    if ( sc->orig_pos>=sf->glyphmax )
5354 			sf->glyphs = realloc(sf->glyphs,(sf->glyphmax = sc->orig_pos+10)*sizeof(SplineChar *));
5355 		    memset(sf->glyphs+sf->glyphcnt,0,(sc->orig_pos+1-sf->glyphcnt)*sizeof(SplineChar *));
5356 		    sf->glyphcnt = sc->orig_pos+1;
5357 		}
5358 		if ( sc->orig_pos+1 > orig_pos )
5359 		    orig_pos = sc->orig_pos+1;
5360 	    } else if ( sf->cidmaster!=NULL ) {		/* In cid fonts the orig_pos is just the cid */
5361 		sc->orig_pos = enc;
5362 	    } else {
5363 		sc->orig_pos = orig_pos++;
5364 	    }
5365 	    SFDSetEncMap(sf,sc->orig_pos,enc);
5366 	} else if ( strmatch(tok,"AltUni:")==0 ) {
5367 	    int uni;
5368 	    while ( getint(sfd,&uni)==1 ) {
5369 		altuni = chunkalloc(sizeof(struct altuni));
5370 		altuni->unienc = uni;
5371 		altuni->vs = -1;
5372 		altuni->fid = 0;
5373 		altuni->next = sc->altuni;
5374 		sc->altuni = altuni;
5375 	    }
5376 	} else if ( strmatch(tok,"AltUni2:")==0 ) {
5377 	    uint32 uni[3];
5378 	    while ( gethexints(sfd,uni,3) ) {
5379 		altuni = chunkalloc(sizeof(struct altuni));
5380 		altuni->unienc = uni[0];
5381 		altuni->vs = uni[1];
5382 		altuni->fid = uni[2];
5383 		altuni->next = sc->altuni;
5384 		sc->altuni = altuni;
5385 	    }
5386 	} else if ( strmatch(tok,"OldEncoding:")==0 ) {
5387 	    int old_enc;		/* Obsolete info */
5388 	    getint(sfd,&old_enc);
5389         } else if ( strmatch(tok,"Script:")==0 ) {
5390 	    /* Obsolete. But still used for parsing obsolete ligature/subs tags */
5391             while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
5392             if ( ch=='\n' || ch=='\r' )
5393                 script = 0;
5394             else {
5395 		ungetc(ch,sfd);
5396 		script = gettag(sfd);
5397             }
5398 	} else if ( strmatch(tok,"GlifName:")==0 ) {
5399             while ( isspace(ch=nlgetc(sfd)));
5400             ungetc(ch,sfd);
5401             if ( ch!='"' ) {
5402               if ( getname(sfd,tok)!=1 ) {
5403                 LogError(_("Invalid glif name.\n"));
5404               }
5405 	      sc->glif_name = copy(tok);
5406             } else {
5407 	      sc->glif_name = SFDReadUTF7Str(sfd);
5408 	      if ( sc->glif_name==NULL ) {
5409                 LogError(_("Invalid glif name.\n"));
5410 	      }
5411             }
5412 	} else if ( strmatch(tok,"Width:")==0 ) {
5413 	    getsint(sfd,&sc->width);
5414 	} else if ( strmatch(tok,"VWidth:")==0 ) {
5415 	    getsint(sfd,&sc->vwidth);
5416 	} else if ( strmatch(tok,"GlyphClass:")==0 ) {
5417 	    getint(sfd,&temp);
5418 	    sc->glyph_class = temp;
5419 	} else if ( strmatch(tok,"UnlinkRmOvrlpSave:")==0 ) {
5420 	    getint(sfd,&temp);
5421 	    sc->unlink_rm_ovrlp_save_undo = temp;
5422 	} else if ( strmatch(tok,"InSpiro:")==0 ) {
5423 	    getint(sfd,&temp);
5424 	    sc->inspiro = temp;
5425 	} else if ( strmatch(tok,"LigCaretCntFixed:")==0 ) {
5426 	    getint(sfd,&temp);
5427 	    sc->lig_caret_cnt_fixed = temp;
5428 	} else if ( strmatch(tok,"Flags:")==0 ) {
5429 	    while ( isspace(ch=nlgetc(sfd)) && ch!='\n' && ch!='\r');
5430 	    while ( ch!='\n' && ch!='\r' ) {
5431 		if ( ch=='H' ) sc->changedsincelasthinted=true;
5432 		else if ( ch=='M' ) sc->manualhints = true;
5433 		else if ( ch=='W' ) sc->widthset = true;
5434 		else if ( ch=='O' ) sc->wasopen = true;
5435 		else if ( ch=='I' ) sc->instructions_out_of_date = true;
5436 		ch = nlgetc(sfd);
5437 	    }
5438 	    if ( sf->multilayer || sf->onlybitmaps || sf->strokedfont || sc->layers[ly_fore].order2 )
5439 		sc->changedsincelasthinted = false;
5440 	} else if ( strmatch(tok,"TeX:")==0 ) {
5441 	    getsint(sfd,&sc->tex_height);
5442 	    getsint(sfd,&sc->tex_depth);
5443 	    while ( isspace(ch=nlgetc(sfd)) && ch!='\n' && ch!='\r');
5444 	    ungetc(ch,sfd);
5445 	    if ( ch!='\n' && ch!='\r' ) {
5446 		int16 old_tex;
5447 		/* Used to store two extra values here */
5448 		getsint(sfd,&old_tex);
5449 		getsint(sfd,&old_tex);
5450 		if ( sc->tex_height==0 && sc->tex_depth==0 )		/* Fixup old bug */
5451 		    sc->tex_height = sc->tex_depth = TEX_UNDEF;
5452 	    }
5453 	} else if ( strmatch(tok,"ItalicCorrection:")==0 ) {
5454 	    SFDParseMathValueRecord(sfd,&sc->italic_correction,&sc->italic_adjusts);
5455 	} else if ( strmatch(tok,"TopAccentHorizontal:")==0 ) {
5456 	    SFDParseMathValueRecord(sfd,&sc->top_accent_horiz,&sc->top_accent_adjusts);
5457 	} else if ( strmatch(tok,"GlyphCompositionVerticalIC:")==0 ) {
5458 	    if ( sc->vert_variants==NULL )
5459 		sc->vert_variants = chunkalloc(sizeof(struct glyphvariants));
5460 	    SFDParseMathValueRecord(sfd,&sc->vert_variants->italic_correction,&sc->vert_variants->italic_adjusts);
5461 	} else if ( strmatch(tok,"GlyphCompositionHorizontalIC:")==0 ) {
5462 	    if ( sc->horiz_variants==NULL )
5463 		sc->horiz_variants = chunkalloc(sizeof(struct glyphvariants));
5464 	    SFDParseMathValueRecord(sfd,&sc->horiz_variants->italic_correction,&sc->horiz_variants->italic_adjusts);
5465 	} else if ( strmatch(tok,"IsExtendedShape:")==0 ) {
5466 	    int temp;
5467 	    getint(sfd,&temp);
5468 	    sc->is_extended_shape = temp;
5469 	} else if ( strmatch(tok,"GlyphVariantsVertical:")==0 ) {
5470 	    if ( sc->vert_variants==NULL )
5471 		sc->vert_variants = chunkalloc(sizeof(struct glyphvariants));
5472 	    geteol(sfd,tok);
5473 	    sc->vert_variants->variants = copy(tok);
5474 	} else if ( strmatch(tok,"GlyphVariantsHorizontal:")==0 ) {
5475 	    if ( sc->horiz_variants==NULL )
5476 		sc->horiz_variants = chunkalloc(sizeof(struct glyphvariants));
5477 	    geteol(sfd,tok);
5478 	    sc->horiz_variants->variants = copy(tok);
5479 	} else if ( strmatch(tok,"GlyphCompositionVertical:")==0 ) {
5480 	    sc->vert_variants = SFDParseGlyphComposition(sfd, sc->vert_variants,tok);
5481 	} else if ( strmatch(tok,"GlyphCompositionHorizontal:")==0 ) {
5482 	    sc->horiz_variants = SFDParseGlyphComposition(sfd, sc->horiz_variants,tok);
5483 	} else if ( strmatch(tok,"TopRightVertex:")==0 ) {
5484 	    if ( sc->mathkern==NULL )
5485 		sc->mathkern = chunkalloc(sizeof(struct mathkern));
5486 	    SFDParseVertexKern(sfd, &sc->mathkern->top_right);
5487 	} else if ( strmatch(tok,"TopLeftVertex:")==0 ) {
5488 	    if ( sc->mathkern==NULL )
5489 		sc->mathkern = chunkalloc(sizeof(struct mathkern));
5490 	    SFDParseVertexKern(sfd, &sc->mathkern->top_left);
5491 	} else if ( strmatch(tok,"BottomRightVertex:")==0 ) {
5492 	    if ( sc->mathkern==NULL )
5493 		sc->mathkern = chunkalloc(sizeof(struct mathkern));
5494 	    SFDParseVertexKern(sfd, &sc->mathkern->bottom_right);
5495 	} else if ( strmatch(tok,"BottomLeftVertex:")==0 ) {
5496 	    if ( sc->mathkern==NULL )
5497 		sc->mathkern = chunkalloc(sizeof(struct mathkern));
5498 	    SFDParseVertexKern(sfd, &sc->mathkern->bottom_left);
5499 #if HANYANG
5500 	} else if ( strmatch(tok,"CompositionUnit:")==0 ) {
5501 	    getsint(sfd,&sc->jamo);
5502 	    getsint(sfd,&sc->varient);
5503 	    sc->compositionunit = true;
5504 #endif
5505 	} else if ( strmatch(tok,"HStem:")==0 ) {
5506 	    sc->hstem = SFDReadHints(sfd);
5507 	    sc->hconflicts = StemListAnyConflicts(sc->hstem);
5508 	} else if ( strmatch(tok,"VStem:")==0 ) {
5509 	    sc->vstem = SFDReadHints(sfd);
5510 	    sc->vconflicts = StemListAnyConflicts(sc->vstem);
5511 	} else if ( strmatch(tok,"DStem:")==0 ) {
5512 	    sc->dstem = SFDReadDHints( sc->parent,sfd,true );
5513             had_old_dstems = true;
5514 	} else if ( strmatch(tok,"DStem2:")==0 ) {
5515 	    sc->dstem = SFDReadDHints( sc->parent,sfd,false );
5516 	} else if ( strmatch(tok,"CounterMasks:")==0 ) {
5517 	    getsint(sfd,&sc->countermask_cnt);
5518 	    sc->countermasks = calloc(sc->countermask_cnt,sizeof(HintMask));
5519 	    for ( i=0; i<sc->countermask_cnt; ++i ) {
5520 		int ch;
5521 		while ( (ch=nlgetc(sfd))==' ' );
5522 		ungetc(ch,sfd);
5523 		SFDGetHintMask(sfd,&sc->countermasks[i]);
5524 	    }
5525 	} else if ( strmatch(tok,"AnchorPoint:")==0 ) {
5526 	    lastap = SFDReadAnchorPoints(sfd,sc,&sc->anchor,lastap);
5527 	} else if ( strmatch(tok,"Fore")==0 ) {
5528 	    while ( isspace(ch = nlgetc(sfd)));
5529 	    ungetc(ch,sfd);
5530 	    if ( ch!='I' && ch!='R' && ch!='S' && ch!='V' && ch!=' ' && ch!='\n' &&
5531 	         !PeekMatch(sfd, "Pickled") && !PeekMatch(sfd, "EndChar") &&
5532 	         !PeekMatch(sfd, "Fore") && !PeekMatch(sfd, "Back") && !PeekMatch(sfd, "Layer") ) {
5533 		/* Old format, without a SplineSet token */
5534 		sc->layers[ly_fore].splines = SFDGetSplineSet(sfd,sc->layers[ly_fore].order2);
5535 	    }
5536 	    current_layer = ly_fore;
5537 	    lastgl = NULL;
5538 	} else if ( strmatch(tok,"MinimumDistance:")==0 ) {
5539 	    SFDGetMinimumDistances(sfd,sc);
5540 	} else if ( strmatch(tok,"Validated:")==0 ) {
5541 	    getsint(sfd,(int16 *) &sc->layers[current_layer].validation_state);
5542 	} else if ( strmatch(tok,"Back")==0 ) {
5543 	    while ( isspace(ch=nlgetc(sfd)));
5544 	    ungetc(ch,sfd);
5545 	    if ( ch!='I' && ch!='R' && ch!='S' && ch!='V' && ch!=' ' && ch!='\n' &&
5546 	         !PeekMatch(sfd, "Pickled") && !PeekMatch(sfd, "EndChar") &&
5547 	         !PeekMatch(sfd, "Fore") && !PeekMatch(sfd, "Back") && !PeekMatch(sfd, "Layer") ) {
5548 		/* Old format, without a SplineSet token */
5549 		sc->layers[ly_back].splines = SFDGetSplineSet(sfd,sc->layers[ly_back].order2);
5550 		oldback = true;
5551 	    }
5552 	    current_layer = ly_back;
5553 	    lastgl = NULL;
5554 	} else if ( strmatch(tok,"LayerCount:")==0 ) {
5555 	    getint(sfd,&temp);
5556 	    if ( temp>sc->layer_cnt ) {
5557 		sc->layers = realloc(sc->layers,temp*sizeof(Layer));
5558 		memset(sc->layers+sc->layer_cnt,0,(temp-sc->layer_cnt)*sizeof(Layer));
5559 	    }
5560 	    sc->layer_cnt = temp;
5561 	    current_layer = ly_fore;
5562 	} else if ( strmatch(tok,"Layer:")==0 ) {
5563 	    int layer;
5564 	    int dofill, dostroke, fillfirst, linejoin, linecap;
5565 	    uint32 fillcol, strokecol;
5566 	    real fillopacity, strokeopacity, strokewidth, trans[4];
5567 	    DashType dashes[DASH_MAX];
5568 	    int i;
5569 	    getint(sfd,&layer);
5570 	    if ( layer>=sc->layer_cnt ) {
5571 		sc->layers = realloc(sc->layers,(layer+1)*sizeof(Layer));
5572 		memset(sc->layers+sc->layer_cnt,0,(layer+1-sc->layer_cnt)*sizeof(Layer));
5573 	    }
5574 	    if ( sc->parent->multilayer ) {
5575 		getint(sfd,&dofill);
5576 		getint(sfd,&dostroke);
5577 		getint(sfd,&fillfirst);
5578 		gethex(sfd,&fillcol);
5579 		getreal(sfd,&fillopacity);
5580 		gethex(sfd,&strokecol);
5581 		getreal(sfd,&strokeopacity);
5582 		getreal(sfd,&strokewidth);
5583 		getname(sfd,tok);
5584 		for ( i=0; joins[i]!=NULL; ++i )
5585 		    if ( strmatch(joins[i],tok)==0 )
5586 		break;
5587 		if ( joins[i]==NULL ) --i;
5588 		linejoin = i;
5589 		getname(sfd,tok);
5590 		for ( i=0; caps[i]!=NULL; ++i )
5591 		    if ( strmatch(caps[i],tok)==0 )
5592 		break;
5593 		if ( caps[i]==NULL ) --i;
5594 		linecap = i;
5595 		while ( (ch=nlgetc(sfd))==' ' || ch=='[' );
5596 		ungetc(ch,sfd);
5597 		getreal(sfd,&trans[0]);
5598 		getreal(sfd,&trans[1]);
5599 		getreal(sfd,&trans[2]);
5600 		getreal(sfd,&trans[3]);
5601 		while ( (ch=nlgetc(sfd))==' ' || ch==']' );
5602 		if ( ch=='[' ) {
5603 		    for ( i=0;; ++i ) { int temp;
5604 			if ( !getint(sfd,&temp) )
5605 		    break;
5606 			else if ( i<DASH_MAX )
5607 			    dashes[i] = temp;
5608 		    }
5609 		    if ( i<DASH_MAX )
5610 			dashes[i] = 0;
5611 		} else {
5612 		    ungetc(ch,sfd);
5613 		    memset(dashes,0,sizeof(dashes));
5614 		}
5615 		sc->layers[layer].dofill = dofill;
5616 		sc->layers[layer].dostroke = dostroke;
5617 		sc->layers[layer].fillfirst = fillfirst;
5618 		sc->layers[layer].fill_brush.col = fillcol;
5619 		sc->layers[layer].fill_brush.opacity = fillopacity;
5620 		sc->layers[layer].stroke_pen.brush.col = strokecol;
5621 		sc->layers[layer].stroke_pen.brush.opacity = strokeopacity;
5622 		sc->layers[layer].stroke_pen.width = strokewidth;
5623 		sc->layers[layer].stroke_pen.linejoin = linejoin;
5624 		sc->layers[layer].stroke_pen.linecap = linecap;
5625 		memcpy(sc->layers[layer].stroke_pen.dashes,dashes,sizeof(dashes));
5626 		memcpy(sc->layers[layer].stroke_pen.trans,trans,sizeof(trans));
5627 	    }
5628 	    current_layer = layer;
5629 	    lasti = NULL;
5630 	    lastr = NULL;
5631 	    lastgl = NULL;
5632 	} else if ( strmatch(tok,"FillGradient:")==0 ) {
5633 	    sc->layers[current_layer].fill_brush.gradient = SFDParseGradient(sfd,tok);
5634 	} else if ( strmatch(tok,"FillPattern:")==0 ) {
5635 	    sc->layers[current_layer].fill_brush.pattern = SFDParsePattern(sfd,tok);
5636 	} else if ( strmatch(tok,"StrokeGradient:")==0 ) {
5637 	    sc->layers[current_layer].stroke_pen.brush.gradient = SFDParseGradient(sfd,tok);
5638 	} else if ( strmatch(tok,"StrokePattern:")==0 ) {
5639 	    sc->layers[current_layer].stroke_pen.brush.pattern = SFDParsePattern(sfd,tok);
5640 	} else if ( strmatch(tok,"UndoRedoHistory")==0 ) {
5641 
5642 	    getname(sfd,tok);
5643 	    if ( !strmatch(tok,"Layer:") ) {
5644 		int layer;
5645 		getint(sfd,&layer);
5646 	    }
5647 
5648 	    int limit;
5649 	    Undoes *undo = 0;
5650 	    struct undoes *last = 0;
5651 
5652 	    getname(sfd,tok);
5653 	    if ( !strmatch(tok,"Undoes") ) {
5654 		undo = 0;
5655 		limit = UndoRedoLimitToLoad;
5656 		last = sc->layers[current_layer].undoes;
5657 		while((undo = SFDGetUndo( sfd, sc, "UndoOperation", current_layer )))
5658 		{
5659 		    // push to back
5660 		    if( last ) last->next = undo;
5661 		    else       sc->layers[current_layer].undoes = undo;
5662 		    last = undo;
5663 
5664 		    if( limit != -1 ) {
5665 			limit--;
5666 			if( limit <= 0 ) {
5667 			    // we have hit our load limit, so lets just chuck everything away
5668 			    // until we hit the EndUndoes/EndRedoes magic line and then start
5669 			    // actually processing again.
5670 			    const char* terminators[] = { "EndUndoes", "EndRedoes", 0 };
5671 			    SFDConsumeUntil( sfd, terminators );
5672 			}
5673 		    }
5674 		}
5675 	    }
5676 	    getname(sfd,tok);
5677 	    if ( !strmatch(tok,"Redoes") ) {
5678 		undo = 0;
5679 		limit = UndoRedoLimitToLoad;
5680 		last = sc->layers[current_layer].redoes;
5681 		while((undo = SFDGetUndo( sfd, sc, "RedoOperation", current_layer )))
5682 		{
5683 		    // push to back
5684 		    if( last ) last->next = undo;
5685 		    else       sc->layers[current_layer].redoes = undo;
5686 		    last = undo;
5687 
5688 		    if( limit != -1 ) {
5689 			limit--;
5690 			if( limit <= 0 ) {
5691 			    // we have hit our load limit, so lets just chuck everything away
5692 			    // until we hit the EndUndoes/EndRedoes magic line and then start
5693 			    // actually processing again.
5694 			    const char* terminators[] = { "EndUndoes", "EndRedoes", 0 };
5695 			    SFDConsumeUntil( sfd, terminators );
5696 			}
5697 		    }
5698 		}
5699 	    }
5700 	} else if ( strmatch(tok,"SplineSet")==0 ) {
5701 	    sc->layers[current_layer].splines = SFDGetSplineSet(sfd,sc->layers[current_layer].order2);
5702 	} else if ( strmatch(tok,"Guideline:")==0 ) {
5703 	    lastgl = SFDReadGuideline(sfd, &sc->layers[current_layer].guidelines, lastgl);
5704 	} else if ( strmatch(tok,"Ref:")==0 || strmatch(tok,"Refer:")==0 ) {
5705 	    /* I should be depending on the version number here, but I made */
5706 	    /*  a mistake and bumped the version too late. So the version is */
5707 	    /*  not an accurate mark, but the presence of a LayerCount keyword*/
5708 	    /*  in the font is an good mark. Before the LayerCount was added */
5709 	    /*  (version 2) only the foreground layer could have references */
5710 	    /*  after that (eventually version 3) any layer could. */
5711 	    if ( oldback || !had_sf_layer_cnt ) current_layer = ly_fore;
5712 	    ref = SFDGetRef(sfd,strmatch(tok,"Ref:")==0);
5713 	    if ( sc->layers[current_layer].refs==NULL )
5714 		sc->layers[current_layer].refs = ref;
5715 	    else
5716 		lastr->next = ref;
5717 	    lastr = ref;
5718 	} else if ( strmatch(tok,"Image:")==0 ) {
5719 	    int ly = current_layer;
5720 	    if ( !multilayer && !sc->layers[ly].background ) ly = ly_back;
5721 	    img = SFDGetImage(sfd);
5722 	    if (img != NULL) {
5723 	    if ( sc->layers[ly].images==NULL )
5724 		sc->layers[ly].images = img;
5725 	    else
5726 		lasti->next = img;
5727 	    lasti = img;
5728 	    }
5729 	} else if ( strmatch(tok,"Image2:")==0 ) {
5730 #ifndef _NO_LIBPNG
5731 	    enum MIME mime = SFDGetImage2MIME(sfd);
5732 	    if (mime == PNG) {
5733 		int ly = current_layer;
5734 		if ( !multilayer && !sc->layers[ly].background ) ly = ly_back;
5735 		img = SFDGetImagePNG(sfd);
5736 		if (img != NULL) {
5737 		    if ( sc->layers[ly].images==NULL )
5738 			sc->layers[ly].images = img;
5739 		    else
5740 			lasti->next = img;
5741 		    lasti = img;
5742 		}
5743 	    } else
5744 #endif
5745 	    {
5746 	    LogError(_("Image2 skipped as it uses an unsupported image type"));
5747 	    const char* im2_terminator[] = { "EndImage2", 0 };
5748 	    SFDConsumeUntil(sfd, im2_terminator);
5749 	    }
5750 	} else if ( strmatch(tok,"PickledData:")==0 ) {
5751 	    if (current_layer < sc->layer_cnt) {
5752 	      sc->layers[current_layer].python_persistent = SFDUnPickle(sfd, 0);
5753 	      sc->layers[current_layer].python_persistent_has_lists = 0;
5754 	    }
5755 	} else if ( strmatch(tok,"PickledDataWithLists:")==0 ) {
5756 	    if (current_layer < sc->layer_cnt) {
5757 	      sc->layers[current_layer].python_persistent = SFDUnPickle(sfd, 1);
5758 	      sc->layers[current_layer].python_persistent_has_lists = 1;
5759 	    }
5760 	} else if ( strmatch(tok,"OrigType1:")==0 ) {	/* Accept, slurp, ignore contents */
5761 	    SFDGetType1(sfd);
5762 	} else if ( strmatch(tok,"TtfInstrs:")==0 ) {	/* Binary format */
5763 	    SFDGetTtfInstrs(sfd,sc);
5764 	} else if ( strmatch(tok,"TtInstrs:")==0 ) {	/* ASCII format */
5765 	    SFDGetTtInstrs(sfd,sc);
5766 	} else if ( strmatch(tok,"Kerns2:")==0 ||
5767 		strmatch(tok,"VKerns2:")==0 ) {
5768 	    KernPair *kp, *last=NULL;
5769 	    int isv = *tok=='V';
5770 	    int off, index;
5771 	    struct lookup_subtable *sub;
5772 
5773 	    if ( sf->sfd_version<2 )
5774 		LogError(_("Found an new style kerning pair inside a version 1 (or lower) sfd file.\n") );
5775 	    while ( fscanf(sfd,"%d %d", &index, &off )==2 ) {
5776 		sub = SFFindLookupSubtableAndFreeName(sf,SFDReadUTF7Str(sfd));
5777 		if ( sub==NULL ) {
5778 		    LogError(_("KernPair with no subtable name.\n"));
5779 	    break;
5780 		}
5781 		kp = chunkalloc(sizeof(KernPair1));
5782 		kp->sc = (SplineChar *) (intpt) index;
5783 		kp->kcid = true;
5784 		kp->off = off;
5785 		kp->subtable = sub;
5786 		kp->next = NULL;
5787 		while ( (ch=nlgetc(sfd))==' ' );
5788 		ungetc(ch,sfd);
5789 		if ( ch=='{' ) {
5790 		    kp->adjust = SFDReadDeviceTable(sfd, NULL);
5791 		}
5792 		if ( last != NULL )
5793 		    last->next = kp;
5794 		else if ( isv )
5795 		    sc->vkerns = kp;
5796 		else
5797 		    sc->kerns = kp;
5798 		last = kp;
5799 	    }
5800 	} else if ( strmatch(tok,"Kerns:")==0 ||
5801 		strmatch(tok,"KernsSLI:")==0 ||
5802 		strmatch(tok,"KernsSLIF:")==0 ||
5803 		strmatch(tok,"VKernsSLIF:")==0 ||
5804 		strmatch(tok,"KernsSLIFO:")==0 ||
5805 		strmatch(tok,"VKernsSLIFO:")==0 ) {
5806 	    KernPair1 *kp, *last=NULL;
5807 	    int index, off, sli, flags=0;
5808 	    int hassli = (strmatch(tok,"KernsSLI:")==0);
5809 	    int isv = *tok=='V';
5810 	    int has_orig = strstr(tok,"SLIFO:")!=NULL;
5811 	    if ( sf->sfd_version>=2 ) {
5812 		IError( "Found an old style kerning pair inside a version 2 (or higher) sfd file." );
5813 exit(1);
5814 	    }
5815 	    if ( strmatch(tok,"KernsSLIF:")==0 || strmatch(tok,"KernsSLIFO:")==0 ||
5816 		    strmatch(tok,"VKernsSLIF:")==0 || strmatch(tok,"VKernsSLIFO:")==0 )
5817 		hassli=2;
5818 	    while ( (hassli==1 && fscanf(sfd,"%d %d %d", &index, &off, &sli )==3) ||
5819 		    (hassli==2 && fscanf(sfd,"%d %d %d %d", &index, &off, &sli, &flags )==4) ||
5820 		    (hassli==0 && fscanf(sfd,"%d %d", &index, &off )==2) ) {
5821 		if ( !hassli )
5822 		    sli = SFFindBiggestScriptLangIndex(sli_sf,
5823 			    script!=0?script:SCScriptFromUnicode(sc),DEFAULT_LANG);
5824 		if ( sli>=((SplineFont1 *) sli_sf)->sli_cnt && sli!=SLI_NESTED) {
5825 		    static int complained=false;
5826 		    if ( !complained )
5827 			IError("'%s' in %s has a script index out of bounds: %d",
5828 				isv ? "vkrn" : "kern",
5829 				sc->name, sli );
5830 		    sli = SFFindBiggestScriptLangIndex(sli_sf,
5831 			    SCScriptFromUnicode(sc),DEFAULT_LANG);
5832 		    complained = true;
5833 		}
5834 		kp = chunkalloc(sizeof(KernPair1));
5835 		kp->kp.sc = (SplineChar *) (intpt) index;
5836 		kp->kp.kcid = has_orig;
5837 		kp->kp.off = off;
5838 		kp->sli = sli;
5839 		kp->flags = flags;
5840 		kp->kp.next = NULL;
5841 		while ( (ch=nlgetc(sfd))==' ' );
5842 		ungetc(ch,sfd);
5843 		if ( ch=='{' ) {
5844 		    kp->kp.adjust = SFDReadDeviceTable(sfd, NULL);
5845 		}
5846 		if ( last != NULL )
5847 		    last->kp.next = (KernPair *) kp;
5848 		else if ( isv )
5849 		    sc->vkerns = (KernPair *) kp;
5850 		else
5851 		    sc->kerns = (KernPair *) kp;
5852 		last = kp;
5853 	    }
5854 	} else if ( (ispos = (strmatch(tok,"Position:")==0)) ||
5855 		( ispos  = (strmatch(tok,"Position2:")==0)) ||
5856 		( ispair = (strmatch(tok,"PairPos:")==0)) ||
5857 		( ispair = (strmatch(tok,"PairPos2:")==0)) ||
5858 		( islcar = (strmatch(tok,"LCarets:")==0)) ||
5859 		( islcar = (strmatch(tok,"LCarets2:")==0)) ||
5860 		( isliga = (strmatch(tok,"Ligature:")==0)) ||
5861 		( isliga = (strmatch(tok,"Ligature2:")==0)) ||
5862 		( issubs = (strmatch(tok,"Substitution:")==0)) ||
5863 		( issubs = (strmatch(tok,"Substitution2:")==0)) ||
5864 		( ismult = (strmatch(tok,"MultipleSubs:")==0)) ||
5865 		( ismult = (strmatch(tok,"MultipleSubs2:")==0)) ||
5866 		strmatch(tok,"AlternateSubs:")==0 ||
5867 		strmatch(tok,"AlternateSubs2:")==0 ) {
5868 	    PST *pst;
5869 	    int old, type;
5870 	    type = ispos ? pst_position :
5871 			 ispair ? pst_pair :
5872 			 islcar ? pst_lcaret :
5873 			 isliga ? pst_ligature :
5874 			 issubs ? pst_substitution :
5875 			 ismult ? pst_multiple :
5876 			 pst_alternate;
5877 	    if ( strchr(tok,'2')!=NULL ) {
5878 		old = false;
5879 		pst = chunkalloc(sizeof(PST));
5880 		if ( type!=pst_lcaret )
5881 		    pst->subtable = SFFindLookupSubtableAndFreeName(sf,SFDReadUTF7Str(sfd));
5882 	    } else {
5883 		old = true;
5884 		pst = chunkalloc(sizeof(PST1));
5885 		((PST1 *) pst)->tag = CHR('l','i','g','a');
5886 		((PST1 *) pst)->script_lang_index = 0xffff;
5887 		while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
5888 		if ( isdigit(ch)) {
5889 		    int temp;
5890 		    ungetc(ch,sfd);
5891 		    getint(sfd,&temp);
5892 		    ((PST1 *) pst)->flags = temp;
5893 		    while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
5894 		} else
5895 		    ((PST1 *) pst)->flags = 0 /*PSTDefaultFlags(type,sc)*/;
5896 		if ( isdigit(ch)) {
5897 		    ungetc(ch,sfd);
5898 		    getusint(sfd,&((PST1 *) pst)->script_lang_index);
5899 		    while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
5900 		} else
5901 		    ((PST1 *) pst)->script_lang_index = SFFindBiggestScriptLangIndex(sf,
5902 			    script!=0?script:SCScriptFromUnicode(sc),DEFAULT_LANG);
5903 		if ( ch=='\'' ) {
5904 		    ungetc(ch,sfd);
5905 		    ((PST1 *) pst)->tag = gettag(sfd);
5906 		} else if ( ch=='<' ) {
5907 		    getint(sfd,&temp);
5908 		    ((PST1 *) pst)->tag = temp<<16;
5909 		    nlgetc(sfd);	/* comma */
5910 		    getint(sfd,&temp);
5911 		    ((PST1 *) pst)->tag |= temp;
5912 		    nlgetc(sfd);	/* close '>' */
5913 		    ((PST1 *) pst)->macfeature = true;
5914 		} else
5915 		    ungetc(ch,sfd);
5916 		if ( type==pst_lcaret ) {
5917 		/* These are meaningless for lcarets, set them to innocuous values */
5918 		    ((PST1 *) pst)->script_lang_index = SLI_UNKNOWN;
5919 		    ((PST1 *) pst)->tag = CHR(' ',' ',' ',' ');
5920 		} else if ( ((PST1 *) pst)->script_lang_index>=((SplineFont1 *) sli_sf)->sli_cnt && ((PST1 *) pst)->script_lang_index!=SLI_NESTED ) {
5921 		    static int complained=false;
5922 		    if ( !complained )
5923 			IError("'%c%c%c%c' in %s has a script index out of bounds: %d",
5924 				(((PST1 *) pst)->tag>>24), (((PST1 *) pst)->tag>>16)&0xff, (((PST1 *) pst)->tag>>8)&0xff, ((PST1 *) pst)->tag&0xff,
5925 				sc->name, ((PST1 *) pst)->script_lang_index );
5926 		    else
5927 			IError( "'%c%c%c%c' in %s has a script index out of bounds: %d\n",
5928 				(((PST1 *) pst)->tag>>24), (((PST1 *) pst)->tag>>16)&0xff, (((PST1 *) pst)->tag>>8)&0xff, ((PST1 *) pst)->tag&0xff,
5929 				sc->name, ((PST1 *) pst)->script_lang_index );
5930 		    ((PST1 *) pst)->script_lang_index = SFFindBiggestScriptLangIndex(sli_sf,
5931 			    SCScriptFromUnicode(sc),DEFAULT_LANG);
5932 		    complained = true;
5933 		}
5934 	    }
5935 	    if ( (sf->sfd_version<2)!=old ) {
5936 		IError( "Version mixup in PST of sfd file." );
5937 exit(1);
5938 	    }
5939 	    if ( last==NULL )
5940 		sc->possub = pst;
5941 	    else
5942 		last->next = pst;
5943 	    last = pst;
5944 	    pst->type = type;
5945 	    if ( pst->type==pst_position ) {
5946 		fscanf( sfd, " dx=%hd dy=%hd dh=%hd dv=%hd",
5947 			&pst->u.pos.xoff, &pst->u.pos.yoff,
5948 			&pst->u.pos.h_adv_off, &pst->u.pos.v_adv_off);
5949 		pst->u.pos.adjust = SFDReadValDevTab(sfd);
5950 		ch = nlgetc(sfd);		/* Eat new line */
5951 	    } else if ( pst->type==pst_pair ) {
5952 		getname(sfd,tok);
5953 		pst->u.pair.paired = copy(tok);
5954 		pst->u.pair.vr = chunkalloc(sizeof(struct vr [2]));
5955 		fscanf( sfd, " dx=%hd dy=%hd dh=%hd dv=%hd",
5956 			&pst->u.pair.vr[0].xoff, &pst->u.pair.vr[0].yoff,
5957 			&pst->u.pair.vr[0].h_adv_off, &pst->u.pair.vr[0].v_adv_off);
5958 		pst->u.pair.vr[0].adjust = SFDReadValDevTab(sfd);
5959 		fscanf( sfd, " dx=%hd dy=%hd dh=%hd dv=%hd",
5960 			&pst->u.pair.vr[1].xoff, &pst->u.pair.vr[1].yoff,
5961 			&pst->u.pair.vr[1].h_adv_off, &pst->u.pair.vr[1].v_adv_off);
5962 		pst->u.pair.vr[0].adjust = SFDReadValDevTab(sfd);
5963 		ch = nlgetc(sfd);
5964 	    } else if ( pst->type==pst_lcaret ) {
5965 		int i;
5966 		fscanf( sfd, " %d", &pst->u.lcaret.cnt );
5967 		pst->u.lcaret.carets = malloc(pst->u.lcaret.cnt*sizeof(int16));
5968 		for ( i=0; i<pst->u.lcaret.cnt; ++i )
5969 		    fscanf( sfd, " %hd", &pst->u.lcaret.carets[i]);
5970 		geteol(sfd,tok);
5971 	    } else {
5972 		geteol(sfd,tok);
5973 		pst->u.lig.components = copy(tok);	/* it's in the same place for all formats */
5974 		if ( isliga ) {
5975 		    pst->u.lig.lig = sc;
5976 		    if ( old )
5977 			last = (PST *) LigaCreateFromOldStyleMultiple((PST1 *) pst);
5978 		}
5979 	    }
5980 #ifdef FONTFORGE_CONFIG_CVT_OLD_MAC_FEATURES
5981 	    if ( old )
5982 		CvtOldMacFeature((PST1 *) pst);
5983 #endif
5984 	} else if ( strmatch(tok,"Colour:")==0 ) {
5985 	    uint32 temp;
5986 	    gethex(sfd,&temp);
5987 	    sc->color = temp;
5988 	} else if ( strmatch(tok,"Comment:")==0 ) {
5989 	    sc->comment = SFDReadUTF7Str(sfd);
5990 	} else if ( strmatch(tok,"Decomposition:")==0 ) {
5991 	    char* decomp = SFDReadUTF7Str(sfd);
5992 	    sc->user_decomp = utf82u_copy(decomp);
5993 	    free(decomp);
5994 	} else if ( strmatch(tok,"TileMargin:")==0 ) {
5995 	    getreal(sfd,&sc->tile_margin);
5996 	} else if ( strmatch(tok,"TileBounds:")==0 ) {
5997 	    getreal(sfd,&sc->tile_bounds.minx);
5998 	    getreal(sfd,&sc->tile_bounds.miny);
5999 	    getreal(sfd,&sc->tile_bounds.maxx);
6000 	    getreal(sfd,&sc->tile_bounds.maxy);
6001 	} else if ( strmatch(tok,"EndChar")==0 ) {
6002 	    if ( sc->orig_pos<sf->glyphcnt )
6003 		sf->glyphs[sc->orig_pos] = sc;
6004             /* Recalculating hint active zones may be needed for old .sfd files. */
6005             /* Do this when we have finished with other glyph components, */
6006             /* so that splines are already available */
6007 	    if ( sf->sfd_version<2 )
6008                 SCGuessHintInstancesList( sc,ly_fore,sc->hstem,sc->vstem,sc->dstem,false,false );
6009             else if ( had_old_dstems && sc->layers[ly_fore].splines != NULL )
6010                 SCGuessHintInstancesList( sc,ly_fore,NULL,NULL,sc->dstem,false,true );
6011 	    if ( sc->layers[ly_fore].order2 )
6012 		SCDefaultInterpolation(sc);
6013 return( sc );
6014 	} else {
6015 	    geteol(sfd,tok);
6016 	}
6017     }
6018 }
6019 
SFDGetBitmapProps(FILE * sfd,BDFFont * bdf,char * tok)6020 static int SFDGetBitmapProps(FILE *sfd,BDFFont *bdf,char *tok) {
6021     int pcnt;
6022     int i;
6023 
6024     if ( getint(sfd,&pcnt)!=1 || pcnt<=0 )
6025 return( 0 );
6026     bdf->prop_cnt = pcnt;
6027     bdf->props = malloc(pcnt*sizeof(BDFProperties));
6028     for ( i=0; i<pcnt; ++i ) {
6029 	if ( getname(sfd,tok)!=1 )
6030     break;
6031 	if ( strcmp(tok,"BDFEndProperties")==0 )
6032     break;
6033 	bdf->props[i].name = copy(tok);
6034 	getint(sfd,&bdf->props[i].type);
6035 	switch ( bdf->props[i].type&~prt_property ) {
6036 	  case prt_int: case prt_uint:
6037 	    getint(sfd,&bdf->props[i].u.val);
6038 	  break;
6039 	  case prt_string: case prt_atom:
6040 	    geteol(sfd,tok);
6041 	    if ( tok[strlen(tok)-1]=='"' ) tok[strlen(tok)-1] = '\0';
6042 	    bdf->props[i].u.str = copy(tok[0]=='"'?tok+1:tok);
6043 	  break;
6044 	  default:
6045 	  break;
6046 	}
6047     }
6048     bdf->prop_cnt = i;
6049 return( 1 );
6050 }
6051 
SFDGetBitmapChar(FILE * sfd,BDFFont * bdf)6052 static int SFDGetBitmapChar(FILE *sfd,BDFFont *bdf) {
6053     BDFChar *bfc;
6054     struct enc85 dec;
6055     int i, enc, orig;
6056     int width,xmax,xmin,ymax,ymin, vwidth=-1;
6057     EncMap *map;
6058     int ch;
6059 
6060     map = bdf->sf->map;
6061 
6062     if ( getint(sfd,&orig)!=1 || orig<0 )
6063 return( 0 );
6064     if ( getint(sfd,&enc)!=1 )
6065 return( 0 );
6066     if ( getint(sfd,&width)!=1 )
6067 return( 0 );
6068     if ( getint(sfd,&xmin)!=1 )
6069 return( 0 );
6070     if ( getint(sfd,&xmax)!=1 )
6071 return( 0 );
6072     if ( getint(sfd,&ymin)!=1 )
6073 return( 0 );
6074     while ( (ch=nlgetc(sfd))==' ');
6075     ungetc(ch,sfd);
6076     if ( ch=='\n' || ch=='\r' || getint(sfd,&ymax)!=1 ) {
6077 	/* Old style format, no orig_pos given, shift everything by 1 */
6078 	ymax = ymin;
6079 	ymin = xmax;
6080 	xmax = xmin;
6081 	xmin = width;
6082 	width = enc;
6083 	enc = orig;
6084 	orig = map->map[enc];
6085     } else {
6086 	while ( (ch=nlgetc(sfd))==' ');
6087 	ungetc(ch,sfd);
6088 	if ( ch!='\n' && ch!='\r' )
6089 	    getint(sfd,&vwidth);
6090     }
6091     if ( enc<0 ||xmax<xmin || ymax<ymin )
6092 return( 0 );
6093 
6094     bfc = chunkalloc(sizeof(BDFChar));
6095     if (bfc == NULL)
6096         return 0;
6097 
6098     if ( orig==-1 ) {
6099 	bfc->sc = SFMakeChar(bdf->sf,map,enc);
6100 	orig = bfc->sc->orig_pos;
6101     }
6102 
6103     bfc->orig_pos = orig;
6104     bfc->width = width;
6105     bfc->ymax = ymax; bfc->ymin = ymin;
6106     bfc->xmax = xmax; bfc->xmin = xmin;
6107     bdf->glyphs[orig] = bfc;
6108     bfc->sc = bdf->sf->glyphs[orig];
6109     bfc->vwidth = vwidth!=-1 ? vwidth :
6110 	    rint(bfc->sc->vwidth*bdf->pixelsize / (real) (bdf->sf->ascent+bdf->sf->descent));
6111     if ( bdf->clut==NULL ) {
6112 	bfc->bytes_per_line = (bfc->xmax-bfc->xmin)/8 +1;
6113 	bfc->depth = 1;
6114     } else {
6115 	bfc->bytes_per_line = bfc->xmax-bfc->xmin +1;
6116 	bfc->byte_data = true;
6117 	bfc->depth = bdf->clut->clut_len==4 ? 2 : bdf->clut->clut_len==16 ? 4 : 8;
6118     }
6119     bfc->bitmap = calloc((bfc->ymax-bfc->ymin+1)*bfc->bytes_per_line,sizeof(uint8));
6120 
6121     memset(&dec,'\0', sizeof(dec)); dec.pos = -1;
6122     dec.sfd = sfd;
6123     for ( i=0; i<=bfc->ymax-bfc->ymin; ++i ) {
6124 	uint8 *pt = (uint8 *) (bfc->bitmap + i*bfc->bytes_per_line);
6125 	uint8 *end = (uint8 *) (bfc->bitmap + (i+1)*bfc->bytes_per_line);
6126 	while ( pt<end ) {
6127 	    *pt++ = Dec85(&dec);
6128 	}
6129     }
6130     if ( bfc->sc==NULL ) {
6131 	bdf->glyphs[bfc->orig_pos] = NULL;
6132 	BDFCharFree(bfc);
6133     }
6134 /* This fixes a bug: We didn't set "widthset" on splinechars when reading in */
6135 /*  winfonts. We should set it now on any bitmaps worth outputting to make up*/
6136 /*  for that. Eventually we should have good sfd files and can remove this */
6137     else if ( bfc->sc->width!=bdf->sf->ascent + bdf->sf->descent )
6138 	bfc->sc->widthset = true;
6139 return( 1 );
6140 }
6141 
SFDGetBitmapReference(FILE * sfd,BDFFont * bdf)6142 static int SFDGetBitmapReference(FILE *sfd,BDFFont *bdf) {
6143     BDFChar *bc;
6144     BDFRefChar *ref, *head;
6145     int gid, rgid, xoff, yoff;
6146     char ch;
6147 
6148     /* 'BDFRefChar:' elements should not occur in the file before the corresponding */
6149     /* 'BDFChar:'. However it is possible that the glyphs they refer to are not yet */
6150     /* available. So we will find them later */
6151     if ( getint(sfd,&gid)!=1 || gid<=0 || gid >= bdf->glyphcnt || ( bc = bdf->glyphs[gid] ) == NULL )
6152 return( 0 );
6153     if ( getint(sfd,&rgid)!=1 || rgid<0 )
6154 return( 0 );
6155     if ( getint(sfd,&xoff)!=1 )
6156 return( 0 );
6157     if ( getint(sfd,&yoff)!=1 )
6158 return( 0 );
6159     while ( isspace( ch=nlgetc( sfd )) && ch!='\r' && ch!='\n' );
6160 
6161     ref = calloc( 1,sizeof( BDFRefChar ));
6162     ref->gid = rgid; ref->xoff = xoff, ref->yoff = yoff;
6163     if ( ch == 'S' ) ref->selected = true;
6164     for ( head = bc->refs; head != NULL && head->next!=NULL; head = head->next );
6165     if ( head == NULL ) bc->refs = ref;
6166     else head->next = ref;
6167 return( 1 );
6168 }
6169 
SFDFixupBitmapRefs(BDFFont * bdf)6170 static void SFDFixupBitmapRefs( BDFFont *bdf ) {
6171     BDFChar *bc, *rbc;
6172     BDFRefChar *head, *next, *prev;
6173     int i;
6174 
6175     for ( i=0; i<bdf->glyphcnt; i++ ) if (( bc = bdf->glyphs[i] ) != NULL ) {
6176 	prev = NULL;
6177 	for ( head = bc->refs; head != NULL; head = next ) {
6178 	    next = head->next;
6179 	    if (( rbc = bdf->glyphs[head->gid] ) != NULL ) {
6180 		head->bdfc = rbc;
6181 		BCMakeDependent( bc,rbc );
6182 		prev = head;
6183 	    } else {
6184 		LogError(_("Glyph %d in bitmap strike %d pixels refers to a missing glyph (%d)"),
6185 		    bc->orig_pos, bdf->pixelsize, head->gid );
6186 		if ( prev == NULL ) bc->refs = next;
6187 		else prev->next = next;
6188 	    }
6189 	}
6190     }
6191 
6192 }
6193 
SFDGetBitmapFont(FILE * sfd,SplineFont * sf,int fromdir,char * dirname)6194 static int SFDGetBitmapFont(FILE *sfd,SplineFont *sf,int fromdir,char *dirname) {
6195     BDFFont *bdf, *prev;
6196     char tok[2000];
6197     int pixelsize, ascent, descent, depth=1;
6198     int ch, enccount;
6199 
6200     if ( getint(sfd,&pixelsize)!=1 || pixelsize<=0 )
6201 return( 0 );
6202     if ( getint(sfd,&enccount)!=1 || enccount<0 )
6203 return( 0 );
6204     if ( getint(sfd,&ascent)!=1 || ascent<0 )
6205 return( 0 );
6206     if ( getint(sfd,&descent)!=1 || descent<0 )
6207 return( 0 );
6208     if ( getint(sfd,&depth)!=1 )
6209 	depth = 1;	/* old sfds don't have a depth here */
6210     else if ( depth!=1 && depth!=2 && depth!=4 && depth!=8 )
6211 return( 0 );
6212     while ( (ch = nlgetc(sfd))==' ' );
6213     ungetc(ch,sfd);		/* old sfds don't have a foundry */
6214 
6215     bdf = calloc(1,sizeof(BDFFont));
6216     if (bdf == NULL)
6217         return 0;
6218 
6219     if ( ch!='\n' && ch!='\r' ) {
6220 	getname(sfd,tok);
6221 	bdf->foundry = copy(tok);
6222     }
6223     bdf->pixelsize = pixelsize;
6224     bdf->ascent = ascent;
6225     bdf->descent = descent;
6226     if ( depth!=1 )
6227 	BDFClut(bdf,(1<<(depth/2)));
6228 
6229     if ( sf->bitmaps==NULL )
6230 	sf->bitmaps = bdf;
6231     else {
6232 	for ( prev=sf->bitmaps; prev->next!=NULL; prev=prev->next );
6233 	prev->next = bdf;
6234     }
6235     bdf->sf = sf;
6236     bdf->glyphcnt = bdf->glyphmax = sf->glyphcnt;
6237     bdf->glyphs = calloc(bdf->glyphcnt,sizeof(BDFChar *));
6238 
6239     while ( getname(sfd,tok)==1 ) {
6240 	if ( strcmp(tok,"BDFStartProperties:")==0 )
6241 	    SFDGetBitmapProps(sfd,bdf,tok);
6242 	else if ( strcmp(tok,"BDFEndProperties")==0 )
6243 	    /* Do Nothing */;
6244 	else if ( strcmp(tok,"Resolution:")==0 )
6245 	    getint(sfd,&bdf->res);
6246 	else if ( strcmp(tok,"BDFChar:")==0 )
6247 	    SFDGetBitmapChar(sfd,bdf);
6248 	else if ( strcmp(tok,"BDFRefChar:")==0 )
6249 	    SFDGetBitmapReference(sfd,bdf);
6250 	else if ( strcmp(tok,"EndBitmapFont")==0 )
6251     break;
6252     }
6253     if ( fromdir ) {
6254 	DIR *dir;
6255 	struct dirent *ent;
6256 	char *name;
6257 
6258 	dir = opendir(dirname);
6259 	if ( dir==NULL )
6260 return( 0 );
6261 	name = malloc(strlen(dirname)+NAME_MAX+3);
6262 
6263 	while ( (ent=readdir(dir))!=NULL ) {
6264 	    char *pt = strrchr(ent->d_name,EXT_CHAR);
6265 	    if ( pt==NULL )
6266 		/* Nothing interesting */;
6267 	    else if ( strcmp(pt,BITMAP_EXT)==0 ) {
6268 		FILE *gsfd;
6269 		sprintf(name,"%s/%s", dirname, ent->d_name);
6270 		gsfd = fopen(name,"r");
6271 		if ( gsfd!=NULL ) {
6272 		    if ( getname(gsfd,tok) && strcmp(tok,"BDFChar:")==0)
6273 			SFDGetBitmapChar(gsfd,bdf);
6274 		    fclose(gsfd);
6275 		    ff_progress_next();
6276 		}
6277 	    }
6278 	}
6279 	free(name);
6280 	closedir(dir);
6281     }
6282     SFDFixupBitmapRefs( bdf );
6283 return( 1 );
6284 }
6285 
SFDFixupRef(SplineChar * sc,RefChar * ref,int layer)6286 static void SFDFixupRef(SplineChar *sc,RefChar *ref,int layer) {
6287     RefChar *rf;
6288     int ly;
6289 
6290     if ( sc->parent->multilayer ) {
6291 	for ( ly=ly_fore; ly<ref->sc->layer_cnt; ++ly ) {
6292 	    for ( rf = ref->sc->layers[ly].refs; rf!=NULL; rf=rf->next ) {
6293 		if ( rf->sc==sc ) {	/* Huh? */
6294 		    ref->sc->layers[ly].refs = NULL;
6295 	    break;
6296 		}
6297 		if ( rf->layers[0].splines==NULL )
6298 		    SFDFixupRef(ref->sc,rf,layer);
6299 	    }
6300 	}
6301     } else {
6302 	for ( rf = ref->sc->layers[layer].refs; rf!=NULL; rf=rf->next ) {
6303 	    if ( rf->sc==sc ) {	/* Huh? */
6304 		ref->sc->layers[layer].refs = NULL;
6305 	break;
6306 	    }
6307 	    if ( rf->layers[0].splines==NULL )
6308 		SFDFixupRef(ref->sc,rf,layer);
6309 	}
6310     }
6311     SCReinstanciateRefChar(sc,ref,layer);
6312     SCMakeDependent(sc,ref->sc);
6313 }
6314 
6315 /* Look for character duplicates, such as might be generated by having the same */
6316 /*  glyph at two encoding slots */
6317 /* This is an obsolete convention, supported now only in sfd files */
6318 /* I think it is ok if something depends on this character, because the */
6319 /*  code that handles references will automatically unwrap it down to be base */
SCDuplicate(SplineChar * sc)6320 static SplineChar *SCDuplicate(SplineChar *sc) {
6321     SplineChar *matched = sc;
6322 
6323     if ( sc==NULL || sc->parent==NULL || sc->parent->cidmaster!=NULL )
6324 return( sc );		/* Can't do this in CID keyed fonts */
6325 
6326     if ( sc->layer_cnt!=2 )
6327 return( sc );
6328 
6329     while ( sc->layers[ly_fore].refs!=NULL &&
6330 	    sc->layers[ly_fore].refs->sc!=NULL &&	/* Can happen if we are called during font loading before references are fixed up */
6331 	    sc->layers[ly_fore].refs->next==NULL &&
6332 	    sc->layers[ly_fore].refs->transform[0]==1 && sc->layers[ly_fore].refs->transform[1]==0 &&
6333 	    sc->layers[ly_fore].refs->transform[2]==0 && sc->layers[ly_fore].refs->transform[3]==1 &&
6334 	    sc->layers[ly_fore].refs->transform[4]==0 && sc->layers[ly_fore].refs->transform[5]==0 ) {
6335 	char *basename = sc->layers[ly_fore].refs->sc->name;
6336 	if ( strcmp(sc->name,basename)!=0 )
6337     break;
6338 	matched = sc->layers[ly_fore].refs->sc;
6339 	sc = sc->layers[ly_fore].refs->sc;
6340     }
6341 return( matched );
6342 }
6343 
SFDFixupUndoRefsUndoList(SplineFont * sf,Undoes * undo)6344 static void SFDFixupUndoRefsUndoList(SplineFont *sf,Undoes *undo) {
6345     for( ; undo; undo = undo->next ) {
6346 	if( undo->undotype == ut_state && undo->u.state.refs ) {
6347 	    RefChar *ref=NULL;
6348 	    for ( ref=undo->u.state.refs; ref!=NULL; ref=ref->next ) {
6349 
6350 		ref->sc = sf->glyphs[ ref->orig_pos ];
6351 	    }
6352 	}
6353     }
6354 }
6355 
SFDFixupUndoRefs(SplineFont * sf)6356 static void SFDFixupUndoRefs(SplineFont *sf) {
6357     int i=0;
6358     Undoes *undo = 0;
6359 
6360     for ( i=0; i<sf->glyphcnt; ++i ) {
6361 	if ( sf->glyphs[i]!=NULL ) {
6362 	    SplineChar *sc = sf->glyphs[i];
6363 	    int layer = 0;
6364 	    for ( layer=0; layer<sc->layer_cnt; ++layer ) {
6365 		if( sc->layers[layer].undoes ) {
6366 		    undo = sc->layers[layer].undoes;
6367 		    SFDFixupUndoRefsUndoList( sf, undo );
6368 		}
6369 		if( sc->layers[layer].redoes ) {
6370 		    undo = sc->layers[layer].redoes;
6371 		    SFDFixupUndoRefsUndoList( sf, undo );
6372 		}
6373 	    }
6374 	}
6375     }
6376 
6377 
6378 }
6379 
6380 
SFDFixupRefs(SplineFont * sf)6381 void SFDFixupRefs(SplineFont *sf) {
6382     int i, isv;
6383     RefChar *refs, *rnext, *rprev;
6384     /*int isautorecovery = sf->changed;*/
6385     KernPair *kp, *prev, *next;
6386     EncMap *map = sf->map;
6387     int layer;
6388     int k,l;
6389     SplineFont *cidmaster = sf, *ksf;
6390 
6391     k = 1;
6392     if ( sf->subfontcnt!=0 )
6393 	sf = sf->subfonts[0];
6394 
6395     ff_progress_change_line2(_("Interpreting Glyphs"));
6396     for (;;) {
6397 	for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
6398 	    SplineChar *sc = sf->glyphs[i];
6399 	    /* A changed character is one that has just been recovered */
6400 	    /*  unchanged characters will already have been fixed up */
6401 	    /* Er... maybe not. If the character being recovered is refered to */
6402 	    /*  by another character then we need to fix up that other char too*/
6403 	    /*if ( isautorecovery && !sc->changed )*/
6404 	/*continue;*/
6405 	    for ( layer = 0; layer<sc->layer_cnt; ++layer ) {
6406 		rprev = NULL;
6407 		for ( refs = sc->layers[layer].refs; refs!=NULL; refs=rnext ) {
6408 		    rnext = refs->next;
6409 		    if ( refs->encoded ) {		/* Old sfd format */
6410 			if ( refs->orig_pos<map->encmax && map->map[refs->orig_pos]!=-1 )
6411 			    refs->orig_pos = map->map[refs->orig_pos];
6412 			else
6413 			    refs->orig_pos = sf->glyphcnt;
6414 			refs->encoded = false;
6415 		    }
6416 		    if ( refs->orig_pos<sf->glyphcnt && refs->orig_pos>=0 )
6417 			refs->sc = sf->glyphs[refs->orig_pos];
6418 		    if ( refs->sc!=NULL ) {
6419 			refs->unicode_enc = refs->sc->unicodeenc;
6420 			refs->adobe_enc = getAdobeEnc(refs->sc->name);
6421 			rprev = refs;
6422 			if ( refs->use_my_metrics ) {
6423 			    if ( sc->width != refs->sc->width ) {
6424 				LogError(_("Bad sfd file. Glyph %s has width %d even though it should be\n  bound to the width of %s which is %d.\n"),
6425 					sc->name, sc->width, refs->sc->name, refs->sc->width );
6426 				sc->width = refs->sc->width;
6427 			    }
6428 			}
6429 		    } else {
6430 			RefCharFree(refs);
6431 			if ( rprev!=NULL )
6432 			    rprev->next = rnext;
6433 			else
6434 			    sc->layers[layer].refs = rnext;
6435 		    }
6436 		}
6437 	    }
6438 	    /* In old sfd files we used a peculiar idiom to represent a multiply */
6439 	    /*  encoded glyph. Fix it up now. Remove the fake glyph and adjust the*/
6440 	    /*  map */
6441 	    /*if ( isautorecovery && !sc->changed )*/
6442 	/*continue;*/
6443 	    for ( isv=0; isv<2; ++isv ) {
6444 		for ( prev = NULL, kp=isv?sc->vkerns : sc->kerns; kp!=NULL; kp=next ) {
6445 		    int index = (intpt) (kp->sc);
6446 
6447 		    next = kp->next;
6448 		    // be impotent if the reference is already to the correct location
6449                     if ( !kp->kcid ) {	/* It's encoded (old sfds), else orig */
6450                         if ( index>=map->encmax || map->map[index]==-1 )
6451                             index = sf->glyphcnt;
6452                         else
6453                             index = map->map[index];
6454                     }
6455                     kp->kcid = false;
6456                     ksf = sf;
6457                     if ( cidmaster!=sf ) {
6458                         for ( l=0; l<cidmaster->subfontcnt; ++l ) {
6459                             ksf = cidmaster->subfonts[l];
6460                             if ( index<ksf->glyphcnt && ksf->glyphs[index]!=NULL )
6461                                 break;
6462                         }
6463                     }
6464                     if ( index>=ksf->glyphcnt || ksf->glyphs[index]==NULL ) {
6465                         IError( "Bad kerning information in glyph %s\n", sc->name );
6466                         kp->sc = NULL;
6467                     } else {
6468                         kp->sc = ksf->glyphs[index];
6469                     }
6470 
6471 		    if ( kp->sc!=NULL )
6472 			prev = kp;
6473 		    else{
6474 			if ( prev!=NULL )
6475 			    prev->next = next;
6476 			else if ( isv )
6477 			    sc->vkerns = next;
6478 			else
6479 			    sc->kerns = next;
6480 			chunkfree(kp,sizeof(KernPair));
6481 		    }
6482 		}
6483 	    }
6484 	    if ( SCDuplicate(sc)!=sc ) {
6485 		SplineChar *base = SCDuplicate(sc);
6486 		int orig = sc->orig_pos, enc = sf->map->backmap[orig], uni = sc->unicodeenc;
6487 		SplineCharFree(sc);
6488 		sf->glyphs[i]=NULL;
6489 		sf->map->backmap[orig] = -1;
6490 		sf->map->map[enc] = base->orig_pos;
6491 		AltUniAdd(base,uni);
6492 	    }
6493 	}
6494 	for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
6495 	    SplineChar *sc = sf->glyphs[i];
6496 	    for ( layer=0; layer<sc->layer_cnt; ++layer ) {
6497 		for ( refs = sf->glyphs[i]->layers[layer].refs; refs!=NULL; refs=refs->next ) {
6498 		    SFDFixupRef(sf->glyphs[i],refs,layer);
6499 		}
6500 	    }
6501 	    ff_progress_next();
6502 	}
6503 	if ( sf->cidmaster==NULL )
6504 	    for ( i=sf->glyphcnt-1; i>=0 && sf->glyphs[i]==NULL; --i )
6505 		sf->glyphcnt = i;
6506 	if ( k>=cidmaster->subfontcnt )
6507     break;
6508 	sf = cidmaster->subfonts[k++];
6509     }
6510 }
6511 
6512 /* When we recover from an autosaved file we must be careful. If that file */
6513 /*  contains a character that is refered to by another character then the */
6514 /*  dependent list will contain a dead pointer without this routine. Similarly*/
6515 /*  for kerning */
6516 /* We might have needed to do something for references except they've already */
6517 /*  got a orig_pos field and passing through SFDFixupRefs will munch their*/
6518 /*  SplineChar pointer */
SFRemoveDependencies(SplineFont * sf)6519 static void SFRemoveDependencies(SplineFont *sf) {
6520     int i;
6521     struct splinecharlist *dlist, *dnext;
6522     KernPair *kp;
6523 
6524     for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
6525 	for ( dlist = sf->glyphs[i]->dependents; dlist!=NULL; dlist = dnext ) {
6526 	    dnext = dlist->next;
6527 	    chunkfree(dlist,sizeof(*dlist));
6528 	}
6529 	sf->glyphs[i]->dependents = NULL;
6530 	for ( kp=sf->glyphs[i]->kerns; kp!=NULL; kp=kp->next ) {
6531 	    kp->sc = (SplineChar *) (intpt) (kp->sc->orig_pos);
6532 	    kp->kcid = true;		/* flag */
6533 	}
6534 	for ( kp=sf->glyphs[i]->vkerns; kp!=NULL; kp=kp->next ) {
6535 	    kp->sc = (SplineChar *) (intpt) (kp->sc->orig_pos);
6536 	    kp->kcid = true;
6537 	}
6538     }
6539 }
6540 
SFDGetPrivate(FILE * sfd,SplineFont * sf)6541 static void SFDGetPrivate(FILE *sfd,SplineFont *sf) {
6542     int i, cnt, len;
6543     char name[200];
6544     char *pt, *end;
6545 
6546     sf->private = calloc(1,sizeof(struct psdict));
6547     getint(sfd,&cnt);
6548     sf->private->next = sf->private->cnt = cnt;
6549     sf->private->values = calloc(cnt,sizeof(char *));
6550     sf->private->keys = calloc(cnt,sizeof(char *));
6551     for ( i=0; i<cnt; ++i ) {
6552 	getname(sfd,name);
6553 	sf->private->keys[i] = copy(name);
6554 	getint(sfd,&len);
6555 	nlgetc(sfd);	/* skip space */
6556 	pt = sf->private->values[i] = malloc(len+1);
6557 	for ( end = pt+len; pt<end; ++pt )
6558 	    *pt = nlgetc(sfd);
6559 	*pt='\0';
6560     }
6561 }
6562 
SFDGetSubrs(FILE * sfd)6563 static void SFDGetSubrs(FILE *sfd) {
6564     /* Obselete, parse it in case there are any old sfds */
6565     int i, cnt, tot, len;
6566     struct enc85 dec;
6567 
6568     getint(sfd,&cnt);
6569     tot = 0;
6570     for ( i=0; i<cnt; ++i ) {
6571 	getint(sfd,&len);
6572 	tot += len;
6573     }
6574 
6575     memset(&dec,'\0', sizeof(dec)); dec.pos = -1;
6576     dec.sfd = sfd;
6577     for ( i=0; i<tot; ++i )
6578 	Dec85(&dec);
6579 }
6580 
SFDGetLangName(FILE * sfd,struct ttflangname * old)6581 static struct ttflangname *SFDGetLangName(FILE *sfd,struct ttflangname *old) {
6582     struct ttflangname *cur = chunkalloc(sizeof(struct ttflangname)), *prev;
6583     int i;
6584 
6585     getint(sfd,&cur->lang);
6586     for ( i=0; i<ttf_namemax; ++i )
6587 	cur->names[i] = SFDReadUTF7Str(sfd);
6588     if ( old==NULL )
6589 return( cur );
6590     for ( prev = old; prev->next !=NULL; prev = prev->next );
6591     prev->next = cur;
6592 return( old );
6593 }
6594 
SFDGetGasp(FILE * sfd,SplineFont * sf)6595 static void SFDGetGasp(FILE *sfd,SplineFont *sf) {
6596     int i;
6597 
6598     getsint(sfd,(int16 *) &sf->gasp_cnt);
6599     sf->gasp = malloc(sf->gasp_cnt*sizeof(struct gasp));
6600     for ( i=0; i<sf->gasp_cnt; ++i ) {
6601 	getsint(sfd,(int16 *) &sf->gasp[i].ppem);
6602 	getsint(sfd,(int16 *) &sf->gasp[i].flags);
6603     }
6604     getsint(sfd,(int16 *) &sf->gasp_version);
6605 }
6606 
SFDGetDesignSize(FILE * sfd,SplineFont * sf)6607 static void SFDGetDesignSize(FILE *sfd,SplineFont *sf) {
6608     int ch;
6609     struct otfname *cur;
6610 
6611     getsint(sfd,(int16 *) &sf->design_size);
6612     while ( (ch=nlgetc(sfd))==' ' );
6613     ungetc(ch,sfd);
6614     if ( isdigit(ch)) {
6615 	getsint(sfd,(int16 *) &sf->design_range_bottom);
6616 	while ( (ch=nlgetc(sfd))==' ' );
6617 	if ( ch!='-' )
6618 	    ungetc(ch,sfd);
6619 	getsint(sfd,(int16 *) &sf->design_range_top);
6620 	getsint(sfd,(int16 *) &sf->fontstyle_id);
6621 	for (;;) {
6622 	    while ( (ch=nlgetc(sfd))==' ' );
6623 	    ungetc(ch,sfd);
6624 	    if ( !isdigit(ch))
6625 	break;
6626 	    cur = chunkalloc(sizeof(struct otfname));
6627 	    cur->next = sf->fontstyle_name;
6628 	    sf->fontstyle_name = cur;
6629 	    getsint(sfd,(int16 *) &cur->lang);
6630 	    cur->name = SFDReadUTF7Str(sfd);
6631 	}
6632     }
6633 }
6634 
SFDGetOtfFeatName(FILE * sfd,SplineFont * sf)6635 static void SFDGetOtfFeatName(FILE *sfd,SplineFont *sf) {
6636     int ch;
6637     struct otfname *cur;
6638     struct otffeatname *fn;
6639 
6640     fn = chunkalloc(sizeof(struct otffeatname));
6641     fn->tag = gettag(sfd);
6642     for (;;) {
6643 	while ( (ch=nlgetc(sfd))==' ' );
6644 	ungetc(ch,sfd);
6645 	if ( !isdigit(ch))
6646     break;
6647 	cur = chunkalloc(sizeof(struct otfname));
6648 	cur->next = fn->names;
6649 	fn->names = cur;
6650 	getsint(sfd,(int16 *) &cur->lang);
6651 	cur->name = SFDReadUTF7Str(sfd);
6652     }
6653     fn->next = sf->feat_names;
6654     sf->feat_names = fn;
6655 }
6656 
SFDGetEncoding(FILE * sfd,char * tok)6657 static Encoding *SFDGetEncoding(FILE *sfd, char *tok) {
6658     Encoding *enc = NULL;
6659     int encname;
6660 
6661     if ( getint(sfd,&encname) ) {
6662 	if ( encname<(int)(sizeof(charset_names)/sizeof(charset_names[0])-1) )
6663 	    enc = FindOrMakeEncoding(charset_names[encname]);
6664     } else {
6665 	geteol(sfd,tok);
6666 	enc = FindOrMakeEncoding(tok);
6667     }
6668     if ( enc==NULL )
6669 	enc = &custom;
6670 return( enc );
6671 }
6672 
SFDGetUniInterp(FILE * sfd,char * tok,SplineFont * sf)6673 static enum uni_interp SFDGetUniInterp(FILE *sfd, char *tok, SplineFont *sf) {
6674     int uniinterp = ui_none;
6675     int i;
6676 
6677     geteol(sfd,tok);
6678     for ( i=0; unicode_interp_names[i]!=NULL; ++i )
6679 	if ( strcmp(tok,unicode_interp_names[i])==0 ) {
6680 	    uniinterp = i;
6681     break;
6682 	}
6683     /* These values are now handled by namelists */
6684     if ( uniinterp == ui_adobe ) {
6685 	sf->for_new_glyphs = NameListByName("AGL with PUA");
6686 	uniinterp = ui_none;
6687     } else if ( uniinterp == ui_greek ) {
6688 	sf->for_new_glyphs = NameListByName("Greek small caps");
6689 	uniinterp = ui_none;
6690     } else if ( uniinterp == ui_ams ) {
6691 	sf->for_new_glyphs = NameListByName("AMS Names");
6692 	uniinterp = ui_none;
6693     }
6694 
6695 return( uniinterp );
6696 }
6697 
SFDGetNameList(FILE * sfd,char * tok,SplineFont * sf)6698 static void SFDGetNameList(FILE *sfd, char *tok, SplineFont *sf) {
6699     NameList *nl;
6700 
6701     geteol(sfd,tok);
6702     nl = NameListByName(tok);
6703     if ( nl==NULL )
6704 	LogError(_("Failed to find NameList: %s"), tok);
6705     else
6706 	sf->for_new_glyphs = nl;
6707 }
6708 
6709 
SFD_ParseNestedLookup(FILE * sfd,SplineFont * sf,int old)6710 static OTLookup *SFD_ParseNestedLookup(FILE *sfd, SplineFont *sf, int old) {
6711     uint32 tag;
6712     int ch, isgpos;
6713     OTLookup *otl;
6714     char *name;
6715 
6716     while ( (ch=nlgetc(sfd))==' ' );
6717     if ( ch=='~' )
6718 return( NULL );
6719     else if ( old ) {
6720 	if ( ch!='\'' )
6721 return( NULL );
6722 
6723 	ungetc(ch,sfd);
6724 	tag = gettag(sfd);
6725 return( (OTLookup *) (intpt) tag );
6726     } else {
6727 	ungetc(ch,sfd);
6728 	name = SFDReadUTF7Str(sfd);
6729 	if ( name==NULL )
6730 return( NULL );
6731 	for ( isgpos=0; isgpos<2; ++isgpos ) {
6732 	    for ( otl=isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl=otl->next ) {
6733 		if ( strcmp(name,otl->lookup_name )==0 )
6734 	goto break2;
6735 	    }
6736 	}
6737 	break2:
6738 	free(name);
6739 return( otl );
6740     }
6741 }
6742 
SFDParseChainContext(FILE * sfd,SplineFont * sf,FPST * fpst,char * tok,int old)6743 static void SFDParseChainContext(FILE *sfd,SplineFont *sf,FPST *fpst, char *tok, int old) {
6744     int ch, i, j, k, temp;
6745     SplineFont *sli_sf = sf->cidmaster ? sf->cidmaster : sf;
6746 
6747     fpst->type = strnmatch(tok,"ContextPos",10)==0 ? pst_contextpos :
6748 		strnmatch(tok,"ContextSub",10)==0 ? pst_contextsub :
6749 		strnmatch(tok,"ChainPos",8)==0 ? pst_chainpos :
6750 		strnmatch(tok,"ChainSub",8)==0 ? pst_chainsub : pst_reversesub;
6751     getname(sfd,tok);
6752     fpst->format = strmatch(tok,"glyph")==0 ? pst_glyphs :
6753 		    strmatch(tok,"class")==0 ? pst_class :
6754 		    strmatch(tok,"coverage")==0 ? pst_coverage : pst_reversecoverage;
6755     if ( old ) {
6756 	fscanf(sfd, "%hu %hu", &((FPST1 *) fpst)->flags, &((FPST1 *) fpst)->script_lang_index );
6757 	if ( ((FPST1 *) fpst)->script_lang_index>=((SplineFont1 *) sli_sf)->sli_cnt && ((FPST1 *) fpst)->script_lang_index!=SLI_NESTED ) {
6758 	    static int complained=false;
6759 	    if ( ((SplineFont1 *) sli_sf)->sli_cnt==0 )
6760 		IError("'%c%c%c%c' has a script index out of bounds: %d\nYou MUST fix this manually",
6761 			(((FPST1 *) fpst)->tag>>24), (((FPST1 *) fpst)->tag>>16)&0xff, (((FPST1 *) fpst)->tag>>8)&0xff, ((FPST1 *) fpst)->tag&0xff,
6762 			((FPST1 *) fpst)->script_lang_index );
6763 	    else if ( !complained )
6764 		IError("'%c%c%c%c' has a script index out of bounds: %d",
6765 			(((FPST1 *) fpst)->tag>>24), (((FPST1 *) fpst)->tag>>16)&0xff, (((FPST1 *) fpst)->tag>>8)&0xff, ((FPST1 *) fpst)->tag&0xff,
6766 			((FPST1 *) fpst)->script_lang_index );
6767 	    else
6768 		IError("'%c%c%c%c' has a script index out of bounds: %d\n",
6769 			(((FPST1 *) fpst)->tag>>24), (((FPST1 *) fpst)->tag>>16)&0xff, (((FPST1 *) fpst)->tag>>8)&0xff, ((FPST1 *) fpst)->tag&0xff,
6770 			((FPST1 *) fpst)->script_lang_index );
6771 	    if ( ((SplineFont1 *) sli_sf)->sli_cnt!=0 )
6772 		((FPST1 *) fpst)->script_lang_index = ((SplineFont1 *) sli_sf)->sli_cnt-1;
6773 	    complained = true;
6774 	}
6775 	while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
6776 	if ( ch=='\'' ) {
6777 	    ungetc(ch,sfd);
6778 	    ((FPST1 *) fpst)->tag = gettag(sfd);
6779 	} else
6780 	    ungetc(ch,sfd);
6781     } else {
6782 	fpst->subtable = SFFindLookupSubtableAndFreeName(sf,SFDReadUTF7Str(sfd));
6783         if ( !fpst->subtable )
6784             LogError(_("Missing Subtable definition found in chained context"));
6785         else
6786 	    fpst->subtable->fpst = fpst;
6787     }
6788     fscanf(sfd, "%hu %hu %hu %hu", &fpst->nccnt, &fpst->bccnt, &fpst->fccnt, &fpst->rule_cnt );
6789     if ( fpst->nccnt!=0 || fpst->bccnt!=0 || fpst->fccnt!=0 ) {
6790 	fpst->nclass = malloc(fpst->nccnt*sizeof(char *));
6791 	fpst->nclassnames = calloc(fpst->nccnt,sizeof(char *));
6792 	if ( fpst->nccnt!=0 ) fpst->nclass[0] = NULL;
6793 	if ( fpst->bccnt!=0 || fpst->fccnt!=0 ) {
6794 	    fpst->bclass = malloc(fpst->bccnt*sizeof(char *));
6795 	    fpst->bclassnames = calloc(fpst->bccnt,sizeof(char *));
6796 	    if (fpst->bccnt!=0 ) fpst->bclass[0] = NULL;
6797 	    fpst->fclass = malloc(fpst->fccnt*sizeof(char *));
6798 	    fpst->fclassnames = calloc(fpst->fccnt,sizeof(char *));
6799 	    if (fpst->fccnt!=0 ) fpst->fclass[0] = NULL;
6800 	}
6801     }
6802 
6803     for ( j=0; j<3; ++j ) {
6804 	for ( i=1; i<(&fpst->nccnt)[j]; ++i ) {
6805 	    getname(sfd,tok);
6806 	    if ( i==1 && j==0 && strcmp(tok,"Class0:")==0 )
6807 		i=0;
6808 	    getint(sfd,&temp);
6809 	    (&fpst->nclass)[j][i] = malloc(temp+1); (&fpst->nclass)[j][i][temp] = '\0';
6810 	    nlgetc(sfd);	/* skip space */
6811 	    fread((&fpst->nclass)[j][i],1,temp,sfd);
6812 	}
6813     }
6814 
6815     fpst->rules = calloc(fpst->rule_cnt,sizeof(struct fpst_rule));
6816     for ( i=0; i<fpst->rule_cnt; ++i ) {
6817 	switch ( fpst->format ) {
6818 	  case pst_glyphs:
6819 	    for ( j=0; j<3; ++j ) {
6820 		getname(sfd,tok);
6821 		getint(sfd,&temp);
6822 		(&fpst->rules[i].u.glyph.names)[j] = malloc(temp+1);
6823 		(&fpst->rules[i].u.glyph.names)[j][temp] = '\0';
6824 		nlgetc(sfd);	/* skip space */
6825 		fread((&fpst->rules[i].u.glyph.names)[j],1,temp,sfd);
6826 	    }
6827 	  break;
6828 	  case pst_class:
6829 	    fscanf( sfd, "%d %d %d", &fpst->rules[i].u.class.ncnt, &fpst->rules[i].u.class.bcnt, &fpst->rules[i].u.class.fcnt );
6830 	    for ( j=0; j<3; ++j ) {
6831 		getname(sfd,tok);
6832 		(&fpst->rules[i].u.class.nclasses)[j] = malloc((&fpst->rules[i].u.class.ncnt)[j]*sizeof(uint16));
6833 		for ( k=0; k<(&fpst->rules[i].u.class.ncnt)[j]; ++k ) {
6834 		    getusint(sfd,&(&fpst->rules[i].u.class.nclasses)[j][k]);
6835 		}
6836 	    }
6837 	  break;
6838 	  case pst_coverage:
6839 	  case pst_reversecoverage:
6840 	    fscanf( sfd, "%d %d %d", &fpst->rules[i].u.coverage.ncnt, &fpst->rules[i].u.coverage.bcnt, &fpst->rules[i].u.coverage.fcnt );
6841 	    for ( j=0; j<3; ++j ) {
6842 		(&fpst->rules[i].u.coverage.ncovers)[j] = malloc((&fpst->rules[i].u.coverage.ncnt)[j]*sizeof(char *));
6843 		for ( k=0; k<(&fpst->rules[i].u.coverage.ncnt)[j]; ++k ) {
6844 		    getname(sfd,tok);
6845 		    getint(sfd,&temp);
6846 		    (&fpst->rules[i].u.coverage.ncovers)[j][k] = malloc(temp+1);
6847 		    (&fpst->rules[i].u.coverage.ncovers)[j][k][temp] = '\0';
6848 		    nlgetc(sfd);	/* skip space */
6849 		    fread((&fpst->rules[i].u.coverage.ncovers)[j][k],1,temp,sfd);
6850 		}
6851 	    }
6852 	  break;
6853 	  default:
6854 	  break;
6855 	}
6856 	switch ( fpst->format ) {
6857 	  case pst_glyphs:
6858 	  case pst_class:
6859 	  case pst_coverage:
6860 	    getint(sfd,&fpst->rules[i].lookup_cnt);
6861 	    fpst->rules[i].lookups = malloc(fpst->rules[i].lookup_cnt*sizeof(struct seqlookup));
6862 	    for ( j=k=0; j<fpst->rules[i].lookup_cnt; ++j ) {
6863 		getname(sfd,tok);
6864 		getint(sfd,&fpst->rules[i].lookups[j].seq);
6865 		fpst->rules[i].lookups[k].lookup = SFD_ParseNestedLookup(sfd,sf,old);
6866 		if ( fpst->rules[i].lookups[k].lookup!=NULL )
6867 		    ++k;
6868 	    }
6869 	    fpst->rules[i].lookup_cnt = k;
6870 	  break;
6871 	  case pst_reversecoverage:
6872 	    getname(sfd,tok);
6873 	    getint(sfd,&temp);
6874 	    fpst->rules[i].u.rcoverage.replacements = malloc(temp+1);
6875 	    fpst->rules[i].u.rcoverage.replacements[temp] = '\0';
6876 	    nlgetc(sfd);	/* skip space */
6877 	    fread(fpst->rules[i].u.rcoverage.replacements,1,temp,sfd);
6878 	  break;
6879 	  default:
6880 	  break;
6881 	}
6882     }
6883     getname(sfd,tok);	/* EndFPST, or one of the ClassName tokens (in newer sfds) */
6884     while ( strcmp(tok,"ClassNames:")==0 || strcmp(tok,"BClassNames:")==0 ||
6885 	    strcmp(tok,"FClassNames:")==0 ) {
6886 	int which = strcmp(tok,"ClassNames:")==0 ? 0 :
6887 		    strcmp(tok,"BClassNames:")==0 ? 1 : 2;
6888 	int cnt = (&fpst->nccnt)[which];
6889 	char **classnames = (&fpst->nclassnames)[which];
6890 	int i;
6891 
6892 	for ( i=0; i<cnt; ++i )
6893 	    classnames[i] = SFDReadUTF7Str(sfd);
6894 	getname(sfd,tok);	/* EndFPST, or one of the ClassName tokens (in newer sfds) */
6895     }
6896 
6897 }
6898 
SFDParseStateMachine(FILE * sfd,SplineFont * sf,ASM * sm,char * tok,int old)6899 static void SFDParseStateMachine(FILE *sfd,SplineFont *sf,ASM *sm, char *tok,int old) {
6900     int i, temp;
6901 
6902     sm->type = strnmatch(tok,"MacIndic",8)==0 ? asm_indic :
6903 		strnmatch(tok,"MacContext",10)==0 ? asm_context :
6904 		strnmatch(tok,"MacLigature",11)==0 ? asm_lig :
6905 		strnmatch(tok,"MacSimple",9)==0 ? asm_simple :
6906 		strnmatch(tok,"MacKern",7)==0 ? asm_kern : asm_insert;
6907     if ( old ) {
6908 	getusint(sfd,&((ASM1 *) sm)->feature);
6909 	nlgetc(sfd);		/* Skip comma */
6910 	getusint(sfd,&((ASM1 *) sm)->setting);
6911     } else {
6912 	sm->subtable = SFFindLookupSubtableAndFreeName(sf,SFDReadUTF7Str(sfd));
6913 	sm->subtable->sm = sm;
6914     }
6915     getusint(sfd,&sm->flags);
6916     getusint(sfd,&sm->class_cnt);
6917     getusint(sfd,&sm->state_cnt);
6918 
6919     sm->classes = malloc(sm->class_cnt*sizeof(char *));
6920     sm->classes[0] = sm->classes[1] = sm->classes[2] = sm->classes[3] = NULL;
6921     for ( i=4; i<sm->class_cnt; ++i ) {
6922 	getname(sfd,tok);
6923 	getint(sfd,&temp);
6924 	sm->classes[i] = malloc(temp+1); sm->classes[i][temp] = '\0';
6925 	nlgetc(sfd);	/* skip space */
6926 	fread(sm->classes[i],1,temp,sfd);
6927     }
6928 
6929     sm->state = malloc(sm->class_cnt*sm->state_cnt*sizeof(struct asm_state));
6930     for ( i=0; i<sm->class_cnt*sm->state_cnt; ++i ) {
6931 	getusint(sfd,&sm->state[i].next_state);
6932 	getusint(sfd,&sm->state[i].flags);
6933 	if ( sm->type == asm_context ) {
6934 	    sm->state[i].u.context.mark_lookup = SFD_ParseNestedLookup(sfd,sf,old);
6935 	    sm->state[i].u.context.cur_lookup = SFD_ParseNestedLookup(sfd,sf,old);
6936 	} else if ( sm->type == asm_insert ) {
6937 	    getint(sfd,&temp);
6938 	    if ( temp==0 )
6939 		sm->state[i].u.insert.mark_ins = NULL;
6940 	    else {
6941 		sm->state[i].u.insert.mark_ins = malloc(temp+1); sm->state[i].u.insert.mark_ins[temp] = '\0';
6942 		nlgetc(sfd);	/* skip space */
6943 		fread(sm->state[i].u.insert.mark_ins,1,temp,sfd);
6944 	    }
6945 	    getint(sfd,&temp);
6946 	    if ( temp==0 )
6947 		sm->state[i].u.insert.cur_ins = NULL;
6948 	    else {
6949 		sm->state[i].u.insert.cur_ins = malloc(temp+1); sm->state[i].u.insert.cur_ins[temp] = '\0';
6950 		nlgetc(sfd);	/* skip space */
6951 		fread(sm->state[i].u.insert.cur_ins,1,temp,sfd);
6952 	    }
6953 	} else if ( sm->type == asm_kern ) {
6954 	    int j;
6955 	    getint(sfd,&sm->state[i].u.kern.kcnt);
6956 	    if ( sm->state[i].u.kern.kcnt!=0 )
6957 		sm->state[i].u.kern.kerns = malloc(sm->state[i].u.kern.kcnt*sizeof(int16));
6958 	    for ( j=0; j<sm->state[i].u.kern.kcnt; ++j ) {
6959 		getint(sfd,&temp);
6960 		sm->state[i].u.kern.kerns[j] = temp;
6961 	    }
6962 	}
6963     }
6964     getname(sfd,tok);			/* EndASM */
6965 }
6966 
SFDParseMacNames(FILE * sfd,char * tok)6967 static struct macname *SFDParseMacNames(FILE *sfd, char *tok) {
6968     struct macname *head=NULL, *last=NULL, *cur;
6969     int enc, lang, len;
6970     char *pt;
6971     int ch;
6972 
6973     while ( strcmp(tok,"MacName:")==0 ) {
6974 	cur = chunkalloc(sizeof(struct macname));
6975 	if ( last==NULL )
6976 	    head = cur;
6977 	else
6978 	    last->next = cur;
6979 	last = cur;
6980 
6981 	getint(sfd,&enc);
6982 	getint(sfd,&lang);
6983 	getint(sfd,&len);
6984 	cur->enc = enc;
6985 	cur->lang = lang;
6986 	cur->name = pt = malloc(len+1);
6987 
6988 	while ( (ch=nlgetc(sfd))==' ');
6989 	if ( ch=='"' )
6990 	    ch = nlgetc(sfd);
6991 	while ( ch!='"' && ch!=EOF && pt<cur->name+len ) {
6992 	    if ( ch=='\\' ) {
6993 		*pt  = (nlgetc(sfd)-'0')<<6;
6994 		*pt |= (nlgetc(sfd)-'0')<<3;
6995 		*pt |= (nlgetc(sfd)-'0');
6996 	    } else
6997 		*pt++ = ch;
6998 	    ch = nlgetc(sfd);
6999 	}
7000 	*pt = '\0';
7001 	getname(sfd,tok);
7002     }
7003 return( head );
7004 }
7005 
SFDParseMacFeatures(FILE * sfd,char * tok)7006 MacFeat *SFDParseMacFeatures(FILE *sfd, char *tok) {
7007     MacFeat *cur, *head=NULL, *last=NULL;
7008     struct macsetting *slast, *scur;
7009     int feat, ism, def, set;
7010 
7011     while ( strcmp(tok,"MacFeat:")==0 ) {
7012 	cur = chunkalloc(sizeof(MacFeat));
7013 	if ( last==NULL )
7014 	    head = cur;
7015 	else
7016 	    last->next = cur;
7017 	last = cur;
7018 
7019 	getint(sfd,&feat); getint(sfd,&ism); getint(sfd, &def);
7020 	cur->feature = feat; cur->ismutex = ism; cur->default_setting = def;
7021 	getname(sfd,tok);
7022 	cur->featname = SFDParseMacNames(sfd,tok);
7023 	slast = NULL;
7024 	while ( strcmp(tok,"MacSetting:")==0 ) {
7025 	    scur = chunkalloc(sizeof(struct macsetting));
7026 	    if ( slast==NULL )
7027 		cur->settings = scur;
7028 	    else
7029 		slast->next = scur;
7030 	    slast = scur;
7031 
7032 	    getint(sfd,&set);
7033 	    scur->setting = set;
7034 	    getname(sfd,tok);
7035 	    scur->setname = SFDParseMacNames(sfd,tok);
7036 	}
7037     }
7038 return( head );
7039 }
7040 
SFDParseMMSubroutine(FILE * sfd)7041 static char *SFDParseMMSubroutine(FILE *sfd) {
7042     char buffer[400], *sofar=calloc(1,1);
7043     const char *endtok = "EndMMSubroutine";
7044     int len = 0, blen, first=true;
7045 
7046     while ( fgets(buffer,sizeof(buffer),sfd)!=NULL ) {
7047 	if ( strncmp(buffer,endtok,strlen(endtok))==0 )
7048     break;
7049 	if ( first ) {
7050 	    first = false;
7051 	    if ( strcmp(buffer,"\n")==0 )
7052     continue;
7053 	}
7054 	blen = strlen(buffer);
7055 	sofar = realloc(sofar,len+blen+1);
7056 	strcpy(sofar+len,buffer);
7057 	len += blen;
7058     }
7059     if ( len>0 && sofar[len-1]=='\n' )
7060 	sofar[len-1] = '\0';
7061 return( sofar );
7062 }
7063 
MMInferStuff(MMSet * mm)7064 static void MMInferStuff(MMSet *mm) {
7065     int i,j;
7066 
7067     if ( mm==NULL )
7068 return;
7069     if ( mm->apple ) {
7070 	for ( i=0; i<mm->axis_count; ++i ) {
7071 	    for ( j=0; j<mm->axismaps[i].points; ++j ) {
7072 		real val = mm->axismaps[i].blends[j];
7073 		if ( val == -1. )
7074 		    mm->axismaps[i].min = mm->axismaps[i].designs[j];
7075 		else if ( val==0 )
7076 		    mm->axismaps[i].def = mm->axismaps[i].designs[j];
7077 		else if ( val==1 )
7078 		    mm->axismaps[i].max = mm->axismaps[i].designs[j];
7079 	    }
7080 	}
7081     }
7082 }
7083 
SFDSizeMap(EncMap * map,int glyphcnt,int enccnt)7084 static void SFDSizeMap(EncMap *map,int glyphcnt,int enccnt) {
7085     if ( glyphcnt>map->backmax ) {
7086 	map->backmap = realloc(map->backmap,glyphcnt*sizeof(int));
7087 	memset(map->backmap+map->backmax,-1,(glyphcnt-map->backmax)*sizeof(int));
7088 	map->backmax = glyphcnt;
7089     }
7090     if ( enccnt>map->encmax ) {
7091 	map->map = realloc(map->map,enccnt*sizeof(int));
7092 	memset(map->map+map->backmax,-1,(enccnt-map->encmax)*sizeof(int));
7093 	map->encmax = map->enccount = enccnt;
7094     }
7095 }
7096 
7097 static SplineFont *SFD_GetFont(FILE *sfd,SplineFont *cidmaster,char *tok,
7098 	int fromdir, char *dirname, float sfdversion);
7099 
SFD_FigureDirType(SplineFont * sf,char * tok,char * dirname,Encoding * enc,struct remap * remap,int had_layer_cnt)7100 static SplineFont *SFD_FigureDirType(SplineFont *sf,char *tok, char *dirname,
7101 	Encoding *enc, struct remap *remap,int had_layer_cnt) {
7102     /* In a sfdir a directory will either contain glyph files */
7103     /*                                            subfont dirs */
7104     /*                                            instance dirs */
7105     /* (or bitmap files, but we don't care about them here */
7106     /* It will not contain some glyph and some subfont nor instance files */
7107     int gc=0, sc=0, ic=0, bc=0;
7108     DIR *dir;
7109     struct dirent *ent;
7110     char *name, *props, *pt;
7111 
7112     dir = opendir(dirname);
7113     if ( dir==NULL )
7114 return( sf );
7115     sf->save_to_dir = true;
7116     while ( (ent=readdir(dir))!=NULL ) {
7117 	pt = strrchr(ent->d_name,EXT_CHAR);
7118 	if ( pt==NULL )
7119 	    /* Nothing interesting */;
7120 	else if ( strcmp(pt,GLYPH_EXT)==0 )
7121 	    ++gc;
7122 	else if ( strcmp(pt,SUBFONT_EXT)==0 )
7123 	    ++sc;
7124 	else if ( strcmp(pt,INSTANCE_EXT)==0 )
7125 	    ++ic;
7126 	else if ( strcmp(pt,STRIKE_EXT)==0 )
7127 	    ++bc;
7128     }
7129     rewinddir(dir);
7130     name = malloc(strlen(dirname)+NAME_MAX+3);
7131     props = malloc(strlen(dirname)+2*NAME_MAX+4);
7132     if ( gc!=0 ) {
7133 	sf->glyphcnt = 0;
7134 	sf->glyphmax = gc;
7135 	sf->glyphs = calloc(gc,sizeof(SplineChar *));
7136 	ff_progress_change_total(gc);
7137 	if ( sf->cidmaster!=NULL ) {
7138 	    sf->map = sf->cidmaster->map;
7139 	} else {
7140 	    sf->map = EncMapNew(enc->char_cnt>gc?enc->char_cnt:gc,gc,enc);
7141 	    sf->map->remap = remap;
7142 	}
7143 	SFDSizeMap(sf->map,sf->glyphcnt,enc->char_cnt>gc?enc->char_cnt:gc);
7144 
7145 	while ( (ent=readdir(dir))!=NULL ) {
7146 	    pt = strrchr(ent->d_name,EXT_CHAR);
7147 	    if ( pt==NULL )
7148 		/* Nothing interesting */;
7149 	    else if ( strcmp(pt,GLYPH_EXT)==0 ) {
7150 		FILE *gsfd;
7151 		sprintf(name,"%s/%s", dirname, ent->d_name);
7152 		gsfd = fopen(name,"r");
7153 		if ( gsfd!=NULL ) {
7154 		    SFDGetChar(gsfd,sf,had_layer_cnt);
7155 		    ff_progress_next();
7156 		    fclose(gsfd);
7157 		}
7158 	    }
7159 	}
7160 	ff_progress_next_stage();
7161     } else if ( sc!=0 ) {
7162 	int i=0;
7163 	sf->subfontcnt = sc;
7164 	sf->subfonts = calloc(sf->subfontcnt,sizeof(SplineFont *));
7165 	sf->map = EncMap1to1(1000);
7166 	ff_progress_change_stages(2*sc);
7167 
7168 	while ( (ent=readdir(dir))!=NULL ) {
7169 	    pt = strrchr(ent->d_name,EXT_CHAR);
7170 	    if ( pt==NULL )
7171 		/* Nothing interesting */;
7172 	    else if ( strcmp(pt,SUBFONT_EXT)==0 && i<sc ) {
7173 		FILE *ssfd;
7174 		sprintf(name,"%s/%s", dirname, ent->d_name);
7175 		sprintf(props,"%s/" FONT_PROPS, name);
7176 		ssfd = fopen(props,"r");
7177 		if ( ssfd!=NULL ) {
7178 		    if ( i!=0 )
7179 			ff_progress_next_stage();
7180 		    sf->subfonts[i++] = SFD_GetFont(ssfd,sf,tok,true,name,sf->sfd_version);
7181 		    fclose(ssfd);
7182 		}
7183 	    }
7184 	}
7185     } else if ( ic!=0 ) {
7186 	MMSet *mm = sf->mm;
7187 	int ipos, i=0;
7188 
7189 	MMInferStuff(sf->mm);
7190 	ff_progress_change_stages(2*(mm->instance_count+1));
7191 	while ( (ent=readdir(dir))!=NULL ) {
7192 	    pt = strrchr(ent->d_name,EXT_CHAR);
7193 	    if ( pt==NULL )
7194 		/* Nothing interesting */;
7195 	    else if ( strcmp(pt,INSTANCE_EXT)==0 && sscanf( ent->d_name, "mm%d", &ipos)==1 ) {
7196 		FILE *ssfd;
7197 		if ( i!=0 )
7198 		    ff_progress_next_stage();
7199 		sprintf(name,"%s/%s", dirname, ent->d_name);
7200 		sprintf(props,"%s/" FONT_PROPS, name);
7201 		ssfd = fopen(props,"r");
7202 		if ( ssfd!=NULL ) {
7203 		    SplineFont *mmsf;
7204 		    mmsf = SFD_GetFont(ssfd,NULL,tok,true,name,sf->sfd_version);
7205 		    if ( ipos!=0 ) {
7206 			EncMapFree(mmsf->map);
7207 			mmsf->map=NULL;
7208 		    }
7209 		    mmsf->mm = mm;
7210 		    if ( ipos == 0 )
7211 			mm->normal = mmsf;
7212 		    else
7213 			mm->instances[ipos-1] = mmsf;
7214 		    fclose(ssfd);
7215 		}
7216 	    }
7217 	}
7218 	ff_progress_next_stage();
7219 	sf->mm = NULL;
7220 	SplineFontFree(sf);
7221 	sf = mm->normal;
7222 	if ( sf->map->enc!=&custom ) {
7223 	    EncMap *map;
7224 	    MMMatchGlyphs(mm);		/* sfd files from before the encoding change can have mismatched orig pos */
7225 	    map = EncMapFromEncoding(sf,sf->map->enc);
7226 	    EncMapFree(sf->map);
7227 	    sf->map = map;
7228 	}
7229     }
7230 
7231     if ( bc!=0 ) {
7232 	rewinddir(dir);
7233 	while ( (ent=readdir(dir))!=NULL ) {
7234 	    pt = strrchr(ent->d_name,EXT_CHAR);
7235 	    if ( pt==NULL )
7236 		/* Nothing interesting */;
7237 	    else if ( strcmp(pt,STRIKE_EXT)==0 ) {
7238 		FILE *ssfd;
7239 		sprintf(name,"%s/%s", dirname, ent->d_name);
7240 		sprintf(props,"%s/" STRIKE_PROPS, name);
7241 		ssfd = fopen(props,"r");
7242 		if ( ssfd!=NULL ) {
7243 		    if ( getname(ssfd,tok)==1 && strcmp(tok,"BitmapFont:")==0 )
7244 			SFDGetBitmapFont(ssfd,sf,true,name);
7245 		    fclose(ssfd);
7246 		}
7247 	    }
7248 	}
7249 	SFOrderBitmapList(sf);
7250     }
7251     closedir(dir);
7252     free(name);
7253     free(props);
7254 return( sf );
7255 }
7256 
SFD_DoAltUnis(SplineFont * sf)7257 static void SFD_DoAltUnis(SplineFont *sf) {
7258     int i;
7259     struct altuni *alt;
7260     SplineChar *sc;
7261 
7262     for ( i=0; i<sf->glyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL ) {
7263 	for ( alt = sc->altuni; alt!=NULL; alt = alt->next ) {
7264 	    if ( alt->vs==-1 && alt->fid==0 ) {
7265 		int enc = EncFromUni(alt->unienc,sf->map->enc);
7266 		if ( enc!=-1 )
7267 		    SFDSetEncMap(sf,sc->orig_pos,enc);
7268 	    }
7269 	}
7270     }
7271 }
7272 
SFDParseLookup(FILE * sfd,OTLookup * otl)7273 static void SFDParseLookup(FILE *sfd,OTLookup *otl) {
7274     int ch;
7275     struct lookup_subtable *sub, *lastsub;
7276     FeatureScriptLangList *fl, *lastfl;
7277     struct scriptlanglist *sl, *lastsl;
7278     int i, lcnt, lmax=0;
7279     uint32 *langs=NULL;
7280     char *subname;
7281 
7282     while ( (ch=nlgetc(sfd))==' ' );
7283     if ( ch=='{' ) {
7284 	lastsub = NULL;
7285 	while ( (subname = SFDReadUTF7Str(sfd))!=NULL ) {
7286 	    while ( (ch=nlgetc(sfd))==' ' );
7287 	    ungetc(ch,sfd);
7288 	    sub = chunkalloc(sizeof(struct lookup_subtable));
7289 	    sub->subtable_name = subname;
7290 	    sub->lookup = otl;
7291 	    switch ( otl->lookup_type ) {
7292 	      case gsub_single:
7293 		while ( (ch=nlgetc(sfd))==' ' );
7294 		if ( ch=='(' ) {
7295 		    sub->suffix = SFDReadUTF7Str(sfd);
7296 		    while ( (ch=nlgetc(sfd))==' ' );
7297 			/* slurp final paren */
7298 		} else
7299 		    ungetc(ch,sfd);
7300 		sub->per_glyph_pst_or_kern = true;
7301 	      break;
7302 	      case gsub_multiple: case gsub_alternate: case gsub_ligature:
7303 	      case gpos_single:
7304 		sub->per_glyph_pst_or_kern = true;
7305 	      break;
7306 	      case gpos_pair:
7307 		if ( (ch=nlgetc(sfd))=='(' ) {
7308 		    ch = nlgetc(sfd);
7309 		    sub->vertical_kerning = (ch=='1');
7310 		    nlgetc(sfd);	/* slurp final paren */
7311 		    ch=nlgetc(sfd);
7312 		}
7313 		if ( ch=='[' ) {
7314 		    getsint(sfd,&sub->separation);
7315 		    nlgetc(sfd);	/* slurp comma */
7316 		    getsint(sfd,&sub->minkern);
7317 		    nlgetc(sfd);	/* slurp comma */
7318 		    ch = nlgetc(sfd);
7319 		    sub->kerning_by_touch = ((ch-'0')&1)?1:0;
7320 		    sub->onlyCloser       = ((ch-'0')&2)?1:0;
7321 		    sub->dontautokern     = ((ch-'0')&4)?1:0;
7322 		    nlgetc(sfd);	/* slurp final bracket */
7323 		} else {
7324 		    ungetc(ch,sfd);
7325 		}
7326 		sub->per_glyph_pst_or_kern = true;
7327 	      break;
7328 	      case gpos_cursive: case gpos_mark2base: case gpos_mark2ligature: case gpos_mark2mark:
7329 		sub->anchor_classes = true;
7330 	      break;
7331 	      default:
7332 	      break;
7333 	    }
7334 	    if ( lastsub==NULL )
7335 		otl->subtables = sub;
7336 	    else
7337 		lastsub->next = sub;
7338 	    lastsub = sub;
7339 	}
7340 	while ( (ch=nlgetc(sfd))==' ' );
7341 	if ( ch=='}' )
7342 	    ch = nlgetc(sfd);
7343     }
7344     while ( ch==' ' )
7345 	ch = nlgetc(sfd);
7346     if ( ch=='[' ) {
7347 	lastfl = NULL;
7348 	for (;;) {
7349 	    while ( (ch=nlgetc(sfd))==' ' );
7350 	    if ( ch==']' )
7351 	break;
7352 	    fl = chunkalloc(sizeof(FeatureScriptLangList));
7353 	    if ( lastfl==NULL )
7354 		otl->features = fl;
7355 	    else
7356 		lastfl->next = fl;
7357 	    lastfl = fl;
7358 	    if ( ch=='<' ) {
7359 		int ft=0,fs=0;
7360 		fscanf(sfd,"%d,%d>", &ft, &fs );
7361 		fl->ismac = true;
7362 		fl->featuretag = (ft<<16) | fs;
7363 	    } else if ( ch=='\'' ) {
7364 		ungetc(ch,sfd);
7365 		fl->featuretag = gettag(sfd);
7366 	    }
7367 	    while ( (ch=nlgetc(sfd))==' ' );
7368 	    if ( ch=='(' ) {
7369 		lastsl = NULL;
7370 		for (;;) {
7371 		    while ( (ch=nlgetc(sfd))==' ' );
7372 		    if ( ch==')' )
7373 		break;
7374 		    sl = chunkalloc(sizeof(struct scriptlanglist));
7375 		    if ( lastsl==NULL )
7376 			fl->scripts = sl;
7377 		    else
7378 			lastsl->next = sl;
7379 		    lastsl = sl;
7380 		    if ( ch=='\'' ) {
7381 			ungetc(ch,sfd);
7382 			sl->script = gettag(sfd);
7383 		    }
7384 		    while ( (ch=nlgetc(sfd))==' ' );
7385 		    if ( ch=='<' ) {
7386 			lcnt = 0;
7387 			for (;;) {
7388 			    while ( (ch=nlgetc(sfd))==' ' );
7389 			    if ( ch=='>' )
7390 			break;
7391 			    if ( ch=='\'' ) {
7392 				ungetc(ch,sfd);
7393 			        if ( lcnt>=lmax )
7394 				    langs = realloc(langs,(lmax+=10)*sizeof(uint32));
7395 				langs[lcnt++] = gettag(sfd);
7396 			    }
7397 			}
7398 			sl->lang_cnt = lcnt;
7399 			if ( lcnt>MAX_LANG )
7400 			    sl->morelangs = malloc((lcnt-MAX_LANG)*sizeof(uint32));
7401 			for ( i=0; i<lcnt; ++i ) {
7402 			    if ( i<MAX_LANG )
7403 				sl->langs[i] = langs[i];
7404 			    else
7405 				sl->morelangs[i-MAX_LANG] = langs[i];
7406 			}
7407 		    }
7408 		}
7409 	    }
7410 	}
7411     }
7412     free(langs);
7413 }
7414 
SFDParseMathItem(FILE * sfd,SplineFont * sf,char * tok)7415 static void SFDParseMathItem(FILE *sfd,SplineFont *sf,char *tok) {
7416     /* The first five characters of a math item's keyword will be "MATH:" */
7417     /*  the rest will be one of the entries in math_constants_descriptor */
7418     int i;
7419     struct MATH *math;
7420 
7421     if ( (math = sf->MATH) == NULL )
7422 	math = sf->MATH = calloc(1,sizeof(struct MATH));
7423     for ( i=0; math_constants_descriptor[i].script_name!=NULL; ++i ) {
7424 	char *name = math_constants_descriptor[i].script_name;
7425 	int len = strlen( name );
7426 	if ( strncmp(tok+5,name,len)==0 && tok[5+len] == ':' && tok[6+len]=='\0' ) {
7427 	    int16 *pos = (int16 *) (((char *) (math)) + math_constants_descriptor[i].offset );
7428 	    getsint(sfd,pos);
7429 	    if ( math_constants_descriptor[i].devtab_offset != -1 ) {
7430 		DeviceTable **devtab = (DeviceTable **) (((char *) (math)) + math_constants_descriptor[i].devtab_offset );
7431 		*devtab = SFDReadDeviceTable(sfd,*devtab);
7432     break;
7433 	    }
7434 	}
7435     }
7436 }
7437 
ParseBaseLang(FILE * sfd)7438 static struct baselangextent *ParseBaseLang(FILE *sfd) {
7439     struct baselangextent *bl;
7440     struct baselangextent *cur, *last;
7441     int ch;
7442 
7443     while ( (ch=nlgetc(sfd))==' ' );
7444     if ( ch=='{' ) {
7445 	bl = chunkalloc(sizeof(struct baselangextent));
7446 	while ( (ch=nlgetc(sfd))==' ' );
7447 	ungetc(ch,sfd);
7448 	if ( ch=='\'' )
7449 	    bl->lang = gettag(sfd);		/* Lang or Feature tag, or nothing */
7450 	getsint(sfd,&bl->descent);
7451 	getsint(sfd,&bl->ascent);
7452 	last = NULL;
7453 	while ( (ch=nlgetc(sfd))==' ' );
7454 	while ( ch=='{' ) {
7455 	    ungetc(ch,sfd);
7456 	    cur = ParseBaseLang(sfd);
7457 	    if ( last==NULL )
7458 		bl->features = cur;
7459 	    else
7460 		last->next = cur;
7461 	    last = cur;
7462 	    while ( (ch=nlgetc(sfd))==' ' );
7463 	}
7464 	if ( ch!='}' ) ungetc(ch,sfd);
7465 return( bl );
7466     }
7467 return( NULL );
7468 }
7469 
SFDParseBaseScript(FILE * sfd,struct Base * base)7470 static struct basescript *SFDParseBaseScript(FILE *sfd,struct Base *base) {
7471     struct basescript *bs;
7472     int i, ch;
7473     struct baselangextent *last, *cur;
7474 
7475     if ( base==NULL )
7476 return(NULL);
7477 
7478     bs = chunkalloc(sizeof(struct basescript));
7479 
7480     bs->script = gettag(sfd);
7481     getint(sfd,&bs->def_baseline);
7482     if ( base->baseline_cnt!=0 ) {
7483 	bs->baseline_pos = calloc(base->baseline_cnt,sizeof(int16));
7484 	for ( i=0; i<base->baseline_cnt; ++i )
7485 	    getsint(sfd, &bs->baseline_pos[i]);
7486     }
7487     while ( (ch=nlgetc(sfd))==' ' );
7488     last = NULL;
7489     while ( ch=='{' ) {
7490 	ungetc(ch,sfd);
7491 	cur = ParseBaseLang(sfd);
7492 	if ( last==NULL )
7493 	    bs->langs = cur;
7494 	else
7495 	    last->next = cur;
7496 	last = cur;
7497 	while ( (ch=nlgetc(sfd))==' ' );
7498     }
7499 return( bs );
7500 }
7501 
SFDParseBase(FILE * sfd)7502 static struct Base *SFDParseBase(FILE *sfd) {
7503     struct Base *base = chunkalloc(sizeof(struct Base));
7504     int i;
7505 
7506     getint(sfd,&base->baseline_cnt);
7507     if ( base->baseline_cnt!=0 ) {
7508 	base->baseline_tags = malloc(base->baseline_cnt*sizeof(uint32));
7509 	for ( i=0; i<base->baseline_cnt; ++i )
7510 	    base->baseline_tags[i] = gettag(sfd);
7511     }
7512 return( base );
7513 }
7514 
SFDLookupList(FILE * sfd,SplineFont * sf)7515 static OTLookup **SFDLookupList(FILE *sfd,SplineFont *sf) {
7516     int ch;
7517     OTLookup **ret=NULL, *otl;
7518     int lcnt=0, lmax=0;
7519     char *name;
7520 
7521     for (;;) {
7522 	while ( (ch=nlgetc(sfd))==' ' );
7523 	if ( ch=='\n' || ch==EOF )
7524     break;
7525 	ungetc(ch,sfd);
7526 	name = SFDReadUTF7Str(sfd);
7527 	otl = SFFindLookup(sf,name);
7528 	free(name);
7529 	if ( otl!=NULL ) {
7530 	    if ( lcnt>=lmax ) {
7531 	        lmax += 100;
7532 	        ret = realloc(ret, lmax * sizeof(OTLookup *));
7533 	    }
7534 	    ret[lcnt++] = otl;
7535 	}
7536     }
7537     if ( lcnt==0 )
7538 return( NULL );
7539     ret = realloc(ret, (lcnt+1) * sizeof(OTLookup *));
7540     ret[lcnt] = NULL;
7541 return( ret );
7542 }
7543 
SFDParseJustify(FILE * sfd,SplineFont * sf,char * tok)7544 static void SFDParseJustify(FILE *sfd, SplineFont *sf, char *tok) {
7545     Justify *last=NULL, *cur;
7546     struct jstf_lang *jlang, *llast;
7547     int p = 0,ch;
7548 
7549     while ( strcmp(tok,"Justify:")==0 ) {
7550 	cur = chunkalloc(sizeof(Justify));
7551 	if ( last==NULL )
7552 	    sf->justify = cur;
7553 	else
7554 	    last->next = cur;
7555 	last = cur;
7556 	llast = jlang = NULL;
7557 	cur->script = gettag(sfd);
7558 	while ( getname(sfd,tok)>0 ) {
7559 	    if ( strcmp(tok,"Justify:")==0 || strcmp(tok,"EndJustify")==0 )
7560 	break;
7561 	    if ( strcmp(tok,"JstfExtender:")==0 ) {
7562 		while ( (ch=nlgetc(sfd))==' ' );
7563 		ungetc(ch,sfd);
7564 		geteol(sfd,tok);
7565 		cur->extenders = copy(tok);
7566 	    } else if ( strcmp(tok,"JstfLang:")==0 ) {
7567 		jlang = chunkalloc(sizeof(struct jstf_lang));
7568 		if ( llast==NULL )
7569 		    cur->langs = jlang;
7570 		else
7571 		    llast->next = jlang;
7572 		llast = jlang;
7573 		jlang->lang = gettag(sfd);
7574 		p = -1;
7575 		getint(sfd,&jlang->cnt);
7576 		if ( jlang->cnt!=0 )
7577 		    jlang->prios = calloc(jlang->cnt,sizeof(struct jstf_prio));
7578 	    } else if ( strcmp(tok,"JstfPrio:")==0 ) {
7579 		if ( jlang!=NULL ) {
7580 		    ++p;
7581 		    if ( p>= jlang->cnt ) {
7582 			jlang->prios = realloc(jlang->prios,(p+1)*sizeof(struct jstf_prio));
7583 			memset(jlang->prios+jlang->cnt,0,(p+1-jlang->cnt)*sizeof(struct jstf_prio));
7584 			jlang->cnt = p+1;
7585 		    }
7586 		}
7587 	    } else if ( strcmp(tok,"JstfEnableShrink:" )==0 ) {
7588 		if ( p<0 ) p=0;
7589 		if ( jlang!=NULL && p<jlang->cnt )
7590 		    jlang->prios[p].enableShrink = SFDLookupList(sfd,sf);
7591 	    } else if ( strcmp(tok,"JstfDisableShrink:" )==0 ) {
7592 		if ( p<0 ) p=0;
7593 		if ( jlang!=NULL && p<jlang->cnt )
7594 		    jlang->prios[p].disableShrink = SFDLookupList(sfd,sf);
7595 	    } else if ( strcmp(tok,"JstfMaxShrink:" )==0 ) {
7596 		if ( p<0 ) p=0;
7597 		if ( jlang!=NULL && p<jlang->cnt )
7598 		    jlang->prios[p].maxShrink = SFDLookupList(sfd,sf);
7599 	    } else if ( strcmp(tok,"JstfEnableExtend:" )==0 ) {
7600 		if ( p<0 ) p=0;
7601 		if ( jlang!=NULL && p<jlang->cnt )
7602 		    jlang->prios[p].enableExtend = SFDLookupList(sfd,sf);
7603 	    } else if ( strcmp(tok,"JstfDisableExtend:" )==0 ) {
7604 		if ( p<0 ) p=0;
7605 		if ( jlang!=NULL && p<jlang->cnt )
7606 		    jlang->prios[p].disableExtend = SFDLookupList(sfd,sf);
7607 	    } else if ( strcmp(tok,"JstfMaxExtend:" )==0 ) {
7608 		if ( p<0 ) p=0;
7609 		if ( jlang!=NULL && p<jlang->cnt )
7610 		    jlang->prios[p].maxExtend = SFDLookupList(sfd,sf);
7611 	    } else
7612 		geteol(sfd,tok);
7613 	}
7614     }
7615 }
7616 
7617 
7618 
SFD_GetFontMetaDataData_Init(SFD_GetFontMetaDataData * d)7619 void SFD_GetFontMetaDataData_Init( SFD_GetFontMetaDataData* d )
7620 {
7621     memset( d, 0, sizeof(SFD_GetFontMetaDataData));
7622 }
7623 
7624 /**
7625  *
7626  * @return true if the function matched the current token. If true
7627  *         is returned the caller should avoid further processing of 'tok'
7628  *         a return of false means that the caller might try
7629  *         to handle the token with another function or drop it.
7630  */
SFD_GetFontMetaData(FILE * sfd,char * tok,SplineFont * sf,SFD_GetFontMetaDataData * d)7631 bool SFD_GetFontMetaData( FILE *sfd,
7632 			  char *tok,
7633 			  SplineFont *sf,
7634 			  SFD_GetFontMetaDataData* d )
7635 {
7636     int ch;
7637     int i;
7638     KernClass* kc = 0;
7639     int old;
7640     char val[2000];
7641 
7642     // This allows us to assume we can dereference d
7643     // at all times
7644     static SFD_GetFontMetaDataData my_static_d;
7645     static int my_static_d_is_virgin = 1;
7646     if( !d )
7647     {
7648 	if( my_static_d_is_virgin )
7649 	{
7650 	    my_static_d_is_virgin = 0;
7651 	    SFD_GetFontMetaDataData_Init( &my_static_d );
7652 	}
7653 	d = &my_static_d;
7654     }
7655 
7656     if ( strmatch(tok,"FontName:")==0 )
7657     {
7658 	geteol(sfd,val);
7659 	sf->fontname = copy(val);
7660     }
7661     else if ( strmatch(tok,"FullName:")==0 )
7662     {
7663 	geteol(sfd,val);
7664 	sf->fullname = copy(val);
7665     }
7666     else if ( strmatch(tok,"FamilyName:")==0 )
7667     {
7668 	geteol(sfd,val);
7669 	sf->familyname = copy(val);
7670     }
7671     else if ( strmatch(tok,"DefaultBaseFilename:")==0 )
7672     {
7673 	geteol(sfd,val);
7674 	sf->defbasefilename = copy(val);
7675     }
7676     else if ( strmatch(tok,"Weight:")==0 )
7677     {
7678 	getprotectedname(sfd,val);
7679 	sf->weight = copy(val);
7680     }
7681     else if ( strmatch(tok,"Copyright:")==0 )
7682     {
7683 	sf->copyright = getquotedeol(sfd);
7684     }
7685     else if ( strmatch(tok,"Comments:")==0 )
7686     {
7687 	char *temp = getquotedeol(sfd);
7688 	sf->comments = latin1_2_utf8_copy(temp);
7689 	free(temp);
7690     }
7691     else if ( strmatch(tok,"UComments:")==0 )
7692     {
7693 	sf->comments = SFDReadUTF7Str(sfd);
7694     }
7695     else if ( strmatch(tok,"FontLog:")==0 )
7696     {
7697 	sf->fontlog = SFDReadUTF7Str(sfd);
7698     }
7699     else if ( strmatch(tok,"Version:")==0 )
7700     {
7701 	geteol(sfd,val);
7702 	sf->version = copy(val);
7703     }
7704     else if ( strmatch(tok,"StyleMapFamilyName:")==0 )
7705     {
7706     sf->styleMapFamilyName = SFDReadUTF7Str(sfd);
7707     }
7708     /* Legacy attribute for StyleMapFamilyName. Deprecated. */
7709     else if ( strmatch(tok,"OS2FamilyName:")==0 )
7710     {
7711     if (sf->styleMapFamilyName == NULL)
7712         sf->styleMapFamilyName = SFDReadUTF7Str(sfd);
7713     }
7714     else if ( strmatch(tok,"FONDName:")==0 )
7715     {
7716 	geteol(sfd,val);
7717 	sf->fondname = copy(val);
7718     }
7719     else if ( strmatch(tok,"ItalicAngle:")==0 )
7720     {
7721 	getreal(sfd,&sf->italicangle);
7722     }
7723     else if ( strmatch(tok,"StrokeWidth:")==0 )
7724     {
7725 	getreal(sfd,&sf->strokewidth);
7726     }
7727     else if ( strmatch(tok,"UnderlinePosition:")==0 )
7728     {
7729 	getreal(sfd,&sf->upos);
7730     }
7731     else if ( strmatch(tok,"UnderlineWidth:")==0 )
7732     {
7733 	getreal(sfd,&sf->uwidth);
7734     }
7735     else if ( strmatch(tok,"ModificationTime:")==0 )
7736     {
7737 	getlonglong(sfd,&sf->modificationtime);
7738     }
7739     else if ( strmatch(tok,"CreationTime:")==0 )
7740     {
7741 	getlonglong(sfd,&sf->creationtime);
7742 	d->hadtimes = true;
7743     }
7744     else if ( strmatch(tok,"PfmFamily:")==0 )
7745     {
7746 	int temp;
7747 	getint(sfd,&temp);
7748 	sf->pfminfo.pfmfamily = temp;
7749 	sf->pfminfo.pfmset = true;
7750     }
7751     else if ( strmatch(tok,"LangName:")==0 )
7752     {
7753 	sf->names = SFDGetLangName(sfd,sf->names);
7754     }
7755     else if ( strmatch(tok,"GaspTable:")==0 )
7756     {
7757 	SFDGetGasp(sfd,sf);
7758     }
7759     else if ( strmatch(tok,"DesignSize:")==0 )
7760     {
7761 	SFDGetDesignSize(sfd,sf);
7762     }
7763     else if ( strmatch(tok,"OtfFeatName:")==0 )
7764     {
7765 	SFDGetOtfFeatName(sfd,sf);
7766     }
7767     else if ( strmatch(tok,"PfmWeight:")==0 || strmatch(tok,"TTFWeight:")==0 )
7768     {
7769 	getsint(sfd,&sf->pfminfo.weight);
7770 	sf->pfminfo.pfmset = true;
7771     }
7772     else if ( strmatch(tok,"TTFWidth:")==0 )
7773     {
7774 	getsint(sfd,&sf->pfminfo.width);
7775 	sf->pfminfo.pfmset = true;
7776     }
7777     else if ( strmatch(tok,"Panose:")==0 )
7778     {
7779 	int temp,i;
7780 	for ( i=0; i<10; ++i )
7781 	{
7782 	    getint(sfd,&temp);
7783 	    sf->pfminfo.panose[i] = temp;
7784 	}
7785 	sf->pfminfo.panose_set = true;
7786     }
7787     else if ( strmatch(tok,"LineGap:")==0 )
7788     {
7789 	getsint(sfd,&sf->pfminfo.linegap);
7790 	sf->pfminfo.pfmset = true;
7791     }
7792     else if ( strmatch(tok,"VLineGap:")==0 )
7793     {
7794 	getsint(sfd,&sf->pfminfo.vlinegap);
7795 	sf->pfminfo.pfmset = true;
7796     }
7797     else if ( strmatch(tok,"HheadAscent:")==0 )
7798     {
7799 	getsint(sfd,&sf->pfminfo.hhead_ascent);
7800     }
7801     else if ( strmatch(tok,"HheadAOffset:")==0 )
7802     {
7803 	int temp;
7804 	getint(sfd,&temp); sf->pfminfo.hheadascent_add = temp;
7805     }
7806     else if ( strmatch(tok,"HheadDescent:")==0 )
7807     {
7808 	getsint(sfd,&sf->pfminfo.hhead_descent);
7809     }
7810     else if ( strmatch(tok,"HheadDOffset:")==0 )
7811     {
7812 	int temp;
7813 	getint(sfd,&temp); sf->pfminfo.hheaddescent_add = temp;
7814     }
7815     else if ( strmatch(tok,"OS2TypoLinegap:")==0 )
7816     {
7817 	getsint(sfd,&sf->pfminfo.os2_typolinegap);
7818     }
7819     else if ( strmatch(tok,"OS2TypoAscent:")==0 )
7820     {
7821 	getsint(sfd,&sf->pfminfo.os2_typoascent);
7822     }
7823     else if ( strmatch(tok,"OS2TypoAOffset:")==0 )
7824     {
7825 	int temp;
7826 	getint(sfd,&temp); sf->pfminfo.typoascent_add = temp;
7827     }
7828     else if ( strmatch(tok,"OS2TypoDescent:")==0 )
7829     {
7830 	getsint(sfd,&sf->pfminfo.os2_typodescent);
7831     }
7832     else if ( strmatch(tok,"OS2TypoDOffset:")==0 )
7833     {
7834 	int temp;
7835 	getint(sfd,&temp); sf->pfminfo.typodescent_add = temp;
7836     }
7837     else if ( strmatch(tok,"OS2WinAscent:")==0 )
7838     {
7839 	getsint(sfd,&sf->pfminfo.os2_winascent);
7840     }
7841     else if ( strmatch(tok,"OS2WinDescent:")==0 )
7842     {
7843 	getsint(sfd,&sf->pfminfo.os2_windescent);
7844     }
7845     else if ( strmatch(tok,"OS2WinAOffset:")==0 )
7846     {
7847 	int temp;
7848 	getint(sfd,&temp); sf->pfminfo.winascent_add = temp;
7849     }
7850     else if ( strmatch(tok,"OS2WinDOffset:")==0 )
7851     {
7852 	int temp;
7853 	getint(sfd,&temp); sf->pfminfo.windescent_add = temp;
7854     }
7855     else if ( strmatch(tok,"HHeadAscent:")==0 )
7856     {
7857 	// DUPLICATE OF ABOVE
7858 	getsint(sfd,&sf->pfminfo.hhead_ascent);
7859     }
7860     else if ( strmatch(tok,"HHeadDescent:")==0 )
7861     {
7862 	// DUPLICATE OF ABOVE
7863 	getsint(sfd,&sf->pfminfo.hhead_descent);
7864     }
7865 
7866     else if ( strmatch(tok,"HHeadAOffset:")==0 )
7867     {
7868 	// DUPLICATE OF ABOVE
7869 	int temp;
7870 	getint(sfd,&temp); sf->pfminfo.hheadascent_add = temp;
7871     }
7872     else if ( strmatch(tok,"HHeadDOffset:")==0 )
7873     {
7874 	// DUPLICATE OF ABOVE
7875 	int temp;
7876 	getint(sfd,&temp); sf->pfminfo.hheaddescent_add = temp;
7877     }
7878     else if ( strmatch(tok,"MacStyle:")==0 )
7879     {
7880 	getsint(sfd,&sf->macstyle);
7881     }
7882     else if ( strmatch(tok,"OS2SubXSize:")==0 )
7883     {
7884 	getsint(sfd,&sf->pfminfo.os2_subxsize);
7885 	sf->pfminfo.subsuper_set = true;
7886     }
7887     else if ( strmatch(tok,"OS2SubYSize:")==0 )
7888     {
7889 	getsint(sfd,&sf->pfminfo.os2_subysize);
7890     }
7891     else if ( strmatch(tok,"OS2SubXOff:")==0 )
7892     {
7893 	getsint(sfd,&sf->pfminfo.os2_subxoff);
7894     }
7895     else if ( strmatch(tok,"OS2SubYOff:")==0 )
7896     {
7897 	getsint(sfd,&sf->pfminfo.os2_subyoff);
7898     }
7899     else if ( strmatch(tok,"OS2SupXSize:")==0 )
7900     {
7901 	getsint(sfd,&sf->pfminfo.os2_supxsize);
7902     }
7903     else if ( strmatch(tok,"OS2SupYSize:")==0 )
7904     {
7905 	getsint(sfd,&sf->pfminfo.os2_supysize);
7906     }
7907     else if ( strmatch(tok,"OS2SupXOff:")==0 )
7908     {
7909 	getsint(sfd,&sf->pfminfo.os2_supxoff);
7910     }
7911     else if ( strmatch(tok,"OS2SupYOff:")==0 )
7912     {
7913 	getsint(sfd,&sf->pfminfo.os2_supyoff);
7914     }
7915     else if ( strmatch(tok,"OS2StrikeYSize:")==0 )
7916     {
7917 	getsint(sfd,&sf->pfminfo.os2_strikeysize);
7918     }
7919     else if ( strmatch(tok,"OS2StrikeYPos:")==0 )
7920     {
7921 	getsint(sfd,&sf->pfminfo.os2_strikeypos);
7922     }
7923     else if ( strmatch(tok,"OS2CapHeight:")==0 )
7924     {
7925 	getsint(sfd,&sf->pfminfo.os2_capheight);
7926     }
7927     else if ( strmatch(tok,"OS2XHeight:")==0 )
7928     {
7929 	getsint(sfd,&sf->pfminfo.os2_xheight);
7930     }
7931     else if ( strmatch(tok,"OS2FamilyClass:")==0 )
7932     {
7933 	getsint(sfd,&sf->pfminfo.os2_family_class);
7934     }
7935     else if ( strmatch(tok,"OS2Vendor:")==0 )
7936     {
7937 	while ( isspace(nlgetc(sfd)));
7938 	sf->pfminfo.os2_vendor[0] = nlgetc(sfd);
7939 	sf->pfminfo.os2_vendor[1] = nlgetc(sfd);
7940 	sf->pfminfo.os2_vendor[2] = nlgetc(sfd);
7941 	sf->pfminfo.os2_vendor[3] = nlgetc(sfd);
7942 	(void) nlgetc(sfd);
7943     }
7944     else if ( strmatch(tok,"OS2CodePages:")==0 )
7945     {
7946 	gethexints(sfd,sf->pfminfo.codepages,2);
7947 	sf->pfminfo.hascodepages = true;
7948     }
7949     else if ( strmatch(tok,"OS2UnicodeRanges:")==0 )
7950     {
7951 	gethexints(sfd,sf->pfminfo.unicoderanges,4);
7952 	sf->pfminfo.hasunicoderanges = true;
7953     }
7954     else if ( strmatch(tok,"TopEncoding:")==0 )
7955     {
7956 	/* Obsolete */
7957 	getint(sfd,&sf->top_enc);
7958     }
7959     else if ( strmatch(tok,"Ascent:")==0 )
7960     {
7961 	getint(sfd,&sf->ascent);
7962     }
7963     else if ( strmatch(tok,"Descent:")==0 )
7964     {
7965 	getint(sfd,&sf->descent);
7966     }
7967     else if ( strmatch(tok,"InvalidEm:")==0 )
7968     {
7969 	getint(sfd,&sf->invalidem);
7970     }
7971     else if ( strmatch(tok,"woffMajor:")==0 )
7972     {
7973 	getint(sfd,&sf->woffMajor);
7974     }
7975     else if ( strmatch(tok,"woffMinor:")==0 )
7976     {
7977 	getint(sfd,&sf->woffMinor);
7978     }
7979     else if ( strmatch(tok,"woffMetadata:")==0 )
7980     {
7981 	sf->woffMetadata = SFDReadUTF7Str(sfd);
7982     }
7983     else if ( strmatch(tok,"UFOAscent:")==0 )
7984     {
7985 	    getreal(sfd,&sf->ufo_ascent);
7986     }
7987     else if ( strmatch(tok,"UFODescent:")==0 )
7988     {
7989 	getreal(sfd,&sf->ufo_descent);
7990     }
7991     else if ( strmatch(tok,"sfntRevision:")==0 )
7992     {
7993 	gethex(sfd,(uint32 *)&sf->sfntRevision);
7994     }
7995     else if ( strmatch(tok,"LayerCount:")==0 )
7996     {
7997 	d->had_layer_cnt = true;
7998 	int layer_cnt_tmp;
7999 	getint(sfd,&layer_cnt_tmp);
8000 	if ( layer_cnt_tmp>2 ) {
8001 	    sf->layer_cnt = layer_cnt_tmp;
8002 	    sf->layers = realloc(sf->layers,sf->layer_cnt*sizeof(LayerInfo));
8003 	    memset(sf->layers+2,0,(sf->layer_cnt-2)*sizeof(LayerInfo));
8004 	}
8005     }
8006     else if ( strmatch(tok,"Layer:")==0 )
8007     {
8008         // TODO: Read the U. F. O. path.
8009 	int layer, o2, bk;
8010 	getint(sfd,&layer);
8011 	if ( layer>=sf->layer_cnt ) {
8012 	    sf->layers = realloc(sf->layers,(layer+1)*sizeof(LayerInfo));
8013 	    memset(sf->layers+sf->layer_cnt,0,((layer+1)-sf->layer_cnt)*sizeof(LayerInfo));
8014 	    sf->layer_cnt = layer+1;
8015 	}
8016 	getint(sfd,&o2);
8017 	sf->layers[layer].order2 = o2;
8018 	sf->layers[layer].background = layer==ly_back;
8019 	/* Used briefly, now background is after layer name */
8020 	while ( (ch=nlgetc(sfd))==' ' );
8021 	ungetc(ch,sfd);
8022 	if ( ch!='"' ) {
8023 	    getint(sfd,&bk);
8024 	    sf->layers[layer].background = bk;
8025 	}
8026 	/* end of section for obsolete format */
8027 	sf->layers[layer].name = SFDReadUTF7Str(sfd);
8028 	while ( (ch=nlgetc(sfd))==' ' );
8029 	ungetc(ch,sfd);
8030 	if ( ch!='\n' ) {
8031 	    getint(sfd,&bk);
8032 	    sf->layers[layer].background = bk;
8033 	}
8034 	while ( (ch=nlgetc(sfd))==' ' );
8035 	ungetc(ch,sfd);
8036 	if ( ch!='\n' ) { sf->layers[layer].ufo_path = SFDReadUTF7Str(sfd); }
8037     }
8038     else if ( strmatch(tok,"PreferredKerning:")==0 )
8039     {
8040 	int temp;
8041 	getint(sfd,&temp);
8042 	sf->preferred_kerning = temp;
8043     }
8044     else if ( strmatch(tok,"StrokedFont:")==0 )
8045     {
8046 	int temp;
8047 	getint(sfd,&temp);
8048 	sf->strokedfont = temp;
8049     }
8050     else if ( strmatch(tok,"MultiLayer:")==0 )
8051     {
8052 	int temp;
8053 	getint(sfd,&temp);
8054 	sf->multilayer = temp;
8055     }
8056     else if ( strmatch(tok,"NeedsXUIDChange:")==0 )
8057     {
8058 	int temp;
8059 	getint(sfd,&temp);
8060 	sf->changed_since_xuidchanged = temp;
8061     }
8062     else if ( strmatch(tok,"VerticalOrigin:")==0 )
8063     {
8064 	// this doesn't seem to be written ever.
8065 	int temp;
8066 	getint(sfd,&temp);
8067 	sf->hasvmetrics = true;
8068     }
8069     else if ( strmatch(tok,"HasVMetrics:")==0 )
8070     {
8071 	int temp;
8072 	getint(sfd,&temp);
8073 	sf->hasvmetrics = temp;
8074     }
8075     else if ( strmatch(tok,"Justify:")==0 )
8076     {
8077 	SFDParseJustify(sfd,sf,tok);
8078     }
8079     else if ( strmatch(tok,"BaseHoriz:")==0 )
8080     {
8081 	sf->horiz_base = SFDParseBase(sfd);
8082 	d->last_base = sf->horiz_base;
8083 	d->last_base_script = NULL;
8084     }
8085     else if ( strmatch(tok,"BaseVert:")==0 )
8086     {
8087 	sf->vert_base = SFDParseBase(sfd);
8088 	d->last_base = sf->vert_base;
8089 	d->last_base_script = NULL;
8090     }
8091     else if ( strmatch(tok,"BaseScript:")==0 )
8092     {
8093 	struct basescript *bs = SFDParseBaseScript(sfd,d->last_base);
8094 	if ( d->last_base==NULL )
8095 	{
8096 	    BaseScriptFree(bs);
8097 	    bs = NULL;
8098 	}
8099 	else if ( d->last_base_script!=NULL )
8100 	    d->last_base_script->next = bs;
8101 	else
8102 	    d->last_base->scripts = bs;
8103 	d->last_base_script = bs;
8104     }
8105     else if ( strmatch(tok,"StyleMap:")==0 )
8106     {
8107 	uint32 u;
8108 	gethex(sfd,&u);
8109 	sf->pfminfo.stylemap = u;
8110     }
8111     /* Legacy attribute for StyleMap. Deprecated. */
8112     else if ( strmatch(tok,"OS2StyleName:")==0 )
8113     {
8114     char* sname = SFDReadUTF7Str(sfd);
8115     if (sf->pfminfo.stylemap == -1) {
8116         if (strcmp(sname,"bold italic")==0) sf->pfminfo.stylemap = 0x21;
8117         else if (strcmp(sname,"bold")==0) sf->pfminfo.stylemap = 0x20;
8118         else if (strcmp(sname,"italic")==0) sf->pfminfo.stylemap = 0x01;
8119         else if (strcmp(sname,"regular")==0) sf->pfminfo.stylemap = 0x40;
8120     }
8121     free(sname);
8122     }
8123     else if ( strmatch(tok,"FSType:")==0 )
8124     {
8125 	getsint(sfd,&sf->pfminfo.fstype);
8126     }
8127     else if ( strmatch(tok,"OS2Version:")==0 )
8128     {
8129 	getsint(sfd,&sf->os2_version);
8130     }
8131     else if ( strmatch(tok,"OS2_WeightWidthSlopeOnly:")==0 )
8132     {
8133 	int temp;
8134 	getint(sfd,&temp);
8135 	sf->weight_width_slope_only = temp;
8136     }
8137     else if ( strmatch(tok,"OS2_UseTypoMetrics:")==0 )
8138     {
8139 	int temp;
8140 	getint(sfd,&temp);
8141 	sf->use_typo_metrics = temp;
8142     }
8143     else if ( strmatch(tok,"UseUniqueID:")==0 )
8144     {
8145 	int temp;
8146 	getint(sfd,&temp);
8147 	sf->use_uniqueid = temp;
8148     }
8149     else if ( strmatch(tok,"UseXUID:")==0 )
8150     {
8151 	int temp;
8152 	getint(sfd,&temp);
8153 	sf->use_xuid = temp;
8154     }
8155     else if ( strmatch(tok,"UniqueID:")==0 )
8156     {
8157 	getint(sfd,&sf->uniqueid);
8158     }
8159     else if ( strmatch(tok,"XUID:")==0 )
8160     {
8161 	geteol(sfd,tok);
8162 	sf->xuid = copy(tok);
8163     }
8164     else if ( strmatch(tok,"Lookup:")==0 )
8165     {
8166 	OTLookup *otl;
8167 	int temp;
8168 	if ( sf->sfd_version<2 ) {
8169 	    IError( "Lookups should not happen in version 1 sfd files." );
8170 	    exit(1);
8171 	}
8172 	otl = chunkalloc(sizeof(OTLookup));
8173 	getint(sfd,&temp); otl->lookup_type = temp;
8174 	getint(sfd,&temp); otl->lookup_flags = temp;
8175 	getint(sfd,&temp); otl->store_in_afm = temp;
8176 	otl->lookup_name = SFDReadUTF7Str(sfd);
8177 	if ( otl->lookup_type<gpos_single ) {
8178 	    if ( d->lastsotl==NULL )
8179 		sf->gsub_lookups = otl;
8180 	    else
8181 		d->lastsotl->next = otl;
8182 	    d->lastsotl = otl;
8183 	} else {
8184 	    if ( d->lastpotl==NULL )
8185 		sf->gpos_lookups = otl;
8186 	    else
8187 		d->lastpotl->next = otl;
8188 	    d->lastpotl = otl;
8189 	}
8190 	SFDParseLookup(sfd,otl);
8191     }
8192     else if ( strmatch(tok,"MarkAttachClasses:")==0 )
8193     {
8194 	getint(sfd,&sf->mark_class_cnt);
8195 	sf->mark_classes = malloc(sf->mark_class_cnt*sizeof(char *));
8196 	sf->mark_class_names = malloc(sf->mark_class_cnt*sizeof(char *));
8197 	sf->mark_classes[0] = NULL; sf->mark_class_names[0] = NULL;
8198 	for ( i=1; i<sf->mark_class_cnt; ++i )
8199 	{
8200 	    /* Class 0 is unused */
8201 	    int temp;
8202 	    while ( (temp=nlgetc(sfd))=='\n' || temp=='\r' ); ungetc(temp,sfd);
8203 	    sf->mark_class_names[i] = SFDReadUTF7Str(sfd);
8204 	    getint(sfd,&temp);
8205 	    sf->mark_classes[i] = malloc(temp+1); sf->mark_classes[i][temp] = '\0';
8206 	    nlgetc(sfd);	/* skip space */
8207 	    fread(sf->mark_classes[i],1,temp,sfd);
8208 	}
8209     }
8210     else if ( strmatch(tok,"MarkAttachSets:")==0 )
8211     {
8212 	getint(sfd,&sf->mark_set_cnt);
8213 	sf->mark_sets = malloc(sf->mark_set_cnt*sizeof(char *));
8214 	sf->mark_set_names = malloc(sf->mark_set_cnt*sizeof(char *));
8215 	for ( i=0; i<sf->mark_set_cnt; ++i )
8216 	{
8217 	    /* Set 0 is used */
8218 	    int temp;
8219 	    while ( (temp=nlgetc(sfd))=='\n' || temp=='\r' ); ungetc(temp,sfd);
8220 	    sf->mark_set_names[i] = SFDReadUTF7Str(sfd);
8221 	    getint(sfd,&temp);
8222 	    sf->mark_sets[i] = malloc(temp+1); sf->mark_sets[i][temp] = '\0';
8223 	    nlgetc(sfd);	/* skip space */
8224 	    fread(sf->mark_sets[i],1,temp,sfd);
8225 	}
8226     }
8227     else if ( strmatch(tok,"KernClass2:")==0 || strmatch(tok,"VKernClass2:")==0 ||
8228 	      strmatch(tok,"KernClass:")==0 || strmatch(tok,"VKernClass:")==0 ||
8229 	      strmatch(tok,"KernClass3:")==0 || strmatch(tok,"VKernClass3:")==0 )
8230     {
8231 	int kernclassversion = 0;
8232 	int isv = tok[0]=='V';
8233 	int kcvoffset = (isv ? 10 : 9); //Offset to read kerning class version
8234 	if (isdigit(tok[kcvoffset])) kernclassversion = tok[kcvoffset] - '0';
8235 	int temp, classstart=1;
8236 	int old = (kernclassversion == 0);
8237 
8238 	if ( (sf->sfd_version<2)!=old ) {
8239 	    IError( "Version mixup in Kerning Classes of sfd file." );
8240 	    exit(1);
8241 	}
8242 	kc = chunkalloc(old ? sizeof(KernClass1) : sizeof(KernClass));
8243 	getint(sfd,&kc->first_cnt);
8244 	ch=nlgetc(sfd);
8245 	if ( ch=='+' )
8246 	    classstart = 0;
8247 	else
8248 	    ungetc(ch,sfd);
8249 	getint(sfd,&kc->second_cnt);
8250 	if ( old ) {
8251 	    getint(sfd,&temp); ((KernClass1 *) kc)->sli = temp;
8252 	    getint(sfd,&temp); ((KernClass1 *) kc)->flags = temp;
8253 	} else {
8254 	    kc->subtable = SFFindLookupSubtableAndFreeName(sf,SFDReadUTF7Str(sfd));
8255 	    if ( kc->subtable!=NULL && kc->subtable->kc==NULL )
8256 		kc->subtable->kc = kc;
8257 	    else {
8258 		if ( kc->subtable==NULL )
8259 		    LogError(_("Bad SFD file, missing subtable in kernclass defn.\n") );
8260 		else
8261 		    LogError(_("Bad SFD file, two kerning classes assigned to the same subtable: %s\n"), kc->subtable->subtable_name );
8262 		kc->subtable = NULL;
8263 	    }
8264 	}
8265 	kc->firsts = calloc(kc->first_cnt,sizeof(char *));
8266 	kc->seconds = calloc(kc->second_cnt,sizeof(char *));
8267 	kc->offsets = calloc(kc->first_cnt*kc->second_cnt,sizeof(int16));
8268 	kc->adjusts = calloc(kc->first_cnt*kc->second_cnt,sizeof(DeviceTable));
8269 	if (kernclassversion >= 3) {
8270 	  kc->firsts_flags = calloc(kc->first_cnt, sizeof(int));
8271 	  kc->seconds_flags = calloc(kc->second_cnt, sizeof(int));
8272 	  kc->offsets_flags = calloc(kc->first_cnt*kc->second_cnt, sizeof(int));
8273 	  kc->firsts_names = calloc(kc->first_cnt, sizeof(char*));
8274 	  kc->seconds_names = calloc(kc->second_cnt, sizeof(char*));
8275 	}
8276 	kc->firsts[0] = NULL;
8277 	for ( i=classstart; i<kc->first_cnt; ++i ) {
8278 	  if (kernclassversion < 3) {
8279 	    getint(sfd,&temp);
8280 	    kc->firsts[i] = malloc(temp+1); kc->firsts[i][temp] = '\0';
8281 	    nlgetc(sfd);	/* skip space */
8282 	    fread(kc->firsts[i],1,temp,sfd);
8283 	  } else {
8284 	    getint(sfd,&kc->firsts_flags[i]);
8285 	    while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd); if (ch == '\n' || ch == EOF) continue;
8286 	    kc->firsts_names[i] = SFDReadUTF7Str(sfd);
8287 	    while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd); if (ch == '\n' || ch == EOF) continue;
8288 	    kc->firsts[i] = SFDReadUTF7Str(sfd);
8289             if (kc->firsts[i] == NULL) kc->firsts[i] = copy(""); // In certain places, this must be defined.
8290 	    while ((ch=nlgetc(sfd)) == ' ' || ch == '\n'); ungetc(ch, sfd);
8291 	  }
8292 	}
8293 	kc->seconds[0] = NULL;
8294 	for ( i=1; i<kc->second_cnt; ++i ) {
8295 	  if (kernclassversion < 3) {
8296 	    getint(sfd,&temp);
8297 	    kc->seconds[i] = malloc(temp+1); kc->seconds[i][temp] = '\0';
8298 	    nlgetc(sfd);	/* skip space */
8299 	    fread(kc->seconds[i],1,temp,sfd);
8300 	  } else {
8301 	    getint(sfd,&temp);
8302 	    kc->seconds_flags[i] = temp;
8303 	    while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd); if (ch == '\n' || ch == EOF) continue;
8304 	    kc->seconds_names[i] = SFDReadUTF7Str(sfd);
8305 	    while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd); if (ch == '\n' || ch == EOF) continue;
8306 	    kc->seconds[i] = SFDReadUTF7Str(sfd);
8307             if (kc->seconds[i] == NULL) kc->seconds[i] = copy(""); // In certain places, this must be defined.
8308 	    while ((ch=nlgetc(sfd)) == ' ' || ch == '\n'); ungetc(ch, sfd);
8309 	  }
8310 	}
8311 	for ( i=0; i<kc->first_cnt*kc->second_cnt; ++i ) {
8312 	  if (kernclassversion >= 3) {
8313 	    getint(sfd,&temp);
8314 	    kc->offsets_flags[i] = temp;
8315 	  }
8316 	    getint(sfd,&temp);
8317 	    kc->offsets[i] = temp;
8318 	    SFDReadDeviceTable(sfd,&kc->adjusts[i]);
8319 	}
8320 	if ( !old && kc->subtable == NULL ) {
8321 	    /* Error. Ignore it. Free it. Whatever */;
8322 	} else if ( !isv ) {
8323 	    if ( d->lastkc==NULL )
8324 		sf->kerns = kc;
8325 	    else
8326 		d->lastkc->next = kc;
8327 	    d->lastkc = kc;
8328 	} else {
8329 	    if ( d->lastvkc==NULL )
8330 		sf->vkerns = kc;
8331 	    else
8332 		d->lastvkc->next = kc;
8333 	    d->lastvkc = kc;
8334 	}
8335     }
8336     else if ( strmatch(tok,"ContextPos2:")==0 || strmatch(tok,"ContextSub2:")==0 ||
8337 	      strmatch(tok,"ChainPos2:")==0 || strmatch(tok,"ChainSub2:")==0 ||
8338 	      strmatch(tok,"ReverseChain2:")==0 ||
8339 	      strmatch(tok,"ContextPos:")==0 || strmatch(tok,"ContextSub:")==0 ||
8340 	      strmatch(tok,"ChainPos:")==0 || strmatch(tok,"ChainSub:")==0 ||
8341 	      strmatch(tok,"ReverseChain:")==0 )
8342     {
8343 	FPST *fpst;
8344 	int old;
8345 	if ( strchr(tok,'2')!=NULL ) {
8346 	    old = false;
8347 	    fpst = chunkalloc(sizeof(FPST));
8348 	} else {
8349 	    old = true;
8350 	    fpst = chunkalloc(sizeof(FPST1));
8351 	}
8352 	if ( (sf->sfd_version<2)!=old ) {
8353 	    IError( "Version mixup in FPST of sfd file." );
8354 	    exit(1);
8355 	}
8356 	if ( d->lastfp==NULL )
8357 	    sf->possub = fpst;
8358 	else
8359 	    d->lastfp->next = fpst;
8360 	d->lastfp = fpst;
8361 	SFDParseChainContext(sfd,sf,fpst,tok,old);
8362     }
8363     else if ( strmatch(tok,"Group:")==0 ) {
8364         struct ff_glyphclasses *grouptmp = calloc(1, sizeof(struct ff_glyphclasses));
8365         while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
8366         grouptmp->classname = SFDReadUTF7Str(sfd);
8367         while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
8368         grouptmp->glyphs = SFDReadUTF7Str(sfd);
8369         while ((ch=nlgetc(sfd)) == ' ' || ch == '\n'); ungetc(ch, sfd);
8370         if (d->lastgroup != NULL) d->lastgroup->next = grouptmp; else sf->groups = grouptmp;
8371         d->lastgroup = grouptmp;
8372     }
8373     else if ( strmatch(tok,"GroupKern:")==0 ) {
8374         int temp = 0;
8375         struct ff_rawoffsets *kerntmp = calloc(1, sizeof(struct ff_rawoffsets));
8376         while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
8377         kerntmp->left = SFDReadUTF7Str(sfd);
8378         while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
8379         kerntmp->right = SFDReadUTF7Str(sfd);
8380         while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
8381         getint(sfd,&temp);
8382         kerntmp->offset = temp;
8383         while ((ch=nlgetc(sfd)) == ' ' || ch == '\n'); ungetc(ch, sfd);
8384         if (d->lastgroupkern != NULL) d->lastgroupkern->next = kerntmp; else sf->groupkerns = kerntmp;
8385         d->lastgroupkern = kerntmp;
8386     }
8387     else if ( strmatch(tok,"GroupVKern:")==0 ) {
8388         int temp = 0;
8389         struct ff_rawoffsets *kerntmp = calloc(1, sizeof(struct ff_rawoffsets));
8390         while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
8391         kerntmp->left = SFDReadUTF7Str(sfd);
8392         while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
8393         kerntmp->right = SFDReadUTF7Str(sfd);
8394         while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
8395         getint(sfd,&temp);
8396         kerntmp->offset = temp;
8397         while ((ch=nlgetc(sfd)) == ' ' || ch == '\n'); ungetc(ch, sfd);
8398         if (d->lastgroupvkern != NULL) d->lastgroupvkern->next = kerntmp; else sf->groupvkerns = kerntmp;
8399         d->lastgroupvkern = kerntmp;
8400     }
8401     else if ( strmatch(tok,"MacIndic2:")==0 || strmatch(tok,"MacContext2:")==0 ||
8402 	      strmatch(tok,"MacLigature2:")==0 || strmatch(tok,"MacSimple2:")==0 ||
8403 	      strmatch(tok,"MacKern2:")==0 || strmatch(tok,"MacInsert2:")==0 ||
8404 	      strmatch(tok,"MacIndic:")==0 || strmatch(tok,"MacContext:")==0 ||
8405 	      strmatch(tok,"MacLigature:")==0 || strmatch(tok,"MacSimple:")==0 ||
8406 	      strmatch(tok,"MacKern:")==0 || strmatch(tok,"MacInsert:")==0 )
8407     {
8408 	ASM *sm;
8409 	if ( strchr(tok,'2')!=NULL ) {
8410 	    old = false;
8411 	    sm = chunkalloc(sizeof(ASM));
8412 	} else {
8413 	    old = true;
8414 	    sm = chunkalloc(sizeof(ASM1));
8415 	}
8416 	if ( (sf->sfd_version<2)!=old ) {
8417 	    IError( "Version mixup in state machine of sfd file." );
8418 	    exit(1);
8419 	}
8420 	if ( d->lastsm==NULL )
8421 	    sf->sm = sm;
8422 	else
8423 	    d->lastsm->next = sm;
8424 	d->lastsm = sm;
8425 	SFDParseStateMachine(sfd,sf,sm,tok,old);
8426     }
8427     else if ( strmatch(tok,"MacFeat:")==0 )
8428     {
8429 	sf->features = SFDParseMacFeatures(sfd,tok);
8430     }
8431     else if ( strmatch(tok,"TtfTable:")==0 )
8432     {
8433 	/* Old, binary format */
8434 	/* still used for maxp and unknown tables */
8435 	SFDGetTtfTable(sfd,sf,d->lastttf);
8436     }
8437     else if ( strmatch(tok,"TtTable:")==0 )
8438     {
8439 	/* text instruction format */
8440 	SFDGetTtTable(sfd,sf,d->lastttf);
8441     }
8442 
8443 
8444     ///////////////////
8445 
8446     else if ( strmatch(tok,"ShortTable:")==0 )
8447     {
8448 	// only read, not written.
8449 	/* text number format */
8450 	SFDGetShortTable(sfd,sf,d->lastttf);
8451     }
8452     else
8453     {
8454         //
8455         // We didn't have a match ourselves.
8456         //
8457         return false;
8458     }
8459     return true;
8460 }
8461 
SFD_GetFontMetaDataVoid(FILE * sfd,char * tok,SplineFont * sf,void * d)8462 void SFD_GetFontMetaDataVoid( FILE *sfd,
8463 			  char *tok,
8464 			  SplineFont *sf,
8465 			  void* d ) {
8466   SFD_GetFontMetaData(sfd, tok, sf, d);
8467 }
8468 
SFD_GetFont(FILE * sfd,SplineFont * cidmaster,char * tok,int fromdir,char * dirname,float sfdversion)8469 static SplineFont *SFD_GetFont( FILE *sfd,SplineFont *cidmaster,char *tok,
8470 				int fromdir, char *dirname, float sfdversion )
8471 {
8472     SplineFont *sf;
8473     int realcnt, i, eof, mappos=-1, ch;
8474     struct table_ordering *lastord = NULL;
8475     struct axismap *lastaxismap = NULL;
8476     struct named_instance *lastnamedinstance = NULL;
8477     int pushedbacktok = false;
8478     Encoding *enc = &custom;
8479     struct remap *remap = NULL;
8480     int haddupenc;
8481     int old_style_order2 = false;
8482     int had_layer_cnt=false;
8483 
8484     orig_pos = 0;		/* Only used for compatibility with extremely old sfd files */
8485 
8486     sf = SplineFontEmpty();
8487     if ( sfdversion>0 && sfdversion<2 ) {
8488 	/* If it's an old style sfd file with old style features we need some */
8489 	/*  extra data space to do the conversion from old to new */
8490 	sf = realloc(sf,sizeof(SplineFont1));
8491 	memset(((uint8 *) sf) + sizeof(SplineFont),0,sizeof(SplineFont1)-sizeof(SplineFont));
8492     }
8493     sf->sfd_version = sfdversion;
8494     sf->cidmaster = cidmaster;
8495     sf->uni_interp = ui_unset;
8496 	SFD_GetFontMetaDataData d;
8497 	SFD_GetFontMetaDataData_Init( &d );
8498     while ( 1 ) {
8499 	if ( pushedbacktok )
8500 	    pushedbacktok = false;
8501 	else if ( (eof = getname(sfd,tok))!=1 ) {
8502 	    if ( eof==-1 )
8503     break;
8504 	    geteol(sfd,tok);
8505     continue;
8506 	}
8507 
8508 
8509 	bool wasMetadata = SFD_GetFontMetaData( sfd, tok, sf, &d );
8510 	had_layer_cnt = d.had_layer_cnt;
8511         if( wasMetadata )
8512         {
8513             // we have handled the token entirely
8514             // inside SFD_GetFontMetaData() move to next token.
8515             continue;
8516         }
8517 
8518 
8519 	if ( strmatch(tok,"DisplaySize:")==0 )
8520 	{
8521 	    getint(sfd,&sf->display_size);
8522 	}
8523 	else if ( strmatch(tok,"DisplayLayer:")==0 )
8524 	{
8525 	    getint(sfd,&sf->display_layer);
8526 	}
8527 	else if ( strmatch(tok,"ExtremaBound:")==0 )
8528 	{
8529 	    getint(sfd,&sf->extrema_bound);
8530 	}
8531 	else if ( strmatch(tok,"WidthSeparation:")==0 )
8532 	{
8533 	    getint(sfd,&sf->width_separation);
8534 	}
8535 	else if ( strmatch(tok,"WinInfo:")==0 )
8536 	{
8537 	    int temp1, temp2;
8538 	    getint(sfd,&sf->top_enc);
8539 	    getint(sfd,&temp1);
8540 	    getint(sfd,&temp2);
8541 	    if ( sf->top_enc<=0 ) sf->top_enc=-1;
8542 	    if ( temp1<=0 ) temp1 = 16;
8543 	    if ( temp2<=0 ) temp2 = 4;
8544 	    sf->desired_col_cnt = temp1;
8545 	    sf->desired_row_cnt = temp2;
8546 	}
8547 	else if ( strmatch(tok,"AntiAlias:")==0 )
8548 	{
8549 	    int temp;
8550 	    getint(sfd,&temp);
8551 	    sf->display_antialias = temp;
8552 	}
8553 	else if ( strmatch(tok,"FitToEm:")==0 )
8554 	{
8555 	    int temp;
8556 	    getint(sfd,&temp);
8557 	    sf->display_bbsized = temp;
8558 	}
8559 	else if ( strmatch(tok,"OnlyBitmaps:")==0 )
8560 	{
8561 	    int temp;
8562 	    getint(sfd,&temp);
8563 	    sf->onlybitmaps = temp;
8564 	}
8565 	else if ( strmatch(tok,"Order2:")==0 )
8566 	{
8567 	    getint(sfd,&old_style_order2);
8568 	    sf->grid.order2 = old_style_order2;
8569 	    sf->layers[ly_back].order2 = old_style_order2;
8570 	    sf->layers[ly_fore].order2 = old_style_order2;
8571 	}
8572 	else if ( strmatch(tok,"GridOrder2:")==0 )
8573 	{
8574 	    int o2;
8575 	    getint(sfd,&o2);
8576 	    sf->grid.order2 = o2;
8577 	}
8578 	else if ( strmatch(tok,"Encoding:")==0 )
8579 	{
8580 	    enc = SFDGetEncoding(sfd,tok);
8581 	    if ( sf->map!=NULL ) sf->map->enc = enc;
8582 	}
8583 	else if ( strmatch(tok,"OldEncoding:")==0 )
8584 	{
8585 	    /* old_encname =*/ (void) SFDGetEncoding(sfd,tok);
8586 	}
8587 	else if ( strmatch(tok,"UnicodeInterp:")==0 )
8588 	{
8589 	    sf->uni_interp = SFDGetUniInterp(sfd,tok,sf);
8590 	}
8591 	else if ( strmatch(tok,"NameList:")==0 )
8592 	{
8593 	    SFDGetNameList(sfd,tok,sf);
8594 	}
8595 	else if ( strmatch(tok,"Compacted:")==0 )
8596 	{
8597 	    int temp;
8598 	    getint(sfd,&temp);
8599 	    sf->compacted = temp;
8600 	}
8601 	else if ( strmatch(tok,"Registry:")==0 )
8602 	{
8603 	    geteol(sfd,tok);
8604 	    sf->cidregistry = copy(tok);
8605 	}
8606 
8607 
8608 	//////////
8609 
8610 
8611 	else if ( strmatch(tok,"Ordering:")==0 ) {
8612 	    geteol(sfd,tok);
8613 	    sf->ordering = copy(tok);
8614 	} else if ( strmatch(tok,"Supplement:")==0 ) {
8615 	    getint(sfd,&sf->supplement);
8616 	} else if ( strmatch(tok,"RemapN:")==0 ) {
8617 	    int n;
8618 	    getint(sfd,&n);
8619 	    remap = calloc(n+1,sizeof(struct remap));
8620 	    remap[n].infont = -1;
8621 	    mappos = 0;
8622 	    if ( sf->map!=NULL ) sf->map->remap = remap;
8623 	} else if ( strmatch(tok,"Remap:")==0 ) {
8624 	    uint32 f, l; int p;
8625 	    gethex(sfd,&f);
8626 	    gethex(sfd,&l);
8627 	    getint(sfd,&p);
8628 	    if ( remap!=NULL && remap[mappos].infont!=-1 ) {
8629 		remap[mappos].firstenc = f;
8630 		remap[mappos].lastenc = l;
8631 		remap[mappos].infont = p;
8632 		mappos++;
8633 	    }
8634 	} else if ( strmatch(tok,"CIDVersion:")==0 ) {
8635 	    real temp;
8636 	    getreal(sfd,&temp);
8637 	    sf->cidversion = temp;
8638 	} else if ( strmatch(tok,"Grid")==0 ) {
8639 	    sf->grid.splines = SFDGetSplineSet(sfd,sf->grid.order2);
8640 	} else if ( strmatch(tok,"ScriptLang:")==0 ) {
8641 	    int i,j,k;
8642 	    int imax, jmax, kmax;
8643 	    if ( sf->sfd_version==0 || sf->sfd_version>=2 ) {
8644 		IError( "Script lang lists should not happen in version 2 sfd files." );
8645                 SplineFontFree(sf);
8646                 return NULL;
8647 	    }
8648 	    getint(sfd,&imax);
8649 	    ((SplineFont1 *) sf)->sli_cnt = imax;
8650 	    ((SplineFont1 *) sf)->script_lang = malloc((imax+1)*sizeof(struct script_record *));
8651 	    ((SplineFont1 *) sf)->script_lang[imax] = NULL;
8652 	    for ( i=0; i<imax; ++i ) {
8653 		getint(sfd,&jmax);
8654 		((SplineFont1 *) sf)->script_lang[i] = malloc((jmax+1)*sizeof(struct script_record));
8655 		((SplineFont1 *) sf)->script_lang[i][jmax].script = 0;
8656 		for ( j=0; j<jmax; ++j ) {
8657 		    ((SplineFont1 *) sf)->script_lang[i][j].script = gettag(sfd);
8658 		    getint(sfd,&kmax);
8659 		    ((SplineFont1 *) sf)->script_lang[i][j].langs = malloc((kmax+1)*sizeof(uint32));
8660 		    ((SplineFont1 *) sf)->script_lang[i][j].langs[kmax] = 0;
8661 		    for ( k=0; k<kmax; ++k ) {
8662 			((SplineFont1 *) sf)->script_lang[i][j].langs[k] = gettag(sfd);
8663 		    }
8664 		}
8665 	    }
8666 	} else if ( strmatch(tok,"TeXData:")==0 ) {
8667 	    int temp;
8668 	    getint(sfd,&temp);
8669 	    sf->texdata.type = temp;
8670 	    getint(sfd, &temp);
8671 	    if ( sf->design_size==0 ) {
8672 	    	sf->design_size = (5*temp+(1<<18))>>19;
8673 	    }
8674 	    for ( i=0; i<22; ++i ) {
8675 		int foo;
8676 		getint(sfd,&foo);
8677 		sf->texdata.params[i]=foo;
8678 	    }
8679 	} else if ( strnmatch(tok,"AnchorClass",11)==0 ) {
8680 	    char *name;
8681 	    AnchorClass *lastan = NULL, *an;
8682 	    int old = strchr(tok,'2')==NULL;
8683 	    while ( (name=SFDReadUTF7Str(sfd))!=NULL ) {
8684 		an = chunkalloc(old ? sizeof(AnchorClass1) : sizeof(AnchorClass));
8685 		an->name = name;
8686 		if ( old ) {
8687 		    getname(sfd,tok);
8688 		    if ( tok[0]=='0' && tok[1]=='\0' )
8689 			((AnchorClass1 *) an)->feature_tag = 0;
8690 		    else {
8691 			if ( tok[1]=='\0' ) { tok[1]=' '; tok[2] = 0; }
8692 			if ( tok[2]=='\0' ) { tok[2]=' '; tok[3] = 0; }
8693 			if ( tok[3]=='\0' ) { tok[3]=' '; tok[4] = 0; }
8694 			((AnchorClass1 *) an)->feature_tag = (tok[0]<<24) | (tok[1]<<16) | (tok[2]<<8) | tok[3];
8695 		    }
8696 		    while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
8697 		    ungetc(ch,sfd);
8698 		    if ( isdigit(ch)) {
8699 			int temp;
8700 			getint(sfd,&temp);
8701 			((AnchorClass1 *) an)->flags = temp;
8702 		    }
8703 		    while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
8704 		    ungetc(ch,sfd);
8705 		    if ( isdigit(ch)) {
8706 			int temp;
8707 			getint(sfd,&temp);
8708 			((AnchorClass1 *) an)->script_lang_index = temp;
8709 		    } else
8710 			((AnchorClass1 *) an)->script_lang_index = 0xffff;		/* Will be fixed up later */
8711 		    while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
8712 		    ungetc(ch,sfd);
8713 		    if ( isdigit(ch)) {
8714 			int temp;
8715 			getint(sfd,&temp);
8716 			((AnchorClass1 *) an)->merge_with = temp;
8717 		    } else
8718 			((AnchorClass1 *) an)->merge_with = 0xffff;			/* Will be fixed up later */
8719 		} else {
8720                     char *subtable_name = SFDReadUTF7Str(sfd);
8721                     if ( subtable_name!=NULL)                                           /* subtable is optional */
8722 		        an->subtable = SFFindLookupSubtableAndFreeName(sf,subtable_name);
8723                 }
8724 		while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
8725 		ungetc(ch,sfd);
8726 		if ( isdigit(ch) ) {
8727 		    /* Early versions of SfdFormat 2 had a number here */
8728 		    int temp;
8729 		    getint(sfd,&temp);
8730 		    an->type = temp;
8731 		} else if ( old ) {
8732 		    if ( ((AnchorClass1 *) an)->feature_tag==CHR('c','u','r','s'))
8733 			an->type = act_curs;
8734 		    else if ( ((AnchorClass1 *) an)->feature_tag==CHR('m','k','m','k'))
8735 			an->type = act_mkmk;
8736 		    else
8737 			an->type = act_mark;
8738 		} else {
8739 		    an->type = act_mark;
8740 		    if( an->subtable && an->subtable->lookup )
8741 		    {
8742 			switch ( an->subtable->lookup->lookup_type )
8743 			{
8744 			case gpos_cursive:
8745 			    an->type = act_curs;
8746 			    break;
8747 			case gpos_mark2base:
8748 			    an->type = act_mark;
8749 			    break;
8750 			case gpos_mark2ligature:
8751 			    an->type = act_mklg;
8752 			    break;
8753 			case gpos_mark2mark:
8754 			    an->type = act_mkmk;
8755 			    break;
8756 			default:
8757 			    an->type = act_mark;
8758 			    break;
8759 			}
8760 		    }
8761 		}
8762 		if ( lastan==NULL )
8763 		    sf->anchor = an;
8764 		else
8765 		    lastan->next = an;
8766 		lastan = an;
8767 	    }
8768 	} else if ( strncmp(tok,"MATH:",5)==0 ) {
8769 	    SFDParseMathItem(sfd,sf,tok);
8770 	} else if ( strmatch(tok,"TableOrder:")==0 ) {
8771 	    int temp;
8772 	    struct table_ordering *ord;
8773 	    if ( sfdversion==0 || sfdversion>=2 ) {
8774 		IError("Table ordering specified in version 2 sfd file.\n" );
8775                 SplineFontFree(sf);
8776                 return NULL;
8777 	    }
8778 	    ord = chunkalloc(sizeof(struct table_ordering));
8779 	    ord->table_tag = gettag(sfd);
8780 	    getint(sfd,&temp);
8781 	    ord->ordered_features = malloc((temp+1)*sizeof(uint32));
8782 	    ord->ordered_features[temp] = 0;
8783 	    for ( i=0; i<temp; ++i ) {
8784 		while ( isspace((ch=nlgetc(sfd))) );
8785 		if ( ch=='\'' ) {
8786 		    ungetc(ch,sfd);
8787 		    ord->ordered_features[i] = gettag(sfd);
8788 		} else if ( ch=='<' ) {
8789 		    int f,s;
8790 		    fscanf(sfd,"%d,%d>", &f, &s );
8791 		    ord->ordered_features[i] = (f<<16)|s;
8792 		}
8793 	    }
8794 	    if ( lastord==NULL )
8795 		((SplineFont1 *) sf)->orders = ord;
8796 	    else
8797 		lastord->next = ord;
8798 	    lastord = ord;
8799 	} else if ( strmatch(tok,"BeginPrivate:")==0 ) {
8800 	    SFDGetPrivate(sfd,sf);
8801 	} else if ( strmatch(tok,"BeginSubrs:")==0 ) {	/* leave in so we don't croak on old sfd files */
8802 	    SFDGetSubrs(sfd);
8803 	} else if ( strmatch(tok,"PickledData:")==0 ) {
8804 	    if (sf->python_persistent != NULL) {
8805 #if defined(_NO_PYTHON)
8806 	      free( sf->python_persistent );	/* It's a string of pickled data which we leave as a string */
8807 #else
8808 	      PyFF_FreePythonPersistent(sf->python_persistent);
8809 #endif
8810 	      sf->python_persistent = NULL;
8811 	    }
8812 	    sf->python_persistent = SFDUnPickle(sfd, 0);
8813 	    sf->python_persistent_has_lists = 0;
8814 	} else if ( strmatch(tok,"PickledDataWithLists:")==0 ) {
8815 	    if (sf->python_persistent != NULL) {
8816 #if defined(_NO_PYTHON)
8817 	      free( sf->python_persistent );	/* It's a string of pickled data which we leave as a string */
8818 #else
8819 	      PyFF_FreePythonPersistent(sf->python_persistent);
8820 #endif
8821 	      sf->python_persistent = NULL;
8822 	    }
8823 	    sf->python_persistent = SFDUnPickle(sfd, 1);
8824 	    sf->python_persistent_has_lists = 1;
8825 	} else if ( strmatch(tok,"MMCounts:")==0 ) {
8826 	    MMSet *mm = sf->mm = chunkalloc(sizeof(MMSet));
8827 	    getint(sfd,&mm->instance_count);
8828 	    getint(sfd,&mm->axis_count);
8829 	    ch = nlgetc(sfd);
8830 	    if ( ch!=' ' )
8831 		ungetc(ch,sfd);
8832 	    else { int temp;
8833 		getint(sfd,&temp);
8834 		mm->apple = temp;
8835 		getint(sfd,&mm->named_instance_count);
8836 	    }
8837 	    mm->instances = calloc(mm->instance_count,sizeof(SplineFont *));
8838 	    mm->positions = malloc(mm->instance_count*mm->axis_count*sizeof(real));
8839 	    mm->defweights = malloc(mm->instance_count*sizeof(real));
8840 	    mm->axismaps = calloc(mm->axis_count,sizeof(struct axismap));
8841 	    if ( mm->named_instance_count!=0 )
8842 		mm->named_instances = calloc(mm->named_instance_count,sizeof(struct named_instance));
8843 	} else if ( strmatch(tok,"MMAxis:")==0 ) {
8844 	    MMSet *mm = sf->mm;
8845 	    if ( mm!=NULL ) {
8846 		for ( i=0; i<mm->axis_count; ++i ) {
8847 		    getname(sfd,tok);
8848 		    mm->axes[i] = copy(tok);
8849 		}
8850 	    }
8851 	} else if ( strmatch(tok,"MMPositions:")==0 ) {
8852 	    MMSet *mm = sf->mm;
8853 	    if ( mm!=NULL ) {
8854 		for ( i=0; i<mm->axis_count*mm->instance_count; ++i )
8855 		    getreal(sfd,&mm->positions[i]);
8856 	    }
8857 	} else if ( strmatch(tok,"MMWeights:")==0 ) {
8858 	    MMSet *mm = sf->mm;
8859 	    if ( mm!=NULL ) {
8860 		for ( i=0; i<mm->instance_count; ++i )
8861 		    getreal(sfd,&mm->defweights[i]);
8862 	    }
8863 	} else if ( strmatch(tok,"MMAxisMap:")==0 ) {
8864 	    MMSet *mm = sf->mm;
8865 	    if ( mm!=NULL ) {
8866 		int index, points;
8867 		getint(sfd,&index); getint(sfd,&points);
8868 		mm->axismaps[index].points = points;
8869 		mm->axismaps[index].blends = malloc(points*sizeof(real));
8870 		mm->axismaps[index].designs = malloc(points*sizeof(real));
8871 		for ( i=0; i<points; ++i ) {
8872 		    getreal(sfd,&mm->axismaps[index].blends[i]);
8873 		    while ( (ch=nlgetc(sfd))!=EOF && isspace(ch));
8874 		    ungetc(ch,sfd);
8875 		    if ( (ch=nlgetc(sfd))!='=' )
8876 			ungetc(ch,sfd);
8877 		    else if ( (ch=nlgetc(sfd))!='>' )
8878 			ungetc(ch,sfd);
8879 		    getreal(sfd,&mm->axismaps[index].designs[i]);
8880 		}
8881 		lastaxismap = &mm->axismaps[index];
8882 		lastnamedinstance = NULL;
8883 	    }
8884 	} else if ( strmatch(tok,"MMNamedInstance:")==0 ) {
8885 	    MMSet *mm = sf->mm;
8886 	    if ( mm!=NULL ) {
8887 		int index;
8888 		getint(sfd,&index);
8889 		mm->named_instances[index].coords = malloc(mm->axis_count*sizeof(real));
8890 		for ( i=0; i<mm->axis_count; ++i )
8891 		    getreal(sfd,&mm->named_instances[index].coords[i]);
8892 		lastnamedinstance = &mm->named_instances[index];
8893 		lastaxismap = NULL;
8894 	    }
8895 	} else if ( strmatch(tok,"MacName:")==0 ) {
8896 	    struct macname *names = SFDParseMacNames(sfd,tok);
8897 	    if ( lastaxismap!=NULL )
8898 		lastaxismap->axisnames = names;
8899 	    else if ( lastnamedinstance !=NULL )
8900 		lastnamedinstance->names = names;
8901 	    pushedbacktok = true;
8902 	} else if ( strmatch(tok,"MMCDV:")==0 ) {
8903 	    MMSet *mm = sf->mm;
8904 	    if ( mm!=NULL )
8905 		mm->cdv = SFDParseMMSubroutine(sfd);
8906 	} else if ( strmatch(tok,"MMNDV:")==0 ) {
8907 	    MMSet *mm = sf->mm;
8908 	    if ( mm!=NULL )
8909 		mm->ndv = SFDParseMMSubroutine(sfd);
8910 	} else if ( strmatch(tok,"BeginMMFonts:")==0 ) {
8911 	    int cnt;
8912 	    getint(sfd,&cnt);
8913 	    getint(sfd,&realcnt);
8914 	    ff_progress_change_stages(cnt);
8915 	    ff_progress_change_total(realcnt);
8916 	    MMInferStuff(sf->mm);
8917     break;
8918 	} else if ( strmatch(tok,"BeginSubFonts:")==0 ) {
8919 	    getint(sfd,&sf->subfontcnt);
8920 	    sf->subfonts = calloc(sf->subfontcnt,sizeof(SplineFont *));
8921 	    getint(sfd,&realcnt);
8922 	    sf->map = EncMap1to1(realcnt);
8923 	    ff_progress_change_stages(2);
8924 	    ff_progress_change_total(realcnt);
8925     break;
8926 	} else if ( strmatch(tok,"BeginChars:")==0 ) {
8927 	    int charcnt;
8928 	    getint(sfd,&charcnt);
8929 	    if (charcnt<enc->char_cnt) {
8930 		IError("SFD file specifies too few slots for its encoding.\n" );
8931 exit( 1 );
8932 	    }
8933 	    if ( getint(sfd,&realcnt)!=1 || realcnt==-1 )
8934 		realcnt = charcnt;
8935 	    else
8936 		++realcnt;		/* value saved is max glyph, not glyph cnt */
8937 	    ff_progress_change_total(realcnt);
8938 	    sf->glyphcnt = sf->glyphmax = realcnt;
8939 	    sf->glyphs = calloc(realcnt,sizeof(SplineChar *));
8940 	    if ( cidmaster!=NULL ) {
8941 		sf->map = cidmaster->map;
8942 	    } else {
8943 		sf->map = EncMapNew(charcnt,realcnt,enc);
8944 		sf->map->remap = remap;
8945 	    }
8946 	    SFDSizeMap(sf->map,sf->glyphcnt,charcnt);
8947     break;
8948 #if HANYANG
8949 	} else if ( strmatch(tok,"BeginCompositionRules")==0 ) {
8950 	    sf->rules = SFDReadCompositionRules(sfd);
8951 #endif
8952 	} else {
8953 	    /* If we don't understand it, skip it */
8954 	    geteol(sfd,tok);
8955 	}
8956     }
8957 
8958     // Many downstream functions assume this isn't NULL (use strlen, etc.)
8959     if ( sf->fontname==NULL)
8960 	sf->fontname = copy("");
8961 
8962     if ( fromdir )
8963 	sf = SFD_FigureDirType(sf,tok,dirname,enc,remap,had_layer_cnt);
8964     else if ( sf->subfontcnt!=0 ) {
8965 	ff_progress_change_stages(2*sf->subfontcnt);
8966 	for ( i=0; i<sf->subfontcnt; ++i ) {
8967 	    if ( i!=0 )
8968 		ff_progress_next_stage();
8969 	    sf->subfonts[i] = SFD_GetFont(sfd,sf,tok,fromdir,dirname,sfdversion);
8970 	}
8971     } else if ( sf->mm!=NULL ) {
8972 	MMSet *mm = sf->mm;
8973 	ff_progress_change_stages(2*(mm->instance_count+1));
8974 	for ( i=0; i<mm->instance_count; ++i ) {
8975 	    if ( i!=0 )
8976 		ff_progress_next_stage();
8977 	    mm->instances[i] = SFD_GetFont(sfd,NULL,tok,fromdir,dirname,sfdversion);
8978 	    EncMapFree(mm->instances[i]->map); mm->instances[i]->map=NULL;
8979 	    mm->instances[i]->mm = mm;
8980 	}
8981 	ff_progress_next_stage();
8982 	mm->normal = SFD_GetFont(sfd,NULL,tok,fromdir,dirname,sfdversion);
8983 	mm->normal->mm = mm;
8984 	sf->mm = NULL;
8985 	SplineFontFree(sf);
8986 	sf = mm->normal;
8987 	if ( sf->map->enc!=&custom ) {
8988 	    EncMap *map;
8989 	    MMMatchGlyphs(mm);		/* sfd files from before the encoding change can have mismatched orig pos */
8990 	    map = EncMapFromEncoding(sf,sf->map->enc);
8991 	    EncMapFree(sf->map);
8992 	    sf->map = map;
8993 	}
8994     } else {
8995 	while ( SFDGetChar(sfd,sf,had_layer_cnt)!=NULL ) {
8996 	    ff_progress_next();
8997 	}
8998 	ff_progress_next_stage();
8999     }
9000     haddupenc = false;
9001     while ( getname(sfd,tok)==1 ) {
9002 	if ( strcmp(tok,"EndSplineFont")==0 || strcmp(tok,"EndSubSplineFont")==0 )
9003     break;
9004 	else if ( strcmp(tok,"BitmapFont:")==0 )
9005 	    SFDGetBitmapFont(sfd,sf,false,NULL);
9006 	else if ( strmatch(tok,"DupEnc:")==0 ) {
9007 	    int enc, orig;
9008 	    haddupenc = true;
9009 	    if ( getint(sfd,&enc) && getint(sfd,&orig) && sf->map!=NULL ) {
9010 		SFDSetEncMap(sf,orig,enc);
9011 	    }
9012 	}
9013     }
9014     if ( sf->cidmaster==NULL )
9015 	SFDFixupRefs(sf);
9016 
9017     if ( !haddupenc )
9018 	SFD_DoAltUnis(sf);
9019     else
9020 	AltUniFigure(sf,sf->map,true);
9021     if ( sf->sfd_version<2 )
9022 	SFD_AssignLookups((SplineFont1 *) sf);
9023     if ( !d.hadtimes )
9024 	SFTimesFromFile(sf,sfd);
9025     // Make a blank encoding if there are no characters so as to avoid crashes later.
9026     if (sf->map == NULL) sf->map = EncMapNew(sf->glyphcnt,sf->glyphcnt,&custom);
9027 
9028     SFDFixupUndoRefs(sf);
9029 return( sf );
9030 }
9031 
SFTimesFromFile(SplineFont * sf,FILE * file)9032 void SFTimesFromFile(SplineFont *sf,FILE *file) {
9033     struct stat b;
9034     if ( fstat(fileno(file),&b)!=-1 ) {
9035 	sf->modificationtime = GetST_MTime(b);
9036 	sf->creationtime = GetST_MTime(b);
9037     }
9038 }
9039 
SFDStartsCorrectly(FILE * sfd,char * tok)9040 static double SFDStartsCorrectly(FILE *sfd,char *tok) {
9041     real dval;
9042     int ch;
9043 
9044     if ( getname(sfd,tok)!=1 )
9045 return( -1 );
9046     if ( strcmp(tok,"SplineFontDB:")!=0 )
9047 return( -1 );
9048     if ( getreal(sfd,&dval)!=1 )
9049 return( -1 );
9050     /* We don't yet generate version 4 of sfd. It will contain backslash */
9051     /*  newline in the middle of very long lines. I've put in code to parse */
9052     /*  this sequence, but I don't yet generate it. I want the parser to */
9053     /*  perculate through to users before I introduce the new format so there */
9054     /*  will be fewer complaints when it happens */
9055     // MIQ: getreal() can give some funky rounding errors it seems
9056     if ( dval!=0 && dval!=1 && dval!=2.0 && dval!=3.0
9057          && !(dval > 3.09 && dval <= 3.21)
9058          && dval!=4.0 )
9059     {
9060         LogError("Bad SFD Version number %.1f", dval );
9061 return( -1 );
9062     }
9063     ch = nlgetc(sfd); ungetc(ch,sfd);
9064     if ( ch!='\r' && ch!='\n' )
9065 return( -1 );
9066 
9067 return( dval );
9068 }
9069 
SFD_Read(char * filename,FILE * sfd,int fromdir)9070 static SplineFont *SFD_Read(char *filename,FILE *sfd, int fromdir) {
9071     SplineFont *sf=NULL;
9072     char tok[2000];
9073     double version;
9074 
9075     if ( sfd==NULL ) {
9076 	if ( fromdir ) {
9077 	    snprintf(tok,sizeof(tok),"%s/" FONT_PROPS, filename );
9078 	    sfd = fopen(tok,"r");
9079 	} else
9080 	    sfd = fopen(filename,"r");
9081     }
9082     if ( sfd==NULL )
9083 return( NULL );
9084     locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
9085     switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
9086     ff_progress_change_stages(2);
9087     if ( (version = SFDStartsCorrectly(sfd,tok))!=-1 )
9088 	sf = SFD_GetFont(sfd,NULL,tok,fromdir,filename,version);
9089     switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
9090     if ( sf!=NULL ) {
9091 	sf->filename = copy(filename);
9092 	if ( sf->mm!=NULL ) {
9093 	    int i;
9094 	    for ( i=0; i<sf->mm->instance_count; ++i )
9095 		sf->mm->instances[i]->filename = copy(filename);
9096 	} else if ( !sf->onlybitmaps ) {
9097 /* Jonathyn Bet'nct points out that once you edit in an outline window, even */
9098 /*  if by mistake, your onlybitmaps status is gone for good */
9099 /* Regenerate it if the font has no splines, refs, etc. */
9100 	    int i;
9101 	    SplineChar *sc;
9102 	    for ( i=sf->glyphcnt-1; i>=0; --i )
9103 		if ( (sc = sf->glyphs[i])!=NULL &&
9104 			(sc->layer_cnt!=2 ||
9105 			 sc->layers[ly_fore].splines!=NULL ||
9106 			 sc->layers[ly_fore].refs!=NULL ))
9107 	     break;
9108 	     if ( i==-1 )
9109 		 sf->onlybitmaps = true;
9110 	}
9111     }
9112     fclose(sfd);
9113 return( sf );
9114 }
9115 
SFDRead(char * filename)9116 SplineFont *SFDRead(char *filename) {
9117 return( SFD_Read(filename,NULL,false));
9118 }
9119 
_SFDRead(char * filename,FILE * sfd)9120 SplineFont *_SFDRead(char *filename,FILE *sfd) {
9121 return( SFD_Read(filename,sfd,false));
9122 }
9123 
SFDirRead(char * filename)9124 SplineFont *SFDirRead(char *filename) {
9125 return( SFD_Read(filename,NULL,true));
9126 }
9127 
SFDReadOneChar(SplineFont * cur_sf,const char * name)9128 SplineChar *SFDReadOneChar(SplineFont *cur_sf,const char *name) {
9129     FILE *sfd;
9130     SplineChar *sc=NULL;
9131     char oldloc[25], tok[2000];
9132     uint32 pos;
9133     SplineFont sf;
9134     LayerInfo layers[2];
9135     double version;
9136     int had_layer_cnt=false;
9137     int chars_seen = false;
9138 
9139     if ( cur_sf->save_to_dir ) {
9140 	snprintf(tok,sizeof(tok),"%s/" FONT_PROPS,cur_sf->filename);
9141 	sfd = fopen(tok,"r");
9142     } else
9143 	sfd = fopen(cur_sf->filename,"r");
9144     if ( sfd==NULL )
9145 return( NULL );
9146     locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
9147     switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
9148 
9149     memset(&sf,0,sizeof(sf));
9150     memset(&layers,0,sizeof(layers));
9151     sf.layer_cnt = 2;
9152     sf.layers = layers;
9153     sf.ascent = 800; sf.descent = 200;
9154     if ( cur_sf->cidmaster ) cur_sf = cur_sf->cidmaster;
9155     if ( (version = SFDStartsCorrectly(sfd,tok))>=2 ) {
9156 	sf.sfd_version = version;
9157 	sf.gpos_lookups = cur_sf->gpos_lookups;
9158 	sf.gsub_lookups = cur_sf->gsub_lookups;
9159 	sf.anchor = cur_sf->anchor;
9160 	pos = ftell(sfd);
9161 	while ( getname(sfd,tok)!=-1 ) {
9162 	    if ( strcmp(tok,"StartChar:")==0 ) {
9163 		if ( getname(sfd,tok)==1 && strcmp(tok,name)==0 ) {
9164 		    fseek(sfd,pos,SEEK_SET);
9165 		    sc = SFDGetChar(sfd,&sf,had_layer_cnt);
9166 	break;
9167 		}
9168 	    } else if ( strmatch(tok,"BeginChars:")==0 ) {
9169 		chars_seen = true;
9170 	    } else if ( chars_seen ) {
9171 		/* Don't try to look for things in the file header any more */
9172 		/* The "Layer" keyword has a different meaning in this context */
9173 	    } else if ( strmatch(tok,"Order2:")==0 ) {
9174 		int order2;
9175 		getint(sfd,&order2);
9176 		sf.grid.order2 = order2;
9177 		sf.layers[ly_back].order2 = order2;
9178 		sf.layers[ly_fore].order2 = order2;
9179 	    } else if ( strmatch(tok,"LayerCount:")==0 ) {
9180 		had_layer_cnt = true;
9181 		getint(sfd,&sf.layer_cnt);
9182 		if ( sf.layer_cnt>2 ) {
9183 		    sf.layers = calloc(sf.layer_cnt,sizeof(LayerInfo));
9184 		}
9185 	    } else if ( strmatch(tok,"Layer:")==0 ) {
9186 		int layer, o2;
9187 		getint(sfd,&layer);
9188 		getint(sfd,&o2);
9189 		if ( layer<sf.layer_cnt )
9190 		    sf.layers[layer].order2 = o2;
9191 		free( SFDReadUTF7Str(sfd));
9192 	    } else if ( strmatch(tok,"MultiLayer:")==0 ) {
9193 		int ml;
9194 		getint(sfd,&ml);
9195 		sf.multilayer = ml;
9196 	    } else if ( strmatch(tok,"StrokedFont:")==0 ) {
9197 		int stk;
9198 		getint(sfd,&stk);
9199 		sf.strokedfont = stk;
9200 	    } else if ( strmatch(tok,"Ascent:")==0 ) {
9201 		getint(sfd,&sf.ascent);
9202 	    } else if ( strmatch(tok,"Descent:")==0 ) {
9203 		getint(sfd,&sf.descent);
9204 	    } else if ( strmatch(tok,"InvalidEm:")==0 ) {
9205 		getint(sfd,&sf.invalidem);
9206 	    }
9207 	    pos = ftell(sfd);
9208 	}
9209     }
9210     fclose(sfd);
9211     if ( cur_sf->save_to_dir ) {
9212 	if ( sc!=NULL ) IError("Read a glyph from font.props");
9213 	/* Doesn't work for CID keyed, nor for mm */
9214 	snprintf(tok,sizeof(tok),"%s/%s" GLYPH_EXT,cur_sf->filename,name);
9215 	sfd = fopen(tok,"r");
9216 	if ( sfd!=NULL ) {
9217 	    sc = SFDGetChar(sfd,&sf,had_layer_cnt);
9218 	    fclose(sfd);
9219 	}
9220     }
9221 
9222     if ( sf.layers!=layers )
9223 	free(sf.layers);
9224     switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
9225 return( sc );
9226 }
9227 
ModSF(FILE * asfd,SplineFont * sf)9228 static int ModSF(FILE *asfd,SplineFont *sf) {
9229     Encoding *newmap;
9230     int cnt;
9231     int multilayer=0;
9232     char tok[200];
9233     int i,k;
9234     SplineChar *sc;
9235     SplineFont *ssf;
9236     SplineFont temp;
9237     int layercnt;
9238 
9239     memset(&temp,0,sizeof(temp));
9240     temp.layers = sf->layers;
9241     temp.layer_cnt = sf->layer_cnt;
9242     temp.layers[ly_back].order2 = sf->layers[ly_back].order2;
9243     temp.layers[ly_fore].order2 = sf->layers[ly_fore].order2;
9244     temp.ascent = sf->ascent; temp.descent = sf->descent;
9245     temp.multilayer = sf->multilayer;
9246     temp.gpos_lookups = sf->gpos_lookups;
9247     temp.gsub_lookups = sf->gsub_lookups;
9248     temp.anchor = sf->anchor;
9249     temp.sfd_version = 2;
9250 
9251     if ( getname(asfd,tok)!=1 || strcmp(tok,"Encoding:")!=0 )
9252 return(false);
9253     newmap = SFDGetEncoding(asfd,tok);
9254     if ( getname(asfd,tok)!=1 )
9255 return( false );
9256     if ( strcmp(tok,"UnicodeInterp:")==0 ) {
9257 	sf->uni_interp = SFDGetUniInterp(asfd,tok,sf);
9258 	if ( getname(asfd,tok)!=1 )
9259 return( false );
9260     }
9261     if ( sf->map!=NULL && sf->map->enc!=newmap ) {
9262 	EncMap *map = EncMapFromEncoding(sf,newmap);
9263 	EncMapFree(sf->map);
9264 	sf->map = map;
9265     }
9266     temp.map = sf->map;
9267     if ( strcmp(tok,"LayerCount:")==0 ) {
9268 	getint(asfd,&layercnt);
9269 	if ( layercnt>sf->layer_cnt ) {
9270 	    sf->layers = realloc(sf->layers,layercnt*sizeof(LayerInfo));
9271 	    memset(sf->layers+sf->layer_cnt,0,(layercnt-sf->layer_cnt)*sizeof(LayerInfo));
9272 	}
9273 	sf->layer_cnt = layercnt;
9274 	if ( getname(asfd,tok)!=1 )
9275 return( false );
9276     }
9277     while ( strcmp(tok,"Layer:")==0 ) {
9278 	int layer, o2;
9279 	getint(asfd,&layer);
9280 	getint(asfd,&o2);
9281 	if ( layer<sf->layer_cnt ) {
9282 	    sf->layers[layer].order2 = o2;
9283 		if (sf->layers[layer].name)
9284 		    free(sf->layers[layer].name);
9285 	    sf->layers[layer].name = SFDReadUTF7Str(asfd);
9286 	}
9287 	if ( getname(asfd,tok)!=1 )
9288 return( false );
9289     }
9290     if ( strcmp(tok,"MultiLayer:")==0 ) {
9291 	getint(asfd,&multilayer);
9292 	if ( getname(asfd,tok)!=1 )
9293 return( false );
9294     }
9295     if ( multilayer!=sf->multilayer ) {
9296 	if ( !multilayer )
9297 	    SFSplinesFromLayers(sf,false);
9298 	sf->multilayer = multilayer;
9299 	/* SFLayerChange(sf);*/		/* Shouldn't have any open windows, should not be needed */
9300     }
9301     if ( strcmp(tok,"BeginChars:")!=0 )
9302 return(false);
9303     SFRemoveDependencies(sf);
9304 
9305     getint(asfd,&cnt);
9306     if ( cnt>sf->glyphcnt ) {
9307 	sf->glyphs = realloc(sf->glyphs,cnt*sizeof(SplineChar *));
9308 	for ( i=sf->glyphcnt; i<cnt; ++i )
9309 	    sf->glyphs[i] = NULL;
9310 	sf->glyphcnt = sf->glyphmax = cnt;
9311     }
9312     while ( (sc = SFDGetChar(asfd,&temp,true))!=NULL ) {
9313 	ssf = sf;
9314 	for ( k=0; k<sf->subfontcnt; ++k ) {
9315 	    if ( sc->orig_pos<sf->subfonts[k]->glyphcnt ) {
9316 		ssf = sf->subfonts[k];
9317 		if ( SCWorthOutputting(ssf->glyphs[sc->orig_pos]))
9318 	break;
9319 	    }
9320 	}
9321 	if ( sc->orig_pos<ssf->glyphcnt ) {
9322 	    if ( ssf->glyphs[sc->orig_pos]!=NULL )
9323 		SplineCharFree(ssf->glyphs[sc->orig_pos]);
9324 	    ssf->glyphs[sc->orig_pos] = sc;
9325 	    sc->parent = ssf;
9326 	    sc->changed = true;
9327 	}
9328     }
9329     sf->changed = true;
9330     SFDFixupRefs(sf);
9331 return(true);
9332 }
9333 
SlurpRecovery(FILE * asfd,char * tok,int sizetok)9334 static SplineFont *SlurpRecovery(FILE *asfd,char *tok,int sizetok) {
9335     char *pt; int ch;
9336     SplineFont *sf;
9337 
9338     ch=nlgetc(asfd);
9339     ungetc(ch,asfd);
9340     if ( ch=='B' ) {
9341 	if ( getname(asfd,tok)!=1 )
9342 return(NULL);
9343 	if ( strcmp(tok,"Base:")!=0 )
9344 return(NULL);
9345 	while ( isspace(ch=nlgetc(asfd)) && ch!=EOF && ch!='\n' );
9346 	for ( pt=tok; ch!=EOF && ch!='\n'; ch = nlgetc(asfd) )
9347 	    if ( pt<tok+sizetok-2 )
9348 		*pt++ = ch;
9349 	*pt = '\0';
9350 	sf = LoadSplineFont(tok,0);
9351     } else {
9352 	sf = SplineFontNew();
9353 	sf->onlybitmaps = false;
9354 	strcpy(tok,"<New File>");
9355     }
9356     if ( sf==NULL )
9357 return( NULL );
9358 
9359     if ( !ModSF(asfd,sf)) {
9360 	SplineFontFree(sf);
9361 return( NULL );
9362     }
9363 return( sf );
9364 }
9365 
9366 /**
9367  * Asks the user whether or not to recover, skip or delete an autosaved file.
9368  * If requested by the user, this function will attempt to delete the file.
9369  * @param [in] filename The path to the autosaved file.
9370  * @param [in,out] state The current state.
9371  *                 state&1: Recover all. state&2: Forget all.
9372  * @param [out] asfd Location to store the file pointer to the autosaved file.
9373  * @return true iff the file is to be recovered. If true, asfd will hold the
9374  *         corresponding file pointer, which must be closed by the caller. If
9375  *         false, asfd will hold NULL.
9376  */
ask_about_file(char * filename,int * state,FILE ** asfd)9377 static int ask_about_file(char *filename, int *state, FILE **asfd) {
9378     int ret;
9379     char *buts[6];
9380     char buffer[800], *pt;
9381 
9382     if ((*asfd = fopen(filename, "r")) == NULL) {
9383         return false;
9384     } else if (*state&1) { //Recover all
9385         return true;
9386     } else if (*state&2) { //Forget all
9387         fclose(*asfd);
9388         *asfd = NULL;
9389         unlink(filename);
9390         return false;
9391     }
9392 
9393     fgets(buffer,sizeof(buffer),*asfd);
9394     rewind(*asfd);
9395     if (strncmp(buffer,"Base: ",6) != 0) {
9396         strcpy(buffer+6, "<New File>");
9397     }
9398     pt = buffer+6;
9399     if (strlen(buffer+6) > 70) {
9400         pt = strrchr(buffer+6,'/');
9401         if (pt == NULL)
9402             pt = buffer+6;
9403     }
9404 
9405     buts[0] = _("Yes"); buts[1] = _("Yes to _All");
9406     buts[2] = _("_Skip for now");
9407     buts[3] = _("Forget _to All"); buts[4] = _("_Forget about it");
9408     buts[5] = NULL;
9409     ret = ff_ask(_("Recover old edit"),(const char **) buts,0,3,_("You appear to have an old editing session on %s.\nWould you like to recover it?"), pt);
9410     switch (ret) {
9411         case 1: //Recover all
9412             *state = 1;
9413             break;
9414         case 2: //Skip one
9415             fclose(*asfd);
9416             *asfd = NULL;
9417             return false;
9418         case 3: //Forget all
9419             *state = 2;
9420             /* Fall through */
9421         case 4: //Forget one
9422             fclose(*asfd);
9423             *asfd = NULL;
9424             unlink(filename);
9425             return false;
9426         default: //Recover one
9427             break;
9428     }
9429     return true;
9430 }
9431 
SFRecoverFile(char * autosavename,int inquire,int * state)9432 SplineFont *SFRecoverFile(char *autosavename,int inquire,int *state) {
9433     FILE *asfd;
9434     SplineFont *ret;
9435     char tok[1025];
9436 
9437     if (!inquire) {
9438         *state = 1; //Default to recover all
9439     }
9440     if (!ask_about_file(autosavename, state, &asfd)) {
9441 return( NULL );
9442     }
9443     locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
9444     switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
9445     ret = SlurpRecovery(asfd,tok,sizeof(tok));
9446     if ( ret==NULL ) {
9447 	const char *buts[3];
9448 	buts[0] = "_Forget It"; buts[1] = "_Try Again"; buts[2] = NULL;
9449 	if ( ff_ask(_("Recovery Failed"),(const char **) buts,0,1,_("Automagic recovery of changes to %.80s failed.\nShould FontForge try again to recover next time you start it?"),tok)==0 )
9450 	    unlink(autosavename);
9451     }
9452     switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
9453     fclose(asfd);
9454     if ( ret )
9455 	ret->autosavename = copy(autosavename);
9456 return( ret );
9457 }
9458 
SFAutoSave(SplineFont * sf,EncMap * map)9459 void SFAutoSave(SplineFont *sf,EncMap *map) {
9460     int i, k, max;
9461     FILE *asfd;
9462     SplineFont *ssf;
9463 
9464     if ( no_windowing_ui )		/* No autosaves when just scripting */
9465 return;
9466 
9467     if ( sf->cidmaster!=NULL ) sf=sf->cidmaster;
9468     asfd = fopen(sf->autosavename,"w");
9469     if ( asfd==NULL )
9470 return;
9471 
9472     max = sf->glyphcnt;
9473     for ( i=0; i<sf->subfontcnt; ++i )
9474 	if ( sf->subfonts[i]->glyphcnt>max ) max = sf->subfonts[i]->glyphcnt;
9475 
9476     locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
9477     switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
9478     if ( !sf->new && sf->origname!=NULL )	/* might be a new file */
9479 	fprintf( asfd, "Base: %s%s\n", sf->origname,
9480 		sf->compression==0?"":compressors[sf->compression-1].ext );
9481     fprintf( asfd, "Encoding: %s\n", map->enc->enc_name );
9482     fprintf( asfd, "UnicodeInterp: %s\n", unicode_interp_names[sf->uni_interp]);
9483     fprintf( asfd, "LayerCount: %d\n", sf->layer_cnt );
9484     for ( i=0; i<sf->layer_cnt; ++i ) {
9485 	fprintf( asfd, "Layer: %d %d ", i, sf->layers[i].order2 );
9486 	SFDDumpUTF7Str(asfd,sf->layers[i].name);
9487 	putc('\n',asfd);
9488     }
9489     if ( sf->multilayer )
9490 	fprintf( asfd, "MultiLayer: %d\n", sf->multilayer );
9491     fprintf( asfd, "BeginChars: %d\n", max );
9492     for ( i=0; i<max; ++i ) {
9493 	ssf = sf;
9494 	for ( k=0; k<sf->subfontcnt; ++k ) {
9495 	    if ( i<sf->subfonts[k]->glyphcnt ) {
9496 		ssf = sf->subfonts[k];
9497 		if ( SCWorthOutputting(ssf->glyphs[i]))
9498 	break;
9499 	    }
9500 	}
9501 	if ( ssf->glyphs[i]!=NULL && ssf->glyphs[i]->changed )
9502 	    SFDDumpChar( asfd,ssf->glyphs[i],map,NULL,false,1);
9503     }
9504     fprintf( asfd, "EndChars\n" );
9505     fprintf( asfd, "EndSplineFont\n" );
9506     fclose(asfd);
9507     switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
9508     sf->changed_since_autosave = false;
9509 }
9510 
SFClearAutoSave(SplineFont * sf)9511 void SFClearAutoSave(SplineFont *sf) {
9512     int i;
9513     SplineFont *ssf;
9514 
9515     if ( sf->cidmaster!=NULL ) sf = sf->cidmaster;
9516     sf->changed_since_autosave = false;
9517     for ( i=0; i<sf->subfontcnt; ++i ) {
9518 	ssf = sf->subfonts[i];
9519 	ssf->changed_since_autosave = false;
9520 	if ( ssf->autosavename!=NULL ) {
9521 	    unlink( ssf->autosavename );
9522 	    free( ssf->autosavename );
9523 	    ssf->autosavename = NULL;
9524 	}
9525     }
9526     if ( sf->autosavename==NULL )
9527 return;
9528     unlink(sf->autosavename);
9529     free(sf->autosavename);
9530     sf->autosavename = NULL;
9531 }
9532 
NamesReadSFD(char * filename)9533 char **NamesReadSFD(char *filename) {
9534     FILE *sfd = fopen(filename,"r");
9535     char tok[2000];
9536     char **ret = NULL;
9537     int eof;
9538 
9539     if ( sfd==NULL )
9540 return( NULL );
9541     locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
9542     switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
9543     if ( SFDStartsCorrectly(sfd,tok)!=-1 ) {
9544 	while ( !feof(sfd)) {
9545 	    if ( (eof = getname(sfd,tok))!=1 ) {
9546 		if ( eof==-1 )
9547 	break;
9548 		geteol(sfd,tok);
9549 	continue;
9550 	    }
9551 	    if ( strmatch(tok,"FontName:")==0 ) {
9552 		getname(sfd,tok);
9553 		ret = malloc(2*sizeof(char*));
9554 		ret[0] = copy(tok);
9555 		ret[1] = NULL;
9556 	break;
9557 	    }
9558 	}
9559     }
9560     switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
9561     fclose(sfd);
9562 return( ret );
9563 }
9564