/***************************************************************************/ | |
/* */ | |
/* ttmtx.c */ | |
/* */ | |
/* Load the metrics tables common to TTF and OTF fonts (body). */ | |
/* */ | |
/* Copyright 2006, 2007, 2008 by */ | |
/* David Turner, Robert Wilhelm, and Werner Lemberg. */ | |
/* */ | |
/* This file is part of the FreeType project, and may only be used, */ | |
/* modified, and distributed under the terms of the FreeType project */ | |
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ | |
/* this file you indicate that you have read the license and */ | |
/* understand and accept it fully. */ | |
/* */ | |
/***************************************************************************/ | |
#include <ft2build.h> | |
#include FT_INTERNAL_DEBUG_H | |
#include FT_INTERNAL_STREAM_H | |
#include FT_TRUETYPE_TAGS_H | |
#include "ttmtx.h" | |
#include "sferrors.h" | |
/*************************************************************************/ | |
/* */ | |
/* The macro FT_COMPONENT is used in trace mode. It is an implicit */ | |
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ | |
/* messages during execution. */ | |
/* */ | |
#undef FT_COMPONENT | |
#define FT_COMPONENT trace_ttmtx | |
/* | |
* Unfortunately, we can't enable our memory optimizations if | |
* FT_CONFIG_OPTION_OLD_INTERNALS is defined. This is because at least | |
* one rogue client (libXfont in the X.Org XServer) is directly accessing | |
* the metrics. | |
*/ | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* tt_face_load_hmtx */ | |
/* */ | |
/* <Description> */ | |
/* Load the `hmtx' or `vmtx' table into a face object. */ | |
/* */ | |
/* <Input> */ | |
/* face :: A handle to the target face object. */ | |
/* */ | |
/* stream :: The input stream. */ | |
/* */ | |
/* vertical :: A boolean flag. If set, load `vmtx'. */ | |
/* */ | |
/* <Return> */ | |
/* FreeType error code. 0 means success. */ | |
/* */ | |
#ifndef FT_CONFIG_OPTION_OLD_INTERNALS | |
FT_LOCAL_DEF( FT_Error ) | |
tt_face_load_hmtx( TT_Face face, | |
FT_Stream stream, | |
FT_Bool vertical ) | |
{ | |
FT_Error error; | |
FT_ULong tag, table_size; | |
FT_ULong* ptable_offset; | |
FT_ULong* ptable_size; | |
if ( vertical ) | |
{ | |
tag = TTAG_vmtx; | |
ptable_offset = &face->vert_metrics_offset; | |
ptable_size = &face->vert_metrics_size; | |
} | |
else | |
{ | |
tag = TTAG_hmtx; | |
ptable_offset = &face->horz_metrics_offset; | |
ptable_size = &face->horz_metrics_size; | |
} | |
error = face->goto_table( face, tag, stream, &table_size ); | |
if ( error ) | |
goto Fail; | |
*ptable_size = table_size; | |
*ptable_offset = FT_STREAM_POS(); | |
Fail: | |
return error; | |
} | |
#else /* !FT_CONFIG_OPTION_OLD_INTERNALS */ | |
FT_LOCAL_DEF( FT_Error ) | |
tt_face_load_hmtx( TT_Face face, | |
FT_Stream stream, | |
FT_Bool vertical ) | |
{ | |
FT_Error error; | |
FT_Memory memory = stream->memory; | |
FT_ULong table_len; | |
FT_Long num_shorts, num_longs, num_shorts_checked; | |
TT_LongMetrics* longs; | |
TT_ShortMetrics** shorts; | |
FT_Byte* p; | |
if ( vertical ) | |
{ | |
void* lm = &face->vertical.long_metrics; | |
void** sm = &face->vertical.short_metrics; | |
error = face->goto_table( face, TTAG_vmtx, stream, &table_len ); | |
if ( error ) | |
goto Fail; | |
num_longs = face->vertical.number_Of_VMetrics; | |
if ( (FT_ULong)num_longs > table_len / 4 ) | |
num_longs = (FT_Long)( table_len / 4 ); | |
face->vertical.number_Of_VMetrics = 0; | |
longs = (TT_LongMetrics*)lm; | |
shorts = (TT_ShortMetrics**)sm; | |
} | |
else | |
{ | |
void* lm = &face->horizontal.long_metrics; | |
void** sm = &face->horizontal.short_metrics; | |
error = face->goto_table( face, TTAG_hmtx, stream, &table_len ); | |
if ( error ) | |
goto Fail; | |
num_longs = face->horizontal.number_Of_HMetrics; | |
if ( (FT_ULong)num_longs > table_len / 4 ) | |
num_longs = (FT_Long)( table_len / 4 ); | |
face->horizontal.number_Of_HMetrics = 0; | |
longs = (TT_LongMetrics*)lm; | |
shorts = (TT_ShortMetrics**)sm; | |
} | |
/* never trust derived values */ | |
num_shorts = face->max_profile.numGlyphs - num_longs; | |
num_shorts_checked = ( table_len - num_longs * 4L ) / 2; | |
if ( num_shorts < 0 ) | |
{ | |
FT_ERROR(( "%cmtx has more metrics than glyphs.\n" )); | |
/* Adobe simply ignores this problem. So we shall do the same. */ | |
#if 0 | |
error = vertical ? SFNT_Err_Invalid_Vert_Metrics | |
: SFNT_Err_Invalid_Horiz_Metrics; | |
goto Exit; | |
#else | |
num_shorts = 0; | |
#endif | |
} | |
if ( FT_QNEW_ARRAY( *longs, num_longs ) || | |
FT_QNEW_ARRAY( *shorts, num_shorts ) ) | |
goto Fail; | |
if ( FT_FRAME_ENTER( table_len ) ) | |
goto Fail; | |
p = stream->cursor; | |
{ | |
TT_LongMetrics cur = *longs; | |
TT_LongMetrics limit = cur + num_longs; | |
for ( ; cur < limit; cur++ ) | |
{ | |
cur->advance = FT_NEXT_USHORT( p ); | |
cur->bearing = FT_NEXT_SHORT( p ); | |
} | |
} | |
/* do we have an inconsistent number of metric values? */ | |
{ | |
TT_ShortMetrics* cur = *shorts; | |
TT_ShortMetrics* limit = cur + | |
FT_MIN( num_shorts, num_shorts_checked ); | |
for ( ; cur < limit; cur++ ) | |
*cur = FT_NEXT_SHORT( p ); | |
/* We fill up the missing left side bearings with the */ | |
/* last valid value. Since this will occur for buggy CJK */ | |
/* fonts usually only, nothing serious will happen. */ | |
if ( num_shorts > num_shorts_checked && num_shorts_checked > 0 ) | |
{ | |
FT_Short val = (*shorts)[num_shorts_checked - 1]; | |
limit = *shorts + num_shorts; | |
for ( ; cur < limit; cur++ ) | |
*cur = val; | |
} | |
} | |
FT_FRAME_EXIT(); | |
if ( vertical ) | |
face->vertical.number_Of_VMetrics = (FT_UShort)num_longs; | |
else | |
face->horizontal.number_Of_HMetrics = (FT_UShort)num_longs; | |
Fail: | |
return error; | |
} | |
#endif /* !FT_CONFIG_OPTION_OLD_INTERNALS */ | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* tt_face_load_hhea */ | |
/* */ | |
/* <Description> */ | |
/* Load the `hhea' or 'vhea' table into a face object. */ | |
/* */ | |
/* <Input> */ | |
/* face :: A handle to the target face object. */ | |
/* */ | |
/* stream :: The input stream. */ | |
/* */ | |
/* vertical :: A boolean flag. If set, load `vhea'. */ | |
/* */ | |
/* <Return> */ | |
/* FreeType error code. 0 means success. */ | |
/* */ | |
FT_LOCAL_DEF( FT_Error ) | |
tt_face_load_hhea( TT_Face face, | |
FT_Stream stream, | |
FT_Bool vertical ) | |
{ | |
FT_Error error; | |
TT_HoriHeader* header; | |
const FT_Frame_Field metrics_header_fields[] = | |
{ | |
#undef FT_STRUCTURE | |
#define FT_STRUCTURE TT_HoriHeader | |
FT_FRAME_START( 36 ), | |
FT_FRAME_ULONG ( Version ), | |
FT_FRAME_SHORT ( Ascender ), | |
FT_FRAME_SHORT ( Descender ), | |
FT_FRAME_SHORT ( Line_Gap ), | |
FT_FRAME_USHORT( advance_Width_Max ), | |
FT_FRAME_SHORT ( min_Left_Side_Bearing ), | |
FT_FRAME_SHORT ( min_Right_Side_Bearing ), | |
FT_FRAME_SHORT ( xMax_Extent ), | |
FT_FRAME_SHORT ( caret_Slope_Rise ), | |
FT_FRAME_SHORT ( caret_Slope_Run ), | |
FT_FRAME_SHORT ( caret_Offset ), | |
FT_FRAME_SHORT ( Reserved[0] ), | |
FT_FRAME_SHORT ( Reserved[1] ), | |
FT_FRAME_SHORT ( Reserved[2] ), | |
FT_FRAME_SHORT ( Reserved[3] ), | |
FT_FRAME_SHORT ( metric_Data_Format ), | |
FT_FRAME_USHORT( number_Of_HMetrics ), | |
FT_FRAME_END | |
}; | |
if ( vertical ) | |
{ | |
void *v = &face->vertical; | |
error = face->goto_table( face, TTAG_vhea, stream, 0 ); | |
if ( error ) | |
goto Fail; | |
header = (TT_HoriHeader*)v; | |
} | |
else | |
{ | |
error = face->goto_table( face, TTAG_hhea, stream, 0 ); | |
if ( error ) | |
goto Fail; | |
header = &face->horizontal; | |
} | |
if ( FT_STREAM_READ_FIELDS( metrics_header_fields, header ) ) | |
goto Fail; | |
FT_TRACE3(( "Ascender: %5d\n", header->Ascender )); | |
FT_TRACE3(( "Descender: %5d\n", header->Descender )); | |
FT_TRACE3(( "number_Of_Metrics: %5u\n", header->number_Of_HMetrics )); | |
header->long_metrics = NULL; | |
header->short_metrics = NULL; | |
Fail: | |
return error; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* tt_face_get_metrics */ | |
/* */ | |
/* <Description> */ | |
/* Returns the horizontal or vertical metrics in font units for a */ | |
/* given glyph. The metrics are the left side bearing (resp. top */ | |
/* side bearing) and advance width (resp. advance height). */ | |
/* */ | |
/* <Input> */ | |
/* header :: A pointer to either the horizontal or vertical metrics */ | |
/* structure. */ | |
/* */ | |
/* idx :: The glyph index. */ | |
/* */ | |
/* <Output> */ | |
/* bearing :: The bearing, either left side or top side. */ | |
/* */ | |
/* advance :: The advance width resp. advance height. */ | |
/* */ | |
#ifndef FT_CONFIG_OPTION_OLD_INTERNALS | |
FT_LOCAL_DEF( FT_Error ) | |
tt_face_get_metrics( TT_Face face, | |
FT_Bool vertical, | |
FT_UInt gindex, | |
FT_Short *abearing, | |
FT_UShort *aadvance ) | |
{ | |
FT_Error error; | |
FT_Stream stream = face->root.stream; | |
TT_HoriHeader* header; | |
FT_ULong table_pos, table_size, table_end; | |
FT_UShort k; | |
if ( vertical ) | |
{ | |
void* v = &face->vertical; | |
header = (TT_HoriHeader*)v; | |
table_pos = face->vert_metrics_offset; | |
table_size = face->vert_metrics_size; | |
} | |
else | |
{ | |
header = &face->horizontal; | |
table_pos = face->horz_metrics_offset; | |
table_size = face->horz_metrics_size; | |
} | |
table_end = table_pos + table_size; | |
k = header->number_Of_HMetrics; | |
if ( k > 0 ) | |
{ | |
if ( gindex < (FT_UInt)k ) | |
{ | |
table_pos += 4 * gindex; | |
if ( table_pos + 4 > table_end ) | |
goto NoData; | |
if ( FT_STREAM_SEEK( table_pos ) || | |
FT_READ_USHORT( *aadvance ) || | |
FT_READ_SHORT( *abearing ) ) | |
goto NoData; | |
} | |
else | |
{ | |
table_pos += 4 * ( k - 1 ); | |
if ( table_pos + 4 > table_end ) | |
goto NoData; | |
if ( FT_STREAM_SEEK( table_pos ) || | |
FT_READ_USHORT( *aadvance ) ) | |
goto NoData; | |
table_pos += 4 + 2 * ( gindex - k ); | |
if ( table_pos + 2 > table_end ) | |
*abearing = 0; | |
else | |
{ | |
if ( !FT_STREAM_SEEK( table_pos ) ) | |
(void)FT_READ_SHORT( *abearing ); | |
} | |
} | |
} | |
else | |
{ | |
NoData: | |
*abearing = 0; | |
*aadvance = 0; | |
} | |
return SFNT_Err_Ok; | |
} | |
#else /* !FT_CONFIG_OPTION_OLD_INTERNALS */ | |
FT_LOCAL_DEF( FT_Error ) | |
tt_face_get_metrics( TT_Face face, | |
FT_Bool vertical, | |
FT_UInt gindex, | |
FT_Short* abearing, | |
FT_UShort* aadvance ) | |
{ | |
void* v = &face->vertical; | |
void* h = &face->horizontal; | |
TT_HoriHeader* header = vertical ? (TT_HoriHeader*)v | |
: (TT_HoriHeader*)h; | |
TT_LongMetrics longs_m; | |
FT_UShort k = header->number_Of_HMetrics; | |
if ( k == 0 || | |
!header->long_metrics || | |
gindex >= (FT_UInt)face->max_profile.numGlyphs ) | |
{ | |
*abearing = *aadvance = 0; | |
return SFNT_Err_Ok; | |
} | |
if ( gindex < (FT_UInt)k ) | |
{ | |
longs_m = (TT_LongMetrics)header->long_metrics + gindex; | |
*abearing = longs_m->bearing; | |
*aadvance = longs_m->advance; | |
} | |
else | |
{ | |
*abearing = ((TT_ShortMetrics*)header->short_metrics)[gindex - k]; | |
*aadvance = ((TT_LongMetrics)header->long_metrics)[k - 1].advance; | |
} | |
return SFNT_Err_Ok; | |
} | |
#endif /* !FT_CONFIG_OPTION_OLD_INTERNALS */ | |
/* END */ |