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