• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  *
3  * cidload.c
4  *
5  *   CID-keyed Type1 font loader (body).
6  *
7  * Copyright 1996-2018 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  */
17 
18 
19 #include <ft2build.h>
20 #include FT_INTERNAL_DEBUG_H
21 #include FT_CONFIG_CONFIG_H
22 #include FT_MULTIPLE_MASTERS_H
23 #include FT_INTERNAL_TYPE1_TYPES_H
24 #include FT_INTERNAL_POSTSCRIPT_AUX_H
25 
26 #include "cidload.h"
27 
28 #include "ciderrs.h"
29 
30 
31   /**************************************************************************
32    *
33    * The macro FT_COMPONENT is used in trace mode.  It is an implicit
34    * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
35    * messages during execution.
36    */
37 #undef  FT_COMPONENT
38 #define FT_COMPONENT  trace_cidload
39 
40 
41   /* read a single offset */
42   FT_LOCAL_DEF( FT_ULong )
cid_get_offset(FT_Byte ** start,FT_Byte offsize)43   cid_get_offset( FT_Byte*  *start,
44                   FT_Byte    offsize )
45   {
46     FT_ULong  result;
47     FT_Byte*  p = *start;
48 
49 
50     for ( result = 0; offsize > 0; offsize-- )
51     {
52       result <<= 8;
53       result  |= *p++;
54     }
55 
56     *start = p;
57     return result;
58   }
59 
60 
61   /*************************************************************************/
62   /*************************************************************************/
63   /*****                                                               *****/
64   /*****                    TYPE 1 SYMBOL PARSING                      *****/
65   /*****                                                               *****/
66   /*************************************************************************/
67   /*************************************************************************/
68 
69 
70   static FT_Error
cid_load_keyword(CID_Face face,CID_Loader * loader,const T1_Field keyword)71   cid_load_keyword( CID_Face        face,
72                     CID_Loader*     loader,
73                     const T1_Field  keyword )
74   {
75     FT_Error      error;
76     CID_Parser*   parser = &loader->parser;
77     FT_Byte*      object;
78     void*         dummy_object;
79     CID_FaceInfo  cid = &face->cid;
80 
81 
82     /* if the keyword has a dedicated callback, call it */
83     if ( keyword->type == T1_FIELD_TYPE_CALLBACK )
84     {
85       FT_TRACE4(( "  %s", keyword->ident ));
86 
87       keyword->reader( (FT_Face)face, parser );
88       error = parser->root.error;
89       goto Exit;
90     }
91 
92     /* we must now compute the address of our target object */
93     switch ( keyword->location )
94     {
95     case T1_FIELD_LOCATION_CID_INFO:
96       object = (FT_Byte*)cid;
97       break;
98 
99     case T1_FIELD_LOCATION_FONT_INFO:
100       object = (FT_Byte*)&cid->font_info;
101       break;
102 
103     case T1_FIELD_LOCATION_FONT_EXTRA:
104       object = (FT_Byte*)&face->font_extra;
105       break;
106 
107     case T1_FIELD_LOCATION_BBOX:
108       object = (FT_Byte*)&cid->font_bbox;
109       break;
110 
111     default:
112       {
113         CID_FaceDict  dict;
114 
115 
116         if ( parser->num_dict < 0 || parser->num_dict >= cid->num_dicts )
117         {
118           FT_ERROR(( "cid_load_keyword: invalid use of `%s'\n",
119                      keyword->ident ));
120           error = FT_THROW( Syntax_Error );
121           goto Exit;
122         }
123 
124         dict = cid->font_dicts + parser->num_dict;
125         switch ( keyword->location )
126         {
127         case T1_FIELD_LOCATION_PRIVATE:
128           object = (FT_Byte*)&dict->private_dict;
129           break;
130 
131         default:
132           object = (FT_Byte*)dict;
133         }
134       }
135     }
136 
137     FT_TRACE4(( "  %s", keyword->ident ));
138 
139     dummy_object = object;
140 
141     /* now, load the keyword data in the object's field(s) */
142     if ( keyword->type == T1_FIELD_TYPE_INTEGER_ARRAY ||
143          keyword->type == T1_FIELD_TYPE_FIXED_ARRAY   )
144       error = cid_parser_load_field_table( &loader->parser, keyword,
145                                            &dummy_object );
146     else
147       error = cid_parser_load_field( &loader->parser,
148                                      keyword, &dummy_object );
149 
150     FT_TRACE4(( "\n" ));
151 
152   Exit:
153     return error;
154   }
155 
156 
157   FT_CALLBACK_DEF( FT_Error )
cid_parse_font_matrix(CID_Face face,CID_Parser * parser)158   cid_parse_font_matrix( CID_Face     face,
159                          CID_Parser*  parser )
160   {
161     CID_FaceDict  dict;
162     FT_Face       root = (FT_Face)&face->root;
163     FT_Fixed      temp[6];
164     FT_Fixed      temp_scale;
165 
166 
167     if ( parser->num_dict >= 0 && parser->num_dict < face->cid.num_dicts )
168     {
169       FT_Matrix*  matrix;
170       FT_Vector*  offset;
171       FT_Int      result;
172 
173 
174       dict   = face->cid.font_dicts + parser->num_dict;
175       matrix = &dict->font_matrix;
176       offset = &dict->font_offset;
177 
178       /* input is scaled by 1000 to accommodate default FontMatrix */
179       result = cid_parser_to_fixed_array( parser, 6, temp, 3 );
180 
181       if ( result < 6 )
182         return FT_THROW( Invalid_File_Format );
183 
184       FT_TRACE4(( " [%f %f %f %f %f %f]\n",
185                   (double)temp[0] / 65536 / 1000,
186                   (double)temp[1] / 65536 / 1000,
187                   (double)temp[2] / 65536 / 1000,
188                   (double)temp[3] / 65536 / 1000,
189                   (double)temp[4] / 65536 / 1000,
190                   (double)temp[5] / 65536 / 1000 ));
191 
192       temp_scale = FT_ABS( temp[3] );
193 
194       if ( temp_scale == 0 )
195       {
196         FT_ERROR(( "cid_parse_font_matrix: invalid font matrix\n" ));
197         return FT_THROW( Invalid_File_Format );
198       }
199 
200       /* atypical case */
201       if ( temp_scale != 0x10000L )
202       {
203         /* set units per EM based on FontMatrix values */
204         root->units_per_EM = (FT_UShort)FT_DivFix( 1000, temp_scale );
205 
206         temp[0] = FT_DivFix( temp[0], temp_scale );
207         temp[1] = FT_DivFix( temp[1], temp_scale );
208         temp[2] = FT_DivFix( temp[2], temp_scale );
209         temp[4] = FT_DivFix( temp[4], temp_scale );
210         temp[5] = FT_DivFix( temp[5], temp_scale );
211         temp[3] = temp[3] < 0 ? -0x10000L : 0x10000L;
212       }
213 
214       matrix->xx = temp[0];
215       matrix->yx = temp[1];
216       matrix->xy = temp[2];
217       matrix->yy = temp[3];
218 
219       if ( !FT_Matrix_Check( matrix ) )
220       {
221         FT_ERROR(( "t1_parse_font_matrix: invalid font matrix\n" ));
222         parser->root.error = FT_THROW( Invalid_File_Format );
223         return FT_THROW( Invalid_File_Format );
224       }
225 
226       /* note that the font offsets are expressed in integer font units */
227       offset->x  = temp[4] >> 16;
228       offset->y  = temp[5] >> 16;
229     }
230 
231     return FT_Err_Ok;
232   }
233 
234 
235   FT_CALLBACK_DEF( FT_Error )
parse_fd_array(CID_Face face,CID_Parser * parser)236   parse_fd_array( CID_Face     face,
237                   CID_Parser*  parser )
238   {
239     CID_FaceInfo  cid    = &face->cid;
240     FT_Memory     memory = face->root.memory;
241     FT_Stream     stream = parser->stream;
242     FT_Error      error  = FT_Err_Ok;
243     FT_Long       num_dicts;
244 
245 
246     num_dicts = cid_parser_to_int( parser );
247     if ( num_dicts < 0 )
248     {
249       FT_ERROR(( "parse_fd_array: invalid number of dictionaries\n" ));
250       error = FT_THROW( Invalid_File_Format );
251       goto Exit;
252     }
253 
254     FT_TRACE4(( " %d\n", num_dicts ));
255 
256     /*
257      * A single entry in the FDArray must (at least) contain the following
258      * structure elements.
259      *
260      *   %ADOBeginFontDict              18
261      *   X dict begin                   13
262      *     /FontMatrix [X X X X]        22
263      *     /Private X dict begin        22
264      *     end                           4
265      *   end                             4
266      *   %ADOEndFontDict                16
267      *
268      * This needs 18+13+22+22+4+4+16=99 bytes or more.  Normally, you also
269      * need a `dup X' at the very beginning and a `put' at the end, so a
270      * rough guess using 100 bytes as the minimum is justified.
271      */
272     if ( (FT_ULong)num_dicts > stream->size / 100 )
273     {
274       FT_TRACE0(( "parse_fd_array: adjusting FDArray size"
275                   " (from %d to %d)\n",
276                   num_dicts,
277                   stream->size / 100 ));
278       num_dicts = (FT_Long)( stream->size / 100 );
279     }
280 
281     if ( !cid->font_dicts )
282     {
283       FT_Int  n;
284 
285 
286       if ( FT_NEW_ARRAY( cid->font_dicts, num_dicts ) )
287         goto Exit;
288 
289       cid->num_dicts = num_dicts;
290 
291       /* set some default values (the same as for Type 1 fonts) */
292       for ( n = 0; n < cid->num_dicts; n++ )
293       {
294         CID_FaceDict  dict = cid->font_dicts + n;
295 
296 
297         dict->private_dict.blue_shift       = 7;
298         dict->private_dict.blue_fuzz        = 1;
299         dict->private_dict.lenIV            = 4;
300         dict->private_dict.expansion_factor = (FT_Fixed)( 0.06 * 0x10000L );
301         dict->private_dict.blue_scale       = (FT_Fixed)(
302                                                 0.039625 * 0x10000L * 1000 );
303       }
304     }
305 
306   Exit:
307     return error;
308   }
309 
310 
311   /* By mistake, `expansion_factor' appears both in PS_PrivateRec */
312   /* and CID_FaceDictRec (both are public header files and can't  */
313   /* changed).  We simply copy the value.                         */
314 
315   FT_CALLBACK_DEF( FT_Error )
parse_expansion_factor(CID_Face face,CID_Parser * parser)316   parse_expansion_factor( CID_Face     face,
317                           CID_Parser*  parser )
318   {
319     CID_FaceDict  dict;
320 
321 
322     if ( parser->num_dict >= 0 && parser->num_dict < face->cid.num_dicts )
323     {
324       dict = face->cid.font_dicts + parser->num_dict;
325 
326       dict->expansion_factor              = cid_parser_to_fixed( parser, 0 );
327       dict->private_dict.expansion_factor = dict->expansion_factor;
328 
329       FT_TRACE4(( "%d\n", dict->expansion_factor ));
330     }
331 
332     return FT_Err_Ok;
333   }
334 
335 
336   /* By mistake, `CID_FaceDictRec' doesn't contain a field for the */
337   /* `FontName' keyword.  FreeType doesn't need it, but it is nice */
338   /* to catch it for producing better trace output.                */
339 
340   FT_CALLBACK_DEF( FT_Error )
parse_font_name(CID_Face face,CID_Parser * parser)341   parse_font_name( CID_Face     face,
342                    CID_Parser*  parser )
343   {
344 #ifdef FT_DEBUG_LEVEL_TRACE
345     if ( parser->num_dict >= 0 && parser->num_dict < face->cid.num_dicts )
346     {
347       T1_TokenRec  token;
348       FT_UInt      len;
349 
350 
351       cid_parser_to_token( parser, &token );
352 
353       len = (FT_UInt)( token.limit - token.start );
354       if ( len )
355         FT_TRACE4(( " %.*s\n", len, token.start ));
356       else
357         FT_TRACE4(( " <no value>\n" ));
358     }
359 #else
360     FT_UNUSED( face );
361     FT_UNUSED( parser );
362 #endif
363 
364     return FT_Err_Ok;
365   }
366 
367 
368   static
369   const T1_FieldRec  cid_field_records[] =
370   {
371 
372 #include "cidtoken.h"
373 
374     T1_FIELD_CALLBACK( "FDArray",         parse_fd_array, 0 )
375     T1_FIELD_CALLBACK( "FontMatrix",      cid_parse_font_matrix, 0 )
376     T1_FIELD_CALLBACK( "ExpansionFactor", parse_expansion_factor, 0 )
377     T1_FIELD_CALLBACK( "FontName",        parse_font_name, 0 )
378 
379     { 0, T1_FIELD_LOCATION_CID_INFO, T1_FIELD_TYPE_NONE, 0, 0, 0, 0, 0, 0 }
380   };
381 
382 
383   static FT_Error
cid_parse_dict(CID_Face face,CID_Loader * loader,FT_Byte * base,FT_ULong size)384   cid_parse_dict( CID_Face     face,
385                   CID_Loader*  loader,
386                   FT_Byte*     base,
387                   FT_ULong     size )
388   {
389     CID_Parser*  parser = &loader->parser;
390 
391 
392     parser->root.cursor = base;
393     parser->root.limit  = base + size;
394     parser->root.error  = FT_Err_Ok;
395 
396     {
397       FT_Byte*  cur   = base;
398       FT_Byte*  limit = cur + size;
399 
400 
401       for (;;)
402       {
403         FT_Byte*  newlimit;
404 
405 
406         parser->root.cursor = cur;
407         cid_parser_skip_spaces( parser );
408 
409         if ( parser->root.cursor >= limit )
410           newlimit = limit - 1 - 17;
411         else
412           newlimit = parser->root.cursor - 17;
413 
414         /* look for `%ADOBeginFontDict' */
415         for ( ; cur < newlimit; cur++ )
416         {
417           if ( *cur == '%'                                            &&
418                ft_strncmp( (char*)cur, "%ADOBeginFontDict", 17 ) == 0 )
419           {
420             /* if /FDArray was found, then cid->num_dicts is > 0, and */
421             /* we can start increasing parser->num_dict               */
422             if ( face->cid.num_dicts > 0 )
423             {
424               parser->num_dict++;
425 
426 #ifdef FT_DEBUG_LEVEL_TRACE
427               FT_TRACE4(( " FontDict %d", parser->num_dict ));
428               if ( parser->num_dict > face->cid.num_dicts )
429                 FT_TRACE4(( " (ignored)" ));
430               FT_TRACE4(( "\n" ));
431 #endif
432             }
433           }
434         }
435 
436         cur = parser->root.cursor;
437         /* no error can occur in cid_parser_skip_spaces */
438         if ( cur >= limit )
439           break;
440 
441         cid_parser_skip_PS_token( parser );
442         if ( parser->root.cursor >= limit || parser->root.error )
443           break;
444 
445         /* look for immediates */
446         if ( *cur == '/' && cur + 2 < limit )
447         {
448           FT_UInt  len;
449 
450 
451           cur++;
452           len = (FT_UInt)( parser->root.cursor - cur );
453 
454           if ( len > 0 && len < 22 )
455           {
456             /* now compare the immediate name to the keyword table */
457             T1_Field  keyword = (T1_Field)cid_field_records;
458 
459 
460             for (;;)
461             {
462               FT_Byte*  name;
463 
464 
465               name = (FT_Byte*)keyword->ident;
466               if ( !name )
467                 break;
468 
469               if ( cur[0] == name[0]                     &&
470                    len == ft_strlen( (const char*)name ) )
471               {
472                 FT_UInt  n;
473 
474 
475                 for ( n = 1; n < len; n++ )
476                   if ( cur[n] != name[n] )
477                     break;
478 
479                 if ( n >= len )
480                 {
481                   /* we found it - run the parsing callback */
482                   parser->root.error = cid_load_keyword( face,
483                                                          loader,
484                                                          keyword );
485                   if ( parser->root.error )
486                     return parser->root.error;
487                   break;
488                 }
489               }
490               keyword++;
491             }
492           }
493         }
494 
495         cur = parser->root.cursor;
496       }
497 
498       if ( !face->cid.num_dicts )
499       {
500         FT_ERROR(( "cid_parse_dict: No font dictionary found\n" ));
501         return FT_THROW( Invalid_File_Format );
502       }
503     }
504 
505     return parser->root.error;
506   }
507 
508 
509   /* read the subrmap and the subrs of each font dict */
510   static FT_Error
cid_read_subrs(CID_Face face)511   cid_read_subrs( CID_Face  face )
512   {
513     CID_FaceInfo   cid    = &face->cid;
514     FT_Memory      memory = face->root.memory;
515     FT_Stream      stream = face->cid_stream;
516     FT_Error       error;
517     FT_Int         n;
518     CID_Subrs      subr;
519     FT_UInt        max_offsets = 0;
520     FT_ULong*      offsets = NULL;
521     PSAux_Service  psaux = (PSAux_Service)face->psaux;
522 
523 
524     if ( FT_NEW_ARRAY( face->subrs, cid->num_dicts ) )
525       goto Exit;
526 
527     subr = face->subrs;
528     for ( n = 0; n < cid->num_dicts; n++, subr++ )
529     {
530       CID_FaceDict  dict  = cid->font_dicts + n;
531       FT_Int        lenIV = dict->private_dict.lenIV;
532       FT_UInt       count, num_subrs = dict->num_subrs;
533       FT_ULong      data_len;
534       FT_Byte*      p;
535 
536 
537       if ( !num_subrs )
538         continue;
539 
540       /* reallocate offsets array if needed */
541       if ( num_subrs + 1 > max_offsets )
542       {
543         FT_UInt  new_max = FT_PAD_CEIL( num_subrs + 1, 4 );
544 
545 
546         if ( new_max <= max_offsets )
547         {
548           error = FT_THROW( Syntax_Error );
549           goto Fail;
550         }
551 
552         if ( FT_RENEW_ARRAY( offsets, max_offsets, new_max ) )
553           goto Fail;
554 
555         max_offsets = new_max;
556       }
557 
558       /* read the subrmap's offsets */
559       if ( FT_STREAM_SEEK( cid->data_offset + dict->subrmap_offset )     ||
560            FT_FRAME_ENTER( ( num_subrs + 1 ) * (FT_UInt)dict->sd_bytes ) )
561         goto Fail;
562 
563       p = (FT_Byte*)stream->cursor;
564       for ( count = 0; count <= num_subrs; count++ )
565         offsets[count] = cid_get_offset( &p, (FT_Byte)dict->sd_bytes );
566 
567       FT_FRAME_EXIT();
568 
569       /* offsets must be ordered */
570       for ( count = 1; count <= num_subrs; count++ )
571         if ( offsets[count - 1] > offsets[count] )
572         {
573           FT_ERROR(( "cid_read_subrs: offsets are not ordered\n" ));
574           error = FT_THROW( Invalid_File_Format );
575           goto Fail;
576         }
577 
578       if ( offsets[num_subrs] > stream->size - cid->data_offset )
579       {
580         FT_ERROR(( "cid_read_subrs: too large `subrs' offsets\n" ));
581         error = FT_THROW( Invalid_File_Format );
582         goto Fail;
583       }
584 
585       /* now, compute the size of subrs charstrings, */
586       /* allocate, and read them                     */
587       data_len = offsets[num_subrs] - offsets[0];
588 
589       if ( FT_NEW_ARRAY( subr->code, num_subrs + 1 ) ||
590            FT_ALLOC( subr->code[0], data_len )       )
591         goto Fail;
592 
593       if ( FT_STREAM_SEEK( cid->data_offset + offsets[0] ) ||
594            FT_STREAM_READ( subr->code[0], data_len )  )
595         goto Fail;
596 
597       /* set up pointers */
598       for ( count = 1; count <= num_subrs; count++ )
599       {
600         FT_ULong  len;
601 
602 
603         len               = offsets[count] - offsets[count - 1];
604         subr->code[count] = subr->code[count - 1] + len;
605       }
606 
607       /* decrypt subroutines, but only if lenIV >= 0 */
608       if ( lenIV >= 0 )
609       {
610         for ( count = 0; count < num_subrs; count++ )
611         {
612           FT_ULong  len;
613 
614 
615           len = offsets[count + 1] - offsets[count];
616           psaux->t1_decrypt( subr->code[count], len, 4330 );
617         }
618       }
619 
620       subr->num_subrs = (FT_Int)num_subrs;
621     }
622 
623   Exit:
624     FT_FREE( offsets );
625     return error;
626 
627   Fail:
628     if ( face->subrs )
629     {
630       for ( n = 0; n < cid->num_dicts; n++ )
631       {
632         if ( face->subrs[n].code )
633           FT_FREE( face->subrs[n].code[0] );
634 
635         FT_FREE( face->subrs[n].code );
636       }
637       FT_FREE( face->subrs );
638     }
639     goto Exit;
640   }
641 
642 
643   static void
cid_init_loader(CID_Loader * loader,CID_Face face)644   cid_init_loader( CID_Loader*  loader,
645                    CID_Face     face )
646   {
647     FT_UNUSED( face );
648 
649     FT_ZERO( loader );
650   }
651 
652 
653   static  void
cid_done_loader(CID_Loader * loader)654   cid_done_loader( CID_Loader*  loader )
655   {
656     CID_Parser*  parser = &loader->parser;
657 
658 
659     /* finalize parser */
660     cid_parser_done( parser );
661   }
662 
663 
664   static FT_Error
cid_hex_to_binary(FT_Byte * data,FT_ULong data_len,FT_ULong offset,CID_Face face)665   cid_hex_to_binary( FT_Byte*  data,
666                      FT_ULong  data_len,
667                      FT_ULong  offset,
668                      CID_Face  face )
669   {
670     FT_Stream  stream = face->root.stream;
671     FT_Error   error;
672 
673     FT_Byte    buffer[256];
674     FT_Byte   *p, *plimit;
675     FT_Byte   *d, *dlimit;
676     FT_Byte    val;
677 
678     FT_Bool    upper_nibble, done;
679 
680 
681     if ( FT_STREAM_SEEK( offset ) )
682       goto Exit;
683 
684     d      = data;
685     dlimit = d + data_len;
686     p      = buffer;
687     plimit = p;
688 
689     upper_nibble = 1;
690     done         = 0;
691 
692     while ( d < dlimit )
693     {
694       if ( p >= plimit )
695       {
696         FT_ULong  oldpos = FT_STREAM_POS();
697         FT_ULong  size   = stream->size - oldpos;
698 
699 
700         if ( size == 0 )
701         {
702           error = FT_THROW( Syntax_Error );
703           goto Exit;
704         }
705 
706         if ( FT_STREAM_READ( buffer, 256 > size ? size : 256 ) )
707           goto Exit;
708         p      = buffer;
709         plimit = p + FT_STREAM_POS() - oldpos;
710       }
711 
712       if ( ft_isdigit( *p ) )
713         val = (FT_Byte)( *p - '0' );
714       else if ( *p >= 'a' && *p <= 'f' )
715         val = (FT_Byte)( *p - 'a' );
716       else if ( *p >= 'A' && *p <= 'F' )
717         val = (FT_Byte)( *p - 'A' + 10 );
718       else if ( *p == ' '  ||
719                 *p == '\t' ||
720                 *p == '\r' ||
721                 *p == '\n' ||
722                 *p == '\f' ||
723                 *p == '\0' )
724       {
725         p++;
726         continue;
727       }
728       else if ( *p == '>' )
729       {
730         val  = 0;
731         done = 1;
732       }
733       else
734       {
735         error = FT_THROW( Syntax_Error );
736         goto Exit;
737       }
738 
739       if ( upper_nibble )
740         *d = (FT_Byte)( val << 4 );
741       else
742       {
743         *d = (FT_Byte)( *d + val );
744         d++;
745       }
746 
747       upper_nibble = (FT_Byte)( 1 - upper_nibble );
748 
749       if ( done )
750         break;
751 
752       p++;
753     }
754 
755     error = FT_Err_Ok;
756 
757   Exit:
758     return error;
759   }
760 
761 
762   FT_LOCAL_DEF( FT_Error )
cid_face_open(CID_Face face,FT_Int face_index)763   cid_face_open( CID_Face  face,
764                  FT_Int    face_index )
765   {
766     CID_Loader   loader;
767     CID_Parser*  parser;
768     FT_Memory    memory = face->root.memory;
769     FT_Error     error;
770     FT_Int       n;
771 
772     CID_FaceInfo  cid = &face->cid;
773 
774     FT_ULong  binary_length;
775     FT_ULong  entry_len;
776 
777 
778     cid_init_loader( &loader, face );
779 
780     parser = &loader.parser;
781     error = cid_parser_new( parser, face->root.stream, face->root.memory,
782                             (PSAux_Service)face->psaux );
783     if ( error )
784       goto Exit;
785 
786     error = cid_parse_dict( face, &loader,
787                             parser->postscript,
788                             parser->postscript_len );
789     if ( error )
790       goto Exit;
791 
792     if ( face_index < 0 )
793       goto Exit;
794 
795     if ( FT_NEW( face->cid_stream ) )
796       goto Exit;
797 
798     if ( parser->binary_length )
799     {
800       if ( parser->binary_length >
801              face->root.stream->size - parser->data_offset )
802       {
803         FT_TRACE0(( "cid_face_open: adjusting length of binary data\n"
804                     "               (from %d to %d bytes)\n",
805                     parser->binary_length,
806                     face->root.stream->size - parser->data_offset ));
807         parser->binary_length = face->root.stream->size -
808                                 parser->data_offset;
809       }
810 
811       /* we must convert the data section from hexadecimal to binary */
812       if ( FT_ALLOC( face->binary_data, parser->binary_length )    ||
813            FT_SET_ERROR( cid_hex_to_binary( face->binary_data,
814                                             parser->binary_length,
815                                             parser->data_offset,
816                                             face ) )               )
817         goto Exit;
818 
819       FT_Stream_OpenMemory( face->cid_stream,
820                             face->binary_data, parser->binary_length );
821       cid->data_offset = 0;
822     }
823     else
824     {
825       *face->cid_stream = *face->root.stream;
826       cid->data_offset  = loader.parser.data_offset;
827     }
828 
829     /* sanity tests */
830 
831     if ( cid->fd_bytes < 0 || cid->gd_bytes < 1 )
832     {
833       FT_ERROR(( "cid_face_open:"
834                  " Invalid `FDBytes' or `GDBytes' value\n" ));
835       error = FT_THROW( Invalid_File_Format );
836       goto Exit;
837     }
838 
839     /* allow at most 32bit offsets */
840     if ( cid->fd_bytes > 4 || cid->gd_bytes > 4 )
841     {
842       FT_ERROR(( "cid_face_open:"
843                  " Values of `FDBytes' or `GDBytes' larger than 4\n"
844                  "               "
845                  " are not supported\n" ));
846       error = FT_THROW( Invalid_File_Format );
847       goto Exit;
848     }
849 
850     binary_length = face->cid_stream->size - cid->data_offset;
851     entry_len     = (FT_ULong)( cid->fd_bytes + cid->gd_bytes );
852 
853     for ( n = 0; n < cid->num_dicts; n++ )
854     {
855       CID_FaceDict  dict = cid->font_dicts + n;
856 
857 
858       /* the upper limits are ad-hoc values */
859       if ( dict->private_dict.blue_shift > 1000 ||
860            dict->private_dict.blue_shift < 0    )
861       {
862         FT_TRACE2(( "cid_face_open:"
863                     " setting unlikely BlueShift value %d to default (7)\n",
864                     dict->private_dict.blue_shift ));
865         dict->private_dict.blue_shift = 7;
866       }
867 
868       if ( dict->private_dict.blue_fuzz > 1000 ||
869            dict->private_dict.blue_fuzz < 0    )
870       {
871         FT_TRACE2(( "cid_face_open:"
872                     " setting unlikely BlueFuzz value %d to default (1)\n",
873                     dict->private_dict.blue_fuzz ));
874         dict->private_dict.blue_fuzz = 1;
875       }
876 
877       if ( dict->sd_bytes < 0                        ||
878            ( dict->num_subrs && dict->sd_bytes < 1 ) )
879       {
880         FT_ERROR(( "cid_face_open: Invalid `SDBytes' value\n" ));
881         error = FT_THROW( Invalid_File_Format );
882         goto Exit;
883       }
884 
885       if ( dict->sd_bytes > 4 )
886       {
887         FT_ERROR(( "cid_face_open:"
888                    " Values of `SDBytes' larger than 4"
889                    " are not supported\n" ));
890         error = FT_THROW( Invalid_File_Format );
891         goto Exit;
892       }
893 
894       if ( dict->subrmap_offset > binary_length )
895       {
896         FT_ERROR(( "cid_face_open: Invalid `SubrMapOffset' value\n" ));
897         error = FT_THROW( Invalid_File_Format );
898         goto Exit;
899       }
900 
901       /* `num_subrs' is scanned as a signed integer */
902       if ( (FT_Int)dict->num_subrs < 0                                     ||
903            ( dict->sd_bytes                                              &&
904              dict->num_subrs > ( binary_length - dict->subrmap_offset ) /
905                                  (FT_UInt)dict->sd_bytes                 ) )
906       {
907         FT_ERROR(( "cid_face_open: Invalid `SubrCount' value\n" ));
908         error = FT_THROW( Invalid_File_Format );
909         goto Exit;
910       }
911     }
912 
913     if ( cid->cidmap_offset > binary_length )
914     {
915       FT_ERROR(( "cid_face_open: Invalid `CIDMapOffset' value\n" ));
916       error = FT_THROW( Invalid_File_Format );
917       goto Exit;
918     }
919 
920     if ( entry_len                                            &&
921          cid->cid_count >
922            ( binary_length - cid->cidmap_offset ) / entry_len )
923     {
924       FT_ERROR(( "cid_face_open: Invalid `CIDCount' value\n" ));
925       error = FT_THROW( Invalid_File_Format );
926       goto Exit;
927     }
928 
929     /* we can now safely proceed */
930     error = cid_read_subrs( face );
931 
932   Exit:
933     cid_done_loader( &loader );
934     return error;
935   }
936 
937 
938 /* END */
939