1 /* Copyright (C) 2000-2008 by George Williams */
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"
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 */
45 /* ************************************************************************** */
46 /* ******************************* Parse feat ******************************* */
47 /* ************************************************************************** */
53 uint16 platform
, specific
, language
;
58 struct tablekeywords
{
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 */
66 int index
; /* in the table key structure above */
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
,
75 ft_subtable
, ft_script
, ft_lang
, ft_lookupflags
, ft_langsys
,
76 ft_pst
, ft_pstclass
, ft_fpst
, ft_ap
, ft_lookup_ref
};
78 uint16
/* enum feat_type */ type
;
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
;
91 /* For kerning by class we'll generate an invalid pst with the class as the "paired" field */
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
;
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) {
114 char *pt
, **names
, *cpt
;
115 char *temp
= copy(class);
118 for ( pt
= class; ; ) {
119 while ( *pt
==' ' ) ++pt
;
122 for ( ; *pt
!=' ' && *pt
!='\0'; ++pt
);
126 names
= galloc(name_cnt
*sizeof(char *));
128 for ( pt
= temp
; ; ) {
129 while ( *pt
==' ' ) ++pt
;
132 for ( names
[name_cnt
++]=pt
; *pt
!=' ' && *pt
!='\0'; ++pt
);
137 qsort(names
,name_cnt
,sizeof(char *),strcmpD
);
139 for ( i
=0; i
<name_cnt
; ++i
) {
140 strcpy(cpt
,names
[i
]);
152 static int fea_classesIntersect(char *class1
, char *class2
) {
153 char *pt1
, *start1
, *pt2
, *start2
;
156 for ( pt1
=class1
; ; ) {
157 while ( *pt1
==' ' ) ++pt1
;
160 for ( start1
= pt1
; *pt1
!=' ' && *pt1
!='\0'; ++pt1
);
161 ch1
= *pt1
; *pt1
= '\0';
162 for ( pt2
=class2
; ; ) {
163 while ( *pt2
==' ' ) ++pt2
;
166 for ( start2
= pt2
; *pt2
!=' ' && *pt2
!='\0'; ++pt2
);
167 ch2
= *pt2
; *pt2
= '\0';
168 if ( strcmp(start1
,start2
)==0 ) {
169 *pt2
= ch2
; *pt1
= ch1
;
179 #define SKIP_SPACES(s, i) \
181 while ((s)[i] == ' ') \
186 #define FIND_SPACE(s, i) \
188 while ((s)[i] != ' ' && (s)[i] != '\0') \
194 static char *fea_classesSplit(char *class1
, char *class2
) {
196 int len
= strlen(class1
), len2
= strlen(class2
);
198 int i
, j
, i_end
, j_end
;
202 if ( len2
>len
) len
= len2
;
203 intersection
= galloc(len
+1);
207 SKIP_SPACES(class1
, i
);
208 while (class1
[i
] != '\0') {
210 FIND_SPACE(class1
, i_end
);
216 SKIP_SPACES(class2
, j
);
217 while (!match_found
&& class2
[j
] != '\0') {
219 FIND_SPACE(class2
, j_end
);
221 if (length
== j_end
- j
&& strncmp(class1
+ i
, class2
+ j
, length
) == 0) {
225 intersection
[ix
] = ' ';
228 memcpy(intersection
+ ix
, class1
+ i
, length
* sizeof (char));
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));
237 SKIP_SPACES(class2
, j
);
242 SKIP_SPACES(class1
, i
);
245 intersection
[ix
] = '\0';
246 return( intersection
);
254 enum toktype
{ tk_name
, tk_class
, tk_int
, tk_char
, tk_cid
, tk_eof
,
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
271 char *filename
[MAXI
];
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;
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
{
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
},
295 { "anchor", tk_anchor
},
296 { "anonymous", tk_anonymous
},
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
},
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
},
318 { "nameid", tk_nameid
},
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
},
330 { "sub", tk_substitute
},
331 { "pos", tk_position
},
332 { "enum", tk_enumerate
},
333 { "anon", tk_anonymous
},
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
) },
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
) },
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
) },
371 static void fea_ParseTok(struct parseState
*tok
);
373 static void fea_handle_include(struct parseState
*tok
) {
375 char namebuf
[1025], *pt
, *filename
;
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
] );
385 in
= tok
->inlist
[tok
->inc_depth
];
390 while ( ch
!=EOF
&& ch
!=')' && pt
<namebuf
+sizeof(namebuf
)-1 ) {
394 if ( ch
!=EOF
&& ch
!=')' ) {
395 while ( ch
!=EOF
&& ch
!=')' )
397 LogError(_("Include filename too long on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
400 while ( pt
>=namebuf
+1 && isspace(pt
[-1]) )
405 LogError(_("End of file in include on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
407 LogError(_("Missing close parenthesis in include on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
413 LogError(_("No filename specified in include on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
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
] );
424 if ( *namebuf
=='/' ||
425 ( pt
= strrchr(tok
->filename
[tok
->inc_depth
],'/') )==NULL
)
426 filename
=copy(namebuf
);
429 filename
= GFileAppendFile(tok
->filename
[tok
->inc_depth
],namebuf
,false);
432 in
= fopen(filename
,"r");
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
] );
442 tok
->filename
[tok
->inc_depth
] = filename
;
443 tok
->inlist
[tok
->inc_depth
] = in
;
444 tok
->line
[tok
->inc_depth
] = 1;
448 static void fea_ParseTok(struct parseState
*tok
) {
449 FILE *in
= tok
->inlist
[tok
->inc_depth
];
453 if ( tok
->backedup
) {
454 tok
->backedup
= false;
460 while ( isspace(ch
) || ch
=='#' ) {
462 while ( (ch
=getc(in
))!=EOF
&& ch
!='\n' && ch
!='\r' );
463 if ( ch
=='\n' || ch
=='\r' ) {
469 ++tok
->line
[tok
->inc_depth
];
474 tok
->could_be_tag
= 0;
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
;
483 strcpy(tok
->tokbuf
,"EOF");
487 start
= pt
= tok
->tokbuf
;
488 if ( ch
=='\\' || ch
=='-' ) {
493 if ( isdigit(ch
) || ch
=='+' || ((ch
=='-' || ch
=='\\') && isdigit(peekch
)) ) {
495 if ( ch
=='-' || ch
=='+' ) {
501 } else if ( ch
=='\\' ) {
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 ) {
512 LogError(_("Number too long on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
514 } else if ( pt
==start
) {
515 LogError(_("Missing number on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
520 tok
->value
= strtol(tok
->tokbuf
,NULL
,tok
->base
);
522 } else if ( ch
=='@' || ch
=='_' || ch
=='\\' || isalnum(ch
)) { /* Names can't start with dot */
523 int check_keywords
= true;
526 tok
->type
= tk_class
;
530 check_keywords
= false;
531 } else if ( ch
=='\\' ) {
533 check_keywords
= false;
535 while (( isalnum(ch
) || ch
=='_' || ch
=='.' ) && pt
<start
+31 ) {
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
] );
544 } else if ( pt
==start
) {
545 LogError(_("Missing name on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
549 if ( check_keywords
) {
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
;
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;
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];
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
==')' ) {
585 tok
->tokbuf
[1] = '\0';
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
] );
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 */
601 if ( tok
->type
==tk_name
&& tok
->could_be_tag
&&
602 tok
->tag
==CHR('O','S',' ',' ') ) {
603 FILE *in
= tok
->inlist
[tok
->inc_depth
];
609 tok
->tag
= CHR('O','S','/','2');
611 tok
->tag
= CHR('O','S','/',' ');
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) */
629 if ( tok
->type
==tk_int
) {
630 FILE *in
= tok
->inlist
[tok
->inc_depth
];
631 char *pt
= tok
->tokbuf
+ strlen(tok
->tokbuf
);
636 while ( (ch
= getc(in
))!=EOF
&& isdigit(ch
)) {
637 if ( pt
<tok
->tokbuf
+sizeof(tok
->tokbuf
)-1 )
641 tok
->value
= rint(strtod(tok
->tokbuf
,NULL
)*10.0);
646 LogError(_("Expected '%s' on line %d of %s"), fea_keywords
[tk_int
],
647 tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
651 return( tok
->value
);
654 static void fea_TokenMustBe(struct parseState
*tok
, enum toktype type
, int ch
) {
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
] );
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
] );
666 static void fea_skip_to_semi(struct parseState
*tok
) {
669 while ( tok
->type
!=tk_char
|| tok
->tokbuf
[0]!=';' || nest
>0 ) {
671 if ( tok
->type
==tk_char
) {
672 if ( tok
->tokbuf
[0]=='{' ) ++nest
;
673 else if ( tok
->tokbuf
[0]=='}' ) --nest
;
677 if ( tok
->type
==tk_eof
)
682 static void fea_skip_to_close_curly(struct parseState
*tok
) {
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 ) {
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
)
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
);
710 static void fea_end_statement(struct parseState
*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 )
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
] );
737 static void fea_AddClassDef(struct parseState
*tok
,char *classname
,char *contents
) {
738 struct glyphclasses
*test
;
740 test
= fea_lookup_class(tok
,classname
);
742 test
=chunkalloc(sizeof(struct glyphclasses
));
743 test
->classname
= classname
;
744 test
->next
= tok
->classes
;
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
);
762 if ( *_max
-cnt
<= len
+1 )
763 glyphs
= grealloc(glyphs
,(*_max
+=200+len
+1)+1);
765 strcpy(glyphs
+cnt
,contents
);
766 cnt
+= strlen(contents
);
773 static char *fea_cid_validate(struct parseState
*tok
,int cid
) {
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;
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
) {
797 /* Not defined, try to create it */
798 if ( maxsf
==NULL
) /* No subfonts */
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
) )
804 SFExpandGlyphCount(maxsf
,MaxCID(cidmap
));
806 if ( cid
>=maxsf
->glyphcnt
)
808 map
= EncMap1to1(maxsf
->glyphcnt
);
809 sc
= SFMakeChar(maxsf
,map
,cid
);
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
);
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
] );
827 if ( sc
!=NULL
|| strcmp(name
,"NULL")==0 )
829 enc
= SFFindSlot(sf
,sf
->fv
->map
,-1,name
);
831 sc
= SFMakeChar(sf
,sf
->fv
->map
,enc
);
835 sc
->name
= copy(name
);
840 for ( gid
=sf
->glyphcnt
-1; gid
>=0; --gid
) if ( (sc
=sf
->glyphs
[gid
])!=NULL
) {
841 if ( strcmp(sc
->name
,name
)==0 )
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
);
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
;
860 static char *fea_glyphname_validate(struct parseState
*tok
,char *name
) {
861 SplineChar
*sc
= fea_glyphname_get(tok
,name
);
866 return( copy( sc
->name
));
869 static char *fea_ParseGlyphClass(struct parseState
*tok
) {
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
] );
879 char *contents
= NULL
;
881 int last_val
= 0, range_type
, range_len
= 0;
882 char last_glyph
[MAXT
+1];
883 char *pt1
, *start1
, *pt2
, *start2
= NULL
;
888 if ( tok
->type
==tk_char
&& tok
->tokbuf
[0]==']' )
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]=='-' ) {
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
] );
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
) {
916 if ( strlen(last_glyph
)==strlen(tok
->tokbuf
) &&
917 strcmp(last_glyph
,tok
->tokbuf
)<0 ) {
919 for ( pt1
=last_glyph
, pt2
=tok
->tokbuf
;
920 *pt1
!='\0'; ++pt1
, ++pt2
) {
922 if ( start1
!=NULL
) {
926 start1
= pt1
; start2
= pt2
;
927 if ( !isdigit(*pt1
) || !isdigit(*pt2
))
930 for ( range_len
=0; range_len
<3 && isdigit(*pt1
) && isdigit(*pt2
);
931 ++range_len
, ++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
] );
941 } else if ( range_type
==1 || range_len
==1 ) {
942 /* Single letter changes */
943 v1
= *start1
; v2
= *start2
;
944 for ( ++v1
; v1
<=v2
; ++v1
) {
946 contents
= fea_glyphname_validate(tok
,start1
);
949 if ( contents
!=NULL
)
950 cnt
= fea_AddGlyphs(&glyphs
,&max
,cnt
,contents
);
953 v1
= strtol(start1
,NULL
,10);
954 v2
= strtol(start2
,NULL
,10);
955 for ( ++v1
; v1
<=v2
; ++v1
) {
957 sprintf( last_glyph
, "%.*s%02d%s", (int) (start2
-tok
->tokbuf
),
958 tok
->tokbuf
, v1
, start2
+2 );
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
);
965 if ( contents
!=NULL
)
966 cnt
= fea_AddGlyphs(&glyphs
,&max
,cnt
,contents
);
970 LogError(_("Unexpected token in glyph class range on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
972 if ( tok
->type
==tk_char
&& tok
->tokbuf
[0]==']' )
975 last_val
=-1; last_glyph
[0] = '\0';
976 } else if ( tok
->type
== tk_NULL
) {
977 contents
= copy("NULL");
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
] );
983 if ( contents
!=NULL
)
984 cnt
= fea_AddGlyphs(&glyphs
,&max
,cnt
,contents
);
987 glyphs
= copy(""); /* Is it legal to have an empty class? I can't think of any use for one */
992 static char *fea_ParseGlyphClassGuarded(struct parseState
*tok
) {
993 char *ret
= fea_ParseGlyphClass(tok
);
999 static void fea_ParseLookupFlags(struct parseState
*tok
) {
1001 struct feat_item
*item
;
1004 if ( tok
->type
==tk_int
) {
1006 fea_end_statement(tok
);
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
)
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
;
1019 if ( tok
->type
== tk_char
&& tok
->tokbuf
[0]==';' )
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
] );
1024 fea_skip_to_semi(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
] );
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
] );
1039 item
= chunkalloc(sizeof(struct feat_item
));
1040 item
->type
= ft_lookupflags
;
1041 item
->u2
.lookupflags
= val
;
1042 item
->next
= tok
->sofar
;
1046 static void fea_ParseGlyphClassDef(struct parseState
*tok
) {
1047 char *classname
= copy(tok
->tokbuf
);
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
] );
1054 fea_skip_to_semi(tok
);
1058 contents
= fea_ParseGlyphClass(tok
);
1059 if ( contents
==NULL
) {
1060 fea_skip_to_semi(tok
);
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
;
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
] );
1076 fea_skip_to_semi(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
] );
1085 fea_skip_to_semi(tok
);
1090 for ( sl
=tok
->def_langsyses
; sl
!=NULL
&& sl
->script
!=script
; sl
=sl
->next
);
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
)
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
;
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
;
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
;
1133 struct markedglyphs
*next
;
1136 #ifdef FONTFORGE_CONFIG_DEVICETABLES
1137 static void fea_ParseDeviceTable(struct parseState
*tok
,DeviceTable
*adjust
)
1139 static void fea_ParseDeviceTable(struct parseState
*tok
)
1143 #ifdef FONTFORGE_CONFIG_DEVICETABLES
1147 memset(values
,0,sizeof(values
));
1150 fea_TokenMustBe(tok
,tk_device
,'\0');
1151 if ( tok
->type
!=tk_device
)
1156 if ( first
&& tok
->type
==tk_NULL
) {
1157 fea_TokenMustBe(tok
,tk_char
,'>');
1159 } else if ( tok
->type
==tk_char
&& tok
->tokbuf
[0]=='>' ) {
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
] );
1165 #ifdef FONTFORGE_CONFIG_DEVICETABLES
1166 int pixel
= tok
->value
;
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
] );
1173 values
[pixel
] = tok
->value
;
1176 else if ( pixel
<min
) min
= pixel
;
1177 else if ( pixel
>max
) max
= pixel
;
1183 #ifdef FONTFORGE_CONFIG_DEVICETABLES
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
];
1195 static void fea_ParseCaret(struct parseState
*tok
) {
1198 fea_TokenMustBe(tok
,tk_caret
,'\0');
1199 if ( tok
->type
!=tk_caret
)
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
] );
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
] );
1215 static AnchorPoint
*fea_ParseAnchor(struct parseState
*tok
) {
1216 AnchorPoint
*ap
= NULL
;
1218 if ( tok
->type
==tk_anchor
) {
1220 if ( tok
->type
==tk_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
;
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
);
1238 fea_ParseDeviceTable(tok
);
1239 fea_TokenMustBe(tok
,tk_char
,'<');
1240 fea_ParseDeviceTable(tok
);
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
] );
1248 LogError(_("Expected integer in anchor on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
1252 LogError(_("Expected 'anchor' keyword in anchor on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
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 )
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;
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) */
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
] );
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
] );
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
;
1306 if ( tok
->type
==tk_char
&& tok
->tokbuf
[0]=='>' ) {
1308 vr
->v_adv_off
= vr
->xoff
;
1310 vr
->h_adv_off
= vr
->xoff
;
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
] );
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
;
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
);
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
);
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
] );
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
;
1360 if ( first
&& is_pos
&& tok
->type
== tk_cursive
)
1362 else if ( first
&& is_pos
&& tok
->type
== tk_mark
)
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
);
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)) {
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
));
1392 last
->vr
->v_adv_off
= tok
->value
;
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
,'>');
1417 if ( head
!=NULL
&& mark_cnt
!=0 )
1418 head
->has_marks
= true;
1419 fea_UnParseTok(tok
);
1423 static void fea_markedglyphsFree(struct markedglyphs
*gl
) {
1424 struct markedglyphs
*next
;
1427 while ( gl
!=NULL
) {
1429 free(gl
->name_or_class
);
1430 free(gl
->lookupname
);
1431 for ( i
=0; i
<gl
->ap_cnt
; ++i
)
1432 AnchorPointsFree(gl
->anchors
[i
]);
1434 if ( gl
->vr
!=NULL
) {
1435 #ifdef FONTFORGE_CONFIG_DEVICETABLES
1436 ValDevFree(gl
->vr
->adjust
);
1438 chunkfree(gl
->vr
,sizeof(struct vr
));
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
;
1449 struct feat_item
*item
;
1451 start
= glyphs
->name_or_class
;
1453 while ( *start
==' ' ) ++start
;
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
;
1462 strcpy(next
,temp
->name
);
1463 after
= next
+strlen(next
);
1464 if ( glyphs
->next
!=NULL
) {
1466 sofar
= fea_AddAllLigPosibilities(tok
,glyphs
->next
,sc
,sequence_start
,after
,sofar
);
1469 item
= chunkalloc(sizeof(struct feat_item
));
1470 item
->type
= ft_pst
;
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
;
1483 static struct markedglyphs
*fea_glyphs_to_names(struct markedglyphs
*glyphs
,
1484 int cnt
,char **to
) {
1485 struct markedglyphs
*g
;
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
);
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
;
1512 start
= glyphs
->name_or_class
;
1514 while ( *start
==' ' ) ++start
;
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
;
1522 item
= chunkalloc(sizeof(struct feat_item
));
1523 item
->type
= ft_pst
;
1527 item
->u2
.pst
= chunkalloc(sizeof(PST
));
1528 item
->u2
.pst
->type
= pst_position
;
1529 item
->u2
.pst
->u
.pos
= glyphs
->vr
[0];
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
;
1540 SplineChar
*sc
, *sc2
;
1542 memset(vr
,0,sizeof(vr
));
1543 if ( glyphs
->vr
==NULL
)
1544 vr
[0] = *glyphs
->next
->vr
;
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
;
1553 while ( *start
==' ' ) ++start
;
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
;
1561 start2
= glyphs
->next
->name_or_class
;
1563 while ( *start2
==' ' ) ++start2
;
1564 if ( *start2
=='\0' )
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
;
1571 item
= chunkalloc(sizeof(struct feat_item
));
1572 item
->type
= ft_pst
;
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
));
1586 item
= chunkalloc(sizeof(struct feat_item
));
1587 item
->type
= ft_pstclass
;
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
));
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
);
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
] );
1617 while ( *start
==' ' ) ++start
;
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
;
1625 item
= chunkalloc(sizeof(struct feat_item
));
1626 item
->type
= ft_pst
;
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
;
1640 while ( *start
==' ' ) ++start
;
1641 while ( *start2
==' ' ) ++start2
;
1642 if ( *start
=='\0' && *start2
=='\0' )
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
] );
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
)
1659 item
= chunkalloc(sizeof(struct feat_item
));
1660 item
->type
= ft_pst
;
1664 item
->u2
.pst
= chunkalloc(sizeof(PST
));
1665 item
->u2
.pst
->type
= pst_substitution
;
1666 item
->u2
.pst
->u
.subs
.variant
= copy(temp
->name
);
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
] );
1675 static struct feat_item
*fea_process_sub_ligature(struct parseState
*tok
,
1676 struct markedglyphs
*glyphs
, struct markedglyphs
*rpl
,
1677 struct feat_item
*sofar
) {
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
);
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
);
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;
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
) {
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 )
1715 /* if we found some unmarked glyphs between two runs of marked */
1716 /* they don't count as lookaheads */
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
;
1727 fpst
->rules
= r
= gcalloc(1,sizeof(struct fpst_rule
));
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
;
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
);
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
;
1758 item
->u2
.fpst
= fpst
;
1761 for ( g
=glyphs
; g
!=NULL
&& g
->mark_count
==0; g
=g
->next
);
1762 for ( i
=0; g
!=NULL
; ++i
) {
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
) &&
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);
1776 LogError(_("Unparseable contextual sequence on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
1779 r
->lookups
[i
].lookup
= (OTLookup
*) head
;
1780 cnt
= g
->mark_count
;
1781 while ( g
!=NULL
&& g
->mark_count
== cnt
) /* skip everything involved here */
1783 for ( ; g
!=NULL
&& g
->mark_count
==0; g
=g
->next
); /* skip any uninvolved glyphs */
1790 static void fea_ParseIgnore(struct parseState
*tok
) {
1791 struct markedglyphs
*glyphs
;
1794 /* ignore [pos|sub] <marked glyph sequence> (, <marked g sequence>)* */
1797 if ( tok
->type
==tk_position
)
1799 else if ( tok
->type
== tk_substitute
)
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
] );
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);
1811 fpst
->type
= pst_chainpos
;
1812 fea_markedglyphsFree(glyphs
);
1814 if ( tok
->type
!=tk_char
|| tok
->tokbuf
[0]!=',' )
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),
1835 struct feat_item
*item
, *head
;
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
] );
1842 } else if ( !glyphs
->has_marks
) {
1843 /* Non-contextual */
1844 if ( cnt
==1 && glyphs
->is_name
&& tok
->type
==tk_from
) {
1845 /* Alternate subs */
1848 alts
= fea_ParseGlyphClassGuarded(tok
);
1849 sc
= fea_glyphname_get(tok
,glyphs
->name_or_class
);
1851 item
= chunkalloc(sizeof(struct feat_item
));
1852 item
->type
= ft_pst
;
1853 item
->next
= tok
->sofar
;
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);
1863 LogError(_("No substitution specified on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
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
] );
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 */
1875 for ( g
=rpl
; g
!=NULL
; g
=g
->next
)
1876 len
+= strlen(g
->name_or_class
)+1;
1877 mult
= galloc(len
+1);
1879 for ( g
=rpl
; g
!=NULL
; g
=g
->next
) {
1880 strcpy(mult
+len
,g
->name_or_class
);
1881 len
+= strlen(g
->name_or_class
);
1885 sc
= fea_glyphname_get(tok
,glyphs
->name_or_class
);
1887 item
= chunkalloc(sizeof(struct feat_item
));
1888 item
->type
= ft_pst
;
1889 item
->next
= tok
->sofar
;
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
);
1900 LogError(_("Unparseable glyph sequence in substitution on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
1904 fea_markedglyphsFree(rpl
);
1906 LogError(_("Expected 'by' or 'from' keywords in substitution on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
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
] );
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
);
1929 LogError(_("Unparseable contextual sequence on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
1932 r
->lookups
[i
].lookup
= (OTLookup
*) head
;
1933 cnt
= g
->mark_count
;
1934 while ( g
!=NULL
&& g
->mark_count
== cnt
) /* skip everything involved here */
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
;
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
);
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
] );
1964 fea_skip_to_semi(tok
);
1967 if ( sc
==NULL
&& contents
==NULL
) {
1968 fea_skip_to_semi(tok
);
1972 fea_TokenMustBe(tok
,tk_char
,'<');
1973 fea_TokenMustBe(tok
,tk_anchor
,'\0');
1974 ap
= fea_ParseAnchor(tok
);
1976 fea_end_statement(tok
);
1981 struct feat_item
*item
= chunkalloc(sizeof(struct feat_item
));
1984 item
->next
= tok
->sofar
;
1987 if ( contents
==NULL
) {
1991 while ( *pt
!='\0' && *pt
!=' ' )
1993 ch
= *pt
; *pt
= '\0';
1994 sc
= fea_glyphname_get(tok
,start
);
1996 while ( isspace(*pt
)) ++pt
;
1998 tok
->sofar
= item
->next
; /* Oops, remove it */
1999 chunkfree(item
,sizeof(*item
));
2001 AnchorPointsFree(ap
);
2008 ap
= AnchorPointsCopy(ap
);
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
;
2031 struct feat_item
*item
;
2032 char *start
, *pt
, ch
;
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
] );
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
] );
2047 start
= glyphs
->name_or_class
;
2048 if ( glyphs
->anchors
[1]!=NULL
)
2049 glyphs
->anchors
[1]->type
= at_cexit
;
2051 while ( *start
==' ' ) ++start
;
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
;
2059 item
= chunkalloc(sizeof(struct feat_item
));
2061 item
->next
= tok
->sofar
;
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]);
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 */
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
] );
2086 if ( tok
->type
==tk_name
)
2087 mark_class
= copy(tok
->tokbuf
);
2089 mark_class
= fea_canonicalClassOrder(fea_ParseGlyphClassGuarded(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
] );
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 */;
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
;
2106 glyphs
->anchors
[i
]->type
= at_basechar
;
2108 head
= glyphs
->anchors
[i
];
2110 last
->next
= glyphs
->anchors
[i
];
2111 last
= glyphs
->anchors
[i
];
2115 start
= glyphs
->name_or_class
;
2117 while ( *start
==' ' ) ++start
;
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
;
2125 item
= chunkalloc(sizeof(struct feat_item
));
2127 item
->next
= tok
->sofar
;
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
) {
2142 LogError(_("Unparseable glyph sequence in position on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
2147 (void) fea_markedglyphs_to_fpst(tok
,glyphs
,true,false);
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
) {
2158 return( gpos_single
);
2160 return( gpos_pair
);
2161 case pst_substitution
:
2162 return( gsub_single
);
2164 return( gsub_alternate
);
2166 return( gsub_multiple
);
2168 return( gsub_ligature
);
2170 return( ot_undef
); /* Can't happen */
2174 switch( item
->u2
.ap
->type
) {
2175 case at_centry
: case at_cexit
:
2176 return( gpos_cursive
);
2178 return( ot_undef
); /* Can be used in three different lookups. Not enough info */
2180 return( gpos_mark2base
);
2182 return( gpos_mark2ligature
);
2184 return( gpos_mark2mark
);
2186 return( ot_undef
); /* Can't happen */
2190 switch( item
->u2
.fpst
->type
) {
2192 return( gpos_contextchain
);
2194 return( gsub_contextchain
);
2196 return( ot_undef
); /* Can't happen */
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
));
2210 item
->next
= tok
->sofar
;
2215 static int fea_LookupSwitch(struct parseState
*tok
) {
2218 switch ( tok
->type
) {
2220 fea_ParseGlyphClassDef(tok
);
2223 fea_ParseLookupFlags(tok
);
2226 fea_ParseMarks(tok
);
2229 fea_ParseIgnore(tok
);
2232 fea_TokenMustBe(tok
,tk_position
,'\0');
2236 fea_ParsePosition(tok
,enumer
);
2239 fea_ParseSubstitute(tok
);
2243 fea_AddFeatItem(tok
,ft_subtable
,0);
2244 fea_TokenMustBe(tok
,tk_char
,';');
2247 if ( tok
->tokbuf
[0]=='}' )
2256 static void fea_ParseLookupDef(struct parseState
*tok
, int could_be_stat
) {
2258 struct feat_item
*item
, *first_after_mark
;
2259 enum otlookup_type lookuptype
;
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
] );
2267 fea_skip_to_semi(tok
);
2270 lookup_name
= copy(tok
->tokbuf
);
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
;
2279 } else if ( tok
->type
==tk_useExtension
) /* I just ignore this */
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
] );
2284 fea_skip_to_semi(tok
);
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
;
2294 first_after_mark
= NULL
;
2297 if ( tok
->err_count
>100 )
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
] );
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'?") );
2309 } else if ( ret
==2 )
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
;
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
)
2332 if ( f
->type
!=ft_ap
|| f
->mark_class
==NULL
)
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
] );
2341 if ( f
==first_after_mark
)
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
] );
2353 fea_end_statement(tok
);
2355 /* Make sure all entries in this lookup of the same lookup type */
2356 lookuptype
= ot_undef
;
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
)
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
)
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
] );
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
] );
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
] );
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
;
2392 static struct nameid
*fea_ParseNameId(struct parseState
*tok
,int strid
) {
2393 int platform
= 3, specific
= 1, language
= 0x409;
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 */
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
] );
2413 } else if ( tok
->value
==1 ) {
2414 specific
= language
= 0;
2417 if ( tok
->type
== tk_int
) {
2418 specific
= tok
->value
;
2420 fea_TokenMustBe(tok
,tk_int
,'\0');
2421 language
= tok
->value
;
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
] );
2430 fea_skip_to_semi(tok
);
2433 if ( platform
==3 && specific
==1 ) {
2434 nm
= chunkalloc(sizeof(struct nameid
));
2436 nm
->platform
= platform
;
2437 nm
->specific
= specific
;
2438 nm
->language
= language
;
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 */
2448 int i
, dmax
= platform
==3 ? 4 : 2;
2450 for ( i
=0; i
<dmax
; ++i
) {
2452 if ( !ishexdigit(ch
)) {
2456 if ( ch
>='a' && ch
<='f' )
2458 else if ( ch
>='A' && ch
<='F' )
2468 if ( pt
-start
+3>=max
) {
2470 start
= grealloc(start
,(max
+=100)+1);
2473 pt
= utf8_idpb(pt
,value
);
2478 nm
->utf8_str
= copy("");
2481 nm
->utf8_str
= copy(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
] );
2490 fea_end_statement(tok
);
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) */
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]==';' )
2509 fea_end_statement(tok
);
2512 feat
= chunkalloc(sizeof(struct feat_item
));
2513 feat
->type
= ft_sizeparams
;
2514 feat
->next
= tok
->sofar
;
2517 feat
->u1
.params
= galloc(sizeof(params
));
2518 memcpy(feat
->u1
.params
,params
,sizeof(params
));
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
) {
2534 feat
= chunkalloc(sizeof(struct feat_item
));
2535 feat
->type
= ft_sizeparams
;
2536 feat
->next
= tok
->sofar
;
2539 string
->next
= feat
->u2
.names
;
2540 feat
->u2
.names
= string
;
2545 static void fea_ParseFeatureDef(struct parseState
*tok
) {
2547 struct feat_item
*item
, *size_item
= NULL
;
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
] );
2554 fea_skip_to_semi(tok
);
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
);
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
;
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
] );
2578 fea_skip_to_semi(tok
);
2584 if ( tok
->err_count
>100 )
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
] );
2590 } else if ( (ret
= fea_LookupSwitch(tok
))==0 ) {
2591 switch ( tok
->type
) {
2593 fea_ParseLookupDef(tok
,true);
2595 case tk_languagesystem
:
2596 fea_ParseLangSys(tok
,true);
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
] );
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
] );
2610 fea_end_statement(tok
);
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
;
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
] );
2621 item
= fea_AddFeatItem(tok
,type
,tok
->tag
);
2622 if ( type
==ft_lang
) {
2625 if ( tok
->type
==tk_include_dflt
)
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]==';' )
2634 LogError(_("Expected ';' on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
2640 fea_end_statement(tok
);
2644 if ( feat_tag
==CHR('s','i','z','e') ) {
2645 size_item
= fea_ParseParameters(tok
, size_item
);
2648 /* Fall on through */
2650 if ( feat_tag
==CHR('s','i','z','e') && strcmp(tok
->tokbuf
,"sizemenuname")==0 ) {
2651 size_item
= fea_ParseSizeMenuName(tok
, size_item
);
2654 /* Fall on through */
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
] );
2660 } else if ( ret
==2 )
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
] );
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
;
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; */
2689 if ( tok
->type
!= tk_nameid
)
2691 fea_TokenMustBe(tok
,tk_int
,'\0');
2692 string
= fea_ParseNameId(tok
,tok
->value
);
2693 if ( string
!=NULL
) {
2694 string
->next
= head
;
2700 item
= chunkalloc(sizeof(struct feat_item
));
2701 item
->type
= ft_names
;
2702 item
->next
= tok
->sofar
;
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
] );
2712 static void fea_ParseTableKeywords(struct parseState
*tok
, struct tablekeywords
*keys
) {
2714 struct tablevalues
*tv
, *head
= NULL
;
2716 struct feat_item
*item
;
2720 if ( tok
->type
!= tk_name
)
2722 for ( index
=0; keys
[index
].name
!=NULL
; ++index
) {
2723 if ( strcmp(keys
[index
].name
,tok
->tokbuf
)==0 )
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
] );
2731 if ( index
!=-1 && keys
[index
].offset
!=-1 ) {
2732 tv
= chunkalloc(sizeof(struct tablevalues
));
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
) {
2751 else if ( ch
=='"' ) {
2757 while ( (ch
=getc(in
))!=EOF
&& ch
!='"' );
2758 tok
->value
=(foo
[0]<<24) | (foo
[1]<<16) | (foo
[2]<<8) | foo
[3];
2760 LogError(_("Expected string on line %d of %s"),
2761 tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
2763 chunkfree(tv
,sizeof(*tv
));
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
] );
2772 chunkfree(tv
,sizeof(*tv
));
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
];
2783 for ( ch
=getc(in
); isdigit(ch
); ch
=getc(in
));
2786 if ( index
!=-1 && keys
[index
].cnt
!=1 ) {
2787 int is_panose
= strcmp(keys
[index
].name
,"Panose")==0 && tv
!=NULL
;
2789 tv
->panose_vals
[0] = tv
->value
;
2790 for ( i
=1; ; ++i
) {
2792 if ( tok
->type
!=tk_int
)
2794 if ( is_panose
&& i
<10 && tv
!=NULL
)
2795 tv
->panose_vals
[i
] = tok
->value
;
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
] );
2805 fea_skip_to_close_curly(tok
);
2806 chunkfree(tv
,sizeof(*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
] );
2818 fea_skip_to_close_curly(tok
);
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
;
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>+ */
2835 struct feat_item
*item
;
2836 int16
*carets
=NULL
; int len
=0, max
=0;
2840 if ( tok
->type
!=tk_name
)
2842 if ( strcmp(tok
->tokbuf
,"Attach")==0 ) {
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
] );
2848 fea_skip_to_semi(tok
);
2852 if ( tok
->type
!=tk_int
)
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
;
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
);
2870 LogError(_("Expected name or class on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
2872 fea_skip_to_semi(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
);
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
] );
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
;
2901 for ( i
=0; i
<4; ++i
) {
2903 item
->u1
.gdef_classes
[i
] = fea_ParseGlyphClassGuarded(tok
);
2907 LogError(_("Expected Attach or LigatureCaret or GlyphClassDef on line %d of %s"), tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
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
] );
2915 fea_skip_to_close_curly(tok
);
2920 static void fea_ParseTableDef(struct parseState
*tok
) {
2922 struct feat_item
*item
;
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
] );
2928 fea_skip_to_semi(tok
);
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
;
2938 fea_TokenMustBe(tok
,tk_char
,'{');
2939 switch ( table_tag
) {
2940 case CHR('G','D','E','F'):
2941 fea_ParseGDEFTable(tok
);
2943 case CHR('n','a','m','e'):
2944 fea_ParseNameTable(tok
);
2947 case CHR('h','h','e','a'):
2948 fea_ParseTableKeywords(tok
,hhead_keys
);
2950 case CHR('v','h','e','a'):
2951 fea_ParseTableKeywords(tok
,vhead_keys
);
2953 case CHR('O','S','/','2'):
2954 fea_ParseTableKeywords(tok
,os2_keys
);
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 */
2965 fea_skip_to_close_curly(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
] );
2973 fea_skip_to_semi(tok
);
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
) {
2988 free( nm
->utf8_str
);
2989 chunkfree(nm
,sizeof(*nm
));
2994 static void TableValsFree(struct tablevalues
*tb
) {
2995 struct tablevalues
*tbnext
;
2997 while ( tb
!=NULL
) {
2999 chunkfree(tb
,sizeof(*tb
));
3004 static void fea_featitemFree(struct feat_item
*item
) {
3005 struct feat_item
*next
;
3008 while ( item
!=NULL
) {
3010 switch ( item
->type
) {
3017 case ft_lookupflags
:
3018 /* Nothing needs freeing */;
3022 ScriptLangListFree( item
->u2
.sl
);
3024 case ft_lookup_start
:
3026 free( item
->u1
.lookup_name
);
3029 free( item
->u1
.params
);
3030 NameIdFree( item
->u2
.names
);
3033 NameIdFree( item
->u2
.names
);
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]));
3041 free( item
->u2
.lcaret
);
3044 TableValsFree( item
->u2
.tvals
);
3047 PSTFree( item
->u2
.pst
);
3050 free( item
->u1
.class );
3051 PSTFree( item
->u2
.pst
);
3054 AnchorPointsFree( item
->u2
.ap
);
3055 free( item
->mark_class
);
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
);
3073 IError("Don't know how to free a feat_item of type %d", item
->type
);
3076 chunkfree(item
,sizeof(*item
));
3081 static void fea_ParseFeatureFile(struct parseState
*tok
) {
3085 if ( tok
->err_count
>100 )
3087 switch ( tok
->type
) {
3089 fea_ParseGlyphClassDef(tok
);
3092 fea_ParseLookupDef(tok
,false);
3094 case tk_languagesystem
:
3095 fea_ParseLangSys(tok
,false);
3098 fea_ParseFeatureDef(tok
);
3101 fea_ParseTableDef(tok
);
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
);
3110 LogError(_("Unexpected token, %s, on line %d of %s"), tok
->tokbuf
, tok
->line
[tok
->inc_depth
], tok
->filename
[tok
->inc_depth
] );
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
) )
3137 if ( nested
->ticked
) {
3138 nested
= nested
->next
;
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 */
3146 prev
->lookup_next
= nested
;
3149 nested
= nested
->next
;
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
;
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 */;
3175 sub
= chunkalloc(sizeof(struct lookup_subtable
));
3177 sub
->per_glyph_pst_or_kern
= true;
3179 otl
->subtables
= 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 */
3190 IError("Unexpected feature type %d in a PST feature", l
->type
);
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
;
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
:
3214 /* Ignore these, already handled them */;
3217 sub
= chunkalloc(sizeof(struct lookup_subtable
));
3220 otl
->subtables
= sub
;
3224 sub
->fpst
= l
->u2
.fpst
;
3225 l
->u2
.fpst
->next
= tok
->sf
->possub
;
3226 tok
->sf
->possub
= l
->u2
.fpst
;
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
);
3242 IError("Unexpected feature type %d in a FPST feature", l
->type
);
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 */;
3270 sub
= chunkalloc(sizeof(struct lookup_subtable
));
3272 sub
->anchor_classes
= true;
3274 otl
->subtables
= sub
;
3278 ac
= chunkalloc(sizeof(AnchorClass
));
3280 ac
->type
= act_curs
;
3281 ac
->next
= tok
->accreated
;
3282 tok
->accreated
= ac
;
3285 for ( ap
=l
->u2
.ap
; ap
!=NULL
; ap
=ap
->next
) {
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 */
3294 IError("Unexpected feature type %d in a cursive feature", l
->type
);
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) */
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
*));
3317 while ( lookup_data
!= NULL
&& lookup_data
->type
!=ft_lookup_end
) {
3318 struct feat_item
*orig
= lookup_data
;
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 )
3347 classes
[i
] = lookup_data
->mark_class
;
3348 acs
[i
] = chunkalloc(sizeof(AnchorClass
));
3350 sub
= chunkalloc(sizeof(struct lookup_subtable
));
3352 sub
->anchor_classes
= true;
3354 otl
->subtables
= 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
:
3363 acs
[i
]->next
= tok
->accreated
;
3364 tok
->accreated
= acs
[i
];
3367 for ( ap
=lookup_data
->u2
.ap
; ap
!=NULL
; ap
=ap
->next
) {
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 */
3394 ap
->anchor
= acs
[i
];
3395 ap
->next
= l
->u1
.sc
->anchor
;
3396 l
->u1
.sc
->anchor
= ap
;
3402 if ( lookup_data
==orig
)
3408 static int is_blank(const char *s
) {
3412 while (s
[i
] != '\0' && s
[i
] == ' ')
3414 return( s
[i
] == '\0');
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
) {
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 )
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
];
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 */
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
];
3470 #ifdef FONTFORGE_CONFIG_DEVICETABLES
3471 static void KCFillDevTab(KernClass
*kc
,int index
,DeviceTable
*dt
) {
3472 if ( dt
==NULL
|| dt
->corrections
== NULL
)
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
)
3485 kp
->adjust
= chunkalloc(sizeof(DeviceTable
));
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);
3492 static void fea_fillKernClass(KernClass
*kc
,struct feat_item
*l
) {
3496 while ( l
!=NULL
&& l
->type
!=ft_subtable
) {
3497 if ( l
->type
==ft_pstclass
) {
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
);
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
);
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
);
3523 if ( strcmp(kc
->seconds
[j
],pst
->u
.pair
.paired
)==0 )
3527 if ( strcmp(kc
->firsts
[i
],l
->u1
.class)==0 )
3536 static void SFKernClassRemoveFree(SplineFont
*sf
,KernClass
*kc
) {
3539 if ( sf
->kerns
==kc
)
3540 sf
->kerns
= kc
->next
;
3541 else if ( sf
->vkerns
==kc
)
3542 sf
->vkerns
= kc
->next
;
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
);
3550 prev
->next
= kc
->next
;
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
;
3568 memset(&lefts
,0,sizeof(lefts
));
3569 memset(&rights
,0,sizeof(rights
));
3571 lefts
.classes
= galloc(kmax
*sizeof(char *));
3572 rights
.classes
= galloc(kmax
*sizeof(char *));
3573 lefts
.max
= rights
.max
= kmax
;
3576 for ( l
= lookup_data
; l
!=NULL
; ) {
3579 while ( l
!=NULL
&& l
->type
!=ft_subtable
) {
3580 if ( l
->type
== ft_pst
) {
3582 sub
= chunkalloc(sizeof(struct lookup_subtable
));
3584 sub
->per_glyph_pst_or_kern
= true;
3585 if ( lastsub
==NULL
)
3586 otl
->subtables
= sub
;
3588 lastsub
->next
= sub
;
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 &&
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
);
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
);
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
);
3630 kp
->next
= sc
->vkerns
;
3633 kp
->next
= sc
->kerns
;
3638 pst
->subtable
= sub
;
3639 pst
->next
= sc
->possub
;
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
));
3649 lefts
.cnt
= rights
.cnt
= kcnt
;
3650 fea_canonicalClassSet(&lefts
);
3651 fea_canonicalClassSet(&rights
);
3653 sub
= chunkalloc(sizeof(struct lookup_subtable
));
3655 if ( lastsub
==NULL
)
3656 otl
->subtables
= sub
;
3658 lastsub
->next
= 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
];
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
));
3677 fea_fillKernClass(kc
,first
);
3678 if ( sub
->vertical_kerning
) {
3679 kc
->next
= tok
->sf
->vkerns
;
3680 tok
->sf
->vkerns
= kc
;
3682 kc
->next
= tok
->sf
->kerns
;
3683 tok
->sf
->kerns
= kc
;
3687 while ( l
!=NULL
&& l
->type
==ft_subtable
)
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 */
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)
3718 otl
= SFFindLookup(tok
->sf
,lookup_data
->u1
.lookup_name
);
3720 LogError( _("No lookup named %s"),lookup_data
->u1
.lookup_name
);
3721 /* Can't give a line number, this is second pass */
3725 otl
= chunkalloc(sizeof(OTLookup
));
3726 otl
->lookup_flags
= lookup_flag
;
3727 otl
->lookup_type
= ot_undef
;
3728 if ( tok
->last
==NULL
)
3731 tok
->last
->next
= otl
;
3734 /* Search first for class counts */
3736 for ( l
= lookup_data
; l
!=NULL
; l
=l
->lookup_next
) {
3737 if ( l
->type
== ft_ap
&& l
->mark_class
!=NULL
)
3739 else if ( l
->type
== ft_pstclass
)
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
);
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
);
3769 fea_ApplyLookupListPST(tok
,lookup_data
,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
;
3783 names
= names
->next
;
3788 static void fea_AttachFeatureToLookup(OTLookup
*otl
,uint32 feat_tag
,
3789 struct scriptlanglist
*sl
) {
3790 FeatureScriptLangList
*fl
;
3795 for ( fl
= otl
->features
; fl
!=NULL
&& fl
->featuretag
!=feat_tag
; fl
=fl
->next
);
3797 fl
= chunkalloc(sizeof(FeatureScriptLangList
));
3798 fl
->next
= otl
->features
;
3800 fl
->featuretag
= feat_tag
;
3801 fl
->scripts
= SListCopy(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
);
3812 cur
= chunkalloc(sizeof(struct ttflangname
));
3813 cur
->lang
= names
->language
;
3814 cur
->next
= sf
->names
;
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
;
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 ) {
3840 *((uint32
*) (((uint8
*) sf
) + cur
->offset
)) = tv
->value
;
3841 else if ( cur
->size
==2 )
3842 *((uint16
*) (((uint8
*) sf
) + cur
->offset
)) = tv
->value
;
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
) {
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
;
3874 for ( start
= pt
; *pt
!=' ' && *pt
!='\0'; ++pt
);
3875 ch
= *pt
; *pt
= '\0';
3876 sc
= SFGetChar(sf
,-1,start
);
3879 sc
->glyph_class
= i
+1;
3884 static void fea_GDefLigCarets(SplineFont
*sf
, struct feat_item
*f
) {
3888 PST
*pst
, *prev
, *next
;
3890 for ( pt
=f
->u1
.class; ; ) {
3891 while ( *pt
==' ' ) ++pt
;
3894 for ( start
= pt
; *pt
!=' ' && *pt
!='\0'; ++pt
);
3895 ch
= *pt
; *pt
= '\0';
3896 sc
= SFGetChar(sf
,-1,start
);
3899 for ( prev
=NULL
, pst
=sc
->possub
; pst
!=NULL
; pst
=next
) {
3901 if ( pst
->type
!=pst_lcaret
)
3912 for ( i
=0; f
->u2
.lcaret
[i
]!=0; ++i
);
3913 pst
= chunkalloc(sizeof(PST
));
3914 pst
->next
= sc
->possub
;
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
;
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
; ) {
3941 switch ( f
->type
) {
3942 case ft_lookupflags
:
3943 lookup_flags
= f
->u2
.lookupflags
;
3947 otl
= fea_ApplyLookupList(tok
,f
,lookup_flags
);
3948 fea_AttachFeatureToLookup(otl
,feature_tag
,sl
);
3951 case ft_lookup_start
:
3953 start
->lookup_next
= f
->next
;
3954 f
= fea_SetLookupLink(start
->next
,ot_undef
);
3955 if ( f
!=NULL
&& f
->type
== ft_lookup_end
)
3957 otl
= fea_ApplyLookupList(tok
,start
,lookup_flags
);
3958 fea_AttachFeatureToLookup(otl
,feature_tag
,sl
);
3961 ScriptLangListFree(sl
);
3962 sl
= chunkalloc(sizeof(struct scriptlanglist
));
3963 sl
->script
= f
->u1
.tag
;
3965 sl
->langs
[0] = DEFAULT_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
;
3977 if ( !f
->u2
.exclude_dflt
) {
3978 if ( sl
->langs
[0]!=DEFAULT_LANG
) {
3979 sl
->langs
[1] = DEFAULT_LANG
;
3986 ScriptLangListFree(sl
);
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
);
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
) {
4018 if ( fea_FeatItemEndsLookup(a
->type
) ||
4019 a
->type
==ft_subtable
|| a
->type
==ft_ap
)
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 */
4029 ltype
= fea_LookupTypeFromItem(a
);
4031 ltype
= fea_LookupTypeFromItem(f
);
4033 f
= fea_SetLookupLink(start
,ltype
);
4034 otl
= fea_ApplyLookupList(tok
,start
,lookup_flags
);
4035 fea_AttachFeatureToLookup(otl
,feature_tag
,sl
);
4038 IError("Unexpected feature item in feature definition %d", f
->type
);
4042 if ( f
!=NULL
&& f
->type
== ft_feat_end
)
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
:
4054 start
->lookup_next
= f
->next
;
4055 f
= fea_SetLookupLink(start
->next
,ot_undef
);
4056 if ( f
!=NULL
&& f
->type
== ft_lookup_end
)
4058 fea_ApplyLookupList(tok
,start
,0);
4061 f
= fea_ApplyFeatureList(tok
,f
);
4064 /* I store things all mushed together, so this tag is useless to me*/
4065 /* ignore it. The stuff inside the table matters though... */
4069 fea_NameID2NameTable(tok
->sf
,f
->u2
.names
);
4073 fea_TableByKeywords(tok
->sf
,f
);
4076 case ft_gdefclasses
:
4077 fea_GDefGlyphClasses(tok
->sf
,f
);
4081 fea_GDefLigCarets(tok
->sf
,f
);
4085 IError("Unexpected feature item in feature file %d", f
->type
);
4091 static struct feat_item
*fea_reverseList(struct feat_item
*f
) {
4092 struct feat_item
*n
= NULL
, *p
= NULL
;
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
++;
4114 for ( otl
= sf
->gsub_lookups
; otl
!=NULL
; otl
=otl
->next
) {
4115 otl
->lookup_index
= gs_cnt
++;
4119 for ( otl
= tok
->created
; otl
!=NULL
; otl
=otlnext
) {
4120 otlnext
= otl
->next
;
4122 if ( otl
->lookup_name
!=NULL
&& SFFindLookup(sf
,otl
->lookup_name
)!=NULL
) {
4124 char *namebuf
= galloc(strlen( otl
->lookup_name
)+8 );
4125 /* Name already in use, modify it */
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
;
4136 gsub_last
->next
= otl
;
4138 otl
->lookup_index
= gs_cnt
++;
4140 if ( gpos_last
==NULL
)
4141 sf
->gpos_lookups
= otl
;
4143 gpos_last
->next
= 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) */
4152 for ( ac
=tok
->accreated
; ac
!=NULL
; ac
=acnext
) {
4154 if ( ac
->name
==NULL
) {
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
;
4171 void SFApplyFeatureFile(SplineFont
*sf
,FILE *file
,char *filename
) {
4172 struct parseState tok
;
4173 struct glyphclasses
*gc
, *gcnext
;
4175 memset(&tok
,0,sizeof(tok
));
4177 tok
.filename
[0] = filename
;
4178 tok
.inlist
[0] = file
;
4180 if ( sf
->cidmaster
) sf
= sf
->cidmaster
;
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
);
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
) {
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");
4203 ff_post_error(_("Cannot open file"),_("Cannot open feature file %.120s"), filename
);
4206 SFApplyFeatureFile(sf
,in
,filename
);