25#include <unordered_map>
37#include <wx/tokenzr.h>
64 std::unique_ptr<MARKUP::NODE>
root;
74 auto it =
m_cache.find( aQuery );
76 m_cacheMru.emplace_front( std::make_pair( aQuery, std::move( aResult ) ) );
99 auto it =
m_cache.find( aQuery );
118 std::unordered_map<wxString, std::list<std::pair<wxString, ENTRY>>::iterator>
m_cache;
144 const std::vector<wxString>* aEmbeddedFiles,
bool aForDrawingSheet )
149 std::tuple<wxString, bool, bool, bool> key = { aFontName, aBold, aItalic, aForDrawingSheet };
151 FONT* font =
nullptr;
178 wxArrayString& aTextLines, std::vector<VECTOR2I>& aPositions,
180 const METRICS& aFontMetrics )
const
183 int lineCount = aTextLines.Count();
184 aPositions.reserve( lineCount );
189 for(
int i = 0; i < lineCount; i++ )
191 VECTOR2I pos( aPosition.
x, aPosition.
y + i * interline );
196 aExtents.push_back( bBox );
199 height += ( aAttrs.
m_Size.
y * 1.17 );
220 wxFAIL_MSG( wxT(
"Indeterminate state legal only in dialogs." ) );
224 for(
int i = 0; i < lineCount; i++ )
226 VECTOR2I lineSize = aExtents.at( i );
229 lineOffset.
y += i * interline;
237 wxFAIL_MSG( wxT(
"Indeterminate state legal only in dialogs." ) );
241 aPositions.push_back( aPosition + lineOffset );
248 wxString* aActiveUrl )
const
250 if( !aGal || aText.empty() )
253 VECTOR2I position( aPosition - aCursor );
256 wxArrayString strings_list;
257 std::vector<VECTOR2I> positions;
258 std::vector<VECTOR2I> extents;
260 getLinePositions( aText, position, strings_list, positions, extents, aAttrs, aFontMetrics );
264 for(
size_t i = 0; i < strings_list.GetCount(); i++ )
268 aFontMetrics, aMousePos, aActiveUrl );
279 const METRICS& aFontMetrics, std::optional<VECTOR2I> aMousePos, wxString* aActiveUrl )
282 bool drawUnderline =
false;
283 bool drawOverbar =
false;
284 bool useHoverColor =
false;
285 int start = aGlyphs ? (int) aGlyphs->size() : 0;
292 if( !aNode->is_root() )
303 drawUnderline =
true;
305 if( aNode->has_content() )
310 nextPosition, aAngle, aMirror, aOrigin, textStyle );
313 aBoundingBox->
Merge( bbox );
315 if( aNode->
isURL() && aMousePos.has_value() && bbox.
Contains( aMousePos.value() ) )
317 useHoverColor =
true;
318 drawUnderline =
true;
323 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
325 nextPosition =
drawMarkup( aBoundingBox, aGlyphs, child.get(), nextPosition, aFont, aSize,
326 aAngle, aMirror, aOrigin, textStyle, aFontMetrics, aMousePos,
334 double barTrim = aSize.
x * 0.1;
337 VECTOR2D barStart( aPosition.
x + barTrim, aPosition.
y - barOffset );
338 VECTOR2D barEnd( nextPosition.
x - barTrim, nextPosition.
y - barOffset );
348 aGlyphs->push_back( barGlyph.
Transform( { 1.0, 1.0 }, { 0, 0 },
false, aAngle, aMirror,
356 double barTrim = aSize.
x * 0.1;
359 VECTOR2D barStart( aPosition.
x + barTrim, aPosition.
y - barOffset );
360 VECTOR2D barEnd( nextPosition.
x - barTrim, nextPosition.
y - barOffset );
370 aGlyphs->push_back( barGlyph.
Transform( { 1.0, 1.0 }, { 0, 0 },
false, aAngle, aMirror,
377 for(
int ii = start; ii < (int) aGlyphs->size(); ++ii )
378 (*aGlyphs)[ii]->SetIsHover(
true );
380 if( aActiveUrl && aActiveUrl->IsEmpty() )
392 std::optional<VECTOR2I> aMousePos, wxString* aActiveUrl )
const
398 if( !markup || !markup->
root )
408 wxASSERT( markup && markup->
root );
410 return ::drawMarkup( aBoundingBox, aGlyphs, markup->
root.get(), aPosition,
this, aSize, aAngle,
411 aMirror, aOrigin, aTextStyle, aFontMetrics, aMousePos, aActiveUrl );
417 bool aMirror,
const VECTOR2I& aOrigin,
bool aItalic,
bool aUnderline,
418 bool aHover,
const METRICS& aFontMetrics, std::optional<VECTOR2I> aMousePos,
419 wxString* aActiveUrl )
const
432 std::vector<std::unique_ptr<GLYPH>> glyphs;
434 (void)
drawMarkup( aBoundingBox, &glyphs, aText, aPosition, aSize, aAngle, aMirror, aOrigin,
435 textStyle, aFontMetrics, aMousePos, aActiveUrl );
439 for( std::unique_ptr<GLYPH>& glyph : glyphs )
440 glyph->SetIsHover(
true );
448 bool aBold,
bool aItalic,
const METRICS& aFontMetrics )
const
461 textStyle, aFontMetrics );
479 bool aItalic,
const METRICS& aFontMetrics )
const
487 VECTOR2I(), textStyle, aFontMetrics );
500 const std::unique_ptr<MARKUP::NODE>& aNode,
const KIFONT::FONT* aFont,
502 bool aInsideMarkup =
false )
506 if( !aNode->is_root() )
508 wxChar escapeChar = 0;
510 if( aNode->isSubscript() )
515 else if( aNode->isSuperscript() )
521 if( aNode->isOverbar() )
529 wxString word = wxString::Format( wxT(
"%c{" ), escapeChar );
532 if( aNode->has_content() )
535 aSize, { 0, 0 },
ANGLE_0,
false, { 0, 0 },
537 word += aNode->asWxString();
541 std::vector<std::pair<wxString, int>> childWords;
543 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
546 for(
const std::pair<wxString, int>& childWord : childWords )
548 word += childWord.first;
549 width += childWord.second;
553 aWords->emplace_back( std::make_pair( word, width ) );
556 else if( aInsideMarkup )
561 wxString content = aNode->asWxString();
562 int w = aFont->
GetTextAsGlyphs(
nullptr,
nullptr, content, aSize, { 0, 0 },
563 ANGLE_0,
false, { 0, 0 }, textStyle ).x;
565 aWords->emplace_back( std::make_pair( content, w ) );
569 wxString textRun = aNode->asWxString();
570 wxStringTokenizer tokenizer( textRun,
" ", wxTOKEN_RET_DELIMS );
571 std::vector<wxString> words;
573 while( tokenizer.HasMoreTokens() )
574 words.emplace_back( tokenizer.GetNextToken() );
576 for(
const wxString& word : words )
578 wxString chars = word;
581 int w = aFont->
GetTextAsGlyphs(
nullptr,
nullptr, chars.IsEmpty() ? word : chars,
582 aSize, { 0, 0 },
ANGLE_0,
false, { 0, 0 },
585 aWords->emplace_back( std::make_pair( word, w ) );
590 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
591 wordbreakMarkup( aWords, child, aFont, aSize, textStyle, aInsideMarkup );
599 std::unique_ptr<MARKUP::NODE> root = markupParser.
Parse();
606 bool aBold,
bool aItalic )
const
619 wxArrayString textLines;
622 aText = wxEmptyString;
624 for(
size_t ii = 0; ii < textLines.Count(); ++ii )
626 std::vector<std::pair<wxString, int>> markup;
627 std::vector<std::pair<wxString, int>> words;
631 for(
const auto& [ run, runWidth ] : markup )
633 if( !words.empty() && !words.back().first.EndsWith(
' ' ) )
635 words.back().first += run;
636 words.back().second += runWidth;
640 words.emplace_back( std::make_pair( run, runWidth ) );
644 bool buryMode =
false;
646 wxString pendingSpaces;
648 for(
const auto& [ word, wordWidth ] : words )
650 int pendingSpaceWidth = (int) pendingSpaces.Length() * spaceWidth;
651 bool overflow = lineWidth + pendingSpaceWidth + wordWidth > aColumnWidth - aThickness;
653 if( overflow && pendingSpaces.Length() > 0 )
657 pendingSpaces = wxEmptyString;
658 pendingSpaceWidth = 0;
662 if( word == wxS(
" " ) )
664 pendingSpaces += word;
674 aText += pendingSpaces;
675 lineWidth += pendingSpaceWidth;
678 if( word.EndsWith(
' ' ) )
680 aText += word.Left( word.Length() - 1 );
681 pendingSpaces = wxS(
" " );
686 pendingSpaces = wxEmptyString;
689 lineWidth += wordWidth;
694 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
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
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