Synchronize with FreeType.
[ttfautohint.git] / lib / talatin.c
blob34180e1186c9c37b74bcbe775e7f917e47e2ceaf
1 /* talatin.c */
3 /*
4 * Copyright (C) 2011-2013 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 (script `%s')\n"
51 "=================================================\n"
52 "\n",
53 ta_script_names[metrics->root.script_class->script]));
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_UInt glyph_index;
63 int dim;
64 TA_LatinMetricsRec dummy[1];
65 TA_Scaler scaler = &dummy->root.scaler;
68 glyph_index = FT_Get_Char_Index(
69 face,
70 metrics->root.script_class->standard_char);
71 if (glyph_index == 0)
72 goto Exit;
74 TA_LOG_GLOBAL(("standard character: U+%04lX (glyph index %d)\n",
75 metrics->root.script_class->standard_char, glyph_index));
77 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
78 if (error || face->glyph->outline.n_points <= 0)
79 goto Exit;
81 memset(dummy, 0, sizeof (TA_LatinMetricsRec));
83 dummy->units_per_em = metrics->units_per_em;
85 scaler->x_scale = 0x10000L;
86 scaler->y_scale = 0x10000L;
87 scaler->x_delta = 0;
88 scaler->y_delta = 0;
90 scaler->face = face;
91 scaler->render_mode = FT_RENDER_MODE_NORMAL;
92 scaler->flags = 0;
94 ta_glyph_hints_rescale(hints, (TA_ScriptMetrics)dummy);
96 error = ta_glyph_hints_reload(hints, &face->glyph->outline);
97 if (error)
98 goto Exit;
100 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
102 TA_LatinAxis axis = &metrics->axis[dim];
103 TA_AxisHints axhints = &hints->axis[dim];
105 TA_Segment seg, limit, link;
106 FT_UInt num_widths = 0;
109 error = ta_latin_hints_compute_segments(hints, (TA_Dimension)dim);
110 if (error)
111 goto Exit;
113 ta_latin_hints_link_segments(hints, (TA_Dimension)dim);
115 seg = axhints->segments;
116 limit = seg + axhints->num_segments;
118 for (; seg < limit; seg++)
120 link = seg->link;
122 /* we only consider stem segments there! */
123 if (link
124 && link->link == seg
125 && link > seg)
127 FT_Pos dist;
130 dist = seg->pos - link->pos;
131 if (dist < 0)
132 dist = -dist;
134 if (num_widths < TA_LATIN_MAX_WIDTHS)
135 axis->widths[num_widths++].org = dist;
139 /* this also replaces multiple almost identical stem widths */
140 /* with a single one (the value 100 is heuristic) */
141 ta_sort_and_quantize_widths(&num_widths, axis->widths,
142 dummy->units_per_em / 100);
143 axis->width_count = num_widths;
146 Exit:
147 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
149 TA_LatinAxis axis = &metrics->axis[dim];
150 FT_Pos stdw;
153 stdw = (axis->width_count > 0) ? axis->widths[0].org
154 : TA_LATIN_CONSTANT(metrics, 50);
156 /* let's try 20% of the smallest width */
157 axis->edge_distance_threshold = stdw / 5;
158 axis->standard_width = stdw;
159 axis->extra_light = 0;
161 #ifdef TA_DEBUG
163 FT_UInt i;
166 TA_LOG_GLOBAL(("%s widths:\n",
167 dim == TA_DIMENSION_VERT ? "horizontal"
168 : "vertical"));
170 TA_LOG_GLOBAL((" %d (standard)", axis->standard_width));
171 for (i = 1; i < axis->width_count; i++)
172 TA_LOG_GLOBAL((" %d", axis->widths[i].org));
174 TA_LOG_GLOBAL(("\n"));
176 #endif
180 TA_LOG_GLOBAL(("\n"));
182 ta_glyph_hints_done(hints);
186 /* find all blue zones; flat segments give the reference points, */
187 /* round segments the overshoot positions */
189 static void
190 ta_latin_metrics_init_blues(TA_LatinMetrics metrics,
191 FT_Face face)
193 FT_Pos flats[TA_BLUE_STRING_MAX_LEN];
194 FT_Pos rounds[TA_BLUE_STRING_MAX_LEN];
195 FT_Int num_flats;
196 FT_Int num_rounds;
198 TA_LatinBlue blue;
199 FT_Error error;
200 TA_LatinAxis axis = &metrics->axis[TA_DIMENSION_VERT];
201 FT_Outline outline;
203 TA_Blue_Stringset bss = metrics->root.script_class->blue_stringset;
204 const TA_Blue_StringRec* bs = &ta_blue_stringsets[bss];
207 /* we walk over the blue character strings as specified in the */
208 /* script's entry in the `af_blue_stringset' array */
210 TA_LOG_GLOBAL(("latin blue zones computation\n"
211 "============================\n"
212 "\n"));
214 for (; bs->string != TA_BLUE_STRING_MAX; bs++)
216 const char* p = &ta_blue_strings[bs->string];
217 FT_Pos* blue_ref;
218 FT_Pos* blue_shoot;
221 #ifdef TA_DEBUG
223 FT_Bool have_flag = 0;
226 TA_LOG_GLOBAL(("blue zone %d", axis->blue_count));
228 if (bs->properties)
230 TA_LOG_GLOBAL((" ("));
232 if (TA_LATIN_IS_TOP_BLUE(bs))
234 TA_LOG_GLOBAL(("top"));
235 have_flag = 1;
238 if (TA_LATIN_IS_X_HEIGHT_BLUE(bs))
240 if (have_flag)
241 TA_LOG_GLOBAL((", "));
242 TA_LOG_GLOBAL(("small top"));
243 have_flag = 1;
246 if (TA_LATIN_IS_LONG_BLUE(bs))
248 if (have_flag)
249 TA_LOG_GLOBAL((", "));
250 TA_LOG_GLOBAL(("long"));
253 TA_LOG_GLOBAL((")"));
256 TA_LOG_GLOBAL((":\n"));
258 #endif /* TA_DEBUG */
260 num_flats = 0;
261 num_rounds = 0;
263 while (*p)
265 FT_ULong ch;
266 FT_UInt glyph_index;
267 FT_Pos best_y; /* same as points.y */
268 FT_Int best_point, best_contour_first, best_contour_last;
269 FT_Vector* points;
270 FT_Bool round = 0;
273 GET_UTF8_CHAR(ch, p);
275 /* load the character in the face -- skip unknown or empty ones */
276 glyph_index = FT_Get_Char_Index(face, ch);
277 if (glyph_index == 0)
279 TA_LOG_GLOBAL((" U+%04lX unavailable\n", ch));
280 continue;
283 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
284 outline = face->glyph->outline;
285 if (error || outline.n_points <= 0)
287 TA_LOG_GLOBAL((" U+%04lX contains no outlines\n", ch));
288 continue;
291 /* now compute min or max point indices and coordinates */
292 points = outline.points;
293 best_point = -1;
294 best_y = 0; /* make compiler happy */
295 best_contour_first = 0; /* ditto */
296 best_contour_last = 0; /* ditto */
299 FT_Int nn;
300 FT_Int first = 0;
301 FT_Int last = -1;
304 for (nn = 0; nn < outline.n_contours; first = last + 1, nn++)
306 FT_Int old_best_point = best_point;
307 FT_Int pp;
310 last = outline.contours[nn];
312 /* avoid single-point contours since they are never rasterized; */
313 /* in some fonts, they correspond to mark attachment points */
314 /* that are way outside of the glyph's real outline */
315 if (last <= first)
316 continue;
318 if (TA_LATIN_IS_TOP_BLUE(bs))
320 for (pp = first; pp <= last; pp++)
321 if (best_point < 0
322 || points[pp].y > best_y)
324 best_point = pp;
325 best_y = points[pp].y;
328 else
330 for (pp = first; pp <= last; pp++)
331 if (best_point < 0
332 || points[pp].y < best_y)
334 best_point = pp;
335 best_y = points[pp].y;
339 if (best_point != old_best_point)
341 best_contour_first = first;
342 best_contour_last = last;
347 /* now check whether the point belongs to a straight or round */
348 /* segment; we first need to find in which contour the extremum */
349 /* lies, then inspect its previous and next points */
350 if (best_point >= 0)
352 FT_Pos best_x = points[best_point].x;
353 FT_Int prev, next;
354 FT_Int best_segment_first, best_segment_last;
355 FT_Int best_on_point_first, best_on_point_last;
356 FT_Pos dist;
359 best_segment_first = best_point;
360 best_segment_last = best_point;
362 if (FT_CURVE_TAG(outline.tags[best_point]) == FT_CURVE_TAG_ON)
364 best_on_point_first = best_point;
365 best_on_point_last = best_point;
367 else
369 best_on_point_first = -1;
370 best_on_point_last = -1;
373 /* look for the previous and next points on the contour */
374 /* that are not on the same Y coordinate, then threshold */
375 /* the `closeness'... */
376 prev = best_point;
377 next = prev;
381 if (prev > best_contour_first)
382 prev--;
383 else
384 prev = best_contour_last;
386 dist = TA_ABS(points[prev].y - best_y);
387 /* accept a small distance or a small angle (both values are */
388 /* heuristic; value 20 corresponds to approx. 2.9 degrees) */
389 if (dist > 5)
390 if (TA_ABS(points[prev].x - best_x) <= 20 * dist)
391 break;
393 best_segment_first = prev;
395 if (FT_CURVE_TAG(outline.tags[prev]) == FT_CURVE_TAG_ON)
397 best_on_point_first = prev;
398 if (best_on_point_last < 0)
399 best_on_point_last = prev;
402 } while (prev != best_point);
406 if (next < best_contour_last)
407 next++;
408 else
409 next = best_contour_first;
411 dist = TA_ABS(points[next].y - best_y);
412 if (dist > 5)
413 if (TA_ABS(points[next].x - best_x) <= 20 * dist)
414 break;
416 best_segment_last = next;
418 if (FT_CURVE_TAG(outline.tags[next]) == FT_CURVE_TAG_ON)
420 best_on_point_last = next;
421 if (best_on_point_first < 0)
422 best_on_point_first = next;
425 } while (next != best_point);
427 if (TA_LATIN_IS_LONG_BLUE(bs))
429 /* If this flag is set, we have an additional constraint to */
430 /* get the blue zone distance: Find a segment of the topmost */
431 /* (or bottommost) contour that is longer than a heuristic */
432 /* threshold. This ensures that small bumps in the outline */
433 /* are ignored (for example, the `vertical serifs' found in */
434 /* many Hebrew glyph designs). */
436 /* If this segment is long enough, we are done. Otherwise, */
437 /* search the segment next to the extremum that is long */
438 /* enough, has the same direction, and a not too large */
439 /* vertical distance from the extremum. Note that the */
440 /* algorithm doesn't check whether the found segment is */
441 /* actually the one (vertically) nearest to the extremum. */
443 /* heuristic threshold value */
444 FT_Pos length_threshold = metrics->units_per_em / 25;
447 dist = TA_ABS(points[best_segment_last].x -
448 points[best_segment_first].x);
450 if (dist < length_threshold
451 && best_segment_last - best_segment_first + 2 <=
452 best_contour_last - best_contour_first)
454 /* heuristic threshold value */
455 FT_Pos height_threshold = metrics->units_per_em / 4;
457 FT_Int first;
458 FT_Int last;
459 FT_Bool hit;
461 FT_Bool left2right;
464 /* compute direction */
465 prev = best_point;
469 if (prev > best_contour_first)
470 prev--;
471 else
472 prev = best_contour_last;
474 if (points[prev].x != best_x)
475 break;
476 } while (prev != best_point);
478 /* skip glyph for the degenerate case */
479 if (prev == best_point)
480 continue;
482 left2right = FT_BOOL(points[prev].x < points[best_point].x);
484 first = best_segment_last;
485 last = first;
486 hit = 0;
490 FT_Bool l2r;
491 FT_Pos d;
492 FT_Int p_first, p_last;
495 if (!hit)
497 /* no hit; adjust first point */
498 first = last;
500 /* also adjust first and last on point */
501 if (FT_CURVE_TAG(outline.tags[first]) == FT_CURVE_TAG_ON)
503 p_first = first;
504 p_last = first;
506 else
508 p_first = -1;
509 p_last = -1;
512 hit = 1;
515 if (last < best_contour_last)
516 last++;
517 else
518 last = best_contour_first;
520 if (TA_ABS(best_y - points[first].y) > height_threshold)
522 /* vertical distance too large */
523 hit = 0;
524 continue;
527 /* same test as above */
528 dist = TA_ABS(points[last].y - points[first].y);
529 if (dist > 5)
530 if (TA_ABS(points[last].x - points[first].x) <= 20 * dist)
532 hit = 0;
533 continue;
536 if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON)
538 p_last = last;
539 if (p_first < 0)
540 p_first = last;
543 l2r = FT_BOOL(points[first].x < points[last].x);
544 d = TA_ABS(points[last].x - points[first].x);
546 if (l2r == left2right
547 && d >= length_threshold)
549 /* all constraints are met; update segment after finding */
550 /* its end */
553 if (last < best_contour_last)
554 last++;
555 else
556 last = best_contour_first;
558 d = TA_ABS(points[last].y - points[first].y);
559 if (d > 5)
560 if (TA_ABS(points[next].x - points[first].x) <=
561 20 * dist)
563 last--;
564 break;
567 p_last = last;
569 if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON)
571 p_last = last;
572 if (p_first < 0)
573 p_first = last;
575 } while (last != best_segment_first);
577 best_y = points[first].y;
579 best_segment_first = first;
580 best_segment_last = last;
582 best_on_point_first = p_first;
583 best_on_point_last = p_last;
585 break;
587 } while (last != best_segment_first);
591 TA_LOG_GLOBAL((" U+%04lX: best_y = %5ld", ch, best_y));
594 * now set the `round' flag depending on the segment's kind:
596 * - if the horizontal distance between the first and last
597 * `on' point is larger than upem/8 (value 8 is heuristic)
598 * we have a flat segment
599 * - if either the first or the last point of the segment is
600 * an `off' point, the segment is round, otherwise it is
601 * flat
603 if (best_on_point_first >= 0
604 && best_on_point_last >= 0
605 && (FT_UInt)(TA_ABS(points[best_on_point_last].x
606 - points[best_on_point_first].x))
607 > metrics->units_per_em / 8)
608 round = 0;
609 else
610 round = FT_BOOL(FT_CURVE_TAG(outline.tags[best_segment_first])
611 != FT_CURVE_TAG_ON
612 || FT_CURVE_TAG(outline.tags[best_segment_last])
613 != FT_CURVE_TAG_ON);
615 TA_LOG_GLOBAL((" (%s)\n", round ? "round" : "flat"));
618 if (round)
619 rounds[num_rounds++] = best_y;
620 else
621 flats[num_flats++] = best_y;
624 if (num_flats == 0 && num_rounds == 0)
626 /* we couldn't find a single glyph to compute this blue zone, */
627 /* we will simply ignore it then */
628 TA_LOG_GLOBAL((" empty\n"));
629 continue;
632 /* we have computed the contents of the `rounds' and `flats' tables, */
633 /* now determine the reference and overshoot position of the blue -- */
634 /* we simply take the median value after a simple sort */
635 ta_sort_pos(num_rounds, rounds);
636 ta_sort_pos(num_flats, flats);
638 blue = &axis->blues[axis->blue_count];
639 blue_ref = &blue->ref.org;
640 blue_shoot = &blue->shoot.org;
642 axis->blue_count++;
644 if (num_flats == 0)
646 *blue_ref =
647 *blue_shoot = rounds[num_rounds / 2];
649 else if (num_rounds == 0)
651 *blue_ref =
652 *blue_shoot = flats[num_flats / 2];
654 else
656 *blue_ref = flats[num_flats / 2];
657 *blue_shoot = rounds[num_rounds / 2];
660 /* there are sometimes problems if the overshoot position of top */
661 /* zones is under its reference position, or the opposite for bottom */
662 /* zones; we must thus check everything there and correct the errors */
663 if (*blue_shoot != *blue_ref)
665 FT_Pos ref = *blue_ref;
666 FT_Pos shoot = *blue_shoot;
667 FT_Bool over_ref = FT_BOOL(shoot > ref);
670 if (TA_LATIN_IS_TOP_BLUE(bs) ^ over_ref)
672 *blue_ref =
673 *blue_shoot = (shoot + ref) / 2;
675 TA_LOG_GLOBAL((" [overshoot smaller than reference,"
676 " taking mean value]\n"));
680 blue->flags = 0;
681 if (TA_LATIN_IS_TOP_BLUE(bs))
682 blue->flags |= TA_LATIN_BLUE_TOP;
684 /* the following flag is used later to adjust the y and x scales */
685 /* in order to optimize the pixel grid alignment */
686 /* of the top of small letters */
687 if (TA_LATIN_IS_X_HEIGHT_BLUE(bs))
688 blue->flags |= TA_LATIN_BLUE_ADJUSTMENT;
690 TA_LOG_GLOBAL((" -> reference = %ld\n"
691 " overshoot = %ld\n",
692 *blue_ref, *blue_shoot));
695 /* add two blue zones for usWinAscent and usWinDescent */
696 /* just in case the above algorithm has missed them -- */
697 /* Windows cuts off everything outside of those two values */
699 TT_OS2* os2;
702 os2 = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
704 if (os2)
706 blue = &axis->blues[axis->blue_count];
707 blue->flags = TA_LATIN_BLUE_TOP | TA_LATIN_BLUE_ACTIVE;
708 blue->ref.org =
709 blue->shoot.org = os2->usWinAscent;
711 TA_LOG_GLOBAL(("artificial blue zone for usWinAscent:\n"
712 " -> reference = %ld\n"
713 " overshoot = %ld\n",
714 blue->ref.org, blue->shoot.org));
716 blue = &axis->blues[axis->blue_count + 1];
717 blue->flags = TA_LATIN_BLUE_ACTIVE;
718 blue->ref.org =
719 blue->shoot.org = -os2->usWinDescent;
721 TA_LOG_GLOBAL(("artificial blue zone for usWinDescent:\n"
722 " -> reference = %ld\n"
723 " overshoot = %ld\n",
724 blue->ref.org, blue->shoot.org));
726 else
728 blue = &axis->blues[axis->blue_count];
729 blue->flags =
730 blue->ref.org =
731 blue->shoot.org = 0;
733 blue = &axis->blues[axis->blue_count + 1];
734 blue->flags =
735 blue->ref.org =
736 blue->shoot.org = 0;
740 TA_LOG_GLOBAL(("\n"));
742 return;
746 /* check whether all ASCII digits have the same advance width */
748 void
749 ta_latin_metrics_check_digits(TA_LatinMetrics metrics,
750 FT_Face face)
752 FT_UInt i;
753 FT_Bool started = 0, same_width = 1;
754 FT_Fixed advance, old_advance = 0;
757 /* digit `0' is 0x30 in all supported charmaps */
758 for (i = 0x30; i <= 0x39; i++)
760 FT_UInt glyph_index;
763 glyph_index = FT_Get_Char_Index(face, i);
764 if (glyph_index == 0)
765 continue;
767 if (FT_Get_Advance(face, glyph_index,
768 FT_LOAD_NO_SCALE
769 | FT_LOAD_NO_HINTING
770 | FT_LOAD_IGNORE_TRANSFORM,
771 &advance))
772 continue;
774 if (started)
776 if (advance != old_advance)
778 same_width = 0;
779 break;
782 else
784 old_advance = advance;
785 started = 1;
789 metrics->root.digits_have_same_width = same_width;
793 /* initialize global metrics */
795 FT_Error
796 ta_latin_metrics_init(TA_LatinMetrics metrics,
797 FT_Face face)
799 FT_CharMap oldmap = face->charmap;
802 metrics->units_per_em = face->units_per_EM;
804 if (!FT_Select_Charmap(face, FT_ENCODING_UNICODE))
806 ta_latin_metrics_init_widths(metrics, face);
807 ta_latin_metrics_init_blues(metrics, face);
808 ta_latin_metrics_check_digits(metrics, face);
811 FT_Set_Charmap(face, oldmap);
812 return FT_Err_Ok;
816 /* adjust scaling value, then scale and shift widths */
817 /* and blue zones (if applicable) for given dimension */
819 static void
820 ta_latin_metrics_scale_dim(TA_LatinMetrics metrics,
821 TA_Scaler scaler,
822 TA_Dimension dim)
824 FT_Fixed scale;
825 FT_Pos delta;
826 TA_LatinAxis axis;
827 FT_UInt ppem;
828 FT_UInt nn;
831 ppem = metrics->root.scaler.face->size->metrics.x_ppem;
833 if (dim == TA_DIMENSION_HORZ)
835 scale = scaler->x_scale;
836 delta = scaler->x_delta;
838 else
840 scale = scaler->y_scale;
841 delta = scaler->y_delta;
844 axis = &metrics->axis[dim];
846 if (axis->org_scale == scale && axis->org_delta == delta)
847 return;
849 axis->org_scale = scale;
850 axis->org_delta = delta;
852 /* correct Y scale to optimize the alignment of the top of */
853 /* small letters to the pixel grid */
854 /* (if we do x-height snapping for this ppem value) */
855 if (!number_set_is_element(
856 metrics->root.globals->font->x_height_snapping_exceptions,
857 ppem))
859 TA_LatinAxis Axis = &metrics->axis[TA_DIMENSION_VERT];
860 TA_LatinBlue blue = NULL;
863 for (nn = 0; nn < Axis->blue_count; nn++)
865 if (Axis->blues[nn].flags & TA_LATIN_BLUE_ADJUSTMENT)
867 blue = &Axis->blues[nn];
868 break;
872 if (blue)
874 FT_Pos scaled;
875 FT_Pos threshold;
876 FT_Pos fitted;
877 FT_UInt limit;
880 scaled = FT_MulFix(blue->shoot.org, scaler->y_scale);
881 limit = metrics->root.globals->increase_x_height;
882 threshold = 40;
884 /* if the `increase-x-height' property is active, */
885 /* we round up much more often */
886 if (limit
887 && ppem <= limit
888 && ppem >= TA_PROP_INCREASE_X_HEIGHT_MIN)
889 threshold = 52;
891 fitted = (scaled + threshold) & ~63;
893 if (scaled != fitted)
895 if (dim == TA_DIMENSION_VERT)
897 scale = FT_MulDiv(scale, fitted, scaled);
899 TA_LOG_GLOBAL((
900 "ta_latin_metrics_scale_dim:"
901 " x height alignment (script `%s'):\n"
903 " vertical scaling changed from %.4f to %.4f (by %d%%)\n"
904 "\n",
905 ta_script_names[metrics->root.script_class->script],
906 axis->org_scale / 65536.0,
907 scale / 65536.0,
908 (fitted - scaled) * 100 / scaled));
914 axis->scale = scale;
915 axis->delta = delta;
917 if (dim == TA_DIMENSION_HORZ)
919 metrics->root.scaler.x_scale = scale;
920 metrics->root.scaler.x_delta = delta;
922 else
924 metrics->root.scaler.y_scale = scale;
925 metrics->root.scaler.y_delta = delta;
928 TA_LOG_GLOBAL(("%s widths (script `%s')\n",
929 dim == TA_DIMENSION_HORZ ? "horizontal" : "vertical",
930 ta_script_names[metrics->root.script_class->script]));
932 /* scale the widths */
933 for (nn = 0; nn < axis->width_count; nn++)
935 TA_Width width = axis->widths + nn;
938 width->cur = FT_MulFix(width->org, scale);
939 width->fit = width->cur;
941 TA_LOG_GLOBAL((" %d scaled to %.2f\n",
942 width->org,
943 width->cur / 64.0));
946 TA_LOG_GLOBAL(("\n"));
948 /* an extra-light axis corresponds to a standard width that is */
949 /* smaller than 5/8 pixels */
950 axis->extra_light =
951 (FT_Bool)(FT_MulFix(axis->standard_width, scale) < 32 + 8);
953 #ifdef TA_DEBUG
954 if (axis->extra_light)
955 TA_LOG_GLOBAL(("`%s' script is extra light (at current resolution)\n"
956 "\n",
957 ta_script_names[metrics->root.script_class->script]));
958 #endif
960 if (dim == TA_DIMENSION_VERT)
962 TA_LOG_GLOBAL(("blue zones (script `%s')\n",
963 ta_script_names[metrics->root.script_class->script]));
965 /* scale the blue zones */
966 for (nn = 0; nn < axis->blue_count; nn++)
968 TA_LatinBlue blue = &axis->blues[nn];
969 FT_Pos dist;
972 blue->ref.cur = FT_MulFix(blue->ref.org, scale) + delta;
973 blue->ref.fit = blue->ref.cur;
974 blue->shoot.cur = FT_MulFix(blue->shoot.org, scale) + delta;
975 blue->shoot.fit = blue->shoot.cur;
976 blue->flags &= ~TA_LATIN_BLUE_ACTIVE;
978 /* a blue zone is only active if it is less than 3/4 pixels tall */
979 dist = FT_MulFix(blue->ref.org - blue->shoot.org, scale);
980 if (dist <= 48 && dist >= -48)
982 #if 0
983 FT_Pos delta1;
984 #endif
985 FT_Pos delta2;
988 /* use discrete values for blue zone widths */
990 #if 0
991 /* generic, original code */
992 delta1 = blue->shoot.org - blue->ref.org;
993 delta2 = delta1;
994 if (delta1 < 0)
995 delta2 = -delta2;
997 delta2 = FT_MulFix(delta2, scale);
999 if (delta2 < 32)
1000 delta2 = 0;
1001 else if (delta2 < 64)
1002 delta2 = 32 + (((delta2 - 32) + 16) & ~31);
1003 else
1004 delta2 = TA_PIX_ROUND(delta2);
1006 if (delta1 < 0)
1007 delta2 = -delta2;
1009 blue->ref.fit = TA_PIX_ROUND(blue->ref.cur);
1010 blue->shoot.fit = blue->ref.fit + delta2;
1011 #else
1012 /* simplified version due to abs(dist) <= 48 */
1013 delta2 = dist;
1014 if (dist < 0)
1015 delta2 = -delta2;
1017 if (delta2 < 32)
1018 delta2 = 0;
1019 else if (delta2 < 48)
1020 delta2 = 32;
1021 else
1022 delta2 = 64;
1024 if (dist < 0)
1025 delta2 = -delta2;
1027 blue->ref.fit = TA_PIX_ROUND(blue->ref.cur);
1028 blue->shoot.fit = blue->ref.fit - delta2;
1029 #endif
1031 blue->flags |= TA_LATIN_BLUE_ACTIVE;
1033 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f%s\n"
1034 " overshoot %d: %d scaled to %.2f%s\n",
1036 blue->ref.org,
1037 blue->ref.fit / 64.0,
1038 blue->flags & TA_LATIN_BLUE_ACTIVE ? ""
1039 : " (inactive)",
1041 blue->shoot.org,
1042 blue->shoot.fit / 64.0,
1043 blue->flags & TA_LATIN_BLUE_ACTIVE ? ""
1044 : " (inactive)"));
1048 /* the last two artificial blue zones are to be scaled */
1049 /* with uncorrected scaling values */
1051 TA_LatinAxis a = &metrics->axis[TA_DIMENSION_VERT];
1052 TA_LatinBlue b;
1055 b = &a->blues[a->blue_count];
1056 b->ref.cur =
1057 b->ref.fit =
1058 b->shoot.cur =
1059 b->shoot.fit = FT_MulFix(b->ref.org, a->org_scale) + delta;
1061 b = &a->blues[a->blue_count + 1];
1062 b->ref.cur =
1063 b->ref.fit =
1064 b->shoot.cur =
1065 b->shoot.fit = FT_MulFix(b->ref.org, a->org_scale) + delta;
1068 TA_LOG_GLOBAL(("\n"));
1073 /* scale global values in both directions */
1075 void
1076 ta_latin_metrics_scale(TA_LatinMetrics metrics,
1077 TA_Scaler scaler)
1079 metrics->root.scaler.render_mode = scaler->render_mode;
1080 metrics->root.scaler.face = scaler->face;
1081 metrics->root.scaler.flags = scaler->flags;
1083 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_HORZ);
1084 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_VERT);
1088 /* walk over all contours and compute its segments */
1090 FT_Error
1091 ta_latin_hints_compute_segments(TA_GlyphHints hints,
1092 TA_Dimension dim)
1094 TA_AxisHints axis = &hints->axis[dim];
1095 FT_Error error = FT_Err_Ok;
1097 TA_Segment segment = NULL;
1098 TA_SegmentRec seg0;
1100 TA_Point* contour = hints->contours;
1101 TA_Point* contour_limit = contour + hints->num_contours;
1102 TA_Direction major_dir, segment_dir;
1105 memset(&seg0, 0, sizeof (TA_SegmentRec));
1106 seg0.score = 32000;
1107 seg0.flags = TA_EDGE_NORMAL;
1109 major_dir = (TA_Direction)TA_ABS(axis->major_dir);
1110 segment_dir = major_dir;
1112 axis->num_segments = 0;
1114 /* set up (u,v) in each point */
1115 if (dim == TA_DIMENSION_HORZ)
1117 TA_Point point = hints->points;
1118 TA_Point limit = point + hints->num_points;
1121 for (; point < limit; point++)
1123 point->u = point->fx;
1124 point->v = point->fy;
1127 else
1129 TA_Point point = hints->points;
1130 TA_Point limit = point + hints->num_points;
1133 for (; point < limit; point++)
1135 point->u = point->fy;
1136 point->v = point->fx;
1140 /* do each contour separately */
1141 for (; contour < contour_limit; contour++)
1143 TA_Point point = contour[0];
1144 TA_Point last = point->prev;
1146 int on_edge = 0;
1148 FT_Pos min_pos = 32000; /* minimum segment pos != min_coord */
1149 FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */
1150 FT_Bool passed;
1153 if (point == last) /* skip singletons -- just in case */
1154 continue;
1156 if (TA_ABS(last->out_dir) == major_dir
1157 && TA_ABS(point->out_dir) == major_dir)
1159 /* we are already on an edge, try to locate its start */
1160 last = point;
1162 for (;;)
1164 point = point->prev;
1165 if (TA_ABS(point->out_dir) != major_dir)
1167 point = point->next;
1168 break;
1170 if (point == last)
1171 break;
1175 last = point;
1176 passed = 0;
1178 for (;;)
1180 FT_Pos u, v;
1183 if (on_edge)
1185 u = point->u;
1186 if (u < min_pos)
1187 min_pos = u;
1188 if (u > max_pos)
1189 max_pos = u;
1191 if (point->out_dir != segment_dir
1192 || point == last)
1194 /* we are just leaving an edge; record a new segment! */
1195 segment->last = point;
1196 segment->pos = (FT_Short)((min_pos + max_pos) >> 1);
1198 /* a segment is round if either its first or last point */
1199 /* is a control point */
1200 if ((segment->first->flags | point->flags) & TA_FLAG_CONTROL)
1201 segment->flags |= TA_EDGE_ROUND;
1203 /* compute segment size */
1204 min_pos = max_pos = point->v;
1206 v = segment->first->v;
1207 if (v < min_pos)
1208 min_pos = v;
1209 if (v > max_pos)
1210 max_pos = v;
1212 segment->min_coord = (FT_Short)min_pos;
1213 segment->max_coord = (FT_Short)max_pos;
1214 segment->height = (FT_Short)(segment->max_coord -
1215 segment->min_coord);
1217 on_edge = 0;
1218 segment = NULL;
1219 /* fall through */
1223 /* now exit if we are at the start/end point */
1224 if (point == last)
1226 if (passed)
1227 break;
1228 passed = 1;
1231 if (!on_edge
1232 && TA_ABS(point->out_dir) == major_dir)
1234 /* this is the start of a new segment! */
1235 segment_dir = (TA_Direction)point->out_dir;
1237 /* clear all segment fields */
1238 error = ta_axis_hints_new_segment(axis, &segment);
1239 if (error)
1240 goto Exit;
1242 segment[0] = seg0;
1243 segment->dir = (FT_Char)segment_dir;
1244 min_pos = max_pos = point->u;
1245 segment->first = point;
1246 segment->last = point;
1247 on_edge = 1;
1250 point = point->next;
1252 } /* contours */
1255 /* now slightly increase the height of segments if this makes sense -- */
1256 /* this is used to better detect and ignore serifs */
1258 TA_Segment segments = axis->segments;
1259 TA_Segment segments_end = segments + axis->num_segments;
1262 for (segment = segments; segment < segments_end; segment++)
1264 TA_Point first = segment->first;
1265 TA_Point last = segment->last;
1267 FT_Pos first_v = first->v;
1268 FT_Pos last_v = last->v;
1271 if (first == last)
1272 continue;
1274 if (first_v < last_v)
1276 TA_Point p;
1279 p = first->prev;
1280 if (p->v < first_v)
1281 segment->height = (FT_Short)(segment->height +
1282 ((first_v - p->v) >> 1));
1284 p = last->next;
1285 if (p->v > last_v)
1286 segment->height = (FT_Short)(segment->height +
1287 ((p->v - last_v) >> 1));
1289 else
1291 TA_Point p;
1294 p = first->prev;
1295 if (p->v > first_v)
1296 segment->height = (FT_Short)(segment->height +
1297 ((p->v - first_v) >> 1));
1299 p = last->next;
1300 if (p->v < last_v)
1301 segment->height = (FT_Short)(segment->height +
1302 ((last_v - p->v) >> 1));
1307 Exit:
1308 return error;
1312 /* link segments to form stems and serifs */
1314 void
1315 ta_latin_hints_link_segments(TA_GlyphHints hints,
1316 TA_Dimension dim)
1318 TA_AxisHints axis = &hints->axis[dim];
1320 TA_Segment segments = axis->segments;
1321 TA_Segment segment_limit = segments + axis->num_segments;
1323 FT_Pos len_threshold, len_score;
1324 TA_Segment seg1, seg2;
1327 len_threshold = TA_LATIN_CONSTANT(hints->metrics, 8);
1328 if (len_threshold == 0)
1329 len_threshold = 1;
1331 len_score = TA_LATIN_CONSTANT(hints->metrics, 6000);
1333 /* now compare each segment to the others */
1334 for (seg1 = segments; seg1 < segment_limit; seg1++)
1336 /* the fake segments are introduced to hint the metrics -- */
1337 /* we must never link them to anything */
1338 if (seg1->dir != axis->major_dir
1339 || seg1->first == seg1->last)
1340 continue;
1342 /* search for stems having opposite directions, */
1343 /* with seg1 to the `left' of seg2 */
1344 for (seg2 = segments; seg2 < segment_limit; seg2++)
1346 FT_Pos pos1 = seg1->pos;
1347 FT_Pos pos2 = seg2->pos;
1350 if (seg1->dir + seg2->dir == 0
1351 && pos2 > pos1)
1353 /* compute distance between the two segments */
1354 FT_Pos dist = pos2 - pos1;
1355 FT_Pos min = seg1->min_coord;
1356 FT_Pos max = seg1->max_coord;
1357 FT_Pos len, score;
1360 if (min < seg2->min_coord)
1361 min = seg2->min_coord;
1362 if (max > seg2->max_coord)
1363 max = seg2->max_coord;
1365 /* compute maximum coordinate difference of the two segments */
1366 len = max - min;
1367 if (len >= len_threshold)
1369 /* small coordinate differences cause a higher score, and */
1370 /* segments with a greater distance cause a higher score also */
1371 score = dist + len_score / len;
1373 /* and we search for the smallest score */
1374 /* of the sum of the two values */
1375 if (score < seg1->score)
1377 seg1->score = score;
1378 seg1->link = seg2;
1381 if (score < seg2->score)
1383 seg2->score = score;
1384 seg2->link = seg1;
1391 /* now compute the `serif' segments, cf. explanations in `tahints.h' */
1392 for (seg1 = segments; seg1 < segment_limit; seg1++)
1394 seg2 = seg1->link;
1396 if (seg2)
1398 if (seg2->link != seg1)
1400 seg1->link = 0;
1401 seg1->serif = seg2->link;
1408 /* link segments to edges, using feature analysis for selection */
1410 FT_Error
1411 ta_latin_hints_compute_edges(TA_GlyphHints hints,
1412 TA_Dimension dim)
1414 TA_AxisHints axis = &hints->axis[dim];
1415 FT_Error error = FT_Err_Ok;
1416 TA_LatinAxis laxis = &((TA_LatinMetrics)hints->metrics)->axis[dim];
1418 TA_Segment segments = axis->segments;
1419 TA_Segment segment_limit = segments + axis->num_segments;
1420 TA_Segment seg;
1422 #if 0
1423 TA_Direction up_dir;
1424 #endif
1425 FT_Fixed scale;
1426 FT_Pos edge_distance_threshold;
1427 FT_Pos segment_length_threshold;
1430 axis->num_edges = 0;
1432 scale = (dim == TA_DIMENSION_HORZ) ? hints->x_scale
1433 : hints->y_scale;
1435 #if 0
1436 up_dir = (dim == TA_DIMENSION_HORZ) ? TA_DIR_UP
1437 : TA_DIR_RIGHT;
1438 #endif
1440 /* we ignore all segments that are less than 1 pixel in length */
1441 /* to avoid many problems with serif fonts */
1442 /* (the corresponding threshold is computed in font units) */
1443 if (dim == TA_DIMENSION_HORZ)
1444 segment_length_threshold = FT_DivFix(64, hints->y_scale);
1445 else
1446 segment_length_threshold = 0;
1448 /********************************************************************/
1449 /* */
1450 /* We begin by generating a sorted table of edges for the current */
1451 /* direction. To do so, we simply scan each segment and try to find */
1452 /* an edge in our table that corresponds to its position. */
1453 /* */
1454 /* If no edge is found, we create and insert a new edge in the */
1455 /* sorted table. Otherwise, we simply add the segment to the edge's */
1456 /* list which gets processed in the second step to compute the */
1457 /* edge's properties. */
1458 /* */
1459 /* Note that the table of edges is sorted along the segment/edge */
1460 /* position. */
1461 /* */
1462 /********************************************************************/
1464 /* assure that edge distance threshold is at most 0.25px */
1465 edge_distance_threshold = FT_MulFix(laxis->edge_distance_threshold,
1466 scale);
1467 if (edge_distance_threshold > 64 / 4)
1468 edge_distance_threshold = 64 / 4;
1470 edge_distance_threshold = FT_DivFix(edge_distance_threshold,
1471 scale);
1473 for (seg = segments; seg < segment_limit; seg++)
1475 TA_Edge found = NULL;
1476 FT_Int ee;
1479 if (seg->height < segment_length_threshold)
1480 continue;
1482 /* a special case for serif edges: */
1483 /* if they are smaller than 1.5 pixels we ignore them */
1484 if (seg->serif
1485 && 2 * seg->height < 3 * segment_length_threshold)
1486 continue;
1488 /* look for an edge corresponding to the segment */
1489 for (ee = 0; ee < axis->num_edges; ee++)
1491 TA_Edge edge = axis->edges + ee;
1492 FT_Pos dist;
1495 dist = seg->pos - edge->fpos;
1496 if (dist < 0)
1497 dist = -dist;
1499 if (dist < edge_distance_threshold && edge->dir == seg->dir)
1501 found = edge;
1502 break;
1506 if (!found)
1508 TA_Edge edge;
1511 /* insert a new edge in the list and sort according to the position */
1512 error = ta_axis_hints_new_edge(axis, seg->pos,
1513 (TA_Direction)seg->dir,
1514 &edge);
1515 if (error)
1516 goto Exit;
1518 /* add the segment to the new edge's list */
1519 memset(edge, 0, sizeof (TA_EdgeRec));
1520 edge->first = seg;
1521 edge->last = seg;
1522 edge->dir = seg->dir;
1523 edge->fpos = seg->pos;
1524 edge->opos = FT_MulFix(seg->pos, scale);
1525 edge->pos = edge->opos;
1526 seg->edge_next = seg;
1528 else
1530 /* if an edge was found, simply add the segment to the edge's list */
1531 seg->edge_next = found->first;
1532 found->last->edge_next = seg;
1533 found->last = seg;
1537 /*****************************************************************/
1538 /* */
1539 /* Good, we now compute each edge's properties according to */
1540 /* the segments found on its position. Basically, these are */
1541 /* */
1542 /* - the edge's main direction */
1543 /* - stem edge, serif edge or both (which defaults to stem then) */
1544 /* - rounded edge, straight or both (which defaults to straight) */
1545 /* - link for edge */
1546 /* */
1547 /*****************************************************************/
1549 /* first of all, set the `edge' field in each segment -- this is */
1550 /* required in order to compute edge links */
1552 /* note that removing this loop and setting the `edge' field of each */
1553 /* segment directly in the code above slows down execution speed for */
1554 /* some reasons on platforms like the Sun */
1556 TA_Edge edges = axis->edges;
1557 TA_Edge edge_limit = edges + axis->num_edges;
1558 TA_Edge edge;
1561 for (edge = edges; edge < edge_limit; edge++)
1563 seg = edge->first;
1564 if (seg)
1567 seg->edge = edge;
1568 seg = seg->edge_next;
1569 } while (seg != edge->first);
1572 /* now compute each edge properties */
1573 for (edge = edges; edge < edge_limit; edge++)
1575 FT_Int is_round = 0; /* does it contain round segments? */
1576 FT_Int is_straight = 0; /* does it contain straight segments? */
1577 #if 0
1578 FT_Pos ups = 0; /* number of upwards segments */
1579 FT_Pos downs = 0; /* number of downwards segments */
1580 #endif
1583 seg = edge->first;
1587 FT_Bool is_serif;
1590 /* check for roundness of segment */
1591 if (seg->flags & TA_EDGE_ROUND)
1592 is_round++;
1593 else
1594 is_straight++;
1596 #if 0
1597 /* check for segment direction */
1598 if (seg->dir == up_dir)
1599 ups += seg->max_coord - seg->min_coord;
1600 else
1601 downs += seg->max_coord - seg->min_coord;
1602 #endif
1604 /* check for links -- */
1605 /* if seg->serif is set, then seg->link must be ignored */
1606 is_serif = (FT_Bool)(seg->serif
1607 && seg->serif->edge
1608 && seg->serif->edge != edge);
1610 if ((seg->link && seg->link->edge != NULL)
1611 || is_serif)
1613 TA_Edge edge2;
1614 TA_Segment seg2;
1617 edge2 = edge->link;
1618 seg2 = seg->link;
1620 if (is_serif)
1622 seg2 = seg->serif;
1623 edge2 = edge->serif;
1626 if (edge2)
1628 FT_Pos edge_delta;
1629 FT_Pos seg_delta;
1632 edge_delta = edge->fpos - edge2->fpos;
1633 if (edge_delta < 0)
1634 edge_delta = -edge_delta;
1636 seg_delta = seg->pos - seg2->pos;
1637 if (seg_delta < 0)
1638 seg_delta = -seg_delta;
1640 if (seg_delta < edge_delta)
1641 edge2 = seg2->edge;
1643 else
1644 edge2 = seg2->edge;
1646 if (is_serif)
1648 edge->serif = edge2;
1649 edge2->flags |= TA_EDGE_SERIF;
1651 else
1652 edge->link = edge2;
1655 seg = seg->edge_next;
1656 } while (seg != edge->first);
1658 /* set the round/straight flags */
1659 edge->flags = TA_EDGE_NORMAL;
1661 if (is_round > 0
1662 && is_round >= is_straight)
1663 edge->flags |= TA_EDGE_ROUND;
1665 #if 0
1666 /* set the edge's main direction */
1667 edge->dir = TA_DIR_NONE;
1669 if (ups > downs)
1670 edge->dir = (FT_Char)up_dir;
1672 else if (ups < downs)
1673 edge->dir = (FT_Char)-up_dir;
1675 else if (ups == downs)
1676 edge->dir = 0; /* both up and down! */
1677 #endif
1679 /* get rid of serifs if link is set */
1680 /* XXX: this gets rid of many unpleasant artefacts! */
1681 /* example: the `c' in cour.pfa at size 13 */
1683 if (edge->serif && edge->link)
1684 edge->serif = 0;
1688 Exit:
1689 return error;
1693 /* detect segments and edges for given dimension */
1695 FT_Error
1696 ta_latin_hints_detect_features(TA_GlyphHints hints,
1697 TA_Dimension dim)
1699 FT_Error error;
1702 error = ta_latin_hints_compute_segments(hints, dim);
1703 if (!error)
1705 ta_latin_hints_link_segments(hints, dim);
1707 error = ta_latin_hints_compute_edges(hints, dim);
1710 return error;
1714 /* compute all edges which lie within blue zones */
1716 void
1717 ta_latin_hints_compute_blue_edges(TA_GlyphHints hints,
1718 TA_LatinMetrics metrics)
1720 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
1722 TA_Edge edge = axis->edges;
1723 TA_Edge edge_limit = edge + axis->num_edges;
1725 TA_LatinAxis latin = &metrics->axis[TA_DIMENSION_VERT];
1726 FT_Fixed scale = latin->scale;
1729 /* compute which blue zones are active, */
1730 /* i.e. have their scaled size < 3/4 pixels */
1732 /* for each horizontal edge search the blue zone which is closest */
1733 for (; edge < edge_limit; edge++)
1735 FT_UInt bb;
1736 TA_Width best_blue = NULL;
1737 FT_Pos best_dist; /* initial threshold */
1739 FT_UInt best_blue_idx = 0;
1740 FT_Bool best_blue_is_shoot = 0;
1743 /* compute the initial threshold as a fraction of the EM size */
1744 /* (the value 40 is heuristic) */
1745 best_dist = FT_MulFix(metrics->units_per_em / 40, scale);
1747 /* assure a minimum distance of 0.5px */
1748 if (best_dist > 64 / 2)
1749 best_dist = 64 / 2;
1751 /* this loop also handles the two extra blue zones */
1752 /* for usWinAscent and usWinDescent */
1753 /* if option `windows-compatibility' is set */
1754 for (bb = 0;
1755 bb < latin->blue_count
1756 + (metrics->root.globals->font->windows_compatibility ? 2 : 0);
1757 bb++)
1759 TA_LatinBlue blue = latin->blues + bb;
1760 FT_Bool is_top_blue, is_major_dir;
1763 /* skip inactive blue zones (i.e., those that are too large) */
1764 if (!(blue->flags & TA_LATIN_BLUE_ACTIVE))
1765 continue;
1767 /* if it is a top zone, check for right edges -- */
1768 /* if it is a bottom zone, check for left edges */
1769 is_top_blue = (FT_Byte)((blue->flags & TA_LATIN_BLUE_TOP) != 0);
1770 is_major_dir = FT_BOOL(edge->dir == axis->major_dir);
1772 /* if it is a top zone, the edge must be against the major */
1773 /* direction; if it is a bottom zone, it must be in the major */
1774 /* direction */
1775 if (is_top_blue ^ is_major_dir)
1777 FT_Pos dist;
1780 /* first of all, compare it to the reference position */
1781 dist = edge->fpos - blue->ref.org;
1782 if (dist < 0)
1783 dist = -dist;
1785 dist = FT_MulFix(dist, scale);
1786 if (dist < best_dist)
1788 best_dist = dist;
1789 best_blue = &blue->ref;
1791 best_blue_idx = bb;
1792 best_blue_is_shoot = 0;
1795 /* now compare it to the overshoot position and check whether */
1796 /* the edge is rounded, and whether the edge is over the */
1797 /* reference position of a top zone, or under the reference */
1798 /* position of a bottom zone */
1799 if (edge->flags & TA_EDGE_ROUND
1800 && dist != 0)
1802 FT_Bool is_under_ref = FT_BOOL(edge->fpos < blue->ref.org);
1805 if (is_top_blue ^ is_under_ref)
1807 dist = edge->fpos - blue->shoot.org;
1808 if (dist < 0)
1809 dist = -dist;
1811 dist = FT_MulFix(dist, scale);
1812 if (dist < best_dist)
1814 best_dist = dist;
1815 best_blue = &blue->shoot;
1817 best_blue_idx = bb;
1818 best_blue_is_shoot = 1;
1825 if (best_blue)
1827 edge->blue_edge = best_blue;
1828 edge->best_blue_idx = best_blue_idx;
1829 edge->best_blue_is_shoot = best_blue_is_shoot;
1835 /* initalize hinting engine */
1837 static FT_Error
1838 ta_latin_hints_init(TA_GlyphHints hints,
1839 TA_LatinMetrics metrics)
1841 FT_Render_Mode mode;
1842 FT_UInt32 scaler_flags, other_flags;
1843 FT_Face face = metrics->root.scaler.face;
1846 ta_glyph_hints_rescale(hints, (TA_ScriptMetrics)metrics);
1848 /* correct x_scale and y_scale if needed, since they may have */
1849 /* been modified by `ta_latin_metrics_scale_dim' above */
1850 hints->x_scale = metrics->axis[TA_DIMENSION_HORZ].scale;
1851 hints->x_delta = metrics->axis[TA_DIMENSION_HORZ].delta;
1852 hints->y_scale = metrics->axis[TA_DIMENSION_VERT].scale;
1853 hints->y_delta = metrics->axis[TA_DIMENSION_VERT].delta;
1855 /* compute flags depending on render mode, etc. */
1856 mode = metrics->root.scaler.render_mode;
1858 #if 0 /* #ifdef TA_CONFIG_OPTION_USE_WARPER */
1859 if (mode == FT_RENDER_MODE_LCD
1860 || mode == FT_RENDER_MODE_LCD_V)
1861 metrics->root.scaler.render_mode =
1862 mode = FT_RENDER_MODE_NORMAL;
1863 #endif
1865 scaler_flags = hints->scaler_flags;
1866 other_flags = 0;
1868 /* we snap the width of vertical stems for the monochrome */
1869 /* and horizontal LCD rendering targets only */
1870 if (mode == FT_RENDER_MODE_MONO
1871 || mode == FT_RENDER_MODE_LCD)
1872 other_flags |= TA_LATIN_HINTS_HORZ_SNAP;
1874 /* we snap the width of horizontal stems for the monochrome */
1875 /* and vertical LCD rendering targets only */
1876 if (mode == FT_RENDER_MODE_MONO
1877 || mode == FT_RENDER_MODE_LCD_V)
1878 other_flags |= TA_LATIN_HINTS_VERT_SNAP;
1880 /* we adjust stems to full pixels only if we don't use the `light' mode */
1881 if (mode != FT_RENDER_MODE_LIGHT)
1882 other_flags |= TA_LATIN_HINTS_STEM_ADJUST;
1884 if (mode == FT_RENDER_MODE_MONO)
1885 other_flags |= TA_LATIN_HINTS_MONO;
1887 /* in `light' hinting mode we disable horizontal hinting completely; */
1888 /* we also do it if the face is italic */
1889 if (mode == FT_RENDER_MODE_LIGHT
1890 || (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0)
1891 scaler_flags |= TA_SCALER_FLAG_NO_HORIZONTAL;
1893 hints->scaler_flags = scaler_flags;
1894 hints->other_flags = other_flags;
1896 return FT_Err_Ok;
1900 /* snap a given width in scaled coordinates to */
1901 /* one of the current standard widths */
1903 static FT_Pos
1904 ta_latin_snap_width(TA_Width widths,
1905 FT_Int count,
1906 FT_Pos width)
1908 int n;
1909 FT_Pos best = 64 + 32 + 2;
1910 FT_Pos reference = width;
1911 FT_Pos scaled;
1914 for (n = 0; n < count; n++)
1916 FT_Pos w;
1917 FT_Pos dist;
1920 w = widths[n].cur;
1921 dist = width - w;
1922 if (dist < 0)
1923 dist = -dist;
1924 if (dist < best)
1926 best = dist;
1927 reference = w;
1931 scaled = TA_PIX_ROUND(reference);
1933 if (width >= reference)
1935 if (width < scaled + 48)
1936 width = reference;
1938 else
1940 if (width > scaled - 48)
1941 width = reference;
1944 return width;
1948 /* compute the snapped width of a given stem, ignoring very thin ones */
1950 /* there is a lot of voodoo in this function; changing the hard-coded */
1951 /* parameters influence the whole hinting process */
1953 static FT_Pos
1954 ta_latin_compute_stem_width(TA_GlyphHints hints,
1955 TA_Dimension dim,
1956 FT_Pos width,
1957 FT_Byte base_flags,
1958 FT_Byte stem_flags)
1960 TA_LatinMetrics metrics = (TA_LatinMetrics) hints->metrics;
1961 TA_LatinAxis axis = &metrics->axis[dim];
1963 FT_Pos dist = width;
1964 FT_Int sign = 0;
1965 FT_Int vertical = (dim == TA_DIMENSION_VERT);
1968 if (!TA_LATIN_HINTS_DO_STEM_ADJUST(hints)
1969 || axis->extra_light)
1970 return width;
1972 if (dist < 0)
1974 dist = -width;
1975 sign = 1;
1978 if ((vertical && !TA_LATIN_HINTS_DO_VERT_SNAP(hints))
1979 || (!vertical && !TA_LATIN_HINTS_DO_HORZ_SNAP(hints)))
1981 /* smooth hinting process: very lightly quantize the stem width */
1983 /* leave the widths of serifs alone */
1984 if ((stem_flags & TA_EDGE_SERIF)
1985 && vertical
1986 && (dist < 3 * 64))
1987 goto Done_Width;
1988 else if (base_flags & TA_EDGE_ROUND)
1990 if (dist < 80)
1991 dist = 64;
1993 else if (dist < 56)
1994 dist = 56;
1996 if (axis->width_count > 0)
1998 FT_Pos delta;
2001 /* compare to standard width */
2002 delta = dist - axis->widths[0].cur;
2004 if (delta < 0)
2005 delta = -delta;
2007 if (delta < 40)
2009 dist = axis->widths[0].cur;
2010 if (dist < 48)
2011 dist = 48;
2013 goto Done_Width;
2016 if (dist < 3 * 64)
2018 delta = dist & 63;
2019 dist &= -64;
2021 if (delta < 10)
2022 dist += delta;
2023 else if (delta < 32)
2024 dist += 10;
2025 else if (delta < 54)
2026 dist += 54;
2027 else
2028 dist += delta;
2030 else
2031 dist = (dist + 32) & ~63;
2034 else
2036 /* strong hinting process: snap the stem width to integer pixels */
2038 FT_Pos org_dist = dist;
2041 dist = ta_latin_snap_width(axis->widths, axis->width_count, dist);
2043 if (vertical)
2045 /* in the case of vertical hinting, */
2046 /* always round the stem heights to integer pixels */
2048 if (dist >= 64)
2049 dist = (dist + 16) & ~63;
2050 else
2051 dist = 64;
2053 else
2055 if (TA_LATIN_HINTS_DO_MONO(hints))
2057 /* monochrome horizontal hinting: */
2058 /* snap widths to integer pixels with a different threshold */
2060 if (dist < 64)
2061 dist = 64;
2062 else
2063 dist = (dist + 32) & ~63;
2065 else
2067 /* for horizontal anti-aliased hinting, we adopt a more subtle */
2068 /* approach: we strengthen small stems, round stems whose size */
2069 /* is between 1 and 2 pixels to an integer, otherwise nothing */
2071 if (dist < 48)
2072 dist = (dist + 64) >> 1;
2074 else if (dist < 128)
2076 /* we only round to an integer width if the corresponding */
2077 /* distortion is less than 1/4 pixel -- otherwise, this */
2078 /* makes everything worse since the diagonals, which are */
2079 /* not hinted, appear a lot bolder or thinner than the */
2080 /* vertical stems */
2082 FT_Pos delta;
2085 dist = (dist + 22) & ~63;
2086 delta = dist - org_dist;
2087 if (delta < 0)
2088 delta = -delta;
2090 if (delta >= 16)
2092 dist = org_dist;
2093 if (dist < 48)
2094 dist = (dist + 64) >> 1;
2097 else
2098 /* round otherwise to prevent color fringes in LCD mode */
2099 dist = (dist + 32) & ~63;
2104 Done_Width:
2105 if (sign)
2106 dist = -dist;
2108 return dist;
2112 /* align one stem edge relative to the previous stem edge */
2114 static void
2115 ta_latin_align_linked_edge(TA_GlyphHints hints,
2116 TA_Dimension dim,
2117 TA_Edge base_edge,
2118 TA_Edge stem_edge)
2120 FT_Pos dist = stem_edge->opos - base_edge->opos;
2122 FT_Pos fitted_width = ta_latin_compute_stem_width(
2123 hints, dim, dist,
2124 base_edge->flags,
2125 stem_edge->flags);
2128 stem_edge->pos = base_edge->pos + fitted_width;
2130 TA_LOG((" LINK: edge %d (opos=%.2f) linked to %.2f,"
2131 " dist was %.2f, now %.2f\n",
2132 stem_edge - hints->axis[dim].edges, stem_edge->opos / 64.0,
2133 stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0));
2135 if (hints->recorder)
2136 hints->recorder(ta_link, hints, dim,
2137 base_edge, stem_edge, NULL, NULL, NULL);
2141 /* shift the coordinates of the `serif' edge by the same amount */
2142 /* as the corresponding `base' edge has been moved already */
2144 static void
2145 ta_latin_align_serif_edge(TA_GlyphHints hints,
2146 TA_Edge base,
2147 TA_Edge serif)
2149 FT_UNUSED(hints);
2151 serif->pos = base->pos + (serif->opos - base->opos);
2155 /* the main grid-fitting routine */
2157 void
2158 ta_latin_hint_edges(TA_GlyphHints hints,
2159 TA_Dimension dim)
2161 TA_AxisHints axis = &hints->axis[dim];
2163 TA_Edge edges = axis->edges;
2164 TA_Edge edge_limit = edges + axis->num_edges;
2165 FT_PtrDist n_edges;
2166 TA_Edge edge;
2168 TA_Edge anchor = NULL;
2169 FT_Int has_serifs = 0;
2171 #ifdef TA_DEBUG
2172 FT_UInt num_actions = 0;
2173 #endif
2175 TA_LOG(("latin %s edge hinting (script `%s')\n",
2176 dim == TA_DIMENSION_VERT ? "horizontal" : "vertical",
2177 ta_script_names[hints->metrics->script_class->script]));
2179 /* we begin by aligning all stems relative to the blue zone if needed -- */
2180 /* that's only for horizontal edges */
2182 if (dim == TA_DIMENSION_VERT
2183 && TA_HINTS_DO_BLUES(hints))
2185 for (edge = edges; edge < edge_limit; edge++)
2187 TA_Width blue;
2188 TA_Edge edge1, edge2; /* these edges form the stem to check */
2191 if (edge->flags & TA_EDGE_DONE)
2192 continue;
2194 blue = edge->blue_edge;
2195 edge1 = NULL;
2196 edge2 = edge->link;
2198 if (blue)
2199 edge1 = edge;
2201 /* flip edges if the other stem is aligned to a blue zone */
2202 else if (edge2 && edge2->blue_edge)
2204 blue = edge2->blue_edge;
2205 edge1 = edge2;
2206 edge2 = edge;
2209 if (!edge1)
2210 continue;
2212 #ifdef TA_DEBUG
2213 if (!anchor)
2214 TA_LOG((" BLUE_ANCHOR: edge %d (opos=%.2f) snapped to %.2f,"
2215 " was %.2f (anchor=edge %d)\n",
2216 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
2217 edge1->pos / 64.0, edge - edges));
2218 else
2219 TA_LOG((" BLUE: edge %d (opos=%.2f) snapped to %.2f, was %.2f\n",
2220 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
2221 edge1->pos / 64.0));
2223 num_actions++;
2224 #endif
2226 edge1->pos = blue->fit;
2227 edge1->flags |= TA_EDGE_DONE;
2229 if (hints->recorder)
2231 if (!anchor)
2232 hints->recorder(ta_blue_anchor, hints, dim,
2233 edge1, edge, NULL, NULL, NULL);
2234 else
2235 hints->recorder(ta_blue, hints, dim,
2236 edge1, NULL, NULL, NULL, NULL);
2239 if (edge2 && !edge2->blue_edge)
2241 ta_latin_align_linked_edge(hints, dim, edge1, edge2);
2242 edge2->flags |= TA_EDGE_DONE;
2244 #ifdef TA_DEBUG
2245 num_actions++;
2246 #endif
2249 if (!anchor)
2250 anchor = edge;
2254 /* now we align all other stem edges, */
2255 /* trying to maintain the relative order of stems in the glyph */
2256 for (edge = edges; edge < edge_limit; edge++)
2258 TA_Edge edge2;
2261 if (edge->flags & TA_EDGE_DONE)
2262 continue;
2264 /* skip all non-stem edges */
2265 edge2 = edge->link;
2266 if (!edge2)
2268 has_serifs++;
2269 continue;
2272 /* now align the stem */
2274 /* this should not happen, but it's better to be safe */
2275 if (edge2->blue_edge)
2277 TA_LOG((" ASSERTION FAILED for edge %d\n", edge2-edges));
2279 ta_latin_align_linked_edge(hints, dim, edge2, edge);
2280 edge->flags |= TA_EDGE_DONE;
2282 #ifdef TA_DEBUG
2283 num_actions++;
2284 #endif
2285 continue;
2288 if (!anchor)
2290 /* if we reach this if clause, no stem has been aligned yet */
2292 FT_Pos org_len, org_center, cur_len;
2293 FT_Pos cur_pos1, error1, error2, u_off, d_off;
2296 org_len = edge2->opos - edge->opos;
2297 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2298 edge->flags, edge2->flags);
2300 /* some voodoo to specially round edges for small stem widths; */
2301 /* the idea is to align the center of a stem, */
2302 /* then shifting the stem edges to suitable positions */
2303 if (cur_len <= 64)
2305 /* width <= 1px */
2306 u_off = 32;
2307 d_off = 32;
2309 else
2311 /* 1px < width < 1.5px */
2312 u_off = 38;
2313 d_off = 26;
2316 if (cur_len < 96)
2318 org_center = edge->opos + (org_len >> 1);
2319 cur_pos1 = TA_PIX_ROUND(org_center);
2321 error1 = org_center - (cur_pos1 - u_off);
2322 if (error1 < 0)
2323 error1 = -error1;
2325 error2 = org_center - (cur_pos1 + d_off);
2326 if (error2 < 0)
2327 error2 = -error2;
2329 if (error1 < error2)
2330 cur_pos1 -= u_off;
2331 else
2332 cur_pos1 += d_off;
2334 edge->pos = cur_pos1 - cur_len / 2;
2335 edge2->pos = edge->pos + cur_len;
2337 else
2338 edge->pos = TA_PIX_ROUND(edge->opos);
2340 anchor = edge;
2341 edge->flags |= TA_EDGE_DONE;
2343 TA_LOG((" ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
2344 " snapped to %.2f and %.2f\n",
2345 edge - edges, edge->opos / 64.0,
2346 edge2 - edges, edge2->opos / 64.0,
2347 edge->pos / 64.0, edge2->pos / 64.0));
2349 if (hints->recorder)
2350 hints->recorder(ta_anchor, hints, dim,
2351 edge, edge2, NULL, NULL, NULL);
2353 ta_latin_align_linked_edge(hints, dim, edge, edge2);
2355 #ifdef TA_DEBUG
2356 num_actions += 2;
2357 #endif
2359 else
2361 FT_Pos org_pos, org_len, org_center, cur_len;
2362 FT_Pos cur_pos1, cur_pos2, delta1, delta2;
2365 org_pos = anchor->pos + (edge->opos - anchor->opos);
2366 org_len = edge2->opos - edge->opos;
2367 org_center = org_pos + (org_len >> 1);
2369 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2370 edge->flags, edge2->flags);
2372 if (edge2->flags & TA_EDGE_DONE)
2374 TA_LOG((" ADJUST: edge %d (pos=%.2f) moved to %.2f\n",
2375 edge - edges, edge->pos / 64.0,
2376 (edge2->pos - cur_len) / 64.0));
2378 edge->pos = edge2->pos - cur_len;
2380 if (hints->recorder)
2382 TA_Edge bound = NULL;
2385 if (edge > edges)
2386 bound = &edge[-1];
2388 hints->recorder(ta_adjust, hints, dim,
2389 edge, edge2, NULL, bound, NULL);
2393 else if (cur_len < 96)
2395 FT_Pos u_off, d_off;
2398 cur_pos1 = TA_PIX_ROUND(org_center);
2400 if (cur_len <= 64)
2402 u_off = 32;
2403 d_off = 32;
2405 else
2407 u_off = 38;
2408 d_off = 26;
2411 delta1 = org_center - (cur_pos1 - u_off);
2412 if (delta1 < 0)
2413 delta1 = -delta1;
2415 delta2 = org_center - (cur_pos1 + d_off);
2416 if (delta2 < 0)
2417 delta2 = -delta2;
2419 if (delta1 < delta2)
2420 cur_pos1 -= u_off;
2421 else
2422 cur_pos1 += d_off;
2424 edge->pos = cur_pos1 - cur_len / 2;
2425 edge2->pos = cur_pos1 + cur_len / 2;
2427 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2428 " snapped to %.2f and %.2f\n",
2429 edge - edges, edge->opos / 64.0,
2430 edge2 - edges, edge2->opos / 64.0,
2431 edge->pos / 64.0, edge2->pos / 64.0));
2433 if (hints->recorder)
2435 TA_Edge bound = NULL;
2438 if (edge > edges)
2439 bound = &edge[-1];
2441 hints->recorder(ta_stem, hints, dim,
2442 edge, edge2, NULL, bound, NULL);
2446 else
2448 org_pos = anchor->pos + (edge->opos - anchor->opos);
2449 org_len = edge2->opos - edge->opos;
2450 org_center = org_pos + (org_len >> 1);
2452 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2453 edge->flags, edge2->flags);
2455 cur_pos1 = TA_PIX_ROUND(org_pos);
2456 delta1 = cur_pos1 + (cur_len >> 1) - org_center;
2457 if (delta1 < 0)
2458 delta1 = -delta1;
2460 cur_pos2 = TA_PIX_ROUND(org_pos + org_len) - cur_len;
2461 delta2 = cur_pos2 + (cur_len >> 1) - org_center;
2462 if (delta2 < 0)
2463 delta2 = -delta2;
2465 edge->pos = (delta1 < delta2) ? cur_pos1 : cur_pos2;
2466 edge2->pos = edge->pos + cur_len;
2468 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2469 " snapped to %.2f and %.2f\n",
2470 edge - edges, edge->opos / 64.0,
2471 edge2 - edges, edge2->opos / 64.0,
2472 edge->pos / 64.0, edge2->pos / 64.0));
2474 if (hints->recorder)
2476 TA_Edge bound = NULL;
2479 if (edge > edges)
2480 bound = &edge[-1];
2482 hints->recorder(ta_stem, hints, dim,
2483 edge, edge2, NULL, bound, NULL);
2487 #ifdef TA_DEBUG
2488 num_actions++;
2489 #endif
2491 edge->flags |= TA_EDGE_DONE;
2492 edge2->flags |= TA_EDGE_DONE;
2494 if (edge > edges
2495 && edge->pos < edge[-1].pos)
2497 #ifdef TA_DEBUG
2498 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2499 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
2501 num_actions++;
2502 #endif
2504 edge->pos = edge[-1].pos;
2506 if (hints->recorder)
2507 hints->recorder(ta_bound, hints, dim,
2508 edge, &edge[-1], NULL, NULL, NULL);
2513 /* make sure that lowercase m's maintain their symmetry */
2515 /* In general, lowercase m's have six vertical edges if they are sans */
2516 /* serif, or twelve if they are with serifs. This implementation is */
2517 /* based on that assumption, and seems to work very well with most */
2518 /* faces. However, if for a certain face this assumption is not */
2519 /* true, the m is just rendered like before. In addition, any stem */
2520 /* correction will only be applied to symmetrical glyphs (even if the */
2521 /* glyph is not an m), so the potential for unwanted distortion is */
2522 /* relatively low. */
2524 /* we don't handle horizontal edges since we can't easily assure that */
2525 /* the third (lowest) stem aligns with the base line; it might end up */
2526 /* one pixel higher or lower */
2528 n_edges = edge_limit - edges;
2529 if (dim == TA_DIMENSION_HORZ
2530 && (n_edges == 6 || n_edges == 12))
2532 TA_Edge edge1, edge2, edge3;
2533 FT_Pos dist1, dist2, span, delta;
2536 if (n_edges == 6)
2538 edge1 = edges;
2539 edge2 = edges + 2;
2540 edge3 = edges + 4;
2542 else
2544 edge1 = edges + 1;
2545 edge2 = edges + 5;
2546 edge3 = edges + 9;
2549 dist1 = edge2->opos - edge1->opos;
2550 dist2 = edge3->opos - edge2->opos;
2552 span = dist1 - dist2;
2553 if (span < 0)
2554 span = -span;
2556 if (span < 8)
2558 delta = edge3->pos - (2 * edge2->pos - edge1->pos);
2559 edge3->pos -= delta;
2560 if (edge3->link)
2561 edge3->link->pos -= delta;
2563 /* move the serifs along with the stem */
2564 if (n_edges == 12)
2566 (edges + 8)->pos -= delta;
2567 (edges + 11)->pos -= delta;
2570 edge3->flags |= TA_EDGE_DONE;
2571 if (edge3->link)
2572 edge3->link->flags |= TA_EDGE_DONE;
2576 if (has_serifs || !anchor)
2578 /* now hint the remaining edges (serifs and single) */
2579 /* in order to complete our processing */
2580 for (edge = edges; edge < edge_limit; edge++)
2582 TA_Edge lower_bound = NULL;
2583 TA_Edge upper_bound = NULL;
2585 FT_Pos delta;
2588 if (edge->flags & TA_EDGE_DONE)
2589 continue;
2591 delta = 1000;
2593 if (edge->serif)
2595 delta = edge->serif->opos - edge->opos;
2596 if (delta < 0)
2597 delta = -delta;
2600 if (edge > edges)
2601 lower_bound = &edge[-1];
2603 if (edge + 1 < edge_limit
2604 && edge[1].flags & TA_EDGE_DONE)
2605 upper_bound = &edge[1];
2608 if (delta < 64 + 16)
2610 ta_latin_align_serif_edge(hints, edge->serif, edge);
2612 TA_LOG((" SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
2613 " aligned to %.2f\n",
2614 edge - edges, edge->opos / 64.0,
2615 edge->serif - edges, edge->serif->opos / 64.0,
2616 edge->pos / 64.0));
2618 if (hints->recorder)
2619 hints->recorder(ta_serif, hints, dim,
2620 edge, NULL, NULL, lower_bound, upper_bound);
2622 else if (!anchor)
2624 edge->pos = TA_PIX_ROUND(edge->opos);
2625 anchor = edge;
2627 TA_LOG((" SERIF_ANCHOR: edge %d (opos=%.2f) snapped to %.2f\n",
2628 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
2630 if (hints->recorder)
2631 hints->recorder(ta_serif_anchor, hints, dim,
2632 edge, NULL, NULL, lower_bound, upper_bound);
2634 else
2636 TA_Edge before, after;
2639 for (before = edge - 1; before >= edges; before--)
2640 if (before->flags & TA_EDGE_DONE)
2641 break;
2643 for (after = edge + 1; after < edge_limit; after++)
2644 if (after->flags & TA_EDGE_DONE)
2645 break;
2647 if (before >= edges && before < edge
2648 && after < edge_limit && after > edge)
2650 if (after->opos == before->opos)
2651 edge->pos = before->pos;
2652 else
2653 edge->pos = before->pos + FT_MulDiv(edge->opos - before->opos,
2654 after->pos - before->pos,
2655 after->opos - before->opos);
2657 TA_LOG((" SERIF_LINK1: edge %d (opos=%.2f) snapped to %.2f"
2658 " from %d (opos=%.2f)\n",
2659 edge - edges, edge->opos / 64.0,
2660 edge->pos / 64.0,
2661 before - edges, before->opos / 64.0));
2663 if (hints->recorder)
2664 hints->recorder(ta_serif_link1, hints, dim,
2665 edge, before, after, lower_bound, upper_bound);
2667 else
2669 edge->pos = anchor->pos + ((edge->opos - anchor->opos + 16) & ~31);
2670 TA_LOG((" SERIF_LINK2: edge %d (opos=%.2f) snapped to %.2f\n",
2671 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
2673 if (hints->recorder)
2674 hints->recorder(ta_serif_link2, hints, dim,
2675 edge, NULL, NULL, lower_bound, upper_bound);
2679 #ifdef TA_DEBUG
2680 num_actions++;
2681 #endif
2682 edge->flags |= TA_EDGE_DONE;
2684 if (edge > edges
2685 && edge->pos < edge[-1].pos)
2687 #ifdef TA_DEBUG
2688 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2689 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
2690 num_actions++;
2691 #endif
2693 edge->pos = edge[-1].pos;
2695 if (hints->recorder)
2696 hints->recorder(ta_bound, hints, dim,
2697 edge, &edge[-1], NULL, NULL, NULL);
2700 if (edge + 1 < edge_limit
2701 && edge[1].flags & TA_EDGE_DONE
2702 && edge->pos > edge[1].pos)
2704 #ifdef TA_DEBUG
2705 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2706 edge - edges, edge->pos / 64.0, edge[1].pos / 64.0));
2708 num_actions++;
2709 #endif
2711 edge->pos = edge[1].pos;
2713 if (hints->recorder)
2714 hints->recorder(ta_bound, hints, dim,
2715 edge, &edge[1], NULL, NULL, NULL);
2720 #ifdef TA_DEBUG
2721 if (!num_actions)
2722 TA_LOG((" (none)\n"));
2723 TA_LOG(("\n"));
2724 #endif
2728 /* apply the complete hinting algorithm to a latin glyph */
2730 static FT_Error
2731 ta_latin_hints_apply(TA_GlyphHints hints,
2732 FT_Outline* outline,
2733 TA_LatinMetrics metrics)
2735 FT_Error error;
2736 int dim;
2739 error = ta_glyph_hints_reload(hints, outline);
2740 if (error)
2741 goto Exit;
2743 /* analyze glyph outline */
2744 #ifdef TA_CONFIG_OPTION_USE_WARPER
2745 if (metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT
2746 || TA_HINTS_DO_HORIZONTAL(hints))
2747 #else
2748 if (TA_HINTS_DO_HORIZONTAL(hints))
2749 #endif
2751 error = ta_latin_hints_detect_features(hints, TA_DIMENSION_HORZ);
2752 if (error)
2753 goto Exit;
2756 if (TA_HINTS_DO_VERTICAL(hints))
2758 error = ta_latin_hints_detect_features(hints, TA_DIMENSION_VERT);
2759 if (error)
2760 goto Exit;
2762 ta_latin_hints_compute_blue_edges(hints, metrics);
2765 /* grid-fit the outline */
2766 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
2768 #ifdef TA_CONFIG_OPTION_USE_WARPER
2769 if (dim == TA_DIMENSION_HORZ
2770 && metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT)
2772 TA_WarperRec warper;
2773 FT_Fixed scale;
2774 FT_Pos delta;
2777 ta_warper_compute(&warper, hints, (TA_Dimension)dim, &scale, &delta);
2778 ta_glyph_hints_scale_dim(hints, (TA_Dimension)dim, scale, delta);
2780 continue;
2782 #endif
2784 if ((dim == TA_DIMENSION_HORZ && TA_HINTS_DO_HORIZONTAL(hints))
2785 || (dim == TA_DIMENSION_VERT && TA_HINTS_DO_VERTICAL(hints)))
2787 ta_latin_hint_edges(hints, (TA_Dimension)dim);
2788 ta_glyph_hints_align_edge_points(hints, (TA_Dimension)dim);
2789 ta_glyph_hints_align_strong_points(hints, (TA_Dimension)dim);
2790 ta_glyph_hints_align_weak_points(hints, (TA_Dimension)dim);
2794 ta_glyph_hints_save(hints, outline);
2796 Exit:
2797 return error;
2801 const TA_WritingSystemClassRec ta_latin_writing_system_class =
2803 TA_WRITING_SYSTEM_LATIN,
2805 sizeof (TA_LatinMetricsRec),
2807 (TA_Script_InitMetricsFunc)ta_latin_metrics_init,
2808 (TA_Script_ScaleMetricsFunc)ta_latin_metrics_scale,
2809 (TA_Script_DoneMetricsFunc)NULL,
2811 (TA_Script_InitHintsFunc)ta_latin_hints_init,
2812 (TA_Script_ApplyHintsFunc)ta_latin_hints_apply
2816 /* XXX: this should probably fine tuned to differentiate better between */
2817 /* scripts... */
2819 static const TA_Script_UniRangeRec ta_latn_uniranges[] =
2821 TA_UNIRANGE_REC(0x0020UL, 0x007FUL), /* Basic Latin (no control chars) */
2822 TA_UNIRANGE_REC(0x00A0UL, 0x00FFUL), /* Latin-1 Supplement (no control chars) */
2823 TA_UNIRANGE_REC(0x0100UL, 0x017FUL), /* Latin Extended-A */
2824 TA_UNIRANGE_REC(0x0180UL, 0x024FUL), /* Latin Extended-B */
2825 TA_UNIRANGE_REC(0x0250UL, 0x02AFUL), /* IPA Extensions */
2826 TA_UNIRANGE_REC(0x02B0UL, 0x02FFUL), /* Spacing Modifier Letters */
2827 TA_UNIRANGE_REC(0x0300UL, 0x036FUL), /* Combining Diacritical Marks */
2828 TA_UNIRANGE_REC(0x1D00UL, 0x1D7FUL), /* Phonetic Extensions */
2829 TA_UNIRANGE_REC(0x1D80UL, 0x1DBFUL), /* Phonetic Extensions Supplement */
2830 TA_UNIRANGE_REC(0x1DC0UL, 0x1DFFUL), /* Combining Diacritical Marks Supplement */
2831 TA_UNIRANGE_REC(0x1E00UL, 0x1EFFUL), /* Latin Extended Additional */
2832 TA_UNIRANGE_REC(0x2000UL, 0x206FUL), /* General Punctuation */
2833 TA_UNIRANGE_REC(0x2070UL, 0x209FUL), /* Superscripts and Subscripts */
2834 TA_UNIRANGE_REC(0x20A0UL, 0x20CFUL), /* Currency Symbols */
2835 TA_UNIRANGE_REC(0x2150UL, 0x218FUL), /* Number Forms */
2836 TA_UNIRANGE_REC(0x2460UL, 0x24FFUL), /* Enclosed Alphanumerics */
2837 TA_UNIRANGE_REC(0x2C60UL, 0x2C7FUL), /* Latin Extended-C */
2838 TA_UNIRANGE_REC(0x2E00UL, 0x2E7FUL), /* Supplemental Punctuation */
2839 TA_UNIRANGE_REC(0xA720UL, 0xA7FFUL), /* Latin Extended-D */
2840 TA_UNIRANGE_REC(0xFB00UL, 0xFB06UL), /* Alphab. Present. Forms (Latin Ligs) */
2841 TA_UNIRANGE_REC(0x1D400UL, 0x1D7FFUL), /* Mathematical Alphanumeric Symbols */
2842 TA_UNIRANGE_REC(0x1F100UL, 0x1F1FFUL), /* Enclosed Alphanumeric Supplement */
2843 TA_UNIRANGE_REC(0UL, 0UL)
2846 static const TA_Script_UniRangeRec ta_grek_uniranges[] =
2848 TA_UNIRANGE_REC(0x0370UL, 0x03FFUL), /* Greek and Coptic */
2849 TA_UNIRANGE_REC(0x1F00UL, 0x1FFFUL), /* Greek Extended */
2850 TA_UNIRANGE_REC(0UL, 0UL )
2853 static const TA_Script_UniRangeRec ta_cyrl_uniranges[] =
2855 TA_UNIRANGE_REC(0x0400UL, 0x04FFUL), /* Cyrillic */
2856 TA_UNIRANGE_REC(0x0500UL, 0x052FUL), /* Cyrillic Supplement */
2857 TA_UNIRANGE_REC(0x2DE0UL, 0x2DFFUL), /* Cyrillic Extended-A */
2858 TA_UNIRANGE_REC(0xA640UL, 0xA69FUL), /* Cyrillic Extended-B */
2859 TA_UNIRANGE_REC(0UL, 0UL )
2862 static const TA_Script_UniRangeRec ta_hebr_uniranges[] =
2864 TA_UNIRANGE_REC(0x0590UL, 0x05FFUL), /* Hebrew */
2865 TA_UNIRANGE_REC(0xFB1DUL, 0xFB4FUL), /* Alphab. Present. Forms (Hebrew) */
2866 TA_UNIRANGE_REC(0UL, 0UL )
2870 const TA_ScriptClassRec ta_latn_script_class =
2872 TA_SCRIPT_LATN,
2873 TA_BLUE_STRINGSET_LATN,
2874 TA_WRITING_SYSTEM_LATIN,
2876 ta_latn_uniranges,
2880 const TA_ScriptClassRec ta_grek_script_class =
2882 TA_SCRIPT_GREK,
2883 TA_BLUE_STRINGSET_GREK,
2884 TA_WRITING_SYSTEM_LATIN,
2886 ta_grek_uniranges,
2887 0x3BF /* ο */
2890 const TA_ScriptClassRec ta_cyrl_script_class =
2892 TA_SCRIPT_CYRL,
2893 TA_BLUE_STRINGSET_CYRL,
2894 TA_WRITING_SYSTEM_LATIN,
2896 ta_cyrl_uniranges,
2897 0x43E /* о */
2900 const TA_ScriptClassRec ta_hebr_script_class =
2902 TA_SCRIPT_HEBR,
2903 TA_BLUE_STRINGSET_HEBR,
2904 TA_WRITING_SYSTEM_LATIN,
2906 ta_hebr_uniranges,
2907 0x5DD /* ם */
2910 /* end of talatin.c */