tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

t1afm.c (10667B)


      1 /****************************************************************************
      2 *
      3 * t1afm.c
      4 *
      5 *   AFM support for Type 1 fonts (body).
      6 *
      7 * Copyright (C) 1996-2025 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 */