From 438c63f58784c5d4badc4c35dc6794f70bcde713 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Fri, 7 Jan 2022 14:49:17 +0000 Subject: [PATCH] Fix outline font boundingbox issues. --- common/eda_text.cpp | 41 +++----------------- common/font/font.cpp | 31 ++++++++++++++++ common/font/outline_font.cpp | 66 ++------------------------------- common/font/stroke_font.cpp | 26 +------------ common/gr_text.cpp | 8 +--- common/plotters/SVG_plotter.cpp | 2 +- eeschema/lib_pin.cpp | 5 ++- eeschema/sch_painter.cpp | 21 +++++++++-- eeschema/sch_text.cpp | 36 ++++++++++-------- eeschema/sch_text.h | 2 + include/font/font.h | 16 +------- include/font/outline_font.h | 17 --------- include/font/stroke_font.h | 14 ------- include/gr_text.h | 2 +- 14 files changed, 92 insertions(+), 195 deletions(-) diff --git a/common/eda_text.cpp b/common/eda_text.cpp index 9d9f1e1262..d14b2fa28f 100644 --- a/common/eda_text.cpp +++ b/common/eda_text.cpp @@ -478,8 +478,6 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const wxArrayString strings; wxString text = GetShownText(); int thickness = GetEffectiveTextPenWidth(); - int linecount = 1; - bool hasOverBar = false; // true if the first line of text as an overbar if( IsMultilineAllowed() ) { @@ -491,19 +489,6 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const text = strings.Item( aLine ); else text = strings.Item( 0 ); - - linecount = strings.GetCount(); - } - } - - // Search for overbar symbol. Only text is scanned, - // because only this line can change the bounding box - for( unsigned ii = 1; ii < text.size(); ii++ ) - { - if( text[ii-1] == '~' && text[ii] == '{' ) - { - hasOverBar = true; - break; } } @@ -511,7 +496,10 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const KIFONT::FONT* font = GetDrawFont(); VECTOR2D fontSize( GetTextSize() ); double penWidth( thickness ); - int dx = KiROUND( font->StringBoundaryLimits( text, fontSize, penWidth ).x ); + bool bold = IsBold(); + bool italic = IsItalic(); + VECTOR2D extents = font->StringBoundaryLimits( text, fontSize, penWidth, bold, italic ); + int dx = KiROUND( extents.x ); int dy = GetInterline(); // Creates bounding box (rectangle) for horizontal, left and top justified text. The @@ -527,26 +515,13 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const rect.SetOrigin( pos ); - if( hasOverBar ) - { // A overbar adds an extra size to the text - // Height from the base line text of chars like [ or { - double curr_height = GetTextHeight() * 1.15; - double overbarPosition = font->ComputeOverbarVerticalPosition( fontSize.y ); - int extra_height = KiROUND( overbarPosition - curr_height ); - - extra_height += thickness / 2; - textsize.y += extra_height; - rect.Move( VECTOR2I( 0, -extra_height ) ); - } - - // for multiline texts and aLine < 0, merge all rectangles - // ( if aLine < 0, we want the full text bounding box ) + // for multiline texts and aLine < 0, merge all rectangles (aLine == -1 signals all lines) if( IsMultilineAllowed() && aLine < 0 ) { for( unsigned ii = 1; ii < strings.GetCount(); ii++ ) { text = strings.Item( ii ); - dx = KiROUND( font->StringBoundaryLimits( text, fontSize, penWidth ).x ); + dx = KiROUND( font->StringBoundaryLimits( text, fontSize, penWidth, bold, italic ).x ); textsize.x = std::max( textsize.x, dx ); textsize.y += dy; } @@ -584,10 +559,6 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const case GR_TEXT_V_ALIGN_BOTTOM: rect.SetY( rect.GetY() - rect.GetHeight() ); break; } - // Many fonts draw diacriticals, descenders, etc. outside the X-height of the font. This - // will cacth most (but probably not all) of them. - rect.Inflate( 0, thickness * 1.5 ); - rect.Normalize(); // Make h and v sizes always >= 0 return rect; diff --git a/common/font/font.cpp b/common/font/font.cpp index 0fbd3348bf..5e86f0e453 100644 --- a/common/font/font.cpp +++ b/common/font/font.cpp @@ -368,6 +368,37 @@ VECTOR2D FONT::drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const } +VECTOR2D FONT::StringBoundaryLimits( const UTF8& aText, const VECTOR2D& aSize, int aThickness, + bool aBold, bool aItalic ) const +{ + // TODO do we need to parse every time - have we already parsed? + std::vector> glyphs; // ignored + BOX2I boundingBox; + TEXT_STYLE_FLAGS textStyle = 0; + + if( aBold ) + textStyle |= TEXT_STYLE::BOLD; + + if( aItalic ) + textStyle |= TEXT_STYLE::ITALIC; + + (void) drawMarkup( &boundingBox, glyphs, aText, VECTOR2D(), aSize, EDA_ANGLE::ANGLE_0, + false, VECTOR2D(), textStyle ); + + if( IsStroke() ) + { + // Inflate by a bit more than thickness/2 to catch diacriticals, descenders, etc. + boundingBox.Inflate( KiROUND( aThickness * 0.75 ) ); + } + else if( IsOutline() ) + { + // Outline fonts have thickness built in + } + + return boundingBox.GetSize(); +} + + VECTOR2D FONT::boundingBoxSingleLine( BOX2I* aBoundingBox, const UTF8& aText, const VECTOR2I& aPosition, const VECTOR2D& aSize, bool aItalic ) const diff --git a/common/font/outline_font.cpp b/common/font/outline_font.cpp index cb0e664ada..79c5b97614 100644 --- a/common/font/outline_font.cpp +++ b/common/font/outline_font.cpp @@ -150,65 +150,16 @@ FT_Error OUTLINE_FONT::loadFace( const wxString& aFontFileName ) } -/** - * Compute the boundary limits of aText (the bounding box of all shapes). - * - * @return a VECTOR2D giving the width and height of text. - */ -VECTOR2D OUTLINE_FONT::StringBoundaryLimits( const KIGFX::GAL* aGal, const UTF8& aText, - const VECTOR2D& aGlyphSize, - double aGlyphThickness ) const -{ - hb_buffer_t* buf = hb_buffer_create(); - hb_buffer_add_utf8( buf, aText.c_str(), -1, 0, -1 ); - - // guess direction, script, and language based on contents - hb_buffer_guess_segment_properties( buf ); - - unsigned int glyphCount; - hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( buf, &glyphCount ); - hb_font_t* referencedFont = hb_ft_font_create_referenced( m_face ); - //hb_glyph_position_t* glyphPos = hb_buffer_get_glyph_positions( buf, &glyphCount ); - - hb_ft_font_set_funcs( referencedFont ); - hb_shape( referencedFont, buf, nullptr, 0 ); - - int width = 0; - int height = m_face->size->metrics.height; - - FT_UInt previous; - - for( int i = 0; i < (int) glyphCount; i++ ) - { - //hb_glyph_position_t& pos = glyphPos[i]; - int codepoint = glyphInfo[i].codepoint; - - if( i > 0 ) - { - FT_Vector delta; - FT_Get_Kerning( m_face, previous, codepoint, FT_KERNING_DEFAULT, &delta ); - width += delta.x >> 6; - } - - FT_Load_Glyph( m_face, codepoint, FT_LOAD_NO_BITMAP ); - FT_GlyphSlot glyph = m_face->glyph; - - width += glyph->advance.x >> 6; - previous = codepoint; - } - - return VECTOR2D( width * m_faceScaler, height * m_faceScaler ); -} - - /** * Compute the vertical position of an overbar. This is the distance between the text * baseline and the overbar. */ double OUTLINE_FONT::ComputeOverbarVerticalPosition( double aGlyphHeight ) const { - // TODO: dummy to make this compile! not used - return aGlyphHeight; + // 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 + // calculating bounding boxes). + return aGlyphHeight * OUTLINE_FONT_SIZE_COMPENSATION; } @@ -227,15 +178,6 @@ double OUTLINE_FONT::GetInterline( double aGlyphHeight, double aLineSpacing ) co } -/** - * Compute the X and Y size of a given text. The text is expected to be a single line. - */ -VECTOR2D OUTLINE_FONT::ComputeTextLineSize( const KIGFX::GAL* aGal, const UTF8& aText ) const -{ - return StringBoundaryLimits( aGal, aText, aGal->GetGlyphSize(), 0.0 ); -} - - static bool contourIsFilled( const CONTOUR& c ) { switch( c.orientation ) diff --git a/common/font/stroke_font.cpp b/common/font/stroke_font.cpp index 5eb8118094..aa856d8067 100644 --- a/common/font/stroke_font.cpp +++ b/common/font/stroke_font.cpp @@ -176,6 +176,8 @@ void STROKE_FONT::loadNewStrokeFont( const char* const aNewStrokeFont[], int aNe m_glyphs = &g_defaultFontGlyphs; m_glyphBoundingBoxes = g_defaultFontGlyphBoundingBoxes; + m_fontName = wxT( "KiCad Font" ); + m_fontFileName = wxEmptyString; } @@ -193,30 +195,6 @@ double STROKE_FONT::ComputeOverbarVerticalPosition( double aGlyphHeight ) const } -VECTOR2D STROKE_FONT::ComputeTextLineSize( const KIGFX::GAL* aGal, const UTF8& aText ) const -{ - //TODO default glyph size (and line width) is a guess - VECTOR2D glyphSize = aGal ? aGal->GetGlyphSize() : VECTOR2D( 16.0, 16.0 ); - double lineWidth = aGal ? aGal->GetLineWidth() : 1.0; - return StringBoundaryLimits( aGal, aText, glyphSize, lineWidth ); -} - - -VECTOR2D STROKE_FONT::StringBoundaryLimits( const KIGFX::GAL* aGal, const UTF8& aText, - const VECTOR2D& aGlyphSize, - double aGlyphThickness ) const -{ - // TODO do we need to parse every time - have we already parsed? - std::vector> glyphs; // ignored - BOX2I boundingBox; - - (void) drawMarkup( &boundingBox, glyphs, aText, VECTOR2D(), aGlyphSize, EDA_ANGLE::ANGLE_0, - false, VECTOR2D(), 0 /* TODO: should include TEXT_STYLE::ITALIC if set */ ); - - return boundingBox.GetSize(); -} - - VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector>& aGlyphs, const UTF8& aText, const VECTOR2D& aSize, const VECTOR2I& aPosition, const EDA_ANGLE& aAngle, diff --git a/common/gr_text.cpp b/common/gr_text.cpp index 6b08c7c999..bb19047107 100644 --- a/common/gr_text.cpp +++ b/common/gr_text.cpp @@ -97,16 +97,12 @@ int Clamp_Text_PenSize( int aPenSize, const VECTOR2I& aSize, bool aBold ) int GraphicTextWidth( const wxString& aText, KIFONT::FONT* aFont, const VECTOR2I& aSize, - bool aItalic, bool aBold ) + int aThickness, bool aBold, bool aItalic ) { - basic_gal.SetFontItalic( aItalic ); - basic_gal.SetFontBold( aBold ); - basic_gal.SetGlyphSize( VECTOR2D( aSize ) ); - if( !aFont ) aFont = KIFONT::FONT::GetFont(); - return KiROUND( aFont->ComputeTextLineSize( &basic_gal, aText ).x ); + return KiROUND( aFont->StringBoundaryLimits( aText, aSize, aThickness, aBold, aItalic ).x ); } diff --git a/common/plotters/SVG_plotter.cpp b/common/plotters/SVG_plotter.cpp index f6657cfbc3..2d85b2a9b0 100644 --- a/common/plotters/SVG_plotter.cpp +++ b/common/plotters/SVG_plotter.cpp @@ -795,7 +795,7 @@ void SVG_PLOTTER::Text( const VECTOR2I& aPos, // aSize.x or aSize.y is < 0 for mirrored texts. // The actual text size value is the absolute value - text_size.x = std::abs( GraphicTextWidth( aText, aFont, aSize, aItalic, aWidth ) ); + text_size.x = std::abs( GraphicTextWidth( aText, aFont, aSize, aWidth, aBold, aItalic ) ); text_size.y = std::abs( aSize.x * 4/3 ); // Hershey font height to em size conversion DPOINT anchor_pos_dev = userToDeviceCoordinates( aPos ); DPOINT text_pos_dev = userToDeviceCoordinates( text_pos ); diff --git a/eeschema/lib_pin.cpp b/eeschema/lib_pin.cpp index f19c5593cb..10c41e707e 100644 --- a/eeschema/lib_pin.cpp +++ b/eeschema/lib_pin.cpp @@ -1092,6 +1092,7 @@ const EDA_RECT LIB_PIN::GetBoundingBox( bool aIncludeInvisibles, bool aPinOnly ) bool showName = !name.IsEmpty(); bool showNum = !number.IsEmpty(); int minsizeV = TARGET_PIN_RADIUS; + int penWidth = GetPenWidth(); if( !aIncludeInvisibles && !IsVisible() ) showName = false; @@ -1117,7 +1118,7 @@ const EDA_RECT LIB_PIN::GetBoundingBox( bool aIncludeInvisibles, bool aPinOnly ) if( showNum ) { VECTOR2D fontSize( m_numTextSize, m_numTextSize ); - VECTOR2D numSize = font->StringBoundaryLimits( number, fontSize, GetPenWidth() ); + VECTOR2D numSize = font->StringBoundaryLimits( number, fontSize, penWidth, false, false ); numberTextLength = KiROUND( numSize.x ); numberTextHeight = KiROUND( numSize.y ); @@ -1135,7 +1136,7 @@ const EDA_RECT LIB_PIN::GetBoundingBox( bool aIncludeInvisibles, bool aPinOnly ) if( showName ) { VECTOR2D fontSize( m_nameTextSize, m_nameTextSize ); - VECTOR2D nameSize = font->StringBoundaryLimits( name, fontSize, GetPenWidth() ); + VECTOR2D nameSize = font->StringBoundaryLimits( name, fontSize, penWidth, false, false ); nameTextLength = KiROUND( nameSize.x ) + nameTextOffset; nameTextHeight = KiROUND( nameSize.y ) + Mils2iu( PIN_TEXT_MARGIN ); diff --git a/eeschema/sch_painter.cpp b/eeschema/sch_painter.cpp index e545292219..6f0d03137c 100644 --- a/eeschema/sch_painter.cpp +++ b/eeschema/sch_painter.cpp @@ -464,7 +464,8 @@ void SCH_PAINTER::boxText( const wxString& aText, const VECTOR2D& aPosition, aAttrs.m_Italic ); } - VECTOR2D extents = font->StringBoundaryLimits( aText, aAttrs.m_Size, aAttrs.m_StrokeWidth ); + VECTOR2D extents = font->StringBoundaryLimits( aText, aAttrs.m_Size, aAttrs.m_StrokeWidth, + aAttrs.m_Bold, aAttrs.m_Italic ); EDA_RECT box( (VECTOR2I) aPosition, wxSize( extents.x, aAttrs.m_Size.y ) ); switch( aAttrs.m_Halign ) @@ -1470,8 +1471,7 @@ void SCH_PAINTER::draw( const SCH_TEXT *aText, int aLayer ) m_gal->SetStrokeColor( color ); m_gal->SetFillColor( color ); - VECTOR2D textPos( aText->GetTextPos() ); - VECTOR2D text_offset = aText->GetSchematicTextOffset( &m_schSettings ); + VECTOR2I text_offset = aText->GetSchematicTextOffset( &m_schSettings ); wxString shownText( aText->GetShownText() ); if( drawingShadows && eeconfig()->m_Selection.text_as_box ) @@ -1491,7 +1491,20 @@ void SCH_PAINTER::draw( const SCH_TEXT *aText, int aLayer ) TEXT_ATTRIBUTES attrs = aText->GetAttributes(); attrs.m_StrokeWidth = getTextThickness( aText, drawingShadows ); - strokeText( shownText, textPos + text_offset, attrs ); + std::vector>* cache = nullptr; + + if( !text_offset.x && !text_offset.y ) + cache = aText->GetRenderCache( shownText ); + + if( cache ) + { + for( const std::unique_ptr& glyph : *cache ) + m_gal->DrawGlyph( *glyph.get() ); + } + else + { + strokeText( shownText, aText->GetTextPos(), attrs ); + } } } diff --git a/eeschema/sch_text.cpp b/eeschema/sch_text.cpp index 86a6a30265..f8edbe46cd 100644 --- a/eeschema/sch_text.cpp +++ b/eeschema/sch_text.cpp @@ -244,21 +244,7 @@ bool SCH_TEXT::IncrementLabel( int aIncrement ) VECTOR2I SCH_TEXT::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const { - VECTOR2I text_offset; - - // add an offset to x (or y) position to aid readability of text on a wire or line - int dist = GetTextOffset( aSettings ) + GetPenWidth(); - - switch( GetLabelSpinStyle() ) - { - case LABEL_SPIN_STYLE::UP: - case LABEL_SPIN_STYLE::BOTTOM: text_offset.x = -dist; break; // Vert Orientation - default: - case LABEL_SPIN_STYLE::LEFT: - case LABEL_SPIN_STYLE::RIGHT: text_offset.y = -dist; break; // Horiz Orientation - } - - return text_offset; + return VECTOR2I( 0, 0 ); } @@ -737,6 +723,26 @@ void SCH_LABEL_BASE::SwapData( SCH_ITEM* aItem ) } +VECTOR2I SCH_LABEL_BASE::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const +{ + VECTOR2I text_offset; + + // add an offset to x (or y) position to aid readability of text on a wire or line + int dist = GetTextOffset( aSettings ) + GetPenWidth(); + + switch( GetLabelSpinStyle() ) + { + case LABEL_SPIN_STYLE::UP: + case LABEL_SPIN_STYLE::BOTTOM: text_offset.x = -dist; break; // Vert Orientation + default: + case LABEL_SPIN_STYLE::LEFT: + case LABEL_SPIN_STYLE::RIGHT: text_offset.y = -dist; break; // Horiz Orientation + } + + return text_offset; +} + + void SCH_LABEL_BASE::Rotate( const VECTOR2I& aCenter ) { VECTOR2I pt = GetTextPos(); diff --git a/eeschema/sch_text.h b/eeschema/sch_text.h index e3a38c6375..22e8fa4a74 100644 --- a/eeschema/sch_text.h +++ b/eeschema/sch_text.h @@ -296,6 +296,8 @@ public: SEARCH_RESULT Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] ) override; + VECTOR2I GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const override; + /** * Calculate the graphic shape (a polygon) associated to the text. * diff --git a/include/font/font.h b/include/font/font.h index fe688f796c..2ede59f125 100644 --- a/include/font/font.h +++ b/include/font/font.h @@ -141,15 +141,8 @@ public: * * @return a VECTOR2D giving the width and height of text. */ - virtual VECTOR2D StringBoundaryLimits( const KIGFX::GAL* aGal, const UTF8& aText, - const VECTOR2D& aGlyphSize, - double aGlyphThickness ) const = 0; - - VECTOR2D StringBoundaryLimits( const UTF8& aText, const VECTOR2D& aGlyphSize, - double aGlyphThickness ) const - { - return StringBoundaryLimits( nullptr, aText, aGlyphSize, aGlyphThickness ); - } + VECTOR2D StringBoundaryLimits( const UTF8& aText, const VECTOR2D& aSize, int aThickness, + bool aBold, bool aItalic ) const; /** * Compute the vertical position of an overbar. This is the distance between the text @@ -163,11 +156,6 @@ public: */ virtual double GetInterline( double aGlyphHeight, double aLineSpacing = 1.0 ) const = 0; - /** - * Compute the X and Y size of a given text. The text is expected to be a single line. - */ - virtual VECTOR2D ComputeTextLineSize( const KIGFX::GAL* aGal, const UTF8& aText ) const = 0; - /** * Convert text string to an array of GLYPHs. * diff --git a/include/font/outline_font.h b/include/font/outline_font.h index 826ad4a086..58d88d6ded 100644 --- a/include/font/outline_font.h +++ b/include/font/outline_font.h @@ -81,17 +81,6 @@ public: const VECTOR2D& aOrigin, const TEXT_ATTRIBUTES& aAttributes ) const override; #endif - /** - * Compute the boundary limits of aText (the bounding box of all shapes). - * - * The overbar and alignment are not taken in account, '~' characters are skipped. - * - * @return a VECTOR2D giving the width and height of text. - */ - VECTOR2D StringBoundaryLimits( const KIGFX::GAL* aGal, const UTF8& aText, - const VECTOR2D& aGlyphSize, - double aGlyphThickness ) const override; - /** * Compute the vertical position of an overbar. This is the distance between the text * baseline and the overbar. @@ -104,12 +93,6 @@ public: */ double GetInterline( double aGlyphHeight = 0.0, double aLineSpacing = 1.0 ) const override; - /** - * Compute the X and Y size of a given text. The text is expected to be a single line. - */ - VECTOR2D ComputeTextLineSize( const KIGFX::GAL* aGal, const UTF8& aText ) const override; - - VECTOR2I GetTextAsGlyphs( BOX2I* aBoundingBox, std::vector>& aGlyphs, const UTF8& aText, const VECTOR2D& aSize, const VECTOR2I& aPosition, const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin, diff --git a/include/font/stroke_font.h b/include/font/stroke_font.h index 9d5abca45e..653f78cb26 100644 --- a/include/font/stroke_font.h +++ b/include/font/stroke_font.h @@ -64,15 +64,6 @@ public: */ static STROKE_FONT* LoadFont( const wxString& aFontName ); - /** - * Compute the boundary limits of aText (the bounding box of all shapes). - * - * @return a VECTOR2D giving the width and height of text. - */ - VECTOR2D StringBoundaryLimits( const KIGFX::GAL* aGal, const UTF8& aText, - const VECTOR2D& aGlyphSize, - double aGlyphThickness ) const override; - /** * Compute the vertical position of an overbar. This is the distance between the text * baseline and the overbar. @@ -85,11 +76,6 @@ public: */ double GetInterline( double aGlyphHeight, double aLineSpacing = 1.0 ) const override; - /** - * Compute the X and Y size of a given text. The text is expected to be a single line. - */ - VECTOR2D ComputeTextLineSize( const KIGFX::GAL* aGal, const UTF8& aText ) const override; - VECTOR2I GetTextAsGlyphs( BOX2I* aBoundingBox, std::vector>& aGlyphs, const UTF8& aText, const VECTOR2D& aSize, const VECTOR2I& aPosition, const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin, diff --git a/include/gr_text.h b/include/gr_text.h index c4e3b9f3d3..ba7d2eb7bc 100644 --- a/include/gr_text.h +++ b/include/gr_text.h @@ -85,7 +85,7 @@ int GetPenSizeForNormal( const wxSize& aTextSize ); * @return the X size of the graphic text. */ int GraphicTextWidth( const wxString& aText, KIFONT::FONT* aFont, const VECTOR2I& aSize, - bool italic, bool bold ); + int aThickness, bool aBold, bool aItalic ); /** * Draw a graphic text (like footprint text)