KiCad PCB EDA Suite
Loading...
Searching...
No Matches
target3001_fonts.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "target3001_fonts.h"
21
22#include <algorithm>
23#include <limits>
24#include <memory>
25#include <utility>
26#include <vector>
27
28#include <eda_text.h>
29#include <font/glyph_types.h>
30#include <font/outline_font.h>
31#include <geometry/eda_angle.h>
33#include <math/util.h>
34#include <trigo.h>
35
36#include "target3001_parser.h"
37
38namespace TARGET3001
39{
40
41namespace
42{
43
44constexpr double MASTER_EM = 300.0;
45constexpr double HEIGHT_DIVISOR = 222.0;
46constexpr double CALIBRI_HEIGHT_DIVISOR = 241.0;
47
48// Text weight times this is the Windows font weight; 16 and above select a bold face
49constexpr int FONT_WEIGHT_PER_WEIGHT = 35;
50constexpr int BOLD_FONT_WEIGHT = 551;
51
52// Measuring at a large size keeps integer advances precise
53constexpr int PROBE_SIZE = 1000000;
54
55// Built-in fonts stroke at this share of their size per weight unit
56constexpr double STROKE_PER_WEIGHT = 0.01;
57
58// A zero weight marks hidden text, which keeps the default weight
59constexpr int DEFAULT_WEIGHT = 10;
60
61constexpr double DIN_CAP_HEIGHT = 0.9;
62
63// DIN-ISO-ANSI width for text too short to fit, the average advance in KiCad's stroke font
64constexpr double DIN_WIDTH = 0.68;
65
66// DIN-ISO-ANSI glyphs are this share of the size plus one stroke apart, and a space widens the gap by DIN_SPACE
67constexpr double DIN_GAP = 0.1;
68constexpr double DIN_SPACE = 0.43;
69
70// DIN-ISO-ANSI glyph widths between stroke centres in DIN_GRID units of the size; others take DIN_DEFAULT_UNITS
71constexpr double DIN_GRID = 0.045;
72constexpr int DIN_DEFAULT_UNITS = 8;
73
74constexpr std::pair<wchar_t, int> DIN_UNITS[] = {
75 { '!', 0 }, { '(', 4 }, { ')', 4 }, { '+', 10 }, { ',', 2 }, { '-', 9 }, { '.', 0 }, { '/', 9 },
76 { '1', 4 }, { ':', 2 }, { ';', 2 }, { 'A', 14 }, { 'B', 10 }, { 'C', 10 }, { 'D', 10 }, { 'E', 10 },
77 { 'F', 10 }, { 'G', 10 }, { 'H', 10 }, { 'I', 0 }, { 'K', 10 }, { 'L', 9 }, { 'M', 14 }, { 'N', 10 },
78 { 'O', 10 }, { 'P', 10 }, { 'Q', 10 }, { 'R', 10 }, { 'S', 10 }, { 'T', 10 }, { 'U', 10 }, { 'V', 10 },
79 { 'W', 20 }, { 'X', 10 }, { 'Z', 10 }, { '_', 19 }, { 'a', 10 }, { 'f', 6 }, { 'i', 0 }, { 'k', 9 },
80 { 'l', 3 }, { 'm', 16 }, { 'r', 5 }, { 't', 6 }, { 'v', 10 }, { 'w', 16 }, { 'y', 10 }, { 'z', 9 },
81 { L'\u00B5', 10 }, { L'\u00E4', 10 }
82};
83
84// TrueType OS/2 version 1 weights for the average character width, per 1000 characters
85constexpr std::pair<char, int> AVERAGE_WIDTH_WEIGHTS[] = {
86 { 'a', 64 }, { 'b', 14 }, { 'c', 27 }, { 'd', 35 }, { 'e', 100 }, { 'f', 20 }, { 'g', 14 },
87 { 'h', 42 }, { 'i', 63 }, { 'j', 3 }, { 'k', 6 }, { 'l', 35 }, { 'm', 20 }, { 'n', 56 },
88 { 'o', 56 }, { 'p', 17 }, { 'q', 4 }, { 'r', 49 }, { 's', 56 }, { 't', 71 }, { 'u', 31 },
89 { 'v', 10 }, { 'w', 18 }, { 'x', 3 }, { 'y', 18 }, { 'z', 2 }, { ' ', 166 }
90};
91
92
94std::vector<double> dinCentres( const wxString& aText, double aSize, double aPen )
95{
96 std::vector<double> centres;
97 double cursor = 0.0;
98
99 for( wxUniChar c : aText )
100 {
101 if( c == ' ' )
102 {
103 cursor += DIN_SPACE * aSize;
104 continue;
105 }
106
107 int units = DIN_DEFAULT_UNITS;
108
109 for( const auto& [ch, chUnits] : DIN_UNITS )
110 {
111 if( c.GetValue() == wxUniChar::value_type( ch ) )
112 {
113 units = chUnits;
114 break;
115 }
116 }
117
118 double glyphWidth = units * DIN_GRID * aSize;
119
120 centres.push_back( cursor + glyphWidth / 2.0 );
121 cursor += glyphWidth + DIN_GAP * aSize + aPen;
122 }
123
124 return centres;
125}
126
127
129std::vector<std::pair<double, double>> strokeSpans( const wxString& aText )
130{
131 std::vector<std::unique_ptr<KIFONT::GLYPH>> glyphs;
132 std::vector<std::pair<double, double>> spans;
133
134 KIFONT::FONT::GetFont()->GetTextAsGlyphs( nullptr, &glyphs, aText, VECTOR2I( PROBE_SIZE, PROBE_SIZE ),
135 VECTOR2I( 0, 0 ), ANGLE_0, false, VECTOR2I( 0, 0 ), 0 );
136
137 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : glyphs )
138 {
139 std::pair<double, double> span( std::numeric_limits<double>::max(), std::numeric_limits<double>::lowest() );
140
141 for( const std::vector<VECTOR2D>& stroke : *static_cast<KIFONT::STROKE_GLYPH*>( glyph.get() ) )
142 {
143 for( const VECTOR2D& pt : stroke )
144 {
145 span.first = std::min( span.first, pt.x );
146 span.second = std::max( span.second, pt.x );
147 }
148 }
149
150 if( span.first <= span.second )
151 spans.push_back( span );
152 }
153
154 return spans;
155}
156
157} // namespace
158
159
160void FONTS::Apply( EDA_TEXT* aText, const ELEMENT& aElem, int aUnit )
161{
162 if( applyTrueType( aText, aElem, aUnit ) )
163 return;
164
165 const TEXT& t = aElem.text;
166 int weight = t.weight ? t.weight : DEFAULT_WEIGHT;
167 double height = double( t.height ) / aUnit;
168 double width = double( t.width ) / aUnit;
169 VECTOR2D size( width, height );
170 double strokeBase = width;
171
172 if( ToWx( t.font ).Trim().Trim( false ).IsSameAs( wxS( "DIN-ISO-ANSI" ), false ) )
173 {
174 double scale = std::max( height, width );
175 double pen = scale * weight * STROKE_PER_WEIGHT;
176 std::vector<double> din = dinCentres( aText->GetText(), scale, pen );
177 std::vector<std::pair<double, double>> spans = strokeSpans( aText->GetText() );
178 double stem = strokeSpans( wxS( "H" ) ).front().first;
179 double cross = 0.0;
180 double norm = 0.0;
181
182 size = VECTOR2D( DIN_WIDTH * scale, DIN_CAP_HEIGHT * scale );
183 strokeBase = scale;
184
185 // Least squares fit of the glyph centres, measured from the stem of H where both fonts start
186 if( din.size() > 1 && din.size() == spans.size() )
187 {
188 for( size_t ii = 0; ii < din.size(); ++ii )
189 {
190 double centre = ( spans[ii].first + spans[ii].second ) / 2.0 - stem;
191
192 cross += centre * din[ii];
193 norm += centre * centre;
194 }
195 }
196
197 if( cross > 0.0 && norm > 0.0 )
198 size.x = cross / norm * PROBE_SIZE;
199 }
200 else
201 {
202 // Fixed pitch, so the string length is known exactly and sets the width
203 wxString text = aText->GetText();
204 double pitch = width * ( 1.0 + 2.0 * STROKE_PER_WEIGHT * weight );
205 VECTOR2I end = KIFONT::FONT::GetFont()->GetTextAsGlyphs( nullptr, nullptr, text,
206 VECTOR2I( PROBE_SIZE, PROBE_SIZE ),
207 VECTOR2I( 0, 0 ), ANGLE_0, false,
208 VECTOR2I( 0, 0 ), 0 );
209
210 if( end.x > 0 )
211 size.x = pitch * text.length() * PROBE_SIZE / end.x;
212 }
213
214 aText->SetTextSize( VECTOR2I( std::max( 1, KiROUND( size.x ) ), std::max( 1, KiROUND( size.y ) ) ) );
215 aText->SetTextThickness( std::max( 1, KiROUND( strokeBase * weight * STROKE_PER_WEIGHT ) ) );
216}
217
218
220{
221 EDA_TEXT probe( aText );
222 bool stroke = !aText.GetFont() || aText.GetFont()->IsStroke();
223 int halfPen = stroke ? aText.GetEffectiveTextPenWidth() / 2 : 0;
224
225 // H stands on the baseline with its stem at the pen in both built-in fonts
226 probe.SetText( wxS( "H" ) );
227 probe.SetTextPos( VECTOR2I( 0, 0 ) );
228 probe.SetTextAngle( ANGLE_0 );
229 probe.SetMirrored( false );
232
233 // Stroke ends are round, so the stroke centre lies half a pen inside the shape's bounds
234 BOX2I ink = probe.GetEffectiveTextShape( false )->BBox();
235 VECTOR2I shift( -( ink.GetLeft() + halfPen ), -( ink.GetBottom() - halfPen ) );
236
237 if( !stroke || aText.GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
238 shift.x = 0;
239
240 if( aText.IsMirrored() )
241 shift.x = -shift.x;
242
243 RotatePoint( shift, aText.GetDrawRotation() );
244 return shift;
245}
246
247
248bool FONTS::applyTrueType( EDA_TEXT* aText, const ELEMENT& aElem, int aUnit )
249{
250 const TEXT& t = aElem.text;
251 wxString name = ToWx( t.font ).Trim().Trim( false );
252 wxString key = name.Upper();
253
254 if( key.IsEmpty() || key == wxS( "TARGET" ) || key == wxS( "DIN-ISO-ANSI" ) )
255 return false;
256
257 // Hidden text keeps its stored weight of zero, which renders regular
258 bool bold = t.weight * FONT_WEIGHT_PER_WEIGHT >= BOLD_FONT_WEIGHT;
259 bool italic = aElem.base.FlagProp( "ITALIC" );
260 auto fontKey = std::make_tuple( key, bold, italic );
261 auto fontIt = m_fonts.find( fontKey );
262
263 if( fontIt == m_fonts.end() )
264 {
265 KIFONT::FONT* font = KIFONT::FONT::GetFont( name, bold, italic );
266 KIFONT::OUTLINE_FONT* outline = font && font->IsOutline() ? static_cast<KIFONT::OUTLINE_FONT*>( font )
267 : nullptr;
268
269 fontIt = m_fonts.emplace( fontKey, outline ).first;
270 }
271
272 KIFONT::OUTLINE_FONT* outline = fontIt->second;
273
274 if( !outline )
275 return false;
276
277 auto widthIt = m_averageWidth.find( outline );
278
279 if( widthIt == m_averageWidth.end() )
280 {
281 double total = 0.0;
282
283 for( const auto& [ch, share] : AVERAGE_WIDTH_WEIGHTS )
284 {
285 VECTOR2I end = outline->GetTextAsGlyphs( nullptr, nullptr, wxString( ch ),
286 VECTOR2I( PROBE_SIZE, PROBE_SIZE ), VECTOR2I( 0, 0 ), ANGLE_0,
287 false, VECTOR2I( 0, 0 ), 0 );
288 total += double( end.x ) * share;
289 }
290
291 widthIt = m_averageWidth.emplace( outline, total / 1000.0 / PROBE_SIZE ).first;
292 }
293
294 double divisor = key == wxS( "CALIBRI" ) ? CALIBRI_HEIGHT_DIVISOR : HEIGHT_DIVISOR;
295 double em = MASTER_EM / divisor * t.height / aUnit;
296 double width = widthIt->second > 0.0 ? double( t.width ) / aUnit / widthIt->second : double( t.width ) / aUnit;
297
298 aText->SetFont( outline );
299 aText->SetBoldFlag( bold );
300 aText->SetItalicFlag( italic );
301 aText->SetTextSize( VECTOR2I( std::max( 1, KiROUND( width ) ),
302 std::max( 1, KiROUND( em / outline->GetEmSize( 1.0 ) ) ) ) );
303 return true;
304}
305
306} // namespace TARGET3001
const char * name
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
constexpr coord_type GetLeft() const
Definition box2.h:225
constexpr coord_type GetBottom() const
Definition box2.h:219
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:512
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:118
virtual void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:556
KIFONT::FONT * GetFont() const
Definition eda_text.h:286
void SetMirrored(bool isMirrored)
Definition eda_text.cpp:354
virtual EDA_ANGLE GetDrawRotation() const
Definition eda_text.h:425
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:378
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:239
void SetBoldFlag(bool aBold)
Set only the bold flag, without changing the font.
Definition eda_text.cpp:324
virtual void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:250
void SetItalicFlag(bool aItalic)
Set only the italic flag, without changing the font.
Definition eda_text.cpp:302
bool IsMirrored() const
Definition eda_text.h:229
int GetEffectiveTextPenWidth(int aDefaultPenWidth=0) const
The EffectiveTextPenWidth uses the text thickness if > 1 or aDefaultPenWidth.
Definition eda_text.cpp:427
std::shared_ptr< SHAPE_COMPOUND > GetEffectiveTextShape(bool aTriangulate=true, const BOX2I &aBBox=BOX2I(), const EDA_ANGLE &aAngle=ANGLE_0) const
Build a list of segments (SHAPE_SEGMENT) to describe a text shape.
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:236
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:268
void SetFont(KIFONT::FONT *aFont)
Definition eda_text.cpp:471
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:370
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:94
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
virtual bool IsOutline() const
Definition font.h:102
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.
Implement outline font drawing.
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.
double GetEmSize(double aSize) const
Edge of the em square for glyphs drawn at text size aSize.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
bool applyTrueType(EDA_TEXT *aText, const ELEMENT &aElem, int aUnit)
static VECTOR2I BaselineShift(const EDA_TEXT &aText)
Offset in text units that moves aText so the baseline-left point of its first glyph lies on its posit...
std::map< std::tuple< wxString, bool, bool >, KIFONT::OUTLINE_FONT * > m_fonts
std::map< const KIFONT::OUTLINE_FONT *, double > m_averageWidth
per unit of text size
void Apply(EDA_TEXT *aText, const ELEMENT &aElem, int aUnit)
Give aText the font, style, size and stroke width of text aElem.
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
Reader for TARGET 3001!
wxString ToWx(const std::string &aRaw)
Convert a stored ANSI string to a wxString.
const int scale
bool FlagProp(const std::string &aKey) const
True when property aKey holds one of the format's boolean true words.
One element of any class.
VECTOR2I end
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:227
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707