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