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