diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index ba97801de1..b0938beb90 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -32,7 +32,6 @@ set( GAL_SRCS gal/gal_display_options.cpp gal/graphics_abstraction_layer.cpp gal/hidpi_gl_canvas.cpp - gal/stroke_font.cpp view/view_controls.cpp view/view_overlay.cpp @@ -295,6 +294,8 @@ set( PLUGINS_EAGLE_SRCS set( FONT_SRCS font/font.cpp + font/glyph.cpp + font/stroke_font.cpp ) set( COMMON_SRCS diff --git a/common/basic_gal.cpp b/common/basic_gal.cpp index 55064cae9e..baaf7b419d 100644 --- a/common/basic_gal.cpp +++ b/common/basic_gal.cpp @@ -107,6 +107,20 @@ void BASIC_GAL::DrawPolyline( const std::deque& aPointList ) } +void BASIC_GAL::DrawPolyline( const std::vector& aPointList ) +{ + if( aPointList.size() < 2 ) + return; + + std::vector polyline_corners; + + for( const VECTOR2D& pt : aPointList ) + polyline_corners.emplace_back( (VECTOR2I) transform( pt ) ); + + doDrawPolyline( polyline_corners ); +} + + void BASIC_GAL::DrawPolyline( const VECTOR2D aPointList[], int aListSize ) { if( aListSize < 2 ) @@ -150,3 +164,18 @@ void BASIC_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint m_callback( startVector.x, startVector.y, endVector.x, endVector.y, m_callbackData ); } } + + +void BASIC_GAL::DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal ) +{ + if( aGlyph.IsStroke() ) + { + for( const std::vector& pointList : aGlyph.GetPoints() ) + DrawPolyline( pointList ); + } +#if 0 // FONT TODO + else if( aGlyph.IsOutline() ) + { + } +#endif +} diff --git a/common/drawing_sheet/ds_painter.cpp b/common/drawing_sheet/ds_painter.cpp index b15c471e77..f805d31ed8 100644 --- a/common/drawing_sheet/ds_painter.cpp +++ b/common/drawing_sheet/ds_painter.cpp @@ -230,20 +230,21 @@ void KIGFX::DS_PAINTER::draw( const DS_DRAW_ITEM_POLYPOLYGONS* aItem, int aLayer void KIGFX::DS_PAINTER::draw( const DS_DRAW_ITEM_TEXT* aItem, int aLayer ) const { - VECTOR2D position( aItem->GetTextPos().x, aItem->GetTextPos().y ); - int penWidth = std::max( aItem->GetEffectiveTextPenWidth(), - m_renderSettings.GetDefaultPenWidth() ); + KIFONT::FONT* font = aItem->GetFont(); - m_gal->Save(); - m_gal->Translate( position ); - m_gal->Rotate( -aItem->GetTextAngle().AsRadians() ); - m_gal->SetStrokeColor( m_renderSettings.GetColor( aItem, aLayer ) ); - m_gal->SetLineWidth( penWidth ); - m_gal->SetTextAttributes( aItem ); - m_gal->SetIsFill( false ); - m_gal->SetIsStroke( true ); - m_gal->StrokeText( aItem->GetShownText(), VECTOR2D( 0, 0 ), 0.0 ); - m_gal->Restore(); + if( !font ) + font = KIFONT::FONT::GetFont( wxEmptyString, aItem->IsBold(), aItem->IsItalic() ); + + const COLOR4D& color = m_renderSettings.GetColor( aItem, aLayer ); + + m_gal->SetStrokeColor( color ); + m_gal->SetFillColor( color ); + + TEXT_ATTRIBUTES attrs = aItem->GetAttributes(); + attrs.m_StrokeWidth = std::max( aItem->GetEffectiveTextPenWidth(), + m_renderSettings.GetDefaultPenWidth() ); + + font->Draw( m_gal, aItem->GetShownText(), aItem->GetTextPos(), attrs ); } diff --git a/common/eda_text.cpp b/common/eda_text.cpp index 9d2b9cf0a5..3dfe0120b6 100644 --- a/common/eda_text.cpp +++ b/common/eda_text.cpp @@ -40,7 +40,6 @@ #include // for EDA_RECT #include // for EDA_TEXT, TEXT_EFFECTS, GR_TEXT_VJUSTIF... #include // for COLOR4D, COLOR4D::BLACK -#include // for STROKE_FONT #include // for GRText #include // for UnescapeString #include // for KiROUND @@ -203,20 +202,6 @@ void EDA_TEXT::cacheShownText() } -int EDA_TEXT::LenSize( const wxString& aLine, int aThickness ) const -{ - basic_gal.SetFontItalic( IsItalic() ); - basic_gal.SetFontBold( IsBold() ); - basic_gal.SetFontUnderlined( false ); - basic_gal.SetLineWidth( (float) aThickness ); - basic_gal.SetGlyphSize( VECTOR2D( GetTextSize() ) ); - - VECTOR2D tsize = basic_gal.GetTextLineSize( aLine ); - - return KiROUND( tsize.x ); -} - - wxString EDA_TEXT::ShortenedShownText() const { wxString tmp = GetShownText(); @@ -234,7 +219,12 @@ wxString EDA_TEXT::ShortenedShownText() const int EDA_TEXT::GetInterline() const { - return KiROUND( KIGFX::STROKE_FONT::GetInterline( GetTextHeight() ) ); + KIFONT::FONT* font = GetFont(); + + if( !font ) + font = KIFONT::FONT::GetFont( wxEmptyString, m_attributes.m_Bold, m_attributes.m_Italic ); + + return KiROUND( font->GetInterline( GetTextHeight() ) ); } @@ -274,21 +264,19 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const } // calculate the H and V size - const auto& font = basic_gal.GetStrokeFont(); - VECTOR2D fontSize( GetTextSize() ); - double penWidth( thickness ); - int dx = KiROUND( font.ComputeStringBoundaryLimits( text, fontSize, penWidth ).x ); - int dy = GetInterline(); + KIFONT::FONT* font = KIFONT::FONT::GetFont(); + VECTOR2D fontSize( GetTextSize() ); + double penWidth( thickness ); + int dx = KiROUND( font->StringBoundaryLimits( text, fontSize, penWidth ).x ); + int dy = GetInterline(); // Creates bounding box (rectangle) for horizontal, left and top justified text. The // bounding box will be moved later according to the actual text options - wxSize textsize = wxSize( dx, dy ); + wxSize textsize = wxSize( dx, fontSize.y ); VECTOR2I pos = GetTextPos(); if( IsMultilineAllowed() && aLine > 0 && ( aLine < static_cast( strings.GetCount() ) ) ) - { pos.y -= aLine * GetInterline(); - } if( aInvertY ) pos.y = -pos.y; @@ -299,7 +287,7 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const { // 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 ); + double overbarPosition = font->ComputeOverbarVerticalPosition( fontSize.y ); int extra_height = KiROUND( overbarPosition - curr_height ); extra_height += thickness / 2; @@ -314,7 +302,7 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const for( unsigned ii = 1; ii < strings.GetCount(); ii++ ) { text = strings.Item( ii ); - dx = KiROUND( font.ComputeStringBoundaryLimits( text, fontSize, penWidth ).x ); + dx = KiROUND( font->StringBoundaryLimits( text, fontSize, penWidth ).x ); textsize.x = std::max( textsize.x, dx ); textsize.y += dy; } @@ -336,7 +324,7 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const break; case GR_TEXT_H_ALIGN_CENTER: - rect.SetX( rect.GetX() - (rect.GetWidth() / 2) ); + rect.SetX( rect.GetX() - rect.GetWidth() / 2 ); break; case GR_TEXT_H_ALIGN_RIGHT: @@ -347,38 +335,9 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const switch( GetVertJustify() ) { - case GR_TEXT_V_ALIGN_TOP: - break; - - case GR_TEXT_V_ALIGN_CENTER: - rect.SetY( rect.GetY() - ( dy / 2) ); - break; - - case GR_TEXT_V_ALIGN_BOTTOM: - rect.SetY( rect.GetY() - dy ); - break; - } - - if( linecount > 1 ) - { - int yoffset; - linecount -= 1; - - switch( GetVertJustify() ) - { - case GR_TEXT_V_ALIGN_TOP: - break; - - case GR_TEXT_V_ALIGN_CENTER: - yoffset = linecount * GetInterline() / 2; - rect.SetY( rect.GetY() - yoffset ); - break; - - case GR_TEXT_V_ALIGN_BOTTOM: - yoffset = linecount * GetInterline(); - rect.SetY( rect.GetY() - yoffset ); - break; - } + 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; } // Many fonts draw diacriticals, descenders, etc. outside the X-height of the font. This diff --git a/common/font/font.cpp b/common/font/font.cpp index 4479b46326..32f03f9771 100644 --- a/common/font/font.cpp +++ b/common/font/font.cpp @@ -24,14 +24,18 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include -#include - +#include +#include +#include +#include +#include +#include using namespace KIFONT; -FONT* FONT::s_defaultFont = nullptr; -std::map FONT::s_fontMap; +FONT* FONT::s_defaultFont = nullptr; + +std::map< std::tuple, FONT*> FONT::s_fontMap; FONT::FONT() @@ -45,3 +49,393 @@ const wxString& FONT::Name() const } +FONT* FONT::getDefaultFont() +{ + // FONT TODO: default font should be user-selectable in Eeschema but the KiCad stroke + // font in Pcbnew + + if( !s_defaultFont ) + s_defaultFont = STROKE_FONT::LoadFont( wxEmptyString ); + + return s_defaultFont; +} + + +FONT* FONT::GetFont( const wxString& aFontName, bool aBold, bool aItalic ) +{ + if( aFontName.empty() ) + return getDefaultFont(); + + std::tuple key = { aFontName, aBold, aItalic }; + + FONT* font = s_fontMap[key]; + + if( !font ) + { + wxFont wx_font( wxFontInfo().FaceName( aFontName ).Bold( aBold ).Italic( aItalic ) ); + wxString fullfontname = wx_font.GetNativeFontInfoDesc(); + +#if 0 + // FONT TODO: load a real font + font->m_fontName = aFontName; +#else + font = getDefaultFont(); +#endif + + s_fontMap[key] = font; + } + + return font; +} + + +bool FONT::IsStroke( const wxString& aFontName ) +{ +#if 0 // FONT TODO + // Stroke fonts will be loaded under all four bold/italic combinations, so we only have + // to check for one. + std::tuple key = { aFontName, false, false }; + + FONT* font = s_fontMap[key]; + + return font && font->IsStroke(); +#else + return aFontName == _( "Default Font" ); +#endif +} + + +/** + * Draw a string. + * + * @param aGal + * @param aTextItem is the underlying text item + * @param aPosition is the text position + * @return bounding box width/height + */ +VECTOR2D FONT::doDrawString( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, + bool aParse, const TEXT_ATTRIBUTES& aAttrs ) const +{ + if( aText.empty() ) + return VECTOR2D( 0.0, 0.0 ); + + wxArrayString strings; + std::vector positions; + int n_lines; + VECTOR2D boundingBox; + std::vector lineBoundingBoxes; + + getLinePositions( aText, aPosition, strings, positions, n_lines, lineBoundingBoxes, aAttrs ); + + for( int i = 0; i < n_lines; i++ ) + { + VECTOR2D lineBoundingBox; + if( aParse ) + { + MARKUP::MARKUP_PARSER markupParser( std::string( strings.Item( i ) ) ); + //auto parse_result = markupParser.Parse(); + VECTOR2D cursor = positions[i]; + + std::function& )> nodeHandler = + [&]( const std::unique_ptr& aNode ) + { + if( !aNode->is_root() && aNode->has_content() ) + { + VECTOR2D itemBoundingBox = Draw( aGal, aNode->string(), cursor, + aPosition, aAttrs ); + lineBoundingBox += itemBoundingBox; + cursor += itemBoundingBox; + } + + for( const auto& child : aNode->children ) + nodeHandler( child ); + }; + + nodeHandler( markupParser.Parse() ); + } + else + { + lineBoundingBox = Draw( aGal, strings.Item( i ), positions[i], aPosition, aAttrs ); + } + + boundingBox.x = fmax( boundingBox.x, lineBoundingBox.x ); + } + + boundingBox.y = ( n_lines + 1 ) * GetInterline( aAttrs.m_Size.y ); + + return boundingBox; +} + + +void FONT::getLinePositions( const UTF8& aText, const VECTOR2D& aPosition, + wxArrayString& aStringList, std::vector& aPositions, + int& aLineCount, std::vector& aBoundingBoxes, + const TEXT_ATTRIBUTES& aAttrs ) const +{ + wxStringSplit( aText, aStringList, '\n' ); + aLineCount = aStringList.Count(); + aPositions.reserve( aLineCount ); + + wxPoint origin( aPosition.x, aPosition.y ); + int interline = GetInterline( aAttrs.m_Size.y, aAttrs.m_LineSpacing ); + int height = 0; + + for( int i = 0; i < aLineCount; i++ ) + { + VECTOR2D pos( origin.x, origin.y + i * interline ); + VECTOR2D end = boundingBoxSingleLine( nullptr, aStringList[i], pos, aAttrs.m_Size, + aAttrs.m_Angle, aAttrs.m_Italic ); + VECTOR2D bBox( end - pos ); + + aBoundingBoxes.push_back( bBox ); + + if( i == 0 ) + height += aAttrs.m_Size.y; + else + height += interline; + } + + wxPoint offset( 0, 0 ); + offset.y += aAttrs.m_Size.y; + + switch( aAttrs.m_Valign ) + { + case GR_TEXT_V_ALIGN_TOP: break; + case GR_TEXT_V_ALIGN_CENTER: offset.y -= height / 2; break; + case GR_TEXT_V_ALIGN_BOTTOM: offset.y -= height; break; + } + + int mirrorX = aAttrs.m_Mirrored ? -1 : 1; + + for( int i = 0; i < aLineCount; i++ ) + { + VECTOR2D lineSize = aBoundingBoxes.at( i ); + wxPoint lineOffset( offset ); + lineOffset.y += i * interline; + + switch( aAttrs.m_Halign ) + { + case GR_TEXT_H_ALIGN_LEFT: break; + case GR_TEXT_H_ALIGN_CENTER: lineOffset.x = mirrorX * -lineSize.x / 2; break; + case GR_TEXT_H_ALIGN_RIGHT: lineOffset.x = mirrorX * -lineSize.x; break; + } + + wxPoint pos( aPosition.x + lineOffset.x, aPosition.y + lineOffset.y ); + RotatePoint( &pos, origin, aAttrs.m_Angle ); + + aPositions.push_back( pos ); + } +} + + +VECTOR2D FONT::getBoundingBox( const UTF8& aText, TEXT_STYLE_FLAGS aTextStyle, + const TEXT_ATTRIBUTES& aAttributes ) const +{ + if( aText.empty() ) + return VECTOR2D( 0.0, 0.0 ); + + if( false ) // aParse ) // FONT TODO: parse markup! + { + MARKUP::MARKUP_PARSER markupParser( aText ); + auto parse_result = markupParser.Parse(); + + /* ... */ + } + + wxArrayString strings; + std::vector positions; + int n_lines; + VECTOR2D boundingBox; + std::vector boundingBoxes; + + getLinePositions( aText, VECTOR2D( 0.0, 0.0 ), strings, positions, n_lines, boundingBoxes, + aAttributes ); + + int i = 1; + + for( VECTOR2D lineBoundingBox : boundingBoxes ) + { + boundingBox.x = fmax( boundingBox.x, lineBoundingBox.x ); + boundingBox.y += lineBoundingBox.y; + i++; + } + + return boundingBox; +} + + +void FONT::DrawText( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, + const TEXT_ATTRIBUTES& aAttributes ) const +{ + // FONT TODO: do we need to set the attributes to the gal at all? + aGal->SetHorizontalJustify( aAttributes.m_Halign ); + aGal->SetVerticalJustify( aAttributes.m_Valign ); + aGal->SetGlyphSize( aAttributes.m_Size ); + aGal->SetFontItalic( aAttributes.m_Italic ); + aGal->SetFontBold( aAttributes.m_Bold ); + aGal->SetTextMirrored( aAttributes.m_Mirrored ); + + Draw( aGal, aText, aPosition, aAttributes ); +} + + +/** + * Draw a string. + * + * @param aGal + * @param aText is the text to be drawn. + * @param aPosition is the text position in world coordinates. + * @param aAngle is the text rotation angle + */ +VECTOR2D FONT::Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, + const VECTOR2D& aOrigin, const TEXT_ATTRIBUTES& aAttrs ) const +{ + if( !aGal || aText.empty() ) + return VECTOR2D( 0, 0 ); + + VECTOR2D position( aPosition - aOrigin ); + + // Context needs to be saved before any transformations + //aGal->Save(); + + // Split multiline strings into separate ones and draw them line by line + wxArrayString strings_list; + std::vector positions; + std::vector boundingBoxes; + int n; + + getLinePositions( aText, position, strings_list, positions, n, boundingBoxes, aAttrs ); + + VECTOR2D boundingBox( 0, 0 ); + BOX2I lineBoundingBox; + + for( int i = 0; i < n; i++ ) + { + aGal->Save(); + aGal->Translate( positions[i] ); + aGal->SetLineWidth( aAttrs.m_StrokeWidth ); + + if( !aAttrs.m_Angle.IsZero() ) + aGal->Rotate( aAttrs.m_Angle.Invert().AsRadians() ); + + (void) drawSingleLineText( aGal, &lineBoundingBox, strings_list[i], VECTOR2D( 0, 0 ), + aAttrs.m_Size, aAttrs.m_Angle, aAttrs.m_Italic, + aAttrs.m_Mirrored ); + aGal->Restore(); + + // expand bounding box of whole text + boundingBox.x = std::max( boundingBox.x, (double) lineBoundingBox.GetWidth() ); + + double lineHeight = GetInterline( aAttrs.m_Size.y, aAttrs.m_LineSpacing ); + boundingBox.y += lineHeight; + } + + // undo rotation + //aGal->Restore(); + + return boundingBox; +} + + +/** + * @return position of cursor for drawing next substring + */ +VECTOR2D FONT::drawMarkup( BOX2I* aBoundingBox, GLYPH_LIST& aGlyphs, + const std::unique_ptr& aNode, const VECTOR2D& aPosition, + const VECTOR2D& aGlyphSize, const EDA_ANGLE& aAngle, + TEXT_STYLE_FLAGS aTextStyle, int aLevel ) const +{ + VECTOR2D nextPosition = aPosition; + + TEXT_STYLE_FLAGS textStyle = aTextStyle; + + if( !aNode->is_root() ) + { + if( aNode->isSubscript() ) + textStyle = TEXT_STYLE::SUBSCRIPT; + else if( aNode->isSuperscript() ) + textStyle = TEXT_STYLE::SUPERSCRIPT; + + if( aNode->isOverbar() ) + textStyle |= TEXT_STYLE::OVERBAR; + + if( aNode->has_content() ) + { + std::string txt = aNode->string(); + //std::vector glyphs; + wxPoint pt( aPosition.x, aPosition.y ); + + BOX2I bbox; + nextPosition = GetTextAsPolygon( &bbox, aGlyphs, txt, aGlyphSize, pt, aAngle, textStyle ); + + if( aBoundingBox ) + { + BOX2I boundingBox; + boundingBox = aBoundingBox->Merge( bbox ); + aBoundingBox->SetOrigin( boundingBox.GetOrigin() ); + aBoundingBox->SetSize( boundingBox.GetSize() ); + } + } + } + + for( const auto& child : aNode->children ) + { + nextPosition = drawMarkup( aBoundingBox, aGlyphs, child, nextPosition, aGlyphSize, aAngle, + textStyle, aLevel + 1 ); + } + + return nextPosition; +} + + +VECTOR2D FONT::drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const UTF8& aText, + const VECTOR2D& aPosition, const VECTOR2D& aGlyphSize, + const EDA_ANGLE& aAngle, bool aIsItalic, bool aIsMirrored ) const +{ + if( !aGal ) + { + // do nothing, cursor does not move + return aPosition; + } + + MARKUP::MARKUP_PARSER markupParser( aText ); + std::unique_ptr markupRoot = markupParser.Parse(); + TEXT_STYLE_FLAGS textStyle = 0; + + if( aIsItalic ) + textStyle |= TEXT_STYLE::ITALIC; + + + GLYPH_LIST glyphs; + VECTOR2D nextPosition = drawMarkup( aBoundingBox, glyphs, markupRoot, aPosition, aGlyphSize, + aAngle, textStyle ); + + for( const std::shared_ptr& glyph : glyphs ) + { + if( aIsMirrored ) + glyph->Mirror( aPosition ); + + aGal->DrawGlyph( glyph ); + } + + return nextPosition; +} + + +VECTOR2D FONT::boundingBoxSingleLine( BOX2I* aBoundingBox, const UTF8& aText, + const VECTOR2D& aPosition, const VECTOR2D& aGlyphSize, + const EDA_ANGLE& aAngle, bool aIsItalic ) const +{ + MARKUP::MARKUP_PARSER markupParser( aText ); + std::unique_ptr markupRoot = markupParser.Parse(); + TEXT_STYLE_FLAGS textStyle = 0; + + if( aIsItalic ) + textStyle |= TEXT_STYLE::ITALIC; + + GLYPH_LIST glyphs; // ignored + VECTOR2D nextPosition = drawMarkup( aBoundingBox, glyphs, markupRoot, aPosition, aGlyphSize, + aAngle, false, textStyle ); + + return nextPosition; +} diff --git a/common/font/glyph.cpp b/common/font/glyph.cpp new file mode 100644 index 0000000000..1873b0e8dd --- /dev/null +++ b/common/font/glyph.cpp @@ -0,0 +1,228 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2021 Ola Rinta-Koski + * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include + +using namespace KIFONT; + + +STROKE_GLYPH::STROKE_GLYPH( const STROKE_GLYPH& aGlyph ) +{ + for( const std::vector& pointList : aGlyph.m_pointLists ) + m_pointLists.push_back( pointList ); +} + + +void STROKE_GLYPH::AddPoint( const VECTOR2D& aPoint ) +{ + if( !m_penIsDown ) + { + std::vector v; + m_pointLists.push_back( v ); + m_penIsDown = true; + } + + m_pointLists.back().push_back( aPoint ); +} + + +void STROKE_GLYPH::RaisePen() +{ + if( m_penIsDown ) + m_pointLists.back().shrink_to_fit(); + + m_penIsDown = false; +} + + +void STROKE_GLYPH::Finalize() +{ + if( !m_pointLists.empty() && !m_pointLists.back().empty() ) + m_pointLists.back().shrink_to_fit(); +} + + +BOX2D STROKE_GLYPH::BoundingBox() +{ + if( m_boundingBox.GetWidth() == 0 && m_boundingBox.GetHeight() == 0 ) + { + bool first = true; + + for( const std::vector& pointList : m_pointLists ) + { + for( const VECTOR2D& point : pointList ) + { + if( first ) + { + m_boundingBox.SetOrigin( point ); + first = false; + } + else + { + m_boundingBox.Merge( point ); + } + } + } + } + + return m_boundingBox; +} + + +std::shared_ptr STROKE_GLYPH::Resize( const VECTOR2D& aGlyphSize ) const +{ + std::shared_ptr glyph = std::make_shared( *this ); + + glyph->clearBoundingBox(); + + bool first = true; + + for( std::vector& pointList : glyph->m_pointLists ) + { + for( VECTOR2D& point : pointList ) + { + point.x = point.x * aGlyphSize.x; + point.y = point.y * aGlyphSize.y; + + if( first ) + { + glyph->m_boundingBox.SetOrigin( point ); + first = false; + } + else + { + glyph->m_boundingBox.Merge( point ); + } + } + } + + return glyph; +} + + +std::shared_ptr STROKE_GLYPH::Translate( const VECTOR2D& aOffset ) const +{ + auto glyph = std::make_shared( *this ); + + glyph->clearBoundingBox(); + + bool first = true; + + for( std::vector& pointList : glyph->m_pointLists ) + { + for( VECTOR2D& point : pointList ) + { + point.x += aOffset.x; + point.y += aOffset.y; + + if( first ) + { + glyph->m_boundingBox.SetOrigin( point ); + first = false; + } + else + { + glyph->m_boundingBox.Merge( point ); + } + } + } + + return glyph; +} + + +std::shared_ptr STROKE_GLYPH::Transform( const VECTOR2D& aGlyphSize, const VECTOR2D& aOffset, + double aTilt ) +{ + std::shared_ptr glyph = std::make_shared( *this ); + + glyph->clearBoundingBox(); + + bool first = true; + + for( std::vector& pointList : glyph->m_pointLists ) + { + for( VECTOR2D& point : pointList ) + { + point.x *= aGlyphSize.x; + point.y *= aGlyphSize.y; + + if( aTilt ) + point.x -= point.y * aTilt; + + point.x += aOffset.x; + point.y += aOffset.y; + + if( first ) + { + glyph->m_boundingBox.SetOrigin( point ); + first = false; + } + else + { + glyph->m_boundingBox.Merge( point ); + } + } + } + + return glyph; +} + + +std::shared_ptr STROKE_GLYPH::Mirror( bool aMirror, const VECTOR2D& aMirrorOrigin ) const +{ + // TODO figure out a way to not make a copy if aMirror is false + auto glyph = std::make_shared( *this ); + + if( aMirror ) + glyph->Mirror( aMirrorOrigin ); + + return glyph; +} + + +void STROKE_GLYPH::Mirror( const VECTOR2D& aMirrorOrigin ) +{ + double originX = aMirrorOrigin.x; + + clearBoundingBox(); + + bool first = true; + + for( std::vector& pointList : m_pointLists ) + { + for( VECTOR2D& point : pointList ) + { + if( first ) + { + //originX = point.x; + point.x = originX - ( point.x - originX ); + m_boundingBox.SetOrigin( point ); + first = false; + } + else + { + point.x = originX - ( point.x - originX ); + m_boundingBox.Merge( point ); + } + } + } +} diff --git a/common/font/stroke_font.cpp b/common/font/stroke_font.cpp new file mode 100644 index 0000000000..ec2955733a --- /dev/null +++ b/common/font/stroke_font.cpp @@ -0,0 +1,305 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2013 CERN + * @author Maciej Suminski + * Copyright (C) 2016 Kicad Developers, see change_log.txt for contributors. + * + * Stroke font class + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace KIFONT; + + +///< Factor that determines relative vertical position of the overbar. +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 (the is is the scaling factor +///< on dY relative coordinates to give a tilt shape +static constexpr double ITALIC_TILT = 1.0 / 8; + +///< Factor that determines the pitch between 2 lines. +static constexpr double INTERLINE_PITCH_RATIO = 1.62; // The golden mean + +static constexpr int FONT_OFFSET = -10; + + +bool g_defaultFontInitialized = false; +GLYPH_LIST g_defaultFontGlyphs; ///< Glyph list +std::vector* g_defaultFontGlyphBoundingBoxes; ///< Bounding boxes of the glyphs + + +STROKE_FONT::STROKE_FONT() : + m_glyphs( nullptr ), + m_glyphBoundingBoxes( nullptr ) +{ +} + + +STROKE_FONT* STROKE_FONT::LoadFont( const wxString& aFontName ) +{ + if( aFontName.empty() ) + { + STROKE_FONT* font = new STROKE_FONT(); + font->loadNewStrokeFont( newstroke_font, newstroke_font_bufsize ); + return font; + } + else + { + // FONT TODO: support for other stroke fonts? + return nullptr; + } +} + +void STROKE_FONT::loadNewStrokeFont( const char* const aNewStrokeFont[], int aNewStrokeFontSize ) +{ + if( !g_defaultFontInitialized ) + { + g_defaultFontGlyphs.reserve( aNewStrokeFontSize ); + + g_defaultFontGlyphBoundingBoxes = new std::vector; + g_defaultFontGlyphBoundingBoxes->reserve( aNewStrokeFontSize ); + + for( int j = 0; j < aNewStrokeFontSize; j++ ) + { + auto glyph = std::make_shared(); + double glyphStartX = 0.0; + + std::vector* pointList = nullptr; + + int i = 0; + + while( aNewStrokeFont[j][i] ) + { + VECTOR2D point( 0.0, 0.0 ); + char coordinate[2] = { + 0, + }; + + for( int k : { 0, 1 } ) + coordinate[k] = aNewStrokeFont[j][i + k]; + + if( i < 2 ) + { + // The first two values contain the width of the char + glyphStartX = ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE; + } + else if( ( coordinate[0] == ' ' ) && ( coordinate[1] == 'R' ) ) + { + glyph->RaisePen(); + } + else + { + // In stroke font, coordinates values are coded as + 'R', where + // is an ASCII char. + // therefore every coordinate description of the Hershey format has an offset, + // it has to be subtracted + // Note: + // * the stroke coordinates are stored in reduced form (-1.0 to +1.0), + // and the actual size is stroke coordinate * glyph size + // * a few shapes have a height slightly bigger than 1.0 ( like '{' '[' ) + point.x = (double) ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE - glyphStartX; + + // FONT_OFFSET is here for historical reasons, due to the way the stroke font + // was built. It allows shapes coordinates like W M ... to be >= 0 + // Only shapes like j y have coordinates < 0 + point.y = (double) ( coordinate[1] - 'R' + FONT_OFFSET ) * STROKE_FONT_SCALE; + + if( !pointList ) + pointList = new std::vector; + + glyph->AddPoint( point ); + } + + i += 2; + } + + glyph->Finalize(); + + // Compute the bounding box of the glyph + g_defaultFontGlyphBoundingBoxes->emplace_back( glyph->BoundingBox() ); + g_defaultFontGlyphs.push_back( glyph ); + m_maxGlyphWidth = std::max( m_maxGlyphWidth, + g_defaultFontGlyphBoundingBoxes->back().GetWidth() ); + } + + g_defaultFontInitialized = true; + } + + m_glyphs = &g_defaultFontGlyphs; + m_glyphBoundingBoxes = g_defaultFontGlyphBoundingBoxes; +} + + +double STROKE_FONT::GetInterline( double aGlyphHeight, double aLineSpacing ) const +{ + // Do not add the glyph thickness to the interline. This makes bold text line-spacing + // different from normal text, which is poor typography. + return ( aGlyphHeight * aLineSpacing * INTERLINE_PITCH_RATIO ); +} + + +double STROKE_FONT::ComputeOverbarVerticalPosition( double aGlyphHeight ) const +{ + return aGlyphHeight * OVERBAR_POSITION_FACTOR; +} + + +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? + MARKUP::MARKUP_PARSER markupParser( aText ); + auto root = markupParser.Parse(); + + GLYPH_LIST glyphs; // ignored + BOX2I boundingBox; + + (void) drawMarkup( &boundingBox, glyphs, root, VECTOR2D(), aGlyphSize, EDA_ANGLE::ANGLE_0, + false, 0 /* TODO: this should really include italic */ ); + + return boundingBox.GetSize(); +} + + +VECTOR2D STROKE_FONT::getBoundingBox( const UTF8& aString, const VECTOR2D& aGlyphSize, + TEXT_STYLE_FLAGS aTextStyle ) const +{ + // TODO: take glyph thickness into account! + return StringBoundaryLimits( nullptr, aString, aGlyphSize, 0 ); +} + + +VECTOR2I STROKE_FONT::GetTextAsPolygon( BOX2I* aBoundingBox, GLYPH_LIST& aGlyphs, + const UTF8& aText, const VECTOR2D& aGlyphSize, + const wxPoint& aPosition, const EDA_ANGLE& aAngle, + TEXT_STYLE_FLAGS aTextStyle ) const +{ + wxPoint cursor( aPosition ); + VECTOR2D glyphSize( aGlyphSize ); + double tilt = ( aTextStyle & TEXT_STYLE::ITALIC ) ? ITALIC_TILT : 0.0; + + if( aTextStyle & TEXT_STYLE::SUBSCRIPT || aTextStyle & TEXT_STYLE::SUPERSCRIPT ) + { + constexpr double subscriptSuperscriptMultiplier = 0.7; + glyphSize.x *= subscriptSuperscriptMultiplier; + glyphSize.y *= subscriptSuperscriptMultiplier; + + if( aTextStyle & TEXT_STYLE::SUBSCRIPT ) + { + constexpr double subscriptVerticalMultiplier = 0.3; + cursor.y += glyphSize.y * subscriptVerticalMultiplier; + } + else + { + constexpr double superscriptVerticalMultiplier = 0.5; + cursor.y -= glyphSize.y * superscriptVerticalMultiplier; + } + } + + for( UTF8::uni_iter i = aText.ubegin(), end = aText.uend(); i < end; ++i ) + { + // Index into bounding boxes table + int dd = (signed) *i - ' '; + + if( dd >= (int) m_glyphBoundingBoxes->size() || dd < 0 ) + { + switch( *i ) + { + case '\t': + // TAB->SPACE + dd = 0; + break; + default: + // everything else is turned into a '?' + dd = '?' - ' '; + } + } + + if( dd == 0 ) + { + // 'space' character - draw nothing, advance cursor position + constexpr double spaceAdvance = 0.6; + cursor.x += glyphSize.x * spaceAdvance; + } + else + { + constexpr double interGlyphSpaceMultiplier = 0.15; + double interGlyphSpace = glyphSize.x * interGlyphSpaceMultiplier; + STROKE_GLYPH* source = static_cast( m_glyphs->at( dd ).get() ); + + aGlyphs.push_back( source->Transform( glyphSize, cursor, tilt ) ); + + cursor.x = aGlyphs.back()->BoundingBox().GetEnd().x + interGlyphSpace; + } + } + + VECTOR2D barOffset( 0.0, 0.0 ); + + if( aTextStyle & TEXT_STYLE::OVERBAR ) + { + std::shared_ptr overbarGlyph = std::make_shared(); + + barOffset.y = ComputeOverbarVerticalPosition( glyphSize.y ); + + if( aTextStyle & TEXT_STYLE::ITALIC ) + barOffset.x = barOffset.y * ITALIC_TILT; + + overbarGlyph->AddPoint( VECTOR2D( aPosition.x + barOffset.x, cursor.y - barOffset.y ) ); + overbarGlyph->AddPoint( VECTOR2D( cursor.x + barOffset.x, cursor.y - barOffset.y ) ); + overbarGlyph->Finalize(); + + aGlyphs.push_back( overbarGlyph ); + } + + if( aBoundingBox ) + { + aBoundingBox->SetOrigin( aPosition.x, aPosition.y ); + aBoundingBox->SetEnd( cursor.x + barOffset.x, cursor.y + std::max( glyphSize.y, barOffset.y ) ); + aBoundingBox->Normalize(); + } + + return VECTOR2I( cursor.x, aPosition.y ); +} diff --git a/common/gal/cairo/cairo_gal.cpp b/common/gal/cairo/cairo_gal.cpp index ec0b97beb4..81bcefba7e 100644 --- a/common/gal/cairo/cairo_gal.cpp +++ b/common/gal/cairo/cairo_gal.cpp @@ -286,11 +286,13 @@ void CAIRO_GAL_BASE::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& a // Draw the rounded end point of the segment double arcStartAngle = lineAngle - M_PI / 2.0; - cairo_arc( m_currentContext, center_b.x, center_b.y, radius, arcStartAngle, arcStartAngle + M_PI ); + cairo_arc( m_currentContext, center_b.x, center_b.y, radius, arcStartAngle, + arcStartAngle + M_PI ); // Draw the rounded start point of the segment arcStartAngle = lineAngle + M_PI / 2.0; - cairo_arc( m_currentContext, center_a.x, center_a.y, radius, arcStartAngle, arcStartAngle + M_PI ); + cairo_arc( m_currentContext, center_a.x, center_a.y, radius, arcStartAngle, + arcStartAngle + M_PI ); flushPath(); } @@ -1194,6 +1196,31 @@ void CAIRO_GAL_BASE::drawPoly( const std::deque& aPointList ) } +void CAIRO_GAL_BASE::drawPoly( const std::vector& aPointList ) +{ + wxCHECK( aPointList.size() > 1, /* void */ ); + + // Iterate over the point list and draw the segments + std::vector::const_iterator it = aPointList.begin(); + + syncLineWidth(); + + const VECTOR2D p = roundp( xform( it->x, it->y ) ); + + cairo_move_to( m_currentContext, p.x, p.y ); + + for( ++it; it != aPointList.end(); ++it ) + { + const VECTOR2D p2 = roundp( xform( it->x, it->y ) ); + + cairo_line_to( m_currentContext, p2.x, p2.y ); + } + + flushPath(); + m_isElementAdded = true; +} + + void CAIRO_GAL_BASE::drawPoly( const VECTOR2D aPointList[], int aListSize ) { wxCHECK( aListSize > 1, /* void */ ); @@ -1732,3 +1759,85 @@ void CAIRO_GAL_BASE::DrawGrid() } } } + + +void CAIRO_GAL_BASE::DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal ) +{ + if( aGlyph.IsStroke() ) + { + for( const std::vector& pointList : aGlyph.GetPoints() ) + drawPoly( pointList ); + } +#if 0 // FONT TODO + else if( aGlyph.IsOutline() ) + { + if( aNth == 0 ) + { + cairo_close_path( m_currentContext ); + flushPath(); + + cairo_new_path( m_currentContext ); + SetIsFill( true ); + SetIsStroke( false ); + } + + // eventually glyphs should not be drawn as polygons at all, + // but as bitmaps with antialiasing, this is just a stopgap measure + // of getting some form of outline font display + auto triangleCallback = [&]( int aPolygonIndex, const VECTOR2D& aVertex1, + const VECTOR2D& aVertex2, const VECTOR2D& aVertex3, + void* aCallbackData ) + { + #if 1 + syncLineWidth(); + + const auto p0 = roundp( xform( aVertex1 ) ); + const auto p1 = roundp( xform( aVertex2 ) ); + const auto p2 = roundp( xform( aVertex3 ) ); + + /* + cairo_move_to( currentContext, aVertex1.x, aVertex1.y ); + cairo_line_to( currentContext, aVertex2.x, aVertex2.y ); + cairo_line_to( currentContext, aVertex3.x, aVertex3.y ); + cairo_line_to( currentContext, aVertex1.x, aVertex1.y ); + */ + cairo_move_to( m_currentContext, p0.x, p0.y ); + cairo_line_to( m_currentContext, p1.x, p1.y ); + cairo_line_to( m_currentContext, p2.x, p2.y ); + cairo_close_path( m_currentContext ); + /* + setSourceRgba( currentContext, fillColor ); + SetIsFill( true ); + cairo_set_fill_rule( currentContext, CAIRO_FILL_RULE_EVEN_ODD ); + flushPath(); + */ + //cairo_fill( currentContext ); + #else + // just a silly test + /* + DrawRectangle(aVertex1, aVertex2); + DrawRectangle(aVertex2, aVertex3); + DrawRectangle(aVertex3, aVertex1); + */ + DrawTriangle( aVertex1, aVertex2, aVertex3 ); + #endif + }; + + Triangulate( aGlyph, triangleCallback ); + + if( aNth == aTotal - 1 ) + { + /* + cairo_close_path( currentContext ); + setSourceRgba( currentContext, fillColor ); + cairo_set_fill_rule( currentContext, CAIRO_FILL_RULE_EVEN_ODD ); + cairo_fill_preserve( currentContext ); + setSourceRgba( currentContext, strokeColor ); + cairo_stroke( currentContext ); + */ + flushPath(); + m_isElementAdded = true; + } + } +#endif +} diff --git a/common/gal/graphics_abstraction_layer.cpp b/common/gal/graphics_abstraction_layer.cpp index 18a6b457fd..6122eabf03 100644 --- a/common/gal/graphics_abstraction_layer.cpp +++ b/common/gal/graphics_abstraction_layer.cpp @@ -28,6 +28,7 @@ #include #include +#include #include // for KiROUND @@ -38,7 +39,6 @@ using namespace KIGFX; GAL::GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions ) : m_options( aDisplayOptions ), - m_strokeFont( this ), // m_currentNativeCursor is initialized with KICURSOR::DEFAULT value to avoid // if comparison with uninitialized value on SetNativeCursorStyle method. // Some classes inheriting from GAL has different SetNativeCursorStyle method @@ -86,8 +86,6 @@ GAL::GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions ) : // Initialize text properties ResetTextAttributes(); - m_strokeFont.LoadNewStrokeFont( newstroke_font, newstroke_font_bufsize ); - // subscribe for settings updates m_observerLink = m_options.Subscribe( this ); } @@ -152,15 +150,9 @@ bool GAL::updatedGalDisplayOptions( const GAL_DISPLAY_OPTIONS& aOptions ) } -void GAL::SetTextAttributes( const EDA_TEXT* aText ) +void GAL::SetTextAttributes( const TEXT_ATTRIBUTES& aAttributes ) { - SetGlyphSize( VECTOR2D( aText->GetTextSize() ) ); - SetHorizontalJustify( aText->GetHorizJustify() ); - SetVerticalJustify( aText->GetVertJustify() ); - SetFontBold( aText->IsBold() ); - SetFontItalic( aText->IsItalic() ); - SetFontUnderlined( false ); - SetTextMirrored( aText->IsMirrored() ); + m_attributes = aAttributes; } @@ -180,15 +172,6 @@ void GAL::ResetTextAttributes() } -VECTOR2D GAL::GetTextLineSize( const UTF8& aText ) const -{ - // Compute the X and Y size of a given text. - // Because computeTextLineSize expects a one line text, - // aText is expected to be only one line text. - return m_strokeFont.computeTextLineSize( aText ); -} - - void GAL::ComputeWorldScreenMatrix() { computeWorldScale(); @@ -277,6 +260,65 @@ COLOR4D GAL::getCursorColor() const } +void GAL::StrokeText( const wxString& aText, const VECTOR2D& aPosition, double aRotationAngle, + KIFONT::FONT* aFont, double aLineSpacing ) +{ + if( aText.IsEmpty() ) + return; + + if( !aFont ) + aFont = KIFONT::FONT::GetFont( wxEmptyString ); + + TEXT_ATTRIBUTES attributes; + attributes.m_Angle = EDA_ANGLE( aRotationAngle, EDA_ANGLE::RADIANS ); + attributes.m_Halign = GetHorizontalJustify(); + attributes.m_Valign = GetVerticalJustify(); + attributes.m_LineSpacing = aLineSpacing; + + aFont->Draw( this, aText, aPosition, attributes ); +} + + +void GAL::DrawGlyphs( const KIFONT::GLYPH_LIST& aGlyphs ) +{ + int nth = 0; + int total = aGlyphs.size(); + + for( const std::shared_ptr& glyph : aGlyphs ) + { + DrawGlyph( glyph, nth, total ); + nth++; + } +} + + +/* + * Fallback for implementations that don't implement bitmap text: use stroke font + */ +void GAL::BitmapText( const wxString& aText, const VECTOR2D& aPosition, double aRotationAngle ) +{ + // Handle flipped view + if( m_globalFlipX ) + m_attributes.m_Mirrored = !m_attributes.m_Mirrored; + + // Bitmap font is slightly smaller and slightly heavier than the stroke font so we + // compensate a bit before stroking + float saveLineWidth = m_lineWidth; + VECTOR2D saveGlyphSize = m_attributes.m_Size; + { + m_lineWidth *= 1.2f; + m_attributes.m_Size = m_attributes.m_Size * 0.8; + + StrokeText( aText, aPosition, aRotationAngle ); + } + m_lineWidth = saveLineWidth; + m_attributes.m_Size = saveGlyphSize; + + if( m_globalFlipX ) + m_attributes.m_Mirrored = !m_attributes.m_Mirrored; +} + + bool GAL::SetNativeCursorStyle( KICURSOR aCursor ) { if( m_currentNativeCursor == aCursor ) diff --git a/common/gal/opengl/opengl_gal.cpp b/common/gal/opengl/opengl_gal.cpp index 44c0c8e280..63ec3984a5 100644 --- a/common/gal/opengl/opengl_gal.cpp +++ b/common/gal/opengl/opengl_gal.cpp @@ -1027,6 +1027,17 @@ void OPENGL_GAL::DrawPolyline( const std::deque& aPointList ) } +void OPENGL_GAL::DrawPolyline( const std::vector& aPointList ) +{ + drawPolyline( + [&]( int idx ) + { + return aPointList[idx]; + }, + aPointList.size() ); +} + + void OPENGL_GAL::DrawPolyline( const VECTOR2D aPointList[], int aListSize ) { drawPolyline( @@ -2380,3 +2391,21 @@ void OPENGL_GAL::ComputeWorldScreenMatrix() GAL::ComputeWorldScreenMatrix(); } + + +void OPENGL_GAL::DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal ) +{ + if( aGlyph.IsStroke() ) + { + for( const std::vector& pointList : aGlyph.GetPoints() ) + DrawPolyline( pointList ); + } +#if 0 // FONT TODO + else if( aGlyph.IsOutline() ) + { + fillPolygonAsTriangles( aGlyph.GetPolylist() ); + } +#endif +} + + diff --git a/common/gal/stroke_font.cpp b/common/gal/stroke_font.cpp deleted file mode 100644 index 1ae7e66810..0000000000 --- a/common/gal/stroke_font.cpp +++ /dev/null @@ -1,640 +0,0 @@ -/* - * This program source code file is part of KICAD, a free EDA CAD application. - * - * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de - * Copyright (C) 2013 CERN - * @author Maciej Suminski - * Copyright (C) 2016-2021 Kicad Developers, see change_log.txt for contributors. - * - * Stroke font class - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include -#include -#include // for KiROUND -#include -#include - - -using namespace KIGFX; - -const double STROKE_FONT::INTERLINE_PITCH_RATIO = 1.61; -const double STROKE_FONT::OVERBAR_POSITION_FACTOR = 1.33; -const double STROKE_FONT::UNDERLINE_POSITION_FACTOR = 0.41; -const double STROKE_FONT::BOLD_FACTOR = 1.3; -const double STROKE_FONT::STROKE_FONT_SCALE = 1.0 / 21.0; -const double STROKE_FONT::ITALIC_TILT = 1.0 / 8; - - -GLYPH_LIST* g_newStrokeFontGlyphs = nullptr; ///< Glyph list -std::vector* g_newStrokeFontGlyphBoundingBoxes; ///< Bounding boxes of the glyphs - - -STROKE_FONT::STROKE_FONT( GAL* aGal ) : - m_gal( aGal ), - m_glyphs( nullptr ), - m_glyphBoundingBoxes( nullptr ) -{ -} - - -bool STROKE_FONT::LoadNewStrokeFont( const char* const aNewStrokeFont[], int aNewStrokeFontSize ) -{ - if( g_newStrokeFontGlyphs ) - { - m_glyphs = g_newStrokeFontGlyphs; - m_glyphBoundingBoxes = g_newStrokeFontGlyphBoundingBoxes; - return true; - } - - g_newStrokeFontGlyphs = new GLYPH_LIST; - g_newStrokeFontGlyphs->reserve( aNewStrokeFontSize ); - - g_newStrokeFontGlyphBoundingBoxes = new std::vector; - g_newStrokeFontGlyphBoundingBoxes->reserve( aNewStrokeFontSize ); - - for( int j = 0; j < aNewStrokeFontSize; j++ ) - { - GLYPH* glyph = new GLYPH; - double glyphStartX = 0.0; - double glyphEndX = 0.0; - double glyphWidth = 0.0; - - std::vector* pointList = nullptr; - - int strokes = 0; - int i = 0; - - while( aNewStrokeFont[j][i] ) - { - - if( aNewStrokeFont[j][i] == ' ' && aNewStrokeFont[j][i+1] == 'R' ) - strokes++; - - i += 2; - } - - glyph->reserve( strokes + 1 ); - - i = 0; - - while( aNewStrokeFont[j][i] ) - { - VECTOR2D point( 0.0, 0.0 ); - char coordinate[2] = { 0, }; - - for( int k : { 0, 1 } ) - coordinate[k] = aNewStrokeFont[j][i + k]; - - if( i < 2 ) - { - // The first two values contain the width of the char - glyphStartX = ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE; - glyphEndX = ( coordinate[1] - 'R' ) * STROKE_FONT_SCALE; - glyphWidth = glyphEndX - glyphStartX; - } - else if( ( coordinate[0] == ' ' ) && ( coordinate[1] == 'R' ) ) - { - if( pointList ) - pointList->shrink_to_fit(); - - // Raise pen - pointList = nullptr; - } - else - { - // In stroke font, coordinates values are coded as + 'R', - // is an ASCII char. - // therefore every coordinate description of the Hershey format has an offset, - // it has to be subtracted - // Note: - // * the stroke coordinates are stored in reduced form (-1.0 to +1.0), - // and the actual size is stroke coordinate * glyph size - // * a few shapes have a height slightly bigger than 1.0 ( like '{' '[' ) - point.x = (double) ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE - glyphStartX; - #define FONT_OFFSET -10 - // FONT_OFFSET is here for historical reasons, due to the way the stroke font - // was built. It allows shapes coordinates like W M ... to be >= 0 - // Only shapes like j y have coordinates < 0 - point.y = (double) ( coordinate[1] - 'R' + FONT_OFFSET ) * STROKE_FONT_SCALE; - - if( !pointList ) - { - pointList = new std::vector; - glyph->push_back( pointList ); - } - - pointList->push_back( point ); - } - - i += 2; - } - - if( pointList ) - pointList->shrink_to_fit(); - - // Compute the bounding box of the glyph - g_newStrokeFontGlyphBoundingBoxes->emplace_back( computeBoundingBox( glyph, glyphWidth ) ); - g_newStrokeFontGlyphs->push_back( glyph ); - } - - m_glyphs = g_newStrokeFontGlyphs; - m_glyphBoundingBoxes = g_newStrokeFontGlyphBoundingBoxes; - return true; -} - - -// Static function: -double STROKE_FONT::GetInterline( double aGlyphHeight ) -{ - // Do not add the glyph thickness to the interline. This makes bold text line-spacing - // different from normal text, which is poor typography. - return ( aGlyphHeight * INTERLINE_PITCH_RATIO ); -} - - -BOX2D STROKE_FONT::computeBoundingBox( const GLYPH* aGLYPH, double aGlyphWidth ) const -{ - VECTOR2D min( 0, 0 ); - VECTOR2D max( aGlyphWidth, 0 ); - - for( const std::vector* pointList : *aGLYPH ) - { - for( const VECTOR2D& point : *pointList ) - { - min.y = std::min( min.y, point.y ); - max.y = std::max( max.y, point.y ); - } - } - - return BOX2D( min, max - min ); -} - - -void STROKE_FONT::Draw( const UTF8& aText, const VECTOR2D& aPosition, double aRotationAngle ) -{ - if( aText.empty() ) - return; - - // Context needs to be saved before any transformations - m_gal->Save(); - - m_gal->Translate( aPosition ); - m_gal->Rotate( -aRotationAngle ); - - // Single line height - int lineHeight = KiROUND( GetInterline( m_gal->GetGlyphSize().y ) ); - int lineCount = linesCount( aText ); - const VECTOR2D& glyphSize = m_gal->GetGlyphSize(); - - // align the 1st line of text - switch( m_gal->GetVerticalJustify() ) - { - case GR_TEXT_V_ALIGN_TOP: - m_gal->Translate( VECTOR2D( 0, glyphSize.y ) ); - break; - - case GR_TEXT_V_ALIGN_CENTER: - m_gal->Translate( VECTOR2D( 0, glyphSize.y / 2.0 ) ); - break; - - case GR_TEXT_V_ALIGN_BOTTOM: - break; - - default: - break; - } - - if( lineCount > 1 ) - { - switch( m_gal->GetVerticalJustify() ) - { - case GR_TEXT_V_ALIGN_TOP: - break; - - case GR_TEXT_V_ALIGN_CENTER: - m_gal->Translate( VECTOR2D(0, -( lineCount - 1 ) * lineHeight / 2) ); - break; - - case GR_TEXT_V_ALIGN_BOTTOM: - m_gal->Translate( VECTOR2D(0, -( lineCount - 1 ) * lineHeight ) ); - break; - } - } - - m_gal->SetIsStroke( true ); - //m_gal->SetIsFill( false ); - - if( m_gal->IsFontBold() ) - m_gal->SetLineWidth( m_gal->GetLineWidth() * BOLD_FACTOR ); - - // Split multiline strings into separate ones and draw them line by line - size_t begin = 0; - size_t newlinePos = aText.find( '\n' ); - - while( newlinePos != aText.npos ) - { - size_t length = newlinePos - begin; - - drawSingleLineText( aText.substr( begin, length ) ); - m_gal->Translate( VECTOR2D( 0.0, lineHeight ) ); - - begin = newlinePos + 1; - newlinePos = aText.find( '\n', begin ); - } - - // Draw the last (or the only one) line - if( !aText.empty() ) - drawSingleLineText( aText.substr( begin ) ); - - m_gal->Restore(); -} - - -void STROKE_FONT::drawSingleLineText( const UTF8& aText ) -{ - double xOffset; - double yOffset; - VECTOR2D baseGlyphSize( m_gal->GetGlyphSize() ); - double overbar_italic_comp = computeOverbarVerticalPosition() * ITALIC_TILT; - - if( m_gal->IsTextMirrored() ) - overbar_italic_comp = -overbar_italic_comp; - - // Compute the text size - VECTOR2D textSize = computeTextLineSize( aText ); - double half_thickness = m_gal->GetLineWidth()/2; - - // Context needs to be saved before any transformations - m_gal->Save(); - - // First adjust: the text X position is corrected by half_thickness - // because when the text with thickness is draw, its full size is textSize, - // but the position of lines is half_thickness to textSize - half_thickness - // so we must translate the coordinates by half_thickness on the X axis - // to place the text inside the 0 to textSize X area. - m_gal->Translate( VECTOR2D( half_thickness, 0 ) ); - - // Adjust the text position to the given horizontal justification - switch( m_gal->GetHorizontalJustify() ) - { - case GR_TEXT_H_ALIGN_CENTER: - m_gal->Translate( VECTOR2D( -textSize.x / 2.0, 0 ) ); - break; - - case GR_TEXT_H_ALIGN_RIGHT: - if( !m_gal->IsTextMirrored() ) - m_gal->Translate( VECTOR2D( -textSize.x, 0 ) ); - break; - - case GR_TEXT_H_ALIGN_LEFT: - if( m_gal->IsTextMirrored() ) - m_gal->Translate( VECTOR2D( -textSize.x, 0 ) ); - break; - - default: - break; - } - - if( m_gal->IsTextMirrored() ) - { - // In case of mirrored text invert the X scale of points and their X direction - // (m_glyphSize.x) and start drawing from the position where text normally should end - // (textSize.x) - xOffset = textSize.x - m_gal->GetLineWidth(); - baseGlyphSize.x = -baseGlyphSize.x; - } - else - { - xOffset = 0.0; - } - - // The overbar is indented inward at the beginning of an italicized section, but - // must not be indented on subsequent letters to ensure that the bar segments - // overlap. - bool lastHadOverbar = false; - int overbarDepth = -1; - int superSubDepth = -1; - int braceNesting = 0; - VECTOR2D glyphSize = baseGlyphSize; - - // Allocate only once (for performance) - std::vector ptListScaled; - int char_count = 0; - - yOffset = 0; - - for( UTF8::uni_iter chIt = aText.ubegin(), end = aText.uend(); chIt < end; ++chIt ) - { - // Handle tabs as locked to the nearest 4th column (counting in space-widths). - // The choice of spaces is somewhat arbitrary but sufficient for aligning text; while - // it can produce tabs that go backwards when following wide characters, spacing in - // widest-char-widths produces tab spacing that is much too wide (and would change the - // layout of existing boards). - if( *chIt == '\t' ) - { - char_count = ( char_count / 4 + 1 ) * 4 - 1; - xOffset = baseGlyphSize.x * char_count; - - glyphSize = baseGlyphSize; - yOffset = 0; - } - else if( *chIt == '^' && superSubDepth == -1 ) - { - UTF8::uni_iter lookahead = chIt; - - if( ++lookahead != end && *lookahead == '{' ) - { - chIt = lookahead; - superSubDepth = braceNesting; - braceNesting++; - - glyphSize = baseGlyphSize * 0.8; - yOffset = -baseGlyphSize.y * 0.3; - continue; - } - } - else if( *chIt == '_' && superSubDepth == -1 ) - { - UTF8::uni_iter lookahead = chIt; - - if( ++lookahead != end && *lookahead == '{' ) - { - chIt = lookahead; - superSubDepth = braceNesting; - braceNesting++; - - glyphSize = baseGlyphSize * 0.8; - yOffset = baseGlyphSize.y * 0.1; - continue; - } - } - else if( *chIt == '~' && overbarDepth == -1 ) - { - UTF8::uni_iter lookahead = chIt; - - if( ++lookahead != end && *lookahead == '{' ) - { - chIt = lookahead; - overbarDepth = braceNesting; - braceNesting++; - continue; - } - } - else if( *chIt == '{' ) - { - braceNesting++; - } - else if( *chIt == '}' ) - { - if( braceNesting > 0 ) - braceNesting--; - - if( braceNesting == superSubDepth ) - { - superSubDepth = -1; - - glyphSize = baseGlyphSize; - yOffset = 0; - continue; - } - - if( braceNesting == overbarDepth ) - { - overbarDepth = -1; - continue; - } - } - - // Index into bounding boxes table - int dd = (signed) *chIt - ' '; - - if( dd >= (int) m_glyphBoundingBoxes->size() || dd < 0 ) - { - int substitute = *chIt == '\t' ? ' ' : '?'; - dd = substitute - ' '; - } - - const GLYPH* glyph = m_glyphs->at( dd ); - const BOX2D& bbox = m_glyphBoundingBoxes->at( dd ); - - if( overbarDepth != -1 ) - { - double overbar_start_x = xOffset; - double overbar_start_y = - computeOverbarVerticalPosition(); - double overbar_end_x = xOffset + glyphSize.x * bbox.GetEnd().x; - double overbar_end_y = overbar_start_y; - - if( !lastHadOverbar ) - { - if( m_gal->IsFontItalic() ) - overbar_start_x += overbar_italic_comp; - - lastHadOverbar = true; - } - - VECTOR2D startOverbar( overbar_start_x, overbar_start_y ); - VECTOR2D endOverbar( overbar_end_x, overbar_end_y ); - - m_gal->DrawLine( startOverbar, endOverbar ); - } - else - { - lastHadOverbar = false; - } - - if( m_gal->IsFontUnderlined() ) - { - double vOffset = computeUnderlineVerticalPosition(); - VECTOR2D startUnderline( xOffset, - vOffset ); - VECTOR2D endUnderline( xOffset + glyphSize.x * bbox.GetEnd().x, - vOffset ); - - m_gal->DrawLine( startUnderline, endUnderline ); - } - - for( const std::vector* ptList : *glyph ) - { - int ptCount = 0; - ptListScaled.clear(); - - for( const VECTOR2D& pt : *ptList ) - { - VECTOR2D scaledPt( pt.x * glyphSize.x + xOffset, pt.y * glyphSize.y + yOffset ); - - if( m_gal->IsFontItalic() ) - { - // FIXME should be done other way - referring to the lowest Y value of point - // because now italic fonts are translated a bit - if( m_gal->IsTextMirrored() ) - scaledPt.x += scaledPt.y * STROKE_FONT::ITALIC_TILT; - else - scaledPt.x -= scaledPt.y * STROKE_FONT::ITALIC_TILT; - } - - ptListScaled.push_back( scaledPt ); - ptCount++; - } - - m_gal->DrawPolyline( &ptListScaled[0], ptCount ); - } - - char_count++; - xOffset += glyphSize.x * bbox.GetEnd().x; - } - - m_gal->Restore(); -} - - -double STROKE_FONT::ComputeOverbarVerticalPosition( double aGlyphHeight ) const -{ - // Static method. - return aGlyphHeight * OVERBAR_POSITION_FACTOR; -} - - -double STROKE_FONT::computeOverbarVerticalPosition() const -{ - // Compute the Y position of the overbar. This is the distance between - // the text base line and the overbar axis. - return ComputeOverbarVerticalPosition( m_gal->GetGlyphSize().y ); -} - - -double STROKE_FONT::computeUnderlineVerticalPosition() const -{ - // Compute the Y position of the underline. This is the distance between - // the text base line and the underline axis. - return - m_gal->GetGlyphSize().y * UNDERLINE_POSITION_FACTOR; -} - - -VECTOR2D STROKE_FONT::computeTextLineSize( const UTF8& aText ) const -{ - return ComputeStringBoundaryLimits( aText, m_gal->GetGlyphSize(), m_gal->GetLineWidth() ); -} - - -VECTOR2D STROKE_FONT::ComputeStringBoundaryLimits( const UTF8& aText, const VECTOR2D& aGlyphSize, - double aGlyphThickness ) const -{ - VECTOR2D string_bbox; - int line_count = 1; - double maxX = 0.0, curX = 0.0; - - double curScale = 1.0; - int overbarDepth = -1; - int superSubDepth = -1; - int braceNesting = 0; - - for( UTF8::uni_iter chIt = aText.ubegin(), end = aText.uend(); chIt < end; ++chIt ) - { - if( *chIt == '\n' ) - { - curX = 0.0; - maxX = std::max( maxX, curX ); - ++line_count; - continue; - } - - // Handle tabs as locked to the nearest 4th column (counting in spaces) - // The choice of spaces is somewhat arbitrary but sufficient for aligning text - if( *chIt == '\t' ) - { - double spaces = m_glyphBoundingBoxes->at( 0 ).GetEnd().x; - double addlSpace = 3.0 * spaces - std::fmod( curX, 4.0 * spaces ); - - // Add the remaining space (between 0 and 3 spaces) - curX += addlSpace; - } - else if( (*chIt == '^' || *chIt == '_') && superSubDepth == -1 ) - { - UTF8::uni_iter lookahead = chIt; - - if( ++lookahead != end && *lookahead == '{' ) - { - // Process superscript - chIt = lookahead; - superSubDepth = braceNesting; - braceNesting++; - - curScale = 0.8; - continue; - } - } - else if( *chIt == '~' && overbarDepth == -1 ) - { - UTF8::uni_iter lookahead = chIt; - - if( ++lookahead != end && *lookahead == '{' ) - { - chIt = lookahead; - overbarDepth = braceNesting; - braceNesting++; - continue; - } - } - else if( *chIt == '{' ) - { - braceNesting++; - } - else if( *chIt == '}' ) - { - if( braceNesting > 0 ) - braceNesting--; - - if( braceNesting == overbarDepth ) - { - overbarDepth = -1; - continue; - } - - if( braceNesting == superSubDepth ) - { - superSubDepth = -1; - - curScale = 1.0; - continue; - } - } - - // Index in the bounding boxes table - int dd = (signed) *chIt - ' '; - - if( dd >= (int) m_glyphBoundingBoxes->size() || dd < 0 ) - { - int substitute = *chIt == '\t' ? ' ' : '?'; - dd = substitute - ' '; - } - - const BOX2D& box = m_glyphBoundingBoxes->at( dd ); - curX += box.GetEnd().x * curScale; - } - - string_bbox.x = std::max( maxX, curX ) * aGlyphSize.x; - string_bbox.x += aGlyphThickness; - string_bbox.y = line_count * GetInterline( aGlyphSize.y ); - - // For italic correction, take in account italic tilt - if( m_gal->IsFontItalic() ) - string_bbox.x += string_bbox.y * STROKE_FONT::ITALIC_TILT; - - return string_bbox; -} diff --git a/common/gr_text.cpp b/common/gr_text.cpp index f7170cfff6..489a6849d8 100644 --- a/common/gr_text.cpp +++ b/common/gr_text.cpp @@ -1,8 +1,3 @@ -/** - * Functions to draw and plot text on screen - * @file draw_graphic_text.cpp - */ - /* * This program source code file is part of KiCad, a free EDA CAD application. * @@ -48,12 +43,24 @@ int GetPenSizeForBold( int aTextSize ) } +int GetPenSizeForBold( const wxSize& aTextSize ) +{ + return GetPenSizeForBold( std::min( aTextSize.x, aTextSize.y ) ); +} + + int GetPenSizeForNormal( int aTextSize ) { return KiROUND( aTextSize / 8.0 ); } +int GetPenSizeForNormal( const wxSize& aTextSize ) +{ + return GetPenSizeForNormal( std::min( aTextSize.x, aTextSize.y ) ); +} + + /** * Don't allow text to become cluttered up in its own fatness. Bold fonts are generally around * aSize/5 in width, so we limit them to aSize/4, and normal text to aSize/6. @@ -89,15 +96,14 @@ int Clamp_Text_PenSize( int aPenSize, const VECTOR2I& aSize, bool aBold ) } -int GraphicTextWidth( const wxString& aText, const VECTOR2I& aSize, bool aItalic, bool aBold ) +int GraphicTextWidth( const wxString& aText, KIFONT::FONT* aFont, const VECTOR2I& aSize, + bool aItalic, bool aBold ) { basic_gal.SetFontItalic( aItalic ); basic_gal.SetFontBold( aBold ); basic_gal.SetGlyphSize( VECTOR2D( aSize ) ); - VECTOR2D tsize = basic_gal.GetTextLineSize( aText ); - - return KiROUND( tsize.x ); + return KiROUND( aFont->ComputeTextLineSize( &basic_gal, aText ).x ); } @@ -134,6 +140,9 @@ void GRText( wxDC* aDC, const VECTOR2I& aPos, const COLOR4D& aColor, const wxStr { bool fill_mode = true; + if( !aFont ) + aFont = KIFONT::FONT::GetFont(); + if( aWidth == 0 && aBold ) // Use default values if aWidth == 0 aWidth = GetPenSizeForBold( std::min( aSize.x, aSize.y ) ); @@ -145,28 +154,29 @@ void GRText( wxDC* aDC, const VECTOR2I& aPos, const COLOR4D& aColor, const wxStr basic_gal.SetIsFill( fill_mode ); basic_gal.SetLineWidth( aWidth ); - - EDA_TEXT dummy; - dummy.SetItalic( aItalic ); - dummy.SetBold( aBold ); - dummy.SetHorizJustify( aH_justify ); - dummy.SetVertJustify( aV_justify ); - - wxSize size = wxSize( aSize.x, aSize.y ); - dummy.SetMirrored( size.x < 0 ); - - if( size.x < 0 ) - size.x = - size.x; - - dummy.SetTextSize( size ); - - basic_gal.SetTextAttributes( &dummy ); basic_gal.SetPlotter( aPlotter ); basic_gal.SetCallback( aCallback, aCallbackData ); basic_gal.m_DC = aDC; basic_gal.m_Color = aColor; basic_gal.SetClipBox( nullptr ); - basic_gal.StrokeText( aText, VECTOR2D( aPos ), aOrient.AsRadians() ); + + TEXT_ATTRIBUTES attributes; + attributes.m_Angle = aOrient; + attributes.m_StrokeWidth = aWidth; + attributes.m_Italic = aItalic; + attributes.m_Bold = aBold; + attributes.m_Halign = aH_justify; + attributes.m_Valign = aV_justify; + + VECTOR2D size = aSize; + attributes.m_Mirrored = size.x < 0; + + if( size.x < 0 ) + size.x = - size.x; + + attributes.m_Size = size; + + aFont->Draw( &basic_gal, aText, VECTOR2D( aPos ), attributes ); } diff --git a/common/markup_parser.cpp b/common/markup_parser.cpp index 11e1ac2c37..481dab0493 100644 --- a/common/markup_parser.cpp +++ b/common/markup_parser.cpp @@ -25,7 +25,7 @@ using namespace MARKUP; -MARKUP::MARKUP_NODE MARKUP_PARSER::Parse() +std::unique_ptr MARKUP_PARSER::Parse() { //string_input<> in( source, "from_input" ); auto root = parse_tree::parse( in ); @@ -33,22 +33,6 @@ MARKUP::MARKUP_NODE MARKUP_PARSER::Parse() } -std::ostream& operator<<( std::ostream& os, const MARKUP_NODE& node ) -{ - os << "<"; - - if( !node->is_root() ) - os << node->asString(); - - for( const auto& child : node->children ) - os << " " << child; - - os << ">"; - - return os; -} - - std::string NODE::typeString() const { std::stringstream os; diff --git a/common/plotters/SVG_plotter.cpp b/common/plotters/SVG_plotter.cpp index 05a5932cf9..f6657cfbc3 100644 --- a/common/plotters/SVG_plotter.cpp +++ b/common/plotters/SVG_plotter.cpp @@ -95,6 +95,7 @@ #include #include #include +#include #include #include @@ -794,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, aSize, aItalic, aWidth ) ); + text_size.x = std::abs( GraphicTextWidth( aText, aFont, aSize, aItalic, aWidth ) ); 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/common/preview_items/draw_context.cpp b/common/preview_items/draw_context.cpp index c023748094..065852e0d9 100644 --- a/common/preview_items/draw_context.cpp +++ b/common/preview_items/draw_context.cpp @@ -22,13 +22,13 @@ */ #include - #include - #include using namespace KIGFX::PREVIEW; +using KIGFX::COLOR4D; + static constexpr double ANGLE_EPSILON = 1e-9; diff --git a/common/preview_items/selection_area.cpp b/common/preview_items/selection_area.cpp index ccfb27ef85..ce9f9db328 100644 --- a/common/preview_items/selection_area.cpp +++ b/common/preview_items/selection_area.cpp @@ -29,6 +29,7 @@ #include using namespace KIGFX::PREVIEW; +using KIGFX::COLOR4D; struct SELECTION_COLORS { diff --git a/eeschema/dialogs/dialog_pin_properties.cpp b/eeschema/dialogs/dialog_pin_properties.cpp index 963205bb49..c622ae6e2c 100644 --- a/eeschema/dialogs/dialog_pin_properties.cpp +++ b/eeschema/dialogs/dialog_pin_properties.cpp @@ -135,7 +135,11 @@ DIALOG_PIN_PROPERTIES::DIALOG_PIN_PROPERTIES( SYMBOL_EDIT_FRAME* parent, LIB_PIN m_initialized( false ) { // Creates a dummy pin to show on a panel, inside this dialog: + m_dummyParent = new LIB_SYMBOL( *m_pin->GetParent() ); m_dummyPin = new LIB_PIN( *m_pin ); + m_dummyPin->SetParent( m_dummyParent ); + m_dummyParent->SetShowPinNames( true ); + m_dummyParent->SetShowPinNumbers( true ); m_bSizerInfo->Show( m_frame->m_SyncPinEdit ); @@ -241,6 +245,7 @@ DIALOG_PIN_PROPERTIES::DIALOG_PIN_PROPERTIES( SYMBOL_EDIT_FRAME* parent, LIB_PIN DIALOG_PIN_PROPERTIES::~DIALOG_PIN_PROPERTIES() { delete m_dummyPin; + delete m_dummyParent; // Prevents crash bug in wxGrid's d'tor m_alternatesGrid->DestroyTable( m_alternatesDataModel ); @@ -366,7 +371,7 @@ void DIALOG_PIN_PROPERTIES::OnPaintShowPanel( wxPaintEvent& event ) wxSize dc_size = dc.GetSize(); dc.SetDeviceOrigin( dc_size.x / 2, dc_size.y / 2 ); - // Give a parent to m_dummyPin only from draw purpose. + // Give a parent to m_dummyPin for draw purposes. // In fact m_dummyPin should not have a parent, but draw functions need a parent // to know some options, about pin texts SYMBOL_EDIT_FRAME* symbolEditor = (SYMBOL_EDIT_FRAME*) GetParent(); @@ -383,6 +388,7 @@ void DIALOG_PIN_PROPERTIES::OnPaintShowPanel( wxPaintEvent& event ) GRResetPenAndBrush( &dc ); LIB_SYMBOL_OPTIONS opts; + opts.force_draw_pin_text = true; opts.draw_hidden_fields = true; opts.show_connect_point = true; diff --git a/eeschema/dialogs/dialog_pin_properties.h b/eeschema/dialogs/dialog_pin_properties.h index 9306fdca59..74987669cc 100644 --- a/eeschema/dialogs/dialog_pin_properties.h +++ b/eeschema/dialogs/dialog_pin_properties.h @@ -73,6 +73,8 @@ protected: private: SYMBOL_EDIT_FRAME* m_frame; LIB_PIN* m_pin; + + LIB_SYMBOL* m_dummyParent; LIB_PIN* m_dummyPin; // a working copy used to show changes UNIT_BINDER m_posX; diff --git a/eeschema/lib_pin.cpp b/eeschema/lib_pin.cpp index 12a770c178..224d64c453 100644 --- a/eeschema/lib_pin.cpp +++ b/eeschema/lib_pin.cpp @@ -212,8 +212,9 @@ void LIB_PIN::print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset, { printPinSymbol( aSettings, pos1, orient ); - printPinTexts( aSettings, pos1, orient, part->GetPinNameOffset(), part->ShowPinNumbers(), - part->ShowPinNames() ); + printPinTexts( aSettings, pos1, orient, part->GetPinNameOffset(), + opts->force_draw_pin_text || part->ShowPinNumbers(), + opts->force_draw_pin_text || part->ShowPinNames() ); if( showPinType ) printPinElectricalTypeName( aSettings, pos1, orient ); @@ -457,7 +458,7 @@ void LIB_PIN::printPinTexts( const RENDER_SETTINGS* aSettings, VECTOR2I& aPinPos } else /**** Draw num & text pin outside ****/ { - if(( aPinOrient == PIN_LEFT) || ( aPinOrient == PIN_RIGHT) ) + if( ( aPinOrient == PIN_LEFT) || ( aPinOrient == PIN_RIGHT) ) { /* Its an horizontal line. */ if( aDrawPinName ) @@ -1068,7 +1069,7 @@ void LIB_PIN::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vectorStringBoundaryLimits( number, fontSize, GetPenWidth() ); numberTextLength = KiROUND( numSize.x ); numberTextHeight = KiROUND( numSize.y ); @@ -1126,7 +1127,7 @@ const EDA_RECT LIB_PIN::GetBoundingBox( bool aIncludeInvisibles, bool aPinOnly ) if( showName ) { VECTOR2D fontSize( m_nameTextSize, m_nameTextSize ); - VECTOR2D nameSize = font.ComputeStringBoundaryLimits( name, fontSize, GetPenWidth() ); + VECTOR2D nameSize = font->StringBoundaryLimits( name, fontSize, GetPenWidth() ); nameTextLength = KiROUND( nameSize.x ) + nameTextOffset; nameTextHeight = KiROUND( nameSize.y ) + Mils2iu( PIN_TEXT_MARGIN ); diff --git a/eeschema/lib_symbol.h b/eeschema/lib_symbol.h index b058840d71..eb02212f88 100644 --- a/eeschema/lib_symbol.h +++ b/eeschema/lib_symbol.h @@ -62,6 +62,7 @@ extern bool operator<( const LIB_SYMBOL& aItem1, const LIB_SYMBOL& aItem2 ); struct LIB_SYMBOL_OPTIONS { TRANSFORM transform; // Coordinate adjustment settings + bool force_draw_pin_text; // Whether or not to force the drawing of pin names and numbers bool draw_visible_fields; // Whether to draw "visible" fields bool draw_hidden_fields; // Whether to draw "hidden" fields bool show_elec_type; // Whether to show the pin electrical type @@ -71,6 +72,7 @@ struct LIB_SYMBOL_OPTIONS LIB_SYMBOL_OPTIONS() { transform = DefaultTransform; + force_draw_pin_text = false; draw_visible_fields = true; draw_hidden_fields = true; show_elec_type = false; diff --git a/eeschema/sch_painter.cpp b/eeschema/sch_painter.cpp index 524ae60dfd..b498f6555d 100644 --- a/eeschema/sch_painter.cpp +++ b/eeschema/sch_painter.cpp @@ -435,32 +435,57 @@ static bool isFieldsLayer( int aLayer ) } -void SCH_PAINTER::strokeText( const wxString& aText, const VECTOR2D& aPosition, double aAngle ) +void SCH_PAINTER::strokeText( const wxString& aText, const VECTOR2D& aPosition, + const TEXT_ATTRIBUTES& aAttrs ) { - m_gal->StrokeText( aText, VECTOR2D( aPosition.x, aPosition.y ), aAngle ); + KIFONT::FONT* font = aAttrs.m_Font; + + if( !font ) + font = KIFONT::FONT::GetFont( wxEmptyString, aAttrs.m_Bold, aAttrs.m_Italic ); + + m_gal->SetIsFill( font->IsOutline() ); + m_gal->SetIsStroke( font->IsStroke() ); + + font->Draw( m_gal, aText, aPosition, aAttrs ); } -void SCH_PAINTER::boxText( const wxString& aText, const VECTOR2D& aPosition, double aAngle ) +void SCH_PAINTER::boxText( const wxString& aText, const VECTOR2D& aPosition, + const TEXT_ATTRIBUTES& aAttrs ) { - const STROKE_FONT& font = m_gal->GetStrokeFont(); - VECTOR2D extents = font.ComputeStringBoundaryLimits( aText, m_gal->GetGlyphSize(), - m_gal->GetLineWidth() ); - EDA_RECT box( (VECTOR2I) aPosition, wxSize( extents.x, extents.y ) ); + KIFONT::FONT* font = aAttrs.m_Font; - if( m_gal->GetHorizontalJustify() == GR_TEXT_H_ALIGN_CENTER ) - box.SetX( box.GetX() - ( box.GetWidth() / 2) ); - else if( m_gal->GetHorizontalJustify() == GR_TEXT_H_ALIGN_RIGHT ) - box.SetX( box.GetX() - box.GetWidth() ); + if( !font ) + font = KIFONT::FONT::GetFont( wxEmptyString, aAttrs.m_Bold, aAttrs.m_Italic ); - if( m_gal->GetVerticalJustify() == GR_TEXT_V_ALIGN_CENTER ) - box.SetY( box.GetY() - ( box.GetHeight() / 2) ); - else if( m_gal->GetVerticalJustify() == GR_TEXT_V_ALIGN_BOTTOM ) - box.SetY( box.GetY() - box.GetHeight() ); + VECTOR2D extents = font->StringBoundaryLimits( aText, aAttrs.m_Size, aAttrs.m_StrokeWidth ); + EDA_RECT box( (VECTOR2I) aPosition, wxSize( extents.x, aAttrs.m_Size.y ) ); + + switch( aAttrs.m_Halign ) + { + case GR_TEXT_H_ALIGN_LEFT: break; + case GR_TEXT_H_ALIGN_CENTER: box.SetX( box.GetX() - box.GetWidth() / 2 ); break; + case GR_TEXT_H_ALIGN_RIGHT: box.SetX( box.GetX() - box.GetWidth() ); break; + } + + switch( aAttrs.m_Valign ) + { + case GR_TEXT_V_ALIGN_TOP: break; + case GR_TEXT_V_ALIGN_CENTER: box.SetY( box.GetY() - box.GetHeight() / 2 ); break; + case GR_TEXT_V_ALIGN_BOTTOM: box.SetY( box.GetY() - box.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. + box.Inflate( 0, aAttrs.m_StrokeWidth * 1.5 ); box.Normalize(); // Make h and v sizes always >= 0 - box = box.GetBoundingBoxRotated((VECTOR2I) aPosition, RAD2DECIDEG( aAngle ) ); + box = box.GetBoundingBoxRotated( (VECTOR2I) aPosition, aAttrs.m_Angle.AsTenthsOfADegree() ); box.RevertYAxis(); + + m_gal->SetIsFill( true ); + m_gal->SetIsStroke( true ); + m_gal->SetLineWidth( getShadowWidth( false ) ); m_gal->DrawRectangle( mapCoords( box.GetOrigin() ), mapCoords( box.GetEnd() ) ); } @@ -678,10 +703,7 @@ void SCH_PAINTER::draw( const LIB_FIELD *aField, int aLayer ) return; } - m_gal->SetIsStroke( true ); - m_gal->SetLineWidth( getTextThickness( aField, drawingShadows ) ); m_gal->SetStrokeColor( color ); - m_gal->SetIsFill( drawingShadows && eeconfig()->m_Selection.text_as_box ); m_gal->SetFillColor( color ); EDA_RECT bbox = aField->GetBoundingBox(); @@ -690,18 +712,19 @@ void SCH_PAINTER::draw( const LIB_FIELD *aField, int aLayer ) if( drawingShadows && eeconfig()->m_Selection.text_as_box ) { bbox.RevertYAxis(); - m_gal->SetLineWidth( m_gal->GetLineWidth() / 2 ); + m_gal->SetIsStroke( true ); + m_gal->SetIsFill( true ); + m_gal->SetLineWidth( getTextThickness( aField, drawingShadows ) ); m_gal->DrawRectangle( mapCoords( bbox.GetPosition() ), mapCoords( bbox.GetEnd() ) ); } else { - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER ); - m_gal->SetGlyphSize( VECTOR2D( aField->GetTextSize() ) ); - m_gal->SetFontItalic( aField->IsItalic() ); + TEXT_ATTRIBUTES attrs( aField->GetAttributes() ); + attrs.m_Halign = GR_TEXT_H_ALIGN_CENTER; + attrs.m_Valign = GR_TEXT_V_ALIGN_CENTER; + attrs.m_StrokeWidth = getTextThickness( aField, drawingShadows ); - strokeText( UnescapeString( aField->GetText() ), textpos, - aField->GetTextAngle().AsRadians() ); + strokeText( UnescapeString( aField->GetText() ), textpos, attrs ); } // Draw the umbilical line when in the schematic editor @@ -737,19 +760,16 @@ void SCH_PAINTER::draw( const LIB_TEXT *aText, int aLayer ) EDA_RECT bBox = aText->GetBoundingBox(); bBox.RevertYAxis(); VECTOR2D pos = mapCoords( bBox.Centre() ); - double orient = aText->GetTextAngle().AsRadians(); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER ); - m_gal->SetLineWidth( getTextThickness( aText, drawingShadows ) ); - m_gal->SetIsFill( false ); - m_gal->SetIsStroke( true ); + m_gal->SetFillColor( color ); m_gal->SetStrokeColor( color ); - m_gal->SetGlyphSize( VECTOR2D( aText->GetTextSize() ) ); - m_gal->SetFontBold( aText->IsBold() ); - m_gal->SetFontItalic( aText->IsItalic() ); - m_gal->SetFontUnderlined( false ); - strokeText( aText->GetText(), pos, orient ); + + TEXT_ATTRIBUTES attrs( aText->GetAttributes() ); + attrs.m_Halign = GR_TEXT_H_ALIGN_CENTER; + attrs.m_Valign = GR_TEXT_V_ALIGN_CENTER; + attrs.m_StrokeWidth = getTextThickness( aText, drawingShadows ); + + strokeText( aText->GetText(), pos, attrs ); } @@ -1039,7 +1059,7 @@ void SCH_PAINTER::draw( LIB_PIN *aPin, int aLayer ) if( m_schSettings.m_ShowPinsElectricalType ) { size [OUTSIDE] = std::max( aPin->GetNameTextSize() * 3 / 4, Millimeter2iu( 0.7 ) ); - thickness[OUTSIDE] = float( size[OUTSIDE] ) / 6.0F; + thickness[OUTSIDE] = float( size[OUTSIDE] ) / 8.0F; colour [OUTSIDE] = getRenderColor( aPin, LAYER_NOTES, drawingShadows ); text [OUTSIDE] = aPin->GetElectricalTypeName(); } @@ -1062,42 +1082,31 @@ void SCH_PAINTER::draw( LIB_PIN *aPin, int aLayer ) { float shadowWidth = getShadowWidth( aPin->IsBrightened() ); - if( eeconfig()->m_Selection.text_as_box ) - { - insideOffset -= thickness[INSIDE] / 2.0; - outsideOffset -= thickness[OUTSIDE] / 2.0; - aboveOffset -= thickness[ABOVE] + penWidth; - belowOffset -= thickness[BELOW] + penWidth; - } - for( float& t : thickness ) t += shadowWidth; - - insideOffset -= shadowWidth / 2.0; - outsideOffset -= shadowWidth / 2.0; } - auto setupDC = - [&]( int i ) - { - m_gal->SetGlyphSize( VECTOR2D( size[i], size[i] ) ); - m_gal->SetIsStroke( !( drawingShadows && eeconfig()->m_Selection.text_as_box ) ); - m_gal->SetLineWidth( thickness[i] ); - m_gal->SetStrokeColor( colour[i] ); - m_gal->SetIsFill( drawingShadows && eeconfig()->m_Selection.text_as_box ); - m_gal->SetFillColor( colour[i] ); - }; - auto drawText = - [&]( const wxString& aText, const VECTOR2D& aPos, double aAngle ) + [&]( int i, const VECTOR2D& aPos, GR_TEXT_H_ALIGN_T hAlign, GR_TEXT_V_ALIGN_T vAlign, + const EDA_ANGLE& aAngle ) { - if( aText.IsEmpty() ) + if( text[i].IsEmpty() ) return; + m_gal->SetStrokeColor( colour[i] ); + m_gal->SetFillColor( colour[i] ); + + TEXT_ATTRIBUTES attrs; + attrs.m_Size = VECTOR2D( size[i], size[i] ); + attrs.m_Halign = hAlign; + attrs.m_Valign = vAlign; + attrs.m_Angle = aAngle; + attrs.m_StrokeWidth = thickness[i]; + if( drawingShadows && eeconfig()->m_Selection.text_as_box ) - boxText( aText, aPos, aAngle ); + boxText( text[i], aPos, attrs ); else - strokeText( aText, aPos, aAngle ); + strokeText( text[i], aPos, attrs ); }; switch( orient ) @@ -1105,125 +1114,92 @@ void SCH_PAINTER::draw( LIB_PIN *aPin, int aLayer ) case PIN_LEFT: if( size[INSIDE] ) { - setupDC( INSIDE ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_RIGHT ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER ); - drawText( text[INSIDE], pos + VECTOR2D( -insideOffset - len, 0 ), 0 ); + drawText( INSIDE, pos + VECTOR2D( -insideOffset - len, 0 ), + GR_TEXT_H_ALIGN_RIGHT, GR_TEXT_V_ALIGN_CENTER, EDA_ANGLE::HORIZONTAL ); } if( size[OUTSIDE] ) { - setupDC( OUTSIDE ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_LEFT ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER ); - drawText( text[OUTSIDE], pos + VECTOR2D( outsideOffset, 0 ), 0 ); + drawText( OUTSIDE, pos + VECTOR2D( outsideOffset, 0 ), + GR_TEXT_H_ALIGN_LEFT, GR_TEXT_V_ALIGN_CENTER, EDA_ANGLE::HORIZONTAL ); } if( size[ABOVE] ) { - setupDC( ABOVE ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_BOTTOM ); - drawText( text[ABOVE], pos + VECTOR2D( -len / 2.0, -aboveOffset ), 0 ); + drawText( ABOVE, pos + VECTOR2D( -len / 2.0, -aboveOffset ), + GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_BOTTOM, EDA_ANGLE::HORIZONTAL ); } if( size[BELOW] ) { - setupDC( BELOW ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_TOP ); - drawText( text[BELOW], pos + VECTOR2D( -len / 2.0, belowOffset ), 0 ); + drawText( BELOW, pos + VECTOR2D( -len / 2.0, belowOffset ), + GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_TOP, EDA_ANGLE::HORIZONTAL ); } break; case PIN_RIGHT: if( size[INSIDE] ) { - setupDC( INSIDE ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_LEFT ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_LEFT ); - drawText( text[INSIDE], pos + VECTOR2D( insideOffset + len, 0 ), 0 ); + drawText( INSIDE, pos + VECTOR2D( insideOffset + len, 0 ), + GR_TEXT_H_ALIGN_LEFT, GR_TEXT_V_ALIGN_CENTER, EDA_ANGLE::HORIZONTAL ); } if( size[OUTSIDE] ) { - setupDC( OUTSIDE ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_RIGHT ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER ); - drawText( text[OUTSIDE], pos + VECTOR2D( -outsideOffset, 0 ), 0 ); + drawText( OUTSIDE, pos + VECTOR2D( -outsideOffset, 0 ), + GR_TEXT_H_ALIGN_RIGHT, GR_TEXT_V_ALIGN_CENTER, EDA_ANGLE::HORIZONTAL ); } if( size[ABOVE] ) { - setupDC( ABOVE ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_BOTTOM ); - drawText( text[ABOVE], pos + VECTOR2D( len / 2.0, -aboveOffset ), 0 ); + drawText( ABOVE, pos + VECTOR2D( len / 2.0, -aboveOffset ), + GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_BOTTOM, EDA_ANGLE::HORIZONTAL ); } if( size[BELOW] ) { - setupDC( BELOW ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_TOP ); - drawText( text[BELOW], pos + VECTOR2D( len / 2.0, belowOffset ), 0 ); + drawText( BELOW, pos + VECTOR2D( len / 2.0, belowOffset ), + GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_TOP, EDA_ANGLE::HORIZONTAL ); } break; case PIN_DOWN: if( size[INSIDE] ) { - setupDC( INSIDE ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_RIGHT ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER ); - drawText( text[INSIDE], pos + VECTOR2D( 0, insideOffset + len ), M_PI / 2 ); + drawText( INSIDE, pos + VECTOR2D( 0, insideOffset + len ), + GR_TEXT_H_ALIGN_RIGHT, GR_TEXT_V_ALIGN_CENTER, EDA_ANGLE::VERTICAL ); } if( size[OUTSIDE] ) { - setupDC( OUTSIDE ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_LEFT ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER ); - drawText( text[OUTSIDE], pos + VECTOR2D( 0, -outsideOffset ), M_PI / 2 ); + drawText( OUTSIDE, pos + VECTOR2D( 0, -outsideOffset ), + GR_TEXT_H_ALIGN_LEFT, GR_TEXT_V_ALIGN_CENTER, EDA_ANGLE::VERTICAL ); } if( size[ABOVE] ) { - setupDC( ABOVE ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_BOTTOM ); - drawText( text[ABOVE], pos + VECTOR2D( -aboveOffset, len / 2.0 ), M_PI / 2 ); + drawText( ABOVE, pos + VECTOR2D( -aboveOffset, len / 2.0 ), + GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_BOTTOM, EDA_ANGLE::VERTICAL ); } if( size[BELOW] ) { - setupDC( BELOW ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_TOP ); - drawText( text[BELOW], pos + VECTOR2D( belowOffset, len / 2.0 ), M_PI / 2 ); + drawText( BELOW, pos + VECTOR2D( belowOffset, len / 2.0 ), + GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_TOP, EDA_ANGLE::VERTICAL ); } break; case PIN_UP: if( size[INSIDE] ) { - setupDC( INSIDE ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_LEFT ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER ); - drawText( text[INSIDE], pos + VECTOR2D( 0, -insideOffset - len ), M_PI / 2 ); + drawText( INSIDE, pos + VECTOR2D( 0, -insideOffset - len ), + GR_TEXT_H_ALIGN_LEFT, GR_TEXT_V_ALIGN_CENTER, EDA_ANGLE::VERTICAL ); } if( size[OUTSIDE] ) { - setupDC( OUTSIDE ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_RIGHT ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER ); - drawText( text[OUTSIDE], pos + VECTOR2D( 0, outsideOffset ), M_PI / 2 ); + drawText( OUTSIDE, pos + VECTOR2D( 0, outsideOffset ), + GR_TEXT_H_ALIGN_RIGHT, GR_TEXT_V_ALIGN_CENTER, EDA_ANGLE::VERTICAL ); } if( size[ABOVE] ) { - setupDC( ABOVE ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_BOTTOM ); - drawText( text[ABOVE], pos + VECTOR2D( -aboveOffset, -len / 2.0 ), M_PI / 2 ); + drawText( ABOVE, pos + VECTOR2D( -aboveOffset, -len / 2.0 ), + GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_BOTTOM, EDA_ANGLE::VERTICAL ); } if( size[BELOW] ) { - setupDC( BELOW ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_TOP ); - drawText( text[BELOW], pos + VECTOR2D( belowOffset, -len / 2.0 ), M_PI / 2 ); + drawText( BELOW, pos + VECTOR2D( belowOffset, -len / 2.0 ), + GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_TOP, EDA_ANGLE::VERTICAL ); } break; @@ -1485,42 +1461,31 @@ void SCH_PAINTER::draw( const SCH_TEXT *aText, int aLayer ) return; } - m_gal->SetIsStroke( true ); - m_gal->SetLineWidth( getTextThickness( aText, drawingShadows ) ); m_gal->SetStrokeColor( color ); - m_gal->SetIsFill( drawingShadows && eeconfig()->m_Selection.text_as_box ); m_gal->SetFillColor( color ); - VECTOR2D text_offset = aText->GetTextPos() + aText->GetSchematicTextOffset( &m_schSettings ); + VECTOR2D textPos( aText->GetTextPos() ); + VECTOR2D text_offset = aText->GetSchematicTextOffset( &m_schSettings ); wxString shownText( aText->GetShownText() ); - if( drawingShadows ) + if( drawingShadows && eeconfig()->m_Selection.text_as_box ) { - if( eeconfig()->m_Selection.text_as_box ) - { - EDA_RECT bBox = aText->GetBoundingBox(); - bBox.RevertYAxis(); - m_gal->DrawRectangle( mapCoords( bBox.GetPosition() ), mapCoords( bBox.GetEnd() ) ); - return; - } - - float shadowWidth = getShadowWidth( aText->IsBrightened() ); - - switch( aText->GetLabelSpinStyle() ) - { - case LABEL_SPIN_STYLE::LEFT: text_offset.x += shadowWidth / 2.0; break; - case LABEL_SPIN_STYLE::UP: text_offset.y += shadowWidth / 2.0; break; - case LABEL_SPIN_STYLE::RIGHT: text_offset.x -= shadowWidth / 2.0; break; - case LABEL_SPIN_STYLE::BOTTOM: text_offset.y -= shadowWidth / 2.0; break; - } + EDA_RECT bBox = aText->GetBoundingBox(); + bBox.Offset( text_offset.x, text_offset.y ); + bBox.RevertYAxis(); + m_gal->SetIsStroke( true ); + m_gal->SetIsFill( true ); + m_gal->SetLineWidth( getTextThickness( aText, drawingShadows ) ); + m_gal->DrawRectangle( mapCoords( bBox.GetPosition() ), mapCoords( bBox.GetEnd() ) ); + return; } if( !shownText.IsEmpty() ) { - m_gal->SetTextAttributes( aText ); - m_gal->SetFontUnderlined( false ); + TEXT_ATTRIBUTES attrs = aText->GetAttributes(); + attrs.m_StrokeWidth = getTextThickness( aText, drawingShadows ); - strokeText( shownText, text_offset, aText->GetTextAngle().AsRadians() ); + strokeText( shownText, textPos + text_offset, attrs ); } } @@ -1708,30 +1673,26 @@ void SCH_PAINTER::draw( const SCH_FIELD *aField, int aLayer ) VECTOR2I textpos = bbox.Centre(); - m_gal->SetIsStroke( true ); - m_gal->SetLineWidth( getTextThickness( aField, drawingShadows ) ); m_gal->SetStrokeColor( color ); - m_gal->SetIsFill( drawingShadows && eeconfig()->m_Selection.text_as_box ); m_gal->SetFillColor( color ); if( drawingShadows && eeconfig()->m_Selection.text_as_box ) { bbox.RevertYAxis(); - m_gal->SetLineWidth( m_gal->GetLineWidth() / 2 ); + m_gal->SetIsStroke( true ); + m_gal->SetIsFill( true ); + m_gal->SetLineWidth( getTextThickness( aField, drawingShadows ) ); m_gal->DrawRectangle( mapCoords( bbox.GetPosition() ), mapCoords( bbox.GetEnd() ) ); } else { - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER ); - m_gal->SetIsFill( false ); - m_gal->SetGlyphSize( VECTOR2D( aField->GetTextSize() ) ); - m_gal->SetFontBold( aField->IsBold() ); - m_gal->SetFontItalic( aField->IsItalic() ); - m_gal->SetFontUnderlined( underline ); - m_gal->SetTextMirrored( aField->IsMirrored() ); + TEXT_ATTRIBUTES attributes = aField->GetAttributes(); + attributes.m_Halign = GR_TEXT_H_ALIGN_CENTER; + attributes.m_Valign = GR_TEXT_V_ALIGN_CENTER; + attributes.m_StrokeWidth = getTextThickness( aField, drawingShadows ); + attributes.m_Angle = orient; - strokeText( aField->GetShownText(), textpos, orient.AsRadians() ); + strokeText( aField->GetShownText(), textpos, attributes ); } // Draw the umbilical line diff --git a/eeschema/sch_painter.h b/eeschema/sch_painter.h index 464cc91b22..47ed4b4a33 100644 --- a/eeschema/sch_painter.h +++ b/eeschema/sch_painter.h @@ -193,8 +193,10 @@ private: bool setDeviceColors( const LIB_ITEM* aItem, int aLayer ); void triLine ( const VECTOR2D &a, const VECTOR2D &b, const VECTOR2D &c ); - void strokeText( const wxString& aText, const VECTOR2D& aPosition, double aRotationAngle ); - void boxText( const wxString& aText, const VECTOR2D& aPosition, double aAngle ); + void strokeText( const wxString& aText, const VECTOR2D& aPosition, + const TEXT_ATTRIBUTES& aAttributes ); + void boxText( const wxString& aText, const VECTOR2D& aPosition, + const TEXT_ATTRIBUTES& aAttrs ); private: SCH_RENDER_SETTINGS m_schSettings; diff --git a/eeschema/sch_text.cpp b/eeschema/sch_text.cpp index b678c44498..dcfbc500ec 100644 --- a/eeschema/sch_text.cpp +++ b/eeschema/sch_text.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -1682,7 +1681,7 @@ void SCH_GLOBALLABEL::CreateGraphicShape( const RENDER_SETTINGS* aRenderSettings int margin = GetLabelBoxExpansion( aRenderSettings ); int halfSize = ( GetTextHeight() / 2 ) + margin; int linewidth = GetPenWidth(); - int symb_len = LenSize( GetShownText(), linewidth ) + 2 * margin; + int symb_len = GetTextBox().GetWidth() + 2 * margin; int x = symb_len + linewidth + 3; int y = halfSize + linewidth + 3; @@ -1812,8 +1811,9 @@ const EDA_RECT SCH_HIERLABEL::GetBodyBoundingBox() const int y = GetTextPos().y; int height = GetTextHeight() + penWidth + margin; - int length = LenSize( GetShownText(), penWidth ) - + height; // add height for triangular shapes + int length = GetTextBox().GetWidth(); + + length += height; // add height for triangular shapes int dx, dy; diff --git a/include/basic_gal.h b/include/basic_gal.h index 8ce68e8a18..34a86b147d 100644 --- a/include/basic_gal.h +++ b/include/basic_gal.h @@ -26,10 +26,7 @@ #define BASIC_GAL_H #include - -#include #include -#include class PLOTTER; @@ -62,12 +59,12 @@ class BASIC_GAL: public KIGFX::GAL public: BASIC_GAL( KIGFX::GAL_DISPLAY_OPTIONS& aDisplayOptions ) : GAL( aDisplayOptions ), + m_callback( nullptr ), m_DC( nullptr ), m_Color( RED ), m_transform(), m_clipBox(), m_isClipped( false ), - m_callback( nullptr ), m_callbackData( nullptr ), m_plotter( nullptr ) { @@ -96,12 +93,12 @@ public: } /// Save the context. - virtual void Save() override + void Save() override { m_transformHistory.push( m_transform ); } - virtual void Restore() override + void Restore() override { m_transform = m_transformHistory.top(); m_transformHistory.pop(); @@ -112,9 +109,9 @@ public: * * @param aPointList is a list of 2D-Vectors containing the polyline points. */ - virtual void DrawPolyline( const std::deque& aPointList ) override; - - virtual void DrawPolyline( const VECTOR2D aPointList[], int aListSize ) override; + void DrawPolyline( const std::deque& aPointList ) override; + void DrawPolyline( const std::vector& aPointList ) override; + void DrawPolyline( const VECTOR2D aPointList[], int aListSize ) override; /** * Start and end points are defined as 2D-Vectors. @@ -122,14 +119,19 @@ public: * @param aStartPoint is the start point of the line. * @param aEndPoint is the end point of the line. */ - virtual void DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) override; + void DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) override; + + /** + * Draw a polygon representing an outline font glyph. + */ + void DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal ) override; /** * Translate the context. * * @param aTranslation is the translation vector. */ - virtual void Translate( const VECTOR2D& aTranslation ) override + void Translate( const VECTOR2D& aTranslation ) override { m_transform.m_moveOffset += aTranslation; } @@ -139,7 +141,7 @@ public: * * @param aAngle is the rotation angle in radians. */ - virtual void Rotate( double aAngle ) override + void Rotate( double aAngle ) override { m_transform.m_rotAngle = aAngle; m_transform.m_rotCenter = m_transform.m_moveOffset; @@ -151,26 +153,26 @@ private: // Apply the rotation/translation transform to aPoint const VECTOR2D transform( const VECTOR2D& aPoint ) const; + // When calling the draw functions outside a wxDC, to get the basic drawings lines / + // polylines ..., a callback function (used in DRC) to store coordinates of each segment: + void (* m_callback)( int x0, int y0, int xf, int yf, void* aData ); + public: - wxDC* m_DC; - COLOR4D m_Color; + wxDC* m_DC; + KIGFX::COLOR4D m_Color; private: - TRANSFORM_PRM m_transform; - std::stack m_transformHistory; + TRANSFORM_PRM m_transform; + std::stack m_transformHistory; // A clip box, to clip drawings in a wxDC (mandatory to avoid draw issues) - EDA_RECT m_clipBox; // The clip box - bool m_isClipped; // Allows/disallows clipping + EDA_RECT m_clipBox; // The clip box + bool m_isClipped; // Allows/disallows clipping - // When calling the draw functions outside a wxDC, to get the basic drawings - // lines / polylines ..., a callback function (used in DRC) to store - // coordinates of each segment: - void (* m_callback)( int x0, int y0, int xf, int yf, void* aData ); - void* m_callbackData; // a optional parameter for m_callback + void* m_callbackData; // a optional parameter for m_callback - // When calling the draw functions for plot, the plotter acts as a wxDC to plot basic items. - PLOTTER* m_plotter; + PLOTTER* m_plotter; // When calling the draw functions for plot, the + // plotter acts as a wxDC to plot basic items. }; diff --git a/include/eda_text.h b/include/eda_text.h index f44f0499a7..50d6dcbc6d 100644 --- a/include/eda_text.h +++ b/include/eda_text.h @@ -193,6 +193,8 @@ public: void CopyText( const EDA_TEXT& aSrc ); + const TEXT_ATTRIBUTES& GetAttributes() const { return m_attributes; } + /** * Helper function used in search and replace dialog. * @@ -292,14 +294,6 @@ public: */ virtual bool TextHitTest( const EDA_RECT& aRect, bool aContains, int aAccuracy = 0 ) const; - /** - * @return the text length in internal units. - * @param aLine the line of text to consider. For single line text, this parameter - * is always m_Text. - * @param aThickness the stroke width of the text. - */ - int LenSize( const wxString& aLine, int aThickness ) const; - /** * Useful in multiline texts to calculate the full text or a line area (for zones filling, * locate functions....) diff --git a/include/font/font.h b/include/font/font.h index 68d9a71c69..32976c0e9f 100644 --- a/include/font/font.h +++ b/include/font/font.h @@ -33,7 +33,9 @@ #include #include +#include #include +#include namespace KIGFX { @@ -105,17 +107,186 @@ public: virtual bool IsBold() const { return false; } virtual bool IsItalic() const { return false; } + static FONT* GetFont( const wxString& aFontName = "", bool aBold = false, + bool aItalic = false ); + static bool IsStroke( const wxString& aFontName ); + const wxString& Name() const; inline const char* NameAsToken() const { return Name().utf8_str().data(); } + /** + * Draw a string. + * + * @param aGal is the graphics context. + * @param aText is the text to be drawn. + * @param aPosition is the text position in world coordinates. + * @param aRotationAngle is the text rotation angle + * @return bounding box + */ + VECTOR2D Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, + const VECTOR2D& aOrigin, const TEXT_ATTRIBUTES& aAttrs ) const; + + VECTOR2D Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, + const TEXT_ATTRIBUTES& aAttributes ) const + { + return Draw( aGal, aText, aPosition, VECTOR2D( 0, 0 ), aAttributes ); + } + + virtual void DrawText( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, + const TEXT_ATTRIBUTES& aAttributes ) const; + + /** + * Draw a string. + * + * @param aGal is the graphics context + * @param aText is the text string + * @param aPosition is the text position in world coordinates + * @param aParse is true is aText should first be parsed for variable substition etc., + * otherwise false (default is false) + * @return bounding box + */ + VECTOR2D DrawString( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, + bool aParse, const TEXT_ATTRIBUTES& aAttributes ) const + { + return doDrawString( aGal, aText, aPosition, aParse, aAttributes ); + } + + VECTOR2D DrawString( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, + bool aParse, const EDA_ANGLE& aAngle ) const + { + TEXT_ATTRIBUTES attributes; + attributes.m_Angle = aAngle; + return doDrawString( aGal, aText, aPosition, aParse, attributes ); + } + + /** + * Compute the boundary limits of aText (the bounding box of all shapes). + * + * @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 ); + } + + /** + * Compute the vertical position of an overbar. This is the distance between the text + * baseline and the overbar. + */ + virtual double ComputeOverbarVerticalPosition( double aGlyphHeight ) const = 0; + + /** + * Compute the distance (interline) between 2 lines of text (for multiline texts). This is + * the distance between baselines, not the space between line bounding boxes. + */ + 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 polygon (outline font) or polyline (stroke font). + * + * @param aBoundingBox pointer to a BOX2I that will set to the bounding box, or nullptr + * @param aText text to convert to polygon/polyline + * @param aGlyphSize glyph size + * @param aPosition position of text (cursor position before this text) + * @param aAngle text angle + * @param aTextStyle text style flags + * @return text cursor position after this text + */ + virtual VECTOR2I GetTextAsPolygon( BOX2I* aBoundingBox, GLYPH_LIST& aGlyphs, + const UTF8& aText, const VECTOR2D& aGlyphSize, + const wxPoint& aPosition, const EDA_ANGLE& aAngle, + TEXT_STYLE_FLAGS aTextStyle ) const = 0; + protected: - wxString m_fontName; ///< Font name - wxString m_fontFileName; ///< Font file name + /** + * Returns number of lines for a given text. + * + * @param aText is the text to be checked. + * @return unsigned - The number of lines in aText. + */ + inline unsigned linesCount( const UTF8& aText ) const + { + if( aText.empty() ) + return 0; // std::count does not work well with empty strings + else + // aText.end() - 1 is to skip a newline character that is potentially at the end + return std::count( aText.begin(), aText.end() - 1, '\n' ) + 1; + } + + /** + * Draws a single line of text. Multiline texts should be split before using the + * function. + * + * @param aGal is a pointer to the graphics abstraction layer, or nullptr (nothing is drawn) + * @param aBoundingBox is a pointer to a BOX2I variable which will be set to the bounding box, + * or nullptr + * @param aText is the text to be drawn. + * @param aPosition is text position. + * @param aAngle is text angle. + * @param aIsMirrored is true if text should be drawn mirrored, false otherwise. + * @return new cursor position + */ + VECTOR2D drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const UTF8& aText, + const VECTOR2D& aPosition, const VECTOR2D& aGlyphSize, + const EDA_ANGLE& aAngle, bool aIsItalic, bool aIsMirrored ) const; + + /** + * Computes the bounding box for a single line of text. + * Multiline texts should be split before using the function. + * + * @param aBoundingBox is a pointer to a BOX2I variable which will be set to the bounding box, + * or nullptr + * @param aText is the text to be drawn. + * @param aPosition is text position. + * @param aGlyphSize is glyph size. + * @param aAngle is text angle. + * @return new cursor position + */ + VECTOR2D boundingBoxSingleLine( BOX2I* aBoundingBox, const UTF8& aText, + const VECTOR2D& aPosition, const VECTOR2D& aGlyphSize, + const EDA_ANGLE& aAngle, bool aIsItalic ) const; + + void getLinePositions( const UTF8& aText, const VECTOR2D& aPosition, wxArrayString& aStringList, + std::vector& aPositions, int& aLineCount, + std::vector& aBoundingBoxes, + const TEXT_ATTRIBUTES& aAttributes ) const; + + virtual VECTOR2D getBoundingBox( const UTF8& aString, const VECTOR2D& aGlyphSize, + TEXT_STYLE_FLAGS aTextStyle = 0 ) const = 0; + + VECTOR2D drawMarkup( BOX2I* aBoundingBox, GLYPH_LIST& aGlyphs, + const std::unique_ptr& aNode, const VECTOR2D& aPosition, + const VECTOR2D& aGlyphSize, const EDA_ANGLE& aAngle, + TEXT_STYLE_FLAGS aTextStyle, int aLevel = 0 ) const; private: - static FONT* s_defaultFont; - static std::map s_fontMap; + static FONT* getDefaultFont(); + + VECTOR2D doDrawString( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, + bool aParse, const TEXT_ATTRIBUTES& aAttrs ) const; + VECTOR2D getBoundingBox( const UTF8& aText, TEXT_STYLE_FLAGS aTextStyle, + const TEXT_ATTRIBUTES& aAttrs ) const; + +protected: + wxString m_fontName; ///< Font name + wxString m_fontFileName; ///< Font file name + +private: + static FONT* s_defaultFont; + + static std::map< std::tuple, FONT* > s_fontMap; }; + } //namespace KIFONT diff --git a/include/font/glyph.h b/include/font/glyph.h new file mode 100644 index 0000000000..bb929ca109 --- /dev/null +++ b/include/font/glyph.h @@ -0,0 +1,207 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2021 Ola Rinta-Koski + * Copyright (C) 2021 Kicad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef GLYPH_H +#define GLYPH_H + +#include +#include +#include +#include + +namespace KIFONT +{ +class GLYPH +{ +public: + virtual ~GLYPH() + {} + + virtual void AddPoint( const VECTOR2D& aPoint ) = 0; + + virtual void RaisePen() = 0; + + virtual void Finalize() = 0; + + virtual BOX2D BoundingBox() = 0; + + virtual std::shared_ptr Resize( const VECTOR2D& aGlyphSize ) const = 0; + + virtual std::shared_ptr Translate( const VECTOR2D& aOffset ) const = 0; + + virtual std::shared_ptr Mirror( bool aMirror, + const VECTOR2D& aMirrorOrigin = { 0, 0 } ) const = 0; + + virtual void Mirror( const VECTOR2D& aMirrorOrigin = { 0, 0 } ) = 0; + + virtual const SHAPE_POLY_SET& GetPolylist() const = 0; + + virtual const std::vector>& GetPoints() const = 0; + + virtual bool IsOutline() const { return false; } + virtual bool IsStroke() const { return false; } +}; + +typedef std::vector> GLYPH_LIST; + + +class OUTLINE_GLYPH : public GLYPH +{ +public: + OUTLINE_GLYPH( const SHAPE_POLY_SET& poly ) + { + m_polySet = poly; + } + + const SHAPE_POLY_SET& GetPolylist() const override { return m_polySet; } + + bool IsOutline() const override + { + return true; + } + + // + void AddPoint( const VECTOR2D& aPoint ) override + { + wxFAIL_MSG( "unimplemented" ); + } + + void RaisePen() override + { + wxFAIL_MSG( "unimplemented" ); + } + + void Finalize() override + { + wxFAIL_MSG( "unimplemented" ); + } + + BOX2D BoundingBox() override + { + wxFAIL_MSG( "unimplemented" ); + return BOX2D(); + } + + std::shared_ptr Resize( const VECTOR2D& aGlyphSize ) const override + { + wxFAIL_MSG( "unimplemented" ); + return nullptr; + } + + std::shared_ptr Translate( const VECTOR2D& aOffset ) const override + { + wxFAIL_MSG( "unimplemented" ); + return nullptr; + } + + std::shared_ptr Mirror( bool aMirror, + const VECTOR2D& aMirrorOrigin = { 0, 0 } ) const override + { + wxFAIL_MSG( "unimplemented" ); + return nullptr; + } + + void Mirror( const VECTOR2D& aMirrorOrigin = VECTOR2D( 0, 0 ) ) override + { + wxFAIL_MSG( "unimplemented" ); + } + + const std::vector>& GetPoints() const override + { + wxFAIL_MSG( "unimplemented" ); + return m_dummy; + } + +private: + SHAPE_POLY_SET m_polySet; + + // For unimplemented return values + std::vector> m_dummy; +}; + + +class STROKE_GLYPH : public GLYPH +{ +public: + STROKE_GLYPH() + {} + + STROKE_GLYPH( const STROKE_GLYPH& aGlyph ); + + void AddPoint( const VECTOR2D& aPoint ) override; + + void RaisePen() override; + + void Finalize() override; + + BOX2D BoundingBox() override; + + std::shared_ptr Resize( const VECTOR2D& aGlyphSize ) const override; + + std::shared_ptr Translate( const VECTOR2D& aOffset ) const override; + + std::shared_ptr Transform( const VECTOR2D& aGlyphSize, const VECTOR2D& aOffset, + double aTilt ); + + std::shared_ptr Mirror( bool aMirror, + const VECTOR2D& aMirrorOrigin = { 0, 0 } ) const override; + + void Mirror( const VECTOR2D& aMirrorOrigin = { 0, 0 } ) override; + + bool IsStroke() const override { return true; } + + // + const SHAPE_POLY_SET& GetPolylist() const override + { + wxFAIL_MSG( "unimplemented" ); + return m_dummy; + } + + const std::vector>& GetPoints() const override { return m_pointLists; } + +private: + void clearBoundingBox() + { + m_boundingBox.SetOrigin( 0, 0 ); + m_boundingBox.SetSize( 0, 0 ); + } + +private: + bool m_penIsDown = false; + std::vector> m_pointLists; + BOX2D m_boundingBox; + + // For unimplemented return values + SHAPE_POLY_SET m_dummy; +}; + + +typedef std::vector GLYPH_POINTS; +typedef std::vector GLYPH_POINTS_LIST; +typedef std::vector GLYPH_BOUNDING_BOX_LIST; + + +} // namespace KIFONT + +#endif // GLYPH_H diff --git a/include/font/stroke_font.h b/include/font/stroke_font.h new file mode 100644 index 0000000000..c7b31e38d3 --- /dev/null +++ b/include/font/stroke_font.h @@ -0,0 +1,118 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2013 CERN + * Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors. + * + * @author Maciej Suminski + * + * Stroke font class + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef STROKE_FONT_H +#define STROKE_FONT_H + +#include +#include +#include +#include +#include +#include + +namespace KIGFX +{ +class GAL; +} + +namespace KIFONT +{ +/** + * Implement a stroke font drawing. + * + * A stroke font is composed of lines. + */ +class STROKE_FONT : public FONT +{ +public: + STROKE_FONT(); + + bool IsStroke() const override { return true; } + + /** + * Load a stroke font. + * + * @param aFontName is the name of the font. If empty, the standard KiCad stroke font is + * loaded. + */ + 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. + */ + double ComputeOverbarVerticalPosition( double aGlyphHeight ) const override; + + /** + * Compute the distance (interline) between 2 lines of text (for multiline texts). This is + * the distance between baselines, not the space between line bounding boxes. + */ + 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 GetTextAsPolygon( BOX2I* aBoundingBox, GLYPH_LIST& aGlyphs, const UTF8& aText, + const VECTOR2D& aGlyphSize, const wxPoint& aPosition, + const EDA_ANGLE& aAngle, TEXT_STYLE_FLAGS aTextStyle ) const override; + +protected: + VECTOR2D getBoundingBox( const UTF8& aString, const VECTOR2D& aGlyphSize, + TEXT_STYLE_FLAGS aTextStyle ) const override; + +private: + /** + * Load the standard KiCad stroke font. + * + * @param aNewStrokeFont is the pointer to the font data. + * @param aNewStrokeFontSize is the size of the font data. + */ + void loadNewStrokeFont( const char* const aNewStrokeFont[], int aNewStrokeFontSize ); + +private: + const GLYPH_LIST* m_glyphs; ///< Glyph list + const GLYPH_BOUNDING_BOX_LIST* m_glyphBoundingBoxes; ///< Bounding boxes of the glyphs + double m_maxGlyphWidth; +}; + +} //namespace KIFONT + +#endif // STROKE_FONT_H diff --git a/include/gal/cairo/cairo_gal.h b/include/gal/cairo/cairo_gal.h index a0864cf45c..e97321e30b 100644 --- a/include/gal/cairo/cairo_gal.h +++ b/include/gal/cairo/cairo_gal.h @@ -109,6 +109,9 @@ public: void DrawPolygon( const SHAPE_POLY_SET& aPolySet, bool aStrokeTriangulation = false ) override; void DrawPolygon( const SHAPE_LINE_CHAIN& aPolySet ) override; + /// @copydoc GAL::DrawGlyph() + void DrawGlyph( const KIFONT::GLYPH& aPolySet, int aNth, int aTotal ) override; + /// @copydoc GAL::DrawCurve() void DrawCurve( const VECTOR2D& startPoint, const VECTOR2D& controlPointA, const VECTOR2D& controlPointB, const VECTOR2D& endPoint, @@ -272,6 +275,7 @@ protected: /// Drawing polygons & polylines is the same in Cairo, so here is the common code void drawPoly( const std::deque& aPointList ); + void drawPoly( const std::vector& aPointList ); void drawPoly( const VECTOR2D aPointList[], int aListSize ); void drawPoly( const SHAPE_LINE_CHAIN& aLineChain ); diff --git a/include/gal/graphics_abstraction_layer.h b/include/gal/graphics_abstraction_layer.h index e95d8f858c..6ff518c110 100644 --- a/include/gal/graphics_abstraction_layer.h +++ b/include/gal/graphics_abstraction_layer.h @@ -36,9 +36,9 @@ #include #include #include -#include #include #include +#include #include class SHAPE_LINE_CHAIN; @@ -171,6 +171,18 @@ public: DrawRectangle( aRect.GetOrigin(), aRect.GetEnd() ); } + /** + * Draw a polygon representing an outline font glyph. + */ + virtual void DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth = 0, int aTotal = 1 ) {}; + + void DrawGlyph( const std::shared_ptr& aGlyph, int aNth = 0, int aTotal = 1 ) + { + DrawGlyph( *aGlyph, aNth, aTotal ); + } + + void DrawGlyphs( const KIFONT::GLYPH_LIST& aGlyphs ); + /** * Draw a polygon. * @@ -338,23 +350,18 @@ public: // Text // ---- - const STROKE_FONT& GetStrokeFont() const - { - return m_strokeFont; - } - /** * Draw a vector type text using preloaded Newstroke font. * * @param aText is the text to be drawn. * @param aPosition is the text position in world coordinates. * @param aRotationAngle is the text rotation angle. + * @param aFont is the text font, or nullptr (defaults to newstroke) + * @param aLineSpacing is the line spacing for multiline text (defaults to 1.0) */ virtual void StrokeText( const wxString& aText, const VECTOR2D& aPosition, - double aRotationAngle ) - { - m_strokeFont.Draw( aText, aPosition, aRotationAngle ); - } + double aRotationAngle, KIFONT::FONT* aFont = nullptr, + double aLineSpacing = 1.0 ); /** * Draw a text using a bitmap font. It should be faster than StrokeText(), but can be used @@ -365,51 +372,18 @@ public: * @param aRotationAngle is the text rotation angle. */ virtual void BitmapText( const wxString& aText, const VECTOR2D& aPosition, - double aRotationAngle ) - { - // Fallback for implementations that don't implement bitmap text: use stroke font - - // Handle flipped view - if( m_globalFlipX ) - m_attributes.m_Mirrored = !m_attributes.m_Mirrored; - - // Bitmap font is slightly smaller and slightly heavier than the stroke font so we - // compensate a bit before stroking - float saveLineWidth = m_lineWidth; - VECTOR2D saveGlyphSize = m_attributes.m_Size; - { - m_lineWidth *= 1.2f; - m_attributes.m_Size = m_attributes.m_Size * 0.8; - - StrokeText( aText, aPosition, aRotationAngle ); - } - m_lineWidth = saveLineWidth; - m_attributes.m_Size = saveGlyphSize; - - if( m_globalFlipX ) - m_attributes.m_Mirrored = !m_attributes.m_Mirrored; - } + double aRotationAngle ); /** - * Compute the X and Y size of a given text. The text is expected to be a only one line text. - * - * @param aText is the text string (one line). - * @return is the text size. + * Loads attributes of the text (bold/italic/underline/mirrored and so on). */ - VECTOR2D GetTextLineSize( const UTF8& aText ) const; - - /** - * Loads attributes of the given text (bold/italic/underline/mirrored and so on). - * - * @param aText is the text item. - */ - virtual void SetTextAttributes( const EDA_TEXT* aText ); + virtual void SetTextAttributes( const TEXT_ATTRIBUTES& aAttributes ); /** * Reset text attributes to default styling. * - * Normally, custom attributes will be set individually after this, - * otherwise you can use SetTextAttributes() + * Normally, custom attributes will be set individually after this, otherwise you can use + * SetTextAttributes() */ void ResetTextAttributes(); @@ -1083,9 +1057,6 @@ protected: bool m_fullscreenCursor; ///< Shape of the cursor (fullscreen or small cross) VECTOR2D m_cursorPosition; ///< Current cursor position (world coordinates) - STROKE_FONT m_strokeFont; ///< Instance of object that stores information - ///< about how to draw texts - KICURSOR m_currentNativeCursor; ///< Current cursor private: diff --git a/include/gal/opengl/opengl_gal.h b/include/gal/opengl/opengl_gal.h index 7970daa9e6..05acbfb078 100644 --- a/include/gal/opengl/opengl_gal.h +++ b/include/gal/opengl/opengl_gal.h @@ -140,6 +140,7 @@ public: /// @copydoc GAL::DrawPolyline() void DrawPolyline( const std::deque& aPointList ) override; + void DrawPolyline( const std::vector& aPointList ) override; void DrawPolyline( const VECTOR2D aPointList[], int aListSize ) override; void DrawPolyline( const SHAPE_LINE_CHAIN& aLineChain ) override; @@ -149,6 +150,9 @@ public: void DrawPolygon( const SHAPE_POLY_SET& aPolySet, bool aStrokeTriangulation = false ) override; void DrawPolygon( const SHAPE_LINE_CHAIN& aPolySet ) override; + /// @copydoc GAL::DrawGlyph() + virtual void DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal ) override; + /// @copydoc GAL::DrawCurve() void DrawCurve( const VECTOR2D& startPoint, const VECTOR2D& controlPointA, const VECTOR2D& controlPointB, const VECTOR2D& endPoint, diff --git a/include/gal/stroke_font.h b/include/gal/stroke_font.h deleted file mode 100644 index d24f77da7f..0000000000 --- a/include/gal/stroke_font.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * This program source code file is part of KICAD, a free EDA CAD application. - * - * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de - * Copyright (C) 2013 CERN - * Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors. - * - * @author Maciej Suminski - * - * Stroke font class - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#ifndef STROKE_FONT_H_ -#define STROKE_FONT_H_ - -#include -#include - -#include - -#include - -#include - -namespace KIGFX -{ -class GAL; - -typedef std::vector*> GLYPH; -typedef std::vector GLYPH_LIST; - -/** - * Implement a stroke font drawing. - * - * A stroke font is composed of lines. - */ -class STROKE_FONT -{ - friend class GAL; - -public: - /// Constructor - STROKE_FONT( GAL* aGal ); - - /** - * Load the new stroke font. - * - * @param aNewStrokeFont is the pointer to the font data. - * @param aNewStrokeFontSize is the size of the font data. - * @return True, if the font was successfully loaded, else false. - */ - bool LoadNewStrokeFont( const char* const aNewStrokeFont[], int aNewStrokeFontSize ); - - /** - * Draw a string. - * - * @param aText is the text to be drawn. - * @param aPosition is the text position in world coordinates. - * @param aRotationAngle is the text rotation angle in radians. - */ - void Draw( const UTF8& aText, const VECTOR2D& aPosition, double aRotationAngle ); - - /** - * Changes Graphics Abstraction Layer used for drawing items for a new one. - * - * @param aGal is the new GAL instance. - */ - void SetGAL( GAL* aGal ) - { - m_gal = aGal; - } - - /** - * 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 ComputeStringBoundaryLimits( const UTF8& aText, const VECTOR2D& aGlyphSize, - double aGlyphThickness ) const; - - /** - * Compute the vertical position of an overbar, sometimes used in texts. - * - * This is the distance between the text base line and the overbar. - * - * @param aGlyphHeight is the height (vertical size) of the text. - * @return the relative position of the overbar axis. - */ - double ComputeOverbarVerticalPosition( double aGlyphHeight ) const; - - /** - * Compute the distance (interline) between 2 lines of text (for multiline texts). - * - * @param aGlyphHeight is the height (vertical size) of the text. - * @return the interline. - */ - static double GetInterline( double aGlyphHeight ); - -private: - /** - * Compute the X and Y size of a given text. The text is expected to be - * a only one line text. - * - * @param aText is the text string (one line). - * @return the text size. - */ - VECTOR2D computeTextLineSize( const UTF8& aText ) const; - - /** - * Compute the vertical position of an overbar, sometimes used in texts. - * This is the distance between the text base line and the overbar. - * @return the relative position of the overbar axis. - */ - double computeOverbarVerticalPosition() const; - double computeUnderlineVerticalPosition() const; - - /** - * Compute the bounding box of a given glyph. - * - * @param aGlyph is the glyph. - * @param aGlyphWidth is the x-component of the bounding box size. - * @return is the complete bounding box size. - */ - BOX2D computeBoundingBox( const GLYPH* aGlyph, double aGlyphWidth ) const; - - /** - * Draw a single line of text. Multiline texts should be split before using the - * function. - * - * @param aText is the text to be drawn. - */ - void drawSingleLineText( const UTF8& aText ); - - /** - * Returns number of lines for a given text. - * - * @param aText is the text to be checked. - * @return unsigned - The number of lines in aText. - */ - inline unsigned linesCount( const UTF8& aText ) const - { - if( aText.empty() ) - return 0; // std::count does not work well with empty strings - else - // aText.end() - 1 is to skip a newline character that is potentially at the end - return std::count( aText.begin(), aText.end() - 1, '\n' ) + 1; - } - - GAL* m_gal; ///< Pointer to the GAL - const GLYPH_LIST* m_glyphs; ///< Glyph list - const std::vector* m_glyphBoundingBoxes; ///< Bounding boxes of the glyphs - - ///< Factor that determines relative vertical position of the overbar. - static const double OVERBAR_POSITION_FACTOR; - static const double UNDERLINE_POSITION_FACTOR; - - ///< Factor that determines relative line width for bold text. - static const double BOLD_FACTOR; - - ///< Scale factor for a glyph - static const double STROKE_FONT_SCALE; - - ///< Tilt factor for italic style (the is is the scaling factor - ///< on dY relative coordinates to give a tilt shape - static const double ITALIC_TILT; - - ///< Factor that determines the pitch between 2 lines. - static const double INTERLINE_PITCH_RATIO; -}; -} // namespace KIGFX - -#endif // STROKE_FONT_H_ diff --git a/include/gr_text.h b/include/gr_text.h index 74ef06092c..c4e3b9f3d3 100644 --- a/include/gr_text.h +++ b/include/gr_text.h @@ -70,19 +70,22 @@ int Clamp_Text_PenSize( int aPenSize, const VECTOR2I& aSize, bool aBold = true ) * @return the "best" value for a pen size to draw/plot a bold text. */ int GetPenSizeForBold( int aTextSize ); +int GetPenSizeForBold( const wxSize& aTextSize ); /** * @param aTextSize = the char size (height or width). * @return the "best" value for a pen size to draw/plot a non-bold text. */ int GetPenSizeForNormal( int aTextSize ); +int GetPenSizeForNormal( const wxSize& aTextSize ); /** * The full X size is GraphicTextWidth + the thickness of graphic lines. * * @return the X size of the graphic text. */ -int GraphicTextWidth( const wxString& aText, const VECTOR2I& aSize, bool italic, bool bold ); +int GraphicTextWidth( const wxString& aText, KIFONT::FONT* aFont, const VECTOR2I& aSize, + bool italic, bool bold ); /** * Draw a graphic text (like footprint text) diff --git a/include/markup_parser.h b/include/markup_parser.h index a9d3e8a077..caeec62115 100644 --- a/include/markup_parser.h +++ b/include/markup_parser.h @@ -107,9 +107,6 @@ using selector = parse_tree::selector< Rule, subscript, overbar>>; -typedef std::unique_ptr> MARKUP_NODE; - - class MARKUP_PARSER { public: @@ -117,7 +114,7 @@ public: in( source, "from_input" ) {} - MARKUP_NODE Parse(); + std::unique_ptr Parse(); private: string_input<> in; @@ -125,6 +122,5 @@ private: } // namespace MARKUP -std::ostream& operator<<( std::ostream& os, const MARKUP::MARKUP_NODE& node ); #endif //MARKUP_PARSER_H diff --git a/pcbnew/autorouter/ar_autoplacer.cpp b/pcbnew/autorouter/ar_autoplacer.cpp index 2eb62bef98..8c29efdb23 100644 --- a/pcbnew/autorouter/ar_autoplacer.cpp +++ b/pcbnew/autorouter/ar_autoplacer.cpp @@ -57,25 +57,6 @@ #define CELL_IS_ZONE 0x80 /* Area and auto-placement: cell available */ -/* Penalty (cost) for CntRot90 and CntRot180: - * CntRot90 and CntRot180 are from 0 (rotation allowed) to 10 (rotation not allowed) - */ -static const double OrientationPenalty[11] = -{ - 2.0, // CntRot = 0 rotation prohibited - 1.9, // CntRot = 1 - 1.8, // CntRot = 2 - 1.7, // CntRot = 3 - 1.6, // CntRot = 4 - 1.5, // CntRot = 5 - 1.4, // CntRot = 5 - 1.3, // CntRot = 7 - 1.2, // CntRot = 8 - 1.1, // CntRot = 9 - 1.0 // CntRot = 10 rotation authorized, no penalty -}; - - AR_AUTOPLACER::AR_AUTOPLACER( BOARD* aBoard ) { m_board = aBoard; diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index f4068de0fd..29cb3b5451 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -1548,6 +1548,21 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer ) } +void PCB_PAINTER::strokeText( const wxString& aText, const VECTOR2D& aPosition, + const TEXT_ATTRIBUTES& aAttrs ) +{ + KIFONT::FONT* font = aAttrs.m_Font; + + if( !font ) + font = KIFONT::FONT::GetFont( wxEmptyString, aAttrs.m_Bold, aAttrs.m_Italic ); + + m_gal->SetIsFill( font->IsOutline() ); + m_gal->SetIsStroke( font->IsStroke() ); + + font->Draw( m_gal, aText, aPosition, aAttrs ); +} + + void PCB_PAINTER::draw( const PCB_TEXT* aText, int aLayer ) { wxString shownText( aText->GetShownText() ); @@ -1556,19 +1571,19 @@ void PCB_PAINTER::draw( const PCB_TEXT* aText, int aLayer ) return; const COLOR4D& color = m_pcbSettings.GetColor( aText, aText->GetLayer() ); - VECTOR2D position( aText->GetTextPos().x, aText->GetTextPos().y ); bool outline_mode = !pcbconfig()->m_Display.m_DisplayTextFill; - if( outline_mode ) - m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth ); - else - m_gal->SetLineWidth( getLineThickness( aText->GetEffectiveTextPenWidth() ) ); - m_gal->SetStrokeColor( color ); - m_gal->SetIsFill( false ); - m_gal->SetIsStroke( true ); - m_gal->SetTextAttributes( aText ); - m_gal->StrokeText( shownText, position, aText->GetTextAngle().AsRadians() ); + m_gal->SetFillColor( color ); + + TEXT_ATTRIBUTES attrs = aText->GetAttributes(); + + if( outline_mode ) + attrs.m_StrokeWidth = m_pcbSettings.m_outlineWidth; + else + attrs.m_StrokeWidth = getLineThickness( aText->GetEffectiveTextPenWidth() ); + + strokeText( shownText, aText->GetTextPos(), attrs ); } @@ -1580,26 +1595,28 @@ void PCB_PAINTER::draw( const FP_TEXT* aText, int aLayer ) return; const COLOR4D& color = m_pcbSettings.GetColor( aText, aLayer ); - VECTOR2D position( aText->GetTextPos().x, aText->GetTextPos().y ); bool outline_mode = !pcbconfig()->m_Display.m_DisplayTextFill; - if( outline_mode ) - m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth ); - else - m_gal->SetLineWidth( getLineThickness( aText->GetEffectiveTextPenWidth() ) ); - m_gal->SetStrokeColor( color ); - m_gal->SetIsFill( false ); - m_gal->SetIsStroke( true ); - m_gal->SetTextAttributes( aText ); - m_gal->StrokeText( shownText, position, aText->GetDrawRotation().AsRadians() ); + m_gal->SetFillColor( color ); + + TEXT_ATTRIBUTES attrs = aText->GetAttributes(); + + attrs.m_Angle = aText->GetDrawRotation(); + + if( outline_mode ) + attrs.m_StrokeWidth = m_pcbSettings.m_outlineWidth; + else + attrs.m_StrokeWidth = getLineThickness( aText->GetEffectiveTextPenWidth() ); + + strokeText( shownText, aText->GetTextPos(), attrs ); // Draw the umbilical line if( aText->IsSelected() ) { m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth ); m_gal->SetStrokeColor( m_pcbSettings.GetColor( nullptr, LAYER_ANCHOR ) ); - m_gal->DrawLine( position, aText->GetParent()->GetPosition() ); + m_gal->DrawLine( aText->GetTextPos(), aText->GetParent()->GetPosition() ); } } @@ -1826,14 +1843,14 @@ void PCB_PAINTER::draw( const PCB_DIMENSION_BASE* aDimension, int aLayer ) // Draw text const PCB_TEXT& text = aDimension->Text(); VECTOR2D position( text.GetTextPos().x, text.GetTextPos().y ); + TEXT_ATTRIBUTES attrs = text.GetAttributes(); if( outline_mode ) - m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth ); + attrs.m_StrokeWidth = m_pcbSettings.m_outlineWidth; else - m_gal->SetLineWidth( getLineThickness( text.GetEffectiveTextPenWidth() ) ); + attrs.m_StrokeWidth = getLineThickness( text.GetEffectiveTextPenWidth() ); - m_gal->SetTextAttributes( &text ); - m_gal->StrokeText( text.GetShownText(), position, text.GetTextAngle().AsRadians() ); + strokeText( text.GetShownText(), position, attrs ); } diff --git a/pcbnew/pcb_painter.h b/pcbnew/pcb_painter.h index 4ef274197e..2b4bedf139 100644 --- a/pcbnew/pcb_painter.h +++ b/pcbnew/pcb_painter.h @@ -53,6 +53,7 @@ class PCB_TARGET; class PCB_MARKER; class NET_SETTINGS; class NETINFO_LIST; +class TEXT_ATTRIBUTES; namespace KIGFX { @@ -200,6 +201,9 @@ protected: */ virtual int getDrillSize( const PCB_VIA* aVia ) const; + void strokeText( const wxString& aText, const VECTOR2D& aPosition, + const TEXT_ATTRIBUTES& aAttrs ); + protected: PCB_RENDER_SETTINGS m_pcbSettings; diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp index 4250f6c367..38b6ac32a1 100644 --- a/pcbnew/tools/drawing_tool.cpp +++ b/pcbnew/tools/drawing_tool.cpp @@ -574,14 +574,19 @@ int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent ) m_controls->ForceCursorPosition( true, m_controls->GetCursorPosition() ); PCB_LAYER_ID layer = m_frame->GetActiveLayer(); + wxSize textSize = dsnSettings.GetTextSize( layer ); + int thickness = dsnSettings.GetTextThickness( layer ); + // Init the new item attributes if( m_isFootprintEditor ) { FP_TEXT* fpText = new FP_TEXT( (FOOTPRINT*) m_frame->GetModel() ); fpText->SetLayer( layer ); - fpText->SetTextSize( dsnSettings.GetTextSize( layer ) ); - fpText->SetTextThickness( dsnSettings.GetTextThickness( layer ) ); + fpText->SetTextSize( textSize ); + fpText->SetTextThickness( thickness ); + fpText->SetBold( abs( thickness - GetPenSizeForBold( textSize ) ) < + abs( thickness - GetPenSizeForNormal( textSize ) ) ); fpText->SetItalic( dsnSettings.GetTextItalic( layer ) ); fpText->SetKeepUpright( dsnSettings.GetTextUpright( layer ) ); fpText->SetTextPos( (wxPoint) cursorPos ); @@ -619,8 +624,10 @@ int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent ) if( IsBackLayer( layer ) ) pcbText->SetMirrored( true ); - pcbText->SetTextSize( dsnSettings.GetTextSize( layer ) ); - pcbText->SetTextThickness( dsnSettings.GetTextThickness( layer ) ); + pcbText->SetTextSize( textSize ); + pcbText->SetTextThickness( thickness ); + pcbText->SetBold( abs( thickness - GetPenSizeForBold( textSize ) ) < + abs( thickness - GetPenSizeForNormal( textSize ) ) ); pcbText->SetItalic( dsnSettings.GetTextItalic( layer ) ); pcbText->SetTextPos( (wxPoint) cursorPos ); diff --git a/qa/pns/pns_log_viewer.cpp b/qa/pns/pns_log_viewer.cpp index 34dc5be66e..9dd4ec5256 100644 --- a/qa/pns/pns_log_viewer.cpp +++ b/qa/pns/pns_log_viewer.cpp @@ -67,7 +67,8 @@ void LABEL_MANAGER::Add( VECTOR2I target, std::string msg, COLOR4D color ) lbl.m_color = color; m_gal->SetGlyphSize( VECTOR2D( m_textSize, m_textSize ) ); - VECTOR2I textDims = m_gal->GetTextLineSize( msg ); + KIFONT::FONT* strokeFont = KIFONT::GetFont( wxEmptyString ); + VECTOR2I textDims = strokeFont->StringBoundaryLimits( m_gal, msg ); lbl.m_bbox.SetOrigin( lbl.m_target - textDims - VECTOR2I( m_textSize, m_textSize ) ); lbl.m_bbox.SetSize( textDims );