You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

304 lines
12 KiB

4 years ago
4 years ago
4 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright The 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. #include <macros.h>
  24. #include <pcb_field.h>
  25. #include <pcb_text.h>
  26. #include <pcb_textbox.h>
  27. #include <drc/drc_engine.h>
  28. #include <drc/drc_item.h>
  29. #include <drc/drc_rule.h>
  30. #include <drc/drc_test_provider.h>
  31. #include <font/font.h>
  32. #include <pcb_dimension.h>
  33. /*
  34. Text dimensions tests.
  35. Errors generated:
  36. - DRCE_TEXT_HEIGHT
  37. - DRCE_TEXT_THICKNESS
  38. */
  39. class DRC_TEST_PROVIDER_TEXT_DIMS : public DRC_TEST_PROVIDER
  40. {
  41. public:
  42. DRC_TEST_PROVIDER_TEXT_DIMS()
  43. {}
  44. virtual ~DRC_TEST_PROVIDER_TEXT_DIMS() = default;
  45. virtual bool Run() override;
  46. virtual const wxString GetName() const override { return wxT( "text_dimensions" ); };
  47. };
  48. bool DRC_TEST_PROVIDER_TEXT_DIMS::Run()
  49. {
  50. const int progressDelta = 250;
  51. int count = 0;
  52. int ii = 0;
  53. if( m_drcEngine->IsErrorLimitExceeded( DRCE_TEXT_HEIGHT )
  54. && m_drcEngine->IsErrorLimitExceeded( DRCE_TEXT_THICKNESS ) )
  55. {
  56. REPORT_AUX( wxT( "Text dimension violations ignored. Tests not run." ) );
  57. return true; // continue with other tests
  58. }
  59. if( !m_drcEngine->HasRulesForConstraintType( TEXT_HEIGHT_CONSTRAINT )
  60. && !m_drcEngine->HasRulesForConstraintType( TEXT_THICKNESS_CONSTRAINT ) )
  61. {
  62. REPORT_AUX( wxT( "No text height or text thickness constraints found. Tests not run." ) );
  63. return true; // continue with other tests
  64. }
  65. if( !reportPhase( _( "Checking text dimensions..." ) ) )
  66. return false; // DRC cancelled
  67. auto checkTextHeight =
  68. [&]( BOARD_ITEM* item, EDA_TEXT* text ) -> bool
  69. {
  70. if( m_drcEngine->IsErrorLimitExceeded( DRCE_TEXT_HEIGHT ) )
  71. return false;
  72. DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( TEXT_HEIGHT_CONSTRAINT, item,
  73. nullptr, item->GetLayer() );
  74. if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
  75. return true;
  76. int actualHeight = text->GetTextSize().y;
  77. if( constraint.Value().HasMin() && actualHeight < constraint.Value().Min() )
  78. {
  79. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TEXT_HEIGHT );
  80. wxString msg = formatMsg( _( "(%s min height %s; actual %s)" ),
  81. constraint.GetName(),
  82. constraint.Value().Min(),
  83. actualHeight );
  84. drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
  85. drcItem->SetItems( item );
  86. drcItem->SetViolatingRule( constraint.GetParentRule() );
  87. reportViolation( drcItem, item->GetPosition(), item->GetLayer() );
  88. }
  89. if( constraint.Value().HasMax() && actualHeight > constraint.Value().Max() )
  90. {
  91. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TEXT_HEIGHT );
  92. wxString msg = formatMsg( _( "(%s max height %s; actual %s)" ),
  93. constraint.GetName(),
  94. constraint.Value().Max(),
  95. actualHeight );
  96. drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
  97. drcItem->SetItems( item );
  98. drcItem->SetViolatingRule( constraint.GetParentRule() );
  99. reportViolation( drcItem, item->GetPosition(), item->GetLayer() );
  100. }
  101. return true;
  102. };
  103. auto checkTextThickness =
  104. [&]( BOARD_ITEM* item, EDA_TEXT* text ) -> bool
  105. {
  106. DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( TEXT_THICKNESS_CONSTRAINT, item,
  107. nullptr, item->GetLayer() );
  108. if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
  109. return true;
  110. KIFONT::FONT* font = text->GetFont();
  111. if( !font )
  112. font = KIFONT::FONT::GetFont( wxEmptyString, text->IsBold(), text->IsItalic() );
  113. if( font->IsOutline() )
  114. {
  115. if( !constraint.Value().HasMin() || constraint.Value().Min() <= 0 )
  116. return true;
  117. auto* glyphs = text->GetRenderCache( font, text->GetShownText( true ) );
  118. bool collapsedStroke = false;
  119. bool collapsedArea = false;
  120. for( const std::unique_ptr<KIFONT::GLYPH>& glyph : *glyphs )
  121. {
  122. // Ensure the glyph is a OUTLINE_GLYPH (for instance, overbars in outline
  123. // font text are represented as STROKE_GLYPHs).
  124. if( !glyph->IsOutline() )
  125. continue;
  126. auto outlineGlyph = static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() );
  127. int outlineCount = outlineGlyph->OutlineCount();
  128. int holeCount = 0;
  129. if( outlineCount == 0 )
  130. continue; // ignore spaces
  131. for( ii = 0; ii < outlineCount; ++ii )
  132. holeCount += outlineGlyph->HoleCount( ii );
  133. SHAPE_POLY_SET poly = outlineGlyph->CloneDropTriangulation();
  134. poly.Deflate( constraint.Value().Min() / 2,
  135. CORNER_STRATEGY::CHAMFER_ALL_CORNERS, ARC_LOW_DEF );
  136. poly.Simplify();
  137. int resultingOutlineCount = poly.OutlineCount();
  138. int resultingHoleCount = 0;
  139. for( ii = 0; ii < resultingOutlineCount; ++ii )
  140. resultingHoleCount += poly.HoleCount( ii );
  141. if( ( resultingOutlineCount != outlineCount )
  142. || ( resultingHoleCount != holeCount ) )
  143. {
  144. collapsedStroke = true;
  145. break;
  146. }
  147. double glyphArea = outlineGlyph->Area();
  148. if( glyphArea == 0 )
  149. continue;
  150. poly.Inflate( constraint.Value().Min() / 2,
  151. CORNER_STRATEGY::CHAMFER_ALL_CORNERS, ARC_LOW_DEF, true );
  152. double resultingGlyphArea = poly.Area();
  153. if( ( std::abs( resultingGlyphArea - glyphArea ) / glyphArea ) > 0.1 )
  154. {
  155. collapsedArea = true;
  156. break;
  157. }
  158. }
  159. if( collapsedStroke || collapsedArea )
  160. {
  161. auto drcItem = DRC_ITEM::Create( DRCE_TEXT_THICKNESS );
  162. wxString msg;
  163. msg = _( "(TrueType font characters with insufficient stroke weight)" );
  164. drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
  165. drcItem->SetItems( item );
  166. drcItem->SetViolatingRule( constraint.GetParentRule() );
  167. reportViolation( drcItem, item->GetPosition(), item->GetLayer() );
  168. }
  169. }
  170. else
  171. {
  172. int actualThickness = text->GetEffectiveTextPenWidth();
  173. if( constraint.Value().HasMin() && actualThickness < constraint.Value().Min() )
  174. {
  175. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TEXT_THICKNESS );
  176. wxString msg = formatMsg( _( "(%s min thickness %s; actual %s)" ),
  177. constraint.GetName(),
  178. constraint.Value().Min(),
  179. actualThickness );
  180. drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
  181. drcItem->SetItems( item );
  182. drcItem->SetViolatingRule( constraint.GetParentRule() );
  183. reportViolation( drcItem, item->GetPosition(), item->GetLayer() );
  184. }
  185. if( constraint.Value().HasMax() && actualThickness > constraint.Value().Max() )
  186. {
  187. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TEXT_THICKNESS );
  188. wxString msg = formatMsg( _( "(%s max thickness %s; actual %s)" ),
  189. constraint.GetName(),
  190. constraint.Value().Max(),
  191. actualThickness );
  192. drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
  193. drcItem->SetItems( item );
  194. drcItem->SetViolatingRule( constraint.GetParentRule() );
  195. reportViolation( drcItem, item->GetPosition(), item->GetLayer() );
  196. }
  197. }
  198. return true;
  199. };
  200. static const std::vector<KICAD_T> itemTypes = {
  201. PCB_FIELD_T,
  202. PCB_TEXT_T, PCB_TEXTBOX_T, PCB_TABLECELL_T,
  203. PCB_DIMENSION_T
  204. };
  205. forEachGeometryItem( itemTypes, LSET::AllLayersMask(),
  206. [&]( BOARD_ITEM* item ) -> bool
  207. {
  208. ++count;
  209. return true;
  210. } );
  211. forEachGeometryItem( itemTypes, LSET::AllLayersMask(),
  212. [&]( BOARD_ITEM* item ) -> bool
  213. {
  214. if( !reportProgress( ii++, count, progressDelta ) )
  215. return false;
  216. if( EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item ) )
  217. {
  218. int strikes = 0;
  219. if( !text->IsVisible() )
  220. return true;
  221. if( m_drcEngine->IsErrorLimitExceeded( DRCE_TEXT_THICKNESS ) )
  222. strikes++;
  223. else
  224. checkTextThickness( item, text );
  225. if( m_drcEngine->IsErrorLimitExceeded( DRCE_TEXT_HEIGHT ) )
  226. strikes++;
  227. else
  228. checkTextHeight( item, text );
  229. if( strikes >= 2 )
  230. return false;
  231. }
  232. return true;
  233. } );
  234. return !m_drcEngine->IsCancelled();
  235. }
  236. namespace detail
  237. {
  238. static DRC_REGISTER_TEST_PROVIDER<DRC_TEST_PROVIDER_TEXT_DIMS> dummy;
  239. }