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