From 7e5fedef6a38cd10fb774dbef245d998335d3622 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Mon, 17 Apr 2023 15:15:29 +0100 Subject: [PATCH] Markup fixes. 1) allow escape sequences inside markup sequences 2) keep overbar when inside nested markup 3) always place overbar at full height, not sub/superscript height Fixes https://gitlab.com/kicad/code/kicad/issues/14553 Fixes https://gitlab.com/kicad/code/kicad/issues/13449 --- common/eda_text.cpp | 20 ++++++-- common/font/font.cpp | 58 +++++++++++++++++++-- common/font/outline_font.cpp | 99 ++++-------------------------------- common/font/stroke_font.cpp | 57 +-------------------- common/string_utils.cpp | 40 ++++++++------- eeschema/sch_painter.cpp | 12 ++--- include/font/outline_font.h | 12 ++--- include/markup_parser.h | 28 ++++++---- pcbnew/pcb_painter.cpp | 10 ++++ 9 files changed, 142 insertions(+), 194 deletions(-) diff --git a/common/eda_text.cpp b/common/eda_text.cpp index fb177e6409..0bdfc3519c 100644 --- a/common/eda_text.cpp +++ b/common/eda_text.cpp @@ -37,6 +37,7 @@ #include #include // for EDA_TEXT, TEXT_EFFECTS, GR_TEXT_VJUSTIF... #include // for COLOR4D, COLOR4D::BLACK +#include #include #include // for UnescapeString #include // for KiROUND @@ -119,8 +120,10 @@ EDA_TEXT::EDA_TEXT( const EDA_TEXT& aText ) : for( const std::unique_ptr& glyph : aText.m_render_cache ) { - KIFONT::OUTLINE_GLYPH* outline_glyph = static_cast( glyph.get() ); - m_render_cache.emplace_back( std::make_unique( *outline_glyph ) ); + if( KIFONT::OUTLINE_GLYPH* outline = dynamic_cast( glyph.get() ) ) + m_render_cache.emplace_back( std::make_unique( *outline ) ); + else if( KIFONT::STROKE_GLYPH* stroke = dynamic_cast( glyph.get() ) ) + m_render_cache.emplace_back( std::make_unique( *stroke ) ); } m_bounding_box_cache_valid = aText.m_bounding_box_cache_valid; @@ -151,8 +154,10 @@ EDA_TEXT& EDA_TEXT::operator=( const EDA_TEXT& aText ) for( const std::unique_ptr& glyph : aText.m_render_cache ) { - KIFONT::OUTLINE_GLYPH* outline_glyph = static_cast( glyph.get() ); - m_render_cache.emplace_back( std::make_unique( *outline_glyph ) ); + if( KIFONT::OUTLINE_GLYPH* outline = dynamic_cast( glyph.get() ) ) + m_render_cache.emplace_back( std::make_unique( *outline ) ); + else if( KIFONT::STROKE_GLYPH* stroke = dynamic_cast( glyph.get() ) ) + m_render_cache.emplace_back( std::make_unique( *stroke ) ); } m_bounding_box_cache_valid = aText.m_bounding_box_cache_valid; @@ -393,7 +398,12 @@ void EDA_TEXT::Offset( const VECTOR2I& aOffset ) m_pos += aOffset; for( std::unique_ptr& glyph : m_render_cache ) - static_cast( glyph.get() )->Move( aOffset ); + { + if( KIFONT::OUTLINE_GLYPH* outline = dynamic_cast( glyph.get() ) ) + outline->Move( aOffset ); + else if( KIFONT::STROKE_GLYPH* stroke = dynamic_cast( glyph.get() ) ) + glyph = stroke->Transform( { 1.0, 1.0 }, aOffset, 0, ANGLE_0, false, { 0, 0 } ); + } m_bounding_box_cache_valid = false; } diff --git a/common/font/font.cpp b/common/font/font.cpp index d5ccaf9da6..77346033da 100644 --- a/common/font/font.cpp +++ b/common/font/font.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2021 Ola Rinta-Koski - * Copyright (C) 2021-2022 Kicad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2021-2023 Kicad Developers, see AUTHORS.txt for contributors. * * Font abstract base class * @@ -264,6 +264,8 @@ VECTOR2I drawMarkup( BOX2I* aBoundingBox, std::vector>* a bool aMirror, const VECTOR2I& aOrigin, TEXT_STYLE_FLAGS aTextStyle ) { VECTOR2I nextPosition = aPosition; + bool drawUnderline = false; + bool drawOverbar = false; if( aNode ) { @@ -272,12 +274,12 @@ VECTOR2I drawMarkup( BOX2I* aBoundingBox, std::vector>* a if( !aNode->is_root() ) { if( aNode->isSubscript() ) - textStyle = TEXT_STYLE::SUBSCRIPT; + textStyle |= TEXT_STYLE::SUBSCRIPT; else if( aNode->isSuperscript() ) - textStyle = TEXT_STYLE::SUPERSCRIPT; + textStyle |= TEXT_STYLE::SUPERSCRIPT; if( aNode->isOverbar() ) - textStyle |= TEXT_STYLE::OVERBAR; + drawOverbar = true; if( aNode->has_content() ) { @@ -291,6 +293,10 @@ VECTOR2I drawMarkup( BOX2I* aBoundingBox, std::vector>* a aBoundingBox->Merge( bbox ); } } + else if( aTextStyle & TEXT_STYLE::UNDERLINE ) + { + drawUnderline = true; + } for( const std::unique_ptr& child : aNode->children ) { @@ -299,6 +305,50 @@ VECTOR2I drawMarkup( BOX2I* aBoundingBox, std::vector>* a } } + if( drawUnderline ) + { + // Shorten the bar a little so its rounded ends don't make it over-long + double barTrim = aSize.x * 0.1; + double barOffset = aFont->ComputeUnderlineVerticalPosition( aSize.y ); + + VECTOR2D barStart( aPosition.x + barTrim, aPosition.y - barOffset ); + VECTOR2D barEnd( nextPosition.x - barTrim, nextPosition.y - barOffset ); + + if( aGlyphs ) + { + STROKE_GLYPH barGlyph; + + barGlyph.AddPoint( barStart ); + barGlyph.AddPoint( barEnd ); + barGlyph.Finalize(); + + aGlyphs->push_back( barGlyph.Transform( { 1.0, 1.0 }, { 0, 0 }, false, aAngle, aMirror, + aOrigin ) ); + } + } + + if( drawOverbar ) + { + // Shorten the bar a little so its rounded ends don't make it over-long + double barTrim = aSize.x * 0.1; + double barOffset = aFont->ComputeOverbarVerticalPosition( aSize.y ); + + VECTOR2D barStart( aPosition.x + barTrim, aPosition.y - barOffset ); + VECTOR2D barEnd( nextPosition.x - barTrim, nextPosition.y - barOffset ); + + if( aGlyphs ) + { + STROKE_GLYPH barGlyph; + + barGlyph.AddPoint( barStart ); + barGlyph.AddPoint( barEnd ); + barGlyph.Finalize(); + + aGlyphs->push_back( barGlyph.Transform( { 1.0, 1.0 }, { 0, 0 }, false, aAngle, aMirror, + aOrigin ) ); + } + } + return nextPosition; } diff --git a/common/font/outline_font.cpp b/common/font/outline_font.cpp index a39b8e09d1..cbc33ee493 100644 --- a/common/font/outline_font.cpp +++ b/common/font/outline_font.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2021 Ola Rinta-Koski - * Copyright (C) 2021-2022 Kicad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2021-2023 Kicad Developers, see AUTHORS.txt for contributors. * * Outline font class * @@ -313,22 +313,6 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector>* aGlyphs, const wxString& aText, const VECTOR2I& aSize, const VECTOR2I& aPosition, const EDA_ANGLE& aAngle, @@ -338,14 +322,15 @@ VECTOR2I OUTLINE_FONT::getTextAsGlyphs( BOX2I* aBBox, std::vector guard( m_freeTypeMutex ); return getTextAsGlyphsUnlocked( aBBox, aGlyphs, aText, aSize, aPosition, aAngle, aMirror, - aOrigin, aTextStyle ); + aOrigin, aTextStyle ); } -VECTOR2I OUTLINE_FONT::getTextAsGlyphsUnlocked( BOX2I* aBBox, std::vector>* aGlyphs, - const wxString& aText, const VECTOR2I& aSize, - const VECTOR2I& aPosition, const EDA_ANGLE& aAngle, - bool aMirror, const VECTOR2I& aOrigin, - TEXT_STYLE_FLAGS aTextStyle ) const +VECTOR2I OUTLINE_FONT::getTextAsGlyphsUnlocked( BOX2I* aBBox, + std::vector>* aGlyphs, + const wxString& aText, const VECTOR2I& aSize, + const VECTOR2I& aPosition, const EDA_ANGLE& aAngle, + bool aMirror, const VECTOR2I& aOrigin, + TEXT_STYLE_FLAGS aTextStyle ) const { VECTOR2D glyphSize = aSize; FT_Face face = m_face; @@ -478,75 +463,9 @@ VECTOR2I OUTLINE_FONT::getTextAsGlyphsUnlocked( BOX2I* aBBox, std::vectorsize->metrics.ascender * GLYPH_SIZE_SCALER ); - int height = abs( face->size->metrics.height * GLYPH_SIZE_SCALER ); int descender = abs( face->size->metrics.descender * GLYPH_SIZE_SCALER ); VECTOR2I extents( cursor.x * scaleFactor.x, ( ascender + descender ) * abs( scaleFactor.y ) ); - // Font metrics don't include all descenders and diacriticals, so beef them up just a little. - extents.y *= 1.05; - - if( IsOverbar( aTextStyle ) ) - { - std::vector> underscoreGlyphs; - - getTextAsGlyphsUnlocked( nullptr, &underscoreGlyphs, wxT( "_" ), aSize, { 0, 0 }, ANGLE_0, false, - { 0, 0 }, aTextStyle & ~TEXT_STYLE::OVERBAR ); - - OUTLINE_GLYPH* underscoreGlyph = static_cast( underscoreGlyphs[0].get() ); - BOX2I underscoreBBox; - - for( const VECTOR2I& pt : underscoreGlyph->Outline( 0 ).CPoints() ) - underscoreBBox.Merge( pt ); - - int barThickness = underscoreBBox.GetHeight(); - - // Shorten the bar a little so its rounded ends don't make it over-long - double barTrim = barThickness / 2.0; - double barOffset = getOverbarOffset( ascender, height, barThickness / scaleFactor.y ); - - VECTOR2I topLeft( aPosition ); - VECTOR2I topRight( aPosition ); - - topLeft.y += ascender * scaleFactor.y * ( 1.0 + barOffset ); - topRight.y = topLeft.y; - - topLeft.x += barTrim; - topRight.x += extents.x - barTrim; - - extents.y *= ( 1.0 + barOffset + barOffset ); - extents.x += barTrim; - - if( IsItalic() ) - { - topLeft.x += aSize.y * ITALIC_TILT; - topRight.x += aSize.y * ITALIC_TILT; - extents.x += aSize.y * ITALIC_TILT; - } - - if( aMirror ) - { - topLeft.x = aOrigin.x - ( topLeft.x - aOrigin.x ); - topRight.x = aOrigin.x - ( topRight.x - aOrigin.x ); - } - - if( !aAngle.IsZero() ) - { - RotatePoint( topLeft, aOrigin, aAngle ); - RotatePoint( topRight, aOrigin, aAngle ); - } - - if( aGlyphs ) - { - int maxError = KiROUND( barThickness / 48 ); - SHAPE_POLY_SET poly; - - TransformOvalToPolygon( poly, topLeft, topRight, barThickness, maxError, ERROR_INSIDE ); - - std::unique_ptr overbarGlyph = std::make_unique( poly ); - aGlyphs->push_back( std::move( overbarGlyph ) ); - } - } - hb_buffer_destroy( buf ); hb_font_destroy( referencedFont ); @@ -575,7 +494,7 @@ void OUTLINE_FONT::RenderToOpenGLCanvas( KIGFX::OPENGL_GAL& aGal, const wxString unsigned int glyphCount; hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( buf, &glyphCount ); hb_glyph_position_t* glyphPos = hb_buffer_get_glyph_positions( buf, &glyphCount ); - + std::lock_guard guard( m_freeTypeMutex ); hb_font_t* referencedFont = hb_ft_font_create_referenced( m_face ); diff --git a/common/font/stroke_font.cpp b/common/font/stroke_font.cpp index ec5f583dc4..98bf742a67 100644 --- a/common/font/stroke_font.cpp +++ b/common/font/stroke_font.cpp @@ -4,7 +4,7 @@ * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de * Copyright (C) 2013 CERN * @author Maciej Suminski - * Copyright (C) 2016-2022 Kicad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2016-2023 Kicad Developers, see AUTHORS.txt for contributors. * * Stroke font class * @@ -128,7 +128,6 @@ void STROKE_FONT::loadNewStrokeFont( const char* const aNewStrokeFont[], int aNe while( aNewStrokeFont[j][i] ) { - if( aNewStrokeFont[j][i] == ' ' && aNewStrokeFont[j][i+1] == 'R' ) strokes++; @@ -304,62 +303,10 @@ VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vectorpush_back( overbarGlyph.Transform( { 1.0, 1.0 }, { 0, 0 }, false, - aAngle, aMirror, aOrigin ) ); - } - } - - if( aTextStyle & TEXT_STYLE::UNDERLINE ) - { - barOffset.y = ComputeUnderlineVerticalPosition( glyphSize.y ); - - if( aTextStyle & TEXT_STYLE::ITALIC ) - barOffset.x = barOffset.y * ITALIC_TILT; - - VECTOR2D barStart( aPosition.x + barOffset.x + barTrim, cursor.y - barOffset.y ); - VECTOR2D barEnd( cursor.x + barOffset.x - barTrim, cursor.y - barOffset.y ); - - if( aGlyphs ) - { - STROKE_GLYPH underlineGlyph; - - underlineGlyph.AddPoint( barStart ); - underlineGlyph.AddPoint( barEnd ); - underlineGlyph.Finalize(); - - aGlyphs->push_back( underlineGlyph.Transform( { 1.0, 1.0 }, { 0, 0 }, false, - aAngle, aMirror, aOrigin ) ); - } - } - if( aBBox ) { aBBox->SetOrigin( aPosition ); - aBBox->SetEnd( cursor.x + barOffset.x - KiROUND( glyphSize.x * INTER_CHAR ), - cursor.y + std::max( glyphSize.y, barOffset.y * OVERBAR_POSITION_FACTOR ) ); + aBBox->SetEnd( cursor.x - KiROUND( glyphSize.x * INTER_CHAR ), cursor.y + glyphSize.y ); aBBox->Normalize(); } diff --git a/common/string_utils.cpp b/common/string_utils.cpp index 0d1ae757f5..5d5a909cab 100644 --- a/common/string_utils.cpp +++ b/common/string_utils.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2004-2022 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2004-2023 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 @@ -280,42 +280,49 @@ wxString UnescapeString( const wxString& aSource ) wxString newbuf; newbuf.reserve( sourceLen ); + wxUniChar prev = 0; + wxUniChar ch = 0; + for( size_t i = 0; i < sourceLen; ++i ) { - wxUniChar ch = aSource[i]; + prev = ch; + ch = aSource[i]; - if( ( ch == '$' || ch == '~' || ch == '^' || ch == '_' ) - && i + 1 < sourceLen && aSource[i+1] == '{' ) - { - for( ; i < sourceLen; ++i ) - { - ch = aSource[i]; - newbuf += ch; - - if( ch == '}' ) - break; - } - } - else if( ch == '{' ) + if( ch == '{' ) { wxString token; int depth = 1; + bool terminated = false; for( i = i + 1; i < sourceLen; ++i ) { ch = aSource[i]; + if( ch == '{' ) depth++; else if( ch == '}' ) depth--; if( depth <= 0 ) + { + terminated = true; break; + } else + { token.append( ch ); + } } - if( token == wxT( "dblquote" ) ) newbuf.append( wxT( "\"" ) ); + if( !terminated ) + { + newbuf.append( wxT( "{" ) + UnescapeString( token ) ); + } + else if( prev == '$' || prev == '~' || prev == '^' || prev == '_' ) + { + newbuf.append( wxT( "{" ) + UnescapeString( token ) + wxT( "}" ) ); + } + else if( token == wxT( "dblquote" ) ) newbuf.append( wxT( "\"" ) ); else if( token == wxT( "quote" ) ) newbuf.append( wxT( "'" ) ); else if( token == wxT( "lt" ) ) newbuf.append( wxT( "<" ) ); else if( token == wxT( "gt" ) ) newbuf.append( wxT( ">" ) ); @@ -329,7 +336,6 @@ wxString UnescapeString( const wxString& aSource ) else if( token == wxT( "tab" ) ) newbuf.append( wxT( "\t" ) ); else if( token == wxT( "return" ) ) newbuf.append( wxT( "\n" ) ); else if( token == wxT( "brace" ) ) newbuf.append( wxT( "{" ) ); - else if( token.IsEmpty() ) newbuf.append( wxT( "{" ) ); else { newbuf.append( wxT( "{" ) + UnescapeString( token ) + wxT( "}" ) ); diff --git a/eeschema/sch_painter.cpp b/eeschema/sch_painter.cpp index c14487ffb2..fedbeed3d9 100644 --- a/eeschema/sch_painter.cpp +++ b/eeschema/sch_painter.cpp @@ -2154,8 +2154,8 @@ void SCH_PAINTER::draw( const SCH_TEXT *aText, int aLayer ) if( cache ) { - for( const std::unique_ptr& glyph : *cache ) - m_gal->DrawGlyph( *glyph ); + m_gal->SetLineWidth( attrs.m_StrokeWidth ); + m_gal->DrawGlyphs( *cache ); } else { @@ -2205,8 +2205,8 @@ void SCH_PAINTER::draw( const SCH_TEXTBOX* aTextBox, int aLayer ) if( cache ) { - for( const std::unique_ptr& glyph : *cache ) - m_gal->DrawGlyph( *glyph ); + m_gal->SetLineWidth( attrs.m_StrokeWidth ); + m_gal->DrawGlyphs( *cache ); } else { @@ -2583,8 +2583,8 @@ void SCH_PAINTER::draw( const SCH_FIELD* aField, int aLayer, bool aDimmed ) if( cache ) { - for( const std::unique_ptr& glyph : *cache ) - m_gal->DrawGlyph( *glyph ); + m_gal->SetLineWidth( attributes.m_StrokeWidth ); + m_gal->DrawGlyphs( *cache ); } else { diff --git a/include/font/outline_font.h b/include/font/outline_font.h index c753e73391..77e0f56ea8 100644 --- a/include/font/outline_font.h +++ b/include/font/outline_font.h @@ -126,8 +126,6 @@ public: protected: FT_Error loadFace( const wxString& aFontFileName, int aFaceIndex ); - double getOverbarOffset( int ascender, int height, int thickness ) const; - BOX2I getBoundingBox( const std::vector>& aGlyphs ) const; VECTOR2I getTextAsGlyphs( BOX2I* aBoundingBox, std::vector>* aGlyphs, @@ -136,10 +134,12 @@ protected: const VECTOR2I& aOrigin, TEXT_STYLE_FLAGS aTextStyle ) const; private: - VECTOR2I getTextAsGlyphsUnlocked( BOX2I* aBoundingBox, std::vector>* aGlyphs, - const wxString& aText, const VECTOR2I& aSize, - const VECTOR2I& aPosition, const EDA_ANGLE& aAngle, bool aMirror, - const VECTOR2I& aOrigin, TEXT_STYLE_FLAGS aTextStyle ) const; + VECTOR2I getTextAsGlyphsUnlocked( BOX2I* aBoundingBox, + std::vector>* aGlyphs, + const wxString& aText, const VECTOR2I& aSize, + const VECTOR2I& aPosition, const EDA_ANGLE& aAngle, + bool aMirror, const VECTOR2I& aOrigin, + TEXT_STYLE_FLAGS aTextStyle ) const; private: // FreeType variables diff --git a/include/markup_parser.h b/include/markup_parser.h index 7427e4aff8..0007eede01 100644 --- a/include/markup_parser.h +++ b/include/markup_parser.h @@ -2,7 +2,7 @@ * 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. + * Copyright (C) 2021-2023 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 @@ -61,19 +61,25 @@ struct markup : sor< subscript, struct anyString : plus< seq< not_at< markup >, utf8::any > > {}; -struct anyStringWithinBraces : plus< seq< not_at< markup >, - utf8::not_one< '}' > > > {}; +struct escapeSequence : seq< string<'{'>, identifier, string<'}'> > {}; + +struct anyStringWithinBraces : plus< sor< seq< not_at< markup >, + escapeSequence >, + seq< not_at< markup >, + utf8::not_one<'}'> > > > {}; template< typename ControlChar > -struct braces : seq< seq< ControlChar, string< '{' > >, - until< string< '}' >, sor< anyStringWithinBraces, - subscript, - superscript, - overbar > > > {}; +struct braces : seq< seq< ControlChar, + string<'{'> >, + until< string<'}'>, + sor< anyStringWithinBraces, + subscript, + superscript, + overbar > > > {}; -struct superscript : braces< string< '^' > > {}; -struct subscript : braces< string< '_' > > {}; -struct overbar : braces< string< '~' > > {}; +struct superscript : braces< string<'^'> > {}; +struct subscript : braces< string<'_'> > {}; +struct overbar : braces< string<'~'> > {}; /** * Finally, the full grammar diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 4b73c03840..2d8b31a13b 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -2003,9 +2003,14 @@ void PCB_PAINTER::draw( const PCB_TEXT* aText, int aLayer ) cache = aText->GetRenderCache( font, resolvedText ); if( cache ) + { + m_gal->SetLineWidth( attrs.m_StrokeWidth ); m_gal->DrawGlyphs( *cache ); + } else + { strokeText( resolvedText, aText->GetTextPos(), attrs ); + } } } @@ -2101,9 +2106,14 @@ void PCB_PAINTER::draw( const PCB_TEXTBOX* aTextBox, int aLayer ) cache = aTextBox->GetRenderCache( font, resolvedText ); if( cache ) + { + m_gal->SetLineWidth( attrs.m_StrokeWidth ); m_gal->DrawGlyphs( *cache ); + } else + { strokeText( resolvedText, aTextBox->GetDrawPos(), attrs ); + } }