25#include <unordered_map>
38#include <wx/tokenzr.h>
65 std::unique_ptr<MARKUP::NODE>
root;
75 auto it =
m_cache.find( aQuery );
77 m_cacheMru.emplace_front( std::make_pair( aQuery, std::move( aResult ) ) );
100 auto it =
m_cache.find( aQuery );
119 std::unordered_map<wxString, std::list<std::pair<wxString, ENTRY>>::iterator>
m_cache;
145 const std::vector<wxString>* aEmbeddedFiles,
bool aForDrawingSheet )
149 if( aFontName.empty() || aFontName.StartsWith( s_kicadFontName ) )
152 std::tuple<wxString, bool, bool, bool> key = { aFontName, aBold, aItalic, aForDrawingSheet };
154 FONT* font =
nullptr;
180 std::vector<VECTOR2I>& aPositions, std::vector<VECTOR2I>& aExtents,
184 int lineCount = aTextLines.Count();
185 aPositions.reserve( lineCount );
190 for(
int i = 0; i < lineCount; i++ )
192 VECTOR2I pos( aPosition.
x, aPosition.
y + i * interline );
197 aExtents.push_back( bBox );
200 height += ( aAttrs.
m_Size.
y * 1.17 );
223 for(
int i = 0; i < lineCount; i++ )
225 VECTOR2I lineSize = aExtents.at( i );
228 lineOffset.
y += i * interline;
238 aPositions.push_back( aPosition + lineOffset );
245 wxString* aActiveUrl )
const
247 if( !aGal || aText.empty() )
250 VECTOR2I position( aPosition - aCursor );
253 wxArrayString strings_list;
254 std::vector<VECTOR2I> positions;
255 std::vector<VECTOR2I> extents;
257 getLinePositions( aText, position, strings_list, positions, extents, aAttrs, aFontMetrics );
261 for(
size_t i = 0; i < strings_list.GetCount(); i++ )
265 aFontMetrics, aMousePos, aActiveUrl );
276 const METRICS& aFontMetrics, std::optional<VECTOR2I> aMousePos, wxString* aActiveUrl )
279 bool drawUnderline =
false;
280 bool drawOverbar =
false;
281 bool useHoverColor =
false;
282 int start = aGlyphs ? (int) aGlyphs->size() : 0;
289 if( !aNode->is_root() )
300 drawUnderline =
true;
302 if( aNode->has_content() )
307 nextPosition, aAngle, aMirror, aOrigin, textStyle );
310 aBoundingBox->
Merge( bbox );
312 if( aNode->
isURL() && aMousePos.has_value() && bbox.
Contains( aMousePos.value() ) )
314 useHoverColor =
true;
315 drawUnderline =
true;
320 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
322 nextPosition =
drawMarkup( aBoundingBox, aGlyphs, child.get(), nextPosition, aFont, aSize,
323 aAngle, aMirror, aOrigin, textStyle, aFontMetrics, aMousePos,
331 double barTrim = aSize.
x * 0.1;
334 VECTOR2D barStart( aPosition.
x + barTrim, aPosition.
y - barOffset );
335 VECTOR2D barEnd( nextPosition.
x - barTrim, nextPosition.
y - barOffset );
345 aGlyphs->push_back( barGlyph.
Transform( { 1.0, 1.0 }, { 0, 0 },
false, aAngle, aMirror, aOrigin ) );
352 double barTrim = aSize.
x * 0.1;
355 VECTOR2D barStart( aPosition.
x + barTrim, aPosition.
y - barOffset );
356 VECTOR2D barEnd( nextPosition.
x - barTrim, nextPosition.
y - barOffset );
366 aGlyphs->push_back( barGlyph.
Transform( { 1.0, 1.0 }, { 0, 0 },
false, aAngle, aMirror, aOrigin ) );
372 for(
int ii = start; ii < (int) aGlyphs->size(); ++ii )
373 (*aGlyphs)[ii]->SetIsHover(
true );
375 if( aActiveUrl && aActiveUrl->IsEmpty() )
387 std::optional<VECTOR2I> aMousePos, wxString* aActiveUrl )
const
393 if( !markup || !markup->
root )
403 wxASSERT( markup && markup->
root );
405 return ::drawMarkup( aBoundingBox, aGlyphs, markup->
root.get(), aPosition,
this, aSize, aAngle,
406 aMirror, aOrigin, aTextStyle, aFontMetrics, aMousePos, aActiveUrl );
412 bool aMirror,
const VECTOR2I& aOrigin,
bool aItalic,
bool aUnderline,
413 bool aHover,
const METRICS& aFontMetrics, std::optional<VECTOR2I> aMousePos,
414 wxString* aActiveUrl )
const
427 std::vector<std::unique_ptr<GLYPH>> glyphs;
429 (void)
drawMarkup( aBoundingBox, &glyphs, aText, aPosition, aSize, aAngle, aMirror, aOrigin,
430 textStyle, aFontMetrics, aMousePos, aActiveUrl );
434 for( std::unique_ptr<GLYPH>& glyph : glyphs )
435 glyph->SetIsHover(
true );
443 bool aBold,
bool aItalic,
const METRICS& aFontMetrics )
const
456 textStyle, aFontMetrics );
475 wxArrayString strings_list;
476 std::vector<VECTOR2I> positions;
477 std::vector<VECTOR2I> extents;
479 getLinePositions( aText, aAnchor, strings_list, positions, extents, aAttributes, aFontMetrics );
481 if( positions.empty() )
484 return positions.front();
489 const VECTOR2I& aSize,
bool aItalic,
const METRICS& aFontMetrics )
const
497 textStyle, aFontMetrics );
509void wordbreakMarkup( std::vector<std::pair<wxString, int>>* aWords,
const std::unique_ptr<MARKUP::NODE>& aNode,
511 bool aInsideMarkup =
false )
515 if( !aNode->is_root() )
517 wxChar escapeChar = 0;
519 if( aNode->isSubscript() )
524 else if( aNode->isSuperscript() )
530 if( aNode->isOverbar() )
538 wxString word = wxString::Format( wxT(
"%c{" ), escapeChar );
541 if( aNode->has_content() )
544 ANGLE_0,
false, { 0, 0 }, textStyle );
545 word += aNode->asWxString();
549 std::vector<std::pair<wxString, int>> childWords;
551 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
554 for(
const std::pair<wxString, int>& childWord : childWords )
556 word += childWord.first;
557 width += childWord.second;
561 aWords->emplace_back( std::make_pair( word, width ) );
564 else if( aInsideMarkup )
569 wxString content = aNode->asWxString();
571 { 0, 0 }, textStyle ).x;
573 aWords->emplace_back( std::make_pair( content, w ) );
577 wxString textRun = aNode->asWxString();
578 wxStringTokenizer tokenizer( textRun,
" ", wxTOKEN_RET_DELIMS );
579 std::vector<wxString> words;
581 while( tokenizer.HasMoreTokens() )
582 words.emplace_back( tokenizer.GetNextToken() );
584 for(
const wxString& word : words )
586 wxString chars = word;
589 int w = aFont->
GetTextAsGlyphs(
nullptr,
nullptr, chars.IsEmpty() ? word : chars, aSize,
590 { 0, 0 },
ANGLE_0,
false, { 0, 0 }, textStyle ).
x;
592 aWords->emplace_back( std::make_pair( word, w ) );
597 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
598 wordbreakMarkup( aWords, child, aFont, aSize, textStyle, aInsideMarkup );
606 std::unique_ptr<MARKUP::NODE> root = markupParser.
Parse();
613 bool aBold,
bool aItalic )
const
626 wxArrayString textLines;
629 aText = wxEmptyString;
631 for(
size_t ii = 0; ii < textLines.Count(); ++ii )
633 std::vector<std::pair<wxString, int>> markup;
634 std::vector<std::pair<wxString, int>> words;
638 for(
const auto& [ run, runWidth ] : markup )
640 if( !words.empty() && !words.back().first.EndsWith(
' ' ) )
642 words.back().first += run;
643 words.back().second += runWidth;
647 words.emplace_back( std::make_pair( run, runWidth ) );
651 bool buryMode =
false;
653 wxString pendingSpaces;
655 for(
const auto& [ word, wordWidth ] : words )
657 int pendingSpaceWidth = (int) pendingSpaces.Length() * spaceWidth;
658 bool overflow = lineWidth + pendingSpaceWidth + wordWidth > aColumnWidth - aThickness;
660 if( overflow && pendingSpaces.Length() > 0 )
664 pendingSpaces = wxEmptyString;
665 pendingSpaceWidth = 0;
669 if( word == wxS(
" " ) )
671 pendingSpaces += word;
681 aText += pendingSpaces;
682 lineWidth += pendingSpaceWidth;
685 if( word.EndsWith(
' ' ) )
687 aText += word.Left( word.Length() - 1 );
688 pendingSpaces = wxS(
" " );
693 pendingSpaces = wxEmptyString;
696 lineWidth += wordWidth;
701 if( ii != ( textLines.Count() - 1 ) )
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
constexpr bool Contains(const Vec &aPoint) const
EDA_ANGLE GetAngle() const
FONT is an abstract base class for both outline and stroke fonts.
VECTOR2I boundingBoxSingleLine(BOX2I *aBBox, const wxString &aText, const VECTOR2I &aPosition, const VECTOR2I &aSize, bool aItalic, const METRICS &aFontMetrics) const
Compute the bounding box for a single line of text.
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
virtual bool IsStroke() const
static FONT * s_defaultFont
void getLinePositions(const wxString &aText, const VECTOR2I &aPosition, wxArrayString &aTextLines, std::vector< VECTOR2I > &aPositions, std::vector< VECTOR2I > &aExtents, const TEXT_ATTRIBUTES &aAttrs, const METRICS &aFontMetrics) const
void wordbreakMarkup(std::vector< std::pair< wxString, int > > *aWords, const wxString &aText, const VECTOR2I &aSize, TEXT_STYLE_FLAGS aTextStyle) const
void Draw(KIGFX::GAL *aGal, const wxString &aText, const VECTOR2I &aPosition, const VECTOR2I &aCursor, const TEXT_ATTRIBUTES &aAttributes, const METRICS &aFontMetrics, std::optional< VECTOR2I > aMousePos=std::nullopt, wxString *aActiveUrl=nullptr) const
Draw a string.
void drawSingleLineText(KIGFX::GAL *aGal, BOX2I *aBoundingBox, const wxString &aText, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aAngle, bool aMirror, const VECTOR2I &aOrigin, bool aItalic, bool aUnderline, bool aHover, const METRICS &aFontMetrics, std::optional< VECTOR2I > aMousePos, wxString *aActiveUrl) const
Draw a single line of text.
virtual bool IsOutline() const
VECTOR2I GetAlignedDrawPosition(const wxString &aText, const VECTOR2I &aAnchor, const TEXT_ATTRIBUTES &aAttributes, const METRICS &aFontMetrics) const
Return the draw position for the first line of text, using the same alignment rules as GAL rendering ...
VECTOR2I drawMarkup(BOX2I *aBoundingBox, std::vector< std::unique_ptr< GLYPH > > *aGlyphs, const wxString &aText, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aAngle, bool aMirror, const VECTOR2I &aOrigin, TEXT_STYLE_FLAGS aTextStyle, const METRICS &aFontMetrics, std::optional< VECTOR2I > aMousePos=std::nullopt, wxString *aActiveUrl=nullptr) const
VECTOR2I StringBoundaryLimits(const wxString &aText, const VECTOR2I &aSize, int aThickness, bool aBold, bool aItalic, const METRICS &aFontMetrics) const
Compute the boundary limits of aText (the bounding box of all shapes).
static std::map< std::tuple< wxString, bool, bool, bool >, FONT * > s_fontMap
virtual double GetInterline(double aGlyphHeight, const METRICS &aFontMetrics) const =0
Compute the distance (interline) between 2 lines of text (for multiline texts).
virtual VECTOR2I GetTextAsGlyphs(BOX2I *aBBox, std::vector< std::unique_ptr< GLYPH > > *aGlyphs, const wxString &aText, const VECTOR2I &aSize, const VECTOR2I &aPosition, const EDA_ANGLE &aAngle, bool aMirror, const VECTOR2I &aOrigin, TEXT_STYLE_FLAGS aTextStyle) const =0
Convert text string to an array of GLYPHs.
static FONT * getDefaultFont()
void LinebreakText(wxString &aText, int aColumnWidth, const VECTOR2I &aGlyphSize, int aThickness, bool aBold, bool aItalic) const
Insert characters into text to ensure that no lines are wider than aColumnWidth.
double GetUnderlineVerticalPosition(double aGlyphHeight) const
Compute the vertical position of an underline.
double GetOverbarVerticalPosition(double aGlyphHeight) const
Compute the vertical position of an overbar.
static const METRICS & Default()
static OUTLINE_FONT * LoadFont(const wxString &aFontFileName, bool aBold, bool aItalic, const std::vector< wxString > *aEmbeddedFiles, bool aForDrawingSheet)
Load an outline font.
static STROKE_FONT * LoadFont(const wxString &aFontName)
Load a stroke font.
void AddPoint(const VECTOR2D &aPoint)
std::unique_ptr< GLYPH > Transform(const VECTOR2D &aGlyphSize, const VECTOR2I &aOffset, double aTilt, const EDA_ANGLE &aAngle, bool aMirror, const VECTOR2I &aOrigin)
Abstract interface for drawing on a 2D-surface.
virtual void SetLineWidth(float aLineWidth)
Set the line width.
virtual void DrawGlyphs(const std::vector< std::unique_ptr< KIFONT::GLYPH > > &aGlyphs)
Draw polygons representing font glyphs.
std::unique_ptr< NODE > Parse()
std::list< std::pair< wxString, ENTRY > > m_cacheMru
MARKUP_CACHE(size_t aMaxSize)
ENTRY & Put(const wxString &aQuery, ENTRY &&aResult)
ENTRY * Get(const wxString &aQuery)
std::unordered_map< wxString, std::list< std::pair< wxString, ENTRY > >::iterator > m_cache
GR_TEXT_H_ALIGN_T m_Halign
GR_TEXT_V_ALIGN_T m_Valign
static constexpr EDA_ANGLE ANGLE_0
void wordbreakMarkup(std::vector< std::pair< wxString, int > > *aWords, const std::unique_ptr< MARKUP::NODE > &aNode, const KIFONT::FONT *aFont, const VECTOR2I &aSize, TEXT_STYLE_FLAGS aTextStyle, bool aInsideMarkup=false)
Break marked-up text into "words".
static std::mutex s_markupCacheMutex
static std::mutex s_defaultFontMutex
static MARKUP_CACHE s_markupCache(1024)
VECTOR2I drawMarkup(BOX2I *aBoundingBox, std::vector< std::unique_ptr< GLYPH > > *aGlyphs, const MARKUP::NODE *aNode, const VECTOR2I &aPosition, const KIFONT::FONT *aFont, const VECTOR2I &aSize, const EDA_ANGLE &aAngle, bool aMirror, const VECTOR2I &aOrigin, TEXT_STYLE_FLAGS aTextStyle, const METRICS &aFontMetrics, std::optional< VECTOR2I > aMousePos, wxString *aActiveUrl)
unsigned int TEXT_STYLE_FLAGS
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
This file contains miscellaneous commonly used macros and functions.
BOX2I boundingBox(T aObject, int aLayer)
Used by SHAPE_INDEX to get the bounding box of a generic T object.
void wxStringSplit(const wxString &aText, wxArrayString &aStrings, wxChar aSplitter)
Split aString to a string list separated at aSplitter.
bool isSuperscript() const
wxString asWxString() const
std::unique_ptr< MARKUP::NODE > root
@ GR_TEXT_H_ALIGN_INDETERMINATE
@ GR_TEXT_V_ALIGN_INDETERMINATE
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D