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