4 * Copyright (C) 2011-2015 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;
70 TA_LatinMetricsRec dummy
[1];
71 TA_Scaler scaler
= &dummy
->root
.scaler
;
73 TA_StyleClass style_class
= metrics
->root
.style_class
;
74 TA_ScriptClass script_class
= ta_script_classes
[style_class
->script
];
76 FT_UInt32 standard_char
;
83 * We check more than a single standard character to catch features
84 * like `c2sc' (small caps from caps) that don't contain lowercase
85 * letters by definition, or other features that mainly operate on
88 standard_char
= script_class
->standard_char1
;
89 ta_get_char_index(&metrics
->root
,
95 if (script_class
->standard_char2
)
97 standard_char
= script_class
->standard_char2
;
98 ta_get_char_index(&metrics
->root
,
104 if (script_class
->standard_char3
)
106 standard_char
= script_class
->standard_char3
;
107 ta_get_char_index(&metrics
->root
,
122 TA_LOG_GLOBAL(("standard character: U+%04lX (glyph index %d)\n",
123 standard_char
, glyph_index
));
125 error
= FT_Load_Glyph(face
, glyph_index
, FT_LOAD_NO_SCALE
);
126 if (error
|| face
->glyph
->outline
.n_points
<= 0)
129 memset(dummy
, 0, sizeof (TA_LatinMetricsRec
));
131 dummy
->units_per_em
= metrics
->units_per_em
;
133 scaler
->x_scale
= 0x10000L
;
134 scaler
->y_scale
= 0x10000L
;
139 scaler
->render_mode
= FT_RENDER_MODE_NORMAL
;
142 ta_glyph_hints_rescale(hints
, (TA_StyleMetrics
)dummy
);
144 error
= ta_glyph_hints_reload(hints
, &face
->glyph
->outline
);
148 for (dim
= 0; dim
< TA_DIMENSION_MAX
; dim
++)
150 TA_LatinAxis axis
= &metrics
->axis
[dim
];
151 TA_AxisHints axhints
= &hints
->axis
[dim
];
153 TA_Segment seg
, limit
, link
;
154 FT_UInt num_widths
= 0;
157 error
= ta_latin_hints_compute_segments(hints
, (TA_Dimension
)dim
);
162 * We assume that the glyphs selected for the stem width
163 * computation are `featureless' enough so that the linking
164 * algorithm works fine without adjustments of its scoring
167 ta_latin_hints_link_segments(hints
, 0, NULL
, (TA_Dimension
)dim
);
169 seg
= axhints
->segments
;
170 limit
= seg
+ axhints
->num_segments
;
172 for (; seg
< limit
; seg
++)
176 /* we only consider stem segments there! */
184 dist
= seg
->pos
- link
->pos
;
188 if (num_widths
< TA_LATIN_MAX_WIDTHS
)
189 axis
->widths
[num_widths
++].org
= dist
;
193 /* this also replaces multiple almost identical stem widths */
194 /* with a single one (the value 100 is heuristic) */
195 ta_sort_and_quantize_widths(&num_widths
, axis
->widths
,
196 dummy
->units_per_em
/ 100);
197 axis
->width_count
= num_widths
;
201 for (dim
= 0; dim
< TA_DIMENSION_MAX
; dim
++)
203 FONT
* font
= metrics
->root
.globals
->font
;
204 TA_LatinAxis axis
= &metrics
->axis
[dim
];
208 if (!axis
->width_count
)
210 /* if we have no standard characters, */
211 /* use `fallback-stem-width', if available, */
212 /* or a default width (value 50 is heuristic) */
213 stdw
= (dim
== TA_DIMENSION_VERT
&& font
->fallback_stem_width
)
214 ? (FT_Pos
)font
->fallback_stem_width
215 : TA_LATIN_CONSTANT(metrics
, 50);
217 /* set one width value if we do hinting */
218 if (style_class
->style
!= TA_STYLE_NONE_DFLT
)
221 axis
->widths
[0].org
= stdw
;
225 stdw
= axis
->widths
[0].org
;
227 /* let's try 20% of the smallest width */
228 axis
->edge_distance_threshold
= stdw
/ 5;
229 axis
->standard_width
= stdw
;
230 axis
->extra_light
= 0;
237 TA_LOG_GLOBAL(("%s widths:\n",
238 dim
== TA_DIMENSION_VERT
? "horizontal"
241 TA_LOG_GLOBAL((" %d (standard)", axis
->standard_width
));
242 for (i
= 1; i
< axis
->width_count
; i
++)
243 TA_LOG_GLOBAL((" %d", axis
->widths
[i
].org
));
245 TA_LOG_GLOBAL(("\n"));
251 TA_LOG_GLOBAL(("\n"));
253 ta_glyph_hints_done(hints
);
257 /* find all blue zones; flat segments give the reference points, */
258 /* round segments the overshoot positions */
261 ta_latin_metrics_init_blues(TA_LatinMetrics metrics
,
264 FT_Pos flats
[TA_BLUE_STRING_MAX_LEN
];
265 FT_Pos rounds
[TA_BLUE_STRING_MAX_LEN
];
271 TA_LatinAxis axis
= &metrics
->axis
[TA_DIMENSION_VERT
];
274 TA_StyleClass sc
= metrics
->root
.style_class
;
276 TA_Blue_Stringset bss
= sc
->blue_stringset
;
277 const TA_Blue_StringRec
* bs
= &ta_blue_stringsets
[bss
];
279 FT_Pos flat_threshold
= FLAT_THRESHOLD(metrics
->units_per_em
);
282 /* we walk over the blue character strings as specified in the */
283 /* style's entry in the `ta_blue_stringset' array */
285 TA_LOG_GLOBAL(("latin blue zones computation\n"
286 "============================\n"
289 for (; bs
->string
!= TA_BLUE_STRING_MAX
; bs
++)
291 const char* p
= &ta_blue_strings
[bs
->string
];
298 FT_Bool have_flag
= 0;
301 TA_LOG_GLOBAL(("blue zone %d", axis
->blue_count
));
305 TA_LOG_GLOBAL((" ("));
307 if (TA_LATIN_IS_TOP_BLUE(bs
))
309 TA_LOG_GLOBAL(("top"));
313 if (TA_LATIN_IS_NEUTRAL_BLUE(bs
))
316 TA_LOG_GLOBAL((", "));
317 TA_LOG_GLOBAL(("neutral"));
321 if (TA_LATIN_IS_X_HEIGHT_BLUE(bs
))
324 TA_LOG_GLOBAL((", "));
325 TA_LOG_GLOBAL(("small top"));
329 if (TA_LATIN_IS_LONG_BLUE(bs
))
332 TA_LOG_GLOBAL((", "));
333 TA_LOG_GLOBAL(("long"));
336 TA_LOG_GLOBAL((")"));
339 TA_LOG_GLOBAL((":\n"));
341 #endif /* TA_DEBUG */
349 FT_ULong glyph_index
;
351 FT_Pos best_y
; /* same as points.y */
352 FT_Int best_point
, best_contour_first
, best_contour_last
;
357 GET_UTF8_CHAR(ch
, p
);
359 /* load the character in the face -- skip unknown or empty ones */
360 ta_get_char_index(&metrics
->root
, ch
, &glyph_index
, &y_offset
);
361 if (glyph_index
== 0)
363 TA_LOG_GLOBAL((" U+%04lX unavailable\n", ch
));
367 error
= FT_Load_Glyph(face
, glyph_index
, FT_LOAD_NO_SCALE
);
368 outline
= face
->glyph
->outline
;
369 /* reject glyphs that don't produce any rendering */
370 if (error
|| outline
.n_points
<= 2)
372 TA_LOG_GLOBAL((" U+%04lX contains no (usable) outlines\n", ch
));
376 /* now compute min or max point indices and coordinates */
377 points
= outline
.points
;
379 best_y
= 0; /* make compiler happy */
380 best_contour_first
= 0; /* ditto */
381 best_contour_last
= 0; /* ditto */
389 for (nn
= 0; nn
< outline
.n_contours
; first
= last
+ 1, nn
++)
391 FT_Int old_best_point
= best_point
;
395 last
= outline
.contours
[nn
];
397 /* avoid single-point contours since they are never rasterized; */
398 /* in some fonts, they correspond to mark attachment points */
399 /* that are way outside of the glyph's real outline */
403 if (TA_LATIN_IS_TOP_BLUE(bs
))
405 for (pp
= first
; pp
<= last
; pp
++)
407 || points
[pp
].y
> best_y
)
410 best_y
= points
[pp
].y
;
415 for (pp
= first
; pp
<= last
; pp
++)
417 || points
[pp
].y
< best_y
)
420 best_y
= points
[pp
].y
;
424 if (best_point
!= old_best_point
)
426 best_contour_first
= first
;
427 best_contour_last
= last
;
432 /* now check whether the point belongs to a straight or round */
433 /* segment; we first need to find in which contour the extremum */
434 /* lies, then inspect its previous and next points */
437 FT_Pos best_x
= points
[best_point
].x
;
439 FT_Int best_segment_first
, best_segment_last
;
440 FT_Int best_on_point_first
, best_on_point_last
;
444 best_segment_first
= best_point
;
445 best_segment_last
= best_point
;
447 if (FT_CURVE_TAG(outline
.tags
[best_point
]) == FT_CURVE_TAG_ON
)
449 best_on_point_first
= best_point
;
450 best_on_point_last
= best_point
;
454 best_on_point_first
= -1;
455 best_on_point_last
= -1;
458 /* look for the previous and next points on the contour */
459 /* that are not on the same Y coordinate, then threshold */
460 /* the `closeness'... */
466 if (prev
> best_contour_first
)
469 prev
= best_contour_last
;
471 dist
= TA_ABS(points
[prev
].y
- best_y
);
472 /* accept a small distance or a small angle (both values are */
473 /* heuristic; value 20 corresponds to approx. 2.9 degrees) */
475 if (TA_ABS(points
[prev
].x
- best_x
) <= 20 * dist
)
478 best_segment_first
= prev
;
480 if (FT_CURVE_TAG(outline
.tags
[prev
]) == FT_CURVE_TAG_ON
)
482 best_on_point_first
= prev
;
483 if (best_on_point_last
< 0)
484 best_on_point_last
= prev
;
487 } while (prev
!= best_point
);
491 if (next
< best_contour_last
)
494 next
= best_contour_first
;
496 dist
= TA_ABS(points
[next
].y
- best_y
);
498 if (TA_ABS(points
[next
].x
- best_x
) <= 20 * dist
)
501 best_segment_last
= next
;
503 if (FT_CURVE_TAG(outline
.tags
[next
]) == FT_CURVE_TAG_ON
)
505 best_on_point_last
= next
;
506 if (best_on_point_first
< 0)
507 best_on_point_first
= next
;
510 } while (next
!= best_point
);
512 if (TA_LATIN_IS_LONG_BLUE(bs
))
514 /* If this flag is set, we have an additional constraint to */
515 /* get the blue zone distance: Find a segment of the topmost */
516 /* (or bottommost) contour that is longer than a heuristic */
517 /* threshold. This ensures that small bumps in the outline */
518 /* are ignored (for example, the `vertical serifs' found in */
519 /* many Hebrew glyph designs). */
521 /* If this segment is long enough, we are done. Otherwise, */
522 /* search the segment next to the extremum that is long */
523 /* enough, has the same direction, and a not too large */
524 /* vertical distance from the extremum. Note that the */
525 /* algorithm doesn't check whether the found segment is */
526 /* actually the one (vertically) nearest to the extremum. */
528 /* heuristic threshold value */
529 FT_Pos length_threshold
= metrics
->units_per_em
/ 25;
532 dist
= TA_ABS(points
[best_segment_last
].x
-
533 points
[best_segment_first
].x
);
535 if (dist
< length_threshold
536 && best_segment_last
- best_segment_first
+ 2 <=
537 best_contour_last
- best_contour_first
)
539 /* heuristic threshold value */
540 FT_Pos height_threshold
= metrics
->units_per_em
/ 4;
546 /* we intentionally declare these two variables */
547 /* outside of the loop since various compilers emit */
548 /* incorrect warning messages otherwise, talking about */
549 /* `possibly uninitialized variables' */
550 FT_Int p_first
= 0; /* make compiler happy */
556 /* compute direction */
561 if (prev
> best_contour_first
)
564 prev
= best_contour_last
;
566 if (points
[prev
].x
!= best_x
)
568 } while (prev
!= best_point
);
570 /* skip glyph for the degenerate case */
571 if (prev
== best_point
)
574 left2right
= FT_BOOL(points
[prev
].x
< points
[best_point
].x
);
576 first
= best_segment_last
;
588 /* no hit; adjust first point */
591 /* also adjust first and last on point */
592 if (FT_CURVE_TAG(outline
.tags
[first
]) == FT_CURVE_TAG_ON
)
606 if (last
< best_contour_last
)
609 last
= best_contour_first
;
611 if (TA_ABS(best_y
- points
[first
].y
) > height_threshold
)
613 /* vertical distance too large */
618 /* same test as above */
619 dist
= TA_ABS(points
[last
].y
- points
[first
].y
);
621 if (TA_ABS(points
[last
].x
- points
[first
].x
) <= 20 * dist
)
627 if (FT_CURVE_TAG(outline
.tags
[last
]) == FT_CURVE_TAG_ON
)
634 l2r
= FT_BOOL(points
[first
].x
< points
[last
].x
);
635 d
= TA_ABS(points
[last
].x
- points
[first
].x
);
637 if (l2r
== left2right
638 && d
>= length_threshold
)
640 /* all constraints are met; update segment after finding */
644 if (last
< best_contour_last
)
647 last
= best_contour_first
;
649 d
= TA_ABS(points
[last
].y
- points
[first
].y
);
651 if (TA_ABS(points
[next
].x
- points
[first
].x
) <=
654 if (last
> best_contour_first
)
657 last
= best_contour_last
;
663 if (FT_CURVE_TAG(outline
.tags
[last
]) == FT_CURVE_TAG_ON
)
669 } while (last
!= best_segment_first
);
671 best_y
= points
[first
].y
;
673 best_segment_first
= first
;
674 best_segment_last
= last
;
676 best_on_point_first
= p_first
;
677 best_on_point_last
= p_last
;
681 } while (last
!= best_segment_first
);
686 * for computing blue zones, we add the y offset as returned
687 * by the currently used OpenType feature --
688 * for example, superscript glyphs might be identical
689 * to subscript glyphs with a vertical shift
693 TA_LOG_GLOBAL((" U+%04lX: best_y = %5ld", ch
, best_y
));
696 * now set the `round' flag depending on the segment's kind:
698 * - if the horizontal distance between the first and last
699 * `on' point is larger than a heuristic threshold
700 * we have a flat segment
701 * - if either the first or the last point of the segment is
702 * an `off' point, the segment is round, otherwise it is
705 if (best_on_point_first
>= 0
706 && best_on_point_last
>= 0
707 && (TA_ABS(points
[best_on_point_last
].x
708 - points
[best_on_point_first
].x
))
712 round
= FT_BOOL(FT_CURVE_TAG(outline
.tags
[best_segment_first
])
714 || FT_CURVE_TAG(outline
.tags
[best_segment_last
])
717 if (round
&& TA_LATIN_IS_NEUTRAL_BLUE(bs
))
719 /* only use flat segments for a neutral blue zone */
720 TA_LOG_GLOBAL((" (round, skipped)\n"));
724 TA_LOG_GLOBAL((" (%s)\n", round
? "round" : "flat"));
728 rounds
[num_rounds
++] = best_y
;
730 flats
[num_flats
++] = best_y
;
733 if (num_flats
== 0 && num_rounds
== 0)
735 /* we couldn't find a single glyph to compute this blue zone, */
736 /* we will simply ignore it then */
737 TA_LOG_GLOBAL((" empty\n"));
741 /* we have computed the contents of the `rounds' and `flats' tables, */
742 /* now determine the reference and overshoot position of the blue -- */
743 /* we simply take the median value after a simple sort */
744 ta_sort_pos(num_rounds
, rounds
);
745 ta_sort_pos(num_flats
, flats
);
747 blue
= &axis
->blues
[axis
->blue_count
];
748 blue_ref
= &blue
->ref
.org
;
749 blue_shoot
= &blue
->shoot
.org
;
756 *blue_shoot
= rounds
[num_rounds
/ 2];
758 else if (num_rounds
== 0)
761 *blue_shoot
= flats
[num_flats
/ 2];
765 *blue_ref
= flats
[num_flats
/ 2];
766 *blue_shoot
= rounds
[num_rounds
/ 2];
769 /* there are sometimes problems if the overshoot position of top */
770 /* zones is under its reference position, or the opposite for bottom */
771 /* zones; we must thus check everything there and correct the errors */
772 if (*blue_shoot
!= *blue_ref
)
774 FT_Pos ref
= *blue_ref
;
775 FT_Pos shoot
= *blue_shoot
;
776 FT_Bool over_ref
= FT_BOOL(shoot
> ref
);
779 if (TA_LATIN_IS_TOP_BLUE(bs
) ^ over_ref
)
782 *blue_shoot
= (shoot
+ ref
) / 2;
784 TA_LOG_GLOBAL((" [overshoot smaller than reference,"
785 " taking mean value]\n"));
790 if (TA_LATIN_IS_TOP_BLUE(bs
))
791 blue
->flags
|= TA_LATIN_BLUE_TOP
;
792 if (TA_LATIN_IS_NEUTRAL_BLUE(bs
))
793 blue
->flags
|= TA_LATIN_BLUE_NEUTRAL
;
795 /* the following flag is used later to adjust the y and x scales */
796 /* in order to optimize the pixel grid alignment */
797 /* of the top of small letters */
798 if (TA_LATIN_IS_X_HEIGHT_BLUE(bs
))
799 blue
->flags
|= TA_LATIN_BLUE_ADJUSTMENT
;
801 TA_LOG_GLOBAL((" -> reference = %ld\n"
802 " overshoot = %ld\n",
803 *blue_ref
, *blue_shoot
));
806 /* add two blue zones for usWinAscent and usWinDescent */
807 /* just in case the above algorithm has missed them -- */
808 /* Windows cuts off everything outside of those two values */
813 os2
= (TT_OS2
*)FT_Get_Sfnt_Table(face
, ft_sfnt_os2
);
817 blue
= &axis
->blues
[axis
->blue_count
];
818 blue
->flags
= TA_LATIN_BLUE_TOP
| TA_LATIN_BLUE_ACTIVE
;
820 blue
->shoot
.org
= os2
->usWinAscent
;
822 TA_LOG_GLOBAL(("artificial blue zone for usWinAscent:\n"
823 " -> reference = %ld\n"
824 " overshoot = %ld\n",
825 blue
->ref
.org
, blue
->shoot
.org
));
827 blue
= &axis
->blues
[axis
->blue_count
+ 1];
828 blue
->flags
= TA_LATIN_BLUE_ACTIVE
;
830 blue
->shoot
.org
= -os2
->usWinDescent
;
832 TA_LOG_GLOBAL(("artificial blue zone for usWinDescent:\n"
833 " -> reference = %ld\n"
834 " overshoot = %ld\n",
835 blue
->ref
.org
, blue
->shoot
.org
));
839 blue
= &axis
->blues
[axis
->blue_count
];
844 blue
= &axis
->blues
[axis
->blue_count
+ 1];
851 TA_LOG_GLOBAL(("\n"));
857 /* check whether all ASCII digits have the same advance width */
860 ta_latin_metrics_check_digits(TA_LatinMetrics metrics
,
864 FT_Bool started
= 0, same_width
= 1;
865 FT_Fixed advance
, old_advance
= 0;
868 /* digit `0' is 0x30 in all supported charmaps */
869 for (i
= 0x30; i
<= 0x39; i
++)
871 FT_ULong glyph_index
;
875 ta_get_char_index(&metrics
->root
, i
, &glyph_index
, &y_offset
);
876 if (glyph_index
== 0)
879 if (FT_Get_Advance(face
, glyph_index
,
882 | FT_LOAD_IGNORE_TRANSFORM
,
888 if (advance
!= old_advance
)
896 old_advance
= advance
;
901 metrics
->root
.digits_have_same_width
= same_width
;
905 /* initialize global metrics */
908 ta_latin_metrics_init(TA_LatinMetrics metrics
,
911 FT_CharMap oldmap
= face
->charmap
;
914 metrics
->units_per_em
= face
->units_per_EM
;
916 if (!FT_Select_Charmap(face
, FT_ENCODING_UNICODE
))
918 ta_latin_metrics_init_widths(metrics
, face
, 1);
919 ta_latin_metrics_init_blues(metrics
, face
);
920 ta_latin_metrics_check_digits(metrics
, face
);
924 /* we only have a symbol font encoding */
925 ta_latin_metrics_init_widths(metrics
, face
, 0);
928 FT_Set_Charmap(face
, oldmap
);
933 /* adjust scaling value, then scale and shift widths */
934 /* and blue zones (if applicable) for given dimension */
937 ta_latin_metrics_scale_dim(TA_LatinMetrics metrics
,
948 ppem
= metrics
->root
.scaler
.face
->size
->metrics
.x_ppem
;
950 if (dim
== TA_DIMENSION_HORZ
)
952 scale
= scaler
->x_scale
;
953 delta
= scaler
->x_delta
;
957 scale
= scaler
->y_scale
;
958 delta
= scaler
->y_delta
;
961 axis
= &metrics
->axis
[dim
];
963 if (axis
->org_scale
== scale
&& axis
->org_delta
== delta
)
966 axis
->org_scale
= scale
;
967 axis
->org_delta
= delta
;
969 /* correct Y scale to optimize the alignment of the top of */
970 /* small letters to the pixel grid */
971 /* (if we do x-height snapping for this ppem value) */
972 if (!number_set_is_element(
973 metrics
->root
.globals
->font
->x_height_snapping_exceptions
,
976 TA_LatinAxis Axis
= &metrics
->axis
[TA_DIMENSION_VERT
];
977 TA_LatinBlue blue
= NULL
;
980 for (nn
= 0; nn
< Axis
->blue_count
; nn
++)
982 if (Axis
->blues
[nn
].flags
& TA_LATIN_BLUE_ADJUSTMENT
)
984 blue
= &Axis
->blues
[nn
];
997 scaled
= FT_MulFix(blue
->shoot
.org
, scaler
->y_scale
);
998 limit
= metrics
->root
.globals
->increase_x_height
;
1001 /* if the `increase-x-height' property is active, */
1002 /* we round up much more often */
1005 && ppem
>= TA_PROP_INCREASE_X_HEIGHT_MIN
)
1008 fitted
= (scaled
+ threshold
) & ~63;
1010 if (scaled
!= fitted
)
1012 if (dim
== TA_DIMENSION_VERT
)
1014 scale
= FT_MulDiv(scale
, fitted
, scaled
);
1017 "ta_latin_metrics_scale_dim:"
1018 " x height alignment (style `%s'):\n"
1020 " vertical scaling changed from %.4f to %.4f (by %d%%)\n"
1022 ta_style_names
[metrics
->root
.style_class
->style
],
1023 axis
->org_scale
/ 65536.0,
1025 (fitted
- scaled
) * 100 / scaled
));
1031 axis
->scale
= scale
;
1032 axis
->delta
= delta
;
1034 if (dim
== TA_DIMENSION_HORZ
)
1036 metrics
->root
.scaler
.x_scale
= scale
;
1037 metrics
->root
.scaler
.x_delta
= delta
;
1041 metrics
->root
.scaler
.y_scale
= scale
;
1042 metrics
->root
.scaler
.y_delta
= delta
;
1045 TA_LOG_GLOBAL(("%s widths (style `%s')\n",
1046 dim
== TA_DIMENSION_HORZ
? "horizontal" : "vertical",
1047 ta_style_names
[metrics
->root
.style_class
->style
]));
1049 /* scale the widths */
1050 for (nn
= 0; nn
< axis
->width_count
; nn
++)
1052 TA_Width width
= axis
->widths
+ nn
;
1055 width
->cur
= FT_MulFix(width
->org
, scale
);
1056 width
->fit
= width
->cur
;
1058 TA_LOG_GLOBAL((" %d scaled to %.2f\n",
1060 width
->cur
/ 64.0));
1063 TA_LOG_GLOBAL(("\n"));
1065 /* an extra-light axis corresponds to a standard width that is */
1066 /* smaller than 5/8 pixels */
1068 (FT_Bool
)(FT_MulFix(axis
->standard_width
, scale
) < 32 + 8);
1071 if (axis
->extra_light
)
1072 TA_LOG_GLOBAL(("`%s' style is extra light (at current resolution)\n"
1074 ta_style_names
[metrics
->root
.style_class
->style
]));
1077 if (dim
== TA_DIMENSION_VERT
)
1080 if (axis
->blue_count
)
1081 TA_LOG_GLOBAL(("blue zones (style `%s')\n",
1082 ta_style_names
[metrics
->root
.style_class
->style
]));
1085 /* scale the blue zones */
1086 for (nn
= 0; nn
< axis
->blue_count
; nn
++)
1088 TA_LatinBlue blue
= &axis
->blues
[nn
];
1092 blue
->ref
.cur
= FT_MulFix(blue
->ref
.org
, scale
) + delta
;
1093 blue
->ref
.fit
= blue
->ref
.cur
;
1094 blue
->shoot
.cur
= FT_MulFix(blue
->shoot
.org
, scale
) + delta
;
1095 blue
->shoot
.fit
= blue
->shoot
.cur
;
1096 blue
->flags
&= ~TA_LATIN_BLUE_ACTIVE
;
1098 /* a blue zone is only active if it is less than 3/4 pixels tall */
1099 dist
= FT_MulFix(blue
->ref
.org
- blue
->shoot
.org
, scale
);
1100 if (dist
<= 48 && dist
>= -48)
1108 /* use discrete values for blue zone widths */
1111 /* generic, original code */
1112 delta1
= blue
->shoot
.org
- blue
->ref
.org
;
1117 delta2
= FT_MulFix(delta2
, scale
);
1121 else if (delta2
< 64)
1122 delta2
= 32 + (((delta2
- 32) + 16) & ~31);
1124 delta2
= TA_PIX_ROUND(delta2
);
1129 blue
->ref
.fit
= TA_PIX_ROUND(blue
->ref
.cur
);
1130 blue
->shoot
.fit
= blue
->ref
.fit
+ delta2
;
1132 /* simplified version due to abs(dist) <= 48 */
1139 else if (delta2
< 48)
1147 blue
->ref
.fit
= TA_PIX_ROUND(blue
->ref
.cur
);
1148 blue
->shoot
.fit
= blue
->ref
.fit
- delta2
;
1151 blue
->flags
|= TA_LATIN_BLUE_ACTIVE
;
1153 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f%s\n"
1154 " overshoot %d: %d scaled to %.2f%s\n",
1157 blue
->ref
.fit
/ 64.0,
1158 blue
->flags
& TA_LATIN_BLUE_ACTIVE
? ""
1162 blue
->shoot
.fit
/ 64.0,
1163 blue
->flags
& TA_LATIN_BLUE_ACTIVE
? ""
1168 /* the last two artificial blue zones are to be scaled */
1169 /* with uncorrected scaling values */
1171 TA_LatinAxis a
= &metrics
->axis
[TA_DIMENSION_VERT
];
1175 b
= &a
->blues
[a
->blue_count
];
1179 b
->shoot
.fit
= FT_MulFix(b
->ref
.org
, a
->org_scale
) + delta
;
1181 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f (artificial)\n"
1182 " overshoot %d: %d scaled to %.2f (artificial)\n",
1188 b
->shoot
.fit
/ 64.0));
1190 b
= &a
->blues
[a
->blue_count
+ 1];
1194 b
->shoot
.fit
= FT_MulFix(b
->ref
.org
, a
->org_scale
) + delta
;
1196 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f (artificial)\n"
1197 " overshoot %d: %d scaled to %.2f (artificial)\n",
1203 b
->shoot
.fit
/ 64.0));
1206 TA_LOG_GLOBAL(("\n"));
1211 /* scale global values in both directions */
1214 ta_latin_metrics_scale(TA_LatinMetrics metrics
,
1217 metrics
->root
.scaler
.render_mode
= scaler
->render_mode
;
1218 metrics
->root
.scaler
.face
= scaler
->face
;
1219 metrics
->root
.scaler
.flags
= scaler
->flags
;
1221 ta_latin_metrics_scale_dim(metrics
, scaler
, TA_DIMENSION_HORZ
);
1222 ta_latin_metrics_scale_dim(metrics
, scaler
, TA_DIMENSION_VERT
);
1226 /* walk over all contours and compute its segments */
1229 ta_latin_hints_compute_segments(TA_GlyphHints hints
,
1232 TA_LatinMetrics metrics
= (TA_LatinMetrics
)hints
->metrics
;
1233 TA_AxisHints axis
= &hints
->axis
[dim
];
1234 FT_Error error
= FT_Err_Ok
;
1236 TA_Segment segment
= NULL
;
1239 TA_Point
* contour
= hints
->contours
;
1240 TA_Point
* contour_limit
= contour
+ hints
->num_contours
;
1241 TA_Direction major_dir
, segment_dir
;
1243 FT_Pos flat_threshold
= FLAT_THRESHOLD(metrics
->units_per_em
);
1246 memset(&seg0
, 0, sizeof (TA_SegmentRec
));
1248 seg0
.flags
= TA_EDGE_NORMAL
;
1250 major_dir
= (TA_Direction
)TA_ABS(axis
->major_dir
);
1251 segment_dir
= major_dir
;
1253 axis
->num_segments
= 0;
1255 /* set up (u,v) in each point */
1256 if (dim
== TA_DIMENSION_HORZ
)
1258 TA_Point point
= hints
->points
;
1259 TA_Point limit
= point
+ hints
->num_points
;
1262 for (; point
< limit
; point
++)
1264 point
->u
= point
->fx
;
1265 point
->v
= point
->fy
;
1270 TA_Point point
= hints
->points
;
1271 TA_Point limit
= point
+ hints
->num_points
;
1274 for (; point
< limit
; point
++)
1276 point
->u
= point
->fy
;
1277 point
->v
= point
->fx
;
1281 /* do each contour separately */
1282 for (; contour
< contour_limit
; contour
++)
1284 TA_Point point
= contour
[0];
1285 TA_Point last
= point
->prev
;
1289 FT_Pos min_pos
= 32000; /* minimum segment pos != min_coord */
1290 FT_Pos max_pos
= -32000; /* maximum segment pos != max_coord */
1291 FT_Pos min_on_pos
= 32000;
1292 FT_Pos max_on_pos
= -32000;
1296 if (point
== last
) /* skip singletons -- just in case */
1299 if (TA_ABS(last
->out_dir
) == major_dir
1300 && TA_ABS(point
->out_dir
) == major_dir
)
1302 /* we are already on an edge, try to locate its start */
1307 point
= point
->prev
;
1308 if (TA_ABS(point
->out_dir
) != major_dir
)
1310 point
= point
->next
;
1334 /* get minimum and maximum coordinate of on points */
1335 if (!(point
->flags
& TA_FLAG_CONTROL
))
1344 if (point
->out_dir
!= segment_dir
1347 /* we are just leaving an edge; record a new segment! */
1348 segment
->last
= point
;
1349 segment
->pos
= (FT_Short
)((min_pos
+ max_pos
) >> 1);
1351 /* a segment is round if either its first or last point */
1352 /* is a control point, and the length of the on points */
1353 /* inbetween doesn't exceed a heuristic limit */
1354 if ((segment
->first
->flags
| point
->flags
) & TA_FLAG_CONTROL
1355 && (max_on_pos
- min_on_pos
) < flat_threshold
)
1356 segment
->flags
|= TA_EDGE_ROUND
;
1358 /* compute segment size */
1359 min_pos
= max_pos
= point
->v
;
1361 v
= segment
->first
->v
;
1367 segment
->min_coord
= (FT_Short
)min_pos
;
1368 segment
->max_coord
= (FT_Short
)max_pos
;
1369 segment
->height
= (FT_Short
)(segment
->max_coord
-
1370 segment
->min_coord
);
1378 /* now exit if we are at the start/end point */
1387 && TA_ABS(point
->out_dir
) == major_dir
)
1389 /* this is the start of a new segment! */
1390 segment_dir
= (TA_Direction
)point
->out_dir
;
1392 error
= ta_axis_hints_new_segment(axis
, &segment
);
1396 /* clear all segment fields */
1399 segment
->dir
= (FT_Char
)segment_dir
;
1400 segment
->first
= point
;
1401 segment
->last
= point
;
1403 min_pos
= max_pos
= point
->u
;
1405 if (point
->flags
& TA_FLAG_CONTROL
)
1408 max_on_pos
= -32000;
1411 min_on_pos
= max_on_pos
= point
->v
;
1415 if (point
->out_dir
!= point
->next
->in_dir
)
1417 /* we have a one-point segment */
1418 segment
->pos
= (FT_Short
)min_pos
;
1420 if (point
->flags
& TA_FLAG_CONTROL
)
1421 segment
->flags
|= TA_EDGE_ROUND
;
1423 /* artificially extend the horizontal size if requested */
1424 segment
->min_coord
= (FT_Short
)point
->v
+ point
->left_offset
;
1425 segment
->max_coord
= (FT_Short
)point
->v
+ point
->right_offset
;
1426 segment
->height
= 0;
1433 point
= point
->next
;
1438 /* now slightly increase the height of segments if this makes sense -- */
1439 /* this is used to better detect and ignore serifs */
1441 TA_Segment segments
= axis
->segments
;
1442 TA_Segment segments_end
= segments
+ axis
->num_segments
;
1445 for (segment
= segments
; segment
< segments_end
; segment
++)
1447 TA_Point first
= segment
->first
;
1448 TA_Point last
= segment
->last
;
1450 FT_Pos first_v
= first
->v
;
1451 FT_Pos last_v
= last
->v
;
1454 if (first_v
< last_v
)
1461 segment
->height
= (FT_Short
)(segment
->height
+
1462 ((first_v
- p
->v
) >> 1));
1466 segment
->height
= (FT_Short
)(segment
->height
+
1467 ((p
->v
- last_v
) >> 1));
1476 segment
->height
= (FT_Short
)(segment
->height
+
1477 ((p
->v
- first_v
) >> 1));
1481 segment
->height
= (FT_Short
)(segment
->height
+
1482 ((last_v
- p
->v
) >> 1));
1492 /* link segments to form stems and serifs; if `width_count' and */
1493 /* `widths' are non-zero, use them to fine-tune the scoring function */
1496 ta_latin_hints_link_segments(TA_GlyphHints hints
,
1497 FT_UInt width_count
,
1498 TA_WidthRec
* widths
,
1501 TA_AxisHints axis
= &hints
->axis
[dim
];
1503 TA_Segment segments
= axis
->segments
;
1504 TA_Segment segment_limit
= segments
+ axis
->num_segments
;
1506 FT_Pos len_threshold
, len_score
, dist_score
, max_width
;
1507 TA_Segment seg1
, seg2
;
1511 max_width
= widths
[width_count
- 1].org
;
1515 /* a heuristic value to set up a minimum value for overlapping */
1516 len_threshold
= TA_LATIN_CONSTANT(hints
->metrics
, 8);
1517 if (len_threshold
== 0)
1520 /* a heuristic value to weight lengths */
1521 len_score
= TA_LATIN_CONSTANT(hints
->metrics
, 6000);
1523 /* a heuristic value to weight distances (no call to */
1524 /* TA_LATIN_CONSTANT needed, since we work on multiples */
1525 /* of the stem width) */
1528 /* now compare each segment to the others */
1529 for (seg1
= segments
; seg1
< segment_limit
; seg1
++)
1531 if (seg1
->dir
!= axis
->major_dir
)
1534 /* search for stems having opposite directions, */
1535 /* with seg1 to the `left' of seg2 */
1536 for (seg2
= segments
; seg2
< segment_limit
; seg2
++)
1538 FT_Pos pos1
= seg1
->pos
;
1539 FT_Pos pos2
= seg2
->pos
;
1542 if (seg1
->dir
+ seg2
->dir
== 0
1545 /* compute distance between the two segments */
1546 FT_Pos min
= seg1
->min_coord
;
1547 FT_Pos max
= seg1
->max_coord
;
1551 if (min
< seg2
->min_coord
)
1552 min
= seg2
->min_coord
;
1553 if (max
> seg2
->max_coord
)
1554 max
= seg2
->max_coord
;
1556 /* compute maximum coordinate difference of the two segments */
1557 /* (this is, how much they overlap) */
1560 /* for one-point segments, `len' is zero if there is an overlap */
1561 /* (and negative otherwise); we have to correct this */
1563 && (seg1
->min_coord
== seg1
->max_coord
1564 || seg2
->min_coord
== seg2
->max_coord
))
1565 len
= len_threshold
;
1567 if (len
>= len_threshold
)
1570 * The score is the sum of two demerits indicating the
1571 * `badness' of a fit, measured along the segments' main axis
1572 * and orthogonal to it, respectively.
1574 * o The less overlapping along the main axis, the worse it
1575 * is, causing a larger demerit.
1577 * o The nearer the orthogonal distance to a stem width, the
1578 * better it is, causing a smaller demerit. For simplicity,
1579 * however, we only increase the demerit for values that
1580 * exceed the largest stem width.
1583 FT_Pos dist
= pos2
- pos1
;
1585 FT_Pos dist_demerit
, score
;
1590 /* distance demerits are based on multiples of `max_width'; */
1591 /* we scale by 1024 for getting more precision */
1592 FT_Pos delta
= (dist
<< 10) / max_width
- (1 << 10);
1596 dist_demerit
= 32000;
1598 dist_demerit
= delta
* delta
/ dist_score
;
1603 dist_demerit
= dist
; /* default if no widths available */
1605 score
= dist_demerit
+ len_score
/ len
;
1607 /* and we search for the smallest score */
1608 if (score
< seg1
->score
)
1610 seg1
->score
= score
;
1614 if (score
< seg2
->score
)
1616 seg2
->score
= score
;
1624 /* now compute the `serif' segments, cf. explanations in `tahints.h' */
1625 for (seg1
= segments
; seg1
< segment_limit
; seg1
++)
1631 if (seg2
->link
!= seg1
)
1634 seg1
->serif
= seg2
->link
;
1641 /* link segments to edges, using feature analysis for selection */
1644 ta_latin_hints_compute_edges(TA_GlyphHints hints
,
1647 TA_AxisHints axis
= &hints
->axis
[dim
];
1648 FT_Error error
= FT_Err_Ok
;
1649 TA_LatinAxis laxis
= &((TA_LatinMetrics
)hints
->metrics
)->axis
[dim
];
1651 TA_Segment segments
= axis
->segments
;
1652 TA_Segment segment_limit
= segments
+ axis
->num_segments
;
1656 TA_Direction up_dir
;
1659 FT_Pos edge_distance_threshold
;
1660 FT_Pos segment_length_threshold
;
1663 axis
->num_edges
= 0;
1665 scale
= (dim
== TA_DIMENSION_HORZ
) ? hints
->x_scale
1669 up_dir
= (dim
== TA_DIMENSION_HORZ
) ? TA_DIR_UP
1673 /* we ignore all segments that are less than 1 pixel in length */
1674 /* to avoid many problems with serif fonts */
1675 /* (the corresponding threshold is computed in font units) */
1676 if (dim
== TA_DIMENSION_HORZ
)
1677 segment_length_threshold
= FT_DivFix(64, hints
->y_scale
);
1679 segment_length_threshold
= 0;
1681 /********************************************************************/
1683 /* We begin by generating a sorted table of edges for the current */
1684 /* direction. To do so, we simply scan each segment and try to find */
1685 /* an edge in our table that corresponds to its position. */
1687 /* If no edge is found, we create and insert a new edge in the */
1688 /* sorted table. Otherwise, we simply add the segment to the edge's */
1689 /* list which gets processed in the second step to compute the */
1690 /* edge's properties. */
1692 /* Note that the table of edges is sorted along the segment/edge */
1695 /********************************************************************/
1697 /* assure that edge distance threshold is at most 0.25px */
1698 edge_distance_threshold
= FT_MulFix(laxis
->edge_distance_threshold
,
1700 if (edge_distance_threshold
> 64 / 4)
1701 edge_distance_threshold
= 64 / 4;
1703 edge_distance_threshold
= FT_DivFix(edge_distance_threshold
,
1706 for (seg
= segments
; seg
< segment_limit
; seg
++)
1708 TA_Edge found
= NULL
;
1712 if (seg
->height
< segment_length_threshold
)
1715 /* a special case for serif edges: */
1716 /* if they are smaller than 1.5 pixels we ignore them */
1718 && 2 * seg
->height
< 3 * segment_length_threshold
)
1721 /* look for an edge corresponding to the segment */
1722 for (ee
= 0; ee
< axis
->num_edges
; ee
++)
1724 TA_Edge edge
= axis
->edges
+ ee
;
1728 dist
= seg
->pos
- edge
->fpos
;
1732 if (dist
< edge_distance_threshold
&& edge
->dir
== seg
->dir
)
1744 /* insert a new edge in the list and sort according to the position */
1745 error
= ta_axis_hints_new_edge(axis
, seg
->pos
,
1746 (TA_Direction
)seg
->dir
,
1751 /* add the segment to the new edge's list */
1752 memset(edge
, 0, sizeof (TA_EdgeRec
));
1755 edge
->dir
= seg
->dir
;
1756 edge
->fpos
= seg
->pos
;
1757 edge
->opos
= FT_MulFix(seg
->pos
, scale
);
1758 edge
->pos
= edge
->opos
;
1759 seg
->edge_next
= seg
;
1763 /* if an edge was found, simply add the segment to the edge's list */
1764 seg
->edge_next
= found
->first
;
1765 found
->last
->edge_next
= seg
;
1770 /*****************************************************************/
1772 /* Good, we now compute each edge's properties according to */
1773 /* the segments found on its position. Basically, these are */
1775 /* - the edge's main direction */
1776 /* - stem edge, serif edge or both (which defaults to stem then) */
1777 /* - rounded edge, straight or both (which defaults to straight) */
1778 /* - link for edge */
1780 /*****************************************************************/
1782 /* first of all, set the `edge' field in each segment -- this is */
1783 /* required in order to compute edge links */
1785 /* note that removing this loop and setting the `edge' field of each */
1786 /* segment directly in the code above slows down execution speed for */
1787 /* some reasons on platforms like the Sun */
1789 TA_Edge edges
= axis
->edges
;
1790 TA_Edge edge_limit
= edges
+ axis
->num_edges
;
1794 for (edge
= edges
; edge
< edge_limit
; edge
++)
1801 seg
= seg
->edge_next
;
1802 } while (seg
!= edge
->first
);
1805 /* now compute each edge properties */
1806 for (edge
= edges
; edge
< edge_limit
; edge
++)
1808 FT_Int is_round
= 0; /* does it contain round segments? */
1809 FT_Int is_straight
= 0; /* does it contain straight segments? */
1811 FT_Pos ups
= 0; /* number of upwards segments */
1812 FT_Pos downs
= 0; /* number of downwards segments */
1823 /* check for roundness of segment */
1824 if (seg
->flags
& TA_EDGE_ROUND
)
1830 /* check for segment direction */
1831 if (seg
->dir
== up_dir
)
1832 ups
+= seg
->max_coord
- seg
->min_coord
;
1834 downs
+= seg
->max_coord
- seg
->min_coord
;
1837 /* check for links -- */
1838 /* if seg->serif is set, then seg->link must be ignored */
1839 is_serif
= (FT_Bool
)(seg
->serif
1841 && seg
->serif
->edge
!= edge
);
1843 if ((seg
->link
&& seg
->link
->edge
!= NULL
)
1856 edge2
= edge
->serif
;
1865 edge_delta
= edge
->fpos
- edge2
->fpos
;
1867 edge_delta
= -edge_delta
;
1869 seg_delta
= seg
->pos
- seg2
->pos
;
1871 seg_delta
= -seg_delta
;
1873 if (seg_delta
< edge_delta
)
1881 edge
->serif
= edge2
;
1882 edge2
->flags
|= TA_EDGE_SERIF
;
1888 seg
= seg
->edge_next
;
1889 } while (seg
!= edge
->first
);
1891 /* set the round/straight flags */
1892 edge
->flags
= TA_EDGE_NORMAL
;
1895 && is_round
>= is_straight
)
1896 edge
->flags
|= TA_EDGE_ROUND
;
1899 /* set the edge's main direction */
1900 edge
->dir
= TA_DIR_NONE
;
1903 edge
->dir
= (FT_Char
)up_dir
;
1905 else if (ups
< downs
)
1906 edge
->dir
= (FT_Char
)-up_dir
;
1908 else if (ups
== downs
)
1909 edge
->dir
= 0; /* both up and down! */
1912 /* get rid of serifs if link is set */
1913 /* XXX: this gets rid of many unpleasant artefacts! */
1914 /* example: the `c' in cour.pfa at size 13 */
1916 if (edge
->serif
&& edge
->link
)
1926 /* detect segments and edges for given dimension */
1929 ta_latin_hints_detect_features(TA_GlyphHints hints
,
1930 FT_UInt width_count
,
1931 TA_WidthRec
* widths
,
1937 error
= ta_latin_hints_compute_segments(hints
, dim
);
1940 ta_latin_hints_link_segments(hints
, width_count
, widths
, dim
);
1942 error
= ta_latin_hints_compute_edges(hints
, dim
);
1949 /* compute all edges which lie within blue zones */
1952 ta_latin_hints_compute_blue_edges(TA_GlyphHints hints
,
1953 TA_LatinMetrics metrics
)
1955 TA_AxisHints axis
= &hints
->axis
[TA_DIMENSION_VERT
];
1957 TA_Edge edge
= axis
->edges
;
1958 TA_Edge edge_limit
= edge
+ axis
->num_edges
;
1960 TA_LatinAxis latin
= &metrics
->axis
[TA_DIMENSION_VERT
];
1961 FT_Fixed scale
= latin
->scale
;
1964 /* compute which blue zones are active, */
1965 /* i.e. have their scaled size < 3/4 pixels */
1967 /* for each horizontal edge search the blue zone which is closest */
1968 for (; edge
< edge_limit
; edge
++)
1971 TA_Width best_blue
= NULL
;
1972 FT_Bool best_blue_is_neutral
= 0;
1973 FT_Pos best_dist
; /* initial threshold */
1975 FT_UInt best_blue_idx
= 0;
1976 FT_Bool best_blue_is_shoot
= 0;
1979 /* compute the initial threshold as a fraction of the EM size */
1980 /* (the value 40 is heuristic) */
1981 best_dist
= FT_MulFix(metrics
->units_per_em
/ 40, scale
);
1983 /* assure a minimum distance of 0.5px */
1984 if (best_dist
> 64 / 2)
1987 /* this loop also handles the two extra blue zones */
1988 /* for usWinAscent and usWinDescent */
1989 /* if option `windows-compatibility' is set */
1991 bb
< latin
->blue_count
1992 + (metrics
->root
.globals
->font
->windows_compatibility
? 2 : 0);
1995 TA_LatinBlue blue
= latin
->blues
+ bb
;
1996 FT_Bool is_top_blue
, is_neutral_blue
, is_major_dir
;
1999 /* skip inactive blue zones (i.e., those that are too large) */
2000 if (!(blue
->flags
& TA_LATIN_BLUE_ACTIVE
))
2003 /* if it is a top zone, check for right edges (against the major */
2004 /* direction); if it is a bottom zone, check for left edges (in */
2005 /* the major direction) */
2006 is_top_blue
= (FT_Byte
)((blue
->flags
& TA_LATIN_BLUE_TOP
) != 0);
2007 is_neutral_blue
= (FT_Byte
)((blue
->flags
& TA_LATIN_BLUE_NEUTRAL
) != 0);
2008 is_major_dir
= FT_BOOL(edge
->dir
== axis
->major_dir
);
2010 /* neutral blue zones are handled for both directions */
2011 if (is_top_blue
^ is_major_dir
|| is_neutral_blue
)
2016 /* first of all, compare it to the reference position */
2017 dist
= edge
->fpos
- blue
->ref
.org
;
2021 dist
= FT_MulFix(dist
, scale
);
2022 if (dist
< best_dist
)
2025 best_blue
= &blue
->ref
;
2026 best_blue_is_neutral
= is_neutral_blue
;
2029 best_blue_is_shoot
= 0;
2032 /* now compare it to the overshoot position and check whether */
2033 /* the edge is rounded, and whether the edge is over the */
2034 /* reference position of a top zone, or under the reference */
2035 /* position of a bottom zone (provided we don't have a */
2036 /* neutral blue zone) */
2037 if (edge
->flags
& TA_EDGE_ROUND
2039 && !is_neutral_blue
)
2041 FT_Bool is_under_ref
= FT_BOOL(edge
->fpos
< blue
->ref
.org
);
2044 if (is_top_blue
^ is_under_ref
)
2046 dist
= edge
->fpos
- blue
->shoot
.org
;
2050 dist
= FT_MulFix(dist
, scale
);
2051 if (dist
< best_dist
)
2054 best_blue
= &blue
->shoot
;
2055 best_blue_is_neutral
= is_neutral_blue
;
2058 best_blue_is_shoot
= 1;
2067 edge
->blue_edge
= best_blue
;
2068 edge
->best_blue_idx
= best_blue_idx
;
2069 edge
->best_blue_is_shoot
= best_blue_is_shoot
;
2070 if (best_blue_is_neutral
)
2071 edge
->flags
|= TA_EDGE_NEUTRAL
;
2077 /* initalize hinting engine */
2080 ta_latin_hints_init(TA_GlyphHints hints
,
2081 TA_LatinMetrics metrics
)
2083 FT_Render_Mode mode
;
2084 FT_UInt32 scaler_flags
, other_flags
;
2085 FT_Face face
= metrics
->root
.scaler
.face
;
2088 ta_glyph_hints_rescale(hints
, (TA_StyleMetrics
)metrics
);
2090 /* correct x_scale and y_scale if needed, since they may have */
2091 /* been modified by `ta_latin_metrics_scale_dim' above */
2092 hints
->x_scale
= metrics
->axis
[TA_DIMENSION_HORZ
].scale
;
2093 hints
->x_delta
= metrics
->axis
[TA_DIMENSION_HORZ
].delta
;
2094 hints
->y_scale
= metrics
->axis
[TA_DIMENSION_VERT
].scale
;
2095 hints
->y_delta
= metrics
->axis
[TA_DIMENSION_VERT
].delta
;
2097 /* compute flags depending on render mode, etc. */
2098 mode
= metrics
->root
.scaler
.render_mode
;
2100 #if 0 /* #ifdef TA_CONFIG_OPTION_USE_WARPER */
2101 if (mode
== FT_RENDER_MODE_LCD
2102 || mode
== FT_RENDER_MODE_LCD_V
)
2103 metrics
->root
.scaler
.render_mode
=
2104 mode
= FT_RENDER_MODE_NORMAL
;
2107 scaler_flags
= hints
->scaler_flags
;
2110 /* we snap the width of vertical stems for the monochrome */
2111 /* and horizontal LCD rendering targets only */
2112 if (mode
== FT_RENDER_MODE_MONO
2113 || mode
== FT_RENDER_MODE_LCD
)
2114 other_flags
|= TA_LATIN_HINTS_HORZ_SNAP
;
2116 /* we snap the width of horizontal stems for the monochrome */
2117 /* and vertical LCD rendering targets only */
2118 if (mode
== FT_RENDER_MODE_MONO
2119 || mode
== FT_RENDER_MODE_LCD_V
)
2120 other_flags
|= TA_LATIN_HINTS_VERT_SNAP
;
2122 /* we adjust stems to full pixels only if we don't use the `light' mode */
2123 if (mode
!= FT_RENDER_MODE_LIGHT
)
2124 other_flags
|= TA_LATIN_HINTS_STEM_ADJUST
;
2126 if (mode
== FT_RENDER_MODE_MONO
)
2127 other_flags
|= TA_LATIN_HINTS_MONO
;
2129 /* in `light' hinting mode we disable horizontal hinting completely; */
2130 /* we also do it if the face is italic -- */
2131 /* however, if warping is enabled (which only works in `light' hinting */
2132 /* mode), advance widths get adjusted, too */
2133 if (mode
== FT_RENDER_MODE_LIGHT
2134 || (face
->style_flags
& FT_STYLE_FLAG_ITALIC
) != 0)
2135 scaler_flags
|= TA_SCALER_FLAG_NO_HORIZONTAL
;
2137 #ifdef TA_CONFIG_OPTION_USE_WARPER
2138 /* get (global) warper flag */
2139 if (!metrics
->root
.globals
->module
->warping
)
2140 scaler_flags
|= TA_SCALER_FLAG_NO_WARPER
;
2143 hints
->scaler_flags
= scaler_flags
;
2144 hints
->other_flags
= other_flags
;
2150 /* snap a given width in scaled coordinates to */
2151 /* one of the current standard widths */
2154 ta_latin_snap_width(TA_Width widths
,
2159 FT_Pos best
= 64 + 32 + 2;
2160 FT_Pos reference
= width
;
2164 for (n
= 0; n
< count
; n
++)
2181 scaled
= TA_PIX_ROUND(reference
);
2183 if (width
>= reference
)
2185 if (width
< scaled
+ 48)
2190 if (width
> scaled
- 48)
2198 /* compute the snapped width of a given stem, ignoring very thin ones */
2200 /* there is a lot of voodoo in this function; changing the hard-coded */
2201 /* parameters influences the whole hinting process */
2204 ta_latin_compute_stem_width(TA_GlyphHints hints
,
2210 TA_LatinMetrics metrics
= (TA_LatinMetrics
) hints
->metrics
;
2211 TA_LatinAxis axis
= &metrics
->axis
[dim
];
2213 FT_Pos dist
= width
;
2215 FT_Int vertical
= (dim
== TA_DIMENSION_VERT
);
2218 if (!TA_LATIN_HINTS_DO_STEM_ADJUST(hints
)
2219 || axis
->extra_light
)
2228 if ((vertical
&& !TA_LATIN_HINTS_DO_VERT_SNAP(hints
))
2229 || (!vertical
&& !TA_LATIN_HINTS_DO_HORZ_SNAP(hints
)))
2231 /* smooth hinting process: very lightly quantize the stem width */
2233 /* leave the widths of serifs alone */
2234 if ((stem_flags
& TA_EDGE_SERIF
)
2238 else if (base_flags
& TA_EDGE_ROUND
)
2246 if (axis
->width_count
> 0)
2251 /* compare to standard width */
2252 delta
= dist
- axis
->widths
[0].cur
;
2259 dist
= axis
->widths
[0].cur
;
2273 else if (delta
< 32)
2275 else if (delta
< 54)
2281 dist
= (dist
+ 32) & ~63;
2286 /* strong hinting process: snap the stem width to integer pixels */
2288 FT_Pos org_dist
= dist
;
2291 dist
= ta_latin_snap_width(axis
->widths
, axis
->width_count
, dist
);
2295 /* in the case of vertical hinting, */
2296 /* always round the stem heights to integer pixels */
2299 dist
= (dist
+ 16) & ~63;
2305 if (TA_LATIN_HINTS_DO_MONO(hints
))
2307 /* monochrome horizontal hinting: */
2308 /* snap widths to integer pixels with a different threshold */
2313 dist
= (dist
+ 32) & ~63;
2317 /* for horizontal anti-aliased hinting, we adopt a more subtle */
2318 /* approach: we strengthen small stems, round stems whose size */
2319 /* is between 1 and 2 pixels to an integer, otherwise nothing */
2322 dist
= (dist
+ 64) >> 1;
2324 else if (dist
< 128)
2326 /* we only round to an integer width if the corresponding */
2327 /* distortion is less than 1/4 pixel -- otherwise, this */
2328 /* makes everything worse since the diagonals, which are */
2329 /* not hinted, appear a lot bolder or thinner than the */
2330 /* vertical stems */
2335 dist
= (dist
+ 22) & ~63;
2336 delta
= dist
- org_dist
;
2344 dist
= (dist
+ 64) >> 1;
2348 /* round otherwise to prevent color fringes in LCD mode */
2349 dist
= (dist
+ 32) & ~63;
2362 /* align one stem edge relative to the previous stem edge */
2365 ta_latin_align_linked_edge(TA_GlyphHints hints
,
2370 FT_Pos dist
= stem_edge
->opos
- base_edge
->opos
;
2372 FT_Pos fitted_width
= ta_latin_compute_stem_width(
2378 stem_edge
->pos
= base_edge
->pos
+ fitted_width
;
2380 TA_LOG((" LINK: edge %d (opos=%.2f) linked to %.2f,"
2381 " dist was %.2f, now %.2f\n",
2382 stem_edge
- hints
->axis
[dim
].edges
, stem_edge
->opos
/ 64.0,
2383 stem_edge
->pos
/ 64.0, dist
/ 64.0, fitted_width
/ 64.0));
2385 if (hints
->recorder
)
2386 hints
->recorder(ta_link
, hints
, dim
,
2387 base_edge
, stem_edge
, NULL
, NULL
, NULL
);
2391 /* shift the coordinates of the `serif' edge by the same amount */
2392 /* as the corresponding `base' edge has been moved already */
2395 ta_latin_align_serif_edge(TA_GlyphHints hints
,
2401 serif
->pos
= base
->pos
+ (serif
->opos
- base
->opos
);
2405 /* the main grid-fitting routine */
2408 ta_latin_hint_edges(TA_GlyphHints hints
,
2411 TA_AxisHints axis
= &hints
->axis
[dim
];
2413 TA_Edge edges
= axis
->edges
;
2414 TA_Edge edge_limit
= edges
+ axis
->num_edges
;
2418 TA_Edge anchor
= NULL
;
2419 FT_Int has_serifs
= 0;
2422 FT_UInt num_actions
= 0;
2425 TA_LOG(("latin %s edge hinting (style `%s')\n",
2426 dim
== TA_DIMENSION_VERT
? "horizontal" : "vertical",
2427 ta_style_names
[hints
->metrics
->style_class
->style
]));
2429 /* we begin by aligning all stems relative to the blue zone if needed -- */
2430 /* that's only for horizontal edges */
2432 if (dim
== TA_DIMENSION_VERT
2433 && TA_HINTS_DO_BLUES(hints
))
2435 for (edge
= edges
; edge
< edge_limit
; edge
++)
2438 TA_Edge edge1
, edge2
; /* these edges form the stem to check */
2441 if (edge
->flags
& TA_EDGE_DONE
)
2448 * If a stem contains both a neutral and a non-neutral blue zone,
2449 * skip the neutral one. Otherwise, outlines with different
2450 * directions might be incorrectly aligned at the same vertical
2453 * If we have two neutral blue zones, skip one of them.
2455 if (edge
->blue_edge
&& edge2
&& edge2
->blue_edge
)
2457 FT_Byte neutral
= edge
->flags
& TA_EDGE_NEUTRAL
;
2458 FT_Byte neutral2
= edge2
->flags
& TA_EDGE_NEUTRAL
;
2463 edge2
->blue_edge
= NULL
;
2464 edge2
->flags
&= ~TA_EDGE_NEUTRAL
;
2468 edge
->blue_edge
= NULL
;
2469 edge
->flags
&= ~TA_EDGE_NEUTRAL
;
2473 blue
= edge
->blue_edge
;
2477 /* flip edges if the other edge is aligned to a blue zone */
2478 else if (edge2
&& edge2
->blue_edge
)
2480 blue
= edge2
->blue_edge
;
2490 TA_LOG((" BLUE_ANCHOR: edge %d (opos=%.2f) snapped to %.2f,"
2491 " was %.2f (anchor=edge %d)\n",
2492 edge1
- edges
, edge1
->opos
/ 64.0, blue
->fit
/ 64.0,
2493 edge1
->pos
/ 64.0, edge
- edges
));
2495 TA_LOG((" BLUE: edge %d (opos=%.2f) snapped to %.2f, was %.2f\n",
2496 edge1
- edges
, edge1
->opos
/ 64.0, blue
->fit
/ 64.0,
2497 edge1
->pos
/ 64.0));
2502 edge1
->pos
= blue
->fit
;
2503 edge1
->flags
|= TA_EDGE_DONE
;
2505 if (hints
->recorder
)
2508 hints
->recorder(ta_blue_anchor
, hints
, dim
,
2509 edge1
, edge
, NULL
, NULL
, NULL
);
2511 hints
->recorder(ta_blue
, hints
, dim
,
2512 edge1
, NULL
, NULL
, NULL
, NULL
);
2515 if (edge2
&& !edge2
->blue_edge
)
2517 ta_latin_align_linked_edge(hints
, dim
, edge1
, edge2
);
2518 edge2
->flags
|= TA_EDGE_DONE
;
2530 /* now we align all other stem edges, */
2531 /* trying to maintain the relative order of stems in the glyph */
2532 for (edge
= edges
; edge
< edge_limit
; edge
++)
2537 if (edge
->flags
& TA_EDGE_DONE
)
2540 /* skip all non-stem edges */
2548 /* now align the stem */
2550 /* this should not happen, but it's better to be safe */
2551 if (edge2
->blue_edge
)
2553 TA_LOG((" ASSERTION FAILED for edge %d\n", edge2
- edges
));
2555 ta_latin_align_linked_edge(hints
, dim
, edge2
, edge
);
2556 edge
->flags
|= TA_EDGE_DONE
;
2566 /* if we reach this if clause, no stem has been aligned yet */
2568 FT_Pos org_len
, org_center
, cur_len
;
2569 FT_Pos cur_pos1
, error1
, error2
, u_off
, d_off
;
2572 org_len
= edge2
->opos
- edge
->opos
;
2573 cur_len
= ta_latin_compute_stem_width(hints
, dim
, org_len
,
2574 edge
->flags
, edge2
->flags
);
2576 /* some voodoo to specially round edges for small stem widths; */
2577 /* the idea is to align the center of a stem, */
2578 /* then shifting the stem edges to suitable positions */
2587 /* 1px < width < 1.5px */
2594 org_center
= edge
->opos
+ (org_len
>> 1);
2595 cur_pos1
= TA_PIX_ROUND(org_center
);
2597 error1
= org_center
- (cur_pos1
- u_off
);
2601 error2
= org_center
- (cur_pos1
+ d_off
);
2605 if (error1
< error2
)
2610 edge
->pos
= cur_pos1
- cur_len
/ 2;
2611 edge2
->pos
= edge
->pos
+ cur_len
;
2614 edge
->pos
= TA_PIX_ROUND(edge
->opos
);
2617 edge
->flags
|= TA_EDGE_DONE
;
2619 TA_LOG((" ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
2620 " snapped to %.2f and %.2f\n",
2621 edge
- edges
, edge
->opos
/ 64.0,
2622 edge2
- edges
, edge2
->opos
/ 64.0,
2623 edge
->pos
/ 64.0, edge2
->pos
/ 64.0));
2625 if (hints
->recorder
)
2626 hints
->recorder(ta_anchor
, hints
, dim
,
2627 edge
, edge2
, NULL
, NULL
, NULL
);
2629 ta_latin_align_linked_edge(hints
, dim
, edge
, edge2
);
2637 FT_Pos org_pos
, org_len
, org_center
, cur_len
;
2638 FT_Pos cur_pos1
, cur_pos2
, delta1
, delta2
;
2641 org_pos
= anchor
->pos
+ (edge
->opos
- anchor
->opos
);
2642 org_len
= edge2
->opos
- edge
->opos
;
2643 org_center
= org_pos
+ (org_len
>> 1);
2645 cur_len
= ta_latin_compute_stem_width(hints
, dim
, org_len
,
2646 edge
->flags
, edge2
->flags
);
2648 if (edge2
->flags
& TA_EDGE_DONE
)
2650 TA_LOG((" ADJUST: edge %d (pos=%.2f) moved to %.2f\n",
2651 edge
- edges
, edge
->pos
/ 64.0,
2652 (edge2
->pos
- cur_len
) / 64.0));
2654 edge
->pos
= edge2
->pos
- cur_len
;
2656 if (hints
->recorder
)
2658 TA_Edge bound
= NULL
;
2664 hints
->recorder(ta_adjust
, hints
, dim
,
2665 edge
, edge2
, NULL
, bound
, NULL
);
2669 else if (cur_len
< 96)
2671 FT_Pos u_off
, d_off
;
2674 cur_pos1
= TA_PIX_ROUND(org_center
);
2687 delta1
= org_center
- (cur_pos1
- u_off
);
2691 delta2
= org_center
- (cur_pos1
+ d_off
);
2695 if (delta1
< delta2
)
2700 edge
->pos
= cur_pos1
- cur_len
/ 2;
2701 edge2
->pos
= cur_pos1
+ cur_len
/ 2;
2703 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2704 " snapped to %.2f and %.2f\n",
2705 edge
- edges
, edge
->opos
/ 64.0,
2706 edge2
- edges
, edge2
->opos
/ 64.0,
2707 edge
->pos
/ 64.0, edge2
->pos
/ 64.0));
2709 if (hints
->recorder
)
2711 TA_Edge bound
= NULL
;
2717 hints
->recorder(ta_stem
, hints
, dim
,
2718 edge
, edge2
, NULL
, bound
, NULL
);
2724 org_pos
= anchor
->pos
+ (edge
->opos
- anchor
->opos
);
2725 org_len
= edge2
->opos
- edge
->opos
;
2726 org_center
= org_pos
+ (org_len
>> 1);
2728 cur_len
= ta_latin_compute_stem_width(hints
, dim
, org_len
,
2729 edge
->flags
, edge2
->flags
);
2731 cur_pos1
= TA_PIX_ROUND(org_pos
);
2732 delta1
= cur_pos1
+ (cur_len
>> 1) - org_center
;
2736 cur_pos2
= TA_PIX_ROUND(org_pos
+ org_len
) - cur_len
;
2737 delta2
= cur_pos2
+ (cur_len
>> 1) - org_center
;
2741 edge
->pos
= (delta1
< delta2
) ? cur_pos1
: cur_pos2
;
2742 edge2
->pos
= edge
->pos
+ cur_len
;
2744 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2745 " snapped to %.2f and %.2f\n",
2746 edge
- edges
, edge
->opos
/ 64.0,
2747 edge2
- edges
, edge2
->opos
/ 64.0,
2748 edge
->pos
/ 64.0, edge2
->pos
/ 64.0));
2750 if (hints
->recorder
)
2752 TA_Edge bound
= NULL
;
2758 hints
->recorder(ta_stem
, hints
, dim
,
2759 edge
, edge2
, NULL
, bound
, NULL
);
2767 edge
->flags
|= TA_EDGE_DONE
;
2768 edge2
->flags
|= TA_EDGE_DONE
;
2771 && edge
->pos
< edge
[-1].pos
)
2774 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2775 edge
- edges
, edge
->pos
/ 64.0, edge
[-1].pos
/ 64.0));
2780 edge
->pos
= edge
[-1].pos
;
2782 if (hints
->recorder
)
2783 hints
->recorder(ta_bound
, hints
, dim
,
2784 edge
, &edge
[-1], NULL
, NULL
, NULL
);
2789 /* make sure that lowercase m's maintain their symmetry */
2791 /* In general, lowercase m's have six vertical edges if they are sans */
2792 /* serif, or twelve if they are with serifs. This implementation is */
2793 /* based on that assumption, and seems to work very well with most */
2794 /* faces. However, if for a certain face this assumption is not */
2795 /* true, the m is just rendered like before. In addition, any stem */
2796 /* correction will only be applied to symmetrical glyphs (even if the */
2797 /* glyph is not an m), so the potential for unwanted distortion is */
2798 /* relatively low. */
2800 /* we don't handle horizontal edges since we can't easily assure that */
2801 /* the third (lowest) stem aligns with the base line; it might end up */
2802 /* one pixel higher or lower */
2804 n_edges
= edge_limit
- edges
;
2805 if (dim
== TA_DIMENSION_HORZ
2806 && (n_edges
== 6 || n_edges
== 12))
2808 TA_Edge edge1
, edge2
, edge3
;
2809 FT_Pos dist1
, dist2
, span
, delta
;
2825 dist1
= edge2
->opos
- edge1
->opos
;
2826 dist2
= edge3
->opos
- edge2
->opos
;
2828 span
= dist1
- dist2
;
2834 delta
= edge3
->pos
- (2 * edge2
->pos
- edge1
->pos
);
2835 edge3
->pos
-= delta
;
2837 edge3
->link
->pos
-= delta
;
2839 /* move the serifs along with the stem */
2842 (edges
+ 8)->pos
-= delta
;
2843 (edges
+ 11)->pos
-= delta
;
2846 edge3
->flags
|= TA_EDGE_DONE
;
2848 edge3
->link
->flags
|= TA_EDGE_DONE
;
2852 if (has_serifs
|| !anchor
)
2854 /* now hint the remaining edges (serifs and single) */
2855 /* in order to complete our processing */
2856 for (edge
= edges
; edge
< edge_limit
; edge
++)
2858 TA_Edge lower_bound
= NULL
;
2859 TA_Edge upper_bound
= NULL
;
2864 if (edge
->flags
& TA_EDGE_DONE
)
2871 delta
= edge
->serif
->opos
- edge
->opos
;
2877 lower_bound
= &edge
[-1];
2879 if (edge
+ 1 < edge_limit
2880 && edge
[1].flags
& TA_EDGE_DONE
)
2881 upper_bound
= &edge
[1];
2884 if (delta
< 64 + 16)
2886 ta_latin_align_serif_edge(hints
, edge
->serif
, edge
);
2888 TA_LOG((" SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
2889 " aligned to %.2f\n",
2890 edge
- edges
, edge
->opos
/ 64.0,
2891 edge
->serif
- edges
, edge
->serif
->opos
/ 64.0,
2894 if (hints
->recorder
)
2895 hints
->recorder(ta_serif
, hints
, dim
,
2896 edge
, NULL
, NULL
, lower_bound
, upper_bound
);
2900 edge
->pos
= TA_PIX_ROUND(edge
->opos
);
2903 TA_LOG((" SERIF_ANCHOR: edge %d (opos=%.2f) snapped to %.2f\n",
2904 edge
- edges
, edge
->opos
/ 64.0, edge
->pos
/ 64.0));
2906 if (hints
->recorder
)
2907 hints
->recorder(ta_serif_anchor
, hints
, dim
,
2908 edge
, NULL
, NULL
, lower_bound
, upper_bound
);
2912 TA_Edge before
, after
;
2915 for (before
= edge
- 1; before
>= edges
; before
--)
2916 if (before
->flags
& TA_EDGE_DONE
)
2919 for (after
= edge
+ 1; after
< edge_limit
; after
++)
2920 if (after
->flags
& TA_EDGE_DONE
)
2923 if (before
>= edges
&& before
< edge
2924 && after
< edge_limit
&& after
> edge
)
2926 if (after
->opos
== before
->opos
)
2927 edge
->pos
= before
->pos
;
2929 edge
->pos
= before
->pos
+ FT_MulDiv(edge
->opos
- before
->opos
,
2930 after
->pos
- before
->pos
,
2931 after
->opos
- before
->opos
);
2933 TA_LOG((" SERIF_LINK1: edge %d (opos=%.2f) snapped to %.2f"
2934 " from %d (opos=%.2f)\n",
2935 edge
- edges
, edge
->opos
/ 64.0,
2937 before
- edges
, before
->opos
/ 64.0));
2939 if (hints
->recorder
)
2940 hints
->recorder(ta_serif_link1
, hints
, dim
,
2941 edge
, before
, after
, lower_bound
, upper_bound
);
2945 edge
->pos
= anchor
->pos
+ ((edge
->opos
- anchor
->opos
+ 16) & ~31);
2946 TA_LOG((" SERIF_LINK2: edge %d (opos=%.2f) snapped to %.2f\n",
2947 edge
- edges
, edge
->opos
/ 64.0, edge
->pos
/ 64.0));
2949 if (hints
->recorder
)
2950 hints
->recorder(ta_serif_link2
, hints
, dim
,
2951 edge
, NULL
, NULL
, lower_bound
, upper_bound
);
2958 edge
->flags
|= TA_EDGE_DONE
;
2961 && edge
->pos
< edge
[-1].pos
)
2964 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2965 edge
- edges
, edge
->pos
/ 64.0, edge
[-1].pos
/ 64.0));
2969 edge
->pos
= edge
[-1].pos
;
2971 if (hints
->recorder
)
2972 hints
->recorder(ta_bound
, hints
, dim
,
2973 edge
, &edge
[-1], NULL
, NULL
, NULL
);
2976 if (edge
+ 1 < edge_limit
2977 && edge
[1].flags
& TA_EDGE_DONE
2978 && edge
->pos
> edge
[1].pos
)
2981 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2982 edge
- edges
, edge
->pos
/ 64.0, edge
[1].pos
/ 64.0));
2987 edge
->pos
= edge
[1].pos
;
2989 if (hints
->recorder
)
2990 hints
->recorder(ta_bound
, hints
, dim
,
2991 edge
, &edge
[1], NULL
, NULL
, NULL
);
2998 TA_LOG((" (none)\n"));
3004 /* apply the complete hinting algorithm to a latin glyph */
3007 ta_latin_hints_apply(FT_UInt glyph_index
,
3008 TA_GlyphHints hints
,
3009 FT_Outline
* outline
,
3010 TA_LatinMetrics metrics
)
3018 error
= ta_glyph_hints_reload(hints
, outline
);
3022 /* analyze glyph outline */
3023 #ifdef TA_CONFIG_OPTION_USE_WARPER
3024 if ((metrics
->root
.scaler
.render_mode
== FT_RENDER_MODE_LIGHT
3025 && TA_HINTS_DO_WARP(hints
))
3026 || TA_HINTS_DO_HORIZONTAL(hints
))
3028 if (TA_HINTS_DO_HORIZONTAL(hints
))
3031 axis
= &metrics
->axis
[TA_DIMENSION_HORZ
];
3032 error
= ta_latin_hints_detect_features(hints
,
3040 if (TA_HINTS_DO_VERTICAL(hints
))
3042 axis
= &metrics
->axis
[TA_DIMENSION_VERT
];
3043 error
= ta_latin_hints_detect_features(hints
,
3050 /* apply blue zones to base characters only */
3051 if (!(metrics
->root
.globals
->glyph_styles
[glyph_index
] & TA_NOBASE
))
3052 ta_latin_hints_compute_blue_edges(hints
, metrics
);
3055 /* grid-fit the outline */
3056 for (dim
= 0; dim
< TA_DIMENSION_MAX
; dim
++)
3058 #ifdef TA_CONFIG_OPTION_USE_WARPER
3059 if (dim
== TA_DIMENSION_HORZ
3060 && metrics
->root
.scaler
.render_mode
== FT_RENDER_MODE_LIGHT
3061 && TA_HINTS_DO_WARP(hints
))
3063 TA_WarperRec warper
;
3068 ta_warper_compute(&warper
, hints
, (TA_Dimension
)dim
, &scale
, &delta
);
3069 ta_glyph_hints_scale_dim(hints
, (TA_Dimension
)dim
, scale
, delta
);
3073 #endif /* TA_CONFIG_OPTION_USE_WARPER */
3075 if ((dim
== TA_DIMENSION_HORZ
&& TA_HINTS_DO_HORIZONTAL(hints
))
3076 || (dim
== TA_DIMENSION_VERT
&& TA_HINTS_DO_VERTICAL(hints
)))
3078 ta_latin_hint_edges(hints
, (TA_Dimension
)dim
);
3079 ta_glyph_hints_align_edge_points(hints
, (TA_Dimension
)dim
);
3080 ta_glyph_hints_align_strong_points(hints
, (TA_Dimension
)dim
);
3081 ta_glyph_hints_align_weak_points(hints
, (TA_Dimension
)dim
);
3085 ta_glyph_hints_save(hints
, outline
);
3092 const TA_WritingSystemClassRec ta_latin_writing_system_class
=
3094 TA_WRITING_SYSTEM_LATIN
,
3096 sizeof (TA_LatinMetricsRec
),
3098 (TA_WritingSystem_InitMetricsFunc
)ta_latin_metrics_init
,
3099 (TA_WritingSystem_ScaleMetricsFunc
)ta_latin_metrics_scale
,
3100 (TA_WritingSystem_DoneMetricsFunc
)NULL
,
3102 (TA_WritingSystem_InitHintsFunc
)ta_latin_hints_init
,
3103 (TA_WritingSystem_ApplyHintsFunc
)ta_latin_hints_apply
3106 /* end of talatin.c */