4 * Copyright (C) 2011-2014 by Werner Lemberg.
6 * This file is part of the ttfautohint library, and may only be used,
7 * modified, and distributed under the terms given in `COPYING'. By
8 * continuing to use, modify, or distribute this file you indicate that you
9 * have read `COPYING' and understand and accept it fully.
11 * The file `COPYING' mentioned in the previous paragraph is distributed
12 * with the ttfautohint library.
16 /* originally file `aflatin.c' (2011-Mar-28) from FreeType */
18 /* heavily modified 2011 by Werner Lemberg <wl@gnu.org> */
23 #include FT_ADVANCES_H
24 #include FT_TRUETYPE_TABLES_H
31 #ifdef TA_CONFIG_OPTION_USE_WARPER
35 #include <numberset.h>
38 /* find segments and links, compute all stem widths, and initialize */
39 /* standard width and height for the glyph with given charcode */
42 ta_latin_metrics_init_widths(TA_LatinMetrics metrics
,
45 /* scan the array of segments in each direction */
46 TA_GlyphHintsRec hints
[1];
50 "latin standard widths computation (script `%s')\n"
51 "=================================================\n"
53 ta_script_names
[metrics
->root
.script_class
->script
]));
55 ta_glyph_hints_init(hints
);
57 metrics
->axis
[TA_DIMENSION_HORZ
].width_count
= 0;
58 metrics
->axis
[TA_DIMENSION_VERT
].width_count
= 0;
64 TA_LatinMetricsRec dummy
[1];
65 TA_Scaler scaler
= &dummy
->root
.scaler
;
68 glyph_index
= FT_Get_Char_Index(
70 metrics
->root
.script_class
->standard_char
);
74 TA_LOG_GLOBAL(("standard character: U+%04lX (glyph index %d)\n",
75 metrics
->root
.script_class
->standard_char
, glyph_index
));
77 error
= FT_Load_Glyph(face
, glyph_index
, FT_LOAD_NO_SCALE
);
78 if (error
|| face
->glyph
->outline
.n_points
<= 0)
81 memset(dummy
, 0, sizeof (TA_LatinMetricsRec
));
83 dummy
->units_per_em
= metrics
->units_per_em
;
85 scaler
->x_scale
= 0x10000L
;
86 scaler
->y_scale
= 0x10000L
;
91 scaler
->render_mode
= FT_RENDER_MODE_NORMAL
;
94 ta_glyph_hints_rescale(hints
, (TA_ScriptMetrics
)dummy
);
96 error
= ta_glyph_hints_reload(hints
, &face
->glyph
->outline
);
100 for (dim
= 0; dim
< TA_DIMENSION_MAX
; dim
++)
102 TA_LatinAxis axis
= &metrics
->axis
[dim
];
103 TA_AxisHints axhints
= &hints
->axis
[dim
];
105 TA_Segment seg
, limit
, link
;
106 FT_UInt num_widths
= 0;
109 error
= ta_latin_hints_compute_segments(hints
, (TA_Dimension
)dim
);
113 ta_latin_hints_link_segments(hints
, (TA_Dimension
)dim
);
115 seg
= axhints
->segments
;
116 limit
= seg
+ axhints
->num_segments
;
118 for (; seg
< limit
; seg
++)
122 /* we only consider stem segments there! */
130 dist
= seg
->pos
- link
->pos
;
134 if (num_widths
< TA_LATIN_MAX_WIDTHS
)
135 axis
->widths
[num_widths
++].org
= dist
;
139 /* this also replaces multiple almost identical stem widths */
140 /* with a single one (the value 100 is heuristic) */
141 ta_sort_and_quantize_widths(&num_widths
, axis
->widths
,
142 dummy
->units_per_em
/ 100);
143 axis
->width_count
= num_widths
;
147 for (dim
= 0; dim
< TA_DIMENSION_MAX
; dim
++)
149 TA_LatinAxis axis
= &metrics
->axis
[dim
];
153 stdw
= (axis
->width_count
> 0) ? axis
->widths
[0].org
154 : TA_LATIN_CONSTANT(metrics
, 50);
156 /* let's try 20% of the smallest width */
157 axis
->edge_distance_threshold
= stdw
/ 5;
158 axis
->standard_width
= stdw
;
159 axis
->extra_light
= 0;
166 TA_LOG_GLOBAL(("%s widths:\n",
167 dim
== TA_DIMENSION_VERT
? "horizontal"
170 TA_LOG_GLOBAL((" %d (standard)", axis
->standard_width
));
171 for (i
= 1; i
< axis
->width_count
; i
++)
172 TA_LOG_GLOBAL((" %d", axis
->widths
[i
].org
));
174 TA_LOG_GLOBAL(("\n"));
180 TA_LOG_GLOBAL(("\n"));
182 ta_glyph_hints_done(hints
);
186 /* find all blue zones; flat segments give the reference points, */
187 /* round segments the overshoot positions */
190 ta_latin_metrics_init_blues(TA_LatinMetrics metrics
,
193 FT_Pos flats
[TA_BLUE_STRING_MAX_LEN
];
194 FT_Pos rounds
[TA_BLUE_STRING_MAX_LEN
];
200 TA_LatinAxis axis
= &metrics
->axis
[TA_DIMENSION_VERT
];
203 TA_Blue_Stringset bss
= metrics
->root
.script_class
->blue_stringset
;
204 const TA_Blue_StringRec
* bs
= &ta_blue_stringsets
[bss
];
207 /* we walk over the blue character strings as specified in the */
208 /* script's entry in the `af_blue_stringset' array */
210 TA_LOG_GLOBAL(("latin blue zones computation\n"
211 "============================\n"
214 for (; bs
->string
!= TA_BLUE_STRING_MAX
; bs
++)
216 const char* p
= &ta_blue_strings
[bs
->string
];
223 FT_Bool have_flag
= 0;
226 TA_LOG_GLOBAL(("blue zone %d", axis
->blue_count
));
230 TA_LOG_GLOBAL((" ("));
232 if (TA_LATIN_IS_TOP_BLUE(bs
))
234 TA_LOG_GLOBAL(("top"));
238 if (TA_LATIN_IS_X_HEIGHT_BLUE(bs
))
241 TA_LOG_GLOBAL((", "));
242 TA_LOG_GLOBAL(("small top"));
246 if (TA_LATIN_IS_LONG_BLUE(bs
))
249 TA_LOG_GLOBAL((", "));
250 TA_LOG_GLOBAL(("long"));
253 TA_LOG_GLOBAL((")"));
256 TA_LOG_GLOBAL((":\n"));
258 #endif /* TA_DEBUG */
267 FT_Pos best_y
; /* same as points.y */
268 FT_Int best_point
, best_contour_first
, best_contour_last
;
273 GET_UTF8_CHAR(ch
, p
);
275 /* load the character in the face -- skip unknown or empty ones */
276 glyph_index
= FT_Get_Char_Index(face
, ch
);
277 if (glyph_index
== 0)
279 TA_LOG_GLOBAL((" U+%04lX unavailable\n", ch
));
283 error
= FT_Load_Glyph(face
, glyph_index
, FT_LOAD_NO_SCALE
);
284 outline
= face
->glyph
->outline
;
285 if (error
|| outline
.n_points
<= 0)
287 TA_LOG_GLOBAL((" U+%04lX contains no outlines\n", ch
));
291 /* now compute min or max point indices and coordinates */
292 points
= outline
.points
;
294 best_y
= 0; /* make compiler happy */
295 best_contour_first
= 0; /* ditto */
296 best_contour_last
= 0; /* ditto */
304 for (nn
= 0; nn
< outline
.n_contours
; first
= last
+ 1, nn
++)
306 FT_Int old_best_point
= best_point
;
310 last
= outline
.contours
[nn
];
312 /* avoid single-point contours since they are never rasterized; */
313 /* in some fonts, they correspond to mark attachment points */
314 /* that are way outside of the glyph's real outline */
318 if (TA_LATIN_IS_TOP_BLUE(bs
))
320 for (pp
= first
; pp
<= last
; pp
++)
322 || points
[pp
].y
> best_y
)
325 best_y
= points
[pp
].y
;
330 for (pp
= first
; pp
<= last
; pp
++)
332 || points
[pp
].y
< best_y
)
335 best_y
= points
[pp
].y
;
339 if (best_point
!= old_best_point
)
341 best_contour_first
= first
;
342 best_contour_last
= last
;
347 /* now check whether the point belongs to a straight or round */
348 /* segment; we first need to find in which contour the extremum */
349 /* lies, then inspect its previous and next points */
352 FT_Pos best_x
= points
[best_point
].x
;
354 FT_Int best_segment_first
, best_segment_last
;
355 FT_Int best_on_point_first
, best_on_point_last
;
359 best_segment_first
= best_point
;
360 best_segment_last
= best_point
;
362 if (FT_CURVE_TAG(outline
.tags
[best_point
]) == FT_CURVE_TAG_ON
)
364 best_on_point_first
= best_point
;
365 best_on_point_last
= best_point
;
369 best_on_point_first
= -1;
370 best_on_point_last
= -1;
373 /* look for the previous and next points on the contour */
374 /* that are not on the same Y coordinate, then threshold */
375 /* the `closeness'... */
381 if (prev
> best_contour_first
)
384 prev
= best_contour_last
;
386 dist
= TA_ABS(points
[prev
].y
- best_y
);
387 /* accept a small distance or a small angle (both values are */
388 /* heuristic; value 20 corresponds to approx. 2.9 degrees) */
390 if (TA_ABS(points
[prev
].x
- best_x
) <= 20 * dist
)
393 best_segment_first
= prev
;
395 if (FT_CURVE_TAG(outline
.tags
[prev
]) == FT_CURVE_TAG_ON
)
397 best_on_point_first
= prev
;
398 if (best_on_point_last
< 0)
399 best_on_point_last
= prev
;
402 } while (prev
!= best_point
);
406 if (next
< best_contour_last
)
409 next
= best_contour_first
;
411 dist
= TA_ABS(points
[next
].y
- best_y
);
413 if (TA_ABS(points
[next
].x
- best_x
) <= 20 * dist
)
416 best_segment_last
= next
;
418 if (FT_CURVE_TAG(outline
.tags
[next
]) == FT_CURVE_TAG_ON
)
420 best_on_point_last
= next
;
421 if (best_on_point_first
< 0)
422 best_on_point_first
= next
;
425 } while (next
!= best_point
);
427 if (TA_LATIN_IS_LONG_BLUE(bs
))
429 /* If this flag is set, we have an additional constraint to */
430 /* get the blue zone distance: Find a segment of the topmost */
431 /* (or bottommost) contour that is longer than a heuristic */
432 /* threshold. This ensures that small bumps in the outline */
433 /* are ignored (for example, the `vertical serifs' found in */
434 /* many Hebrew glyph designs). */
436 /* If this segment is long enough, we are done. Otherwise, */
437 /* search the segment next to the extremum that is long */
438 /* enough, has the same direction, and a not too large */
439 /* vertical distance from the extremum. Note that the */
440 /* algorithm doesn't check whether the found segment is */
441 /* actually the one (vertically) nearest to the extremum. */
443 /* heuristic threshold value */
444 FT_Pos length_threshold
= metrics
->units_per_em
/ 25;
447 dist
= TA_ABS(points
[best_segment_last
].x
-
448 points
[best_segment_first
].x
);
450 if (dist
< length_threshold
451 && best_segment_last
- best_segment_first
+ 2 <=
452 best_contour_last
- best_contour_first
)
454 /* heuristic threshold value */
455 FT_Pos height_threshold
= metrics
->units_per_em
/ 4;
464 /* compute direction */
469 if (prev
> best_contour_first
)
472 prev
= best_contour_last
;
474 if (points
[prev
].x
!= best_x
)
476 } while (prev
!= best_point
);
478 /* skip glyph for the degenerate case */
479 if (prev
== best_point
)
482 left2right
= FT_BOOL(points
[prev
].x
< points
[best_point
].x
);
484 first
= best_segment_last
;
492 FT_Int p_first
, p_last
;
497 /* no hit; adjust first point */
500 /* also adjust first and last on point */
501 if (FT_CURVE_TAG(outline
.tags
[first
]) == FT_CURVE_TAG_ON
)
515 if (last
< best_contour_last
)
518 last
= best_contour_first
;
520 if (TA_ABS(best_y
- points
[first
].y
) > height_threshold
)
522 /* vertical distance too large */
527 /* same test as above */
528 dist
= TA_ABS(points
[last
].y
- points
[first
].y
);
530 if (TA_ABS(points
[last
].x
- points
[first
].x
) <= 20 * dist
)
536 if (FT_CURVE_TAG(outline
.tags
[last
]) == FT_CURVE_TAG_ON
)
543 l2r
= FT_BOOL(points
[first
].x
< points
[last
].x
);
544 d
= TA_ABS(points
[last
].x
- points
[first
].x
);
546 if (l2r
== left2right
547 && d
>= length_threshold
)
549 /* all constraints are met; update segment after finding */
553 if (last
< best_contour_last
)
556 last
= best_contour_first
;
558 d
= TA_ABS(points
[last
].y
- points
[first
].y
);
560 if (TA_ABS(points
[next
].x
- points
[first
].x
) <=
569 if (FT_CURVE_TAG(outline
.tags
[last
]) == FT_CURVE_TAG_ON
)
575 } while (last
!= best_segment_first
);
577 best_y
= points
[first
].y
;
579 best_segment_first
= first
;
580 best_segment_last
= last
;
582 best_on_point_first
= p_first
;
583 best_on_point_last
= p_last
;
587 } while (last
!= best_segment_first
);
591 TA_LOG_GLOBAL((" U+%04lX: best_y = %5ld", ch
, best_y
));
594 * now set the `round' flag depending on the segment's kind:
596 * - if the horizontal distance between the first and last
597 * `on' point is larger than upem/8 (value 8 is heuristic)
598 * we have a flat segment
599 * - if either the first or the last point of the segment is
600 * an `off' point, the segment is round, otherwise it is
603 if (best_on_point_first
>= 0
604 && best_on_point_last
>= 0
605 && (FT_UInt
)(TA_ABS(points
[best_on_point_last
].x
606 - points
[best_on_point_first
].x
))
607 > metrics
->units_per_em
/ 8)
610 round
= FT_BOOL(FT_CURVE_TAG(outline
.tags
[best_segment_first
])
612 || FT_CURVE_TAG(outline
.tags
[best_segment_last
])
615 TA_LOG_GLOBAL((" (%s)\n", round
? "round" : "flat"));
619 rounds
[num_rounds
++] = best_y
;
621 flats
[num_flats
++] = best_y
;
624 if (num_flats
== 0 && num_rounds
== 0)
626 /* we couldn't find a single glyph to compute this blue zone, */
627 /* we will simply ignore it then */
628 TA_LOG_GLOBAL((" empty\n"));
632 /* we have computed the contents of the `rounds' and `flats' tables, */
633 /* now determine the reference and overshoot position of the blue -- */
634 /* we simply take the median value after a simple sort */
635 ta_sort_pos(num_rounds
, rounds
);
636 ta_sort_pos(num_flats
, flats
);
638 blue
= &axis
->blues
[axis
->blue_count
];
639 blue_ref
= &blue
->ref
.org
;
640 blue_shoot
= &blue
->shoot
.org
;
647 *blue_shoot
= rounds
[num_rounds
/ 2];
649 else if (num_rounds
== 0)
652 *blue_shoot
= flats
[num_flats
/ 2];
656 *blue_ref
= flats
[num_flats
/ 2];
657 *blue_shoot
= rounds
[num_rounds
/ 2];
660 /* there are sometimes problems if the overshoot position of top */
661 /* zones is under its reference position, or the opposite for bottom */
662 /* zones; we must thus check everything there and correct the errors */
663 if (*blue_shoot
!= *blue_ref
)
665 FT_Pos ref
= *blue_ref
;
666 FT_Pos shoot
= *blue_shoot
;
667 FT_Bool over_ref
= FT_BOOL(shoot
> ref
);
670 if (TA_LATIN_IS_TOP_BLUE(bs
) ^ over_ref
)
673 *blue_shoot
= (shoot
+ ref
) / 2;
675 TA_LOG_GLOBAL((" [overshoot smaller than reference,"
676 " taking mean value]\n"));
681 if (TA_LATIN_IS_TOP_BLUE(bs
))
682 blue
->flags
|= TA_LATIN_BLUE_TOP
;
684 /* the following flag is used later to adjust the y and x scales */
685 /* in order to optimize the pixel grid alignment */
686 /* of the top of small letters */
687 if (TA_LATIN_IS_X_HEIGHT_BLUE(bs
))
688 blue
->flags
|= TA_LATIN_BLUE_ADJUSTMENT
;
690 TA_LOG_GLOBAL((" -> reference = %ld\n"
691 " overshoot = %ld\n",
692 *blue_ref
, *blue_shoot
));
695 /* add two blue zones for usWinAscent and usWinDescent */
696 /* just in case the above algorithm has missed them -- */
697 /* Windows cuts off everything outside of those two values */
702 os2
= (TT_OS2
*)FT_Get_Sfnt_Table(face
, ft_sfnt_os2
);
706 blue
= &axis
->blues
[axis
->blue_count
];
707 blue
->flags
= TA_LATIN_BLUE_TOP
| TA_LATIN_BLUE_ACTIVE
;
709 blue
->shoot
.org
= os2
->usWinAscent
;
711 TA_LOG_GLOBAL(("artificial blue zone for usWinAscent:\n"
712 " -> reference = %ld\n"
713 " overshoot = %ld\n",
714 blue
->ref
.org
, blue
->shoot
.org
));
716 blue
= &axis
->blues
[axis
->blue_count
+ 1];
717 blue
->flags
= TA_LATIN_BLUE_ACTIVE
;
719 blue
->shoot
.org
= -os2
->usWinDescent
;
721 TA_LOG_GLOBAL(("artificial blue zone for usWinDescent:\n"
722 " -> reference = %ld\n"
723 " overshoot = %ld\n",
724 blue
->ref
.org
, blue
->shoot
.org
));
728 blue
= &axis
->blues
[axis
->blue_count
];
733 blue
= &axis
->blues
[axis
->blue_count
+ 1];
740 TA_LOG_GLOBAL(("\n"));
746 /* check whether all ASCII digits have the same advance width */
749 ta_latin_metrics_check_digits(TA_LatinMetrics metrics
,
753 FT_Bool started
= 0, same_width
= 1;
754 FT_Fixed advance
, old_advance
= 0;
757 /* digit `0' is 0x30 in all supported charmaps */
758 for (i
= 0x30; i
<= 0x39; i
++)
763 glyph_index
= FT_Get_Char_Index(face
, i
);
764 if (glyph_index
== 0)
767 if (FT_Get_Advance(face
, glyph_index
,
770 | FT_LOAD_IGNORE_TRANSFORM
,
776 if (advance
!= old_advance
)
784 old_advance
= advance
;
789 metrics
->root
.digits_have_same_width
= same_width
;
793 /* initialize global metrics */
796 ta_latin_metrics_init(TA_LatinMetrics metrics
,
799 FT_CharMap oldmap
= face
->charmap
;
802 metrics
->units_per_em
= face
->units_per_EM
;
804 if (!FT_Select_Charmap(face
, FT_ENCODING_UNICODE
))
806 ta_latin_metrics_init_widths(metrics
, face
);
807 ta_latin_metrics_init_blues(metrics
, face
);
808 ta_latin_metrics_check_digits(metrics
, face
);
811 FT_Set_Charmap(face
, oldmap
);
816 /* adjust scaling value, then scale and shift widths */
817 /* and blue zones (if applicable) for given dimension */
820 ta_latin_metrics_scale_dim(TA_LatinMetrics metrics
,
831 ppem
= metrics
->root
.scaler
.face
->size
->metrics
.x_ppem
;
833 if (dim
== TA_DIMENSION_HORZ
)
835 scale
= scaler
->x_scale
;
836 delta
= scaler
->x_delta
;
840 scale
= scaler
->y_scale
;
841 delta
= scaler
->y_delta
;
844 axis
= &metrics
->axis
[dim
];
846 if (axis
->org_scale
== scale
&& axis
->org_delta
== delta
)
849 axis
->org_scale
= scale
;
850 axis
->org_delta
= delta
;
852 /* correct Y scale to optimize the alignment of the top of */
853 /* small letters to the pixel grid */
854 /* (if we do x-height snapping for this ppem value) */
855 if (!number_set_is_element(
856 metrics
->root
.globals
->font
->x_height_snapping_exceptions
,
859 TA_LatinAxis Axis
= &metrics
->axis
[TA_DIMENSION_VERT
];
860 TA_LatinBlue blue
= NULL
;
863 for (nn
= 0; nn
< Axis
->blue_count
; nn
++)
865 if (Axis
->blues
[nn
].flags
& TA_LATIN_BLUE_ADJUSTMENT
)
867 blue
= &Axis
->blues
[nn
];
880 scaled
= FT_MulFix(blue
->shoot
.org
, scaler
->y_scale
);
881 limit
= metrics
->root
.globals
->increase_x_height
;
884 /* if the `increase-x-height' property is active, */
885 /* we round up much more often */
888 && ppem
>= TA_PROP_INCREASE_X_HEIGHT_MIN
)
891 fitted
= (scaled
+ threshold
) & ~63;
893 if (scaled
!= fitted
)
895 if (dim
== TA_DIMENSION_VERT
)
897 scale
= FT_MulDiv(scale
, fitted
, scaled
);
900 "ta_latin_metrics_scale_dim:"
901 " x height alignment (script `%s'):\n"
903 " vertical scaling changed from %.4f to %.4f (by %d%%)\n"
905 ta_script_names
[metrics
->root
.script_class
->script
],
906 axis
->org_scale
/ 65536.0,
908 (fitted
- scaled
) * 100 / scaled
));
917 if (dim
== TA_DIMENSION_HORZ
)
919 metrics
->root
.scaler
.x_scale
= scale
;
920 metrics
->root
.scaler
.x_delta
= delta
;
924 metrics
->root
.scaler
.y_scale
= scale
;
925 metrics
->root
.scaler
.y_delta
= delta
;
928 TA_LOG_GLOBAL(("%s widths (script `%s')\n",
929 dim
== TA_DIMENSION_HORZ
? "horizontal" : "vertical",
930 ta_script_names
[metrics
->root
.script_class
->script
]));
932 /* scale the widths */
933 for (nn
= 0; nn
< axis
->width_count
; nn
++)
935 TA_Width width
= axis
->widths
+ nn
;
938 width
->cur
= FT_MulFix(width
->org
, scale
);
939 width
->fit
= width
->cur
;
941 TA_LOG_GLOBAL((" %d scaled to %.2f\n",
946 TA_LOG_GLOBAL(("\n"));
948 /* an extra-light axis corresponds to a standard width that is */
949 /* smaller than 5/8 pixels */
951 (FT_Bool
)(FT_MulFix(axis
->standard_width
, scale
) < 32 + 8);
954 if (axis
->extra_light
)
955 TA_LOG_GLOBAL(("`%s' script is extra light (at current resolution)\n"
957 ta_script_names
[metrics
->root
.script_class
->script
]));
960 if (dim
== TA_DIMENSION_VERT
)
962 TA_LOG_GLOBAL(("blue zones (script `%s')\n",
963 ta_script_names
[metrics
->root
.script_class
->script
]));
965 /* scale the blue zones */
966 for (nn
= 0; nn
< axis
->blue_count
; nn
++)
968 TA_LatinBlue blue
= &axis
->blues
[nn
];
972 blue
->ref
.cur
= FT_MulFix(blue
->ref
.org
, scale
) + delta
;
973 blue
->ref
.fit
= blue
->ref
.cur
;
974 blue
->shoot
.cur
= FT_MulFix(blue
->shoot
.org
, scale
) + delta
;
975 blue
->shoot
.fit
= blue
->shoot
.cur
;
976 blue
->flags
&= ~TA_LATIN_BLUE_ACTIVE
;
978 /* a blue zone is only active if it is less than 3/4 pixels tall */
979 dist
= FT_MulFix(blue
->ref
.org
- blue
->shoot
.org
, scale
);
980 if (dist
<= 48 && dist
>= -48)
988 /* use discrete values for blue zone widths */
991 /* generic, original code */
992 delta1
= blue
->shoot
.org
- blue
->ref
.org
;
997 delta2
= FT_MulFix(delta2
, scale
);
1001 else if (delta2
< 64)
1002 delta2
= 32 + (((delta2
- 32) + 16) & ~31);
1004 delta2
= TA_PIX_ROUND(delta2
);
1009 blue
->ref
.fit
= TA_PIX_ROUND(blue
->ref
.cur
);
1010 blue
->shoot
.fit
= blue
->ref
.fit
+ delta2
;
1012 /* simplified version due to abs(dist) <= 48 */
1019 else if (delta2
< 48)
1027 blue
->ref
.fit
= TA_PIX_ROUND(blue
->ref
.cur
);
1028 blue
->shoot
.fit
= blue
->ref
.fit
- delta2
;
1031 blue
->flags
|= TA_LATIN_BLUE_ACTIVE
;
1033 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f%s\n"
1034 " overshoot %d: %d scaled to %.2f%s\n",
1037 blue
->ref
.fit
/ 64.0,
1038 blue
->flags
& TA_LATIN_BLUE_ACTIVE
? ""
1042 blue
->shoot
.fit
/ 64.0,
1043 blue
->flags
& TA_LATIN_BLUE_ACTIVE
? ""
1048 /* the last two artificial blue zones are to be scaled */
1049 /* with uncorrected scaling values */
1051 TA_LatinAxis a
= &metrics
->axis
[TA_DIMENSION_VERT
];
1055 b
= &a
->blues
[a
->blue_count
];
1059 b
->shoot
.fit
= FT_MulFix(b
->ref
.org
, a
->org_scale
) + delta
;
1061 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f (artificial)\n"
1062 " overshoot %d: %d scaled to %.2f (artificial)\n",
1068 b
->shoot
.fit
/ 64.0));
1070 b
= &a
->blues
[a
->blue_count
+ 1];
1074 b
->shoot
.fit
= FT_MulFix(b
->ref
.org
, a
->org_scale
) + delta
;
1076 TA_LOG_GLOBAL((" reference %d: %d scaled to %.2f (artificial)\n"
1077 " overshoot %d: %d scaled to %.2f (artificial)\n",
1083 b
->shoot
.fit
/ 64.0));
1086 TA_LOG_GLOBAL(("\n"));
1091 /* scale global values in both directions */
1094 ta_latin_metrics_scale(TA_LatinMetrics metrics
,
1097 metrics
->root
.scaler
.render_mode
= scaler
->render_mode
;
1098 metrics
->root
.scaler
.face
= scaler
->face
;
1099 metrics
->root
.scaler
.flags
= scaler
->flags
;
1101 ta_latin_metrics_scale_dim(metrics
, scaler
, TA_DIMENSION_HORZ
);
1102 ta_latin_metrics_scale_dim(metrics
, scaler
, TA_DIMENSION_VERT
);
1106 /* walk over all contours and compute its segments */
1109 ta_latin_hints_compute_segments(TA_GlyphHints hints
,
1112 TA_AxisHints axis
= &hints
->axis
[dim
];
1113 FT_Error error
= FT_Err_Ok
;
1115 TA_Segment segment
= NULL
;
1118 TA_Point
* contour
= hints
->contours
;
1119 TA_Point
* contour_limit
= contour
+ hints
->num_contours
;
1120 TA_Direction major_dir
, segment_dir
;
1123 memset(&seg0
, 0, sizeof (TA_SegmentRec
));
1125 seg0
.flags
= TA_EDGE_NORMAL
;
1127 major_dir
= (TA_Direction
)TA_ABS(axis
->major_dir
);
1128 segment_dir
= major_dir
;
1130 axis
->num_segments
= 0;
1132 /* set up (u,v) in each point */
1133 if (dim
== TA_DIMENSION_HORZ
)
1135 TA_Point point
= hints
->points
;
1136 TA_Point limit
= point
+ hints
->num_points
;
1139 for (; point
< limit
; point
++)
1141 point
->u
= point
->fx
;
1142 point
->v
= point
->fy
;
1147 TA_Point point
= hints
->points
;
1148 TA_Point limit
= point
+ hints
->num_points
;
1151 for (; point
< limit
; point
++)
1153 point
->u
= point
->fy
;
1154 point
->v
= point
->fx
;
1158 /* do each contour separately */
1159 for (; contour
< contour_limit
; contour
++)
1161 TA_Point point
= contour
[0];
1162 TA_Point last
= point
->prev
;
1166 FT_Pos min_pos
= 32000; /* minimum segment pos != min_coord */
1167 FT_Pos max_pos
= -32000; /* maximum segment pos != max_coord */
1171 if (point
== last
) /* skip singletons -- just in case */
1174 if (TA_ABS(last
->out_dir
) == major_dir
1175 && TA_ABS(point
->out_dir
) == major_dir
)
1177 /* we are already on an edge, try to locate its start */
1182 point
= point
->prev
;
1183 if (TA_ABS(point
->out_dir
) != major_dir
)
1185 point
= point
->next
;
1209 if (point
->out_dir
!= segment_dir
1212 /* we are just leaving an edge; record a new segment! */
1213 segment
->last
= point
;
1214 segment
->pos
= (FT_Short
)((min_pos
+ max_pos
) >> 1);
1216 /* a segment is round if either its first or last point */
1217 /* is a control point */
1218 if ((segment
->first
->flags
| point
->flags
) & TA_FLAG_CONTROL
)
1219 segment
->flags
|= TA_EDGE_ROUND
;
1221 /* compute segment size */
1222 min_pos
= max_pos
= point
->v
;
1224 v
= segment
->first
->v
;
1230 segment
->min_coord
= (FT_Short
)min_pos
;
1231 segment
->max_coord
= (FT_Short
)max_pos
;
1232 segment
->height
= (FT_Short
)(segment
->max_coord
-
1233 segment
->min_coord
);
1241 /* now exit if we are at the start/end point */
1250 && TA_ABS(point
->out_dir
) == major_dir
)
1252 /* this is the start of a new segment! */
1253 segment_dir
= (TA_Direction
)point
->out_dir
;
1255 /* clear all segment fields */
1256 error
= ta_axis_hints_new_segment(axis
, &segment
);
1261 segment
->dir
= (FT_Char
)segment_dir
;
1262 min_pos
= max_pos
= point
->u
;
1263 segment
->first
= point
;
1264 segment
->last
= point
;
1268 point
= point
->next
;
1273 /* now slightly increase the height of segments if this makes sense -- */
1274 /* this is used to better detect and ignore serifs */
1276 TA_Segment segments
= axis
->segments
;
1277 TA_Segment segments_end
= segments
+ axis
->num_segments
;
1280 for (segment
= segments
; segment
< segments_end
; segment
++)
1282 TA_Point first
= segment
->first
;
1283 TA_Point last
= segment
->last
;
1285 FT_Pos first_v
= first
->v
;
1286 FT_Pos last_v
= last
->v
;
1292 if (first_v
< last_v
)
1299 segment
->height
= (FT_Short
)(segment
->height
+
1300 ((first_v
- p
->v
) >> 1));
1304 segment
->height
= (FT_Short
)(segment
->height
+
1305 ((p
->v
- last_v
) >> 1));
1314 segment
->height
= (FT_Short
)(segment
->height
+
1315 ((p
->v
- first_v
) >> 1));
1319 segment
->height
= (FT_Short
)(segment
->height
+
1320 ((last_v
- p
->v
) >> 1));
1330 /* link segments to form stems and serifs */
1333 ta_latin_hints_link_segments(TA_GlyphHints hints
,
1336 TA_AxisHints axis
= &hints
->axis
[dim
];
1338 TA_Segment segments
= axis
->segments
;
1339 TA_Segment segment_limit
= segments
+ axis
->num_segments
;
1341 FT_Pos len_threshold
, len_score
;
1342 TA_Segment seg1
, seg2
;
1345 len_threshold
= TA_LATIN_CONSTANT(hints
->metrics
, 8);
1346 if (len_threshold
== 0)
1349 len_score
= TA_LATIN_CONSTANT(hints
->metrics
, 6000);
1351 /* now compare each segment to the others */
1352 for (seg1
= segments
; seg1
< segment_limit
; seg1
++)
1354 /* the fake segments are introduced to hint the metrics -- */
1355 /* we must never link them to anything */
1356 if (seg1
->dir
!= axis
->major_dir
1357 || seg1
->first
== seg1
->last
)
1360 /* search for stems having opposite directions, */
1361 /* with seg1 to the `left' of seg2 */
1362 for (seg2
= segments
; seg2
< segment_limit
; seg2
++)
1364 FT_Pos pos1
= seg1
->pos
;
1365 FT_Pos pos2
= seg2
->pos
;
1368 if (seg1
->dir
+ seg2
->dir
== 0
1371 /* compute distance between the two segments */
1372 FT_Pos dist
= pos2
- pos1
;
1373 FT_Pos min
= seg1
->min_coord
;
1374 FT_Pos max
= seg1
->max_coord
;
1378 if (min
< seg2
->min_coord
)
1379 min
= seg2
->min_coord
;
1380 if (max
> seg2
->max_coord
)
1381 max
= seg2
->max_coord
;
1383 /* compute maximum coordinate difference of the two segments */
1385 if (len
>= len_threshold
)
1387 /* small coordinate differences cause a higher score, and */
1388 /* segments with a greater distance cause a higher score also */
1389 score
= dist
+ len_score
/ len
;
1391 /* and we search for the smallest score */
1392 /* of the sum of the two values */
1393 if (score
< seg1
->score
)
1395 seg1
->score
= score
;
1399 if (score
< seg2
->score
)
1401 seg2
->score
= score
;
1409 /* now compute the `serif' segments, cf. explanations in `tahints.h' */
1410 for (seg1
= segments
; seg1
< segment_limit
; seg1
++)
1416 if (seg2
->link
!= seg1
)
1419 seg1
->serif
= seg2
->link
;
1426 /* link segments to edges, using feature analysis for selection */
1429 ta_latin_hints_compute_edges(TA_GlyphHints hints
,
1432 TA_AxisHints axis
= &hints
->axis
[dim
];
1433 FT_Error error
= FT_Err_Ok
;
1434 TA_LatinAxis laxis
= &((TA_LatinMetrics
)hints
->metrics
)->axis
[dim
];
1436 TA_Segment segments
= axis
->segments
;
1437 TA_Segment segment_limit
= segments
+ axis
->num_segments
;
1441 TA_Direction up_dir
;
1444 FT_Pos edge_distance_threshold
;
1445 FT_Pos segment_length_threshold
;
1448 axis
->num_edges
= 0;
1450 scale
= (dim
== TA_DIMENSION_HORZ
) ? hints
->x_scale
1454 up_dir
= (dim
== TA_DIMENSION_HORZ
) ? TA_DIR_UP
1458 /* we ignore all segments that are less than 1 pixel in length */
1459 /* to avoid many problems with serif fonts */
1460 /* (the corresponding threshold is computed in font units) */
1461 if (dim
== TA_DIMENSION_HORZ
)
1462 segment_length_threshold
= FT_DivFix(64, hints
->y_scale
);
1464 segment_length_threshold
= 0;
1466 /********************************************************************/
1468 /* We begin by generating a sorted table of edges for the current */
1469 /* direction. To do so, we simply scan each segment and try to find */
1470 /* an edge in our table that corresponds to its position. */
1472 /* If no edge is found, we create and insert a new edge in the */
1473 /* sorted table. Otherwise, we simply add the segment to the edge's */
1474 /* list which gets processed in the second step to compute the */
1475 /* edge's properties. */
1477 /* Note that the table of edges is sorted along the segment/edge */
1480 /********************************************************************/
1482 /* assure that edge distance threshold is at most 0.25px */
1483 edge_distance_threshold
= FT_MulFix(laxis
->edge_distance_threshold
,
1485 if (edge_distance_threshold
> 64 / 4)
1486 edge_distance_threshold
= 64 / 4;
1488 edge_distance_threshold
= FT_DivFix(edge_distance_threshold
,
1491 for (seg
= segments
; seg
< segment_limit
; seg
++)
1493 TA_Edge found
= NULL
;
1497 if (seg
->height
< segment_length_threshold
)
1500 /* a special case for serif edges: */
1501 /* if they are smaller than 1.5 pixels we ignore them */
1503 && 2 * seg
->height
< 3 * segment_length_threshold
)
1506 /* look for an edge corresponding to the segment */
1507 for (ee
= 0; ee
< axis
->num_edges
; ee
++)
1509 TA_Edge edge
= axis
->edges
+ ee
;
1513 dist
= seg
->pos
- edge
->fpos
;
1517 if (dist
< edge_distance_threshold
&& edge
->dir
== seg
->dir
)
1529 /* insert a new edge in the list and sort according to the position */
1530 error
= ta_axis_hints_new_edge(axis
, seg
->pos
,
1531 (TA_Direction
)seg
->dir
,
1536 /* add the segment to the new edge's list */
1537 memset(edge
, 0, sizeof (TA_EdgeRec
));
1540 edge
->dir
= seg
->dir
;
1541 edge
->fpos
= seg
->pos
;
1542 edge
->opos
= FT_MulFix(seg
->pos
, scale
);
1543 edge
->pos
= edge
->opos
;
1544 seg
->edge_next
= seg
;
1548 /* if an edge was found, simply add the segment to the edge's list */
1549 seg
->edge_next
= found
->first
;
1550 found
->last
->edge_next
= seg
;
1555 /*****************************************************************/
1557 /* Good, we now compute each edge's properties according to */
1558 /* the segments found on its position. Basically, these are */
1560 /* - the edge's main direction */
1561 /* - stem edge, serif edge or both (which defaults to stem then) */
1562 /* - rounded edge, straight or both (which defaults to straight) */
1563 /* - link for edge */
1565 /*****************************************************************/
1567 /* first of all, set the `edge' field in each segment -- this is */
1568 /* required in order to compute edge links */
1570 /* note that removing this loop and setting the `edge' field of each */
1571 /* segment directly in the code above slows down execution speed for */
1572 /* some reasons on platforms like the Sun */
1574 TA_Edge edges
= axis
->edges
;
1575 TA_Edge edge_limit
= edges
+ axis
->num_edges
;
1579 for (edge
= edges
; edge
< edge_limit
; edge
++)
1586 seg
= seg
->edge_next
;
1587 } while (seg
!= edge
->first
);
1590 /* now compute each edge properties */
1591 for (edge
= edges
; edge
< edge_limit
; edge
++)
1593 FT_Int is_round
= 0; /* does it contain round segments? */
1594 FT_Int is_straight
= 0; /* does it contain straight segments? */
1596 FT_Pos ups
= 0; /* number of upwards segments */
1597 FT_Pos downs
= 0; /* number of downwards segments */
1608 /* check for roundness of segment */
1609 if (seg
->flags
& TA_EDGE_ROUND
)
1615 /* check for segment direction */
1616 if (seg
->dir
== up_dir
)
1617 ups
+= seg
->max_coord
- seg
->min_coord
;
1619 downs
+= seg
->max_coord
- seg
->min_coord
;
1622 /* check for links -- */
1623 /* if seg->serif is set, then seg->link must be ignored */
1624 is_serif
= (FT_Bool
)(seg
->serif
1626 && seg
->serif
->edge
!= edge
);
1628 if ((seg
->link
&& seg
->link
->edge
!= NULL
)
1641 edge2
= edge
->serif
;
1650 edge_delta
= edge
->fpos
- edge2
->fpos
;
1652 edge_delta
= -edge_delta
;
1654 seg_delta
= seg
->pos
- seg2
->pos
;
1656 seg_delta
= -seg_delta
;
1658 if (seg_delta
< edge_delta
)
1666 edge
->serif
= edge2
;
1667 edge2
->flags
|= TA_EDGE_SERIF
;
1673 seg
= seg
->edge_next
;
1674 } while (seg
!= edge
->first
);
1676 /* set the round/straight flags */
1677 edge
->flags
= TA_EDGE_NORMAL
;
1680 && is_round
>= is_straight
)
1681 edge
->flags
|= TA_EDGE_ROUND
;
1684 /* set the edge's main direction */
1685 edge
->dir
= TA_DIR_NONE
;
1688 edge
->dir
= (FT_Char
)up_dir
;
1690 else if (ups
< downs
)
1691 edge
->dir
= (FT_Char
)-up_dir
;
1693 else if (ups
== downs
)
1694 edge
->dir
= 0; /* both up and down! */
1697 /* get rid of serifs if link is set */
1698 /* XXX: this gets rid of many unpleasant artefacts! */
1699 /* example: the `c' in cour.pfa at size 13 */
1701 if (edge
->serif
&& edge
->link
)
1711 /* detect segments and edges for given dimension */
1714 ta_latin_hints_detect_features(TA_GlyphHints hints
,
1720 error
= ta_latin_hints_compute_segments(hints
, dim
);
1723 ta_latin_hints_link_segments(hints
, dim
);
1725 error
= ta_latin_hints_compute_edges(hints
, dim
);
1732 /* compute all edges which lie within blue zones */
1735 ta_latin_hints_compute_blue_edges(TA_GlyphHints hints
,
1736 TA_LatinMetrics metrics
)
1738 TA_AxisHints axis
= &hints
->axis
[TA_DIMENSION_VERT
];
1740 TA_Edge edge
= axis
->edges
;
1741 TA_Edge edge_limit
= edge
+ axis
->num_edges
;
1743 TA_LatinAxis latin
= &metrics
->axis
[TA_DIMENSION_VERT
];
1744 FT_Fixed scale
= latin
->scale
;
1747 /* compute which blue zones are active, */
1748 /* i.e. have their scaled size < 3/4 pixels */
1750 /* for each horizontal edge search the blue zone which is closest */
1751 for (; edge
< edge_limit
; edge
++)
1754 TA_Width best_blue
= NULL
;
1755 FT_Pos best_dist
; /* initial threshold */
1757 FT_UInt best_blue_idx
= 0;
1758 FT_Bool best_blue_is_shoot
= 0;
1761 /* compute the initial threshold as a fraction of the EM size */
1762 /* (the value 40 is heuristic) */
1763 best_dist
= FT_MulFix(metrics
->units_per_em
/ 40, scale
);
1765 /* assure a minimum distance of 0.5px */
1766 if (best_dist
> 64 / 2)
1769 /* this loop also handles the two extra blue zones */
1770 /* for usWinAscent and usWinDescent */
1771 /* if option `windows-compatibility' is set */
1773 bb
< latin
->blue_count
1774 + (metrics
->root
.globals
->font
->windows_compatibility
? 2 : 0);
1777 TA_LatinBlue blue
= latin
->blues
+ bb
;
1778 FT_Bool is_top_blue
, is_major_dir
;
1781 /* skip inactive blue zones (i.e., those that are too large) */
1782 if (!(blue
->flags
& TA_LATIN_BLUE_ACTIVE
))
1785 /* if it is a top zone, check for right edges -- */
1786 /* if it is a bottom zone, check for left edges */
1787 is_top_blue
= (FT_Byte
)((blue
->flags
& TA_LATIN_BLUE_TOP
) != 0);
1788 is_major_dir
= FT_BOOL(edge
->dir
== axis
->major_dir
);
1790 /* if it is a top zone, the edge must be against the major */
1791 /* direction; if it is a bottom zone, it must be in the major */
1793 if (is_top_blue
^ is_major_dir
)
1798 /* first of all, compare it to the reference position */
1799 dist
= edge
->fpos
- blue
->ref
.org
;
1803 dist
= FT_MulFix(dist
, scale
);
1804 if (dist
< best_dist
)
1807 best_blue
= &blue
->ref
;
1810 best_blue_is_shoot
= 0;
1813 /* now compare it to the overshoot position and check whether */
1814 /* the edge is rounded, and whether the edge is over the */
1815 /* reference position of a top zone, or under the reference */
1816 /* position of a bottom zone */
1817 if (edge
->flags
& TA_EDGE_ROUND
1820 FT_Bool is_under_ref
= FT_BOOL(edge
->fpos
< blue
->ref
.org
);
1823 if (is_top_blue
^ is_under_ref
)
1825 dist
= edge
->fpos
- blue
->shoot
.org
;
1829 dist
= FT_MulFix(dist
, scale
);
1830 if (dist
< best_dist
)
1833 best_blue
= &blue
->shoot
;
1836 best_blue_is_shoot
= 1;
1845 edge
->blue_edge
= best_blue
;
1846 edge
->best_blue_idx
= best_blue_idx
;
1847 edge
->best_blue_is_shoot
= best_blue_is_shoot
;
1853 /* initalize hinting engine */
1856 ta_latin_hints_init(TA_GlyphHints hints
,
1857 TA_LatinMetrics metrics
)
1859 FT_Render_Mode mode
;
1860 FT_UInt32 scaler_flags
, other_flags
;
1861 FT_Face face
= metrics
->root
.scaler
.face
;
1864 ta_glyph_hints_rescale(hints
, (TA_ScriptMetrics
)metrics
);
1866 /* correct x_scale and y_scale if needed, since they may have */
1867 /* been modified by `ta_latin_metrics_scale_dim' above */
1868 hints
->x_scale
= metrics
->axis
[TA_DIMENSION_HORZ
].scale
;
1869 hints
->x_delta
= metrics
->axis
[TA_DIMENSION_HORZ
].delta
;
1870 hints
->y_scale
= metrics
->axis
[TA_DIMENSION_VERT
].scale
;
1871 hints
->y_delta
= metrics
->axis
[TA_DIMENSION_VERT
].delta
;
1873 /* compute flags depending on render mode, etc. */
1874 mode
= metrics
->root
.scaler
.render_mode
;
1876 #if 0 /* #ifdef TA_CONFIG_OPTION_USE_WARPER */
1877 if (mode
== FT_RENDER_MODE_LCD
1878 || mode
== FT_RENDER_MODE_LCD_V
)
1879 metrics
->root
.scaler
.render_mode
=
1880 mode
= FT_RENDER_MODE_NORMAL
;
1883 scaler_flags
= hints
->scaler_flags
;
1886 /* we snap the width of vertical stems for the monochrome */
1887 /* and horizontal LCD rendering targets only */
1888 if (mode
== FT_RENDER_MODE_MONO
1889 || mode
== FT_RENDER_MODE_LCD
)
1890 other_flags
|= TA_LATIN_HINTS_HORZ_SNAP
;
1892 /* we snap the width of horizontal stems for the monochrome */
1893 /* and vertical LCD rendering targets only */
1894 if (mode
== FT_RENDER_MODE_MONO
1895 || mode
== FT_RENDER_MODE_LCD_V
)
1896 other_flags
|= TA_LATIN_HINTS_VERT_SNAP
;
1898 /* we adjust stems to full pixels only if we don't use the `light' mode */
1899 if (mode
!= FT_RENDER_MODE_LIGHT
)
1900 other_flags
|= TA_LATIN_HINTS_STEM_ADJUST
;
1902 if (mode
== FT_RENDER_MODE_MONO
)
1903 other_flags
|= TA_LATIN_HINTS_MONO
;
1905 /* in `light' hinting mode we disable horizontal hinting completely; */
1906 /* we also do it if the face is italic */
1907 if (mode
== FT_RENDER_MODE_LIGHT
1908 || (face
->style_flags
& FT_STYLE_FLAG_ITALIC
) != 0)
1909 scaler_flags
|= TA_SCALER_FLAG_NO_HORIZONTAL
;
1911 hints
->scaler_flags
= scaler_flags
;
1912 hints
->other_flags
= other_flags
;
1918 /* snap a given width in scaled coordinates to */
1919 /* one of the current standard widths */
1922 ta_latin_snap_width(TA_Width widths
,
1927 FT_Pos best
= 64 + 32 + 2;
1928 FT_Pos reference
= width
;
1932 for (n
= 0; n
< count
; n
++)
1949 scaled
= TA_PIX_ROUND(reference
);
1951 if (width
>= reference
)
1953 if (width
< scaled
+ 48)
1958 if (width
> scaled
- 48)
1966 /* compute the snapped width of a given stem, ignoring very thin ones */
1968 /* there is a lot of voodoo in this function; changing the hard-coded */
1969 /* parameters influence the whole hinting process */
1972 ta_latin_compute_stem_width(TA_GlyphHints hints
,
1978 TA_LatinMetrics metrics
= (TA_LatinMetrics
) hints
->metrics
;
1979 TA_LatinAxis axis
= &metrics
->axis
[dim
];
1981 FT_Pos dist
= width
;
1983 FT_Int vertical
= (dim
== TA_DIMENSION_VERT
);
1986 if (!TA_LATIN_HINTS_DO_STEM_ADJUST(hints
)
1987 || axis
->extra_light
)
1996 if ((vertical
&& !TA_LATIN_HINTS_DO_VERT_SNAP(hints
))
1997 || (!vertical
&& !TA_LATIN_HINTS_DO_HORZ_SNAP(hints
)))
1999 /* smooth hinting process: very lightly quantize the stem width */
2001 /* leave the widths of serifs alone */
2002 if ((stem_flags
& TA_EDGE_SERIF
)
2006 else if (base_flags
& TA_EDGE_ROUND
)
2014 if (axis
->width_count
> 0)
2019 /* compare to standard width */
2020 delta
= dist
- axis
->widths
[0].cur
;
2027 dist
= axis
->widths
[0].cur
;
2041 else if (delta
< 32)
2043 else if (delta
< 54)
2049 dist
= (dist
+ 32) & ~63;
2054 /* strong hinting process: snap the stem width to integer pixels */
2056 FT_Pos org_dist
= dist
;
2059 dist
= ta_latin_snap_width(axis
->widths
, axis
->width_count
, dist
);
2063 /* in the case of vertical hinting, */
2064 /* always round the stem heights to integer pixels */
2067 dist
= (dist
+ 16) & ~63;
2073 if (TA_LATIN_HINTS_DO_MONO(hints
))
2075 /* monochrome horizontal hinting: */
2076 /* snap widths to integer pixels with a different threshold */
2081 dist
= (dist
+ 32) & ~63;
2085 /* for horizontal anti-aliased hinting, we adopt a more subtle */
2086 /* approach: we strengthen small stems, round stems whose size */
2087 /* is between 1 and 2 pixels to an integer, otherwise nothing */
2090 dist
= (dist
+ 64) >> 1;
2092 else if (dist
< 128)
2094 /* we only round to an integer width if the corresponding */
2095 /* distortion is less than 1/4 pixel -- otherwise, this */
2096 /* makes everything worse since the diagonals, which are */
2097 /* not hinted, appear a lot bolder or thinner than the */
2098 /* vertical stems */
2103 dist
= (dist
+ 22) & ~63;
2104 delta
= dist
- org_dist
;
2112 dist
= (dist
+ 64) >> 1;
2116 /* round otherwise to prevent color fringes in LCD mode */
2117 dist
= (dist
+ 32) & ~63;
2130 /* align one stem edge relative to the previous stem edge */
2133 ta_latin_align_linked_edge(TA_GlyphHints hints
,
2138 FT_Pos dist
= stem_edge
->opos
- base_edge
->opos
;
2140 FT_Pos fitted_width
= ta_latin_compute_stem_width(
2146 stem_edge
->pos
= base_edge
->pos
+ fitted_width
;
2148 TA_LOG((" LINK: edge %d (opos=%.2f) linked to %.2f,"
2149 " dist was %.2f, now %.2f\n",
2150 stem_edge
- hints
->axis
[dim
].edges
, stem_edge
->opos
/ 64.0,
2151 stem_edge
->pos
/ 64.0, dist
/ 64.0, fitted_width
/ 64.0));
2153 if (hints
->recorder
)
2154 hints
->recorder(ta_link
, hints
, dim
,
2155 base_edge
, stem_edge
, NULL
, NULL
, NULL
);
2159 /* shift the coordinates of the `serif' edge by the same amount */
2160 /* as the corresponding `base' edge has been moved already */
2163 ta_latin_align_serif_edge(TA_GlyphHints hints
,
2169 serif
->pos
= base
->pos
+ (serif
->opos
- base
->opos
);
2173 /* the main grid-fitting routine */
2176 ta_latin_hint_edges(TA_GlyphHints hints
,
2179 TA_AxisHints axis
= &hints
->axis
[dim
];
2181 TA_Edge edges
= axis
->edges
;
2182 TA_Edge edge_limit
= edges
+ axis
->num_edges
;
2186 TA_Edge anchor
= NULL
;
2187 FT_Int has_serifs
= 0;
2190 FT_UInt num_actions
= 0;
2193 TA_LOG(("latin %s edge hinting (script `%s')\n",
2194 dim
== TA_DIMENSION_VERT
? "horizontal" : "vertical",
2195 ta_script_names
[hints
->metrics
->script_class
->script
]));
2197 /* we begin by aligning all stems relative to the blue zone if needed -- */
2198 /* that's only for horizontal edges */
2200 if (dim
== TA_DIMENSION_VERT
2201 && TA_HINTS_DO_BLUES(hints
))
2203 for (edge
= edges
; edge
< edge_limit
; edge
++)
2206 TA_Edge edge1
, edge2
; /* these edges form the stem to check */
2209 if (edge
->flags
& TA_EDGE_DONE
)
2212 blue
= edge
->blue_edge
;
2219 /* flip edges if the other stem is aligned to a blue zone */
2220 else if (edge2
&& edge2
->blue_edge
)
2222 blue
= edge2
->blue_edge
;
2232 TA_LOG((" BLUE_ANCHOR: edge %d (opos=%.2f) snapped to %.2f,"
2233 " was %.2f (anchor=edge %d)\n",
2234 edge1
- edges
, edge1
->opos
/ 64.0, blue
->fit
/ 64.0,
2235 edge1
->pos
/ 64.0, edge
- edges
));
2237 TA_LOG((" BLUE: edge %d (opos=%.2f) snapped to %.2f, was %.2f\n",
2238 edge1
- edges
, edge1
->opos
/ 64.0, blue
->fit
/ 64.0,
2239 edge1
->pos
/ 64.0));
2244 edge1
->pos
= blue
->fit
;
2245 edge1
->flags
|= TA_EDGE_DONE
;
2247 if (hints
->recorder
)
2250 hints
->recorder(ta_blue_anchor
, hints
, dim
,
2251 edge1
, edge
, NULL
, NULL
, NULL
);
2253 hints
->recorder(ta_blue
, hints
, dim
,
2254 edge1
, NULL
, NULL
, NULL
, NULL
);
2257 if (edge2
&& !edge2
->blue_edge
)
2259 ta_latin_align_linked_edge(hints
, dim
, edge1
, edge2
);
2260 edge2
->flags
|= TA_EDGE_DONE
;
2272 /* now we align all other stem edges, */
2273 /* trying to maintain the relative order of stems in the glyph */
2274 for (edge
= edges
; edge
< edge_limit
; edge
++)
2279 if (edge
->flags
& TA_EDGE_DONE
)
2282 /* skip all non-stem edges */
2290 /* now align the stem */
2292 /* this should not happen, but it's better to be safe */
2293 if (edge2
->blue_edge
)
2295 TA_LOG((" ASSERTION FAILED for edge %d\n", edge2
-edges
));
2297 ta_latin_align_linked_edge(hints
, dim
, edge2
, edge
);
2298 edge
->flags
|= TA_EDGE_DONE
;
2308 /* if we reach this if clause, no stem has been aligned yet */
2310 FT_Pos org_len
, org_center
, cur_len
;
2311 FT_Pos cur_pos1
, error1
, error2
, u_off
, d_off
;
2314 org_len
= edge2
->opos
- edge
->opos
;
2315 cur_len
= ta_latin_compute_stem_width(hints
, dim
, org_len
,
2316 edge
->flags
, edge2
->flags
);
2318 /* some voodoo to specially round edges for small stem widths; */
2319 /* the idea is to align the center of a stem, */
2320 /* then shifting the stem edges to suitable positions */
2329 /* 1px < width < 1.5px */
2336 org_center
= edge
->opos
+ (org_len
>> 1);
2337 cur_pos1
= TA_PIX_ROUND(org_center
);
2339 error1
= org_center
- (cur_pos1
- u_off
);
2343 error2
= org_center
- (cur_pos1
+ d_off
);
2347 if (error1
< error2
)
2352 edge
->pos
= cur_pos1
- cur_len
/ 2;
2353 edge2
->pos
= edge
->pos
+ cur_len
;
2356 edge
->pos
= TA_PIX_ROUND(edge
->opos
);
2359 edge
->flags
|= TA_EDGE_DONE
;
2361 TA_LOG((" ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
2362 " snapped to %.2f and %.2f\n",
2363 edge
- edges
, edge
->opos
/ 64.0,
2364 edge2
- edges
, edge2
->opos
/ 64.0,
2365 edge
->pos
/ 64.0, edge2
->pos
/ 64.0));
2367 if (hints
->recorder
)
2368 hints
->recorder(ta_anchor
, hints
, dim
,
2369 edge
, edge2
, NULL
, NULL
, NULL
);
2371 ta_latin_align_linked_edge(hints
, dim
, edge
, edge2
);
2379 FT_Pos org_pos
, org_len
, org_center
, cur_len
;
2380 FT_Pos cur_pos1
, cur_pos2
, delta1
, delta2
;
2383 org_pos
= anchor
->pos
+ (edge
->opos
- anchor
->opos
);
2384 org_len
= edge2
->opos
- edge
->opos
;
2385 org_center
= org_pos
+ (org_len
>> 1);
2387 cur_len
= ta_latin_compute_stem_width(hints
, dim
, org_len
,
2388 edge
->flags
, edge2
->flags
);
2390 if (edge2
->flags
& TA_EDGE_DONE
)
2392 TA_LOG((" ADJUST: edge %d (pos=%.2f) moved to %.2f\n",
2393 edge
- edges
, edge
->pos
/ 64.0,
2394 (edge2
->pos
- cur_len
) / 64.0));
2396 edge
->pos
= edge2
->pos
- cur_len
;
2398 if (hints
->recorder
)
2400 TA_Edge bound
= NULL
;
2406 hints
->recorder(ta_adjust
, hints
, dim
,
2407 edge
, edge2
, NULL
, bound
, NULL
);
2411 else if (cur_len
< 96)
2413 FT_Pos u_off
, d_off
;
2416 cur_pos1
= TA_PIX_ROUND(org_center
);
2429 delta1
= org_center
- (cur_pos1
- u_off
);
2433 delta2
= org_center
- (cur_pos1
+ d_off
);
2437 if (delta1
< delta2
)
2442 edge
->pos
= cur_pos1
- cur_len
/ 2;
2443 edge2
->pos
= cur_pos1
+ cur_len
/ 2;
2445 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2446 " snapped to %.2f and %.2f\n",
2447 edge
- edges
, edge
->opos
/ 64.0,
2448 edge2
- edges
, edge2
->opos
/ 64.0,
2449 edge
->pos
/ 64.0, edge2
->pos
/ 64.0));
2451 if (hints
->recorder
)
2453 TA_Edge bound
= NULL
;
2459 hints
->recorder(ta_stem
, hints
, dim
,
2460 edge
, edge2
, NULL
, bound
, NULL
);
2466 org_pos
= anchor
->pos
+ (edge
->opos
- anchor
->opos
);
2467 org_len
= edge2
->opos
- edge
->opos
;
2468 org_center
= org_pos
+ (org_len
>> 1);
2470 cur_len
= ta_latin_compute_stem_width(hints
, dim
, org_len
,
2471 edge
->flags
, edge2
->flags
);
2473 cur_pos1
= TA_PIX_ROUND(org_pos
);
2474 delta1
= cur_pos1
+ (cur_len
>> 1) - org_center
;
2478 cur_pos2
= TA_PIX_ROUND(org_pos
+ org_len
) - cur_len
;
2479 delta2
= cur_pos2
+ (cur_len
>> 1) - org_center
;
2483 edge
->pos
= (delta1
< delta2
) ? cur_pos1
: cur_pos2
;
2484 edge2
->pos
= edge
->pos
+ cur_len
;
2486 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2487 " snapped to %.2f and %.2f\n",
2488 edge
- edges
, edge
->opos
/ 64.0,
2489 edge2
- edges
, edge2
->opos
/ 64.0,
2490 edge
->pos
/ 64.0, edge2
->pos
/ 64.0));
2492 if (hints
->recorder
)
2494 TA_Edge bound
= NULL
;
2500 hints
->recorder(ta_stem
, hints
, dim
,
2501 edge
, edge2
, NULL
, bound
, NULL
);
2509 edge
->flags
|= TA_EDGE_DONE
;
2510 edge2
->flags
|= TA_EDGE_DONE
;
2513 && edge
->pos
< edge
[-1].pos
)
2516 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2517 edge
- edges
, edge
->pos
/ 64.0, edge
[-1].pos
/ 64.0));
2522 edge
->pos
= edge
[-1].pos
;
2524 if (hints
->recorder
)
2525 hints
->recorder(ta_bound
, hints
, dim
,
2526 edge
, &edge
[-1], NULL
, NULL
, NULL
);
2531 /* make sure that lowercase m's maintain their symmetry */
2533 /* In general, lowercase m's have six vertical edges if they are sans */
2534 /* serif, or twelve if they are with serifs. This implementation is */
2535 /* based on that assumption, and seems to work very well with most */
2536 /* faces. However, if for a certain face this assumption is not */
2537 /* true, the m is just rendered like before. In addition, any stem */
2538 /* correction will only be applied to symmetrical glyphs (even if the */
2539 /* glyph is not an m), so the potential for unwanted distortion is */
2540 /* relatively low. */
2542 /* we don't handle horizontal edges since we can't easily assure that */
2543 /* the third (lowest) stem aligns with the base line; it might end up */
2544 /* one pixel higher or lower */
2546 n_edges
= edge_limit
- edges
;
2547 if (dim
== TA_DIMENSION_HORZ
2548 && (n_edges
== 6 || n_edges
== 12))
2550 TA_Edge edge1
, edge2
, edge3
;
2551 FT_Pos dist1
, dist2
, span
, delta
;
2567 dist1
= edge2
->opos
- edge1
->opos
;
2568 dist2
= edge3
->opos
- edge2
->opos
;
2570 span
= dist1
- dist2
;
2576 delta
= edge3
->pos
- (2 * edge2
->pos
- edge1
->pos
);
2577 edge3
->pos
-= delta
;
2579 edge3
->link
->pos
-= delta
;
2581 /* move the serifs along with the stem */
2584 (edges
+ 8)->pos
-= delta
;
2585 (edges
+ 11)->pos
-= delta
;
2588 edge3
->flags
|= TA_EDGE_DONE
;
2590 edge3
->link
->flags
|= TA_EDGE_DONE
;
2594 if (has_serifs
|| !anchor
)
2596 /* now hint the remaining edges (serifs and single) */
2597 /* in order to complete our processing */
2598 for (edge
= edges
; edge
< edge_limit
; edge
++)
2600 TA_Edge lower_bound
= NULL
;
2601 TA_Edge upper_bound
= NULL
;
2606 if (edge
->flags
& TA_EDGE_DONE
)
2613 delta
= edge
->serif
->opos
- edge
->opos
;
2619 lower_bound
= &edge
[-1];
2621 if (edge
+ 1 < edge_limit
2622 && edge
[1].flags
& TA_EDGE_DONE
)
2623 upper_bound
= &edge
[1];
2626 if (delta
< 64 + 16)
2628 ta_latin_align_serif_edge(hints
, edge
->serif
, edge
);
2630 TA_LOG((" SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
2631 " aligned to %.2f\n",
2632 edge
- edges
, edge
->opos
/ 64.0,
2633 edge
->serif
- edges
, edge
->serif
->opos
/ 64.0,
2636 if (hints
->recorder
)
2637 hints
->recorder(ta_serif
, hints
, dim
,
2638 edge
, NULL
, NULL
, lower_bound
, upper_bound
);
2642 edge
->pos
= TA_PIX_ROUND(edge
->opos
);
2645 TA_LOG((" SERIF_ANCHOR: edge %d (opos=%.2f) snapped to %.2f\n",
2646 edge
- edges
, edge
->opos
/ 64.0, edge
->pos
/ 64.0));
2648 if (hints
->recorder
)
2649 hints
->recorder(ta_serif_anchor
, hints
, dim
,
2650 edge
, NULL
, NULL
, lower_bound
, upper_bound
);
2654 TA_Edge before
, after
;
2657 for (before
= edge
- 1; before
>= edges
; before
--)
2658 if (before
->flags
& TA_EDGE_DONE
)
2661 for (after
= edge
+ 1; after
< edge_limit
; after
++)
2662 if (after
->flags
& TA_EDGE_DONE
)
2665 if (before
>= edges
&& before
< edge
2666 && after
< edge_limit
&& after
> edge
)
2668 if (after
->opos
== before
->opos
)
2669 edge
->pos
= before
->pos
;
2671 edge
->pos
= before
->pos
+ FT_MulDiv(edge
->opos
- before
->opos
,
2672 after
->pos
- before
->pos
,
2673 after
->opos
- before
->opos
);
2675 TA_LOG((" SERIF_LINK1: edge %d (opos=%.2f) snapped to %.2f"
2676 " from %d (opos=%.2f)\n",
2677 edge
- edges
, edge
->opos
/ 64.0,
2679 before
- edges
, before
->opos
/ 64.0));
2681 if (hints
->recorder
)
2682 hints
->recorder(ta_serif_link1
, hints
, dim
,
2683 edge
, before
, after
, lower_bound
, upper_bound
);
2687 edge
->pos
= anchor
->pos
+ ((edge
->opos
- anchor
->opos
+ 16) & ~31);
2688 TA_LOG((" SERIF_LINK2: edge %d (opos=%.2f) snapped to %.2f\n",
2689 edge
- edges
, edge
->opos
/ 64.0, edge
->pos
/ 64.0));
2691 if (hints
->recorder
)
2692 hints
->recorder(ta_serif_link2
, hints
, dim
,
2693 edge
, NULL
, NULL
, lower_bound
, upper_bound
);
2700 edge
->flags
|= TA_EDGE_DONE
;
2703 && edge
->pos
< edge
[-1].pos
)
2706 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2707 edge
- edges
, edge
->pos
/ 64.0, edge
[-1].pos
/ 64.0));
2711 edge
->pos
= edge
[-1].pos
;
2713 if (hints
->recorder
)
2714 hints
->recorder(ta_bound
, hints
, dim
,
2715 edge
, &edge
[-1], NULL
, NULL
, NULL
);
2718 if (edge
+ 1 < edge_limit
2719 && edge
[1].flags
& TA_EDGE_DONE
2720 && edge
->pos
> edge
[1].pos
)
2723 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2724 edge
- edges
, edge
->pos
/ 64.0, edge
[1].pos
/ 64.0));
2729 edge
->pos
= edge
[1].pos
;
2731 if (hints
->recorder
)
2732 hints
->recorder(ta_bound
, hints
, dim
,
2733 edge
, &edge
[1], NULL
, NULL
, NULL
);
2740 TA_LOG((" (none)\n"));
2746 /* apply the complete hinting algorithm to a latin glyph */
2749 ta_latin_hints_apply(TA_GlyphHints hints
,
2750 FT_Outline
* outline
,
2751 TA_LatinMetrics metrics
)
2757 error
= ta_glyph_hints_reload(hints
, outline
);
2761 /* analyze glyph outline */
2762 #ifdef TA_CONFIG_OPTION_USE_WARPER
2763 if (metrics
->root
.scaler
.render_mode
== FT_RENDER_MODE_LIGHT
2764 || TA_HINTS_DO_HORIZONTAL(hints
))
2766 if (TA_HINTS_DO_HORIZONTAL(hints
))
2769 error
= ta_latin_hints_detect_features(hints
, TA_DIMENSION_HORZ
);
2774 if (TA_HINTS_DO_VERTICAL(hints
))
2776 error
= ta_latin_hints_detect_features(hints
, TA_DIMENSION_VERT
);
2780 ta_latin_hints_compute_blue_edges(hints
, metrics
);
2783 /* grid-fit the outline */
2784 for (dim
= 0; dim
< TA_DIMENSION_MAX
; dim
++)
2786 #ifdef TA_CONFIG_OPTION_USE_WARPER
2787 if (dim
== TA_DIMENSION_HORZ
2788 && metrics
->root
.scaler
.render_mode
== FT_RENDER_MODE_LIGHT
)
2790 TA_WarperRec warper
;
2795 ta_warper_compute(&warper
, hints
, (TA_Dimension
)dim
, &scale
, &delta
);
2796 ta_glyph_hints_scale_dim(hints
, (TA_Dimension
)dim
, scale
, delta
);
2802 if ((dim
== TA_DIMENSION_HORZ
&& TA_HINTS_DO_HORIZONTAL(hints
))
2803 || (dim
== TA_DIMENSION_VERT
&& TA_HINTS_DO_VERTICAL(hints
)))
2805 ta_latin_hint_edges(hints
, (TA_Dimension
)dim
);
2806 ta_glyph_hints_align_edge_points(hints
, (TA_Dimension
)dim
);
2807 ta_glyph_hints_align_strong_points(hints
, (TA_Dimension
)dim
);
2808 ta_glyph_hints_align_weak_points(hints
, (TA_Dimension
)dim
);
2812 ta_glyph_hints_save(hints
, outline
);
2819 const TA_WritingSystemClassRec ta_latin_writing_system_class
=
2821 TA_WRITING_SYSTEM_LATIN
,
2823 sizeof (TA_LatinMetricsRec
),
2825 (TA_Script_InitMetricsFunc
)ta_latin_metrics_init
,
2826 (TA_Script_ScaleMetricsFunc
)ta_latin_metrics_scale
,
2827 (TA_Script_DoneMetricsFunc
)NULL
,
2829 (TA_Script_InitHintsFunc
)ta_latin_hints_init
,
2830 (TA_Script_ApplyHintsFunc
)ta_latin_hints_apply
2834 /* XXX: this should probably fine tuned to differentiate better between */
2837 static const TA_Script_UniRangeRec ta_latn_uniranges
[] =
2839 TA_UNIRANGE_REC(0x0020UL
, 0x007FUL
), /* Basic Latin (no control chars) */
2840 TA_UNIRANGE_REC(0x00A0UL
, 0x00FFUL
), /* Latin-1 Supplement (no control chars) */
2841 TA_UNIRANGE_REC(0x0100UL
, 0x017FUL
), /* Latin Extended-A */
2842 TA_UNIRANGE_REC(0x0180UL
, 0x024FUL
), /* Latin Extended-B */
2843 TA_UNIRANGE_REC(0x0250UL
, 0x02AFUL
), /* IPA Extensions */
2844 TA_UNIRANGE_REC(0x02B0UL
, 0x02FFUL
), /* Spacing Modifier Letters */
2845 TA_UNIRANGE_REC(0x0300UL
, 0x036FUL
), /* Combining Diacritical Marks */
2846 TA_UNIRANGE_REC(0x1D00UL
, 0x1D7FUL
), /* Phonetic Extensions */
2847 TA_UNIRANGE_REC(0x1D80UL
, 0x1DBFUL
), /* Phonetic Extensions Supplement */
2848 TA_UNIRANGE_REC(0x1DC0UL
, 0x1DFFUL
), /* Combining Diacritical Marks Supplement */
2849 TA_UNIRANGE_REC(0x1E00UL
, 0x1EFFUL
), /* Latin Extended Additional */
2850 TA_UNIRANGE_REC(0x2000UL
, 0x206FUL
), /* General Punctuation */
2851 TA_UNIRANGE_REC(0x2070UL
, 0x209FUL
), /* Superscripts and Subscripts */
2852 TA_UNIRANGE_REC(0x20A0UL
, 0x20CFUL
), /* Currency Symbols */
2853 TA_UNIRANGE_REC(0x2150UL
, 0x218FUL
), /* Number Forms */
2854 TA_UNIRANGE_REC(0x2460UL
, 0x24FFUL
), /* Enclosed Alphanumerics */
2855 TA_UNIRANGE_REC(0x2C60UL
, 0x2C7FUL
), /* Latin Extended-C */
2856 TA_UNIRANGE_REC(0x2E00UL
, 0x2E7FUL
), /* Supplemental Punctuation */
2857 TA_UNIRANGE_REC(0xA720UL
, 0xA7FFUL
), /* Latin Extended-D */
2858 TA_UNIRANGE_REC(0xFB00UL
, 0xFB06UL
), /* Alphab. Present. Forms (Latin Ligs) */
2859 TA_UNIRANGE_REC(0x1D400UL
, 0x1D7FFUL
), /* Mathematical Alphanumeric Symbols */
2860 TA_UNIRANGE_REC(0x1F100UL
, 0x1F1FFUL
), /* Enclosed Alphanumeric Supplement */
2861 TA_UNIRANGE_REC(0UL, 0UL)
2864 static const TA_Script_UniRangeRec ta_grek_uniranges
[] =
2866 TA_UNIRANGE_REC(0x0370UL
, 0x03FFUL
), /* Greek and Coptic */
2867 TA_UNIRANGE_REC(0x1F00UL
, 0x1FFFUL
), /* Greek Extended */
2868 TA_UNIRANGE_REC(0UL, 0UL )
2871 static const TA_Script_UniRangeRec ta_cyrl_uniranges
[] =
2873 TA_UNIRANGE_REC(0x0400UL
, 0x04FFUL
), /* Cyrillic */
2874 TA_UNIRANGE_REC(0x0500UL
, 0x052FUL
), /* Cyrillic Supplement */
2875 TA_UNIRANGE_REC(0x2DE0UL
, 0x2DFFUL
), /* Cyrillic Extended-A */
2876 TA_UNIRANGE_REC(0xA640UL
, 0xA69FUL
), /* Cyrillic Extended-B */
2877 TA_UNIRANGE_REC(0UL, 0UL )
2880 static const TA_Script_UniRangeRec ta_hebr_uniranges
[] =
2882 TA_UNIRANGE_REC(0x0590UL
, 0x05FFUL
), /* Hebrew */
2883 TA_UNIRANGE_REC(0xFB1DUL
, 0xFB4FUL
), /* Alphab. Present. Forms (Hebrew) */
2884 TA_UNIRANGE_REC(0UL, 0UL )
2888 const TA_ScriptClassRec ta_latn_script_class
=
2891 TA_BLUE_STRINGSET_LATN
,
2892 TA_WRITING_SYSTEM_LATIN
,
2898 const TA_ScriptClassRec ta_grek_script_class
=
2901 TA_BLUE_STRINGSET_GREK
,
2902 TA_WRITING_SYSTEM_LATIN
,
2908 const TA_ScriptClassRec ta_cyrl_script_class
=
2911 TA_BLUE_STRINGSET_CYRL
,
2912 TA_WRITING_SYSTEM_LATIN
,
2918 const TA_ScriptClassRec ta_hebr_script_class
=
2921 TA_BLUE_STRINGSET_HEBR
,
2922 TA_WRITING_SYSTEM_LATIN
,
2928 /* end of talatin.c */