Move to new font engine.

This commit is contained in:
Jeff Young 2021-12-31 14:07:24 +00:00
parent e41a4d406a
commit 89c0f8e297
39 changed files with 2016 additions and 1300 deletions

View File

@ -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

View File

@ -107,6 +107,20 @@ void BASIC_GAL::DrawPolyline( const std::deque<VECTOR2D>& aPointList )
}
void BASIC_GAL::DrawPolyline( const std::vector<VECTOR2D>& aPointList )
{
if( aPointList.size() < 2 )
return;
std::vector<VECTOR2I> 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<VECTOR2D>& pointList : aGlyph.GetPoints() )
DrawPolyline( pointList );
}
#if 0 // FONT TODO
else if( aGlyph.IsOutline() )
{
}
#endif
}

View File

@ -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(),
KIFONT::FONT* font = aItem->GetFont();
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() );
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();
font->Draw( m_gal, aItem->GetShownText(), aItem->GetTextPos(), attrs );
}

View File

@ -40,7 +40,6 @@
#include <eda_rect.h> // for EDA_RECT
#include <eda_text.h> // for EDA_TEXT, TEXT_EFFECTS, GR_TEXT_VJUSTIF...
#include <gal/color4d.h> // for COLOR4D, COLOR4D::BLACK
#include <gal/stroke_font.h> // for STROKE_FONT
#include <gr_text.h> // for GRText
#include <string_utils.h> // for UnescapeString
#include <math/util.h> // 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();
KIFONT::FONT* font = KIFONT::FONT::GetFont();
VECTOR2D fontSize( GetTextSize() );
double penWidth( thickness );
int dx = KiROUND( font.ComputeStringBoundaryLimits( text, fontSize, penWidth ).x );
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<int>( 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

View File

@ -24,14 +24,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <core/wx_stl_compat.h>
#include <font/font.h>
#include <wx/font.h>
#include <string_utils.h>
#include <gal/graphics_abstraction_layer.h>
#include <font/stroke_font.h>
#include <trigo.h>
#include <markup_parser.h>
using namespace KIFONT;
FONT* FONT::s_defaultFont = nullptr;
std::map<wxString, FONT*> FONT::s_fontMap;
std::map< std::tuple<wxString, bool, bool>, 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<wxString, bool, bool> 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<wxString, bool, bool> 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<wxPoint> positions;
int n_lines;
VECTOR2D boundingBox;
std::vector<VECTOR2D> 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<void( const std::unique_ptr<MARKUP::NODE>& )> nodeHandler =
[&]( const std::unique_ptr<MARKUP::NODE>& 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<wxPoint>& aPositions,
int& aLineCount, std::vector<VECTOR2D>& 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<wxPoint> positions;
int n_lines;
VECTOR2D boundingBox;
std::vector<VECTOR2D> 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<wxPoint> positions;
std::vector<VECTOR2D> 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<MARKUP::NODE>& 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<SHAPE_POLY_SET> 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<MARKUP::NODE> 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>& 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<MARKUP::NODE> 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;
}

228
common/font/glyph.cpp Normal file
View File

@ -0,0 +1,228 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Ola Rinta-Koski <gitlab@rinta-koski.net>
* 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 <http://www.gnu.org/licenses/>.
*/
#include <vector>
#include <font/glyph.h>
using namespace KIFONT;
STROKE_GLYPH::STROKE_GLYPH( const STROKE_GLYPH& aGlyph )
{
for( const std::vector<VECTOR2D>& pointList : aGlyph.m_pointLists )
m_pointLists.push_back( pointList );
}
void STROKE_GLYPH::AddPoint( const VECTOR2D& aPoint )
{
if( !m_penIsDown )
{
std::vector<VECTOR2D> 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<VECTOR2D>& 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<GLYPH> STROKE_GLYPH::Resize( const VECTOR2D& aGlyphSize ) const
{
std::shared_ptr<STROKE_GLYPH> glyph = std::make_shared<STROKE_GLYPH>( *this );
glyph->clearBoundingBox();
bool first = true;
for( std::vector<VECTOR2D>& 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<GLYPH> STROKE_GLYPH::Translate( const VECTOR2D& aOffset ) const
{
auto glyph = std::make_shared<STROKE_GLYPH>( *this );
glyph->clearBoundingBox();
bool first = true;
for( std::vector<VECTOR2D>& 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<GLYPH> STROKE_GLYPH::Transform( const VECTOR2D& aGlyphSize, const VECTOR2D& aOffset,
double aTilt )
{
std::shared_ptr<STROKE_GLYPH> glyph = std::make_shared<STROKE_GLYPH>( *this );
glyph->clearBoundingBox();
bool first = true;
for( std::vector<VECTOR2D>& 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<GLYPH> 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<STROKE_GLYPH>( *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<VECTOR2D>& 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 );
}
}
}
}

305
common/font/stroke_font.cpp Normal file
View File

@ -0,0 +1,305 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
* Copyright (C) 2013 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
* 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 <gal/graphics_abstraction_layer.h>
#include <wx/string.h>
#include <wx/filename.h>
#include <wx/textfile.h>
#include <newstroke_font.h>
#include <font/glyph.h>
#include <font/stroke_font.h>
#include <markup_parser.h>
#include <geometry/shape_line_chain.h>
#include <trigo.h>
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<BOX2D>* 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<BOX2D>;
g_defaultFontGlyphBoundingBoxes->reserve( aNewStrokeFontSize );
for( int j = 0; j < aNewStrokeFontSize; j++ )
{
auto glyph = std::make_shared<STROKE_GLYPH>();
double glyphStartX = 0.0;
std::vector<VECTOR2D>* 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 <value> + 'R', where
// <value> 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<VECTOR2D>;
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<STROKE_GLYPH*>( 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<STROKE_GLYPH> overbarGlyph = std::make_shared<STROKE_GLYPH>();
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 );
}

View File

@ -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<VECTOR2D>& aPointList )
}
void CAIRO_GAL_BASE::drawPoly( const std::vector<VECTOR2D>& aPointList )
{
wxCHECK( aPointList.size() > 1, /* void */ );
// Iterate over the point list and draw the segments
std::vector<VECTOR2D>::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<VECTOR2D>& 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
}

View File

@ -28,6 +28,7 @@
#include <gal/graphics_abstraction_layer.h>
#include <gal/definitions.h>
#include <font/font.h>
#include <math/util.h> // 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<KIFONT::GLYPH>& 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 )

View File

@ -1027,6 +1027,17 @@ void OPENGL_GAL::DrawPolyline( const std::deque<VECTOR2D>& aPointList )
}
void OPENGL_GAL::DrawPolyline( const std::vector<VECTOR2D>& 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<VECTOR2D>& pointList : aGlyph.GetPoints() )
DrawPolyline( pointList );
}
#if 0 // FONT TODO
else if( aGlyph.IsOutline() )
{
fillPolygonAsTriangles( aGlyph.GetPolylist() );
}
#endif
}

View File

@ -1,640 +0,0 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
* Copyright (C) 2013 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
* 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 <gal/stroke_font.h>
#include <gal/graphics_abstraction_layer.h>
#include <math/util.h> // for KiROUND
#include <wx/string.h>
#include <gr_text.h>
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<BOX2D>* 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<BOX2D>;
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<VECTOR2D>* 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 <value> + 'R',
// <value> 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<VECTOR2D>;
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<VECTOR2D>* 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<VECTOR2D> 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<VECTOR2D>* 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;
}

View File

@ -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 );
}

View File

@ -25,7 +25,7 @@
using namespace MARKUP;
MARKUP::MARKUP_NODE MARKUP_PARSER::Parse()
std::unique_ptr<NODE> MARKUP_PARSER::Parse()
{
//string_input<> in( source, "from_input" );
auto root = parse_tree::parse<MARKUP::grammar, MARKUP::NODE, MARKUP::selector>( 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;

View File

@ -95,6 +95,7 @@
#include <eda_rect.h>
#include <eda_shape.h>
#include <string_utils.h>
#include <font/font.h>
#include <macros.h>
#include <trigo.h>
@ -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 );

View File

@ -22,13 +22,13 @@
*/
#include <preview_items/draw_context.h>
#include <preview_items/preview_utils.h>
#include <view/view.h>
using namespace KIGFX::PREVIEW;
using KIGFX::COLOR4D;
static constexpr double ANGLE_EPSILON = 1e-9;

View File

@ -29,6 +29,7 @@
#include <view/view.h>
using namespace KIGFX::PREVIEW;
using KIGFX::COLOR4D;
struct SELECTION_COLORS
{

View File

@ -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;

View File

@ -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;

View File

@ -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 );
@ -1068,7 +1069,7 @@ void LIB_PIN::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITE
const EDA_RECT LIB_PIN::GetBoundingBox( bool aIncludeInvisibles, bool aPinOnly ) const
{
const KIGFX::STROKE_FONT& font = basic_gal.GetStrokeFont();
KIFONT::FONT* font = KIFONT::FONT::GetFont();
EDA_RECT bbox;
VECTOR2I begin;
@ -1108,7 +1109,7 @@ const EDA_RECT LIB_PIN::GetBoundingBox( bool aIncludeInvisibles, bool aPinOnly )
if( showNum )
{
VECTOR2D fontSize( m_numTextSize, m_numTextSize );
VECTOR2D numSize = font.ComputeStringBoundaryLimits( number, fontSize, GetPenWidth() );
VECTOR2D numSize = font->StringBoundaryLimits( 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 );

View File

@ -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;

View File

@ -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( eeconfig()->m_Selection.text_as_box )
if( drawingShadows && eeconfig()->m_Selection.text_as_box )
{
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;
}
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;
}
}
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

View File

@ -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;

View File

@ -32,7 +32,6 @@
#include <sch_edit_frame.h>
#include <plotters/plotter.h>
#include <widgets/msgpanel.h>
#include <gal/stroke_font.h>
#include <bitmaps.h>
#include <string_utils.h>
#include <sch_text.h>
@ -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;

View File

@ -26,10 +26,7 @@
#define BASIC_GAL_H
#include <eda_rect.h>
#include <gal/stroke_font.h>
#include <gal/graphics_abstraction_layer.h>
#include <newstroke_font.h>
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<VECTOR2D>& aPointList ) override;
virtual void DrawPolyline( const VECTOR2D aPointList[], int aListSize ) override;
void DrawPolyline( const std::deque<VECTOR2D>& aPointList ) override;
void DrawPolyline( const std::vector<VECTOR2D>& 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,9 +153,13 @@ 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;
KIGFX::COLOR4D m_Color;
private:
TRANSFORM_PRM m_transform;
@ -163,14 +169,10 @@ private:
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
// 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.
};

View File

@ -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....)

View File

@ -33,7 +33,9 @@
#include <wx/string.h>
#include <utf8.h>
#include <font/glyph.h>
#include <font/text_attributes.h>
#include <markup_parser.h>
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:
/**
* 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<wxPoint>& aPositions, int& aLineCount,
std::vector<VECTOR2D>& 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<MARKUP::NODE>& aNode, const VECTOR2D& aPosition,
const VECTOR2D& aGlyphSize, const EDA_ANGLE& aAngle,
TEXT_STYLE_FLAGS aTextStyle, int aLevel = 0 ) const;
private:
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<wxString, FONT*> s_fontMap;
static std::map< std::tuple<wxString, bool, bool>, FONT* > s_fontMap;
};
} //namespace KIFONT

207
include/font/glyph.h Normal file
View File

@ -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 <memory>
#include <math/box2.h>
#include <geometry/shape_poly_set.h>
#include <wx/debug.h>
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<GLYPH> Resize( const VECTOR2D& aGlyphSize ) const = 0;
virtual std::shared_ptr<GLYPH> Translate( const VECTOR2D& aOffset ) const = 0;
virtual std::shared_ptr<GLYPH> 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<std::vector<VECTOR2D>>& GetPoints() const = 0;
virtual bool IsOutline() const { return false; }
virtual bool IsStroke() const { return false; }
};
typedef std::vector<std::shared_ptr<GLYPH>> 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<GLYPH> Resize( const VECTOR2D& aGlyphSize ) const override
{
wxFAIL_MSG( "unimplemented" );
return nullptr;
}
std::shared_ptr<GLYPH> Translate( const VECTOR2D& aOffset ) const override
{
wxFAIL_MSG( "unimplemented" );
return nullptr;
}
std::shared_ptr<GLYPH> 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<std::vector<VECTOR2D>>& GetPoints() const override
{
wxFAIL_MSG( "unimplemented" );
return m_dummy;
}
private:
SHAPE_POLY_SET m_polySet;
// For unimplemented return values
std::vector<std::vector<VECTOR2D>> 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<GLYPH> Resize( const VECTOR2D& aGlyphSize ) const override;
std::shared_ptr<GLYPH> Translate( const VECTOR2D& aOffset ) const override;
std::shared_ptr<GLYPH> Transform( const VECTOR2D& aGlyphSize, const VECTOR2D& aOffset,
double aTilt );
std::shared_ptr<GLYPH> 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<std::vector<VECTOR2D>>& 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<std::vector<VECTOR2D>> m_pointLists;
BOX2D m_boundingBox;
// For unimplemented return values
SHAPE_POLY_SET m_dummy;
};
typedef std::vector<VECTOR2D> GLYPH_POINTS;
typedef std::vector<GLYPH_POINTS> GLYPH_POINTS_LIST;
typedef std::vector<BOX2D> GLYPH_BOUNDING_BOX_LIST;
} // namespace KIFONT
#endif // GLYPH_H

118
include/font/stroke_font.h Normal file
View File

@ -0,0 +1,118 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
* Copyright (C) 2013 CERN
* Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* 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 <map>
#include <deque>
#include <algorithm>
#include <utf8.h>
#include <math/box2.h>
#include <font/font.h>
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

View File

@ -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<VECTOR2D>& aPointList );
void drawPoly( const std::vector<VECTOR2D>& aPointList );
void drawPoly( const VECTOR2D aPointList[], int aListSize );
void drawPoly( const SHAPE_LINE_CHAIN& aLineChain );

View File

@ -36,9 +36,9 @@
#include <gal/color4d.h>
#include <gal/cursors.h>
#include <gal/definitions.h>
#include <gal/stroke_font.h>
#include <gal/gal_display_options.h>
#include <newstroke_font.h>
#include <font/stroke_font.h>
#include <eda_rect.h>
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<KIFONT::GLYPH>& 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:

View File

@ -140,6 +140,7 @@ public:
/// @copydoc GAL::DrawPolyline()
void DrawPolyline( const std::deque<VECTOR2D>& aPointList ) override;
void DrawPolyline( const std::vector<VECTOR2D>& 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,

View File

@ -1,191 +0,0 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
* Copyright (C) 2013 CERN
* Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* 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 <deque>
#include <algorithm>
#include <utf8.h>
#include <eda_text.h>
#include <math/box2.h>
namespace KIGFX
{
class GAL;
typedef std::vector<std::vector<VECTOR2D>*> GLYPH;
typedef std::vector<GLYPH*> 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<BOX2D>* 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_

View File

@ -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)

View File

@ -107,9 +107,6 @@ using selector = parse_tree::selector< Rule,
subscript,
overbar>>;
typedef std::unique_ptr<MARKUP::NODE, std::default_delete<MARKUP::NODE>> MARKUP_NODE;
class MARKUP_PARSER
{
public:
@ -117,7 +114,7 @@ public:
in( source, "from_input" )
{}
MARKUP_NODE Parse();
std::unique_ptr<NODE> 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

View File

@ -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;

View File

@ -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 );
}

View File

@ -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;

View File

@ -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 );

View File

@ -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 );