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