Overhaul the font metrics calcs for overbar, italics and bboxes.

This commit is contained in:
Jeff Young 2022-02-07 00:24:08 +00:00
parent 624dc393bf
commit 5ce559176d
7 changed files with 91 additions and 72 deletions

View File

@ -544,6 +544,7 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const
bool bold = IsBold();
bool italic = IsItalic();
VECTOR2I extents = font->StringBoundaryLimits( text, fontSize, thickness, bold, italic );
int overbarOffset = 0;
// Creates bounding box (rectangle) for horizontal, left and top justified text. The
// bounding box will be moved later according to the actual text options
@ -553,6 +554,9 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const
if( IsMultilineAllowed() && aLine > 0 && ( aLine < static_cast<int>( strings.GetCount() ) ) )
pos.y -= KiROUND( aLine * font->GetInterline( fontSize.y ) );
if( text.Contains( wxT( "~{" ) ) )
overbarOffset = extents.y / 14;
if( aInvertY )
pos.y = -pos.y;
@ -575,34 +579,41 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const
rect.SetSize( textsize );
/* Now, calculate the rect origin, according to text justification
* At this point the rectangle origin is the text origin (m_Pos).
* This is true only for left and top text justified texts (using top to bottom Y axis
* orientation). and must be recalculated for others justifications
* also, note the V justification is relative to the first line
/*
* At this point the rectangle origin is the text origin (m_Pos). This is correct only for
* left and top justified, non-mirrored, non-overbarred texts. Recalculate for all others.
*/
int italicOffset = IsItalic() ? fontSize.y * ITALIC_TILT : 0;
switch( GetHorizJustify() )
{
case GR_TEXT_H_ALIGN_LEFT:
if( IsMirrored() )
rect.SetX( rect.GetX() - rect.GetWidth() );
rect.SetX( rect.GetX() - ( rect.GetWidth() - italicOffset ) );
break;
case GR_TEXT_H_ALIGN_CENTER:
rect.SetX( rect.GetX() - rect.GetWidth() / 2 );
rect.SetX( rect.GetX() - ( rect.GetWidth() - italicOffset ) / 2 );
break;
case GR_TEXT_H_ALIGN_RIGHT:
if( !IsMirrored() )
rect.SetX( rect.GetX() - rect.GetWidth() );
rect.SetX( rect.GetX() - ( rect.GetWidth() - italicOffset ) );
break;
}
switch( GetVertJustify() )
{
case GR_TEXT_V_ALIGN_TOP: break;
case GR_TEXT_V_ALIGN_CENTER: rect.SetY( rect.GetY() - rect.GetHeight() / 2 ); break;
case GR_TEXT_V_ALIGN_BOTTOM: rect.SetY( rect.GetY() - rect.GetHeight() ); break;
case GR_TEXT_V_ALIGN_TOP:
break;
case GR_TEXT_V_ALIGN_CENTER:
rect.SetY( rect.GetY() - ( rect.GetHeight() + overbarOffset ) / 2 );
break;
case GR_TEXT_V_ALIGN_BOTTOM:
rect.SetY( rect.GetY() - ( rect.GetHeight() + overbarOffset ) );
break;
}
rect.Normalize(); // Make h and v sizes always >= 0
@ -911,6 +922,11 @@ void EDA_TEXT::TransformBoundingBoxWithClearanceToPolygon( SHAPE_POLY_SET* aCorn
EDA_RECT rect = GetTextBox();
// TrueType bounding boxes aren't guaranteed to include all descenders, diacriticals, etc.
// Since we use this for zone knockouts and DRC, we need something more accurate.
if( GetDrawFont()->IsOutline() )
rect = GetEffectiveTextShape()->BBox();
rect.Inflate( aClearanceValue );
corners[0].x = rect.GetOrigin().x;

View File

@ -248,8 +248,6 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_pt
scaleFactor = scaleFactor * m_outlineFontSizeCompensation;
VECTOR2I cursor( 0, 0 );
VECTOR2D topLeft( INT_MAX * 1.0, -INT_MAX * 1.0 );
VECTOR2D topRight( -INT_MAX * 1.0, -INT_MAX * 1.0 );
for( unsigned int i = 0; i < glyphCount; i++ )
{
@ -257,9 +255,8 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_pt
{
FT_Load_Glyph( face, glyphInfo[i].codepoint, FT_LOAD_NO_BITMAP );
// contours is a collection of all outlines in the glyph;
// example: glyph for 'o' generally contains 2 contours,
// one for the glyph outline and one for the hole
// contours is a collection of all outlines in the glyph; for example the 'o' glyph
// generally contains 2 contours, one for the glyph outline and one for the hole
CONTOURS contours;
OUTLINE_DECOMPOSER decomposer( face->glyph->outline );
@ -277,11 +274,6 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_pt
{
VECTOR2D pt( v + cursor );
topLeft.x = std::min( topLeft.x, pt.x );
topLeft.y = std::max( topLeft.y, pt.y );
topRight.x = std::max( topRight.x, pt.x );
topRight.y = std::max( topRight.y, pt.y );
if( IsSubscript( aTextStyle ) )
pt.y += m_subscriptVerticalOffset * scaler;
else if( IsSuperscript( aTextStyle ) )
@ -337,16 +329,36 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_pt
cursor.y += ( pos.y_advance * GLYPH_SIZE_SCALER );
}
if( IsOverbar( aTextStyle ) && aGlyphs )
int ascender = abs( face->size->metrics.ascender * GLYPH_SIZE_SCALER );
int descender = abs( face->size->metrics.descender * GLYPH_SIZE_SCALER );
VECTOR2I extents( cursor.x * scaleFactor.x, ( ascender + descender ) * abs( scaleFactor.y ) );
// Font metrics don't include all descenders and diacriticals, so beef them up just a little.
extents.y *= 1.05;
// Shorten the bar a little so its rounded ends don't make it over-long
double barTrim = glyphSize.x * 0.125;
if( IsOverbar( aTextStyle ) )
{
topLeft *= scaleFactor;
topRight *= scaleFactor;
VECTOR2I topLeft( aPosition );
VECTOR2I topRight( aPosition );
topLeft.y -= aSize.y * m_overbarOffset;
topRight.y -= aSize.y * m_overbarOffset;
topLeft.y += ascender * scaleFactor.y * ( 1.0 + m_overbarOffsetRatio );
topRight.y += ascender * scaleFactor.y * ( 1.0 + m_overbarOffsetRatio );
topLeft += aPosition;
topRight += aPosition;
topLeft.x += barTrim;
topRight.x += extents.x - barTrim;
extents.y *= ( 1.0 + m_overbarOffsetRatio + m_overbarOffsetRatio );
extents.x += barTrim;
if( IsItalic() )
{
topLeft.x += aSize.y * ITALIC_TILT;
topRight.x += aSize.y * ITALIC_TILT;
extents.x += aSize.y * ITALIC_TILT;
}
if( !aAngle.IsZero() )
{
@ -354,14 +366,21 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_pt
RotatePoint( topRight, aOrigin, aAngle );
}
double overbarHeight = aSize.y * m_overbarHeightMultiplier;
SHAPE_POLY_SET overbar;
if( aGlyphs )
{
int thickness = abs( ascender * scaleFactor.y * m_overbarThicknessRatio );
int maxError = KiROUND( thickness / 48 );
TransformOvalToPolygon( overbar, topLeft, topRight, overbarHeight, overbarHeight / 8,
ERROR_INSIDE );
if( IsBold() )
thickness = KiROUND( thickness * 1.5 );
std::unique_ptr<OUTLINE_GLYPH> overbarGlyph = std::make_unique<OUTLINE_GLYPH>( overbar );
aGlyphs->push_back( std::move( overbarGlyph ) );
SHAPE_POLY_SET poly;
TransformOvalToPolygon( poly, topLeft, topRight, thickness, maxError, ERROR_INSIDE );
std::unique_ptr<OUTLINE_GLYPH> overbarGlyph = std::make_unique<OUTLINE_GLYPH>( poly );
aGlyphs->push_back( std::move( overbarGlyph ) );
}
}
hb_buffer_destroy( buf );
@ -371,7 +390,7 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_pt
if( aBBox )
{
aBBox->SetOrigin( aPosition.x, aPosition.y );
aBBox->SetEnd( cursorDisplacement );
aBBox->SetEnd( aPosition + extents );
}
return VECTOR2I( aPosition.x + cursorDisplacement.x, aPosition.y + cursorDisplacement.y );

View File

@ -47,10 +47,6 @@ static constexpr double OVERBAR_POSITION_FACTOR = 1.33;
///< Scale factor for a glyph
static constexpr double STROKE_FONT_SCALE = 1.0 / 21.0;
///< Tilt factor for italic style (this is the scaling factor on dY relative coordinates to
///< give a tilted shape)
static constexpr double ITALIC_TILT = 1.0 / 8;
static constexpr int FONT_OFFSET = -10;
@ -279,6 +275,9 @@ VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr
VECTOR2D barOffset( 0.0, 0.0 );
// Shorten the bar a little so its rounded ends don't make it over-long
double barTrim = glyphSize.x * 0.1;
if( aTextStyle & TEXT_STYLE::OVERBAR )
{
barOffset.y = ComputeOverbarVerticalPosition( glyphSize.y );
@ -286,8 +285,8 @@ VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr
if( aTextStyle & TEXT_STYLE::ITALIC )
barOffset.x = barOffset.y * ITALIC_TILT;
VECTOR2D barStart( aPosition.x + barOffset.x, cursor.y - barOffset.y );
VECTOR2D barEnd( cursor.x + barOffset.x, cursor.y - barOffset.y );
VECTOR2D barStart( aPosition.x + barOffset.x + barTrim, cursor.y - barOffset.y );
VECTOR2D barEnd( cursor.x + barOffset.x - barTrim, cursor.y - barOffset.y );
if( !aAngle.IsZero() )
{
@ -311,7 +310,7 @@ VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr
{
aBBox->SetOrigin( aPosition );
aBBox->SetEnd( cursor.x + barOffset.x - KiROUND( glyphSize.x * INTER_CHAR ),
cursor.y + std::max( glyphSize.y, barOffset.y ) );
cursor.y + std::max( glyphSize.y, barOffset.y * OVERBAR_POSITION_FACTOR ) );
aBBox->Normalize();
}

View File

@ -50,6 +50,15 @@ enum TEXT_STYLE
};
/**
* Tilt factor for italic style (this is the scaling factor on dY relative coordinates to give
* a tilted shape).
* This is applied directly to stroke fonts, and is used as an estimate for outline fonts (which
* have the actual tilt built in to their polygonal glyph outlines).
*/
static constexpr double ITALIC_TILT = 1.0 / 8;
using TEXT_STYLE_FLAGS = unsigned int;

View File

@ -72,21 +72,6 @@ public:
*/
static OUTLINE_FONT* LoadFont( const wxString& aFontFileName, bool aBold, bool aItalic );
#if 0
/**
* Draw a string.
*
* @param aGal
* @param aText is the text to be drawn.
* @param aPosition is the text position in world coordinates.
* @param aOrigin is the item origin
* @param aAttributes contains text attributes (angle, line spacing, ...)
* @return bounding box width/height
*/
VECTOR2D Draw( KIGFX::GAL* aGal, const wxString& aText, const VECTOR2D& aPosition,
const VECTOR2D& aOrigin, const TEXT_ATTRIBUTES& aAttributes ) const override;
#endif
/**
* Compute the vertical position of an overbar. This is the distance between the text
* baseline and the overbar.
@ -104,14 +89,6 @@ public:
const VECTOR2I& aPosition, const EDA_ANGLE& aAngle, bool aMirror,
const VECTOR2I& aOrigin, TEXT_STYLE_FLAGS aTextStyle ) const override;
/**
* Like GetTextAsGlyphs, but handles multiple lines.
* TODO: Combine with GetTextAsGlyphs, maybe with a boolean parameter,
* but it's possible a non-line-breaking version isn't even needed
*
* @param aGlyphs returns text glyphs
* @param aText the text item
*/
void GetLinesAsGlyphs( std::vector<std::unique_ptr<GLYPH>>* aGlyphs, const wxString& aText,
const VECTOR2I& aPosition, const TEXT_ATTRIBUTES& aAttrs ) const;
@ -145,8 +122,7 @@ private:
// 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
// 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
@ -171,8 +147,8 @@ private:
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;
static constexpr double m_overbarOffsetRatio = 0.02;
static constexpr double m_overbarThicknessRatio = 0.08;
};
} //namespace KIFONT

View File

@ -150,8 +150,8 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::addItemToRTrees( BOARD_ITEM* item )
PCB_VIA* via = static_cast<PCB_VIA*>( item );
int clearance = ( m_webWidth / 2 ) + via->GetSolderMaskExpansion();
item->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ), layer,
clearance, m_maxError, ERROR_OUTSIDE );
via->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ), layer,
clearance, m_maxError, ERROR_OUTSIDE );
m_itemTree->Insert( item, layer, m_largestClearance );
}

View File

@ -2007,7 +2007,7 @@ double FOOTPRINT::GetCoverageArea( const BOARD_ITEM* aItem, const GENERAL_COLLEC
}
default:
aItem->TransformShapeWithClearanceToPolygon( poly, UNDEFINED_LAYER, 0,
shape->TransformShapeWithClearanceToPolygon( poly, UNDEFINED_LAYER, 0,
ARC_LOW_DEF, ERROR_OUTSIDE );
}
}