Text glyph caches and bug fixes.

1) Unify metrics between stroke and outline fonts
2) Unify handling of rotation and mirroring
3) Bug fixes in collision handling
4) Use of VECTOR2I (instead of VECTOR2D) for world-coordinates
5) Generate outline font overbar with rounded ends and fix rotation
bugs
6) Generate wxEVT_CHOICE events from FONT_CHOICE::SetFontSelection
7) Change text-item PNS SOLIDs to use compound shapes
This commit is contained in:
Jeff Young 2022-01-04 23:00:00 +00:00
parent 5b7198dfc8
commit eb58d7e44c
21 changed files with 831 additions and 821 deletions

View File

@ -70,6 +70,14 @@ void addTextSegmToPoly( int x0, int y0, int xf, int yf, void* aData )
} }
void addTextSegmToShape( int x0, int y0, int xf, int yf, void* aData )
{
TSEGM_2_SHAPE_PRMS* prm = static_cast<TSEGM_2_SHAPE_PRMS*>( aData );
prm->m_shape->AddShape( new SHAPE_SEGMENT( VECTOR2I( x0, y0 ), VECTOR2I( xf, yf ),
prm->m_penWidth ) );
}
GR_TEXT_H_ALIGN_T EDA_TEXT::MapHorizJustify( int aHorizJustify ) GR_TEXT_H_ALIGN_T EDA_TEXT::MapHorizJustify( int aHorizJustify )
{ {
wxASSERT( aHorizJustify >= GR_TEXT_H_ALIGN_LEFT && aHorizJustify <= GR_TEXT_H_ALIGN_RIGHT ); wxASSERT( aHorizJustify >= GR_TEXT_H_ALIGN_LEFT && aHorizJustify <= GR_TEXT_H_ALIGN_RIGHT );
@ -107,12 +115,25 @@ EDA_TEXT::EDA_TEXT( const wxString& text ) :
} }
EDA_TEXT::EDA_TEXT( const EDA_TEXT& aText ) : EDA_TEXT::EDA_TEXT( const EDA_TEXT& aText )
m_text( aText.m_text ),
m_attributes( aText.m_attributes ),
m_pos( aText.m_pos )
{ {
cacheShownText(); m_text = aText.m_text;
m_shown_text = aText.m_shown_text;
m_shown_text_has_text_var_refs = aText.m_shown_text_has_text_var_refs;
m_attributes = aText.m_attributes;
m_pos = aText.m_pos;
m_render_cache_text = aText.m_render_cache_text;
m_render_cache_angle = aText.m_render_cache_angle;
m_render_cache.clear();
for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aText.m_render_cache )
{
KIFONT::OUTLINE_GLYPH* outline_glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() );
m_render_cache.emplace_back( std::make_unique<KIFONT::OUTLINE_GLYPH>( *outline_glyph ) );
}
} }
@ -121,6 +142,30 @@ EDA_TEXT::~EDA_TEXT()
} }
EDA_TEXT& EDA_TEXT::operator=( const EDA_TEXT& aText )
{
m_text = aText.m_text;
m_shown_text = aText.m_shown_text;
m_shown_text_has_text_var_refs = aText.m_shown_text_has_text_var_refs;
m_attributes = aText.m_attributes;
m_pos = aText.m_pos;
m_render_cache_text = aText.m_render_cache_text;
m_render_cache_angle = aText.m_render_cache_angle;
m_render_cache.clear();
for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aText.m_render_cache )
{
KIFONT::OUTLINE_GLYPH* outline_glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() );
m_render_cache.emplace_back( std::make_unique<KIFONT::OUTLINE_GLYPH>( *outline_glyph ) );
}
return *this;
}
void EDA_TEXT::SetText( const wxString& aText ) void EDA_TEXT::SetText( const wxString& aText )
{ {
m_text = aText; m_text = aText;
@ -133,6 +178,78 @@ void EDA_TEXT::CopyText( const EDA_TEXT& aSrc )
m_text = aSrc.m_text; m_text = aSrc.m_text;
m_shown_text = aSrc.m_shown_text; m_shown_text = aSrc.m_shown_text;
m_shown_text_has_text_var_refs = aSrc.m_shown_text_has_text_var_refs; m_shown_text_has_text_var_refs = aSrc.m_shown_text_has_text_var_refs;
m_render_cache.clear();
}
void EDA_TEXT::SetTextThickness( int aWidth )
{
m_attributes.m_StrokeWidth = aWidth;
m_render_cache.clear();
}
void EDA_TEXT::SetTextAngle( const EDA_ANGLE& aAngle )
{
m_attributes.m_Angle = aAngle;
m_render_cache.clear();
}
void EDA_TEXT::SetItalic( bool aItalic )
{
m_attributes.m_Italic = aItalic;
m_render_cache.clear();
}
void EDA_TEXT::SetBold( bool aBold )
{
m_attributes.m_Bold = aBold;
m_render_cache.clear();
}
void EDA_TEXT::SetVisible( bool aVisible )
{
m_attributes.m_Visible = aVisible;
m_render_cache.clear();
}
void EDA_TEXT::SetMirrored( bool isMirrored )
{
m_attributes.m_Mirrored = isMirrored;
m_render_cache.clear();
}
void EDA_TEXT::SetMultilineAllowed( bool aAllow )
{
m_attributes.m_Multiline = aAllow;
m_render_cache.clear();
}
void EDA_TEXT::SetHorizJustify( GR_TEXT_H_ALIGN_T aType )
{
m_attributes.m_Halign = aType;
m_render_cache.clear();
}
void EDA_TEXT::SetVertJustify( GR_TEXT_V_ALIGN_T aType )
{
m_attributes.m_Valign = aType;
m_render_cache.clear();
}
void EDA_TEXT::SetKeepUpright( bool aKeepUpright )
{
m_attributes.m_KeepUpright = aKeepUpright;
m_render_cache.clear();
} }
@ -140,6 +257,7 @@ void EDA_TEXT::SetAttributes( const EDA_TEXT& aSrc )
{ {
m_attributes = aSrc.m_attributes; m_attributes = aSrc.m_attributes;
m_pos = aSrc.m_pos; m_pos = aSrc.m_pos;
m_render_cache.clear();
} }
@ -148,6 +266,8 @@ void EDA_TEXT::SwapText( EDA_TEXT& aTradingPartner )
std::swap( m_text, aTradingPartner.m_text ); std::swap( m_text, aTradingPartner.m_text );
std::swap( m_shown_text, aTradingPartner.m_shown_text ); std::swap( m_shown_text, aTradingPartner.m_shown_text );
std::swap( m_shown_text_has_text_var_refs, aTradingPartner.m_shown_text_has_text_var_refs ); std::swap( m_shown_text_has_text_var_refs, aTradingPartner.m_shown_text_has_text_var_refs );
m_render_cache.clear();
} }
@ -155,6 +275,8 @@ void EDA_TEXT::SwapAttributes( EDA_TEXT& aTradingPartner )
{ {
std::swap( m_attributes, aTradingPartner.m_attributes ); std::swap( m_attributes, aTradingPartner.m_attributes );
std::swap( m_pos, aTradingPartner.m_pos ); std::swap( m_pos, aTradingPartner.m_pos );
m_render_cache.clear();
} }
@ -188,6 +310,78 @@ bool EDA_TEXT::Replace( const wxFindReplaceData& aSearchData )
} }
void EDA_TEXT::SetFont( KIFONT::FONT* aFont )
{
m_attributes.m_Font = aFont;
m_render_cache.clear();
}
void EDA_TEXT::SetLineSpacing( double aLineSpacing )
{
m_attributes.m_LineSpacing = aLineSpacing;
m_render_cache.clear();
}
void EDA_TEXT::SetTextSize( const wxSize& aNewSize )
{
m_attributes.m_Size = aNewSize;
m_render_cache.clear();
}
void EDA_TEXT::SetTextWidth( int aWidth )
{
m_attributes.m_Size.x = aWidth;
m_render_cache.clear();
}
void EDA_TEXT::SetTextHeight( int aHeight )
{
m_attributes.m_Size.y = aHeight;
m_render_cache.clear();
}
void EDA_TEXT::SetTextPos( const VECTOR2I& aPoint )
{
Offset( VECTOR2I( aPoint.x - m_pos.x, aPoint.y - m_pos.y ) );
}
void EDA_TEXT::SetTextX( int aX )
{
Offset( VECTOR2I( aX - m_pos.x, 0 ) );
}
void EDA_TEXT::SetTextY( int aY )
{
Offset( VECTOR2I( 0, aY - m_pos.y ) );
}
void EDA_TEXT::Offset( const VECTOR2I& aOffset )
{
m_pos += aOffset;
for( std::unique_ptr<KIFONT::GLYPH>& glyph : m_render_cache )
{
KIFONT::OUTLINE_GLYPH* outline_glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() );
outline_glyph->Move( aOffset );
}
}
void EDA_TEXT::Empty()
{
m_text.Empty();
m_render_cache.clear();
}
void EDA_TEXT::cacheShownText() void EDA_TEXT::cacheShownText()
{ {
if( m_text.IsEmpty() || m_text == wxT( "~" ) ) // ~ is legacy empty-string token if( m_text.IsEmpty() || m_text == wxT( "~" ) ) // ~ is legacy empty-string token
@ -200,6 +394,49 @@ void EDA_TEXT::cacheShownText()
m_shown_text = UnescapeString( m_text ); m_shown_text = UnescapeString( m_text );
m_shown_text_has_text_var_refs = m_shown_text.Contains( wxT( "${" ) ); m_shown_text_has_text_var_refs = m_shown_text.Contains( wxT( "${" ) );
} }
m_render_cache.clear();
}
std::vector<std::unique_ptr<KIFONT::GLYPH>>*
EDA_TEXT::GetRenderCache( const wxString& forResolvedText ) const
{
if( GetFont() && GetFont()->IsOutline() )
{
EDA_ANGLE resolvedAngle = GetDrawRotation();
if( m_render_cache.empty()
|| m_render_cache_text != forResolvedText
|| m_render_cache_angle != resolvedAngle )
{
m_render_cache.clear();
KIFONT::OUTLINE_FONT* font = static_cast<KIFONT::OUTLINE_FONT*>( GetFont() );
font->GetLinesAsGlyphs( m_render_cache, this );
m_render_cache_angle = resolvedAngle;
m_render_cache_text = forResolvedText;
}
return &m_render_cache;
}
return nullptr;
}
void EDA_TEXT::SetupRenderCache( const wxString& aResolvedText, const EDA_ANGLE& aAngle )
{
m_render_cache_text = aResolvedText;
m_render_cache_angle = aAngle;
m_render_cache.clear();
}
void EDA_TEXT::AddRenderCacheGlyph( const SHAPE_POLY_SET& aPoly )
{
m_render_cache.emplace_back( std::make_unique<KIFONT::OUTLINE_GLYPH>( aPoly ) );
} }
@ -567,58 +804,6 @@ void EDA_TEXT::Format( OUTPUTFORMATTER* aFormatter, int aNestLevel, int aControl
#endif #endif
} }
// Convert the text shape to a list of segment
// each segment is stored as 2 VECTOR2Is: its starting point and its ending point
// we are using GRText to create the segments and therefore a call-back function is needed
// This is a call back function, used by GRText to put each segment in buffer
static void addTextSegmToBuffer( int x0, int y0, int xf, int yf, void* aData )
{
std::vector<VECTOR2I>* cornerBuffer = static_cast<std::vector<VECTOR2I>*>( aData );
cornerBuffer->push_back( VECTOR2I( x0, y0 ) );
cornerBuffer->push_back( VECTOR2I( xf, yf ) );
}
std::vector<VECTOR2I> EDA_TEXT::TransformToSegmentList() const
{
std::vector<VECTOR2I> cornerBuffer;
wxSize size = GetTextSize();
if( IsMirrored() )
size.x = -size.x;
bool forceBold = true;
int penWidth = 0; // use max-width for bold text
COLOR4D color = COLOR4D::BLACK; // not actually used, but needed by GRText
if( IsMultilineAllowed() )
{
wxArrayString strings_list;
wxStringSplit( GetShownText(), strings_list, wxChar('\n') );
std::vector<VECTOR2I> positions;
positions.reserve( strings_list.Count() );
GetLinePositions( positions, strings_list.Count() );
for( unsigned ii = 0; ii < strings_list.Count(); ii++ )
{
wxString txt = strings_list.Item( ii );
GRText( nullptr, positions[ii], color, txt, GetDrawRotation(), size,
GetDrawHorizJustify(), GetDrawVertJustify(), penWidth, IsItalic(), forceBold,
GetFont(), addTextSegmToBuffer, &cornerBuffer );
}
}
else
{
GRText( nullptr, GetDrawPos(), color, GetShownText(), GetDrawRotation(), size,
GetDrawHorizJustify(), GetDrawVertJustify(), penWidth, IsItalic(), forceBold,
GetFont(), addTextSegmToBuffer, &cornerBuffer );
}
return cornerBuffer;
}
std::shared_ptr<SHAPE_COMPOUND> EDA_TEXT::GetEffectiveTextShape( ) const std::shared_ptr<SHAPE_COMPOUND> EDA_TEXT::GetEffectiveTextShape( ) const
{ {
@ -626,20 +811,13 @@ std::shared_ptr<SHAPE_COMPOUND> EDA_TEXT::GetEffectiveTextShape( ) const
if( GetFont() && GetFont()->IsOutline() ) if( GetFont() && GetFont()->IsOutline() )
{ {
// FONT TODO: Use the cached glyphs rather than rendering them // Make sure the cache is up-to-date before using it
(void) GetRenderCache( m_render_cache_text );
KIFONT::OUTLINE_FONT* font = static_cast<KIFONT::OUTLINE_FONT*>( GetFont() ); for( std::unique_ptr<KIFONT::GLYPH>& baseGlyph : m_render_cache )
std::vector<std::unique_ptr<KIFONT::GLYPH>> glyphs;
font->GetLinesAsGlyphs( glyphs, this );
for( std::unique_ptr<KIFONT::GLYPH>& baseGlyph : glyphs )
{ {
KIFONT::OUTLINE_GLYPH* glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( baseGlyph.get() ); KIFONT::OUTLINE_GLYPH* glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( baseGlyph.get() );
if( IsMirrored() )
glyph->Mirror( GetTextPos() );
glyph->CacheTriangulation(); glyph->CacheTriangulation();
for( unsigned int ii = 0; ii < glyph->TriangulatedPolyCount(); ++ii ) for( unsigned int ii = 0; ii < glyph->TriangulatedPolyCount(); ++ii )
@ -663,11 +841,38 @@ std::shared_ptr<SHAPE_COMPOUND> EDA_TEXT::GetEffectiveTextShape( ) const
} }
else else
{ {
wxSize size = GetTextSize();
int penWidth = GetEffectiveTextPenWidth(); int penWidth = GetEffectiveTextPenWidth();
std::vector<VECTOR2I> pts = TransformToSegmentList(); bool forceBold = true;
for( unsigned jj = 0; jj < pts.size(); jj += 2 ) TSEGM_2_SHAPE_PRMS prms;
shape->AddShape( new SHAPE_SEGMENT( pts[jj], pts[jj+1], penWidth ) ); prms.m_penWidth = penWidth;
prms.m_shape = shape.get();
if( IsMirrored() )
size.x = -size.x;
if( IsMultilineAllowed() )
{
wxArrayString strings_list;
wxStringSplit( GetShownText(), strings_list, wxChar('\n') );
std::vector<VECTOR2I> positions;
positions.reserve( strings_list.Count() );
GetLinePositions( positions, strings_list.Count() );
for( unsigned ii = 0; ii < strings_list.Count(); ii++ )
{
GRText( nullptr, positions[ii], COLOR4D::BLACK, strings_list.Item( ii ),
GetDrawRotation(), size, GetDrawHorizJustify(), GetDrawVertJustify(),
penWidth, IsItalic(), forceBold, GetFont(), addTextSegmToShape, &prms );
}
}
else
{
GRText( nullptr, GetDrawPos(), COLOR4D::BLACK, GetShownText(),
GetDrawRotation(), size, GetDrawHorizJustify(), GetDrawVertJustify(),
penWidth, IsItalic(), forceBold, GetFont(), addTextSegmToShape, &prms );
}
} }
return shape; return shape;

View File

@ -112,28 +112,28 @@ bool FONT::IsStroke( const wxString& aFontName )
* @param aPosition is the text position * @param aPosition is the text position
* @return bounding box width/height * @return bounding box width/height
*/ */
VECTOR2D FONT::doDrawString( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, VECTOR2D FONT::doDrawString( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
bool aParse, const TEXT_ATTRIBUTES& aAttrs ) const bool aParse, const TEXT_ATTRIBUTES& aAttrs ) const
{ {
if( aText.empty() ) if( aText.empty() )
return VECTOR2D( 0.0, 0.0 ); return VECTOR2D( 0.0, 0.0 );
wxArrayString strings; wxArrayString strings;
std::vector<wxPoint> positions; std::vector<VECTOR2I> positions;
int n_lines;
VECTOR2D boundingBox; VECTOR2D boundingBox;
std::vector<VECTOR2D> lineBoundingBoxes; std::vector<VECTOR2D> lineBoundingBoxes;
getLinePositions( aText, aPosition, strings, positions, n_lines, lineBoundingBoxes, aAttrs ); getLinePositions( aText, aPosition, strings, positions, lineBoundingBoxes, aAttrs );
for( int i = 0; i < n_lines; i++ ) for( size_t i = 0; i < strings.GetCount(); i++ )
{ {
VECTOR2D lineBoundingBox; VECTOR2D lineBoundingBox;
if( aParse ) if( aParse )
{ {
MARKUP::MARKUP_PARSER markupParser( std::string( strings.Item( i ) ) ); MARKUP::MARKUP_PARSER markupParser( std::string( strings.Item( i ) ) );
//auto parse_result = markupParser.Parse(); //auto parse_result = markupParser.Parse();
VECTOR2D cursor = positions[i]; VECTOR2I cursor = positions[i];
std::function<void( const std::unique_ptr<MARKUP::NODE>& )> nodeHandler = std::function<void( const std::unique_ptr<MARKUP::NODE>& )> nodeHandler =
[&]( const std::unique_ptr<MARKUP::NODE>& aNode ) [&]( const std::unique_ptr<MARKUP::NODE>& aNode )
@ -160,30 +160,29 @@ VECTOR2D FONT::doDrawString( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D
boundingBox.x = fmax( boundingBox.x, lineBoundingBox.x ); boundingBox.x = fmax( boundingBox.x, lineBoundingBox.x );
} }
boundingBox.y = ( n_lines + 1 ) * GetInterline( aAttrs.m_Size.y ); boundingBox.y = ( strings.GetCount() + 1 ) * GetInterline( aAttrs.m_Size.y );
return boundingBox; return boundingBox;
} }
void FONT::getLinePositions( const UTF8& aText, const VECTOR2D& aPosition, void FONT::getLinePositions( const UTF8& aText, const VECTOR2I& aPosition,
wxArrayString& aStringList, std::vector<wxPoint>& aPositions, wxArrayString& aTextLines, std::vector<VECTOR2I>& aPositions,
int& aLineCount, std::vector<VECTOR2D>& aBoundingBoxes, std::vector<VECTOR2D>& aBoundingBoxes,
const TEXT_ATTRIBUTES& aAttrs ) const const TEXT_ATTRIBUTES& aAttrs ) const
{ {
wxStringSplit( aText, aStringList, '\n' ); wxStringSplit( aText, aTextLines, '\n' );
aLineCount = aStringList.Count(); int lineCount = aTextLines.Count();
aPositions.reserve( aLineCount ); aPositions.reserve( lineCount );
wxPoint origin( aPosition.x, aPosition.y );
int interline = GetInterline( aAttrs.m_Size.y, aAttrs.m_LineSpacing ); int interline = GetInterline( aAttrs.m_Size.y, aAttrs.m_LineSpacing );
int height = 0; int height = 0;
for( int i = 0; i < aLineCount; i++ ) for( int i = 0; i < lineCount; i++ )
{ {
VECTOR2D pos( origin.x, origin.y + i * interline ); VECTOR2D pos( aPosition.x, aPosition.y + i * interline );
VECTOR2D end = boundingBoxSingleLine( nullptr, aStringList[i], pos, aAttrs.m_Size, VECTOR2D end = boundingBoxSingleLine( nullptr, aTextLines[i], pos, aAttrs.m_Size,
aAttrs.m_Angle, aAttrs.m_Italic ); aAttrs.m_Italic );
VECTOR2D bBox( end - pos ); VECTOR2D bBox( end - pos );
aBoundingBoxes.push_back( bBox ); aBoundingBoxes.push_back( bBox );
@ -204,66 +203,26 @@ void FONT::getLinePositions( const UTF8& aText, const VECTOR2D& aPosition,
case GR_TEXT_V_ALIGN_BOTTOM: offset.y -= height; break; case GR_TEXT_V_ALIGN_BOTTOM: offset.y -= height; break;
} }
int mirrorX = aAttrs.m_Mirrored ? -1 : 1; for( int i = 0; i < lineCount; i++ )
for( int i = 0; i < aLineCount; i++ )
{ {
VECTOR2D lineSize = aBoundingBoxes.at( i ); VECTOR2I lineSize = aBoundingBoxes.at( i );
wxPoint lineOffset( offset ); wxPoint lineOffset( offset );
lineOffset.y += i * interline; lineOffset.y += i * interline;
switch( aAttrs.m_Halign ) switch( aAttrs.m_Halign )
{ {
case GR_TEXT_H_ALIGN_LEFT: break; 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_CENTER: lineOffset.x = -lineSize.x / 2; break;
case GR_TEXT_H_ALIGN_RIGHT: lineOffset.x = mirrorX * -lineSize.x; break; case GR_TEXT_H_ALIGN_RIGHT: lineOffset.x = -lineSize.x; break;
} }
VECTOR2I pos( aPosition.x + lineOffset.x, aPosition.y + lineOffset.y ); aPositions.push_back( aPosition + lineOffset );
RotatePoint( pos, origin, aAttrs.m_Angle );
aPositions.push_back( (wxPoint) pos );
} }
} }
VECTOR2D FONT::getBoundingBox( const UTF8& aText, TEXT_STYLE_FLAGS aTextStyle, void FONT::DrawText( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
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 const TEXT_ATTRIBUTES& aAttributes ) const
{ {
// FONT TODO: do we need to set the attributes to the gal at all? // FONT TODO: do we need to set the attributes to the gal at all?
@ -283,45 +242,36 @@ void FONT::DrawText( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosit
* *
* @param aGal * @param aGal
* @param aText is the text to be drawn. * @param aText is the text to be drawn.
* @param aPosition is the text position in world coordinates. * @param aPosition is the text object position in world coordinates.
* @param aAngle is the text rotation angle * @param aCursor is the current text position (for multiple text blocks within a single text
* object, such as a run of superscript characters)
* @param aAttrs are the styling attributes of the text, including its rotation
*/ */
VECTOR2D FONT::Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, VECTOR2D FONT::Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
const VECTOR2D& aOrigin, const TEXT_ATTRIBUTES& aAttrs ) const const VECTOR2I& aCursor, const TEXT_ATTRIBUTES& aAttrs ) const
{ {
if( !aGal || aText.empty() ) if( !aGal || aText.empty() )
return VECTOR2D( 0, 0 ); return VECTOR2D( 0, 0 );
VECTOR2D position( aPosition - aOrigin ); VECTOR2D position( aPosition - aCursor );
// Context needs to be saved before any transformations
//aGal->Save();
// Split multiline strings into separate ones and draw them line by line // Split multiline strings into separate ones and draw them line by line
wxArrayString strings_list; wxArrayString strings_list;
std::vector<wxPoint> positions; std::vector<VECTOR2I> positions;
std::vector<VECTOR2D> boundingBoxes; std::vector<VECTOR2D> boundingBoxes;
int n;
getLinePositions( aText, position, strings_list, positions, n, boundingBoxes, aAttrs ); getLinePositions( aText, position, strings_list, positions, boundingBoxes, aAttrs );
VECTOR2D boundingBox( 0, 0 ); VECTOR2D boundingBox( 0, 0 );
BOX2I lineBoundingBox; BOX2I lineBoundingBox;
for( int i = 0; i < n; i++ )
{
aGal->Save();
aGal->Translate( positions[i] );
aGal->SetLineWidth( aAttrs.m_StrokeWidth ); aGal->SetLineWidth( aAttrs.m_StrokeWidth );
if( !aAttrs.m_Angle.IsZero() ) for( size_t i = 0; i < strings_list.GetCount(); i++ )
aGal->Rotate( aAttrs.m_Angle.Invert().AsRadians() ); {
(void) drawSingleLineText( aGal, &lineBoundingBox, strings_list[i], positions[i],
(void) drawSingleLineText( aGal, &lineBoundingBox, strings_list[i], VECTOR2D( 0, 0 ), aAttrs.m_Size, aAttrs.m_Angle, aAttrs.m_Mirrored, aPosition,
aAttrs.m_Size, aAttrs.m_Angle, aAttrs.m_Italic, aAttrs.m_Italic );
aAttrs.m_Mirrored );
aGal->Restore();
// expand bounding box of whole text // expand bounding box of whole text
boundingBox.x = std::max( boundingBox.x, (double) lineBoundingBox.GetWidth() ); boundingBox.x = std::max( boundingBox.x, (double) lineBoundingBox.GetWidth() );
@ -329,9 +279,6 @@ VECTOR2D FONT::Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosit
boundingBox.y += lineHeight; boundingBox.y += lineHeight;
} }
// undo rotation
//aGal->Restore();
return boundingBox; return boundingBox;
} }
@ -340,11 +287,11 @@ VECTOR2D FONT::Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosit
* @return position of cursor for drawing next substring * @return position of cursor for drawing next substring
*/ */
VECTOR2D drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs, VECTOR2D drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const std::unique_ptr<MARKUP::NODE>& aNode, const VECTOR2D& aPosition, const std::unique_ptr<MARKUP::NODE>& aNode, const VECTOR2I& aPosition,
const KIFONT::FONT* aFont, const VECTOR2D& aGlyphSize, const EDA_ANGLE& aAngle, const KIFONT::FONT* aFont, const VECTOR2D& aSize, const EDA_ANGLE& aAngle,
TEXT_STYLE_FLAGS aTextStyle ) bool aMirror, const VECTOR2I& aOrigin, TEXT_STYLE_FLAGS aTextStyle )
{ {
VECTOR2D nextPosition = aPosition; VECTOR2I nextPosition = aPosition;
TEXT_STYLE_FLAGS textStyle = aTextStyle; TEXT_STYLE_FLAGS textStyle = aTextStyle;
@ -361,27 +308,20 @@ VECTOR2D drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& a
if( aNode->has_content() ) if( aNode->has_content() )
{ {
std::string txt = aNode->string(); std::string txt = aNode->string();
//std::vector<SHAPE_POLY_SET> glyphs;
wxPoint pt( aPosition.x, aPosition.y );
BOX2I bbox; BOX2I bbox;
nextPosition = aFont->GetTextAsGlyphs( &bbox, aGlyphs, txt, aGlyphSize, pt, aAngle,
textStyle ); nextPosition = aFont->GetTextAsGlyphs( &bbox, aGlyphs, txt, aSize, aPosition, aAngle,
aMirror, aOrigin, textStyle );
if( aBoundingBox ) if( aBoundingBox )
{ aBoundingBox->Merge( bbox );
BOX2I boundingBox;
boundingBox = aBoundingBox->Merge( bbox );
aBoundingBox->SetOrigin( boundingBox.GetOrigin() );
aBoundingBox->SetSize( boundingBox.GetSize() );
}
} }
} }
for( const auto& child : aNode->children ) for( const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
{ {
nextPosition = drawMarkup( aBoundingBox, aGlyphs, child, nextPosition, aFont, aGlyphSize, nextPosition = drawMarkup( aBoundingBox, aGlyphs, child, nextPosition, aFont, aSize,
aAngle, textStyle ); aAngle, aMirror, aOrigin, textStyle );
} }
return nextPosition; return nextPosition;
@ -389,20 +329,22 @@ VECTOR2D drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& a
VECTOR2D FONT::drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs, VECTOR2D FONT::drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const UTF8& aText, const VECTOR2D& aPosition, const VECTOR2D& aGlyphSize, const UTF8& aText, const VECTOR2I& aPosition, const VECTOR2D& aSize,
const EDA_ANGLE& aAngle, TEXT_STYLE_FLAGS aTextStyle ) const const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const
{ {
MARKUP::MARKUP_PARSER markupParser( aText ); MARKUP::MARKUP_PARSER markupParser( aText );
std::unique_ptr<MARKUP::NODE> root = markupParser.Parse(); std::unique_ptr<MARKUP::NODE> root = markupParser.Parse();
return ::drawMarkup( aBoundingBox, aGlyphs, root, aPosition, this, aGlyphSize, aAngle, return ::drawMarkup( aBoundingBox, aGlyphs, root, aPosition, this, aSize, aAngle, aMirror,
aTextStyle ); aOrigin, aTextStyle );
} }
VECTOR2D FONT::drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const UTF8& aText, VECTOR2D FONT::drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const UTF8& aText,
const VECTOR2D& aPosition, const VECTOR2D& aGlyphSize, const VECTOR2I& aPosition, const VECTOR2D& aSize,
const EDA_ANGLE& aAngle, bool aIsItalic, bool aIsMirrored ) const const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
bool aItalic ) const
{ {
if( !aGal ) if( !aGal )
{ {
@ -412,37 +354,32 @@ VECTOR2D FONT::drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const
TEXT_STYLE_FLAGS textStyle = 0; TEXT_STYLE_FLAGS textStyle = 0;
if( aIsItalic ) if( aItalic )
textStyle |= TEXT_STYLE::ITALIC; textStyle |= TEXT_STYLE::ITALIC;
std::vector<std::unique_ptr<GLYPH>> glyphs; std::vector<std::unique_ptr<GLYPH>> glyphs;
VECTOR2D nextPosition = drawMarkup( aBoundingBox, glyphs, aText, aPosition, aGlyphSize, VECTOR2D nextPosition = drawMarkup( aBoundingBox, glyphs, aText, aPosition, aSize, aAngle,
aAngle, textStyle ); aMirror, aOrigin, textStyle );
for( const std::unique_ptr<GLYPH>& glyph : glyphs ) for( const std::unique_ptr<GLYPH>& glyph : glyphs )
{
if( aIsMirrored )
glyph->Mirror( aPosition );
aGal->DrawGlyph( *glyph.get() ); aGal->DrawGlyph( *glyph.get() );
}
return nextPosition; return nextPosition;
} }
VECTOR2D FONT::boundingBoxSingleLine( BOX2I* aBoundingBox, const UTF8& aText, VECTOR2D FONT::boundingBoxSingleLine( BOX2I* aBoundingBox, const UTF8& aText,
const VECTOR2D& aPosition, const VECTOR2D& aGlyphSize, const VECTOR2I& aPosition, const VECTOR2D& aSize,
const EDA_ANGLE& aAngle, bool aIsItalic ) const bool aItalic ) const
{ {
TEXT_STYLE_FLAGS textStyle = 0; TEXT_STYLE_FLAGS textStyle = 0;
if( aIsItalic ) if( aItalic )
textStyle |= TEXT_STYLE::ITALIC; textStyle |= TEXT_STYLE::ITALIC;
std::vector<std::unique_ptr<GLYPH>> glyphs; // ignored std::vector<std::unique_ptr<GLYPH>> glyphs; // ignored
VECTOR2D nextPosition = drawMarkup( aBoundingBox, glyphs, aText, aPosition, aGlyphSize, VECTOR2D nextPosition = drawMarkup( aBoundingBox, glyphs, aText, aPosition, aSize,
aAngle, textStyle ); EDA_ANGLE::ANGLE_0, false, VECTOR2I(), textStyle );
return nextPosition; return nextPosition;
} }

View File

@ -20,6 +20,7 @@
#include <vector> #include <vector>
#include <font/glyph.h> #include <font/glyph.h>
#include <trigo.h>
using namespace KIFONT; using namespace KIFONT;
@ -62,8 +63,9 @@ void STROKE_GLYPH::Finalize()
} }
std::unique_ptr<GLYPH> STROKE_GLYPH::Transform( const VECTOR2D& aGlyphSize, const VECTOR2D& aOffset, std::unique_ptr<GLYPH> STROKE_GLYPH::Transform( const VECTOR2D& aGlyphSize, const VECTOR2I& aOffset,
double aTilt ) double aTilt, const EDA_ANGLE& aAngle, bool aMirror,
const VECTOR2I& aOrigin )
{ {
std::unique_ptr<STROKE_GLYPH> glyph = std::make_unique<STROKE_GLYPH>( *this ); std::unique_ptr<STROKE_GLYPH> glyph = std::make_unique<STROKE_GLYPH>( *this );
@ -82,14 +84,18 @@ std::unique_ptr<GLYPH> STROKE_GLYPH::Transform( const VECTOR2D& aGlyphSize, cons
{ {
for( VECTOR2D& point : pointList ) for( VECTOR2D& point : pointList )
{ {
point.x *= aGlyphSize.x; point *= aGlyphSize;
point.y *= aGlyphSize.y;
if( aTilt ) if( aTilt )
point.x -= point.y * aTilt; point.x -= point.y * aTilt;
point.x += aOffset.x; point += aOffset;
point.y += aOffset.y;
if( aMirror )
point.x = aOrigin.x - ( point.x - aOrigin.x );
if( !aAngle.IsZero() )
RotatePoint( point, aOrigin, aAngle );
} }
} }
@ -97,35 +103,8 @@ std::unique_ptr<GLYPH> STROKE_GLYPH::Transform( const VECTOR2D& aGlyphSize, cons
} }
void STROKE_GLYPH::Mirror( const VECTOR2D& aMirrorOrigin )
{
double originX = aMirrorOrigin.x;
VECTOR2D pos = m_boundingBox.GetPosition();
VECTOR2D end = m_boundingBox.GetEnd();
pos.x = originX - ( pos.x - originX );
end.x = originX - ( end.x - originX );
m_boundingBox.SetOrigin( pos );
m_boundingBox.SetEnd( end );
for( std::vector<VECTOR2D>& pointList : *this )
{
for( VECTOR2D& point : pointList )
point.x = originX - ( point.x - originX );
}
}
BOX2D OUTLINE_GLYPH::BoundingBox() BOX2D OUTLINE_GLYPH::BoundingBox()
{ {
BOX2I bbox = BBox(); BOX2I bbox = BBox();
return BOX2D( bbox.GetOrigin(), bbox.GetSize() ); return BOX2D( bbox.GetOrigin(), bbox.GetSize() );
} }
void OUTLINE_GLYPH::Mirror( const VECTOR2D& aMirrorOrigin )
{
SHAPE_POLY_SET::Mirror( true, false, aMirrorOrigin );
}

View File

@ -36,14 +36,30 @@
#include FT_BBOX_H #include FT_BBOX_H
#include <trigo.h> #include <trigo.h>
#include <font/fontconfig.h> #include <font/fontconfig.h>
#include <convert_basic_shapes_to_polygon.h>
using namespace KIFONT; using namespace KIFONT;
// The height of the KiCad stroke font is the distance between stroke endpoints for a vertical
// line of cap-height. So the cap-height of the font is actually stroke-width taller than its
// height.
// Outline fonts are normally scaled on full-height (including ascenders and descenders), so we
// need to compensate to keep them from being much smaller than their stroked counterparts.
constexpr double OUTLINE_FONT_SIZE_COMPENSATION = 1.4;
// The KiCad stroke font uses a subscript/superscript size ratio of 0.7. This ratio is also
// commonly used in LaTeX, but fonts with designed-in subscript and superscript glyphs are more
// likely to use 0.58.
// For auto-generated subscript and superscript glyphs in outline fonts we split the difference
// with 0.64.
static constexpr double SUBSCRIPT_SUPERSCRIPT_SIZE = 0.64;
FT_Library OUTLINE_FONT::m_freeType = nullptr; FT_Library OUTLINE_FONT::m_freeType = nullptr;
OUTLINE_FONT::OUTLINE_FONT() : OUTLINE_FONT::OUTLINE_FONT() :
m_faceSize( 16 ), m_faceSize( 16 )
m_subscriptSize( 13 )
{ {
if( !m_freeType ) if( !m_freeType )
{ {
@ -83,27 +99,6 @@ bool OUTLINE_FONT::loadFontSimple( const wxString& aFontFileName )
// TODO: handle ft_error properly (now we just return false if load does not succeed) // TODO: handle ft_error properly (now we just return false if load does not succeed)
FT_Error ft_error = loadFace( fileName ); FT_Error ft_error = loadFace( fileName );
if( ft_error )
{
// Try user dir
fontFile.SetExt( "otf" );
fontFile.SetPath( Pgm().GetSettingsManager().GetUserSettingsPath() + wxT( "/fonts" ) );
fileName = fontFile.GetFullPath();
if( wxFile::Exists( fileName ) )
{
ft_error = loadFace( fileName );
}
else
{
fontFile.SetExt( "ttf" );
fileName = fontFile.GetFullPath();
if( wxFile::Exists( fileName ) )
ft_error = loadFace( fileName );
}
}
if( ft_error == FT_Err_Unknown_File_Format ) if( ft_error == FT_Err_Unknown_File_Format )
{ {
wxLogWarning( _( "The font file %s could be opened and read, " wxLogWarning( _( "The font file %s could be opened and read, "
@ -128,7 +123,7 @@ bool OUTLINE_FONT::loadFontSimple( const wxString& aFontFileName )
FT_Error OUTLINE_FONT::loadFace( const wxString& aFontFileName ) FT_Error OUTLINE_FONT::loadFace( const wxString& aFontFileName )
{ {
m_faceScaler = m_faceSize * 64; m_faceScaler = m_faceSize * 64;
m_subscriptFaceScaler = m_subscriptSize * 64; m_subscriptFaceScaler = KiROUND( m_faceSize * 64 * SUBSCRIPT_SUPERSCRIPT_SIZE );
// TODO: check that going from wxString to char* with UTF-8 // TODO: check that going from wxString to char* with UTF-8
// conversion for filename makes sense on any/all platforms // conversion for filename makes sense on any/all platforms
@ -223,10 +218,12 @@ double OUTLINE_FONT::ComputeOverbarVerticalPosition( double aGlyphHeight ) const
*/ */
double OUTLINE_FONT::GetInterline( double aGlyphHeight, double aLineSpacing ) const double OUTLINE_FONT::GetInterline( double aGlyphHeight, double aLineSpacing ) const
{ {
double pitch = INTERLINE_PITCH_RATIO;
if( GetFace()->units_per_EM ) if( GetFace()->units_per_EM )
return ( aLineSpacing * aGlyphHeight * ( GetFace()->height / GetFace()->units_per_EM ) ); pitch = GetFace()->height / GetFace()->units_per_EM;
else
return ( aLineSpacing * aGlyphHeight * INTERLINE_PITCH_RATIO ); return ( aLineSpacing * aGlyphHeight * pitch * OUTLINE_FONT_SIZE_COMPENSATION );
} }
@ -292,32 +289,34 @@ VECTOR2I OUTLINE_FONT::GetLinesAsGlyphs( std::vector<std::unique_ptr<GLYPH>>& aG
const EDA_TEXT* aText ) const const EDA_TEXT* aText ) const
{ {
wxArrayString strings; wxArrayString strings;
std::vector<wxPoint> positions; std::vector<VECTOR2I> positions;
int n;
VECTOR2I ret; VECTOR2I ret;
std::vector<VECTOR2D> boundingBoxes; std::vector<VECTOR2D> boundingBoxes;
TEXT_ATTRIBUTES attrs = aText->GetAttributes();
TEXT_STYLE_FLAGS textStyle = 0; TEXT_STYLE_FLAGS textStyle = 0;
attrs.m_Angle = aText->GetDrawRotation();
if( aText->IsItalic() ) if( aText->IsItalic() )
textStyle |= TEXT_STYLE::ITALIC; textStyle |= TEXT_STYLE::ITALIC;
getLinePositions( aText->GetShownText(), aText->GetTextPos(), strings, positions, n, getLinePositions( aText->GetShownText(), aText->GetTextPos(), strings, positions, boundingBoxes,
boundingBoxes, aText->GetAttributes() ); attrs );
for( int i = 0; i < n; i++ ) for( size_t i = 0; i < strings.GetCount(); i++ )
{ {
ret = drawMarkup( nullptr, aGlyphs, UTF8( strings.Item( i ) ), positions[i], ret = drawMarkup( nullptr, aGlyphs, UTF8( strings.Item( i ) ), positions[i], attrs.m_Size,
aText->GetTextSize(), aText->GetTextAngle(), textStyle ); attrs.m_Angle, attrs.m_Mirrored, aText->GetTextPos(), textStyle );
} }
return ret; return ret;
} }
VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox, VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
std::vector<std::unique_ptr<GLYPH>>& aGlyphs, const UTF8& aText, const VECTOR2D& aSize,
const UTF8& aText, const VECTOR2D& aGlyphSize, const VECTOR2I& aPosition, const EDA_ANGLE& aAngle,
const wxPoint& aPosition, const EDA_ANGLE& aOrientation, bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const TEXT_STYLE_FLAGS aTextStyle ) const
{ {
hb_buffer_t* buf = hb_buffer_create(); hb_buffer_t* buf = hb_buffer_create();
@ -331,28 +330,22 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox,
hb_glyph_position_t* glyphPos = hb_buffer_get_glyph_positions( buf, &glyphCount ); hb_glyph_position_t* glyphPos = hb_buffer_get_glyph_positions( buf, &glyphCount );
hb_font_t* referencedFont; hb_font_t* referencedFont;
//const double subscriptAndSuperscriptScaler = 0.5; VECTOR2D glyphSize = aSize;
VECTOR2D glyphSize = aGlyphSize;
FT_Face face = m_face; FT_Face face = m_face;
int scaler = m_faceScaler; double scaler = m_faceScaler / OUTLINE_FONT_SIZE_COMPENSATION;
if( IsSubscript( aTextStyle ) || IsSuperscript( aTextStyle ) ) if( IsSubscript( aTextStyle ) || IsSuperscript( aTextStyle ) )
{
face = m_subscriptFace; face = m_subscriptFace;
//scaler = m_subscriptFaceScaler;
}
referencedFont = hb_ft_font_create_referenced( face ); referencedFont = hb_ft_font_create_referenced( face );
hb_ft_font_set_funcs( referencedFont ); hb_ft_font_set_funcs( referencedFont );
hb_shape( referencedFont, buf, nullptr, 0 ); hb_shape( referencedFont, buf, nullptr, 0 );
const VECTOR2D scaleFactor( -glyphSize.x / scaler, glyphSize.y / scaler ); const VECTOR2D scaleFactor( glyphSize.x / scaler, -glyphSize.y / scaler );
VECTOR2I cursor( 0, 0 ); VECTOR2I cursor( 0, 0 );
VECTOR2I extentBottomLeft( INT_MAX, INT_MAX ); VECTOR2D topLeft( INT_MAX * 1.0, -INT_MAX * 1.0 );
VECTOR2I extentTopRight( INT_MIN, INT_MIN ); VECTOR2D topRight( -INT_MAX * 1.0, -INT_MAX * 1.0 );
VECTOR2I vBottomLeft( INT_MAX, INT_MAX );
VECTOR2I vTopRight( INT_MIN, INT_MIN );
for( unsigned int i = 0; i < glyphCount; i++ ) for( unsigned int i = 0; i < glyphCount; i++ )
{ {
@ -380,46 +373,34 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox,
GLYPH_POINTS points = c.points; GLYPH_POINTS points = c.points;
SHAPE_LINE_CHAIN shape; SHAPE_LINE_CHAIN shape;
VECTOR2D offset( aPosition );
if( IsSubscript( aTextStyle ) )
offset.y += glyphSize.y * 0.1;
else if( IsSuperscript( aTextStyle ) )
offset.y -= glyphSize.y * 0.2;
for( const VECTOR2D& v : points ) for( const VECTOR2D& v : points )
{ {
// Save text extents VECTOR2D pt( v + cursor );
if( vBottomLeft.x > v.x )
vBottomLeft.x = v.x;
if( vBottomLeft.y > v.y )
vBottomLeft.y = v.y;
if( vTopRight.x < v.x )
vTopRight.x = v.x;
if( vTopRight.y < v.y )
vTopRight.y = v.y;
VECTOR2D pt( v.x, v.y ); topLeft.x = std::min( topLeft.x, pt.x );
VECTOR2D ptC( pt.x + cursor.x, pt.y + cursor.y ); topLeft.y = std::max( topLeft.y, pt.y );
wxPoint scaledPtOrig( -ptC.x * scaleFactor.x, -ptC.y * scaleFactor.y ); topRight.x = std::max( topRight.x, pt.x );
wxPoint scaledPt( scaledPtOrig ); topRight.y = std::max( topRight.y, pt.y );
RotatePoint( &scaledPt.x, &scaledPt.y, aOrientation.AsTenthsOfADegree() );
scaledPt.x += offset.x;
scaledPt.y += offset.y;
if( extentBottomLeft.x > scaledPt.x ) if( IsSubscript( aTextStyle ) )
extentBottomLeft.x = scaledPt.x; pt.y -= 0.25 * scaler;
if( extentBottomLeft.y > scaledPt.y ) else if( IsSuperscript( aTextStyle ) )
extentBottomLeft.y = scaledPt.y; pt.y += 0.45 * scaler;
if( extentTopRight.x < scaledPt.x )
extentTopRight.x = scaledPt.x;
if( extentTopRight.y < scaledPt.y )
extentTopRight.y = scaledPt.y;
shape.Append( scaledPt.x, scaledPt.y ); pt *= scaleFactor;
//ptListScaled.push_back( scaledPt ); pt += aPosition;
if( aMirror )
pt.x = aOrigin.x - ( pt.x - aOrigin.x );
if( !aAngle.IsZero() )
RotatePoint( pt, aOrigin, aAngle );
shape.Append( pt.x, pt.y );
} }
shape.SetClosed( true );
if( contourIsHole( c ) ) if( contourIsHole( c ) )
holes.push_back( std::move( shape ) ); holes.push_back( std::move( shape ) );
else else
@ -429,11 +410,8 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox,
for( SHAPE_LINE_CHAIN& outline : outlines ) for( SHAPE_LINE_CHAIN& outline : outlines )
{ {
if( outline.PointCount() ) if( outline.PointCount() )
{
outline.SetClosed( true );
glyph->AddOutline( outline ); glyph->AddOutline( outline );
} }
}
int nthHole = 0; int nthHole = 0;
@ -441,9 +419,7 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox,
{ {
if( hole.PointCount() ) if( hole.PointCount() )
{ {
hole.SetClosed( true );
VECTOR2I firstPoint = hole.GetPoint( 0 ); VECTOR2I firstPoint = hole.GetPoint( 0 );
//SHAPE_SIMPLE *outlineForHole = nullptr;
int nthOutline = -1; int nthOutline = -1;
int n = 0; int n = 0;
@ -451,7 +427,6 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox,
{ {
if( outline.PointInside( firstPoint ) ) if( outline.PointInside( firstPoint ) )
{ {
//outlineForHole = outline;
nthOutline = n; nthOutline = n;
break; break;
} }
@ -476,106 +451,52 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox,
if( IsOverbar( aTextStyle ) ) if( IsOverbar( aTextStyle ) )
{ {
std::unique_ptr<OUTLINE_GLYPH> overbarGlyph = std::make_unique<OUTLINE_GLYPH>(); topLeft *= scaleFactor;
SHAPE_LINE_CHAIN overbar; topRight *= scaleFactor;
int left = extentBottomLeft.x; topLeft.y -= aSize.y * 0.16;
int right = extentTopRight.x; topRight.y -= aSize.y * 0.16;
int top = extentBottomLeft.y - 800;
int barHeight = -3200;
overbar.Append( VECTOR2D( left, top ) ); topLeft += aPosition;
overbar.Append( VECTOR2D( right, top ) ); topRight += aPosition;
overbar.Append( VECTOR2D( right, top + barHeight ) );
overbar.Append( VECTOR2D( left, top + barHeight ) );
overbar.SetClosed( true );
overbarGlyph->AddOutline( overbar ); if( !aAngle.IsZero() )
{
RotatePoint( topLeft, aOrigin, aAngle );
RotatePoint( topRight, aOrigin, aAngle );
}
double overbarHeight = aSize.y * 0.07;
SHAPE_POLY_SET overbar;
TransformOvalToPolygon( overbar, topLeft, topRight, overbarHeight, overbarHeight / 8,
ERROR_INSIDE );
std::unique_ptr<OUTLINE_GLYPH> overbarGlyph = std::make_unique<OUTLINE_GLYPH>( overbar );
aGlyphs.push_back( std::move( overbarGlyph ) ); aGlyphs.push_back( std::move( overbarGlyph ) );
} }
hb_buffer_destroy( buf ); hb_buffer_destroy( buf );
VECTOR2I cursorDisplacement( -cursorEnd.x * scaleFactor.x, cursorEnd.y * scaleFactor.y ); VECTOR2I cursorDisplacement( cursorEnd.x * scaleFactor.x, -cursorEnd.y * scaleFactor.y );
if( aBoundingBox ) if( aBBox )
{ {
aBoundingBox->SetOrigin( aPosition.x, aPosition.y ); aBBox->SetOrigin( aPosition.x, aPosition.y );
aBoundingBox->SetEnd( cursorDisplacement ); aBBox->SetEnd( cursorDisplacement );
} }
return VECTOR2I( aPosition.x + cursorDisplacement.x, aPosition.y + cursorDisplacement.y ); return VECTOR2I( aPosition.x + cursorDisplacement.x, aPosition.y + cursorDisplacement.y );
} }
VECTOR2D OUTLINE_FONT::getBoundingBox( const UTF8& aString, const VECTOR2D& aGlyphSize,
TEXT_STYLE_FLAGS aTextStyle ) const
{
hb_buffer_t* buf = hb_buffer_create();
hb_buffer_add_utf8( buf, aString.c_str(), -1, 0, -1 );
// guess direction, script, and language based on contents
hb_buffer_guess_segment_properties( buf );
FT_Face face = m_face;
int scaler = m_faceScaler;
if( IsSubscript( aTextStyle ) || IsSuperscript( aTextStyle ) )
face = m_subscriptFace;
hb_font_t* referencedFont = hb_ft_font_create_referenced( face );
hb_ft_font_set_funcs( referencedFont );
hb_shape( referencedFont, buf, nullptr, 0 );
unsigned int glyphCount;
hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( buf, &glyphCount );
//hb_glyph_position_t* glyphPos = hb_buffer_get_glyph_positions( buf, &glyphCount );
VECTOR2D boundingBox( 0, 0 );
int xScaler = aGlyphSize.x / scaler;
int yScaler = aGlyphSize.y / scaler;
double maxHeight = 0.0;
for( unsigned int i = 0; i < glyphCount; i++ )
{
//hb_glyph_position_t& pos = glyphPos[i];
int codepoint = glyphInfo[i].codepoint;
FT_Load_Glyph( face, codepoint, FT_LOAD_NO_BITMAP );
FT_GlyphSlot glyphSlot = face->glyph;
FT_Glyph glyph;
FT_BBox controlBox;
FT_Get_Glyph( glyphSlot, &glyph );
FT_Glyph_Get_CBox( glyph, FT_Glyph_BBox_Mode::FT_GLYPH_BBOX_UNSCALED, &controlBox );
double width = controlBox.xMax * xScaler;
boundingBox.x += width;
double height = controlBox.yMax * yScaler;
if( height > maxHeight )
maxHeight = height;
FT_Done_Glyph( glyph );
}
boundingBox.y = aGlyphSize.y; //maxHeight;
hb_buffer_destroy( buf );
return boundingBox;
}
#undef OUTLINEFONT_RENDER_AS_PIXELS #undef OUTLINEFONT_RENDER_AS_PIXELS
#ifdef OUTLINEFONT_RENDER_AS_PIXELS #ifdef OUTLINEFONT_RENDER_AS_PIXELS
/* /*
* WIP: eeschema (and PDF output?) should use pixel rendering instead of linear segmentation * WIP: eeschema (and PDF output?) should use pixel rendering instead of linear segmentation
*/ */
void OUTLINE_FONT::RenderToOpenGLCanvas( KIGFX::OPENGL_GAL& aGal, const UTF8& aString, void OUTLINE_FONT::RenderToOpenGLCanvas( KIGFX::OPENGL_GAL& aGal, const UTF8& aString,
const VECTOR2D& aGlyphSize, const wxPoint& aPosition, const VECTOR2D& aGlyphSize, const VECTOR2I& aPosition,
const EDA_ANGLE& aOrientation, bool aIsMirrored ) const const EDA_ANGLE& aOrientation, bool aIsMirrored ) const
{ {
hb_buffer_t* buf = hb_buffer_create(); hb_buffer_t* buf = hb_buffer_create();

View File

@ -211,28 +211,20 @@ VECTOR2D STROKE_FONT::StringBoundaryLimits( const KIGFX::GAL* aGal, const UTF8&
BOX2I boundingBox; BOX2I boundingBox;
(void) drawMarkup( &boundingBox, glyphs, aText, VECTOR2D(), aGlyphSize, EDA_ANGLE::ANGLE_0, (void) drawMarkup( &boundingBox, glyphs, aText, VECTOR2D(), aGlyphSize, EDA_ANGLE::ANGLE_0,
0 /* TODO: this should really include TEXT_STYLE::ITALIC if set */ ); false, VECTOR2D(), 0 /* TODO: should include TEXT_STYLE::ITALIC if set */ );
return boundingBox.GetSize(); return boundingBox.GetSize();
} }
VECTOR2D STROKE_FONT::getBoundingBox( const UTF8& aString, const VECTOR2D& aGlyphSize, VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
TEXT_STYLE_FLAGS aTextStyle ) const const UTF8& aText, const VECTOR2D& aSize,
{ const VECTOR2I& aPosition, const EDA_ANGLE& aAngle,
// TODO: take glyph thickness into account! bool aMirror, const VECTOR2I& aOrigin,
return StringBoundaryLimits( nullptr, aString, aGlyphSize, 0 );
}
VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox,
std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const UTF8& aText, const VECTOR2D& aGlyphSize,
const wxPoint& aPosition, const EDA_ANGLE& aAngle,
TEXT_STYLE_FLAGS aTextStyle ) const TEXT_STYLE_FLAGS aTextStyle ) const
{ {
wxPoint cursor( aPosition ); wxPoint cursor( aPosition );
VECTOR2D glyphSize( aGlyphSize ); VECTOR2D glyphSize( aSize );
double tilt = ( aTextStyle & TEXT_STYLE::ITALIC ) ? ITALIC_TILT : 0.0; double tilt = ( aTextStyle & TEXT_STYLE::ITALIC ) ? ITALIC_TILT : 0.0;
if( aTextStyle & TEXT_STYLE::SUBSCRIPT || aTextStyle & TEXT_STYLE::SUPERSCRIPT ) if( aTextStyle & TEXT_STYLE::SUBSCRIPT || aTextStyle & TEXT_STYLE::SUPERSCRIPT )
@ -282,7 +274,8 @@ VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox,
{ {
STROKE_GLYPH* source = static_cast<STROKE_GLYPH*>( m_glyphs->at( dd ).get() ); STROKE_GLYPH* source = static_cast<STROKE_GLYPH*>( m_glyphs->at( dd ).get() );
aGlyphs.push_back( source->Transform( glyphSize, cursor, tilt ) ); aGlyphs.push_back( source->Transform( glyphSize, cursor, tilt, aAngle, aMirror,
aOrigin ) );
cursor.x = aGlyphs.back()->BoundingBox().GetEnd().x; cursor.x = aGlyphs.back()->BoundingBox().GetEnd().x;
} }
@ -299,18 +292,27 @@ VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox,
if( aTextStyle & TEXT_STYLE::ITALIC ) if( aTextStyle & TEXT_STYLE::ITALIC )
barOffset.x = barOffset.y * ITALIC_TILT; barOffset.x = barOffset.y * ITALIC_TILT;
overbarGlyph->AddPoint( VECTOR2D( aPosition.x + barOffset.x, cursor.y - barOffset.y ) ); VECTOR2D barStart( aPosition.x + barOffset.x, cursor.y - barOffset.y );
overbarGlyph->AddPoint( VECTOR2D( cursor.x + barOffset.x, cursor.y - barOffset.y ) ); VECTOR2D barEnd( cursor.x + barOffset.x, cursor.y - barOffset.y );
if( !aAngle.IsZero() )
{
RotatePoint( barStart, aOrigin, aAngle );
RotatePoint( barEnd, aOrigin, aAngle );
}
overbarGlyph->AddPoint( barStart );
overbarGlyph->AddPoint( barEnd );
overbarGlyph->Finalize(); overbarGlyph->Finalize();
aGlyphs.push_back( std::move( overbarGlyph ) ); aGlyphs.push_back( std::move( overbarGlyph ) );
} }
if( aBoundingBox ) if( aBBox )
{ {
aBoundingBox->SetOrigin( aPosition.x, aPosition.y ); aBBox->SetOrigin( aPosition );
aBoundingBox->SetEnd( cursor.x + barOffset.x, cursor.y + std::max( glyphSize.y, barOffset.y ) ); aBBox->SetEnd( cursor.x + barOffset.x, cursor.y + std::max( glyphSize.y, barOffset.y ) );
aBoundingBox->Normalize(); aBBox->Normalize();
} }
return VECTOR2I( cursor.x, aPosition.y ); return VECTOR2I( cursor.x, aPosition.y );

View File

@ -238,6 +238,7 @@ rect
rect_delta rect_delta
reference reference
remove_unused_layers remove_unused_layers
render_cache
right right
rotate rotate
roundrect roundrect

View File

@ -53,9 +53,9 @@ void FONT_CHOICE::SetFontSelection( KIFONT::FONT* aFont )
if( !aFont ) if( !aFont )
{ {
SetSelection( 0 ); SetSelection( 0 );
return;
} }
else
{
SetStringSelection( aFont->Name() ); SetStringSelection( aFont->Name() );
if( GetSelection() == wxNOT_FOUND ) if( GetSelection() == wxNOT_FOUND )
@ -65,6 +65,9 @@ void FONT_CHOICE::SetFontSelection( KIFONT::FONT* aFont )
} }
} }
SendSelectionChangedEvent( wxEVT_CHOICE );
}
bool FONT_CHOICE::HaveFontSelection() const bool FONT_CHOICE::HaveFontSelection() const
{ {

View File

@ -30,6 +30,7 @@
#include <outline_mode.h> #include <outline_mode.h>
#include <eda_rect.h> #include <eda_rect.h>
#include <font/glyph.h>
#include <font/text_attributes.h> #include <font/text_attributes.h>
class OUTPUTFORMATTER; class OUTPUTFORMATTER;
@ -52,6 +53,13 @@ struct TSEGM_2_POLY_PRMS
}; };
struct TSEGM_2_SHAPE_PRMS
{
int m_penWidth;
SHAPE_COMPOUND* m_shape;
};
/** /**
* Callback function used to convert text segments to polygons. * Callback function used to convert text segments to polygons.
*/ */
@ -97,6 +105,8 @@ public:
virtual ~EDA_TEXT(); virtual ~EDA_TEXT();
EDA_TEXT& operator=( const EDA_TEXT& aItem );
/** /**
* Return the string associated with the text object. * Return the string associated with the text object.
* *
@ -128,7 +138,7 @@ public:
* The TextThickness is that set by the user. The EffectiveTextPenWidth also factors * The TextThickness is that set by the user. The EffectiveTextPenWidth also factors
* in bold text and thickness clamping. * in bold text and thickness clamping.
*/ */
void SetTextThickness( int aWidth ) { m_attributes.m_StrokeWidth = aWidth; }; void SetTextThickness( int aWidth );
int GetTextThickness() const { return m_attributes.m_StrokeWidth; }; int GetTextThickness() const { return m_attributes.m_StrokeWidth; };
/** /**
@ -141,42 +151,38 @@ public:
// Higher level classes may be more restrictive than this by overloading // Higher level classes may be more restrictive than this by overloading
// SetTextAngle() or merely calling EDA_TEXT::SetTextAngle() after clamping // SetTextAngle() or merely calling EDA_TEXT::SetTextAngle() after clamping
// aAngle before calling this lowest inline accessor. // aAngle before calling this lowest inline accessor.
m_attributes.m_Angle = EDA_ANGLE( aAngleInTenthsOfADegree, EDA_ANGLE::TENTHS_OF_A_DEGREE ); SetTextAngle( EDA_ANGLE( aAngleInTenthsOfADegree, EDA_ANGLE::TENTHS_OF_A_DEGREE ) );
}
void SetTextAngle( const EDA_ANGLE& aAngle )
{
m_attributes.m_Angle = aAngle;
} }
void SetTextAngle( const EDA_ANGLE& aAngle );
const EDA_ANGLE& GetTextAngle() const { return m_attributes.m_Angle; } const EDA_ANGLE& GetTextAngle() const { return m_attributes.m_Angle; }
void SetItalic( bool aItalic ) { m_attributes.m_Italic = aItalic; } void SetItalic( bool aItalic );
bool IsItalic() const { return m_attributes.m_Italic; } bool IsItalic() const { return m_attributes.m_Italic; }
void SetBold( bool aBold ) { m_attributes.m_Bold = aBold; } void SetBold( bool aBold );
bool IsBold() const { return m_attributes.m_Bold; } bool IsBold() const { return m_attributes.m_Bold; }
virtual void SetVisible( bool aVisible ) { m_attributes.m_Visible = aVisible; } virtual void SetVisible( bool aVisible );
virtual bool IsVisible() const { return m_attributes.m_Visible; } virtual bool IsVisible() const { return m_attributes.m_Visible; }
void SetMirrored( bool isMirrored ) { m_attributes.m_Mirrored = isMirrored; } void SetMirrored( bool isMirrored );
bool IsMirrored() const { return m_attributes.m_Mirrored; } bool IsMirrored() const { return m_attributes.m_Mirrored; }
/** /**
* @param aAllow true if ok to use multiline option, false if ok to use only single line * @param aAllow true if ok to use multiline option, false if ok to use only single line
* text. (Single line is faster in calculations than multiline.) * text. (Single line is faster in calculations than multiline.)
*/ */
void SetMultilineAllowed( bool aAllow ) { m_attributes.m_Multiline = aAllow; } void SetMultilineAllowed( bool aAllow );
bool IsMultilineAllowed() const { return m_attributes.m_Multiline; } bool IsMultilineAllowed() const { return m_attributes.m_Multiline; }
void SetHorizJustify( GR_TEXT_H_ALIGN_T aType );
GR_TEXT_H_ALIGN_T GetHorizJustify() const { return m_attributes.m_Halign; }; GR_TEXT_H_ALIGN_T GetHorizJustify() const { return m_attributes.m_Halign; };
void SetVertJustify( GR_TEXT_V_ALIGN_T aType );
GR_TEXT_V_ALIGN_T GetVertJustify() const { return m_attributes.m_Valign; }; GR_TEXT_V_ALIGN_T GetVertJustify() const { return m_attributes.m_Valign; };
void SetHorizJustify( GR_TEXT_H_ALIGN_T aType ) { m_attributes.m_Halign = aType; }; void SetKeepUpright( bool aKeepUpright );
void SetVertJustify( GR_TEXT_V_ALIGN_T aType ) { m_attributes.m_Valign = aType; };
void SetKeepUpright( bool aKeepUpright ) { m_attributes.m_KeepUpright = aKeepUpright; }
bool IsKeepUpright() const { return m_attributes.m_KeepUpright; } bool IsKeepUpright() const { return m_attributes.m_KeepUpright; }
/** /**
@ -208,36 +214,35 @@ public:
bool IsDefaultFormatting() const; bool IsDefaultFormatting() const;
void SetFont( KIFONT::FONT* aFont ) { m_attributes.m_Font = aFont; } void SetFont( KIFONT::FONT* aFont );
KIFONT::FONT* GetFont() const { return m_attributes.m_Font; } KIFONT::FONT* GetFont() const { return m_attributes.m_Font; }
wxString GetFontName() const; wxString GetFontName() const;
void SetLineSpacing( double aLineSpacing ) { m_attributes.m_LineSpacing = aLineSpacing; } void SetLineSpacing( double aLineSpacing );
double GetLineSpacing() const { return m_attributes.m_LineSpacing; } double GetLineSpacing() const { return m_attributes.m_LineSpacing; }
void SetTextSize( const wxSize& aNewSize ) { m_attributes.m_Size = aNewSize; } void SetTextSize( const wxSize& aNewSize );
wxSize GetTextSize() const { return wxSize( m_attributes.m_Size.x, wxSize GetTextSize() const { return wxSize( m_attributes.m_Size.x,
m_attributes.m_Size.y ); } m_attributes.m_Size.y ); }
void SetTextWidth( int aWidth ) { m_attributes.m_Size.x = aWidth; } void SetTextWidth( int aWidth );
int GetTextWidth() const { return m_attributes.m_Size.x; } int GetTextWidth() const { return m_attributes.m_Size.x; }
void SetTextHeight( int aHeight ) { m_attributes.m_Size.y = aHeight; } void SetTextHeight( int aHeight );
int GetTextHeight() const { return m_attributes.m_Size.y; } int GetTextHeight() const { return m_attributes.m_Size.y; }
void SetTextPos( const VECTOR2I& aPoint ) { m_pos = aPoint; } void SetTextPos( const VECTOR2I& aPoint );
const VECTOR2I& GetTextPos() const { return m_pos; } const VECTOR2I& GetTextPos() const { return m_pos; }
void SetTextX( int aX ) { m_pos.x = aX; } void SetTextX( int aX );
void SetTextY( int aY ) { m_pos.y = aY; } void SetTextY( int aY );
void Offset( const VECTOR2I& aOffset ) { m_pos += aOffset; } void Offset( const VECTOR2I& aOffset );
void Empty() { m_text.Empty(); } void Empty();
static GR_TEXT_H_ALIGN_T MapHorizJustify( int aHorizJustify ); static GR_TEXT_H_ALIGN_T MapHorizJustify( int aHorizJustify );
static GR_TEXT_V_ALIGN_T MapVertJustify( int aVertJustify ); static GR_TEXT_V_ALIGN_T MapVertJustify( int aVertJustify );
/** /**
@ -251,14 +256,6 @@ public:
void Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset, void Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset,
const COLOR4D& aColor, OUTLINE_MODE aDisplay_mode = FILLED ); const COLOR4D& aColor, OUTLINE_MODE aDisplay_mode = FILLED );
/**
* Convert the text shape to a list of segment.
*
* Each segment is stored as 2 VECTOR2Is: the starting point and the ending point
* there are therefore 2*n points.
*/
std::vector<VECTOR2I> TransformToSegmentList() const;
/** /**
* Convert the text bounding box to a rectangular polygon depending on the text * Convert the text bounding box to a rectangular polygon depending on the text
* orientation, the bounding box is not always horizontal or vertical * orientation, the bounding box is not always horizontal or vertical
@ -344,6 +341,15 @@ public:
virtual GR_TEXT_H_ALIGN_T GetDrawHorizJustify() const { return GetHorizJustify(); }; virtual GR_TEXT_H_ALIGN_T GetDrawHorizJustify() const { return GetHorizJustify(); };
virtual GR_TEXT_V_ALIGN_T GetDrawVertJustify() const { return GetVertJustify(); }; virtual GR_TEXT_V_ALIGN_T GetDrawVertJustify() const { return GetVertJustify(); };
void ClearRenderCache() { m_render_cache.clear(); }
std::vector<std::unique_ptr<KIFONT::GLYPH>>*
GetRenderCache( const wxString& forResolvedText ) const;
// Support for reading the cache from disk.
void SetupRenderCache( const wxString& aResolvedText, const EDA_ANGLE& aAngle );
void AddRenderCacheGlyph( const SHAPE_POLY_SET& aPoly );
int Compare( const EDA_TEXT* aOther ) const; int Compare( const EDA_TEXT* aOther ) const;
private: private:
@ -366,6 +372,10 @@ private:
wxString m_shown_text; // Cache of unescaped text for efficient access wxString m_shown_text; // Cache of unescaped text for efficient access
bool m_shown_text_has_text_var_refs; bool m_shown_text_has_text_var_refs;
mutable wxString m_render_cache_text;
mutable EDA_ANGLE m_render_cache_angle;
mutable std::vector<std::unique_ptr<KIFONT::GLYPH>> m_render_cache;
TEXT_ATTRIBUTES m_attributes; TEXT_ATTRIBUTES m_attributes;
VECTOR2I m_pos; VECTOR2I m_pos;
}; };

View File

@ -119,19 +119,21 @@ public:
* @param aGal is the graphics context. * @param aGal is the graphics context.
* @param aText is the text to be drawn. * @param aText is the text to be drawn.
* @param aPosition is the text position in world coordinates. * @param aPosition is the text position in world coordinates.
* @param aRotationAngle is the text rotation angle * @param aCursor is the current text position (for multiple text blocks within a single text
* object, such as a run of superscript characters)
* @param aAttrs are the styling attributes of the text, including its rotation
* @return bounding box * @return bounding box
*/ */
VECTOR2D Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, VECTOR2D Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
const VECTOR2D& aOrigin, const TEXT_ATTRIBUTES& aAttrs ) const; const VECTOR2I& aCursor, const TEXT_ATTRIBUTES& aAttrs ) const;
VECTOR2D Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, VECTOR2D Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
const TEXT_ATTRIBUTES& aAttributes ) const const TEXT_ATTRIBUTES& aAttributes ) const
{ {
return Draw( aGal, aText, aPosition, VECTOR2D( 0, 0 ), aAttributes ); return Draw( aGal, aText, aPosition, VECTOR2I( 0, 0 ), aAttributes );
} }
virtual void DrawText( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, virtual void DrawText( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
const TEXT_ATTRIBUTES& aAttributes ) const; const TEXT_ATTRIBUTES& aAttributes ) const;
/** /**
@ -169,19 +171,21 @@ public:
/** /**
* Convert text string to an array of GLYPHs. * Convert text string to an array of GLYPHs.
* *
* @param aBoundingBox pointer to a BOX2I that will set to the bounding box, or nullptr * @param aBBox pointer to a BOX2I that will set to the bounding box, or nullptr
* @param aGlyphs storage for the returned GLYPHs * @param aGlyphs storage for the returned GLYPHs
* @param aText text to convert to polygon/polyline * @param aText text to convert to polygon/polyline
* @param aGlyphSize glyph size * @param aSize is the cap-height and em-width of the text
* @param aPosition position of text (cursor position before this text) * @param aPosition position of text (cursor position before this text)
* @param aAngle text angle * @param aAngle text angle
* @param aMirror is true if text should be drawn mirrored, false otherwise.
* @param aOrigin is the point around which the text should be rotated, mirrored, etc.
* @param aTextStyle text style flags * @param aTextStyle text style flags
* @return text cursor position after this text * @return text cursor position after this text
*/ */
virtual VECTOR2I GetTextAsGlyphs( BOX2I* aBoundingBox, virtual VECTOR2I GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
std::vector<std::unique_ptr<GLYPH>>& aGlyphs, const UTF8& aText, const VECTOR2D& aSize,
const UTF8& aText, const VECTOR2D& aGlyphSize, const VECTOR2I& aPosition, const EDA_ANGLE& aAngle,
const wxPoint& aPosition, const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const = 0; TEXT_STYLE_FLAGS aTextStyle ) const = 0;
protected: protected:
@ -205,45 +209,42 @@ protected:
* function. * function.
* *
* @param aGal is a pointer to the graphics abstraction layer, or nullptr (nothing is drawn) * @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, * @param aBBox is an optional pointer to be filled with the bounding box.
* or nullptr
* @param aText is the text to be drawn. * @param aText is the text to be drawn.
* @param aPosition is text position. * @param aPosition is text position.
* @param aSize is the cap-height and em-width of the text
* @param aAngle is text angle. * @param aAngle is text angle.
* @param aIsMirrored is true if text should be drawn mirrored, false otherwise. * @param aMirror is true if text should be drawn mirrored, false otherwise.
* @return new cursor position * @param aOrigin is the point around which the text should be rotated, mirrored, etc.
* @return new cursor position in non-rotated, non-mirrored coordinates
*/ */
VECTOR2D drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const UTF8& aText, VECTOR2D drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const UTF8& aText,
const VECTOR2D& aPosition, const VECTOR2D& aGlyphSize, const VECTOR2I& aPosition, const VECTOR2D& aSize,
const EDA_ANGLE& aAngle, bool aIsItalic, bool aIsMirrored ) const; const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
bool aItalic ) const;
/** /**
* Computes the bounding box for a single line of text. * Computes the bounding box for a single line of text.
* Multiline texts should be split before using the function. * 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, * @param aBBox is an optional pointer to be filled with the bounding box.
* or nullptr
* @param aText is the text to be drawn. * @param aText is the text to be drawn.
* @param aPosition is text position. * @param aPosition is text position.
* @param aGlyphSize is glyph size. * @param aSize is the cap-height and em-width of the text.
* @param aAngle is text angle.
* @return new cursor position * @return new cursor position
*/ */
VECTOR2D boundingBoxSingleLine( BOX2I* aBoundingBox, const UTF8& aText, VECTOR2D boundingBoxSingleLine( BOX2I* aBBox, const UTF8& aText, const VECTOR2I& aPosition,
const VECTOR2D& aPosition, const VECTOR2D& aGlyphSize, const VECTOR2D& aSize, bool aItalic ) const;
const EDA_ANGLE& aAngle, bool aIsItalic ) const;
void getLinePositions( const UTF8& aText, const VECTOR2D& aPosition, wxArrayString& aStringList, void getLinePositions( const UTF8& aText, const VECTOR2I& aPosition,
std::vector<wxPoint>& aPositions, int& aLineCount, wxArrayString& aTextLines, std::vector<VECTOR2I>& aPositions,
std::vector<VECTOR2D>& aBoundingBoxes, std::vector<VECTOR2D>& aBoundingBoxes,
const TEXT_ATTRIBUTES& aAttributes ) const; 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, std::vector<std::unique_ptr<GLYPH>>& aGlyphs, VECTOR2D drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const UTF8& aText, const VECTOR2D& aPosition, const VECTOR2D& aGlyphSize, const UTF8& aText, const VECTOR2I& aPosition, const VECTOR2D& aSize,
const EDA_ANGLE& aAngle, TEXT_STYLE_FLAGS aTextStyle ) const; const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const;
///< Factor that determines the pitch between 2 lines. ///< Factor that determines the pitch between 2 lines.
static constexpr double INTERLINE_PITCH_RATIO = 1.62; // The golden mean static constexpr double INTERLINE_PITCH_RATIO = 1.62; // The golden mean
@ -251,10 +252,8 @@ protected:
private: private:
static FONT* getDefaultFont(); static FONT* getDefaultFont();
VECTOR2D doDrawString( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, VECTOR2D doDrawString( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
bool aParse, const TEXT_ATTRIBUTES& aAttrs ) const; bool aParse, const TEXT_ATTRIBUTES& aAttrs ) const;
VECTOR2D getBoundingBox( const UTF8& aText, TEXT_STYLE_FLAGS aTextStyle,
const TEXT_ATTRIBUTES& aAttrs ) const;
protected: protected:
wxString m_fontName; ///< Font name wxString m_fontName; ///< Font name

View File

@ -27,6 +27,7 @@
#include <memory> #include <memory>
#include <math/box2.h> #include <math/box2.h>
#include <eda_angle.h>
#include <geometry/shape_poly_set.h> #include <geometry/shape_poly_set.h>
#include <wx/debug.h> #include <wx/debug.h>
@ -42,8 +43,6 @@ public:
virtual bool IsStroke() const { return false; } virtual bool IsStroke() const { return false; }
virtual BOX2D BoundingBox() = 0; virtual BOX2D BoundingBox() = 0;
virtual void Mirror( const VECTOR2D& aMirrorOrigin = { 0, 0 } ) = 0;
}; };
@ -54,11 +53,17 @@ public:
SHAPE_POLY_SET() SHAPE_POLY_SET()
{} {}
OUTLINE_GLYPH( const OUTLINE_GLYPH& aGlyph ) :
SHAPE_POLY_SET( aGlyph )
{}
OUTLINE_GLYPH( const SHAPE_POLY_SET& aPoly ) :
SHAPE_POLY_SET( aPoly )
{}
bool IsOutline() const override { return true; } bool IsOutline() const override { return true; }
BOX2D BoundingBox() override; BOX2D BoundingBox() override;
void Mirror( const VECTOR2D& aMirrorOrigin = VECTOR2D( 0, 0 ) ) override;
}; };
@ -79,10 +84,9 @@ public:
BOX2D BoundingBox() override { return m_boundingBox; } BOX2D BoundingBox() override { return m_boundingBox; }
void SetBoundingBox( const BOX2D& bbox ) { m_boundingBox = bbox; } void SetBoundingBox( const BOX2D& bbox ) { m_boundingBox = bbox; }
void Mirror( const VECTOR2D& aMirrorOrigin = { 0, 0 } ) override; std::unique_ptr<GLYPH> Transform( const VECTOR2D& aGlyphSize, const VECTOR2I& aOffset,
double aTilt, const EDA_ANGLE& aAngle, bool aMirror,
std::unique_ptr<GLYPH> Transform( const VECTOR2D& aGlyphSize, const VECTOR2D& aOffset, const VECTOR2I& aOrigin );
double aTilt );
private: private:
bool m_penIsDown = false; bool m_penIsDown = false;

View File

@ -111,8 +111,8 @@ public:
VECTOR2I GetTextAsGlyphs( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs, VECTOR2I GetTextAsGlyphs( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const UTF8& aText, const VECTOR2D& aGlyphSize, const UTF8& aText, const VECTOR2D& aSize, const VECTOR2I& aPosition,
const wxPoint& aPosition, const EDA_ANGLE& aAngle, const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const override; TEXT_STYLE_FLAGS aTextStyle ) const override;
/** /**
@ -130,15 +130,11 @@ public:
#if 0 #if 0
void RenderToOpenGLCanvas( KIGFX::OPENGL_FREETYPE& aTarget, const UTF8& aString, void RenderToOpenGLCanvas( KIGFX::OPENGL_FREETYPE& aTarget, const UTF8& aString,
const VECTOR2D& aGlyphSize, const wxPoint& aPosition, const VECTOR2D& aSize, const wxPoint& aPosition,
double aOrientation, bool aIsMirrored ) const; const EDA_ANGLE& aAngle, bool aMirror ) const;
#endif #endif
protected: protected:
VECTOR2D getBoundingBox( const UTF8& aString, const VECTOR2D& aGlyphSize,
TEXT_STYLE_FLAGS aTextStyle ) const override;
FT_Error loadFace( const wxString& aFontFileName ); FT_Error loadFace( const wxString& aFontFileName );
bool loadFontSimple( const wxString& aFontFileName ); bool loadFontSimple( const wxString& aFontFileName );
@ -151,7 +147,6 @@ private:
FT_Face m_face; FT_Face m_face;
const int m_faceSize; const int m_faceSize;
FT_Face m_subscriptFace; FT_Face m_subscriptFace;
const int m_subscriptSize;
int m_faceScaler; int m_faceScaler;
int m_subscriptFaceScaler; int m_subscriptFaceScaler;

View File

@ -91,12 +91,8 @@ public:
VECTOR2D ComputeTextLineSize( const KIGFX::GAL* aGal, const UTF8& aText ) const override; VECTOR2D ComputeTextLineSize( const KIGFX::GAL* aGal, const UTF8& aText ) const override;
VECTOR2I GetTextAsGlyphs( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs, VECTOR2I GetTextAsGlyphs( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const UTF8& aText, const VECTOR2D& aGlyphSize, const UTF8& aText, const VECTOR2D& aSize, const VECTOR2I& aPosition,
const wxPoint& aPosition, const EDA_ANGLE& aAngle, const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const override;
protected:
VECTOR2D getBoundingBox( const UTF8& aString, const VECTOR2D& aGlyphSize,
TEXT_STYLE_FLAGS aTextStyle ) const override; TEXT_STYLE_FLAGS aTextStyle ) const override;
private: private:

View File

@ -220,6 +220,9 @@ public:
/// Compound assignment operator /// Compound assignment operator
VECTOR2<T>& operator+=( const VECTOR2<T>& aVector ); VECTOR2<T>& operator+=( const VECTOR2<T>& aVector );
/// Compound assignment operator
VECTOR2<T>& operator*=( const VECTOR2<T>& aVector );
/// Compound assignment operator /// Compound assignment operator
VECTOR2<T>& operator+=( const T& aScalar ); VECTOR2<T>& operator+=( const T& aScalar );
@ -346,6 +349,15 @@ VECTOR2<T>& VECTOR2<T>::operator+=( const VECTOR2<T>& aVector )
} }
template <class T>
VECTOR2<T>& VECTOR2<T>::operator*=( const VECTOR2<T>& aVector )
{
x *= aVector.x;
y *= aVector.y;
return *this;
}
template <class T> template <class T>
VECTOR2<T>& VECTOR2<T>::operator+=( const T& aScalar ) VECTOR2<T>& VECTOR2<T>::operator+=( const T& aScalar )
{ {

View File

@ -101,8 +101,18 @@ inline void RotatePoint( VECTOR2I& point, const VECTOR2I& centre, EDA_ANGLE angl
void RotatePoint( double* pX, double* pY, double angle ); void RotatePoint( double* pX, double* pY, double angle );
inline void RotatePoint( VECTOR2D& point, EDA_ANGLE angle )
{
RotatePoint( &point.x, &point.y, angle.AsTenthsOfADegree() );
}
void RotatePoint( double* pX, double* pY, double cx, double cy, double angle ); void RotatePoint( double* pX, double* pY, double cx, double cy, double angle );
inline void RotatePoint( VECTOR2D& point, const VECTOR2D& aCenter, EDA_ANGLE angle )
{
RotatePoint( &point.x, &point.y, aCenter.x, aCenter.y, angle.AsTenthsOfADegree() );
}
/** /**
* Determine the center of an arc or circle given three points on its circumference. * Determine the center of an arc or circle given three points on its circumference.
* *

View File

@ -1554,7 +1554,7 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
} }
void PCB_PAINTER::strokeText( const wxString& aText, const VECTOR2D& aPosition, void PCB_PAINTER::strokeText( const wxString& aText, const VECTOR2I& aPosition,
const TEXT_ATTRIBUTES& aAttrs ) const TEXT_ATTRIBUTES& aAttrs )
{ {
KIFONT::FONT* font = aAttrs.m_Font; KIFONT::FONT* font = aAttrs.m_Font;
@ -1571,9 +1571,9 @@ void PCB_PAINTER::strokeText( const wxString& aText, const VECTOR2D& aPosition,
void PCB_PAINTER::draw( const PCB_TEXT* aText, int aLayer ) void PCB_PAINTER::draw( const PCB_TEXT* aText, int aLayer )
{ {
wxString shownText( aText->GetShownText() ); wxString resolvedText( aText->GetShownText() );
if( shownText.Length() == 0 ) if( resolvedText.Length() == 0 )
return; return;
const COLOR4D& color = m_pcbSettings.GetColor( aText, aText->GetLayer() ); const COLOR4D& color = m_pcbSettings.GetColor( aText, aText->GetLayer() );
@ -1589,15 +1589,25 @@ void PCB_PAINTER::draw( const PCB_TEXT* aText, int aLayer )
else else
attrs.m_StrokeWidth = getLineThickness( aText->GetEffectiveTextPenWidth() ); attrs.m_StrokeWidth = getLineThickness( aText->GetEffectiveTextPenWidth() );
strokeText( shownText, aText->GetTextPos(), attrs ); std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = aText->GetRenderCache( resolvedText );
if( cache )
{
for( const std::unique_ptr<KIFONT::GLYPH>& glyph : *cache )
m_gal->DrawGlyph( *glyph.get() );
}
else
{
strokeText( resolvedText, aText->GetTextPos(), attrs );
}
} }
void PCB_PAINTER::draw( const FP_TEXT* aText, int aLayer ) void PCB_PAINTER::draw( const FP_TEXT* aText, int aLayer )
{ {
wxString shownText( aText->GetShownText() ); wxString resolvedText( aText->GetShownText() );
if( shownText.Length() == 0 ) if( resolvedText.Length() == 0 )
return; return;
const COLOR4D& color = m_pcbSettings.GetColor( aText, aLayer ); const COLOR4D& color = m_pcbSettings.GetColor( aText, aLayer );
@ -1615,7 +1625,17 @@ void PCB_PAINTER::draw( const FP_TEXT* aText, int aLayer )
else else
attrs.m_StrokeWidth = getLineThickness( aText->GetEffectiveTextPenWidth() ); attrs.m_StrokeWidth = getLineThickness( aText->GetEffectiveTextPenWidth() );
strokeText( shownText, aText->GetTextPos(), attrs ); std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = aText->GetRenderCache( resolvedText );
if( cache )
{
for( const std::unique_ptr<KIFONT::GLYPH>& glyph : *cache )
m_gal->DrawGlyph( *glyph.get() );
}
else
{
strokeText( resolvedText, aText->GetTextPos(), attrs );
}
// Draw the umbilical line // Draw the umbilical line
if( aText->IsSelected() ) if( aText->IsSelected() )
@ -1848,6 +1868,7 @@ void PCB_PAINTER::draw( const PCB_DIMENSION_BASE* aDimension, int aLayer )
// Draw text // Draw text
const PCB_TEXT& text = aDimension->Text(); const PCB_TEXT& text = aDimension->Text();
wxString resolvedText = text.GetShownText();
VECTOR2D position( text.GetTextPos().x, text.GetTextPos().y ); VECTOR2D position( text.GetTextPos().x, text.GetTextPos().y );
TEXT_ATTRIBUTES attrs = text.GetAttributes(); TEXT_ATTRIBUTES attrs = text.GetAttributes();
@ -1856,7 +1877,17 @@ void PCB_PAINTER::draw( const PCB_DIMENSION_BASE* aDimension, int aLayer )
else else
attrs.m_StrokeWidth = getLineThickness( text.GetEffectiveTextPenWidth() ); attrs.m_StrokeWidth = getLineThickness( text.GetEffectiveTextPenWidth() );
strokeText( text.GetShownText(), position, attrs ); std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = text.GetRenderCache( resolvedText );
if( cache )
{
for( const std::unique_ptr<KIFONT::GLYPH>& glyph : *cache )
m_gal->DrawGlyph( *glyph.get() );
}
else
{
strokeText( resolvedText, position, attrs );
}
} }

View File

@ -201,7 +201,7 @@ protected:
*/ */
virtual int getDrillSize( const PCB_VIA* aVia ) const; virtual int getDrillSize( const PCB_VIA* aVia ) const;
void strokeText( const wxString& aText, const VECTOR2D& aPosition, void strokeText( const wxString& aText, const VECTOR2I& aPosition,
const TEXT_ATTRIBUTES& aAttrs ); const TEXT_ATTRIBUTES& aAttrs );
protected: protected:

View File

@ -537,6 +537,57 @@ void PCB_PARSER::parseEDA_TEXT( EDA_TEXT* aText )
} }
void PCB_PARSER::parseRenderCache( EDA_TEXT* text )
{
T token;
NeedSYMBOLorNUMBER();
wxString cacheText = FROM_UTF8( CurText() );
double cacheAngle = parseAngle( "render cache angle" );
text->SetupRenderCache( cacheText,
EDA_ANGLE( cacheAngle, EDA_ANGLE::TENTHS_OF_A_DEGREE ) );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
if( token != T_polygon )
Expecting( T_polygon );
SHAPE_POLY_SET poly;
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
if( token != T_pts )
Expecting( T_pts );
SHAPE_LINE_CHAIN lineChain;
while( (token = NextTok() ) != T_RIGHT )
parseOutlinePoints( lineChain );
lineChain.SetClosed( true );
if( poly.OutlineCount() == 0 )
poly.AddOutline( lineChain );
else
poly.AddHole( lineChain );
}
text->AddRenderCacheGlyph( poly );
}
}
FP_3DMODEL* PCB_PARSER::parse3DModel() FP_3DMODEL* PCB_PARSER::parse3DModel()
{ {
wxCHECK_MSG( CurTok() == T_model, nullptr, wxCHECK_MSG( CurTok() == T_model, nullptr,
@ -2811,7 +2862,11 @@ PCB_TEXT* PCB_PARSER::parsePCB_TEXT()
break; break;
case T_effects: case T_effects:
parseEDA_TEXT( (EDA_TEXT*) text.get() ); parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ) );
break;
case T_render_cache:
parseRenderCache( static_cast<EDA_TEXT*>( text.get() ) );
break; break;
default: default:
@ -3714,7 +3769,11 @@ FP_TEXT* PCB_PARSER::parseFP_TEXT()
break; break;
case T_effects: case T_effects:
parseEDA_TEXT( (EDA_TEXT*) text.get() ); parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ) );
break;
case T_render_cache:
parseRenderCache( static_cast<EDA_TEXT*>( text.get() ) );
break; break;
case T_tstamp: case T_tstamp:

View File

@ -254,11 +254,19 @@ private:
/** /**
* Parse the common settings for any object derived from #EDA_TEXT. * Parse the common settings for any object derived from #EDA_TEXT.
* *
* @param aText A point to the #EDA_TEXT object to save the parsed settings into. * @param aText A pointer to the #EDA_TEXT object to save the parsed settings into.
* @throw PARSE_ERROR if the text syntax is not valid. * @throw PARSE_ERROR if the text syntax is not valid.
*/ */
void parseEDA_TEXT( EDA_TEXT* aText ); void parseEDA_TEXT( EDA_TEXT* aText );
/**
* Parse the render cache for any object derived from #EDA_TEXT.
*
* @param aText A pointer to the #EDA_TEXT object to save the parsed settings into.
* @throw PARSE_ERROR if the text syntax is not valid.
*/
void parseRenderCache( EDA_TEXT* text );
FP_3DMODEL* parse3DModel(); FP_3DMODEL* parse3DModel();
/** /**

View File

@ -470,6 +470,92 @@ void PCB_PLUGIN::formatLayer( const BOARD_ITEM* aItem ) const
} }
void PCB_PLUGIN::formatPolyPts( const SHAPE_LINE_CHAIN& outline, int aNestLevel,
bool aCompact ) const
{
m_out->Print( aNestLevel + 1, "(pts\n" );
bool needNewline = false;
int nestLevel = aNestLevel + 2;
int shapesAdded = 0;
for( int ii = 0; ii < outline.PointCount(); ++ii )
{
int ind = outline.ArcIndex( ii );
if( ind < 0 )
{
m_out->Print( nestLevel, "(xy %s)",
FormatInternalUnits( outline.CPoint( ii ) ).c_str() );
needNewline = true;
}
else
{
const SHAPE_ARC& arc = outline.Arc( ind );
m_out->Print( nestLevel, "(arc (start %s) (mid %s) (end %s))",
FormatInternalUnits( arc.GetP0() ).c_str(),
FormatInternalUnits( arc.GetArcMid() ).c_str(),
FormatInternalUnits( arc.GetP1() ).c_str() );
needNewline = true;
do
{
++ii;
} while( ii < outline.PointCount() && outline.ArcIndex( ii ) == ind );
--ii;
}
++shapesAdded;
if( !( shapesAdded % 4 ) || !aCompact )
{
// newline every 4 shapes if compact save
m_out->Print( 0, "\n" );
needNewline = false;
}
}
if( needNewline )
m_out->Print( 0, "\n" );
m_out->Print( aNestLevel + 1, ")\n" );
}
void PCB_PLUGIN::formatRenderCache( const EDA_TEXT* aText, int aNestLevel ) const
{
const wxString& shownText = aText->GetShownText();
std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = aText->GetRenderCache( shownText );
m_out->Print( aNestLevel, "(render_cache %s %s\n",
m_out->Quotew( shownText ).c_str(),
FormatAngle( aText->GetDrawRotation().AsTenthsOfADegree() ).c_str() );
for( const std::unique_ptr<KIFONT::GLYPH>& baseGlyph : *cache )
{
KIFONT::OUTLINE_GLYPH* glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( baseGlyph.get() );
if( glyph->OutlineCount() > 0 )
{
for( int ii = 0; ii < glyph->OutlineCount(); ++ii )
{
m_out->Print( aNestLevel + 1, "(polygon\n" );
formatPolyPts( glyph->Outline( ii ), aNestLevel + 1, true );
for( int jj = 0; jj < glyph->HoleCount( ii ); ++jj )
formatPolyPts( glyph->Hole( ii, jj ), aNestLevel + 2, true );
m_out->Print( aNestLevel + 1, ")\n" );
}
}
}
m_out->Print( aNestLevel, ")\n" );
}
void PCB_PLUGIN::formatSetup( const BOARD* aBoard, int aNestLevel ) const void PCB_PLUGIN::formatSetup( const BOARD* aBoard, int aNestLevel ) const
{ {
// Setup // Setup
@ -854,53 +940,8 @@ void PCB_PLUGIN::format( const PCB_SHAPE* aShape, int aNestLevel ) const
const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 ); const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
m_out->Print( aNestLevel, "(gr_poly%s\n", locked.c_str() ); m_out->Print( aNestLevel, "(gr_poly%s\n", locked.c_str() );
m_out->Print( aNestLevel + 1, "(pts\n" ); formatPolyPts( outline, aNestLevel, ADVANCED_CFG::GetCfg().m_CompactSave );
m_out->Print( aNestLevel + 1, ")\n" );
bool needNewline = false;
int nestLevel = aNestLevel + 2;
int shapesAdded = 0;
for( int ii = 0; ii < outline.PointCount(); ++ii )
{
int ind = outline.ArcIndex( ii );
if( ind < 0 )
{
m_out->Print( nestLevel, "(xy %s)",
FormatInternalUnits( outline.CPoint( ii ) ).c_str() );
needNewline = true;
}
else
{
const SHAPE_ARC& arc = outline.Arc( ind );
m_out->Print( nestLevel, "(arc (start %s) (mid %s) (end %s))",
FormatInternalUnits( arc.GetP0() ).c_str(),
FormatInternalUnits( arc.GetArcMid() ).c_str(),
FormatInternalUnits( arc.GetP1() ).c_str() );
needNewline = true;
do
{
++ii;
} while( ii < outline.PointCount() && outline.ArcIndex( ii ) == ind );
--ii;
}
++shapesAdded;
if( !( shapesAdded % 4 ) || !ADVANCED_CFG::GetCfg().m_CompactSave )
{
// newline every 4 shapes if compact save
m_out->Print( 0, "\n" );
needNewline = false;
}
}
if( needNewline )
m_out->Print( 0, "\n" );
m_out->Print( aNestLevel + 1, ")" );
} }
else else
{ {
@ -988,55 +1029,9 @@ void PCB_PLUGIN::format( const FP_SHAPE* aFPShape, int aNestLevel ) const
const SHAPE_POLY_SET& poly = aFPShape->GetPolyShape(); const SHAPE_POLY_SET& poly = aFPShape->GetPolyShape();
const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 ); const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
m_out->Print( aNestLevel, "(fp_poly%s (pts", m_out->Print( aNestLevel, "(fp_poly%s\n", locked.c_str() );
locked.c_str() ); formatPolyPts( outline, aNestLevel, ADVANCED_CFG::GetCfg().m_CompactSave );
m_out->Print( aNestLevel + 1, ")\n" );
bool need_newline = false;
for( int ii = 0; ii < outline.PointCount(); ++ii )
{
int nestLevel = 0;
if( !( ii % 4 ) || !ADVANCED_CFG::GetCfg().m_CompactSave )
{
// newline every 4 pts.
m_out->Print( 0, "\n" );
need_newline = false;
nestLevel = aNestLevel + 2;
}
int ind = outline.ArcIndex( ii );
if( ind < 0 )
{
m_out->Print( nestLevel, "%s(xy %s)",
nestLevel ? "" : " ",
FormatInternalUnits( outline.CPoint( ii ) ).c_str() );
need_newline = true;
}
else
{
auto& arc = outline.Arc( ind );
m_out->Print( nestLevel, "%s(arc (start %s) (mid %s) (end %s))",
nestLevel ? "" : " ",
FormatInternalUnits( arc.GetP0() ).c_str(),
FormatInternalUnits( arc.GetArcMid() ).c_str(),
FormatInternalUnits( arc.GetP1() ).c_str() );
need_newline = true;
do
{
++ii;
} while( ii < outline.PointCount() && outline.ArcIndex( ii ) == ind );
--ii;
}
}
if( need_newline )
m_out->Print( 0, "\n" );
m_out->Print( aNestLevel + 1, ")" );
} }
else else
{ {
@ -1691,54 +1686,9 @@ void PCB_PLUGIN::format( const PAD* aPad, int aNestLevel ) const
const SHAPE_POLY_SET& poly = primitive->GetPolyShape(); const SHAPE_POLY_SET& poly = primitive->GetPolyShape();
const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 ); const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
m_out->Print( nested_level, "(gr_poly (pts" ); m_out->Print( nested_level, "(gr_poly\n" );
formatPolyPts( outline, nested_level, ADVANCED_CFG::GetCfg().m_CompactSave );
bool need_newline = false; m_out->Print( aNestLevel + 1, ")\n" );
for( int ii = 0; ii < outline.PointCount(); ++ii )
{
nested_level = 0;
if( !( ii % 4 ) || !ADVANCED_CFG::GetCfg().m_CompactSave )
{
// newline every 4 pts.
m_out->Print( 0, "\n" );
need_newline = false;
nested_level = aNestLevel + 4;
}
int ind = outline.ArcIndex( ii );
if( ind < 0 )
{
m_out->Print( nested_level, "%s(xy %s)",
nested_level ? "" : " ",
FormatInternalUnits( outline.CPoint( ii ) ).c_str() );
need_newline = true;
}
else
{
const SHAPE_ARC& arc = outline.Arc( ind );
m_out->Print( nested_level, "%s(arc (start %s) (mid %s) (end %s))",
nested_level ? "" : " ",
FormatInternalUnits( arc.GetP0() ).c_str(),
FormatInternalUnits( arc.GetArcMid() ).c_str(),
FormatInternalUnits( arc.GetP1() ).c_str() );
need_newline = true;
do
{
++ii;
} while( ii < outline.PointCount() && outline.ArcIndex( ii ) == ind );
--ii;
}
}
if( need_newline )
m_out->Print( 0, "\n" );
m_out->Print( aNestLevel + 3, ")" );
} }
break; break;
@ -1785,6 +1735,9 @@ void PCB_PLUGIN::format( const PCB_TEXT* aText, int aNestLevel ) const
// PCB_TEXTS are never hidden, so always omit "hide" attribute // PCB_TEXTS are never hidden, so always omit "hide" attribute
aText->EDA_TEXT::Format( m_out, aNestLevel, m_ctl | CTL_OMIT_HIDE ); aText->EDA_TEXT::Format( m_out, aNestLevel, m_ctl | CTL_OMIT_HIDE );
if( aText->GetFont() && aText->GetFont()->IsOutline() )
formatRenderCache( aText, aNestLevel + 1 );
m_out->Print( aNestLevel, ")\n" ); m_out->Print( aNestLevel, ")\n" );
} }
@ -1878,6 +1831,9 @@ void PCB_PLUGIN::format( const FP_TEXT* aText, int aNestLevel ) const
m_out->Print( aNestLevel + 1, "(tstamp %s)\n", TO_UTF8( aText->m_Uuid.AsString() ) ); m_out->Print( aNestLevel + 1, "(tstamp %s)\n", TO_UTF8( aText->m_Uuid.AsString() ) );
if( aText->GetFont() && aText->GetFont()->IsOutline() )
formatRenderCache( aText, aNestLevel + 1 );
m_out->Print( aNestLevel, ")\n" ); m_out->Print( aNestLevel, ")\n" );
} }
@ -2149,55 +2105,8 @@ void PCB_PLUGIN::format( const ZONE* aZone, int aNestLevel ) const
for( auto& chain : poly ) for( auto& chain : poly )
{ {
m_out->Print( aNestLevel + 1, "(polygon\n" ); m_out->Print( aNestLevel + 1, "(polygon\n" );
m_out->Print( aNestLevel + 2, "(pts" ); formatPolyPts( chain, aNestLevel + 1, ADVANCED_CFG::GetCfg().m_CompactSave );
bool need_newline = true;
for( int ii = 0; ii < chain.PointCount(); ++ii )
{
int nestLevel = 0;
if( !( ii % 4 ) || !ADVANCED_CFG::GetCfg().m_CompactSave ) // newline every 4 pts
{
m_out->Print( 0, "\n" );
need_newline = false;
nestLevel = aNestLevel + 3;
}
int ind = chain.ArcIndex( ii );
if( ind < 0 )
{
m_out->Print( nestLevel, "%s(xy %s)",
nestLevel ? "" : " ",
FormatInternalUnits( chain.CPoint( ii ) ).c_str() );
need_newline = true;
}
else
{
auto& arc = chain.Arc( ind );
m_out->Print( nestLevel, "%s(arc (start %s) (mid %s) (end %s))",
nestLevel ? "" : " ",
FormatInternalUnits( arc.GetP0() ).c_str(),
FormatInternalUnits( arc.GetArcMid() ).c_str(),
FormatInternalUnits( arc.GetP1() ).c_str() );
need_newline = true;
do
{
++ii;
} while( ii < chain.PointCount() && chain.ArcIndex( ii ) == ind );
--ii;
}
}
if( need_newline )
m_out->Print( 0, "\n" );
m_out->Print( aNestLevel + 2, ")\n" );
m_out->Print( aNestLevel + 1, ")\n" ); m_out->Print( aNestLevel + 1, ")\n" );
} }
} }
@ -2215,55 +2124,9 @@ void PCB_PLUGIN::format( const ZONE* aZone, int aNestLevel ) const
if( aZone->IsIsland( layer, ii ) ) if( aZone->IsIsland( layer, ii ) )
m_out->Print( aNestLevel + 2, "(island)\n" ); m_out->Print( aNestLevel + 2, "(island)\n" );
m_out->Print( aNestLevel + 2, "(pts" );
const SHAPE_LINE_CHAIN& chain = fv.COutline( ii ); const SHAPE_LINE_CHAIN& chain = fv.COutline( ii );
bool need_newline = true; formatPolyPts( chain, aNestLevel + 1, ADVANCED_CFG::GetCfg().m_CompactSave );
for( int jj = 0; jj < chain.PointCount(); ++jj )
{
int nestLevel = 0;
if( !( jj%4 ) || !ADVANCED_CFG::GetCfg().m_CompactSave ) // newline every 4 pts
{
m_out->Print( 0, "\n" );
need_newline = false;
nestLevel = aNestLevel + 3;
}
int ind = chain.ArcIndex( jj );
if( ind < 0 )
{
m_out->Print( nestLevel, "%s(xy %s)",
nestLevel ? "" : " ",
FormatInternalUnits( chain.CPoint( jj ) ).c_str() );
need_newline = true;
}
else
{
auto& arc = chain.Arc( ind );
m_out->Print( nestLevel, "%s(arc (start %s) (mid %s) (end %s))",
nestLevel ? "" : " ",
FormatInternalUnits( arc.GetP0() ).c_str(),
FormatInternalUnits( arc.GetArcMid() ).c_str(),
FormatInternalUnits( arc.GetP1() ).c_str() );
need_newline = true;
do
{
++jj;
} while( jj < chain.PointCount() && chain.ArcIndex( jj ) == ind );
--jj;
}
}
if( need_newline )
m_out->Print( 0, "\n" );
m_out->Print( aNestLevel+2, ")\n" );
m_out->Print( aNestLevel + 1, ")\n" ); m_out->Print( aNestLevel + 1, ")\n" );
} }

View File

@ -45,6 +45,8 @@ class PCB_GROUP;
class PCB_TRACK; class PCB_TRACK;
class ZONE; class ZONE;
class PCB_TEXT; class PCB_TEXT;
class EDA_TEXT;
class SHAPE_LINE_CHAIN;
/// Current s-expression file format version. 2 was the last legacy format version. /// Current s-expression file format version. 2 was the last legacy format version.
@ -280,6 +282,10 @@ private:
void format( const ZONE* aZone, int aNestLevel = 0 ) const; void format( const ZONE* aZone, int aNestLevel = 0 ) const;
void formatPolyPts( const SHAPE_LINE_CHAIN& outline, int aNestLevel, bool aCompact ) const;
void formatRenderCache( const EDA_TEXT* aText, int aNestLevel ) const;
void formatLayer( const BOARD_ITEM* aItem ) const; void formatLayer( const BOARD_ITEM* aItem ) const;
void formatLayers( LSET aLayerMask, int aNestLevel = 0 ) const; void formatLayers( LSET aLayerMask, int aNestLevel = 0 ) const;

View File

@ -1110,46 +1110,15 @@ bool PNS_KICAD_IFACE_BASE::syncTextItem( PNS::NODE* aWorld, EDA_TEXT* aText, PCB
if( !IsCopperLayer( aLayer ) ) if( !IsCopperLayer( aLayer ) )
return false; return false;
if( aText->GetFont() && aText->GetFont()->IsOutline() )
{
for( SHAPE* shape : aText->GetEffectiveTextShape()->Shapes() )
{
std::unique_ptr<PNS::SOLID> solid = std::make_unique<PNS::SOLID>(); std::unique_ptr<PNS::SOLID> solid = std::make_unique<PNS::SOLID>();
solid->SetLayer( aLayer ); solid->SetLayer( aLayer );
solid->SetNet( -1 ); solid->SetNet( -1 );
solid->SetParent( dynamic_cast<BOARD_ITEM*>( aText ) ); solid->SetParent( dynamic_cast<BOARD_ITEM*>( aText ) );
solid->SetShape( shape ); solid->SetShape( aText->GetEffectiveTextShape()->Clone() );
solid->SetIsCompoundShapePrimitive();
solid->SetRoutable( false ); solid->SetRoutable( false );
aWorld->Add( std::move( solid ) ); aWorld->Add( std::move( solid ) );
}
}
else
{
int textWidth = aText->GetEffectiveTextPenWidth();
std::vector<VECTOR2I> textShape = aText->TransformToSegmentList();
if( textShape.size() < 2 )
return false;
for( size_t jj = 0; jj < textShape.size(); jj += 2 )
{
VECTOR2I start( textShape[jj] );
VECTOR2I end( textShape[jj+1] );
std::unique_ptr<PNS::SOLID> solid = std::make_unique<PNS::SOLID>();
solid->SetLayer( aLayer );
solid->SetNet( -1 );
solid->SetParent( dynamic_cast<BOARD_ITEM*>( aText ) );
solid->SetShape( new SHAPE_SEGMENT( start, end, textWidth ) );
solid->SetIsCompoundShapePrimitive();
solid->SetRoutable( false );
aWorld->Add( std::move( solid ) );
}
}
return true; return true;