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: // legacy token no longer used
3726 NeedRIGHT();
3727 break;
3728
3729 case T_layer:
3730 table->SetLayer( parseBoardItemLayer() );
3731 NeedRIGHT();
3732 break;
3733
3734 case T_column_widths:
3735 {
3736 int col = 0;
3737
3738 while( ( token = NextTok() ) != T_RIGHT )
3739 table->SetColWidth( col++, parseBoardUnits() );
3740
3741 break;
3742 }
3743
3744 case T_row_heights:
3745 {
3746 int row = 0;
3747
3748 while( ( token = NextTok() ) != T_RIGHT )
3749 table->SetRowHeight( row++, parseBoardUnits() );
3750
3751 break;
3752 }
3753
3754 case T_cells:
3755 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3756 {
3757 if( token != T_LEFT )
3758 Expecting( T_LEFT );
3759
3760 token = NextTok();
3761
3762 if( token != T_table_cell )
3763 Expecting( "table_cell" );
3764
3765 table->AddCell( parsePCB_TABLECELL( table.get() ) );
3766 }
3767
3768 break;
3769
3770 case T_border:
3771 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3772 {
3773 if( token != T_LEFT )
3774 Expecting( T_LEFT );
3775
3776 token = NextTok();
3777
3778 switch( token )
3779 {
3780 case T_external:
3781 table->SetStrokeExternal( parseBool() );
3782 NeedRIGHT();
3783 break;
3784
3785 case T_header:
3786 table->SetStrokeHeader( parseBool() );
3787 NeedRIGHT();
3788 break;
3789
3790 case T_stroke:
3791 {
3792 STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
3793 strokeParser.SyncLineReaderWith( *this );
3794
3795 strokeParser.ParseStroke( borderStroke );
3796 SyncLineReaderWith( strokeParser );
3797
3798 table->SetBorderStroke( borderStroke );
3799 break;
3800 }
3801
3802 default:
3803 Expecting( "external, header or stroke" );
3804 break;
3805 }
3806 }
3807
3808 break;
3809
3810 case T_separators:
3811 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3812 {
3813 if( token != T_LEFT )
3814 Expecting( T_LEFT );
3815
3816 token = NextTok();
3817
3818 switch( token )
3819 {
3820 case T_rows:
3821 table->SetStrokeRows( parseBool() );
3822 NeedRIGHT();
3823 break;
3824
3825 case T_cols:
3826 table->SetStrokeColumns( parseBool() );
3827 NeedRIGHT();
3828 break;
3829
3830 case T_stroke:
3831 {
3832 STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
3833 strokeParser.SyncLineReaderWith( *this );
3834
3835 strokeParser.ParseStroke( separatorsStroke );
3836 SyncLineReaderWith( strokeParser );
3837
3838 table->SetSeparatorsStroke( separatorsStroke );
3839 break;
3840 }
3841
3842 default:
3843 Expecting( "rows, cols, or stroke" );
3844 break;
3845 }
3846 }
3847
3848 break;
3849
3850 default:
3851 Expecting( "columns, layer, col_widths, row_heights, border, separators, header or "
3852 "cells" );
3853 }
3854 }
3855
3856 return table.release();
3857}
3858
3859
3861{
3862 wxCHECK_MSG( CurTok() == T_dimension, nullptr,
3863 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as DIMENSION." ) );
3864
3865 T token;
3866 bool locked = false;
3867 std::unique_ptr<PCB_DIMENSION_BASE> dim;
3868
3869 token = NextTok();
3870
3871 // Free 'locked' token from 6.0/7.0 formats
3872 if( token == T_locked )
3873 {
3874 locked = true;
3875 token = NextTok();
3876 }
3877
3878 // skip value that used to be saved
3879 if( token != T_LEFT )
3880 NeedLEFT();
3881
3882 token = NextTok();
3883
3884 bool isLegacyDimension = false;
3885 bool isStyleKnown = false;
3886
3887 // Old format
3888 if( token == T_width )
3889 {
3890 isLegacyDimension = true;
3891 dim = std::make_unique<PCB_DIM_ALIGNED>( aParent );
3892 dim->SetLineThickness( parseBoardUnits( "dimension width value" ) );
3893 NeedRIGHT();
3894 }
3895 else
3896 {
3897 if( token != T_type )
3898 Expecting( T_type );
3899
3900 switch( NextTok() )
3901 {
3902 case T_aligned: dim = std::make_unique<PCB_DIM_ALIGNED>( aParent ); break;
3903 case T_orthogonal: dim = std::make_unique<PCB_DIM_ORTHOGONAL>( aParent ); break;
3904 case T_leader: dim = std::make_unique<PCB_DIM_LEADER>( aParent ); break;
3905 case T_center: dim = std::make_unique<PCB_DIM_CENTER>( aParent ); break;
3906 case T_radial: dim = std::make_unique<PCB_DIM_RADIAL>( aParent ); break;
3907 default: wxFAIL_MSG( wxT( "Cannot parse unknown dimension type " )
3908 + GetTokenString( CurTok() ) );
3909 }
3910
3911 NeedRIGHT();
3912
3913 // Before parsing further, set default properites for old KiCad file
3914 // versions that didnt have these properties:
3915 dim->SetArrowDirection( DIM_ARROW_DIRECTION::OUTWARD );
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( parseMaybeAbsentBool( true ) );
4085 break;
4086
4087 default:
4088 std::cerr << "Unknown format token: " << GetTokenString( token ) << std::endl;
4089 Expecting( "prefix, suffix, units, units_format, precision, override_value, "
4090 "suppress_zeroes" );
4091 }
4092 }
4093 break;
4094 }
4095
4096 case T_style:
4097 {
4098 isStyleKnown = true;
4099
4100 // new format: default to keep text aligned off unless token is present
4101 dim->SetKeepTextAligned( false );
4102
4103 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4104 {
4105 switch( token )
4106 {
4107 case T_LEFT:
4108 continue;
4109
4110 case T_thickness:
4111 dim->SetLineThickness( parseBoardUnits( "extension line thickness value" ) );
4112 NeedRIGHT();
4113 break;
4114
4115 case T_arrow_direction:
4116 {
4117 token = NextTok();
4118
4119 if( token == T_inward )
4120 dim->ChangeArrowDirection( DIM_ARROW_DIRECTION::INWARD );
4121 else if( token == T_outward )
4122 dim->ChangeArrowDirection( DIM_ARROW_DIRECTION::OUTWARD );
4123 else
4124 Expecting( "inward or outward" );
4125
4126 NeedRIGHT();
4127 break;
4128 }
4129 case T_arrow_length:
4130
4131 dim->SetArrowLength( parseBoardUnits( "arrow length value" ) );
4132 NeedRIGHT();
4133 break;
4134
4135 case T_text_position_mode:
4136 {
4137 int mode = parseInt( "text position mode" );
4138 mode = std::max( 0, std::min( 3, mode ) );
4139 dim->SetTextPositionMode( static_cast<DIM_TEXT_POSITION>( mode ) );
4140 NeedRIGHT();
4141 break;
4142 }
4143
4144 case T_extension_height:
4145 {
4146 PCB_DIM_ALIGNED* aligned = dynamic_cast<PCB_DIM_ALIGNED*>( dim.get() );
4147 wxCHECK_MSG( aligned, nullptr, wxT( "Invalid extension_height token" ) );
4148 aligned->SetExtensionHeight( parseBoardUnits( "extension height value" ) );
4149 NeedRIGHT();
4150 break;
4151 }
4152
4153 case T_extension_offset:
4154 dim->SetExtensionOffset( parseBoardUnits( "extension offset value" ) );
4155 NeedRIGHT();
4156 break;
4157
4158 case T_keep_text_aligned:
4159 dim->SetKeepTextAligned( parseMaybeAbsentBool( true ) );
4160 break;
4161
4162 case T_text_frame:
4163 {
4164 wxCHECK_MSG( dim->Type() == PCB_DIM_LEADER_T, nullptr,
4165 wxT( "Invalid text_frame token" ) );
4166
4167 PCB_DIM_LEADER* leader = static_cast<PCB_DIM_LEADER*>( dim.get() );
4168
4169 int textFrame = parseInt( "text frame mode" );
4170 textFrame = std::clamp( textFrame, 0, 3 );
4171 leader->SetTextBorder( static_cast<DIM_TEXT_BORDER>( textFrame ));
4172 NeedRIGHT();
4173 break;
4174 }
4175
4176 default:
4177 Expecting( "thickness, arrow_length, arrow_direction, text_position_mode, "
4178 "extension_height, extension_offset" );
4179 }
4180 }
4181
4182 break;
4183 }
4184
4185 // Old format: feature1 stores a feature line. We only care about the origin.
4186 case T_feature1:
4187 {
4188 NeedLEFT();
4189 token = NextTok();
4190
4191 if( token != T_pts )
4192 Expecting( T_pts );
4193
4194 VECTOR2I point;
4195
4196 parseXY( &point.x, &point.y );
4197 dim->SetStart( point );
4198
4199 parseXY( nullptr, nullptr ); // Ignore second point
4200 NeedRIGHT();
4201 NeedRIGHT();
4202 break;
4203 }
4204
4205 // Old format: feature2 stores a feature line. We only care about the end point.
4206 case T_feature2:
4207 {
4208 NeedLEFT();
4209 token = NextTok();
4210
4211 if( token != T_pts )
4212 Expecting( T_pts );
4213
4214 VECTOR2I point;
4215
4216 parseXY( &point.x, &point.y );
4217 dim->SetEnd( point );
4218
4219 parseXY( nullptr, nullptr ); // Ignore second point
4220
4221 NeedRIGHT();
4222 NeedRIGHT();
4223 break;
4224 }
4225
4226 case T_crossbar:
4227 {
4228 NeedLEFT();
4229 token = NextTok();
4230
4231 if( token == T_pts )
4232 {
4233 // If we have a crossbar, we know we're an old aligned dim
4234 PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dim.get() );
4235
4236 // Old style: calculate height from crossbar
4237 VECTOR2I point1, point2;
4238 parseXY( &point1.x, &point1.y );
4239 parseXY( &point2.x, &point2.y );
4240 aligned->UpdateHeight( point2, point1 ); // Yes, backwards intentionally
4241 NeedRIGHT();
4242 }
4243
4244 NeedRIGHT();
4245 break;
4246 }
4247
4248 // Arrow: no longer saved; no-op
4249 case T_arrow1a:
4250 NeedLEFT();
4251 token = NextTok();
4252
4253 if( token != T_pts )
4254 Expecting( T_pts );
4255
4256 parseXY( nullptr, nullptr );
4257 parseXY( nullptr, nullptr );
4258 NeedRIGHT();
4259 NeedRIGHT();
4260 break;
4261
4262 // Arrow: no longer saved; no-op
4263 case T_arrow1b:
4264 NeedLEFT();
4265 token = NextTok();
4266
4267 if( token != T_pts )
4268 Expecting( T_pts );
4269
4270 parseXY( nullptr, nullptr );
4271 parseXY( nullptr, nullptr );
4272 NeedRIGHT();
4273 NeedRIGHT();
4274 break;
4275
4276 // Arrow: no longer saved; no-op
4277 case T_arrow2a:
4278 NeedLEFT();
4279 token = NextTok();
4280
4281 if( token != T_pts )
4282 Expecting( T_pts );
4283
4284 parseXY( nullptr, nullptr );
4285 parseXY( nullptr, nullptr );
4286 NeedRIGHT();
4287 NeedRIGHT();
4288 break;
4289
4290 // Arrow: no longer saved; no-op
4291 case T_arrow2b:
4292 NeedLEFT();
4293 token = NextTok();
4294
4295 if( token != T_pts )
4296 Expecting( T_pts );
4297
4298 parseXY( nullptr, nullptr );
4299 parseXY( nullptr, nullptr );
4300 NeedRIGHT();
4301 NeedRIGHT();
4302 break;
4303
4304 // Handle (locked yes) from modern times
4305 case T_locked:
4306 {
4307 // Unsure if we ever wrote out (locked) for dimensions, so use maybeAbsent just in case
4308 bool isLocked = parseMaybeAbsentBool( true );
4309 dim->SetLocked( isLocked );
4310 break;
4311 }
4312
4313 default:
4314 Expecting( "layer, tstamp, uuid, gr_text, feature1, feature2, crossbar, arrow1a, "
4315 "arrow1b, arrow2a, or arrow2b" );
4316 }
4317 }
4318
4319 if( locked )
4320 dim->SetLocked( true );
4321
4322 dim->Update();
4323
4324 return dim.release();
4325}
4326
4327
4328FOOTPRINT* PCB_IO_KICAD_SEXPR_PARSER::parseFOOTPRINT( wxArrayString* aInitialComments )
4329{
4330 try
4331 {
4332 return parseFOOTPRINT_unchecked( aInitialComments );
4333 }
4334 catch( const PARSE_ERROR& parse_error )
4335 {
4336 if( m_tooRecent )
4337 throw FUTURE_FORMAT_ERROR( parse_error, GetRequiredVersion() );
4338 else
4339 throw;
4340 }
4341}
4342
4343
4345{
4346 wxCHECK_MSG( CurTok() == T_module || CurTok() == T_footprint, nullptr,
4347 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as FOOTPRINT." ) );
4348
4349 wxString name;
4350 VECTOR2I pt;
4351 T token;
4352 LIB_ID fpid;
4353 int attributes = 0;
4354
4355 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
4356
4357 footprint->SetInitialComments( aInitialComments );
4358
4359 if( m_board )
4360 {
4361 footprint->SetComponentClass( m_board->GetComponentClassManager().GetNoneComponentClass() );
4362 }
4363
4364 token = NextTok();
4365
4366 if( !IsSymbol( token ) && token != T_NUMBER )
4367 Expecting( "symbol|number" );
4368
4369 name = FromUTF8();
4370
4371 if( !name.IsEmpty() && fpid.Parse( name, true ) >= 0 )
4372 {
4373 THROW_IO_ERROR( wxString::Format( _( "Invalid footprint ID in\nfile: %s\nline: %d\n"
4374 "offset: %d." ),
4375 CurSource(), CurLineNumber(), CurOffset() ) );
4376 }
4377
4378 auto checkVersion =
4379 [&]()
4380 {
4382 {
4383 throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
4385 }
4386 };
4387
4388 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4389 {
4390 if( token == T_LEFT )
4391 token = NextTok();
4392
4393 switch( token )
4394 {
4395 case T_version:
4396 {
4397 // Theoretically a footprint nested in a PCB could declare its own version, though
4398 // as of writing this comment we don't do that. Just in case, take the greater
4399 // version.
4400 int this_version = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
4401 NeedRIGHT();
4402 m_requiredVersion = std::max( m_requiredVersion, this_version );
4404 footprint->SetFileFormatVersionAtLoad( this_version );
4405 break;
4406 }
4407
4408 case T_generator:
4409 // We currently ignore the generator when parsing. It is included in the file for manual
4410 // indication of where the footprint came from.
4411 NeedSYMBOL();
4412 NeedRIGHT();
4413 break;
4414
4415 case T_generator_version:
4416 {
4417 NeedSYMBOL();
4418 m_generatorVersion = FromUTF8();
4419 NeedRIGHT();
4420
4421 // If the format includes a generator version, by this point we have enough info to
4422 // do the version check here
4423 checkVersion();
4424
4425 break;
4426 }
4427
4428 case T_locked:
4429 footprint->SetLocked( parseMaybeAbsentBool( true ) );
4430 break;
4431
4432 case T_placed:
4433 footprint->SetIsPlaced( parseMaybeAbsentBool( true ) );
4434 break;
4435
4436 case T_layer:
4437 {
4438 // Footprints can be only on the front side or the back side.
4439 // but because we can find some stupid layer in file, ensure a
4440 // acceptable layer is set for the footprint
4442 footprint->SetLayer( layer == B_Cu ? B_Cu : F_Cu );
4443 NeedRIGHT();
4444 break;
4445 }
4446
4447 case T_tedit:
4448 parseHex();
4449 NeedRIGHT();
4450 break;
4451
4452 case T_tstamp:
4453 case T_uuid:
4454 NextTok();
4455 const_cast<KIID&>( footprint->m_Uuid ) = CurStrToKIID();
4456 NeedRIGHT();
4457 break;
4458
4459 case T_at:
4460 pt.x = parseBoardUnits( "X coordinate" );
4461 pt.y = parseBoardUnits( "Y coordinate" );
4462 footprint->SetPosition( pt );
4463 token = NextTok();
4464
4465 if( token == T_NUMBER )
4466 {
4467 footprint->SetOrientation( EDA_ANGLE( parseDouble(), DEGREES_T ) );
4468 NeedRIGHT();
4469 }
4470 else if( token != T_RIGHT )
4471 {
4472 Expecting( T_RIGHT );
4473 }
4474
4475 break;
4476
4477 case T_descr:
4478 NeedSYMBOLorNUMBER(); // some symbols can be 0508, so a number is also a symbol here
4479 footprint->SetLibDescription( FromUTF8() );
4480 NeedRIGHT();
4481 break;
4482
4483 case T_tags:
4484 NeedSYMBOLorNUMBER(); // some symbols can be 0508, so a number is also a symbol here
4485 footprint->SetKeywords( FromUTF8() );
4486 NeedRIGHT();
4487 break;
4488
4489 case T_property:
4490 {
4491 wxString value;
4492
4493 NeedSYMBOL();
4494 wxString pName = FromUTF8();
4495 NeedSYMBOL();
4496 wxString pValue = FromUTF8();
4497
4498 // Prior to PCB fields, we used to use properties for special values instead of
4499 // using (keyword_example "value")
4500 if( m_requiredVersion < 20230620 )
4501 {
4502 // Skip legacy non-field properties sent from symbols that should not be kept
4503 // in footprints.
4504 if( pName == "ki_keywords" || pName == "ki_locked" )
4505 {
4506 NeedRIGHT();
4507 break;
4508 }
4509
4510 // Description from symbol (not the fooprint library description stored in (descr) )
4511 // used to be stored as a reserved key value
4512 if( pName == "ki_description" )
4513 {
4514 footprint->GetFieldById( DESCRIPTION_FIELD )->SetText( pValue );
4515 NeedRIGHT();
4516 break;
4517 }
4518
4519 // Sheet file and name used to be stored as properties invisible to the user
4520 if( pName == "Sheetfile" || pName == "Sheet file" )
4521 {
4522 footprint->SetSheetfile( pValue );
4523 NeedRIGHT();
4524 break;
4525 }
4526
4527 if( pName == "Sheetname" || pName == "Sheet name" )
4528 {
4529 footprint->SetSheetname( pValue );
4530 NeedRIGHT();
4531 break;
4532 }
4533 }
4534
4535 // 8.0.0rc3 had a bug where these properties were mistakenly added to the footprint as fields,
4536 // this will remove them as fields but still correctly set the footprint filters
4537 if( pName == "ki_fp_filters" )
4538 {
4539 footprint->SetFilters( pValue );
4540
4541 // Use the text effect parsing function because it will handle ki_fp_filters as a property
4542 // with no text effects, but will also handle parsing the text effects. We just drop the effects
4543 // if they're present.
4544 PCB_FIELD ignored = PCB_FIELD( footprint.get(), footprint->GetFieldCount(), pName );
4545
4546 parsePCB_TEXT_effects( &ignored );
4547
4548 // This doesn't currently happen, but if it does, we don't want to leave a dangling pointer in the map
4549 if( auto it = m_fontTextMap.find( &ignored ); it != m_fontTextMap.end() )
4550 m_fontTextMap.erase( it );
4551
4552 break;
4553 }
4554
4555 PCB_FIELD* field = nullptr;
4556 std::unique_ptr<PCB_FIELD> unusedField;
4557
4558 if( pName == "Footprint" )
4559 {
4560 // Until V9, footprints had a Footprint field that usually (but not always)
4561 // duplicated the footprint's LIB_ID. In V9 this was removed. Parse it
4562 // like any other, but don't add it to anything.
4563 unusedField = std::make_unique<PCB_FIELD>( footprint.get(), 0 );
4564 field = unusedField.get();
4565 }
4566 else if( footprint->HasFieldByName( pName ) )
4567 {
4568 field = footprint->GetFieldByName( pName );
4569 field->SetText( pValue );
4570 }
4571 else
4572 {
4573 field = footprint->AddField( PCB_FIELD( footprint.get(), footprint->GetFieldCount(),
4574 pName ) );
4575
4576 field->SetText( pValue );
4577 field->SetLayer( footprint->GetLayer() == F_Cu ? F_Fab : B_Fab );
4578
4579 if( m_board ) // can be null when reading a lib
4581 }
4582
4583 // Hide the field by default if it is a legacy field that did not have
4584 // text effects applied, since hide is a negative effect
4585 if( m_requiredVersion < 20230620 )
4586 field->SetVisible( false );
4587 else
4588 field->SetVisible( true );
4589
4590 parsePCB_TEXT_effects( field );
4591 }
4592 break;
4593
4594 case T_path:
4595 NeedSYMBOLorNUMBER(); // Paths can be numerical so a number is also a symbol here
4596 footprint->SetPath( KIID_PATH( FromUTF8() ) );
4597 NeedRIGHT();
4598 break;
4599
4600 case T_sheetname:
4601 NeedSYMBOL();
4602 footprint->SetSheetname( FromUTF8() );
4603 NeedRIGHT();
4604 break;
4605
4606 case T_sheetfile:
4607 NeedSYMBOL();
4608 footprint->SetSheetfile( FromUTF8() );
4609 NeedRIGHT();
4610 break;
4611
4612 case T_autoplace_cost90:
4613 case T_autoplace_cost180:
4614 parseInt( "legacy auto-place cost" );
4615 NeedRIGHT();
4616 break;
4617
4618 case T_private_layers:
4619 {
4620 LSET privateLayers;
4621
4622 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4623 {
4624 auto it = m_layerIndices.find( CurStr() );
4625
4626 if( it != m_layerIndices.end() )
4627 privateLayers.set( it->second );
4628 else
4629 Expecting( "layer name" );
4630 }
4631
4632 if( m_requiredVersion < 20220427 )
4633 {
4634 privateLayers.set( Edge_Cuts, false );
4635 privateLayers.set( Margin, false );
4636 }
4637
4638 footprint->SetPrivateLayers( privateLayers );
4639 break;
4640 }
4641
4642 case T_net_tie_pad_groups:
4643 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4644 footprint->AddNetTiePadGroup( CurStr() );
4645
4646 break;
4647
4648 case T_solder_mask_margin:
4649 footprint->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
4650 NeedRIGHT();
4651
4652 // In pre-9.0 files "0" meant inherit.
4653 if( m_requiredVersion <= 20240201 && footprint->GetLocalSolderMaskMargin() == 0 )
4654 footprint->SetLocalSolderMaskMargin( {} );
4655
4656 break;
4657
4658 case T_solder_paste_margin:
4659 footprint->SetLocalSolderPasteMargin( parseBoardUnits( "local solder paste margin value" ) );
4660 NeedRIGHT();
4661
4662 // In pre-9.0 files "0" meant inherit.
4663 if( m_requiredVersion <= 20240201 && footprint->GetLocalSolderPasteMargin() == 0 )
4664 footprint->SetLocalSolderPasteMargin( {} );
4665
4666 break;
4667
4668 case T_solder_paste_ratio: // legacy token
4669 case T_solder_paste_margin_ratio:
4670 footprint->SetLocalSolderPasteMarginRatio( parseDouble( "local solder paste margin ratio value" ) );
4671 NeedRIGHT();
4672
4673 // In pre-9.0 files "0" meant inherit.
4674 if( m_requiredVersion <= 20240201 && footprint->GetLocalSolderPasteMarginRatio() == 0 )
4675 footprint->SetLocalSolderPasteMarginRatio( {} );
4676
4677 break;
4678
4679 case T_clearance:
4680 footprint->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
4681 NeedRIGHT();
4682
4683 // In pre-9.0 files "0" meant inherit.
4684 if( m_requiredVersion <= 20240201 && footprint->GetLocalClearance() == 0 )
4685 footprint->SetLocalClearance( {} );
4686
4687 break;
4688
4689 case T_zone_connect:
4690 footprint->SetLocalZoneConnection((ZONE_CONNECTION) parseInt( "zone connection value" ) );
4691 NeedRIGHT();
4692 break;
4693
4694 case T_thermal_width:
4695 case T_thermal_gap:
4696 // Interestingly, these have never been exposed in the GUI
4697 parseBoardUnits( token );
4698 NeedRIGHT();
4699 break;
4700
4701 case T_attr:
4702 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4703 {
4704 switch( token )
4705 {
4706 case T_virtual: // legacy token prior to version 20200826
4708 break;
4709
4710 case T_through_hole:
4711 attributes |= FP_THROUGH_HOLE;
4712 break;
4713
4714 case T_smd:
4715 attributes |= FP_SMD;
4716 break;
4717
4718 case T_board_only:
4719 attributes |= FP_BOARD_ONLY;
4720 break;
4721
4722 case T_exclude_from_pos_files:
4723 attributes |= FP_EXCLUDE_FROM_POS_FILES;
4724 break;
4725
4726 case T_exclude_from_bom:
4727 attributes |= FP_EXCLUDE_FROM_BOM;
4728 break;
4729
4730 case T_allow_missing_courtyard:
4731 attributes |= FP_ALLOW_MISSING_COURTYARD;
4732 break;
4733
4734 case T_dnp:
4735 attributes |= FP_DNP;
4736 break;
4737
4738 case T_allow_soldermask_bridges:
4739 attributes |= FP_ALLOW_SOLDERMASK_BRIDGES;
4740 break;
4741
4742 default:
4743 Expecting( "through_hole, smd, virtual, board_only, exclude_from_pos_files, "
4744 "exclude_from_bom or allow_solder_mask_bridges" );
4745 }
4746 }
4747
4748 break;
4749
4750 case T_fp_text:
4751 {
4752 PCB_TEXT* text = parsePCB_TEXT( footprint.get() );
4753
4754 if( PCB_FIELD* field = dynamic_cast<PCB_FIELD*>( text ) )
4755 {
4756 // Fields other than reference and value weren't historically
4757 // stored in fp_texts so we don't need to handle them here
4758 switch( field->GetId() )
4759 {
4760 case REFERENCE_FIELD:
4761 footprint->Reference() = PCB_FIELD( *text, REFERENCE_FIELD );
4762 const_cast<KIID&>( footprint->Reference().m_Uuid ) = text->m_Uuid;
4763 // We don't want to leave a dangling pointer in the map
4764 removeReplaceEntryInFontTextMap( text, &footprint->Reference() );
4765 delete text;
4766 break;
4767
4768 case VALUE_FIELD:
4769 footprint->Value() = PCB_FIELD( *text, VALUE_FIELD );
4770 const_cast<KIID&>( footprint->Value().m_Uuid ) = text->m_Uuid;
4771 removeReplaceEntryInFontTextMap( text, &footprint->Value() );
4772 delete text;
4773 break;
4774 }
4775 }
4776 else
4777 footprint->Add( text, ADD_MODE::APPEND, true );
4778
4779 break;
4780 }
4781
4782 case T_fp_text_box:
4783 {
4784 PCB_TEXTBOX* textbox = parsePCB_TEXTBOX( footprint.get() );
4785 footprint->Add( textbox, ADD_MODE::APPEND, true );
4786 break;
4787 }
4788
4789 case T_table:
4790 {
4791 PCB_TABLE* table = parsePCB_TABLE( footprint.get() );
4792 footprint->Add( table, ADD_MODE::APPEND, true );
4793 break;
4794 }
4795
4796 case T_fp_arc:
4797 case T_fp_circle:
4798 case T_fp_curve:
4799 case T_fp_rect:
4800 case T_fp_line:
4801 case T_fp_poly:
4802 {
4803 PCB_SHAPE* shape = parsePCB_SHAPE( footprint.get() );
4804 footprint->Add( shape, ADD_MODE::APPEND, true );
4805 break;
4806 }
4807
4808 case T_image:
4809 {
4811 footprint->Add( image, ADD_MODE::APPEND, true );
4812 break;
4813 }
4814
4815 case T_dimension:
4816 {
4817 PCB_DIMENSION_BASE* dimension = parseDIMENSION( footprint.get() );
4818 footprint->Add( dimension, ADD_MODE::APPEND, true );
4819 break;
4820 }
4821
4822 case T_pad:
4823 {
4824 PAD* pad = parsePAD( footprint.get() );
4825 footprint->Add( pad, ADD_MODE::APPEND, true );
4826 break;
4827 }
4828
4829 case T_model:
4830 {
4831 FP_3DMODEL* model = parse3DModel();
4832 footprint->Add3DModel( model );
4833 delete model;
4834 break;
4835 }
4836
4837 case T_zone:
4838 {
4839 ZONE* zone = parseZONE( footprint.get() );
4840 footprint->Add( zone, ADD_MODE::APPEND, true );
4841 break;
4842 }
4843
4844 case T_group:
4845 parseGROUP( footprint.get() );
4846 break;
4847
4848 case T_embedded_fonts:
4849 {
4850 footprint->GetEmbeddedFiles()->SetAreFontsEmbedded( parseBool() );
4851 NeedRIGHT();
4852 break;
4853 }
4854
4855 case T_embedded_files:
4856 {
4857 EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
4858 embeddedFilesParser.SyncLineReaderWith( *this );
4859
4860 try
4861 {
4862 embeddedFilesParser.ParseEmbedded( footprint->GetEmbeddedFiles() );
4863 }
4864 catch( const PARSE_ERROR& e )
4865 {
4866 wxLogError( e.What() );
4867 }
4868
4869 SyncLineReaderWith( embeddedFilesParser );
4870 break;
4871 }
4872
4873 case T_component_classes:
4874 {
4875 std::unordered_set<wxString> componentClassNames;
4876
4877 while( ( token = NextTok() ) != T_RIGHT )
4878 {
4879 if( token != T_LEFT )
4880 Expecting( T_LEFT );
4881
4882 if( ( token = NextTok() ) != T_class )
4883 Expecting( T_class );
4884
4885 NeedSYMBOLorNUMBER();
4886 componentClassNames.insert( From_UTF8( CurText() ) );
4887 NeedRIGHT();
4888 }
4889
4890 if( m_board )
4891 {
4892 const COMPONENT_CLASS* componentClass =
4894 componentClassNames );
4895 footprint->SetComponentClass( componentClass );
4896 }
4897
4898 break;
4899 }
4900
4901 default:
4902 Expecting( "at, descr, locked, placed, tedit, tstamp, uuid, "
4903 "autoplace_cost90, autoplace_cost180, attr, clearance, "
4904 "embedded_files, fp_arc, fp_circle, fp_curve, fp_line, fp_poly, "
4905 "fp_rect, fp_text, pad, group, generator, model, path, solder_mask_margin, "
4906 "solder_paste_margin, solder_paste_margin_ratio, tags, thermal_gap, "
4907 "version, zone, zone_connect, or component_classes" );
4908 }
4909 }
4910
4911 // In legacy files the lack of attributes indicated a through-hole component which was by
4912 // default excluded from pos files. However there was a hack to look for SMD pads and
4913 // consider those "mislabeled through-hole components" and therefore include them in place
4914 // files. We probably don't want to get into that game so we'll just include them by
4915 // default and let the user change it if required.
4916 if( m_requiredVersion < 20200826 && attributes == 0 )
4917 attributes |= FP_THROUGH_HOLE;
4918
4920 {
4921 if( footprint->GetKeywords().StartsWith( wxT( "net tie" ) ) )
4922 {
4923 wxString padGroup;
4924
4925 for( PAD* pad : footprint->Pads() )
4926 {
4927 if( !padGroup.IsEmpty() )
4928 padGroup += wxS( ", " );
4929
4930 padGroup += pad->GetNumber();
4931 }
4932
4933 if( !padGroup.IsEmpty() )
4934 footprint->AddNetTiePadGroup( padGroup );
4935 }
4936 }
4937
4938 footprint->SetAttributes( attributes );
4939
4940 footprint->SetFPID( fpid );
4941
4942 return footprint.release();
4943}
4944
4945
4947{
4948 wxCHECK_MSG( CurTok() == T_pad, nullptr,
4949 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PAD." ) );
4950
4951 VECTOR2I sz;
4952 VECTOR2I pt;
4953 bool foundNet = false;
4954
4955 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aParent );
4956
4957 NeedSYMBOLorNUMBER();
4958 pad->SetNumber( FromUTF8() );
4959
4960 T token = NextTok();
4961
4962 switch( token )
4963 {
4964 case T_thru_hole:
4965 pad->SetAttribute( PAD_ATTRIB::PTH );
4966
4967 // The drill token is usually missing if 0 drill size is specified.
4968 // Emulate it using 1 nm drill size to avoid errors.
4969 // Drill size cannot be set to 0 in newer versions.
4970 pad->SetDrillSize( VECTOR2I( 1, 1 ) );
4971 break;
4972
4973 case T_smd:
4974 pad->SetAttribute( PAD_ATTRIB::SMD );
4975
4976 // Default PAD object is thru hole with drill.
4977 // SMD pads have no hole
4978 pad->SetDrillSize( VECTOR2I( 0, 0 ) );
4979 break;
4980
4981 case T_connect:
4982 pad->SetAttribute( PAD_ATTRIB::CONN );
4983
4984 // Default PAD object is thru hole with drill.
4985 // CONN pads have no hole
4986 pad->SetDrillSize( VECTOR2I( 0, 0 ) );
4987 break;
4988
4989 case T_np_thru_hole:
4990 pad->SetAttribute( PAD_ATTRIB::NPTH );
4991 break;
4992
4993 default:
4994 Expecting( "thru_hole, smd, connect, or np_thru_hole" );
4995 }
4996
4997 token = NextTok();
4998
4999 switch( token )
5000 {
5001 case T_circle:
5002 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
5003 break;
5004
5005 case T_rect:
5006 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::RECTANGLE );
5007 break;
5008
5009 case T_oval:
5010 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::OVAL );
5011 break;
5012
5013 case T_trapezoid:
5014 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::TRAPEZOID );
5015 break;
5016
5017 case T_roundrect:
5018 // Note: the shape can be PAD_SHAPE::ROUNDRECT or PAD_SHAPE::CHAMFERED_RECT
5019 // (if chamfer parameters are found later in pad descr.)
5020 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::ROUNDRECT );
5021 break;
5022
5023 case T_custom:
5024 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CUSTOM );
5025 break;
5026
5027 default:
5028 Expecting( "circle, rectangle, roundrect, oval, trapezoid or custom" );
5029 }
5030
5031 std::optional<EDA_ANGLE> thermalBrAngleOverride;
5032
5033 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5034 {
5035 if( token == T_locked )
5036 {
5037 // Pad locking is now a session preference
5038 token = NextTok();
5039 }
5040
5041 if( token != T_LEFT )
5042 Expecting( T_LEFT );
5043
5044 token = NextTok();
5045
5046 switch( token )
5047 {
5048 case T_size:
5049 sz.x = parseBoardUnits( "width value" );
5050 sz.y = parseBoardUnits( "height value" );
5051 pad->SetSize( PADSTACK::ALL_LAYERS, sz );
5052 NeedRIGHT();
5053 break;
5054
5055 case T_at:
5056 pt.x = parseBoardUnits( "X coordinate" );
5057 pt.y = parseBoardUnits( "Y coordinate" );
5058 pad->SetFPRelativePosition( pt );
5059 token = NextTok();
5060
5061 if( token == T_NUMBER )
5062 {
5063 pad->SetOrientation( EDA_ANGLE( parseDouble(), DEGREES_T ) );
5064 NeedRIGHT();
5065 }
5066 else if( token != T_RIGHT )
5067 {
5068 Expecting( ") or angle value" );
5069 }
5070
5071 break;
5072
5073 case T_rect_delta:
5074 {
5076 delta.x = parseBoardUnits( "rectangle delta width" );
5077 delta.y = parseBoardUnits( "rectangle delta height" );
5078 pad->SetDelta( PADSTACK::ALL_LAYERS, delta );
5079 NeedRIGHT();
5080 break;
5081 }
5082
5083 case T_drill:
5084 {
5085 bool haveWidth = false;
5086 VECTOR2I drillSize = pad->GetDrillSize();
5087
5088 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5089 {
5090 if( token == T_LEFT )
5091 token = NextTok();
5092
5093 switch( token )
5094 {
5095 case T_oval: pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG ); break;
5096
5097 case T_NUMBER:
5098 {
5099 if( !haveWidth )
5100 {
5101 drillSize.x = parseBoardUnits();
5102
5103 // If height is not defined the width and height are the same.
5104 drillSize.y = drillSize.x;
5105 haveWidth = true;
5106 }
5107 else
5108 {
5109 drillSize.y = parseBoardUnits();
5110 }
5111 }
5112
5113 break;
5114
5115 case T_offset:
5116 pt.x = parseBoardUnits( "drill offset x" );
5117 pt.y = parseBoardUnits( "drill offset y" );
5118 pad->SetOffset( PADSTACK::ALL_LAYERS, pt );
5119 NeedRIGHT();
5120 break;
5121
5122 default:
5123 Expecting( "oval, size, or offset" );
5124 }
5125 }
5126
5127 // This fixes a bug caused by setting the default PAD drill size to a value other
5128 // than 0 used to fix a bunch of debug assertions even though it is defined as a
5129 // through hole pad. Wouldn't a though hole pad with no drill be a surface mount
5130 // pad (or a conn pad which is a smd pad with no solder paste)?
5131 if( pad->GetAttribute() != PAD_ATTRIB::SMD && pad->GetAttribute() != PAD_ATTRIB::CONN )
5132 pad->SetDrillSize( drillSize );
5133 else
5134 pad->SetDrillSize( VECTOR2I( 0, 0 ) );
5135
5136 break;
5137 }
5138
5139 case T_layers:
5140 {
5141 LSET layerMask = parseBoardItemLayersAsMask();
5142 pad->SetLayerSet( layerMask );
5143 break;
5144 }
5145
5146 case T_net:
5147 foundNet = true;
5148
5149 if( ! pad->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
5150 {
5151 wxLogError( _( "Invalid net ID in\nfile: %s\nline: %d offset: %d" ),
5152 CurSource(), CurLineNumber(), CurOffset() );
5153 }
5154
5155 NeedSYMBOLorNUMBER();
5156
5157 // Test validity of the netname in file for netcodes expected having a net name
5158 if( m_board && pad->GetNetCode() > 0 )
5159 {
5160 wxString netName( FromUTF8() );
5161
5162 // Convert overbar syntax from `~...~` to `~{...}`. These were left out of the
5163 // first merge so the version is a bit later.
5164 if( m_requiredVersion < 20210606 )
5165 netName = ConvertToNewOverbarNotation( netName );
5166
5167 if( netName != m_board->FindNet( pad->GetNetCode() )->GetNetname() )
5168 {
5169 pad->SetNetCode( NETINFO_LIST::ORPHANED, /* aNoAssert */ true );
5170 wxLogError( _( "Net name doesn't match ID in\nfile: %s\nline: %d offset: %d" ),
5171 CurSource(), CurLineNumber(), CurOffset() );
5172 }
5173 }
5174
5175 NeedRIGHT();
5176 break;
5177
5178 case T_pinfunction:
5179 NeedSYMBOLorNUMBER();
5180 pad->SetPinFunction( FromUTF8() );
5181 NeedRIGHT();
5182 break;
5183
5184 case T_pintype:
5185 NeedSYMBOLorNUMBER();
5186 pad->SetPinType( FromUTF8() );
5187 NeedRIGHT();
5188 break;
5189
5190 case T_die_length:
5191 pad->SetPadToDieLength( parseBoardUnits( T_die_length ) );
5192 NeedRIGHT();
5193 break;
5194
5195 case T_solder_mask_margin:
5196 pad->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
5197 NeedRIGHT();
5198
5199 // In pre-9.0 files "0" meant inherit.
5200 if( m_requiredVersion <= 20240201 && pad->GetLocalSolderMaskMargin() == 0 )
5201 pad->SetLocalSolderMaskMargin( {} );
5202
5203 break;
5204
5205 case T_solder_paste_margin:
5206 pad->SetLocalSolderPasteMargin( parseBoardUnits( "local solder paste margin value" ) );
5207 NeedRIGHT();
5208
5209 // In pre-9.0 files "0" meant inherit.
5210 if( m_requiredVersion <= 20240201 && pad->GetLocalSolderPasteMargin() == 0 )
5211 pad->SetLocalSolderPasteMargin( {} );
5212
5213 break;
5214
5215 case T_solder_paste_margin_ratio:
5216 pad->SetLocalSolderPasteMarginRatio( parseDouble( "local solder paste margin ratio value" ) );
5217 NeedRIGHT();
5218
5219 // In pre-9.0 files "0" meant inherit.
5220 if( m_requiredVersion <= 20240201 && pad->GetLocalSolderPasteMarginRatio() == 0 )
5221 pad->SetLocalSolderPasteMarginRatio( {} );
5222
5223 break;
5224
5225 case T_clearance:
5226 pad->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
5227 NeedRIGHT();
5228
5229 // In pre-9.0 files "0" meant inherit.
5230 if( m_requiredVersion <= 20240201 && pad->GetLocalClearance() == 0 )
5231 pad->SetLocalClearance( {} );
5232
5233 break;
5234
5235 case T_teardrops:
5236 parseTEARDROP_PARAMETERS( &pad->GetTeardropParams() );
5237 break;
5238
5239 case T_zone_connect:
5240 pad->SetLocalZoneConnection( (ZONE_CONNECTION) parseInt( "zone connection value" ) );
5241 NeedRIGHT();
5242 break;
5243
5244 case T_thermal_width: // legacy token
5245 case T_thermal_bridge_width:
5246 pad->SetThermalSpokeWidth( parseBoardUnits( token ) );
5247 NeedRIGHT();
5248 break;
5249
5250 case T_thermal_bridge_angle:
5251 thermalBrAngleOverride = EDA_ANGLE( parseDouble( "thermal spoke angle" ), DEGREES_T );
5252 NeedRIGHT();
5253 break;
5254
5255
5256 case T_thermal_gap:
5257 pad->SetThermalGap( parseBoardUnits( "thermal relief gap value" ) );
5258 NeedRIGHT();
5259 break;
5260
5261 case T_roundrect_rratio:
5262 pad->SetRoundRectRadiusRatio( PADSTACK::ALL_LAYERS,
5263 parseDouble( "roundrect radius ratio" ) );
5264 NeedRIGHT();
5265 break;
5266
5267 case T_chamfer_ratio:
5268 pad->SetChamferRectRatio( PADSTACK::ALL_LAYERS, parseDouble( "chamfer ratio" ) );
5269
5270 if( pad->GetChamferRectRatio( PADSTACK::ALL_LAYERS ) > 0 )
5271 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CHAMFERED_RECT );
5272
5273 NeedRIGHT();
5274 break;
5275
5276 case T_chamfer:
5277 {
5278 int chamfers = 0;
5279 bool end_list = false;
5280
5281 while( !end_list )
5282 {
5283 token = NextTok();
5284
5285 switch( token )
5286 {
5287 case T_top_left:
5288 chamfers |= RECT_CHAMFER_TOP_LEFT;
5289 break;
5290
5291 case T_top_right:
5292 chamfers |= RECT_CHAMFER_TOP_RIGHT;
5293 break;
5294
5295 case T_bottom_left:
5296 chamfers |= RECT_CHAMFER_BOTTOM_LEFT;
5297 break;
5298
5299 case T_bottom_right:
5300 chamfers |= RECT_CHAMFER_BOTTOM_RIGHT;
5301 break;
5302
5303 case T_RIGHT:
5304 pad->SetChamferPositions( PADSTACK::ALL_LAYERS, chamfers );
5305 end_list = true;
5306 break;
5307
5308 default:
5309 Expecting( "chamfer_top_left chamfer_top_right chamfer_bottom_left or "
5310 "chamfer_bottom_right" );
5311 }
5312 }
5313
5314 if( pad->GetChamferPositions( PADSTACK::ALL_LAYERS ) != RECT_NO_CHAMFER )
5315 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CHAMFERED_RECT );
5316
5317 break;
5318 }
5319
5320 case T_property:
5321 while( token != T_RIGHT )
5322 {
5323 token = NextTok();
5324
5325 switch( token )
5326 {
5327 case T_pad_prop_bga: pad->SetProperty( PAD_PROP::BGA ); break;
5328 case T_pad_prop_fiducial_glob: pad->SetProperty( PAD_PROP::FIDUCIAL_GLBL ); break;
5329 case T_pad_prop_fiducial_loc: pad->SetProperty( PAD_PROP::FIDUCIAL_LOCAL ); break;
5330 case T_pad_prop_testpoint: pad->SetProperty( PAD_PROP::TESTPOINT ); break;
5331 case T_pad_prop_castellated: pad->SetProperty( PAD_PROP::CASTELLATED ); break;
5332 case T_pad_prop_heatsink: pad->SetProperty( PAD_PROP::HEATSINK ); break;
5333 case T_pad_prop_mechanical: pad->SetProperty( PAD_PROP::MECHANICAL ); break;
5334 case T_none: pad->SetProperty( PAD_PROP::NONE ); break;
5335 case T_RIGHT: break;
5336
5337 default:
5338#if 0 // Currently: skip unknown property
5339 Expecting( "pad_prop_bga pad_prop_fiducial_glob pad_prop_fiducial_loc"
5340 " pad_prop_heatsink or pad_prop_castellated" );
5341#endif
5342 break;
5343 }
5344 }
5345
5346 break;
5347
5348 case T_options:
5349 parsePAD_option( pad.get() );
5350 break;
5351
5352 case T_padstack:
5353 parsePadstack( pad.get() );
5354 break;
5355
5356 case T_primitives:
5357 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5358 {
5359 if( token == T_LEFT )
5360 token = NextTok();
5361
5362 switch( token )
5363 {
5364 case T_gr_arc:
5365 case T_gr_line:
5366 case T_gr_circle:
5367 case T_gr_rect:
5368 case T_gr_poly:
5369 case T_gr_curve:
5370 pad->AddPrimitive( PADSTACK::ALL_LAYERS, parsePCB_SHAPE( nullptr ) );
5371 break;
5372
5373 case T_gr_bbox:
5374 {
5375 PCB_SHAPE* numberBox = parsePCB_SHAPE( nullptr );
5376 numberBox->SetIsProxyItem();
5377 pad->AddPrimitive( PADSTACK::ALL_LAYERS, numberBox );
5378 break;
5379 }
5380
5381 case T_gr_vector:
5382 {
5383 PCB_SHAPE* spokeTemplate = parsePCB_SHAPE( nullptr );
5384 spokeTemplate->SetIsProxyItem();
5385 pad->AddPrimitive( PADSTACK::ALL_LAYERS, spokeTemplate );
5386 break;
5387 }
5388
5389 default:
5390 Expecting( "gr_line, gr_arc, gr_circle, gr_curve, gr_rect, gr_bbox or gr_poly" );
5391 break;
5392 }
5393 }
5394
5395 break;
5396
5397 case T_remove_unused_layers:
5398 {
5399 bool remove = parseMaybeAbsentBool( true );
5400 pad->SetRemoveUnconnected( remove );
5401 break;
5402 }
5403
5404 case T_keep_end_layers:
5405 {
5406 bool keep = parseMaybeAbsentBool( true );
5407 pad->SetKeepTopBottom( keep );
5408 break;
5409 }
5410
5411 case T_zone_layer_connections:
5412 {
5413 LSET cuLayers = pad->GetLayerSet() & LSET::AllCuMask();
5414
5415 for( PCB_LAYER_ID layer : cuLayers.Seq() )
5416 pad->SetZoneLayerOverride( layer, ZLO_FORCE_NO_ZONE_CONNECTION );
5417
5418 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5419 {
5421
5422 if( !IsCopperLayer( layer ) )
5423 Expecting( "copper layer name" );
5424
5425 pad->SetZoneLayerOverride( layer, ZLO_FORCE_FLASHED );
5426 }
5427
5428 break;
5429 }
5430
5431 // Continue to process "(locked)" format which was output during 5.99 development
5432 case T_locked:
5433 // Pad locking is now a session preference
5434 parseMaybeAbsentBool( true );
5435 break;
5436
5437 case T_tstamp:
5438 case T_uuid:
5439 NextTok();
5440 const_cast<KIID&>( pad->m_Uuid ) = CurStrToKIID();
5441 NeedRIGHT();
5442 break;
5443
5444 default:
5445 Expecting( "at, locked, drill, layers, net, die_length, roundrect_rratio, "
5446 "solder_mask_margin, solder_paste_margin, solder_paste_margin_ratio, uuid, "
5447 "clearance, tstamp, primitives, remove_unused_layers, keep_end_layers, "
5448 "pinfunction, pintype, zone_connect, thermal_width, thermal_gap, padstack or "
5449 "teardrops" );
5450 }
5451 }
5452
5453 if( !foundNet )
5454 {
5455 // Make sure default netclass is correctly assigned to pads that don't define a net.
5456 pad->SetNetCode( 0, /* aNoAssert */ true );
5457 }
5458
5459 if( thermalBrAngleOverride )
5460 {
5461 pad->SetThermalSpokeAngle( *thermalBrAngleOverride );
5462 }
5463 else
5464 {
5465 // This is here because custom pad anchor shape isn't known before reading (options
5466 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE )
5467 {
5468 pad->SetThermalSpokeAngle( ANGLE_45 );
5469 }
5470 else if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CUSTOM
5471 && pad->GetAnchorPadShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE )
5472 {
5473 if( m_requiredVersion <= 20211014 ) // 6.0
5474 pad->SetThermalSpokeAngle( ANGLE_90 );
5475 else
5476 pad->SetThermalSpokeAngle( ANGLE_45 );
5477 }
5478 else
5479 {
5480 pad->SetThermalSpokeAngle( ANGLE_90 );
5481 }
5482 }
5483
5484 if( !pad->CanHaveNumber() )
5485 {
5486 // At some point it was possible to assign a number to aperture pads so we need to clean
5487 // those out here.
5488 pad->SetNumber( wxEmptyString );
5489 }
5490
5491 // Zero-sized pads are likely algorithmically unsafe.
5492 if( pad->GetSizeX() <= 0 || pad->GetSizeY() <= 0 )
5493 {
5494 pad->SetSize( PADSTACK::ALL_LAYERS,
5495 VECTOR2I( pcbIUScale.mmToIU( 0.001 ), pcbIUScale.mmToIU( 0.001 ) ) );
5496
5497 wxLogWarning( _( "Invalid zero-sized pad pinned to %s in\nfile: %s\nline: %d\noffset: %d" ),
5498 wxT( "1µm" ), CurSource(), CurLineNumber(), CurOffset() );
5499 }
5500
5501 return pad.release();
5502}
5503
5504
5506{
5507 // Parse only the (option ...) inside a pad description
5508 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
5509 {
5510 if( token != T_LEFT )
5511 Expecting( T_LEFT );
5512
5513 token = NextTok();
5514
5515 switch( token )
5516 {
5517 case T_anchor:
5518 token = NextTok();
5519 // Custom shaped pads have a "anchor pad", which is the reference
5520 // for connection calculations.
5521 // Because this is an anchor, only the 2 very basic shapes are managed:
5522 // circle and rect. The default is circle
5523 switch( token )
5524 {
5525 case T_circle: // default
5526 break;
5527
5528 case T_rect:
5529 aPad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::RECTANGLE );
5530 break;
5531
5532 default:
5533 // Currently, because pad options is a moving target
5534 // just skip unknown keywords
5535 break;
5536 }
5537 NeedRIGHT();
5538 break;
5539
5540 case T_clearance:
5541 token = NextTok();
5542 // Custom shaped pads have a clearance area that is the pad shape
5543 // (like usual pads) or the convex hull of the pad shape.
5544 switch( token )
5545 {
5546 case T_outline:
5548 break;
5549
5550 case T_convexhull:
5552 break;
5553
5554 default:
5555 // Currently, because pad options is a moving target
5556 // just skip unknown keywords
5557 break;
5558 }
5559
5560 NeedRIGHT();
5561 break;
5562
5563 default:
5564 // Currently, because pad options is a moving target
5565 // just skip unknown keywords
5566 while( (token = NextTok() ) != T_RIGHT )
5567 {}
5568
5569 break;
5570 }
5571 }
5572
5573 return true;
5574}
5575
5576
5578{
5579 PADSTACK& padstack = aPad->Padstack();
5580
5581 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
5582 {
5583 if( token != T_LEFT )
5584 Expecting( T_LEFT );
5585
5586 token = NextTok();
5587
5588 switch( token )
5589 {
5590 case T_mode:
5591 token = NextTok();
5592
5593 switch( token )
5594 {
5595 case T_front_inner_back:
5597 break;
5598
5599 case T_custom:
5600 padstack.SetMode( PADSTACK::MODE::CUSTOM );
5601 break;
5602
5603 default:
5604 Expecting( "front_inner_back or custom" );
5605 }
5606
5607 NeedRIGHT();
5608 break;
5609
5610 case T_layer:
5611 {
5612 NextTok();
5613 PCB_LAYER_ID curLayer = UNDEFINED_LAYER;
5614
5615 if( curText == "Inner" )
5616 {
5617 if( padstack.Mode() != PADSTACK::MODE::FRONT_INNER_BACK )
5618 {
5619 THROW_IO_ERROR( wxString::Format( _( "Invalid padstack layer in\nfile: %s\n"
5620 "line: %d\noffset: %d." ),
5621 CurSource(), CurLineNumber(), CurOffset() ) );
5622 }
5623
5624 curLayer = PADSTACK::INNER_LAYERS;
5625 }
5626 else
5627 {
5628 curLayer = lookUpLayer( m_layerIndices );
5629 }
5630
5631 if( !IsCopperLayer( curLayer ) )
5632 {
5633 wxString error;
5634 error.Printf( _( "Invalid padstack layer '%s' in file '%s' at line %d, offset %d." ),
5635 curText, CurSource().GetData(), CurLineNumber(), CurOffset() );
5636 THROW_IO_ERROR( error );
5637 }
5638
5639 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5640 {
5641 if( token != T_LEFT )
5642 Expecting( T_LEFT );
5643
5644 token = NextTok();
5645
5646 switch( token )
5647 {
5648 case T_shape:
5649 token = NextTok();
5650
5651 switch( token )
5652 {
5653 case T_circle:
5654 aPad->SetShape( curLayer, PAD_SHAPE::CIRCLE );
5655 break;
5656
5657 case T_rect:
5658 aPad->SetShape( curLayer, PAD_SHAPE::RECTANGLE );
5659 break;
5660
5661 case T_oval:
5662 aPad->SetShape( curLayer, PAD_SHAPE::OVAL );
5663 break;
5664
5665 case T_trapezoid:
5666 aPad->SetShape( curLayer, PAD_SHAPE::TRAPEZOID );
5667 break;
5668
5669 case T_roundrect:
5670 // Note: the shape can be PAD_SHAPE::ROUNDRECT or PAD_SHAPE::CHAMFERED_RECT
5671 // (if chamfer parameters are found later in pad descr.)
5672 aPad->SetShape( curLayer, PAD_SHAPE::ROUNDRECT );
5673 break;
5674
5675 case T_custom:
5676 aPad->SetShape( curLayer, PAD_SHAPE::CUSTOM );
5677 break;
5678
5679 default:
5680 Expecting( "circle, rectangle, roundrect, oval, trapezoid or custom" );
5681 }
5682
5683 NeedRIGHT();
5684 break;
5685
5686 case T_size:
5687 {
5688 VECTOR2I sz;
5689 sz.x = parseBoardUnits( "width value" );
5690 sz.y = parseBoardUnits( "height value" );
5691 aPad->SetSize( curLayer, sz );
5692 NeedRIGHT();
5693 break;
5694 }
5695
5696 case T_offset:
5697 {
5698 VECTOR2I pt;
5699 pt.x = parseBoardUnits( "drill offset x" );
5700 pt.y = parseBoardUnits( "drill offset y" );
5701 aPad->SetOffset( curLayer, pt );
5702 NeedRIGHT();
5703 break;
5704 }
5705
5706 case T_rect_delta:
5707 {
5709 delta.x = parseBoardUnits( "rectangle delta width" );
5710 delta.y = parseBoardUnits( "rectangle delta height" );
5711 aPad->SetDelta( curLayer, delta );
5712 NeedRIGHT();
5713 break;
5714 }
5715
5716 case T_roundrect_rratio:
5717 aPad->SetRoundRectRadiusRatio( curLayer,
5718 parseDouble( "roundrect radius ratio" ) );
5719 NeedRIGHT();
5720 break;
5721
5722 case T_chamfer_ratio:
5723 {
5724 double ratio = parseDouble( "chamfer ratio" );
5725 aPad->SetChamferRectRatio( curLayer, ratio );
5726
5727 if( ratio > 0 )
5728 aPad->SetShape( curLayer, PAD_SHAPE::CHAMFERED_RECT );
5729
5730 NeedRIGHT();
5731 break;
5732 }
5733
5734 case T_chamfer:
5735 {
5736 int chamfers = 0;
5737 bool end_list = false;
5738
5739 while( !end_list )
5740 {
5741 token = NextTok();
5742
5743 switch( token )
5744 {
5745 case T_top_left:
5746 chamfers |= RECT_CHAMFER_TOP_LEFT;
5747 break;
5748
5749 case T_top_right:
5750 chamfers |= RECT_CHAMFER_TOP_RIGHT;
5751 break;
5752
5753 case T_bottom_left:
5754 chamfers |= RECT_CHAMFER_BOTTOM_LEFT;
5755 break;
5756
5757 case T_bottom_right:
5758 chamfers |= RECT_CHAMFER_BOTTOM_RIGHT;
5759 break;
5760
5761 case T_RIGHT:
5762 aPad->SetChamferPositions( curLayer, chamfers );
5763 end_list = true;
5764 break;
5765
5766 default:
5767 Expecting( "chamfer_top_left chamfer_top_right chamfer_bottom_left or "
5768 "chamfer_bottom_right" );
5769 }
5770 }
5771
5772 if( end_list && chamfers != RECT_NO_CHAMFER )
5773 aPad->SetShape( curLayer, PAD_SHAPE::CHAMFERED_RECT );
5774
5775 break;
5776 }
5777
5778 case T_thermal_bridge_width:
5779 padstack.ThermalSpokeWidth( curLayer ) =
5780 parseBoardUnits( "thermal relief spoke width" );
5781 NeedRIGHT();
5782 break;
5783
5784 case T_thermal_gap:
5785 padstack.ThermalGap( curLayer ) = parseBoardUnits( "thermal relief gap value" );
5786 NeedRIGHT();
5787 break;
5788
5789 case T_thermal_bridge_angle:
5790 padstack.SetThermalSpokeAngle(
5791 EDA_ANGLE( parseDouble( "thermal spoke angle" ), DEGREES_T ) );
5792 NeedRIGHT();
5793 break;
5794
5795 case T_zone_connect:
5796 padstack.ZoneConnection( curLayer ) = magic_enum::enum_cast<ZONE_CONNECTION>(
5797 parseInt( "zone connection value" ) );
5798 NeedRIGHT();
5799 break;
5800
5801 case T_clearance:
5802 padstack.Clearance( curLayer ) = parseBoardUnits( "local clearance value" );
5803 NeedRIGHT();
5804 break;
5805
5806 case T_tenting:
5807 parseTenting( padstack );
5808 break;
5809
5810 // TODO: refactor parsePAD_options to work on padstacks too
5811 case T_options:
5812 {
5813 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5814 {
5815 if( token != T_LEFT )
5816 Expecting( T_LEFT );
5817
5818 token = NextTok();
5819
5820 switch( token )
5821 {
5822 case T_anchor:
5823 token = NextTok();
5824 // Custom shaped pads have a "anchor pad", which is the reference
5825 // for connection calculations.
5826 // Because this is an anchor, only the 2 very basic shapes are managed:
5827 // circle and rect. The default is circle
5828 switch( token )
5829 {
5830 case T_circle: // default
5831 break;
5832
5833 case T_rect:
5834 padstack.SetAnchorShape( PAD_SHAPE::RECTANGLE, curLayer );
5835 break;
5836
5837 default:
5838 // Currently, because pad options is a moving target
5839 // just skip unknown keywords
5840 break;
5841 }
5842 NeedRIGHT();
5843 break;
5844
5845 case T_clearance:
5846 token = NextTok();
5847 // TODO: m_customShapeInZoneMode is not per-layer at the moment
5848 NeedRIGHT();
5849 break;
5850
5851 default:
5852 // Currently, because pad options is a moving target
5853 // just skip unknown keywords
5854 while( ( token = NextTok() ) != T_RIGHT )
5855 {
5856 }
5857
5858 break;
5859 }
5860 }
5861
5862 break;
5863 }
5864
5865 // TODO: deduplicate with non-padstack parser
5866 case T_primitives:
5867 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5868 {
5869 if( token == T_LEFT )
5870 token = NextTok();
5871
5872 switch( token )
5873 {
5874 case T_gr_arc:
5875 case T_gr_line:
5876 case T_gr_circle:
5877 case T_gr_rect:
5878 case T_gr_poly:
5879 case T_gr_curve:
5880 padstack.AddPrimitive( parsePCB_SHAPE( nullptr ), curLayer );
5881 break;
5882
5883 case T_gr_bbox:
5884 {
5885 PCB_SHAPE* numberBox = parsePCB_SHAPE( nullptr );
5886 numberBox->SetIsProxyItem();
5887 padstack.AddPrimitive( numberBox, curLayer );
5888 break;
5889 }
5890
5891 case T_gr_vector:
5892 {
5893 PCB_SHAPE* spokeTemplate = parsePCB_SHAPE( nullptr );
5894 spokeTemplate->SetIsProxyItem();
5895 padstack.AddPrimitive( spokeTemplate, curLayer );
5896 break;
5897 }
5898
5899 default:
5900 Expecting( "gr_line, gr_arc, gr_circle, gr_curve, gr_rect, gr_bbox or gr_poly" );
5901 break;
5902 }
5903 }
5904
5905 break;
5906
5907 default:
5908 // Not strict-parsing padstack layers yet
5909 continue;
5910 }
5911 }
5912
5913 break;
5914 }
5915
5916 default:
5917 Expecting( "mode or layer" );
5918 break;
5919 }
5920 }
5921}
5922
5923
5925{
5926 T token;
5927
5928 while( ( token = NextTok() ) != T_RIGHT )
5929 {
5930 // This token is the Uuid of the item in the group.
5931 // Since groups are serialized at the end of the file/footprint, the Uuid should already
5932 // have been seen and exist in the board.
5933 KIID uuid( CurStr() );
5934 aGroupInfo.memberUuids.push_back( uuid );
5935 }
5936}
5937
5938
5940{
5941 wxCHECK_RET( CurTok() == T_group,
5942 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_GROUP." ) );
5943
5944 T token;
5945
5946 m_groupInfos.push_back( GROUP_INFO() );
5947 GROUP_INFO& groupInfo = m_groupInfos.back();
5948 groupInfo.parent = aParent;
5949
5950 while( ( token = NextTok() ) != T_LEFT )
5951 {
5952 if( token == T_STRING )
5953 groupInfo.name = FromUTF8();
5954 else if( token == T_locked )
5955 groupInfo.locked = true;
5956 else
5957 Expecting( "group name or locked" );
5958 }
5959
5960 for( ; token != T_RIGHT; token = NextTok() )
5961 {
5962 if( token != T_LEFT )
5963 Expecting( T_LEFT );
5964
5965 token = NextTok();
5966
5967 switch( token )
5968 {
5969 // From formats [20200811, 20231215), 'id' was used instead of 'uuid'
5970 case T_id:
5971 case T_uuid:
5972 NextTok();
5973 groupInfo.uuid = CurStrToKIID();
5974 NeedRIGHT();
5975 break;
5976
5977 case T_locked:
5978 groupInfo.locked = parseBool();
5979 NeedRIGHT();
5980 break;
5981
5982 case T_members:
5983 {
5984 parseGROUP_members( groupInfo );
5985 break;
5986 }
5987
5988 default:
5989 Expecting( "uuid, locked, or members" );
5990 }
5991 }
5992}
5993
5994
5996{
5997 wxCHECK_RET( CurTok() == T_generated, wxT( "Cannot parse " ) + GetTokenString( CurTok() )
5998 + wxT( " as PCB_GENERATOR." ) );
5999
6000 T token;
6001
6002 m_generatorInfos.push_back( GENERATOR_INFO() );
6003 GENERATOR_INFO& genInfo = m_generatorInfos.back();
6004
6005 genInfo.layer = F_Cu;
6006 genInfo.parent = aParent;
6008
6009 NeedLEFT();
6010 token = NextTok();
6011
6012 // For formats [20231007, 20231215), 'id' was used instead of 'uuid'
6013 if( token != T_uuid && token != T_id )
6014 Expecting( T_uuid );
6015
6016 NextTok();
6017 genInfo.uuid = CurStrToKIID();
6018 NeedRIGHT();
6019
6020 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6021 {
6022 if( token != T_LEFT )
6023 Expecting( T_LEFT );
6024
6025 token = NextTok();
6026
6027 switch( token )
6028 {
6029 case T_type:
6030 NeedSYMBOL();
6031 genInfo.genType = FromUTF8();
6032 NeedRIGHT();
6033 break;
6034
6035 case T_name:
6036 NeedSYMBOL();
6037 genInfo.name = FromUTF8();
6038 NeedRIGHT();
6039 break;
6040
6041 case T_locked:
6042 token = NextTok();
6043 genInfo.locked = token == T_yes;
6044 NeedRIGHT();
6045 break;
6046
6047 case T_layer:
6048 genInfo.layer = parseBoardItemLayer();
6049 NeedRIGHT();
6050 break;
6051
6052 case T_members: parseGROUP_members( genInfo ); break;
6053
6054 default:
6055 {
6056 wxString pName = FromUTF8();
6057 T tok1 = NextTok();
6058
6059 switch( tok1 )
6060 {
6061 case T_yes:
6062 {
6063 genInfo.properties.emplace( pName, wxAny( true ) );
6064 NeedRIGHT();
6065 break;
6066 }
6067 case T_no:
6068 {
6069 genInfo.properties.emplace( pName, wxAny( false ) );
6070 NeedRIGHT();
6071 break;
6072 }
6073 case T_NUMBER:
6074 {
6075 double pValue = parseDouble();
6076 genInfo.properties.emplace( pName, wxAny( pValue ) );
6077 NeedRIGHT();
6078 break;
6079 }
6080 case T_STRING: // Quoted string
6081 {
6082 wxString pValue = FromUTF8();
6083 genInfo.properties.emplace( pName, pValue );
6084 NeedRIGHT();
6085 break;
6086 }
6087 case T_LEFT:
6088 {
6089 NeedSYMBOL();
6090 T tok2 = CurTok();
6091
6092 switch( tok2 )
6093 {
6094 case T_xy:
6095 {
6096 VECTOR2I pt;
6097
6098 pt.x = parseBoardUnits( "X coordinate" );
6099 pt.y = parseBoardUnits( "Y coordinate" );
6100
6101 genInfo.properties.emplace( pName, wxAny( pt ) );
6102 NeedRIGHT();
6103 NeedRIGHT();
6104
6105 break;
6106 }
6107 case T_pts:
6108 {
6109 SHAPE_LINE_CHAIN chain;
6110
6111 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6112 parseOutlinePoints( chain );
6113
6114 NeedRIGHT();
6115
6116 genInfo.properties.emplace( pName, wxAny( chain ) );
6117 break;
6118 }
6119 default: Expecting( "xy or pts" );
6120 }
6121
6122 break;
6123 }
6124 default: Expecting( "a number, symbol, string or (" );
6125 }
6126
6127 break;
6128 }
6129 }
6130 }
6131}
6132
6133
6135{
6136 wxCHECK_MSG( CurTok() == T_arc, nullptr,
6137 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as ARC." ) );
6138
6139 VECTOR2I pt;
6140 T token;
6141
6142 std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( m_board );
6143
6144 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6145 {
6146 // Legacy locked
6147 if( token == T_locked )
6148 {
6149 arc->SetLocked( true );
6150 token = NextTok();
6151 }
6152
6153 if( token != T_LEFT )
6154 Expecting( T_LEFT );
6155
6156 token = NextTok();
6157
6158 switch( token )
6159 {
6160 case T_start:
6161 pt.x = parseBoardUnits( "start x" );
6162 pt.y = parseBoardUnits( "start y" );
6163 arc->SetStart( pt );
6164 NeedRIGHT();
6165 break;
6166
6167 case T_mid:
6168 pt.x = parseBoardUnits( "mid x" );
6169 pt.y = parseBoardUnits( "mid y" );
6170 arc->SetMid( pt );
6171 NeedRIGHT();
6172 break;
6173
6174 case T_end:
6175 pt.x = parseBoardUnits( "end x" );
6176 pt.y = parseBoardUnits( "end y" );
6177 arc->SetEnd( pt );
6178 NeedRIGHT();
6179 break;
6180
6181 case T_width:
6182 arc->SetWidth( parseBoardUnits( "width" ) );
6183 NeedRIGHT();
6184 break;
6185
6186 case T_layer:
6187 arc->SetLayer( parseBoardItemLayer() );
6188 NeedRIGHT();
6189 break;
6190
6191 case T_layers:
6192 arc->SetLayerSet( parseLayersForCuItemWithSoldermask() );
6193 break;
6194
6195 case T_solder_mask_margin:
6196 arc->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
6197 NeedRIGHT();
6198 break;
6199
6200 case T_net:
6201 if( !arc->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
6202 {
6203 wxLogError( _( "Invalid net ID in\nfile: %s\nline: %d\noffset: %d." ),
6204 CurSource(), CurLineNumber(), CurOffset() );
6205 }
6206 NeedRIGHT();
6207 break;
6208
6209 case T_tstamp:
6210 case T_uuid:
6211 NextTok();
6212 const_cast<KIID&>( arc->m_Uuid ) = CurStrToKIID();
6213 NeedRIGHT();
6214 break;
6215
6216 // We continue to parse the status field but it is no longer written
6217 case T_status:
6218 parseHex();
6219 NeedRIGHT();
6220 break;
6221
6222 case T_locked:
6223 arc->SetLocked( parseMaybeAbsentBool( true ) );
6224 break;
6225
6226 default:
6227 Expecting( "start, mid, end, width, layer, solder_mask_margin, net, tstamp, uuid, "
6228 "or status" );
6229 }
6230 }
6231
6232 return arc.release();
6233}
6234
6235
6237{
6238 wxCHECK_MSG( CurTok() == T_segment, nullptr,
6239 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TRACK." ) );
6240
6241 VECTOR2I pt;
6242 T token;
6243
6244 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( m_board );
6245
6246 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6247 {
6248 // Legacy locked flag
6249 if( token == T_locked )
6250 {
6251 track->SetLocked( true );
6252 token = NextTok();
6253 }
6254
6255 if( token != T_LEFT )
6256 Expecting( T_LEFT );
6257
6258 token = NextTok();
6259
6260 switch( token )
6261 {
6262 case T_start:
6263 pt.x = parseBoardUnits( "start x" );
6264 pt.y = parseBoardUnits( "start y" );
6265 track->SetStart( pt );
6266 NeedRIGHT();
6267 break;
6268
6269 case T_end:
6270 pt.x = parseBoardUnits( "end x" );
6271 pt.y = parseBoardUnits( "end y" );
6272 track->SetEnd( pt );
6273 NeedRIGHT();
6274 break;
6275
6276 case T_width:
6277 track->SetWidth( parseBoardUnits( "width" ) );
6278 NeedRIGHT();
6279 break;
6280
6281 case T_layer:
6282 track->SetLayer( parseBoardItemLayer() );
6283 NeedRIGHT();
6284 break;
6285
6286 case T_layers:
6287 track->SetLayerSet( parseLayersForCuItemWithSoldermask() );
6288 break;
6289
6290 case T_solder_mask_margin:
6291 track->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
6292 NeedRIGHT();
6293 break;
6294
6295 case T_net:
6296 if( !track->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
6297 {
6298 wxLogError( _( "Invalid net ID in\nfile: '%s'\nline: %d\noffset: %d." ),
6299 CurSource(), CurLineNumber(), CurOffset() );
6300 }
6301 NeedRIGHT();
6302 break;
6303
6304 case T_tstamp:
6305 case T_uuid:
6306 NextTok();
6307 const_cast<KIID&>( track->m_Uuid ) = CurStrToKIID();
6308 NeedRIGHT();
6309 break;
6310
6311 // We continue to parse the status field but it is no longer written
6312 case T_status:
6313 parseHex();
6314 NeedRIGHT();
6315 break;
6316
6317 case T_locked:
6318 track->SetLocked( parseMaybeAbsentBool( true ) );
6319 break;
6320
6321 default:
6322 Expecting( "start, end, width, layer, solder_mask_margin, net, tstamp, uuid, "
6323 "or locked" );
6324 }
6325 }
6326
6327 return track.release();
6328}
6329
6330
6332{
6333 wxCHECK_MSG( CurTok() == T_via, nullptr,
6334 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_VIA." ) );
6335
6336 VECTOR2I pt;
6337 T token;
6338
6339 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( m_board );
6340
6341 // File format default is no-token == no-feature.
6342 via->Padstack().SetUnconnectedLayerMode( PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL );
6343
6344 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6345 {
6346 // Legacy locked
6347 if( token == T_locked )
6348 {
6349 via->SetLocked( true );
6350 token = NextTok();
6351 }
6352
6353 if( token == T_LEFT )
6354 token = NextTok();
6355
6356 switch( token )
6357 {
6358 case T_blind:
6359 via->SetViaType( VIATYPE::BLIND_BURIED );
6360 break;
6361
6362 case T_micro:
6363 via->SetViaType( VIATYPE::MICROVIA );
6364 break;
6365
6366 case T_at:
6367 pt.x = parseBoardUnits( "start x" );
6368 pt.y = parseBoardUnits( "start y" );
6369 via->SetStart( pt );
6370 via->SetEnd( pt );
6371 NeedRIGHT();
6372 break;
6373
6374 case T_size:
6375 via->SetWidth( PADSTACK::ALL_LAYERS, parseBoardUnits( "via width" ) );
6376 NeedRIGHT();
6377 break;
6378
6379 case T_drill:
6380 via->SetDrill( parseBoardUnits( "drill diameter" ) );
6381 NeedRIGHT();
6382 break;
6383
6384 case T_layers:
6385 {
6386 PCB_LAYER_ID layer1, layer2;
6387 NextTok();
6388 layer1 = lookUpLayer( m_layerIndices );
6389 NextTok();
6390 layer2 = lookUpLayer( m_layerIndices );
6391 via->SetLayerPair( layer1, layer2 );
6392
6393 if( layer1 == UNDEFINED_LAYER || layer2 == UNDEFINED_LAYER )
6394 Expecting( "layer name" );
6395
6396 NeedRIGHT();
6397 break;
6398 }
6399
6400 case T_net:
6401 if( !via->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
6402 {
6403 wxLogError( _( "Invalid net ID in\nfile: %s\nline: %d\noffset: %d" ),
6404 CurSource(), CurLineNumber(), CurOffset() );
6405 }
6406
6407 NeedRIGHT();
6408 break;
6409
6410 case T_remove_unused_layers:
6411 {
6412 bool remove = parseMaybeAbsentBool( true );
6413 via->SetRemoveUnconnected( remove );
6414 break;
6415 }
6416
6417 case T_keep_end_layers:
6418 {
6419 bool keep = parseMaybeAbsentBool( true );
6420 via->SetKeepStartEnd( keep );
6421 break;
6422 }
6423
6424 case T_zone_layer_connections:
6425 {
6426 // Ensure only copper layers are stored int ZoneLayerOverride array
6427 LSET cuLayers = via->GetLayerSet() & LSET::AllCuMask();
6428
6429 for( PCB_LAYER_ID layer : cuLayers.Seq() )
6430 {
6431 via->SetZoneLayerOverride( layer, ZLO_FORCE_NO_ZONE_CONNECTION );
6432 }
6433
6434 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6435 {
6437
6438 if( !IsCopperLayer( layer ) )
6439 Expecting( "copper layer name" );
6440
6441 via->SetZoneLayerOverride( layer, ZLO_FORCE_FLASHED );
6442 }
6443 }
6444 break;
6445
6446 case T_padstack:
6447 parseViastack( via.get() );
6448 break;
6449
6450 case T_teardrops:
6451 parseTEARDROP_PARAMETERS( &via->GetTeardropParams() );
6452 break;
6453
6454 case T_tenting:
6455 parseTenting( via->Padstack() );
6456 break;
6457
6458 case T_tstamp:
6459 case T_uuid:
6460 NextTok();
6461 const_cast<KIID&>( via->m_Uuid ) = CurStrToKIID();
6462 NeedRIGHT();
6463 break;
6464
6465 // We continue to parse the status field but it is no longer written
6466 case T_status:
6467 parseHex();
6468 NeedRIGHT();
6469 break;
6470
6471 case T_locked:
6472 via->SetLocked( parseMaybeAbsentBool( true ) );
6473 break;
6474
6475 case T_free:
6476 via->SetIsFree( parseMaybeAbsentBool( true ) );
6477 break;
6478
6479 default:
6480 Expecting( "blind, micro, at, size, drill, layers, net, free, tstamp, uuid, status or "
6481 "teardrops" );
6482 }
6483 }
6484
6485 return via.release();
6486}
6487
6488
6490{
6491 bool front = false;
6492 bool back = false;
6493
6494 // If there is a tenting token, it means this individual pad/via has a tenting override
6495 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
6496 {
6497 if( token == T_front )
6498 front = true;
6499 else if( token == T_back )
6500 back = true;
6501 else if( token != T_none )
6502 Expecting( "front, back, or none" );
6503 }
6504
6505 aPadstack.FrontOuterLayers().has_solder_mask = front;
6506 aPadstack.BackOuterLayers().has_solder_mask = back;
6507}
6508
6509
6511{
6512 PADSTACK& padstack = aVia->Padstack();
6513
6514 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
6515 {
6516 if( token != T_LEFT )
6517 Expecting( T_LEFT );
6518
6519 token = NextTok();
6520
6521 switch( token )
6522 {
6523 case T_mode:
6524 token = NextTok();
6525
6526 switch( token )
6527 {
6528 case T_front_inner_back:
6530 break;
6531
6532 case T_custom:
6533 padstack.SetMode( PADSTACK::MODE::CUSTOM );
6534 break;
6535
6536 default:
6537 Expecting( "front_inner_back or custom" );
6538 }
6539
6540 NeedRIGHT();
6541 break;
6542
6543 case T_layer:
6544 {
6545 NextTok();
6546 PCB_LAYER_ID curLayer = UNDEFINED_LAYER;
6547
6548 if( curText == "Inner" )
6549 {
6550 if( padstack.Mode() != PADSTACK::MODE::FRONT_INNER_BACK )
6551 {
6552 THROW_IO_ERROR( wxString::Format( _( "Invalid padstack layer in\nfile: %s\n"
6553 "line: %d\noffset: %d." ),
6554 CurSource(), CurLineNumber(), CurOffset() ) );
6555 }
6556
6557 curLayer = PADSTACK::INNER_LAYERS;
6558 }
6559 else
6560 {
6561 curLayer = lookUpLayer( m_layerIndices );
6562 }
6563
6564 if( !IsCopperLayer( curLayer ) )
6565 {
6566 wxString error;
6567 error.Printf( _( "Invalid padstack layer '%s' in file '%s' at line %d, offset %d." ),
6568 curText, CurSource().GetData(), CurLineNumber(), CurOffset() );
6569 THROW_IO_ERROR( error );
6570 }
6571
6572 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6573 {
6574 if( token != T_LEFT )
6575 Expecting( T_LEFT );
6576
6577 token = NextTok();
6578
6579 switch( token )
6580 {
6581
6582 case T_size:
6583 {
6584 int diameter = parseBoardUnits( "via width" );
6585 padstack.SetSize( { diameter, diameter }, curLayer );
6586 NeedRIGHT();
6587 break;
6588 }
6589
6590 default:
6591 // Currently only supporting custom via diameter per layer, not other properties
6592 Expecting( "size" );
6593 }
6594 }
6595
6596 break;
6597 }
6598
6599 default:
6600 Expecting( "mode or layer" );
6601 break;
6602 }
6603 }
6604}
6605
6606
6608{
6609 wxCHECK_MSG( CurTok() == T_zone, nullptr,
6610 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as ZONE." ) );
6611
6612 ZONE_BORDER_DISPLAY_STYLE hatchStyle = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH;
6613
6614 int hatchPitch = ZONE::GetDefaultHatchPitch();
6615 T token;
6616 int tmp;
6617 wxString netnameFromfile; // the zone net name find in file
6618
6619 // bigger scope since each filled_polygon is concatenated in here
6620 std::map<PCB_LAYER_ID, SHAPE_POLY_SET> pts;
6621 std::map<PCB_LAYER_ID, std::vector<SEG>> legacySegs;
6622 PCB_LAYER_ID filledLayer;
6623 bool addedFilledPolygons = false;
6624 bool isStrokedFill = true;
6625
6626 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aParent );
6627
6628 zone->SetAssignedPriority( 0 );
6629
6630 // This is the default for board files:
6631 zone->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::ALWAYS );
6632
6633 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6634 {
6635 // legacy locked
6636 if( token == T_locked )
6637 {
6638 zone->SetLocked( true );
6639 token = NextTok();
6640 }
6641
6642 if( token == T_LEFT )
6643 token = NextTok();
6644
6645 switch( token )
6646 {
6647 case T_net:
6648 // Init the net code only, not the netname, to be sure
6649 // the zone net name is the name read in file.
6650 // (When mismatch, the user will be prompted in DRC, to fix the actual name)
6651 tmp = getNetCode( parseInt( "net number" ) );
6652
6653 if( tmp < 0 )
6654 tmp = 0;
6655
6656 if( !zone->SetNetCode( tmp, /* aNoAssert */ true ) )
6657 {
6658 wxLogError( _( "Invalid net ID in\nfile: %s;\nline: %d\noffset: %d." ),
6659 CurSource(), CurLineNumber(), CurOffset() );
6660 }
6661
6662 NeedRIGHT();
6663 break;
6664
6665 case T_net_name:
6666 NeedSYMBOLorNUMBER();
6667 netnameFromfile = FromUTF8();
6668 NeedRIGHT();
6669 break;
6670
6671 case T_layer: // keyword for zones that are on only one layer
6672 zone->SetLayer( parseBoardItemLayer() );
6673 NeedRIGHT();
6674 break;
6675
6676 case T_layers: // keyword for zones that can live on a set of layers
6677 zone->SetLayerSet( parseBoardItemLayersAsMask() );
6678 break;
6679
6680 case T_tstamp:
6681 case T_uuid:
6682 NextTok();
6683 const_cast<KIID&>( zone->m_Uuid ) = CurStrToKIID();
6684 NeedRIGHT();
6685 break;
6686
6687 case T_hatch:
6688 token = NextTok();
6689
6690 if( token != T_none && token != T_edge && token != T_full )
6691 Expecting( "none, edge, or full" );
6692
6693 switch( token )
6694 {
6695 default:
6696 case T_none: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH; break;
6697 case T_edge: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; break;
6698 case T_full: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL; break;
6699 }
6700
6701 hatchPitch = parseBoardUnits( "hatch pitch" );
6702 NeedRIGHT();
6703 break;
6704
6705 case T_priority:
6706 zone->SetAssignedPriority( parseInt( "zone priority" ) );
6707 NeedRIGHT();
6708 break;
6709
6710 case T_connect_pads:
6711 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6712 {
6713 if( token == T_LEFT )
6714 token = NextTok();
6715
6716 switch( token )
6717 {
6718 case T_yes:
6719 zone->SetPadConnection( ZONE_CONNECTION::FULL );
6720 break;
6721
6722 case T_no:
6723 zone->SetPadConnection( ZONE_CONNECTION::NONE );
6724 break;
6725
6726 case T_thru_hole_only:
6727 zone->SetPadConnection( ZONE_CONNECTION::THT_THERMAL );
6728 break;
6729
6730 case T_clearance:
6731 zone->SetLocalClearance( parseBoardUnits( "zone clearance" ) );
6732 NeedRIGHT();
6733 break;
6734
6735 default:
6736 Expecting( "yes, no, or clearance" );
6737 }
6738 }
6739
6740 break;
6741
6742 case T_min_thickness:
6743 zone->SetMinThickness( parseBoardUnits( T_min_thickness ) );
6744 NeedRIGHT();
6745 break;
6746
6747 case T_filled_areas_thickness:
6748 // A new zone fill strategy was added in v6, so we need to know if we're parsing
6749 // a zone that was filled before that. Note that the change was implemented as
6750 // a new parameter, so we need to check for the presence of filled_areas_thickness
6751 // instead of just its value.
6752
6753 if( !parseBool() )
6754 isStrokedFill = false;
6755
6756 NeedRIGHT();
6757 break;
6758
6759 case T_fill:
6760 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6761 {
6762 if( token == T_LEFT )
6763 token = NextTok();
6764
6765 switch( token )
6766 {
6767 case T_yes:
6768 zone->SetIsFilled( true );
6769 break;
6770
6771 case T_mode:
6772 token = NextTok();
6773
6774 if( token != T_segment && token != T_hatch && token != T_polygon )
6775 Expecting( "segment, hatch or polygon" );
6776
6777 switch( token )
6778 {
6779 case T_hatch:
6780 zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
6781 break;
6782
6783 case T_segment: // deprecated, convert to polygons
6784 case T_polygon:
6785 default:
6786 zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
6787 break;
6788 }
6789
6790 NeedRIGHT();
6791 break;
6792
6793 case T_hatch_thickness:
6794 zone->SetHatchThickness( parseBoardUnits( T_hatch_thickness ) );
6795 NeedRIGHT();
6796 break;
6797
6798 case T_hatch_gap:
6799 zone->SetHatchGap( parseBoardUnits( T_hatch_gap ) );
6800 NeedRIGHT();
6801 break;
6802
6803 case T_hatch_orientation:
6804 {
6805 EDA_ANGLE orientation( parseDouble( T_hatch_orientation ), DEGREES_T );
6806 zone->SetHatchOrientation( orientation );
6807 NeedRIGHT();
6808 break;
6809 }
6810
6811 case T_hatch_smoothing_level:
6812 zone->SetHatchSmoothingLevel( parseDouble( T_hatch_smoothing_level ) );
6813 NeedRIGHT();
6814 break;
6815
6816 case T_hatch_smoothing_value:
6817 zone->SetHatchSmoothingValue( parseDouble( T_hatch_smoothing_value ) );
6818 NeedRIGHT();
6819 break;
6820
6821 case T_hatch_border_algorithm:
6822 token = NextTok();
6823
6824 if( token != T_hatch_thickness && token != T_min_thickness )
6825 Expecting( "hatch_thickness or min_thickness" );
6826
6827 zone->SetHatchBorderAlgorithm( token == T_hatch_thickness ? 1 : 0 );
6828 NeedRIGHT();
6829 break;
6830
6831 case T_hatch_min_hole_area:
6832 zone->SetHatchHoleMinArea( parseDouble( T_hatch_min_hole_area ) );
6833 NeedRIGHT();
6834 break;
6835
6836 case T_arc_segments:
6837 ignore_unused( parseInt( "arc segment count" ) );
6838 NeedRIGHT();
6839 break;
6840
6841 case T_thermal_gap:
6842 zone->SetThermalReliefGap( parseBoardUnits( T_thermal_gap ) );
6843 NeedRIGHT();
6844 break;
6845
6846 case T_thermal_bridge_width:
6847 zone->SetThermalReliefSpokeWidth( parseBoardUnits( T_thermal_bridge_width ) );
6848 NeedRIGHT();
6849 break;
6850
6851 case T_smoothing:
6852 switch( NextTok() )
6853 {
6854 case T_none:
6855 zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_NONE );
6856 break;
6857
6858 case T_chamfer:
6859 if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
6860 zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_CHAMFER );
6861
6862 break;
6863
6864 case T_fillet:
6865 if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
6866 zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_FILLET );
6867
6868 break;
6869
6870 default:
6871 Expecting( "none, chamfer, or fillet" );
6872 }
6873
6874 NeedRIGHT();
6875 break;
6876
6877 case T_radius:
6878 tmp = parseBoardUnits( "corner radius" );
6879
6880 if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
6881 zone->SetCornerRadius( tmp );
6882
6883 NeedRIGHT();
6884 break;
6885
6886 case T_island_removal_mode:
6887 tmp = parseInt( "island_removal_mode" );
6888
6889 if( tmp >= 0 && tmp <= 2 )
6890 zone->SetIslandRemovalMode( static_cast<ISLAND_REMOVAL_MODE>( tmp ) );
6891
6892 NeedRIGHT();
6893 break;
6894
6895 case T_island_area_min:
6896 {
6897 int area = parseBoardUnits( T_island_area_min );
6898 zone->SetMinIslandArea( area * pcbIUScale.IU_PER_MM );
6899 NeedRIGHT();
6900 break;
6901 }
6902
6903 default:
6904 Expecting( "mode, arc_segments, thermal_gap, thermal_bridge_width, "
6905 "hatch_thickness, hatch_gap, hatch_orientation, "
6906 "hatch_smoothing_level, hatch_smoothing_value, "
6907 "hatch_border_algorithm, hatch_min_hole_area, smoothing, radius, "
6908 "island_removal_mode, or island_area_min" );
6909 }
6910 }
6911
6912 break;
6913
6914 case T_placement:
6915 zone->SetIsRuleArea( true );
6916
6917 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6918 {
6919 if( token == T_LEFT )
6920 token = NextTok();
6921
6922 switch( token )
6923 {
6924 case T_sheetname:
6925 {
6926 zone->SetRuleAreaPlacementSourceType(
6927 RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME );
6928 NeedSYMBOL();
6929 zone->SetRuleAreaPlacementSource( FromUTF8() );
6930 break;
6931 }
6932 case T_component_class:
6933 {
6934 zone->SetRuleAreaPlacementSourceType(
6935 RULE_AREA_PLACEMENT_SOURCE_TYPE::COMPONENT_CLASS );
6936 NeedSYMBOL();
6937 zone->SetRuleAreaPlacementSource( FromUTF8() );
6938 break;
6939 }
6940 case T_enabled:
6941 {
6942 token = NextTok();
6943
6944 if( token == T_yes )
6945 zone->SetRuleAreaPlacementEnabled( true );
6946 else if( token == T_no )
6947 zone->SetRuleAreaPlacementEnabled( false );
6948 else
6949 Expecting( "yes or no" );
6950
6951 break;
6952 }
6953 default:
6954 {
6955 Expecting( "enabled, sheetname or component_class" );
6956 break;
6957 }
6958 }
6959
6960 NeedRIGHT();
6961 }
6962
6963 break;
6964
6965 case T_keepout:
6966 // "keepout" now means rule area, but the file token stays the same
6967 zone->SetIsRuleArea( true );
6968
6969 // Initialize these two because their tokens won't appear in older files:
6970 zone->SetDoNotAllowPads( false );
6971 zone->SetDoNotAllowFootprints( false );
6972
6973 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6974 {
6975 if( token == T_LEFT )
6976 token = NextTok();
6977
6978 switch( token )
6979 {
6980 case T_tracks:
6981 token = NextTok();
6982
6983 if( token != T_allowed && token != T_not_allowed )
6984 Expecting( "allowed or not_allowed" );
6985
6986 zone->SetDoNotAllowTracks( token == T_not_allowed );
6987 break;
6988
6989 case T_vias:
6990 token = NextTok();
6991
6992 if( token != T_allowed && token != T_not_allowed )
6993 Expecting( "allowed or not_allowed" );
6994
6995 zone->SetDoNotAllowVias( token == T_not_allowed );
6996 break;
6997
6998 case T_copperpour:
6999 token = NextTok();
7000
7001 if( token != T_allowed && token != T_not_allowed )
7002 Expecting( "allowed or not_allowed" );
7003
7004 zone->SetDoNotAllowCopperPour( token == T_not_allowed );
7005 break;
7006
7007 case T_pads:
7008 token = NextTok();
7009
7010 if( token != T_allowed && token != T_not_allowed )
7011 Expecting( "allowed or not_allowed" );
7012
7013 zone->SetDoNotAllowPads( token == T_not_allowed );
7014 break;
7015
7016 case T_footprints:
7017 token = NextTok();
7018
7019 if( token != T_allowed && token != T_not_allowed )
7020 Expecting( "allowed or not_allowed" );
7021
7022 zone->SetDoNotAllowFootprints( token == T_not_allowed );
7023 break;
7024
7025 default:
7026 Expecting( "tracks, vias or copperpour" );
7027 }
7028
7029 NeedRIGHT();
7030 }
7031
7032 break;
7033
7034 case T_polygon:
7035 {
7036 SHAPE_LINE_CHAIN outline;
7037
7038 NeedLEFT();
7039 token = NextTok();
7040
7041 if( token != T_pts )
7042 Expecting( T_pts );
7043
7044 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7045 parseOutlinePoints( outline );
7046
7047 NeedRIGHT();
7048
7049 outline.SetClosed( true );
7050
7051 // Remark: The first polygon is the main outline.
7052 // Others are holes inside the main outline.
7053 zone->AddPolygon( outline );
7054 break;
7055 }
7056
7057 case T_filled_polygon:
7058 {
7059 // "(filled_polygon (pts"
7060 NeedLEFT();
7061 token = NextTok();
7062
7063 if( token == T_layer )
7064 {
7065 filledLayer = parseBoardItemLayer();
7066 NeedRIGHT();
7067 token = NextTok();
7068
7069 if( token != T_LEFT )
7070 Expecting( T_LEFT );
7071
7072 token = NextTok();
7073 }
7074 else
7075 {
7076 // for legacy, single-layer zones
7077 filledLayer = zone->GetFirstLayer();
7078 }
7079
7080 bool island = false;
7081
7082 if( token == T_island )
7083 {
7084 island = true;
7085 NeedRIGHT();
7086 NeedLEFT();
7087 token = NextTok();
7088 }
7089
7090 if( token != T_pts )
7091 Expecting( T_pts );
7092
7093 if( !pts.count( filledLayer ) )
7094 pts[filledLayer] = SHAPE_POLY_SET();
7095
7096 SHAPE_POLY_SET& poly = pts.at( filledLayer );
7097
7098 int idx = poly.NewOutline();
7099 SHAPE_LINE_CHAIN& chain = poly.Outline( idx );
7100
7101 if( island )
7102 zone->SetIsIsland( filledLayer, idx );
7103
7104 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7105 parseOutlinePoints( chain );
7106
7107 NeedRIGHT();
7108
7109 addedFilledPolygons |= !poly.IsEmpty();
7110 }
7111
7112 break;
7113
7114 case T_fill_segments:
7115 {
7116 // Legacy segment fill
7117
7118 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7119 {
7120 if( token != T_LEFT )
7121 Expecting( T_LEFT );
7122
7123 token = NextTok();
7124
7125 if( token != T_pts )
7126 Expecting( T_pts );
7127
7128 // Legacy zones only had one layer
7129 filledLayer = zone->GetFirstLayer();
7130
7131 SEG fillSegment;
7132
7133 fillSegment.A = parseXY();
7134 fillSegment.B = parseXY();
7135
7136 legacySegs[filledLayer].push_back( fillSegment );
7137
7138 NeedRIGHT();
7139 }
7140
7141 break;
7142 }
7143
7144 case T_name:
7145 NextTok();
7146 zone->SetZoneName( FromUTF8() );
7147 NeedRIGHT();
7148 break;
7149
7150 case T_attr:
7151 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7152 {
7153 if( token == T_LEFT )
7154 token = NextTok();
7155
7156 switch( token )
7157 {
7158 case T_teardrop:
7159 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7160 {
7161 if( token == T_LEFT )
7162 token = NextTok();
7163
7164 switch( token )
7165 {
7166 case T_type:
7167 token = NextTok();
7168
7169 if( token == T_padvia )
7170 zone->SetTeardropAreaType( TEARDROP_TYPE::TD_VIAPAD );
7171 else if( token == T_track_end )
7172 zone->SetTeardropAreaType( TEARDROP_TYPE::TD_TRACKEND );
7173 else
7174 Expecting( "padvia or track_end" );
7175
7176 NeedRIGHT();
7177 break;
7178
7179 default:
7180 Expecting( "type" );
7181 }
7182 }
7183
7184 break;
7185
7186 default:
7187 Expecting( "teardrop" );
7188 }
7189 }
7190 break;
7191
7192 case T_locked:
7193 zone->SetLocked( parseBool() );
7194 NeedRIGHT();
7195 break;
7196
7197 default:
7198 Expecting( "net, layer/layers, tstamp, hatch, priority, connect_pads, min_thickness, "
7199 "fill, polygon, filled_polygon, fill_segments, attr, locked, uuid, or name" );
7200 }
7201 }
7202
7203 if( zone->GetNumCorners() > 2 )
7204 {
7205 if( !zone->IsOnCopperLayer() )
7206 {
7207 //zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
7208 zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
7209 }
7210
7211 // Set hatch here, after outlines corners are read
7212 zone->SetBorderDisplayStyle( hatchStyle, hatchPitch, true );
7213 }
7214
7215 if( addedFilledPolygons )
7216 {
7217 if( isStrokedFill && !zone->GetIsRuleArea() )
7218 {
7220 {
7221 wxLogWarning(
7222 _( "Legacy zone fill strategy is not supported anymore.\nZone fills will "
7223 "be converted on best-effort basis." ) );
7224
7226 }
7227
7228 if( zone->GetMinThickness() > 0 )
7229 {
7230 for( auto& [layer, polyset] : pts )
7231 {
7232 polyset.InflateWithLinkedHoles( zone->GetMinThickness() / 2,
7233 CORNER_STRATEGY::ROUND_ALL_CORNERS,
7234 ARC_HIGH_DEF / 2,
7236 }
7237 }
7238 }
7239
7240 for( auto& [layer, polyset] : pts )
7241 zone->SetFilledPolysList( layer, polyset );
7242
7243 zone->CalculateFilledArea();
7244 }
7245 else if( legacySegs.size() > 0 )
7246 {
7247 // No polygons, just segment fill?
7248 // Note RFB: This code might be removed if turns out this never existed for sexpr file
7249 // format or otherwise we should add a test case to the qa folder
7250
7252 {
7253 wxLogWarning( _( "The legacy segment zone fill mode is no longer supported.\n"
7254 "Zone fills will be converted on a best-effort basis." ) );
7255
7257 }
7258
7259
7260 for( const auto& [layer, segments] : legacySegs )
7261 {
7262 SHAPE_POLY_SET layerFill;
7263
7264 if( zone->HasFilledPolysForLayer( layer ) )
7265 layerFill = SHAPE_POLY_SET( *zone->GetFill( layer ) );
7266
7267 for( const auto& seg : segments )
7268 {
7269 SHAPE_POLY_SET segPolygon;
7270
7271 TransformOvalToPolygon( segPolygon, seg.A, seg.B, zone->GetMinThickness(),
7273
7274 layerFill.BooleanAdd( segPolygon, SHAPE_POLY_SET::PM_FAST );
7275 }
7276
7277
7278 zone->SetFilledPolysList( layer, layerFill );
7279 zone->CalculateFilledArea();
7280 }
7281 }
7282
7283
7284 // Ensure keepout and non copper zones do not have a net
7285 // (which have no sense for these zones)
7286 // the netcode 0 is used for these zones
7287 bool zone_has_net = zone->IsOnCopperLayer() && !zone->GetIsRuleArea();
7288
7289 if( !zone_has_net )
7290 zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
7291
7292 // Ensure the zone net name is valid, and matches the net code, for copper zones
7293 if( zone_has_net
7294 && ( !zone->GetNet() || zone->GetNet()->GetNetname() != netnameFromfile ) )
7295 {
7296 // Can happens which old boards, with nonexistent nets ...
7297 // or after being edited by hand
7298 // We try to fix the mismatch.
7299 NETINFO_ITEM* net = m_board->FindNet( netnameFromfile );
7300
7301 if( net ) // An existing net has the same net name. use it for the zone
7302 {
7303 zone->SetNetCode( net->GetNetCode() );
7304 }
7305 else // Not existing net: add a new net to keep trace of the zone netname
7306 {
7307 int newnetcode = m_board->GetNetCount();
7308 net = new NETINFO_ITEM( m_board, netnameFromfile, newnetcode );
7309 m_board->Add( net, ADD_MODE::INSERT, true );
7310
7311 // Store the new code mapping
7312 pushValueIntoMap( newnetcode, net->GetNetCode() );
7313
7314 // and update the zone netcode
7315 zone->SetNetCode( net->GetNetCode() );
7316 }
7317 }
7318
7319 if( zone->IsTeardropArea() && m_requiredVersion < 20230517 )
7320 m_board->SetLegacyTeardrops( true );
7321
7322 // Clear flags used in zone edition:
7323 zone->SetNeedRefill( false );
7324
7325 return zone.release();
7326}
7327
7328
7330{
7331 wxCHECK_MSG( CurTok() == T_target, nullptr,
7332 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TARGET." ) );
7333
7334 VECTOR2I pt;
7335 T token;
7336
7337 std::unique_ptr<PCB_TARGET> target = std::make_unique<PCB_TARGET>( nullptr );
7338
7339 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7340 {
7341 if( token == T_LEFT )
7342 token = NextTok();
7343
7344 switch( token )
7345 {
7346 case T_x:
7347 target->SetShape( 1 );
7348 break;
7349
7350 case T_plus:
7351 target->SetShape( 0 );
7352 break;
7353
7354 case T_at:
7355 pt.x = parseBoardUnits( "target x position" );
7356 pt.y = parseBoardUnits( "target y position" );
7357 target->SetPosition( pt );
7358 NeedRIGHT();
7359 break;
7360
7361 case T_size:
7362 target->SetSize( parseBoardUnits( "target size" ) );
7363 NeedRIGHT();
7364 break;
7365
7366 case T_width:
7367 target->SetWidth( parseBoardUnits( "target thickness" ) );
7368 NeedRIGHT();
7369 break;
7370
7371 case T_layer:
7372 target->SetLayer( parseBoardItemLayer() );
7373 NeedRIGHT();
7374 break;
7375
7376 case T_tstamp:
7377 case T_uuid:
7378 NextTok();
7379 const_cast<KIID&>( target->m_Uuid ) = CurStrToKIID();
7380 NeedRIGHT();
7381 break;
7382
7383 default:
7384 Expecting( "x, plus, at, size, width, layer, uuid, or tstamp" );
7385 }
7386 }
7387
7388 return target.release();
7389}
7390
7392 EDA_TEXT* aReplacer )
7393{
7394 if( auto it = m_fontTextMap.find( aEntry ); it != m_fontTextMap.end() )
7395 {
7396 // Replace it by aReplacer if not nullptr
7397 if( aReplacer )
7398 {
7399 m_fontTextMap[aReplacer] = it->second;
7400 }
7401
7402 // Remove old entry from m_fontTextMap.
7403 m_fontTextMap.erase( it );
7404 }
7405}
7406
7408{
7409 KIID aId;
7410 std::string idStr( CurStr() );
7411
7412 // Older files did not quote UUIDs
7413 if( *idStr.begin() == '"' && *idStr.rbegin() == '"' )
7414 idStr = idStr.substr( 1, idStr.length() - 1 );
7415
7416 if( m_appendToExisting )
7417 {
7418 aId = KIID();
7419 m_resetKIIDMap.insert( std::make_pair( idStr, aId ) );
7420 }
7421 else
7422 {
7423 aId = KIID( idStr );
7424 }
7425
7426 return aId;
7427}
int color
Definition: DXF_plotter.cpp: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:298
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:778
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition: board.cpp:2550
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:1003
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:798
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:546
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1922
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:983
PCB_LAYER_ID GetLayerID(const wxString &aLayerName) const
Return the ID of a layer.
Definition: board.cpp:554
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:741
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:747
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:574
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:1125
wxString GroupsSanityCheck(bool repair=false)
Consistency check of internal m_groups structure.
Definition: board.cpp:2753
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:895
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:286
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:141
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:131
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:178
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:80
bool IsItalic() const
Definition: eda_text.h:156
const EDA_ANGLE & GetTextAngle() const
Definition: eda_text.h:134
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition: eda_text.cpp:506
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:98
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:551
void SetMirrored(bool isMirrored)
Definition: eda_text.cpp:384
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition: eda_text.cpp:408
void SetBoldFlag(bool aBold)
Set only the italic flag, without changing the font.
Definition: eda_text.cpp:369
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:377
void SetLineSpacing(double aLineSpacing)
Definition: eda_text.cpp:498
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:283
void SetItalicFlag(bool aItalic)
Set only the italic flag, without changing the font.
Definition: eda_text.cpp:319
bool IsBold() const
Definition: eda_text.h:171
void SetKeepUpright(bool aKeepUpright)
Definition: eda_text.cpp:416
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:269
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:291
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:400
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:1025
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:323
static GAL_SET DefaultVisible()
Definition: lset.cpp:852
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:675
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:686
static int NameToLayer(wxString &aName)
Return the layer number from a layer name.
Definition: lset.cpp:117
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:420
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition: lset.cpp:193
Handle the data for a net.
Definition: netinfo.h:56
const wxString & GetNetname() const
Definition: netinfo.h:114
void SetNetCode(int aNetCode)
Definition: netinfo.h:109
int GetNetCode() const
Definition: netinfo.h:108
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition: netinfo.h:381
static const int ORPHANED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition: netinfo.h:385
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
Definition: padstack.h:124
std::optional< int > & Clearance(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1162
void AddPrimitive(PCB_SHAPE *aShape, PCB_LAYER_ID aLayer)
Adds a custom shape primitive to the padstack.
Definition: padstack.cpp:1288
MASK_LAYER_PROPS & FrontOuterLayers()
Definition: padstack.h:312
void SetThermalSpokeAngle(EDA_ANGLE aAngle, PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1270
void SetMode(MODE aMode)
Definition: padstack.cpp:821
std::optional< int > & ThermalSpokeWidth(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1227
std::optional< int > & ThermalGap(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1239
void SetAnchorShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1085
@ CUSTOM
Shapes can be defined on arbitrary layers.
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
MODE Mode() const
Definition: padstack.h:287
MASK_LAYER_PROPS & BackOuterLayers()
Definition: padstack.h:315
void SetSize(const VECTOR2I &aSize, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1040
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:144
static constexpr PCB_LAYER_ID INNER_LAYERS
! The layer identifier to use for "inner layers" on top/inner/bottom padstacks
Definition: padstack.h:147
std::optional< ZONE_CONNECTION > & ZoneConnection(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1215
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:468
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:453
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition: page_info.h:59
void SetPortrait(bool aIsPortrait)
Rotate the paper page 90 degrees.
Definition: page_info.cpp:187
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:120
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...
void removeReplaceEntryInFontTextMap(EDA_TEXT *aEntry, EDA_TEXT *aReplacer)
Remove an entry from m_fontTextMap.
PCB_DIMENSION_BASE * parseDIMENSION(BOARD_ITEM *aParent)
LSET lookUpLayerSet(const LSET_MAP &aMap)
bool m_appendToExisting
reading into an existing board; reset UUIDs
void parsePCB_TEXT_effects(PCB_TEXT *aText, PCB_TEXT *aBaseText=nullptr)
PCB_SHAPE * parsePCB_SHAPE(BOARD_ITEM *aParent)
void parseDefaults(BOARD_DESIGN_SETTINGS &aSettings)
wxString GetRequiredVersion()
Return a string representing the version of KiCad required to open this file.
The parser for PCB_PLOT_PARAMS.
Parameters and options when plotting/printing a board.
std::optional< bool > GetLegacyPlotViaOnMaskLayer() const
void Parse(PCB_PLOT_PARAMS_PARSER *aParser)
Object to handle a bitmap image that can be inserted in a PCB.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:170
void SetIsProxyItem(bool aIsProxy=true) override
Definition: pcb_shape.cpp:530
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:90
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
void SetBorderEnabled(bool enabled)
void SetMarginTop(int aTop)
Definition: pcb_textbox.h:83
void SetMarginLeft(int aLeft)
Definition: pcb_textbox.h:82
void SetMarginBottom(int aBottom)
Definition: pcb_textbox.h:85
void SetMarginRight(int aRight)
Definition: pcb_textbox.h:84
void Move(const VECTOR2I &aMoveVector) override
Move this object.
int GetLegacyTextMargin() const
void StyleFromSettings(const BOARD_DESIGN_SETTINGS &settings) override
Definition: pcb_text.cpp:305
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:373
const PADSTACK & Padstack() const
Definition: pcb_track.h:412
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
bool ReadImageFile(const wxString &aFullFilename)
Read and store an image file.
double GetImageScale() const
void SetImageScale(double aScale)
Set the image "zoom" value.
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
Represent a set of closed polygons.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
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:487
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:133
static int GetDefaultHatchPitch()
Definition: zone.cpp:1281
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:495
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:531
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:92
DIM_PRECISION
Definition: pcb_dimension.h:47
Class to handle a set of BOARD_ITEMs.
#define SEXPR_BOARD_FILE_VERSION
Current s-expression file format version. 2 was the last legacy format version.
#define LEGACY_ARC_FORMATTING
These were the last to use old arc formatting.
#define LEGACY_NET_TIES
These were the last to use the keywords field to indicate a net-tie.
#define BOARD_FILE_HOST_VERSION
Earlier files than this include the host tag.
constexpr double INT_LIMIT
Pcbnew s-expression file format parser definition.
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: pgm_base.cpp: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:660
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:231
A filename or source description, a problem input line, a line number, a byte offset,...
Definition: ki_exception.h:120
Convert net code using the mapping table if available, otherwise returns unchanged net code if < 0 or...
Container to handle a stock of specific vias each with unique diameter and drill sizes in the BOARD c...
@ VALUE_FIELD
Field Value of part, i.e. "3.3K".
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".
@ DESCRIPTION_FIELD
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
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