Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
fa64df531f | ||
|
6bc1ac488b | ||
|
faae5501ca | ||
|
adf5129ad9 | ||
|
1e5e193adb | ||
|
c70b3d9484 | ||
|
fe31700701 | ||
|
efa555bfe6 |
15
docs/CHANGES
15
docs/CHANGES
@ -7,6 +7,21 @@ CHANGES BETWEEN 2.13.2 and 2.13.3 (2024-Mmm-DD)
|
||||
usage. It is also an additional means to protect against
|
||||
malformed input.
|
||||
|
||||
- The auto-hinter got new abilities.
|
||||
|
||||
. It can now better separate accents from base characters at small
|
||||
sizes by artificially moving accents up if necessary.
|
||||
|
||||
. Tilde characters get vertically stretched at small sizes so that
|
||||
they don't degenerate to horizontal lines.
|
||||
|
||||
Both features use a database, which currently has entries for
|
||||
Unicode characters lower than U+0180. FreeType needs to access or
|
||||
construct a proper Unicode character map from a given font to make
|
||||
this work.
|
||||
|
||||
This was Craig White's GSoC 2023 project.
|
||||
|
||||
|
||||
II. IMPORTANT BUG FIXES
|
||||
|
||||
|
@ -275,6 +275,28 @@ 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,
|
||||
|
@ -159,6 +159,7 @@ FT_TRACE_DEF( gxvprop )
|
||||
FT_TRACE_DEF( gxvtrak )
|
||||
|
||||
/* autofit components */
|
||||
FT_TRACE_DEF( afadjust )
|
||||
FT_TRACE_DEF( afcjk )
|
||||
FT_TRACE_DEF( afglobal )
|
||||
FT_TRACE_DEF( afhints )
|
||||
|
843
src/autofit/afadjust.c
Normal file
843
src/autofit/afadjust.c
Normal file
@ -0,0 +1,843 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* afadjust.c
|
||||
*
|
||||
* Auto-fitter routines to adjust components based on charcode (body).
|
||||
*
|
||||
* Copyright (C) 2023-2024 by
|
||||
* David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
*
|
||||
* Written by Craig White <gerzytet@gmail.com>.
|
||||
*
|
||||
* This file is part of the FreeType project, and may only be used,
|
||||
* modified, and distributed under the terms of the FreeType project
|
||||
* license, LICENSE.TXT. By continuing to use, modify, or distribute
|
||||
* this file you indicate that you have read the license and
|
||||
* understand and accept it fully.
|
||||
*
|
||||
*/
|
||||
|
||||
#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
|
||||
|
||||
|
||||
/*
|
||||
All entries in this list must be sorted by ascending Unicode code
|
||||
points. The table entries are 3 numbers consisting of:
|
||||
|
||||
- Unicode code point.
|
||||
- The vertical adjustment type. This should be one of the enum
|
||||
constants in `AF_VerticalSeparationAdjustmentType`.
|
||||
- Value 1 if the topmost contour is a tilde and should be prevented from
|
||||
flattening, and 0 otherwise.
|
||||
*/
|
||||
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 }, /* ¡ */
|
||||
{ 0xBF, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ¿ */
|
||||
|
||||
{ 0xC0, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* À */
|
||||
{ 0xC1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Á */
|
||||
{ 0xC2, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Â */
|
||||
{ 0xC3, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1 }, /* Ã */
|
||||
{ 0xC8, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* È */
|
||||
{ 0xC9, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* É */
|
||||
{ 0xCA, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ê */
|
||||
{ 0xCC, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ì */
|
||||
{ 0xCD, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Í */
|
||||
{ 0xCE, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Î */
|
||||
|
||||
{ 0xD1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1 }, /* Ñ */
|
||||
{ 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, 1 }, /* Õ */
|
||||
{ 0xD9, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ù */
|
||||
{ 0xDA, 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 }, /* ã */
|
||||
{ 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, 1 }, /* ñ */
|
||||
{ 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 }, /* õ */
|
||||
{ 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 }, /* Ć */
|
||||
{ 0x107, 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 }, /* ė */
|
||||
{ 0x11A, 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, 1 }, /* Ĩ */
|
||||
{ 0x129, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1 }, /* ĩ */
|
||||
{ 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 }, /* İ */
|
||||
{ 0x133, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ij */
|
||||
{ 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 }, /* Ň */
|
||||
{ 0x148, 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, 1 }, /* Ũ */
|
||||
{ 0x169, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1 }, /* ũ */
|
||||
{ 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 } /* ž */
|
||||
};
|
||||
|
||||
|
||||
/* Helper function: get the adjustment database entry for a codepoint. */
|
||||
static 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;
|
||||
}
|
||||
|
||||
|
||||
/* `qsort` compare function for reverse character map. */
|
||||
FT_COMPARE_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;
|
||||
}
|
||||
|
||||
|
||||
static FT_UInt32
|
||||
af_reverse_character_map_lookup( AF_ReverseCharacterMap map,
|
||||
FT_Int glyph_index )
|
||||
{
|
||||
FT_Int low, high;
|
||||
FT_Long length;
|
||||
|
||||
|
||||
if ( !map )
|
||||
return 0;
|
||||
|
||||
length = map->length;
|
||||
|
||||
/* Binary search for reverse character map entry. */
|
||||
low = 0;
|
||||
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( AF_VerticalSeparationAdjustmentType )
|
||||
af_lookup_vertical_separation_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 )
|
||||
return AF_VERTICAL_ADJUSTMENT_NONE;
|
||||
|
||||
return entry->vertical_separation_adjustment_type;
|
||||
}
|
||||
|
||||
|
||||
/* Return 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 )
|
||||
return 0;
|
||||
|
||||
return entry->apply_tilde;
|
||||
}
|
||||
|
||||
|
||||
/* Prepare to add one more entry to the reverse character map. */
|
||||
/* This is a helper function for `af_reverse_character_map_new`. */
|
||||
static 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;
|
||||
}
|
||||
|
||||
|
||||
#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
|
||||
|
||||
/*
|
||||
Recursive algorithm to find all glyphs that a code point could turn into
|
||||
from the 'GSUB' table.
|
||||
|
||||
buffer: a buffer containing only the input code point
|
||||
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 code point can map to
|
||||
|
||||
The algorithm works by running the `hb_ot_shape_glyphs_closure` function
|
||||
on different lists of features to check which features map the glyph onto
|
||||
something different. This function 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` only returns the
|
||||
glyph given by the cmap. This character is the first to be placed into
|
||||
the result set.
|
||||
|
||||
Next, the algorithm tests the same lookup enabling one feature at a time
|
||||
and check whether any of those features change the result.
|
||||
|
||||
If any new glyph variants are found this way, they are added to the
|
||||
result set and the algorithm recurses, trying that feature in combination
|
||||
with 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 takes the following steps to find all variants of 'a'.
|
||||
|
||||
- Add 'a' to the result.
|
||||
- Look up with feature list {f1}, yielding {a, b}.
|
||||
=> Add 'b' to the result list, recurse.
|
||||
- Look up with feature list {f1, f2}, yielding {a, b, c}.
|
||||
=> Add 'c' to the result list, recurse.
|
||||
- Look up with feature list {f1, f2, f3}, yielding {a, b, c}.
|
||||
=> No new glyphs.
|
||||
- Look up with feature list {f1, f3}, yielding {a, b}.
|
||||
=> No new glyphs.
|
||||
- Look up with feature list {f2}, yielding {a}.
|
||||
=> No new glyphs.
|
||||
- Look up with feature list {f3}, yielding {a}.
|
||||
=> No new glyphs.
|
||||
*/
|
||||
static 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;
|
||||
|
||||
hb_set_t *baseline_glyphs = NULL;
|
||||
hb_set_t *new_glyphs = NULL;
|
||||
|
||||
hb_tag_t feature_tag;
|
||||
|
||||
|
||||
/* Get the list of glyphs that are created by only transforming, */
|
||||
/* based on the features in `current_features`. */
|
||||
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 result. The baseline glyph set */
|
||||
/* contains at least the glyph specified by the 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 ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
/* Prepare for adding different features to `current_features` to */
|
||||
/* check whether 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; /* enable the feature */
|
||||
|
||||
/*
|
||||
Quote from the HarfBuzz documentation about the `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;
|
||||
}
|
||||
|
||||
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 with other features */
|
||||
/* by recursing. */
|
||||
if ( hb_set_get_population( new_glyphs ) != 0 )
|
||||
{
|
||||
/* Remove this feature from the feature pool temporarily so that */
|
||||
/* a 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;
|
||||
}
|
||||
}
|
||||
|
||||
Exit:
|
||||
hb_set_destroy( baseline_glyphs );
|
||||
hb_set_destroy( new_glyphs );
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
|
||||
static FT_Error
|
||||
af_all_glyph_variants( FT_Face face,
|
||||
hb_font_t *hb_font,
|
||||
FT_UInt32 codepoint,
|
||||
hb_set_t* result )
|
||||
{
|
||||
FT_Error error;
|
||||
|
||||
FT_Memory memory = face->memory;
|
||||
hb_face_t *hb_face = hb_font_get_face( hb_font );
|
||||
|
||||
FT_Bool feature_list_done;
|
||||
unsigned int start_offset;
|
||||
|
||||
/* The set of all feature tags in the font. */
|
||||
hb_set_t *feature_tags = hb_set_create();
|
||||
hb_set_t *type_3_lookup_indices = hb_set_create();
|
||||
hb_buffer_t *codepoint_buffer = hb_buffer_create();
|
||||
hb_codepoint_t *type_3_alternate_glyphs_buffer;
|
||||
|
||||
hb_feature_t *feature_buffer;
|
||||
|
||||
/* 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_codepoint_t lookup_index;
|
||||
FT_UInt base_glyph_index;
|
||||
|
||||
|
||||
if ( !hb_set_allocation_successful( feature_tags ) ||
|
||||
!hb_buffer_allocation_successful( codepoint_buffer ) ||
|
||||
!hb_set_allocation_successful ( type_3_lookup_indices ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
/* Populate `feature_tags` using the output of */
|
||||
/* `hb_ot_layout_table_get_feature_tags`. */
|
||||
feature_list_done = 0;
|
||||
start_offset = 0;
|
||||
|
||||
while ( !feature_list_done )
|
||||
{
|
||||
unsigned int feature_count = 20;
|
||||
hb_tag_t tags[20];
|
||||
|
||||
unsigned int i;
|
||||
|
||||
|
||||
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 ( 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 code point. */
|
||||
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 is used by the recursive part has at */
|
||||
/* most as many entries as there are features, so make the length */
|
||||
/* equal to the length of `feature_tags`. */
|
||||
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://gitlab.gnome.org/GNOME/gtk/-/blob/40f20fee3d8468749dfb233a6f95921c765c1163/gtk/gtkfontchooserwidget.c#L2100
|
||||
*/
|
||||
hb_ot_layout_collect_lookups( hb_face,
|
||||
HB_OT_TAG_GSUB,
|
||||
NULL,
|
||||
NULL,
|
||||
feature_list,
|
||||
type_3_lookup_indices );
|
||||
if ( !hb_set_allocation_successful( type_3_lookup_indices ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
#define MAX_ALTERNATES 100 /* ad-hoc value */
|
||||
|
||||
if ( ( error = FT_NEW_ARRAY( type_3_alternate_glyphs_buffer,
|
||||
MAX_ALTERNATES ) ) )
|
||||
goto Exit;
|
||||
|
||||
lookup_index = HB_SET_VALUE_INVALID;
|
||||
base_glyph_index = FT_Get_Char_Index( face, codepoint );
|
||||
|
||||
if ( base_glyph_index )
|
||||
{
|
||||
while ( hb_set_next( type_3_lookup_indices, &lookup_index ) )
|
||||
{
|
||||
unsigned alternate_count = MAX_ALTERNATES;
|
||||
unsigned i;
|
||||
|
||||
|
||||
hb_ot_layout_lookup_get_glyph_alternates(
|
||||
hb_face,
|
||||
lookup_index,
|
||||
base_glyph_index,
|
||||
0,
|
||||
&alternate_count,
|
||||
type_3_alternate_glyphs_buffer );
|
||||
|
||||
for ( 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_Error error;
|
||||
|
||||
FT_Face face = globals->face;
|
||||
FT_Memory memory = face->memory;
|
||||
|
||||
FT_CharMap old_charmap;
|
||||
|
||||
FT_Long capacity;
|
||||
|
||||
|
||||
/* 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" ));
|
||||
|
||||
/* Back up `face->charmap` because `find_unicode_charmap` sets it. */
|
||||
old_charmap = face->charmap;
|
||||
|
||||
if ( ( error = find_unicode_charmap( face ) ) )
|
||||
goto Exit;
|
||||
|
||||
*map = NULL;
|
||||
if ( FT_NEW( *map ) )
|
||||
goto Exit;
|
||||
|
||||
/* Start with a capacity of 10 entries. */
|
||||
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_set_t *result_set = hb_set_create();
|
||||
|
||||
FT_ULong i;
|
||||
|
||||
|
||||
if ( !hb_set_allocation_successful( result_set ) )
|
||||
{
|
||||
error = FT_Err_Out_Of_Memory;
|
||||
goto harfbuzz_path_Exit;
|
||||
}
|
||||
|
||||
/* Find all glyph variants of the code points, then make an entry */
|
||||
/* from the glyph to the code point for each one. */
|
||||
for ( i = 0; i < AF_ADJUSTMENT_DATABASE_LENGTH; i++ )
|
||||
{
|
||||
FT_UInt32 codepoint = adjustment_database[i].codepoint;
|
||||
|
||||
hb_codepoint_t glyph;
|
||||
|
||||
|
||||
error = af_all_glyph_variants( face,
|
||||
hb_font,
|
||||
codepoint,
|
||||
result_set );
|
||||
if ( error )
|
||||
goto harfbuzz_path_Exit;
|
||||
|
||||
glyph = HB_SET_VALUE_INVALID;
|
||||
|
||||
while ( hb_set_next( result_set, &glyph ) )
|
||||
{
|
||||
FT_Long insert_point;
|
||||
|
||||
|
||||
error = af_reverse_character_map_expand( *map, &capacity, memory );
|
||||
if ( error )
|
||||
goto harfbuzz_path_Exit;
|
||||
|
||||
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 */
|
||||
|
||||
{
|
||||
FT_UInt i;
|
||||
#ifdef FT_DEBUG_LEVEL_TRACE
|
||||
int failed_lookups = 0;
|
||||
#endif
|
||||
|
||||
|
||||
for ( 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 %ld 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 );
|
||||
*map = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
|
||||
FT_LOCAL_DEF( FT_Error )
|
||||
af_reverse_character_map_done( AF_ReverseCharacterMap map,
|
||||
FT_Memory memory )
|
||||
{
|
||||
if ( map )
|
||||
FT_FREE( map->entries );
|
||||
FT_FREE( map );
|
||||
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
|
||||
/* END */
|
86
src/autofit/afadjust.h
Normal file
86
src/autofit/afadjust.h
Normal file
@ -0,0 +1,86 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* afadjust.h
|
||||
*
|
||||
* Auto-fitter routines to adjust components based on charcode (header).
|
||||
*
|
||||
* Copyright (C) 2023-2024 by
|
||||
* David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
*
|
||||
* Written by Craig White <gerzytet@gmail.com>.
|
||||
*
|
||||
* This file is part of the FreeType project, and may only be used,
|
||||
* modified, and distributed under the terms of the FreeType project
|
||||
* license, LICENSE.TXT. By continuing to use, modify, or distribute
|
||||
* this file you indicate that you have read the license and
|
||||
* understand and accept it fully.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef AFADJUST_H_
|
||||
#define AFADJUST_H_
|
||||
|
||||
#include <freetype/fttypes.h>
|
||||
|
||||
#include "afglobal.h"
|
||||
#include "aftypes.h"
|
||||
|
||||
|
||||
FT_BEGIN_HEADER
|
||||
|
||||
|
||||
/* The type of adjustment that should be done to prevent cases where */
|
||||
/* two 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 one 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 one-pixel gap. */
|
||||
AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN,
|
||||
|
||||
AF_VERTICAL_ADJUSTMENT_NONE
|
||||
|
||||
} 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_separation_type( AF_ReverseCharacterMap map,
|
||||
FT_Int glyph_index );
|
||||
|
||||
FT_LOCAL( FT_Bool )
|
||||
af_lookup_tilde_correction_type( 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 /* AFADJUST_H_ */
|
||||
|
||||
|
||||
/* END */
|
@ -22,6 +22,7 @@
|
||||
#include "afglobal.h"
|
||||
#include "aflatin.h"
|
||||
#include "aferrors.h"
|
||||
#include "afadjust.h"
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
@ -1155,6 +1156,9 @@
|
||||
af_latin_metrics_check_digits( metrics, face );
|
||||
}
|
||||
|
||||
af_reverse_character_map_new( &metrics->root.reverse_charmap,
|
||||
metrics->root.globals );
|
||||
|
||||
Exit:
|
||||
face->charmap = oldmap;
|
||||
return error;
|
||||
@ -1484,6 +1488,17 @@
|
||||
}
|
||||
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
|
||||
/* Scale global values in both directions. */
|
||||
|
||||
FT_LOCAL_DEF( void )
|
||||
@ -2737,6 +2752,633 @@
|
||||
}
|
||||
|
||||
|
||||
#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 )
|
||||
{
|
||||
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;
|
||||
|
||||
FT_Int contour;
|
||||
|
||||
|
||||
for ( contour = 0; contour < hints->num_contours; contour++ )
|
||||
{
|
||||
AF_Point point = hints->contours[contour];
|
||||
AF_Point first_point = point;
|
||||
|
||||
|
||||
if ( !point )
|
||||
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;
|
||||
|
||||
FT_UInt i;
|
||||
|
||||
|
||||
for ( i = 0; i < axis->num_segments; i++ )
|
||||
{
|
||||
AF_Segment seg = &segments[i];
|
||||
AF_Point p = seg->first;
|
||||
|
||||
FT_Bool remove = 0;
|
||||
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
if ( p == point )
|
||||
{
|
||||
remove = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( p == seg->last )
|
||||
break;
|
||||
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
if ( remove )
|
||||
{
|
||||
/* First, check the first and last segment 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 highest_contour = af_find_highest_contour( hints );
|
||||
AF_Point first_point = hints->contours[highest_contour];
|
||||
|
||||
AF_Point p = first_point;
|
||||
|
||||
|
||||
do
|
||||
{
|
||||
p = p->next;
|
||||
af_remove_segments_containing_point( hints, p );
|
||||
|
||||
} while ( p != first_point );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
The tilde-unflattening 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.
|
||||
|
||||
For example, the 'n tilde' glyph from 'Times New Roman', with forced
|
||||
autofitting on, exhibits this behaviour in the font size range 16.5 to
|
||||
18 ppem.
|
||||
*/
|
||||
static void
|
||||
af_latin_stretch_tildes( AF_GlyphHints hints )
|
||||
{
|
||||
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;
|
||||
FT_Short min_fy, max_fy;
|
||||
|
||||
FT_Pos min_measurement;
|
||||
|
||||
FT_Pos height;
|
||||
FT_Pos target_height;
|
||||
|
||||
|
||||
min_y = max_y = p->y;
|
||||
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 );
|
||||
|
||||
min_measurement = 32000;
|
||||
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;
|
||||
|
||||
FT_Pos measurement;
|
||||
|
||||
|
||||
while ( prevOn->flags & AF_FLAG_CONTROL )
|
||||
prevOn = prevOn->prev;
|
||||
while ( nextOn->flags & AF_FLAG_CONTROL )
|
||||
nextOn = nextOn->next;
|
||||
|
||||
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;
|
||||
}
|
||||
} while ( p != first_point );
|
||||
height = max_y - min_y;
|
||||
|
||||
if ( height < 256 ) {
|
||||
/* touch all points */
|
||||
p = first_point;
|
||||
do
|
||||
{
|
||||
p = p->next;
|
||||
if ( !( p->flags & AF_FLAG_CONTROL ) )
|
||||
p->flags |= AF_FLAG_TOUCH_Y;
|
||||
|
||||
} while ( p != first_point );
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
} while ( p != first_point );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
As part of `af_latin_stretch_tildes`, all points in the tilde are marked
|
||||
as touched, so the existing grid fitting will leave the tilde misaligned
|
||||
with the grid.
|
||||
|
||||
This function moves the tilde contour down to be grid-fitted. We assume
|
||||
that if moving the tilde down would cause it to touch or overlap another
|
||||
countour, the vertical adjustment step will fix it.
|
||||
|
||||
Because the vertical adjustment step comes after all other grid-fitting
|
||||
steps, the top edge of the contour under the tilde is usually aligned
|
||||
with a horizontal grid line. The vertical gap enforced by the vertical
|
||||
adjustment is exactly one pixel, so if the top edge of the contour below
|
||||
the tilde is on a grid line, the resulting tilde contour will also be
|
||||
grid-aligned.
|
||||
|
||||
But in cases where the gap is already big enough so that the vertical
|
||||
adjustment does nothing, this function ensures that even without the
|
||||
intervention of the vertical adjustment step, the tilde will be
|
||||
grid-aligned.
|
||||
*/
|
||||
static void
|
||||
af_latin_align_tildes( AF_GlyphHints hints )
|
||||
{
|
||||
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;
|
||||
FT_Pos delta;
|
||||
|
||||
|
||||
do
|
||||
{
|
||||
p = p->next;
|
||||
if ( p->y < min_y )
|
||||
min_y = p->y;
|
||||
if ( p->y > max_y )
|
||||
max_y = p->y;
|
||||
|
||||
} while ( p != first_point );
|
||||
|
||||
// If the tilde is less than 2 pixels tall, snap the center of it to
|
||||
// the grid instead of the bottom to improve readability
|
||||
//
|
||||
FT_Pos min_y_rounded = FT_PIX_ROUND( min_y );
|
||||
delta = min_y_rounded - min_y;
|
||||
FT_Pos height = max_y - min_y;
|
||||
if ( height < 64*3 ) {
|
||||
delta += ( FT_PIX_ROUND( height ) - height ) / 2;
|
||||
}
|
||||
|
||||
p = first_point;
|
||||
do
|
||||
{
|
||||
p = p->next;
|
||||
p->y += delta;
|
||||
|
||||
} while ( p != first_point );
|
||||
}
|
||||
|
||||
|
||||
/* Return 1 if the given contour overlaps horizontally with the bounding */
|
||||
/* box of all other contours combined. This is a helper for function */
|
||||
/* `af_glyph_hints_apply_vertical_separation_adjustments`. */
|
||||
static 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;
|
||||
|
||||
FT_Int contour;
|
||||
|
||||
FT_Bool horizontal_overlap;
|
||||
|
||||
|
||||
for ( 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
|
||||
{
|
||||
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 );
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
af_glyph_hints_apply_vertical_separation_adjustments(
|
||||
AF_GlyphHints hints,
|
||||
AF_Dimension dim,
|
||||
FT_Int glyph_index,
|
||||
AF_ReverseCharacterMap reverse_charmap )
|
||||
{
|
||||
FT_TRACE4(( "Entering"
|
||||
" af_glyph_hints_apply_vertical_separation_adjustments\n" ));
|
||||
|
||||
if ( dim != AF_DIMENSION_VERT )
|
||||
return;
|
||||
|
||||
if ( af_lookup_vertical_separation_type( reverse_charmap,
|
||||
glyph_index ) ==
|
||||
AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP &&
|
||||
hints->num_contours >= 2 )
|
||||
{
|
||||
FT_Int highest_contour = -1;
|
||||
FT_Pos highest_min_y = 0;
|
||||
FT_Pos current_min_y = 0;
|
||||
|
||||
FT_Int contour;
|
||||
FT_Bool horizontal_overlap;
|
||||
FT_Int adjustment_amount;
|
||||
|
||||
FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments:\n"
|
||||
" Applying vertical adjustment:"
|
||||
" AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP\n" ));
|
||||
|
||||
/* Figure out which contour is the higher one by finding the one */
|
||||
/* with the highest minimum y value. */
|
||||
for ( contour = 0; contour < hints->num_contours; contour++ )
|
||||
{
|
||||
AF_Point point = hints->contours[contour];
|
||||
AF_Point first_point = point;
|
||||
|
||||
|
||||
if ( !point )
|
||||
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 overlap between the top contour and the */
|
||||
/* rest. If there is no overlap, do not adjust. */
|
||||
horizontal_overlap =
|
||||
af_check_contour_horizontal_overlap( hints, highest_contour );
|
||||
if ( !horizontal_overlap )
|
||||
{
|
||||
FT_TRACE4(( " Top contour does not horizontally overlap"
|
||||
" with other contours.\n"
|
||||
" Skipping adjustment.\n" ));
|
||||
return;
|
||||
}
|
||||
|
||||
AF_Point point = hints->contours[highest_contour];
|
||||
AF_Point first_point = point;
|
||||
FT_Pos highest_max_y = point->y;
|
||||
do
|
||||
{
|
||||
if ( point->y > highest_max_y )
|
||||
{
|
||||
highest_max_y = point->y;
|
||||
}
|
||||
} while ( point != first_point );
|
||||
|
||||
/* If there are any contours that have a maximum y coordinate */
|
||||
/* greater than or equal to the minimum y coordinate of the */
|
||||
/* previously found highest contour, bump the high contour up until */
|
||||
/* the distance is one pixel. */
|
||||
adjustment_amount = 0;
|
||||
|
||||
for ( contour = 0; contour < hints->num_contours; contour++ )
|
||||
{
|
||||
AF_Point point;
|
||||
AF_Point first_point;
|
||||
|
||||
FT_Pos max_y;
|
||||
|
||||
|
||||
if ( contour == highest_contour )
|
||||
continue;
|
||||
|
||||
point = hints->contours[contour];
|
||||
first_point = point;
|
||||
if ( !point )
|
||||
continue;
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
FT_TRACE4(( " Calculated adjustment amount 1: %d\n",
|
||||
adjustment_amount ));
|
||||
|
||||
|
||||
if ( adjustment_amount > 0 && ( highest_max_y - highest_min_y ) < 128 )
|
||||
{
|
||||
adjustment_amount += ( 128 - ( highest_max_y - highest_min_y ) ) / 2;
|
||||
FT_TRACE4(( " Additional push: %d\n",
|
||||
( 128 - ( highest_max_y - highest_min_y ) ) / 2 ));
|
||||
}
|
||||
|
||||
FT_TRACE4(( " Calculated adjustment amount: %d\n",
|
||||
adjustment_amount ));
|
||||
|
||||
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_separation_type( reverse_charmap,
|
||||
glyph_index ) ==
|
||||
AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN &&
|
||||
hints->num_contours >= 2 )
|
||||
{
|
||||
FT_Int lowest_contour = -1;
|
||||
FT_Pos lowest_max_y = 0;
|
||||
FT_Pos current_max_y = 0;
|
||||
|
||||
FT_Int contour;
|
||||
FT_Int adjustment_amount;
|
||||
|
||||
|
||||
FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments:\n"
|
||||
" Applying vertical adjustment:"
|
||||
" AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN\n" ));
|
||||
|
||||
/* Find lowest contour. */
|
||||
for ( contour = 0; contour < hints->num_contours; contour++ )
|
||||
{
|
||||
AF_Point point = hints->contours[contour];
|
||||
AF_Point first_point = point;
|
||||
|
||||
|
||||
if ( !point )
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
adjustment_amount = 0;
|
||||
|
||||
for ( contour = 0; contour < hints->num_contours; contour++ )
|
||||
{
|
||||
AF_Point point;
|
||||
AF_Point first_point;
|
||||
|
||||
FT_Pos min_y;
|
||||
|
||||
|
||||
if ( contour == lowest_contour )
|
||||
continue;
|
||||
|
||||
point = hints->contours[contour];
|
||||
first_point = point;
|
||||
if ( !point )
|
||||
continue;
|
||||
|
||||
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:\n"
|
||||
" No vertical adjustment needed\n" ));
|
||||
|
||||
FT_TRACE4(( "Exiting"
|
||||
" af_glyph_hints_apply_vertical_separation_adjustments\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,6 +4189,48 @@
|
||||
}
|
||||
|
||||
|
||||
#ifdef FT_DEBUG_LEVEL_TRACE
|
||||
/* Print the height of the topmost contour for debugging purposes. */
|
||||
/* TODO: remove this once the tilde unflattening works. */
|
||||
static void
|
||||
af_latin_trace_height( FT_UInt num,
|
||||
AF_GlyphHints hints )
|
||||
{
|
||||
AF_Point p = hints->contours[af_find_highest_contour(hints)];
|
||||
AF_Point first_point = p;
|
||||
|
||||
FT_Pos min_y, max_y;
|
||||
|
||||
|
||||
min_y = max_y = p->y;
|
||||
|
||||
do
|
||||
{
|
||||
p = p->next;
|
||||
if ( !(p->flags & AF_FLAG_CONTROL) )
|
||||
{
|
||||
if ( p->y < min_y )
|
||||
min_y = p->y;
|
||||
if ( p->y > max_y )
|
||||
max_y = p->y;
|
||||
}
|
||||
|
||||
} while ( p != first_point );
|
||||
|
||||
FT_TRACE4(( "height %d: %ld\n", num, max_y - min_y ));
|
||||
FT_TRACE4(( "min y %d: %ld\n", num, min_y ));
|
||||
}
|
||||
#else
|
||||
static void
|
||||
af_latin_trace_height( FT_UInt num,
|
||||
AF_GlyphHints hints )
|
||||
{
|
||||
FT_UNUSED( num );
|
||||
FT_UNUSED( hints );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Apply the complete hinting algorithm to a latin glyph. */
|
||||
|
||||
static FT_Error
|
||||
@ -3581,11 +4265,27 @@
|
||||
|
||||
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_trace_height(0, hints );
|
||||
af_latin_stretch_tildes( hints );
|
||||
af_latin_trace_height(33, hints );
|
||||
af_latin_align_tildes( hints );
|
||||
af_latin_trace_height(1, hints );
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
if ( error )
|
||||
goto Exit;
|
||||
|
||||
@ -3601,9 +4301,19 @@
|
||||
( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) ) )
|
||||
{
|
||||
af_latin_hint_edges( hints, (AF_Dimension)dim );
|
||||
af_latin_trace_height(2, hints );
|
||||
af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim );
|
||||
af_latin_trace_height(3, hints );
|
||||
af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
|
||||
af_latin_trace_height(4, hints );
|
||||
af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
|
||||
af_latin_trace_height(5, hints );
|
||||
af_glyph_hints_apply_vertical_separation_adjustments(
|
||||
hints,
|
||||
(AF_Dimension)dim,
|
||||
glyph_index,
|
||||
metrics->root.reverse_charmap );
|
||||
af_latin_trace_height(99, hints );
|
||||
}
|
||||
}
|
||||
|
||||
@ -3632,7 +4342,7 @@
|
||||
|
||||
(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 */
|
||||
|
@ -406,6 +406,27 @@ extern void* af_debug_hints_;
|
||||
|
||||
typedef struct AF_FaceGlobalsRec_* AF_FaceGlobals;
|
||||
|
||||
|
||||
/* Store a mapping from glyphs to unicode codepoints. */
|
||||
/* See `afadjust.c` for details. */
|
||||
typedef struct AF_ReverseMapEntry_
|
||||
{
|
||||
FT_Int glyph_index;
|
||||
FT_UInt32 codepoint;
|
||||
|
||||
} AF_ReverseMapEntry;
|
||||
|
||||
|
||||
typedef struct AF_ReverseCharacterMapRec_
|
||||
{
|
||||
FT_Long length;
|
||||
AF_ReverseMapEntry *entries;
|
||||
|
||||
} AF_ReverseCharacterMapRec;
|
||||
|
||||
typedef struct AF_ReverseCharacterMapRec_* AF_ReverseCharacterMap;
|
||||
|
||||
|
||||
/* This is the main structure that combines everything. Autofit modules */
|
||||
/* specific to writing systems derive their structures from it, for */
|
||||
/* example `AF_LatinMetrics'. */
|
||||
@ -418,6 +439,8 @@ extern void* af_debug_hints_;
|
||||
|
||||
AF_FaceGlobals globals; /* to access properties */
|
||||
|
||||
AF_ReverseCharacterMap reverse_charmap;
|
||||
|
||||
} AF_StyleMetricsRec;
|
||||
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#define FT_MAKE_OPTION_SINGLE_OBJECT
|
||||
|
||||
#include "ft-hb.c"
|
||||
#include "afadjust.c"
|
||||
#include "afblue.c"
|
||||
#include "afcjk.c"
|
||||
#include "afdummy.c"
|
||||
|
@ -28,7 +28,8 @@ AUTOF_COMPILE := $(CC) $(ANSIFLAGS) \
|
||||
|
||||
# AUTOF driver sources (i.e., C files)
|
||||
#
|
||||
AUTOF_DRV_SRC := $(AUTOF_DIR)/afblue.c \
|
||||
AUTOF_DRV_SRC := $(AUTOF_DIR)/afadjust.c \
|
||||
$(AUTOF_DIR)/afblue.c \
|
||||
$(AUTOF_DIR)/afcjk.c \
|
||||
$(AUTOF_DIR)/afdummy.c \
|
||||
$(AUTOF_DIR)/afglobal.c \
|
||||
|
@ -1359,21 +1359,9 @@
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* @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
|
||||
/* documentation is in ftobjs.h */
|
||||
|
||||
FT_BASE_DEF( FT_Error )
|
||||
find_unicode_charmap( FT_Face face )
|
||||
{
|
||||
FT_CharMap* first;
|
||||
|
Loading…
Reference in New Issue
Block a user