KiCad PCB EDA Suite
Loading...
Searching...
No Matches
font.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KICAD, a free EDA CAD application.
3 *
4 * Copyright (C) 2021 Ola Rinta-Koski
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * Font abstract base class
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23#include <list>
24#include <mutex>
25#include <unordered_map>
26
27#include <macros.h>
28#include <string_utils.h>
30#include <font/glyph_types.h>
31#include <font/stroke_font.h>
32#include <font/outline_font.h>
33#include <trigo.h>
34#include <markup_parser.h>
35
36// The "official" name of the Kicad stroke font (always existing)
38#include <wx/tokenzr.h>
39
40// markup_parser.h includes pegtl.hpp which includes windows.h... which leaks #define DrawText
41#undef DrawText
42
43
44using namespace KIFONT;
45
47
48
50{
51 return g_defaultMetrics;
52}
53
54
55FONT* FONT::s_defaultFont = nullptr;
56
57std::map< std::tuple<wxString, bool, bool, bool>, FONT*> FONT::s_fontMap;
58
60{
61public:
62 struct ENTRY
63 {
64 std::string source;
65 std::unique_ptr<MARKUP::NODE> root;
66 };
67
68 MARKUP_CACHE( size_t aMaxSize ) :
69 m_maxSize( aMaxSize )
70 {
71 }
72
73 ENTRY& Put( const wxString& aQuery, ENTRY&& aResult )
74 {
75 auto it = m_cache.find( aQuery );
76
77 m_cacheMru.emplace_front( std::make_pair( aQuery, std::move( aResult ) ) );
78
79 if( it != m_cache.end() )
80 {
81 m_cacheMru.erase( it->second );
82 m_cache.erase( it );
83 }
84
85 m_cache[aQuery] = m_cacheMru.begin();
86
87 if( m_cache.size() > m_maxSize )
88 {
89 auto last = m_cacheMru.end();
90 last--;
91 m_cache.erase( last->first );
92 m_cacheMru.pop_back();
93 }
94
95 return m_cacheMru.begin()->second;
96 }
97
98 ENTRY* Get( const wxString& aQuery )
99 {
100 auto it = m_cache.find( aQuery );
101
102 if( it == m_cache.end() )
103 return nullptr;
104
105 m_cacheMru.splice( m_cacheMru.begin(), m_cacheMru, it->second );
106
107 return &m_cacheMru.begin()->second;
108 }
109
110 void Clear()
111 {
112 m_cacheMru.clear();
113 m_cache.clear();
114 }
115
116private:
117 size_t m_maxSize;
118 std::list<std::pair<wxString, ENTRY>> m_cacheMru;
119 std::unordered_map<wxString, std::list<std::pair<wxString, ENTRY>>::iterator> m_cache;
120};
121
122
124static std::mutex s_markupCacheMutex;
125static std::mutex s_defaultFontMutex;;
126
127
129{
130}
131
132
134{
135 std::lock_guard lock( s_defaultFontMutex );
136
137 if( !s_defaultFont )
138 s_defaultFont = STROKE_FONT::LoadFont( wxEmptyString );
139
140 return s_defaultFont;
141}
142
143
144FONT* FONT::GetFont( const wxString& aFontName, bool aBold, bool aItalic,
145 const std::vector<wxString>* aEmbeddedFiles, bool aForDrawingSheet )
146{
147 static const wxString s_kicadFontName( KICAD_FONT_NAME );
148
149 if( aFontName.empty() || aFontName.StartsWith( s_kicadFontName ) )
150 return getDefaultFont();
151
152 std::tuple<wxString, bool, bool, bool> key = { aFontName, aBold, aItalic, aForDrawingSheet };
153
154 FONT* font = nullptr;
155
156 if( s_fontMap.find( key ) != s_fontMap.end() )
157 font = s_fontMap[key];
158
159 if( !font )
160 font = OUTLINE_FONT::LoadFont( aFontName, aBold, aItalic, aEmbeddedFiles, aForDrawingSheet );
161
162 if( !font )
163 font = getDefaultFont();
164
165 s_fontMap[key] = font;
166
167 return font;
168}
169
170
171bool FONT::IsStroke( const wxString& aFontName )
172{
173 // This would need a more complex implementation if we ever support more stroke fonts
174 // than the KiCad Font.
175 return aFontName == _( "Default Font" ) || aFontName == KICAD_FONT_NAME;
176}
177
178
179void FONT::getLinePositions( const wxString& aText, const VECTOR2I& aPosition, wxArrayString& aTextLines,
180 std::vector<VECTOR2I>& aPositions, std::vector<VECTOR2I>& aExtents,
181 const TEXT_ATTRIBUTES& aAttrs, const METRICS& aFontMetrics ) const
182{
183 wxStringSplit( aText, aTextLines, '\n' );
184 int lineCount = aTextLines.Count();
185 aPositions.reserve( lineCount );
186
187 int interline = GetInterline( aAttrs.m_Size.y, aFontMetrics ) * aAttrs.m_LineSpacing;
188 int height = 0;
189
190 for( int i = 0; i < lineCount; i++ )
191 {
192 VECTOR2I pos( aPosition.x, aPosition.y + i * interline );
193 VECTOR2I end = boundingBoxSingleLine( nullptr, aTextLines[i], pos, aAttrs.m_Size, aAttrs.m_Italic,
194 aFontMetrics );
195 VECTOR2I bBox( end - pos );
196
197 aExtents.push_back( bBox );
198
199 if( i == 0 )
200 height += ( aAttrs.m_Size.y * 1.17 ); // 1.17 is a fudge to match 6.0 positioning
201 else
202 height += interline;
203 }
204
205 VECTOR2I offset( 0, 0 );
206 offset.y += aAttrs.m_Size.y;
207
208 if( IsStroke() )
209 {
210 // Fudge factors to match 6.0 positioning
211 offset.x += aAttrs.m_StrokeWidth / 1.52;
212 offset.y -= aAttrs.m_StrokeWidth * 0.052;
213 }
214
215 switch( aAttrs.m_Valign )
216 {
217 case GR_TEXT_V_ALIGN_TOP: break;
218 case GR_TEXT_V_ALIGN_CENTER: offset.y -= height / 2; break;
219 case GR_TEXT_V_ALIGN_BOTTOM: offset.y -= height; break;
220 case GR_TEXT_V_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) ); break;
221 }
222
223 for( int i = 0; i < lineCount; i++ )
224 {
225 VECTOR2I lineSize = aExtents.at( i );
226 VECTOR2I lineOffset( offset );
227
228 lineOffset.y += i * interline;
229
230 switch( aAttrs.m_Halign )
231 {
232 case GR_TEXT_H_ALIGN_LEFT: break;
233 case GR_TEXT_H_ALIGN_CENTER: lineOffset.x = -lineSize.x / 2; break;
234 case GR_TEXT_H_ALIGN_RIGHT: lineOffset.x = -( lineSize.x + offset.x ); break;
235 case GR_TEXT_H_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) ); break;
236 }
237
238 aPositions.push_back( aPosition + lineOffset );
239 }
240}
241
242
243void FONT::Draw( KIGFX::GAL* aGal, const wxString& aText, const VECTOR2I& aPosition, const VECTOR2I& aCursor,
244 const TEXT_ATTRIBUTES& aAttrs, const METRICS& aFontMetrics, std::optional<VECTOR2I> aMousePos,
245 wxString* aActiveUrl ) const
246{
247 if( !aGal || aText.empty() )
248 return;
249
250 VECTOR2I position( aPosition - aCursor );
251
252 // Split multiline strings into separate ones and draw them line by line
253 wxArrayString strings_list;
254 std::vector<VECTOR2I> positions;
255 std::vector<VECTOR2I> extents;
256
257 getLinePositions( aText, position, strings_list, positions, extents, aAttrs, aFontMetrics );
258
259 aGal->SetLineWidth( aAttrs.m_StrokeWidth );
260
261 for( size_t i = 0; i < strings_list.GetCount(); i++ )
262 {
263 drawSingleLineText( aGal, nullptr, strings_list[i], positions[i], aAttrs.m_Size, aAttrs.m_Angle.GetAngle(),
264 aAttrs.m_Mirrored, aPosition, aAttrs.m_Italic, aAttrs.m_Underlined, aAttrs.m_Hover,
265 aFontMetrics, aMousePos, aActiveUrl );
266 }
267}
268
269
273VECTOR2I drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>* aGlyphs, const MARKUP::NODE* aNode,
274 const VECTOR2I& aPosition, const KIFONT::FONT* aFont, const VECTOR2I& aSize,
275 const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin, TEXT_STYLE_FLAGS aTextStyle,
276 const METRICS& aFontMetrics, std::optional<VECTOR2I> aMousePos, wxString* aActiveUrl )
277{
278 VECTOR2I nextPosition = aPosition;
279 bool drawUnderline = false;
280 bool drawOverbar = false;
281 bool useHoverColor = false;
282 int start = aGlyphs ? (int) aGlyphs->size() : 0;
283
284
285 if( aNode )
286 {
287 TEXT_STYLE_FLAGS textStyle = aTextStyle;
288
289 if( !aNode->is_root() )
290 {
291 if( aNode->isSubscript() )
292 textStyle |= TEXT_STYLE::SUBSCRIPT;
293 else if( aNode->isSuperscript() )
294 textStyle |= TEXT_STYLE::SUPERSCRIPT;
295
296 if( aNode->isOverbar() )
297 drawOverbar = true;
298
299 if( aTextStyle & TEXT_STYLE::UNDERLINE )
300 drawUnderline = true;
301
302 if( aNode->has_content() )
303 {
304 BOX2I bbox;
305
306 nextPosition = aFont->GetTextAsGlyphs( &bbox, aGlyphs, aNode->asWxString(), aSize,
307 nextPosition, aAngle, aMirror, aOrigin, textStyle );
308
309 if( aBoundingBox )
310 aBoundingBox->Merge( bbox );
311
312 if( aNode->isURL() && aMousePos.has_value() && bbox.Contains( aMousePos.value() ) )
313 {
314 useHoverColor = true;
315 drawUnderline = true;
316 }
317 }
318 }
319
320 for( const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
321 {
322 nextPosition = drawMarkup( aBoundingBox, aGlyphs, child.get(), nextPosition, aFont, aSize,
323 aAngle, aMirror, aOrigin, textStyle, aFontMetrics, aMousePos,
324 aActiveUrl );
325 }
326 }
327
328 if( drawUnderline )
329 {
330 // Shorten the bar a little so its rounded ends don't make it over-long
331 double barTrim = aSize.x * 0.1;
332 double barOffset = aFontMetrics.GetUnderlineVerticalPosition( aSize.y );
333
334 VECTOR2D barStart( aPosition.x + barTrim, aPosition.y - barOffset );
335 VECTOR2D barEnd( nextPosition.x - barTrim, nextPosition.y - barOffset );
336
337 if( aGlyphs )
338 {
339 STROKE_GLYPH barGlyph;
340
341 barGlyph.AddPoint( barStart );
342 barGlyph.AddPoint( barEnd );
343 barGlyph.Finalize();
344
345 aGlyphs->push_back( barGlyph.Transform( { 1.0, 1.0 }, { 0, 0 }, false, aAngle, aMirror, aOrigin ) );
346 }
347 }
348
349 if( drawOverbar )
350 {
351 // Shorten the bar a little so its rounded ends don't make it over-long
352 double barTrim = aSize.x * 0.1;
353 double barOffset = aFontMetrics.GetOverbarVerticalPosition( aSize.y );
354
355 VECTOR2D barStart( aPosition.x + barTrim, aPosition.y - barOffset );
356 VECTOR2D barEnd( nextPosition.x - barTrim, nextPosition.y - barOffset );
357
358 if( aGlyphs )
359 {
360 STROKE_GLYPH barGlyph;
361
362 barGlyph.AddPoint( barStart );
363 barGlyph.AddPoint( barEnd );
364 barGlyph.Finalize();
365
366 aGlyphs->push_back( barGlyph.Transform( { 1.0, 1.0 }, { 0, 0 }, false, aAngle, aMirror, aOrigin ) );
367 }
368 }
369
370 if( useHoverColor )
371 {
372 for( int ii = start; ii < (int) aGlyphs->size(); ++ii )
373 (*aGlyphs)[ii]->SetIsHover( true );
374
375 if( aActiveUrl && aActiveUrl->IsEmpty() )
376 *aActiveUrl = aNode->asWxString();
377 }
378
379 return nextPosition;
380}
381
382
383VECTOR2I FONT::drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
384 const wxString& aText, const VECTOR2I& aPosition, const VECTOR2I& aSize,
385 const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
386 TEXT_STYLE_FLAGS aTextStyle, const METRICS& aFontMetrics,
387 std::optional<VECTOR2I> aMousePos, wxString* aActiveUrl ) const
388{
389 std::lock_guard<std::mutex> lock( s_markupCacheMutex );
390
391 MARKUP_CACHE::ENTRY* markup = s_markupCache.Get( aText );
392
393 if( !markup || !markup->root )
394 {
395 MARKUP_CACHE::ENTRY& cached = s_markupCache.Put( aText, {} );
396
397 cached.source = TO_UTF8( aText );
398 MARKUP::MARKUP_PARSER markupParser( &cached.source );
399 cached.root = markupParser.Parse();
400 markup = &cached;
401 }
402
403 wxASSERT( markup && markup->root );
404
405 return ::drawMarkup( aBoundingBox, aGlyphs, markup->root.get(), aPosition, this, aSize, aAngle,
406 aMirror, aOrigin, aTextStyle, aFontMetrics, aMousePos, aActiveUrl );
407}
408
409
410void FONT::drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const wxString& aText,
411 const VECTOR2I& aPosition, const VECTOR2I& aSize, const EDA_ANGLE& aAngle,
412 bool aMirror, const VECTOR2I& aOrigin, bool aItalic, bool aUnderline,
413 bool aHover, const METRICS& aFontMetrics, std::optional<VECTOR2I> aMousePos,
414 wxString* aActiveUrl ) const
415{
416 if( !aGal )
417 return;
418
419 TEXT_STYLE_FLAGS textStyle = 0;
420
421 if( aItalic )
422 textStyle |= TEXT_STYLE::ITALIC;
423
424 if( aUnderline )
425 textStyle |= TEXT_STYLE::UNDERLINE;
426
427 std::vector<std::unique_ptr<GLYPH>> glyphs;
428
429 (void) drawMarkup( aBoundingBox, &glyphs, aText, aPosition, aSize, aAngle, aMirror, aOrigin,
430 textStyle, aFontMetrics, aMousePos, aActiveUrl );
431
432 if( aHover )
433 {
434 for( std::unique_ptr<GLYPH>& glyph : glyphs )
435 glyph->SetIsHover( true );
436 }
437
438 aGal->DrawGlyphs( glyphs );
439}
440
441
442VECTOR2I FONT::StringBoundaryLimits( const wxString& aText, const VECTOR2I& aSize, int aThickness,
443 bool aBold, bool aItalic, const METRICS& aFontMetrics ) const
444{
445 // TODO do we need to parse every time - have we already parsed?
447 TEXT_STYLE_FLAGS textStyle = 0;
448
449 if( aBold )
450 textStyle |= TEXT_STYLE::BOLD;
451
452 if( aItalic )
453 textStyle |= TEXT_STYLE::ITALIC;
454
455 (void) drawMarkup( &boundingBox, nullptr, aText, VECTOR2I(), aSize, ANGLE_0, false, VECTOR2I(),
456 textStyle, aFontMetrics );
457
458 if( IsStroke() )
459 {
460 // Inflate by a bit more than thickness/2 to catch diacriticals, descenders, etc.
461 boundingBox.Inflate( KiROUND( aThickness * 1.5 ) );
462 }
463 else if( IsOutline() )
464 {
465 // Outline fonts have thickness built in, and *usually* stay within their ascent/descent
466 }
467
468 return boundingBox.GetSize();
469}
470
471
472VECTOR2I FONT::GetAlignedDrawPosition( const wxString& aText, const VECTOR2I& aAnchor,
473 const TEXT_ATTRIBUTES& aAttributes, const METRICS& aFontMetrics ) const
474{
475 wxArrayString strings_list;
476 std::vector<VECTOR2I> positions;
477 std::vector<VECTOR2I> extents;
478
479 getLinePositions( aText, aAnchor, strings_list, positions, extents, aAttributes, aFontMetrics );
480
481 if( positions.empty() )
482 return aAnchor;
483
484 return positions.front();
485}
486
487
488VECTOR2I FONT::boundingBoxSingleLine( BOX2I* aBBox, const wxString& aText, const VECTOR2I& aPosition,
489 const VECTOR2I& aSize, bool aItalic, const METRICS& aFontMetrics ) const
490{
491 TEXT_STYLE_FLAGS textStyle = 0;
492
493 if( aItalic )
494 textStyle |= TEXT_STYLE::ITALIC;
495
496 VECTOR2I extents = drawMarkup( aBBox, nullptr, aText, aPosition, aSize, ANGLE_0, false, VECTOR2I(),
497 textStyle, aFontMetrics );
498
499 return extents;
500}
501
502
509void wordbreakMarkup( std::vector<std::pair<wxString, int>>* aWords, const std::unique_ptr<MARKUP::NODE>& aNode,
510 const KIFONT::FONT* aFont, const VECTOR2I& aSize, TEXT_STYLE_FLAGS aTextStyle,
511 bool aInsideMarkup = false )
512{
513 TEXT_STYLE_FLAGS textStyle = aTextStyle;
514
515 if( !aNode->is_root() )
516 {
517 wxChar escapeChar = 0;
518
519 if( aNode->isSubscript() )
520 {
521 escapeChar = '_';
522 textStyle = TEXT_STYLE::SUBSCRIPT;
523 }
524 else if( aNode->isSuperscript() )
525 {
526 escapeChar = '^';
527 textStyle = TEXT_STYLE::SUPERSCRIPT;
528 }
529
530 if( aNode->isOverbar() )
531 {
532 escapeChar = '~';
533 textStyle |= TEXT_STYLE::OVERBAR;
534 }
535
536 if( escapeChar )
537 {
538 wxString word = wxString::Format( wxT( "%c{" ), escapeChar );
539 int width = 0;
540
541 if( aNode->has_content() )
542 {
543 VECTOR2I next = aFont->GetTextAsGlyphs( nullptr, nullptr, aNode->asWxString(), aSize, { 0, 0 },
544 ANGLE_0, false, { 0, 0 }, textStyle );
545 word += aNode->asWxString();
546 width += next.x;
547 }
548
549 std::vector<std::pair<wxString, int>> childWords;
550
551 for( const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
552 wordbreakMarkup( &childWords, child, aFont, aSize, textStyle, true );
553
554 for( const std::pair<wxString, int>& childWord : childWords )
555 {
556 word += childWord.first;
557 width += childWord.second;
558 }
559
560 word += wxT( "}" );
561 aWords->emplace_back( std::make_pair( word, width ) );
562 return;
563 }
564 else if( aInsideMarkup )
565 {
566 // Inside a markup node, preserve the content verbatim (no tokenization).
567 // wxStringTokenizer collapses consecutive spaces, which corrupts content
568 // like ~{ } where spaces are intentional.
569 wxString content = aNode->asWxString();
570 int w = aFont->GetTextAsGlyphs( nullptr, nullptr, content, aSize, { 0, 0 }, ANGLE_0, false,
571 { 0, 0 }, textStyle ).x;
572
573 aWords->emplace_back( std::make_pair( content, w ) );
574 }
575 else
576 {
577 wxString textRun = aNode->asWxString();
578 wxStringTokenizer tokenizer( textRun, " ", wxTOKEN_RET_DELIMS );
579 std::vector<wxString> words;
580
581 while( tokenizer.HasMoreTokens() )
582 words.emplace_back( tokenizer.GetNextToken() );
583
584 for( const wxString& word : words )
585 {
586 wxString chars = word;
587 chars.Trim();
588
589 int w = aFont->GetTextAsGlyphs( nullptr, nullptr, chars.IsEmpty() ? word : chars, aSize,
590 { 0, 0 }, ANGLE_0, false, { 0, 0 }, textStyle ).x;
591
592 aWords->emplace_back( std::make_pair( word, w ) );
593 }
594 }
595 }
596
597 for( const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
598 wordbreakMarkup( aWords, child, aFont, aSize, textStyle, aInsideMarkup );
599}
600
601
602void FONT::wordbreakMarkup( std::vector<std::pair<wxString, int>>* aWords, const wxString& aText,
603 const VECTOR2I& aSize, TEXT_STYLE_FLAGS aTextStyle ) const
604{
605 MARKUP::MARKUP_PARSER markupParser( TO_UTF8( aText ) );
606 std::unique_ptr<MARKUP::NODE> root = markupParser.Parse();
607
608 ::wordbreakMarkup( aWords, root, this, aSize, aTextStyle );
609}
610
611
612void FONT::LinebreakText( wxString& aText, int aColumnWidth, const VECTOR2I& aSize, int aThickness,
613 bool aBold, bool aItalic ) const
614{
615 TEXT_STYLE_FLAGS textStyle = 0;
616
617 if( aBold )
618 textStyle |= TEXT_STYLE::BOLD;
619
620 if( aItalic )
621 textStyle |= TEXT_STYLE::ITALIC;
622
623 int spaceWidth = GetTextAsGlyphs( nullptr, nullptr, wxS( " " ), aSize, VECTOR2I(), ANGLE_0, false,
624 VECTOR2I(), textStyle ).x;
625
626 wxArrayString textLines;
627 wxStringSplit( aText, textLines, '\n' );
628
629 aText = wxEmptyString;
630
631 for( size_t ii = 0; ii < textLines.Count(); ++ii )
632 {
633 std::vector<std::pair<wxString, int>> markup;
634 std::vector<std::pair<wxString, int>> words;
635
636 wordbreakMarkup( &markup, textLines[ii], aSize, textStyle );
637
638 for( const auto& [ run, runWidth ] : markup )
639 {
640 if( !words.empty() && !words.back().first.EndsWith( ' ' ) )
641 {
642 words.back().first += run;
643 words.back().second += runWidth;
644 }
645 else
646 {
647 words.emplace_back( std::make_pair( run, runWidth ) );
648 }
649 }
650
651 bool buryMode = false;
652 int lineWidth = 0;
653 wxString pendingSpaces;
654
655 for( const auto& [ word, wordWidth ] : words )
656 {
657 int pendingSpaceWidth = (int) pendingSpaces.Length() * spaceWidth;
658 bool overflow = lineWidth + pendingSpaceWidth + wordWidth > aColumnWidth - aThickness;
659
660 if( overflow && pendingSpaces.Length() > 0 )
661 {
662 aText += '\n';
663 lineWidth = 0;
664 pendingSpaces = wxEmptyString;
665 pendingSpaceWidth = 0;
666 buryMode = true;
667 }
668
669 if( word == wxS( " " ) )
670 {
671 pendingSpaces += word;
672 }
673 else
674 {
675 if( buryMode )
676 {
677 buryMode = false;
678 }
679 else
680 {
681 aText += pendingSpaces;
682 lineWidth += pendingSpaceWidth;
683 }
684
685 if( word.EndsWith( ' ' ) )
686 {
687 aText += word.Left( word.Length() - 1 );
688 pendingSpaces = wxS( " " );
689 }
690 else
691 {
692 aText += word;
693 pendingSpaces = wxEmptyString;
694 }
695
696 lineWidth += wordWidth;
697 }
698 }
699
700 // Add the newlines back onto the string
701 if( ii != ( textLines.Count() - 1 ) )
702 aText += '\n';
703 }
704}
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:594
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:165
EDA_ANGLE GetAngle() const
Definition eda_angle.h:533
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:94
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.
Definition font.cpp:488
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
Definition font.cpp:144
virtual bool IsStroke() const
Definition font.h:101
static FONT * s_defaultFont
Definition font.h:289
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
Definition font.cpp:179
void wordbreakMarkup(std::vector< std::pair< wxString, int > > *aWords, const wxString &aText, const VECTOR2I &aSize, TEXT_STYLE_FLAGS aTextStyle) const
Definition font.cpp:602
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.
Definition font.cpp:243
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.
Definition font.cpp:410
virtual bool IsOutline() const
Definition font.h:102
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 ...
Definition font.cpp:472
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
Definition font.cpp:383
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).
Definition font.cpp:442
static std::map< std::tuple< wxString, bool, bool, bool >, FONT * > s_fontMap
Definition font.h:291
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()
Definition font.cpp:133
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.
Definition font.cpp:612
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()
Definition font.cpp:49
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)
Definition glyph.cpp:40
std::unique_ptr< GLYPH > Transform(const VECTOR2D &aGlyphSize, const VECTOR2I &aOffset, double aTilt, const EDA_ANGLE &aAngle, bool aMirror, const VECTOR2I &aOrigin)
Definition glyph.cpp:75
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
Definition font.cpp:118
MARKUP_CACHE(size_t aMaxSize)
Definition font.cpp:68
ENTRY & Put(const wxString &aQuery, ENTRY &&aResult)
Definition font.cpp:73
void Clear()
Definition font.cpp:110
size_t m_maxSize
Definition font.cpp:117
ENTRY * Get(const wxString &aQuery)
Definition font.cpp:98
std::unordered_map< wxString, std::list< std::pair< wxString, ENTRY > >::iterator > m_cache
Definition font.cpp:119
EDA_ORIENTATION m_Angle
GR_TEXT_H_ALIGN_T m_Halign
GR_TEXT_V_ALIGN_T m_Valign
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
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".
Definition font.cpp:509
METRICS g_defaultMetrics
Definition font.cpp:46
static std::mutex s_markupCacheMutex
Definition font.cpp:124
static std::mutex s_defaultFontMutex
Definition font.cpp:125
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)
Definition font.cpp:273
@ BOLD
Definition font.h:43
@ SUBSCRIPT
Definition font.h:45
@ OVERBAR
Definition font.h:47
@ UNDERLINE
Definition font.h:48
@ ITALIC
Definition font.h:44
@ SUPERSCRIPT
Definition font.h:46
unsigned int TEXT_STYLE_FLAGS
Definition font.h:61
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition idf_helpers.h:37
#define KICAD_FONT_NAME
This file contains miscellaneous commonly used macros and functions.
CITER next(CITER it)
Definition ptree.cpp:120
BOX2I boundingBox(T aObject, int aLayer)
Used by SHAPE_INDEX to get the bounding box of a generic T object.
Definition shape_index.h:61
void wxStringSplit(const wxString &aText, wxArrayString &aStrings, wxChar aSplitter)
Split aString to a string list separated at aSplitter.
bool isOverbar() const
bool isURL() const
bool isSuperscript() const
wxString asWxString() const
bool isSubscript() const
std::unique_ptr< MARKUP::NODE > root
Definition font.cpp:65
std::string source
Definition font.cpp:64
VECTOR2I end
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_H_ALIGN_INDETERMINATE
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_INDETERMINATE
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707