w32tex
About: TeX Live provides a comprehensive TeX system including all the major TeX-related programs, macro packages, and fonts that are free software. Windows sources.
  Fossies Dox: w32tex-src.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

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