OLD | NEW |
(Empty) | |
| 1 /***************************************************************************/ |
| 2 /* */ |
| 3 /* ttsubpix.c */ |
| 4 /* */ |
| 5 /* TrueType Subpixel Hinting. */ |
| 6 /* */ |
| 7 /* Copyright 2010-2013 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 #include <ft2build.h> |
| 19 #include FT_INTERNAL_DEBUG_H |
| 20 #include FT_INTERNAL_CALC_H |
| 21 #include FT_INTERNAL_STREAM_H |
| 22 #include FT_INTERNAL_SFNT_H |
| 23 #include FT_TRUETYPE_TAGS_H |
| 24 #include FT_OUTLINE_H |
| 25 #include FT_TRUETYPE_DRIVER_H |
| 26 |
| 27 #include "ttsubpix.h" |
| 28 |
| 29 |
| 30 #ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING |
| 31 |
| 32 /*************************************************************************/ |
| 33 /* */ |
| 34 /* These rules affect how the TT Interpreter does hinting, with the */ |
| 35 /* goal of doing subpixel hinting by (in general) ignoring x moves. */ |
| 36 /* Some of these rules are fixes that go above and beyond the */ |
| 37 /* stated techniques in the MS whitepaper on Cleartype, due to */ |
| 38 /* artifacts in many glyphs. So, these rules make some glyphs render */ |
| 39 /* better than they do in the MS rasterizer. */ |
| 40 /* */ |
| 41 /* "" string or 0 int/char indicates to apply to all glyphs. */ |
| 42 /* "-" used as dummy placeholders, but any non-matching string works. */ |
| 43 /* */ |
| 44 /* Some of this could arguably be implemented in fontconfig, however: */ |
| 45 /* */ |
| 46 /* - Fontconfig can't set things on a glyph-by-glyph basis. */ |
| 47 /* - The tweaks that happen here are very low-level, from an average */ |
| 48 /* user's point of view and are best implemented in the hinter. */ |
| 49 /* */ |
| 50 /* The goal is to make the subpixel hinting techniques as generalized */ |
| 51 /* as possible across all fonts to prevent the need for extra rules such */ |
| 52 /* as these. */ |
| 53 /* */ |
| 54 /* The rule structure is designed so that entirely new rules can easily */ |
| 55 /* be added when a new compatibility feature is discovered. */ |
| 56 /* */ |
| 57 /* The rule structures could also use some enhancement to handle ranges. */ |
| 58 /* */ |
| 59 /* ****************** WORK IN PROGRESS ******************* */ |
| 60 /* */ |
| 61 |
| 62 /* These are `classes' of fonts that can be grouped together and used in */ |
| 63 /* rules below. A blank entry "" is required at the end of these! */ |
| 64 #define FAMILY_CLASS_RULES_SIZE 7 |
| 65 |
| 66 static const SPH_Font_Class FAMILY_CLASS_Rules |
| 67 [FAMILY_CLASS_RULES_SIZE] = |
| 68 { |
| 69 { "MS Legacy Fonts", |
| 70 { "Aharoni", |
| 71 "Andale Mono", |
| 72 "Andalus", |
| 73 "Angsana New", |
| 74 "AngsanaUPC", |
| 75 "Arabic Transparent", |
| 76 "Arial Black", |
| 77 "Arial Narrow", |
| 78 "Arial Unicode MS", |
| 79 "Arial", |
| 80 "Batang", |
| 81 "Browallia New", |
| 82 "BrowalliaUPC", |
| 83 "Comic Sans MS", |
| 84 "Cordia New", |
| 85 "CordiaUPC", |
| 86 "Courier New", |
| 87 "DFKai-SB", |
| 88 "David Transparent", |
| 89 "David", |
| 90 "DilleniaUPC", |
| 91 "Estrangelo Edessa", |
| 92 "EucrosiaUPC", |
| 93 "FangSong_GB2312", |
| 94 "Fixed Miriam Transparent", |
| 95 "FrankRuehl", |
| 96 "Franklin Gothic Medium", |
| 97 "FreesiaUPC", |
| 98 "Garamond", |
| 99 "Gautami", |
| 100 "Georgia", |
| 101 "Gulim", |
| 102 "Impact", |
| 103 "IrisUPC", |
| 104 "JasmineUPC", |
| 105 "KaiTi_GB2312", |
| 106 "KodchiangUPC", |
| 107 "Latha", |
| 108 "Levenim MT", |
| 109 "LilyUPC", |
| 110 "Lucida Console", |
| 111 "Lucida Sans Unicode", |
| 112 "MS Gothic", |
| 113 "MS Mincho", |
| 114 "MV Boli", |
| 115 "Mangal", |
| 116 "Marlett", |
| 117 "Microsoft Sans Serif", |
| 118 "Mingliu", |
| 119 "Miriam Fixed", |
| 120 "Miriam Transparent", |
| 121 "Miriam", |
| 122 "Narkisim", |
| 123 "Palatino Linotype", |
| 124 "Raavi", |
| 125 "Rod Transparent", |
| 126 "Rod", |
| 127 "Shruti", |
| 128 "SimHei", |
| 129 "Simplified Arabic Fixed", |
| 130 "Simplified Arabic", |
| 131 "Simsun", |
| 132 "Sylfaen", |
| 133 "Symbol", |
| 134 "Tahoma", |
| 135 "Times New Roman", |
| 136 "Traditional Arabic", |
| 137 "Trebuchet MS", |
| 138 "Tunga", |
| 139 "Verdana", |
| 140 "Webdings", |
| 141 "Wingdings", |
| 142 "", |
| 143 }, |
| 144 }, |
| 145 { "Core MS Legacy Fonts", |
| 146 { "Arial Black", |
| 147 "Arial Narrow", |
| 148 "Arial Unicode MS", |
| 149 "Arial", |
| 150 "Comic Sans MS", |
| 151 "Courier New", |
| 152 "Garamond", |
| 153 "Georgia", |
| 154 "Impact", |
| 155 "Lucida Console", |
| 156 "Lucida Sans Unicode", |
| 157 "Microsoft Sans Serif", |
| 158 "Palatino Linotype", |
| 159 "Tahoma", |
| 160 "Times New Roman", |
| 161 "Trebuchet MS", |
| 162 "Verdana", |
| 163 "", |
| 164 }, |
| 165 }, |
| 166 { "Apple Legacy Fonts", |
| 167 { "Geneva", |
| 168 "Times", |
| 169 "Monaco", |
| 170 "Century", |
| 171 "Chalkboard", |
| 172 "Lobster", |
| 173 "Century Gothic", |
| 174 "Optima", |
| 175 "Lucida Grande", |
| 176 "Gill Sans", |
| 177 "Baskerville", |
| 178 "Helvetica", |
| 179 "Helvetica Neue", |
| 180 "", |
| 181 }, |
| 182 }, |
| 183 { "Legacy Sans Fonts", |
| 184 { "Andale Mono", |
| 185 "Arial Unicode MS", |
| 186 "Arial", |
| 187 "Century Gothic", |
| 188 "Comic Sans MS", |
| 189 "Franklin Gothic Medium", |
| 190 "Geneva", |
| 191 "Lucida Console", |
| 192 "Lucida Grande", |
| 193 "Lucida Sans Unicode", |
| 194 "Lucida Sans Typewriter", |
| 195 "Microsoft Sans Serif", |
| 196 "Monaco", |
| 197 "Tahoma", |
| 198 "Trebuchet MS", |
| 199 "Verdana", |
| 200 "", |
| 201 }, |
| 202 }, |
| 203 |
| 204 { "Misc Legacy Fonts", |
| 205 { "Dark Courier", "", }, }, |
| 206 { "Verdana Clones", |
| 207 { "DejaVu Sans", |
| 208 "Bitstream Vera Sans", "", }, }, |
| 209 { "Verdana and Clones", |
| 210 { "DejaVu Sans", |
| 211 "Bitstream Vera Sans", |
| 212 "Verdana", "", }, }, |
| 213 }; |
| 214 |
| 215 |
| 216 /* Define this to force natural (i.e. not bitmap-compatible) widths. */ |
| 217 /* The default leans strongly towards natural widths except for a few */ |
| 218 /* legacy fonts where a selective combination produces nicer results. */ |
| 219 /* #define FORCE_NATURAL_WIDTHS */ |
| 220 |
| 221 |
| 222 /* Define `classes' of styles that can be grouped together and used in */ |
| 223 /* rules below. A blank entry "" is required at the end of these! */ |
| 224 #define STYLE_CLASS_RULES_SIZE 5 |
| 225 |
| 226 const SPH_Font_Class STYLE_CLASS_Rules |
| 227 [STYLE_CLASS_RULES_SIZE] = |
| 228 { |
| 229 { "Regular Class", |
| 230 { "Regular", |
| 231 "Book", |
| 232 "Medium", |
| 233 "Roman", |
| 234 "Normal", |
| 235 "", |
| 236 }, |
| 237 }, |
| 238 { "Regular/Italic Class", |
| 239 { "Regular", |
| 240 "Book", |
| 241 "Medium", |
| 242 "Italic", |
| 243 "Oblique", |
| 244 "Roman", |
| 245 "Normal", |
| 246 "", |
| 247 }, |
| 248 }, |
| 249 { "Bold/BoldItalic Class", |
| 250 { "Bold", |
| 251 "Bold Italic", |
| 252 "Black", |
| 253 "", |
| 254 }, |
| 255 }, |
| 256 { "Bold/Italic/BoldItalic Class", |
| 257 { "Bold", |
| 258 "Bold Italic", |
| 259 "Black", |
| 260 "Italic", |
| 261 "Oblique", |
| 262 "", |
| 263 }, |
| 264 }, |
| 265 { "Regular/Bold Class", |
| 266 { "Regular", |
| 267 "Book", |
| 268 "Medium", |
| 269 "Normal", |
| 270 "Roman", |
| 271 "Bold", |
| 272 "Black", |
| 273 "", |
| 274 }, |
| 275 }, |
| 276 }; |
| 277 |
| 278 |
| 279 /* Force special legacy fixes for fonts. */ |
| 280 #define COMPATIBILITY_MODE_RULES_SIZE 1 |
| 281 |
| 282 const SPH_TweakRule COMPATIBILITY_MODE_Rules |
| 283 [COMPATIBILITY_MODE_RULES_SIZE] = |
| 284 { |
| 285 { "-", 0, "", 0 }, |
| 286 }; |
| 287 |
| 288 |
| 289 /* Don't do subpixel (ignore_x_mode) hinting; do normal hinting. */ |
| 290 #define PIXEL_HINTING_RULES_SIZE 2 |
| 291 |
| 292 const SPH_TweakRule PIXEL_HINTING_Rules |
| 293 [PIXEL_HINTING_RULES_SIZE] = |
| 294 { |
| 295 /* these characters are almost always safe */ |
| 296 { "Courier New", 12, "Italic", 'z' }, |
| 297 { "Courier New", 11, "Italic", 'z' }, |
| 298 }; |
| 299 |
| 300 |
| 301 /* Subpixel hinting ignores SHPIX rules on X. Force SHPIX for these. */ |
| 302 #define DO_SHPIX_RULES_SIZE 1 |
| 303 |
| 304 const SPH_TweakRule DO_SHPIX_Rules |
| 305 [DO_SHPIX_RULES_SIZE] = |
| 306 { |
| 307 { "-", 0, "", 0 }, |
| 308 }; |
| 309 |
| 310 |
| 311 /* Skip Y moves that start with a point that is not on a Y pixel */ |
| 312 /* boundary and don't move that point to a Y pixel boundary. */ |
| 313 #define SKIP_NONPIXEL_Y_MOVES_RULES_SIZE 4 |
| 314 |
| 315 const SPH_TweakRule SKIP_NONPIXEL_Y_MOVES_Rules |
| 316 [SKIP_NONPIXEL_Y_MOVES_RULES_SIZE] = |
| 317 { |
| 318 /* fix vwxyz thinness*/ |
| 319 { "Consolas", 0, "", 0 }, |
| 320 /* Fix thin middle stems */ |
| 321 { "Core MS Legacy Fonts", 0, "Regular", 0 }, |
| 322 /* Cyrillic small letter I */ |
| 323 { "Legacy Sans Fonts", 0, "", 0 }, |
| 324 /* Fix artifacts with some Regular & Bold */ |
| 325 { "Verdana Clones", 0, "", 0 }, |
| 326 }; |
| 327 |
| 328 |
| 329 #define SKIP_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE 1 |
| 330 |
| 331 const SPH_TweakRule SKIP_NONPIXEL_Y_MOVES_Rules_Exceptions |
| 332 [SKIP_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE] = |
| 333 { |
| 334 /* Fixes < and > */ |
| 335 { "Courier New", 0, "Regular", 0 }, |
| 336 }; |
| 337 |
| 338 |
| 339 /* Skip Y moves that start with a point that is not on a Y pixel */ |
| 340 /* boundary and don't move that point to a Y pixel boundary. */ |
| 341 #define SKIP_NONPIXEL_Y_MOVES_DELTAP_RULES_SIZE 2 |
| 342 |
| 343 const SPH_TweakRule SKIP_NONPIXEL_Y_MOVES_DELTAP_Rules |
| 344 [SKIP_NONPIXEL_Y_MOVES_DELTAP_RULES_SIZE] = |
| 345 { |
| 346 /* Maintain thickness of diagonal in 'N' */ |
| 347 { "Times New Roman", 0, "Regular/Bold Class", 'N' }, |
| 348 { "Georgia", 0, "Regular/Bold Class", 'N' }, |
| 349 }; |
| 350 |
| 351 |
| 352 /* Skip Y moves that move a point off a Y pixel boundary. */ |
| 353 #define SKIP_OFFPIXEL_Y_MOVES_RULES_SIZE 1 |
| 354 |
| 355 const SPH_TweakRule SKIP_OFFPIXEL_Y_MOVES_Rules |
| 356 [SKIP_OFFPIXEL_Y_MOVES_RULES_SIZE] = |
| 357 { |
| 358 { "-", 0, "", 0 }, |
| 359 }; |
| 360 |
| 361 |
| 362 #define SKIP_OFFPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE 1 |
| 363 |
| 364 const SPH_TweakRule SKIP_OFFPIXEL_Y_MOVES_Rules_Exceptions |
| 365 [SKIP_OFFPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE] = |
| 366 { |
| 367 { "-", 0, "", 0 }, |
| 368 }; |
| 369 |
| 370 |
| 371 /* Round moves that don't move a point to a Y pixel boundary. */ |
| 372 #define ROUND_NONPIXEL_Y_MOVES_RULES_SIZE 2 |
| 373 |
| 374 const SPH_TweakRule ROUND_NONPIXEL_Y_MOVES_Rules |
| 375 [ROUND_NONPIXEL_Y_MOVES_RULES_SIZE] = |
| 376 { |
| 377 /* Droid font instructions don't snap Y to pixels */ |
| 378 { "Droid Sans", 0, "Regular/Italic Class", 0 }, |
| 379 { "Droid Sans Mono", 0, "", 0 }, |
| 380 }; |
| 381 |
| 382 |
| 383 #define ROUND_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE 1 |
| 384 |
| 385 const SPH_TweakRule ROUND_NONPIXEL_Y_MOVES_Rules_Exceptions |
| 386 [ROUND_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE] = |
| 387 { |
| 388 { "-", 0, "", 0 }, |
| 389 }; |
| 390 |
| 391 |
| 392 /* Allow a Direct_Move along X freedom vector if matched. */ |
| 393 #define ALLOW_X_DMOVE_RULES_SIZE 1 |
| 394 |
| 395 const SPH_TweakRule ALLOW_X_DMOVE_Rules |
| 396 [ALLOW_X_DMOVE_RULES_SIZE] = |
| 397 { |
| 398 /* Fixes vanishing diagonal in 4 */ |
| 399 { "Verdana", 0, "Regular", '4' }, |
| 400 }; |
| 401 |
| 402 |
| 403 /* Return MS rasterizer version 35 if matched. */ |
| 404 #define RASTERIZER_35_RULES_SIZE 8 |
| 405 |
| 406 const SPH_TweakRule RASTERIZER_35_Rules |
| 407 [RASTERIZER_35_RULES_SIZE] = |
| 408 { |
| 409 /* This seems to be the only way to make these look good */ |
| 410 { "Times New Roman", 0, "Regular", 'i' }, |
| 411 { "Times New Roman", 0, "Regular", 'j' }, |
| 412 { "Times New Roman", 0, "Regular", 'm' }, |
| 413 { "Times New Roman", 0, "Regular", 'r' }, |
| 414 { "Times New Roman", 0, "Regular", 'a' }, |
| 415 { "Times New Roman", 0, "Regular", 'n' }, |
| 416 { "Times New Roman", 0, "Regular", 'p' }, |
| 417 { "Times", 0, "", 0 }, |
| 418 }; |
| 419 |
| 420 |
| 421 /* Don't round to the subpixel grid. Round to pixel grid. */ |
| 422 #define NORMAL_ROUND_RULES_SIZE 1 |
| 423 |
| 424 const SPH_TweakRule NORMAL_ROUND_Rules |
| 425 [NORMAL_ROUND_RULES_SIZE] = |
| 426 { |
| 427 /* Fix serif thickness for certain ppems */ |
| 428 /* Can probably be generalized somehow */ |
| 429 { "Courier New", 0, "", 0 }, |
| 430 }; |
| 431 |
| 432 |
| 433 /* Skip IUP instructions if matched. */ |
| 434 #define SKIP_IUP_RULES_SIZE 1 |
| 435 |
| 436 const SPH_TweakRule SKIP_IUP_Rules |
| 437 [SKIP_IUP_RULES_SIZE] = |
| 438 { |
| 439 { "Arial", 13, "Regular", 'a' }, |
| 440 }; |
| 441 |
| 442 |
| 443 /* Skip MIAP Twilight hack if matched. */ |
| 444 #define MIAP_HACK_RULES_SIZE 1 |
| 445 |
| 446 const SPH_TweakRule MIAP_HACK_Rules |
| 447 [MIAP_HACK_RULES_SIZE] = |
| 448 { |
| 449 { "Geneva", 12, "", 0 }, |
| 450 }; |
| 451 |
| 452 |
| 453 /* Skip DELTAP instructions if matched. */ |
| 454 #define ALWAYS_SKIP_DELTAP_RULES_SIZE 23 |
| 455 |
| 456 const SPH_TweakRule ALWAYS_SKIP_DELTAP_Rules |
| 457 [ALWAYS_SKIP_DELTAP_RULES_SIZE] = |
| 458 { |
| 459 { "Georgia", 0, "Regular", 'k' }, |
| 460 /* fix various problems with e in different versions */ |
| 461 { "Trebuchet MS", 14, "Regular", 'e' }, |
| 462 { "Trebuchet MS", 13, "Regular", 'e' }, |
| 463 { "Trebuchet MS", 15, "Regular", 'e' }, |
| 464 { "Trebuchet MS", 0, "Italic", 'v' }, |
| 465 { "Trebuchet MS", 0, "Italic", 'w' }, |
| 466 { "Trebuchet MS", 0, "Regular", 'Y' }, |
| 467 { "Arial", 11, "Regular", 's' }, |
| 468 /* prevent problems with '3' and others */ |
| 469 { "Verdana", 10, "Regular", 0 }, |
| 470 { "Verdana", 9, "Regular", 0 }, |
| 471 /* Cyrillic small letter short I */ |
| 472 { "Legacy Sans Fonts", 0, "", 0x438 }, |
| 473 { "Legacy Sans Fonts", 0, "", 0x439 }, |
| 474 { "Arial", 10, "Regular", '6' }, |
| 475 { "Arial", 0, "Bold/BoldItalic Class", 'a' }, |
| 476 /* Make horizontal stems consistent with the rest */ |
| 477 { "Arial", 24, "Bold", 'a' }, |
| 478 { "Arial", 25, "Bold", 'a' }, |
| 479 { "Arial", 24, "Bold", 's' }, |
| 480 { "Arial", 25, "Bold", 's' }, |
| 481 { "Arial", 34, "Bold", 's' }, |
| 482 { "Arial", 35, "Bold", 's' }, |
| 483 { "Arial", 36, "Bold", 's' }, |
| 484 { "Arial", 25, "Regular", 's' }, |
| 485 { "Arial", 26, "Regular", 's' }, |
| 486 }; |
| 487 |
| 488 |
| 489 /* Always do DELTAP instructions if matched. */ |
| 490 #define ALWAYS_DO_DELTAP_RULES_SIZE 1 |
| 491 |
| 492 const SPH_TweakRule ALWAYS_DO_DELTAP_Rules |
| 493 [ALWAYS_DO_DELTAP_RULES_SIZE] = |
| 494 { |
| 495 { "-", 0, "", 0 }, |
| 496 }; |
| 497 |
| 498 |
| 499 /* Don't allow ALIGNRP after IUP. */ |
| 500 #define NO_ALIGNRP_AFTER_IUP_RULES_SIZE 1 |
| 501 |
| 502 static const SPH_TweakRule NO_ALIGNRP_AFTER_IUP_Rules |
| 503 [NO_ALIGNRP_AFTER_IUP_RULES_SIZE] = |
| 504 { |
| 505 /* Prevent creation of dents in outline */ |
| 506 { "-", 0, "", 0 }, |
| 507 }; |
| 508 |
| 509 |
| 510 /* Don't allow DELTAP after IUP. */ |
| 511 #define NO_DELTAP_AFTER_IUP_RULES_SIZE 1 |
| 512 |
| 513 static const SPH_TweakRule NO_DELTAP_AFTER_IUP_Rules |
| 514 [NO_DELTAP_AFTER_IUP_RULES_SIZE] = |
| 515 { |
| 516 { "-", 0, "", 0 }, |
| 517 }; |
| 518 |
| 519 |
| 520 /* Don't allow CALL after IUP. */ |
| 521 #define NO_CALL_AFTER_IUP_RULES_SIZE 1 |
| 522 |
| 523 static const SPH_TweakRule NO_CALL_AFTER_IUP_Rules |
| 524 [NO_CALL_AFTER_IUP_RULES_SIZE] = |
| 525 { |
| 526 /* Prevent creation of dents in outline */ |
| 527 { "-", 0, "", 0 }, |
| 528 }; |
| 529 |
| 530 |
| 531 /* De-embolden these glyphs slightly. */ |
| 532 #define DEEMBOLDEN_RULES_SIZE 9 |
| 533 |
| 534 static const SPH_TweakRule DEEMBOLDEN_Rules |
| 535 [DEEMBOLDEN_RULES_SIZE] = |
| 536 { |
| 537 { "Courier New", 0, "Bold", 'A' }, |
| 538 { "Courier New", 0, "Bold", 'W' }, |
| 539 { "Courier New", 0, "Bold", 'w' }, |
| 540 { "Courier New", 0, "Bold", 'M' }, |
| 541 { "Courier New", 0, "Bold", 'X' }, |
| 542 { "Courier New", 0, "Bold", 'K' }, |
| 543 { "Courier New", 0, "Bold", 'x' }, |
| 544 { "Courier New", 0, "Bold", 'z' }, |
| 545 { "Courier New", 0, "Bold", 'v' }, |
| 546 }; |
| 547 |
| 548 |
| 549 /* Embolden these glyphs slightly. */ |
| 550 #define EMBOLDEN_RULES_SIZE 2 |
| 551 |
| 552 static const SPH_TweakRule EMBOLDEN_Rules |
| 553 [EMBOLDEN_RULES_SIZE] = |
| 554 { |
| 555 { "Courier New", 0, "Regular", 0 }, |
| 556 { "Courier New", 0, "Italic", 0 }, |
| 557 }; |
| 558 |
| 559 |
| 560 /* This is a CVT hack that makes thick horizontal stems on 2, 5, 7 */ |
| 561 /* similar to Windows XP. */ |
| 562 #define TIMES_NEW_ROMAN_HACK_RULES_SIZE 12 |
| 563 |
| 564 static const SPH_TweakRule TIMES_NEW_ROMAN_HACK_Rules |
| 565 [TIMES_NEW_ROMAN_HACK_RULES_SIZE] = |
| 566 { |
| 567 { "Times New Roman", 16, "Italic", '2' }, |
| 568 { "Times New Roman", 16, "Italic", '5' }, |
| 569 { "Times New Roman", 16, "Italic", '7' }, |
| 570 { "Times New Roman", 16, "Regular", '2' }, |
| 571 { "Times New Roman", 16, "Regular", '5' }, |
| 572 { "Times New Roman", 16, "Regular", '7' }, |
| 573 { "Times New Roman", 17, "Italic", '2' }, |
| 574 { "Times New Roman", 17, "Italic", '5' }, |
| 575 { "Times New Roman", 17, "Italic", '7' }, |
| 576 { "Times New Roman", 17, "Regular", '2' }, |
| 577 { "Times New Roman", 17, "Regular", '5' }, |
| 578 { "Times New Roman", 17, "Regular", '7' }, |
| 579 }; |
| 580 |
| 581 |
| 582 /* This fudges distance on 2 to get rid of the vanishing stem issue. */ |
| 583 /* A real solution to this is certainly welcome. */ |
| 584 #define COURIER_NEW_2_HACK_RULES_SIZE 15 |
| 585 |
| 586 static const SPH_TweakRule COURIER_NEW_2_HACK_Rules |
| 587 [COURIER_NEW_2_HACK_RULES_SIZE] = |
| 588 { |
| 589 { "Courier New", 10, "Regular", '2' }, |
| 590 { "Courier New", 11, "Regular", '2' }, |
| 591 { "Courier New", 12, "Regular", '2' }, |
| 592 { "Courier New", 13, "Regular", '2' }, |
| 593 { "Courier New", 14, "Regular", '2' }, |
| 594 { "Courier New", 15, "Regular", '2' }, |
| 595 { "Courier New", 16, "Regular", '2' }, |
| 596 { "Courier New", 17, "Regular", '2' }, |
| 597 { "Courier New", 18, "Regular", '2' }, |
| 598 { "Courier New", 19, "Regular", '2' }, |
| 599 { "Courier New", 20, "Regular", '2' }, |
| 600 { "Courier New", 21, "Regular", '2' }, |
| 601 { "Courier New", 22, "Regular", '2' }, |
| 602 { "Courier New", 23, "Regular", '2' }, |
| 603 { "Courier New", 24, "Regular", '2' }, |
| 604 }; |
| 605 |
| 606 |
| 607 #ifndef FORCE_NATURAL_WIDTHS |
| 608 |
| 609 /* Use compatible widths with these glyphs. Compatible widths is always */ |
| 610 /* on when doing B/W TrueType instructing, but is used selectively here, */ |
| 611 /* typically on glyphs with 3 or more vertical stems. */ |
| 612 #define COMPATIBLE_WIDTHS_RULES_SIZE 38 |
| 613 |
| 614 static const SPH_TweakRule COMPATIBLE_WIDTHS_Rules |
| 615 [COMPATIBLE_WIDTHS_RULES_SIZE] = |
| 616 { |
| 617 { "Arial Unicode MS", 12, "Regular Class", 'm' }, |
| 618 { "Arial Unicode MS", 14, "Regular Class", 'm' }, |
| 619 /* Cyrillic small letter sha */ |
| 620 { "Arial", 10, "Regular Class", 0x448 }, |
| 621 { "Arial", 11, "Regular Class", 'm' }, |
| 622 { "Arial", 12, "Regular Class", 'm' }, |
| 623 /* Cyrillic small letter sha */ |
| 624 { "Arial", 12, "Regular Class", 0x448 }, |
| 625 { "Arial", 13, "Regular Class", 0x448 }, |
| 626 { "Arial", 14, "Regular Class", 'm' }, |
| 627 /* Cyrillic small letter sha */ |
| 628 { "Arial", 14, "Regular Class", 0x448 }, |
| 629 { "Arial", 15, "Regular Class", 0x448 }, |
| 630 { "Arial", 17, "Regular Class", 'm' }, |
| 631 { "DejaVu Sans", 15, "Regular Class", 0 }, |
| 632 { "Microsoft Sans Serif", 11, "Regular Class", 0 }, |
| 633 { "Microsoft Sans Serif", 12, "Regular Class", 0 }, |
| 634 { "Segoe UI", 11, "Regular Class", 0 }, |
| 635 { "Monaco", 0, "Regular Class", 0 }, |
| 636 { "Segoe UI", 12, "Regular Class", 'm' }, |
| 637 { "Segoe UI", 14, "Regular Class", 'm' }, |
| 638 { "Tahoma", 11, "Regular Class", 0 }, |
| 639 { "Times New Roman", 16, "Regular Class", 'c' }, |
| 640 { "Times New Roman", 16, "Regular Class", 'm' }, |
| 641 { "Times New Roman", 16, "Regular Class", 'o' }, |
| 642 { "Times New Roman", 16, "Regular Class", 'w' }, |
| 643 { "Trebuchet MS", 11, "Regular Class", 0 }, |
| 644 { "Trebuchet MS", 12, "Regular Class", 0 }, |
| 645 { "Trebuchet MS", 14, "Regular Class", 0 }, |
| 646 { "Trebuchet MS", 15, "Regular Class", 0 }, |
| 647 { "Ubuntu", 12, "Regular Class", 'm' }, |
| 648 /* Cyrillic small letter sha */ |
| 649 { "Verdana", 10, "Regular Class", 0x448 }, |
| 650 { "Verdana", 11, "Regular Class", 0x448 }, |
| 651 { "Verdana and Clones", 12, "Regular Class", 'i' }, |
| 652 { "Verdana and Clones", 12, "Regular Class", 'j' }, |
| 653 { "Verdana and Clones", 12, "Regular Class", 'l' }, |
| 654 { "Verdana and Clones", 12, "Regular Class", 'm' }, |
| 655 { "Verdana and Clones", 13, "Regular Class", 'i' }, |
| 656 { "Verdana and Clones", 13, "Regular Class", 'j' }, |
| 657 { "Verdana and Clones", 13, "Regular Class", 'l' }, |
| 658 { "Verdana and Clones", 14, "Regular Class", 'm' }, |
| 659 }; |
| 660 |
| 661 |
| 662 /* Scaling slightly in the x-direction prior to hinting results in */ |
| 663 /* more visually pleasing glyphs in certain cases. */ |
| 664 /* This sometimes needs to be coordinated with compatible width rules. */ |
| 665 /* A value of 1000 corresponds to a scaled value of 1.0. */ |
| 666 |
| 667 #define X_SCALING_RULES_SIZE 50 |
| 668 |
| 669 static const SPH_ScaleRule X_SCALING_Rules[X_SCALING_RULES_SIZE] = |
| 670 { |
| 671 { "DejaVu Sans", 12, "Regular Class", 'm', 950 }, |
| 672 { "Verdana and Clones", 12, "Regular Class", 'a', 1100 }, |
| 673 { "Verdana and Clones", 13, "Regular Class", 'a', 1050 }, |
| 674 { "Arial", 11, "Regular Class", 'm', 975 }, |
| 675 { "Arial", 12, "Regular Class", 'm', 1050 }, |
| 676 /* Cyrillic small letter el */ |
| 677 { "Arial", 13, "Regular Class", 0x43B, 950 }, |
| 678 { "Arial", 13, "Regular Class", 'o', 950 }, |
| 679 { "Arial", 13, "Regular Class", 'e', 950 }, |
| 680 { "Arial", 14, "Regular Class", 'm', 950 }, |
| 681 /* Cyrillic small letter el */ |
| 682 { "Arial", 15, "Regular Class", 0x43B, 925 }, |
| 683 { "Bitstream Vera Sans", 10, "Regular/Italic Class", 0, 1100 }, |
| 684 { "Bitstream Vera Sans", 12, "Regular/Italic Class", 0, 1050 }, |
| 685 { "Bitstream Vera Sans", 16, "Regular Class", 0, 1050 }, |
| 686 { "Bitstream Vera Sans", 9, "Regular/Italic Class", 0, 1050 }, |
| 687 { "DejaVu Sans", 12, "Regular Class", 'l', 975 }, |
| 688 { "DejaVu Sans", 12, "Regular Class", 'i', 975 }, |
| 689 { "DejaVu Sans", 12, "Regular Class", 'j', 975 }, |
| 690 { "DejaVu Sans", 13, "Regular Class", 'l', 950 }, |
| 691 { "DejaVu Sans", 13, "Regular Class", 'i', 950 }, |
| 692 { "DejaVu Sans", 13, "Regular Class", 'j', 950 }, |
| 693 { "DejaVu Sans", 10, "Regular/Italic Class", 0, 1100 }, |
| 694 { "DejaVu Sans", 12, "Regular/Italic Class", 0, 1050 }, |
| 695 { "Georgia", 10, "", 0, 1050 }, |
| 696 { "Georgia", 11, "", 0, 1100 }, |
| 697 { "Georgia", 12, "", 0, 1025 }, |
| 698 { "Georgia", 13, "", 0, 1050 }, |
| 699 { "Georgia", 16, "", 0, 1050 }, |
| 700 { "Georgia", 17, "", 0, 1030 }, |
| 701 { "Liberation Sans", 12, "Regular Class", 'm', 1100 }, |
| 702 { "Lucida Grande", 11, "Regular Class", 'm', 1100 }, |
| 703 { "Microsoft Sans Serif", 11, "Regular Class", 'm', 950 }, |
| 704 { "Microsoft Sans Serif", 12, "Regular Class", 'm', 1050 }, |
| 705 { "Segoe UI", 12, "Regular Class", 'H', 1050 }, |
| 706 { "Segoe UI", 12, "Regular Class", 'm', 1050 }, |
| 707 { "Segoe UI", 14, "Regular Class", 'm', 1050 }, |
| 708 { "Tahoma", 11, "Regular Class", 'i', 975 }, |
| 709 { "Tahoma", 11, "Regular Class", 'l', 975 }, |
| 710 { "Tahoma", 11, "Regular Class", 'j', 900 }, |
| 711 { "Tahoma", 11, "Regular Class", 'm', 918 }, |
| 712 { "Verdana", 10, "Regular/Italic Class", 0, 1100 }, |
| 713 { "Verdana", 12, "Regular Class", 'm', 975 }, |
| 714 { "Verdana", 12, "Regular/Italic Class", 0, 1050 }, |
| 715 { "Verdana", 13, "Regular/Italic Class", 'i', 950 }, |
| 716 { "Verdana", 13, "Regular/Italic Class", 'j', 950 }, |
| 717 { "Verdana", 13, "Regular/Italic Class", 'l', 950 }, |
| 718 { "Verdana", 16, "Regular Class", 0, 1050 }, |
| 719 { "Verdana", 9, "Regular/Italic Class", 0, 1050 }, |
| 720 { "Times New Roman", 16, "Regular Class", 'm', 918 }, |
| 721 { "Trebuchet MS", 11, "Regular Class", 'm', 800 }, |
| 722 { "Trebuchet MS", 12, "Regular Class", 'm', 800 }, |
| 723 }; |
| 724 |
| 725 #else |
| 726 |
| 727 #define COMPATIBLE_WIDTHS_RULES_SIZE 1 |
| 728 |
| 729 static const SPH_TweakRule COMPATIBLE_WIDTHS_Rules |
| 730 [COMPATIBLE_WIDTHS_RULES_SIZE] = |
| 731 { |
| 732 { "-", 0, "", 0 }, |
| 733 }; |
| 734 |
| 735 |
| 736 #define X_SCALING_RULES_SIZE 1 |
| 737 |
| 738 static const SPH_ScaleRule X_SCALING_Rules |
| 739 [X_SCALING_RULES_SIZE] = |
| 740 { |
| 741 { "-", 0, "", 0, 1000 }, |
| 742 }; |
| 743 |
| 744 #endif /* FORCE_NATURAL_WIDTHS */ |
| 745 |
| 746 |
| 747 FT_LOCAL_DEF( FT_Bool ) |
| 748 is_member_of_family_class( const FT_String* detected_font_name, |
| 749 const FT_String* rule_font_name ) |
| 750 { |
| 751 FT_UInt i, j; |
| 752 |
| 753 |
| 754 /* Does font name match rule family? */ |
| 755 if ( strcmp( detected_font_name, rule_font_name ) == 0 ) |
| 756 return TRUE; |
| 757 |
| 758 /* Is font name a wildcard ""? */ |
| 759 if ( strcmp( rule_font_name, "" ) == 0 ) |
| 760 return TRUE; |
| 761 |
| 762 /* Is font name contained in a class list? */ |
| 763 for ( i = 0; i < FAMILY_CLASS_RULES_SIZE; i++ ) |
| 764 { |
| 765 if ( strcmp( FAMILY_CLASS_Rules[i].name, rule_font_name ) == 0 ) |
| 766 { |
| 767 for ( j = 0; j < SPH_MAX_CLASS_MEMBERS; j++ ) |
| 768 { |
| 769 if ( strcmp( FAMILY_CLASS_Rules[i].member[j], "" ) == 0 ) |
| 770 continue; |
| 771 if ( strcmp( FAMILY_CLASS_Rules[i].member[j], |
| 772 detected_font_name ) == 0 ) |
| 773 return TRUE; |
| 774 } |
| 775 } |
| 776 } |
| 777 |
| 778 return FALSE; |
| 779 } |
| 780 |
| 781 |
| 782 FT_LOCAL_DEF( FT_Bool ) |
| 783 is_member_of_style_class( const FT_String* detected_font_style, |
| 784 const FT_String* rule_font_style ) |
| 785 { |
| 786 FT_UInt i, j; |
| 787 |
| 788 |
| 789 /* Does font style match rule style? */ |
| 790 if ( strcmp( detected_font_style, rule_font_style ) == 0 ) |
| 791 return TRUE; |
| 792 |
| 793 /* Is font style a wildcard ""? */ |
| 794 if ( strcmp( rule_font_style, "" ) == 0 ) |
| 795 return TRUE; |
| 796 |
| 797 /* Is font style contained in a class list? */ |
| 798 for ( i = 0; i < STYLE_CLASS_RULES_SIZE; i++ ) |
| 799 { |
| 800 if ( strcmp( STYLE_CLASS_Rules[i].name, rule_font_style ) == 0 ) |
| 801 { |
| 802 for ( j = 0; j < SPH_MAX_CLASS_MEMBERS; j++ ) |
| 803 { |
| 804 if ( strcmp( STYLE_CLASS_Rules[i].member[j], "" ) == 0 ) |
| 805 continue; |
| 806 if ( strcmp( STYLE_CLASS_Rules[i].member[j], |
| 807 detected_font_style ) == 0 ) |
| 808 return TRUE; |
| 809 } |
| 810 } |
| 811 } |
| 812 |
| 813 return FALSE; |
| 814 } |
| 815 |
| 816 |
| 817 FT_LOCAL_DEF( FT_Bool ) |
| 818 sph_test_tweak( TT_Face face, |
| 819 const FT_String* family, |
| 820 FT_UInt ppem, |
| 821 const FT_String* style, |
| 822 FT_UInt glyph_index, |
| 823 const SPH_TweakRule* rule, |
| 824 FT_UInt num_rules ) |
| 825 { |
| 826 FT_UInt i; |
| 827 |
| 828 |
| 829 /* rule checks may be able to be optimized further */ |
| 830 for ( i = 0; i < num_rules; i++ ) |
| 831 { |
| 832 if ( family && |
| 833 ( is_member_of_family_class ( family, rule[i].family ) ) ) |
| 834 if ( rule[i].ppem == 0 || |
| 835 rule[i].ppem == ppem ) |
| 836 if ( style && |
| 837 is_member_of_style_class ( style, rule[i].style ) ) |
| 838 if ( rule[i].glyph == 0 || |
| 839 FT_Get_Char_Index( (FT_Face)face, |
| 840 rule[i].glyph ) == glyph_index ) |
| 841 return TRUE; |
| 842 } |
| 843 |
| 844 return FALSE; |
| 845 } |
| 846 |
| 847 |
| 848 static FT_UInt |
| 849 scale_test_tweak( TT_Face face, |
| 850 const FT_String* family, |
| 851 FT_UInt ppem, |
| 852 const FT_String* style, |
| 853 FT_UInt glyph_index, |
| 854 const SPH_ScaleRule* rule, |
| 855 FT_UInt num_rules ) |
| 856 { |
| 857 FT_UInt i; |
| 858 |
| 859 |
| 860 /* rule checks may be able to be optimized further */ |
| 861 for ( i = 0; i < num_rules; i++ ) |
| 862 { |
| 863 if ( family && |
| 864 ( is_member_of_family_class ( family, rule[i].family ) ) ) |
| 865 if ( rule[i].ppem == 0 || |
| 866 rule[i].ppem == ppem ) |
| 867 if ( style && |
| 868 is_member_of_style_class( style, rule[i].style ) ) |
| 869 if ( rule[i].glyph == 0 || |
| 870 FT_Get_Char_Index( (FT_Face)face, |
| 871 rule[i].glyph ) == glyph_index ) |
| 872 return rule[i].scale; |
| 873 } |
| 874 |
| 875 return 1000; |
| 876 } |
| 877 |
| 878 |
| 879 FT_LOCAL_DEF( FT_UInt ) |
| 880 sph_test_tweak_x_scaling( TT_Face face, |
| 881 const FT_String* family, |
| 882 FT_UInt ppem, |
| 883 const FT_String* style, |
| 884 FT_UInt glyph_index ) |
| 885 { |
| 886 return scale_test_tweak( face, family, ppem, style, glyph_index, |
| 887 X_SCALING_Rules, X_SCALING_RULES_SIZE ); |
| 888 } |
| 889 |
| 890 |
| 891 #define TWEAK_RULES( x ) \ |
| 892 if ( sph_test_tweak( face, family, ppem, style, glyph_index, \ |
| 893 x##_Rules, x##_RULES_SIZE ) ) \ |
| 894 loader->exec->sph_tweak_flags |= SPH_TWEAK_##x; |
| 895 |
| 896 #define TWEAK_RULES_EXCEPTIONS( x ) \ |
| 897 if ( sph_test_tweak( face, family, ppem, style, glyph_index, \ |
| 898 x##_Rules_Exceptions, x##_RULES_EXCEPTIONS_SIZE ) ) \ |
| 899 loader->exec->sph_tweak_flags &= ~SPH_TWEAK_##x; |
| 900 |
| 901 |
| 902 FT_LOCAL_DEF( void ) |
| 903 sph_set_tweaks( TT_Loader loader, |
| 904 FT_UInt glyph_index ) |
| 905 { |
| 906 TT_Face face = (TT_Face)loader->face; |
| 907 FT_String* family = face->root.family_name; |
| 908 int ppem = loader->size->metrics.x_ppem; |
| 909 FT_String* style = face->root.style_name; |
| 910 |
| 911 |
| 912 /* don't apply rules if style isn't set */ |
| 913 if ( !face->root.style_name ) |
| 914 return; |
| 915 |
| 916 #ifdef SPH_DEBUG_MORE_VERBOSE |
| 917 printf( "%s,%d,%s,%c=%d ", |
| 918 family, ppem, style, glyph_index, glyph_index ); |
| 919 #endif |
| 920 |
| 921 TWEAK_RULES( PIXEL_HINTING ); |
| 922 |
| 923 if ( loader->exec->sph_tweak_flags & SPH_TWEAK_PIXEL_HINTING ) |
| 924 { |
| 925 loader->exec->ignore_x_mode = FALSE; |
| 926 return; |
| 927 } |
| 928 |
| 929 TWEAK_RULES( ALLOW_X_DMOVE ); |
| 930 TWEAK_RULES( ALWAYS_DO_DELTAP ); |
| 931 TWEAK_RULES( ALWAYS_SKIP_DELTAP ); |
| 932 TWEAK_RULES( DEEMBOLDEN ); |
| 933 TWEAK_RULES( DO_SHPIX ); |
| 934 TWEAK_RULES( EMBOLDEN ); |
| 935 TWEAK_RULES( MIAP_HACK ); |
| 936 TWEAK_RULES( NORMAL_ROUND ); |
| 937 TWEAK_RULES( NO_ALIGNRP_AFTER_IUP ); |
| 938 TWEAK_RULES( NO_CALL_AFTER_IUP ); |
| 939 TWEAK_RULES( NO_DELTAP_AFTER_IUP ); |
| 940 TWEAK_RULES( RASTERIZER_35 ); |
| 941 TWEAK_RULES( SKIP_IUP ); |
| 942 |
| 943 TWEAK_RULES( SKIP_OFFPIXEL_Y_MOVES ); |
| 944 TWEAK_RULES_EXCEPTIONS( SKIP_OFFPIXEL_Y_MOVES ); |
| 945 |
| 946 TWEAK_RULES( SKIP_NONPIXEL_Y_MOVES_DELTAP ); |
| 947 |
| 948 TWEAK_RULES( SKIP_NONPIXEL_Y_MOVES ); |
| 949 TWEAK_RULES_EXCEPTIONS( SKIP_NONPIXEL_Y_MOVES ); |
| 950 |
| 951 TWEAK_RULES( ROUND_NONPIXEL_Y_MOVES ); |
| 952 TWEAK_RULES_EXCEPTIONS( ROUND_NONPIXEL_Y_MOVES ); |
| 953 |
| 954 if ( loader->exec->sph_tweak_flags & SPH_TWEAK_RASTERIZER_35 ) |
| 955 { |
| 956 if ( loader->exec->rasterizer_version != TT_INTERPRETER_VERSION_35 ) |
| 957 { |
| 958 loader->exec->rasterizer_version = TT_INTERPRETER_VERSION_35; |
| 959 loader->exec->size->cvt_ready = FALSE; |
| 960 |
| 961 tt_size_ready_bytecode( |
| 962 loader->exec->size, |
| 963 FT_BOOL( loader->load_flags & FT_LOAD_PEDANTIC ) ); |
| 964 } |
| 965 else |
| 966 loader->exec->rasterizer_version = TT_INTERPRETER_VERSION_35; |
| 967 } |
| 968 else |
| 969 { |
| 970 if ( loader->exec->rasterizer_version != |
| 971 SPH_OPTION_SET_RASTERIZER_VERSION ) |
| 972 { |
| 973 loader->exec->rasterizer_version = SPH_OPTION_SET_RASTERIZER_VERSION; |
| 974 loader->exec->size->cvt_ready = FALSE; |
| 975 |
| 976 tt_size_ready_bytecode( |
| 977 loader->exec->size, |
| 978 FT_BOOL( loader->load_flags & FT_LOAD_PEDANTIC ) ); |
| 979 } |
| 980 else |
| 981 loader->exec->rasterizer_version = SPH_OPTION_SET_RASTERIZER_VERSION; |
| 982 } |
| 983 |
| 984 if ( IS_HINTED( loader->load_flags ) ) |
| 985 { |
| 986 TWEAK_RULES( TIMES_NEW_ROMAN_HACK ); |
| 987 TWEAK_RULES( COURIER_NEW_2_HACK ); |
| 988 } |
| 989 |
| 990 if ( sph_test_tweak( face, family, ppem, style, glyph_index, |
| 991 COMPATIBILITY_MODE_Rules, COMPATIBILITY_MODE_RULES_SIZE ) ) |
| 992 loader->exec->face->sph_compatibility_mode = TRUE; |
| 993 |
| 994 |
| 995 if ( IS_HINTED( loader->load_flags ) ) |
| 996 { |
| 997 if ( sph_test_tweak( face, family, ppem, style, glyph_index, |
| 998 COMPATIBLE_WIDTHS_Rules, COMPATIBLE_WIDTHS_RULES_SIZE ) ) |
| 999 loader->exec->compatible_widths |= TRUE; |
| 1000 } |
| 1001 } |
| 1002 |
| 1003 #else /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ |
| 1004 |
| 1005 /* ANSI C doesn't like empty source files */ |
| 1006 typedef int _tt_subpix_dummy; |
| 1007 |
| 1008 #endif /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ |
| 1009 |
| 1010 |
| 1011 /* END */ |
OLD | NEW |