cidparse.c revision e1d5dd78f4a75697a2584deac4af71dd21939f6a
1/*******************************************************************
2 *
3 *  cidparse.c                                                   2.0
4 *
5 *    CID-keyed Type1 parser.
6 *
7 *  Copyright 1996-1998 by
8 *  David Turner, Robert Wilhelm, and Werner Lemberg.
9 *
10 *  This file is part of the FreeType project, and may only be used
11 *  modified and distributed under the terms of the FreeType project
12 *  license, LICENSE.TXT.  By continuing to use, modify, or distribute
13 *  this file you indicate that you have read the license and
14 *  understand and accept it fully.
15 *
16 *  The Type 1 parser is in charge of the following:
17 *
18 *   - provide an implementation of a growing sequence of
19 *     objects called a T1_Table (used to build various tables
20 *     needed by the loader).
21 *
22 *   - opening .pfb and .pfa files to extract their top-level
23 *     and private dictionaries
24 *
25 *   - read numbers, arrays & strings from any dictionary
26 *
27 *  See "t1load.c" to see how data is loaded from the font file
28 *
29 ******************************************************************/
30
31#include <freetype/internal/ftdebug.h>
32#include <freetype/internal/ftcalc.h>
33#include <freetype/internal/ftobjs.h>
34#include <freetype/internal/ftstream.h>
35#include <freetype/internal/t1errors.h>
36#include <cidparse.h>
37
38#undef FT_COMPONENT
39#define FT_COMPONENT  trace_t1load
40
41#if 0
42/*************************************************************************/
43/*************************************************************************/
44/*************************************************************************/
45/*****                                                               *****/
46/*****           IMPLEMENTATION OF T1_TABLE OBJECT                   *****/
47/*****                                                               *****/
48/*****                                                               *****/
49/*************************************************************************/
50/*************************************************************************/
51/*************************************************************************/
52
53
54/*************************************************************************/
55/*                                                                       */
56/* <Function> T1_New_Table                                               */
57/*                                                                       */
58/* <Description>                                                         */
59/*    Initialise a T1_Table.                                             */
60/*                                                                       */
61/* <Input>                                                               */
62/*    table  :: address of target table                                  */
63/*    count  :: table size = maximum number of elements                  */
64/*    memory :: memory object to use for all subsequent reallocations    */
65/*                                                                       */
66/* <Return>                                                              */
67/*    Error code. 0 means success                                        */
68/*                                                                       */
69
70  LOCAL_FUNC
71  FT_Error  T1_New_Table( T1_Table*  table,
72                          FT_Int     count,
73                          FT_Memory  memory )
74  {
75	 FT_Error  error;
76
77	 table->memory = memory;
78	 if ( ALLOC_ARRAY( table->elements, count, FT_Byte*  ) ||
79          ALLOC_ARRAY( table->lengths, count, FT_Byte* ) )
80       goto Exit;
81
82	table->max_elems = count;
83    table->init      = 0xdeadbeef;
84	table->num_elems = 0;
85	table->block     = 0;
86	table->capacity  = 0;
87	table->cursor    = 0;
88
89  Exit:
90    if (error) FREE(table->elements);
91
92	return error;
93  }
94
95
96
97/*************************************************************************/
98/*                                                                       */
99/* <Function> T1_Add_Table                                               */
100/*                                                                       */
101/* <Description>                                                         */
102/*    Adds an object to a T1_Table, possibly growing its memory block    */
103/*                                                                       */
104/* <Input>                                                               */
105/*    table  :: target table                                             */
106/*    index  :: index of object in table                                 */
107/*    object :: address of object to copy in memory                      */
108/*    length :: length in bytes of source object                         */
109/*                                                                       */
110/* <Return>                                                              */
111/*    Error code. 0 means success. An error is returned when a           */
112/*    realloc failed..                                                   */
113/*                                                                       */
114
115
116      static void  shift_elements( T1_Table*  table, FT_Byte*  old_base )
117      {
118        FT_Long    delta  = table->block - old_base;
119        FT_Byte**  offset = table->elements;
120        FT_Byte**  limit  = offset + table->max_elems;
121
122        if (delta)
123          for ( ; offset < limit; offset++ )
124          {
125            if (offset[0])
126              offset[0] += delta;
127          }
128      }
129
130      static
131      FT_Error  reallocate_t1_table( T1_Table*  table,
132                                     FT_Int     new_size )
133      {
134        FT_Memory  memory   = table->memory;
135        FT_Byte*   old_base = table->block;
136        FT_Error   error;
137
138        /* realloc the base block */
139        if ( REALLOC( table->block, table->capacity, new_size ) )
140          return error;
141
142        table->capacity = new_size;
143
144        /* shift all offsets when needed */
145        if (old_base)
146          shift_elements( table, old_base );
147
148        return T1_Err_Ok;
149      }
150
151
152
153  LOCAL_FUNC
154  FT_Error  T1_Add_Table( T1_Table*  table,
155                          FT_Int     index,
156                          void*      object,
157                          FT_Int     length )
158  {
159	if (index < 0 || index > table->max_elems)
160    {
161	  FT_ERROR(( "T1.Add_Table: invalid index\n" ));
162	  return T1_Err_Syntax_Error;
163    }
164
165    /* grow the base block if needed */
166    if ( table->cursor + length > table->capacity )
167    {
168      FT_Error  error;
169      FT_Int    new_size = table->capacity;
170
171      while ( new_size < table->cursor+length )
172        new_size += 1024;
173
174      error = reallocate_t1_table( table, new_size );
175      if (error) return error;
176    }
177
178    /* add the object to the base block and adjust offset */
179    table->elements[ index ] = table->block + table->cursor;
180    table->lengths [ index ] = length;
181    MEM_Copy( table->block + table->cursor, object, length );
182
183    table->cursor += length;
184    return T1_Err_Ok;
185  }
186
187
188/*************************************************************************/
189/*                                                                       */
190/* <Function> T1_Done_Table                                              */
191/*                                                                       */
192/* <Description>                                                         */
193/*    Finalise a T1_Table. (realloc it to its current cursor).           */
194/*                                                                       */
195/* <Input>                                                               */
196/*    table :: target table                                              */
197/*                                                                       */
198/* <Note>                                                                */
199/*    This function does NOT release the heap's memory block. It is up   */
200/*    to the caller to clean it, or reference it in its own structures.  */
201/*                                                                       */
202#if 0
203  LOCAL_FUNC
204  void  T1_Done_Table( T1_Table*  table )
205  {
206    FT_Memory  memory = table->memory;
207    FT_Error   error;
208    FT_Byte*   old_base;
209
210    /* should never fail, as rec.cursor <= rec.size */
211    old_base = table->block;
212    if (!old_base)
213      return;
214
215    (void)REALLOC( table->block, table->capacity, table->cursor );
216    table->capacity = table->cursor;
217
218    if (old_base != table->block)
219      shift_elements( table, old_base );
220  }
221#endif
222
223  LOCAL_FUNC
224  void  T1_Release_Table( T1_Table*  table )
225  {
226    FT_Memory  memory = table->memory;
227
228    if (table->init == (FT_Long)0xdeadbeef)
229    {
230      FREE( table->block );
231      FREE( table->elements );
232      FREE( table->lengths );
233      table->init = 0;
234    }
235  }
236
237#endif
238
239/*************************************************************************/
240/*************************************************************************/
241/*************************************************************************/
242/*****                                                               *****/
243/*****               INPUT STREAM PARSER                             *****/
244/*****                                                               *****/
245/*****                                                               *****/
246/*************************************************************************/
247/*************************************************************************/
248/*************************************************************************/
249
250  #define IS_T1_WHITESPACE(c)  ( (c) == ' '  || (c) == '\t' )
251  #define IS_T1_LINESPACE(c)   ( (c) == '\r' || (c) == '\n' )
252
253  #define IS_T1_SPACE(c)  ( IS_T1_WHITESPACE(c) || IS_T1_LINESPACE(c) )
254
255  LOCAL_FUNC
256  void     CID_Skip_Spaces( CID_Parser*  parser )
257  {
258    FT_Byte* cur   = parser->cursor;
259    FT_Byte* limit = parser->limit;
260
261    while (cur < limit)
262    {
263      FT_Byte  c = *cur;
264      if (!IS_T1_SPACE(c))
265        break;
266      cur++;
267    }
268    parser->cursor = cur;
269  }
270
271  LOCAL_FUNC
272  void  CID_ToToken( CID_Parser*    parser,
273                     T1_Token_Rec*  token )
274  {
275    FT_Byte*  cur;
276    FT_Byte*  limit;
277    FT_Byte   starter, ender;
278    FT_Int    embed;
279
280    token->type  = t1_token_none;
281    token->start = 0;
282    token->limit = 0;
283
284    /* first of all, skip space */
285    CID_Skip_Spaces(parser);
286
287    cur   = parser->cursor;
288    limit = parser->limit;
289
290    if ( cur < limit )
291    {
292      switch (*cur)
293      {
294        /************* check for strings ***********************/
295        case '(':
296          token->type = t1_token_string;
297          ender = ')';
298          goto Lookup_Ender;
299
300        /************* check for programs/array ****************/
301        case '{':
302          token->type = t1_token_array;
303          ender = '}';
304          goto Lookup_Ender;
305
306        /************* check for table/array ******************/
307        case '[':
308          token->type = t1_token_array;
309          ender = ']';
310
311        Lookup_Ender:
312          embed   = 1;
313          starter = *cur++;
314          token->start = cur;
315          while (cur < limit)
316          {
317            if (*cur == starter)
318              embed++;
319            else if (*cur == ender)
320            {
321              embed--;
322              if (embed <= 0)
323              {
324                token->limit = cur++;
325                break;
326              }
327            }
328            cur++;
329          }
330          break;
331
332        /* **************** otherwise, it's any token **********/
333        default:
334          token->start = cur++;
335          token->type  = t1_token_any;
336          while (cur < limit && !IS_T1_SPACE(*cur))
337            cur++;
338
339          token->limit = cur;
340      }
341
342      if (!token->limit)
343      {
344        token->start = 0;
345        token->type  = t1_token_none;
346      }
347
348      parser->cursor = cur;
349    }
350  }
351
352
353  LOCAL_FUNC
354  void  CID_ToTokenArray( CID_Parser*    parser,
355                          T1_Token_Rec*  tokens,
356                          FT_UInt        max_tokens,
357                          FT_Int        *pnum_tokens )
358  {
359    T1_Token_Rec  master;
360
361    *pnum_tokens = -1;
362
363    CID_ToToken( parser, &master );
364    if (master.type == t1_token_array)
365    {
366      FT_Byte*       old_cursor = parser->cursor;
367      FT_Byte*       old_limit  = parser->limit;
368      T1_Token_Rec*  cur        = tokens;
369      T1_Token_Rec*  limit      = cur + max_tokens;
370
371      parser->cursor = master.start;
372      parser->limit  = master.limit;
373
374      while (parser->cursor < parser->limit)
375      {
376        T1_Token_Rec  token;
377
378        CID_ToToken( parser, &token );
379        if (!token.type)
380          break;
381
382        if (cur < limit)
383          *cur = token;
384
385        cur++;
386      }
387
388      *pnum_tokens = cur - tokens;
389
390      parser->cursor = old_cursor;
391      parser->limit  = old_limit;
392    }
393  }
394
395
396  static
397  FT_Long  t1_toint( FT_Byte* *cursor,
398                     FT_Byte*  limit )
399  {
400    FT_Long  result = 0;
401    FT_Byte* cur    = *cursor;
402    FT_Byte  c, d;
403
404    for (; cur < limit; cur++)
405    {
406      c = *cur;
407      d = (FT_Byte)(c - '0');
408      if (d < 10) break;
409
410      if ( c=='-' )
411      {
412        cur++;
413        break;
414      }
415    }
416
417    if (cur < limit)
418    {
419      do
420      {
421        d = (FT_Byte)(cur[0] - '0');
422        if (d >= 10)
423          break;
424
425        result = result*10 + d;
426        cur++;
427
428      } while (cur < limit);
429
430      if (c == '-')
431        result = -result;
432    }
433
434    *cursor = cur;
435    return result;
436  }
437
438
439  static
440  FT_Long  t1_tofixed( FT_Byte* *cursor,
441                       FT_Byte*  limit,
442                       FT_Long   power_ten )
443  {
444    FT_Byte* cur    = *cursor;
445    FT_Long  num, divider, result;
446    FT_Int   sign   = 0;
447    FT_Byte  d;
448
449    if (cur >= limit) return 0;
450
451    /* first of all, read the integer part */
452    result  = t1_toint( &cur, limit ) << 16;
453    num     = 0;
454    divider = 1;
455
456    if (result < 0)
457    {
458      sign   = 1;
459      result = -result;
460    }
461    if (cur >= limit) goto Exit;
462
463    /* read decimal part, if any */
464    if (*cur == '.' && cur+1 < limit)
465    {
466      cur++;
467
468      for (;;)
469      {
470        d = (FT_Byte)(*cur - '0');
471        if (d >= 10) break;
472
473        if (divider < 10000000L)
474        {
475          num      = num*10 + d;
476          divider *= 10;
477        }
478        cur++;
479        if (cur >= limit) break;
480      }
481    }
482
483    /* read exponent, if any */
484    if ( cur+1 < limit && (*cur == 'e' || *cur == 'E'))
485    {
486      cur++;
487      power_ten += t1_toint( &cur, limit );
488    }
489
490  Exit:
491    /* raise to power of ten if needed */
492    while (power_ten > 0)
493    {
494      result = result*10;
495      num    = num*10;
496      power_ten--;
497    }
498
499    while (power_ten < 0)
500    {
501      result  = result/10;
502      divider = divider*10;
503      power_ten++;
504    }
505
506    if (num)
507      result += FT_DivFix( num, divider );
508
509    if (sign)
510      result = -result;
511
512    *cursor = cur;
513    return result;
514  }
515
516
517  static
518  int  t1_tobool( FT_Byte* *cursor, FT_Byte* limit )
519  {
520    FT_Byte*  cur    = *cursor;
521    T1_Bool   result = 0;
522
523    /* return 1 if we find a "true", 0 otherwise */
524    if ( cur+3 < limit &&
525         cur[0] == 't' &&
526         cur[1] == 'r' &&
527         cur[2] == 'u' &&
528         cur[3] == 'e' )
529    {
530      result = 1;
531      cur   += 5;
532    }
533    else if ( cur+4 < limit &&
534              cur[0] == 'f' &&
535              cur[1] == 'a' &&
536              cur[2] == 'l' &&
537              cur[3] == 's' &&
538              cur[4] == 'e' )
539    {
540      result = 0;
541      cur   += 6;
542    }
543    *cursor = cur;
544    return result;
545  }
546
547
548  static
549  FT_Int  t1_tocoordarray( FT_Byte*  *cursor,
550                           FT_Byte*   limit,
551                           FT_Int     max_coords,
552                           FT_Short*  coords )
553  {
554    FT_Byte*  cur   = *cursor;
555    FT_Int    count = 0;
556    FT_Byte   c, ender;
557
558    if (cur >= limit) goto Exit;
559
560    /* check for the beginning of an array. If not, only one number will be read */
561    c     = *cur;
562    ender = 0;
563
564    if (c == '[')
565      ender = ']';
566
567    if (c == '{')
568      ender = '}';
569
570    if (ender)
571      cur++;
572
573    /* now, read the coordinates */
574    for ( ; cur < limit; )
575    {
576      /* skip whitespace in front of data */
577      for (;;)
578      {
579        c = *cur;
580        if ( c != ' ' && c != '\t' ) break;
581
582        cur++;
583        if (cur >= limit) goto Exit;
584      }
585
586      if (count >= max_coords || c == ender)
587        break;
588
589      coords[count] = (T1_Short)(t1_tofixed(&cur,limit,0) >> 16);
590      count++;
591
592      if (!ender)
593        break;
594    }
595
596  Exit:
597    *cursor = cur;
598    return count;
599  }
600
601
602
603  static
604  FT_Int  t1_tofixedarray( FT_Byte*  *cursor,
605                           FT_Byte*   limit,
606                           FT_Int     max_values,
607                           FT_Fixed*  values,
608                           FT_Int     power_ten )
609  {
610    FT_Byte*  cur   = *cursor;
611    FT_Int    count = 0;
612    FT_Byte   c, ender;
613
614    if (cur >= limit) goto Exit;
615
616    /* check for the beginning of an array. If not, only one number will be read */
617    c     = *cur;
618    ender = 0;
619
620    if (c == '[')
621      ender = ']';
622
623    if (c == '{')
624      ender = '}';
625
626    if (ender)
627      cur++;
628
629    /* now, read the values */
630    for ( ; cur < limit; )
631    {
632      /* skip whitespace in front of data */
633      for (;;)
634      {
635        c = *cur;
636        if ( c != ' ' && c != '\t' ) break;
637
638        cur++;
639        if (cur >= limit) goto Exit;
640      }
641
642      if (count >= max_values || c == ender)
643        break;
644
645      values[count] = t1_tofixed(&cur,limit,power_ten);
646      count++;
647
648      if (!ender)
649        break;
650    }
651
652  Exit:
653    *cursor = cur;
654    return count;
655  }
656
657
658
659 /* Loads a simple field (i.e. non-table) into the current list of objects */
660  LOCAL_FUNC
661  FT_Error  CID_Load_Field( CID_Parser*          parser,
662                            const T1_Field_Rec*  field,
663                            void*                object )
664  {
665    T1_Token_Rec  token;
666    FT_Byte*      cur;
667    FT_Byte*      limit;
668    FT_UInt       count;
669    FT_UInt       index;
670    FT_Error      error;
671
672    CID_ToToken( parser, &token );
673    if (!token.type)
674      goto Fail;
675
676    count = 1;
677    index = 0;
678    cur   = token.start;
679    limit = token.limit;
680
681    {
682      FT_Byte*   q = (FT_Byte*)object + field->offset;
683      FT_Long    val;
684      T1_String* string;
685
686      switch (field->type)
687      {
688        case t1_field_bool:
689          {
690            val = t1_tobool( &cur, limit );
691            goto Store_Integer;
692          }
693
694        case t1_field_fixed:
695          {
696            val = t1_tofixed( &cur, limit, 0 );
697            goto Store_Integer;
698          }
699
700        case t1_field_integer:
701          {
702            val = t1_toint( &cur, limit );
703          Store_Integer:
704            switch (field->size)
705            {
706              case 1:  *(FT_Byte*)  q = (FT_Byte)val;   break;
707              case 2:  *(FT_UShort*)q = (FT_UShort)val; break;
708              default: *(FT_Long*)  q = val;
709            }
710          }
711          break;
712
713        case t1_field_string:
714          {
715            FT_Memory  memory = parser->memory;
716            FT_UInt    len    = limit-cur;
717
718            if ( ALLOC( string, len+1 ) )
719              goto Exit;
720
721            MEM_Copy( string, cur, len );
722            string[len] = 0;
723
724            *(T1_String**)q = string;
725          }
726          break;
727
728        default:
729          /* an error occured */
730          goto Fail;
731      }
732    }
733    error    = 0;
734
735  Exit:
736    return error;
737  Fail:
738    error = T1_Err_Invalid_File_Format;
739    goto Exit;
740  }
741
742
743#define CID_MAX_TABLE_ELEMENTS  32
744
745  LOCAL_FUNC
746  FT_Error  CID_Load_Field_Table( CID_Parser*          parser,
747                                  const T1_Field_Rec*  field,
748                                  void*                object )
749  {
750    T1_Token_Rec  elements[CID_MAX_TABLE_ELEMENTS];
751    T1_Token_Rec* token;
752    FT_Int        num_elements;
753    FT_Error      error = 0;
754    FT_Byte*      old_cursor;
755    FT_Byte*      old_limit;
756    T1_Field_Rec  fieldrec = *(T1_Field_Rec*)field;
757
758    fieldrec.type = t1_field_integer;
759    if (field->type == t1_field_fixed_array )
760      fieldrec.type = t1_field_fixed;
761
762    CID_ToTokenArray( parser, elements, 32, &num_elements );
763    if (num_elements < 0)
764      goto Fail;
765
766    if (num_elements > CID_MAX_TABLE_ELEMENTS)
767      num_elements = CID_MAX_TABLE_ELEMENTS;
768
769    old_cursor = parser->cursor;
770    old_limit  = parser->limit;
771
772    /* we store the elements count */
773    if (field->count_offset)
774      *(FT_Byte*)((FT_Byte*)object + field->count_offset) = num_elements;
775
776    /* we now load each element, adjusting the field.offset on each one */
777    token = elements;
778    for ( ; num_elements > 0; num_elements--, token++ )
779    {
780      parser->cursor = token->start;
781      parser->limit  = token->limit;
782      CID_Load_Field( parser, &fieldrec, object );
783      fieldrec.offset += fieldrec.size;
784    }
785
786    parser->cursor = old_cursor;
787    parser->limit  = old_limit;
788
789  Exit:
790    return error;
791  Fail:
792    error = T1_Err_Invalid_File_Format;
793    goto Exit;
794  }
795
796
797
798
799
800
801
802  LOCAL_FUNC
803  FT_Long  CID_ToInt  ( CID_Parser*  parser )
804  {
805    return t1_toint( &parser->cursor, parser->limit );
806  }
807
808
809  LOCAL_FUNC
810  FT_Int  CID_ToCoordArray( CID_Parser* parser,
811                            FT_Int      max_coords,
812                            FT_Short*   coords )
813  {
814    return t1_tocoordarray( &parser->cursor, parser->limit, max_coords, coords );
815  }
816
817
818  LOCAL_FUNC
819  FT_Int  CID_ToFixedArray( CID_Parser* parser,
820                            FT_Int      max_values,
821                            FT_Fixed*   values,
822                            FT_Int      power_ten )
823  {
824    return t1_tofixedarray( &parser->cursor, parser->limit, max_values, values, power_ten );
825  }
826
827
828#if 0
829 /* return the value of an hexadecimal digit */
830 static
831 int  hexa_value( char c )
832 {
833   unsigned int  d;
834
835   d = (unsigned int)(c-'0');
836   if ( d <= 9 ) return (int)d;
837
838   d = (unsigned int)(c-'a');
839   if ( d <= 5 ) return (int)(d+10);
840
841   d = (unsigned int)(c-'A');
842   if ( d <= 5 ) return (int)(d+10);
843
844   return -1;
845 }
846#endif
847
848
849  LOCAL_FUNC
850  FT_Error  CID_New_Parser( CID_Parser*  parser,
851                            FT_Stream    stream,
852                            FT_Memory    memory )
853  {
854    FT_Error  error;
855    FT_ULong  base_offset, offset, ps_len;
856    FT_Byte   buffer[ 256 + 10 ];
857    FT_Int    buff_len;
858
859    MEM_Set( parser, 0, sizeof(*parser ) );
860    parser->stream = stream;
861    parser->memory = memory;
862
863    base_offset = FILE_Pos();
864
865    /* first of all, check the font format in the  header */
866    if ( ACCESS_Frame(31) )
867      goto Exit;
868
869    if ( strncmp( stream->cursor, "%!PS-Adobe-3.0 Resource-CIDFont", 31 ) )
870    {
871      FT_ERROR(( "[not a valid CID-keyed font]\n" ));
872      error = FT_Err_Unknown_File_Format;
873    }
874
875    FORGET_Frame();
876    if (error) goto Exit;
877
878    /* now, read the rest of the file, until we find a "StartData" */
879    buff_len = 256;
880    for (;;)
881    {
882      FT_Byte *p, *limit = buffer + 256;
883
884      /* fill input buffer */
885      buff_len -= 256;
886      if (buff_len > 0)
887        MEM_Move( buffer, limit, buff_len );
888
889      if ( FILE_Read( buffer, 256+10-buff_len ) )
890        goto Exit;
891
892      buff_len = 256+10;
893
894      /* look for "StartData" */
895      for ( p = buffer; p < limit; p++ )
896      {
897        if ( p[0] == 'S' && strncmp( (char*)p, "StartData", 9 ) == 0 )
898        {
899          /* save offset of binary data after "StartData" */
900          offset = FILE_Pos() - ( limit-p ) + 10;
901          goto Found;
902        }
903      }
904    }
905
906  Found:
907    /* all right, we found the start of the binary data. We will now rewind */
908    /* and extract the frame of corresponding to the Postscript section     */
909    ps_len = offset - base_offset;
910    if ( FILE_Seek( base_offset )                    ||
911         EXTRACT_Frame( ps_len, parser->postscript ) )
912      goto Exit;
913
914    parser->data_offset    = offset;
915    parser->postscript_len = ps_len;
916    parser->cursor         = parser->postscript;
917    parser->limit          = parser->cursor + ps_len;
918    parser->num_dict       = -1;
919
920  Exit:
921    return error;
922  }
923
924
925
926  LOCAL_FUNC
927  void  CID_Done_Parser( CID_Parser*  parser )
928  {
929    /* always free the private dictionary */
930    if (parser->postscript)
931    {
932      FT_Stream  stream = parser->stream;
933      RELEASE_Frame( parser->postscript );
934    }
935  }
936
937