KiCad PCB EDA Suite
Loading...
Searching...
No Matches
stroke_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) 2012 Torsten Hueter, torstenhtr <at> gmx.de
5 * Copyright (C) 2013 CERN
6 * @author Maciej Suminski <[email protected]>
7 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
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
24#include <wx/string.h>
25#include <wx/textfile.h>
26#include <newstroke_font.h>
27#include <font/glyph.h>
28#include <font/stroke_font.h>
30#include <trigo.h>
31
32// The "official" name of the building Kicad stroke font (always existing)
34
35#include <mutex>
36
37using namespace KIFONT;
38
39
41static constexpr double STROKE_FONT_SCALE = 1.0 / 21.0;
42
44static constexpr int FONT_OFFSET = -8;
45
46
48std::vector<std::shared_ptr<GLYPH>> g_defaultFontGlyphs;
51
52
54 m_glyphs( nullptr ),
55 m_glyphBoundingBoxes( nullptr ),
56 m_maxGlyphWidth( 0.0 )
57{
58}
59
60
61STROKE_FONT* STROKE_FONT::LoadFont( const wxString& aFontName )
62{
63 if( aFontName.empty() )
64 {
65 STROKE_FONT* font = new STROKE_FONT();
67 return font;
68 }
69 else
70 {
71 // If we ever supported other stroke fonts, the code would go here.
72 return nullptr;
73 }
74}
75
76
77void buildGlyphBoundingBox( std::shared_ptr<STROKE_GLYPH>& aGlyph, double aGlyphWidth )
78{
79 VECTOR2D min( 0, 0 );
80 VECTOR2D max( aGlyphWidth, 0 );
81
82 for( const std::vector<VECTOR2D>& pointList : *aGlyph )
83 {
84 for( const VECTOR2D& point : pointList )
85 {
86 min.y = std::min( min.y, point.y );
87 max.y = std::max( max.y, point.y );
88 }
89 }
90
91 aGlyph->SetBoundingBox( BOX2D( min, max - min ) );
92}
93
94
95void STROKE_FONT::loadNewStrokeFont( const char* const aNewStrokeFont[], int aNewStrokeFontSize )
96{
97 // Protect the initialization sequence against multiple entries
98 std::lock_guard<std::mutex> lock( g_defaultFontLoadMutex );
99
101 {
102 g_defaultFontGlyphs.reserve( aNewStrokeFontSize );
103
104 g_defaultFontGlyphBoundingBoxes = new std::vector<BOX2D>;
105 g_defaultFontGlyphBoundingBoxes->reserve( aNewStrokeFontSize );
106
107 for( int j = 0; j < aNewStrokeFontSize; j++ )
108 {
109 std::shared_ptr<STROKE_GLYPH> glyph = std::make_shared<STROKE_GLYPH>();
110
111 double glyphStartX = 0.0;
112 double glyphEndX = 0.0;
113 double glyphWidth = 0.0;
114 int strokes = 0;
115 int i = 0;
116
117 while( aNewStrokeFont[j][i] )
118 {
119 if( aNewStrokeFont[j][i] == ' ' && aNewStrokeFont[j][i+1] == 'R' )
120 strokes++;
121
122 i += 2;
123 }
124
125 glyph->reserve( strokes + 1 );
126
127 i = 0;
128
129 while( aNewStrokeFont[j][i] )
130 {
131 VECTOR2D point( 0.0, 0.0 );
132 char coordinate[2] = { 0, };
133
134 for( int k : { 0, 1 } )
135 coordinate[k] = aNewStrokeFont[j][i + k];
136
137 if( i < 2 )
138 {
139 // The first two values contain the width of the char
140 glyphStartX = ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE;
141 glyphEndX = ( coordinate[1] - 'R' ) * STROKE_FONT_SCALE;
142 glyphWidth = glyphEndX - glyphStartX;
143 }
144 else if( ( coordinate[0] == ' ' ) && ( coordinate[1] == 'R' ) )
145 {
146 glyph->RaisePen();
147 }
148 else
149 {
150 // In stroke font, coordinates values are coded as <value> + 'R', where
151 // <value> is an ASCII char.
152 // therefore every coordinate description of the Hershey format has an offset,
153 // it has to be subtracted
154 // Note:
155 // * the stroke coordinates are stored in reduced form (-1.0 to +1.0),
156 // and the actual size is stroke coordinate * glyph size
157 // * a few shapes have a height slightly bigger than 1.0 ( like '{' '[' )
158 point.x = (double) ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE - glyphStartX;
159
160 // FONT_OFFSET is here for historical reasons, due to the way the stroke font
161 // was built. It allows shapes coordinates like W M ... to be >= 0
162 // Only shapes like j y have coordinates < 0
163 point.y = (double) ( coordinate[1] - 'R' + FONT_OFFSET ) * STROKE_FONT_SCALE;
164
165 glyph->AddPoint( point );
166 }
167
168 i += 2;
169 }
170
171 glyph->Finalize();
172
173 // Compute the bounding box of the glyph
174 buildGlyphBoundingBox( glyph, glyphWidth );
175 g_defaultFontGlyphBoundingBoxes->emplace_back( glyph->BoundingBox() );
176 g_defaultFontGlyphs.push_back( glyph );
177 m_maxGlyphWidth = std::max( m_maxGlyphWidth, glyphWidth );
178 }
179
181 }
182
186 m_fontFileName = wxEmptyString;
187}
188
189
190double STROKE_FONT::GetInterline( double aGlyphHeight, const METRICS& aFontMetrics ) const
191{
192 static double LEGACY_FACTOR = 0.9583; // Adjustment to match legacy spacing
193
194 return aFontMetrics.GetInterline( aGlyphHeight ) * LEGACY_FACTOR;
195}
196
197
198VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
199 const wxString& aText, const VECTOR2I& aSize,
200 const VECTOR2I& aPosition, const EDA_ANGLE& aAngle,
201 bool aMirror, const VECTOR2I& aOrigin,
202 TEXT_STYLE_FLAGS aTextStyle ) const
203{
204 constexpr int TAB_WIDTH = 4;
205 constexpr double INTER_CHAR = 0.2;
206 constexpr double SUPER_SUB_SIZE_MULTIPLIER = 0.8;
207 constexpr double SUPER_HEIGHT_OFFSET = 0.35;
208 constexpr double SUB_HEIGHT_OFFSET = 0.15;
209
210 VECTOR2I cursor( aPosition );
211 VECTOR2D glyphSize( aSize );
212 double tilt = ( aTextStyle & TEXT_STYLE::ITALIC ) ? ITALIC_TILT : 0.0;
213 double space_width = m_glyphBoundingBoxes->front().GetWidth(); // First char is space
214 int char_count = 0;
215
216 if( aTextStyle & TEXT_STYLE::SUBSCRIPT || aTextStyle & TEXT_STYLE::SUPERSCRIPT )
217 {
218 glyphSize = glyphSize * SUPER_SUB_SIZE_MULTIPLIER;
219
220 if( aTextStyle & TEXT_STYLE::SUBSCRIPT )
221 cursor.y += glyphSize.y * SUB_HEIGHT_OFFSET;
222 else
223 cursor.y -= glyphSize.y * SUPER_HEIGHT_OFFSET;
224 }
225
226 for( wxUniChar c : aText )
227 {
228 // Handle tabs as locked to the next 4th column (in base-widths).
229 if( c == '\t' )
230 {
231 char_count = ( char_count / TAB_WIDTH + 1 ) * TAB_WIDTH - 1;
232
233 int new_cursor = aPosition.x + aSize.x * char_count
234 + aSize.x * space_width;
235
236 while( new_cursor <= cursor.x )
237 {
238 char_count += TAB_WIDTH;
239 new_cursor += aSize.x * TAB_WIDTH;
240 }
241
242 cursor.x = new_cursor;
243 }
244 else if( c == ' ' )
245 {
246 // 'space' character - draw nothing, advance cursor position
247 cursor.x += KiROUND( glyphSize.x * space_width );
248 }
249 else
250 {
251 // dd is the index into bounding boxes table
252 int dd = (signed) c - ' ';
253
254 // Filtering non existing glyphs and non printable chars
255 if( dd < 0 || dd >= (int) m_glyphBoundingBoxes->size() )
256 {
257 c = '?';
258 dd = (signed) c - ' ';
259 }
260
261 STROKE_GLYPH* source = static_cast<STROKE_GLYPH*>( m_glyphs->at( dd ).get() );
262
263 if( aGlyphs )
264 {
265 aGlyphs->push_back( source->Transform( glyphSize, cursor, tilt, aAngle, aMirror,
266 aOrigin ) );
267 }
268
269 VECTOR2D glyphExtents = source->BoundingBox().GetEnd();
270
271 glyphExtents *= glyphSize;
272
273 cursor.x += KiROUND( glyphExtents.x );
274 }
275
276 ++char_count;
277 }
278
279 if( aBBox )
280 {
281 aBBox->SetOrigin( aPosition );
282 aBBox->SetEnd( cursor.x - KiROUND( glyphSize.x * INTER_CHAR ), cursor.y - glyphSize.y );
283 aBBox->Normalize();
284 }
285
286 return VECTOR2I( cursor.x, aPosition.y );
287}
288
289
291{
292 return m_glyphs ? m_glyphs->size() : 0;
293}
294
295
296const STROKE_GLYPH* STROKE_FONT::GetGlyph( unsigned aIndex ) const
297{
298 if( !m_glyphs || aIndex >= m_glyphs->size() )
299 return nullptr;
300
301 return static_cast<const STROKE_GLYPH*>( m_glyphs->at( aIndex ).get() );
302}
303
304
305const BOX2D& STROKE_FONT::GetGlyphBoundingBox( unsigned aIndex ) const
306{
307 static const BOX2D empty;
308
309 if( !m_glyphBoundingBoxes || aIndex >= m_glyphBoundingBoxes->size() )
310 return empty;
311
312 return m_glyphBoundingBoxes->at( aIndex );
313}
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
BOX2< VECTOR2D > BOX2D
Definition box2.h:919
constexpr const Vec GetEnd() const
Definition box2.h:208
constexpr void SetOrigin(const Vec &pos)
Definition box2.h:233
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:142
constexpr void SetEnd(coord_type x, coord_type y)
Definition box2.h:293
wxString m_fontName
Font name.
Definition font.h:262
wxString m_fontFileName
Font file name.
Definition font.h:263
double GetInterline(double aFontHeight) const
double GetInterline(double aGlyphHeight, const METRICS &aFontMetrics) const override
Compute the distance (interline) between 2 lines of text (for multiline texts).
void loadNewStrokeFont(const char *const aNewStrokeFont[], int aNewStrokeFontSize)
Load the standard KiCad stroke font.
const STROKE_GLYPH * GetGlyph(unsigned aIndex) const
const BOX2D & GetGlyphBoundingBox(unsigned aIndex) const
const std::vector< BOX2D > * m_glyphBoundingBoxes
Definition stroke_font.h:93
const std::vector< std::shared_ptr< GLYPH > > * m_glyphs
Definition stroke_font.h:92
static STROKE_FONT * LoadFont(const wxString &aFontName)
Load a stroke font.
unsigned GetGlyphCount() const
VECTOR2I GetTextAsGlyphs(BOX2I *aBoundingBox, 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 override
Convert text string to an array of GLYPHs.
BOX2D BoundingBox() override
Definition glyph.h:113
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:74
static bool empty(const wxTextEntryBase *aCtrl)
@ SUBSCRIPT
Definition font.h:45
@ ITALIC
Definition font.h:44
@ SUPERSCRIPT
Definition font.h:46
unsigned int TEXT_STYLE_FLAGS
Definition font.h:61
static constexpr double ITALIC_TILT
Tilt factor for italic style (this is the scaling factor on dY relative coordinates to give a tilted ...
Definition font.h:58
#define KICAD_FONT_NAME
const int newstroke_font_bufsize
const char *const newstroke_font[]
Modified 2019 to include based CJK Unicode Ideographs, using code copyright (c) 2018 Lingdong Huang.
static constexpr int FONT_OFFSET
Offset (in stroke font units) to move the origin to the baseline.
std::mutex g_defaultFontLoadMutex
std::vector< std::shared_ptr< GLYPH > > g_defaultFontGlyphs
bool g_defaultFontInitialized
std::vector< BOX2D > * g_defaultFontGlyphBoundingBoxes
void buildGlyphBoundingBox(std::shared_ptr< STROKE_GLYPH > &aGlyph, double aGlyphWidth)
static constexpr double STROKE_FONT_SCALE
Scale factor for a glyph.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682