1 /***************************************************************************/ 2 /* */ 3 /* t1decode.c */ 4 /* */ 5 /* PostScript Type 1 decoding routines (body). */ 6 /* */ 7 /* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 */ 8 /* 2010 by */ 9 /* David Turner, Robert Wilhelm, and Werner Lemberg. */ 10 /* */ 11 /* This file is part of the FreeType project, and may only be used, */ 12 /* modified, and distributed under the terms of the FreeType project */ 13 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ 14 /* this file you indicate that you have read the license and */ 15 /* understand and accept it fully. */ 16 /* */ 17 /***************************************************************************/ 18 19 20 #include <ft2build.h> 21 #include FT_INTERNAL_CALC_H 22 #include FT_INTERNAL_DEBUG_H 23 #include FT_INTERNAL_POSTSCRIPT_HINTS_H 24 #include FT_OUTLINE_H 25 26 #include "t1decode.h" 27 #include "psobjs.h" 28 29 #include "psauxerr.h" 30 31 32 /*************************************************************************/ 33 /* */ 34 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ 35 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ 36 /* messages during execution. */ 37 /* */ 38 #undef FT_COMPONENT 39 #define FT_COMPONENT trace_t1decode 40 41 42 typedef enum T1_Operator_ 43 { 44 op_none = 0, 45 op_endchar, 46 op_hsbw, 47 op_seac, 48 op_sbw, 49 op_closepath, 50 op_hlineto, 51 op_hmoveto, 52 op_hvcurveto, 53 op_rlineto, 54 op_rmoveto, 55 op_rrcurveto, 56 op_vhcurveto, 57 op_vlineto, 58 op_vmoveto, 59 op_dotsection, 60 op_hstem, 61 op_hstem3, 62 op_vstem, 63 op_vstem3, 64 op_div, 65 op_callothersubr, 66 op_callsubr, 67 op_pop, 68 op_return, 69 op_setcurrentpoint, 70 op_unknown15, 71 72 op_max /* never remove this one */ 73 74 } T1_Operator; 75 76 77 static 78 const FT_Int t1_args_count[op_max] = 79 { 80 0, /* none */ 81 0, /* endchar */ 82 2, /* hsbw */ 83 5, /* seac */ 84 4, /* sbw */ 85 0, /* closepath */ 86 1, /* hlineto */ 87 1, /* hmoveto */ 88 4, /* hvcurveto */ 89 2, /* rlineto */ 90 2, /* rmoveto */ 91 6, /* rrcurveto */ 92 4, /* vhcurveto */ 93 1, /* vlineto */ 94 1, /* vmoveto */ 95 0, /* dotsection */ 96 2, /* hstem */ 97 6, /* hstem3 */ 98 2, /* vstem */ 99 6, /* vstem3 */ 100 2, /* div */ 101 -1, /* callothersubr */ 102 1, /* callsubr */ 103 0, /* pop */ 104 0, /* return */ 105 2, /* setcurrentpoint */ 106 2 /* opcode 15 (undocumented and obsolete) */ 107 }; 108 109 110 /*************************************************************************/ 111 /* */ 112 /* <Function> */ 113 /* t1_lookup_glyph_by_stdcharcode */ 114 /* */ 115 /* <Description> */ 116 /* Looks up a given glyph by its StandardEncoding charcode. Used to */ 117 /* implement the SEAC Type 1 operator. */ 118 /* */ 119 /* <Input> */ 120 /* face :: The current face object. */ 121 /* */ 122 /* charcode :: The character code to look for. */ 123 /* */ 124 /* <Return> */ 125 /* A glyph index in the font face. Returns -1 if the corresponding */ 126 /* glyph wasn't found. */ 127 /* */ 128 static FT_Int 129 t1_lookup_glyph_by_stdcharcode( T1_Decoder decoder, 130 FT_Int charcode ) 131 { 132 FT_UInt n; 133 const FT_String* glyph_name; 134 FT_Service_PsCMaps psnames = decoder->psnames; 135 136 137 /* check range of standard char code */ 138 if ( charcode < 0 || charcode > 255 ) 139 return -1; 140 141 glyph_name = psnames->adobe_std_strings( 142 psnames->adobe_std_encoding[charcode]); 143 144 for ( n = 0; n < decoder->num_glyphs; n++ ) 145 { 146 FT_String* name = (FT_String*)decoder->glyph_names[n]; 147 148 149 if ( name && 150 name[0] == glyph_name[0] && 151 ft_strcmp( name, glyph_name ) == 0 ) 152 return n; 153 } 154 155 return -1; 156 } 157 158 159 /*************************************************************************/ 160 /* */ 161 /* <Function> */ 162 /* t1operator_seac */ 163 /* */ 164 /* <Description> */ 165 /* Implements the `seac' Type 1 operator for a Type 1 decoder. */ 166 /* */ 167 /* <Input> */ 168 /* decoder :: The current CID decoder. */ 169 /* */ 170 /* asb :: The accent's side bearing. */ 171 /* */ 172 /* adx :: The horizontal offset of the accent. */ 173 /* */ 174 /* ady :: The vertical offset of the accent. */ 175 /* */ 176 /* bchar :: The base character's StandardEncoding charcode. */ 177 /* */ 178 /* achar :: The accent character's StandardEncoding charcode. */ 179 /* */ 180 /* <Return> */ 181 /* FreeType error code. 0 means success. */ 182 /* */ 183 static FT_Error 184 t1operator_seac( T1_Decoder decoder, 185 FT_Pos asb, 186 FT_Pos adx, 187 FT_Pos ady, 188 FT_Int bchar, 189 FT_Int achar ) 190 { 191 FT_Error error; 192 FT_Int bchar_index, achar_index; 193 #if 0 194 FT_Int n_base_points; 195 FT_Outline* base = decoder->builder.base; 196 #endif 197 FT_Vector left_bearing, advance; 198 199 #ifdef FT_CONFIG_OPTION_INCREMENTAL 200 T1_Face face = (T1_Face)decoder->builder.face; 201 #endif 202 203 204 if ( decoder->seac ) 205 { 206 FT_ERROR(( "t1operator_seac: invalid nested seac\n" )); 207 return PSaux_Err_Syntax_Error; 208 } 209 210 /* seac weirdness */ 211 adx += decoder->builder.left_bearing.x; 212 213 /* `glyph_names' is set to 0 for CID fonts which do not */ 214 /* include an encoding. How can we deal with these? */ 215 #ifdef FT_CONFIG_OPTION_INCREMENTAL 216 if ( decoder->glyph_names == 0 && 217 !face->root.internal->incremental_interface ) 218 #else 219 if ( decoder->glyph_names == 0 ) 220 #endif /* FT_CONFIG_OPTION_INCREMENTAL */ 221 { 222 FT_ERROR(( "t1operator_seac:" 223 " glyph names table not available in this font\n" )); 224 return PSaux_Err_Syntax_Error; 225 } 226 227 #ifdef FT_CONFIG_OPTION_INCREMENTAL 228 if ( face->root.internal->incremental_interface ) 229 { 230 /* the caller must handle the font encoding also */ 231 bchar_index = bchar; 232 achar_index = achar; 233 } 234 else 235 #endif 236 { 237 bchar_index = t1_lookup_glyph_by_stdcharcode( decoder, bchar ); 238 achar_index = t1_lookup_glyph_by_stdcharcode( decoder, achar ); 239 } 240 241 if ( bchar_index < 0 || achar_index < 0 ) 242 { 243 FT_ERROR(( "t1operator_seac:" 244 " invalid seac character code arguments\n" )); 245 return PSaux_Err_Syntax_Error; 246 } 247 248 /* if we are trying to load a composite glyph, do not load the */ 249 /* accent character and return the array of subglyphs. */ 250 if ( decoder->builder.no_recurse ) 251 { 252 FT_GlyphSlot glyph = (FT_GlyphSlot)decoder->builder.glyph; 253 FT_GlyphLoader loader = glyph->internal->loader; 254 FT_SubGlyph subg; 255 256 257 /* reallocate subglyph array if necessary */ 258 error = FT_GlyphLoader_CheckSubGlyphs( loader, 2 ); 259 if ( error ) 260 goto Exit; 261 262 subg = loader->current.subglyphs; 263 264 /* subglyph 0 = base character */ 265 subg->index = bchar_index; 266 subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES | 267 FT_SUBGLYPH_FLAG_USE_MY_METRICS; 268 subg->arg1 = 0; 269 subg->arg2 = 0; 270 subg++; 271 272 /* subglyph 1 = accent character */ 273 subg->index = achar_index; 274 subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES; 275 subg->arg1 = (FT_Int)FIXED_TO_INT( adx - asb ); 276 subg->arg2 = (FT_Int)FIXED_TO_INT( ady ); 277 278 /* set up remaining glyph fields */ 279 glyph->num_subglyphs = 2; 280 glyph->subglyphs = loader->base.subglyphs; 281 glyph->format = FT_GLYPH_FORMAT_COMPOSITE; 282 283 loader->current.num_subglyphs = 2; 284 goto Exit; 285 } 286 287 /* First load `bchar' in builder */ 288 /* now load the unscaled outline */ 289 290 FT_GlyphLoader_Prepare( decoder->builder.loader ); /* prepare loader */ 291 292 /* the seac operator must not be nested */ 293 decoder->seac = TRUE; 294 error = t1_decoder_parse_glyph( decoder, bchar_index ); 295 decoder->seac = FALSE; 296 if ( error ) 297 goto Exit; 298 299 /* save the left bearing and width of the base character */ 300 /* as they will be erased by the next load. */ 301 302 left_bearing = decoder->builder.left_bearing; 303 advance = decoder->builder.advance; 304 305 decoder->builder.left_bearing.x = 0; 306 decoder->builder.left_bearing.y = 0; 307 308 decoder->builder.pos_x = adx - asb; 309 decoder->builder.pos_y = ady; 310 311 /* Now load `achar' on top of */ 312 /* the base outline */ 313 314 /* the seac operator must not be nested */ 315 decoder->seac = TRUE; 316 error = t1_decoder_parse_glyph( decoder, achar_index ); 317 decoder->seac = FALSE; 318 if ( error ) 319 goto Exit; 320 321 /* restore the left side bearing and */ 322 /* advance width of the base character */ 323 324 decoder->builder.left_bearing = left_bearing; 325 decoder->builder.advance = advance; 326 327 decoder->builder.pos_x = 0; 328 decoder->builder.pos_y = 0; 329 330 Exit: 331 return error; 332 } 333 334 335 /*************************************************************************/ 336 /* */ 337 /* <Function> */ 338 /* t1_decoder_parse_charstrings */ 339 /* */ 340 /* <Description> */ 341 /* Parses a given Type 1 charstrings program. */ 342 /* */ 343 /* <Input> */ 344 /* decoder :: The current Type 1 decoder. */ 345 /* */ 346 /* charstring_base :: The base address of the charstring stream. */ 347 /* */ 348 /* charstring_len :: The length in bytes of the charstring stream. */ 349 /* */ 350 /* <Return> */ 351 /* FreeType error code. 0 means success. */ 352 /* */ 353 FT_LOCAL_DEF( FT_Error ) 354 t1_decoder_parse_charstrings( T1_Decoder decoder, 355 FT_Byte* charstring_base, 356 FT_UInt charstring_len ) 357 { 358 FT_Error error; 359 T1_Decoder_Zone zone; 360 FT_Byte* ip; 361 FT_Byte* limit; 362 T1_Builder builder = &decoder->builder; 363 FT_Pos x, y, orig_x, orig_y; 364 FT_Int known_othersubr_result_cnt = 0; 365 FT_Int unknown_othersubr_result_cnt = 0; 366 FT_Bool large_int; 367 FT_Fixed seed; 368 369 T1_Hints_Funcs hinter; 370 371 #ifdef FT_DEBUG_LEVEL_TRACE 372 FT_Bool bol = TRUE; 373 #endif 374 375 376 /* we don't want to touch the source code -- use macro trick */ 377 #define start_point t1_builder_start_point 378 #define check_points t1_builder_check_points 379 #define add_point t1_builder_add_point 380 #define add_point1 t1_builder_add_point1 381 #define add_contour t1_builder_add_contour 382 #define close_contour t1_builder_close_contour 383 384 385 /* compute random seed from stack address of parameter */ 386 seed = (FT_Fixed)( ( (FT_PtrDist)(char*)&seed ^ 387 (FT_PtrDist)(char*)&decoder ^ 388 (FT_PtrDist)(char*)&charstring_base ) & 389 FT_ULONG_MAX ) ; 390 seed = ( seed ^ ( seed >> 10 ) ^ ( seed >> 20 ) ) & 0xFFFFL; 391 if ( seed == 0 ) 392 seed = 0x7384; 393 394 /* First of all, initialize the decoder */ 395 decoder->top = decoder->stack; 396 decoder->zone = decoder->zones; 397 zone = decoder->zones; 398 399 builder->parse_state = T1_Parse_Start; 400 401 hinter = (T1_Hints_Funcs)builder->hints_funcs; 402 403 /* a font that reads BuildCharArray without setting */ 404 /* its values first is buggy, but ... */ 405 FT_ASSERT( ( decoder->len_buildchar == 0 ) == 406 ( decoder->buildchar == NULL ) ); 407 408 if ( decoder->len_buildchar > 0 ) 409 ft_memset( &decoder->buildchar[0], 410 0, 411 sizeof( decoder->buildchar[0] ) * decoder->len_buildchar ); 412 413 FT_TRACE4(( "\n" 414 "Start charstring\n" )); 415 416 zone->base = charstring_base; 417 limit = zone->limit = charstring_base + charstring_len; 418 ip = zone->cursor = zone->base; 419 420 error = PSaux_Err_Ok; 421 422 x = orig_x = builder->pos_x; 423 y = orig_y = builder->pos_y; 424 425 /* begin hints recording session, if any */ 426 if ( hinter ) 427 hinter->open( hinter->hints ); 428 429 large_int = FALSE; 430 431 /* now, execute loop */ 432 while ( ip < limit ) 433 { 434 FT_Long* top = decoder->top; 435 T1_Operator op = op_none; 436 FT_Int32 value = 0; 437 438 439 FT_ASSERT( known_othersubr_result_cnt == 0 || 440 unknown_othersubr_result_cnt == 0 ); 441 442 #ifdef FT_DEBUG_LEVEL_TRACE 443 if ( bol ) 444 { 445 FT_TRACE5(( " (%d)", decoder->top - decoder->stack )); 446 bol = FALSE; 447 } 448 #endif 449 450 /*********************************************************************/ 451 /* */ 452 /* Decode operator or operand */ 453 /* */ 454 /* */ 455 456 /* first of all, decompress operator or value */ 457 switch ( *ip++ ) 458 { 459 case 1: 460 op = op_hstem; 461 break; 462 463 case 3: 464 op = op_vstem; 465 break; 466 case 4: 467 op = op_vmoveto; 468 break; 469 case 5: 470 op = op_rlineto; 471 break; 472 case 6: 473 op = op_hlineto; 474 break; 475 case 7: 476 op = op_vlineto; 477 break; 478 case 8: 479 op = op_rrcurveto; 480 break; 481 case 9: 482 op = op_closepath; 483 break; 484 case 10: 485 op = op_callsubr; 486 break; 487 case 11: 488 op = op_return; 489 break; 490 491 case 13: 492 op = op_hsbw; 493 break; 494 case 14: 495 op = op_endchar; 496 break; 497 498 case 15: /* undocumented, obsolete operator */ 499 op = op_unknown15; 500 break; 501 502 case 21: 503 op = op_rmoveto; 504 break; 505 case 22: 506 op = op_hmoveto; 507 break; 508 509 case 30: 510 op = op_vhcurveto; 511 break; 512 case 31: 513 op = op_hvcurveto; 514 break; 515 516 case 12: 517 if ( ip > limit ) 518 { 519 FT_ERROR(( "t1_decoder_parse_charstrings:" 520 " invalid escape (12+EOF)\n" )); 521 goto Syntax_Error; 522 } 523 524 switch ( *ip++ ) 525 { 526 case 0: 527 op = op_dotsection; 528 break; 529 case 1: 530 op = op_vstem3; 531 break; 532 case 2: 533 op = op_hstem3; 534 break; 535 case 6: 536 op = op_seac; 537 break; 538 case 7: 539 op = op_sbw; 540 break; 541 case 12: 542 op = op_div; 543 break; 544 case 16: 545 op = op_callothersubr; 546 break; 547 case 17: 548 op = op_pop; 549 break; 550 case 33: 551 op = op_setcurrentpoint; 552 break; 553 554 default: 555 FT_ERROR(( "t1_decoder_parse_charstrings:" 556 " invalid escape (12+%d)\n", 557 ip[-1] )); 558 goto Syntax_Error; 559 } 560 break; 561 562 case 255: /* four bytes integer */ 563 if ( ip + 4 > limit ) 564 { 565 FT_ERROR(( "t1_decoder_parse_charstrings:" 566 " unexpected EOF in integer\n" )); 567 goto Syntax_Error; 568 } 569 570 value = (FT_Int32)( ( (FT_Long)ip[0] << 24 ) | 571 ( (FT_Long)ip[1] << 16 ) | 572 ( (FT_Long)ip[2] << 8 ) | 573 ip[3] ); 574 ip += 4; 575 576 /* According to the specification, values > 32000 or < -32000 must */ 577 /* be followed by a `div' operator to make the result be in the */ 578 /* range [-32000;32000]. We expect that the second argument of */ 579 /* `div' is not a large number. Additionally, we don't handle */ 580 /* stuff like `<large1> <large2> <num> div <num> div' or */ 581 /* <large1> <large2> <num> div div'. This is probably not allowed */ 582 /* anyway. */ 583 if ( value > 32000 || value < -32000 ) 584 { 585 if ( large_int ) 586 { 587 FT_ERROR(( "t1_decoder_parse_charstrings:" 588 " no `div' after large integer\n" )); 589 } 590 else 591 large_int = TRUE; 592 } 593 else 594 { 595 if ( !large_int ) 596 value <<= 16; 597 } 598 599 break; 600 601 default: 602 if ( ip[-1] >= 32 ) 603 { 604 if ( ip[-1] < 247 ) 605 value = (FT_Int32)ip[-1] - 139; 606 else 607 { 608 if ( ++ip > limit ) 609 { 610 FT_ERROR(( "t1_decoder_parse_charstrings:" 611 " unexpected EOF in integer\n" )); 612 goto Syntax_Error; 613 } 614 615 if ( ip[-2] < 251 ) 616 value = ( ( (FT_Int32)ip[-2] - 247 ) << 8 ) + ip[-1] + 108; 617 else 618 value = -( ( ( (FT_Int32)ip[-2] - 251 ) << 8 ) + ip[-1] + 108 ); 619 } 620 621 if ( !large_int ) 622 value <<= 16; 623 } 624 else 625 { 626 FT_ERROR(( "t1_decoder_parse_charstrings:" 627 " invalid byte (%d)\n", ip[-1] )); 628 goto Syntax_Error; 629 } 630 } 631 632 if ( unknown_othersubr_result_cnt > 0 ) 633 { 634 switch ( op ) 635 { 636 case op_callsubr: 637 case op_return: 638 case op_none: 639 case op_pop: 640 break; 641 642 default: 643 /* all operands have been transferred by previous pops */ 644 unknown_othersubr_result_cnt = 0; 645 break; 646 } 647 } 648 649 if ( large_int && !( op == op_none || op == op_div ) ) 650 { 651 FT_ERROR(( "t1_decoder_parse_charstrings:" 652 " no `div' after large integer\n" )); 653 654 large_int = FALSE; 655 } 656 657 /*********************************************************************/ 658 /* */ 659 /* Push value on stack, or process operator */ 660 /* */ 661 /* */ 662 if ( op == op_none ) 663 { 664 if ( top - decoder->stack >= T1_MAX_CHARSTRINGS_OPERANDS ) 665 { 666 FT_ERROR(( "t1_decoder_parse_charstrings: stack overflow\n" )); 667 goto Syntax_Error; 668 } 669 670 #ifdef FT_DEBUG_LEVEL_TRACE 671 if ( large_int ) 672 FT_TRACE4(( " %ld", value )); 673 else 674 FT_TRACE4(( " %ld", (FT_Int32)( value >> 16 ) )); 675 #endif 676 677 *top++ = value; 678 decoder->top = top; 679 } 680 else if ( op == op_callothersubr ) /* callothersubr */ 681 { 682 FT_Int subr_no; 683 FT_Int arg_cnt; 684 685 686 #ifdef FT_DEBUG_LEVEL_TRACE 687 FT_TRACE4(( " callothersubr\n" )); 688 bol = TRUE; 689 #endif 690 691 if ( top - decoder->stack < 2 ) 692 goto Stack_Underflow; 693 694 top -= 2; 695 696 subr_no = (FT_Int)( top[1] >> 16 ); 697 arg_cnt = (FT_Int)( top[0] >> 16 ); 698 699 /***********************************************************/ 700 /* */ 701 /* remove all operands to callothersubr from the stack */ 702 /* */ 703 /* for handled othersubrs, where we know the number of */ 704 /* arguments, we increase the stack by the value of */ 705 /* known_othersubr_result_cnt */ 706 /* */ 707 /* for unhandled othersubrs the following pops adjust the */ 708 /* stack pointer as necessary */ 709 710 if ( arg_cnt > top - decoder->stack ) 711 goto Stack_Underflow; 712 713 top -= arg_cnt; 714 715 known_othersubr_result_cnt = 0; 716 unknown_othersubr_result_cnt = 0; 717 718 /* XXX TODO: The checks to `arg_count == <whatever>' */ 719 /* might not be correct; an othersubr expects a certain */ 720 /* number of operands on the PostScript stack (as opposed */ 721 /* to the T1 stack) but it doesn't have to put them there */ 722 /* by itself; previous othersubrs might have left the */ 723 /* operands there if they were not followed by an */ 724 /* appropriate number of pops */ 725 /* */ 726 /* On the other hand, Adobe Reader 7.0.8 for Linux doesn't */ 727 /* accept a font that contains charstrings like */ 728 /* */ 729 /* 100 200 2 20 callothersubr */ 730 /* 300 1 20 callothersubr pop */ 731 /* */ 732 /* Perhaps this is the reason why BuildCharArray exists. */ 733 734 switch ( subr_no ) 735 { 736 case 1: /* start flex feature */ 737 if ( arg_cnt != 0 ) 738 goto Unexpected_OtherSubr; 739 740 decoder->flex_state = 1; 741 decoder->num_flex_vectors = 0; 742 if ( start_point( builder, x, y ) || 743 check_points( builder, 6 ) ) 744 goto Fail; 745 break; 746 747 case 2: /* add flex vectors */ 748 { 749 FT_Int idx; 750 751 752 if ( arg_cnt != 0 ) 753 goto Unexpected_OtherSubr; 754 755 /* note that we should not add a point for index 0; */ 756 /* this will move our current position to the flex */ 757 /* point without adding any point to the outline */ 758 idx = decoder->num_flex_vectors++; 759 if ( idx > 0 && idx < 7 ) 760 add_point( builder, 761 x, 762 y, 763 (FT_Byte)( idx == 3 || idx == 6 ) ); 764 } 765 break; 766 767 case 0: /* end flex feature */ 768 if ( arg_cnt != 3 ) 769 goto Unexpected_OtherSubr; 770 771 if ( decoder->flex_state == 0 || 772 decoder->num_flex_vectors != 7 ) 773 { 774 FT_ERROR(( "t1_decoder_parse_charstrings:" 775 " unexpected flex end\n" )); 776 goto Syntax_Error; 777 } 778 779 /* the two `results' are popped by the following setcurrentpoint */ 780 top[0] = x; 781 top[1] = y; 782 known_othersubr_result_cnt = 2; 783 break; 784 785 case 3: /* change hints */ 786 if ( arg_cnt != 1 ) 787 goto Unexpected_OtherSubr; 788 789 known_othersubr_result_cnt = 1; 790 791 if ( hinter ) 792 hinter->reset( hinter->hints, builder->current->n_points ); 793 break; 794 795 case 12: 796 case 13: 797 /* counter control hints, clear stack */ 798 top = decoder->stack; 799 break; 800 801 case 14: 802 case 15: 803 case 16: 804 case 17: 805 case 18: /* multiple masters */ 806 { 807 PS_Blend blend = decoder->blend; 808 FT_UInt num_points, nn, mm; 809 FT_Long* delta; 810 FT_Long* values; 811 812 813 if ( !blend ) 814 { 815 FT_ERROR(( "t1_decoder_parse_charstrings:" 816 " unexpected multiple masters operator\n" )); 817 goto Syntax_Error; 818 } 819 820 num_points = (FT_UInt)subr_no - 13 + ( subr_no == 18 ); 821 if ( arg_cnt != (FT_Int)( num_points * blend->num_designs ) ) 822 { 823 FT_ERROR(( "t1_decoder_parse_charstrings:" 824 " incorrect number of multiple masters arguments\n" )); 825 goto Syntax_Error; 826 } 827 828 /* we want to compute: */ 829 /* */ 830 /* a0*w0 + a1*w1 + ... + ak*wk */ 831 /* */ 832 /* but we only have the a0, a1-a0, a2-a0, .. ak-a0 */ 833 /* however, given that w0 + w1 + ... + wk == 1, we can */ 834 /* rewrite it easily as: */ 835 /* */ 836 /* a0 + (a1-a0)*w1 + (a2-a0)*w2 + .. + (ak-a0)*wk */ 837 /* */ 838 /* where k == num_designs-1 */ 839 /* */ 840 /* I guess that's why it's written in this `compact' */ 841 /* form. */ 842 /* */ 843 delta = top + num_points; 844 values = top; 845 for ( nn = 0; nn < num_points; nn++ ) 846 { 847 FT_Long tmp = values[0]; 848 849 850 for ( mm = 1; mm < blend->num_designs; mm++ ) 851 tmp += FT_MulFix( *delta++, blend->weight_vector[mm] ); 852 853 *values++ = tmp; 854 } 855 856 known_othersubr_result_cnt = num_points; 857 break; 858 } 859 860 case 19: 861 /* <idx> 1 19 callothersubr */ 862 /* => replace elements starting from index cvi( <idx> ) */ 863 /* of BuildCharArray with WeightVector */ 864 { 865 FT_Int idx; 866 PS_Blend blend = decoder->blend; 867 868 869 if ( arg_cnt != 1 || blend == NULL ) 870 goto Unexpected_OtherSubr; 871 872 idx = (FT_Int)( top[0] >> 16 ); 873 874 if ( idx < 0 || 875 idx + blend->num_designs > decoder->len_buildchar ) 876 goto Unexpected_OtherSubr; 877 878 ft_memcpy( &decoder->buildchar[idx], 879 blend->weight_vector, 880 blend->num_designs * 881 sizeof( blend->weight_vector[0] ) ); 882 } 883 break; 884 885 case 20: 886 /* <arg1> <arg2> 2 20 callothersubr pop */ 887 /* ==> push <arg1> + <arg2> onto T1 stack */ 888 if ( arg_cnt != 2 ) 889 goto Unexpected_OtherSubr; 890 891 top[0] += top[1]; /* XXX (over|under)flow */ 892 893 known_othersubr_result_cnt = 1; 894 break; 895 896 case 21: 897 /* <arg1> <arg2> 2 21 callothersubr pop */ 898 /* ==> push <arg1> - <arg2> onto T1 stack */ 899 if ( arg_cnt != 2 ) 900 goto Unexpected_OtherSubr; 901 902 top[0] -= top[1]; /* XXX (over|under)flow */ 903 904 known_othersubr_result_cnt = 1; 905 break; 906 907 case 22: 908 /* <arg1> <arg2> 2 22 callothersubr pop */ 909 /* ==> push <arg1> * <arg2> onto T1 stack */ 910 if ( arg_cnt != 2 ) 911 goto Unexpected_OtherSubr; 912 913 top[0] = FT_MulFix( top[0], top[1] ); 914 915 known_othersubr_result_cnt = 1; 916 break; 917 918 case 23: 919 /* <arg1> <arg2> 2 23 callothersubr pop */ 920 /* ==> push <arg1> / <arg2> onto T1 stack */ 921 if ( arg_cnt != 2 || top[1] == 0 ) 922 goto Unexpected_OtherSubr; 923 924 top[0] = FT_DivFix( top[0], top[1] ); 925 926 known_othersubr_result_cnt = 1; 927 break; 928 929 case 24: 930 /* <val> <idx> 2 24 callothersubr */ 931 /* ==> set BuildCharArray[cvi( <idx> )] = <val> */ 932 { 933 FT_Int idx; 934 PS_Blend blend = decoder->blend; 935 936 937 if ( arg_cnt != 2 || blend == NULL ) 938 goto Unexpected_OtherSubr; 939 940 idx = (FT_Int)( top[1] >> 16 ); 941 942 if ( idx < 0 || (FT_UInt) idx >= decoder->len_buildchar ) 943 goto Unexpected_OtherSubr; 944 945 decoder->buildchar[idx] = top[0]; 946 } 947 break; 948 949 case 25: 950 /* <idx> 1 25 callothersubr pop */ 951 /* ==> push BuildCharArray[cvi( idx )] */ 952 /* onto T1 stack */ 953 { 954 FT_Int idx; 955 PS_Blend blend = decoder->blend; 956 957 958 if ( arg_cnt != 1 || blend == NULL ) 959 goto Unexpected_OtherSubr; 960 961 idx = (FT_Int)( top[0] >> 16 ); 962 963 if ( idx < 0 || (FT_UInt) idx >= decoder->len_buildchar ) 964 goto Unexpected_OtherSubr; 965 966 top[0] = decoder->buildchar[idx]; 967 } 968 969 known_othersubr_result_cnt = 1; 970 break; 971 972 #if 0 973 case 26: 974 /* <val> mark <idx> ==> set BuildCharArray[cvi( <idx> )] = <val>, */ 975 /* leave mark on T1 stack */ 976 /* <val> <idx> ==> set BuildCharArray[cvi( <idx> )] = <val> */ 977 XXX which routine has left its mark on the (PostScript) stack?; 978 break; 979 #endif 980 981 case 27: 982 /* <res1> <res2> <val1> <val2> 4 27 callothersubr pop */ 983 /* ==> push <res1> onto T1 stack if <val1> <= <val2>, */ 984 /* otherwise push <res2> */ 985 if ( arg_cnt != 4 ) 986 goto Unexpected_OtherSubr; 987 988 if ( top[2] > top[3] ) 989 top[0] = top[1]; 990 991 known_othersubr_result_cnt = 1; 992 break; 993 994 case 28: 995 /* 0 28 callothersubr pop */ 996 /* => push random value from interval [0, 1) onto stack */ 997 if ( arg_cnt != 0 ) 998 goto Unexpected_OtherSubr; 999 1000 { 1001 FT_Fixed Rand; 1002 1003 1004 Rand = seed; 1005 if ( Rand >= 0x8000L ) 1006 Rand++; 1007 1008 top[0] = Rand; 1009 1010 seed = FT_MulFix( seed, 0x10000L - seed ); 1011 if ( seed == 0 ) 1012 seed += 0x2873; 1013 } 1014 1015 known_othersubr_result_cnt = 1; 1016 break; 1017 1018 default: 1019 FT_ERROR(( "t1_decoder_parse_charstrings:" 1020 " unknown othersubr [%d %d], wish me luck\n", 1021 arg_cnt, subr_no )); 1022 unknown_othersubr_result_cnt = arg_cnt; 1023 break; 1024 1025 Unexpected_OtherSubr: 1026 FT_ERROR(( "t1_decoder_parse_charstrings:" 1027 " invalid othersubr [%d %d]\n", arg_cnt, subr_no )); 1028 goto Syntax_Error; 1029 } 1030 1031 top += known_othersubr_result_cnt; 1032 1033 decoder->top = top; 1034 } 1035 else /* general operator */ 1036 { 1037 FT_Int num_args = t1_args_count[op]; 1038 1039 1040 FT_ASSERT( num_args >= 0 ); 1041 1042 if ( top - decoder->stack < num_args ) 1043 goto Stack_Underflow; 1044 1045 /* XXX Operators usually take their operands from the */ 1046 /* bottom of the stack, i.e., the operands are */ 1047 /* decoder->stack[0], ..., decoder->stack[num_args - 1]; */ 1048 /* only div, callsubr, and callothersubr are different. */ 1049 /* In practice it doesn't matter (?). */ 1050 1051 #ifdef FT_DEBUG_LEVEL_TRACE 1052 1053 switch ( op ) 1054 { 1055 case op_callsubr: 1056 case op_div: 1057 case op_callothersubr: 1058 case op_pop: 1059 case op_return: 1060 break; 1061 1062 default: 1063 if ( top - decoder->stack != num_args ) 1064 FT_TRACE0(( "t1_decoder_parse_charstrings:" 1065 " too much operands on the stack" 1066 " (seen %d, expected %d)\n", 1067 top - decoder->stack, num_args )); 1068 break; 1069 } 1070 1071 #endif /* FT_DEBUG_LEVEL_TRACE */ 1072 1073 top -= num_args; 1074 1075 switch ( op ) 1076 { 1077 case op_endchar: 1078 FT_TRACE4(( " endchar\n" )); 1079 1080 close_contour( builder ); 1081 1082 /* close hints recording session */ 1083 if ( hinter ) 1084 { 1085 if ( hinter->close( hinter->hints, builder->current->n_points ) ) 1086 goto Syntax_Error; 1087 1088 /* apply hints to the loaded glyph outline now */ 1089 hinter->apply( hinter->hints, 1090 builder->current, 1091 (PSH_Globals)builder->hints_globals, 1092 decoder->hint_mode ); 1093 } 1094 1095 /* add current outline to the glyph slot */ 1096 FT_GlyphLoader_Add( builder->loader ); 1097 1098 /* the compiler should optimize away this empty loop but ... */ 1099 1100 #ifdef FT_DEBUG_LEVEL_TRACE 1101 1102 if ( decoder->len_buildchar > 0 ) 1103 { 1104 FT_UInt i; 1105 1106 1107 FT_TRACE4(( "BuildCharArray = [ " )); 1108 1109 for ( i = 0; i < decoder->len_buildchar; ++i ) 1110 FT_TRACE4(( "%d ", decoder->buildchar[ i ] )); 1111 1112 FT_TRACE4(( "]\n" )); 1113 } 1114 1115 #endif /* FT_DEBUG_LEVEL_TRACE */ 1116 1117 FT_TRACE4(( "\n" )); 1118 1119 /* return now! */ 1120 return PSaux_Err_Ok; 1121 1122 case op_hsbw: 1123 FT_TRACE4(( " hsbw" )); 1124 1125 builder->parse_state = T1_Parse_Have_Width; 1126 1127 builder->left_bearing.x += top[0]; 1128 builder->advance.x = top[1]; 1129 builder->advance.y = 0; 1130 1131 orig_x = x = builder->pos_x + top[0]; 1132 orig_y = y = builder->pos_y; 1133 1134 FT_UNUSED( orig_y ); 1135 1136 /* the `metrics_only' indicates that we only want to compute */ 1137 /* the glyph's metrics (lsb + advance width), not load the */ 1138 /* rest of it; so exit immediately */ 1139 if ( builder->metrics_only ) 1140 return PSaux_Err_Ok; 1141 1142 break; 1143 1144 case op_seac: 1145 return t1operator_seac( decoder, 1146 top[0], 1147 top[1], 1148 top[2], 1149 (FT_Int)( top[3] >> 16 ), 1150 (FT_Int)( top[4] >> 16 ) ); 1151 1152 case op_sbw: 1153 FT_TRACE4(( " sbw" )); 1154 1155 builder->parse_state = T1_Parse_Have_Width; 1156 1157 builder->left_bearing.x += top[0]; 1158 builder->left_bearing.y += top[1]; 1159 builder->advance.x = top[2]; 1160 builder->advance.y = top[3]; 1161 1162 x = builder->pos_x + top[0]; 1163 y = builder->pos_y + top[1]; 1164 1165 /* the `metrics_only' indicates that we only want to compute */ 1166 /* the glyph's metrics (lsb + advance width), not load the */ 1167 /* rest of it; so exit immediately */ 1168 if ( builder->metrics_only ) 1169 return PSaux_Err_Ok; 1170 1171 break; 1172 1173 case op_closepath: 1174 FT_TRACE4(( " closepath" )); 1175 1176 /* if there is no path, `closepath' is a no-op */ 1177 if ( builder->parse_state == T1_Parse_Have_Path || 1178 builder->parse_state == T1_Parse_Have_Moveto ) 1179 close_contour( builder ); 1180 1181 builder->parse_state = T1_Parse_Have_Width; 1182 break; 1183 1184 case op_hlineto: 1185 FT_TRACE4(( " hlineto" )); 1186 1187 if ( start_point( builder, x, y ) ) 1188 goto Fail; 1189 1190 x += top[0]; 1191 goto Add_Line; 1192 1193 case op_hmoveto: 1194 FT_TRACE4(( " hmoveto" )); 1195 1196 x += top[0]; 1197 if ( !decoder->flex_state ) 1198 { 1199 if ( builder->parse_state == T1_Parse_Start ) 1200 goto Syntax_Error; 1201 builder->parse_state = T1_Parse_Have_Moveto; 1202 } 1203 break; 1204 1205 case op_hvcurveto: 1206 FT_TRACE4(( " hvcurveto" )); 1207 1208 if ( start_point( builder, x, y ) || 1209 check_points( builder, 3 ) ) 1210 goto Fail; 1211 1212 x += top[0]; 1213 add_point( builder, x, y, 0 ); 1214 x += top[1]; 1215 y += top[2]; 1216 add_point( builder, x, y, 0 ); 1217 y += top[3]; 1218 add_point( builder, x, y, 1 ); 1219 break; 1220 1221 case op_rlineto: 1222 FT_TRACE4(( " rlineto" )); 1223 1224 if ( start_point( builder, x, y ) ) 1225 goto Fail; 1226 1227 x += top[0]; 1228 y += top[1]; 1229 1230 Add_Line: 1231 if ( add_point1( builder, x, y ) ) 1232 goto Fail; 1233 break; 1234 1235 case op_rmoveto: 1236 FT_TRACE4(( " rmoveto" )); 1237 1238 x += top[0]; 1239 y += top[1]; 1240 if ( !decoder->flex_state ) 1241 { 1242 if ( builder->parse_state == T1_Parse_Start ) 1243 goto Syntax_Error; 1244 builder->parse_state = T1_Parse_Have_Moveto; 1245 } 1246 break; 1247 1248 case op_rrcurveto: 1249 FT_TRACE4(( " rrcurveto" )); 1250 1251 if ( start_point( builder, x, y ) || 1252 check_points( builder, 3 ) ) 1253 goto Fail; 1254 1255 x += top[0]; 1256 y += top[1]; 1257 add_point( builder, x, y, 0 ); 1258 1259 x += top[2]; 1260 y += top[3]; 1261 add_point( builder, x, y, 0 ); 1262 1263 x += top[4]; 1264 y += top[5]; 1265 add_point( builder, x, y, 1 ); 1266 break; 1267 1268 case op_vhcurveto: 1269 FT_TRACE4(( " vhcurveto" )); 1270 1271 if ( start_point( builder, x, y ) || 1272 check_points( builder, 3 ) ) 1273 goto Fail; 1274 1275 y += top[0]; 1276 add_point( builder, x, y, 0 ); 1277 x += top[1]; 1278 y += top[2]; 1279 add_point( builder, x, y, 0 ); 1280 x += top[3]; 1281 add_point( builder, x, y, 1 ); 1282 break; 1283 1284 case op_vlineto: 1285 FT_TRACE4(( " vlineto" )); 1286 1287 if ( start_point( builder, x, y ) ) 1288 goto Fail; 1289 1290 y += top[0]; 1291 goto Add_Line; 1292 1293 case op_vmoveto: 1294 FT_TRACE4(( " vmoveto" )); 1295 1296 y += top[0]; 1297 if ( !decoder->flex_state ) 1298 { 1299 if ( builder->parse_state == T1_Parse_Start ) 1300 goto Syntax_Error; 1301 builder->parse_state = T1_Parse_Have_Moveto; 1302 } 1303 break; 1304 1305 case op_div: 1306 FT_TRACE4(( " div" )); 1307 1308 /* if `large_int' is set, we divide unscaled numbers; */ 1309 /* otherwise, we divide numbers in 16.16 format -- */ 1310 /* in both cases, it is the same operation */ 1311 *top = FT_DivFix( top[0], top[1] ); 1312 ++top; 1313 1314 large_int = FALSE; 1315 break; 1316 1317 case op_callsubr: 1318 { 1319 FT_Int idx; 1320 1321 1322 FT_TRACE4(( " callsubr" )); 1323 1324 idx = (FT_Int)( top[0] >> 16 ); 1325 if ( idx < 0 || idx >= (FT_Int)decoder->num_subrs ) 1326 { 1327 FT_ERROR(( "t1_decoder_parse_charstrings:" 1328 " invalid subrs index\n" )); 1329 goto Syntax_Error; 1330 } 1331 1332 if ( zone - decoder->zones >= T1_MAX_SUBRS_CALLS ) 1333 { 1334 FT_ERROR(( "t1_decoder_parse_charstrings:" 1335 " too many nested subrs\n" )); 1336 goto Syntax_Error; 1337 } 1338 1339 zone->cursor = ip; /* save current instruction pointer */ 1340 1341 zone++; 1342 1343 /* The Type 1 driver stores subroutines without the seed bytes. */ 1344 /* The CID driver stores subroutines with seed bytes. This */ 1345 /* case is taken care of when decoder->subrs_len == 0. */ 1346 zone->base = decoder->subrs[idx]; 1347 1348 if ( decoder->subrs_len ) 1349 zone->limit = zone->base + decoder->subrs_len[idx]; 1350 else 1351 { 1352 /* We are using subroutines from a CID font. We must adjust */ 1353 /* for the seed bytes. */ 1354 zone->base += ( decoder->lenIV >= 0 ? decoder->lenIV : 0 ); 1355 zone->limit = decoder->subrs[idx + 1]; 1356 } 1357 1358 zone->cursor = zone->base; 1359 1360 if ( !zone->base ) 1361 { 1362 FT_ERROR(( "t1_decoder_parse_charstrings:" 1363 " invoking empty subrs\n" )); 1364 goto Syntax_Error; 1365 } 1366 1367 decoder->zone = zone; 1368 ip = zone->base; 1369 limit = zone->limit; 1370 break; 1371 } 1372 1373 case op_pop: 1374 FT_TRACE4(( " pop" )); 1375 1376 if ( known_othersubr_result_cnt > 0 ) 1377 { 1378 known_othersubr_result_cnt--; 1379 /* ignore, we pushed the operands ourselves */ 1380 break; 1381 } 1382 1383 if ( unknown_othersubr_result_cnt == 0 ) 1384 { 1385 FT_ERROR(( "t1_decoder_parse_charstrings:" 1386 " no more operands for othersubr\n" )); 1387 goto Syntax_Error; 1388 } 1389 1390 unknown_othersubr_result_cnt--; 1391 top++; /* `push' the operand to callothersubr onto the stack */ 1392 break; 1393 1394 case op_return: 1395 FT_TRACE4(( " return" )); 1396 1397 if ( zone <= decoder->zones ) 1398 { 1399 FT_ERROR(( "t1_decoder_parse_charstrings:" 1400 " unexpected return\n" )); 1401 goto Syntax_Error; 1402 } 1403 1404 zone--; 1405 ip = zone->cursor; 1406 limit = zone->limit; 1407 decoder->zone = zone; 1408 break; 1409 1410 case op_dotsection: 1411 FT_TRACE4(( " dotsection" )); 1412 1413 break; 1414 1415 case op_hstem: 1416 FT_TRACE4(( " hstem" )); 1417 1418 /* record horizontal hint */ 1419 if ( hinter ) 1420 { 1421 /* top[0] += builder->left_bearing.y; */ 1422 hinter->stem( hinter->hints, 1, top ); 1423 } 1424 break; 1425 1426 case op_hstem3: 1427 FT_TRACE4(( " hstem3" )); 1428 1429 /* record horizontal counter-controlled hints */ 1430 if ( hinter ) 1431 hinter->stem3( hinter->hints, 1, top ); 1432 break; 1433 1434 case op_vstem: 1435 FT_TRACE4(( " vstem" )); 1436 1437 /* record vertical hint */ 1438 if ( hinter ) 1439 { 1440 top[0] += orig_x; 1441 hinter->stem( hinter->hints, 0, top ); 1442 } 1443 break; 1444 1445 case op_vstem3: 1446 FT_TRACE4(( " vstem3" )); 1447 1448 /* record vertical counter-controlled hints */ 1449 if ( hinter ) 1450 { 1451 FT_Pos dx = orig_x; 1452 1453 1454 top[0] += dx; 1455 top[2] += dx; 1456 top[4] += dx; 1457 hinter->stem3( hinter->hints, 0, top ); 1458 } 1459 break; 1460 1461 case op_setcurrentpoint: 1462 FT_TRACE4(( " setcurrentpoint" )); 1463 1464 /* From the T1 specification, section 6.4: */ 1465 /* */ 1466 /* The setcurrentpoint command is used only in */ 1467 /* conjunction with results from OtherSubrs procedures. */ 1468 1469 /* known_othersubr_result_cnt != 0 is already handled */ 1470 /* above. */ 1471 1472 /* Note, however, that both Ghostscript and Adobe */ 1473 /* Distiller handle this situation by silently ignoring */ 1474 /* the inappropriate `setcurrentpoint' instruction. So */ 1475 /* we do the same. */ 1476 #if 0 1477 1478 if ( decoder->flex_state != 1 ) 1479 { 1480 FT_ERROR(( "t1_decoder_parse_charstrings:" 1481 " unexpected `setcurrentpoint'\n" )); 1482 goto Syntax_Error; 1483 } 1484 else 1485 ... 1486 #endif 1487 1488 x = top[0]; 1489 y = top[1]; 1490 decoder->flex_state = 0; 1491 break; 1492 1493 case op_unknown15: 1494 FT_TRACE4(( " opcode_15" )); 1495 /* nothing to do except to pop the two arguments */ 1496 break; 1497 1498 default: 1499 FT_ERROR(( "t1_decoder_parse_charstrings:" 1500 " unhandled opcode %d\n", op )); 1501 goto Syntax_Error; 1502 } 1503 1504 /* XXX Operators usually clear the operand stack; */ 1505 /* only div, callsubr, callothersubr, pop, and */ 1506 /* return are different. */ 1507 /* In practice it doesn't matter (?). */ 1508 1509 decoder->top = top; 1510 1511 #ifdef FT_DEBUG_LEVEL_TRACE 1512 FT_TRACE4(( "\n" )); 1513 bol = TRUE; 1514 #endif 1515 1516 } /* general operator processing */ 1517 1518 } /* while ip < limit */ 1519 1520 FT_TRACE4(( "..end..\n\n" )); 1521 1522 Fail: 1523 return error; 1524 1525 Syntax_Error: 1526 return PSaux_Err_Syntax_Error; 1527 1528 Stack_Underflow: 1529 return PSaux_Err_Stack_Underflow; 1530 } 1531 1532 1533 /* parse a single Type 1 glyph */ 1534 FT_LOCAL_DEF( FT_Error ) 1535 t1_decoder_parse_glyph( T1_Decoder decoder, 1536 FT_UInt glyph ) 1537 { 1538 return decoder->parse_callback( decoder, glyph ); 1539 } 1540 1541 1542 /* initialize T1 decoder */ 1543 FT_LOCAL_DEF( FT_Error ) 1544 t1_decoder_init( T1_Decoder decoder, 1545 FT_Face face, 1546 FT_Size size, 1547 FT_GlyphSlot slot, 1548 FT_Byte** glyph_names, 1549 PS_Blend blend, 1550 FT_Bool hinting, 1551 FT_Render_Mode hint_mode, 1552 T1_Decoder_Callback parse_callback ) 1553 { 1554 FT_MEM_ZERO( decoder, sizeof ( *decoder ) ); 1555 1556 /* retrieve PSNames interface from list of current modules */ 1557 { 1558 FT_Service_PsCMaps psnames = 0; 1559 1560 1561 FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS ); 1562 if ( !psnames ) 1563 { 1564 FT_ERROR(( "t1_decoder_init:" 1565 " the `psnames' module is not available\n" )); 1566 return PSaux_Err_Unimplemented_Feature; 1567 } 1568 1569 decoder->psnames = psnames; 1570 } 1571 1572 t1_builder_init( &decoder->builder, face, size, slot, hinting ); 1573 1574 /* decoder->buildchar and decoder->len_buildchar have to be */ 1575 /* initialized by the caller since we cannot know the length */ 1576 /* of the BuildCharArray */ 1577 1578 decoder->num_glyphs = (FT_UInt)face->num_glyphs; 1579 decoder->glyph_names = glyph_names; 1580 decoder->hint_mode = hint_mode; 1581 decoder->blend = blend; 1582 decoder->parse_callback = parse_callback; 1583 1584 decoder->funcs = t1_decoder_funcs; 1585 1586 return PSaux_Err_Ok; 1587 } 1588 1589 1590 /* finalize T1 decoder */ 1591 FT_LOCAL_DEF( void ) 1592 t1_decoder_done( T1_Decoder decoder ) 1593 { 1594 t1_builder_done( &decoder->builder ); 1595 } 1596 1597 1598 /* END */ 1599