1 /***************************************************************************/ 2 /* */ 3 /* ttmtx.c */ 4 /* */ 5 /* Load the metrics tables common to TTF and OTF fonts (body). */ 6 /* */ 7 /* Copyright 2006, 2007, 2008 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 ) tt_face_load_hmtx(TT_Face face,FT_Stream stream,FT_Bool vertical)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 ) tt_face_load_hmtx(TT_Face face,FT_Stream stream,FT_Bool vertical)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_ERROR(( "%cmtx has more metrics than glyphs.\n" )); 165 166 /* Adobe simply ignores this problem. So we shall do the same. */ 167 #if 0 168 error = vertical ? SFNT_Err_Invalid_Vert_Metrics 169 : SFNT_Err_Invalid_Horiz_Metrics; 170 goto Exit; 171 #else 172 num_shorts = 0; 173 #endif 174 } 175 176 if ( FT_QNEW_ARRAY( *longs, num_longs ) || 177 FT_QNEW_ARRAY( *shorts, num_shorts ) ) 178 goto Fail; 179 180 if ( FT_FRAME_ENTER( table_len ) ) 181 goto Fail; 182 183 p = stream->cursor; 184 185 { 186 TT_LongMetrics cur = *longs; 187 TT_LongMetrics limit = cur + num_longs; 188 189 190 for ( ; cur < limit; cur++ ) 191 { 192 cur->advance = FT_NEXT_USHORT( p ); 193 cur->bearing = FT_NEXT_SHORT( p ); 194 } 195 } 196 197 /* do we have an inconsistent number of metric values? */ 198 { 199 TT_ShortMetrics* cur = *shorts; 200 TT_ShortMetrics* limit = cur + 201 FT_MIN( num_shorts, num_shorts_checked ); 202 203 204 for ( ; cur < limit; cur++ ) 205 *cur = FT_NEXT_SHORT( p ); 206 207 /* We fill up the missing left side bearings with the */ 208 /* last valid value. Since this will occur for buggy CJK */ 209 /* fonts usually only, nothing serious will happen. */ 210 if ( num_shorts > num_shorts_checked && num_shorts_checked > 0 ) 211 { 212 FT_Short val = (*shorts)[num_shorts_checked - 1]; 213 214 215 limit = *shorts + num_shorts; 216 for ( ; cur < limit; cur++ ) 217 *cur = val; 218 } 219 } 220 221 FT_FRAME_EXIT(); 222 223 if ( vertical ) 224 face->vertical.number_Of_VMetrics = (FT_UShort)num_longs; 225 else 226 face->horizontal.number_Of_HMetrics = (FT_UShort)num_longs; 227 228 Fail: 229 return error; 230 } 231 232 #endif /* !FT_CONFIG_OPTION_OLD_INTERNALS */ 233 234 235 /*************************************************************************/ 236 /* */ 237 /* <Function> */ 238 /* tt_face_load_hhea */ 239 /* */ 240 /* <Description> */ 241 /* Load the `hhea' or 'vhea' table into a face object. */ 242 /* */ 243 /* <Input> */ 244 /* face :: A handle to the target face object. */ 245 /* */ 246 /* stream :: The input stream. */ 247 /* */ 248 /* vertical :: A boolean flag. If set, load `vhea'. */ 249 /* */ 250 /* <Return> */ 251 /* FreeType error code. 0 means success. */ 252 /* */ 253 FT_LOCAL_DEF( FT_Error ) tt_face_load_hhea(TT_Face face,FT_Stream stream,FT_Bool vertical)254 tt_face_load_hhea( TT_Face face, 255 FT_Stream stream, 256 FT_Bool vertical ) 257 { 258 FT_Error error; 259 TT_HoriHeader* header; 260 261 const FT_Frame_Field metrics_header_fields[] = 262 { 263 #undef FT_STRUCTURE 264 #define FT_STRUCTURE TT_HoriHeader 265 266 FT_FRAME_START( 36 ), 267 FT_FRAME_ULONG ( Version ), 268 FT_FRAME_SHORT ( Ascender ), 269 FT_FRAME_SHORT ( Descender ), 270 FT_FRAME_SHORT ( Line_Gap ), 271 FT_FRAME_USHORT( advance_Width_Max ), 272 FT_FRAME_SHORT ( min_Left_Side_Bearing ), 273 FT_FRAME_SHORT ( min_Right_Side_Bearing ), 274 FT_FRAME_SHORT ( xMax_Extent ), 275 FT_FRAME_SHORT ( caret_Slope_Rise ), 276 FT_FRAME_SHORT ( caret_Slope_Run ), 277 FT_FRAME_SHORT ( caret_Offset ), 278 FT_FRAME_SHORT ( Reserved[0] ), 279 FT_FRAME_SHORT ( Reserved[1] ), 280 FT_FRAME_SHORT ( Reserved[2] ), 281 FT_FRAME_SHORT ( Reserved[3] ), 282 FT_FRAME_SHORT ( metric_Data_Format ), 283 FT_FRAME_USHORT( number_Of_HMetrics ), 284 FT_FRAME_END 285 }; 286 287 288 if ( vertical ) 289 { 290 void *v = &face->vertical; 291 292 293 error = face->goto_table( face, TTAG_vhea, stream, 0 ); 294 if ( error ) 295 goto Fail; 296 297 header = (TT_HoriHeader*)v; 298 } 299 else 300 { 301 error = face->goto_table( face, TTAG_hhea, stream, 0 ); 302 if ( error ) 303 goto Fail; 304 305 header = &face->horizontal; 306 } 307 308 if ( FT_STREAM_READ_FIELDS( metrics_header_fields, header ) ) 309 goto Fail; 310 311 FT_TRACE3(( "Ascender: %5d\n", header->Ascender )); 312 FT_TRACE3(( "Descender: %5d\n", header->Descender )); 313 FT_TRACE3(( "number_Of_Metrics: %5u\n", header->number_Of_HMetrics )); 314 315 header->long_metrics = NULL; 316 header->short_metrics = NULL; 317 318 Fail: 319 return error; 320 } 321 322 323 /*************************************************************************/ 324 /* */ 325 /* <Function> */ 326 /* tt_face_get_metrics */ 327 /* */ 328 /* <Description> */ 329 /* Returns the horizontal or vertical metrics in font units for a */ 330 /* given glyph. The metrics are the left side bearing (resp. top */ 331 /* side bearing) and advance width (resp. advance height). */ 332 /* */ 333 /* <Input> */ 334 /* header :: A pointer to either the horizontal or vertical metrics */ 335 /* structure. */ 336 /* */ 337 /* idx :: The glyph index. */ 338 /* */ 339 /* <Output> */ 340 /* bearing :: The bearing, either left side or top side. */ 341 /* */ 342 /* advance :: The advance width resp. advance height. */ 343 /* */ 344 #ifndef FT_CONFIG_OPTION_OLD_INTERNALS 345 346 FT_LOCAL_DEF( FT_Error ) tt_face_get_metrics(TT_Face face,FT_Bool vertical,FT_UInt gindex,FT_Short * abearing,FT_UShort * aadvance)347 tt_face_get_metrics( TT_Face face, 348 FT_Bool vertical, 349 FT_UInt gindex, 350 FT_Short *abearing, 351 FT_UShort *aadvance ) 352 { 353 FT_Error error; 354 FT_Stream stream = face->root.stream; 355 TT_HoriHeader* header; 356 FT_ULong table_pos, table_size, table_end; 357 FT_UShort k; 358 359 360 if ( vertical ) 361 { 362 void* v = &face->vertical; 363 364 365 header = (TT_HoriHeader*)v; 366 table_pos = face->vert_metrics_offset; 367 table_size = face->vert_metrics_size; 368 } 369 else 370 { 371 header = &face->horizontal; 372 table_pos = face->horz_metrics_offset; 373 table_size = face->horz_metrics_size; 374 } 375 376 table_end = table_pos + table_size; 377 378 k = header->number_Of_HMetrics; 379 380 if ( k > 0 ) 381 { 382 if ( gindex < (FT_UInt)k ) 383 { 384 table_pos += 4 * gindex; 385 if ( table_pos + 4 > table_end ) 386 goto NoData; 387 388 if ( FT_STREAM_SEEK( table_pos ) || 389 FT_READ_USHORT( *aadvance ) || 390 FT_READ_SHORT( *abearing ) ) 391 goto NoData; 392 } 393 else 394 { 395 table_pos += 4 * ( k - 1 ); 396 if ( table_pos + 4 > table_end ) 397 goto NoData; 398 399 if ( FT_STREAM_SEEK( table_pos ) || 400 FT_READ_USHORT( *aadvance ) ) 401 goto NoData; 402 403 table_pos += 4 + 2 * ( gindex - k ); 404 if ( table_pos + 2 > table_end ) 405 *abearing = 0; 406 else 407 { 408 if ( !FT_STREAM_SEEK( table_pos ) ) 409 (void)FT_READ_SHORT( *abearing ); 410 } 411 } 412 } 413 else 414 { 415 NoData: 416 *abearing = 0; 417 *aadvance = 0; 418 } 419 420 return SFNT_Err_Ok; 421 } 422 423 #else /* !FT_CONFIG_OPTION_OLD_INTERNALS */ 424 425 FT_LOCAL_DEF( FT_Error ) tt_face_get_metrics(TT_Face face,FT_Bool vertical,FT_UInt gindex,FT_Short * abearing,FT_UShort * aadvance)426 tt_face_get_metrics( TT_Face face, 427 FT_Bool vertical, 428 FT_UInt gindex, 429 FT_Short* abearing, 430 FT_UShort* aadvance ) 431 { 432 void* v = &face->vertical; 433 void* h = &face->horizontal; 434 TT_HoriHeader* header = vertical ? (TT_HoriHeader*)v 435 : (TT_HoriHeader*)h; 436 TT_LongMetrics longs_m; 437 FT_UShort k = header->number_Of_HMetrics; 438 439 440 if ( k == 0 || 441 !header->long_metrics || 442 gindex >= (FT_UInt)face->max_profile.numGlyphs ) 443 { 444 *abearing = *aadvance = 0; 445 return SFNT_Err_Ok; 446 } 447 448 if ( gindex < (FT_UInt)k ) 449 { 450 longs_m = (TT_LongMetrics)header->long_metrics + gindex; 451 *abearing = longs_m->bearing; 452 *aadvance = longs_m->advance; 453 } 454 else 455 { 456 *abearing = ((TT_ShortMetrics*)header->short_metrics)[gindex - k]; 457 *aadvance = ((TT_LongMetrics)header->long_metrics)[k - 1].advance; 458 } 459 460 return SFNT_Err_Ok; 461 } 462 463 #endif /* !FT_CONFIG_OPTION_OLD_INTERNALS */ 464 465 466 /* END */ 467