1 /****************************************************************************
2  *
3  * ttload.c
4  *
5  *   Load the basic TrueType tables, i.e., tables that can be either in
6  *   TTF or OTF fonts (body).
7  *
8  * Copyright (C) 1996-2019 by
9  * David Turner, Robert Wilhelm, and Werner Lemberg.
10  *
11  * This file is part of the FreeType project, and may only be used,
12  * modified, and distributed under the terms of the FreeType project
13  * license, LICENSE.TXT.  By continuing to use, modify, or distribute
14  * this file you indicate that you have read the license and
15  * understand and accept it fully.
16  *
17  */
18 
19 
20 #include <ft2build.h>
21 #include FT_INTERNAL_DEBUG_H
22 #include FT_INTERNAL_STREAM_H
23 #include FT_TRUETYPE_TAGS_H
24 #include "ttload.h"
25 
26 #include "sferrors.h"
27 
28 
29   /**************************************************************************
30    *
31    * The macro FT_COMPONENT is used in trace mode.  It is an implicit
32    * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
33    * messages during execution.
34    */
35 #undef  FT_COMPONENT
36 #define FT_COMPONENT  ttload
37 
38 
39   /**************************************************************************
40    *
41    * @Function:
42    *   tt_face_lookup_table
43    *
44    * @Description:
45    *   Looks for a TrueType table by name.
46    *
47    * @Input:
48    *   face ::
49    *     A face object handle.
50    *
51    *   tag ::
52    *     The searched tag.
53    *
54    * @Return:
55    *   A pointer to the table directory entry.  0 if not found.
56    */
57   FT_LOCAL_DEF( TT_Table  )
tt_face_lookup_table(TT_Face face,FT_ULong tag)58   tt_face_lookup_table( TT_Face   face,
59                         FT_ULong  tag  )
60   {
61     TT_Table  entry;
62     TT_Table  limit;
63 #ifdef FT_DEBUG_LEVEL_TRACE
64     FT_Bool   zero_length = FALSE;
65 #endif
66 
67 
68     FT_TRACE4(( "tt_face_lookup_table: %08p, `%c%c%c%c' -- ",
69                 face,
70                 (FT_Char)( tag >> 24 ),
71                 (FT_Char)( tag >> 16 ),
72                 (FT_Char)( tag >> 8  ),
73                 (FT_Char)( tag       ) ));
74 
75     entry = face->dir_tables;
76     limit = entry + face->num_tables;
77 
78     for ( ; entry < limit; entry++ )
79     {
80       /* For compatibility with Windows, we consider    */
81       /* zero-length tables the same as missing tables. */
82       if ( entry->Tag == tag )
83       {
84         if ( entry->Length != 0 )
85         {
86           FT_TRACE4(( "found table.\n" ));
87           return entry;
88         }
89 #ifdef FT_DEBUG_LEVEL_TRACE
90         zero_length = TRUE;
91 #endif
92       }
93     }
94 
95 #ifdef FT_DEBUG_LEVEL_TRACE
96     if ( zero_length )
97       FT_TRACE4(( "ignoring empty table\n" ));
98     else
99       FT_TRACE4(( "could not find table\n" ));
100 #endif
101 
102     return NULL;
103   }
104 
105 
106   /**************************************************************************
107    *
108    * @Function:
109    *   tt_face_goto_table
110    *
111    * @Description:
112    *   Looks for a TrueType table by name, then seek a stream to it.
113    *
114    * @Input:
115    *   face ::
116    *     A face object handle.
117    *
118    *   tag ::
119    *     The searched tag.
120    *
121    *   stream ::
122    *     The stream to seek when the table is found.
123    *
124    * @Output:
125    *   length ::
126    *     The length of the table if found, undefined otherwise.
127    *
128    * @Return:
129    *   FreeType error code.  0 means success.
130    */
131   FT_LOCAL_DEF( FT_Error )
tt_face_goto_table(TT_Face face,FT_ULong tag,FT_Stream stream,FT_ULong * length)132   tt_face_goto_table( TT_Face    face,
133                       FT_ULong   tag,
134                       FT_Stream  stream,
135                       FT_ULong*  length )
136   {
137     TT_Table  table;
138     FT_Error  error;
139 
140 
141     table = tt_face_lookup_table( face, tag );
142     if ( table )
143     {
144       if ( length )
145         *length = table->Length;
146 
147       if ( FT_STREAM_SEEK( table->Offset ) )
148         goto Exit;
149     }
150     else
151       error = FT_THROW( Table_Missing );
152 
153   Exit:
154     return error;
155   }
156 
157 
158   /* Here, we                                                         */
159   /*                                                                  */
160   /* - check that `num_tables' is valid (and adjust it if necessary); */
161   /*   also return the number of valid table entries                  */
162   /*                                                                  */
163   /* - look for a `head' table, check its size, and parse it to check */
164   /*   whether its `magic' field is correctly set                     */
165   /*                                                                  */
166   /* - errors (except errors returned by stream handling)             */
167   /*                                                                  */
168   /*     SFNT_Err_Unknown_File_Format:                                */
169   /*       no table is defined in directory, it is not sfnt-wrapped   */
170   /*       data                                                       */
171   /*     SFNT_Err_Table_Missing:                                      */
172   /*       table directory is valid, but essential tables             */
173   /*       (head/bhed/SING) are missing                               */
174   /*                                                                  */
175   static FT_Error
check_table_dir(SFNT_Header sfnt,FT_Stream stream,FT_UShort * valid)176   check_table_dir( SFNT_Header  sfnt,
177                    FT_Stream    stream,
178                    FT_UShort*   valid )
179   {
180     FT_Error   error;
181     FT_UShort  nn, valid_entries = 0;
182     FT_UInt    has_head = 0, has_sing = 0, has_meta = 0;
183     FT_ULong   offset = sfnt->offset + 12;
184 
185     static const FT_Frame_Field  table_dir_entry_fields[] =
186     {
187 #undef  FT_STRUCTURE
188 #define FT_STRUCTURE  TT_TableRec
189 
190       FT_FRAME_START( 16 ),
191         FT_FRAME_ULONG( Tag ),
192         FT_FRAME_ULONG( CheckSum ),
193         FT_FRAME_ULONG( Offset ),
194         FT_FRAME_ULONG( Length ),
195       FT_FRAME_END
196     };
197 
198 
199     if ( FT_STREAM_SEEK( offset ) )
200       goto Exit;
201 
202     for ( nn = 0; nn < sfnt->num_tables; nn++ )
203     {
204       TT_TableRec  table;
205 
206 
207       if ( FT_STREAM_READ_FIELDS( table_dir_entry_fields, &table ) )
208       {
209         nn--;
210         FT_TRACE2(( "check_table_dir:"
211                     " can read only %d table%s in font (instead of %d)\n",
212                     nn, nn == 1 ? "" : "s", sfnt->num_tables ));
213         sfnt->num_tables = nn;
214         break;
215       }
216 
217       /* we ignore invalid tables */
218 
219       if ( table.Offset > stream->size )
220       {
221         FT_TRACE2(( "check_table_dir: table entry %d invalid\n", nn ));
222         continue;
223       }
224       else if ( table.Length > stream->size - table.Offset )
225       {
226         /* Some tables have such a simple structure that clipping its     */
227         /* contents is harmless.  This also makes FreeType less sensitive */
228         /* to invalid table lengths (which programs like Acroread seem to */
229         /* ignore in general).                                            */
230 
231         if ( table.Tag == TTAG_hmtx ||
232              table.Tag == TTAG_vmtx )
233           valid_entries++;
234         else
235         {
236           FT_TRACE2(( "check_table_dir: table entry %d invalid\n", nn ));
237           continue;
238         }
239       }
240       else
241         valid_entries++;
242 
243       if ( table.Tag == TTAG_head || table.Tag == TTAG_bhed )
244       {
245         FT_UInt32  magic;
246 
247 
248 #ifndef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
249         if ( table.Tag == TTAG_head )
250 #endif
251           has_head = 1;
252 
253         /*
254          * The table length should be 0x36, but certain font tools make it
255          * 0x38, so we will just check that it is greater.
256          *
257          * Note that according to the specification, the table must be
258          * padded to 32-bit lengths, but this doesn't apply to the value of
259          * its `Length' field!
260          *
261          */
262         if ( table.Length < 0x36 )
263         {
264           FT_TRACE2(( "check_table_dir:"
265                       " `head' or `bhed' table too small\n" ));
266           error = FT_THROW( Table_Missing );
267           goto Exit;
268         }
269 
270         if ( FT_STREAM_SEEK( table.Offset + 12 ) ||
271              FT_READ_ULONG( magic )              )
272           goto Exit;
273 
274         if ( magic != 0x5F0F3CF5UL )
275           FT_TRACE2(( "check_table_dir:"
276                       " invalid magic number in `head' or `bhed' table\n"));
277 
278         if ( FT_STREAM_SEEK( offset + ( nn + 1 ) * 16 ) )
279           goto Exit;
280       }
281       else if ( table.Tag == TTAG_SING )
282         has_sing = 1;
283       else if ( table.Tag == TTAG_META )
284         has_meta = 1;
285     }
286 
287     *valid = valid_entries;
288 
289     if ( !valid_entries )
290     {
291       FT_TRACE2(( "check_table_dir: no valid tables found\n" ));
292       error = FT_THROW( Unknown_File_Format );
293       goto Exit;
294     }
295 
296     /* if `sing' and `meta' tables are present, there is no `head' table */
297     if ( has_head || ( has_sing && has_meta ) )
298     {
299       error = FT_Err_Ok;
300       goto Exit;
301     }
302     else
303     {
304       FT_TRACE2(( "check_table_dir:" ));
305 #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
306       FT_TRACE2(( " neither `head', `bhed', nor `sing' table found\n" ));
307 #else
308       FT_TRACE2(( " neither `head' nor `sing' table found\n" ));
309 #endif
310       error = FT_THROW( Table_Missing );
311     }
312 
313   Exit:
314     return error;
315   }
316 
317 
318   /**************************************************************************
319    *
320    * @Function:
321    *   tt_face_load_font_dir
322    *
323    * @Description:
324    *   Loads the header of a SFNT font file.
325    *
326    * @Input:
327    *   face ::
328    *     A handle to the target face object.
329    *
330    *   stream ::
331    *     The input stream.
332    *
333    * @Output:
334    *   sfnt ::
335    *     The SFNT header.
336    *
337    * @Return:
338    *   FreeType error code.  0 means success.
339    *
340    * @Note:
341    *   The stream cursor must be at the beginning of the font directory.
342    */
343   FT_LOCAL_DEF( FT_Error )
tt_face_load_font_dir(TT_Face face,FT_Stream stream)344   tt_face_load_font_dir( TT_Face    face,
345                          FT_Stream  stream )
346   {
347     SFNT_HeaderRec  sfnt;
348     FT_Error        error;
349     FT_Memory       memory = stream->memory;
350     FT_UShort       nn, valid_entries = 0;
351 
352     static const FT_Frame_Field  offset_table_fields[] =
353     {
354 #undef  FT_STRUCTURE
355 #define FT_STRUCTURE  SFNT_HeaderRec
356 
357       FT_FRAME_START( 8 ),
358         FT_FRAME_USHORT( num_tables ),
359         FT_FRAME_USHORT( search_range ),
360         FT_FRAME_USHORT( entry_selector ),
361         FT_FRAME_USHORT( range_shift ),
362       FT_FRAME_END
363     };
364 
365 
366     FT_TRACE2(( "tt_face_load_font_dir: %08p\n", face ));
367 
368     /* read the offset table */
369 
370     sfnt.offset = FT_STREAM_POS();
371 
372     if ( FT_READ_ULONG( sfnt.format_tag )                    ||
373          FT_STREAM_READ_FIELDS( offset_table_fields, &sfnt ) )
374       goto Exit;
375 
376     /* many fonts don't have these fields set correctly */
377 #if 0
378     if ( sfnt.search_range != 1 << ( sfnt.entry_selector + 4 )        ||
379          sfnt.search_range + sfnt.range_shift != sfnt.num_tables << 4 )
380       return FT_THROW( Unknown_File_Format );
381 #endif
382 
383     /* load the table directory */
384 
385     FT_TRACE2(( "-- Number of tables: %10u\n",    sfnt.num_tables ));
386     FT_TRACE2(( "-- Format version:   0x%08lx\n", sfnt.format_tag ));
387 
388     if ( sfnt.format_tag != TTAG_OTTO )
389     {
390       /* check first */
391       error = check_table_dir( &sfnt, stream, &valid_entries );
392       if ( error )
393       {
394         FT_TRACE2(( "tt_face_load_font_dir:"
395                     " invalid table directory for TrueType\n" ));
396         goto Exit;
397       }
398     }
399     else
400       valid_entries = sfnt.num_tables;
401 
402     face->num_tables = valid_entries;
403     face->format_tag = sfnt.format_tag;
404 
405     if ( FT_QNEW_ARRAY( face->dir_tables, face->num_tables ) )
406       goto Exit;
407 
408     if ( FT_STREAM_SEEK( sfnt.offset + 12 )      ||
409          FT_FRAME_ENTER( sfnt.num_tables * 16L ) )
410       goto Exit;
411 
412     FT_TRACE2(( "\n"
413                 "  tag    offset    length   checksum\n"
414                 "  ----------------------------------\n" ));
415 
416     valid_entries = 0;
417     for ( nn = 0; nn < sfnt.num_tables; nn++ )
418     {
419       TT_TableRec  entry;
420       FT_UShort    i;
421       FT_Bool      duplicate;
422 
423 
424       entry.Tag      = FT_GET_TAG4();
425       entry.CheckSum = FT_GET_ULONG();
426       entry.Offset   = FT_GET_ULONG();
427       entry.Length   = FT_GET_ULONG();
428 
429       /* ignore invalid tables that can't be sanitized */
430 
431       if ( entry.Offset > stream->size )
432         continue;
433       else if ( entry.Length > stream->size - entry.Offset )
434       {
435         if ( entry.Tag == TTAG_hmtx ||
436              entry.Tag == TTAG_vmtx )
437         {
438 #ifdef FT_DEBUG_LEVEL_TRACE
439           FT_ULong  old_length = entry.Length;
440 #endif
441 
442 
443           /* make metrics table length a multiple of 4 */
444           entry.Length = ( stream->size - entry.Offset ) & ~3U;
445 
446           FT_TRACE2(( "  %c%c%c%c  %08lx  %08lx  %08lx"
447                       " (sanitized; original length %08lx)",
448                       (FT_Char)( entry.Tag >> 24 ),
449                       (FT_Char)( entry.Tag >> 16 ),
450                       (FT_Char)( entry.Tag >> 8  ),
451                       (FT_Char)( entry.Tag       ),
452                       entry.Offset,
453                       entry.Length,
454                       entry.CheckSum,
455                       old_length ));
456         }
457         else
458           continue;
459       }
460 #ifdef FT_DEBUG_LEVEL_TRACE
461       else
462         FT_TRACE2(( "  %c%c%c%c  %08lx  %08lx  %08lx",
463                     (FT_Char)( entry.Tag >> 24 ),
464                     (FT_Char)( entry.Tag >> 16 ),
465                     (FT_Char)( entry.Tag >> 8  ),
466                     (FT_Char)( entry.Tag       ),
467                     entry.Offset,
468                     entry.Length,
469                     entry.CheckSum ));
470 #endif
471 
472       /* ignore duplicate tables – the first one wins */
473       duplicate = 0;
474       for ( i = 0; i < valid_entries; i++ )
475       {
476         if ( face->dir_tables[i].Tag == entry.Tag )
477         {
478           duplicate = 1;
479           break;
480         }
481       }
482       if ( duplicate )
483       {
484         FT_TRACE2(( "  (duplicate, ignored)\n" ));
485         continue;
486       }
487       else
488       {
489         FT_TRACE2(( "\n" ));
490 
491         /* we finally have a valid entry */
492         face->dir_tables[valid_entries++] = entry;
493       }
494     }
495 
496     /* final adjustment to number of tables */
497     face->num_tables = valid_entries;
498 
499     FT_FRAME_EXIT();
500 
501     FT_TRACE2(( "table directory loaded\n\n" ));
502 
503   Exit:
504     return error;
505   }
506 
507 
508   /**************************************************************************
509    *
510    * @Function:
511    *   tt_face_load_any
512    *
513    * @Description:
514    *   Loads any font table into client memory.
515    *
516    * @Input:
517    *   face ::
518    *     The face object to look for.
519    *
520    *   tag ::
521    *     The tag of table to load.  Use the value 0 if you want
522    *     to access the whole font file, else set this parameter
523    *     to a valid TrueType table tag that you can forge with
524    *     the MAKE_TT_TAG macro.
525    *
526    *   offset ::
527    *     The starting offset in the table (or the file if
528    *     tag == 0).
529    *
530    *   length ::
531    *     The address of the decision variable:
532    *
533    *     If length == NULL:
534    *       Loads the whole table.  Returns an error if
535    *       `offset' == 0!
536    *
537    *     If *length == 0:
538    *       Exits immediately; returning the length of the given
539    *       table or of the font file, depending on the value of
540    *       `tag'.
541    *
542    *     If *length != 0:
543    *       Loads the next `length' bytes of table or font,
544    *       starting at offset `offset' (in table or font too).
545    *
546    * @Output:
547    *   buffer ::
548    *     The address of target buffer.
549    *
550    * @Return:
551    *   FreeType error code.  0 means success.
552    */
553   FT_LOCAL_DEF( FT_Error )
tt_face_load_any(TT_Face face,FT_ULong tag,FT_Long offset,FT_Byte * buffer,FT_ULong * length)554   tt_face_load_any( TT_Face    face,
555                     FT_ULong   tag,
556                     FT_Long    offset,
557                     FT_Byte*   buffer,
558                     FT_ULong*  length )
559   {
560     FT_Error   error;
561     FT_Stream  stream;
562     TT_Table   table;
563     FT_ULong   size;
564 
565 
566     if ( tag != 0 )
567     {
568       /* look for tag in font directory */
569       table = tt_face_lookup_table( face, tag );
570       if ( !table )
571       {
572         error = FT_THROW( Table_Missing );
573         goto Exit;
574       }
575 
576       offset += table->Offset;
577       size    = table->Length;
578     }
579     else
580       /* tag == 0 -- the user wants to access the font file directly */
581       size = face->root.stream->size;
582 
583     if ( length && *length == 0 )
584     {
585       *length = size;
586 
587       return FT_Err_Ok;
588     }
589 
590     if ( length )
591       size = *length;
592 
593     stream = face->root.stream;
594     /* the `if' is syntactic sugar for picky compilers */
595     if ( FT_STREAM_READ_AT( offset, buffer, size ) )
596       goto Exit;
597 
598   Exit:
599     return error;
600   }
601 
602 
603   /**************************************************************************
604    *
605    * @Function:
606    *   tt_face_load_generic_header
607    *
608    * @Description:
609    *   Loads the TrueType table `head' or `bhed'.
610    *
611    * @Input:
612    *   face ::
613    *     A handle to the target face object.
614    *
615    *   stream ::
616    *     The input stream.
617    *
618    * @Return:
619    *   FreeType error code.  0 means success.
620    */
621   static FT_Error
tt_face_load_generic_header(TT_Face face,FT_Stream stream,FT_ULong tag)622   tt_face_load_generic_header( TT_Face    face,
623                                FT_Stream  stream,
624                                FT_ULong   tag )
625   {
626     FT_Error    error;
627     TT_Header*  header;
628 
629     static const FT_Frame_Field  header_fields[] =
630     {
631 #undef  FT_STRUCTURE
632 #define FT_STRUCTURE  TT_Header
633 
634       FT_FRAME_START( 54 ),
635         FT_FRAME_ULONG ( Table_Version ),
636         FT_FRAME_ULONG ( Font_Revision ),
637         FT_FRAME_LONG  ( CheckSum_Adjust ),
638         FT_FRAME_LONG  ( Magic_Number ),
639         FT_FRAME_USHORT( Flags ),
640         FT_FRAME_USHORT( Units_Per_EM ),
641         FT_FRAME_ULONG ( Created[0] ),
642         FT_FRAME_ULONG ( Created[1] ),
643         FT_FRAME_ULONG ( Modified[0] ),
644         FT_FRAME_ULONG ( Modified[1] ),
645         FT_FRAME_SHORT ( xMin ),
646         FT_FRAME_SHORT ( yMin ),
647         FT_FRAME_SHORT ( xMax ),
648         FT_FRAME_SHORT ( yMax ),
649         FT_FRAME_USHORT( Mac_Style ),
650         FT_FRAME_USHORT( Lowest_Rec_PPEM ),
651         FT_FRAME_SHORT ( Font_Direction ),
652         FT_FRAME_SHORT ( Index_To_Loc_Format ),
653         FT_FRAME_SHORT ( Glyph_Data_Format ),
654       FT_FRAME_END
655     };
656 
657 
658     error = face->goto_table( face, tag, stream, 0 );
659     if ( error )
660       goto Exit;
661 
662     header = &face->header;
663 
664     if ( FT_STREAM_READ_FIELDS( header_fields, header ) )
665       goto Exit;
666 
667     FT_TRACE3(( "Units per EM: %4u\n", header->Units_Per_EM ));
668     FT_TRACE3(( "IndexToLoc:   %4d\n", header->Index_To_Loc_Format ));
669 
670   Exit:
671     return error;
672   }
673 
674 
675   FT_LOCAL_DEF( FT_Error )
tt_face_load_head(TT_Face face,FT_Stream stream)676   tt_face_load_head( TT_Face    face,
677                      FT_Stream  stream )
678   {
679     return tt_face_load_generic_header( face, stream, TTAG_head );
680   }
681 
682 
683 #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
684 
685   FT_LOCAL_DEF( FT_Error )
tt_face_load_bhed(TT_Face face,FT_Stream stream)686   tt_face_load_bhed( TT_Face    face,
687                      FT_Stream  stream )
688   {
689     return tt_face_load_generic_header( face, stream, TTAG_bhed );
690   }
691 
692 #endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
693 
694 
695   /**************************************************************************
696    *
697    * @Function:
698    *   tt_face_load_maxp
699    *
700    * @Description:
701    *   Loads the maximum profile into a face object.
702    *
703    * @Input:
704    *   face ::
705    *     A handle to the target face object.
706    *
707    *   stream ::
708    *     The input stream.
709    *
710    * @Return:
711    *   FreeType error code.  0 means success.
712    */
713   FT_LOCAL_DEF( FT_Error )
tt_face_load_maxp(TT_Face face,FT_Stream stream)714   tt_face_load_maxp( TT_Face    face,
715                      FT_Stream  stream )
716   {
717     FT_Error        error;
718     TT_MaxProfile*  maxProfile = &face->max_profile;
719 
720     static const FT_Frame_Field  maxp_fields[] =
721     {
722 #undef  FT_STRUCTURE
723 #define FT_STRUCTURE  TT_MaxProfile
724 
725       FT_FRAME_START( 6 ),
726         FT_FRAME_LONG  ( version ),
727         FT_FRAME_USHORT( numGlyphs ),
728       FT_FRAME_END
729     };
730 
731     static const FT_Frame_Field  maxp_fields_extra[] =
732     {
733       FT_FRAME_START( 26 ),
734         FT_FRAME_USHORT( maxPoints ),
735         FT_FRAME_USHORT( maxContours ),
736         FT_FRAME_USHORT( maxCompositePoints ),
737         FT_FRAME_USHORT( maxCompositeContours ),
738         FT_FRAME_USHORT( maxZones ),
739         FT_FRAME_USHORT( maxTwilightPoints ),
740         FT_FRAME_USHORT( maxStorage ),
741         FT_FRAME_USHORT( maxFunctionDefs ),
742         FT_FRAME_USHORT( maxInstructionDefs ),
743         FT_FRAME_USHORT( maxStackElements ),
744         FT_FRAME_USHORT( maxSizeOfInstructions ),
745         FT_FRAME_USHORT( maxComponentElements ),
746         FT_FRAME_USHORT( maxComponentDepth ),
747       FT_FRAME_END
748     };
749 
750 
751     error = face->goto_table( face, TTAG_maxp, stream, 0 );
752     if ( error )
753       goto Exit;
754 
755     if ( FT_STREAM_READ_FIELDS( maxp_fields, maxProfile ) )
756       goto Exit;
757 
758     maxProfile->maxPoints             = 0;
759     maxProfile->maxContours           = 0;
760     maxProfile->maxCompositePoints    = 0;
761     maxProfile->maxCompositeContours  = 0;
762     maxProfile->maxZones              = 0;
763     maxProfile->maxTwilightPoints     = 0;
764     maxProfile->maxStorage            = 0;
765     maxProfile->maxFunctionDefs       = 0;
766     maxProfile->maxInstructionDefs    = 0;
767     maxProfile->maxStackElements      = 0;
768     maxProfile->maxSizeOfInstructions = 0;
769     maxProfile->maxComponentElements  = 0;
770     maxProfile->maxComponentDepth     = 0;
771 
772     if ( maxProfile->version >= 0x10000L )
773     {
774       if ( FT_STREAM_READ_FIELDS( maxp_fields_extra, maxProfile ) )
775         goto Exit;
776 
777       /* XXX: an adjustment that is necessary to load certain */
778       /*      broken fonts like `Keystrokes MT' :-(           */
779       /*                                                      */
780       /*   We allocate 64 function entries by default when    */
781       /*   the maxFunctionDefs value is smaller.              */
782 
783       if ( maxProfile->maxFunctionDefs < 64 )
784         maxProfile->maxFunctionDefs = 64;
785 
786       /* we add 4 phantom points later */
787       if ( maxProfile->maxTwilightPoints > ( 0xFFFFU - 4 ) )
788       {
789         FT_TRACE0(( "tt_face_load_maxp:"
790                     " too much twilight points in `maxp' table;\n"
791                     "                  "
792                     " some glyphs might be rendered incorrectly\n" ));
793 
794         maxProfile->maxTwilightPoints = 0xFFFFU - 4;
795       }
796     }
797 
798     FT_TRACE3(( "numGlyphs: %u\n", maxProfile->numGlyphs ));
799 
800   Exit:
801     return error;
802   }
803 
804 
805   /**************************************************************************
806    *
807    * @Function:
808    *   tt_face_load_name
809    *
810    * @Description:
811    *   Loads the name records.
812    *
813    * @Input:
814    *   face ::
815    *     A handle to the target face object.
816    *
817    *   stream ::
818    *     The input stream.
819    *
820    * @Return:
821    *   FreeType error code.  0 means success.
822    */
823   FT_LOCAL_DEF( FT_Error )
tt_face_load_name(TT_Face face,FT_Stream stream)824   tt_face_load_name( TT_Face    face,
825                      FT_Stream  stream )
826   {
827     FT_Error      error;
828     FT_Memory     memory = stream->memory;
829     FT_ULong      table_pos, table_len;
830     FT_ULong      storage_start, storage_limit;
831     TT_NameTable  table;
832 
833     static const FT_Frame_Field  name_table_fields[] =
834     {
835 #undef  FT_STRUCTURE
836 #define FT_STRUCTURE  TT_NameTableRec
837 
838       FT_FRAME_START( 6 ),
839         FT_FRAME_USHORT( format ),
840         FT_FRAME_USHORT( numNameRecords ),
841         FT_FRAME_USHORT( storageOffset ),
842       FT_FRAME_END
843     };
844 
845     static const FT_Frame_Field  name_record_fields[] =
846     {
847 #undef  FT_STRUCTURE
848 #define FT_STRUCTURE  TT_NameRec
849 
850       /* no FT_FRAME_START */
851         FT_FRAME_USHORT( platformID ),
852         FT_FRAME_USHORT( encodingID ),
853         FT_FRAME_USHORT( languageID ),
854         FT_FRAME_USHORT( nameID ),
855         FT_FRAME_USHORT( stringLength ),
856         FT_FRAME_USHORT( stringOffset ),
857       FT_FRAME_END
858     };
859 
860     static const FT_Frame_Field  langTag_record_fields[] =
861     {
862 #undef  FT_STRUCTURE
863 #define FT_STRUCTURE  TT_LangTagRec
864 
865       /* no FT_FRAME_START */
866         FT_FRAME_USHORT( stringLength ),
867         FT_FRAME_USHORT( stringOffset ),
868       FT_FRAME_END
869     };
870 
871 
872     table         = &face->name_table;
873     table->stream = stream;
874 
875     error = face->goto_table( face, TTAG_name, stream, &table_len );
876     if ( error )
877       goto Exit;
878 
879     table_pos = FT_STREAM_POS();
880 
881     if ( FT_STREAM_READ_FIELDS( name_table_fields, table ) )
882       goto Exit;
883 
884     /* Some popular Asian fonts have an invalid `storageOffset' value (it */
885     /* should be at least `6 + 12*numNameRecords').  However, the string  */
886     /* offsets, computed as `storageOffset + entry->stringOffset', are    */
887     /* valid pointers within the name table...                            */
888     /*                                                                    */
889     /* We thus can't check `storageOffset' right now.                     */
890     /*                                                                    */
891     storage_start = table_pos + 6 + 12 * table->numNameRecords;
892     storage_limit = table_pos + table_len;
893 
894     if ( storage_start > storage_limit )
895     {
896       FT_ERROR(( "tt_face_load_name: invalid `name' table\n" ));
897       error = FT_THROW( Name_Table_Missing );
898       goto Exit;
899     }
900 
901     /* `name' format 1 contains additional language tag records, */
902     /* which we load first                                       */
903     if ( table->format == 1 )
904     {
905       if ( FT_STREAM_SEEK( storage_start )            ||
906            FT_READ_USHORT( table->numLangTagRecords ) )
907         goto Exit;
908 
909       storage_start += 2 + 4 * table->numLangTagRecords;
910 
911       /* allocate language tag records array */
912       if ( FT_NEW_ARRAY( table->langTags, table->numLangTagRecords ) ||
913            FT_FRAME_ENTER( table->numLangTagRecords * 4 )            )
914         goto Exit;
915 
916       /* load language tags */
917       {
918         TT_LangTag  entry = table->langTags;
919         TT_LangTag  limit = entry + table->numLangTagRecords;
920 
921 
922         for ( ; entry < limit; entry++ )
923         {
924           (void)FT_STREAM_READ_FIELDS( langTag_record_fields, entry );
925 
926           /* check that the langTag string is within the table */
927           entry->stringOffset += table_pos + table->storageOffset;
928           if ( entry->stringOffset                       < storage_start ||
929                entry->stringOffset + entry->stringLength > storage_limit )
930           {
931             /* invalid entry; ignore it */
932             entry->stringLength = 0;
933           }
934         }
935       }
936 
937       FT_FRAME_EXIT();
938 
939       (void)FT_STREAM_SEEK( table_pos + 6 );
940     }
941 
942     /* allocate name records array */
943     if ( FT_NEW_ARRAY( table->names, table->numNameRecords ) ||
944          FT_FRAME_ENTER( table->numNameRecords * 12 )        )
945       goto Exit;
946 
947     /* load name records */
948     {
949       TT_Name  entry = table->names;
950       FT_UInt  count = table->numNameRecords;
951 
952 
953       for ( ; count > 0; count-- )
954       {
955         if ( FT_STREAM_READ_FIELDS( name_record_fields, entry ) )
956           continue;
957 
958         /* check that the name is not empty */
959         if ( entry->stringLength == 0 )
960           continue;
961 
962         /* check that the name string is within the table */
963         entry->stringOffset += table_pos + table->storageOffset;
964         if ( entry->stringOffset                       < storage_start ||
965              entry->stringOffset + entry->stringLength > storage_limit )
966         {
967           /* invalid entry; ignore it */
968           continue;
969         }
970 
971         /* assure that we have a valid language tag ID, and   */
972         /* that the corresponding langTag entry is valid, too */
973         if ( table->format == 1 && entry->languageID >= 0x8000U )
974         {
975           if ( entry->languageID - 0x8000U >= table->numLangTagRecords    ||
976                !table->langTags[entry->languageID - 0x8000U].stringLength )
977           {
978             /* invalid entry; ignore it */
979             continue;
980           }
981         }
982 
983         entry++;
984       }
985 
986       /* reduce array size to the actually used elements */
987       count = (FT_UInt)( entry - table->names );
988       (void)FT_RENEW_ARRAY( table->names,
989                             table->numNameRecords,
990                             count );
991       table->numNameRecords = count;
992     }
993 
994     FT_FRAME_EXIT();
995 
996     /* everything went well, update face->num_names */
997     face->num_names = (FT_UShort)table->numNameRecords;
998 
999   Exit:
1000     return error;
1001   }
1002 
1003 
1004   /**************************************************************************
1005    *
1006    * @Function:
1007    *   tt_face_free_name
1008    *
1009    * @Description:
1010    *   Frees the name records.
1011    *
1012    * @Input:
1013    *   face ::
1014    *     A handle to the target face object.
1015    */
1016   FT_LOCAL_DEF( void )
tt_face_free_name(TT_Face face)1017   tt_face_free_name( TT_Face  face )
1018   {
1019     FT_Memory     memory = face->root.driver->root.memory;
1020     TT_NameTable  table  = &face->name_table;
1021 
1022 
1023     if ( table->names )
1024     {
1025       TT_Name  entry = table->names;
1026       TT_Name  limit = entry + table->numNameRecords;
1027 
1028 
1029       for ( ; entry < limit; entry++ )
1030         FT_FREE( entry->string );
1031 
1032       FT_FREE( table->names );
1033     }
1034 
1035     if ( table->langTags )
1036     {
1037       TT_LangTag  entry = table->langTags;
1038       TT_LangTag  limit = entry + table->numLangTagRecords;
1039 
1040 
1041       for ( ; entry < limit; entry++ )
1042         FT_FREE( entry->string );
1043 
1044       FT_FREE( table->langTags );
1045     }
1046 
1047     table->numNameRecords    = 0;
1048     table->numLangTagRecords = 0;
1049     table->format            = 0;
1050     table->storageOffset     = 0;
1051   }
1052 
1053 
1054   /**************************************************************************
1055    *
1056    * @Function:
1057    *   tt_face_load_cmap
1058    *
1059    * @Description:
1060    *   Loads the cmap directory in a face object.  The cmaps themselves
1061    *   are loaded on demand in the `ttcmap.c' module.
1062    *
1063    * @Input:
1064    *   face ::
1065    *     A handle to the target face object.
1066    *
1067    *   stream ::
1068    *     A handle to the input stream.
1069    *
1070    * @Return:
1071    *   FreeType error code.  0 means success.
1072    */
1073 
1074   FT_LOCAL_DEF( FT_Error )
tt_face_load_cmap(TT_Face face,FT_Stream stream)1075   tt_face_load_cmap( TT_Face    face,
1076                      FT_Stream  stream )
1077   {
1078     FT_Error  error;
1079 
1080 
1081     error = face->goto_table( face, TTAG_cmap, stream, &face->cmap_size );
1082     if ( error )
1083       goto Exit;
1084 
1085     if ( FT_FRAME_EXTRACT( face->cmap_size, face->cmap_table ) )
1086       face->cmap_size = 0;
1087 
1088   Exit:
1089     return error;
1090   }
1091 
1092 
1093 
1094   /**************************************************************************
1095    *
1096    * @Function:
1097    *   tt_face_load_os2
1098    *
1099    * @Description:
1100    *   Loads the OS2 table.
1101    *
1102    * @Input:
1103    *   face ::
1104    *     A handle to the target face object.
1105    *
1106    *   stream ::
1107    *     A handle to the input stream.
1108    *
1109    * @Return:
1110    *   FreeType error code.  0 means success.
1111    */
1112   FT_LOCAL_DEF( FT_Error )
tt_face_load_os2(TT_Face face,FT_Stream stream)1113   tt_face_load_os2( TT_Face    face,
1114                     FT_Stream  stream )
1115   {
1116     FT_Error  error;
1117     TT_OS2*   os2;
1118 
1119     static const FT_Frame_Field  os2_fields[] =
1120     {
1121 #undef  FT_STRUCTURE
1122 #define FT_STRUCTURE  TT_OS2
1123 
1124       FT_FRAME_START( 78 ),
1125         FT_FRAME_USHORT( version ),
1126         FT_FRAME_SHORT ( xAvgCharWidth ),
1127         FT_FRAME_USHORT( usWeightClass ),
1128         FT_FRAME_USHORT( usWidthClass ),
1129         FT_FRAME_SHORT ( fsType ),
1130         FT_FRAME_SHORT ( ySubscriptXSize ),
1131         FT_FRAME_SHORT ( ySubscriptYSize ),
1132         FT_FRAME_SHORT ( ySubscriptXOffset ),
1133         FT_FRAME_SHORT ( ySubscriptYOffset ),
1134         FT_FRAME_SHORT ( ySuperscriptXSize ),
1135         FT_FRAME_SHORT ( ySuperscriptYSize ),
1136         FT_FRAME_SHORT ( ySuperscriptXOffset ),
1137         FT_FRAME_SHORT ( ySuperscriptYOffset ),
1138         FT_FRAME_SHORT ( yStrikeoutSize ),
1139         FT_FRAME_SHORT ( yStrikeoutPosition ),
1140         FT_FRAME_SHORT ( sFamilyClass ),
1141         FT_FRAME_BYTE  ( panose[0] ),
1142         FT_FRAME_BYTE  ( panose[1] ),
1143         FT_FRAME_BYTE  ( panose[2] ),
1144         FT_FRAME_BYTE  ( panose[3] ),
1145         FT_FRAME_BYTE  ( panose[4] ),
1146         FT_FRAME_BYTE  ( panose[5] ),
1147         FT_FRAME_BYTE  ( panose[6] ),
1148         FT_FRAME_BYTE  ( panose[7] ),
1149         FT_FRAME_BYTE  ( panose[8] ),
1150         FT_FRAME_BYTE  ( panose[9] ),
1151         FT_FRAME_ULONG ( ulUnicodeRange1 ),
1152         FT_FRAME_ULONG ( ulUnicodeRange2 ),
1153         FT_FRAME_ULONG ( ulUnicodeRange3 ),
1154         FT_FRAME_ULONG ( ulUnicodeRange4 ),
1155         FT_FRAME_BYTE  ( achVendID[0] ),
1156         FT_FRAME_BYTE  ( achVendID[1] ),
1157         FT_FRAME_BYTE  ( achVendID[2] ),
1158         FT_FRAME_BYTE  ( achVendID[3] ),
1159 
1160         FT_FRAME_USHORT( fsSelection ),
1161         FT_FRAME_USHORT( usFirstCharIndex ),
1162         FT_FRAME_USHORT( usLastCharIndex ),
1163         FT_FRAME_SHORT ( sTypoAscender ),
1164         FT_FRAME_SHORT ( sTypoDescender ),
1165         FT_FRAME_SHORT ( sTypoLineGap ),
1166         FT_FRAME_USHORT( usWinAscent ),
1167         FT_FRAME_USHORT( usWinDescent ),
1168       FT_FRAME_END
1169     };
1170 
1171     /* `OS/2' version 1 and newer */
1172     static const FT_Frame_Field  os2_fields_extra1[] =
1173     {
1174       FT_FRAME_START( 8 ),
1175         FT_FRAME_ULONG( ulCodePageRange1 ),
1176         FT_FRAME_ULONG( ulCodePageRange2 ),
1177       FT_FRAME_END
1178     };
1179 
1180     /* `OS/2' version 2 and newer */
1181     static const FT_Frame_Field  os2_fields_extra2[] =
1182     {
1183       FT_FRAME_START( 10 ),
1184         FT_FRAME_SHORT ( sxHeight ),
1185         FT_FRAME_SHORT ( sCapHeight ),
1186         FT_FRAME_USHORT( usDefaultChar ),
1187         FT_FRAME_USHORT( usBreakChar ),
1188         FT_FRAME_USHORT( usMaxContext ),
1189       FT_FRAME_END
1190     };
1191 
1192     /* `OS/2' version 5 and newer */
1193     static const FT_Frame_Field  os2_fields_extra5[] =
1194     {
1195       FT_FRAME_START( 4 ),
1196         FT_FRAME_USHORT( usLowerOpticalPointSize ),
1197         FT_FRAME_USHORT( usUpperOpticalPointSize ),
1198       FT_FRAME_END
1199     };
1200 
1201 
1202     /* We now support old Mac fonts where the OS/2 table doesn't  */
1203     /* exist.  Simply put, we set the `version' field to 0xFFFF   */
1204     /* and test this value each time we need to access the table. */
1205     error = face->goto_table( face, TTAG_OS2, stream, 0 );
1206     if ( error )
1207       goto Exit;
1208 
1209     os2 = &face->os2;
1210 
1211     if ( FT_STREAM_READ_FIELDS( os2_fields, os2 ) )
1212       goto Exit;
1213 
1214     os2->ulCodePageRange1        = 0;
1215     os2->ulCodePageRange2        = 0;
1216     os2->sxHeight                = 0;
1217     os2->sCapHeight              = 0;
1218     os2->usDefaultChar           = 0;
1219     os2->usBreakChar             = 0;
1220     os2->usMaxContext            = 0;
1221     os2->usLowerOpticalPointSize = 0;
1222     os2->usUpperOpticalPointSize = 0xFFFF;
1223 
1224     if ( os2->version >= 0x0001 )
1225     {
1226       /* only version 1 tables */
1227       if ( FT_STREAM_READ_FIELDS( os2_fields_extra1, os2 ) )
1228         goto Exit;
1229 
1230       if ( os2->version >= 0x0002 )
1231       {
1232         /* only version 2 tables */
1233         if ( FT_STREAM_READ_FIELDS( os2_fields_extra2, os2 ) )
1234           goto Exit;
1235 
1236         if ( os2->version >= 0x0005 )
1237         {
1238           /* only version 5 tables */
1239           if ( FT_STREAM_READ_FIELDS( os2_fields_extra5, os2 ) )
1240             goto Exit;
1241         }
1242       }
1243     }
1244 
1245     FT_TRACE3(( "sTypoAscender:  %4d\n",   os2->sTypoAscender ));
1246     FT_TRACE3(( "sTypoDescender: %4d\n",   os2->sTypoDescender ));
1247     FT_TRACE3(( "usWinAscent:    %4u\n",   os2->usWinAscent ));
1248     FT_TRACE3(( "usWinDescent:   %4u\n",   os2->usWinDescent ));
1249     FT_TRACE3(( "fsSelection:    0x%2x\n", os2->fsSelection ));
1250 
1251   Exit:
1252     return error;
1253   }
1254 
1255 
1256   /**************************************************************************
1257    *
1258    * @Function:
1259    *   tt_face_load_postscript
1260    *
1261    * @Description:
1262    *   Loads the Postscript table.
1263    *
1264    * @Input:
1265    *   face ::
1266    *     A handle to the target face object.
1267    *
1268    *   stream ::
1269    *     A handle to the input stream.
1270    *
1271    * @Return:
1272    *   FreeType error code.  0 means success.
1273    */
1274   FT_LOCAL_DEF( FT_Error )
tt_face_load_post(TT_Face face,FT_Stream stream)1275   tt_face_load_post( TT_Face    face,
1276                      FT_Stream  stream )
1277   {
1278     FT_Error        error;
1279     TT_Postscript*  post = &face->postscript;
1280 
1281     static const FT_Frame_Field  post_fields[] =
1282     {
1283 #undef  FT_STRUCTURE
1284 #define FT_STRUCTURE  TT_Postscript
1285 
1286       FT_FRAME_START( 32 ),
1287         FT_FRAME_LONG ( FormatType ),
1288         FT_FRAME_LONG ( italicAngle ),
1289         FT_FRAME_SHORT( underlinePosition ),
1290         FT_FRAME_SHORT( underlineThickness ),
1291         FT_FRAME_ULONG( isFixedPitch ),
1292         FT_FRAME_ULONG( minMemType42 ),
1293         FT_FRAME_ULONG( maxMemType42 ),
1294         FT_FRAME_ULONG( minMemType1 ),
1295         FT_FRAME_ULONG( maxMemType1 ),
1296       FT_FRAME_END
1297     };
1298 
1299 
1300     error = face->goto_table( face, TTAG_post, stream, 0 );
1301     if ( error )
1302       return error;
1303 
1304     if ( FT_STREAM_READ_FIELDS( post_fields, post ) )
1305       return error;
1306 
1307     /* we don't load the glyph names, we do that in another */
1308     /* module (ttpost).                                     */
1309 
1310     FT_TRACE3(( "FormatType:   0x%x\n", post->FormatType ));
1311     FT_TRACE3(( "isFixedPitch:   %s\n", post->isFixedPitch
1312                                         ? "  yes" : "   no" ));
1313 
1314     return FT_Err_Ok;
1315   }
1316 
1317 
1318   /**************************************************************************
1319    *
1320    * @Function:
1321    *   tt_face_load_pclt
1322    *
1323    * @Description:
1324    *   Loads the PCL 5 Table.
1325    *
1326    * @Input:
1327    *   face ::
1328    *     A handle to the target face object.
1329    *
1330    *   stream ::
1331    *     A handle to the input stream.
1332    *
1333    * @Return:
1334    *   FreeType error code.  0 means success.
1335    */
1336   FT_LOCAL_DEF( FT_Error )
tt_face_load_pclt(TT_Face face,FT_Stream stream)1337   tt_face_load_pclt( TT_Face    face,
1338                      FT_Stream  stream )
1339   {
1340     static const FT_Frame_Field  pclt_fields[] =
1341     {
1342 #undef  FT_STRUCTURE
1343 #define FT_STRUCTURE  TT_PCLT
1344 
1345       FT_FRAME_START( 54 ),
1346         FT_FRAME_ULONG ( Version ),
1347         FT_FRAME_ULONG ( FontNumber ),
1348         FT_FRAME_USHORT( Pitch ),
1349         FT_FRAME_USHORT( xHeight ),
1350         FT_FRAME_USHORT( Style ),
1351         FT_FRAME_USHORT( TypeFamily ),
1352         FT_FRAME_USHORT( CapHeight ),
1353         FT_FRAME_USHORT( SymbolSet ),
1354         FT_FRAME_BYTES ( TypeFace, 16 ),
1355         FT_FRAME_BYTES ( CharacterComplement, 8 ),
1356         FT_FRAME_BYTES ( FileName, 6 ),
1357         FT_FRAME_CHAR  ( StrokeWeight ),
1358         FT_FRAME_CHAR  ( WidthType ),
1359         FT_FRAME_BYTE  ( SerifStyle ),
1360         FT_FRAME_BYTE  ( Reserved ),
1361       FT_FRAME_END
1362     };
1363 
1364     FT_Error  error;
1365     TT_PCLT*  pclt = &face->pclt;
1366 
1367 
1368     /* optional table */
1369     error = face->goto_table( face, TTAG_PCLT, stream, 0 );
1370     if ( error )
1371       goto Exit;
1372 
1373     if ( FT_STREAM_READ_FIELDS( pclt_fields, pclt ) )
1374       goto Exit;
1375 
1376   Exit:
1377     return error;
1378   }
1379 
1380 
1381   /**************************************************************************
1382    *
1383    * @Function:
1384    *   tt_face_load_gasp
1385    *
1386    * @Description:
1387    *   Loads the `gasp' table into a face object.
1388    *
1389    * @Input:
1390    *   face ::
1391    *     A handle to the target face object.
1392    *
1393    *   stream ::
1394    *     The input stream.
1395    *
1396    * @Return:
1397    *   FreeType error code.  0 means success.
1398    */
1399   FT_LOCAL_DEF( FT_Error )
tt_face_load_gasp(TT_Face face,FT_Stream stream)1400   tt_face_load_gasp( TT_Face    face,
1401                      FT_Stream  stream )
1402   {
1403     FT_Error   error;
1404     FT_Memory  memory = stream->memory;
1405 
1406     FT_UInt        j,num_ranges;
1407     TT_GaspRange   gaspranges = NULL;
1408 
1409 
1410     /* the gasp table is optional */
1411     error = face->goto_table( face, TTAG_gasp, stream, 0 );
1412     if ( error )
1413       goto Exit;
1414 
1415     if ( FT_FRAME_ENTER( 4L ) )
1416       goto Exit;
1417 
1418     face->gasp.version   = FT_GET_USHORT();
1419     face->gasp.numRanges = FT_GET_USHORT();
1420 
1421     FT_FRAME_EXIT();
1422 
1423     /* only support versions 0 and 1 of the table */
1424     if ( face->gasp.version >= 2 )
1425     {
1426       face->gasp.numRanges = 0;
1427       error = FT_THROW( Invalid_Table );
1428       goto Exit;
1429     }
1430 
1431     num_ranges = face->gasp.numRanges;
1432     FT_TRACE3(( "numRanges: %u\n", num_ranges ));
1433 
1434     if ( FT_QNEW_ARRAY( face->gasp.gaspRanges, num_ranges ) ||
1435          FT_FRAME_ENTER( num_ranges * 4L )                  )
1436       goto Exit;
1437 
1438     gaspranges = face->gasp.gaspRanges;
1439 
1440     for ( j = 0; j < num_ranges; j++ )
1441     {
1442       gaspranges[j].maxPPEM  = FT_GET_USHORT();
1443       gaspranges[j].gaspFlag = FT_GET_USHORT();
1444 
1445       FT_TRACE3(( "gaspRange %d: rangeMaxPPEM %5d, rangeGaspBehavior 0x%x\n",
1446                   j,
1447                   gaspranges[j].maxPPEM,
1448                   gaspranges[j].gaspFlag ));
1449     }
1450 
1451     FT_FRAME_EXIT();
1452 
1453   Exit:
1454     return error;
1455   }
1456 
1457 
1458 /* END */
1459