1 /***************************************************************************/
   2 /*                                                                         */
   3 /*  t1afm.c                                                                */
   4 /*                                                                         */
   5 /*    AFM support for Type 1 fonts (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 "t1afm.h"
  21 #include FT_INTERNAL_DEBUG_H
  22 #include FT_INTERNAL_STREAM_H
  23 #include FT_INTERNAL_POSTSCRIPT_AUX_H
  24 #include "t1errors.h"
  25 
  26 
  27 #ifndef T1_CONFIG_OPTION_NO_AFM
  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_t1afm
  37 
  38 
  39   FT_LOCAL_DEF( void )
  40   T1_Done_Metrics( FT_Memory     memory,
  41                    AFM_FontInfo  fi )
  42   {
  43     FT_FREE( fi->KernPairs );
  44     fi->NumKernPair = 0;
  45 
  46     FT_FREE( fi->TrackKerns );
  47     fi->NumTrackKern = 0;
  48 
  49     FT_FREE( fi );
  50   }
  51 
  52 
  53   /* read a glyph name and return the equivalent glyph index */
  54   static FT_Int
  55   t1_get_index( const char*  name,
  56                 FT_Offset    len,
  57                 void*        user_data )
  58   {
  59     T1_Font  type1 = (T1_Font)user_data;
  60     FT_Int   n;
  61 
  62 
  63     /* PS string/name length must be < 16-bit */
  64     if ( len > 0xFFFFU )
  65       return 0;
  66 
  67     for ( n = 0; n < type1->num_glyphs; n++ )
  68     {
  69       char*  gname = (char*)type1->glyph_names[n];
  70 
  71 
  72       if ( gname && gname[0] == name[0]        &&
  73            ft_strlen( gname ) == len           &&
  74            ft_strncmp( gname, name, len ) == 0 )
  75         return n;
  76     }
  77 
  78     return 0;
  79   }
  80 
  81 
  82 #undef  KERN_INDEX
  83 #define KERN_INDEX( g1, g2 )  ( ( (FT_ULong)(g1) << 16 ) | (g2) )
  84 
  85 
  86   /* compare two kerning pairs */
  87   FT_CALLBACK_DEF( int )
  88   compare_kern_pairs( const void*  a,
  89                       const void*  b )
  90   {
  91     AFM_KernPair  pair1 = (AFM_KernPair)a;
  92     AFM_KernPair  pair2 = (AFM_KernPair)b;
  93 
  94     FT_ULong  index1 = KERN_INDEX( pair1->index1, pair1->index2 );
  95     FT_ULong  index2 = KERN_INDEX( pair2->index1, pair2->index2 );
  96 
  97 
  98     if ( index1 > index2 )
  99       return 1;
 100     else if ( index1 < index2 )
 101       return -1;
 102     else
 103       return 0;
 104   }
 105 
 106 
 107   /* parse a PFM file -- for now, only read the kerning pairs */
 108   static FT_Error
 109   T1_Read_PFM( FT_Face       t1_face,
 110                FT_Stream     stream,
 111                AFM_FontInfo  fi )
 112   {
 113     FT_Error      error  = FT_Err_Ok;
 114     FT_Memory     memory = stream->memory;
 115     FT_Byte*      start;
 116     FT_Byte*      limit;
 117     FT_Byte*      p;
 118     AFM_KernPair  kp;
 119     FT_Int        width_table_length;
 120     FT_CharMap    oldcharmap;
 121     FT_CharMap    charmap;
 122     FT_Int        n;
 123 
 124 
 125     start = (FT_Byte*)stream->cursor;
 126     limit = (FT_Byte*)stream->limit;
 127 
 128     /* Figure out how long the width table is.          */
 129     /* This info is a little-endian short at offset 99. */
 130     p = start + 99;
 131     if ( p + 2 > limit )
 132     {
 133       error = FT_THROW( Unknown_File_Format );
 134       goto Exit;
 135     }
 136     width_table_length = FT_PEEK_USHORT_LE( p );
 137 
 138     p += 18 + width_table_length;
 139     if ( p + 0x12 > limit || FT_PEEK_USHORT_LE( p ) < 0x12 )
 140       /* extension table is probably optional */
 141       goto Exit;
 142 
 143     /* Kerning offset is 14 bytes from start of extensions table. */
 144     p += 14;
 145     p = start + FT_PEEK_ULONG_LE( p );
 146 
 147     if ( p == start )
 148       /* zero offset means no table */
 149       goto Exit;
 150 
 151     if ( p + 2 > limit )
 152     {
 153       error = FT_THROW( Unknown_File_Format );
 154       goto Exit;
 155     }
 156 
 157     fi->NumKernPair = FT_PEEK_USHORT_LE( p );
 158     p += 2;
 159     if ( p + 4 * fi->NumKernPair > limit )
 160     {
 161       error = FT_THROW( Unknown_File_Format );
 162       goto Exit;
 163     }
 164 
 165     /* Actually, kerning pairs are simply optional! */
 166     if ( fi->NumKernPair == 0 )
 167       goto Exit;
 168 
 169     /* allocate the pairs */
 170     if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) )
 171       goto Exit;
 172 
 173     /* now, read each kern pair */
 174     kp    = fi->KernPairs;
 175     limit = p + 4 * fi->NumKernPair;
 176 
 177     /* PFM kerning data are stored by encoding rather than glyph index, */
 178     /* so find the PostScript charmap of this font and install it       */
 179     /* temporarily.  If we find no PostScript charmap, then just use    */
 180     /* the default and hope it is the right one.                        */
 181     oldcharmap = t1_face->charmap;
 182     charmap    = NULL;
 183 
 184     for ( n = 0; n < t1_face->num_charmaps; n++ )
 185     {
 186       charmap = t1_face->charmaps[n];
 187       /* check against PostScript pseudo platform */
 188       if ( charmap->platform_id == 7 )
 189       {
 190         error = FT_Set_Charmap( t1_face, charmap );
 191         if ( error )
 192           goto Exit;
 193         break;
 194       }
 195     }
 196 
 197     /* Kerning info is stored as:             */
 198     /*                                        */
 199     /*   encoding of first glyph (1 byte)     */
 200     /*   encoding of second glyph (1 byte)    */
 201     /*   offset (little-endian short)         */
 202     for ( ; p < limit; p += 4 )
 203     {
 204       kp->index1 = FT_Get_Char_Index( t1_face, p[0] );
 205       kp->index2 = FT_Get_Char_Index( t1_face, p[1] );
 206 
 207       kp->x = (FT_Int)FT_PEEK_SHORT_LE(p + 2);
 208       kp->y = 0;
 209 
 210       kp++;
 211     }
 212 
 213     if ( oldcharmap )
 214       error = FT_Set_Charmap( t1_face, oldcharmap );
 215     if ( error )
 216       goto Exit;
 217 
 218     /* now, sort the kern pairs according to their glyph indices */
 219     ft_qsort( fi->KernPairs, fi->NumKernPair, sizeof ( AFM_KernPairRec ),
 220               compare_kern_pairs );
 221 
 222   Exit:
 223     if ( error )
 224     {
 225       FT_FREE( fi->KernPairs );
 226       fi->NumKernPair = 0;
 227     }
 228 
 229     return error;
 230   }
 231 
 232 
 233   /* parse a metrics file -- either AFM or PFM depending on what */
 234   /* it turns out to be                                          */
 235   FT_LOCAL_DEF( FT_Error )
 236   T1_Read_Metrics( FT_Face    t1_face,
 237                    FT_Stream  stream )
 238   {
 239     PSAux_Service  psaux;
 240     FT_Memory      memory  = stream->memory;
 241     AFM_ParserRec  parser;
 242     AFM_FontInfo   fi      = NULL;
 243     FT_Error       error   = FT_ERR( Unknown_File_Format );
 244     T1_Face        face    = (T1_Face)t1_face;
 245     T1_Font        t1_font = &face->type1;
 246 
 247 
 248     if ( face->afm_data )
 249     {
 250       FT_TRACE1(( "T1_Read_Metrics:"
 251                   " Freeing previously attached metrics data.\n" ));
 252       T1_Done_Metrics( memory, (AFM_FontInfo)face->afm_data );
 253 
 254       face->afm_data = NULL;
 255     }
 256 
 257     if ( FT_NEW( fi )                   ||
 258          FT_FRAME_ENTER( stream->size ) )
 259       goto Exit;
 260 
 261     fi->FontBBox  = t1_font->font_bbox;
 262     fi->Ascender  = t1_font->font_bbox.yMax;
 263     fi->Descender = t1_font->font_bbox.yMin;
 264 
 265     psaux = (PSAux_Service)face->psaux;
 266     if ( psaux->afm_parser_funcs )
 267     {
 268       error = psaux->afm_parser_funcs->init( &parser,
 269                                              stream->memory,
 270                                              stream->cursor,
 271                                              stream->limit );
 272 
 273       if ( !error )
 274       {
 275         parser.FontInfo  = fi;
 276         parser.get_index = t1_get_index;
 277         parser.user_data = t1_font;
 278 
 279         error = psaux->afm_parser_funcs->parse( &parser );
 280         psaux->afm_parser_funcs->done( &parser );
 281       }
 282     }
 283 
 284     if ( FT_ERR_EQ( error, Unknown_File_Format ) )
 285     {
 286       FT_Byte*  start = stream->cursor;
 287 
 288 
 289       /* MS Windows allows versions up to 0x3FF without complaining */
 290       if ( stream->size > 6                              &&
 291            start[1] < 4                                  &&
 292            FT_PEEK_ULONG_LE( start + 2 ) == stream->size )
 293         error = T1_Read_PFM( t1_face, stream, fi );
 294     }
 295 
 296     if ( !error )
 297     {
 298       t1_font->font_bbox = fi->FontBBox;
 299 
 300       t1_face->bbox.xMin =   fi->FontBBox.xMin            >> 16;
 301       t1_face->bbox.yMin =   fi->FontBBox.yMin            >> 16;
 302       /* no `U' suffix here to 0xFFFF! */
 303       t1_face->bbox.xMax = ( fi->FontBBox.xMax + 0xFFFF ) >> 16;
 304       t1_face->bbox.yMax = ( fi->FontBBox.yMax + 0xFFFF ) >> 16;
 305 
 306       /* no `U' suffix here to 0x8000! */
 307       t1_face->ascender  = (FT_Short)( ( fi->Ascender  + 0x8000 ) >> 16 );
 308       t1_face->descender = (FT_Short)( ( fi->Descender + 0x8000 ) >> 16 );
 309 
 310       if ( fi->NumKernPair )
 311       {
 312         t1_face->face_flags |= FT_FACE_FLAG_KERNING;
 313         face->afm_data       = fi;
 314         fi                   = NULL;
 315       }
 316     }
 317 
 318     FT_FRAME_EXIT();
 319 
 320   Exit:
 321     if ( fi )
 322       T1_Done_Metrics( memory, fi );
 323 
 324     return error;
 325   }
 326 
 327 
 328   /* find the kerning for a given glyph pair */
 329   FT_LOCAL_DEF( void )
 330   T1_Get_Kerning( AFM_FontInfo  fi,
 331                   FT_UInt       glyph1,
 332                   FT_UInt       glyph2,
 333                   FT_Vector*    kerning )
 334   {
 335     AFM_KernPair  min, mid, max;
 336     FT_ULong      idx = KERN_INDEX( glyph1, glyph2 );
 337 
 338 
 339     /* simple binary search */
 340     min = fi->KernPairs;
 341     max = min + fi->NumKernPair - 1;
 342 
 343     while ( min <= max )
 344     {
 345       FT_ULong  midi;
 346 
 347 
 348       mid  = min + ( max - min ) / 2;
 349       midi = KERN_INDEX( mid->index1, mid->index2 );
 350 
 351       if ( midi == idx )
 352       {
 353         kerning->x = mid->x;
 354         kerning->y = mid->y;
 355 
 356         return;
 357       }
 358 
 359       if ( midi < idx )
 360         min = mid + 1;
 361       else
 362         max = mid - 1;
 363     }
 364 
 365     kerning->x = 0;
 366     kerning->y = 0;
 367   }
 368 
 369 
 370   FT_LOCAL_DEF( FT_Error )
 371   T1_Get_Track_Kerning( FT_Face    face,
 372                         FT_Fixed   ptsize,
 373                         FT_Int     degree,
 374                         FT_Fixed*  kerning )
 375   {
 376     AFM_FontInfo  fi = (AFM_FontInfo)( (T1_Face)face )->afm_data;
 377     FT_UInt       i;
 378 
 379 
 380     if ( !fi )
 381       return FT_THROW( Invalid_Argument );
 382 
 383     for ( i = 0; i < fi->NumTrackKern; i++ )
 384     {
 385       AFM_TrackKern  tk = fi->TrackKerns + i;
 386 
 387 
 388       if ( tk->degree != degree )
 389         continue;
 390 
 391       if ( ptsize < tk->min_ptsize )
 392         *kerning = tk->min_kern;
 393       else if ( ptsize > tk->max_ptsize )
 394         *kerning = tk->max_kern;
 395       else
 396       {
 397         *kerning = FT_MulDiv( ptsize - tk->min_ptsize,
 398                               tk->max_kern - tk->min_kern,
 399                               tk->max_ptsize - tk->min_ptsize ) +
 400                    tk->min_kern;
 401       }
 402     }
 403 
 404     return FT_Err_Ok;
 405   }
 406 
 407 #else /* T1_CONFIG_OPTION_NO_AFM */
 408 
 409   /* ANSI C doesn't like empty source files */
 410   typedef int  _t1_afm_dummy;
 411 
 412 #endif /* T1_CONFIG_OPTION_NO_AFM */
 413 
 414 
 415 /* END */