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