Font outline decomposition fixes
This commit is contained in:
parent
f3fa7febeb
commit
da65fe0469
|
@ -38,7 +38,8 @@ OUTLINE_DECOMPOSER::OUTLINE_DECOMPOSER( FT_Outline& aOutline ) :
|
||||||
|
|
||||||
static VECTOR2D toVector2D( const FT_Vector* aFreeTypeVector )
|
static VECTOR2D toVector2D( const FT_Vector* aFreeTypeVector )
|
||||||
{
|
{
|
||||||
return VECTOR2D( aFreeTypeVector->x, aFreeTypeVector->y );
|
return VECTOR2D( aFreeTypeVector->x * GLYPH_SIZE_SCALER,
|
||||||
|
aFreeTypeVector->y * GLYPH_SIZE_SCALER );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,8 +63,7 @@ int OUTLINE_DECOMPOSER::moveTo( const FT_Vector* aEndPoint, void* aCallbackData
|
||||||
{
|
{
|
||||||
OUTLINE_DECOMPOSER* decomposer = static_cast<OUTLINE_DECOMPOSER*>( aCallbackData );
|
OUTLINE_DECOMPOSER* decomposer = static_cast<OUTLINE_DECOMPOSER*>( aCallbackData );
|
||||||
|
|
||||||
decomposer->m_lastEndPoint.x = aEndPoint->x;
|
decomposer->m_lastEndPoint = toVector2D( aEndPoint );
|
||||||
decomposer->m_lastEndPoint.y = aEndPoint->y;
|
|
||||||
|
|
||||||
decomposer->newContour();
|
decomposer->newContour();
|
||||||
decomposer->addContourPoint( decomposer->m_lastEndPoint );
|
decomposer->addContourPoint( decomposer->m_lastEndPoint );
|
||||||
|
@ -76,8 +76,7 @@ int OUTLINE_DECOMPOSER::lineTo( const FT_Vector* aEndPoint, void* aCallbackData
|
||||||
{
|
{
|
||||||
OUTLINE_DECOMPOSER* decomposer = static_cast<OUTLINE_DECOMPOSER*>( aCallbackData );
|
OUTLINE_DECOMPOSER* decomposer = static_cast<OUTLINE_DECOMPOSER*>( aCallbackData );
|
||||||
|
|
||||||
decomposer->m_lastEndPoint.x = aEndPoint->x;
|
decomposer->m_lastEndPoint = toVector2D( aEndPoint );
|
||||||
decomposer->m_lastEndPoint.y = aEndPoint->y;
|
|
||||||
|
|
||||||
decomposer->addContourPoint( decomposer->m_lastEndPoint );
|
decomposer->addContourPoint( decomposer->m_lastEndPoint );
|
||||||
|
|
||||||
|
@ -112,12 +111,10 @@ int OUTLINE_DECOMPOSER::cubicTo( const FT_Vector* aFirstControlPoint,
|
||||||
|
|
||||||
GLYPH_POINTS result;
|
GLYPH_POINTS result;
|
||||||
decomposer->approximateBezierCurve( result, bezier );
|
decomposer->approximateBezierCurve( result, bezier );
|
||||||
|
|
||||||
for( const VECTOR2D& p : result )
|
for( const VECTOR2D& p : result )
|
||||||
decomposer->addContourPoint( p );
|
decomposer->addContourPoint( p );
|
||||||
|
|
||||||
decomposer->m_lastEndPoint.x = aEndPoint->x;
|
decomposer->m_lastEndPoint = toVector2D( aEndPoint );
|
||||||
decomposer->m_lastEndPoint.y = aEndPoint->y;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -177,14 +174,12 @@ bool OUTLINE_DECOMPOSER::approximateCubicBezierCurve( GLYPH_POINTS& aResul
|
||||||
{
|
{
|
||||||
wxASSERT( aCubicBezier.size() == 4 );
|
wxASSERT( aCubicBezier.size() == 4 );
|
||||||
|
|
||||||
|
// minimumSegmentLength defines the "smoothness" of the
|
||||||
|
// curve-to-straight-segments conversion: the larger, the coarser
|
||||||
// TODO: find out what the minimum segment length should really be!
|
// TODO: find out what the minimum segment length should really be!
|
||||||
static const int minimumSegmentLength = 50;
|
constexpr int minimumSegmentLength = 10;
|
||||||
GLYPH_POINTS tmp;
|
BEZIER_POLY converter( aCubicBezier );
|
||||||
BEZIER_POLY converter( aCubicBezier );
|
converter.GetPoly( aResult, minimumSegmentLength );
|
||||||
converter.GetPoly( tmp, minimumSegmentLength );
|
|
||||||
|
|
||||||
for( unsigned int i = 0; i < tmp.size(); i++ )
|
|
||||||
aResult.push_back( tmp[i] );
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -242,19 +237,8 @@ int OUTLINE_DECOMPOSER::winding( const GLYPH_POINTS& aContour ) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int i_prev_vertex;
|
unsigned int i_prev_vertex = ( i_lowest_vertex + aContour.size() - 1 ) % aContour.size();
|
||||||
unsigned int i_next_vertex;
|
unsigned int i_next_vertex = ( i_lowest_vertex + 1 ) % aContour.size();
|
||||||
|
|
||||||
// TODO: this should be done with modulo arithmetic for clarity
|
|
||||||
if( i_lowest_vertex == 0 )
|
|
||||||
i_prev_vertex = aContour.size() - 1;
|
|
||||||
else
|
|
||||||
i_prev_vertex = i_lowest_vertex - 1;
|
|
||||||
|
|
||||||
if( i_lowest_vertex == aContour.size() - 1 )
|
|
||||||
i_next_vertex = 0;
|
|
||||||
else
|
|
||||||
i_next_vertex = i_lowest_vertex + 1;
|
|
||||||
|
|
||||||
const VECTOR2D& lowest = aContour[i_lowest_vertex];
|
const VECTOR2D& lowest = aContour[i_lowest_vertex];
|
||||||
VECTOR2D prev( aContour[i_prev_vertex] );
|
VECTOR2D prev( aContour[i_prev_vertex] );
|
||||||
|
@ -268,7 +252,7 @@ int OUTLINE_DECOMPOSER::winding( const GLYPH_POINTS& aContour ) const
|
||||||
|
|
||||||
if( i_prev_vertex == i_lowest_vertex )
|
if( i_prev_vertex == i_lowest_vertex )
|
||||||
{
|
{
|
||||||
// ERROR: degenerate contour (all points are equal)
|
// ERROR: degenerate contour (all points are colinear with equal Y coordinate)
|
||||||
// TODO: signal error
|
// TODO: signal error
|
||||||
// for now let's just return something at random
|
// for now let's just return something at random
|
||||||
return cw;
|
return cw;
|
||||||
|
|
|
@ -41,21 +41,6 @@
|
||||||
using namespace KIFONT;
|
using namespace KIFONT;
|
||||||
|
|
||||||
|
|
||||||
// The height of the KiCad stroke font is the distance between stroke endpoints for a vertical
|
|
||||||
// line of cap-height. So the cap-height of the font is actually stroke-width taller than its
|
|
||||||
// height.
|
|
||||||
// Outline fonts are normally scaled on full-height (including ascenders and descenders), so we
|
|
||||||
// need to compensate to keep them from being much smaller than their stroked counterparts.
|
|
||||||
constexpr double OUTLINE_FONT_SIZE_COMPENSATION = 1.4;
|
|
||||||
|
|
||||||
// The KiCad stroke font uses a subscript/superscript size ratio of 0.7. This ratio is also
|
|
||||||
// commonly used in LaTeX, but fonts with designed-in subscript and superscript glyphs are more
|
|
||||||
// likely to use 0.58.
|
|
||||||
// For auto-generated subscript and superscript glyphs in outline fonts we split the difference
|
|
||||||
// with 0.64.
|
|
||||||
static constexpr double SUBSCRIPT_SUPERSCRIPT_SIZE = 0.64;
|
|
||||||
|
|
||||||
|
|
||||||
FT_Library OUTLINE_FONT::m_freeType = nullptr;
|
FT_Library OUTLINE_FONT::m_freeType = nullptr;
|
||||||
|
|
||||||
OUTLINE_FONT::OUTLINE_FONT() :
|
OUTLINE_FONT::OUTLINE_FONT() :
|
||||||
|
@ -91,9 +76,6 @@ OUTLINE_FONT* OUTLINE_FONT::LoadFont( const wxString& aFontName, bool aBold, boo
|
||||||
|
|
||||||
FT_Error OUTLINE_FONT::loadFace( const wxString& aFontFileName )
|
FT_Error OUTLINE_FONT::loadFace( const wxString& aFontFileName )
|
||||||
{
|
{
|
||||||
m_faceScaler = m_faceSize * 64;
|
|
||||||
int subscriptFaceScaler = KiROUND( m_faceSize * 64 * SUBSCRIPT_SUPERSCRIPT_SIZE );
|
|
||||||
|
|
||||||
// TODO: check that going from wxString to char* with UTF-8
|
// TODO: check that going from wxString to char* with UTF-8
|
||||||
// conversion for filename makes sense on any/all platforms
|
// conversion for filename makes sense on any/all platforms
|
||||||
FT_Error e = FT_New_Face( m_freeType, aFontFileName.mb_str( wxConvUTF8 ), 0, &m_face );
|
FT_Error e = FT_New_Face( m_freeType, aFontFileName.mb_str( wxConvUTF8 ), 0, &m_face );
|
||||||
|
@ -101,15 +83,13 @@ FT_Error OUTLINE_FONT::loadFace( const wxString& aFontFileName )
|
||||||
if( !e )
|
if( !e )
|
||||||
{
|
{
|
||||||
FT_Select_Charmap( m_face, FT_Encoding::FT_ENCODING_UNICODE );
|
FT_Select_Charmap( m_face, FT_Encoding::FT_ENCODING_UNICODE );
|
||||||
FT_Set_Char_Size( m_face, 0, m_faceScaler, 0, 0 );
|
// params:
|
||||||
|
// m_face = handle to face object
|
||||||
e = FT_New_Face( m_freeType, aFontFileName.mb_str( wxConvUTF8 ), 0, &m_subscriptFace );
|
// 0 = char width in 1/64th of points ( 0 = same as char height )
|
||||||
|
// faceSize() = char height in 1/64th of points
|
||||||
if( !e )
|
// GLYPH_RESOLUTION = horizontal device resolution (288dpi, 4x default)
|
||||||
{
|
// 0 = vertical device resolution ( 0 = same as horizontal )
|
||||||
FT_Select_Charmap( m_subscriptFace, FT_Encoding::FT_ENCODING_UNICODE );
|
FT_Set_Char_Size( m_face, 0, faceSize(), GLYPH_RESOLUTION, 0 );
|
||||||
FT_Set_Char_Size( m_subscriptFace, 0, subscriptFaceScaler, 0, 0 );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
|
@ -125,7 +105,7 @@ double OUTLINE_FONT::ComputeOverbarVerticalPosition( double aGlyphHeight ) const
|
||||||
// The overbar on actual text is positioned above the bounding box of the glyphs. However,
|
// The overbar on actual text is positioned above the bounding box of the glyphs. However,
|
||||||
// that's expensive to calculate so we use an estimation here (as this is only used for
|
// that's expensive to calculate so we use an estimation here (as this is only used for
|
||||||
// calculating bounding boxes).
|
// calculating bounding boxes).
|
||||||
return aGlyphHeight * OUTLINE_FONT_SIZE_COMPENSATION;
|
return aGlyphHeight * m_outlineFontSizeCompensation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,7 +120,13 @@ double OUTLINE_FONT::GetInterline( double aGlyphHeight, double aLineSpacing ) co
|
||||||
if( GetFace()->units_per_EM )
|
if( GetFace()->units_per_EM )
|
||||||
pitch = GetFace()->height / GetFace()->units_per_EM;
|
pitch = GetFace()->height / GetFace()->units_per_EM;
|
||||||
|
|
||||||
return ( aLineSpacing * aGlyphHeight * pitch * OUTLINE_FONT_SIZE_COMPENSATION );
|
double interline = aLineSpacing * aGlyphHeight * pitch * m_outlineFontSizeCompensation;
|
||||||
|
|
||||||
|
// FONT TODO this hack is an attempt to fix interline spacing by eyeballing it
|
||||||
|
static constexpr double interlineHackMultiplier = 1.2;
|
||||||
|
interline *= interlineHackMultiplier;
|
||||||
|
|
||||||
|
return interline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -235,29 +221,32 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_pt
|
||||||
bool aMirror, const VECTOR2I& aOrigin,
|
bool aMirror, const VECTOR2I& aOrigin,
|
||||||
TEXT_STYLE_FLAGS aTextStyle ) const
|
TEXT_STYLE_FLAGS aTextStyle ) const
|
||||||
{
|
{
|
||||||
|
VECTOR2D glyphSize = aSize;
|
||||||
|
FT_Face face = m_face;
|
||||||
|
double scaler = faceSize();
|
||||||
|
|
||||||
|
if( IsSubscript( aTextStyle ) || IsSuperscript( aTextStyle ) )
|
||||||
|
{
|
||||||
|
scaler = subscriptSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set glyph resolution so that FT_Load_Glyph() results are good enough for decomposing
|
||||||
|
FT_Set_Char_Size( face, 0, scaler, GLYPH_RESOLUTION, 0 );
|
||||||
|
|
||||||
hb_buffer_t* buf = hb_buffer_create();
|
hb_buffer_t* buf = hb_buffer_create();
|
||||||
hb_buffer_add_utf8( buf, aText.c_str(), -1, 0, -1 );
|
hb_buffer_add_utf8( buf, aText.c_str(), -1, 0, -1 );
|
||||||
|
hb_buffer_guess_segment_properties( buf ); // guess direction, script, and language based on contents
|
||||||
// guess direction, script, and language based on contents
|
|
||||||
hb_buffer_guess_segment_properties( buf );
|
|
||||||
|
|
||||||
unsigned int glyphCount;
|
unsigned int glyphCount;
|
||||||
hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( buf, &glyphCount );
|
hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( buf, &glyphCount );
|
||||||
hb_glyph_position_t* glyphPos = hb_buffer_get_glyph_positions( buf, &glyphCount );
|
hb_glyph_position_t* glyphPos = hb_buffer_get_glyph_positions( buf, &glyphCount );
|
||||||
hb_font_t* referencedFont;
|
|
||||||
|
|
||||||
VECTOR2D glyphSize = aSize;
|
hb_font_t* referencedFont = hb_ft_font_create_referenced( face );
|
||||||
FT_Face face = m_face;
|
|
||||||
double scaler = m_faceScaler / OUTLINE_FONT_SIZE_COMPENSATION;
|
|
||||||
|
|
||||||
if( IsSubscript( aTextStyle ) || IsSuperscript( aTextStyle ) )
|
|
||||||
face = m_subscriptFace;
|
|
||||||
|
|
||||||
referencedFont = hb_ft_font_create_referenced( face );
|
|
||||||
hb_ft_font_set_funcs( referencedFont );
|
hb_ft_font_set_funcs( referencedFont );
|
||||||
hb_shape( referencedFont, buf, nullptr, 0 );
|
hb_shape( referencedFont, buf, nullptr, 0 );
|
||||||
|
|
||||||
const VECTOR2D scaleFactor( glyphSize.x / scaler, -glyphSize.y / scaler );
|
VECTOR2D scaleFactor( glyphSize.x / faceSize(), -glyphSize.y / faceSize() );
|
||||||
|
scaleFactor = scaleFactor * m_outlineFontSizeCompensation;
|
||||||
|
|
||||||
VECTOR2I cursor( 0, 0 );
|
VECTOR2I cursor( 0, 0 );
|
||||||
VECTOR2D topLeft( INT_MAX * 1.0, -INT_MAX * 1.0 );
|
VECTOR2D topLeft( INT_MAX * 1.0, -INT_MAX * 1.0 );
|
||||||
|
@ -265,21 +254,16 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_pt
|
||||||
|
|
||||||
for( unsigned int i = 0; i < glyphCount; i++ )
|
for( unsigned int i = 0; i < glyphCount; i++ )
|
||||||
{
|
{
|
||||||
hb_glyph_position_t& pos = glyphPos[i];
|
|
||||||
int codepoint = glyphInfo[i].codepoint;
|
|
||||||
|
|
||||||
if( aGlyphs )
|
if( aGlyphs )
|
||||||
{
|
{
|
||||||
FT_Load_Glyph( face, codepoint, FT_LOAD_NO_BITMAP );
|
FT_Load_Glyph( face, glyphInfo[i].codepoint, FT_LOAD_NO_BITMAP );
|
||||||
|
|
||||||
FT_GlyphSlot faceGlyph = face->glyph;
|
|
||||||
|
|
||||||
// contours is a collection of all outlines in the glyph;
|
// contours is a collection of all outlines in the glyph;
|
||||||
// example: glyph for 'o' generally contains 2 contours,
|
// example: glyph for 'o' generally contains 2 contours,
|
||||||
// one for the glyph outline and one for the hole
|
// one for the glyph outline and one for the hole
|
||||||
CONTOURS contours;
|
CONTOURS contours;
|
||||||
|
|
||||||
OUTLINE_DECOMPOSER decomposer( faceGlyph->outline );
|
OUTLINE_DECOMPOSER decomposer( face->glyph->outline );
|
||||||
decomposer.OutlineToSegments( &contours );
|
decomposer.OutlineToSegments( &contours );
|
||||||
|
|
||||||
std::unique_ptr<OUTLINE_GLYPH> glyph = std::make_unique<OUTLINE_GLYPH>();
|
std::unique_ptr<OUTLINE_GLYPH> glyph = std::make_unique<OUTLINE_GLYPH>();
|
||||||
|
@ -300,9 +284,9 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_pt
|
||||||
topRight.y = std::max( topRight.y, pt.y );
|
topRight.y = std::max( topRight.y, pt.y );
|
||||||
|
|
||||||
if( IsSubscript( aTextStyle ) )
|
if( IsSubscript( aTextStyle ) )
|
||||||
pt.y -= 0.25 * scaler;
|
pt.y += m_subscriptVerticalOffset * scaler;
|
||||||
else if( IsSuperscript( aTextStyle ) )
|
else if( IsSuperscript( aTextStyle ) )
|
||||||
pt.y += 0.45 * scaler;
|
pt.y += m_superscriptVerticalOffset * scaler;
|
||||||
|
|
||||||
pt *= scaleFactor;
|
pt *= scaleFactor;
|
||||||
pt += aPosition;
|
pt += aPosition;
|
||||||
|
@ -339,14 +323,19 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_pt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FONT TODO we might not want to do Fracture() here;
|
||||||
|
// knockout text (eg. silkscreen labels with a background) will
|
||||||
|
// need to do that after the contours have been turned into holes
|
||||||
|
// and vice versa
|
||||||
if( glyph->HasHoles() )
|
if( glyph->HasHoles() )
|
||||||
glyph->Fracture( SHAPE_POLY_SET::PM_FAST ); // FONT TODO verify aFastMode
|
glyph->Fracture( SHAPE_POLY_SET::PM_FAST ); // FONT TODO verify aFastMode
|
||||||
|
|
||||||
aGlyphs->push_back( std::move( glyph ) );
|
aGlyphs->push_back( std::move( glyph ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor.x += pos.x_advance;
|
hb_glyph_position_t& pos = glyphPos[i];
|
||||||
cursor.y += pos.y_advance;
|
cursor.x += ( pos.x_advance * GLYPH_SIZE_SCALER );
|
||||||
|
cursor.y += ( pos.y_advance * GLYPH_SIZE_SCALER );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( IsOverbar( aTextStyle ) && aGlyphs )
|
if( IsOverbar( aTextStyle ) && aGlyphs )
|
||||||
|
@ -354,8 +343,8 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_pt
|
||||||
topLeft *= scaleFactor;
|
topLeft *= scaleFactor;
|
||||||
topRight *= scaleFactor;
|
topRight *= scaleFactor;
|
||||||
|
|
||||||
topLeft.y -= aSize.y * 0.16;
|
topLeft.y -= aSize.y * m_overbarOffset;
|
||||||
topRight.y -= aSize.y * 0.16;
|
topRight.y -= aSize.y * m_overbarOffset;
|
||||||
|
|
||||||
topLeft += aPosition;
|
topLeft += aPosition;
|
||||||
topRight += aPosition;
|
topRight += aPosition;
|
||||||
|
@ -366,7 +355,7 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_pt
|
||||||
RotatePoint( topRight, aOrigin, aAngle );
|
RotatePoint( topRight, aOrigin, aAngle );
|
||||||
}
|
}
|
||||||
|
|
||||||
double overbarHeight = aSize.y * 0.07;
|
double overbarHeight = aSize.y * m_overbarHeightMultiplier;
|
||||||
SHAPE_POLY_SET overbar;
|
SHAPE_POLY_SET overbar;
|
||||||
|
|
||||||
TransformOvalToPolygon( overbar, topLeft, topRight, overbarHeight, overbarHeight / 8,
|
TransformOvalToPolygon( overbar, topLeft, topRight, overbarHeight, overbarHeight / 8,
|
||||||
|
@ -401,9 +390,7 @@ void OUTLINE_FONT::RenderToOpenGLCanvas( KIGFX::OPENGL_GAL& aGal, const UTF8& aS
|
||||||
{
|
{
|
||||||
hb_buffer_t* buf = hb_buffer_create();
|
hb_buffer_t* buf = hb_buffer_create();
|
||||||
hb_buffer_add_utf8( buf, aString.c_str(), -1, 0, -1 );
|
hb_buffer_add_utf8( buf, aString.c_str(), -1, 0, -1 );
|
||||||
|
hb_buffer_guess_segment_properties( buf ); // guess direction, script, and language based on contents
|
||||||
// guess direction, script, and language based on contents
|
|
||||||
hb_buffer_guess_segment_properties( buf );
|
|
||||||
|
|
||||||
unsigned int glyphCount;
|
unsigned int glyphCount;
|
||||||
hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( buf, &glyphCount );
|
hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( buf, &glyphCount );
|
||||||
|
|
|
@ -34,6 +34,14 @@
|
||||||
|
|
||||||
namespace KIFONT
|
namespace KIFONT
|
||||||
{
|
{
|
||||||
|
|
||||||
|
constexpr int GLYPH_DEFAULT_DPI = 72; ///< FreeType default
|
||||||
|
// The FreeType default of 72 DPI is not enough for outline decomposition;
|
||||||
|
// so we'll use something larger than that.
|
||||||
|
constexpr int GLYPH_RESOLUTION = 288;
|
||||||
|
constexpr double GLYPH_SIZE_SCALER = GLYPH_DEFAULT_DPI / (double) GLYPH_RESOLUTION;
|
||||||
|
|
||||||
|
|
||||||
class GLYPH
|
class GLYPH
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -134,13 +134,46 @@ private:
|
||||||
static FT_Library m_freeType;
|
static FT_Library m_freeType;
|
||||||
FT_Face m_face;
|
FT_Face m_face;
|
||||||
const int m_faceSize;
|
const int m_faceSize;
|
||||||
FT_Face m_subscriptFace;
|
|
||||||
|
|
||||||
int m_faceScaler;
|
|
||||||
|
|
||||||
// cache for glyphs converted to straight segments
|
// cache for glyphs converted to straight segments
|
||||||
// key is glyph index (FT_GlyphSlot field glyph_index)
|
// key is glyph index (FT_GlyphSlot field glyph_index)
|
||||||
std::map<unsigned int, GLYPH_POINTS_LIST> m_contourCache;
|
std::map<unsigned int, GLYPH_POINTS_LIST> m_contourCache;
|
||||||
|
|
||||||
|
// The height of the KiCad stroke font is the distance between stroke endpoints for a vertical
|
||||||
|
// line of cap-height. So the cap-height of the font is actually stroke-width taller than its
|
||||||
|
// height.
|
||||||
|
// Outline fonts are normally scaled on full-height (including ascenders and descenders), so we
|
||||||
|
// need to compensate to keep them from being much smaller than their stroked counterparts.
|
||||||
|
static constexpr double m_outlineFontSizeCompensation = 1.4;
|
||||||
|
|
||||||
|
// FT_Set_Char_Size() gets character width and height specified in
|
||||||
|
// 1/64ths of a point
|
||||||
|
static constexpr int m_charSizeScaler = 64;
|
||||||
|
|
||||||
|
// The KiCad stroke font uses a subscript/superscript size ratio of 0.7. This ratio is also
|
||||||
|
// commonly used in LaTeX, but fonts with designed-in subscript and superscript glyphs are more
|
||||||
|
// likely to use 0.58.
|
||||||
|
// For auto-generated subscript and superscript glyphs in outline fonts we split the difference
|
||||||
|
// with 0.64.
|
||||||
|
static constexpr double m_subscriptSuperscriptSize = 0.64;
|
||||||
|
|
||||||
|
int faceSize( int aSize ) const
|
||||||
|
{
|
||||||
|
return aSize * m_charSizeScaler * m_outlineFontSizeCompensation;
|
||||||
|
};
|
||||||
|
int faceSize() const { return faceSize( m_faceSize ); }
|
||||||
|
|
||||||
|
// also for superscripts
|
||||||
|
int subscriptSize( int aSize ) const
|
||||||
|
{
|
||||||
|
return KiROUND( faceSize( aSize ) * m_subscriptSuperscriptSize );
|
||||||
|
}
|
||||||
|
int subscriptSize() const { return subscriptSize( m_faceSize ); }
|
||||||
|
|
||||||
|
static constexpr double m_subscriptVerticalOffset = -0.25;
|
||||||
|
static constexpr double m_superscriptVerticalOffset = 0.45;
|
||||||
|
static constexpr double m_overbarOffset = 0.16;
|
||||||
|
static constexpr double m_overbarHeightMultiplier = 0.07;
|
||||||
};
|
};
|
||||||
|
|
||||||
} //namespace KIFONT
|
} //namespace KIFONT
|
||||||
|
|
Loading…
Reference in New Issue