ttgload.c revision c5cdf8bcf1bded63fbd57853ded641a42292a91c
1/***************************************************************************/ 2/* */ 3/* ttgload.c */ 4/* */ 5/* TrueType Glyph Loader (body). */ 6/* */ 7/* Copyright 1996-2000 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 <freetype/internal/ftdebug.h> 20#include <freetype/internal/ftcalc.h> 21#include <freetype/internal/ftstream.h> 22#include <freetype/internal/sfnt.h> 23#include <freetype/tttags.h> 24#include <freetype/ftoutln.h> 25 26 27#ifdef FT_FLAT_COMPILE 28 29#include "ttgload.h" 30 31#else 32 33#include <truetype/ttgload.h> 34 35#endif 36 37 38 /*************************************************************************/ 39 /* */ 40 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ 41 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ 42 /* messages during execution. */ 43 /* */ 44#undef FT_COMPONENT 45#define FT_COMPONENT trace_ttgload 46 47 48 /*************************************************************************/ 49 /* */ 50 /* Composite font flags. */ 51 /* */ 52#define ARGS_ARE_WORDS 0x001 53#define ARGS_ARE_XY_VALUES 0x002 54#define ROUND_XY_TO_GRID 0x004 55#define WE_HAVE_A_SCALE 0x008 56/* reserved 0x010 */ 57#define MORE_COMPONENTS 0x020 58#define WE_HAVE_AN_XY_SCALE 0x040 59#define WE_HAVE_A_2X2 0x080 60#define WE_HAVE_INSTR 0x100 61#define USE_MY_METRICS 0x200 62 63 64 65 /*************************************************************************/ 66 /* */ 67 /* <Function> */ 68 /* TT_Get_Metrics */ 69 /* */ 70 /* <Description> */ 71 /* Returns the horizontal or vertical metrics in font units for a */ 72 /* given glyph. The metrics are the left side bearing (resp. top */ 73 /* side bearing) and advance width (resp. advance height). */ 74 /* */ 75 /* <Input> */ 76 /* header :: A pointer to either the horizontal or vertical metrics */ 77 /* structure. */ 78 /* */ 79 /* index :: The glyph index. */ 80 /* */ 81 /* <Output> */ 82 /* bearing :: The bearing, either left side or top side. */ 83 /* */ 84 /* advance :: The advance width resp. advance height. */ 85 /* */ 86 /* <Note> */ 87 /* This function will much probably move to another component in the */ 88 /* near future, but I haven't decided which yet. */ 89 /* */ 90 LOCAL_FUNC 91 void TT_Get_Metrics( TT_HoriHeader* header, 92 FT_UInt index, 93 FT_Short* bearing, 94 FT_UShort* advance ) 95 { 96 TT_LongMetrics* longs_m; 97 FT_UShort k = header->number_Of_HMetrics; 98 99 100 if ( index < k ) 101 { 102 longs_m = (TT_LongMetrics*)header->long_metrics + index; 103 *bearing = longs_m->bearing; 104 *advance = longs_m->advance; 105 } 106 else 107 { 108 *bearing = ((TT_ShortMetrics*)header->short_metrics)[index - k]; 109 *advance = ((TT_LongMetrics*)header->long_metrics)[k - 1].advance; 110 } 111 } 112 113 114 /*************************************************************************/ 115 /* */ 116 /* Returns the horizontal metrics in font units for a given glyph. If */ 117 /* `check' is true, take care of monospaced fonts by returning the */ 118 /* advance width maximum. */ 119 /* */ 120 static 121 void Get_HMetrics( TT_Face face, 122 FT_UInt index, 123 FT_Bool check, 124 FT_Short* lsb, 125 FT_UShort* aw ) 126 { 127 TT_Get_Metrics( &face->horizontal, index, lsb, aw ); 128 129 if ( check && face->postscript.isFixedPitch ) 130 *aw = face->horizontal.advance_Width_Max; 131 } 132 133 134 /*************************************************************************/ 135 /* */ 136 /* Returns the advance width table for a given pixel size if it is */ 137 /* found in the font's `hdmx' table (if any). */ 138 /* */ 139 static 140 FT_Byte* Get_Advance_Widths( TT_Face face, 141 FT_UShort ppem ) 142 { 143 FT_UShort n; 144 145 for ( n = 0; n < face->hdmx.num_records; n++ ) 146 if ( face->hdmx.records[n].ppem == ppem ) 147 return face->hdmx.records[n].widths; 148 149 return NULL; 150 } 151 152 153#define cur_to_org( n, zone ) \ 154 MEM_Copy( (zone)->org, (zone)->cur, n * sizeof ( FT_Vector ) ) 155 156#define org_to_cur( n, zone ) \ 157 MEM_Copy( (zone)->cur, (zone)->org, n * sizeof ( FT_Vector ) ) 158 159 160 /*************************************************************************/ 161 /* */ 162 /* Translates an array of coordinates. */ 163 /* */ 164 static 165 void translate_array( FT_UInt n, 166 FT_Vector* coords, 167 FT_Pos delta_x, 168 FT_Pos delta_y ) 169 { 170 FT_UInt k; 171 172 173 if ( delta_x ) 174 for ( k = 0; k < n; k++ ) 175 coords[k].x += delta_x; 176 177 if ( delta_y ) 178 for ( k = 0; k < n; k++ ) 179 coords[k].y += delta_y; 180 } 181 182 183 static 184 void tt_prepare_zone( TT_GlyphZone* zone, 185 FT_GlyphLoad* load, 186 FT_UInt start_point, 187 FT_UInt start_contour ) 188 { 189 zone->n_points = load->outline.n_points - start_point; 190 zone->n_contours = load->outline.n_contours - start_contour; 191 zone->org = load->extra_points + start_point; 192 zone->cur = load->outline.points + start_point; 193 zone->tags = (FT_Byte*)load->outline.tags + start_point; 194 zone->contours = (FT_UShort*)load->outline.contours + start_contour; 195 } 196 197 198#undef IS_HINTED 199#define IS_HINTED( flags ) ( ( flags & FT_LOAD_NO_HINTING ) == 0 ) 200 201 202 /*************************************************************************/ 203 /* */ 204 /* The following functions are used by default with TrueType fonts. */ 205 /* However, they can be replaced by alternatives if we need to support */ 206 /* TrueType-compressed formats (like MicroType) in the future. */ 207 /* */ 208 /*************************************************************************/ 209 210 static 211 FT_Error TT_Access_Glyph_Frame( TT_Loader* loader, 212 FT_UInt glyph_index, 213 FT_ULong offset, 214 FT_UInt byte_count ) 215 { 216 FT_Error error; 217 FT_Stream stream = loader->stream; 218 219 220 /* the following line sets the `error' variable through macros! */ 221 (void)( FILE_Seek( offset ) || ACCESS_Frame( byte_count ) ); 222 223 FT_TRACE5(( "Glyph %ld\n", glyph_index )); 224 return error; 225 } 226 227 228 static 229 void TT_Forget_Glyph_Frame( TT_Loader* loader ) 230 { 231 FT_Stream stream = loader->stream; 232 233 234 FORGET_Frame(); 235 } 236 237 238 static 239 FT_Error TT_Load_Glyph_Header( TT_Loader* loader ) 240 { 241 FT_Stream stream = loader->stream; 242 243 244 loader->n_contours = GET_Short(); 245 246 loader->bbox.xMin = GET_Short(); 247 loader->bbox.yMin = GET_Short(); 248 loader->bbox.xMax = GET_Short(); 249 loader->bbox.yMax = GET_Short(); 250 251 FT_TRACE5(( " # of contours: %d\n", loader->n_contours )); 252 FT_TRACE5(( " xMin: %4d xMax: %4d\n", loader->bbox.xMin, 253 loader->bbox.xMax )); 254 FT_TRACE5(( " yMin: %4d yMax: %4d\n", loader->bbox.yMin, 255 loader->bbox.yMax )); 256 257 return FT_Err_Ok; 258 } 259 260 261 static 262 FT_Error TT_Load_Simple_Glyph( TT_Loader* load ) 263 { 264 FT_Error error; 265 FT_Stream stream = load->stream; 266 FT_GlyphLoader* gloader = load->gloader; 267 FT_Int n_contours = load->n_contours; 268 FT_Outline* outline; 269 TT_Face face = (TT_Face)load->face; 270 TT_GlyphSlot slot = (TT_GlyphSlot)load->glyph; 271 FT_UShort n_ins; 272 FT_Int n, n_points; 273 274 275 /* reading the contours endpoints & number of points */ 276 { 277 short* cur = gloader->current.outline.contours; 278 short* limit = cur + n_contours; 279 280 281 for ( ; cur < limit; cur++ ) 282 cur[0] = GET_UShort(); 283 284 n_points = 0; 285 if ( n_contours > 0 ) 286 n_points = cur[-1] + 1; 287 288 error = FT_GlyphLoader_Check_Points( gloader, n_points + 2, 0 ); 289 if ( error ) 290 goto Fail; 291 292 outline = &gloader->current.outline; 293 } 294 295 /* reading the bytecode instructions */ 296 slot->control_len = 0; 297 slot->control_data = 0; 298 299 n_ins = GET_UShort(); 300 301 FT_TRACE5(( " Instructions size: %d\n", n_ins )); 302 303 if ( n_ins > face->max_profile.maxSizeOfInstructions ) 304 { 305 FT_TRACE0(( "ERROR: Too many instructions!\n" )); 306 error = TT_Err_Too_Many_Ins; 307 goto Fail; 308 } 309 310 if ( stream->cursor + n_ins > stream->limit ) 311 { 312 FT_TRACE0(( "ERROR: Instruction count mismatch!\n" )); 313 error = TT_Err_Too_Many_Ins; 314 goto Fail; 315 } 316 317#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER 318 319 if ( ( load->load_flags & 320 ( FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING ) ) == 0 && 321 load->instructions ) 322 { 323 slot->control_len = n_ins; 324 slot->control_data = load->instructions; 325 326 MEM_Copy( load->instructions, stream->cursor, n_ins ); 327 } 328 329#endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ 330 331 stream->cursor += n_ins; 332 333 /* reading the point tags */ 334 335 { 336 FT_Byte* flag = (FT_Byte*)outline->tags; 337 FT_Byte* limit = flag + n_points; 338 FT_Byte c, count; 339 340 341 for ( ; flag < limit; flag++ ) 342 { 343 *flag = c = GET_Byte(); 344 if ( c & 8 ) 345 { 346 for ( count = GET_Byte(); count > 0; count-- ) 347 *++flag = c; 348 } 349 } 350 } 351 352 /* reading the X coordinates */ 353 354 { 355 FT_Vector* vec = outline->points; 356 FT_Vector* limit = vec + n_points; 357 FT_Byte* flag = (FT_Byte*)outline->tags; 358 FT_Pos x = 0; 359 360 361 for ( ; vec < limit; vec++, flag++ ) 362 { 363 FT_Pos y = 0; 364 365 366 if ( *flag & 2 ) 367 { 368 y = GET_Byte(); 369 if ( ( *flag & 16 ) == 0 ) 370 y = -y; 371 } 372 else if ( ( *flag & 16 ) == 0 ) 373 y = GET_Short(); 374 375 x += y; 376 vec->x = x; 377 } 378 } 379 380 /* reading the Y coordinates */ 381 382 { 383 FT_Vector* vec = gloader->current.outline.points; 384 FT_Vector* limit = vec + n_points; 385 FT_Byte* flag = (FT_Byte*)outline->tags; 386 FT_Pos x = 0; 387 388 389 for ( ; vec < limit; vec++, flag++ ) 390 { 391 FT_Pos y = 0; 392 393 394 if ( *flag & 4 ) 395 { 396 y = GET_Byte(); 397 if ( ( *flag & 32 ) == 0 ) 398 y = -y; 399 } 400 else if ( ( *flag & 32 ) == 0 ) 401 y = GET_Short(); 402 403 x += y; 404 vec->y = x; 405 } 406 } 407 408 /* clear the touch tags */ 409 for ( n = 0; n < n_points; n++ ) 410 outline->tags[n] &= FT_Curve_Tag_On; 411 412 outline->n_points = n_points; 413 outline->n_contours = n_contours; 414 415 Fail: 416 return error; 417 } 418 419 420 static 421 FT_Error TT_Load_Composite_Glyph( TT_Loader* loader ) 422 { 423 FT_Error error; 424 FT_Stream stream = loader->stream; 425 FT_GlyphLoader* gloader = loader->gloader; 426 FT_SubGlyph* subglyph; 427 FT_UInt num_subglyphs; 428 429 430 num_subglyphs = 0; 431 432 do 433 { 434 FT_Fixed xx, xy, yy, yx; 435 436 437 /* check that we can load a new subglyph */ 438 error = FT_GlyphLoader_Check_Subglyphs( gloader, num_subglyphs + 1 ); 439 if ( error ) 440 goto Fail; 441 442 subglyph = gloader->current.subglyphs + num_subglyphs; 443 444 subglyph->arg1 = subglyph->arg2 = 0; 445 446 subglyph->flags = GET_UShort(); 447 subglyph->index = GET_UShort(); 448 449 /* read arguments */ 450 if ( subglyph->flags & ARGS_ARE_WORDS ) 451 { 452 subglyph->arg1 = GET_Short(); 453 subglyph->arg2 = GET_Short(); 454 } 455 else 456 { 457 subglyph->arg1 = GET_Char(); 458 subglyph->arg2 = GET_Char(); 459 } 460 461 /* read transform */ 462 xx = yy = 0x10000L; 463 xy = yx = 0; 464 465 if ( subglyph->flags & WE_HAVE_A_SCALE ) 466 { 467 xx = (FT_Fixed)GET_Short() << 2; 468 yy = xx; 469 } 470 else if ( subglyph->flags & WE_HAVE_AN_XY_SCALE ) 471 { 472 xx = (FT_Fixed)GET_Short() << 2; 473 yy = (FT_Fixed)GET_Short() << 2; 474 } 475 else if ( subglyph->flags & WE_HAVE_A_2X2 ) 476 { 477 xx = (FT_Fixed)GET_Short() << 2; 478 xy = (FT_Fixed)GET_Short() << 2; 479 yx = (FT_Fixed)GET_Short() << 2; 480 yy = (FT_Fixed)GET_Short() << 2; 481 } 482 483 subglyph->transform.xx = xx; 484 subglyph->transform.xy = xy; 485 subglyph->transform.yx = yx; 486 subglyph->transform.yy = yy; 487 488 num_subglyphs++; 489 490 } while ( subglyph->flags & MORE_COMPONENTS ); 491 492 gloader->current.num_subglyphs = num_subglyphs; 493 494#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER 495 { 496 /* we must undo the ACCESS_Frame in order to point to the */ 497 /* composite instructions, if we find some. */ 498 /* we will process them later... */ 499 /* */ 500 loader->ins_pos = FILE_Pos() + stream->cursor - stream->limit; 501 } 502#endif 503 504 Fail: 505 return error; 506 } 507 508 509 LOCAL_FUNC 510 void TT_Init_Glyph_Loading( TT_Face face ) 511 { 512 face->access_glyph_frame = TT_Access_Glyph_Frame; 513 face->read_glyph_header = TT_Load_Glyph_Header; 514 face->read_simple_glyph = TT_Load_Simple_Glyph; 515 face->read_composite_glyph = TT_Load_Composite_Glyph; 516 face->forget_glyph_frame = TT_Forget_Glyph_Frame; 517 } 518 519 520 /*************************************************************************/ 521 /* */ 522 /* <Function> */ 523 /* TT_Process_Simple_Glyph */ 524 /* */ 525 /* <Description> */ 526 /* Once a simple glyph has been loaded, it needs to be processed. */ 527 /* Usually, this means scaling and hinting through bytecode */ 528 /* interpretation. */ 529 /* */ 530 static 531 FT_Error TT_Process_Simple_Glyph( TT_Loader* load, 532 FT_Bool debug ) 533 { 534 FT_GlyphLoader* gloader = load->gloader; 535 FT_Outline* outline = &gloader->current.outline; 536 FT_UInt n_points = outline->n_points; 537 FT_UInt n_ins; 538 TT_GlyphZone* zone = &load->zone; 539 FT_Error error = FT_Err_Ok; 540 541 542 FT_UNUSED(debug); /* used by truetype interpreter only */ 543 544 n_ins = load->glyph->control_len; 545 546 /* add shadow points */ 547 548 /* Now add the two shadow points at n and n + 1. */ 549 /* We need the left side bearing and advance width. */ 550 551 { 552 FT_Vector* pp1; 553 FT_Vector* pp2; 554 555 556 /* pp1 = xMin - lsb */ 557 pp1 = outline->points + n_points; 558 pp1->x = load->bbox.xMin - load->left_bearing; 559 pp1->y = 0; 560 561 /* pp2 = pp1 + aw */ 562 pp2 = pp1 + 1; 563 pp2->x = pp1->x + load->advance; 564 pp2->y = 0; 565 566 outline->tags[n_points ] = 0; 567 outline->tags[n_points + 1] = 0; 568 } 569 570 /* Note that we return two more points that are not */ 571 /* part of the glyph outline. */ 572 573 n_points += 2; 574 575 /* set up zone for hinting */ 576 tt_prepare_zone( zone, &gloader->current, 0, 0 ); 577 578 /* eventually scale the glyph */ 579 if ( !( load->load_flags & FT_LOAD_NO_SCALE ) ) 580 { 581 FT_Vector* vec = zone->cur; 582 FT_Vector* limit = vec + n_points; 583 FT_Fixed x_scale = load->size->metrics.x_scale; 584 FT_Fixed y_scale = load->size->metrics.y_scale; 585 586 587 /* first scale the glyph points */ 588 for ( ; vec < limit; vec++ ) 589 { 590 vec->x = FT_MulFix( vec->x, x_scale ); 591 vec->y = FT_MulFix( vec->y, y_scale ); 592 } 593 } 594 595 cur_to_org( n_points, zone ); 596 597 /* eventually hint the glyph */ 598 if ( IS_HINTED( load->load_flags ) ) 599 { 600 FT_Pos x = zone->org[n_points-2].x; 601 602 603 x = ( ( x + 32 ) & -64 ) - x; 604 translate_array( n_points, zone->org, x, 0 ); 605 606 org_to_cur( n_points, zone ); 607 608 zone->cur[n_points - 1].x = ( zone->cur[n_points - 1].x + 32 ) & -64; 609 610#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER 611 612 /* now consider hinting */ 613 if ( n_ins > 0 ) 614 { 615 error = TT_Set_CodeRange( load->exec, tt_coderange_glyph, 616 load->exec->glyphIns, n_ins ); 617 if ( error ) 618 goto Exit; 619 620 load->exec->is_composite = FALSE; 621 load->exec->pedantic_hinting = (FT_Bool)( load->load_flags & 622 FT_LOAD_PEDANTIC ); 623 load->exec->pts = *zone; 624 load->exec->pts.n_points += 2; 625 626 error = TT_Run_Context( load->exec, debug ); 627 if ( error && load->exec->pedantic_hinting ) 628 goto Exit; 629 630 error = FT_Err_Ok; /* ignore bytecode errors in non-pedantic mode */ 631 } 632 633#endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ 634 635 } 636 637 /* save glyph phantom points */ 638 if ( !load->preserve_pps ) 639 { 640 load->pp1 = zone->cur[n_points - 2]; 641 load->pp2 = zone->cur[n_points - 1]; 642 } 643 644#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER 645 Exit: 646#endif 647 return error; 648 } 649 650 651 /*************************************************************************/ 652 /* */ 653 /* <Function> */ 654 /* load_truetype_glyph */ 655 /* */ 656 /* <Description> */ 657 /* Loads a given truetype glyph. Handles composites and uses a */ 658 /* TT_Loader object. */ 659 /* */ 660 static 661 FT_Error load_truetype_glyph( TT_Loader* loader, 662 FT_UInt glyph_index ) 663 { 664 665#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER 666 FT_Stream stream = loader->stream; 667#endif 668 669 FT_Error error; 670 TT_Face face = (TT_Face)loader->face; 671 FT_ULong offset; 672 FT_Int contours_count; 673 FT_UInt index, num_points, num_contours, count; 674 FT_Fixed x_scale, y_scale; 675 FT_ULong ins_offset; 676 FT_GlyphLoader* gloader = loader->gloader; 677 FT_Bool opened_frame = 0; 678 679 FT_UNUSED(stream); /* used with bytecode interpreter only */ 680 681 /* check glyph index */ 682 index = glyph_index; 683 if ( index >= (FT_UInt)face->root.num_glyphs ) 684 { 685 error = TT_Err_Invalid_Glyph_Index; 686 goto Exit; 687 } 688 689 loader->glyph_index = glyph_index; 690 num_contours = 0; 691 num_points = 0; 692 ins_offset = 0; 693 694 x_scale = 0x10000L; 695 y_scale = 0x10000L; 696 if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 ) 697 { 698 x_scale = loader->size->metrics.x_scale; 699 y_scale = loader->size->metrics.y_scale; 700 } 701 702 /* get horizontal metrics */ 703 { 704 FT_Short left_bearing; 705 FT_UShort advance_width; 706 707 708 Get_HMetrics( face, index, 709 (FT_Bool)!(loader->load_flags & 710 FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH), 711 &left_bearing, 712 &advance_width ); 713 714 loader->left_bearing = left_bearing; 715 loader->advance = advance_width; 716 } 717 718 offset = face->glyph_locations[index]; 719 count = 0; 720 721 if ( index < (FT_UInt)face->num_locations - 1 ) 722 count = face->glyph_locations[index + 1] - offset; 723 724 if ( count == 0 ) 725 { 726 /* as described by Frederic Loyer, these are spaces, and */ 727 /* not the unknown glyph. */ 728 loader->bbox.xMin = 0; 729 loader->bbox.xMax = 0; 730 loader->bbox.yMin = 0; 731 loader->bbox.yMax = 0; 732 733 loader->pp1.x = 0; 734 loader->pp2.x = loader->advance; 735 736 if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 ) 737 loader->pp2.x = FT_MulFix( loader->pp2.x, x_scale ); 738 739#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER 740 741 if ( loader->exec ) 742 loader->exec->glyphSize = 0; 743 744#endif 745 746 error = FT_Err_Ok; 747 goto Exit; 748 } 749 750 offset = loader->glyf_offset + offset; 751 752 /* access glyph frame */ 753 error = face->access_glyph_frame( loader, glyph_index, offset, count ); 754 if ( error ) 755 goto Exit; 756 757 opened_frame = 1; 758 759 /* read first glyph header */ 760 error = face->read_glyph_header( loader ); 761 if ( error ) 762 goto Fail; 763 764 contours_count = loader->n_contours; 765 766 count -= 10; 767 768 loader->pp1.x = loader->bbox.xMin - loader->left_bearing; 769 loader->pp1.y = 0; 770 loader->pp2.x = loader->pp1.x + loader->advance; 771 loader->pp2.y = 0; 772 773 if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 ) 774 { 775 loader->pp1.x = FT_MulFix( loader->pp1.x, x_scale ); 776 loader->pp2.x = FT_MulFix( loader->pp2.x, x_scale ); 777 } 778 779 /***********************************************************************/ 780 /***********************************************************************/ 781 /***********************************************************************/ 782 783 /* if it is a simple glyph, load it */ 784 785 if ( contours_count >= 0 ) 786 { 787 /* check that we can add the contours to the glyph */ 788 error = FT_GlyphLoader_Check_Points( gloader, 0, contours_count ); 789 if ( error ) 790 goto Fail; 791 792 error = face->read_simple_glyph( loader ); 793 if ( error ) 794 goto Fail; 795 796#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER 797 798 { 799 TT_Size size = (TT_Size)loader->size; 800 801 802 error = TT_Process_Simple_Glyph( loader, 803 (FT_Bool)( size && size->debug ) ); 804 } 805 806#else 807 808 error = TT_Process_Simple_Glyph( loader, 0 ); 809 810#endif 811 812 if ( error ) 813 goto Fail; 814 815 FT_GlyphLoader_Add( gloader ); 816 817 /* Note: We could have put the simple loader source there */ 818 /* but the code is fat enough already :-) */ 819 } 820 821 /***********************************************************************/ 822 /***********************************************************************/ 823 /***********************************************************************/ 824 825 /* otherwise, load a composite! */ 826 else 827 { 828 TT_GlyphSlot glyph = (TT_GlyphSlot)loader->glyph; 829 FT_UInt start_point, start_contour; 830 FT_ULong ins_pos; /* position of composite instructions, if any */ 831 832 833 /* for each subglyph, read composite header */ 834 start_point = gloader->base.outline.n_points; 835 start_contour = gloader->base.outline.n_contours; 836 837 error = face->read_composite_glyph( loader ); 838 if ( error ) 839 goto Fail; 840 841 ins_pos = loader->ins_pos; 842 face->forget_glyph_frame( loader ); 843 opened_frame = 0; 844 845 /* if the flag FT_LOAD_NO_RECURSE is set, we return the subglyph */ 846 /* `as is' in the glyph slot (the client application will be */ 847 /* responsible for interpreting this data)... */ 848 /* */ 849 if ( loader->load_flags & FT_LOAD_NO_RECURSE ) 850 { 851 /* set up remaining glyph fields */ 852 FT_GlyphLoader_Add( gloader ); 853 854 glyph->num_subglyphs = gloader->base.num_subglyphs; 855 glyph->format = ft_glyph_format_composite; 856 glyph->subglyphs = gloader->base.subglyphs; 857 858 goto Exit; 859 } 860 861 /*********************************************************************/ 862 /*********************************************************************/ 863 /*********************************************************************/ 864 865 /* Now, read each subglyph independently. */ 866 { 867 FT_Int n, num_base_points, num_new_points; 868 FT_SubGlyph* subglyph = 0; 869 870 FT_UInt num_subglyphs = gloader->current.num_subglyphs; 871 FT_UInt num_base_subgs = gloader->base.num_subglyphs; 872 873 874 FT_GlyphLoader_Add( gloader ); 875 876 for ( n = 0; n < (FT_Int)num_subglyphs; n++ ) 877 { 878 FT_Vector pp1, pp2; 879 FT_Pos x, y; 880 881 882 /* Each time we call load_truetype_glyph in this loop, the */ 883 /* value of `gloader.base.subglyphs' can change due to table */ 884 /* reallocations. We thus need to recompute the subglyph */ 885 /* pointer on each iteration. */ 886 subglyph = gloader->base.subglyphs + num_base_subgs + n; 887 888 pp1 = loader->pp1; 889 pp2 = loader->pp2; 890 891 num_base_points = gloader->base.outline.n_points; 892 893 error = load_truetype_glyph( loader, subglyph->index ); 894 if ( error ) 895 goto Fail; 896 897 subglyph = gloader->base.subglyphs + num_base_subgs + n; 898 899 if ( subglyph->flags & USE_MY_METRICS ) 900 { 901 pp1 = loader->pp1; 902 pp2 = loader->pp2; 903 } 904 else 905 { 906 loader->pp1 = pp1; 907 loader->pp2 = pp2; 908 } 909 910 num_points = gloader->base.outline.n_points; 911 912 num_new_points = num_points - num_base_points; 913 914 /* now perform the transform required for this subglyph */ 915 916 if ( subglyph->flags & ( WE_HAVE_A_SCALE | 917 WE_HAVE_AN_XY_SCALE | 918 WE_HAVE_A_2X2 ) ) 919 { 920 FT_Vector* cur = gloader->base.outline.points + 921 num_base_points; 922 FT_Vector* org = gloader->base.extra_points + 923 num_base_points; 924 FT_Vector* limit = cur + num_new_points; 925 926 927 for ( ; cur < limit; cur++, org++ ) 928 { 929 FT_Vector_Transform( cur, &subglyph->transform ); 930 FT_Vector_Transform( org, &subglyph->transform ); 931 } 932 } 933 934 /* apply offset */ 935 936 if ( !( subglyph->flags & ARGS_ARE_XY_VALUES ) ) 937 { 938 FT_UInt k = subglyph->arg1; 939 FT_UInt l = subglyph->arg2; 940 FT_Vector* p1; 941 FT_Vector* p2; 942 943 944 if ( start_point + k >= (FT_UInt)num_base_points || 945 l >= (FT_UInt)num_new_points ) 946 { 947 error = TT_Err_Invalid_Composite; 948 goto Fail; 949 } 950 951 l += num_base_points; 952 953 p1 = gloader->base.outline.points + start_point + k; 954 p2 = gloader->base.outline.points + start_point + l; 955 956 x = p1->x - p2->x; 957 y = p1->y - p2->y; 958 } 959 else 960 { 961 x = subglyph->arg1; 962 y = subglyph->arg2; 963 964 if ( !( loader->load_flags & FT_LOAD_NO_SCALE ) ) 965 { 966 x = FT_MulFix( x, x_scale ); 967 y = FT_MulFix( y, y_scale ); 968 969 if ( subglyph->flags & ROUND_XY_TO_GRID ) 970 { 971 x = ( x + 32 ) & -64; 972 y = ( y + 32 ) & -64; 973 } 974 } 975 } 976 977 translate_array( num_new_points, loader->zone.cur, x, y ); 978 cur_to_org( num_new_points, &loader->zone ); 979 } 980 981 /*******************************************************************/ 982 /*******************************************************************/ 983 /*******************************************************************/ 984 985 /* we have finished loading all sub-glyphs; now, look for */ 986 /* instructions for this composite! */ 987 988#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER 989 990 if ( num_subglyphs > 0 && 991 loader->exec && 992 ins_pos > 0 && 993 subglyph->flags & WE_HAVE_INSTR ) 994 { 995 FT_UShort n_ins; 996 TT_ExecContext exec = loader->exec; 997 TT_GlyphZone* pts; 998 FT_Vector* pp1; 999 1000 1001 /* read size of instructions */ 1002 if ( FILE_Seek( ins_pos ) || 1003 READ_UShort( n_ins ) ) 1004 goto Fail; 1005 FT_TRACE5(( " Instructions size = %d\n", n_ins )); 1006 1007 /* in some fonts? */ 1008 if ( n_ins == 0xFFFF ) 1009 n_ins = 0; 1010 1011 /* check it */ 1012 if ( n_ins > face->max_profile.maxSizeOfInstructions ) 1013 { 1014 FT_TRACE0(( "Too many instructions (%d) in composite glyph %ld\n", 1015 n_ins, subglyph->index )); 1016 return TT_Err_Too_Many_Ins; 1017 } 1018 1019 /* read the instructions */ 1020 if ( FILE_Read( exec->glyphIns, n_ins ) ) 1021 goto Fail; 1022 1023 glyph->control_data = exec->glyphIns; 1024 glyph->control_len = n_ins; 1025 1026 error = TT_Set_CodeRange( exec, 1027 tt_coderange_glyph, 1028 exec->glyphIns, 1029 n_ins ); 1030 if ( error ) 1031 goto Fail; 1032 1033 /* prepare the execution context */ 1034 tt_prepare_zone( &exec->pts, &gloader->base, 1035 start_point, start_contour ); 1036 pts = &exec->pts; 1037 1038 pts->n_points = num_points + 2; 1039 pts->n_contours = gloader->base.outline.n_contours; 1040 1041 /* add phantom points */ 1042 pp1 = pts->cur + num_points; 1043 pp1[0] = loader->pp1; 1044 pp1[1] = loader->pp2; 1045 1046 pts->tags[num_points ] = 0; 1047 pts->tags[num_points + 1] = 0; 1048 1049 /* if hinting, round the phantom points */ 1050 if ( IS_HINTED( loader->load_flags ) ) 1051 { 1052 pp1[0].x = ( ( loader->pp1.x + 32 ) & -64 ); 1053 pp1[1].x = ( ( loader->pp2.x + 32 ) & -64 ); 1054 } 1055 1056 { 1057 FT_UInt k; 1058 1059 1060 for ( k = 0; k < num_points; k++ ) 1061 pts->tags[k] &= FT_Curve_Tag_On; 1062 } 1063 1064 cur_to_org( num_points + 2, pts ); 1065 1066 /* now consider hinting */ 1067 if ( IS_HINTED( loader->load_flags ) && n_ins > 0 ) 1068 { 1069 exec->is_composite = TRUE; 1070 exec->pedantic_hinting = 1071 (FT_Bool)( loader->load_flags & FT_LOAD_PEDANTIC ); 1072 1073 error = TT_Run_Context( exec, ((TT_Size)loader->size)->debug ); 1074 if ( error && exec->pedantic_hinting ) 1075 goto Fail; 1076 } 1077 1078 /* save glyph origin and advance points */ 1079 loader->pp1 = pp1[0]; 1080 loader->pp2 = pp1[1]; 1081 } 1082 1083#endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ 1084 1085 } 1086 /* end of composite loading */ 1087 } 1088 1089 /***********************************************************************/ 1090 /***********************************************************************/ 1091 /***********************************************************************/ 1092 1093 Fail: 1094 if ( opened_frame ) 1095 face->forget_glyph_frame( loader ); 1096 1097 Exit: 1098 return error; 1099 } 1100 1101 1102 static 1103 void compute_glyph_metrics( TT_Loader* loader, 1104 FT_UInt glyph_index ) 1105 { 1106 FT_BBox bbox; 1107 TT_Face face = (TT_Face)loader->face; 1108 FT_Fixed x_scale, y_scale; 1109 TT_GlyphSlot glyph = loader->glyph; 1110 TT_Size size = (TT_Size)loader->size; 1111 1112 1113 x_scale = 0x10000L; 1114 y_scale = 0x10000L; 1115 if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 ) 1116 { 1117 x_scale = size->root.metrics.x_scale; 1118 y_scale = size->root.metrics.y_scale; 1119 } 1120 1121 if ( glyph->format != ft_glyph_format_composite ) 1122 { 1123 glyph->outline.flags &= ~ft_outline_single_pass; 1124 1125 /* copy outline to our glyph slot */ 1126 FT_GlyphLoader_Copy_Points( glyph->loader, loader->gloader ); 1127 glyph->outline = glyph->loader->base.outline; 1128 1129 /* translate array so that (0,0) is the glyph's origin */ 1130 FT_Outline_Translate( &glyph->outline, -loader->pp1.x, 0 ); 1131 1132 FT_Outline_Get_CBox( &glyph->outline, &bbox ); 1133 1134 if ( IS_HINTED( loader->load_flags ) ) 1135 { 1136 /* grid-fit the bounding box */ 1137 bbox.xMin &= -64; 1138 bbox.yMin &= -64; 1139 bbox.xMax = ( bbox.xMax + 63 ) & -64; 1140 bbox.yMax = ( bbox.yMax + 63 ) & -64; 1141 } 1142 } 1143 else 1144 bbox = loader->bbox; 1145 1146 /* get the device-independent horizontal advance. It is scaled later */ 1147 /* by the base layer. */ 1148 { 1149 FT_Pos advance = loader->advance; 1150 1151 1152 /* the flag FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH was introduced to */ 1153 /* correctly support DynaLab fonts, which have an incorrect */ 1154 /* `advance_Width_Max' field! It is used, to my knowledge, */ 1155 /* exclusively in the X-TrueType font server. */ 1156 /* */ 1157 if ( face->postscript.isFixedPitch && 1158 ( loader->load_flags & FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH ) == 0 ) 1159 advance = face->horizontal.advance_Width_Max; 1160 1161 /* we need to return the advance in font units in linearHoriAdvance, */ 1162 /* it will be scaled later by the base layer. */ 1163 glyph->linearHoriAdvance = advance; 1164 } 1165 1166 glyph->metrics.horiBearingX = bbox.xMin; 1167 glyph->metrics.horiBearingY = bbox.yMax; 1168 glyph->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; 1169 1170 /* Now take care of vertical metrics. In the case where there is */ 1171 /* no vertical information within the font (relatively common), make */ 1172 /* up some metrics by `hand'... */ 1173 1174 { 1175 FT_Short top_bearing; /* vertical top side bearing (EM units) */ 1176 FT_UShort advance_height; /* vertical advance height (EM units) */ 1177 1178 FT_Pos left; /* scaled vertical left side bearing */ 1179 FT_Pos Top; /* scaled original vertical top side bearing */ 1180 FT_Pos top; /* scaled vertical top side bearing */ 1181 FT_Pos advance; /* scaled vertical advance height */ 1182 1183 1184 /* Get the unscaled `tsb' and `ah' */ 1185 if ( face->vertical_info && 1186 face->vertical.number_Of_VMetrics > 0 ) 1187 { 1188 /* Don't assume that both the vertical header and vertical */ 1189 /* metrics are present in the same font :-) */ 1190 1191 TT_Get_Metrics( (TT_HoriHeader*)&face->vertical, 1192 glyph_index, 1193 &top_bearing, 1194 &advance_height ); 1195 } 1196 else 1197 { 1198 /* Make up the distances from the horizontal header. */ 1199 1200 /* NOTE: The OS/2 values are the only `portable' ones, */ 1201 /* which is why we use them, if there is an OS/2 */ 1202 /* table in the font. Otherwise, we use the */ 1203 /* values defined in the horizontal header. */ 1204 /* */ 1205 /* NOTE2: The sTypoDescender is negative, which is why */ 1206 /* we compute the baseline-to-baseline distance */ 1207 /* here with: */ 1208 /* ascender - descender + linegap */ 1209 /* */ 1210 if ( face->os2.version != 0xFFFF ) 1211 { 1212 top_bearing = face->os2.sTypoLineGap / 2; 1213 advance_height = (FT_UShort)( face->os2.sTypoAscender - 1214 face->os2.sTypoDescender + 1215 face->os2.sTypoLineGap ); 1216 } 1217 else 1218 { 1219 top_bearing = face->horizontal.Line_Gap / 2; 1220 advance_height = (FT_UShort)( face->horizontal.Ascender + 1221 face->horizontal.Descender + 1222 face->horizontal.Line_Gap ); 1223 } 1224 } 1225 1226 /* We must adjust the top_bearing value from the bounding box given */ 1227 /* in the glyph header to te bounding box calculated with */ 1228 /* FT_Get_Outline_CBox(). */ 1229 1230 /* scale the metrics */ 1231 if ( !( loader->load_flags & FT_LOAD_NO_SCALE ) ) 1232 { 1233 Top = FT_MulFix( top_bearing, y_scale ); 1234 top = FT_MulFix( top_bearing + loader->bbox.yMax, y_scale ) 1235 - bbox.yMax; 1236 advance = FT_MulFix( advance_height, y_scale ); 1237 } 1238 else 1239 { 1240 Top = top_bearing; 1241 top = top_bearing + loader->bbox.yMax - bbox.yMax; 1242 advance = advance_height; 1243 } 1244 1245 /* set the advance height in design units. It is scaled later by */ 1246 /* the base layer. */ 1247 glyph->linearVertAdvance = advance_height; 1248 1249 /* XXX: for now, we have no better algorithm for the lsb, but it */ 1250 /* should work fine. */ 1251 /* */ 1252 left = ( bbox.xMin - bbox.xMax ) / 2; 1253 1254 /* grid-fit them if necessary */ 1255 if ( IS_HINTED( loader->load_flags ) ) 1256 { 1257 left &= -64; 1258 top = ( top + 63 ) & -64; 1259 advance = ( advance + 32 ) & -64; 1260 } 1261 1262 glyph->metrics.vertBearingX = left; 1263 glyph->metrics.vertBearingY = top; 1264 glyph->metrics.vertAdvance = advance; 1265 } 1266 1267 /* adjust advance width to the value contained in the hdmx table */ 1268 if ( !face->postscript.isFixedPitch && size && 1269 IS_HINTED( loader->load_flags ) ) 1270 { 1271 FT_Byte* widths = Get_Advance_Widths( face, 1272 size->root.metrics.x_ppem ); 1273 1274 1275 if ( widths ) 1276 glyph->metrics.horiAdvance = widths[glyph_index] << 6; 1277 } 1278 1279 /* set glyph dimensions */ 1280 glyph->metrics.width = bbox.xMax - bbox.xMin; 1281 glyph->metrics.height = bbox.yMax - bbox.yMin; 1282 } 1283 1284 1285 /*************************************************************************/ 1286 /* */ 1287 /* <Function> */ 1288 /* TT_Load_Glyph */ 1289 /* */ 1290 /* <Description> */ 1291 /* A function used to load a single glyph within a given glyph slot, */ 1292 /* for a given size. */ 1293 /* */ 1294 /* <Input> */ 1295 /* glyph :: A handle to a target slot object where the glyph */ 1296 /* will be loaded. */ 1297 /* */ 1298 /* size :: A handle to the source face size at which the glyph */ 1299 /* must be scaled/loaded. */ 1300 /* */ 1301 /* glyph_index :: The index of the glyph in the font file. */ 1302 /* */ 1303 /* load_flags :: A flag indicating what to load for this glyph. The */ 1304 /* FT_LOAD_XXX constants can be used to control the */ 1305 /* glyph loading process (e.g., whether the outline */ 1306 /* should be scaled, whether to load bitmaps or not, */ 1307 /* whether to hint the outline, etc). */ 1308 /* */ 1309 /* <Return> */ 1310 /* FreeType error code. 0 means success. */ 1311 /* */ 1312 LOCAL_FUNC 1313 FT_Error TT_Load_Glyph( TT_Size size, 1314 TT_GlyphSlot glyph, 1315 FT_UShort glyph_index, 1316 FT_UInt load_flags ) 1317 { 1318 SFNT_Interface* sfnt; 1319 TT_Face face; 1320 FT_Stream stream; 1321 FT_Memory memory; 1322 FT_Error error; 1323 TT_Loader loader; 1324 1325 1326 face = (TT_Face)glyph->face; 1327 sfnt = (SFNT_Interface*)face->sfnt; 1328 stream = face->root.stream; 1329 memory = face->root.memory; 1330 error = 0; 1331 1332 if ( !size || ( load_flags & FT_LOAD_NO_SCALE ) || 1333 ( load_flags & FT_LOAD_NO_RECURSE ) ) 1334 { 1335 size = NULL; 1336 load_flags |= FT_LOAD_NO_SCALE | 1337 FT_LOAD_NO_HINTING | 1338 FT_LOAD_NO_BITMAP; 1339 } 1340 1341 glyph->num_subglyphs = 0; 1342 1343#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS 1344 1345 /* try to load embedded bitmap if any */ 1346 if ( size && 1347 ( load_flags & FT_LOAD_NO_BITMAP ) == 0 && 1348 sfnt->load_sbits ) 1349 { 1350 TT_SBit_Metrics metrics; 1351 1352 1353 error = sfnt->load_sbit_image( face, 1354 size->root.metrics.x_ppem, 1355 size->root.metrics.y_ppem, 1356 glyph_index, 1357 load_flags, 1358 stream, 1359 &glyph->bitmap, 1360 &metrics ); 1361 if ( !error ) 1362 { 1363 glyph->outline.n_points = 0; 1364 glyph->outline.n_contours = 0; 1365 1366 glyph->metrics.width = (FT_Pos)metrics.width << 6; 1367 glyph->metrics.height = (FT_Pos)metrics.height << 6; 1368 1369 glyph->metrics.horiBearingX = (FT_Pos)metrics.horiBearingX << 6; 1370 glyph->metrics.horiBearingY = (FT_Pos)metrics.horiBearingY << 6; 1371 glyph->metrics.horiAdvance = (FT_Pos)metrics.horiAdvance << 6; 1372 1373 glyph->metrics.vertBearingX = (FT_Pos)metrics.vertBearingX << 6; 1374 glyph->metrics.vertBearingY = (FT_Pos)metrics.vertBearingY << 6; 1375 glyph->metrics.vertAdvance = (FT_Pos)metrics.vertAdvance << 6; 1376 1377 glyph->format = ft_glyph_format_bitmap; 1378 if ( load_flags & FT_LOAD_VERTICAL_LAYOUT ) 1379 { 1380 glyph->bitmap_left = metrics.vertBearingX; 1381 glyph->bitmap_top = metrics.vertBearingY; 1382 } 1383 else 1384 { 1385 glyph->bitmap_left = metrics.horiBearingX; 1386 glyph->bitmap_top = metrics.horiBearingY; 1387 } 1388 return error; 1389 } 1390 } 1391 1392#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ 1393 1394 /* seek to the beginning of the glyph table. For Type 42 fonts */ 1395 /* the table might be accessed from a Postscript stream or something */ 1396 /* else... */ 1397 1398 error = face->goto_table( face, TTAG_glyf, stream, 0 ); 1399 if ( error ) 1400 { 1401 FT_ERROR(( "TT_Load_Glyph: could not access glyph table\n" )); 1402 goto Exit; 1403 } 1404 1405 MEM_Set( &loader, 0, sizeof ( loader ) ); 1406 1407 /* update the glyph zone bounds */ 1408 { 1409 FT_GlyphLoader* gloader = FT_FACE_DRIVER(face)->glyph_loader; 1410 1411 1412 loader.gloader = gloader; 1413 1414 FT_GlyphLoader_Rewind( gloader ); 1415 1416 tt_prepare_zone( &loader.zone, &gloader->base, 0, 0 ); 1417 tt_prepare_zone( &loader.base, &gloader->base, 0, 0 ); 1418 } 1419 1420#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER 1421 1422 if ( size ) 1423 { 1424 /* query new execution context */ 1425 loader.exec = size->debug ? size->context : TT_New_Context( face ); 1426 if ( !loader.exec ) 1427 return TT_Err_Could_Not_Find_Context; 1428 1429 TT_Load_Context( loader.exec, face, size ); 1430 loader.instructions = loader.exec->glyphIns; 1431 1432 /* load default graphics state - if needed */ 1433 if ( size->GS.instruct_control & 2 ) 1434 loader.exec->GS = tt_default_graphics_state; 1435 } 1436 1437#endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ 1438 1439 /* clear all outline flags, except the `owner' one */ 1440 glyph->outline.flags = 0; 1441 1442 if ( size && size->root.metrics.y_ppem < 24 ) 1443 glyph->outline.flags |= ft_outline_high_precision; 1444 1445 /* let's initialize the rest of our loader now */ 1446 1447 loader.load_flags = load_flags; 1448 1449 loader.face = (FT_Face)face; 1450 loader.size = (FT_Size)size; 1451 loader.glyph = (FT_GlyphSlot)glyph; 1452 loader.stream = stream; 1453 1454 loader.glyf_offset = FILE_Pos(); 1455 1456#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER 1457 1458 /* if the cvt program has disabled hinting, the argument */ 1459 /* is ignored. */ 1460 if ( size && ( size->GS.instruct_control & 1 ) ) 1461 loader.load_flags |= FT_LOAD_NO_HINTING; 1462 1463#endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ 1464 1465 /* Main loading loop */ 1466 glyph->format = ft_glyph_format_outline; 1467 glyph->num_subglyphs = 0; 1468 error = load_truetype_glyph( &loader, glyph_index ); 1469 if ( !error ) 1470 compute_glyph_metrics( &loader, glyph_index ); 1471 1472#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER 1473 1474 if ( !size || !size->debug ) 1475 TT_Done_Context( loader.exec ); 1476 1477#endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ 1478 1479 Exit: 1480 return error; 1481 } 1482 1483 1484/* END */ 1485