KiCad PCB EDA Suite
pcb_parser.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 CERN
5  * Copyright (C) 2012-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
30 #include <cerrno>
31 #include <common.h>
32 #include <confirm.h>
33 #include <macros.h>
34 #include <title_block.h>
35 #include <trigo.h>
36 
37 #include <advanced_config.h>
38 #include <board.h>
39 #include <dimension.h>
40 #include <pcb_shape.h>
41 #include <fp_shape.h>
42 #include <pcb_group.h>
43 #include <pcb_target.h>
44 #include <footprint.h>
45 #include <netclass.h>
46 #include <pad.h>
47 #include <track.h>
48 #include <zone.h>
50 #include <pcb_plot_params_parser.h>
51 #include <pcb_plot_params.h>
52 #include <locale_io.h>
53 #include <zones.h>
55 #include <convert_basic_shapes_to_polygon.h> // for RECT_CHAMFER_POSITIONS definition
56 #include <template_fieldnames.h>
57 
58 using namespace PCB_KEYS_T;
59 
60 
62 {
63  m_showLegacyZoneWarning = true;
64  m_tooRecent = false;
65  m_requiredVersion = 0;
66  m_layerIndices.clear();
67  m_layerMasks.clear();
68  m_resetKIIDMap.clear();
69 
70  // Add untranslated default (i.e. English) layernames.
71  // Some may be overridden later if parsing a board rather than a footprint.
72  // The English name will survive if parsing only a footprint.
73  for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
74  {
75  std::string untranslated = TO_UTF8( wxString( LSET::Name( PCB_LAYER_ID( layer ) ) ) );
76 
77  m_layerIndices[ untranslated ] = PCB_LAYER_ID( layer );
78  m_layerMasks[ untranslated ] = LSET( PCB_LAYER_ID( layer ) );
79  }
80 
81  m_layerMasks[ "*.Cu" ] = LSET::AllCuMask();
82  m_layerMasks[ "*In.Cu" ] = LSET::InternalCuMask();
83  m_layerMasks[ "F&B.Cu" ] = LSET( 2, F_Cu, B_Cu );
84  m_layerMasks[ "*.Adhes" ] = LSET( 2, B_Adhes, F_Adhes );
85  m_layerMasks[ "*.Paste" ] = LSET( 2, B_Paste, F_Paste );
86  m_layerMasks[ "*.Mask" ] = LSET( 2, B_Mask, F_Mask );
87  m_layerMasks[ "*.SilkS" ] = LSET( 2, B_SilkS, F_SilkS );
88  m_layerMasks[ "*.Fab" ] = LSET( 2, B_Fab, F_Fab );
89  m_layerMasks[ "*.CrtYd" ] = LSET( 2, B_CrtYd, F_CrtYd );
90 
91  // This is for the first pretty & *.kicad_pcb formats, which had
92  // Inner1_Cu - Inner14_Cu with the numbering sequence
93  // reversed from the subsequent format's In1_Cu - In30_Cu numbering scheme.
94  // The newer format brought in an additional 16 Cu layers and flipped the cu stack but
95  // kept the gap between one of the outside layers and the last cu internal.
96 
97  for( int i=1; i<=14; ++i )
98  {
99  std::string key = StrPrintf( "Inner%d.Cu", i );
100 
101  m_layerMasks[ key ] = LSET( PCB_LAYER_ID( In15_Cu - i ) );
102  }
103 }
104 
105 
107 {
108  int curr_level = 0;
109  T token;
110 
111  while( ( token = NextTok() ) != T_EOF )
112  {
113  if( token == T_LEFT )
114  curr_level--;
115 
116  if( token == T_RIGHT )
117  {
118  curr_level++;
119 
120  if( curr_level > 0 )
121  return;
122  }
123  }
124 }
125 
126 
127 void PCB_PARSER::pushValueIntoMap( int aIndex, int aValue )
128 {
129  // Add aValue in netcode mapping (m_netCodes) at index aNetCode
130  // ensure there is room in m_netCodes for that, and add room if needed.
131 
132  if( (int)m_netCodes.size() <= aIndex )
133  m_netCodes.resize( static_cast<std::size_t>( aIndex ) + 1 );
134 
135  m_netCodes[aIndex] = aValue;
136 }
137 
138 
140 {
141  char* tmp;
142 
143  errno = 0;
144 
145  double fval = strtod( CurText(), &tmp );
146 
147  if( errno )
148  {
149  wxString error;
150  error.Printf( _( "Invalid floating point number in\nfile: \"%s\"\nline: %d\noffset: %d" ),
151  CurSource(), CurLineNumber(), CurOffset() );
152 
153  THROW_IO_ERROR( error );
154  }
155 
156  if( CurText() == tmp )
157  {
158  wxString error;
159  error.Printf( _( "Missing floating point number in\nfile: \"%s\"\nline: %d\noffset: %d" ),
160  CurSource(), CurLineNumber(), CurOffset() );
161 
162  THROW_IO_ERROR( error );
163  }
164 
165  return fval;
166 }
167 
168 
170 {
171  T token = NextTok();
172 
173  if( token == T_yes )
174  return true;
175  else if( token == T_no )
176  return false;
177  else
178  Expecting( "yes or no" );
179 
180  return false;
181 }
182 
183 
185 {
186  int year, month, day;
187 
188  year = m_requiredVersion / 10000;
189  month = ( m_requiredVersion / 100 ) - ( year * 100 );
190  day = m_requiredVersion - ( year * 10000 ) - ( month * 100 );
191 
192  // wx throws an assertion, not a catchable exception, when the date is invalid.
193  // User input shouldn't give wx asserts, so check manually and throw a proper
194  // error instead
195  if( day <= 0 || month <= 0 || month > 12 ||
196  day > wxDateTime::GetNumberOfDays( (wxDateTime::Month)( month - 1 ), year ) )
197  {
198  wxString err;
199  err.Printf( _( "Cannot interpret date code %d" ), m_requiredVersion );
200  THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
201  }
202 
203  wxDateTime date( day, (wxDateTime::Month)( month - 1 ), year, 0, 0, 0, 0 );
204  return date.FormatDate();
205 }
206 
207 
209 {
210  if( CurTok() != T_LEFT )
211  NeedLEFT();
212 
213  wxPoint pt;
214  T token = NextTok();
215 
216  if( token != T_xy )
217  Expecting( T_xy );
218 
219  pt.x = parseBoardUnits( "X coordinate" );
220  pt.y = parseBoardUnits( "Y coordinate" );
221 
222  NeedRIGHT();
223 
224  return pt;
225 }
226 
227 
228 void PCB_PARSER::parseXY( int* aX, int* aY )
229 {
230  wxPoint pt = parseXY();
231 
232  if( aX )
233  *aX = pt.x;
234 
235  if( aY )
236  *aY = pt.y;
237 }
238 
239 
240 std::pair<wxString, wxString> PCB_PARSER::parseProperty()
241 {
242  wxString pName;
243  wxString pValue;
244 
245  NeedSYMBOL();
246  pName = FromUTF8();
247  NeedSYMBOL();
248  pValue = FromUTF8();
249  NeedRIGHT();
250 
251  return { pName, pValue };
252 }
253 
254 
256 {
257  wxCHECK_RET( CurTok() == T_effects,
258  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as EDA_TEXT." ) );
259 
260  T token;
261 
262  // Prior to v5.0 text size was omitted from file format if equal to 60mils
263  // Now, it is always explicitly written to file
264  bool foundTextSize = false;
265 
266  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
267  {
268  if( token == T_LEFT )
269  token = NextTok();
270 
271  switch( token )
272  {
273  case T_font:
274  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
275  {
276  if( token == T_LEFT )
277  continue;
278 
279  switch( token )
280  {
281  case T_size:
282  {
283  wxSize sz;
284  sz.SetHeight( parseBoardUnits( "text height" ) );
285  sz.SetWidth( parseBoardUnits( "text width" ) );
286  aText->SetTextSize( sz );
287  NeedRIGHT();
288 
289  foundTextSize = true;
290  }
291  break;
292 
293  case T_thickness:
294  aText->SetTextThickness( parseBoardUnits( "text thickness" ) );
295  NeedRIGHT();
296  break;
297 
298  case T_bold:
299  aText->SetBold( true );
300  break;
301 
302  case T_italic:
303  aText->SetItalic( true );
304  break;
305 
306  default:
307  Expecting( "size, bold, or italic" );
308  }
309  }
310  break;
311 
312  case T_justify:
313  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
314  {
315  if( token == T_LEFT )
316  continue;
317 
318  switch( token )
319  {
320  case T_left:
322  break;
323 
324  case T_right:
326  break;
327 
328  case T_top:
330  break;
331 
332  case T_bottom:
334  break;
335 
336  case T_mirror:
337  aText->SetMirrored( true );
338  break;
339 
340  default:
341  Expecting( "left, right, top, bottom, or mirror" );
342  }
343 
344  }
345  break;
346 
347  case T_hide:
348  aText->SetVisible( false );
349  break;
350 
351  default:
352  Expecting( "font, justify, or hide" );
353  }
354  }
355 
356  // Text size was not specified in file, force legacy default units
357  // 60mils is 1.524mm
358  if( !foundTextSize )
359  {
360  const double defaultTextSize = 1.524 * IU_PER_MM;
361 
362  aText->SetTextSize( wxSize( defaultTextSize, defaultTextSize ) );
363  }
364 }
365 
366 
368 {
369  wxCHECK_MSG( CurTok() == T_model, NULL,
370  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as FP_3DMODEL." ) );
371 
372  T token;
373 
374  FP_3DMODEL* n3D = new FP_3DMODEL;
375  NeedSYMBOLorNUMBER();
376  n3D->m_Filename = FromUTF8();
377 
378  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
379  {
380  if( token == T_LEFT )
381  token = NextTok();
382 
383  switch( token )
384  {
385  case T_at:
386  NeedLEFT();
387  token = NextTok();
388 
389  if( token != T_xyz )
390  Expecting( T_xyz );
391 
392  /* Note:
393  * Prior to KiCad v5, model offset was designated by "at",
394  * and the units were in inches.
395  * Now we use mm, but support reading of legacy files
396  */
397 
398  n3D->m_Offset.x = parseDouble( "x value" ) * 25.4f;
399  n3D->m_Offset.y = parseDouble( "y value" ) * 25.4f;
400  n3D->m_Offset.z = parseDouble( "z value" ) * 25.4f;
401 
402  NeedRIGHT(); // xyz
403  NeedRIGHT(); // at
404  break;
405 
406  case T_hide:
407  n3D->m_Show = false;
408  break;
409 
410  case T_opacity:
411  n3D->m_Opacity = parseDouble( "opacity value" );
412  NeedRIGHT();
413  break;
414 
415  case T_offset:
416  NeedLEFT();
417  token = NextTok();
418 
419  if( token != T_xyz )
420  Expecting( T_xyz );
421 
422  /*
423  * 3D model offset is in mm
424  */
425  n3D->m_Offset.x = parseDouble( "x value" );
426  n3D->m_Offset.y = parseDouble( "y value" );
427  n3D->m_Offset.z = parseDouble( "z value" );
428 
429  NeedRIGHT(); // xyz
430  NeedRIGHT(); // offset
431  break;
432 
433  case T_scale:
434  NeedLEFT();
435  token = NextTok();
436 
437  if( token != T_xyz )
438  Expecting( T_xyz );
439 
440  n3D->m_Scale.x = parseDouble( "x value" );
441  n3D->m_Scale.y = parseDouble( "y value" );
442  n3D->m_Scale.z = parseDouble( "z value" );
443 
444  NeedRIGHT(); // xyz
445  NeedRIGHT(); // scale
446  break;
447 
448  case T_rotate:
449  NeedLEFT();
450  token = NextTok();
451 
452  if( token != T_xyz )
453  Expecting( T_xyz );
454 
455  n3D->m_Rotation.x = parseDouble( "x value" );
456  n3D->m_Rotation.y = parseDouble( "y value" );
457  n3D->m_Rotation.z = parseDouble( "z value" );
458 
459  NeedRIGHT(); // xyz
460  NeedRIGHT(); // rotate
461  break;
462 
463  default:
464  Expecting( "at, hide, opacity, offset, scale, or rotate" );
465  }
466 
467  }
468 
469  return n3D;
470 }
471 
472 
474 {
475  T token;
476  BOARD_ITEM* item;
477  LOCALE_IO toggle;
478 
479  m_groupInfos.clear();
480 
481  // FOOTPRINTS can be prefixed with an initial block of single line comments and these are
482  // kept for Format() so they round trip in s-expression form. BOARDs might eventually do
483  // the same, but currently do not.
484  std::unique_ptr<wxArrayString> initial_comments( ReadCommentLines() );
485 
486  token = CurTok();
487 
488  if( token != T_LEFT )
489  Expecting( T_LEFT );
490 
491  switch( NextTok() )
492  {
493  case T_kicad_pcb:
494  if( m_board == NULL )
495  m_board = new BOARD();
496 
497  item = (BOARD_ITEM*) parseBOARD();
498  break;
499 
500  case T_module: // legacy token
501  case T_footprint:
502  item = (BOARD_ITEM*) parseFOOTPRINT( initial_comments.release() );
503  break;
504 
505  default:
506  wxString err;
507  err.Printf( _( "Unknown token \"%s\"" ), FromUTF8() );
508  THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
509  }
510 
511  resolveGroups( item );
512 
513  return item;
514 }
515 
516 
518 {
519  try
520  {
521  return parseBOARD_unchecked();
522  }
523  catch( const PARSE_ERROR& parse_error )
524  {
525  if( m_tooRecent )
526  throw FUTURE_FORMAT_ERROR( parse_error, GetRequiredVersion() );
527  else
528  throw;
529  }
530 }
531 
532 
534 {
535  T token;
536  std::map<wxString, wxString> properties;
537 
538  parseHeader();
539 
540  std::vector<BOARD_ITEM*> bulkAddedItems;
541  BOARD_ITEM* item = nullptr;
542 
543  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
544  {
545  if( token != T_LEFT )
546  Expecting( T_LEFT );
547 
548  token = NextTok();
549 
550  if( token == T_page && m_requiredVersion <= 20200119 )
551  token = T_paper;
552 
553  switch( token )
554  {
555  case T_general:
556  parseGeneralSection();
557  break;
558 
559  case T_paper:
560  parsePAGE_INFO();
561  break;
562 
563  case T_title_block:
564  parseTITLE_BLOCK();
565  break;
566 
567  case T_layers:
568  parseLayers();
569  break;
570 
571  case T_setup:
572  parseSetup();
573  break;
574 
575  case T_property:
576  properties.insert( parseProperty() );
577  break;
578 
579  case T_net:
580  parseNETINFO_ITEM();
581  break;
582 
583  case T_net_class:
584  parseNETCLASS();
585  m_board->m_LegacyNetclassesLoaded = true;
586  break;
587 
588  case T_gr_arc:
589  case T_gr_curve:
590  case T_gr_line:
591  case T_gr_poly:
592  case T_gr_circle:
593  case T_gr_rect:
594  item = parsePCB_SHAPE();
595  m_board->Add( item, ADD_MODE::BULK_APPEND );
596  bulkAddedItems.push_back( item );
597  break;
598 
599  case T_gr_text:
600  item = parsePCB_TEXT();
601  m_board->Add( item, ADD_MODE::BULK_APPEND );
602  bulkAddedItems.push_back( item );
603  break;
604 
605  case T_dimension:
606  item = parseDIMENSION();
607  m_board->Add( item, ADD_MODE::BULK_APPEND );
608  bulkAddedItems.push_back( item );
609  break;
610 
611  case T_module: // legacy token
612  case T_footprint:
613  item = parseFOOTPRINT();
614  m_board->Add( item, ADD_MODE::BULK_APPEND );
615  bulkAddedItems.push_back( item );
616  break;
617 
618  case T_segment:
619  item = parseTRACK();
620  m_board->Add( item, ADD_MODE::BULK_APPEND );
621  bulkAddedItems.push_back( item );
622  break;
623 
624  case T_arc:
625  item = parseARC();
626  m_board->Add( item, ADD_MODE::BULK_APPEND );
627  bulkAddedItems.push_back( item );
628  break;
629 
630  case T_group:
631  parseGROUP( m_board );
632  break;
633 
634  case T_via:
635  item = parseVIA();
636  m_board->Add( item, ADD_MODE::BULK_APPEND );
637  bulkAddedItems.push_back( item );
638  break;
639 
640  case T_zone:
641  item = parseZONE( m_board );
642  m_board->Add( item, ADD_MODE::BULK_APPEND );
643  bulkAddedItems.push_back( item );
644  break;
645 
646  case T_target:
647  item = parsePCB_TARGET();
648  m_board->Add( item, ADD_MODE::BULK_APPEND );
649  bulkAddedItems.push_back( item );
650  break;
651 
652  default:
653  wxString err;
654  err.Printf( _( "Unknown token \"%s\"" ), FromUTF8() );
655  THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
656  }
657  }
658 
659  if( bulkAddedItems.size() > 0 )
660  m_board->FinalizeBulkAdd( bulkAddedItems );
661 
662  m_board->SetProperties( properties );
663 
664  if( m_undefinedLayers.size() > 0 )
665  {
666  bool deleteItems;
667  std::vector<BOARD_ITEM*> deleteList;
668  wxString msg = wxString::Format( _( "Items found on undefined layers. Do you wish to\n"
669  "rescue them to the User.Comments layer?" ) );
670  wxString details = wxString::Format( _( "Undefined layers:" ) );
671 
672  for( const wxString& undefinedLayer : m_undefinedLayers )
673  details += wxT( "\n " ) + undefinedLayer;
674 
675  wxRichMessageDialog dlg( nullptr, msg, _( "Warning" ),
676  wxYES_NO | wxCANCEL | wxCENTRE | wxICON_WARNING | wxSTAY_ON_TOP );
677  dlg.ShowDetailedText( details );
678  dlg.SetYesNoCancelLabels( _( "Rescue" ), _( "Delete" ), _( "Cancel" ) );
679 
680  switch( dlg.ShowModal() )
681  {
682  case wxID_YES: deleteItems = false; break;
683  case wxID_NO: deleteItems = true; break;
684  case wxID_CANCEL:
685  default: THROW_IO_ERROR( wxT( "CANCEL" ) );
686  }
687 
688  auto visitItem = [&]( BOARD_ITEM* curr_item )
689  {
690  if( curr_item->GetLayer() == Rescue )
691  {
692  if( deleteItems )
693  deleteList.push_back( curr_item );
694  else
695  curr_item->SetLayer( Cmts_User );
696  }
697  };
698 
699  for( auto segm : m_board->Tracks() )
700  {
701  if( segm->Type() == PCB_VIA_T )
702  {
703  VIA* via = (VIA*) segm;
704  PCB_LAYER_ID top_layer, bottom_layer;
705 
706  if( via->GetViaType() == VIATYPE::THROUGH )
707  continue;
708 
709  via->LayerPair( &top_layer, &bottom_layer );
710 
711  if( top_layer == Rescue || bottom_layer == Rescue )
712  {
713  if( deleteItems )
714  deleteList.push_back( via );
715  else
716  {
717  if( top_layer == Rescue )
718  top_layer = F_Cu;
719 
720  if( bottom_layer == Rescue )
721  bottom_layer = B_Cu;
722 
723  via->SetLayerPair( top_layer, bottom_layer );
724  }
725  }
726  }
727  else
728  visitItem( segm );
729  }
730 
731  for( BOARD_ITEM* zone : m_board->Zones() )
732  visitItem( zone );
733 
734  for( BOARD_ITEM* drawing : m_board->Drawings() )
735  visitItem( drawing );
736 
737  for( FOOTPRINT* fp : m_board->Footprints() )
738  {
739  for( BOARD_ITEM* drawing : fp->GraphicalItems() )
740  visitItem( drawing );
741 
742  for( BOARD_ITEM* zone : fp->Zones() )
743  visitItem( zone );
744  }
745 
746  for( BOARD_ITEM* curr_item : deleteList )
747  m_board->Delete( curr_item );
748 
749  m_undefinedLayers.clear();
750  }
751 
752  return m_board;
753 }
754 
755 
757 {
758  auto getItem = [&]( const KIID& aId )
759  {
760  BOARD_ITEM* aItem = nullptr;
761 
762  if( dynamic_cast<BOARD*>( aParent ) )
763  {
764  aItem = static_cast<BOARD*>( aParent )->GetItem( aId );
765  }
766  else if( aParent->Type() == PCB_FOOTPRINT_T )
767  {
768  static_cast<FOOTPRINT*>( aParent )->RunOnChildren(
769  [&]( BOARD_ITEM* child )
770  {
771  if( child->m_Uuid == aId )
772  aItem = child;
773  } );
774  }
775 
776  return aItem;
777  };
778 
779  // Now that we've parsed the other Uuids in the file we can resolve the uuids referred
780  // to in the group declarations we saw.
781  //
782  // First add all group objects so subsequent GetItem() calls for nested groups work.
783 
784  for( size_t idx = 0; idx < m_groupInfos.size(); idx++ )
785  {
786  GROUP_INFO& aGrp = m_groupInfos[idx];
787  PCB_GROUP* group = new PCB_GROUP( aGrp.parent );
788 
789  group->SetName( aGrp.name );
790  const_cast<KIID&>( group->m_Uuid ) = aGrp.uuid;
791 
792  if( aGrp.parent->Type() == PCB_FOOTPRINT_T )
793  static_cast<FOOTPRINT*>( aGrp.parent )->Add( group );
794  else
795  static_cast<BOARD*>( aGrp.parent )->Add( group );
796  }
797 
798  wxString error;
799 
800  for( size_t idx = 0; idx < m_groupInfos.size(); idx++ )
801  {
802  GROUP_INFO& aGrp = m_groupInfos[idx];
803  BOARD_ITEM* bItem = getItem( aGrp.uuid );
804 
805  if( bItem == nullptr || bItem->Type() != PCB_GROUP_T )
806  continue;
807 
808  PCB_GROUP* group = static_cast<PCB_GROUP*>( bItem );
809 
810  for( const KIID& aUuid : aGrp.memberUuids )
811  {
812  BOARD_ITEM* item;
813 
814  if( m_resetKIIDs )
815  item = getItem( m_resetKIIDMap[ aUuid.AsString() ] );
816  else
817  item = getItem( aUuid );
818 
819  if( item && item->Type() != NOT_USED )
820  {
821  switch( item->Type() )
822  {
823  // We used to allow fp items in non-footprint groups. It was a mistake.
824  case PCB_FP_TEXT_T:
825  case PCB_FP_SHAPE_T:
826  case PCB_FP_ZONE_T:
827  if( item->GetParent() == group->GetParent() )
828  group->AddItem( item );
829 
830  break;
831 
832  // This is the deleted item singleton, which means we didn't find the uuid.
833  case NOT_USED:
834  break;
835 
836  default:
837  group->AddItem( item );
838  }
839  }
840  }
841  }
842 
843  // Don't allow group cycles
844  if( m_board )
845  m_board->GroupsSanityCheck( true );
846 }
847 
848 
850 {
851  wxCHECK_RET( CurTok() == T_kicad_pcb,
852  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a header." ) );
853 
854  NeedLEFT();
855 
856  T tok = NextTok();
857 
858  if( tok == T_version )
859  {
860  m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
861  m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION );
862  NeedRIGHT();
863 
864  NeedLEFT();
865  NeedSYMBOL();
866  NeedSYMBOL();
867 
868  // Older formats included build data
869  if( m_requiredVersion < BOARD_FILE_HOST_VERSION )
870  NeedSYMBOL();
871 
872  NeedRIGHT();
873  }
874  else
875  {
876  m_requiredVersion = 20201115; // Last version before we started writing version #s
877  // in footprint files as well as board files.
878  m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION );
879 
880  // Skip the host name and host build version information.
881  NeedSYMBOL();
882  NeedSYMBOL();
883  NeedRIGHT();
884  }
885 
886  m_board->SetFileFormatVersionAtLoad( m_requiredVersion );
887 }
888 
889 
891 {
892  wxCHECK_RET( CurTok() == T_general,
893  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
894  wxT( " as a general section." ) );
895 
896  T token;
897 
898  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
899  {
900  if( token != T_LEFT )
901  Expecting( T_LEFT );
902 
903  token = NextTok();
904 
905  switch( token )
906  {
907  case T_thickness:
908  m_board->GetDesignSettings().SetBoardThickness( parseBoardUnits( T_thickness ) );
909  NeedRIGHT();
910  break;
911 
912  default: // Skip everything but the board thickness.
913  while( ( token = NextTok() ) != T_RIGHT )
914  {
915  if( !IsSymbol( token ) && token != T_NUMBER )
916  Expecting( "symbol or number" );
917  }
918  }
919  }
920 }
921 
922 
924 {
925  wxCHECK_RET( ( CurTok() == T_page && m_requiredVersion <= 20200119 ) || CurTok() == T_paper,
926  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a PAGE_INFO." ) );
927 
928  T token;
929  PAGE_INFO pageInfo;
930 
931  NeedSYMBOL();
932 
933  wxString pageType = FromUTF8();
934 
935  if( !pageInfo.SetType( pageType ) )
936  {
937  wxString err;
938  err.Printf( _( "Page type \"%s\" is not valid " ), FromUTF8() );
939  THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
940  }
941 
942  if( pageType == PAGE_INFO::Custom )
943  {
944  double width = parseDouble( "width" ); // width in mm
945 
946  // Perform some controls to avoid crashes if the size is edited by hands
947  if( width < 100.0 )
948  width = 100.0;
949  else if( width > 1200.0 )
950  width = 1200.0;
951 
952  double height = parseDouble( "height" ); // height in mm
953 
954  if( height < 100.0 )
955  height = 100.0;
956  else if( height > 1200.0 )
957  height = 1200.0;
958 
959  pageInfo.SetWidthMils( Mm2mils( width ) );
960  pageInfo.SetHeightMils( Mm2mils( height ) );
961  }
962 
963  token = NextTok();
964 
965  if( token == T_portrait )
966  {
967  pageInfo.SetPortrait( true );
968  NeedRIGHT();
969  }
970  else if( token != T_RIGHT )
971  {
972  Expecting( "portrait|)" );
973  }
974 
975  m_board->SetPageSettings( pageInfo );
976 }
977 
978 
980 {
981  wxCHECK_RET( CurTok() == T_title_block,
982  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
983  wxT( " as TITLE_BLOCK." ) );
984 
985  T token;
986  TITLE_BLOCK titleBlock;
987 
988  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
989  {
990  if( token != T_LEFT )
991  Expecting( T_LEFT );
992 
993  token = NextTok();
994 
995  switch( token )
996  {
997  case T_title:
998  NextTok();
999  titleBlock.SetTitle( FromUTF8() );
1000  break;
1001 
1002  case T_date:
1003  NextTok();
1004  titleBlock.SetDate( FromUTF8() );
1005  break;
1006 
1007  case T_rev:
1008  NextTok();
1009  titleBlock.SetRevision( FromUTF8() );
1010  break;
1011 
1012  case T_company:
1013  NextTok();
1014  titleBlock.SetCompany( FromUTF8() );
1015  break;
1016 
1017  case T_comment:
1018  {
1019  int commentNumber = parseInt( "comment" );
1020 
1021  switch( commentNumber )
1022  {
1023  case 1:
1024  NextTok();
1025  titleBlock.SetComment( 0, FromUTF8() );
1026  break;
1027 
1028  case 2:
1029  NextTok();
1030  titleBlock.SetComment( 1, FromUTF8() );
1031  break;
1032 
1033  case 3:
1034  NextTok();
1035  titleBlock.SetComment( 2, FromUTF8() );
1036  break;
1037 
1038  case 4:
1039  NextTok();
1040  titleBlock.SetComment( 3, FromUTF8() );
1041  break;
1042 
1043  case 5:
1044  NextTok();
1045  titleBlock.SetComment( 4, FromUTF8() );
1046  break;
1047 
1048  case 6:
1049  NextTok();
1050  titleBlock.SetComment( 5, FromUTF8() );
1051  break;
1052 
1053  case 7:
1054  NextTok();
1055  titleBlock.SetComment( 6, FromUTF8() );
1056  break;
1057 
1058  case 8:
1059  NextTok();
1060  titleBlock.SetComment( 7, FromUTF8() );
1061  break;
1062 
1063  case 9:
1064  NextTok();
1065  titleBlock.SetComment( 8, FromUTF8() );
1066  break;
1067 
1068  default:
1069  wxString err;
1070  err.Printf( wxT( "%d is not a valid title block comment number" ), commentNumber );
1071  THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
1072  }
1073  }
1074  break;
1075 
1076  default:
1077  Expecting( "title, date, rev, company, or comment" );
1078  }
1079 
1080  NeedRIGHT();
1081  }
1082 
1083  m_board->SetTitleBlock( titleBlock );
1084 }
1085 
1086 
1088 {
1089  T token;
1090 
1091  std::string name;
1092  std::string userName;
1093  std::string type;
1094  bool isVisible = true;
1095 
1096  aLayer->clear();
1097 
1098  if( CurTok() != T_LEFT )
1099  Expecting( T_LEFT );
1100 
1101  // this layer_num is not used, we DO depend on LAYER_T however.
1102  LAYER_NUM layer_num = parseInt( "layer index" );
1103 
1104  NeedSYMBOLorNUMBER();
1105  name = CurText();
1106 
1107  NeedSYMBOL();
1108  type = CurText();
1109 
1110  token = NextTok();
1111 
1112  // @todo Figure out why we are looking for a hide token in the layer definition.
1113  if( token == T_hide )
1114  {
1115  isVisible = false;
1116  NeedRIGHT();
1117  }
1118  else if( token == T_STRING )
1119  {
1120  userName = CurText();
1121  NeedRIGHT();
1122  }
1123  else if( token != T_RIGHT )
1124  {
1125  Expecting( "hide, user defined name, or )" );
1126  }
1127 
1128  aLayer->m_name = FROM_UTF8( name.c_str() );
1129  aLayer->m_type = LAYER::ParseType( type.c_str() );
1130  aLayer->m_number = layer_num;
1131  aLayer->m_visible = isVisible;
1132 
1133  if( !userName.empty() )
1134  aLayer->m_userName = FROM_UTF8( userName.c_str() );
1135 
1136  // The canonical name will get reset back to the default for copper layer on the next
1137  // save. The user defined name is now a separate optional layer token from the canonical
1138  // name.
1139  if( aLayer->m_name != LSET::Name( static_cast<PCB_LAYER_ID>( aLayer->m_number ) ) )
1140  aLayer->m_userName = aLayer->m_name;
1141 }
1142 
1143 
1145 {
1146  T token;
1147  wxString name;
1148  int dielectric_idx = 1; // the index of dielectric layers
1149  BOARD_STACKUP& stackup = m_board->GetDesignSettings().GetStackupDescriptor();
1150 
1151  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1152  {
1153  if( CurTok() != T_LEFT )
1154  Expecting( T_LEFT );
1155 
1156  token = NextTok();
1157 
1158  if( token != T_layer )
1159  {
1160  switch( token )
1161  {
1162  case T_copper_finish:
1163  NeedSYMBOL();
1164  stackup.m_FinishType = FromUTF8();
1165  NeedRIGHT();
1166  break;
1167 
1168  case T_edge_plating:
1169  token = NextTok();
1170  stackup.m_EdgePlating = token == T_yes;
1171  NeedRIGHT();
1172  break;
1173 
1174  case T_dielectric_constraints:
1175  token = NextTok();
1176  stackup.m_HasDielectricConstrains = token == T_yes;
1177  NeedRIGHT();
1178  break;
1179 
1180  case T_edge_connector:
1181  token = NextTok();
1183 
1184  if( token == T_yes )
1186  else if( token == T_bevelled )
1188 
1189  NeedRIGHT();
1190  break;
1191 
1192  case T_castellated_pads:
1193  token = NextTok();
1194  stackup.m_CastellatedPads = token == T_yes;
1195  NeedRIGHT();
1196  break;
1197 
1198  default:
1199  // Currently, skip this item if not defined, because the stackup def
1200  // is a moving target
1201  //Expecting( "copper_finish, edge_plating, dielectric_constrains, edge_connector, castellated_pads" );
1202  skipCurrent();
1203  break;
1204  }
1205 
1206  continue;
1207  }
1208 
1209  NeedSYMBOL();
1210  name = FromUTF8();
1211 
1212  // init the layer id. For dielectric, layer id = UNDEFINED_LAYER
1213  PCB_LAYER_ID layerId = m_board->GetLayerID( name );
1214 
1215  // Init the type
1217 
1218  if( layerId == F_SilkS || layerId == B_SilkS )
1219  type = BS_ITEM_TYPE_SILKSCREEN;
1220  else if( layerId == F_Mask || layerId == B_Mask )
1221  type = BS_ITEM_TYPE_SOLDERMASK;
1222  else if( layerId == F_Paste || layerId == B_Paste )
1223  type = BS_ITEM_TYPE_SOLDERPASTE;
1224  else if( layerId == UNDEFINED_LAYER )
1225  type = BS_ITEM_TYPE_DIELECTRIC;
1226  else if( layerId >= F_Cu && layerId <= B_Cu )
1227  type = BS_ITEM_TYPE_COPPER;
1228 
1229  BOARD_STACKUP_ITEM* item = nullptr;
1230 
1231  if( type != BS_ITEM_TYPE_UNDEFINED )
1232  {
1233  item = new BOARD_STACKUP_ITEM( type );
1234  item->SetBrdLayerId( layerId );
1235 
1236  if( type == BS_ITEM_TYPE_DIELECTRIC )
1237  item->SetDielectricLayerId( dielectric_idx++ );
1238 
1239  stackup.Add( item );
1240  }
1241  else
1242  Expecting( "layer_name" );
1243 
1244  bool has_next_sublayer = true;
1245  int sublayer_idx = 0; // the index of dielectric sub layers
1246  // sublayer 0 is always existing (main sublayer)
1247 
1248  while( has_next_sublayer )
1249  {
1250  has_next_sublayer = false;
1251 
1252  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1253  {
1254  if( token == T_addsublayer )
1255  {
1256  has_next_sublayer = true;
1257  break;
1258  }
1259 
1260  if( token == T_LEFT )
1261  {
1262  token = NextTok();
1263 
1264  switch( token )
1265  {
1266  case T_type:
1267  NeedSYMBOL();
1268  item->SetTypeName( FromUTF8() );
1269  NeedRIGHT();
1270  break;
1271 
1272  case T_thickness:
1273  item->SetThickness( parseBoardUnits( T_thickness ), sublayer_idx );
1274  token = NextTok();
1275 
1276  if( token == T_LEFT )
1277  break;
1278 
1279  if( token == T_locked )
1280  {
1281  // Dielectric thickness can be locked (for impedance controlled layers)
1282  if( type == BS_ITEM_TYPE_DIELECTRIC )
1283  item->SetThicknessLocked( true, sublayer_idx );
1284 
1285  NeedRIGHT();
1286  }
1287  break;
1288 
1289  case T_material:
1290  NeedSYMBOL();
1291  item->SetMaterial( FromUTF8(), sublayer_idx );
1292  NeedRIGHT();
1293  break;
1294 
1295  case T_epsilon_r:
1296  NextTok();
1297  item->SetEpsilonR( parseDouble(), sublayer_idx );
1298  NeedRIGHT();
1299  break;
1300 
1301  case T_loss_tangent:
1302  NextTok();
1303  item->SetLossTangent( parseDouble(), sublayer_idx );
1304  NeedRIGHT();
1305  break;
1306 
1307  case T_color:
1308  NeedSYMBOL();
1309  item->SetColor( FromUTF8() );
1310  NeedRIGHT();
1311  break;
1312 
1313  default:
1314  // Currently, skip this item if not defined, because the stackup def
1315  // is a moving target
1316  //Expecting( "type, thickness, material, epsilon_r, loss_tangent, color" );
1317  skipCurrent();
1318  }
1319  }
1320  }
1321 
1322  if( has_next_sublayer ) // Prepare reading the next sublayer description
1323  {
1324  sublayer_idx++;
1325  item->AddDielectricPrms( sublayer_idx );
1326  }
1327  }
1328  }
1329 
1330  if( token != T_RIGHT )
1331  {
1332  Expecting( ")" );
1333  }
1334 
1335  // Success:
1336  m_board->GetDesignSettings().m_HasStackup = true;
1337 }
1338 
1339 
1340 void PCB_PARSER::createOldLayerMapping( std::unordered_map< std::string, std::string >& aMap )
1341 {
1342  // N.B. This mapping only includes Italian, Polish and French as they were the only languages that
1343  // mapped the layer names as of cc2022b1ac739aa673d2a0b7a2047638aa7a47b3 (kicad-i18n) when the
1344  // bug was fixed in KiCad source.
1345 
1346  // Italian
1347  aMap["Adesivo.Retro"] = "B.Adhes";
1348  aMap["Adesivo.Fronte"] = "F.Adhes";
1349  aMap["Pasta.Retro"] = "B.Paste";
1350  aMap["Pasta.Fronte"] = "F.Paste";
1351  aMap["Serigrafia.Retro"] = "B.SilkS";
1352  aMap["Serigrafia.Fronte"] = "F.SilkS";
1353  aMap["Maschera.Retro"] = "B.Mask";
1354  aMap["Maschera.Fronte"] = "F.Mask";
1355  aMap["Grafica"] = "Dwgs.User";
1356  aMap["Commenti"] = "Cmts.User";
1357  aMap["Eco1"] = "Eco1.User";
1358  aMap["Eco2"] = "Eco2.User";
1359  aMap["Contorno.scheda"] = "Edge.Cuts";
1360 
1361  // Polish
1362  aMap["Kleju_Dolna"] = "B.Adhes";
1363  aMap["Kleju_Gorna"] = "F.Adhes";
1364  aMap["Pasty_Dolna"] = "B.Paste";
1365  aMap["Pasty_Gorna"] = "F.Paste";
1366  aMap["Opisowa_Dolna"] = "B.SilkS";
1367  aMap["Opisowa_Gorna"] = "F.SilkS";
1368  aMap["Maski_Dolna"] = "B.Mask";
1369  aMap["Maski_Gorna"] = "F.Mask";
1370  aMap["Rysunkowa"] = "Dwgs.User";
1371  aMap["Komentarzy"] = "Cmts.User";
1372  aMap["ECO1"] = "Eco1.User";
1373  aMap["ECO2"] = "Eco2.User";
1374  aMap["Krawedziowa"] = "Edge.Cuts";
1375 
1376  // French
1377  aMap["Dessous.Adhes"] = "B.Adhes";
1378  aMap["Dessus.Adhes"] = "F.Adhes";
1379  aMap["Dessous.Pate"] = "B.Paste";
1380  aMap["Dessus.Pate"] = "F.Paste";
1381  aMap["Dessous.SilkS"] = "B.SilkS";
1382  aMap["Dessus.SilkS"] = "F.SilkS";
1383  aMap["Dessous.Masque"] = "B.Mask";
1384  aMap["Dessus.Masque"] = "F.Mask";
1385  aMap["Dessin.User"] = "Dwgs.User";
1386  aMap["Contours.Ci"] = "Edge.Cuts";
1387 }
1388 
1389 
1391 {
1392  wxCHECK_RET( CurTok() == T_layers,
1393  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as layers." ) );
1394 
1395  T token;
1396  LSET visibleLayers;
1397  LSET enabledLayers;
1398  int copperLayerCount = 0;
1399  LAYER layer;
1400  bool anyHidden = false;
1401 
1402  std::unordered_map< std::string, std::string > v3_layer_names;
1403  std::vector<LAYER> cu;
1404 
1405  createOldLayerMapping( v3_layer_names );
1406 
1407  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1408  {
1409  parseLayer( &layer );
1410 
1411  if( layer.m_type == LT_UNDEFINED ) // it's a non-copper layer
1412  break;
1413 
1414  cu.push_back( layer ); // it's copper
1415  }
1416 
1417  // All Cu layers are parsed, but not the non-cu layers here.
1418 
1419  // The original *.kicad_pcb file format and the inverted
1420  // Cu stack format both have all the Cu layers first, so use this
1421  // trick to handle either. The layer number in the (layers ..)
1422  // s-expression element are ignored.
1423  if( cu.size() )
1424  {
1425  // Rework the layer numbers, which changed when the Cu stack
1426  // was flipped. So we instead use position in the list.
1427  cu[cu.size()-1].m_number = B_Cu;
1428 
1429  for( unsigned i=0; i < cu.size()-1; ++i )
1430  {
1431  cu[i].m_number = i;
1432  }
1433 
1434  for( std::vector<LAYER>::const_iterator it = cu.begin(); it<cu.end(); ++it )
1435  {
1436  enabledLayers.set( it->m_number );
1437 
1438  if( it->m_visible )
1439  visibleLayers.set( it->m_number );
1440  else
1441  anyHidden = true;
1442 
1443  m_board->SetLayerDescr( PCB_LAYER_ID( it->m_number ), *it );
1444 
1445  UTF8 name = it->m_name;
1446 
1447  m_layerIndices[ name ] = PCB_LAYER_ID( it->m_number );
1448  m_layerMasks[ name ] = LSET( PCB_LAYER_ID( it->m_number ) );
1449  }
1450 
1451  copperLayerCount = cu.size();
1452  }
1453 
1454  // process non-copper layers
1455  while( token != T_RIGHT )
1456  {
1457  LAYER_ID_MAP::const_iterator it = m_layerIndices.find( UTF8( layer.m_name ) );
1458 
1459  if( it == m_layerIndices.end() )
1460  {
1461  auto new_layer_it = v3_layer_names.find( layer.m_name.ToStdString() );
1462 
1463  if( new_layer_it != v3_layer_names.end() )
1464  it = m_layerIndices.find( new_layer_it->second );
1465 
1466  if( it == m_layerIndices.end() )
1467  {
1468  wxString error = wxString::Format(
1469  _( "Layer \"%s\" in file \"%s\" at line %d, is not in fixed layer hash" ),
1470  layer.m_name,
1471  CurSource(),
1472  CurLineNumber(),
1473  CurOffset()
1474  );
1475 
1476  THROW_IO_ERROR( error );
1477  }
1478 
1479  // If we are here, then we have found a translated layer name. Put it in the maps so that
1480  // items on this layer get the appropriate layer ID number
1481  m_layerIndices[ UTF8( layer.m_name ) ] = it->second;
1482  m_layerMasks[ UTF8( layer.m_name ) ] = it->second;
1483  layer.m_name = it->first;
1484  }
1485 
1486  layer.m_number = it->second;
1487  enabledLayers.set( layer.m_number );
1488 
1489  if( layer.m_visible )
1490  visibleLayers.set( layer.m_number );
1491  else
1492  anyHidden = true;
1493 
1494  m_board->SetLayerDescr( it->second, layer );
1495 
1496  token = NextTok();
1497 
1498  if( token != T_LEFT )
1499  break;
1500 
1501  parseLayer( &layer );
1502  }
1503 
1504  // We need at least 2 copper layers and there must be an even number of them.
1505  if( copperLayerCount < 2 || (copperLayerCount % 2) != 0 )
1506  {
1507  wxString err = wxString::Format(
1508  _( "%d is not a valid layer count" ), copperLayerCount );
1509 
1510  THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
1511  }
1512 
1513  m_board->SetCopperLayerCount( copperLayerCount );
1514  m_board->SetEnabledLayers( enabledLayers );
1515 
1516  // Only set this if any layers were explicitly marked as hidden. Otherwise, we want to leave
1517  // this alone; default visibility will show everything
1518  if( anyHidden )
1519  m_board->m_LegacyVisibleLayers = visibleLayers;
1520 }
1521 
1522 
1523 template<class T, class M>
1524 T PCB_PARSER::lookUpLayer( const M& aMap )
1525 {
1526  // avoid constructing another std::string, use lexer's directly
1527  typename M::const_iterator it = aMap.find( curText );
1528 
1529  if( it == aMap.end() )
1530  {
1531  m_undefinedLayers.insert( curText );
1532  return Rescue;
1533  }
1534 
1535  // Some files may have saved items to the Rescue Layer due to an issue in v5
1536  if( it->second == Rescue )
1537  m_undefinedLayers.insert( curText );
1538 
1539  return it->second;
1540 }
1541 
1542 
1544 {
1545  wxCHECK_MSG( CurTok() == T_layer, UNDEFINED_LAYER,
1546  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as layer." ) );
1547 
1548  NextTok();
1549 
1550  PCB_LAYER_ID layerIndex = lookUpLayer<PCB_LAYER_ID>( m_layerIndices );
1551 
1552  // Handle closing ) in object parser.
1553 
1554  return layerIndex;
1555 }
1556 
1557 
1559 {
1560  wxCHECK_MSG( CurTok() == T_layers, LSET(),
1561  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
1562  wxT( " as item layer mask." ) );
1563 
1564  LSET layerMask;
1565 
1566  for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
1567  {
1568  LSET mask = lookUpLayer<LSET>( m_layerMasks );
1569  layerMask |= mask;
1570  }
1571 
1572  return layerMask;
1573 }
1574 
1575 
1577 {
1578  wxCHECK_RET( CurTok() == T_setup,
1579  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as setup." ) );
1580 
1581  T token;
1582  NETCLASS* defaultNetClass = m_board->GetDesignSettings().GetDefault();
1583  BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
1584  ZONE_SETTINGS& zoneSettings = designSettings.GetDefaultZoneSettings();
1585 
1586  // Missing soldermask min width value means that the user has set the value to 0 and
1587  // not the default value (0.25mm)
1588  designSettings.m_SolderMaskMinWidth = 0;
1589 
1590  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1591  {
1592  if( token != T_LEFT )
1593  Expecting( T_LEFT );
1594 
1595  token = NextTok();
1596 
1597  switch( token )
1598  {
1599  case T_stackup:
1600  parseBoardStackup();
1601  break;
1602 
1603  case T_last_trace_width: // not used now
1604  /* lastTraceWidth =*/ parseBoardUnits( T_last_trace_width );
1605  NeedRIGHT();
1606  break;
1607 
1608  case T_user_trace_width:
1609  {
1610  // Make room for the netclass value
1611  if( designSettings.m_TrackWidthList.empty() )
1612  designSettings.m_TrackWidthList.emplace_back( 0 );
1613 
1614  designSettings.m_TrackWidthList.push_back( parseBoardUnits( T_user_trace_width ) );
1615  m_board->m_LegacyDesignSettingsLoaded = true;
1616  NeedRIGHT();
1617  break;
1618  }
1619 
1620  case T_trace_clearance:
1621  defaultNetClass->SetClearance( parseBoardUnits( T_trace_clearance ) );
1622  m_board->m_LegacyDesignSettingsLoaded = true;
1623  NeedRIGHT();
1624  break;
1625 
1626  case T_zone_clearance:
1627  zoneSettings.m_ZoneClearance = parseBoardUnits( T_zone_clearance );
1628  m_board->m_LegacyDesignSettingsLoaded = true;
1629  NeedRIGHT();
1630  break;
1631 
1632  case T_zone_45_only:
1633  zoneSettings.m_Zone_45_Only = parseBool();
1634  m_board->m_LegacyDesignSettingsLoaded = true;
1635  NeedRIGHT();
1636  break;
1637 
1638  case T_clearance_min:
1639  designSettings.m_MinClearance = parseBoardUnits( T_clearance_min );
1640  m_board->m_LegacyDesignSettingsLoaded = true;
1641  NeedRIGHT();
1642  break;
1643 
1644  case T_trace_min:
1645  designSettings.m_TrackMinWidth = parseBoardUnits( T_trace_min );
1646  m_board->m_LegacyDesignSettingsLoaded = true;
1647  NeedRIGHT();
1648  break;
1649 
1650  case T_via_size:
1651  defaultNetClass->SetViaDiameter( parseBoardUnits( T_via_size ) );
1652  m_board->m_LegacyDesignSettingsLoaded = true;
1653  NeedRIGHT();
1654  break;
1655 
1656  case T_via_drill:
1657  defaultNetClass->SetViaDrill( parseBoardUnits( T_via_drill ) );
1658  m_board->m_LegacyDesignSettingsLoaded = true;
1659  NeedRIGHT();
1660  break;
1661 
1662  case T_via_min_annulus:
1663  designSettings.m_ViasMinAnnulus = parseBoardUnits( T_via_min_annulus );
1664  m_board->m_LegacyDesignSettingsLoaded = true;
1665  NeedRIGHT();
1666  break;
1667 
1668  case T_via_min_size:
1669  designSettings.m_ViasMinSize = parseBoardUnits( T_via_min_size );
1670  m_board->m_LegacyDesignSettingsLoaded = true;
1671  NeedRIGHT();
1672  break;
1673 
1674  case T_through_hole_min:
1675  designSettings.m_MinThroughDrill = parseBoardUnits( T_through_hole_min );
1676  m_board->m_LegacyDesignSettingsLoaded = true;
1677  NeedRIGHT();
1678  break;
1679 
1680  // Legacy token for T_through_hole_min
1681  case T_via_min_drill:
1682  designSettings.m_MinThroughDrill = parseBoardUnits( T_via_min_drill );
1683  m_board->m_LegacyDesignSettingsLoaded = true;
1684  NeedRIGHT();
1685  break;
1686 
1687  case T_hole_to_hole_min:
1688  designSettings.m_HoleToHoleMin = parseBoardUnits( T_hole_to_hole_min );
1689  m_board->m_LegacyDesignSettingsLoaded = true;
1690  NeedRIGHT();
1691  break;
1692 
1693  case T_user_via:
1694  {
1695  int viaSize = parseBoardUnits( "user via size" );
1696  int viaDrill = parseBoardUnits( "user via drill" );
1697 
1698  // Make room for the netclass value
1699  if( designSettings.m_ViasDimensionsList.empty() )
1700  designSettings.m_ViasDimensionsList.emplace_back( VIA_DIMENSION( 0, 0 ) );
1701 
1702  designSettings.m_ViasDimensionsList.emplace_back( VIA_DIMENSION( viaSize, viaDrill ) );
1703  m_board->m_LegacyDesignSettingsLoaded = true;
1704  NeedRIGHT();
1705  }
1706  break;
1707 
1708  case T_uvia_size:
1709  defaultNetClass->SetuViaDiameter( parseBoardUnits( T_uvia_size ) );
1710  m_board->m_LegacyDesignSettingsLoaded = true;
1711  NeedRIGHT();
1712  break;
1713 
1714  case T_uvia_drill:
1715  defaultNetClass->SetuViaDrill( parseBoardUnits( T_uvia_drill ) );
1716  m_board->m_LegacyDesignSettingsLoaded = true;
1717  NeedRIGHT();
1718  break;
1719 
1720  case T_uvias_allowed:
1721  designSettings.m_MicroViasAllowed = parseBool();
1722  m_board->m_LegacyDesignSettingsLoaded = true;
1723  NeedRIGHT();
1724  break;
1725 
1726  case T_blind_buried_vias_allowed:
1727  designSettings.m_BlindBuriedViaAllowed = parseBool();
1728  m_board->m_LegacyDesignSettingsLoaded = true;
1729  NeedRIGHT();
1730  break;
1731 
1732  case T_uvia_min_size:
1733  designSettings.m_MicroViasMinSize = parseBoardUnits( T_uvia_min_size );
1734  m_board->m_LegacyDesignSettingsLoaded = true;
1735  NeedRIGHT();
1736  break;
1737 
1738  case T_uvia_min_drill:
1739  designSettings.m_MicroViasMinDrill = parseBoardUnits( T_uvia_min_drill );
1740  m_board->m_LegacyDesignSettingsLoaded = true;
1741  NeedRIGHT();
1742  break;
1743 
1744  case T_user_diff_pair:
1745  {
1746  int width = parseBoardUnits( "user diff-pair width" );
1747  int gap = parseBoardUnits( "user diff-pair gap" );
1748  int viaGap = parseBoardUnits( "user diff-pair via gap" );
1749  designSettings.m_DiffPairDimensionsList.emplace_back( DIFF_PAIR_DIMENSION( width, gap, viaGap ) );
1750  m_board->m_LegacyDesignSettingsLoaded = true;
1751  NeedRIGHT();
1752  }
1753  break;
1754 
1755  case T_segment_width: // note: legacy (pre-6.0) token
1756  designSettings.m_LineThickness[ LAYER_CLASS_COPPER ] = parseBoardUnits( T_segment_width );
1757  m_board->m_LegacyDesignSettingsLoaded = true;
1758  NeedRIGHT();
1759  break;
1760 
1761  case T_edge_width: // note: legacy (pre-6.0) token
1762  designSettings.m_LineThickness[ LAYER_CLASS_EDGES ] = parseBoardUnits( T_edge_width );
1763  m_board->m_LegacyDesignSettingsLoaded = true;
1764  NeedRIGHT();
1765  break;
1766 
1767  case T_mod_edge_width: // note: legacy (pre-6.0) token
1768  designSettings.m_LineThickness[ LAYER_CLASS_SILK ] = parseBoardUnits( T_mod_edge_width );
1769  m_board->m_LegacyDesignSettingsLoaded = true;
1770  NeedRIGHT();
1771  break;
1772 
1773  case T_pcb_text_width: // note: legacy (pre-6.0) token
1774  designSettings.m_TextThickness[ LAYER_CLASS_COPPER ] = parseBoardUnits( T_pcb_text_width );
1775  m_board->m_LegacyDesignSettingsLoaded = true;
1776  NeedRIGHT();
1777  break;
1778 
1779  case T_mod_text_width: // note: legacy (pre-6.0) token
1780  designSettings.m_TextThickness[ LAYER_CLASS_SILK ] = parseBoardUnits( T_mod_text_width );
1781  m_board->m_LegacyDesignSettingsLoaded = true;
1782  NeedRIGHT();
1783  break;
1784 
1785  case T_pcb_text_size: // note: legacy (pre-6.0) token
1786  designSettings.m_TextSize[ LAYER_CLASS_COPPER ].x = parseBoardUnits( "pcb text width" );
1787  designSettings.m_TextSize[ LAYER_CLASS_COPPER ].y = parseBoardUnits( "pcb text height" );
1788  m_board->m_LegacyDesignSettingsLoaded = true;
1789  NeedRIGHT();
1790  break;
1791 
1792  case T_mod_text_size: // note: legacy (pre-6.0) token
1793  designSettings.m_TextSize[ LAYER_CLASS_SILK ].x = parseBoardUnits( "footprint text width" );
1794  designSettings.m_TextSize[ LAYER_CLASS_SILK ].y = parseBoardUnits( "footprint text height" );
1795  m_board->m_LegacyDesignSettingsLoaded = true;
1796  NeedRIGHT();
1797  break;
1798 
1799  case T_defaults:
1800  parseDefaults( designSettings );
1801  m_board->m_LegacyDesignSettingsLoaded = true;
1802  break;
1803 
1804  case T_pad_size:
1805  {
1806  wxSize sz;
1807  sz.SetWidth( parseBoardUnits( "master pad width" ) );
1808  sz.SetHeight( parseBoardUnits( "master pad height" ) );
1809  designSettings.m_Pad_Master.SetSize( sz );
1810  m_board->m_LegacyDesignSettingsLoaded = true;
1811  NeedRIGHT();
1812  }
1813  break;
1814 
1815  case T_pad_drill:
1816  {
1817  int drillSize = parseBoardUnits( T_pad_drill );
1818  designSettings.m_Pad_Master.SetDrillSize( wxSize( drillSize, drillSize ) );
1819  m_board->m_LegacyDesignSettingsLoaded = true;
1820  NeedRIGHT();
1821  }
1822  break;
1823 
1824  case T_pad_to_mask_clearance:
1825  designSettings.m_SolderMaskMargin = parseBoardUnits( T_pad_to_mask_clearance );
1826  NeedRIGHT();
1827  break;
1828 
1829  case T_solder_mask_min_width:
1830  designSettings.m_SolderMaskMinWidth = parseBoardUnits( T_solder_mask_min_width );
1831  NeedRIGHT();
1832  break;
1833 
1834  case T_pad_to_paste_clearance:
1835  designSettings.m_SolderPasteMargin = parseBoardUnits( T_pad_to_paste_clearance );
1836  NeedRIGHT();
1837  break;
1838 
1839  case T_pad_to_paste_clearance_ratio:
1840  designSettings.m_SolderPasteMarginRatio = parseDouble( T_pad_to_paste_clearance_ratio );
1841  NeedRIGHT();
1842  break;
1843 
1844  case T_aux_axis_origin:
1845  {
1846  int x = parseBoardUnits( "auxiliary origin X" );
1847  int y = parseBoardUnits( "auxiliary origin Y" );
1848  designSettings.m_AuxOrigin = wxPoint( x, y );
1849  // Aux origin still stored in board for the moment
1850  //m_board->m_LegacyDesignSettingsLoaded = true;
1851  NeedRIGHT();
1852  }
1853  break;
1854 
1855  case T_grid_origin:
1856  {
1857  int x = parseBoardUnits( "grid origin X" );
1858  int y = parseBoardUnits( "grid origin Y" );
1859  designSettings.m_GridOrigin = wxPoint( x, y );
1860  // Grid origin still stored in board for the moment
1861  //m_board->m_LegacyDesignSettingsLoaded = true;
1862  NeedRIGHT();
1863  }
1864  break;
1865 
1866  // Stored in board prior to 6.0
1867  case T_visible_elements:
1868  {
1869  // Make sure to start with DefaultVisible so all new layers are set
1870  m_board->m_LegacyVisibleItems = GAL_SET::DefaultVisible();
1871 
1872  int visible = parseHex() | MIN_VISIBILITY_MASK;
1873 
1874  for( size_t i = 0; i < sizeof( int ) * CHAR_BIT; i++ )
1875  m_board->m_LegacyVisibleItems.set( i, visible & ( 1u << i ) );
1876 
1877  NeedRIGHT();
1878  }
1879  break;
1880 
1881  case T_max_error:
1882  designSettings.m_MaxError = parseBoardUnits( T_max_error );
1883  m_board->m_LegacyDesignSettingsLoaded = true;
1884  NeedRIGHT();
1885  break;
1886 
1887  case T_filled_areas_thickness: // Note: legacy (early 5.99) token
1888  designSettings.m_ZoneFillVersion = parseBool() ? 5 : 6;
1889  m_board->m_LegacyDesignSettingsLoaded = true;
1890  NeedRIGHT();
1891  break;
1892 
1893  case T_pcbplotparams:
1894  {
1895  PCB_PLOT_PARAMS plotParams;
1896  PCB_PLOT_PARAMS_PARSER parser( reader );
1897  // parser must share the same current line as our current PCB parser
1898  // synchronize it.
1899  parser.SyncLineReaderWith( *this );
1900 
1901  plotParams.Parse( &parser );
1902  SyncLineReaderWith( parser );
1903 
1904  m_board->SetPlotOptions( plotParams );
1905  }
1906  break;
1907 
1908  default:
1909  Unexpected( CurText() );
1910  }
1911  }
1912 }
1913 
1914 
1916 {
1917  T token;
1918 
1919  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1920  {
1921  if( token != T_LEFT )
1922  Expecting( T_LEFT );
1923 
1924  token = NextTok();
1925 
1926  switch( token )
1927  {
1928  case T_edge_clearance:
1929  designSettings.m_CopperEdgeClearance = parseBoardUnits( T_edge_clearance );
1930  m_board->m_LegacyCopperEdgeClearanceLoaded = true;
1931  NeedRIGHT();
1932  break;
1933 
1934  case T_copper_line_width:
1935  designSettings.m_LineThickness[ LAYER_CLASS_COPPER ] = parseBoardUnits( token );
1936  NeedRIGHT();
1937  break;
1938 
1939  case T_copper_text_dims:
1940  parseDefaultTextDims( designSettings, LAYER_CLASS_COPPER );
1941  break;
1942 
1943  case T_courtyard_line_width:
1944  designSettings.m_LineThickness[ LAYER_CLASS_COURTYARD ] = parseBoardUnits( token );
1945  NeedRIGHT();
1946  break;
1947 
1948  case T_edge_cuts_line_width:
1949  designSettings.m_LineThickness[ LAYER_CLASS_EDGES ] = parseBoardUnits( token );
1950  NeedRIGHT();
1951  break;
1952 
1953  case T_silk_line_width:
1954  designSettings.m_LineThickness[ LAYER_CLASS_SILK ] = parseBoardUnits( token );
1955  NeedRIGHT();
1956  break;
1957 
1958  case T_silk_text_dims:
1959  parseDefaultTextDims( designSettings, LAYER_CLASS_SILK );
1960  break;
1961 
1962  case T_fab_layers_line_width:
1963  designSettings.m_LineThickness[ LAYER_CLASS_FAB ] = parseBoardUnits( token );
1964  NeedRIGHT();
1965  break;
1966 
1967  case T_fab_layers_text_dims:
1968  parseDefaultTextDims( designSettings, LAYER_CLASS_FAB );
1969  break;
1970 
1971  case T_other_layers_line_width:
1972  designSettings.m_LineThickness[ LAYER_CLASS_OTHERS ] = parseBoardUnits( token );
1973  NeedRIGHT();
1974  break;
1975 
1976  case T_other_layers_text_dims:
1977  parseDefaultTextDims( designSettings, LAYER_CLASS_OTHERS );
1978  break;
1979 
1980  case T_dimension_units:
1981  designSettings.m_DimensionUnitsMode =
1982  static_cast<DIM_UNITS_MODE>( parseInt( "dimension units" ) );
1983  NeedRIGHT();
1984  break;
1985 
1986  case T_dimension_precision:
1987  designSettings.m_DimensionPrecision = parseInt( "dimension precision" );
1988  NeedRIGHT();
1989  break;
1990 
1991  default:
1992  Unexpected( CurText() );
1993  }
1994  }
1995 }
1996 
1997 
1999 {
2000  T token;
2001 
2002  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2003  {
2004  if( token == T_LEFT )
2005  token = NextTok();
2006 
2007  switch( token )
2008  {
2009  case T_size:
2010  aSettings.m_TextSize[ aLayer ].x = parseBoardUnits( "default text size X" );
2011  aSettings.m_TextSize[ aLayer ].y = parseBoardUnits( "default text size Y" );
2012  NeedRIGHT();
2013  break;
2014 
2015  case T_thickness:
2016  aSettings.m_TextThickness[ aLayer ] = parseBoardUnits( "default text width" );
2017  NeedRIGHT();
2018  break;
2019 
2020  case T_italic:
2021  aSettings.m_TextItalic[ aLayer ] = true;
2022  break;
2023 
2024  case T_keep_upright:
2025  aSettings.m_TextUpright[ aLayer ] = true;
2026  break;
2027 
2028  default:
2029  Expecting( "size, thickness, italic or keep_upright" );
2030  }
2031  }
2032 }
2033 
2034 
2036 {
2037  wxCHECK_RET( CurTok() == T_net,
2038  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as net." ) );
2039 
2040  int netCode = parseInt( "net number" );
2041 
2042  NeedSYMBOLorNUMBER();
2043  wxString name = FromUTF8();
2044 
2045  NeedRIGHT();
2046 
2047  // net 0 should be already in list, so store this net
2048  // if it is not the net 0, or if the net 0 does not exists.
2049  // (TODO: a better test.)
2050  if( netCode > NETINFO_LIST::UNCONNECTED || !m_board->FindNet( NETINFO_LIST::UNCONNECTED ) )
2051  {
2052  NETINFO_ITEM* net = new NETINFO_ITEM( m_board, name, netCode );
2053  m_board->Add( net );
2054 
2055  // Store the new code mapping
2056  pushValueIntoMap( netCode, net->GetNetCode() );
2057  }
2058 }
2059 
2060 
2062 {
2063  wxCHECK_RET( CurTok() == T_net_class,
2064  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as net class." ) );
2065 
2066  T token;
2067 
2068  NETCLASSPTR nc = std::make_shared<NETCLASS>( wxEmptyString );
2069 
2070  // Read netclass name (can be a name or just a number like track width)
2071  NeedSYMBOLorNUMBER();
2072  nc->SetName( FromUTF8() );
2073  NeedSYMBOL();
2074  nc->SetDescription( FromUTF8() );
2075 
2076  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2077  {
2078  if( token != T_LEFT )
2079  Expecting( T_LEFT );
2080 
2081  token = NextTok();
2082 
2083  switch( token )
2084  {
2085  case T_clearance:
2086  nc->SetClearance( parseBoardUnits( T_clearance ) );
2087  break;
2088 
2089  case T_trace_width:
2090  nc->SetTrackWidth( parseBoardUnits( T_trace_width ) );
2091  break;
2092 
2093  case T_via_dia:
2094  nc->SetViaDiameter( parseBoardUnits( T_via_dia ) );
2095  break;
2096 
2097  case T_via_drill:
2098  nc->SetViaDrill( parseBoardUnits( T_via_drill ) );
2099  break;
2100 
2101  case T_uvia_dia:
2102  nc->SetuViaDiameter( parseBoardUnits( T_uvia_dia ) );
2103  break;
2104 
2105  case T_uvia_drill:
2106  nc->SetuViaDrill( parseBoardUnits( T_uvia_drill ) );
2107  break;
2108 
2109  case T_diff_pair_width:
2110  nc->SetDiffPairWidth( parseBoardUnits( T_diff_pair_width ) );
2111  break;
2112 
2113  case T_diff_pair_gap:
2114  nc->SetDiffPairGap( parseBoardUnits( T_diff_pair_gap ) );
2115  break;
2116 
2117  case T_add_net:
2118  NeedSYMBOLorNUMBER();
2119  nc->Add( FromUTF8() );
2120  break;
2121 
2122  default:
2123  Expecting( "clearance, trace_width, via_dia, via_drill, uvia_dia, uvia_drill, "
2124  "diff_pair_width, diff_pair_gap or add_net" );
2125  }
2126 
2127  NeedRIGHT();
2128  }
2129 
2130  if( !m_board->GetDesignSettings().GetNetClasses().Add( nc ) )
2131  {
2132  // Must have been a name conflict, this is a bad board file.
2133  // User may have done a hand edit to the file.
2134 
2135  // unique_ptr will delete nc on this code path
2136 
2137  wxString error;
2138  error.Printf( _( "Duplicate NETCLASS name \"%s\" in file \"%s\" at line %d, offset %d" ),
2139  nc->GetName().GetData(), CurSource().GetData(), CurLineNumber(), CurOffset() );
2140  THROW_IO_ERROR( error );
2141  }
2142 }
2143 
2144 
2146 {
2147  wxCHECK_MSG( CurTok() == T_gr_arc || CurTok() == T_gr_circle || CurTok() == T_gr_curve ||
2148  CurTok() == T_gr_rect || CurTok() == T_gr_line || CurTok() == T_gr_poly, NULL,
2149  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_SHAPE." ) );
2150 
2151  T token;
2152  wxPoint pt;
2153  std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( nullptr );
2154 
2155  switch( CurTok() )
2156  {
2157  case T_gr_arc:
2158  shape->SetShape( S_ARC );
2159  NeedLEFT();
2160  token = NextTok();
2161 
2162  // the start keyword actually gives the arc center
2163  // Allows also T_center for future change
2164  if( token != T_start && token != T_center )
2165  Expecting( T_start );
2166 
2167  pt.x = parseBoardUnits( "X coordinate" );
2168  pt.y = parseBoardUnits( "Y coordinate" );
2169  shape->SetCenter( pt );
2170  NeedRIGHT();
2171  NeedLEFT();
2172  token = NextTok();
2173 
2174  if( token != T_end ) // the end keyword actually gives the starting point of the arc
2175  Expecting( T_end );
2176 
2177  pt.x = parseBoardUnits( "X coordinate" );
2178  pt.y = parseBoardUnits( "Y coordinate" );
2179  shape->SetArcStart( pt );
2180  NeedRIGHT();
2181  break;
2182 
2183  case T_gr_circle:
2184  shape->SetShape( S_CIRCLE );
2185  NeedLEFT();
2186  token = NextTok();
2187 
2188  if( token != T_center )
2189  Expecting( T_center );
2190 
2191  pt.x = parseBoardUnits( "X coordinate" );
2192  pt.y = parseBoardUnits( "Y coordinate" );
2193  shape->SetCenter( pt );
2194  NeedRIGHT();
2195  NeedLEFT();
2196 
2197  token = NextTok();
2198 
2199  if( token != T_end )
2200  Expecting( T_end );
2201 
2202  pt.x = parseBoardUnits( "X coordinate" );
2203  pt.y = parseBoardUnits( "Y coordinate" );
2204  shape->SetEnd( pt );
2205  NeedRIGHT();
2206  break;
2207 
2208  case T_gr_curve:
2209  shape->SetShape( S_CURVE );
2210  NeedLEFT();
2211  token = NextTok();
2212 
2213  if( token != T_pts )
2214  Expecting( T_pts );
2215 
2216  shape->SetStart( parseXY() );
2217  shape->SetBezControl1( parseXY() );
2218  shape->SetBezControl2( parseXY() );
2219  shape->SetEnd( parseXY() );
2220  NeedRIGHT();
2221  break;
2222 
2223  case T_gr_rect:
2224  shape->SetShape( S_RECT );
2225  NeedLEFT();
2226  token = NextTok();
2227 
2228  if( token != T_start )
2229  Expecting( T_start );
2230 
2231  pt.x = parseBoardUnits( "X coordinate" );
2232  pt.y = parseBoardUnits( "Y coordinate" );
2233  shape->SetStart( pt );
2234  NeedRIGHT();
2235  NeedLEFT();
2236  token = NextTok();
2237 
2238  if( token != T_end )
2239  Expecting( T_end );
2240 
2241  pt.x = parseBoardUnits( "X coordinate" );
2242  pt.y = parseBoardUnits( "Y coordinate" );
2243  shape->SetEnd( pt );
2244  NeedRIGHT();
2245  break;
2246 
2247  case T_gr_line:
2248  // Default PCB_SHAPE type is S_SEGMENT.
2249  NeedLEFT();
2250  token = NextTok();
2251 
2252  if( token != T_start )
2253  Expecting( T_start );
2254 
2255  pt.x = parseBoardUnits( "X coordinate" );
2256  pt.y = parseBoardUnits( "Y coordinate" );
2257  shape->SetStart( pt );
2258  NeedRIGHT();
2259  NeedLEFT();
2260  token = NextTok();
2261 
2262  if( token != T_end )
2263  Expecting( T_end );
2264 
2265  pt.x = parseBoardUnits( "X coordinate" );
2266  pt.y = parseBoardUnits( "Y coordinate" );
2267  shape->SetEnd( pt );
2268  NeedRIGHT();
2269  break;
2270 
2271  case T_gr_poly:
2272  {
2273  shape->SetShape( S_POLYGON );
2274  shape->SetWidth( 0 ); // this is the default value. will be (perhaps) modified later
2275  NeedLEFT();
2276  token = NextTok();
2277 
2278  if( token != T_pts )
2279  Expecting( T_pts );
2280 
2281  std::vector< wxPoint > pts;
2282 
2283  while( (token = NextTok() ) != T_RIGHT )
2284  pts.push_back( parseXY() );
2285 
2286  shape->SetPolyPoints( pts );
2287  }
2288  break;
2289 
2290  default:
2291  Expecting( "gr_arc, gr_circle, gr_curve, gr_line, gr_poly, or gp_rect" );
2292  }
2293 
2294  bool foundFill = false;
2295 
2296  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2297  {
2298  if( token != T_LEFT )
2299  Expecting( T_LEFT );
2300 
2301  token = NextTok();
2302 
2303  switch( token )
2304  {
2305  case T_angle:
2306  shape->SetAngle( parseDouble( "segment angle" ) * 10.0 );
2307  NeedRIGHT();
2308  break;
2309 
2310  case T_layer:
2311  shape->SetLayer( parseBoardItemLayer() );
2312  NeedRIGHT();
2313  break;
2314 
2315  case T_width:
2316  shape->SetWidth( parseBoardUnits( T_width ) );
2317  NeedRIGHT();
2318  break;
2319 
2320  case T_tstamp:
2321  NextTok();
2322  const_cast<KIID&>( shape->m_Uuid ) = CurStrToKIID();
2323  NeedRIGHT();
2324  break;
2325 
2326  case T_fill:
2327  foundFill = true;
2328 
2329  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2330  {
2331  if( token == T_LEFT )
2332  token = NextTok();
2333 
2334  switch( token )
2335  {
2336  // T_yes was used to indicate filling when first introduced,
2337  // so treat it like a solid fill since that was the only fill available
2338  case T_yes:
2339  case T_solid:
2340  shape->SetFilled( true );
2341  break;
2342 
2343  case T_none:
2344  shape->SetFilled( false );
2345  break;
2346 
2347  default:
2348  Expecting( "yes, none, solid" );
2349  }
2350  }
2351  break;
2352 
2354  case T_status:
2355  shape->SetStatus( static_cast<STATUS_FLAGS>( parseHex() ) );
2356  NeedRIGHT();
2357  break;
2358 
2359  case T_locked:
2360  shape->SetLocked( true );
2361  NeedRIGHT();
2362  break;
2363 
2364  default:
2365  Expecting( "layer, width, fill, tstamp, locked or status" );
2366  }
2367  }
2368 
2369  if( !foundFill )
2370  {
2371  // Legacy versions didn't have a filled flag but allowed some shapes to indicate they
2372  // should be filled by specifying a 0 stroke-width.
2373  if( shape->GetWidth() == 0
2374  && ( shape->GetShape() == S_RECT || shape->GetShape() == S_CIRCLE ) )
2375  {
2376  shape->SetFilled( true );
2377  }
2378  // Polygons on non-Edge_Cuts layers were always filled
2379  else if( shape->GetShape() == S_POLYGON && shape->GetLayer() != Edge_Cuts )
2380  {
2381  shape->SetFilled( true );
2382  }
2383  }
2384 
2385  // Only filled shapes may have a zero line-width. This is not permitted in KiCad but some
2386  // external tools can generate invalid files.
2387  if( shape->GetWidth() <= 0 && !shape->IsFilled() )
2388  {
2389  shape->SetWidth( Millimeter2iu( DEFAULT_LINE_WIDTH ) );
2390  }
2391 
2392  return shape.release();
2393 }
2394 
2395 
2397 {
2398  wxCHECK_MSG( CurTok() == T_gr_text, NULL,
2399  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TEXT." ) );
2400 
2401  T token;
2402 
2403  std::unique_ptr<PCB_TEXT> text = std::make_unique<PCB_TEXT>( m_board );
2404  NeedSYMBOLorNUMBER();
2405 
2406  text->SetText( FromUTF8() );
2407  NeedLEFT();
2408  token = NextTok();
2409 
2410  if( token != T_at )
2411  Expecting( T_at );
2412 
2413  wxPoint pt;
2414 
2415  pt.x = parseBoardUnits( "X coordinate" );
2416  pt.y = parseBoardUnits( "Y coordinate" );
2417  text->SetTextPos( pt );
2418 
2419  // If there is no orientation defined, then it is the default value of 0 degrees.
2420  token = NextTok();
2421 
2422  if( token == T_NUMBER )
2423  {
2424  text->SetTextAngle( parseDouble() * 10.0 );
2425  NeedRIGHT();
2426  }
2427  else if( token != T_RIGHT )
2428  {
2429  Unexpected( CurText() );
2430  }
2431 
2432  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2433  {
2434  if( token != T_LEFT )
2435  Expecting( T_LEFT );
2436 
2437  token = NextTok();
2438 
2439  switch( token )
2440  {
2441  case T_layer:
2442  text->SetLayer( parseBoardItemLayer() );
2443  NeedRIGHT();
2444  break;
2445 
2446  case T_tstamp:
2447  NextTok();
2448  const_cast<KIID&>( text->m_Uuid ) = CurStrToKIID();
2449  NeedRIGHT();
2450  break;
2451 
2452  case T_effects:
2453  parseEDA_TEXT( (EDA_TEXT*) text.get() );
2454  break;
2455 
2456  default:
2457  Expecting( "layer, tstamp or effects" );
2458  }
2459  }
2460 
2461  return text.release();
2462 }
2463 
2464 
2466 {
2467  wxCHECK_MSG( CurTok() == T_dimension, NULL,
2468  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as DIMENSION." ) );
2469 
2470  T token;
2471 
2472  std::unique_ptr<DIMENSION_BASE> dimension;
2473 
2474  // skip value that used to be saved
2475  if( NextTok() != T_LEFT )
2476  NeedLEFT();
2477 
2478  token = NextTok();
2479 
2480  bool isLegacyDimension = false;
2481 
2482  // Old format
2483  if( token == T_width )
2484  {
2485  isLegacyDimension = true;
2486  dimension = std::make_unique<ALIGNED_DIMENSION>( nullptr );
2487  dimension->SetLineThickness( parseBoardUnits( "dimension width value" ) );
2488  NeedRIGHT();
2489  }
2490  else
2491  {
2492  if( token != T_type )
2493  Expecting( T_type );
2494 
2495  switch( NextTok() )
2496  {
2497  case T_aligned:
2498  dimension = std::make_unique<ALIGNED_DIMENSION>( nullptr );
2499  break;
2500 
2501  case T_orthogonal:
2502  dimension = std::make_unique<ORTHOGONAL_DIMENSION>( nullptr );
2503  break;
2504 
2505  case T_leader:
2506  dimension = std::make_unique<LEADER>( nullptr );
2507  break;
2508 
2509  case T_center:
2510  dimension = std::make_unique<CENTER_DIMENSION>( nullptr );
2511  break;
2512 
2513  default:
2514  wxFAIL_MSG( wxT( "Cannot parse unknown dimension type %s" ) +
2515  GetTokenString( CurTok() ) );
2516  }
2517 
2518  NeedRIGHT();
2519  }
2520 
2521  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2522  {
2523  if( token != T_LEFT )
2524  Expecting( T_LEFT );
2525 
2526  token = NextTok();
2527 
2528  switch( token )
2529  {
2530  case T_layer:
2531  dimension->SetLayer( parseBoardItemLayer() );
2532  NeedRIGHT();
2533  break;
2534 
2535  case T_tstamp:
2536  NextTok();
2537  const_cast<KIID&>( dimension->m_Uuid ) = CurStrToKIID();
2538  NeedRIGHT();
2539  break;
2540 
2541  case T_gr_text:
2542  {
2543  PCB_TEXT* text = parsePCB_TEXT();
2544  dimension->Text() = *text;
2545 
2546  // The text is part of the dimension and shares its uuid
2547  const_cast<KIID&>( dimension->Text().m_Uuid ) = dimension->m_Uuid;
2548 
2549  // Fetch other dimension properties out of the text item
2550  dimension->Text().SetTextPos( text->GetTextPos() );
2551 
2552  if( isLegacyDimension )
2553  {
2554  EDA_UNITS units = EDA_UNITS::INCHES;
2555  FetchUnitsFromString( text->GetText(), units );
2556  dimension->SetUnits( units );
2557  }
2558 
2559  delete text;
2560  break;
2561  }
2562 
2563  // New format: feature points
2564  case T_pts:
2565  {
2566  wxPoint point;
2567 
2568  parseXY( &point.x, &point.y );
2569  dimension->SetStart( point );
2570  parseXY( &point.x, &point.y );
2571  dimension->SetEnd( point );
2572 
2573  NeedRIGHT();
2574  break;
2575  }
2576 
2577  case T_height:
2578  {
2579  wxCHECK_MSG( dimension->Type() == PCB_DIM_ALIGNED_T ||
2580  dimension->Type() == PCB_DIM_ORTHOGONAL_T, nullptr,
2581  wxT( "Invalid height token" ) );
2582  ALIGNED_DIMENSION* aligned = static_cast<ALIGNED_DIMENSION*>( dimension.get() );
2583  aligned->SetHeight( parseBoardUnits( "dimension height value" ) );
2584  NeedRIGHT();
2585  break;
2586  }
2587 
2588  case T_orientation:
2589  {
2590  wxCHECK_MSG( dimension->Type() == PCB_DIM_ORTHOGONAL_T, nullptr,
2591  wxT( "Invalid orientation token" ) );
2592  ORTHOGONAL_DIMENSION* ortho = static_cast<ORTHOGONAL_DIMENSION*>( dimension.get() );
2593 
2594  int orientation = parseInt( "orthogonal dimension orientation" );
2595  orientation = std::max( 0, std::min( 1, orientation ) );
2596  ortho->SetOrientation( static_cast<ORTHOGONAL_DIMENSION::DIR>( orientation ) );
2597  NeedRIGHT();
2598  break;
2599  }
2600 
2601  case T_format:
2602  {
2603  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2604  {
2605  switch( token )
2606  {
2607  case T_LEFT:
2608  continue;
2609 
2610  case T_prefix:
2611  NeedSYMBOLorNUMBER();
2612  dimension->SetPrefix( FromUTF8() );
2613  NeedRIGHT();
2614  break;
2615 
2616  case T_suffix:
2617  NeedSYMBOLorNUMBER();
2618  dimension->SetSuffix( FromUTF8() );
2619  NeedRIGHT();
2620  break;
2621 
2622  case T_units:
2623  {
2624  int mode = parseInt( "dimension units mode" );
2625  mode = std::max( 0, std::min( 4, mode ) );
2626  dimension->SetUnitsMode( static_cast<DIM_UNITS_MODE>( mode ) );
2627  NeedRIGHT();
2628  break;
2629  }
2630 
2631  case T_units_format:
2632  {
2633  int format = parseInt( "dimension units format" );
2634  format = std::max( 0, std::min( 3, format ) );
2635  dimension->SetUnitsFormat( static_cast<DIM_UNITS_FORMAT>( format ) );
2636  NeedRIGHT();
2637  break;
2638  }
2639 
2640  case T_precision:
2641  dimension->SetPrecision( parseInt( "dimension precision" ) );
2642  NeedRIGHT();
2643  break;
2644 
2645  case T_override_value:
2646  NeedSYMBOLorNUMBER();
2647  dimension->SetOverrideTextEnabled( true );
2648  dimension->SetOverrideText( FromUTF8() );
2649  NeedRIGHT();
2650  break;
2651 
2652  case T_suppress_zeroes:
2653  dimension->SetSuppressZeroes( true );
2654  break;
2655 
2656  default:
2657  Expecting( "prefix, suffix, units, units_format, precision, override_value, "
2658  "suppress_zeroes" );
2659  }
2660  }
2661  break;
2662  }
2663 
2664  case T_style:
2665  {
2666  // new format: default to keep text aligned off unless token is present
2667  dimension->SetKeepTextAligned( false );
2668 
2669  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2670  {
2671  switch( token )
2672  {
2673  case T_LEFT:
2674  continue;
2675 
2676  case T_thickness:
2677  dimension->SetLineThickness( parseBoardUnits( "extension line thickness" ) );
2678  NeedRIGHT();
2679  break;
2680 
2681  case T_arrow_length:
2682  dimension->SetArrowLength( parseBoardUnits( "arrow length" ) );
2683  NeedRIGHT();
2684  break;
2685 
2686  case T_text_position_mode:
2687  {
2688  int mode = parseInt( "dimension text position mode" );
2689  mode = std::max( 0, std::min( 3, mode ) );
2690  dimension->SetTextPositionMode( static_cast<DIM_TEXT_POSITION>( mode ) );
2691  NeedRIGHT();
2692  break;
2693  }
2694 
2695  case T_extension_height:
2696  {
2697  ALIGNED_DIMENSION* aligned = dynamic_cast<ALIGNED_DIMENSION*>( dimension.get() );
2698  wxCHECK_MSG( aligned, nullptr, wxT( "Invalid extension_height token" ) );
2699  aligned->SetExtensionHeight( parseBoardUnits( "extension height" ) );
2700  NeedRIGHT();
2701  break;
2702  }
2703 
2704  case T_extension_offset:
2705  dimension->SetExtensionOffset( parseBoardUnits( "extension offset" ) );
2706  NeedRIGHT();
2707  break;
2708 
2709  case T_keep_text_aligned:
2710  dimension->SetKeepTextAligned( true );
2711  break;
2712 
2713  case T_text_frame:
2714  {
2715  wxCHECK_MSG( dimension->Type() == PCB_DIM_LEADER_T, nullptr,
2716  wxT( "Invalid text_frame token" ) );
2717  LEADER* leader = static_cast<LEADER*>( dimension.get() );
2718 
2719  int textFrame = parseInt( "dimension text frame mode" );
2720  textFrame = std::max( 0, std::min( 3, textFrame ) );
2721  leader->SetTextFrame( static_cast<DIM_TEXT_FRAME>( textFrame ) );
2722  NeedRIGHT();
2723  break;
2724  }
2725 
2726  default:
2727  Expecting( "thickness, arrow_length, text_position_mode, extension_height, "
2728  "extension_offset" );
2729  }
2730  }
2731 
2732  break;
2733  }
2734 
2735  // Old format: feature1 stores a feature line. We only care about the origin.
2736  case T_feature1:
2737  {
2738  NeedLEFT();
2739  token = NextTok();
2740 
2741  if( token != T_pts )
2742  Expecting( T_pts );
2743 
2744  wxPoint point;
2745 
2746  parseXY( &point.x, &point.y );
2747  dimension->SetStart( point );
2748 
2749  parseXY( nullptr, nullptr ); // Ignore second point
2750  NeedRIGHT();
2751  NeedRIGHT();
2752  break;
2753  }
2754 
2755  // Old format: feature2 stores a feature line. We only care about the end point.
2756  case T_feature2:
2757  {
2758  NeedLEFT();
2759  token = NextTok();
2760 
2761  if( token != T_pts )
2762  Expecting( T_pts );
2763 
2764  wxPoint point;
2765 
2766  parseXY( &point.x, &point.y );
2767  dimension->SetEnd( point );
2768 
2769  parseXY( nullptr, nullptr ); // Ignore second point
2770 
2771  NeedRIGHT();
2772  NeedRIGHT();
2773  break;
2774  }
2775 
2776  case T_crossbar:
2777  {
2778  NeedLEFT();
2779  token = NextTok();
2780 
2781  if( token == T_pts )
2782  {
2783  // If we have a crossbar, we know we're an old aligned dimension
2784  ALIGNED_DIMENSION* aligned = static_cast<ALIGNED_DIMENSION*>( dimension.get() );
2785 
2786  // Old style: calculate height from crossbar
2787  wxPoint point1, point2;
2788  parseXY( &point1.x, &point1.y );
2789  parseXY( &point2.x, &point2.y );
2790  aligned->UpdateHeight( point2, point1 ); // Yes, backwards intentionally
2791  NeedRIGHT();
2792  }
2793 
2794  NeedRIGHT();
2795  break;
2796  }
2797 
2798  // Arrow: no longer saved; no-op
2799  case T_arrow1a:
2800  NeedLEFT();
2801  token = NextTok();
2802 
2803  if( token != T_pts )
2804  Expecting( T_pts );
2805 
2806  parseXY( nullptr, nullptr );
2807  parseXY( nullptr, nullptr );
2808  NeedRIGHT();
2809  NeedRIGHT();
2810  break;
2811 
2812  // Arrow: no longer saved; no-op
2813  case T_arrow1b:
2814  NeedLEFT();
2815  token = NextTok();
2816 
2817  if( token != T_pts )
2818  Expecting( T_pts );
2819 
2820  parseXY( nullptr, nullptr );
2821  parseXY( nullptr, nullptr );
2822  NeedRIGHT();
2823  NeedRIGHT();
2824  break;
2825 
2826  // Arrow: no longer saved; no-op
2827  case T_arrow2a:
2828  NeedLEFT();
2829  token = NextTok();
2830 
2831  if( token != T_pts )
2832  Expecting( T_pts );
2833 
2834  parseXY( nullptr, nullptr );
2835  parseXY( nullptr, nullptr );
2836  NeedRIGHT();
2837  NeedRIGHT();
2838  break;
2839 
2840  // Arrow: no longer saved; no-op
2841  case T_arrow2b:
2842  NeedLEFT();
2843  token = NextTok();
2844 
2845  if( token != T_pts )
2846  Expecting( T_pts );
2847 
2848  parseXY( nullptr, nullptr );
2849  parseXY( nullptr, nullptr );
2850  NeedRIGHT();
2851  NeedRIGHT();
2852  break;
2853 
2854  default:
2855  Expecting( "layer, tstamp, gr_text, feature1, feature2, crossbar, arrow1a, "
2856  "arrow1b, arrow2a, or arrow2b" );
2857  }
2858  }
2859 
2860  dimension->Update();
2861 
2862  return dimension.release();
2863 }
2864 
2865 
2866 FOOTPRINT* PCB_PARSER::parseFOOTPRINT( wxArrayString* aInitialComments )
2867 {
2868  try
2869  {
2870  return parseFOOTPRINT_unchecked( aInitialComments );
2871  }
2872  catch( const PARSE_ERROR& parse_error )
2873  {
2874  if( m_tooRecent )
2875  throw FUTURE_FORMAT_ERROR( parse_error, GetRequiredVersion() );
2876  else
2877  throw;
2878  }
2879 }
2880 
2881 
2882 FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments )
2883 {
2884  wxCHECK_MSG( CurTok() == T_module || CurTok() == T_footprint, NULL,
2885  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as FOOTPRINT." ) );
2886 
2887  wxString name;
2888  wxPoint pt;
2889  T token;
2890  LIB_ID fpid;
2891  int attributes = 0;
2892 
2893  std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
2894 
2895  std::map<wxString, wxString> properties;
2896 
2897  footprint->SetInitialComments( aInitialComments );
2898 
2899  token = NextTok();
2900 
2901  if( !IsSymbol( token ) && token != T_NUMBER )
2902  Expecting( "symbol|number" );
2903 
2904  name = FromUTF8();
2905 
2906  if( !name.IsEmpty() && fpid.Parse( name, true ) >= 0 )
2907  {
2908  wxString error;
2909  error.Printf( _( "Invalid footprint ID in\nfile: \"%s\"\nline: %d\noffset: %d" ),
2910  CurSource(), CurLineNumber(), CurOffset() );
2911  THROW_IO_ERROR( error );
2912  }
2913 
2914  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2915  {
2916  if( token == T_LEFT )
2917  token = NextTok();
2918 
2919  switch( token )
2920  {
2921  case T_version:
2922  {
2923  // Theoretically a footprint nested in a PCB could declare its own version, though
2924  // as of writing this comment we don't do that. Just in case, take the greater
2925  // version.
2926  int this_version = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
2927  NeedRIGHT();
2928  m_requiredVersion = std::max( m_requiredVersion, this_version );
2929  m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION );
2930  break;
2931  }
2932 
2933  case T_generator:
2934  // We currently ignore the generator when parsing. It is included in the file for manual
2935  // indication of where the footprint came from.
2936  NeedSYMBOL();
2937  NeedRIGHT();
2938  break;
2939 
2940  case T_locked:
2941  footprint->SetLocked( true );
2942  break;
2943 
2944  case T_placed:
2945  footprint->SetIsPlaced( true );
2946  break;
2947 
2948  case T_layer:
2949  {
2950  // Footprints can be only on the front side or the back side.
2951  // but because we can find some stupid layer in file, ensure a
2952  // acceptable layer is set for the footprint
2953  PCB_LAYER_ID layer = parseBoardItemLayer();
2954  footprint->SetLayer( layer == B_Cu ? B_Cu : F_Cu );
2955  }
2956  NeedRIGHT();
2957  break;
2958 
2959  case T_tedit:
2960  footprint->SetLastEditTime( parseHex() );
2961  NeedRIGHT();
2962  break;
2963 
2964  case T_tstamp:
2965  NextTok();
2966  const_cast<KIID&>( footprint->m_Uuid ) = CurStrToKIID();
2967  NeedRIGHT();
2968  break;
2969 
2970  case T_at:
2971  pt.x = parseBoardUnits( "X coordinate" );
2972  pt.y = parseBoardUnits( "Y coordinate" );
2973  footprint->SetPosition( pt );
2974  token = NextTok();
2975 
2976  if( token == T_NUMBER )
2977  {
2978  footprint->SetOrientation( parseDouble() * 10.0 );
2979  NeedRIGHT();
2980  }
2981  else if( token != T_RIGHT )
2982  {
2983  Expecting( T_RIGHT );
2984  }
2985 
2986  break;
2987 
2988  case T_descr:
2989  NeedSYMBOLorNUMBER(); // some symbols can be 0508, so a number is also a symbol here
2990  footprint->SetDescription( FromUTF8() );
2991  NeedRIGHT();
2992  break;
2993 
2994  case T_tags:
2995  NeedSYMBOLorNUMBER(); // some symbols can be 0508, so a number is also a symbol here
2996  footprint->SetKeywords( FromUTF8() );
2997  NeedRIGHT();
2998  break;
2999 
3000  case T_property:
3001  properties.insert( parseProperty() );
3002  break;
3003 
3004  case T_path:
3005  NeedSYMBOLorNUMBER(); // Paths can be numerical so a number is also a symbol here
3006  footprint->SetPath( KIID_PATH( FromUTF8() ) );
3007  NeedRIGHT();
3008  break;
3009 
3010  case T_autoplace_cost90:
3011  footprint->SetPlacementCost90( parseInt( "auto place cost at 90 degrees" ) );
3012  NeedRIGHT();
3013  break;
3014 
3015  case T_autoplace_cost180:
3016  footprint->SetPlacementCost180( parseInt( "auto place cost at 180 degrees" ) );
3017  NeedRIGHT();
3018  break;
3019 
3020  case T_solder_mask_margin:
3021  footprint->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
3022  NeedRIGHT();
3023  break;
3024 
3025  case T_solder_paste_margin:
3026  footprint->SetLocalSolderPasteMargin(
3027  parseBoardUnits( "local solder paste margin value" ) );
3028  NeedRIGHT();
3029  break;
3030 
3031  case T_solder_paste_ratio:
3032  footprint->SetLocalSolderPasteMarginRatio(
3033  parseDouble( "local solder paste margin ratio value" ) );
3034  NeedRIGHT();
3035  break;
3036 
3037  case T_clearance:
3038  footprint->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
3039  NeedRIGHT();
3040  break;
3041 
3042  case T_zone_connect:
3043  footprint->SetZoneConnection((ZONE_CONNECTION) parseInt( "zone connection value" ) );
3044  NeedRIGHT();
3045  break;
3046 
3047  case T_thermal_width:
3048  footprint->SetThermalWidth( parseBoardUnits( "thermal width value" ) );
3049  NeedRIGHT();
3050  break;
3051 
3052  case T_thermal_gap:
3053  footprint->SetThermalGap( parseBoardUnits( "thermal gap value" ) );
3054  NeedRIGHT();
3055  break;
3056 
3057  case T_attr:
3058  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3059  {
3060  switch( token )
3061  {
3062  case T_virtual: // legacy token prior to version 20200826
3064  break;
3065 
3066  case T_through_hole:
3067  attributes |= FP_THROUGH_HOLE;
3068  break;
3069 
3070  case T_smd:
3071  attributes |= FP_SMD;
3072  break;
3073 
3074  case T_board_only:
3075  attributes |= FP_BOARD_ONLY;
3076  break;
3077 
3078  case T_exclude_from_pos_files:
3079  attributes |= FP_EXCLUDE_FROM_POS_FILES;
3080  break;
3081 
3082  case T_exclude_from_bom:
3083  attributes |= FP_EXCLUDE_FROM_BOM;
3084  break;
3085 
3086  default:
3087  Expecting( "through_hole, smd, virtual, board_only, exclude_from_pos_files "
3088  "or exclude_from_bom" );
3089  }
3090  }
3091  break;
3092 
3093  case T_fp_text:
3094  {
3095  FP_TEXT* text = parseFP_TEXT();
3096  text->SetParent( footprint.get() );
3097  double orientation = text->GetTextAngle();
3098  orientation -= footprint->GetOrientation();
3099  text->SetTextAngle( orientation );
3100  text->SetDrawCoord();
3101 
3102  switch( text->GetType() )
3103  {
3105  footprint->Reference() = *text;
3106  const_cast<KIID&>( footprint->Reference().m_Uuid ) = text->m_Uuid;
3107  delete text;
3108  break;
3109 
3111  footprint->Value() = *text;
3112  const_cast<KIID&>( footprint->Value().m_Uuid ) = text->m_Uuid;
3113  delete text;
3114  break;
3115 
3116  default:
3117  footprint->Add( text, ADD_MODE::APPEND );
3118  }
3119  }
3120  break;
3121 
3122  case T_fp_arc:
3123  {
3124  FP_SHAPE* shape = parseFP_SHAPE();
3125 
3126  // Drop 0 and NaN angles as these can corrupt/crash the schematic
3127  if( std::isnormal( shape->GetAngle() ) )
3128  {
3129  shape->SetParent( footprint.get() );
3130  shape->SetDrawCoord();
3131  footprint->Add( shape, ADD_MODE::APPEND );
3132  }
3133  else
3134  delete shape;
3135  }
3136  break;
3137 
3138  case T_fp_circle:
3139  case T_fp_curve:
3140  case T_fp_rect:
3141  case T_fp_line:
3142  case T_fp_poly:
3143  {
3144  FP_SHAPE* shape = parseFP_SHAPE();
3145  shape->SetParent( footprint.get() );
3146  shape->SetDrawCoord();
3147  footprint->Add( shape, ADD_MODE::APPEND );
3148  }
3149  break;
3150 
3151  case T_pad:
3152  {
3153  PAD* pad = parsePAD( footprint.get() );
3154  pt = pad->GetPos0();
3155 
3156  RotatePoint( &pt, footprint->GetOrientation() );
3157  pad->SetPosition( pt + footprint->GetPosition() );
3158  footprint->Add( pad, ADD_MODE::APPEND );
3159  }
3160  break;
3161 
3162  case T_model:
3163  {
3164  FP_3DMODEL* model = parse3DModel();
3165  footprint->Add3DModel( model );
3166  delete model;
3167  }
3168  break;
3169 
3170  case T_zone:
3171  {
3172  ZONE* zone = parseZONE( footprint.get() );
3173  footprint->Add( zone, ADD_MODE::APPEND );
3174  }
3175  break;
3176 
3177  case T_group:
3178  parseGROUP( footprint.get() );
3179  break;
3180 
3181  default:
3182  Expecting( "locked, placed, tedit, tstamp, at, descr, tags, path, "
3183  "autoplace_cost90, autoplace_cost180, solder_mask_margin, "
3184  "solder_paste_margin, solder_paste_ratio, clearance, "
3185  "zone_connect, thermal_width, thermal_gap, attr, fp_text, "
3186  "fp_arc, fp_circle, fp_curve, fp_line, fp_poly, fp_rect, pad, "
3187  "zone, group, generator, version or model" );
3188  }
3189  }
3190 
3191  // In legacy files the lack of attributes indicated a through-hole component which was by
3192  // default excluded from pos files. However there was a hack to look for SMD pads and
3193  // consider those "mislabeled through-hole components" and therefore include them in place
3194  // files. We probably don't want to get into that game so we'll just include them by
3195  // default and let the user change it if required.
3196  if( m_requiredVersion < 20200826 && attributes == 0 )
3197  attributes |= FP_THROUGH_HOLE;
3198 
3199  // Legacy files controlled pad locking at the footprint level.
3200  if( m_requiredVersion < 20210108 )
3201  {
3202  for( PAD* pad : footprint->Pads() )
3203  pad->SetLocked( footprint->IsLocked() || footprint->LegacyPadsLocked() );
3204  }
3205 
3206  footprint->SetAttributes( attributes );
3207 
3208  footprint->SetFPID( fpid );
3209  footprint->SetProperties( properties );
3210 
3211  return footprint.release();
3212 }
3213 
3214 
3216 {
3217  wxCHECK_MSG( CurTok() == T_fp_text, NULL,
3218  wxString::Format( wxT( "Cannot parse %s as FP_TEXT at line %d, offset %d." ),
3219  GetTokenString( CurTok() ),
3220  CurLineNumber(), CurOffset() ) );
3221 
3222  T token = NextTok();
3223 
3224  std::unique_ptr<FP_TEXT> text = std::make_unique<FP_TEXT>( nullptr );
3225 
3226  switch( token )
3227  {
3228  case T_reference:
3229  text->SetType( FP_TEXT::TEXT_is_REFERENCE );
3230  break;
3231 
3232  case T_value:
3233  text->SetType( FP_TEXT::TEXT_is_VALUE );
3234  break;
3235 
3236  case T_user:
3237  break; // Default type is user text.
3238 
3239  default:
3241  wxString::Format( _( "Cannot handle footprint text type %s" ), FromUTF8() ) );
3242  }
3243 
3244  NeedSYMBOLorNUMBER();
3245 
3246  wxString value = FromUTF8();
3247  value.Replace( "%V", "${VALUE}" );
3248  value.Replace( "%R", "${REFERENCE}" );
3249  text->SetText( value );
3250  NeedLEFT();
3251  token = NextTok();
3252 
3253  if( token != T_at )
3254  Expecting( T_at );
3255 
3256  wxPoint pt;
3257 
3258  pt.x = parseBoardUnits( "X coordinate" );
3259  pt.y = parseBoardUnits( "Y coordinate" );
3260  text->SetPos0( pt );
3261 
3262  NextTok();
3263 
3264  if( CurTok() == T_NUMBER )
3265  {
3266  text->SetTextAngle( parseDouble() * 10.0 );
3267  NextTok();
3268  }
3269 
3270  if( CurTok() == T_unlocked )
3271  {
3272  text->SetKeepUpright( false );
3273  NextTok();
3274  }
3275 
3276  if( CurTok() != T_RIGHT )
3277  {
3278  Unexpected( CurText() );
3279  }
3280 
3281  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3282  {
3283  if( token == T_LEFT )
3284  token = NextTok();
3285 
3286  switch( token )
3287  {
3288  case T_layer:
3289  text->SetLayer( parseBoardItemLayer() );
3290  NeedRIGHT();
3291  break;
3292 
3293  case T_hide:
3294  text->SetVisible( false );
3295  break;
3296 
3297  case T_effects:
3298  parseEDA_TEXT( (EDA_TEXT*) text.get() );
3299  break;
3300 
3301  case T_tstamp:
3302  NextTok();
3303  const_cast<KIID&>( text->m_Uuid ) = CurStrToKIID();
3304  NeedRIGHT();
3305  break;
3306 
3307  default:
3308  Expecting( "layer, hide, effects or tstamp" );
3309  }
3310  }
3311 
3312  return text.release();
3313 }
3314 
3315 
3317 {
3318  wxCHECK_MSG( CurTok() == T_fp_arc || CurTok() == T_fp_circle || CurTok() == T_fp_curve ||
3319  CurTok() == T_fp_rect || CurTok() == T_fp_line || CurTok() == T_fp_poly, NULL,
3320  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as FP_SHAPE." ) );
3321 
3322  wxPoint pt;
3323  T token;
3324 
3325  std::unique_ptr<FP_SHAPE> shape = std::make_unique<FP_SHAPE>( nullptr );
3326 
3327  switch( CurTok() )
3328  {
3329  case T_fp_arc:
3330  shape->SetShape( S_ARC );
3331  NeedLEFT();
3332  token = NextTok();
3333 
3334  // the start keyword actually gives the arc center
3335  // Allows also T_center for future change
3336  if( token != T_start && token != T_center )
3337  Expecting( T_start );
3338 
3339  pt.x = parseBoardUnits( "X coordinate" );
3340  pt.y = parseBoardUnits( "Y coordinate" );
3341  shape->SetStart0( pt );
3342  NeedRIGHT();
3343  NeedLEFT();
3344  token = NextTok();
3345 
3346  if( token != T_end ) // end keyword actually gives the starting point of the arc
3347  Expecting( T_end );
3348 
3349  pt.x = parseBoardUnits( "X coordinate" );
3350  pt.y = parseBoardUnits( "Y coordinate" );
3351  shape->SetEnd0( pt );
3352  NeedRIGHT();
3353  NeedLEFT();
3354  token = NextTok();
3355 
3356  if( token != T_angle )
3357  Expecting( T_angle );
3358 
3359  // Setting angle will set m_ThirdPoint0, so must be done after setting
3360  // m_Start0 and m_End0
3361  shape->SetAngle( parseDouble( "segment angle" ) * 10.0 );
3362  NeedRIGHT();
3363  break;
3364 
3365  case T_fp_circle:
3366  shape->SetShape( S_CIRCLE );
3367  NeedLEFT();
3368  token = NextTok();
3369 
3370  if( token != T_center )
3371  Expecting( T_center );
3372 
3373  pt.x = parseBoardUnits( "X coordinate" );
3374  pt.y = parseBoardUnits( "Y coordinate" );
3375  shape->SetStart0( pt );
3376  NeedRIGHT();
3377  NeedLEFT();
3378  token = NextTok();
3379 
3380  if( token != T_end )
3381  Expecting( T_end );
3382 
3383  pt.x = parseBoardUnits( "X coordinate" );
3384  pt.y = parseBoardUnits( "Y coordinate" );
3385  shape->SetEnd0( pt );
3386  NeedRIGHT();
3387  break;
3388 
3389  case T_fp_curve:
3390  shape->SetShape( S_CURVE );
3391  NeedLEFT();
3392  token = NextTok();
3393 
3394  if( token != T_pts )
3395  Expecting( T_pts );
3396 
3397  shape->SetStart0( parseXY() );
3398  shape->SetBezier0_C1( parseXY() );
3399  shape->SetBezier0_C2( parseXY() );
3400  shape->SetEnd0( parseXY() );
3401  NeedRIGHT();
3402  break;
3403 
3404  case T_fp_rect:
3405  shape->SetShape( S_RECT );
3406  NeedLEFT();
3407  token = NextTok();
3408 
3409  if( token != T_start )
3410  Expecting( T_start );
3411 
3412  pt.x = parseBoardUnits( "X coordinate" );
3413  pt.y = parseBoardUnits( "Y coordinate" );
3414  shape->SetStart0( pt );
3415 
3416  NeedRIGHT();
3417  NeedLEFT();
3418  token = NextTok();
3419 
3420  if( token != T_end )
3421  Expecting( T_end );
3422 
3423  pt.x = parseBoardUnits( "X coordinate" );
3424  pt.y = parseBoardUnits( "Y coordinate" );
3425  shape->SetEnd0( pt );
3426  NeedRIGHT();
3427  break;
3428 
3429  case T_fp_line:
3430  // Default PCB_SHAPE type is S_SEGMENT.
3431  NeedLEFT();
3432  token = NextTok();
3433 
3434  if( token != T_start )
3435  Expecting( T_start );
3436 
3437  pt.x = parseBoardUnits( "X coordinate" );
3438  pt.y = parseBoardUnits( "Y coordinate" );
3439  shape->SetStart0( pt );
3440 
3441  NeedRIGHT();
3442  NeedLEFT();
3443  token = NextTok();
3444 
3445  if( token != T_end )
3446  Expecting( T_end );
3447 
3448  pt.x = parseBoardUnits( "X coordinate" );
3449  pt.y = parseBoardUnits( "Y coordinate" );
3450  shape->SetEnd0( pt );
3451  NeedRIGHT();
3452  break;
3453 
3454  case T_fp_poly:
3455  {
3456  shape->SetShape( S_POLYGON );
3457  NeedLEFT();
3458  token = NextTok();
3459 
3460  if( token != T_pts )
3461  Expecting( T_pts );
3462 
3463  std::vector< wxPoint > pts;
3464 
3465  while( (token = NextTok() ) != T_RIGHT )
3466  pts.push_back( parseXY() );
3467 
3468  shape->SetPolyPoints( pts );
3469  }
3470  break;
3471 
3472  default:
3473  Expecting( "fp_arc, fp_circle, fp_curve, fp_line, fp_poly, or fp_rect" );
3474  }
3475 
3476  bool foundFill = false;
3477 
3478  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3479  {
3480  if( token != T_LEFT )
3481  Expecting( T_LEFT );
3482 
3483  token = NextTok();
3484 
3485  switch( token )
3486  {
3487  case T_layer:
3488  shape->SetLayer( parseBoardItemLayer() );
3489  NeedRIGHT();
3490  break;
3491 
3492  case T_width:
3493  shape->SetWidth( parseBoardUnits( T_width ) );
3494  NeedRIGHT();
3495  break;
3496 
3497  case T_tstamp:
3498  NextTok();
3499  const_cast<KIID&>( shape->m_Uuid ) = CurStrToKIID();
3500  NeedRIGHT();
3501  break;
3502 
3503  case T_fill:
3504  foundFill = true;
3505 
3506  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3507  {
3508  if( token == T_LEFT )
3509  token = NextTok();
3510 
3511  switch( token )
3512  {
3513  // T_yes was used to indicate filling when first introduced,
3514  // so treat it like a solid fill since that was the only fill available
3515  case T_yes:
3516  case T_solid:
3517  shape->SetFilled( true );
3518  break;
3519 
3520  case T_none:
3521  shape->SetFilled( false );
3522  break;
3523 
3524  default:
3525  Expecting( "yes, none, solid" );
3526  }
3527  }
3528 
3529  break;
3530 
3532  case T_status:
3533  shape->SetStatus( static_cast<STATUS_FLAGS>( parseHex() ) );
3534  NeedRIGHT();
3535  break;
3536 
3537  case T_locked:
3538  shape->SetLocked( true );
3539  NeedRIGHT();
3540  break;
3541 
3542  default:
3543  Expecting( "layer, width, fill, tstamp, locked, or status" );
3544  }
3545  }
3546 
3547  if( !foundFill )
3548  {
3549  // Legacy versions didn't have a filled flag but allowed some shapes to indicate they
3550  // should be filled by specifying a 0 stroke-width.
3551  if( shape->GetWidth() == 0
3552  && ( shape->GetShape() == S_RECT || shape->GetShape() == S_CIRCLE ) )
3553  {
3554  shape->SetFilled( true );
3555  }
3556  // Polygons on non-Edge_Cuts layers were always filled
3557  else if( shape->GetShape() == S_POLYGON && shape->GetLayer() != Edge_Cuts )
3558  {
3559  shape->SetFilled( true );
3560  }
3561  }
3562 
3563  // Only filled shapes may have a zero line-width. This is not permitted in KiCad but some
3564  // external tools can generate invalid files.
3565  if( shape->GetWidth() <= 0 && !shape->IsFilled() )
3566  {
3567  shape->SetWidth( Millimeter2iu( DEFAULT_LINE_WIDTH ) );
3568  }
3569 
3570  return shape.release();
3571 }
3572 
3573 
3575 {
3576  wxCHECK_MSG( CurTok() == T_pad, NULL,
3577  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PAD." ) );
3578 
3579  wxSize sz;
3580  wxPoint pt;
3581 
3582  std::unique_ptr<PAD> pad = std::make_unique<PAD>( aParent );
3583 
3584  // File only contains a token if KeepTopBottom is true
3585  pad->SetKeepTopBottom( false );
3586 
3587  NeedSYMBOLorNUMBER();
3588  pad->SetName( FromUTF8() );
3589 
3590  T token = NextTok();
3591 
3592  switch( token )
3593  {
3594  case T_thru_hole:
3595  pad->SetAttribute( PAD_ATTRIB_PTH );
3596  break;
3597 
3598  case T_smd:
3599  pad->SetAttribute( PAD_ATTRIB_SMD );
3600 
3601  // Default PAD object is thru hole with drill.
3602  // SMD pads have no hole
3603  pad->SetDrillSize( wxSize( 0, 0 ) );
3604  break;
3605 
3606  case T_connect:
3607  pad->SetAttribute( PAD_ATTRIB_CONN );
3608 
3609  // Default PAD object is thru hole with drill.
3610  // CONN pads have no hole
3611  pad->SetDrillSize( wxSize( 0, 0 ) );
3612  break;
3613 
3614  case T_np_thru_hole:
3615  pad->SetAttribute( PAD_ATTRIB_NPTH );
3616  break;
3617 
3618  default:
3619  Expecting( "thru_hole, smd, connect, or np_thru_hole" );
3620  }
3621 
3622  token = NextTok();
3623 
3624  switch( token )
3625  {
3626  case T_circle:
3627  pad->SetShape( PAD_SHAPE_CIRCLE );
3628  break;
3629 
3630  case T_rect:
3631  pad->SetShape( PAD_SHAPE_RECT );
3632  break;
3633 
3634  case T_oval:
3635  pad->SetShape( PAD_SHAPE_OVAL );
3636  break;
3637 
3638  case T_trapezoid:
3639  pad->SetShape( PAD_SHAPE_TRAPEZOID );
3640  break;
3641 
3642  case T_roundrect:
3643  // Note: the shape can be PAD_SHAPE_ROUNDRECT or PAD_SHAPE_CHAMFERED_RECT
3644  // (if chamfer parameters are found later in pad descr.)
3645  pad->SetShape( PAD_SHAPE_ROUNDRECT );
3646  break;
3647 
3648  case T_custom:
3649  pad->SetShape( PAD_SHAPE_CUSTOM );
3650  break;
3651 
3652  default:
3653  Expecting( "circle, rectangle, roundrect, oval, trapezoid or custom" );
3654  }
3655 
3656  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3657  {
3658  if( token != T_LEFT )
3659  Expecting( T_LEFT );
3660 
3661  token = NextTok();
3662 
3663  switch( token )
3664  {
3665  case T_size:
3666  sz.SetWidth( parseBoardUnits( "width value" ) );
3667  sz.SetHeight( parseBoardUnits( "height value" ) );
3668  pad->SetSize( sz );
3669  NeedRIGHT();
3670  break;
3671 
3672  case T_at:
3673  pt.x = parseBoardUnits( "X coordinate" );
3674  pt.y = parseBoardUnits( "Y coordinate" );
3675  pad->SetPos0( pt );
3676  token = NextTok();
3677 
3678  if( token == T_NUMBER )
3679  {
3680  pad->SetOrientation( parseDouble() * 10.0 );
3681  NeedRIGHT();
3682  }
3683  else if( token != T_RIGHT )
3684  {
3685  Expecting( ") or angle value" );
3686  }
3687 
3688  break;
3689 
3690  case T_rect_delta:
3691  {
3692  wxSize delta;
3693  delta.SetWidth( parseBoardUnits( "rectangle delta width" ) );
3694  delta.SetHeight( parseBoardUnits( "rectangle delta height" ) );
3695  pad->SetDelta( delta );
3696  NeedRIGHT();
3697  }
3698  break;
3699 
3700  case T_drill:
3701  {
3702  bool haveWidth = false;
3703  wxSize drillSize = pad->GetDrillSize();
3704 
3705  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3706  {
3707  if( token == T_LEFT )
3708  token = NextTok();
3709 
3710  switch( token )
3711  {
3712  case T_oval:
3713  pad->SetDrillShape( PAD_DRILL_SHAPE_OBLONG );
3714  break;
3715 
3716  case T_NUMBER:
3717  {
3718  if( !haveWidth )
3719  {
3720  drillSize.SetWidth( parseBoardUnits() );
3721 
3722  // If height is not defined the width and height are the same.
3723  drillSize.SetHeight( drillSize.GetWidth() );
3724  haveWidth = true;
3725  }
3726  else
3727  {
3728  drillSize.SetHeight( parseBoardUnits() );
3729  }
3730 
3731  }
3732  break;
3733 
3734  case T_offset:
3735  pt.x = parseBoardUnits( "drill offset x" );
3736  pt.y = parseBoardUnits( "drill offset y" );
3737  pad->SetOffset( pt );
3738  NeedRIGHT();
3739  break;
3740 
3741  default:
3742  Expecting( "oval, size, or offset" );
3743  }
3744  }
3745 
3746  // This fixes a bug caused by setting the default PAD drill size to a value other
3747  // than 0 used to fix a bunch of debug assertions even though it is defined as a
3748  // through hole pad. Wouldn't a though hole pad with no drill be a surface mount
3749  // pad (or a conn pad which is a smd pad with no solder paste)?
3750  if( ( pad->GetAttribute() != PAD_ATTRIB_SMD ) && ( pad->GetAttribute() != PAD_ATTRIB_CONN ) )
3751  pad->SetDrillSize( drillSize );
3752  else
3753  pad->SetDrillSize( wxSize( 0, 0 ) );
3754 
3755  }
3756  break;
3757 
3758  case T_layers:
3759  {
3760  LSET layerMask = parseBoardItemLayersAsMask();
3761  pad->SetLayerSet( layerMask );
3762  }
3763  break;
3764 
3765  case T_net:
3766  if( ! pad->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
3767  {
3768  wxLogError( wxString::Format( _( "Invalid net ID in\n"
3769  "file: '%s'\n"
3770  "line: %d\n"
3771  "offset: %d" ),
3772  CurSource(),
3773  CurLineNumber(),
3774  CurOffset() ) );
3775  }
3776 
3777  NeedSYMBOLorNUMBER();
3778 
3779  // Test validity of the netname in file for netcodes expected having a net name
3780  if( m_board && pad->GetNetCode() > 0 &&
3781  FromUTF8() != m_board->FindNet( pad->GetNetCode() )->GetNetname() )
3782  {
3783  pad->SetNetCode( NETINFO_LIST::ORPHANED, /* aNoAssert */ true );
3784  wxLogError( wxString::Format( _( "Net name doesn't match net ID in\n"
3785  "file: '%s'\n"
3786  "line: %d\n"
3787  "offset: %d" ),
3788  CurSource(),
3789  CurLineNumber(),
3790  CurOffset() ) );
3791  }
3792 
3793  NeedRIGHT();
3794  break;
3795 
3796  case T_pinfunction:
3797  NeedSYMBOLorNUMBER();
3798  pad->SetPinFunction( FromUTF8() );
3799  NeedRIGHT();
3800  break;
3801 
3802  case T_pintype:
3803  NeedSYMBOLorNUMBER();
3804  pad->SetPinType( FromUTF8() );
3805  NeedRIGHT();
3806  break;
3807 
3808  case T_die_length:
3809  pad->SetPadToDieLength( parseBoardUnits( T_die_length ) );
3810  NeedRIGHT();
3811  break;
3812 
3813  case T_solder_mask_margin:
3814  pad->SetLocalSolderMaskMargin( parseBoardUnits( T_solder_mask_margin ) );
3815  NeedRIGHT();
3816  break;
3817 
3818  case T_solder_paste_margin:
3819  pad->SetLocalSolderPasteMargin( parseBoardUnits( T_solder_paste_margin ) );
3820  NeedRIGHT();
3821  break;
3822 
3823  case T_solder_paste_margin_ratio:
3824  pad->SetLocalSolderPasteMarginRatio(
3825  parseDouble( "pad local solder paste margin ratio value" ) );
3826  NeedRIGHT();
3827  break;
3828 
3829  case T_clearance:
3830  pad->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
3831  NeedRIGHT();
3832  break;
3833 
3834  case T_zone_connect:
3835  pad->SetZoneConnection( (ZONE_CONNECTION) parseInt( "zone connection value" ) );
3836  NeedRIGHT();
3837  break;
3838 
3839  case T_thermal_width:
3840  pad->SetThermalSpokeWidth( parseBoardUnits( T_thermal_width ) );
3841  NeedRIGHT();
3842  break;
3843 
3844  case T_thermal_gap:
3845  pad->SetThermalGap( parseBoardUnits( T_thermal_gap ) );
3846  NeedRIGHT();
3847  break;
3848 
3849  case T_roundrect_rratio:
3850  pad->SetRoundRectRadiusRatio( parseDouble( "roundrect radius ratio" ) );
3851  NeedRIGHT();
3852  break;
3853 
3854  case T_chamfer_ratio:
3855  pad->SetChamferRectRatio( parseDouble( "chamfer ratio" ) );
3856 
3857  if( pad->GetChamferRectRatio() > 0 )
3858  pad->SetShape( PAD_SHAPE_CHAMFERED_RECT );
3859 
3860  NeedRIGHT();
3861  break;
3862 
3863  case T_chamfer:
3864  {
3865  int chamfers = 0;
3866  bool end_list = false;
3867 
3868  while( !end_list )
3869  {
3870  token = NextTok();
3871  switch( token )
3872  {
3873  case T_top_left:
3874  chamfers |= RECT_CHAMFER_TOP_LEFT;
3875  break;
3876 
3877  case T_top_right:
3878  chamfers |= RECT_CHAMFER_TOP_RIGHT;
3879  break;
3880 
3881  case T_bottom_left:
3882  chamfers |= RECT_CHAMFER_BOTTOM_LEFT;
3883  break;
3884 
3885  case T_bottom_right:
3886  chamfers |= RECT_CHAMFER_BOTTOM_RIGHT;
3887  break;
3888 
3889  case T_RIGHT:
3890  pad->SetChamferPositions( chamfers );
3891  end_list = true;
3892  break;
3893 
3894  default:
3895  Expecting( "chamfer_top_left chamfer_top_right chamfer_bottom_left or chamfer_bottom_right" );
3896  }
3897  }
3898 
3899  if( pad->GetChamferPositions() != RECT_NO_CHAMFER )
3900  pad->SetShape( PAD_SHAPE_CHAMFERED_RECT );
3901  }
3902  break;
3903 
3904  case T_property:
3905  {
3906  while( token != T_RIGHT )
3907  {
3908  token = NextTok();
3909 
3910  switch( token )
3911  {
3912  case T_pad_prop_bga:
3913  pad->SetProperty( PAD_PROP_BGA );
3914  break;
3915 
3916  case T_pad_prop_fiducial_glob:
3917  pad->SetProperty( PAD_PROP_FIDUCIAL_GLBL );
3918  break;
3919 
3920  case T_pad_prop_fiducial_loc:
3921  pad->SetProperty( PAD_PROP_FIDUCIAL_LOCAL );
3922  break;
3923 
3924  case T_pad_prop_testpoint:
3925  pad->SetProperty( PAD_PROP_TESTPOINT );
3926  break;
3927 
3928  case T_pad_prop_castellated:
3929  pad->SetProperty( PAD_PROP_CASTELLATED );
3930  break;
3931 
3932  case T_pad_prop_heatsink:
3933  pad->SetProperty( PAD_PROP_HEATSINK );
3934  break;
3935 
3936  case T_none:
3937  pad->SetProperty( PAD_PROP_NONE );
3938  break;
3939 
3940  case T_RIGHT:
3941  break;
3942 
3943  default:
3944 #if 0 // Currently: skip unknown property
3945  Expecting( "pad_prop_bga pad_prop_fiducial_glob pad_prop_fiducial_loc"
3946  " pad_prop_heatsink or pad_prop_castellated" );
3947 #endif
3948  break;
3949  }
3950  }
3951  }
3952  break;
3953 
3954  case T_options:
3955  parsePAD_option( pad.get() );
3956  break;
3957 
3958  case T_primitives:
3959  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3960  {
3961  if( token == T_LEFT )
3962  token = NextTok();
3963 
3964  // Currently, I am using parsePCB_SHAPE() to read basic shapes parameters,
3965  // because they are the same as a PCB_SHAPE.
3966  // However it could be better to write a specific parser, to avoid possible issues
3967  // if the PCB_SHAPE parser is modified.
3968  PCB_SHAPE* dummysegm = NULL;
3969 
3970  switch( token )
3971  {
3972  case T_gr_arc:
3973  dummysegm = parsePCB_SHAPE();
3974  pad->AddPrimitiveArc( dummysegm->GetCenter(), dummysegm->GetArcStart(),
3975  dummysegm->GetAngle(), dummysegm->GetWidth() );
3976  break;
3977 
3978  case T_gr_line:
3979  dummysegm = parsePCB_SHAPE();
3980  pad->AddPrimitiveSegment( dummysegm->GetStart(), dummysegm->GetEnd(),
3981  dummysegm->GetWidth() );
3982  break;
3983 
3984  case T_gr_circle:
3985  dummysegm = parsePCB_SHAPE();
3986  pad->AddPrimitiveCircle( dummysegm->GetCenter(), dummysegm->GetRadius(),
3987  dummysegm->GetWidth(), dummysegm->IsFilled() );
3988  break;
3989 
3990  case T_gr_rect:
3991  dummysegm = parsePCB_SHAPE();
3992  pad->AddPrimitiveRect( dummysegm->GetStart(), dummysegm->GetEnd(),
3993  dummysegm->GetWidth(), dummysegm->IsFilled() );
3994  break;
3995 
3996 
3997  case T_gr_poly:
3998  dummysegm = parsePCB_SHAPE();
3999  pad->AddPrimitivePoly( dummysegm->BuildPolyPointsList(), dummysegm->GetWidth(),
4000  dummysegm->IsFilled() );
4001  break;
4002 
4003  case T_gr_curve:
4004  dummysegm = parsePCB_SHAPE();
4005  pad->AddPrimitiveCurve( dummysegm->GetStart(), dummysegm->GetEnd(),
4006  dummysegm->GetBezControl1(),
4007  dummysegm->GetBezControl2(), dummysegm->GetWidth() );
4008  break;
4009 
4010  default:
4011  Expecting( "gr_line, gr_arc, gr_circle, gr_curve, gr_rect or gr_poly" );
4012  break;
4013  }
4014 
4015  delete dummysegm;
4016  }
4017  break;
4018 
4019  case T_remove_unused_layers:
4020  pad->SetRemoveUnconnected( true );
4021  NeedRIGHT();
4022  break;
4023 
4024  case T_keep_end_layers:
4025  pad->SetKeepTopBottom( true );
4026  NeedRIGHT();
4027  break;
4028 
4029  case T_locked:
4030  pad->SetLocked( true );
4031  NeedRIGHT();
4032  break;
4033 
4034  case T_tstamp:
4035  NextTok();
4036  const_cast<KIID&>( pad->m_Uuid ) = CurStrToKIID();
4037  NeedRIGHT();
4038  break;
4039 
4040  default:
4041  Expecting( "at, locked, drill, layers, net, die_length, roundrect_rratio, "
4042  "solder_mask_margin, solder_paste_margin, solder_paste_margin_ratio, "
4043  "clearance, tstamp, primitives, remove_unused_layers, keep_end_layers, "
4044  "pinfunction, pintype, zone_connect, thermal_width, or thermal_gap" );
4045  }
4046  }
4047 
4048  return pad.release();
4049 }
4050 
4051 
4053 {
4054  // Parse only the (option ...) inside a pad description
4055  for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
4056  {
4057  if( token != T_LEFT )
4058  Expecting( T_LEFT );
4059 
4060  token = NextTok();
4061 
4062  switch( token )
4063  {
4064  case T_anchor:
4065  token = NextTok();
4066  // Custom shaped pads have a "anchor pad", which is the reference
4067  // for connection calculations.
4068  // Because this is an anchor, only the 2 very basic shapes are managed:
4069  // circle and rect. The default is circle
4070  switch( token )
4071  {
4072  case T_circle: // default
4073  break;
4074 
4075  case T_rect:
4077  break;
4078 
4079  default:
4080  // Currently, because pad options is a moving target
4081  // just skip unknown keywords
4082  break;
4083  }
4084  NeedRIGHT();
4085  break;
4086 
4087  case T_clearance:
4088  token = NextTok();
4089  // Custom shaped pads have a clearance area that is the pad shape
4090  // (like usual pads) or the convex hull of the pad shape.
4091  switch( token )
4092  {
4093  case T_outline:
4095  break;
4096 
4097  case T_convexhull:
4099  break;
4100 
4101  default:
4102  // Currently, because pad options is a moving target
4103  // just skip unknown keywords
4104  break;
4105  }
4106  NeedRIGHT();
4107  break;
4108 
4109  default:
4110  // Currently, because pad options is a moving target
4111  // just skip unknown keywords
4112  while( (token = NextTok() ) != T_RIGHT )
4113  {}
4114  break;
4115  }
4116  }
4117 
4118  return true;
4119 }
4120 
4121 
4122 // Example of group format:
4123 // (group <(name “groupName”)> (id 12345679)
4124 // (members id_1 id_2 … id_last )
4125 // )
4127 {
4128  wxCHECK_RET( CurTok() == T_group,
4129  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_GROUP." ) );
4130 
4131  wxPoint pt;
4132  T token;
4133 
4134  m_groupInfos.push_back( GROUP_INFO() );
4135  GROUP_INFO& groupInfo = m_groupInfos.back();
4136  groupInfo.parent = aParent;
4137 
4138  token = NextTok();
4139 
4140  if( token != T_LEFT )
4141  {
4142  // Optional group name present.
4143 
4144  if( !IsSymbol( token ) )
4145  Expecting( DSN_SYMBOL );
4146 
4147  groupInfo.name = FromUTF8();
4148  }
4149 
4150  NeedLEFT();
4151  token = NextTok();
4152 
4153  if( token != T_id )
4154  {
4155  Expecting( T_id );
4156  }
4157 
4158  NextTok();
4159  groupInfo.uuid = CurStrToKIID();
4160  NeedRIGHT();
4161 
4162  NeedLEFT();
4163  token = NextTok();
4164 
4165  if( token != T_members )
4166  {
4167  Expecting( T_members );
4168  }
4169 
4170  while( ( token = NextTok() ) != T_RIGHT )
4171  {
4172  // This token is the Uuid of the item in the group.
4173  // Since groups are serialized at the end of the file/footprint, the Uuid should already
4174  // have been seen and exist in the board.
4175  KIID uuid( CurStr() );
4176  groupInfo.memberUuids.push_back( uuid );
4177  }
4178 
4179  NeedRIGHT();
4180 }
4181 
4182 
4184 {
4185  wxCHECK_MSG( CurTok() == T_arc, NULL,
4186  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as ARC." ) );
4187 
4188  wxPoint pt;
4189  T token;
4190 
4191  std::unique_ptr<ARC> arc = std::make_unique<ARC>( m_board );
4192 
4193  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4194  {
4195  if( token != T_LEFT )
4196  Expecting( T_LEFT );
4197 
4198  token = NextTok();
4199 
4200  switch( token )
4201  {
4202  case T_start:
4203  pt.x = parseBoardUnits( "start x" );
4204  pt.y = parseBoardUnits( "start y" );
4205  arc->SetStart( pt );
4206  break;
4207 
4208  case T_mid:
4209  pt.x = parseBoardUnits( "mid x" );
4210  pt.y = parseBoardUnits( "mid y" );
4211  arc->SetMid( pt );
4212  break;
4213 
4214  case T_end:
4215  pt.x = parseBoardUnits( "end x" );
4216  pt.y = parseBoardUnits( "end y" );
4217  arc->SetEnd( pt );
4218  break;
4219 
4220  case T_width:
4221  arc->SetWidth( parseBoardUnits( "width" ) );
4222  break;
4223 
4224  case T_layer:
4225  arc->SetLayer( parseBoardItemLayer() );
4226  break;
4227 
4228  case T_net:
4229  if( !arc->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
4231  _( "Invalid net ID in\nfile: \"%s\"\nline: %d\noffset: %d" ), CurSource(),
4232  CurLineNumber(), CurOffset() ) );
4233  break;
4234 
4235  case T_tstamp:
4236  NextTok();
4237  const_cast<KIID&>( arc->m_Uuid ) = CurStrToKIID();
4238  break;
4239 
4241  case T_status:
4242  arc->SetStatus( static_cast<STATUS_FLAGS>( parseHex() ) );
4243  break;
4244 
4245  case T_locked:
4246  arc->SetLocked( true );
4247  break;
4248 
4249  default:
4250  Expecting( "start, mid, end, width, layer, net, tstamp, or status" );
4251  }
4252 
4253  NeedRIGHT();
4254  }
4255 
4256  return arc.release();
4257 }
4258 
4259 
4261 {
4262  wxCHECK_MSG( CurTok() == T_segment, NULL,
4263  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as TRACK." ) );
4264 
4265  wxPoint pt;
4266  T token;
4267 
4268  std::unique_ptr<TRACK> track = std::make_unique<TRACK>( m_board );
4269 
4270  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4271  {
4272  if( token != T_LEFT )
4273  Expecting( T_LEFT );
4274 
4275  token = NextTok();
4276 
4277  switch( token )
4278  {
4279  case T_start:
4280  pt.x = parseBoardUnits( "start x" );
4281  pt.y = parseBoardUnits( "start y" );
4282  track->SetStart( pt );
4283  break;
4284 
4285  case T_end:
4286  pt.x = parseBoardUnits( "end x" );
4287  pt.y = parseBoardUnits( "end y" );
4288  track->SetEnd( pt );
4289  break;
4290 
4291  case T_width:
4292  track->SetWidth( parseBoardUnits( "width" ) );
4293  break;
4294 
4295  case T_layer:
4296  track->SetLayer( parseBoardItemLayer() );
4297  break;
4298 
4299  case T_net:
4300  if( !track->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
4302  _( "Invalid net ID in\nfile: \"%s\"\nline: %d\noffset: %d" ), CurSource(),
4303  CurLineNumber(), CurOffset() ) );
4304  break;
4305 
4306  case T_tstamp:
4307  NextTok();
4308  const_cast<KIID&>( track->m_Uuid ) = CurStrToKIID();
4309  break;
4310 
4312  case T_status:
4313  track->SetStatus( static_cast<STATUS_FLAGS>( parseHex() ) );
4314  break;
4315 
4316  case T_locked:
4317  track->SetLocked( true );
4318  break;
4319 
4320  default:
4321  Expecting( "start, end, width, layer, net, tstamp, or locked" );
4322  }
4323 
4324  NeedRIGHT();
4325  }
4326 
4327  return track.release();
4328 }
4329 
4330 
4332 {
4333  wxCHECK_MSG( CurTok() == T_via, NULL,
4334  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as VIA." ) );
4335 
4336  wxPoint pt;
4337  T token;
4338 
4339  std::unique_ptr<VIA> via = std::make_unique<VIA>( m_board );
4340 
4341  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4342  {
4343  if( token == T_LEFT )
4344  token = NextTok();
4345 
4346  switch( token )
4347  {
4348  case T_blind:
4349  via->SetViaType( VIATYPE::BLIND_BURIED );
4350  break;
4351 
4352  case T_micro:
4353  via->SetViaType( VIATYPE::MICROVIA );
4354  break;
4355 
4356  case T_at:
4357  pt.x = parseBoardUnits( "start x" );
4358  pt.y = parseBoardUnits( "start y" );
4359  via->SetStart( pt );
4360  via->SetEnd( pt );
4361  NeedRIGHT();
4362  break;
4363 
4364  case T_size:
4365  via->SetWidth( parseBoardUnits( "via width" ) );
4366  NeedRIGHT();
4367  break;
4368 
4369  case T_drill:
4370  via->SetDrill( parseBoardUnits( "drill diameter" ) );
4371  NeedRIGHT();
4372  break;
4373 
4374  case T_layers:
4375  {
4376  PCB_LAYER_ID layer1, layer2;
4377  NextTok();
4378  layer1 = lookUpLayer<PCB_LAYER_ID>( m_layerIndices );
4379  NextTok();
4380  layer2 = lookUpLayer<PCB_LAYER_ID>( m_layerIndices );
4381  via->SetLayerPair( layer1, layer2 );
4382  NeedRIGHT();
4383  }
4384  break;
4385 
4386  case T_net:
4387  if( !via->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
4388  {
4389  THROW_IO_ERROR( wxString::Format( _( "Invalid net ID in\n"
4390  "file: '%s'\n"
4391  "line: %d\n"
4392  "offset: %d" ),
4393  CurSource(),
4394  CurLineNumber(),
4395  CurOffset() ) );
4396  }
4397 
4398  NeedRIGHT();
4399  break;
4400 
4401  case T_remove_unused_layers:
4402  via->SetRemoveUnconnected( true );
4403  NeedRIGHT();
4404  break;
4405 
4406  case T_keep_end_layers:
4407  via->SetKeepTopBottom( true );
4408  NeedRIGHT();
4409  break;
4410 
4411  case T_tstamp:
4412  NextTok();
4413  const_cast<KIID&>( via->m_Uuid ) = CurStrToKIID();
4414  NeedRIGHT();
4415  break;
4416 
4418  case T_status:
4419  via->SetStatus( static_cast<STATUS_FLAGS>( parseHex() ) );
4420  NeedRIGHT();
4421  break;
4422 
4423  case T_locked:
4424  via->SetLocked( true );
4425  NeedRIGHT();
4426  break;
4427 
4428  case T_free:
4429  via->SetIsFree();
4430  NeedRIGHT();
4431  break;
4432 
4433  default:
4434  Expecting( "blind, micro, at, size, drill, layers, net, free, tstamp, or status" );
4435  }
4436  }
4437 
4438  return via.release();
4439 }
4440 
4441 
4443 {
4444  wxCHECK_MSG( CurTok() == T_zone, NULL,
4445  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
4446  wxT( " as ZONE." ) );
4447 
4449 
4450  int hatchPitch = ZONE::GetDefaultHatchPitch();
4451  wxPoint pt;
4452  T token;
4453  int tmp;
4454  wxString netnameFromfile; // the zone net name find in file
4455 
4456  // bigger scope since each filled_polygon is concatenated in here
4457  std::map<PCB_LAYER_ID, SHAPE_POLY_SET> pts;
4458  bool inFootprint = false;
4459  PCB_LAYER_ID filledLayer;
4460  bool addedFilledPolygons = false;
4461 
4462  if( dynamic_cast<FOOTPRINT*>( aParent ) ) // The zone belongs a footprint
4463  inFootprint = true;
4464 
4465  std::unique_ptr<ZONE> zone;
4466 
4467  if( inFootprint )
4468  zone = std::make_unique<FP_ZONE>( aParent );
4469  else
4470  zone = std::make_unique<ZONE>( aParent );
4471 
4472  zone->SetPriority( 0 );
4473 
4474  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4475  {
4476  if( token == T_LEFT )
4477  token = NextTok();
4478 
4479  switch( token )
4480  {
4481  case T_net:
4482  // Init the net code only, not the netname, to be sure
4483  // the zone net name is the name read in file.
4484  // (When mismatch, the user will be prompted in DRC, to fix the actual name)
4485  tmp = getNetCode( parseInt( "net number" ) );
4486 
4487  if( tmp < 0 )
4488  tmp = 0;
4489 
4490  if( !zone->SetNetCode( tmp, /* aNoAssert */ true ) )
4492  _( "Invalid net ID in\nfile: \"%s\"\nline: %d\noffset: %d" ), CurSource(),
4493  CurLineNumber(), CurOffset() ) );
4494 
4495  NeedRIGHT();
4496  break;
4497 
4498  case T_net_name:
4499  NeedSYMBOLorNUMBER();
4500  netnameFromfile = FromUTF8();
4501  NeedRIGHT();
4502  break;
4503 
4504  case T_layer: // keyword for zones that are on only one layer
4505  zone->SetLayer( parseBoardItemLayer() );
4506  NeedRIGHT();
4507  break;
4508 
4509  case T_layers: // keyword for zones that can live on a set of layers
4510  zone->SetLayerSet( parseBoardItemLayersAsMask() );
4511  break;
4512 
4513  case T_tstamp:
4514  NextTok();
4515  const_cast<KIID&>( zone->m_Uuid ) = CurStrToKIID();
4516  NeedRIGHT();
4517  break;
4518 
4519  case T_hatch:
4520  token = NextTok();
4521 
4522  if( token != T_none && token != T_edge && token != T_full )
4523  Expecting( "none, edge, or full" );
4524 
4525  switch( token )
4526  {
4527  default:
4528  case T_none: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH; break;
4529  case T_edge: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; break;
4530  case T_full: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL; break;
4531  }
4532 
4533  hatchPitch = parseBoardUnits( "hatch pitch" );
4534  NeedRIGHT();
4535  break;
4536 
4537  case T_priority:
4538  zone->SetPriority( parseInt( "zone priority" ) );
4539  NeedRIGHT();
4540  break;
4541 
4542  case T_connect_pads:
4543  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4544  {
4545  if( token == T_LEFT )
4546  token = NextTok();
4547 
4548  switch( token )
4549  {
4550  case T_yes:
4551  zone->SetPadConnection( ZONE_CONNECTION::FULL );
4552  break;
4553 
4554  case T_no:
4555  zone->SetPadConnection( ZONE_CONNECTION::NONE );
4556  break;
4557 
4558  case T_thru_hole_only:
4559  zone->SetPadConnection( ZONE_CONNECTION::THT_THERMAL );
4560  break;
4561 
4562  case T_clearance:
4563  zone->SetLocalClearance( parseBoardUnits( "zone clearance" ) );
4564  NeedRIGHT();
4565  break;
4566 
4567  default:
4568  Expecting( "yes, no, or clearance" );
4569  }
4570  }
4571 
4572  break;
4573 
4574  case T_min_thickness:
4575  zone->SetMinThickness( parseBoardUnits( T_min_thickness ) );
4576  NeedRIGHT();
4577  break;
4578 
4579  case T_filled_areas_thickness:
4580  zone->SetFillVersion( parseBool() ? 5 : 6 );
4581  NeedRIGHT();
4582  break;
4583 
4584  case T_fill:
4585  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4586  {
4587  if( token == T_LEFT )
4588  token = NextTok();
4589 
4590  switch( token )
4591  {
4592  case T_yes:
4593  zone->SetIsFilled( true );
4594  break;
4595 
4596  case T_mode:
4597  token = NextTok();
4598 
4599  if( token != T_segment && token != T_hatch && token != T_polygon )
4600  Expecting( "segment, hatch or polygon" );
4601 
4602  if( token == T_segment ) // deprecated
4603  {
4604  // SEGMENT fill mode no longer supported. Make sure user is OK with converting them.
4605  if( m_showLegacyZoneWarning )
4606  {
4607  KIDIALOG dlg( nullptr,
4608  _( "The legacy segment fill mode is no longer supported.\n"
4609  "Convert zones to polygon fills?"),
4610  _( "Legacy Zone Warning" ),
4611  wxYES_NO | wxICON_WARNING );
4612 
4613  dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
4614 
4615  if( dlg.ShowModal() == wxID_NO )
4616  THROW_IO_ERROR( wxT( "CANCEL" ) );
4617 
4618  m_showLegacyZoneWarning = false;
4619  }
4620 
4621  zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
4622  m_board->SetModified();
4623  }
4624  else if( token == T_hatch )
4625  {
4626  zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
4627  }
4628  else
4629  {
4630  zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
4631  }
4632 
4633  NeedRIGHT();
4634  break;
4635 
4636  case T_hatch_thickness:
4637  zone->SetHatchThickness( parseBoardUnits( T_hatch_thickness ) );
4638  NeedRIGHT();
4639  break;
4640 
4641  case T_hatch_gap:
4642  zone->SetHatchGap( parseBoardUnits( T_hatch_gap ) );
4643  NeedRIGHT();
4644  break;
4645 
4646  case T_hatch_orientation:
4647  zone->SetHatchOrientation( parseDouble( T_hatch_orientation ) );
4648  NeedRIGHT();
4649  break;
4650 
4651  case T_hatch_smoothing_level:
4652  zone->SetHatchSmoothingLevel( parseDouble( T_hatch_smoothing_level ) );
4653  NeedRIGHT();
4654  break;
4655 
4656  case T_hatch_smoothing_value:
4657  zone->SetHatchSmoothingValue( parseDouble( T_hatch_smoothing_value ) );
4658  NeedRIGHT();
4659  break;
4660 
4661  case T_hatch_border_algorithm:
4662  token = NextTok();
4663 
4664  if( token != T_hatch_thickness && token != T_min_thickness )
4665  Expecting( "hatch_thickness or min_thickness" );
4666 
4667  zone->SetHatchBorderAlgorithm( token == T_hatch_thickness ? 1 : 0 );
4668  NeedRIGHT();
4669  break;
4670 
4671  case T_hatch_min_hole_area:
4672  zone->SetHatchHoleMinArea( parseDouble( T_hatch_min_hole_area ) );
4673  NeedRIGHT();
4674  break;
4675 
4676  case T_arc_segments:
4677  static_cast<void>( parseInt( "arc segment count" ) );
4678  NeedRIGHT();
4679  break;
4680 
4681  case T_thermal_gap:
4682  zone->SetThermalReliefGap( parseBoardUnits( T_thermal_gap ) );
4683  NeedRIGHT();
4684  break;
4685 
4686  case T_thermal_bridge_width:
4687  zone->SetThermalReliefSpokeWidth( parseBoardUnits( T_thermal_bridge_width ) );
4688  NeedRIGHT();
4689  break;
4690 
4691  case T_smoothing:
4692  switch( NextTok() )
4693  {
4694  case T_none:
4695  zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_NONE );
4696  break;
4697 
4698  case T_chamfer:
4699  if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
4700  zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_CHAMFER );
4701  break;
4702 
4703  case T_fillet:
4704  if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
4705  zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_FILLET );
4706  break;
4707 
4708  default:
4709  Expecting( "none, chamfer, or fillet" );
4710  }
4711  NeedRIGHT();
4712  break;
4713 
4714  case T_radius:
4715  tmp = parseBoardUnits( "corner radius" );
4716  if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
4717  zone->SetCornerRadius( tmp );
4718  NeedRIGHT();
4719  break;
4720 
4721 
4722  case T_island_removal_mode:
4723  tmp = parseInt( "island_removal_mode" );
4724 
4725  if( tmp >= 0 && tmp <= 2 )
4726  zone->SetIslandRemovalMode( static_cast<ISLAND_REMOVAL_MODE>( tmp ) );
4727 
4728  NeedRIGHT();
4729  break;
4730 
4731  case T_island_area_min:
4732  {
4733  int area = parseBoardUnits( T_island_area_min );
4734  zone->SetMinIslandArea( area * IU_PER_MM );
4735  NeedRIGHT();
4736  break;
4737  }
4738 
4739  default:
4740  Expecting( "mode, arc_segments, thermal_gap, thermal_bridge_width, "
4741  "hatch_thickness, hatch_gap, hatch_orientation, "
4742  "hatch_smoothing_level, hatch_smoothing_value, "
4743  "hatch_border_algorithm, hatch_min_hole_area, smoothing, radius, "
4744  "island_removal_mode, or island_area_min" );
4745  }
4746  }
4747  break;
4748 
4749  case T_keepout:
4750  // "keepout" now means rule area, but the file token stays the same
4751  zone->SetIsRuleArea( true );
4752 
4753  // Initialize these two because their tokens won't appear in older files:
4754  zone->SetDoNotAllowPads( false );
4755  zone->SetDoNotAllowFootprints( false );
4756 
4757  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4758  {
4759  if( token == T_LEFT )
4760  token = NextTok();
4761 
4762  switch( token )
4763  {
4764  case T_tracks:
4765  token = NextTok();
4766 
4767  if( token != T_allowed && token != T_not_allowed )
4768  Expecting( "allowed or not_allowed" );
4769  zone->SetDoNotAllowTracks( token == T_not_allowed );
4770  break;
4771 
4772  case T_vias:
4773  token = NextTok();
4774 
4775  if( token != T_allowed && token != T_not_allowed )
4776  Expecting( "allowed or not_allowed" );
4777  zone->SetDoNotAllowVias( token == T_not_allowed );
4778  break;
4779 
4780  case T_copperpour:
4781  token = NextTok();
4782 
4783  if( token != T_allowed && token != T_not_allowed )
4784  Expecting( "allowed or not_allowed" );
4785  zone->SetDoNotAllowCopperPour( token == T_not_allowed );
4786  break;
4787 
4788  case T_pads:
4789  token = NextTok();
4790 
4791  if( token != T_allowed && token != T_not_allowed )
4792  Expecting( "allowed or not_allowed" );
4793  zone->SetDoNotAllowPads( token == T_not_allowed );
4794  break;
4795 
4796  case T_footprints:
4797  token = NextTok();
4798 
4799  if( token != T_allowed && token != T_not_allowed )
4800  Expecting( "allowed or not_allowed" );
4801  zone->SetDoNotAllowFootprints( token == T_not_allowed );
4802  break;
4803 
4804  default:
4805  Expecting( "tracks, vias or copperpour" );
4806  }
4807 
4808  NeedRIGHT();
4809  }
4810 
4811  break;
4812 
4813  case T_polygon:
4814  {
4815  std::vector< wxPoint > corners;
4816 
4817  NeedLEFT();
4818  token = NextTok();
4819 
4820  if( token != T_pts )
4821  Expecting( T_pts );
4822 
4823  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4824  {
4825  corners.push_back( parseXY() );
4826  }
4827 
4828  NeedRIGHT();
4829 
4830  // Remark: The first polygon is the main outline.
4831  // Others are holes inside the main outline.
4832  zone->AddPolygon( corners );
4833  }
4834  break;
4835 
4836  case T_filled_polygon:
4837  {
4838  // "(filled_polygon (pts"
4839  NeedLEFT();
4840  token = NextTok();
4841 
4842  if( token == T_layer )
4843  {
4844  filledLayer = parseBoardItemLayer();
4845  NeedRIGHT();
4846  token = NextTok();
4847 
4848  if( token != T_LEFT )
4849  Expecting( T_LEFT );
4850 
4851  token = NextTok();
4852  }
4853  else
4854  {
4855  filledLayer = zone->GetLayer();
4856  }
4857 
4858  bool island = false;
4859 
4860  if( token == T_island )
4861  {
4862  island = true;
4863  NeedRIGHT();
4864  NeedLEFT();
4865  token = NextTok();
4866  }
4867 
4868  if( token != T_pts )
4869  Expecting( T_pts );
4870 
4871  if( !pts.count( filledLayer ) )
4872  pts[filledLayer] = SHAPE_POLY_SET();
4873 
4874  SHAPE_POLY_SET& poly = pts.at( filledLayer );
4875 
4876  int idx = poly.NewOutline();
4877 
4878  if( island )
4879  zone->SetIsIsland( filledLayer, idx );
4880 
4881  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4882  {
4883  poly.Append( parseXY() );
4884  }
4885 
4886  NeedRIGHT();
4887 
4888  addedFilledPolygons |= !poly.IsEmpty();
4889  }
4890  break;
4891 
4892  case T_fill_segments:
4893  {
4894  ZONE_SEGMENT_FILL segs;
4895 
4896  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4897  {
4898  if( token != T_LEFT )
4899  Expecting( T_LEFT );
4900 
4901  token = NextTok();
4902 
4903  if( token == T_layer )
4904  {
4905  filledLayer = parseBoardItemLayer();
4906  NeedRIGHT();
4907  token = NextTok();
4908 
4909  if( token != T_LEFT )
4910  Expecting( T_LEFT );
4911 
4912  token = NextTok();
4913  }
4914  else
4915  {
4916  filledLayer = zone->GetLayer();
4917  }
4918 
4919  if( token != T_pts )
4920  Expecting( T_pts );
4921 
4922  SEG segment( parseXY(), parseXY() );
4923  NeedRIGHT();
4924  segs.push_back( segment );
4925  }
4926 
4927  zone->SetFillSegments( filledLayer, segs );
4928  }
4929  break;
4930 
4931  case T_name:
4932  {
4933  NextTok();
4934  zone->SetZoneName( FromUTF8() );
4935  NeedRIGHT();
4936  }
4937  break;
4938 
4939  default:
4940  Expecting( "net, layer/layers, tstamp, hatch, priority, connect_pads, min_thickness, "
4941  "fill, polygon, filled_polygon, fill_segments, or name" );
4942  }
4943  }
4944 
4945  if( zone->GetNumCorners() > 2 )
4946  {
4947  if( !zone->IsOnCopperLayer() )
4948  {
4949  //zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
4950  zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
4951  }
4952 
4953  // Set hatch here, after outlines corners are read
4954  zone->SetBorderDisplayStyle( hatchStyle, hatchPitch, true );
4955  }
4956 
4957  if( addedFilledPolygons )
4958  {
4959  for( auto& pair : pts )
4960  zone->SetFilledPolysList( pair.first, pair.second );
4961 
4962  zone->CalculateFilledArea();
4963  }
4964 
4965  // Ensure keepout and non copper zones do not have a net
4966  // (which have no sense for these zones)
4967  // the netcode 0 is used for these zones
4968  bool zone_has_net = zone->IsOnCopperLayer() && !zone->GetIsRuleArea();
4969 
4970  if( !zone_has_net )
4971  zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
4972 
4973  // Ensure the zone net name is valid, and matches the net code, for copper zones
4974  if( zone_has_net && ( zone->GetNet()->GetNetname() != netnameFromfile ) )
4975  {
4976  // Can happens which old boards, with nonexistent nets ...
4977  // or after being edited by hand
4978  // We try to fix the mismatch.
4979  NETINFO_ITEM* net = m_board->FindNet( netnameFromfile );
4980 
4981  if( net ) // An existing net has the same net name. use it for the zone
4982  {
4983  zone->SetNetCode( net->GetNetCode() );
4984  }
4985  else // Not existing net: add a new net to keep trace of the zone netname
4986  {
4987  int newnetcode = m_board->GetNetCount();
4988  net = new NETINFO_ITEM( m_board, netnameFromfile, newnetcode );
4989  m_board->Add( net );
4990 
4991  // Store the new code mapping
4992  pushValueIntoMap( newnetcode, net->GetNetCode() );
4993  // and update the zone netcode
4994  zone->SetNetCode( net->GetNetCode() );
4995  }
4996  }
4997 
4998  // Clear flags used in zone edition:
4999  zone->SetNeedRefill( false );
5000 
5001  return zone.release();
5002 }
5003 
5004 
5006 {
5007  wxCHECK_MSG( CurTok() == T_target, NULL,
5008  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TARGET." ) );
5009 
5010  wxPoint pt;
5011  T token;
5012 
5013  std::unique_ptr<PCB_TARGET> target = std::make_unique<PCB_TARGET>( nullptr );
5014 
5015  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5016  {
5017  if( token == T_LEFT )
5018  token = NextTok();
5019 
5020  switch( token )
5021  {
5022  case T_x:
5023  target->SetShape( 1 );
5024  break;
5025 
5026  case T_plus:
5027  target->SetShape( 0 );
5028  break;
5029 
5030  case T_at:
5031  pt.x = parseBoardUnits( "target x position" );
5032  pt.y = parseBoardUnits( "target y position" );
5033  target->SetPosition( pt );
5034  NeedRIGHT();
5035  break;
5036 
5037  case T_size:
5038  target->SetSize( parseBoardUnits( "target size" ) );
5039  NeedRIGHT();
5040  break;
5041 
5042  case T_width:
5043  target->SetWidth( parseBoardUnits( "target thickness" ) );
5044  NeedRIGHT();
5045  break;
5046 
5047  case T_layer:
5048  target->SetLayer( parseBoardItemLayer() );
5049  NeedRIGHT();
5050  break;
5051 
5052  case T_tstamp:
5053  NextTok();
5054  const_cast<KIID&>( target->m_Uuid ) = CurStrToKIID();
5055  NeedRIGHT();
5056  break;
5057 
5058  default:
5059  Expecting( "x, plus, at, size, width, layer or tstamp" );
5060  }
5061  }
5062 
5063  return target.release();
5064 }
5065 
5066 
5068 {
5069  KIID aId;
5070 
5071  if( m_resetKIIDs )
5072  {
5073  aId = KIID();
5074  m_resetKIIDMap.insert( std::make_pair( CurStr(), aId ) );
5075  }
5076  else
5077  {
5078  aId = KIID( CurStr() );
5079  }
5080 
5081  return aId;
5082 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:195
void parseHeader()
Definition: pcb_parser.cpp:849
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:750
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition: utf8.h:70
int Mm2mils(double x)
Convert mm to mils.
Definition: base_units.h:62
ZONE_CONNECTION
How pads are covered by copper in zone.
Definition: zones.h:41
Container to handle a stock of specific vias each with unique diameter and drill sizes in the BOARD c...
int m_ZoneFillVersion
Option to select different fill algorithms.
bool AddItem(BOARD_ITEM *aItem)
Add item to group.
Definition: pcb_group.cpp:38
void LayerPair(PCB_LAYER_ID *top_layer, PCB_LAYER_ID *bottom_layer) const
Function LayerPair Return the 2 layers used by the via (the via actually uses all layers between thes...
Definition: track.cpp:435
Definition: track.h:343
bool IsFilled() const
Definition: pcb_shape.h:96
void SetBrdLayerId(PCB_LAYER_ID aBrdLayerId)
void SetTypeName(const wxString &aName)
void DoNotShowCheckbox(wxString file, int line)
Checks the 'do not show again' setting for the dialog.
Definition: confirm.cpp:56
KIID CurStrToKIID()
class ALIGNED_DIMENSION, a linear dimension (graphic item)
Definition: typeinfo.h:100
class LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:101
LSET parseBoardItemLayersAsMask()
Parse the layers definition of a BOARD_ITEM object.
class FP_TEXT, text in a footprint
Definition: typeinfo.h:92
wxPoint m_GridOrigin
origin for grid offsets
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
no special fabrication property
Definition: pad_shapes.h:96
wxString m_name
The canonical name of the layer.
Definition: board.h:103
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition: confirm.h:45
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
Definition: pcb_shape.h:156
this class manage the layers needed to make a physical board they are solder mask,...
This file is part of the common library.
PCB_PLOT_PARAMS_PARSER is the parser class for PCB_PLOT_PARAMS.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:82
std::vector< int > m_TrackWidthList
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:108
void SetRevision(const wxString &aRevision)
Definition: title_block.h:81
TRACK * parseTRACK()
wxString m_FinishType
The name of external copper finish.
static constexpr double IU_PER_MM
Mock up a conversion function.
VIA * parseVIA()
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:50
std::vector< DIFF_PAIR_DIMENSION > m_DiffPairDimensionsList
bool m_EdgePlating
True if the edge board is plated.
void SetTextAngle(double aAngle) override
Definition: fp_text.cpp:70
a fiducial (usually a smd) for the full board
Definition: pad_shapes.h:98
the 3d code uses this value
Definition: typeinfo.h:79
static uint32_t parseHex(LINE_READER &aReader, const char *aLine, const char **aOutput=NULL)
Parse an ASCII hex integer string with possible leading whitespace into a long integer and updates th...
int GetRadius() const
Function GetRadius returns the radius of this item Has meaning only for arc and circle.
Definition: pcb_shape.h:201
LAYER_T m_type
The type of the layer.
Definition: board.h:105
void SetItalic(bool isItalic)
Definition: eda_text.h:186
std::vector< KIID > memberUuids
Definition: pcb_parser.h:381
bool IsEmpty() const
int GetWidth() const
Definition: pcb_shape.h:118
BS_EDGE_CONNECTOR_CONSTRAINTS m_EdgeConnectorConstraints
If the board has edge connector cards, some constrains can be specified in job file: BS_EDGE_CONNECTO...
void parseBoardStackup()
void SetAnchorPadShape(PAD_SHAPE_T aShape)
Set the shape of the anchor pad for custom shaped pads.
Definition: pad.h:208
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:81
wxPoint parseXY()
Parse a coordinate pair (xy X Y) in board units (mm).
Definition: pcb_parser.cpp:208
polygon (not yet used for tracks, but could be in microwave apps)
Definition: board_item.h:54
void SetVisible(bool aVisible)
Definition: eda_text.h:192
void parseNETINFO_ITEM()
bool SetType(const wxString &aStandardPageDescriptionName, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
Definition: page_info.cpp:119
void createOldLayerMapping(std::unordered_map< std::string, std::string > &aMap)
Create a mapping from the (short-lived) bug where layer names were translated.
bool m_CastellatedPads
True if castellated pads exist.
int m_DimensionPrecision
Number of digits after the decimal.
double GetTextAngle() const
Definition: eda_text.h:181
#define DEFAULT_LINE_WIDTH
void SetDrawCoord()
Set relative coordinates.
Definition: fp_text.cpp:199
static double parseDouble(LINE_READER &aReader, const char *aLine, const char **aOutput=NULL)
Parses an ASCII point string with possible leading whitespace into a double precision floating point ...
BOARD * parseBOARD()
Definition: pcb_parser.cpp:517
Smd pad, used in BGA footprints.
Definition: pad_shapes.h:97
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition: title_block.h:71
wxString AsString() const
Definition: kiid.cpp:213
Arcs (with rounded ends)
Definition: board_item.h:52
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
void parseEDA_TEXT(EDA_TEXT *aText)
Parse the common settings for any object derived from EDA_TEXT.
Definition: pcb_parser.cpp:255
static const wxChar Custom[]
"User" defined page type
Definition: page_info.h:77
ARC * parseARC()
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:244
void SetSize(const wxSize &aSize)
Definition: pad.h:231
void parseTITLE_BLOCK()
Definition: pcb_parser.cpp:979
PCB_SHAPE * parsePCB_SHAPE()
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:228
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition: pad_shapes.h:85
a pad used as heat sink, usually in SMD footprints
Definition: pad_shapes.h:101
segment with non rounded ends
Definition: board_item.h:51
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition: title_block.h:40
VECTOR3D m_Offset
3D model offset (mm)
Definition: footprint.h:95
Container to handle a stock of specific differential pairs each with unique track width,...
void SetViaDrill(int aSize)
Definition: netclass.h:137
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:165
void parseLayers()
DIM_UNITS_MODE m_DimensionUnitsMode
This file contains miscellaneous commonly used macros and functions.
wxPoint GetArcStart() const
Definition: pcb_shape.h:179
a pad with a castellated through hole
Definition: pad_shapes.h:102
FP_SHAPE * parseFP_SHAPE()
void SetExtensionHeight(int aHeight)
Definition: dimension.h:379
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:119
void parsePAGE_INFO()
Definition: pcb_parser.cpp:923
int StrPrintf(std::string *result, const char *format,...)
This is like sprintf() but the output is appended to a std::string instead of to a character array.
Definition: richio.cpp:78
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
wxSize m_TextSize[LAYER_CLASS_COUNT]
bool parseBool()
Definition: pcb_parser.cpp:169
void skipCurrent()
Skip the current token level, i.e search for the RIGHT parenthesis which closes the current descripti...
Definition: pcb_parser.cpp:106
bool m_HasDielectricConstrains
True if some layers have impedance controlled tracks or have specific constrains for micro-wave appli...
Definition: kiid.h:44
const std::vector< wxPoint > BuildPolyPointsList() const
Build and return the list of corners in a std::vector<wxPoint> It must be used only to convert the SH...
Definition: pcb_shape.cpp:1206
bool parsePAD_option(PAD *aPad)
bool m_Show
Include model in rendering.
Definition: footprint.h:98
PCB_LAYER_ID
A quick note on layer IDs:
wxString GetRequiredVersion()
Return a string representing the version of KiCad required to open this file.
Definition: pcb_parser.cpp:184
int m_TextThickness[LAYER_CLASS_COUNT]
void SetHeight(int aHeight)
Sets the distance from the feature points to the crossbar line.
Definition: dimension.h:370
LSET is a set of PCB_LAYER_IDs.
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:164
pads are covered by copper
void FetchUnitsFromString(const wxString &aTextValue, EDA_UNITS &aUnits)
Function FetchUnitsFromString writes any unit info found in the string to aUnits.
Definition: base_units.cpp:422
void SetComment(int aIdx, const wxString &aComment)
Definition: title_block.h:101
#define NULL
std::pair< wxString, wxString > parseProperty()
Definition: pcb_parser.cpp:240
static int GetDefaultHatchPitch()
Function GetDefaultHatchPitchMils.
Definition: zone.cpp:1084
static const wxChar * Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition: lset.cpp:82
#define MIN_VISIBILITY_MASK
A leader is a dimension-like object pointing to a specific point.
Definition: dimension.h:476
void SetClearance(int aClearance)
Definition: netclass.h:125
TEXT_TYPE GetType() const
Definition: fp_text.h:141
Represent a set of closed polygons.
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition: page_info.h:53
void SetDielectricLayerId(int aLayerId)
PCB_TEXT * parsePCB_TEXT()
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:209
void SetDrillSize(const wxSize &aSize)
Definition: pad.h:241
A collection of nets and the parameters used to route or test these nets.
Definition: netclass.h:46
void SetNetCode(int aNetCode)
Definition: netinfo.h:114
wxPoint GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.cpp:341
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
Definition: pcb_shape.h:145
VECTOR3D m_Scale
3D model scaling factor (dimensionless)
Definition: footprint.h:93
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
Definition: dimension.h:413
bool m_TextItalic[LAYER_CLASS_COUNT]
PCB_TARGET * parsePCB_TARGET()
PCB_LAYER_ID parseBoardItemLayer()
Parse the layer definition of a BOARD_ITEM object.
void SetCompany(const wxString &aCompany)
Definition: title_block.h:91
static LSET InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
Definition: lset.cpp:710
bool m_visible
Definition: board.h:106
static GAL_SET DefaultVisible()
Definition: lset.cpp:933
FOOTPRINT * parseFOOTPRINT(wxArrayString *aInitialComments=0)
FP_TEXT * parseFP_TEXT()
double m_Opacity
Definition: footprint.h:96
PAD * parsePAD(FOOTPRINT *aParent=NULL)
FOOTPRINT * parseFOOTPRINT_unchecked(wxArrayString *aInitialComments=0)
bool m_BlindBuriedViaAllowed
true to allow blind/buried vias
void SetLayerPair(PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer)
Function SetLayerPair For a via m_layer contains the top layer, the other layer is in m_bottomLayer.
Definition: track.cpp:414
void SetMaterial(const wxString &aName, int aDielectricSubLayer=0)
ZONE handles a list of polygons defining a copper zone.
Definition: zone.h:57
wxString m_userName
The user defined name of the layer.
Definition: board.h:104
void SetuViaDiameter(int aSize)
Definition: netclass.h:141
#define BOARD_FILE_HOST_VERSION
Earlier files than this include the host tag.
static LAYER_T ParseType(const char *aType)
Convert a string to a LAYER_T.
Definition: board.cpp:420
void SetCustomShapeInZoneOpt(CUST_PAD_SHAPE_IN_ZONE aOption)
Set the option for the custom pad shape to use as clearance area in copper zones.
Definition: pad.h:197
static int parseInt(LINE_READER &aReader, const char *aLine, const char **aOutput=NULL)
Parse an ASCII integer string with possible leading whitespace into an integer and updates the pointe...
BOARD_ITEM * Parse()
Definition: pcb_parser.cpp:473
void SetDrawCoord()
Set draw coordinates (absolute values ) from relative coordinates.
Definition: fp_shape.cpp:81
void SetTitle(const wxString &aTitle)
Definition: title_block.h:58
PCB_PLOT_PARAMS handles plot parameters and options when plotting/printing a board.
void parseNETCLASS()
Like smd, does not appear on the solder paste layer (default) note also has a special attribute in Ge...
Definition: pad_shapes.h:82
int NewOutline()
Creates a new hole in a given outline.
this class manage one layer needed to make a physical board it can be a solder mask,...
int m_LineThickness[LAYER_CLASS_COUNT]
void SetHeightMils(int aHeightInMils)
Definition: page_info.cpp:257
void SetEpsilonR(double aEpsilon, int aDielectricSubLayer=0)
void UpdateHeight(const wxPoint &aCrossbarStart, const wxPoint &aCrossbarEnd)
Updates stored height basing on points coordinates.
Definition: dimension.cpp:513
void AddDielectricPrms(int aDielectricPrmsIdx)
true if this stackup item must be taken in account, false to ignore it.
Thermal relief only for THT pads.
void parseDefaults(BOARD_DESIGN_SETTINGS &aSettings)
void SetThickness(int aThickness, int aDielectricSubLayer=0)
static const int ORPHANED
NETINFO_ITEM meaning that there was no net assigned for an item, as there was no board storing net li...
Definition: netinfo.h:369
int LAYER_NUM
This can be replaced with int and removed.
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
ZONE_SETTINGS handles zones parameters.
Definition: zone_settings.h:67
const KIID m_Uuid
Definition: eda_item.h:524
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:97
Definition: seg.h:41
void parseGROUP(BOARD_ITEM *aParent)
EDA_UNITS
Definition: eda_units.h:38
BOARD * parseBOARD_unchecked()
Definition: pcb_parser.cpp:533
void clear()
Definition: board.h:83
void parseLayer(LAYER *aLayer)
const wxPoint & GetPos0() const
Definition: pad.h:226
void SetThicknessLocked(bool aLocked, int aDielectricSubLayer=0)
Handle the data for a net.
Definition: netinfo.h:64
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:122
A filename or source description, a problem input line, a line number, a byte offset,...
Definition: ki_exception.h:118
void parseDefaultTextDims(BOARD_DESIGN_SETTINGS &aSettings, int aLayer)
a fiducial (usually a smd) local to the parent footprint
Definition: pad_shapes.h:99
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: footprint.h:94
void SetuViaDrill(int aSize)
Definition: netclass.h:145
std::vector< SEG > ZONE_SEGMENT_FILL
Definition: zone.h:44
const char * name
Definition: DXF_plotter.cpp:59
Definition: track.h:262
Container to hold information pertinent to a layer of a BOARD.
Definition: board.h:76
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
#define SEXPR_BOARD_FILE_VERSION
Current s-expression file format version. 2 was the last legacy format version.
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:208
#define _(s)
Definition: 3d_actions.cpp:33
const wxPoint & GetBezControl2() const
Definition: pcb_shape.h:136
void parseGeneralSection()
Definition: pcb_parser.cpp:890
void SetPosition(const wxPoint &aPos) override
Definition: pad.h:171
void init()
Clear and re-establish m_layerMap with the default layer names.
Definition: pcb_parser.cpp:61
void SetWidthMils(int aWidthInMils)
Definition: page_info.cpp:243
class ZONE, managed by a footprint
Definition: typeinfo.h:94
double GetAngle() const
Definition: pcb_shape.h:127
Plated through hole pad.
Definition: pad_shapes.h:80
VIATYPE GetViaType() const
Definition: track.h:373
BOARD_STACKUP_ITEM_TYPE
void pushValueIntoMap(int aIndex, int aValue)
Add aValue value in netcode mapping (m_netCodes) at aIndex.
Definition: pcb_parser.cpp:127
The common library.
Pads are not covered.
std::vector< VIA_DIMENSION > m_ViasDimensionsList
double parseDouble()
Parse the current token as an ASCII numeric string with possible leading whitespace into a double pre...
Definition: pcb_parser.cpp:139
int ShowModal() override
Definition: confirm.cpp:100
ring
Definition: board_item.h:53
void SetViaDiameter(int aDia)
Definition: netclass.h:133
const wxPoint & GetTextPos() const
Definition: eda_text.h:254
ZONE_BORDER_DISPLAY_STYLE
Zone border styles.
Definition: zone_settings.h:46
class ORTHOGONAL_DIMENSION, a linear dimension constrained to x/y
Definition: typeinfo.h:103
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.h:166
Pcbnew s-expression file format parser definition.
ZONE_SETTINGS & GetDefaultZoneSettings()
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
Definition: ki_exception.h:174
bool m_MicroViasAllowed
true to allow micro vias
void SetColor(const wxString &aColorName)
Abstract interface for BOARD_ITEMs capable of storing other items inside.
int m_number
The layer ID.
Definition: board.h:107
void Add(BOARD_STACKUP_ITEM *aItem)
Add a new item in stackup layer.
Definition: pad.h:60
For better understanding of the points that make a dimension:
Definition: dimension.h:333
void Parse(PCB_PLOT_PARAMS_PARSER *aParser)
BOARD_ITEM * parent
Definition: pcb_parser.h:378
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:168
DIMENSION_BASE * parseDIMENSION()
Abstract dimension API.
Definition: dimension.h:95
a test point pad
Definition: pad_shapes.h:100
Bezier Curve.
Definition: board_item.h:55
static constexpr int Millimeter2iu(double mm)
void resolveGroups(BOARD_ITEM *aParent)
Called after parsing a footprint definition or board to build the group membership lists.
Definition: pcb_parser.cpp:756
void SetBold(bool aBold)
Definition: eda_text.h:189
void SetPortrait(bool aIsPortrait)
Rotate the paper page 90 degrees.
Definition: page_info.cpp:186
static const int UNCONNECTED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition: netinfo.h:365
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
void SetLossTangent(double aTg, int aDielectricSubLayer=0)
const wxPoint & GetBezControl1() const
Definition: pcb_shape.h:133
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:133
wxPoint m_AuxOrigin
origin for plot exports
FP_3DMODEL * parse3DModel()
Definition: pcb_parser.cpp:367
bool m_TextUpright[LAYER_CLASS_COUNT]
Definition: track.h:83
void parseSetup()
T lookUpLayer(const M &aMap)
Parse the current token for the layer definition of a BOARD_ITEM object.
void SetName(wxString aName)
Definition: pcb_group.h:66
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:162
Container for design settings for a BOARD object.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Add a new vertex to the contour indexed by aOutline and aHole (defaults to the outline of the last po...
int GetNetCode() const
Definition: netinfo.h:113
ZONE * parseZONE(BOARD_ITEM_CONTAINER *aParent)