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 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1995 {
1996 if( CurTok() != T_LEFT )
1997 Expecting( T_LEFT );
1998
1999 token = NextTok();
2000
2001 if( token != T_layer )
2002 {
2003 switch( token )
2004 {
2005 case T_copper_finish:
2006 NeedSYMBOL();
2007 stackup.m_FinishType = FromUTF8();
2008 NeedRIGHT();
2009 break;
2010
2011 case T_edge_plating:
2012 token = NextTok();
2013 stackup.m_EdgePlating = token == T_yes;
2014 NeedRIGHT();
2015 break;
2016
2017 case T_dielectric_constraints:
2018 token = NextTok();
2019 stackup.m_HasDielectricConstrains = token == T_yes;
2020 NeedRIGHT();
2021 break;
2022
2023 case T_edge_connector:
2024 token = NextTok();
2026
2027 if( token == T_yes )
2029 else if( token == T_bevelled )
2031
2032 NeedRIGHT();
2033 break;
2034
2035 case T_castellated_pads: // Legacy compatibility. just skip it
2036 token = NextTok();
2037 NeedRIGHT();
2038 break;
2039
2040 default:
2041 // Currently, skip this item if not defined, because the stackup def
2042 // is a moving target
2043 //Expecting( "copper_finish, edge_plating, dielectric_constrains,
2044 // edge_connector, castellated_pads" );
2045 skipCurrent();
2046 break;
2047 }
2048
2049 continue;
2050 }
2051
2052 NeedSYMBOL();
2053 name = FromUTF8();
2054
2055 // init the layer id. For dielectric, layer id = UNDEFINED_LAYER
2056 PCB_LAYER_ID layerId = m_board->GetLayerID( name );
2057
2058 // Init the type
2060
2061 if( layerId == F_SilkS || layerId == B_SilkS )
2063 else if( layerId == F_Mask || layerId == B_Mask )
2065 else if( layerId == F_Paste || layerId == B_Paste )
2067 else if( layerId == UNDEFINED_LAYER )
2069 else if( !( layerId & 1 ) )
2070 type = BS_ITEM_TYPE_COPPER;
2071
2072 std::unique_ptr<BOARD_STACKUP_ITEM> itemOwner;
2073 BOARD_STACKUP_ITEM* item = nullptr;
2074
2075 if( type != BS_ITEM_TYPE_UNDEFINED )
2076 {
2077 // A 32-copper-layer board has at most 69 stackup items (32 copper +
2078 // 31 dielectric + 6 mask/paste/silk). Anything far beyond that
2079 // indicates a corrupted file. Parse the item so tokens are consumed
2080 // correctly, but don't keep it.
2081 static constexpr int MAX_STACKUP_ITEMS = 128;
2082
2083 itemOwner = std::make_unique<BOARD_STACKUP_ITEM>( type );
2084 item = itemOwner.get();
2085 item->SetBrdLayerId( layerId );
2086
2087 if( type == BS_ITEM_TYPE_DIELECTRIC )
2088 item->SetDielectricLayerId( dielectric_idx++ );
2089
2090 if( stackup.GetCount() < MAX_STACKUP_ITEMS )
2091 stackup.Add( itemOwner.release() );
2092 }
2093 else
2094 {
2095 Expecting( "layer_name" );
2096 }
2097
2098 bool has_next_sublayer = true;
2099 int sublayer_idx = 0; // the index of dielectric sub layers
2100 // sublayer 0 is always existing (main sublayer)
2101 wxString specFreqUnits;
2102 wxString dielectricModel;
2103
2104 while( has_next_sublayer )
2105 {
2106 has_next_sublayer = false;
2107
2108 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2109 {
2110 if( token == T_addsublayer )
2111 {
2112 has_next_sublayer = true;
2113 break;
2114 }
2115
2116 if( token == T_LEFT )
2117 {
2118 token = NextTok();
2119
2120 switch( token )
2121 {
2122 case T_type:
2123 NeedSYMBOL();
2124 item->SetTypeName( FromUTF8() );
2125 NeedRIGHT();
2126 break;
2127
2128 case T_thickness:
2129 item->SetThickness( parseBoardUnits( T_thickness ), sublayer_idx );
2130 token = NextTok();
2131
2132 if( token == T_LEFT )
2133 break;
2134
2135 if( token == T_locked )
2136 {
2137 // Dielectric thickness can be locked (for impedance controlled layers)
2138 if( type == BS_ITEM_TYPE_DIELECTRIC )
2139 item->SetThicknessLocked( true, sublayer_idx );
2140
2141 NeedRIGHT();
2142 }
2143
2144 break;
2145
2146 case T_material:
2147 NeedSYMBOL();
2148 item->SetMaterial( FromUTF8(), sublayer_idx );
2149 NeedRIGHT();
2150 break;
2151
2152 case T_epsilon_r:
2153 NextTok();
2154 item->SetEpsilonR( parseDouble(), sublayer_idx );
2155 NeedRIGHT();
2156 break;
2157
2158 case T_loss_tangent:
2159 NextTok();
2160 item->SetLossTangent( parseDouble(), sublayer_idx );
2161 NeedRIGHT();
2162 break;
2163
2164 case T_spec_frequency:
2165 NextTok();
2166 item->SetSpecFreq( parseDouble(), sublayer_idx );
2167 NeedRIGHT();
2168 break;
2169
2170 case T_dielectric_model:
2171 token = NextTok();
2172
2173 switch( token )
2174 {
2175 case T_constant:
2176 item->SetDielectricModel( DIELECTRIC_MODEL::CONSTANT, sublayer_idx );
2177 break;
2178
2179 case T_djordjevic_sarkar:
2181 break;
2182
2183 default:
2184 Expecting( "constant or djordjevic_sarkar" );
2185 break;
2186 }
2187
2188 NeedRIGHT();
2189 break;
2190
2191 case T_color:
2192 NeedSYMBOL();
2193 name = FromUTF8();
2194
2195 // Older versions didn't store opacity with custom colors
2196 if( name.StartsWith( wxT( "#" ) ) && m_requiredVersion < 20210824 )
2197 {
2198 KIGFX::COLOR4D color( name );
2199
2200 if( item->GetType() == BS_ITEM_TYPE_SOLDERMASK )
2201 color = color.WithAlpha( DEFAULT_SOLDERMASK_OPACITY );
2202 else
2203 color = color.WithAlpha( 1.0 );
2204
2205 wxColour wx_color = color.ToColour();
2206
2207 // Open-code wxColour::GetAsString() because 3.0 doesn't handle rgba
2208 name.Printf( wxT("#%02X%02X%02X%02X" ),
2209 wx_color.Red(),
2210 wx_color.Green(),
2211 wx_color.Blue(),
2212 wx_color.Alpha() );
2213 }
2214
2215 item->SetColor( name, sublayer_idx );
2216 NeedRIGHT();
2217 break;
2218
2219 default:
2220 // Currently, skip this item if not defined, because the stackup def
2221 // is a moving target
2222 //Expecting( "type, thickness, material, epsilon_r, loss_tangent, color" );
2223 skipCurrent();
2224 }
2225 }
2226 }
2227
2228 if( has_next_sublayer ) // Prepare reading the next sublayer description
2229 {
2230 sublayer_idx++;
2231 item->AddDielectricPrms( sublayer_idx );
2232 }
2233 }
2234
2235 }
2236
2237 if( token != T_RIGHT )
2238 {
2239 Expecting( ")" );
2240 }
2241
2242 // Success:
2243 m_board->GetDesignSettings().m_HasStackup = true;
2244}
2245
2246
2247void PCB_IO_KICAD_SEXPR_PARSER::createOldLayerMapping( std::unordered_map< std::string, std::string >& aMap )
2248{
2249 // N.B. This mapping only includes Italian, Polish and French as they were the only languages
2250 // that mapped the layer names as of cc2022b1ac739aa673d2a0b7a2047638aa7a47b3 (kicad-i18n)
2251 // when the bug was fixed in KiCad source.
2252
2253 // Italian
2254 aMap["Adesivo.Retro"] = "B.Adhes";
2255 aMap["Adesivo.Fronte"] = "F.Adhes";
2256 aMap["Pasta.Retro"] = "B.Paste";
2257 aMap["Pasta.Fronte"] = "F.Paste";
2258 aMap["Serigrafia.Retro"] = "B.SilkS";
2259 aMap["Serigrafia.Fronte"] = "F.SilkS";
2260 aMap["Maschera.Retro"] = "B.Mask";
2261 aMap["Maschera.Fronte"] = "F.Mask";
2262 aMap["Grafica"] = "Dwgs.User";
2263 aMap["Commenti"] = "Cmts.User";
2264 aMap["Eco1"] = "Eco1.User";
2265 aMap["Eco2"] = "Eco2.User";
2266 aMap["Contorno.scheda"] = "Edge.Cuts";
2267
2268 // Polish
2269 aMap["Kleju_Dolna"] = "B.Adhes";
2270 aMap["Kleju_Gorna"] = "F.Adhes";
2271 aMap["Pasty_Dolna"] = "B.Paste";
2272 aMap["Pasty_Gorna"] = "F.Paste";
2273 aMap["Opisowa_Dolna"] = "B.SilkS";
2274 aMap["Opisowa_Gorna"] = "F.SilkS";
2275 aMap["Maski_Dolna"] = "B.Mask";
2276 aMap["Maski_Gorna"] = "F.Mask";
2277 aMap["Rysunkowa"] = "Dwgs.User";
2278 aMap["Komentarzy"] = "Cmts.User";
2279 aMap["ECO1"] = "Eco1.User";
2280 aMap["ECO2"] = "Eco2.User";
2281 aMap["Krawedziowa"] = "Edge.Cuts";
2282
2283 // French
2284 aMap["Dessous.Adhes"] = "B.Adhes";
2285 aMap["Dessus.Adhes"] = "F.Adhes";
2286 aMap["Dessous.Pate"] = "B.Paste";
2287 aMap["Dessus.Pate"] = "F.Paste";
2288 aMap["Dessous.SilkS"] = "B.SilkS";
2289 aMap["Dessus.SilkS"] = "F.SilkS";
2290 aMap["Dessous.Masque"] = "B.Mask";
2291 aMap["Dessus.Masque"] = "F.Mask";
2292 aMap["Dessin.User"] = "Dwgs.User";
2293 aMap["Contours.Ci"] = "Edge.Cuts";
2294}
2295
2296
2298{
2299 wxCHECK_RET( CurTok() == T_layers,
2300 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as layers." ) );
2301
2302 T token;
2303 LSET visibleLayers;
2304 LSET enabledLayers;
2305 int copperLayerCount = 0;
2306 LAYER layer;
2307 bool anyHidden = false;
2308
2309 std::unordered_map< std::string, std::string > v3_layer_names;
2310 std::vector<LAYER> cu;
2311
2312 createOldLayerMapping( v3_layer_names );
2313
2314 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2315 {
2316 parseLayer( &layer );
2317
2318 if( layer.m_type == LT_UNDEFINED ) // it's a non-copper layer
2319 break;
2320
2321 cu.push_back( layer ); // it's copper
2322 }
2323
2324 // All Cu layers are parsed, but not the non-cu layers here.
2325
2326 // The original *.kicad_pcb file format and the inverted
2327 // Cu stack format both have all the Cu layers first, so use this
2328 // trick to handle either. The layer number in the (layers ..)
2329 // s-expression element are ignored.
2330 if( cu.size() )
2331 {
2332 // Rework the layer numbers, which changed when the Cu stack
2333 // was flipped. So we instead use position in the list.
2334 for( size_t i = 1; i < cu.size() - 1; i++ )
2335 {
2336 int tmpLayer = LSET::NameToLayer( cu[i].m_name );
2337
2338 if( tmpLayer < 0 )
2339 tmpLayer = ( i + 1 ) * 2;
2340
2341 cu[i].m_number = tmpLayer;
2342 }
2343
2344 cu[0].m_number = F_Cu;
2345 cu[cu.size()-1].m_number = B_Cu;
2346
2347 for( auto& cu_layer : cu )
2348 {
2349 enabledLayers.set( cu_layer.m_number );
2350
2351 if( cu_layer.m_visible )
2352 visibleLayers.set( cu_layer.m_number );
2353 else
2354 anyHidden = true;
2355
2356 m_board->SetLayerDescr( PCB_LAYER_ID( cu_layer.m_number ), cu_layer );
2357
2358 UTF8 name = cu_layer.m_name;
2359
2360 m_layerIndices[ name ] = PCB_LAYER_ID( cu_layer.m_number );
2361 m_layerMasks[ name ] = LSET( { PCB_LAYER_ID( cu_layer.m_number ) } );
2362 }
2363
2364 copperLayerCount = cu.size();
2365 }
2366
2367 // process non-copper layers
2368 while( token != T_RIGHT )
2369 {
2370 LAYER_ID_MAP::const_iterator it = m_layerIndices.find( UTF8( layer.m_name ) );
2371
2372 if( it == m_layerIndices.end() )
2373 {
2374 auto new_layer_it = v3_layer_names.find( layer.m_name.ToStdString() );
2375
2376 if( new_layer_it != v3_layer_names.end() )
2377 it = m_layerIndices.find( new_layer_it->second );
2378
2379 if( it == m_layerIndices.end() )
2380 {
2381 wxString error;
2382 error.Printf( _( "Layer '%s' in file '%s' at line %d is not in fixed layer hash." ),
2383 layer.m_name,
2384 CurSource(),
2385 CurLineNumber(),
2386 CurOffset() );
2387
2388 THROW_IO_ERROR( error );
2389 }
2390
2391 // If we are here, then we have found a translated layer name. Put it in the maps
2392 // so that items on this layer get the appropriate layer ID number.
2393 m_layerIndices[ UTF8( layer.m_name ) ] = it->second;
2394 m_layerMasks[ UTF8( layer.m_name ) ] = LSET( { it->second } );
2395 layer.m_name = it->first;
2396 }
2397
2398 layer.m_number = it->second;
2399 enabledLayers.set( layer.m_number );
2400
2401 if( layer.m_visible )
2402 visibleLayers.set( layer.m_number );
2403 else
2404 anyHidden = true;
2405
2406 m_board->SetLayerDescr( it->second, layer );
2407
2408 token = NextTok();
2409
2410 if( token != T_LEFT )
2411 break;
2412
2413 parseLayer( &layer );
2414 }
2415
2416 // We need at least 2 copper layers and there must be an even number of them.
2417 if( copperLayerCount < 2 || (copperLayerCount % 2) != 0 )
2418 {
2419 wxString err = wxString::Format( _( "%d is not a valid layer count" ), copperLayerCount );
2420
2421 THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
2422 }
2423
2424 m_board->SetCopperLayerCount( copperLayerCount );
2425 m_board->SetEnabledLayers( enabledLayers );
2426
2427 // Only set this if any layers were explicitly marked as hidden. Otherwise, we want to leave
2428 // this alone; default visibility will show everything
2429 if( anyHidden )
2430 m_board->m_LegacyVisibleLayers = visibleLayers;
2431}
2432
2433
2435{
2436 LSET_MAP::const_iterator it = aMap.find( curText );
2437
2438 if( it == aMap.end() )
2439 return LSET( { Rescue } );
2440
2441 return it->second;
2442}
2443
2444
2446{
2447 // avoid constructing another std::string, use lexer's directly
2448 LAYER_ID_MAP::const_iterator it = aMap.find( curText );
2449
2450 if( it == aMap.end() )
2451 {
2452 m_undefinedLayers.insert( curText );
2453 return Rescue;
2454 }
2455
2456 // Some files may have saved items to the Rescue Layer due to an issue in v5
2457 if( it->second == Rescue )
2458 m_undefinedLayers.insert( curText );
2459
2460 return it->second;
2461}
2462
2463
2465{
2466 wxCHECK_MSG( CurTok() == T_layer, UNDEFINED_LAYER,
2467 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as layer." ) );
2468
2469 NextTok();
2470
2471 PCB_LAYER_ID layerIndex = lookUpLayer( m_layerIndices );
2472
2473 // Handle closing ) in object parser.
2474
2475 return layerIndex;
2476}
2477
2478
2480{
2481 wxCHECK_MSG( CurTok() == T_layers, LSET(),
2482 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as item layers." ) );
2483
2484 LSET layerMask;
2485
2486 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
2487 {
2488 layerMask |= lookUpLayerSet( m_layerMasks );
2489 }
2490
2491 return layerMask;
2492}
2493
2494
2496{
2497 LSET layerMask = parseBoardItemLayersAsMask();
2498
2499 if( ( layerMask & LSET::AllCuMask() ).count() != 1 )
2500 Expecting( "single copper layer" );
2501
2502 if( ( layerMask & LSET( { F_Mask, B_Mask } ) ).count() > 1 )
2503 Expecting( "max one soldermask layer" );
2504
2505 if( ( ( layerMask & LSET::InternalCuMask() ).any()
2506 && ( layerMask & LSET( { F_Mask, B_Mask } ) ).any() ) )
2507 {
2508 Expecting( "no mask layer when track is on internal layer" );
2509 }
2510
2511 if( ( layerMask & LSET( { F_Cu, B_Mask } ) ).count() > 1 )
2512 Expecting( "copper and mask on the same side" );
2513
2514 if( ( layerMask & LSET( { B_Cu, F_Mask } ) ).count() > 1 )
2515 Expecting( "copper and mask on the same side" );
2516
2517 return layerMask;
2518}
2519
2520
2522{
2523 wxCHECK_RET( CurTok() == T_setup,
2524 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as setup." ) );
2525
2526 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
2527 const std::shared_ptr<NETCLASS>& defaultNetClass = bds.m_NetSettings->GetDefaultNetclass();
2528 ZONE_SETTINGS& zoneSettings = bds.GetDefaultZoneSettings();
2529
2530 // Missing soldermask min width value means that the user has set the value to 0 and
2531 // not the default value (0.25mm)
2532 bds.m_SolderMaskMinWidth = 0;
2533
2534 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
2535 {
2536 if( token != T_LEFT )
2537 Expecting( T_LEFT );
2538
2539 token = NextTok();
2540
2541 switch( token )
2542 {
2543 case T_stackup:
2545 skipCurrent();
2546 else
2548 break;
2549
2550 case T_last_trace_width: // not used now
2551 /* lastTraceWidth =*/ parseBoardUnits( T_last_trace_width );
2552 NeedRIGHT();
2553 break;
2554
2555 case T_user_trace_width:
2556 {
2557 // Make room for the netclass value
2558 if( bds.m_TrackWidthList.empty() )
2559 bds.m_TrackWidthList.emplace_back( 0 );
2560
2561 int trackWidth = parseBoardUnits( T_user_trace_width );
2562
2563 if( !m_appendToExisting || !alg::contains( bds.m_TrackWidthList, trackWidth ) )
2564 bds.m_TrackWidthList.push_back( trackWidth );
2565
2566 m_board->m_LegacyDesignSettingsLoaded = true;
2567 NeedRIGHT();
2568 break;
2569 }
2570
2571 case T_trace_clearance:
2572 defaultNetClass->SetClearance( parseBoardUnits( T_trace_clearance ) );
2573 m_board->m_LegacyDesignSettingsLoaded = true;
2574 NeedRIGHT();
2575 break;
2576
2577 case T_zone_clearance:
2578 zoneSettings.m_ZoneClearance = parseBoardUnits( T_zone_clearance );
2579 m_board->m_LegacyDesignSettingsLoaded = true;
2580 NeedRIGHT();
2581 break;
2582
2583 case T_zone_45_only: // legacy setting
2584 /* zoneSettings.m_Zone_45_Only = */ parseBool();
2585 m_board->m_LegacyDesignSettingsLoaded = true;
2586 NeedRIGHT();
2587 break;
2588
2589 case T_clearance_min:
2590 bds.m_MinClearance = parseBoardUnits( T_clearance_min );
2591 m_board->m_LegacyDesignSettingsLoaded = true;
2592 NeedRIGHT();
2593 break;
2594
2595 case T_trace_min:
2596 bds.m_TrackMinWidth = parseBoardUnits( T_trace_min );
2597 m_board->m_LegacyDesignSettingsLoaded = true;
2598 NeedRIGHT();
2599 break;
2600
2601 case T_via_size:
2602 defaultNetClass->SetViaDiameter( parseBoardUnits( T_via_size ) );
2603 m_board->m_LegacyDesignSettingsLoaded = true;
2604 NeedRIGHT();
2605 break;
2606
2607 case T_via_drill:
2608 defaultNetClass->SetViaDrill( parseBoardUnits( T_via_drill ) );
2609 m_board->m_LegacyDesignSettingsLoaded = true;
2610 NeedRIGHT();
2611 break;
2612
2613 case T_via_min_annulus:
2614 bds.m_ViasMinAnnularWidth = parseBoardUnits( T_via_min_annulus );
2615 m_board->m_LegacyDesignSettingsLoaded = true;
2616 NeedRIGHT();
2617 break;
2618
2619 case T_via_min_size:
2620 bds.m_ViasMinSize = parseBoardUnits( T_via_min_size );
2621 m_board->m_LegacyDesignSettingsLoaded = true;
2622 NeedRIGHT();
2623 break;
2624
2625 case T_through_hole_min:
2626 bds.m_MinThroughDrill = parseBoardUnits( T_through_hole_min );
2627 m_board->m_LegacyDesignSettingsLoaded = true;
2628 NeedRIGHT();
2629 break;
2630
2631 // Legacy token for T_through_hole_min
2632 case T_via_min_drill:
2633 bds.m_MinThroughDrill = parseBoardUnits( T_via_min_drill );
2634 m_board->m_LegacyDesignSettingsLoaded = true;
2635 NeedRIGHT();
2636 break;
2637
2638 case T_hole_to_hole_min:
2639 bds.m_HoleToHoleMin = parseBoardUnits( T_hole_to_hole_min );
2640 m_board->m_LegacyDesignSettingsLoaded = true;
2641 NeedRIGHT();
2642 break;
2643
2644 case T_user_via:
2645 {
2646 int viaSize = parseBoardUnits( "user via size" );
2647 int viaDrill = parseBoardUnits( "user via drill" );
2648 VIA_DIMENSION via( viaSize, viaDrill );
2649
2650 // Make room for the netclass value
2651 if( bds.m_ViasDimensionsList.empty() )
2652 bds.m_ViasDimensionsList.emplace_back( VIA_DIMENSION( 0, 0 ) );
2653
2655 bds.m_ViasDimensionsList.emplace_back( via );
2656
2657 m_board->m_LegacyDesignSettingsLoaded = true;
2658 NeedRIGHT();
2659 break;
2660 }
2661
2662 case T_uvia_size:
2663 defaultNetClass->SetuViaDiameter( parseBoardUnits( T_uvia_size ) );
2664 m_board->m_LegacyDesignSettingsLoaded = true;
2665 NeedRIGHT();
2666 break;
2667
2668 case T_uvia_drill:
2669 defaultNetClass->SetuViaDrill( parseBoardUnits( T_uvia_drill ) );
2670 m_board->m_LegacyDesignSettingsLoaded = true;
2671 NeedRIGHT();
2672 break;
2673
2674 case T_uvias_allowed:
2675 parseBool();
2676 m_board->m_LegacyDesignSettingsLoaded = true;
2677 NeedRIGHT();
2678 break;
2679
2680 case T_blind_buried_vias_allowed:
2681 parseBool();
2682 m_board->m_LegacyDesignSettingsLoaded = true;
2683 NeedRIGHT();
2684 break;
2685
2686 case T_uvia_min_size:
2687 bds.m_MicroViasMinSize = parseBoardUnits( T_uvia_min_size );
2688 m_board->m_LegacyDesignSettingsLoaded = true;
2689 NeedRIGHT();
2690 break;
2691
2692 case T_uvia_min_drill:
2693 bds.m_MicroViasMinDrill = parseBoardUnits( T_uvia_min_drill );
2694 m_board->m_LegacyDesignSettingsLoaded = true;
2695 NeedRIGHT();
2696 break;
2697
2698 case T_user_diff_pair:
2699 {
2700 int width = parseBoardUnits( "user diff-pair width" );
2701 int gap = parseBoardUnits( "user diff-pair gap" );
2702 int viaGap = parseBoardUnits( "user diff-pair via gap" );
2703 DIFF_PAIR_DIMENSION diffPair( width, gap, viaGap );
2704
2706 bds.m_DiffPairDimensionsList.emplace_back( diffPair );
2707
2708 m_board->m_LegacyDesignSettingsLoaded = true;
2709 NeedRIGHT();
2710 break;
2711 }
2712
2713 case T_segment_width: // note: legacy (pre-6.0) token
2714 bds.m_LineThickness[ LAYER_CLASS_COPPER ] = parseBoardUnits( T_segment_width );
2715 m_board->m_LegacyDesignSettingsLoaded = true;
2716 NeedRIGHT();
2717 break;
2718
2719 case T_edge_width: // note: legacy (pre-6.0) token
2720 bds.m_LineThickness[ LAYER_CLASS_EDGES ] = parseBoardUnits( T_edge_width );
2721 m_board->m_LegacyDesignSettingsLoaded = true;
2722 NeedRIGHT();
2723 break;
2724
2725 case T_mod_edge_width: // note: legacy (pre-6.0) token
2726 bds.m_LineThickness[ LAYER_CLASS_SILK ] = parseBoardUnits( T_mod_edge_width );
2727 m_board->m_LegacyDesignSettingsLoaded = true;
2728 NeedRIGHT();
2729 break;
2730
2731 case T_pcb_text_width: // note: legacy (pre-6.0) token
2732 bds.m_TextThickness[ LAYER_CLASS_COPPER ] = parseBoardUnits( T_pcb_text_width );
2733 m_board->m_LegacyDesignSettingsLoaded = true;
2734 NeedRIGHT();
2735 break;
2736
2737 case T_mod_text_width: // note: legacy (pre-6.0) token
2738 bds.m_TextThickness[ LAYER_CLASS_SILK ] = parseBoardUnits( T_mod_text_width );
2739 m_board->m_LegacyDesignSettingsLoaded = true;
2740 NeedRIGHT();
2741 break;
2742
2743 case T_pcb_text_size: // note: legacy (pre-6.0) token
2744 bds.m_TextSize[ LAYER_CLASS_COPPER ].x = parseBoardUnits( "pcb text width" );
2745 bds.m_TextSize[ LAYER_CLASS_COPPER ].y = parseBoardUnits( "pcb text height" );
2746 m_board->m_LegacyDesignSettingsLoaded = true;
2747 NeedRIGHT();
2748 break;
2749
2750 case T_mod_text_size: // note: legacy (pre-6.0) token
2751 bds.m_TextSize[ LAYER_CLASS_SILK ].x = parseBoardUnits( "footprint text width" );
2752 bds.m_TextSize[ LAYER_CLASS_SILK ].y = parseBoardUnits( "footprint text height" );
2753 m_board->m_LegacyDesignSettingsLoaded = true;
2754 NeedRIGHT();
2755 break;
2756
2757 case T_defaults:
2758 parseDefaults( bds );
2759 m_board->m_LegacyDesignSettingsLoaded = true;
2760 break;
2761
2762 case T_pad_size:
2763 {
2764 VECTOR2I sz;
2765 sz.x = parseBoardUnits( "master pad width" );
2766 sz.y = parseBoardUnits( "master pad height" );
2768 m_board->m_LegacyDesignSettingsLoaded = true;
2769 NeedRIGHT();
2770 break;
2771 }
2772
2773 case T_pad_drill:
2774 {
2775 int drillSize = parseBoardUnits( T_pad_drill );
2776 bds.m_Pad_Master->SetDrillSize( VECTOR2I( drillSize, drillSize ) );
2777 m_board->m_LegacyDesignSettingsLoaded = true;
2778 NeedRIGHT();
2779 break;
2780 }
2781
2782 case T_pad_to_mask_clearance:
2783 bds.m_SolderMaskExpansion = parseBoardUnits( T_pad_to_mask_clearance );
2784 NeedRIGHT();
2785 break;
2786
2787 case T_solder_mask_min_width:
2788 bds.m_SolderMaskMinWidth = parseBoardUnits( T_solder_mask_min_width );
2789 NeedRIGHT();
2790 break;
2791
2792 case T_pad_to_paste_clearance:
2793 bds.m_SolderPasteMargin = parseBoardUnits( T_pad_to_paste_clearance );
2794 NeedRIGHT();
2795 break;
2796
2797 case T_pad_to_paste_clearance_ratio:
2798 bds.m_SolderPasteMarginRatio = parseDouble( T_pad_to_paste_clearance_ratio );
2799 NeedRIGHT();
2800 break;
2801
2802 case T_allow_soldermask_bridges_in_footprints:
2804 NeedRIGHT();
2805 break;
2806
2807 case T_tenting:
2808 {
2809 auto [front, back] = parseFrontBackOptBool( true );
2810 bds.m_TentViasFront = front.value_or( false );
2811 bds.m_TentViasBack = back.value_or( false );
2812 break;
2813 }
2814
2815 case T_covering:
2816 {
2817 auto [front, back] = parseFrontBackOptBool();
2818 bds.m_CoverViasFront = front.value_or( false );
2819 bds.m_CoverViasBack = back.value_or( false );
2820 break;
2821 }
2822
2823 case T_plugging:
2824 {
2825 auto [front, back] = parseFrontBackOptBool();
2826 bds.m_PlugViasFront = front.value_or( false );
2827 bds.m_PlugViasBack = back.value_or( false );
2828 break;
2829 }
2830
2831 case T_capping:
2832 {
2833 bds.m_CapVias = parseBool();
2834 NeedRIGHT();
2835 break;
2836 }
2837
2838 case T_filling:
2839 {
2840 bds.m_FillVias = parseBool();
2841 NeedRIGHT();
2842 break;
2843 }
2844
2845 case T_aux_axis_origin:
2846 {
2847 int x = parseBoardUnits( "auxiliary origin X" );
2848 int y = parseBoardUnits( "auxiliary origin Y" );
2849 bds.SetAuxOrigin( VECTOR2I( x, y ) );
2850
2851 // Aux origin still stored in board for the moment
2852 //m_board->m_LegacyDesignSettingsLoaded = true;
2853 NeedRIGHT();
2854 break;
2855 }
2856
2857 case T_grid_origin:
2858 {
2859 int x = parseBoardUnits( "grid origin X" );
2860 int y = parseBoardUnits( "grid origin Y" );
2861 bds.SetGridOrigin( VECTOR2I( x, y ) );
2862 // Grid origin still stored in board for the moment
2863 //m_board->m_LegacyDesignSettingsLoaded = true;
2864 NeedRIGHT();
2865 break;
2866 }
2867
2868 // Stored in board prior to 6.0
2869 case T_visible_elements:
2870 {
2871 // Make sure to start with DefaultVisible so all new layers are set
2872 m_board->m_LegacyVisibleItems = GAL_SET::DefaultVisible();
2873
2874 int visible = parseHex() | MIN_VISIBILITY_MASK;
2875
2876 for( size_t i = 0; i < sizeof( int ) * CHAR_BIT; i++ )
2877 m_board->m_LegacyVisibleItems.set( i, visible & ( 1u << i ) );
2878
2879 NeedRIGHT();
2880 break;
2881 }
2882
2883 case T_max_error:
2884 bds.m_MaxError = parseBoardUnits( T_max_error );
2885 m_board->m_LegacyDesignSettingsLoaded = true;
2886 NeedRIGHT();
2887 break;
2888
2889 case T_filled_areas_thickness:
2890 // Ignore this value, it is not used anymore
2891 parseBool();
2892 NeedRIGHT();
2893 break;
2894
2895 case T_pcbplotparams:
2896 {
2897 PCB_PLOT_PARAMS plotParams;
2898 PCB_PLOT_PARAMS_PARSER parser( reader, m_requiredVersion );
2899 // parser must share the same current line as our current PCB parser
2900 // synchronize it.
2901 parser.SyncLineReaderWith( *this );
2902
2903 plotParams.Parse( &parser );
2904 SyncLineReaderWith( parser );
2905
2906 m_board->SetPlotOptions( plotParams );
2907
2908 if( plotParams.GetLegacyPlotViaOnMaskLayer().has_value() )
2909 {
2910 bool tent = !( *plotParams.GetLegacyPlotViaOnMaskLayer() );
2911 m_board->GetDesignSettings().m_TentViasFront = tent;
2912 m_board->GetDesignSettings().m_TentViasBack = tent;
2913 }
2914
2915 break;
2916 }
2917 case T_zone_defaults:
2919 break;
2920
2921 default:
2922 Unexpected( CurText() );
2923 }
2924 }
2925
2926 // Set up a default stackup in case the file doesn't define one, and now we know
2927 // the enabled layers
2928 if( !m_preserveDestinationStackup && !m_board->GetDesignSettings().m_HasStackup )
2929 {
2930 BOARD_STACKUP& stackup = bds.GetStackupDescriptor();
2931 stackup.RemoveAll();
2932 stackup.BuildDefaultStackupList( &bds, m_board->GetCopperLayerCount() );
2933 }
2934}
2935
2936
2938{
2939 T token;
2940
2941 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2942 {
2943 if( token != T_LEFT )
2944 {
2945 Expecting( T_LEFT );
2946 }
2947
2948 token = NextTok();
2949
2950 switch( token )
2951 {
2952 case T_property:
2954 break;
2955 default:
2956 Unexpected( CurText() );
2957 }
2958 }
2959}
2960
2961
2963 std::map<PCB_LAYER_ID, ZONE_LAYER_PROPERTIES>& aProperties )
2964{
2965 T token;
2966
2968 ZONE_LAYER_PROPERTIES properties;
2969
2970 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2971 {
2972 if( token != T_LEFT )
2973 {
2974 Expecting( T_LEFT );
2975 }
2976
2977 token = NextTok();
2978
2979 switch( token )
2980 {
2981 case T_layer:
2982 layer = parseBoardItemLayer();
2983 NeedRIGHT();
2984 break;
2985 case T_hatch_position:
2986 {
2987 properties.hatching_offset = parseXY();
2988 NeedRIGHT();
2989 break;
2990 }
2991 default:
2992 Unexpected( CurText() );
2993 break;
2994 }
2995 }
2996
2997 aProperties.emplace( layer, properties );
2998}
2999
3000
3002{
3003 T token;
3004
3005 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3006 {
3007 if( token != T_LEFT )
3008 Expecting( T_LEFT );
3009
3010 token = NextTok();
3011
3012 switch( token )
3013 {
3014 case T_edge_clearance:
3015 designSettings.m_CopperEdgeClearance = parseBoardUnits( T_edge_clearance );
3016 m_board->m_LegacyCopperEdgeClearanceLoaded = true;
3017 NeedRIGHT();
3018 break;
3019
3020 case T_copper_line_width:
3021 designSettings.m_LineThickness[ LAYER_CLASS_COPPER ] = parseBoardUnits( token );
3022 NeedRIGHT();
3023 break;
3024
3025 case T_copper_text_dims:
3026 parseDefaultTextDims( designSettings, LAYER_CLASS_COPPER );
3027 break;
3028
3029 case T_courtyard_line_width:
3030 designSettings.m_LineThickness[ LAYER_CLASS_COURTYARD ] = parseBoardUnits( token );
3031 NeedRIGHT();
3032 break;
3033
3034 case T_edge_cuts_line_width:
3035 designSettings.m_LineThickness[ LAYER_CLASS_EDGES ] = parseBoardUnits( token );
3036 NeedRIGHT();
3037 break;
3038
3039 case T_silk_line_width:
3040 designSettings.m_LineThickness[ LAYER_CLASS_SILK ] = parseBoardUnits( token );
3041 NeedRIGHT();
3042 break;
3043
3044 case T_silk_text_dims:
3045 parseDefaultTextDims( designSettings, LAYER_CLASS_SILK );
3046 break;
3047
3048 case T_fab_layers_line_width:
3049 designSettings.m_LineThickness[ LAYER_CLASS_FAB ] = parseBoardUnits( token );
3050 NeedRIGHT();
3051 break;
3052
3053 case T_fab_layers_text_dims:
3054 parseDefaultTextDims( designSettings, LAYER_CLASS_FAB );
3055 break;
3056
3057 case T_other_layers_line_width:
3058 designSettings.m_LineThickness[ LAYER_CLASS_OTHERS ] = parseBoardUnits( token );
3059 NeedRIGHT();
3060 break;
3061
3062 case T_other_layers_text_dims:
3063 parseDefaultTextDims( designSettings, LAYER_CLASS_OTHERS );
3064 break;
3065
3066 case T_dimension_units:
3067 designSettings.m_DimensionUnitsMode =
3068 static_cast<DIM_UNITS_MODE>( parseInt( "dimension units" ) );
3069 NeedRIGHT();
3070 break;
3071
3072 case T_dimension_precision:
3073 designSettings.m_DimensionPrecision =
3074 static_cast<DIM_PRECISION>( parseInt( "dimension precision" ) );
3075 NeedRIGHT();
3076 break;
3077
3078 default:
3079 Unexpected( CurText() );
3080 }
3081 }
3082}
3083
3084
3086{
3087 T token;
3088
3089 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3090 {
3091 if( token == T_LEFT )
3092 token = NextTok();
3093
3094 switch( token )
3095 {
3096 case T_size:
3097 aSettings.m_TextSize[ aLayer ].x = parseBoardUnits( "default text size X" );
3098 aSettings.m_TextSize[ aLayer ].y = parseBoardUnits( "default text size Y" );
3099 NeedRIGHT();
3100 break;
3101
3102 case T_thickness:
3103 aSettings.m_TextThickness[ aLayer ] = parseBoardUnits( "default text width" );
3104 NeedRIGHT();
3105 break;
3106
3107 case T_italic:
3108 aSettings.m_TextItalic[ aLayer ] = true;
3109 break;
3110
3111 case T_keep_upright:
3112 aSettings.m_TextUpright[ aLayer ] = true;
3113 break;
3114
3115 default:
3116 Expecting( "size, thickness, italic or keep_upright" );
3117 }
3118 }
3119}
3120
3121
3123{
3124 wxCHECK_RET( CurTok() == T_net,
3125 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as net." ) );
3126
3127 int netCode = parseInt( "net number" );
3128
3129 NeedSYMBOLorNUMBER();
3130 wxString name = FromUTF8();
3131
3132 // Convert overbar syntax from `~...~` to `~{...}`. These were left out of the first merge
3133 // so the version is a bit later.
3134 if( m_requiredVersion < 20210606 )
3136
3137 NeedRIGHT();
3138
3139 // net 0 should be already in list, so store this net
3140 // if it is not the net 0, or if the net 0 does not exists.
3141 // (TODO: a better test.)
3142 if( netCode > NETINFO_LIST::UNCONNECTED || !m_board->FindNet( NETINFO_LIST::UNCONNECTED ) )
3143 {
3144 NETINFO_ITEM* net = new NETINFO_ITEM( m_board, name, netCode );
3145 m_board->Add( net, ADD_MODE::INSERT, true );
3146
3147 // Store the new code mapping
3148 pushValueIntoMap( netCode, net->GetNetCode() );
3149 }
3150}
3151
3152// Parse the (net_chains ...) aggregation block providing chain names, member nets and optional terminal pads.
3154{
3155 // Tokens inside section: (net_chain (name <str>) (members (member (net <code>))...) (terminal_pad <uuid>) (terminal_pad <uuid>))
3156 for( T token = NextTok(); token != T_EOF; token = NextTok() )
3157 {
3158 if( token == T_RIGHT )
3159 break; // end of (net_chains ...)
3160 else if( token == T_LEFT )
3161 token = NextTok();
3162
3163 if( token != T_net_chain )
3164 {
3165 skipCurrent();
3166 continue;
3167 }
3168
3169 wxString name;
3170 std::vector<int> netCodes;
3171 std::vector<wxString> netNames;
3172 KIID padUuids[2];
3173 int padCount = 0;
3174
3175 for( T t = NextTok(); t != T_EOF; t = NextTok() )
3176 {
3177 if( t == T_RIGHT )
3178 break; // end chain
3179 else if( t == T_LEFT )
3180 t = NextTok();
3181
3182 switch( t )
3183 {
3184 case T_name:
3185 NeedSYMBOLorNUMBER();
3186 name = FromUTF8();
3187 NeedRIGHT();
3188 break;
3189
3190 case T_members:
3191 {
3192 for( T mt = NextTok(); mt != T_RIGHT; mt = NextTok() )
3193 {
3194 if( mt == T_LEFT )
3195 mt = NextTok();
3196 if( mt == T_net )
3197 {
3198 // Accept either a net code (legacy) or a net name (current).
3199 T tok = NextTok();
3200 if( tok == T_NUMBER )
3201 {
3202 netCodes.push_back( (int) strtol( CurText(), nullptr, 10 ) );
3203 }
3204 else
3205 {
3206 netNames.push_back( FromUTF8() );
3207 }
3208 NeedRIGHT();
3209 }
3210 else if( mt == T_member )
3211 {
3212 // legacy style (member (net <code>)) wrapper
3213 T inner = NextTok();
3214 if( inner == T_LEFT )
3215 inner = NextTok();
3216 if( inner == T_net )
3217 {
3218 T tok2 = NextTok();
3219 if( tok2 == T_NUMBER )
3220 netCodes.push_back( (int) strtol( CurText(), nullptr, 10 ) );
3221 else
3222 netNames.push_back( FromUTF8() );
3223 NeedRIGHT();
3224 }
3225 NeedRIGHT();
3226 }
3227 else
3228 skipCurrent();
3229 }
3230 break;
3231 }
3232
3233 case T_terminal_pad:
3234 if( padCount < 2 )
3235 {
3236 NeedSYMBOLorNUMBER();
3237 padUuids[padCount++] = KIID( FromUTF8() );
3238 NeedRIGHT();
3239 }
3240 else
3241 {
3242 skipCurrent();
3243 }
3244 break;
3245
3246 default:
3247 skipCurrent();
3248 break;
3249 }
3250 }
3251
3252 std::vector<NETINFO_ITEM*> resolvedNets;
3253
3254 for( int code : netCodes )
3255 {
3256 int internalCode = code;
3257
3258 if( code >= 0 && code < (int) m_netCodes.size() && m_netCodes[code] != 0 )
3259 internalCode = m_netCodes[code];
3260
3261 if( NETINFO_ITEM* net = m_board->FindNet( internalCode ) )
3262 resolvedNets.push_back( net );
3263 }
3264
3265 for( const wxString& netName : netNames )
3266 {
3267 if( NETINFO_ITEM* net = m_board->FindNet( netName ) )
3268 resolvedNets.push_back( net );
3269 }
3270
3271 for( NETINFO_ITEM* net : resolvedNets )
3272 {
3273 net->SetNetChain( name );
3274
3275 for( int i = 0; i < padCount && i < 2; ++i )
3276 {
3277 net->SetTerminalPadUuid( i, padUuids[i] );
3278
3279 if( PAD* pad = m_board->FindPadByUuid( padUuids[i] ) )
3280 net->SetTerminalPad( i, pad );
3281 }
3282 }
3283 }
3284}
3285
3286
3288{
3289 wxCHECK_RET( CurTok() == T_net_class,
3290 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as net class." ) );
3291
3292 T token;
3293
3294 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( wxEmptyString );
3295
3296 // Read netclass name (can be a name or just a number like track width)
3297 NeedSYMBOLorNUMBER();
3298 nc->SetName( FromUTF8() );
3299 NeedSYMBOL();
3300 nc->SetDescription( FromUTF8() );
3301
3302 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3303 {
3304 if( token != T_LEFT )
3305 Expecting( T_LEFT );
3306
3307 token = NextTok();
3308
3309 switch( token )
3310 {
3311 case T_clearance:
3312 nc->SetClearance( parseBoardUnits( T_clearance ) );
3313 break;
3314
3315 case T_trace_width:
3316 nc->SetTrackWidth( parseBoardUnits( T_trace_width ) );
3317 break;
3318
3319 case T_via_dia:
3320 nc->SetViaDiameter( parseBoardUnits( T_via_dia ) );
3321 break;
3322
3323 case T_via_drill:
3324 nc->SetViaDrill( parseBoardUnits( T_via_drill ) );
3325 break;
3326
3327 case T_uvia_dia:
3328 nc->SetuViaDiameter( parseBoardUnits( T_uvia_dia ) );
3329 break;
3330
3331 case T_uvia_drill:
3332 nc->SetuViaDrill( parseBoardUnits( T_uvia_drill ) );
3333 break;
3334
3335 case T_diff_pair_width:
3336 nc->SetDiffPairWidth( parseBoardUnits( T_diff_pair_width ) );
3337 break;
3338
3339 case T_diff_pair_gap:
3340 nc->SetDiffPairGap( parseBoardUnits( T_diff_pair_gap ) );
3341 break;
3342
3343 case T_add_net:
3344 {
3345 NeedSYMBOLorNUMBER();
3346
3347 wxString netName = FromUTF8();
3348
3349 // Convert overbar syntax from `~...~` to `~{...}`. These were left out of the
3350 // first merge so the version is a bit later.
3351 if( m_requiredVersion < 20210606 )
3352 netName = ConvertToNewOverbarNotation( FromUTF8() );
3353
3354 m_board->GetDesignSettings().m_NetSettings->SetNetclassPatternAssignment(
3355 netName, nc->GetName() );
3356
3357 break;
3358 }
3359
3360 default:
3361 Expecting( "clearance, trace_width, via_dia, via_drill, uvia_dia, uvia_drill, "
3362 "diff_pair_width, diff_pair_gap or add_net" );
3363 }
3364
3365 NeedRIGHT();
3366 }
3367
3368 std::shared_ptr<NET_SETTINGS>& netSettings = m_board->GetDesignSettings().m_NetSettings;
3369
3370 if( netSettings->HasNetclass( nc->GetName() ) )
3371 {
3372 // Must have been a name conflict, this is a bad board file.
3373 // User may have done a hand edit to the file.
3374 wxString error;
3375 error.Printf( _( "Duplicate NETCLASS name '%s' in file '%s' at line %d, offset %d." ),
3376 nc->GetName().GetData(), CurSource().GetData(), CurLineNumber(),
3377 CurOffset() );
3378 THROW_IO_ERROR( error );
3379 }
3380 else if( nc->GetName() == netSettings->GetDefaultNetclass()->GetName() )
3381 {
3382 netSettings->SetDefaultNetclass( nc );
3383 }
3384 else
3385 {
3386 netSettings->SetNetclass( nc->GetName(), nc );
3387 }
3388}
3389
3390
3392{
3393 wxCHECK_MSG( CurTok() == T_fp_arc || CurTok() == T_fp_circle || CurTok() == T_fp_curve || CurTok() == T_fp_rect
3394 || CurTok() == T_fp_line || CurTok() == T_fp_poly || CurTok() == T_fp_ellipse
3395 || CurTok() == T_fp_ellipse_arc || CurTok() == T_gr_arc || CurTok() == T_gr_circle
3396 || CurTok() == T_gr_curve || CurTok() == T_gr_rect || CurTok() == T_gr_bbox
3397 || CurTok() == T_gr_line || CurTok() == T_gr_poly || CurTok() == T_gr_vector
3398 || CurTok() == T_gr_ellipse || CurTok() == T_gr_ellipse_arc,
3399 nullptr, wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_SHAPE." ) );
3400
3401 T token;
3402 VECTOR2I pt;
3403 STROKE_PARAMS stroke( 0, LINE_STYLE::SOLID );
3404 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aParent );
3405
3406 switch( CurTok() )
3407 {
3408 case T_gr_arc:
3409 case T_fp_arc:
3410 shape->SetShape( SHAPE_T::ARC );
3411 token = NextTok();
3412
3413 if( token == T_locked )
3414 {
3415 shape->SetLocked( true );
3416 token = NextTok();
3417 }
3418
3419 if( token != T_LEFT )
3420 Expecting( T_LEFT );
3421
3422 token = NextTok();
3423
3425 {
3426 // In legacy files the start keyword actually gives the arc center...
3427 if( token != T_start )
3428 Expecting( T_start );
3429
3430 pt.x = parseBoardUnits( "X coordinate" );
3431 pt.y = parseBoardUnits( "Y coordinate" );
3432 shape->SetCenter( pt );
3433 NeedRIGHT();
3434 NeedLEFT();
3435 token = NextTok();
3436
3437 // ... and the end keyword gives the start point of the arc
3438 if( token != T_end )
3439 Expecting( T_end );
3440
3441 pt.x = parseBoardUnits( "X coordinate" );
3442 pt.y = parseBoardUnits( "Y coordinate" );
3443 shape->SetStart( pt );
3444 NeedRIGHT();
3445 NeedLEFT();
3446 token = NextTok();
3447
3448 if( token != T_angle )
3449 Expecting( T_angle );
3450
3451 shape->SetArcAngleAndEnd( EDA_ANGLE( parseDouble( "arc angle" ), DEGREES_T ), true );
3452 NeedRIGHT();
3453 }
3454 else
3455 {
3456 VECTOR2I arc_start, arc_mid, arc_end;
3457
3458 if( token != T_start )
3459 Expecting( T_start );
3460
3461 arc_start.x = parseBoardUnits( "X coordinate" );
3462 arc_start.y = parseBoardUnits( "Y coordinate" );
3463 NeedRIGHT();
3464 NeedLEFT();
3465 token = NextTok();
3466
3467 if( token != T_mid )
3468 Expecting( T_mid );
3469
3470 arc_mid.x = parseBoardUnits( "X coordinate" );
3471 arc_mid.y = parseBoardUnits( "Y coordinate" );
3472 NeedRIGHT();
3473 NeedLEFT();
3474 token = NextTok();
3475
3476 if( token != T_end )
3477 Expecting( T_end );
3478
3479 arc_end.x = parseBoardUnits( "X coordinate" );
3480 arc_end.y = parseBoardUnits( "Y coordinate" );
3481 NeedRIGHT();
3482
3483 shape->SetArcGeometry( arc_start, arc_mid, arc_end );
3484 }
3485
3486 break;
3487
3488 case T_gr_circle:
3489 case T_fp_circle:
3490 shape->SetShape( SHAPE_T::CIRCLE );
3491 token = NextTok();
3492
3493 if( token == T_locked )
3494 {
3495 shape->SetLocked( true );
3496 token = NextTok();
3497 }
3498
3499 if( token != T_LEFT )
3500 Expecting( T_LEFT );
3501
3502 token = NextTok();
3503
3504 if( token != T_center )
3505 Expecting( T_center );
3506
3507 pt.x = parseBoardUnits( "X coordinate" );
3508 pt.y = parseBoardUnits( "Y coordinate" );
3509 shape->SetStart( pt );
3510 NeedRIGHT();
3511 NeedLEFT();
3512
3513 token = NextTok();
3514
3515 if( token != T_end )
3516 Expecting( T_end );
3517
3518 pt.x = parseBoardUnits( "X coordinate" );
3519 pt.y = parseBoardUnits( "Y coordinate" );
3520 shape->SetEnd( pt );
3521 NeedRIGHT();
3522 break;
3523
3524 case T_gr_curve:
3525 case T_fp_curve:
3526 shape->SetShape( SHAPE_T::BEZIER );
3527 token = NextTok();
3528
3529 if( token == T_locked )
3530 {
3531 shape->SetLocked( true );
3532 token = NextTok();
3533 }
3534
3535 if( token != T_LEFT )
3536 Expecting( T_LEFT );
3537
3538 token = NextTok();
3539
3540 if( token != T_pts )
3541 Expecting( T_pts );
3542
3543 shape->SetStart( parseXY() );
3544 shape->SetBezierC1( parseXY());
3545 shape->SetBezierC2( parseXY());
3546 shape->SetEnd( parseXY() );
3547
3548 if( m_board )
3549 shape->RebuildBezierToSegmentsPointsList( m_board->GetDesignSettings().m_MaxError );
3550 else
3551 shape->RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
3552
3553 NeedRIGHT();
3554 break;
3555
3556 case T_gr_bbox:
3557 case T_gr_rect:
3558 case T_fp_rect:
3559 shape->SetShape( SHAPE_T::RECTANGLE );
3560 token = NextTok();
3561
3562 if( token == T_locked )
3563 {
3564 shape->SetLocked( true );
3565 token = NextTok();
3566 }
3567
3568 if( token != T_LEFT )
3569 Expecting( T_LEFT );
3570
3571 token = NextTok();
3572
3573 if( token != T_start )
3574 Expecting( T_start );
3575
3576 pt.x = parseBoardUnits( "X coordinate" );
3577 pt.y = parseBoardUnits( "Y coordinate" );
3578 shape->SetStart( pt );
3579 NeedRIGHT();
3580 NeedLEFT();
3581 token = NextTok();
3582
3583 if( token != T_end )
3584 Expecting( T_end );
3585
3586 pt.x = parseBoardUnits( "X coordinate" );
3587 pt.y = parseBoardUnits( "Y coordinate" );
3588 shape->SetEnd( pt );
3589
3590 if( aParent && aParent->Type() == PCB_FOOTPRINT_T )
3591 {
3592 // Footprint shapes are stored in board-relative coordinates, but we want the
3593 // normalization to remain in footprint-relative coordinates.
3594 }
3595 else
3596 {
3597 shape->Normalize();
3598 }
3599
3600 NeedRIGHT();
3601 break;
3602
3603 case T_gr_vector:
3604 case T_gr_line:
3605 case T_fp_line:
3606 shape->SetShape( SHAPE_T::SEGMENT );
3607 token = NextTok();
3608
3609 if( token == T_locked )
3610 {
3611 shape->SetLocked( true );
3612 token = NextTok();
3613 }
3614
3615 if( token != T_LEFT )
3616 Expecting( T_LEFT );
3617
3618 token = NextTok();
3619
3620 if( token != T_start )
3621 Expecting( T_start );
3622
3623 pt.x = parseBoardUnits( "X coordinate" );
3624 pt.y = parseBoardUnits( "Y coordinate" );
3625 shape->SetStart( pt );
3626 NeedRIGHT();
3627 NeedLEFT();
3628 token = NextTok();
3629
3630 if( token != T_end )
3631 Expecting( T_end );
3632
3633 pt.x = parseBoardUnits( "X coordinate" );
3634 pt.y = parseBoardUnits( "Y coordinate" );
3635 shape->SetEnd( pt );
3636 NeedRIGHT();
3637 break;
3638
3639 case T_gr_poly:
3640 case T_fp_poly:
3641 {
3642 shape->SetShape( SHAPE_T::POLY );
3643 shape->SetPolyPoints( {} );
3644
3645 SHAPE_LINE_CHAIN& outline = shape->GetPolyShape().Outline( 0 );
3646
3647 token = NextTok();
3648
3649 if( token == T_locked )
3650 {
3651 shape->SetLocked( true );
3652 token = NextTok();
3653 }
3654
3655 if( token != T_LEFT )
3656 Expecting( T_LEFT );
3657
3658 token = NextTok();
3659
3660 if( token != T_pts )
3661 Expecting( T_pts );
3662
3663 while( (token = NextTok() ) != T_RIGHT )
3664 parseOutlinePoints( outline );
3665
3666 break;
3667 }
3668
3669 case T_gr_ellipse:
3670 case T_fp_ellipse: shape->SetShape( SHAPE_T::ELLIPSE ); break;
3671
3672 case T_gr_ellipse_arc:
3673 case T_fp_ellipse_arc: shape->SetShape( SHAPE_T::ELLIPSE_ARC ); break;
3674
3675 default:
3676 if( aParent && aParent->Type() == PCB_FOOTPRINT_T )
3677 {
3678 Expecting( "fp_arc, fp_circle, fp_curve, fp_ellipse, fp_ellipse_arc, "
3679 "fp_line, fp_poly or fp_rect" );
3680 }
3681 else
3682 {
3683 Expecting( "gr_arc, gr_circle, gr_curve, gr_ellipse, gr_ellipse_arc, "
3684 "gr_vector, gr_line, gr_poly, gr_rect or gr_bbox" );
3685 }
3686 }
3687
3688 bool foundFill = false;
3689
3690 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3691 {
3692 if( token != T_LEFT )
3693 Expecting( T_LEFT );
3694
3695 token = NextTok();
3696
3697 switch( token )
3698 {
3699 case T_angle: // legacy token; ignore value
3700 parseDouble( "arc angle" );
3701 NeedRIGHT();
3702 break;
3703
3704 case T_layer:
3705 shape->SetLayer( parseBoardItemLayer() );
3706 NeedRIGHT();
3707 break;
3708
3709 case T_layers:
3710 shape->SetLayerSet( parseBoardItemLayersAsMask() );
3711 break;
3712
3713 case T_solder_mask_margin:
3714 shape->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
3715 NeedRIGHT();
3716 break;
3717
3718 case T_width: // legacy token
3719 stroke.SetWidth( parseBoardUnits( T_width ) );
3720 NeedRIGHT();
3721 break;
3722
3723 case T_radius:
3724 shape->SetCornerRadius( parseBoardUnits( "corner radius" ) );
3725 NeedRIGHT();
3726 break;
3727
3728 case T_stroke:
3729 {
3730 STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
3731 strokeParser.SyncLineReaderWith( *this );
3732
3733 strokeParser.ParseStroke( stroke );
3734 SyncLineReaderWith( strokeParser );
3735 break;
3736 }
3737
3738 case T_tstamp:
3739 case T_uuid:
3740 NextTok();
3741 shape->SetUuidDirect( CurStrToKIID() );
3742 NeedRIGHT();
3743 break;
3744
3745 case T_fill:
3746 foundFill = true;
3747
3748 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3749 {
3750 if( token == T_LEFT )
3751 token = NextTok();
3752
3753 switch( token )
3754 {
3755 // T_yes was used to indicate filling when first introduced, so treat it like a
3756 // solid fill since that was the only fill available at the time.
3757 case T_yes:
3758 case T_solid: shape->SetFillMode( FILL_T::FILLED_SHAPE ); break;
3759
3760 case T_none:
3761 case T_no: shape->SetFillMode( FILL_T::NO_FILL ); break;
3762
3763 case T_hatch: shape->SetFillMode( FILL_T::HATCH ); break;
3764 case T_reverse_hatch: shape->SetFillMode( FILL_T::REVERSE_HATCH ); break;
3765 case T_cross_hatch: shape->SetFillMode( FILL_T::CROSS_HATCH ); break;
3766
3767 default: Expecting( "yes, no, solid, none, hatch, reverse_hatch or cross_hatch" );
3768 }
3769 }
3770
3771 break;
3772
3773 case T_status: // legacy token; ignore value
3774 parseHex();
3775 NeedRIGHT();
3776 break;
3777
3778 // Handle "(locked)" from 5.99 development, and "(locked yes)" from modern times
3779 case T_locked:
3780 shape->SetLocked( parseMaybeAbsentBool( true ) );
3781 break;
3782
3783 case T_net:
3784 parseNet( shape.get() );
3785 break;
3786
3787 case T_center:
3788 pt.x = parseBoardUnits( "X coordinate" );
3789 pt.y = parseBoardUnits( "Y coordinate" );
3790 shape->SetEllipseCenter( pt );
3791 NeedRIGHT();
3792 break;
3793
3794 case T_major_radius:
3795 shape->SetEllipseMajorRadius( parseBoardUnits( "major radius" ) );
3796 NeedRIGHT();
3797 break;
3798
3799 case T_minor_radius:
3800 shape->SetEllipseMinorRadius( parseBoardUnits( "minor radius" ) );
3801 NeedRIGHT();
3802 break;
3803
3804 case T_rotation_angle:
3805 shape->SetEllipseRotation( EDA_ANGLE( parseDouble( "rotation angle" ), DEGREES_T ) );
3806 NeedRIGHT();
3807 break;
3808
3809 case T_start_angle:
3810 shape->SetEllipseStartAngle( EDA_ANGLE( parseDouble( "start angle" ), DEGREES_T ) );
3811 NeedRIGHT();
3812 break;
3813
3814 case T_end_angle:
3815 shape->SetEllipseEndAngle( EDA_ANGLE( parseDouble( "end angle" ), DEGREES_T ) );
3816 NeedRIGHT();
3817 break;
3818
3819 default:
3820 Expecting( "layer, width, fill, tstamp, uuid, locked, net, status, "
3821 "solder_mask_margin, center, major_radius, minor_radius, "
3822 "rotation_angle, start_angle, or end_angle" );
3823 }
3824 }
3825
3826 if( !foundFill )
3827 {
3828 // Legacy versions didn't have a filled flag but allowed some shapes to indicate they
3829 // should be filled by specifying a 0 stroke-width.
3830 if( stroke.GetWidth() == 0
3831 && ( shape->GetShape() == SHAPE_T::RECTANGLE || shape->GetShape() == SHAPE_T::CIRCLE ) )
3832 {
3833 shape->SetFilled( true );
3834 }
3835 else if( shape->GetShape() == SHAPE_T::POLY && shape->GetLayer() != Edge_Cuts )
3836 {
3837 // Polygons on non-Edge_Cuts layers were always filled.
3838 shape->SetFilled( true );
3839 }
3840 }
3841
3842 // Only filled shapes may have a zero line-width. This is not permitted in KiCad but some
3843 // external tools can generate invalid files.
3844 if( stroke.GetWidth() <= 0 && !shape->IsAnyFill() )
3845 {
3846 stroke.SetWidth( pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH ) );
3847 }
3848
3849 shape->SetStroke( stroke );
3850
3851 if( FOOTPRINT* parentFP = shape->GetParentFootprint() )
3852 {
3853 shape->Rotate( { 0, 0 }, parentFP->GetOrientation() );
3854 shape->Move( parentFP->GetPosition() );
3855 }
3856
3857 return shape.release();
3858}
3859
3860
3862{
3863 wxCHECK_MSG( CurTok() == T_image, nullptr,
3864 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a reference image." ) );
3865
3866 T token;
3867 std::unique_ptr<PCB_REFERENCE_IMAGE> bitmap = std::make_unique<PCB_REFERENCE_IMAGE>( aParent );
3868
3869 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3870 {
3871 if( token != T_LEFT )
3872 Expecting( T_LEFT );
3873
3874 token = NextTok();
3875
3876 switch( token )
3877 {
3878 case T_at:
3879 {
3880 VECTOR2I pos;
3881 pos.x = parseBoardUnits( "X coordinate" );
3882 pos.y = parseBoardUnits( "Y coordinate" );
3883 bitmap->SetPosition( pos );
3884 NeedRIGHT();
3885 break;
3886 }
3887
3888 case T_layer:
3889 bitmap->SetLayer( parseBoardItemLayer() );
3890 NeedRIGHT();
3891 break;
3892
3893 case T_scale:
3894 {
3895 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
3896 refImage.SetImageScale( parseDouble( "image scale factor" ) );
3897
3898 if( !std::isnormal( refImage.GetImageScale() ) )
3899 refImage.SetImageScale( 1.0 );
3900
3901 NeedRIGHT();
3902 break;
3903 }
3904 case T_data:
3905 {
3906 token = NextTok();
3907
3908 wxString data;
3909
3910 // Reserve 512K because most image files are going to be larger than the default
3911 // 1K that wxString reserves.
3912 data.reserve( 1 << 19 );
3913
3914 while( token != T_RIGHT )
3915 {
3916 if( !IsSymbol( token ) )
3917 Expecting( "base64 image data" );
3918
3919 data += FromUTF8();
3920 token = NextTok();
3921 }
3922
3923 wxMemoryBuffer buffer = wxBase64Decode( data );
3924
3925 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
3926 if( !refImage.ReadImageFile( buffer ) )
3927 THROW_IO_ERROR( _( "Failed to read image data." ) );
3928
3929 break;
3930 }
3931
3932 case T_locked:
3933 {
3934 // This has only ever been (locked yes) format
3935 const bool locked = parseBool();
3936 bitmap->SetLocked( locked );
3937
3938 NeedRIGHT();
3939 break;
3940 }
3941
3942 case T_uuid:
3943 {
3944 NextTok();
3945 bitmap->SetUuidDirect( CurStrToKIID() );
3946 NeedRIGHT();
3947 break;
3948 }
3949
3950 default:
3951 Expecting( "at, layer, scale, data, locked or uuid" );
3952 }
3953 }
3954
3955 return bitmap.release();
3956}
3957
3958
3960{
3961 wxCHECK_MSG( CurTok() == T_gr_text || CurTok() == T_fp_text, nullptr,
3962 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TEXT." ) );
3963
3964 FOOTPRINT* parentFP = dynamic_cast<FOOTPRINT*>( aParent );
3965 std::unique_ptr<PCB_TEXT> text;
3966
3967 T token = NextTok();
3968
3969 // If a base text is provided, we have a derived text already parsed and just need to update it
3970 if( aBaseText )
3971 {
3972 text = std::unique_ptr<PCB_TEXT>( aBaseText );
3973 }
3974 else if( parentFP )
3975 {
3976 switch( token )
3977 {
3978 case T_reference:
3979 text = std::make_unique<PCB_FIELD>( parentFP, FIELD_T::REFERENCE );
3980 break;
3981
3982 case T_value:
3983 text = std::make_unique<PCB_FIELD>( parentFP, FIELD_T::VALUE );
3984 break;
3985
3986 case T_user:
3987 text = std::make_unique<PCB_TEXT>( parentFP );
3988 break;
3989
3990 default:
3991 THROW_IO_ERROR( wxString::Format( _( "Cannot handle footprint text type %s" ),
3992 FromUTF8() ) );
3993 }
3994
3995 token = NextTok();
3996 }
3997 else
3998 {
3999 text = std::make_unique<PCB_TEXT>( aParent );
4000 }
4001
4002 // Legacy bare locked token
4003 if( token == T_locked )
4004 {
4005 text->SetLocked( true );
4006 token = NextTok();
4007 }
4008
4009 if( !IsSymbol( token ) && (int) token != DSN_NUMBER )
4010 Expecting( "text value" );
4011
4012 wxString value = FromUTF8();
4013 value.Replace( wxT( "%V" ), wxT( "${VALUE}" ) );
4014 value.Replace( wxT( "%R" ), wxT( "${REFERENCE}" ) );
4015 text->SetText( value );
4016
4017 NeedLEFT();
4018
4019 parsePCB_TEXT_effects( text.get(), aBaseText );
4020
4021 if( parentFP )
4022 {
4023 // Convert hidden footprint text (which is no longer supported) into a hidden field
4024 if( !text->IsVisible() && text->Type() == PCB_TEXT_T )
4025 {
4026 wxString fieldName = GetUserFieldName( parentFP->GetFields().size(), !DO_TRANSLATE );
4027 return new PCB_FIELD( *text.get(), FIELD_T::USER, fieldName );
4028 }
4029 }
4030 else
4031 {
4032 // Hidden PCB text is no longer supported
4033 text->SetVisible( true );
4034 }
4035
4036 return text.release();
4037}
4038
4039
4041{
4042 FOOTPRINT* parentFP = dynamic_cast<FOOTPRINT*>( aText->GetParent() );
4043 bool hasAngle = false; // Old files do not have a angle specified.
4044 // in this case it is 0 expected to be 0
4045 bool hasPos = false;
4046
4047 // By default, texts in footprints have a locked rotation (i.e. rot = -90 ... 90 deg)
4048 if( parentFP )
4049 aText->SetKeepUpright( true );
4050
4051 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
4052 {
4053 if( token == T_LEFT )
4054 token = NextTok();
4055
4056 switch( token )
4057 {
4058 case T_at:
4059 {
4060 VECTOR2I pt;
4061
4062 hasPos = true;
4063 pt.x = parseBoardUnits( "X coordinate" );
4064 pt.y = parseBoardUnits( "Y coordinate" );
4065 aText->SetTextPos( pt );
4066 token = NextTok();
4067
4068 if( CurTok() == T_NUMBER )
4069 {
4071 hasAngle = true;
4072 token = NextTok();
4073 }
4074
4075 // Legacy location of this token; presence implies true
4076 if( parentFP && CurTok() == T_unlocked )
4077 {
4078 aText->SetKeepUpright( false );
4079 token = NextTok();
4080 }
4081
4082 if( (int) token != DSN_RIGHT )
4083 Expecting( DSN_RIGHT );
4084
4085 break;
4086 }
4087
4088 case T_layer:
4089 aText->SetLayer( parseBoardItemLayer() );
4090
4091 token = NextTok();
4092
4093 if( token == T_knockout )
4094 {
4095 aText->SetIsKnockout( true );
4096 token = NextTok();
4097 }
4098
4099 if( (int) token != DSN_RIGHT )
4100 Expecting( DSN_RIGHT );
4101
4102 break;
4103
4104 case T_tstamp:
4105 case T_uuid:
4106 NextTok();
4107 aText->SetUuidDirect( CurStrToKIID() );
4108 NeedRIGHT();
4109 break;
4110
4111 case T_hide:
4112 {
4113 // In older files, the hide token appears bare, and indicates hide==true.
4114 // In newer files, it will be an explicit bool in a list like (hide yes)
4115 bool hide = parseMaybeAbsentBool( true );
4116
4117 if( parentFP )
4118 aText->SetVisible( !hide );
4119 else
4120 Expecting( "layer, effects, locked, render_cache, uuid or tstamp" );
4121
4122 break;
4123 }
4124
4125 case T_locked:
4126 // Newer list-enclosed locked
4127 aText->SetLocked( parseBool() );
4128 NeedRIGHT();
4129 break;
4130
4131 // Confusingly, "unlocked" is not the opposite of "locked", but refers to "keep upright"
4132 case T_unlocked:
4133 if( parentFP )
4134 aText->SetKeepUpright( !parseBool() );
4135 else
4136 Expecting( "layer, effects, locked, render_cache or tstamp" );
4137
4138 NeedRIGHT();
4139 break;
4140
4141 case T_effects:
4142 parseEDA_TEXT( static_cast<EDA_TEXT*>( aText ) );
4143 break;
4144
4145 case T_render_cache:
4146 parseRenderCache( static_cast<EDA_TEXT*>( aText ) );
4147 break;
4148
4149 default:
4150 if( parentFP )
4151 Expecting( "layer, hide, effects, locked, render_cache or tstamp" );
4152 else
4153 Expecting( "layer, effects, locked, render_cache or tstamp" );
4154 }
4155 }
4156
4157 // If there is no orientation defined, then it is the default value of 0 degrees.
4158 if( !hasAngle )
4159 aText->SetTextAngle( ANGLE_0 );
4160
4161 if( parentFP && !dynamic_cast<PCB_DIMENSION_BASE*>( aBaseText ) )
4162 {
4163 // make PCB_TEXT rotation relative to the parent footprint.
4164 // It was read as absolute rotation from file
4165 // Note: this is not rue for PCB_DIMENSION items that use the board
4166 // coordinates
4167 aText->SetTextAngle( aText->GetTextAngle() - parentFP->GetOrientation() );
4168
4169 // Move and rotate the text to its board coordinates
4170 aText->Rotate( { 0, 0 }, parentFP->GetOrientation() );
4171
4172 // Only move offset from parent position if we read a position from the file.
4173 // These positions are relative to the parent footprint. If we don't have a position
4174 // then the text defaults to the parent position and moving again will double it.
4175 if (hasPos)
4176 aText->Move( parentFP->GetPosition() );
4177 }
4178}
4179
4180
4182{
4183 wxCHECK_MSG( CurTok() == T_barcode, nullptr,
4184 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_BARCODE." ) );
4185
4186 std::unique_ptr<PCB_BARCODE> barcode = std::make_unique<PCB_BARCODE>( aParent );
4187
4188 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
4189 {
4190 if( token != T_LEFT )
4191 Expecting( T_LEFT );
4192
4193 token = NextTok();
4194
4195 switch( token )
4196 {
4197 case T_at:
4198 {
4199 VECTOR2I pos;
4200 pos.x = parseBoardUnits( "X coordinate" );
4201 pos.y = parseBoardUnits( "Y coordinate" );
4202 barcode->SetPosition( pos );
4203 token = NextTok();
4204
4205 if( CurTok() == T_NUMBER )
4206 barcode->SetOrientation( parseDouble() );
4207
4208 NeedRIGHT();
4209 break;
4210 }
4211
4212 case T_layer:
4213 barcode->SetLayer( parseBoardItemLayer() );
4214 NeedRIGHT();
4215 break;
4216
4217 case T_size:
4218 {
4219 int w = parseBoardUnits( "barcode width" );
4220 int h = parseBoardUnits( "barcode height" );
4221 barcode->SetWidth( w );
4222 barcode->SetHeight( h );
4223 NeedRIGHT();
4224 break;
4225 }
4226
4227 case T_text:
4228
4229 if( NextTok() != T_STRING )
4230 Expecting( T_STRING );
4231
4232 barcode->SetText( FromUTF8() );
4233 NeedRIGHT();
4234 break;
4235
4236 case T_text_height:
4237 {
4238 int h = parseBoardUnits( "barcode text height" );
4239 barcode->SetTextSize( h );
4240 NeedRIGHT();
4241 break;
4242 }
4243
4244 case T_type:
4245 NeedSYMBOL();
4246 {
4247 std::string kind = CurText();
4248 if( kind == "code39" )
4249 barcode->SetKind( BARCODE_T::CODE_39 );
4250 else if( kind == "code128" )
4251 barcode->SetKind( BARCODE_T::CODE_128 );
4252 else if( kind == "datamatrix" || kind == "data_matrix" )
4253 barcode->SetKind( BARCODE_T::DATA_MATRIX );
4254 else if( kind == "qr" || kind == "qrcode" )
4255 barcode->SetKind( BARCODE_T::QR_CODE );
4256 else if( kind == "microqr" || kind == "micro_qr" )
4257 barcode->SetKind( BARCODE_T::MICRO_QR_CODE );
4258 else
4259 Expecting( "barcode type" );
4260 }
4261 NeedRIGHT();
4262 break;
4263
4264 case T_ecc_level:
4265 NeedSYMBOL();
4266 {
4267 std::string ecc = CurText();
4268 if( ecc == "L" || ecc == "l" )
4269 barcode->SetErrorCorrection( BARCODE_ECC_T::L );
4270 else if( ecc == "M" || ecc == "m" )
4271 barcode->SetErrorCorrection( BARCODE_ECC_T::M );
4272 else if( ecc == "Q" || ecc == "q" )
4273 barcode->SetErrorCorrection( BARCODE_ECC_T::Q );
4274 else if( ecc == "H" || ecc == "h" )
4275 barcode->SetErrorCorrection( BARCODE_ECC_T::H );
4276 else
4277 Expecting( "ecc level" );
4278 }
4279 NeedRIGHT();
4280 break;
4281
4282
4283 case T_locked:
4284 barcode->SetLocked( parseMaybeAbsentBool( true ) );
4285 break;
4286
4287 case T_tstamp:
4288 case T_uuid:
4289 NextTok();
4290 barcode->SetUuidDirect( CurStrToKIID() );
4291 NeedRIGHT();
4292 break;
4293
4294 case T_hide:
4295 barcode->SetShowText( !parseBool() );
4296 NeedRIGHT();
4297 break;
4298
4299 case T_knockout:
4300 barcode->SetIsKnockout( parseBool() );
4301 NeedRIGHT();
4302 break;
4303
4304 case T_margins:
4305 {
4306 int marginX = parseBoardUnits( "margin X" );
4307 int marginY = parseBoardUnits( "margin Y" );
4308 barcode->SetMargin( VECTOR2I( marginX, marginY ) );
4309 NeedRIGHT();
4310 break;
4311 }
4312
4313 default:
4314 Expecting( "at, layer, size, text, text_height, type, ecc_level, locked, hide, knockout, margins or uuid" );
4315 }
4316 }
4317
4318 barcode->AssembleBarcode();
4319
4320 return barcode.release();
4321}
4322
4323
4325{
4326 wxCHECK_MSG( CurTok() == T_gr_text_box || CurTok() == T_fp_text_box, nullptr,
4327 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TEXTBOX." ) );
4328
4329 std::unique_ptr<PCB_TEXTBOX> textbox = std::make_unique<PCB_TEXTBOX>( aParent );
4330
4331 parseTextBoxContent( textbox.get() );
4332
4333 return textbox.release();
4334}
4335
4336
4338{
4339 wxCHECK_MSG( CurTok() == T_table_cell, nullptr,
4340 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table cell." ) );
4341
4342 std::unique_ptr<PCB_TABLECELL> cell = std::make_unique<PCB_TABLECELL>( aParent );
4343
4344 parseTextBoxContent( cell.get() );
4345
4346 return cell.release();
4347}
4348
4349
4351{
4352 int left;
4353 int top;
4354 int right;
4355 int bottom;
4356 STROKE_PARAMS stroke( -1, LINE_STYLE::SOLID );
4357 bool foundMargins = false;
4358
4359 T token = NextTok();
4360
4361 // Legacy locked
4362 if( token == T_locked )
4363 {
4364 aTextBox->SetLocked( true );
4365 token = NextTok();
4366 }
4367
4368 if( !IsSymbol( token ) && (int) token != DSN_NUMBER )
4369 Expecting( "text value" );
4370
4371 aTextBox->SetText( FromUTF8() );
4372
4373 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4374 {
4375 if( token != T_LEFT )
4376 Expecting( T_LEFT );
4377
4378 token = NextTok();
4379
4380 switch( token )
4381 {
4382 case T_locked:
4383 aTextBox->SetLocked( parseMaybeAbsentBool( true ) );
4384 break;
4385
4386 case T_start:
4387 {
4388 int x = parseBoardUnits( "X coordinate" );
4389 int y = parseBoardUnits( "Y coordinate" );
4390 aTextBox->SetStart( VECTOR2I( x, y ) );
4391 NeedRIGHT();
4392
4393 NeedLEFT();
4394 token = NextTok();
4395
4396 if( token != T_end )
4397 Expecting( T_end );
4398
4399 x = parseBoardUnits( "X coordinate" );
4400 y = parseBoardUnits( "Y coordinate" );
4401 aTextBox->SetEnd( VECTOR2I( x, y ) );
4402 NeedRIGHT();
4403 break;
4404 }
4405
4406 case T_pts:
4407 {
4408 aTextBox->SetShape( SHAPE_T::POLY );
4409 aTextBox->GetPolyShape().RemoveAllContours();
4410 aTextBox->GetPolyShape().NewOutline();
4411
4412 while( (token = NextTok() ) != T_RIGHT )
4413 parseOutlinePoints( aTextBox->GetPolyShape().Outline( 0 ) );
4414
4415 break;
4416 }
4417
4418 case T_angle:
4419 // Set the angle of the text only, the coordinates of the box (a polygon) are
4420 // already at the right position, and must not be rotated
4421 aTextBox->EDA_TEXT::SetTextAngle( EDA_ANGLE( parseDouble( "text box angle" ), DEGREES_T ) );
4422 NeedRIGHT();
4423 break;
4424
4425 case T_stroke:
4426 {
4427 STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
4428 strokeParser.SyncLineReaderWith( *this );
4429
4430 strokeParser.ParseStroke( stroke );
4431 SyncLineReaderWith( strokeParser );
4432 break;
4433 }
4434
4435 case T_border:
4436 aTextBox->SetBorderEnabled( parseBool() );
4437 NeedRIGHT();
4438 break;
4439
4440 case T_margins:
4441 parseMargins( left, top, right, bottom );
4442 aTextBox->SetMarginLeft( left );
4443 aTextBox->SetMarginTop( top );
4444 aTextBox->SetMarginRight( right );
4445 aTextBox->SetMarginBottom( bottom );
4446 foundMargins = true;
4447 NeedRIGHT();
4448 break;
4449
4450 case T_layer:
4451 aTextBox->SetLayer( parseBoardItemLayer() );
4452 NeedRIGHT();
4453 break;
4454
4455 case T_knockout:
4456 if( [[maybe_unused]] PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( aTextBox ) )
4457 {
4458 Expecting( "locked, start, pts, angle, width, margins, layer, effects, span, "
4459 "render_cache, uuid or tstamp" );
4460 }
4461 else
4462 {
4463 aTextBox->SetIsKnockout( parseBool() );
4464 }
4465
4466 NeedRIGHT();
4467 break;
4468
4469 case T_span:
4470 if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( aTextBox ) )
4471 {
4472 cell->SetColSpan( parseInt( "column span" ) );
4473 cell->SetRowSpan( parseInt( "row span" ) );
4474 }
4475 else
4476 {
4477 Expecting( "locked, start, pts, angle, width, stroke, border, margins, knockout, "
4478 "layer, effects, render_cache, uuid or tstamp" );
4479 }
4480
4481 NeedRIGHT();
4482 break;
4483
4484 case T_tstamp:
4485 case T_uuid:
4486 NextTok();
4487 aTextBox->SetUuidDirect( CurStrToKIID() );
4488 NeedRIGHT();
4489 break;
4490
4491 case T_effects:
4492 parseEDA_TEXT( static_cast<EDA_TEXT*>( aTextBox ) );
4493 break;
4494
4495 case T_render_cache:
4496 parseRenderCache( static_cast<EDA_TEXT*>( aTextBox ) );
4497 break;
4498
4499 default:
4500 if( dynamic_cast<PCB_TABLECELL*>( aTextBox ) != nullptr )
4501 {
4502 Expecting( "locked, start, pts, angle, width, margins, layer, effects, span, "
4503 "render_cache, uuid or tstamp" );
4504 }
4505 else
4506 {
4507 Expecting( "locked, start, pts, angle, width, stroke, border, margins, knockout,"
4508 "layer, effects, render_cache, uuid or tstamp" );
4509 }
4510 }
4511 }
4512
4513 aTextBox->SetStroke( stroke );
4514
4515 if( m_requiredVersion < 20230825 ) // compat, we move to an explicit flag
4516 aTextBox->SetBorderEnabled( stroke.GetWidth() >= 0 );
4517
4518 if( !foundMargins )
4519 {
4520 int margin = aTextBox->GetLegacyTextMargin();
4521 aTextBox->SetMarginLeft( margin );
4522 aTextBox->SetMarginTop( margin );
4523 aTextBox->SetMarginRight( margin );
4524 aTextBox->SetMarginBottom( margin );
4525 }
4526
4527 if( FOOTPRINT* parentFP = aTextBox->GetParentFootprint() )
4528 {
4529 aTextBox->Rotate( { 0, 0 }, parentFP->GetOrientation() );
4530 aTextBox->Move( parentFP->GetPosition() );
4531 }
4532}
4533
4534
4536{
4537 wxCHECK_MSG( CurTok() == T_table, nullptr,
4538 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table." ) );
4539
4540 T token;
4541 STROKE_PARAMS borderStroke( -1, LINE_STYLE::SOLID );
4542 STROKE_PARAMS separatorsStroke( -1, LINE_STYLE::SOLID );
4543 std::unique_ptr<PCB_TABLE> table = std::make_unique<PCB_TABLE>( aParent, -1 );
4544
4545 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4546 {
4547 if( token != T_LEFT )
4548 Expecting( T_LEFT );
4549
4550 token = NextTok();
4551
4552 switch( token )
4553 {
4554 case T_column_count:
4555 table->SetColCount( parseInt( "column count" ) );
4556 NeedRIGHT();
4557 break;
4558
4559 case T_uuid:
4560 NextTok();
4561 table->SetUuidDirect( CurStrToKIID() );
4562 NeedRIGHT();
4563 break;
4564
4565 case T_locked:
4566 table->SetLocked( parseBool() );
4567 NeedRIGHT();
4568 break;
4569
4570 case T_angle: // legacy token no longer used
4571 NeedRIGHT();
4572 break;
4573
4574 case T_layer:
4575 table->SetLayer( parseBoardItemLayer() );
4576 NeedRIGHT();
4577 break;
4578
4579 case T_column_widths:
4580 {
4581 int col = 0;
4582
4583 while( ( token = NextTok() ) != T_RIGHT )
4584 table->SetColWidth( col++, parseBoardUnits() );
4585
4586 break;
4587 }
4588
4589 case T_row_heights:
4590 {
4591 int row = 0;
4592
4593 while( ( token = NextTok() ) != T_RIGHT )
4594 table->SetRowHeight( row++, parseBoardUnits() );
4595
4596 break;
4597 }
4598
4599 case T_cells:
4600 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4601 {
4602 if( token != T_LEFT )
4603 Expecting( T_LEFT );
4604
4605 token = NextTok();
4606
4607 if( token != T_table_cell )
4608 Expecting( "table_cell" );
4609
4610 table->AddCell( parsePCB_TABLECELL( table.get() ) );
4611 }
4612
4613 break;
4614
4615 case T_border:
4616 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4617 {
4618 if( token != T_LEFT )
4619 Expecting( T_LEFT );
4620
4621 token = NextTok();
4622
4623 switch( token )
4624 {
4625 case T_external:
4626 table->SetStrokeExternal( parseBool() );
4627 NeedRIGHT();
4628 break;
4629
4630 case T_header:
4631 table->SetStrokeHeaderSeparator( parseBool() );
4632 NeedRIGHT();
4633 break;
4634
4635 case T_stroke:
4636 {
4637 STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
4638 strokeParser.SyncLineReaderWith( *this );
4639
4640 strokeParser.ParseStroke( borderStroke );
4641 SyncLineReaderWith( strokeParser );
4642
4643 table->SetBorderStroke( borderStroke );
4644 break;
4645 }
4646
4647 default:
4648 Expecting( "external, header or stroke" );
4649 break;
4650 }
4651 }
4652
4653 break;
4654
4655 case T_separators:
4656 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4657 {
4658 if( token != T_LEFT )
4659 Expecting( T_LEFT );
4660
4661 token = NextTok();
4662
4663 switch( token )
4664 {
4665 case T_rows:
4666 table->SetStrokeRows( parseBool() );
4667 NeedRIGHT();
4668 break;
4669
4670 case T_cols:
4671 table->SetStrokeColumns( parseBool() );
4672 NeedRIGHT();
4673 break;
4674
4675 case T_stroke:
4676 {
4677 STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
4678 strokeParser.SyncLineReaderWith( *this );
4679
4680 strokeParser.ParseStroke( separatorsStroke );
4681 SyncLineReaderWith( strokeParser );
4682
4683 table->SetSeparatorsStroke( separatorsStroke );
4684 break;
4685 }
4686
4687 default:
4688 Expecting( "rows, cols, or stroke" );
4689 break;
4690 }
4691 }
4692
4693 break;
4694
4695 default:
4696 Expecting( "columns, layer, col_widths, row_heights, border, separators, header or "
4697 "cells" );
4698 }
4699 }
4700
4701 return table.release();
4702}
4703
4704
4706{
4707 wxCHECK_MSG( CurTok() == T_dimension, nullptr,
4708 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as DIMENSION." ) );
4709
4710 T token;
4711 bool locked = false;
4712 std::unique_ptr<PCB_DIMENSION_BASE> dim;
4713
4714 token = NextTok();
4715
4716 // Free 'locked' token from 6.0/7.0 formats
4717 if( token == T_locked )
4718 {
4719 locked = true;
4720 token = NextTok();
4721 }
4722
4723 // skip value that used to be saved
4724 if( token != T_LEFT )
4725 NeedLEFT();
4726
4727 token = NextTok();
4728
4729 bool isLegacyDimension = false;
4730 bool isStyleKnown = false;
4731
4732 // Old format
4733 if( token == T_width )
4734 {
4735 isLegacyDimension = true;
4736 dim = std::make_unique<PCB_DIM_ALIGNED>( aParent );
4737 dim->SetLineThickness( parseBoardUnits( "dimension width value" ) );
4738 NeedRIGHT();
4739 }
4740 else
4741 {
4742 if( token != T_type )
4743 Expecting( T_type );
4744
4745 switch( NextTok() )
4746 {
4747 case T_aligned: dim = std::make_unique<PCB_DIM_ALIGNED>( aParent ); break;
4748 case T_orthogonal: dim = std::make_unique<PCB_DIM_ORTHOGONAL>( aParent ); break;
4749 case T_leader: dim = std::make_unique<PCB_DIM_LEADER>( aParent ); break;
4750 case T_center: dim = std::make_unique<PCB_DIM_CENTER>( aParent ); break;
4751 case T_radial: dim = std::make_unique<PCB_DIM_RADIAL>( aParent ); break;
4752 default: wxFAIL_MSG( wxT( "Cannot parse unknown dimension type " )
4753 + GetTokenString( CurTok() ) );
4754 }
4755
4756 NeedRIGHT();
4757
4758 // Before parsing further, set default properites for old KiCad file
4759 // versions that didnt have these properties:
4760 dim->SetArrowDirection( DIM_ARROW_DIRECTION::OUTWARD );
4761 }
4762
4763 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4764 {
4765 if( token != T_LEFT )
4766 Expecting( T_LEFT );
4767
4768 token = NextTok();
4769
4770 switch( token )
4771 {
4772 case T_layer:
4773 dim->SetLayer( parseBoardItemLayer() );
4774 NeedRIGHT();
4775 break;
4776
4777 case T_tstamp:
4778 case T_uuid:
4779 NextTok();
4780 dim->SetUuidDirect( CurStrToKIID() );
4781 NeedRIGHT();
4782 break;
4783
4784 case T_gr_text:
4785 {
4786 // In old pcb files, when parsing the text we do not yet know
4787 // if the text is kept aligned or not, and its DIM_TEXT_POSITION option.
4788 // Leave the text not aligned for now to read the text angle, and no
4789 // constraint for DIM_TEXT_POSITION in this case.
4790 // It will be set aligned (or not) later
4791 bool is_aligned = dim->GetKeepTextAligned();
4792 DIM_TEXT_POSITION t_dim_pos = dim->GetTextPositionMode();
4793
4794 if( !isStyleKnown )
4795 {
4796 dim->SetTextPositionMode( DIM_TEXT_POSITION::MANUAL );
4797 dim->SetKeepTextAligned( false );
4798 }
4799
4800 parsePCB_TEXT( m_board, dim.get() );
4801
4802 if( isLegacyDimension )
4803 {
4804 EDA_UNITS units = EDA_UNITS::MM;
4805
4806 if( !EDA_UNIT_UTILS::FetchUnitsFromString( dim->GetText(), units ) )
4807 dim->SetAutoUnits( true ); //Not determined => use automatic units
4808
4809 dim->SetUnits( units );
4810 }
4811
4812 if( !isStyleKnown )
4813 {
4814 dim->SetKeepTextAligned( is_aligned );
4815 dim->SetTextPositionMode( t_dim_pos );
4816 }
4817 break;
4818 }
4819
4820 // New format: feature points
4821 case T_pts:
4822 {
4823 VECTOR2I point;
4824
4825 parseXY( &point.x, &point.y );
4826 dim->SetStart( point );
4827 parseXY( &point.x, &point.y );
4828 dim->SetEnd( point );
4829
4830 NeedRIGHT();
4831 break;
4832 }
4833
4834 case T_height:
4835 {
4836 int height = parseBoardUnits( "dimension height value" );
4837 NeedRIGHT();
4838
4839 if( dim->Type() == PCB_DIM_ORTHOGONAL_T || dim->Type() == PCB_DIM_ALIGNED_T )
4840 {
4841 PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dim.get() );
4842 aligned->SetHeight( height );
4843 }
4844
4845 break;
4846 }
4847
4848 case T_leader_length:
4849 {
4850 int length = parseBoardUnits( "leader length value" );
4851 NeedRIGHT();
4852
4853 if( dim->Type() == PCB_DIM_RADIAL_T )
4854 {
4855 PCB_DIM_RADIAL* radial = static_cast<PCB_DIM_RADIAL*>( dim.get() );
4856 radial->SetLeaderLength( length );
4857 }
4858
4859 break;
4860 }
4861
4862 case T_orientation:
4863 {
4864 int orientation = parseInt( "orthogonal dimension orientation" );
4865 NeedRIGHT();
4866
4867 if( dim->Type() == PCB_DIM_ORTHOGONAL_T )
4868 {
4869 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dim.get() );
4870 orientation = std::clamp( orientation, 0, 1 );
4871 ortho->SetOrientation( static_cast<PCB_DIM_ORTHOGONAL::DIR>( orientation ) );
4872 }
4873
4874 break;
4875 }
4876
4877 case T_format:
4878 {
4879 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4880 {
4881 switch( token )
4882 {
4883 case T_LEFT:
4884 continue;
4885
4886 case T_prefix:
4887 NeedSYMBOLorNUMBER();
4888 dim->SetPrefix( FromUTF8() );
4889 NeedRIGHT();
4890 break;
4891
4892 case T_suffix:
4893 NeedSYMBOLorNUMBER();
4894 dim->SetSuffix( FromUTF8() );
4895 NeedRIGHT();
4896 break;
4897
4898 case T_units:
4899 {
4900 int mode = parseInt( "dimension units mode" );
4901 mode = std::max( 0, std::min( 4, mode ) );
4902 dim->SetUnitsMode( static_cast<DIM_UNITS_MODE>( mode ) );
4903 NeedRIGHT();
4904 break;
4905 }
4906
4907 case T_units_format:
4908 {
4909 int format = parseInt( "dimension units format" );
4910 format = std::clamp( format, 0, 3 );
4911 dim->SetUnitsFormat( static_cast<DIM_UNITS_FORMAT>( format ) );
4912 NeedRIGHT();
4913 break;
4914 }
4915
4916 case T_precision:
4917 dim->SetPrecision( static_cast<DIM_PRECISION>( parseInt( "dimension precision" ) ) );
4918 NeedRIGHT();
4919 break;
4920
4921 case T_override_value:
4922 NeedSYMBOLorNUMBER();
4923 dim->SetOverrideTextEnabled( true );
4924 dim->SetOverrideText( FromUTF8() );
4925 NeedRIGHT();
4926 break;
4927
4928 case T_suppress_zeroes:
4929 dim->SetSuppressZeroes( parseMaybeAbsentBool( true ) );
4930 break;
4931
4932 default:
4933 std::cerr << "Unknown format token: " << GetTokenString( token ) << std::endl;
4934 Expecting( "prefix, suffix, units, units_format, precision, override_value, "
4935 "suppress_zeroes" );
4936 }
4937 }
4938 break;
4939 }
4940
4941 case T_style:
4942 {
4943 isStyleKnown = true;
4944
4945 // new format: default to keep text aligned off unless token is present
4946 dim->SetKeepTextAligned( false );
4947
4948 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4949 {
4950 switch( token )
4951 {
4952 case T_LEFT:
4953 continue;
4954
4955 case T_thickness:
4956 dim->SetLineThickness( parseBoardUnits( "extension line thickness value" ) );
4957 NeedRIGHT();
4958 break;
4959
4960 case T_arrow_direction:
4961 {
4962 token = NextTok();
4963
4964 if( token == T_inward )
4965 dim->ChangeArrowDirection( DIM_ARROW_DIRECTION::INWARD );
4966 else if( token == T_outward )
4967 dim->ChangeArrowDirection( DIM_ARROW_DIRECTION::OUTWARD );
4968 else
4969 Expecting( "inward or outward" );
4970
4971 NeedRIGHT();
4972 break;
4973 }
4974 case T_arrow_length:
4975
4976 dim->SetArrowLength( parseBoardUnits( "arrow length value" ) );
4977 NeedRIGHT();
4978 break;
4979
4980 case T_text_position_mode:
4981 {
4982 int mode = parseInt( "text position mode" );
4983 mode = std::max( 0, std::min( 3, mode ) );
4984 dim->SetTextPositionMode( static_cast<DIM_TEXT_POSITION>( mode ) );
4985 NeedRIGHT();
4986 break;
4987 }
4988
4989 case T_extension_height:
4990 {
4991 PCB_DIM_ALIGNED* aligned = dynamic_cast<PCB_DIM_ALIGNED*>( dim.get() );
4992 wxCHECK_MSG( aligned, nullptr, wxT( "Invalid extension_height token" ) );
4993 aligned->SetExtensionHeight( parseBoardUnits( "extension height value" ) );
4994 NeedRIGHT();
4995 break;
4996 }
4997
4998 case T_extension_offset:
4999 dim->SetExtensionOffset( parseBoardUnits( "extension offset value" ) );
5000 NeedRIGHT();
5001 break;
5002
5003 case T_keep_text_aligned:
5004 dim->SetKeepTextAligned( parseMaybeAbsentBool( true ) );
5005 break;
5006
5007 case T_text_frame:
5008 {
5009 wxCHECK_MSG( dim->Type() == PCB_DIM_LEADER_T, nullptr,
5010 wxT( "Invalid text_frame token" ) );
5011
5012 PCB_DIM_LEADER* leader = static_cast<PCB_DIM_LEADER*>( dim.get() );
5013
5014 int textFrame = parseInt( "text frame mode" );
5015 textFrame = std::clamp( textFrame, 0, 3 );
5016 leader->SetTextBorder( static_cast<DIM_TEXT_BORDER>( textFrame ));
5017 NeedRIGHT();
5018 break;
5019 }
5020
5021 default:
5022 Expecting( "thickness, arrow_length, arrow_direction, text_position_mode, "
5023 "extension_height, extension_offset" );
5024 }
5025 }
5026
5027 break;
5028 }
5029
5030 // Old format: feature1 stores a feature line. We only care about the origin.
5031 case T_feature1:
5032 {
5033 NeedLEFT();
5034 token = NextTok();
5035
5036 if( token != T_pts )
5037 Expecting( T_pts );
5038
5039 VECTOR2I point;
5040
5041 parseXY( &point.x, &point.y );
5042 dim->SetStart( point );
5043
5044 parseXY( nullptr, nullptr ); // Ignore second point
5045 NeedRIGHT();
5046 NeedRIGHT();
5047 break;
5048 }
5049
5050 // Old format: feature2 stores a feature line. We only care about the end point.
5051 case T_feature2:
5052 {
5053 NeedLEFT();
5054 token = NextTok();
5055
5056 if( token != T_pts )
5057 Expecting( T_pts );
5058
5059 VECTOR2I point;
5060
5061 parseXY( &point.x, &point.y );
5062 dim->SetEnd( point );
5063
5064 parseXY( nullptr, nullptr ); // Ignore second point
5065
5066 NeedRIGHT();
5067 NeedRIGHT();
5068 break;
5069 }
5070
5071 case T_crossbar:
5072 {
5073 NeedLEFT();
5074 token = NextTok();
5075
5076 if( token == T_pts )
5077 {
5078 // If we have a crossbar, we know we're an old aligned dim
5079 PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dim.get() );
5080
5081 // Old style: calculate height from crossbar
5082 VECTOR2I point1, point2;
5083 parseXY( &point1.x, &point1.y );
5084 parseXY( &point2.x, &point2.y );
5085 aligned->UpdateHeight( point2, point1 ); // Yes, backwards intentionally
5086 NeedRIGHT();
5087 }
5088
5089 NeedRIGHT();
5090 break;
5091 }
5092
5093 // Arrow: no longer saved; no-op
5094 case T_arrow1a:
5095 NeedLEFT();
5096 token = NextTok();
5097
5098 if( token != T_pts )
5099 Expecting( T_pts );
5100
5101 parseXY( nullptr, nullptr );
5102 parseXY( nullptr, nullptr );
5103 NeedRIGHT();
5104 NeedRIGHT();
5105 break;
5106
5107 // Arrow: no longer saved; no-op
5108 case T_arrow1b:
5109 NeedLEFT();
5110 token = NextTok();
5111
5112 if( token != T_pts )
5113 Expecting( T_pts );
5114
5115 parseXY( nullptr, nullptr );
5116 parseXY( nullptr, nullptr );
5117 NeedRIGHT();
5118 NeedRIGHT();
5119 break;
5120
5121 // Arrow: no longer saved; no-op
5122 case T_arrow2a:
5123 NeedLEFT();
5124 token = NextTok();
5125
5126 if( token != T_pts )
5127 Expecting( T_pts );
5128
5129 parseXY( nullptr, nullptr );
5130 parseXY( nullptr, nullptr );
5131 NeedRIGHT();
5132 NeedRIGHT();
5133 break;
5134
5135 // Arrow: no longer saved; no-op
5136 case T_arrow2b:
5137 NeedLEFT();
5138 token = NextTok();
5139
5140 if( token != T_pts )
5141 Expecting( T_pts );
5142
5143 parseXY( nullptr, nullptr );
5144 parseXY( nullptr, nullptr );
5145 NeedRIGHT();
5146 NeedRIGHT();
5147 break;
5148
5149 // Handle (locked yes) from modern times
5150 case T_locked:
5151 {
5152 // Unsure if we ever wrote out (locked) for dimensions, so use maybeAbsent just in case
5153 bool isLocked = parseMaybeAbsentBool( true );
5154 dim->SetLocked( isLocked );
5155 break;
5156 }
5157
5158 default:
5159 Expecting( "layer, tstamp, uuid, gr_text, feature1, feature2, crossbar, arrow1a, "
5160 "arrow1b, arrow2a, or arrow2b" );
5161 }
5162 }
5163
5164 if( locked )
5165 dim->SetLocked( true );
5166
5167 dim->Update();
5168
5169 return dim.release();
5170}
5171
5172
5173FOOTPRINT* PCB_IO_KICAD_SEXPR_PARSER::parseFOOTPRINT( wxArrayString* aInitialComments )
5174{
5175 try
5176 {
5177 return parseFOOTPRINT_unchecked( aInitialComments );
5178 }
5179 catch( const PARSE_ERROR& parse_error )
5180 {
5181 if( m_tooRecent )
5182 throw FUTURE_FORMAT_ERROR( parse_error, GetRequiredVersion() );
5183 else
5184 throw;
5185 }
5186}
5187
5188
5190{
5191 wxCHECK_MSG( CurTok() == T_module || CurTok() == T_footprint, nullptr,
5192 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as FOOTPRINT." ) );
5193
5194 wxString name;
5195 VECTOR2I pt;
5196 T token;
5197 LIB_ID fpid;
5198 int attributes = 0;
5199
5200 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
5201
5202 footprint->SetInitialComments( aInitialComments );
5203
5204 if( m_board )
5205 {
5206 footprint->SetStaticComponentClass(
5207 m_board->GetComponentClassManager().GetNoneComponentClass() );
5208 }
5209
5210 token = NextTok();
5211
5212 if( !IsSymbol( token ) && token != T_NUMBER )
5213 Expecting( "symbol|number" );
5214
5215 name = FromUTF8();
5216
5217 if( !name.IsEmpty() && fpid.Parse( name, true ) >= 0 )
5218 {
5219 THROW_IO_ERROR( wxString::Format( _( "Invalid footprint ID in\nfile: %s\nline: %d\n"
5220 "offset: %d." ),
5221 CurSource(), CurLineNumber(), CurOffset() ) );
5222 }
5223
5224 auto checkVersion =
5225 [&]()
5226 {
5228 {
5229 throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
5231 }
5232 };
5233
5234 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5235 {
5236 if( token == T_LEFT )
5237 token = NextTok();
5238
5239 switch( token )
5240 {
5241 case T_version:
5242 {
5243 // Theoretically a footprint nested in a PCB could declare its own version, though
5244 // as of writing this comment we don't do that. Just in case, take the greater
5245 // version.
5246 int this_version = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
5247 NeedRIGHT();
5248 m_requiredVersion = std::max( m_requiredVersion, this_version );
5250 SetKnowsBar( m_requiredVersion >= 20240706 ); // Bar token is known from this version
5251 footprint->SetFileFormatVersionAtLoad( this_version );
5252 break;
5253 }
5254
5255 case T_generator:
5256 // We currently ignore the generator when parsing. It is included in the file for manual
5257 // indication of where the footprint came from.
5258 NeedSYMBOL();
5259 NeedRIGHT();
5260 break;
5261
5262 case T_generator_version:
5263 {
5264 NeedSYMBOL();
5265 m_generatorVersion = FromUTF8();
5266 NeedRIGHT();
5267
5268 // If the format includes a generator version, by this point we have enough info to
5269 // do the version check here
5270 checkVersion();
5271
5272 break;
5273 }
5274
5275 case T_locked:
5276 footprint->SetLocked( parseMaybeAbsentBool( true ) );
5277 break;
5278
5279 case T_placed:
5280 footprint->SetIsPlaced( parseMaybeAbsentBool( true ) );
5281 break;
5282
5283 case T_layer:
5284 {
5285 // Footprints can be only on the front side or the back side.
5286 // but because we can find some stupid layer in file, ensure a
5287 // acceptable layer is set for the footprint
5289 footprint->SetLayer( layer == B_Cu ? B_Cu : F_Cu );
5290 NeedRIGHT();
5291 break;
5292 }
5293
5294 case T_stackup:
5295 {
5296 parseFootprintStackup( *footprint );
5297 break;
5298 }
5299
5300 case T_tedit:
5301 parseHex();
5302 NeedRIGHT();
5303 break;
5304
5305 case T_tstamp:
5306 case T_uuid:
5307 NextTok();
5308 footprint->SetUuidDirect( CurStrToKIID() );
5309 NeedRIGHT();
5310 break;
5311
5312 case T_at:
5313 pt.x = parseBoardUnits( "X coordinate" );
5314 pt.y = parseBoardUnits( "Y coordinate" );
5315 footprint->SetPosition( pt );
5316 token = NextTok();
5317
5318 if( token == T_NUMBER )
5319 {
5320 footprint->SetOrientation( EDA_ANGLE( parseDouble(), DEGREES_T ) );
5321 NeedRIGHT();
5322 }
5323 else if( token != T_RIGHT )
5324 {
5325 Expecting( T_RIGHT );
5326 }
5327
5328 break;
5329
5330 case T_descr:
5331 NeedSYMBOLorNUMBER(); // some symbols can be 0508, so a number is also a symbol here
5332 footprint->SetLibDescription( FromUTF8() );
5333 NeedRIGHT();
5334 break;
5335
5336 case T_tags:
5337 NeedSYMBOLorNUMBER(); // some symbols can be 0508, so a number is also a symbol here
5338 footprint->SetKeywords( FromUTF8() );
5339 NeedRIGHT();
5340 break;
5341
5342 case T_property:
5343 {
5344 NeedSYMBOL();
5345 wxString pName = FromUTF8();
5346 NeedSYMBOL();
5347 wxString pValue = FromUTF8();
5348
5349 // Prior to PCB fields, we used to use properties for special values instead of
5350 // using (keyword_example "value")
5351 if( m_requiredVersion < 20230620 )
5352 {
5353 // Skip legacy non-field properties sent from symbols that should not be kept
5354 // in footprints.
5355 if( pName == "ki_keywords" || pName == "ki_locked" )
5356 {
5357 NeedRIGHT();
5358 break;
5359 }
5360
5361 // Description from symbol (not the fooprint library description stored in (descr) )
5362 // used to be stored as a reserved key value
5363 if( pName == "ki_description" )
5364 {
5365 footprint->GetField( FIELD_T::DESCRIPTION )->SetText( pValue );
5366 NeedRIGHT();
5367 break;
5368 }
5369
5370 // Sheet file and name used to be stored as properties invisible to the user
5371 if( pName == "Sheetfile" || pName == "Sheet file" )
5372 {
5373 footprint->SetSheetfile( pValue );
5374 NeedRIGHT();
5375 break;
5376 }
5377
5378 if( pName == "Sheetname" || pName == "Sheet name" )
5379 {
5380 footprint->SetSheetname( pValue );
5381 NeedRIGHT();
5382 break;
5383 }
5384 }
5385
5386 PCB_FIELD* field = nullptr;
5387 std::unique_ptr<PCB_FIELD> unusedField;
5388
5389 // 8.0.0rc3 had a bug where these properties were mistakenly added to the footprint as
5390 // fields, this will remove them as fields but still correctly set the footprint filters
5391 if( pName == "ki_fp_filters" )
5392 {
5393 footprint->SetFilters( pValue );
5394
5395 // Use the text effect parsing function because it will handle ki_fp_filters as a
5396 // property with no text effects, but will also handle parsing the text effects.
5397 // We just drop the effects if they're present.
5398 unusedField = std::make_unique<PCB_FIELD>( footprint.get(), FIELD_T::USER );
5399 field = unusedField.get();
5400 }
5401 else if( pName == "Footprint" )
5402 {
5403 // Until V9, footprints had a Footprint field that usually (but not always)
5404 // duplicated the footprint's LIB_ID. In V9 this was removed. Parse it
5405 // like any other, but don't add it to anything.
5406 unusedField = std::make_unique<PCB_FIELD>( footprint.get(), FIELD_T::FOOTPRINT );
5407 field = unusedField.get();
5408 }
5409 else if( footprint->HasField( pName ) )
5410 {
5411 field = footprint->GetField( pName );
5412 field->SetText( pValue );
5413 }
5414 else
5415 {
5416 field = new PCB_FIELD( footprint.get(), FIELD_T::USER, pName );
5417 footprint->Add( field );
5418
5419 field->SetText( pValue );
5420 field->SetLayer( footprint->GetLayer() == F_Cu ? F_Fab : B_Fab );
5421
5422 if( m_board ) // can be null when reading a lib
5423 field->StyleFromSettings( m_board->GetDesignSettings(), true );
5424 }
5425
5426 // Hide the field by default if it is a legacy field that did not have
5427 // text effects applied, since hide is a negative effect
5428 if( m_requiredVersion < 20230620 )
5429 field->SetVisible( false );
5430 else
5431 field->SetVisible( true );
5432
5433 parsePCB_TEXT_effects( field );
5434 }
5435 break;
5436
5437 case T_path:
5438 NeedSYMBOLorNUMBER(); // Paths can be numerical so a number is also a symbol here
5439 footprint->SetPath( KIID_PATH( FromUTF8() ) );
5440 NeedRIGHT();
5441 break;
5442
5443 case T_sheetname:
5444 NeedSYMBOL();
5445 footprint->SetSheetname( FromUTF8() );
5446 NeedRIGHT();
5447 break;
5448
5449 case T_sheetfile:
5450 NeedSYMBOL();
5451 footprint->SetSheetfile( FromUTF8() );
5452 NeedRIGHT();
5453 break;
5454
5455 case T_units:
5456 {
5457 std::vector<FOOTPRINT::FP_UNIT_INFO> unitInfos;
5458
5459 // (units (unit (name "A") (pins "1" "2" ...)) ...)
5460 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5461 {
5462 if( token == T_LEFT )
5463 token = NextTok();
5464
5465 if( token == T_unit )
5466 {
5468
5469 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5470 {
5471 if( token == T_LEFT )
5472 token = NextTok();
5473
5474 if( token == T_name )
5475 {
5476 NeedSYMBOLorNUMBER();
5477 info.m_unitName = FromUTF8();
5478 NeedRIGHT();
5479 }
5480 else if( token == T_pins )
5481 {
5482 // Parse a flat list of quoted numbers or symbols until ')'
5483 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5484 {
5485 if( token == T_STRING || token == T_NUMBER )
5486 {
5487 info.m_pins.emplace_back( FromUTF8() );
5488 }
5489 else
5490 {
5491 Expecting( "pin number" );
5492 }
5493 }
5494 }
5495 else
5496 {
5497 // Unknown sub-token inside unit; skip its list if any
5498 skipCurrent();
5499 }
5500 }
5501
5502 unitInfos.push_back( info );
5503 }
5504 else
5505 {
5506 // Unknown entry under units; skip
5507 skipCurrent();
5508 }
5509 }
5510
5511 if( !unitInfos.empty() )
5512 footprint->SetUnitInfo( unitInfos );
5513
5514 break;
5515 }
5516
5517 case T_autoplace_cost90:
5518 case T_autoplace_cost180:
5519 parseInt( "legacy auto-place cost" );
5520 NeedRIGHT();
5521 break;
5522
5523 case T_private_layers:
5524 {
5525 LSET privateLayers;
5526
5527 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5528 {
5529 auto it = m_layerIndices.find( CurStr() );
5530
5531 if( it != m_layerIndices.end() )
5532 privateLayers.set( it->second );
5533 else
5534 Expecting( "layer name" );
5535 }
5536
5537 if( m_requiredVersion < 20220427 )
5538 {
5539 privateLayers.set( Edge_Cuts, false );
5540 privateLayers.set( Margin, false );
5541 }
5542
5543 footprint->SetPrivateLayers( privateLayers );
5544 break;
5545 }
5546
5547 case T_net_tie_pad_groups:
5548 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5549 footprint->AddNetTiePadGroup( CurStr() );
5550
5551 break;
5552
5553 case T_duplicate_pad_numbers_are_jumpers:
5554 footprint->SetDuplicatePadNumbersAreJumpers( parseBool() );
5555 NeedRIGHT();
5556 break;
5557
5558 case T_jumper_pad_groups:
5559 {
5560 // This should only be formatted if there is at least one group
5561 std::vector<std::set<wxString>>& groups = footprint->JumperPadGroups();
5562 std::set<wxString>* currentGroup = nullptr;
5563
5564 for( token = NextTok(); currentGroup || token != T_RIGHT; token = NextTok() )
5565 {
5566 switch( static_cast<int>( token ) )
5567 {
5568 case T_LEFT:
5569 currentGroup = &groups.emplace_back();
5570 break;
5571
5572 case DSN_STRING:
5573 if( currentGroup )
5574 currentGroup->insert( FromUTF8() );
5575
5576 break;
5577
5578 case T_RIGHT:
5579 currentGroup = nullptr;
5580 break;
5581
5582 default:
5583 Expecting( "list of pad names" );
5584 }
5585 }
5586
5587 break;
5588 }
5589
5590 case T_solder_mask_margin:
5591 footprint->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
5592 NeedRIGHT();
5593
5594 // In pre-9.0 files "0" meant inherit.
5595 if( m_requiredVersion <= 20240201 && footprint->GetLocalSolderMaskMargin() == 0 )
5596 footprint->SetLocalSolderMaskMargin( {} );
5597
5598 break;
5599
5600 case T_solder_paste_margin:
5601 footprint->SetLocalSolderPasteMargin( parseBoardUnits( "local solder paste margin value" ) );
5602 NeedRIGHT();
5603
5604 // In pre-9.0 files "0" meant inherit.
5605 if( m_requiredVersion <= 20240201 && footprint->GetLocalSolderPasteMargin() == 0 )
5606 footprint->SetLocalSolderPasteMargin( {} );
5607
5608 break;
5609
5610 case T_solder_paste_ratio: // legacy token
5611 case T_solder_paste_margin_ratio:
5612 footprint->SetLocalSolderPasteMarginRatio( parseDouble( "local solder paste margin ratio value" ) );
5613 NeedRIGHT();
5614
5615 // In pre-9.0 files "0" meant inherit.
5616 if( m_requiredVersion <= 20240201 && footprint->GetLocalSolderPasteMarginRatio() == 0 )
5617 footprint->SetLocalSolderPasteMarginRatio( {} );
5618
5619 break;
5620
5621 case T_clearance:
5622 footprint->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
5623 NeedRIGHT();
5624
5625 // In pre-9.0 files "0" meant inherit.
5626 if( m_requiredVersion <= 20240201 && footprint->GetLocalClearance() == 0 )
5627 footprint->SetLocalClearance( {} );
5628
5629 break;
5630
5631 case T_zone_connect:
5632 footprint->SetLocalZoneConnection((ZONE_CONNECTION) parseInt( "zone connection value" ) );
5633 NeedRIGHT();
5634 break;
5635
5636 case T_thermal_width:
5637 case T_thermal_gap:
5638 // Interestingly, these have never been exposed in the GUI
5639 parseBoardUnits( token );
5640 NeedRIGHT();
5641 break;
5642
5643 case T_attr:
5644 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5645 {
5646 switch( token )
5647 {
5648 case T_virtual: // legacy token prior to version 20200826
5650 break;
5651
5652 case T_through_hole:
5653 attributes |= FP_THROUGH_HOLE;
5654 break;
5655
5656 case T_smd:
5657 attributes |= FP_SMD;
5658 break;
5659
5660 case T_board_only:
5661 attributes |= FP_BOARD_ONLY;
5662 break;
5663
5664 case T_exclude_from_pos_files:
5665 attributes |= FP_EXCLUDE_FROM_POS_FILES;
5666 break;
5667
5668 case T_exclude_from_bom:
5669 attributes |= FP_EXCLUDE_FROM_BOM;
5670 break;
5671
5672 case T_allow_missing_courtyard:
5673 footprint->SetAllowMissingCourtyard( true );
5674 break;
5675
5676 case T_dnp:
5677 attributes |= FP_DNP;
5678 break;
5679
5680 case T_allow_soldermask_bridges:
5681 footprint->SetAllowSolderMaskBridges( true );
5682 break;
5683
5684 default:
5685 Expecting( "through_hole, smd, virtual, board_only, exclude_from_pos_files, "
5686 "exclude_from_bom or allow_solder_mask_bridges" );
5687 }
5688 }
5689 footprint->SetAttributes( attributes );
5690 break;
5691
5692 case T_fp_text:
5693 {
5694 PCB_TEXT* text = parsePCB_TEXT( footprint.get() );
5695
5696 if( PCB_FIELD* field = dynamic_cast<PCB_FIELD*>( text ) )
5697 {
5698 switch( field->GetId() )
5699 {
5700 case FIELD_T::REFERENCE:
5701 footprint->Reference() = PCB_FIELD( *text, FIELD_T::REFERENCE );
5702 footprint->Reference().SetUuidDirect( text->m_Uuid );
5703 delete text;
5704 break;
5705
5706 case FIELD_T::VALUE:
5707 footprint->Value() = PCB_FIELD( *text, FIELD_T::VALUE );
5708 footprint->Value().SetUuidDirect( text->m_Uuid );
5709 delete text;
5710 break;
5711
5712 default:
5713 // Fields other than reference and value aren't treated specially,
5714 // and can be created if the fp_text was hidden on the board,
5715 // so just add those to the footprint as normal.
5716 footprint->Add(text, ADD_MODE::APPEND, true );
5717 break;
5718 }
5719 }
5720 else
5721 {
5722 footprint->Add( text, ADD_MODE::APPEND, true );
5723 }
5724
5725 break;
5726 }
5727
5728 case T_fp_text_box:
5729 {
5730 PCB_TEXTBOX* textbox = parsePCB_TEXTBOX( footprint.get() );
5731 footprint->Add( textbox, ADD_MODE::APPEND, true );
5732 break;
5733 }
5734
5735 case T_table:
5736 {
5737 PCB_TABLE* table = parsePCB_TABLE( footprint.get() );
5738 footprint->Add( table, ADD_MODE::APPEND, true );
5739 break;
5740 }
5741
5742 case T_fp_arc:
5743 case T_fp_circle:
5744 case T_fp_curve:
5745 case T_fp_rect:
5746 case T_fp_line:
5747 case T_fp_poly:
5748 case T_fp_ellipse:
5749 case T_fp_ellipse_arc:
5750 {
5751 PCB_SHAPE* shape = parsePCB_SHAPE( footprint.get() );
5752 footprint->Add( shape, ADD_MODE::APPEND, true );
5753 break;
5754 }
5755
5756 case T_image:
5757 {
5759 footprint->Add( image, ADD_MODE::APPEND, true );
5760 break;
5761 }
5762
5763 case T_barcode:
5764 {
5765 PCB_BARCODE* barcode = parsePCB_BARCODE( footprint.get() );
5766 footprint->Add( barcode, ADD_MODE::APPEND, true );
5767 break;
5768 }
5769
5770 case T_dimension:
5771 {
5772 PCB_DIMENSION_BASE* dimension = parseDIMENSION( footprint.get() );
5773 footprint->Add( dimension, ADD_MODE::APPEND, true );
5774 break;
5775 }
5776
5777 case T_pad:
5778 {
5779 PAD* pad = parsePAD( footprint.get() );
5780 footprint->Add( pad, ADD_MODE::APPEND, true );
5781 break;
5782 }
5783
5784 case T_model:
5785 {
5786 token = NextTok();
5787
5788 if( token == T_LEFT )
5789 {
5790 // Typed model (model (type extruded) ...)
5791 token = NextTok();
5792
5793 if( token != T_type )
5794 Expecting( T_type );
5795
5796 NeedSYMBOL();
5797
5798 if( CurTok() == T_extruded )
5799 {
5800 NeedRIGHT(); // close (type extruded)
5801
5802 EXTRUDED_3D_BODY& body = footprint->EnsureExtrudedBody();
5803
5804 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5805 {
5806 if( token != T_LEFT )
5807 Expecting( T_LEFT );
5808
5809 token = NextTok();
5810
5811 switch( token )
5812 {
5813 case T_hide:
5814 {
5815 bool hide = parseMaybeAbsentBool( true );
5816 body.m_show = !hide;
5817 break;
5818 }
5819
5820 case T_overall_height:
5821 body.m_height = parseBoardUnits( "overall height" );
5822 NeedRIGHT();
5823 break;
5824
5825 case T_body_pcb_gap:
5826 body.m_standoff = parseBoardUnits( "body pcb gap" );
5827 NeedRIGHT();
5828 break;
5829
5830 case T_layer:
5831 {
5832 NeedSYMBOL();
5833 wxString layerName = From_UTF8( CurText() );
5834
5835 if( layerName == wxT( "auto" ) )
5836 {
5837 body.m_layer = UNDEFINED_LAYER;
5838 }
5839 else if( layerName == wxT( "pad_bbox" ) )
5840 {
5842 }
5843 else
5844 {
5845 int layer = LSET::NameToLayer( layerName );
5846
5847 if( layer >= 0 )
5848 body.m_layer = static_cast<PCB_LAYER_ID>( layer );
5849 }
5850
5851 NeedRIGHT();
5852 break;
5853 }
5854
5855 case T_material:
5856 {
5857 NeedSYMBOL();
5858 wxString matName = From_UTF8( CurText() );
5859
5860 if( matName == wxT( "matte" ) )
5862 else if( matName == wxT( "metal" ) )
5864 else if( matName == wxT( "copper" ) )
5866 else
5868
5869 NeedRIGHT();
5870 break;
5871 }
5872
5873 case T_color:
5874 {
5875 NeedSYMBOLorNUMBER();
5876 wxString first = From_UTF8( CurText() );
5877
5878 if( first == wxT( "unspecified" ) )
5879 {
5881 }
5882 else
5883 {
5884 body.m_color.r = parseDouble();
5885 body.m_color.g = parseDouble( "green" );
5886 body.m_color.b = parseDouble( "blue" );
5887 body.m_color.a = parseDouble( "alpha" );
5888 }
5889
5890 NeedRIGHT();
5891 break;
5892 }
5893
5894 case T_offset:
5895 NeedLEFT();
5896 token = NextTok();
5897
5898 if( token != T_xyz )
5899 Expecting( T_xyz );
5900
5901 body.m_offset.x = parseDouble( "x value" );
5902 body.m_offset.y = parseDouble( "y value" );
5903 body.m_offset.z = parseDouble( "z value" );
5904 NeedRIGHT();
5905 NeedRIGHT();
5906 break;
5907
5908 case T_scale:
5909 NeedLEFT();
5910 token = NextTok();
5911
5912 if( token != T_xyz )
5913 Expecting( T_xyz );
5914
5915 body.m_scale.x = parseDouble( "x value" );
5916 body.m_scale.y = parseDouble( "y value" );
5917 body.m_scale.z = parseDouble( "z value" );
5918 NeedRIGHT();
5919 NeedRIGHT();
5920 break;
5921
5922 case T_rotate:
5923 NeedLEFT();
5924 token = NextTok();
5925
5926 if( token != T_xyz )
5927 Expecting( T_xyz );
5928
5929 body.m_rotation.x = parseDouble( "x value" );
5930 body.m_rotation.y = parseDouble( "y value" );
5931 body.m_rotation.z = parseDouble( "z value" );
5932 NeedRIGHT();
5933 NeedRIGHT();
5934 break;
5935
5936 default:
5937 Expecting( "hide, overall_height, body_pcb_gap, layer, material, "
5938 "color, offset, scale, or rotate" );
5939 }
5940 }
5941 }
5942 else
5943 {
5944 Expecting( "extruded" );
5945 }
5946 }
5947 else
5948 {
5949 // Reference model (model "filename" ...)
5950 FP_3DMODEL* model = parse3DModel( true );
5951 footprint->Add3DModel( model );
5952 delete model;
5953 }
5954
5955 break;
5956 }
5957
5958 case T_zone:
5959 {
5960 ZONE* zone = parseZONE( footprint.get() );
5961
5962 if( zone->GetNumCorners() == 0 )
5963 {
5964 delete zone;
5965 break;
5966 }
5967
5968 footprint->Add( zone, ADD_MODE::APPEND, true );
5969 break;
5970 }
5971
5972 case T_group:
5973 parseGROUP( footprint.get() );
5974 break;
5975
5976 case T_point:
5977 {
5978 PCB_POINT* point = parsePCB_POINT();
5979 footprint->Add( point, ADD_MODE::APPEND, true );
5980 break;
5981 }
5982 case T_embedded_fonts:
5983 {
5984 footprint->GetEmbeddedFiles()->SetAreFontsEmbedded( parseBool() );
5985 NeedRIGHT();
5986 break;
5987 }
5988
5989 case T_embedded_files:
5990 {
5991 EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
5992 embeddedFilesParser.SyncLineReaderWith( *this );
5993
5994 try
5995 {
5996 embeddedFilesParser.ParseEmbedded( footprint->GetEmbeddedFiles() );
5997 }
5998 catch( const PARSE_ERROR& e )
5999 {
6000 m_parseWarnings.push_back( e.What() );
6001
6002 int depth = 0;
6003
6004 for( int tok = embeddedFilesParser.NextTok();
6005 tok != DSN_EOF;
6006 tok = embeddedFilesParser.NextTok() )
6007 {
6008 if( tok == DSN_LEFT )
6009 depth++;
6010 else if( tok == DSN_RIGHT && --depth < 0 )
6011 break;
6012 }
6013 }
6014
6015 SyncLineReaderWith( embeddedFilesParser );
6016 break;
6017 }
6018
6019 case T_component_classes:
6020 {
6021 std::unordered_set<wxString> componentClassNames;
6022
6023 while( ( token = NextTok() ) != T_RIGHT )
6024 {
6025 if( token != T_LEFT )
6026 Expecting( T_LEFT );
6027
6028 if( ( token = NextTok() ) != T_class )
6029 Expecting( T_class );
6030
6031 NeedSYMBOLorNUMBER();
6032 componentClassNames.insert( From_UTF8( CurText() ) );
6033 NeedRIGHT();
6034 }
6035
6036 footprint->SetTransientComponentClassNames( componentClassNames );
6037
6038 if( m_board )
6039 footprint->ResolveComponentClassNames( m_board, componentClassNames );
6040
6041 break;
6042 }
6043
6044 case T_variant:
6045 parseFootprintVariant( footprint.get() );
6046 break;
6047
6048 default:
6049 Expecting( "at, descr, locked, placed, tedit, tstamp, uuid, variant, "
6050 "autoplace_cost90, autoplace_cost180, attr, clearance, "
6051 "embedded_files, fp_arc, fp_circle, fp_curve, fp_line, fp_poly, "
6052 "fp_rect, fp_text, pad, group, generator, model, path, solder_mask_margin, "
6053 "solder_paste_margin, solder_paste_margin_ratio, tags, thermal_gap, "
6054 "version, zone, zone_connect, or component_classes" );
6055 }
6056 }
6057
6058 footprint->FixUpPadsForBoard( m_board );
6059
6060 // In legacy files the lack of attributes indicated a through-hole component which was by
6061 // default excluded from pos files. However there was a hack to look for SMD pads and
6062 // consider those "mislabeled through-hole components" and therefore include them in place
6063 // files. We probably don't want to get into that game so we'll just include them by
6064 // default and let the user change it if required.
6065 if( m_requiredVersion < 20200826 && attributes == 0 )
6066 attributes |= FP_THROUGH_HOLE;
6067
6069 {
6070 if( footprint->GetKeywords().StartsWith( wxT( "net tie" ) ) )
6071 {
6072 wxString padGroup;
6073
6074 for( PAD* pad : footprint->Pads() )
6075 {
6076 if( !padGroup.IsEmpty() )
6077 padGroup += wxS( ", " );
6078
6079 padGroup += pad->GetNumber();
6080 }
6081
6082 if( !padGroup.IsEmpty() )
6083 footprint->AddNetTiePadGroup( padGroup );
6084 }
6085 }
6086
6087 footprint->SetAttributes( attributes );
6088
6089 footprint->SetFPID( fpid );
6090
6091 return footprint.release();
6092}
6093
6094
6096{
6097 wxCHECK_RET( CurTok() == T_stackup, "Expected stackup token" );
6098
6099 // If we have a stackup list at all, we must be in custom layer mode
6101 LSET layers = LSET{};
6102
6103 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
6104 {
6105 if( CurTok() != T_LEFT )
6106 Expecting( T_LEFT );
6107
6108 token = NextTok();
6109
6110 switch( token )
6111 {
6112 case T_layer:
6113 {
6114 NeedSYMBOLorNUMBER();
6115
6116 const auto it = m_layerIndices.find( CurStr() );
6117 if( it == m_layerIndices.end() )
6118 {
6119 Expecting( "layer name" );
6120 }
6121 else
6122 {
6123 layers.set( it->second );
6124 }
6125
6126 NeedRIGHT();
6127 break;
6128 }
6129 default:
6130 {
6131 Expecting( "layer" );
6132 break;
6133 }
6134 }
6135 }
6136
6137 // Check that the copper layers are sensible and contiguous
6138 const LSET gotCuLayers = layers & LSET::AllCuMask();
6139
6140 // Remove this check when we support odd copper layer stackups
6141 if( gotCuLayers.count() % 2 != 0 )
6142 {
6143 THROW_IO_ERROR( wxString::Format( _( "Invalid stackup in footprint: "
6144 "odd number of copper layers (%d)." ),
6145 gotCuLayers.count() ) );
6146 }
6147
6148 const LSET expectedCuLayers = LSET::AllCuMask( gotCuLayers.count() );
6149 if( gotCuLayers != expectedCuLayers )
6150 {
6151 THROW_IO_ERROR( wxString::Format( _( "Invalid stackup in footprint: "
6152 "copper layers are not contiguous." ) ) );
6153 }
6154
6155 if( ( layers & LSET::AllTechMask() ).count() > 0 )
6156 {
6157 THROW_IO_ERROR( wxString::Format( _( "Invalid stackup in footprint: "
6158 "technology layers are implicit in footprints and "
6159 "should not be specified in the stackup." ) ) );
6160 }
6161
6162 // Set the mode first, so that the layer count is unlocked if needed
6163 aFootprint.SetStackupMode( stackupMode );
6164 aFootprint.SetStackupLayers( std::move( layers ) );
6165}
6166
6167
6169{
6170 wxCHECK_MSG( CurTok() == T_pad, nullptr,
6171 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PAD." ) );
6172
6173 VECTOR2I sz;
6174 VECTOR2I pt;
6175 bool foundNet = false;
6176 bool foundNetcode = false;
6177
6178 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aParent );
6179
6180 NeedSYMBOLorNUMBER();
6181 pad->SetNumber( FromUTF8() );
6182
6183 T token = NextTok();
6184
6185 switch( token )
6186 {
6187 case T_thru_hole:
6188 pad->SetAttribute( PAD_ATTRIB::PTH );
6189
6190 // The drill token is usually missing if 0 drill size is specified.
6191 // Emulate it using 1 nm drill size to avoid errors.
6192 // Drill size cannot be set to 0 in newer versions.
6193 pad->SetDrillSize( VECTOR2I( 1, 1 ) );
6194 break;
6195
6196 case T_smd:
6197 pad->SetAttribute( PAD_ATTRIB::SMD );
6198
6199 // Default PAD object is thru hole with drill.
6200 // SMD pads have no hole
6201 pad->SetDrillSize( VECTOR2I( 0, 0 ) );
6202 break;
6203
6204 case T_connect:
6205 pad->SetAttribute( PAD_ATTRIB::CONN );
6206
6207 // Default PAD object is thru hole with drill.
6208 // CONN pads have no hole
6209 pad->SetDrillSize( VECTOR2I( 0, 0 ) );
6210 break;
6211
6212 case T_np_thru_hole:
6213 pad->SetAttribute( PAD_ATTRIB::NPTH );
6214 break;
6215
6216 default:
6217 Expecting( "thru_hole, smd, connect, or np_thru_hole" );
6218 }
6219
6220 token = NextTok();
6221
6222 switch( token )
6223 {
6224 case T_circle:
6226 break;
6227
6228 case T_rect:
6230 break;
6231
6232 case T_oval:
6234 break;
6235
6236 case T_trapezoid:
6238 break;
6239
6240 case T_roundrect:
6241 // Note: the shape can be PAD_SHAPE::ROUNDRECT or PAD_SHAPE::CHAMFERED_RECT
6242 // (if chamfer parameters are found later in pad descr.)
6244 break;
6245
6246 case T_custom:
6248 break;
6249
6250 default:
6251 Expecting( "circle, rectangle, roundrect, oval, trapezoid or custom" );
6252 }
6253
6254 std::optional<EDA_ANGLE> thermalBrAngleOverride;
6255
6256 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6257 {
6258 if( token == T_locked )
6259 {
6260 // Pad locking is now a session preference
6261 token = NextTok();
6262 }
6263
6264 if( token != T_LEFT )
6265 Expecting( T_LEFT );
6266
6267 token = NextTok();
6268
6269 switch( token )
6270 {
6271 case T_size:
6272 sz.x = parseBoardUnits( "width value" );
6273 sz.y = parseBoardUnits( "height value" );
6274 pad->SetSize( PADSTACK::ALL_LAYERS, sz );
6275 NeedRIGHT();
6276 break;
6277
6278 case T_at:
6279 pt.x = parseBoardUnits( "X coordinate" );
6280 pt.y = parseBoardUnits( "Y coordinate" );
6281 pad->SetFPRelativePosition( pt );
6282 token = NextTok();
6283
6284 if( token == T_NUMBER )
6285 {
6286 pad->SetOrientation( EDA_ANGLE( parseDouble(), DEGREES_T ) );
6287 NeedRIGHT();
6288 }
6289 else if( token != T_RIGHT )
6290 {
6291 Expecting( ") or angle value" );
6292 }
6293
6294 break;
6295
6296 case T_rect_delta:
6297 {
6299 delta.x = parseBoardUnits( "rectangle delta width" );
6300 delta.y = parseBoardUnits( "rectangle delta height" );
6301 pad->SetDelta( PADSTACK::ALL_LAYERS, delta );
6302 NeedRIGHT();
6303 break;
6304 }
6305
6306 case T_drill:
6307 {
6308 bool haveWidth = false;
6309 VECTOR2I drillSize = pad->GetDrillSize();
6310
6311 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6312 {
6313 if( token == T_LEFT )
6314 token = NextTok();
6315
6316 switch( token )
6317 {
6318 case T_oval: pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG ); break;
6319
6320 case T_NUMBER:
6321 {
6322 if( !haveWidth )
6323 {
6324 drillSize.x = parseBoardUnits();
6325
6326 // If height is not defined the width and height are the same.
6327 drillSize.y = drillSize.x;
6328 haveWidth = true;
6329 }
6330 else
6331 {
6332 drillSize.y = parseBoardUnits();
6333 }
6334 }
6335
6336 break;
6337
6338 case T_offset:
6339 pt.x = parseBoardUnits( "drill offset x" );
6340 pt.y = parseBoardUnits( "drill offset y" );
6341 pad->SetOffset( PADSTACK::ALL_LAYERS, pt );
6342 NeedRIGHT();
6343 break;
6344
6345 default:
6346 Expecting( "oval, size, or offset" );
6347 }
6348 }
6349
6350 // This fixes a bug caused by setting the default PAD drill size to a value other
6351 // than 0 used to fix a bunch of debug assertions even though it is defined as a
6352 // through hole pad. Wouldn't a though hole pad with no drill be a surface mount
6353 // pad (or a conn pad which is a smd pad with no solder paste)?
6354 if( pad->GetAttribute() != PAD_ATTRIB::SMD && pad->GetAttribute() != PAD_ATTRIB::CONN )
6355 pad->SetDrillSize( drillSize );
6356 else
6357 pad->SetDrillSize( VECTOR2I( 0, 0 ) );
6358
6359 break;
6360 }
6361
6362 case T_backdrill:
6363 {
6364 // Parse: (backdrill (size ...) (layers start end))
6365 PADSTACK::DRILL_PROPS& secondary = pad->Padstack().SecondaryDrill();
6366
6367 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6368 {
6369 if( token != T_LEFT )
6370 Expecting( T_LEFT );
6371
6372 token = NextTok();
6373
6374 switch( token )
6375 {
6376 case T_size:
6377 {
6378 int size = parseBoardUnits( "backdrill size" );
6379 secondary.size = VECTOR2I( size, size );
6380 NeedRIGHT();
6381 break;
6382 }
6383
6384 case T_layers:
6385 {
6386 NextTok();
6387 secondary.start = lookUpLayer( m_layerIndices );
6388 NextTok();
6389 secondary.end = lookUpLayer( m_layerIndices );
6390 NeedRIGHT();
6391 break;
6392 }
6393
6394 default:
6395 Expecting( "size or layers" );
6396 }
6397 }
6398
6399 break;
6400 }
6401
6402 case T_tertiary_drill:
6403 {
6404 // Parse: (tertiary_drill (size ...) (layers start end))
6405 PADSTACK::DRILL_PROPS& tertiary = pad->Padstack().TertiaryDrill();
6406
6407 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6408 {
6409 if( token != T_LEFT )
6410 Expecting( T_LEFT );
6411
6412 token = NextTok();
6413
6414 switch( token )
6415 {
6416 case T_size:
6417 {
6418 int size = parseBoardUnits( "tertiary drill size" );
6419 tertiary.size = VECTOR2I( size, size );
6420 NeedRIGHT();
6421 break;
6422 }
6423
6424 case T_layers:
6425 {
6426 NextTok();
6427 tertiary.start = lookUpLayer( m_layerIndices );
6428 NextTok();
6429 tertiary.end = lookUpLayer( m_layerIndices );
6430 NeedRIGHT();
6431 break;
6432 }
6433
6434 default:
6435 Expecting( "size or layers" );
6436 }
6437 }
6438
6439 break;
6440 }
6441
6442 case T_layers:
6443 {
6444 LSET layerMask = parseBoardItemLayersAsMask();
6445
6446 pad->SetLayerSet( layerMask );
6447 break;
6448 }
6449
6450 case T_net:
6451 foundNet = true;
6452
6453 token = NextTok();
6454
6455 // Legacy files (pre-10.0) will have a netcode written before the netname. This netcode
6456 // is authoratative (though may be mapped by getNetCode() to prevent collisions).
6457 if( IsNumber( token ) )
6458 {
6459 if( !pad->SetNetCode( getNetCode( parseInt() ), /* aNoAssert */ true ) )
6460 {
6461 wxLogTrace( traceKicadPcbPlugin,
6462 _( "Invalid net ID in\nfile: %s\nline: %d offset: %d" ),
6463 CurSource(), CurLineNumber(), CurOffset() );
6464 }
6465 else
6466 {
6467 foundNetcode = true;
6468 }
6469
6470 token = NextTok();
6471 }
6472
6473 if( !IsSymbol( token ) )
6474 {
6475 Expecting( "net name" );
6476 break;
6477 }
6478
6479 if( m_board )
6480 {
6481 wxString netName( FromUTF8() );
6482
6483 // Convert overbar syntax from `~...~` to `~{...}`. These were left out of the
6484 // first merge so the version is a bit later.
6485 if( m_requiredVersion < 20210606 )
6486 netName = ConvertToNewOverbarNotation( netName );
6487
6488 if( foundNetcode )
6489 {
6490 if( netName != m_board->FindNet( pad->GetNetCode() )->GetNetname() )
6491 {
6492 pad->SetNetCode( NETINFO_LIST::ORPHANED, /* aNoAssert */ true );
6493 wxLogTrace( traceKicadPcbPlugin,
6494 _( "Net name doesn't match ID in\nfile: %s\nline: %d offset: %d" ),
6495 CurSource(), CurLineNumber(), CurOffset() );
6496 }
6497 }
6498 else
6499 {
6500 NETINFO_ITEM* netinfo = m_board->FindNet( netName );
6501
6502 if( !netinfo )
6503 {
6504 netinfo = new NETINFO_ITEM( m_board, netName );
6505 m_board->Add( netinfo, ADD_MODE::INSERT, true );
6506 }
6507
6508 pad->SetNet( netinfo );
6509 }
6510 }
6511
6512 NeedRIGHT();
6513 break;
6514
6515 case T_pinfunction:
6516 NeedSYMBOLorNUMBER();
6517 pad->SetPinFunction( FromUTF8() );
6518 NeedRIGHT();
6519 break;
6520
6521 case T_pintype:
6522 NeedSYMBOLorNUMBER();
6523 pad->SetPinType( FromUTF8() );
6524 NeedRIGHT();
6525 break;
6526
6527 case T_die_length:
6528 pad->SetPadToDieLength( parseBoardUnits( T_die_length ) );
6529 NeedRIGHT();
6530 break;
6531
6532 case T_die_delay:
6533 {
6534 if( m_requiredVersion <= 20250926 )
6535 pad->SetPadToDieDelay( parseBoardUnits( T_die_delay ) );
6536 else
6537 pad->SetPadToDieDelay( parseBoardUnits( T_die_delay, EDA_DATA_TYPE::TIME ) );
6538
6539 NeedRIGHT();
6540 break;
6541 }
6542
6543 case T_solder_mask_margin:
6544 pad->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
6545 NeedRIGHT();
6546
6547 // In pre-9.0 files "0" meant inherit.
6548 if( m_requiredVersion <= 20240201 && pad->GetLocalSolderMaskMargin() == 0 )
6549 pad->SetLocalSolderMaskMargin( {} );
6550
6551 break;
6552
6553 case T_solder_paste_margin:
6554 pad->SetLocalSolderPasteMargin( parseBoardUnits( "local solder paste margin value" ) );
6555 NeedRIGHT();
6556
6557 // In pre-9.0 files "0" meant inherit.
6558 if( m_requiredVersion <= 20240201 && pad->GetLocalSolderPasteMargin() == 0 )
6559 pad->SetLocalSolderPasteMargin( {} );
6560
6561 break;
6562
6563 case T_solder_paste_margin_ratio:
6564 pad->SetLocalSolderPasteMarginRatio( parseDouble( "local solder paste margin ratio value" ) );
6565 NeedRIGHT();
6566
6567 // In pre-9.0 files "0" meant inherit.
6568 if( m_requiredVersion <= 20240201 && pad->GetLocalSolderPasteMarginRatio() == 0 )
6569 pad->SetLocalSolderPasteMarginRatio( {} );
6570
6571 break;
6572
6573 case T_clearance:
6574 pad->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
6575 NeedRIGHT();
6576
6577 // In pre-9.0 files "0" meant inherit.
6578 if( m_requiredVersion <= 20240201 && pad->GetLocalClearance() == 0 )
6579 pad->SetLocalClearance( {} );
6580
6581 break;
6582
6583 case T_teardrops:
6584 parseTEARDROP_PARAMETERS( &pad->GetTeardropParams() );
6585 break;
6586
6587 case T_zone_connect:
6588 pad->SetLocalZoneConnection( (ZONE_CONNECTION) parseInt( "zone connection value" ) );
6589 NeedRIGHT();
6590 break;
6591
6592 case T_thermal_width: // legacy token
6593 case T_thermal_bridge_width:
6594 pad->SetLocalThermalSpokeWidthOverride( parseBoardUnits( token ) );
6595 NeedRIGHT();
6596 break;
6597
6598 case T_thermal_bridge_angle:
6599 thermalBrAngleOverride = EDA_ANGLE( parseDouble( "thermal spoke angle" ), DEGREES_T );
6600 NeedRIGHT();
6601 break;
6602
6603
6604 case T_thermal_gap:
6605 pad->SetThermalGap( parseBoardUnits( "thermal relief gap value" ) );
6606 NeedRIGHT();
6607 break;
6608
6609 case T_roundrect_rratio:
6610 pad->SetRoundRectRadiusRatio( PADSTACK::ALL_LAYERS,
6611 parseDouble( "roundrect radius ratio" ) );
6612 NeedRIGHT();
6613 break;
6614
6615 case T_chamfer_ratio:
6616 pad->SetChamferRectRatio( PADSTACK::ALL_LAYERS, parseDouble( "chamfer ratio" ) );
6617
6618 if( pad->GetChamferRectRatio( PADSTACK::ALL_LAYERS ) > 0 )
6620
6621 NeedRIGHT();
6622 break;
6623
6624 case T_chamfer:
6625 {
6626 int chamfers = 0;
6627 bool end_list = false;
6628
6629 while( !end_list )
6630 {
6631 token = NextTok();
6632
6633 switch( token )
6634 {
6635 case T_top_left:
6636 chamfers |= RECT_CHAMFER_TOP_LEFT;
6637 break;
6638
6639 case T_top_right:
6640 chamfers |= RECT_CHAMFER_TOP_RIGHT;
6641 break;
6642
6643 case T_bottom_left:
6644 chamfers |= RECT_CHAMFER_BOTTOM_LEFT;
6645 break;
6646
6647 case T_bottom_right:
6648 chamfers |= RECT_CHAMFER_BOTTOM_RIGHT;
6649 break;
6650
6651 case T_RIGHT:
6652 pad->SetChamferPositions( PADSTACK::ALL_LAYERS, chamfers );
6653 end_list = true;
6654 break;
6655
6656 default:
6657 Expecting( "chamfer_top_left chamfer_top_right chamfer_bottom_left or "
6658 "chamfer_bottom_right" );
6659 }
6660 }
6661
6662 if( pad->GetChamferPositions( PADSTACK::ALL_LAYERS ) != RECT_NO_CHAMFER )
6664
6665 break;
6666 }
6667
6668 case T_property:
6669 while( token != T_RIGHT )
6670 {
6671 token = NextTok();
6672
6673 switch( token )
6674 {
6675 case T_pad_prop_bga: pad->SetProperty( PAD_PROP::BGA ); break;
6676 case T_pad_prop_fiducial_glob: pad->SetProperty( PAD_PROP::FIDUCIAL_GLBL ); break;
6677 case T_pad_prop_fiducial_loc: pad->SetProperty( PAD_PROP::FIDUCIAL_LOCAL ); break;
6678 case T_pad_prop_testpoint: pad->SetProperty( PAD_PROP::TESTPOINT ); break;
6679 case T_pad_prop_castellated: pad->SetProperty( PAD_PROP::CASTELLATED ); break;
6680 case T_pad_prop_heatsink: pad->SetProperty( PAD_PROP::HEATSINK ); break;
6681 case T_pad_prop_mechanical: pad->SetProperty( PAD_PROP::MECHANICAL ); break;
6682 case T_pad_prop_pressfit: pad->SetProperty( PAD_PROP::PRESSFIT ); break;
6683 case T_none: pad->SetProperty( PAD_PROP::NONE ); break;
6684 case T_RIGHT: break;
6685
6686 default:
6687#if 0 // Currently: skip unknown property
6688 Expecting( "pad_prop_bga pad_prop_fiducial_glob pad_prop_fiducial_loc"
6689 " pad_prop_heatsink or pad_prop_castellated" );
6690#endif
6691 break;
6692 }
6693 }
6694
6695 break;
6696
6697 case T_options:
6698 parsePAD_option( pad.get() );
6699 break;
6700
6701 case T_padstack:
6702 parsePadstack( pad.get() );
6703 break;
6704
6705 case T_primitives:
6706 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6707 {
6708 if( token == T_LEFT )
6709 token = NextTok();
6710
6711 switch( token )
6712 {
6713 case T_gr_arc:
6714 case T_gr_line:
6715 case T_gr_circle:
6716 case T_gr_rect:
6717 case T_gr_poly:
6718 case T_gr_curve:
6719 pad->AddPrimitive( PADSTACK::ALL_LAYERS, parsePCB_SHAPE( nullptr ) );
6720 break;
6721
6722 case T_gr_bbox:
6723 {
6724 PCB_SHAPE* numberBox = parsePCB_SHAPE( nullptr );
6725 numberBox->SetIsProxyItem();
6726 pad->AddPrimitive( PADSTACK::ALL_LAYERS, numberBox );
6727 break;
6728 }
6729
6730 case T_gr_vector:
6731 {
6732 PCB_SHAPE* spokeTemplate = parsePCB_SHAPE( nullptr );
6733 spokeTemplate->SetIsProxyItem();
6734 pad->AddPrimitive( PADSTACK::ALL_LAYERS, spokeTemplate );
6735 break;
6736 }
6737
6738 default:
6739 Expecting( "gr_line, gr_arc, gr_circle, gr_curve, gr_rect, gr_bbox or gr_poly" );
6740 break;
6741 }
6742 }
6743
6744 break;
6745
6746 case T_remove_unused_layers:
6747 {
6748 bool remove = parseMaybeAbsentBool( true );
6749 pad->SetRemoveUnconnected( remove );
6750 break;
6751 }
6752
6753 case T_keep_end_layers:
6754 {
6755 bool keep = parseMaybeAbsentBool( true );
6756 pad->SetKeepTopBottom( keep );
6757 break;
6758 }
6759
6760 case T_tenting:
6761 {
6762 auto [front, back] = parseFrontBackOptBool( true );
6763 pad->Padstack().FrontOuterLayers().has_solder_mask = front;
6764 pad->Padstack().BackOuterLayers().has_solder_mask = back;
6765 break;
6766 }
6767
6768 case T_zone_layer_connections:
6769 {
6770 LSET cuLayers = pad->GetLayerSet() & LSET::AllCuMask();
6771
6772 for( PCB_LAYER_ID layer : cuLayers )
6773 pad->SetZoneLayerOverride( layer, ZLO_FORCE_NO_ZONE_CONNECTION );
6774
6775 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6776 {
6778
6779 if( !IsCopperLayer( layer ) )
6780 Expecting( "copper layer name" );
6781
6782 pad->SetZoneLayerOverride( layer, ZLO_FORCE_FLASHED );
6783 }
6784
6785 break;
6786 }
6787
6788 // Continue to process "(locked)" format which was output during 5.99 development
6789 case T_locked:
6790 // Pad locking is now a session preference
6791 parseMaybeAbsentBool( true );
6792 break;
6793
6794 case T_tstamp:
6795 case T_uuid:
6796 NextTok();
6797 pad->SetUuidDirect( CurStrToKIID() );
6798 NeedRIGHT();
6799 break;
6800
6801 case T_front_post_machining:
6802 parsePostMachining( pad->Padstack().FrontPostMachining() );
6803 break;
6804
6805 case T_back_post_machining:
6806 parsePostMachining( pad->Padstack().BackPostMachining() );
6807 break;
6808
6809 default:
6810 Expecting( "at, locked, drill, layers, net, die_length, roundrect_rratio, "
6811 "solder_mask_margin, solder_paste_margin, solder_paste_margin_ratio, uuid, "
6812 "clearance, tstamp, primitives, remove_unused_layers, keep_end_layers, "
6813 "pinfunction, pintype, zone_connect, thermal_width, thermal_gap, padstack, "
6814 "teardrops, front_post_machining, or back_post_machining" );
6815 }
6816 }
6817
6818 if( !foundNet )
6819 {
6820 // Make sure default netclass is correctly assigned to pads that don't define a net.
6821 pad->SetNetCode( 0, /* aNoAssert */ true );
6822 }
6823
6824 if( thermalBrAngleOverride )
6825 {
6826 pad->SetThermalSpokeAngle( *thermalBrAngleOverride );
6827 }
6828 else
6829 {
6830 // This is here because custom pad anchor shape isn't known before reading (options
6831 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE )
6832 {
6833 pad->SetThermalSpokeAngle( ANGLE_45 );
6834 }
6835 else if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CUSTOM
6836 && pad->GetAnchorPadShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE )
6837 {
6838 if( m_requiredVersion <= 20211014 ) // 6.0
6839 pad->SetThermalSpokeAngle( ANGLE_90 );
6840 else
6841 pad->SetThermalSpokeAngle( ANGLE_45 );
6842 }
6843 else
6844 {
6845 pad->SetThermalSpokeAngle( ANGLE_90 );
6846 }
6847 }
6848
6849 if( !pad->CanHaveNumber() )
6850 {
6851 // At some point it was possible to assign a number to aperture pads so we need to clean
6852 // those out here.
6853 pad->SetNumber( wxEmptyString );
6854 }
6855
6856 // Zero-sized pads are likely algorithmically unsafe.
6857 if( pad->GetSizeX() <= 0 || pad->GetSizeY() <= 0 )
6858 {
6859 pad->SetSize( PADSTACK::ALL_LAYERS,
6860 VECTOR2I( pcbIUScale.mmToIU( 0.001 ), pcbIUScale.mmToIU( 0.001 ) ) );
6861
6862 m_parseWarnings.push_back(
6863 wxString::Format( _( "Invalid zero-sized pad pinned to %s in\nfile: %s\nline: %d\noffset: %d" ),
6864 wxT( "1µm" ), CurSource(), CurLineNumber(), CurOffset() ) );
6865 }
6866
6867 return pad.release();
6868}
6869
6870
6872{
6873 // Parse only the (option ...) inside a pad description
6874 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
6875 {
6876 if( token != T_LEFT )
6877 Expecting( T_LEFT );
6878
6879 token = NextTok();
6880
6881 switch( token )
6882 {
6883 case T_anchor:
6884 token = NextTok();
6885 // Custom shaped pads have a "anchor pad", which is the reference for connection calculations.
6886 // Because this is an anchor, only the 2 very basic shapes are managed: circle and rect.
6887 switch( token )
6888 {
6889 case T_circle:
6891 break;
6892
6893 case T_rect:
6895 break;
6896
6897 default:
6898 Expecting( "circle or rect" );
6899 break;
6900 }
6901 NeedRIGHT();
6902 break;
6903
6904 case T_clearance:
6905 token = NextTok();
6906 // Custom shaped pads have a clearance area that is the pad shape (like usual pads) or the
6907 // convex hull of the pad shape.
6908 switch( token )
6909 {
6910 case T_outline:
6912 break;
6913
6914 case T_convexhull:
6916 break;
6917
6918 default:
6919 Expecting( "outline or convexhull" );
6920 break;
6921 }
6922
6923 NeedRIGHT();
6924 break;
6925
6926 default:
6927 Expecting( "anchor or clearance" );
6928 break;
6929 }
6930 }
6931
6932 return true;
6933}
6934
6935
6937{
6938 // Parse: (front_post_machining counterbore (size ...) (depth ...) (angle ...))
6939 // or: (back_post_machining countersink (size ...) (depth ...) (angle ...))
6940 // The mode token (counterbore/countersink) comes first
6941 T token = NextTok();
6942
6943 switch( token )
6944 {
6945 case T_counterbore:
6947 break;
6948
6949 case T_countersink:
6951 break;
6952
6953 default:
6954 Expecting( "counterbore or countersink" );
6955 }
6956
6957 // Parse optional properties
6958 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6959 {
6960 if( token != T_LEFT )
6961 Expecting( T_LEFT );
6962
6963 token = NextTok();
6964
6965 switch( token )
6966 {
6967 case T_size:
6968 aProps.size = parseBoardUnits( "post machining size" );
6969 NeedRIGHT();
6970 break;
6971
6972 case T_depth:
6973 aProps.depth = parseBoardUnits( "post machining depth" );
6974 NeedRIGHT();
6975 break;
6976
6977 case T_angle:
6978 aProps.angle = KiROUND( parseDouble( "post machining angle" ) * 10.0 );
6979 NeedRIGHT();
6980 break;
6981
6982 default:
6983 Expecting( "size, depth, or angle" );
6984 }
6985 }
6986}
6987
6988
6990{
6991 PADSTACK& padstack = aPad->Padstack();
6992
6993 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
6994 {
6995 if( token != T_LEFT )
6996 Expecting( T_LEFT );
6997
6998 token = NextTok();
6999
7000 switch( token )
7001 {
7002 case T_mode:
7003 token = NextTok();
7004
7005 switch( token )
7006 {
7007 case T_front_inner_back:
7009 break;
7010
7011 case T_custom:
7012 padstack.SetMode( PADSTACK::MODE::CUSTOM );
7013 break;
7014
7015 default:
7016 Expecting( "front_inner_back or custom" );
7017 }
7018
7019 NeedRIGHT();
7020 break;
7021
7022 case T_layer:
7023 {
7024 NextTok();
7025 PCB_LAYER_ID curLayer = UNDEFINED_LAYER;
7026
7027 if( curText == "Inner" )
7028 {
7029 if( padstack.Mode() != PADSTACK::MODE::FRONT_INNER_BACK )
7030 {
7031 THROW_IO_ERROR( wxString::Format( _( "Invalid padstack layer in\nfile: %s\n"
7032 "line: %d\noffset: %d." ),
7033 CurSource(), CurLineNumber(), CurOffset() ) );
7034 }
7035
7036 curLayer = PADSTACK::INNER_LAYERS;
7037 }
7038 else
7039 {
7040 curLayer = lookUpLayer( m_layerIndices );
7041 }
7042
7043 if( !IsCopperLayer( curLayer ) )
7044 {
7045 wxString error;
7046 error.Printf( _( "Invalid padstack layer '%s' in file '%s' at line %d, offset %d." ),
7047 curText, CurSource().GetData(), CurLineNumber(), CurOffset() );
7048 THROW_IO_ERROR( error );
7049 }
7050
7051 // Reset layer properties to default that are omitted when default in the formatter
7052 aPad->SetOffset( curLayer, VECTOR2I( 0, 0 ) );
7053 aPad->SetDelta( curLayer, VECTOR2I( 0, 0 ) );
7054
7055 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7056 {
7057 if( token != T_LEFT )
7058 Expecting( T_LEFT );
7059
7060 token = NextTok();
7061
7062 switch( token )
7063 {
7064 case T_shape:
7065 token = NextTok();
7066
7067 switch( token )
7068 {
7069 case T_circle:
7070 aPad->SetShape( curLayer, PAD_SHAPE::CIRCLE );
7071 break;
7072
7073 case T_rect:
7074 aPad->SetShape( curLayer, PAD_SHAPE::RECTANGLE );
7075 break;
7076
7077 case T_oval:
7078 aPad->SetShape( curLayer, PAD_SHAPE::OVAL );
7079 break;
7080
7081 case T_trapezoid:
7082 aPad->SetShape( curLayer, PAD_SHAPE::TRAPEZOID );
7083 break;
7084
7085 case T_roundrect:
7086 // Note: the shape can be PAD_SHAPE::ROUNDRECT or PAD_SHAPE::CHAMFERED_RECT
7087 // (if chamfer parameters are found later in pad descr.)
7088 aPad->SetShape( curLayer, PAD_SHAPE::ROUNDRECT );
7089 break;
7090
7091 case T_custom:
7092 aPad->SetShape( curLayer, PAD_SHAPE::CUSTOM );
7093 break;
7094
7095 default:
7096 Expecting( "circle, rectangle, roundrect, oval, trapezoid or custom" );
7097 }
7098
7099 NeedRIGHT();
7100 break;
7101
7102 case T_size:
7103 {
7104 VECTOR2I sz;
7105 sz.x = parseBoardUnits( "width value" );
7106 sz.y = parseBoardUnits( "height value" );
7107 aPad->SetSize( curLayer, sz );
7108 NeedRIGHT();
7109 break;
7110 }
7111
7112 case T_offset:
7113 {
7114 VECTOR2I pt;
7115 pt.x = parseBoardUnits( "drill offset x" );
7116 pt.y = parseBoardUnits( "drill offset y" );
7117 aPad->SetOffset( curLayer, pt );
7118 NeedRIGHT();
7119 break;
7120 }
7121
7122 case T_rect_delta:
7123 {
7125 delta.x = parseBoardUnits( "rectangle delta width" );
7126 delta.y = parseBoardUnits( "rectangle delta height" );
7127 aPad->SetDelta( curLayer, delta );
7128 NeedRIGHT();
7129 break;
7130 }
7131
7132 case T_roundrect_rratio:
7133 aPad->SetRoundRectRadiusRatio( curLayer, parseDouble( "roundrect radius ratio" ) );
7134 NeedRIGHT();
7135 break;
7136
7137 case T_chamfer_ratio:
7138 {
7139 double ratio = parseDouble( "chamfer ratio" );
7140 aPad->SetChamferRectRatio( curLayer, ratio );
7141
7142 if( ratio > 0 )
7143 aPad->SetShape( curLayer, PAD_SHAPE::CHAMFERED_RECT );
7144
7145 NeedRIGHT();
7146 break;
7147 }
7148
7149 case T_chamfer:
7150 {
7151 int chamfers = 0;
7152 bool end_list = false;
7153
7154 while( !end_list )
7155 {
7156 token = NextTok();
7157
7158 switch( token )
7159 {
7160 case T_top_left:
7161 chamfers |= RECT_CHAMFER_TOP_LEFT;
7162 break;
7163
7164 case T_top_right:
7165 chamfers |= RECT_CHAMFER_TOP_RIGHT;
7166 break;
7167
7168 case T_bottom_left:
7169 chamfers |= RECT_CHAMFER_BOTTOM_LEFT;
7170 break;
7171
7172 case T_bottom_right:
7173 chamfers |= RECT_CHAMFER_BOTTOM_RIGHT;
7174 break;
7175
7176 case T_RIGHT:
7177 aPad->SetChamferPositions( curLayer, chamfers );
7178 end_list = true;
7179 break;
7180
7181 default:
7182 Expecting( "chamfer_top_left chamfer_top_right chamfer_bottom_left or "
7183 "chamfer_bottom_right" );
7184 }
7185 }
7186
7187 if( end_list && chamfers != RECT_NO_CHAMFER )
7188 aPad->SetShape( curLayer, PAD_SHAPE::CHAMFERED_RECT );
7189
7190 break;
7191 }
7192
7193 case T_thermal_bridge_width:
7194 padstack.ThermalSpokeWidth( curLayer ) = parseBoardUnits( "thermal relief spoke width" );
7195 NeedRIGHT();
7196 break;
7197
7198 case T_thermal_gap:
7199 padstack.ThermalGap( curLayer ) = parseBoardUnits( "thermal relief gap value" );
7200 NeedRIGHT();
7201 break;
7202
7203 case T_thermal_bridge_angle:
7204 padstack.SetThermalSpokeAngle( EDA_ANGLE( parseDouble( "thermal spoke angle" ), DEGREES_T ) );
7205 NeedRIGHT();
7206 break;
7207
7208 case T_zone_connect:
7209 padstack.ZoneConnection( curLayer ) =
7210 magic_enum::enum_cast<ZONE_CONNECTION>( parseInt( "zone connection value" ) );
7211 NeedRIGHT();
7212 break;
7213
7214 case T_clearance:
7215 padstack.Clearance( curLayer ) = parseBoardUnits( "local clearance value" );
7216 NeedRIGHT();
7217 break;
7218
7219 case T_tenting:
7220 {
7221 auto [front, back] = parseFrontBackOptBool( true );
7222 padstack.FrontOuterLayers().has_solder_mask = front;
7223 padstack.BackOuterLayers().has_solder_mask = back;
7224 break;
7225 }
7226
7227 // TODO: refactor parsePAD_options to work on padstacks too
7228 case T_options:
7229 {
7230 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7231 {
7232 if( token != T_LEFT )
7233 Expecting( T_LEFT );
7234
7235 token = NextTok();
7236
7237 switch( token )
7238 {
7239 case T_anchor:
7240 token = NextTok();
7241 // Custom shaped pads have a "anchor pad", which is the reference
7242 // for connection calculations.
7243 // Because this is an anchor, only the 2 very basic shapes are managed:
7244 // circle and rect.
7245 switch( token )
7246 {
7247 case T_circle:
7248 padstack.SetAnchorShape( PAD_SHAPE::CIRCLE, curLayer );
7249 break;
7250
7251 case T_rect:
7252 padstack.SetAnchorShape( PAD_SHAPE::RECTANGLE, curLayer );
7253 break;
7254
7255 default:
7256 // Currently, because pad options is a moving target
7257 // just skip unknown keywords
7258 break;
7259 }
7260 NeedRIGHT();
7261 break;
7262
7263 case T_clearance:
7264 token = NextTok();
7265 // TODO: m_customShapeInZoneMode is not per-layer at the moment
7266 NeedRIGHT();
7267 break;
7268
7269 default:
7270 // Currently, because pad options is a moving target
7271 // just skip unknown keywords
7272 while( ( token = NextTok() ) != T_RIGHT )
7273 {
7274 }
7275
7276 break;
7277 }
7278 }
7279
7280 break;
7281 }
7282
7283 // TODO: deduplicate with non-padstack parser
7284 case T_primitives:
7285 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7286 {
7287 if( token == T_LEFT )
7288 token = NextTok();
7289
7290 switch( token )
7291 {
7292 case T_gr_arc:
7293 case T_gr_line:
7294 case T_gr_circle:
7295 case T_gr_rect:
7296 case T_gr_poly:
7297 case T_gr_curve:
7298 padstack.AddPrimitive( parsePCB_SHAPE( nullptr ), curLayer );
7299 break;
7300
7301 case T_gr_bbox:
7302 {
7303 PCB_SHAPE* numberBox = parsePCB_SHAPE( nullptr );
7304 numberBox->SetIsProxyItem();
7305 padstack.AddPrimitive( numberBox, curLayer );
7306 break;
7307 }
7308
7309 case T_gr_vector:
7310 {
7311 PCB_SHAPE* spokeTemplate = parsePCB_SHAPE( nullptr );
7312 spokeTemplate->SetIsProxyItem();
7313 padstack.AddPrimitive( spokeTemplate, curLayer );
7314 break;
7315 }
7316
7317 default:
7318 Expecting( "gr_line, gr_arc, gr_circle, gr_curve, gr_rect, gr_bbox or gr_poly" );
7319 break;
7320 }
7321 }
7322
7323 break;
7324
7325 default:
7326 // Not strict-parsing padstack layers yet
7327 continue;
7328 }
7329 }
7330
7331 break;
7332 }
7333
7334 default:
7335 Expecting( "mode or layer" );
7336 break;
7337 }
7338 }
7339}
7340
7341
7343{
7344 T token;
7345
7346 while( ( token = NextTok() ) != T_RIGHT )
7347 {
7348 // This token is the Uuid of the item in the group.
7349 // Since groups are serialized at the end of the file/footprint, the Uuid should already
7350 // have been seen and exist in the board.
7351 KIID uuid( CurStr() );
7352 aGroupInfo.memberUuids.push_back( uuid );
7353 }
7354}
7355
7356
7358{
7359 wxCHECK_RET( CurTok() == T_group,
7360 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_GROUP." ) );
7361
7362 T token;
7363
7364 m_groupInfos.push_back( GROUP_INFO() );
7365 GROUP_INFO& groupInfo = m_groupInfos.back();
7366 groupInfo.parent = aParent;
7367
7368 while( ( token = NextTok() ) != T_LEFT )
7369 {
7370 if( token == T_STRING )
7371 groupInfo.name = FromUTF8();
7372 else if( token == T_locked )
7373 groupInfo.locked = true;
7374 else
7375 Expecting( "group name or locked" );
7376 }
7377
7378 for( ; token != T_RIGHT; token = NextTok() )
7379 {
7380 if( token != T_LEFT )
7381 Expecting( T_LEFT );
7382
7383 token = NextTok();
7384
7385 switch( token )
7386 {
7387 // From formats [20200811, 20231215), 'id' was used instead of 'uuid'
7388 case T_id:
7389 case T_uuid:
7390 NextTok();
7391 groupInfo.uuid = CurStrToKIID();
7392 NeedRIGHT();
7393 break;
7394
7395 case T_lib_id:
7396 {
7397 token = NextTok();
7398
7399 if( !IsSymbol( token ) && token != T_NUMBER )
7400 Expecting( "symbol|number" );
7401
7402 wxString name = FromUTF8();
7403 // Some symbol LIB_IDs have the '/' character escaped which can break
7404 // symbol links. The '/' character is no longer an illegal LIB_ID character so
7405 // it doesn't need to be escaped.
7406 name.Replace( "{slash}", "/" );
7407
7408 int bad_pos = groupInfo.libId.Parse( name );
7409
7410 if( bad_pos >= 0 )
7411 {
7412 if( static_cast<int>( name.size() ) > bad_pos )
7413 {
7414 wxString msg = wxString::Format( _( "Group library link %s contains invalid character '%c'" ),
7415 name,
7416 name[bad_pos] );
7417
7418 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
7419 }
7420
7421 THROW_PARSE_ERROR( _( "Invalid library ID" ), CurSource(), CurLine(), CurLineNumber(), CurOffset() );
7422 }
7423
7424 NeedRIGHT();
7425 break;
7426 }
7427
7428 case T_locked:
7429 groupInfo.locked = parseBool();
7430 NeedRIGHT();
7431 break;
7432
7433 case T_members:
7434 parseGROUP_members( groupInfo );
7435 break;
7436
7437 default:
7438 Expecting( "uuid, locked, lib_id, or members" );
7439 }
7440 }
7441}
7442
7443
7445{
7446 wxCHECK_RET( CurTok() == T_generated,
7447 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_GENERATOR." ) );
7448
7449 T token;
7450
7451 m_generatorInfos.push_back( GENERATOR_INFO() );
7452 GENERATOR_INFO& genInfo = m_generatorInfos.back();
7453
7454 genInfo.layer = F_Cu;
7455 genInfo.parent = aParent;
7456 genInfo.properties = STRING_ANY_MAP( pcbIUScale.IU_PER_MM );
7457
7458 NeedLEFT();
7459 token = NextTok();
7460
7461 // For formats [20231007, 20231215), 'id' was used instead of 'uuid'
7462 if( token != T_uuid && token != T_id )
7463 Expecting( T_uuid );
7464
7465 NextTok();
7466 genInfo.uuid = CurStrToKIID();
7467 NeedRIGHT();
7468
7469 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7470 {
7471 if( token != T_LEFT )
7472 Expecting( T_LEFT );
7473
7474 token = NextTok();
7475
7476 switch( token )
7477 {
7478 case T_type:
7479 NeedSYMBOL();
7480 genInfo.genType = FromUTF8();
7481 NeedRIGHT();
7482 break;
7483
7484 case T_name:
7485 NeedSYMBOL();
7486 genInfo.name = FromUTF8();
7487 NeedRIGHT();
7488 break;
7489
7490 case T_locked:
7491 token = NextTok();
7492 genInfo.locked = token == T_yes;
7493 NeedRIGHT();
7494 break;
7495
7496 case T_layer:
7497 genInfo.layer = parseBoardItemLayer();
7498 NeedRIGHT();
7499 break;
7500
7501 case T_members:
7502 parseGROUP_members( genInfo );
7503 break;
7504
7505 default:
7506 {
7507 wxString pName = FromUTF8();
7508 T tok1 = NextTok();
7509
7510 switch( tok1 )
7511 {
7512 case T_yes:
7513 genInfo.properties.emplace( pName, wxAny( true ) );
7514 NeedRIGHT();
7515 break;
7516
7517 case T_no:
7518 genInfo.properties.emplace( pName, wxAny( false ) );
7519 NeedRIGHT();
7520 break;
7521
7522 case T_NUMBER:
7523 {
7524 double pValue = parseDouble();
7525 genInfo.properties.emplace( pName, wxAny( pValue ) );
7526 NeedRIGHT();
7527 break;
7528 }
7529
7530 case T_STRING: // Quoted string
7531 {
7532 wxString pValue = FromUTF8();
7533 genInfo.properties.emplace( pName, pValue );
7534 NeedRIGHT();
7535 break;
7536 }
7537
7538 case T_LEFT:
7539 {
7540 NeedSYMBOL();
7541 T tok2 = CurTok();
7542
7543 switch( tok2 )
7544 {
7545 case T_xy:
7546 {
7547 VECTOR2I pt;
7548
7549 pt.x = parseBoardUnits( "X coordinate" );
7550 pt.y = parseBoardUnits( "Y coordinate" );
7551
7552 genInfo.properties.emplace( pName, wxAny( pt ) );
7553 NeedRIGHT();
7554 NeedRIGHT();
7555 break;
7556 }
7557
7558 case T_pts:
7559 {
7561
7562 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7564
7565 NeedRIGHT();
7566 genInfo.properties.emplace( pName, wxAny( chain ) );
7567 break;
7568 }
7569
7570 default:
7571 Expecting( "xy or pts" );
7572 }
7573
7574 break;
7575 }
7576
7577 default:
7578 Expecting( "a number, symbol, string or (" );
7579 }
7580
7581 break;
7582 }
7583 }
7584 }
7585
7586 // Previous versions had bugs which could save ghost tuning patterns. Ignore them.
7587 if( genInfo.genType == wxT( "tuning_pattern" ) && genInfo.memberUuids.empty() )
7588 m_generatorInfos.pop_back();
7589}
7590
7591
7593{
7594 wxCHECK_MSG( CurTok() == T_arc, nullptr,
7595 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as ARC." ) );
7596
7597 VECTOR2I pt;
7598 T token;
7599
7600 std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( m_board );
7601
7602 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7603 {
7604 // Legacy locked
7605 if( token == T_locked )
7606 {
7607 arc->SetLocked( true );
7608 token = NextTok();
7609 }
7610
7611 if( token != T_LEFT )
7612 Expecting( T_LEFT );
7613
7614 token = NextTok();
7615
7616 switch( token )
7617 {
7618 case T_start:
7619 pt.x = parseBoardUnits( "start x" );
7620 pt.y = parseBoardUnits( "start y" );
7621 arc->SetStart( pt );
7622 NeedRIGHT();
7623 break;
7624
7625 case T_mid:
7626 pt.x = parseBoardUnits( "mid x" );
7627 pt.y = parseBoardUnits( "mid y" );
7628 arc->SetMid( pt );
7629 NeedRIGHT();
7630 break;
7631
7632 case T_end:
7633 pt.x = parseBoardUnits( "end x" );
7634 pt.y = parseBoardUnits( "end y" );
7635 arc->SetEnd( pt );
7636 NeedRIGHT();
7637 break;
7638
7639 case T_width:
7640 arc->SetWidth( parseBoardUnits( "width" ) );
7641 NeedRIGHT();
7642 break;
7643
7644 case T_layer:
7645 arc->SetLayer( parseBoardItemLayer() );
7646 NeedRIGHT();
7647 break;
7648
7649 case T_layers:
7650 arc->SetLayerSet( parseLayersForCuItemWithSoldermask() );
7651 break;
7652
7653 case T_solder_mask_margin:
7654 arc->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
7655 NeedRIGHT();
7656 break;
7657
7658 case T_net:
7659 parseNet( arc.get() );
7660 break;
7661
7662 case T_tstamp:
7663 case T_uuid:
7664 NextTok();
7665 arc->SetUuidDirect( CurStrToKIID() );
7666 NeedRIGHT();
7667 break;
7668
7669 // We continue to parse the status field but it is no longer written
7670 case T_status:
7671 parseHex();
7672 NeedRIGHT();
7673 break;
7674
7675 case T_locked:
7676 arc->SetLocked( parseMaybeAbsentBool( true ) );
7677 break;
7678
7679 default:
7680 Expecting( "start, mid, end, width, layer, solder_mask_margin, net, tstamp, uuid or status" );
7681 }
7682 }
7683
7684 if( !IsCopperLayer( arc->GetLayer() ) )
7685 {
7686 // No point in asserting; these usually come from hand-edited boards
7687 return nullptr;
7688 }
7689
7690 return arc.release();
7691}
7692
7693
7695{
7696 wxCHECK_MSG( CurTok() == T_segment, nullptr,
7697 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TRACK." ) );
7698
7699 VECTOR2I pt;
7700 T token;
7701
7702 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( m_board );
7703
7704 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7705 {
7706 // Legacy locked flag
7707 if( token == T_locked )
7708 {
7709 track->SetLocked( true );
7710 token = NextTok();
7711 }
7712
7713 if( token != T_LEFT )
7714 Expecting( T_LEFT );
7715
7716 token = NextTok();
7717
7718 switch( token )
7719 {
7720 case T_start:
7721 pt.x = parseBoardUnits( "start x" );
7722 pt.y = parseBoardUnits( "start y" );
7723 track->SetStart( pt );
7724 NeedRIGHT();
7725 break;
7726
7727 case T_end:
7728 pt.x = parseBoardUnits( "end x" );
7729 pt.y = parseBoardUnits( "end y" );
7730 track->SetEnd( pt );
7731 NeedRIGHT();
7732 break;
7733
7734 case T_width:
7735 track->SetWidth( parseBoardUnits( "width" ) );
7736 NeedRIGHT();
7737 break;
7738
7739 case T_layer:
7740 track->SetLayer( parseBoardItemLayer() );
7741 NeedRIGHT();
7742 break;
7743
7744 case T_layers:
7745 track->SetLayerSet( parseLayersForCuItemWithSoldermask() );
7746 break;
7747
7748 case T_solder_mask_margin:
7749 track->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
7750 NeedRIGHT();
7751 break;
7752
7753 case T_net:
7754 parseNet( track.get() );
7755 break;
7756
7757 case T_tstamp:
7758 case T_uuid:
7759 NextTok();
7760 track->SetUuidDirect( CurStrToKIID() );
7761 NeedRIGHT();
7762 break;
7763
7764 // We continue to parse the status field but it is no longer written
7765 case T_status:
7766 parseHex();
7767 NeedRIGHT();
7768 break;
7769
7770 case T_locked:
7771 track->SetLocked( parseMaybeAbsentBool( true ) );
7772 break;
7773
7774 default:
7775 Expecting( "start, end, width, layer, solder_mask_margin, net, tstamp, uuid or locked" );
7776 }
7777 }
7778
7779 if( !IsCopperLayer( track->GetLayer() ) )
7780 {
7781 // No point in asserting; these usually come from hand-edited boards
7782 return nullptr;
7783 }
7784
7785 return track.release();
7786}
7787
7788
7790{
7791 wxCHECK_MSG( CurTok() == T_via, nullptr,
7792 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_VIA." ) );
7793
7794 VECTOR2I pt;
7795 T token;
7796
7797 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( m_board );
7798
7799 // File format default is no-token == no-feature.
7800 via->Padstack().SetUnconnectedLayerMode( UNCONNECTED_LAYER_MODE::KEEP_ALL );
7801
7802 // Versions before 10.0 had no protection features other than tenting, so those features must
7803 // be interpreted as OFF in legacy boards, not as unspecified (aka: inherit from board stackup)
7804 if( m_requiredVersion < 20250228 )
7805 {
7806 via->Padstack().FrontOuterLayers().has_covering = false;
7807 via->Padstack().BackOuterLayers().has_covering = false;
7808 via->Padstack().FrontOuterLayers().has_plugging = false;
7809 via->Padstack().BackOuterLayers().has_plugging = false;
7810 via->Padstack().Drill().is_filled = false;
7811 via->Padstack().Drill().is_capped = false;
7812 }
7813
7814 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7815 {
7816 // Legacy locked
7817 if( token == T_locked )
7818 {
7819 via->SetLocked( true );
7820 token = NextTok();
7821 }
7822
7823 if( token == T_LEFT )
7824 token = NextTok();
7825
7826 switch( token )
7827 {
7828 case T_blind:
7829 via->SetViaType( VIATYPE::BLIND );
7830 break;
7831
7832 case T_buried:
7833 via->SetViaType( VIATYPE::BURIED );
7834 break;
7835
7836 case T_micro:
7837 via->SetViaType( VIATYPE::MICROVIA );
7838 break;
7839
7840 case T_at:
7841 pt.x = parseBoardUnits( "start x" );
7842 pt.y = parseBoardUnits( "start y" );
7843 via->SetStart( pt );
7844 via->SetEnd( pt );
7845 NeedRIGHT();
7846 break;
7847
7848 case T_size:
7849 via->SetWidth( PADSTACK::ALL_LAYERS, parseBoardUnits( "via width" ) );
7850 NeedRIGHT();
7851 break;
7852
7853 case T_drill:
7854 via->SetDrill( parseBoardUnits( "drill diameter" ) );
7855 NeedRIGHT();
7856 break;
7857
7858 case T_layers:
7859 {
7860 PCB_LAYER_ID layer1, layer2;
7861 NextTok();
7862 layer1 = lookUpLayer( m_layerIndices );
7863 NextTok();
7864 layer2 = lookUpLayer( m_layerIndices );
7865 via->SetLayerPair( layer1, layer2 );
7866
7867 if( layer1 == UNDEFINED_LAYER || layer2 == UNDEFINED_LAYER )
7868 Expecting( "layer name" );
7869
7870 NeedRIGHT();
7871 break;
7872 }
7873
7874 case T_net:
7875 parseNet( via.get() );
7876 break;
7877
7878 case T_remove_unused_layers:
7879 if( parseMaybeAbsentBool( true ) )
7880 via->SetRemoveUnconnected( true );
7881
7882 break;
7883
7884 case T_keep_end_layers:
7885 if( parseMaybeAbsentBool( true ) )
7886 via->SetKeepStartEnd( true );
7887
7888 break;
7889
7890 case T_start_end_only:
7891 if( parseMaybeAbsentBool( true ) )
7892 via->Padstack().SetUnconnectedLayerMode( UNCONNECTED_LAYER_MODE::START_END_ONLY );
7893
7894 break;
7895
7896 case T_zone_layer_connections:
7897 {
7898 // Ensure only copper layers are stored int ZoneLayerOverride array
7899 LSET cuLayers = via->GetLayerSet() & LSET::AllCuMask();
7900
7901 for( PCB_LAYER_ID layer : cuLayers )
7902 via->SetZoneLayerOverride( layer, ZLO_FORCE_NO_ZONE_CONNECTION );
7903
7904 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7905 {
7907
7908 if( !IsCopperLayer( layer ) )
7909 Expecting( "copper layer name" );
7910
7911 via->SetZoneLayerOverride( layer, ZLO_FORCE_FLASHED );
7912 }
7913
7914 break;
7915 }
7916
7917 case T_padstack:
7918 parseViastack( via.get() );
7919 break;
7920
7921 case T_teardrops:
7922 parseTEARDROP_PARAMETERS( &via->GetTeardropParams() );
7923 break;
7924
7925 case T_tenting:
7926 {
7927 auto [front, back] = parseFrontBackOptBool( true );
7928 via->Padstack().FrontOuterLayers().has_solder_mask = front;
7929 via->Padstack().BackOuterLayers().has_solder_mask = back;
7930 break;
7931 }
7932
7933 case T_covering:
7934 {
7935 auto [front, back] = parseFrontBackOptBool();
7936 via->Padstack().FrontOuterLayers().has_covering = front;
7937 via->Padstack().BackOuterLayers().has_covering = back;
7938 break;
7939 }
7940
7941 case T_plugging:
7942 {
7943 auto [front, back] = parseFrontBackOptBool();
7944 via->Padstack().FrontOuterLayers().has_plugging = front;
7945 via->Padstack().BackOuterLayers().has_plugging = back;
7946 break;
7947 }
7948
7949 case T_filling:
7950 via->Padstack().Drill().is_filled = parseOptBool();
7951 NeedRIGHT();
7952 break;
7953
7954 case T_capping:
7955 via->Padstack().Drill().is_capped = parseOptBool();
7956 NeedRIGHT();
7957 break;
7958
7959 case T_tstamp:
7960 case T_uuid:
7961 NextTok();
7962 via->SetUuidDirect( CurStrToKIID() );
7963 NeedRIGHT();
7964 break;
7965
7966 // We continue to parse the status field but it is no longer written
7967 case T_status:
7968 parseHex();
7969 NeedRIGHT();
7970 break;
7971
7972 case T_locked:
7973 via->SetLocked( parseMaybeAbsentBool( true ) );
7974 break;
7975
7976 case T_free:
7977 via->SetIsFree( parseMaybeAbsentBool( true ) );
7978 break;
7979
7980 case T_backdrill:
7981 {
7982 // Parse: (backdrill (size ...) (layers start end))
7983 PADSTACK::DRILL_PROPS& secondary = via->Padstack().SecondaryDrill();
7984
7985 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7986 {
7987 if( token != T_LEFT )
7988 Expecting( T_LEFT );
7989
7990 token = NextTok();
7991
7992 switch( token )
7993 {
7994 case T_size:
7995 {
7996 int size = parseBoardUnits( "backdrill size" );
7997 secondary.size = VECTOR2I( size, size );
7998 NeedRIGHT();
7999 break;
8000 }
8001
8002 case T_layers:
8003 {
8004 NextTok();
8005 secondary.start = lookUpLayer( m_layerIndices );
8006 NextTok();
8007 secondary.end = lookUpLayer( m_layerIndices );
8008 NeedRIGHT();
8009 break;
8010 }
8011
8012 default:
8013 Expecting( "size or layers" );
8014 }
8015 }
8016
8017 break;
8018 }
8019
8020 case T_tertiary_drill:
8021 {
8022 // Parse: (tertiary_drill (size ...) (layers start end))
8023 PADSTACK::DRILL_PROPS& tertiary = via->Padstack().TertiaryDrill();
8024
8025 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
8026 {
8027 if( token != T_LEFT )
8028 Expecting( T_LEFT );
8029
8030 token = NextTok();
8031
8032 switch( token )
8033 {
8034 case T_size:
8035 {
8036 int size = parseBoardUnits( "tertiary drill size" );
8037 tertiary.size = VECTOR2I( size, size );
8038 NeedRIGHT();
8039 break;
8040 }
8041
8042 case T_layers:
8043 {
8044 NextTok();
8045 tertiary.start = lookUpLayer( m_layerIndices );
8046 NextTok();
8047 tertiary.end = lookUpLayer( m_layerIndices );
8048 NeedRIGHT();
8049 break;
8050 }
8051
8052 default:
8053 Expecting( "size or layers" );
8054 }
8055 }
8056
8057 break;
8058 }
8059
8060 case T_front_post_machining:
8061 parsePostMachining( via->Padstack().FrontPostMachining() );
8062 break;
8063
8064 case T_back_post_machining:
8065 parsePostMachining( via->Padstack().BackPostMachining() );
8066 break;
8067
8068 default:
8069 Expecting( "blind, micro, at, size, drill, layers, net, free, tstamp, uuid, status, "
8070 "teardrops, backdrill, tertiary_drill, front_post_machining, or "
8071 "back_post_machining" );
8072 }
8073 }
8074
8075 return via.release();
8076}
8077
8078
8079std::pair<std::optional<bool>, std::optional<bool>>
8081{
8082 T token = NextTok();
8083
8084 std::optional<bool> front{};
8085 std::optional<bool> back{};
8086
8087 if( token != T_LEFT && aAllowLegacyFormat )
8088 {
8089 // legacy format for tenting.
8090 while( token != T_RIGHT )
8091 {
8092 if( token == T_front )
8093 {
8094 front = true;
8095 }
8096 else if( token == T_back )
8097 {
8098 back = true;
8099 }
8100 else if( token == T_none )
8101 {
8102 front.reset();
8103 back.reset();
8104 }
8105 else
8106 {
8107 Expecting( "front, back or none" );
8108 }
8109
8110 token = NextTok();
8111 }
8112
8113 // GCC false-positive: both front and back are initialized to {} above and can only be
8114 // set or reset inside this loop, never left in an indeterminate state.
8115#if defined( __GNUC__ ) && !defined( __clang__ )
8116#pragma GCC diagnostic push
8117#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
8118#endif
8119 return { front, back };
8120#if defined( __GNUC__ ) && !defined( __clang__ )
8121#pragma GCC diagnostic pop
8122#endif
8123 }
8124
8125 while( token != T_RIGHT )
8126 {
8127 if( token != T_LEFT )
8128 Expecting( "(" );
8129
8130 token = NextTok();
8131
8132 if( token == T_front )
8133 front = parseOptBool();
8134 else if( token == T_back )
8135 back = parseOptBool();
8136 else
8137 Expecting( "front or back" );
8138
8139 NeedRIGHT();
8140
8141 token = NextTok();
8142 }
8143
8144 return { front, back };
8145}
8146
8147
8149{
8150 PADSTACK& padstack = aVia->Padstack();
8151
8152 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
8153 {
8154 if( token != T_LEFT )
8155 Expecting( T_LEFT );
8156
8157 token = NextTok();
8158
8159 switch( token )
8160 {
8161 case T_mode:
8162 token = NextTok();
8163
8164 switch( token )
8165 {
8166 case T_front_inner_back:
8168 break;
8169
8170 case T_custom:
8171 padstack.SetMode( PADSTACK::MODE::CUSTOM );
8172 break;
8173
8174 default:
8175 Expecting( "front_inner_back or custom" );
8176 }
8177
8178 NeedRIGHT();
8179 break;
8180
8181 case T_layer:
8182 {
8183 NextTok();
8184 PCB_LAYER_ID curLayer = UNDEFINED_LAYER;
8185
8186 if( curText == "Inner" )
8187 {
8188 if( padstack.Mode() != PADSTACK::MODE::FRONT_INNER_BACK )
8189 {
8190 THROW_IO_ERROR( wxString::Format( _( "Invalid padstack layer in\nfile: %s\n"
8191 "line: %d\noffset: %d." ),
8192 CurSource(), CurLineNumber(), CurOffset() ) );
8193 }
8194
8195 curLayer = PADSTACK::INNER_LAYERS;
8196 }
8197 else
8198 {
8199 curLayer = lookUpLayer( m_layerIndices );
8200 }
8201
8202 if( !IsCopperLayer( curLayer ) )
8203 {
8204 wxString error;
8205 error.Printf( _( "Invalid padstack layer '%s' in file '%s' at line %d, offset %d." ),
8206 curText, CurSource().GetData(), CurLineNumber(), CurOffset() );
8207 THROW_IO_ERROR( error );
8208 }
8209
8210 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
8211 {
8212 if( token != T_LEFT )
8213 Expecting( T_LEFT );
8214
8215 token = NextTok();
8216
8217 switch( token )
8218 {
8219
8220 case T_size:
8221 {
8222 int diameter = parseBoardUnits( "via width" );
8223 padstack.SetSize( { diameter, diameter }, curLayer );
8224 NeedRIGHT();
8225 break;
8226 }
8227
8228 default:
8229 // Currently only supporting custom via diameter per layer, not other properties
8230 Expecting( "size" );
8231 }
8232 }
8233
8234 break;
8235 }
8236
8237 default:
8238 Expecting( "mode or layer" );
8239 break;
8240 }
8241 }
8242}
8243
8244
8246{
8247 wxCHECK_MSG( CurTok() == T_zone, nullptr,
8248 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as ZONE." ) );
8249
8251
8252 int hatchPitch = ZONE::GetDefaultHatchPitch();
8253 T token;
8254 int tmp;
8255 wxString legacyNetnameFromFile; // the (non-authoratative) zone net name found in a legacy file
8256
8257 // bigger scope since each filled_polygon is concatenated in here
8258 std::map<PCB_LAYER_ID, SHAPE_POLY_SET> pts;
8259 std::map<PCB_LAYER_ID, std::vector<SEG>> legacySegs;
8260 PCB_LAYER_ID filledLayer;
8261 bool addedFilledPolygons = false;
8262
8263 // This hasn't been supported since V6 or so, but we only stopped writing out the token
8264 // in V10.
8265 bool isStrokedFill = m_requiredVersion < 20250210;
8266
8267 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aParent );
8268
8269 zone->SetAssignedPriority( 0 );
8270
8271 // This is the default for board files:
8272 zone->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::ALWAYS );
8273
8274 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
8275 {
8276 // legacy locked
8277 if( token == T_locked )
8278 {
8279 zone->SetLocked( true );
8280 token = NextTok();
8281 }
8282
8283 if( token == T_LEFT )
8284 token = NextTok();
8285
8286 switch( token )
8287 {
8288 case T_net:
8289 parseNet( zone.get() );
8290 break;
8291
8292 case T_net_name:
8293 NeedSYMBOLorNUMBER();
8294 legacyNetnameFromFile = FromUTF8();
8295 NeedRIGHT();
8296 break;
8297
8298 case T_layer: // keyword for zones that are on only one layer
8299 zone->SetLayer( parseBoardItemLayer() );
8300 NeedRIGHT();
8301 break;
8302
8303 case T_layers: // keyword for zones that can live on a set of layers
8304 zone->SetLayerSet( parseBoardItemLayersAsMask() );
8305 break;
8306
8307 case T_property:
8308 parseZoneLayerProperty( zone->LayerProperties() );
8309 break;
8310
8311 case T_tstamp:
8312 case T_uuid:
8313 NextTok();
8314 zone->SetUuidDirect( CurStrToKIID() );
8315 NeedRIGHT();
8316 break;
8317
8318 case T_hatch:
8319 token = NextTok();
8320
8321 if( token != T_none && token != T_edge && token != T_full )
8322 Expecting( "none, edge, or full" );
8323
8324 switch( token )
8325 {
8326 default:
8327 case T_none: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH; break;
8328 case T_edge: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; break;
8329 case T_full: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL; break;
8330 }
8331
8332 hatchPitch = parseBoardUnits( "hatch pitch" );
8333 NeedRIGHT();
8334 break;
8335
8336 case T_priority:
8337 zone->SetAssignedPriority( parseInt( "zone priority" ) );
8338 NeedRIGHT();
8339 break;
8340
8341 case T_connect_pads:
8342 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
8343 {
8344 if( token == T_LEFT )
8345 token = NextTok();
8346
8347 switch( token )
8348 {
8349 case T_yes:
8350 zone->SetPadConnection( ZONE_CONNECTION::FULL );
8351 break;
8352
8353 case T_no:
8354 zone->SetPadConnection( ZONE_CONNECTION::NONE );
8355 break;
8356
8357 case T_thru_hole_only:
8358 zone->SetPadConnection( ZONE_CONNECTION::THT_THERMAL );
8359 break;
8360
8361 case T_clearance:
8362 zone->SetLocalClearance( parseBoardUnits( "zone clearance" ) );
8363 NeedRIGHT();
8364 break;
8365
8366 default:
8367 Expecting( "yes, no, or clearance" );
8368 }
8369 }
8370
8371 break;
8372
8373 case T_min_thickness:
8374 zone->SetMinThickness( parseBoardUnits( T_min_thickness ) );
8375 NeedRIGHT();
8376 break;
8377
8378 case T_filled_areas_thickness:
8379 // A new zone fill strategy was added in v6, so we need to know if we're parsing
8380 // a zone that was filled before that. Note that the change was implemented as
8381 // a new parameter, so we need to check for the presence of filled_areas_thickness
8382 // instead of just its value.
8383
8384 if( !parseBool() )
8385 isStrokedFill = false;
8386
8387 NeedRIGHT();
8388 break;
8389
8390 case T_fill:
8391 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
8392 {
8393 if( token == T_LEFT )
8394 token = NextTok();
8395
8396 switch( token )
8397 {
8398 case T_yes:
8399 zone->SetIsFilled( true );
8400 break;
8401
8402 case T_mode:
8403 token = NextTok();
8404
8405 if( token != T_segment && token != T_hatch && token != T_polygon
8406 && token != T_thieving )
8407 {
8408 Expecting( "segment, hatch, polygon or thieving" );
8409 }
8410
8411 switch( token )
8412 {
8413 case T_hatch:
8414 zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
8415 break;
8416
8417 case T_thieving:
8418 if( m_requiredVersion < 20260513 )
8419 {
8420 Expecting( "segment, hatch or polygon "
8421 "(thieving requires file version >= 20260513)" );
8422 }
8423
8424 zone->SetFillMode( ZONE_FILL_MODE::COPPER_THIEVING );
8425 break;
8426
8427 case T_segment: // deprecated, convert to polygons
8428 case T_polygon:
8429 default:
8430 zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
8431 break;
8432 }
8433
8434 NeedRIGHT();
8435 break;
8436
8437 case T_hatch_thickness:
8438 zone->SetHatchThickness( parseBoardUnits( T_hatch_thickness ) );
8439 NeedRIGHT();
8440 break;
8441
8442 case T_hatch_gap:
8443 zone->SetHatchGap( parseBoardUnits( T_hatch_gap ) );
8444 NeedRIGHT();
8445 break;
8446
8447 case T_hatch_orientation:
8448 {
8449 EDA_ANGLE orientation( parseDouble( T_hatch_orientation ), DEGREES_T );
8450 zone->SetHatchOrientation( orientation );
8451 NeedRIGHT();
8452 break;
8453 }
8454
8455 case T_hatch_smoothing_level:
8456 zone->SetHatchSmoothingLevel( parseDouble( T_hatch_smoothing_level ) );
8457 NeedRIGHT();
8458 break;
8459
8460 case T_hatch_smoothing_value:
8461 zone->SetHatchSmoothingValue( parseDouble( T_hatch_smoothing_value ) );
8462 NeedRIGHT();
8463 break;
8464
8465 case T_hatch_border_algorithm:
8466 token = NextTok();
8467
8468 if( token != T_hatch_thickness && token != T_min_thickness )
8469 Expecting( "hatch_thickness or min_thickness" );
8470
8471 zone->SetHatchBorderAlgorithm( token == T_hatch_thickness ? 1 : 0 );
8472 NeedRIGHT();
8473 break;
8474
8475 case T_hatch_min_hole_area:
8476 zone->SetHatchHoleMinArea( parseDouble( T_hatch_min_hole_area ) );
8477 NeedRIGHT();
8478 break;
8479
8480 case T_thieving:
8481 {
8482 if( m_requiredVersion < 20260513 )
8483 {
8484 Expecting( "thieving requires file version >= 20260513" );
8485 }
8486
8487 THIEVING_SETTINGS thieving = zone->GetThievingSettings();
8488
8489 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
8490 {
8491 if( token == T_LEFT )
8492 token = NextTok();
8493
8494 switch( token )
8495 {
8496 case T_type:
8497 token = NextTok();
8498
8499 switch( token )
8500 {
8501 case T_dots: thieving.pattern = THIEVING_PATTERN::DOTS; break;
8502 case T_squares: thieving.pattern = THIEVING_PATTERN::SQUARES; break;
8503 case T_hatch: thieving.pattern = THIEVING_PATTERN::HATCH; break;
8504 default: Expecting( "dots, squares or hatch" );
8505 }
8506
8507 NeedRIGHT();
8508 break;
8509
8510 // Reject non-positive geometry inline. Zero size would emit
8511 // zero-area stamps and zero gap would deadlock the filler grid
8512 // loop. The zone's existing setting (constructor defaults)
8513 // stays in place for any malformed field.
8514 case T_size:
8515 {
8516 int val = parseBoardUnits( T_size );
8517
8518 if( val > 0 )
8519 thieving.element_size = val;
8520
8521 NeedRIGHT();
8522 break;
8523 }
8524
8525 case T_gap:
8526 {
8527 int val = parseBoardUnits( T_gap );
8528
8529 if( val > 0 )
8530 thieving.gap = val;
8531
8532 NeedRIGHT();
8533 break;
8534 }
8535
8536 case T_width:
8537 {
8538 int val = parseBoardUnits( T_width );
8539
8540 if( val > 0 )
8541 thieving.line_width = val;
8542
8543 NeedRIGHT();
8544 break;
8545 }
8546
8547 case T_stagger:
8548 thieving.stagger = parseBool();
8549 NeedRIGHT();
8550 break;
8551
8552 case T_orientation:
8553 thieving.orientation = EDA_ANGLE( parseDouble( T_orientation ),
8554 DEGREES_T );
8555 NeedRIGHT();
8556 break;
8557
8558 default:
8559 Expecting( "type, size, gap, width, stagger or orientation" );
8560 }
8561 }
8562
8563 zone->SetThievingSettings( thieving );
8564 break;
8565 }
8566
8567 case T_arc_segments:
8568 ignore_unused( parseInt( "arc segment count" ) );
8569 NeedRIGHT();
8570 break;
8571
8572 case T_thermal_gap:
8573 zone->SetThermalReliefGap( parseBoardUnits( T_thermal_gap ) );
8574 NeedRIGHT();
8575 break;
8576
8577 case T_thermal_bridge_width:
8578 zone->SetThermalReliefSpokeWidth( parseBoardUnits( T_thermal_bridge_width ) );
8579 NeedRIGHT();
8580 break;
8581
8582 case T_smoothing:
8583 switch( NextTok() )
8584 {
8585 case T_none:
8586 zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_NONE );
8587 break;
8588
8589 case T_chamfer:
8590 if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
8591 zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_CHAMFER );
8592
8593 break;
8594
8595 case T_fillet:
8596 if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
8597 zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_FILLET );
8598
8599 break;
8600
8601 default:
8602 Expecting( "none, chamfer, or fillet" );
8603 }
8604
8605 NeedRIGHT();
8606 break;
8607
8608 case T_radius:
8609 tmp = parseBoardUnits( "corner radius" );
8610
8611 if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
8612 zone->SetCornerRadius( tmp );
8613
8614 NeedRIGHT();
8615 break;
8616
8617 case T_island_removal_mode:
8618 tmp = parseInt( "island_removal_mode" );
8619
8620 if( tmp >= 0 && tmp <= 2 )
8621 zone->SetIslandRemovalMode( static_cast<ISLAND_REMOVAL_MODE>( tmp ) );
8622
8623 NeedRIGHT();
8624 break;
8625
8626 case T_island_area_min:
8627 {
8628 int area = parseBoardUnits( T_island_area_min );
8629 zone->SetMinIslandArea( area * pcbIUScale.IU_PER_MM );
8630 NeedRIGHT();
8631 break;
8632 }
8633
8634 default:
8635 Expecting( "mode, arc_segments, thermal_gap, thermal_bridge_width, "
8636 "hatch_thickness, hatch_gap, hatch_orientation, "
8637 "hatch_smoothing_level, hatch_smoothing_value, "
8638 "hatch_border_algorithm, hatch_min_hole_area, thieving, "
8639 "smoothing, radius, island_removal_mode, or island_area_min" );
8640 }
8641 }
8642
8643 break;
8644
8645 case T_placement:
8646 zone->SetIsRuleArea( true );
8647
8648 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
8649 {
8650 if( token == T_LEFT )
8651 token = NextTok();
8652
8653 switch( token )
8654 {
8655 case T_sheetname:
8656 {
8657 zone->SetPlacementAreaSourceType( PLACEMENT_SOURCE_T::SHEETNAME );
8658 NeedSYMBOL();
8659 zone->SetPlacementAreaSource( FromUTF8() );
8660 break;
8661 }
8662 case T_component_class:
8663 {
8664 zone->SetPlacementAreaSourceType( PLACEMENT_SOURCE_T::COMPONENT_CLASS );
8665 NeedSYMBOL();
8666 zone->SetPlacementAreaSource( FromUTF8() );
8667 break;
8668 }
8669 case T_group:
8670 {
8671 zone->SetPlacementAreaSourceType( PLACEMENT_SOURCE_T::GROUP_PLACEMENT );
8672 NeedSYMBOL();
8673 zone->SetPlacementAreaSource( FromUTF8() );
8674 break;
8675 }
8676 case T_enabled:
8677 {
8678 token = NextTok();
8679
8680 if( token == T_yes )
8681 zone->SetPlacementAreaEnabled( true );
8682 else if( token == T_no )
8683 zone->SetPlacementAreaEnabled( false );
8684 else
8685 Expecting( "yes or no" );
8686
8687 break;
8688 }
8689 default:
8690 {
8691 Expecting( "enabled, sheetname, component_class, or group" );
8692 break;
8693 }
8694 }
8695
8696 NeedRIGHT();
8697 }
8698
8699 break;
8700
8701 case T_keepout:
8702 // "keepout" now means rule area, but the file token stays the same
8703 zone->SetIsRuleArea( true );
8704
8705 // Initialize these two because their tokens won't appear in older files:
8706 zone->SetDoNotAllowPads( false );
8707 zone->SetDoNotAllowFootprints( false );
8708
8709 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
8710 {
8711 if( token == T_LEFT )
8712 token = NextTok();
8713
8714 switch( token )
8715 {
8716 case T_tracks:
8717 token = NextTok();
8718
8719 if( token != T_allowed && token != T_not_allowed )
8720 Expecting( "allowed or not_allowed" );
8721
8722 zone->SetDoNotAllowTracks( token == T_not_allowed );
8723 break;
8724
8725 case T_vias:
8726 token = NextTok();
8727
8728 if( token != T_allowed && token != T_not_allowed )
8729 Expecting( "allowed or not_allowed" );
8730
8731 zone->SetDoNotAllowVias( token == T_not_allowed );
8732 break;
8733
8734 case T_copperpour:
8735 token = NextTok();
8736
8737 if( token != T_allowed && token != T_not_allowed )
8738 Expecting( "allowed or not_allowed" );
8739
8740 zone->SetDoNotAllowZoneFills( token == T_not_allowed );
8741 break;
8742
8743 case T_pads:
8744 token = NextTok();
8745
8746 if( token != T_allowed && token != T_not_allowed )
8747 Expecting( "allowed or not_allowed" );
8748
8749 zone->SetDoNotAllowPads( token == T_not_allowed );
8750 break;
8751
8752 case T_footprints:
8753 token = NextTok();
8754
8755 if( token != T_allowed && token != T_not_allowed )
8756 Expecting( "allowed or not_allowed" );
8757
8758 zone->SetDoNotAllowFootprints( token == T_not_allowed );
8759 break;
8760
8761 default:
8762 Expecting( "tracks, vias or copperpour" );
8763 }
8764
8765 NeedRIGHT();
8766 }
8767
8768 break;
8769
8770 case T_polygon:
8771 {
8772 SHAPE_LINE_CHAIN outline;
8773
8774 NeedLEFT();
8775 token = NextTok();
8776
8777 if( token != T_pts )
8778 Expecting( T_pts );
8779
8780 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
8781 parseOutlinePoints( outline );
8782
8783 NeedRIGHT();
8784
8785 outline.SetClosed( true );
8786
8787 if( outline.PointCount() == 0 )
8788 break;
8789
8790 // Remark: The first polygon is the main outline.
8791 // Others are holes inside the main outline.
8792 zone->AddPolygon( outline );
8793 break;
8794 }
8795
8796 case T_filled_polygon:
8797 {
8798 // "(filled_polygon (pts"
8799 NeedLEFT();
8800 token = NextTok();
8801
8802 if( token == T_layer )
8803 {
8804 filledLayer = parseBoardItemLayer();
8805 NeedRIGHT();
8806 token = NextTok();
8807
8808 if( token != T_LEFT )
8809 Expecting( T_LEFT );
8810
8811 token = NextTok();
8812 }
8813 else
8814 {
8815 // for legacy, single-layer zones
8816 filledLayer = zone->GetFirstLayer();
8817 }
8818
8819 bool island = false;
8820
8821 if( token == T_island )
8822 {
8823 island = parseMaybeAbsentBool( true );
8824 NeedLEFT();
8825 token = NextTok();
8826 }
8827
8828 if( token != T_pts )
8829 Expecting( T_pts );
8830
8831 if( !pts.count( filledLayer ) )
8832 pts[filledLayer] = SHAPE_POLY_SET();
8833
8834 SHAPE_POLY_SET& poly = pts.at( filledLayer );
8835
8836 int idx = poly.NewOutline();
8837 SHAPE_LINE_CHAIN& chain = poly.Outline( idx );
8838
8839 if( island )
8840 zone->SetIsIsland( filledLayer, idx );
8841
8842 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
8844
8845 NeedRIGHT();
8846
8847 addedFilledPolygons |= !poly.IsEmpty();
8848 }
8849
8850 break;
8851
8852 case T_fill_segments:
8853 {
8854 // Legacy segment fill
8855
8856 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
8857 {
8858 if( token != T_LEFT )
8859 Expecting( T_LEFT );
8860
8861 token = NextTok();
8862
8863 if( token != T_pts )
8864 Expecting( T_pts );
8865
8866 // Legacy zones only had one layer
8867 filledLayer = zone->GetFirstLayer();
8868
8869 SEG fillSegment;
8870
8871 fillSegment.A = parseXY();
8872 fillSegment.B = parseXY();
8873
8874 legacySegs[filledLayer].push_back( fillSegment );
8875
8876 NeedRIGHT();
8877 }
8878
8879 break;
8880 }
8881
8882 case T_name:
8883 NextTok();
8884 zone->SetZoneName( FromUTF8() );
8885 NeedRIGHT();
8886 break;
8887
8888 case T_attr:
8889 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
8890 {
8891 if( token == T_LEFT )
8892 token = NextTok();
8893
8894 switch( token )
8895 {
8896 case T_teardrop:
8897 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
8898 {
8899 if( token == T_LEFT )
8900 token = NextTok();
8901
8902 switch( token )
8903 {
8904 case T_type:
8905 token = NextTok();
8906
8907 if( token == T_padvia )
8908 zone->SetTeardropAreaType( TEARDROP_TYPE::TD_VIAPAD );
8909 else if( token == T_track_end )
8910 zone->SetTeardropAreaType( TEARDROP_TYPE::TD_TRACKEND );
8911 else
8912 Expecting( "padvia or track_end" );
8913
8914 NeedRIGHT();
8915 break;
8916
8917 default:
8918 Expecting( "type" );
8919 }
8920 }
8921
8922 break;
8923
8924 default:
8925 Expecting( "teardrop" );
8926 }
8927 }
8928 break;
8929
8930 case T_locked:
8931 zone->SetLocked( parseBool() );
8932 NeedRIGHT();
8933 break;
8934
8935 default:
8936 Expecting( "net, layer/layers, tstamp, hatch, priority, connect_pads, min_thickness, "
8937 "fill, polygon, filled_polygon, fill_segments, attr, locked, uuid, or name" );
8938 }
8939 }
8940
8941 if( zone->GetNumCorners() > 2 )
8942 {
8943 if( !zone->IsOnCopperLayer() )
8944 {
8945 //zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
8946 zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
8947 }
8948
8949 // Set hatch here, after outlines corners are read
8950 zone->SetBorderDisplayStyle( hatchStyle, hatchPitch, true );
8951 }
8952
8953 if( addedFilledPolygons )
8954 {
8955 if( isStrokedFill && !zone->GetIsRuleArea() )
8956 {
8958 {
8959 m_parseWarnings.push_back( _( "Legacy zone fill strategy is not supported anymore.\n"
8960 "Zone fills will be converted on best-effort basis." ) );
8961
8963 }
8964
8965 if( zone->GetMinThickness() > 0 )
8966 {
8967 for( auto& [layer, polyset] : pts )
8968 {
8969 polyset.InflateWithLinkedHoles( zone->GetMinThickness() / 2,
8971 ARC_HIGH_DEF / 2 );
8972 }
8973 }
8974 }
8975
8976 for( auto& [layer, polyset] : pts )
8977 zone->SetFilledPolysList( layer, polyset );
8978
8979 zone->CalculateFilledArea();
8980 }
8981 else if( legacySegs.size() > 0 )
8982 {
8983 // No polygons, just segment fill?
8984 // Note RFB: This code might be removed if turns out this never existed for sexpr file
8985 // format or otherwise we should add a test case to the qa folder
8986
8988 {
8989 m_parseWarnings.push_back( _( "The legacy segment zone fill mode is no longer supported.\n"
8990 "Zone fills will be converted on a best-effort basis." ) );
8991
8993 }
8994
8995
8996 for( const auto& [layer, segments] : legacySegs )
8997 {
8998 SHAPE_POLY_SET layerFill;
8999
9000 if( zone->HasFilledPolysForLayer( layer ) )
9001 layerFill = SHAPE_POLY_SET( *zone->GetFill( layer ) );
9002
9003 for( const auto& seg : segments )
9004 {
9005 SHAPE_POLY_SET segPolygon;
9006
9007 TransformOvalToPolygon( segPolygon, seg.A, seg.B, zone->GetMinThickness(),
9009
9010 layerFill.BooleanAdd( segPolygon );
9011 }
9012
9013
9014 zone->SetFilledPolysList( layer, layerFill );
9015 zone->CalculateFilledArea();
9016 }
9017 }
9018
9019
9020 // Ensure keepout and non copper zones do not have a net
9021 // (which have no sense for these zones)
9022 // the netcode 0 is used for these zones
9023 bool zone_has_net = zone->IsOnCopperLayer() && !zone->GetIsRuleArea();
9024
9025 if( !zone_has_net )
9026 zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
9027
9028 // In legacy files, ensure the zone net name is valid, and matches the net code
9029 if( !legacyNetnameFromFile.IsEmpty() && zone->GetNetname() != legacyNetnameFromFile )
9030 {
9031 // Can happens which old boards, with nonexistent nets ...
9032 // or after being edited by hand
9033 // We try to fix the mismatch.
9034 NETINFO_ITEM* net = m_board->FindNet( legacyNetnameFromFile );
9035
9036 if( net ) // An existing net has the same net name. use it for the zone
9037 {
9038 zone->SetNetCode( net->GetNetCode() );
9039 }
9040 else // Not existing net: add a new net to keep track of the zone netname
9041 {
9042 int newnetcode = m_board->GetNetCount();
9043 net = new NETINFO_ITEM( m_board, legacyNetnameFromFile, newnetcode );
9044 m_board->Add( net, ADD_MODE::INSERT, true );
9045
9046 // Store the new code mapping
9047 pushValueIntoMap( newnetcode, net->GetNetCode() );
9048
9049 // and update the zone netcode
9050 zone->SetNetCode( net->GetNetCode() );
9051 }
9052 }
9053
9054 if( zone->IsTeardropArea() && m_requiredVersion < 20230517 )
9055 m_board->SetLegacyTeardrops( true );
9056
9057 // Clear flags used in zone edition:
9058 zone->SetNeedRefill( false );
9059
9060 return zone.release();
9061}
9062
9063
9065{
9066 wxCHECK_MSG( CurTok() == T_point, nullptr,
9067 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_POINT." ) );
9068
9069 std::unique_ptr<PCB_POINT> point = std::make_unique<PCB_POINT>( nullptr );
9070
9071 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
9072 {
9073 if( token == T_LEFT )
9074 token = NextTok();
9075
9076 switch( token )
9077 {
9078 case T_at:
9079 {
9080 VECTOR2I pt;
9081 pt.x = parseBoardUnits( "point x position" );
9082 pt.y = parseBoardUnits( "point y position" );
9083 point->SetPosition( pt );
9084 NeedRIGHT();
9085 break;
9086 }
9087 case T_size:
9088 {
9089 point->SetSize( parseBoardUnits( "point size" ) );
9090 NeedRIGHT();
9091 break;
9092 }
9093 case T_layer:
9094 {
9095 point->SetLayer( parseBoardItemLayer() );
9096 NeedRIGHT();
9097 break;
9098 }
9099 case T_uuid:
9100 {
9101 NextTok();
9102 point->SetUuidDirect( CurStrToKIID() );
9103 NeedRIGHT();
9104 break;
9105 }
9106 default: Expecting( "at, size, layer or uuid" );
9107 }
9108 }
9109
9110 return point.release();
9111}
9112
9113
9115{
9116 wxCHECK_MSG( CurTok() == T_target, nullptr,
9117 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TARGET." ) );
9118
9119 VECTOR2I pt;
9120 T token;
9121
9122 std::unique_ptr<PCB_TARGET> target = std::make_unique<PCB_TARGET>( nullptr );
9123
9124 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
9125 {
9126 if( token == T_LEFT )
9127 token = NextTok();
9128
9129 switch( token )
9130 {
9131 case T_x:
9132 target->SetShape( 1 );
9133 break;
9134
9135 case T_plus:
9136 target->SetShape( 0 );
9137 break;
9138
9139 case T_at:
9140 pt.x = parseBoardUnits( "target x position" );
9141 pt.y = parseBoardUnits( "target y position" );
9142 target->SetPosition( pt );
9143 NeedRIGHT();
9144 break;
9145
9146 case T_size:
9147 target->SetSize( parseBoardUnits( "target size" ) );
9148 NeedRIGHT();
9149 break;
9150
9151 case T_width:
9152 target->SetWidth( parseBoardUnits( "target thickness" ) );
9153 NeedRIGHT();
9154 break;
9155
9156 case T_layer:
9157 target->SetLayer( parseBoardItemLayer() );
9158 NeedRIGHT();
9159 break;
9160
9161 case T_tstamp:
9162 case T_uuid:
9163 NextTok();
9164 target->SetUuidDirect( CurStrToKIID() );
9165 NeedRIGHT();
9166 break;
9167
9168 default:
9169 Expecting( "x, plus, at, size, width, layer, uuid, or tstamp" );
9170 }
9171 }
9172
9173 return target.release();
9174}
9175
9176
9178{
9179 KIID aId;
9180 std::string idStr( CurStr() );
9181
9182 // Older files did not quote UUIDs
9183 if( *idStr.begin() == '"' && *idStr.rbegin() == '"' )
9184 idStr = idStr.substr( 1, idStr.length() - 1 );
9185
9186 if( m_appendToExisting )
9187 {
9188 aId = KIID();
9189 m_resetKIIDMap.insert( std::make_pair( idStr, aId ) );
9190 }
9191 else
9192 {
9193 aId = KIID( idStr );
9194 }
9195
9196 return aId;
9197}
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:528
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
virtual EMBEDDED_FILES * GetEmbeddedFiles()
Definition eda_item.h:480
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:532
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:576
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:524
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:97
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition netinfo.h:259
static const int ORPHANED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition netinfo.h:263
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:55
void SetAnchorPadShape(PCB_LAYER_ID aLayer, PAD_SHAPE aShape)
Set the shape of the anchor pad for custom shaped pads.
Definition pad.h:243
void SetShape(PCB_LAYER_ID aLayer, PAD_SHAPE aShape)
Set the new shape of this pad.
Definition pad.h:187
void SetDelta(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition pad.h:298
void SetOffset(PCB_LAYER_ID aLayer, const VECTOR2I &aOffset)
Definition pad.h:323
void SetCustomShapeInZoneOpt(CUSTOM_SHAPE_ZONE_MODE aOption)
Set the option for the custom pad shape to use as clearance area in copper zones.
Definition pad.h:232
void SetChamferRectRatio(PCB_LAYER_ID aLayer, double aChamferScale)
Has meaning only for chamfered rectangular pads.
Definition pad.cpp:940
const PADSTACK & Padstack() const
Definition pad.h:333
void SetDrillSize(const VECTOR2I &aSize)
Definition pad.h:316
void SetSize(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition pad.h:259
void SetChamferPositions(PCB_LAYER_ID aLayer, int aPositions)
Has meaning only for chamfered rectangular pads.
Definition pad.h:840
void SetRoundRectRadiusRatio(PCB_LAYER_ID aLayer, double aRadiusScale)
Has meaning only for rounded rectangle pads.
Definition pad.cpp:902
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_MODEL * model
KIBIS top(path, &reporter)
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