1 /**************************************************************************** 2 * 3 * pfrgload.c 4 * 5 * FreeType PFR glyph loader (body). 6 * 7 * Copyright (C) 2002-2023 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 "pfrgload.h" 20 #include "pfrsbit.h" 21 #include "pfrload.h" /* for macro definitions */ 22 #include <freetype/internal/ftdebug.h> 23 24 #include "pfrerror.h" 25 26 #undef FT_COMPONENT 27 #define FT_COMPONENT pfr 28 29 30 /*************************************************************************/ 31 /*************************************************************************/ 32 /***** *****/ 33 /***** PFR GLYPH BUILDER *****/ 34 /***** *****/ 35 /*************************************************************************/ 36 /*************************************************************************/ 37 38 39 FT_LOCAL_DEF( void ) pfr_glyph_init(PFR_Glyph glyph,FT_GlyphLoader loader)40 pfr_glyph_init( PFR_Glyph glyph, 41 FT_GlyphLoader loader ) 42 { 43 FT_ZERO( glyph ); 44 45 glyph->loader = loader; 46 47 FT_GlyphLoader_Rewind( loader ); 48 } 49 50 51 FT_LOCAL_DEF( void ) pfr_glyph_done(PFR_Glyph glyph)52 pfr_glyph_done( PFR_Glyph glyph ) 53 { 54 FT_Memory memory = glyph->loader->memory; 55 56 57 FT_FREE( glyph->x_control ); 58 glyph->y_control = NULL; 59 60 glyph->max_xy_control = 0; 61 #if 0 62 glyph->num_x_control = 0; 63 glyph->num_y_control = 0; 64 #endif 65 66 FT_FREE( glyph->subs ); 67 68 glyph->max_subs = 0; 69 glyph->num_subs = 0; 70 71 glyph->loader = NULL; 72 glyph->path_begun = 0; 73 } 74 75 76 /* close current contour, if any */ 77 static void pfr_glyph_close_contour(PFR_Glyph glyph)78 pfr_glyph_close_contour( PFR_Glyph glyph ) 79 { 80 FT_GlyphLoader loader = glyph->loader; 81 FT_Outline* outline = &loader->current.outline; 82 FT_Int last, first; 83 84 85 if ( !glyph->path_begun ) 86 return; 87 88 /* compute first and last point indices in current glyph outline */ 89 last = outline->n_points - 1; 90 first = 0; 91 if ( outline->n_contours > 0 ) 92 first = outline->contours[outline->n_contours - 1]; 93 94 /* if the last point falls on the same location as the first one */ 95 /* we need to delete it */ 96 if ( last > first ) 97 { 98 FT_Vector* p1 = outline->points + first; 99 FT_Vector* p2 = outline->points + last; 100 101 102 if ( p1->x == p2->x && p1->y == p2->y ) 103 { 104 outline->n_points--; 105 last--; 106 } 107 } 108 109 /* don't add empty contours */ 110 if ( last >= first ) 111 outline->contours[outline->n_contours++] = (short)last; 112 113 glyph->path_begun = 0; 114 } 115 116 117 /* reset glyph to start the loading of a new glyph */ 118 static void pfr_glyph_start(PFR_Glyph glyph)119 pfr_glyph_start( PFR_Glyph glyph ) 120 { 121 glyph->path_begun = 0; 122 } 123 124 125 static FT_Error pfr_glyph_line_to(PFR_Glyph glyph,FT_Vector * to)126 pfr_glyph_line_to( PFR_Glyph glyph, 127 FT_Vector* to ) 128 { 129 FT_GlyphLoader loader = glyph->loader; 130 FT_Outline* outline = &loader->current.outline; 131 FT_Error error; 132 133 134 /* check that we have begun a new path */ 135 if ( !glyph->path_begun ) 136 { 137 error = FT_THROW( Invalid_Table ); 138 FT_ERROR(( "pfr_glyph_line_to: invalid glyph data\n" )); 139 goto Exit; 140 } 141 142 error = FT_GLYPHLOADER_CHECK_POINTS( loader, 1, 0 ); 143 if ( !error ) 144 { 145 FT_Int n = outline->n_points; 146 147 148 outline->points[n] = *to; 149 outline->tags [n] = FT_CURVE_TAG_ON; 150 151 outline->n_points++; 152 } 153 154 Exit: 155 return error; 156 } 157 158 159 static FT_Error pfr_glyph_curve_to(PFR_Glyph glyph,FT_Vector * control1,FT_Vector * control2,FT_Vector * to)160 pfr_glyph_curve_to( PFR_Glyph glyph, 161 FT_Vector* control1, 162 FT_Vector* control2, 163 FT_Vector* to ) 164 { 165 FT_GlyphLoader loader = glyph->loader; 166 FT_Outline* outline = &loader->current.outline; 167 FT_Error error; 168 169 170 /* check that we have begun a new path */ 171 if ( !glyph->path_begun ) 172 { 173 error = FT_THROW( Invalid_Table ); 174 FT_ERROR(( "pfr_glyph_line_to: invalid glyph data\n" )); 175 goto Exit; 176 } 177 178 error = FT_GLYPHLOADER_CHECK_POINTS( loader, 3, 0 ); 179 if ( !error ) 180 { 181 FT_Vector* vec = outline->points + outline->n_points; 182 FT_Byte* tag = (FT_Byte*)outline->tags + outline->n_points; 183 184 185 vec[0] = *control1; 186 vec[1] = *control2; 187 vec[2] = *to; 188 tag[0] = FT_CURVE_TAG_CUBIC; 189 tag[1] = FT_CURVE_TAG_CUBIC; 190 tag[2] = FT_CURVE_TAG_ON; 191 192 outline->n_points = (FT_Short)( outline->n_points + 3 ); 193 } 194 195 Exit: 196 return error; 197 } 198 199 200 static FT_Error pfr_glyph_move_to(PFR_Glyph glyph,FT_Vector * to)201 pfr_glyph_move_to( PFR_Glyph glyph, 202 FT_Vector* to ) 203 { 204 FT_GlyphLoader loader = glyph->loader; 205 FT_Error error; 206 207 208 /* close current contour if any */ 209 pfr_glyph_close_contour( glyph ); 210 211 /* indicate that a new contour has started */ 212 glyph->path_begun = 1; 213 214 /* check that there is space for a new contour and a new point */ 215 error = FT_GLYPHLOADER_CHECK_POINTS( loader, 1, 1 ); 216 if ( !error ) 217 { 218 /* add new start point */ 219 error = pfr_glyph_line_to( glyph, to ); 220 } 221 222 return error; 223 } 224 225 226 static void pfr_glyph_end(PFR_Glyph glyph)227 pfr_glyph_end( PFR_Glyph glyph ) 228 { 229 /* close current contour if any */ 230 pfr_glyph_close_contour( glyph ); 231 232 /* merge the current glyph into the stack */ 233 FT_GlyphLoader_Add( glyph->loader ); 234 } 235 236 237 /*************************************************************************/ 238 /*************************************************************************/ 239 /***** *****/ 240 /***** PFR GLYPH LOADER *****/ 241 /***** *****/ 242 /*************************************************************************/ 243 /*************************************************************************/ 244 245 246 /* load a simple glyph */ 247 static FT_Error pfr_glyph_load_simple(PFR_Glyph glyph,FT_Byte * p,FT_Byte * limit)248 pfr_glyph_load_simple( PFR_Glyph glyph, 249 FT_Byte* p, 250 FT_Byte* limit ) 251 { 252 FT_Error error = FT_Err_Ok; 253 FT_Memory memory = glyph->loader->memory; 254 FT_UInt flags, x_count, y_count, i, count, mask; 255 FT_Int x; 256 257 258 PFR_CHECK( 1 ); 259 flags = PFR_NEXT_BYTE( p ); 260 261 /* test for composite glyphs */ 262 if ( flags & PFR_GLYPH_IS_COMPOUND ) 263 goto Failure; 264 265 x_count = 0; 266 y_count = 0; 267 268 if ( flags & PFR_GLYPH_1BYTE_XYCOUNT ) 269 { 270 PFR_CHECK( 1 ); 271 count = PFR_NEXT_BYTE( p ); 272 x_count = count & 15; 273 y_count = count >> 4; 274 } 275 else 276 { 277 if ( flags & PFR_GLYPH_XCOUNT ) 278 { 279 PFR_CHECK( 1 ); 280 x_count = PFR_NEXT_BYTE( p ); 281 } 282 283 if ( flags & PFR_GLYPH_YCOUNT ) 284 { 285 PFR_CHECK( 1 ); 286 y_count = PFR_NEXT_BYTE( p ); 287 } 288 } 289 290 count = x_count + y_count; 291 292 /* re-allocate array when necessary */ 293 if ( count > glyph->max_xy_control ) 294 { 295 FT_UInt new_max = FT_PAD_CEIL( count, 8 ); 296 297 298 if ( FT_RENEW_ARRAY( glyph->x_control, 299 glyph->max_xy_control, 300 new_max ) ) 301 goto Exit; 302 303 glyph->max_xy_control = new_max; 304 } 305 306 glyph->y_control = glyph->x_control + x_count; 307 308 mask = 0; 309 x = 0; 310 311 for ( i = 0; i < count; i++ ) 312 { 313 if ( ( i & 7 ) == 0 ) 314 { 315 PFR_CHECK( 1 ); 316 mask = PFR_NEXT_BYTE( p ); 317 } 318 319 if ( mask & 1 ) 320 { 321 PFR_CHECK( 2 ); 322 x = PFR_NEXT_SHORT( p ); 323 } 324 else 325 { 326 PFR_CHECK( 1 ); 327 x += PFR_NEXT_BYTE( p ); 328 } 329 330 glyph->x_control[i] = x; 331 332 mask >>= 1; 333 } 334 335 /* XXX: we ignore the secondary stroke and edge definitions */ 336 /* since we don't support native PFR hinting */ 337 /* */ 338 if ( flags & PFR_GLYPH_SINGLE_EXTRA_ITEMS ) 339 { 340 error = pfr_extra_items_skip( &p, limit ); 341 if ( error ) 342 goto Exit; 343 } 344 345 pfr_glyph_start( glyph ); 346 347 /* now load a simple glyph */ 348 { 349 FT_Vector pos[4]; 350 FT_Vector* cur; 351 352 353 pos[0].x = pos[0].y = 0; 354 pos[3] = pos[0]; 355 356 for (;;) 357 { 358 FT_UInt format, format_low, args_format = 0, args_count, n; 359 360 361 /**************************************************************** 362 * read instruction 363 */ 364 PFR_CHECK( 1 ); 365 format = PFR_NEXT_BYTE( p ); 366 format_low = format & 15; 367 368 switch ( format >> 4 ) 369 { 370 case 0: /* end glyph */ 371 FT_TRACE6(( "- end glyph" )); 372 args_count = 0; 373 break; 374 375 case 1: /* general line operation */ 376 FT_TRACE6(( "- general line" )); 377 goto Line1; 378 379 case 4: /* move to inside contour */ 380 FT_TRACE6(( "- move to inside" )); 381 goto Line1; 382 383 case 5: /* move to outside contour */ 384 FT_TRACE6(( "- move to outside" )); 385 Line1: 386 args_format = format_low; 387 args_count = 1; 388 break; 389 390 case 2: /* horizontal line to */ 391 FT_TRACE6(( "- horizontal line to cx.%d", format_low )); 392 if ( format_low >= x_count ) 393 goto Failure; 394 pos[0].x = glyph->x_control[format_low]; 395 pos[0].y = pos[3].y; 396 pos[3] = pos[0]; 397 args_count = 0; 398 break; 399 400 case 3: /* vertical line to */ 401 FT_TRACE6(( "- vertical line to cy.%d", format_low )); 402 if ( format_low >= y_count ) 403 goto Failure; 404 pos[0].x = pos[3].x; 405 pos[0].y = glyph->y_control[format_low]; 406 pos[3] = pos[0]; 407 args_count = 0; 408 break; 409 410 case 6: /* horizontal to vertical curve */ 411 FT_TRACE6(( "- hv curve" )); 412 args_format = 0xB8E; 413 args_count = 3; 414 break; 415 416 case 7: /* vertical to horizontal curve */ 417 FT_TRACE6(( "- vh curve" )); 418 args_format = 0xE2B; 419 args_count = 3; 420 break; 421 422 default: /* general curve to */ 423 FT_TRACE6(( "- general curve" )); 424 args_count = 4; 425 args_format = format_low; 426 } 427 428 /************************************************************ 429 * now read arguments 430 */ 431 cur = pos; 432 for ( n = 0; n < args_count; n++ ) 433 { 434 FT_UInt idx; 435 FT_Int delta; 436 437 438 /* read the X argument */ 439 switch ( args_format & 3 ) 440 { 441 case 0: /* 8-bit index */ 442 PFR_CHECK( 1 ); 443 idx = PFR_NEXT_BYTE( p ); 444 if ( idx >= x_count ) 445 goto Failure; 446 cur->x = glyph->x_control[idx]; 447 FT_TRACE7(( " cx#%d", idx )); 448 break; 449 450 case 1: /* 16-bit absolute value */ 451 PFR_CHECK( 2 ); 452 cur->x = PFR_NEXT_SHORT( p ); 453 FT_TRACE7(( " x.%ld", cur->x )); 454 break; 455 456 case 2: /* 8-bit delta */ 457 PFR_CHECK( 1 ); 458 delta = PFR_NEXT_INT8( p ); 459 cur->x = pos[3].x + delta; 460 FT_TRACE7(( " dx.%d", delta )); 461 break; 462 463 default: 464 FT_TRACE7(( " |" )); 465 cur->x = pos[3].x; 466 } 467 468 /* read the Y argument */ 469 switch ( ( args_format >> 2 ) & 3 ) 470 { 471 case 0: /* 8-bit index */ 472 PFR_CHECK( 1 ); 473 idx = PFR_NEXT_BYTE( p ); 474 if ( idx >= y_count ) 475 goto Failure; 476 cur->y = glyph->y_control[idx]; 477 FT_TRACE7(( " cy#%d", idx )); 478 break; 479 480 case 1: /* 16-bit absolute value */ 481 PFR_CHECK( 2 ); 482 cur->y = PFR_NEXT_SHORT( p ); 483 FT_TRACE7(( " y.%ld", cur->y )); 484 break; 485 486 case 2: /* 8-bit delta */ 487 PFR_CHECK( 1 ); 488 delta = PFR_NEXT_INT8( p ); 489 cur->y = pos[3].y + delta; 490 FT_TRACE7(( " dy.%d", delta )); 491 break; 492 493 default: 494 FT_TRACE7(( " -" )); 495 cur->y = pos[3].y; 496 } 497 498 /* read the additional format flag for the general curve */ 499 if ( n == 0 && args_count == 4 ) 500 { 501 PFR_CHECK( 1 ); 502 args_format = PFR_NEXT_BYTE( p ); 503 args_count--; 504 } 505 else 506 args_format >>= 4; 507 508 /* save the previous point */ 509 pos[3] = cur[0]; 510 cur++; 511 } 512 513 FT_TRACE7(( "\n" )); 514 515 /************************************************************ 516 * finally, execute instruction 517 */ 518 switch ( format >> 4 ) 519 { 520 case 0: /* end glyph => EXIT */ 521 pfr_glyph_end( glyph ); 522 goto Exit; 523 524 case 1: /* line operations */ 525 case 2: 526 case 3: 527 error = pfr_glyph_line_to( glyph, pos ); 528 goto Test_Error; 529 530 case 4: /* move to inside contour */ 531 case 5: /* move to outside contour */ 532 error = pfr_glyph_move_to( glyph, pos ); 533 goto Test_Error; 534 535 default: /* curve operations */ 536 error = pfr_glyph_curve_to( glyph, pos, pos + 1, pos + 2 ); 537 538 Test_Error: /* test error condition */ 539 if ( error ) 540 goto Exit; 541 } 542 } /* for (;;) */ 543 } 544 545 Exit: 546 return error; 547 548 Failure: 549 Too_Short: 550 error = FT_THROW( Invalid_Table ); 551 FT_ERROR(( "pfr_glyph_load_simple: invalid glyph data\n" )); 552 goto Exit; 553 } 554 555 556 /* load a composite/compound glyph */ 557 static FT_Error pfr_glyph_load_compound(PFR_Glyph glyph,FT_Byte * p,FT_Byte * limit)558 pfr_glyph_load_compound( PFR_Glyph glyph, 559 FT_Byte* p, 560 FT_Byte* limit ) 561 { 562 FT_Error error = FT_Err_Ok; 563 FT_Memory memory = glyph->loader->memory; 564 PFR_SubGlyph subglyph; 565 FT_UInt flags, i, count, org_count; 566 FT_Int x_pos, y_pos; 567 568 569 PFR_CHECK( 1 ); 570 flags = PFR_NEXT_BYTE( p ); 571 572 /* test for composite glyphs */ 573 if ( !( flags & PFR_GLYPH_IS_COMPOUND ) ) 574 goto Failure; 575 576 count = flags & 0x3F; 577 578 /* ignore extra items when present */ 579 /* */ 580 if ( flags & PFR_GLYPH_COMPOUND_EXTRA_ITEMS ) 581 { 582 error = pfr_extra_items_skip( &p, limit ); 583 if ( error ) 584 goto Exit; 585 } 586 587 /* we can't rely on the FT_GlyphLoader to load sub-glyphs, because */ 588 /* the PFR format is dumb, using direct file offsets to point to the */ 589 /* sub-glyphs (instead of glyph indices). Sigh. */ 590 /* */ 591 /* For now, we load the list of sub-glyphs into a different array */ 592 /* but this will prevent us from using the auto-hinter at its best */ 593 /* quality. */ 594 /* */ 595 org_count = glyph->num_subs; 596 597 if ( org_count + count > glyph->max_subs ) 598 { 599 FT_UInt new_max = ( org_count + count + 3 ) & (FT_UInt)-4; 600 601 602 /* we arbitrarily limit the number of subglyphs */ 603 /* to avoid endless recursion */ 604 if ( new_max > 64 ) 605 { 606 error = FT_THROW( Invalid_Table ); 607 FT_ERROR(( "pfr_glyph_load_compound:" 608 " too many compound glyphs components\n" )); 609 goto Exit; 610 } 611 612 if ( FT_RENEW_ARRAY( glyph->subs, glyph->max_subs, new_max ) ) 613 goto Exit; 614 615 glyph->max_subs = new_max; 616 } 617 618 subglyph = glyph->subs + org_count; 619 620 for ( i = 0; i < count; i++, subglyph++ ) 621 { 622 FT_UInt format; 623 624 625 x_pos = 0; 626 y_pos = 0; 627 628 PFR_CHECK( 1 ); 629 format = PFR_NEXT_BYTE( p ); 630 631 /* read scale when available */ 632 subglyph->x_scale = 0x10000L; 633 if ( format & PFR_SUBGLYPH_XSCALE ) 634 { 635 PFR_CHECK( 2 ); 636 subglyph->x_scale = PFR_NEXT_SHORT( p ) * 16; 637 } 638 639 subglyph->y_scale = 0x10000L; 640 if ( format & PFR_SUBGLYPH_YSCALE ) 641 { 642 PFR_CHECK( 2 ); 643 subglyph->y_scale = PFR_NEXT_SHORT( p ) * 16; 644 } 645 646 /* read offset */ 647 switch ( format & 3 ) 648 { 649 case 1: 650 PFR_CHECK( 2 ); 651 x_pos = PFR_NEXT_SHORT( p ); 652 break; 653 654 case 2: 655 PFR_CHECK( 1 ); 656 x_pos += PFR_NEXT_INT8( p ); 657 break; 658 659 default: 660 ; 661 } 662 663 switch ( ( format >> 2 ) & 3 ) 664 { 665 case 1: 666 PFR_CHECK( 2 ); 667 y_pos = PFR_NEXT_SHORT( p ); 668 break; 669 670 case 2: 671 PFR_CHECK( 1 ); 672 y_pos += PFR_NEXT_INT8( p ); 673 break; 674 675 default: 676 ; 677 } 678 679 subglyph->x_delta = x_pos; 680 subglyph->y_delta = y_pos; 681 682 /* read glyph position and size now */ 683 if ( format & PFR_SUBGLYPH_2BYTE_SIZE ) 684 { 685 PFR_CHECK( 2 ); 686 subglyph->gps_size = PFR_NEXT_USHORT( p ); 687 } 688 else 689 { 690 PFR_CHECK( 1 ); 691 subglyph->gps_size = PFR_NEXT_BYTE( p ); 692 } 693 694 if ( format & PFR_SUBGLYPH_3BYTE_OFFSET ) 695 { 696 PFR_CHECK( 3 ); 697 subglyph->gps_offset = PFR_NEXT_ULONG( p ); 698 } 699 else 700 { 701 PFR_CHECK( 2 ); 702 subglyph->gps_offset = PFR_NEXT_USHORT( p ); 703 } 704 705 glyph->num_subs++; 706 } 707 708 Exit: 709 return error; 710 711 Failure: 712 Too_Short: 713 error = FT_THROW( Invalid_Table ); 714 FT_ERROR(( "pfr_glyph_load_compound: invalid glyph data\n" )); 715 goto Exit; 716 } 717 718 719 static FT_Error pfr_glyph_load_rec(PFR_Glyph glyph,FT_Stream stream,FT_ULong gps_offset,FT_ULong offset,FT_ULong size)720 pfr_glyph_load_rec( PFR_Glyph glyph, 721 FT_Stream stream, 722 FT_ULong gps_offset, 723 FT_ULong offset, 724 FT_ULong size ) 725 { 726 FT_Error error; 727 FT_Byte* p; 728 FT_Byte* limit; 729 730 731 if ( FT_STREAM_SEEK( gps_offset + offset ) || 732 FT_FRAME_ENTER( size ) ) 733 goto Exit; 734 735 p = (FT_Byte*)stream->cursor; 736 limit = p + size; 737 738 if ( size > 0 && *p & PFR_GLYPH_IS_COMPOUND ) 739 { 740 FT_UInt n, old_count, count; 741 FT_GlyphLoader loader = glyph->loader; 742 FT_Outline* base = &loader->base.outline; 743 744 745 old_count = glyph->num_subs; 746 747 /* this is a compound glyph - load it */ 748 error = pfr_glyph_load_compound( glyph, p, limit ); 749 750 FT_FRAME_EXIT(); 751 752 if ( error ) 753 goto Exit; 754 755 count = glyph->num_subs - old_count; 756 757 FT_TRACE4(( "compound glyph with %d element%s (offset %lu):\n", 758 count, 759 count == 1 ? "" : "s", 760 offset )); 761 762 /* now, load each individual glyph */ 763 for ( n = 0; n < count; n++ ) 764 { 765 FT_Int i, old_points, num_points; 766 PFR_SubGlyph subglyph; 767 768 769 FT_TRACE4(( " subglyph %d:\n", n )); 770 771 subglyph = glyph->subs + old_count + n; 772 old_points = base->n_points; 773 774 error = pfr_glyph_load_rec( glyph, stream, gps_offset, 775 subglyph->gps_offset, 776 subglyph->gps_size ); 777 if ( error ) 778 break; 779 780 /* note that `glyph->subs' might have been re-allocated */ 781 subglyph = glyph->subs + old_count + n; 782 num_points = base->n_points - old_points; 783 784 /* translate and eventually scale the new glyph points */ 785 if ( subglyph->x_scale != 0x10000L || subglyph->y_scale != 0x10000L ) 786 { 787 FT_Vector* vec = base->points + old_points; 788 789 790 for ( i = 0; i < num_points; i++, vec++ ) 791 { 792 vec->x = FT_MulFix( vec->x, subglyph->x_scale ) + 793 subglyph->x_delta; 794 vec->y = FT_MulFix( vec->y, subglyph->y_scale ) + 795 subglyph->y_delta; 796 } 797 } 798 else 799 { 800 FT_Vector* vec = loader->base.outline.points + old_points; 801 802 803 for ( i = 0; i < num_points; i++, vec++ ) 804 { 805 vec->x += subglyph->x_delta; 806 vec->y += subglyph->y_delta; 807 } 808 } 809 810 /* proceed to next sub-glyph */ 811 } 812 813 FT_TRACE4(( "end compound glyph with %d element%s\n", 814 count, 815 count == 1 ? "" : "s" )); 816 } 817 else 818 { 819 FT_TRACE4(( "simple glyph (offset %lu)\n", offset )); 820 821 /* load a simple glyph */ 822 error = pfr_glyph_load_simple( glyph, p, limit ); 823 824 FT_FRAME_EXIT(); 825 } 826 827 Exit: 828 return error; 829 } 830 831 832 FT_LOCAL_DEF( FT_Error ) pfr_glyph_load(PFR_Glyph glyph,FT_Stream stream,FT_ULong gps_offset,FT_ULong offset,FT_ULong size)833 pfr_glyph_load( PFR_Glyph glyph, 834 FT_Stream stream, 835 FT_ULong gps_offset, 836 FT_ULong offset, 837 FT_ULong size ) 838 { 839 /* initialize glyph loader */ 840 FT_GlyphLoader_Rewind( glyph->loader ); 841 842 glyph->num_subs = 0; 843 844 /* load the glyph, recursively when needed */ 845 return pfr_glyph_load_rec( glyph, stream, gps_offset, offset, size ); 846 } 847 848 849 /* END */ 850