1 /* Copyright (C) 2000-2008 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 #include "fontforgevw.h"
28 #include "ttf.h"
29 #include <stdio.h>
30 #include <math.h>
31 #include <stdlib.h>
32 #ifdef __need_size_t
33 /* This is a bug on the mac, someone defines this and leaves it defined */
34 /*  that means when I load stddef.h it only defines size_t and doesn't */
35 /*  do offset_of, which is what I need */
36 # undef __need_size_t
37 #endif
38 #include <stddef.h>
39 #include <string.h>
40 #include <utype.h>
41 #include <ustring.h>
42 
43 
44 
45 /* ************************************************************************** */
46 /* ******************************* Parse feat ******************************* */
47 /* ************************************************************************** */
48 
49 #include <gfile.h>
50 
51 struct nameid {
52     uint16 strid;
53     uint16 platform, specific, language;
54     char *utf8_str;
55     struct nameid *next;
56 };
57 
58 struct tablekeywords {
59     char *name;
60     int size;			/* 1=>byte, 2=>short, 4=>int32 */
61     int cnt;			/* normally 1, but 10 for panose, -1 for infinite */
62     int offset;			/* -1 => parse but don't store */
63 };
64 
65 struct tablevalues {
66     int index;			/* in the table key structure above */
67     int value;
68     uint8 panose_vals[10];
69     struct tablevalues *next;
70 };
71 
72 enum feat_type { ft_lookup_start, ft_lookup_end, ft_feat_start, ft_feat_end,
73     ft_table, ft_names, ft_gdefclasses, ft_lcaret, ft_tablekeys,
74     ft_sizeparams,
75     ft_subtable, ft_script, ft_lang, ft_lookupflags, ft_langsys,
76     ft_pst, ft_pstclass, ft_fpst, ft_ap, ft_lookup_ref };
77 struct feat_item {
78     uint16 /* enum feat_type */ type;
79     uint8 ticked;
80     union {
81 	SplineChar *sc;		/* For psts, aps */
82 	char *class;		/* List of glyph names for kerning by class, lcarets */
83 	char *lookup_name;	/* for lookup_start/ lookup_ref */
84 	uint32 tag;		/* for feature/script/lang tag */
85 	int *params;		/* size params */
86 	struct tablekeywords *offsets;
87 	char **gdef_classes;
88     } u1;
89     union {
90 	PST *pst;
91 		/* For kerning by class we'll generate an invalid pst with the class as the "paired" field */
92 	FPST *fpst;
93 	AnchorPoint *ap;
94 	int lookupflags;
95 	struct scriptlanglist *sl;	/* Default langsyses for features/langsys */
96 	int exclude_dflt;		/* for lang tags */
97 	struct nameid *names;		/* size params */
98 	struct tablevalues *tvals;
99 	int16 *lcaret;
100     } u2;
101     char *mark_class;			/* For mark to base-ligature-mark, names of all marks which attach to this anchor */
102     struct feat_item *next, *lookup_next;
103 };
104 
strcmpD(const void * _str1,const void * _str2)105 static int strcmpD(const void *_str1, const void *_str2) {
106     const char *str1 = *(const char **)_str1, *str2 = *(const char **) _str2;
107 return( strcmp(str1,str2));
108 }
109 
110 /* Order glyph classes just so we can do a simple string compare to check for */
111 /*  class match. So the order doesn't really matter, just so it is consistent */
fea_canonicalClassOrder(char * class)112 static char *fea_canonicalClassOrder(char *class) {
113     int name_cnt, i;
114     char *pt, **names, *cpt;
115     char *temp = copy(class);
116 
117     name_cnt = 0;
118     for ( pt = class; ; ) {
119 	while ( *pt==' ' ) ++pt;
120 	if ( *pt=='\0' )
121     break;
122 	for ( ; *pt!=' ' && *pt!='\0'; ++pt );
123 	++name_cnt;
124     }
125 
126     names = galloc(name_cnt*sizeof(char *));
127     name_cnt = 0;
128     for ( pt = temp; ; ) {
129 	while ( *pt==' ' ) ++pt;
130 	if ( *pt=='\0' )
131     break;
132 	for ( names[name_cnt++]=pt ; *pt!=' ' && *pt!='\0'; ++pt );
133 	if ( *pt==' ' )
134 	    *pt++ = '\0';
135     }
136 
137     qsort(names,name_cnt,sizeof(char *),strcmpD);
138     cpt = class;
139     for ( i=0; i<name_cnt; ++i ) {
140 	strcpy(cpt,names[i]);
141 	cpt += strlen(cpt);
142 	*cpt++ = ' ';
143     }
144     if ( name_cnt!=0 )
145 	cpt[-1] = '\0';
146     free(names);
147     free(temp);
148 
149 return( class );
150 }
151 
fea_classesIntersect(char * class1,char * class2)152 static int fea_classesIntersect(char *class1, char *class2) {
153     char *pt1, *start1, *pt2, *start2;
154     int ch1, ch2;
155 
156     for ( pt1=class1 ; ; ) {
157         while ( *pt1==' ' ) ++pt1;
158         if ( *pt1=='\0' )
159             return( 0 );
160         for ( start1 = pt1; *pt1!=' ' && *pt1!='\0'; ++pt1 );
161         ch1 = *pt1; *pt1 = '\0';
162         for ( pt2=class2 ; ; ) {
163             while ( *pt2==' ' ) ++pt2;
164             if ( *pt2=='\0' )
165                 break;
166             for ( start2 = pt2; *pt2!=' ' && *pt2!='\0'; ++pt2 );
167             ch2 = *pt2; *pt2 = '\0';
168             if ( strcmp(start1,start2)==0 ) {
169                 *pt2 = ch2; *pt1 = ch1;
170                 return( 1 );
171             }
172             *pt2 = ch2;
173         }
174         *pt1 = ch1;
175     }
176 }
177 
178 
179 #define SKIP_SPACES(s, i)                       \
180     do {                                        \
181         while ((s)[i] == ' ')                   \
182             i++;                                \
183     }                                           \
184     while (0)
185 
186 #define FIND_SPACE(s, i)                        \
187     do {                                        \
188         while ((s)[i] != ' ' && (s)[i] != '\0') \
189             i++;                                \
190     }                                           \
191     while (0)
192 
193 
fea_classesSplit(char * class1,char * class2)194 static char *fea_classesSplit(char *class1, char *class2) {
195     char *intersection;
196     int len = strlen(class1), len2 = strlen(class2);
197     int ix;
198     int i, j, i_end, j_end;
199     int length;
200     int match_found;
201 
202     if ( len2>len ) len = len2;
203     intersection = galloc(len+1);
204     ix = 0;
205 
206     i = 0;
207     SKIP_SPACES(class1, i);
208     while (class1[i] != '\0') {
209         i_end = i;
210         FIND_SPACE(class1, i_end);
211 
212         length = i_end - i;
213 
214         match_found = 0;
215         j = 0;
216         SKIP_SPACES(class2, j);
217         while (!match_found && class2[j] != '\0') {
218             j_end = j;
219             FIND_SPACE(class2, j_end);
220 
221             if (length == j_end - j && strncmp(class1 + i, class2 + j, length) == 0) {
222                 match_found = 1;
223 
224                 if (ix != 0) {
225                     intersection[ix] = ' ';
226                     ix++;
227                 }
228                 memcpy(intersection + ix, class1 + i, length * sizeof (char));
229                 ix += length;
230 
231                 SKIP_SPACES(class1, i_end);
232                 memmove(class1 + i, class1 + i_end, (strlen(class1 + i_end) + 1) * sizeof (char));
233                 SKIP_SPACES(class2, j_end);
234                 memmove(class2 + j, class2 + j_end, (strlen(class2 + j_end) + 1) * sizeof (char));
235             } else {
236                 j = j_end;
237                 SKIP_SPACES(class2, j);
238             }
239         }
240         if (!match_found) {
241             i = i_end;
242             SKIP_SPACES(class1, i);
243         }
244     }
245     intersection[ix] = '\0';
246     return( intersection );
247 }
248 
249 #define MAXT	40
250 #define MAXI	5
251 struct parseState {
252     char tokbuf[MAXT+1];
253     long value;
254     enum toktype { tk_name, tk_class, tk_int, tk_char, tk_cid, tk_eof,
255 /* keywords */
256 	tk_firstkey,
257 	tk_anchor=tk_firstkey, tk_anonymous, tk_by, tk_caret, tk_cursive, tk_device,
258 	tk_enumerate, tk_excludeDFLT, tk_exclude_dflt, tk_feature, tk_from,
259 	tk_ignore, tk_ignoreDFLT, tk_ignoredflt, tk_IgnoreBaseGlyphs,
260 	tk_IgnoreLigatures, tk_IgnoreMarks, tk_include, tk_includeDFLT,
261 	tk_include_dflt, tk_language, tk_languagesystem, tk_lookup,
262 	tk_lookupflag, tk_mark, tk_nameid, tk_NULL, tk_parameters, tk_position,
263 	tk_required, tk_RightToLeft, tk_script, tk_substitute, tk_subtable,
264 	tk_table, tk_useExtension
265     } type;
266     uint32 tag;
267     int could_be_tag;
268     FILE *inlist[MAXI];
269     int inc_depth;
270     int line[MAXI];
271     char *filename[MAXI];
272     int err_count;
273     unsigned int warned_about_not_cid: 1;
274     unsigned int lookup_in_sf_warned: 1;
275     unsigned int in_vkrn: 1;
276     unsigned int backedup: 1;
277     unsigned int skipping: 1;
278     SplineFont *sf;
279     struct scriptlanglist *def_langsyses;
280     struct glyphclasses { char *classname, *glyphs; struct glyphclasses *next; } *classes;
281     struct feat_item *sofar;
282     int base;			/* normally numbers are base 10, but in the case of languages in stringids, they can be octal or hex */
283     OTLookup *created, *last;	/* Ordered, but not sorted into GSUB, GPOS yet */
284     AnchorClass *accreated;
285 };
286 
287 static struct keywords {
288     char *name;
289     enum toktype tok;
290 } fea_keywords[] = {
291 /* list must be in toktype order */
292     { "name", tk_name }, { "glyphclass", tk_class }, { "integer", tk_int },
293     { "random character", tk_char}, { "cid", tk_cid }, { "EOF", tk_eof },
294 /* keywords now */
295     { "anchor", tk_anchor },
296     { "anonymous", tk_anonymous },
297     { "by", tk_by },
298     { "caret", tk_caret },
299     { "cursive", tk_cursive },
300     { "device", tk_device },
301     { "enumerate", tk_enumerate },
302     { "excludeDFLT", tk_excludeDFLT },
303     { "exclude_dflt", tk_exclude_dflt },
304     { "feature", tk_feature },
305     { "from", tk_from },
306     { "ignore", tk_ignore },
307     { "IgnoreBaseGlyphs", tk_IgnoreBaseGlyphs },
308     { "IgnoreLigatures", tk_IgnoreLigatures },
309     { "IgnoreMarks", tk_IgnoreMarks },
310     { "include", tk_include },
311     { "includeDFLT", tk_includeDFLT },
312     { "include_dflt", tk_include_dflt },
313     { "language", tk_language },
314     { "languagesystem", tk_languagesystem },
315     { "lookup", tk_lookup },
316     { "lookupflag", tk_lookupflag },
317     { "mark", tk_mark },
318     { "nameid", tk_nameid },
319     { "NULL", tk_NULL },
320     { "parameters", tk_parameters },
321     { "position", tk_position },
322     { "required", tk_required },
323     { "RightToLeft", tk_RightToLeft },
324     { "script", tk_script },
325     { "substitute", tk_substitute },
326     { "subtable", tk_subtable },
327     { "table", tk_table },
328     { "useExtension", tk_useExtension },
329 /* synonyms */
330     { "sub", tk_substitute },
331     { "pos", tk_position },
332     { "enum", tk_enumerate },
333     { "anon", tk_anonymous },
334     { NULL, 0 }
335 };
336 
337 static struct tablekeywords hhead_keys[] = {
338     { "CaretOffset", sizeof(short), 1, -1 },		/* Don't even know what this is! */
339     { "Ascender", sizeof(short), 1, offsetof(struct pfminfo,hhead_ascent)+offsetof(SplineFont,pfminfo) },
340     { "Descender", sizeof(short), 1, offsetof(struct pfminfo,hhead_descent)+offsetof(SplineFont,pfminfo) },
341     { "LineGap", sizeof(short), 1, offsetof(struct pfminfo,linegap)+offsetof(SplineFont,pfminfo) },
342     { NULL, 0, 0, 0 }
343 };
344 
345 static struct tablekeywords vhead_keys[] = {
346     { "VertTypoAscender", sizeof(short), 1, -1 },
347     { "VertTypoDescender", sizeof(short), 1, -1 },
348     { "VertTypoLineGap", sizeof(short), 1, offsetof(struct pfminfo,vlinegap)+offsetof(SplineFont,pfminfo) },
349     { NULL, 0, 0, 0 }
350 };
351 
352 static struct tablekeywords os2_keys[] = {
353     { "FSType", sizeof(short), 1, offsetof(struct pfminfo,fstype)+offsetof(SplineFont,pfminfo) },
354     { "Panose", sizeof(uint8), 10, offsetof(struct pfminfo,panose)+offsetof(SplineFont,pfminfo) },
355     { "UnicodeRange", sizeof(short), -1, -1 },
356     { "CodePageRange", sizeof(short), -1, -1 },
357     { "TypoAscender", sizeof(short), 1, offsetof(struct pfminfo,os2_typoascent)+offsetof(SplineFont,pfminfo) },
358     { "TypoDescender", sizeof(short), 1, offsetof(struct pfminfo,os2_typodescent)+offsetof(SplineFont,pfminfo) },
359     { "TypoLineGap", sizeof(short), 1, offsetof(struct pfminfo,os2_typolinegap)+offsetof(SplineFont,pfminfo) },
360     { "winAscent", sizeof(short), 1, offsetof(struct pfminfo,os2_winascent)+offsetof(SplineFont,pfminfo) },
361     { "winDescent", sizeof(short), 1, offsetof(struct pfminfo,os2_windescent)+offsetof(SplineFont,pfminfo) },
362     { "XHeight", sizeof(short), 1, -1 },
363     { "CapHeight", sizeof(short), 1, -1 },
364     { "WeightClass", sizeof(short), 1, offsetof(struct pfminfo,weight)+offsetof(SplineFont,pfminfo) },
365     { "WidthClass", sizeof(short), 1, offsetof(struct pfminfo,width)+offsetof(SplineFont,pfminfo) },
366     { "Vendor", sizeof(short), 1, offsetof(struct pfminfo,os2_vendor)+offsetof(SplineFont,pfminfo) },
367     { NULL, 0, 0, 0 }
368 };
369 
370 
371 static void fea_ParseTok(struct parseState *tok);
372 
fea_handle_include(struct parseState * tok)373 static void fea_handle_include(struct parseState *tok) {
374     FILE *in;
375     char namebuf[1025], *pt, *filename;
376     int ch;
377 
378     fea_ParseTok(tok);
379     if ( tok->type!=tk_char || tok->tokbuf[0]!='(' ) {
380 	LogError(_("Unparseable include on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
381 	++tok->err_count;
382 return;
383     }
384 
385     in = tok->inlist[tok->inc_depth];
386     ch = getc(in);
387     while ( isspace(ch))
388 	ch = getc(in);
389     pt = namebuf;
390     while ( ch!=EOF && ch!=')' && pt<namebuf+sizeof(namebuf)-1 ) {
391 	*pt++ = ch;
392 	ch = getc(in);
393     }
394     if ( ch!=EOF && ch!=')' ) {
395 	while ( ch!=EOF && ch!=')' )
396 	    ch = getc(in);
397 	LogError(_("Include filename too long on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
398 	++tok->err_count;
399     }
400     while ( pt>=namebuf+1 && isspace(pt[-1]) )
401 	--pt;
402     *pt = '\0';
403     if ( ch!=')' ) {
404 	if ( ch==EOF )
405 	    LogError(_("End of file in include on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
406 	else
407 	    LogError(_("Missing close parenthesis in include on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
408 	++tok->err_count;
409 return;
410     }
411 
412     if ( pt==namebuf ) {
413 	LogError(_("No filename specified in include on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
414 	++tok->err_count;
415 return;
416     }
417 
418     if ( tok->inc_depth>=MAXI-1 ) {
419 	LogError(_("Includes nested too deeply on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
420 	++tok->err_count;
421 return;
422     }
423 
424     if ( *namebuf=='/' ||
425 	    ( pt = strrchr(tok->filename[tok->inc_depth],'/') )==NULL )
426 	filename=copy(namebuf);
427     else {
428 	*pt = '\0';
429 	filename = GFileAppendFile(tok->filename[tok->inc_depth],namebuf,false);
430 	*pt = '/';
431     }
432     in = fopen(filename,"r");
433     if ( in==NULL ) {
434 	LogError(_("Could not open include file (%s) on line %d of %s"),
435 		filename, tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
436 	++tok->err_count;
437 	free(filename);
438 return;
439     }
440 
441     ++tok->inc_depth;
442     tok->filename[tok->inc_depth] = filename;
443     tok->inlist[tok->inc_depth] = in;
444     tok->line[tok->inc_depth] = 1;
445     fea_ParseTok(tok);
446 }
447 
fea_ParseTok(struct parseState * tok)448 static void fea_ParseTok(struct parseState *tok) {
449     FILE *in = tok->inlist[tok->inc_depth];
450     int ch, peekch = 0;
451     char *pt, *start;
452 
453     if ( tok->backedup ) {
454 	tok->backedup = false;
455 return;
456     }
457 
458   skip_whitespace:
459     ch = getc(in);
460     while ( isspace(ch) || ch=='#' ) {
461 	if ( ch=='#' )
462 	    while ( (ch=getc(in))!=EOF && ch!='\n' && ch!='\r' );
463 	if ( ch=='\n' || ch=='\r' ) {
464 	    if ( ch=='\r' ) {
465 		ch = getc(in);
466 		if ( ch!='\n' )
467 		    ungetc(ch,in);
468 	    }
469 	    ++tok->line[tok->inc_depth];
470 	}
471 	ch = getc(in);
472     }
473 
474     tok->could_be_tag = 0;
475     if ( ch==EOF ) {
476 	if ( tok->inc_depth>0 ) {
477 	    fclose(tok->inlist[tok->inc_depth]);
478 	    free(tok->filename[tok->inc_depth]);
479 	    in = tok->inlist[--tok->inc_depth];
480   goto skip_whitespace;
481 	}
482 	tok->type = tk_eof;
483 	strcpy(tok->tokbuf,"EOF");
484 return;
485     }
486 
487     start = pt = tok->tokbuf;
488     if ( ch=='\\' || ch=='-' ) {
489 	peekch=getc(in);
490 	ungetc(peekch,in);
491     }
492 
493     if ( isdigit(ch) || ch=='+' || ((ch=='-' || ch=='\\') && isdigit(peekch)) ) {
494 	tok->type = tk_int;
495 	if ( ch=='-' || ch=='+' ) {
496 	    if ( ch=='-' ) {
497 		*pt++ = ch;
498 		start = pt;
499 	    }
500 	    ch = getc(in);
501 	} else if ( ch=='\\' ) {
502 	    ch = getc(in);
503 	    tok->type = tk_cid;
504 	}
505 	while ( (isdigit( ch ) ||
506 		(tok->base==0 && (ch=='x' || ch=='X' || (ch>='a' && ch<='f') || (ch>='A' && ch<='F'))))
507 		&& pt<tok->tokbuf+15 ) {
508 	    *pt++ = ch;
509 	    ch = getc(in);
510 	}
511 	if ( isdigit(ch)) {
512 	    LogError(_("Number too long on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
513 	    ++tok->err_count;
514 	} else if ( pt==start ) {
515 	    LogError(_("Missing number on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
516 	    ++tok->err_count;
517 	}
518 	ungetc(ch,in);
519 	*pt = '\0';
520 	tok->value = strtol(tok->tokbuf,NULL,tok->base);
521 return;
522     } else if ( ch=='@' || ch=='_' || ch=='\\' || isalnum(ch)) {	/* Names can't start with dot */
523 	int check_keywords = true;
524 	tok->type = tk_name;
525 	if ( ch=='@' ) {
526 	    tok->type = tk_class;
527 	    *pt++ = ch;
528 	    start = pt;
529 	    ch = getc(in);
530 	    check_keywords = false;
531 	} else if ( ch=='\\' ) {
532 	    ch = getc(in);
533 	    check_keywords = false;
534 	}
535 	while (( isalnum(ch) || ch=='_' || ch=='.' ) && pt<start+31 ) {
536 	    *pt++ = ch;
537 	    ch = getc(in);
538 	}
539 	*pt = '\0';
540 	ungetc(ch,in);
541 	if ( isalnum(ch) || ch=='_' || ch=='.' ) {
542 	    LogError(_("Name too long on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
543 	    ++tok->err_count;
544 	} else if ( pt==start ) {
545 	    LogError(_("Missing name on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
546 	    ++tok->err_count;
547 	}
548 
549 	if ( check_keywords ) {
550 	    int i;
551 	    for ( i=tk_firstkey; fea_keywords[i].name!=NULL; ++i ) {
552 		if ( strcmp(fea_keywords[i].name,tok->tokbuf)==0 ) {
553 		    tok->type = fea_keywords[i].tok;
554 	    break;
555 		}
556 	    }
557 	    if ( tok->type==tk_include )
558 		fea_handle_include(tok);
559 	}
560 	if ( tok->type==tk_name && pt-tok->tokbuf<=4 && pt!=tok->tokbuf ) {
561 	    unsigned char tag[4];
562 	    tok->could_be_tag = true;
563 	    memset(tag,' ',4);
564 	    tag[0] = tok->tokbuf[0];
565 	    if ( tok->tokbuf[1]!='\0' ) {
566 		tag[1] = tok->tokbuf[1];
567 		if ( tok->tokbuf[2]!='\0' ) {
568 		    tag[2] = tok->tokbuf[2];
569 		    if ( tok->tokbuf[3]!='\0' )
570 			tag[3] = tok->tokbuf[3];
571 		}
572 	    }
573 	    tok->tag = (tag[0]<<24) | (tag[1]<<16) | (tag[2]<<8) | tag[3];
574 	}
575     } else {
576 	/* I've already handled the special characters # @ and \ */
577 	/*  so don't treat them as errors here, if they occur they will be out of context */
578 	if ( ch==';' || ch==',' || ch=='-' || ch=='=' || ch=='\'' || ch=='"' ||
579 		ch=='{' || ch=='}' ||
580 		ch=='[' || ch==']' ||
581 		ch=='<' || ch=='>' ||
582 		ch=='(' || ch==')' ) {
583 	    tok->type = tk_char;
584 	    tok->tokbuf[0] = ch;
585 	    tok->tokbuf[1] = '\0';
586 	} else {
587 	    if ( !tok->skipping ) {
588 		LogError(_("Unexpected character (0x%02X) on line %d of %s"), ch, tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
589 		++tok->err_count;
590 	    }
591   goto skip_whitespace;
592 	}
593     }
594 }
595 
fea_ParseTag(struct parseState * tok)596 static void fea_ParseTag(struct parseState *tok) {
597     /* The tag used for OS/2 doesn't get parsed properly */
598     /* So if we know we are looking for a tag do some fixups */
599 
600     fea_ParseTok(tok);
601     if ( tok->type==tk_name && tok->could_be_tag &&
602 	    tok->tag==CHR('O','S',' ',' ') ) {
603 	FILE *in = tok->inlist[tok->inc_depth];
604 	int ch;
605 	ch = getc(in);
606 	if ( ch=='/' ) {
607 	    ch = getc(in);
608 	    if ( ch=='2' ) {
609 		tok->tag = CHR('O','S','/','2');
610 	    } else {
611 		tok->tag = CHR('O','S','/',' ');
612 		ungetc(ch,in);
613 	    }
614 	} else
615 	    ungetc(ch,in);
616     }
617 }
618 
fea_UnParseTok(struct parseState * tok)619 static void fea_UnParseTok(struct parseState *tok) {
620     tok->backedup = true;
621 }
622 
fea_ParseDeciPoints(struct parseState * tok)623 static int fea_ParseDeciPoints(struct parseState *tok) {
624     /* When parsing size features floating point numbers are allowed */
625     /*  but they should be converted to ints by multiplying by 10 */
626     /* (not my convention) */
627 
628     fea_ParseTok(tok);
629     if ( tok->type==tk_int ) {
630 	FILE *in = tok->inlist[tok->inc_depth];
631 	char *pt = tok->tokbuf + strlen(tok->tokbuf);
632 	int ch;
633 	ch = getc(in);
634 	if ( ch=='.' ) {
635 	    *pt++ = ch;
636 	    while ( (ch = getc(in))!=EOF && isdigit(ch)) {
637 		if ( pt<tok->tokbuf+sizeof(tok->tokbuf)-1 )
638 		    *pt++ = ch;
639 	    }
640 	    *pt = '\0';
641 	    tok->value = rint(strtod(tok->tokbuf,NULL)*10.0);
642 	}
643 	if ( ch!=EOF )
644 	    ungetc(ch,in);
645     } else {
646 	LogError(_("Expected '%s' on line %d of %s"), fea_keywords[tk_int],
647 		tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
648 	++tok->err_count;
649 	tok->value = -1;
650     }
651 return( tok->value );
652 }
653 
fea_TokenMustBe(struct parseState * tok,enum toktype type,int ch)654 static void fea_TokenMustBe(struct parseState *tok, enum toktype type, int ch) {
655     fea_ParseTok(tok);
656     if ( type==tk_char && (tok->type!=type || tok->tokbuf[0]!=ch) ) {
657 	LogError(_("Expected '%c' on line %d of %s"), ch, tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
658 	++tok->err_count;
659     } else if ( type!=tk_char && tok->type!=type ) {
660 	LogError(_("Expected '%s' on line %d of %s"), fea_keywords[type],
661 		tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
662 	++tok->err_count;
663     }
664 }
665 
fea_skip_to_semi(struct parseState * tok)666 static void fea_skip_to_semi(struct parseState *tok) {
667     int nest=0;
668 
669     while ( tok->type!=tk_char || tok->tokbuf[0]!=';' || nest>0 ) {
670 	fea_ParseTok(tok);
671 	if ( tok->type==tk_char ) {
672 	    if ( tok->tokbuf[0]=='{' ) ++nest;
673 	    else if ( tok->tokbuf[0]=='}' ) --nest;
674 	    if ( nest<0 )
675     break;
676 	}
677 	if ( tok->type==tk_eof )
678     break;
679     }
680 }
681 
fea_skip_to_close_curly(struct parseState * tok)682 static void fea_skip_to_close_curly(struct parseState *tok) {
683     int nest=0;
684 
685     tok->skipping = true;
686     /* The table blocks have slightly different syntaxes and can take strings */
687     /* and floating point numbers. So don't complain about unknown chars when */
688     /*  in a table (that's skipping) */
689     while ( tok->type!=tk_char || tok->tokbuf[0]!='}' || nest>0 ) {
690 	fea_ParseTok(tok);
691 	if ( tok->type==tk_char ) {
692 	    if ( tok->tokbuf[0]=='{' ) ++nest;
693 	    else if ( tok->tokbuf[0]=='}' ) --nest;
694 	}
695 	if ( tok->type==tk_eof )
696     break;
697     }
698     tok->skipping = false;
699 }
700 
fea_now_semi(struct parseState * tok)701 static void fea_now_semi(struct parseState *tok) {
702     if ( tok->type!=tk_char || tok->tokbuf[0]!=';' ) {
703 	LogError(_("Expected ';' at statement end on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
704 	fea_skip_to_semi(tok);
705 	++tok->err_count;
706 return;
707     }
708 }
709 
fea_end_statement(struct parseState * tok)710 static void fea_end_statement(struct parseState *tok) {
711     fea_ParseTok(tok);
712     fea_now_semi(tok);
713 }
714 
fea_lookup_class(struct parseState * tok,char * classname)715 static struct glyphclasses *fea_lookup_class(struct parseState *tok,char *classname) {
716     struct glyphclasses *test;
717 
718     for ( test=tok->classes; test!=NULL; test=test->next ) {
719 	if ( strcmp(classname,test->classname)==0 )
720 return( test );
721     }
722 return( NULL );
723 }
724 
fea_lookup_class_complain(struct parseState * tok,char * classname)725 static char *fea_lookup_class_complain(struct parseState *tok,char *classname) {
726     struct glyphclasses *test;
727 
728     for ( test=tok->classes; test!=NULL; test=test->next ) {
729 	if ( strcmp(classname,test->classname)==0 )
730 return( copy( test->glyphs) );
731     }
732     LogError(_("Use of undefined glyph class, %s, on line %d of %s"), classname, tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
733     ++tok->err_count;
734 return( NULL );
735 }
736 
fea_AddClassDef(struct parseState * tok,char * classname,char * contents)737 static void fea_AddClassDef(struct parseState *tok,char *classname,char *contents) {
738     struct glyphclasses *test;
739 
740     test = fea_lookup_class(tok,classname);
741     if ( test==NULL ) {
742 	test=chunkalloc(sizeof(struct glyphclasses));
743 	test->classname = classname;
744 	test->next = tok->classes;
745 	tok->classes = test;
746     } else {
747 	free(classname);
748 	free(test->glyphs);
749     }
750     test->glyphs = contents;
751 }
752 
fea_AddGlyphs(char ** _glyphs,int * _max,int cnt,char * contents)753 static int fea_AddGlyphs(char **_glyphs, int *_max, int cnt, char *contents ) {
754     int len = strlen(contents);
755     char *glyphs = *_glyphs;
756     /* Append a glyph name, etc. to a glyph class */
757 
758     if ( glyphs==NULL ) {
759 	glyphs = copy(contents);
760 	cnt = *_max = len;
761     } else {
762 	if ( *_max-cnt <= len+1 )
763 	    glyphs = grealloc(glyphs,(*_max+=200+len+1)+1);
764 	glyphs[cnt++] = ' ';
765 	strcpy(glyphs+cnt,contents);
766 	cnt += strlen(contents);
767     }
768     free(contents);
769     *_glyphs = glyphs;
770 return( cnt );
771 }
772 
fea_cid_validate(struct parseState * tok,int cid)773 static char *fea_cid_validate(struct parseState *tok,int cid) {
774     int i, max;
775     SplineFont *maxsf;
776     SplineChar *sc;
777     EncMap *map;
778 
779     if ( tok->sf->subfontcnt==0 ) {
780 	if ( !tok->warned_about_not_cid ) {
781 	    LogError(_("Reference to a CID in a non-CID-keyed font on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
782 	    tok->warned_about_not_cid = true;
783 	}
784 	++tok->err_count;
785 return(NULL);
786     }
787     max = 0; maxsf = NULL;
788     for ( i=0; i<tok->sf->subfontcnt; ++i ) {
789 	SplineFont *sub = tok->sf->subfonts[i];
790 	if ( cid<sub->glyphcnt && sub->glyphs[cid]!=NULL )
791 return( sub->glyphs[cid]->name );
792 	if ( sub->glyphcnt>max ) {
793 	    max = sub->glyphcnt;
794 	    maxsf = sub;
795 	}
796     }
797     /* Not defined, try to create it */
798     if ( maxsf==NULL )		/* No subfonts */
799 return( NULL );
800     if ( cid>=maxsf->glyphcnt ) {
801 	struct cidmap *cidmap = FindCidMap(tok->sf->cidregistry,tok->sf->ordering,tok->sf->supplement,tok->sf);
802 	if ( cidmap==NULL || cid>=MaxCID(cidmap) )
803 return( NULL );
804 	SFExpandGlyphCount(maxsf,MaxCID(cidmap));
805     }
806     if ( cid>=maxsf->glyphcnt )
807 return( NULL );
808     map = EncMap1to1(maxsf->glyphcnt);
809     sc = SFMakeChar(maxsf,map,cid);
810     EncMapFree(map);
811     if ( sc==NULL )
812 return( NULL );
813 return( copy( sc->name ));
814 }
815 
fea_glyphname_get(struct parseState * tok,char * name)816 static SplineChar *fea_glyphname_get(struct parseState *tok,char *name) {
817     SplineFont *sf = tok->sf;
818     SplineChar *sc = SFGetChar(sf,-1,name);
819     int enc, gid;
820 
821     if ( sf->subfontcnt!=0 ) {
822 	LogError(_("Reference to a glyph name in a CID-keyed font on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
823 	++tok->err_count;
824 return(sc);
825     }
826 
827     if ( sc!=NULL || strcmp(name,"NULL")==0 )
828 return( sc );
829     enc = SFFindSlot(sf,sf->fv->map,-1,name);
830     if ( enc!=-1 ) {
831 	sc = SFMakeChar(sf,sf->fv->map,enc);
832 	if ( sc!=NULL ) {
833 	    sc->widthset = true;
834 	    free(sc->name);
835 	    sc->name = copy(name);
836 	}
837 return( sc );
838     }
839 
840     for ( gid=sf->glyphcnt-1; gid>=0; --gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
841 	if ( strcmp(sc->name,name)==0 )
842 return( sc );
843     }
844 
845 /* Don't encode it (not in current encoding), just add it, so we needn't */
846 /*  mess with maps or selections */
847     SFExpandGlyphCount(sf,sf->glyphcnt+1);
848     sc = SFSplineCharCreate(sf);
849     sc->name = copy(name);
850     sc->unicodeenc = UniFromName(name,ui_none,&custom);
851     sc->parent = sf;
852     sc->vwidth = (sf->ascent+sf->descent);
853     sc->width = 6*sc->vwidth/10;
854     sc->widthset = true;		/* So we don't lose the glyph */
855     sc->orig_pos = sf->glyphcnt-1;
856     sf->glyphs[sc->orig_pos] = sc;
857 return( sc );
858 }
859 
fea_glyphname_validate(struct parseState * tok,char * name)860 static char *fea_glyphname_validate(struct parseState *tok,char *name) {
861     SplineChar *sc = fea_glyphname_get(tok,name);
862 
863     if ( sc==NULL )
864 return( NULL );
865 
866 return( copy( sc->name ));
867 }
868 
fea_ParseGlyphClass(struct parseState * tok)869 static char *fea_ParseGlyphClass(struct parseState *tok) {
870     char *glyphs = NULL;
871 
872     if ( tok->type==tk_class ) {
873 	glyphs = fea_lookup_class_complain(tok,tok->tokbuf);
874     } else if ( tok->type!=tk_char || tok->tokbuf[0]!='[' ) {
875 	LogError(_("Expected '[' in glyph class definition on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
876 	++tok->err_count;
877 return( NULL );
878     } else {
879 	char *contents = NULL;
880 	int cnt=0, max=0;
881 	int last_val = 0, range_type, range_len = 0;
882 	char last_glyph[MAXT+1];
883 	char *pt1, *start1, *pt2, *start2 = NULL;
884 	int v1, v2;
885 
886 	forever {
887 	    fea_ParseTok(tok);
888 	    if ( tok->type==tk_char && tok->tokbuf[0]==']' )
889 	break;
890 	    if ( tok->type==tk_class ) {
891 		contents = fea_lookup_class_complain(tok,tok->tokbuf);
892 		last_val=-1; last_glyph[0] = '\0';
893 	    } else if ( tok->type==tk_cid ) {
894 		last_val = tok->value; last_glyph[0] = '\0';
895 		contents = fea_cid_validate(tok,tok->value);
896 	    } else if ( tok->type==tk_name ) {
897 		strcpy(last_glyph,tok->tokbuf); last_val = -1;
898 		contents = fea_glyphname_validate(tok,tok->tokbuf);
899 	    } else if ( tok->type==tk_char && tok->tokbuf[0]=='-' ) {
900 		fea_ParseTok(tok);
901 		if ( last_val!=-1 && tok->type==tk_cid ) {
902 		    if ( last_val>=tok->value ) {
903 			LogError(_("Invalid CID range in glyph class on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
904 			++tok->err_count;
905 		    }
906 		    /* Last val has already been added to the class */
907 		    /* and we'll add the current value later */
908 		    for ( ++last_val; last_val<tok->value; ++last_val ) {
909 			contents = fea_cid_validate(tok,last_val);
910 			if ( contents!=NULL )
911 			    cnt = fea_AddGlyphs(&glyphs,&max,cnt,contents);
912 		    }
913 		    contents = fea_cid_validate(tok,tok->value);
914 		} else if ( last_glyph[0]!='\0' && tok->type==tk_name ) {
915 		    range_type=0;
916 		    if ( strlen(last_glyph)==strlen(tok->tokbuf) &&
917 			    strcmp(last_glyph,tok->tokbuf)<0 ) {
918 			start1=NULL;
919 			for ( pt1=last_glyph, pt2=tok->tokbuf;
920 				*pt1!='\0'; ++pt1, ++pt2 ) {
921 			    if ( *pt1!=*pt2 ) {
922 				if ( start1!=NULL ) {
923 				    range_type=0;
924 			break;
925 				}
926 			        start1 = pt1; start2 = pt2;
927 			        if ( !isdigit(*pt1) || !isdigit(*pt2))
928 				    range_type = 1;
929 				else {
930 				    for ( range_len=0; range_len<3 && isdigit(*pt1) && isdigit(*pt2);
931 					    ++range_len, ++pt1, ++pt2 );
932 				    range_type = 2;
933 			            --pt1; --pt2;
934 				}
935 			    }
936 			}
937 		    }
938 		    if ( range_type==0 ) {
939 			LogError(_("Invalid glyph name range in glyph class on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
940 			++tok->err_count;
941 		    } else if ( range_type==1 || range_len==1 ) {
942 			/* Single letter changes */
943 			v1 = *start1; v2 = *start2;
944 			for ( ++v1; v1<=v2; ++v1 ) {
945 			    *start1 = v1;
946 			    contents = fea_glyphname_validate(tok,start1);
947 			    if ( v1==v2 )
948 			break;
949 			    if ( contents!=NULL )
950 				cnt = fea_AddGlyphs(&glyphs,&max,cnt,contents);
951 			}
952 		    } else {
953 			v1 = strtol(start1,NULL,10);
954 			v2 = strtol(start2,NULL,10);
955 			for ( ++v1; v1<=v2; ++v1 ) {
956 			    if ( range_len==2 )
957 				sprintf( last_glyph, "%.*s%02d%s", (int) (start2-tok->tokbuf),
958 					tok->tokbuf, v1, start2+2 );
959 			    else
960 				sprintf( last_glyph, "%.*s%03d%s", (int) (start2-tok->tokbuf),
961 					tok->tokbuf, v1, start2+3 );
962 			    contents = fea_glyphname_validate(tok,last_glyph);
963 			    if ( v1==v2 )
964 			break;
965 			    if ( contents!=NULL )
966 				cnt = fea_AddGlyphs(&glyphs,&max,cnt,contents);
967 			}
968 		    }
969 		} else {
970 		    LogError(_("Unexpected token in glyph class range on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
971 		    ++tok->err_count;
972 		    if ( tok->type==tk_char && tok->tokbuf[0]==']' )
973 	break;
974 		}
975 		last_val=-1; last_glyph[0] = '\0';
976 	    } else if ( tok->type == tk_NULL ) {
977 		contents = copy("NULL");
978 	    } else {
979 		LogError(_("Expected glyph name, cid, or class in glyph class definition on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
980 		++tok->err_count;
981 	break;
982 	    }
983 	    if ( contents!=NULL )
984 		cnt = fea_AddGlyphs(&glyphs,&max,cnt,contents);
985 	}
986 	if ( glyphs==NULL )
987 	    glyphs = copy("");	/* Is it legal to have an empty class? I can't think of any use for one */
988     }
989 return( glyphs );
990 }
991 
fea_ParseGlyphClassGuarded(struct parseState * tok)992 static char *fea_ParseGlyphClassGuarded(struct parseState *tok) {
993     char *ret = fea_ParseGlyphClass(tok);
994     if ( ret==NULL )
995 	ret = copy("");
996 return( ret );
997 }
998 
fea_ParseLookupFlags(struct parseState * tok)999 static void fea_ParseLookupFlags(struct parseState *tok) {
1000     int val = 0;
1001     struct feat_item *item;
1002 
1003     fea_ParseTok(tok);
1004     if ( tok->type==tk_int ) {
1005 	val = tok->value;
1006 	fea_end_statement(tok);
1007     } else {
1008 	while ( tok->type==tk_RightToLeft || tok->type==tk_IgnoreBaseGlyphs ||
1009 		tok->type==tk_IgnoreMarks || tok->type==tk_IgnoreLigatures ) {
1010 	    if ( tok->type == tk_RightToLeft )
1011 		val |= pst_r2l;
1012 	    else if ( tok->type == tk_IgnoreBaseGlyphs )
1013 		val |= pst_ignorebaseglyphs;
1014 	    else if ( tok->type == tk_IgnoreMarks )
1015 		val |= pst_ignorecombiningmarks;
1016 	    else if ( tok->type == tk_IgnoreLigatures )
1017 		val |= pst_ignoreligatures;
1018 	    fea_ParseTok(tok);
1019 	    if ( tok->type == tk_char && tok->tokbuf[0]==';' )
1020 	break;
1021 	    else if ( tok->type==tk_char && tok->tokbuf[0]!=',' ) {
1022 		LogError(_("Expected ';' in lookupflags on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1023 		++tok->err_count;
1024 		fea_skip_to_semi(tok);
1025 	break;
1026 	    }
1027 	    fea_ParseTok(tok);
1028 	}
1029 	if ( tok->type != tk_char || tok->tokbuf[0]!=';' ) {
1030 	    LogError(_("Unexpected token in lookupflags on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1031 	    ++tok->err_count;
1032 	    fea_skip_to_semi(tok);
1033 	} else if ( val==0 ) {
1034 	    LogError(_("No flags specified in lookupflags on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1035 	    ++tok->err_count;
1036 	}
1037     }
1038 
1039     item = chunkalloc(sizeof(struct feat_item));
1040     item->type = ft_lookupflags;
1041     item->u2.lookupflags = val;
1042     item->next = tok->sofar;
1043     tok->sofar = item;
1044 }
1045 
fea_ParseGlyphClassDef(struct parseState * tok)1046 static void fea_ParseGlyphClassDef(struct parseState *tok) {
1047     char *classname = copy(tok->tokbuf );
1048     char *contents;
1049 
1050     fea_ParseTok(tok);
1051     if ( tok->type!=tk_char || tok->tokbuf[0]!='=' ) {
1052 	LogError(_("Expected '=' in glyph class definition on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1053 	++tok->err_count;
1054 	fea_skip_to_semi(tok);
1055 return;
1056     }
1057     fea_ParseTok(tok);
1058     contents = fea_ParseGlyphClass(tok);
1059     if ( contents==NULL ) {
1060 	fea_skip_to_semi(tok);
1061 return;
1062     }
1063     fea_AddClassDef(tok,classname,copy(contents));
1064     fea_end_statement(tok);
1065 }
1066 
fea_ParseLangSys(struct parseState * tok,int inside_feat)1067 static void fea_ParseLangSys(struct parseState *tok, int inside_feat) {
1068     uint32 script, lang;
1069     struct scriptlanglist *sl;
1070     int l;
1071 
1072     fea_ParseTok(tok);
1073     if ( tok->type!=tk_name || !tok->could_be_tag ) {
1074 	LogError(_("Expected tag in languagesystem on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1075 	++tok->err_count;
1076 	fea_skip_to_semi(tok);
1077 return;
1078     }
1079     script = tok->tag;
1080 
1081     fea_ParseTok(tok);
1082     if ( tok->type!=tk_name || !tok->could_be_tag ) {
1083 	LogError(_("Expected tag in languagesystem on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1084 	++tok->err_count;
1085 	fea_skip_to_semi(tok);
1086 return;
1087     }
1088     lang = tok->tag;
1089 
1090     for ( sl=tok->def_langsyses; sl!=NULL && sl->script!=script; sl=sl->next );
1091     if ( sl==NULL ) {
1092 	sl = chunkalloc(sizeof(struct scriptlanglist));
1093 	sl->script = script;
1094 	sl->next = tok->def_langsyses;
1095 	tok->def_langsyses = sl;
1096     }
1097     for ( l=0; l<sl->lang_cnt; ++l ) {
1098 	uint32 language = l<MAX_LANG ? sl->langs[l] : sl->morelangs[l-MAX_LANG];
1099 	if ( language==lang )
1100     break;
1101     }
1102     if ( l<sl->lang_cnt )
1103 	/* Er... this combination is already in the list. I guess that's ok */;
1104     else if ( sl->lang_cnt<MAX_LANG )
1105 	sl->langs[sl->lang_cnt++] = lang;
1106     else {
1107 	sl->morelangs = grealloc(sl->morelangs,(sl->lang_cnt+1)*sizeof(uint32));
1108 	sl->morelangs[sl->lang_cnt++ - MAX_LANG] = lang;
1109     }
1110     fea_end_statement(tok);
1111 
1112     if ( inside_feat ) {
1113 	struct feat_item *item = chunkalloc(sizeof(struct feat_item));
1114 	item->type = ft_langsys;
1115 	item->u2.sl = SListCopy(tok->def_langsyses);
1116 	item->next = tok->sofar;
1117 	tok->sofar = item;
1118     }
1119 }
1120 
1121 struct markedglyphs {
1122     unsigned int has_marks: 1;		/* Are there any marked glyphs in the entire sequence? */
1123     unsigned int is_cursive: 1;		/* Only in a position sequence */
1124     unsigned int is_mark: 1;		/* Only in a position sequence/mark keyword=>mark2mark */
1125     unsigned int is_name: 1;		/* Otherwise a class */
1126     unsigned int is_lookup: 1;		/* Or a lookup when parsing a subs replacement list */
1127     uint16 mark_count;			/* 0=>unmarked, 1=>first mark, etc. */
1128     char *name_or_class;		/* Glyph name / class contents */
1129     struct vr *vr;			/* A value record. Only in position sequences */
1130     int ap_cnt;				/* Number of anchor points */
1131     AnchorPoint **anchors;
1132     char *lookupname;
1133     struct markedglyphs *next;
1134 };
1135 
1136 #ifdef FONTFORGE_CONFIG_DEVICETABLES
fea_ParseDeviceTable(struct parseState * tok,DeviceTable * adjust)1137 static void fea_ParseDeviceTable(struct parseState *tok,DeviceTable *adjust)
1138 #else
1139 static void fea_ParseDeviceTable(struct parseState *tok)
1140 #endif
1141 	{
1142     int first = true;
1143 #ifdef FONTFORGE_CONFIG_DEVICETABLES
1144     int min=0, max= -1;
1145     int8 values[512];
1146 
1147     memset(values,0,sizeof(values));
1148 #endif
1149 
1150     fea_TokenMustBe(tok,tk_device,'\0');
1151     if ( tok->type!=tk_device )
1152 return;
1153 
1154     forever {
1155 	fea_ParseTok(tok);
1156 	if ( first && tok->type==tk_NULL ) {
1157 	    fea_TokenMustBe(tok,tk_char,'>');
1158     break;
1159 	} else if ( tok->type==tk_char && tok->tokbuf[0]=='>' ) {
1160     break;
1161 	} else if ( tok->type!=tk_int ) {
1162 	    LogError(_("Expected integer in device table on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1163 	    ++tok->err_count;
1164 	} else {
1165 #ifdef FONTFORGE_CONFIG_DEVICETABLES
1166 	    int pixel = tok->value;
1167 #endif
1168 	    fea_TokenMustBe(tok,tk_int,'\0');
1169 #ifdef FONTFORGE_CONFIG_DEVICETABLES
1170 	    if ( pixel>=sizeof(values) || pixel<0 )
1171 		LogError(_("Pixel size too big in device table on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1172 	    else {
1173 		values[pixel] = tok->value;
1174 		if ( max==-1 )
1175 		    min=max=pixel;
1176 		else if ( pixel<min ) min = pixel;
1177 		else if ( pixel>max ) max = pixel;
1178 	    }
1179 #endif
1180 	}
1181 	first = false;
1182     }
1183 #ifdef FONTFORGE_CONFIG_DEVICETABLES
1184     if ( max!=-1 ) {
1185 	int i;
1186 	adjust->first_pixel_size = min;
1187 	adjust->last_pixel_size = max;
1188 	adjust->corrections = galloc(max-min+1);
1189 	for ( i=min; i<=max; ++i )
1190 	    adjust->corrections[i-min] = values[i];
1191     }
1192 #endif
1193 }
1194 
fea_ParseCaret(struct parseState * tok)1195 static void fea_ParseCaret(struct parseState *tok) {
1196     int val=0;
1197 
1198     fea_TokenMustBe(tok,tk_caret,'\0');
1199     if ( tok->type!=tk_caret )
1200 return;
1201     fea_ParseTok(tok);
1202     if ( tok->type!=tk_int ) {
1203 	LogError(_("Expected integer in caret on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1204 	++tok->err_count;
1205     } else
1206 	val = tok->value;
1207     fea_ParseTok(tok);
1208     if ( tok->type!=tk_char || tok->tokbuf[0]!='>' ) {
1209 	LogError(_("Expected '>' in caret on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1210 	++tok->err_count;
1211     }
1212     tok->value = val;
1213 }
1214 
fea_ParseAnchor(struct parseState * tok)1215 static AnchorPoint *fea_ParseAnchor(struct parseState *tok) {
1216     AnchorPoint *ap = NULL;
1217 
1218     if ( tok->type==tk_anchor ) {
1219 	fea_ParseTok(tok);
1220 	if ( tok->type==tk_NULL )
1221 	    ap = NULL;
1222 	else if ( tok->type==tk_int ) {
1223 	    ap = chunkalloc(sizeof(AnchorPoint));
1224 	    ap->me.x = tok->value;
1225 	    fea_TokenMustBe(tok,tk_int,'\0');
1226 	    ap->me.y = tok->value;
1227 	    fea_ParseTok(tok);
1228 	    if ( tok->type==tk_int ) {
1229 		ap->ttf_pt_index = tok->value;
1230 		ap->has_ttf_pt = true;
1231 		fea_TokenMustBe(tok,tk_char,'>');
1232 	    } else if ( tok->type==tk_char && tok->tokbuf[0]=='<' ) {
1233 #ifdef FONTFORGE_CONFIG_DEVICETABLES
1234 		fea_ParseDeviceTable(tok,&ap->xadjust);
1235 		fea_TokenMustBe(tok,tk_char,'<');
1236 		fea_ParseDeviceTable(tok,&ap->yadjust);
1237 #else
1238 		fea_ParseDeviceTable(tok);
1239 		fea_TokenMustBe(tok,tk_char,'<');
1240 		fea_ParseDeviceTable(tok);
1241 #endif
1242 		fea_TokenMustBe(tok,tk_char,'>');
1243 	    } else if ( tok->type!=tk_char || tok->tokbuf[0]!='>' ) {
1244 		LogError(_("Expected '>' in anchor on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1245 		++tok->err_count;
1246 	    }
1247 	} else {
1248 	    LogError(_("Expected integer in anchor on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1249 	    ++tok->err_count;
1250 	}
1251     } else {
1252 	LogError(_("Expected 'anchor' keyword in anchor on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1253 	++tok->err_count;
1254     }
1255 return( ap );
1256 }
1257 
fea_findLookup(struct parseState * tok,char * name)1258 static int fea_findLookup(struct parseState *tok,char *name ) {
1259     struct feat_item *feat;
1260 
1261     for ( feat=tok->sofar; feat!=NULL; feat=feat->next ) {
1262 	if ( feat->type==ft_lookup_start && strcmp(name,feat->u1.lookup_name)==0 )
1263 return( true );
1264     }
1265 
1266     if ( SFFindLookup(tok->sf,name)!=NULL ) {
1267 	if ( !tok->lookup_in_sf_warned ) {
1268 	    ff_post_notice(_("Refers to Font"),_("Reference to a lookup which is not in the feature file but which is in the font, %.50s"), name );
1269 	    tok->lookup_in_sf_warned = true;
1270 	}
1271 return( true );
1272     }
1273 
1274 return( false );
1275 }
1276 
fea_ParseBroket(struct parseState * tok,struct markedglyphs * last)1277 static void fea_ParseBroket(struct parseState *tok,struct markedglyphs *last) {
1278     /* We've read the open broket. Might find: value record, anchor, lookup */
1279     /* (lookups are my extension) */
1280     struct vr *vr;
1281 
1282     fea_ParseTok(tok);
1283     if ( tok->type==tk_lookup ) {
1284 	fea_TokenMustBe(tok,tk_name,'\0');
1285 	if ( last->mark_count==0 ) {
1286 	    LogError(_("Lookups may only be specified after marked glyphs on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1287 	    ++tok->err_count;
1288 	}
1289 	if ( !fea_findLookup(tok,tok->tokbuf) ) {
1290 	    LogError(_("Lookups must be defined before being used on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1291 	    ++tok->err_count;
1292 	} else
1293 	    last->lookupname = copy(tok->tokbuf);
1294 	fea_TokenMustBe(tok,tk_char,'>');
1295     } else if ( tok->type==tk_anchor ) {
1296 	last->anchors = grealloc(last->anchors,(++last->ap_cnt)*sizeof(AnchorPoint *));
1297 	last->anchors[last->ap_cnt-1] = fea_ParseAnchor(tok);
1298     } else if ( tok->type==tk_NULL ) {
1299 	/* NULL value record. Adobe documents it and doesn't implement it */
1300 	/* Not sure what it's good for */
1301 	fea_TokenMustBe(tok,tk_char,'>');
1302     } else if ( tok->type==tk_int ) {
1303 	last->vr = vr = chunkalloc(sizeof( struct vr ));
1304 	vr->xoff = tok->value;
1305 	fea_ParseTok(tok);
1306 	if ( tok->type==tk_char && tok->tokbuf[0]=='>' ) {
1307 	    if ( tok->in_vkrn )
1308 		vr->v_adv_off = vr->xoff;
1309 	    else
1310 		vr->h_adv_off = vr->xoff;
1311 	    vr->xoff = 0;
1312 	} else if ( tok->type!=tk_int ) {
1313 	    LogError(_("Expected integer in value record on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1314 	    ++tok->err_count;
1315 	} else {
1316 	    vr->yoff = tok->value;
1317 	    fea_TokenMustBe(tok,tk_int,'\0');
1318 	    vr->h_adv_off = tok->value;
1319 	    fea_TokenMustBe(tok,tk_int,'\0');
1320 	    vr->v_adv_off = tok->value;
1321 	    fea_ParseTok(tok);
1322 	    if ( tok->type==tk_char && tok->tokbuf[0]=='<' ) {
1323 #ifdef FONTFORGE_CONFIG_DEVICETABLES
1324 		vr->adjust = chunkalloc(sizeof(struct valdev));
1325 		fea_ParseDeviceTable(tok,&vr->adjust->xadjust);
1326 		fea_TokenMustBe(tok,tk_char,'<');
1327 		fea_ParseDeviceTable(tok,&vr->adjust->yadjust);
1328 		fea_TokenMustBe(tok,tk_char,'<');
1329 		fea_ParseDeviceTable(tok,&vr->adjust->xadv);
1330 		fea_TokenMustBe(tok,tk_char,'<');
1331 		fea_ParseDeviceTable(tok,&vr->adjust->yadv);
1332 #else
1333 		fea_ParseDeviceTable(tok);
1334 		fea_TokenMustBe(tok,tk_char,'<');
1335 		fea_ParseDeviceTable(tok);
1336 		fea_TokenMustBe(tok,tk_char,'<');
1337 		fea_ParseDeviceTable(tok);
1338 		fea_TokenMustBe(tok,tk_char,'<');
1339 		fea_ParseDeviceTable(tok);
1340 #endif
1341 		fea_TokenMustBe(tok,tk_char,'>');
1342 	    } else if ( tok->type!=tk_char || tok->tokbuf[0]!='>' ) {
1343 		LogError(_("Expected '>' in value record on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1344 		++tok->err_count;
1345 	    }
1346 	}
1347     }
1348 }
1349 
fea_ParseMarkedGlyphs(struct parseState * tok,int is_pos,int allow_marks,int allow_lookups)1350 static struct markedglyphs *fea_ParseMarkedGlyphs(struct parseState *tok,
1351 	int is_pos, int allow_marks, int allow_lookups) {
1352     int mark_cnt = 0, last_mark=0, is_cursive = false, is_mark=false;
1353     struct markedglyphs *head=NULL, *last=NULL, *prev=NULL, *cur;
1354     int first = true;
1355     char *contents;
1356 
1357     forever {
1358 	fea_ParseTok(tok);
1359 	cur = NULL;
1360 	if ( first && is_pos && tok->type == tk_cursive )
1361 	    is_cursive = true;
1362 	else if ( first && is_pos && tok->type == tk_mark )
1363 	    is_mark = true;
1364 	else if ( tok->type==tk_name || tok->type == tk_cid ) {
1365 	    if ( tok->type == tk_name )
1366 		contents = fea_glyphname_validate(tok,tok->tokbuf);
1367 	    else
1368 		contents = fea_cid_validate(tok,tok->value);
1369 	    if ( contents!=NULL ) {
1370 		cur = chunkalloc(sizeof(struct markedglyphs));
1371 		cur->is_cursive = is_cursive;
1372 		cur->is_mark = is_mark;
1373 		cur->is_name = true;
1374 		cur->name_or_class = contents;
1375 	    }
1376 	} else if ( tok->type == tk_class || (tok->type==tk_char && tok->tokbuf[0]=='[')) {
1377 	    cur = chunkalloc(sizeof(struct markedglyphs));
1378 	    cur->is_cursive = is_cursive;
1379 	    cur->is_mark = is_mark;
1380 	    cur->is_name = false;
1381 	    cur->name_or_class = fea_ParseGlyphClassGuarded(tok);
1382 	} else if ( allow_marks && tok->type==tk_char &&
1383 		(tok->tokbuf[0]=='\'' || tok->tokbuf[0]=='"') && last!=NULL ) {
1384 	    if ( last_mark!=tok->tokbuf[0] || (prev!=NULL && prev->mark_count==0)) {
1385 		++mark_cnt;
1386 		last_mark = tok->tokbuf[0];
1387 	    }
1388 	    last->mark_count = mark_cnt;
1389 	} else if ( is_pos && last!=NULL && last->vr==NULL && tok->type == tk_int ) {
1390 	    last->vr = chunkalloc(sizeof(struct vr));
1391 	    if ( tok->in_vkrn )
1392 		last->vr->v_adv_off = tok->value;
1393 	    else
1394 		last->vr->h_adv_off = tok->value;
1395 	} else if ( is_pos && last!=NULL && tok->type == tk_char && tok->tokbuf[0]=='<' ) {
1396 	    fea_ParseBroket(tok,last);
1397 	} else if ( !is_pos && allow_lookups && tok->type == tk_char && tok->tokbuf[0]=='<' ) {
1398 	    fea_TokenMustBe(tok,tk_lookup,'\0');
1399 	    fea_TokenMustBe(tok,tk_name,'\0');
1400 	    cur = chunkalloc(sizeof(struct markedglyphs));
1401 	    cur->is_name = false;
1402 	    cur->is_lookup = true;
1403 	    cur->lookupname = copy(tok->tokbuf);
1404 	    fea_TokenMustBe(tok,tk_char,'>');
1405 	} else
1406     break;
1407 	if ( cur!=NULL ) {
1408 	    prev = last;
1409 	    if ( last==NULL )
1410 		head = cur;
1411 	    else
1412 		last->next = cur;
1413 	    last = cur;
1414 	}
1415 	first = false;
1416     }
1417     if ( head!=NULL && mark_cnt!=0 )
1418 	head->has_marks = true;
1419     fea_UnParseTok(tok);
1420 return( head );
1421 }
1422 
fea_markedglyphsFree(struct markedglyphs * gl)1423 static void fea_markedglyphsFree(struct markedglyphs *gl) {
1424     struct markedglyphs *next;
1425     int i;
1426 
1427     while ( gl!=NULL ) {
1428 	next = gl->next;
1429 	free(gl->name_or_class);
1430 	free(gl->lookupname);
1431 	for ( i=0; i<gl->ap_cnt; ++i )
1432 	    AnchorPointsFree(gl->anchors[i]);
1433 	free(gl->anchors);
1434 	if ( gl->vr!=NULL ) {
1435 #ifdef FONTFORGE_CONFIG_DEVICETABLES
1436 	    ValDevFree(gl->vr->adjust);
1437 #endif
1438 	    chunkfree(gl->vr,sizeof(struct vr));
1439 	}
1440 	gl = next;
1441     }
1442 }
1443 
fea_AddAllLigPosibilities(struct parseState * tok,struct markedglyphs * glyphs,SplineChar * sc,char * sequence_start,char * next,struct feat_item * sofar)1444 static struct feat_item *fea_AddAllLigPosibilities(struct parseState *tok,struct markedglyphs *glyphs,
1445 	SplineChar *sc,char *sequence_start,char *next, struct feat_item *sofar) {
1446     char *start, *pt, ch;
1447     SplineChar *temp;
1448     char *after;
1449     struct feat_item *item;
1450 
1451     start = glyphs->name_or_class;
1452     forever {
1453 	while ( *start==' ' ) ++start;
1454 	if ( *start=='\0' )
1455     break;
1456 	for ( pt=start; *pt!='\0' && *pt!=' '; ++pt );
1457 	ch = *pt; *pt = '\0';
1458 	temp = fea_glyphname_get(tok,start);
1459 	*pt = ch; start = pt;
1460 	if ( temp==NULL )
1461     continue;
1462 	strcpy(next,temp->name);
1463 	after = next+strlen(next);
1464 	if ( glyphs->next!=NULL ) {
1465 	    *after++ = ' ';
1466 	    sofar = fea_AddAllLigPosibilities(tok,glyphs->next,sc,sequence_start,after,sofar);
1467 	} else {
1468 	    *after = '\0';
1469 	    item = chunkalloc(sizeof(struct feat_item));
1470 	    item->type = ft_pst;
1471 	    item->next = sofar;
1472 	    sofar = item;
1473 	    item->u1.sc = sc;
1474 	    item->u2.pst = chunkalloc(sizeof(PST));
1475 	    item->u2.pst->type = pst_ligature;
1476 	    item->u2.pst->u.lig.components = copy(sequence_start);
1477 	    item->u2.pst->u.lig.lig = sc;
1478 	}
1479     }
1480 return( sofar );
1481 }
1482 
fea_glyphs_to_names(struct markedglyphs * glyphs,int cnt,char ** to)1483 static struct markedglyphs *fea_glyphs_to_names(struct markedglyphs *glyphs,
1484 	int cnt,char **to) {
1485     struct markedglyphs *g;
1486     int len, i;
1487     char *names, *pt;
1488 
1489     len = 0;
1490     for ( g=glyphs, i=0; i<cnt; ++i, g=g->next )
1491 	len += strlen( g->name_or_class ) +1;
1492     names = pt = galloc(len+1);
1493     for ( g=glyphs, i=0; i<cnt; ++i, g=g->next ) {
1494 	strcpy(pt,g->name_or_class);
1495 	pt += strlen( pt );
1496 	*pt++ = ' ';
1497     }
1498     if ( pt!=names )
1499 	pt[-1] = '\0';
1500     else
1501 	*pt = '\0';
1502     *to = names;
1503 return( g );
1504 }
1505 
fea_process_pos_single(struct parseState * tok,struct markedglyphs * glyphs,struct feat_item * sofar)1506 static struct feat_item *fea_process_pos_single(struct parseState *tok,
1507 	struct markedglyphs *glyphs, struct feat_item *sofar) {
1508     char *start, *pt, ch;
1509     struct feat_item *item;
1510     SplineChar *sc;
1511 
1512     start = glyphs->name_or_class;
1513     forever {
1514 	while ( *start==' ' ) ++start;
1515 	if ( *start=='\0' )
1516     break;
1517 	for ( pt=start; *pt!='\0' && *pt!=' '; ++pt );
1518 	ch = *pt; *pt = '\0';
1519 	sc = fea_glyphname_get(tok,start);
1520 	*pt = ch; start = pt;
1521 	if ( sc!=NULL ) {
1522 	    item = chunkalloc(sizeof(struct feat_item));
1523 	    item->type = ft_pst;
1524 	    item->next = sofar;
1525 	    sofar = item;
1526 	    item->u1.sc = sc;
1527 	    item->u2.pst = chunkalloc(sizeof(PST));
1528 	    item->u2.pst->type = pst_position;
1529 	    item->u2.pst->u.pos = glyphs->vr[0];
1530 	}
1531     }
1532 return( sofar );
1533 }
1534 
fea_process_pos_pair(struct parseState * tok,struct markedglyphs * glyphs,struct feat_item * sofar,int enumer)1535 static struct feat_item *fea_process_pos_pair(struct parseState *tok,
1536 	struct markedglyphs *glyphs, struct feat_item *sofar, int enumer) {
1537     char *start, *pt, ch, *start2, *pt2, ch2;
1538     struct feat_item *item;
1539     struct vr vr[2];
1540     SplineChar *sc, *sc2;
1541 
1542     memset(vr,0,sizeof(vr));
1543     if ( glyphs->vr==NULL )
1544 	vr[0] = *glyphs->next->vr;
1545     else {
1546 	vr[0] = *glyphs->vr;
1547 	if ( glyphs->next->vr!=NULL )
1548 	    vr[1] = *glyphs->next->vr;
1549     }
1550     if ( enumer || (glyphs->is_name && glyphs->next->is_name)) {
1551 	start = glyphs->name_or_class;
1552 	forever {
1553 	    while ( *start==' ' ) ++start;
1554 	    if ( *start=='\0' )
1555 	break;
1556 	    for ( pt=start; *pt!='\0' && *pt!=' '; ++pt );
1557 	    ch = *pt; *pt = '\0';
1558 	    sc = fea_glyphname_get(tok,start);
1559 	    *pt = ch; start = pt;
1560 	    if ( sc!=NULL ) {
1561 		start2 = glyphs->next->name_or_class;
1562 		forever {
1563 		    while ( *start2==' ' ) ++start2;
1564 		    if ( *start2=='\0' )
1565 		break;
1566 		    for ( pt2=start2; *pt2!='\0' && *pt2!=' '; ++pt2 );
1567 		    ch2 = *pt2; *pt2 = '\0';
1568 		    sc2 = fea_glyphname_get(tok,start2);
1569 		    *pt2 = ch2; start2 = pt2;
1570 		    if ( sc2!=NULL ) {
1571 			item = chunkalloc(sizeof(struct feat_item));
1572 			item->type = ft_pst;
1573 			item->next = sofar;
1574 			sofar = item;
1575 			item->u1.sc = sc;
1576 			item->u2.pst = chunkalloc(sizeof(PST));
1577 			item->u2.pst->type = pst_pair;
1578 			item->u2.pst->u.pair.paired = copy(sc2->name);
1579 			item->u2.pst->u.pair.vr = chunkalloc(sizeof( struct vr[2]));
1580 			memcpy(item->u2.pst->u.pair.vr,vr,sizeof(vr));
1581 		    }
1582 		}
1583 	    }
1584 	}
1585     } else {
1586 	item = chunkalloc(sizeof(struct feat_item));
1587 	item->type = ft_pstclass;
1588 	item->next = sofar;
1589 	sofar = item;
1590 	item->u1.class = copy(glyphs->name_or_class);
1591 	item->u2.pst = chunkalloc(sizeof(PST));
1592 	item->u2.pst->type = pst_pair;
1593 	item->u2.pst->u.pair.paired = copy(glyphs->next->name_or_class);
1594 	item->u2.pst->u.pair.vr = chunkalloc(sizeof( struct vr[2]));
1595 	memcpy(item->u2.pst->u.pair.vr,vr,sizeof(vr));
1596     }
1597 return( sofar );
1598 }
1599 
fea_process_sub_single(struct parseState * tok,struct markedglyphs * glyphs,struct markedglyphs * rpl,struct feat_item * sofar)1600 static struct feat_item *fea_process_sub_single(struct parseState *tok,
1601 	struct markedglyphs *glyphs, struct markedglyphs *rpl,
1602 	struct feat_item *sofar ) {
1603     char *start, *pt, ch, *start2, *pt2, ch2;
1604     struct feat_item *item;
1605     SplineChar *sc, *temp;
1606 
1607     if ( rpl->is_name ) {
1608 	temp = fea_glyphname_get(tok,rpl->name_or_class);
1609 	if ( temp!=NULL ) {
1610 	    start = glyphs->name_or_class;
1611 	    if ( start==NULL ) {
1612 		LogError(_("Internal state messed up on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1613 		++tok->err_count;
1614 return( sofar );
1615 	    }
1616 	    forever {
1617 		while ( *start==' ' ) ++start;
1618 		if ( *start=='\0' )
1619 	    break;
1620 		for ( pt=start; *pt!='\0' && *pt!=' '; ++pt );
1621 		ch = *pt; *pt = '\0';
1622 		sc = fea_glyphname_get(tok,start);
1623 		*pt = ch; start = pt;
1624 		if ( sc!=NULL ) {
1625 		    item = chunkalloc(sizeof(struct feat_item));
1626 		    item->type = ft_pst;
1627 		    item->next = sofar;
1628 		    sofar = item;
1629 		    item->u1.sc = sc;
1630 		    item->u2.pst = chunkalloc(sizeof(PST));
1631 		    item->u2.pst->type = pst_substitution;
1632 		    item->u2.pst->u.subs.variant = copy(temp->name);
1633 		}
1634 	    }
1635 	}
1636     } else if ( !glyphs->is_name ) {
1637 	start = glyphs->name_or_class;
1638 	start2 = rpl->name_or_class;
1639 	forever {
1640 	    while ( *start==' ' ) ++start;
1641 	    while ( *start2==' ' ) ++start2;
1642 	    if ( *start=='\0' && *start2=='\0' )
1643 	break;
1644 	    else if ( *start=='\0' || *start2=='\0' ) {
1645 		LogError(_("When a single substitution is specified by glyph classes, those classes must be of the same length on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1646 		++tok->err_count;
1647 	break;
1648 	    }
1649 	    for ( pt=start; *pt!='\0' && *pt!=' '; ++pt );
1650 	    ch = *pt; *pt = '\0';
1651 	    for ( pt2=start2; *pt2!='\0' && *pt2!=' '; ++pt2 );
1652 	    ch2 = *pt2; *pt2 = '\0';
1653 	    sc = fea_glyphname_get(tok,start);
1654 	    temp = fea_glyphname_get(tok,start2);
1655 	    *pt = ch; start = pt;
1656 	    *pt2 = ch2; start2 = pt2;
1657 	    if ( sc==NULL || temp==NULL )
1658 	continue;
1659 	    item = chunkalloc(sizeof(struct feat_item));
1660 	    item->type = ft_pst;
1661 	    item->next = sofar;
1662 	    sofar = item;
1663 	    item->u1.sc = sc;
1664 	    item->u2.pst = chunkalloc(sizeof(PST));
1665 	    item->u2.pst->type = pst_substitution;
1666 	    item->u2.pst->u.subs.variant = copy(temp->name);
1667 	}
1668     } else {
1669 	LogError(_("When a single substitution's replacement is specified by a glyph class, the thing being replaced must also be a class on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1670 	++tok->err_count;
1671     }
1672 return( sofar );
1673 }
1674 
fea_process_sub_ligature(struct parseState * tok,struct markedglyphs * glyphs,struct markedglyphs * rpl,struct feat_item * sofar)1675 static struct feat_item *fea_process_sub_ligature(struct parseState *tok,
1676 	struct markedglyphs *glyphs, struct markedglyphs *rpl,
1677 	struct feat_item *sofar ) {
1678     SplineChar *sc;
1679     struct markedglyphs *g;
1680 
1681     /* I store ligatures backwards, in the ligature glyph not the glyphs being substituted */
1682     sc = fea_glyphname_get(tok,rpl->name_or_class);
1683     if ( sc!=NULL ) {
1684 	int len=0;
1685 	char *space;
1686 	for ( g=glyphs; g!=NULL && g->mark_count==glyphs->mark_count; g=g->next )
1687 	    len += strlen(g->name_or_class)+1;
1688 	space = galloc(len+1);
1689 	sofar = fea_AddAllLigPosibilities(tok,glyphs,sc,space,space,sofar);
1690 	free(space);
1691     }
1692 return( sofar );
1693 }
1694 
fea_markedglyphs_to_fpst(struct parseState * tok,struct markedglyphs * glyphs,int is_pos,int is_ignore)1695 static FPST *fea_markedglyphs_to_fpst(struct parseState *tok,struct markedglyphs *glyphs,
1696 	int is_pos,int is_ignore) {
1697     struct markedglyphs *g;
1698     int bcnt=0, ncnt=0, fcnt=0, cnt;
1699     int all_single=true;
1700     int mmax = 0;
1701     int i;
1702     FPST *fpst;
1703     struct fpst_rule *r;
1704     struct feat_item *item, *head = NULL;
1705 
1706     for ( g=glyphs; g!=NULL && g->mark_count==0; g=g->next ) {
1707 	++bcnt;
1708 	if ( !g->is_name ) all_single = false;
1709     }
1710     for ( ; g!=NULL ; g=g->next ) {
1711 	if ( !g->is_name ) all_single = false;
1712 	if ( g->mark_count==0 )
1713 	    ++fcnt;
1714 	else {
1715 	    /* if we found some unmarked glyphs between two runs of marked */
1716 	    /*  they don't count as lookaheads */
1717 	    ncnt += fcnt + 1;
1718 	    fcnt = 0;
1719 	    if ( g->mark_count>mmax ) mmax = g->mark_count;
1720 	}
1721     }
1722 
1723     fpst = chunkalloc(sizeof(FPST));
1724     fpst->type = is_pos ? pst_chainpos : pst_chainsub;
1725     fpst->format = all_single ? pst_glyphs : pst_coverage;
1726     fpst->rule_cnt = 1;
1727     fpst->rules = r = gcalloc(1,sizeof(struct fpst_rule));
1728     if ( is_ignore )
1729 	mmax = 0;
1730     r->lookup_cnt = mmax;
1731     r->lookups = gcalloc(mmax,sizeof(struct seqlookup));
1732     for ( i=0; i<mmax; ++i )
1733 	r->lookups[i].seq = i;
1734 
1735     if ( all_single ) {
1736 	g = fea_glyphs_to_names(glyphs,bcnt,&r->u.glyph.back);
1737 	g = fea_glyphs_to_names(g,ncnt,&r->u.glyph.names);
1738 	g = fea_glyphs_to_names(g,fcnt,&r->u.glyph.fore);
1739     } else {
1740 	r->u.coverage.ncnt = ncnt;
1741 	r->u.coverage.bcnt = bcnt;
1742 	r->u.coverage.fcnt = fcnt;
1743 	r->u.coverage.ncovers = galloc(ncnt*sizeof(char*));
1744 	r->u.coverage.bcovers = galloc(bcnt*sizeof(char*));
1745 	r->u.coverage.fcovers = galloc(fcnt*sizeof(char*));
1746 	for ( i=0, g=glyphs; i<bcnt; ++i, g=g->next )
1747 	    r->u.coverage.bcovers[i] = copy(g->name_or_class);
1748 	for ( i=0; i<ncnt; ++i, g=g->next )
1749 	    r->u.coverage.ncovers[i] = copy(g->name_or_class);
1750 	for ( i=0; i<fcnt; ++i, g=g->next )
1751 	    r->u.coverage.fcovers[i] = copy(g->name_or_class);
1752     }
1753 
1754     item = chunkalloc(sizeof(struct feat_item));
1755     item->type = ft_fpst;
1756     item->next = tok->sofar;
1757     tok->sofar = item;
1758     item->u2.fpst = fpst;
1759 
1760     if ( is_pos ) {
1761 	for ( g=glyphs; g!=NULL && g->mark_count==0; g=g->next );
1762 	for ( i=0; g!=NULL; ++i ) {
1763 	    head = NULL;
1764 	    if ( g->lookupname!=NULL ) {
1765 		head = chunkalloc(sizeof(struct feat_item));
1766 		head->type = ft_lookup_ref;
1767 		head->u1.lookup_name = copy(g->lookupname);
1768 	    } else if ( (g->next==NULL || g->next->mark_count!=g->mark_count) &&
1769 		    g->vr!=NULL ) {
1770 		head = fea_process_pos_single(tok,g,NULL);
1771 	    } else if ( g->next!=NULL && g->mark_count==g->next->mark_count &&
1772 		    (g->vr!=NULL || g->next->vr!=NULL ) &&
1773 		    ( g->next->next==NULL || g->next->next->mark_count!=g->mark_count)) {
1774 		head = fea_process_pos_pair(tok,g,NULL,false);
1775 	    } else {
1776 		LogError(_("Unparseable contextual sequence on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1777 		++tok->err_count;
1778 	    }
1779 	    r->lookups[i].lookup = (OTLookup *) head;
1780 	    cnt = g->mark_count;
1781 	    while ( g!=NULL && g->mark_count == cnt )	/* skip everything involved here */
1782 		g=g->next;
1783 	    for ( ; g!=NULL && g->mark_count==0; g=g->next ); /* skip any uninvolved glyphs */
1784 	}
1785     }
1786 
1787 return( fpst );
1788 }
1789 
fea_ParseIgnore(struct parseState * tok)1790 static void fea_ParseIgnore(struct parseState *tok) {
1791     struct markedglyphs *glyphs;
1792     int is_pos;
1793     FPST *fpst;
1794     /* ignore [pos|sub] <marked glyph sequence> (, <marked g sequence>)* */
1795 
1796     fea_ParseTok(tok);
1797     if ( tok->type==tk_position )
1798 	is_pos = true;
1799     else if ( tok->type == tk_substitute )
1800 	is_pos = false;
1801     else {
1802 	LogError(_("The ignore keyword must be followed by either position or substitute on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1803 	++tok->err_count;
1804 	is_pos = true;
1805     }
1806     forever {
1807 	glyphs = fea_ParseMarkedGlyphs(tok,false/* don't parse value records, etc*/,
1808 		true/*allow marks*/,false/* no lookups */);
1809 	fpst = fea_markedglyphs_to_fpst(tok,glyphs,false,true);
1810 	if ( is_pos )
1811 	    fpst->type = pst_chainpos;
1812 	fea_markedglyphsFree(glyphs);
1813 	fea_ParseTok(tok);
1814 	if ( tok->type!=tk_char || tok->tokbuf[0]!=',' )
1815     break;
1816     }
1817 
1818     fea_now_semi(tok);
1819 }
1820 
fea_ParseSubstitute(struct parseState * tok)1821 static void fea_ParseSubstitute(struct parseState *tok) {
1822     /* name by name => single subs */
1823     /* class by name => single subs */
1824     /* class by class => single subs */
1825     /* name by <glyph sequence> => multiple subs */
1826     /* name from <class> => alternate subs */
1827     /* <glyph sequence> by name => ligature */
1828     /* <marked glyph sequence> by <name> => context chaining */
1829     /* <marked glyph sequence> by <lookup name>* => context chaining */
1830     /* [ignore sub] <marked glyph sequence> (, <marked g sequence>)* */
1831     struct markedglyphs *glyphs = fea_ParseMarkedGlyphs(tok,false,true,false),
1832 	    *g, *rpl, *rp;
1833     int cnt, i;
1834     SplineChar *sc;
1835     struct feat_item *item, *head;
1836 
1837     fea_ParseTok(tok);
1838     for ( cnt=0, g=glyphs; g!=NULL; g=g->next, ++cnt );
1839     if ( glyphs==NULL ) {
1840 	LogError(_("Empty subsitute on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1841 	++tok->err_count;
1842     } else if ( !glyphs->has_marks ) {
1843 	/* Non-contextual */
1844 	if ( cnt==1 && glyphs->is_name && tok->type==tk_from ) {
1845 	    /* Alternate subs */
1846 	    char *alts;
1847 	    fea_ParseTok(tok);
1848 	    alts = fea_ParseGlyphClassGuarded(tok);
1849 	    sc = fea_glyphname_get(tok,glyphs->name_or_class);
1850 	    if ( sc!=NULL ) {
1851 		item = chunkalloc(sizeof(struct feat_item));
1852 		item->type = ft_pst;
1853 		item->next = tok->sofar;
1854 		tok->sofar = item;
1855 		item->u1.sc = sc;
1856 		item->u2.pst = chunkalloc(sizeof(PST));
1857 		item->u2.pst->type = pst_alternate;
1858 		item->u2.pst->u.alt.components = alts;
1859 	    }
1860 	} else if ( cnt>=1 && tok->type==tk_by ) {
1861 	    rpl = fea_ParseMarkedGlyphs(tok,false,false,false);
1862 	    if ( rpl==NULL ) {
1863 		LogError(_("No substitution specified on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1864 		++tok->err_count;
1865 	    } else if ( rpl->has_marks ) {
1866 		LogError(_("No marked glyphs allowed in replacement on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1867 		++tok->err_count;
1868 	    } else {
1869 		if ( cnt==1 && rpl->next==NULL ) {
1870 		    tok->sofar = fea_process_sub_single(tok,glyphs,rpl,tok->sofar);
1871 		} else if ( cnt==1 && glyphs->is_name && rpl->next!=NULL && rpl->is_name ) {
1872 		    /* Multiple substitution */
1873 		    int len=0;
1874 		    char *mult;
1875 		    for ( g=rpl; g!=NULL; g=g->next )
1876 			len += strlen(g->name_or_class)+1;
1877 		    mult = galloc(len+1);
1878 		    len = 0;
1879 		    for ( g=rpl; g!=NULL; g=g->next ) {
1880 			strcpy(mult+len,g->name_or_class);
1881 			len += strlen(g->name_or_class);
1882 			mult[len++] = ' ';
1883 		    }
1884 		    mult[len-1] = '\0';
1885 		    sc = fea_glyphname_get(tok,glyphs->name_or_class);
1886 		    if ( sc!=NULL ) {
1887 			item = chunkalloc(sizeof(struct feat_item));
1888 			item->type = ft_pst;
1889 			item->next = tok->sofar;
1890 			tok->sofar = item;
1891 			item->u1.sc = sc;
1892 			item->u2.pst = chunkalloc(sizeof(PST));
1893 			item->u2.pst->type = pst_multiple;
1894 			item->u2.pst->u.mult.components = mult;
1895 		    }
1896 		} else if ( cnt>1 && rpl->is_name && rpl->next==NULL ) {
1897 		    tok->sofar = fea_process_sub_ligature(tok,glyphs,rpl,tok->sofar);
1898 		    /* Ligature */
1899 		} else {
1900 		    LogError(_("Unparseable glyph sequence in substitution on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1901 		    ++tok->err_count;
1902 		}
1903 	    }
1904 	    fea_markedglyphsFree(rpl);
1905 	} else {
1906 	    LogError(_("Expected 'by' or 'from' keywords in substitution on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1907 	    ++tok->err_count;
1908 	}
1909     } else {
1910 	/* Contextual */
1911 	FPST *fpst = fea_markedglyphs_to_fpst(tok,glyphs,false,false);
1912 	struct fpst_rule *r = fpst->rules;
1913 	if ( tok->type!=tk_by ) {
1914 	    LogError(_("Expected 'by' keyword in substitution on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1915 	    ++tok->err_count;
1916 	}
1917 	rpl = fea_ParseMarkedGlyphs(tok,false,false,true);
1918 	for ( g=glyphs; g!=NULL && g->mark_count==0; g=g->next );
1919 	for ( i=0, rp=rpl; g!=NULL && rp!=NULL; ++i, rp=rp->next ) {
1920 	    if ( rp->lookupname!=NULL ) {
1921 		head = chunkalloc(sizeof(struct feat_item));
1922 		head->type = ft_lookup_ref;
1923 		head->u1.lookup_name = copy(rp->lookupname);
1924 	    } else if ( g->next==NULL || g->next->mark_count!=g->mark_count ) {
1925 		head = fea_process_sub_single(tok,g,rp,NULL);
1926 	    } else if ( g->next!=NULL && g->mark_count==g->next->mark_count ) {
1927 		head = fea_process_sub_ligature(tok,g,rpl,NULL);
1928 	    } else {
1929 		LogError(_("Unparseable contextual sequence on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1930 		++tok->err_count;
1931 	    }
1932 	    r->lookups[i].lookup = (OTLookup *) head;
1933 	    cnt = g->mark_count;
1934 	    while ( g!=NULL && g->mark_count == cnt )	/* skip everything involved here */
1935 		g=g->next;
1936 	    for ( ; g!=NULL && g->mark_count!=0; g=g->next ); /* skip any uninvolved glyphs */
1937 	}
1938 
1939 	fea_markedglyphsFree(rpl);
1940     }
1941 
1942     fea_end_statement(tok);
1943     fea_markedglyphsFree(glyphs);
1944 }
1945 
fea_ParseMarks(struct parseState * tok)1946 static void fea_ParseMarks(struct parseState *tok) {
1947     /* mark name|class <anchor> */
1948     char *contents = NULL;
1949     SplineChar *sc = NULL;
1950     AnchorPoint *ap;
1951     char *start, *pt;
1952     int ch;
1953 
1954     fea_ParseTok(tok);
1955     if ( tok->type==tk_name )
1956 	sc = fea_glyphname_get(tok,tok->tokbuf);
1957     else if ( tok->type==tk_class )
1958 	contents = fea_lookup_class_complain(tok,tok->tokbuf);
1959     else if ( tok->type==tk_char && tok->tokbuf[0]=='[' )
1960 	contents = fea_ParseGlyphClass(tok);
1961     else {
1962 	LogError(_("Expected glyph name or class in mark statement on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
1963 	++tok->err_count;
1964 	fea_skip_to_semi(tok);
1965 return;
1966     }
1967     if ( sc==NULL && contents==NULL ) {
1968 	fea_skip_to_semi(tok);
1969 return;
1970     }
1971 
1972     fea_TokenMustBe(tok,tk_char,'<');
1973     fea_TokenMustBe(tok,tk_anchor,'\0');
1974     ap = fea_ParseAnchor(tok);
1975     ap->type = at_mark;
1976     fea_end_statement(tok);
1977 
1978     if ( ap!=NULL ) {
1979 	pt = contents;
1980 	forever {
1981 	    struct feat_item *item = chunkalloc(sizeof(struct feat_item));
1982 	    item->type = ft_ap;
1983 	    item->u2.ap = ap;
1984 	    item->next = tok->sofar;
1985 	    tok->sofar = item;
1986 	    start = pt;
1987 	    if ( contents==NULL ) {
1988 		item->u1.sc = sc;
1989 	break;
1990 	    }
1991 	    while ( *pt!='\0' && *pt!=' ' )
1992 		++pt;
1993 	    ch = *pt; *pt = '\0';
1994 	    sc = fea_glyphname_get(tok,start);
1995 	    *pt = ch;
1996 	    while ( isspace(*pt)) ++pt;
1997 	    if ( sc==NULL ) {
1998 		tok->sofar = item->next;	/* Oops, remove it */
1999 		chunkfree(item,sizeof(*item));
2000 		if ( *pt=='\0' ) {
2001 		    AnchorPointsFree(ap);
2002 	break;
2003 		}
2004 	    } else {
2005 		item->u1.sc = sc;
2006 		if ( *pt=='\0' )
2007 	break;
2008 		ap = AnchorPointsCopy(ap);
2009 	    }
2010 	}
2011     }
2012     free(contents);
2013 }
2014 
fea_ParsePosition(struct parseState * tok,int enumer)2015 static void fea_ParsePosition(struct parseState *tok, int enumer) {
2016     /* name <vr> => single pos */
2017     /* class <vr> => single pos */
2018     /* name|class <vr> name|class <vr> => pair pos */
2019     /* name|class name|class <vr> => pair pos */
2020     /* cursive name|class <anchor> <anchor> => cursive positioning */
2021     /* name|class <anchor> mark name|class => mark to base pos */
2022 	/* Must be preceded by a mark statement */
2023     /* name|class <anchor> <anchor>+ mark name|class => mark to ligature pos */
2024 	/* Must be preceded by a mark statement */
2025     /* mark name|class <anchor> mark name|class => mark to base pos */
2026 	/* Must be preceded by a mark statement */
2027     /* <marked glyph pos sequence> => context chaining */
2028     /* [ignore pos] <marked glyph sequence> (, <marked g sequence>)* */
2029     struct markedglyphs *glyphs = fea_ParseMarkedGlyphs(tok,true,true,false), *g;
2030     int cnt, i;
2031     struct feat_item *item;
2032     char *start, *pt, ch;
2033     SplineChar *sc;
2034 
2035     fea_ParseTok(tok);
2036     for ( cnt=0, g=glyphs; g!=NULL; g=g->next, ++cnt );
2037     if ( glyphs==NULL ) {
2038 	LogError(_("Empty position on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2039 	++tok->err_count;
2040     } else if ( !glyphs->has_marks ) {
2041 	/* Non-contextual */
2042 	if ( glyphs->is_cursive ) {
2043 	    if ( cnt!=1 || glyphs->ap_cnt!=2 ) {
2044 		LogError(_("Invalid cursive position on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2045 		++tok->err_count;
2046 	    } else {
2047 		start = glyphs->name_or_class;
2048 		if ( glyphs->anchors[1]!=NULL )
2049 		    glyphs->anchors[1]->type = at_cexit;
2050 		forever {
2051 		    while ( *start==' ' ) ++start;
2052 		    if ( *start=='\0' )
2053 		break;
2054 		    for ( pt=start; *pt!='\0' && *pt!=' '; ++pt );
2055 		    ch = *pt; *pt = '\0';
2056 		    sc = fea_glyphname_get(tok,start);
2057 		    *pt = ch; start = pt;
2058 		    if ( sc!=NULL ) {
2059 			item = chunkalloc(sizeof(struct feat_item));
2060 			item->type = ft_ap;
2061 			item->next = tok->sofar;
2062 			tok->sofar = item;
2063 			item->u1.sc = sc;
2064 			if ( glyphs->anchors[0]!=NULL ) {
2065 			    glyphs->anchors[0]->type = at_centry;
2066 			    glyphs->anchors[0]->next = glyphs->anchors[1];
2067 			    item->u2.ap = AnchorPointsCopy(glyphs->anchors[0]);
2068 			} else
2069 			    item->u2.ap = AnchorPointsCopy(glyphs->anchors[1]);
2070 		    }
2071 		}
2072 	    }
2073 	} else if ( cnt==1 && glyphs->vr!=NULL ) {
2074 	    tok->sofar = fea_process_pos_single(tok,glyphs,tok->sofar);
2075 	} else if ( cnt==2 && (glyphs->vr!=NULL || glyphs->next->vr!=NULL) ) {
2076 	    tok->sofar = fea_process_pos_pair(tok,glyphs,tok->sofar, enumer);
2077 	} else if ( cnt==1 && glyphs->ap_cnt>=1 && tok->type == tk_mark ) {
2078 	    /* Mark to base, mark to mark, mark to ligature */
2079 	    char *mark_class;
2080 	    AnchorPoint *head=NULL, *last=NULL;
2081 	    if ( tok->type!=tk_mark ) {
2082 		LogError(_("A mark glyph (or class of marks) must be specified here on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2083 		++tok->err_count;
2084 	    }
2085 	    fea_ParseTok(tok);
2086 	    if ( tok->type==tk_name )
2087 		mark_class = copy(tok->tokbuf);
2088 	    else
2089 		mark_class = fea_canonicalClassOrder(fea_ParseGlyphClassGuarded(tok));
2090 	    fea_ParseTok(tok);
2091 	    if ( glyphs->is_mark && glyphs->ap_cnt>1 ) {
2092 		LogError(_("Mark to base anchor statements may only have one anchor on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2093 		++tok->err_count;
2094 	    }
2095 	    if ( mark_class!=NULL ) {
2096 		for ( i=0; i<glyphs->ap_cnt; ++i ) {
2097 		    if ( glyphs->anchors[i]==NULL )
2098 			/* Nothing to be done */;
2099 		    else {
2100 			if ( glyphs->ap_cnt>1 ) {
2101 			    glyphs->anchors[i]->type = at_baselig;
2102 			    glyphs->anchors[i]->lig_index = i;
2103 			} else if ( glyphs->is_mark )
2104 			    glyphs->anchors[i]->type = at_basemark;
2105 			else
2106 			    glyphs->anchors[i]->type = at_basechar;
2107 			if ( head==NULL )
2108 			    head = glyphs->anchors[i];
2109 			else
2110 			    last->next = glyphs->anchors[i];
2111 			last = glyphs->anchors[i];
2112 		    }
2113 		}
2114 
2115 		start = glyphs->name_or_class;
2116 		forever {
2117 		    while ( *start==' ' ) ++start;
2118 		    if ( *start=='\0' )
2119 		break;
2120 		    for ( pt=start; *pt!='\0' && *pt!=' '; ++pt );
2121 		    ch = *pt; *pt = '\0';
2122 		    sc = fea_glyphname_get(tok,start);
2123 		    *pt = ch; start = pt;
2124 		    if ( sc!=NULL ) {
2125 			item = chunkalloc(sizeof(struct feat_item));
2126 			item->type = ft_ap;
2127 			item->next = tok->sofar;
2128 			tok->sofar = item;
2129 			item->u1.sc = sc;
2130 			item->u2.ap = AnchorPointsCopy(head);
2131 			item->mark_class = copy(mark_class);
2132 		    }
2133 		}
2134 
2135 		/* So we can free them properly */
2136 		for ( ; head!=NULL; head = last ) {
2137 		    last = head->next;
2138 		    head->next = NULL;
2139 		}
2140 	    }
2141 	} else {
2142 	    LogError(_("Unparseable glyph sequence in position on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2143 	    ++tok->err_count;
2144 	}
2145     } else {
2146 	/* Contextual */
2147 	(void) fea_markedglyphs_to_fpst(tok,glyphs,true,false);
2148     }
2149     fea_now_semi(tok);
2150     fea_markedglyphsFree(glyphs);
2151 }
2152 
fea_LookupTypeFromItem(struct feat_item * item)2153 static enum otlookup_type fea_LookupTypeFromItem(struct feat_item *item) {
2154     switch ( item->type ) {
2155       case ft_pst: case ft_pstclass:
2156 	switch ( item->u2.pst->type ) {
2157 	  case pst_position:
2158 return( gpos_single );
2159 	  case pst_pair:
2160 return( gpos_pair );
2161 	  case pst_substitution:
2162 return( gsub_single );
2163 	  case pst_alternate:
2164 return( gsub_alternate );
2165 	  case pst_multiple:
2166 return( gsub_multiple );
2167 	  case pst_ligature:
2168 return( gsub_ligature );
2169 	  default:
2170 return( ot_undef );		/* Can't happen */
2171 	}
2172       break;
2173       case ft_ap:
2174 	switch( item->u2.ap->type ) {
2175 	  case at_centry: case at_cexit:
2176 return( gpos_cursive );
2177 	  case at_mark:
2178 return( ot_undef );		/* Can be used in three different lookups. Not enough info */
2179 	  case at_basechar:
2180 return( gpos_mark2base );
2181 	  case at_baselig:
2182 return( gpos_mark2ligature );
2183 	  case at_basemark:
2184 return( gpos_mark2mark );
2185 	  default:
2186 return( ot_undef );		/* Can't happen */
2187 	}
2188       break;
2189       case ft_fpst:
2190 	switch( item->u2.fpst->type ) {
2191 	  case pst_chainpos:
2192 return( gpos_contextchain );
2193 	  case pst_chainsub:
2194 return( gsub_contextchain );
2195 	  default:
2196 return( ot_undef );		/* Can't happen */
2197 	}
2198       break;
2199       default:
2200 return( ot_undef );		/* Can happen */
2201     }
2202 }
2203 
fea_AddFeatItem(struct parseState * tok,enum feat_type type,uint32 tag)2204 static struct feat_item *fea_AddFeatItem(struct parseState *tok,enum feat_type type,uint32 tag) {
2205     struct feat_item *item;
2206 
2207     item = chunkalloc(sizeof(struct feat_item));
2208     item->type = type;
2209     item->u1.tag = tag;
2210     item->next = tok->sofar;
2211     tok->sofar = item;
2212 return( item );
2213 }
2214 
fea_LookupSwitch(struct parseState * tok)2215 static int fea_LookupSwitch(struct parseState *tok) {
2216     int enumer = false;
2217 
2218     switch ( tok->type ) {
2219       case tk_class:
2220 	fea_ParseGlyphClassDef(tok);
2221       break;
2222       case tk_lookupflag:
2223 	fea_ParseLookupFlags(tok);
2224       break;
2225       case tk_mark:
2226 	fea_ParseMarks(tok);
2227       break;
2228       case tk_ignore:
2229 	fea_ParseIgnore(tok);
2230       break;
2231       case tk_enumerate:
2232 	fea_TokenMustBe(tok,tk_position,'\0');
2233 	enumer = true;
2234 	/* Fall through */;
2235       case tk_position:
2236 	fea_ParsePosition(tok,enumer);
2237       break;
2238       case tk_substitute:
2239 	fea_ParseSubstitute(tok);
2240 	enumer = false;
2241       break;
2242       case tk_subtable:
2243 	fea_AddFeatItem(tok,ft_subtable,0);
2244 	fea_TokenMustBe(tok,tk_char,';');
2245       break;
2246       case tk_char:
2247 	if ( tok->tokbuf[0]=='}' )
2248 return( 2 );
2249 	/* Fall through */
2250       default:
2251 return( 0 );
2252     }
2253 return( 1 );
2254 }
2255 
fea_ParseLookupDef(struct parseState * tok,int could_be_stat)2256 static void fea_ParseLookupDef(struct parseState *tok, int could_be_stat ) {
2257     char *lookup_name;
2258     struct feat_item *item, *first_after_mark;
2259     enum otlookup_type lookuptype;
2260     int has_marks;
2261     int ret;
2262 
2263     fea_ParseTok(tok);
2264     if ( tok->type!=tk_name ) {
2265 	LogError(_("Expected name in lookup on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2266 	++tok->err_count;
2267 	fea_skip_to_semi(tok);
2268 return;
2269     }
2270     lookup_name = copy(tok->tokbuf);
2271     fea_ParseTok(tok);
2272     if ( could_be_stat && tok->type==tk_char && tok->tokbuf[0]==';' ) {
2273 	item = chunkalloc(sizeof(struct feat_item));
2274 	item->type = ft_lookup_ref;
2275 	item->u1.lookup_name = lookup_name;
2276 	item->next = tok->sofar;
2277 	tok->sofar = item;
2278 return;
2279     } else if ( tok->type==tk_useExtension )		/* I just ignore this */
2280 	fea_ParseTok(tok);
2281     if ( tok->type!=tk_char || tok->tokbuf[0]!='{' ) {
2282 	LogError(_("Expected '{' in feature definition on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2283 	++tok->err_count;
2284 	fea_skip_to_semi(tok);
2285 return;
2286     }
2287 
2288     item = chunkalloc(sizeof(struct feat_item));
2289     item->type = ft_lookup_start;
2290     item->u1.lookup_name = lookup_name;
2291     item->next = tok->sofar;
2292     tok->sofar = item;
2293 
2294     first_after_mark = NULL;
2295     forever {
2296 	fea_ParseTok(tok);
2297 	if ( tok->err_count>100 )
2298     break;
2299 	if ( tok->type==tk_eof ) {
2300 	    LogError(_("Unexpected end of file in lookup definition on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2301 	    ++tok->err_count;
2302 return;
2303 	} else if ( (ret = fea_LookupSwitch(tok))==0 ) {
2304 	    LogError(_("Unexpected token, %s, in lookup definition on line %d of %s"), tok->tokbuf, tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2305 	    if ( tok->type==tk_name && strcmp(tok->tokbuf,"subs")==0 )
2306 		LogError(_(" Perhaps you meant to use the keyword 'sub' rather than 'subs'?") );
2307 	    ++tok->err_count;
2308 return;
2309 	} else if ( ret==2 )
2310     break;
2311 	/* Ok, mark classes must either contain exactly the same glyphs, or */
2312 	/*  they may not intersect at all */
2313 	/* Mark2* lookups are not well documented (because adobe FDK doesn't */
2314 	/*  support them) but I'm going to assume that if I have some mark */
2315 	/*  statements, then some pos statement, then another mark statement */
2316 	/*  that I begin a new subtable with the second set of marks (and a */
2317 	/*  different set of mark classes) */
2318 	if ( tok->sofar!=NULL && tok->sofar->type==ft_subtable )
2319 	    first_after_mark = NULL;
2320 	else if ( tok->sofar!=NULL && tok->sofar->type==ft_ap ) {
2321 	    if ( tok->sofar->u2.ap->type == at_mark )
2322 		first_after_mark = NULL;
2323 	    else if ( tok->sofar->mark_class==NULL )
2324 		/* we don't have to worry about Cursive connections */;
2325 	    else if ( first_after_mark == NULL )
2326 		first_after_mark = tok->sofar;
2327 	    else {
2328 		struct feat_item *f;
2329 		for ( f = tok->sofar->next; f!=NULL; f=f->next ) {
2330 		    if ( f->type==ft_lookup_start || f->type==ft_subtable )
2331 		break;
2332 		    if ( f->type!=ft_ap || f->mark_class==NULL )
2333 		continue;
2334 		    if ( strcmp(tok->sofar->mark_class,f->mark_class)==0 )
2335 		continue;		/* same glyphs, that's ok */
2336 		    else if ( fea_classesIntersect(tok->sofar->mark_class,f->mark_class)) {
2337 			LogError(_("Mark classes must either be exactly the same or contain no common glyphs\n But the class on line %d of %s contains a match."),
2338 				tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2339 			++tok->err_count;
2340 		    }
2341 		    if ( f==first_after_mark )
2342 		break;
2343 		}
2344 	    }
2345 	}
2346     }
2347     fea_ParseTok(tok);
2348     if ( tok->type!=tk_name || strcmp(tok->tokbuf,lookup_name)!=0 ) {
2349 	LogError(_("Expected %s in lookup definition on line %d of %s"),
2350 		lookup_name, tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2351 	++tok->err_count;
2352     }
2353     fea_end_statement(tok);
2354 
2355     /* Make sure all entries in this lookup of the same lookup type */
2356     lookuptype = ot_undef;
2357     has_marks = false;
2358     for ( item=tok->sofar ; item!=NULL && item->type!=ft_lookup_start; item=item->next ) {
2359 	enum otlookup_type cur = fea_LookupTypeFromItem(item);
2360 	if ( item->type==ft_ap && item->u2.ap->type == at_mark )
2361 	    has_marks = true;
2362 	if ( cur==ot_undef )	/* Some entries in the list (lookupflags) have no type */
2363 	    /* Tum, ty, tum tum */;
2364 	else if ( lookuptype==ot_undef )
2365 	    lookuptype = cur;
2366 	else if ( lookuptype!=cur ) {
2367 	    LogError(_("All entries in a lookup must have the same type on line %d of %s"),
2368 		    tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2369 	    ++tok->err_count;
2370     break;
2371 	}
2372     }
2373     if ( lookuptype==ot_undef ) {
2374 	LogError(_("This lookup has no effect, I can't figure out its type on line %d of %s"),
2375 		tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2376 	++tok->err_count;
2377     } else if ( has_marks && lookuptype!=gpos_mark2base &&
2378 		lookuptype!=gpos_mark2mark &&
2379 		lookuptype!=gpos_mark2ligature ) {
2380 	LogError(_("Mark glyphs may not be specified with this type of lookup on line %d of %s"),
2381 		tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2382 	++tok->err_count;
2383     }
2384 
2385     item = chunkalloc(sizeof(struct feat_item));
2386     item->type = ft_lookup_end;
2387     /* item->u1.lookup_name = lookup_name; */
2388     item->next = tok->sofar;
2389     tok->sofar = item;
2390 }
2391 
fea_ParseNameId(struct parseState * tok,int strid)2392 static struct nameid *fea_ParseNameId(struct parseState *tok,int strid) {
2393     int platform = 3, specific = 1, language = 0x409;
2394     struct nameid *nm;
2395     char *start, *pt;
2396     int max, ch, value;
2397     FILE *in = tok->inlist[tok->inc_depth];
2398     /* nameid <id> [<string attibute>] string; */
2399     /*  "nameid" and <id> will already have been parsed when we get here */
2400     /* <string attribute> := <platform> | <platform> <specific> <language> */
2401     /* <patform>==3 => <specific>=1 <language>=0x409 */
2402     /* <platform>==1 => <specific>=0 <lang>=0 */
2403     /* string in double quotes \XXXX escapes to UCS2 (for 3) */
2404     /* string in double quotes \XX escapes to MacLatin (for 1) */
2405     /* I only store 3,1 strings */
2406 
2407     fea_ParseTok(tok);
2408     if ( tok->type == tk_int ) {
2409 	if ( tok->value!=3 && tok->value!=1 ) {
2410 	    LogError(_("Invalid platform for string on line %d of %s"),
2411 		    tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2412 	    ++tok->err_count;
2413 	} else if ( tok->value==1 ) {
2414 	    specific = language = 0;
2415 	}
2416 	fea_ParseTok(tok);
2417 	if ( tok->type == tk_int ) {
2418 	    specific = tok->value;
2419 	    tok->base = 0;
2420 	    fea_TokenMustBe(tok,tk_int,'\0');
2421 	    language = tok->value;
2422 	    tok->base = 10;
2423 	    fea_ParseTok(tok);
2424 	}
2425     }
2426     if ( tok->type!=tk_char || tok->tokbuf[0]!='"' ) {
2427 	LogError(_("Expected string on line %d of %s"),
2428 		tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2429 	++tok->err_count;
2430 	fea_skip_to_semi(tok);
2431 	nm = NULL;
2432     } else {
2433 	if ( platform==3 && specific==1 ) {
2434 	    nm = chunkalloc(sizeof(struct nameid));
2435 	    nm->strid = strid;
2436 	    nm->platform = platform;
2437 	    nm->specific = specific;
2438 	    nm->language = language;
2439 	} else
2440 	    nm = NULL;
2441 	max = 0;
2442 	pt = start = NULL;
2443 	while ( (ch=getc(in))!=EOF && ch!='"' ) {
2444 	    if ( ch=='\n' || ch=='\r' )
2445 	continue;		/* Newline characters are ignored here */
2446 				/*  may be specified with backslashes */
2447 	    if ( ch=='\\' ) {
2448 		int i, dmax = platform==3 ? 4 : 2;
2449 		value = 0;
2450 		for ( i=0; i<dmax; ++i ) {
2451 		    ch = getc(in);
2452 		    if ( !ishexdigit(ch)) {
2453 			ungetc(ch,in);
2454 		break;
2455 		    }
2456 		    if ( ch>='a' && ch<='f' )
2457 			ch -= ('a'-10);
2458 		    else if ( ch>='A' && ch<='F' )
2459 			ch -= ('A'-10);
2460 		    else
2461 			ch -= '0';
2462 		    value <<= 4;
2463 		    value |= ch;
2464 		}
2465 	    } else
2466 		value = ch;
2467 	    if ( nm!=NULL ) {
2468 		if ( pt-start+3>=max ) {
2469 		    int off = pt-start;
2470 		    start = grealloc(start,(max+=100)+1);
2471 		    pt = start+off;
2472 		}
2473 		pt = utf8_idpb(pt,value);
2474 	    }
2475 	}
2476 	if ( nm!=NULL ) {
2477 	    if ( pt==NULL )
2478 		nm->utf8_str = copy("");
2479 	    else {
2480 		*pt = '\0';
2481 		nm->utf8_str = copy(start);
2482 		free(start);
2483 	    }
2484 	}
2485 	if ( tok->type!=tk_char || tok->tokbuf[0]!='"' ) {
2486 	    LogError(_("End of file found in string on line %d of %s"),
2487 		    tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2488 	    ++tok->err_count;
2489 	} else
2490 	    fea_end_statement(tok);
2491     }
2492 return( nm );
2493 }
2494 
fea_ParseParameters(struct parseState * tok,struct feat_item * feat)2495 static struct feat_item *fea_ParseParameters(struct parseState *tok, struct feat_item *feat) {
2496     /* Ok. The only time a parameter keyword may be used is inside a 'size' */
2497     /*  feature and then it takes 4 numbers */
2498     /* The first, third and fourth are in decipoints and may be either */
2499     /*  integers or floats (in which case we must multiply them by 10) */
2500     int params[4];
2501     int i;
2502 
2503     memset(params,0,sizeof(params));
2504     for ( i=0; i<4; ++i ) {
2505 	params[i] = fea_ParseDeciPoints(tok);
2506 	if ( tok->type==tk_char && tok->tokbuf[0]==';' )
2507     break;
2508     }
2509     fea_end_statement(tok);
2510 
2511     if ( feat==NULL ) {
2512 	feat = chunkalloc(sizeof(struct feat_item));
2513 	feat->type = ft_sizeparams;
2514 	feat->next = tok->sofar;
2515 	tok->sofar = feat;
2516     }
2517     feat->u1.params = galloc(sizeof(params));
2518     memcpy(feat->u1.params,params,sizeof(params));
2519 return( feat );
2520 }
2521 
fea_ParseSizeMenuName(struct parseState * tok,struct feat_item * feat)2522 static struct feat_item *fea_ParseSizeMenuName(struct parseState *tok, struct feat_item *feat) {
2523     /* Sizemenuname takes either 0, 1 or 3 numbers and a string */
2524     /* if no numbers are given (or the first number is 3) then the string is */
2525     /*  unicode. Otherwise a mac encoding, treated as single byte */
2526     /* Since fontforge only supports windows strings here I shall parse and */
2527     /*  ignore mac strings */
2528     struct nameid *string;
2529 
2530     string = fea_ParseNameId(tok,-1);
2531 
2532     if ( string!=NULL ) {
2533 	if ( feat==NULL ) {
2534 	    feat = chunkalloc(sizeof(struct feat_item));
2535 	    feat->type = ft_sizeparams;
2536 	    feat->next = tok->sofar;
2537 	    tok->sofar = feat;
2538 	}
2539 	string->next = feat->u2.names;
2540 	feat->u2.names = string;
2541     }
2542 return( feat );
2543 }
2544 
fea_ParseFeatureDef(struct parseState * tok)2545 static void fea_ParseFeatureDef(struct parseState *tok) {
2546     uint32 feat_tag;
2547     struct feat_item *item, *size_item = NULL;
2548     int type, ret;
2549 
2550     fea_ParseTok(tok);
2551     if ( tok->type!=tk_name || !tok->could_be_tag ) {
2552 	LogError(_("Expected tag in feature on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2553 	++tok->err_count;
2554 	fea_skip_to_semi(tok);
2555 return;
2556     }
2557     feat_tag = tok->tag;
2558     tok->in_vkrn = feat_tag == CHR('v','k','r','n');
2559 
2560     item = chunkalloc(sizeof(struct feat_item));
2561     item->type = ft_feat_start;
2562     item->u1.tag = feat_tag;
2563     if ( tok->def_langsyses!=NULL )
2564 	item->u2.sl = SListCopy(tok->def_langsyses);
2565     else {
2566 	item->u2.sl = chunkalloc(sizeof(struct scriptlanglist));
2567 	item->u2.sl->script = DEFAULT_SCRIPT;
2568 	item->u2.sl->lang_cnt = 1;
2569 	item->u2.sl->langs[0] = DEFAULT_LANG;
2570     }
2571     item->next = tok->sofar;
2572     tok->sofar = item;
2573 
2574     fea_ParseTok(tok);
2575     if ( tok->type!=tk_char || tok->tokbuf[0]!='{' ) {
2576 	LogError(_("Expected '{' in feature definition on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2577 	++tok->err_count;
2578 	fea_skip_to_semi(tok);
2579 return;
2580     }
2581 
2582     forever {
2583 	fea_ParseTok(tok);
2584 	if ( tok->err_count>100 )
2585     break;
2586 	if ( tok->type==tk_eof ) {
2587 	    LogError(_("Unexpected end of file in feature definition on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2588 	    ++tok->err_count;
2589 return;
2590 	} else if ( (ret = fea_LookupSwitch(tok))==0 ) {
2591 	    switch ( tok->type ) {
2592 	      case tk_lookup:
2593 		fea_ParseLookupDef(tok,true);
2594 	      break;
2595 	      case tk_languagesystem:
2596 		fea_ParseLangSys(tok,true);
2597 	      break;
2598 	      case tk_feature:
2599 		/* can appear inside an 'aalt' feature. I don't support it, but */
2600 		/*  just parse and ignore it */
2601 		if ( feat_tag!=CHR('a','a','l','t')) {
2602 		    LogError(_("Features inside of other features are only permitted for 'aalt' features on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2603 		    ++tok->err_count;
2604 		}
2605 		fea_ParseTok(tok);
2606 		if ( tok->type!=tk_name || !tok->could_be_tag ) {
2607 		    LogError(_("Expected tag on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2608 		    ++tok->err_count;
2609 		}
2610 		fea_end_statement(tok);
2611 	      break;
2612 	      case tk_script:
2613 	      case tk_language:
2614 		/* If no lang specified after script use 'dflt', if no script specified before a language use 'latn' */
2615 		type = tok->type==tk_script ? ft_script : ft_lang;
2616 		fea_ParseTok(tok);
2617 		if ( tok->type!=tk_name || !tok->could_be_tag ) {
2618 		    LogError(_("Expected tag on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2619 		    ++tok->err_count;
2620 		} else {
2621 		    item = fea_AddFeatItem(tok,type,tok->tag);
2622 		    if ( type==ft_lang ) {
2623 			forever {
2624 			    fea_ParseTok(tok);
2625 			    if ( tok->type==tk_include_dflt )
2626 				/* Unneeded */;
2627 			    else if ( tok->type==tk_exclude_dflt )
2628 				item->u2.exclude_dflt = true;
2629 			    else if ( tok->type==tk_required )
2630 				/* Not supported by adobe (or me) */;
2631 			    else if ( tok->type==tk_char && tok->tokbuf[0]==';' )
2632 			break;
2633 			    else {
2634 				LogError(_("Expected ';' on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2635 				++tok->err_count;
2636 			break;
2637 			    }
2638 			}
2639 		    } else
2640 			fea_end_statement(tok);
2641 		}
2642 	      break;
2643 	      case tk_parameters:
2644 		if ( feat_tag==CHR('s','i','z','e') ) {
2645 		    size_item = fea_ParseParameters(tok, size_item);
2646 	      break;
2647 		}
2648 		/* Fall on through */
2649 	      case tk_name:
2650 		if ( feat_tag==CHR('s','i','z','e') && strcmp(tok->tokbuf,"sizemenuname")==0 ) {
2651 		    size_item = fea_ParseSizeMenuName(tok, size_item);
2652 	      break;
2653 		}
2654 		/* Fall on through */
2655 	      default:
2656 		LogError(_("Unexpected token, %s, in feature definition on line %d of %s"), tok->tokbuf, tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2657 		++tok->err_count;
2658 return;
2659 	    }
2660 	} else if ( ret==2 )
2661     break;
2662     }
2663 
2664     fea_ParseTok(tok);
2665     if ( tok->type!=tk_name || !tok->could_be_tag || tok->tag!=feat_tag ) {
2666 	LogError(_("Expected '%c%c%c%c' in lookup definition on line %d of %s"),
2667 		feat_tag>>24, feat_tag>>16, feat_tag>>8, feat_tag,
2668 		tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2669 	++tok->err_count;
2670     }
2671     fea_end_statement(tok);
2672 
2673     item = chunkalloc(sizeof(struct feat_item));
2674     item->type = ft_feat_end;
2675     item->u1.tag = feat_tag;
2676     item->next = tok->sofar;
2677     tok->sofar = item;
2678 
2679     tok->in_vkrn = false;
2680 }
2681 
fea_ParseNameTable(struct parseState * tok)2682 static void fea_ParseNameTable(struct parseState *tok) {
2683     struct nameid *head=NULL, *string;
2684     struct feat_item *item;
2685     /* nameid <id> [<string attibute>] string; */
2686 
2687     forever {
2688 	fea_ParseTok(tok);
2689 	if ( tok->type != tk_nameid )
2690     break;
2691 	fea_TokenMustBe(tok,tk_int,'\0');
2692 	string = fea_ParseNameId(tok,tok->value);
2693 	if ( string!=NULL ) {
2694 	    string->next = head;
2695 	    head = string;
2696 	}
2697     }
2698 
2699     if ( head!=NULL ) {
2700 	item = chunkalloc(sizeof(struct feat_item));
2701 	item->type = ft_names;
2702 	item->next = tok->sofar;
2703 	tok->sofar = item;
2704 	item->u2.names = head;
2705     }
2706     if ( tok->type!=tk_char || tok->tokbuf[0]!='}' ) {
2707 	LogError(_("Expected closing curly brace on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2708 	++tok->err_count;
2709     }
2710 }
2711 
fea_ParseTableKeywords(struct parseState * tok,struct tablekeywords * keys)2712 static void fea_ParseTableKeywords(struct parseState *tok, struct tablekeywords *keys) {
2713     int index;
2714     struct tablevalues *tv, *head = NULL;
2715     int i;
2716     struct feat_item *item;
2717 
2718     forever {
2719 	fea_ParseTok(tok);
2720 	if ( tok->type != tk_name )
2721     break;
2722 	for ( index=0; keys[index].name!=NULL; ++index ) {
2723 	    if ( strcmp(keys[index].name,tok->tokbuf)==0 )
2724 	break;
2725 	}
2726 	if ( keys[index].name==NULL ) {
2727 	    LogError(_("Unknown field %s on line %d of %s"), tok->tokbuf, tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2728 	    ++tok->err_count;
2729 	    index = -1;
2730 	}
2731 	if ( index!=-1 && keys[index].offset!=-1 ) {
2732 	    tv = chunkalloc(sizeof(struct tablevalues));
2733 	    tv->index = index;
2734 	} else
2735 	    tv = NULL;
2736 	fea_ParseTok(tok);
2737 	if ( strcmp(tok->tokbuf,"Vendor")==0 && tv!=NULL) {
2738 	    /* This takes a 4 character string */
2739 	    /* of course strings aren't part of the syntax, but it takes one anyway */
2740 	    if ( tok->type==tk_name && tok->could_be_tag )
2741 		/* Accept a normal tag, since that's what it really is */
2742 		tv->value = tok->tag;
2743 	    else if ( tok->type==tk_char && tok->tokbuf[0]=='"' ) {
2744 		uint8 foo[4]; int ch;
2745 		FILE *in = tok->inlist[tok->inc_depth];
2746 		memset(foo,' ',sizeof(foo));
2747 		for ( i=0; i<4; ++i ) {
2748 		    ch = getc(in);
2749 		    if ( ch==EOF )
2750 		break;
2751 		    else if ( ch=='"' ) {
2752 			ungetc(ch,in);
2753 		break;
2754 		    }
2755 		    foo[i] = ch;
2756 		}
2757 		while ( (ch=getc(in))!=EOF && ch!='"' );
2758 		tok->value=(foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3];
2759 	    } else {
2760 		LogError(_("Expected string on line %d of %s"),
2761 			tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2762 		++tok->err_count;
2763 		chunkfree(tv,sizeof(*tv));
2764 		tv = NULL;
2765 	    }
2766 	    fea_ParseTok(tok);
2767 	} else {
2768 	    if ( tok->type!=tk_int ) {
2769 		LogError(_("Expected integer on line %d of %s"),
2770 			tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2771 		++tok->err_count;
2772 		chunkfree(tv,sizeof(*tv));
2773 		tv = NULL;
2774 		fea_ParseTok(tok);
2775 	    } else {
2776 		if ( tv!=NULL )
2777 		    tv->value = tok->value;
2778 		if ( strcmp(keys[index].name,"FontRevision")==0 ) {
2779 		    /* Can take a float */
2780 		    FILE *in = tok->inlist[tok->inc_depth];
2781 		    int ch = getc(in);
2782 		    if ( ch=='.' )
2783 			for ( ch=getc(in); isdigit(ch); ch=getc(in));
2784 		    ungetc(ch,in);
2785 		}
2786 		if ( index!=-1 && keys[index].cnt!=1 ) {
2787 		    int is_panose = strcmp(keys[index].name,"Panose")==0 && tv!=NULL;
2788 		    if ( is_panose )
2789 			tv->panose_vals[0] = tv->value;
2790 		    for ( i=1; ; ++i ) {
2791 			fea_ParseTok(tok);
2792 			if ( tok->type!=tk_int )
2793 		    break;
2794 			if ( is_panose && i<10 && tv!=NULL )
2795 			    tv->panose_vals[i] = tok->value;
2796 		    }
2797 		} else
2798 		    fea_ParseTok(tok);
2799 	    }
2800 	}
2801 	if ( tok->type!=tk_char || tok->tokbuf[0]!=';' ) {
2802 	    LogError(_("Expected semicolon on line %d of %s"),
2803 		    tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2804 	    ++tok->err_count;
2805 	    fea_skip_to_close_curly(tok);
2806 	    chunkfree(tv,sizeof(*tv));
2807     break;
2808 	}
2809 	if ( tv!=NULL ) {
2810 	    tv->next = head;
2811 	    head = tv;
2812 	}
2813     }
2814     if ( tok->type!=tk_char || tok->tokbuf[0]!='}' ) {
2815 	LogError(_("Expected '}' on line %d of %s"),
2816 		tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2817 	++tok->err_count;
2818 	fea_skip_to_close_curly(tok);
2819     }
2820     if ( head!=NULL ) {
2821 	item = chunkalloc(sizeof(struct feat_item));
2822 	item->type = ft_tablekeys;
2823 	item->u1.offsets = keys;
2824 	item->u2.tvals = head;
2825 	item->next = tok->sofar;
2826 	tok->sofar = item;
2827     }
2828 }
2829 
fea_ParseGDEFTable(struct parseState * tok)2830 static void fea_ParseGDEFTable(struct parseState *tok) {
2831     /* GlyphClassDef <base> <lig> <mark> <component>; */
2832     /* Attach <glyph>|<glyph class> <number>+; */	/* parse & ignore */
2833     /* LigatureCaret <glyph>|<glyph class> <caret value>+ */
2834     int i;
2835     struct feat_item *item;
2836     int16 *carets=NULL; int len=0, max=0;
2837 
2838     forever {
2839 	fea_ParseTok(tok);
2840 	if ( tok->type!=tk_name )
2841     break;
2842 	if ( strcmp(tok->tokbuf,"Attach")==0 ) {
2843 	    fea_ParseTok(tok);
2844 	    /* Bug. Not parsing inline classes */
2845 	    if ( tok->type!=tk_class && tok->type!=tk_name ) {
2846 		LogError(_("Expected name or class on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2847 		++tok->err_count;
2848 		fea_skip_to_semi(tok);
2849 	    } else {
2850 		forever {
2851 		    fea_ParseTok(tok);
2852 		    if ( tok->type!=tk_int )
2853 		break;
2854 		}
2855 	    }
2856 	} else if ( strcmp(tok->tokbuf,"LigatureCaret")==0 ) {
2857 	    item = chunkalloc(sizeof(struct feat_item));
2858 	    item->type = ft_lcaret;
2859 	    item->next = tok->sofar;
2860 	    tok->sofar = item;
2861 
2862 	    fea_ParseTok(tok);
2863 	    if ( tok->type==tk_name )
2864 		item->u1.class = fea_glyphname_validate(tok,tok->tokbuf);
2865 	    else if ( tok->type==tk_cid )
2866 		item->u1.class = fea_cid_validate(tok,tok->value);
2867 	    else if ( tok->type == tk_class || (tok->type==tk_char && tok->tokbuf[0]=='['))
2868 		item->u1.class = fea_ParseGlyphClassGuarded(tok);
2869 	    else {
2870 		LogError(_("Expected name or class on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2871 		++tok->err_count;
2872 		fea_skip_to_semi(tok);
2873     continue;
2874 	    }
2875 	    forever {
2876 		fea_ParseTok(tok);
2877 		if ( tok->type==tk_int )
2878 		    /* Not strictly cricket, but I'll accept it */;
2879 		else if ( tok->type==tk_char && tok->tokbuf[0]=='<' )
2880 		    fea_ParseCaret(tok);
2881 		else
2882 	    break;
2883 		if ( len>=max )
2884 		    carets = grealloc(carets,(max+=10)*sizeof(int16));
2885 		carets[len++] = tok->value;
2886 	    }
2887 	    if ( tok->type!=tk_char || tok->tokbuf[0]!=';' ) {
2888 		LogError(_("Expected semicolon on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2889 		++tok->err_count;
2890 		fea_skip_to_semi(tok);
2891 	    }
2892 	    item->u2.lcaret = galloc((len+1)*sizeof(int16));
2893 	    memcpy(item->u2.lcaret,carets,len*sizeof(int16));
2894 	    item->u2.lcaret[len] = 0;
2895 	} else if ( strcmp(tok->tokbuf,"GlyphClassDef")==0 ) {
2896 	    item = chunkalloc(sizeof(struct feat_item));
2897 	    item->type = ft_gdefclasses;
2898 	    item->u1.gdef_classes = chunkalloc(sizeof(char *[4]));
2899 	    item->next = tok->sofar;
2900 	    tok->sofar = item;
2901 	    for ( i=0; i<4; ++i ) {
2902 		fea_ParseTok(tok);
2903 		item->u1.gdef_classes[i] = fea_ParseGlyphClassGuarded(tok);
2904 	    }
2905 	    fea_ParseTok(tok);
2906 	} else {
2907 	    LogError(_("Expected Attach or LigatureCaret or GlyphClassDef on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2908 	    ++tok->err_count;
2909     break;
2910 	}
2911     }
2912     if ( tok->type!=tk_char || tok->tokbuf[0]!='}' ) {
2913 	LogError(_("Unexpected token in GDEF on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2914 	++tok->err_count;
2915 	fea_skip_to_close_curly(tok);
2916     }
2917     free(carets);
2918 }
2919 
fea_ParseTableDef(struct parseState * tok)2920 static void fea_ParseTableDef(struct parseState *tok) {
2921     uint32 table_tag;
2922     struct feat_item *item;
2923 
2924     fea_ParseTag(tok);
2925     if ( tok->type!=tk_name || !tok->could_be_tag ) {
2926 	LogError(_("Expected tag in table on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2927 	++tok->err_count;
2928 	fea_skip_to_semi(tok);
2929 return;
2930     }
2931     table_tag = tok->tag;
2932 
2933     item = chunkalloc(sizeof(struct feat_item));
2934     item->type = ft_table;
2935     item->u1.tag = table_tag;
2936     item->next = tok->sofar;
2937     tok->sofar = item;
2938     fea_TokenMustBe(tok,tk_char,'{');
2939     switch ( table_tag ) {
2940       case CHR('G','D','E','F'):
2941 	fea_ParseGDEFTable(tok);
2942       break;
2943       case CHR('n','a','m','e'):
2944 	fea_ParseNameTable(tok);
2945       break;
2946 
2947       case CHR('h','h','e','a'):
2948 	fea_ParseTableKeywords(tok,hhead_keys);
2949       break;
2950       case CHR('v','h','e','a'):
2951 	fea_ParseTableKeywords(tok,vhead_keys);
2952       break;
2953       case CHR('O','S','/','2'):
2954 	fea_ParseTableKeywords(tok,os2_keys);
2955       break;
2956 
2957       case CHR('h','e','a','d'):
2958 	/* FontRevision <number>.<number>; */
2959 	/* Only one field here, and I don't really support it */
2960       case CHR('v','m','t','x'):
2961 	/* I don't support 'vmtx' tables */
2962       case CHR('B','A','S','E'):
2963 	/* I don't support 'BASE' tables */
2964       default:
2965 	fea_skip_to_close_curly(tok);
2966       break;
2967     }
2968 
2969     fea_ParseTag(tok);
2970     if ( tok->type!=tk_name || !tok->could_be_tag || tok->tag!=table_tag ) {
2971 	LogError(_("Expected matching tag in table on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
2972 	++tok->err_count;
2973 	fea_skip_to_semi(tok);
2974 return;
2975     }
2976     fea_end_statement(tok);
2977 }
2978 
2979 /* ************************************************************************** */
2980 /* ******************************* Free feat ******************************** */
2981 /* ************************************************************************** */
2982 
NameIdFree(struct nameid * nm)2983 static void NameIdFree(struct nameid *nm) {
2984     struct nameid *nmnext;
2985 
2986     while ( nm!=NULL ) {
2987 	nmnext = nm->next;
2988 	free( nm->utf8_str );
2989 	chunkfree(nm,sizeof(*nm));
2990 	nm = nmnext;
2991     }
2992 }
2993 
TableValsFree(struct tablevalues * tb)2994 static void TableValsFree(struct tablevalues *tb) {
2995     struct tablevalues *tbnext;
2996 
2997     while ( tb!=NULL ) {
2998 	tbnext = tb->next;
2999 	chunkfree(tb,sizeof(*tb));
3000 	tb = tbnext;
3001     }
3002 }
3003 
fea_featitemFree(struct feat_item * item)3004 static void fea_featitemFree(struct feat_item *item) {
3005     struct feat_item *next;
3006     int i,j;
3007 
3008     while ( item!=NULL ) {
3009 	next = item->next;
3010 	switch ( item->type ) {
3011 	  case ft_lookup_end:
3012 	  case ft_feat_end:
3013 	  case ft_table:
3014 	  case ft_subtable:
3015 	  case ft_script:
3016 	  case ft_lang:
3017 	  case ft_lookupflags:
3018 	    /* Nothing needs freeing */;
3019 	  break;
3020 	  case ft_feat_start:
3021 	  case ft_langsys:
3022 	    ScriptLangListFree( item->u2.sl);
3023 	  break;
3024 	  case ft_lookup_start:
3025 	  case ft_lookup_ref:
3026 	    free( item->u1.lookup_name );
3027 	  break;
3028 	  case ft_sizeparams:
3029 	    free( item->u1.params );
3030 	    NameIdFree( item->u2.names );
3031 	  break;
3032 	  case ft_names:
3033 	    NameIdFree( item->u2.names );
3034 	  break;
3035 	  case ft_gdefclasses:
3036 	    for ( i=0; i<4; ++i )
3037 		free(item->u1.gdef_classes[i]);
3038 	    chunkfree(item->u1.gdef_classes,sizeof(char *[4]));
3039 	  break;
3040 	  case ft_lcaret:
3041 	    free( item->u2.lcaret );
3042 	  break;
3043 	  case ft_tablekeys:
3044 	    TableValsFree( item->u2.tvals );
3045 	  break;
3046 	  case ft_pst:
3047 	    PSTFree( item->u2.pst );
3048 	  break;
3049 	  case ft_pstclass:
3050 	    free( item->u1.class );
3051 	    PSTFree( item->u2.pst );
3052 	  break;
3053 	  case ft_ap:
3054 	    AnchorPointsFree( item->u2.ap );
3055 	    free( item->mark_class );
3056 	  break;
3057 	  case ft_fpst:
3058 	    if ( item->u2.fpst!=NULL ) {
3059 		for ( i=0; i<item->u2.fpst->rule_cnt; ++i ) {
3060 		    struct fpst_rule *r = &item->u2.fpst->rules[i];
3061 		    for ( j=0; j<r->lookup_cnt; ++j ) {
3062 			if ( r->lookups[j].lookup!=NULL ) {
3063 			    struct feat_item *nested = (struct feat_item *) (r->lookups[j].lookup);
3064 			    fea_featitemFree(nested);
3065 			    r->lookups[j].lookup = NULL;
3066 			}
3067 		    }
3068 		}
3069 		FPSTFree(item->u2.fpst);
3070 	    }
3071 	  break;
3072 	  default:
3073 	    IError("Don't know how to free a feat_item of type %d", item->type );
3074 	  break;
3075 	}
3076 	chunkfree(item,sizeof(*item));
3077 	item = next;
3078     }
3079 }
3080 
fea_ParseFeatureFile(struct parseState * tok)3081 static void fea_ParseFeatureFile(struct parseState *tok) {
3082 
3083     forever {
3084 	fea_ParseTok(tok);
3085 	if ( tok->err_count>100 )
3086     break;
3087 	switch ( tok->type ) {
3088 	  case tk_class:
3089 	    fea_ParseGlyphClassDef(tok);
3090 	  break;
3091 	  case tk_lookup:
3092 	    fea_ParseLookupDef(tok,false);
3093 	  break;
3094 	  case tk_languagesystem:
3095 	    fea_ParseLangSys(tok,false);
3096 	  break;
3097 	  case tk_feature:
3098 	    fea_ParseFeatureDef(tok);
3099 	  break;
3100 	  case tk_table:
3101 	    fea_ParseTableDef(tok);
3102 	  break;
3103 	  case tk_anonymous:
3104 	    LogError(_("FontForge does not support anonymous tables on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
3105 	    fea_skip_to_close_curly(tok);
3106 	  break;
3107 	  case tk_eof:
3108   goto end_loop;
3109 	  default:
3110 	    LogError(_("Unexpected token, %s, on line %d of %s"), tok->tokbuf, tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
3111 	    ++tok->err_count;
3112   goto end_loop;
3113       }
3114     }
3115   end_loop:;
3116 }
3117 
3118 /* ************************************************************************** */
3119 /* ******************************* Apply feat ******************************* */
3120 /* ************************************************************************** */
fea_FeatItemEndsLookup(enum feat_type type)3121 static int fea_FeatItemEndsLookup(enum feat_type type) {
3122 return( type==ft_lookup_end || type == ft_feat_end ||
3123 	    type == ft_table || type == ft_script ||
3124 	    type == ft_lang || type == ft_langsys ||
3125 	    type == ft_lookup_ref );
3126 }
3127 
fea_SetLookupLink(struct feat_item * nested,enum otlookup_type type)3128 static struct feat_item *fea_SetLookupLink(struct feat_item *nested,
3129 	enum otlookup_type type) {
3130     struct feat_item *prev = NULL;
3131     enum otlookup_type found_type;
3132 
3133     while ( nested!=NULL ) {
3134 	/* Stop when we find something which forces a new lookup */
3135 	if ( fea_FeatItemEndsLookup(nested->type) )
3136     break;
3137 	if ( nested->ticked ) {
3138 	    nested = nested->next;
3139     continue;
3140 	}
3141 	found_type = fea_LookupTypeFromItem(nested);
3142 	if ( type==ot_undef || found_type == ot_undef || found_type == type ) {
3143 	    if ( nested->type!=ft_ap || nested->u2.ap->type!=at_mark )
3144 		nested->ticked = true;		/* Marks might get used in more than one lookup */
3145 	    if ( prev!=NULL )
3146 		prev->lookup_next = nested;
3147 	    prev = nested;
3148 	}
3149 	nested = nested->next;
3150     }
3151 return( nested );
3152 }
3153 
fea_ApplyLookupListPST(struct parseState * tok,struct feat_item * lookup_data,OTLookup * otl)3154 static void fea_ApplyLookupListPST(struct parseState *tok,
3155 	struct feat_item *lookup_data,OTLookup *otl) {
3156     struct lookup_subtable *sub = NULL, *last=NULL;
3157     struct feat_item *l;
3158     (void)tok;
3159     /* Simple pst's are easy. We just attach them to their proper glyphs */
3160     /*  and then clear the feat_item pst slot (so we don't free them later) */
3161     /* There might be a subtable break */
3162     /* There might be a lookupflags */
3163 
3164     for ( l = lookup_data; l!=NULL; l=l->lookup_next ) {
3165 	switch ( l->type ) {
3166 	  case ft_lookup_start:
3167 	  case ft_lookupflags:
3168 	    /* Ignore these, already handled them */;
3169 	  break;
3170 	  case ft_subtable:
3171 	    sub = NULL;
3172 	  break;
3173 	  case ft_pst:
3174 	    if ( sub==NULL ) {
3175 		sub = chunkalloc(sizeof(struct lookup_subtable));
3176 		sub->lookup = otl;
3177 		sub->per_glyph_pst_or_kern = true;
3178 		if ( last==NULL )
3179 		    otl->subtables = sub;
3180 		else
3181 		    last->next = sub;
3182 		last = sub;
3183 	    }
3184 	    l->u2.pst->subtable = sub;
3185 	    l->u2.pst->next = l->u1.sc->possub;
3186 	    l->u1.sc->possub = l->u2.pst;
3187 	    l->u2.pst = NULL;			/* So we don't free it later */
3188 	  break;
3189 	  default:
3190 	    IError("Unexpected feature type %d in a PST feature", l->type );
3191 	  break;
3192 	}
3193     }
3194 }
3195 
3196 static OTLookup *fea_ApplyLookupList(struct parseState *tok,
3197 	struct feat_item *lookup_data,int lookup_flag);
3198 
fea_ApplyLookupListContextual(struct parseState * tok,struct feat_item * lookup_data,OTLookup * otl)3199 static void fea_ApplyLookupListContextual(struct parseState *tok,
3200 	struct feat_item *lookup_data,OTLookup *otl) {
3201     struct lookup_subtable *sub = NULL, *last=NULL;
3202     struct feat_item *l;
3203     int i,j;
3204     /* Fpst's are almost as easy as psts. We don't worry about subtables */
3205     /*  (every fpst gets a new subtable, so the statement is irrelevant) */
3206     /* the only complication is that we must recursively handle a lookup list */
3207     /* There might be a lookupflags */
3208 
3209     for ( l = lookup_data; l!=NULL; l=l->lookup_next ) {
3210 	switch ( l->type ) {
3211 	  case ft_lookup_start:
3212 	  case ft_lookupflags:
3213 	  case ft_subtable:
3214 	    /* Ignore these, already handled them */;
3215 	  break;
3216 	  case ft_fpst:
3217 	    sub = chunkalloc(sizeof(struct lookup_subtable));
3218 	    sub->lookup = otl;
3219 	    if ( last==NULL )
3220 		otl->subtables = sub;
3221 	    else
3222 		last->next = sub;
3223 	    last = sub;
3224 	    sub->fpst = l->u2.fpst;
3225 	    l->u2.fpst->next = tok->sf->possub;
3226 	    tok->sf->possub = l->u2.fpst;
3227 	    l->u2.fpst = NULL;
3228 	    sub->fpst->subtable = sub;
3229 	    for ( i=0; i<sub->fpst->rule_cnt; ++i ) {
3230 		struct fpst_rule *r = &sub->fpst->rules[i];
3231 		for ( j=0; j<r->lookup_cnt; ++j ) {
3232 		    if ( r->lookups[j].lookup!=NULL ) {
3233 			struct feat_item *nested = (struct feat_item *) (r->lookups[j].lookup);
3234 			fea_SetLookupLink(nested,ot_undef);
3235 			r->lookups[j].lookup = fea_ApplyLookupList(tok,nested,otl->lookup_flags);	/* Not really sure what the lookup flag should be here. */
3236 			fea_featitemFree(nested);
3237 		    }
3238 		}
3239 	    }
3240 	  break;
3241 	  default:
3242 	    IError("Unexpected feature type %d in a FPST feature", l->type );
3243 	  break;
3244 	}
3245     }
3246 }
3247 
fea_ApplyLookupListCursive(struct parseState * tok,struct feat_item * lookup_data,OTLookup * otl)3248 static void fea_ApplyLookupListCursive(struct parseState *tok,
3249 	struct feat_item *lookup_data,OTLookup *otl) {
3250     struct lookup_subtable *sub = NULL, *last=NULL;
3251     struct feat_item *l;
3252     AnchorPoint *aplast, *ap;
3253     AnchorClass *ac = NULL;
3254     /* Cursive's are also easy. There might be two ap's in the list so slight */
3255     /*  care needed when adding them to a glyph, and we must create an anchorclass */
3256     /* There might be a subtable break */
3257     /* There might be a lookupflags */
3258 
3259     for ( l = lookup_data; l!=NULL; l=l->lookup_next ) {
3260 	switch ( l->type ) {
3261 	  case ft_lookup_start:
3262 	  case ft_lookupflags:
3263 	    /* Ignore these, already handled them */;
3264 	  break;
3265 	  case ft_subtable:
3266 	    sub = NULL;
3267 	  break;
3268 	  case ft_ap:
3269 	    if ( sub==NULL ) {
3270 		sub = chunkalloc(sizeof(struct lookup_subtable));
3271 		sub->lookup = otl;
3272 		sub->anchor_classes = true;
3273 		if ( last==NULL )
3274 		    otl->subtables = sub;
3275 		else
3276 		    last->next = sub;
3277 		last = sub;
3278 		ac = chunkalloc(sizeof(AnchorClass));
3279 		ac->subtable = sub;
3280 		ac->type = act_curs;
3281 		ac->next = tok->accreated;
3282 		tok->accreated = ac;
3283 	    }
3284 	    aplast = NULL;
3285 	    for ( ap=l->u2.ap; ap!=NULL; ap=ap->next ) {
3286 		aplast = ap;
3287 		ap->anchor = ac;
3288 	    }
3289 	    aplast->next = l->u1.sc->anchor;
3290 	    l->u1.sc->anchor = l->u2.ap;
3291 	    l->u2.ap = NULL;			/* So we don't free them later */
3292 	  break;
3293 	  default:
3294 	    IError("Unexpected feature type %d in a cursive feature", l->type );
3295 	  break;
3296 	}
3297     }
3298 }
3299 
fea_ApplyLookupListMark2(struct parseState * tok,struct feat_item * lookup_data,int mcnt,OTLookup * otl)3300 static void fea_ApplyLookupListMark2(struct parseState *tok,
3301 	struct feat_item *lookup_data,int mcnt,OTLookup *otl) {
3302     /* Mark2* lookups are not well documented (because adobe FDK doesn't */
3303     /*  support them) but I'm going to assume that if I have some mark */
3304     /*  statements, then some pos statement, then another mark statement */
3305     /*  that I begin a new subtable with the second set of marks (and a */
3306     /*  different set of mark classes) */
3307     char **classes;
3308     AnchorClass **acs;
3309     int ac_cnt, i;
3310     struct lookup_subtable *sub = NULL, *last=NULL;
3311     struct feat_item *mark_start, *l;
3312     AnchorPoint *ap, *aplast;
3313 
3314     classes = galloc(mcnt*sizeof(char *));
3315     acs = galloc(mcnt*sizeof(AnchorClass *));
3316     ac_cnt = 0;
3317     while ( lookup_data != NULL && lookup_data->type!=ft_lookup_end ) {
3318 	struct feat_item *orig = lookup_data;
3319 	sub = NULL;
3320 	/* Skip any subtable marks */
3321 	while ( lookup_data!=NULL &&
3322 		(lookup_data->type==ft_subtable ||
3323 		 lookup_data->type==ft_lookup_start ||
3324 		 lookup_data->type==ft_lookupflags ) )
3325 	    lookup_data = lookup_data->lookup_next;
3326 
3327 	/* Skip over the marks, we'll deal with them after we know the mark classes */
3328 	mark_start = lookup_data;
3329 	while ( lookup_data!=NULL &&
3330 		((lookup_data->type==ft_ap && lookup_data->u2.ap->type==at_mark) ||
3331 		 lookup_data->type==ft_lookup_start ||
3332 		 lookup_data->type==ft_lookupflags ) )
3333 	    lookup_data = lookup_data->lookup_next;
3334 
3335 	/* Now process the base glyphs and figure out the mark classes */
3336 	while ( lookup_data!=NULL &&
3337 		((lookup_data->type==ft_ap && lookup_data->mark_class!=NULL) ||
3338 		 lookup_data->type==ft_lookup_start ||
3339 		 lookup_data->type==ft_lookupflags ) ) {
3340 	    if ( lookup_data->type == ft_ap ) {
3341 		for ( i=0; i<ac_cnt; ++i ) {
3342 		    if ( strcmp(lookup_data->mark_class,classes[i])==0 )
3343 		break;
3344 		}
3345 		if ( i==ac_cnt ) {
3346 		    ++ac_cnt;
3347 		    classes[i] = lookup_data->mark_class;
3348 		    acs[i] = chunkalloc(sizeof(AnchorClass));
3349 		    if ( sub==NULL ) {
3350 			sub = chunkalloc(sizeof(struct lookup_subtable));
3351 			sub->lookup = otl;
3352 			sub->anchor_classes = true;
3353 			if ( last==NULL )
3354 			    otl->subtables = sub;
3355 			else
3356 			    last->next = sub;
3357 			last = sub;
3358 		    }
3359 		    acs[i]->subtable = sub;
3360 		    acs[i]->type = otl->lookup_type==gpos_mark2mark ? act_mkmk :
3361 				    otl->lookup_type==gpos_mark2base ? act_mark :
3362 					    act_mklg;
3363 		    acs[i]->next = tok->accreated;
3364 		    tok->accreated = acs[i];
3365 		}
3366 		aplast = NULL;
3367 		for ( ap=lookup_data->u2.ap; ap!=NULL; ap=ap->next ) {
3368 		    aplast = ap;
3369 		    ap->anchor = acs[i];
3370 		}
3371 		aplast->next = lookup_data->u1.sc->anchor;
3372 		lookup_data->u1.sc->anchor = lookup_data->u2.ap;
3373 		lookup_data->u2.ap = NULL;	/* So we don't free them later */
3374 	    }
3375 	    lookup_data = lookup_data->next;
3376 	}
3377 
3378 	/* Now go back and assign the marks to the correct anchor classes */
3379 	for ( l=mark_start; l!=NULL &&
3380 		/* The base aps will have been set to NULL above */
3381 		((l->type==ft_ap && l->u2.ap!=NULL && l->u2.ap->type==at_mark) ||
3382 		 l->type==ft_lookup_start ||
3383 		 l->type==ft_lookupflags ) ;
3384 		l = l->lookup_next ) {
3385 	    if ( l->type==ft_ap ) {
3386 		for ( i=0; i<ac_cnt; ++i ) {
3387 		    if ( fea_classesIntersect(l->u1.sc->name,classes[i])) {
3388 			AnchorPoint *ap = AnchorPointsCopy(l->u2.ap);
3389 			/* We make a copy of this anchor point because marks */
3390 			/*  might be used in more than one lookup. It makes */
3391 			/*  sense for a user to define a set of marks to be */
3392 			/*  used with both a m2base and a m2lig lookup within */
3393 			/*  a feature */
3394 			ap->anchor = acs[i];
3395 			ap->next = l->u1.sc->anchor;
3396 			l->u1.sc->anchor = ap;
3397 		break;
3398 		    }
3399 		}
3400 	    }
3401 	}
3402 	if ( lookup_data==orig )
3403     break;
3404     }
3405 }
3406 
3407 
is_blank(const char * s)3408 static int is_blank(const char *s) {
3409     int i;
3410 
3411     i = 0;
3412     while (s[i] != '\0' && s[i] == ' ')
3413         i++;
3414     return( s[i] == '\0');
3415 }
3416 
3417 struct class_set {
3418     char **classes;
3419     int cnt, max;
3420 };
3421 
3422 /* We've got a set of glyph classes -- but they are the classes that make sense */
3423 /*  to the user and so there's no guarantee that there aren't two classes with */
3424 /*  the same glyph(s) */
3425 /* Simplify the list so that: There are no duplicates classes and each name */
3426 /*  appears in at most one class. This is what we need */
fea_canonicalClassSet(struct class_set * set)3427 static void fea_canonicalClassSet(struct class_set *set) {
3428     int i,j,k;
3429 
3430     /* Remove any duplicate classes */
3431     qsort(set->classes,set->cnt,sizeof(char *), strcmpD);
3432     for ( i=0; i<set->cnt; ++i ) {
3433 	for ( j=i+1; j<set->cnt; ++j )
3434 	    if ( strcmp(set->classes[i],set->classes[j])!=0 )
3435 	break;
3436 	if ( j>i+1 ) {
3437 	    int off = j-(i+1);
3438 	    for ( k=i+1; k<j; ++k )
3439 		free(set->classes[k]);
3440 	    for ( k=j ; k<set->cnt; ++k )
3441 		set->classes[k-off] = set->classes[k];
3442 	    set->cnt -= off;
3443 	}
3444     }
3445 
3446     for ( i=0; i < set->cnt - 1; ++i ) {
3447         for ( j=i+1; j < set->cnt; ++j ) {
3448             if ( fea_classesIntersect(set->classes[i],set->classes[j]) ) {
3449                 if ( set->cnt>=set->max )
3450                     set->classes = grealloc(set->classes,(set->max+=20)*sizeof(char *));
3451                 set->classes[set->cnt++] = fea_classesSplit(set->classes[i],set->classes[j]);
3452             }
3453         }
3454     }
3455 
3456     /* Remove empty classes */
3457     i = 0;
3458     while (i < set->cnt) {
3459         if (is_blank(set->classes[i])) {
3460             free(set->classes[i]);
3461             for ( k=i+1 ; k < set->cnt; ++k )
3462                 set->classes[k-1] = set->classes[k];
3463             set->cnt -= 1;
3464         } else {
3465             i++;
3466         }
3467     }
3468 }
3469 
3470 #ifdef FONTFORGE_CONFIG_DEVICETABLES
KCFillDevTab(KernClass * kc,int index,DeviceTable * dt)3471 static void KCFillDevTab(KernClass *kc,int index,DeviceTable *dt) {
3472     if ( dt==NULL || dt->corrections == NULL )
3473 return;
3474     if ( kc->adjusts == NULL )
3475 	kc->adjusts = gcalloc(kc->first_cnt*kc->second_cnt,sizeof(DeviceTable));
3476     kc->adjusts[index] = *dt;
3477     kc->adjusts[index].corrections = galloc(dt->last_pixel_size-dt->first_pixel_size+1);
3478     memcpy(kc->adjusts[index].corrections,dt->corrections,dt->last_pixel_size-dt->first_pixel_size+1);
3479 
3480 }
3481 
KPFillDevTab(KernPair * kp,DeviceTable * dt)3482 static void KPFillDevTab(KernPair *kp,DeviceTable *dt) {
3483     if ( dt==NULL || dt->corrections == NULL )
3484 return;
3485     kp->adjust = chunkalloc(sizeof(DeviceTable));
3486     *kp->adjust = *dt;
3487     kp->adjust->corrections = galloc(dt->last_pixel_size-dt->first_pixel_size+1);
3488     memcpy(kp->adjust->corrections,dt->corrections,dt->last_pixel_size-dt->first_pixel_size+1);
3489 }
3490 #endif
3491 
fea_fillKernClass(KernClass * kc,struct feat_item * l)3492 static void fea_fillKernClass(KernClass *kc,struct feat_item *l) {
3493     int i,j;
3494     PST *pst;
3495 
3496     while ( l!=NULL && l->type!=ft_subtable ) {
3497 	if ( l->type==ft_pstclass ) {
3498 	    pst = l->u2.pst;
3499 	    for ( i=1; i<kc->first_cnt; ++i ) {
3500 		if ( fea_classesIntersect(kc->firsts[i],l->u1.class) ) {
3501 		    for ( j=1; j<kc->second_cnt; ++j ) {
3502 			if ( fea_classesIntersect(kc->seconds[j],pst->u.pair.paired) ) {
3503 			    /* FontForge only supports kerning classes in one direction at a time, not full value records */
3504 			    if ( pst->u.pair.vr[0].h_adv_off != 0 ) {
3505 				kc->offsets[i*kc->second_cnt+j] = pst->u.pair.vr[0].h_adv_off;
3506 #ifdef FONTFORGE_CONFIG_DEVICETABLES
3507 				if ( pst->u.pair.vr[0].adjust!=NULL )
3508 				    KCFillDevTab(kc,i*kc->second_cnt+j,&pst->u.pair.vr[0].adjust->xadv);
3509 #endif
3510 			    } else if ( pst->u.pair.vr[0].v_adv_off != 0 ) {
3511 				kc->offsets[i*kc->second_cnt+j] = pst->u.pair.vr[0].v_adv_off;
3512 #ifdef FONTFORGE_CONFIG_DEVICETABLES
3513 				if ( pst->u.pair.vr[0].adjust!=NULL )
3514 				    KCFillDevTab(kc,i*kc->second_cnt+j,&pst->u.pair.vr[0].adjust->yadv);
3515 #endif
3516 			    } else if ( pst->u.pair.vr[1].h_adv_off != 0 ) {
3517 				kc->offsets[i*kc->second_cnt+j] = pst->u.pair.vr[1].h_adv_off;
3518 #ifdef FONTFORGE_CONFIG_DEVICETABLES
3519 				if ( pst->u.pair.vr[1].adjust!=NULL )
3520 				    KCFillDevTab(kc,i*kc->second_cnt+j,&pst->u.pair.vr[1].adjust->xadv);
3521 #endif
3522 			    }
3523 			    if ( strcmp(kc->seconds[j],pst->u.pair.paired)==0 )
3524 		    break;
3525 			}
3526 		    }
3527 		    if ( strcmp(kc->firsts[i],l->u1.class)==0 )
3528 	    break;
3529 		}
3530 	    }
3531 	}
3532 	l = l->lookup_next;
3533     }
3534 }
3535 
SFKernClassRemoveFree(SplineFont * sf,KernClass * kc)3536 static void SFKernClassRemoveFree(SplineFont *sf,KernClass *kc) {
3537     KernClass *prev;
3538 
3539     if ( sf->kerns==kc )
3540 	sf->kerns = kc->next;
3541     else if ( sf->vkerns==kc )
3542 	sf->vkerns = kc->next;
3543     else {
3544 	prev = NULL;
3545 	if ( sf->kerns!=NULL )
3546 	    for ( prev=sf->kerns; prev!=NULL && prev->next!=kc; prev=prev->next );
3547 	if ( prev==NULL && sf->vkerns!=NULL )
3548 	    for ( prev=sf->vkerns; prev!=NULL && prev->next!=kc; prev=prev->next );
3549 	if ( prev!=NULL )
3550 	    prev->next = kc->next;
3551     }
3552     kc->next = NULL;
3553     KernClassListFree(kc);
3554 }
3555 
fea_ApplyLookupListPair(struct parseState * tok,struct feat_item * lookup_data,int kmax,OTLookup * otl)3556 static void fea_ApplyLookupListPair(struct parseState *tok,
3557 	struct feat_item *lookup_data,int kmax,OTLookup *otl) {
3558     /* kcnt is the number of left/right glyph-name-lists we must sort into classes */
3559     struct feat_item *l, *first;
3560     struct class_set lefts, rights;
3561     struct lookup_subtable *sub = NULL, *lastsub=NULL;
3562     SplineChar *sc, *other;
3563     PST *pst;
3564     KernPair *kp;
3565     KernClass *kc;
3566     int vkern, kcnt, i;
3567 
3568     memset(&lefts,0,sizeof(lefts));
3569     memset(&rights,0,sizeof(rights));
3570     if ( kmax!=0 ) {
3571 	lefts.classes = galloc(kmax*sizeof(char *));
3572 	rights.classes = galloc(kmax*sizeof(char *));
3573 	lefts.max = rights.max = kmax;
3574     }
3575     vkern = false;
3576     for ( l = lookup_data; l!=NULL; ) {
3577 	first = l;
3578 	kcnt = 0;
3579 	while ( l!=NULL && l->type!=ft_subtable ) {
3580 	    if ( l->type == ft_pst ) {
3581 		if ( sub==NULL ) {
3582 		    sub = chunkalloc(sizeof(struct lookup_subtable));
3583 		    sub->lookup = otl;
3584 		    sub->per_glyph_pst_or_kern = true;
3585 		    if ( lastsub==NULL )
3586 			otl->subtables = sub;
3587 		    else
3588 			lastsub->next = sub;
3589 		    lastsub = sub;
3590 		}
3591 		pst = l->u2.pst;
3592 		sc = l->u1.sc;
3593 		l->u2.pst = NULL;
3594 		kp = NULL;
3595 		other = SFGetChar(sc->parent,-1,pst->u.pair.paired);
3596 		if ( pst->u.pair.vr[0].xoff==0 && pst->u.pair.vr[0].yoff==0 &&
3597 			pst->u.pair.vr[1].xoff==0 && pst->u.pair.vr[1].yoff==0 &&
3598 			pst->u.pair.vr[1].v_adv_off==0 &&
3599 			other!=NULL ) {
3600 		    if ( (otl->lookup_flags&pst_r2l) &&
3601 			    (pst->u.pair.vr[0].h_adv_off==0 && pst->u.pair.vr[0].v_adv_off==0 )) {
3602 			kp = chunkalloc(sizeof(KernPair));
3603 			kp->off = pst->u.pair.vr[1].h_adv_off;
3604 #ifdef FONTFORGE_CONFIG_DEVICETABLES
3605 			if ( pst->u.pair.vr[1].adjust!=NULL )
3606 			    KPFillDevTab(kp,&pst->u.pair.vr[1].adjust->xadv);
3607 #endif
3608 		    } else if ( !(otl->lookup_flags&pst_r2l) &&
3609 			    (pst->u.pair.vr[1].h_adv_off==0 && pst->u.pair.vr[0].v_adv_off==0 )) {
3610 			kp = chunkalloc(sizeof(KernPair));
3611 			kp->off = pst->u.pair.vr[0].h_adv_off;
3612 #ifdef FONTFORGE_CONFIG_DEVICETABLES
3613 			if ( pst->u.pair.vr[0].adjust!=NULL )
3614 			    KPFillDevTab(kp,&pst->u.pair.vr[0].adjust->xadv);
3615 #endif
3616 		    } else if ( (pst->u.pair.vr[0].h_adv_off==0 && pst->u.pair.vr[1].h_adv_off==0 )) {
3617 			vkern = sub->vertical_kerning = true;
3618 			kp = chunkalloc(sizeof(KernPair));
3619 			kp->off = pst->u.pair.vr[0].v_adv_off;
3620 #ifdef FONTFORGE_CONFIG_DEVICETABLES
3621 			if ( pst->u.pair.vr[0].adjust!=NULL )
3622 			    KPFillDevTab(kp,&pst->u.pair.vr[0].adjust->yadv);
3623 #endif
3624 		    }
3625 		}
3626 		if ( kp!=NULL ) {
3627 		    kp->sc = other;
3628 		    kp->subtable = sub;
3629 		    if ( vkern ) {
3630 			kp->next = sc->vkerns;
3631 			sc->vkerns = kp;
3632 		    } else {
3633 			kp->next = sc->kerns;
3634 			sc->kerns = kp;
3635 		    }
3636 		    PSTFree(pst);
3637 		} else {
3638 		    pst->subtable = sub;
3639 		    pst->next = sc->possub;
3640 		    sc->possub = pst;
3641 		}
3642 	    } else if ( l->type == ft_pstclass ) {
3643 		lefts.classes[kcnt] = copy(fea_canonicalClassOrder(l->u1.class));
3644 		rights.classes[kcnt++] = copy(fea_canonicalClassOrder(l->u2.pst->u.pair.paired));
3645 	    }
3646 	    l = l->lookup_next;
3647 	}
3648 	if ( kcnt!=0 ) {
3649 	    lefts.cnt = rights.cnt = kcnt;
3650 	    fea_canonicalClassSet(&lefts);
3651 	    fea_canonicalClassSet(&rights);
3652 
3653 	    sub = chunkalloc(sizeof(struct lookup_subtable));
3654 	    sub->lookup = otl;
3655 	    if ( lastsub==NULL )
3656 		otl->subtables = sub;
3657 	    else
3658 		lastsub->next = sub;
3659 	    lastsub = sub;
3660 
3661 	    if ( sub->kc!=NULL )
3662 		SFKernClassRemoveFree(tok->sf,sub->kc);
3663 	    sub->kc = kc = chunkalloc(sizeof(KernClass));
3664 	    kc->first_cnt = lefts.cnt+1; kc->second_cnt = rights.cnt+1;
3665 	    kc->firsts = galloc(kc->first_cnt*sizeof(char *));
3666 	    kc->seconds = galloc(kc->second_cnt*sizeof(char *));
3667 	    kc->firsts[0] = kc->seconds[0] = NULL;
3668 	    for ( i=0; i<lefts.cnt; ++i )
3669 		kc->firsts[i+1] = lefts.classes[i];
3670 	    for ( i=0; i<rights.cnt; ++i )
3671 		kc->seconds[i+1] = rights.classes[i];
3672 	    kc->subtable = sub;
3673 	    kc->offsets = gcalloc(kc->first_cnt*kc->second_cnt,sizeof(int16));
3674 #ifdef FONTFORGE_CONFIG_DEVICETABLES
3675 	    kc->adjusts = gcalloc(kc->first_cnt*kc->second_cnt,sizeof(DeviceTable));
3676 #endif
3677 	    fea_fillKernClass(kc,first);
3678 	    if ( sub->vertical_kerning ) {
3679 		kc->next = tok->sf->vkerns;
3680 		tok->sf->vkerns = kc;
3681 	    } else {
3682 		kc->next = tok->sf->kerns;
3683 		tok->sf->kerns = kc;
3684 	    }
3685 	}
3686 	sub = NULL;
3687 	while ( l!=NULL && l->type==ft_subtable )
3688 	    l = l->lookup_next;
3689     }
3690     if ( kmax!=0 ) {
3691 	free(lefts.classes);
3692 	free(rights.classes);
3693     }
3694 }
3695 
fea_ApplyLookupList(struct parseState * tok,struct feat_item * lookup_data,int lookup_flag)3696 static OTLookup *fea_ApplyLookupList(struct parseState *tok,
3697 	struct feat_item *lookup_data,int lookup_flag) {
3698     /* A lookup list might consist just of a lookup_ref so find the lookup named u1.lookup_name */
3699     /* A lookup_start is optional and provides the lookup name */
3700     /* A lookupflags is optional and may occur anywhere u2.lookupflags */
3701     /* An ap is for mark2 types for the mark u1.sc and u2.ap (not grouped in anchor classes yet) */
3702     /* A fpst is for contextuals u2.fpst (rule.lookups[i].lookup are lookup lists in their own rights that need to become lookups) */
3703     /* A subtable means a subtable break, make up a new name, ignore multiple subtable entries */
3704     /* A pst is for simple things u1.sc, u2.pst */
3705     /* A pstclass is for kerning classes u1.class, u2.pst (paired may be a class list too) */
3706     /* An ap is for cursive types for the u1.sc and u2.ap (an entry and an exit ap) */
3707     /* An ap is for mark2 types for the base u1.sc and u2.ap and mark_class */
3708     OTLookup *otl;
3709     int kcnt, mcnt;
3710     struct feat_item *l;
3711     enum otlookup_type temp;
3712 
3713     if ( lookup_data->type == ft_lookup_ref ) {
3714 	for ( otl=tok->created; otl!=NULL; otl=otl->next )
3715 	    if ( otl->lookup_name!=NULL &&
3716 		    strcmp(otl->lookup_name,lookup_data->u1.lookup_name)==0)
3717 return( otl );
3718 	otl = SFFindLookup(tok->sf,lookup_data->u1.lookup_name);
3719 	if ( otl==NULL )
3720 	    LogError( _("No lookup named %s"),lookup_data->u1.lookup_name );
3721 	    /* Can't give a line number, this is second pass */
3722 return( otl );
3723     }
3724 
3725     otl = chunkalloc(sizeof(OTLookup));
3726     otl->lookup_flags = lookup_flag;
3727     otl->lookup_type = ot_undef;
3728     if ( tok->last==NULL )
3729 	tok->created = otl;
3730     else
3731 	tok->last->next = otl;
3732     tok->last = otl;
3733 
3734     /* Search first for class counts */
3735     kcnt = mcnt = 0;
3736     for ( l = lookup_data; l!=NULL; l=l->lookup_next ) {
3737 	if ( l->type == ft_ap && l->mark_class!=NULL )
3738 	    ++mcnt;
3739 	else if ( l->type == ft_pstclass )
3740 	    ++kcnt;
3741 	else if ( l->type == ft_lookupflags )
3742 	    otl->lookup_flags = l->u2.lookupflags;
3743 	else if ( l->type == ft_lookup_start ) {
3744 	    otl->lookup_name = l->u1.lookup_name;
3745 	    l->u1.lookup_name = NULL;			/* So we don't free it later */
3746 	}
3747 	temp = fea_LookupTypeFromItem(l);
3748 	if ( temp==ot_undef )
3749 	    /* Tum ty tum tum. No information */;
3750 	else if ( otl->lookup_type == ot_undef )
3751 	    otl->lookup_type = temp;
3752 	else if ( otl->lookup_type != temp )
3753 	    IError(_("Mismatch lookup types inside a parsed lookup"));
3754     }
3755     if ( otl->lookup_type==gpos_mark2base ||
3756 	    otl->lookup_type==gpos_mark2ligature ||
3757 	    otl->lookup_type==gpos_mark2mark )
3758 	fea_ApplyLookupListMark2(tok,lookup_data,mcnt,otl);
3759     else if ( mcnt!=0 )
3760 	IError(_("Mark anchors provided when nothing can use them"));
3761     else if ( otl->lookup_type==gpos_cursive )
3762 	fea_ApplyLookupListCursive(tok,lookup_data,otl);
3763     else if ( otl->lookup_type==gpos_pair )
3764 	fea_ApplyLookupListPair(tok,lookup_data,kcnt,otl);
3765     else if ( otl->lookup_type==gpos_contextchain ||
3766 	    otl->lookup_type==gsub_contextchain )
3767 	fea_ApplyLookupListContextual(tok,lookup_data,otl);
3768     else
3769 	fea_ApplyLookupListPST(tok,lookup_data,otl);
3770 return( otl );
3771 }
3772 
fea_NameID2OTFName(struct nameid * names)3773 static struct otfname *fea_NameID2OTFName(struct nameid *names) {
3774     struct otfname *head=NULL, *cur;
3775 
3776     while ( names!=NULL ) {
3777 	cur = chunkalloc(sizeof(struct otfname));
3778 	cur->lang = names->language;
3779 	cur->name = names->utf8_str;
3780 	names->utf8_str = NULL;
3781 	cur->next = head;
3782 	head = cur;
3783 	names = names->next;
3784     }
3785 return( head );
3786 }
3787 
fea_AttachFeatureToLookup(OTLookup * otl,uint32 feat_tag,struct scriptlanglist * sl)3788 static void fea_AttachFeatureToLookup(OTLookup *otl,uint32 feat_tag,
3789 	struct scriptlanglist *sl) {
3790     FeatureScriptLangList *fl;
3791 
3792     if ( otl==NULL )
3793 return;
3794 
3795     for ( fl = otl->features; fl!=NULL && fl->featuretag!=feat_tag; fl=fl->next );
3796     if ( fl==NULL ) {
3797 	fl = chunkalloc(sizeof(FeatureScriptLangList));
3798 	fl->next = otl->features;
3799 	otl->features = fl;
3800 	fl->featuretag = feat_tag;
3801 	fl->scripts = SListCopy(sl);
3802     } else
3803 	SLMerge(fl,sl);
3804 }
3805 
fea_NameID2NameTable(SplineFont * sf,struct nameid * names)3806 static void fea_NameID2NameTable(SplineFont *sf, struct nameid *names) {
3807     struct ttflangname *cur;
3808 
3809     while ( names!=NULL ) {
3810 	for ( cur = sf->names; cur!=NULL && cur->lang!=names->language; cur=cur->next );
3811 	if ( cur==NULL ) {
3812 	    cur = chunkalloc(sizeof(struct ttflangname));
3813 	    cur->lang = names->language;
3814 	    cur->next = sf->names;
3815 	    sf->names = cur;
3816 	}
3817 	free(cur->names[names->strid]);
3818 	cur->names[names->strid] = names->utf8_str;
3819 	names->utf8_str = NULL;
3820 	names = names->next;
3821     }
3822 }
3823 
fea_TableByKeywords(SplineFont * sf,struct feat_item * f)3824 static void fea_TableByKeywords(SplineFont *sf, struct feat_item *f) {
3825     struct tablevalues *tv;
3826     struct tablekeywords *offsets = f->u1.offsets, *cur;
3827     int i;
3828 
3829     if ( !sf->pfminfo.pfmset ) {
3830 	SFDefaultOS2Info(&sf->pfminfo,sf,sf->fontname);
3831 	sf->pfminfo.pfmset = sf->pfminfo.subsuper_set = sf->pfminfo.panose_set =
3832 	    sf->pfminfo.hheadset = sf->pfminfo.vheadset = true;
3833     }
3834     for ( tv = f->u2.tvals; tv!=NULL; tv=tv->next ) {
3835 	cur = &offsets[tv->index];
3836 	if ( cur->offset==-1 )
3837 	    /* We don't support this guy, whatever he may be, but we did parse it */;
3838 	else if ( cur->cnt==1 ) {
3839 	    if ( cur->size==4 )
3840 		*((uint32 *) (((uint8 *) sf) + cur->offset)) = tv->value;
3841 	    else if ( cur->size==2 )
3842 		*((uint16 *) (((uint8 *) sf) + cur->offset)) = tv->value;
3843 	    else
3844 		*((uint8 *) (((uint8 *) sf) + cur->offset)) = tv->value;
3845 	    if ( strcmp(cur->name,"Ascender")==0 )
3846 		sf->pfminfo.hheadascent_add = false;
3847 	    else if ( strcmp(cur->name,"Descender")==0 )
3848 		sf->pfminfo.hheaddescent_add = false;
3849 	    else if ( strcmp(cur->name,"winAscent")==0 )
3850 		sf->pfminfo.winascent_add = false;
3851 	    else if ( strcmp(cur->name,"winDescent")==0 )
3852 		sf->pfminfo.windescent_add = false;
3853 	    else if ( strcmp(cur->name,"TypoAscender")==0 )
3854 		sf->pfminfo.typoascent_add = false;
3855 	    else if ( strcmp(cur->name,"TypoDescender")==0 )
3856 		sf->pfminfo.typodescent_add = false;
3857 	} else if ( cur->cnt==10 && cur->size==1 ) {
3858 	    for ( i=0; i<10; ++i )
3859 		(((uint8 *) sf) + cur->offset)[i] = tv->panose_vals[i];
3860 	}
3861     }
3862 }
3863 
fea_GDefGlyphClasses(SplineFont * sf,struct feat_item * f)3864 static void fea_GDefGlyphClasses(SplineFont *sf, struct feat_item *f) {
3865     int i, ch;
3866     char *pt, *start;
3867     SplineChar *sc;
3868 
3869     for ( i=0; i<4; ++i ) if ( f->u1.gdef_classes[i]!=NULL ) {
3870 	for ( pt=f->u1.gdef_classes[i]; ; ) {
3871 	    while ( *pt==' ' ) ++pt;
3872 	    if ( *pt=='\0' )
3873 	break;
3874 	    for ( start = pt; *pt!=' ' && *pt!='\0'; ++pt );
3875 	    ch = *pt; *pt = '\0';
3876 	    sc = SFGetChar(sf,-1,start);
3877 	    *pt = ch;
3878 	    if ( sc!=NULL )
3879 		sc->glyph_class = i+1;
3880 	}
3881     }
3882 }
3883 
fea_GDefLigCarets(SplineFont * sf,struct feat_item * f)3884 static void fea_GDefLigCarets(SplineFont *sf, struct feat_item *f) {
3885     int i, ch;
3886     char *pt, *start;
3887     SplineChar *sc;
3888     PST *pst, *prev, *next;
3889 
3890     for ( pt=f->u1.class; ; ) {
3891 	while ( *pt==' ' ) ++pt;
3892 	if ( *pt=='\0' )
3893     break;
3894 	for ( start = pt; *pt!=' ' && *pt!='\0'; ++pt );
3895 	ch = *pt; *pt = '\0';
3896 	sc = SFGetChar(sf,-1,start);
3897 	*pt = ch;
3898 	if ( sc!=NULL ) {
3899 	    for ( prev=NULL, pst=sc->possub; pst!=NULL; pst=next ) {
3900 		next = pst->next;
3901 		if ( pst->type!=pst_lcaret )
3902 		    prev = pst;
3903 		else {
3904 		    if ( prev==NULL )
3905 			sc->possub = next;
3906 		    else
3907 			prev->next = next;
3908 		    pst->next = NULL;
3909 		    PSTFree(pst);
3910 		}
3911 	    }
3912 	    for ( i=0; f->u2.lcaret[i]!=0; ++i );
3913 	    pst = chunkalloc(sizeof(PST));
3914 	    pst->next = sc->possub;
3915 	    sc->possub = pst;
3916 	    pst->type = pst_lcaret;
3917 	    pst->u.lcaret.cnt = i;
3918 	    pst->u.lcaret.carets = f->u2.lcaret;
3919 	    f->u2.lcaret = NULL;
3920 	}
3921     }
3922 }
3923 
fea_ApplyFeatureList(struct parseState * tok,struct feat_item * feat_data)3924 static struct feat_item *fea_ApplyFeatureList(struct parseState *tok,
3925 	struct feat_item *feat_data) {
3926     int lookup_flags = 0;
3927     uint32 feature_tag = feat_data->u1.tag;
3928     struct scriptlanglist *sl = feat_data->u2.sl;
3929     struct feat_item *f, *start;
3930     OTLookup *otl;
3931     int saw_script = false;
3932     enum otlookup_type ltype;
3933 
3934     feat_data->u2.sl = NULL;
3935 
3936     for ( f=feat_data->next; f!=NULL && f->type!=ft_feat_end ; ) {
3937 	if ( f->ticked ) {
3938 	    f = f->next;
3939     continue;
3940 	}
3941 	switch ( f->type ) {
3942 	  case ft_lookupflags:
3943 	    lookup_flags = f->u2.lookupflags;
3944 	    f = f->next;
3945     continue;
3946 	  case ft_lookup_ref:
3947 	    otl = fea_ApplyLookupList(tok,f,lookup_flags);
3948 	    fea_AttachFeatureToLookup(otl,feature_tag,sl);
3949 	    f = f->next;
3950     continue;
3951 	  case ft_lookup_start:
3952 	    start = f;
3953 	    start->lookup_next = f->next;
3954 	    f = fea_SetLookupLink(start->next,ot_undef);
3955 	    if ( f!=NULL && f->type == ft_lookup_end )
3956 		f = f->next;
3957 	    otl = fea_ApplyLookupList(tok,start,lookup_flags);
3958 	    fea_AttachFeatureToLookup(otl,feature_tag,sl);
3959     continue;
3960 	  case ft_script:
3961 	    ScriptLangListFree(sl);
3962 	    sl = chunkalloc(sizeof(struct scriptlanglist));
3963 	    sl->script = f->u1.tag;
3964 	    sl->lang_cnt = 1;
3965 	    sl->langs[0] = DEFAULT_LANG;
3966 	    saw_script = true;
3967 	    f = f->next;
3968     continue;
3969 	  case ft_lang:
3970 	    if ( !saw_script ) {
3971 		ScriptLangListFree(sl);
3972 		sl = chunkalloc(sizeof(struct scriptlanglist));
3973 		sl->script = CHR('l','a','t','n');
3974 	    }
3975 	    sl->langs[0] = f->u1.tag;
3976 	    sl->lang_cnt = 1;
3977 	    if ( !f->u2.exclude_dflt ) {
3978 		if ( sl->langs[0]!=DEFAULT_LANG ) {
3979 		    sl->langs[1] = DEFAULT_LANG;
3980 		    sl->lang_cnt = 2;
3981 		}
3982 	    }
3983 	    f = f->next;
3984     continue;
3985 	  case ft_langsys:
3986 	    ScriptLangListFree(sl);
3987 	    saw_script = false;
3988 	    sl = f->u2.sl;
3989 	    f->u2.sl = NULL;
3990 	    f = f->next;
3991     continue;
3992 	  case ft_sizeparams:
3993 	    if ( f->u1.params!=NULL ) {
3994 		tok->sf->design_size = f->u1.params[0];
3995 		tok->sf->fontstyle_id = f->u1.params[1];
3996 		tok->sf->design_range_bottom = f->u1.params[2];
3997 		tok->sf->design_range_top = f->u1.params[3];
3998 	    }
3999 	    OtfNameListFree(tok->sf->fontstyle_name);
4000 	    tok->sf->fontstyle_name = fea_NameID2OTFName(f->u2.names);
4001 	    f = f->next;
4002     continue;
4003 	  case ft_subtable:
4004 	    f = f->next;
4005     continue;
4006 	  case ft_pst:
4007 	  case ft_pstclass:
4008 	  case ft_ap:
4009 	  case ft_fpst:
4010 	    if ( f->type==ft_ap && f->u2.ap->type==at_mark ) {
4011 		struct feat_item *n, *a;
4012 		/* skip over the marks */
4013 		for ( n=f; n!=NULL && n->type == ft_ap && n->u2.ap->type==at_mark; n=n->next );
4014 		/* find the next thing which can use those marks (might not be anything) */
4015 		for ( a=n; a!=NULL; a=a->next ) {
4016 		    if ( a->ticked )
4017 		continue;
4018 		    if ( fea_FeatItemEndsLookup(a->type) ||
4019 			    a->type==ft_subtable || a->type==ft_ap )
4020 		break;
4021 		}
4022 		if ( a==NULL || fea_FeatItemEndsLookup(a->type) || a->type==ft_subtable ||
4023 			(a->type==ft_ap && a->u2.ap->type == at_mark )) {
4024 		    /* There's nothing else that can use these marks so we are */
4025 		    /*  done with them. Skip over all of them */
4026 		    f = n;
4027     continue;
4028 		}
4029 		ltype = fea_LookupTypeFromItem(a);
4030 	    } else
4031 		ltype = fea_LookupTypeFromItem(f);
4032 	    start = f;
4033 	    f = fea_SetLookupLink(start,ltype);
4034 	    otl = fea_ApplyLookupList(tok,start,lookup_flags);
4035 	    fea_AttachFeatureToLookup(otl,feature_tag,sl);
4036     continue;
4037 	  default:
4038 	    IError("Unexpected feature item in feature definition %d", f->type );
4039 	    f = f->next;
4040 	}
4041     }
4042     if ( f!=NULL && f->type == ft_feat_end )
4043 	f = f->next;
4044 return( f );
4045 }
4046 
fea_ApplyFile(struct parseState * tok,struct feat_item * item)4047 static void fea_ApplyFile(struct parseState *tok, struct feat_item *item) {
4048     struct feat_item *f, *start;
4049 
4050     for ( f=item; f!=NULL ; ) {
4051 	switch ( f->type ) {
4052 	  case ft_lookup_start:
4053 	    start = f;
4054 	    start->lookup_next = f->next;
4055 	    f = fea_SetLookupLink(start->next,ot_undef);
4056 	    if ( f!=NULL && f->type == ft_lookup_end )
4057 		f = f->next;
4058 	    fea_ApplyLookupList(tok,start,0);
4059     continue;
4060 	  case ft_feat_start:
4061 	    f = fea_ApplyFeatureList(tok,f);
4062     continue;
4063 	  case ft_table:
4064 	    /* I store things all mushed together, so this tag is useless to me*/
4065 	    /*  ignore it. The stuff inside the table matters though... */
4066 	    f = f->next;
4067     continue;
4068 	  case ft_names:
4069 	    fea_NameID2NameTable(tok->sf,f->u2.names);
4070 	    f = f->next;
4071     continue;
4072 	  case ft_tablekeys:
4073 	    fea_TableByKeywords(tok->sf,f);
4074 	    f = f->next;
4075     continue;
4076 	  case ft_gdefclasses:
4077 	    fea_GDefGlyphClasses(tok->sf,f);
4078 	    f = f->next;
4079     continue;
4080 	  case ft_lcaret:
4081 	    fea_GDefLigCarets(tok->sf,f);
4082 	    f = f->next;
4083     continue;
4084 	  default:
4085 	    IError("Unexpected feature item in feature file %d", f->type );
4086 	    f = f->next;
4087 	}
4088     }
4089 }
4090 
fea_reverseList(struct feat_item * f)4091 static struct feat_item *fea_reverseList(struct feat_item *f) {
4092     struct feat_item *n = NULL, *p = NULL;
4093 
4094     p = NULL;
4095     while ( f!=NULL ) {
4096 	n = f->next;
4097 	f->next = p;
4098 	p = f;
4099 	f = n;
4100     }
4101 return( p );
4102 }
4103 
fea_NameLookups(struct parseState * tok)4104 static void fea_NameLookups(struct parseState *tok) {
4105     SplineFont *sf = tok->sf;
4106     OTLookup *gpos_last=NULL, *gsub_last=NULL, *otl, *otlnext;
4107     int gp_cnt=0, gs_cnt=0, acnt;
4108     AnchorClass *ac, *acnext, *an;
4109 
4110     for ( otl = sf->gpos_lookups; otl!=NULL; otl=otl->next ) {
4111 	otl->lookup_index = gp_cnt++;
4112 	gpos_last = otl;
4113     }
4114     for ( otl = sf->gsub_lookups; otl!=NULL; otl=otl->next ) {
4115 	otl->lookup_index = gs_cnt++;
4116 	gsub_last = otl;
4117     }
4118 
4119     for ( otl = tok->created; otl!=NULL; otl=otlnext ) {
4120 	otlnext = otl->next;
4121 	otl->next = NULL;
4122 	if ( otl->lookup_name!=NULL && SFFindLookup(sf,otl->lookup_name)!=NULL ) {
4123 	    int cnt=0;
4124 	    char *namebuf = galloc(strlen( otl->lookup_name )+8 );
4125 	    /* Name already in use, modify it */
4126 	    do {
4127 		sprintf(namebuf,"%s-%d", otl->lookup_name, cnt++ );
4128 	    } while ( SFFindLookup(sf,namebuf)!=NULL );
4129 	    free(otl->lookup_name);
4130 	    otl->lookup_name = namebuf;
4131 	}
4132 	if ( otl->lookup_type < gpos_start ) {
4133 	    if ( gsub_last==NULL )
4134 		sf->gsub_lookups = otl;
4135 	    else
4136 		gsub_last->next = otl;
4137 	    gsub_last = otl;
4138 	    otl->lookup_index = gs_cnt++;
4139 	} else {
4140 	    if ( gpos_last==NULL )
4141 		sf->gpos_lookups = otl;
4142 	    else
4143 		gpos_last->next = otl;
4144 	    gpos_last = otl;
4145 	    otl->lookup_index = gp_cnt++;
4146 	}
4147 	NameOTLookup(otl,sf);		/* But only if it has no name */
4148     }
4149 
4150     /* Now name and attach any unnamed anchor classes (order here doesn't matter) */
4151     acnt = 0;
4152     for ( ac=tok->accreated; ac!=NULL; ac=acnext ) {
4153 	acnext = ac->next;
4154 	if ( ac->name==NULL ) {
4155 	    char buf[50];
4156 	    do {
4157 		snprintf(buf,sizeof(buf),_("Anchor-%d"), acnt++ );
4158 		for ( an=sf->anchor; an!=NULL && strcmp(an->name,buf)!=0; an=an->next );
4159 	    } while ( an!=NULL );
4160 	    ac->name = copy(buf);
4161 	}
4162 	ac->next = sf->anchor;
4163 	sf->anchor = ac;
4164     }
4165 
4166     sf->changed = true;
4167     FVSetTitles(sf);
4168     FVRefreshAll(sf);
4169 }
4170 
SFApplyFeatureFile(SplineFont * sf,FILE * file,char * filename)4171 void SFApplyFeatureFile(SplineFont *sf,FILE *file,char *filename) {
4172     struct parseState tok;
4173     struct glyphclasses *gc, *gcnext;
4174 
4175     memset(&tok,0,sizeof(tok));
4176     tok.line[0] = 1;
4177     tok.filename[0] = filename;
4178     tok.inlist[0] = file;
4179     tok.base = 10;
4180     if ( sf->cidmaster ) sf = sf->cidmaster;
4181     tok.sf = sf;
4182 
4183     fea_ParseFeatureFile(&tok);
4184     if ( tok.err_count==0 ) {
4185 	tok.sofar = fea_reverseList(tok.sofar);
4186 	fea_ApplyFile(&tok, tok.sofar);
4187 	fea_NameLookups(&tok);
4188     } else
4189 	ff_post_error("Not applied","There were errors when parsing the feature file and the features have not been applied");
4190     fea_featitemFree(tok.sofar);
4191     ScriptLangListFree(tok.def_langsyses);
4192     for ( gc = tok.classes; gc!=NULL; gc=gcnext ) {
4193 	gcnext = gc->next;
4194 	free(gc->classname); free(gc->glyphs);
4195 	chunkfree(gc,sizeof(struct glyphclasses));
4196     }
4197 }
4198 
SFApplyFeatureFilename(SplineFont * sf,char * filename)4199 void SFApplyFeatureFilename(SplineFont *sf,char *filename) {
4200     FILE *in = fopen(filename,"r");
4201 
4202     if ( in==NULL ) {
4203 	ff_post_error(_("Cannot open file"),_("Cannot open feature file %.120s"), filename );
4204 return;
4205     }
4206     SFApplyFeatureFile(sf,in,filename);
4207     fclose(in);
4208 }
4209