Home | History | Annotate | Download | only in type1
      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 */
    416