Synchronize with FreeType.
[ttfautohint.git] / lib / talatin.c
blob35cc7bdad2dbdb64d981323bfab60a47fa32a09a
1 /* talatin.c */
3 /*
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> */
20 #include <string.h>
22 #include <ft2build.h>
23 #include FT_ADVANCES_H
24 #include FT_TRUETYPE_TABLES_H
26 #include "taglobal.h"
27 #include "talatin.h"
28 #include "tasort.h"
31 #ifdef TA_CONFIG_OPTION_USE_WARPER
32 #include "tawarp.h"
33 #endif
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 */
45 void
46 ta_latin_metrics_init_widths(TA_LatinMetrics metrics,
47 FT_Face face,
48 FT_Bool use_cmap)
50 /* scan the array of segments in each direction */
51 TA_GlyphHintsRec hints[1];
54 TA_LOG_GLOBAL(("\n"
55 "latin standard widths computation (style `%s')\n"
56 "=====================================================\n"
57 "\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;
66 FT_Error error;
67 FT_ULong glyph_index;
68 FT_Long y_offset;
69 int dim;
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;
79 if (!use_cmap)
80 goto Exit;
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
86 * numerals.
88 standard_char = script_class->standard_char1;
89 ta_get_char_index(&metrics->root,
90 standard_char,
91 &glyph_index,
92 &y_offset);
93 if (!glyph_index)
95 if (script_class->standard_char2)
97 standard_char = script_class->standard_char2;
98 ta_get_char_index(&metrics->root,
99 standard_char,
100 &glyph_index,
101 &y_offset);
102 if (!glyph_index)
104 if (script_class->standard_char3)
106 standard_char = script_class->standard_char3;
107 ta_get_char_index(&metrics->root,
108 standard_char,
109 &glyph_index,
110 &y_offset);
111 if (!glyph_index)
112 goto Exit;
114 else
115 goto Exit;
118 else
119 goto Exit;
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)
127 goto Exit;
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;
135 scaler->x_delta = 0;
136 scaler->y_delta = 0;
138 scaler->face = face;
139 scaler->render_mode = FT_RENDER_MODE_NORMAL;
140 scaler->flags = 0;
142 ta_glyph_hints_rescale(hints, (TA_StyleMetrics)dummy);
144 error = ta_glyph_hints_reload(hints, &face->glyph->outline);
145 if (error)
146 goto Exit;
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);
158 if (error)
159 goto Exit;
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
165 * function.
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++)
174 link = seg->link;
176 /* we only consider stem segments there! */
177 if (link
178 && link->link == seg
179 && link > seg)
181 FT_Pos dist;
184 dist = seg->pos - link->pos;
185 if (dist < 0)
186 dist = -dist;
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;
200 Exit:
201 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
203 FONT* font = metrics->root.globals->font;
204 TA_LatinAxis axis = &metrics->axis[dim];
205 FT_Pos stdw;
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)
220 axis->width_count++;
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;
232 #ifdef TA_DEBUG
234 FT_UInt i;
237 TA_LOG_GLOBAL(("%s widths:\n",
238 dim == TA_DIMENSION_VERT ? "horizontal"
239 : "vertical"));
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"));
247 #endif
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 */
260 static void
261 ta_latin_metrics_init_blues(TA_LatinMetrics metrics,
262 FT_Face face)
264 FT_Pos flats[TA_BLUE_STRING_MAX_LEN];
265 FT_Pos rounds[TA_BLUE_STRING_MAX_LEN];
266 FT_UInt num_flats;
267 FT_UInt num_rounds;
269 TA_LatinBlue blue;
270 FT_Error error;
271 TA_LatinAxis axis = &metrics->axis[TA_DIMENSION_VERT];
272 FT_Outline outline;
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"
287 "\n"));
289 for (; bs->string != TA_BLUE_STRING_MAX; bs++)
291 const char* p = &ta_blue_strings[bs->string];
292 FT_Pos* blue_ref;
293 FT_Pos* blue_shoot;
296 #ifdef TA_DEBUG
298 FT_Bool have_flag = 0;
301 TA_LOG_GLOBAL(("blue zone %d", axis->blue_count));
303 if (bs->properties)
305 TA_LOG_GLOBAL((" ("));
307 if (TA_LATIN_IS_TOP_BLUE(bs))
309 TA_LOG_GLOBAL(("top"));
310 have_flag = 1;
313 if (TA_LATIN_IS_NEUTRAL_BLUE(bs))
315 if (have_flag)
316 TA_LOG_GLOBAL((", "));
317 TA_LOG_GLOBAL(("neutral"));
318 have_flag = 1;
321 if (TA_LATIN_IS_X_HEIGHT_BLUE(bs))
323 if (have_flag)
324 TA_LOG_GLOBAL((", "));
325 TA_LOG_GLOBAL(("small top"));
326 have_flag = 1;
329 if (TA_LATIN_IS_LONG_BLUE(bs))
331 if (have_flag)
332 TA_LOG_GLOBAL((", "));
333 TA_LOG_GLOBAL(("long"));
336 TA_LOG_GLOBAL((")"));
339 TA_LOG_GLOBAL((":\n"));
341 #endif /* TA_DEBUG */
343 num_flats = 0;
344 num_rounds = 0;
346 while (*p)
348 FT_ULong ch;
349 FT_ULong glyph_index;
350 FT_Long y_offset;
351 FT_Pos best_y; /* same as points.y */
352 FT_Int best_point, best_contour_first, best_contour_last;
353 FT_Vector* points;
354 FT_Bool round = 0;
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));
364 continue;
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));
373 continue;
376 /* now compute min or max point indices and coordinates */
377 points = outline.points;
378 best_point = -1;
379 best_y = 0; /* make compiler happy */
380 best_contour_first = 0; /* ditto */
381 best_contour_last = 0; /* ditto */
384 FT_Int nn;
385 FT_Int first = 0;
386 FT_Int last = -1;
389 for (nn = 0; nn < outline.n_contours; first = last + 1, nn++)
391 FT_Int old_best_point = best_point;
392 FT_Int pp;
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 */
400 if (last <= first)
401 continue;
403 if (TA_LATIN_IS_TOP_BLUE(bs))
405 for (pp = first; pp <= last; pp++)
406 if (best_point < 0
407 || points[pp].y > best_y)
409 best_point = pp;
410 best_y = points[pp].y;
413 else
415 for (pp = first; pp <= last; pp++)
416 if (best_point < 0
417 || points[pp].y < best_y)
419 best_point = pp;
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 */
435 if (best_point >= 0)
437 FT_Pos best_x = points[best_point].x;
438 FT_Int prev, next;
439 FT_Int best_segment_first, best_segment_last;
440 FT_Int best_on_point_first, best_on_point_last;
441 FT_Pos dist;
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;
452 else
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'... */
461 prev = best_point;
462 next = prev;
466 if (prev > best_contour_first)
467 prev--;
468 else
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) */
474 if (dist > 5)
475 if (TA_ABS(points[prev].x - best_x) <= 20 * dist)
476 break;
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)
492 next++;
493 else
494 next = best_contour_first;
496 dist = TA_ABS(points[next].y - best_y);
497 if (dist > 5)
498 if (TA_ABS(points[next].x - best_x) <= 20 * dist)
499 break;
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;
542 FT_Int first;
543 FT_Int last;
544 FT_Bool hit;
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 */
551 FT_Int p_last = 0;
553 FT_Bool left2right;
556 /* compute direction */
557 prev = best_point;
561 if (prev > best_contour_first)
562 prev--;
563 else
564 prev = best_contour_last;
566 if (points[prev].x != best_x)
567 break;
568 } while (prev != best_point);
570 /* skip glyph for the degenerate case */
571 if (prev == best_point)
572 continue;
574 left2right = FT_BOOL(points[prev].x < points[best_point].x);
576 first = best_segment_last;
577 last = first;
578 hit = 0;
582 FT_Bool l2r;
583 FT_Pos d;
586 if (!hit)
588 /* no hit; adjust first point */
589 first = last;
591 /* also adjust first and last on point */
592 if (FT_CURVE_TAG(outline.tags[first]) == FT_CURVE_TAG_ON)
594 p_first = first;
595 p_last = first;
597 else
599 p_first = -1;
600 p_last = -1;
603 hit = 1;
606 if (last < best_contour_last)
607 last++;
608 else
609 last = best_contour_first;
611 if (TA_ABS(best_y - points[first].y) > height_threshold)
613 /* vertical distance too large */
614 hit = 0;
615 continue;
618 /* same test as above */
619 dist = TA_ABS(points[last].y - points[first].y);
620 if (dist > 5)
621 if (TA_ABS(points[last].x - points[first].x) <= 20 * dist)
623 hit = 0;
624 continue;
627 if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON)
629 p_last = last;
630 if (p_first < 0)
631 p_first = last;
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 */
641 /* its end */
644 if (last < best_contour_last)
645 last++;
646 else
647 last = best_contour_first;
649 d = TA_ABS(points[last].y - points[first].y);
650 if (d > 5)
651 if (TA_ABS(points[next].x - points[first].x) <=
652 20 * dist)
654 if (last > best_contour_first)
655 last--;
656 else
657 last = best_contour_last;
658 break;
661 p_last = last;
663 if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON)
665 p_last = last;
666 if (p_first < 0)
667 p_first = last;
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;
679 break;
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
691 best_y += y_offset;
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
703 * flat
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))
709 > flat_threshold)
710 round = 0;
711 else
712 round = FT_BOOL(FT_CURVE_TAG(outline.tags[best_segment_first])
713 != FT_CURVE_TAG_ON
714 || FT_CURVE_TAG(outline.tags[best_segment_last])
715 != FT_CURVE_TAG_ON);
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"));
721 continue;
724 TA_LOG_GLOBAL((" (%s)\n", round ? "round" : "flat"));
727 if (round)
728 rounds[num_rounds++] = best_y;
729 else
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"));
738 continue;
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;
751 axis->blue_count++;
753 if (num_flats == 0)
755 *blue_ref =
756 *blue_shoot = rounds[num_rounds / 2];
758 else if (num_rounds == 0)
760 *blue_ref =
761 *blue_shoot = flats[num_flats / 2];
763 else
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)
781 *blue_ref =
782 *blue_shoot = (shoot + ref) / 2;
784 TA_LOG_GLOBAL((" [overshoot smaller than reference,"
785 " taking mean value]\n"));
789 blue->flags = 0;
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 */
810 TT_OS2* os2;
813 os2 = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
815 if (os2)
817 blue = &axis->blues[axis->blue_count];
818 blue->flags = TA_LATIN_BLUE_TOP | TA_LATIN_BLUE_ACTIVE;
819 blue->ref.org =
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;
829 blue->ref.org =
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));
837 else
839 blue = &axis->blues[axis->blue_count];
840 blue->flags =
841 blue->ref.org =
842 blue->shoot.org = 0;
844 blue = &axis->blues[axis->blue_count + 1];
845 blue->flags =
846 blue->ref.org =
847 blue->shoot.org = 0;
851 TA_LOG_GLOBAL(("\n"));
853 return;
857 /* check whether all ASCII digits have the same advance width */
859 void
860 ta_latin_metrics_check_digits(TA_LatinMetrics metrics,
861 FT_Face face)
863 FT_UInt i;
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;
872 FT_Long y_offset;
875 ta_get_char_index(&metrics->root, i, &glyph_index, &y_offset);
876 if (glyph_index == 0)
877 continue;
879 if (FT_Get_Advance(face, glyph_index,
880 FT_LOAD_NO_SCALE
881 | FT_LOAD_NO_HINTING
882 | FT_LOAD_IGNORE_TRANSFORM,
883 &advance))
884 continue;
886 if (started)
888 if (advance != old_advance)
890 same_width = 0;
891 break;
894 else
896 old_advance = advance;
897 started = 1;
901 metrics->root.digits_have_same_width = same_width;
905 /* initialize global metrics */
907 FT_Error
908 ta_latin_metrics_init(TA_LatinMetrics metrics,
909 FT_Face face)
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);
922 else
924 /* we only have a symbol font encoding */
925 ta_latin_metrics_init_widths(metrics, face, 0);
928 FT_Set_Charmap(face, oldmap);
929 return FT_Err_Ok;
933 /* adjust scaling value, then scale and shift widths */
934 /* and blue zones (if applicable) for given dimension */
936 static void
937 ta_latin_metrics_scale_dim(TA_LatinMetrics metrics,
938 TA_Scaler scaler,
939 TA_Dimension dim)
941 FT_Fixed scale;
942 FT_Pos delta;
943 TA_LatinAxis axis;
944 FT_UInt ppem;
945 FT_UInt nn;
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;
955 else
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)
964 return;
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,
974 (int)ppem))
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];
985 break;
989 if (blue)
991 FT_Pos scaled;
992 FT_Pos threshold;
993 FT_Pos fitted;
994 FT_UInt limit;
997 scaled = FT_MulFix(blue->shoot.org, scaler->y_scale);
998 limit = metrics->root.globals->increase_x_height;
999 threshold = 40;
1001 /* if the `increase-x-height' property is active, */
1002 /* we round up much more often */
1003 if (limit
1004 && ppem <= limit
1005 && ppem >= TA_PROP_INCREASE_X_HEIGHT_MIN)
1006 threshold = 52;
1008 fitted = (scaled + threshold) & ~63;
1010 if (scaled != fitted)
1012 if (dim == TA_DIMENSION_VERT)
1014 scale = FT_MulDiv(scale, fitted, scaled);
1016 TA_LOG_GLOBAL((
1017 "ta_latin_metrics_scale_dim:"
1018 " x height alignment (style `%s'):\n"
1020 " vertical scaling changed from %.4f to %.4f (by %d%%)\n"
1021 "\n",
1022 ta_style_names[metrics->root.style_class->style],
1023 axis->org_scale / 65536.0,
1024 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;
1039 else
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",
1059 width->org,
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 */
1067 axis->extra_light =
1068 (FT_Bool)(FT_MulFix(axis->standard_width, scale) < 32 + 8);
1070 #ifdef TA_DEBUG
1071 if (axis->extra_light)
1072 TA_LOG_GLOBAL(("`%s' style is extra light (at current resolution)\n"
1073 "\n",
1074 ta_style_names[metrics->root.style_class->style]));
1075 #endif
1077 if (dim == TA_DIMENSION_VERT)
1079 #ifdef TA_DEBUG
1080 if (axis->blue_count)
1081 TA_LOG_GLOBAL(("blue zones (style `%s')\n",
1082 ta_style_names[metrics->root.style_class->style]));
1083 #endif
1085 /* scale the blue zones */
1086 for (nn = 0; nn < axis->blue_count; nn++)
1088 TA_LatinBlue blue = &axis->blues[nn];
1089 FT_Pos dist;
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)
1102 #if 0
1103 FT_Pos delta1;
1104 #endif
1105 FT_Pos delta2;
1108 /* use discrete values for blue zone widths */
1110 #if 0
1111 /* generic, original code */
1112 delta1 = blue->shoot.org - blue->ref.org;
1113 delta2 = delta1;
1114 if (delta1 < 0)
1115 delta2 = -delta2;
1117 delta2 = FT_MulFix(delta2, scale);
1119 if (delta2 < 32)
1120 delta2 = 0;
1121 else if (delta2 < 64)
1122 delta2 = 32 + (((delta2 - 32) + 16) & ~31);
1123 else
1124 delta2 = TA_PIX_ROUND(delta2);
1126 if (delta1 < 0)
1127 delta2 = -delta2;
1129 blue->ref.fit = TA_PIX_ROUND(blue->ref.cur);
1130 blue->shoot.fit = blue->ref.fit + delta2;
1131 #else
1132 /* simplified version due to abs(dist) <= 48 */
1133 delta2 = dist;
1134 if (dist < 0)
1135 delta2 = -delta2;
1137 if (delta2 < 32)
1138 delta2 = 0;
1139 else if (delta2 < 48)
1140 delta2 = 32;
1141 else
1142 delta2 = 64;
1144 if (dist < 0)
1145 delta2 = -delta2;
1147 blue->ref.fit = TA_PIX_ROUND(blue->ref.cur);
1148 blue->shoot.fit = blue->ref.fit - delta2;
1149 #endif
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",
1156 blue->ref.org,
1157 blue->ref.fit / 64.0,
1158 blue->flags & TA_LATIN_BLUE_ACTIVE ? ""
1159 : " (inactive)",
1161 blue->shoot.org,
1162 blue->shoot.fit / 64.0,
1163 blue->flags & TA_LATIN_BLUE_ACTIVE ? ""
1164 : " (inactive)"));
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];
1172 TA_LatinBlue b;
1175 b = &a->blues[a->blue_count];
1176 b->ref.cur =
1177 b->ref.fit =
1178 b->shoot.cur =
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",
1183 a->blue_count,
1184 b->ref.org,
1185 b->ref.fit / 64.0,
1186 a->blue_count,
1187 b->shoot.org,
1188 b->shoot.fit / 64.0));
1190 b = &a->blues[a->blue_count + 1];
1191 b->ref.cur =
1192 b->ref.fit =
1193 b->shoot.cur =
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",
1198 a->blue_count + 1,
1199 b->ref.org,
1200 b->ref.fit / 64.0,
1201 a->blue_count + 1,
1202 b->shoot.org,
1203 b->shoot.fit / 64.0));
1206 TA_LOG_GLOBAL(("\n"));
1211 /* scale global values in both directions */
1213 void
1214 ta_latin_metrics_scale(TA_LatinMetrics metrics,
1215 TA_Scaler scaler)
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 */
1228 FT_Error
1229 ta_latin_hints_compute_segments(TA_GlyphHints hints,
1230 TA_Dimension dim)
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;
1237 TA_SegmentRec seg0;
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));
1247 seg0.score = 32000;
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;
1268 else
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;
1287 int on_edge = 0;
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;
1293 FT_Bool passed;
1296 if (point == last) /* skip singletons -- just in case */
1297 continue;
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 */
1303 last = point;
1305 for (;;)
1307 point = point->prev;
1308 if (TA_ABS(point->out_dir) != major_dir)
1310 point = point->next;
1311 break;
1313 if (point == last)
1314 break;
1318 last = point;
1319 passed = 0;
1321 for (;;)
1323 FT_Pos u, v;
1326 if (on_edge)
1328 u = point->u;
1329 if (u < min_pos)
1330 min_pos = u;
1331 if (u > max_pos)
1332 max_pos = u;
1334 /* get minimum and maximum coordinate of on points */
1335 if (!(point->flags & TA_FLAG_CONTROL))
1337 v = point->v;
1338 if (v < min_on_pos)
1339 min_on_pos = v;
1340 if (v > max_on_pos)
1341 max_on_pos = v;
1344 if (point->out_dir != segment_dir
1345 || point == last)
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;
1362 if (v < min_pos)
1363 min_pos = v;
1364 if (v > max_pos)
1365 max_pos = 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);
1372 on_edge = 0;
1373 segment = NULL;
1374 /* fall through */
1378 /* now exit if we are at the start/end point */
1379 if (point == last)
1381 if (passed)
1382 break;
1383 passed = 1;
1386 if (!on_edge
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);
1393 if (error)
1394 goto Exit;
1396 /* clear all segment fields */
1397 segment[0] = seg0;
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)
1407 min_on_pos = 32000;
1408 max_on_pos = -32000;
1410 else
1411 min_on_pos = max_on_pos = point->v;
1413 on_edge = 1;
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;
1428 on_edge = 0;
1429 segment = NULL;
1433 point = point->next;
1435 } /* contours */
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)
1456 TA_Point p;
1459 p = first->prev;
1460 if (p->v < first_v)
1461 segment->height = (FT_Short)(segment->height +
1462 ((first_v - p->v) >> 1));
1464 p = last->next;
1465 if (p->v > last_v)
1466 segment->height = (FT_Short)(segment->height +
1467 ((p->v - last_v) >> 1));
1469 else
1471 TA_Point p;
1474 p = first->prev;
1475 if (p->v > first_v)
1476 segment->height = (FT_Short)(segment->height +
1477 ((p->v - first_v) >> 1));
1479 p = last->next;
1480 if (p->v < last_v)
1481 segment->height = (FT_Short)(segment->height +
1482 ((last_v - p->v) >> 1));
1487 Exit:
1488 return error;
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 */
1495 void
1496 ta_latin_hints_link_segments(TA_GlyphHints hints,
1497 FT_UInt width_count,
1498 TA_WidthRec* widths,
1499 TA_Dimension dim)
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;
1510 if (width_count)
1511 max_width = widths[width_count - 1].org;
1512 else
1513 max_width = 0;
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)
1518 len_threshold = 1;
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) */
1526 dist_score = 3000;
1528 /* now compare each segment to the others */
1529 for (seg1 = segments; seg1 < segment_limit; seg1++)
1531 if (seg1->dir != axis->major_dir)
1532 continue;
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
1543 && pos2 > pos1)
1545 /* compute distance between the two segments */
1546 FT_Pos min = seg1->min_coord;
1547 FT_Pos max = seg1->max_coord;
1548 FT_Pos len;
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) */
1558 len = max - min;
1560 /* for one-point segments, `len' is zero if there is an overlap */
1561 /* (and negative otherwise); we have to correct this */
1562 if (len == 0
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;
1588 if (max_width)
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);
1595 if (delta > 10000)
1596 dist_demerit = 32000;
1597 else if (delta > 0)
1598 dist_demerit = delta * delta / dist_score;
1599 else
1600 dist_demerit = 0;
1602 else
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;
1611 seg1->link = seg2;
1614 if (score < seg2->score)
1616 seg2->score = score;
1617 seg2->link = seg1;
1624 /* now compute the `serif' segments, cf. explanations in `tahints.h' */
1625 for (seg1 = segments; seg1 < segment_limit; seg1++)
1627 seg2 = seg1->link;
1629 if (seg2)
1631 if (seg2->link != seg1)
1633 seg1->link = 0;
1634 seg1->serif = seg2->link;
1641 /* link segments to edges, using feature analysis for selection */
1643 FT_Error
1644 ta_latin_hints_compute_edges(TA_GlyphHints hints,
1645 TA_Dimension dim)
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;
1653 TA_Segment seg;
1655 #if 0
1656 TA_Direction up_dir;
1657 #endif
1658 FT_Fixed scale;
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
1666 : hints->y_scale;
1668 #if 0
1669 up_dir = (dim == TA_DIMENSION_HORZ) ? TA_DIR_UP
1670 : TA_DIR_RIGHT;
1671 #endif
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);
1678 else
1679 segment_length_threshold = 0;
1681 /********************************************************************/
1682 /* */
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. */
1686 /* */
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. */
1691 /* */
1692 /* Note that the table of edges is sorted along the segment/edge */
1693 /* position. */
1694 /* */
1695 /********************************************************************/
1697 /* assure that edge distance threshold is at most 0.25px */
1698 edge_distance_threshold = FT_MulFix(laxis->edge_distance_threshold,
1699 scale);
1700 if (edge_distance_threshold > 64 / 4)
1701 edge_distance_threshold = 64 / 4;
1703 edge_distance_threshold = FT_DivFix(edge_distance_threshold,
1704 scale);
1706 for (seg = segments; seg < segment_limit; seg++)
1708 TA_Edge found = NULL;
1709 FT_Int ee;
1712 if (seg->height < segment_length_threshold)
1713 continue;
1715 /* a special case for serif edges: */
1716 /* if they are smaller than 1.5 pixels we ignore them */
1717 if (seg->serif
1718 && 2 * seg->height < 3 * segment_length_threshold)
1719 continue;
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;
1725 FT_Pos dist;
1728 dist = seg->pos - edge->fpos;
1729 if (dist < 0)
1730 dist = -dist;
1732 if (dist < edge_distance_threshold && edge->dir == seg->dir)
1734 found = edge;
1735 break;
1739 if (!found)
1741 TA_Edge edge;
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,
1747 &edge);
1748 if (error)
1749 goto Exit;
1751 /* add the segment to the new edge's list */
1752 memset(edge, 0, sizeof (TA_EdgeRec));
1753 edge->first = seg;
1754 edge->last = seg;
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;
1761 else
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;
1766 found->last = seg;
1770 /*****************************************************************/
1771 /* */
1772 /* Good, we now compute each edge's properties according to */
1773 /* the segments found on its position. Basically, these are */
1774 /* */
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 */
1779 /* */
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;
1791 TA_Edge edge;
1794 for (edge = edges; edge < edge_limit; edge++)
1796 seg = edge->first;
1797 if (seg)
1800 seg->edge = 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? */
1810 #if 0
1811 FT_Pos ups = 0; /* number of upwards segments */
1812 FT_Pos downs = 0; /* number of downwards segments */
1813 #endif
1816 seg = edge->first;
1820 FT_Bool is_serif;
1823 /* check for roundness of segment */
1824 if (seg->flags & TA_EDGE_ROUND)
1825 is_round++;
1826 else
1827 is_straight++;
1829 #if 0
1830 /* check for segment direction */
1831 if (seg->dir == up_dir)
1832 ups += seg->max_coord - seg->min_coord;
1833 else
1834 downs += seg->max_coord - seg->min_coord;
1835 #endif
1837 /* check for links -- */
1838 /* if seg->serif is set, then seg->link must be ignored */
1839 is_serif = (FT_Bool)(seg->serif
1840 && seg->serif->edge
1841 && seg->serif->edge != edge);
1843 if ((seg->link && seg->link->edge != NULL)
1844 || is_serif)
1846 TA_Edge edge2;
1847 TA_Segment seg2;
1850 edge2 = edge->link;
1851 seg2 = seg->link;
1853 if (is_serif)
1855 seg2 = seg->serif;
1856 edge2 = edge->serif;
1859 if (edge2)
1861 FT_Pos edge_delta;
1862 FT_Pos seg_delta;
1865 edge_delta = edge->fpos - edge2->fpos;
1866 if (edge_delta < 0)
1867 edge_delta = -edge_delta;
1869 seg_delta = seg->pos - seg2->pos;
1870 if (seg_delta < 0)
1871 seg_delta = -seg_delta;
1873 if (seg_delta < edge_delta)
1874 edge2 = seg2->edge;
1876 else
1877 edge2 = seg2->edge;
1879 if (is_serif)
1881 edge->serif = edge2;
1882 edge2->flags |= TA_EDGE_SERIF;
1884 else
1885 edge->link = edge2;
1888 seg = seg->edge_next;
1889 } while (seg != edge->first);
1891 /* set the round/straight flags */
1892 edge->flags = TA_EDGE_NORMAL;
1894 if (is_round > 0
1895 && is_round >= is_straight)
1896 edge->flags |= TA_EDGE_ROUND;
1898 #if 0
1899 /* set the edge's main direction */
1900 edge->dir = TA_DIR_NONE;
1902 if (ups > downs)
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! */
1910 #endif
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)
1917 edge->serif = NULL;
1921 Exit:
1922 return error;
1926 /* detect segments and edges for given dimension */
1928 FT_Error
1929 ta_latin_hints_detect_features(TA_GlyphHints hints,
1930 FT_UInt width_count,
1931 TA_WidthRec* widths,
1932 TA_Dimension dim)
1934 FT_Error error;
1937 error = ta_latin_hints_compute_segments(hints, dim);
1938 if (!error)
1940 ta_latin_hints_link_segments(hints, width_count, widths, dim);
1942 error = ta_latin_hints_compute_edges(hints, dim);
1945 return error;
1949 /* compute all edges which lie within blue zones */
1951 static void
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++)
1970 FT_UInt bb;
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)
1985 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 */
1990 for (bb = 0;
1991 bb < latin->blue_count
1992 + (metrics->root.globals->font->windows_compatibility ? 2 : 0);
1993 bb++)
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))
2001 continue;
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)
2013 FT_Pos dist;
2016 /* first of all, compare it to the reference position */
2017 dist = edge->fpos - blue->ref.org;
2018 if (dist < 0)
2019 dist = -dist;
2021 dist = FT_MulFix(dist, scale);
2022 if (dist < best_dist)
2024 best_dist = dist;
2025 best_blue = &blue->ref;
2026 best_blue_is_neutral = is_neutral_blue;
2028 best_blue_idx = bb;
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
2038 && dist != 0
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;
2047 if (dist < 0)
2048 dist = -dist;
2050 dist = FT_MulFix(dist, scale);
2051 if (dist < best_dist)
2053 best_dist = dist;
2054 best_blue = &blue->shoot;
2055 best_blue_is_neutral = is_neutral_blue;
2057 best_blue_idx = bb;
2058 best_blue_is_shoot = 1;
2065 if (best_blue)
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 */
2079 static FT_Error
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;
2105 #endif
2107 scaler_flags = hints->scaler_flags;
2108 other_flags = 0;
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;
2141 #endif
2143 hints->scaler_flags = scaler_flags;
2144 hints->other_flags = other_flags;
2146 return FT_Err_Ok;
2150 /* snap a given width in scaled coordinates to */
2151 /* one of the current standard widths */
2153 static FT_Pos
2154 ta_latin_snap_width(TA_Width widths,
2155 FT_UInt count,
2156 FT_Pos width)
2158 FT_UInt n;
2159 FT_Pos best = 64 + 32 + 2;
2160 FT_Pos reference = width;
2161 FT_Pos scaled;
2164 for (n = 0; n < count; n++)
2166 FT_Pos w;
2167 FT_Pos dist;
2170 w = widths[n].cur;
2171 dist = width - w;
2172 if (dist < 0)
2173 dist = -dist;
2174 if (dist < best)
2176 best = dist;
2177 reference = w;
2181 scaled = TA_PIX_ROUND(reference);
2183 if (width >= reference)
2185 if (width < scaled + 48)
2186 width = reference;
2188 else
2190 if (width > scaled - 48)
2191 width = reference;
2194 return width;
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 */
2203 static FT_Pos
2204 ta_latin_compute_stem_width(TA_GlyphHints hints,
2205 TA_Dimension dim,
2206 FT_Pos width,
2207 FT_Byte base_flags,
2208 FT_Byte stem_flags)
2210 TA_LatinMetrics metrics = (TA_LatinMetrics) hints->metrics;
2211 TA_LatinAxis axis = &metrics->axis[dim];
2213 FT_Pos dist = width;
2214 FT_Int sign = 0;
2215 FT_Int vertical = (dim == TA_DIMENSION_VERT);
2218 if (!TA_LATIN_HINTS_DO_STEM_ADJUST(hints)
2219 || axis->extra_light)
2220 return width;
2222 if (dist < 0)
2224 dist = -width;
2225 sign = 1;
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)
2235 && vertical
2236 && (dist < 3 * 64))
2237 goto Done_Width;
2238 else if (base_flags & TA_EDGE_ROUND)
2240 if (dist < 80)
2241 dist = 64;
2243 else if (dist < 56)
2244 dist = 56;
2246 if (axis->width_count > 0)
2248 FT_Pos delta;
2251 /* compare to standard width */
2252 delta = dist - axis->widths[0].cur;
2254 if (delta < 0)
2255 delta = -delta;
2257 if (delta < 40)
2259 dist = axis->widths[0].cur;
2260 if (dist < 48)
2261 dist = 48;
2263 goto Done_Width;
2266 if (dist < 3 * 64)
2268 delta = dist & 63;
2269 dist &= -64;
2271 if (delta < 10)
2272 dist += delta;
2273 else if (delta < 32)
2274 dist += 10;
2275 else if (delta < 54)
2276 dist += 54;
2277 else
2278 dist += delta;
2280 else
2281 dist = (dist + 32) & ~63;
2284 else
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);
2293 if (vertical)
2295 /* in the case of vertical hinting, */
2296 /* always round the stem heights to integer pixels */
2298 if (dist >= 64)
2299 dist = (dist + 16) & ~63;
2300 else
2301 dist = 64;
2303 else
2305 if (TA_LATIN_HINTS_DO_MONO(hints))
2307 /* monochrome horizontal hinting: */
2308 /* snap widths to integer pixels with a different threshold */
2310 if (dist < 64)
2311 dist = 64;
2312 else
2313 dist = (dist + 32) & ~63;
2315 else
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 */
2321 if (dist < 48)
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 */
2332 FT_Pos delta;
2335 dist = (dist + 22) & ~63;
2336 delta = dist - org_dist;
2337 if (delta < 0)
2338 delta = -delta;
2340 if (delta >= 16)
2342 dist = org_dist;
2343 if (dist < 48)
2344 dist = (dist + 64) >> 1;
2347 else
2348 /* round otherwise to prevent color fringes in LCD mode */
2349 dist = (dist + 32) & ~63;
2354 Done_Width:
2355 if (sign)
2356 dist = -dist;
2358 return dist;
2362 /* align one stem edge relative to the previous stem edge */
2364 static void
2365 ta_latin_align_linked_edge(TA_GlyphHints hints,
2366 TA_Dimension dim,
2367 TA_Edge base_edge,
2368 TA_Edge stem_edge)
2370 FT_Pos dist = stem_edge->opos - base_edge->opos;
2372 FT_Pos fitted_width = ta_latin_compute_stem_width(
2373 hints, dim, dist,
2374 base_edge->flags,
2375 stem_edge->flags);
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 */
2394 static void
2395 ta_latin_align_serif_edge(TA_GlyphHints hints,
2396 TA_Edge base,
2397 TA_Edge serif)
2399 FT_UNUSED(hints);
2401 serif->pos = base->pos + (serif->opos - base->opos);
2405 /* the main grid-fitting routine */
2407 static void
2408 ta_latin_hint_edges(TA_GlyphHints hints,
2409 TA_Dimension dim)
2411 TA_AxisHints axis = &hints->axis[dim];
2413 TA_Edge edges = axis->edges;
2414 TA_Edge edge_limit = edges + axis->num_edges;
2415 FT_PtrDist n_edges;
2416 TA_Edge edge;
2418 TA_Edge anchor = NULL;
2419 FT_Int has_serifs = 0;
2421 #ifdef TA_DEBUG
2422 FT_UInt num_actions = 0;
2423 #endif
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++)
2437 TA_Width blue;
2438 TA_Edge edge1, edge2; /* these edges form the stem to check */
2441 if (edge->flags & TA_EDGE_DONE)
2442 continue;
2444 edge1 = NULL;
2445 edge2 = edge->link;
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
2451 * position.
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;
2461 if (neutral2)
2463 edge2->blue_edge = NULL;
2464 edge2->flags &= ~TA_EDGE_NEUTRAL;
2466 else if (neutral)
2468 edge->blue_edge = NULL;
2469 edge->flags &= ~TA_EDGE_NEUTRAL;
2473 blue = edge->blue_edge;
2474 if (blue)
2475 edge1 = 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;
2481 edge1 = edge2;
2482 edge2 = edge;
2485 if (!edge1)
2486 continue;
2488 #ifdef TA_DEBUG
2489 if (!anchor)
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));
2494 else
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));
2499 num_actions++;
2500 #endif
2502 edge1->pos = blue->fit;
2503 edge1->flags |= TA_EDGE_DONE;
2505 if (hints->recorder)
2507 if (!anchor)
2508 hints->recorder(ta_blue_anchor, hints, dim,
2509 edge1, edge, NULL, NULL, NULL);
2510 else
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;
2520 #ifdef TA_DEBUG
2521 num_actions++;
2522 #endif
2525 if (!anchor)
2526 anchor = edge;
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++)
2534 TA_Edge edge2;
2537 if (edge->flags & TA_EDGE_DONE)
2538 continue;
2540 /* skip all non-stem edges */
2541 edge2 = edge->link;
2542 if (!edge2)
2544 has_serifs++;
2545 continue;
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;
2558 #ifdef TA_DEBUG
2559 num_actions++;
2560 #endif
2561 continue;
2564 if (!anchor)
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 */
2579 if (cur_len <= 64)
2581 /* width <= 1px */
2582 u_off = 32;
2583 d_off = 32;
2585 else
2587 /* 1px < width < 1.5px */
2588 u_off = 38;
2589 d_off = 26;
2592 if (cur_len < 96)
2594 org_center = edge->opos + (org_len >> 1);
2595 cur_pos1 = TA_PIX_ROUND(org_center);
2597 error1 = org_center - (cur_pos1 - u_off);
2598 if (error1 < 0)
2599 error1 = -error1;
2601 error2 = org_center - (cur_pos1 + d_off);
2602 if (error2 < 0)
2603 error2 = -error2;
2605 if (error1 < error2)
2606 cur_pos1 -= u_off;
2607 else
2608 cur_pos1 += d_off;
2610 edge->pos = cur_pos1 - cur_len / 2;
2611 edge2->pos = edge->pos + cur_len;
2613 else
2614 edge->pos = TA_PIX_ROUND(edge->opos);
2616 anchor = edge;
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);
2631 #ifdef TA_DEBUG
2632 num_actions += 2;
2633 #endif
2635 else
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;
2661 if (edge > edges)
2662 bound = &edge[-1];
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);
2676 if (cur_len <= 64)
2678 u_off = 32;
2679 d_off = 32;
2681 else
2683 u_off = 38;
2684 d_off = 26;
2687 delta1 = org_center - (cur_pos1 - u_off);
2688 if (delta1 < 0)
2689 delta1 = -delta1;
2691 delta2 = org_center - (cur_pos1 + d_off);
2692 if (delta2 < 0)
2693 delta2 = -delta2;
2695 if (delta1 < delta2)
2696 cur_pos1 -= u_off;
2697 else
2698 cur_pos1 += d_off;
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;
2714 if (edge > edges)
2715 bound = &edge[-1];
2717 hints->recorder(ta_stem, hints, dim,
2718 edge, edge2, NULL, bound, NULL);
2722 else
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;
2733 if (delta1 < 0)
2734 delta1 = -delta1;
2736 cur_pos2 = TA_PIX_ROUND(org_pos + org_len) - cur_len;
2737 delta2 = cur_pos2 + (cur_len >> 1) - org_center;
2738 if (delta2 < 0)
2739 delta2 = -delta2;
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;
2755 if (edge > edges)
2756 bound = &edge[-1];
2758 hints->recorder(ta_stem, hints, dim,
2759 edge, edge2, NULL, bound, NULL);
2763 #ifdef TA_DEBUG
2764 num_actions++;
2765 #endif
2767 edge->flags |= TA_EDGE_DONE;
2768 edge2->flags |= TA_EDGE_DONE;
2770 if (edge > edges
2771 && edge->pos < edge[-1].pos)
2773 #ifdef TA_DEBUG
2774 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2775 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
2777 num_actions++;
2778 #endif
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;
2812 if (n_edges == 6)
2814 edge1 = edges;
2815 edge2 = edges + 2;
2816 edge3 = edges + 4;
2818 else
2820 edge1 = edges + 1;
2821 edge2 = edges + 5;
2822 edge3 = edges + 9;
2825 dist1 = edge2->opos - edge1->opos;
2826 dist2 = edge3->opos - edge2->opos;
2828 span = dist1 - dist2;
2829 if (span < 0)
2830 span = -span;
2832 if (span < 8)
2834 delta = edge3->pos - (2 * edge2->pos - edge1->pos);
2835 edge3->pos -= delta;
2836 if (edge3->link)
2837 edge3->link->pos -= delta;
2839 /* move the serifs along with the stem */
2840 if (n_edges == 12)
2842 (edges + 8)->pos -= delta;
2843 (edges + 11)->pos -= delta;
2846 edge3->flags |= TA_EDGE_DONE;
2847 if (edge3->link)
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;
2861 FT_Pos delta;
2864 if (edge->flags & TA_EDGE_DONE)
2865 continue;
2867 delta = 1000;
2869 if (edge->serif)
2871 delta = edge->serif->opos - edge->opos;
2872 if (delta < 0)
2873 delta = -delta;
2876 if (edge > edges)
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,
2892 edge->pos / 64.0));
2894 if (hints->recorder)
2895 hints->recorder(ta_serif, hints, dim,
2896 edge, NULL, NULL, lower_bound, upper_bound);
2898 else if (!anchor)
2900 edge->pos = TA_PIX_ROUND(edge->opos);
2901 anchor = edge;
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);
2910 else
2912 TA_Edge before, after;
2915 for (before = edge - 1; before >= edges; before--)
2916 if (before->flags & TA_EDGE_DONE)
2917 break;
2919 for (after = edge + 1; after < edge_limit; after++)
2920 if (after->flags & TA_EDGE_DONE)
2921 break;
2923 if (before >= edges && before < edge
2924 && after < edge_limit && after > edge)
2926 if (after->opos == before->opos)
2927 edge->pos = before->pos;
2928 else
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,
2936 edge->pos / 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);
2943 else
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);
2955 #ifdef TA_DEBUG
2956 num_actions++;
2957 #endif
2958 edge->flags |= TA_EDGE_DONE;
2960 if (edge > edges
2961 && edge->pos < edge[-1].pos)
2963 #ifdef TA_DEBUG
2964 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2965 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
2966 num_actions++;
2967 #endif
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)
2980 #ifdef TA_DEBUG
2981 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2982 edge - edges, edge->pos / 64.0, edge[1].pos / 64.0));
2984 num_actions++;
2985 #endif
2987 edge->pos = edge[1].pos;
2989 if (hints->recorder)
2990 hints->recorder(ta_bound, hints, dim,
2991 edge, &edge[1], NULL, NULL, NULL);
2996 #ifdef TA_DEBUG
2997 if (!num_actions)
2998 TA_LOG((" (none)\n"));
2999 TA_LOG(("\n"));
3000 #endif
3004 /* apply the complete hinting algorithm to a latin glyph */
3006 static FT_Error
3007 ta_latin_hints_apply(FT_UInt glyph_index,
3008 TA_GlyphHints hints,
3009 FT_Outline* outline,
3010 TA_LatinMetrics metrics)
3012 FT_Error error;
3013 int dim;
3015 TA_LatinAxis axis;
3018 error = ta_glyph_hints_reload(hints, outline);
3019 if (error)
3020 goto Exit;
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))
3027 #else
3028 if (TA_HINTS_DO_HORIZONTAL(hints))
3029 #endif
3031 axis = &metrics->axis[TA_DIMENSION_HORZ];
3032 error = ta_latin_hints_detect_features(hints,
3033 axis->width_count,
3034 axis->widths,
3035 TA_DIMENSION_HORZ);
3036 if (error)
3037 goto Exit;
3040 if (TA_HINTS_DO_VERTICAL(hints))
3042 axis = &metrics->axis[TA_DIMENSION_VERT];
3043 error = ta_latin_hints_detect_features(hints,
3044 axis->width_count,
3045 axis->widths,
3046 TA_DIMENSION_VERT);
3047 if (error)
3048 goto Exit;
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;
3064 FT_Fixed scale;
3065 FT_Pos delta;
3068 ta_warper_compute(&warper, hints, (TA_Dimension)dim, &scale, &delta);
3069 ta_glyph_hints_scale_dim(hints, (TA_Dimension)dim, scale, delta);
3071 continue;
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);
3087 Exit:
3088 return error;
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 */