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 )
{
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 ) :
m_text( aText.m_text ),
m_attributes( aText.m_attributes ),
m_pos( aText.m_pos )
EDA_TEXT::EDA_TEXT( const EDA_TEXT& aText )
{
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 )
{
m_text = aText;
@ -133,6 +178,78 @@ void EDA_TEXT::CopyText( const EDA_TEXT& aSrc )
m_text = aSrc.m_text;
m_shown_text = aSrc.m_shown_text;
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_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_shown_text, aTradingPartner.m_shown_text );
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_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()
{
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_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
}
// 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
{
@ -626,20 +811,13 @@ std::shared_ptr<SHAPE_COMPOUND> EDA_TEXT::GetEffectiveTextShape( ) const
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() );
std::vector<std::unique_ptr<KIFONT::GLYPH>> glyphs;
font->GetLinesAsGlyphs( glyphs, this );
for( std::unique_ptr<KIFONT::GLYPH>& baseGlyph : glyphs )
for( std::unique_ptr<KIFONT::GLYPH>& baseGlyph : m_render_cache )
{
KIFONT::OUTLINE_GLYPH* glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( baseGlyph.get() );
if( IsMirrored() )
glyph->Mirror( GetTextPos() );
glyph->CacheTriangulation();
for( unsigned int ii = 0; ii < glyph->TriangulatedPolyCount(); ++ii )
@ -663,11 +841,38 @@ std::shared_ptr<SHAPE_COMPOUND> EDA_TEXT::GetEffectiveTextShape( ) const
}
else
{
int penWidth = GetEffectiveTextPenWidth();
std::vector<VECTOR2I> pts = TransformToSegmentList();
wxSize size = GetTextSize();
int penWidth = GetEffectiveTextPenWidth();
bool forceBold = true;
for( unsigned jj = 0; jj < pts.size(); jj += 2 )
shape->AddShape( new SHAPE_SEGMENT( pts[jj], pts[jj+1], penWidth ) );
TSEGM_2_SHAPE_PRMS prms;
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;

View File

@ -112,28 +112,28 @@ bool FONT::IsStroke( const wxString& aFontName )
* @param aPosition is the text position
* @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
{
if( aText.empty() )
return VECTOR2D( 0.0, 0.0 );
wxArrayString strings;
std::vector<wxPoint> positions;
int n_lines;
std::vector<VECTOR2I> positions;
VECTOR2D boundingBox;
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;
if( aParse )
{
MARKUP::MARKUP_PARSER markupParser( std::string( strings.Item( i ) ) );
//auto parse_result = markupParser.Parse();
VECTOR2D cursor = positions[i];
VECTOR2I cursor = positions[i];
std::function<void( const std::unique_ptr<MARKUP::NODE>& )> nodeHandler =
[&]( 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.y = ( n_lines + 1 ) * GetInterline( aAttrs.m_Size.y );
boundingBox.y = ( strings.GetCount() + 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,
void FONT::getLinePositions( const UTF8& aText, const VECTOR2I& aPosition,
wxArrayString& aTextLines, std::vector<VECTOR2I>& aPositions,
std::vector<VECTOR2D>& aBoundingBoxes,
const TEXT_ATTRIBUTES& aAttrs ) const
{
wxStringSplit( aText, aStringList, '\n' );
aLineCount = aStringList.Count();
aPositions.reserve( aLineCount );
wxStringSplit( aText, aTextLines, '\n' );
int lineCount = aTextLines.Count();
aPositions.reserve( lineCount );
wxPoint origin( aPosition.x, aPosition.y );
int interline = GetInterline( aAttrs.m_Size.y, aAttrs.m_LineSpacing );
int height = 0;
int interline = GetInterline( aAttrs.m_Size.y, aAttrs.m_LineSpacing );
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 end = boundingBoxSingleLine( nullptr, aStringList[i], pos, aAttrs.m_Size,
aAttrs.m_Angle, aAttrs.m_Italic );
VECTOR2D pos( aPosition.x, aPosition.y + i * interline );
VECTOR2D end = boundingBoxSingleLine( nullptr, aTextLines[i], pos, aAttrs.m_Size,
aAttrs.m_Italic );
VECTOR2D bBox( end - pos );
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;
}
int mirrorX = aAttrs.m_Mirrored ? -1 : 1;
for( int i = 0; i < aLineCount; i++ )
for( int i = 0; i < lineCount; i++ )
{
VECTOR2D lineSize = aBoundingBoxes.at( i );
VECTOR2I 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;
case GR_TEXT_H_ALIGN_LEFT: break;
case GR_TEXT_H_ALIGN_CENTER: lineOffset.x = -lineSize.x / 2; break;
case GR_TEXT_H_ALIGN_RIGHT: lineOffset.x = -lineSize.x; break;
}
VECTOR2I pos( aPosition.x + lineOffset.x, aPosition.y + lineOffset.y );
RotatePoint( pos, origin, aAttrs.m_Angle );
aPositions.push_back( (wxPoint) pos );
aPositions.push_back( aPosition + lineOffset );
}
}
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,
void FONT::DrawText( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
const TEXT_ATTRIBUTES& aAttributes ) const
{
// 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 aText is the text to be drawn.
* @param aPosition is the text position in world coordinates.
* @param aAngle is the text rotation angle
* @param aPosition is the text object position in world coordinates.
* @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,
const VECTOR2D& aOrigin, const TEXT_ATTRIBUTES& aAttrs ) const
VECTOR2D FONT::Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
const VECTOR2I& aCursor, 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();
VECTOR2D position( aPosition - aCursor );
// Split multiline strings into separate ones and draw them line by line
wxArrayString strings_list;
std::vector<wxPoint> positions;
std::vector<VECTOR2I> positions;
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 );
BOX2I lineBoundingBox;
for( int i = 0; i < n; i++ )
aGal->SetLineWidth( aAttrs.m_StrokeWidth );
for( size_t i = 0; i < strings_list.GetCount(); 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();
(void) drawSingleLineText( aGal, &lineBoundingBox, strings_list[i], positions[i],
aAttrs.m_Size, aAttrs.m_Angle, aAttrs.m_Mirrored, aPosition,
aAttrs.m_Italic );
// expand bounding box of whole text
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;
}
// undo rotation
//aGal->Restore();
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
*/
VECTOR2D drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const std::unique_ptr<MARKUP::NODE>& aNode, const VECTOR2D& aPosition,
const KIFONT::FONT* aFont, const VECTOR2D& aGlyphSize, const EDA_ANGLE& aAngle,
TEXT_STYLE_FLAGS aTextStyle )
const std::unique_ptr<MARKUP::NODE>& aNode, const VECTOR2I& aPosition,
const KIFONT::FONT* aFont, const VECTOR2D& aSize, const EDA_ANGLE& aAngle,
bool aMirror, const VECTOR2I& aOrigin, TEXT_STYLE_FLAGS aTextStyle )
{
VECTOR2D nextPosition = aPosition;
VECTOR2I nextPosition = aPosition;
TEXT_STYLE_FLAGS textStyle = aTextStyle;
@ -361,27 +308,20 @@ VECTOR2D drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& a
if( aNode->has_content() )
{
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 )
{
BOX2I boundingBox;
boundingBox = aBoundingBox->Merge( bbox );
aBoundingBox->SetOrigin( boundingBox.GetOrigin() );
aBoundingBox->SetSize( boundingBox.GetSize() );
}
aBoundingBox->Merge( bbox );
}
}
for( const auto& child : aNode->children )
for( const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
{
nextPosition = drawMarkup( aBoundingBox, aGlyphs, child, nextPosition, aFont, aGlyphSize,
aAngle, textStyle );
nextPosition = drawMarkup( aBoundingBox, aGlyphs, child, nextPosition, aFont, aSize,
aAngle, aMirror, aOrigin, textStyle );
}
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,
const UTF8& aText, const VECTOR2D& aPosition, const VECTOR2D& aGlyphSize,
const EDA_ANGLE& aAngle, TEXT_STYLE_FLAGS aTextStyle ) const
const UTF8& aText, const VECTOR2I& aPosition, const VECTOR2D& aSize,
const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const
{
MARKUP::MARKUP_PARSER markupParser( aText );
std::unique_ptr<MARKUP::NODE> root = markupParser.Parse();
return ::drawMarkup( aBoundingBox, aGlyphs, root, aPosition, this, aGlyphSize, aAngle,
aTextStyle );
return ::drawMarkup( aBoundingBox, aGlyphs, root, aPosition, this, aSize, aAngle, aMirror,
aOrigin, aTextStyle );
}
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
const VECTOR2I& aPosition, const VECTOR2D& aSize,
const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
bool aItalic ) const
{
if( !aGal )
{
@ -412,37 +354,32 @@ VECTOR2D FONT::drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const
TEXT_STYLE_FLAGS textStyle = 0;
if( aIsItalic )
if( aItalic )
textStyle |= TEXT_STYLE::ITALIC;
std::vector<std::unique_ptr<GLYPH>> glyphs;
VECTOR2D nextPosition = drawMarkup( aBoundingBox, glyphs, aText, aPosition, aGlyphSize,
aAngle, textStyle );
VECTOR2D nextPosition = drawMarkup( aBoundingBox, glyphs, aText, aPosition, aSize, aAngle,
aMirror, aOrigin, textStyle );
for( const std::unique_ptr<GLYPH>& glyph : glyphs )
{
if( aIsMirrored )
glyph->Mirror( aPosition );
aGal->DrawGlyph( *glyph.get() );
}
return nextPosition;
}
VECTOR2D FONT::boundingBoxSingleLine( BOX2I* aBoundingBox, const UTF8& aText,
const VECTOR2D& aPosition, const VECTOR2D& aGlyphSize,
const EDA_ANGLE& aAngle, bool aIsItalic ) const
const VECTOR2I& aPosition, const VECTOR2D& aSize,
bool aItalic ) const
{
TEXT_STYLE_FLAGS textStyle = 0;
if( aIsItalic )
if( aItalic )
textStyle |= TEXT_STYLE::ITALIC;
std::vector<std::unique_ptr<GLYPH>> glyphs; // ignored
VECTOR2D nextPosition = drawMarkup( aBoundingBox, glyphs, aText, aPosition, aGlyphSize,
aAngle, textStyle );
VECTOR2D nextPosition = drawMarkup( aBoundingBox, glyphs, aText, aPosition, aSize,
EDA_ANGLE::ANGLE_0, false, VECTOR2I(), textStyle );
return nextPosition;
}

View File

@ -20,6 +20,7 @@
#include <vector>
#include <font/glyph.h>
#include <trigo.h>
using namespace KIFONT;
@ -62,8 +63,9 @@ void STROKE_GLYPH::Finalize()
}
std::unique_ptr<GLYPH> STROKE_GLYPH::Transform( const VECTOR2D& aGlyphSize, const VECTOR2D& aOffset,
double aTilt )
std::unique_ptr<GLYPH> STROKE_GLYPH::Transform( const VECTOR2D& aGlyphSize, const VECTOR2I& aOffset,
double aTilt, const EDA_ANGLE& aAngle, bool aMirror,
const VECTOR2I& aOrigin )
{
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 )
{
point.x *= aGlyphSize.x;
point.y *= aGlyphSize.y;
point *= aGlyphSize;
if( aTilt )
point.x -= point.y * aTilt;
point.x += aOffset.x;
point.y += aOffset.y;
point += aOffset;
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()
{
BOX2I bbox = BBox();
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 <trigo.h>
#include <font/fontconfig.h>
#include <convert_basic_shapes_to_polygon.h>
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;
OUTLINE_FONT::OUTLINE_FONT() :
m_faceSize( 16 ),
m_subscriptSize( 13 )
m_faceSize( 16 )
{
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)
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 )
{
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 )
{
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
// 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 pitch = INTERLINE_PITCH_RATIO;
if( GetFace()->units_per_EM )
return ( aLineSpacing * aGlyphHeight * ( GetFace()->height / GetFace()->units_per_EM ) );
else
return ( aLineSpacing * aGlyphHeight * INTERLINE_PITCH_RATIO );
pitch = GetFace()->height / GetFace()->units_per_EM;
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
{
wxArrayString strings;
std::vector<wxPoint> positions;
int n;
std::vector<VECTOR2I> positions;
VECTOR2I ret;
std::vector<VECTOR2D> boundingBoxes;
TEXT_ATTRIBUTES attrs = aText->GetAttributes();
TEXT_STYLE_FLAGS textStyle = 0;
attrs.m_Angle = aText->GetDrawRotation();
if( aText->IsItalic() )
textStyle |= TEXT_STYLE::ITALIC;
getLinePositions( aText->GetShownText(), aText->GetTextPos(), strings, positions, n,
boundingBoxes, aText->GetAttributes() );
getLinePositions( aText->GetShownText(), aText->GetTextPos(), strings, positions, boundingBoxes,
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],
aText->GetTextSize(), aText->GetTextAngle(), textStyle );
ret = drawMarkup( nullptr, aGlyphs, UTF8( strings.Item( i ) ), positions[i], attrs.m_Size,
attrs.m_Angle, attrs.m_Mirrored, aText->GetTextPos(), textStyle );
}
return ret;
}
VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox,
std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const UTF8& aText, const VECTOR2D& aGlyphSize,
const wxPoint& aPosition, const EDA_ANGLE& aOrientation,
VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const UTF8& aText, const VECTOR2D& aSize,
const VECTOR2I& aPosition, const EDA_ANGLE& aAngle,
bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const
{
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_font_t* referencedFont;
//const double subscriptAndSuperscriptScaler = 0.5;
VECTOR2D glyphSize = aGlyphSize;
VECTOR2D glyphSize = aSize;
FT_Face face = m_face;
int scaler = m_faceScaler;
double scaler = m_faceScaler / OUTLINE_FONT_SIZE_COMPENSATION;
if( IsSubscript( aTextStyle ) || IsSuperscript( aTextStyle ) )
{
face = m_subscriptFace;
//scaler = m_subscriptFaceScaler;
}
referencedFont = hb_ft_font_create_referenced( face );
hb_ft_font_set_funcs( referencedFont );
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 extentBottomLeft( INT_MAX, INT_MAX );
VECTOR2I extentTopRight( INT_MIN, INT_MIN );
VECTOR2I vBottomLeft( INT_MAX, INT_MAX );
VECTOR2I vTopRight( INT_MIN, INT_MIN );
VECTOR2D topLeft( INT_MAX * 1.0, -INT_MAX * 1.0 );
VECTOR2D topRight( -INT_MAX * 1.0, -INT_MAX * 1.0 );
for( unsigned int i = 0; i < glyphCount; i++ )
{
@ -380,46 +373,34 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox,
GLYPH_POINTS points = c.points;
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 )
{
// Save text extents
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 + cursor );
VECTOR2D pt( v.x, v.y );
VECTOR2D ptC( pt.x + cursor.x, pt.y + cursor.y );
wxPoint scaledPtOrig( -ptC.x * scaleFactor.x, -ptC.y * scaleFactor.y );
wxPoint scaledPt( scaledPtOrig );
RotatePoint( &scaledPt.x, &scaledPt.y, aOrientation.AsTenthsOfADegree() );
scaledPt.x += offset.x;
scaledPt.y += offset.y;
topLeft.x = std::min( topLeft.x, pt.x );
topLeft.y = std::max( topLeft.y, pt.y );
topRight.x = std::max( topRight.x, pt.x );
topRight.y = std::max( topRight.y, pt.y );
if( extentBottomLeft.x > scaledPt.x )
extentBottomLeft.x = scaledPt.x;
if( extentBottomLeft.y > scaledPt.y )
extentBottomLeft.y = scaledPt.y;
if( extentTopRight.x < scaledPt.x )
extentTopRight.x = scaledPt.x;
if( extentTopRight.y < scaledPt.y )
extentTopRight.y = scaledPt.y;
if( IsSubscript( aTextStyle ) )
pt.y -= 0.25 * scaler;
else if( IsSuperscript( aTextStyle ) )
pt.y += 0.45 * scaler;
shape.Append( scaledPt.x, scaledPt.y );
//ptListScaled.push_back( scaledPt );
pt *= scaleFactor;
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 ) )
holes.push_back( std::move( shape ) );
else
@ -429,10 +410,7 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox,
for( SHAPE_LINE_CHAIN& outline : outlines )
{
if( outline.PointCount() )
{
outline.SetClosed( true );
glyph->AddOutline( outline );
}
}
int nthHole = 0;
@ -441,17 +419,14 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox,
{
if( hole.PointCount() )
{
hole.SetClosed( true );
VECTOR2I firstPoint = hole.GetPoint( 0 );
//SHAPE_SIMPLE *outlineForHole = nullptr;
int nthOutline = -1;
int n = 0;
int nthOutline = -1;
int n = 0;
for( SHAPE_LINE_CHAIN& outline : outlines )
{
if( outline.PointInside( firstPoint ) )
{
//outlineForHole = outline;
nthOutline = n;
break;
}
@ -476,106 +451,52 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox,
if( IsOverbar( aTextStyle ) )
{
std::unique_ptr<OUTLINE_GLYPH> overbarGlyph = std::make_unique<OUTLINE_GLYPH>();
SHAPE_LINE_CHAIN overbar;
topLeft *= scaleFactor;
topRight *= scaleFactor;
int left = extentBottomLeft.x;
int right = extentTopRight.x;
int top = extentBottomLeft.y - 800;
int barHeight = -3200;
topLeft.y -= aSize.y * 0.16;
topRight.y -= aSize.y * 0.16;
overbar.Append( VECTOR2D( left, top ) );
overbar.Append( VECTOR2D( right, top ) );
overbar.Append( VECTOR2D( right, top + barHeight ) );
overbar.Append( VECTOR2D( left, top + barHeight ) );
overbar.SetClosed( true );
topLeft += aPosition;
topRight += aPosition;
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 ) );
}
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 );
aBoundingBox->SetEnd( cursorDisplacement );
aBBox->SetOrigin( aPosition.x, aPosition.y );
aBBox->SetEnd( cursorDisplacement );
}
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
#ifdef OUTLINEFONT_RENDER_AS_PIXELS
/*
* WIP: eeschema (and PDF output?) should use pixel rendering instead of linear segmentation
*/
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
{
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;
(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();
}
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::GetTextAsGlyphs( BOX2I* aBoundingBox,
std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const UTF8& aText, const VECTOR2D& aGlyphSize,
const wxPoint& aPosition, const EDA_ANGLE& aAngle,
VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const UTF8& aText, const VECTOR2D& aSize,
const VECTOR2I& aPosition, const EDA_ANGLE& aAngle,
bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const
{
wxPoint cursor( aPosition );
VECTOR2D glyphSize( aGlyphSize );
VECTOR2D glyphSize( aSize );
double tilt = ( aTextStyle & TEXT_STYLE::ITALIC ) ? ITALIC_TILT : 0.0;
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() );
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;
}
@ -299,18 +292,27 @@ VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox,
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 ) );
VECTOR2D barStart( aPosition.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();
aGlyphs.push_back( std::move( overbarGlyph ) );
}
if( aBoundingBox )
if( aBBox )
{
aBoundingBox->SetOrigin( aPosition.x, aPosition.y );
aBoundingBox->SetEnd( cursor.x + barOffset.x, cursor.y + std::max( glyphSize.y, barOffset.y ) );
aBoundingBox->Normalize();
aBBox->SetOrigin( aPosition );
aBBox->SetEnd( cursor.x + barOffset.x, cursor.y + std::max( glyphSize.y, barOffset.y ) );
aBBox->Normalize();
}
return VECTOR2I( cursor.x, aPosition.y );

View File

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

View File

@ -53,16 +53,19 @@ void FONT_CHOICE::SetFontSelection( KIFONT::FONT* aFont )
if( !aFont )
{
SetSelection( 0 );
return;
}
SetStringSelection( aFont->Name() );
if( GetSelection() == wxNOT_FOUND )
else
{
Append( aFont->Name() + m_notFound );
SetSelection( GetCount() );
SetStringSelection( aFont->Name() );
if( GetSelection() == wxNOT_FOUND )
{
Append( aFont->Name() + m_notFound );
SetSelection( GetCount() );
}
}
SendSelectionChangedEvent( wxEVT_CHOICE );
}

View File

@ -30,6 +30,7 @@
#include <outline_mode.h>
#include <eda_rect.h>
#include <font/glyph.h>
#include <font/text_attributes.h>
class OUTPUTFORMATTER;
@ -46,12 +47,19 @@ class wxFindReplaceData;
*/
struct TSEGM_2_POLY_PRMS
{
int m_textWidth;
int m_error;
int m_textWidth;
int m_error;
SHAPE_POLY_SET* m_cornerBuffer;
};
struct TSEGM_2_SHAPE_PRMS
{
int m_penWidth;
SHAPE_COMPOUND* m_shape;
};
/**
* Callback function used to convert text segments to polygons.
*/
@ -97,6 +105,8 @@ public:
virtual ~EDA_TEXT();
EDA_TEXT& operator=( const EDA_TEXT& aItem );
/**
* 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
* 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; };
/**
@ -141,42 +151,38 @@ public:
// Higher level classes may be more restrictive than this by overloading
// SetTextAngle() or merely calling EDA_TEXT::SetTextAngle() after clamping
// aAngle before calling this lowest inline accessor.
m_attributes.m_Angle = EDA_ANGLE( aAngleInTenthsOfADegree, EDA_ANGLE::TENTHS_OF_A_DEGREE );
}
void SetTextAngle( const EDA_ANGLE& aAngle )
{
m_attributes.m_Angle = aAngle;
SetTextAngle( EDA_ANGLE( aAngleInTenthsOfADegree, EDA_ANGLE::TENTHS_OF_A_DEGREE ) );
}
void SetTextAngle( const EDA_ANGLE& aAngle );
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; }
void SetBold( bool aBold ) { m_attributes.m_Bold = aBold; }
void SetBold( bool aBold );
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; }
void SetMirrored( bool isMirrored ) { m_attributes.m_Mirrored = isMirrored; }
void SetMirrored( bool isMirrored );
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
* 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; }
void SetHorizJustify( GR_TEXT_H_ALIGN_T aType );
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; };
void SetHorizJustify( GR_TEXT_H_ALIGN_T aType ) { m_attributes.m_Halign = aType; };
void SetVertJustify( GR_TEXT_V_ALIGN_T aType ) { m_attributes.m_Valign = aType; };
void SetKeepUpright( bool aKeepUpright ) { m_attributes.m_KeepUpright = aKeepUpright; }
void SetKeepUpright( bool aKeepUpright );
bool IsKeepUpright() const { return m_attributes.m_KeepUpright; }
/**
@ -208,36 +214,35 @@ public:
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; }
wxString GetFontName() const;
void SetLineSpacing( double aLineSpacing ) { m_attributes.m_LineSpacing = aLineSpacing; }
void SetLineSpacing( double aLineSpacing );
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,
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; }
void SetTextHeight( int aHeight ) { m_attributes.m_Size.y = aHeight; }
void SetTextHeight( int aHeight );
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; }
void SetTextX( int aX ) { m_pos.x = aX; }
void SetTextY( int aY ) { m_pos.y = aY; }
void SetTextX( int aX );
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_V_ALIGN_T MapVertJustify( int aVertJustify );
/**
@ -251,14 +256,6 @@ public:
void Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset,
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
* orientation, the bounding box is not always horizontal or vertical
@ -340,10 +337,19 @@ public:
virtual void Format( OUTPUTFORMATTER* aFormatter, int aNestLevel, int aControlBits ) const;
virtual EDA_ANGLE GetDrawRotation() const { return GetTextAngle(); }
virtual VECTOR2I GetDrawPos() const { return GetTextPos(); }
virtual VECTOR2I GetDrawPos() const { return GetTextPos(); }
virtual GR_TEXT_H_ALIGN_T GetDrawHorizJustify() const { return GetHorizJustify(); };
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;
private:
@ -366,6 +372,10 @@ private:
wxString m_shown_text; // Cache of unescaped text for efficient access
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;
VECTOR2I m_pos;
};

View File

@ -119,19 +119,21 @@ public:
* @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
* @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
*/
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 VECTOR2I& aPosition,
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
{
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;
/**
@ -169,19 +171,21 @@ public:
/**
* 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 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 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
* @return text cursor position after this text
*/
virtual VECTOR2I GetTextAsGlyphs( BOX2I* aBoundingBox,
std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const UTF8& aText, const VECTOR2D& aGlyphSize,
const wxPoint& aPosition, const EDA_ANGLE& aAngle,
virtual VECTOR2I GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const UTF8& aText, const VECTOR2D& aSize,
const VECTOR2I& aPosition, const EDA_ANGLE& aAngle,
bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const = 0;
protected:
@ -205,45 +209,42 @@ protected:
* 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 aBBox is an optional pointer to be filled with the bounding box.
* @param aText is the text to be drawn.
* @param aPosition is text position.
* @param aSize is the cap-height and em-width of the text
* @param aAngle is text angle.
* @param aIsMirrored is true if text should be drawn mirrored, false otherwise.
* @return new cursor position
* @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.
* @return new cursor position in non-rotated, non-mirrored coordinates
*/
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;
const VECTOR2I& aPosition, const VECTOR2D& aSize,
const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
bool aItalic ) 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 aBBox is an optional pointer to be filled with the bounding box.
* @param aText is the text to be drawn.
* @param aPosition is text position.
* @param aGlyphSize is glyph size.
* @param aAngle is text angle.
* @param aSize is the cap-height and em-width of the text.
* @return new cursor position
*/
VECTOR2D boundingBoxSingleLine( BOX2I* aBoundingBox, const UTF8& aText,
const VECTOR2D& aPosition, const VECTOR2D& aGlyphSize,
const EDA_ANGLE& aAngle, bool aIsItalic ) const;
VECTOR2D boundingBoxSingleLine( BOX2I* aBBox, const UTF8& aText, const VECTOR2I& aPosition,
const VECTOR2D& aSize, bool aItalic ) const;
void getLinePositions( const UTF8& aText, const VECTOR2D& aPosition, wxArrayString& aStringList,
std::vector<wxPoint>& aPositions, int& aLineCount,
void getLinePositions( const UTF8& aText, const VECTOR2I& aPosition,
wxArrayString& aTextLines, std::vector<VECTOR2I>& aPositions,
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, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const UTF8& aText, const VECTOR2D& aPosition, const VECTOR2D& aGlyphSize,
const EDA_ANGLE& aAngle, TEXT_STYLE_FLAGS aTextStyle ) const;
const UTF8& aText, const VECTOR2I& aPosition, const VECTOR2D& aSize,
const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const;
///< Factor that determines the pitch between 2 lines.
static constexpr double INTERLINE_PITCH_RATIO = 1.62; // The golden mean
@ -251,10 +252,8 @@ protected:
private:
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;
VECTOR2D getBoundingBox( const UTF8& aText, TEXT_STYLE_FLAGS aTextStyle,
const TEXT_ATTRIBUTES& aAttrs ) const;
protected:
wxString m_fontName; ///< Font name

View File

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

View File

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

View File

@ -91,14 +91,10 @@ public:
VECTOR2D ComputeTextLineSize( const KIGFX::GAL* aGal, const UTF8& aText ) const override;
VECTOR2I GetTextAsGlyphs( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const UTF8& aText, const VECTOR2D& aGlyphSize,
const wxPoint& aPosition, const EDA_ANGLE& aAngle,
const UTF8& aText, const VECTOR2D& aSize, const VECTOR2I& aPosition,
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;
private:
/**
* Load the standard KiCad stroke font.

View File

@ -220,6 +220,9 @@ public:
/// Compound assignment operator
VECTOR2<T>& operator+=( const VECTOR2<T>& aVector );
/// Compound assignment operator
VECTOR2<T>& operator*=( const VECTOR2<T>& aVector );
/// Compound assignment operator
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>
VECTOR2<T>& VECTOR2<T>::operator+=( const T& aScalar )
{

View File

@ -99,9 +99,19 @@ inline void RotatePoint( VECTOR2I& point, const VECTOR2I& centre, EDA_ANGLE angl
* Calculate the new coord point point for a center rotation center and angle in (1/10 degree).
*/
void RotatePoint( double *pX, double *pY, double angle );
void RotatePoint( double* pX, double* pY, double angle );
void RotatePoint( double *pX, double *pY, double cx, double cy, 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 );
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.

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 )
{
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 )
{
wxString shownText( aText->GetShownText() );
wxString resolvedText( aText->GetShownText() );
if( shownText.Length() == 0 )
if( resolvedText.Length() == 0 )
return;
const COLOR4D& color = m_pcbSettings.GetColor( aText, aText->GetLayer() );
@ -1589,15 +1589,25 @@ void PCB_PAINTER::draw( const PCB_TEXT* aText, int aLayer )
else
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 )
{
wxString shownText( aText->GetShownText() );
wxString resolvedText( aText->GetShownText() );
if( shownText.Length() == 0 )
if( resolvedText.Length() == 0 )
return;
const COLOR4D& color = m_pcbSettings.GetColor( aText, aLayer );
@ -1615,7 +1625,17 @@ void PCB_PAINTER::draw( const FP_TEXT* aText, int aLayer )
else
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
if( aText->IsSelected() )
@ -1848,6 +1868,7 @@ void PCB_PAINTER::draw( const PCB_DIMENSION_BASE* aDimension, int aLayer )
// Draw text
const PCB_TEXT& text = aDimension->Text();
wxString resolvedText = text.GetShownText();
VECTOR2D position( text.GetTextPos().x, text.GetTextPos().y );
TEXT_ATTRIBUTES attrs = text.GetAttributes();
@ -1856,7 +1877,17 @@ void PCB_PAINTER::draw( const PCB_DIMENSION_BASE* aDimension, int aLayer )
else
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;
void strokeText( const wxString& aText, const VECTOR2D& aPosition,
void strokeText( const wxString& aText, const VECTOR2I& aPosition,
const TEXT_ATTRIBUTES& aAttrs );
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()
{
wxCHECK_MSG( CurTok() == T_model, nullptr,
@ -2811,7 +2862,11 @@ PCB_TEXT* PCB_PARSER::parsePCB_TEXT()
break;
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;
default:
@ -3714,7 +3769,11 @@ FP_TEXT* PCB_PARSER::parseFP_TEXT()
break;
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;
case T_tstamp:

View File

@ -254,11 +254,19 @@ private:
/**
* 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.
*/
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();
/**

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
{
// Setup
@ -854,53 +940,8 @@ void PCB_PLUGIN::format( const PCB_SHAPE* aShape, int aNestLevel ) const
const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
m_out->Print( aNestLevel, "(gr_poly%s\n", locked.c_str() );
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 ) || !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, ")" );
formatPolyPts( outline, aNestLevel, ADVANCED_CFG::GetCfg().m_CompactSave );
m_out->Print( aNestLevel + 1, ")\n" );
}
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_LINE_CHAIN& outline = poly.Outline( 0 );
m_out->Print( aNestLevel, "(fp_poly%s (pts",
locked.c_str() );
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, ")" );
m_out->Print( aNestLevel, "(fp_poly%s\n", locked.c_str() );
formatPolyPts( outline, aNestLevel, ADVANCED_CFG::GetCfg().m_CompactSave );
m_out->Print( aNestLevel + 1, ")\n" );
}
else
{
@ -1691,54 +1686,9 @@ void PCB_PLUGIN::format( const PAD* aPad, int aNestLevel ) const
const SHAPE_POLY_SET& poly = primitive->GetPolyShape();
const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
m_out->Print( nested_level, "(gr_poly (pts" );
bool need_newline = false;
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, ")" );
m_out->Print( nested_level, "(gr_poly\n" );
formatPolyPts( outline, nested_level, ADVANCED_CFG::GetCfg().m_CompactSave );
m_out->Print( aNestLevel + 1, ")\n" );
}
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
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" );
}
@ -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() ) );
if( aText->GetFont() && aText->GetFont()->IsOutline() )
formatRenderCache( aText, aNestLevel + 1 );
m_out->Print( aNestLevel, ")\n" );
}
@ -2149,55 +2105,8 @@ void PCB_PLUGIN::format( const ZONE* aZone, int aNestLevel ) const
for( auto& chain : poly )
{
m_out->Print( aNestLevel + 1, "(polygon\n" );
m_out->Print( aNestLevel + 2, "(pts" );
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" );
formatPolyPts( chain, aNestLevel + 1, ADVANCED_CFG::GetCfg().m_CompactSave );
m_out->Print( aNestLevel + 1, ")\n" );
}
}
@ -2215,56 +2124,10 @@ void PCB_PLUGIN::format( const ZONE* aZone, int aNestLevel ) const
if( aZone->IsIsland( layer, ii ) )
m_out->Print( aNestLevel + 2, "(island)\n" );
m_out->Print( aNestLevel + 2, "(pts" );
const SHAPE_LINE_CHAIN& chain = fv.COutline( ii );
bool need_newline = true;
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" );
formatPolyPts( chain, aNestLevel + 1, ADVANCED_CFG::GetCfg().m_CompactSave );
m_out->Print( aNestLevel + 1, ")\n" );
}
// Save the filling segments list

View File

@ -45,6 +45,8 @@ class PCB_GROUP;
class PCB_TRACK;
class ZONE;
class PCB_TEXT;
class EDA_TEXT;
class SHAPE_LINE_CHAIN;
/// 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 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 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 ) )
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->SetNet( -1 );
solid->SetParent( dynamic_cast<BOARD_ITEM*>( aText ) );
solid->SetShape( shape );
solid->SetIsCompoundShapePrimitive();
solid->SetRoutable( false );
solid->SetLayer( aLayer );
solid->SetNet( -1 );
solid->SetParent( dynamic_cast<BOARD_ITEM*>( aText ) );
solid->SetShape( aText->GetEffectiveTextShape()->Clone() );
solid->SetRoutable( false );
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 ) );
}
}
aWorld->Add( std::move( solid ) );
return true;