Bringing apdf from vendor into main branch.
[AROS-Contrib.git] / apdf / freetype2 / type1 / t1load.c
blobcbacdba572202a27b7995a24ee612f7288077b48
1 /***************************************************************************/
2 /* */
3 /* t1load.c */
4 /* */
5 /* Type 1 font loader (body). */
6 /* */
7 /* Copyright 1996-2001, 2002 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 /***************************************************************************/
19 /*************************************************************************/
20 /* */
21 /* This is the new and improved Type 1 data loader for FreeType 2. The */
22 /* old loader has several problems: it is slow, complex, difficult to */
23 /* maintain, and contains incredible hacks to make it accept some */
24 /* ill-formed Type 1 fonts without hiccup-ing. Moreover, about 5% of */
25 /* the Type 1 fonts on my machine still aren't loaded correctly by it. */
26 /* */
27 /* This version is much simpler, much faster and also easier to read and */
28 /* maintain by a great order of magnitude. The idea behind it is to */
29 /* _not_ try to read the Type 1 token stream with a state machine (i.e. */
30 /* a Postscript-like interpreter) but rather to perform simple pattern */
31 /* matching. */
32 /* */
33 /* Indeed, nearly all data definitions follow a simple pattern like */
34 /* */
35 /* ... /Field <data> ... */
36 /* */
37 /* where <data> can be a number, a boolean, a string, or an array of */
38 /* numbers. There are a few exceptions, namely the encoding, font name, */
39 /* charstrings, and subrs; they are handled with a special pattern */
40 /* matching routine. */
41 /* */
42 /* All other common cases are handled very simply. The matching rules */
43 /* are defined in the file `t1tokens.h' through the use of several */
44 /* macros calls PARSE_XXX. */
45 /* */
46 /* This file is included twice here; the first time to generate parsing */
47 /* callback functions, the second to generate a table of keywords (with */
48 /* pointers to the associated callback). */
49 /* */
50 /* The function `parse_dict' simply scans *linearly* a given dictionary */
51 /* (either the top-level or private one) and calls the appropriate */
52 /* callback when it encounters an immediate keyword. */
53 /* */
54 /* This is by far the fastest way one can find to parse and read all */
55 /* data. */
56 /* */
57 /* This led to tremendous code size reduction. Note that later, the */
58 /* glyph loader will also be _greatly_ simplified, and the automatic */
59 /* hinter will replace the clumsy `t1hinter'. */
60 /* */
61 /*************************************************************************/
64 #include <ft2build.h>
65 #include FT_INTERNAL_DEBUG_H
66 #include FT_CONFIG_CONFIG_H
67 #include FT_MULTIPLE_MASTERS_H
68 #include FT_INTERNAL_TYPE1_TYPES_H
70 #include "t1load.h"
71 #include "t1errors.h"
75 /*************************************************************************/
76 /* */
77 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
78 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
79 /* messages during execution. */
80 /* */
81 #undef FT_COMPONENT
82 #define FT_COMPONENT trace_t1load
85 #ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
88 /*************************************************************************/
89 /*************************************************************************/
90 /***** *****/
91 /***** MULTIPLE MASTERS SUPPORT *****/
92 /***** *****/
93 /*************************************************************************/
94 /*************************************************************************/
96 static FT_Error
97 t1_allocate_blend( T1_Face face,
98 FT_UInt num_designs,
99 FT_UInt num_axis )
101 PS_Blend blend;
102 FT_Memory memory = face->root.memory;
103 FT_Error error = 0;
106 blend = face->blend;
107 if ( !blend )
109 if ( FT_NEW( blend ) )
110 goto Exit;
112 face->blend = blend;
115 /* allocate design data if needed */
116 if ( num_designs > 0 )
118 if ( blend->num_designs == 0 )
120 FT_UInt nn;
123 /* allocate the blend `private' and `font_info' dictionaries */
124 if ( FT_NEW_ARRAY( blend->font_infos[1], num_designs ) ||
125 FT_NEW_ARRAY( blend->privates[1], num_designs ) ||
126 FT_NEW_ARRAY( blend->weight_vector, num_designs * 2 ) )
127 goto Exit;
129 blend->default_weight_vector = blend->weight_vector + num_designs;
131 blend->font_infos[0] = &face->type1.font_info;
132 blend->privates [0] = &face->type1.private_dict;
134 for ( nn = 2; nn <= num_designs; nn++ )
136 blend->privates[nn] = blend->privates [nn - 1] + 1;
137 blend->font_infos[nn] = blend->font_infos[nn - 1] + 1;
140 blend->num_designs = num_designs;
142 else if ( blend->num_designs != num_designs )
143 goto Fail;
146 /* allocate axis data if needed */
147 if ( num_axis > 0 )
149 if ( blend->num_axis != 0 && blend->num_axis != num_axis )
150 goto Fail;
152 blend->num_axis = num_axis;
155 /* allocate the blend design pos table if needed */
156 num_designs = blend->num_designs;
157 num_axis = blend->num_axis;
158 if ( num_designs && num_axis && blend->design_pos[0] == 0 )
160 FT_UInt n;
163 if ( FT_NEW_ARRAY( blend->design_pos[0], num_designs * num_axis ) )
164 goto Exit;
166 for ( n = 1; n < num_designs; n++ )
167 blend->design_pos[n] = blend->design_pos[0] + num_axis * n;
170 Exit:
171 return error;
173 Fail:
174 error = -1;
175 goto Exit;
179 FT_LOCAL_DEF( FT_Error )
180 T1_Get_Multi_Master( T1_Face face,
181 FT_Multi_Master* master )
183 PS_Blend blend = face->blend;
184 FT_UInt n;
185 FT_Error error;
188 error = T1_Err_Invalid_Argument;
190 if ( blend )
192 master->num_axis = blend->num_axis;
193 master->num_designs = blend->num_designs;
195 for ( n = 0; n < blend->num_axis; n++ )
197 FT_MM_Axis* axis = master->axis + n;
198 PS_DesignMap map = blend->design_map + n;
201 axis->name = blend->axis_names[n];
202 axis->minimum = map->design_points[0];
203 axis->maximum = map->design_points[map->num_points - 1];
205 error = 0;
207 return error;
211 FT_LOCAL_DEF( FT_Error )
212 T1_Set_MM_Blend( T1_Face face,
213 FT_UInt num_coords,
214 FT_Fixed* coords )
216 PS_Blend blend = face->blend;
217 FT_Error error;
218 FT_UInt n, m;
221 error = T1_Err_Invalid_Argument;
223 if ( blend && blend->num_axis == num_coords )
225 /* recompute the weight vector from the blend coordinates */
226 error = T1_Err_Ok;
228 for ( n = 0; n < blend->num_designs; n++ )
230 FT_Fixed result = 0x10000L; /* 1.0 fixed */
233 for ( m = 0; m < blend->num_axis; m++ )
235 FT_Fixed factor;
238 /* get current blend axis position */
239 factor = coords[m];
240 if ( factor < 0 ) factor = 0;
241 if ( factor > 0x10000L ) factor = 0x10000L;
243 if ( ( n & ( 1 << m ) ) == 0 )
244 factor = 0x10000L - factor;
246 result = FT_MulFix( result, factor );
248 blend->weight_vector[n] = result;
251 error = T1_Err_Ok;
253 return error;
257 FT_LOCAL_DEF( FT_Error )
258 T1_Set_MM_Design( T1_Face face,
259 FT_UInt num_coords,
260 FT_Long* coords )
262 PS_Blend blend = face->blend;
263 FT_Error error;
264 FT_UInt n, p;
267 error = T1_Err_Invalid_Argument;
268 if ( blend && blend->num_axis == num_coords )
270 /* compute the blend coordinates through the blend design map */
271 FT_Fixed final_blends[T1_MAX_MM_DESIGNS];
274 for ( n = 0; n < blend->num_axis; n++ )
276 FT_Long design = coords[n];
277 FT_Fixed the_blend;
278 PS_DesignMap map = blend->design_map + n;
279 FT_Fixed* designs = map->design_points;
280 FT_Fixed* blends = map->blend_points;
281 FT_Int before = -1, after = -1;
284 for ( p = 0; p < (FT_UInt)map->num_points; p++ )
286 FT_Fixed p_design = designs[p];
289 /* exact match ? */
290 if ( design == p_design )
292 the_blend = blends[p];
293 goto Found;
296 if ( design < p_design )
298 after = p;
299 break;
302 before = p;
305 /* now, interpolate if needed */
306 if ( before < 0 )
307 the_blend = blends[0];
309 else if ( after < 0 )
310 the_blend = blends[map->num_points - 1];
312 else
313 the_blend = FT_MulDiv( design - designs[before],
314 blends [after] - blends [before],
315 designs[after] - designs[before] );
317 Found:
318 final_blends[n] = the_blend;
321 error = T1_Set_MM_Blend( face, num_coords, final_blends );
324 return error;
328 FT_LOCAL_DEF( void )
329 T1_Done_Blend( T1_Face face )
331 FT_Memory memory = face->root.memory;
332 PS_Blend blend = face->blend;
335 if ( blend )
337 FT_UInt num_designs = blend->num_designs;
338 FT_UInt num_axis = blend->num_axis;
339 FT_UInt n;
342 /* release design pos table */
343 FT_FREE( blend->design_pos[0] );
344 for ( n = 1; n < num_designs; n++ )
345 blend->design_pos[n] = 0;
347 /* release blend `private' and `font info' dictionaries */
348 FT_FREE( blend->privates[1] );
349 FT_FREE( blend->font_infos[1] );
351 for ( n = 0; n < num_designs; n++ )
353 blend->privates [n] = 0;
354 blend->font_infos[n] = 0;
357 /* release weight vectors */
358 FT_FREE( blend->weight_vector );
359 blend->default_weight_vector = 0;
361 /* release axis names */
362 for ( n = 0; n < num_axis; n++ )
363 FT_FREE( blend->axis_names[n] );
365 /* release design map */
366 for ( n = 0; n < num_axis; n++ )
368 PS_DesignMap dmap = blend->design_map + n;
371 FT_FREE( dmap->design_points );
372 dmap->num_points = 0;
375 FT_FREE( face->blend );
380 static void
381 parse_blend_axis_types( T1_Face face,
382 T1_Loader loader )
384 T1_TokenRec axis_tokens[ T1_MAX_MM_AXIS ];
385 FT_Int n, num_axis;
386 FT_Error error = 0;
387 PS_Blend blend;
388 FT_Memory memory;
391 /* take an array of objects */
392 T1_ToTokenArray( &loader->parser, axis_tokens,
393 T1_MAX_MM_AXIS, &num_axis );
394 if ( num_axis <= 0 || num_axis > T1_MAX_MM_AXIS )
396 FT_ERROR(( "parse_blend_axis_types: incorrect number of axes: %d\n",
397 num_axis ));
398 error = T1_Err_Invalid_File_Format;
399 goto Exit;
402 /* allocate blend if necessary */
403 error = t1_allocate_blend( face, 0, (FT_UInt)num_axis );
404 if ( error )
405 goto Exit;
407 blend = face->blend;
408 memory = face->root.memory;
410 /* each token is an immediate containing the name of the axis */
411 for ( n = 0; n < num_axis; n++ )
413 T1_Token token = axis_tokens + n;
414 FT_Byte* name;
415 FT_Int len;
418 /* skip first slash, if any */
419 if ( token->start[0] == '/' )
420 token->start++;
422 len = (FT_Int)( token->limit - token->start );
423 if ( len <= 0 )
425 error = T1_Err_Invalid_File_Format;
426 goto Exit;
429 if ( FT_ALLOC( blend->axis_names[n], len + 1 ) )
430 goto Exit;
432 name = (FT_Byte*)blend->axis_names[n];
433 FT_MEM_COPY( name, token->start, len );
434 name[len] = 0;
437 Exit:
438 loader->parser.root.error = error;
442 static void
443 parse_blend_design_positions( T1_Face face,
444 T1_Loader loader )
446 T1_TokenRec design_tokens[ T1_MAX_MM_DESIGNS ];
447 FT_Int num_designs;
448 FT_Int num_axis;
449 T1_Parser parser = &loader->parser;
451 FT_Error error = 0;
452 PS_Blend blend;
455 /* get the array of design tokens - compute number of designs */
456 T1_ToTokenArray( parser, design_tokens, T1_MAX_MM_DESIGNS, &num_designs );
457 if ( num_designs <= 0 || num_designs > T1_MAX_MM_DESIGNS )
459 FT_ERROR(( "parse_blend_design_positions:" ));
460 FT_ERROR(( " incorrect number of designs: %d\n",
461 num_designs ));
462 error = T1_Err_Invalid_File_Format;
463 goto Exit;
467 FT_Byte* old_cursor = parser->root.cursor;
468 FT_Byte* old_limit = parser->root.limit;
469 FT_UInt n;
472 blend = face->blend;
473 num_axis = 0; /* make compiler happy */
475 for ( n = 0; n < (FT_UInt)num_designs; n++ )
477 T1_TokenRec axis_tokens[ T1_MAX_MM_DESIGNS ];
478 T1_Token token;
479 FT_Int axis, n_axis;
482 /* read axis/coordinates tokens */
483 token = design_tokens + n;
484 parser->root.cursor = token->start - 1;
485 parser->root.limit = token->limit + 1;
486 T1_ToTokenArray( parser, axis_tokens, T1_MAX_MM_AXIS, &n_axis );
488 if ( n == 0 )
490 num_axis = n_axis;
491 error = t1_allocate_blend( face, num_designs, num_axis );
492 if ( error )
493 goto Exit;
494 blend = face->blend;
496 else if ( n_axis != num_axis )
498 FT_ERROR(( "parse_blend_design_positions: incorrect table\n" ));
499 error = T1_Err_Invalid_File_Format;
500 goto Exit;
503 /* now, read each axis token into the design position */
504 for ( axis = 0; axis < n_axis; axis++ )
506 T1_Token token2 = axis_tokens + axis;
509 parser->root.cursor = token2->start;
510 parser->root.limit = token2->limit;
511 blend->design_pos[n][axis] = T1_ToFixed( parser, 0 );
515 loader->parser.root.cursor = old_cursor;
516 loader->parser.root.limit = old_limit;
519 Exit:
520 loader->parser.root.error = error;
524 static void
525 parse_blend_design_map( T1_Face face,
526 T1_Loader loader )
528 FT_Error error = 0;
529 T1_Parser parser = &loader->parser;
530 PS_Blend blend;
531 T1_TokenRec axis_tokens[T1_MAX_MM_AXIS];
532 FT_Int n, num_axis;
533 FT_Byte* old_cursor;
534 FT_Byte* old_limit;
535 FT_Memory memory = face->root.memory;
538 T1_ToTokenArray( parser, axis_tokens, T1_MAX_MM_AXIS, &num_axis );
539 if ( num_axis <= 0 || num_axis > T1_MAX_MM_AXIS )
541 FT_ERROR(( "parse_blend_design_map: incorrect number of axes: %d\n",
542 num_axis ));
543 error = T1_Err_Invalid_File_Format;
544 goto Exit;
546 old_cursor = parser->root.cursor;
547 old_limit = parser->root.limit;
549 error = t1_allocate_blend( face, 0, num_axis );
550 if ( error )
551 goto Exit;
552 blend = face->blend;
554 /* now, read each axis design map */
555 for ( n = 0; n < num_axis; n++ )
557 PS_DesignMap map = blend->design_map + n;
558 T1_Token token;
559 FT_Int p, num_points;
562 token = axis_tokens + n;
563 parser->root.cursor = token->start;
564 parser->root.limit = token->limit;
566 /* count the number of map points */
568 FT_Byte* ptr = token->start;
569 FT_Byte* limit = token->limit;
572 num_points = 0;
573 for ( ; ptr < limit; ptr++ )
574 if ( ptr[0] == '[' )
575 num_points++;
577 if ( num_points <= 0 || num_points > T1_MAX_MM_MAP_POINTS )
579 FT_ERROR(( "parse_blend_design_map: incorrect table\n" ));
580 error = T1_Err_Invalid_File_Format;
581 goto Exit;
584 /* allocate design map data */
585 if ( FT_NEW_ARRAY( map->design_points, num_points * 2 ) )
586 goto Exit;
587 map->blend_points = map->design_points + num_points;
588 map->num_points = (FT_Byte)num_points;
590 for ( p = 0; p < num_points; p++ )
592 map->design_points[p] = T1_ToInt( parser );
593 map->blend_points [p] = T1_ToFixed( parser, 0 );
597 parser->root.cursor = old_cursor;
598 parser->root.limit = old_limit;
600 Exit:
601 parser->root.error = error;
605 static void
606 parse_weight_vector( T1_Face face,
607 T1_Loader loader )
609 FT_Error error = 0;
610 T1_Parser parser = &loader->parser;
611 PS_Blend blend = face->blend;
612 T1_TokenRec master;
613 FT_UInt n;
614 FT_Byte* old_cursor;
615 FT_Byte* old_limit;
618 if ( !blend || blend->num_designs == 0 )
620 FT_ERROR(( "parse_weight_vector: too early!\n" ));
621 error = T1_Err_Invalid_File_Format;
622 goto Exit;
625 T1_ToToken( parser, &master );
626 if ( master.type != T1_TOKEN_TYPE_ARRAY )
628 FT_ERROR(( "parse_weight_vector: incorrect format!\n" ));
629 error = T1_Err_Invalid_File_Format;
630 goto Exit;
633 old_cursor = parser->root.cursor;
634 old_limit = parser->root.limit;
636 parser->root.cursor = master.start;
637 parser->root.limit = master.limit;
639 for ( n = 0; n < blend->num_designs; n++ )
641 blend->default_weight_vector[n] =
642 blend->weight_vector[n] = T1_ToFixed( parser, 0 );
645 parser->root.cursor = old_cursor;
646 parser->root.limit = old_limit;
648 Exit:
649 parser->root.error = error;
653 /* the keyword `/shareddict' appears in some multiple master fonts */
654 /* with a lot of Postscript garbage behind it (that's completely out */
655 /* of spec!); we detect it and terminate the parsing */
656 /* */
657 static void
658 parse_shared_dict( T1_Face face,
659 T1_Loader loader )
661 T1_Parser parser = &loader->parser;
663 FT_UNUSED( face );
666 parser->root.cursor = parser->root.limit;
667 parser->root.error = 0;
670 #endif /* T1_CONFIG_OPTION_NO_MM_SUPPORT */
673 /*************************************************************************/
674 /*************************************************************************/
675 /***** *****/
676 /***** TYPE 1 SYMBOL PARSING *****/
677 /***** *****/
678 /*************************************************************************/
679 /*************************************************************************/
682 /*************************************************************************/
683 /* */
684 /* First of all, define the token field static variables. This is a set */
685 /* of T1_FieldRec variables used later. */
686 /* */
687 /*************************************************************************/
690 static FT_Error
691 t1_load_keyword( T1_Face face,
692 T1_Loader loader,
693 T1_Field field )
695 FT_Error error;
696 void* dummy_object;
697 void** objects;
698 FT_UInt max_objects;
699 PS_Blend blend = face->blend;
702 /* if the keyword has a dedicated callback, call it */
703 if ( field->type == T1_FIELD_TYPE_CALLBACK )
705 field->reader( (FT_Face)face, loader );
706 error = loader->parser.root.error;
707 goto Exit;
710 /* now, the keyword is either a simple field, or a table of fields; */
711 /* we are now going to take care of it */
712 switch ( field->location )
714 case T1_FIELD_LOCATION_FONT_INFO:
715 dummy_object = &face->type1.font_info;
716 objects = &dummy_object;
717 max_objects = 0;
719 if ( blend )
721 objects = (void**)blend->font_infos;
722 max_objects = blend->num_designs;
724 break;
726 case T1_FIELD_LOCATION_PRIVATE:
727 dummy_object = &face->type1.private_dict;
728 objects = &dummy_object;
729 max_objects = 0;
731 if ( blend )
733 objects = (void**)blend->privates;
734 max_objects = blend->num_designs;
736 break;
738 default:
739 dummy_object = &face->type1;
740 objects = &dummy_object;
741 max_objects = 0;
744 if ( field->type == T1_FIELD_TYPE_INTEGER_ARRAY ||
745 field->type == T1_FIELD_TYPE_FIXED_ARRAY )
746 error = T1_Load_Field_Table( &loader->parser, field,
747 objects, max_objects, 0 );
748 else
749 error = T1_Load_Field( &loader->parser, field,
750 objects, max_objects, 0 );
752 Exit:
753 return error;
757 static int
758 is_space( FT_Byte c )
760 return ( c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0' || c == '\f' );
764 static int
765 is_alpha( FT_Byte c )
767 /* Note: we must accept "+" as a valid character, as it is used in */
768 /* embedded type1 fonts in PDF documents. */
769 /* */
770 return ( ft_isalnum( c ) ||
771 c == '.' ||
772 c == '_' ||
773 c == '-' ||
774 c == '+' );
778 static int
779 read_binary_data( T1_Parser parser,
780 FT_Int* size,
781 FT_Byte** base )
783 FT_Byte* cur;
784 FT_Byte* limit = parser->root.limit;
787 /* the binary data has the following format */
788 /* */
789 /* `size' [white*] RD white ....... ND */
790 /* */
792 T1_Skip_Spaces( parser );
793 cur = parser->root.cursor;
795 if ( cur < limit && (FT_Byte)( *cur - '0' ) < 10 )
797 *size = T1_ToInt( parser );
799 T1_Skip_Spaces( parser );
800 T1_Skip_Alpha ( parser ); /* `RD' or `-|' or something else */
802 /* there is only one whitespace char after the */
803 /* `RD' or `-|' token */
804 *base = parser->root.cursor + 1;
806 parser->root.cursor += *size + 1;
807 return 1;
810 FT_ERROR(( "read_binary_data: invalid size field\n" ));
811 parser->root.error = T1_Err_Invalid_File_Format;
812 return 0;
816 /* we will now define the routines used to handle */
817 /* the `/Encoding', `/Subrs', and `/CharStrings' */
818 /* dictionaries */
820 static void
821 parse_font_name( T1_Face face,
822 T1_Loader loader )
824 T1_Parser parser = &loader->parser;
825 FT_Error error;
826 FT_Memory memory = parser->root.memory;
827 FT_Int len;
828 FT_Byte* cur;
829 FT_Byte* cur2;
830 FT_Byte* limit;
833 if ( face->type1.font_name )
834 /* with synthetic fonts, it's possible we get here twice */
835 return;
837 T1_Skip_Spaces( parser );
839 cur = parser->root.cursor;
840 limit = parser->root.limit;
842 if ( cur >= limit - 1 || *cur != '/' )
843 return;
845 cur++;
846 cur2 = cur;
847 while ( cur2 < limit && is_alpha( *cur2 ) )
848 cur2++;
850 len = (FT_Int)( cur2 - cur );
851 if ( len > 0 )
853 if ( FT_ALLOC( face->type1.font_name, len + 1 ) )
855 parser->root.error = error;
856 return;
859 FT_MEM_COPY( face->type1.font_name, cur, len );
860 face->type1.font_name[len] = '\0';
862 parser->root.cursor = cur2;
866 static void
867 parse_font_bbox( T1_Face face,
868 T1_Loader loader )
870 T1_Parser parser = &loader->parser;
871 FT_Fixed temp[4];
872 FT_BBox* bbox = &face->type1.font_bbox;
875 (void)T1_ToFixedArray( parser, 4, temp, 0 );
876 bbox->xMin = FT_RoundFix( temp[0] );
877 bbox->yMin = FT_RoundFix( temp[1] );
878 bbox->xMax = FT_RoundFix( temp[2] );
879 bbox->yMax = FT_RoundFix( temp[3] );
883 static void
884 parse_font_matrix( T1_Face face,
885 T1_Loader loader )
887 T1_Parser parser = &loader->parser;
888 FT_Matrix* matrix = &face->type1.font_matrix;
889 FT_Vector* offset = &face->type1.font_offset;
890 FT_Face root = (FT_Face)&face->root;
891 FT_Fixed temp[6];
892 FT_Fixed temp_scale;
895 if ( matrix->xx || matrix->yx )
896 /* with synthetic fonts, it's possible we get here twice */
897 return;
899 (void)T1_ToFixedArray( parser, 6, temp, 3 );
901 temp_scale = ABS( temp[3] );
903 /* Set Units per EM based on FontMatrix values. We set the value to */
904 /* 1000 / temp_scale, because temp_scale was already multiplied by */
905 /* 1000 (in t1_tofixed, from psobjs.c). */
907 root->units_per_EM = (FT_UShort)( FT_DivFix( 1000 * 0x10000L,
908 temp_scale ) >> 16 );
910 /* we need to scale the values by 1.0/temp_scale */
911 if ( temp_scale != 0x10000L )
913 temp[0] = FT_DivFix( temp[0], temp_scale );
914 temp[1] = FT_DivFix( temp[1], temp_scale );
915 temp[2] = FT_DivFix( temp[2], temp_scale );
916 temp[4] = FT_DivFix( temp[4], temp_scale );
917 temp[5] = FT_DivFix( temp[5], temp_scale );
918 temp[3] = 0x10000L;
921 matrix->xx = temp[0];
922 matrix->yx = temp[2]; //Emm: was 1
923 matrix->xy = temp[1]; //Emm: was 2
924 matrix->yy = temp[3];
926 /* note that the offsets must be expressed in integer font units */
927 offset->x = temp[4] >> 16;
928 offset->y = temp[5] >> 16;
932 static void
933 parse_encoding( T1_Face face,
934 T1_Loader loader )
936 T1_Parser parser = &loader->parser;
937 FT_Byte* cur = parser->root.cursor;
938 FT_Byte* limit = parser->root.limit;
940 PSAux_Service psaux = (PSAux_Service)face->psaux;
943 /* skip whitespace */
944 while ( is_space( *cur ) )
946 cur++;
947 if ( cur >= limit )
949 FT_ERROR(( "parse_encoding: out of bounds!\n" ));
950 parser->root.error = T1_Err_Invalid_File_Format;
951 return;
955 /* if we have a number, then the encoding is an array, */
956 /* and we must load it now */
957 if ( (FT_Byte)( *cur - '0' ) < 10 )
959 T1_Encoding encode = &face->type1.encoding;
960 FT_Int count, n;
961 PS_Table char_table = &loader->encoding_table;
962 FT_Memory memory = parser->root.memory;
963 FT_Error error;
966 if ( encode->char_index )
967 /* with synthetic fonts, it's possible we get here twice */
968 return;
970 /* read the number of entries in the encoding, should be 256 */
971 count = T1_ToInt( parser );
972 if ( parser->root.error )
973 return;
975 /* we use a T1_Table to store our charnames */
976 loader->num_chars = encode->num_chars = count;
977 if ( FT_NEW_ARRAY( encode->char_index, count ) ||
978 FT_NEW_ARRAY( encode->char_name, count ) ||
979 FT_SET_ERROR( psaux->ps_table_funcs->init(
980 char_table, count, memory ) ) )
982 parser->root.error = error;
983 return;
986 /* We need to `zero' out encoding_table.elements */
987 for ( n = 0; n < count; n++ )
989 char* notdef = (char *)".notdef";
992 T1_Add_Table( char_table, n, notdef, 8 );
995 /* Now, we will need to read a record of the form */
996 /* ... charcode /charname ... for each entry in our table */
997 /* */
998 /* We simply look for a number followed by an immediate */
999 /* name. Note that this ignores correctly the sequence */
1000 /* that is often seen in type1 fonts: */
1001 /* */
1002 /* 0 1 255 { 1 index exch /.notdef put } for dup */
1003 /* */
1004 /* used to clean the encoding array before anything else. */
1005 /* */
1006 /* We stop when we encounter a `def'. */
1008 cur = parser->root.cursor;
1009 limit = parser->root.limit;
1010 n = 0;
1012 for ( ; cur < limit; )
1014 FT_Byte c;
1017 c = *cur;
1019 /* we stop when we encounter a `def' */
1020 if ( c == 'd' && cur + 3 < limit )
1022 if ( cur[1] == 'e' &&
1023 cur[2] == 'f' &&
1024 is_space( cur[-1] ) &&
1025 is_space( cur[3] ) )
1027 FT_TRACE6(( "encoding end\n" ));
1028 break;
1032 /* otherwise, we must find a number before anything else */
1033 if ( (FT_Byte)( c - '0' ) < 10 )
1035 FT_Int charcode;
1038 parser->root.cursor = cur;
1039 charcode = T1_ToInt( parser );
1040 cur = parser->root.cursor;
1042 /* skip whitespace */
1043 while ( cur < limit && is_space( *cur ) )
1044 cur++;
1046 if ( cur < limit && *cur == '/' )
1048 /* bingo, we have an immediate name -- it must be a */
1049 /* character name */
1050 FT_Byte* cur2 = cur + 1;
1051 FT_Int len;
1054 while ( cur2 < limit && is_alpha( *cur2 ) )
1055 cur2++;
1057 len = (FT_Int)( cur2 - cur - 1 );
1059 parser->root.error = T1_Add_Table( char_table, charcode,
1060 cur + 1, len + 1 );
1061 char_table->elements[charcode][len] = '\0';
1062 if ( parser->root.error )
1063 return;
1065 cur = cur2;
1068 else
1069 cur++;
1072 face->type1.encoding_type = T1_ENCODING_TYPE_ARRAY;
1073 parser->root.cursor = cur;
1075 /* Otherwise, we should have either `StandardEncoding', */
1076 /* `ExpertEncoding', or `ISOLatin1Encoding' */
1077 else
1079 if ( cur + 17 < limit &&
1080 ft_strncmp( (const char*)cur, "StandardEncoding", 16 ) == 0 )
1081 face->type1.encoding_type = T1_ENCODING_TYPE_STANDARD;
1083 else if ( cur + 15 < limit &&
1084 ft_strncmp( (const char*)cur, "ExpertEncoding", 14 ) == 0 )
1085 face->type1.encoding_type = T1_ENCODING_TYPE_EXPERT;
1087 else if ( cur + 18 < limit &&
1088 ft_strncmp( (const char*)cur, "ISOLatin1Encoding", 17 ) == 0 )
1089 face->type1.encoding_type = T1_ENCODING_TYPE_ISOLATIN1;
1091 else
1093 FT_ERROR(( "parse_encoding: invalid token!\n" ));
1094 parser->root.error = T1_Err_Invalid_File_Format;
1100 static void
1101 parse_subrs( T1_Face face,
1102 T1_Loader loader )
1104 T1_Parser parser = &loader->parser;
1105 PS_Table table = &loader->subrs;
1106 FT_Memory memory = parser->root.memory;
1107 FT_Error error;
1108 FT_Int n;
1110 PSAux_Service psaux = (PSAux_Service)face->psaux;
1113 if ( loader->num_subrs )
1114 /* with synthetic fonts, it's possible we get here twice */
1115 return;
1117 loader->num_subrs = T1_ToInt( parser );
1118 if ( parser->root.error )
1119 return;
1121 /* position the parser right before the `dup' of the first subr */
1122 T1_Skip_Spaces( parser );
1123 T1_Skip_Alpha( parser ); /* `array' */
1124 T1_Skip_Spaces( parser );
1126 /* initialize subrs array */
1127 error = psaux->ps_table_funcs->init( table, loader->num_subrs, memory );
1128 if ( error )
1129 goto Fail;
1131 /* the format is simple: */
1132 /* */
1133 /* `index' + binary data */
1134 /* */
1135 for ( n = 0; n < loader->num_subrs; n++ )
1137 FT_Int idx, size;
1138 FT_Byte* base;
1141 /* If the next token isn't `dup', we are also done. This */
1142 /* happens when there are `holes' in the Subrs array. */
1143 if ( ft_strncmp( (char*)parser->root.cursor, "dup", 3 ) != 0 )
1144 break;
1146 idx = T1_ToInt( parser );
1148 if ( !read_binary_data( parser, &size, &base ) )
1149 return;
1151 /* The binary string is followed by one token, e.g. `NP' */
1152 /* (bound to `noaccess put') or by two separate tokens: */
1153 /* `noaccess' & `put'. We position the parser right */
1154 /* before the next `dup', if any. */
1155 T1_Skip_Spaces( parser );
1156 T1_Skip_Alpha( parser ); /* `NP' or `I' or `noaccess' */
1157 T1_Skip_Spaces( parser );
1159 if ( ft_strncmp( (char*)parser->root.cursor, "put", 3 ) == 0 )
1161 T1_Skip_Alpha( parser ); /* skip `put' */
1162 T1_Skip_Spaces( parser );
1165 /* some fonts use a value of -1 for lenIV to indicate that */
1166 /* the charstrings are unencoded */
1167 /* */
1168 /* thanks to Tom Kacvinsky for pointing this out */
1169 /* */
1170 if ( face->type1.private_dict.lenIV >= 0 )
1172 FT_Byte* temp;
1175 /* t1_decrypt() shouldn't write to base -- make temporary copy */
1176 if ( FT_ALLOC( temp, size ) )
1177 goto Fail;
1178 FT_MEM_COPY( temp, base, size );
1179 psaux->t1_decrypt( temp, size, 4330 );
1180 size -= face->type1.private_dict.lenIV;
1181 error = T1_Add_Table( table, idx,
1182 temp + face->type1.private_dict.lenIV, size );
1183 FT_FREE( temp );
1185 else
1186 error = T1_Add_Table( table, idx, base, size );
1187 if ( error )
1188 goto Fail;
1190 return;
1192 Fail:
1193 parser->root.error = error;
1197 static void
1198 parse_charstrings( T1_Face face,
1199 T1_Loader loader )
1201 T1_Parser parser = &loader->parser;
1202 PS_Table code_table = &loader->charstrings;
1203 PS_Table name_table = &loader->glyph_names;
1204 PS_Table swap_table = &loader->swap_table;
1205 FT_Memory memory = parser->root.memory;
1206 FT_Error error;
1208 PSAux_Service psaux = (PSAux_Service)face->psaux;
1210 FT_Byte* cur;
1211 FT_Byte* limit = parser->root.limit;
1212 FT_Int n;
1213 FT_UInt notdef_index = 0;
1214 FT_Byte notdef_found = 0;
1217 if ( loader->num_glyphs )
1218 /* with synthetic fonts, it's possible we get here twice */
1219 return;
1221 loader->num_glyphs = T1_ToInt( parser );
1222 if ( parser->root.error )
1223 return;
1225 /* initialize tables (leaving room for addition of .notdef, */
1226 /* if necessary). */
1228 error = psaux->ps_table_funcs->init( code_table,
1229 loader->num_glyphs + 1,
1230 memory );
1231 if ( error )
1232 goto Fail;
1234 error = psaux->ps_table_funcs->init( name_table,
1235 loader->num_glyphs + 1,
1236 memory );
1237 if ( error )
1238 goto Fail;
1240 /* Initialize table for swapping index notdef_index and */
1241 /* index 0 names and codes (if necessary). */
1243 error = psaux->ps_table_funcs->init( swap_table, 4, memory );
1245 if ( error )
1246 goto Fail;
1248 n = 0;
1250 for (;;)
1252 FT_Int size;
1253 FT_Byte* base;
1256 /* the format is simple: */
1257 /* `/glyphname' + binary data */
1258 /* */
1259 /* note that we stop when we find a `def' */
1260 /* */
1261 T1_Skip_Spaces( parser );
1263 cur = parser->root.cursor;
1264 if ( cur >= limit )
1265 break;
1267 /* we stop when we find a `def' or `end' keyword */
1268 if ( *cur == 'd' &&
1269 cur + 3 < limit &&
1270 cur[1] == 'e' &&
1271 cur[2] == 'f' )
1272 break;
1274 if ( *cur == 'e' &&
1275 cur + 3 < limit &&
1276 cur[1] == 'n' &&
1277 cur[2] == 'd' )
1278 break;
1280 if ( *cur != '/' )
1281 T1_Skip_Alpha( parser );
1282 else
1284 FT_Byte* cur2 = cur + 1;
1285 FT_Int len;
1288 while ( cur2 < limit && is_alpha( *cur2 ) )
1289 cur2++;
1290 len = (FT_Int)( cur2 - cur - 1 );
1292 error = T1_Add_Table( name_table, n, cur + 1, len + 1 );
1293 if ( error )
1294 goto Fail;
1296 /* add a trailing zero to the name table */
1297 name_table->elements[n][len] = '\0';
1299 /* record index of /.notdef */
1300 if ( ft_strcmp( (const char*)".notdef",
1301 (const char*)(name_table->elements[n]) ) == 0 )
1303 notdef_index = n;
1304 notdef_found = 1;
1307 parser->root.cursor = cur2;
1308 if ( !read_binary_data( parser, &size, &base ) )
1309 return;
1311 if ( face->type1.private_dict.lenIV >= 0 )
1313 FT_Byte* temp;
1316 /* t1_decrypt() shouldn't write to base -- make temporary copy */
1317 if ( FT_ALLOC( temp, size ) )
1318 goto Fail;
1319 FT_MEM_COPY( temp, base, size );
1320 psaux->t1_decrypt( temp, size, 4330 );
1321 size -= face->type1.private_dict.lenIV;
1322 error = T1_Add_Table( code_table, n,
1323 temp + face->type1.private_dict.lenIV, size );
1324 FT_FREE( temp );
1326 else
1327 error = T1_Add_Table( code_table, n, base, size );
1328 if ( error )
1329 goto Fail;
1331 n++;
1332 if ( n >= loader->num_glyphs )
1333 break;
1337 loader->num_glyphs = n;
1339 /* if /.notdef is found but does not occupy index 0, do our magic. */
1340 if ( ft_strcmp( (const char*)".notdef",
1341 (const char*)name_table->elements[0] ) &&
1342 notdef_found )
1344 /* Swap glyph in index 0 with /.notdef glyph. First, add index 0 */
1345 /* name and code entries to swap_table. Then place notdef_index name */
1346 /* and code entries into swap_table. Then swap name and code */
1347 /* entries at indices notdef_index and 0 using values stored in */
1348 /* swap_table. */
1350 /* Index 0 name */
1351 error = T1_Add_Table( swap_table, 0,
1352 name_table->elements[0],
1353 name_table->lengths [0] );
1354 if ( error )
1355 goto Fail;
1357 /* Index 0 code */
1358 error = T1_Add_Table( swap_table, 1,
1359 code_table->elements[0],
1360 code_table->lengths [0] );
1361 if ( error )
1362 goto Fail;
1364 /* Index notdef_index name */
1365 error = T1_Add_Table( swap_table, 2,
1366 name_table->elements[notdef_index],
1367 name_table->lengths [notdef_index] );
1368 if ( error )
1369 goto Fail;
1371 /* Index notdef_index code */
1372 error = T1_Add_Table( swap_table, 3,
1373 code_table->elements[notdef_index],
1374 code_table->lengths [notdef_index] );
1375 if ( error )
1376 goto Fail;
1378 error = T1_Add_Table( name_table, notdef_index,
1379 swap_table->elements[0],
1380 swap_table->lengths [0] );
1381 if ( error )
1382 goto Fail;
1384 error = T1_Add_Table( code_table, notdef_index,
1385 swap_table->elements[1],
1386 swap_table->lengths [1] );
1387 if ( error )
1388 goto Fail;
1390 error = T1_Add_Table( name_table, 0,
1391 swap_table->elements[2],
1392 swap_table->lengths [2] );
1393 if ( error )
1394 goto Fail;
1396 error = T1_Add_Table( code_table, 0,
1397 swap_table->elements[3],
1398 swap_table->lengths [3] );
1399 if ( error )
1400 goto Fail;
1403 else if ( !notdef_found )
1405 /* notdef_index is already 0, or /.notdef is undefined in */
1406 /* charstrings dictionary. Worry about /.notdef undefined. */
1407 /* We take index 0 and add it to the end of the table(s) */
1408 /* and add our own /.notdef glyph to index 0. */
1410 /* 0 333 hsbw endchar */
1411 FT_Byte notdef_glyph[] = {0x8B, 0xF7, 0xE1, 0x0D, 0x0E};
1412 char* notdef_name = (char *)".notdef";
1415 error = T1_Add_Table( swap_table, 0,
1416 name_table->elements[0],
1417 name_table->lengths [0] );
1418 if ( error )
1419 goto Fail;
1421 error = T1_Add_Table( swap_table, 1,
1422 code_table->elements[0],
1423 code_table->lengths [0] );
1424 if ( error )
1425 goto Fail;
1427 error = T1_Add_Table( name_table, 0, notdef_name, 8 );
1428 if ( error )
1429 goto Fail;
1431 error = T1_Add_Table( code_table, 0, notdef_glyph, 5 );
1433 if ( error )
1434 goto Fail;
1436 error = T1_Add_Table( name_table, n,
1437 swap_table->elements[0],
1438 swap_table->lengths [0] );
1439 if ( error )
1440 goto Fail;
1442 error = T1_Add_Table( code_table, n,
1443 swap_table->elements[1],
1444 swap_table->lengths [1] );
1445 if ( error )
1446 goto Fail;
1448 /* we added a glyph. */
1449 loader->num_glyphs = n + 1;
1452 return;
1454 Fail:
1455 parser->root.error = error;
1459 static
1460 const T1_FieldRec t1_keywords[] =
1463 #include "t1tokens.h"
1465 /* now add the special functions... */
1466 T1_FIELD_CALLBACK( "FontName", parse_font_name )
1467 T1_FIELD_CALLBACK( "FontBBox", parse_font_bbox )
1468 T1_FIELD_CALLBACK( "FontMatrix", parse_font_matrix )
1469 T1_FIELD_CALLBACK( "Encoding", parse_encoding )
1470 T1_FIELD_CALLBACK( "Subrs", parse_subrs )
1471 T1_FIELD_CALLBACK( "CharStrings", parse_charstrings )
1473 #ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
1474 T1_FIELD_CALLBACK( "BlendDesignPositions", parse_blend_design_positions )
1475 T1_FIELD_CALLBACK( "BlendDesignMap", parse_blend_design_map )
1476 T1_FIELD_CALLBACK( "BlendAxisTypes", parse_blend_axis_types )
1477 T1_FIELD_CALLBACK( "WeightVector", parse_weight_vector )
1478 T1_FIELD_CALLBACK( "shareddict", parse_shared_dict )
1479 #endif
1481 { 0, T1_FIELD_LOCATION_CID_INFO, T1_FIELD_TYPE_NONE, 0, 0, 0, 0, 0 }
1485 static FT_Error
1486 parse_dict( T1_Face face,
1487 T1_Loader loader,
1488 FT_Byte* base,
1489 FT_Long size )
1491 T1_Parser parser = &loader->parser;
1494 parser->root.cursor = base;
1495 parser->root.limit = base + size;
1496 parser->root.error = 0;
1499 FT_Byte* cur = base;
1500 FT_Byte* limit = cur + size;
1503 for ( ; cur < limit; cur++ )
1505 /* look for `FontDirectory', which causes problems on some fonts */
1506 if ( *cur == 'F' && cur + 25 < limit &&
1507 ft_strncmp( (char*)cur, "FontDirectory", 13 ) == 0 )
1509 FT_Byte* cur2;
1512 /* skip the `FontDirectory' keyword */
1513 cur += 13;
1514 cur2 = cur;
1516 /* lookup the `known' keyword */
1517 while ( cur < limit && *cur != 'k' &&
1518 ft_strncmp( (char*)cur, "known", 5 ) )
1519 cur++;
1521 if ( cur < limit )
1523 T1_TokenRec token;
1526 /* skip the `known' keyword and the token following it */
1527 cur += 5;
1528 loader->parser.root.cursor = cur;
1529 T1_ToToken( &loader->parser, &token );
1531 /* if the last token was an array, skip it! */
1532 if ( token.type == T1_TOKEN_TYPE_ARRAY )
1533 cur2 = parser->root.cursor;
1535 cur = cur2;
1537 /* look for immediates */
1538 else if ( *cur == '/' && cur + 2 < limit )
1540 FT_Byte* cur2;
1541 FT_Int len;
1544 cur++;
1545 cur2 = cur;
1546 while ( cur2 < limit && is_alpha( *cur2 ) )
1547 cur2++;
1549 len = (FT_Int)( cur2 - cur );
1550 if ( len > 0 && len < 22 )
1553 /* now, compare the immediate name to the keyword table */
1554 T1_Field keyword = (T1_Field)t1_keywords;
1557 for (;;)
1559 FT_Byte* name;
1562 name = (FT_Byte*)keyword->ident;
1563 if ( !name )
1564 break;
1566 if ( cur[0] == name[0] &&
1567 len == (FT_Int)ft_strlen( (const char*)name ) )
1569 FT_Int n;
1572 for ( n = 1; n < len; n++ )
1573 if ( cur[n] != name[n] )
1574 break;
1576 if ( n >= len )
1578 /* we found it -- run the parsing callback! */
1579 parser->root.cursor = cur2;
1580 T1_Skip_Spaces( parser );
1581 parser->root.error = t1_load_keyword( face,
1582 loader,
1583 keyword );
1584 if ( parser->root.error )
1585 return parser->root.error;
1587 cur = parser->root.cursor;
1588 break;
1591 keyword++;
1598 return parser->root.error;
1602 static void
1603 t1_init_loader( T1_Loader loader,
1604 T1_Face face )
1606 FT_UNUSED( face );
1608 FT_MEM_SET( loader, 0, sizeof ( *loader ) );
1609 loader->num_glyphs = 0;
1610 loader->num_chars = 0;
1612 /* initialize the tables -- simply set their `init' field to 0 */
1613 loader->encoding_table.init = 0;
1614 loader->charstrings.init = 0;
1615 loader->glyph_names.init = 0;
1616 loader->subrs.init = 0;
1617 loader->swap_table.init = 0;
1618 loader->fontdata = 0;
1622 static void
1623 t1_done_loader( T1_Loader loader )
1625 T1_Parser parser = &loader->parser;
1628 /* finalize tables */
1629 T1_Release_Table( &loader->encoding_table );
1630 T1_Release_Table( &loader->charstrings );
1631 T1_Release_Table( &loader->glyph_names );
1632 T1_Release_Table( &loader->swap_table );
1633 T1_Release_Table( &loader->subrs );
1635 /* finalize parser */
1636 T1_Finalize_Parser( parser );
1640 FT_LOCAL_DEF( FT_Error )
1641 T1_Open_Face( T1_Face face )
1643 T1_LoaderRec loader;
1644 T1_Parser parser;
1645 T1_Font type1 = &face->type1;
1646 FT_Error error;
1648 PSAux_Service psaux = (PSAux_Service)face->psaux;
1651 t1_init_loader( &loader, face );
1653 /* default lenIV */
1654 type1->private_dict.lenIV = 4;
1656 parser = &loader.parser;
1657 error = T1_New_Parser( parser,
1658 face->root.stream,
1659 face->root.memory,
1660 psaux );
1661 if ( error )
1662 goto Exit;
1664 error = parse_dict( face, &loader, parser->base_dict, parser->base_len );
1665 if ( error )
1666 goto Exit;
1668 error = T1_Get_Private_Dict( parser, psaux );
1669 if ( error )
1670 goto Exit;
1672 error = parse_dict( face, &loader, parser->private_dict,
1673 parser->private_len );
1674 if ( error )
1675 goto Exit;
1677 /* now, propagate the subrs, charstrings, and glyphnames tables */
1678 /* to the Type1 data */
1679 type1->num_glyphs = loader.num_glyphs;
1681 if ( loader.subrs.init )
1683 loader.subrs.init = 0;
1684 type1->num_subrs = loader.num_subrs;
1685 type1->subrs_block = loader.subrs.block;
1686 type1->subrs = loader.subrs.elements;
1687 type1->subrs_len = loader.subrs.lengths;
1690 if ( !loader.charstrings.init )
1692 FT_ERROR(( "T1_Open_Face: no charstrings array in face!\n" ));
1693 error = T1_Err_Invalid_File_Format;
1696 loader.charstrings.init = 0;
1697 type1->charstrings_block = loader.charstrings.block;
1698 type1->charstrings = loader.charstrings.elements;
1699 type1->charstrings_len = loader.charstrings.lengths;
1701 /* we copy the glyph names `block' and `elements' fields; */
1702 /* the `lengths' field must be released later */
1703 type1->glyph_names_block = loader.glyph_names.block;
1704 type1->glyph_names = (FT_String**)loader.glyph_names.elements;
1705 loader.glyph_names.block = 0;
1706 loader.glyph_names.elements = 0;
1708 /* we must now build type1.encoding when we have a custom array */
1709 if ( type1->encoding_type == T1_ENCODING_TYPE_ARRAY )
1711 FT_Int charcode, idx, min_char, max_char;
1712 FT_Byte* char_name;
1713 FT_Byte* glyph_name;
1716 /* OK, we do the following: for each element in the encoding */
1717 /* table, look up the index of the glyph having the same name */
1718 /* the index is then stored in type1.encoding.char_index, and */
1719 /* a the name to type1.encoding.char_name */
1721 min_char = +32000;
1722 max_char = -32000;
1724 charcode = 0;
1725 for ( ; charcode < loader.encoding_table.max_elems; charcode++ )
1727 type1->encoding.char_index[charcode] = 0;
1728 type1->encoding.char_name [charcode] = (char *)".notdef";
1730 char_name = loader.encoding_table.elements[charcode];
1731 if ( char_name )
1732 for ( idx = 0; idx < type1->num_glyphs; idx++ )
1734 glyph_name = (FT_Byte*)type1->glyph_names[idx];
1735 if ( ft_strcmp( (const char*)char_name,
1736 (const char*)glyph_name ) == 0 )
1738 type1->encoding.char_index[charcode] = (FT_UShort)idx;
1739 type1->encoding.char_name [charcode] = (char*)glyph_name;
1741 /* Change min/max encoded char only if glyph name is */
1742 /* not /.notdef */
1743 if ( ft_strcmp( (const char*)".notdef",
1744 (const char*)glyph_name ) != 0 )
1746 if ( charcode < min_char ) min_char = charcode;
1747 if ( charcode > max_char ) max_char = charcode;
1749 break;
1753 type1->encoding.code_first = min_char;
1754 type1->encoding.code_last = max_char;
1755 type1->encoding.num_chars = loader.num_chars;
1758 Exit:
1759 t1_done_loader( &loader );
1760 return error;
1764 /* END */