29#include <unordered_map>
41#include <wx/tokenzr.h>
68 std::unique_ptr<MARKUP::NODE>
root;
78 auto it =
m_cache.find( aQuery );
80 m_cacheMru.emplace_front( std::make_pair( aQuery, std::move( aResult ) ) );
103 auto it =
m_cache.find( aQuery );
122 std::unordered_map<wxString, std::list<std::pair<wxString, ENTRY>>::iterator>
m_cache;
148 const std::vector<wxString>* aEmbeddedFiles,
bool aForDrawingSheet )
153 std::tuple<wxString, bool, bool, bool> key = { aFontName, aBold, aItalic, aForDrawingSheet };
155 FONT* font =
nullptr;
182 wxArrayString& aTextLines, std::vector<VECTOR2I>& aPositions,
184 const METRICS& aFontMetrics )
const
187 int lineCount = aTextLines.Count();
188 aPositions.reserve( lineCount );
193 for(
int i = 0; i < lineCount; i++ )
195 VECTOR2I pos( aPosition.
x, aPosition.
y + i * interline );
200 aExtents.push_back( bBox );
203 height += ( aAttrs.
m_Size.
y * 1.17 );
224 wxFAIL_MSG( wxT(
"Indeterminate state legal only in dialogs." ) );
228 for(
int i = 0; i < lineCount; i++ )
230 VECTOR2I lineSize = aExtents.at( i );
233 lineOffset.
y += i * interline;
241 wxFAIL_MSG( wxT(
"Indeterminate state legal only in dialogs." ) );
245 aPositions.push_back( aPosition + lineOffset );
252 wxString* aActiveUrl )
const
254 if( !aGal || aText.empty() )
257 VECTOR2I position( aPosition - aCursor );
260 wxArrayString strings_list;
261 std::vector<VECTOR2I> positions;
262 std::vector<VECTOR2I> extents;
264 getLinePositions( aText, position, strings_list, positions, extents, aAttrs, aFontMetrics );
268 for(
size_t i = 0; i < strings_list.GetCount(); i++ )
272 aFontMetrics, aMousePos, aActiveUrl );
283 const METRICS& aFontMetrics, std::optional<VECTOR2I> aMousePos, wxString* aActiveUrl )
286 bool drawUnderline =
false;
287 bool drawOverbar =
false;
288 bool useHoverColor =
false;
289 int start = aGlyphs ? (int) aGlyphs->size() : 0;
296 if( !aNode->is_root() )
307 drawUnderline =
true;
309 if( aNode->has_content() )
314 nextPosition, aAngle, aMirror, aOrigin, textStyle );
317 aBoundingBox->
Merge( bbox );
319 if( aNode->
isURL() && aMousePos.has_value() && bbox.
Contains( aMousePos.value() ) )
321 useHoverColor =
true;
322 drawUnderline =
true;
327 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
329 nextPosition =
drawMarkup( aBoundingBox, aGlyphs, child.get(), nextPosition, aFont, aSize,
330 aAngle, aMirror, aOrigin, textStyle, aFontMetrics, aMousePos,
338 double barTrim = aSize.
x * 0.1;
341 VECTOR2D barStart( aPosition.
x + barTrim, aPosition.
y - barOffset );
342 VECTOR2D barEnd( nextPosition.
x - barTrim, nextPosition.
y - barOffset );
352 aGlyphs->push_back( barGlyph.
Transform( { 1.0, 1.0 }, { 0, 0 },
false, aAngle, aMirror,
360 double barTrim = aSize.
x * 0.1;
363 VECTOR2D barStart( aPosition.
x + barTrim, aPosition.
y - barOffset );
364 VECTOR2D barEnd( nextPosition.
x - barTrim, nextPosition.
y - barOffset );
374 aGlyphs->push_back( barGlyph.
Transform( { 1.0, 1.0 }, { 0, 0 },
false, aAngle, aMirror,
381 for(
int ii = start; ii < (int) aGlyphs->size(); ++ii )
382 (*aGlyphs)[ii]->SetIsHover(
true );
396 std::optional<VECTOR2I> aMousePos, wxString* aActiveUrl )
const
402 if( !markup || !markup->
root )
412 wxASSERT( markup && markup->
root );
414 return ::drawMarkup( aBoundingBox, aGlyphs, markup->
root.get(), aPosition,
this, aSize, aAngle,
415 aMirror, aOrigin, aTextStyle, aFontMetrics, aMousePos, aActiveUrl );
421 bool aMirror,
const VECTOR2I& aOrigin,
bool aItalic,
bool aUnderline,
422 bool aHover,
const METRICS& aFontMetrics, std::optional<VECTOR2I> aMousePos,
423 wxString* aActiveUrl )
const
436 std::vector<std::unique_ptr<GLYPH>> glyphs;
438 (void)
drawMarkup( aBoundingBox, &glyphs, aText, aPosition, aSize, aAngle, aMirror, aOrigin,
439 textStyle, aFontMetrics, aMousePos, aActiveUrl );
443 for( std::unique_ptr<GLYPH>& glyph : glyphs )
444 glyph->SetIsHover(
true );
452 bool aBold,
bool aItalic,
const METRICS& aFontMetrics )
const
465 textStyle, aFontMetrics );
483 bool aItalic,
const METRICS& aFontMetrics )
const
491 VECTOR2I(), textStyle, aFontMetrics );
504 const std::unique_ptr<MARKUP::NODE>& aNode,
const KIFONT::FONT* aFont,
509 if( !aNode->is_root() )
511 wxChar escapeChar = 0;
513 if( aNode->isSubscript() )
518 else if( aNode->isSuperscript() )
524 if( aNode->isOverbar() )
532 wxString word = wxString::Format( wxT(
"%c{" ), escapeChar );
535 if( aNode->has_content() )
538 aSize, { 0, 0 },
ANGLE_0,
false, { 0, 0 },
540 word += aNode->asWxString();
544 std::vector<std::pair<wxString, int>> childWords;
546 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
549 for(
const std::pair<wxString, int>& childWord : childWords )
551 word += childWord.first;
552 width += childWord.second;
556 aWords->emplace_back( std::make_pair( word, width ) );
561 wxString textRun = aNode->asWxString();
562 wxStringTokenizer tokenizer( textRun,
" ", wxTOKEN_RET_DELIMS );
563 std::vector<wxString> words;
565 while( tokenizer.HasMoreTokens() )
566 words.emplace_back( tokenizer.GetNextToken() );
568 for(
const wxString& word : words )
570 wxString chars = word;
573 int w = aFont->
GetTextAsGlyphs(
nullptr,
nullptr, chars, aSize, { 0, 0 },
574 ANGLE_0,
false, { 0, 0 }, textStyle ).x;
576 aWords->emplace_back( std::make_pair( word, w ) );
581 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
590 std::unique_ptr<MARKUP::NODE> root = markupParser.
Parse();
597 bool aBold,
bool aItalic )
const
610 wxArrayString textLines;
613 aText = wxEmptyString;
615 for(
size_t ii = 0; ii < textLines.Count(); ++ii )
617 std::vector<std::pair<wxString, int>> markup;
618 std::vector<std::pair<wxString, int>> words;
622 for(
const auto& [ run, runWidth ] : markup )
624 if( !words.empty() && !words.back().first.EndsWith(
' ' ) )
626 words.back().first += run;
627 words.back().second += runWidth;
631 words.emplace_back( std::make_pair( run, runWidth ) );
635 bool buryMode =
false;
637 wxString pendingSpaces;
639 for(
const auto& [ word, wordWidth ] : words )
641 int pendingSpaceWidth = (int) pendingSpaces.Length() * spaceWidth;
642 bool overflow = lineWidth + pendingSpaceWidth + wordWidth > aColumnWidth - aThickness;
644 if( overflow && pendingSpaces.Length() > 0 )
648 pendingSpaces = wxEmptyString;
649 pendingSpaceWidth = 0;
653 if( word == wxS(
" " ) )
655 pendingSpaces += word;
665 aText += pendingSpaces;
666 lineWidth += pendingSpaceWidth;
669 if( word.EndsWith(
' ' ) )
671 aText += word.Left( word.Length() - 1 );
672 pendingSpaces = wxS(
" " );
677 pendingSpaces = wxEmptyString;
680 lineWidth += wordWidth;
685 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
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 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
static std::mutex s_markupCacheMutex
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)
Break marked-up text into "words".
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
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.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
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