[doc] Increase description text size in `glyph-terms.svg'.
[ttfautohint.git] / lib / talatin.c
blobf1beea933c6f18077754d71f0a2a3402986748fd
1 /* talatin.c */
3 /*
4 * Copyright (C) 2011-2014 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 /* find segments and links, compute all stem widths, and initialize */
39 /* standard width and height for the glyph with given charcode */
41 void
42 ta_latin_metrics_init_widths(TA_LatinMetrics metrics,
43 FT_Face face)
45 /* scan the array of segments in each direction */
46 TA_GlyphHintsRec hints[1];
49 TA_LOG_GLOBAL(("\n"
50 "latin standard widths computation (style `%s')\n"
51 "=====================================================\n"
52 "\n",
53 ta_style_names[metrics->root.style_class->style]));
55 ta_glyph_hints_init(hints);
57 metrics->axis[TA_DIMENSION_HORZ].width_count = 0;
58 metrics->axis[TA_DIMENSION_VERT].width_count = 0;
61 FT_Error error;
62 FT_ULong glyph_index;
63 FT_Long y_offset;
64 int dim;
65 TA_LatinMetricsRec dummy[1];
66 TA_Scaler scaler = &dummy->root.scaler;
68 TA_StyleClass style_class = metrics->root.style_class;
69 TA_ScriptClass script_class = ta_script_classes[style_class->script];
72 /* XXX: Extend this with a list of possible standard characters: */
73 /* Especially in non-default coverages, a singe standard */
74 /* character may not be available. */
75 ta_get_char_index(&metrics->root,
76 script_class->standard_char,
77 &glyph_index,
78 &y_offset);
79 if (glyph_index == 0)
80 goto Exit;
82 TA_LOG_GLOBAL(("standard character: U+%04lX (glyph index %d)\n",
83 script_class->standard_char, glyph_index));
85 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
86 if (error || face->glyph->outline.n_points <= 0)
87 goto Exit;
89 memset(dummy, 0, sizeof (TA_LatinMetricsRec));
91 dummy->units_per_em = metrics->units_per_em;
93 scaler->x_scale = 0x10000L;
94 scaler->y_scale = 0x10000L;
95 scaler->x_delta = 0;
96 scaler->y_delta = 0;
98 scaler->face = face;
99 scaler->render_mode = FT_RENDER_MODE_NORMAL;
100 scaler->flags = 0;
102 ta_glyph_hints_rescale(hints, (TA_StyleMetrics)dummy);
104 error = ta_glyph_hints_reload(hints, &face->glyph->outline);
105 if (error)
106 goto Exit;
108 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
110 TA_LatinAxis axis = &metrics->axis[dim];
111 TA_AxisHints axhints = &hints->axis[dim];
113 TA_Segment seg, limit, link;
114 FT_UInt num_widths = 0;
117 error = ta_latin_hints_compute_segments(hints, (TA_Dimension)dim);
118 if (error)
119 goto Exit;
121 ta_latin_hints_link_segments(hints, (TA_Dimension)dim);
123 seg = axhints->segments;
124 limit = seg + axhints->num_segments;
126 for (; seg < limit; seg++)
128 link = seg->link;
130 /* we only consider stem segments there! */
131 if (link
132 && link->link == seg
133 && link > seg)
135 FT_Pos dist;
138 dist = seg->pos - link->pos;
139 if (dist < 0)
140 dist = -dist;
142 if (num_widths < TA_LATIN_MAX_WIDTHS)
143 axis->widths[num_widths++].org = dist;
147 /* this also replaces multiple almost identical stem widths */
148 /* with a single one (the value 100 is heuristic) */
149 ta_sort_and_quantize_widths(&num_widths, axis->widths,
150 dummy->units_per_em / 100);
151 axis->width_count = num_widths;
154 Exit:
155 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
157 TA_LatinAxis axis = &metrics->axis[dim];
158 FT_Pos stdw;
161 stdw = (axis->width_count > 0) ? axis->widths[0].org
162 : TA_LATIN_CONSTANT(metrics, 50);
164 /* let's try 20% of the smallest width */
165 axis->edge_distance_threshold = stdw / 5;
166 axis->standard_width = stdw;
167 axis->extra_light = 0;
169 #ifdef TA_DEBUG
171 FT_UInt i;
174 TA_LOG_GLOBAL(("%s widths:\n",
175 dim == TA_DIMENSION_VERT ? "horizontal"
176 : "vertical"));
178 TA_LOG_GLOBAL((" %d (standard)", axis->standard_width));
179 for (i = 1; i < axis->width_count; i++)
180 TA_LOG_GLOBAL((" %d", axis->widths[i].org));
182 TA_LOG_GLOBAL(("\n"));
184 #endif
188 TA_LOG_GLOBAL(("\n"));
190 ta_glyph_hints_done(hints);
194 /* find all blue zones; flat segments give the reference points, */
195 /* round segments the overshoot positions */
197 static void
198 ta_latin_metrics_init_blues(TA_LatinMetrics metrics,
199 FT_Face face)
201 FT_Pos flats[TA_BLUE_STRING_MAX_LEN];
202 FT_Pos rounds[TA_BLUE_STRING_MAX_LEN];
203 FT_Int num_flats;
204 FT_Int num_rounds;
206 TA_LatinBlue blue;
207 FT_Error error;
208 TA_LatinAxis axis = &metrics->axis[TA_DIMENSION_VERT];
209 FT_Outline outline;
211 TA_StyleClass sc = metrics->root.style_class;
213 TA_Blue_Stringset bss = sc->blue_stringset;
214 const TA_Blue_StringRec* bs = &ta_blue_stringsets[bss];
217 /* we walk over the blue character strings as specified in the */
218 /* style's entry in the `ta_blue_stringset' array */
220 TA_LOG_GLOBAL(("latin blue zones computation\n"
221 "============================\n"
222 "\n"));
224 for (; bs->string != TA_BLUE_STRING_MAX; bs++)
226 const char* p = &ta_blue_strings[bs->string];
227 FT_Pos* blue_ref;
228 FT_Pos* blue_shoot;
231 #ifdef TA_DEBUG
233 FT_Bool have_flag = 0;
236 TA_LOG_GLOBAL(("blue zone %d", axis->blue_count));
238 if (bs->properties)
240 TA_LOG_GLOBAL((" ("));
242 if (TA_LATIN_IS_TOP_BLUE(bs))
244 TA_LOG_GLOBAL(("top"));
245 have_flag = 1;
248 if (TA_LATIN_IS_X_HEIGHT_BLUE(bs))
250 if (have_flag)
251 TA_LOG_GLOBAL((", "));
252 TA_LOG_GLOBAL(("small top"));
253 have_flag = 1;
256 if (TA_LATIN_IS_LONG_BLUE(bs))
258 if (have_flag)
259 TA_LOG_GLOBAL((", "));
260 TA_LOG_GLOBAL(("long"));
263 TA_LOG_GLOBAL((")"));
266 TA_LOG_GLOBAL((":\n"));
268 #endif /* TA_DEBUG */
270 num_flats = 0;
271 num_rounds = 0;
273 while (*p)
275 FT_ULong ch;
276 FT_ULong glyph_index;
277 FT_Long y_offset;
278 FT_Pos best_y; /* same as points.y */
279 FT_Int best_point, best_contour_first, best_contour_last;
280 FT_Vector* points;
281 FT_Bool round = 0;
284 GET_UTF8_CHAR(ch, p);
286 /* load the character in the face -- skip unknown or empty ones */
287 ta_get_char_index(&metrics->root, ch, &glyph_index, &y_offset);
288 if (glyph_index == 0)
290 TA_LOG_GLOBAL((" U+%04lX unavailable\n", ch));
291 continue;
294 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
295 outline = face->glyph->outline;
296 if (error || outline.n_points <= 0)
298 TA_LOG_GLOBAL((" U+%04lX contains no outlines\n", ch));
299 continue;
302 /* now compute min or max point indices and coordinates */
303 points = outline.points;
304 best_point = -1;
305 best_y = 0; /* make compiler happy */
306 best_contour_first = 0; /* ditto */
307 best_contour_last = 0; /* ditto */
310 FT_Int nn;
311 FT_Int first = 0;
312 FT_Int last = -1;
315 for (nn = 0; nn < outline.n_contours; first = last + 1, nn++)
317 FT_Int old_best_point = best_point;
318 FT_Int pp;
321 last = outline.contours[nn];
323 /* avoid single-point contours since they are never rasterized; */
324 /* in some fonts, they correspond to mark attachment points */
325 /* that are way outside of the glyph's real outline */
326 if (last <= first)
327 continue;
329 if (TA_LATIN_IS_TOP_BLUE(bs))
331 for (pp = first; pp <= last; pp++)
332 if (best_point < 0
333 || points[pp].y > best_y)
335 best_point = pp;
336 best_y = points[pp].y;
339 else
341 for (pp = first; pp <= last; pp++)
342 if (best_point < 0
343 || points[pp].y < best_y)
345 best_point = pp;
346 best_y = points[pp].y;
350 if (best_point != old_best_point)
352 best_contour_first = first;
353 best_contour_last = last;
358 /* now check whether the point belongs to a straight or round */
359 /* segment; we first need to find in which contour the extremum */
360 /* lies, then inspect its previous and next points */
361 if (best_point >= 0)
363 FT_Pos best_x = points[best_point].x;
364 FT_Int prev, next;
365 FT_Int best_segment_first, best_segment_last;
366 FT_Int best_on_point_first, best_on_point_last;
367 FT_Pos dist;
370 best_segment_first = best_point;
371 best_segment_last = best_point;
373 if (FT_CURVE_TAG(outline.tags[best_point]) == FT_CURVE_TAG_ON)
375 best_on_point_first = best_point;
376 best_on_point_last = best_point;
378 else
380 best_on_point_first = -1;
381 best_on_point_last = -1;
384 /* look for the previous and next points on the contour */
385 /* that are not on the same Y coordinate, then threshold */
386 /* the `closeness'... */
387 prev = best_point;
388 next = prev;
392 if (prev > best_contour_first)
393 prev--;
394 else
395 prev = best_contour_last;
397 dist = TA_ABS(points[prev].y - best_y);
398 /* accept a small distance or a small angle (both values are */
399 /* heuristic; value 20 corresponds to approx. 2.9 degrees) */
400 if (dist > 5)
401 if (TA_ABS(points[prev].x - best_x) <= 20 * dist)
402 break;
404 best_segment_first = prev;
406 if (FT_CURVE_TAG(outline.tags[prev]) == FT_CURVE_TAG_ON)
408 best_on_point_first = prev;
409 if (best_on_point_last < 0)
410 best_on_point_last = prev;
413 } while (prev != best_point);
417 if (next < best_contour_last)
418 next++;
419 else
420 next = best_contour_first;
422 dist = TA_ABS(points[next].y - best_y);
423 if (dist > 5)
424 if (TA_ABS(points[next].x - best_x) <= 20 * dist)
425 break;
427 best_segment_last = next;
429 if (FT_CURVE_TAG(outline.tags[next]) == FT_CURVE_TAG_ON)
431 best_on_point_last = next;
432 if (best_on_point_first < 0)
433 best_on_point_first = next;
436 } while (next != best_point);
438 if (TA_LATIN_IS_LONG_BLUE(bs))
440 /* If this flag is set, we have an additional constraint to */
441 /* get the blue zone distance: Find a segment of the topmost */
442 /* (or bottommost) contour that is longer than a heuristic */
443 /* threshold. This ensures that small bumps in the outline */
444 /* are ignored (for example, the `vertical serifs' found in */
445 /* many Hebrew glyph designs). */
447 /* If this segment is long enough, we are done. Otherwise, */
448 /* search the segment next to the extremum that is long */
449 /* enough, has the same direction, and a not too large */
450 /* vertical distance from the extremum. Note that the */
451 /* algorithm doesn't check whether the found segment is */
452 /* actually the one (vertically) nearest to the extremum. */
454 /* heuristic threshold value */
455 FT_Pos length_threshold = metrics->units_per_em / 25;
458 dist = TA_ABS(points[best_segment_last].x -
459 points[best_segment_first].x);
461 if (dist < length_threshold
462 && best_segment_last - best_segment_first + 2 <=
463 best_contour_last - best_contour_first)
465 /* heuristic threshold value */
466 FT_Pos height_threshold = metrics->units_per_em / 4;
468 FT_Int first;
469 FT_Int last;
470 FT_Bool hit;
472 FT_Bool left2right;
475 /* compute direction */
476 prev = best_point;
480 if (prev > best_contour_first)
481 prev--;
482 else
483 prev = best_contour_last;
485 if (points[prev].x != best_x)
486 break;
487 } while (prev != best_point);
489 /* skip glyph for the degenerate case */
490 if (prev == best_point)
491 continue;
493 left2right = FT_BOOL(points[prev].x < points[best_point].x);
495 first = best_segment_last;
496 last = first;
497 hit = 0;
501 FT_Bool l2r;
502 FT_Pos d;
503 FT_Int p_first, p_last;
506 if (!hit)
508 /* no hit; adjust first point */
509 first = last;
511 /* also adjust first and last on point */
512 if (FT_CURVE_TAG(outline.tags[first]) == FT_CURVE_TAG_ON)
514 p_first = first;
515 p_last = first;
517 else
519 p_first = -1;
520 p_last = -1;
523 hit = 1;
526 if (last < best_contour_last)
527 last++;
528 else
529 last = best_contour_first;
531 if (TA_ABS(best_y - points[first].y) > height_threshold)
533 /* vertical distance too large */
534 hit = 0;
535 continue;
538 /* same test as above */
539 dist = TA_ABS(points[last].y - points[first].y);
540 if (dist > 5)
541 if (TA_ABS(points[last].x - points[first].x) <= 20 * dist)
543 hit = 0;
544 continue;
547 if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON)
549 p_last = last;
550 if (p_first < 0)
551 p_first = last;
554 l2r = FT_BOOL(points[first].x < points[last].x);
555 d = TA_ABS(points[last].x - points[first].x);
557 if (l2r == left2right
558 && d >= length_threshold)
560 /* all constraints are met; update segment after finding */
561 /* its end */
564 if (last < best_contour_last)
565 last++;
566 else
567 last = best_contour_first;
569 d = TA_ABS(points[last].y - points[first].y);
570 if (d > 5)
571 if (TA_ABS(points[next].x - points[first].x) <=
572 20 * dist)
574 last--;
575 break;
578 p_last = last;
580 if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON)
582 p_last = last;
583 if (p_first < 0)
584 p_first = last;
586 } while (last != best_segment_first);
588 best_y = points[first].y;
590 best_segment_first = first;
591 best_segment_last = last;
593 best_on_point_first = p_first;
594 best_on_point_last = p_last;
596 break;
598 } while (last != best_segment_first);
603 * for computing blue zones, we add the y offset as returned
604 * by the currently used OpenType feature --
605 * for example, superscript glyphs might be identical
606 * to subscript glyphs with a vertical shift
608 best_y += y_offset;
610 TA_LOG_GLOBAL((" U+%04lX: best_y = %5ld", ch, best_y));
613 * now set the `round' flag depending on the segment's kind:
615 * - if the horizontal distance between the first and last
616 * `on' point is larger than upem/8 (value 8 is heuristic)
617 * we have a flat segment
618 * - if either the first or the last point of the segment is
619 * an `off' point, the segment is round, otherwise it is
620 * flat
622 if (best_on_point_first >= 0
623 && best_on_point_last >= 0
624 && (FT_UInt)(TA_ABS(points[best_on_point_last].x
625 - points[best_on_point_first].x))
626 > metrics->units_per_em / 8)
627 round = 0;
628 else
629 round = FT_BOOL(FT_CURVE_TAG(outline.tags[best_segment_first])
630 != FT_CURVE_TAG_ON
631 || FT_CURVE_TAG(outline.tags[best_segment_last])
632 != FT_CURVE_TAG_ON);
634 TA_LOG_GLOBAL((" (%s)\n", round ? "round" : "flat"));
637 if (round)
638 rounds[num_rounds++] = best_y;
639 else
640 flats[num_flats++] = best_y;
643 if (num_flats == 0 && num_rounds == 0)
645 /* we couldn't find a single glyph to compute this blue zone, */
646 /* we will simply ignore it then */
647 TA_LOG_GLOBAL((" empty\n"));
648 continue;
651 /* we have computed the contents of the `rounds' and `flats' tables, */
652 /* now determine the reference and overshoot position of the blue -- */
653 /* we simply take the median value after a simple sort */
654 ta_sort_pos(num_rounds, rounds);
655 ta_sort_pos(num_flats, flats);
657 blue = &axis->blues[axis->blue_count];
658 blue_ref = &blue->ref.org;
659 blue_shoot = &blue->shoot.org;
661 axis->blue_count++;
663 if (num_flats == 0)
665 *blue_ref =
666 *blue_shoot = rounds[num_rounds / 2];
668 else if (num_rounds == 0)
670 *blue_ref =
671 *blue_shoot = flats[num_flats / 2];
673 else
675 *blue_ref = flats[num_flats / 2];
676 *blue_shoot = rounds[num_rounds / 2];
679 /* there are sometimes problems if the overshoot position of top */
680 /* zones is under its reference position, or the opposite for bottom */
681 /* zones; we must thus check everything there and correct the errors */
682 if (*blue_shoot != *blue_ref)
684 FT_Pos ref = *blue_ref;
685 FT_Pos shoot = *blue_shoot;
686 FT_Bool over_ref = FT_BOOL(shoot > ref);
689 if (TA_LATIN_IS_TOP_BLUE(bs) ^ over_ref)
691 *blue_ref =
692 *blue_shoot = (shoot + ref) / 2;
694 TA_LOG_GLOBAL((" [overshoot smaller than reference,"
695 " taking mean value]\n"));
699 blue->flags = 0;
700 if (TA_LATIN_IS_TOP_BLUE(bs))
701 blue->flags |= TA_LATIN_BLUE_TOP;
703 /* the following flag is used later to adjust the y and x scales */
704 /* in order to optimize the pixel grid alignment */
705 /* of the top of small letters */
706 if (TA_LATIN_IS_X_HEIGHT_BLUE(bs))
707 blue->flags |= TA_LATIN_BLUE_ADJUSTMENT;
709 TA_LOG_GLOBAL((" -> reference = %ld\n"
710 " overshoot = %ld\n",
711 *blue_ref, *blue_shoot));
714 /* add two blue zones for usWinAscent and usWinDescent */
715 /* just in case the above algorithm has missed them -- */
716 /* Windows cuts off everything outside of those two values */
718 TT_OS2* os2;
721 os2 = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
723 if (os2)
725 blue = &axis->blues[axis->blue_count];
726 blue->flags = TA_LATIN_BLUE_TOP | TA_LATIN_BLUE_ACTIVE;
727 blue->ref.org =
728 blue->shoot.org = os2->usWinAscent;
730 TA_LOG_GLOBAL(("artificial blue zone for usWinAscent:\n"
731 " -> reference = %ld\n"
732 " overshoot = %ld\n",
733 blue->ref.org, blue->shoot.org));
735 blue = &axis->blues[axis->blue_count + 1];
736 blue->flags = TA_LATIN_BLUE_ACTIVE;
737 blue->ref.org =
738 blue->shoot.org = -os2->usWinDescent;
740 TA_LOG_GLOBAL(("artificial blue zone for usWinDescent:\n"
741 " -> reference = %ld\n"
742 " overshoot = %ld\n",
743 blue->ref.org, blue->shoot.org));
745 else
747 blue = &axis->blues[axis->blue_count];
748 blue->flags =
749 blue->ref.org =
750 blue->shoot.org = 0;
752 blue = &axis->blues[axis->blue_count + 1];
753 blue->flags =
754 blue->ref.org =
755 blue->shoot.org = 0;
759 TA_LOG_GLOBAL(("\n"));
761 return;
765 /* check whether all ASCII digits have the same advance width */
767 void
768 ta_latin_metrics_check_digits(TA_LatinMetrics metrics,
769 FT_Face face)
771 FT_UInt i;
772 FT_Bool started = 0, same_width = 1;
773 FT_Fixed advance, old_advance = 0;
776 /* digit `0' is 0x30 in all supported charmaps */
777 for (i = 0x30; i <= 0x39; i++)
779 FT_ULong glyph_index;
780 FT_Long y_offset;
783 ta_get_char_index(&metrics->root, i, &glyph_index, &y_offset);
784 if (glyph_index == 0)
785 continue;
787 if (FT_Get_Advance(face, glyph_index,
788 FT_LOAD_NO_SCALE
789 | FT_LOAD_NO_HINTING
790 | FT_LOAD_IGNORE_TRANSFORM,
791 &advance))
792 continue;
794 if (started)
796 if (advance != old_advance)
798 same_width = 0;
799 break;
802 else
804 old_advance = advance;
805 started = 1;
809 metrics->root.digits_have_same_width = same_width;
813 /* initialize global metrics */
815 FT_Error
816 ta_latin_metrics_init(TA_LatinMetrics metrics,
817 FT_Face face)
819 FT_CharMap oldmap = face->charmap;
822 metrics->units_per_em = face->units_per_EM;
824 if (!FT_Select_Charmap(face, FT_ENCODING_UNICODE))
826 ta_latin_metrics_init_widths(metrics, face);
827 ta_latin_metrics_init_blues(metrics, face);
828 ta_latin_metrics_check_digits(metrics, face);
831 FT_Set_Charmap(face, oldmap);
832 return FT_Err_Ok;
836 /* adjust scaling value, then scale and shift widths */
837 /* and blue zones (if applicable) for given dimension */
839 static void
840 ta_latin_metrics_scale_dim(TA_LatinMetrics metrics,
841 TA_Scaler scaler,
842 TA_Dimension dim)
844 FT_Fixed scale;
845 FT_Pos delta;
846 TA_LatinAxis axis;
847 FT_UInt ppem;
848 FT_UInt nn;
851 ppem = metrics->root.scaler.face->size->metrics.x_ppem;
853 if (dim == TA_DIMENSION_HORZ)
855 scale = scaler->x_scale;
856 delta = scaler->x_delta;
858 else
860 scale = scaler->y_scale;
861 delta = scaler->y_delta;
864 axis = &metrics->axis[dim];
866 if (axis->org_scale == scale && axis->org_delta == delta)
867 return;
869 axis->org_scale = scale;
870 axis->org_delta = delta;
872 /* correct Y scale to optimize the alignment of the top of */
873 /* small letters to the pixel grid */
874 /* (if we do x-height snapping for this ppem value) */
875 if (!number_set_is_element(
876 metrics->root.globals->font->x_height_snapping_exceptions,
877 ppem))
879 TA_LatinAxis Axis = &metrics->axis[TA_DIMENSION_VERT];
880 TA_LatinBlue blue = NULL;
883 for (nn = 0; nn < Axis->blue_count; nn++)
885 if (Axis->blues[nn].flags & TA_LATIN_BLUE_ADJUSTMENT)
887 blue = &Axis->blues[nn];
888 break;
892 if (blue)
894 FT_Pos scaled;
895 FT_Pos threshold;
896 FT_Pos fitted;
897 FT_UInt limit;
900 scaled = FT_MulFix(blue->shoot.org, scaler->y_scale);
901 limit = metrics->root.globals->increase_x_height;
902 threshold = 40;
904 /* if the `increase-x-height' property is active, */
905 /* we round up much more often */
906 if (limit
907 && ppem <= limit
908 && ppem >= TA_PROP_INCREASE_X_HEIGHT_MIN)
909 threshold = 52;
911 fitted = (scaled + threshold) & ~63;
913 if (scaled != fitted)
915 if (dim == TA_DIMENSION_VERT)
917 scale = FT_MulDiv(scale, fitted, scaled);
919 TA_LOG_GLOBAL((
920 "ta_latin_metrics_scale_dim:"
921 " x height alignment (style `%s'):\n"
923 " vertical scaling changed from %.4f to %.4f (by %d%%)\n"
924 "\n",
925 ta_style_names[metrics->root.style_class->style],
926 axis->org_scale / 65536.0,
927 scale / 65536.0,
928 (fitted - scaled) * 100 / scaled));
934 axis->scale = scale;
935 axis->delta = delta;
937 if (dim == TA_DIMENSION_HORZ)
939 metrics->root.scaler.x_scale = scale;
940 metrics->root.scaler.x_delta = delta;
942 else
944 metrics->root.scaler.y_scale = scale;
945 metrics->root.scaler.y_delta = delta;
948 TA_LOG_GLOBAL(("%s widths (style `%s')\n",
949 dim == TA_DIMENSION_HORZ ? "horizontal" : "vertical",
950 ta_style_names[metrics->root.style_class->style]));
952 /* scale the widths */
953 for (nn = 0; nn < axis->width_count; nn++)
955 TA_Width width = axis->widths + nn;
958 width->cur = FT_MulFix(width->org, scale);
959 width->fit = width->cur;
961 TA_LOG_GLOBAL((" %d scaled to %.2f\n",
962 width->org,
963 width->cur / 64.0));
966 TA_LOG_GLOBAL(("\n"));
968 /* an extra-light axis corresponds to a standard width that is */
969 /* smaller than 5/8 pixels */
970 axis->extra_light =
971 (FT_Bool)(FT_MulFix(axis->standard_width, scale) < 32 + 8);
973 #ifdef TA_DEBUG
974 if (axis->extra_light)
975 TA_LOG_GLOBAL(("`%s' style is extra light (at current resolution)\n"
976 "\n",
977 ta_style_names[metrics->root.style_class->style]));
978 #endif
980 if (dim == TA_DIMENSION_VERT)
982 TA_LOG_GLOBAL(("blue zones (style `%s')\n",
983 ta_style_names[metrics->root.style_class->style]));
985 /* scale the blue zones */
986 for (nn = 0; nn < axis->blue_count; nn++)
988 TA_LatinBlue blue = &axis->blues[nn];
989 FT_Pos dist;
992 blue->ref.cur = FT_MulFix(blue->ref.org, scale) + delta;
993 blue->ref.fit = blue->ref.cur;
994 blue->shoot.cur = FT_MulFix(blue->shoot.org, scale) + delta;
995 blue->shoot.fit = blue->shoot.cur;
996 blue->flags &= ~TA_LATIN_BLUE_ACTIVE;
998 /* a blue zone is only active if it is less than 3/4 pixels tall */
999 dist = FT_MulFix(blue->ref.org - blue->shoot.org, scale);
1000 if (dist <= 48 && dist >= -48)
1002 #if 0
1003 FT_Pos delta1;
1004 #endif
1005 FT_Pos delta2;
1008 /* use discrete values for blue zone widths */
1010 #if 0
1011 /* generic, original code */
1012 delta1 = blue->shoot.org - blue->ref.org;
1013 delta2 = delta1;
1014 if (delta1 < 0)
1015 delta2 = -delta2;
1017 delta2 = FT_MulFix(delta2, scale);
1019 if (delta2 < 32)
1020 delta2 = 0;
1021 else if (delta2 < 64)
1022 delta2 = 32 + (((delta2 - 32) + 16) & ~31);
1023 else
1024 delta2 = TA_PIX_ROUND(delta2);
1026 if (delta1 < 0)
1027 delta2 = -delta2;
1029 blue->ref.fit = TA_PIX_ROUND(blue->ref.cur);
1030 blue->shoot.fit = blue->ref.fit + delta2;
1031 #else
1032 /* simplified version due to abs(dist) <= 48 */
1033 delta2 = dist;
1034 if (dist < 0)
1035 delta2 = -delta2;
1037 if (delta2 < 32)
1038 delta2 = 0;
1039 else if (delta2 < 48)
1040 delta2 = 32;
1041 else
1042 delta2 = 64;
1044 if (dist < 0)
1045 delta2 = -delta2;
1047 blue->ref.fit = TA_PIX_ROUND(blue->ref.cur);
1048 blue->shoot.fit = blue->ref.fit - delta2;
1049 #endif
1051 blue->flags |= TA_LATIN_BLUE_ACTIVE;
1053 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f%s\n"
1054 " overshoot %d: %d scaled to %.2f%s\n",
1056 blue->ref.org,
1057 blue->ref.fit / 64.0,
1058 blue->flags & TA_LATIN_BLUE_ACTIVE ? ""
1059 : " (inactive)",
1061 blue->shoot.org,
1062 blue->shoot.fit / 64.0,
1063 blue->flags & TA_LATIN_BLUE_ACTIVE ? ""
1064 : " (inactive)"));
1068 /* the last two artificial blue zones are to be scaled */
1069 /* with uncorrected scaling values */
1071 TA_LatinAxis a = &metrics->axis[TA_DIMENSION_VERT];
1072 TA_LatinBlue b;
1075 b = &a->blues[a->blue_count];
1076 b->ref.cur =
1077 b->ref.fit =
1078 b->shoot.cur =
1079 b->shoot.fit = FT_MulFix(b->ref.org, a->org_scale) + delta;
1081 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f (artificial)\n"
1082 " overshoot %d: %d scaled to %.2f (artificial)\n",
1083 a->blue_count,
1084 b->ref.org,
1085 b->ref.fit / 64.0,
1086 a->blue_count,
1087 b->shoot.org,
1088 b->shoot.fit / 64.0));
1090 b = &a->blues[a->blue_count + 1];
1091 b->ref.cur =
1092 b->ref.fit =
1093 b->shoot.cur =
1094 b->shoot.fit = FT_MulFix(b->ref.org, a->org_scale) + delta;
1096 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f (artificial)\n"
1097 " overshoot %d: %d scaled to %.2f (artificial)\n",
1098 a->blue_count + 1,
1099 b->ref.org,
1100 b->ref.fit / 64.0,
1101 a->blue_count + 1,
1102 b->shoot.org,
1103 b->shoot.fit / 64.0));
1106 TA_LOG_GLOBAL(("\n"));
1111 /* scale global values in both directions */
1113 void
1114 ta_latin_metrics_scale(TA_LatinMetrics metrics,
1115 TA_Scaler scaler)
1117 metrics->root.scaler.render_mode = scaler->render_mode;
1118 metrics->root.scaler.face = scaler->face;
1119 metrics->root.scaler.flags = scaler->flags;
1121 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_HORZ);
1122 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_VERT);
1126 /* walk over all contours and compute its segments */
1128 FT_Error
1129 ta_latin_hints_compute_segments(TA_GlyphHints hints,
1130 TA_Dimension dim)
1132 TA_AxisHints axis = &hints->axis[dim];
1133 FT_Error error = FT_Err_Ok;
1135 TA_Segment segment = NULL;
1136 TA_SegmentRec seg0;
1138 TA_Point* contour = hints->contours;
1139 TA_Point* contour_limit = contour + hints->num_contours;
1140 TA_Direction major_dir, segment_dir;
1143 memset(&seg0, 0, sizeof (TA_SegmentRec));
1144 seg0.score = 32000;
1145 seg0.flags = TA_EDGE_NORMAL;
1147 major_dir = (TA_Direction)TA_ABS(axis->major_dir);
1148 segment_dir = major_dir;
1150 axis->num_segments = 0;
1152 /* set up (u,v) in each point */
1153 if (dim == TA_DIMENSION_HORZ)
1155 TA_Point point = hints->points;
1156 TA_Point limit = point + hints->num_points;
1159 for (; point < limit; point++)
1161 point->u = point->fx;
1162 point->v = point->fy;
1165 else
1167 TA_Point point = hints->points;
1168 TA_Point limit = point + hints->num_points;
1171 for (; point < limit; point++)
1173 point->u = point->fy;
1174 point->v = point->fx;
1178 /* do each contour separately */
1179 for (; contour < contour_limit; contour++)
1181 TA_Point point = contour[0];
1182 TA_Point last = point->prev;
1184 int on_edge = 0;
1186 FT_Pos min_pos = 32000; /* minimum segment pos != min_coord */
1187 FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */
1188 FT_Bool passed;
1191 if (point == last) /* skip singletons -- just in case */
1192 continue;
1194 if (TA_ABS(last->out_dir) == major_dir
1195 && TA_ABS(point->out_dir) == major_dir)
1197 /* we are already on an edge, try to locate its start */
1198 last = point;
1200 for (;;)
1202 point = point->prev;
1203 if (TA_ABS(point->out_dir) != major_dir)
1205 point = point->next;
1206 break;
1208 if (point == last)
1209 break;
1213 last = point;
1214 passed = 0;
1216 for (;;)
1218 FT_Pos u, v;
1221 if (on_edge)
1223 u = point->u;
1224 if (u < min_pos)
1225 min_pos = u;
1226 if (u > max_pos)
1227 max_pos = u;
1229 if (point->out_dir != segment_dir
1230 || point == last)
1232 /* we are just leaving an edge; record a new segment! */
1233 segment->last = point;
1234 segment->pos = (FT_Short)((min_pos + max_pos) >> 1);
1236 /* a segment is round if either its first or last point */
1237 /* is a control point */
1238 if ((segment->first->flags | point->flags) & TA_FLAG_CONTROL)
1239 segment->flags |= TA_EDGE_ROUND;
1241 /* compute segment size */
1242 min_pos = max_pos = point->v;
1244 v = segment->first->v;
1245 if (v < min_pos)
1246 min_pos = v;
1247 if (v > max_pos)
1248 max_pos = v;
1250 segment->min_coord = (FT_Short)min_pos;
1251 segment->max_coord = (FT_Short)max_pos;
1252 segment->height = (FT_Short)(segment->max_coord -
1253 segment->min_coord);
1255 on_edge = 0;
1256 segment = NULL;
1257 /* fall through */
1261 /* now exit if we are at the start/end point */
1262 if (point == last)
1264 if (passed)
1265 break;
1266 passed = 1;
1269 if (!on_edge
1270 && TA_ABS(point->out_dir) == major_dir)
1272 /* this is the start of a new segment! */
1273 segment_dir = (TA_Direction)point->out_dir;
1275 /* clear all segment fields */
1276 error = ta_axis_hints_new_segment(axis, &segment);
1277 if (error)
1278 goto Exit;
1280 segment[0] = seg0;
1281 segment->dir = (FT_Char)segment_dir;
1282 min_pos = max_pos = point->u;
1283 segment->first = point;
1284 segment->last = point;
1285 on_edge = 1;
1288 point = point->next;
1290 } /* contours */
1293 /* now slightly increase the height of segments if this makes sense -- */
1294 /* this is used to better detect and ignore serifs */
1296 TA_Segment segments = axis->segments;
1297 TA_Segment segments_end = segments + axis->num_segments;
1300 for (segment = segments; segment < segments_end; segment++)
1302 TA_Point first = segment->first;
1303 TA_Point last = segment->last;
1305 FT_Pos first_v = first->v;
1306 FT_Pos last_v = last->v;
1309 if (first == last)
1310 continue;
1312 if (first_v < last_v)
1314 TA_Point p;
1317 p = first->prev;
1318 if (p->v < first_v)
1319 segment->height = (FT_Short)(segment->height +
1320 ((first_v - p->v) >> 1));
1322 p = last->next;
1323 if (p->v > last_v)
1324 segment->height = (FT_Short)(segment->height +
1325 ((p->v - last_v) >> 1));
1327 else
1329 TA_Point p;
1332 p = first->prev;
1333 if (p->v > first_v)
1334 segment->height = (FT_Short)(segment->height +
1335 ((p->v - first_v) >> 1));
1337 p = last->next;
1338 if (p->v < last_v)
1339 segment->height = (FT_Short)(segment->height +
1340 ((last_v - p->v) >> 1));
1345 Exit:
1346 return error;
1350 /* link segments to form stems and serifs */
1352 void
1353 ta_latin_hints_link_segments(TA_GlyphHints hints,
1354 TA_Dimension dim)
1356 TA_AxisHints axis = &hints->axis[dim];
1358 TA_Segment segments = axis->segments;
1359 TA_Segment segment_limit = segments + axis->num_segments;
1361 FT_Pos len_threshold, len_score;
1362 TA_Segment seg1, seg2;
1365 len_threshold = TA_LATIN_CONSTANT(hints->metrics, 8);
1366 if (len_threshold == 0)
1367 len_threshold = 1;
1369 len_score = TA_LATIN_CONSTANT(hints->metrics, 6000);
1371 /* now compare each segment to the others */
1372 for (seg1 = segments; seg1 < segment_limit; seg1++)
1374 /* the fake segments are introduced to hint the metrics -- */
1375 /* we must never link them to anything */
1376 if (seg1->dir != axis->major_dir
1377 || seg1->first == seg1->last)
1378 continue;
1380 /* search for stems having opposite directions, */
1381 /* with seg1 to the `left' of seg2 */
1382 for (seg2 = segments; seg2 < segment_limit; seg2++)
1384 FT_Pos pos1 = seg1->pos;
1385 FT_Pos pos2 = seg2->pos;
1388 if (seg1->dir + seg2->dir == 0
1389 && pos2 > pos1)
1391 /* compute distance between the two segments */
1392 FT_Pos dist = pos2 - pos1;
1393 FT_Pos min = seg1->min_coord;
1394 FT_Pos max = seg1->max_coord;
1395 FT_Pos len, score;
1398 if (min < seg2->min_coord)
1399 min = seg2->min_coord;
1400 if (max > seg2->max_coord)
1401 max = seg2->max_coord;
1403 /* compute maximum coordinate difference of the two segments */
1404 len = max - min;
1405 if (len >= len_threshold)
1407 /* small coordinate differences cause a higher score, and */
1408 /* segments with a greater distance cause a higher score also */
1409 score = dist + len_score / len;
1411 /* and we search for the smallest score */
1412 /* of the sum of the two values */
1413 if (score < seg1->score)
1415 seg1->score = score;
1416 seg1->link = seg2;
1419 if (score < seg2->score)
1421 seg2->score = score;
1422 seg2->link = seg1;
1429 /* now compute the `serif' segments, cf. explanations in `tahints.h' */
1430 for (seg1 = segments; seg1 < segment_limit; seg1++)
1432 seg2 = seg1->link;
1434 if (seg2)
1436 if (seg2->link != seg1)
1438 seg1->link = 0;
1439 seg1->serif = seg2->link;
1446 /* link segments to edges, using feature analysis for selection */
1448 FT_Error
1449 ta_latin_hints_compute_edges(TA_GlyphHints hints,
1450 TA_Dimension dim)
1452 TA_AxisHints axis = &hints->axis[dim];
1453 FT_Error error = FT_Err_Ok;
1454 TA_LatinAxis laxis = &((TA_LatinMetrics)hints->metrics)->axis[dim];
1456 TA_Segment segments = axis->segments;
1457 TA_Segment segment_limit = segments + axis->num_segments;
1458 TA_Segment seg;
1460 #if 0
1461 TA_Direction up_dir;
1462 #endif
1463 FT_Fixed scale;
1464 FT_Pos edge_distance_threshold;
1465 FT_Pos segment_length_threshold;
1468 axis->num_edges = 0;
1470 scale = (dim == TA_DIMENSION_HORZ) ? hints->x_scale
1471 : hints->y_scale;
1473 #if 0
1474 up_dir = (dim == TA_DIMENSION_HORZ) ? TA_DIR_UP
1475 : TA_DIR_RIGHT;
1476 #endif
1478 /* we ignore all segments that are less than 1 pixel in length */
1479 /* to avoid many problems with serif fonts */
1480 /* (the corresponding threshold is computed in font units) */
1481 if (dim == TA_DIMENSION_HORZ)
1482 segment_length_threshold = FT_DivFix(64, hints->y_scale);
1483 else
1484 segment_length_threshold = 0;
1486 /********************************************************************/
1487 /* */
1488 /* We begin by generating a sorted table of edges for the current */
1489 /* direction. To do so, we simply scan each segment and try to find */
1490 /* an edge in our table that corresponds to its position. */
1491 /* */
1492 /* If no edge is found, we create and insert a new edge in the */
1493 /* sorted table. Otherwise, we simply add the segment to the edge's */
1494 /* list which gets processed in the second step to compute the */
1495 /* edge's properties. */
1496 /* */
1497 /* Note that the table of edges is sorted along the segment/edge */
1498 /* position. */
1499 /* */
1500 /********************************************************************/
1502 /* assure that edge distance threshold is at most 0.25px */
1503 edge_distance_threshold = FT_MulFix(laxis->edge_distance_threshold,
1504 scale);
1505 if (edge_distance_threshold > 64 / 4)
1506 edge_distance_threshold = 64 / 4;
1508 edge_distance_threshold = FT_DivFix(edge_distance_threshold,
1509 scale);
1511 for (seg = segments; seg < segment_limit; seg++)
1513 TA_Edge found = NULL;
1514 FT_Int ee;
1517 if (seg->height < segment_length_threshold)
1518 continue;
1520 /* a special case for serif edges: */
1521 /* if they are smaller than 1.5 pixels we ignore them */
1522 if (seg->serif
1523 && 2 * seg->height < 3 * segment_length_threshold)
1524 continue;
1526 /* look for an edge corresponding to the segment */
1527 for (ee = 0; ee < axis->num_edges; ee++)
1529 TA_Edge edge = axis->edges + ee;
1530 FT_Pos dist;
1533 dist = seg->pos - edge->fpos;
1534 if (dist < 0)
1535 dist = -dist;
1537 if (dist < edge_distance_threshold && edge->dir == seg->dir)
1539 found = edge;
1540 break;
1544 if (!found)
1546 TA_Edge edge;
1549 /* insert a new edge in the list and sort according to the position */
1550 error = ta_axis_hints_new_edge(axis, seg->pos,
1551 (TA_Direction)seg->dir,
1552 &edge);
1553 if (error)
1554 goto Exit;
1556 /* add the segment to the new edge's list */
1557 memset(edge, 0, sizeof (TA_EdgeRec));
1558 edge->first = seg;
1559 edge->last = seg;
1560 edge->dir = seg->dir;
1561 edge->fpos = seg->pos;
1562 edge->opos = FT_MulFix(seg->pos, scale);
1563 edge->pos = edge->opos;
1564 seg->edge_next = seg;
1566 else
1568 /* if an edge was found, simply add the segment to the edge's list */
1569 seg->edge_next = found->first;
1570 found->last->edge_next = seg;
1571 found->last = seg;
1575 /*****************************************************************/
1576 /* */
1577 /* Good, we now compute each edge's properties according to */
1578 /* the segments found on its position. Basically, these are */
1579 /* */
1580 /* - the edge's main direction */
1581 /* - stem edge, serif edge or both (which defaults to stem then) */
1582 /* - rounded edge, straight or both (which defaults to straight) */
1583 /* - link for edge */
1584 /* */
1585 /*****************************************************************/
1587 /* first of all, set the `edge' field in each segment -- this is */
1588 /* required in order to compute edge links */
1590 /* note that removing this loop and setting the `edge' field of each */
1591 /* segment directly in the code above slows down execution speed for */
1592 /* some reasons on platforms like the Sun */
1594 TA_Edge edges = axis->edges;
1595 TA_Edge edge_limit = edges + axis->num_edges;
1596 TA_Edge edge;
1599 for (edge = edges; edge < edge_limit; edge++)
1601 seg = edge->first;
1602 if (seg)
1605 seg->edge = edge;
1606 seg = seg->edge_next;
1607 } while (seg != edge->first);
1610 /* now compute each edge properties */
1611 for (edge = edges; edge < edge_limit; edge++)
1613 FT_Int is_round = 0; /* does it contain round segments? */
1614 FT_Int is_straight = 0; /* does it contain straight segments? */
1615 #if 0
1616 FT_Pos ups = 0; /* number of upwards segments */
1617 FT_Pos downs = 0; /* number of downwards segments */
1618 #endif
1621 seg = edge->first;
1625 FT_Bool is_serif;
1628 /* check for roundness of segment */
1629 if (seg->flags & TA_EDGE_ROUND)
1630 is_round++;
1631 else
1632 is_straight++;
1634 #if 0
1635 /* check for segment direction */
1636 if (seg->dir == up_dir)
1637 ups += seg->max_coord - seg->min_coord;
1638 else
1639 downs += seg->max_coord - seg->min_coord;
1640 #endif
1642 /* check for links -- */
1643 /* if seg->serif is set, then seg->link must be ignored */
1644 is_serif = (FT_Bool)(seg->serif
1645 && seg->serif->edge
1646 && seg->serif->edge != edge);
1648 if ((seg->link && seg->link->edge != NULL)
1649 || is_serif)
1651 TA_Edge edge2;
1652 TA_Segment seg2;
1655 edge2 = edge->link;
1656 seg2 = seg->link;
1658 if (is_serif)
1660 seg2 = seg->serif;
1661 edge2 = edge->serif;
1664 if (edge2)
1666 FT_Pos edge_delta;
1667 FT_Pos seg_delta;
1670 edge_delta = edge->fpos - edge2->fpos;
1671 if (edge_delta < 0)
1672 edge_delta = -edge_delta;
1674 seg_delta = seg->pos - seg2->pos;
1675 if (seg_delta < 0)
1676 seg_delta = -seg_delta;
1678 if (seg_delta < edge_delta)
1679 edge2 = seg2->edge;
1681 else
1682 edge2 = seg2->edge;
1684 if (is_serif)
1686 edge->serif = edge2;
1687 edge2->flags |= TA_EDGE_SERIF;
1689 else
1690 edge->link = edge2;
1693 seg = seg->edge_next;
1694 } while (seg != edge->first);
1696 /* set the round/straight flags */
1697 edge->flags = TA_EDGE_NORMAL;
1699 if (is_round > 0
1700 && is_round >= is_straight)
1701 edge->flags |= TA_EDGE_ROUND;
1703 #if 0
1704 /* set the edge's main direction */
1705 edge->dir = TA_DIR_NONE;
1707 if (ups > downs)
1708 edge->dir = (FT_Char)up_dir;
1710 else if (ups < downs)
1711 edge->dir = (FT_Char)-up_dir;
1713 else if (ups == downs)
1714 edge->dir = 0; /* both up and down! */
1715 #endif
1717 /* get rid of serifs if link is set */
1718 /* XXX: this gets rid of many unpleasant artefacts! */
1719 /* example: the `c' in cour.pfa at size 13 */
1721 if (edge->serif && edge->link)
1722 edge->serif = 0;
1726 Exit:
1727 return error;
1731 /* detect segments and edges for given dimension */
1733 FT_Error
1734 ta_latin_hints_detect_features(TA_GlyphHints hints,
1735 TA_Dimension dim)
1737 FT_Error error;
1740 error = ta_latin_hints_compute_segments(hints, dim);
1741 if (!error)
1743 ta_latin_hints_link_segments(hints, dim);
1745 error = ta_latin_hints_compute_edges(hints, dim);
1748 return error;
1752 /* compute all edges which lie within blue zones */
1754 void
1755 ta_latin_hints_compute_blue_edges(TA_GlyphHints hints,
1756 TA_LatinMetrics metrics)
1758 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
1760 TA_Edge edge = axis->edges;
1761 TA_Edge edge_limit = edge + axis->num_edges;
1763 TA_LatinAxis latin = &metrics->axis[TA_DIMENSION_VERT];
1764 FT_Fixed scale = latin->scale;
1767 /* compute which blue zones are active, */
1768 /* i.e. have their scaled size < 3/4 pixels */
1770 /* for each horizontal edge search the blue zone which is closest */
1771 for (; edge < edge_limit; edge++)
1773 FT_UInt bb;
1774 TA_Width best_blue = NULL;
1775 FT_Pos best_dist; /* initial threshold */
1777 FT_UInt best_blue_idx = 0;
1778 FT_Bool best_blue_is_shoot = 0;
1781 /* compute the initial threshold as a fraction of the EM size */
1782 /* (the value 40 is heuristic) */
1783 best_dist = FT_MulFix(metrics->units_per_em / 40, scale);
1785 /* assure a minimum distance of 0.5px */
1786 if (best_dist > 64 / 2)
1787 best_dist = 64 / 2;
1789 /* this loop also handles the two extra blue zones */
1790 /* for usWinAscent and usWinDescent */
1791 /* if option `windows-compatibility' is set */
1792 for (bb = 0;
1793 bb < latin->blue_count
1794 + (metrics->root.globals->font->windows_compatibility ? 2 : 0);
1795 bb++)
1797 TA_LatinBlue blue = latin->blues + bb;
1798 FT_Bool is_top_blue, is_major_dir;
1801 /* skip inactive blue zones (i.e., those that are too large) */
1802 if (!(blue->flags & TA_LATIN_BLUE_ACTIVE))
1803 continue;
1805 /* if it is a top zone, check for right edges -- */
1806 /* if it is a bottom zone, check for left edges */
1807 is_top_blue = (FT_Byte)((blue->flags & TA_LATIN_BLUE_TOP) != 0);
1808 is_major_dir = FT_BOOL(edge->dir == axis->major_dir);
1810 /* if it is a top zone, the edge must be against the major */
1811 /* direction; if it is a bottom zone, it must be in the major */
1812 /* direction */
1813 if (is_top_blue ^ is_major_dir)
1815 FT_Pos dist;
1818 /* first of all, compare it to the reference position */
1819 dist = edge->fpos - blue->ref.org;
1820 if (dist < 0)
1821 dist = -dist;
1823 dist = FT_MulFix(dist, scale);
1824 if (dist < best_dist)
1826 best_dist = dist;
1827 best_blue = &blue->ref;
1829 best_blue_idx = bb;
1830 best_blue_is_shoot = 0;
1833 /* now compare it to the overshoot position and check whether */
1834 /* the edge is rounded, and whether the edge is over the */
1835 /* reference position of a top zone, or under the reference */
1836 /* position of a bottom zone */
1837 if (edge->flags & TA_EDGE_ROUND
1838 && dist != 0)
1840 FT_Bool is_under_ref = FT_BOOL(edge->fpos < blue->ref.org);
1843 if (is_top_blue ^ is_under_ref)
1845 dist = edge->fpos - blue->shoot.org;
1846 if (dist < 0)
1847 dist = -dist;
1849 dist = FT_MulFix(dist, scale);
1850 if (dist < best_dist)
1852 best_dist = dist;
1853 best_blue = &blue->shoot;
1855 best_blue_idx = bb;
1856 best_blue_is_shoot = 1;
1863 if (best_blue)
1865 edge->blue_edge = best_blue;
1866 edge->best_blue_idx = best_blue_idx;
1867 edge->best_blue_is_shoot = best_blue_is_shoot;
1873 /* initalize hinting engine */
1875 static FT_Error
1876 ta_latin_hints_init(TA_GlyphHints hints,
1877 TA_LatinMetrics metrics)
1879 FT_Render_Mode mode;
1880 FT_UInt32 scaler_flags, other_flags;
1881 FT_Face face = metrics->root.scaler.face;
1884 ta_glyph_hints_rescale(hints, (TA_StyleMetrics)metrics);
1886 /* correct x_scale and y_scale if needed, since they may have */
1887 /* been modified by `ta_latin_metrics_scale_dim' above */
1888 hints->x_scale = metrics->axis[TA_DIMENSION_HORZ].scale;
1889 hints->x_delta = metrics->axis[TA_DIMENSION_HORZ].delta;
1890 hints->y_scale = metrics->axis[TA_DIMENSION_VERT].scale;
1891 hints->y_delta = metrics->axis[TA_DIMENSION_VERT].delta;
1893 /* compute flags depending on render mode, etc. */
1894 mode = metrics->root.scaler.render_mode;
1896 #if 0 /* #ifdef TA_CONFIG_OPTION_USE_WARPER */
1897 if (mode == FT_RENDER_MODE_LCD
1898 || mode == FT_RENDER_MODE_LCD_V)
1899 metrics->root.scaler.render_mode =
1900 mode = FT_RENDER_MODE_NORMAL;
1901 #endif
1903 scaler_flags = hints->scaler_flags;
1904 other_flags = 0;
1906 /* we snap the width of vertical stems for the monochrome */
1907 /* and horizontal LCD rendering targets only */
1908 if (mode == FT_RENDER_MODE_MONO
1909 || mode == FT_RENDER_MODE_LCD)
1910 other_flags |= TA_LATIN_HINTS_HORZ_SNAP;
1912 /* we snap the width of horizontal stems for the monochrome */
1913 /* and vertical LCD rendering targets only */
1914 if (mode == FT_RENDER_MODE_MONO
1915 || mode == FT_RENDER_MODE_LCD_V)
1916 other_flags |= TA_LATIN_HINTS_VERT_SNAP;
1918 /* we adjust stems to full pixels only if we don't use the `light' mode */
1919 if (mode != FT_RENDER_MODE_LIGHT)
1920 other_flags |= TA_LATIN_HINTS_STEM_ADJUST;
1922 if (mode == FT_RENDER_MODE_MONO)
1923 other_flags |= TA_LATIN_HINTS_MONO;
1925 /* in `light' hinting mode we disable horizontal hinting completely; */
1926 /* we also do it if the face is italic */
1927 if (mode == FT_RENDER_MODE_LIGHT
1928 || (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0)
1929 scaler_flags |= TA_SCALER_FLAG_NO_HORIZONTAL;
1931 hints->scaler_flags = scaler_flags;
1932 hints->other_flags = other_flags;
1934 return FT_Err_Ok;
1938 /* snap a given width in scaled coordinates to */
1939 /* one of the current standard widths */
1941 static FT_Pos
1942 ta_latin_snap_width(TA_Width widths,
1943 FT_Int count,
1944 FT_Pos width)
1946 int n;
1947 FT_Pos best = 64 + 32 + 2;
1948 FT_Pos reference = width;
1949 FT_Pos scaled;
1952 for (n = 0; n < count; n++)
1954 FT_Pos w;
1955 FT_Pos dist;
1958 w = widths[n].cur;
1959 dist = width - w;
1960 if (dist < 0)
1961 dist = -dist;
1962 if (dist < best)
1964 best = dist;
1965 reference = w;
1969 scaled = TA_PIX_ROUND(reference);
1971 if (width >= reference)
1973 if (width < scaled + 48)
1974 width = reference;
1976 else
1978 if (width > scaled - 48)
1979 width = reference;
1982 return width;
1986 /* compute the snapped width of a given stem, ignoring very thin ones */
1988 /* there is a lot of voodoo in this function; changing the hard-coded */
1989 /* parameters influence the whole hinting process */
1991 static FT_Pos
1992 ta_latin_compute_stem_width(TA_GlyphHints hints,
1993 TA_Dimension dim,
1994 FT_Pos width,
1995 FT_Byte base_flags,
1996 FT_Byte stem_flags)
1998 TA_LatinMetrics metrics = (TA_LatinMetrics) hints->metrics;
1999 TA_LatinAxis axis = &metrics->axis[dim];
2001 FT_Pos dist = width;
2002 FT_Int sign = 0;
2003 FT_Int vertical = (dim == TA_DIMENSION_VERT);
2006 if (!TA_LATIN_HINTS_DO_STEM_ADJUST(hints)
2007 || axis->extra_light)
2008 return width;
2010 if (dist < 0)
2012 dist = -width;
2013 sign = 1;
2016 if ((vertical && !TA_LATIN_HINTS_DO_VERT_SNAP(hints))
2017 || (!vertical && !TA_LATIN_HINTS_DO_HORZ_SNAP(hints)))
2019 /* smooth hinting process: very lightly quantize the stem width */
2021 /* leave the widths of serifs alone */
2022 if ((stem_flags & TA_EDGE_SERIF)
2023 && vertical
2024 && (dist < 3 * 64))
2025 goto Done_Width;
2026 else if (base_flags & TA_EDGE_ROUND)
2028 if (dist < 80)
2029 dist = 64;
2031 else if (dist < 56)
2032 dist = 56;
2034 if (axis->width_count > 0)
2036 FT_Pos delta;
2039 /* compare to standard width */
2040 delta = dist - axis->widths[0].cur;
2042 if (delta < 0)
2043 delta = -delta;
2045 if (delta < 40)
2047 dist = axis->widths[0].cur;
2048 if (dist < 48)
2049 dist = 48;
2051 goto Done_Width;
2054 if (dist < 3 * 64)
2056 delta = dist & 63;
2057 dist &= -64;
2059 if (delta < 10)
2060 dist += delta;
2061 else if (delta < 32)
2062 dist += 10;
2063 else if (delta < 54)
2064 dist += 54;
2065 else
2066 dist += delta;
2068 else
2069 dist = (dist + 32) & ~63;
2072 else
2074 /* strong hinting process: snap the stem width to integer pixels */
2076 FT_Pos org_dist = dist;
2079 dist = ta_latin_snap_width(axis->widths, axis->width_count, dist);
2081 if (vertical)
2083 /* in the case of vertical hinting, */
2084 /* always round the stem heights to integer pixels */
2086 if (dist >= 64)
2087 dist = (dist + 16) & ~63;
2088 else
2089 dist = 64;
2091 else
2093 if (TA_LATIN_HINTS_DO_MONO(hints))
2095 /* monochrome horizontal hinting: */
2096 /* snap widths to integer pixels with a different threshold */
2098 if (dist < 64)
2099 dist = 64;
2100 else
2101 dist = (dist + 32) & ~63;
2103 else
2105 /* for horizontal anti-aliased hinting, we adopt a more subtle */
2106 /* approach: we strengthen small stems, round stems whose size */
2107 /* is between 1 and 2 pixels to an integer, otherwise nothing */
2109 if (dist < 48)
2110 dist = (dist + 64) >> 1;
2112 else if (dist < 128)
2114 /* we only round to an integer width if the corresponding */
2115 /* distortion is less than 1/4 pixel -- otherwise, this */
2116 /* makes everything worse since the diagonals, which are */
2117 /* not hinted, appear a lot bolder or thinner than the */
2118 /* vertical stems */
2120 FT_Pos delta;
2123 dist = (dist + 22) & ~63;
2124 delta = dist - org_dist;
2125 if (delta < 0)
2126 delta = -delta;
2128 if (delta >= 16)
2130 dist = org_dist;
2131 if (dist < 48)
2132 dist = (dist + 64) >> 1;
2135 else
2136 /* round otherwise to prevent color fringes in LCD mode */
2137 dist = (dist + 32) & ~63;
2142 Done_Width:
2143 if (sign)
2144 dist = -dist;
2146 return dist;
2150 /* align one stem edge relative to the previous stem edge */
2152 static void
2153 ta_latin_align_linked_edge(TA_GlyphHints hints,
2154 TA_Dimension dim,
2155 TA_Edge base_edge,
2156 TA_Edge stem_edge)
2158 FT_Pos dist = stem_edge->opos - base_edge->opos;
2160 FT_Pos fitted_width = ta_latin_compute_stem_width(
2161 hints, dim, dist,
2162 base_edge->flags,
2163 stem_edge->flags);
2166 stem_edge->pos = base_edge->pos + fitted_width;
2168 TA_LOG((" LINK: edge %d (opos=%.2f) linked to %.2f,"
2169 " dist was %.2f, now %.2f\n",
2170 stem_edge - hints->axis[dim].edges, stem_edge->opos / 64.0,
2171 stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0));
2173 if (hints->recorder)
2174 hints->recorder(ta_link, hints, dim,
2175 base_edge, stem_edge, NULL, NULL, NULL);
2179 /* shift the coordinates of the `serif' edge by the same amount */
2180 /* as the corresponding `base' edge has been moved already */
2182 static void
2183 ta_latin_align_serif_edge(TA_GlyphHints hints,
2184 TA_Edge base,
2185 TA_Edge serif)
2187 FT_UNUSED(hints);
2189 serif->pos = base->pos + (serif->opos - base->opos);
2193 /* the main grid-fitting routine */
2195 void
2196 ta_latin_hint_edges(TA_GlyphHints hints,
2197 TA_Dimension dim)
2199 TA_AxisHints axis = &hints->axis[dim];
2201 TA_Edge edges = axis->edges;
2202 TA_Edge edge_limit = edges + axis->num_edges;
2203 FT_PtrDist n_edges;
2204 TA_Edge edge;
2206 TA_Edge anchor = NULL;
2207 FT_Int has_serifs = 0;
2209 #ifdef TA_DEBUG
2210 FT_UInt num_actions = 0;
2211 #endif
2213 TA_LOG(("latin %s edge hinting (style `%s')\n",
2214 dim == TA_DIMENSION_VERT ? "horizontal" : "vertical",
2215 ta_style_names[hints->metrics->style_class->style]));
2217 /* we begin by aligning all stems relative to the blue zone if needed -- */
2218 /* that's only for horizontal edges */
2220 if (dim == TA_DIMENSION_VERT
2221 && TA_HINTS_DO_BLUES(hints))
2223 for (edge = edges; edge < edge_limit; edge++)
2225 TA_Width blue;
2226 TA_Edge edge1, edge2; /* these edges form the stem to check */
2229 if (edge->flags & TA_EDGE_DONE)
2230 continue;
2232 blue = edge->blue_edge;
2233 edge1 = NULL;
2234 edge2 = edge->link;
2236 if (blue)
2237 edge1 = edge;
2239 /* flip edges if the other stem is aligned to a blue zone */
2240 else if (edge2 && edge2->blue_edge)
2242 blue = edge2->blue_edge;
2243 edge1 = edge2;
2244 edge2 = edge;
2247 if (!edge1)
2248 continue;
2250 #ifdef TA_DEBUG
2251 if (!anchor)
2252 TA_LOG((" BLUE_ANCHOR: edge %d (opos=%.2f) snapped to %.2f,"
2253 " was %.2f (anchor=edge %d)\n",
2254 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
2255 edge1->pos / 64.0, edge - edges));
2256 else
2257 TA_LOG((" BLUE: edge %d (opos=%.2f) snapped to %.2f, was %.2f\n",
2258 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
2259 edge1->pos / 64.0));
2261 num_actions++;
2262 #endif
2264 edge1->pos = blue->fit;
2265 edge1->flags |= TA_EDGE_DONE;
2267 if (hints->recorder)
2269 if (!anchor)
2270 hints->recorder(ta_blue_anchor, hints, dim,
2271 edge1, edge, NULL, NULL, NULL);
2272 else
2273 hints->recorder(ta_blue, hints, dim,
2274 edge1, NULL, NULL, NULL, NULL);
2277 if (edge2 && !edge2->blue_edge)
2279 ta_latin_align_linked_edge(hints, dim, edge1, edge2);
2280 edge2->flags |= TA_EDGE_DONE;
2282 #ifdef TA_DEBUG
2283 num_actions++;
2284 #endif
2287 if (!anchor)
2288 anchor = edge;
2292 /* now we align all other stem edges, */
2293 /* trying to maintain the relative order of stems in the glyph */
2294 for (edge = edges; edge < edge_limit; edge++)
2296 TA_Edge edge2;
2299 if (edge->flags & TA_EDGE_DONE)
2300 continue;
2302 /* skip all non-stem edges */
2303 edge2 = edge->link;
2304 if (!edge2)
2306 has_serifs++;
2307 continue;
2310 /* now align the stem */
2312 /* this should not happen, but it's better to be safe */
2313 if (edge2->blue_edge)
2315 TA_LOG((" ASSERTION FAILED for edge %d\n", edge2-edges));
2317 ta_latin_align_linked_edge(hints, dim, edge2, edge);
2318 edge->flags |= TA_EDGE_DONE;
2320 #ifdef TA_DEBUG
2321 num_actions++;
2322 #endif
2323 continue;
2326 if (!anchor)
2328 /* if we reach this if clause, no stem has been aligned yet */
2330 FT_Pos org_len, org_center, cur_len;
2331 FT_Pos cur_pos1, error1, error2, u_off, d_off;
2334 org_len = edge2->opos - edge->opos;
2335 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2336 edge->flags, edge2->flags);
2338 /* some voodoo to specially round edges for small stem widths; */
2339 /* the idea is to align the center of a stem, */
2340 /* then shifting the stem edges to suitable positions */
2341 if (cur_len <= 64)
2343 /* width <= 1px */
2344 u_off = 32;
2345 d_off = 32;
2347 else
2349 /* 1px < width < 1.5px */
2350 u_off = 38;
2351 d_off = 26;
2354 if (cur_len < 96)
2356 org_center = edge->opos + (org_len >> 1);
2357 cur_pos1 = TA_PIX_ROUND(org_center);
2359 error1 = org_center - (cur_pos1 - u_off);
2360 if (error1 < 0)
2361 error1 = -error1;
2363 error2 = org_center - (cur_pos1 + d_off);
2364 if (error2 < 0)
2365 error2 = -error2;
2367 if (error1 < error2)
2368 cur_pos1 -= u_off;
2369 else
2370 cur_pos1 += d_off;
2372 edge->pos = cur_pos1 - cur_len / 2;
2373 edge2->pos = edge->pos + cur_len;
2375 else
2376 edge->pos = TA_PIX_ROUND(edge->opos);
2378 anchor = edge;
2379 edge->flags |= TA_EDGE_DONE;
2381 TA_LOG((" ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
2382 " snapped to %.2f and %.2f\n",
2383 edge - edges, edge->opos / 64.0,
2384 edge2 - edges, edge2->opos / 64.0,
2385 edge->pos / 64.0, edge2->pos / 64.0));
2387 if (hints->recorder)
2388 hints->recorder(ta_anchor, hints, dim,
2389 edge, edge2, NULL, NULL, NULL);
2391 ta_latin_align_linked_edge(hints, dim, edge, edge2);
2393 #ifdef TA_DEBUG
2394 num_actions += 2;
2395 #endif
2397 else
2399 FT_Pos org_pos, org_len, org_center, cur_len;
2400 FT_Pos cur_pos1, cur_pos2, delta1, delta2;
2403 org_pos = anchor->pos + (edge->opos - anchor->opos);
2404 org_len = edge2->opos - edge->opos;
2405 org_center = org_pos + (org_len >> 1);
2407 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2408 edge->flags, edge2->flags);
2410 if (edge2->flags & TA_EDGE_DONE)
2412 TA_LOG((" ADJUST: edge %d (pos=%.2f) moved to %.2f\n",
2413 edge - edges, edge->pos / 64.0,
2414 (edge2->pos - cur_len) / 64.0));
2416 edge->pos = edge2->pos - cur_len;
2418 if (hints->recorder)
2420 TA_Edge bound = NULL;
2423 if (edge > edges)
2424 bound = &edge[-1];
2426 hints->recorder(ta_adjust, hints, dim,
2427 edge, edge2, NULL, bound, NULL);
2431 else if (cur_len < 96)
2433 FT_Pos u_off, d_off;
2436 cur_pos1 = TA_PIX_ROUND(org_center);
2438 if (cur_len <= 64)
2440 u_off = 32;
2441 d_off = 32;
2443 else
2445 u_off = 38;
2446 d_off = 26;
2449 delta1 = org_center - (cur_pos1 - u_off);
2450 if (delta1 < 0)
2451 delta1 = -delta1;
2453 delta2 = org_center - (cur_pos1 + d_off);
2454 if (delta2 < 0)
2455 delta2 = -delta2;
2457 if (delta1 < delta2)
2458 cur_pos1 -= u_off;
2459 else
2460 cur_pos1 += d_off;
2462 edge->pos = cur_pos1 - cur_len / 2;
2463 edge2->pos = cur_pos1 + cur_len / 2;
2465 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2466 " snapped to %.2f and %.2f\n",
2467 edge - edges, edge->opos / 64.0,
2468 edge2 - edges, edge2->opos / 64.0,
2469 edge->pos / 64.0, edge2->pos / 64.0));
2471 if (hints->recorder)
2473 TA_Edge bound = NULL;
2476 if (edge > edges)
2477 bound = &edge[-1];
2479 hints->recorder(ta_stem, hints, dim,
2480 edge, edge2, NULL, bound, NULL);
2484 else
2486 org_pos = anchor->pos + (edge->opos - anchor->opos);
2487 org_len = edge2->opos - edge->opos;
2488 org_center = org_pos + (org_len >> 1);
2490 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2491 edge->flags, edge2->flags);
2493 cur_pos1 = TA_PIX_ROUND(org_pos);
2494 delta1 = cur_pos1 + (cur_len >> 1) - org_center;
2495 if (delta1 < 0)
2496 delta1 = -delta1;
2498 cur_pos2 = TA_PIX_ROUND(org_pos + org_len) - cur_len;
2499 delta2 = cur_pos2 + (cur_len >> 1) - org_center;
2500 if (delta2 < 0)
2501 delta2 = -delta2;
2503 edge->pos = (delta1 < delta2) ? cur_pos1 : cur_pos2;
2504 edge2->pos = edge->pos + cur_len;
2506 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2507 " snapped to %.2f and %.2f\n",
2508 edge - edges, edge->opos / 64.0,
2509 edge2 - edges, edge2->opos / 64.0,
2510 edge->pos / 64.0, edge2->pos / 64.0));
2512 if (hints->recorder)
2514 TA_Edge bound = NULL;
2517 if (edge > edges)
2518 bound = &edge[-1];
2520 hints->recorder(ta_stem, hints, dim,
2521 edge, edge2, NULL, bound, NULL);
2525 #ifdef TA_DEBUG
2526 num_actions++;
2527 #endif
2529 edge->flags |= TA_EDGE_DONE;
2530 edge2->flags |= TA_EDGE_DONE;
2532 if (edge > edges
2533 && edge->pos < edge[-1].pos)
2535 #ifdef TA_DEBUG
2536 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2537 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
2539 num_actions++;
2540 #endif
2542 edge->pos = edge[-1].pos;
2544 if (hints->recorder)
2545 hints->recorder(ta_bound, hints, dim,
2546 edge, &edge[-1], NULL, NULL, NULL);
2551 /* make sure that lowercase m's maintain their symmetry */
2553 /* In general, lowercase m's have six vertical edges if they are sans */
2554 /* serif, or twelve if they are with serifs. This implementation is */
2555 /* based on that assumption, and seems to work very well with most */
2556 /* faces. However, if for a certain face this assumption is not */
2557 /* true, the m is just rendered like before. In addition, any stem */
2558 /* correction will only be applied to symmetrical glyphs (even if the */
2559 /* glyph is not an m), so the potential for unwanted distortion is */
2560 /* relatively low. */
2562 /* we don't handle horizontal edges since we can't easily assure that */
2563 /* the third (lowest) stem aligns with the base line; it might end up */
2564 /* one pixel higher or lower */
2566 n_edges = edge_limit - edges;
2567 if (dim == TA_DIMENSION_HORZ
2568 && (n_edges == 6 || n_edges == 12))
2570 TA_Edge edge1, edge2, edge3;
2571 FT_Pos dist1, dist2, span, delta;
2574 if (n_edges == 6)
2576 edge1 = edges;
2577 edge2 = edges + 2;
2578 edge3 = edges + 4;
2580 else
2582 edge1 = edges + 1;
2583 edge2 = edges + 5;
2584 edge3 = edges + 9;
2587 dist1 = edge2->opos - edge1->opos;
2588 dist2 = edge3->opos - edge2->opos;
2590 span = dist1 - dist2;
2591 if (span < 0)
2592 span = -span;
2594 if (span < 8)
2596 delta = edge3->pos - (2 * edge2->pos - edge1->pos);
2597 edge3->pos -= delta;
2598 if (edge3->link)
2599 edge3->link->pos -= delta;
2601 /* move the serifs along with the stem */
2602 if (n_edges == 12)
2604 (edges + 8)->pos -= delta;
2605 (edges + 11)->pos -= delta;
2608 edge3->flags |= TA_EDGE_DONE;
2609 if (edge3->link)
2610 edge3->link->flags |= TA_EDGE_DONE;
2614 if (has_serifs || !anchor)
2616 /* now hint the remaining edges (serifs and single) */
2617 /* in order to complete our processing */
2618 for (edge = edges; edge < edge_limit; edge++)
2620 TA_Edge lower_bound = NULL;
2621 TA_Edge upper_bound = NULL;
2623 FT_Pos delta;
2626 if (edge->flags & TA_EDGE_DONE)
2627 continue;
2629 delta = 1000;
2631 if (edge->serif)
2633 delta = edge->serif->opos - edge->opos;
2634 if (delta < 0)
2635 delta = -delta;
2638 if (edge > edges)
2639 lower_bound = &edge[-1];
2641 if (edge + 1 < edge_limit
2642 && edge[1].flags & TA_EDGE_DONE)
2643 upper_bound = &edge[1];
2646 if (delta < 64 + 16)
2648 ta_latin_align_serif_edge(hints, edge->serif, edge);
2650 TA_LOG((" SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
2651 " aligned to %.2f\n",
2652 edge - edges, edge->opos / 64.0,
2653 edge->serif - edges, edge->serif->opos / 64.0,
2654 edge->pos / 64.0));
2656 if (hints->recorder)
2657 hints->recorder(ta_serif, hints, dim,
2658 edge, NULL, NULL, lower_bound, upper_bound);
2660 else if (!anchor)
2662 edge->pos = TA_PIX_ROUND(edge->opos);
2663 anchor = edge;
2665 TA_LOG((" SERIF_ANCHOR: edge %d (opos=%.2f) snapped to %.2f\n",
2666 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
2668 if (hints->recorder)
2669 hints->recorder(ta_serif_anchor, hints, dim,
2670 edge, NULL, NULL, lower_bound, upper_bound);
2672 else
2674 TA_Edge before, after;
2677 for (before = edge - 1; before >= edges; before--)
2678 if (before->flags & TA_EDGE_DONE)
2679 break;
2681 for (after = edge + 1; after < edge_limit; after++)
2682 if (after->flags & TA_EDGE_DONE)
2683 break;
2685 if (before >= edges && before < edge
2686 && after < edge_limit && after > edge)
2688 if (after->opos == before->opos)
2689 edge->pos = before->pos;
2690 else
2691 edge->pos = before->pos + FT_MulDiv(edge->opos - before->opos,
2692 after->pos - before->pos,
2693 after->opos - before->opos);
2695 TA_LOG((" SERIF_LINK1: edge %d (opos=%.2f) snapped to %.2f"
2696 " from %d (opos=%.2f)\n",
2697 edge - edges, edge->opos / 64.0,
2698 edge->pos / 64.0,
2699 before - edges, before->opos / 64.0));
2701 if (hints->recorder)
2702 hints->recorder(ta_serif_link1, hints, dim,
2703 edge, before, after, lower_bound, upper_bound);
2705 else
2707 edge->pos = anchor->pos + ((edge->opos - anchor->opos + 16) & ~31);
2708 TA_LOG((" SERIF_LINK2: edge %d (opos=%.2f) snapped to %.2f\n",
2709 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
2711 if (hints->recorder)
2712 hints->recorder(ta_serif_link2, hints, dim,
2713 edge, NULL, NULL, lower_bound, upper_bound);
2717 #ifdef TA_DEBUG
2718 num_actions++;
2719 #endif
2720 edge->flags |= TA_EDGE_DONE;
2722 if (edge > edges
2723 && edge->pos < edge[-1].pos)
2725 #ifdef TA_DEBUG
2726 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2727 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
2728 num_actions++;
2729 #endif
2731 edge->pos = edge[-1].pos;
2733 if (hints->recorder)
2734 hints->recorder(ta_bound, hints, dim,
2735 edge, &edge[-1], NULL, NULL, NULL);
2738 if (edge + 1 < edge_limit
2739 && edge[1].flags & TA_EDGE_DONE
2740 && edge->pos > edge[1].pos)
2742 #ifdef TA_DEBUG
2743 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2744 edge - edges, edge->pos / 64.0, edge[1].pos / 64.0));
2746 num_actions++;
2747 #endif
2749 edge->pos = edge[1].pos;
2751 if (hints->recorder)
2752 hints->recorder(ta_bound, hints, dim,
2753 edge, &edge[1], NULL, NULL, NULL);
2758 #ifdef TA_DEBUG
2759 if (!num_actions)
2760 TA_LOG((" (none)\n"));
2761 TA_LOG(("\n"));
2762 #endif
2766 /* apply the complete hinting algorithm to a latin glyph */
2768 static FT_Error
2769 ta_latin_hints_apply(TA_GlyphHints hints,
2770 FT_Outline* outline,
2771 TA_LatinMetrics metrics)
2773 FT_Error error;
2774 int dim;
2777 error = ta_glyph_hints_reload(hints, outline);
2778 if (error)
2779 goto Exit;
2781 /* analyze glyph outline */
2782 #ifdef TA_CONFIG_OPTION_USE_WARPER
2783 if (metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT
2784 || TA_HINTS_DO_HORIZONTAL(hints))
2785 #else
2786 if (TA_HINTS_DO_HORIZONTAL(hints))
2787 #endif
2789 error = ta_latin_hints_detect_features(hints, TA_DIMENSION_HORZ);
2790 if (error)
2791 goto Exit;
2794 if (TA_HINTS_DO_VERTICAL(hints))
2796 error = ta_latin_hints_detect_features(hints, TA_DIMENSION_VERT);
2797 if (error)
2798 goto Exit;
2800 ta_latin_hints_compute_blue_edges(hints, metrics);
2803 /* grid-fit the outline */
2804 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
2806 #ifdef TA_CONFIG_OPTION_USE_WARPER
2807 if (dim == TA_DIMENSION_HORZ
2808 && metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT)
2810 TA_WarperRec warper;
2811 FT_Fixed scale;
2812 FT_Pos delta;
2815 ta_warper_compute(&warper, hints, (TA_Dimension)dim, &scale, &delta);
2816 ta_glyph_hints_scale_dim(hints, (TA_Dimension)dim, scale, delta);
2818 continue;
2820 #endif
2822 if ((dim == TA_DIMENSION_HORZ && TA_HINTS_DO_HORIZONTAL(hints))
2823 || (dim == TA_DIMENSION_VERT && TA_HINTS_DO_VERTICAL(hints)))
2825 ta_latin_hint_edges(hints, (TA_Dimension)dim);
2826 ta_glyph_hints_align_edge_points(hints, (TA_Dimension)dim);
2827 ta_glyph_hints_align_strong_points(hints, (TA_Dimension)dim);
2828 ta_glyph_hints_align_weak_points(hints, (TA_Dimension)dim);
2832 ta_glyph_hints_save(hints, outline);
2834 Exit:
2835 return error;
2839 const TA_WritingSystemClassRec ta_latin_writing_system_class =
2841 TA_WRITING_SYSTEM_LATIN,
2843 sizeof (TA_LatinMetricsRec),
2845 (TA_WritingSystem_InitMetricsFunc)ta_latin_metrics_init,
2846 (TA_WritingSystem_ScaleMetricsFunc)ta_latin_metrics_scale,
2847 (TA_WritingSystem_DoneMetricsFunc)NULL,
2849 (TA_WritingSystem_InitHintsFunc)ta_latin_hints_init,
2850 (TA_WritingSystem_ApplyHintsFunc)ta_latin_hints_apply
2853 /* end of talatin.c */