Update file `no-copyright' and run `update-copyright'.
[ttfautohint.git] / lib / talatin.c
blob1e54afdfce256009632018175a211de8af75c2a5
1 /* talatin.c */
3 /*
4 * Copyright (C) 2011-2014 by Werner Lemberg.
6 * This file is part of the ttfautohint library, and may only be used,
7 * modified, and distributed under the terms given in `COPYING'. By
8 * continuing to use, modify, or distribute this file you indicate that you
9 * have read `COPYING' and understand and accept it fully.
11 * The file `COPYING' mentioned in the previous paragraph is distributed
12 * with the ttfautohint library.
16 /* originally file `aflatin.c' (2011-Mar-28) from FreeType */
18 /* heavily modified 2011 by Werner Lemberg <wl@gnu.org> */
20 #include <string.h>
22 #include <ft2build.h>
23 #include FT_ADVANCES_H
24 #include FT_TRUETYPE_TABLES_H
26 #include "taglobal.h"
27 #include "talatin.h"
28 #include "tasort.h"
31 #ifdef TA_CONFIG_OPTION_USE_WARPER
32 #include "tawarp.h"
33 #endif
35 #include <numberset.h>
38 /* find segments and links, compute all stem widths, and initialize */
39 /* standard width and height for the glyph with given charcode */
41 void
42 ta_latin_metrics_init_widths(TA_LatinMetrics metrics,
43 FT_Face face)
45 /* scan the array of segments in each direction */
46 TA_GlyphHintsRec hints[1];
49 TA_LOG_GLOBAL(("\n"
50 "latin standard widths computation (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 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f (artificial)\n"
1062 " overshoot %d: %d scaled to %.2f (artificial)\n",
1063 a->blue_count,
1064 b->ref.org,
1065 b->ref.fit / 64.0,
1066 a->blue_count,
1067 b->shoot.org,
1068 b->shoot.fit / 64.0));
1070 b = &a->blues[a->blue_count + 1];
1071 b->ref.cur =
1072 b->ref.fit =
1073 b->shoot.cur =
1074 b->shoot.fit = FT_MulFix(b->ref.org, a->org_scale) + delta;
1076 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f (artificial)\n"
1077 " overshoot %d: %d scaled to %.2f (artificial)\n",
1078 a->blue_count + 1,
1079 b->ref.org,
1080 b->ref.fit / 64.0,
1081 a->blue_count + 1,
1082 b->shoot.org,
1083 b->shoot.fit / 64.0));
1086 TA_LOG_GLOBAL(("\n"));
1091 /* scale global values in both directions */
1093 void
1094 ta_latin_metrics_scale(TA_LatinMetrics metrics,
1095 TA_Scaler scaler)
1097 metrics->root.scaler.render_mode = scaler->render_mode;
1098 metrics->root.scaler.face = scaler->face;
1099 metrics->root.scaler.flags = scaler->flags;
1101 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_HORZ);
1102 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_VERT);
1106 /* walk over all contours and compute its segments */
1108 FT_Error
1109 ta_latin_hints_compute_segments(TA_GlyphHints hints,
1110 TA_Dimension dim)
1112 TA_AxisHints axis = &hints->axis[dim];
1113 FT_Error error = FT_Err_Ok;
1115 TA_Segment segment = NULL;
1116 TA_SegmentRec seg0;
1118 TA_Point* contour = hints->contours;
1119 TA_Point* contour_limit = contour + hints->num_contours;
1120 TA_Direction major_dir, segment_dir;
1123 memset(&seg0, 0, sizeof (TA_SegmentRec));
1124 seg0.score = 32000;
1125 seg0.flags = TA_EDGE_NORMAL;
1127 major_dir = (TA_Direction)TA_ABS(axis->major_dir);
1128 segment_dir = major_dir;
1130 axis->num_segments = 0;
1132 /* set up (u,v) in each point */
1133 if (dim == TA_DIMENSION_HORZ)
1135 TA_Point point = hints->points;
1136 TA_Point limit = point + hints->num_points;
1139 for (; point < limit; point++)
1141 point->u = point->fx;
1142 point->v = point->fy;
1145 else
1147 TA_Point point = hints->points;
1148 TA_Point limit = point + hints->num_points;
1151 for (; point < limit; point++)
1153 point->u = point->fy;
1154 point->v = point->fx;
1158 /* do each contour separately */
1159 for (; contour < contour_limit; contour++)
1161 TA_Point point = contour[0];
1162 TA_Point last = point->prev;
1164 int on_edge = 0;
1166 FT_Pos min_pos = 32000; /* minimum segment pos != min_coord */
1167 FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */
1168 FT_Bool passed;
1171 if (point == last) /* skip singletons -- just in case */
1172 continue;
1174 if (TA_ABS(last->out_dir) == major_dir
1175 && TA_ABS(point->out_dir) == major_dir)
1177 /* we are already on an edge, try to locate its start */
1178 last = point;
1180 for (;;)
1182 point = point->prev;
1183 if (TA_ABS(point->out_dir) != major_dir)
1185 point = point->next;
1186 break;
1188 if (point == last)
1189 break;
1193 last = point;
1194 passed = 0;
1196 for (;;)
1198 FT_Pos u, v;
1201 if (on_edge)
1203 u = point->u;
1204 if (u < min_pos)
1205 min_pos = u;
1206 if (u > max_pos)
1207 max_pos = u;
1209 if (point->out_dir != segment_dir
1210 || point == last)
1212 /* we are just leaving an edge; record a new segment! */
1213 segment->last = point;
1214 segment->pos = (FT_Short)((min_pos + max_pos) >> 1);
1216 /* a segment is round if either its first or last point */
1217 /* is a control point */
1218 if ((segment->first->flags | point->flags) & TA_FLAG_CONTROL)
1219 segment->flags |= TA_EDGE_ROUND;
1221 /* compute segment size */
1222 min_pos = max_pos = point->v;
1224 v = segment->first->v;
1225 if (v < min_pos)
1226 min_pos = v;
1227 if (v > max_pos)
1228 max_pos = v;
1230 segment->min_coord = (FT_Short)min_pos;
1231 segment->max_coord = (FT_Short)max_pos;
1232 segment->height = (FT_Short)(segment->max_coord -
1233 segment->min_coord);
1235 on_edge = 0;
1236 segment = NULL;
1237 /* fall through */
1241 /* now exit if we are at the start/end point */
1242 if (point == last)
1244 if (passed)
1245 break;
1246 passed = 1;
1249 if (!on_edge
1250 && TA_ABS(point->out_dir) == major_dir)
1252 /* this is the start of a new segment! */
1253 segment_dir = (TA_Direction)point->out_dir;
1255 /* clear all segment fields */
1256 error = ta_axis_hints_new_segment(axis, &segment);
1257 if (error)
1258 goto Exit;
1260 segment[0] = seg0;
1261 segment->dir = (FT_Char)segment_dir;
1262 min_pos = max_pos = point->u;
1263 segment->first = point;
1264 segment->last = point;
1265 on_edge = 1;
1268 point = point->next;
1270 } /* contours */
1273 /* now slightly increase the height of segments if this makes sense -- */
1274 /* this is used to better detect and ignore serifs */
1276 TA_Segment segments = axis->segments;
1277 TA_Segment segments_end = segments + axis->num_segments;
1280 for (segment = segments; segment < segments_end; segment++)
1282 TA_Point first = segment->first;
1283 TA_Point last = segment->last;
1285 FT_Pos first_v = first->v;
1286 FT_Pos last_v = last->v;
1289 if (first == last)
1290 continue;
1292 if (first_v < last_v)
1294 TA_Point p;
1297 p = first->prev;
1298 if (p->v < first_v)
1299 segment->height = (FT_Short)(segment->height +
1300 ((first_v - p->v) >> 1));
1302 p = last->next;
1303 if (p->v > last_v)
1304 segment->height = (FT_Short)(segment->height +
1305 ((p->v - last_v) >> 1));
1307 else
1309 TA_Point p;
1312 p = first->prev;
1313 if (p->v > first_v)
1314 segment->height = (FT_Short)(segment->height +
1315 ((p->v - first_v) >> 1));
1317 p = last->next;
1318 if (p->v < last_v)
1319 segment->height = (FT_Short)(segment->height +
1320 ((last_v - p->v) >> 1));
1325 Exit:
1326 return error;
1330 /* link segments to form stems and serifs */
1332 void
1333 ta_latin_hints_link_segments(TA_GlyphHints hints,
1334 TA_Dimension dim)
1336 TA_AxisHints axis = &hints->axis[dim];
1338 TA_Segment segments = axis->segments;
1339 TA_Segment segment_limit = segments + axis->num_segments;
1341 FT_Pos len_threshold, len_score;
1342 TA_Segment seg1, seg2;
1345 len_threshold = TA_LATIN_CONSTANT(hints->metrics, 8);
1346 if (len_threshold == 0)
1347 len_threshold = 1;
1349 len_score = TA_LATIN_CONSTANT(hints->metrics, 6000);
1351 /* now compare each segment to the others */
1352 for (seg1 = segments; seg1 < segment_limit; seg1++)
1354 /* the fake segments are introduced to hint the metrics -- */
1355 /* we must never link them to anything */
1356 if (seg1->dir != axis->major_dir
1357 || seg1->first == seg1->last)
1358 continue;
1360 /* search for stems having opposite directions, */
1361 /* with seg1 to the `left' of seg2 */
1362 for (seg2 = segments; seg2 < segment_limit; seg2++)
1364 FT_Pos pos1 = seg1->pos;
1365 FT_Pos pos2 = seg2->pos;
1368 if (seg1->dir + seg2->dir == 0
1369 && pos2 > pos1)
1371 /* compute distance between the two segments */
1372 FT_Pos dist = pos2 - pos1;
1373 FT_Pos min = seg1->min_coord;
1374 FT_Pos max = seg1->max_coord;
1375 FT_Pos len, score;
1378 if (min < seg2->min_coord)
1379 min = seg2->min_coord;
1380 if (max > seg2->max_coord)
1381 max = seg2->max_coord;
1383 /* compute maximum coordinate difference of the two segments */
1384 len = max - min;
1385 if (len >= len_threshold)
1387 /* small coordinate differences cause a higher score, and */
1388 /* segments with a greater distance cause a higher score also */
1389 score = dist + len_score / len;
1391 /* and we search for the smallest score */
1392 /* of the sum of the two values */
1393 if (score < seg1->score)
1395 seg1->score = score;
1396 seg1->link = seg2;
1399 if (score < seg2->score)
1401 seg2->score = score;
1402 seg2->link = seg1;
1409 /* now compute the `serif' segments, cf. explanations in `tahints.h' */
1410 for (seg1 = segments; seg1 < segment_limit; seg1++)
1412 seg2 = seg1->link;
1414 if (seg2)
1416 if (seg2->link != seg1)
1418 seg1->link = 0;
1419 seg1->serif = seg2->link;
1426 /* link segments to edges, using feature analysis for selection */
1428 FT_Error
1429 ta_latin_hints_compute_edges(TA_GlyphHints hints,
1430 TA_Dimension dim)
1432 TA_AxisHints axis = &hints->axis[dim];
1433 FT_Error error = FT_Err_Ok;
1434 TA_LatinAxis laxis = &((TA_LatinMetrics)hints->metrics)->axis[dim];
1436 TA_Segment segments = axis->segments;
1437 TA_Segment segment_limit = segments + axis->num_segments;
1438 TA_Segment seg;
1440 #if 0
1441 TA_Direction up_dir;
1442 #endif
1443 FT_Fixed scale;
1444 FT_Pos edge_distance_threshold;
1445 FT_Pos segment_length_threshold;
1448 axis->num_edges = 0;
1450 scale = (dim == TA_DIMENSION_HORZ) ? hints->x_scale
1451 : hints->y_scale;
1453 #if 0
1454 up_dir = (dim == TA_DIMENSION_HORZ) ? TA_DIR_UP
1455 : TA_DIR_RIGHT;
1456 #endif
1458 /* we ignore all segments that are less than 1 pixel in length */
1459 /* to avoid many problems with serif fonts */
1460 /* (the corresponding threshold is computed in font units) */
1461 if (dim == TA_DIMENSION_HORZ)
1462 segment_length_threshold = FT_DivFix(64, hints->y_scale);
1463 else
1464 segment_length_threshold = 0;
1466 /********************************************************************/
1467 /* */
1468 /* We begin by generating a sorted table of edges for the current */
1469 /* direction. To do so, we simply scan each segment and try to find */
1470 /* an edge in our table that corresponds to its position. */
1471 /* */
1472 /* If no edge is found, we create and insert a new edge in the */
1473 /* sorted table. Otherwise, we simply add the segment to the edge's */
1474 /* list which gets processed in the second step to compute the */
1475 /* edge's properties. */
1476 /* */
1477 /* Note that the table of edges is sorted along the segment/edge */
1478 /* position. */
1479 /* */
1480 /********************************************************************/
1482 /* assure that edge distance threshold is at most 0.25px */
1483 edge_distance_threshold = FT_MulFix(laxis->edge_distance_threshold,
1484 scale);
1485 if (edge_distance_threshold > 64 / 4)
1486 edge_distance_threshold = 64 / 4;
1488 edge_distance_threshold = FT_DivFix(edge_distance_threshold,
1489 scale);
1491 for (seg = segments; seg < segment_limit; seg++)
1493 TA_Edge found = NULL;
1494 FT_Int ee;
1497 if (seg->height < segment_length_threshold)
1498 continue;
1500 /* a special case for serif edges: */
1501 /* if they are smaller than 1.5 pixels we ignore them */
1502 if (seg->serif
1503 && 2 * seg->height < 3 * segment_length_threshold)
1504 continue;
1506 /* look for an edge corresponding to the segment */
1507 for (ee = 0; ee < axis->num_edges; ee++)
1509 TA_Edge edge = axis->edges + ee;
1510 FT_Pos dist;
1513 dist = seg->pos - edge->fpos;
1514 if (dist < 0)
1515 dist = -dist;
1517 if (dist < edge_distance_threshold && edge->dir == seg->dir)
1519 found = edge;
1520 break;
1524 if (!found)
1526 TA_Edge edge;
1529 /* insert a new edge in the list and sort according to the position */
1530 error = ta_axis_hints_new_edge(axis, seg->pos,
1531 (TA_Direction)seg->dir,
1532 &edge);
1533 if (error)
1534 goto Exit;
1536 /* add the segment to the new edge's list */
1537 memset(edge, 0, sizeof (TA_EdgeRec));
1538 edge->first = seg;
1539 edge->last = seg;
1540 edge->dir = seg->dir;
1541 edge->fpos = seg->pos;
1542 edge->opos = FT_MulFix(seg->pos, scale);
1543 edge->pos = edge->opos;
1544 seg->edge_next = seg;
1546 else
1548 /* if an edge was found, simply add the segment to the edge's list */
1549 seg->edge_next = found->first;
1550 found->last->edge_next = seg;
1551 found->last = seg;
1555 /*****************************************************************/
1556 /* */
1557 /* Good, we now compute each edge's properties according to */
1558 /* the segments found on its position. Basically, these are */
1559 /* */
1560 /* - the edge's main direction */
1561 /* - stem edge, serif edge or both (which defaults to stem then) */
1562 /* - rounded edge, straight or both (which defaults to straight) */
1563 /* - link for edge */
1564 /* */
1565 /*****************************************************************/
1567 /* first of all, set the `edge' field in each segment -- this is */
1568 /* required in order to compute edge links */
1570 /* note that removing this loop and setting the `edge' field of each */
1571 /* segment directly in the code above slows down execution speed for */
1572 /* some reasons on platforms like the Sun */
1574 TA_Edge edges = axis->edges;
1575 TA_Edge edge_limit = edges + axis->num_edges;
1576 TA_Edge edge;
1579 for (edge = edges; edge < edge_limit; edge++)
1581 seg = edge->first;
1582 if (seg)
1585 seg->edge = edge;
1586 seg = seg->edge_next;
1587 } while (seg != edge->first);
1590 /* now compute each edge properties */
1591 for (edge = edges; edge < edge_limit; edge++)
1593 FT_Int is_round = 0; /* does it contain round segments? */
1594 FT_Int is_straight = 0; /* does it contain straight segments? */
1595 #if 0
1596 FT_Pos ups = 0; /* number of upwards segments */
1597 FT_Pos downs = 0; /* number of downwards segments */
1598 #endif
1601 seg = edge->first;
1605 FT_Bool is_serif;
1608 /* check for roundness of segment */
1609 if (seg->flags & TA_EDGE_ROUND)
1610 is_round++;
1611 else
1612 is_straight++;
1614 #if 0
1615 /* check for segment direction */
1616 if (seg->dir == up_dir)
1617 ups += seg->max_coord - seg->min_coord;
1618 else
1619 downs += seg->max_coord - seg->min_coord;
1620 #endif
1622 /* check for links -- */
1623 /* if seg->serif is set, then seg->link must be ignored */
1624 is_serif = (FT_Bool)(seg->serif
1625 && seg->serif->edge
1626 && seg->serif->edge != edge);
1628 if ((seg->link && seg->link->edge != NULL)
1629 || is_serif)
1631 TA_Edge edge2;
1632 TA_Segment seg2;
1635 edge2 = edge->link;
1636 seg2 = seg->link;
1638 if (is_serif)
1640 seg2 = seg->serif;
1641 edge2 = edge->serif;
1644 if (edge2)
1646 FT_Pos edge_delta;
1647 FT_Pos seg_delta;
1650 edge_delta = edge->fpos - edge2->fpos;
1651 if (edge_delta < 0)
1652 edge_delta = -edge_delta;
1654 seg_delta = seg->pos - seg2->pos;
1655 if (seg_delta < 0)
1656 seg_delta = -seg_delta;
1658 if (seg_delta < edge_delta)
1659 edge2 = seg2->edge;
1661 else
1662 edge2 = seg2->edge;
1664 if (is_serif)
1666 edge->serif = edge2;
1667 edge2->flags |= TA_EDGE_SERIF;
1669 else
1670 edge->link = edge2;
1673 seg = seg->edge_next;
1674 } while (seg != edge->first);
1676 /* set the round/straight flags */
1677 edge->flags = TA_EDGE_NORMAL;
1679 if (is_round > 0
1680 && is_round >= is_straight)
1681 edge->flags |= TA_EDGE_ROUND;
1683 #if 0
1684 /* set the edge's main direction */
1685 edge->dir = TA_DIR_NONE;
1687 if (ups > downs)
1688 edge->dir = (FT_Char)up_dir;
1690 else if (ups < downs)
1691 edge->dir = (FT_Char)-up_dir;
1693 else if (ups == downs)
1694 edge->dir = 0; /* both up and down! */
1695 #endif
1697 /* get rid of serifs if link is set */
1698 /* XXX: this gets rid of many unpleasant artefacts! */
1699 /* example: the `c' in cour.pfa at size 13 */
1701 if (edge->serif && edge->link)
1702 edge->serif = 0;
1706 Exit:
1707 return error;
1711 /* detect segments and edges for given dimension */
1713 FT_Error
1714 ta_latin_hints_detect_features(TA_GlyphHints hints,
1715 TA_Dimension dim)
1717 FT_Error error;
1720 error = ta_latin_hints_compute_segments(hints, dim);
1721 if (!error)
1723 ta_latin_hints_link_segments(hints, dim);
1725 error = ta_latin_hints_compute_edges(hints, dim);
1728 return error;
1732 /* compute all edges which lie within blue zones */
1734 void
1735 ta_latin_hints_compute_blue_edges(TA_GlyphHints hints,
1736 TA_LatinMetrics metrics)
1738 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
1740 TA_Edge edge = axis->edges;
1741 TA_Edge edge_limit = edge + axis->num_edges;
1743 TA_LatinAxis latin = &metrics->axis[TA_DIMENSION_VERT];
1744 FT_Fixed scale = latin->scale;
1747 /* compute which blue zones are active, */
1748 /* i.e. have their scaled size < 3/4 pixels */
1750 /* for each horizontal edge search the blue zone which is closest */
1751 for (; edge < edge_limit; edge++)
1753 FT_UInt bb;
1754 TA_Width best_blue = NULL;
1755 FT_Pos best_dist; /* initial threshold */
1757 FT_UInt best_blue_idx = 0;
1758 FT_Bool best_blue_is_shoot = 0;
1761 /* compute the initial threshold as a fraction of the EM size */
1762 /* (the value 40 is heuristic) */
1763 best_dist = FT_MulFix(metrics->units_per_em / 40, scale);
1765 /* assure a minimum distance of 0.5px */
1766 if (best_dist > 64 / 2)
1767 best_dist = 64 / 2;
1769 /* this loop also handles the two extra blue zones */
1770 /* for usWinAscent and usWinDescent */
1771 /* if option `windows-compatibility' is set */
1772 for (bb = 0;
1773 bb < latin->blue_count
1774 + (metrics->root.globals->font->windows_compatibility ? 2 : 0);
1775 bb++)
1777 TA_LatinBlue blue = latin->blues + bb;
1778 FT_Bool is_top_blue, is_major_dir;
1781 /* skip inactive blue zones (i.e., those that are too large) */
1782 if (!(blue->flags & TA_LATIN_BLUE_ACTIVE))
1783 continue;
1785 /* if it is a top zone, check for right edges -- */
1786 /* if it is a bottom zone, check for left edges */
1787 is_top_blue = (FT_Byte)((blue->flags & TA_LATIN_BLUE_TOP) != 0);
1788 is_major_dir = FT_BOOL(edge->dir == axis->major_dir);
1790 /* if it is a top zone, the edge must be against the major */
1791 /* direction; if it is a bottom zone, it must be in the major */
1792 /* direction */
1793 if (is_top_blue ^ is_major_dir)
1795 FT_Pos dist;
1798 /* first of all, compare it to the reference position */
1799 dist = edge->fpos - blue->ref.org;
1800 if (dist < 0)
1801 dist = -dist;
1803 dist = FT_MulFix(dist, scale);
1804 if (dist < best_dist)
1806 best_dist = dist;
1807 best_blue = &blue->ref;
1809 best_blue_idx = bb;
1810 best_blue_is_shoot = 0;
1813 /* now compare it to the overshoot position and check whether */
1814 /* the edge is rounded, and whether the edge is over the */
1815 /* reference position of a top zone, or under the reference */
1816 /* position of a bottom zone */
1817 if (edge->flags & TA_EDGE_ROUND
1818 && dist != 0)
1820 FT_Bool is_under_ref = FT_BOOL(edge->fpos < blue->ref.org);
1823 if (is_top_blue ^ is_under_ref)
1825 dist = edge->fpos - blue->shoot.org;
1826 if (dist < 0)
1827 dist = -dist;
1829 dist = FT_MulFix(dist, scale);
1830 if (dist < best_dist)
1832 best_dist = dist;
1833 best_blue = &blue->shoot;
1835 best_blue_idx = bb;
1836 best_blue_is_shoot = 1;
1843 if (best_blue)
1845 edge->blue_edge = best_blue;
1846 edge->best_blue_idx = best_blue_idx;
1847 edge->best_blue_is_shoot = best_blue_is_shoot;
1853 /* initalize hinting engine */
1855 static FT_Error
1856 ta_latin_hints_init(TA_GlyphHints hints,
1857 TA_LatinMetrics metrics)
1859 FT_Render_Mode mode;
1860 FT_UInt32 scaler_flags, other_flags;
1861 FT_Face face = metrics->root.scaler.face;
1864 ta_glyph_hints_rescale(hints, (TA_ScriptMetrics)metrics);
1866 /* correct x_scale and y_scale if needed, since they may have */
1867 /* been modified by `ta_latin_metrics_scale_dim' above */
1868 hints->x_scale = metrics->axis[TA_DIMENSION_HORZ].scale;
1869 hints->x_delta = metrics->axis[TA_DIMENSION_HORZ].delta;
1870 hints->y_scale = metrics->axis[TA_DIMENSION_VERT].scale;
1871 hints->y_delta = metrics->axis[TA_DIMENSION_VERT].delta;
1873 /* compute flags depending on render mode, etc. */
1874 mode = metrics->root.scaler.render_mode;
1876 #if 0 /* #ifdef TA_CONFIG_OPTION_USE_WARPER */
1877 if (mode == FT_RENDER_MODE_LCD
1878 || mode == FT_RENDER_MODE_LCD_V)
1879 metrics->root.scaler.render_mode =
1880 mode = FT_RENDER_MODE_NORMAL;
1881 #endif
1883 scaler_flags = hints->scaler_flags;
1884 other_flags = 0;
1886 /* we snap the width of vertical stems for the monochrome */
1887 /* and horizontal LCD rendering targets only */
1888 if (mode == FT_RENDER_MODE_MONO
1889 || mode == FT_RENDER_MODE_LCD)
1890 other_flags |= TA_LATIN_HINTS_HORZ_SNAP;
1892 /* we snap the width of horizontal stems for the monochrome */
1893 /* and vertical LCD rendering targets only */
1894 if (mode == FT_RENDER_MODE_MONO
1895 || mode == FT_RENDER_MODE_LCD_V)
1896 other_flags |= TA_LATIN_HINTS_VERT_SNAP;
1898 /* we adjust stems to full pixels only if we don't use the `light' mode */
1899 if (mode != FT_RENDER_MODE_LIGHT)
1900 other_flags |= TA_LATIN_HINTS_STEM_ADJUST;
1902 if (mode == FT_RENDER_MODE_MONO)
1903 other_flags |= TA_LATIN_HINTS_MONO;
1905 /* in `light' hinting mode we disable horizontal hinting completely; */
1906 /* we also do it if the face is italic */
1907 if (mode == FT_RENDER_MODE_LIGHT
1908 || (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0)
1909 scaler_flags |= TA_SCALER_FLAG_NO_HORIZONTAL;
1911 hints->scaler_flags = scaler_flags;
1912 hints->other_flags = other_flags;
1914 return FT_Err_Ok;
1918 /* snap a given width in scaled coordinates to */
1919 /* one of the current standard widths */
1921 static FT_Pos
1922 ta_latin_snap_width(TA_Width widths,
1923 FT_Int count,
1924 FT_Pos width)
1926 int n;
1927 FT_Pos best = 64 + 32 + 2;
1928 FT_Pos reference = width;
1929 FT_Pos scaled;
1932 for (n = 0; n < count; n++)
1934 FT_Pos w;
1935 FT_Pos dist;
1938 w = widths[n].cur;
1939 dist = width - w;
1940 if (dist < 0)
1941 dist = -dist;
1942 if (dist < best)
1944 best = dist;
1945 reference = w;
1949 scaled = TA_PIX_ROUND(reference);
1951 if (width >= reference)
1953 if (width < scaled + 48)
1954 width = reference;
1956 else
1958 if (width > scaled - 48)
1959 width = reference;
1962 return width;
1966 /* compute the snapped width of a given stem, ignoring very thin ones */
1968 /* there is a lot of voodoo in this function; changing the hard-coded */
1969 /* parameters influence the whole hinting process */
1971 static FT_Pos
1972 ta_latin_compute_stem_width(TA_GlyphHints hints,
1973 TA_Dimension dim,
1974 FT_Pos width,
1975 FT_Byte base_flags,
1976 FT_Byte stem_flags)
1978 TA_LatinMetrics metrics = (TA_LatinMetrics) hints->metrics;
1979 TA_LatinAxis axis = &metrics->axis[dim];
1981 FT_Pos dist = width;
1982 FT_Int sign = 0;
1983 FT_Int vertical = (dim == TA_DIMENSION_VERT);
1986 if (!TA_LATIN_HINTS_DO_STEM_ADJUST(hints)
1987 || axis->extra_light)
1988 return width;
1990 if (dist < 0)
1992 dist = -width;
1993 sign = 1;
1996 if ((vertical && !TA_LATIN_HINTS_DO_VERT_SNAP(hints))
1997 || (!vertical && !TA_LATIN_HINTS_DO_HORZ_SNAP(hints)))
1999 /* smooth hinting process: very lightly quantize the stem width */
2001 /* leave the widths of serifs alone */
2002 if ((stem_flags & TA_EDGE_SERIF)
2003 && vertical
2004 && (dist < 3 * 64))
2005 goto Done_Width;
2006 else if (base_flags & TA_EDGE_ROUND)
2008 if (dist < 80)
2009 dist = 64;
2011 else if (dist < 56)
2012 dist = 56;
2014 if (axis->width_count > 0)
2016 FT_Pos delta;
2019 /* compare to standard width */
2020 delta = dist - axis->widths[0].cur;
2022 if (delta < 0)
2023 delta = -delta;
2025 if (delta < 40)
2027 dist = axis->widths[0].cur;
2028 if (dist < 48)
2029 dist = 48;
2031 goto Done_Width;
2034 if (dist < 3 * 64)
2036 delta = dist & 63;
2037 dist &= -64;
2039 if (delta < 10)
2040 dist += delta;
2041 else if (delta < 32)
2042 dist += 10;
2043 else if (delta < 54)
2044 dist += 54;
2045 else
2046 dist += delta;
2048 else
2049 dist = (dist + 32) & ~63;
2052 else
2054 /* strong hinting process: snap the stem width to integer pixels */
2056 FT_Pos org_dist = dist;
2059 dist = ta_latin_snap_width(axis->widths, axis->width_count, dist);
2061 if (vertical)
2063 /* in the case of vertical hinting, */
2064 /* always round the stem heights to integer pixels */
2066 if (dist >= 64)
2067 dist = (dist + 16) & ~63;
2068 else
2069 dist = 64;
2071 else
2073 if (TA_LATIN_HINTS_DO_MONO(hints))
2075 /* monochrome horizontal hinting: */
2076 /* snap widths to integer pixels with a different threshold */
2078 if (dist < 64)
2079 dist = 64;
2080 else
2081 dist = (dist + 32) & ~63;
2083 else
2085 /* for horizontal anti-aliased hinting, we adopt a more subtle */
2086 /* approach: we strengthen small stems, round stems whose size */
2087 /* is between 1 and 2 pixels to an integer, otherwise nothing */
2089 if (dist < 48)
2090 dist = (dist + 64) >> 1;
2092 else if (dist < 128)
2094 /* we only round to an integer width if the corresponding */
2095 /* distortion is less than 1/4 pixel -- otherwise, this */
2096 /* makes everything worse since the diagonals, which are */
2097 /* not hinted, appear a lot bolder or thinner than the */
2098 /* vertical stems */
2100 FT_Pos delta;
2103 dist = (dist + 22) & ~63;
2104 delta = dist - org_dist;
2105 if (delta < 0)
2106 delta = -delta;
2108 if (delta >= 16)
2110 dist = org_dist;
2111 if (dist < 48)
2112 dist = (dist + 64) >> 1;
2115 else
2116 /* round otherwise to prevent color fringes in LCD mode */
2117 dist = (dist + 32) & ~63;
2122 Done_Width:
2123 if (sign)
2124 dist = -dist;
2126 return dist;
2130 /* align one stem edge relative to the previous stem edge */
2132 static void
2133 ta_latin_align_linked_edge(TA_GlyphHints hints,
2134 TA_Dimension dim,
2135 TA_Edge base_edge,
2136 TA_Edge stem_edge)
2138 FT_Pos dist = stem_edge->opos - base_edge->opos;
2140 FT_Pos fitted_width = ta_latin_compute_stem_width(
2141 hints, dim, dist,
2142 base_edge->flags,
2143 stem_edge->flags);
2146 stem_edge->pos = base_edge->pos + fitted_width;
2148 TA_LOG((" LINK: edge %d (opos=%.2f) linked to %.2f,"
2149 " dist was %.2f, now %.2f\n",
2150 stem_edge - hints->axis[dim].edges, stem_edge->opos / 64.0,
2151 stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0));
2153 if (hints->recorder)
2154 hints->recorder(ta_link, hints, dim,
2155 base_edge, stem_edge, NULL, NULL, NULL);
2159 /* shift the coordinates of the `serif' edge by the same amount */
2160 /* as the corresponding `base' edge has been moved already */
2162 static void
2163 ta_latin_align_serif_edge(TA_GlyphHints hints,
2164 TA_Edge base,
2165 TA_Edge serif)
2167 FT_UNUSED(hints);
2169 serif->pos = base->pos + (serif->opos - base->opos);
2173 /* the main grid-fitting routine */
2175 void
2176 ta_latin_hint_edges(TA_GlyphHints hints,
2177 TA_Dimension dim)
2179 TA_AxisHints axis = &hints->axis[dim];
2181 TA_Edge edges = axis->edges;
2182 TA_Edge edge_limit = edges + axis->num_edges;
2183 FT_PtrDist n_edges;
2184 TA_Edge edge;
2186 TA_Edge anchor = NULL;
2187 FT_Int has_serifs = 0;
2189 #ifdef TA_DEBUG
2190 FT_UInt num_actions = 0;
2191 #endif
2193 TA_LOG(("latin %s edge hinting (script `%s')\n",
2194 dim == TA_DIMENSION_VERT ? "horizontal" : "vertical",
2195 ta_script_names[hints->metrics->script_class->script]));
2197 /* we begin by aligning all stems relative to the blue zone if needed -- */
2198 /* that's only for horizontal edges */
2200 if (dim == TA_DIMENSION_VERT
2201 && TA_HINTS_DO_BLUES(hints))
2203 for (edge = edges; edge < edge_limit; edge++)
2205 TA_Width blue;
2206 TA_Edge edge1, edge2; /* these edges form the stem to check */
2209 if (edge->flags & TA_EDGE_DONE)
2210 continue;
2212 blue = edge->blue_edge;
2213 edge1 = NULL;
2214 edge2 = edge->link;
2216 if (blue)
2217 edge1 = edge;
2219 /* flip edges if the other stem is aligned to a blue zone */
2220 else if (edge2 && edge2->blue_edge)
2222 blue = edge2->blue_edge;
2223 edge1 = edge2;
2224 edge2 = edge;
2227 if (!edge1)
2228 continue;
2230 #ifdef TA_DEBUG
2231 if (!anchor)
2232 TA_LOG((" BLUE_ANCHOR: edge %d (opos=%.2f) snapped to %.2f,"
2233 " was %.2f (anchor=edge %d)\n",
2234 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
2235 edge1->pos / 64.0, edge - edges));
2236 else
2237 TA_LOG((" BLUE: edge %d (opos=%.2f) snapped to %.2f, was %.2f\n",
2238 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
2239 edge1->pos / 64.0));
2241 num_actions++;
2242 #endif
2244 edge1->pos = blue->fit;
2245 edge1->flags |= TA_EDGE_DONE;
2247 if (hints->recorder)
2249 if (!anchor)
2250 hints->recorder(ta_blue_anchor, hints, dim,
2251 edge1, edge, NULL, NULL, NULL);
2252 else
2253 hints->recorder(ta_blue, hints, dim,
2254 edge1, NULL, NULL, NULL, NULL);
2257 if (edge2 && !edge2->blue_edge)
2259 ta_latin_align_linked_edge(hints, dim, edge1, edge2);
2260 edge2->flags |= TA_EDGE_DONE;
2262 #ifdef TA_DEBUG
2263 num_actions++;
2264 #endif
2267 if (!anchor)
2268 anchor = edge;
2272 /* now we align all other stem edges, */
2273 /* trying to maintain the relative order of stems in the glyph */
2274 for (edge = edges; edge < edge_limit; edge++)
2276 TA_Edge edge2;
2279 if (edge->flags & TA_EDGE_DONE)
2280 continue;
2282 /* skip all non-stem edges */
2283 edge2 = edge->link;
2284 if (!edge2)
2286 has_serifs++;
2287 continue;
2290 /* now align the stem */
2292 /* this should not happen, but it's better to be safe */
2293 if (edge2->blue_edge)
2295 TA_LOG((" ASSERTION FAILED for edge %d\n", edge2-edges));
2297 ta_latin_align_linked_edge(hints, dim, edge2, edge);
2298 edge->flags |= TA_EDGE_DONE;
2300 #ifdef TA_DEBUG
2301 num_actions++;
2302 #endif
2303 continue;
2306 if (!anchor)
2308 /* if we reach this if clause, no stem has been aligned yet */
2310 FT_Pos org_len, org_center, cur_len;
2311 FT_Pos cur_pos1, error1, error2, u_off, d_off;
2314 org_len = edge2->opos - edge->opos;
2315 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2316 edge->flags, edge2->flags);
2318 /* some voodoo to specially round edges for small stem widths; */
2319 /* the idea is to align the center of a stem, */
2320 /* then shifting the stem edges to suitable positions */
2321 if (cur_len <= 64)
2323 /* width <= 1px */
2324 u_off = 32;
2325 d_off = 32;
2327 else
2329 /* 1px < width < 1.5px */
2330 u_off = 38;
2331 d_off = 26;
2334 if (cur_len < 96)
2336 org_center = edge->opos + (org_len >> 1);
2337 cur_pos1 = TA_PIX_ROUND(org_center);
2339 error1 = org_center - (cur_pos1 - u_off);
2340 if (error1 < 0)
2341 error1 = -error1;
2343 error2 = org_center - (cur_pos1 + d_off);
2344 if (error2 < 0)
2345 error2 = -error2;
2347 if (error1 < error2)
2348 cur_pos1 -= u_off;
2349 else
2350 cur_pos1 += d_off;
2352 edge->pos = cur_pos1 - cur_len / 2;
2353 edge2->pos = edge->pos + cur_len;
2355 else
2356 edge->pos = TA_PIX_ROUND(edge->opos);
2358 anchor = edge;
2359 edge->flags |= TA_EDGE_DONE;
2361 TA_LOG((" ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
2362 " snapped to %.2f and %.2f\n",
2363 edge - edges, edge->opos / 64.0,
2364 edge2 - edges, edge2->opos / 64.0,
2365 edge->pos / 64.0, edge2->pos / 64.0));
2367 if (hints->recorder)
2368 hints->recorder(ta_anchor, hints, dim,
2369 edge, edge2, NULL, NULL, NULL);
2371 ta_latin_align_linked_edge(hints, dim, edge, edge2);
2373 #ifdef TA_DEBUG
2374 num_actions += 2;
2375 #endif
2377 else
2379 FT_Pos org_pos, org_len, org_center, cur_len;
2380 FT_Pos cur_pos1, cur_pos2, delta1, delta2;
2383 org_pos = anchor->pos + (edge->opos - anchor->opos);
2384 org_len = edge2->opos - edge->opos;
2385 org_center = org_pos + (org_len >> 1);
2387 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2388 edge->flags, edge2->flags);
2390 if (edge2->flags & TA_EDGE_DONE)
2392 TA_LOG((" ADJUST: edge %d (pos=%.2f) moved to %.2f\n",
2393 edge - edges, edge->pos / 64.0,
2394 (edge2->pos - cur_len) / 64.0));
2396 edge->pos = edge2->pos - cur_len;
2398 if (hints->recorder)
2400 TA_Edge bound = NULL;
2403 if (edge > edges)
2404 bound = &edge[-1];
2406 hints->recorder(ta_adjust, hints, dim,
2407 edge, edge2, NULL, bound, NULL);
2411 else if (cur_len < 96)
2413 FT_Pos u_off, d_off;
2416 cur_pos1 = TA_PIX_ROUND(org_center);
2418 if (cur_len <= 64)
2420 u_off = 32;
2421 d_off = 32;
2423 else
2425 u_off = 38;
2426 d_off = 26;
2429 delta1 = org_center - (cur_pos1 - u_off);
2430 if (delta1 < 0)
2431 delta1 = -delta1;
2433 delta2 = org_center - (cur_pos1 + d_off);
2434 if (delta2 < 0)
2435 delta2 = -delta2;
2437 if (delta1 < delta2)
2438 cur_pos1 -= u_off;
2439 else
2440 cur_pos1 += d_off;
2442 edge->pos = cur_pos1 - cur_len / 2;
2443 edge2->pos = cur_pos1 + cur_len / 2;
2445 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2446 " snapped to %.2f and %.2f\n",
2447 edge - edges, edge->opos / 64.0,
2448 edge2 - edges, edge2->opos / 64.0,
2449 edge->pos / 64.0, edge2->pos / 64.0));
2451 if (hints->recorder)
2453 TA_Edge bound = NULL;
2456 if (edge > edges)
2457 bound = &edge[-1];
2459 hints->recorder(ta_stem, hints, dim,
2460 edge, edge2, NULL, bound, NULL);
2464 else
2466 org_pos = anchor->pos + (edge->opos - anchor->opos);
2467 org_len = edge2->opos - edge->opos;
2468 org_center = org_pos + (org_len >> 1);
2470 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2471 edge->flags, edge2->flags);
2473 cur_pos1 = TA_PIX_ROUND(org_pos);
2474 delta1 = cur_pos1 + (cur_len >> 1) - org_center;
2475 if (delta1 < 0)
2476 delta1 = -delta1;
2478 cur_pos2 = TA_PIX_ROUND(org_pos + org_len) - cur_len;
2479 delta2 = cur_pos2 + (cur_len >> 1) - org_center;
2480 if (delta2 < 0)
2481 delta2 = -delta2;
2483 edge->pos = (delta1 < delta2) ? cur_pos1 : cur_pos2;
2484 edge2->pos = edge->pos + cur_len;
2486 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2487 " snapped to %.2f and %.2f\n",
2488 edge - edges, edge->opos / 64.0,
2489 edge2 - edges, edge2->opos / 64.0,
2490 edge->pos / 64.0, edge2->pos / 64.0));
2492 if (hints->recorder)
2494 TA_Edge bound = NULL;
2497 if (edge > edges)
2498 bound = &edge[-1];
2500 hints->recorder(ta_stem, hints, dim,
2501 edge, edge2, NULL, bound, NULL);
2505 #ifdef TA_DEBUG
2506 num_actions++;
2507 #endif
2509 edge->flags |= TA_EDGE_DONE;
2510 edge2->flags |= TA_EDGE_DONE;
2512 if (edge > edges
2513 && edge->pos < edge[-1].pos)
2515 #ifdef TA_DEBUG
2516 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2517 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
2519 num_actions++;
2520 #endif
2522 edge->pos = edge[-1].pos;
2524 if (hints->recorder)
2525 hints->recorder(ta_bound, hints, dim,
2526 edge, &edge[-1], NULL, NULL, NULL);
2531 /* make sure that lowercase m's maintain their symmetry */
2533 /* In general, lowercase m's have six vertical edges if they are sans */
2534 /* serif, or twelve if they are with serifs. This implementation is */
2535 /* based on that assumption, and seems to work very well with most */
2536 /* faces. However, if for a certain face this assumption is not */
2537 /* true, the m is just rendered like before. In addition, any stem */
2538 /* correction will only be applied to symmetrical glyphs (even if the */
2539 /* glyph is not an m), so the potential for unwanted distortion is */
2540 /* relatively low. */
2542 /* we don't handle horizontal edges since we can't easily assure that */
2543 /* the third (lowest) stem aligns with the base line; it might end up */
2544 /* one pixel higher or lower */
2546 n_edges = edge_limit - edges;
2547 if (dim == TA_DIMENSION_HORZ
2548 && (n_edges == 6 || n_edges == 12))
2550 TA_Edge edge1, edge2, edge3;
2551 FT_Pos dist1, dist2, span, delta;
2554 if (n_edges == 6)
2556 edge1 = edges;
2557 edge2 = edges + 2;
2558 edge3 = edges + 4;
2560 else
2562 edge1 = edges + 1;
2563 edge2 = edges + 5;
2564 edge3 = edges + 9;
2567 dist1 = edge2->opos - edge1->opos;
2568 dist2 = edge3->opos - edge2->opos;
2570 span = dist1 - dist2;
2571 if (span < 0)
2572 span = -span;
2574 if (span < 8)
2576 delta = edge3->pos - (2 * edge2->pos - edge1->pos);
2577 edge3->pos -= delta;
2578 if (edge3->link)
2579 edge3->link->pos -= delta;
2581 /* move the serifs along with the stem */
2582 if (n_edges == 12)
2584 (edges + 8)->pos -= delta;
2585 (edges + 11)->pos -= delta;
2588 edge3->flags |= TA_EDGE_DONE;
2589 if (edge3->link)
2590 edge3->link->flags |= TA_EDGE_DONE;
2594 if (has_serifs || !anchor)
2596 /* now hint the remaining edges (serifs and single) */
2597 /* in order to complete our processing */
2598 for (edge = edges; edge < edge_limit; edge++)
2600 TA_Edge lower_bound = NULL;
2601 TA_Edge upper_bound = NULL;
2603 FT_Pos delta;
2606 if (edge->flags & TA_EDGE_DONE)
2607 continue;
2609 delta = 1000;
2611 if (edge->serif)
2613 delta = edge->serif->opos - edge->opos;
2614 if (delta < 0)
2615 delta = -delta;
2618 if (edge > edges)
2619 lower_bound = &edge[-1];
2621 if (edge + 1 < edge_limit
2622 && edge[1].flags & TA_EDGE_DONE)
2623 upper_bound = &edge[1];
2626 if (delta < 64 + 16)
2628 ta_latin_align_serif_edge(hints, edge->serif, edge);
2630 TA_LOG((" SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
2631 " aligned to %.2f\n",
2632 edge - edges, edge->opos / 64.0,
2633 edge->serif - edges, edge->serif->opos / 64.0,
2634 edge->pos / 64.0));
2636 if (hints->recorder)
2637 hints->recorder(ta_serif, hints, dim,
2638 edge, NULL, NULL, lower_bound, upper_bound);
2640 else if (!anchor)
2642 edge->pos = TA_PIX_ROUND(edge->opos);
2643 anchor = edge;
2645 TA_LOG((" SERIF_ANCHOR: edge %d (opos=%.2f) snapped to %.2f\n",
2646 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
2648 if (hints->recorder)
2649 hints->recorder(ta_serif_anchor, hints, dim,
2650 edge, NULL, NULL, lower_bound, upper_bound);
2652 else
2654 TA_Edge before, after;
2657 for (before = edge - 1; before >= edges; before--)
2658 if (before->flags & TA_EDGE_DONE)
2659 break;
2661 for (after = edge + 1; after < edge_limit; after++)
2662 if (after->flags & TA_EDGE_DONE)
2663 break;
2665 if (before >= edges && before < edge
2666 && after < edge_limit && after > edge)
2668 if (after->opos == before->opos)
2669 edge->pos = before->pos;
2670 else
2671 edge->pos = before->pos + FT_MulDiv(edge->opos - before->opos,
2672 after->pos - before->pos,
2673 after->opos - before->opos);
2675 TA_LOG((" SERIF_LINK1: edge %d (opos=%.2f) snapped to %.2f"
2676 " from %d (opos=%.2f)\n",
2677 edge - edges, edge->opos / 64.0,
2678 edge->pos / 64.0,
2679 before - edges, before->opos / 64.0));
2681 if (hints->recorder)
2682 hints->recorder(ta_serif_link1, hints, dim,
2683 edge, before, after, lower_bound, upper_bound);
2685 else
2687 edge->pos = anchor->pos + ((edge->opos - anchor->opos + 16) & ~31);
2688 TA_LOG((" SERIF_LINK2: edge %d (opos=%.2f) snapped to %.2f\n",
2689 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
2691 if (hints->recorder)
2692 hints->recorder(ta_serif_link2, hints, dim,
2693 edge, NULL, NULL, lower_bound, upper_bound);
2697 #ifdef TA_DEBUG
2698 num_actions++;
2699 #endif
2700 edge->flags |= TA_EDGE_DONE;
2702 if (edge > edges
2703 && edge->pos < edge[-1].pos)
2705 #ifdef TA_DEBUG
2706 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2707 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);
2718 if (edge + 1 < edge_limit
2719 && edge[1].flags & TA_EDGE_DONE
2720 && edge->pos > edge[1].pos)
2722 #ifdef TA_DEBUG
2723 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2724 edge - edges, edge->pos / 64.0, edge[1].pos / 64.0));
2726 num_actions++;
2727 #endif
2729 edge->pos = edge[1].pos;
2731 if (hints->recorder)
2732 hints->recorder(ta_bound, hints, dim,
2733 edge, &edge[1], NULL, NULL, NULL);
2738 #ifdef TA_DEBUG
2739 if (!num_actions)
2740 TA_LOG((" (none)\n"));
2741 TA_LOG(("\n"));
2742 #endif
2746 /* apply the complete hinting algorithm to a latin glyph */
2748 static FT_Error
2749 ta_latin_hints_apply(TA_GlyphHints hints,
2750 FT_Outline* outline,
2751 TA_LatinMetrics metrics)
2753 FT_Error error;
2754 int dim;
2757 error = ta_glyph_hints_reload(hints, outline);
2758 if (error)
2759 goto Exit;
2761 /* analyze glyph outline */
2762 #ifdef TA_CONFIG_OPTION_USE_WARPER
2763 if (metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT
2764 || TA_HINTS_DO_HORIZONTAL(hints))
2765 #else
2766 if (TA_HINTS_DO_HORIZONTAL(hints))
2767 #endif
2769 error = ta_latin_hints_detect_features(hints, TA_DIMENSION_HORZ);
2770 if (error)
2771 goto Exit;
2774 if (TA_HINTS_DO_VERTICAL(hints))
2776 error = ta_latin_hints_detect_features(hints, TA_DIMENSION_VERT);
2777 if (error)
2778 goto Exit;
2780 ta_latin_hints_compute_blue_edges(hints, metrics);
2783 /* grid-fit the outline */
2784 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
2786 #ifdef TA_CONFIG_OPTION_USE_WARPER
2787 if (dim == TA_DIMENSION_HORZ
2788 && metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT)
2790 TA_WarperRec warper;
2791 FT_Fixed scale;
2792 FT_Pos delta;
2795 ta_warper_compute(&warper, hints, (TA_Dimension)dim, &scale, &delta);
2796 ta_glyph_hints_scale_dim(hints, (TA_Dimension)dim, scale, delta);
2798 continue;
2800 #endif
2802 if ((dim == TA_DIMENSION_HORZ && TA_HINTS_DO_HORIZONTAL(hints))
2803 || (dim == TA_DIMENSION_VERT && TA_HINTS_DO_VERTICAL(hints)))
2805 ta_latin_hint_edges(hints, (TA_Dimension)dim);
2806 ta_glyph_hints_align_edge_points(hints, (TA_Dimension)dim);
2807 ta_glyph_hints_align_strong_points(hints, (TA_Dimension)dim);
2808 ta_glyph_hints_align_weak_points(hints, (TA_Dimension)dim);
2812 ta_glyph_hints_save(hints, outline);
2814 Exit:
2815 return error;
2819 const TA_WritingSystemClassRec ta_latin_writing_system_class =
2821 TA_WRITING_SYSTEM_LATIN,
2823 sizeof (TA_LatinMetricsRec),
2825 (TA_Script_InitMetricsFunc)ta_latin_metrics_init,
2826 (TA_Script_ScaleMetricsFunc)ta_latin_metrics_scale,
2827 (TA_Script_DoneMetricsFunc)NULL,
2829 (TA_Script_InitHintsFunc)ta_latin_hints_init,
2830 (TA_Script_ApplyHintsFunc)ta_latin_hints_apply
2834 /* XXX: this should probably fine tuned to differentiate better between */
2835 /* scripts... */
2837 static const TA_Script_UniRangeRec ta_latn_uniranges[] =
2839 TA_UNIRANGE_REC(0x0020UL, 0x007FUL), /* Basic Latin (no control chars) */
2840 TA_UNIRANGE_REC(0x00A0UL, 0x00FFUL), /* Latin-1 Supplement (no control chars) */
2841 TA_UNIRANGE_REC(0x0100UL, 0x017FUL), /* Latin Extended-A */
2842 TA_UNIRANGE_REC(0x0180UL, 0x024FUL), /* Latin Extended-B */
2843 TA_UNIRANGE_REC(0x0250UL, 0x02AFUL), /* IPA Extensions */
2844 TA_UNIRANGE_REC(0x02B0UL, 0x02FFUL), /* Spacing Modifier Letters */
2845 TA_UNIRANGE_REC(0x0300UL, 0x036FUL), /* Combining Diacritical Marks */
2846 TA_UNIRANGE_REC(0x1D00UL, 0x1D7FUL), /* Phonetic Extensions */
2847 TA_UNIRANGE_REC(0x1D80UL, 0x1DBFUL), /* Phonetic Extensions Supplement */
2848 TA_UNIRANGE_REC(0x1DC0UL, 0x1DFFUL), /* Combining Diacritical Marks Supplement */
2849 TA_UNIRANGE_REC(0x1E00UL, 0x1EFFUL), /* Latin Extended Additional */
2850 TA_UNIRANGE_REC(0x2000UL, 0x206FUL), /* General Punctuation */
2851 TA_UNIRANGE_REC(0x2070UL, 0x209FUL), /* Superscripts and Subscripts */
2852 TA_UNIRANGE_REC(0x20A0UL, 0x20CFUL), /* Currency Symbols */
2853 TA_UNIRANGE_REC(0x2150UL, 0x218FUL), /* Number Forms */
2854 TA_UNIRANGE_REC(0x2460UL, 0x24FFUL), /* Enclosed Alphanumerics */
2855 TA_UNIRANGE_REC(0x2C60UL, 0x2C7FUL), /* Latin Extended-C */
2856 TA_UNIRANGE_REC(0x2E00UL, 0x2E7FUL), /* Supplemental Punctuation */
2857 TA_UNIRANGE_REC(0xA720UL, 0xA7FFUL), /* Latin Extended-D */
2858 TA_UNIRANGE_REC(0xFB00UL, 0xFB06UL), /* Alphab. Present. Forms (Latin Ligs) */
2859 TA_UNIRANGE_REC(0x1D400UL, 0x1D7FFUL), /* Mathematical Alphanumeric Symbols */
2860 TA_UNIRANGE_REC(0x1F100UL, 0x1F1FFUL), /* Enclosed Alphanumeric Supplement */
2861 TA_UNIRANGE_REC(0UL, 0UL)
2864 static const TA_Script_UniRangeRec ta_grek_uniranges[] =
2866 TA_UNIRANGE_REC(0x0370UL, 0x03FFUL), /* Greek and Coptic */
2867 TA_UNIRANGE_REC(0x1F00UL, 0x1FFFUL), /* Greek Extended */
2868 TA_UNIRANGE_REC(0UL, 0UL )
2871 static const TA_Script_UniRangeRec ta_cyrl_uniranges[] =
2873 TA_UNIRANGE_REC(0x0400UL, 0x04FFUL), /* Cyrillic */
2874 TA_UNIRANGE_REC(0x0500UL, 0x052FUL), /* Cyrillic Supplement */
2875 TA_UNIRANGE_REC(0x2DE0UL, 0x2DFFUL), /* Cyrillic Extended-A */
2876 TA_UNIRANGE_REC(0xA640UL, 0xA69FUL), /* Cyrillic Extended-B */
2877 TA_UNIRANGE_REC(0UL, 0UL )
2880 static const TA_Script_UniRangeRec ta_hebr_uniranges[] =
2882 TA_UNIRANGE_REC(0x0590UL, 0x05FFUL), /* Hebrew */
2883 TA_UNIRANGE_REC(0xFB1DUL, 0xFB4FUL), /* Alphab. Present. Forms (Hebrew) */
2884 TA_UNIRANGE_REC(0UL, 0UL )
2888 const TA_ScriptClassRec ta_latn_script_class =
2890 TA_SCRIPT_LATN,
2891 TA_BLUE_STRINGSET_LATN,
2892 TA_WRITING_SYSTEM_LATIN,
2894 ta_latn_uniranges,
2898 const TA_ScriptClassRec ta_grek_script_class =
2900 TA_SCRIPT_GREK,
2901 TA_BLUE_STRINGSET_GREK,
2902 TA_WRITING_SYSTEM_LATIN,
2904 ta_grek_uniranges,
2905 0x3BF /* ο */
2908 const TA_ScriptClassRec ta_cyrl_script_class =
2910 TA_SCRIPT_CYRL,
2911 TA_BLUE_STRINGSET_CYRL,
2912 TA_WRITING_SYSTEM_LATIN,
2914 ta_cyrl_uniranges,
2915 0x43E /* о */
2918 const TA_ScriptClassRec ta_hebr_script_class =
2920 TA_SCRIPT_HEBR,
2921 TA_BLUE_STRINGSET_HEBR,
2922 TA_WRITING_SYSTEM_LATIN,
2924 ta_hebr_uniranges,
2925 0x5DD /* ם */
2928 /* end of talatin.c */