3 /* originally file `aflatin.c' (2011-Mar-28) from FreeType */
5 /* heavily modified 2011 by Werner Lemberg <wl@gnu.org> */
10 #include FT_ADVANCES_H
16 #ifdef TA_CONFIG_OPTION_USE_WARPER
21 /* find segments and links, compute all stem widths, and initialize */
22 /* standard width and height for the glyph with given charcode */
25 ta_latin_metrics_init_widths(TA_LatinMetrics metrics
,
29 /* scan the array of segments in each direction */
30 TA_GlyphHintsRec hints
[1];
33 ta_glyph_hints_init(hints
);
35 metrics
->axis
[TA_DIMENSION_HORZ
].width_count
= 0;
36 metrics
->axis
[TA_DIMENSION_VERT
].width_count
= 0;
42 TA_LatinMetricsRec dummy
[1];
43 TA_Scaler scaler
= &dummy
->root
.scaler
;
46 glyph_index
= FT_Get_Char_Index(face
, charcode
);
50 error
= FT_Load_Glyph(face
, glyph_index
, FT_LOAD_NO_SCALE
);
51 if (error
|| face
->glyph
->outline
.n_points
<= 0)
54 memset(dummy
, 0, sizeof (TA_LatinMetricsRec
));
56 dummy
->units_per_em
= metrics
->units_per_em
;
58 scaler
->x_scale
= 0x10000L
;
59 scaler
->y_scale
= 0x10000L
;
64 scaler
->render_mode
= FT_RENDER_MODE_NORMAL
;
67 ta_glyph_hints_rescale(hints
, (TA_ScriptMetrics
)dummy
);
69 error
= ta_glyph_hints_reload(hints
, &face
->glyph
->outline
);
73 for (dim
= 0; dim
< TA_DIMENSION_MAX
; dim
++)
75 TA_LatinAxis axis
= &metrics
->axis
[dim
];
76 TA_AxisHints axhints
= &hints
->axis
[dim
];
78 TA_Segment seg
, limit
, link
;
79 FT_UInt num_widths
= 0;
82 error
= ta_latin_hints_compute_segments(hints
, (TA_Dimension
)dim
);
86 ta_latin_hints_link_segments(hints
, (TA_Dimension
)dim
);
88 seg
= axhints
->segments
;
89 limit
= seg
+ axhints
->num_segments
;
91 for (; seg
< limit
; seg
++)
95 /* we only consider stem segments there! */
103 dist
= seg
->pos
- link
->pos
;
107 if (num_widths
< TA_LATIN_MAX_WIDTHS
)
108 axis
->widths
[num_widths
++].org
= dist
;
112 ta_sort_widths(num_widths
, axis
->widths
);
113 axis
->width_count
= num_widths
;
117 for (dim
= 0; dim
< TA_DIMENSION_MAX
; dim
++)
119 TA_LatinAxis axis
= &metrics
->axis
[dim
];
123 stdw
= (axis
->width_count
> 0) ? axis
->widths
[0].org
124 : TA_LATIN_CONSTANT(metrics
, 50);
126 /* let's try 20% of the smallest width */
127 axis
->edge_distance_threshold
= stdw
/ 5;
128 axis
->standard_width
= stdw
;
129 axis
->extra_light
= 0;
133 ta_glyph_hints_done(hints
);
137 #define TA_LATIN_MAX_TEST_CHARACTERS 12
140 static const char ta_latin_blue_chars
[TA_LATIN_MAX_BLUES
]
141 [TA_LATIN_MAX_TEST_CHARACTERS
+ 1] =
152 /* find all blue zones; flat segments give the reference points, */
153 /* round segments the overshoot positions */
156 ta_latin_metrics_init_blues(TA_LatinMetrics metrics
,
159 FT_Pos flats
[TA_LATIN_MAX_TEST_CHARACTERS
];
160 FT_Pos rounds
[TA_LATIN_MAX_TEST_CHARACTERS
];
167 TA_LatinAxis axis
= &metrics
->axis
[TA_DIMENSION_VERT
];
168 FT_GlyphSlot glyph
= face
->glyph
;
171 /* we compute the blues simply by loading each character from the */
172 /* `ta_latin_blue_chars[blues]' string, then finding its top-most or */
173 /* bottom-most points (depending on `TA_IS_TOP_BLUE') */
175 TA_LOG(("blue zones computation\n"));
176 TA_LOG(("------------------------------------------------\n"));
178 for (bb
= 0; bb
< TA_LATIN_BLUE_MAX
; bb
++)
180 const char* p
= ta_latin_blue_chars
[bb
];
181 const char* limit
= p
+ TA_LATIN_MAX_TEST_CHARACTERS
;
186 TA_LOG(("blue %3d: ", bb
));
191 for (; p
< limit
&& *p
; p
++)
194 FT_Pos best_y
; /* same as points.y */
195 FT_Int best_point
, best_first
, best_last
;
200 TA_LOG(("'%c'", *p
));
202 /* load the character in the face -- skip unknown or empty ones */
203 glyph_index
= FT_Get_Char_Index(face
, (FT_UInt
)*p
);
204 if (glyph_index
== 0)
207 error
= FT_Load_Glyph(face
, glyph_index
, FT_LOAD_NO_SCALE
);
208 if (error
|| glyph
->outline
.n_points
<= 0)
211 /* now compute min or max point indices and coordinates */
212 points
= glyph
->outline
.points
;
214 best_y
= 0; /* make compiler happy */
215 best_first
= 0; /* ditto */
216 best_last
= 0; /* ditto */
224 for (nn
= 0; nn
< glyph
->outline
.n_contours
; first
= last
+ 1, nn
++)
226 FT_Int old_best_point
= best_point
;
230 last
= glyph
->outline
.contours
[nn
];
232 /* avoid single-point contours since they are never rasterized; */
233 /* in some fonts, they correspond to mark attachment points */
234 /* which are way outside of the glyph's real outline */
238 if (TA_LATIN_IS_TOP_BLUE(bb
))
240 for (pp
= first
; pp
<= last
; pp
++)
242 || points
[pp
].y
> best_y
)
245 best_y
= points
[pp
].y
;
250 for (pp
= first
; pp
<= last
; pp
++)
252 || points
[pp
].y
< best_y
)
255 best_y
= points
[pp
].y
;
259 if (best_point
!= old_best_point
)
265 TA_LOG(("%5d", best_y
));
268 /* now check whether the point belongs to a straight or round */
269 /* segment; we first need to find in which contour the extremum */
270 /* lies, then inspect its previous and next points */
277 /* now look for the previous and next points that are not on the */
278 /* same Y coordinate and threshold the `closeness'... */
284 if (prev
> best_first
)
289 dist
= points
[prev
].y
- best_y
;
290 if (dist
< -5 || dist
> 5)
292 } while (prev
!= best_point
);
296 if (next
< best_last
)
301 dist
= points
[next
].y
- best_y
;
302 if (dist
< -5 || dist
> 5)
304 } while (next
!= best_point
);
306 /* now set the `round' flag depending on the segment's kind */
308 FT_CURVE_TAG(glyph
->outline
.tags
[prev
]) != FT_CURVE_TAG_ON
309 || FT_CURVE_TAG(glyph
->outline
.tags
[next
]) != FT_CURVE_TAG_ON
);
311 TA_LOG(("%c ", round
? 'r' : 'f'));
315 rounds
[num_rounds
++] = best_y
;
317 flats
[num_flats
++] = best_y
;
322 if (num_flats
== 0 && num_rounds
== 0)
324 /* we couldn't find a single glyph to compute this blue zone, */
325 /* we will simply ignore it then */
330 /* we have computed the contents of the `rounds' and `flats' tables, */
331 /* now determine the reference and overshoot position of the blue -- */
332 /* we simply take the median value after a simple sort */
333 ta_sort_pos(num_rounds
, rounds
);
334 ta_sort_pos(num_flats
, flats
);
336 blue
= &axis
->blues
[axis
->blue_count
];
337 blue_ref
= &blue
->ref
.org
;
338 blue_shoot
= &blue
->shoot
.org
;
345 *blue_shoot
= rounds
[num_rounds
/ 2];
347 else if (num_rounds
== 0)
350 *blue_shoot
= flats
[num_flats
/ 2];
354 *blue_ref
= flats
[num_flats
/ 2];
355 *blue_shoot
= rounds
[num_rounds
/ 2];
358 /* there are sometimes problems if the overshoot position of top */
359 /* zones is under its reference position, or the opposite for bottom */
360 /* zones; we must thus check everything there and correct the errors */
361 if (*blue_shoot
!= *blue_ref
)
363 FT_Pos ref
= *blue_ref
;
364 FT_Pos shoot
= *blue_shoot
;
365 FT_Bool over_ref
= FT_BOOL(shoot
> ref
);
368 if (TA_LATIN_IS_TOP_BLUE(bb
) ^ over_ref
)
370 *blue_shoot
= (shoot
+ ref
) / 2;
374 if (TA_LATIN_IS_TOP_BLUE(bb
))
375 blue
->flags
|= TA_LATIN_BLUE_TOP
;
377 /* the following flag is used later to adjust the y and x scales */
378 /* in order to optimize the pixel grid alignment */
379 /* of the top of small letters */
380 if (bb
== TA_LATIN_BLUE_SMALL_TOP
)
381 blue
->flags
|= TA_LATIN_BLUE_ADJUSTMENT
;
383 TA_LOG(("-- ref = %ld, shoot = %ld\n", *blue_ref
, *blue_shoot
));
390 /* check whether all ASCII digits have the same advance width */
393 ta_latin_metrics_check_digits(TA_LatinMetrics metrics
,
397 FT_Bool started
= 0, same_width
= 1;
398 FT_Fixed advance
, old_advance
= 0;
401 /* digit `0' is 0x30 in all supported charmaps */
402 for (i
= 0x30; i
<= 0x39; i
++)
407 glyph_index
= FT_Get_Char_Index(face
, i
);
408 if (glyph_index
== 0)
411 if (FT_Get_Advance(face
, glyph_index
,
414 | FT_LOAD_IGNORE_TRANSFORM
,
420 if (advance
!= old_advance
)
428 old_advance
= advance
;
433 metrics
->root
.digits_have_same_width
= same_width
;
437 /* initialize global metrics */
440 ta_latin_metrics_init(TA_LatinMetrics metrics
,
443 FT_Error error
= FT_Err_Ok
;
444 FT_CharMap oldmap
= face
->charmap
;
447 static const FT_Encoding latin_encodings
[] =
450 FT_ENCODING_APPLE_ROMAN
,
451 FT_ENCODING_ADOBE_STANDARD
,
452 FT_ENCODING_ADOBE_LATIN_1
,
454 FT_ENCODING_NONE
/* end of list */
458 metrics
->units_per_em
= face
->units_per_EM
;
460 /* do we have a latin charmap in there? */
461 for (ee
= 0; latin_encodings
[ee
] != FT_ENCODING_NONE
; ee
++)
463 error
= FT_Select_Charmap(face
, latin_encodings
[ee
]);
470 /* for now, compute the standard width and height from the `o' */
471 ta_latin_metrics_init_widths(metrics
, face
, 'o');
472 ta_latin_metrics_init_blues(metrics
, face
);
473 ta_latin_metrics_check_digits(metrics
, face
);
476 FT_Set_Charmap(face
, oldmap
);
481 /* adjust scaling value, then scale and shift widths */
482 /* and blue zones (if applicable) for given dimension */
485 ta_latin_metrics_scale_dim(TA_LatinMetrics metrics
,
495 if (dim
== TA_DIMENSION_HORZ
)
497 scale
= scaler
->x_scale
;
498 delta
= scaler
->x_delta
;
502 scale
= scaler
->y_scale
;
503 delta
= scaler
->y_delta
;
506 axis
= &metrics
->axis
[dim
];
508 if (axis
->org_scale
== scale
&& axis
->org_delta
== delta
)
511 axis
->org_scale
= scale
;
512 axis
->org_delta
= delta
;
514 /* correct X and Y scale to optimize the alignment of the top of */
515 /* small letters to the pixel grid */
517 TA_LatinAxis Axis
= &metrics
->axis
[TA_DIMENSION_VERT
];
518 TA_LatinBlue blue
= NULL
;
521 for (nn
= 0; nn
< Axis
->blue_count
; nn
++)
523 if (Axis
->blues
[nn
].flags
& TA_LATIN_BLUE_ADJUSTMENT
)
525 blue
= &Axis
->blues
[nn
];
532 FT_Pos scaled
= FT_MulFix(blue
->shoot
.org
, scaler
->y_scale
);
533 FT_Pos fitted
= (scaled
+ 40) & ~63;
536 if (scaled
!= fitted
)
538 if (dim
== TA_DIMENSION_VERT
)
539 scale
= FT_MulDiv(scale
, fitted
, scaled
);
547 if (dim
== TA_DIMENSION_HORZ
)
549 metrics
->root
.scaler
.x_scale
= scale
;
550 metrics
->root
.scaler
.x_delta
= delta
;
554 metrics
->root
.scaler
.y_scale
= scale
;
555 metrics
->root
.scaler
.y_delta
= delta
;
558 /* scale the widths */
559 for (nn
= 0; nn
< axis
->width_count
; nn
++)
561 TA_Width width
= axis
->widths
+ nn
;
564 width
->cur
= FT_MulFix(width
->org
, scale
);
565 width
->fit
= width
->cur
;
568 /* an extra-light axis corresponds to a standard width that is */
569 /* smaller than 0.75 pixels */
571 (FT_Bool
)(FT_MulFix(axis
->standard_width
, scale
) < 32 + 8);
573 if (dim
== TA_DIMENSION_VERT
)
575 /* scale the blue zones */
576 for (nn
= 0; nn
< axis
->blue_count
; nn
++)
578 TA_LatinBlue blue
= &axis
->blues
[nn
];
582 blue
->ref
.cur
= FT_MulFix(blue
->ref
.org
, scale
) + delta
;
583 blue
->ref
.fit
= blue
->ref
.cur
;
584 blue
->shoot
.cur
= FT_MulFix(blue
->shoot
.org
, scale
) + delta
;
585 blue
->shoot
.fit
= blue
->shoot
.cur
;
586 blue
->flags
&= ~TA_LATIN_BLUE_ACTIVE
;
588 /* a blue zone is only active if it is less than 3/4 pixels tall */
589 dist
= FT_MulFix(blue
->ref
.org
- blue
->shoot
.org
, scale
);
590 if (dist
<= 48 && dist
>= -48)
592 FT_Pos delta1
, delta2
;
595 delta1
= blue
->shoot
.org
- blue
->ref
.org
;
600 delta2
= FT_MulFix(delta2
, scale
);
604 else if (delta2
< 64)
605 delta2
= 32 + (((delta2
- 32) + 16) & ~31);
607 delta2
= TA_PIX_ROUND(delta2
);
612 blue
->ref
.fit
= TA_PIX_ROUND(blue
->ref
.cur
);
613 blue
->shoot
.fit
= blue
->ref
.fit
+ delta2
;
615 blue
->flags
|= TA_LATIN_BLUE_ACTIVE
;
622 /* scale global values in both directions */
625 ta_latin_metrics_scale(TA_LatinMetrics metrics
,
628 metrics
->root
.scaler
.render_mode
= scaler
->render_mode
;
629 metrics
->root
.scaler
.face
= scaler
->face
;
631 ta_latin_metrics_scale_dim(metrics
, scaler
, TA_DIMENSION_HORZ
);
632 ta_latin_metrics_scale_dim(metrics
, scaler
, TA_DIMENSION_VERT
);
636 /* walk over all contours and compute its segments */
639 ta_latin_hints_compute_segments(TA_GlyphHints hints
,
642 TA_AxisHints axis
= &hints
->axis
[dim
];
643 FT_Error error
= FT_Err_Ok
;
645 TA_Segment segment
= NULL
;
648 TA_Point
* contour
= hints
->contours
;
649 TA_Point
* contour_limit
= contour
+ hints
->num_contours
;
650 TA_Direction major_dir
, segment_dir
;
653 memset(&seg0
, 0, sizeof (TA_SegmentRec
));
655 seg0
.flags
= TA_EDGE_NORMAL
;
657 major_dir
= (TA_Direction
)TA_ABS(axis
->major_dir
);
658 segment_dir
= major_dir
;
660 axis
->num_segments
= 0;
662 /* set up (u,v) in each point */
663 if (dim
== TA_DIMENSION_HORZ
)
665 TA_Point point
= hints
->points
;
666 TA_Point limit
= point
+ hints
->num_points
;
669 for (; point
< limit
; point
++)
671 point
->u
= point
->fx
;
672 point
->v
= point
->fy
;
677 TA_Point point
= hints
->points
;
678 TA_Point limit
= point
+ hints
->num_points
;
681 for (; point
< limit
; point
++)
683 point
->u
= point
->fy
;
684 point
->v
= point
->fx
;
688 /* do each contour separately */
689 for (; contour
< contour_limit
; contour
++)
691 TA_Point point
= contour
[0];
692 TA_Point last
= point
->prev
;
696 FT_Pos min_pos
= 32000; /* minimum segment pos != min_coord */
697 FT_Pos max_pos
= -32000; /* maximum segment pos != max_coord */
701 if (point
== last
) /* skip singletons -- just in case */
704 if (TA_ABS(last
->out_dir
) == major_dir
705 && TA_ABS(point
->out_dir
) == major_dir
)
707 /* we are already on an edge, try to locate its start */
713 if (TA_ABS(point
->out_dir
) != major_dir
)
739 if (point
->out_dir
!= segment_dir
742 /* we are just leaving an edge; record a new segment! */
743 segment
->last
= point
;
744 segment
->pos
= (FT_Short
)((min_pos
+ max_pos
) >> 1);
746 /* a segment is round if either its first or last point */
747 /* is a control point */
748 if ((segment
->first
->flags
| point
->flags
) & TA_FLAG_CONTROL
)
749 segment
->flags
|= TA_EDGE_ROUND
;
751 /* compute segment size */
752 min_pos
= max_pos
= point
->v
;
754 v
= segment
->first
->v
;
760 segment
->min_coord
= (FT_Short
)min_pos
;
761 segment
->max_coord
= (FT_Short
)max_pos
;
762 segment
->height
= (FT_Short
)(segment
->max_coord
-
771 /* now exit if we are at the start/end point */
780 && TA_ABS(point
->out_dir
) == major_dir
)
782 /* this is the start of a new segment! */
783 segment_dir
= (TA_Direction
)point
->out_dir
;
785 /* clear all segment fields */
786 error
= ta_axis_hints_new_segment(axis
, &segment
);
791 segment
->dir
= (FT_Char
)segment_dir
;
792 min_pos
= max_pos
= point
->u
;
793 segment
->first
= point
;
794 segment
->last
= point
;
795 segment
->contour
= contour
;
804 /* now slightly increase the height of segments if this makes sense -- */
805 /* this is used to better detect and ignore serifs */
807 TA_Segment segments
= axis
->segments
;
808 TA_Segment segments_end
= segments
+ axis
->num_segments
;
811 for (segment
= segments
; segment
< segments_end
; segment
++)
813 TA_Point first
= segment
->first
;
814 TA_Point last
= segment
->last
;
816 FT_Pos first_v
= first
->v
;
817 FT_Pos last_v
= last
->v
;
823 if (first_v
< last_v
)
830 segment
->height
= (FT_Short
)(segment
->height
+
831 ((first_v
- p
->v
) >> 1));
835 segment
->height
= (FT_Short
)(segment
->height
+
836 ((p
->v
- last_v
) >> 1));
845 segment
->height
= (FT_Short
)(segment
->height
+
846 ((p
->v
- first_v
) >> 1));
850 segment
->height
= (FT_Short
)(segment
->height
+
851 ((last_v
- p
->v
) >> 1));
861 /* link segments to form stems and serifs */
864 ta_latin_hints_link_segments(TA_GlyphHints hints
,
867 TA_AxisHints axis
= &hints
->axis
[dim
];
869 TA_Segment segments
= axis
->segments
;
870 TA_Segment segment_limit
= segments
+ axis
->num_segments
;
872 FT_Pos len_threshold
, len_score
;
873 TA_Segment seg1
, seg2
;
876 len_threshold
= TA_LATIN_CONSTANT(hints
->metrics
, 8);
877 if (len_threshold
== 0)
880 len_score
= TA_LATIN_CONSTANT(hints
->metrics
, 6000);
882 /* now compare each segment to the others */
883 for (seg1
= segments
; seg1
< segment_limit
; seg1
++)
885 /* the fake segments are introduced to hint the metrics -- */
886 /* we must never link them to anything */
887 if (seg1
->dir
!= axis
->major_dir
888 || seg1
->first
== seg1
->last
)
891 /* search for stems having opposite directions, */
892 /* with seg1 to the `left' of seg2 */
893 for (seg2
= segments
; seg2
< segment_limit
; seg2
++)
895 FT_Pos pos1
= seg1
->pos
;
896 FT_Pos pos2
= seg2
->pos
;
899 if (seg1
->dir
+ seg2
->dir
== 0
902 /* compute distance between the two segments */
903 FT_Pos dist
= pos2
- pos1
;
904 FT_Pos min
= seg1
->min_coord
;
905 FT_Pos max
= seg1
->max_coord
;
909 if (min
< seg2
->min_coord
)
910 min
= seg2
->min_coord
;
911 if (max
> seg2
->max_coord
)
912 max
= seg2
->max_coord
;
914 /* compute maximum coordinate difference of the two segments */
916 if (len
>= len_threshold
)
918 /* small coordinate differences cause a higher score, and */
919 /* segments with a greater distance cause a higher score also */
920 score
= dist
+ len_score
/ len
;
922 /* and we search for the smallest score */
923 /* of the sum of the two values */
924 if (score
< seg1
->score
)
930 if (score
< seg2
->score
)
940 /* now compute the `serif' segments, cf. explanations in `tahints.h' */
941 for (seg1
= segments
; seg1
< segment_limit
; seg1
++)
947 if (seg2
->link
!= seg1
)
950 seg1
->serif
= seg2
->link
;
957 /* link segments to edges, using feature analysis for selection */
960 ta_latin_hints_compute_edges(TA_GlyphHints hints
,
963 TA_AxisHints axis
= &hints
->axis
[dim
];
964 FT_Error error
= FT_Err_Ok
;
965 TA_LatinAxis laxis
= &((TA_LatinMetrics
)hints
->metrics
)->axis
[dim
];
967 TA_Segment segments
= axis
->segments
;
968 TA_Segment segment_limit
= segments
+ axis
->num_segments
;
973 FT_Pos edge_distance_threshold
;
974 FT_Pos segment_length_threshold
;
979 scale
= (dim
== TA_DIMENSION_HORZ
) ? hints
->x_scale
982 up_dir
= (dim
== TA_DIMENSION_HORZ
) ? TA_DIR_UP
985 /* we ignore all segments that are less than 1 pixel in length */
986 /* to avoid many problems with serif fonts */
987 /* (the corresponding threshold is computed in font units) */
988 if (dim
== TA_DIMENSION_HORZ
)
989 segment_length_threshold
= FT_DivFix(64, hints
->y_scale
);
991 segment_length_threshold
= 0;
993 /********************************************************************/
995 /* We begin by generating a sorted table of edges for the current */
996 /* direction. To do so, we simply scan each segment and try to find */
997 /* an edge in our table that corresponds to its position. */
999 /* If no edge is found, we create and insert a new edge in the */
1000 /* sorted table. Otherwise, we simply add the segment to the edge's */
1001 /* list which gets processed in the second step to compute the */
1002 /* edge's properties. */
1004 /* Note that the table of edges is sorted along the segment/edge */
1007 /********************************************************************/
1009 /* assure that edge distance threshold is at least 0.25px */
1010 edge_distance_threshold
= FT_MulFix(laxis
->edge_distance_threshold
,
1012 if (edge_distance_threshold
> 64 / 4)
1013 edge_distance_threshold
= 64 / 4;
1015 edge_distance_threshold
= FT_DivFix(edge_distance_threshold
,
1018 for (seg
= segments
; seg
< segment_limit
; seg
++)
1020 TA_Edge found
= NULL
;
1024 if (seg
->height
< segment_length_threshold
)
1027 /* a special case for serif edges: */
1028 /* if they are smaller than 1.5 pixels we ignore them */
1030 && 2 * seg
->height
< 3 * segment_length_threshold
)
1033 /* look for an edge corresponding to the segment */
1034 for (ee
= 0; ee
< axis
->num_edges
; ee
++)
1036 TA_Edge edge
= axis
->edges
+ ee
;
1040 dist
= seg
->pos
- edge
->fpos
;
1044 if (dist
< edge_distance_threshold
&& edge
->dir
== seg
->dir
)
1056 /* insert a new edge in the list and sort according to the position */
1057 error
= ta_axis_hints_new_edge(axis
, seg
->pos
,
1058 (TA_Direction
)seg
->dir
,
1063 /* add the segment to the new edge's list */
1064 memset(edge
, 0, sizeof (TA_EdgeRec
));
1067 edge
->dir
= seg
->dir
;
1068 edge
->fpos
= seg
->pos
;
1069 edge
->opos
= FT_MulFix(seg
->pos
, scale
);
1070 edge
->pos
= edge
->opos
;
1071 seg
->edge_next
= seg
;
1075 /* if an edge was found, simply add the segment to the edge's list */
1076 seg
->edge_next
= found
->first
;
1077 found
->last
->edge_next
= seg
;
1082 /*****************************************************************/
1084 /* Good, we now compute each edge's properties according to */
1085 /* the segments found on its position. Basically, these are */
1087 /* - the edge's main direction */
1088 /* - stem edge, serif edge or both (which defaults to stem then) */
1089 /* - rounded edge, straight or both (which defaults to straight) */
1090 /* - link for edge */
1092 /*****************************************************************/
1094 /* first of all, set the `edge' field in each segment -- this is */
1095 /* required in order to compute edge links */
1097 /* note that removing this loop and setting the `edge' field of each */
1098 /* segment directly in the code above slows down execution speed for */
1099 /* some reasons on platforms like the Sun */
1101 TA_Edge edges
= axis
->edges
;
1102 TA_Edge edge_limit
= edges
+ axis
->num_edges
;
1106 for (edge
= edges
; edge
< edge_limit
; edge
++)
1113 seg
= seg
->edge_next
;
1114 } while (seg
!= edge
->first
);
1117 /* now compute each edge properties */
1118 for (edge
= edges
; edge
< edge_limit
; edge
++)
1120 FT_Int is_round
= 0; /* does it contain round segments? */
1121 FT_Int is_straight
= 0; /* does it contain straight segments? */
1123 FT_Pos ups
= 0; /* number of upwards segments */
1124 FT_Pos downs
= 0; /* number of downwards segments */
1135 /* check for roundness of segment */
1136 if (seg
->flags
& TA_EDGE_ROUND
)
1142 /* check for segment direction */
1143 if (seg
->dir
== up_dir
)
1144 ups
+= seg
->max_coord
- seg
->min_coord
;
1146 downs
+= seg
->max_coord
- seg
->min_coord
;
1149 /* check for links -- */
1150 /* if seg->serif is set, then seg->link must be ignored */
1151 is_serif
= (FT_Bool
)(seg
->serif
1153 && seg
->serif
->edge
!= edge
);
1155 if ((seg
->link
&& seg
->link
->edge
!= NULL
)
1168 edge2
= edge
->serif
;
1177 edge_delta
= edge
->fpos
- edge2
->fpos
;
1179 edge_delta
= -edge_delta
;
1181 seg_delta
= seg
->pos
- seg2
->pos
;
1183 seg_delta
= -seg_delta
;
1185 if (seg_delta
< edge_delta
)
1193 edge
->serif
= edge2
;
1194 edge2
->flags
|= TA_EDGE_SERIF
;
1200 seg
= seg
->edge_next
;
1201 } while (seg
!= edge
->first
);
1203 /* set the round/straight flags */
1204 edge
->flags
= TA_EDGE_NORMAL
;
1207 && is_round
>= is_straight
)
1208 edge
->flags
|= TA_EDGE_ROUND
;
1211 /* set the edge's main direction */
1212 edge
->dir
= TA_DIR_NONE
;
1215 edge
->dir
= (FT_Char
)up_dir
;
1217 else if (ups
< downs
)
1218 edge
->dir
= (FT_Char
)-up_dir
;
1220 else if (ups
== downs
)
1221 edge
->dir
= 0; /* both up and down! */
1224 /* get rid of serifs if link is set */
1225 /* XXX: this gets rid of many unpleasant artefacts! */
1226 /* example: the `c' in cour.pfa at size 13 */
1228 if (edge
->serif
&& edge
->link
)
1238 /* detect segments and edges for given dimension */
1241 ta_latin_hints_detect_features(TA_GlyphHints hints
,
1247 error
= ta_latin_hints_compute_segments(hints
, dim
);
1250 ta_latin_hints_link_segments(hints
, dim
);
1252 error
= ta_latin_hints_compute_edges(hints
, dim
);
1259 /* compute all edges which lie within blue zones */
1262 ta_latin_hints_compute_blue_edges(TA_GlyphHints hints
,
1263 TA_LatinMetrics metrics
)
1265 TA_AxisHints axis
= &hints
->axis
[TA_DIMENSION_VERT
];
1267 TA_Edge edge
= axis
->edges
;
1268 TA_Edge edge_limit
= edge
+ axis
->num_edges
;
1270 TA_LatinAxis latin
= &metrics
->axis
[TA_DIMENSION_VERT
];
1271 FT_Fixed scale
= latin
->scale
;
1274 /* compute which blue zones are active, */
1275 /* i.e. have their scaled size < 3/4 pixels */
1277 /* for each horizontal edge search the blue zone which is closest */
1278 for (; edge
< edge_limit
; edge
++)
1281 TA_Width best_blue
= NULL
;
1282 FT_Pos best_dist
; /* initial threshold */
1285 /* compute the initial threshold as a fraction of the EM size */
1286 /* (the value 40 is heuristic) */
1287 best_dist
= FT_MulFix(metrics
->units_per_em
/ 40, scale
);
1289 /* assure a minimum distance of 0.5px */
1290 if (best_dist
> 64 / 2)
1293 for (bb
= 0; bb
< TA_LATIN_BLUE_MAX
; bb
++)
1295 TA_LatinBlue blue
= latin
->blues
+ bb
;
1296 FT_Bool is_top_blue
, is_major_dir
;
1299 /* skip inactive blue zones (i.e., those that are too large) */
1300 if (!(blue
->flags
& TA_LATIN_BLUE_ACTIVE
))
1303 /* if it is a top zone, check for right edges -- */
1304 /* if it is a bottom zone, check for left edges */
1305 is_top_blue
= (FT_Byte
)((blue
->flags
& TA_LATIN_BLUE_TOP
) != 0);
1306 is_major_dir
= FT_BOOL(edge
->dir
== axis
->major_dir
);
1308 /* if it is a top zone, the edge must be against the major */
1309 /* direction; if it is a bottom zone, it must be in the major */
1311 if (is_top_blue
^ is_major_dir
)
1316 /* first of all, compare it to the reference position */
1317 dist
= edge
->fpos
- blue
->ref
.org
;
1321 dist
= FT_MulFix(dist
, scale
);
1322 if (dist
< best_dist
)
1325 best_blue
= &blue
->ref
;
1328 /* now compare it to the overshoot position and check whether */
1329 /* the edge is rounded, and whether the edge is over the */
1330 /* reference position of a top zone, or under the reference */
1331 /* position of a bottom zone */
1332 if (edge
->flags
& TA_EDGE_ROUND
1335 FT_Bool is_under_ref
= FT_BOOL(edge
->fpos
< blue
->ref
.org
);
1338 if (is_top_blue
^ is_under_ref
)
1340 dist
= edge
->fpos
- blue
->shoot
.org
;
1344 dist
= FT_MulFix(dist
, scale
);
1345 if (dist
< best_dist
)
1348 best_blue
= &blue
->shoot
;
1356 edge
->blue_edge
= best_blue
;
1361 /* initalize hinting engine */
1364 ta_latin_hints_init(TA_GlyphHints hints
,
1365 TA_LatinMetrics metrics
)
1367 FT_Render_Mode mode
;
1368 FT_UInt32 scaler_flags
, other_flags
;
1369 FT_Face face
= metrics
->root
.scaler
.face
;
1372 ta_glyph_hints_rescale(hints
, (TA_ScriptMetrics
)metrics
);
1374 /* correct x_scale and y_scale if needed, since they may have */
1375 /* been modified by `ta_latin_metrics_scale_dim' above */
1376 hints
->x_scale
= metrics
->axis
[TA_DIMENSION_HORZ
].scale
;
1377 hints
->x_delta
= metrics
->axis
[TA_DIMENSION_HORZ
].delta
;
1378 hints
->y_scale
= metrics
->axis
[TA_DIMENSION_VERT
].scale
;
1379 hints
->y_delta
= metrics
->axis
[TA_DIMENSION_VERT
].delta
;
1381 /* compute flags depending on render mode, etc. */
1382 mode
= metrics
->root
.scaler
.render_mode
;
1384 #if 0 /* #ifdef TA_CONFIG_OPTION_USE_WARPER */
1385 if (mode
== FT_RENDER_MODE_LCD
1386 || mode
== FT_RENDER_MODE_LCD_V
)
1387 metrics
->root
.scaler
.render_mode
=
1388 mode
= FT_RENDER_MODE_NORMAL
;
1391 scaler_flags
= hints
->scaler_flags
;
1394 /* we snap the width of vertical stems for the monochrome */
1395 /* and horizontal LCD rendering targets only */
1396 if (mode
== FT_RENDER_MODE_MONO
1397 || mode
== FT_RENDER_MODE_LCD
)
1398 other_flags
|= TA_LATIN_HINTS_HORZ_SNAP
;
1400 /* we snap the width of horizontal stems for the monochrome */
1401 /* and vertical LCD rendering targets only */
1402 if (mode
== FT_RENDER_MODE_MONO
1403 || mode
== FT_RENDER_MODE_LCD_V
)
1404 other_flags
|= TA_LATIN_HINTS_VERT_SNAP
;
1406 /* we adjust stems to full pixels only if we don't use the `light' mode */
1407 if (mode
!= FT_RENDER_MODE_LIGHT
)
1408 other_flags
|= TA_LATIN_HINTS_STEM_ADJUST
;
1410 if (mode
== FT_RENDER_MODE_MONO
)
1411 other_flags
|= TA_LATIN_HINTS_MONO
;
1413 /* in `light' hinting mode we disable horizontal hinting completely; */
1414 /* we also do it if the face is italic */
1415 if (mode
== FT_RENDER_MODE_LIGHT
1416 || (face
->style_flags
& FT_STYLE_FLAG_ITALIC
) != 0)
1417 scaler_flags
|= TA_SCALER_FLAG_NO_HORIZONTAL
;
1419 hints
->scaler_flags
= scaler_flags
;
1420 hints
->other_flags
= other_flags
;
1426 /* snap a given width in scaled coordinates to */
1427 /* one of the current standard widths */
1430 ta_latin_snap_width(TA_Width widths
,
1435 FT_Pos best
= 64 + 32 + 2;
1436 FT_Pos reference
= width
;
1440 for (n
= 0; n
< count
; n
++)
1457 scaled
= TA_PIX_ROUND(reference
);
1459 if (width
>= reference
)
1461 if (width
< scaled
+ 48)
1466 if (width
> scaled
- 48)
1474 /* compute the snapped width of a given stem, ignoring very thin ones */
1476 /* there is a lot of voodoo in this function; changing the hard-coded */
1477 /* parameters influence the whole hinting process */
1480 ta_latin_compute_stem_width(TA_GlyphHints hints
,
1486 TA_LatinMetrics metrics
= (TA_LatinMetrics
) hints
->metrics
;
1487 TA_LatinAxis axis
= & metrics
->axis
[dim
];
1489 FT_Pos dist
= width
;
1491 FT_Int vertical
= (dim
== TA_DIMENSION_VERT
);
1494 if (!TA_LATIN_HINTS_DO_STEM_ADJUST(hints
)
1495 || axis
->extra_light
)
1504 if ((vertical
&& !TA_LATIN_HINTS_DO_VERT_SNAP(hints
))
1505 || (!vertical
&& !TA_LATIN_HINTS_DO_HORZ_SNAP(hints
)))
1507 /* smooth hinting process: very lightly quantize the stem width */
1509 /* leave the widths of serifs alone */
1510 if ((stem_flags
& TA_EDGE_SERIF
)
1514 else if (base_flags
& TA_EDGE_ROUND
)
1522 if (axis
->width_count
> 0)
1527 /* compare to standard width */
1528 delta
= dist
- axis
->widths
[0].cur
;
1535 dist
= axis
->widths
[0].cur
;
1549 else if (delta
< 32)
1551 else if (delta
< 54)
1557 dist
= (dist
+ 32) & ~63;
1562 /* strong hinting process: snap the stem width to integer pixels */
1564 FT_Pos org_dist
= dist
;
1567 dist
= ta_latin_snap_width(axis
->widths
, axis
->width_count
, dist
);
1571 /* in the case of vertical hinting, */
1572 /* always round the stem heights to integer pixels */
1575 dist
= (dist
+ 16) & ~63;
1581 if (TA_LATIN_HINTS_DO_MONO(hints
))
1583 /* monochrome horizontal hinting: */
1584 /* snap widths to integer pixels with a different threshold */
1589 dist
= (dist
+ 32) & ~63;
1593 /* for horizontal anti-aliased hinting, we adopt a more subtle */
1594 /* approach: we strengthen small stems, round stems whose size */
1595 /* is between 1 and 2 pixels to an integer, otherwise nothing */
1598 dist
= (dist
+ 64) >> 1;
1600 else if (dist
< 128)
1602 /* we only round to an integer width if the corresponding */
1603 /* distortion is less than 1/4 pixel -- otherwise, this */
1604 /* makes everything worse since the diagonals, which are */
1605 /* not hinted, appear a lot bolder or thinner than the */
1606 /* vertical stems */
1611 dist
= (dist
+ 22) & ~63;
1612 delta
= dist
- org_dist
;
1620 dist
= (dist
+ 64) >> 1;
1624 /* round otherwise to prevent color fringes in LCD mode */
1625 dist
= (dist
+ 32) & ~63;
1638 /* align one stem edge relative to the previous stem edge */
1641 ta_latin_align_linked_edge(TA_GlyphHints hints
,
1646 FT_Pos dist
= stem_edge
->opos
- base_edge
->opos
;
1648 FT_Pos fitted_width
= ta_latin_compute_stem_width(
1654 stem_edge
->pos
= base_edge
->pos
+ fitted_width
;
1656 TA_LOG(("LINK: edge %d (opos=%.2f) linked to (%.2f),"
1657 " dist was %.2f, now %.2f\n",
1658 stem_edge
-hints
->axis
[dim
].edges
, stem_edge
->opos
/ 64.0,
1659 stem_edge
->pos
/ 64.0, dist
/ 64.0, fitted_width
/ 64.0));
1663 /* shift the coordinates of the `serif' edge by the same amount */
1664 /* as the corresponding `base' edge has been moved already */
1667 ta_latin_align_serif_edge(TA_GlyphHints hints
,
1673 serif
->pos
= base
->pos
+ (serif
->opos
- base
->opos
);
1677 /* the main grid-fitting routine */
1680 ta_latin_hint_edges(TA_GlyphHints hints
,
1683 TA_AxisHints axis
= &hints
->axis
[dim
];
1685 TA_Edge edges
= axis
->edges
;
1686 TA_Edge edge_limit
= edges
+ axis
->num_edges
;
1690 TA_Edge anchor
= NULL
;
1691 FT_Int has_serifs
= 0;
1694 /* we begin by aligning all stems relative to the blue zone if needed -- */
1695 /* that's only for horizontal edges */
1697 if (dim
== TA_DIMENSION_VERT
1698 && TA_HINTS_DO_BLUES(hints
))
1700 for (edge
= edges
; edge
< edge_limit
; edge
++)
1703 TA_Edge edge1
, edge2
;
1706 if (edge
->flags
& TA_EDGE_DONE
)
1709 blue
= edge
->blue_edge
;
1715 else if (edge2
&& edge2
->blue_edge
)
1717 blue
= edge2
->blue_edge
;
1725 TA_LOG(("BLUE: edge %d (opos=%.2f) snapped to (%.2f),"
1727 edge1
- edges
, edge1
->opos
/ 64.0, blue
->fit
/ 64.0,
1728 edge1
->pos
/ 64.0));
1730 edge1
->pos
= blue
->fit
;
1731 edge1
->flags
|= TA_EDGE_DONE
;
1733 if (edge2
&& !edge2
->blue_edge
)
1735 ta_latin_align_linked_edge(hints
, dim
, edge1
, edge2
);
1736 edge2
->flags
|= TA_EDGE_DONE
;
1744 /* now we align all stem edges, */
1745 /* trying to maintain the relative order of stems in the glyph */
1746 for (edge
= edges
; edge
< edge_limit
; edge
++)
1751 if (edge
->flags
& TA_EDGE_DONE
)
1754 /* skip all non-stem edges */
1762 /* now align the stem */
1764 /* this should not happen, but it's better to be safe */
1765 if (edge2
->blue_edge
)
1767 TA_LOG(("ASSERTION FAILED for edge %d\n", edge2
-edges
));
1769 ta_latin_align_linked_edge(hints
, dim
, edge2
, edge
);
1770 edge
->flags
|= TA_EDGE_DONE
;
1776 FT_Pos org_len
, org_center
, cur_len
;
1777 FT_Pos cur_pos1
, error1
, error2
, u_off
, d_off
;
1780 org_len
= edge2
->opos
- edge
->opos
;
1781 cur_len
= ta_latin_compute_stem_width(hints
, dim
, org_len
,
1782 edge
->flags
, edge2
->flags
);
1784 /* some voodoo to specially round edges for small stem widths */
1798 org_center
= edge
->opos
+ (org_len
>> 1);
1799 cur_pos1
= TA_PIX_ROUND(org_center
);
1801 error1
= org_center
- (cur_pos1
- u_off
);
1805 error2
= org_center
- (cur_pos1
+ d_off
);
1809 if (error1
< error2
)
1814 edge
->pos
= cur_pos1
- cur_len
/ 2;
1815 edge2
->pos
= edge
->pos
+ cur_len
;
1818 edge
->pos
= TA_PIX_ROUND(edge
->opos
);
1820 TA_LOG(("ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
1821 " snapped to (%.2f) (%.2f)\n",
1822 edge
- edges
, edge
->opos
/ 64.0,
1823 edge2
- edges
, edge2
->opos
/ 64.0,
1824 edge
->pos
/ 64.0, edge2
->pos
/ 64.0));
1827 edge
->flags
|= TA_EDGE_DONE
;
1829 ta_latin_align_linked_edge(hints
, dim
, edge
, edge2
);
1833 FT_Pos org_pos
, org_len
, org_center
, cur_len
;
1834 FT_Pos cur_pos1
, cur_pos2
, delta1
, delta2
;
1837 org_pos
= anchor
->pos
+ (edge
->opos
- anchor
->opos
);
1838 org_len
= edge2
->opos
- edge
->opos
;
1839 org_center
= org_pos
+ (org_len
>> 1);
1841 cur_len
= ta_latin_compute_stem_width(hints
, dim
, org_len
,
1842 edge
->flags
, edge2
->flags
);
1844 if (edge2
->flags
& TA_EDGE_DONE
)
1845 edge
->pos
= edge2
->pos
- cur_len
;
1847 else if (cur_len
< 96)
1849 FT_Pos u_off
, d_off
;
1852 cur_pos1
= TA_PIX_ROUND(org_center
);
1865 delta1
= org_center
- (cur_pos1
- u_off
);
1869 delta2
= org_center
- (cur_pos1
+ d_off
);
1873 if (delta1
< delta2
)
1878 edge
->pos
= cur_pos1
- cur_len
/ 2;
1879 edge2
->pos
= cur_pos1
+ cur_len
/ 2;
1881 TA_LOG(("STEM: %d (opos=%.2f) to %d (opos=%.2f)"
1882 " snapped to (%.2f) and (%.2f)\n",
1883 edge
- edges
, edge
->opos
/ 64.0,
1884 edge2
- edges
, edge2
->opos
/ 64.0,
1885 edge
->pos
/ 64.0, edge2
->pos
/ 64.0));
1890 org_pos
= anchor
->pos
+ (edge
->opos
- anchor
->opos
);
1891 org_len
= edge2
->opos
- edge
->opos
;
1892 org_center
= org_pos
+ (org_len
>> 1);
1894 cur_len
= ta_latin_compute_stem_width(hints
, dim
, org_len
,
1895 edge
->flags
, edge2
->flags
);
1897 cur_pos1
= TA_PIX_ROUND(org_pos
);
1898 delta1
= cur_pos1
+ (cur_len
>> 1) - org_center
;
1902 cur_pos2
= TA_PIX_ROUND(org_pos
+ org_len
) - cur_len
;
1903 delta2
= cur_pos2
+ (cur_len
>> 1) - org_center
;
1907 edge
->pos
= (delta1
< delta2
) ? cur_pos1
: cur_pos2
;
1908 edge2
->pos
= edge
->pos
+ cur_len
;
1910 TA_LOG(("STEM: %d (opos=%.2f) to %d (opos=%.2f)"
1911 " snapped to (%.2f) and (%.2f)\n",
1912 edge
- edges
, edge
->opos
/ 64.0,
1913 edge2
- edges
, edge2
->opos
/ 64.0,
1914 edge
->pos
/ 64.0, edge2
->pos
/ 64.0));
1917 edge
->flags
|= TA_EDGE_DONE
;
1918 edge2
->flags
|= TA_EDGE_DONE
;
1921 && edge
->pos
< edge
[-1].pos
)
1923 TA_LOG(("BOUND: %d (pos=%.2f) to (%.2f)\n",
1924 edge
- edges
, edge
->pos
/ 64.0, edge
[-1].pos
/ 64.0));
1925 edge
->pos
= edge
[-1].pos
;
1930 /* make sure that lowercase m's maintain their symmetry */
1932 /* In general, lowercase m's have six vertical edges if they are sans */
1933 /* serif, or twelve if they are with serifs. This implementation is */
1934 /* based on that assumption, and seems to work very well with most */
1935 /* faces. However, if for a certain face this assumption is not */
1936 /* true, the m is just rendered like before. In addition, any stem */
1937 /* correction will only be applied to symmetrical glyphs (even if the */
1938 /* glyph is not an m), so the potential for unwanted distortion is */
1939 /* relatively low. */
1941 /* we don't handle horizontal edges since we can't easily assure that */
1942 /* the third (lowest) stem aligns with the base line; it might end up */
1943 /* one pixel higher or lower */
1945 n_edges
= edge_limit
- edges
;
1946 if (dim
== TA_DIMENSION_HORZ
1947 && (n_edges
== 6 || n_edges
== 12))
1949 TA_Edge edge1
, edge2
, edge3
;
1950 FT_Pos dist1
, dist2
, span
, delta
;
1966 dist1
= edge2
->opos
- edge1
->opos
;
1967 dist2
= edge3
->opos
- edge2
->opos
;
1969 span
= dist1
- dist2
;
1975 delta
= edge3
->pos
- (2 * edge2
->pos
- edge1
->pos
);
1976 edge3
->pos
-= delta
;
1978 edge3
->link
->pos
-= delta
;
1980 /* move the serifs along with the stem */
1983 (edges
+ 8)->pos
-= delta
;
1984 (edges
+ 11)->pos
-= delta
;
1987 edge3
->flags
|= TA_EDGE_DONE
;
1989 edge3
->link
->flags
|= TA_EDGE_DONE
;
1993 if (has_serifs
|| !anchor
)
1995 /* now hint the remaining edges (serifs and single) */
1996 /* in order to complete our processing */
1997 for (edge
= edges
; edge
< edge_limit
; edge
++)
2002 if (edge
->flags
& TA_EDGE_DONE
)
2009 delta
= edge
->serif
->opos
- edge
->opos
;
2014 if (delta
< 64 + 16)
2016 ta_latin_align_serif_edge(hints
, edge
->serif
, edge
);
2017 TA_LOG(("SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
2018 " aligned to (%.2f)\n",
2019 edge
- edges
, edge
->opos
/ 64.0,
2020 edge
->serif
- edges
, edge
->serif
->opos
/ 64.0,
2025 TA_LOG(("SERIF_ANCHOR: edge %d (opos=%.2f) snapped to (%.2f)\n",
2026 edge
-edges
, edge
->opos
/ 64.0, edge
->pos
/ 64.0));
2027 edge
->pos
= TA_PIX_ROUND(edge
->opos
);
2032 TA_Edge before
, after
;
2035 for (before
= edge
- 1; before
>= edges
; before
--)
2036 if (before
->flags
& TA_EDGE_DONE
)
2039 for (after
= edge
+ 1; after
< edge_limit
; after
++)
2040 if (after
->flags
& TA_EDGE_DONE
)
2043 if (before
>= edges
&& before
< edge
2044 && after
< edge_limit
&& after
> edge
)
2046 if (after
->opos
== before
->opos
)
2047 edge
->pos
= before
->pos
;
2049 edge
->pos
= before
->pos
+ FT_MulDiv(edge
->opos
- before
->opos
,
2050 after
->pos
- before
->pos
,
2051 after
->opos
- before
->opos
);
2053 TA_LOG(("SERIF_LINK1: edge %d (opos=%.2f) snapped to (%.2f)"
2054 " from %d (opos=%.2f)\n",
2055 edge
- edges
, edge
->opos
/ 64.0,
2057 before
- edges
, before
->opos
/ 64.0));
2061 edge
->pos
= anchor
->pos
+ ((edge
->opos
- anchor
->opos
+ 16) & ~31);
2063 TA_LOG(("SERIF_LINK2: edge %d (opos=%.2f) snapped to (%.2f)\n",
2064 edge
- edges
, edge
->opos
/ 64.0, edge
->pos
/ 64.0));
2068 edge
->flags
|= TA_EDGE_DONE
;
2071 && edge
->pos
< edge
[-1].pos
)
2072 edge
->pos
= edge
[-1].pos
;
2074 if (edge
+ 1 < edge_limit
2075 && edge
[1].flags
& TA_EDGE_DONE
2076 && edge
->pos
> edge
[1].pos
)
2077 edge
->pos
= edge
[1].pos
;
2083 /* apply the complete hinting algorithm to a latin glyph */
2086 ta_latin_hints_apply(TA_GlyphHints hints
,
2087 FT_Outline
* outline
,
2088 TA_LatinMetrics metrics
)
2094 error
= ta_glyph_hints_reload(hints
, outline
);
2098 /* analyze glyph outline */
2099 #ifdef TA_CONFIG_OPTION_USE_WARPER
2100 if (metrics
->root
.scaler
.render_mode
== FT_RENDER_MODE_LIGHT
2101 || TA_HINTS_DO_HORIZONTAL(hints
))
2103 if (TA_HINTS_DO_HORIZONTAL(hints
))
2106 error
= ta_latin_hints_detect_features(hints
, TA_DIMENSION_HORZ
);
2111 if (TA_HINTS_DO_VERTICAL(hints
))
2113 error
= ta_latin_hints_detect_features(hints
, TA_DIMENSION_VERT
);
2117 ta_latin_hints_compute_blue_edges(hints
, metrics
);
2120 /* grid-fit the outline */
2121 for (dim
= 0; dim
< TA_DIMENSION_MAX
; dim
++)
2123 #ifdef TA_CONFIG_OPTION_USE_WARPER
2124 if (dim
== TA_DIMENSION_HORZ
2125 && metrics
->root
.scaler
.render_mode
== FT_RENDER_MODE_LIGHT
)
2127 TA_WarperRec warper
;
2132 ta_warper_compute(&warper
, hints
, (TA_Dimension
)dim
, &scale
, &delta
);
2133 ta_glyph_hints_scale_dim(hints
, (TA_Dimension
)dim
, scale
, delta
);
2139 if ((dim
== TA_DIMENSION_HORZ
&& TA_HINTS_DO_HORIZONTAL(hints
))
2140 || (dim
== TA_DIMENSION_VERT
&& TA_HINTS_DO_VERTICAL(hints
)))
2142 ta_latin_hint_edges(hints
, (TA_Dimension
)dim
);
2143 ta_glyph_hints_align_edge_points(hints
, (TA_Dimension
)dim
);
2144 ta_glyph_hints_align_strong_points(hints
, (TA_Dimension
)dim
);
2145 ta_glyph_hints_align_weak_points(hints
, (TA_Dimension
)dim
);
2148 ta_glyph_hints_save(hints
, outline
);
2155 /* XXX: this should probably fine tuned to differentiate better between */
2158 static const TA_Script_UniRangeRec ta_latin_uniranges
[] =
2160 TA_UNIRANGE_REC(0x0020UL
, 0x007FUL
), /* Basic Latin (no control chars) */
2161 TA_UNIRANGE_REC(0x00A0UL
, 0x00FFUL
), /* Latin-1 Supplement (no control chars) */
2162 TA_UNIRANGE_REC(0x0100UL
, 0x017FUL
), /* Latin Extended-A */
2163 TA_UNIRANGE_REC(0x0180UL
, 0x024FUL
), /* Latin Extended-B */
2164 TA_UNIRANGE_REC(0x0250UL
, 0x02AFUL
), /* IPA Extensions */
2165 TA_UNIRANGE_REC(0x02B0UL
, 0x02FFUL
), /* Spacing Modifier Letters */
2166 TA_UNIRANGE_REC(0x0300UL
, 0x036FUL
), /* Combining Diacritical Marks */
2167 TA_UNIRANGE_REC(0x0370UL
, 0x03FFUL
), /* Greek and Coptic */
2168 TA_UNIRANGE_REC(0x0400UL
, 0x04FFUL
), /* Cyrillic */
2169 TA_UNIRANGE_REC(0x0500UL
, 0x052FUL
), /* Cyrillic Supplement */
2170 TA_UNIRANGE_REC(0x1D00UL
, 0x1D7FUL
), /* Phonetic Extensions */
2171 TA_UNIRANGE_REC(0x1D80UL
, 0x1DBFUL
), /* Phonetic Extensions Supplement */
2172 TA_UNIRANGE_REC(0x1DC0UL
, 0x1DFFUL
), /* Combining Diacritical Marks Supplement */
2173 TA_UNIRANGE_REC(0x1E00UL
, 0x1EFFUL
), /* Latin Extended Additional */
2174 TA_UNIRANGE_REC(0x1F00UL
, 0x1FFFUL
), /* Greek Extended */
2175 TA_UNIRANGE_REC(0x2000UL
, 0x206FUL
), /* General Punctuation */
2176 TA_UNIRANGE_REC(0x2070UL
, 0x209FUL
), /* Superscripts and Subscripts */
2177 TA_UNIRANGE_REC(0x20A0UL
, 0x20CFUL
), /* Currency Symbols */
2178 TA_UNIRANGE_REC(0x2150UL
, 0x218FUL
), /* Number Forms */
2179 TA_UNIRANGE_REC(0x2460UL
, 0x24FFUL
), /* Enclosed Alphanumerics */
2180 TA_UNIRANGE_REC(0x2C60UL
, 0x2C7FUL
), /* Latin Extended-C */
2181 TA_UNIRANGE_REC(0x2DE0UL
, 0x2DFFUL
), /* Cyrillic Extended-A */
2182 TA_UNIRANGE_REC(0xA640UL
, 0xA69FUL
), /* Cyrillic Extended-B */
2183 TA_UNIRANGE_REC(0xA720UL
, 0xA7FFUL
), /* Latin Extended-D */
2184 TA_UNIRANGE_REC(0xFB00UL
, 0xFB06UL
), /* Alphab. Present. Forms (Latin Ligs) */
2185 TA_UNIRANGE_REC(0x1D400UL
, 0x1D7FFUL
), /* Mathematical Alphanumeric Symbols */
2186 TA_UNIRANGE_REC(0UL, 0UL)
2190 const TA_ScriptClassRec ta_latin_script_class
=
2195 sizeof (TA_LatinMetricsRec
),
2197 (TA_Script_InitMetricsFunc
)ta_latin_metrics_init
,
2198 (TA_Script_ScaleMetricsFunc
)ta_latin_metrics_scale
,
2199 (TA_Script_DoneMetricsFunc
)NULL
,
2201 (TA_Script_InitHintsFunc
)ta_latin_hints_init
,
2202 (TA_Script_ApplyHintsFunc
)ta_latin_hints_apply
2205 /* end of talatin.c */