Craig White pushed to branch gsoc-craig-2023 at FreeType / FreeType
Commits:
-
f8e996bf
by Craig White at 2023-06-04T15:31:13-04:00
6 changed files:
- + src/autofit/afadjust.c
- + src/autofit/afadjust.h
- src/autofit/aflatin.c
- src/autofit/aftypes.h
- src/autofit/autofit.c
- src/base/ftobjs.c
Changes:
1 | +#include "afadjust.h"
|
|
2 | +#include <freetype/freetype.h>
|
|
3 | +#include <freetype/internal/ftobjs.h>
|
|
4 | +#include <freetype/internal/ftmemory.h>
|
|
5 | + |
|
6 | +#define AF_ADJUSTMENT_DATABASE_LENGTH 12
|
|
7 | + |
|
8 | +/*TODO: find out whether capital u/U with accent entries are needed*/
|
|
9 | +/*the accent won't merge with the rest of the glyph because the accent mark is sitting above empty space*/
|
|
10 | +FT_LOCAL_ARRAY_DEF( AF_AdjustmentDatabaseEntry )
|
|
11 | +adjustment_database[AF_ADJUSTMENT_DATABASE_LENGTH] = {
|
|
12 | + {'i', AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE},
|
|
13 | + {'j', AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE},
|
|
14 | + {0xC8, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*E with grave*/
|
|
15 | + {0xCC, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*I with grave*/
|
|
16 | + {0xD9, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*U with grave*/
|
|
17 | + {0xE0, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*a with grave*/
|
|
18 | + {0xEC, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*i with grave*/
|
|
19 | + {0x114, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*E with macron*/
|
|
20 | + {0x12A, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*I with macron*/
|
|
21 | + {0x12B, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*i with macron*/
|
|
22 | + {0x16A, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*U with macron*/
|
|
23 | + {0x16B, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE} /*u with macron*/
|
|
24 | + /*TODO: find out why E won't work, even though it appears to be one-on-one*/
|
|
25 | +};
|
|
26 | + |
|
27 | +/*Helper function: get the adjustment database entry for a codepoint*/
|
|
28 | +FT_LOCAL_DEF( const AF_AdjustmentDatabaseEntry* )
|
|
29 | +af_adjustment_database_lookup( FT_UInt32 codepoint ) {
|
|
30 | + for ( FT_Int entry = 0; entry < AF_ADJUSTMENT_DATABASE_LENGTH; entry++ ) {
|
|
31 | + if ( adjustment_database[entry].codepoint == codepoint ) {
|
|
32 | + return &adjustment_database[entry];
|
|
33 | + }
|
|
34 | + }
|
|
35 | + |
|
36 | + return NULL;
|
|
37 | +}
|
|
38 | + |
|
39 | +FT_LOCAL_DEF( AF_VerticalSeparationAdjustmentType )
|
|
40 | +af_lookup_vertical_seperation_type( AF_ReverseCharacterMap map, FT_Int glyph_index ) {
|
|
41 | + FT_UInt32 codepoint = af_reverse_character_map_lookup( map, glyph_index );
|
|
42 | + const AF_AdjustmentDatabaseEntry *entry = af_adjustment_database_lookup( codepoint );
|
|
43 | + if ( entry == NULL ) {
|
|
44 | + return AF_VERTICAL_ADJUSTMENT_NONE;
|
|
45 | + }
|
|
46 | + return entry->vertical_separation_adjustment_type;
|
|
47 | +}
|
|
48 | + |
|
49 | +typedef struct AF_ReverseMapEntry_ {
|
|
50 | + FT_Int glyph_index;
|
|
51 | + FT_UInt32 codepoint;
|
|
52 | +} AF_ReverseMapEntry;
|
|
53 | + |
|
54 | +typedef struct AF_ReverseCharacterMap_ {
|
|
55 | + FT_UInt length;
|
|
56 | + AF_ReverseMapEntry *entries;
|
|
57 | +} AF_ReverseCharacterMap_Rec;
|
|
58 | + |
|
59 | +FT_LOCAL_DEF(FT_UInt32)
|
|
60 | +af_reverse_character_map_lookup( AF_ReverseCharacterMap map, FT_Int glyph_index ) {
|
|
61 | + if ( map == NULL ) {
|
|
62 | + return 0;
|
|
63 | + }
|
|
64 | + |
|
65 | + for ( FT_UInt entry = 0; entry < map->length; entry++ ) {
|
|
66 | + if ( map->entries[entry].glyph_index == glyph_index ) {
|
|
67 | + return map->entries[entry].codepoint;
|
|
68 | + }
|
|
69 | + }
|
|
70 | + |
|
71 | + return 0;
|
|
72 | +}
|
|
73 | + |
|
74 | +FT_LOCAL_DEF( FT_Error )
|
|
75 | +af_reverse_character_map_new( FT_Face face, AF_ReverseCharacterMap *map, FT_Memory memory ) {
|
|
76 | + /* Search for a unicode charmap */
|
|
77 | + /* If there isn't one, create a blank map */
|
|
78 | +
|
|
79 | + /*TODO: change this to logic that searches for a "preferred" unicode charmap that maps the most codepoints*/
|
|
80 | + /*see find_unicode_charmap*/
|
|
81 | + /*TODO: use GSUB lookups */
|
|
82 | + FT_CMap unicode_charmap = NULL;
|
|
83 | + for ( FT_UInt i = 0; i < face->num_charmaps; i++ ) {
|
|
84 | + if ( face->charmaps[i]->encoding == FT_ENCODING_UNICODE ) {
|
|
85 | + unicode_charmap = FT_CMAP( face->charmaps[i] );
|
|
86 | + }
|
|
87 | + }
|
|
88 | +
|
|
89 | + if ( unicode_charmap == NULL ) {
|
|
90 | + *map = NULL;
|
|
91 | + return FT_Err_Ok;
|
|
92 | + }
|
|
93 | + |
|
94 | + FT_Error error;
|
|
95 | + |
|
96 | + if ( FT_NEW( *map ) ) {
|
|
97 | + goto Exit;
|
|
98 | + }
|
|
99 | + |
|
100 | + FT_Int capacity = 10;
|
|
101 | + FT_Int size = 0;
|
|
102 | +
|
|
103 | + if ( FT_NEW_ARRAY((*map)->entries, capacity) ) {
|
|
104 | + goto Exit;
|
|
105 | + }
|
|
106 | + for ( FT_Int i = 0; i < AF_ADJUSTMENT_DATABASE_LENGTH; i++ ) {
|
|
107 | + FT_UInt32 codepoint = adjustment_database[i].codepoint;
|
|
108 | + FT_Int glyph = unicode_charmap->clazz->char_index(unicode_charmap, codepoint);
|
|
109 | + if ( glyph == 0 ) {
|
|
110 | + continue;
|
|
111 | + }
|
|
112 | + if (size == capacity) {
|
|
113 | + capacity += capacity / 2;
|
|
114 | + if ( FT_RENEW_ARRAY((*map)->entries, size, capacity) ) {
|
|
115 | + goto Exit;
|
|
116 | + }
|
|
117 | + }
|
|
118 | + size++;
|
|
119 | + (*map)->entries[i].glyph_index = glyph;
|
|
120 | + (*map)->entries[i].codepoint = codepoint;
|
|
121 | + }
|
|
122 | + (*map)->length = size;
|
|
123 | + |
|
124 | +Exit:
|
|
125 | + if ( error ) {
|
|
126 | + if ( *map ) {
|
|
127 | + FT_FREE( ( *map )->entries );
|
|
128 | + }
|
|
129 | + FT_FREE( *map );
|
|
130 | + return error;
|
|
131 | + }
|
|
132 | + |
|
133 | + return FT_Err_Ok;
|
|
134 | +}
|
|
135 | + |
|
136 | +FT_LOCAL_DEF( FT_Error )
|
|
137 | +af_reverse_character_map_done( AF_ReverseCharacterMap map, FT_Memory memory ) {
|
|
138 | + FT_FREE( map->entries );
|
|
139 | + return FT_Err_Ok;
|
|
140 | +} |
|
\ No newline at end of file |
1 | +#ifndef AFADJUST_H_
|
|
2 | +#define AFADJUST_H_
|
|
3 | + |
|
4 | +#include <freetype/fttypes.h>
|
|
5 | + |
|
6 | +FT_BEGIN_HEADER
|
|
7 | + |
|
8 | +/*The type of adjustment that should be done to prevent cases where 2 parts of a character*/
|
|
9 | +/*stacked vertically merge, even though they should be separate*/
|
|
10 | +typedef enum AF_VerticalSeparationAdjustmentType_ {
|
|
11 | + AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE,
|
|
12 | + /*"One on one" means that the character is expected to be one contour on top of another, where the contours should not touch*/
|
|
13 | + /*the hinter will force the contours to have a gap of at least 1 pixel between them*/
|
|
14 | + /*by moving the top contour up */
|
|
15 | + AF_VERTICAL_ADJUSTMENT_NONE
|
|
16 | + |
|
17 | + /*others will be needed, such as the case where the lower contour should be moved in the adjustment instead of the upper one*/
|
|
18 | + /*or umlats, where there are 2 contours which should be moved together*/
|
|
19 | + /*and a way of handling A and O, where the letter consists of 2 contours*/
|
|
20 | +} AF_VerticalSeparationAdjustmentType;
|
|
21 | + |
|
22 | +typedef struct AF_AdjustmentDatabaseEntry_ {
|
|
23 | + FT_UInt32 codepoint;
|
|
24 | + AF_VerticalSeparationAdjustmentType vertical_separation_adjustment_type;
|
|
25 | + } AF_AdjustmentDatabaseEntry;
|
|
26 | + |
|
27 | +struct AF_ReverseCharacterMap_;
|
|
28 | + |
|
29 | +typedef struct AF_ReverseCharacterMap_ *AF_ReverseCharacterMap;
|
|
30 | + |
|
31 | +FT_LOCAL(AF_VerticalSeparationAdjustmentType)
|
|
32 | +af_lookup_vertical_seperation_type( AF_ReverseCharacterMap map, FT_Int glyph_index );
|
|
33 | + |
|
34 | +FT_LOCAL( FT_UInt32 )
|
|
35 | +af_reverse_character_map_lookup( AF_ReverseCharacterMap map, FT_Int glyph_index );
|
|
36 | + |
|
37 | +/*allocate and populate the reverse character map, using the character map within the face*/
|
|
38 | +FT_LOCAL( FT_Error )
|
|
39 | +af_reverse_character_map_new( FT_Face face, AF_ReverseCharacterMap *map, FT_Memory memory );
|
|
40 | +
|
|
41 | +/*free the reverse character map*/
|
|
42 | +FT_LOCAL( FT_Error )
|
|
43 | +af_reverse_character_map_done( AF_ReverseCharacterMap map, FT_Memory memory );
|
|
44 | + |
|
45 | +FT_END_HEADER
|
|
46 | + |
|
47 | +#endif |
|
\ No newline at end of file |
... | ... | @@ -22,6 +22,7 @@ |
22 | 22 | #include "afglobal.h"
|
23 | 23 | #include "aflatin.h"
|
24 | 24 | #include "aferrors.h"
|
25 | +#include "afadjust.h"
|
|
25 | 26 | |
26 | 27 | |
27 | 28 | /**************************************************************************
|
... | ... | @@ -227,7 +228,6 @@ |
227 | 228 | dummy->units_per_em / 100 );
|
228 | 229 | axis->width_count = num_widths;
|
229 | 230 | }
|
230 | - |
|
231 | 231 | Exit:
|
232 | 232 | for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
|
233 | 233 | {
|
... | ... | @@ -1153,6 +1153,8 @@ |
1153 | 1153 | goto Exit;
|
1154 | 1154 | }
|
1155 | 1155 | af_latin_metrics_check_digits( metrics, face );
|
1156 | +
|
|
1157 | + af_reverse_character_map_new( face, &metrics->root.reverse_charmap, face->memory );
|
|
1156 | 1158 | }
|
1157 | 1159 | |
1158 | 1160 | Exit:
|
... | ... | @@ -1502,6 +1504,11 @@ |
1502 | 1504 | af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
|
1503 | 1505 | }
|
1504 | 1506 | |
1507 | + FT_CALLBACK_DEF( void )
|
|
1508 | + af_latin_metrics_done( AF_StyleMetrics metrics_ ) {
|
|
1509 | + AF_LatinMetrics metrics = (AF_LatinMetrics)metrics_;
|
|
1510 | + af_reverse_character_map_done( metrics->root.reverse_charmap, metrics->root.globals->face->memory );
|
|
1511 | + }
|
|
1505 | 1512 | |
1506 | 1513 | /* Extract standard_width from writing system/script specific */
|
1507 | 1514 | /* metrics class. */
|
... | ... | @@ -2738,6 +2745,83 @@ |
2738 | 2745 | }
|
2739 | 2746 | |
2740 | 2747 | |
2748 | +void af_glyph_hints_apply_adjustments(AF_GlyphHints hints, AF_Dimension dim, FT_Int glyph_index, AF_ReverseCharacterMap reverse_charmap) {
|
|
2749 | + if ( dim != AF_DIMENSION_VERT ) {
|
|
2750 | + return;
|
|
2751 | + }
|
|
2752 | + if ( af_lookup_vertical_seperation_type( reverse_charmap, glyph_index ) == AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE &&
|
|
2753 | + hints->num_contours == 2 ) {
|
|
2754 | +
|
|
2755 | + /* Figure out which contout is the higher one by finding the one */
|
|
2756 | + /* with the highest minimum y value */
|
|
2757 | + |
|
2758 | + FT_Int highest_contour = -1;
|
|
2759 | + FT_Pos highest_min_y = 0;
|
|
2760 | + FT_Pos current_min_y = 0;
|
|
2761 | + |
|
2762 | + for ( FT_Int contour = 0; contour < hints->num_contours; contour++ ) {
|
|
2763 | + AF_Point point = hints->contours[contour];
|
|
2764 | + AF_Point first_point = point;
|
|
2765 | + if ( point == NULL ) { /*TODO: is this necessary?*/
|
|
2766 | + continue;
|
|
2767 | + }
|
|
2768 | + current_min_y = point->y;
|
|
2769 | + |
|
2770 | + do {
|
|
2771 | + if ( point->y < current_min_y ) {
|
|
2772 | + current_min_y = point->y;
|
|
2773 | + }
|
|
2774 | + point = point->next;
|
|
2775 | + } while ( point != first_point );
|
|
2776 | + |
|
2777 | + if ( highest_contour == -1 || current_min_y > highest_min_y ) {
|
|
2778 | + highest_min_y = current_min_y;
|
|
2779 | + highest_contour = contour;
|
|
2780 | + }
|
|
2781 | + }
|
|
2782 | + |
|
2783 | + /* If there are any contours that have a maximum y coordinate */
|
|
2784 | + /* greater or equal to the minimum y coordinate of the previously found highest*/
|
|
2785 | + /* contour, bump the high contour up until the distance is one pixel */
|
|
2786 | + |
|
2787 | + FT_Int adjustment_amount = 0;
|
|
2788 | + for ( FT_Int contour = 0; contour < hints->num_contours; contour++ ) {
|
|
2789 | + if (contour == highest_contour) {
|
|
2790 | + continue;
|
|
2791 | + }
|
|
2792 | + AF_Point point = hints->contours[contour];
|
|
2793 | + AF_Point first_point = point;
|
|
2794 | + if ( point == NULL ) {
|
|
2795 | + continue;
|
|
2796 | + }
|
|
2797 | + FT_Pos max_y = point->y;
|
|
2798 | + |
|
2799 | + do {
|
|
2800 | + if ( point->y > max_y ) {
|
|
2801 | + max_y = point->y;
|
|
2802 | + }
|
|
2803 | + point = point->next;
|
|
2804 | + } while ( point != first_point );
|
|
2805 | + |
|
2806 | + if ( max_y >= highest_min_y - 64 ) {
|
|
2807 | + adjustment_amount = 64 - (highest_min_y - max_y);
|
|
2808 | + }
|
|
2809 | + }
|
|
2810 | + |
|
2811 | + if ( adjustment_amount > 0 ) {
|
|
2812 | + AF_Point point = hints->contours[highest_contour];
|
|
2813 | + AF_Point first_point = point;
|
|
2814 | + if ( point != NULL ) {
|
|
2815 | + do {
|
|
2816 | + point->y += adjustment_amount;
|
|
2817 | + point = point->next;
|
|
2818 | + } while ( point != first_point );
|
|
2819 | + }
|
|
2820 | + }
|
|
2821 | + }
|
|
2822 | +}
|
|
2823 | + |
|
2824 | + |
|
2741 | 2825 | /* Compute the snapped width of a given stem, ignoring very thin ones. */
|
2742 | 2826 | /* There is a lot of voodoo in this function; changing the hard-coded */
|
2743 | 2827 | /* parameters influence the whole hinting process. */
|
... | ... | @@ -3605,6 +3689,7 @@ |
3605 | 3689 | af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim );
|
3606 | 3690 | af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
|
3607 | 3691 | af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
|
3692 | + af_glyph_hints_apply_adjustments(hints, (AF_Dimension) dim, glyph_index, metrics->root.reverse_charmap);
|
|
3608 | 3693 | }
|
3609 | 3694 | }
|
3610 | 3695 | |
... | ... | @@ -3633,12 +3718,11 @@ |
3633 | 3718 | |
3634 | 3719 | (AF_WritingSystem_InitMetricsFunc) af_latin_metrics_init, /* style_metrics_init */
|
3635 | 3720 | (AF_WritingSystem_ScaleMetricsFunc)af_latin_metrics_scale, /* style_metrics_scale */
|
3636 | - (AF_WritingSystem_DoneMetricsFunc) NULL, /* style_metrics_done */
|
|
3721 | + (AF_WritingSystem_DoneMetricsFunc) af_latin_metrics_done, /* style_metrics_done */
|
|
3637 | 3722 | (AF_WritingSystem_GetStdWidthsFunc)af_latin_get_standard_widths, /* style_metrics_getstdw */
|
3638 | 3723 | |
3639 | 3724 | (AF_WritingSystem_InitHintsFunc) af_latin_hints_init, /* style_hints_init */
|
3640 | 3725 | (AF_WritingSystem_ApplyHintsFunc) af_latin_hints_apply /* style_hints_apply */
|
3641 | 3726 | )
|
3642 | 3727 | |
3643 | - |
|
3644 | 3728 | /* END */ |
... | ... | @@ -39,6 +39,7 @@ |
39 | 39 | #include <freetype/internal/ftdebug.h>
|
40 | 40 | |
41 | 41 | #include "afblue.h"
|
42 | +#include "afadjust.h"
|
|
42 | 43 | |
43 | 44 | #ifdef FT_DEBUG_AUTOFIT
|
44 | 45 | #include FT_CONFIG_STANDARD_LIBRARY_H
|
... | ... | @@ -417,6 +418,7 @@ extern void* af_debug_hints_; |
417 | 418 | FT_Bool digits_have_same_width;
|
418 | 419 | |
419 | 420 | AF_FaceGlobals globals; /* to access properties */
|
421 | + AF_ReverseCharacterMap reverse_charmap;
|
|
420 | 422 | |
421 | 423 | } AF_StyleMetricsRec;
|
422 | 424 |
... | ... | @@ -30,6 +30,7 @@ |
30 | 30 | #include "afmodule.c"
|
31 | 31 | #include "afranges.c"
|
32 | 32 | #include "afshaper.c"
|
33 | +#include "afadjust.c"
|
|
33 | 34 | |
34 | 35 | |
35 | 36 | /* END */ |
... | ... | @@ -1358,7 +1358,6 @@ |
1358 | 1358 | driver );
|
1359 | 1359 | }
|
1360 | 1360 | |
1361 | - |
|
1362 | 1361 | /**************************************************************************
|
1363 | 1362 | *
|
1364 | 1363 | * @Function:
|