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