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