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