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