KiCad PCB EDA Suite
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 (C) 2016-2022 Kicad Developers, see AUTHORS.txt for contributors.
8 *
9 * Stroke font class
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, you may find one here:
23 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
24 * or you may search the http://www.gnu.org website for the version 2 license,
25 * or you may write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27 */
28
30#include <wx/string.h>
31#include <wx/textfile.h>
32#include <newstroke_font.h>
33#include <font/glyph.h>
34#include <font/stroke_font.h>
36#include <trigo.h>
37
38// The "official" name of the building Kicad stroke font (always existing)
40
41#include <mutex>
42
43using namespace KIFONT;
44
45
47static constexpr double OVERBAR_POSITION_FACTOR = 1.40;
48
50static constexpr double UNDERLINE_POSITION_FACTOR = -0.16;
51
53static constexpr double STROKE_FONT_SCALE = 1.0 / 21.0;
54
55static constexpr int FONT_OFFSET = -10;
56
57
59std::vector<std::shared_ptr<GLYPH>> g_defaultFontGlyphs;
62
63
64STROKE_FONT::STROKE_FONT() :
65 m_glyphs( nullptr ),
66 m_glyphBoundingBoxes( nullptr ),
67 m_maxGlyphWidth( 0.0 )
68{
69}
70
71
72STROKE_FONT* STROKE_FONT::LoadFont( const wxString& aFontName )
73{
74 if( aFontName.empty() )
75 {
76 STROKE_FONT* font = new STROKE_FONT();
78 return font;
79 }
80 else
81 {
82 // FONT TODO: support for other stroke fonts?
83 return nullptr;
84 }
85}
86
87
88void buildGlyphBoundingBox( std::shared_ptr<STROKE_GLYPH>& aGlyph, double aGlyphWidth )
89{
90 VECTOR2D min( 0, 0 );
91 VECTOR2D max( aGlyphWidth, 0 );
92
93 for( const std::vector<VECTOR2D>& pointList : *aGlyph )
94 {
95 for( const VECTOR2D& point : pointList )
96 {
97 min.y = std::min( min.y, point.y );
98 max.y = std::max( max.y, point.y );
99 }
100 }
101
102 aGlyph->SetBoundingBox( BOX2D( min, max - min ) );
103}
104
105
106void STROKE_FONT::loadNewStrokeFont( const char* const aNewStrokeFont[], int aNewStrokeFontSize )
107{
108 // Protect the initialization sequence against multiple entries
109 std::lock_guard<std::mutex> lock( g_defaultFontLoadMutex );
110
112 {
113 g_defaultFontGlyphs.reserve( aNewStrokeFontSize );
114
115 g_defaultFontGlyphBoundingBoxes = new std::vector<BOX2D>;
116 g_defaultFontGlyphBoundingBoxes->reserve( aNewStrokeFontSize );
117
118 for( int j = 0; j < aNewStrokeFontSize; j++ )
119 {
120 std::shared_ptr<STROKE_GLYPH> glyph = std::make_shared<STROKE_GLYPH>();
121
122 double glyphStartX = 0.0;
123 double glyphEndX = 0.0;
124 double glyphWidth = 0.0;
125 int strokes = 0;
126 int i = 0;
127
128 while( aNewStrokeFont[j][i] )
129 {
130
131 if( aNewStrokeFont[j][i] == ' ' && aNewStrokeFont[j][i+1] == 'R' )
132 strokes++;
133
134 i += 2;
135 }
136
137 glyph->reserve( strokes + 1 );
138
139 i = 0;
140
141 while( aNewStrokeFont[j][i] )
142 {
143 VECTOR2D point( 0.0, 0.0 );
144 char coordinate[2] = { 0, };
145
146 for( int k : { 0, 1 } )
147 coordinate[k] = aNewStrokeFont[j][i + k];
148
149 if( i < 2 )
150 {
151 // The first two values contain the width of the char
152 glyphStartX = ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE;
153 glyphEndX = ( coordinate[1] - 'R' ) * STROKE_FONT_SCALE;
154 glyphWidth = glyphEndX - glyphStartX;
155 }
156 else if( ( coordinate[0] == ' ' ) && ( coordinate[1] == 'R' ) )
157 {
158 glyph->RaisePen();
159 }
160 else
161 {
162 // In stroke font, coordinates values are coded as <value> + 'R', where
163 // <value> is an ASCII char.
164 // therefore every coordinate description of the Hershey format has an offset,
165 // it has to be subtracted
166 // Note:
167 // * the stroke coordinates are stored in reduced form (-1.0 to +1.0),
168 // and the actual size is stroke coordinate * glyph size
169 // * a few shapes have a height slightly bigger than 1.0 ( like '{' '[' )
170 point.x = (double) ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE - glyphStartX;
171
172 // FONT_OFFSET is here for historical reasons, due to the way the stroke font
173 // was built. It allows shapes coordinates like W M ... to be >= 0
174 // Only shapes like j y have coordinates < 0
175 point.y = (double) ( coordinate[1] - 'R' + FONT_OFFSET ) * STROKE_FONT_SCALE;
176
177 glyph->AddPoint( point );
178 }
179
180 i += 2;
181 }
182
183 glyph->Finalize();
184
185 // Compute the bounding box of the glyph
186 buildGlyphBoundingBox( glyph, glyphWidth );
187 g_defaultFontGlyphBoundingBoxes->emplace_back( glyph->BoundingBox() );
188 g_defaultFontGlyphs.push_back( glyph );
189 m_maxGlyphWidth = std::max( m_maxGlyphWidth, glyphWidth );
190 }
191
193 }
194
198 m_fontFileName = wxEmptyString;
199}
200
201
202double STROKE_FONT::GetInterline( double aGlyphHeight, double aLineSpacing ) const
203{
204 // Do not add the glyph thickness to the interline. This makes bold text line-spacing
205 // different from normal text, which is poor typography.
206 return ( aGlyphHeight * aLineSpacing * INTERLINE_PITCH_RATIO );
207}
208
209
210double STROKE_FONT::ComputeOverbarVerticalPosition( double aGlyphHeight ) const
211{
212 return aGlyphHeight * OVERBAR_POSITION_FACTOR;
213}
214
215
216double STROKE_FONT::ComputeUnderlineVerticalPosition( double aGlyphHeight ) const
217{
218 return aGlyphHeight * UNDERLINE_POSITION_FACTOR;
219}
220
221
222VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
223 const wxString& aText, const VECTOR2I& aSize,
224 const VECTOR2I& aPosition, const EDA_ANGLE& aAngle,
225 bool aMirror, const VECTOR2I& aOrigin,
226 TEXT_STYLE_FLAGS aTextStyle ) const
227{
228 constexpr double SPACE_WIDTH = 0.6;
229 constexpr double INTER_CHAR = 0.2;
230 constexpr double TAB_WIDTH = 4 * 0.82; // Not quite as wide as 5.1/6.0 tab formatting, but
231 // a better match for Scintilla, and closer to the
232 // nominal SPACE_WIDTH + INTER_CHAR
233 constexpr double SUPER_SUB_SIZE_MULTIPLIER = 0.7;
234 constexpr double SUPER_HEIGHT_OFFSET = 0.5;
235 constexpr double SUB_HEIGHT_OFFSET = 0.3;
236
237 VECTOR2I cursor( aPosition );
238 VECTOR2D glyphSize( aSize );
239 double tilt = ( aTextStyle & TEXT_STYLE::ITALIC ) ? ITALIC_TILT : 0.0;
240
241 if( aTextStyle & TEXT_STYLE::SUBSCRIPT || aTextStyle & TEXT_STYLE::SUPERSCRIPT )
242 {
243 glyphSize = glyphSize * SUPER_SUB_SIZE_MULTIPLIER;
244
245 if( aTextStyle & TEXT_STYLE::SUBSCRIPT )
246 cursor.y += glyphSize.y * SUB_HEIGHT_OFFSET;
247 else
248 cursor.y -= glyphSize.y * SUPER_HEIGHT_OFFSET;
249 }
250
251 for( wxUniChar c : aText )
252 {
253 // Handle tabs as locked to the nearest 4th column (in space-widths).
254 if( c == '\t' )
255 {
256 int tabWidth = KiROUND( glyphSize.x * TAB_WIDTH );
257 int currentIntrusion = ( cursor.x - aOrigin.x ) % tabWidth;
258
259 cursor.x += tabWidth - currentIntrusion;
260 }
261 else if( c == ' ' )
262 {
263 // 'space' character - draw nothing, advance cursor position
264 cursor.x += KiROUND( glyphSize.x * SPACE_WIDTH );
265 }
266 else
267 {
268 // dd is the index into bounding boxes table
269 int dd = (signed) c - ' ';
270
271 // Filtering non existing glyphs and non printable chars
272 if( dd < 0 || dd >= (int) m_glyphBoundingBoxes->size() )
273 {
274 c = '?';
275 dd = (signed) c - ' ';
276 }
277
278 STROKE_GLYPH* source = static_cast<STROKE_GLYPH*>( m_glyphs->at( dd ).get() );
279
280 if( aGlyphs )
281 {
282 aGlyphs->push_back( source->Transform( glyphSize, cursor, tilt, aAngle, aMirror,
283 aOrigin ) );
284 }
285
286 VECTOR2D glyphExtents = source->BoundingBox().GetEnd();
287
288 glyphExtents *= glyphSize;
289
290 if( tilt > 0.0 )
291 glyphExtents.x -= glyphExtents.y * tilt;
292
293 cursor.x += KiROUND( glyphExtents.x );
294 }
295 }
296
297 VECTOR2D barOffset( 0.0, 0.0 );
298
299 // Shorten the bar a little so its rounded ends don't make it over-long
300 double barTrim = glyphSize.x * 0.1;
301
302 if( aTextStyle & TEXT_STYLE::OVERBAR )
303 {
304 barOffset.y = ComputeOverbarVerticalPosition( glyphSize.y );
305
306 if( aTextStyle & TEXT_STYLE::ITALIC )
307 barOffset.x = barOffset.y * ITALIC_TILT;
308
309 VECTOR2D barStart( aPosition.x + barOffset.x + barTrim, cursor.y - barOffset.y );
310 VECTOR2D barEnd( cursor.x + barOffset.x - barTrim, cursor.y - barOffset.y );
311
312 if( aGlyphs )
313 {
314 STROKE_GLYPH overbarGlyph;
315
316 overbarGlyph.AddPoint( barStart );
317 overbarGlyph.AddPoint( barEnd );
318 overbarGlyph.Finalize();
319
320 aGlyphs->push_back( overbarGlyph.Transform( { 1.0, 1.0 }, { 0, 0 }, false,
321 aAngle, aMirror, aOrigin ) );
322 }
323 }
324
325 if( aTextStyle & TEXT_STYLE::UNDERLINE )
326 {
327 barOffset.y = ComputeUnderlineVerticalPosition( glyphSize.y );
328
329 if( aTextStyle & TEXT_STYLE::ITALIC )
330 barOffset.x = barOffset.y * ITALIC_TILT;
331
332 VECTOR2D barStart( aPosition.x + barOffset.x + barTrim, cursor.y - barOffset.y );
333 VECTOR2D barEnd( cursor.x + barOffset.x - barTrim, cursor.y - barOffset.y );
334
335 if( aGlyphs )
336 {
337 STROKE_GLYPH underlineGlyph;
338
339 underlineGlyph.AddPoint( barStart );
340 underlineGlyph.AddPoint( barEnd );
341 underlineGlyph.Finalize();
342
343 aGlyphs->push_back( underlineGlyph.Transform( { 1.0, 1.0 }, { 0, 0 }, false,
344 aAngle, aMirror, aOrigin ) );
345 }
346 }
347
348 if( aBBox )
349 {
350 aBBox->SetOrigin( aPosition );
351 aBBox->SetEnd( cursor.x + barOffset.x - KiROUND( glyphSize.x * INTER_CHAR ),
352 cursor.y + std::max( glyphSize.y, barOffset.y * OVERBAR_POSITION_FACTOR ) );
353 aBBox->Normalize();
354 }
355
356 return VECTOR2I( cursor.x, aPosition.y );
357}
BOX2< VECTOR2D > BOX2D
Definition: box2.h:848
void SetOrigin(const Vec &pos)
Definition: box2.h:202
BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition: box2.h:119
const Vec GetEnd() const
Definition: box2.h:185
void SetEnd(coord_type x, coord_type y)
Definition: box2.h:255
wxString m_fontName
Font name.
Definition: font.h:262
wxString m_fontFileName
Font file name.
Definition: font.h:263
static constexpr double INTERLINE_PITCH_RATIO
Definition: font.h:256
Implement a stroke font drawing.
Definition: stroke_font.h:53
double ComputeUnderlineVerticalPosition(double aGlyphHeight) const override
Compute the vertical position of an underline.
void loadNewStrokeFont(const char *const aNewStrokeFont[], int aNewStrokeFontSize)
Load the standard KiCad stroke font.
const GLYPH_BOUNDING_BOX_LIST * m_glyphBoundingBoxes
Definition: stroke_font.h:101
const std::vector< std::shared_ptr< GLYPH > > * m_glyphs
Definition: stroke_font.h:100
double ComputeOverbarVerticalPosition(double aGlyphHeight) const override
Compute the vertical position of an overbar.
static STROKE_FONT * LoadFont(const wxString &aFontName)
Load a stroke font.
Definition: stroke_font.cpp:72
double GetInterline(double aGlyphHeight, double aLineSpacing=1.0) const override
Compute the distance (interline) between 2 lines of text (for multiline texts).
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.
void AddPoint(const VECTOR2D &aPoint)
Definition: glyph.cpp:39
BOX2D BoundingBox() override
Definition: glyph.h:97
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
@ SUBSCRIPT
Definition: font.h:47
@ OVERBAR
Definition: font.h:49
@ UNDERLINE
Definition: font.h:50
@ ITALIC
Definition: font.h:46
@ SUPERSCRIPT
Definition: font.h:48
unsigned int TEXT_STYLE_FLAGS
Definition: font.h:63
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:60
#define KICAD_FONT_NAME
Definition: font.h:100
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
Definition: stroke_font.cpp:55
std::mutex g_defaultFontLoadMutex
Definition: stroke_font.cpp:61
std::vector< std::shared_ptr< GLYPH > > g_defaultFontGlyphs
Definition: stroke_font.cpp:59
bool g_defaultFontInitialized
Definition: stroke_font.cpp:58
std::vector< BOX2D > * g_defaultFontGlyphBoundingBoxes
Definition: stroke_font.cpp:60
void buildGlyphBoundingBox(std::shared_ptr< STROKE_GLYPH > &aGlyph, double aGlyphWidth)
Definition: stroke_font.cpp:88
static constexpr double OVERBAR_POSITION_FACTOR
< Factor that determines relative vertical position of the overbar.
Definition: stroke_font.cpp:47
static constexpr double UNDERLINE_POSITION_FACTOR
Scale factor for a glyph.
Definition: stroke_font.cpp:50
static constexpr double STROKE_FONT_SCALE
Definition: stroke_font.cpp:53
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618