4 * Copyright (C) 2011-2022 by Werner Lemberg.
6 * This file is part of the ttfautohint library, and may only be used,
7 * modified, and distributed under the terms given in `COPYING'. By
8 * continuing to use, modify, or distribute this file you indicate that you
9 * have read `COPYING' and understand and accept it fully.
11 * The file `COPYING' mentioned in the previous paragraph is distributed
12 * with the ttfautohint library.
16 /* originally file `aflatin.c' (2011-Mar-28) from FreeType */
18 /* heavily modified 2011 by Werner Lemberg <wl@gnu.org> */
23 #include FT_ADVANCES_H
24 #include FT_TRUETYPE_TABLES_H
31 #ifdef TA_CONFIG_OPTION_USE_WARPER
35 #include <numberset.h>
38 /* needed for computation of round vs. flat segments */
39 #define FLAT_THRESHOLD(x) (x / 14)
42 /* find segments and links, compute all stem widths, and initialize */
43 /* standard width and height for the glyph with given charcode */
46 ta_latin_metrics_init_widths(TA_LatinMetrics metrics
,
50 /* scan the array of segments in each direction */
51 TA_GlyphHintsRec hints
[1];
55 "latin standard widths computation (style `%s')\n"
56 "=====================================================\n"
58 ta_style_names
[metrics
->root
.style_class
->style
]));
60 ta_glyph_hints_init(hints
);
62 metrics
->axis
[TA_DIMENSION_HORZ
].width_count
= 0;
63 metrics
->axis
[TA_DIMENSION_VERT
].width_count
= 0;
69 TA_LatinMetricsRec dummy
[1];
70 TA_Scaler scaler
= &dummy
->root
.scaler
;
72 TA_StyleClass style_class
= metrics
->root
.style_class
;
73 TA_ScriptClass script_class
= ta_script_classes
[style_class
->script
];
75 FONT
* font
= metrics
->root
.globals
->font
;
85 /* if the user provides horizontal stem widths, */
86 /* apply the algorithm only along the horizontal axis */
87 TA_control_set_stem_widths(metrics
, font
);
88 if (metrics
->axis
[TA_DIMENSION_VERT
].width_count
)
90 TA_LatinAxis axis
= &metrics
->axis
[TA_DIMENSION_VERT
];
93 dim_max
= TA_DIMENSION_VERT
;
95 axis
->standard_width
= axis
->widths
[0].org
;
96 axis
->edge_distance_threshold
= axis
->standard_width
/ 5;
97 axis
->extra_light
= 0;
104 TA_LOG_GLOBAL(("horizontal widths (user provided):\n" ));
106 TA_LOG_GLOBAL((" %d (standard)", axis
->standard_width
));
107 for (i
= 1; i
< axis
->width_count
; i
++)
108 TA_LOG_GLOBAL((" %d", axis
->widths
[i
].org
));
110 TA_LOG_GLOBAL(("\n"));
116 dim_max
= TA_DIMENSION_MAX
;
121 p
= script_class
->standard_charstring
;
122 shaper_buf
= ta_shaper_buf_create(face
);
125 * We check a list of standard characters to catch features like
126 * `c2sc' (small caps from caps) that don't contain lowercase letters
127 * by definition, or other features that mainly operate on numerals.
128 * The first match wins.
134 unsigned int num_idx
;
146 GET_UTF8_CHAR(ch
, p_old
);
149 /* reject input that maps to more than a single glyph */
150 p
= ta_shaper_get_cluster(p
, &metrics
->root
, shaper_buf
, &num_idx
);
154 /* otherwise exit loop if we have a result */
155 glyph_index
= ta_shaper_get_elem(&metrics
->root
,
164 ta_shaper_buf_destroy(face
, shaper_buf
);
168 TA_LOG_GLOBAL(("no standard character\n"));
172 TA_LOG_GLOBAL(("standard character: U+%04lX (glyph index %d)\n",
175 error
= FT_Load_Glyph(face
, glyph_index
, FT_LOAD_NO_SCALE
);
176 if (error
|| face
->glyph
->outline
.n_points
<= 0)
179 memset(dummy
, 0, sizeof (TA_LatinMetricsRec
));
181 dummy
->units_per_em
= metrics
->units_per_em
;
183 scaler
->x_scale
= 0x10000L
;
184 scaler
->y_scale
= 0x10000L
;
189 scaler
->render_mode
= FT_RENDER_MODE_NORMAL
;
192 ta_glyph_hints_rescale(hints
, (TA_StyleMetrics
)dummy
);
194 error
= ta_glyph_hints_reload(hints
, &face
->glyph
->outline
);
198 for (dim
= 0; dim
< dim_max
; dim
++)
200 TA_LatinAxis axis
= &metrics
->axis
[dim
];
201 TA_AxisHints axhints
= &hints
->axis
[dim
];
203 TA_Segment seg
, limit
, link
;
204 FT_UInt num_widths
= 0;
207 error
= ta_latin_hints_compute_segments(hints
, (TA_Dimension
)dim
);
212 * We assume that the glyphs selected for the stem width
213 * computation are `featureless' enough so that the linking
214 * algorithm works fine without adjustments of its scoring
217 ta_latin_hints_link_segments(hints
, 0, NULL
, (TA_Dimension
)dim
);
219 seg
= axhints
->segments
;
220 limit
= seg
+ axhints
->num_segments
;
222 for (; seg
< limit
; seg
++)
226 /* we only consider stem segments there! */
234 dist
= seg
->pos
- link
->pos
;
238 if (num_widths
< TA_LATIN_MAX_WIDTHS
)
239 axis
->widths
[num_widths
++].org
= dist
;
243 /* this also replaces multiple almost identical stem widths */
244 /* with a single one (the value 100 is heuristic) */
245 ta_sort_and_quantize_widths(&num_widths
, axis
->widths
,
246 dummy
->units_per_em
/ 100);
247 axis
->width_count
= num_widths
;
251 for (dim
= 0; dim
< dim_max
; dim
++)
253 TA_LatinAxis axis
= &metrics
->axis
[dim
];
257 if (!axis
->width_count
)
259 /* if we have no standard characters, */
260 /* use `fallback-stem-width', if available, */
261 /* or a default width (value 50 is heuristic) */
262 if (dim
== TA_DIMENSION_VERT
&& font
->fallback_stem_width
)
264 stdw
= (FT_Pos
)font
->fallback_stem_width
;
265 TA_LOG_GLOBAL(("using horizontal fallback stem width\n"));
269 stdw
= TA_LATIN_CONSTANT(metrics
, 50);
270 TA_LOG_GLOBAL(("using a default %s stem width\n",
271 dim
== TA_DIMENSION_VERT
? "horizontal"
276 axis
->widths
[0].org
= stdw
;
279 stdw
= axis
->widths
[0].org
;
281 /* let's try 20% of the smallest width */
282 axis
->edge_distance_threshold
= stdw
/ 5;
283 axis
->standard_width
= stdw
;
284 axis
->extra_light
= 0;
291 TA_LOG_GLOBAL(("%s widths:\n",
292 dim
== TA_DIMENSION_VERT
? "horizontal"
295 TA_LOG_GLOBAL((" %d (standard)", axis
->standard_width
));
296 for (i
= 1; i
< axis
->width_count
; i
++)
297 TA_LOG_GLOBAL((" %d", axis
->widths
[i
].org
));
299 TA_LOG_GLOBAL(("\n"));
305 TA_LOG_GLOBAL(("\n"));
307 ta_glyph_hints_done(hints
);
312 ta_latin_sort_blue(FT_UInt count
,
320 /* we sort from bottom to top */
321 for (i
= 1; i
< count
; i
++)
323 for (j
= i
; j
> 0; j
--)
328 if (table
[j
- 1]->flags
& (TA_LATIN_BLUE_TOP
329 | TA_LATIN_BLUE_SUB_TOP
))
330 a
= table
[j
- 1]->ref
.org
;
332 a
= table
[j
- 1]->shoot
.org
;
334 if (table
[j
]->flags
& (TA_LATIN_BLUE_TOP
335 | TA_LATIN_BLUE_SUB_TOP
))
336 b
= table
[j
]->ref
.org
;
338 b
= table
[j
]->shoot
.org
;
344 table
[j
] = table
[j
- 1];
351 /* find all blue zones; flat segments give the reference points, */
352 /* round segments the overshoot positions */
355 ta_latin_metrics_init_blues(TA_LatinMetrics metrics
,
358 FT_Pos flats
[TA_BLUE_STRING_MAX_LEN
];
359 FT_Pos rounds
[TA_BLUE_STRING_MAX_LEN
];
365 TA_LatinAxis axis
= &metrics
->axis
[TA_DIMENSION_VERT
];
368 TA_StyleClass sc
= metrics
->root
.style_class
;
370 TA_Blue_Stringset bss
= sc
->blue_stringset
;
371 const TA_Blue_StringRec
* bs
= &ta_blue_stringsets
[bss
];
373 FT_Pos flat_threshold
= FLAT_THRESHOLD(metrics
->units_per_em
);
378 /* we walk over the blue character strings as specified in the */
379 /* style's entry in the `ta_blue_stringset' array */
381 TA_LOG_GLOBAL(("latin blue zones computation\n"
382 "============================\n"
385 shaper_buf
= ta_shaper_buf_create(face
);
387 for (; bs
->string
!= TA_BLUE_STRING_MAX
; bs
++)
389 const char* p
= &ta_blue_strings
[bs
->string
];
398 FT_Bool have_flag
= 0;
401 TA_LOG_GLOBAL(("blue zone %d", axis
->blue_count
));
405 TA_LOG_GLOBAL((" ("));
407 if (TA_LATIN_IS_TOP_BLUE(bs
))
409 TA_LOG_GLOBAL(("top"));
412 else if (TA_LATIN_IS_SUB_TOP_BLUE(bs
))
414 TA_LOG_GLOBAL(("sub top"));
418 if (TA_LATIN_IS_NEUTRAL_BLUE(bs
))
421 TA_LOG_GLOBAL((", "));
422 TA_LOG_GLOBAL(("neutral"));
426 if (TA_LATIN_IS_X_HEIGHT_BLUE(bs
))
429 TA_LOG_GLOBAL((", "));
430 TA_LOG_GLOBAL(("small top"));
434 if (TA_LATIN_IS_LONG_BLUE(bs
))
437 TA_LOG_GLOBAL((", "));
438 TA_LOG_GLOBAL(("long"));
441 TA_LOG_GLOBAL((")"));
444 TA_LOG_GLOBAL((":\n"));
446 #endif /* TA_DEBUG */
455 FT_ULong glyph_index
;
457 FT_Int best_point
, best_contour_first
, best_contour_last
;
460 FT_Pos best_y_extremum
; /* same as points.y */
461 FT_Bool best_round
= 0;
463 unsigned int i
, num_idx
;
476 GET_UTF8_CHAR(ch
, p_old
);
479 p
= ta_shaper_get_cluster(p
, &metrics
->root
, shaper_buf
, &num_idx
);
483 TA_LOG_GLOBAL((" U+%04lX unavailable\n", ch
));
487 if (TA_LATIN_IS_TOP_BLUE(bs
))
488 best_y_extremum
= FT_INT_MIN
;
490 best_y_extremum
= FT_INT_MAX
;
492 /* iterate over all glyph elements of the character cluster */
493 /* and get the data of the `biggest' one */
494 for (i
= 0; i
< num_idx
; i
++)
500 /* load the character in the face -- skip unknown or empty ones */
501 glyph_index
= ta_shaper_get_elem(&metrics
->root
,
506 if (glyph_index
== 0)
508 TA_LOG_GLOBAL((" U+%04lX unavailable\n", ch
));
512 error
= FT_Load_Glyph(face
, glyph_index
, FT_LOAD_NO_SCALE
);
513 outline
= face
->glyph
->outline
;
514 /* reject glyphs that don't produce any rendering */
515 if (error
|| outline
.n_points
<= 2)
519 TA_LOG_GLOBAL((" U+%04lX contains no (usable) outlines\n", ch
));
521 TA_LOG_GLOBAL((" component %d of cluster starting with U+%04lX"
522 " contains no (usable) outlines\n", i
, ch
));
527 /* now compute min or max point indices and coordinates */
528 points
= outline
.points
;
530 best_y
= 0; /* make compiler happy */
531 best_contour_first
= 0; /* ditto */
532 best_contour_last
= 0; /* ditto */
540 for (nn
= 0; nn
< outline
.n_contours
; first
= last
+ 1, nn
++)
542 FT_Int old_best_point
= best_point
;
546 last
= outline
.contours
[nn
];
548 /* avoid single-point contours since they are never */
549 /* rasterized; in some fonts, they correspond to mark */
550 /* attachment points that are way outside of the glyph's */
555 if (TA_LATIN_IS_TOP_BLUE(bs
)
556 || TA_LATIN_IS_SUB_TOP_BLUE(bs
))
558 for (pp
= first
; pp
<= last
; pp
++)
561 || points
[pp
].y
> best_y
)
564 best_y
= points
[pp
].y
;
565 ascender
= TA_MAX(ascender
, best_y
+ y_offset
);
568 descender
= TA_MIN(descender
, points
[pp
].y
+ y_offset
);
573 for (pp
= first
; pp
<= last
; pp
++)
576 || points
[pp
].y
< best_y
)
579 best_y
= points
[pp
].y
;
580 descender
= TA_MIN(descender
, best_y
+ y_offset
);
583 ascender
= TA_MAX(ascender
, points
[pp
].y
+ y_offset
);
587 if (best_point
!= old_best_point
)
589 best_contour_first
= first
;
590 best_contour_last
= last
;
595 /* now check whether the point belongs to a straight or round */
596 /* segment; we first need to find in which contour the extremum */
597 /* lies, then inspect its previous and next points */
600 FT_Pos best_x
= points
[best_point
].x
;
602 FT_Int best_segment_first
, best_segment_last
;
603 FT_Int best_on_point_first
, best_on_point_last
;
607 best_segment_first
= best_point
;
608 best_segment_last
= best_point
;
610 if (FT_CURVE_TAG(outline
.tags
[best_point
]) == FT_CURVE_TAG_ON
)
612 best_on_point_first
= best_point
;
613 best_on_point_last
= best_point
;
617 best_on_point_first
= -1;
618 best_on_point_last
= -1;
621 /* look for the previous and next points on the contour */
622 /* that are not on the same Y coordinate, then threshold */
623 /* the `closeness'... */
629 if (prev
> best_contour_first
)
632 prev
= best_contour_last
;
634 dist
= TA_ABS(points
[prev
].y
- best_y
);
635 /* accept a small distance or a small angle (both values are */
636 /* heuristic; value 20 corresponds to approx. 2.9 degrees) */
638 if (TA_ABS(points
[prev
].x
- best_x
) <= 20 * dist
)
641 best_segment_first
= prev
;
643 if (FT_CURVE_TAG(outline
.tags
[prev
]) == FT_CURVE_TAG_ON
)
645 best_on_point_first
= prev
;
646 if (best_on_point_last
< 0)
647 best_on_point_last
= prev
;
650 } while (prev
!= best_point
);
654 if (next
< best_contour_last
)
657 next
= best_contour_first
;
659 dist
= TA_ABS(points
[next
].y
- best_y
);
661 if (TA_ABS(points
[next
].x
- best_x
) <= 20 * dist
)
664 best_segment_last
= next
;
666 if (FT_CURVE_TAG(outline
.tags
[next
]) == FT_CURVE_TAG_ON
)
668 best_on_point_last
= next
;
669 if (best_on_point_first
< 0)
670 best_on_point_first
= next
;
673 } while (next
!= best_point
);
675 if (TA_LATIN_IS_LONG_BLUE(bs
))
677 /* If this flag is set, we have an additional constraint to */
678 /* get the blue zone distance: Find a segment of the topmost */
679 /* (or bottommost) contour that is longer than a heuristic */
680 /* threshold. This ensures that small bumps in the outline */
681 /* are ignored (for example, the `vertical serifs' found in */
682 /* many Hebrew glyph designs). */
684 /* If this segment is long enough, we are done. Otherwise, */
685 /* search the segment next to the extremum that is long */
686 /* enough, has the same direction, and a not too large */
687 /* vertical distance from the extremum. Note that the */
688 /* algorithm doesn't check whether the found segment is */
689 /* actually the one (vertically) nearest to the extremum. */
691 /* heuristic threshold value */
692 FT_Pos length_threshold
= metrics
->units_per_em
/ 25;
695 dist
= TA_ABS(points
[best_segment_last
].x
-
696 points
[best_segment_first
].x
);
698 if (dist
< length_threshold
699 && best_segment_last
- best_segment_first
+ 2 <=
700 best_contour_last
- best_contour_first
)
702 /* heuristic threshold value */
703 FT_Pos height_threshold
= metrics
->units_per_em
/ 4;
709 /* we intentionally declare these two variables */
710 /* outside of the loop since various compilers emit */
711 /* incorrect warning messages otherwise, talking about */
712 /* `possibly uninitialized variables' */
713 FT_Int p_first
= 0; /* make compiler happy */
719 /* compute direction */
724 if (prev
> best_contour_first
)
727 prev
= best_contour_last
;
729 if (points
[prev
].x
!= best_x
)
731 } while (prev
!= best_point
);
733 /* skip glyph for the degenerate case */
734 if (prev
== best_point
)
737 left2right
= FT_BOOL(points
[prev
].x
< points
[best_point
].x
);
739 first
= best_segment_last
;
751 /* no hit; adjust first point */
754 /* also adjust first and last on point */
755 if (FT_CURVE_TAG(outline
.tags
[first
]) == FT_CURVE_TAG_ON
)
769 if (last
< best_contour_last
)
772 last
= best_contour_first
;
774 if (TA_ABS(best_y
- points
[first
].y
) > height_threshold
)
776 /* vertical distance too large */
781 /* same test as above */
782 dist
= TA_ABS(points
[last
].y
- points
[first
].y
);
784 if (TA_ABS(points
[last
].x
- points
[first
].x
) <= 20 * dist
)
790 if (FT_CURVE_TAG(outline
.tags
[last
]) == FT_CURVE_TAG_ON
)
797 l2r
= FT_BOOL(points
[first
].x
< points
[last
].x
);
798 d
= TA_ABS(points
[last
].x
- points
[first
].x
);
800 if (l2r
== left2right
801 && d
>= length_threshold
)
803 /* all constraints are met; update segment after */
804 /* finding its end */
807 if (last
< best_contour_last
)
810 last
= best_contour_first
;
812 d
= TA_ABS(points
[last
].y
- points
[first
].y
);
814 if (TA_ABS(points
[next
].x
- points
[first
].x
) <=
817 if (last
> best_contour_first
)
820 last
= best_contour_last
;
826 if (FT_CURVE_TAG(outline
.tags
[last
]) == FT_CURVE_TAG_ON
)
832 } while (last
!= best_segment_first
);
834 best_y
= points
[first
].y
;
836 best_segment_first
= first
;
837 best_segment_last
= last
;
839 best_on_point_first
= p_first
;
840 best_on_point_last
= p_last
;
844 } while (last
!= best_segment_first
);
849 * for computing blue zones, we add the y offset as returned
850 * by the currently used OpenType feature --
851 * for example, superscript glyphs might be identical
852 * to subscript glyphs with a vertical shift
858 TA_LOG_GLOBAL((" U+%04lX: best_y = %5ld", ch
, best_y
));
860 TA_LOG_GLOBAL((" component %d of cluster starting with U+%04lX:"
861 " best_y = %5ld", i
, ch
, best_y
));
865 * now set the `round' flag depending on the segment's kind:
867 * - if the horizontal distance between the first and last
868 * `on' point is larger than a heuristic threshold
869 * we have a flat segment
870 * - if either the first or the last point of the segment is
871 * an `off' point, the segment is round, otherwise it is
874 if (best_on_point_first
>= 0
875 && best_on_point_last
>= 0
876 && (TA_ABS(points
[best_on_point_last
].x
877 - points
[best_on_point_first
].x
))
881 round
= FT_BOOL(FT_CURVE_TAG(outline
.tags
[best_segment_first
])
883 || FT_CURVE_TAG(outline
.tags
[best_segment_last
])
886 if (round
&& TA_LATIN_IS_NEUTRAL_BLUE(bs
))
888 /* only use flat segments for a neutral blue zone */
889 TA_LOG_GLOBAL((" (round, skipped)\n"));
893 TA_LOG_GLOBAL((" (%s)\n", round
? "round" : "flat"));
896 if (TA_LATIN_IS_TOP_BLUE(bs
))
898 if (best_y
> best_y_extremum
)
900 best_y_extremum
= best_y
;
906 if (best_y
< best_y_extremum
)
908 best_y_extremum
= best_y
;
915 if (!(best_y_extremum
== FT_INT_MIN
916 || best_y_extremum
== FT_INT_MAX
))
919 rounds
[num_rounds
++] = best_y_extremum
;
921 flats
[num_flats
++] = best_y_extremum
;
924 } /* end while loop */
926 if (num_flats
== 0 && num_rounds
== 0)
928 /* we couldn't find a single glyph to compute this blue zone, */
929 /* we will simply ignore it then */
930 TA_LOG_GLOBAL((" empty\n"));
934 /* we have computed the contents of the `rounds' and `flats' tables, */
935 /* now determine the reference and overshoot position of the blue -- */
936 /* we simply take the median value after a simple sort */
937 ta_sort_pos(num_rounds
, rounds
);
938 ta_sort_pos(num_flats
, flats
);
940 blue
= &axis
->blues
[axis
->blue_count
];
941 blue_ref
= &blue
->ref
.org
;
942 blue_shoot
= &blue
->shoot
.org
;
949 *blue_shoot
= rounds
[num_rounds
/ 2];
951 else if (num_rounds
== 0)
954 *blue_shoot
= flats
[num_flats
/ 2];
958 *blue_ref
= flats
[num_flats
/ 2];
959 *blue_shoot
= rounds
[num_rounds
/ 2];
962 /* there are sometimes problems if the overshoot position of top */
963 /* zones is under its reference position, or the opposite for bottom */
964 /* zones; we must thus check everything there and correct the errors */
965 if (*blue_shoot
!= *blue_ref
)
967 FT_Pos ref
= *blue_ref
;
968 FT_Pos shoot
= *blue_shoot
;
969 FT_Bool over_ref
= FT_BOOL(shoot
> ref
);
972 if ((TA_LATIN_IS_TOP_BLUE(bs
)
973 || TA_LATIN_IS_SUB_TOP_BLUE(bs
)) ^ over_ref
)
976 *blue_shoot
= (shoot
+ ref
) / 2;
978 TA_LOG_GLOBAL((" [overshoot smaller than reference,"
979 " taking mean value]\n"));
983 blue
->ascender
= ascender
;
984 blue
->descender
= descender
;
987 if (TA_LATIN_IS_TOP_BLUE(bs
))
988 blue
->flags
|= TA_LATIN_BLUE_TOP
;
989 if (TA_LATIN_IS_SUB_TOP_BLUE(bs
))
990 blue
->flags
|= TA_LATIN_BLUE_SUB_TOP
;
991 if (TA_LATIN_IS_NEUTRAL_BLUE(bs
))
992 blue
->flags
|= TA_LATIN_BLUE_NEUTRAL
;
994 /* the following flag is used later to adjust the y and x scales */
995 /* in order to optimize the pixel grid alignment */
996 /* of the top of small letters */
997 if (TA_LATIN_IS_X_HEIGHT_BLUE(bs
))
998 blue
->flags
|= TA_LATIN_BLUE_ADJUSTMENT
;
1000 TA_LOG_GLOBAL((" -> reference = %ld\n"
1001 " overshoot = %ld\n",
1002 *blue_ref
, *blue_shoot
));
1004 } /* end for loop */
1006 ta_shaper_buf_destroy(face
, shaper_buf
);
1008 /* if windows compatibility mode is activated, */
1009 /* add two artificial blue zones for usWinAscent and usWinDescent */
1010 /* just in case the above algorithm has missed them -- */
1011 /* Windows cuts off everything outside of those two values */
1012 if (metrics
->root
.globals
->font
->windows_compatibility
)
1017 os2
= (TT_OS2
*)FT_Get_Sfnt_Table(face
, ft_sfnt_os2
);
1021 blue
= &axis
->blues
[axis
->blue_count
];
1022 blue
->flags
= TA_LATIN_BLUE_TOP
| TA_LATIN_BLUE_ACTIVE
;
1024 blue
->shoot
.org
= os2
->usWinAscent
;
1026 TA_LOG_GLOBAL(("artificial blue zone for usWinAscent:\n"
1027 " -> reference = %ld\n"
1028 " overshoot = %ld\n",
1029 blue
->ref
.org
, blue
->shoot
.org
));
1031 blue
= &axis
->blues
[axis
->blue_count
+ 1];
1032 blue
->flags
= TA_LATIN_BLUE_ACTIVE
;
1034 blue
->shoot
.org
= -os2
->usWinDescent
;
1036 TA_LOG_GLOBAL(("artificial blue zone for usWinDescent:\n"
1037 " -> reference = %ld\n"
1038 " overshoot = %ld\n",
1039 blue
->ref
.org
, blue
->shoot
.org
));
1043 blue
= &axis
->blues
[axis
->blue_count
];
1046 blue
->shoot
.org
= 0;
1048 blue
= &axis
->blues
[axis
->blue_count
+ 1];
1051 blue
->shoot
.org
= 0;
1055 /* we finally check whether blue zones are ordered; */
1056 /* `ref' and `shoot' values of two blue zones must not overlap */
1057 if (axis
->blue_count
)
1060 TA_LatinBlue blue_sorted
[TA_BLUE_STRINGSET_MAX_LEN
+ 2];
1063 for (i
= 0; i
< axis
->blue_count
; i
++)
1064 blue_sorted
[i
] = &axis
->blues
[i
];
1066 /* sort bottoms of blue zones... */
1067 ta_latin_sort_blue(axis
->blue_count
, blue_sorted
);
1069 /* ...and adjust top values if necessary */
1070 for (i
= 0; i
< axis
->blue_count
- 1; i
++)
1076 FT_Bool a_is_top
= 0;
1080 if (blue_sorted
[i
]->flags
& (TA_LATIN_BLUE_TOP
1081 | TA_LATIN_BLUE_SUB_TOP
))
1083 a
= &blue_sorted
[i
]->shoot
.org
;
1089 a
= &blue_sorted
[i
]->ref
.org
;
1091 if (blue_sorted
[i
+ 1]->flags
& (TA_LATIN_BLUE_TOP
1092 | TA_LATIN_BLUE_SUB_TOP
))
1093 b
= &blue_sorted
[i
+ 1]->shoot
.org
;
1095 b
= &blue_sorted
[i
+ 1]->ref
.org
;
1100 TA_LOG_GLOBAL(("blue zone overlap:"
1101 " adjusting %s %d to %ld\n",
1102 a_is_top
? "overshoot" : "reference",
1103 blue_sorted
[i
] - axis
->blues
,
1109 TA_LOG_GLOBAL(("\n"));
1115 /* check whether all ASCII digits have the same advance width */
1118 ta_latin_metrics_check_digits(TA_LatinMetrics metrics
,
1121 FT_Bool started
= 0, same_width
= 1;
1122 FT_Fixed advance
= 0, old_advance
= 0;
1126 /* in all supported charmaps, digits have character codes 0x30-0x39 */
1127 const char digits
[] = "0 1 2 3 4 5 6 7 8 9";
1132 shaper_buf
= ta_shaper_buf_create(face
);
1136 FT_ULong glyph_index
;
1137 unsigned int num_idx
;
1140 /* reject input that maps to more than a single glyph */
1141 p
= ta_shaper_get_cluster(p
, &metrics
->root
, shaper_buf
, &num_idx
);
1145 glyph_index
= ta_shaper_get_elem(&metrics
->root
,
1155 if (advance
!= old_advance
)
1163 old_advance
= advance
;
1168 ta_shaper_buf_destroy(face
, shaper_buf
);
1170 metrics
->root
.digits_have_same_width
= same_width
;
1174 /* initialize global metrics */
1177 ta_latin_metrics_init(TA_LatinMetrics metrics
,
1181 FT_CharMap oldmap
= face
->charmap
;
1184 metrics
->units_per_em
= face
->units_per_EM
;
1186 if (!FT_Select_Charmap(face
, FT_ENCODING_UNICODE
))
1188 ta_latin_metrics_init_widths(metrics
, face
, 1);
1189 ta_latin_metrics_init_blues(metrics
, reference
? reference
: face
);
1190 ta_latin_metrics_check_digits(metrics
, face
);
1194 /* we only have a symbol font encoding */
1195 ta_latin_metrics_init_widths(metrics
, face
, 0);
1198 FT_Set_Charmap(face
, oldmap
);
1203 /* adjust scaling value, then scale and shift widths */
1204 /* and blue zones (if applicable) for given dimension */
1207 ta_latin_metrics_scale_dim(TA_LatinMetrics metrics
,
1218 ppem
= metrics
->root
.scaler
.face
->size
->metrics
.x_ppem
;
1220 if (dim
== TA_DIMENSION_HORZ
)
1222 scale
= scaler
->x_scale
;
1223 delta
= scaler
->x_delta
;
1227 scale
= scaler
->y_scale
;
1228 delta
= scaler
->y_delta
;
1231 axis
= &metrics
->axis
[dim
];
1233 if (axis
->org_scale
== scale
&& axis
->org_delta
== delta
)
1236 axis
->org_scale
= scale
;
1237 axis
->org_delta
= delta
;
1239 /* correct Y scale to optimize the alignment of the top of */
1240 /* small letters to the pixel grid */
1241 /* (if we do x-height snapping for this ppem value) */
1242 if (!number_set_is_element(
1243 metrics
->root
.globals
->font
->x_height_snapping_exceptions
,
1246 TA_LatinAxis Axis
= &metrics
->axis
[TA_DIMENSION_VERT
];
1247 TA_LatinBlue blue
= NULL
;
1250 for (nn
= 0; nn
< Axis
->blue_count
; nn
++)
1252 if (Axis
->blues
[nn
].flags
& TA_LATIN_BLUE_ADJUSTMENT
)
1254 blue
= &Axis
->blues
[nn
];
1267 scaled
= FT_MulFix(blue
->shoot
.org
, scale
);
1268 limit
= metrics
->root
.globals
->increase_x_height
;
1271 /* if the `increase-x-height' property is active, */
1272 /* we round up much more often */
1275 && ppem
>= TA_PROP_INCREASE_X_HEIGHT_MIN
)
1278 fitted
= (scaled
+ threshold
) & ~63;
1280 if (scaled
!= fitted
)
1282 if (dim
== TA_DIMENSION_VERT
)
1289 new_scale
= FT_MulDiv(scale
, fitted
, scaled
);
1291 /* the scaling should not change the result by more than two pixels */
1292 max_height
= metrics
->units_per_em
;
1294 for (nn
= 0; nn
< Axis
->blue_count
; nn
++)
1296 max_height
= TA_MAX(max_height
, Axis
->blues
[nn
].ascender
);
1297 max_height
= TA_MAX(max_height
, -Axis
->blues
[nn
].descender
);
1300 dist
= TA_ABS(FT_MulFix(max_height
, new_scale
- scale
));
1306 "ta_latin_metrics_scale_dim:"
1307 " x height alignment (style `%s'):\n"
1309 " vertical scaling changed from %.5f to %.5f (by %d%%)\n"
1311 ta_style_names
[metrics
->root
.style_class
->style
],
1313 new_scale
/ 65536.0,
1314 (fitted
- scaled
) * 100 / scaled
));
1322 "ta_latin_metrics_scale_dim:"
1323 " x height alignment (style `%s'):\n"
1325 " excessive vertical scaling abandoned\n"
1327 ta_style_names
[metrics
->root
.style_class
->style
]));
1335 axis
->scale
= scale
;
1336 axis
->delta
= delta
;
1338 if (dim
== TA_DIMENSION_HORZ
)
1340 metrics
->root
.scaler
.x_scale
= scale
;
1341 metrics
->root
.scaler
.x_delta
= delta
;
1345 metrics
->root
.scaler
.y_scale
= scale
;
1346 metrics
->root
.scaler
.y_delta
= delta
;
1349 TA_LOG_GLOBAL(("%s widths (style `%s')\n",
1350 dim
== TA_DIMENSION_HORZ
? "horizontal" : "vertical",
1351 ta_style_names
[metrics
->root
.style_class
->style
]));
1353 /* scale the widths */
1354 for (nn
= 0; nn
< axis
->width_count
; nn
++)
1356 TA_Width width
= axis
->widths
+ nn
;
1359 width
->cur
= FT_MulFix(width
->org
, scale
);
1360 width
->fit
= width
->cur
;
1362 TA_LOG_GLOBAL((" %d scaled to %.2f\n",
1364 width
->cur
/ 64.0));
1367 TA_LOG_GLOBAL(("\n"));
1369 /* an extra-light axis corresponds to a standard width that is */
1370 /* smaller than 5/8 pixels */
1372 (FT_Bool
)(FT_MulFix(axis
->standard_width
, scale
) < 32 + 8);
1375 if (axis
->extra_light
)
1376 TA_LOG_GLOBAL(("`%s' style is extra light (at current resolution)\n"
1378 ta_style_names
[metrics
->root
.style_class
->style
]));
1381 if (dim
== TA_DIMENSION_VERT
)
1384 if (axis
->blue_count
)
1385 TA_LOG_GLOBAL(("blue zones (style `%s')\n",
1386 ta_style_names
[metrics
->root
.style_class
->style
]));
1389 /* scale the blue zones */
1390 for (nn
= 0; nn
< axis
->blue_count
; nn
++)
1392 TA_LatinBlue blue
= &axis
->blues
[nn
];
1396 blue
->ref
.cur
= FT_MulFix(blue
->ref
.org
, scale
) + delta
;
1397 blue
->ref
.fit
= blue
->ref
.cur
;
1398 blue
->shoot
.cur
= FT_MulFix(blue
->shoot
.org
, scale
) + delta
;
1399 blue
->shoot
.fit
= blue
->shoot
.cur
;
1400 blue
->flags
&= ~TA_LATIN_BLUE_ACTIVE
;
1402 /* a blue zone is only active if it is less than 3/4 pixels tall */
1403 dist
= FT_MulFix(blue
->ref
.org
- blue
->shoot
.org
, scale
);
1404 if (dist
<= 48 && dist
>= -48)
1412 /* use discrete values for blue zone widths */
1415 /* generic, original code */
1416 delta1
= blue
->shoot
.org
- blue
->ref
.org
;
1421 delta2
= FT_MulFix(delta2
, scale
);
1425 else if (delta2
< 64)
1426 delta2
= 32 + (((delta2
- 32) + 16) & ~31);
1428 delta2
= TA_PIX_ROUND(delta2
);
1433 blue
->ref
.fit
= TA_PIX_ROUND(blue
->ref
.cur
);
1434 blue
->shoot
.fit
= blue
->ref
.fit
+ delta2
;
1436 /* simplified version due to abs(dist) <= 48 */
1443 else if (delta2
< 48)
1451 blue
->ref
.fit
= TA_PIX_ROUND(blue
->ref
.cur
);
1452 blue
->shoot
.fit
= blue
->ref
.fit
- delta2
;
1455 blue
->flags
|= TA_LATIN_BLUE_ACTIVE
;
1459 /* use sub-top blue zone only if it doesn't overlap with */
1460 /* another (non-sup-top) blue zone; otherwise, the */
1461 /* effect would be similar to a neutral blue zone, which */
1462 /* is not desired here */
1463 for (nn
= 0; nn
< axis
->blue_count
; nn
++)
1465 TA_LatinBlue blue
= &axis
->blues
[nn
];
1469 if (!(blue
->flags
& TA_LATIN_BLUE_SUB_TOP
))
1471 if (!(blue
->flags
& TA_LATIN_BLUE_ACTIVE
))
1474 for (i
= 0; i
< axis
->blue_count
; i
++)
1476 TA_LatinBlue b
= &axis
->blues
[i
];
1479 if (b
->flags
& TA_LATIN_BLUE_SUB_TOP
)
1481 if (!(b
->flags
& TA_LATIN_BLUE_ACTIVE
))
1484 if (b
->ref
.fit
<= blue
->shoot
.fit
1485 && b
->shoot
.fit
>= blue
->ref
.fit
)
1487 blue
->flags
&= ~TA_LATIN_BLUE_ACTIVE
;
1494 for (nn
= 0; nn
< axis
->blue_count
; nn
++)
1496 TA_LatinBlue blue
= &axis
->blues
[nn
];
1499 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f%s\n"
1500 " overshoot %d: %d scaled to %.2f%s\n",
1503 blue
->ref
.fit
/ 64.0,
1504 (blue
->flags
& TA_LATIN_BLUE_ACTIVE
) ? ""
1508 blue
->shoot
.fit
/ 64.0,
1509 (blue
->flags
& TA_LATIN_BLUE_ACTIVE
) ? ""
1514 /* the last two artificial blue zones are to be scaled */
1515 /* with uncorrected scaling values */
1516 if (metrics
->root
.globals
->font
->windows_compatibility
)
1518 TA_LatinAxis a
= &metrics
->axis
[TA_DIMENSION_VERT
];
1522 b
= &a
->blues
[a
->blue_count
];
1526 b
->shoot
.fit
= FT_MulFix(b
->ref
.org
, a
->org_scale
) + delta
;
1528 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f (artificial)\n"
1529 " overshoot %d: %d scaled to %.2f (artificial)\n",
1535 b
->shoot
.fit
/ 64.0));
1537 b
= &a
->blues
[a
->blue_count
+ 1];
1541 b
->shoot
.fit
= FT_MulFix(b
->ref
.org
, a
->org_scale
) + delta
;
1543 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f (artificial)\n"
1544 " overshoot %d: %d scaled to %.2f (artificial)\n",
1550 b
->shoot
.fit
/ 64.0));
1553 TA_LOG_GLOBAL(("\n"));
1558 /* scale global values in both directions */
1561 ta_latin_metrics_scale(TA_LatinMetrics metrics
,
1564 metrics
->root
.scaler
.render_mode
= scaler
->render_mode
;
1565 metrics
->root
.scaler
.face
= scaler
->face
;
1566 metrics
->root
.scaler
.flags
= scaler
->flags
;
1568 ta_latin_metrics_scale_dim(metrics
, scaler
, TA_DIMENSION_HORZ
);
1569 ta_latin_metrics_scale_dim(metrics
, scaler
, TA_DIMENSION_VERT
);
1573 /* walk over all contours and compute its segments */
1576 ta_latin_hints_compute_segments(TA_GlyphHints hints
,
1579 TA_LatinMetrics metrics
= (TA_LatinMetrics
)hints
->metrics
;
1580 TA_AxisHints axis
= &hints
->axis
[dim
];
1581 FT_Error error
= FT_Err_Ok
;
1583 TA_Segment segment
= NULL
;
1586 TA_Point
* contour
= hints
->contours
;
1587 TA_Point
* contour_limit
= contour
+ hints
->num_contours
;
1588 TA_Direction major_dir
, segment_dir
;
1590 FT_Pos flat_threshold
= FLAT_THRESHOLD(metrics
->units_per_em
);
1593 memset(&seg0
, 0, sizeof (TA_SegmentRec
));
1595 seg0
.flags
= TA_EDGE_NORMAL
;
1597 major_dir
= (TA_Direction
)TA_ABS(axis
->major_dir
);
1598 segment_dir
= major_dir
;
1600 axis
->num_segments
= 0;
1602 /* set up (u,v) in each point */
1603 if (dim
== TA_DIMENSION_HORZ
)
1605 TA_Point point
= hints
->points
;
1606 TA_Point limit
= point
+ hints
->num_points
;
1609 for (; point
< limit
; point
++)
1611 point
->u
= point
->fx
;
1612 point
->v
= point
->fy
;
1617 TA_Point point
= hints
->points
;
1618 TA_Point limit
= point
+ hints
->num_points
;
1621 for (; point
< limit
; point
++)
1623 point
->u
= point
->fy
;
1624 point
->v
= point
->fx
;
1628 /* do each contour separately */
1629 for (; contour
< contour_limit
; contour
++)
1631 TA_Point point
= contour
[0];
1632 TA_Point last
= point
->prev
;
1636 /* we call values measured along a segment (point->v) */
1637 /* `coordinates', and values orthogonal to it (point->u) */
1639 FT_Pos min_pos
= 32000;
1640 FT_Pos max_pos
= -32000;
1641 FT_Pos min_coord
= 32000;
1642 FT_Pos max_coord
= -32000;
1643 FT_UShort min_flags
= TA_FLAG_NONE
;
1644 FT_UShort max_flags
= TA_FLAG_NONE
;
1645 FT_Pos min_on_coord
= 32000;
1646 FT_Pos max_on_coord
= -32000;
1650 TA_Segment prev_segment
= NULL
;
1652 FT_Pos prev_min_pos
= min_pos
;
1653 FT_Pos prev_max_pos
= max_pos
;
1654 FT_Pos prev_min_coord
= min_coord
;
1655 FT_Pos prev_max_coord
= max_coord
;
1656 FT_UShort prev_min_flags
= min_flags
;
1657 FT_UShort prev_max_flags
= max_flags
;
1658 FT_Pos prev_min_on_coord
= min_on_coord
;
1659 FT_Pos prev_max_on_coord
= max_on_coord
;
1662 if (TA_ABS(last
->out_dir
) == major_dir
1663 && TA_ABS(point
->out_dir
) == major_dir
)
1665 /* we are already on an edge, try to locate its start */
1670 point
= point
->prev
;
1671 if (TA_ABS(point
->out_dir
) != major_dir
)
1673 point
= point
->next
;
1691 /* get minimum and maximum position */
1698 /* get minimum and maximum coordinate together with flags */
1703 min_flags
= point
->flags
;
1708 max_flags
= point
->flags
;
1711 /* get minimum and maximum coordinate of `on' points */
1712 if (!(point
->flags
& TA_FLAG_CONTROL
))
1715 if (v
< min_on_coord
)
1717 if (v
> max_on_coord
)
1721 if (point
->out_dir
!= segment_dir
1724 /* check whether the new segment's start point is identical to */
1725 /* the previous segment's end point; for example, this might */
1726 /* happen for spikes */
1729 || segment
->first
!= prev_segment
->last
)
1731 /* points are different: we are just leaving an edge, thus */
1732 /* record a new segment */
1734 segment
->last
= point
;
1735 segment
->pos
= (FT_Short
)((min_pos
+ max_pos
) >> 1);
1736 segment
->delta
= (FT_Short
)((max_pos
- min_pos
) >> 1);
1738 /* a segment is round if either its first or last point */
1739 /* is a control point, and the length of the on points */
1740 /* inbetween doesn't exceed a heuristic limit */
1741 if ((min_flags
| max_flags
) & TA_FLAG_CONTROL
1742 && (max_on_coord
- min_on_coord
) < flat_threshold
)
1743 segment
->flags
|= TA_EDGE_ROUND
;
1745 segment
->min_coord
= (FT_Short
)min_coord
;
1746 segment
->max_coord
= (FT_Short
)max_coord
;
1747 segment
->height
= segment
->max_coord
- segment
->min_coord
;
1749 prev_segment
= segment
;
1750 prev_min_pos
= min_pos
;
1751 prev_max_pos
= max_pos
;
1752 prev_min_coord
= min_coord
;
1753 prev_max_coord
= max_coord
;
1754 prev_min_flags
= min_flags
;
1755 prev_max_flags
= max_flags
;
1756 prev_min_on_coord
= min_on_coord
;
1757 prev_max_on_coord
= max_on_coord
;
1761 /* points are the same: we don't create a new segment but */
1762 /* merge the current segment with the previous one */
1764 if (prev_segment
->last
->in_dir
== point
->in_dir
)
1766 /* we have identical directions (this can happen for */
1767 /* degenerate outlines that move zig-zag along the main */
1768 /* axis without changing the coordinate value of the other */
1769 /* axis, and where the segments have just been merged): */
1770 /* unify segments */
1772 /* update constraints */
1774 if (prev_min_pos
< min_pos
)
1775 min_pos
= prev_min_pos
;
1776 if (prev_max_pos
> max_pos
)
1777 max_pos
= prev_max_pos
;
1779 if (prev_min_coord
< min_coord
)
1781 min_coord
= prev_min_coord
;
1782 min_flags
= prev_min_flags
;
1784 if (prev_max_coord
> max_coord
)
1786 max_coord
= prev_max_coord
;
1787 max_flags
= prev_max_flags
;
1790 if (prev_min_on_coord
< min_on_coord
)
1791 min_on_coord
= prev_min_on_coord
;
1792 if (prev_max_on_coord
> max_on_coord
)
1793 max_on_coord
= prev_max_on_coord
;
1795 prev_segment
->last
= point
;
1796 prev_segment
->pos
= (FT_Short
)((min_pos
+ max_pos
) >> 1);
1798 if ((min_flags
| max_flags
) & TA_FLAG_CONTROL
1799 && (max_on_coord
- min_on_coord
) < flat_threshold
)
1800 prev_segment
->flags
|= TA_EDGE_ROUND
;
1802 prev_segment
->flags
&= ~TA_EDGE_ROUND
;
1804 prev_segment
->min_coord
= (FT_Short
)min_coord
;
1805 prev_segment
->max_coord
= (FT_Short
)max_coord
;
1806 prev_segment
->height
= prev_segment
->max_coord
1807 - prev_segment
->min_coord
;
1811 /* we have different directions; use the properties of the */
1812 /* longer segment and discard the other one */
1814 if (TA_ABS(prev_max_coord
- prev_min_coord
)
1815 > TA_ABS(max_coord
- min_coord
))
1817 /* discard current segment */
1819 if (min_pos
< prev_min_pos
)
1820 prev_min_pos
= min_pos
;
1821 if (max_pos
> prev_max_pos
)
1822 prev_max_pos
= max_pos
;
1824 prev_segment
->last
= point
;
1825 prev_segment
->pos
= (FT_Short
)((prev_min_pos
1826 + prev_max_pos
) >> 1);
1830 /* discard previous segment */
1832 if (prev_min_pos
< min_pos
)
1833 min_pos
= prev_min_pos
;
1834 if (prev_max_pos
> max_pos
)
1835 max_pos
= prev_max_pos
;
1837 segment
->last
= point
;
1838 segment
->pos
= (FT_Short
)((min_pos
+ max_pos
) >> 1);
1840 if ((min_flags
| max_flags
) & TA_FLAG_CONTROL
1841 && (max_on_coord
- min_on_coord
) < flat_threshold
)
1842 segment
->flags
|= TA_EDGE_ROUND
;
1844 segment
->min_coord
= (FT_Short
)min_coord
;
1845 segment
->max_coord
= (FT_Short
)max_coord
;
1846 segment
->height
= segment
->max_coord
- segment
->min_coord
;
1848 *prev_segment
= *segment
;
1850 prev_min_pos
= min_pos
;
1851 prev_max_pos
= max_pos
;
1852 prev_min_coord
= min_coord
;
1853 prev_max_coord
= max_coord
;
1854 prev_min_flags
= min_flags
;
1855 prev_max_flags
= max_flags
;
1856 prev_min_on_coord
= min_on_coord
;
1857 prev_max_on_coord
= max_on_coord
;
1861 axis
->num_segments
--;
1871 /* now exit if we are at the start/end point */
1879 /* if we are not on an edge, check whether the major direction */
1880 /* coincides with the current point's `out' direction, or */
1881 /* whether we have a single-point contour */
1883 && (TA_ABS(point
->out_dir
) == major_dir
1884 || point
== point
->prev
))
1887 * For efficiency, we restrict the number of segments to 1000,
1888 * which is a heuristic value: it is very unlikely that a glyph
1889 * with so many segments can be hinted in a sensible way.
1892 * - The glyph has really 1000 segments; this implies that it has
1893 * at least 2000 outline points. Assuming 'normal' fonts that
1894 * have superfluous points optimized away, viewing such a glyph
1895 * only makes sense at large magnifications where hinting
1896 * isn't applied anyway.
1898 * - We have a broken glyph. Hinting doesn't make sense in this
1901 if (axis
->num_segments
> 1000)
1903 TA_LOG_GLOBAL(("ta_latin_hints_compute_segments:"
1904 " more than 1000 segments in this glyph;\n"
1906 " hinting is suppressed\n"));
1907 axis
->num_segments
= 0;
1911 /* this is the start of a new segment! */
1912 segment_dir
= (TA_Direction
)point
->out_dir
;
1914 error
= ta_axis_hints_new_segment(axis
, &segment
);
1918 /* clear all segment fields */
1921 segment
->dir
= (FT_Char
)segment_dir
;
1922 segment
->first
= point
;
1923 segment
->last
= point
;
1925 /* `ta_axis_hints_new_segment' reallocates memory, */
1926 /* thus we have to refresh the `prev_segment' pointer */
1928 prev_segment
= segment
- 1;
1930 min_pos
= max_pos
= point
->u
;
1931 min_coord
= max_coord
= point
->v
;
1932 min_flags
= max_flags
= point
->flags
;
1934 if (point
->flags
& TA_FLAG_CONTROL
)
1936 min_on_coord
= 32000;
1937 max_on_coord
= -32000;
1940 min_on_coord
= max_on_coord
= point
->v
;
1944 if (point
->out_dir
!= point
->next
->in_dir
1945 || point
== point
->prev
)
1948 * We have a one-point segment. This is either
1950 * . a one-point contour (with `in' and `out' direction
1951 * set to TA_DIR_NONE by default), or
1953 * . an artificial one-point segment (with a forced
1956 segment
->pos
= (FT_Short
)min_pos
;
1958 if (point
->flags
& TA_FLAG_CONTROL
)
1959 segment
->flags
|= TA_EDGE_ROUND
;
1961 /* artificially extend the horizontal size if requested */
1962 segment
->min_coord
= (FT_Short
)point
->v
+ point
->left_offset
;
1963 segment
->max_coord
= (FT_Short
)point
->v
+ point
->right_offset
;
1964 segment
->height
= 0;
1971 point
= point
->next
;
1976 /* now slightly increase the height of segments if this makes sense -- */
1977 /* this is used to better detect and ignore serifs */
1979 TA_Segment segments
= axis
->segments
;
1980 TA_Segment segments_end
= segments
+ axis
->num_segments
;
1983 for (segment
= segments
; segment
< segments_end
; segment
++)
1985 TA_Point first
= segment
->first
;
1986 TA_Point last
= segment
->last
;
1988 FT_Pos first_v
= first
->v
;
1989 FT_Pos last_v
= last
->v
;
1992 if (first_v
< last_v
)
1999 segment
->height
= (FT_Short
)(segment
->height
+
2000 ((first_v
- p
->v
) >> 1));
2004 segment
->height
= (FT_Short
)(segment
->height
+
2005 ((p
->v
- last_v
) >> 1));
2014 segment
->height
= (FT_Short
)(segment
->height
+
2015 ((p
->v
- first_v
) >> 1));
2019 segment
->height
= (FT_Short
)(segment
->height
+
2020 ((last_v
- p
->v
) >> 1));
2030 /* link segments to form stems and serifs; if `width_count' and */
2031 /* `widths' are non-zero, use them to fine-tune the scoring function */
2034 ta_latin_hints_link_segments(TA_GlyphHints hints
,
2035 FT_UInt width_count
,
2036 TA_WidthRec
* widths
,
2039 TA_AxisHints axis
= &hints
->axis
[dim
];
2041 TA_Segment segments
= axis
->segments
;
2042 TA_Segment segment_limit
= segments
+ axis
->num_segments
;
2044 FT_Pos len_threshold
, len_score
, dist_score
, max_width
;
2045 TA_Segment seg1
, seg2
;
2049 max_width
= widths
[width_count
- 1].org
;
2053 /* a heuristic value to set up a minimum value for overlapping */
2054 len_threshold
= TA_LATIN_CONSTANT(hints
->metrics
, 8);
2055 if (len_threshold
== 0)
2058 /* a heuristic value to weight lengths */
2059 len_score
= TA_LATIN_CONSTANT(hints
->metrics
, 6000);
2061 /* a heuristic value to weight distances (no call to */
2062 /* TA_LATIN_CONSTANT needed, since we work on multiples */
2063 /* of the stem width) */
2066 /* now compare each segment to the others */
2067 for (seg1
= segments
; seg1
< segment_limit
; seg1
++)
2069 if (seg1
->dir
!= axis
->major_dir
)
2072 /* search for stems having opposite directions, */
2073 /* with seg1 to the `left' of seg2 */
2074 for (seg2
= segments
; seg2
< segment_limit
; seg2
++)
2076 FT_Pos pos1
= seg1
->pos
;
2077 FT_Pos pos2
= seg2
->pos
;
2080 if (seg1
->dir
+ seg2
->dir
== 0
2083 /* compute distance between the two segments */
2084 FT_Pos min
= seg1
->min_coord
;
2085 FT_Pos max
= seg1
->max_coord
;
2089 if (min
< seg2
->min_coord
)
2090 min
= seg2
->min_coord
;
2091 if (max
> seg2
->max_coord
)
2092 max
= seg2
->max_coord
;
2094 /* compute maximum coordinate difference of the two segments */
2095 /* (this is, how much they overlap) */
2098 /* for one-point segments, `len' is zero if there is an overlap */
2099 /* (and negative otherwise); we have to correct this */
2101 && (seg1
->min_coord
== seg1
->max_coord
2102 || seg2
->min_coord
== seg2
->max_coord
))
2103 len
= len_threshold
;
2105 if (len
>= len_threshold
)
2108 * The score is the sum of two demerits indicating the
2109 * `badness' of a fit, measured along the segments' main axis
2110 * and orthogonal to it, respectively.
2112 * o The less overlapping along the main axis, the worse it
2113 * is, causing a larger demerit.
2115 * o The nearer the orthogonal distance to a stem width, the
2116 * better it is, causing a smaller demerit. For simplicity,
2117 * however, we only increase the demerit for values that
2118 * exceed the largest stem width.
2121 FT_Pos dist
= pos2
- pos1
;
2123 FT_Pos dist_demerit
, score
;
2128 /* distance demerits are based on multiples of `max_width'; */
2129 /* we scale by 1024 for getting more precision */
2130 FT_Pos delta
= (dist
<< 10) / max_width
- (1 << 10);
2134 dist_demerit
= 32000;
2136 dist_demerit
= delta
* delta
/ dist_score
;
2141 dist_demerit
= dist
; /* default if no widths available */
2143 score
= dist_demerit
+ len_score
/ len
;
2145 /* and we search for the smallest score */
2146 if (score
< seg1
->score
)
2148 seg1
->score
= score
;
2152 if (score
< seg2
->score
)
2154 seg2
->score
= score
;
2162 /* now compute the `serif' segments, cf. explanations in `tahints.h' */
2163 for (seg1
= segments
; seg1
< segment_limit
; seg1
++)
2169 if (seg2
->link
!= seg1
)
2172 seg1
->serif
= seg2
->link
;
2179 /* link segments to edges, using feature analysis for selection */
2182 ta_latin_hints_compute_edges(TA_GlyphHints hints
,
2185 TA_AxisHints axis
= &hints
->axis
[dim
];
2186 FT_Error error
= FT_Err_Ok
;
2187 TA_LatinAxis laxis
= &((TA_LatinMetrics
)hints
->metrics
)->axis
[dim
];
2189 TA_StyleClass style_class
= hints
->metrics
->style_class
;
2190 TA_ScriptClass script_class
= ta_script_classes
[style_class
->script
];
2192 FT_Bool top_to_bottom_hinting
= 0;
2194 TA_Segment segments
= axis
->segments
;
2195 TA_Segment segment_limit
= segments
+ axis
->num_segments
;
2199 TA_Direction up_dir
;
2202 FT_Pos edge_distance_threshold
;
2203 FT_Pos segment_length_threshold
;
2204 FT_Pos segment_width_threshold
;
2207 axis
->num_edges
= 0;
2209 scale
= (dim
== TA_DIMENSION_HORZ
) ? hints
->x_scale
2213 up_dir
= (dim
== TA_DIMENSION_HORZ
) ? TA_DIR_UP
2217 if (dim
== TA_DIMENSION_VERT
)
2218 top_to_bottom_hinting
= script_class
->top_to_bottom_hinting
;
2220 /* we ignore all segments that are less than 1 pixel in length */
2221 /* to avoid many problems with serif fonts */
2222 /* (the corresponding threshold is computed in font units) */
2223 if (dim
== TA_DIMENSION_HORZ
)
2224 segment_length_threshold
= FT_DivFix(64, hints
->y_scale
);
2226 segment_length_threshold
= 0;
2229 * Similarly, we ignore segments that have a width delta
2230 * larger than 0.5px (i.e., a width larger than 1px).
2232 segment_width_threshold
= FT_DivFix(32, scale
);
2234 /********************************************************************/
2236 /* We begin by generating a sorted table of edges for the current */
2237 /* direction. To do so, we simply scan each segment and try to find */
2238 /* an edge in our table that corresponds to its position. */
2240 /* If no edge is found, we create and insert a new edge in the */
2241 /* sorted table. Otherwise, we simply add the segment to the edge's */
2242 /* list which gets processed in the second step to compute the */
2243 /* edge's properties. */
2245 /* Note that the table of edges is sorted along the segment/edge */
2248 /********************************************************************/
2250 /* assure that edge distance threshold is at most 0.25px */
2251 edge_distance_threshold
= FT_MulFix(laxis
->edge_distance_threshold
,
2253 if (edge_distance_threshold
> 64 / 4)
2254 edge_distance_threshold
= 64 / 4;
2256 edge_distance_threshold
= FT_DivFix(edge_distance_threshold
,
2259 for (seg
= segments
; seg
< segment_limit
; seg
++)
2261 TA_Edge found
= NULL
;
2265 /* ignore too short segments, too wide ones, and, in this loop, */
2266 /* one-point segments without a direction */
2267 if (seg
->height
< segment_length_threshold
2268 || seg
->delta
> segment_width_threshold
2269 || seg
->dir
== TA_DIR_NONE
)
2272 /* a special case for serif edges: */
2273 /* if they are smaller than 1.5 pixels we ignore them */
2275 && 2 * seg
->height
< 3 * segment_length_threshold
)
2278 /* look for an edge corresponding to the segment */
2279 for (ee
= 0; ee
< axis
->num_edges
; ee
++)
2281 TA_Edge edge
= axis
->edges
+ ee
;
2285 dist
= seg
->pos
- edge
->fpos
;
2289 if (dist
< edge_distance_threshold
&& edge
->dir
== seg
->dir
)
2301 /* insert a new edge in the list and sort according to the position */
2302 error
= ta_axis_hints_new_edge(axis
, seg
->pos
,
2303 (TA_Direction
)seg
->dir
,
2304 top_to_bottom_hinting
,
2309 /* add the segment to the new edge's list */
2310 memset(edge
, 0, sizeof (TA_EdgeRec
));
2313 edge
->dir
= seg
->dir
;
2314 edge
->fpos
= seg
->pos
;
2315 edge
->opos
= FT_MulFix(seg
->pos
, scale
);
2316 edge
->pos
= edge
->opos
;
2317 seg
->edge_next
= seg
;
2321 /* if an edge was found, simply add the segment to the edge's list */
2322 seg
->edge_next
= found
->first
;
2323 found
->last
->edge_next
= seg
;
2328 /* we loop again over all segments to catch one-point segments */
2329 /* without a direction: if possible, link them to existing edges */
2330 for (seg
= segments
; seg
< segment_limit
; seg
++)
2332 TA_Edge found
= NULL
;
2336 if (seg
->dir
!= TA_DIR_NONE
)
2339 /* look for an edge corresponding to the segment */
2340 for (ee
= 0; ee
< axis
->num_edges
; ee
++)
2342 TA_Edge edge
= axis
->edges
+ ee
;
2346 dist
= seg
->pos
- edge
->fpos
;
2350 if (dist
< edge_distance_threshold
)
2357 /* one-point segments without a match are ignored */
2360 seg
->edge_next
= found
->first
;
2361 found
->last
->edge_next
= seg
;
2366 /*****************************************************************/
2368 /* Good, we now compute each edge's properties according to */
2369 /* the segments found on its position. Basically, these are */
2371 /* - the edge's main direction */
2372 /* - stem edge, serif edge or both (which defaults to stem then) */
2373 /* - rounded edge, straight or both (which defaults to straight) */
2374 /* - link for edge */
2376 /*****************************************************************/
2378 /* first of all, set the `edge' field in each segment -- this is */
2379 /* required in order to compute edge links */
2381 /* note that removing this loop and setting the `edge' field of each */
2382 /* segment directly in the code above slows down execution speed for */
2383 /* some reasons on platforms like the Sun */
2385 TA_Edge edges
= axis
->edges
;
2386 TA_Edge edge_limit
= edges
+ axis
->num_edges
;
2390 for (edge
= edges
; edge
< edge_limit
; edge
++)
2397 seg
= seg
->edge_next
;
2398 } while (seg
!= edge
->first
);
2401 /* now compute each edge properties */
2402 for (edge
= edges
; edge
< edge_limit
; edge
++)
2404 FT_Int is_round
= 0; /* does it contain round segments? */
2405 FT_Int is_straight
= 0; /* does it contain straight segments? */
2407 FT_Pos ups
= 0; /* number of upwards segments */
2408 FT_Pos downs
= 0; /* number of downwards segments */
2419 /* check for roundness of segment */
2420 if (seg
->flags
& TA_EDGE_ROUND
)
2426 /* check for segment direction */
2427 if (seg
->dir
== up_dir
)
2428 ups
+= seg
->max_coord
- seg
->min_coord
;
2430 downs
+= seg
->max_coord
- seg
->min_coord
;
2433 /* check for links -- */
2434 /* if seg->serif is set, then seg->link must be ignored */
2435 is_serif
= (FT_Bool
)(seg
->serif
2437 && seg
->serif
->edge
!= edge
);
2439 if ((seg
->link
&& seg
->link
->edge
)
2452 edge2
= edge
->serif
;
2461 edge_delta
= edge
->fpos
- edge2
->fpos
;
2463 edge_delta
= -edge_delta
;
2465 seg_delta
= seg
->pos
- seg2
->pos
;
2467 seg_delta
= -seg_delta
;
2469 if (seg_delta
< edge_delta
)
2477 edge
->serif
= edge2
;
2478 edge2
->flags
|= TA_EDGE_SERIF
;
2484 seg
= seg
->edge_next
;
2485 } while (seg
!= edge
->first
);
2487 /* set the round/straight flags */
2488 edge
->flags
= TA_EDGE_NORMAL
;
2491 && is_round
>= is_straight
)
2492 edge
->flags
|= TA_EDGE_ROUND
;
2495 /* set the edge's main direction */
2496 edge
->dir
= TA_DIR_NONE
;
2499 edge
->dir
= (FT_Char
)up_dir
;
2501 else if (ups
< downs
)
2502 edge
->dir
= (FT_Char
)-up_dir
;
2504 else if (ups
== downs
)
2505 edge
->dir
= 0; /* both up and down! */
2508 /* get rid of serifs if link is set */
2509 /* XXX: this gets rid of many unpleasant artefacts! */
2510 /* example: the `c' in cour.pfa at size 13 */
2512 if (edge
->serif
&& edge
->link
)
2522 /* detect segments and edges for given dimension */
2525 ta_latin_hints_detect_features(TA_GlyphHints hints
,
2526 FT_UInt width_count
,
2527 TA_WidthRec
* widths
,
2533 error
= ta_latin_hints_compute_segments(hints
, dim
);
2536 ta_latin_hints_link_segments(hints
, width_count
, widths
, dim
);
2538 error
= ta_latin_hints_compute_edges(hints
, dim
);
2545 /* compute all edges which lie within blue zones */
2548 ta_latin_hints_compute_blue_edges(TA_GlyphHints hints
,
2549 TA_LatinMetrics metrics
)
2551 TA_AxisHints axis
= &hints
->axis
[TA_DIMENSION_VERT
];
2553 TA_Edge edge
= axis
->edges
;
2554 TA_Edge edge_limit
= edge
+ axis
->num_edges
;
2556 TA_LatinAxis latin
= &metrics
->axis
[TA_DIMENSION_VERT
];
2557 FT_Fixed scale
= latin
->scale
;
2560 /* compute which blue zones are active, */
2561 /* i.e. have their scaled size < 3/4 pixels */
2563 /* for each horizontal edge search the blue zone which is closest */
2564 for (; edge
< edge_limit
; edge
++)
2567 TA_Width best_blue
= NULL
;
2568 FT_Bool best_blue_is_neutral
= 0;
2569 FT_Pos best_dist
; /* initial threshold */
2571 FT_UInt best_blue_idx
= 0;
2572 FT_Bool best_blue_is_shoot
= 0;
2575 /* compute the initial threshold as a fraction of the EM size */
2576 /* (the value 40 is heuristic) */
2577 best_dist
= FT_MulFix(metrics
->units_per_em
/ 40, scale
);
2579 /* assure a minimum distance of 0.5px */
2580 if (best_dist
> 64 / 2)
2583 /* this loop also handles the two extra blue zones */
2584 /* for usWinAscent and usWinDescent */
2585 /* if option `windows-compatibility' is set */
2587 bb
< latin
->blue_count
2588 + (metrics
->root
.globals
->font
->windows_compatibility
? 2 : 0);
2591 TA_LatinBlue blue
= latin
->blues
+ bb
;
2592 FT_Bool is_top_blue
, is_neutral_blue
, is_major_dir
;
2595 /* skip inactive blue zones (i.e., those that are too large) */
2596 if (!(blue
->flags
& TA_LATIN_BLUE_ACTIVE
))
2599 /* if it is a top zone, check for right edges (against the major */
2600 /* direction); if it is a bottom zone, check for left edges (in */
2601 /* the major direction) */
2602 is_top_blue
= (FT_Byte
)((blue
->flags
& (TA_LATIN_BLUE_TOP
2603 | TA_LATIN_BLUE_SUB_TOP
)) != 0);
2604 is_neutral_blue
= (FT_Byte
)((blue
->flags
& TA_LATIN_BLUE_NEUTRAL
) != 0);
2605 is_major_dir
= FT_BOOL(edge
->dir
== axis
->major_dir
);
2607 /* neutral blue zones are handled for both directions */
2608 if (is_top_blue
^ is_major_dir
|| is_neutral_blue
)
2613 /* first of all, compare it to the reference position */
2614 dist
= edge
->fpos
- blue
->ref
.org
;
2618 dist
= FT_MulFix(dist
, scale
);
2619 if (dist
< best_dist
)
2622 best_blue
= &blue
->ref
;
2623 best_blue_is_neutral
= is_neutral_blue
;
2626 best_blue_is_shoot
= 0;
2629 /* now compare it to the overshoot position and check whether */
2630 /* the edge is rounded, and whether the edge is over the */
2631 /* reference position of a top zone, or under the reference */
2632 /* position of a bottom zone (provided we don't have a */
2633 /* neutral blue zone) */
2634 if (edge
->flags
& TA_EDGE_ROUND
2636 && !is_neutral_blue
)
2638 FT_Bool is_under_ref
= FT_BOOL(edge
->fpos
< blue
->ref
.org
);
2641 if (is_top_blue
^ is_under_ref
)
2643 dist
= edge
->fpos
- blue
->shoot
.org
;
2647 dist
= FT_MulFix(dist
, scale
);
2648 if (dist
< best_dist
)
2651 best_blue
= &blue
->shoot
;
2652 best_blue_is_neutral
= is_neutral_blue
;
2655 best_blue_is_shoot
= 1;
2664 edge
->blue_edge
= best_blue
;
2665 edge
->best_blue_idx
= best_blue_idx
;
2666 edge
->best_blue_is_shoot
= best_blue_is_shoot
;
2667 if (best_blue_is_neutral
)
2668 edge
->flags
|= TA_EDGE_NEUTRAL
;
2674 /* initalize hinting engine */
2677 ta_latin_hints_init(TA_GlyphHints hints
,
2678 TA_LatinMetrics metrics
)
2680 FT_Render_Mode mode
;
2681 FT_UInt32 scaler_flags
, other_flags
;
2682 FT_Face face
= metrics
->root
.scaler
.face
;
2685 ta_glyph_hints_rescale(hints
, (TA_StyleMetrics
)metrics
);
2687 /* correct x_scale and y_scale if needed, since they may have */
2688 /* been modified by `ta_latin_metrics_scale_dim' above */
2689 hints
->x_scale
= metrics
->axis
[TA_DIMENSION_HORZ
].scale
;
2690 hints
->x_delta
= metrics
->axis
[TA_DIMENSION_HORZ
].delta
;
2691 hints
->y_scale
= metrics
->axis
[TA_DIMENSION_VERT
].scale
;
2692 hints
->y_delta
= metrics
->axis
[TA_DIMENSION_VERT
].delta
;
2694 /* compute flags depending on render mode, etc. */
2695 mode
= metrics
->root
.scaler
.render_mode
;
2697 #if 0 /* #ifdef TA_CONFIG_OPTION_USE_WARPER */
2698 if (mode
== FT_RENDER_MODE_LCD
2699 || mode
== FT_RENDER_MODE_LCD_V
)
2700 metrics
->root
.scaler
.render_mode
=
2701 mode
= FT_RENDER_MODE_NORMAL
;
2704 scaler_flags
= hints
->scaler_flags
;
2707 /* we snap the width of vertical stems for the monochrome */
2708 /* and horizontal LCD rendering targets only */
2709 if (mode
== FT_RENDER_MODE_MONO
2710 || mode
== FT_RENDER_MODE_LCD
)
2711 other_flags
|= TA_LATIN_HINTS_HORZ_SNAP
;
2713 /* we snap the width of horizontal stems for the monochrome */
2714 /* and vertical LCD rendering targets only */
2715 if (mode
== FT_RENDER_MODE_MONO
2716 || mode
== FT_RENDER_MODE_LCD_V
)
2717 other_flags
|= TA_LATIN_HINTS_VERT_SNAP
;
2719 /* we adjust stems to full pixels unless in `light' or `lcd' mode */
2720 if (mode
!= FT_RENDER_MODE_LIGHT
&& mode
!= FT_RENDER_MODE_LCD
)
2721 other_flags
|= TA_LATIN_HINTS_STEM_ADJUST
;
2723 if (mode
== FT_RENDER_MODE_MONO
)
2724 other_flags
|= TA_LATIN_HINTS_MONO
;
2726 /* in `light' or `lcd' mode we disable horizontal hinting completely; */
2727 /* we also do it if the face is italic -- */
2728 /* however, if warping is enabled (which only works in `light' hinting */
2729 /* mode), advance widths get adjusted, too */
2730 if (mode
== FT_RENDER_MODE_LIGHT
|| mode
== FT_RENDER_MODE_LCD
2731 || (face
->style_flags
& FT_STYLE_FLAG_ITALIC
) != 0)
2732 scaler_flags
|= TA_SCALER_FLAG_NO_HORIZONTAL
;
2734 #ifdef TA_CONFIG_OPTION_USE_WARPER
2735 /* get (global) warper flag */
2736 if (!metrics
->root
.globals
->module
->warping
)
2737 scaler_flags
|= TA_SCALER_FLAG_NO_WARPER
;
2740 hints
->scaler_flags
= scaler_flags
;
2741 hints
->other_flags
= other_flags
;
2747 /* snap a given width in scaled coordinates to */
2748 /* one of the current standard widths */
2751 ta_latin_snap_width(TA_Width widths
,
2756 FT_Pos best
= 64 + 32 + 2;
2757 FT_Pos reference
= width
;
2761 for (n
= 0; n
< count
; n
++)
2778 scaled
= TA_PIX_ROUND(reference
);
2780 if (width
>= reference
)
2782 if (width
< scaled
+ 48)
2787 if (width
> scaled
- 48)
2795 /* compute the snapped width of a given stem, ignoring very thin ones */
2797 /* there is a lot of voodoo in this function; changing the hard-coded */
2798 /* parameters influences the whole hinting process */
2801 ta_latin_compute_stem_width(TA_GlyphHints hints
,
2808 TA_LatinMetrics metrics
= (TA_LatinMetrics
) hints
->metrics
;
2809 TA_LatinAxis axis
= &metrics
->axis
[dim
];
2811 FT_Pos dist
= width
;
2813 FT_Int vertical
= (dim
== TA_DIMENSION_VERT
);
2816 if (!TA_LATIN_HINTS_DO_STEM_ADJUST(hints
)
2817 || axis
->extra_light
)
2826 if ((vertical
&& !TA_LATIN_HINTS_DO_VERT_SNAP(hints
))
2827 || (!vertical
&& !TA_LATIN_HINTS_DO_HORZ_SNAP(hints
)))
2829 /* smooth hinting process: very lightly quantize the stem width */
2831 /* leave the widths of serifs alone */
2832 if ((stem_flags
& TA_EDGE_SERIF
)
2836 else if (base_flags
& TA_EDGE_ROUND
)
2844 if (axis
->width_count
> 0)
2849 /* compare to standard width */
2850 delta
= dist
- axis
->widths
[0].cur
;
2857 dist
= axis
->widths
[0].cur
;
2871 else if (delta
< 32)
2873 else if (delta
< 54)
2880 /* A stem's end position depends on two values: the start */
2881 /* position and the stem length. The former gets usually */
2882 /* rounded to the grid, while the latter gets rounded also if it */
2883 /* exceeds a certain length (see below in this function). This */
2884 /* `double rounding' can lead to a great difference to the */
2885 /* original, unhinted position; this normally doesn't matter for */
2886 /* large PPEM values, but for small sizes it can easily make */
2887 /* outlines collide. For this reason, we adjust the stem length */
2888 /* by a small amount depending on the PPEM value in case the */
2889 /* former and latter rounding both point into the same */
2895 if (((width
> 0) && (base_delta
> 0))
2896 || ((width
< 0) && (base_delta
< 0)))
2898 FT_UInt ppem
= metrics
->root
.scaler
.face
->size
->metrics
.x_ppem
;
2902 bdelta
= base_delta
;
2904 bdelta
= (base_delta
* (FT_Pos
)(30 - ppem
)) / 20;
2910 dist
= (dist
- bdelta
+ 32) & ~63;
2916 /* strong hinting process: snap the stem width to integer pixels */
2918 FT_Pos org_dist
= dist
;
2921 dist
= ta_latin_snap_width(axis
->widths
, axis
->width_count
, dist
);
2925 /* in the case of vertical hinting, */
2926 /* always round the stem heights to integer pixels */
2929 dist
= (dist
+ 16) & ~63;
2935 if (TA_LATIN_HINTS_DO_MONO(hints
))
2937 /* monochrome horizontal hinting: */
2938 /* snap widths to integer pixels with a different threshold */
2943 dist
= (dist
+ 32) & ~63;
2947 /* for horizontal anti-aliased hinting, we adopt a more subtle */
2948 /* approach: we strengthen small stems, round stems whose size */
2949 /* is between 1 and 2 pixels to an integer, otherwise nothing */
2952 dist
= (dist
+ 64) >> 1;
2954 else if (dist
< 128)
2956 /* we only round to an integer width if the corresponding */
2957 /* distortion is less than 1/4 pixel -- otherwise, this */
2958 /* makes everything worse since the diagonals, which are */
2959 /* not hinted, appear a lot bolder or thinner than the */
2960 /* vertical stems */
2965 dist
= (dist
+ 22) & ~63;
2966 delta
= dist
- org_dist
;
2974 dist
= (dist
+ 64) >> 1;
2978 /* round otherwise to prevent color fringes in LCD mode */
2979 dist
= (dist
+ 32) & ~63;
2992 /* align one stem edge relative to the previous stem edge */
2995 ta_latin_align_linked_edge(TA_GlyphHints hints
,
3000 FT_Pos dist
, base_delta
;
3001 FT_Pos fitted_width
;
3004 dist
= stem_edge
->opos
- base_edge
->opos
;
3005 base_delta
= base_edge
->pos
- base_edge
->opos
;
3008 fitted_width
= ta_latin_compute_stem_width(hints
, dim
,
3013 stem_edge
->pos
= base_edge
->pos
+ fitted_width
;
3015 TA_LOG((" LINK: edge %d (opos=%.2f) linked to %.2f,"
3016 " dist was %.2f, now %.2f\n",
3017 stem_edge
- hints
->axis
[dim
].edges
, stem_edge
->opos
/ 64.0,
3018 stem_edge
->pos
/ 64.0, dist
/ 64.0, fitted_width
/ 64.0));
3020 if (hints
->recorder
)
3021 hints
->recorder(ta_link
, hints
, dim
,
3022 base_edge
, stem_edge
, NULL
, NULL
, NULL
);
3026 /* shift the coordinates of the `serif' edge by the same amount */
3027 /* as the corresponding `base' edge has been moved already */
3030 ta_latin_align_serif_edge(TA_GlyphHints hints
,
3036 serif
->pos
= base
->pos
+ (serif
->opos
- base
->opos
);
3040 /* the main grid-fitting routine */
3043 ta_latin_hint_edges(TA_GlyphHints hints
,
3046 TA_AxisHints axis
= &hints
->axis
[dim
];
3048 TA_Edge edges
= axis
->edges
;
3049 TA_Edge edge_limit
= edges
+ axis
->num_edges
;
3053 TA_Edge anchor
= NULL
;
3054 FT_Int has_serifs
= 0;
3056 TA_StyleClass style_class
= hints
->metrics
->style_class
;
3057 TA_ScriptClass script_class
= ta_script_classes
[style_class
->script
];
3059 FT_Bool top_to_bottom_hinting
= 0;
3062 FT_UInt num_actions
= 0;
3065 TA_LOG(("latin %s edge hinting (style `%s')\n",
3066 dim
== TA_DIMENSION_VERT
? "horizontal" : "vertical",
3067 ta_style_names
[hints
->metrics
->style_class
->style
]));
3069 if (dim
== TA_DIMENSION_VERT
)
3070 top_to_bottom_hinting
= script_class
->top_to_bottom_hinting
;
3072 /* we begin by aligning all stems relative to the blue zone if needed -- */
3073 /* that's only for horizontal edges */
3075 if (dim
== TA_DIMENSION_VERT
3076 && TA_HINTS_DO_BLUES(hints
))
3078 for (edge
= edges
; edge
< edge_limit
; edge
++)
3081 TA_Edge edge1
, edge2
; /* these edges form the stem to check */
3084 if (edge
->flags
& TA_EDGE_DONE
)
3091 * If a stem contains both a neutral and a non-neutral blue zone,
3092 * skip the neutral one. Otherwise, outlines with different
3093 * directions might be incorrectly aligned at the same vertical
3096 * If we have two neutral blue zones, skip one of them.
3098 if (edge
->blue_edge
&& edge2
&& edge2
->blue_edge
)
3100 FT_Byte neutral
= edge
->flags
& TA_EDGE_NEUTRAL
;
3101 FT_Byte neutral2
= edge2
->flags
& TA_EDGE_NEUTRAL
;
3106 edge2
->blue_edge
= NULL
;
3107 edge2
->flags
&= ~TA_EDGE_NEUTRAL
;
3111 edge
->blue_edge
= NULL
;
3112 edge
->flags
&= ~TA_EDGE_NEUTRAL
;
3116 blue
= edge
->blue_edge
;
3120 /* flip edges if the other edge is aligned to a blue zone */
3121 else if (edge2
&& edge2
->blue_edge
)
3123 blue
= edge2
->blue_edge
;
3133 TA_LOG((" BLUE_ANCHOR: edge %d (opos=%.2f) snapped to %.2f,"
3134 " was %.2f (anchor=edge %d)\n",
3135 edge1
- edges
, edge1
->opos
/ 64.0, blue
->fit
/ 64.0,
3136 edge1
->pos
/ 64.0, edge
- edges
));
3138 TA_LOG((" BLUE: edge %d (opos=%.2f) snapped to %.2f, was %.2f\n",
3139 edge1
- edges
, edge1
->opos
/ 64.0, blue
->fit
/ 64.0,
3140 edge1
->pos
/ 64.0));
3145 edge1
->pos
= blue
->fit
;
3146 edge1
->flags
|= TA_EDGE_DONE
;
3148 if (hints
->recorder
)
3151 hints
->recorder(ta_blue_anchor
, hints
, dim
,
3152 edge1
, edge
, NULL
, NULL
, NULL
);
3154 hints
->recorder(ta_blue
, hints
, dim
,
3155 edge1
, NULL
, NULL
, NULL
, NULL
);
3158 if (edge2
&& !edge2
->blue_edge
)
3160 ta_latin_align_linked_edge(hints
, dim
, edge1
, edge2
);
3161 edge2
->flags
|= TA_EDGE_DONE
;
3173 /* now we align all other stem edges, */
3174 /* trying to maintain the relative order of stems in the glyph */
3175 for (edge
= edges
; edge
< edge_limit
; edge
++)
3180 if (edge
->flags
& TA_EDGE_DONE
)
3183 /* skip all non-stem edges */
3191 /* now align the stem */
3193 /* this should not happen, but it's better to be safe */
3194 if (edge2
->blue_edge
)
3196 TA_LOG((" ASSERTION FAILED for edge %d\n", edge2
- edges
));
3198 ta_latin_align_linked_edge(hints
, dim
, edge2
, edge
);
3199 edge
->flags
|= TA_EDGE_DONE
;
3209 /* if we reach this if clause, no stem has been aligned yet */
3211 FT_Pos org_len
, org_center
, cur_len
;
3212 FT_Pos cur_pos1
, error1
, error2
, u_off
, d_off
;
3215 org_len
= edge2
->opos
- edge
->opos
;
3216 cur_len
= ta_latin_compute_stem_width(hints
, dim
,
3218 edge
->flags
, edge2
->flags
);
3220 /* some voodoo to specially round edges for small stem widths; */
3221 /* the idea is to align the center of a stem, */
3222 /* then shifting the stem edges to suitable positions */
3231 /* 1px < width < 1.5px */
3238 org_center
= edge
->opos
+ (org_len
>> 1);
3239 cur_pos1
= TA_PIX_ROUND(org_center
);
3241 error1
= org_center
- (cur_pos1
- u_off
);
3245 error2
= org_center
- (cur_pos1
+ d_off
);
3249 if (error1
< error2
)
3254 edge
->pos
= cur_pos1
- cur_len
/ 2;
3255 edge2
->pos
= edge
->pos
+ cur_len
;
3258 edge
->pos
= TA_PIX_ROUND(edge
->opos
);
3261 edge
->flags
|= TA_EDGE_DONE
;
3263 TA_LOG((" ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
3264 " snapped to %.2f and %.2f\n",
3265 edge
- edges
, edge
->opos
/ 64.0,
3266 edge2
- edges
, edge2
->opos
/ 64.0,
3267 edge
->pos
/ 64.0, edge2
->pos
/ 64.0));
3269 if (hints
->recorder
)
3270 hints
->recorder(ta_anchor
, hints
, dim
,
3271 edge
, edge2
, NULL
, NULL
, NULL
);
3273 ta_latin_align_linked_edge(hints
, dim
, edge
, edge2
);
3281 FT_Pos org_pos
, org_len
, org_center
, cur_len
;
3282 FT_Pos cur_pos1
, cur_pos2
, delta1
, delta2
;
3285 org_pos
= anchor
->pos
+ (edge
->opos
- anchor
->opos
);
3286 org_len
= edge2
->opos
- edge
->opos
;
3287 org_center
= org_pos
+ (org_len
>> 1);
3289 cur_len
= ta_latin_compute_stem_width(hints
, dim
,
3291 edge
->flags
, edge2
->flags
);
3293 if (edge2
->flags
& TA_EDGE_DONE
)
3295 TA_LOG((" ADJUST: edge %d (pos=%.2f) moved to %.2f\n",
3296 edge
- edges
, edge
->pos
/ 64.0,
3297 (edge2
->pos
- cur_len
) / 64.0));
3299 edge
->pos
= edge2
->pos
- cur_len
;
3301 if (hints
->recorder
)
3303 TA_Edge bound
= NULL
;
3309 hints
->recorder(ta_adjust
, hints
, dim
,
3310 edge
, edge2
, NULL
, bound
, NULL
);
3314 else if (cur_len
< 96)
3316 FT_Pos u_off
, d_off
;
3319 cur_pos1
= TA_PIX_ROUND(org_center
);
3332 delta1
= org_center
- (cur_pos1
- u_off
);
3336 delta2
= org_center
- (cur_pos1
+ d_off
);
3340 if (delta1
< delta2
)
3345 edge
->pos
= cur_pos1
- cur_len
/ 2;
3346 edge2
->pos
= cur_pos1
+ cur_len
/ 2;
3348 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
3349 " snapped to %.2f and %.2f\n",
3350 edge
- edges
, edge
->opos
/ 64.0,
3351 edge2
- edges
, edge2
->opos
/ 64.0,
3352 edge
->pos
/ 64.0, edge2
->pos
/ 64.0));
3354 if (hints
->recorder
)
3356 TA_Edge bound
= NULL
;
3362 hints
->recorder(ta_stem
, hints
, dim
,
3363 edge
, edge2
, NULL
, bound
, NULL
);
3369 org_pos
= anchor
->pos
+ (edge
->opos
- anchor
->opos
);
3370 org_len
= edge2
->opos
- edge
->opos
;
3371 org_center
= org_pos
+ (org_len
>> 1);
3373 cur_len
= ta_latin_compute_stem_width(hints
, dim
,
3375 edge
->flags
, edge2
->flags
);
3377 cur_pos1
= TA_PIX_ROUND(org_pos
);
3378 delta1
= cur_pos1
+ (cur_len
>> 1) - org_center
;
3382 cur_pos2
= TA_PIX_ROUND(org_pos
+ org_len
) - cur_len
;
3383 delta2
= cur_pos2
+ (cur_len
>> 1) - org_center
;
3387 edge
->pos
= (delta1
< delta2
) ? cur_pos1
: cur_pos2
;
3388 edge2
->pos
= edge
->pos
+ cur_len
;
3390 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
3391 " snapped to %.2f and %.2f\n",
3392 edge
- edges
, edge
->opos
/ 64.0,
3393 edge2
- edges
, edge2
->opos
/ 64.0,
3394 edge
->pos
/ 64.0, edge2
->pos
/ 64.0));
3396 if (hints
->recorder
)
3398 TA_Edge bound
= NULL
;
3404 hints
->recorder(ta_stem
, hints
, dim
,
3405 edge
, edge2
, NULL
, bound
, NULL
);
3413 edge
->flags
|= TA_EDGE_DONE
;
3414 edge2
->flags
|= TA_EDGE_DONE
;
3417 && (top_to_bottom_hinting
? (edge
->pos
> edge
[-1].pos
)
3418 : (edge
->pos
< edge
[-1].pos
)))
3420 /* don't move if stem would (almost) disappear otherwise; */
3421 /* the ad-hoc value 16 corresponds to 1/4px */
3423 && TA_ABS(edge
->link
->pos
- edge
[-1].pos
) > 16)
3426 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
3427 edge
- edges
, edge
->pos
/ 64.0, edge
[-1].pos
/ 64.0));
3432 edge
->pos
= edge
[-1].pos
;
3434 if (hints
->recorder
)
3435 hints
->recorder(ta_bound
, hints
, dim
,
3436 edge
, &edge
[-1], NULL
, NULL
, NULL
);
3442 /* make sure that lowercase m's maintain their symmetry */
3444 /* In general, lowercase m's have six vertical edges if they are sans */
3445 /* serif, or twelve if they are with serifs. This implementation is */
3446 /* based on that assumption, and seems to work very well with most */
3447 /* faces. However, if for a certain face this assumption is not */
3448 /* true, the m is just rendered like before. In addition, any stem */
3449 /* correction will only be applied to symmetrical glyphs (even if the */
3450 /* glyph is not an m), so the potential for unwanted distortion is */
3451 /* relatively low. */
3453 /* we don't handle horizontal edges since we can't easily assure that */
3454 /* the third (lowest) stem aligns with the base line; it might end up */
3455 /* one pixel higher or lower */
3457 n_edges
= edge_limit
- edges
;
3458 if (dim
== TA_DIMENSION_HORZ
3459 && (n_edges
== 6 || n_edges
== 12))
3461 TA_Edge edge1
, edge2
, edge3
;
3462 FT_Pos dist1
, dist2
, span
, delta
;
3478 dist1
= edge2
->opos
- edge1
->opos
;
3479 dist2
= edge3
->opos
- edge2
->opos
;
3481 span
= dist1
- dist2
;
3487 delta
= edge3
->pos
- (2 * edge2
->pos
- edge1
->pos
);
3488 edge3
->pos
-= delta
;
3490 edge3
->link
->pos
-= delta
;
3492 /* move the serifs along with the stem */
3495 (edges
+ 8)->pos
-= delta
;
3496 (edges
+ 11)->pos
-= delta
;
3499 edge3
->flags
|= TA_EDGE_DONE
;
3501 edge3
->link
->flags
|= TA_EDGE_DONE
;
3505 if (has_serifs
|| !anchor
)
3507 /* now hint the remaining edges (serifs and single) */
3508 /* in order to complete our processing */
3509 for (edge
= edges
; edge
< edge_limit
; edge
++)
3511 TA_Edge lower_bound
= NULL
;
3512 TA_Edge upper_bound
= NULL
;
3517 if (edge
->flags
& TA_EDGE_DONE
)
3524 delta
= edge
->serif
->opos
- edge
->opos
;
3530 lower_bound
= &edge
[-1];
3532 if (edge
+ 1 < edge_limit
3533 && edge
[1].flags
& TA_EDGE_DONE
)
3534 upper_bound
= &edge
[1];
3537 if (delta
< 64 + 16)
3539 ta_latin_align_serif_edge(hints
, edge
->serif
, edge
);
3541 TA_LOG((" SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
3542 " aligned to %.2f\n",
3543 edge
- edges
, edge
->opos
/ 64.0,
3544 edge
->serif
- edges
, edge
->serif
->opos
/ 64.0,
3547 if (hints
->recorder
)
3548 hints
->recorder(ta_serif
, hints
, dim
,
3549 edge
, NULL
, NULL
, lower_bound
, upper_bound
);
3553 edge
->pos
= TA_PIX_ROUND(edge
->opos
);
3556 TA_LOG((" SERIF_ANCHOR: edge %d (opos=%.2f) snapped to %.2f\n",
3557 edge
- edges
, edge
->opos
/ 64.0, edge
->pos
/ 64.0));
3559 if (hints
->recorder
)
3560 hints
->recorder(ta_serif_anchor
, hints
, dim
,
3561 edge
, NULL
, NULL
, lower_bound
, upper_bound
);
3565 TA_Edge before
, after
;
3568 for (before
= edge
- 1; before
>= edges
; before
--)
3569 if (before
->flags
& TA_EDGE_DONE
)
3572 for (after
= edge
+ 1; after
< edge_limit
; after
++)
3573 if (after
->flags
& TA_EDGE_DONE
)
3576 if (before
>= edges
&& before
< edge
3577 && after
< edge_limit
&& after
> edge
)
3579 if (after
->opos
== before
->opos
)
3580 edge
->pos
= before
->pos
;
3582 edge
->pos
= before
->pos
+ FT_MulDiv(edge
->opos
- before
->opos
,
3583 after
->pos
- before
->pos
,
3584 after
->opos
- before
->opos
);
3586 TA_LOG((" SERIF_LINK1: edge %d (opos=%.2f) snapped to %.2f"
3587 " from %d (opos=%.2f)\n",
3588 edge
- edges
, edge
->opos
/ 64.0,
3590 before
- edges
, before
->opos
/ 64.0));
3592 if (hints
->recorder
)
3593 hints
->recorder(ta_serif_link1
, hints
, dim
,
3594 edge
, before
, after
, lower_bound
, upper_bound
);
3598 edge
->pos
= anchor
->pos
+ ((edge
->opos
- anchor
->opos
+ 16) & ~31);
3599 TA_LOG((" SERIF_LINK2: edge %d (opos=%.2f) snapped to %.2f\n",
3600 edge
- edges
, edge
->opos
/ 64.0, edge
->pos
/ 64.0));
3602 if (hints
->recorder
)
3603 hints
->recorder(ta_serif_link2
, hints
, dim
,
3604 edge
, NULL
, NULL
, lower_bound
, upper_bound
);
3611 edge
->flags
|= TA_EDGE_DONE
;
3614 && (top_to_bottom_hinting
? (edge
->pos
> edge
[-1].pos
)
3615 : (edge
->pos
< edge
[-1].pos
)))
3617 /* don't move if stem would (almost) disappear otherwise; */
3618 /* the ad-hoc value 16 corresponds to 1/4px */
3620 && TA_ABS(edge
->link
->pos
- edge
[-1].pos
) > 16)
3623 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
3624 edge
- edges
, edge
->pos
/ 64.0, edge
[-1].pos
/ 64.0));
3628 edge
->pos
= edge
[-1].pos
;
3630 if (hints
->recorder
)
3631 hints
->recorder(ta_bound
, hints
, dim
,
3632 edge
, &edge
[-1], NULL
, NULL
, NULL
);
3636 if (edge
+ 1 < edge_limit
3637 && edge
[1].flags
& TA_EDGE_DONE
3638 && (top_to_bottom_hinting
? (edge
->pos
< edge
[1].pos
)
3639 : (edge
->pos
> edge
[1].pos
)))
3642 /* don't move if stem would (almost) disappear otherwise; */
3643 /* the ad-hoc value 16 corresponds to 1/4px */
3645 && TA_ABS(edge
->link
->pos
- edge
[-1].pos
) > 16)
3648 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
3649 edge
- edges
, edge
->pos
/ 64.0, edge
[1].pos
/ 64.0));
3654 edge
->pos
= edge
[1].pos
;
3656 if (hints
->recorder
)
3657 hints
->recorder(ta_bound
, hints
, dim
,
3658 edge
, &edge
[1], NULL
, NULL
, NULL
);
3666 TA_LOG((" (none)\n"));
3672 /* apply the complete hinting algorithm to a latin glyph */
3675 ta_latin_hints_apply(FT_UInt glyph_index
,
3676 TA_GlyphHints hints
,
3677 FT_Outline
* outline
,
3678 TA_LatinMetrics metrics
)
3686 error
= ta_glyph_hints_reload(hints
, outline
);
3690 /* analyze glyph outline */
3691 if (TA_HINTS_DO_HORIZONTAL(hints
))
3693 axis
= &metrics
->axis
[TA_DIMENSION_HORZ
];
3694 error
= ta_latin_hints_detect_features(hints
,
3702 if (TA_HINTS_DO_VERTICAL(hints
))
3704 axis
= &metrics
->axis
[TA_DIMENSION_VERT
];
3705 error
= ta_latin_hints_detect_features(hints
,
3712 /* apply blue zones to base characters only */
3713 if (!(metrics
->root
.globals
->glyph_styles
[glyph_index
] & TA_NONBASE
))
3714 ta_latin_hints_compute_blue_edges(hints
, metrics
);
3717 /* grid-fit the outline */
3718 for (dim
= 0; dim
< TA_DIMENSION_MAX
; dim
++)
3720 #ifdef TA_CONFIG_OPTION_USE_WARPER
3721 if (dim
== TA_DIMENSION_HORZ
3722 && metrics
->root
.scaler
.render_mode
== FT_RENDER_MODE_NORMAL
3723 && TA_HINTS_DO_WARP(hints
))
3725 TA_WarperRec warper
;
3730 ta_warper_compute(&warper
, hints
, (TA_Dimension
)dim
, &scale
, &delta
);
3731 ta_glyph_hints_scale_dim(hints
, (TA_Dimension
)dim
, scale
, delta
);
3735 #endif /* TA_CONFIG_OPTION_USE_WARPER */
3737 if ((dim
== TA_DIMENSION_HORZ
&& TA_HINTS_DO_HORIZONTAL(hints
))
3738 || (dim
== TA_DIMENSION_VERT
&& TA_HINTS_DO_VERTICAL(hints
)))
3740 ta_latin_hint_edges(hints
, (TA_Dimension
)dim
);
3741 ta_glyph_hints_align_edge_points(hints
, (TA_Dimension
)dim
);
3742 ta_glyph_hints_align_strong_points(hints
, (TA_Dimension
)dim
);
3743 ta_glyph_hints_align_weak_points(hints
, (TA_Dimension
)dim
);
3747 ta_glyph_hints_save(hints
, outline
);
3754 const TA_WritingSystemClassRec ta_latin_writing_system_class
=
3756 TA_WRITING_SYSTEM_LATIN
,
3758 sizeof (TA_LatinMetricsRec
),
3760 (TA_WritingSystem_InitMetricsFunc
)ta_latin_metrics_init
, /* style_metrics_init */
3761 (TA_WritingSystem_ScaleMetricsFunc
)ta_latin_metrics_scale
, /* style_metrics_scale */
3762 (TA_WritingSystem_DoneMetricsFunc
)NULL
, /* style_metrics_done */
3764 (TA_WritingSystem_InitHintsFunc
)ta_latin_hints_init
, /* style_hints_init */
3765 (TA_WritingSystem_ApplyHintsFunc
)ta_latin_hints_apply
/* style_hints_apply */
3768 /* end of talatin.c */