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