1 /***************************************************************************/
   2 /*                                                                         */
   3 /*  cidparse.c                                                             */
   4 /*                                                                         */
   5 /*    CID-keyed Type1 parser (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_INTERNAL_OBJECTS_H
  22 #include FT_INTERNAL_STREAM_H
  23 
  24 #include "cidparse.h"
  25 
  26 #include "ciderrs.h"
  27 
  28 
  29   /*************************************************************************/
  30   /*                                                                       */
  31   /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
  32   /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
  33   /* messages during execution.                                            */
  34   /*                                                                       */
  35 #undef  FT_COMPONENT
  36 #define FT_COMPONENT  trace_cidparse
  37 
  38 
  39   /*************************************************************************/
  40   /*************************************************************************/
  41   /*************************************************************************/
  42   /*****                                                               *****/
  43   /*****                    INPUT STREAM PARSER                        *****/
  44   /*****                                                               *****/
  45   /*************************************************************************/
  46   /*************************************************************************/
  47   /*************************************************************************/
  48 
  49 
  50 #define STARTDATA      "StartData"
  51 #define STARTDATA_LEN  ( sizeof ( STARTDATA ) - 1 )
  52 #define SFNTS          "/sfnts"
  53 #define SFNTS_LEN      ( sizeof ( SFNTS ) - 1 )
  54 
  55 
  56   FT_LOCAL_DEF( FT_Error )
  57   cid_parser_new( CID_Parser*    parser,
  58                   FT_Stream      stream,
  59                   FT_Memory      memory,
  60                   PSAux_Service  psaux )
  61   {
  62     FT_Error  error;
  63     FT_ULong  base_offset, offset, ps_len;
  64     FT_Byte   *cur, *limit;
  65     FT_Byte   *arg1, *arg2;
  66 
  67 
  68     FT_ZERO( parser );
  69     psaux->ps_parser_funcs->init( &parser->root, 0, 0, memory );
  70 
  71     parser->stream = stream;
  72 
  73     base_offset = FT_STREAM_POS();
  74 
  75     /* first of all, check the font format in the header */
  76     if ( FT_FRAME_ENTER( 31 ) )
  77       goto Exit;
  78 
  79     if ( ft_strncmp( (char *)stream->cursor,
  80                      "%!PS-Adobe-3.0 Resource-CIDFont", 31 ) )
  81     {
  82       FT_TRACE2(( "  not a CID-keyed font\n" ));
  83       error = FT_THROW( Unknown_File_Format );
  84     }
  85 
  86     FT_FRAME_EXIT();
  87     if ( error )
  88       goto Exit;
  89 
  90   Again:
  91     /* now, read the rest of the file until we find */
  92     /* `StartData' or `/sfnts'                      */
  93     {
  94       /*
  95        * The algorithm is as follows (omitting the case with less than 256
  96        * bytes to fill for simplicity).
  97        *
  98        * 1. Fill the buffer with 256 + STARTDATA_LEN bytes.
  99        *
 100        * 2. Search for the STARTDATA and SFNTS strings at positions
 101        *    buffer[0], buffer[1], ...,
 102        *    buffer[255 + STARTDATA_LEN - SFNTS_LEN].
 103        *
 104        * 3. Move the last STARTDATA_LEN bytes to buffer[0].
 105        *
 106        * 4. Fill the buffer with 256 bytes, starting at STARTDATA_LEN.
 107        *
 108        * 5. Repeat with step 2.
 109        *
 110        */
 111       FT_Byte  buffer[256 + STARTDATA_LEN + 1];
 112 
 113       /* values for the first loop */
 114       FT_ULong  read_len    = 256 + STARTDATA_LEN;
 115       FT_ULong  read_offset = 0;
 116       FT_Byte*  p           = buffer;
 117 
 118 
 119       for ( offset = FT_STREAM_POS(); ; offset += 256 )
 120       {
 121         FT_ULong  stream_len;
 122 
 123 
 124         stream_len = stream->size - FT_STREAM_POS();
 125 
 126         read_len = FT_MIN( read_len, stream_len );
 127         if ( FT_STREAM_READ( p, read_len ) )
 128           goto Exit;
 129 
 130         /* ensure that we do not compare with data beyond the buffer */
 131         p[read_len] = '\0';
 132 
 133         limit = p + read_len - SFNTS_LEN;
 134 
 135         for ( p = buffer; p < limit; p++ )
 136         {
 137           if ( p[0] == 'S'                                           &&
 138                ft_strncmp( (char*)p, STARTDATA, STARTDATA_LEN ) == 0 )
 139           {
 140             /* save offset of binary data after `StartData' */
 141             offset += (FT_ULong)( p - buffer ) + STARTDATA_LEN + 1;
 142             goto Found;
 143           }
 144           else if ( p[1] == 's'                                   &&
 145                     ft_strncmp( (char*)p, SFNTS, SFNTS_LEN ) == 0 )
 146           {
 147             offset += (FT_ULong)( p - buffer ) + SFNTS_LEN + 1;
 148             goto Found;
 149           }
 150         }
 151 
 152         if ( read_offset + read_len < STARTDATA_LEN )
 153         {
 154           FT_TRACE2(( "cid_parser_new: no `StartData' keyword found\n" ));
 155           error = FT_THROW( Invalid_File_Format );
 156           goto Exit;
 157         }
 158 
 159         FT_MEM_MOVE( buffer,
 160                      buffer + read_offset + read_len - STARTDATA_LEN,
 161                      STARTDATA_LEN );
 162 
 163         /* values for the next loop */
 164         read_len    = 256;
 165         read_offset = STARTDATA_LEN;
 166         p           = buffer + read_offset;
 167       }
 168     }
 169 
 170   Found:
 171     /* We have found the start of the binary data or the `/sfnts' token. */
 172     /* Now rewind and extract the frame corresponding to this PostScript */
 173     /* section.                                                          */
 174 
 175     ps_len = offset - base_offset;
 176     if ( FT_STREAM_SEEK( base_offset )                  ||
 177          FT_FRAME_EXTRACT( ps_len, parser->postscript ) )
 178       goto Exit;
 179 
 180     parser->data_offset    = offset;
 181     parser->postscript_len = ps_len;
 182     parser->root.base      = parser->postscript;
 183     parser->root.cursor    = parser->postscript;
 184     parser->root.limit     = parser->root.cursor + ps_len;
 185     parser->num_dict       = -1;
 186 
 187     /* Finally, we check whether `StartData' or `/sfnts' was real --  */
 188     /* it could be in a comment or string.  We also get the arguments */
 189     /* of `StartData' to find out whether the data is represented in  */
 190     /* binary or hex format.                                          */
 191 
 192     arg1 = parser->root.cursor;
 193     cid_parser_skip_PS_token( parser );
 194     cid_parser_skip_spaces  ( parser );
 195     arg2 = parser->root.cursor;
 196     cid_parser_skip_PS_token( parser );
 197     cid_parser_skip_spaces  ( parser );
 198 
 199     limit = parser->root.limit;
 200     cur   = parser->root.cursor;
 201 
 202     while ( cur <= limit - SFNTS_LEN )
 203     {
 204       if ( parser->root.error )
 205       {
 206         error = parser->root.error;
 207         goto Exit;
 208       }
 209 
 210       if ( cur[0] == 'S'                                           &&
 211            cur <= limit - STARTDATA_LEN                            &&
 212            ft_strncmp( (char*)cur, STARTDATA, STARTDATA_LEN ) == 0 )
 213       {
 214         if ( ft_strncmp( (char*)arg1, "(Hex)", 5 ) == 0 )
 215         {
 216           FT_Long  tmp = ft_strtol( (const char *)arg2, NULL, 10 );
 217 
 218 
 219           if ( tmp < 0 )
 220           {
 221             FT_ERROR(( "cid_parser_new: invalid length of hex data\n" ));
 222             error = FT_THROW( Invalid_File_Format );
 223           }
 224           else
 225             parser->binary_length = (FT_ULong)tmp;
 226         }
 227 
 228         goto Exit;
 229       }
 230       else if ( cur[1] == 's'                                   &&
 231                 ft_strncmp( (char*)cur, SFNTS, SFNTS_LEN ) == 0 )
 232       {
 233         FT_TRACE2(( "cid_parser_new: cannot handle Type 11 fonts\n" ));
 234         error = FT_THROW( Unknown_File_Format );
 235         goto Exit;
 236       }
 237 
 238       cid_parser_skip_PS_token( parser );
 239       cid_parser_skip_spaces  ( parser );
 240       arg1 = arg2;
 241       arg2 = cur;
 242       cur  = parser->root.cursor;
 243     }
 244 
 245     /* we haven't found the correct `StartData'; go back and continue */
 246     /* searching                                                      */
 247     FT_FRAME_RELEASE( parser->postscript );
 248     if ( !FT_STREAM_SEEK( offset ) )
 249       goto Again;
 250 
 251   Exit:
 252     return error;
 253   }
 254 
 255 
 256 #undef STARTDATA
 257 #undef STARTDATA_LEN
 258 #undef SFNTS
 259 #undef SFNTS_LEN
 260 
 261 
 262   FT_LOCAL_DEF( void )
 263   cid_parser_done( CID_Parser*  parser )
 264   {
 265     /* always free the private dictionary */
 266     if ( parser->postscript )
 267     {
 268       FT_Stream  stream = parser->stream;
 269 
 270 
 271       FT_FRAME_RELEASE( parser->postscript );
 272     }
 273     parser->root.funcs.done( &parser->root );
 274   }
 275 
 276 
 277 /* END */