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 );
480 wxArrayString strings_list;
481 std::vector<VECTOR2I> positions;
482 std::vector<VECTOR2I> extents;
484 getLinePositions( aText, aAnchor, strings_list, positions, extents, aAttributes, aFontMetrics );
486 if( positions.empty() )
489 return positions.front();
495 bool aItalic,
const METRICS& aFontMetrics )
const
503 VECTOR2I(), textStyle, aFontMetrics );
516 const std::unique_ptr<MARKUP::NODE>& aNode,
const KIFONT::FONT* aFont,
518 bool aInsideMarkup =
false )
522 if( !aNode->is_root() )
524 wxChar escapeChar = 0;
526 if( aNode->isSubscript() )
531 else if( aNode->isSuperscript() )
537 if( aNode->isOverbar() )
545 wxString word = wxString::Format( wxT(
"%c{" ), escapeChar );
548 if( aNode->has_content() )
551 aSize, { 0, 0 },
ANGLE_0,
false, { 0, 0 },
553 word += aNode->asWxString();
557 std::vector<std::pair<wxString, int>> childWords;
559 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
562 for(
const std::pair<wxString, int>& childWord : childWords )
564 word += childWord.first;
565 width += childWord.second;
569 aWords->emplace_back( std::make_pair( word, width ) );
572 else if( aInsideMarkup )
577 wxString content = aNode->asWxString();
578 int w = aFont->
GetTextAsGlyphs(
nullptr,
nullptr, content, aSize, { 0, 0 },
579 ANGLE_0,
false, { 0, 0 }, textStyle ).x;
581 aWords->emplace_back( std::make_pair( content, w ) );
585 wxString textRun = aNode->asWxString();
586 wxStringTokenizer tokenizer( textRun,
" ", wxTOKEN_RET_DELIMS );
587 std::vector<wxString> words;
589 while( tokenizer.HasMoreTokens() )
590 words.emplace_back( tokenizer.GetNextToken() );
592 for(
const wxString& word : words )
594 wxString chars = word;
597 int w = aFont->
GetTextAsGlyphs(
nullptr,
nullptr, chars.IsEmpty() ? word : chars,
598 aSize, { 0, 0 },
ANGLE_0,
false, { 0, 0 },
601 aWords->emplace_back( std::make_pair( word, w ) );
606 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
607 wordbreakMarkup( aWords, child, aFont, aSize, textStyle, aInsideMarkup );
615 std::unique_ptr<MARKUP::NODE> root = markupParser.
Parse();
622 bool aBold,
bool aItalic )
const
635 wxArrayString textLines;
638 aText = wxEmptyString;
640 for(
size_t ii = 0; ii < textLines.Count(); ++ii )
642 std::vector<std::pair<wxString, int>> markup;
643 std::vector<std::pair<wxString, int>> words;
647 for(
const auto& [ run, runWidth ] : markup )
649 if( !words.empty() && !words.back().first.EndsWith(
' ' ) )
651 words.back().first += run;
652 words.back().second += runWidth;
656 words.emplace_back( std::make_pair( run, runWidth ) );
660 bool buryMode =
false;
662 wxString pendingSpaces;
664 for(
const auto& [ word, wordWidth ] : words )
666 int pendingSpaceWidth = (int) pendingSpaces.Length() * spaceWidth;
667 bool overflow = lineWidth + pendingSpaceWidth + wordWidth > aColumnWidth - aThickness;
669 if( overflow && pendingSpaces.Length() > 0 )
673 pendingSpaces = wxEmptyString;
674 pendingSpaceWidth = 0;
678 if( word == wxS(
" " ) )
680 pendingSpaces += word;
690 aText += pendingSpaces;
691 lineWidth += pendingSpaceWidth;
694 if( word.EndsWith(
' ' ) )
696 aText += word.Left( word.Length() - 1 );
697 pendingSpaces = wxS(
" " );
702 pendingSpaces = wxEmptyString;
705 lineWidth += wordWidth;
710 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 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
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