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