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