1 | /**************************************************************************** |
2 | * |
3 | * afmparse.c |
4 | * |
5 | * AFM parser (body). |
6 | * |
7 | * Copyright (C) 2006-2023 by |
8 | * David Turner, Robert Wilhelm, and Werner Lemberg. |
9 | * |
10 | * This file is part of the FreeType project, and may only be used, |
11 | * modified, and distributed under the terms of the FreeType project |
12 | * license, LICENSE.TXT. By continuing to use, modify, or distribute |
13 | * this file you indicate that you have read the license and |
14 | * understand and accept it fully. |
15 | * |
16 | */ |
17 | |
18 | #include <freetype/freetype.h> |
19 | #include <freetype/internal/ftdebug.h> |
20 | #include <freetype/internal/psaux.h> |
21 | |
22 | #ifndef T1_CONFIG_OPTION_NO_AFM |
23 | |
24 | #include "afmparse.h" |
25 | #include "psconv.h" |
26 | |
27 | #include "psauxerr.h" |
28 | |
29 | |
30 | /************************************************************************** |
31 | * |
32 | * The macro FT_COMPONENT is used in trace mode. It is an implicit |
33 | * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log |
34 | * messages during execution. |
35 | */ |
36 | #undef FT_COMPONENT |
37 | #define FT_COMPONENT afmparse |
38 | |
39 | |
40 | /************************************************************************** |
41 | * |
42 | * AFM_Stream |
43 | * |
44 | * The use of AFM_Stream is largely inspired by parseAFM.[ch] from t1lib. |
45 | * |
46 | */ |
47 | |
48 | enum |
49 | { |
50 | AFM_STREAM_STATUS_NORMAL, |
51 | AFM_STREAM_STATUS_EOC, |
52 | AFM_STREAM_STATUS_EOL, |
53 | AFM_STREAM_STATUS_EOF |
54 | }; |
55 | |
56 | |
57 | typedef struct AFM_StreamRec_ |
58 | { |
59 | FT_Byte* cursor; |
60 | FT_Byte* base; |
61 | FT_Byte* limit; |
62 | |
63 | FT_Int status; |
64 | |
65 | } AFM_StreamRec; |
66 | |
67 | |
68 | #ifndef EOF |
69 | #define EOF -1 |
70 | #endif |
71 | |
72 | |
73 | /* this works because empty lines are ignored */ |
74 | #define AFM_IS_NEWLINE( ch ) ( (ch) == '\r' || (ch) == '\n' ) |
75 | |
76 | #define AFM_IS_EOF( ch ) ( (ch) == EOF || (ch) == '\x1a' ) |
77 | #define AFM_IS_SPACE( ch ) ( (ch) == ' ' || (ch) == '\t' ) |
78 | |
79 | /* column separator; there is no `column' in the spec actually */ |
80 | #define AFM_IS_SEP( ch ) ( (ch) == ';' ) |
81 | |
82 | #define AFM_GETC() \ |
83 | ( ( (stream)->cursor < (stream)->limit ) ? *(stream)->cursor++ \ |
84 | : EOF ) |
85 | |
86 | #define AFM_STREAM_KEY_BEGIN( stream ) \ |
87 | (char*)( (stream)->cursor - 1 ) |
88 | |
89 | #define AFM_STREAM_KEY_LEN( stream, key ) \ |
90 | (FT_Offset)( (char*)(stream)->cursor - key - 1 ) |
91 | |
92 | #define AFM_STATUS_EOC( stream ) \ |
93 | ( (stream)->status >= AFM_STREAM_STATUS_EOC ) |
94 | |
95 | #define AFM_STATUS_EOL( stream ) \ |
96 | ( (stream)->status >= AFM_STREAM_STATUS_EOL ) |
97 | |
98 | #define AFM_STATUS_EOF( stream ) \ |
99 | ( (stream)->status >= AFM_STREAM_STATUS_EOF ) |
100 | |
101 | |
102 | static int |
103 | afm_stream_skip_spaces( AFM_Stream stream ) |
104 | { |
105 | int ch = 0; /* make stupid compiler happy */ |
106 | |
107 | |
108 | if ( AFM_STATUS_EOC( stream ) ) |
109 | return ';'; |
110 | |
111 | while ( 1 ) |
112 | { |
113 | ch = AFM_GETC(); |
114 | if ( !AFM_IS_SPACE( ch ) ) |
115 | break; |
116 | } |
117 | |
118 | if ( AFM_IS_NEWLINE( ch ) ) |
119 | stream->status = AFM_STREAM_STATUS_EOL; |
120 | else if ( AFM_IS_SEP( ch ) ) |
121 | stream->status = AFM_STREAM_STATUS_EOC; |
122 | else if ( AFM_IS_EOF( ch ) ) |
123 | stream->status = AFM_STREAM_STATUS_EOF; |
124 | |
125 | return ch; |
126 | } |
127 | |
128 | |
129 | /* read a key or value in current column */ |
130 | static char* |
131 | afm_stream_read_one( AFM_Stream stream ) |
132 | { |
133 | char* str; |
134 | |
135 | |
136 | afm_stream_skip_spaces( stream ); |
137 | if ( AFM_STATUS_EOC( stream ) ) |
138 | return NULL; |
139 | |
140 | str = AFM_STREAM_KEY_BEGIN( stream ); |
141 | |
142 | while ( 1 ) |
143 | { |
144 | int ch = AFM_GETC(); |
145 | |
146 | |
147 | if ( AFM_IS_SPACE( ch ) ) |
148 | break; |
149 | else if ( AFM_IS_NEWLINE( ch ) ) |
150 | { |
151 | stream->status = AFM_STREAM_STATUS_EOL; |
152 | break; |
153 | } |
154 | else if ( AFM_IS_SEP( ch ) ) |
155 | { |
156 | stream->status = AFM_STREAM_STATUS_EOC; |
157 | break; |
158 | } |
159 | else if ( AFM_IS_EOF( ch ) ) |
160 | { |
161 | stream->status = AFM_STREAM_STATUS_EOF; |
162 | break; |
163 | } |
164 | } |
165 | |
166 | return str; |
167 | } |
168 | |
169 | |
170 | /* read a string (i.e., read to EOL) */ |
171 | static char* |
172 | afm_stream_read_string( AFM_Stream stream ) |
173 | { |
174 | char* str; |
175 | |
176 | |
177 | afm_stream_skip_spaces( stream ); |
178 | if ( AFM_STATUS_EOL( stream ) ) |
179 | return NULL; |
180 | |
181 | str = AFM_STREAM_KEY_BEGIN( stream ); |
182 | |
183 | /* scan to eol */ |
184 | while ( 1 ) |
185 | { |
186 | int ch = AFM_GETC(); |
187 | |
188 | |
189 | if ( AFM_IS_NEWLINE( ch ) ) |
190 | { |
191 | stream->status = AFM_STREAM_STATUS_EOL; |
192 | break; |
193 | } |
194 | else if ( AFM_IS_EOF( ch ) ) |
195 | { |
196 | stream->status = AFM_STREAM_STATUS_EOF; |
197 | break; |
198 | } |
199 | } |
200 | |
201 | return str; |
202 | } |
203 | |
204 | |
205 | /************************************************************************** |
206 | * |
207 | * AFM_Parser |
208 | * |
209 | */ |
210 | |
211 | /* all keys defined in Ch. 7-10 of 5004.AFM_Spec.pdf */ |
212 | typedef enum AFM_Token_ |
213 | { |
214 | AFM_TOKEN_ASCENDER, |
215 | AFM_TOKEN_AXISLABEL, |
216 | AFM_TOKEN_AXISTYPE, |
217 | AFM_TOKEN_B, |
218 | AFM_TOKEN_BLENDAXISTYPES, |
219 | AFM_TOKEN_BLENDDESIGNMAP, |
220 | AFM_TOKEN_BLENDDESIGNPOSITIONS, |
221 | AFM_TOKEN_C, |
222 | AFM_TOKEN_CC, |
223 | AFM_TOKEN_CH, |
224 | AFM_TOKEN_CAPHEIGHT, |
225 | AFM_TOKEN_CHARWIDTH, |
226 | AFM_TOKEN_CHARACTERSET, |
227 | AFM_TOKEN_CHARACTERS, |
228 | AFM_TOKEN_DESCENDER, |
229 | AFM_TOKEN_ENCODINGSCHEME, |
230 | AFM_TOKEN_ENDAXIS, |
231 | AFM_TOKEN_ENDCHARMETRICS, |
232 | AFM_TOKEN_ENDCOMPOSITES, |
233 | AFM_TOKEN_ENDDIRECTION, |
234 | AFM_TOKEN_ENDFONTMETRICS, |
235 | AFM_TOKEN_ENDKERNDATA, |
236 | AFM_TOKEN_ENDKERNPAIRS, |
237 | AFM_TOKEN_ENDTRACKKERN, |
238 | AFM_TOKEN_ESCCHAR, |
239 | AFM_TOKEN_FAMILYNAME, |
240 | AFM_TOKEN_FONTBBOX, |
241 | AFM_TOKEN_FONTNAME, |
242 | AFM_TOKEN_FULLNAME, |
243 | AFM_TOKEN_ISBASEFONT, |
244 | AFM_TOKEN_ISCIDFONT, |
245 | AFM_TOKEN_ISFIXEDPITCH, |
246 | AFM_TOKEN_ISFIXEDV, |
247 | AFM_TOKEN_ITALICANGLE, |
248 | AFM_TOKEN_KP, |
249 | AFM_TOKEN_KPH, |
250 | AFM_TOKEN_KPX, |
251 | AFM_TOKEN_KPY, |
252 | AFM_TOKEN_L, |
253 | AFM_TOKEN_MAPPINGSCHEME, |
254 | AFM_TOKEN_METRICSSETS, |
255 | AFM_TOKEN_N, |
256 | AFM_TOKEN_NOTICE, |
257 | AFM_TOKEN_PCC, |
258 | AFM_TOKEN_STARTAXIS, |
259 | AFM_TOKEN_STARTCHARMETRICS, |
260 | AFM_TOKEN_STARTCOMPOSITES, |
261 | AFM_TOKEN_STARTDIRECTION, |
262 | AFM_TOKEN_STARTFONTMETRICS, |
263 | AFM_TOKEN_STARTKERNDATA, |
264 | AFM_TOKEN_STARTKERNPAIRS, |
265 | AFM_TOKEN_STARTKERNPAIRS0, |
266 | AFM_TOKEN_STARTKERNPAIRS1, |
267 | AFM_TOKEN_STARTTRACKKERN, |
268 | AFM_TOKEN_STDHW, |
269 | AFM_TOKEN_STDVW, |
270 | AFM_TOKEN_TRACKKERN, |
271 | AFM_TOKEN_UNDERLINEPOSITION, |
272 | AFM_TOKEN_UNDERLINETHICKNESS, |
273 | AFM_TOKEN_VV, |
274 | AFM_TOKEN_VVECTOR, |
275 | AFM_TOKEN_VERSION, |
276 | AFM_TOKEN_W, |
277 | AFM_TOKEN_W0, |
278 | AFM_TOKEN_W0X, |
279 | AFM_TOKEN_W0Y, |
280 | AFM_TOKEN_W1, |
281 | AFM_TOKEN_W1X, |
282 | AFM_TOKEN_W1Y, |
283 | AFM_TOKEN_WX, |
284 | AFM_TOKEN_WY, |
285 | AFM_TOKEN_WEIGHT, |
286 | AFM_TOKEN_WEIGHTVECTOR, |
287 | AFM_TOKEN_XHEIGHT, |
288 | N_AFM_TOKENS, |
289 | AFM_TOKEN_UNKNOWN |
290 | |
291 | } AFM_Token; |
292 | |
293 | |
294 | static const char* const afm_key_table[N_AFM_TOKENS] = |
295 | { |
296 | "Ascender" , |
297 | "AxisLabel" , |
298 | "AxisType" , |
299 | "B" , |
300 | "BlendAxisTypes" , |
301 | "BlendDesignMap" , |
302 | "BlendDesignPositions" , |
303 | "C" , |
304 | "CC" , |
305 | "CH" , |
306 | "CapHeight" , |
307 | "CharWidth" , |
308 | "CharacterSet" , |
309 | "Characters" , |
310 | "Descender" , |
311 | "EncodingScheme" , |
312 | "EndAxis" , |
313 | "EndCharMetrics" , |
314 | "EndComposites" , |
315 | "EndDirection" , |
316 | "EndFontMetrics" , |
317 | "EndKernData" , |
318 | "EndKernPairs" , |
319 | "EndTrackKern" , |
320 | "EscChar" , |
321 | "FamilyName" , |
322 | "FontBBox" , |
323 | "FontName" , |
324 | "FullName" , |
325 | "IsBaseFont" , |
326 | "IsCIDFont" , |
327 | "IsFixedPitch" , |
328 | "IsFixedV" , |
329 | "ItalicAngle" , |
330 | "KP" , |
331 | "KPH" , |
332 | "KPX" , |
333 | "KPY" , |
334 | "L" , |
335 | "MappingScheme" , |
336 | "MetricsSets" , |
337 | "N" , |
338 | "Notice" , |
339 | "PCC" , |
340 | "StartAxis" , |
341 | "StartCharMetrics" , |
342 | "StartComposites" , |
343 | "StartDirection" , |
344 | "StartFontMetrics" , |
345 | "StartKernData" , |
346 | "StartKernPairs" , |
347 | "StartKernPairs0" , |
348 | "StartKernPairs1" , |
349 | "StartTrackKern" , |
350 | "StdHW" , |
351 | "StdVW" , |
352 | "TrackKern" , |
353 | "UnderlinePosition" , |
354 | "UnderlineThickness" , |
355 | "VV" , |
356 | "VVector" , |
357 | "Version" , |
358 | "W" , |
359 | "W0" , |
360 | "W0X" , |
361 | "W0Y" , |
362 | "W1" , |
363 | "W1X" , |
364 | "W1Y" , |
365 | "WX" , |
366 | "WY" , |
367 | "Weight" , |
368 | "WeightVector" , |
369 | "XHeight" |
370 | }; |
371 | |
372 | |
373 | /* |
374 | * `afm_parser_read_vals' and `afm_parser_next_key' provide |
375 | * high-level operations to an AFM_Stream. The rest of the |
376 | * parser functions should use them without accessing the |
377 | * AFM_Stream directly. |
378 | */ |
379 | |
380 | FT_LOCAL_DEF( FT_Int ) |
381 | afm_parser_read_vals( AFM_Parser parser, |
382 | AFM_Value vals, |
383 | FT_Int n ) |
384 | { |
385 | AFM_Stream stream = parser->stream; |
386 | char* str; |
387 | FT_Int i; |
388 | |
389 | |
390 | if ( n > AFM_MAX_ARGUMENTS ) |
391 | return 0; |
392 | |
393 | for ( i = 0; i < n; i++ ) |
394 | { |
395 | FT_Offset len; |
396 | AFM_Value val = vals + i; |
397 | |
398 | |
399 | if ( val->type == AFM_VALUE_TYPE_STRING ) |
400 | str = afm_stream_read_string( stream ); |
401 | else |
402 | str = afm_stream_read_one( stream ); |
403 | |
404 | if ( !str ) |
405 | break; |
406 | |
407 | len = AFM_STREAM_KEY_LEN( stream, str ); |
408 | |
409 | switch ( val->type ) |
410 | { |
411 | case AFM_VALUE_TYPE_STRING: |
412 | case AFM_VALUE_TYPE_NAME: |
413 | { |
414 | FT_Memory memory = parser->memory; |
415 | FT_Error error; |
416 | |
417 | |
418 | if ( !FT_QALLOC( val->u.s, len + 1 ) ) |
419 | { |
420 | ft_memcpy( val->u.s, str, len ); |
421 | val->u.s[len] = '\0'; |
422 | } |
423 | } |
424 | break; |
425 | |
426 | case AFM_VALUE_TYPE_FIXED: |
427 | val->u.f = PS_Conv_ToFixed( (FT_Byte**)(void*)&str, |
428 | (FT_Byte*)str + len, 0 ); |
429 | break; |
430 | |
431 | case AFM_VALUE_TYPE_INTEGER: |
432 | val->u.i = PS_Conv_ToInt( (FT_Byte**)(void*)&str, |
433 | (FT_Byte*)str + len ); |
434 | break; |
435 | |
436 | case AFM_VALUE_TYPE_BOOL: |
437 | val->u.b = FT_BOOL( len == 4 && |
438 | !ft_strncmp( str, "true" , 4 ) ); |
439 | break; |
440 | |
441 | case AFM_VALUE_TYPE_INDEX: |
442 | if ( parser->get_index ) |
443 | val->u.i = parser->get_index( str, len, parser->user_data ); |
444 | else |
445 | val->u.i = 0; |
446 | break; |
447 | } |
448 | } |
449 | |
450 | return i; |
451 | } |
452 | |
453 | |
454 | FT_LOCAL_DEF( char* ) |
455 | afm_parser_next_key( AFM_Parser parser, |
456 | FT_Bool line, |
457 | FT_Offset* len ) |
458 | { |
459 | AFM_Stream stream = parser->stream; |
460 | char* key = NULL; /* make stupid compiler happy */ |
461 | |
462 | |
463 | if ( line ) |
464 | { |
465 | while ( 1 ) |
466 | { |
467 | /* skip current line */ |
468 | if ( !AFM_STATUS_EOL( stream ) ) |
469 | afm_stream_read_string( stream ); |
470 | |
471 | stream->status = AFM_STREAM_STATUS_NORMAL; |
472 | key = afm_stream_read_one( stream ); |
473 | |
474 | /* skip empty line */ |
475 | if ( !key && |
476 | !AFM_STATUS_EOF( stream ) && |
477 | AFM_STATUS_EOL( stream ) ) |
478 | continue; |
479 | |
480 | break; |
481 | } |
482 | } |
483 | else |
484 | { |
485 | while ( 1 ) |
486 | { |
487 | /* skip current column */ |
488 | while ( !AFM_STATUS_EOC( stream ) ) |
489 | afm_stream_read_one( stream ); |
490 | |
491 | stream->status = AFM_STREAM_STATUS_NORMAL; |
492 | key = afm_stream_read_one( stream ); |
493 | |
494 | /* skip empty column */ |
495 | if ( !key && |
496 | !AFM_STATUS_EOF( stream ) && |
497 | AFM_STATUS_EOC( stream ) ) |
498 | continue; |
499 | |
500 | break; |
501 | } |
502 | } |
503 | |
504 | if ( len ) |
505 | *len = ( key ) ? (FT_Offset)AFM_STREAM_KEY_LEN( stream, key ) |
506 | : 0; |
507 | |
508 | return key; |
509 | } |
510 | |
511 | |
512 | static AFM_Token |
513 | afm_tokenize( const char* key, |
514 | FT_Offset len ) |
515 | { |
516 | int n; |
517 | |
518 | |
519 | for ( n = 0; n < N_AFM_TOKENS; n++ ) |
520 | { |
521 | if ( *( afm_key_table[n] ) == *key ) |
522 | { |
523 | for ( ; n < N_AFM_TOKENS; n++ ) |
524 | { |
525 | if ( *( afm_key_table[n] ) != *key ) |
526 | return AFM_TOKEN_UNKNOWN; |
527 | |
528 | if ( ft_strncmp( afm_key_table[n], key, len ) == 0 ) |
529 | return (AFM_Token) n; |
530 | } |
531 | } |
532 | } |
533 | |
534 | return AFM_TOKEN_UNKNOWN; |
535 | } |
536 | |
537 | |
538 | FT_LOCAL_DEF( FT_Error ) |
539 | afm_parser_init( AFM_Parser parser, |
540 | FT_Memory memory, |
541 | FT_Byte* base, |
542 | FT_Byte* limit ) |
543 | { |
544 | AFM_Stream stream = NULL; |
545 | FT_Error error; |
546 | |
547 | |
548 | if ( FT_NEW( stream ) ) |
549 | return error; |
550 | |
551 | stream->cursor = stream->base = base; |
552 | stream->limit = limit; |
553 | |
554 | /* don't skip the first line during the first call */ |
555 | stream->status = AFM_STREAM_STATUS_EOL; |
556 | |
557 | parser->memory = memory; |
558 | parser->stream = stream; |
559 | parser->FontInfo = NULL; |
560 | parser->get_index = NULL; |
561 | |
562 | return FT_Err_Ok; |
563 | } |
564 | |
565 | |
566 | FT_LOCAL_DEF( void ) |
567 | afm_parser_done( AFM_Parser parser ) |
568 | { |
569 | FT_Memory memory = parser->memory; |
570 | |
571 | |
572 | FT_FREE( parser->stream ); |
573 | } |
574 | |
575 | |
576 | static FT_Error |
577 | afm_parser_read_int( AFM_Parser parser, |
578 | FT_Int* aint ) |
579 | { |
580 | AFM_ValueRec val; |
581 | |
582 | |
583 | val.type = AFM_VALUE_TYPE_INTEGER; |
584 | |
585 | if ( afm_parser_read_vals( parser, &val, 1 ) == 1 ) |
586 | { |
587 | *aint = val.u.i; |
588 | |
589 | return FT_Err_Ok; |
590 | } |
591 | else |
592 | return FT_THROW( Syntax_Error ); |
593 | } |
594 | |
595 | |
596 | static FT_Error |
597 | afm_parse_track_kern( AFM_Parser parser ) |
598 | { |
599 | AFM_FontInfo fi = parser->FontInfo; |
600 | AFM_Stream stream = parser->stream; |
601 | AFM_TrackKern tk; |
602 | |
603 | char* key; |
604 | FT_Offset len; |
605 | int n = -1; |
606 | FT_Int tmp; |
607 | |
608 | |
609 | if ( afm_parser_read_int( parser, &tmp ) ) |
610 | goto Fail; |
611 | |
612 | if ( tmp < 0 ) |
613 | { |
614 | FT_ERROR(( "afm_parse_track_kern: invalid number of track kerns\n" )); |
615 | goto Fail; |
616 | } |
617 | |
618 | fi->NumTrackKern = (FT_UInt)tmp; |
619 | FT_TRACE3(( "afm_parse_track_kern: %u track kern%s expected\n" , |
620 | fi->NumTrackKern, |
621 | fi->NumTrackKern == 1 ? "" : "s" )); |
622 | |
623 | /* Rough sanity check: The minimum line length of the `TrackKern` */ |
624 | /* command is 20 characters (including the EOL character). */ |
625 | if ( (FT_ULong)( stream->limit - stream->cursor ) / 20 < |
626 | fi->NumTrackKern ) |
627 | { |
628 | FT_ERROR(( "afm_parse_track_kern:" |
629 | " number of track kern entries exceeds stream size\n" )); |
630 | goto Fail; |
631 | } |
632 | |
633 | if ( fi->NumTrackKern ) |
634 | { |
635 | FT_Memory memory = parser->memory; |
636 | FT_Error error; |
637 | |
638 | |
639 | if ( FT_QNEW_ARRAY( fi->TrackKerns, fi->NumTrackKern ) ) |
640 | return error; |
641 | } |
642 | |
643 | while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 ) |
644 | { |
645 | AFM_ValueRec shared_vals[5]; |
646 | |
647 | |
648 | switch ( afm_tokenize( key, len ) ) |
649 | { |
650 | case AFM_TOKEN_TRACKKERN: |
651 | n++; |
652 | |
653 | if ( n >= (int)fi->NumTrackKern ) |
654 | { |
655 | FT_ERROR(( "afm_parse_track_kern: too many track kern data\n" )); |
656 | goto Fail; |
657 | } |
658 | |
659 | tk = fi->TrackKerns + n; |
660 | |
661 | shared_vals[0].type = AFM_VALUE_TYPE_INTEGER; |
662 | shared_vals[1].type = AFM_VALUE_TYPE_FIXED; |
663 | shared_vals[2].type = AFM_VALUE_TYPE_FIXED; |
664 | shared_vals[3].type = AFM_VALUE_TYPE_FIXED; |
665 | shared_vals[4].type = AFM_VALUE_TYPE_FIXED; |
666 | if ( afm_parser_read_vals( parser, shared_vals, 5 ) != 5 ) |
667 | { |
668 | FT_ERROR(( "afm_parse_track_kern:" |
669 | " insufficient number of parameters for entry %d\n" , |
670 | n )); |
671 | goto Fail; |
672 | } |
673 | |
674 | tk->degree = shared_vals[0].u.i; |
675 | tk->min_ptsize = shared_vals[1].u.f; |
676 | tk->min_kern = shared_vals[2].u.f; |
677 | tk->max_ptsize = shared_vals[3].u.f; |
678 | tk->max_kern = shared_vals[4].u.f; |
679 | |
680 | break; |
681 | |
682 | case AFM_TOKEN_ENDTRACKKERN: |
683 | case AFM_TOKEN_ENDKERNDATA: |
684 | case AFM_TOKEN_ENDFONTMETRICS: |
685 | tmp = n + 1; |
686 | if ( (FT_UInt)tmp != fi->NumTrackKern ) |
687 | { |
688 | FT_TRACE1(( "afm_parse_track_kern: %s%d track kern entr%s seen\n" , |
689 | tmp == 0 ? "" : "only " , |
690 | tmp, |
691 | tmp == 1 ? "y" : "ies" )); |
692 | fi->NumTrackKern = (FT_UInt)tmp; |
693 | } |
694 | else |
695 | FT_TRACE3(( "afm_parse_track_kern: %d track kern entr%s seen\n" , |
696 | tmp, |
697 | tmp == 1 ? "y" : "ies" )); |
698 | return FT_Err_Ok; |
699 | |
700 | case AFM_TOKEN_UNKNOWN: |
701 | break; |
702 | |
703 | default: |
704 | goto Fail; |
705 | } |
706 | } |
707 | |
708 | Fail: |
709 | return FT_THROW( Syntax_Error ); |
710 | } |
711 | |
712 | |
713 | #undef KERN_INDEX |
714 | #define KERN_INDEX( g1, g2 ) ( ( (FT_ULong)g1 << 16 ) | g2 ) |
715 | |
716 | |
717 | /* compare two kerning pairs */ |
718 | FT_COMPARE_DEF( int ) |
719 | afm_compare_kern_pairs( const void* a, |
720 | const void* b ) |
721 | { |
722 | AFM_KernPair kp1 = (AFM_KernPair)a; |
723 | AFM_KernPair kp2 = (AFM_KernPair)b; |
724 | |
725 | FT_ULong index1 = KERN_INDEX( kp1->index1, kp1->index2 ); |
726 | FT_ULong index2 = KERN_INDEX( kp2->index1, kp2->index2 ); |
727 | |
728 | |
729 | if ( index1 > index2 ) |
730 | return 1; |
731 | else if ( index1 < index2 ) |
732 | return -1; |
733 | else |
734 | return 0; |
735 | } |
736 | |
737 | |
738 | static FT_Error |
739 | afm_parse_kern_pairs( AFM_Parser parser ) |
740 | { |
741 | AFM_FontInfo fi = parser->FontInfo; |
742 | AFM_Stream stream = parser->stream; |
743 | AFM_KernPair kp; |
744 | char* key; |
745 | FT_Offset len; |
746 | int n = -1; |
747 | FT_Int tmp; |
748 | |
749 | |
750 | if ( afm_parser_read_int( parser, &tmp ) ) |
751 | goto Fail; |
752 | |
753 | if ( tmp < 0 ) |
754 | { |
755 | FT_ERROR(( "afm_parse_kern_pairs: invalid number of kern pairs\n" )); |
756 | goto Fail; |
757 | } |
758 | |
759 | fi->NumKernPair = (FT_UInt)tmp; |
760 | FT_TRACE3(( "afm_parse_kern_pairs: %u kern pair%s expected\n" , |
761 | fi->NumKernPair, |
762 | fi->NumKernPair == 1 ? "" : "s" )); |
763 | |
764 | /* Rough sanity check: The minimum line length of the `KP`, */ |
765 | /* `KPH`,`KPX`, and `KPY` commands is 10 characters (including */ |
766 | /* the EOL character). */ |
767 | if ( (FT_ULong)( stream->limit - stream->cursor ) / 10 < |
768 | fi->NumKernPair ) |
769 | { |
770 | FT_ERROR(( "afm_parse_kern_pairs:" |
771 | " number of kern pairs exceeds stream size\n" )); |
772 | goto Fail; |
773 | } |
774 | |
775 | if ( fi->NumKernPair ) |
776 | { |
777 | FT_Memory memory = parser->memory; |
778 | FT_Error error; |
779 | |
780 | |
781 | if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) ) |
782 | return error; |
783 | } |
784 | |
785 | while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 ) |
786 | { |
787 | AFM_Token token = afm_tokenize( key, len ); |
788 | |
789 | |
790 | switch ( token ) |
791 | { |
792 | case AFM_TOKEN_KP: |
793 | case AFM_TOKEN_KPX: |
794 | case AFM_TOKEN_KPY: |
795 | { |
796 | FT_Int r; |
797 | AFM_ValueRec shared_vals[4]; |
798 | |
799 | |
800 | n++; |
801 | |
802 | if ( n >= (int)fi->NumKernPair ) |
803 | { |
804 | FT_ERROR(( "afm_parse_kern_pairs: too many kern pairs\n" )); |
805 | goto Fail; |
806 | } |
807 | |
808 | kp = fi->KernPairs + n; |
809 | |
810 | shared_vals[0].type = AFM_VALUE_TYPE_INDEX; |
811 | shared_vals[1].type = AFM_VALUE_TYPE_INDEX; |
812 | shared_vals[2].type = AFM_VALUE_TYPE_INTEGER; |
813 | shared_vals[3].type = AFM_VALUE_TYPE_INTEGER; |
814 | r = afm_parser_read_vals( parser, shared_vals, 4 ); |
815 | if ( r < 3 ) |
816 | { |
817 | FT_ERROR(( "afm_parse_kern_pairs:" |
818 | " insufficient number of parameters for entry %d\n" , |
819 | n )); |
820 | goto Fail; |
821 | } |
822 | |
823 | /* index values can't be negative */ |
824 | kp->index1 = shared_vals[0].u.u; |
825 | kp->index2 = shared_vals[1].u.u; |
826 | if ( token == AFM_TOKEN_KPY ) |
827 | { |
828 | kp->x = 0; |
829 | kp->y = shared_vals[2].u.i; |
830 | } |
831 | else |
832 | { |
833 | kp->x = shared_vals[2].u.i; |
834 | kp->y = ( token == AFM_TOKEN_KP && r == 4 ) |
835 | ? shared_vals[3].u.i : 0; |
836 | } |
837 | } |
838 | break; |
839 | |
840 | case AFM_TOKEN_ENDKERNPAIRS: |
841 | case AFM_TOKEN_ENDKERNDATA: |
842 | case AFM_TOKEN_ENDFONTMETRICS: |
843 | tmp = n + 1; |
844 | if ( (FT_UInt)tmp != fi->NumKernPair ) |
845 | { |
846 | FT_TRACE1(( "afm_parse_kern_pairs: %s%d kern pair%s seen\n" , |
847 | tmp == 0 ? "" : "only " , |
848 | tmp, |
849 | tmp == 1 ? "" : "s" )); |
850 | fi->NumKernPair = (FT_UInt)tmp; |
851 | } |
852 | else |
853 | FT_TRACE3(( "afm_parse_kern_pairs: %d kern pair%s seen\n" , |
854 | tmp, |
855 | tmp == 1 ? "" : "s" )); |
856 | |
857 | ft_qsort( fi->KernPairs, fi->NumKernPair, |
858 | sizeof ( AFM_KernPairRec ), |
859 | afm_compare_kern_pairs ); |
860 | return FT_Err_Ok; |
861 | |
862 | case AFM_TOKEN_UNKNOWN: |
863 | break; |
864 | |
865 | default: |
866 | goto Fail; |
867 | } |
868 | } |
869 | |
870 | Fail: |
871 | return FT_THROW( Syntax_Error ); |
872 | } |
873 | |
874 | |
875 | static FT_Error |
876 | afm_parse_kern_data( AFM_Parser parser ) |
877 | { |
878 | FT_Error error; |
879 | char* key; |
880 | FT_Offset len; |
881 | |
882 | int have_trackkern = 0; |
883 | int have_kernpairs = 0; |
884 | |
885 | |
886 | while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 ) |
887 | { |
888 | switch ( afm_tokenize( key, len ) ) |
889 | { |
890 | case AFM_TOKEN_STARTTRACKKERN: |
891 | if ( have_trackkern ) |
892 | { |
893 | FT_ERROR(( "afm_parse_kern_data:" |
894 | " invalid second horizontal track kern section\n" )); |
895 | goto Fail; |
896 | } |
897 | |
898 | error = afm_parse_track_kern( parser ); |
899 | if ( error ) |
900 | return error; |
901 | |
902 | have_trackkern = 1; |
903 | break; |
904 | |
905 | case AFM_TOKEN_STARTKERNPAIRS: |
906 | case AFM_TOKEN_STARTKERNPAIRS0: |
907 | if ( have_kernpairs ) |
908 | { |
909 | FT_ERROR(( "afm_parse_kern_data:" |
910 | " invalid second horizontal kern pair section\n" )); |
911 | goto Fail; |
912 | } |
913 | |
914 | error = afm_parse_kern_pairs( parser ); |
915 | if ( error ) |
916 | return error; |
917 | |
918 | have_kernpairs = 1; |
919 | break; |
920 | |
921 | case AFM_TOKEN_ENDKERNDATA: |
922 | case AFM_TOKEN_ENDFONTMETRICS: |
923 | return FT_Err_Ok; |
924 | |
925 | case AFM_TOKEN_UNKNOWN: |
926 | break; |
927 | |
928 | default: |
929 | goto Fail; |
930 | } |
931 | } |
932 | |
933 | Fail: |
934 | return FT_THROW( Syntax_Error ); |
935 | } |
936 | |
937 | |
938 | static FT_Error |
939 | afm_parser_skip_section( AFM_Parser parser, |
940 | FT_Int n, |
941 | AFM_Token end_section ) |
942 | { |
943 | char* key; |
944 | FT_Offset len; |
945 | |
946 | |
947 | while ( n-- > 0 ) |
948 | { |
949 | key = afm_parser_next_key( parser, 1, NULL ); |
950 | if ( !key ) |
951 | goto Fail; |
952 | } |
953 | |
954 | while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 ) |
955 | { |
956 | AFM_Token token = afm_tokenize( key, len ); |
957 | |
958 | |
959 | if ( token == end_section || token == AFM_TOKEN_ENDFONTMETRICS ) |
960 | return FT_Err_Ok; |
961 | } |
962 | |
963 | Fail: |
964 | return FT_THROW( Syntax_Error ); |
965 | } |
966 | |
967 | |
968 | FT_LOCAL_DEF( FT_Error ) |
969 | afm_parser_parse( AFM_Parser parser ) |
970 | { |
971 | FT_Memory memory = parser->memory; |
972 | AFM_FontInfo fi = parser->FontInfo; |
973 | FT_Error error = FT_ERR( Syntax_Error ); |
974 | char* key; |
975 | FT_Offset len; |
976 | FT_Int metrics_sets = 0; |
977 | |
978 | |
979 | if ( !fi ) |
980 | return FT_THROW( Invalid_Argument ); |
981 | |
982 | key = afm_parser_next_key( parser, 1, &len ); |
983 | if ( !key || len != 16 || |
984 | ft_strncmp( key, "StartFontMetrics" , 16 ) != 0 ) |
985 | return FT_THROW( Unknown_File_Format ); |
986 | |
987 | while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 ) |
988 | { |
989 | AFM_ValueRec shared_vals[4]; |
990 | |
991 | |
992 | switch ( afm_tokenize( key, len ) ) |
993 | { |
994 | case AFM_TOKEN_METRICSSETS: |
995 | if ( afm_parser_read_int( parser, &metrics_sets ) ) |
996 | goto Fail; |
997 | |
998 | if ( metrics_sets != 0 && metrics_sets != 2 ) |
999 | { |
1000 | error = FT_THROW( Unimplemented_Feature ); |
1001 | |
1002 | goto Fail; |
1003 | } |
1004 | break; |
1005 | |
1006 | case AFM_TOKEN_ISCIDFONT: |
1007 | shared_vals[0].type = AFM_VALUE_TYPE_BOOL; |
1008 | if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 ) |
1009 | goto Fail; |
1010 | |
1011 | fi->IsCIDFont = shared_vals[0].u.b; |
1012 | break; |
1013 | |
1014 | case AFM_TOKEN_FONTBBOX: |
1015 | shared_vals[0].type = AFM_VALUE_TYPE_FIXED; |
1016 | shared_vals[1].type = AFM_VALUE_TYPE_FIXED; |
1017 | shared_vals[2].type = AFM_VALUE_TYPE_FIXED; |
1018 | shared_vals[3].type = AFM_VALUE_TYPE_FIXED; |
1019 | if ( afm_parser_read_vals( parser, shared_vals, 4 ) != 4 ) |
1020 | goto Fail; |
1021 | |
1022 | fi->FontBBox.xMin = shared_vals[0].u.f; |
1023 | fi->FontBBox.yMin = shared_vals[1].u.f; |
1024 | fi->FontBBox.xMax = shared_vals[2].u.f; |
1025 | fi->FontBBox.yMax = shared_vals[3].u.f; |
1026 | break; |
1027 | |
1028 | case AFM_TOKEN_ASCENDER: |
1029 | shared_vals[0].type = AFM_VALUE_TYPE_FIXED; |
1030 | if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 ) |
1031 | goto Fail; |
1032 | |
1033 | fi->Ascender = shared_vals[0].u.f; |
1034 | break; |
1035 | |
1036 | case AFM_TOKEN_DESCENDER: |
1037 | shared_vals[0].type = AFM_VALUE_TYPE_FIXED; |
1038 | if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 ) |
1039 | goto Fail; |
1040 | |
1041 | fi->Descender = shared_vals[0].u.f; |
1042 | break; |
1043 | |
1044 | case AFM_TOKEN_STARTCHARMETRICS: |
1045 | { |
1046 | FT_Int n = 0; |
1047 | |
1048 | |
1049 | if ( afm_parser_read_int( parser, &n ) ) |
1050 | goto Fail; |
1051 | |
1052 | error = afm_parser_skip_section( parser, n, |
1053 | AFM_TOKEN_ENDCHARMETRICS ); |
1054 | if ( error ) |
1055 | return error; |
1056 | } |
1057 | break; |
1058 | |
1059 | case AFM_TOKEN_STARTKERNDATA: |
1060 | error = afm_parse_kern_data( parser ); |
1061 | if ( error ) |
1062 | goto Fail; |
1063 | /* we only support kern data, so ... */ |
1064 | FALL_THROUGH; |
1065 | |
1066 | case AFM_TOKEN_ENDFONTMETRICS: |
1067 | return FT_Err_Ok; |
1068 | |
1069 | default: |
1070 | break; |
1071 | } |
1072 | } |
1073 | |
1074 | Fail: |
1075 | FT_FREE( fi->TrackKerns ); |
1076 | fi->NumTrackKern = 0; |
1077 | |
1078 | FT_FREE( fi->KernPairs ); |
1079 | fi->NumKernPair = 0; |
1080 | |
1081 | fi->IsCIDFont = 0; |
1082 | |
1083 | return error; |
1084 | } |
1085 | |
1086 | #else /* T1_CONFIG_OPTION_NO_AFM */ |
1087 | |
1088 | /* ANSI C doesn't like empty source files */ |
1089 | typedef int afm_parse_dummy_; |
1090 | |
1091 | #endif /* T1_CONFIG_OPTION_NO_AFM */ |
1092 | |
1093 | |
1094 | /* END */ |
1095 | |