Synchronize with FreeType. [2/2]
[ttfautohint.git] / lib / talatin.c
blob66404d93f167764e08d2355324c639f35cd52092
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(("\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(("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(("%s widths:\n",
167 dim == TA_DIMENSION_VERT ? "horizontal"
168 : "vertical"));
170 TA_LOG((" %d (standard)", axis->standard_width));
171 for (i = 1; i < axis->width_count; i++)
172 TA_LOG((" %d", axis->widths[i].org));
174 TA_LOG(("\n"));
176 #endif
180 TA_LOG(("\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(("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 TA_LOG(("blue zone %d:\n", axis->blue_count));
223 num_flats = 0;
224 num_rounds = 0;
226 while (*p)
228 FT_ULong ch;
229 FT_UInt glyph_index;
230 FT_Pos best_y; /* same as points.y */
231 FT_Int best_point, best_contour_first, best_contour_last;
232 FT_Vector* points;
233 FT_Bool round = 0;
236 GET_UTF8_CHAR(ch, p);
238 /* load the character in the face -- skip unknown or empty ones */
239 glyph_index = FT_Get_Char_Index(face, ch);
240 if (glyph_index == 0)
242 TA_LOG((" U+%04lX unavailable\n", ch));
243 continue;
246 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
247 outline = face->glyph->outline;
248 if (error || outline.n_points <= 0)
250 TA_LOG((" U+%04lX contains no outlines\n", ch));
251 continue;
254 /* now compute min or max point indices and coordinates */
255 points = outline.points;
256 best_point = -1;
257 best_y = 0; /* make compiler happy */
258 best_contour_first = 0; /* ditto */
259 best_contour_last = 0; /* ditto */
262 FT_Int nn;
263 FT_Int first = 0;
264 FT_Int last = -1;
267 for (nn = 0; nn < outline.n_contours; first = last + 1, nn++)
269 FT_Int old_best_point = best_point;
270 FT_Int pp;
273 last = outline.contours[nn];
275 /* avoid single-point contours since they are never rasterized; */
276 /* in some fonts, they correspond to mark attachment points */
277 /* that are way outside of the glyph's real outline */
278 if (last <= first)
279 continue;
281 if (TA_LATIN_IS_TOP_BLUE(bs))
283 for (pp = first; pp <= last; pp++)
284 if (best_point < 0
285 || points[pp].y > best_y)
287 best_point = pp;
288 best_y = points[pp].y;
291 else
293 for (pp = first; pp <= last; pp++)
294 if (best_point < 0
295 || points[pp].y < best_y)
297 best_point = pp;
298 best_y = points[pp].y;
302 if (best_point != old_best_point)
304 best_contour_first = first;
305 best_contour_last = last;
310 /* now check whether the point belongs to a straight or round */
311 /* segment; we first need to find in which contour the extremum */
312 /* lies, then inspect its previous and next points */
313 if (best_point >= 0)
315 FT_Pos best_x = points[best_point].x;
316 FT_Int prev, next;
317 FT_Int best_segment_first, best_segment_last;
318 FT_Int best_on_point_first, best_on_point_last;
319 FT_Pos dist;
322 best_segment_first = best_point;
323 best_segment_last = best_point;
325 if (FT_CURVE_TAG(outline.tags[best_point]) == FT_CURVE_TAG_ON)
327 best_on_point_first = best_point;
328 best_on_point_last = best_point;
330 else
332 best_on_point_first = -1;
333 best_on_point_last = -1;
336 /* look for the previous and next points on the contour */
337 /* that are not on the same Y coordinate, then threshold */
338 /* the `closeness'... */
339 prev = best_point;
340 next = prev;
344 if (prev > best_contour_first)
345 prev--;
346 else
347 prev = best_contour_last;
349 dist = TA_ABS(points[prev].y - best_y);
350 /* accept a small distance or a small angle (both values are */
351 /* heuristic; value 20 corresponds to approx. 2.9 degrees) */
352 if (dist > 5)
353 if (TA_ABS(points[prev].x - best_x) <= 20 * dist)
354 break;
356 best_segment_first = prev;
358 if (FT_CURVE_TAG(outline.tags[prev]) == FT_CURVE_TAG_ON)
360 best_on_point_first = prev;
361 if (best_on_point_last < 0)
362 best_on_point_last = prev;
365 } while (prev != best_point);
369 if (next < best_contour_last)
370 next++;
371 else
372 next = best_contour_first;
374 dist = TA_ABS(points[next].y - best_y);
375 if (dist > 5)
376 if (TA_ABS(points[next].x - best_x) <= 20 * dist)
377 break;
379 best_segment_last = next;
381 if (FT_CURVE_TAG(outline.tags[next]) == FT_CURVE_TAG_ON)
383 best_on_point_last = next;
384 if (best_on_point_first < 0)
385 best_on_point_first = next;
388 } while (next != best_point);
390 if (TA_LATIN_IS_LONG_BLUE(bs))
392 /* If this flag is set, we have an additional constraint to */
393 /* get the blue zone distance: Find a segment of the topmost */
394 /* (or bottommost) contour that is longer than a heuristic */
395 /* threshold. This ensures that small bumps in the outline */
396 /* are ignored (for example, the `vertical serifs' found in */
397 /* many Hebrew glyph designs). */
399 /* If this segment is long enough, we are done. Otherwise, */
400 /* search the segment next to the extremum that is long */
401 /* enough, has the same direction, and a not too large */
402 /* vertical distance from the extremum. Note that the */
403 /* algorithm doesn't check whether the found segment is */
404 /* actually the one (vertically) nearest to the extremum. */
406 /* heuristic threshold value */
407 FT_Pos length_threshold = metrics->units_per_em / 25;
410 dist = TA_ABS(points[best_segment_last].x -
411 points[best_segment_first].x);
413 if (dist < length_threshold
414 && best_segment_last - best_segment_first + 2 <=
415 best_contour_last - best_contour_first)
417 /* heuristic threshold value */
418 FT_Pos height_threshold = metrics->units_per_em / 4;
420 FT_Int first;
421 FT_Int last;
422 FT_Bool hit;
424 FT_Bool left2right;
427 /* compute direction */
428 prev = best_point;
432 if (prev > best_contour_first)
433 prev--;
434 else
435 prev = best_contour_last;
437 if (points[prev].x != best_x)
438 break;
439 } while (prev != best_point);
441 /* skip glyph for the degenerate case */
442 if (prev == best_point)
443 continue;
445 left2right = FT_BOOL(points[prev].x < points[best_point].x);
447 first = best_segment_last;
448 last = first;
449 hit = 0;
453 FT_Bool l2r;
454 FT_Pos d;
455 FT_Int p_first, p_last;
458 if (!hit)
460 /* no hit; adjust first point */
461 first = last;
463 /* also adjust first and last on point */
464 if (FT_CURVE_TAG(outline.tags[first]) == FT_CURVE_TAG_ON)
466 p_first = first;
467 p_last = first;
469 else
471 p_first = -1;
472 p_last = -1;
475 hit = 1;
478 if (last < best_contour_last)
479 last++;
480 else
481 last = best_contour_first;
483 if (TA_ABS(best_y - points[first].y) > height_threshold)
485 /* vertical distance too large */
486 hit = 0;
487 continue;
490 /* same test as above */
491 dist = TA_ABS(points[last].y - points[first].y);
492 if (dist > 5)
493 if (TA_ABS(points[last].x - points[first].x) <= 20 * dist)
495 hit = 0;
496 continue;
499 if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON)
501 p_last = last;
502 if (p_first < 0)
503 p_first = last;
506 l2r = FT_BOOL(points[first].x < points[last].x);
507 d = TA_ABS(points[last].x - points[first].x);
509 if (l2r == left2right
510 && d >= length_threshold)
512 /* all constraints are met; update segment after finding */
513 /* its end */
516 if (last < best_contour_last)
517 last++;
518 else
519 last = best_contour_first;
521 d = TA_ABS(points[last].y - points[first].y);
522 if (d > 5)
523 if (TA_ABS(points[next].x - points[first].x) <=
524 20 * dist)
526 last--;
527 break;
530 p_last = last;
532 if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON)
534 p_last = last;
535 if (p_first < 0)
536 p_first = last;
538 } while (last != best_segment_first);
540 best_y = points[first].y;
542 best_segment_first = first;
543 best_segment_last = last;
545 best_on_point_first = p_first;
546 best_on_point_last = p_last;
548 break;
550 } while (last != best_segment_first);
554 TA_LOG((" U+%04lX: best_y = %5ld", ch, best_y));
557 * now set the `round' flag depending on the segment's kind:
559 * - if the horizontal distance between the first and last
560 * `on' point is larger than upem/8 (value 8 is heuristic)
561 * we have a flat segment
562 * - if either the first or the last point of the segment is
563 * an `off' point, the segment is round, otherwise it is
564 * flat
566 if (best_on_point_first >= 0
567 && best_on_point_last >= 0
568 && (FT_UInt)(TA_ABS(points[best_on_point_last].x
569 - points[best_on_point_first].x))
570 > metrics->units_per_em / 8)
571 round = 0;
572 else
573 round = FT_BOOL(FT_CURVE_TAG(outline.tags[best_segment_first])
574 != FT_CURVE_TAG_ON
575 || FT_CURVE_TAG(outline.tags[best_segment_last])
576 != FT_CURVE_TAG_ON);
578 TA_LOG((" (%s)\n", round ? "round" : "flat"));
581 if (round)
582 rounds[num_rounds++] = best_y;
583 else
584 flats[num_flats++] = best_y;
587 if (num_flats == 0 && num_rounds == 0)
589 /* we couldn't find a single glyph to compute this blue zone, */
590 /* we will simply ignore it then */
591 TA_LOG((" empty\n"));
592 continue;
595 /* we have computed the contents of the `rounds' and `flats' tables, */
596 /* now determine the reference and overshoot position of the blue -- */
597 /* we simply take the median value after a simple sort */
598 ta_sort_pos(num_rounds, rounds);
599 ta_sort_pos(num_flats, flats);
601 blue = &axis->blues[axis->blue_count];
602 blue_ref = &blue->ref.org;
603 blue_shoot = &blue->shoot.org;
605 axis->blue_count++;
607 if (num_flats == 0)
609 *blue_ref =
610 *blue_shoot = rounds[num_rounds / 2];
612 else if (num_rounds == 0)
614 *blue_ref =
615 *blue_shoot = flats[num_flats / 2];
617 else
619 *blue_ref = flats[num_flats / 2];
620 *blue_shoot = rounds[num_rounds / 2];
623 /* there are sometimes problems if the overshoot position of top */
624 /* zones is under its reference position, or the opposite for bottom */
625 /* zones; we must thus check everything there and correct the errors */
626 if (*blue_shoot != *blue_ref)
628 FT_Pos ref = *blue_ref;
629 FT_Pos shoot = *blue_shoot;
630 FT_Bool over_ref = FT_BOOL(shoot > ref);
633 if (TA_LATIN_IS_TOP_BLUE(bs) ^ over_ref)
635 *blue_ref =
636 *blue_shoot = (shoot + ref) / 2;
638 TA_LOG((" [overshoot smaller than reference,"
639 " taking mean value]\n"));
643 blue->flags = 0;
644 if (TA_LATIN_IS_TOP_BLUE(bs))
645 blue->flags |= TA_LATIN_BLUE_TOP;
647 /* the following flag is used later to adjust the y and x scales */
648 /* in order to optimize the pixel grid alignment */
649 /* of the top of small letters */
650 if (TA_LATIN_IS_SMALL_TOP_BLUE(bs))
651 blue->flags |= TA_LATIN_BLUE_ADJUSTMENT;
653 TA_LOG((" -> reference = %ld\n"
654 " overshoot = %ld\n",
655 *blue_ref, *blue_shoot));
658 /* add two blue zones for usWinAscent and usWinDescent */
659 /* just in case the above algorithm has missed them -- */
660 /* Windows cuts off everything outside of those two values */
662 TT_OS2* os2;
665 os2 = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
667 if (os2)
669 blue = &axis->blues[axis->blue_count];
670 blue->flags = TA_LATIN_BLUE_TOP | TA_LATIN_BLUE_ACTIVE;
671 blue->ref.org =
672 blue->shoot.org = os2->usWinAscent;
674 TA_LOG(("artificial blue zone for usWinAscent:\n"
675 " -> reference = %ld\n"
676 " overshoot = %ld\n",
677 blue->ref.org, blue->shoot.org));
679 blue = &axis->blues[axis->blue_count + 1];
680 blue->flags = TA_LATIN_BLUE_ACTIVE;
681 blue->ref.org =
682 blue->shoot.org = -os2->usWinDescent;
684 TA_LOG(("artificial blue zone for usWinDescent:\n"
685 " -> reference = %ld\n"
686 " overshoot = %ld\n",
687 blue->ref.org, blue->shoot.org));
689 else
691 blue = &axis->blues[axis->blue_count];
692 blue->flags =
693 blue->ref.org =
694 blue->shoot.org = 0;
696 blue = &axis->blues[axis->blue_count + 1];
697 blue->flags =
698 blue->ref.org =
699 blue->shoot.org = 0;
703 TA_LOG(("\n"));
705 return;
709 /* check whether all ASCII digits have the same advance width */
711 void
712 ta_latin_metrics_check_digits(TA_LatinMetrics metrics,
713 FT_Face face)
715 FT_UInt i;
716 FT_Bool started = 0, same_width = 1;
717 FT_Fixed advance, old_advance = 0;
720 /* digit `0' is 0x30 in all supported charmaps */
721 for (i = 0x30; i <= 0x39; i++)
723 FT_UInt glyph_index;
726 glyph_index = FT_Get_Char_Index(face, i);
727 if (glyph_index == 0)
728 continue;
730 if (FT_Get_Advance(face, glyph_index,
731 FT_LOAD_NO_SCALE
732 | FT_LOAD_NO_HINTING
733 | FT_LOAD_IGNORE_TRANSFORM,
734 &advance))
735 continue;
737 if (started)
739 if (advance != old_advance)
741 same_width = 0;
742 break;
745 else
747 old_advance = advance;
748 started = 1;
752 metrics->root.digits_have_same_width = same_width;
756 /* initialize global metrics */
758 FT_Error
759 ta_latin_metrics_init(TA_LatinMetrics metrics,
760 FT_Face face)
762 FT_CharMap oldmap = face->charmap;
765 metrics->units_per_em = face->units_per_EM;
767 if (!FT_Select_Charmap(face, FT_ENCODING_UNICODE))
769 ta_latin_metrics_init_widths(metrics, face);
770 ta_latin_metrics_init_blues(metrics, face);
771 ta_latin_metrics_check_digits(metrics, face);
774 FT_Set_Charmap(face, oldmap);
775 return FT_Err_Ok;
779 /* adjust scaling value, then scale and shift widths */
780 /* and blue zones (if applicable) for given dimension */
782 static void
783 ta_latin_metrics_scale_dim(TA_LatinMetrics metrics,
784 TA_Scaler scaler,
785 TA_Dimension dim)
787 FT_Fixed scale;
788 FT_Pos delta;
789 TA_LatinAxis axis;
790 FT_UInt ppem;
791 FT_UInt nn;
794 ppem = metrics->root.scaler.face->size->metrics.x_ppem;
796 if (dim == TA_DIMENSION_HORZ)
798 scale = scaler->x_scale;
799 delta = scaler->x_delta;
801 else
803 scale = scaler->y_scale;
804 delta = scaler->y_delta;
807 axis = &metrics->axis[dim];
809 if (axis->org_scale == scale && axis->org_delta == delta)
810 return;
812 axis->org_scale = scale;
813 axis->org_delta = delta;
815 /* correct X and Y scale to optimize the alignment of the top of */
816 /* small letters to the pixel grid */
817 /* (if we do x-height snapping for this ppem value) */
818 if (!number_set_is_element(
819 metrics->root.globals->font->x_height_snapping_exceptions,
820 ppem))
822 TA_LatinAxis Axis = &metrics->axis[TA_DIMENSION_VERT];
823 TA_LatinBlue blue = NULL;
826 for (nn = 0; nn < Axis->blue_count; nn++)
828 if (Axis->blues[nn].flags & TA_LATIN_BLUE_ADJUSTMENT)
830 blue = &Axis->blues[nn];
831 break;
835 if (blue)
837 FT_Pos scaled;
838 FT_Pos threshold;
839 FT_Pos fitted;
840 FT_UInt limit;
843 scaled = FT_MulFix(blue->shoot.org, scaler->y_scale);
844 limit = metrics->root.globals->increase_x_height;
845 threshold = 40;
847 /* if the `increase-x-height' property is active, */
848 /* we round up much more often */
849 if (limit
850 && ppem <= limit
851 && ppem >= TA_PROP_INCREASE_X_HEIGHT_MIN)
852 threshold = 52;
854 fitted = (scaled + threshold) & ~63;
856 if (scaled != fitted)
858 if (dim == TA_DIMENSION_VERT)
859 scale = FT_MulDiv(scale, fitted, scaled);
864 axis->scale = scale;
865 axis->delta = delta;
867 if (dim == TA_DIMENSION_HORZ)
869 metrics->root.scaler.x_scale = scale;
870 metrics->root.scaler.x_delta = delta;
872 else
874 metrics->root.scaler.y_scale = scale;
875 metrics->root.scaler.y_delta = delta;
878 /* scale the widths */
879 for (nn = 0; nn < axis->width_count; nn++)
881 TA_Width width = axis->widths + nn;
884 width->cur = FT_MulFix(width->org, scale);
885 width->fit = width->cur;
888 /* an extra-light axis corresponds to a standard width that is */
889 /* smaller than 5/8 pixels */
890 axis->extra_light =
891 (FT_Bool)(FT_MulFix(axis->standard_width, scale) < 32 + 8);
893 if (dim == TA_DIMENSION_VERT)
895 /* scale the blue zones */
896 for (nn = 0; nn < axis->blue_count; nn++)
898 TA_LatinBlue blue = &axis->blues[nn];
899 FT_Pos dist;
902 blue->ref.cur = FT_MulFix(blue->ref.org, scale) + delta;
903 blue->ref.fit = blue->ref.cur;
904 blue->shoot.cur = FT_MulFix(blue->shoot.org, scale) + delta;
905 blue->shoot.fit = blue->shoot.cur;
906 blue->flags &= ~TA_LATIN_BLUE_ACTIVE;
908 /* a blue zone is only active if it is less than 3/4 pixels tall */
909 dist = FT_MulFix(blue->ref.org - blue->shoot.org, scale);
910 if (dist <= 48 && dist >= -48)
912 #if 0
913 FT_Pos delta1;
914 #endif
915 FT_Pos delta2;
918 /* use discrete values for blue zone widths */
920 #if 0
921 /* generic, original code */
922 delta1 = blue->shoot.org - blue->ref.org;
923 delta2 = delta1;
924 if (delta1 < 0)
925 delta2 = -delta2;
927 delta2 = FT_MulFix(delta2, scale);
929 if (delta2 < 32)
930 delta2 = 0;
931 else if (delta2 < 64)
932 delta2 = 32 + (((delta2 - 32) + 16) & ~31);
933 else
934 delta2 = TA_PIX_ROUND(delta2);
936 if (delta1 < 0)
937 delta2 = -delta2;
939 blue->ref.fit = TA_PIX_ROUND(blue->ref.cur);
940 blue->shoot.fit = blue->ref.fit + delta2;
941 #else
942 /* simplified version due to abs(dist) <= 48 */
943 delta2 = dist;
944 if (dist < 0)
945 delta2 = -delta2;
947 if (delta2 < 32)
948 delta2 = 0;
949 else if (delta2 < 48)
950 delta2 = 32;
951 else
952 delta2 = 64;
954 if (dist < 0)
955 delta2 = -delta2;
957 blue->ref.fit = TA_PIX_ROUND(blue->ref.cur);
958 blue->shoot.fit = blue->ref.fit - delta2;
959 #endif
961 blue->flags |= TA_LATIN_BLUE_ACTIVE;
965 /* the last two artificial blue zones are to be scaled */
966 /* with uncorrected scaling values */
968 TA_LatinAxis a = &metrics->axis[TA_DIMENSION_VERT];
969 TA_LatinBlue b;
972 b = &a->blues[a->blue_count];
973 b->ref.cur =
974 b->ref.fit =
975 b->shoot.cur =
976 b->shoot.fit = FT_MulFix(b->ref.org, a->org_scale) + delta;
978 b = &a->blues[a->blue_count + 1];
979 b->ref.cur =
980 b->ref.fit =
981 b->shoot.cur =
982 b->shoot.fit = FT_MulFix(b->ref.org, a->org_scale) + delta;
988 /* scale global values in both directions */
990 void
991 ta_latin_metrics_scale(TA_LatinMetrics metrics,
992 TA_Scaler scaler)
994 metrics->root.scaler.render_mode = scaler->render_mode;
995 metrics->root.scaler.face = scaler->face;
996 metrics->root.scaler.flags = scaler->flags;
998 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_HORZ);
999 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_VERT);
1003 /* walk over all contours and compute its segments */
1005 FT_Error
1006 ta_latin_hints_compute_segments(TA_GlyphHints hints,
1007 TA_Dimension dim)
1009 TA_AxisHints axis = &hints->axis[dim];
1010 FT_Error error = FT_Err_Ok;
1012 TA_Segment segment = NULL;
1013 TA_SegmentRec seg0;
1015 TA_Point* contour = hints->contours;
1016 TA_Point* contour_limit = contour + hints->num_contours;
1017 TA_Direction major_dir, segment_dir;
1020 memset(&seg0, 0, sizeof (TA_SegmentRec));
1021 seg0.score = 32000;
1022 seg0.flags = TA_EDGE_NORMAL;
1024 major_dir = (TA_Direction)TA_ABS(axis->major_dir);
1025 segment_dir = major_dir;
1027 axis->num_segments = 0;
1029 /* set up (u,v) in each point */
1030 if (dim == TA_DIMENSION_HORZ)
1032 TA_Point point = hints->points;
1033 TA_Point limit = point + hints->num_points;
1036 for (; point < limit; point++)
1038 point->u = point->fx;
1039 point->v = point->fy;
1042 else
1044 TA_Point point = hints->points;
1045 TA_Point limit = point + hints->num_points;
1048 for (; point < limit; point++)
1050 point->u = point->fy;
1051 point->v = point->fx;
1055 /* do each contour separately */
1056 for (; contour < contour_limit; contour++)
1058 TA_Point point = contour[0];
1059 TA_Point last = point->prev;
1061 int on_edge = 0;
1063 FT_Pos min_pos = 32000; /* minimum segment pos != min_coord */
1064 FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */
1065 FT_Bool passed;
1068 if (point == last) /* skip singletons -- just in case */
1069 continue;
1071 if (TA_ABS(last->out_dir) == major_dir
1072 && TA_ABS(point->out_dir) == major_dir)
1074 /* we are already on an edge, try to locate its start */
1075 last = point;
1077 for (;;)
1079 point = point->prev;
1080 if (TA_ABS(point->out_dir) != major_dir)
1082 point = point->next;
1083 break;
1085 if (point == last)
1086 break;
1090 last = point;
1091 passed = 0;
1093 for (;;)
1095 FT_Pos u, v;
1098 if (on_edge)
1100 u = point->u;
1101 if (u < min_pos)
1102 min_pos = u;
1103 if (u > max_pos)
1104 max_pos = u;
1106 if (point->out_dir != segment_dir
1107 || point == last)
1109 /* we are just leaving an edge; record a new segment! */
1110 segment->last = point;
1111 segment->pos = (FT_Short)((min_pos + max_pos) >> 1);
1113 /* a segment is round if either its first or last point */
1114 /* is a control point */
1115 if ((segment->first->flags | point->flags) & TA_FLAG_CONTROL)
1116 segment->flags |= TA_EDGE_ROUND;
1118 /* compute segment size */
1119 min_pos = max_pos = point->v;
1121 v = segment->first->v;
1122 if (v < min_pos)
1123 min_pos = v;
1124 if (v > max_pos)
1125 max_pos = v;
1127 segment->min_coord = (FT_Short)min_pos;
1128 segment->max_coord = (FT_Short)max_pos;
1129 segment->height = (FT_Short)(segment->max_coord -
1130 segment->min_coord);
1132 on_edge = 0;
1133 segment = NULL;
1134 /* fall through */
1138 /* now exit if we are at the start/end point */
1139 if (point == last)
1141 if (passed)
1142 break;
1143 passed = 1;
1146 if (!on_edge
1147 && TA_ABS(point->out_dir) == major_dir)
1149 /* this is the start of a new segment! */
1150 segment_dir = (TA_Direction)point->out_dir;
1152 /* clear all segment fields */
1153 error = ta_axis_hints_new_segment(axis, &segment);
1154 if (error)
1155 goto Exit;
1157 segment[0] = seg0;
1158 segment->dir = (FT_Char)segment_dir;
1159 min_pos = max_pos = point->u;
1160 segment->first = point;
1161 segment->last = point;
1162 on_edge = 1;
1165 point = point->next;
1167 } /* contours */
1170 /* now slightly increase the height of segments if this makes sense -- */
1171 /* this is used to better detect and ignore serifs */
1173 TA_Segment segments = axis->segments;
1174 TA_Segment segments_end = segments + axis->num_segments;
1177 for (segment = segments; segment < segments_end; segment++)
1179 TA_Point first = segment->first;
1180 TA_Point last = segment->last;
1182 FT_Pos first_v = first->v;
1183 FT_Pos last_v = last->v;
1186 if (first == last)
1187 continue;
1189 if (first_v < last_v)
1191 TA_Point p;
1194 p = first->prev;
1195 if (p->v < first_v)
1196 segment->height = (FT_Short)(segment->height +
1197 ((first_v - p->v) >> 1));
1199 p = last->next;
1200 if (p->v > last_v)
1201 segment->height = (FT_Short)(segment->height +
1202 ((p->v - last_v) >> 1));
1204 else
1206 TA_Point p;
1209 p = first->prev;
1210 if (p->v > first_v)
1211 segment->height = (FT_Short)(segment->height +
1212 ((p->v - first_v) >> 1));
1214 p = last->next;
1215 if (p->v < last_v)
1216 segment->height = (FT_Short)(segment->height +
1217 ((last_v - p->v) >> 1));
1222 Exit:
1223 return error;
1227 /* link segments to form stems and serifs */
1229 void
1230 ta_latin_hints_link_segments(TA_GlyphHints hints,
1231 TA_Dimension dim)
1233 TA_AxisHints axis = &hints->axis[dim];
1235 TA_Segment segments = axis->segments;
1236 TA_Segment segment_limit = segments + axis->num_segments;
1238 FT_Pos len_threshold, len_score;
1239 TA_Segment seg1, seg2;
1242 len_threshold = TA_LATIN_CONSTANT(hints->metrics, 8);
1243 if (len_threshold == 0)
1244 len_threshold = 1;
1246 len_score = TA_LATIN_CONSTANT(hints->metrics, 6000);
1248 /* now compare each segment to the others */
1249 for (seg1 = segments; seg1 < segment_limit; seg1++)
1251 /* the fake segments are introduced to hint the metrics -- */
1252 /* we must never link them to anything */
1253 if (seg1->dir != axis->major_dir
1254 || seg1->first == seg1->last)
1255 continue;
1257 /* search for stems having opposite directions, */
1258 /* with seg1 to the `left' of seg2 */
1259 for (seg2 = segments; seg2 < segment_limit; seg2++)
1261 FT_Pos pos1 = seg1->pos;
1262 FT_Pos pos2 = seg2->pos;
1265 if (seg1->dir + seg2->dir == 0
1266 && pos2 > pos1)
1268 /* compute distance between the two segments */
1269 FT_Pos dist = pos2 - pos1;
1270 FT_Pos min = seg1->min_coord;
1271 FT_Pos max = seg1->max_coord;
1272 FT_Pos len, score;
1275 if (min < seg2->min_coord)
1276 min = seg2->min_coord;
1277 if (max > seg2->max_coord)
1278 max = seg2->max_coord;
1280 /* compute maximum coordinate difference of the two segments */
1281 len = max - min;
1282 if (len >= len_threshold)
1284 /* small coordinate differences cause a higher score, and */
1285 /* segments with a greater distance cause a higher score also */
1286 score = dist + len_score / len;
1288 /* and we search for the smallest score */
1289 /* of the sum of the two values */
1290 if (score < seg1->score)
1292 seg1->score = score;
1293 seg1->link = seg2;
1296 if (score < seg2->score)
1298 seg2->score = score;
1299 seg2->link = seg1;
1306 /* now compute the `serif' segments, cf. explanations in `tahints.h' */
1307 for (seg1 = segments; seg1 < segment_limit; seg1++)
1309 seg2 = seg1->link;
1311 if (seg2)
1313 if (seg2->link != seg1)
1315 seg1->link = 0;
1316 seg1->serif = seg2->link;
1323 /* link segments to edges, using feature analysis for selection */
1325 FT_Error
1326 ta_latin_hints_compute_edges(TA_GlyphHints hints,
1327 TA_Dimension dim)
1329 TA_AxisHints axis = &hints->axis[dim];
1330 FT_Error error = FT_Err_Ok;
1331 TA_LatinAxis laxis = &((TA_LatinMetrics)hints->metrics)->axis[dim];
1333 TA_Segment segments = axis->segments;
1334 TA_Segment segment_limit = segments + axis->num_segments;
1335 TA_Segment seg;
1337 #if 0
1338 TA_Direction up_dir;
1339 #endif
1340 FT_Fixed scale;
1341 FT_Pos edge_distance_threshold;
1342 FT_Pos segment_length_threshold;
1345 axis->num_edges = 0;
1347 scale = (dim == TA_DIMENSION_HORZ) ? hints->x_scale
1348 : hints->y_scale;
1350 #if 0
1351 up_dir = (dim == TA_DIMENSION_HORZ) ? TA_DIR_UP
1352 : TA_DIR_RIGHT;
1353 #endif
1355 /* we ignore all segments that are less than 1 pixel in length */
1356 /* to avoid many problems with serif fonts */
1357 /* (the corresponding threshold is computed in font units) */
1358 if (dim == TA_DIMENSION_HORZ)
1359 segment_length_threshold = FT_DivFix(64, hints->y_scale);
1360 else
1361 segment_length_threshold = 0;
1363 /********************************************************************/
1364 /* */
1365 /* We begin by generating a sorted table of edges for the current */
1366 /* direction. To do so, we simply scan each segment and try to find */
1367 /* an edge in our table that corresponds to its position. */
1368 /* */
1369 /* If no edge is found, we create and insert a new edge in the */
1370 /* sorted table. Otherwise, we simply add the segment to the edge's */
1371 /* list which gets processed in the second step to compute the */
1372 /* edge's properties. */
1373 /* */
1374 /* Note that the table of edges is sorted along the segment/edge */
1375 /* position. */
1376 /* */
1377 /********************************************************************/
1379 /* assure that edge distance threshold is at most 0.25px */
1380 edge_distance_threshold = FT_MulFix(laxis->edge_distance_threshold,
1381 scale);
1382 if (edge_distance_threshold > 64 / 4)
1383 edge_distance_threshold = 64 / 4;
1385 edge_distance_threshold = FT_DivFix(edge_distance_threshold,
1386 scale);
1388 for (seg = segments; seg < segment_limit; seg++)
1390 TA_Edge found = NULL;
1391 FT_Int ee;
1394 if (seg->height < segment_length_threshold)
1395 continue;
1397 /* a special case for serif edges: */
1398 /* if they are smaller than 1.5 pixels we ignore them */
1399 if (seg->serif
1400 && 2 * seg->height < 3 * segment_length_threshold)
1401 continue;
1403 /* look for an edge corresponding to the segment */
1404 for (ee = 0; ee < axis->num_edges; ee++)
1406 TA_Edge edge = axis->edges + ee;
1407 FT_Pos dist;
1410 dist = seg->pos - edge->fpos;
1411 if (dist < 0)
1412 dist = -dist;
1414 if (dist < edge_distance_threshold && edge->dir == seg->dir)
1416 found = edge;
1417 break;
1421 if (!found)
1423 TA_Edge edge;
1426 /* insert a new edge in the list and sort according to the position */
1427 error = ta_axis_hints_new_edge(axis, seg->pos,
1428 (TA_Direction)seg->dir,
1429 &edge);
1430 if (error)
1431 goto Exit;
1433 /* add the segment to the new edge's list */
1434 memset(edge, 0, sizeof (TA_EdgeRec));
1435 edge->first = seg;
1436 edge->last = seg;
1437 edge->dir = seg->dir;
1438 edge->fpos = seg->pos;
1439 edge->opos = FT_MulFix(seg->pos, scale);
1440 edge->pos = edge->opos;
1441 seg->edge_next = seg;
1443 else
1445 /* if an edge was found, simply add the segment to the edge's list */
1446 seg->edge_next = found->first;
1447 found->last->edge_next = seg;
1448 found->last = seg;
1452 /*****************************************************************/
1453 /* */
1454 /* Good, we now compute each edge's properties according to */
1455 /* the segments found on its position. Basically, these are */
1456 /* */
1457 /* - the edge's main direction */
1458 /* - stem edge, serif edge or both (which defaults to stem then) */
1459 /* - rounded edge, straight or both (which defaults to straight) */
1460 /* - link for edge */
1461 /* */
1462 /*****************************************************************/
1464 /* first of all, set the `edge' field in each segment -- this is */
1465 /* required in order to compute edge links */
1467 /* note that removing this loop and setting the `edge' field of each */
1468 /* segment directly in the code above slows down execution speed for */
1469 /* some reasons on platforms like the Sun */
1471 TA_Edge edges = axis->edges;
1472 TA_Edge edge_limit = edges + axis->num_edges;
1473 TA_Edge edge;
1476 for (edge = edges; edge < edge_limit; edge++)
1478 seg = edge->first;
1479 if (seg)
1482 seg->edge = edge;
1483 seg = seg->edge_next;
1484 } while (seg != edge->first);
1487 /* now compute each edge properties */
1488 for (edge = edges; edge < edge_limit; edge++)
1490 FT_Int is_round = 0; /* does it contain round segments? */
1491 FT_Int is_straight = 0; /* does it contain straight segments? */
1492 #if 0
1493 FT_Pos ups = 0; /* number of upwards segments */
1494 FT_Pos downs = 0; /* number of downwards segments */
1495 #endif
1498 seg = edge->first;
1502 FT_Bool is_serif;
1505 /* check for roundness of segment */
1506 if (seg->flags & TA_EDGE_ROUND)
1507 is_round++;
1508 else
1509 is_straight++;
1511 #if 0
1512 /* check for segment direction */
1513 if (seg->dir == up_dir)
1514 ups += seg->max_coord - seg->min_coord;
1515 else
1516 downs += seg->max_coord - seg->min_coord;
1517 #endif
1519 /* check for links -- */
1520 /* if seg->serif is set, then seg->link must be ignored */
1521 is_serif = (FT_Bool)(seg->serif
1522 && seg->serif->edge
1523 && seg->serif->edge != edge);
1525 if ((seg->link && seg->link->edge != NULL)
1526 || is_serif)
1528 TA_Edge edge2;
1529 TA_Segment seg2;
1532 edge2 = edge->link;
1533 seg2 = seg->link;
1535 if (is_serif)
1537 seg2 = seg->serif;
1538 edge2 = edge->serif;
1541 if (edge2)
1543 FT_Pos edge_delta;
1544 FT_Pos seg_delta;
1547 edge_delta = edge->fpos - edge2->fpos;
1548 if (edge_delta < 0)
1549 edge_delta = -edge_delta;
1551 seg_delta = seg->pos - seg2->pos;
1552 if (seg_delta < 0)
1553 seg_delta = -seg_delta;
1555 if (seg_delta < edge_delta)
1556 edge2 = seg2->edge;
1558 else
1559 edge2 = seg2->edge;
1561 if (is_serif)
1563 edge->serif = edge2;
1564 edge2->flags |= TA_EDGE_SERIF;
1566 else
1567 edge->link = edge2;
1570 seg = seg->edge_next;
1571 } while (seg != edge->first);
1573 /* set the round/straight flags */
1574 edge->flags = TA_EDGE_NORMAL;
1576 if (is_round > 0
1577 && is_round >= is_straight)
1578 edge->flags |= TA_EDGE_ROUND;
1580 #if 0
1581 /* set the edge's main direction */
1582 edge->dir = TA_DIR_NONE;
1584 if (ups > downs)
1585 edge->dir = (FT_Char)up_dir;
1587 else if (ups < downs)
1588 edge->dir = (FT_Char)-up_dir;
1590 else if (ups == downs)
1591 edge->dir = 0; /* both up and down! */
1592 #endif
1594 /* get rid of serifs if link is set */
1595 /* XXX: this gets rid of many unpleasant artefacts! */
1596 /* example: the `c' in cour.pfa at size 13 */
1598 if (edge->serif && edge->link)
1599 edge->serif = 0;
1603 Exit:
1604 return error;
1608 /* detect segments and edges for given dimension */
1610 FT_Error
1611 ta_latin_hints_detect_features(TA_GlyphHints hints,
1612 TA_Dimension dim)
1614 FT_Error error;
1617 error = ta_latin_hints_compute_segments(hints, dim);
1618 if (!error)
1620 ta_latin_hints_link_segments(hints, dim);
1622 error = ta_latin_hints_compute_edges(hints, dim);
1625 return error;
1629 /* compute all edges which lie within blue zones */
1631 void
1632 ta_latin_hints_compute_blue_edges(TA_GlyphHints hints,
1633 TA_LatinMetrics metrics)
1635 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
1637 TA_Edge edge = axis->edges;
1638 TA_Edge edge_limit = edge + axis->num_edges;
1640 TA_LatinAxis latin = &metrics->axis[TA_DIMENSION_VERT];
1641 FT_Fixed scale = latin->scale;
1644 /* compute which blue zones are active, */
1645 /* i.e. have their scaled size < 3/4 pixels */
1647 /* for each horizontal edge search the blue zone which is closest */
1648 for (; edge < edge_limit; edge++)
1650 FT_UInt bb;
1651 TA_Width best_blue = NULL;
1652 FT_Pos best_dist; /* initial threshold */
1654 FT_UInt best_blue_idx = 0;
1655 FT_Bool best_blue_is_shoot = 0;
1658 /* compute the initial threshold as a fraction of the EM size */
1659 /* (the value 40 is heuristic) */
1660 best_dist = FT_MulFix(metrics->units_per_em / 40, scale);
1662 /* assure a minimum distance of 0.5px */
1663 if (best_dist > 64 / 2)
1664 best_dist = 64 / 2;
1666 /* this loop also handles the two extra blue zones */
1667 /* for usWinAscent and usWinDescent */
1668 /* if option `windows-compatibility' is set */
1669 for (bb = 0;
1670 bb < latin->blue_count
1671 + (metrics->root.globals->font->windows_compatibility ? 2 : 0);
1672 bb++)
1674 TA_LatinBlue blue = latin->blues + bb;
1675 FT_Bool is_top_blue, is_major_dir;
1678 /* skip inactive blue zones (i.e., those that are too large) */
1679 if (!(blue->flags & TA_LATIN_BLUE_ACTIVE))
1680 continue;
1682 /* if it is a top zone, check for right edges -- */
1683 /* if it is a bottom zone, check for left edges */
1684 is_top_blue = (FT_Byte)((blue->flags & TA_LATIN_BLUE_TOP) != 0);
1685 is_major_dir = FT_BOOL(edge->dir == axis->major_dir);
1687 /* if it is a top zone, the edge must be against the major */
1688 /* direction; if it is a bottom zone, it must be in the major */
1689 /* direction */
1690 if (is_top_blue ^ is_major_dir)
1692 FT_Pos dist;
1695 /* first of all, compare it to the reference position */
1696 dist = edge->fpos - blue->ref.org;
1697 if (dist < 0)
1698 dist = -dist;
1700 dist = FT_MulFix(dist, scale);
1701 if (dist < best_dist)
1703 best_dist = dist;
1704 best_blue = &blue->ref;
1706 best_blue_idx = bb;
1707 best_blue_is_shoot = 0;
1710 /* now compare it to the overshoot position and check whether */
1711 /* the edge is rounded, and whether the edge is over the */
1712 /* reference position of a top zone, or under the reference */
1713 /* position of a bottom zone */
1714 if (edge->flags & TA_EDGE_ROUND
1715 && dist != 0)
1717 FT_Bool is_under_ref = FT_BOOL(edge->fpos < blue->ref.org);
1720 if (is_top_blue ^ is_under_ref)
1722 dist = edge->fpos - blue->shoot.org;
1723 if (dist < 0)
1724 dist = -dist;
1726 dist = FT_MulFix(dist, scale);
1727 if (dist < best_dist)
1729 best_dist = dist;
1730 best_blue = &blue->shoot;
1732 best_blue_idx = bb;
1733 best_blue_is_shoot = 1;
1740 if (best_blue)
1742 edge->blue_edge = best_blue;
1743 edge->best_blue_idx = best_blue_idx;
1744 edge->best_blue_is_shoot = best_blue_is_shoot;
1750 /* initalize hinting engine */
1752 static FT_Error
1753 ta_latin_hints_init(TA_GlyphHints hints,
1754 TA_LatinMetrics metrics)
1756 FT_Render_Mode mode;
1757 FT_UInt32 scaler_flags, other_flags;
1758 FT_Face face = metrics->root.scaler.face;
1761 ta_glyph_hints_rescale(hints, (TA_ScriptMetrics)metrics);
1763 /* correct x_scale and y_scale if needed, since they may have */
1764 /* been modified by `ta_latin_metrics_scale_dim' above */
1765 hints->x_scale = metrics->axis[TA_DIMENSION_HORZ].scale;
1766 hints->x_delta = metrics->axis[TA_DIMENSION_HORZ].delta;
1767 hints->y_scale = metrics->axis[TA_DIMENSION_VERT].scale;
1768 hints->y_delta = metrics->axis[TA_DIMENSION_VERT].delta;
1770 /* compute flags depending on render mode, etc. */
1771 mode = metrics->root.scaler.render_mode;
1773 #if 0 /* #ifdef TA_CONFIG_OPTION_USE_WARPER */
1774 if (mode == FT_RENDER_MODE_LCD
1775 || mode == FT_RENDER_MODE_LCD_V)
1776 metrics->root.scaler.render_mode =
1777 mode = FT_RENDER_MODE_NORMAL;
1778 #endif
1780 scaler_flags = hints->scaler_flags;
1781 other_flags = 0;
1783 /* we snap the width of vertical stems for the monochrome */
1784 /* and horizontal LCD rendering targets only */
1785 if (mode == FT_RENDER_MODE_MONO
1786 || mode == FT_RENDER_MODE_LCD)
1787 other_flags |= TA_LATIN_HINTS_HORZ_SNAP;
1789 /* we snap the width of horizontal stems for the monochrome */
1790 /* and vertical LCD rendering targets only */
1791 if (mode == FT_RENDER_MODE_MONO
1792 || mode == FT_RENDER_MODE_LCD_V)
1793 other_flags |= TA_LATIN_HINTS_VERT_SNAP;
1795 /* we adjust stems to full pixels only if we don't use the `light' mode */
1796 if (mode != FT_RENDER_MODE_LIGHT)
1797 other_flags |= TA_LATIN_HINTS_STEM_ADJUST;
1799 if (mode == FT_RENDER_MODE_MONO)
1800 other_flags |= TA_LATIN_HINTS_MONO;
1802 /* in `light' hinting mode we disable horizontal hinting completely; */
1803 /* we also do it if the face is italic */
1804 if (mode == FT_RENDER_MODE_LIGHT
1805 || (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0)
1806 scaler_flags |= TA_SCALER_FLAG_NO_HORIZONTAL;
1808 hints->scaler_flags = scaler_flags;
1809 hints->other_flags = other_flags;
1811 return FT_Err_Ok;
1815 /* snap a given width in scaled coordinates to */
1816 /* one of the current standard widths */
1818 static FT_Pos
1819 ta_latin_snap_width(TA_Width widths,
1820 FT_Int count,
1821 FT_Pos width)
1823 int n;
1824 FT_Pos best = 64 + 32 + 2;
1825 FT_Pos reference = width;
1826 FT_Pos scaled;
1829 for (n = 0; n < count; n++)
1831 FT_Pos w;
1832 FT_Pos dist;
1835 w = widths[n].cur;
1836 dist = width - w;
1837 if (dist < 0)
1838 dist = -dist;
1839 if (dist < best)
1841 best = dist;
1842 reference = w;
1846 scaled = TA_PIX_ROUND(reference);
1848 if (width >= reference)
1850 if (width < scaled + 48)
1851 width = reference;
1853 else
1855 if (width > scaled - 48)
1856 width = reference;
1859 return width;
1863 /* compute the snapped width of a given stem, ignoring very thin ones */
1865 /* there is a lot of voodoo in this function; changing the hard-coded */
1866 /* parameters influence the whole hinting process */
1868 static FT_Pos
1869 ta_latin_compute_stem_width(TA_GlyphHints hints,
1870 TA_Dimension dim,
1871 FT_Pos width,
1872 FT_Byte base_flags,
1873 FT_Byte stem_flags)
1875 TA_LatinMetrics metrics = (TA_LatinMetrics) hints->metrics;
1876 TA_LatinAxis axis = &metrics->axis[dim];
1878 FT_Pos dist = width;
1879 FT_Int sign = 0;
1880 FT_Int vertical = (dim == TA_DIMENSION_VERT);
1883 if (!TA_LATIN_HINTS_DO_STEM_ADJUST(hints)
1884 || axis->extra_light)
1885 return width;
1887 if (dist < 0)
1889 dist = -width;
1890 sign = 1;
1893 if ((vertical && !TA_LATIN_HINTS_DO_VERT_SNAP(hints))
1894 || (!vertical && !TA_LATIN_HINTS_DO_HORZ_SNAP(hints)))
1896 /* smooth hinting process: very lightly quantize the stem width */
1898 /* leave the widths of serifs alone */
1899 if ((stem_flags & TA_EDGE_SERIF)
1900 && vertical
1901 && (dist < 3 * 64))
1902 goto Done_Width;
1903 else if (base_flags & TA_EDGE_ROUND)
1905 if (dist < 80)
1906 dist = 64;
1908 else if (dist < 56)
1909 dist = 56;
1911 if (axis->width_count > 0)
1913 FT_Pos delta;
1916 /* compare to standard width */
1917 delta = dist - axis->widths[0].cur;
1919 if (delta < 0)
1920 delta = -delta;
1922 if (delta < 40)
1924 dist = axis->widths[0].cur;
1925 if (dist < 48)
1926 dist = 48;
1928 goto Done_Width;
1931 if (dist < 3 * 64)
1933 delta = dist & 63;
1934 dist &= -64;
1936 if (delta < 10)
1937 dist += delta;
1938 else if (delta < 32)
1939 dist += 10;
1940 else if (delta < 54)
1941 dist += 54;
1942 else
1943 dist += delta;
1945 else
1946 dist = (dist + 32) & ~63;
1949 else
1951 /* strong hinting process: snap the stem width to integer pixels */
1953 FT_Pos org_dist = dist;
1956 dist = ta_latin_snap_width(axis->widths, axis->width_count, dist);
1958 if (vertical)
1960 /* in the case of vertical hinting, */
1961 /* always round the stem heights to integer pixels */
1963 if (dist >= 64)
1964 dist = (dist + 16) & ~63;
1965 else
1966 dist = 64;
1968 else
1970 if (TA_LATIN_HINTS_DO_MONO(hints))
1972 /* monochrome horizontal hinting: */
1973 /* snap widths to integer pixels with a different threshold */
1975 if (dist < 64)
1976 dist = 64;
1977 else
1978 dist = (dist + 32) & ~63;
1980 else
1982 /* for horizontal anti-aliased hinting, we adopt a more subtle */
1983 /* approach: we strengthen small stems, round stems whose size */
1984 /* is between 1 and 2 pixels to an integer, otherwise nothing */
1986 if (dist < 48)
1987 dist = (dist + 64) >> 1;
1989 else if (dist < 128)
1991 /* we only round to an integer width if the corresponding */
1992 /* distortion is less than 1/4 pixel -- otherwise, this */
1993 /* makes everything worse since the diagonals, which are */
1994 /* not hinted, appear a lot bolder or thinner than the */
1995 /* vertical stems */
1997 FT_Pos delta;
2000 dist = (dist + 22) & ~63;
2001 delta = dist - org_dist;
2002 if (delta < 0)
2003 delta = -delta;
2005 if (delta >= 16)
2007 dist = org_dist;
2008 if (dist < 48)
2009 dist = (dist + 64) >> 1;
2012 else
2013 /* round otherwise to prevent color fringes in LCD mode */
2014 dist = (dist + 32) & ~63;
2019 Done_Width:
2020 if (sign)
2021 dist = -dist;
2023 return dist;
2027 /* align one stem edge relative to the previous stem edge */
2029 static void
2030 ta_latin_align_linked_edge(TA_GlyphHints hints,
2031 TA_Dimension dim,
2032 TA_Edge base_edge,
2033 TA_Edge stem_edge)
2035 FT_Pos dist = stem_edge->opos - base_edge->opos;
2037 FT_Pos fitted_width = ta_latin_compute_stem_width(
2038 hints, dim, dist,
2039 base_edge->flags,
2040 stem_edge->flags);
2043 stem_edge->pos = base_edge->pos + fitted_width;
2045 TA_LOG((" LINK: edge %d (opos=%.2f) linked to %.2f,"
2046 " dist was %.2f, now %.2f\n",
2047 stem_edge - hints->axis[dim].edges, stem_edge->opos / 64.0,
2048 stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0));
2050 if (hints->recorder)
2051 hints->recorder(ta_link, hints, dim,
2052 base_edge, stem_edge, NULL, NULL, NULL);
2056 /* shift the coordinates of the `serif' edge by the same amount */
2057 /* as the corresponding `base' edge has been moved already */
2059 static void
2060 ta_latin_align_serif_edge(TA_GlyphHints hints,
2061 TA_Edge base,
2062 TA_Edge serif)
2064 FT_UNUSED(hints);
2066 serif->pos = base->pos + (serif->opos - base->opos);
2070 /* the main grid-fitting routine */
2072 void
2073 ta_latin_hint_edges(TA_GlyphHints hints,
2074 TA_Dimension dim)
2076 TA_AxisHints axis = &hints->axis[dim];
2078 TA_Edge edges = axis->edges;
2079 TA_Edge edge_limit = edges + axis->num_edges;
2080 FT_PtrDist n_edges;
2081 TA_Edge edge;
2083 TA_Edge anchor = NULL;
2084 FT_Int has_serifs = 0;
2086 #ifdef TA_DEBUG
2087 FT_UInt num_actions = 0;
2088 #endif
2090 TA_LOG(("latin %s edge hinting (script `%s')\n",
2091 dim == TA_DIMENSION_VERT ? "horizontal" : "vertical",
2092 ta_script_names[hints->metrics->script_class->script]));
2094 /* we begin by aligning all stems relative to the blue zone if needed -- */
2095 /* that's only for horizontal edges */
2097 if (dim == TA_DIMENSION_VERT
2098 && TA_HINTS_DO_BLUES(hints))
2100 for (edge = edges; edge < edge_limit; edge++)
2102 TA_Width blue;
2103 TA_Edge edge1, edge2; /* these edges form the stem to check */
2106 if (edge->flags & TA_EDGE_DONE)
2107 continue;
2109 blue = edge->blue_edge;
2110 edge1 = NULL;
2111 edge2 = edge->link;
2113 if (blue)
2114 edge1 = edge;
2116 /* flip edges if the other stem is aligned to a blue zone */
2117 else if (edge2 && edge2->blue_edge)
2119 blue = edge2->blue_edge;
2120 edge1 = edge2;
2121 edge2 = edge;
2124 if (!edge1)
2125 continue;
2127 #ifdef TA_DEBUG
2128 if (!anchor)
2129 TA_LOG((" BLUE_ANCHOR: edge %d (opos=%.2f) snapped to %.2f,"
2130 " was %.2f (anchor=edge %d)\n",
2131 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
2132 edge1->pos / 64.0, edge - edges));
2133 else
2134 TA_LOG((" BLUE: edge %d (opos=%.2f) snapped to %.2f, was %.2f\n",
2135 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
2136 edge1->pos / 64.0));
2138 num_actions++;
2139 #endif
2141 edge1->pos = blue->fit;
2142 edge1->flags |= TA_EDGE_DONE;
2144 if (hints->recorder)
2146 if (!anchor)
2147 hints->recorder(ta_blue_anchor, hints, dim,
2148 edge1, edge, NULL, NULL, NULL);
2149 else
2150 hints->recorder(ta_blue, hints, dim,
2151 edge1, NULL, NULL, NULL, NULL);
2154 if (edge2 && !edge2->blue_edge)
2156 ta_latin_align_linked_edge(hints, dim, edge1, edge2);
2157 edge2->flags |= TA_EDGE_DONE;
2159 #ifdef TA_DEBUG
2160 num_actions++;
2161 #endif
2164 if (!anchor)
2165 anchor = edge;
2169 /* now we align all other stem edges, */
2170 /* trying to maintain the relative order of stems in the glyph */
2171 for (edge = edges; edge < edge_limit; edge++)
2173 TA_Edge edge2;
2176 if (edge->flags & TA_EDGE_DONE)
2177 continue;
2179 /* skip all non-stem edges */
2180 edge2 = edge->link;
2181 if (!edge2)
2183 has_serifs++;
2184 continue;
2187 /* now align the stem */
2189 /* this should not happen, but it's better to be safe */
2190 if (edge2->blue_edge)
2192 TA_LOG((" ASSERTION FAILED for edge %d\n", edge2-edges));
2194 ta_latin_align_linked_edge(hints, dim, edge2, edge);
2195 edge->flags |= TA_EDGE_DONE;
2197 #ifdef TA_DEBUG
2198 num_actions++;
2199 #endif
2200 continue;
2203 if (!anchor)
2205 /* if we reach this if clause, no stem has been aligned yet */
2207 FT_Pos org_len, org_center, cur_len;
2208 FT_Pos cur_pos1, error1, error2, u_off, d_off;
2211 org_len = edge2->opos - edge->opos;
2212 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2213 edge->flags, edge2->flags);
2215 /* some voodoo to specially round edges for small stem widths; */
2216 /* the idea is to align the center of a stem, */
2217 /* then shifting the stem edges to suitable positions */
2218 if (cur_len <= 64)
2220 /* width <= 1px */
2221 u_off = 32;
2222 d_off = 32;
2224 else
2226 /* 1px < width < 1.5px */
2227 u_off = 38;
2228 d_off = 26;
2231 if (cur_len < 96)
2233 org_center = edge->opos + (org_len >> 1);
2234 cur_pos1 = TA_PIX_ROUND(org_center);
2236 error1 = org_center - (cur_pos1 - u_off);
2237 if (error1 < 0)
2238 error1 = -error1;
2240 error2 = org_center - (cur_pos1 + d_off);
2241 if (error2 < 0)
2242 error2 = -error2;
2244 if (error1 < error2)
2245 cur_pos1 -= u_off;
2246 else
2247 cur_pos1 += d_off;
2249 edge->pos = cur_pos1 - cur_len / 2;
2250 edge2->pos = edge->pos + cur_len;
2252 else
2253 edge->pos = TA_PIX_ROUND(edge->opos);
2255 anchor = edge;
2256 edge->flags |= TA_EDGE_DONE;
2258 TA_LOG((" ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
2259 " snapped to %.2f and %.2f\n",
2260 edge - edges, edge->opos / 64.0,
2261 edge2 - edges, edge2->opos / 64.0,
2262 edge->pos / 64.0, edge2->pos / 64.0));
2264 if (hints->recorder)
2265 hints->recorder(ta_anchor, hints, dim,
2266 edge, edge2, NULL, NULL, NULL);
2268 ta_latin_align_linked_edge(hints, dim, edge, edge2);
2270 #ifdef TA_DEBUG
2271 num_actions += 2;
2272 #endif
2274 else
2276 FT_Pos org_pos, org_len, org_center, cur_len;
2277 FT_Pos cur_pos1, cur_pos2, delta1, delta2;
2280 org_pos = anchor->pos + (edge->opos - anchor->opos);
2281 org_len = edge2->opos - edge->opos;
2282 org_center = org_pos + (org_len >> 1);
2284 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2285 edge->flags, edge2->flags);
2287 if (edge2->flags & TA_EDGE_DONE)
2289 TA_LOG((" ADJUST: edge %d (pos=%.2f) moved to %.2f\n",
2290 edge - edges, edge->pos / 64.0,
2291 (edge2->pos - cur_len) / 64.0));
2293 edge->pos = edge2->pos - cur_len;
2295 if (hints->recorder)
2297 TA_Edge bound = NULL;
2300 if (edge > edges)
2301 bound = &edge[-1];
2303 hints->recorder(ta_adjust, hints, dim,
2304 edge, edge2, NULL, bound, NULL);
2308 else if (cur_len < 96)
2310 FT_Pos u_off, d_off;
2313 cur_pos1 = TA_PIX_ROUND(org_center);
2315 if (cur_len <= 64)
2317 u_off = 32;
2318 d_off = 32;
2320 else
2322 u_off = 38;
2323 d_off = 26;
2326 delta1 = org_center - (cur_pos1 - u_off);
2327 if (delta1 < 0)
2328 delta1 = -delta1;
2330 delta2 = org_center - (cur_pos1 + d_off);
2331 if (delta2 < 0)
2332 delta2 = -delta2;
2334 if (delta1 < delta2)
2335 cur_pos1 -= u_off;
2336 else
2337 cur_pos1 += d_off;
2339 edge->pos = cur_pos1 - cur_len / 2;
2340 edge2->pos = cur_pos1 + cur_len / 2;
2342 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2343 " snapped to %.2f and %.2f\n",
2344 edge - edges, edge->opos / 64.0,
2345 edge2 - edges, edge2->opos / 64.0,
2346 edge->pos / 64.0, edge2->pos / 64.0));
2348 if (hints->recorder)
2350 TA_Edge bound = NULL;
2353 if (edge > edges)
2354 bound = &edge[-1];
2356 hints->recorder(ta_stem, hints, dim,
2357 edge, edge2, NULL, bound, NULL);
2361 else
2363 org_pos = anchor->pos + (edge->opos - anchor->opos);
2364 org_len = edge2->opos - edge->opos;
2365 org_center = org_pos + (org_len >> 1);
2367 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2368 edge->flags, edge2->flags);
2370 cur_pos1 = TA_PIX_ROUND(org_pos);
2371 delta1 = cur_pos1 + (cur_len >> 1) - org_center;
2372 if (delta1 < 0)
2373 delta1 = -delta1;
2375 cur_pos2 = TA_PIX_ROUND(org_pos + org_len) - cur_len;
2376 delta2 = cur_pos2 + (cur_len >> 1) - org_center;
2377 if (delta2 < 0)
2378 delta2 = -delta2;
2380 edge->pos = (delta1 < delta2) ? cur_pos1 : cur_pos2;
2381 edge2->pos = edge->pos + cur_len;
2383 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2384 " snapped to %.2f and %.2f\n",
2385 edge - edges, edge->opos / 64.0,
2386 edge2 - edges, edge2->opos / 64.0,
2387 edge->pos / 64.0, edge2->pos / 64.0));
2389 if (hints->recorder)
2391 TA_Edge bound = NULL;
2394 if (edge > edges)
2395 bound = &edge[-1];
2397 hints->recorder(ta_stem, hints, dim,
2398 edge, edge2, NULL, bound, NULL);
2402 #ifdef TA_DEBUG
2403 num_actions++;
2404 #endif
2406 edge->flags |= TA_EDGE_DONE;
2407 edge2->flags |= TA_EDGE_DONE;
2409 if (edge > edges
2410 && edge->pos < edge[-1].pos)
2412 #ifdef TA_DEBUG
2413 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2414 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
2416 num_actions++;
2417 #endif
2419 edge->pos = edge[-1].pos;
2421 if (hints->recorder)
2422 hints->recorder(ta_bound, hints, dim,
2423 edge, &edge[-1], NULL, NULL, NULL);
2428 /* make sure that lowercase m's maintain their symmetry */
2430 /* In general, lowercase m's have six vertical edges if they are sans */
2431 /* serif, or twelve if they are with serifs. This implementation is */
2432 /* based on that assumption, and seems to work very well with most */
2433 /* faces. However, if for a certain face this assumption is not */
2434 /* true, the m is just rendered like before. In addition, any stem */
2435 /* correction will only be applied to symmetrical glyphs (even if the */
2436 /* glyph is not an m), so the potential for unwanted distortion is */
2437 /* relatively low. */
2439 /* we don't handle horizontal edges since we can't easily assure that */
2440 /* the third (lowest) stem aligns with the base line; it might end up */
2441 /* one pixel higher or lower */
2443 n_edges = edge_limit - edges;
2444 if (dim == TA_DIMENSION_HORZ
2445 && (n_edges == 6 || n_edges == 12))
2447 TA_Edge edge1, edge2, edge3;
2448 FT_Pos dist1, dist2, span, delta;
2451 if (n_edges == 6)
2453 edge1 = edges;
2454 edge2 = edges + 2;
2455 edge3 = edges + 4;
2457 else
2459 edge1 = edges + 1;
2460 edge2 = edges + 5;
2461 edge3 = edges + 9;
2464 dist1 = edge2->opos - edge1->opos;
2465 dist2 = edge3->opos - edge2->opos;
2467 span = dist1 - dist2;
2468 if (span < 0)
2469 span = -span;
2471 if (span < 8)
2473 delta = edge3->pos - (2 * edge2->pos - edge1->pos);
2474 edge3->pos -= delta;
2475 if (edge3->link)
2476 edge3->link->pos -= delta;
2478 /* move the serifs along with the stem */
2479 if (n_edges == 12)
2481 (edges + 8)->pos -= delta;
2482 (edges + 11)->pos -= delta;
2485 edge3->flags |= TA_EDGE_DONE;
2486 if (edge3->link)
2487 edge3->link->flags |= TA_EDGE_DONE;
2491 if (has_serifs || !anchor)
2493 /* now hint the remaining edges (serifs and single) */
2494 /* in order to complete our processing */
2495 for (edge = edges; edge < edge_limit; edge++)
2497 TA_Edge lower_bound = NULL;
2498 TA_Edge upper_bound = NULL;
2500 FT_Pos delta;
2503 if (edge->flags & TA_EDGE_DONE)
2504 continue;
2506 delta = 1000;
2508 if (edge->serif)
2510 delta = edge->serif->opos - edge->opos;
2511 if (delta < 0)
2512 delta = -delta;
2515 if (edge > edges)
2516 lower_bound = &edge[-1];
2518 if (edge + 1 < edge_limit
2519 && edge[1].flags & TA_EDGE_DONE)
2520 upper_bound = &edge[1];
2523 if (delta < 64 + 16)
2525 ta_latin_align_serif_edge(hints, edge->serif, edge);
2527 TA_LOG((" SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
2528 " aligned to %.2f\n",
2529 edge - edges, edge->opos / 64.0,
2530 edge->serif - edges, edge->serif->opos / 64.0,
2531 edge->pos / 64.0));
2533 if (hints->recorder)
2534 hints->recorder(ta_serif, hints, dim,
2535 edge, NULL, NULL, lower_bound, upper_bound);
2537 else if (!anchor)
2539 edge->pos = TA_PIX_ROUND(edge->opos);
2540 anchor = edge;
2542 TA_LOG((" SERIF_ANCHOR: edge %d (opos=%.2f) snapped to %.2f\n",
2543 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
2545 if (hints->recorder)
2546 hints->recorder(ta_serif_anchor, hints, dim,
2547 edge, NULL, NULL, lower_bound, upper_bound);
2549 else
2551 TA_Edge before, after;
2554 for (before = edge - 1; before >= edges; before--)
2555 if (before->flags & TA_EDGE_DONE)
2556 break;
2558 for (after = edge + 1; after < edge_limit; after++)
2559 if (after->flags & TA_EDGE_DONE)
2560 break;
2562 if (before >= edges && before < edge
2563 && after < edge_limit && after > edge)
2565 if (after->opos == before->opos)
2566 edge->pos = before->pos;
2567 else
2568 edge->pos = before->pos + FT_MulDiv(edge->opos - before->opos,
2569 after->pos - before->pos,
2570 after->opos - before->opos);
2572 TA_LOG((" SERIF_LINK1: edge %d (opos=%.2f) snapped to %.2f"
2573 " from %d (opos=%.2f)\n",
2574 edge - edges, edge->opos / 64.0,
2575 edge->pos / 64.0,
2576 before - edges, before->opos / 64.0));
2578 if (hints->recorder)
2579 hints->recorder(ta_serif_link1, hints, dim,
2580 edge, before, after, lower_bound, upper_bound);
2582 else
2584 edge->pos = anchor->pos + ((edge->opos - anchor->opos + 16) & ~31);
2585 TA_LOG((" SERIF_LINK2: edge %d (opos=%.2f) snapped to %.2f\n",
2586 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
2588 if (hints->recorder)
2589 hints->recorder(ta_serif_link2, hints, dim,
2590 edge, NULL, NULL, lower_bound, upper_bound);
2594 #ifdef TA_DEBUG
2595 num_actions++;
2596 #endif
2597 edge->flags |= TA_EDGE_DONE;
2599 if (edge > edges
2600 && edge->pos < edge[-1].pos)
2602 #ifdef TA_DEBUG
2603 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2604 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
2605 num_actions++;
2606 #endif
2608 edge->pos = edge[-1].pos;
2610 if (hints->recorder)
2611 hints->recorder(ta_bound, hints, dim,
2612 edge, &edge[-1], NULL, NULL, NULL);
2615 if (edge + 1 < edge_limit
2616 && edge[1].flags & TA_EDGE_DONE
2617 && edge->pos > edge[1].pos)
2619 #ifdef TA_DEBUG
2620 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2621 edge - edges, edge->pos / 64.0, edge[1].pos / 64.0));
2623 num_actions++;
2624 #endif
2626 edge->pos = edge[1].pos;
2628 if (hints->recorder)
2629 hints->recorder(ta_bound, hints, dim,
2630 edge, &edge[1], NULL, NULL, NULL);
2635 #ifdef TA_DEBUG
2636 if (!num_actions)
2637 TA_LOG((" (none)\n"));
2638 TA_LOG(("\n"));
2639 #endif
2643 /* apply the complete hinting algorithm to a latin glyph */
2645 static FT_Error
2646 ta_latin_hints_apply(TA_GlyphHints hints,
2647 FT_Outline* outline,
2648 TA_LatinMetrics metrics)
2650 FT_Error error;
2651 int dim;
2654 error = ta_glyph_hints_reload(hints, outline);
2655 if (error)
2656 goto Exit;
2658 /* analyze glyph outline */
2659 #ifdef TA_CONFIG_OPTION_USE_WARPER
2660 if (metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT
2661 || TA_HINTS_DO_HORIZONTAL(hints))
2662 #else
2663 if (TA_HINTS_DO_HORIZONTAL(hints))
2664 #endif
2666 error = ta_latin_hints_detect_features(hints, TA_DIMENSION_HORZ);
2667 if (error)
2668 goto Exit;
2671 if (TA_HINTS_DO_VERTICAL(hints))
2673 error = ta_latin_hints_detect_features(hints, TA_DIMENSION_VERT);
2674 if (error)
2675 goto Exit;
2677 ta_latin_hints_compute_blue_edges(hints, metrics);
2680 /* grid-fit the outline */
2681 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
2683 #ifdef TA_CONFIG_OPTION_USE_WARPER
2684 if (dim == TA_DIMENSION_HORZ
2685 && metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT)
2687 TA_WarperRec warper;
2688 FT_Fixed scale;
2689 FT_Pos delta;
2692 ta_warper_compute(&warper, hints, (TA_Dimension)dim, &scale, &delta);
2693 ta_glyph_hints_scale_dim(hints, (TA_Dimension)dim, scale, delta);
2695 continue;
2697 #endif
2699 if ((dim == TA_DIMENSION_HORZ && TA_HINTS_DO_HORIZONTAL(hints))
2700 || (dim == TA_DIMENSION_VERT && TA_HINTS_DO_VERTICAL(hints)))
2702 ta_latin_hint_edges(hints, (TA_Dimension)dim);
2703 ta_glyph_hints_align_edge_points(hints, (TA_Dimension)dim);
2704 ta_glyph_hints_align_strong_points(hints, (TA_Dimension)dim);
2705 ta_glyph_hints_align_weak_points(hints, (TA_Dimension)dim);
2709 ta_glyph_hints_save(hints, outline);
2711 Exit:
2712 return error;
2716 const TA_WritingSystemClassRec ta_latin_writing_system_class =
2718 TA_WRITING_SYSTEM_LATIN,
2720 sizeof (TA_LatinMetricsRec),
2722 (TA_Script_InitMetricsFunc)ta_latin_metrics_init,
2723 (TA_Script_ScaleMetricsFunc)ta_latin_metrics_scale,
2724 (TA_Script_DoneMetricsFunc)NULL,
2726 (TA_Script_InitHintsFunc)ta_latin_hints_init,
2727 (TA_Script_ApplyHintsFunc)ta_latin_hints_apply
2731 /* XXX: this should probably fine tuned to differentiate better between */
2732 /* scripts... */
2734 static const TA_Script_UniRangeRec ta_latn_uniranges[] =
2736 TA_UNIRANGE_REC(0x0020UL, 0x007FUL), /* Basic Latin (no control chars) */
2737 TA_UNIRANGE_REC(0x00A0UL, 0x00FFUL), /* Latin-1 Supplement (no control chars) */
2738 TA_UNIRANGE_REC(0x0100UL, 0x017FUL), /* Latin Extended-A */
2739 TA_UNIRANGE_REC(0x0180UL, 0x024FUL), /* Latin Extended-B */
2740 TA_UNIRANGE_REC(0x0250UL, 0x02AFUL), /* IPA Extensions */
2741 TA_UNIRANGE_REC(0x02B0UL, 0x02FFUL), /* Spacing Modifier Letters */
2742 TA_UNIRANGE_REC(0x0300UL, 0x036FUL), /* Combining Diacritical Marks */
2743 TA_UNIRANGE_REC(0x1D00UL, 0x1D7FUL), /* Phonetic Extensions */
2744 TA_UNIRANGE_REC(0x1D80UL, 0x1DBFUL), /* Phonetic Extensions Supplement */
2745 TA_UNIRANGE_REC(0x1DC0UL, 0x1DFFUL), /* Combining Diacritical Marks Supplement */
2746 TA_UNIRANGE_REC(0x1E00UL, 0x1EFFUL), /* Latin Extended Additional */
2747 TA_UNIRANGE_REC(0x2000UL, 0x206FUL), /* General Punctuation */
2748 TA_UNIRANGE_REC(0x2070UL, 0x209FUL), /* Superscripts and Subscripts */
2749 TA_UNIRANGE_REC(0x20A0UL, 0x20CFUL), /* Currency Symbols */
2750 TA_UNIRANGE_REC(0x2150UL, 0x218FUL), /* Number Forms */
2751 TA_UNIRANGE_REC(0x2460UL, 0x24FFUL), /* Enclosed Alphanumerics */
2752 TA_UNIRANGE_REC(0x2C60UL, 0x2C7FUL), /* Latin Extended-C */
2753 TA_UNIRANGE_REC(0x2E00UL, 0x2E7FUL), /* Supplemental Punctuation */
2754 TA_UNIRANGE_REC(0xA720UL, 0xA7FFUL), /* Latin Extended-D */
2755 TA_UNIRANGE_REC(0xFB00UL, 0xFB06UL), /* Alphab. Present. Forms (Latin Ligs) */
2756 TA_UNIRANGE_REC(0x1D400UL, 0x1D7FFUL), /* Mathematical Alphanumeric Symbols */
2757 TA_UNIRANGE_REC(0x1F100UL, 0x1F1FFUL), /* Enclosed Alphanumeric Supplement */
2758 TA_UNIRANGE_REC(0UL, 0UL)
2761 static const TA_Script_UniRangeRec ta_grek_uniranges[] =
2763 TA_UNIRANGE_REC(0x0370UL, 0x03FFUL), /* Greek and Coptic */
2764 TA_UNIRANGE_REC(0x1F00UL, 0x1FFFUL), /* Greek Extended */
2765 TA_UNIRANGE_REC(0UL, 0UL )
2768 static const TA_Script_UniRangeRec ta_cyrl_uniranges[] =
2770 TA_UNIRANGE_REC(0x0400UL, 0x04FFUL), /* Cyrillic */
2771 TA_UNIRANGE_REC(0x0500UL, 0x052FUL), /* Cyrillic Supplement */
2772 TA_UNIRANGE_REC(0x2DE0UL, 0x2DFFUL), /* Cyrillic Extended-A */
2773 TA_UNIRANGE_REC(0xA640UL, 0xA69FUL), /* Cyrillic Extended-B */
2774 TA_UNIRANGE_REC(0UL, 0UL )
2777 static const TA_Script_UniRangeRec ta_hebr_uniranges[] =
2779 TA_UNIRANGE_REC(0x0590UL, 0x05FFUL), /* Hebrew */
2780 TA_UNIRANGE_REC(0xFB1DUL, 0xFB4FUL), /* Alphab. Present. Forms (Hebrew) */
2781 TA_UNIRANGE_REC(0UL, 0UL )
2785 const TA_ScriptClassRec ta_latn_script_class =
2787 TA_SCRIPT_LATN,
2788 TA_BLUE_STRINGSET_LATN,
2789 TA_WRITING_SYSTEM_LATIN,
2791 ta_latn_uniranges,
2795 const TA_ScriptClassRec ta_grek_script_class =
2797 TA_SCRIPT_GREK,
2798 TA_BLUE_STRINGSET_GREK,
2799 TA_WRITING_SYSTEM_LATIN,
2801 ta_grek_uniranges,
2802 0x3BF /* ο */
2805 const TA_ScriptClassRec ta_cyrl_script_class =
2807 TA_SCRIPT_CYRL,
2808 TA_BLUE_STRINGSET_CYRL,
2809 TA_WRITING_SYSTEM_LATIN,
2811 ta_cyrl_uniranges,
2812 0x43E /* о */
2815 const TA_ScriptClassRec ta_hebr_script_class =
2817 TA_SCRIPT_HEBR,
2818 TA_BLUE_STRINGSET_HEBR,
2819 TA_WRITING_SYSTEM_LATIN,
2821 ta_hebr_uniranges,
2822 0x5DD /* ם */
2825 /* end of talatin.c */