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;
177 std::vector<VECTOR2I>& aPositions, std::vector<VECTOR2I>& aExtents,
181 int lineCount = aTextLines.Count();
182 aPositions.reserve( lineCount );
187 for(
int i = 0; i < lineCount; i++ )
189 VECTOR2I pos( aPosition.
x, aPosition.
y + i * interline );
194 aExtents.push_back( bBox );
197 height += ( aAttrs.
m_Size.
y * 1.17 );
220 for(
int i = 0; i < lineCount; i++ )
222 VECTOR2I lineSize = aExtents.at( i );
225 lineOffset.
y += i * interline;
235 aPositions.push_back( aPosition + lineOffset );
242 wxString* aActiveUrl )
const
244 if( !aGal || aText.empty() )
247 VECTOR2I position( aPosition - aCursor );
250 wxArrayString strings_list;
251 std::vector<VECTOR2I> positions;
252 std::vector<VECTOR2I> extents;
254 getLinePositions( aText, position, strings_list, positions, extents, aAttrs, aFontMetrics );
258 for(
size_t i = 0; i < strings_list.GetCount(); i++ )
262 aFontMetrics, aMousePos, aActiveUrl );
273 const METRICS& aFontMetrics, std::optional<VECTOR2I> aMousePos, wxString* aActiveUrl )
276 bool drawUnderline =
false;
277 bool drawOverbar =
false;
278 bool useHoverColor =
false;
279 int start = aGlyphs ? (int) aGlyphs->size() : 0;
286 if( !aNode->is_root() )
297 drawUnderline =
true;
299 if( aNode->has_content() )
304 nextPosition, aAngle, aMirror, aOrigin, textStyle );
307 aBoundingBox->
Merge( bbox );
309 if( aNode->
isURL() && aMousePos.has_value() && bbox.
Contains( aMousePos.value() ) )
311 useHoverColor =
true;
312 drawUnderline =
true;
317 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
319 nextPosition =
drawMarkup( aBoundingBox, aGlyphs, child.get(), nextPosition, aFont, aSize,
320 aAngle, aMirror, aOrigin, textStyle, aFontMetrics, aMousePos,
328 double barTrim = aSize.
x * 0.1;
331 VECTOR2D barStart( aPosition.
x + barTrim, aPosition.
y - barOffset );
332 VECTOR2D barEnd( nextPosition.
x - barTrim, nextPosition.
y - barOffset );
342 aGlyphs->push_back( barGlyph.
Transform( { 1.0, 1.0 }, { 0, 0 },
false, aAngle, aMirror, aOrigin ) );
349 double barTrim = aSize.
x * 0.1;
352 VECTOR2D barStart( aPosition.
x + barTrim, aPosition.
y - barOffset );
353 VECTOR2D barEnd( nextPosition.
x - barTrim, nextPosition.
y - barOffset );
363 aGlyphs->push_back( barGlyph.
Transform( { 1.0, 1.0 }, { 0, 0 },
false, aAngle, aMirror, aOrigin ) );
369 for(
int ii = start; ii < (int) aGlyphs->size(); ++ii )
370 (*aGlyphs)[ii]->SetIsHover(
true );
372 if( aActiveUrl && aActiveUrl->IsEmpty() )
384 std::optional<VECTOR2I> aMousePos, wxString* aActiveUrl )
const
390 if( !markup || !markup->
root )
400 wxASSERT( markup && markup->
root );
402 return ::drawMarkup( aBoundingBox, aGlyphs, markup->
root.get(), aPosition,
this, aSize, aAngle,
403 aMirror, aOrigin, aTextStyle, aFontMetrics, aMousePos, aActiveUrl );
409 bool aMirror,
const VECTOR2I& aOrigin,
bool aItalic,
bool aUnderline,
410 bool aHover,
const METRICS& aFontMetrics, std::optional<VECTOR2I> aMousePos,
411 wxString* aActiveUrl )
const
424 std::vector<std::unique_ptr<GLYPH>> glyphs;
426 (void)
drawMarkup( aBoundingBox, &glyphs, aText, aPosition, aSize, aAngle, aMirror, aOrigin,
427 textStyle, aFontMetrics, aMousePos, aActiveUrl );
431 for( std::unique_ptr<GLYPH>& glyph : glyphs )
432 glyph->SetIsHover(
true );
440 bool aBold,
bool aItalic,
const METRICS& aFontMetrics )
const
453 textStyle, aFontMetrics );
472 wxArrayString strings_list;
473 std::vector<VECTOR2I> positions;
474 std::vector<VECTOR2I> extents;
476 getLinePositions( aText, aAnchor, strings_list, positions, extents, aAttributes, aFontMetrics );
478 if( positions.empty() )
481 return positions.front();
486 const VECTOR2I& aSize,
bool aItalic,
const METRICS& aFontMetrics )
const
494 textStyle, aFontMetrics );
506void wordbreakMarkup( std::vector<std::pair<wxString, int>>* aWords,
const std::unique_ptr<MARKUP::NODE>& aNode,
508 bool aInsideMarkup =
false )
512 if( !aNode->is_root() )
514 wxChar escapeChar = 0;
516 if( aNode->isSubscript() )
521 else if( aNode->isSuperscript() )
527 if( aNode->isOverbar() )
535 wxString word = wxString::Format( wxT(
"%c{" ), escapeChar );
538 if( aNode->has_content() )
541 ANGLE_0,
false, { 0, 0 }, textStyle );
542 word += aNode->asWxString();
546 std::vector<std::pair<wxString, int>> childWords;
548 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
551 for(
const std::pair<wxString, int>& childWord : childWords )
553 word += childWord.first;
554 width += childWord.second;
558 aWords->emplace_back( std::make_pair( word, width ) );
561 else if( aInsideMarkup )
566 wxString content = aNode->asWxString();
568 { 0, 0 }, textStyle ).x;
570 aWords->emplace_back( std::make_pair( content, w ) );
574 wxString textRun = aNode->asWxString();
575 wxStringTokenizer tokenizer( textRun,
" ", wxTOKEN_RET_DELIMS );
576 std::vector<wxString> words;
578 while( tokenizer.HasMoreTokens() )
579 words.emplace_back( tokenizer.GetNextToken() );
581 for(
const wxString& word : words )
583 wxString chars = word;
586 int w = aFont->
GetTextAsGlyphs(
nullptr,
nullptr, chars.IsEmpty() ? word : chars, aSize,
587 { 0, 0 },
ANGLE_0,
false, { 0, 0 }, textStyle ).
x;
589 aWords->emplace_back( std::make_pair( word, w ) );
594 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
595 wordbreakMarkup( aWords, child, aFont, aSize, textStyle, aInsideMarkup );
603 std::unique_ptr<MARKUP::NODE> root = markupParser.
Parse();
610 bool aBold,
bool aItalic )
const
623 wxArrayString textLines;
626 aText = wxEmptyString;
628 for(
size_t ii = 0; ii < textLines.Count(); ++ii )
630 std::vector<std::pair<wxString, int>> markup;
631 std::vector<std::pair<wxString, int>> words;
635 for(
const auto& [ run, runWidth ] : markup )
637 if( !words.empty() && !words.back().first.EndsWith(
' ' ) )
639 words.back().first += run;
640 words.back().second += runWidth;
644 words.emplace_back( std::make_pair( run, runWidth ) );
648 bool buryMode =
false;
650 wxString pendingSpaces;
652 for(
const auto& [ word, wordWidth ] : words )
654 int pendingSpaceWidth = (int) pendingSpaces.Length() * spaceWidth;
655 bool overflow = lineWidth + pendingSpaceWidth + wordWidth > aColumnWidth - aThickness;
657 if( overflow && pendingSpaces.Length() > 0 )
661 pendingSpaces = wxEmptyString;
662 pendingSpaceWidth = 0;
666 if( word == wxS(
" " ) )
668 pendingSpaces += word;
678 aText += pendingSpaces;
679 lineWidth += pendingSpaceWidth;
682 if( word.EndsWith(
' ' ) )
684 aText += word.Left( word.Length() - 1 );
685 pendingSpaces = wxS(
" " );
690 pendingSpaces = wxEmptyString;
693 lineWidth += wordWidth;
698 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