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