1 /***************************************************************************/ 2 /* */ 3 /* psobjs.c */ 4 /* */ 5 /* Auxiliary functions for PostScript fonts (body). */ 6 /* */ 7 /* Copyright 1996-2014 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_POSTSCRIPT_AUX_H 21 #include FT_INTERNAL_DEBUG_H 22 #include FT_INTERNAL_CALC_H 23 24 #include "psobjs.h" 25 #include "psconv.h" 26 27 #include "psauxerr.h" 28 29 30 /*************************************************************************/ 31 /* */ 32 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ 33 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ 34 /* messages during execution. */ 35 /* */ 36 #undef FT_COMPONENT 37 #define FT_COMPONENT trace_psobjs 38 39 40 /*************************************************************************/ 41 /*************************************************************************/ 42 /***** *****/ 43 /***** PS_TABLE *****/ 44 /***** *****/ 45 /*************************************************************************/ 46 /*************************************************************************/ 47 48 /*************************************************************************/ 49 /* */ 50 /* <Function> */ 51 /* ps_table_new */ 52 /* */ 53 /* <Description> */ 54 /* Initializes a PS_Table. */ 55 /* */ 56 /* <InOut> */ 57 /* table :: The address of the target table. */ 58 /* */ 59 /* <Input> */ 60 /* count :: The table size = the maximum number of elements. */ 61 /* */ 62 /* memory :: The memory object to use for all subsequent */ 63 /* reallocations. */ 64 /* */ 65 /* <Return> */ 66 /* FreeType error code. 0 means success. */ 67 /* */ 68 FT_LOCAL_DEF( FT_Error ) 69 ps_table_new( PS_Table table, 70 FT_Int count, 71 FT_Memory memory ) 72 { 73 FT_Error error; 74 75 76 table->memory = memory; 77 if ( FT_NEW_ARRAY( table->elements, count ) || 78 FT_NEW_ARRAY( table->lengths, count ) ) 79 goto Exit; 80 81 table->max_elems = count; 82 table->init = 0xDEADBEEFUL; 83 table->num_elems = 0; 84 table->block = 0; 85 table->capacity = 0; 86 table->cursor = 0; 87 88 *(PS_Table_FuncsRec*)&table->funcs = ps_table_funcs; 89 90 Exit: 91 if ( error ) 92 FT_FREE( table->elements ); 93 94 return error; 95 } 96 97 98 static void 99 shift_elements( PS_Table table, 100 FT_Byte* old_base ) 101 { 102 FT_PtrDist delta = table->block - old_base; 103 FT_Byte** offset = table->elements; 104 FT_Byte** limit = offset + table->max_elems; 105 106 107 for ( ; offset < limit; offset++ ) 108 { 109 if ( offset[0] ) 110 offset[0] += delta; 111 } 112 } 113 114 115 static FT_Error 116 reallocate_t1_table( PS_Table table, 117 FT_Offset new_size ) 118 { 119 FT_Memory memory = table->memory; 120 FT_Byte* old_base = table->block; 121 FT_Error error; 122 123 124 /* allocate new base block */ 125 if ( FT_ALLOC( table->block, new_size ) ) 126 { 127 table->block = old_base; 128 return error; 129 } 130 131 /* copy elements and shift offsets */ 132 if ( old_base ) 133 { 134 FT_MEM_COPY( table->block, old_base, table->capacity ); 135 shift_elements( table, old_base ); 136 FT_FREE( old_base ); 137 } 138 139 table->capacity = new_size; 140 141 return FT_Err_Ok; 142 } 143 144 145 /*************************************************************************/ 146 /* */ 147 /* <Function> */ 148 /* ps_table_add */ 149 /* */ 150 /* <Description> */ 151 /* Adds an object to a PS_Table, possibly growing its memory block. */ 152 /* */ 153 /* <InOut> */ 154 /* table :: The target table. */ 155 /* */ 156 /* <Input> */ 157 /* idx :: The index of the object in the table. */ 158 /* */ 159 /* object :: The address of the object to copy in memory. */ 160 /* */ 161 /* length :: The length in bytes of the source object. */ 162 /* */ 163 /* <Return> */ 164 /* FreeType error code. 0 means success. An error is returned if a */ 165 /* reallocation fails. */ 166 /* */ 167 FT_LOCAL_DEF( FT_Error ) 168 ps_table_add( PS_Table table, 169 FT_Int idx, 170 void* object, 171 FT_PtrDist length ) 172 { 173 if ( idx < 0 || idx >= table->max_elems ) 174 { 175 FT_ERROR(( "ps_table_add: invalid index\n" )); 176 return FT_THROW( Invalid_Argument ); 177 } 178 179 if ( length < 0 ) 180 { 181 FT_ERROR(( "ps_table_add: invalid length\n" )); 182 return FT_THROW( Invalid_Argument ); 183 } 184 185 /* grow the base block if needed */ 186 if ( table->cursor + length > table->capacity ) 187 { 188 FT_Error error; 189 FT_Offset new_size = table->capacity; 190 FT_PtrDist in_offset; 191 192 193 in_offset = (FT_Byte*)object - table->block; 194 if ( in_offset < 0 || (FT_Offset)in_offset >= table->capacity ) 195 in_offset = -1; 196 197 while ( new_size < table->cursor + length ) 198 { 199 /* increase size by 25% and round up to the nearest multiple 200 of 1024 */ 201 new_size += ( new_size >> 2 ) + 1; 202 new_size = FT_PAD_CEIL( new_size, 1024 ); 203 } 204 205 error = reallocate_t1_table( table, new_size ); 206 if ( error ) 207 return error; 208 209 if ( in_offset >= 0 ) 210 object = table->block + in_offset; 211 } 212 213 /* add the object to the base block and adjust offset */ 214 table->elements[idx] = table->block + table->cursor; 215 table->lengths [idx] = length; 216 FT_MEM_COPY( table->block + table->cursor, object, length ); 217 218 table->cursor += length; 219 return FT_Err_Ok; 220 } 221 222 223 /*************************************************************************/ 224 /* */ 225 /* <Function> */ 226 /* ps_table_done */ 227 /* */ 228 /* <Description> */ 229 /* Finalizes a PS_TableRec (i.e., reallocate it to its current */ 230 /* cursor). */ 231 /* */ 232 /* <InOut> */ 233 /* table :: The target table. */ 234 /* */ 235 /* <Note> */ 236 /* This function does NOT release the heap's memory block. It is up */ 237 /* to the caller to clean it, or reference it in its own structures. */ 238 /* */ 239 FT_LOCAL_DEF( void ) 240 ps_table_done( PS_Table table ) 241 { 242 FT_Memory memory = table->memory; 243 FT_Error error; 244 FT_Byte* old_base = table->block; 245 246 247 /* should never fail, because rec.cursor <= rec.size */ 248 if ( !old_base ) 249 return; 250 251 if ( FT_ALLOC( table->block, table->cursor ) ) 252 return; 253 FT_MEM_COPY( table->block, old_base, table->cursor ); 254 shift_elements( table, old_base ); 255 256 table->capacity = table->cursor; 257 FT_FREE( old_base ); 258 259 FT_UNUSED( error ); 260 } 261 262 263 FT_LOCAL_DEF( void ) 264 ps_table_release( PS_Table table ) 265 { 266 FT_Memory memory = table->memory; 267 268 269 if ( (FT_ULong)table->init == 0xDEADBEEFUL ) 270 { 271 FT_FREE( table->block ); 272 FT_FREE( table->elements ); 273 FT_FREE( table->lengths ); 274 table->init = 0; 275 } 276 } 277 278 279 /*************************************************************************/ 280 /*************************************************************************/ 281 /***** *****/ 282 /***** T1 PARSER *****/ 283 /***** *****/ 284 /*************************************************************************/ 285 /*************************************************************************/ 286 287 288 /* first character must be already part of the comment */ 289 290 static void 291 skip_comment( FT_Byte* *acur, 292 FT_Byte* limit ) 293 { 294 FT_Byte* cur = *acur; 295 296 297 while ( cur < limit ) 298 { 299 if ( IS_PS_NEWLINE( *cur ) ) 300 break; 301 cur++; 302 } 303 304 *acur = cur; 305 } 306 307 308 static void 309 skip_spaces( FT_Byte* *acur, 310 FT_Byte* limit ) 311 { 312 FT_Byte* cur = *acur; 313 314 315 while ( cur < limit ) 316 { 317 if ( !IS_PS_SPACE( *cur ) ) 318 { 319 if ( *cur == '%' ) 320 /* According to the PLRM, a comment is equal to a space. */ 321 skip_comment( &cur, limit ); 322 else 323 break; 324 } 325 cur++; 326 } 327 328 *acur = cur; 329 } 330 331 332 #define IS_OCTAL_DIGIT( c ) ( '0' <= (c) && (c) <= '7' ) 333 334 335 /* first character must be `('; */ 336 /* *acur is positioned at the character after the closing `)' */ 337 338 static FT_Error 339 skip_literal_string( FT_Byte* *acur, 340 FT_Byte* limit ) 341 { 342 FT_Byte* cur = *acur; 343 FT_Int embed = 0; 344 FT_Error error = FT_ERR( Invalid_File_Format ); 345 unsigned int i; 346 347 348 while ( cur < limit ) 349 { 350 FT_Byte c = *cur; 351 352 353 ++cur; 354 355 if ( c == '\\' ) 356 { 357 /* Red Book 3rd ed., section `Literal Text Strings', p. 29: */ 358 /* A backslash can introduce three different types */ 359 /* of escape sequences: */ 360 /* - a special escaped char like \r, \n, etc. */ 361 /* - a one-, two-, or three-digit octal number */ 362 /* - none of the above in which case the backslash is ignored */ 363 364 if ( cur == limit ) 365 /* error (or to be ignored?) */ 366 break; 367 368 switch ( *cur ) 369 { 370 /* skip `special' escape */ 371 case 'n': 372 case 'r': 373 case 't': 374 case 'b': 375 case 'f': 376 case '\\': 377 case '(': 378 case ')': 379 ++cur; 380 break; 381 382 default: 383 /* skip octal escape or ignore backslash */ 384 for ( i = 0; i < 3 && cur < limit; ++i ) 385 { 386 if ( !IS_OCTAL_DIGIT( *cur ) ) 387 break; 388 389 ++cur; 390 } 391 } 392 } 393 else if ( c == '(' ) 394 embed++; 395 else if ( c == ')' ) 396 { 397 embed--; 398 if ( embed == 0 ) 399 { 400 error = FT_Err_Ok; 401 break; 402 } 403 } 404 } 405 406 *acur = cur; 407 408 return error; 409 } 410 411 412 /* first character must be `<' */ 413 414 static FT_Error 415 skip_string( FT_Byte* *acur, 416 FT_Byte* limit ) 417 { 418 FT_Byte* cur = *acur; 419 FT_Error err = FT_Err_Ok; 420 421 422 while ( ++cur < limit ) 423 { 424 /* All whitespace characters are ignored. */ 425 skip_spaces( &cur, limit ); 426 if ( cur >= limit ) 427 break; 428 429 if ( !IS_PS_XDIGIT( *cur ) ) 430 break; 431 } 432 433 if ( cur < limit && *cur != '>' ) 434 { 435 FT_ERROR(( "skip_string: missing closing delimiter `>'\n" )); 436 err = FT_THROW( Invalid_File_Format ); 437 } 438 else 439 cur++; 440 441 *acur = cur; 442 return err; 443 } 444 445 446 /* first character must be the opening brace that */ 447 /* starts the procedure */ 448 449 /* NB: [ and ] need not match: */ 450 /* `/foo {[} def' is a valid PostScript fragment, */ 451 /* even within a Type1 font */ 452 453 static FT_Error 454 skip_procedure( FT_Byte* *acur, 455 FT_Byte* limit ) 456 { 457 FT_Byte* cur; 458 FT_Int embed = 0; 459 FT_Error error = FT_Err_Ok; 460 461 462 FT_ASSERT( **acur == '{' ); 463 464 for ( cur = *acur; cur < limit && error == FT_Err_Ok; ++cur ) 465 { 466 switch ( *cur ) 467 { 468 case '{': 469 ++embed; 470 break; 471 472 case '}': 473 --embed; 474 if ( embed == 0 ) 475 { 476 ++cur; 477 goto end; 478 } 479 break; 480 481 case '(': 482 error = skip_literal_string( &cur, limit ); 483 break; 484 485 case '<': 486 error = skip_string( &cur, limit ); 487 break; 488 489 case '%': 490 skip_comment( &cur, limit ); 491 break; 492 } 493 } 494 495 end: 496 if ( embed != 0 ) 497 error = FT_THROW( Invalid_File_Format ); 498 499 *acur = cur; 500 501 return error; 502 } 503 504 505 /***********************************************************************/ 506 /* */ 507 /* All exported parsing routines handle leading whitespace and stop at */ 508 /* the first character which isn't part of the just handled token. */ 509 /* */ 510 /***********************************************************************/ 511 512 513 FT_LOCAL_DEF( void ) 514 ps_parser_skip_PS_token( PS_Parser parser ) 515 { 516 /* Note: PostScript allows any non-delimiting, non-whitespace */ 517 /* character in a name (PS Ref Manual, 3rd ed, p31). */ 518 /* PostScript delimiters are (, ), <, >, [, ], {, }, /, and %. */ 519 520 FT_Byte* cur = parser->cursor; 521 FT_Byte* limit = parser->limit; 522 FT_Error error = FT_Err_Ok; 523 524 525 skip_spaces( &cur, limit ); /* this also skips comments */ 526 if ( cur >= limit ) 527 goto Exit; 528 529 /* self-delimiting, single-character tokens */ 530 if ( *cur == '[' || *cur == ']' ) 531 { 532 cur++; 533 goto Exit; 534 } 535 536 /* skip balanced expressions (procedures and strings) */ 537 538 if ( *cur == '{' ) /* {...} */ 539 { 540 error = skip_procedure( &cur, limit ); 541 goto Exit; 542 } 543 544 if ( *cur == '(' ) /* (...) */ 545 { 546 error = skip_literal_string( &cur, limit ); 547 goto Exit; 548 } 549 550 if ( *cur == '<' ) /* <...> */ 551 { 552 if ( cur + 1 < limit && *(cur + 1) == '<' ) /* << */ 553 { 554 cur++; 555 cur++; 556 } 557 else 558 error = skip_string( &cur, limit ); 559 560 goto Exit; 561 } 562 563 if ( *cur == '>' ) 564 { 565 cur++; 566 if ( cur >= limit || *cur != '>' ) /* >> */ 567 { 568 FT_ERROR(( "ps_parser_skip_PS_token:" 569 " unexpected closing delimiter `>'\n" )); 570 error = FT_THROW( Invalid_File_Format ); 571 goto Exit; 572 } 573 cur++; 574 goto Exit; 575 } 576 577 if ( *cur == '/' ) 578 cur++; 579 580 /* anything else */ 581 while ( cur < limit ) 582 { 583 /* *cur might be invalid (e.g., ')' or '}'), but this */ 584 /* is handled by the test `cur == parser->cursor' below */ 585 if ( IS_PS_DELIM( *cur ) ) 586 break; 587 588 cur++; 589 } 590 591 Exit: 592 if ( cur < limit && cur == parser->cursor ) 593 { 594 FT_ERROR(( "ps_parser_skip_PS_token:" 595 " current token is `%c' which is self-delimiting\n" 596 " " 597 " but invalid at this point\n", 598 *cur )); 599 600 error = FT_THROW( Invalid_File_Format ); 601 } 602 603 parser->error = error; 604 parser->cursor = cur; 605 } 606 607 608 FT_LOCAL_DEF( void ) 609 ps_parser_skip_spaces( PS_Parser parser ) 610 { 611 skip_spaces( &parser->cursor, parser->limit ); 612 } 613 614 615 /* `token' here means either something between balanced delimiters */ 616 /* or the next token; the delimiters are not removed. */ 617 618 FT_LOCAL_DEF( void ) 619 ps_parser_to_token( PS_Parser parser, 620 T1_Token token ) 621 { 622 FT_Byte* cur; 623 FT_Byte* limit; 624 FT_Int embed; 625 626 627 token->type = T1_TOKEN_TYPE_NONE; 628 token->start = 0; 629 token->limit = 0; 630 631 /* first of all, skip leading whitespace */ 632 ps_parser_skip_spaces( parser ); 633 634 cur = parser->cursor; 635 limit = parser->limit; 636 637 if ( cur >= limit ) 638 return; 639 640 switch ( *cur ) 641 { 642 /************* check for literal string *****************/ 643 case '(': 644 token->type = T1_TOKEN_TYPE_STRING; 645 token->start = cur; 646 647 if ( skip_literal_string( &cur, limit ) == FT_Err_Ok ) 648 token->limit = cur; 649 break; 650 651 /************* check for programs/array *****************/ 652 case '{': 653 token->type = T1_TOKEN_TYPE_ARRAY; 654 token->start = cur; 655 656 if ( skip_procedure( &cur, limit ) == FT_Err_Ok ) 657 token->limit = cur; 658 break; 659 660 /************* check for table/array ********************/ 661 /* XXX: in theory we should also look for "<<" */ 662 /* since this is semantically equivalent to "["; */ 663 /* in practice it doesn't matter (?) */ 664 case '[': 665 token->type = T1_TOKEN_TYPE_ARRAY; 666 embed = 1; 667 token->start = cur++; 668 669 /* we need this to catch `[ ]' */ 670 parser->cursor = cur; 671 ps_parser_skip_spaces( parser ); 672 cur = parser->cursor; 673 674 while ( cur < limit && !parser->error ) 675 { 676 /* XXX: this is wrong because it does not */ 677 /* skip comments, procedures, and strings */ 678 if ( *cur == '[' ) 679 embed++; 680 else if ( *cur == ']' ) 681 { 682 embed--; 683 if ( embed <= 0 ) 684 { 685 token->limit = ++cur; 686 break; 687 } 688 } 689 690 parser->cursor = cur; 691 ps_parser_skip_PS_token( parser ); 692 /* we need this to catch `[XXX ]' */ 693 ps_parser_skip_spaces ( parser ); 694 cur = parser->cursor; 695 } 696 break; 697 698 /* ************ otherwise, it is any token **************/ 699 default: 700 token->start = cur; 701 token->type = ( *cur == '/' ? T1_TOKEN_TYPE_KEY : T1_TOKEN_TYPE_ANY ); 702 ps_parser_skip_PS_token( parser ); 703 cur = parser->cursor; 704 if ( !parser->error ) 705 token->limit = cur; 706 } 707 708 if ( !token->limit ) 709 { 710 token->start = 0; 711 token->type = T1_TOKEN_TYPE_NONE; 712 } 713 714 parser->cursor = cur; 715 } 716 717 718 /* NB: `tokens' can be NULL if we only want to count */ 719 /* the number of array elements */ 720 721 FT_LOCAL_DEF( void ) 722 ps_parser_to_token_array( PS_Parser parser, 723 T1_Token tokens, 724 FT_UInt max_tokens, 725 FT_Int* pnum_tokens ) 726 { 727 T1_TokenRec master; 728 729 730 *pnum_tokens = -1; 731 732 /* this also handles leading whitespace */ 733 ps_parser_to_token( parser, &master ); 734 735 if ( master.type == T1_TOKEN_TYPE_ARRAY ) 736 { 737 FT_Byte* old_cursor = parser->cursor; 738 FT_Byte* old_limit = parser->limit; 739 T1_Token cur = tokens; 740 T1_Token limit = cur + max_tokens; 741 742 743 /* don't include outermost delimiters */ 744 parser->cursor = master.start + 1; 745 parser->limit = master.limit - 1; 746 747 while ( parser->cursor < parser->limit ) 748 { 749 T1_TokenRec token; 750 751 752 ps_parser_to_token( parser, &token ); 753 if ( !token.type ) 754 break; 755 756 if ( tokens != NULL && cur < limit ) 757 *cur = token; 758 759 cur++; 760 } 761 762 *pnum_tokens = (FT_Int)( cur - tokens ); 763 764 parser->cursor = old_cursor; 765 parser->limit = old_limit; 766 } 767 } 768 769 770 /* first character must be a delimiter or a part of a number */ 771 /* NB: `coords' can be NULL if we just want to skip the */ 772 /* array; in this case we ignore `max_coords' */ 773 774 static FT_Int 775 ps_tocoordarray( FT_Byte* *acur, 776 FT_Byte* limit, 777 FT_Int max_coords, 778 FT_Short* coords ) 779 { 780 FT_Byte* cur = *acur; 781 FT_Int count = 0; 782 FT_Byte c, ender; 783 784 785 if ( cur >= limit ) 786 goto Exit; 787 788 /* check for the beginning of an array; otherwise, only one number */ 789 /* will be read */ 790 c = *cur; 791 ender = 0; 792 793 if ( c == '[' ) 794 ender = ']'; 795 else if ( c == '{' ) 796 ender = '}'; 797 798 if ( ender ) 799 cur++; 800 801 /* now, read the coordinates */ 802 while ( cur < limit ) 803 { 804 FT_Short dummy; 805 FT_Byte* old_cur; 806 807 808 /* skip whitespace in front of data */ 809 skip_spaces( &cur, limit ); 810 if ( cur >= limit ) 811 goto Exit; 812 813 if ( *cur == ender ) 814 { 815 cur++; 816 break; 817 } 818 819 old_cur = cur; 820 821 if ( coords != NULL && count >= max_coords ) 822 break; 823 824 /* call PS_Conv_ToFixed() even if coords == NULL */ 825 /* to properly parse number at `cur' */ 826 *( coords != NULL ? &coords[count] : &dummy ) = 827 (FT_Short)( PS_Conv_ToFixed( &cur, limit, 0 ) >> 16 ); 828 829 if ( old_cur == cur ) 830 { 831 count = -1; 832 goto Exit; 833 } 834 else 835 count++; 836 837 if ( !ender ) 838 break; 839 } 840 841 Exit: 842 *acur = cur; 843 return count; 844 } 845 846 847 /* first character must be a delimiter or a part of a number */ 848 /* NB: `values' can be NULL if we just want to skip the */ 849 /* array; in this case we ignore `max_values' */ 850 /* */ 851 /* return number of successfully parsed values */ 852 853 static FT_Int 854 ps_tofixedarray( FT_Byte* *acur, 855 FT_Byte* limit, 856 FT_Int max_values, 857 FT_Fixed* values, 858 FT_Int power_ten ) 859 { 860 FT_Byte* cur = *acur; 861 FT_Int count = 0; 862 FT_Byte c, ender; 863 864 865 if ( cur >= limit ) 866 goto Exit; 867 868 /* Check for the beginning of an array. Otherwise, only one number */ 869 /* will be read. */ 870 c = *cur; 871 ender = 0; 872 873 if ( c == '[' ) 874 ender = ']'; 875 else if ( c == '{' ) 876 ender = '}'; 877 878 if ( ender ) 879 cur++; 880 881 /* now, read the values */ 882 while ( cur < limit ) 883 { 884 FT_Fixed dummy; 885 FT_Byte* old_cur; 886 887 888 /* skip whitespace in front of data */ 889 skip_spaces( &cur, limit ); 890 if ( cur >= limit ) 891 goto Exit; 892 893 if ( *cur == ender ) 894 { 895 cur++; 896 break; 897 } 898 899 old_cur = cur; 900 901 if ( values != NULL && count >= max_values ) 902 break; 903 904 /* call PS_Conv_ToFixed() even if coords == NULL */ 905 /* to properly parse number at `cur' */ 906 *( values != NULL ? &values[count] : &dummy ) = 907 PS_Conv_ToFixed( &cur, limit, power_ten ); 908 909 if ( old_cur == cur ) 910 { 911 count = -1; 912 goto Exit; 913 } 914 else 915 count++; 916 917 if ( !ender ) 918 break; 919 } 920 921 Exit: 922 *acur = cur; 923 return count; 924 } 925 926 927 #if 0 928 929 static FT_String* 930 ps_tostring( FT_Byte** cursor, 931 FT_Byte* limit, 932 FT_Memory memory ) 933 { 934 FT_Byte* cur = *cursor; 935 FT_PtrDist len = 0; 936 FT_Int count; 937 FT_String* result; 938 FT_Error error; 939 940 941 /* XXX: some stupid fonts have a `Notice' or `Copyright' string */ 942 /* that simply doesn't begin with an opening parenthesis, even */ 943 /* though they have a closing one! E.g. "amuncial.pfb" */ 944 /* */ 945 /* We must deal with these ill-fated cases there. Note that */ 946 /* these fonts didn't work with the old Type 1 driver as the */ 947 /* notice/copyright was not recognized as a valid string token */ 948 /* and made the old token parser commit errors. */ 949 950 while ( cur < limit && ( *cur == ' ' || *cur == '\t' ) ) 951 cur++; 952 if ( cur + 1 >= limit ) 953 return 0; 954 955 if ( *cur == '(' ) 956 cur++; /* skip the opening parenthesis, if there is one */ 957 958 *cursor = cur; 959 count = 0; 960 961 /* then, count its length */ 962 for ( ; cur < limit; cur++ ) 963 { 964 if ( *cur == '(' ) 965 count++; 966 967 else if ( *cur == ')' ) 968 { 969 count--; 970 if ( count < 0 ) 971 break; 972 } 973 } 974 975 len = cur - *cursor; 976 if ( cur >= limit || FT_ALLOC( result, len + 1 ) ) 977 return 0; 978 979 /* now copy the string */ 980 FT_MEM_COPY( result, *cursor, len ); 981 result[len] = '\0'; 982 *cursor = cur; 983 return result; 984 } 985 986 #endif /* 0 */ 987 988 989 static int 990 ps_tobool( FT_Byte* *acur, 991 FT_Byte* limit ) 992 { 993 FT_Byte* cur = *acur; 994 FT_Bool result = 0; 995 996 997 /* return 1 if we find `true', 0 otherwise */ 998 if ( cur + 3 < limit && 999 cur[0] == 't' && 1000 cur[1] == 'r' && 1001 cur[2] == 'u' && 1002 cur[3] == 'e' ) 1003 { 1004 result = 1; 1005 cur += 5; 1006 } 1007 else if ( cur + 4 < limit && 1008 cur[0] == 'f' && 1009 cur[1] == 'a' && 1010 cur[2] == 'l' && 1011 cur[3] == 's' && 1012 cur[4] == 'e' ) 1013 { 1014 result = 0; 1015 cur += 6; 1016 } 1017 1018 *acur = cur; 1019 return result; 1020 } 1021 1022 1023 /* load a simple field (i.e. non-table) into the current list of objects */ 1024 1025 FT_LOCAL_DEF( FT_Error ) 1026 ps_parser_load_field( PS_Parser parser, 1027 const T1_Field field, 1028 void** objects, 1029 FT_UInt max_objects, 1030 FT_ULong* pflags ) 1031 { 1032 T1_TokenRec token; 1033 FT_Byte* cur; 1034 FT_Byte* limit; 1035 FT_UInt count; 1036 FT_UInt idx; 1037 FT_Error error; 1038 T1_FieldType type; 1039 1040 1041 /* this also skips leading whitespace */ 1042 ps_parser_to_token( parser, &token ); 1043 if ( !token.type ) 1044 goto Fail; 1045 1046 count = 1; 1047 idx = 0; 1048 cur = token.start; 1049 limit = token.limit; 1050 1051 type = field->type; 1052 1053 /* we must detect arrays in /FontBBox */ 1054 if ( type == T1_FIELD_TYPE_BBOX ) 1055 { 1056 T1_TokenRec token2; 1057 FT_Byte* old_cur = parser->cursor; 1058 FT_Byte* old_limit = parser->limit; 1059 1060 1061 /* don't include delimiters */ 1062 parser->cursor = token.start + 1; 1063 parser->limit = token.limit - 1; 1064 1065 ps_parser_to_token( parser, &token2 ); 1066 parser->cursor = old_cur; 1067 parser->limit = old_limit; 1068 1069 if ( token2.type == T1_TOKEN_TYPE_ARRAY ) 1070 { 1071 type = T1_FIELD_TYPE_MM_BBOX; 1072 goto FieldArray; 1073 } 1074 } 1075 else if ( token.type == T1_TOKEN_TYPE_ARRAY ) 1076 { 1077 count = max_objects; 1078 1079 FieldArray: 1080 /* if this is an array and we have no blend, an error occurs */ 1081 if ( max_objects == 0 ) 1082 goto Fail; 1083 1084 idx = 1; 1085 1086 /* don't include delimiters */ 1087 cur++; 1088 limit--; 1089 } 1090 1091 for ( ; count > 0; count--, idx++ ) 1092 { 1093 FT_Byte* q = (FT_Byte*)objects[idx] + field->offset; 1094 FT_Long val; 1095 FT_String* string; 1096 1097 1098 skip_spaces( &cur, limit ); 1099 1100 switch ( type ) 1101 { 1102 case T1_FIELD_TYPE_BOOL: 1103 val = ps_tobool( &cur, limit ); 1104 goto Store_Integer; 1105 1106 case T1_FIELD_TYPE_FIXED: 1107 val = PS_Conv_ToFixed( &cur, limit, 0 ); 1108 goto Store_Integer; 1109 1110 case T1_FIELD_TYPE_FIXED_1000: 1111 val = PS_Conv_ToFixed( &cur, limit, 3 ); 1112 goto Store_Integer; 1113 1114 case T1_FIELD_TYPE_INTEGER: 1115 val = PS_Conv_ToInt( &cur, limit ); 1116 /* fall through */ 1117 1118 Store_Integer: 1119 switch ( field->size ) 1120 { 1121 case (8 / FT_CHAR_BIT): 1122 *(FT_Byte*)q = (FT_Byte)val; 1123 break; 1124 1125 case (16 / FT_CHAR_BIT): 1126 *(FT_UShort*)q = (FT_UShort)val; 1127 break; 1128 1129 case (32 / FT_CHAR_BIT): 1130 *(FT_UInt32*)q = (FT_UInt32)val; 1131 break; 1132 1133 default: /* for 64-bit systems */ 1134 *(FT_Long*)q = val; 1135 } 1136 break; 1137 1138 case T1_FIELD_TYPE_STRING: 1139 case T1_FIELD_TYPE_KEY: 1140 { 1141 FT_Memory memory = parser->memory; 1142 FT_UInt len = (FT_UInt)( limit - cur ); 1143 1144 1145 if ( cur >= limit ) 1146 break; 1147 1148 /* we allow both a string or a name */ 1149 /* for cases like /FontName (foo) def */ 1150 if ( token.type == T1_TOKEN_TYPE_KEY ) 1151 { 1152 /* don't include leading `/' */ 1153 len--; 1154 cur++; 1155 } 1156 else if ( token.type == T1_TOKEN_TYPE_STRING ) 1157 { 1158 /* don't include delimiting parentheses */ 1159 /* XXX we don't handle <<...>> here */ 1160 /* XXX should we convert octal escapes? */ 1161 /* if so, what encoding should we use? */ 1162 cur++; 1163 len -= 2; 1164 } 1165 else 1166 { 1167 FT_ERROR(( "ps_parser_load_field:" 1168 " expected a name or string\n" 1169 " " 1170 " but found token of type %d instead\n", 1171 token.type )); 1172 error = FT_THROW( Invalid_File_Format ); 1173 goto Exit; 1174 } 1175 1176 /* for this to work (FT_String**)q must have been */ 1177 /* initialized to NULL */ 1178 if ( *(FT_String**)q != NULL ) 1179 { 1180 FT_TRACE0(( "ps_parser_load_field: overwriting field %s\n", 1181 field->ident )); 1182 FT_FREE( *(FT_String**)q ); 1183 *(FT_String**)q = NULL; 1184 } 1185 1186 if ( FT_ALLOC( string, len + 1 ) ) 1187 goto Exit; 1188 1189 FT_MEM_COPY( string, cur, len ); 1190 string[len] = 0; 1191 1192 *(FT_String**)q = string; 1193 } 1194 break; 1195 1196 case T1_FIELD_TYPE_BBOX: 1197 { 1198 FT_Fixed temp[4]; 1199 FT_BBox* bbox = (FT_BBox*)q; 1200 FT_Int result; 1201 1202 1203 result = ps_tofixedarray( &cur, limit, 4, temp, 0 ); 1204 1205 if ( result < 4 ) 1206 { 1207 FT_ERROR(( "ps_parser_load_field:" 1208 " expected four integers in bounding box\n" )); 1209 error = FT_THROW( Invalid_File_Format ); 1210 goto Exit; 1211 } 1212 1213 bbox->xMin = FT_RoundFix( temp[0] ); 1214 bbox->yMin = FT_RoundFix( temp[1] ); 1215 bbox->xMax = FT_RoundFix( temp[2] ); 1216 bbox->yMax = FT_RoundFix( temp[3] ); 1217 } 1218 break; 1219 1220 case T1_FIELD_TYPE_MM_BBOX: 1221 { 1222 FT_Memory memory = parser->memory; 1223 FT_Fixed* temp; 1224 FT_Int result; 1225 FT_UInt i; 1226 1227 1228 if ( FT_NEW_ARRAY( temp, max_objects * 4 ) ) 1229 goto Exit; 1230 1231 for ( i = 0; i < 4; i++ ) 1232 { 1233 result = ps_tofixedarray( &cur, limit, max_objects, 1234 temp + i * max_objects, 0 ); 1235 if ( result < 0 || (FT_UInt)result < max_objects ) 1236 { 1237 FT_ERROR(( "ps_parser_load_field:" 1238 " expected %d integers in the %s subarray\n" 1239 " " 1240 " of /FontBBox in the /Blend dictionary\n", 1241 max_objects, 1242 i == 0 ? "first" 1243 : ( i == 1 ? "second" 1244 : ( i == 2 ? "third" 1245 : "fourth" ) ) )); 1246 error = FT_THROW( Invalid_File_Format ); 1247 goto Exit; 1248 } 1249 1250 skip_spaces( &cur, limit ); 1251 } 1252 1253 for ( i = 0; i < max_objects; i++ ) 1254 { 1255 FT_BBox* bbox = (FT_BBox*)objects[i]; 1256 1257 1258 bbox->xMin = FT_RoundFix( temp[i ] ); 1259 bbox->yMin = FT_RoundFix( temp[i + max_objects] ); 1260 bbox->xMax = FT_RoundFix( temp[i + 2 * max_objects] ); 1261 bbox->yMax = FT_RoundFix( temp[i + 3 * max_objects] ); 1262 } 1263 1264 FT_FREE( temp ); 1265 } 1266 break; 1267 1268 default: 1269 /* an error occurred */ 1270 goto Fail; 1271 } 1272 } 1273 1274 #if 0 /* obsolete -- keep for reference */ 1275 if ( pflags ) 1276 *pflags |= 1L << field->flag_bit; 1277 #else 1278 FT_UNUSED( pflags ); 1279 #endif 1280 1281 error = FT_Err_Ok; 1282 1283 Exit: 1284 return error; 1285 1286 Fail: 1287 error = FT_THROW( Invalid_File_Format ); 1288 goto Exit; 1289 } 1290 1291 1292 #define T1_MAX_TABLE_ELEMENTS 32 1293 1294 1295 FT_LOCAL_DEF( FT_Error ) 1296 ps_parser_load_field_table( PS_Parser parser, 1297 const T1_Field field, 1298 void** objects, 1299 FT_UInt max_objects, 1300 FT_ULong* pflags ) 1301 { 1302 T1_TokenRec elements[T1_MAX_TABLE_ELEMENTS]; 1303 T1_Token token; 1304 FT_Int num_elements; 1305 FT_Error error = FT_Err_Ok; 1306 FT_Byte* old_cursor; 1307 FT_Byte* old_limit; 1308 T1_FieldRec fieldrec = *(T1_Field)field; 1309 1310 1311 fieldrec.type = T1_FIELD_TYPE_INTEGER; 1312 if ( field->type == T1_FIELD_TYPE_FIXED_ARRAY || 1313 field->type == T1_FIELD_TYPE_BBOX ) 1314 fieldrec.type = T1_FIELD_TYPE_FIXED; 1315 1316 ps_parser_to_token_array( parser, elements, 1317 T1_MAX_TABLE_ELEMENTS, &num_elements ); 1318 if ( num_elements < 0 ) 1319 { 1320 error = FT_ERR( Ignore ); 1321 goto Exit; 1322 } 1323 if ( (FT_UInt)num_elements > field->array_max ) 1324 num_elements = field->array_max; 1325 1326 old_cursor = parser->cursor; 1327 old_limit = parser->limit; 1328 1329 /* we store the elements count if necessary; */ 1330 /* we further assume that `count_offset' can't be zero */ 1331 if ( field->type != T1_FIELD_TYPE_BBOX && field->count_offset != 0 ) 1332 *(FT_Byte*)( (FT_Byte*)objects[0] + field->count_offset ) = 1333 (FT_Byte)num_elements; 1334 1335 /* we now load each element, adjusting the field.offset on each one */ 1336 token = elements; 1337 for ( ; num_elements > 0; num_elements--, token++ ) 1338 { 1339 parser->cursor = token->start; 1340 parser->limit = token->limit; 1341 ps_parser_load_field( parser, &fieldrec, objects, max_objects, 0 ); 1342 fieldrec.offset += fieldrec.size; 1343 } 1344 1345 #if 0 /* obsolete -- keep for reference */ 1346 if ( pflags ) 1347 *pflags |= 1L << field->flag_bit; 1348 #else 1349 FT_UNUSED( pflags ); 1350 #endif 1351 1352 parser->cursor = old_cursor; 1353 parser->limit = old_limit; 1354 1355 Exit: 1356 return error; 1357 } 1358 1359 1360 FT_LOCAL_DEF( FT_Long ) 1361 ps_parser_to_int( PS_Parser parser ) 1362 { 1363 ps_parser_skip_spaces( parser ); 1364 return PS_Conv_ToInt( &parser->cursor, parser->limit ); 1365 } 1366 1367 1368 /* first character must be `<' if `delimiters' is non-zero */ 1369 1370 FT_LOCAL_DEF( FT_Error ) 1371 ps_parser_to_bytes( PS_Parser parser, 1372 FT_Byte* bytes, 1373 FT_Offset max_bytes, 1374 FT_Long* pnum_bytes, 1375 FT_Bool delimiters ) 1376 { 1377 FT_Error error = FT_Err_Ok; 1378 FT_Byte* cur; 1379 1380 1381 ps_parser_skip_spaces( parser ); 1382 cur = parser->cursor; 1383 1384 if ( cur >= parser->limit ) 1385 goto Exit; 1386 1387 if ( delimiters ) 1388 { 1389 if ( *cur != '<' ) 1390 { 1391 FT_ERROR(( "ps_parser_to_bytes: Missing starting delimiter `<'\n" )); 1392 error = FT_THROW( Invalid_File_Format ); 1393 goto Exit; 1394 } 1395 1396 cur++; 1397 } 1398 1399 *pnum_bytes = PS_Conv_ASCIIHexDecode( &cur, 1400 parser->limit, 1401 bytes, 1402 max_bytes ); 1403 1404 if ( delimiters ) 1405 { 1406 if ( cur < parser->limit && *cur != '>' ) 1407 { 1408 FT_ERROR(( "ps_parser_to_bytes: Missing closing delimiter `>'\n" )); 1409 error = FT_THROW( Invalid_File_Format ); 1410 goto Exit; 1411 } 1412 1413 cur++; 1414 } 1415 1416 parser->cursor = cur; 1417 1418 Exit: 1419 return error; 1420 } 1421 1422 1423 FT_LOCAL_DEF( FT_Fixed ) 1424 ps_parser_to_fixed( PS_Parser parser, 1425 FT_Int power_ten ) 1426 { 1427 ps_parser_skip_spaces( parser ); 1428 return PS_Conv_ToFixed( &parser->cursor, parser->limit, power_ten ); 1429 } 1430 1431 1432 FT_LOCAL_DEF( FT_Int ) 1433 ps_parser_to_coord_array( PS_Parser parser, 1434 FT_Int max_coords, 1435 FT_Short* coords ) 1436 { 1437 ps_parser_skip_spaces( parser ); 1438 return ps_tocoordarray( &parser->cursor, parser->limit, 1439 max_coords, coords ); 1440 } 1441 1442 1443 FT_LOCAL_DEF( FT_Int ) 1444 ps_parser_to_fixed_array( PS_Parser parser, 1445 FT_Int max_values, 1446 FT_Fixed* values, 1447 FT_Int power_ten ) 1448 { 1449 ps_parser_skip_spaces( parser ); 1450 return ps_tofixedarray( &parser->cursor, parser->limit, 1451 max_values, values, power_ten ); 1452 } 1453 1454 1455 #if 0 1456 1457 FT_LOCAL_DEF( FT_String* ) 1458 T1_ToString( PS_Parser parser ) 1459 { 1460 return ps_tostring( &parser->cursor, parser->limit, parser->memory ); 1461 } 1462 1463 1464 FT_LOCAL_DEF( FT_Bool ) 1465 T1_ToBool( PS_Parser parser ) 1466 { 1467 return ps_tobool( &parser->cursor, parser->limit ); 1468 } 1469 1470 #endif /* 0 */ 1471 1472 1473 FT_LOCAL_DEF( void ) 1474 ps_parser_init( PS_Parser parser, 1475 FT_Byte* base, 1476 FT_Byte* limit, 1477 FT_Memory memory ) 1478 { 1479 parser->error = FT_Err_Ok; 1480 parser->base = base; 1481 parser->limit = limit; 1482 parser->cursor = base; 1483 parser->memory = memory; 1484 parser->funcs = ps_parser_funcs; 1485 } 1486 1487 1488 FT_LOCAL_DEF( void ) 1489 ps_parser_done( PS_Parser parser ) 1490 { 1491 FT_UNUSED( parser ); 1492 } 1493 1494 1495 /*************************************************************************/ 1496 /*************************************************************************/ 1497 /***** *****/ 1498 /***** T1 BUILDER *****/ 1499 /***** *****/ 1500 /*************************************************************************/ 1501 /*************************************************************************/ 1502 1503 /*************************************************************************/ 1504 /* */ 1505 /* <Function> */ 1506 /* t1_builder_init */ 1507 /* */ 1508 /* <Description> */ 1509 /* Initializes a given glyph builder. */ 1510 /* */ 1511 /* <InOut> */ 1512 /* builder :: A pointer to the glyph builder to initialize. */ 1513 /* */ 1514 /* <Input> */ 1515 /* face :: The current face object. */ 1516 /* */ 1517 /* size :: The current size object. */ 1518 /* */ 1519 /* glyph :: The current glyph object. */ 1520 /* */ 1521 /* hinting :: Whether hinting should be applied. */ 1522 /* */ 1523 FT_LOCAL_DEF( void ) 1524 t1_builder_init( T1_Builder builder, 1525 FT_Face face, 1526 FT_Size size, 1527 FT_GlyphSlot glyph, 1528 FT_Bool hinting ) 1529 { 1530 builder->parse_state = T1_Parse_Start; 1531 builder->load_points = 1; 1532 1533 builder->face = face; 1534 builder->glyph = glyph; 1535 builder->memory = face->memory; 1536 1537 if ( glyph ) 1538 { 1539 FT_GlyphLoader loader = glyph->internal->loader; 1540 1541 1542 builder->loader = loader; 1543 builder->base = &loader->base.outline; 1544 builder->current = &loader->current.outline; 1545 FT_GlyphLoader_Rewind( loader ); 1546 1547 builder->hints_globals = size->internal; 1548 builder->hints_funcs = 0; 1549 1550 if ( hinting ) 1551 builder->hints_funcs = glyph->internal->glyph_hints; 1552 } 1553 1554 builder->pos_x = 0; 1555 builder->pos_y = 0; 1556 1557 builder->left_bearing.x = 0; 1558 builder->left_bearing.y = 0; 1559 builder->advance.x = 0; 1560 builder->advance.y = 0; 1561 1562 builder->funcs = t1_builder_funcs; 1563 } 1564 1565 1566 /*************************************************************************/ 1567 /* */ 1568 /* <Function> */ 1569 /* t1_builder_done */ 1570 /* */ 1571 /* <Description> */ 1572 /* Finalizes a given glyph builder. Its contents can still be used */ 1573 /* after the call, but the function saves important information */ 1574 /* within the corresponding glyph slot. */ 1575 /* */ 1576 /* <Input> */ 1577 /* builder :: A pointer to the glyph builder to finalize. */ 1578 /* */ 1579 FT_LOCAL_DEF( void ) 1580 t1_builder_done( T1_Builder builder ) 1581 { 1582 FT_GlyphSlot glyph = builder->glyph; 1583 1584 1585 if ( glyph ) 1586 glyph->outline = *builder->base; 1587 } 1588 1589 1590 /* check that there is enough space for `count' more points */ 1591 FT_LOCAL_DEF( FT_Error ) 1592 t1_builder_check_points( T1_Builder builder, 1593 FT_Int count ) 1594 { 1595 return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 ); 1596 } 1597 1598 1599 /* add a new point, do not check space */ 1600 FT_LOCAL_DEF( void ) 1601 t1_builder_add_point( T1_Builder builder, 1602 FT_Pos x, 1603 FT_Pos y, 1604 FT_Byte flag ) 1605 { 1606 FT_Outline* outline = builder->current; 1607 1608 1609 if ( builder->load_points ) 1610 { 1611 FT_Vector* point = outline->points + outline->n_points; 1612 FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points; 1613 1614 1615 point->x = FIXED_TO_INT( x ); 1616 point->y = FIXED_TO_INT( y ); 1617 *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC ); 1618 } 1619 outline->n_points++; 1620 } 1621 1622 1623 /* check space for a new on-curve point, then add it */ 1624 FT_LOCAL_DEF( FT_Error ) 1625 t1_builder_add_point1( T1_Builder builder, 1626 FT_Pos x, 1627 FT_Pos y ) 1628 { 1629 FT_Error error; 1630 1631 1632 error = t1_builder_check_points( builder, 1 ); 1633 if ( !error ) 1634 t1_builder_add_point( builder, x, y, 1 ); 1635 1636 return error; 1637 } 1638 1639 1640 /* check space for a new contour, then add it */ 1641 FT_LOCAL_DEF( FT_Error ) 1642 t1_builder_add_contour( T1_Builder builder ) 1643 { 1644 FT_Outline* outline = builder->current; 1645 FT_Error error; 1646 1647 1648 /* this might happen in invalid fonts */ 1649 if ( !outline ) 1650 { 1651 FT_ERROR(( "t1_builder_add_contour: no outline to add points to\n" )); 1652 return FT_THROW( Invalid_File_Format ); 1653 } 1654 1655 if ( !builder->load_points ) 1656 { 1657 outline->n_contours++; 1658 return FT_Err_Ok; 1659 } 1660 1661 error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 ); 1662 if ( !error ) 1663 { 1664 if ( outline->n_contours > 0 ) 1665 outline->contours[outline->n_contours - 1] = 1666 (short)( outline->n_points - 1 ); 1667 1668 outline->n_contours++; 1669 } 1670 1671 return error; 1672 } 1673 1674 1675 /* if a path was begun, add its first on-curve point */ 1676 FT_LOCAL_DEF( FT_Error ) 1677 t1_builder_start_point( T1_Builder builder, 1678 FT_Pos x, 1679 FT_Pos y ) 1680 { 1681 FT_Error error = FT_ERR( Invalid_File_Format ); 1682 1683 1684 /* test whether we are building a new contour */ 1685 1686 if ( builder->parse_state == T1_Parse_Have_Path ) 1687 error = FT_Err_Ok; 1688 else 1689 { 1690 builder->parse_state = T1_Parse_Have_Path; 1691 error = t1_builder_add_contour( builder ); 1692 if ( !error ) 1693 error = t1_builder_add_point1( builder, x, y ); 1694 } 1695 1696 return error; 1697 } 1698 1699 1700 /* close the current contour */ 1701 FT_LOCAL_DEF( void ) 1702 t1_builder_close_contour( T1_Builder builder ) 1703 { 1704 FT_Outline* outline = builder->current; 1705 FT_Int first; 1706 1707 1708 if ( !outline ) 1709 return; 1710 1711 first = outline->n_contours <= 1 1712 ? 0 : outline->contours[outline->n_contours - 2] + 1; 1713 1714 /* We must not include the last point in the path if it */ 1715 /* is located on the first point. */ 1716 if ( outline->n_points > 1 ) 1717 { 1718 FT_Vector* p1 = outline->points + first; 1719 FT_Vector* p2 = outline->points + outline->n_points - 1; 1720 FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points - 1; 1721 1722 1723 /* `delete' last point only if it coincides with the first */ 1724 /* point and it is not a control point (which can happen). */ 1725 if ( p1->x == p2->x && p1->y == p2->y ) 1726 if ( *control == FT_CURVE_TAG_ON ) 1727 outline->n_points--; 1728 } 1729 1730 if ( outline->n_contours > 0 ) 1731 { 1732 /* Don't add contours only consisting of one point, i.e., */ 1733 /* check whether the first and the last point is the same. */ 1734 if ( first == outline->n_points - 1 ) 1735 { 1736 outline->n_contours--; 1737 outline->n_points--; 1738 } 1739 else 1740 outline->contours[outline->n_contours - 1] = 1741 (short)( outline->n_points - 1 ); 1742 } 1743 } 1744 1745 1746 /*************************************************************************/ 1747 /*************************************************************************/ 1748 /***** *****/ 1749 /***** OTHER *****/ 1750 /***** *****/ 1751 /*************************************************************************/ 1752 /*************************************************************************/ 1753 1754 FT_LOCAL_DEF( void ) 1755 t1_decrypt( FT_Byte* buffer, 1756 FT_Offset length, 1757 FT_UShort seed ) 1758 { 1759 PS_Conv_EexecDecode( &buffer, 1760 buffer + length, 1761 buffer, 1762 length, 1763 &seed ); 1764 } 1765 1766 1767 /* END */ 1768