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