KiCad PCB EDA Suite
drc_test_provider_text_dims.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-2022 KiCad Developers.
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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <macros.h>
25#include <pcb_text.h>
26#include <pcb_textbox.h>
27#include <fp_text.h>
28#include <fp_textbox.h>
29#include <drc/drc_engine.h>
30#include <drc/drc_item.h>
31#include <drc/drc_rule.h>
33#include <font/font.h>
34
35
36/*
37 Text dimensions tests.
38 Errors generated:
39 - DRCE_TEXT_HEIGHT
40 - DRCE_TEXT_THICKNESS
41*/
42
44{
45public:
47 {
48 }
49
51 {
52 }
53
54 virtual bool Run() override;
55
56 virtual const wxString GetName() const override
57 {
58 return wxT( "text_dimensions" );
59 };
60
61 virtual const wxString GetDescription() const override
62 {
63 return wxT( "Tests text height and thickness" );
64 }
65};
66
67
69{
70 const int progressDelta = 250;
71 int count = 0;
72 int ii = 0;
73
76 {
77 reportAux( wxT( "Text dimension violations ignored. Tests not run." ) );
78 return true; // continue with other tests
79 }
80
83 {
84 reportAux( wxT( "No text height or text thickness constraints found. Tests not run." ) );
85 return true; // continue with other tests
86 }
87
88 if( !reportPhase( _( "Checking text dimensions..." ) ) )
89 return false; // DRC cancelled
90
91 auto checkTextHeight =
92 [&]( BOARD_ITEM* item, EDA_TEXT* text ) -> bool
93 {
95 return false;
96
98 nullptr, item->GetLayer() );
99
100 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
101 return true;
102
103 int actualHeight = text->GetTextSize().y;
104
105 if( constraint.Value().HasMin() && actualHeight < constraint.Value().Min() )
106 {
107 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TEXT_HEIGHT );
108 wxString msg = formatMsg( _( "(%s min height %s; actual %s)" ),
109 constraint.GetName(),
110 constraint.Value().Min(),
111 actualHeight );
112
113 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
114 drcItem->SetItems( item );
115 drcItem->SetViolatingRule( constraint.GetParentRule() );
116
117 reportViolation( drcItem, item->GetPosition(), item->GetLayer() );
118 }
119
120 if( constraint.Value().HasMax() && actualHeight > constraint.Value().Max() )
121 {
122 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TEXT_HEIGHT );
123 wxString msg = formatMsg( _( "(%s max height %s; actual %s)" ),
124 constraint.GetName(),
125 constraint.Value().Max(),
126 actualHeight );
127
128 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
129 drcItem->SetItems( item );
130 drcItem->SetViolatingRule( constraint.GetParentRule() );
131
132 reportViolation( drcItem, item->GetPosition(), item->GetLayer() );
133 }
134
135 return true;
136 };
137
138 auto checkTextThickness =
139 [&]( BOARD_ITEM* item, EDA_TEXT* text ) -> bool
140 {
142 nullptr, item->GetLayer() );
143
144 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
145 return true;
146
147 KIFONT::FONT* font = text->GetFont();
148
149 if( !font )
150 font = KIFONT::FONT::GetFont( wxEmptyString, text->IsBold(), text->IsItalic() );
151
152 if( font->IsOutline() )
153 {
154 if( !constraint.Value().HasMin() )
155 return true;
156
157 auto* glyphs = text->GetRenderCache( font, text->GetShownText() );
158 bool collapsedStroke = false;
159 bool collapsedArea = false;
160
161 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : *glyphs )
162 {
163 auto outlineGlyph = static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() );
164 int outlineCount = outlineGlyph->OutlineCount();
165 int holeCount = 0;
166
167 if( outlineCount == 0 )
168 continue; // ignore spaces
169
170 for( ii = 0; ii < outlineCount; ++ii )
171 holeCount += outlineGlyph->HoleCount( ii );
172
173 SHAPE_POLY_SET poly = outlineGlyph->CloneDropTriangulation();
174 poly.Deflate( constraint.Value().Min() / 2, 16 );
176
177 int resultingOutlineCount = poly.OutlineCount();
178 int resultingHoleCount = 0;
179
180 for( ii = 0; ii < resultingOutlineCount; ++ii )
181 resultingHoleCount += poly.HoleCount( ii );
182
183 if( ( resultingOutlineCount != outlineCount )
184 || ( resultingHoleCount != holeCount ) )
185 {
186 collapsedStroke = true;
187 break;
188 }
189
190 double glyphArea = outlineGlyph->Area();
191
192 if( glyphArea == 0 )
193 continue;
194
195 poly.Inflate( constraint.Value().Min() / 2, 16 );
197 double resultingGlyphArea = poly.Area();
198
199 if( ( std::abs( resultingGlyphArea - glyphArea ) / glyphArea ) > 0.1 )
200 {
201 collapsedArea = true;
202 break;
203 }
204 }
205
206 if( collapsedStroke || collapsedArea )
207 {
208 auto drcItem = DRC_ITEM::Create( DRCE_TEXT_THICKNESS );
209 wxString msg;
210
211 msg = _( "(TrueType font characters with insufficient stroke weight)" );
212
213 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
214 drcItem->SetItems( item );
215 drcItem->SetViolatingRule( constraint.GetParentRule() );
216
217 reportViolation( drcItem, item->GetPosition(), item->GetLayer() );
218 }
219 }
220 else
221 {
222 int actualThickness = text->GetEffectiveTextPenWidth();
223
224 if( constraint.Value().HasMin() && actualThickness < constraint.Value().Min() )
225 {
226 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TEXT_THICKNESS );
227 wxString msg = formatMsg( _( "(%s min thickness %s; actual %s)" ),
228 constraint.GetName(),
229 constraint.Value().Min(),
230 actualThickness );
231
232 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
233 drcItem->SetItems( item );
234 drcItem->SetViolatingRule( constraint.GetParentRule() );
235
236 reportViolation( drcItem, item->GetPosition(), item->GetLayer() );
237 }
238
239 if( constraint.Value().HasMax() && actualThickness > constraint.Value().Max() )
240 {
241 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TEXT_THICKNESS );
242 wxString msg = formatMsg( _( "(%s max thickness %s; actual %s)" ),
243 constraint.GetName(),
244 constraint.Value().Max(),
245 actualThickness );
246
247 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
248 drcItem->SetItems( item );
249 drcItem->SetViolatingRule( constraint.GetParentRule() );
250
251 reportViolation( drcItem, item->GetPosition(), item->GetLayer() );
252 }
253 }
254
255 return true;
256 };
257
258 static const std::vector<KICAD_T> itemTypes = { PCB_TEXT_T, PCB_FP_TEXT_T,
260
262 [&]( BOARD_ITEM* item ) -> bool
263 {
264 ++count;
265 return true;
266 } );
267
269 [&]( BOARD_ITEM* item ) -> bool
270 {
271 if( !reportProgress( ii++, count, progressDelta ) )
272 return false;
273
274 EDA_TEXT* text = nullptr;
275 int strikes = 0;
276
277 switch( item->Type() )
278 {
279 case PCB_TEXT_T: text = static_cast<PCB_TEXT*>( item ); break;
280 case PCB_TEXTBOX_T: text = static_cast<PCB_TEXTBOX*>( item ); break;
281 case PCB_FP_TEXT_T: text = static_cast<FP_TEXT*>( item ); break;
282 case PCB_FP_TEXTBOX_T: text = static_cast<FP_TEXTBOX*>( item ); break;
283 default: UNIMPLEMENTED_FOR( item->GetClass() ); break;
284 }
285
286 if( !text || !text->IsVisible() )
287 return true;
288
290 strikes++;
291 else
292 checkTextThickness( item, text );
293
295 strikes++;
296 else
297 checkTextHeight( item, text );
298
299 if( strikes >= 2 )
300 return false;
301
302 return true;
303 } );
304
306
307 return !m_drcEngine->IsCancelled();
308}
309
310
311namespace detail
312{
314}
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:70
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:192
wxString GetName() const
Definition: drc_rule.h:149
SEVERITY GetSeverity() const
Definition: drc_rule.h:162
MINOPTMAX< int > & Value()
Definition: drc_rule.h:142
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:145
bool HasRulesForConstraintType(DRC_CONSTRAINT_T constraintID)
bool IsErrorLimitExceeded(int error_code)
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
Definition: drc_engine.cpp:669
bool IsCancelled() const
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:325
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
virtual const wxString GetName() const override
virtual const wxString GetDescription() const override
Represent a DRC "provider" which runs some DRC functions over a BOARD and spits out #DRC_ITEMs and po...
virtual bool reportPhase(const wxString &aStageName)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, LSET aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
virtual bool reportProgress(int aCount, int aSize, int aDelta)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer)
virtual void reportAux(wxString fmt,...)
DRC_ENGINE * m_drcEngine
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, int aConstraint, int aActual)
virtual void reportRuleStatistics()
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:249
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:72
FONT is an abstract base class for both outline and stroke fonts.
Definition: font.h:105
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false)
Definition: font.cpp:138
virtual bool IsOutline() const
Definition: font.h:113
static LSET AllLayersMask()
Definition: lset.cpp:808
T Min() const
Definition: minoptmax.h:33
bool HasMax() const
Definition: minoptmax.h:38
bool HasMin() const
Definition: minoptmax.h:37
T Max() const
Definition: minoptmax.h:34
Represent a set of closed polygons.
double Area()
Count the number of arc shapes present.
void Deflate(int aAmount, int aCircleSegmentsCount, CORNER_STRATEGY aCornerStrategy=CHAMFER_ALL_CORNERS)
void Inflate(int aAmount, int aCircleSegCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
Perform outline inflation/deflation.
int HoleCount(int aOutline) const
Return the reference to aIndex-th outline in the set.
void Simplify(POLYGON_MODE aFastMode)
int OutlineCount() const
Return the number of vertices in a given outline/hole.
SHAPE_POLY_SET CloneDropTriangulation() const
Creates a new empty polygon in the set and returns its index.
@ DRCE_TEXT_HEIGHT
Definition: drc_item.h:91
@ DRCE_TEXT_THICKNESS
Definition: drc_item.h:92
@ TEXT_THICKNESS_CONSTRAINT
Definition: drc_rule.h:55
@ TEXT_HEIGHT_CONSTRAINT
Definition: drc_rule.h:54
#define _(s)
This file contains miscellaneous commonly used macros and functions.
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:418
@ RPT_SEVERITY_IGNORE
@ PCB_FP_TEXTBOX_T
class FP_TEXTBOX, wrapped text in a footprint
Definition: typeinfo.h:93
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition: typeinfo.h:91
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:90
@ PCB_FP_TEXT_T
class FP_TEXT, text in a footprint
Definition: typeinfo.h:92