Compare commits
16 Commits
master
...
gsoc-craig
Author | SHA1 | Date | |
---|---|---|---|
|
aeba770c07 | ||
|
99f56eed57 | ||
|
23daa69048 | ||
|
2e5620a46f | ||
|
e0f9830b00 | ||
|
2c473abb1c | ||
|
ed574779d7 | ||
|
75876af476 | ||
|
86e5080e86 | ||
|
fa336db624 | ||
|
9a813d0c55 | ||
|
cfce1acb14 | ||
|
6650b9ebc7 | ||
|
37e3e348b5 | ||
|
62378cca3a | ||
|
f8e996bfb1 |
@ -431,7 +431,7 @@ FT_BEGIN_HEADER
|
||||
* them for certain configurations only.
|
||||
*/
|
||||
/* #define FT_DEBUG_LEVEL_ERROR */
|
||||
/* #define FT_DEBUG_LEVEL_TRACE */
|
||||
#define FT_DEBUG_LEVEL_TRACE
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
|
@ -275,6 +275,26 @@ FT_BEGIN_HEADER
|
||||
FT_GlyphSlot slot,
|
||||
FT_Render_Mode mode );
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* @Function:
|
||||
* find_unicode_charmap
|
||||
*
|
||||
* @Description:
|
||||
* This function finds a Unicode charmap, if there is one.
|
||||
* And if there is more than one, it tries to favour the more
|
||||
* extensive one, i.e., one that supports UCS-4 against those which
|
||||
* are limited to the BMP ( UCS-2 encoding.)
|
||||
*
|
||||
* If a unicode charmap is found, face->charmap is set to it.
|
||||
*
|
||||
* This function is called from open_face(),
|
||||
* from FT_Select_Charmap( ..., FT_ENCODING_UNICODE ),
|
||||
* and also from afadjust.c in the autofit module.
|
||||
*/
|
||||
FT_BASE( FT_Error )
|
||||
find_unicode_charmap( FT_Face face );
|
||||
|
||||
#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
|
||||
|
||||
typedef void (*FT_Bitmap_LcdFilterFunc)( FT_Bitmap* bitmap,
|
||||
|
@ -164,6 +164,7 @@ FT_TRACE_DEF( afhints )
|
||||
FT_TRACE_DEF( afmodule )
|
||||
FT_TRACE_DEF( aflatin )
|
||||
FT_TRACE_DEF( afshaper )
|
||||
FT_TRACE_DEF( afadjust )
|
||||
|
||||
/* SDF components */
|
||||
FT_TRACE_DEF( sdf ) /* signed distance raster for outlines (ftsdf.c) */
|
||||
|
731
src/autofit/afadjust.c
Normal file
731
src/autofit/afadjust.c
Normal file
@ -0,0 +1,731 @@
|
||||
#include "afadjust.h"
|
||||
#include <freetype/freetype.h>
|
||||
#include <freetype/internal/ftobjs.h>
|
||||
#include <freetype/internal/ftmemory.h>
|
||||
#include <freetype/internal/ftdebug.h>
|
||||
|
||||
#define AF_ADJUSTMENT_DATABASE_LENGTH ( sizeof(adjustment_database)/sizeof(adjustment_database[0]) )
|
||||
#undef FT_COMPONENT
|
||||
#define FT_COMPONENT afadjust
|
||||
|
||||
#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
|
||||
#include <hb.h>
|
||||
#include <hb-ot.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
All entries in this list must be sorted by unicode codepoint ascending
|
||||
The table entries are 3 numbers consisting of:
|
||||
- unicode codepoint. THESE MUST BE LISTED IN ASCENDING ORDER
|
||||
- the vertical adjustment type. One of the entries in AF_VerticalSeparationAdjustmentType
|
||||
- 0 if the topmost contour is a tilde and should be prevented from flattening.
|
||||
*/
|
||||
FT_LOCAL_ARRAY_DEF( AF_AdjustmentDatabaseEntry )
|
||||
adjustment_database[] =
|
||||
{
|
||||
{0x21, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /* ! */
|
||||
{0x69, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /* i */
|
||||
{0x6A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /* j */
|
||||
{0xA1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*Inverted Exclamation Mark*/
|
||||
{0xBF, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /* j */
|
||||
{0xA1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*Inverted Exclamation Mark*/
|
||||
{0xBF, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*Inverted Question Mark*/
|
||||
{0xC0, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*A with grave*/
|
||||
{0xC1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*A with acute*/
|
||||
{0xC2, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*A with circumflex*/
|
||||
{0xC3, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*A with tilde*/
|
||||
{0xC8, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*E with grave*/
|
||||
{0xCC, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*I with grave*/
|
||||
{0xCD, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xCE, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xD1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xD2, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xD3, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xD4, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xD5, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xD9, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xD1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xDB, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xDD, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xE0, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xE1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xE2, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xE3, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1}, /*a with tilde*/
|
||||
{0xE8, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xE9, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xEA, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xEC, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xED, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xEE, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xF1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*n with tilde*/
|
||||
{0xF2, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xF3, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xF4, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xF5, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1}, /*o with tilde*/
|
||||
{0xF9, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xFA, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xFB, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0xFD, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x100, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x101, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x102, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x103, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x106, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x108, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x109, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x10A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x10B, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x10C, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x10D, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x10E, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x112, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x113, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x114, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x115, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x116, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x117, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x11B, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x11C, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x11D, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x11E, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x11F, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x120, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x121, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x123, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x124, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x125, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x128, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x129, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1}, /*i with tilde*/
|
||||
{0x12A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x12B, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x12C, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x12D, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x12F, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x130, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x134, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x135, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x139, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x13A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x143, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x144, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x147, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x14C, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x14D, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x14E, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x14F, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x154, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x155, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x158, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x159, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x15A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x15B, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x15C, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x15D, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x160, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x161, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x164, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x168, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x169, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1}, /*u with tilde*/
|
||||
{0x16A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x16B, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x16C, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x16D, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x174, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x175, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x176, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x177, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x179, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x17A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x17B, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x17C, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x17D, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
|
||||
{0x17E, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}
|
||||
};
|
||||
|
||||
/*FT_LOCAL_DEF( FT_Bool )
|
||||
af_adjustment_database_entry_equals( const AF_AdjustmentDatabaseEntry* a, const AF_AdjustmentDatabaseEntry* b )
|
||||
{
|
||||
return a->codepoint == b->codepoint && a->vertical_separation_adjustment_type == b->vertical_separation_adjustment_type;
|
||||
}*/
|
||||
|
||||
/*Helper function: get the adjustment database entry for a codepoint*/
|
||||
FT_LOCAL_DEF( const AF_AdjustmentDatabaseEntry* )
|
||||
af_adjustment_database_lookup( FT_UInt32 codepoint ) {
|
||||
/* Binary search for database entry */
|
||||
FT_Int low = 0;
|
||||
FT_Int high = AF_ADJUSTMENT_DATABASE_LENGTH - 1;
|
||||
while ( high >= low )
|
||||
{
|
||||
FT_Int mid = ( low + high ) / 2;
|
||||
FT_UInt32 mid_codepoint = adjustment_database[mid].codepoint;
|
||||
if ( mid_codepoint < codepoint )
|
||||
{
|
||||
low = mid + 1;
|
||||
}
|
||||
else if ( mid_codepoint > codepoint )
|
||||
{
|
||||
high = mid - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return &adjustment_database[mid];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FT_LOCAL_DEF( AF_VerticalSeparationAdjustmentType )
|
||||
af_lookup_vertical_seperation_type( AF_ReverseCharacterMap map, FT_Int glyph_index ) {
|
||||
FT_UInt32 codepoint = af_reverse_character_map_lookup( map, glyph_index );
|
||||
const AF_AdjustmentDatabaseEntry *entry = af_adjustment_database_lookup( codepoint );
|
||||
if ( entry == NULL )
|
||||
{
|
||||
return AF_VERTICAL_ADJUSTMENT_NONE;
|
||||
}
|
||||
return entry->vertical_separation_adjustment_type;
|
||||
}
|
||||
|
||||
/*1 if tilde correction should be applied to the topmost contour
|
||||
else 0*/
|
||||
FT_LOCAL_DEF( FT_Bool )
|
||||
af_lookup_tilde_correction_type( AF_ReverseCharacterMap map, FT_Int glyph_index ) {
|
||||
FT_UInt32 codepoint = af_reverse_character_map_lookup( map, glyph_index );
|
||||
const AF_AdjustmentDatabaseEntry *entry = af_adjustment_database_lookup( codepoint );
|
||||
if ( entry == NULL )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return entry->apply_tilde;
|
||||
}
|
||||
|
||||
typedef struct AF_ReverseMapEntry_
|
||||
{
|
||||
FT_Int glyph_index;
|
||||
FT_UInt32 codepoint;
|
||||
} AF_ReverseMapEntry;
|
||||
|
||||
typedef struct AF_ReverseCharacterMap_
|
||||
{
|
||||
FT_Long length;
|
||||
AF_ReverseMapEntry *entries;
|
||||
} AF_ReverseCharacterMap_Rec;
|
||||
|
||||
FT_LOCAL_DEF( FT_UInt32 )
|
||||
af_reverse_character_map_lookup_( AF_ReverseCharacterMap map, FT_Int glyph_index, FT_Long length )
|
||||
{
|
||||
if ( map == NULL )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/* Binary search for reverse character map entry */
|
||||
FT_Int low = 0;
|
||||
FT_Int high = length - 1;
|
||||
while ( high >= low )
|
||||
{
|
||||
FT_Int mid = ( high + low ) / 2;
|
||||
FT_Int mid_glyph_index = map->entries[mid].glyph_index;
|
||||
if ( glyph_index < mid_glyph_index )
|
||||
{
|
||||
high = mid - 1;
|
||||
}
|
||||
else if ( glyph_index > mid_glyph_index )
|
||||
{
|
||||
low = mid + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return map->entries[mid].codepoint;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FT_LOCAL_DEF( FT_UInt32 )
|
||||
af_reverse_character_map_lookup( AF_ReverseCharacterMap map, FT_Int glyph_index )
|
||||
{
|
||||
return af_reverse_character_map_lookup_( map, glyph_index, map->length );
|
||||
}
|
||||
|
||||
/*prepare to add one more entry to the reverse character map
|
||||
this is a helper for af_reverse_character_map_new*/
|
||||
FT_LOCAL_DEF( FT_Error )
|
||||
af_reverse_character_map_expand( AF_ReverseCharacterMap map, FT_Long *capacity, FT_Memory memory )
|
||||
{
|
||||
FT_Error error;
|
||||
if ( map->length < *capacity )
|
||||
{
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
if ( map->length == *capacity )
|
||||
{
|
||||
FT_Long new_capacity = *capacity + *capacity / 2;
|
||||
if ( FT_RENEW_ARRAY( map->entries, map->length, new_capacity ) ) {
|
||||
return error;
|
||||
}
|
||||
*capacity = new_capacity;
|
||||
}
|
||||
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
/* qsort compare function for reverse character map */
|
||||
FT_LOCAL_DEF( FT_Int )
|
||||
af_reverse_character_map_entry_compare( const void *a, const void *b ) {
|
||||
const AF_ReverseMapEntry entry_a = *((const AF_ReverseMapEntry *)a);
|
||||
const AF_ReverseMapEntry entry_b = *((const AF_ReverseMapEntry *)b);
|
||||
return entry_a.glyph_index < entry_b.glyph_index ? -1 : entry_a.glyph_index > entry_b.glyph_index ? 1 : 0;
|
||||
}
|
||||
|
||||
#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
|
||||
/*
|
||||
Recursive algorithm to find all glyphs that a codepoint could turn into from the GSUB table.
|
||||
|
||||
buffer: a buffer containing only the input codepoint
|
||||
feature_tag_pool: the current list of features under consideration
|
||||
current_features: the current list of features being applied
|
||||
num_features: length of current_features
|
||||
result: the set of glyphs that the input codepoint can map to.
|
||||
|
||||
The algorithm works by running the hb_ot_shape_glyphs_closure function on different lists of features
|
||||
to see which features will map to glyph onto something different. This functions returns
|
||||
the result of transforming a glyph using a list of features as well as all intermediate
|
||||
forms if the glyph was transformed multiple times.
|
||||
With no features enabled, hb_ot_shape_glyphs_closure will only return the glyph given by cmap.
|
||||
This character will be the first to be placed into the results set.
|
||||
Next, the algorithm will test the same lookup enabline one feature at a time
|
||||
and see if any of those features change the result.
|
||||
If any new glyph variants are found this way, they are added to the results set
|
||||
and the algorithm will recurce, trying that feature in combination will every other feature
|
||||
to look for further glyph variants.
|
||||
|
||||
example:
|
||||
suppose we have the following features in the GSUB table:
|
||||
f1:
|
||||
a -> b
|
||||
|
||||
f2:
|
||||
b -> c
|
||||
|
||||
f3:
|
||||
d -> e
|
||||
|
||||
The algorithm will take the following steps to find all variants of "a":
|
||||
- a is added to the results
|
||||
- lookup with feature list {f1}, yielding {a, b}. b is added to the results list and the algorithm recurses
|
||||
- - lookup with feature list {f1, f2}, yielding {a, b, c}. c is added to the results list and the algorithm recurses
|
||||
- - - lookup with feature list {f1, f2, f3} yielding {a, b, c}. No new glyphs
|
||||
- - lookup with feature list {f1, f3}, yielding {a, b}. No new glyphs
|
||||
- lookup with feature list {f2}, yielding {a}. No new glyphs
|
||||
- lookup with feature list {f3}, yielding {a}. No new glyphs
|
||||
|
||||
*/
|
||||
FT_LOCAL_DEF( FT_Error )
|
||||
af_all_glyph_variants_helper( hb_font_t *font,
|
||||
hb_buffer_t *buffer,
|
||||
hb_set_t *feature_tag_pool,
|
||||
hb_feature_t *current_features,
|
||||
FT_UInt32 num_features,
|
||||
hb_set_t* result )
|
||||
{
|
||||
FT_Error error;
|
||||
/*get the list of glyphs that are created by only transforming based on the
|
||||
features in current_features*/
|
||||
hb_set_t *baseline_glyphs = NULL, *new_glyphs = NULL;
|
||||
baseline_glyphs = hb_set_create();
|
||||
if ( !hb_set_allocation_successful( baseline_glyphs ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
hb_ot_shape_glyphs_closure ( font,
|
||||
buffer,
|
||||
current_features,
|
||||
num_features,
|
||||
baseline_glyphs );
|
||||
if ( !hb_set_allocation_successful( baseline_glyphs ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
/*Add these baseline glyphs to the results. The baseline glyphs
|
||||
will contain at minimum the glyph specified by CMAP*/
|
||||
hb_set_union( result, baseline_glyphs );
|
||||
if ( !hb_set_allocation_successful( result ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto Exit;
|
||||
}
|
||||
if ( hb_set_get_population( feature_tag_pool ) == 0 )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
/*setup to try adding different features to current_features
|
||||
to see if any of them have an effect of the glyphs we get from
|
||||
hb_ot_shape_glyphs_closure*/
|
||||
current_features[num_features].start = HB_FEATURE_GLOBAL_START;
|
||||
current_features[num_features].end = HB_FEATURE_GLOBAL_END;
|
||||
current_features[num_features].value = 1; /*set the feature to enabled*/
|
||||
/*quote from docs about value attribute:
|
||||
0 disables the feature, non-zero (usually 1) enables the feature. For features implemented as lookup type 3 (like 'salt') the value is a one based index into the alternates.
|
||||
this algorithm does not handle these lookup type 3 cases fully*/
|
||||
|
||||
new_glyphs = hb_set_create();
|
||||
if ( !hb_set_allocation_successful( new_glyphs ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
hb_tag_t feature_tag = HB_SET_VALUE_INVALID;
|
||||
while ( hb_set_next( feature_tag_pool, &feature_tag ) )
|
||||
{
|
||||
hb_set_clear( new_glyphs );
|
||||
current_features[num_features].tag = feature_tag;
|
||||
hb_ot_shape_glyphs_closure ( font,
|
||||
buffer,
|
||||
current_features,
|
||||
num_features + 1,
|
||||
new_glyphs );
|
||||
if ( !hb_set_allocation_successful( new_glyphs ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
hb_set_subtract( new_glyphs, result );
|
||||
/*new_glyphs now contains all glyphs that appeared in the result
|
||||
of hb_ot_shape_glyphs_closure that haven't already been accounted for in the result.
|
||||
If this contains any glyphs, we also need to try this feature
|
||||
in combination will other features by recursing
|
||||
*/
|
||||
if ( hb_set_get_population( new_glyphs ) != 0 )
|
||||
{
|
||||
/*remove this feature from the feature pool so that
|
||||
the later recursion won't try it*/
|
||||
hb_set_del( feature_tag_pool, feature_tag );
|
||||
error = af_all_glyph_variants_helper( font,
|
||||
buffer,
|
||||
feature_tag_pool,
|
||||
current_features,
|
||||
num_features + 1,
|
||||
result );
|
||||
if ( error )
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
/*add back the feature we removed*/
|
||||
hb_set_add( feature_tag_pool, feature_tag );
|
||||
if ( !hb_set_allocation_successful( feature_tag_pool ) ) {
|
||||
return FT_Err_Out_Of_Memory;
|
||||
}
|
||||
} /* if( !hb_set_is_subset( glyphs, result ) ) */
|
||||
|
||||
} /*while ( hb_set_next( feature_tag_pool, &feature_tag ) )*/
|
||||
Exit:
|
||||
hb_set_destroy( baseline_glyphs );
|
||||
hb_set_destroy( new_glyphs );
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
FT_LOCAL_DEF( FT_Error )
|
||||
af_all_glyph_variants( FT_Face face,
|
||||
hb_font_t *hb_font,
|
||||
FT_UInt32 codepoint,
|
||||
hb_set_t* result )
|
||||
{
|
||||
FT_Memory memory = face->memory;
|
||||
FT_Error error;
|
||||
hb_face_t *hb_face = hb_font_get_face( hb_font );
|
||||
|
||||
/*The set of all feature tags in the font*/
|
||||
hb_set_t *feature_tags = hb_set_create();
|
||||
hb_set_t *type_3_lookup_indicies = hb_set_create();
|
||||
hb_buffer_t *codepoint_buffer = hb_buffer_create();
|
||||
hb_codepoint_t *type_3_alternate_glyphs_buffer;
|
||||
if ( !hb_set_allocation_successful( feature_tags ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto Exit;
|
||||
}
|
||||
if ( !hb_buffer_allocation_successful( codepoint_buffer ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto Exit;
|
||||
}
|
||||
if ( !hb_set_allocation_successful ( type_3_lookup_indicies ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
/*populate feature_tags using the output of hb_ot_layout_table_get_feature_tags*/
|
||||
FT_Bool feature_list_done = 0;
|
||||
unsigned int start_offset = 0;
|
||||
while ( !feature_list_done )
|
||||
{
|
||||
unsigned int feature_count = 20;
|
||||
hb_tag_t tags[20];
|
||||
hb_ot_layout_table_get_feature_tags ( hb_face,
|
||||
HB_OT_TAG_GSUB,
|
||||
start_offset,
|
||||
&feature_count,
|
||||
tags );
|
||||
start_offset += 20;
|
||||
if ( feature_count < 20 )
|
||||
{
|
||||
feature_list_done = 1;
|
||||
}
|
||||
for ( int i = 0; i < feature_count; i++ )
|
||||
{
|
||||
hb_set_add( feature_tags, tags[i] );
|
||||
}
|
||||
if ( !hb_set_allocation_successful( feature_tags ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
|
||||
/*make a buffer only consisting of the given codepoint*/
|
||||
if ( !hb_buffer_pre_allocate( codepoint_buffer, 1 ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto Exit;
|
||||
}
|
||||
hb_buffer_set_direction ( codepoint_buffer,
|
||||
HB_DIRECTION_LTR );
|
||||
hb_buffer_add( codepoint_buffer, codepoint, 0 );
|
||||
|
||||
/*The array of features that will be used by the recursive part
|
||||
it will have at most as many entries as there are features, so
|
||||
make the length = length of feature_tags*/
|
||||
hb_feature_t *feature_buffer;
|
||||
if (( error = FT_NEW_ARRAY( feature_buffer, hb_set_get_population( feature_tags ) ) ))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
error = af_all_glyph_variants_helper( hb_font,
|
||||
codepoint_buffer,
|
||||
feature_tags,
|
||||
feature_buffer,
|
||||
0,
|
||||
result );
|
||||
if ( error )
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the alternative glyph forms that come from features using
|
||||
type 3 lookups.
|
||||
This file from gtk was very useful in figuring out my approach:
|
||||
https://github.com/val-verde/gtk/blob/212e85e92628eda9f9650e5cc8d88c44062642ae/gtk/gtkfontchooserwidget.c#L2085
|
||||
*/
|
||||
/*list of features containing type 3 lookups:*/
|
||||
hb_tag_t feature_list[] = {
|
||||
HB_TAG('s','a','l','t'),
|
||||
HB_TAG('s','w','s','h'),
|
||||
HB_TAG('n','a','l','t'),
|
||||
HB_TAG_NONE
|
||||
};
|
||||
|
||||
|
||||
hb_ot_layout_collect_lookups ( hb_face,
|
||||
HB_OT_TAG_GSUB,
|
||||
NULL,
|
||||
NULL,
|
||||
feature_list,
|
||||
type_3_lookup_indicies);
|
||||
|
||||
if ( !hb_set_allocation_successful( type_3_lookup_indicies ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if (( error = FT_NEW_ARRAY( type_3_alternate_glyphs_buffer, 100 ) ))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
hb_codepoint_t lookup_index = HB_SET_VALUE_INVALID;
|
||||
FT_UInt base_glyph_index = FT_Get_Char_Index( face, codepoint );
|
||||
if ( base_glyph_index != 0 ) {
|
||||
while ( hb_set_next( type_3_lookup_indicies, &lookup_index ) )
|
||||
{
|
||||
unsigned alternate_count = 100;
|
||||
|
||||
hb_ot_layout_lookup_get_glyph_alternates
|
||||
( hb_face,
|
||||
lookup_index,
|
||||
base_glyph_index,
|
||||
0,
|
||||
&alternate_count,
|
||||
type_3_alternate_glyphs_buffer );
|
||||
|
||||
for ( unsigned i = 0; i < alternate_count; i++ )
|
||||
{
|
||||
hb_set_add( result, type_3_alternate_glyphs_buffer[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Exit:
|
||||
hb_set_destroy( feature_tags );
|
||||
hb_buffer_destroy( codepoint_buffer );
|
||||
FT_FREE( feature_buffer );
|
||||
FT_FREE( type_3_alternate_glyphs_buffer );
|
||||
return error;
|
||||
}
|
||||
#endif /*FT_CONFIG_OPTION_USE_HARFBUZZ*/
|
||||
|
||||
FT_LOCAL_DEF( FT_Error )
|
||||
af_reverse_character_map_new( AF_ReverseCharacterMap *map, AF_FaceGlobals globals )
|
||||
{
|
||||
FT_Face face = globals->face;
|
||||
FT_Memory memory = face->memory;
|
||||
/* Search for a unicode charmap */
|
||||
/* If there isn't one, create a blank map */
|
||||
|
||||
FT_TRACE4(( "af_reverse_character_map_new: building reverse character map\n" ));
|
||||
|
||||
FT_Error error;
|
||||
/* backup face->charmap because find_unicode_charmap sets it */
|
||||
FT_CharMap old_charmap = face->charmap;
|
||||
if ( ( error = find_unicode_charmap( face ) ) )
|
||||
{
|
||||
*map = NULL;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if ( FT_NEW( *map ) )
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
FT_Long capacity = 10;
|
||||
( *map )->length = 0;
|
||||
|
||||
if ( FT_NEW_ARRAY( ( *map )->entries, capacity) )
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
|
||||
#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
|
||||
hb_font_t *hb_font = globals->hb_font;
|
||||
/*hb_face_t *hb_face = hb_font_get_face( hb_font );*/
|
||||
hb_set_t *result_set = hb_set_create();
|
||||
if ( !hb_set_allocation_successful( result_set ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto harfbuzz_path_Exit;
|
||||
}
|
||||
|
||||
/*find alll glyph variants of the codepoints, then make an entry from
|
||||
the glyph to the codepoint for each one*/
|
||||
for ( FT_Long i = 0; i < AF_ADJUSTMENT_DATABASE_LENGTH; i++ )
|
||||
{
|
||||
FT_UInt32 codepoint = adjustment_database[i].codepoint;
|
||||
|
||||
error = af_all_glyph_variants( face,
|
||||
hb_font,
|
||||
codepoint,
|
||||
result_set );
|
||||
if ( error )
|
||||
{
|
||||
goto harfbuzz_path_Exit;
|
||||
}
|
||||
|
||||
hb_codepoint_t glyph = HB_SET_VALUE_INVALID;
|
||||
while ( hb_set_next( result_set, &glyph ) )
|
||||
{
|
||||
error = af_reverse_character_map_expand( *map, &capacity, memory );
|
||||
if ( error ) {
|
||||
goto harfbuzz_path_Exit;
|
||||
}
|
||||
|
||||
FT_Long insert_point = ( *map )->length;
|
||||
( *map )->length++;
|
||||
( *map )->entries[insert_point].glyph_index = glyph;
|
||||
( *map )->entries[insert_point].codepoint = codepoint;
|
||||
}
|
||||
|
||||
hb_set_clear( result_set );
|
||||
}
|
||||
harfbuzz_path_Exit:
|
||||
hb_set_destroy( result_set );
|
||||
if ( error )
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
#else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
|
||||
#ifdef FT_DEBUG_LEVEL_TRACE
|
||||
int failed_lookups = 0;
|
||||
#endif
|
||||
for ( FT_Int i = 0; i < AF_ADJUSTMENT_DATABASE_LENGTH; i++ )
|
||||
{
|
||||
FT_UInt32 codepoint = adjustment_database[i].codepoint;
|
||||
FT_Int glyph = FT_Get_Char_Index( face, codepoint );
|
||||
if ( glyph == 0 )
|
||||
{
|
||||
#ifdef FT_DEBUG_LEVEL_TRACE
|
||||
failed_lookups++;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
error = af_reverse_character_map_expand( *map, &capacity, memory );
|
||||
if ( error ) {
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
( *map )->length++;
|
||||
( *map )->entries[i].glyph_index = glyph;
|
||||
( *map )->entries[i].codepoint = codepoint;
|
||||
}
|
||||
|
||||
#endif /*FT_CONFIG_OPTION_USE_HARFBUZZ*/
|
||||
|
||||
ft_qsort(
|
||||
( *map )->entries,
|
||||
( *map )->length,
|
||||
sizeof( AF_ReverseMapEntry ),
|
||||
af_reverse_character_map_entry_compare
|
||||
);
|
||||
|
||||
FT_TRACE4(( " reverse character map built successfully"\
|
||||
" with %d entries\n", (*map)->length ));
|
||||
|
||||
Exit:
|
||||
face->charmap = old_charmap;
|
||||
if ( error )
|
||||
{
|
||||
FT_TRACE4(( " error while building reverse character map. Using blank map.\n" ));
|
||||
if ( *map )
|
||||
{
|
||||
FT_FREE( ( *map )->entries );
|
||||
}
|
||||
FT_FREE( *map );
|
||||
return error;
|
||||
}
|
||||
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
FT_LOCAL_DEF( FT_Error )
|
||||
af_reverse_character_map_done( AF_ReverseCharacterMap map, FT_Memory memory ) {
|
||||
FT_FREE( map->entries );
|
||||
return FT_Err_Ok;
|
||||
}
|
52
src/autofit/afadjust.h
Normal file
52
src/autofit/afadjust.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef AFADJUST_H_
|
||||
#define AFADJUST_H_
|
||||
#include <freetype/fttypes.h>
|
||||
#include "aftypes.h"
|
||||
#include "afglobal.h"
|
||||
|
||||
FT_BEGIN_HEADER
|
||||
|
||||
/*The type of adjustment that should be done to prevent cases where 2 parts of a character*/
|
||||
/*stacked vertically merge, even though they should be separate*/
|
||||
typedef enum AF_VerticalSeparationAdjustmentType_
|
||||
{
|
||||
/*This means that the hinter should find the topmost contour and push it up until its lowest point is 1 pixel*/
|
||||
/*above the highest point not part of that contour.*/
|
||||
AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP,
|
||||
|
||||
/*This is the opposite direction. The hinter should find the bottommost contour*/
|
||||
/*And push it down until there is a 1 pixel gap*/
|
||||
AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN,
|
||||
|
||||
AF_VERTICAL_ADJUSTMENT_NONE
|
||||
|
||||
/*others will be needed, such as umlats, where there are 2 contours which should be moved together*/
|
||||
} AF_VerticalSeparationAdjustmentType;
|
||||
|
||||
typedef struct AF_AdjustmentDatabaseEntry_
|
||||
{
|
||||
FT_UInt32 codepoint;
|
||||
AF_VerticalSeparationAdjustmentType vertical_separation_adjustment_type;
|
||||
FT_Bool apply_tilde;
|
||||
} AF_AdjustmentDatabaseEntry;
|
||||
|
||||
FT_LOCAL(AF_VerticalSeparationAdjustmentType)
|
||||
af_lookup_vertical_seperation_type( AF_ReverseCharacterMap map, FT_Int glyph_index );
|
||||
|
||||
FT_LOCAL( FT_Bool )
|
||||
af_lookup_tilde_correction_type( AF_ReverseCharacterMap map, FT_Int glyph_index );
|
||||
|
||||
FT_LOCAL( FT_UInt32 )
|
||||
af_reverse_character_map_lookup( AF_ReverseCharacterMap map, FT_Int glyph_index );
|
||||
|
||||
/*allocate and populate the reverse character map, using the character map within the face*/
|
||||
FT_LOCAL( FT_Error )
|
||||
af_reverse_character_map_new( AF_ReverseCharacterMap *map, AF_FaceGlobals globals );
|
||||
|
||||
/*free the reverse character map*/
|
||||
FT_LOCAL( FT_Error )
|
||||
af_reverse_character_map_done( AF_ReverseCharacterMap map, FT_Memory memory );
|
||||
|
||||
FT_END_HEADER
|
||||
|
||||
#endif
|
@ -22,6 +22,7 @@
|
||||
#include "afglobal.h"
|
||||
#include "aflatin.h"
|
||||
#include "aferrors.h"
|
||||
#include "afadjust.h"
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
@ -227,7 +228,6 @@
|
||||
dummy->units_per_em / 100 );
|
||||
axis->width_count = num_widths;
|
||||
}
|
||||
|
||||
Exit:
|
||||
for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
|
||||
{
|
||||
@ -1153,6 +1153,8 @@
|
||||
goto Exit;
|
||||
}
|
||||
af_latin_metrics_check_digits( metrics, face );
|
||||
|
||||
af_reverse_character_map_new( &metrics->root.reverse_charmap, metrics->root.globals );
|
||||
}
|
||||
|
||||
Exit:
|
||||
@ -1502,6 +1504,11 @@
|
||||
af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
|
||||
}
|
||||
|
||||
FT_CALLBACK_DEF( void )
|
||||
af_latin_metrics_done( AF_StyleMetrics metrics_ ) {
|
||||
AF_LatinMetrics metrics = (AF_LatinMetrics)metrics_;
|
||||
af_reverse_character_map_done( metrics->root.reverse_charmap, metrics->root.globals->face->memory );
|
||||
}
|
||||
|
||||
/* Extract standard_width from writing system/script specific */
|
||||
/* metrics class. */
|
||||
@ -2738,6 +2745,516 @@
|
||||
}
|
||||
|
||||
|
||||
#undef FT_COMPONENT
|
||||
#define FT_COMPONENT afadjust
|
||||
|
||||
static void
|
||||
af_move_contour_vertically( AF_Point contour,
|
||||
FT_Int movement )
|
||||
{
|
||||
AF_Point point = contour;
|
||||
AF_Point first_point = point;
|
||||
if ( point != NULL )
|
||||
{
|
||||
do
|
||||
{
|
||||
point->y += movement;
|
||||
point = point->next;
|
||||
} while ( point != first_point );
|
||||
}
|
||||
}
|
||||
|
||||
static FT_Int
|
||||
af_find_highest_contour( AF_GlyphHints hints ) {
|
||||
FT_Int highest_contour = -1;
|
||||
FT_Pos highest_min_y = 0;
|
||||
FT_Pos current_min_y = 0;
|
||||
|
||||
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ )
|
||||
{
|
||||
AF_Point point = hints->contours[contour];
|
||||
AF_Point first_point = point;
|
||||
if ( point == NULL )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
current_min_y = point->y;
|
||||
|
||||
do
|
||||
{
|
||||
if ( point->y < current_min_y )
|
||||
{
|
||||
current_min_y = point->y;
|
||||
}
|
||||
point = point->next;
|
||||
} while ( point != first_point );
|
||||
|
||||
if ( highest_contour == -1 || current_min_y > highest_min_y )
|
||||
{
|
||||
highest_min_y = current_min_y;
|
||||
highest_contour = contour;
|
||||
}
|
||||
}
|
||||
|
||||
return highest_contour;
|
||||
}
|
||||
|
||||
static void
|
||||
af_remove_segments_containing_point(AF_GlyphHints hints, AF_Point point)
|
||||
{
|
||||
AF_AxisHints axis = &hints->axis[AF_DIMENSION_VERT];
|
||||
AF_Segment segments = axis->segments;
|
||||
for ( FT_Int i = 0; i < axis->num_segments; i++ )
|
||||
{
|
||||
AF_Segment seg = &segments[i];
|
||||
FT_Bool remove = 0;
|
||||
AF_Point p = seg->first;
|
||||
while ( 1 )
|
||||
{
|
||||
if ( p == point )
|
||||
{
|
||||
remove = 1;
|
||||
break;
|
||||
}
|
||||
if (p == seg->last) {
|
||||
break;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
if ( remove )
|
||||
{
|
||||
/* first, check the first and last fields of the edge */
|
||||
AF_Edge edge = seg->edge;
|
||||
if ( edge->first == seg && edge->last == seg )
|
||||
{
|
||||
/* The edge only consists of the segment to be removed. remove the edge*/
|
||||
*edge = axis->edges[--axis->num_edges];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( edge->first == seg )
|
||||
{
|
||||
edge->first = seg->edge_next;
|
||||
}
|
||||
if ( edge->last == seg )
|
||||
{
|
||||
edge->last = edge->first;
|
||||
while ( edge->last->edge_next != seg )
|
||||
{
|
||||
edge->last = edge->last->edge_next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now, delete the segment */
|
||||
*seg = axis->segments[--axis->num_segments];
|
||||
|
||||
i--; /* we have to check the new segment at this position */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*remove all segments containing points on the tilde contour*/
|
||||
static void
|
||||
af_latin_remove_tilde_points_from_edges( AF_GlyphHints hints,
|
||||
FT_Int glyph_index )
|
||||
{
|
||||
FT_Int highest_contour = af_find_highest_contour(hints);
|
||||
AF_Point first_point = hints->contours[highest_contour];
|
||||
|
||||
/* search for any curve tips that are on a y extrema, and delete any
|
||||
segments that contain this point.*/
|
||||
AF_Point p = first_point;
|
||||
|
||||
do
|
||||
{
|
||||
p = p->next;
|
||||
if ( /*!(p->flags & AF_FLAG_CONTROL)
|
||||
&& p->prev->y == p->y && p->next->y == p->y
|
||||
&& p->prev->flags & AF_FLAG_CONTROL
|
||||
&& p->next->flags & AF_FLAG_CONTROL*/ 1 )
|
||||
{
|
||||
FT_TRACE4(("%p", p));
|
||||
af_remove_segments_containing_point( hints, p );
|
||||
}
|
||||
} while ( p != first_point );
|
||||
}
|
||||
/*
|
||||
The tilde unflatenning algorithm sometimes goes too far and makes an
|
||||
unusually high tilde, where decreasing the ppem will increase the height
|
||||
instead of a steady decrease in height as less pixels are used.
|
||||
|
||||
The n tilde on times new roman with forced autofitting on,
|
||||
16.5-18 ppem font size exhibits this behaviour.
|
||||
*/
|
||||
void
|
||||
af_latin_stretch_tildes( AF_GlyphHints hints,
|
||||
FT_Int glyph_index )
|
||||
{
|
||||
FT_Int highest_contour = af_find_highest_contour( hints );
|
||||
AF_Point p = hints->contours[highest_contour];
|
||||
AF_Point first_point = p;
|
||||
|
||||
FT_Pos min_y, max_y;
|
||||
min_y = max_y = p->y;
|
||||
|
||||
FT_Short min_fy, max_fy;
|
||||
min_fy = max_fy = p->fy;
|
||||
|
||||
do
|
||||
{
|
||||
p = p->next;
|
||||
if ( p->y < min_y )
|
||||
{
|
||||
min_y = p->y;
|
||||
}
|
||||
if ( p->y > max_y )
|
||||
{
|
||||
max_y = p->y;
|
||||
}
|
||||
|
||||
if ( p->fy < min_fy )
|
||||
{
|
||||
min_fy = p->fy;
|
||||
}
|
||||
|
||||
if ( p->fy > max_fy )
|
||||
{
|
||||
max_fy = p->fy;
|
||||
}
|
||||
|
||||
}
|
||||
while ( p != first_point );
|
||||
|
||||
FT_Pos min_measurement = 32000;
|
||||
FT_UInt measurements_taken = 0;
|
||||
|
||||
do
|
||||
{
|
||||
p = p->next;
|
||||
if ( !(p->flags & AF_FLAG_CONTROL)
|
||||
&& p->prev->y == p->y && p->next->y == p->y
|
||||
&& p->y != min_y && p->y != max_y
|
||||
&& p->prev->flags & AF_FLAG_CONTROL
|
||||
&& p->next->flags & AF_FLAG_CONTROL )
|
||||
{
|
||||
/* This point could be a candidate. Find the next and previous on-curve */
|
||||
/* points, and make sure they are both either above or below the point, */
|
||||
/* Then make the measurement */
|
||||
AF_Point prevOn = p->prev;
|
||||
AF_Point nextOn = p->next;
|
||||
while ( prevOn->flags & AF_FLAG_CONTROL )
|
||||
{
|
||||
prevOn = prevOn->prev;
|
||||
}
|
||||
while ( nextOn->flags & AF_FLAG_CONTROL )
|
||||
{
|
||||
nextOn = nextOn->next;
|
||||
}
|
||||
FT_Pos measurement;
|
||||
if ( nextOn->y > p->y && prevOn->y > p->y )
|
||||
{
|
||||
measurement = p->y - min_y;
|
||||
}
|
||||
else if ( nextOn->y < p->y && prevOn->y < p->y )
|
||||
{
|
||||
measurement = max_y - p->y;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (measurement < min_measurement)
|
||||
{
|
||||
min_measurement = measurement;
|
||||
}
|
||||
measurements_taken++;
|
||||
}
|
||||
|
||||
}
|
||||
while ( p != first_point );
|
||||
|
||||
FT_Pos height = max_y - min_y;
|
||||
|
||||
FT_Pos target_height = min_measurement + 64;
|
||||
if ( height >= target_height )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
p = first_point;
|
||||
do
|
||||
{
|
||||
p = p->next;
|
||||
p->y = ((p->y - min_y) * target_height / height) + min_y;
|
||||
p->fy = ((p->fy - min_fy) * target_height / height) + min_fy;
|
||||
p->oy = p->y;
|
||||
if ( !(p->flags & AF_FLAG_CONTROL) )
|
||||
p->flags |= AF_FLAG_TOUCH_Y;
|
||||
}
|
||||
while ( p != first_point );
|
||||
|
||||
FT_Pos new_min_y, new_max_y;
|
||||
new_min_y = new_max_y = first_point->y;
|
||||
p = first_point;
|
||||
do {
|
||||
p = p->next;
|
||||
if ( p->y < new_min_y )
|
||||
{
|
||||
new_min_y = p->y;
|
||||
}
|
||||
if ( p->y > new_max_y )
|
||||
{
|
||||
new_max_y = p->y;
|
||||
}
|
||||
}
|
||||
while ( p != first_point );
|
||||
}
|
||||
|
||||
/*True if the given contour overlaps horizontally with the bounding box
|
||||
Of all other contours combined.
|
||||
This is a helper for af_glyph_hints_apply_vertical_separation_adjustments */
|
||||
FT_Bool
|
||||
af_check_contour_horizontal_overlap( AF_GlyphHints hints,
|
||||
FT_Int contour_index )
|
||||
{
|
||||
FT_Pos contour_max_x = -32000;
|
||||
FT_Pos contour_min_x = 32000;
|
||||
FT_Pos others_max_x = -32000;
|
||||
FT_Pos others_min_x = 32000;
|
||||
|
||||
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ )
|
||||
{
|
||||
AF_Point first_point = hints->contours[contour];
|
||||
AF_Point p = first_point;
|
||||
|
||||
do
|
||||
{
|
||||
p = p->next;
|
||||
if ( contour == contour_index )
|
||||
{
|
||||
if ( p->x < contour_min_x )
|
||||
{
|
||||
contour_min_x = p->x;
|
||||
}
|
||||
if ( p->x > contour_max_x )
|
||||
{
|
||||
contour_max_x = p->x;
|
||||
}
|
||||
}
|
||||
else /* ( contour != contour_index ) */
|
||||
{
|
||||
if ( p->x < others_min_x )
|
||||
{
|
||||
others_min_x = p->x;
|
||||
}
|
||||
if ( p->x > others_max_x )
|
||||
{
|
||||
others_max_x = p->x;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (p != first_point);
|
||||
}
|
||||
|
||||
FT_Bool horizontal_overlap =
|
||||
(others_min_x <= contour_max_x && contour_max_x <= others_max_x) ||
|
||||
(others_min_x <= contour_min_x && contour_min_x <= others_max_x) ||
|
||||
(contour_max_x >= others_max_x && contour_min_x <= others_min_x);
|
||||
|
||||
return horizontal_overlap;
|
||||
}
|
||||
|
||||
void
|
||||
af_glyph_hints_apply_vertical_separation_adjustments( AF_GlyphHints hints,
|
||||
AF_Dimension dim,
|
||||
FT_Int glyph_index,
|
||||
AF_ReverseCharacterMap reverse_charmap )
|
||||
{
|
||||
if ( dim != AF_DIMENSION_VERT )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( af_lookup_vertical_seperation_type( reverse_charmap, glyph_index ) == AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP
|
||||
&& hints->num_contours >= 2 )
|
||||
{
|
||||
FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments: Applying vertical adjustment: AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP\n" ));
|
||||
|
||||
/* Figure out which contout is the higher one by finding the one */
|
||||
/* with the highest minimum y value */
|
||||
|
||||
FT_Int highest_contour = -1;
|
||||
FT_Pos highest_min_y = 0;
|
||||
FT_Pos current_min_y = 0;
|
||||
|
||||
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ )
|
||||
{
|
||||
AF_Point point = hints->contours[contour];
|
||||
AF_Point first_point = point;
|
||||
if ( point == NULL )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
current_min_y = point->y;
|
||||
|
||||
do
|
||||
{
|
||||
if ( point->y < current_min_y )
|
||||
{
|
||||
current_min_y = point->y;
|
||||
}
|
||||
point = point->next;
|
||||
} while ( point != first_point );
|
||||
|
||||
if ( highest_contour == -1 || current_min_y > highest_min_y )
|
||||
{
|
||||
highest_min_y = current_min_y;
|
||||
highest_contour = contour;
|
||||
}
|
||||
}
|
||||
|
||||
/* check for a horizontal overtap between the top contour and the rest */
|
||||
/* if there is no overlap, do not adjust. */
|
||||
|
||||
FT_Bool horizontal_overlap = af_check_contour_horizontal_overlap( hints, highest_contour );
|
||||
if (!horizontal_overlap) {
|
||||
FT_TRACE4(( " Top contour does not horizontally overlap with other contours. Skipping adjustment.\n" ));
|
||||
return;
|
||||
}
|
||||
|
||||
/* If there are any contours that have a maximum y coordinate */
|
||||
/* greater or equal to the minimum y coordinate of the previously found highest*/
|
||||
/* contour, bump the high contour up until the distance is one pixel */
|
||||
|
||||
FT_Int adjustment_amount = 0;
|
||||
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ )
|
||||
{
|
||||
if ( contour == highest_contour )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
AF_Point point = hints->contours[contour];
|
||||
AF_Point first_point = point;
|
||||
if ( point == NULL )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
FT_Pos max_y = point->y;
|
||||
|
||||
do
|
||||
{
|
||||
if ( point->y > max_y )
|
||||
{
|
||||
max_y = point->y;
|
||||
}
|
||||
point = point->next;
|
||||
} while ( point != first_point );
|
||||
|
||||
if ( max_y >= highest_min_y - 64 )
|
||||
{
|
||||
adjustment_amount = 64 - ( highest_min_y - max_y );
|
||||
}
|
||||
}
|
||||
|
||||
if ( adjustment_amount > 64 )
|
||||
{
|
||||
FT_TRACE4(( " Calculated adjustment amount %d was more than threshold of 64. Not adjusting\n", adjustment_amount ));
|
||||
}
|
||||
else if ( adjustment_amount > 0 )
|
||||
{
|
||||
FT_TRACE4(( " Pushing top contour %d units up\n", adjustment_amount ));
|
||||
af_move_contour_vertically(hints->contours[highest_contour], adjustment_amount);
|
||||
}
|
||||
} else if ( af_lookup_vertical_seperation_type( reverse_charmap, glyph_index ) == AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN
|
||||
&& hints->num_contours >= 2 )
|
||||
{
|
||||
FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments: Applying vertical adjustment: AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN\n" ));
|
||||
|
||||
/*Find lowest contour*/
|
||||
FT_Int lowest_contour = -1;
|
||||
FT_Pos lowest_max_y = 0;
|
||||
FT_Pos current_max_y = 0;
|
||||
|
||||
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ )
|
||||
{
|
||||
AF_Point point = hints->contours[contour];
|
||||
AF_Point first_point = point;
|
||||
if ( point == NULL )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
current_max_y = point->y;
|
||||
|
||||
do
|
||||
{
|
||||
if ( point->y > current_max_y )
|
||||
{
|
||||
current_max_y = point->y;
|
||||
}
|
||||
point = point->next;
|
||||
} while ( point != first_point );
|
||||
|
||||
if ( lowest_contour == -1 || current_max_y < lowest_max_y )
|
||||
{
|
||||
lowest_max_y = current_max_y;
|
||||
lowest_contour = contour;
|
||||
}
|
||||
}
|
||||
|
||||
FT_Int adjustment_amount = 0;
|
||||
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ )
|
||||
{
|
||||
if ( contour == lowest_contour )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
AF_Point point = hints->contours[contour];
|
||||
AF_Point first_point = point;
|
||||
if ( point == NULL )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
FT_Pos min_y = point->y;
|
||||
|
||||
do
|
||||
{
|
||||
if ( point->y < min_y )
|
||||
{
|
||||
min_y = point->y;
|
||||
}
|
||||
point = point->next;
|
||||
} while ( point != first_point );
|
||||
|
||||
if ( min_y <= lowest_max_y - 64 )
|
||||
{
|
||||
adjustment_amount = 64 - ( min_y - lowest_max_y );
|
||||
}
|
||||
|
||||
if ( adjustment_amount > 64 )
|
||||
{
|
||||
FT_TRACE4(( " Calculated adjustment amount %d was more than threshold of 64. Not adjusting\n", adjustment_amount ));
|
||||
}
|
||||
else if ( adjustment_amount > 0 )
|
||||
{
|
||||
FT_TRACE4(( " Pushing bottom contour %d units down\n", adjustment_amount ));
|
||||
af_move_contour_vertically(hints->contours[lowest_contour], -adjustment_amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments: No vertical adjustment needed\n" ));
|
||||
}
|
||||
}
|
||||
|
||||
#undef FT_COMPONENT
|
||||
#define FT_COMPONENT aflatin
|
||||
|
||||
|
||||
|
||||
/* Compute the snapped width of a given stem, ignoring very thin ones. */
|
||||
/* There is a lot of voodoo in this function; changing the hard-coded */
|
||||
/* parameters influence the whole hinting process. */
|
||||
@ -3547,9 +4064,7 @@
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Apply the complete hinting algorithm to a latin glyph. */
|
||||
|
||||
static FT_Error
|
||||
af_latin_hints_apply( FT_UInt glyph_index,
|
||||
AF_GlyphHints hints,
|
||||
@ -3582,11 +4097,18 @@
|
||||
|
||||
if ( AF_HINTS_DO_VERTICAL( hints ) )
|
||||
{
|
||||
FT_Bool is_tilde = af_lookup_tilde_correction_type( metrics->root.reverse_charmap, glyph_index );
|
||||
if ( is_tilde ) {
|
||||
af_latin_stretch_tildes( hints, glyph_index );
|
||||
}
|
||||
axis = &metrics->axis[AF_DIMENSION_VERT];
|
||||
error = af_latin_hints_detect_features( hints,
|
||||
axis->width_count,
|
||||
axis->widths,
|
||||
AF_DIMENSION_VERT );
|
||||
if ( is_tilde ) {
|
||||
af_latin_remove_tilde_points_from_edges( hints, glyph_index );
|
||||
}
|
||||
if ( error )
|
||||
goto Exit;
|
||||
|
||||
@ -3605,6 +4127,7 @@
|
||||
af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim );
|
||||
af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
|
||||
af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
|
||||
af_glyph_hints_apply_vertical_separation_adjustments(hints, (AF_Dimension) dim, glyph_index, metrics->root.reverse_charmap);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3633,12 +4156,11 @@
|
||||
|
||||
(AF_WritingSystem_InitMetricsFunc) af_latin_metrics_init, /* style_metrics_init */
|
||||
(AF_WritingSystem_ScaleMetricsFunc)af_latin_metrics_scale, /* style_metrics_scale */
|
||||
(AF_WritingSystem_DoneMetricsFunc) NULL, /* style_metrics_done */
|
||||
(AF_WritingSystem_DoneMetricsFunc) af_latin_metrics_done, /* style_metrics_done */
|
||||
(AF_WritingSystem_GetStdWidthsFunc)af_latin_get_standard_widths, /* style_metrics_getstdw */
|
||||
|
||||
(AF_WritingSystem_InitHintsFunc) af_latin_hints_init, /* style_hints_init */
|
||||
(AF_WritingSystem_ApplyHintsFunc) af_latin_hints_apply /* style_hints_apply */
|
||||
)
|
||||
|
||||
|
||||
/* END */
|
||||
|
@ -410,6 +410,10 @@ extern void* af_debug_hints_;
|
||||
/* specific to writing systems derive their structures from it, for */
|
||||
/* example `AF_LatinMetrics'. */
|
||||
|
||||
struct AF_ReverseCharacterMap_;
|
||||
|
||||
typedef struct AF_ReverseCharacterMap_ *AF_ReverseCharacterMap;
|
||||
|
||||
typedef struct AF_StyleMetricsRec_
|
||||
{
|
||||
AF_StyleClass style_class;
|
||||
@ -417,6 +421,7 @@ extern void* af_debug_hints_;
|
||||
FT_Bool digits_have_same_width;
|
||||
|
||||
AF_FaceGlobals globals; /* to access properties */
|
||||
AF_ReverseCharacterMap reverse_charmap;
|
||||
|
||||
} AF_StyleMetricsRec;
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "afmodule.c"
|
||||
#include "afranges.c"
|
||||
#include "afshaper.c"
|
||||
#include "afadjust.c"
|
||||
|
||||
|
||||
/* END */
|
||||
|
@ -1358,22 +1358,7 @@
|
||||
driver );
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* @Function:
|
||||
* find_unicode_charmap
|
||||
*
|
||||
* @Description:
|
||||
* This function finds a Unicode charmap, if there is one.
|
||||
* And if there is more than one, it tries to favour the more
|
||||
* extensive one, i.e., one that supports UCS-4 against those which
|
||||
* are limited to the BMP (said UCS-2 encoding.)
|
||||
*
|
||||
* This function is called from open_face() (just below), and also
|
||||
* from FT_Select_Charmap( ..., FT_ENCODING_UNICODE ).
|
||||
*/
|
||||
static FT_Error
|
||||
FT_Error
|
||||
find_unicode_charmap( FT_Face face )
|
||||
{
|
||||
FT_CharMap* first;
|
||||
|
Loading…
Reference in New Issue
Block a user