Fix OTS warning about `maxp.maxSizeOfInstructions`.
[ttfautohint.git] / lib / talatin.c
blobbb3249edb88bf147954ebfb5d8e0490ab0edae75
1 /* talatin.c */
3 /*
4 * Copyright (C) 2011-2022 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 /* needed for computation of round vs. flat segments */
39 #define FLAT_THRESHOLD(x) (x / 14)
42 /* find segments and links, compute all stem widths, and initialize */
43 /* standard width and height for the glyph with given charcode */
45 void
46 ta_latin_metrics_init_widths(TA_LatinMetrics metrics,
47 FT_Face face,
48 FT_Bool use_cmap)
50 /* scan the array of segments in each direction */
51 TA_GlyphHintsRec hints[1];
54 TA_LOG_GLOBAL(("\n"
55 "latin standard widths computation (style `%s')\n"
56 "=====================================================\n"
57 "\n",
58 ta_style_names[metrics->root.style_class->style]));
60 ta_glyph_hints_init(hints);
62 metrics->axis[TA_DIMENSION_HORZ].width_count = 0;
63 metrics->axis[TA_DIMENSION_VERT].width_count = 0;
66 FT_Error error;
67 FT_ULong glyph_index;
68 int dim, dim_max;
69 TA_LatinMetricsRec dummy[1];
70 TA_Scaler scaler = &dummy->root.scaler;
72 TA_StyleClass style_class = metrics->root.style_class;
73 TA_ScriptClass script_class = ta_script_classes[style_class->script];
75 FONT* font = metrics->root.globals->font;
77 void* shaper_buf;
78 const char* p;
80 #ifdef TA_DEBUG
81 FT_ULong ch = 0;
82 #endif
85 /* if the user provides horizontal stem widths, */
86 /* apply the algorithm only along the horizontal axis */
87 TA_control_set_stem_widths(metrics, font);
88 if (metrics->axis[TA_DIMENSION_VERT].width_count)
90 TA_LatinAxis axis = &metrics->axis[TA_DIMENSION_VERT];
93 dim_max = TA_DIMENSION_VERT;
95 axis->standard_width = axis->widths[0].org;
96 axis->edge_distance_threshold = axis->standard_width / 5;
97 axis->extra_light = 0;
99 #ifdef TA_DEBUG
101 FT_UInt i;
104 TA_LOG_GLOBAL(("horizontal widths (user provided):\n" ));
106 TA_LOG_GLOBAL((" %d (standard)", axis->standard_width));
107 for (i = 1; i < axis->width_count; i++)
108 TA_LOG_GLOBAL((" %d", axis->widths[i].org));
110 TA_LOG_GLOBAL(("\n"));
112 #endif
115 else
116 dim_max = TA_DIMENSION_MAX;
118 if (!use_cmap)
119 goto Exit;
121 p = script_class->standard_charstring;
122 shaper_buf = ta_shaper_buf_create(face);
125 * We check a list of standard characters to catch features like
126 * `c2sc' (small caps from caps) that don't contain lowercase letters
127 * by definition, or other features that mainly operate on numerals.
128 * The first match wins.
131 glyph_index = 0;
132 while (*p)
134 unsigned int num_idx;
136 #ifdef TA_DEBUG
137 const char* p_old;
138 #endif
141 while (*p == ' ')
142 p++;
144 #ifdef TA_DEBUG
145 p_old = p;
146 GET_UTF8_CHAR(ch, p_old);
147 #endif
149 /* reject input that maps to more than a single glyph */
150 p = ta_shaper_get_cluster(p, &metrics->root, shaper_buf, &num_idx);
151 if (num_idx > 1)
152 continue;
154 /* otherwise exit loop if we have a result */
155 glyph_index = ta_shaper_get_elem(&metrics->root,
156 shaper_buf,
158 NULL,
159 NULL);
160 if (glyph_index)
161 break;
164 ta_shaper_buf_destroy(face, shaper_buf);
166 if (!glyph_index)
168 TA_LOG_GLOBAL(("no standard character\n"));
169 goto Exit;
172 TA_LOG_GLOBAL(("standard character: U+%04lX (glyph index %d)\n",
173 ch, glyph_index));
175 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
176 if (error || face->glyph->outline.n_points <= 0)
177 goto Exit;
179 memset(dummy, 0, sizeof (TA_LatinMetricsRec));
181 dummy->units_per_em = metrics->units_per_em;
183 scaler->x_scale = 0x10000L;
184 scaler->y_scale = 0x10000L;
185 scaler->x_delta = 0;
186 scaler->y_delta = 0;
188 scaler->face = face;
189 scaler->render_mode = FT_RENDER_MODE_NORMAL;
190 scaler->flags = 0;
192 ta_glyph_hints_rescale(hints, (TA_StyleMetrics)dummy);
194 error = ta_glyph_hints_reload(hints, &face->glyph->outline);
195 if (error)
196 goto Exit;
198 for (dim = 0; dim < dim_max; dim++)
200 TA_LatinAxis axis = &metrics->axis[dim];
201 TA_AxisHints axhints = &hints->axis[dim];
203 TA_Segment seg, limit, link;
204 FT_UInt num_widths = 0;
207 error = ta_latin_hints_compute_segments(hints, (TA_Dimension)dim);
208 if (error)
209 goto Exit;
212 * We assume that the glyphs selected for the stem width
213 * computation are `featureless' enough so that the linking
214 * algorithm works fine without adjustments of its scoring
215 * function.
217 ta_latin_hints_link_segments(hints, 0, NULL, (TA_Dimension)dim);
219 seg = axhints->segments;
220 limit = seg + axhints->num_segments;
222 for (; seg < limit; seg++)
224 link = seg->link;
226 /* we only consider stem segments there! */
227 if (link
228 && link->link == seg
229 && link > seg)
231 FT_Pos dist;
234 dist = seg->pos - link->pos;
235 if (dist < 0)
236 dist = -dist;
238 if (num_widths < TA_LATIN_MAX_WIDTHS)
239 axis->widths[num_widths++].org = dist;
243 /* this also replaces multiple almost identical stem widths */
244 /* with a single one (the value 100 is heuristic) */
245 ta_sort_and_quantize_widths(&num_widths, axis->widths,
246 dummy->units_per_em / 100);
247 axis->width_count = num_widths;
250 Exit:
251 for (dim = 0; dim < dim_max; dim++)
253 TA_LatinAxis axis = &metrics->axis[dim];
254 FT_Pos stdw;
257 if (!axis->width_count)
259 /* if we have no standard characters, */
260 /* use `fallback-stem-width', if available, */
261 /* or a default width (value 50 is heuristic) */
262 if (dim == TA_DIMENSION_VERT && font->fallback_stem_width)
264 stdw = (FT_Pos)font->fallback_stem_width;
265 TA_LOG_GLOBAL(("using horizontal fallback stem width\n"));
267 else
269 stdw = TA_LATIN_CONSTANT(metrics, 50);
270 TA_LOG_GLOBAL(("using a default %s stem width\n",
271 dim == TA_DIMENSION_VERT ? "horizontal"
272 : "vertical"));
275 axis->width_count++;
276 axis->widths[0].org = stdw;
279 stdw = axis->widths[0].org;
281 /* let's try 20% of the smallest width */
282 axis->edge_distance_threshold = stdw / 5;
283 axis->standard_width = stdw;
284 axis->extra_light = 0;
286 #ifdef TA_DEBUG
288 FT_UInt i;
291 TA_LOG_GLOBAL(("%s widths:\n",
292 dim == TA_DIMENSION_VERT ? "horizontal"
293 : "vertical"));
295 TA_LOG_GLOBAL((" %d (standard)", axis->standard_width));
296 for (i = 1; i < axis->width_count; i++)
297 TA_LOG_GLOBAL((" %d", axis->widths[i].org));
299 TA_LOG_GLOBAL(("\n"));
301 #endif
305 TA_LOG_GLOBAL(("\n"));
307 ta_glyph_hints_done(hints);
311 static void
312 ta_latin_sort_blue(FT_UInt count,
313 TA_LatinBlue* table)
315 FT_UInt i;
316 FT_UInt j;
317 TA_LatinBlue swap;
320 /* we sort from bottom to top */
321 for (i = 1; i < count; i++)
323 for (j = i; j > 0; j--)
325 FT_Pos a, b;
328 if (table[j - 1]->flags & (TA_LATIN_BLUE_TOP
329 | TA_LATIN_BLUE_SUB_TOP))
330 a = table[j - 1]->ref.org;
331 else
332 a = table[j - 1]->shoot.org;
334 if (table[j]->flags & (TA_LATIN_BLUE_TOP
335 | TA_LATIN_BLUE_SUB_TOP))
336 b = table[j]->ref.org;
337 else
338 b = table[j]->shoot.org;
340 if (b >= a)
341 break;
343 swap = table[j];
344 table[j] = table[j - 1];
345 table[j - 1] = swap;
351 /* find all blue zones; flat segments give the reference points, */
352 /* round segments the overshoot positions */
354 static void
355 ta_latin_metrics_init_blues(TA_LatinMetrics metrics,
356 FT_Face face)
358 FT_Pos flats[TA_BLUE_STRING_MAX_LEN];
359 FT_Pos rounds[TA_BLUE_STRING_MAX_LEN];
360 FT_UInt num_flats;
361 FT_UInt num_rounds;
363 TA_LatinBlue blue;
364 FT_Error error;
365 TA_LatinAxis axis = &metrics->axis[TA_DIMENSION_VERT];
366 FT_Outline outline;
368 TA_StyleClass sc = metrics->root.style_class;
370 TA_Blue_Stringset bss = sc->blue_stringset;
371 const TA_Blue_StringRec* bs = &ta_blue_stringsets[bss];
373 FT_Pos flat_threshold = FLAT_THRESHOLD(metrics->units_per_em);
375 void* shaper_buf;
378 /* we walk over the blue character strings as specified in the */
379 /* style's entry in the `ta_blue_stringset' array */
381 TA_LOG_GLOBAL(("latin blue zones computation\n"
382 "============================\n"
383 "\n"));
385 shaper_buf = ta_shaper_buf_create(face);
387 for (; bs->string != TA_BLUE_STRING_MAX; bs++)
389 const char* p = &ta_blue_strings[bs->string];
390 FT_Pos* blue_ref;
391 FT_Pos* blue_shoot;
392 FT_Pos ascender;
393 FT_Pos descender;
396 #ifdef TA_DEBUG
398 FT_Bool have_flag = 0;
401 TA_LOG_GLOBAL(("blue zone %d", axis->blue_count));
403 if (bs->properties)
405 TA_LOG_GLOBAL((" ("));
407 if (TA_LATIN_IS_TOP_BLUE(bs))
409 TA_LOG_GLOBAL(("top"));
410 have_flag = 1;
412 else if (TA_LATIN_IS_SUB_TOP_BLUE(bs))
414 TA_LOG_GLOBAL(("sub top"));
415 have_flag = 1;
418 if (TA_LATIN_IS_NEUTRAL_BLUE(bs))
420 if (have_flag)
421 TA_LOG_GLOBAL((", "));
422 TA_LOG_GLOBAL(("neutral"));
423 have_flag = 1;
426 if (TA_LATIN_IS_X_HEIGHT_BLUE(bs))
428 if (have_flag)
429 TA_LOG_GLOBAL((", "));
430 TA_LOG_GLOBAL(("small top"));
431 have_flag = 1;
434 if (TA_LATIN_IS_LONG_BLUE(bs))
436 if (have_flag)
437 TA_LOG_GLOBAL((", "));
438 TA_LOG_GLOBAL(("long"));
441 TA_LOG_GLOBAL((")"));
444 TA_LOG_GLOBAL((":\n"));
446 #endif /* TA_DEBUG */
448 num_flats = 0;
449 num_rounds = 0;
450 ascender = 0;
451 descender = 0;
453 while (*p)
455 FT_ULong glyph_index;
456 FT_Long y_offset;
457 FT_Int best_point, best_contour_first, best_contour_last;
458 FT_Vector* points;
460 FT_Pos best_y_extremum; /* same as points.y */
461 FT_Bool best_round = 0;
463 unsigned int i, num_idx;
465 #ifdef TA_DEBUG
466 const char* p_old;
467 FT_ULong ch;
468 #endif
471 while (*p == ' ')
472 p++;
474 #ifdef TA_DEBUG
475 p_old = p;
476 GET_UTF8_CHAR(ch, p_old);
477 #endif
479 p = ta_shaper_get_cluster(p, &metrics->root, shaper_buf, &num_idx);
481 if (!num_idx)
483 TA_LOG_GLOBAL((" U+%04lX unavailable\n", ch));
484 continue;
487 if (TA_LATIN_IS_TOP_BLUE(bs))
488 best_y_extremum = FT_INT_MIN;
489 else
490 best_y_extremum = FT_INT_MAX;
492 /* iterate over all glyph elements of the character cluster */
493 /* and get the data of the `biggest' one */
494 for (i = 0; i < num_idx; i++)
496 FT_Pos best_y;
497 FT_Bool round = 0;
500 /* load the character in the face -- skip unknown or empty ones */
501 glyph_index = ta_shaper_get_elem(&metrics->root,
502 shaper_buf,
504 NULL,
505 &y_offset);
506 if (glyph_index == 0)
508 TA_LOG_GLOBAL((" U+%04lX unavailable\n", ch));
509 continue;
512 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
513 outline = face->glyph->outline;
514 /* reject glyphs that don't produce any rendering */
515 if (error || outline.n_points <= 2)
517 #ifdef TA_DEBUG
518 if (num_idx == 1)
519 TA_LOG_GLOBAL((" U+%04lX contains no (usable) outlines\n", ch));
520 else
521 TA_LOG_GLOBAL((" component %d of cluster starting with U+%04lX"
522 " contains no (usable) outlines\n", i, ch));
523 #endif
524 continue;
527 /* now compute min or max point indices and coordinates */
528 points = outline.points;
529 best_point = -1;
530 best_y = 0; /* make compiler happy */
531 best_contour_first = 0; /* ditto */
532 best_contour_last = 0; /* ditto */
535 FT_Int nn;
536 FT_Int first = 0;
537 FT_Int last = -1;
540 for (nn = 0; nn < outline.n_contours; first = last + 1, nn++)
542 FT_Int old_best_point = best_point;
543 FT_Int pp;
546 last = outline.contours[nn];
548 /* avoid single-point contours since they are never */
549 /* rasterized; in some fonts, they correspond to mark */
550 /* attachment points that are way outside of the glyph's */
551 /* real outline */
552 if (last <= first)
553 continue;
555 if (TA_LATIN_IS_TOP_BLUE(bs)
556 || TA_LATIN_IS_SUB_TOP_BLUE(bs))
558 for (pp = first; pp <= last; pp++)
560 if (best_point < 0
561 || points[pp].y > best_y)
563 best_point = pp;
564 best_y = points[pp].y;
565 ascender = TA_MAX(ascender, best_y + y_offset);
567 else
568 descender = TA_MIN(descender, points[pp].y + y_offset);
571 else
573 for (pp = first; pp <= last; pp++)
575 if (best_point < 0
576 || points[pp].y < best_y)
578 best_point = pp;
579 best_y = points[pp].y;
580 descender = TA_MIN(descender, best_y + y_offset);
582 else
583 ascender = TA_MAX(ascender, points[pp].y + y_offset);
587 if (best_point != old_best_point)
589 best_contour_first = first;
590 best_contour_last = last;
595 /* now check whether the point belongs to a straight or round */
596 /* segment; we first need to find in which contour the extremum */
597 /* lies, then inspect its previous and next points */
598 if (best_point >= 0)
600 FT_Pos best_x = points[best_point].x;
601 FT_Int prev, next;
602 FT_Int best_segment_first, best_segment_last;
603 FT_Int best_on_point_first, best_on_point_last;
604 FT_Pos dist;
607 best_segment_first = best_point;
608 best_segment_last = best_point;
610 if (FT_CURVE_TAG(outline.tags[best_point]) == FT_CURVE_TAG_ON)
612 best_on_point_first = best_point;
613 best_on_point_last = best_point;
615 else
617 best_on_point_first = -1;
618 best_on_point_last = -1;
621 /* look for the previous and next points on the contour */
622 /* that are not on the same Y coordinate, then threshold */
623 /* the `closeness'... */
624 prev = best_point;
625 next = prev;
629 if (prev > best_contour_first)
630 prev--;
631 else
632 prev = best_contour_last;
634 dist = TA_ABS(points[prev].y - best_y);
635 /* accept a small distance or a small angle (both values are */
636 /* heuristic; value 20 corresponds to approx. 2.9 degrees) */
637 if (dist > 5)
638 if (TA_ABS(points[prev].x - best_x) <= 20 * dist)
639 break;
641 best_segment_first = prev;
643 if (FT_CURVE_TAG(outline.tags[prev]) == FT_CURVE_TAG_ON)
645 best_on_point_first = prev;
646 if (best_on_point_last < 0)
647 best_on_point_last = prev;
650 } while (prev != best_point);
654 if (next < best_contour_last)
655 next++;
656 else
657 next = best_contour_first;
659 dist = TA_ABS(points[next].y - best_y);
660 if (dist > 5)
661 if (TA_ABS(points[next].x - best_x) <= 20 * dist)
662 break;
664 best_segment_last = next;
666 if (FT_CURVE_TAG(outline.tags[next]) == FT_CURVE_TAG_ON)
668 best_on_point_last = next;
669 if (best_on_point_first < 0)
670 best_on_point_first = next;
673 } while (next != best_point);
675 if (TA_LATIN_IS_LONG_BLUE(bs))
677 /* If this flag is set, we have an additional constraint to */
678 /* get the blue zone distance: Find a segment of the topmost */
679 /* (or bottommost) contour that is longer than a heuristic */
680 /* threshold. This ensures that small bumps in the outline */
681 /* are ignored (for example, the `vertical serifs' found in */
682 /* many Hebrew glyph designs). */
684 /* If this segment is long enough, we are done. Otherwise, */
685 /* search the segment next to the extremum that is long */
686 /* enough, has the same direction, and a not too large */
687 /* vertical distance from the extremum. Note that the */
688 /* algorithm doesn't check whether the found segment is */
689 /* actually the one (vertically) nearest to the extremum. */
691 /* heuristic threshold value */
692 FT_Pos length_threshold = metrics->units_per_em / 25;
695 dist = TA_ABS(points[best_segment_last].x -
696 points[best_segment_first].x);
698 if (dist < length_threshold
699 && best_segment_last - best_segment_first + 2 <=
700 best_contour_last - best_contour_first)
702 /* heuristic threshold value */
703 FT_Pos height_threshold = metrics->units_per_em / 4;
705 FT_Int first;
706 FT_Int last;
707 FT_Bool hit;
709 /* we intentionally declare these two variables */
710 /* outside of the loop since various compilers emit */
711 /* incorrect warning messages otherwise, talking about */
712 /* `possibly uninitialized variables' */
713 FT_Int p_first = 0; /* make compiler happy */
714 FT_Int p_last = 0;
716 FT_Bool left2right;
719 /* compute direction */
720 prev = best_point;
724 if (prev > best_contour_first)
725 prev--;
726 else
727 prev = best_contour_last;
729 if (points[prev].x != best_x)
730 break;
731 } while (prev != best_point);
733 /* skip glyph for the degenerate case */
734 if (prev == best_point)
735 continue;
737 left2right = FT_BOOL(points[prev].x < points[best_point].x);
739 first = best_segment_last;
740 last = first;
741 hit = 0;
745 FT_Bool l2r;
746 FT_Pos d;
749 if (!hit)
751 /* no hit; adjust first point */
752 first = last;
754 /* also adjust first and last on point */
755 if (FT_CURVE_TAG(outline.tags[first]) == FT_CURVE_TAG_ON)
757 p_first = first;
758 p_last = first;
760 else
762 p_first = -1;
763 p_last = -1;
766 hit = 1;
769 if (last < best_contour_last)
770 last++;
771 else
772 last = best_contour_first;
774 if (TA_ABS(best_y - points[first].y) > height_threshold)
776 /* vertical distance too large */
777 hit = 0;
778 continue;
781 /* same test as above */
782 dist = TA_ABS(points[last].y - points[first].y);
783 if (dist > 5)
784 if (TA_ABS(points[last].x - points[first].x) <= 20 * dist)
786 hit = 0;
787 continue;
790 if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON)
792 p_last = last;
793 if (p_first < 0)
794 p_first = last;
797 l2r = FT_BOOL(points[first].x < points[last].x);
798 d = TA_ABS(points[last].x - points[first].x);
800 if (l2r == left2right
801 && d >= length_threshold)
803 /* all constraints are met; update segment after */
804 /* finding its end */
807 if (last < best_contour_last)
808 last++;
809 else
810 last = best_contour_first;
812 d = TA_ABS(points[last].y - points[first].y);
813 if (d > 5)
814 if (TA_ABS(points[next].x - points[first].x) <=
815 20 * dist)
817 if (last > best_contour_first)
818 last--;
819 else
820 last = best_contour_last;
821 break;
824 p_last = last;
826 if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON)
828 p_last = last;
829 if (p_first < 0)
830 p_first = last;
832 } while (last != best_segment_first);
834 best_y = points[first].y;
836 best_segment_first = first;
837 best_segment_last = last;
839 best_on_point_first = p_first;
840 best_on_point_last = p_last;
842 break;
844 } while (last != best_segment_first);
849 * for computing blue zones, we add the y offset as returned
850 * by the currently used OpenType feature --
851 * for example, superscript glyphs might be identical
852 * to subscript glyphs with a vertical shift
854 best_y += y_offset;
856 #ifdef TA_DEBUG
857 if (num_idx == 1)
858 TA_LOG_GLOBAL((" U+%04lX: best_y = %5ld", ch, best_y));
859 else
860 TA_LOG_GLOBAL((" component %d of cluster starting with U+%04lX:"
861 " best_y = %5ld", i, ch, best_y));
862 #endif
865 * now set the `round' flag depending on the segment's kind:
867 * - if the horizontal distance between the first and last
868 * `on' point is larger than a heuristic threshold
869 * we have a flat segment
870 * - if either the first or the last point of the segment is
871 * an `off' point, the segment is round, otherwise it is
872 * flat
874 if (best_on_point_first >= 0
875 && best_on_point_last >= 0
876 && (TA_ABS(points[best_on_point_last].x
877 - points[best_on_point_first].x))
878 > flat_threshold)
879 round = 0;
880 else
881 round = FT_BOOL(FT_CURVE_TAG(outline.tags[best_segment_first])
882 != FT_CURVE_TAG_ON
883 || FT_CURVE_TAG(outline.tags[best_segment_last])
884 != FT_CURVE_TAG_ON);
886 if (round && TA_LATIN_IS_NEUTRAL_BLUE(bs))
888 /* only use flat segments for a neutral blue zone */
889 TA_LOG_GLOBAL((" (round, skipped)\n"));
890 continue;
893 TA_LOG_GLOBAL((" (%s)\n", round ? "round" : "flat"));
896 if (TA_LATIN_IS_TOP_BLUE(bs))
898 if (best_y > best_y_extremum)
900 best_y_extremum = best_y;
901 best_round = round;
904 else
906 if (best_y < best_y_extremum)
908 best_y_extremum = best_y;
909 best_round = round;
913 } /* end for loop */
915 if (!(best_y_extremum == FT_INT_MIN
916 || best_y_extremum == FT_INT_MAX))
918 if (best_round)
919 rounds[num_rounds++] = best_y_extremum;
920 else
921 flats[num_flats++] = best_y_extremum;
924 } /* end while loop */
926 if (num_flats == 0 && num_rounds == 0)
928 /* we couldn't find a single glyph to compute this blue zone, */
929 /* we will simply ignore it then */
930 TA_LOG_GLOBAL((" empty\n"));
931 continue;
934 /* we have computed the contents of the `rounds' and `flats' tables, */
935 /* now determine the reference and overshoot position of the blue -- */
936 /* we simply take the median value after a simple sort */
937 ta_sort_pos(num_rounds, rounds);
938 ta_sort_pos(num_flats, flats);
940 blue = &axis->blues[axis->blue_count];
941 blue_ref = &blue->ref.org;
942 blue_shoot = &blue->shoot.org;
944 axis->blue_count++;
946 if (num_flats == 0)
948 *blue_ref =
949 *blue_shoot = rounds[num_rounds / 2];
951 else if (num_rounds == 0)
953 *blue_ref =
954 *blue_shoot = flats[num_flats / 2];
956 else
958 *blue_ref = flats[num_flats / 2];
959 *blue_shoot = rounds[num_rounds / 2];
962 /* there are sometimes problems if the overshoot position of top */
963 /* zones is under its reference position, or the opposite for bottom */
964 /* zones; we must thus check everything there and correct the errors */
965 if (*blue_shoot != *blue_ref)
967 FT_Pos ref = *blue_ref;
968 FT_Pos shoot = *blue_shoot;
969 FT_Bool over_ref = FT_BOOL(shoot > ref);
972 if ((TA_LATIN_IS_TOP_BLUE(bs)
973 || TA_LATIN_IS_SUB_TOP_BLUE(bs)) ^ over_ref)
975 *blue_ref =
976 *blue_shoot = (shoot + ref) / 2;
978 TA_LOG_GLOBAL((" [overshoot smaller than reference,"
979 " taking mean value]\n"));
983 blue->ascender = ascender;
984 blue->descender = descender;
986 blue->flags = 0;
987 if (TA_LATIN_IS_TOP_BLUE(bs))
988 blue->flags |= TA_LATIN_BLUE_TOP;
989 if (TA_LATIN_IS_SUB_TOP_BLUE(bs))
990 blue->flags |= TA_LATIN_BLUE_SUB_TOP;
991 if (TA_LATIN_IS_NEUTRAL_BLUE(bs))
992 blue->flags |= TA_LATIN_BLUE_NEUTRAL;
994 /* the following flag is used later to adjust the y and x scales */
995 /* in order to optimize the pixel grid alignment */
996 /* of the top of small letters */
997 if (TA_LATIN_IS_X_HEIGHT_BLUE(bs))
998 blue->flags |= TA_LATIN_BLUE_ADJUSTMENT;
1000 TA_LOG_GLOBAL((" -> reference = %ld\n"
1001 " overshoot = %ld\n",
1002 *blue_ref, *blue_shoot));
1004 } /* end for loop */
1006 ta_shaper_buf_destroy(face, shaper_buf);
1008 /* if windows compatibility mode is activated, */
1009 /* add two artificial blue zones for usWinAscent and usWinDescent */
1010 /* just in case the above algorithm has missed them -- */
1011 /* Windows cuts off everything outside of those two values */
1012 if (metrics->root.globals->font->windows_compatibility)
1014 TT_OS2* os2;
1017 os2 = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
1019 if (os2)
1021 blue = &axis->blues[axis->blue_count];
1022 blue->flags = TA_LATIN_BLUE_TOP | TA_LATIN_BLUE_ACTIVE;
1023 blue->ref.org =
1024 blue->shoot.org = os2->usWinAscent;
1026 TA_LOG_GLOBAL(("artificial blue zone for usWinAscent:\n"
1027 " -> reference = %ld\n"
1028 " overshoot = %ld\n",
1029 blue->ref.org, blue->shoot.org));
1031 blue = &axis->blues[axis->blue_count + 1];
1032 blue->flags = TA_LATIN_BLUE_ACTIVE;
1033 blue->ref.org =
1034 blue->shoot.org = -os2->usWinDescent;
1036 TA_LOG_GLOBAL(("artificial blue zone for usWinDescent:\n"
1037 " -> reference = %ld\n"
1038 " overshoot = %ld\n",
1039 blue->ref.org, blue->shoot.org));
1041 else
1043 blue = &axis->blues[axis->blue_count];
1044 blue->flags =
1045 blue->ref.org =
1046 blue->shoot.org = 0;
1048 blue = &axis->blues[axis->blue_count + 1];
1049 blue->flags =
1050 blue->ref.org =
1051 blue->shoot.org = 0;
1055 /* we finally check whether blue zones are ordered; */
1056 /* `ref' and `shoot' values of two blue zones must not overlap */
1057 if (axis->blue_count)
1059 FT_UInt i;
1060 TA_LatinBlue blue_sorted[TA_BLUE_STRINGSET_MAX_LEN + 2];
1063 for (i = 0; i < axis->blue_count; i++)
1064 blue_sorted[i] = &axis->blues[i];
1066 /* sort bottoms of blue zones... */
1067 ta_latin_sort_blue(axis->blue_count, blue_sorted);
1069 /* ...and adjust top values if necessary */
1070 for (i = 0; i < axis->blue_count - 1; i++)
1072 FT_Pos* a;
1073 FT_Pos* b;
1075 #ifdef TA_DEBUG
1076 FT_Bool a_is_top = 0;
1077 #endif
1080 if (blue_sorted[i]->flags & (TA_LATIN_BLUE_TOP
1081 | TA_LATIN_BLUE_SUB_TOP))
1083 a = &blue_sorted[i]->shoot.org;
1084 #ifdef TA_DEBUG
1085 a_is_top = 1;
1086 #endif
1088 else
1089 a = &blue_sorted[i]->ref.org;
1091 if (blue_sorted[i + 1]->flags & (TA_LATIN_BLUE_TOP
1092 | TA_LATIN_BLUE_SUB_TOP))
1093 b = &blue_sorted[i + 1]->shoot.org;
1094 else
1095 b = &blue_sorted[i + 1]->ref.org;
1097 if (*a > *b)
1099 *a = *b;
1100 TA_LOG_GLOBAL(("blue zone overlap:"
1101 " adjusting %s %d to %ld\n",
1102 a_is_top ? "overshoot" : "reference",
1103 blue_sorted[i] - axis->blues,
1104 *a));
1109 TA_LOG_GLOBAL(("\n"));
1111 return;
1115 /* check whether all ASCII digits have the same advance width */
1117 void
1118 ta_latin_metrics_check_digits(TA_LatinMetrics metrics,
1119 FT_Face face)
1121 FT_Bool started = 0, same_width = 1;
1122 FT_Fixed advance = 0, old_advance = 0;
1124 void* shaper_buf;
1126 /* in all supported charmaps, digits have character codes 0x30-0x39 */
1127 const char digits[] = "0 1 2 3 4 5 6 7 8 9";
1128 const char* p;
1131 p = digits;
1132 shaper_buf = ta_shaper_buf_create(face);
1134 while (*p)
1136 FT_ULong glyph_index;
1137 unsigned int num_idx;
1140 /* reject input that maps to more than a single glyph */
1141 p = ta_shaper_get_cluster(p, &metrics->root, shaper_buf, &num_idx);
1142 if (num_idx > 1)
1143 continue;
1145 glyph_index = ta_shaper_get_elem(&metrics->root,
1146 shaper_buf,
1148 &advance,
1149 NULL);
1150 if (!glyph_index)
1151 continue;
1153 if (started)
1155 if (advance != old_advance)
1157 same_width = 0;
1158 break;
1161 else
1163 old_advance = advance;
1164 started = 1;
1168 ta_shaper_buf_destroy(face, shaper_buf);
1170 metrics->root.digits_have_same_width = same_width;
1174 /* initialize global metrics */
1176 FT_Error
1177 ta_latin_metrics_init(TA_LatinMetrics metrics,
1178 FT_Face face,
1179 FT_Face reference)
1181 FT_CharMap oldmap = face->charmap;
1184 metrics->units_per_em = face->units_per_EM;
1186 if (!FT_Select_Charmap(face, FT_ENCODING_UNICODE))
1188 ta_latin_metrics_init_widths(metrics, face, 1);
1189 ta_latin_metrics_init_blues(metrics, reference ? reference : face);
1190 ta_latin_metrics_check_digits(metrics, face);
1192 else
1194 /* we only have a symbol font encoding */
1195 ta_latin_metrics_init_widths(metrics, face, 0);
1198 FT_Set_Charmap(face, oldmap);
1199 return FT_Err_Ok;
1203 /* adjust scaling value, then scale and shift widths */
1204 /* and blue zones (if applicable) for given dimension */
1206 static void
1207 ta_latin_metrics_scale_dim(TA_LatinMetrics metrics,
1208 TA_Scaler scaler,
1209 TA_Dimension dim)
1211 FT_Fixed scale;
1212 FT_Pos delta;
1213 TA_LatinAxis axis;
1214 FT_UInt ppem;
1215 FT_UInt nn;
1218 ppem = metrics->root.scaler.face->size->metrics.x_ppem;
1220 if (dim == TA_DIMENSION_HORZ)
1222 scale = scaler->x_scale;
1223 delta = scaler->x_delta;
1225 else
1227 scale = scaler->y_scale;
1228 delta = scaler->y_delta;
1231 axis = &metrics->axis[dim];
1233 if (axis->org_scale == scale && axis->org_delta == delta)
1234 return;
1236 axis->org_scale = scale;
1237 axis->org_delta = delta;
1239 /* correct Y scale to optimize the alignment of the top of */
1240 /* small letters to the pixel grid */
1241 /* (if we do x-height snapping for this ppem value) */
1242 if (!number_set_is_element(
1243 metrics->root.globals->font->x_height_snapping_exceptions,
1244 (int)ppem))
1246 TA_LatinAxis Axis = &metrics->axis[TA_DIMENSION_VERT];
1247 TA_LatinBlue blue = NULL;
1250 for (nn = 0; nn < Axis->blue_count; nn++)
1252 if (Axis->blues[nn].flags & TA_LATIN_BLUE_ADJUSTMENT)
1254 blue = &Axis->blues[nn];
1255 break;
1259 if (blue)
1261 FT_Pos scaled;
1262 FT_Pos threshold;
1263 FT_Pos fitted;
1264 FT_UInt limit;
1267 scaled = FT_MulFix(blue->shoot.org, scale);
1268 limit = metrics->root.globals->increase_x_height;
1269 threshold = 40;
1271 /* if the `increase-x-height' property is active, */
1272 /* we round up much more often */
1273 if (limit
1274 && ppem <= limit
1275 && ppem >= TA_PROP_INCREASE_X_HEIGHT_MIN)
1276 threshold = 52;
1278 fitted = (scaled + threshold) & ~63;
1280 if (scaled != fitted)
1282 if (dim == TA_DIMENSION_VERT)
1284 FT_Pos max_height;
1285 FT_Pos dist;
1286 FT_Fixed new_scale;
1289 new_scale = FT_MulDiv(scale, fitted, scaled);
1291 /* the scaling should not change the result by more than two pixels */
1292 max_height = metrics->units_per_em;
1294 for (nn = 0; nn < Axis->blue_count; nn++)
1296 max_height = TA_MAX(max_height, Axis->blues[nn].ascender);
1297 max_height = TA_MAX(max_height, -Axis->blues[nn].descender);
1300 dist = TA_ABS(FT_MulFix(max_height, new_scale - scale));
1301 dist &= ~127;
1303 if (dist == 0)
1305 TA_LOG_GLOBAL((
1306 "ta_latin_metrics_scale_dim:"
1307 " x height alignment (style `%s'):\n"
1309 " vertical scaling changed from %.5f to %.5f (by %d%%)\n"
1310 "\n",
1311 ta_style_names[metrics->root.style_class->style],
1312 scale / 65536.0,
1313 new_scale / 65536.0,
1314 (fitted - scaled) * 100 / scaled));
1316 scale = new_scale;
1318 #ifdef TA_DEBUG
1319 else
1321 TA_LOG_GLOBAL((
1322 "ta_latin_metrics_scale_dim:"
1323 " x height alignment (style `%s'):\n"
1325 " excessive vertical scaling abandoned\n"
1326 "\n",
1327 ta_style_names[metrics->root.style_class->style]));
1329 #endif
1335 axis->scale = scale;
1336 axis->delta = delta;
1338 if (dim == TA_DIMENSION_HORZ)
1340 metrics->root.scaler.x_scale = scale;
1341 metrics->root.scaler.x_delta = delta;
1343 else
1345 metrics->root.scaler.y_scale = scale;
1346 metrics->root.scaler.y_delta = delta;
1349 TA_LOG_GLOBAL(("%s widths (style `%s')\n",
1350 dim == TA_DIMENSION_HORZ ? "horizontal" : "vertical",
1351 ta_style_names[metrics->root.style_class->style]));
1353 /* scale the widths */
1354 for (nn = 0; nn < axis->width_count; nn++)
1356 TA_Width width = axis->widths + nn;
1359 width->cur = FT_MulFix(width->org, scale);
1360 width->fit = width->cur;
1362 TA_LOG_GLOBAL((" %d scaled to %.2f\n",
1363 width->org,
1364 width->cur / 64.0));
1367 TA_LOG_GLOBAL(("\n"));
1369 /* an extra-light axis corresponds to a standard width that is */
1370 /* smaller than 5/8 pixels */
1371 axis->extra_light =
1372 (FT_Bool)(FT_MulFix(axis->standard_width, scale) < 32 + 8);
1374 #ifdef TA_DEBUG
1375 if (axis->extra_light)
1376 TA_LOG_GLOBAL(("`%s' style is extra light (at current resolution)\n"
1377 "\n",
1378 ta_style_names[metrics->root.style_class->style]));
1379 #endif
1381 if (dim == TA_DIMENSION_VERT)
1383 #ifdef TA_DEBUG
1384 if (axis->blue_count)
1385 TA_LOG_GLOBAL(("blue zones (style `%s')\n",
1386 ta_style_names[metrics->root.style_class->style]));
1387 #endif
1389 /* scale the blue zones */
1390 for (nn = 0; nn < axis->blue_count; nn++)
1392 TA_LatinBlue blue = &axis->blues[nn];
1393 FT_Pos dist;
1396 blue->ref.cur = FT_MulFix(blue->ref.org, scale) + delta;
1397 blue->ref.fit = blue->ref.cur;
1398 blue->shoot.cur = FT_MulFix(blue->shoot.org, scale) + delta;
1399 blue->shoot.fit = blue->shoot.cur;
1400 blue->flags &= ~TA_LATIN_BLUE_ACTIVE;
1402 /* a blue zone is only active if it is less than 3/4 pixels tall */
1403 dist = FT_MulFix(blue->ref.org - blue->shoot.org, scale);
1404 if (dist <= 48 && dist >= -48)
1406 #if 0
1407 FT_Pos delta1;
1408 #endif
1409 FT_Pos delta2;
1412 /* use discrete values for blue zone widths */
1414 #if 0
1415 /* generic, original code */
1416 delta1 = blue->shoot.org - blue->ref.org;
1417 delta2 = delta1;
1418 if (delta1 < 0)
1419 delta2 = -delta2;
1421 delta2 = FT_MulFix(delta2, scale);
1423 if (delta2 < 32)
1424 delta2 = 0;
1425 else if (delta2 < 64)
1426 delta2 = 32 + (((delta2 - 32) + 16) & ~31);
1427 else
1428 delta2 = TA_PIX_ROUND(delta2);
1430 if (delta1 < 0)
1431 delta2 = -delta2;
1433 blue->ref.fit = TA_PIX_ROUND(blue->ref.cur);
1434 blue->shoot.fit = blue->ref.fit + delta2;
1435 #else
1436 /* simplified version due to abs(dist) <= 48 */
1437 delta2 = dist;
1438 if (dist < 0)
1439 delta2 = -delta2;
1441 if (delta2 < 32)
1442 delta2 = 0;
1443 else if (delta2 < 48)
1444 delta2 = 32;
1445 else
1446 delta2 = 64;
1448 if (dist < 0)
1449 delta2 = -delta2;
1451 blue->ref.fit = TA_PIX_ROUND(blue->ref.cur);
1452 blue->shoot.fit = blue->ref.fit - delta2;
1453 #endif
1455 blue->flags |= TA_LATIN_BLUE_ACTIVE;
1459 /* use sub-top blue zone only if it doesn't overlap with */
1460 /* another (non-sup-top) blue zone; otherwise, the */
1461 /* effect would be similar to a neutral blue zone, which */
1462 /* is not desired here */
1463 for (nn = 0; nn < axis->blue_count; nn++)
1465 TA_LatinBlue blue = &axis->blues[nn];
1466 FT_UInt i;
1469 if (!(blue->flags & TA_LATIN_BLUE_SUB_TOP))
1470 continue;
1471 if (!(blue->flags & TA_LATIN_BLUE_ACTIVE))
1472 continue;
1474 for (i = 0; i < axis->blue_count; i++)
1476 TA_LatinBlue b = &axis->blues[i];
1479 if (b->flags & TA_LATIN_BLUE_SUB_TOP)
1480 continue;
1481 if (!(b->flags & TA_LATIN_BLUE_ACTIVE))
1482 continue;
1484 if (b->ref.fit <= blue->shoot.fit
1485 && b->shoot.fit >= blue->ref.fit)
1487 blue->flags &= ~TA_LATIN_BLUE_ACTIVE;
1488 break;
1493 #ifdef TA_DEBUG
1494 for (nn = 0; nn < axis->blue_count; nn++)
1496 TA_LatinBlue blue = &axis->blues[nn];
1499 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f%s\n"
1500 " overshoot %d: %d scaled to %.2f%s\n",
1502 blue->ref.org,
1503 blue->ref.fit / 64.0,
1504 (blue->flags & TA_LATIN_BLUE_ACTIVE) ? ""
1505 : " (inactive)",
1507 blue->shoot.org,
1508 blue->shoot.fit / 64.0,
1509 (blue->flags & TA_LATIN_BLUE_ACTIVE) ? ""
1510 : " (inactive)"));
1512 #endif
1514 /* the last two artificial blue zones are to be scaled */
1515 /* with uncorrected scaling values */
1516 if (metrics->root.globals->font->windows_compatibility)
1518 TA_LatinAxis a = &metrics->axis[TA_DIMENSION_VERT];
1519 TA_LatinBlue b;
1522 b = &a->blues[a->blue_count];
1523 b->ref.cur =
1524 b->ref.fit =
1525 b->shoot.cur =
1526 b->shoot.fit = FT_MulFix(b->ref.org, a->org_scale) + delta;
1528 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f (artificial)\n"
1529 " overshoot %d: %d scaled to %.2f (artificial)\n",
1530 a->blue_count,
1531 b->ref.org,
1532 b->ref.fit / 64.0,
1533 a->blue_count,
1534 b->shoot.org,
1535 b->shoot.fit / 64.0));
1537 b = &a->blues[a->blue_count + 1];
1538 b->ref.cur =
1539 b->ref.fit =
1540 b->shoot.cur =
1541 b->shoot.fit = FT_MulFix(b->ref.org, a->org_scale) + delta;
1543 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f (artificial)\n"
1544 " overshoot %d: %d scaled to %.2f (artificial)\n",
1545 a->blue_count + 1,
1546 b->ref.org,
1547 b->ref.fit / 64.0,
1548 a->blue_count + 1,
1549 b->shoot.org,
1550 b->shoot.fit / 64.0));
1553 TA_LOG_GLOBAL(("\n"));
1558 /* scale global values in both directions */
1560 void
1561 ta_latin_metrics_scale(TA_LatinMetrics metrics,
1562 TA_Scaler scaler)
1564 metrics->root.scaler.render_mode = scaler->render_mode;
1565 metrics->root.scaler.face = scaler->face;
1566 metrics->root.scaler.flags = scaler->flags;
1568 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_HORZ);
1569 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_VERT);
1573 /* walk over all contours and compute its segments */
1575 FT_Error
1576 ta_latin_hints_compute_segments(TA_GlyphHints hints,
1577 TA_Dimension dim)
1579 TA_LatinMetrics metrics = (TA_LatinMetrics)hints->metrics;
1580 TA_AxisHints axis = &hints->axis[dim];
1581 FT_Error error = FT_Err_Ok;
1583 TA_Segment segment = NULL;
1584 TA_SegmentRec seg0;
1586 TA_Point* contour = hints->contours;
1587 TA_Point* contour_limit = contour + hints->num_contours;
1588 TA_Direction major_dir, segment_dir;
1590 FT_Pos flat_threshold = FLAT_THRESHOLD(metrics->units_per_em);
1593 memset(&seg0, 0, sizeof (TA_SegmentRec));
1594 seg0.score = 32000;
1595 seg0.flags = TA_EDGE_NORMAL;
1597 major_dir = (TA_Direction)TA_ABS(axis->major_dir);
1598 segment_dir = major_dir;
1600 axis->num_segments = 0;
1602 /* set up (u,v) in each point */
1603 if (dim == TA_DIMENSION_HORZ)
1605 TA_Point point = hints->points;
1606 TA_Point limit = point + hints->num_points;
1609 for (; point < limit; point++)
1611 point->u = point->fx;
1612 point->v = point->fy;
1615 else
1617 TA_Point point = hints->points;
1618 TA_Point limit = point + hints->num_points;
1621 for (; point < limit; point++)
1623 point->u = point->fy;
1624 point->v = point->fx;
1628 /* do each contour separately */
1629 for (; contour < contour_limit; contour++)
1631 TA_Point point = contour[0];
1632 TA_Point last = point->prev;
1634 int on_edge = 0;
1636 /* we call values measured along a segment (point->v) */
1637 /* `coordinates', and values orthogonal to it (point->u) */
1638 /* `positions' */
1639 FT_Pos min_pos = 32000;
1640 FT_Pos max_pos = -32000;
1641 FT_Pos min_coord = 32000;
1642 FT_Pos max_coord = -32000;
1643 FT_UShort min_flags = TA_FLAG_NONE;
1644 FT_UShort max_flags = TA_FLAG_NONE;
1645 FT_Pos min_on_coord = 32000;
1646 FT_Pos max_on_coord = -32000;
1648 FT_Bool passed;
1650 TA_Segment prev_segment = NULL;
1652 FT_Pos prev_min_pos = min_pos;
1653 FT_Pos prev_max_pos = max_pos;
1654 FT_Pos prev_min_coord = min_coord;
1655 FT_Pos prev_max_coord = max_coord;
1656 FT_UShort prev_min_flags = min_flags;
1657 FT_UShort prev_max_flags = max_flags;
1658 FT_Pos prev_min_on_coord = min_on_coord;
1659 FT_Pos prev_max_on_coord = max_on_coord;
1662 if (TA_ABS(last->out_dir) == major_dir
1663 && TA_ABS(point->out_dir) == major_dir)
1665 /* we are already on an edge, try to locate its start */
1666 last = point;
1668 for (;;)
1670 point = point->prev;
1671 if (TA_ABS(point->out_dir) != major_dir)
1673 point = point->next;
1674 break;
1676 if (point == last)
1677 break;
1681 last = point;
1682 passed = 0;
1684 for (;;)
1686 FT_Pos u, v;
1689 if (on_edge)
1691 /* get minimum and maximum position */
1692 u = point->u;
1693 if (u < min_pos)
1694 min_pos = u;
1695 if (u > max_pos)
1696 max_pos = u;
1698 /* get minimum and maximum coordinate together with flags */
1699 v = point->v;
1700 if (v < min_coord)
1702 min_coord = v;
1703 min_flags = point->flags;
1705 if (v > max_coord)
1707 max_coord = v;
1708 max_flags = point->flags;
1711 /* get minimum and maximum coordinate of `on' points */
1712 if (!(point->flags & TA_FLAG_CONTROL))
1714 v = point->v;
1715 if (v < min_on_coord)
1716 min_on_coord = v;
1717 if (v > max_on_coord)
1718 max_on_coord = v;
1721 if (point->out_dir != segment_dir
1722 || point == last)
1724 /* check whether the new segment's start point is identical to */
1725 /* the previous segment's end point; for example, this might */
1726 /* happen for spikes */
1728 if (!prev_segment
1729 || segment->first != prev_segment->last)
1731 /* points are different: we are just leaving an edge, thus */
1732 /* record a new segment */
1734 segment->last = point;
1735 segment->pos = (FT_Short)((min_pos + max_pos) >> 1);
1736 segment->delta = (FT_Short)((max_pos - min_pos) >> 1);
1738 /* a segment is round if either its first or last point */
1739 /* is a control point, and the length of the on points */
1740 /* inbetween doesn't exceed a heuristic limit */
1741 if ((min_flags | max_flags) & TA_FLAG_CONTROL
1742 && (max_on_coord - min_on_coord) < flat_threshold)
1743 segment->flags |= TA_EDGE_ROUND;
1745 segment->min_coord = (FT_Short)min_coord;
1746 segment->max_coord = (FT_Short)max_coord;
1747 segment->height = segment->max_coord - segment->min_coord;
1749 prev_segment = segment;
1750 prev_min_pos = min_pos;
1751 prev_max_pos = max_pos;
1752 prev_min_coord = min_coord;
1753 prev_max_coord = max_coord;
1754 prev_min_flags = min_flags;
1755 prev_max_flags = max_flags;
1756 prev_min_on_coord = min_on_coord;
1757 prev_max_on_coord = max_on_coord;
1759 else
1761 /* points are the same: we don't create a new segment but */
1762 /* merge the current segment with the previous one */
1764 if (prev_segment->last->in_dir == point->in_dir)
1766 /* we have identical directions (this can happen for */
1767 /* degenerate outlines that move zig-zag along the main */
1768 /* axis without changing the coordinate value of the other */
1769 /* axis, and where the segments have just been merged): */
1770 /* unify segments */
1772 /* update constraints */
1774 if (prev_min_pos < min_pos)
1775 min_pos = prev_min_pos;
1776 if (prev_max_pos > max_pos)
1777 max_pos = prev_max_pos;
1779 if (prev_min_coord < min_coord)
1781 min_coord = prev_min_coord;
1782 min_flags = prev_min_flags;
1784 if (prev_max_coord > max_coord)
1786 max_coord = prev_max_coord;
1787 max_flags = prev_max_flags;
1790 if (prev_min_on_coord < min_on_coord)
1791 min_on_coord = prev_min_on_coord;
1792 if (prev_max_on_coord > max_on_coord)
1793 max_on_coord = prev_max_on_coord;
1795 prev_segment->last = point;
1796 prev_segment->pos = (FT_Short)((min_pos + max_pos) >> 1);
1798 if ((min_flags | max_flags) & TA_FLAG_CONTROL
1799 && (max_on_coord - min_on_coord) < flat_threshold)
1800 prev_segment->flags |= TA_EDGE_ROUND;
1801 else
1802 prev_segment->flags &= ~TA_EDGE_ROUND;
1804 prev_segment->min_coord = (FT_Short)min_coord;
1805 prev_segment->max_coord = (FT_Short)max_coord;
1806 prev_segment->height = prev_segment->max_coord
1807 - prev_segment->min_coord;
1809 else
1811 /* we have different directions; use the properties of the */
1812 /* longer segment and discard the other one */
1814 if (TA_ABS(prev_max_coord - prev_min_coord)
1815 > TA_ABS(max_coord - min_coord))
1817 /* discard current segment */
1819 if (min_pos < prev_min_pos)
1820 prev_min_pos = min_pos;
1821 if (max_pos > prev_max_pos)
1822 prev_max_pos = max_pos;
1824 prev_segment->last = point;
1825 prev_segment->pos = (FT_Short)((prev_min_pos
1826 + prev_max_pos) >> 1);
1828 else
1830 /* discard previous segment */
1832 if (prev_min_pos < min_pos)
1833 min_pos = prev_min_pos;
1834 if (prev_max_pos > max_pos)
1835 max_pos = prev_max_pos;
1837 segment->last = point;
1838 segment->pos = (FT_Short)((min_pos + max_pos) >> 1);
1840 if ((min_flags | max_flags) & TA_FLAG_CONTROL
1841 && (max_on_coord - min_on_coord) < flat_threshold)
1842 segment->flags |= TA_EDGE_ROUND;
1844 segment->min_coord = (FT_Short)min_coord;
1845 segment->max_coord = (FT_Short)max_coord;
1846 segment->height = segment->max_coord - segment->min_coord;
1848 *prev_segment = *segment;
1850 prev_min_pos = min_pos;
1851 prev_max_pos = max_pos;
1852 prev_min_coord = min_coord;
1853 prev_max_coord = max_coord;
1854 prev_min_flags = min_flags;
1855 prev_max_flags = max_flags;
1856 prev_min_on_coord = min_on_coord;
1857 prev_max_on_coord = max_on_coord;
1861 axis->num_segments--;
1864 on_edge = 0;
1865 segment = NULL;
1867 /* fall through */
1871 /* now exit if we are at the start/end point */
1872 if (point == last)
1874 if (passed)
1875 break;
1876 passed = 1;
1879 /* if we are not on an edge, check whether the major direction */
1880 /* coincides with the current point's `out' direction, or */
1881 /* whether we have a single-point contour */
1882 if (!on_edge
1883 && (TA_ABS(point->out_dir) == major_dir
1884 || point == point->prev))
1887 * For efficiency, we restrict the number of segments to 1000,
1888 * which is a heuristic value: it is very unlikely that a glyph
1889 * with so many segments can be hinted in a sensible way.
1890 * Reasons:
1892 * - The glyph has really 1000 segments; this implies that it has
1893 * at least 2000 outline points. Assuming 'normal' fonts that
1894 * have superfluous points optimized away, viewing such a glyph
1895 * only makes sense at large magnifications where hinting
1896 * isn't applied anyway.
1898 * - We have a broken glyph. Hinting doesn't make sense in this
1899 * case either.
1901 if (axis->num_segments > 1000)
1903 TA_LOG_GLOBAL(("ta_latin_hints_compute_segments:"
1904 " more than 1000 segments in this glyph;\n"
1906 " hinting is suppressed\n"));
1907 axis->num_segments = 0;
1908 return FT_Err_Ok;
1911 /* this is the start of a new segment! */
1912 segment_dir = (TA_Direction)point->out_dir;
1914 error = ta_axis_hints_new_segment(axis, &segment);
1915 if (error)
1916 goto Exit;
1918 /* clear all segment fields */
1919 segment[0] = seg0;
1921 segment->dir = (FT_Char)segment_dir;
1922 segment->first = point;
1923 segment->last = point;
1925 /* `ta_axis_hints_new_segment' reallocates memory, */
1926 /* thus we have to refresh the `prev_segment' pointer */
1927 if (prev_segment)
1928 prev_segment = segment - 1;
1930 min_pos = max_pos = point->u;
1931 min_coord = max_coord = point->v;
1932 min_flags = max_flags = point->flags;
1934 if (point->flags & TA_FLAG_CONTROL)
1936 min_on_coord = 32000;
1937 max_on_coord = -32000;
1939 else
1940 min_on_coord = max_on_coord = point->v;
1942 on_edge = 1;
1944 if (point->out_dir != point->next->in_dir
1945 || point == point->prev)
1948 * We have a one-point segment. This is either
1950 * . a one-point contour (with `in' and `out' direction
1951 * set to TA_DIR_NONE by default), or
1953 * . an artificial one-point segment (with a forced
1954 * `out' direction).
1956 segment->pos = (FT_Short)min_pos;
1958 if (point->flags & TA_FLAG_CONTROL)
1959 segment->flags |= TA_EDGE_ROUND;
1961 /* artificially extend the horizontal size if requested */
1962 segment->min_coord = (FT_Short)point->v + point->left_offset;
1963 segment->max_coord = (FT_Short)point->v + point->right_offset;
1964 segment->height = 0;
1966 on_edge = 0;
1967 segment = NULL;
1971 point = point->next;
1973 } /* contours */
1976 /* now slightly increase the height of segments if this makes sense -- */
1977 /* this is used to better detect and ignore serifs */
1979 TA_Segment segments = axis->segments;
1980 TA_Segment segments_end = segments + axis->num_segments;
1983 for (segment = segments; segment < segments_end; segment++)
1985 TA_Point first = segment->first;
1986 TA_Point last = segment->last;
1988 FT_Pos first_v = first->v;
1989 FT_Pos last_v = last->v;
1992 if (first_v < last_v)
1994 TA_Point p;
1997 p = first->prev;
1998 if (p->v < first_v)
1999 segment->height = (FT_Short)(segment->height +
2000 ((first_v - p->v) >> 1));
2002 p = last->next;
2003 if (p->v > last_v)
2004 segment->height = (FT_Short)(segment->height +
2005 ((p->v - last_v) >> 1));
2007 else
2009 TA_Point p;
2012 p = first->prev;
2013 if (p->v > first_v)
2014 segment->height = (FT_Short)(segment->height +
2015 ((p->v - first_v) >> 1));
2017 p = last->next;
2018 if (p->v < last_v)
2019 segment->height = (FT_Short)(segment->height +
2020 ((last_v - p->v) >> 1));
2025 Exit:
2026 return error;
2030 /* link segments to form stems and serifs; if `width_count' and */
2031 /* `widths' are non-zero, use them to fine-tune the scoring function */
2033 void
2034 ta_latin_hints_link_segments(TA_GlyphHints hints,
2035 FT_UInt width_count,
2036 TA_WidthRec* widths,
2037 TA_Dimension dim)
2039 TA_AxisHints axis = &hints->axis[dim];
2041 TA_Segment segments = axis->segments;
2042 TA_Segment segment_limit = segments + axis->num_segments;
2044 FT_Pos len_threshold, len_score, dist_score, max_width;
2045 TA_Segment seg1, seg2;
2048 if (width_count)
2049 max_width = widths[width_count - 1].org;
2050 else
2051 max_width = 0;
2053 /* a heuristic value to set up a minimum value for overlapping */
2054 len_threshold = TA_LATIN_CONSTANT(hints->metrics, 8);
2055 if (len_threshold == 0)
2056 len_threshold = 1;
2058 /* a heuristic value to weight lengths */
2059 len_score = TA_LATIN_CONSTANT(hints->metrics, 6000);
2061 /* a heuristic value to weight distances (no call to */
2062 /* TA_LATIN_CONSTANT needed, since we work on multiples */
2063 /* of the stem width) */
2064 dist_score = 3000;
2066 /* now compare each segment to the others */
2067 for (seg1 = segments; seg1 < segment_limit; seg1++)
2069 if (seg1->dir != axis->major_dir)
2070 continue;
2072 /* search for stems having opposite directions, */
2073 /* with seg1 to the `left' of seg2 */
2074 for (seg2 = segments; seg2 < segment_limit; seg2++)
2076 FT_Pos pos1 = seg1->pos;
2077 FT_Pos pos2 = seg2->pos;
2080 if (seg1->dir + seg2->dir == 0
2081 && pos2 > pos1)
2083 /* compute distance between the two segments */
2084 FT_Pos min = seg1->min_coord;
2085 FT_Pos max = seg1->max_coord;
2086 FT_Pos len;
2089 if (min < seg2->min_coord)
2090 min = seg2->min_coord;
2091 if (max > seg2->max_coord)
2092 max = seg2->max_coord;
2094 /* compute maximum coordinate difference of the two segments */
2095 /* (this is, how much they overlap) */
2096 len = max - min;
2098 /* for one-point segments, `len' is zero if there is an overlap */
2099 /* (and negative otherwise); we have to correct this */
2100 if (len == 0
2101 && (seg1->min_coord == seg1->max_coord
2102 || seg2->min_coord == seg2->max_coord))
2103 len = len_threshold;
2105 if (len >= len_threshold)
2108 * The score is the sum of two demerits indicating the
2109 * `badness' of a fit, measured along the segments' main axis
2110 * and orthogonal to it, respectively.
2112 * o The less overlapping along the main axis, the worse it
2113 * is, causing a larger demerit.
2115 * o The nearer the orthogonal distance to a stem width, the
2116 * better it is, causing a smaller demerit. For simplicity,
2117 * however, we only increase the demerit for values that
2118 * exceed the largest stem width.
2121 FT_Pos dist = pos2 - pos1;
2123 FT_Pos dist_demerit, score;
2126 if (max_width)
2128 /* distance demerits are based on multiples of `max_width'; */
2129 /* we scale by 1024 for getting more precision */
2130 FT_Pos delta = (dist << 10) / max_width - (1 << 10);
2133 if (delta > 10000)
2134 dist_demerit = 32000;
2135 else if (delta > 0)
2136 dist_demerit = delta * delta / dist_score;
2137 else
2138 dist_demerit = 0;
2140 else
2141 dist_demerit = dist; /* default if no widths available */
2143 score = dist_demerit + len_score / len;
2145 /* and we search for the smallest score */
2146 if (score < seg1->score)
2148 seg1->score = score;
2149 seg1->link = seg2;
2152 if (score < seg2->score)
2154 seg2->score = score;
2155 seg2->link = seg1;
2162 /* now compute the `serif' segments, cf. explanations in `tahints.h' */
2163 for (seg1 = segments; seg1 < segment_limit; seg1++)
2165 seg2 = seg1->link;
2167 if (seg2)
2169 if (seg2->link != seg1)
2171 seg1->link = 0;
2172 seg1->serif = seg2->link;
2179 /* link segments to edges, using feature analysis for selection */
2181 FT_Error
2182 ta_latin_hints_compute_edges(TA_GlyphHints hints,
2183 TA_Dimension dim)
2185 TA_AxisHints axis = &hints->axis[dim];
2186 FT_Error error = FT_Err_Ok;
2187 TA_LatinAxis laxis = &((TA_LatinMetrics)hints->metrics)->axis[dim];
2189 TA_StyleClass style_class = hints->metrics->style_class;
2190 TA_ScriptClass script_class = ta_script_classes[style_class->script];
2192 FT_Bool top_to_bottom_hinting = 0;
2194 TA_Segment segments = axis->segments;
2195 TA_Segment segment_limit = segments + axis->num_segments;
2196 TA_Segment seg;
2198 #if 0
2199 TA_Direction up_dir;
2200 #endif
2201 FT_Fixed scale;
2202 FT_Pos edge_distance_threshold;
2203 FT_Pos segment_length_threshold;
2204 FT_Pos segment_width_threshold;
2207 axis->num_edges = 0;
2209 scale = (dim == TA_DIMENSION_HORZ) ? hints->x_scale
2210 : hints->y_scale;
2212 #if 0
2213 up_dir = (dim == TA_DIMENSION_HORZ) ? TA_DIR_UP
2214 : TA_DIR_RIGHT;
2215 #endif
2217 if (dim == TA_DIMENSION_VERT)
2218 top_to_bottom_hinting = script_class->top_to_bottom_hinting;
2220 /* we ignore all segments that are less than 1 pixel in length */
2221 /* to avoid many problems with serif fonts */
2222 /* (the corresponding threshold is computed in font units) */
2223 if (dim == TA_DIMENSION_HORZ)
2224 segment_length_threshold = FT_DivFix(64, hints->y_scale);
2225 else
2226 segment_length_threshold = 0;
2229 * Similarly, we ignore segments that have a width delta
2230 * larger than 0.5px (i.e., a width larger than 1px).
2232 segment_width_threshold = FT_DivFix(32, scale);
2234 /********************************************************************/
2235 /* */
2236 /* We begin by generating a sorted table of edges for the current */
2237 /* direction. To do so, we simply scan each segment and try to find */
2238 /* an edge in our table that corresponds to its position. */
2239 /* */
2240 /* If no edge is found, we create and insert a new edge in the */
2241 /* sorted table. Otherwise, we simply add the segment to the edge's */
2242 /* list which gets processed in the second step to compute the */
2243 /* edge's properties. */
2244 /* */
2245 /* Note that the table of edges is sorted along the segment/edge */
2246 /* position. */
2247 /* */
2248 /********************************************************************/
2250 /* assure that edge distance threshold is at most 0.25px */
2251 edge_distance_threshold = FT_MulFix(laxis->edge_distance_threshold,
2252 scale);
2253 if (edge_distance_threshold > 64 / 4)
2254 edge_distance_threshold = 64 / 4;
2256 edge_distance_threshold = FT_DivFix(edge_distance_threshold,
2257 scale);
2259 for (seg = segments; seg < segment_limit; seg++)
2261 TA_Edge found = NULL;
2262 FT_Int ee;
2265 /* ignore too short segments, too wide ones, and, in this loop, */
2266 /* one-point segments without a direction */
2267 if (seg->height < segment_length_threshold
2268 || seg->delta > segment_width_threshold
2269 || seg->dir == TA_DIR_NONE)
2270 continue;
2272 /* a special case for serif edges: */
2273 /* if they are smaller than 1.5 pixels we ignore them */
2274 if (seg->serif
2275 && 2 * seg->height < 3 * segment_length_threshold)
2276 continue;
2278 /* look for an edge corresponding to the segment */
2279 for (ee = 0; ee < axis->num_edges; ee++)
2281 TA_Edge edge = axis->edges + ee;
2282 FT_Pos dist;
2285 dist = seg->pos - edge->fpos;
2286 if (dist < 0)
2287 dist = -dist;
2289 if (dist < edge_distance_threshold && edge->dir == seg->dir)
2291 found = edge;
2292 break;
2296 if (!found)
2298 TA_Edge edge;
2301 /* insert a new edge in the list and sort according to the position */
2302 error = ta_axis_hints_new_edge(axis, seg->pos,
2303 (TA_Direction)seg->dir,
2304 top_to_bottom_hinting,
2305 &edge);
2306 if (error)
2307 goto Exit;
2309 /* add the segment to the new edge's list */
2310 memset(edge, 0, sizeof (TA_EdgeRec));
2311 edge->first = seg;
2312 edge->last = seg;
2313 edge->dir = seg->dir;
2314 edge->fpos = seg->pos;
2315 edge->opos = FT_MulFix(seg->pos, scale);
2316 edge->pos = edge->opos;
2317 seg->edge_next = seg;
2319 else
2321 /* if an edge was found, simply add the segment to the edge's list */
2322 seg->edge_next = found->first;
2323 found->last->edge_next = seg;
2324 found->last = seg;
2328 /* we loop again over all segments to catch one-point segments */
2329 /* without a direction: if possible, link them to existing edges */
2330 for (seg = segments; seg < segment_limit; seg++)
2332 TA_Edge found = NULL;
2333 FT_Int ee;
2336 if (seg->dir != TA_DIR_NONE)
2337 continue;
2339 /* look for an edge corresponding to the segment */
2340 for (ee = 0; ee < axis->num_edges; ee++)
2342 TA_Edge edge = axis->edges + ee;
2343 FT_Pos dist;
2346 dist = seg->pos - edge->fpos;
2347 if (dist < 0)
2348 dist = -dist;
2350 if (dist < edge_distance_threshold)
2352 found = edge;
2353 break;
2357 /* one-point segments without a match are ignored */
2358 if (found)
2360 seg->edge_next = found->first;
2361 found->last->edge_next = seg;
2362 found->last = seg;
2366 /*****************************************************************/
2367 /* */
2368 /* Good, we now compute each edge's properties according to */
2369 /* the segments found on its position. Basically, these are */
2370 /* */
2371 /* - the edge's main direction */
2372 /* - stem edge, serif edge or both (which defaults to stem then) */
2373 /* - rounded edge, straight or both (which defaults to straight) */
2374 /* - link for edge */
2375 /* */
2376 /*****************************************************************/
2378 /* first of all, set the `edge' field in each segment -- this is */
2379 /* required in order to compute edge links */
2381 /* note that removing this loop and setting the `edge' field of each */
2382 /* segment directly in the code above slows down execution speed for */
2383 /* some reasons on platforms like the Sun */
2385 TA_Edge edges = axis->edges;
2386 TA_Edge edge_limit = edges + axis->num_edges;
2387 TA_Edge edge;
2390 for (edge = edges; edge < edge_limit; edge++)
2392 seg = edge->first;
2393 if (seg)
2396 seg->edge = edge;
2397 seg = seg->edge_next;
2398 } while (seg != edge->first);
2401 /* now compute each edge properties */
2402 for (edge = edges; edge < edge_limit; edge++)
2404 FT_Int is_round = 0; /* does it contain round segments? */
2405 FT_Int is_straight = 0; /* does it contain straight segments? */
2406 #if 0
2407 FT_Pos ups = 0; /* number of upwards segments */
2408 FT_Pos downs = 0; /* number of downwards segments */
2409 #endif
2412 seg = edge->first;
2416 FT_Bool is_serif;
2419 /* check for roundness of segment */
2420 if (seg->flags & TA_EDGE_ROUND)
2421 is_round++;
2422 else
2423 is_straight++;
2425 #if 0
2426 /* check for segment direction */
2427 if (seg->dir == up_dir)
2428 ups += seg->max_coord - seg->min_coord;
2429 else
2430 downs += seg->max_coord - seg->min_coord;
2431 #endif
2433 /* check for links -- */
2434 /* if seg->serif is set, then seg->link must be ignored */
2435 is_serif = (FT_Bool)(seg->serif
2436 && seg->serif->edge
2437 && seg->serif->edge != edge);
2439 if ((seg->link && seg->link->edge)
2440 || is_serif)
2442 TA_Edge edge2;
2443 TA_Segment seg2;
2446 edge2 = edge->link;
2447 seg2 = seg->link;
2449 if (is_serif)
2451 seg2 = seg->serif;
2452 edge2 = edge->serif;
2455 if (edge2)
2457 FT_Pos edge_delta;
2458 FT_Pos seg_delta;
2461 edge_delta = edge->fpos - edge2->fpos;
2462 if (edge_delta < 0)
2463 edge_delta = -edge_delta;
2465 seg_delta = seg->pos - seg2->pos;
2466 if (seg_delta < 0)
2467 seg_delta = -seg_delta;
2469 if (seg_delta < edge_delta)
2470 edge2 = seg2->edge;
2472 else
2473 edge2 = seg2->edge;
2475 if (is_serif)
2477 edge->serif = edge2;
2478 edge2->flags |= TA_EDGE_SERIF;
2480 else
2481 edge->link = edge2;
2484 seg = seg->edge_next;
2485 } while (seg != edge->first);
2487 /* set the round/straight flags */
2488 edge->flags = TA_EDGE_NORMAL;
2490 if (is_round > 0
2491 && is_round >= is_straight)
2492 edge->flags |= TA_EDGE_ROUND;
2494 #if 0
2495 /* set the edge's main direction */
2496 edge->dir = TA_DIR_NONE;
2498 if (ups > downs)
2499 edge->dir = (FT_Char)up_dir;
2501 else if (ups < downs)
2502 edge->dir = (FT_Char)-up_dir;
2504 else if (ups == downs)
2505 edge->dir = 0; /* both up and down! */
2506 #endif
2508 /* get rid of serifs if link is set */
2509 /* XXX: this gets rid of many unpleasant artefacts! */
2510 /* example: the `c' in cour.pfa at size 13 */
2512 if (edge->serif && edge->link)
2513 edge->serif = NULL;
2517 Exit:
2518 return error;
2522 /* detect segments and edges for given dimension */
2524 FT_Error
2525 ta_latin_hints_detect_features(TA_GlyphHints hints,
2526 FT_UInt width_count,
2527 TA_WidthRec* widths,
2528 TA_Dimension dim)
2530 FT_Error error;
2533 error = ta_latin_hints_compute_segments(hints, dim);
2534 if (!error)
2536 ta_latin_hints_link_segments(hints, width_count, widths, dim);
2538 error = ta_latin_hints_compute_edges(hints, dim);
2541 return error;
2545 /* compute all edges which lie within blue zones */
2547 static void
2548 ta_latin_hints_compute_blue_edges(TA_GlyphHints hints,
2549 TA_LatinMetrics metrics)
2551 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
2553 TA_Edge edge = axis->edges;
2554 TA_Edge edge_limit = edge + axis->num_edges;
2556 TA_LatinAxis latin = &metrics->axis[TA_DIMENSION_VERT];
2557 FT_Fixed scale = latin->scale;
2560 /* compute which blue zones are active, */
2561 /* i.e. have their scaled size < 3/4 pixels */
2563 /* for each horizontal edge search the blue zone which is closest */
2564 for (; edge < edge_limit; edge++)
2566 FT_UInt bb;
2567 TA_Width best_blue = NULL;
2568 FT_Bool best_blue_is_neutral = 0;
2569 FT_Pos best_dist; /* initial threshold */
2571 FT_UInt best_blue_idx = 0;
2572 FT_Bool best_blue_is_shoot = 0;
2575 /* compute the initial threshold as a fraction of the EM size */
2576 /* (the value 40 is heuristic) */
2577 best_dist = FT_MulFix(metrics->units_per_em / 40, scale);
2579 /* assure a minimum distance of 0.5px */
2580 if (best_dist > 64 / 2)
2581 best_dist = 64 / 2;
2583 /* this loop also handles the two extra blue zones */
2584 /* for usWinAscent and usWinDescent */
2585 /* if option `windows-compatibility' is set */
2586 for (bb = 0;
2587 bb < latin->blue_count
2588 + (metrics->root.globals->font->windows_compatibility ? 2 : 0);
2589 bb++)
2591 TA_LatinBlue blue = latin->blues + bb;
2592 FT_Bool is_top_blue, is_neutral_blue, is_major_dir;
2595 /* skip inactive blue zones (i.e., those that are too large) */
2596 if (!(blue->flags & TA_LATIN_BLUE_ACTIVE))
2597 continue;
2599 /* if it is a top zone, check for right edges (against the major */
2600 /* direction); if it is a bottom zone, check for left edges (in */
2601 /* the major direction) */
2602 is_top_blue = (FT_Byte)((blue->flags & (TA_LATIN_BLUE_TOP
2603 | TA_LATIN_BLUE_SUB_TOP)) != 0);
2604 is_neutral_blue = (FT_Byte)((blue->flags & TA_LATIN_BLUE_NEUTRAL) != 0);
2605 is_major_dir = FT_BOOL(edge->dir == axis->major_dir);
2607 /* neutral blue zones are handled for both directions */
2608 if (is_top_blue ^ is_major_dir || is_neutral_blue)
2610 FT_Pos dist;
2613 /* first of all, compare it to the reference position */
2614 dist = edge->fpos - blue->ref.org;
2615 if (dist < 0)
2616 dist = -dist;
2618 dist = FT_MulFix(dist, scale);
2619 if (dist < best_dist)
2621 best_dist = dist;
2622 best_blue = &blue->ref;
2623 best_blue_is_neutral = is_neutral_blue;
2625 best_blue_idx = bb;
2626 best_blue_is_shoot = 0;
2629 /* now compare it to the overshoot position and check whether */
2630 /* the edge is rounded, and whether the edge is over the */
2631 /* reference position of a top zone, or under the reference */
2632 /* position of a bottom zone (provided we don't have a */
2633 /* neutral blue zone) */
2634 if (edge->flags & TA_EDGE_ROUND
2635 && dist != 0
2636 && !is_neutral_blue)
2638 FT_Bool is_under_ref = FT_BOOL(edge->fpos < blue->ref.org);
2641 if (is_top_blue ^ is_under_ref)
2643 dist = edge->fpos - blue->shoot.org;
2644 if (dist < 0)
2645 dist = -dist;
2647 dist = FT_MulFix(dist, scale);
2648 if (dist < best_dist)
2650 best_dist = dist;
2651 best_blue = &blue->shoot;
2652 best_blue_is_neutral = is_neutral_blue;
2654 best_blue_idx = bb;
2655 best_blue_is_shoot = 1;
2662 if (best_blue)
2664 edge->blue_edge = best_blue;
2665 edge->best_blue_idx = best_blue_idx;
2666 edge->best_blue_is_shoot = best_blue_is_shoot;
2667 if (best_blue_is_neutral)
2668 edge->flags |= TA_EDGE_NEUTRAL;
2674 /* initalize hinting engine */
2676 static FT_Error
2677 ta_latin_hints_init(TA_GlyphHints hints,
2678 TA_LatinMetrics metrics)
2680 FT_Render_Mode mode;
2681 FT_UInt32 scaler_flags, other_flags;
2682 FT_Face face = metrics->root.scaler.face;
2685 ta_glyph_hints_rescale(hints, (TA_StyleMetrics)metrics);
2687 /* correct x_scale and y_scale if needed, since they may have */
2688 /* been modified by `ta_latin_metrics_scale_dim' above */
2689 hints->x_scale = metrics->axis[TA_DIMENSION_HORZ].scale;
2690 hints->x_delta = metrics->axis[TA_DIMENSION_HORZ].delta;
2691 hints->y_scale = metrics->axis[TA_DIMENSION_VERT].scale;
2692 hints->y_delta = metrics->axis[TA_DIMENSION_VERT].delta;
2694 /* compute flags depending on render mode, etc. */
2695 mode = metrics->root.scaler.render_mode;
2697 #if 0 /* #ifdef TA_CONFIG_OPTION_USE_WARPER */
2698 if (mode == FT_RENDER_MODE_LCD
2699 || mode == FT_RENDER_MODE_LCD_V)
2700 metrics->root.scaler.render_mode =
2701 mode = FT_RENDER_MODE_NORMAL;
2702 #endif
2704 scaler_flags = hints->scaler_flags;
2705 other_flags = 0;
2707 /* we snap the width of vertical stems for the monochrome */
2708 /* and horizontal LCD rendering targets only */
2709 if (mode == FT_RENDER_MODE_MONO
2710 || mode == FT_RENDER_MODE_LCD)
2711 other_flags |= TA_LATIN_HINTS_HORZ_SNAP;
2713 /* we snap the width of horizontal stems for the monochrome */
2714 /* and vertical LCD rendering targets only */
2715 if (mode == FT_RENDER_MODE_MONO
2716 || mode == FT_RENDER_MODE_LCD_V)
2717 other_flags |= TA_LATIN_HINTS_VERT_SNAP;
2719 /* we adjust stems to full pixels unless in `light' or `lcd' mode */
2720 if (mode != FT_RENDER_MODE_LIGHT && mode != FT_RENDER_MODE_LCD)
2721 other_flags |= TA_LATIN_HINTS_STEM_ADJUST;
2723 if (mode == FT_RENDER_MODE_MONO)
2724 other_flags |= TA_LATIN_HINTS_MONO;
2726 /* in `light' or `lcd' mode we disable horizontal hinting completely; */
2727 /* we also do it if the face is italic -- */
2728 /* however, if warping is enabled (which only works in `light' hinting */
2729 /* mode), advance widths get adjusted, too */
2730 if (mode == FT_RENDER_MODE_LIGHT || mode == FT_RENDER_MODE_LCD
2731 || (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0)
2732 scaler_flags |= TA_SCALER_FLAG_NO_HORIZONTAL;
2734 #ifdef TA_CONFIG_OPTION_USE_WARPER
2735 /* get (global) warper flag */
2736 if (!metrics->root.globals->module->warping)
2737 scaler_flags |= TA_SCALER_FLAG_NO_WARPER;
2738 #endif
2740 hints->scaler_flags = scaler_flags;
2741 hints->other_flags = other_flags;
2743 return FT_Err_Ok;
2747 /* snap a given width in scaled coordinates to */
2748 /* one of the current standard widths */
2750 static FT_Pos
2751 ta_latin_snap_width(TA_Width widths,
2752 FT_UInt count,
2753 FT_Pos width)
2755 FT_UInt n;
2756 FT_Pos best = 64 + 32 + 2;
2757 FT_Pos reference = width;
2758 FT_Pos scaled;
2761 for (n = 0; n < count; n++)
2763 FT_Pos w;
2764 FT_Pos dist;
2767 w = widths[n].cur;
2768 dist = width - w;
2769 if (dist < 0)
2770 dist = -dist;
2771 if (dist < best)
2773 best = dist;
2774 reference = w;
2778 scaled = TA_PIX_ROUND(reference);
2780 if (width >= reference)
2782 if (width < scaled + 48)
2783 width = reference;
2785 else
2787 if (width > scaled - 48)
2788 width = reference;
2791 return width;
2795 /* compute the snapped width of a given stem, ignoring very thin ones */
2797 /* there is a lot of voodoo in this function; changing the hard-coded */
2798 /* parameters influences the whole hinting process */
2800 static FT_Pos
2801 ta_latin_compute_stem_width(TA_GlyphHints hints,
2802 TA_Dimension dim,
2803 FT_Pos width,
2804 FT_Pos base_delta,
2805 FT_Byte base_flags,
2806 FT_Byte stem_flags)
2808 TA_LatinMetrics metrics = (TA_LatinMetrics) hints->metrics;
2809 TA_LatinAxis axis = &metrics->axis[dim];
2811 FT_Pos dist = width;
2812 FT_Int sign = 0;
2813 FT_Int vertical = (dim == TA_DIMENSION_VERT);
2816 if (!TA_LATIN_HINTS_DO_STEM_ADJUST(hints)
2817 || axis->extra_light)
2818 return width;
2820 if (dist < 0)
2822 dist = -width;
2823 sign = 1;
2826 if ((vertical && !TA_LATIN_HINTS_DO_VERT_SNAP(hints))
2827 || (!vertical && !TA_LATIN_HINTS_DO_HORZ_SNAP(hints)))
2829 /* smooth hinting process: very lightly quantize the stem width */
2831 /* leave the widths of serifs alone */
2832 if ((stem_flags & TA_EDGE_SERIF)
2833 && vertical
2834 && (dist < 3 * 64))
2835 goto Done_Width;
2836 else if (base_flags & TA_EDGE_ROUND)
2838 if (dist < 80)
2839 dist = 64;
2841 else if (dist < 56)
2842 dist = 56;
2844 if (axis->width_count > 0)
2846 FT_Pos delta;
2849 /* compare to standard width */
2850 delta = dist - axis->widths[0].cur;
2852 if (delta < 0)
2853 delta = -delta;
2855 if (delta < 40)
2857 dist = axis->widths[0].cur;
2858 if (dist < 48)
2859 dist = 48;
2861 goto Done_Width;
2864 if (dist < 3 * 64)
2866 delta = dist & 63;
2867 dist &= -64;
2869 if (delta < 10)
2870 dist += delta;
2871 else if (delta < 32)
2872 dist += 10;
2873 else if (delta < 54)
2874 dist += 54;
2875 else
2876 dist += delta;
2878 else
2880 /* A stem's end position depends on two values: the start */
2881 /* position and the stem length. The former gets usually */
2882 /* rounded to the grid, while the latter gets rounded also if it */
2883 /* exceeds a certain length (see below in this function). This */
2884 /* `double rounding' can lead to a great difference to the */
2885 /* original, unhinted position; this normally doesn't matter for */
2886 /* large PPEM values, but for small sizes it can easily make */
2887 /* outlines collide. For this reason, we adjust the stem length */
2888 /* by a small amount depending on the PPEM value in case the */
2889 /* former and latter rounding both point into the same */
2890 /* direction. */
2892 FT_Pos bdelta = 0;
2895 if (((width > 0) && (base_delta > 0))
2896 || ((width < 0) && (base_delta < 0)))
2898 FT_UInt ppem = metrics->root.scaler.face->size->metrics.x_ppem;
2901 if (ppem < 10)
2902 bdelta = base_delta;
2903 else if (ppem < 30)
2904 bdelta = (base_delta * (FT_Pos)(30 - ppem)) / 20;
2906 if (bdelta < 0)
2907 bdelta = -bdelta;
2910 dist = (dist - bdelta + 32) & ~63;
2914 else
2916 /* strong hinting process: snap the stem width to integer pixels */
2918 FT_Pos org_dist = dist;
2921 dist = ta_latin_snap_width(axis->widths, axis->width_count, dist);
2923 if (vertical)
2925 /* in the case of vertical hinting, */
2926 /* always round the stem heights to integer pixels */
2928 if (dist >= 64)
2929 dist = (dist + 16) & ~63;
2930 else
2931 dist = 64;
2933 else
2935 if (TA_LATIN_HINTS_DO_MONO(hints))
2937 /* monochrome horizontal hinting: */
2938 /* snap widths to integer pixels with a different threshold */
2940 if (dist < 64)
2941 dist = 64;
2942 else
2943 dist = (dist + 32) & ~63;
2945 else
2947 /* for horizontal anti-aliased hinting, we adopt a more subtle */
2948 /* approach: we strengthen small stems, round stems whose size */
2949 /* is between 1 and 2 pixels to an integer, otherwise nothing */
2951 if (dist < 48)
2952 dist = (dist + 64) >> 1;
2954 else if (dist < 128)
2956 /* we only round to an integer width if the corresponding */
2957 /* distortion is less than 1/4 pixel -- otherwise, this */
2958 /* makes everything worse since the diagonals, which are */
2959 /* not hinted, appear a lot bolder or thinner than the */
2960 /* vertical stems */
2962 FT_Pos delta;
2965 dist = (dist + 22) & ~63;
2966 delta = dist - org_dist;
2967 if (delta < 0)
2968 delta = -delta;
2970 if (delta >= 16)
2972 dist = org_dist;
2973 if (dist < 48)
2974 dist = (dist + 64) >> 1;
2977 else
2978 /* round otherwise to prevent color fringes in LCD mode */
2979 dist = (dist + 32) & ~63;
2984 Done_Width:
2985 if (sign)
2986 dist = -dist;
2988 return dist;
2992 /* align one stem edge relative to the previous stem edge */
2994 static void
2995 ta_latin_align_linked_edge(TA_GlyphHints hints,
2996 TA_Dimension dim,
2997 TA_Edge base_edge,
2998 TA_Edge stem_edge)
3000 FT_Pos dist, base_delta;
3001 FT_Pos fitted_width;
3004 dist = stem_edge->opos - base_edge->opos;
3005 base_delta = base_edge->pos - base_edge->opos;
3008 fitted_width = ta_latin_compute_stem_width(hints, dim,
3009 dist, base_delta,
3010 base_edge->flags,
3011 stem_edge->flags);
3013 stem_edge->pos = base_edge->pos + fitted_width;
3015 TA_LOG((" LINK: edge %d (opos=%.2f) linked to %.2f,"
3016 " dist was %.2f, now %.2f\n",
3017 stem_edge - hints->axis[dim].edges, stem_edge->opos / 64.0,
3018 stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0));
3020 if (hints->recorder)
3021 hints->recorder(ta_link, hints, dim,
3022 base_edge, stem_edge, NULL, NULL, NULL);
3026 /* shift the coordinates of the `serif' edge by the same amount */
3027 /* as the corresponding `base' edge has been moved already */
3029 static void
3030 ta_latin_align_serif_edge(TA_GlyphHints hints,
3031 TA_Edge base,
3032 TA_Edge serif)
3034 FT_UNUSED(hints);
3036 serif->pos = base->pos + (serif->opos - base->opos);
3040 /* the main grid-fitting routine */
3042 static void
3043 ta_latin_hint_edges(TA_GlyphHints hints,
3044 TA_Dimension dim)
3046 TA_AxisHints axis = &hints->axis[dim];
3048 TA_Edge edges = axis->edges;
3049 TA_Edge edge_limit = edges + axis->num_edges;
3050 FT_PtrDist n_edges;
3051 TA_Edge edge;
3053 TA_Edge anchor = NULL;
3054 FT_Int has_serifs = 0;
3056 TA_StyleClass style_class = hints->metrics->style_class;
3057 TA_ScriptClass script_class = ta_script_classes[style_class->script];
3059 FT_Bool top_to_bottom_hinting = 0;
3061 #ifdef TA_DEBUG
3062 FT_UInt num_actions = 0;
3063 #endif
3065 TA_LOG(("latin %s edge hinting (style `%s')\n",
3066 dim == TA_DIMENSION_VERT ? "horizontal" : "vertical",
3067 ta_style_names[hints->metrics->style_class->style]));
3069 if (dim == TA_DIMENSION_VERT)
3070 top_to_bottom_hinting = script_class->top_to_bottom_hinting;
3072 /* we begin by aligning all stems relative to the blue zone if needed -- */
3073 /* that's only for horizontal edges */
3075 if (dim == TA_DIMENSION_VERT
3076 && TA_HINTS_DO_BLUES(hints))
3078 for (edge = edges; edge < edge_limit; edge++)
3080 TA_Width blue;
3081 TA_Edge edge1, edge2; /* these edges form the stem to check */
3084 if (edge->flags & TA_EDGE_DONE)
3085 continue;
3087 edge1 = NULL;
3088 edge2 = edge->link;
3091 * If a stem contains both a neutral and a non-neutral blue zone,
3092 * skip the neutral one. Otherwise, outlines with different
3093 * directions might be incorrectly aligned at the same vertical
3094 * position.
3096 * If we have two neutral blue zones, skip one of them.
3098 if (edge->blue_edge && edge2 && edge2->blue_edge)
3100 FT_Byte neutral = edge->flags & TA_EDGE_NEUTRAL;
3101 FT_Byte neutral2 = edge2->flags & TA_EDGE_NEUTRAL;
3104 if (neutral2)
3106 edge2->blue_edge = NULL;
3107 edge2->flags &= ~TA_EDGE_NEUTRAL;
3109 else if (neutral)
3111 edge->blue_edge = NULL;
3112 edge->flags &= ~TA_EDGE_NEUTRAL;
3116 blue = edge->blue_edge;
3117 if (blue)
3118 edge1 = edge;
3120 /* flip edges if the other edge is aligned to a blue zone */
3121 else if (edge2 && edge2->blue_edge)
3123 blue = edge2->blue_edge;
3124 edge1 = edge2;
3125 edge2 = edge;
3128 if (!edge1)
3129 continue;
3131 #ifdef TA_DEBUG
3132 if (!anchor)
3133 TA_LOG((" BLUE_ANCHOR: edge %d (opos=%.2f) snapped to %.2f,"
3134 " was %.2f (anchor=edge %d)\n",
3135 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
3136 edge1->pos / 64.0, edge - edges));
3137 else
3138 TA_LOG((" BLUE: edge %d (opos=%.2f) snapped to %.2f, was %.2f\n",
3139 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
3140 edge1->pos / 64.0));
3142 num_actions++;
3143 #endif
3145 edge1->pos = blue->fit;
3146 edge1->flags |= TA_EDGE_DONE;
3148 if (hints->recorder)
3150 if (!anchor)
3151 hints->recorder(ta_blue_anchor, hints, dim,
3152 edge1, edge, NULL, NULL, NULL);
3153 else
3154 hints->recorder(ta_blue, hints, dim,
3155 edge1, NULL, NULL, NULL, NULL);
3158 if (edge2 && !edge2->blue_edge)
3160 ta_latin_align_linked_edge(hints, dim, edge1, edge2);
3161 edge2->flags |= TA_EDGE_DONE;
3163 #ifdef TA_DEBUG
3164 num_actions++;
3165 #endif
3168 if (!anchor)
3169 anchor = edge;
3173 /* now we align all other stem edges, */
3174 /* trying to maintain the relative order of stems in the glyph */
3175 for (edge = edges; edge < edge_limit; edge++)
3177 TA_Edge edge2;
3180 if (edge->flags & TA_EDGE_DONE)
3181 continue;
3183 /* skip all non-stem edges */
3184 edge2 = edge->link;
3185 if (!edge2)
3187 has_serifs++;
3188 continue;
3191 /* now align the stem */
3193 /* this should not happen, but it's better to be safe */
3194 if (edge2->blue_edge)
3196 TA_LOG((" ASSERTION FAILED for edge %d\n", edge2 - edges));
3198 ta_latin_align_linked_edge(hints, dim, edge2, edge);
3199 edge->flags |= TA_EDGE_DONE;
3201 #ifdef TA_DEBUG
3202 num_actions++;
3203 #endif
3204 continue;
3207 if (!anchor)
3209 /* if we reach this if clause, no stem has been aligned yet */
3211 FT_Pos org_len, org_center, cur_len;
3212 FT_Pos cur_pos1, error1, error2, u_off, d_off;
3215 org_len = edge2->opos - edge->opos;
3216 cur_len = ta_latin_compute_stem_width(hints, dim,
3217 org_len, 0,
3218 edge->flags, edge2->flags);
3220 /* some voodoo to specially round edges for small stem widths; */
3221 /* the idea is to align the center of a stem, */
3222 /* then shifting the stem edges to suitable positions */
3223 if (cur_len <= 64)
3225 /* width <= 1px */
3226 u_off = 32;
3227 d_off = 32;
3229 else
3231 /* 1px < width < 1.5px */
3232 u_off = 38;
3233 d_off = 26;
3236 if (cur_len < 96)
3238 org_center = edge->opos + (org_len >> 1);
3239 cur_pos1 = TA_PIX_ROUND(org_center);
3241 error1 = org_center - (cur_pos1 - u_off);
3242 if (error1 < 0)
3243 error1 = -error1;
3245 error2 = org_center - (cur_pos1 + d_off);
3246 if (error2 < 0)
3247 error2 = -error2;
3249 if (error1 < error2)
3250 cur_pos1 -= u_off;
3251 else
3252 cur_pos1 += d_off;
3254 edge->pos = cur_pos1 - cur_len / 2;
3255 edge2->pos = edge->pos + cur_len;
3257 else
3258 edge->pos = TA_PIX_ROUND(edge->opos);
3260 anchor = edge;
3261 edge->flags |= TA_EDGE_DONE;
3263 TA_LOG((" ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
3264 " snapped to %.2f and %.2f\n",
3265 edge - edges, edge->opos / 64.0,
3266 edge2 - edges, edge2->opos / 64.0,
3267 edge->pos / 64.0, edge2->pos / 64.0));
3269 if (hints->recorder)
3270 hints->recorder(ta_anchor, hints, dim,
3271 edge, edge2, NULL, NULL, NULL);
3273 ta_latin_align_linked_edge(hints, dim, edge, edge2);
3275 #ifdef TA_DEBUG
3276 num_actions += 2;
3277 #endif
3279 else
3281 FT_Pos org_pos, org_len, org_center, cur_len;
3282 FT_Pos cur_pos1, cur_pos2, delta1, delta2;
3285 org_pos = anchor->pos + (edge->opos - anchor->opos);
3286 org_len = edge2->opos - edge->opos;
3287 org_center = org_pos + (org_len >> 1);
3289 cur_len = ta_latin_compute_stem_width(hints, dim,
3290 org_len, 0,
3291 edge->flags, edge2->flags);
3293 if (edge2->flags & TA_EDGE_DONE)
3295 TA_LOG((" ADJUST: edge %d (pos=%.2f) moved to %.2f\n",
3296 edge - edges, edge->pos / 64.0,
3297 (edge2->pos - cur_len) / 64.0));
3299 edge->pos = edge2->pos - cur_len;
3301 if (hints->recorder)
3303 TA_Edge bound = NULL;
3306 if (edge > edges)
3307 bound = &edge[-1];
3309 hints->recorder(ta_adjust, hints, dim,
3310 edge, edge2, NULL, bound, NULL);
3314 else if (cur_len < 96)
3316 FT_Pos u_off, d_off;
3319 cur_pos1 = TA_PIX_ROUND(org_center);
3321 if (cur_len <= 64)
3323 u_off = 32;
3324 d_off = 32;
3326 else
3328 u_off = 38;
3329 d_off = 26;
3332 delta1 = org_center - (cur_pos1 - u_off);
3333 if (delta1 < 0)
3334 delta1 = -delta1;
3336 delta2 = org_center - (cur_pos1 + d_off);
3337 if (delta2 < 0)
3338 delta2 = -delta2;
3340 if (delta1 < delta2)
3341 cur_pos1 -= u_off;
3342 else
3343 cur_pos1 += d_off;
3345 edge->pos = cur_pos1 - cur_len / 2;
3346 edge2->pos = cur_pos1 + cur_len / 2;
3348 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
3349 " snapped to %.2f and %.2f\n",
3350 edge - edges, edge->opos / 64.0,
3351 edge2 - edges, edge2->opos / 64.0,
3352 edge->pos / 64.0, edge2->pos / 64.0));
3354 if (hints->recorder)
3356 TA_Edge bound = NULL;
3359 if (edge > edges)
3360 bound = &edge[-1];
3362 hints->recorder(ta_stem, hints, dim,
3363 edge, edge2, NULL, bound, NULL);
3367 else
3369 org_pos = anchor->pos + (edge->opos - anchor->opos);
3370 org_len = edge2->opos - edge->opos;
3371 org_center = org_pos + (org_len >> 1);
3373 cur_len = ta_latin_compute_stem_width(hints, dim,
3374 org_len, 0,
3375 edge->flags, edge2->flags);
3377 cur_pos1 = TA_PIX_ROUND(org_pos);
3378 delta1 = cur_pos1 + (cur_len >> 1) - org_center;
3379 if (delta1 < 0)
3380 delta1 = -delta1;
3382 cur_pos2 = TA_PIX_ROUND(org_pos + org_len) - cur_len;
3383 delta2 = cur_pos2 + (cur_len >> 1) - org_center;
3384 if (delta2 < 0)
3385 delta2 = -delta2;
3387 edge->pos = (delta1 < delta2) ? cur_pos1 : cur_pos2;
3388 edge2->pos = edge->pos + cur_len;
3390 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
3391 " snapped to %.2f and %.2f\n",
3392 edge - edges, edge->opos / 64.0,
3393 edge2 - edges, edge2->opos / 64.0,
3394 edge->pos / 64.0, edge2->pos / 64.0));
3396 if (hints->recorder)
3398 TA_Edge bound = NULL;
3401 if (edge > edges)
3402 bound = &edge[-1];
3404 hints->recorder(ta_stem, hints, dim,
3405 edge, edge2, NULL, bound, NULL);
3409 #ifdef TA_DEBUG
3410 num_actions++;
3411 #endif
3413 edge->flags |= TA_EDGE_DONE;
3414 edge2->flags |= TA_EDGE_DONE;
3416 if (edge > edges
3417 && (top_to_bottom_hinting ? (edge->pos > edge[-1].pos)
3418 : (edge->pos < edge[-1].pos)))
3420 /* don't move if stem would (almost) disappear otherwise; */
3421 /* the ad-hoc value 16 corresponds to 1/4px */
3422 if (edge->link
3423 && TA_ABS(edge->link->pos - edge[-1].pos) > 16)
3425 #ifdef TA_DEBUG
3426 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
3427 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
3429 num_actions++;
3430 #endif
3432 edge->pos = edge[-1].pos;
3434 if (hints->recorder)
3435 hints->recorder(ta_bound, hints, dim,
3436 edge, &edge[-1], NULL, NULL, NULL);
3442 /* make sure that lowercase m's maintain their symmetry */
3444 /* In general, lowercase m's have six vertical edges if they are sans */
3445 /* serif, or twelve if they are with serifs. This implementation is */
3446 /* based on that assumption, and seems to work very well with most */
3447 /* faces. However, if for a certain face this assumption is not */
3448 /* true, the m is just rendered like before. In addition, any stem */
3449 /* correction will only be applied to symmetrical glyphs (even if the */
3450 /* glyph is not an m), so the potential for unwanted distortion is */
3451 /* relatively low. */
3453 /* we don't handle horizontal edges since we can't easily assure that */
3454 /* the third (lowest) stem aligns with the base line; it might end up */
3455 /* one pixel higher or lower */
3457 n_edges = edge_limit - edges;
3458 if (dim == TA_DIMENSION_HORZ
3459 && (n_edges == 6 || n_edges == 12))
3461 TA_Edge edge1, edge2, edge3;
3462 FT_Pos dist1, dist2, span, delta;
3465 if (n_edges == 6)
3467 edge1 = edges;
3468 edge2 = edges + 2;
3469 edge3 = edges + 4;
3471 else
3473 edge1 = edges + 1;
3474 edge2 = edges + 5;
3475 edge3 = edges + 9;
3478 dist1 = edge2->opos - edge1->opos;
3479 dist2 = edge3->opos - edge2->opos;
3481 span = dist1 - dist2;
3482 if (span < 0)
3483 span = -span;
3485 if (span < 8)
3487 delta = edge3->pos - (2 * edge2->pos - edge1->pos);
3488 edge3->pos -= delta;
3489 if (edge3->link)
3490 edge3->link->pos -= delta;
3492 /* move the serifs along with the stem */
3493 if (n_edges == 12)
3495 (edges + 8)->pos -= delta;
3496 (edges + 11)->pos -= delta;
3499 edge3->flags |= TA_EDGE_DONE;
3500 if (edge3->link)
3501 edge3->link->flags |= TA_EDGE_DONE;
3505 if (has_serifs || !anchor)
3507 /* now hint the remaining edges (serifs and single) */
3508 /* in order to complete our processing */
3509 for (edge = edges; edge < edge_limit; edge++)
3511 TA_Edge lower_bound = NULL;
3512 TA_Edge upper_bound = NULL;
3514 FT_Pos delta;
3517 if (edge->flags & TA_EDGE_DONE)
3518 continue;
3520 delta = 1000;
3522 if (edge->serif)
3524 delta = edge->serif->opos - edge->opos;
3525 if (delta < 0)
3526 delta = -delta;
3529 if (edge > edges)
3530 lower_bound = &edge[-1];
3532 if (edge + 1 < edge_limit
3533 && edge[1].flags & TA_EDGE_DONE)
3534 upper_bound = &edge[1];
3537 if (delta < 64 + 16)
3539 ta_latin_align_serif_edge(hints, edge->serif, edge);
3541 TA_LOG((" SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
3542 " aligned to %.2f\n",
3543 edge - edges, edge->opos / 64.0,
3544 edge->serif - edges, edge->serif->opos / 64.0,
3545 edge->pos / 64.0));
3547 if (hints->recorder)
3548 hints->recorder(ta_serif, hints, dim,
3549 edge, NULL, NULL, lower_bound, upper_bound);
3551 else if (!anchor)
3553 edge->pos = TA_PIX_ROUND(edge->opos);
3554 anchor = edge;
3556 TA_LOG((" SERIF_ANCHOR: edge %d (opos=%.2f) snapped to %.2f\n",
3557 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
3559 if (hints->recorder)
3560 hints->recorder(ta_serif_anchor, hints, dim,
3561 edge, NULL, NULL, lower_bound, upper_bound);
3563 else
3565 TA_Edge before, after;
3568 for (before = edge - 1; before >= edges; before--)
3569 if (before->flags & TA_EDGE_DONE)
3570 break;
3572 for (after = edge + 1; after < edge_limit; after++)
3573 if (after->flags & TA_EDGE_DONE)
3574 break;
3576 if (before >= edges && before < edge
3577 && after < edge_limit && after > edge)
3579 if (after->opos == before->opos)
3580 edge->pos = before->pos;
3581 else
3582 edge->pos = before->pos + FT_MulDiv(edge->opos - before->opos,
3583 after->pos - before->pos,
3584 after->opos - before->opos);
3586 TA_LOG((" SERIF_LINK1: edge %d (opos=%.2f) snapped to %.2f"
3587 " from %d (opos=%.2f)\n",
3588 edge - edges, edge->opos / 64.0,
3589 edge->pos / 64.0,
3590 before - edges, before->opos / 64.0));
3592 if (hints->recorder)
3593 hints->recorder(ta_serif_link1, hints, dim,
3594 edge, before, after, lower_bound, upper_bound);
3596 else
3598 edge->pos = anchor->pos + ((edge->opos - anchor->opos + 16) & ~31);
3599 TA_LOG((" SERIF_LINK2: edge %d (opos=%.2f) snapped to %.2f\n",
3600 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
3602 if (hints->recorder)
3603 hints->recorder(ta_serif_link2, hints, dim,
3604 edge, NULL, NULL, lower_bound, upper_bound);
3608 #ifdef TA_DEBUG
3609 num_actions++;
3610 #endif
3611 edge->flags |= TA_EDGE_DONE;
3613 if (edge > edges
3614 && (top_to_bottom_hinting ? (edge->pos > edge[-1].pos)
3615 : (edge->pos < edge[-1].pos)))
3617 /* don't move if stem would (almost) disappear otherwise; */
3618 /* the ad-hoc value 16 corresponds to 1/4px */
3619 if (edge->link
3620 && TA_ABS(edge->link->pos - edge[-1].pos) > 16)
3622 #ifdef TA_DEBUG
3623 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
3624 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
3625 num_actions++;
3626 #endif
3628 edge->pos = edge[-1].pos;
3630 if (hints->recorder)
3631 hints->recorder(ta_bound, hints, dim,
3632 edge, &edge[-1], NULL, NULL, NULL);
3636 if (edge + 1 < edge_limit
3637 && edge[1].flags & TA_EDGE_DONE
3638 && (top_to_bottom_hinting ? (edge->pos < edge[1].pos)
3639 : (edge->pos > edge[1].pos)))
3642 /* don't move if stem would (almost) disappear otherwise; */
3643 /* the ad-hoc value 16 corresponds to 1/4px */
3644 if (edge->link
3645 && TA_ABS(edge->link->pos - edge[-1].pos) > 16)
3647 #ifdef TA_DEBUG
3648 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
3649 edge - edges, edge->pos / 64.0, edge[1].pos / 64.0));
3651 num_actions++;
3652 #endif
3654 edge->pos = edge[1].pos;
3656 if (hints->recorder)
3657 hints->recorder(ta_bound, hints, dim,
3658 edge, &edge[1], NULL, NULL, NULL);
3664 #ifdef TA_DEBUG
3665 if (!num_actions)
3666 TA_LOG((" (none)\n"));
3667 TA_LOG(("\n"));
3668 #endif
3672 /* apply the complete hinting algorithm to a latin glyph */
3674 static FT_Error
3675 ta_latin_hints_apply(FT_UInt glyph_index,
3676 TA_GlyphHints hints,
3677 FT_Outline* outline,
3678 TA_LatinMetrics metrics)
3680 FT_Error error;
3681 int dim;
3683 TA_LatinAxis axis;
3686 error = ta_glyph_hints_reload(hints, outline);
3687 if (error)
3688 goto Exit;
3690 /* analyze glyph outline */
3691 if (TA_HINTS_DO_HORIZONTAL(hints))
3693 axis = &metrics->axis[TA_DIMENSION_HORZ];
3694 error = ta_latin_hints_detect_features(hints,
3695 axis->width_count,
3696 axis->widths,
3697 TA_DIMENSION_HORZ);
3698 if (error)
3699 goto Exit;
3702 if (TA_HINTS_DO_VERTICAL(hints))
3704 axis = &metrics->axis[TA_DIMENSION_VERT];
3705 error = ta_latin_hints_detect_features(hints,
3706 axis->width_count,
3707 axis->widths,
3708 TA_DIMENSION_VERT);
3709 if (error)
3710 goto Exit;
3712 /* apply blue zones to base characters only */
3713 if (!(metrics->root.globals->glyph_styles[glyph_index] & TA_NONBASE))
3714 ta_latin_hints_compute_blue_edges(hints, metrics);
3717 /* grid-fit the outline */
3718 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
3720 #ifdef TA_CONFIG_OPTION_USE_WARPER
3721 if (dim == TA_DIMENSION_HORZ
3722 && metrics->root.scaler.render_mode == FT_RENDER_MODE_NORMAL
3723 && TA_HINTS_DO_WARP(hints))
3725 TA_WarperRec warper;
3726 FT_Fixed scale;
3727 FT_Pos delta;
3730 ta_warper_compute(&warper, hints, (TA_Dimension)dim, &scale, &delta);
3731 ta_glyph_hints_scale_dim(hints, (TA_Dimension)dim, scale, delta);
3733 continue;
3735 #endif /* TA_CONFIG_OPTION_USE_WARPER */
3737 if ((dim == TA_DIMENSION_HORZ && TA_HINTS_DO_HORIZONTAL(hints))
3738 || (dim == TA_DIMENSION_VERT && TA_HINTS_DO_VERTICAL(hints)))
3740 ta_latin_hint_edges(hints, (TA_Dimension)dim);
3741 ta_glyph_hints_align_edge_points(hints, (TA_Dimension)dim);
3742 ta_glyph_hints_align_strong_points(hints, (TA_Dimension)dim);
3743 ta_glyph_hints_align_weak_points(hints, (TA_Dimension)dim);
3747 ta_glyph_hints_save(hints, outline);
3749 Exit:
3750 return error;
3754 const TA_WritingSystemClassRec ta_latin_writing_system_class =
3756 TA_WRITING_SYSTEM_LATIN,
3758 sizeof (TA_LatinMetricsRec),
3760 (TA_WritingSystem_InitMetricsFunc)ta_latin_metrics_init, /* style_metrics_init */
3761 (TA_WritingSystem_ScaleMetricsFunc)ta_latin_metrics_scale, /* style_metrics_scale */
3762 (TA_WritingSystem_DoneMetricsFunc)NULL, /* style_metrics_done */
3764 (TA_WritingSystem_InitHintsFunc)ta_latin_hints_init, /* style_hints_init */
3765 (TA_WritingSystem_ApplyHintsFunc)ta_latin_hints_apply /* style_hints_apply */
3768 /* end of talatin.c */