ttobjs.c (39580B)
1 /**************************************************************************** 2 * 3 * ttobjs.c 4 * 5 * Objects manager (body). 6 * 7 * Copyright (C) 1996-2025 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/ftstream.h> 21 #include <freetype/tttags.h> 22 #include <freetype/internal/sfnt.h> 23 #include <freetype/ftdriver.h> 24 25 #include "ttgload.h" 26 #include "ttpload.h" 27 28 #include "tterrors.h" 29 30 #ifdef TT_USE_BYTECODE_INTERPRETER 31 #include "ttinterp.h" 32 #endif 33 34 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT 35 #include "ttgxvar.h" 36 #endif 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 ttobjs 46 47 48 #ifdef TT_USE_BYTECODE_INTERPRETER 49 50 /************************************************************************** 51 * 52 * GLYPH ZONE FUNCTIONS 53 * 54 */ 55 56 57 /************************************************************************** 58 * 59 * @Function: 60 * tt_glyphzone_done 61 * 62 * @Description: 63 * Deallocate a glyph zone. 64 * 65 * @Input: 66 * zone :: 67 * A pointer to the target glyph zone. 68 */ 69 FT_LOCAL_DEF( void ) 70 tt_glyphzone_done( FT_Memory memory, 71 TT_GlyphZone zone ) 72 { 73 FT_FREE( zone->org ); 74 75 zone->n_points = 0; 76 zone->n_contours = 0; 77 } 78 79 80 /************************************************************************** 81 * 82 * @Function: 83 * tt_glyphzone_new 84 * 85 * @Description: 86 * Allocate a new glyph zone. 87 * 88 * @Input: 89 * memory :: 90 * A handle to the current memory object. 91 * 92 * maxPoints :: 93 * The capacity of glyph zone in points. 94 * 95 * maxContours :: 96 * The capacity of glyph zone in contours. 97 * 98 * @Output: 99 * zone :: 100 * A pointer to the target glyph zone record. 101 * 102 * @Return: 103 * FreeType error code. 0 means success. 104 */ 105 FT_LOCAL_DEF( FT_Error ) 106 tt_glyphzone_new( FT_Memory memory, 107 FT_UShort maxPoints, 108 FT_UShort maxContours, 109 TT_GlyphZone zone ) 110 { 111 FT_Error error; 112 FT_Long size = 3 * maxPoints * sizeof ( FT_Vector ) + 113 maxContours * sizeof ( FT_UShort ) + 114 maxPoints * sizeof ( FT_Byte ); 115 116 117 if ( !FT_ALLOC( zone->org, size ) ) 118 { 119 zone->n_points = maxPoints; 120 zone->n_contours = maxContours; 121 122 zone->cur = zone->org + maxPoints; 123 zone->orus = zone->cur + maxPoints; 124 zone->contours = (FT_UShort*)( zone->orus + maxPoints ); 125 zone->tags = (FT_Byte*)( zone->contours + maxContours ); 126 127 zone->first_point = 0; 128 } 129 130 return error; 131 } 132 133 134 /* 135 * Fonts embedded in PDFs are made unique by prepending randomization 136 * prefixes to their names: as defined in Section 5.5.3, 'Font Subsets', 137 * of the PDF Reference, they consist of 6 uppercase letters followed by 138 * the `+` sign. For safety, we do not skip prefixes violating this rule. 139 */ 140 141 static const FT_String* 142 tt_skip_pdffont_random_tag( const FT_String* name ) 143 { 144 if ( ft_isupper( name[0] ) && 145 ft_isupper( name[1] ) && 146 ft_isupper( name[2] ) && 147 ft_isupper( name[3] ) && 148 ft_isupper( name[4] ) && 149 ft_isupper( name[5] ) && 150 '+' == name[6] && 151 name[7] ) 152 { 153 FT_TRACE7(( "name without randomization tag: %s\n", name + 7 )); 154 return name + 7; 155 } 156 157 return name; 158 } 159 160 161 /* Compare the face with a list of well-known `tricky' fonts. */ 162 /* This list shall be expanded as we find more of them. */ 163 164 static FT_Bool 165 tt_check_trickyness_family( const FT_String* name ) 166 { 167 168 #define TRICK_NAMES_MAX_CHARACTERS 19 169 #define TRICK_NAMES_COUNT 20 170 171 static const char trick_names[TRICK_NAMES_COUNT] 172 [TRICK_NAMES_MAX_CHARACTERS + 1] = 173 { 174 /* 175 PostScript names are given in brackets if they differ from the 176 family name. The version numbers, together with the copyright or 177 release year data, are taken from fonts available to the 178 developers. 179 180 Note that later versions of the fonts might be no longer tricky; 181 for example, `MingLiU' version 7.00 (file `mingliu.ttc' from 182 Windows 7) is an ordinary TTC with non-tricky subfonts. 183 */ 184 185 "cpop", /* dftt-p7.ttf; version 1.00, 1992 [DLJGyShoMedium] */ 186 "DFGirl-W6-WIN-BF", /* dftt-h6.ttf; version 1.00, 1993 */ 187 "DFGothic-EB", /* DynaLab Inc. 1992-1995 */ 188 "DFGyoSho-Lt", /* DynaLab Inc. 1992-1995 */ 189 "DFHei", /* DynaLab Inc. 1992-1995 [DFHei-Bd-WIN-HK-BF] */ 190 /* covers "DFHei-Md-HK-BF", maybe DynaLab Inc. */ 191 192 "DFHSGothic-W5", /* DynaLab Inc. 1992-1995 */ 193 "DFHSMincho-W3", /* DynaLab Inc. 1992-1995 */ 194 "DFHSMincho-W7", /* DynaLab Inc. 1992-1995 */ 195 "DFKaiSho-SB", /* dfkaisb.ttf */ 196 "DFKaiShu", /* covers "DFKaiShu-Md-HK-BF", maybe DynaLab Inc. */ 197 "DFKai-SB", /* kaiu.ttf; version 3.00, 1998 [DFKaiShu-SB-Estd-BF] */ 198 199 "DFMing", /* DynaLab Inc. 1992-1995 [DFMing-Md-WIN-HK-BF] */ 200 /* covers "DFMing-Bd-HK-BF", maybe DynaLab Inc. */ 201 202 "DLC", /* dftt-m7.ttf; version 1.00, 1993 [DLCMingBold] */ 203 /* dftt-f5.ttf; version 1.00, 1993 [DLCFongSung] */ 204 /* covers following */ 205 /* "DLCHayMedium", dftt-b5.ttf; version 1.00, 1993 */ 206 /* "DLCHayBold", dftt-b7.ttf; version 1.00, 1993 */ 207 /* "DLCKaiMedium", dftt-k5.ttf; version 1.00, 1992 */ 208 /* "DLCLiShu", dftt-l5.ttf; version 1.00, 1992 */ 209 /* "DLCRoundBold", dftt-r7.ttf; version 1.00, 1993 */ 210 211 "HuaTianKaiTi?", /* htkt2.ttf */ 212 "HuaTianSongTi?", /* htst3.ttf */ 213 "Ming(for ISO10646)", /* hkscsiic.ttf; version 0.12, 2007 [Ming] */ 214 /* iicore.ttf; version 0.07, 2007 [Ming] */ 215 "MingLiU", /* mingliu.ttf */ 216 /* mingliu.ttc; version 3.21, 2001 */ 217 "MingMedium", /* dftt-m5.ttf; version 1.00, 1993 [DLCMingMedium] */ 218 "PMingLiU", /* mingliu.ttc; version 3.21, 2001 */ 219 "MingLi43", /* mingli.ttf; version 1.00, 1992 */ 220 }; 221 222 int nn; 223 const FT_String* name_without_tag; 224 225 226 name_without_tag = tt_skip_pdffont_random_tag( name ); 227 for ( nn = 0; nn < TRICK_NAMES_COUNT; nn++ ) 228 if ( ft_strstr( name_without_tag, trick_names[nn] ) ) 229 return TRUE; 230 231 return FALSE; 232 } 233 234 235 /* XXX: This function should be in the `sfnt' module. */ 236 237 /* Some PDF generators clear the checksums in the TrueType header table. */ 238 /* For example, Quartz ContextPDF clears all entries, or Bullzip PDF */ 239 /* Printer clears the entries for subsetted subtables. We thus have to */ 240 /* recalculate the checksums where necessary. */ 241 242 static FT_UInt32 243 tt_synth_sfnt_checksum( FT_Stream stream, 244 FT_ULong length ) 245 { 246 FT_Error error; 247 FT_UInt32 checksum = 0; 248 FT_Byte* p; 249 FT_Int shift; 250 251 252 if ( FT_FRAME_ENTER( length ) ) 253 return 0; 254 255 p = (FT_Byte*)stream->cursor; 256 257 for ( ; length > 3; length -= 4 ) 258 checksum += FT_NEXT_ULONG( p ); 259 260 for ( shift = 24; length > 0; length--, shift -=8 ) 261 checksum += (FT_UInt32)FT_NEXT_BYTE( p ) << shift; 262 263 FT_FRAME_EXIT(); 264 265 return checksum; 266 } 267 268 269 /* XXX: This function should be in the `sfnt' module. */ 270 271 static FT_ULong 272 tt_get_sfnt_checksum( TT_Face face, 273 FT_UShort i ) 274 { 275 #if 0 /* if we believe the written value, use following part. */ 276 if ( face->dir_tables[i].CheckSum ) 277 return face->dir_tables[i].CheckSum; 278 #endif 279 280 if ( !face->goto_table ) 281 return 0; 282 283 if ( face->goto_table( face, 284 face->dir_tables[i].Tag, 285 face->root.stream, 286 NULL ) ) 287 return 0; 288 289 return (FT_ULong)tt_synth_sfnt_checksum( face->root.stream, 290 face->dir_tables[i].Length ); 291 } 292 293 294 typedef struct tt_sfnt_id_rec_ 295 { 296 FT_ULong CheckSum; 297 FT_ULong Length; 298 299 } tt_sfnt_id_rec; 300 301 302 static FT_Bool 303 tt_check_trickyness_sfnt_ids( TT_Face face ) 304 { 305 #define TRICK_SFNT_IDS_PER_FACE 3 306 #define TRICK_SFNT_IDS_NUM_FACES 31 307 308 static const tt_sfnt_id_rec sfnt_id[TRICK_SFNT_IDS_NUM_FACES] 309 [TRICK_SFNT_IDS_PER_FACE] = 310 { 311 312 #define TRICK_SFNT_ID_cvt 0 313 #define TRICK_SFNT_ID_fpgm 1 314 #define TRICK_SFNT_ID_prep 2 315 316 { /* MingLiU 1995 */ 317 { 0x05BCF058UL, 0x000002E4UL }, /* cvt */ 318 { 0x28233BF1UL, 0x000087C4UL }, /* fpgm */ 319 { 0xA344A1EAUL, 0x000001E1UL } /* prep */ 320 }, 321 { /* MingLiU 1996- */ 322 { 0x05BCF058UL, 0x000002E4UL }, /* cvt */ 323 { 0x28233BF1UL, 0x000087C4UL }, /* fpgm */ 324 { 0xA344A1EBUL, 0x000001E1UL } /* prep */ 325 }, 326 { /* DFGothic-EB */ 327 { 0x12C3EBB2UL, 0x00000350UL }, /* cvt */ 328 { 0xB680EE64UL, 0x000087A7UL }, /* fpgm */ 329 { 0xCE939563UL, 0x00000758UL } /* prep */ 330 }, 331 { /* DFGyoSho-Lt */ 332 { 0x11E5EAD4UL, 0x00000350UL }, /* cvt */ 333 { 0xCE5956E9UL, 0x0000BC85UL }, /* fpgm */ 334 { 0x8272F416UL, 0x00000045UL } /* prep */ 335 }, 336 { /* DFHei-Md-HK-BF */ 337 { 0x1257EB46UL, 0x00000350UL }, /* cvt */ 338 { 0xF699D160UL, 0x0000715FUL }, /* fpgm */ 339 { 0xD222F568UL, 0x000003BCUL } /* prep */ 340 }, 341 { /* DFHSGothic-W5 */ 342 { 0x1262EB4EUL, 0x00000350UL }, /* cvt */ 343 { 0xE86A5D64UL, 0x00007940UL }, /* fpgm */ 344 { 0x7850F729UL, 0x000005FFUL } /* prep */ 345 }, 346 { /* DFHSMincho-W3 */ 347 { 0x122DEB0AUL, 0x00000350UL }, /* cvt */ 348 { 0x3D16328AUL, 0x0000859BUL }, /* fpgm */ 349 { 0xA93FC33BUL, 0x000002CBUL } /* prep */ 350 }, 351 { /* DFHSMincho-W7 */ 352 { 0x125FEB26UL, 0x00000350UL }, /* cvt */ 353 { 0xA5ACC982UL, 0x00007EE1UL }, /* fpgm */ 354 { 0x90999196UL, 0x0000041FUL } /* prep */ 355 }, 356 { /* DFKaiShu */ 357 { 0x11E5EAD4UL, 0x00000350UL }, /* cvt */ 358 { 0x5A30CA3BUL, 0x00009063UL }, /* fpgm */ 359 { 0x13A42602UL, 0x0000007EUL } /* prep */ 360 }, 361 { /* DFKaiShu, variant */ 362 { 0x11E5EAD4UL, 0x00000350UL }, /* cvt */ 363 { 0xA6E78C01UL, 0x00008998UL }, /* fpgm */ 364 { 0x13A42602UL, 0x0000007EUL } /* prep */ 365 }, 366 { /* DFKaiShu-Md-HK-BF */ 367 { 0x11E5EAD4UL, 0x00000360UL }, /* cvt */ 368 { 0x9DB282B2UL, 0x0000C06EUL }, /* fpgm */ 369 { 0x53E6D7CAUL, 0x00000082UL } /* prep */ 370 }, 371 { /* DFMing-Bd-HK-BF */ 372 { 0x1243EB18UL, 0x00000350UL }, /* cvt */ 373 { 0xBA0A8C30UL, 0x000074ADUL }, /* fpgm */ 374 { 0xF3D83409UL, 0x0000037BUL } /* prep */ 375 }, 376 { /* DLCLiShu */ 377 { 0x07DCF546UL, 0x00000308UL }, /* cvt */ 378 { 0x40FE7C90UL, 0x00008E2AUL }, /* fpgm */ 379 { 0x608174B5UL, 0x0000007AUL } /* prep */ 380 }, 381 { /* DLCHayBold */ 382 { 0xEB891238UL, 0x00000308UL }, /* cvt */ 383 { 0xD2E4DCD4UL, 0x0000676FUL }, /* fpgm */ 384 { 0x8EA5F293UL, 0x000003B8UL } /* prep */ 385 }, 386 { /* HuaTianKaiTi */ 387 { 0xFFFBFFFCUL, 0x00000008UL }, /* cvt */ 388 { 0x9C9E48B8UL, 0x0000BEA2UL }, /* fpgm */ 389 { 0x70020112UL, 0x00000008UL } /* prep */ 390 }, 391 { /* HuaTianSongTi */ 392 { 0xFFFBFFFCUL, 0x00000008UL }, /* cvt */ 393 { 0x0A5A0483UL, 0x00017C39UL }, /* fpgm */ 394 { 0x70020112UL, 0x00000008UL } /* prep */ 395 }, 396 { /* NEC fadpop7.ttf */ 397 { 0x00000000UL, 0x00000000UL }, /* cvt */ 398 { 0x40C92555UL, 0x000000E5UL }, /* fpgm */ 399 { 0xA39B58E3UL, 0x0000117CUL } /* prep */ 400 }, 401 { /* NEC fadrei5.ttf */ 402 { 0x00000000UL, 0x00000000UL }, /* cvt */ 403 { 0x33C41652UL, 0x000000E5UL }, /* fpgm */ 404 { 0x26D6C52AUL, 0x00000F6AUL } /* prep */ 405 }, 406 { /* NEC fangot7.ttf */ 407 { 0x00000000UL, 0x00000000UL }, /* cvt */ 408 { 0x6DB1651DUL, 0x0000019DUL }, /* fpgm */ 409 { 0x6C6E4B03UL, 0x00002492UL } /* prep */ 410 }, 411 { /* NEC fangyo5.ttf */ 412 { 0x00000000UL, 0x00000000UL }, /* cvt */ 413 { 0x40C92555UL, 0x000000E5UL }, /* fpgm */ 414 { 0xDE51FAD0UL, 0x0000117CUL } /* prep */ 415 }, 416 { /* NEC fankyo5.ttf */ 417 { 0x00000000UL, 0x00000000UL }, /* cvt */ 418 { 0x85E47664UL, 0x000000E5UL }, /* fpgm */ 419 { 0xA6C62831UL, 0x00001CAAUL } /* prep */ 420 }, 421 { /* NEC fanrgo5.ttf */ 422 { 0x00000000UL, 0x00000000UL }, /* cvt */ 423 { 0x2D891CFDUL, 0x0000019DUL }, /* fpgm */ 424 { 0xA0604633UL, 0x00001DE8UL } /* prep */ 425 }, 426 { /* NEC fangot5.ttc */ 427 { 0x00000000UL, 0x00000000UL }, /* cvt */ 428 { 0x40AA774CUL, 0x000001CBUL }, /* fpgm */ 429 { 0x9B5CAA96UL, 0x00001F9AUL } /* prep */ 430 }, 431 { /* NEC fanmin3.ttc */ 432 { 0x00000000UL, 0x00000000UL }, /* cvt */ 433 { 0x0D3DE9CBUL, 0x00000141UL }, /* fpgm */ 434 { 0xD4127766UL, 0x00002280UL } /* prep */ 435 }, 436 { /* NEC FA-Gothic, 1996 */ 437 { 0x00000000UL, 0x00000000UL }, /* cvt */ 438 { 0x4A692698UL, 0x000001F0UL }, /* fpgm */ 439 { 0x340D4346UL, 0x00001FCAUL } /* prep */ 440 }, 441 { /* NEC FA-Minchou, 1996 */ 442 { 0x00000000UL, 0x00000000UL }, /* cvt */ 443 { 0xCD34C604UL, 0x00000166UL }, /* fpgm */ 444 { 0x6CF31046UL, 0x000022B0UL } /* prep */ 445 }, 446 { /* NEC FA-RoundGothicB, 1996 */ 447 { 0x00000000UL, 0x00000000UL }, /* cvt */ 448 { 0x5DA75315UL, 0x0000019DUL }, /* fpgm */ 449 { 0x40745A5FUL, 0x000022E0UL } /* prep */ 450 }, 451 { /* NEC FA-RoundGothicM, 1996 */ 452 { 0x00000000UL, 0x00000000UL }, /* cvt */ 453 { 0xF055FC48UL, 0x000001C2UL }, /* fpgm */ 454 { 0x3900DED3UL, 0x00001E18UL } /* prep */ 455 }, 456 { /* MINGLI.TTF, 1992 */ 457 { 0x00170003UL, 0x00000060UL }, /* cvt */ 458 { 0xDBB4306EUL, 0x000058AAUL }, /* fpgm */ 459 { 0xD643482AUL, 0x00000035UL } /* prep */ 460 }, 461 { /* DFHei-Bd-WIN-HK-BF, issue #1087 */ 462 { 0x1269EB58UL, 0x00000350UL }, /* cvt */ 463 { 0x5CD5957AUL, 0x00006A4EUL }, /* fpgm */ 464 { 0xF758323AUL, 0x00000380UL } /* prep */ 465 }, 466 { /* DFMing-Md-WIN-HK-BF, issue #1087 */ 467 { 0x122FEB0BUL, 0x00000350UL }, /* cvt */ 468 { 0x7F10919AUL, 0x000070A9UL }, /* fpgm */ 469 { 0x7CD7E7B7UL, 0x0000025CUL } /* prep */ 470 } 471 }; 472 473 FT_ULong checksum; 474 int num_matched_ids[TRICK_SFNT_IDS_NUM_FACES]; 475 FT_Bool has_cvt, has_fpgm, has_prep; 476 FT_UShort i; 477 int j, k; 478 479 480 FT_ARRAY_ZERO( num_matched_ids, TRICK_SFNT_IDS_NUM_FACES ); 481 has_cvt = FALSE; 482 has_fpgm = FALSE; 483 has_prep = FALSE; 484 485 for ( i = 0; i < face->num_tables; i++ ) 486 { 487 checksum = 0; 488 489 switch( face->dir_tables[i].Tag ) 490 { 491 case TTAG_cvt: 492 k = TRICK_SFNT_ID_cvt; 493 has_cvt = TRUE; 494 break; 495 496 case TTAG_fpgm: 497 k = TRICK_SFNT_ID_fpgm; 498 has_fpgm = TRUE; 499 break; 500 501 case TTAG_prep: 502 k = TRICK_SFNT_ID_prep; 503 has_prep = TRUE; 504 break; 505 506 default: 507 continue; 508 } 509 510 for ( j = 0; j < TRICK_SFNT_IDS_NUM_FACES; j++ ) 511 if ( face->dir_tables[i].Length == sfnt_id[j][k].Length ) 512 { 513 if ( !checksum ) 514 checksum = tt_get_sfnt_checksum( face, i ); 515 516 if ( sfnt_id[j][k].CheckSum == checksum ) 517 num_matched_ids[j]++; 518 519 if ( num_matched_ids[j] == TRICK_SFNT_IDS_PER_FACE ) 520 return TRUE; 521 } 522 } 523 524 for ( j = 0; j < TRICK_SFNT_IDS_NUM_FACES; j++ ) 525 { 526 if ( !has_cvt && !sfnt_id[j][TRICK_SFNT_ID_cvt].Length ) 527 num_matched_ids[j]++; 528 if ( !has_fpgm && !sfnt_id[j][TRICK_SFNT_ID_fpgm].Length ) 529 num_matched_ids[j]++; 530 if ( !has_prep && !sfnt_id[j][TRICK_SFNT_ID_prep].Length ) 531 num_matched_ids[j]++; 532 if ( num_matched_ids[j] == TRICK_SFNT_IDS_PER_FACE ) 533 return TRUE; 534 } 535 536 return FALSE; 537 } 538 539 540 static FT_Bool 541 tt_check_trickyness( FT_Face face ) 542 { 543 if ( !face ) 544 return FALSE; 545 546 /* For first, check the face name for quick check. */ 547 if ( face->family_name && 548 tt_check_trickyness_family( face->family_name ) ) 549 { 550 FT_TRACE3(( "found as a tricky font" 551 " by its family name: %s\n", face->family_name )); 552 return TRUE; 553 } 554 555 /* Type42 fonts may lack `name' tables, we thus try to identify */ 556 /* tricky fonts by checking the checksums of Type42-persistent */ 557 /* sfnt tables (`cvt', `fpgm', and `prep'). */ 558 if ( tt_check_trickyness_sfnt_ids( (TT_Face)face ) ) 559 { 560 FT_TRACE3(( "found as a tricky font" 561 " by its cvt/fpgm/prep table checksum\n" )); 562 return TRUE; 563 } 564 565 return FALSE; 566 } 567 568 #endif /* TT_USE_BYTECODE_INTERPRETER */ 569 570 571 /* Check whether `.notdef' is the only glyph in the `loca' table. */ 572 static FT_Bool 573 tt_check_single_notdef( FT_Face ttface ) 574 { 575 FT_Bool result = FALSE; 576 577 TT_Face face = (TT_Face)ttface; 578 FT_ULong asize; 579 FT_ULong i; 580 FT_ULong glyph_index = 0; 581 FT_UInt count = 0; 582 583 584 for( i = 0; i < face->num_locations; i++ ) 585 { 586 tt_face_get_location( ttface, i, &asize ); 587 if ( asize > 0 ) 588 { 589 count += 1; 590 if ( count > 1 ) 591 break; 592 glyph_index = i; 593 } 594 } 595 596 /* Only have a single outline. */ 597 if ( count == 1 ) 598 { 599 if ( glyph_index == 0 ) 600 result = TRUE; 601 else 602 { 603 /* FIXME: Need to test glyphname == .notdef ? */ 604 FT_Error error; 605 char buf[8]; 606 607 608 error = FT_Get_Glyph_Name( ttface, glyph_index, buf, 8 ); 609 if ( !error && 610 buf[0] == '.' && !ft_strncmp( buf, ".notdef", 8 ) ) 611 result = TRUE; 612 } 613 } 614 615 return result; 616 } 617 618 619 /************************************************************************** 620 * 621 * @Function: 622 * tt_face_init 623 * 624 * @Description: 625 * Initialize a given TrueType face object. 626 * 627 * @Input: 628 * stream :: 629 * The source font stream. 630 * 631 * face_index :: 632 * The index of the TrueType font, if we are opening a 633 * collection, in bits 0-15. The numbered instance 634 * index~+~1 of a GX (sub)font, if applicable, in bits 635 * 16-30. 636 * 637 * num_params :: 638 * Number of additional generic parameters. Ignored. 639 * 640 * params :: 641 * Additional generic parameters. Ignored. 642 * 643 * @InOut: 644 * face :: 645 * The newly built face object. 646 * 647 * @Return: 648 * FreeType error code. 0 means success. 649 */ 650 FT_LOCAL_DEF( FT_Error ) 651 tt_face_init( FT_Stream stream, 652 FT_Face ttface, /* TT_Face */ 653 FT_Int face_index, 654 FT_Int num_params, 655 FT_Parameter* params ) 656 { 657 FT_Error error; 658 FT_Library library; 659 SFNT_Service sfnt; 660 TT_Face face = (TT_Face)ttface; 661 662 663 FT_TRACE2(( "TTF driver\n" )); 664 665 library = ttface->driver->root.library; 666 667 sfnt = (SFNT_Service)FT_Get_Module_Interface( library, "sfnt" ); 668 if ( !sfnt ) 669 { 670 FT_ERROR(( "tt_face_init: cannot access `sfnt' module\n" )); 671 error = FT_THROW( Missing_Module ); 672 goto Exit; 673 } 674 675 /* create input stream from resource */ 676 if ( FT_STREAM_SEEK( 0 ) ) 677 goto Exit; 678 679 /* check that we have a valid TrueType file */ 680 FT_TRACE2(( " " )); 681 error = sfnt->init_face( stream, face, face_index, num_params, params ); 682 683 /* Stream may have changed. */ 684 stream = face->root.stream; 685 686 if ( error ) 687 goto Exit; 688 689 /* We must also be able to accept Mac/GX fonts, as well as OT ones. */ 690 /* The 0x00020000 tag is completely undocumented; some fonts from */ 691 /* Arphic made for Chinese Windows 3.1 have this. */ 692 if ( face->format_tag != 0x00010000L && /* MS fonts */ 693 face->format_tag != 0x00020000L && /* CJK fonts for Win 3.1 */ 694 face->format_tag != TTAG_true && /* Mac fonts */ 695 face->format_tag != TTAG_0xA5kbd && /* `Keyboard.dfont' (legacy Mac OS X) */ 696 face->format_tag != TTAG_0xA5lst ) /* `LastResort.dfont' (legacy Mac OS X) */ 697 { 698 FT_TRACE2(( " not a TTF font\n" )); 699 goto Bad_Format; 700 } 701 702 #ifdef TT_USE_BYTECODE_INTERPRETER 703 ttface->face_flags |= FT_FACE_FLAG_HINTER; 704 #endif 705 706 /* If we are performing a simple font format check, exit immediately. */ 707 if ( face_index < 0 ) 708 return FT_Err_Ok; 709 710 /* Load font directory */ 711 error = sfnt->load_face( stream, face, face_index, num_params, params ); 712 if ( error ) 713 goto Exit; 714 715 #ifdef TT_USE_BYTECODE_INTERPRETER 716 if ( tt_check_trickyness( ttface ) ) 717 ttface->face_flags |= FT_FACE_FLAG_TRICKY; 718 #endif 719 720 error = tt_face_load_hdmx( face, stream ); 721 if ( error ) 722 goto Exit; 723 724 if ( FT_IS_SCALABLE( ttface ) || 725 FT_HAS_SBIX( ttface ) ) 726 { 727 #ifdef FT_CONFIG_OPTION_INCREMENTAL 728 if ( !ttface->internal->incremental_interface ) 729 #endif 730 { 731 error = tt_face_load_loca( face, stream ); 732 733 /* having a (non-zero) `glyf' table without */ 734 /* a `loca' table is not valid */ 735 if ( face->glyf_len && FT_ERR_EQ( error, Table_Missing ) ) 736 goto Exit; 737 if ( error ) 738 goto Exit; 739 } 740 741 /* `fpgm', `cvt', and `prep' are optional */ 742 error = tt_face_load_cvt( face, stream ); 743 if ( error && FT_ERR_NEQ( error, Table_Missing ) ) 744 goto Exit; 745 746 error = tt_face_load_fpgm( face, stream ); 747 if ( error && FT_ERR_NEQ( error, Table_Missing ) ) 748 goto Exit; 749 750 error = tt_face_load_prep( face, stream ); 751 if ( error && FT_ERR_NEQ( error, Table_Missing ) ) 752 goto Exit; 753 754 /* Check the scalable flag based on `loca'. */ 755 #ifdef FT_CONFIG_OPTION_INCREMENTAL 756 if ( !ttface->internal->incremental_interface ) 757 #endif 758 { 759 if ( ttface->num_fixed_sizes && 760 face->glyph_locations && 761 tt_check_single_notdef( ttface ) ) 762 { 763 FT_TRACE5(( "tt_face_init:" 764 " Only the `.notdef' glyph has an outline.\n" )); 765 FT_TRACE5(( " " 766 " Resetting scalable flag to FALSE.\n" )); 767 768 ttface->face_flags &= ~FT_FACE_FLAG_SCALABLE; 769 } 770 } 771 } 772 773 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT 774 { 775 FT_UInt instance_index = (FT_UInt)face_index >> 16; 776 777 778 if ( instance_index && FT_HAS_MULTIPLE_MASTERS( ttface ) ) 779 { 780 error = FT_Set_Named_Instance( ttface, instance_index ); 781 if ( error ) 782 goto Exit; 783 } 784 } 785 #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */ 786 787 /* initialize standard glyph loading routines */ 788 TT_Init_Glyph_Loading( face ); 789 790 Exit: 791 return error; 792 793 Bad_Format: 794 error = FT_THROW( Unknown_File_Format ); 795 goto Exit; 796 } 797 798 799 /************************************************************************** 800 * 801 * @Function: 802 * tt_face_done 803 * 804 * @Description: 805 * Finalize a given face object. 806 * 807 * @Input: 808 * face :: 809 * A pointer to the face object to destroy. 810 */ 811 FT_LOCAL_DEF( void ) 812 tt_face_done( FT_Face ttface ) /* TT_Face */ 813 { 814 TT_Face face = (TT_Face)ttface; 815 FT_Memory memory; 816 FT_Stream stream; 817 SFNT_Service sfnt; 818 819 820 if ( !face ) 821 return; 822 823 memory = ttface->memory; 824 stream = ttface->stream; 825 sfnt = (SFNT_Service)face->sfnt; 826 827 /* for `extended TrueType formats' (i.e. compressed versions) */ 828 if ( face->extra.finalizer ) 829 face->extra.finalizer( face->extra.data ); 830 831 if ( sfnt ) 832 sfnt->done_face( face ); 833 834 /* freeing the locations table */ 835 tt_face_done_loca( face ); 836 837 tt_face_free_hdmx( face ); 838 839 /* freeing the CVT */ 840 FT_FREE( face->cvt ); 841 face->cvt_size = 0; 842 843 /* freeing the programs */ 844 FT_FRAME_RELEASE( face->font_program ); 845 FT_FRAME_RELEASE( face->cvt_program ); 846 face->font_program_size = 0; 847 face->cvt_program_size = 0; 848 849 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT 850 tt_done_blend( ttface ); 851 face->blend = NULL; 852 #endif 853 } 854 855 856 /************************************************************************** 857 * 858 * SIZE FUNCTIONS 859 * 860 */ 861 862 #ifdef TT_USE_BYTECODE_INTERPRETER 863 864 /************************************************************************** 865 * 866 * @Function: 867 * tt_size_run_fpgm 868 * 869 * @Description: 870 * Run the font program. 871 * 872 * @Input: 873 * size :: 874 * A handle to the size object. 875 * 876 * @Return: 877 * FreeType error code. 0 means success. 878 */ 879 FT_LOCAL_DEF( FT_Error ) 880 tt_size_run_fpgm( TT_Size size ) 881 { 882 TT_Face face = (TT_Face)size->root.face; 883 TT_ExecContext exec = size->context; 884 FT_Error error; 885 886 887 error = TT_Load_Context( exec, face, size ); 888 if ( error ) 889 return error; 890 891 /* disable CVT and glyph programs coderange */ 892 TT_Clear_CodeRange( exec, tt_coderange_cvt ); 893 TT_Clear_CodeRange( exec, tt_coderange_glyph ); 894 895 if ( face->font_program_size > 0 ) 896 { 897 /* allow font program execution */ 898 TT_Set_CodeRange( exec, 899 tt_coderange_font, 900 face->font_program, 901 (FT_Long)face->font_program_size ); 902 903 exec->pts.n_points = 0; 904 exec->pts.n_contours = 0; 905 906 FT_TRACE4(( "Executing `fpgm' table.\n" )); 907 error = TT_Run_Context( exec, size ); 908 FT_TRACE4(( error ? " failed (error code 0x%x)\n" : "", 909 error )); 910 } 911 else 912 error = FT_Err_Ok; 913 914 size->bytecode_ready = error; 915 916 if ( !error ) 917 TT_Save_Context( exec, size ); 918 919 return error; 920 } 921 922 923 /************************************************************************** 924 * 925 * @Function: 926 * tt_size_run_prep 927 * 928 * @Description: 929 * Run the control value program. 930 * 931 * @Input: 932 * size :: 933 * A handle to the size object. 934 * 935 * @Return: 936 * FreeType error code. 0 means success. 937 */ 938 FT_LOCAL_DEF( FT_Error ) 939 tt_size_run_prep( TT_Size size ) 940 { 941 TT_Face face = (TT_Face)size->root.face; 942 TT_ExecContext exec = size->context; 943 FT_Error error; 944 FT_UInt i; 945 946 947 /* set default GS, twilight points, and storage */ 948 /* before CV program can modify them. */ 949 size->GS = tt_default_graphics_state; 950 951 /* all twilight points are originally zero */ 952 FT_ARRAY_ZERO( size->twilight.org, size->twilight.n_points ); 953 FT_ARRAY_ZERO( size->twilight.cur, size->twilight.n_points ); 954 955 error = TT_Load_Context( exec, face, size ); 956 if ( error ) 957 return error; 958 959 /* clear storage area */ 960 FT_ARRAY_ZERO( exec->storage, exec->storeSize ); 961 962 /* Scale the cvt values to the new ppem. */ 963 /* By default, we use the y ppem value for scaling. */ 964 FT_TRACE6(( "CVT values:\n" )); 965 for ( i = 0; i < exec->cvtSize; i++ ) 966 { 967 /* Unscaled CVT values are already stored in 26.6 format. */ 968 /* Note that this scaling operation is very sensitive to rounding; */ 969 /* the integer division by 64 must be applied to the first argument. */ 970 exec->cvt[i] = FT_MulFix( face->cvt[i] / 64, size->ttmetrics.scale ); 971 FT_TRACE6(( " %3u: %f (%f)\n", 972 i, (double)face->cvt[i] / 64, (double)exec->cvt[i] / 64 )); 973 } 974 FT_TRACE6(( "\n" )); 975 976 TT_Clear_CodeRange( exec, tt_coderange_glyph ); 977 978 if ( face->cvt_program_size > 0 ) 979 { 980 /* allow CV program execution */ 981 TT_Set_CodeRange( exec, 982 tt_coderange_cvt, 983 face->cvt_program, 984 (FT_Long)face->cvt_program_size ); 985 986 exec->pts.n_points = 0; 987 exec->pts.n_contours = 0; 988 989 FT_TRACE4(( "Executing `prep' table.\n" )); 990 error = TT_Run_Context( exec, size ); 991 FT_TRACE4(( error ? " failed (error code 0x%x)\n" : "", 992 error )); 993 } 994 else 995 error = FT_Err_Ok; 996 997 size->cvt_ready = error; 998 999 if ( !error ) 1000 TT_Save_Context( exec, size ); 1001 1002 return error; 1003 } 1004 1005 1006 static void 1007 tt_size_done_bytecode( TT_Size size ) 1008 { 1009 FT_Memory memory = size->root.face->memory; 1010 TT_ExecContext exec = size->context; 1011 1012 1013 if ( exec ) 1014 { 1015 FT_FREE( exec->stack ); 1016 FT_FREE( exec->FDefs ); 1017 1018 TT_Done_Context( exec ); 1019 size->context = NULL; 1020 } 1021 1022 /* twilight zone */ 1023 tt_glyphzone_done( memory, &size->twilight ); 1024 } 1025 1026 1027 /* Initialize bytecode-related fields in the size object. */ 1028 /* We do this only if bytecode interpretation is really needed. */ 1029 FT_LOCAL_DEF( FT_Error ) 1030 tt_size_init_bytecode( TT_Size size, 1031 FT_Bool pedantic ) 1032 { 1033 FT_Error error; 1034 TT_Face face = (TT_Face)size->root.face; 1035 FT_Memory memory = size->root.face->memory; 1036 1037 FT_UShort n_twilight; 1038 TT_MaxProfile* maxp = &face->max_profile; 1039 TT_ExecContext exec; 1040 1041 1042 exec = TT_New_Context( (TT_Driver)face->root.driver ); 1043 if ( !exec ) 1044 return FT_THROW( Could_Not_Find_Context ); 1045 1046 exec->pedantic_hinting = pedantic; 1047 1048 exec->maxFDefs = maxp->maxFunctionDefs; 1049 exec->maxIDefs = maxp->maxInstructionDefs; 1050 1051 if ( FT_NEW_ARRAY( exec->FDefs, exec->maxFDefs + exec->maxIDefs ) ) 1052 goto Exit; 1053 1054 exec->IDefs = exec->FDefs + exec->maxFDefs; 1055 1056 exec->numFDefs = 0; 1057 exec->numIDefs = 0; 1058 1059 exec->maxFunc = 0; 1060 exec->maxIns = 0; 1061 1062 /* XXX: We reserve a little more elements on the stack to deal */ 1063 /* with broken fonts like arialbs, courbs, timesbs, etc. */ 1064 exec->stackSize = maxp->maxStackElements + 32; 1065 exec->storeSize = maxp->maxStorage; 1066 exec->cvtSize = face->cvt_size; 1067 1068 if ( FT_NEW_ARRAY( exec->stack, 1069 exec->stackSize + 1070 (FT_Long)( exec->storeSize + exec->cvtSize ) ) ) 1071 goto Exit; 1072 1073 /* reserve twilight zone and set GS before fpgm is executed, */ 1074 /* just in case, even though fpgm should not touch them */ 1075 n_twilight = maxp->maxTwilightPoints; 1076 1077 /* there are 4 phantom points (do we need this?) */ 1078 n_twilight += 4; 1079 1080 error = tt_glyphzone_new( memory, n_twilight, 0, &size->twilight ); 1081 if ( error ) 1082 goto Exit; 1083 1084 size->GS = tt_default_graphics_state; 1085 size->cvt_ready = -1; 1086 size->context = exec; 1087 1088 size->ttmetrics.rotated = FALSE; 1089 size->ttmetrics.stretched = FALSE; 1090 1091 /* Fine, now run the font program! */ 1092 1093 /* In case of an error while executing `fpgm', we intentionally don't */ 1094 /* clean up immediately – bugs in the `fpgm' are so fundamental that */ 1095 /* all following hinting calls should fail. Additionally, `fpgm' is */ 1096 /* to be executed just once; calling it again is completely useless */ 1097 /* and might even lead to extremely slow behaviour if it is malformed */ 1098 /* (containing an infinite loop, for example). */ 1099 error = tt_size_run_fpgm( size ); 1100 return error; 1101 1102 Exit: 1103 if ( error ) 1104 tt_size_done_bytecode( size ); 1105 1106 return error; 1107 } 1108 1109 #endif /* TT_USE_BYTECODE_INTERPRETER */ 1110 1111 1112 /************************************************************************** 1113 * 1114 * @Function: 1115 * tt_size_init 1116 * 1117 * @Description: 1118 * Initialize a new TrueType size object. 1119 * 1120 * @InOut: 1121 * size :: 1122 * A handle to the size object. 1123 * 1124 * @Return: 1125 * FreeType error code. 0 means success. 1126 */ 1127 FT_LOCAL_DEF( FT_Error ) 1128 tt_size_init( FT_Size ttsize ) /* TT_Size */ 1129 { 1130 TT_Size size = (TT_Size)ttsize; 1131 FT_Error error = FT_Err_Ok; 1132 1133 1134 #ifdef TT_USE_BYTECODE_INTERPRETER 1135 size->bytecode_ready = -1; 1136 #endif 1137 1138 size->strike_index = 0xFFFFFFFFUL; 1139 1140 return error; 1141 } 1142 1143 1144 /************************************************************************** 1145 * 1146 * @Function: 1147 * tt_size_done 1148 * 1149 * @Description: 1150 * The TrueType size object finalizer. 1151 * 1152 * @Input: 1153 * size :: 1154 * A handle to the target size object. 1155 */ 1156 FT_LOCAL_DEF( void ) 1157 tt_size_done( FT_Size ttsize ) /* TT_Size */ 1158 { 1159 #ifdef TT_USE_BYTECODE_INTERPRETER 1160 tt_size_done_bytecode( (TT_Size)ttsize ); 1161 #else 1162 FT_UNUSED( ttsize ); 1163 #endif 1164 } 1165 1166 1167 /************************************************************************** 1168 * 1169 * @Function: 1170 * tt_size_reset_height 1171 * 1172 * @Description: 1173 * Recompute a TrueType size's ascender, descender, and height 1174 * when resolutions and character dimensions have been changed. 1175 * Used for variation fonts as an iterator function. 1176 * 1177 * @Input: 1178 * ft_size :: 1179 * A handle to the target TT_Size object. This function will be called 1180 * through a `FT_Size_Reset_Func` pointer which takes `FT_Size`. This 1181 * function must take `FT_Size` as a result. The passed `FT_Size` is 1182 * expected to point to a `TT_Size`. 1183 */ 1184 FT_LOCAL_DEF( void ) 1185 tt_size_reset_height( FT_Size ft_size ) 1186 { 1187 TT_Size size = (TT_Size)ft_size; 1188 TT_Face face = (TT_Face)ft_size->face; 1189 FT_Size_Metrics* size_metrics = &size->hinted_metrics; 1190 1191 /* This bit flag, if set, indicates that the ppems must be */ 1192 /* rounded to integers. Nearly all TrueType fonts have this bit */ 1193 /* set, as hinting won't work really well otherwise. */ 1194 /* */ 1195 if ( face->header.Flags & 8 ) 1196 { 1197 /* the TT spec always asks for ROUND, not FLOOR or CEIL */ 1198 size_metrics->ascender = FT_PIX_ROUND( 1199 FT_MulFix( face->root.ascender, 1200 size_metrics->y_scale ) ); 1201 size_metrics->descender = FT_PIX_ROUND( 1202 FT_MulFix( face->root.descender, 1203 size_metrics->y_scale ) ); 1204 size_metrics->height = FT_PIX_ROUND( 1205 FT_MulFix( face->root.height, 1206 size_metrics->y_scale ) ); 1207 } 1208 } 1209 1210 1211 /************************************************************************** 1212 * 1213 * @Function: 1214 * tt_size_reset 1215 * 1216 * @Description: 1217 * Reset a TrueType size when resolutions and character dimensions 1218 * have been changed. 1219 * 1220 * @Input: 1221 * size :: 1222 * A handle to the target size object. 1223 */ 1224 FT_LOCAL_DEF( FT_Error ) 1225 tt_size_reset( TT_Size size ) 1226 { 1227 TT_Face face = (TT_Face)size->root.face; 1228 FT_Size_Metrics* size_metrics = &size->hinted_metrics; 1229 1230 1231 /* invalidate the size object first */ 1232 size->ttmetrics.ppem = 0; 1233 1234 if ( size->root.metrics.x_ppem == 0 || size->root.metrics.y_ppem == 0 ) 1235 return FT_THROW( Invalid_PPem ); 1236 1237 /* copy the result from base layer */ 1238 *size_metrics = size->root.metrics; 1239 1240 tt_size_reset_height( (FT_Size)size ); 1241 1242 if ( face->header.Flags & 8 ) 1243 { 1244 /* base scaling values on integer ppem values, */ 1245 /* as mandated by the TrueType specification */ 1246 size_metrics->x_scale = FT_DivFix( size_metrics->x_ppem << 6, 1247 face->root.units_per_EM ); 1248 size_metrics->y_scale = FT_DivFix( size_metrics->y_ppem << 6, 1249 face->root.units_per_EM ); 1250 1251 size_metrics->max_advance = FT_PIX_ROUND( 1252 FT_MulFix( face->root.max_advance_width, 1253 size_metrics->x_scale ) ); 1254 } 1255 1256 /* compute new transformation */ 1257 if ( size_metrics->x_ppem >= size_metrics->y_ppem ) 1258 { 1259 size->ttmetrics.scale = size_metrics->x_scale; 1260 size->ttmetrics.ppem = size_metrics->x_ppem; 1261 size->ttmetrics.x_ratio = 0x10000L; 1262 size->ttmetrics.y_ratio = FT_DivFix( size_metrics->y_ppem, 1263 size_metrics->x_ppem ); 1264 } 1265 else 1266 { 1267 size->ttmetrics.scale = size_metrics->y_scale; 1268 size->ttmetrics.ppem = size_metrics->y_ppem; 1269 size->ttmetrics.x_ratio = FT_DivFix( size_metrics->x_ppem, 1270 size_metrics->y_ppem ); 1271 size->ttmetrics.y_ratio = 0x10000L; 1272 } 1273 1274 size->widthp = tt_face_get_device_metrics( face, size_metrics->x_ppem, 0 ); 1275 1276 size->metrics = size_metrics; 1277 1278 #ifdef TT_USE_BYTECODE_INTERPRETER 1279 size->cvt_ready = -1; 1280 #endif /* TT_USE_BYTECODE_INTERPRETER */ 1281 1282 return FT_Err_Ok; 1283 } 1284 1285 1286 /************************************************************************** 1287 * 1288 * @Function: 1289 * tt_driver_init 1290 * 1291 * @Description: 1292 * Initialize a given TrueType driver object. 1293 * 1294 * @Input: 1295 * driver :: 1296 * A handle to the target driver object. 1297 * 1298 * @Return: 1299 * FreeType error code. 0 means success. 1300 */ 1301 FT_LOCAL_DEF( FT_Error ) 1302 tt_driver_init( FT_Module ttdriver ) /* TT_Driver */ 1303 { 1304 1305 #ifdef TT_USE_BYTECODE_INTERPRETER 1306 1307 TT_Driver driver = (TT_Driver)ttdriver; 1308 1309 driver->interpreter_version = TT_INTERPRETER_VERSION_35; 1310 #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL 1311 driver->interpreter_version = TT_INTERPRETER_VERSION_40; 1312 #endif 1313 1314 #else /* !TT_USE_BYTECODE_INTERPRETER */ 1315 1316 FT_UNUSED( ttdriver ); 1317 1318 #endif /* !TT_USE_BYTECODE_INTERPRETER */ 1319 1320 return FT_Err_Ok; 1321 } 1322 1323 1324 /************************************************************************** 1325 * 1326 * @Function: 1327 * tt_driver_done 1328 * 1329 * @Description: 1330 * Finalize a given TrueType driver. 1331 * 1332 * @Input: 1333 * driver :: 1334 * A handle to the target TrueType driver. 1335 */ 1336 FT_LOCAL_DEF( void ) 1337 tt_driver_done( FT_Module ttdriver ) /* TT_Driver */ 1338 { 1339 FT_UNUSED( ttdriver ); 1340 } 1341 1342 1343 /************************************************************************** 1344 * 1345 * @Function: 1346 * tt_slot_init 1347 * 1348 * @Description: 1349 * Initialize a new slot object. 1350 * 1351 * @InOut: 1352 * slot :: 1353 * A handle to the slot object. 1354 * 1355 * @Return: 1356 * FreeType error code. 0 means success. 1357 */ 1358 FT_LOCAL_DEF( FT_Error ) 1359 tt_slot_init( FT_GlyphSlot slot ) 1360 { 1361 return FT_GlyphLoader_CreateExtra( slot->internal->loader ); 1362 } 1363 1364 1365 /* END */