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