s/lower_upper/upper_lower/.
[ttfautohint.git] / lib / talatin.c
blob7f3eba76b222c3e1655ebb56bcc49532fddcaca2
1 /* talatin.c */
3 /*
4 * Copyright (C) 2011-2012 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
25 #include "talatin.h"
26 #include "tasort.h"
29 #ifdef TA_CONFIG_OPTION_USE_WARPER
30 #include "afwarp.h"
31 #endif
34 /* find segments and links, compute all stem widths, and initialize */
35 /* standard width and height for the glyph with given charcode */
37 void
38 ta_latin_metrics_init_widths(TA_LatinMetrics metrics,
39 FT_Face face,
40 FT_ULong charcode)
42 /* scan the array of segments in each direction */
43 TA_GlyphHintsRec hints[1];
46 ta_glyph_hints_init(hints);
48 metrics->axis[TA_DIMENSION_HORZ].width_count = 0;
49 metrics->axis[TA_DIMENSION_VERT].width_count = 0;
52 FT_Error error;
53 FT_UInt glyph_index;
54 int dim;
55 TA_LatinMetricsRec dummy[1];
56 TA_Scaler scaler = &dummy->root.scaler;
59 glyph_index = FT_Get_Char_Index(face, charcode);
60 if (glyph_index == 0)
61 goto Exit;
63 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
64 if (error || face->glyph->outline.n_points <= 0)
65 goto Exit;
67 memset(dummy, 0, sizeof (TA_LatinMetricsRec));
69 dummy->units_per_em = metrics->units_per_em;
71 scaler->x_scale = 0x10000L;
72 scaler->y_scale = 0x10000L;
73 scaler->x_delta = 0;
74 scaler->y_delta = 0;
76 scaler->face = face;
77 scaler->render_mode = FT_RENDER_MODE_NORMAL;
78 scaler->flags = 0;
80 ta_glyph_hints_rescale(hints, (TA_ScriptMetrics)dummy);
82 error = ta_glyph_hints_reload(hints, &face->glyph->outline);
83 if (error)
84 goto Exit;
86 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
88 TA_LatinAxis axis = &metrics->axis[dim];
89 TA_AxisHints axhints = &hints->axis[dim];
91 TA_Segment seg, limit, link;
92 FT_UInt num_widths = 0;
95 error = ta_latin_hints_compute_segments(hints, (TA_Dimension)dim);
96 if (error)
97 goto Exit;
99 ta_latin_hints_link_segments(hints, (TA_Dimension)dim);
101 seg = axhints->segments;
102 limit = seg + axhints->num_segments;
104 for (; seg < limit; seg++)
106 link = seg->link;
108 /* we only consider stem segments there! */
109 if (link
110 && link->link == seg
111 && link > seg)
113 FT_Pos dist;
116 dist = seg->pos - link->pos;
117 if (dist < 0)
118 dist = -dist;
120 if (num_widths < TA_LATIN_MAX_WIDTHS)
121 axis->widths[num_widths++].org = dist;
125 ta_sort_widths(num_widths, axis->widths);
126 axis->width_count = num_widths;
129 Exit:
130 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
132 TA_LatinAxis axis = &metrics->axis[dim];
133 FT_Pos stdw;
136 stdw = (axis->width_count > 0) ? axis->widths[0].org
137 : TA_LATIN_CONSTANT(metrics, 50);
139 /* let's try 20% of the smallest width */
140 axis->edge_distance_threshold = stdw / 5;
141 axis->standard_width = stdw;
142 axis->extra_light = 0;
146 ta_glyph_hints_done(hints);
150 #define TA_LATIN_MAX_TEST_CHARACTERS 12
153 static const char ta_latin_blue_chars[TA_LATIN_MAX_BLUES]
154 [TA_LATIN_MAX_TEST_CHARACTERS + 1] =
156 "THEZOCQS",
157 "HEZLOCUS",
158 "fijkdbh",
159 "xzroesc",
160 "xzroesc",
161 "pqgjy"
165 /* find all blue zones; flat segments give the reference points, */
166 /* round segments the overshoot positions */
168 static void
169 ta_latin_metrics_init_blues(TA_LatinMetrics metrics,
170 FT_Face face)
172 FT_Pos flats[TA_LATIN_MAX_TEST_CHARACTERS];
173 FT_Pos rounds[TA_LATIN_MAX_TEST_CHARACTERS];
174 FT_Int num_flats;
175 FT_Int num_rounds;
177 FT_Int bb;
178 TA_LatinBlue blue;
179 FT_Error error;
180 TA_LatinAxis axis = &metrics->axis[TA_DIMENSION_VERT];
181 FT_GlyphSlot glyph = face->glyph;
184 /* we compute the blues simply by loading each character from the */
185 /* `ta_latin_blue_chars[blues]' string, then finding its top-most or */
186 /* bottom-most points (depending on `TA_IS_TOP_BLUE') */
188 TA_LOG(("blue zones computation\n"));
189 TA_LOG(("------------------------------------------------\n"));
191 for (bb = 0; bb < TA_LATIN_BLUE_MAX; bb++)
193 const char* p = ta_latin_blue_chars[bb];
194 const char* limit = p + TA_LATIN_MAX_TEST_CHARACTERS;
195 FT_Pos* blue_ref;
196 FT_Pos* blue_shoot;
199 TA_LOG(("blue %3d: ", bb));
201 num_flats = 0;
202 num_rounds = 0;
204 for (; p < limit && *p; p++)
206 FT_UInt glyph_index;
207 FT_Pos best_y; /* same as points.y */
208 FT_Int best_point, best_first, best_last;
209 FT_Vector* points;
210 FT_Bool round = 0;
213 TA_LOG(("'%c'", *p));
215 /* load the character in the face -- skip unknown or empty ones */
216 glyph_index = FT_Get_Char_Index(face, (FT_UInt)*p);
217 if (glyph_index == 0)
218 continue;
220 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
221 if (error || glyph->outline.n_points <= 0)
222 continue;
224 /* now compute min or max point indices and coordinates */
225 points = glyph->outline.points;
226 best_point = -1;
227 best_y = 0; /* make compiler happy */
228 best_first = 0; /* ditto */
229 best_last = 0; /* ditto */
232 FT_Int nn;
233 FT_Int first = 0;
234 FT_Int last = -1;
237 for (nn = 0; nn < glyph->outline.n_contours; first = last + 1, nn++)
239 FT_Int old_best_point = best_point;
240 FT_Int pp;
243 last = glyph->outline.contours[nn];
245 /* avoid single-point contours since they are never rasterized; */
246 /* in some fonts, they correspond to mark attachment points */
247 /* which are way outside of the glyph's real outline */
248 if (last <= first)
249 continue;
251 if (TA_LATIN_IS_TOP_BLUE(bb))
253 for (pp = first; pp <= last; pp++)
254 if (best_point < 0
255 || points[pp].y > best_y)
257 best_point = pp;
258 best_y = points[pp].y;
261 else
263 for (pp = first; pp <= last; pp++)
264 if (best_point < 0
265 || points[pp].y < best_y)
267 best_point = pp;
268 best_y = points[pp].y;
272 if (best_point != old_best_point)
274 best_first = first;
275 best_last = last;
278 TA_LOG(("%5ld", best_y));
281 /* now check whether the point belongs to a straight or round */
282 /* segment; we first need to find in which contour the extremum */
283 /* lies, then inspect its previous and next points */
284 if (best_point >= 0)
286 FT_Int prev, next;
287 FT_Pos dist;
290 /* now look for the previous and next points that are not on the */
291 /* same Y coordinate and threshold the `closeness'... */
292 prev = best_point;
293 next = prev;
297 if (prev > best_first)
298 prev--;
299 else
300 prev = best_last;
302 dist = points[prev].y - best_y;
303 if (dist < -5 || dist > 5)
304 break;
305 } while (prev != best_point);
309 if (next < best_last)
310 next++;
311 else
312 next = best_first;
314 dist = points[next].y - best_y;
315 if (dist < -5 || dist > 5)
316 break;
317 } while (next != best_point);
319 /* now set the `round' flag depending on the segment's kind */
320 round = FT_BOOL(
321 FT_CURVE_TAG(glyph->outline.tags[prev]) != FT_CURVE_TAG_ON
322 || FT_CURVE_TAG(glyph->outline.tags[next]) != FT_CURVE_TAG_ON);
324 TA_LOG(("%c ", round ? 'r' : 'f'));
327 if (round)
328 rounds[num_rounds++] = best_y;
329 else
330 flats[num_flats++] = best_y;
333 TA_LOG(("\n"));
335 if (num_flats == 0 && num_rounds == 0)
337 /* we couldn't find a single glyph to compute this blue zone, */
338 /* we will simply ignore it then */
339 TA_LOG(("empty\n"));
340 continue;
343 /* we have computed the contents of the `rounds' and `flats' tables, */
344 /* now determine the reference and overshoot position of the blue -- */
345 /* we simply take the median value after a simple sort */
346 ta_sort_pos(num_rounds, rounds);
347 ta_sort_pos(num_flats, flats);
349 blue = &axis->blues[axis->blue_count];
350 blue_ref = &blue->ref.org;
351 blue_shoot = &blue->shoot.org;
353 axis->blue_count++;
355 if (num_flats == 0)
357 *blue_ref =
358 *blue_shoot = rounds[num_rounds / 2];
360 else if (num_rounds == 0)
362 *blue_ref =
363 *blue_shoot = flats[num_flats / 2];
365 else
367 *blue_ref = flats[num_flats / 2];
368 *blue_shoot = rounds[num_rounds / 2];
371 /* there are sometimes problems if the overshoot position of top */
372 /* zones is under its reference position, or the opposite for bottom */
373 /* zones; we must thus check everything there and correct the errors */
374 if (*blue_shoot != *blue_ref)
376 FT_Pos ref = *blue_ref;
377 FT_Pos shoot = *blue_shoot;
378 FT_Bool over_ref = FT_BOOL(shoot > ref);
381 if (TA_LATIN_IS_TOP_BLUE(bb) ^ over_ref)
382 *blue_ref =
383 *blue_shoot = (shoot + ref) / 2;
386 blue->flags = 0;
387 if (TA_LATIN_IS_TOP_BLUE(bb))
388 blue->flags |= TA_LATIN_BLUE_TOP;
390 /* the following flag is used later to adjust the y and x scales */
391 /* in order to optimize the pixel grid alignment */
392 /* of the top of small letters */
393 if (bb == TA_LATIN_BLUE_SMALL_TOP)
394 blue->flags |= TA_LATIN_BLUE_ADJUSTMENT;
396 TA_LOG(("-- ref = %ld, shoot = %ld\n", *blue_ref, *blue_shoot));
399 TA_LOG(("\n"));
401 return;
405 /* check whether all ASCII digits have the same advance width */
407 void
408 ta_latin_metrics_check_digits(TA_LatinMetrics metrics,
409 FT_Face face)
411 FT_UInt i;
412 FT_Bool started = 0, same_width = 1;
413 FT_Fixed advance, old_advance = 0;
416 /* digit `0' is 0x30 in all supported charmaps */
417 for (i = 0x30; i <= 0x39; i++)
419 FT_UInt glyph_index;
422 glyph_index = FT_Get_Char_Index(face, i);
423 if (glyph_index == 0)
424 continue;
426 if (FT_Get_Advance(face, glyph_index,
427 FT_LOAD_NO_SCALE
428 | FT_LOAD_NO_HINTING
429 | FT_LOAD_IGNORE_TRANSFORM,
430 &advance))
431 continue;
433 if (started)
435 if (advance != old_advance)
437 same_width = 0;
438 break;
441 else
443 old_advance = advance;
444 started = 1;
448 metrics->root.digits_have_same_width = same_width;
452 /* initialize global metrics */
454 FT_Error
455 ta_latin_metrics_init(TA_LatinMetrics metrics,
456 FT_Face face)
458 FT_Error error = FT_Err_Ok;
459 FT_CharMap oldmap = face->charmap;
460 FT_UInt ee;
462 static const FT_Encoding latin_encodings[] =
464 FT_ENCODING_UNICODE,
465 FT_ENCODING_APPLE_ROMAN,
466 FT_ENCODING_ADOBE_STANDARD,
467 FT_ENCODING_ADOBE_LATIN_1,
469 FT_ENCODING_NONE /* end of list */
473 metrics->units_per_em = face->units_per_EM;
475 /* do we have a latin charmap in there? */
476 for (ee = 0; latin_encodings[ee] != FT_ENCODING_NONE; ee++)
478 error = FT_Select_Charmap(face, latin_encodings[ee]);
479 if (!error)
480 break;
483 if (!error)
485 /* for now, compute the standard width and height from the `o' */
486 ta_latin_metrics_init_widths(metrics, face, 'o');
487 ta_latin_metrics_init_blues(metrics, face);
488 ta_latin_metrics_check_digits(metrics, face);
491 FT_Set_Charmap(face, oldmap);
492 return FT_Err_Ok;
496 /* adjust scaling value, then scale and shift widths */
497 /* and blue zones (if applicable) for given dimension */
499 static void
500 ta_latin_metrics_scale_dim(TA_LatinMetrics metrics,
501 TA_Scaler scaler,
502 TA_Dimension dim)
504 FT_Fixed scale;
505 FT_Pos delta;
506 TA_LatinAxis axis;
507 FT_UInt nn;
510 if (dim == TA_DIMENSION_HORZ)
512 scale = scaler->x_scale;
513 delta = scaler->x_delta;
515 else
517 scale = scaler->y_scale;
518 delta = scaler->y_delta;
521 axis = &metrics->axis[dim];
523 if (axis->org_scale == scale && axis->org_delta == delta)
524 return;
526 axis->org_scale = scale;
527 axis->org_delta = delta;
529 /* correct X and Y scale to optimize the alignment of the top of */
530 /* small letters to the pixel grid */
532 TA_LatinAxis Axis = &metrics->axis[TA_DIMENSION_VERT];
533 TA_LatinBlue blue = NULL;
536 for (nn = 0; nn < Axis->blue_count; nn++)
538 if (Axis->blues[nn].flags & TA_LATIN_BLUE_ADJUSTMENT)
540 blue = &Axis->blues[nn];
541 break;
545 if (blue)
547 FT_Pos scaled;
548 FT_Pos threshold;
549 FT_Pos fitted;
552 scaled = FT_MulFix(blue->shoot.org, scaler->y_scale);
554 threshold = 40;
555 if ((scaler->flags & TA_SCALER_FLAG_INCREASE_X_HEIGHT)
556 && metrics->root.scaler.face->size->metrics.x_ppem < 15
557 && metrics->root.scaler.face->size->metrics.x_ppem > 5)
558 threshold = 52;
560 fitted = (scaled + threshold) & ~63;
562 if (scaled != fitted)
564 if (dim == TA_DIMENSION_VERT)
565 scale = FT_MulDiv(scale, fitted, scaled);
570 axis->scale = scale;
571 axis->delta = delta;
573 if (dim == TA_DIMENSION_HORZ)
575 metrics->root.scaler.x_scale = scale;
576 metrics->root.scaler.x_delta = delta;
578 else
580 metrics->root.scaler.y_scale = scale;
581 metrics->root.scaler.y_delta = delta;
584 /* scale the widths */
585 for (nn = 0; nn < axis->width_count; nn++)
587 TA_Width width = axis->widths + nn;
590 width->cur = FT_MulFix(width->org, scale);
591 width->fit = width->cur;
594 /* an extra-light axis corresponds to a standard width that is */
595 /* smaller than 5/8 pixels */
596 axis->extra_light =
597 (FT_Bool)(FT_MulFix(axis->standard_width, scale) < 32 + 8);
599 if (dim == TA_DIMENSION_VERT)
601 /* scale the blue zones */
602 for (nn = 0; nn < axis->blue_count; nn++)
604 TA_LatinBlue blue = &axis->blues[nn];
605 FT_Pos dist;
608 blue->ref.cur = FT_MulFix(blue->ref.org, scale) + delta;
609 blue->ref.fit = blue->ref.cur;
610 blue->shoot.cur = FT_MulFix(blue->shoot.org, scale) + delta;
611 blue->shoot.fit = blue->shoot.cur;
612 blue->flags &= ~TA_LATIN_BLUE_ACTIVE;
614 /* a blue zone is only active if it is less than 3/4 pixels tall */
615 dist = FT_MulFix(blue->ref.org - blue->shoot.org, scale);
616 if (dist <= 48 && dist >= -48)
618 #if 0
619 FT_Pos delta1;
620 #endif
621 FT_Pos delta2;
624 /* use discrete values for blue zone widths */
626 #if 0
627 /* generic, original code */
628 delta1 = blue->shoot.org - blue->ref.org;
629 delta2 = delta1;
630 if (delta1 < 0)
631 delta2 = -delta2;
633 delta2 = FT_MulFix(delta2, scale);
635 if (delta2 < 32)
636 delta2 = 0;
637 else if (delta2 < 64)
638 delta2 = 32 + (((delta2 - 32) + 16) & ~31);
639 else
640 delta2 = TA_PIX_ROUND(delta2);
642 if (delta1 < 0)
643 delta2 = -delta2;
645 blue->ref.fit = TA_PIX_ROUND(blue->ref.cur);
646 blue->shoot.fit = blue->ref.fit + delta2;
647 #else
648 /* simplified version due to abs(dist) <= 48 */
649 delta2 = dist;
650 if (dist < 0)
651 delta2 = -delta2;
653 if (delta2 < 32)
654 delta2 = 0;
655 else if (delta < 48)
656 delta2 = 32;
657 else
658 delta2 = 64;
660 if (dist < 0)
661 delta2 = -delta2;
663 blue->ref.fit = TA_PIX_ROUND(blue->ref.cur);
664 blue->shoot.fit = blue->ref.fit - delta2;
665 #endif
667 blue->flags |= TA_LATIN_BLUE_ACTIVE;
674 /* scale global values in both directions */
676 void
677 ta_latin_metrics_scale(TA_LatinMetrics metrics,
678 TA_Scaler scaler)
680 metrics->root.scaler.render_mode = scaler->render_mode;
681 metrics->root.scaler.face = scaler->face;
682 metrics->root.scaler.flags = scaler->flags;
684 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_HORZ);
685 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_VERT);
689 /* walk over all contours and compute its segments */
691 FT_Error
692 ta_latin_hints_compute_segments(TA_GlyphHints hints,
693 TA_Dimension dim)
695 TA_AxisHints axis = &hints->axis[dim];
696 FT_Error error = FT_Err_Ok;
698 TA_Segment segment = NULL;
699 TA_SegmentRec seg0;
701 TA_Point* contour = hints->contours;
702 TA_Point* contour_limit = contour + hints->num_contours;
703 TA_Direction major_dir, segment_dir;
706 memset(&seg0, 0, sizeof (TA_SegmentRec));
707 seg0.score = 32000;
708 seg0.flags = TA_EDGE_NORMAL;
710 major_dir = (TA_Direction)TA_ABS(axis->major_dir);
711 segment_dir = major_dir;
713 axis->num_segments = 0;
715 /* set up (u,v) in each point */
716 if (dim == TA_DIMENSION_HORZ)
718 TA_Point point = hints->points;
719 TA_Point limit = point + hints->num_points;
722 for (; point < limit; point++)
724 point->u = point->fx;
725 point->v = point->fy;
728 else
730 TA_Point point = hints->points;
731 TA_Point limit = point + hints->num_points;
734 for (; point < limit; point++)
736 point->u = point->fy;
737 point->v = point->fx;
741 /* do each contour separately */
742 for (; contour < contour_limit; contour++)
744 TA_Point point = contour[0];
745 TA_Point last = point->prev;
747 int on_edge = 0;
749 FT_Pos min_pos = 32000; /* minimum segment pos != min_coord */
750 FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */
751 FT_Bool passed;
754 if (point == last) /* skip singletons -- just in case */
755 continue;
757 if (TA_ABS(last->out_dir) == major_dir
758 && TA_ABS(point->out_dir) == major_dir)
760 /* we are already on an edge, try to locate its start */
761 last = point;
763 for (;;)
765 point = point->prev;
766 if (TA_ABS(point->out_dir) != major_dir)
768 point = point->next;
769 break;
771 if (point == last)
772 break;
776 last = point;
777 passed = 0;
779 for (;;)
781 FT_Pos u, v;
784 if (on_edge)
786 u = point->u;
787 if (u < min_pos)
788 min_pos = u;
789 if (u > max_pos)
790 max_pos = u;
792 if (point->out_dir != segment_dir
793 || point == last)
795 /* we are just leaving an edge; record a new segment! */
796 segment->last = point;
797 segment->pos = (FT_Short)((min_pos + max_pos) >> 1);
799 /* a segment is round if either its first or last point */
800 /* is a control point */
801 if ((segment->first->flags | point->flags) & TA_FLAG_CONTROL)
802 segment->flags |= TA_EDGE_ROUND;
804 /* compute segment size */
805 min_pos = max_pos = point->v;
807 v = segment->first->v;
808 if (v < min_pos)
809 min_pos = v;
810 if (v > max_pos)
811 max_pos = v;
813 segment->min_coord = (FT_Short)min_pos;
814 segment->max_coord = (FT_Short)max_pos;
815 segment->height = (FT_Short)(segment->max_coord -
816 segment->min_coord);
818 on_edge = 0;
819 segment = NULL;
820 /* fall through */
824 /* now exit if we are at the start/end point */
825 if (point == last)
827 if (passed)
828 break;
829 passed = 1;
832 if (!on_edge
833 && TA_ABS(point->out_dir) == major_dir)
835 /* this is the start of a new segment! */
836 segment_dir = (TA_Direction)point->out_dir;
838 /* clear all segment fields */
839 error = ta_axis_hints_new_segment(axis, &segment);
840 if (error)
841 goto Exit;
843 segment[0] = seg0;
844 segment->dir = (FT_Char)segment_dir;
845 min_pos = max_pos = point->u;
846 segment->first = point;
847 segment->last = point;
848 on_edge = 1;
851 point = point->next;
853 } /* contours */
856 /* now slightly increase the height of segments if this makes sense -- */
857 /* this is used to better detect and ignore serifs */
859 TA_Segment segments = axis->segments;
860 TA_Segment segments_end = segments + axis->num_segments;
863 for (segment = segments; segment < segments_end; segment++)
865 TA_Point first = segment->first;
866 TA_Point last = segment->last;
868 FT_Pos first_v = first->v;
869 FT_Pos last_v = last->v;
872 if (first == last)
873 continue;
875 if (first_v < last_v)
877 TA_Point p;
880 p = first->prev;
881 if (p->v < first_v)
882 segment->height = (FT_Short)(segment->height +
883 ((first_v - p->v) >> 1));
885 p = last->next;
886 if (p->v > last_v)
887 segment->height = (FT_Short)(segment->height +
888 ((p->v - last_v) >> 1));
890 else
892 TA_Point p;
895 p = first->prev;
896 if (p->v > first_v)
897 segment->height = (FT_Short)(segment->height +
898 ((p->v - first_v) >> 1));
900 p = last->next;
901 if (p->v < last_v)
902 segment->height = (FT_Short)(segment->height +
903 ((last_v - p->v) >> 1));
908 Exit:
909 return error;
913 /* link segments to form stems and serifs */
915 void
916 ta_latin_hints_link_segments(TA_GlyphHints hints,
917 TA_Dimension dim)
919 TA_AxisHints axis = &hints->axis[dim];
921 TA_Segment segments = axis->segments;
922 TA_Segment segment_limit = segments + axis->num_segments;
924 FT_Pos len_threshold, len_score;
925 TA_Segment seg1, seg2;
928 len_threshold = TA_LATIN_CONSTANT(hints->metrics, 8);
929 if (len_threshold == 0)
930 len_threshold = 1;
932 len_score = TA_LATIN_CONSTANT(hints->metrics, 6000);
934 /* now compare each segment to the others */
935 for (seg1 = segments; seg1 < segment_limit; seg1++)
937 /* the fake segments are introduced to hint the metrics -- */
938 /* we must never link them to anything */
939 if (seg1->dir != axis->major_dir
940 || seg1->first == seg1->last)
941 continue;
943 /* search for stems having opposite directions, */
944 /* with seg1 to the `left' of seg2 */
945 for (seg2 = segments; seg2 < segment_limit; seg2++)
947 FT_Pos pos1 = seg1->pos;
948 FT_Pos pos2 = seg2->pos;
951 if (seg1->dir + seg2->dir == 0
952 && pos2 > pos1)
954 /* compute distance between the two segments */
955 FT_Pos dist = pos2 - pos1;
956 FT_Pos min = seg1->min_coord;
957 FT_Pos max = seg1->max_coord;
958 FT_Pos len, score;
961 if (min < seg2->min_coord)
962 min = seg2->min_coord;
963 if (max > seg2->max_coord)
964 max = seg2->max_coord;
966 /* compute maximum coordinate difference of the two segments */
967 len = max - min;
968 if (len >= len_threshold)
970 /* small coordinate differences cause a higher score, and */
971 /* segments with a greater distance cause a higher score also */
972 score = dist + len_score / len;
974 /* and we search for the smallest score */
975 /* of the sum of the two values */
976 if (score < seg1->score)
978 seg1->score = score;
979 seg1->link = seg2;
982 if (score < seg2->score)
984 seg2->score = score;
985 seg2->link = seg1;
992 /* now compute the `serif' segments, cf. explanations in `tahints.h' */
993 for (seg1 = segments; seg1 < segment_limit; seg1++)
995 seg2 = seg1->link;
997 if (seg2)
999 if (seg2->link != seg1)
1001 seg1->link = 0;
1002 seg1->serif = seg2->link;
1009 /* link segments to edges, using feature analysis for selection */
1011 FT_Error
1012 ta_latin_hints_compute_edges(TA_GlyphHints hints,
1013 TA_Dimension dim)
1015 TA_AxisHints axis = &hints->axis[dim];
1016 FT_Error error = FT_Err_Ok;
1017 TA_LatinAxis laxis = &((TA_LatinMetrics)hints->metrics)->axis[dim];
1019 TA_Segment segments = axis->segments;
1020 TA_Segment segment_limit = segments + axis->num_segments;
1021 TA_Segment seg;
1023 #if 0
1024 TA_Direction up_dir;
1025 #endif
1026 FT_Fixed scale;
1027 FT_Pos edge_distance_threshold;
1028 FT_Pos segment_length_threshold;
1031 axis->num_edges = 0;
1033 scale = (dim == TA_DIMENSION_HORZ) ? hints->x_scale
1034 : hints->y_scale;
1036 #if 0
1037 up_dir = (dim == TA_DIMENSION_HORZ) ? TA_DIR_UP
1038 : TA_DIR_RIGHT;
1039 #endif
1041 /* we ignore all segments that are less than 1 pixel in length */
1042 /* to avoid many problems with serif fonts */
1043 /* (the corresponding threshold is computed in font units) */
1044 if (dim == TA_DIMENSION_HORZ)
1045 segment_length_threshold = FT_DivFix(64, hints->y_scale);
1046 else
1047 segment_length_threshold = 0;
1049 /********************************************************************/
1050 /* */
1051 /* We begin by generating a sorted table of edges for the current */
1052 /* direction. To do so, we simply scan each segment and try to find */
1053 /* an edge in our table that corresponds to its position. */
1054 /* */
1055 /* If no edge is found, we create and insert a new edge in the */
1056 /* sorted table. Otherwise, we simply add the segment to the edge's */
1057 /* list which gets processed in the second step to compute the */
1058 /* edge's properties. */
1059 /* */
1060 /* Note that the table of edges is sorted along the segment/edge */
1061 /* position. */
1062 /* */
1063 /********************************************************************/
1065 /* assure that edge distance threshold is at most 0.25px */
1066 edge_distance_threshold = FT_MulFix(laxis->edge_distance_threshold,
1067 scale);
1068 if (edge_distance_threshold > 64 / 4)
1069 edge_distance_threshold = 64 / 4;
1071 edge_distance_threshold = FT_DivFix(edge_distance_threshold,
1072 scale);
1074 for (seg = segments; seg < segment_limit; seg++)
1076 TA_Edge found = NULL;
1077 FT_Int ee;
1080 if (seg->height < segment_length_threshold)
1081 continue;
1083 /* a special case for serif edges: */
1084 /* if they are smaller than 1.5 pixels we ignore them */
1085 if (seg->serif
1086 && 2 * seg->height < 3 * segment_length_threshold)
1087 continue;
1089 /* look for an edge corresponding to the segment */
1090 for (ee = 0; ee < axis->num_edges; ee++)
1092 TA_Edge edge = axis->edges + ee;
1093 FT_Pos dist;
1096 dist = seg->pos - edge->fpos;
1097 if (dist < 0)
1098 dist = -dist;
1100 if (dist < edge_distance_threshold && edge->dir == seg->dir)
1102 found = edge;
1103 break;
1107 if (!found)
1109 TA_Edge edge;
1112 /* insert a new edge in the list and sort according to the position */
1113 error = ta_axis_hints_new_edge(axis, seg->pos,
1114 (TA_Direction)seg->dir,
1115 &edge);
1116 if (error)
1117 goto Exit;
1119 /* add the segment to the new edge's list */
1120 memset(edge, 0, sizeof (TA_EdgeRec));
1121 edge->first = seg;
1122 edge->last = seg;
1123 edge->dir = seg->dir;
1124 edge->fpos = seg->pos;
1125 edge->opos = FT_MulFix(seg->pos, scale);
1126 edge->pos = edge->opos;
1127 seg->edge_next = seg;
1129 else
1131 /* if an edge was found, simply add the segment to the edge's list */
1132 seg->edge_next = found->first;
1133 found->last->edge_next = seg;
1134 found->last = seg;
1138 /*****************************************************************/
1139 /* */
1140 /* Good, we now compute each edge's properties according to */
1141 /* the segments found on its position. Basically, these are */
1142 /* */
1143 /* - the edge's main direction */
1144 /* - stem edge, serif edge or both (which defaults to stem then) */
1145 /* - rounded edge, straight or both (which defaults to straight) */
1146 /* - link for edge */
1147 /* */
1148 /*****************************************************************/
1150 /* first of all, set the `edge' field in each segment -- this is */
1151 /* required in order to compute edge links */
1153 /* note that removing this loop and setting the `edge' field of each */
1154 /* segment directly in the code above slows down execution speed for */
1155 /* some reasons on platforms like the Sun */
1157 TA_Edge edges = axis->edges;
1158 TA_Edge edge_limit = edges + axis->num_edges;
1159 TA_Edge edge;
1162 for (edge = edges; edge < edge_limit; edge++)
1164 seg = edge->first;
1165 if (seg)
1168 seg->edge = edge;
1169 seg = seg->edge_next;
1170 } while (seg != edge->first);
1173 /* now compute each edge properties */
1174 for (edge = edges; edge < edge_limit; edge++)
1176 FT_Int is_round = 0; /* does it contain round segments? */
1177 FT_Int is_straight = 0; /* does it contain straight segments? */
1178 #if 0
1179 FT_Pos ups = 0; /* number of upwards segments */
1180 FT_Pos downs = 0; /* number of downwards segments */
1181 #endif
1184 seg = edge->first;
1188 FT_Bool is_serif;
1191 /* check for roundness of segment */
1192 if (seg->flags & TA_EDGE_ROUND)
1193 is_round++;
1194 else
1195 is_straight++;
1197 #if 0
1198 /* check for segment direction */
1199 if (seg->dir == up_dir)
1200 ups += seg->max_coord - seg->min_coord;
1201 else
1202 downs += seg->max_coord - seg->min_coord;
1203 #endif
1205 /* check for links -- */
1206 /* if seg->serif is set, then seg->link must be ignored */
1207 is_serif = (FT_Bool)(seg->serif
1208 && seg->serif->edge
1209 && seg->serif->edge != edge);
1211 if ((seg->link && seg->link->edge != NULL)
1212 || is_serif)
1214 TA_Edge edge2;
1215 TA_Segment seg2;
1218 edge2 = edge->link;
1219 seg2 = seg->link;
1221 if (is_serif)
1223 seg2 = seg->serif;
1224 edge2 = edge->serif;
1227 if (edge2)
1229 FT_Pos edge_delta;
1230 FT_Pos seg_delta;
1233 edge_delta = edge->fpos - edge2->fpos;
1234 if (edge_delta < 0)
1235 edge_delta = -edge_delta;
1237 seg_delta = seg->pos - seg2->pos;
1238 if (seg_delta < 0)
1239 seg_delta = -seg_delta;
1241 if (seg_delta < edge_delta)
1242 edge2 = seg2->edge;
1244 else
1245 edge2 = seg2->edge;
1247 if (is_serif)
1249 edge->serif = edge2;
1250 edge2->flags |= TA_EDGE_SERIF;
1252 else
1253 edge->link = edge2;
1256 seg = seg->edge_next;
1257 } while (seg != edge->first);
1259 /* set the round/straight flags */
1260 edge->flags = TA_EDGE_NORMAL;
1262 if (is_round > 0
1263 && is_round >= is_straight)
1264 edge->flags |= TA_EDGE_ROUND;
1266 #if 0
1267 /* set the edge's main direction */
1268 edge->dir = TA_DIR_NONE;
1270 if (ups > downs)
1271 edge->dir = (FT_Char)up_dir;
1273 else if (ups < downs)
1274 edge->dir = (FT_Char)-up_dir;
1276 else if (ups == downs)
1277 edge->dir = 0; /* both up and down! */
1278 #endif
1280 /* get rid of serifs if link is set */
1281 /* XXX: this gets rid of many unpleasant artefacts! */
1282 /* example: the `c' in cour.pfa at size 13 */
1284 if (edge->serif && edge->link)
1285 edge->serif = 0;
1289 Exit:
1290 return error;
1294 /* detect segments and edges for given dimension */
1296 FT_Error
1297 ta_latin_hints_detect_features(TA_GlyphHints hints,
1298 TA_Dimension dim)
1300 FT_Error error;
1303 error = ta_latin_hints_compute_segments(hints, dim);
1304 if (!error)
1306 ta_latin_hints_link_segments(hints, dim);
1308 error = ta_latin_hints_compute_edges(hints, dim);
1311 return error;
1315 /* compute all edges which lie within blue zones */
1317 void
1318 ta_latin_hints_compute_blue_edges(TA_GlyphHints hints,
1319 TA_LatinMetrics metrics)
1321 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
1323 TA_Edge edge = axis->edges;
1324 TA_Edge edge_limit = edge + axis->num_edges;
1326 TA_LatinAxis latin = &metrics->axis[TA_DIMENSION_VERT];
1327 FT_Fixed scale = latin->scale;
1330 /* compute which blue zones are active, */
1331 /* i.e. have their scaled size < 3/4 pixels */
1333 /* for each horizontal edge search the blue zone which is closest */
1334 for (; edge < edge_limit; edge++)
1336 FT_Int bb;
1337 TA_Width best_blue = NULL;
1338 FT_Pos best_dist; /* initial threshold */
1340 FT_UInt best_blue_idx = 0;
1341 FT_Bool best_blue_is_shoot = 0;
1344 /* compute the initial threshold as a fraction of the EM size */
1345 /* (the value 40 is heuristic) */
1346 best_dist = FT_MulFix(metrics->units_per_em / 40, scale);
1348 /* assure a minimum distance of 0.5px */
1349 if (best_dist > 64 / 2)
1350 best_dist = 64 / 2;
1352 for (bb = 0; bb < TA_LATIN_BLUE_MAX; bb++)
1354 TA_LatinBlue blue = latin->blues + bb;
1355 FT_Bool is_top_blue, is_major_dir;
1358 /* skip inactive blue zones (i.e., those that are too large) */
1359 if (!(blue->flags & TA_LATIN_BLUE_ACTIVE))
1360 continue;
1362 /* if it is a top zone, check for right edges -- */
1363 /* if it is a bottom zone, check for left edges */
1364 is_top_blue = (FT_Byte)((blue->flags & TA_LATIN_BLUE_TOP) != 0);
1365 is_major_dir = FT_BOOL(edge->dir == axis->major_dir);
1367 /* if it is a top zone, the edge must be against the major */
1368 /* direction; if it is a bottom zone, it must be in the major */
1369 /* direction */
1370 if (is_top_blue ^ is_major_dir)
1372 FT_Pos dist;
1375 /* first of all, compare it to the reference position */
1376 dist = edge->fpos - blue->ref.org;
1377 if (dist < 0)
1378 dist = -dist;
1380 dist = FT_MulFix(dist, scale);
1381 if (dist < best_dist)
1383 best_dist = dist;
1384 best_blue = &blue->ref;
1386 best_blue_idx = bb;
1387 best_blue_is_shoot = 0;
1390 /* now compare it to the overshoot position and check whether */
1391 /* the edge is rounded, and whether the edge is over the */
1392 /* reference position of a top zone, or under the reference */
1393 /* position of a bottom zone */
1394 if (edge->flags & TA_EDGE_ROUND
1395 && dist != 0)
1397 FT_Bool is_under_ref = FT_BOOL(edge->fpos < blue->ref.org);
1400 if (is_top_blue ^ is_under_ref)
1402 dist = edge->fpos - blue->shoot.org;
1403 if (dist < 0)
1404 dist = -dist;
1406 dist = FT_MulFix(dist, scale);
1407 if (dist < best_dist)
1409 best_dist = dist;
1410 best_blue = &blue->shoot;
1412 best_blue_idx = bb;
1413 best_blue_is_shoot = 1;
1420 if (best_blue)
1422 edge->blue_edge = best_blue;
1423 edge->best_blue_idx = best_blue_idx;
1424 edge->best_blue_is_shoot = best_blue_is_shoot;
1430 /* initalize hinting engine */
1432 static FT_Error
1433 ta_latin_hints_init(TA_GlyphHints hints,
1434 TA_LatinMetrics metrics)
1436 FT_Render_Mode mode;
1437 FT_UInt32 scaler_flags, other_flags;
1438 FT_Face face = metrics->root.scaler.face;
1441 ta_glyph_hints_rescale(hints, (TA_ScriptMetrics)metrics);
1443 /* correct x_scale and y_scale if needed, since they may have */
1444 /* been modified by `ta_latin_metrics_scale_dim' above */
1445 hints->x_scale = metrics->axis[TA_DIMENSION_HORZ].scale;
1446 hints->x_delta = metrics->axis[TA_DIMENSION_HORZ].delta;
1447 hints->y_scale = metrics->axis[TA_DIMENSION_VERT].scale;
1448 hints->y_delta = metrics->axis[TA_DIMENSION_VERT].delta;
1450 /* compute flags depending on render mode, etc. */
1451 mode = metrics->root.scaler.render_mode;
1453 #if 0 /* #ifdef TA_CONFIG_OPTION_USE_WARPER */
1454 if (mode == FT_RENDER_MODE_LCD
1455 || mode == FT_RENDER_MODE_LCD_V)
1456 metrics->root.scaler.render_mode =
1457 mode = FT_RENDER_MODE_NORMAL;
1458 #endif
1460 scaler_flags = hints->scaler_flags;
1461 other_flags = 0;
1463 /* we snap the width of vertical stems for the monochrome */
1464 /* and horizontal LCD rendering targets only */
1465 if (mode == FT_RENDER_MODE_MONO
1466 || mode == FT_RENDER_MODE_LCD)
1467 other_flags |= TA_LATIN_HINTS_HORZ_SNAP;
1469 /* we snap the width of horizontal stems for the monochrome */
1470 /* and vertical LCD rendering targets only */
1471 if (mode == FT_RENDER_MODE_MONO
1472 || mode == FT_RENDER_MODE_LCD_V)
1473 other_flags |= TA_LATIN_HINTS_VERT_SNAP;
1475 /* we adjust stems to full pixels only if we don't use the `light' mode */
1476 if (mode != FT_RENDER_MODE_LIGHT)
1477 other_flags |= TA_LATIN_HINTS_STEM_ADJUST;
1479 if (mode == FT_RENDER_MODE_MONO)
1480 other_flags |= TA_LATIN_HINTS_MONO;
1482 /* in `light' hinting mode we disable horizontal hinting completely; */
1483 /* we also do it if the face is italic */
1484 if (mode == FT_RENDER_MODE_LIGHT
1485 || (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0)
1486 scaler_flags |= TA_SCALER_FLAG_NO_HORIZONTAL;
1488 hints->scaler_flags = scaler_flags;
1489 hints->other_flags = other_flags;
1491 return FT_Err_Ok;
1495 /* snap a given width in scaled coordinates to */
1496 /* one of the current standard widths */
1498 static FT_Pos
1499 ta_latin_snap_width(TA_Width widths,
1500 FT_Int count,
1501 FT_Pos width)
1503 int n;
1504 FT_Pos best = 64 + 32 + 2;
1505 FT_Pos reference = width;
1506 FT_Pos scaled;
1509 for (n = 0; n < count; n++)
1511 FT_Pos w;
1512 FT_Pos dist;
1515 w = widths[n].cur;
1516 dist = width - w;
1517 if (dist < 0)
1518 dist = -dist;
1519 if (dist < best)
1521 best = dist;
1522 reference = w;
1526 scaled = TA_PIX_ROUND(reference);
1528 if (width >= reference)
1530 if (width < scaled + 48)
1531 width = reference;
1533 else
1535 if (width > scaled - 48)
1536 width = reference;
1539 return width;
1543 /* compute the snapped width of a given stem, ignoring very thin ones */
1545 /* there is a lot of voodoo in this function; changing the hard-coded */
1546 /* parameters influence the whole hinting process */
1548 static FT_Pos
1549 ta_latin_compute_stem_width(TA_GlyphHints hints,
1550 TA_Dimension dim,
1551 FT_Pos width,
1552 FT_Byte base_flags,
1553 FT_Byte stem_flags)
1555 TA_LatinMetrics metrics = (TA_LatinMetrics) hints->metrics;
1556 TA_LatinAxis axis = &metrics->axis[dim];
1558 FT_Pos dist = width;
1559 FT_Int sign = 0;
1560 FT_Int vertical = (dim == TA_DIMENSION_VERT);
1563 if (!TA_LATIN_HINTS_DO_STEM_ADJUST(hints)
1564 || axis->extra_light)
1565 return width;
1567 if (dist < 0)
1569 dist = -width;
1570 sign = 1;
1573 if ((vertical && !TA_LATIN_HINTS_DO_VERT_SNAP(hints))
1574 || (!vertical && !TA_LATIN_HINTS_DO_HORZ_SNAP(hints)))
1576 /* smooth hinting process: very lightly quantize the stem width */
1578 /* leave the widths of serifs alone */
1579 if ((stem_flags & TA_EDGE_SERIF)
1580 && vertical
1581 && (dist < 3 * 64))
1582 goto Done_Width;
1583 else if (base_flags & TA_EDGE_ROUND)
1585 if (dist < 80)
1586 dist = 64;
1588 else if (dist < 56)
1589 dist = 56;
1591 if (axis->width_count > 0)
1593 FT_Pos delta;
1596 /* compare to standard width */
1597 delta = dist - axis->widths[0].cur;
1599 if (delta < 0)
1600 delta = -delta;
1602 if (delta < 40)
1604 dist = axis->widths[0].cur;
1605 if (dist < 48)
1606 dist = 48;
1608 goto Done_Width;
1611 if (dist < 3 * 64)
1613 delta = dist & 63;
1614 dist &= -64;
1616 if (delta < 10)
1617 dist += delta;
1618 else if (delta < 32)
1619 dist += 10;
1620 else if (delta < 54)
1621 dist += 54;
1622 else
1623 dist += delta;
1625 else
1626 dist = (dist + 32) & ~63;
1629 else
1631 /* strong hinting process: snap the stem width to integer pixels */
1633 FT_Pos org_dist = dist;
1636 dist = ta_latin_snap_width(axis->widths, axis->width_count, dist);
1638 if (vertical)
1640 /* in the case of vertical hinting, */
1641 /* always round the stem heights to integer pixels */
1643 if (dist >= 64)
1644 dist = (dist + 16) & ~63;
1645 else
1646 dist = 64;
1648 else
1650 if (TA_LATIN_HINTS_DO_MONO(hints))
1652 /* monochrome horizontal hinting: */
1653 /* snap widths to integer pixels with a different threshold */
1655 if (dist < 64)
1656 dist = 64;
1657 else
1658 dist = (dist + 32) & ~63;
1660 else
1662 /* for horizontal anti-aliased hinting, we adopt a more subtle */
1663 /* approach: we strengthen small stems, round stems whose size */
1664 /* is between 1 and 2 pixels to an integer, otherwise nothing */
1666 if (dist < 48)
1667 dist = (dist + 64) >> 1;
1669 else if (dist < 128)
1671 /* we only round to an integer width if the corresponding */
1672 /* distortion is less than 1/4 pixel -- otherwise, this */
1673 /* makes everything worse since the diagonals, which are */
1674 /* not hinted, appear a lot bolder or thinner than the */
1675 /* vertical stems */
1677 FT_Pos delta;
1680 dist = (dist + 22) & ~63;
1681 delta = dist - org_dist;
1682 if (delta < 0)
1683 delta = -delta;
1685 if (delta >= 16)
1687 dist = org_dist;
1688 if (dist < 48)
1689 dist = (dist + 64) >> 1;
1692 else
1693 /* round otherwise to prevent color fringes in LCD mode */
1694 dist = (dist + 32) & ~63;
1699 Done_Width:
1700 if (sign)
1701 dist = -dist;
1703 return dist;
1707 /* align one stem edge relative to the previous stem edge */
1709 static void
1710 ta_latin_align_linked_edge(TA_GlyphHints hints,
1711 TA_Dimension dim,
1712 TA_Edge base_edge,
1713 TA_Edge stem_edge)
1715 FT_Pos dist = stem_edge->opos - base_edge->opos;
1717 FT_Pos fitted_width = ta_latin_compute_stem_width(
1718 hints, dim, dist,
1719 base_edge->flags,
1720 stem_edge->flags);
1723 stem_edge->pos = base_edge->pos + fitted_width;
1725 TA_LOG((" LINK: edge %d (opos=%.2f) linked to %.2f,"
1726 " dist was %.2f, now %.2f\n",
1727 stem_edge - hints->axis[dim].edges, stem_edge->opos / 64.0,
1728 stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0));
1730 if (hints->recorder)
1731 hints->recorder(ta_link, hints, dim,
1732 base_edge, stem_edge, NULL, NULL, NULL);
1736 /* shift the coordinates of the `serif' edge by the same amount */
1737 /* as the corresponding `base' edge has been moved already */
1739 static void
1740 ta_latin_align_serif_edge(TA_GlyphHints hints,
1741 TA_Edge base,
1742 TA_Edge serif)
1744 FT_UNUSED(hints);
1746 serif->pos = base->pos + (serif->opos - base->opos);
1750 /* the main grid-fitting routine */
1752 void
1753 ta_latin_hint_edges(TA_GlyphHints hints,
1754 TA_Dimension dim)
1756 TA_AxisHints axis = &hints->axis[dim];
1758 TA_Edge edges = axis->edges;
1759 TA_Edge edge_limit = edges + axis->num_edges;
1760 FT_PtrDist n_edges;
1761 TA_Edge edge;
1763 TA_Edge anchor = NULL;
1764 FT_Int has_serifs = 0;
1767 TA_LOG(("%s edge hinting\n", dim == TA_DIMENSION_VERT ? "horizontal"
1768 : "vertical"));
1770 /* we begin by aligning all stems relative to the blue zone if needed -- */
1771 /* that's only for horizontal edges */
1773 if (dim == TA_DIMENSION_VERT
1774 && TA_HINTS_DO_BLUES(hints))
1776 for (edge = edges; edge < edge_limit; edge++)
1778 TA_Width blue;
1779 TA_Edge edge1, edge2; /* these edges form the stem to check */
1782 if (edge->flags & TA_EDGE_DONE)
1783 continue;
1785 blue = edge->blue_edge;
1786 edge1 = NULL;
1787 edge2 = edge->link;
1789 if (blue)
1790 edge1 = edge;
1792 /* flip edges if the other stem is aligned to a blue zone */
1793 else if (edge2 && edge2->blue_edge)
1795 blue = edge2->blue_edge;
1796 edge1 = edge2;
1797 edge2 = edge;
1800 if (!edge1)
1801 continue;
1803 if (!anchor)
1804 TA_LOG((" BLUE_ANCHOR: edge %d (opos=%.2f) snapped to %.2f,"
1805 " was %.2f (anchor=edge %d)\n",
1806 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
1807 edge1->pos / 64.0, edge - edges));
1808 else
1809 TA_LOG((" BLUE: edge %d (opos=%.2f) snapped to %.2f, was %.2f\n",
1810 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
1811 edge1->pos / 64.0));
1813 edge1->pos = blue->fit;
1814 edge1->flags |= TA_EDGE_DONE;
1816 if (hints->recorder)
1818 if (!anchor)
1819 hints->recorder(ta_blue_anchor, hints, dim,
1820 edge1, edge, NULL, NULL, NULL);
1821 else
1822 hints->recorder(ta_blue, hints, dim,
1823 edge1, NULL, NULL, NULL, NULL);
1826 if (edge2 && !edge2->blue_edge)
1828 ta_latin_align_linked_edge(hints, dim, edge1, edge2);
1829 edge2->flags |= TA_EDGE_DONE;
1832 if (!anchor)
1833 anchor = edge;
1837 /* now we align all other stem edges, */
1838 /* trying to maintain the relative order of stems in the glyph */
1839 for (edge = edges; edge < edge_limit; edge++)
1841 TA_Edge edge2;
1844 if (edge->flags & TA_EDGE_DONE)
1845 continue;
1847 /* skip all non-stem edges */
1848 edge2 = edge->link;
1849 if (!edge2)
1851 has_serifs++;
1852 continue;
1855 /* now align the stem */
1857 /* this should not happen, but it's better to be safe */
1858 if (edge2->blue_edge)
1860 TA_LOG((" ASSERTION FAILED for edge %d\n", edge2-edges));
1862 ta_latin_align_linked_edge(hints, dim, edge2, edge);
1863 edge->flags |= TA_EDGE_DONE;
1864 continue;
1867 if (!anchor)
1869 /* if we reach this if clause, no stem has been aligned yet */
1871 FT_Pos org_len, org_center, cur_len;
1872 FT_Pos cur_pos1, error1, error2, u_off, d_off;
1875 org_len = edge2->opos - edge->opos;
1876 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
1877 edge->flags, edge2->flags);
1879 /* some voodoo to specially round edges for small stem widths; */
1880 /* the idea is to align the center of a stem, */
1881 /* then shifting the stem edges to suitable positions */
1882 if (cur_len <= 64)
1884 /* width <= 1px */
1885 u_off = 32;
1886 d_off = 32;
1888 else
1890 /* 1px < width < 1.5px */
1891 u_off = 38;
1892 d_off = 26;
1895 if (cur_len < 96)
1897 org_center = edge->opos + (org_len >> 1);
1898 cur_pos1 = TA_PIX_ROUND(org_center);
1900 error1 = org_center - (cur_pos1 - u_off);
1901 if (error1 < 0)
1902 error1 = -error1;
1904 error2 = org_center - (cur_pos1 + d_off);
1905 if (error2 < 0)
1906 error2 = -error2;
1908 if (error1 < error2)
1909 cur_pos1 -= u_off;
1910 else
1911 cur_pos1 += d_off;
1913 edge->pos = cur_pos1 - cur_len / 2;
1914 edge2->pos = edge->pos + cur_len;
1916 else
1917 edge->pos = TA_PIX_ROUND(edge->opos);
1919 anchor = edge;
1920 edge->flags |= TA_EDGE_DONE;
1922 TA_LOG((" ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
1923 " snapped to %.2f and %.2f\n",
1924 edge - edges, edge->opos / 64.0,
1925 edge2 - edges, edge2->opos / 64.0,
1926 edge->pos / 64.0, edge2->pos / 64.0));
1928 if (hints->recorder)
1929 hints->recorder(ta_anchor, hints, dim,
1930 edge, edge2, NULL, NULL, NULL);
1932 ta_latin_align_linked_edge(hints, dim, edge, edge2);
1934 else
1936 FT_Pos org_pos, org_len, org_center, cur_len;
1937 FT_Pos cur_pos1, cur_pos2, delta1, delta2;
1940 org_pos = anchor->pos + (edge->opos - anchor->opos);
1941 org_len = edge2->opos - edge->opos;
1942 org_center = org_pos + (org_len >> 1);
1944 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
1945 edge->flags, edge2->flags);
1947 if (edge2->flags & TA_EDGE_DONE)
1949 TA_LOG((" ADJUST: edge %d (pos=%.2f) moved to %.2f\n",
1950 edge - edges, edge->pos / 64.0,
1951 (edge2->pos - cur_len) / 64.0));
1953 edge->pos = edge2->pos - cur_len;
1955 if (hints->recorder)
1957 TA_Edge bound = NULL;
1960 if (edge > edges)
1961 bound = &edge[-1];
1963 hints->recorder(ta_adjust, hints, dim,
1964 edge, edge2, NULL, bound, NULL);
1968 else if (cur_len < 96)
1970 FT_Pos u_off, d_off;
1973 cur_pos1 = TA_PIX_ROUND(org_center);
1975 if (cur_len <= 64)
1977 u_off = 32;
1978 d_off = 32;
1980 else
1982 u_off = 38;
1983 d_off = 26;
1986 delta1 = org_center - (cur_pos1 - u_off);
1987 if (delta1 < 0)
1988 delta1 = -delta1;
1990 delta2 = org_center - (cur_pos1 + d_off);
1991 if (delta2 < 0)
1992 delta2 = -delta2;
1994 if (delta1 < delta2)
1995 cur_pos1 -= u_off;
1996 else
1997 cur_pos1 += d_off;
1999 edge->pos = cur_pos1 - cur_len / 2;
2000 edge2->pos = cur_pos1 + cur_len / 2;
2002 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2003 " snapped to %.2f and %.2f\n",
2004 edge - edges, edge->opos / 64.0,
2005 edge2 - edges, edge2->opos / 64.0,
2006 edge->pos / 64.0, edge2->pos / 64.0));
2008 if (hints->recorder)
2010 TA_Edge bound = NULL;
2013 if (edge > edges)
2014 bound = &edge[-1];
2016 hints->recorder(ta_stem, hints, dim,
2017 edge, edge2, NULL, bound, NULL);
2021 else
2023 org_pos = anchor->pos + (edge->opos - anchor->opos);
2024 org_len = edge2->opos - edge->opos;
2025 org_center = org_pos + (org_len >> 1);
2027 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
2028 edge->flags, edge2->flags);
2030 cur_pos1 = TA_PIX_ROUND(org_pos);
2031 delta1 = cur_pos1 + (cur_len >> 1) - org_center;
2032 if (delta1 < 0)
2033 delta1 = -delta1;
2035 cur_pos2 = TA_PIX_ROUND(org_pos + org_len) - cur_len;
2036 delta2 = cur_pos2 + (cur_len >> 1) - org_center;
2037 if (delta2 < 0)
2038 delta2 = -delta2;
2040 edge->pos = (delta1 < delta2) ? cur_pos1 : cur_pos2;
2041 edge2->pos = edge->pos + cur_len;
2043 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2044 " snapped to %.2f and %.2f\n",
2045 edge - edges, edge->opos / 64.0,
2046 edge2 - edges, edge2->opos / 64.0,
2047 edge->pos / 64.0, edge2->pos / 64.0));
2049 if (hints->recorder)
2051 TA_Edge bound = NULL;
2054 if (edge > edges)
2055 bound = &edge[-1];
2057 hints->recorder(ta_stem, hints, dim,
2058 edge, edge2, NULL, bound, NULL);
2062 edge->flags |= TA_EDGE_DONE;
2063 edge2->flags |= TA_EDGE_DONE;
2065 if (edge > edges
2066 && edge->pos < edge[-1].pos)
2068 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2069 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
2071 edge->pos = edge[-1].pos;
2073 if (hints->recorder)
2074 hints->recorder(ta_bound, hints, dim,
2075 edge, &edge[-1], NULL, NULL, NULL);
2080 /* make sure that lowercase m's maintain their symmetry */
2082 /* In general, lowercase m's have six vertical edges if they are sans */
2083 /* serif, or twelve if they are with serifs. This implementation is */
2084 /* based on that assumption, and seems to work very well with most */
2085 /* faces. However, if for a certain face this assumption is not */
2086 /* true, the m is just rendered like before. In addition, any stem */
2087 /* correction will only be applied to symmetrical glyphs (even if the */
2088 /* glyph is not an m), so the potential for unwanted distortion is */
2089 /* relatively low. */
2091 /* we don't handle horizontal edges since we can't easily assure that */
2092 /* the third (lowest) stem aligns with the base line; it might end up */
2093 /* one pixel higher or lower */
2095 n_edges = edge_limit - edges;
2096 if (dim == TA_DIMENSION_HORZ
2097 && (n_edges == 6 || n_edges == 12))
2099 TA_Edge edge1, edge2, edge3;
2100 FT_Pos dist1, dist2, span, delta;
2103 if (n_edges == 6)
2105 edge1 = edges;
2106 edge2 = edges + 2;
2107 edge3 = edges + 4;
2109 else
2111 edge1 = edges + 1;
2112 edge2 = edges + 5;
2113 edge3 = edges + 9;
2116 dist1 = edge2->opos - edge1->opos;
2117 dist2 = edge3->opos - edge2->opos;
2119 span = dist1 - dist2;
2120 if (span < 0)
2121 span = -span;
2123 if (span < 8)
2125 delta = edge3->pos - (2 * edge2->pos - edge1->pos);
2126 edge3->pos -= delta;
2127 if (edge3->link)
2128 edge3->link->pos -= delta;
2130 /* move the serifs along with the stem */
2131 if (n_edges == 12)
2133 (edges + 8)->pos -= delta;
2134 (edges + 11)->pos -= delta;
2137 edge3->flags |= TA_EDGE_DONE;
2138 if (edge3->link)
2139 edge3->link->flags |= TA_EDGE_DONE;
2143 if (has_serifs || !anchor)
2145 /* now hint the remaining edges (serifs and single) */
2146 /* in order to complete our processing */
2147 for (edge = edges; edge < edge_limit; edge++)
2149 TA_Edge lower_bound = NULL;
2150 TA_Edge upper_bound = NULL;
2152 FT_Pos delta;
2155 if (edge->flags & TA_EDGE_DONE)
2156 continue;
2158 delta = 1000;
2160 if (edge->serif)
2162 delta = edge->serif->opos - edge->opos;
2163 if (delta < 0)
2164 delta = -delta;
2167 if (edge > edges)
2168 lower_bound = &edge[-1];
2170 if (edge + 1 < edge_limit
2171 && edge[1].flags & TA_EDGE_DONE)
2172 upper_bound = &edge[1];
2175 if (delta < 64 + 16)
2177 ta_latin_align_serif_edge(hints, edge->serif, edge);
2179 TA_LOG((" SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
2180 " aligned to %.2f\n",
2181 edge - edges, edge->opos / 64.0,
2182 edge->serif - edges, edge->serif->opos / 64.0,
2183 edge->pos / 64.0));
2185 if (hints->recorder)
2186 hints->recorder(ta_serif, hints, dim,
2187 edge, NULL, NULL, lower_bound, upper_bound);
2189 else if (!anchor)
2191 edge->pos = TA_PIX_ROUND(edge->opos);
2192 anchor = edge;
2194 TA_LOG((" SERIF_ANCHOR: edge %d (opos=%.2f) snapped to %.2f\n",
2195 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
2197 if (hints->recorder)
2198 hints->recorder(ta_serif_anchor, hints, dim,
2199 edge, NULL, NULL, lower_bound, upper_bound);
2201 else
2203 TA_Edge before, after;
2206 for (before = edge - 1; before >= edges; before--)
2207 if (before->flags & TA_EDGE_DONE)
2208 break;
2210 for (after = edge + 1; after < edge_limit; after++)
2211 if (after->flags & TA_EDGE_DONE)
2212 break;
2214 if (before >= edges && before < edge
2215 && after < edge_limit && after > edge)
2217 if (after->opos == before->opos)
2218 edge->pos = before->pos;
2219 else
2220 edge->pos = before->pos + FT_MulDiv(edge->opos - before->opos,
2221 after->pos - before->pos,
2222 after->opos - before->opos);
2224 TA_LOG((" SERIF_LINK1: edge %d (opos=%.2f) snapped to %.2f"
2225 " from %d (opos=%.2f)\n",
2226 edge - edges, edge->opos / 64.0,
2227 edge->pos / 64.0,
2228 before - edges, before->opos / 64.0));
2230 if (hints->recorder)
2231 hints->recorder(ta_serif_link1, hints, dim,
2232 edge, before, after, lower_bound, upper_bound);
2234 else
2236 edge->pos = anchor->pos + ((edge->opos - anchor->opos + 16) & ~31);
2238 TA_LOG((" SERIF_LINK2: edge %d (opos=%.2f) snapped to %.2f\n",
2239 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
2241 if (hints->recorder)
2242 hints->recorder(ta_serif_link2, hints, dim,
2243 edge, NULL, NULL, lower_bound, upper_bound);
2247 edge->flags |= TA_EDGE_DONE;
2249 if (edge > edges
2250 && edge->pos < edge[-1].pos)
2252 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2253 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
2255 edge->pos = edge[-1].pos;
2257 if (hints->recorder)
2258 hints->recorder(ta_bound, hints, dim,
2259 edge, &edge[-1], NULL, NULL, NULL);
2262 if (edge + 1 < edge_limit
2263 && edge[1].flags & TA_EDGE_DONE
2264 && edge->pos > edge[1].pos)
2266 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2267 edge - edges, edge->pos / 64.0, edge[1].pos / 64.0));
2269 edge->pos = edge[1].pos;
2271 if (hints->recorder)
2272 hints->recorder(ta_bound, hints, dim,
2273 edge, &edge[1], NULL, NULL, NULL);
2278 TA_LOG(("\n"));
2282 /* apply the complete hinting algorithm to a latin glyph */
2284 static FT_Error
2285 ta_latin_hints_apply(TA_GlyphHints hints,
2286 FT_Outline* outline,
2287 TA_LatinMetrics metrics)
2289 FT_Error error;
2290 int dim;
2293 error = ta_glyph_hints_reload(hints, outline);
2294 if (error)
2295 goto Exit;
2297 /* analyze glyph outline */
2298 #ifdef TA_CONFIG_OPTION_USE_WARPER
2299 if (metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT
2300 || TA_HINTS_DO_HORIZONTAL(hints))
2301 #else
2302 if (TA_HINTS_DO_HORIZONTAL(hints))
2303 #endif
2305 error = ta_latin_hints_detect_features(hints, TA_DIMENSION_HORZ);
2306 if (error)
2307 goto Exit;
2310 if (TA_HINTS_DO_VERTICAL(hints))
2312 error = ta_latin_hints_detect_features(hints, TA_DIMENSION_VERT);
2313 if (error)
2314 goto Exit;
2316 ta_latin_hints_compute_blue_edges(hints, metrics);
2319 /* grid-fit the outline */
2320 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
2322 #ifdef TA_CONFIG_OPTION_USE_WARPER
2323 if (dim == TA_DIMENSION_HORZ
2324 && metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT)
2326 TA_WarperRec warper;
2327 FT_Fixed scale;
2328 FT_Pos delta;
2331 ta_warper_compute(&warper, hints, (TA_Dimension)dim, &scale, &delta);
2332 ta_glyph_hints_scale_dim(hints, (TA_Dimension)dim, scale, delta);
2334 continue;
2336 #endif
2338 if ((dim == TA_DIMENSION_HORZ && TA_HINTS_DO_HORIZONTAL(hints))
2339 || (dim == TA_DIMENSION_VERT && TA_HINTS_DO_VERTICAL(hints)))
2341 ta_latin_hint_edges(hints, (TA_Dimension)dim);
2342 ta_glyph_hints_align_edge_points(hints, (TA_Dimension)dim);
2343 ta_glyph_hints_align_strong_points(hints, (TA_Dimension)dim);
2344 ta_glyph_hints_align_weak_points(hints, (TA_Dimension)dim);
2347 ta_glyph_hints_save(hints, outline);
2349 Exit:
2350 return error;
2354 /* XXX: this should probably fine tuned to differentiate better between */
2355 /* scripts... */
2357 static const TA_Script_UniRangeRec ta_latin_uniranges[] =
2359 TA_UNIRANGE_REC(0x0020UL, 0x007FUL), /* Basic Latin (no control chars) */
2360 TA_UNIRANGE_REC(0x00A0UL, 0x00FFUL), /* Latin-1 Supplement (no control chars) */
2361 TA_UNIRANGE_REC(0x0100UL, 0x017FUL), /* Latin Extended-A */
2362 TA_UNIRANGE_REC(0x0180UL, 0x024FUL), /* Latin Extended-B */
2363 TA_UNIRANGE_REC(0x0250UL, 0x02AFUL), /* IPA Extensions */
2364 TA_UNIRANGE_REC(0x02B0UL, 0x02FFUL), /* Spacing Modifier Letters */
2365 TA_UNIRANGE_REC(0x0300UL, 0x036FUL), /* Combining Diacritical Marks */
2366 TA_UNIRANGE_REC(0x0370UL, 0x03FFUL), /* Greek and Coptic */
2367 TA_UNIRANGE_REC(0x0400UL, 0x04FFUL), /* Cyrillic */
2368 TA_UNIRANGE_REC(0x0500UL, 0x052FUL), /* Cyrillic Supplement */
2369 TA_UNIRANGE_REC(0x1D00UL, 0x1D7FUL), /* Phonetic Extensions */
2370 TA_UNIRANGE_REC(0x1D80UL, 0x1DBFUL), /* Phonetic Extensions Supplement */
2371 TA_UNIRANGE_REC(0x1DC0UL, 0x1DFFUL), /* Combining Diacritical Marks Supplement */
2372 TA_UNIRANGE_REC(0x1E00UL, 0x1EFFUL), /* Latin Extended Additional */
2373 TA_UNIRANGE_REC(0x1F00UL, 0x1FFFUL), /* Greek Extended */
2374 TA_UNIRANGE_REC(0x2000UL, 0x206FUL), /* General Punctuation */
2375 TA_UNIRANGE_REC(0x2070UL, 0x209FUL), /* Superscripts and Subscripts */
2376 TA_UNIRANGE_REC(0x20A0UL, 0x20CFUL), /* Currency Symbols */
2377 TA_UNIRANGE_REC(0x2150UL, 0x218FUL), /* Number Forms */
2378 TA_UNIRANGE_REC(0x2460UL, 0x24FFUL), /* Enclosed Alphanumerics */
2379 TA_UNIRANGE_REC(0x2C60UL, 0x2C7FUL), /* Latin Extended-C */
2380 TA_UNIRANGE_REC(0x2DE0UL, 0x2DFFUL), /* Cyrillic Extended-A */
2381 TA_UNIRANGE_REC(0xA640UL, 0xA69FUL), /* Cyrillic Extended-B */
2382 TA_UNIRANGE_REC(0xA720UL, 0xA7FFUL), /* Latin Extended-D */
2383 TA_UNIRANGE_REC(0xFB00UL, 0xFB06UL), /* Alphab. Present. Forms (Latin Ligs) */
2384 TA_UNIRANGE_REC(0x1D400UL, 0x1D7FFUL), /* Mathematical Alphanumeric Symbols */
2385 TA_UNIRANGE_REC(0UL, 0UL)
2389 const TA_ScriptClassRec ta_latin_script_class =
2391 TA_SCRIPT_LATIN,
2392 ta_latin_uniranges,
2394 sizeof (TA_LatinMetricsRec),
2396 (TA_Script_InitMetricsFunc)ta_latin_metrics_init,
2397 (TA_Script_ScaleMetricsFunc)ta_latin_metrics_scale,
2398 (TA_Script_DoneMetricsFunc)NULL,
2400 (TA_Script_InitHintsFunc)ta_latin_hints_init,
2401 (TA_Script_ApplyHintsFunc)ta_latin_hints_apply
2404 /* end of talatin.c */