Updates to `NEWS' and documentation.
[ttfautohint.git] / lib / talatin.c
blob3cce63ab78b43f7bcca2bbb74528aad7d98d0e59
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,
44 FT_Bool use_cmap)
46 /* scan the array of segments in each direction */
47 TA_GlyphHintsRec hints[1];
50 TA_LOG_GLOBAL(("\n"
51 "latin standard widths computation (style `%s')\n"
52 "=====================================================\n"
53 "\n",
54 ta_style_names[metrics->root.style_class->style]));
56 ta_glyph_hints_init(hints);
58 metrics->axis[TA_DIMENSION_HORZ].width_count = 0;
59 metrics->axis[TA_DIMENSION_VERT].width_count = 0;
62 FT_Error error;
63 FT_ULong glyph_index;
64 FT_Long y_offset;
65 int dim;
66 TA_LatinMetricsRec dummy[1];
67 TA_Scaler scaler = &dummy->root.scaler;
69 TA_StyleClass style_class = metrics->root.style_class;
70 TA_ScriptClass script_class = ta_script_classes[style_class->script];
72 FT_UInt32 standard_char;
75 if (!use_cmap)
76 goto Exit;
79 * We check more than a single standard character to catch features
80 * like `c2sc' (small caps from caps) that don't contain lowercase
81 * letters by definition, or other features that mainly operate on
82 * numerals.
84 standard_char = script_class->standard_char1;
85 ta_get_char_index(&metrics->root,
86 standard_char,
87 &glyph_index,
88 &y_offset);
89 if (!glyph_index)
91 if (script_class->standard_char2)
93 standard_char = script_class->standard_char2;
94 ta_get_char_index(&metrics->root,
95 standard_char,
96 &glyph_index,
97 &y_offset);
98 if (!glyph_index)
100 if (script_class->standard_char3)
102 standard_char = script_class->standard_char3;
103 ta_get_char_index(&metrics->root,
104 standard_char,
105 &glyph_index,
106 &y_offset);
107 if (!glyph_index)
108 goto Exit;
110 else
111 goto Exit;
114 else
115 goto Exit;
118 TA_LOG_GLOBAL(("standard character: U+%04lX (glyph index %d)\n",
119 standard_char, glyph_index));
121 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
122 if (error || face->glyph->outline.n_points <= 0)
123 goto Exit;
125 memset(dummy, 0, sizeof (TA_LatinMetricsRec));
127 dummy->units_per_em = metrics->units_per_em;
129 scaler->x_scale = 0x10000L;
130 scaler->y_scale = 0x10000L;
131 scaler->x_delta = 0;
132 scaler->y_delta = 0;
134 scaler->face = face;
135 scaler->render_mode = FT_RENDER_MODE_NORMAL;
136 scaler->flags = 0;
138 ta_glyph_hints_rescale(hints, (TA_StyleMetrics)dummy);
140 error = ta_glyph_hints_reload(hints, &face->glyph->outline);
141 if (error)
142 goto Exit;
144 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
146 TA_LatinAxis axis = &metrics->axis[dim];
147 TA_AxisHints axhints = &hints->axis[dim];
149 TA_Segment seg, limit, link;
150 FT_UInt num_widths = 0;
153 error = ta_latin_hints_compute_segments(hints, (TA_Dimension)dim);
154 if (error)
155 goto Exit;
158 * We assume that the glyphs selected for the stem width
159 * computation are `featureless' enough so that the linking
160 * algorithm works fine without adjustments of its scoring
161 * function.
163 ta_latin_hints_link_segments(hints, 0, NULL, (TA_Dimension)dim);
165 seg = axhints->segments;
166 limit = seg + axhints->num_segments;
168 for (; seg < limit; seg++)
170 link = seg->link;
172 /* we only consider stem segments there! */
173 if (link
174 && link->link == seg
175 && link > seg)
177 FT_Pos dist;
180 dist = seg->pos - link->pos;
181 if (dist < 0)
182 dist = -dist;
184 if (num_widths < TA_LATIN_MAX_WIDTHS)
185 axis->widths[num_widths++].org = dist;
189 /* this also replaces multiple almost identical stem widths */
190 /* with a single one (the value 100 is heuristic) */
191 ta_sort_and_quantize_widths(&num_widths, axis->widths,
192 dummy->units_per_em / 100);
193 axis->width_count = num_widths;
196 Exit:
197 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
199 FONT* font = metrics->root.globals->font;
200 TA_LatinAxis axis = &metrics->axis[dim];
201 FT_Pos stdw;
204 if (!axis->width_count)
206 /* if we have no standard characters, */
207 /* use `fallback-stem-width', if available, */
208 /* or a default width (value 50 is heuristic) */
209 stdw = (dim == TA_DIMENSION_VERT && font->fallback_stem_width)
210 ? (FT_Pos)font->fallback_stem_width
211 : TA_LATIN_CONSTANT(metrics, 50);
213 /* set one width value if we do hinting */
214 if (style_class->style != TA_STYLE_NONE_DFLT)
216 axis->width_count++;
217 axis->widths[0].org = stdw;
221 stdw = axis->widths[0].org;
223 /* let's try 20% of the smallest width */
224 axis->edge_distance_threshold = stdw / 5;
225 axis->standard_width = stdw;
226 axis->extra_light = 0;
228 #ifdef TA_DEBUG
230 FT_UInt i;
233 TA_LOG_GLOBAL(("%s widths:\n",
234 dim == TA_DIMENSION_VERT ? "horizontal"
235 : "vertical"));
237 TA_LOG_GLOBAL((" %d (standard)", axis->standard_width));
238 for (i = 1; i < axis->width_count; i++)
239 TA_LOG_GLOBAL((" %d", axis->widths[i].org));
241 TA_LOG_GLOBAL(("\n"));
243 #endif
247 TA_LOG_GLOBAL(("\n"));
249 ta_glyph_hints_done(hints);
253 /* find all blue zones; flat segments give the reference points, */
254 /* round segments the overshoot positions */
256 static void
257 ta_latin_metrics_init_blues(TA_LatinMetrics metrics,
258 FT_Face face)
260 FT_Pos flats[TA_BLUE_STRING_MAX_LEN];
261 FT_Pos rounds[TA_BLUE_STRING_MAX_LEN];
262 FT_Int num_flats;
263 FT_Int num_rounds;
265 TA_LatinBlue blue;
266 FT_Error error;
267 TA_LatinAxis axis = &metrics->axis[TA_DIMENSION_VERT];
268 FT_Outline outline;
270 TA_StyleClass sc = metrics->root.style_class;
272 TA_Blue_Stringset bss = sc->blue_stringset;
273 const TA_Blue_StringRec* bs = &ta_blue_stringsets[bss];
276 /* we walk over the blue character strings as specified in the */
277 /* style's entry in the `ta_blue_stringset' array */
279 TA_LOG_GLOBAL(("latin blue zones computation\n"
280 "============================\n"
281 "\n"));
283 for (; bs->string != TA_BLUE_STRING_MAX; bs++)
285 const char* p = &ta_blue_strings[bs->string];
286 FT_Pos* blue_ref;
287 FT_Pos* blue_shoot;
290 #ifdef TA_DEBUG
292 FT_Bool have_flag = 0;
295 TA_LOG_GLOBAL(("blue zone %d", axis->blue_count));
297 if (bs->properties)
299 TA_LOG_GLOBAL((" ("));
301 if (TA_LATIN_IS_TOP_BLUE(bs))
303 TA_LOG_GLOBAL(("top"));
304 have_flag = 1;
307 if (TA_LATIN_IS_NEUTRAL_BLUE(bs))
309 if (have_flag)
310 TA_LOG_GLOBAL((", "));
311 TA_LOG_GLOBAL(("neutral"));
312 have_flag = 1;
315 if (TA_LATIN_IS_X_HEIGHT_BLUE(bs))
317 if (have_flag)
318 TA_LOG_GLOBAL((", "));
319 TA_LOG_GLOBAL(("small top"));
320 have_flag = 1;
323 if (TA_LATIN_IS_LONG_BLUE(bs))
325 if (have_flag)
326 TA_LOG_GLOBAL((", "));
327 TA_LOG_GLOBAL(("long"));
330 TA_LOG_GLOBAL((")"));
333 TA_LOG_GLOBAL((":\n"));
335 #endif /* TA_DEBUG */
337 num_flats = 0;
338 num_rounds = 0;
340 while (*p)
342 FT_ULong ch;
343 FT_ULong glyph_index;
344 FT_Long y_offset;
345 FT_Pos best_y; /* same as points.y */
346 FT_Int best_point, best_contour_first, best_contour_last;
347 FT_Vector* points;
348 FT_Bool round = 0;
351 GET_UTF8_CHAR(ch, p);
353 /* load the character in the face -- skip unknown or empty ones */
354 ta_get_char_index(&metrics->root, ch, &glyph_index, &y_offset);
355 if (glyph_index == 0)
357 TA_LOG_GLOBAL((" U+%04lX unavailable\n", ch));
358 continue;
361 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
362 outline = face->glyph->outline;
363 if (error || outline.n_points <= 0)
365 TA_LOG_GLOBAL((" U+%04lX contains no outlines\n", ch));
366 continue;
369 /* now compute min or max point indices and coordinates */
370 points = outline.points;
371 best_point = -1;
372 best_y = 0; /* make compiler happy */
373 best_contour_first = 0; /* ditto */
374 best_contour_last = 0; /* ditto */
377 FT_Int nn;
378 FT_Int first = 0;
379 FT_Int last = -1;
382 for (nn = 0; nn < outline.n_contours; first = last + 1, nn++)
384 FT_Int old_best_point = best_point;
385 FT_Int pp;
388 last = outline.contours[nn];
390 /* avoid single-point contours since they are never rasterized; */
391 /* in some fonts, they correspond to mark attachment points */
392 /* that are way outside of the glyph's real outline */
393 if (last <= first)
394 continue;
396 if (TA_LATIN_IS_TOP_BLUE(bs))
398 for (pp = first; pp <= last; pp++)
399 if (best_point < 0
400 || points[pp].y > best_y)
402 best_point = pp;
403 best_y = points[pp].y;
406 else
408 for (pp = first; pp <= last; pp++)
409 if (best_point < 0
410 || points[pp].y < best_y)
412 best_point = pp;
413 best_y = points[pp].y;
417 if (best_point != old_best_point)
419 best_contour_first = first;
420 best_contour_last = last;
425 /* now check whether the point belongs to a straight or round */
426 /* segment; we first need to find in which contour the extremum */
427 /* lies, then inspect its previous and next points */
428 if (best_point >= 0)
430 FT_Pos best_x = points[best_point].x;
431 FT_Int prev, next;
432 FT_Int best_segment_first, best_segment_last;
433 FT_Int best_on_point_first, best_on_point_last;
434 FT_Pos dist;
437 best_segment_first = best_point;
438 best_segment_last = best_point;
440 if (FT_CURVE_TAG(outline.tags[best_point]) == FT_CURVE_TAG_ON)
442 best_on_point_first = best_point;
443 best_on_point_last = best_point;
445 else
447 best_on_point_first = -1;
448 best_on_point_last = -1;
451 /* look for the previous and next points on the contour */
452 /* that are not on the same Y coordinate, then threshold */
453 /* the `closeness'... */
454 prev = best_point;
455 next = prev;
459 if (prev > best_contour_first)
460 prev--;
461 else
462 prev = best_contour_last;
464 dist = TA_ABS(points[prev].y - best_y);
465 /* accept a small distance or a small angle (both values are */
466 /* heuristic; value 20 corresponds to approx. 2.9 degrees) */
467 if (dist > 5)
468 if (TA_ABS(points[prev].x - best_x) <= 20 * dist)
469 break;
471 best_segment_first = prev;
473 if (FT_CURVE_TAG(outline.tags[prev]) == FT_CURVE_TAG_ON)
475 best_on_point_first = prev;
476 if (best_on_point_last < 0)
477 best_on_point_last = prev;
480 } while (prev != best_point);
484 if (next < best_contour_last)
485 next++;
486 else
487 next = best_contour_first;
489 dist = TA_ABS(points[next].y - best_y);
490 if (dist > 5)
491 if (TA_ABS(points[next].x - best_x) <= 20 * dist)
492 break;
494 best_segment_last = next;
496 if (FT_CURVE_TAG(outline.tags[next]) == FT_CURVE_TAG_ON)
498 best_on_point_last = next;
499 if (best_on_point_first < 0)
500 best_on_point_first = next;
503 } while (next != best_point);
505 if (TA_LATIN_IS_LONG_BLUE(bs))
507 /* If this flag is set, we have an additional constraint to */
508 /* get the blue zone distance: Find a segment of the topmost */
509 /* (or bottommost) contour that is longer than a heuristic */
510 /* threshold. This ensures that small bumps in the outline */
511 /* are ignored (for example, the `vertical serifs' found in */
512 /* many Hebrew glyph designs). */
514 /* If this segment is long enough, we are done. Otherwise, */
515 /* search the segment next to the extremum that is long */
516 /* enough, has the same direction, and a not too large */
517 /* vertical distance from the extremum. Note that the */
518 /* algorithm doesn't check whether the found segment is */
519 /* actually the one (vertically) nearest to the extremum. */
521 /* heuristic threshold value */
522 FT_Pos length_threshold = metrics->units_per_em / 25;
525 dist = TA_ABS(points[best_segment_last].x -
526 points[best_segment_first].x);
528 if (dist < length_threshold
529 && best_segment_last - best_segment_first + 2 <=
530 best_contour_last - best_contour_first)
532 /* heuristic threshold value */
533 FT_Pos height_threshold = metrics->units_per_em / 4;
535 FT_Int first;
536 FT_Int last;
537 FT_Bool hit;
539 /* we intentionally declare these two variables */
540 /* outside of the loop since various compilers emit */
541 /* incorrect warning messages otherwise, talking about */
542 /* `possibly uninitialized variables' */
543 FT_Int p_first = 0; /* make compiler happy */
544 FT_Int p_last = 0;
546 FT_Bool left2right;
549 /* compute direction */
550 prev = best_point;
554 if (prev > best_contour_first)
555 prev--;
556 else
557 prev = best_contour_last;
559 if (points[prev].x != best_x)
560 break;
561 } while (prev != best_point);
563 /* skip glyph for the degenerate case */
564 if (prev == best_point)
565 continue;
567 left2right = FT_BOOL(points[prev].x < points[best_point].x);
569 first = best_segment_last;
570 last = first;
571 hit = 0;
575 FT_Bool l2r;
576 FT_Pos d;
579 if (!hit)
581 /* no hit; adjust first point */
582 first = last;
584 /* also adjust first and last on point */
585 if (FT_CURVE_TAG(outline.tags[first]) == FT_CURVE_TAG_ON)
587 p_first = first;
588 p_last = first;
590 else
592 p_first = -1;
593 p_last = -1;
596 hit = 1;
599 if (last < best_contour_last)
600 last++;
601 else
602 last = best_contour_first;
604 if (TA_ABS(best_y - points[first].y) > height_threshold)
606 /* vertical distance too large */
607 hit = 0;
608 continue;
611 /* same test as above */
612 dist = TA_ABS(points[last].y - points[first].y);
613 if (dist > 5)
614 if (TA_ABS(points[last].x - points[first].x) <= 20 * dist)
616 hit = 0;
617 continue;
620 if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON)
622 p_last = last;
623 if (p_first < 0)
624 p_first = last;
627 l2r = FT_BOOL(points[first].x < points[last].x);
628 d = TA_ABS(points[last].x - points[first].x);
630 if (l2r == left2right
631 && d >= length_threshold)
633 /* all constraints are met; update segment after finding */
634 /* its end */
637 if (last < best_contour_last)
638 last++;
639 else
640 last = best_contour_first;
642 d = TA_ABS(points[last].y - points[first].y);
643 if (d > 5)
644 if (TA_ABS(points[next].x - points[first].x) <=
645 20 * dist)
647 if (last > best_contour_first)
648 last--;
649 else
650 last = best_contour_last;
651 break;
654 p_last = last;
656 if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON)
658 p_last = last;
659 if (p_first < 0)
660 p_first = last;
662 } while (last != best_segment_first);
664 best_y = points[first].y;
666 best_segment_first = first;
667 best_segment_last = last;
669 best_on_point_first = p_first;
670 best_on_point_last = p_last;
672 break;
674 } while (last != best_segment_first);
679 * for computing blue zones, we add the y offset as returned
680 * by the currently used OpenType feature --
681 * for example, superscript glyphs might be identical
682 * to subscript glyphs with a vertical shift
684 best_y += y_offset;
686 TA_LOG_GLOBAL((" U+%04lX: best_y = %5ld", ch, best_y));
689 * now set the `round' flag depending on the segment's kind:
691 * - if the horizontal distance between the first and last
692 * `on' point is larger than upem/8 (value 8 is heuristic)
693 * we have a flat segment
694 * - if either the first or the last point of the segment is
695 * an `off' point, the segment is round, otherwise it is
696 * flat
698 if (best_on_point_first >= 0
699 && best_on_point_last >= 0
700 && (FT_UInt)(TA_ABS(points[best_on_point_last].x
701 - points[best_on_point_first].x))
702 > metrics->units_per_em / 8)
703 round = 0;
704 else
705 round = FT_BOOL(FT_CURVE_TAG(outline.tags[best_segment_first])
706 != FT_CURVE_TAG_ON
707 || FT_CURVE_TAG(outline.tags[best_segment_last])
708 != FT_CURVE_TAG_ON);
710 if (round && TA_LATIN_IS_NEUTRAL_BLUE(bs))
712 /* only use flat segments for a neutral blue zone */
713 TA_LOG_GLOBAL((" (round, skipped)\n"));
714 continue;
717 TA_LOG_GLOBAL((" (%s)\n", round ? "round" : "flat"));
720 if (round)
721 rounds[num_rounds++] = best_y;
722 else
723 flats[num_flats++] = best_y;
726 if (num_flats == 0 && num_rounds == 0)
728 /* we couldn't find a single glyph to compute this blue zone, */
729 /* we will simply ignore it then */
730 TA_LOG_GLOBAL((" empty\n"));
731 continue;
734 /* we have computed the contents of the `rounds' and `flats' tables, */
735 /* now determine the reference and overshoot position of the blue -- */
736 /* we simply take the median value after a simple sort */
737 ta_sort_pos(num_rounds, rounds);
738 ta_sort_pos(num_flats, flats);
740 blue = &axis->blues[axis->blue_count];
741 blue_ref = &blue->ref.org;
742 blue_shoot = &blue->shoot.org;
744 axis->blue_count++;
746 if (num_flats == 0)
748 *blue_ref =
749 *blue_shoot = rounds[num_rounds / 2];
751 else if (num_rounds == 0)
753 *blue_ref =
754 *blue_shoot = flats[num_flats / 2];
756 else
758 *blue_ref = flats[num_flats / 2];
759 *blue_shoot = rounds[num_rounds / 2];
762 /* there are sometimes problems if the overshoot position of top */
763 /* zones is under its reference position, or the opposite for bottom */
764 /* zones; we must thus check everything there and correct the errors */
765 if (*blue_shoot != *blue_ref)
767 FT_Pos ref = *blue_ref;
768 FT_Pos shoot = *blue_shoot;
769 FT_Bool over_ref = FT_BOOL(shoot > ref);
772 if (TA_LATIN_IS_TOP_BLUE(bs) ^ over_ref)
774 *blue_ref =
775 *blue_shoot = (shoot + ref) / 2;
777 TA_LOG_GLOBAL((" [overshoot smaller than reference,"
778 " taking mean value]\n"));
782 blue->flags = 0;
783 if (TA_LATIN_IS_TOP_BLUE(bs))
784 blue->flags |= TA_LATIN_BLUE_TOP;
785 if (TA_LATIN_IS_NEUTRAL_BLUE(bs))
786 blue->flags |= TA_LATIN_BLUE_NEUTRAL;
788 /* the following flag is used later to adjust the y and x scales */
789 /* in order to optimize the pixel grid alignment */
790 /* of the top of small letters */
791 if (TA_LATIN_IS_X_HEIGHT_BLUE(bs))
792 blue->flags |= TA_LATIN_BLUE_ADJUSTMENT;
794 TA_LOG_GLOBAL((" -> reference = %ld\n"
795 " overshoot = %ld\n",
796 *blue_ref, *blue_shoot));
799 /* add two blue zones for usWinAscent and usWinDescent */
800 /* just in case the above algorithm has missed them -- */
801 /* Windows cuts off everything outside of those two values */
803 TT_OS2* os2;
806 os2 = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
808 if (os2)
810 blue = &axis->blues[axis->blue_count];
811 blue->flags = TA_LATIN_BLUE_TOP | TA_LATIN_BLUE_ACTIVE;
812 blue->ref.org =
813 blue->shoot.org = os2->usWinAscent;
815 TA_LOG_GLOBAL(("artificial blue zone for usWinAscent:\n"
816 " -> reference = %ld\n"
817 " overshoot = %ld\n",
818 blue->ref.org, blue->shoot.org));
820 blue = &axis->blues[axis->blue_count + 1];
821 blue->flags = TA_LATIN_BLUE_ACTIVE;
822 blue->ref.org =
823 blue->shoot.org = -os2->usWinDescent;
825 TA_LOG_GLOBAL(("artificial blue zone for usWinDescent:\n"
826 " -> reference = %ld\n"
827 " overshoot = %ld\n",
828 blue->ref.org, blue->shoot.org));
830 else
832 blue = &axis->blues[axis->blue_count];
833 blue->flags =
834 blue->ref.org =
835 blue->shoot.org = 0;
837 blue = &axis->blues[axis->blue_count + 1];
838 blue->flags =
839 blue->ref.org =
840 blue->shoot.org = 0;
844 TA_LOG_GLOBAL(("\n"));
846 return;
850 /* check whether all ASCII digits have the same advance width */
852 void
853 ta_latin_metrics_check_digits(TA_LatinMetrics metrics,
854 FT_Face face)
856 FT_UInt i;
857 FT_Bool started = 0, same_width = 1;
858 FT_Fixed advance, old_advance = 0;
861 /* digit `0' is 0x30 in all supported charmaps */
862 for (i = 0x30; i <= 0x39; i++)
864 FT_ULong glyph_index;
865 FT_Long y_offset;
868 ta_get_char_index(&metrics->root, i, &glyph_index, &y_offset);
869 if (glyph_index == 0)
870 continue;
872 if (FT_Get_Advance(face, glyph_index,
873 FT_LOAD_NO_SCALE
874 | FT_LOAD_NO_HINTING
875 | FT_LOAD_IGNORE_TRANSFORM,
876 &advance))
877 continue;
879 if (started)
881 if (advance != old_advance)
883 same_width = 0;
884 break;
887 else
889 old_advance = advance;
890 started = 1;
894 metrics->root.digits_have_same_width = same_width;
898 /* initialize global metrics */
900 FT_Error
901 ta_latin_metrics_init(TA_LatinMetrics metrics,
902 FT_Face face)
904 FT_CharMap oldmap = face->charmap;
907 metrics->units_per_em = face->units_per_EM;
909 if (!FT_Select_Charmap(face, FT_ENCODING_UNICODE))
911 ta_latin_metrics_init_widths(metrics, face, 1);
912 ta_latin_metrics_init_blues(metrics, face);
913 ta_latin_metrics_check_digits(metrics, face);
915 else
917 /* we only have a symbol font encoding */
918 ta_latin_metrics_init_widths(metrics, face, 0);
921 FT_Set_Charmap(face, oldmap);
922 return FT_Err_Ok;
926 /* adjust scaling value, then scale and shift widths */
927 /* and blue zones (if applicable) for given dimension */
929 static void
930 ta_latin_metrics_scale_dim(TA_LatinMetrics metrics,
931 TA_Scaler scaler,
932 TA_Dimension dim)
934 FT_Fixed scale;
935 FT_Pos delta;
936 TA_LatinAxis axis;
937 FT_UInt ppem;
938 FT_UInt nn;
941 ppem = metrics->root.scaler.face->size->metrics.x_ppem;
943 if (dim == TA_DIMENSION_HORZ)
945 scale = scaler->x_scale;
946 delta = scaler->x_delta;
948 else
950 scale = scaler->y_scale;
951 delta = scaler->y_delta;
954 axis = &metrics->axis[dim];
956 if (axis->org_scale == scale && axis->org_delta == delta)
957 return;
959 axis->org_scale = scale;
960 axis->org_delta = delta;
962 /* correct Y scale to optimize the alignment of the top of */
963 /* small letters to the pixel grid */
964 /* (if we do x-height snapping for this ppem value) */
965 if (!number_set_is_element(
966 metrics->root.globals->font->x_height_snapping_exceptions,
967 ppem))
969 TA_LatinAxis Axis = &metrics->axis[TA_DIMENSION_VERT];
970 TA_LatinBlue blue = NULL;
973 for (nn = 0; nn < Axis->blue_count; nn++)
975 if (Axis->blues[nn].flags & TA_LATIN_BLUE_ADJUSTMENT)
977 blue = &Axis->blues[nn];
978 break;
982 if (blue)
984 FT_Pos scaled;
985 FT_Pos threshold;
986 FT_Pos fitted;
987 FT_UInt limit;
990 scaled = FT_MulFix(blue->shoot.org, scaler->y_scale);
991 limit = metrics->root.globals->increase_x_height;
992 threshold = 40;
994 /* if the `increase-x-height' property is active, */
995 /* we round up much more often */
996 if (limit
997 && ppem <= limit
998 && ppem >= TA_PROP_INCREASE_X_HEIGHT_MIN)
999 threshold = 52;
1001 fitted = (scaled + threshold) & ~63;
1003 if (scaled != fitted)
1005 if (dim == TA_DIMENSION_VERT)
1007 scale = FT_MulDiv(scale, fitted, scaled);
1009 TA_LOG_GLOBAL((
1010 "ta_latin_metrics_scale_dim:"
1011 " x height alignment (style `%s'):\n"
1013 " vertical scaling changed from %.4f to %.4f (by %d%%)\n"
1014 "\n",
1015 ta_style_names[metrics->root.style_class->style],
1016 axis->org_scale / 65536.0,
1017 scale / 65536.0,
1018 (fitted - scaled) * 100 / scaled));
1024 axis->scale = scale;
1025 axis->delta = delta;
1027 if (dim == TA_DIMENSION_HORZ)
1029 metrics->root.scaler.x_scale = scale;
1030 metrics->root.scaler.x_delta = delta;
1032 else
1034 metrics->root.scaler.y_scale = scale;
1035 metrics->root.scaler.y_delta = delta;
1038 TA_LOG_GLOBAL(("%s widths (style `%s')\n",
1039 dim == TA_DIMENSION_HORZ ? "horizontal" : "vertical",
1040 ta_style_names[metrics->root.style_class->style]));
1042 /* scale the widths */
1043 for (nn = 0; nn < axis->width_count; nn++)
1045 TA_Width width = axis->widths + nn;
1048 width->cur = FT_MulFix(width->org, scale);
1049 width->fit = width->cur;
1051 TA_LOG_GLOBAL((" %d scaled to %.2f\n",
1052 width->org,
1053 width->cur / 64.0));
1056 TA_LOG_GLOBAL(("\n"));
1058 /* an extra-light axis corresponds to a standard width that is */
1059 /* smaller than 5/8 pixels */
1060 axis->extra_light =
1061 (FT_Bool)(FT_MulFix(axis->standard_width, scale) < 32 + 8);
1063 #ifdef TA_DEBUG
1064 if (axis->extra_light)
1065 TA_LOG_GLOBAL(("`%s' style is extra light (at current resolution)\n"
1066 "\n",
1067 ta_style_names[metrics->root.style_class->style]));
1068 #endif
1070 if (dim == TA_DIMENSION_VERT)
1072 TA_LOG_GLOBAL(("blue zones (style `%s')\n",
1073 ta_style_names[metrics->root.style_class->style]));
1075 /* scale the blue zones */
1076 for (nn = 0; nn < axis->blue_count; nn++)
1078 TA_LatinBlue blue = &axis->blues[nn];
1079 FT_Pos dist;
1082 blue->ref.cur = FT_MulFix(blue->ref.org, scale) + delta;
1083 blue->ref.fit = blue->ref.cur;
1084 blue->shoot.cur = FT_MulFix(blue->shoot.org, scale) + delta;
1085 blue->shoot.fit = blue->shoot.cur;
1086 blue->flags &= ~TA_LATIN_BLUE_ACTIVE;
1088 /* a blue zone is only active if it is less than 3/4 pixels tall */
1089 dist = FT_MulFix(blue->ref.org - blue->shoot.org, scale);
1090 if (dist <= 48 && dist >= -48)
1092 #if 0
1093 FT_Pos delta1;
1094 #endif
1095 FT_Pos delta2;
1098 /* use discrete values for blue zone widths */
1100 #if 0
1101 /* generic, original code */
1102 delta1 = blue->shoot.org - blue->ref.org;
1103 delta2 = delta1;
1104 if (delta1 < 0)
1105 delta2 = -delta2;
1107 delta2 = FT_MulFix(delta2, scale);
1109 if (delta2 < 32)
1110 delta2 = 0;
1111 else if (delta2 < 64)
1112 delta2 = 32 + (((delta2 - 32) + 16) & ~31);
1113 else
1114 delta2 = TA_PIX_ROUND(delta2);
1116 if (delta1 < 0)
1117 delta2 = -delta2;
1119 blue->ref.fit = TA_PIX_ROUND(blue->ref.cur);
1120 blue->shoot.fit = blue->ref.fit + delta2;
1121 #else
1122 /* simplified version due to abs(dist) <= 48 */
1123 delta2 = dist;
1124 if (dist < 0)
1125 delta2 = -delta2;
1127 if (delta2 < 32)
1128 delta2 = 0;
1129 else if (delta2 < 48)
1130 delta2 = 32;
1131 else
1132 delta2 = 64;
1134 if (dist < 0)
1135 delta2 = -delta2;
1137 blue->ref.fit = TA_PIX_ROUND(blue->ref.cur);
1138 blue->shoot.fit = blue->ref.fit - delta2;
1139 #endif
1141 blue->flags |= TA_LATIN_BLUE_ACTIVE;
1143 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f%s\n"
1144 " overshoot %d: %d scaled to %.2f%s\n",
1146 blue->ref.org,
1147 blue->ref.fit / 64.0,
1148 blue->flags & TA_LATIN_BLUE_ACTIVE ? ""
1149 : " (inactive)",
1151 blue->shoot.org,
1152 blue->shoot.fit / 64.0,
1153 blue->flags & TA_LATIN_BLUE_ACTIVE ? ""
1154 : " (inactive)"));
1158 /* the last two artificial blue zones are to be scaled */
1159 /* with uncorrected scaling values */
1161 TA_LatinAxis a = &metrics->axis[TA_DIMENSION_VERT];
1162 TA_LatinBlue b;
1165 b = &a->blues[a->blue_count];
1166 b->ref.cur =
1167 b->ref.fit =
1168 b->shoot.cur =
1169 b->shoot.fit = FT_MulFix(b->ref.org, a->org_scale) + delta;
1171 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f (artificial)\n"
1172 " overshoot %d: %d scaled to %.2f (artificial)\n",
1173 a->blue_count,
1174 b->ref.org,
1175 b->ref.fit / 64.0,
1176 a->blue_count,
1177 b->shoot.org,
1178 b->shoot.fit / 64.0));
1180 b = &a->blues[a->blue_count + 1];
1181 b->ref.cur =
1182 b->ref.fit =
1183 b->shoot.cur =
1184 b->shoot.fit = FT_MulFix(b->ref.org, a->org_scale) + delta;
1186 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f (artificial)\n"
1187 " overshoot %d: %d scaled to %.2f (artificial)\n",
1188 a->blue_count + 1,
1189 b->ref.org,
1190 b->ref.fit / 64.0,
1191 a->blue_count + 1,
1192 b->shoot.org,
1193 b->shoot.fit / 64.0));
1196 TA_LOG_GLOBAL(("\n"));
1201 /* scale global values in both directions */
1203 void
1204 ta_latin_metrics_scale(TA_LatinMetrics metrics,
1205 TA_Scaler scaler)
1207 metrics->root.scaler.render_mode = scaler->render_mode;
1208 metrics->root.scaler.face = scaler->face;
1209 metrics->root.scaler.flags = scaler->flags;
1211 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_HORZ);
1212 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_VERT);
1216 /* walk over all contours and compute its segments */
1218 FT_Error
1219 ta_latin_hints_compute_segments(TA_GlyphHints hints,
1220 TA_Dimension dim)
1222 TA_AxisHints axis = &hints->axis[dim];
1223 FT_Error error = FT_Err_Ok;
1225 TA_Segment segment = NULL;
1226 TA_SegmentRec seg0;
1228 TA_Point* contour = hints->contours;
1229 TA_Point* contour_limit = contour + hints->num_contours;
1230 TA_Direction major_dir, segment_dir;
1233 memset(&seg0, 0, sizeof (TA_SegmentRec));
1234 seg0.score = 32000;
1235 seg0.flags = TA_EDGE_NORMAL;
1237 major_dir = (TA_Direction)TA_ABS(axis->major_dir);
1238 segment_dir = major_dir;
1240 axis->num_segments = 0;
1242 /* set up (u,v) in each point */
1243 if (dim == TA_DIMENSION_HORZ)
1245 TA_Point point = hints->points;
1246 TA_Point limit = point + hints->num_points;
1249 for (; point < limit; point++)
1251 point->u = point->fx;
1252 point->v = point->fy;
1255 else
1257 TA_Point point = hints->points;
1258 TA_Point limit = point + hints->num_points;
1261 for (; point < limit; point++)
1263 point->u = point->fy;
1264 point->v = point->fx;
1268 /* do each contour separately */
1269 for (; contour < contour_limit; contour++)
1271 TA_Point point = contour[0];
1272 TA_Point last = point->prev;
1274 int on_edge = 0;
1276 FT_Pos min_pos = 32000; /* minimum segment pos != min_coord */
1277 FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */
1278 FT_Bool passed;
1281 if (point == last) /* skip singletons -- just in case */
1282 continue;
1284 if (TA_ABS(last->out_dir) == major_dir
1285 && TA_ABS(point->out_dir) == major_dir)
1287 /* we are already on an edge, try to locate its start */
1288 last = point;
1290 for (;;)
1292 point = point->prev;
1293 if (TA_ABS(point->out_dir) != major_dir)
1295 point = point->next;
1296 break;
1298 if (point == last)
1299 break;
1303 last = point;
1304 passed = 0;
1306 for (;;)
1308 FT_Pos u, v;
1311 if (on_edge)
1313 u = point->u;
1314 if (u < min_pos)
1315 min_pos = u;
1316 if (u > max_pos)
1317 max_pos = u;
1319 if (point->out_dir != segment_dir
1320 || point == last)
1322 /* we are just leaving an edge; record a new segment! */
1323 segment->last = point;
1324 segment->pos = (FT_Short)((min_pos + max_pos) >> 1);
1326 /* a segment is round if either its first or last point */
1327 /* is a control point */
1328 if ((segment->first->flags | point->flags) & TA_FLAG_CONTROL)
1329 segment->flags |= TA_EDGE_ROUND;
1331 /* compute segment size */
1332 min_pos = max_pos = point->v;
1334 v = segment->first->v;
1335 if (v < min_pos)
1336 min_pos = v;
1337 if (v > max_pos)
1338 max_pos = v;
1340 segment->min_coord = (FT_Short)min_pos;
1341 segment->max_coord = (FT_Short)max_pos;
1342 segment->height = (FT_Short)(segment->max_coord -
1343 segment->min_coord);
1345 on_edge = 0;
1346 segment = NULL;
1347 /* fall through */
1351 /* now exit if we are at the start/end point */
1352 if (point == last)
1354 if (passed)
1355 break;
1356 passed = 1;
1359 if (!on_edge
1360 && TA_ABS(point->out_dir) == major_dir)
1362 /* this is the start of a new segment! */
1363 segment_dir = (TA_Direction)point->out_dir;
1365 error = ta_axis_hints_new_segment(axis, &segment);
1366 if (error)
1367 goto Exit;
1369 /* clear all segment fields */
1370 segment[0] = seg0;
1372 segment->dir = (FT_Char)segment_dir;
1373 min_pos = max_pos = point->u;
1374 segment->first = point;
1375 segment->last = point;
1377 on_edge = 1;
1379 if (point->out_dir != point->next->in_dir)
1381 /* we have a one-point segment */
1382 segment->pos = (FT_Short)min_pos;
1384 if (point->flags & TA_FLAG_CONTROL)
1385 segment->flags |= TA_EDGE_ROUND;
1387 /* artificially extend the horizontal size if requested */
1388 segment->min_coord = (FT_Short)point->v + point->left_offset;
1389 segment->max_coord = (FT_Short)point->v + point->right_offset;
1390 segment->height = 0;
1392 on_edge = 0;
1393 segment = NULL;
1397 point = point->next;
1399 } /* contours */
1402 /* now slightly increase the height of segments if this makes sense -- */
1403 /* this is used to better detect and ignore serifs */
1405 TA_Segment segments = axis->segments;
1406 TA_Segment segments_end = segments + axis->num_segments;
1409 for (segment = segments; segment < segments_end; segment++)
1411 TA_Point first = segment->first;
1412 TA_Point last = segment->last;
1414 FT_Pos first_v = first->v;
1415 FT_Pos last_v = last->v;
1418 if (first_v < last_v)
1420 TA_Point p;
1423 p = first->prev;
1424 if (p->v < first_v)
1425 segment->height = (FT_Short)(segment->height +
1426 ((first_v - p->v) >> 1));
1428 p = last->next;
1429 if (p->v > last_v)
1430 segment->height = (FT_Short)(segment->height +
1431 ((p->v - last_v) >> 1));
1433 else
1435 TA_Point p;
1438 p = first->prev;
1439 if (p->v > first_v)
1440 segment->height = (FT_Short)(segment->height +
1441 ((p->v - first_v) >> 1));
1443 p = last->next;
1444 if (p->v < last_v)
1445 segment->height = (FT_Short)(segment->height +
1446 ((last_v - p->v) >> 1));
1451 Exit:
1452 return error;
1456 /* link segments to form stems and serifs; if `width_count' and */
1457 /* `widths' are non-zero, use them to fine-tune the scoring function */
1459 void
1460 ta_latin_hints_link_segments(TA_GlyphHints hints,
1461 FT_UInt width_count,
1462 TA_WidthRec* widths,
1463 TA_Dimension dim)
1465 TA_AxisHints axis = &hints->axis[dim];
1467 TA_Segment segments = axis->segments;
1468 TA_Segment segment_limit = segments + axis->num_segments;
1470 FT_Pos len_threshold, len_score, dist_score, max_width;
1471 TA_Segment seg1, seg2;
1474 if (width_count)
1475 max_width = widths[width_count - 1].org;
1476 else
1477 max_width = 0;
1479 /* a heuristic value to set up a minimum value for overlapping */
1480 len_threshold = TA_LATIN_CONSTANT(hints->metrics, 8);
1481 if (len_threshold == 0)
1482 len_threshold = 1;
1484 /* a heuristic value to weight lengths */
1485 len_score = TA_LATIN_CONSTANT(hints->metrics, 6000);
1487 /* a heuristic value to weight distances (no call to */
1488 /* TA_LATIN_CONSTANT needed, since we work on multiples */
1489 /* of the stem width) */
1490 dist_score = 3000;
1492 /* now compare each segment to the others */
1493 for (seg1 = segments; seg1 < segment_limit; seg1++)
1495 if (seg1->dir != axis->major_dir)
1496 continue;
1498 /* search for stems having opposite directions, */
1499 /* with seg1 to the `left' of seg2 */
1500 for (seg2 = segments; seg2 < segment_limit; seg2++)
1502 FT_Pos pos1 = seg1->pos;
1503 FT_Pos pos2 = seg2->pos;
1506 if (seg1->dir + seg2->dir == 0
1507 && pos2 > pos1)
1509 /* compute distance between the two segments */
1510 FT_Pos min = seg1->min_coord;
1511 FT_Pos max = seg1->max_coord;
1512 FT_Pos len;
1515 if (min < seg2->min_coord)
1516 min = seg2->min_coord;
1517 if (max > seg2->max_coord)
1518 max = seg2->max_coord;
1520 /* compute maximum coordinate difference of the two segments */
1521 /* (this is, how much they overlap) */
1522 len = max - min;
1524 /* for one-point segments, `len' is zero if there is an overlap */
1525 /* (and negative otherwise); we have to correct this */
1526 if (len == 0
1527 && (seg1->min_coord == seg1->max_coord
1528 || seg2->min_coord == seg2->max_coord))
1529 len = len_threshold;
1531 if (len >= len_threshold)
1534 * The score is the sum of two demerits indicating the
1535 * `badness' of a fit, measured along the segments' main axis
1536 * and orthogonal to it, respectively.
1538 * o The less overlapping along the main axis, the worse it
1539 * is, causing a larger demerit.
1541 * o The nearer the orthogonal distance to a stem width, the
1542 * better it is, causing a smaller demerit. For simplicity,
1543 * however, we only increase the demerit for values that
1544 * exceed the largest stem width.
1547 FT_Pos dist = pos2 - pos1;
1549 FT_Pos dist_demerit, score;
1552 if (max_width)
1554 /* distance demerits are based on multiples of `max_width'; */
1555 /* we scale by 1024 for getting more precision */
1556 FT_Pos delta = (dist << 10) / max_width - (1 << 10);
1559 if (delta > 10000)
1560 dist_demerit = 32000;
1561 else if (delta > 0)
1562 dist_demerit = delta * delta / dist_score;
1563 else
1564 dist_demerit = 0;
1566 else
1567 dist_demerit = dist; /* default if no widths available */
1569 score = dist_demerit + len_score / len;
1571 /* and we search for the smallest score */
1572 if (score < seg1->score)
1574 seg1->score = score;
1575 seg1->link = seg2;
1578 if (score < seg2->score)
1580 seg2->score = score;
1581 seg2->link = seg1;
1588 /* now compute the `serif' segments, cf. explanations in `tahints.h' */
1589 for (seg1 = segments; seg1 < segment_limit; seg1++)
1591 seg2 = seg1->link;
1593 if (seg2)
1595 if (seg2->link != seg1)
1597 seg1->link = 0;
1598 seg1->serif = seg2->link;
1605 /* link segments to edges, using feature analysis for selection */
1607 FT_Error
1608 ta_latin_hints_compute_edges(TA_GlyphHints hints,
1609 TA_Dimension dim)
1611 TA_AxisHints axis = &hints->axis[dim];
1612 FT_Error error = FT_Err_Ok;
1613 TA_LatinAxis laxis = &((TA_LatinMetrics)hints->metrics)->axis[dim];
1615 TA_Segment segments = axis->segments;
1616 TA_Segment segment_limit = segments + axis->num_segments;
1617 TA_Segment seg;
1619 #if 0
1620 TA_Direction up_dir;
1621 #endif
1622 FT_Fixed scale;
1623 FT_Pos edge_distance_threshold;
1624 FT_Pos segment_length_threshold;
1627 axis->num_edges = 0;
1629 scale = (dim == TA_DIMENSION_HORZ) ? hints->x_scale
1630 : hints->y_scale;
1632 #if 0
1633 up_dir = (dim == TA_DIMENSION_HORZ) ? TA_DIR_UP
1634 : TA_DIR_RIGHT;
1635 #endif
1637 /* we ignore all segments that are less than 1 pixel in length */
1638 /* to avoid many problems with serif fonts */
1639 /* (the corresponding threshold is computed in font units) */
1640 if (dim == TA_DIMENSION_HORZ)
1641 segment_length_threshold = FT_DivFix(64, hints->y_scale);
1642 else
1643 segment_length_threshold = 0;
1645 /********************************************************************/
1646 /* */
1647 /* We begin by generating a sorted table of edges for the current */
1648 /* direction. To do so, we simply scan each segment and try to find */
1649 /* an edge in our table that corresponds to its position. */
1650 /* */
1651 /* If no edge is found, we create and insert a new edge in the */
1652 /* sorted table. Otherwise, we simply add the segment to the edge's */
1653 /* list which gets processed in the second step to compute the */
1654 /* edge's properties. */
1655 /* */
1656 /* Note that the table of edges is sorted along the segment/edge */
1657 /* position. */
1658 /* */
1659 /********************************************************************/
1661 /* assure that edge distance threshold is at most 0.25px */
1662 edge_distance_threshold = FT_MulFix(laxis->edge_distance_threshold,
1663 scale);
1664 if (edge_distance_threshold > 64 / 4)
1665 edge_distance_threshold = 64 / 4;
1667 edge_distance_threshold = FT_DivFix(edge_distance_threshold,
1668 scale);
1670 for (seg = segments; seg < segment_limit; seg++)
1672 TA_Edge found = NULL;
1673 FT_Int ee;
1676 if (seg->height < segment_length_threshold)
1677 continue;
1679 /* a special case for serif edges: */
1680 /* if they are smaller than 1.5 pixels we ignore them */
1681 if (seg->serif
1682 && 2 * seg->height < 3 * segment_length_threshold)
1683 continue;
1685 /* look for an edge corresponding to the segment */
1686 for (ee = 0; ee < axis->num_edges; ee++)
1688 TA_Edge edge = axis->edges + ee;
1689 FT_Pos dist;
1692 dist = seg->pos - edge->fpos;
1693 if (dist < 0)
1694 dist = -dist;
1696 if (dist < edge_distance_threshold && edge->dir == seg->dir)
1698 found = edge;
1699 break;
1703 if (!found)
1705 TA_Edge edge;
1708 /* insert a new edge in the list and sort according to the position */
1709 error = ta_axis_hints_new_edge(axis, seg->pos,
1710 (TA_Direction)seg->dir,
1711 &edge);
1712 if (error)
1713 goto Exit;
1715 /* add the segment to the new edge's list */
1716 memset(edge, 0, sizeof (TA_EdgeRec));
1717 edge->first = seg;
1718 edge->last = seg;
1719 edge->dir = seg->dir;
1720 edge->fpos = seg->pos;
1721 edge->opos = FT_MulFix(seg->pos, scale);
1722 edge->pos = edge->opos;
1723 seg->edge_next = seg;
1725 else
1727 /* if an edge was found, simply add the segment to the edge's list */
1728 seg->edge_next = found->first;
1729 found->last->edge_next = seg;
1730 found->last = seg;
1734 /*****************************************************************/
1735 /* */
1736 /* Good, we now compute each edge's properties according to */
1737 /* the segments found on its position. Basically, these are */
1738 /* */
1739 /* - the edge's main direction */
1740 /* - stem edge, serif edge or both (which defaults to stem then) */
1741 /* - rounded edge, straight or both (which defaults to straight) */
1742 /* - link for edge */
1743 /* */
1744 /*****************************************************************/
1746 /* first of all, set the `edge' field in each segment -- this is */
1747 /* required in order to compute edge links */
1749 /* note that removing this loop and setting the `edge' field of each */
1750 /* segment directly in the code above slows down execution speed for */
1751 /* some reasons on platforms like the Sun */
1753 TA_Edge edges = axis->edges;
1754 TA_Edge edge_limit = edges + axis->num_edges;
1755 TA_Edge edge;
1758 for (edge = edges; edge < edge_limit; edge++)
1760 seg = edge->first;
1761 if (seg)
1764 seg->edge = edge;
1765 seg = seg->edge_next;
1766 } while (seg != edge->first);
1769 /* now compute each edge properties */
1770 for (edge = edges; edge < edge_limit; edge++)
1772 FT_Int is_round = 0; /* does it contain round segments? */
1773 FT_Int is_straight = 0; /* does it contain straight segments? */
1774 #if 0
1775 FT_Pos ups = 0; /* number of upwards segments */
1776 FT_Pos downs = 0; /* number of downwards segments */
1777 #endif
1780 seg = edge->first;
1784 FT_Bool is_serif;
1787 /* check for roundness of segment */
1788 if (seg->flags & TA_EDGE_ROUND)
1789 is_round++;
1790 else
1791 is_straight++;
1793 #if 0
1794 /* check for segment direction */
1795 if (seg->dir == up_dir)
1796 ups += seg->max_coord - seg->min_coord;
1797 else
1798 downs += seg->max_coord - seg->min_coord;
1799 #endif
1801 /* check for links -- */
1802 /* if seg->serif is set, then seg->link must be ignored */
1803 is_serif = (FT_Bool)(seg->serif
1804 && seg->serif->edge
1805 && seg->serif->edge != edge);
1807 if ((seg->link && seg->link->edge != NULL)
1808 || is_serif)
1810 TA_Edge edge2;
1811 TA_Segment seg2;
1814 edge2 = edge->link;
1815 seg2 = seg->link;
1817 if (is_serif)
1819 seg2 = seg->serif;
1820 edge2 = edge->serif;
1823 if (edge2)
1825 FT_Pos edge_delta;
1826 FT_Pos seg_delta;
1829 edge_delta = edge->fpos - edge2->fpos;
1830 if (edge_delta < 0)
1831 edge_delta = -edge_delta;
1833 seg_delta = seg->pos - seg2->pos;
1834 if (seg_delta < 0)
1835 seg_delta = -seg_delta;
1837 if (seg_delta < edge_delta)
1838 edge2 = seg2->edge;
1840 else
1841 edge2 = seg2->edge;
1843 if (is_serif)
1845 edge->serif = edge2;
1846 edge2->flags |= TA_EDGE_SERIF;
1848 else
1849 edge->link = edge2;
1852 seg = seg->edge_next;
1853 } while (seg != edge->first);
1855 /* set the round/straight flags */
1856 edge->flags = TA_EDGE_NORMAL;
1858 if (is_round > 0
1859 && is_round >= is_straight)
1860 edge->flags |= TA_EDGE_ROUND;
1862 #if 0
1863 /* set the edge's main direction */
1864 edge->dir = TA_DIR_NONE;
1866 if (ups > downs)
1867 edge->dir = (FT_Char)up_dir;
1869 else if (ups < downs)
1870 edge->dir = (FT_Char)-up_dir;
1872 else if (ups == downs)
1873 edge->dir = 0; /* both up and down! */
1874 #endif
1876 /* get rid of serifs if link is set */
1877 /* XXX: this gets rid of many unpleasant artefacts! */
1878 /* example: the `c' in cour.pfa at size 13 */
1880 if (edge->serif && edge->link)
1881 edge->serif = 0;
1885 Exit:
1886 return error;
1890 /* detect segments and edges for given dimension */
1892 FT_Error
1893 ta_latin_hints_detect_features(TA_GlyphHints hints,
1894 FT_UInt width_count,
1895 TA_WidthRec* widths,
1896 TA_Dimension dim)
1898 FT_Error error;
1901 error = ta_latin_hints_compute_segments(hints, dim);
1902 if (!error)
1904 ta_latin_hints_link_segments(hints, width_count, widths, dim);
1906 error = ta_latin_hints_compute_edges(hints, dim);
1909 return error;
1913 /* compute all edges which lie within blue zones */
1915 void
1916 ta_latin_hints_compute_blue_edges(TA_GlyphHints hints,
1917 TA_LatinMetrics metrics)
1919 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
1921 TA_Edge edge = axis->edges;
1922 TA_Edge edge_limit = edge + axis->num_edges;
1924 TA_LatinAxis latin = &metrics->axis[TA_DIMENSION_VERT];
1925 FT_Fixed scale = latin->scale;
1928 /* compute which blue zones are active, */
1929 /* i.e. have their scaled size < 3/4 pixels */
1931 /* for each horizontal edge search the blue zone which is closest */
1932 for (; edge < edge_limit; edge++)
1934 FT_UInt bb;
1935 TA_Width best_blue = NULL;
1936 FT_Bool best_blue_is_neutral = 0;
1937 FT_Pos best_dist; /* initial threshold */
1939 FT_UInt best_blue_idx = 0;
1940 FT_Bool best_blue_is_shoot = 0;
1943 /* compute the initial threshold as a fraction of the EM size */
1944 /* (the value 40 is heuristic) */
1945 best_dist = FT_MulFix(metrics->units_per_em / 40, scale);
1947 /* assure a minimum distance of 0.5px */
1948 if (best_dist > 64 / 2)
1949 best_dist = 64 / 2;
1951 /* this loop also handles the two extra blue zones */
1952 /* for usWinAscent and usWinDescent */
1953 /* if option `windows-compatibility' is set */
1954 for (bb = 0;
1955 bb < latin->blue_count
1956 + (metrics->root.globals->font->windows_compatibility ? 2 : 0);
1957 bb++)
1959 TA_LatinBlue blue = latin->blues + bb;
1960 FT_Bool is_top_blue, is_neutral_blue, is_major_dir;
1963 /* skip inactive blue zones (i.e., those that are too large) */
1964 if (!(blue->flags & TA_LATIN_BLUE_ACTIVE))
1965 continue;
1967 /* if it is a top zone, check for right edges (against the major */
1968 /* direction); if it is a bottom zone, check for left edges (in */
1969 /* the major direction) */
1970 is_top_blue = (FT_Byte)((blue->flags & TA_LATIN_BLUE_TOP) != 0);
1971 is_neutral_blue = (FT_Byte)((blue->flags & TA_LATIN_BLUE_NEUTRAL) != 0);
1972 is_major_dir = FT_BOOL(edge->dir == axis->major_dir);
1974 /* neutral blue zones are handled for both directions */
1975 if (is_top_blue ^ is_major_dir || is_neutral_blue)
1977 FT_Pos dist;
1980 /* first of all, compare it to the reference position */
1981 dist = edge->fpos - blue->ref.org;
1982 if (dist < 0)
1983 dist = -dist;
1985 dist = FT_MulFix(dist, scale);
1986 if (dist < best_dist)
1988 best_dist = dist;
1989 best_blue = &blue->ref;
1990 best_blue_is_neutral = is_neutral_blue;
1992 best_blue_idx = bb;
1993 best_blue_is_shoot = 0;
1996 /* now compare it to the overshoot position and check whether */
1997 /* the edge is rounded, and whether the edge is over the */
1998 /* reference position of a top zone, or under the reference */
1999 /* position of a bottom zone (provided we don't have a */
2000 /* neutral blue zone) */
2001 if (edge->flags & TA_EDGE_ROUND
2002 && dist != 0
2003 && !is_neutral_blue)
2005 FT_Bool is_under_ref = FT_BOOL(edge->fpos < blue->ref.org);
2008 if (is_top_blue ^ is_under_ref)
2010 dist = edge->fpos - blue->shoot.org;
2011 if (dist < 0)
2012 dist = -dist;
2014 dist = FT_MulFix(dist, scale);
2015 if (dist < best_dist)
2017 best_dist = dist;
2018 best_blue = &blue->shoot;
2019 best_blue_is_neutral = is_neutral_blue;
2021 best_blue_idx = bb;
2022 best_blue_is_shoot = 1;
2029 if (best_blue)
2031 edge->blue_edge = best_blue;
2032 edge->best_blue_idx = best_blue_idx;
2033 edge->best_blue_is_shoot = best_blue_is_shoot;
2034 if (best_blue_is_neutral)
2035 edge->flags |= TA_EDGE_NEUTRAL;
2041 /* initalize hinting engine */
2043 static FT_Error
2044 ta_latin_hints_init(TA_GlyphHints hints,
2045 TA_LatinMetrics metrics)
2047 FT_Render_Mode mode;
2048 FT_UInt32 scaler_flags, other_flags;
2049 FT_Face face = metrics->root.scaler.face;
2052 ta_glyph_hints_rescale(hints, (TA_StyleMetrics)metrics);
2054 /* correct x_scale and y_scale if needed, since they may have */
2055 /* been modified by `ta_latin_metrics_scale_dim' above */
2056 hints->x_scale = metrics->axis[TA_DIMENSION_HORZ].scale;
2057 hints->x_delta = metrics->axis[TA_DIMENSION_HORZ].delta;
2058 hints->y_scale = metrics->axis[TA_DIMENSION_VERT].scale;
2059 hints->y_delta = metrics->axis[TA_DIMENSION_VERT].delta;
2061 /* compute flags depending on render mode, etc. */
2062 mode = metrics->root.scaler.render_mode;
2064 #if 0 /* #ifdef TA_CONFIG_OPTION_USE_WARPER */
2065 if (mode == FT_RENDER_MODE_LCD
2066 || mode == FT_RENDER_MODE_LCD_V)
2067 metrics->root.scaler.render_mode =
2068 mode = FT_RENDER_MODE_NORMAL;
2069 #endif
2071 scaler_flags = hints->scaler_flags;
2072 other_flags = 0;
2074 /* we snap the width of vertical stems for the monochrome */
2075 /* and horizontal LCD rendering targets only */
2076 if (mode == FT_RENDER_MODE_MONO
2077 || mode == FT_RENDER_MODE_LCD)
2078 other_flags |= TA_LATIN_HINTS_HORZ_SNAP;
2080 /* we snap the width of horizontal stems for the monochrome */
2081 /* and vertical LCD rendering targets only */
2082 if (mode == FT_RENDER_MODE_MONO
2083 || mode == FT_RENDER_MODE_LCD_V)
2084 other_flags |= TA_LATIN_HINTS_VERT_SNAP;
2086 /* we adjust stems to full pixels only if we don't use the `light' mode */
2087 if (mode != FT_RENDER_MODE_LIGHT)
2088 other_flags |= TA_LATIN_HINTS_STEM_ADJUST;
2090 if (mode == FT_RENDER_MODE_MONO)
2091 other_flags |= TA_LATIN_HINTS_MONO;
2093 /* in `light' hinting mode we disable horizontal hinting completely; */
2094 /* we also do it if the face is italic */
2095 if (mode == FT_RENDER_MODE_LIGHT
2096 || (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0)
2097 scaler_flags |= TA_SCALER_FLAG_NO_HORIZONTAL;
2099 hints->scaler_flags = scaler_flags;
2100 hints->other_flags = other_flags;
2102 return FT_Err_Ok;
2106 /* snap a given width in scaled coordinates to */
2107 /* one of the current standard widths */
2109 static FT_Pos
2110 ta_latin_snap_width(TA_Width widths,
2111 FT_Int count,
2112 FT_Pos width)
2114 int n;
2115 FT_Pos best = 64 + 32 + 2;
2116 FT_Pos reference = width;
2117 FT_Pos scaled;
2120 for (n = 0; n < count; n++)
2122 FT_Pos w;
2123 FT_Pos dist;
2126 w = widths[n].cur;
2127 dist = width - w;
2128 if (dist < 0)
2129 dist = -dist;
2130 if (dist < best)
2132 best = dist;
2133 reference = w;
2137 scaled = TA_PIX_ROUND(reference);
2139 if (width >= reference)
2141 if (width < scaled + 48)
2142 width = reference;
2144 else
2146 if (width > scaled - 48)
2147 width = reference;
2150 return width;
2154 /* compute the snapped width of a given stem, ignoring very thin ones */
2156 /* there is a lot of voodoo in this function; changing the hard-coded */
2157 /* parameters influences the whole hinting process */
2159 static FT_Pos
2160 ta_latin_compute_stem_width(TA_GlyphHints hints,
2161 TA_Dimension dim,
2162 FT_Pos width,
2163 FT_Byte base_flags,
2164 FT_Byte stem_flags)
2166 TA_LatinMetrics metrics = (TA_LatinMetrics) hints->metrics;
2167 TA_LatinAxis axis = &metrics->axis[dim];
2169 FT_Pos dist = width;
2170 FT_Int sign = 0;
2171 FT_Int vertical = (dim == TA_DIMENSION_VERT);
2174 if (!TA_LATIN_HINTS_DO_STEM_ADJUST(hints)
2175 || axis->extra_light)
2176 return width;
2178 if (dist < 0)
2180 dist = -width;
2181 sign = 1;
2184 if ((vertical && !TA_LATIN_HINTS_DO_VERT_SNAP(hints))
2185 || (!vertical && !TA_LATIN_HINTS_DO_HORZ_SNAP(hints)))
2187 /* smooth hinting process: very lightly quantize the stem width */
2189 /* leave the widths of serifs alone */
2190 if ((stem_flags & TA_EDGE_SERIF)
2191 && vertical
2192 && (dist < 3 * 64))
2193 goto Done_Width;
2194 else if (base_flags & TA_EDGE_ROUND)
2196 if (dist < 80)
2197 dist = 64;
2199 else if (dist < 56)
2200 dist = 56;
2202 if (axis->width_count > 0)
2204 FT_Pos delta;
2207 /* compare to standard width */
2208 delta = dist - axis->widths[0].cur;
2210 if (delta < 0)
2211 delta = -delta;
2213 if (delta < 40)
2215 dist = axis->widths[0].cur;
2216 if (dist < 48)
2217 dist = 48;
2219 goto Done_Width;
2222 if (dist < 3 * 64)
2224 delta = dist & 63;
2225 dist &= -64;
2227 if (delta < 10)
2228 dist += delta;
2229 else if (delta < 32)
2230 dist += 10;
2231 else if (delta < 54)
2232 dist += 54;
2233 else
2234 dist += delta;
2236 else
2237 dist = (dist + 32) & ~63;
2240 else
2242 /* strong hinting process: snap the stem width to integer pixels */
2244 FT_Pos org_dist = dist;
2247 dist = ta_latin_snap_width(axis->widths, axis->width_count, dist);
2249 if (vertical)
2251 /* in the case of vertical hinting, */
2252 /* always round the stem heights to integer pixels */
2254 if (dist >= 64)
2255 dist = (dist + 16) & ~63;
2256 else
2257 dist = 64;
2259 else
2261 if (TA_LATIN_HINTS_DO_MONO(hints))
2263 /* monochrome horizontal hinting: */
2264 /* snap widths to integer pixels with a different threshold */
2266 if (dist < 64)
2267 dist = 64;
2268 else
2269 dist = (dist + 32) & ~63;
2271 else
2273 /* for horizontal anti-aliased hinting, we adopt a more subtle */
2274 /* approach: we strengthen small stems, round stems whose size */
2275 /* is between 1 and 2 pixels to an integer, otherwise nothing */
2277 if (dist < 48)
2278 dist = (dist + 64) >> 1;
2280 else if (dist < 128)
2282 /* we only round to an integer width if the corresponding */
2283 /* distortion is less than 1/4 pixel -- otherwise, this */
2284 /* makes everything worse since the diagonals, which are */
2285 /* not hinted, appear a lot bolder or thinner than the */
2286 /* vertical stems */
2288 FT_Pos delta;
2291 dist = (dist + 22) & ~63;
2292 delta = dist - org_dist;
2293 if (delta < 0)
2294 delta = -delta;
2296 if (delta >= 16)
2298 dist = org_dist;
2299 if (dist < 48)
2300 dist = (dist + 64) >> 1;
2303 else
2304 /* round otherwise to prevent color fringes in LCD mode */
2305 dist = (dist + 32) & ~63;
2310 Done_Width:
2311 if (sign)
2312 dist = -dist;
2314 return dist;
2318 /* align one stem edge relative to the previous stem edge */
2320 static void
2321 ta_latin_align_linked_edge(TA_GlyphHints hints,
2322 TA_Dimension dim,
2323 TA_Edge base_edge,
2324 TA_Edge stem_edge)
2326 FT_Pos dist = stem_edge->opos - base_edge->opos;
2328 FT_Pos fitted_width = ta_latin_compute_stem_width(
2329 hints, dim, dist,
2330 base_edge->flags,
2331 stem_edge->flags);
2334 stem_edge->pos = base_edge->pos + fitted_width;
2336 TA_LOG((" LINK: edge %d (opos=%.2f) linked to %.2f,"
2337 " dist was %.2f, now %.2f\n",
2338 stem_edge - hints->axis[dim].edges, stem_edge->opos / 64.0,
2339 stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0));
2341 if (hints->recorder)
2342 hints->recorder(ta_link, hints, dim,
2343 base_edge, stem_edge, NULL, NULL, NULL);
2347 /* shift the coordinates of the `serif' edge by the same amount */
2348 /* as the corresponding `base' edge has been moved already */
2350 static void
2351 ta_latin_align_serif_edge(TA_GlyphHints hints,
2352 TA_Edge base,
2353 TA_Edge serif)
2355 FT_UNUSED(hints);
2357 serif->pos = base->pos + (serif->opos - base->opos);
2361 /* the main grid-fitting routine */
2363 void
2364 ta_latin_hint_edges(TA_GlyphHints hints,
2365 TA_Dimension dim)
2367 TA_AxisHints axis = &hints->axis[dim];
2369 TA_Edge edges = axis->edges;
2370 TA_Edge edge_limit = edges + axis->num_edges;
2371 FT_PtrDist n_edges;
2372 TA_Edge edge;
2374 TA_Edge anchor = NULL;
2375 FT_Int has_serifs = 0;
2377 #ifdef TA_DEBUG
2378 FT_UInt num_actions = 0;
2379 #endif
2381 TA_LOG(("latin %s edge hinting (style `%s')\n",
2382 dim == TA_DIMENSION_VERT ? "horizontal" : "vertical",
2383 ta_style_names[hints->metrics->style_class->style]));
2385 /* we begin by aligning all stems relative to the blue zone if needed -- */
2386 /* that's only for horizontal edges */
2388 if (dim == TA_DIMENSION_VERT
2389 && TA_HINTS_DO_BLUES(hints))
2391 for (edge = edges; edge < edge_limit; edge++)
2393 TA_Width blue;
2394 TA_Edge edge1, edge2; /* these edges form the stem to check */
2397 if (edge->flags & TA_EDGE_DONE)
2398 continue;
2400 edge1 = NULL;
2401 edge2 = edge->link;
2404 * If a stem contains both a neutral and a non-neutral blue zone,
2405 * skip the neutral one. Otherwise, outlines with different
2406 * directions might be incorrectly aligned at the same vertical
2407 * position.
2409 * If we have two neutral blue zones, skip one of them.
2411 if (edge->blue_edge && edge2 && edge2->blue_edge)
2413 FT_Byte neutral = edge->flags & TA_EDGE_NEUTRAL;
2414 FT_Byte neutral2 = edge2->flags & TA_EDGE_NEUTRAL;
2417 if ((neutral && neutral2) || neutral2)
2419 edge2->blue_edge = NULL;
2420 edge2->flags &= ~TA_EDGE_NEUTRAL;
2422 else if (neutral)
2424 edge->blue_edge = NULL;
2425 edge->flags &= ~TA_EDGE_NEUTRAL;
2429 blue = edge->blue_edge;
2430 if (blue)
2431 edge1 = edge;
2433 /* flip edges if the other edge is aligned to a blue zone */
2434 else if (edge2 && edge2->blue_edge)
2436 blue = edge2->blue_edge;
2437 edge1 = edge2;
2438 edge2 = edge;
2441 if (!edge1)
2442 continue;
2444 #ifdef TA_DEBUG
2445 if (!anchor)
2446 TA_LOG((" BLUE_ANCHOR: edge %d (opos=%.2f) snapped to %.2f,"
2447 " was %.2f (anchor=edge %d)\n",
2448 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
2449 edge1->pos / 64.0, edge - edges));
2450 else
2451 TA_LOG((" BLUE: edge %d (opos=%.2f) snapped to %.2f, was %.2f\n",
2452 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
2453 edge1->pos / 64.0));
2455 num_actions++;
2456 #endif
2458 edge1->pos = blue->fit;
2459 edge1->flags |= TA_EDGE_DONE;
2461 if (hints->recorder)
2463 if (!anchor)
2464 hints->recorder(ta_blue_anchor, hints, dim,
2465 edge1, edge, NULL, NULL, NULL);
2466 else
2467 hints->recorder(ta_blue, hints, dim,
2468 edge1, NULL, NULL, NULL, NULL);
2471 if (edge2 && !edge2->blue_edge)
2473 ta_latin_align_linked_edge(hints, dim, edge1, edge2);
2474 edge2->flags |= TA_EDGE_DONE;
2476 #ifdef TA_DEBUG
2477 num_actions++;
2478 #endif
2481 if (!anchor)
2482 anchor = edge;
2486 /* now we align all other stem edges, */
2487 /* trying to maintain the relative order of stems in the glyph */
2488 for (edge = edges; edge < edge_limit; edge++)
2490 TA_Edge edge2;
2493 if (edge->flags & TA_EDGE_DONE)
2494 continue;
2496 /* skip all non-stem edges */
2497 edge2 = edge->link;
2498 if (!edge2)
2500 has_serifs++;
2501 continue;
2504 /* now align the stem */
2506 /* this should not happen, but it's better to be safe */
2507 if (edge2->blue_edge)
2509 TA_LOG((" ASSERTION FAILED for edge %d\n", edge2 - edges));
2511 ta_latin_align_linked_edge(hints, dim, edge2, edge);
2512 edge->flags |= TA_EDGE_DONE;
2514 #ifdef TA_DEBUG
2515 num_actions++;
2516 #endif
2517 continue;
2520 if (!anchor)
2522 /* if we reach this if clause, no stem has been aligned yet */
2524 FT_Pos org_len, org_center, cur_len;
2525 FT_Pos cur_pos1, error1, error2, u_off, d_off;
2528 org_len = edge2->opos - edge->opos;
2529 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2530 edge->flags, edge2->flags);
2532 /* some voodoo to specially round edges for small stem widths; */
2533 /* the idea is to align the center of a stem, */
2534 /* then shifting the stem edges to suitable positions */
2535 if (cur_len <= 64)
2537 /* width <= 1px */
2538 u_off = 32;
2539 d_off = 32;
2541 else
2543 /* 1px < width < 1.5px */
2544 u_off = 38;
2545 d_off = 26;
2548 if (cur_len < 96)
2550 org_center = edge->opos + (org_len >> 1);
2551 cur_pos1 = TA_PIX_ROUND(org_center);
2553 error1 = org_center - (cur_pos1 - u_off);
2554 if (error1 < 0)
2555 error1 = -error1;
2557 error2 = org_center - (cur_pos1 + d_off);
2558 if (error2 < 0)
2559 error2 = -error2;
2561 if (error1 < error2)
2562 cur_pos1 -= u_off;
2563 else
2564 cur_pos1 += d_off;
2566 edge->pos = cur_pos1 - cur_len / 2;
2567 edge2->pos = edge->pos + cur_len;
2569 else
2570 edge->pos = TA_PIX_ROUND(edge->opos);
2572 anchor = edge;
2573 edge->flags |= TA_EDGE_DONE;
2575 TA_LOG((" ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
2576 " snapped to %.2f and %.2f\n",
2577 edge - edges, edge->opos / 64.0,
2578 edge2 - edges, edge2->opos / 64.0,
2579 edge->pos / 64.0, edge2->pos / 64.0));
2581 if (hints->recorder)
2582 hints->recorder(ta_anchor, hints, dim,
2583 edge, edge2, NULL, NULL, NULL);
2585 ta_latin_align_linked_edge(hints, dim, edge, edge2);
2587 #ifdef TA_DEBUG
2588 num_actions += 2;
2589 #endif
2591 else
2593 FT_Pos org_pos, org_len, org_center, cur_len;
2594 FT_Pos cur_pos1, cur_pos2, delta1, delta2;
2597 org_pos = anchor->pos + (edge->opos - anchor->opos);
2598 org_len = edge2->opos - edge->opos;
2599 org_center = org_pos + (org_len >> 1);
2601 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2602 edge->flags, edge2->flags);
2604 if (edge2->flags & TA_EDGE_DONE)
2606 TA_LOG((" ADJUST: edge %d (pos=%.2f) moved to %.2f\n",
2607 edge - edges, edge->pos / 64.0,
2608 (edge2->pos - cur_len) / 64.0));
2610 edge->pos = edge2->pos - cur_len;
2612 if (hints->recorder)
2614 TA_Edge bound = NULL;
2617 if (edge > edges)
2618 bound = &edge[-1];
2620 hints->recorder(ta_adjust, hints, dim,
2621 edge, edge2, NULL, bound, NULL);
2625 else if (cur_len < 96)
2627 FT_Pos u_off, d_off;
2630 cur_pos1 = TA_PIX_ROUND(org_center);
2632 if (cur_len <= 64)
2634 u_off = 32;
2635 d_off = 32;
2637 else
2639 u_off = 38;
2640 d_off = 26;
2643 delta1 = org_center - (cur_pos1 - u_off);
2644 if (delta1 < 0)
2645 delta1 = -delta1;
2647 delta2 = org_center - (cur_pos1 + d_off);
2648 if (delta2 < 0)
2649 delta2 = -delta2;
2651 if (delta1 < delta2)
2652 cur_pos1 -= u_off;
2653 else
2654 cur_pos1 += d_off;
2656 edge->pos = cur_pos1 - cur_len / 2;
2657 edge2->pos = cur_pos1 + cur_len / 2;
2659 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2660 " snapped to %.2f and %.2f\n",
2661 edge - edges, edge->opos / 64.0,
2662 edge2 - edges, edge2->opos / 64.0,
2663 edge->pos / 64.0, edge2->pos / 64.0));
2665 if (hints->recorder)
2667 TA_Edge bound = NULL;
2670 if (edge > edges)
2671 bound = &edge[-1];
2673 hints->recorder(ta_stem, hints, dim,
2674 edge, edge2, NULL, bound, NULL);
2678 else
2680 org_pos = anchor->pos + (edge->opos - anchor->opos);
2681 org_len = edge2->opos - edge->opos;
2682 org_center = org_pos + (org_len >> 1);
2684 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2685 edge->flags, edge2->flags);
2687 cur_pos1 = TA_PIX_ROUND(org_pos);
2688 delta1 = cur_pos1 + (cur_len >> 1) - org_center;
2689 if (delta1 < 0)
2690 delta1 = -delta1;
2692 cur_pos2 = TA_PIX_ROUND(org_pos + org_len) - cur_len;
2693 delta2 = cur_pos2 + (cur_len >> 1) - org_center;
2694 if (delta2 < 0)
2695 delta2 = -delta2;
2697 edge->pos = (delta1 < delta2) ? cur_pos1 : cur_pos2;
2698 edge2->pos = edge->pos + cur_len;
2700 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2701 " snapped to %.2f and %.2f\n",
2702 edge - edges, edge->opos / 64.0,
2703 edge2 - edges, edge2->opos / 64.0,
2704 edge->pos / 64.0, edge2->pos / 64.0));
2706 if (hints->recorder)
2708 TA_Edge bound = NULL;
2711 if (edge > edges)
2712 bound = &edge[-1];
2714 hints->recorder(ta_stem, hints, dim,
2715 edge, edge2, NULL, bound, NULL);
2719 #ifdef TA_DEBUG
2720 num_actions++;
2721 #endif
2723 edge->flags |= TA_EDGE_DONE;
2724 edge2->flags |= TA_EDGE_DONE;
2726 if (edge > edges
2727 && edge->pos < edge[-1].pos)
2729 #ifdef TA_DEBUG
2730 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2731 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
2733 num_actions++;
2734 #endif
2736 edge->pos = edge[-1].pos;
2738 if (hints->recorder)
2739 hints->recorder(ta_bound, hints, dim,
2740 edge, &edge[-1], NULL, NULL, NULL);
2745 /* make sure that lowercase m's maintain their symmetry */
2747 /* In general, lowercase m's have six vertical edges if they are sans */
2748 /* serif, or twelve if they are with serifs. This implementation is */
2749 /* based on that assumption, and seems to work very well with most */
2750 /* faces. However, if for a certain face this assumption is not */
2751 /* true, the m is just rendered like before. In addition, any stem */
2752 /* correction will only be applied to symmetrical glyphs (even if the */
2753 /* glyph is not an m), so the potential for unwanted distortion is */
2754 /* relatively low. */
2756 /* we don't handle horizontal edges since we can't easily assure that */
2757 /* the third (lowest) stem aligns with the base line; it might end up */
2758 /* one pixel higher or lower */
2760 n_edges = edge_limit - edges;
2761 if (dim == TA_DIMENSION_HORZ
2762 && (n_edges == 6 || n_edges == 12))
2764 TA_Edge edge1, edge2, edge3;
2765 FT_Pos dist1, dist2, span, delta;
2768 if (n_edges == 6)
2770 edge1 = edges;
2771 edge2 = edges + 2;
2772 edge3 = edges + 4;
2774 else
2776 edge1 = edges + 1;
2777 edge2 = edges + 5;
2778 edge3 = edges + 9;
2781 dist1 = edge2->opos - edge1->opos;
2782 dist2 = edge3->opos - edge2->opos;
2784 span = dist1 - dist2;
2785 if (span < 0)
2786 span = -span;
2788 if (span < 8)
2790 delta = edge3->pos - (2 * edge2->pos - edge1->pos);
2791 edge3->pos -= delta;
2792 if (edge3->link)
2793 edge3->link->pos -= delta;
2795 /* move the serifs along with the stem */
2796 if (n_edges == 12)
2798 (edges + 8)->pos -= delta;
2799 (edges + 11)->pos -= delta;
2802 edge3->flags |= TA_EDGE_DONE;
2803 if (edge3->link)
2804 edge3->link->flags |= TA_EDGE_DONE;
2808 if (has_serifs || !anchor)
2810 /* now hint the remaining edges (serifs and single) */
2811 /* in order to complete our processing */
2812 for (edge = edges; edge < edge_limit; edge++)
2814 TA_Edge lower_bound = NULL;
2815 TA_Edge upper_bound = NULL;
2817 FT_Pos delta;
2820 if (edge->flags & TA_EDGE_DONE)
2821 continue;
2823 delta = 1000;
2825 if (edge->serif)
2827 delta = edge->serif->opos - edge->opos;
2828 if (delta < 0)
2829 delta = -delta;
2832 if (edge > edges)
2833 lower_bound = &edge[-1];
2835 if (edge + 1 < edge_limit
2836 && edge[1].flags & TA_EDGE_DONE)
2837 upper_bound = &edge[1];
2840 if (delta < 64 + 16)
2842 ta_latin_align_serif_edge(hints, edge->serif, edge);
2844 TA_LOG((" SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
2845 " aligned to %.2f\n",
2846 edge - edges, edge->opos / 64.0,
2847 edge->serif - edges, edge->serif->opos / 64.0,
2848 edge->pos / 64.0));
2850 if (hints->recorder)
2851 hints->recorder(ta_serif, hints, dim,
2852 edge, NULL, NULL, lower_bound, upper_bound);
2854 else if (!anchor)
2856 edge->pos = TA_PIX_ROUND(edge->opos);
2857 anchor = edge;
2859 TA_LOG((" SERIF_ANCHOR: edge %d (opos=%.2f) snapped to %.2f\n",
2860 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
2862 if (hints->recorder)
2863 hints->recorder(ta_serif_anchor, hints, dim,
2864 edge, NULL, NULL, lower_bound, upper_bound);
2866 else
2868 TA_Edge before, after;
2871 for (before = edge - 1; before >= edges; before--)
2872 if (before->flags & TA_EDGE_DONE)
2873 break;
2875 for (after = edge + 1; after < edge_limit; after++)
2876 if (after->flags & TA_EDGE_DONE)
2877 break;
2879 if (before >= edges && before < edge
2880 && after < edge_limit && after > edge)
2882 if (after->opos == before->opos)
2883 edge->pos = before->pos;
2884 else
2885 edge->pos = before->pos + FT_MulDiv(edge->opos - before->opos,
2886 after->pos - before->pos,
2887 after->opos - before->opos);
2889 TA_LOG((" SERIF_LINK1: edge %d (opos=%.2f) snapped to %.2f"
2890 " from %d (opos=%.2f)\n",
2891 edge - edges, edge->opos / 64.0,
2892 edge->pos / 64.0,
2893 before - edges, before->opos / 64.0));
2895 if (hints->recorder)
2896 hints->recorder(ta_serif_link1, hints, dim,
2897 edge, before, after, lower_bound, upper_bound);
2899 else
2901 edge->pos = anchor->pos + ((edge->opos - anchor->opos + 16) & ~31);
2902 TA_LOG((" SERIF_LINK2: edge %d (opos=%.2f) snapped to %.2f\n",
2903 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
2905 if (hints->recorder)
2906 hints->recorder(ta_serif_link2, hints, dim,
2907 edge, NULL, NULL, lower_bound, upper_bound);
2911 #ifdef TA_DEBUG
2912 num_actions++;
2913 #endif
2914 edge->flags |= TA_EDGE_DONE;
2916 if (edge > edges
2917 && edge->pos < edge[-1].pos)
2919 #ifdef TA_DEBUG
2920 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2921 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
2922 num_actions++;
2923 #endif
2925 edge->pos = edge[-1].pos;
2927 if (hints->recorder)
2928 hints->recorder(ta_bound, hints, dim,
2929 edge, &edge[-1], NULL, NULL, NULL);
2932 if (edge + 1 < edge_limit
2933 && edge[1].flags & TA_EDGE_DONE
2934 && edge->pos > edge[1].pos)
2936 #ifdef TA_DEBUG
2937 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2938 edge - edges, edge->pos / 64.0, edge[1].pos / 64.0));
2940 num_actions++;
2941 #endif
2943 edge->pos = edge[1].pos;
2945 if (hints->recorder)
2946 hints->recorder(ta_bound, hints, dim,
2947 edge, &edge[1], NULL, NULL, NULL);
2952 #ifdef TA_DEBUG
2953 if (!num_actions)
2954 TA_LOG((" (none)\n"));
2955 TA_LOG(("\n"));
2956 #endif
2960 /* apply the complete hinting algorithm to a latin glyph */
2962 static FT_Error
2963 ta_latin_hints_apply(TA_GlyphHints hints,
2964 FT_Outline* outline,
2965 TA_LatinMetrics metrics)
2967 FT_Error error;
2968 int dim;
2970 TA_LatinAxis axis;
2973 error = ta_glyph_hints_reload(hints, outline);
2974 if (error)
2975 goto Exit;
2977 /* analyze glyph outline */
2978 #ifdef TA_CONFIG_OPTION_USE_WARPER
2979 if (metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT
2980 || TA_HINTS_DO_HORIZONTAL(hints))
2981 #else
2982 if (TA_HINTS_DO_HORIZONTAL(hints))
2983 #endif
2985 axis = &metrics->axis[TA_DIMENSION_HORZ];
2986 error = ta_latin_hints_detect_features(hints,
2987 axis->width_count,
2988 axis->widths,
2989 TA_DIMENSION_HORZ);
2990 if (error)
2991 goto Exit;
2994 if (TA_HINTS_DO_VERTICAL(hints))
2996 axis = &metrics->axis[TA_DIMENSION_VERT];
2997 error = ta_latin_hints_detect_features(hints,
2998 axis->width_count,
2999 axis->widths,
3000 TA_DIMENSION_VERT);
3001 if (error)
3002 goto Exit;
3004 ta_latin_hints_compute_blue_edges(hints, metrics);
3007 /* grid-fit the outline */
3008 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
3010 #ifdef TA_CONFIG_OPTION_USE_WARPER
3011 if (dim == TA_DIMENSION_HORZ
3012 && metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT)
3014 TA_WarperRec warper;
3015 FT_Fixed scale;
3016 FT_Pos delta;
3019 ta_warper_compute(&warper, hints, (TA_Dimension)dim, &scale, &delta);
3020 ta_glyph_hints_scale_dim(hints, (TA_Dimension)dim, scale, delta);
3022 continue;
3024 #endif
3026 if ((dim == TA_DIMENSION_HORZ && TA_HINTS_DO_HORIZONTAL(hints))
3027 || (dim == TA_DIMENSION_VERT && TA_HINTS_DO_VERTICAL(hints)))
3029 ta_latin_hint_edges(hints, (TA_Dimension)dim);
3030 ta_glyph_hints_align_edge_points(hints, (TA_Dimension)dim);
3031 ta_glyph_hints_align_strong_points(hints, (TA_Dimension)dim);
3032 ta_glyph_hints_align_weak_points(hints, (TA_Dimension)dim);
3036 ta_glyph_hints_save(hints, outline);
3038 Exit:
3039 return error;
3043 const TA_WritingSystemClassRec ta_latin_writing_system_class =
3045 TA_WRITING_SYSTEM_LATIN,
3047 sizeof (TA_LatinMetricsRec),
3049 (TA_WritingSystem_InitMetricsFunc)ta_latin_metrics_init,
3050 (TA_WritingSystem_ScaleMetricsFunc)ta_latin_metrics_scale,
3051 (TA_WritingSystem_DoneMetricsFunc)NULL,
3053 (TA_WritingSystem_InitHintsFunc)ta_latin_hints_init,
3054 (TA_WritingSystem_ApplyHintsFunc)ta_latin_hints_apply
3057 /* end of talatin.c */