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