1/***************************************************************************/
2/*                                                                         */
3/*  ttmtx.c                                                                */
4/*                                                                         */
5/*    Load the metrics tables common to TTF and OTF fonts (body).          */
6/*                                                                         */
7/*  Copyright 2006, 2007, 2008, 2009 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 FT_INTERNAL_DEBUG_H
21#include FT_INTERNAL_STREAM_H
22#include FT_TRUETYPE_TAGS_H
23#include "ttmtx.h"
24
25#include "sferrors.h"
26
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  trace_ttmtx
36
37
38  /*
39   *  Unfortunately, we can't enable our memory optimizations if
40   *  FT_CONFIG_OPTION_OLD_INTERNALS is defined.  This is because at least
41   *  one rogue client (libXfont in the X.Org XServer) is directly accessing
42   *  the metrics.
43   */
44
45  /*************************************************************************/
46  /*                                                                       */
47  /* <Function>                                                            */
48  /*    tt_face_load_hmtx                                                  */
49  /*                                                                       */
50  /* <Description>                                                         */
51  /*    Load the `hmtx' or `vmtx' table into a face object.                */
52  /*                                                                       */
53  /* <Input>                                                               */
54  /*    face     :: A handle to the target face object.                    */
55  /*                                                                       */
56  /*    stream   :: The input stream.                                      */
57  /*                                                                       */
58  /*    vertical :: A boolean flag.  If set, load `vmtx'.                  */
59  /*                                                                       */
60  /* <Return>                                                              */
61  /*    FreeType error code.  0 means success.                             */
62  /*                                                                       */
63#ifndef FT_CONFIG_OPTION_OLD_INTERNALS
64
65  FT_LOCAL_DEF( FT_Error )
66  tt_face_load_hmtx( TT_Face    face,
67                     FT_Stream  stream,
68                     FT_Bool    vertical )
69  {
70    FT_Error   error;
71    FT_ULong   tag, table_size;
72    FT_ULong*  ptable_offset;
73    FT_ULong*  ptable_size;
74
75
76    if ( vertical )
77    {
78      tag           = TTAG_vmtx;
79      ptable_offset = &face->vert_metrics_offset;
80      ptable_size   = &face->vert_metrics_size;
81    }
82    else
83    {
84      tag           = TTAG_hmtx;
85      ptable_offset = &face->horz_metrics_offset;
86      ptable_size   = &face->horz_metrics_size;
87    }
88
89    error = face->goto_table( face, tag, stream, &table_size );
90    if ( error )
91      goto Fail;
92
93    *ptable_size   = table_size;
94    *ptable_offset = FT_STREAM_POS();
95
96  Fail:
97    return error;
98  }
99
100#else /* !FT_CONFIG_OPTION_OLD_INTERNALS */
101
102  FT_LOCAL_DEF( FT_Error )
103  tt_face_load_hmtx( TT_Face    face,
104                     FT_Stream  stream,
105                     FT_Bool    vertical )
106  {
107    FT_Error   error;
108    FT_Memory  memory = stream->memory;
109
110    FT_ULong   table_len;
111    FT_Long    num_shorts, num_longs, num_shorts_checked;
112
113    TT_LongMetrics*    longs;
114    TT_ShortMetrics**  shorts;
115    FT_Byte*           p;
116
117
118    if ( vertical )
119    {
120      void*   lm = &face->vertical.long_metrics;
121      void**  sm = &face->vertical.short_metrics;
122
123
124      error = face->goto_table( face, TTAG_vmtx, stream, &table_len );
125      if ( error )
126        goto Fail;
127
128      num_longs = face->vertical.number_Of_VMetrics;
129      if ( (FT_ULong)num_longs > table_len / 4 )
130        num_longs = (FT_Long)( table_len / 4 );
131
132      face->vertical.number_Of_VMetrics = 0;
133
134      longs  = (TT_LongMetrics*)lm;
135      shorts = (TT_ShortMetrics**)sm;
136    }
137    else
138    {
139      void*   lm = &face->horizontal.long_metrics;
140      void**  sm = &face->horizontal.short_metrics;
141
142
143      error = face->goto_table( face, TTAG_hmtx, stream, &table_len );
144      if ( error )
145        goto Fail;
146
147      num_longs = face->horizontal.number_Of_HMetrics;
148      if ( (FT_ULong)num_longs > table_len / 4 )
149        num_longs = (FT_Long)( table_len / 4 );
150
151      face->horizontal.number_Of_HMetrics = 0;
152
153      longs  = (TT_LongMetrics*)lm;
154      shorts = (TT_ShortMetrics**)sm;
155    }
156
157    /* never trust derived values */
158
159    num_shorts         = face->max_profile.numGlyphs - num_longs;
160    num_shorts_checked = ( table_len - num_longs * 4L ) / 2;
161
162    if ( num_shorts < 0 )
163    {
164      FT_TRACE0(( "tt_face_load_hmtx:"
165                  " %cmtx has more metrics than glyphs.\n",
166                  vertical ? "v" : "h" ));
167
168      /* Adobe simply ignores this problem.  So we shall do the same. */
169#if 0
170      error = vertical ? SFNT_Err_Invalid_Vert_Metrics
171                       : SFNT_Err_Invalid_Horiz_Metrics;
172      goto Exit;
173#else
174      num_shorts = 0;
175#endif
176    }
177
178    if ( FT_QNEW_ARRAY( *longs,  num_longs  ) ||
179         FT_QNEW_ARRAY( *shorts, num_shorts ) )
180      goto Fail;
181
182    if ( FT_FRAME_ENTER( table_len ) )
183      goto Fail;
184
185    p = stream->cursor;
186
187    {
188      TT_LongMetrics  cur   = *longs;
189      TT_LongMetrics  limit = cur + num_longs;
190
191
192      for ( ; cur < limit; cur++ )
193      {
194        cur->advance = FT_NEXT_USHORT( p );
195        cur->bearing = FT_NEXT_SHORT( p );
196      }
197    }
198
199    /* do we have an inconsistent number of metric values? */
200    {
201      TT_ShortMetrics*  cur   = *shorts;
202      TT_ShortMetrics*  limit = cur +
203                                FT_MIN( num_shorts, num_shorts_checked );
204
205
206      for ( ; cur < limit; cur++ )
207        *cur = FT_NEXT_SHORT( p );
208
209      /* We fill up the missing left side bearings with the     */
210      /* last valid value.  Since this will occur for buggy CJK */
211      /* fonts usually only, nothing serious will happen.       */
212      if ( num_shorts > num_shorts_checked && num_shorts_checked > 0 )
213      {
214        FT_Short  val = (*shorts)[num_shorts_checked - 1];
215
216
217        limit = *shorts + num_shorts;
218        for ( ; cur < limit; cur++ )
219          *cur = val;
220      }
221    }
222
223    FT_FRAME_EXIT();
224
225    if ( vertical )
226      face->vertical.number_Of_VMetrics = (FT_UShort)num_longs;
227    else
228      face->horizontal.number_Of_HMetrics = (FT_UShort)num_longs;
229
230  Fail:
231    return error;
232  }
233
234#endif /* !FT_CONFIG_OPTION_OLD_INTERNALS */
235
236
237  /*************************************************************************/
238  /*                                                                       */
239  /* <Function>                                                            */
240  /*    tt_face_load_hhea                                                  */
241  /*                                                                       */
242  /* <Description>                                                         */
243  /*    Load the `hhea' or 'vhea' table into a face object.                */
244  /*                                                                       */
245  /* <Input>                                                               */
246  /*    face     :: A handle to the target face object.                    */
247  /*                                                                       */
248  /*    stream   :: The input stream.                                      */
249  /*                                                                       */
250  /*    vertical :: A boolean flag.  If set, load `vhea'.                  */
251  /*                                                                       */
252  /* <Return>                                                              */
253  /*    FreeType error code.  0 means success.                             */
254  /*                                                                       */
255  FT_LOCAL_DEF( FT_Error )
256  tt_face_load_hhea( TT_Face    face,
257                     FT_Stream  stream,
258                     FT_Bool    vertical )
259  {
260    FT_Error        error;
261    TT_HoriHeader*  header;
262
263    const FT_Frame_Field  metrics_header_fields[] =
264    {
265#undef  FT_STRUCTURE
266#define FT_STRUCTURE  TT_HoriHeader
267
268      FT_FRAME_START( 36 ),
269        FT_FRAME_ULONG ( Version ),
270        FT_FRAME_SHORT ( Ascender ),
271        FT_FRAME_SHORT ( Descender ),
272        FT_FRAME_SHORT ( Line_Gap ),
273        FT_FRAME_USHORT( advance_Width_Max ),
274        FT_FRAME_SHORT ( min_Left_Side_Bearing ),
275        FT_FRAME_SHORT ( min_Right_Side_Bearing ),
276        FT_FRAME_SHORT ( xMax_Extent ),
277        FT_FRAME_SHORT ( caret_Slope_Rise ),
278        FT_FRAME_SHORT ( caret_Slope_Run ),
279        FT_FRAME_SHORT ( caret_Offset ),
280        FT_FRAME_SHORT ( Reserved[0] ),
281        FT_FRAME_SHORT ( Reserved[1] ),
282        FT_FRAME_SHORT ( Reserved[2] ),
283        FT_FRAME_SHORT ( Reserved[3] ),
284        FT_FRAME_SHORT ( metric_Data_Format ),
285        FT_FRAME_USHORT( number_Of_HMetrics ),
286      FT_FRAME_END
287    };
288
289
290    if ( vertical )
291    {
292      void  *v = &face->vertical;
293
294
295      error = face->goto_table( face, TTAG_vhea, stream, 0 );
296      if ( error )
297        goto Fail;
298
299      header = (TT_HoriHeader*)v;
300    }
301    else
302    {
303      error = face->goto_table( face, TTAG_hhea, stream, 0 );
304      if ( error )
305        goto Fail;
306
307      header = &face->horizontal;
308    }
309
310    if ( FT_STREAM_READ_FIELDS( metrics_header_fields, header ) )
311      goto Fail;
312
313    FT_TRACE3(( "Ascender:          %5d\n", header->Ascender ));
314    FT_TRACE3(( "Descender:         %5d\n", header->Descender ));
315    FT_TRACE3(( "number_Of_Metrics: %5u\n", header->number_Of_HMetrics ));
316
317    header->long_metrics  = NULL;
318    header->short_metrics = NULL;
319
320  Fail:
321    return error;
322  }
323
324
325  /*************************************************************************/
326  /*                                                                       */
327  /* <Function>                                                            */
328  /*    tt_face_get_metrics                                                */
329  /*                                                                       */
330  /* <Description>                                                         */
331  /*    Returns the horizontal or vertical metrics in font units for a     */
332  /*    given glyph.  The metrics are the left side bearing (resp. top     */
333  /*    side bearing) and advance width (resp. advance height).            */
334  /*                                                                       */
335  /* <Input>                                                               */
336  /*    header  :: A pointer to either the horizontal or vertical metrics  */
337  /*               structure.                                              */
338  /*                                                                       */
339  /*    idx     :: The glyph index.                                        */
340  /*                                                                       */
341  /* <Output>                                                              */
342  /*    bearing :: The bearing, either left side or top side.              */
343  /*                                                                       */
344  /*    advance :: The advance width resp. advance height.                 */
345  /*                                                                       */
346#ifndef FT_CONFIG_OPTION_OLD_INTERNALS
347
348  FT_LOCAL_DEF( FT_Error )
349  tt_face_get_metrics( TT_Face     face,
350                       FT_Bool     vertical,
351                       FT_UInt     gindex,
352                       FT_Short   *abearing,
353                       FT_UShort  *aadvance )
354  {
355    FT_Error        error;
356    FT_Stream       stream = face->root.stream;
357    TT_HoriHeader*  header;
358    FT_ULong        table_pos, table_size, table_end;
359    FT_UShort       k;
360
361
362    if ( vertical )
363    {
364      void*  v = &face->vertical;
365
366
367      header     = (TT_HoriHeader*)v;
368      table_pos  = face->vert_metrics_offset;
369      table_size = face->vert_metrics_size;
370    }
371    else
372    {
373      header     = &face->horizontal;
374      table_pos  = face->horz_metrics_offset;
375      table_size = face->horz_metrics_size;
376    }
377
378    table_end = table_pos + table_size;
379
380    k = header->number_Of_HMetrics;
381
382    if ( k > 0 )
383    {
384      if ( gindex < (FT_UInt)k )
385      {
386        table_pos += 4 * gindex;
387        if ( table_pos + 4 > table_end )
388          goto NoData;
389
390        if ( FT_STREAM_SEEK( table_pos ) ||
391             FT_READ_USHORT( *aadvance ) ||
392             FT_READ_SHORT( *abearing )  )
393          goto NoData;
394      }
395      else
396      {
397        table_pos += 4 * ( k - 1 );
398        if ( table_pos + 4 > table_end )
399          goto NoData;
400
401        if ( FT_STREAM_SEEK( table_pos ) ||
402             FT_READ_USHORT( *aadvance ) )
403          goto NoData;
404
405        table_pos += 4 + 2 * ( gindex - k );
406        if ( table_pos + 2 > table_end )
407          *abearing = 0;
408        else
409        {
410          if ( !FT_STREAM_SEEK( table_pos ) )
411            (void)FT_READ_SHORT( *abearing );
412        }
413      }
414    }
415    else
416    {
417    NoData:
418      *abearing = 0;
419      *aadvance = 0;
420    }
421
422    return SFNT_Err_Ok;
423  }
424
425#else /* !FT_CONFIG_OPTION_OLD_INTERNALS */
426
427  FT_LOCAL_DEF( FT_Error )
428  tt_face_get_metrics( TT_Face     face,
429                       FT_Bool     vertical,
430                       FT_UInt     gindex,
431                       FT_Short*   abearing,
432                       FT_UShort*  aadvance )
433  {
434    void*           v = &face->vertical;
435    void*           h = &face->horizontal;
436    TT_HoriHeader*  header = vertical ? (TT_HoriHeader*)v
437                                      : (TT_HoriHeader*)h;
438    TT_LongMetrics  longs_m;
439    FT_UShort       k = header->number_Of_HMetrics;
440
441
442    if ( k == 0                                         ||
443         !header->long_metrics                          ||
444         gindex >= (FT_UInt)face->max_profile.numGlyphs )
445    {
446      *abearing = *aadvance = 0;
447      return SFNT_Err_Ok;
448    }
449
450    if ( gindex < (FT_UInt)k )
451    {
452      longs_m   = (TT_LongMetrics)header->long_metrics + gindex;
453      *abearing = longs_m->bearing;
454      *aadvance = longs_m->advance;
455    }
456    else
457    {
458      *abearing = ((TT_ShortMetrics*)header->short_metrics)[gindex - k];
459      *aadvance = ((TT_LongMetrics)header->long_metrics)[k - 1].advance;
460    }
461
462    return SFNT_Err_Ok;
463  }
464
465#endif /* !FT_CONFIG_OPTION_OLD_INTERNALS */
466
467
468/* END */
469