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