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