KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_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) 2020 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Wayne Stambaugh <[email protected]>
8 *
9 * This program is free software: you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
26
27// For some reason wxWidgets is built with wxUSE_BASE64 unset so expose the wxWidgets
28// base64 code.
29#include <charconv>
30
31#include <fmt/format.h>
32#define wxUSE_BASE64 1
33#include <wx/base64.h>
34#include <wx/log.h>
35#include <wx/mstream.h>
36#include <wx/tokenzr.h>
37
38#include <base_units.h>
39#include <bitmap_base.h>
41#include <lib_id.h>
42#include <sch_pin.h>
43#include <math/util.h> // KiROUND
44#include <font/font.h>
45#include <font/fontconfig.h>
46#include <pgm_base.h>
47#include <string_utils.h>
48#include <sch_bitmap.h>
49#include <sch_bus_entry.h>
50#include <sch_symbol.h>
51#include <sch_netchain.h>
52#include <sch_edit_frame.h> // SYM_ORIENT_XXX
53#include <sch_field.h>
54#include <sch_group.h>
55#include <sch_line.h>
56#include <sch_rule_area.h>
57#include <sch_textbox.h>
58#include <sch_table.h>
59#include <sch_tablecell.h>
60#include <sch_label.h>
61#include <sch_junction.h>
62#include <sch_no_connect.h>
63#include <sch_screen.h>
64#include <sch_shape.h>
65#include <sch_sheet_pin.h>
68#include <template_fieldnames.h>
69#include <trigo.h>
70#include <progress_reporter.h>
71#include <sim/sim_model.h>
72
73
74using namespace TSCHEMATIC_T;
75
76
78 PROGRESS_REPORTER* aProgressReporter,
79 unsigned aLineCount, SCH_SHEET* aRootSheet,
80 bool aIsAppending ) :
81 SCHEMATIC_LEXER( aLineReader ),
83 m_unit( 1 ),
84 m_bodyStyle( 1 ),
85 m_appending( aIsAppending ),
86 m_progressReporter( aProgressReporter ),
87 m_lineReader( aLineReader ),
89 m_lineCount( aLineCount ),
90 m_rootSheet( aRootSheet ),
92{
93}
94
95
97{
99 {
100 unsigned progressDelta = std::max( 50u, m_lineCount / 10 );
101 unsigned curLine = m_lineReader->LineNumber();
102
103 if( m_lastProgressLine == 0 || curLine > m_lastProgressLine + progressDelta )
104 {
105 m_progressReporter->SetCurrentProgress( ( (double) curLine )
106 / std::max( 1U, m_lineCount ) );
107
108 if( !m_progressReporter->KeepRefreshing() )
109 THROW_IO_ERROR( _( "Open canceled by user." ) );
110
111 m_lastProgressLine = curLine;
112 }
113 }
114}
115
116
118{
119 KIID id( FromUTF8() );
120
121 while( m_uuids.count( id ) )
122 id.Increment();
123
124 m_uuids.insert( id );
125
126 return id;
127}
128
129
131{
132 T token = NextTok();
133
134 if( token == T_yes )
135 return true;
136 else if( token == T_no )
137 return false;
138 else
139 Expecting( "yes or no" );
140
141 return false;
142}
143
144
145/*
146 * e.g. "hide", "hide)", "(hide yes)"
147 */
149{
150 bool ret = aDefaultValue;
151
152 if( PrevTok() == T_LEFT )
153 {
154 T token = NextTok();
155
156 // "hide)"
157 if( static_cast<int>( token ) == DSN_RIGHT )
158 return aDefaultValue;
159
160 if( token == T_yes )
161 ret = true;
162 else if( token == T_no )
163 ret = false;
164 else
165 Expecting( "yes or no" );
166
167 NeedRIGHT();
168 }
169 else
170 {
171 // "hide"
172 return aDefaultValue;
173 }
174
175 return ret;
176}
177
178
180{
181 T token;
182
183 NeedLEFT();
184 NextTok();
185 parseHeader( T_kicad_symbol_lib, SEXPR_SYMBOL_LIB_FILE_VERSION );
186
187 // Prior to this, bar was a valid string char for unquoted strings.
188 SetKnowsBar( m_requiredVersion >= 20240529 );
189
190 bool versionChecked = false;
191
192 auto checkVersion =
193 [&]()
194 {
195 if( !versionChecked && m_requiredVersion > SEXPR_SYMBOL_LIB_FILE_VERSION )
196 {
197 throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
199 }
200
201 versionChecked = true;
202 };
203
204 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
205 {
206 if( token != T_LEFT )
207 Expecting( T_LEFT );
208
209 token = NextTok();
210
211 switch( token )
212 {
213 case T_generator:
214 // (generator "genname"); we don't care about it at the moment.
215 NeedSYMBOL();
216 NeedRIGHT();
217 break;
218
219 case T_host:
220 {
221 // (host eeschema ["5.99"]); old version of generator token
222 NeedSYMBOL();
223
224 // Really old versions also included a host version
225 if( m_requiredVersion < 20200827 )
226 NeedSYMBOL();
227
228 NeedRIGHT();
229 break;
230 }
231
232 case T_generator_version:
233 {
234 NextTok();
235 m_generatorVersion = FromUTF8();
236 NeedRIGHT();
237
238 // If the format includes a generator version, by this point we have enough info to
239 // do the version check here
240 checkVersion();
241 break;
242 }
243
244 case T_symbol:
245 {
246 // By the time we get to the first symbol, we can check the version
247 checkVersion();
248
249 m_unit = 1;
250 m_bodyStyle = 1;
251
252 try
253 {
254 LIB_SYMBOL* symbol = parseLibSymbol( aSymbolLibMap );
255 aSymbolLibMap[symbol->GetName()] = symbol;
256 }
257 catch( const IO_ERROR& e )
258 {
259 // Record the error and skip to the end of this symbol block
260 wxString warning = wxString::Format(
261 _( "Error parsing symbol at line %d: %s\nSkipping symbol and continuing." ),
262 CurLineNumber(), e.What() );
263 m_parseWarnings.push_back( warning );
264
265 // Skip to the end of this symbol's S-expression block
266 // We're already past T_symbol, so we're inside the symbol definition
267 skipToBlockEnd( 1 );
268 }
269
270 break;
271 }
272
273 default:
274 Expecting( "symbol, generator, or generator_version" );
275 }
276 }
277}
278
279
281 int aFileVersion )
282{
283 LIB_SYMBOL* newSymbol = nullptr;
284
285 NextTok();
286
287 // If there actually isn't anything here, don't throw just return a nullptr
288 if( CurTok() == T_LEFT )
289 {
290 NextTok();
291
292 if( CurTok() == T_symbol )
293 {
294 m_requiredVersion = aFileVersion;
295 newSymbol = parseLibSymbol( aSymbolLibMap );
296
297 const std::vector<wxString>* embeddedFonts =
298 newSymbol->GetEmbeddedFiles()->UpdateFontFiles();
299
300 newSymbol->RunOnChildren(
301 [&]( SCH_ITEM* aChild )
302 {
303 if( EDA_TEXT* textItem = dynamic_cast<EDA_TEXT*>( aChild ) )
304 textItem->ResolveFont( embeddedFonts );
305 },
307 }
308 else
309 {
310 wxString msg = wxString::Format( _( "Cannot parse %s as a symbol" ),
311 GetTokenString( CurTok() ) );
312 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
313 }
314 }
315
316 return newSymbol;
317}
318
319
321{
322 wxCHECK_MSG( CurTok() == T_symbol, nullptr,
323 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
324
325 T token;
326 long tmp;
327 wxString name;
328 wxString error;
329 SCH_ITEM* item;
330 std::unique_ptr<LIB_SYMBOL> symbol = std::make_unique<LIB_SYMBOL>( wxEmptyString );
331
332 symbol->SetUnitCount( 1, true );
333
334 token = NextTok();
335
336 if( !IsSymbol( token ) )
337 THROW_PARSE_ERROR( _( "Invalid symbol name" ), CurSource(), CurLine(), CurLineNumber(), CurOffset() );
338
339 name = FromUTF8();
340
341 // Some symbol LIB_IDs have the '/' character escaped which can break derived symbol links.
342 // The '/' character is no longer an illegal LIB_ID character so it doesn't need to be
343 // escaped.
344 name.Replace( wxS( "{slash}" ), wxT( "/" ) );
345
346 LIB_ID id;
347 int bad_pos = id.Parse( name );
348
349 if( bad_pos >= 0 )
350 {
351 if( static_cast<int>( name.size() ) > bad_pos )
352 {
353 wxString msg = wxString::Format( _( "Symbol %s contains invalid character '%c'" ),
354 name,
355 name[bad_pos] );
356
357 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
358 }
359
360
361 THROW_PARSE_ERROR( _( "Invalid library identifier" ), CurSource(), CurLine(),
362 CurLineNumber(), CurOffset() );
363 }
364
365 m_symbolName = id.GetLibItemName().wx_str();
366 symbol->SetName( m_symbolName );
367 symbol->SetLibId( id );
368
369 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
370 {
371 if( token != T_LEFT )
372 Expecting( T_LEFT );
373
374 token = NextTok();
375
376 switch( token )
377 {
378 case T_power:
379 symbol->SetGlobalPower();
380 token = NextTok();
381
382 if( token == T_RIGHT )
383 break;
384
385 if( token == T_local )
386 symbol->SetLocalPower();
387 else if( token != T_global )
388 Expecting( "global or local" );
389
390 NeedRIGHT();
391 break;
392
393 case T_body_styles:
394 parseBodyStyles( symbol );
395 break;
396
397 case T_pin_names:
398 parsePinNames( symbol );
399 break;
400
401 case T_pin_numbers:
402 parsePinNumbers( symbol );
403 break;
404
405 case T_exclude_from_sim:
406 symbol->SetExcludedFromSim( parseBool() );
407 NeedRIGHT();
408 break;
409
410 case T_in_bom:
411 symbol->SetExcludedFromBOM( !parseBool() );
412 NeedRIGHT();
413 break;
414
415 case T_on_board:
416 symbol->SetExcludedFromBoard( !parseBool() );
417 NeedRIGHT();
418 break;
419
420 case T_in_pos_files:
421 symbol->SetExcludedFromPosFiles( !parseBool() );
422 NeedRIGHT();
423 break;
424
425 case T_duplicate_pin_numbers_are_jumpers:
426 symbol->SetDuplicatePinNumbersAreJumpers( parseBool() );
427 NeedRIGHT();
428 break;
429
430 case T_jumper_pin_groups:
431 {
432 std::vector<std::set<wxString>>& groups = symbol->JumperPinGroups();
433 std::set<wxString>* currentGroup = nullptr;
434
435 for( token = NextTok(); currentGroup || token != T_RIGHT; token = NextTok() )
436 {
437 switch( static_cast<int>( token ) )
438 {
439 case T_LEFT:
440 currentGroup = &groups.emplace_back();
441 break;
442
443 case DSN_STRING:
444 currentGroup->insert( FromUTF8() );
445 break;
446
447 case T_RIGHT:
448 currentGroup = nullptr;
449 break;
450
451 default:
452 Expecting( "list of pin names" );
453 }
454 }
455
456 break;
457 }
458
459 case T_property:
460 parseProperty( symbol );
461 break;
462
463 case T_extends:
464 {
465 token = NextTok();
466
467 if( !IsSymbol( token ) )
468 {
469 THROW_PARSE_ERROR( _( "Invalid parent symbol name" ), CurSource(), CurLine(),
470 CurLineNumber(), CurOffset() );
471 }
472
473 name = FromUTF8();
474
475 // Some symbol LIB_IDs have the '/' character escaped which can break derived
476 // symbol links. The '/' character is no longer an illegal LIB_ID character so
477 // it doesn't need to be escaped.
478 name.Replace( wxS( "{slash}" ), wxT( "/" ) );
479
480 symbol->SetParentName( name );
481 NeedRIGHT();
482 break;
483 }
484
485 case T_symbol:
486 {
487 token = NextTok();
488
489 if( !IsSymbol( token ) )
490 {
491 THROW_PARSE_ERROR( _( "Invalid symbol unit name" ), CurSource(), CurLine(),
492 CurLineNumber(), CurOffset() );
493 }
494
495 name = FromUTF8();
496
497 // Some symbol LIB_IDs have the '/' character escaped which can break derived
498 // symbol links. The '/' character is no longer an illegal LIB_ID character so
499 // it doesn't need to be escaped.
500 name.Replace( wxS( "{slash}" ), wxT( "/" ) );
501
502 if( !name.StartsWith( m_symbolName ) )
503 {
504 error.Printf( _( "Invalid symbol unit name prefix %s" ), name.c_str() );
505 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
506 }
507
508 name = name.Right( name.Length() - m_symbolName.Length() - 1 );
509
510 wxStringTokenizer tokenizer( name, "_" );
511
512 if( tokenizer.CountTokens() != 2 )
513 {
514 error.Printf( _( "Invalid symbol unit name suffix %s" ), name.c_str() );
515 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
516 }
517
518 if( !tokenizer.GetNextToken().ToLong( &tmp ) )
519 {
520 error.Printf( _( "Invalid symbol unit number %s" ), name.c_str() );
521 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
522 }
523
524 m_unit = static_cast<int>( tmp );
525
526 if( !tokenizer.GetNextToken().ToLong( &tmp ) )
527 {
528 error.Printf( _( "Invalid symbol body style number %s" ), name.c_str() );
529 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
530 }
531
532 m_bodyStyle = static_cast<int>( tmp );
533
534 if( m_bodyStyle > 1 )
535 symbol->SetBodyStyleCount( m_bodyStyle, false, false );
536
537 if( m_unit > symbol->GetUnitCount() )
538 symbol->SetUnitCount( m_unit, false );
539
540 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
541 {
542 if( token != T_LEFT )
543 Expecting( T_LEFT );
544
545 token = NextTok();
546
547 switch( token )
548 {
549 case T_unit_name:
550 token = NextTok();
551
552 if( IsSymbol( token ) )
553 symbol->GetUnitDisplayNames()[m_unit] = FromUTF8();
554
555 NeedRIGHT();
556 break;
557
558 case T_arc:
559 case T_bezier:
560 case T_circle:
561 case T_ellipse:
562 case T_ellipse_arc:
563 case T_pin:
564 case T_polyline:
565 case T_rectangle:
566 case T_text:
567 case T_text_box:
568 item = ParseSymbolDrawItem();
569
570 wxCHECK_MSG( item, nullptr, "Invalid draw item pointer." );
571
572 item->SetParent( symbol.get() );
573 symbol->AddDrawItem( item, false );
574 break;
575
576 default:
577 Expecting( "arc, bezier, circle, ellipse, ellipse_arc, pin, polyline, "
578 "rectangle, or text" );
579 };
580 }
581
582 m_unit = 1;
583 m_bodyStyle = 1;
584 break;
585 }
586
587 case T_arc:
588 case T_bezier:
589 case T_circle:
590 case T_ellipse:
591 case T_ellipse_arc:
592 case T_pin:
593 case T_polyline:
594 case T_rectangle:
595 case T_text:
596 case T_text_box:
597 item = ParseSymbolDrawItem();
598
599 wxCHECK_MSG( item, nullptr, "Invalid draw item pointer." );
600
601 item->SetParent( symbol.get() );
602 symbol->AddDrawItem( item, false );
603 break;
604
605 case T_embedded_fonts:
606 {
607 symbol->SetAreFontsEmbedded( parseBool() );
608 NeedRIGHT();
609 break;
610 }
611
612 case T_embedded_files:
613 {
614 EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
615 embeddedFilesParser.SyncLineReaderWith( *this );
616
617 try
618 {
619 embeddedFilesParser.ParseEmbedded( symbol->GetEmbeddedFiles() );
620 }
621 catch( const IO_ERROR& e )
622 {
623 m_parseWarnings.push_back( e.What() );
624
625 int depth = 0;
626
627 for( int tok = embeddedFilesParser.NextTok();
628 tok != DSN_EOF;
629 tok = embeddedFilesParser.NextTok() )
630 {
631 if( tok == DSN_LEFT )
632 depth++;
633 else if( tok == DSN_RIGHT && --depth < 0 )
634 break;
635 }
636 }
637
638 SyncLineReaderWith( embeddedFilesParser );
639 break;
640 }
641
642 default:
643 Expecting( "pin_names, pin_numbers, arc, bezier, circle, ellipse, ellipse_arc, "
644 "pin, polyline, rectangle, or text" );
645 }
646 }
647
648 symbol->GetDrawItems().sort();
649 m_symbolName.clear();
650
651 const std::vector<wxString>* embeddedFonts = symbol->GetEmbeddedFiles()->UpdateFontFiles();
652
653 symbol->RunOnChildren(
654 [&]( SCH_ITEM* aChild )
655 {
656 if( EDA_TEXT* textItem = dynamic_cast<EDA_TEXT*>( aChild ) )
657 textItem->ResolveFont( embeddedFonts );
658 },
660
661 // Before V10 we didn't store the number of body styles in a symbol, we just looked at all its
662 // drawings each time we wanted to know.
663 if( m_requiredVersion < 20250827 )
664 symbol->SetHasDeMorganBodyStyles( symbol->HasLegacyAlternateBodyStyle() );
665
666 symbol->RefreshLibraryTreeCaches();
667
668 return symbol.release();
669}
670
671
673{
674 switch( CurTok() )
675 {
676 case T_arc: return parseSymbolArc(); break;
677 case T_bezier: return parseSymbolBezier(); break;
678 case T_circle: return parseSymbolCircle(); break;
679 case T_ellipse: return parseSymbolEllipse(); break;
680 case T_ellipse_arc: return parseSymbolEllipseArc(); break;
681 case T_pin: return parseSymbolPin(); break;
682 case T_polyline: return parseSymbolPolyLine(); break;
683 case T_rectangle: return parseSymbolRectangle(); break;
684 case T_text: return parseSymbolText(); break;
685 case T_text_box: return parseSymbolTextBox(); break;
686 default:
687 Expecting( "arc, bezier, circle, ellipse, ellipse_arc, pin, "
688 "polyline, rectangle, or text" );
689 }
690
691 return nullptr;
692}
693
694
696{
697 auto retval = parseDouble() * schIUScale.IU_PER_MM;
698
699 // Schematic internal units are represented as integers. Any values that are
700 // larger or smaller than the schematic units represent undefined behavior for
701 // the system. Limit values to the largest that can be displayed on the screen.
702 constexpr double int_limit = std::numeric_limits<int>::max() * 0.7071; // 0.7071 = roughly 1/sqrt(2)
703
704 return KiROUND( std::clamp( retval, -int_limit, int_limit ) );
705}
706
707
709{
710 auto retval = parseDouble( aExpected ) * schIUScale.IU_PER_MM;
711
712 constexpr double int_limit = std::numeric_limits<int>::max() * 0.7071;
713
714 return KiROUND( std::clamp( retval, -int_limit, int_limit ) );
715}
716
717
719{
720 STROKE_PARAMS_PARSER strokeParser( reader, schIUScale.IU_PER_MM );
721 strokeParser.SyncLineReaderWith( *this );
722
723 strokeParser.ParseStroke( aStroke );
724 SyncLineReaderWith( strokeParser );
725}
726
727
729{
730 wxCHECK_RET( CurTok() == T_fill, "Cannot parse " + GetTokenString( CurTok() ) + " as a fill." );
731
734
735 T token;
736
737 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
738 {
739 if( token != T_LEFT )
740 Expecting( T_LEFT );
741
742 token = NextTok();
743
744 switch( token )
745 {
746 case T_type:
747 {
748 token = NextTok();
749
750 switch( token )
751 {
752 case T_none: aFill.m_FillType = FILL_T::NO_FILL; break;
753 case T_outline: aFill.m_FillType = FILL_T::FILLED_SHAPE; break;
754 case T_background: aFill.m_FillType = FILL_T::FILLED_WITH_BG_BODYCOLOR; break;
755 case T_color: aFill.m_FillType = FILL_T::FILLED_WITH_COLOR; break;
756 case T_hatch: aFill.m_FillType = FILL_T::HATCH; break;
757 case T_reverse_hatch: aFill.m_FillType = FILL_T::REVERSE_HATCH; break;
758 case T_cross_hatch: aFill.m_FillType = FILL_T::CROSS_HATCH; break;
759 default: Expecting( "none, outline, hatch, reverse_hatch, "
760 "cross_hatch, color or background" );
761 }
762
763 NeedRIGHT();
764 break;
765 }
766
767 case T_color:
768 {
769 COLOR4D color;
770
771 color.r = parseInt( "red" ) / 255.0;
772 color.g = parseInt( "green" ) / 255.0;
773 color.b = parseInt( "blue" ) / 255.0;
774 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
775 aFill.m_Color = color;
776 NeedRIGHT();
777 break;
778 }
779
780 default:
781 Expecting( "type or color" );
782 }
783 }
784}
785
786
787void SCH_IO_KICAD_SEXPR_PARSER::parseEDA_TEXT( EDA_TEXT* aText, bool aConvertOverbarSyntax,
788 bool aEnforceMinTextSize )
789{
790 wxCHECK_RET( aText && ( CurTok() == T_effects || CurTok() == T_href ),
791 "Cannot parse " + GetTokenString( CurTok() ) + " as an EDA_TEXT." );
792
793 // In version 20210606 the notation for overbars was changed from `~...~` to `~{...}`.
794 // We need to convert the old syntax to the new one.
795 if( aConvertOverbarSyntax && m_requiredVersion < 20210606 )
796 aText->SetText( ConvertToNewOverbarNotation( aText->GetText() ) );
797
798 T token;
799 bool bold = false;
800 bool italic = false;
802
803 // Various text objects (text boxes, schematic text, etc.) all have their own defaults,
804 // but the file format default is {center,center} so we have to set that before parsing.
807
808 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
809 {
810 if( token == T_LEFT )
811 token = NextTok();
812
813 switch( token )
814 {
815 case T_font:
816 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
817 {
818 if( token == T_LEFT )
819 token = NextTok();
820
821 switch( token )
822 {
823 case T_face:
824 NeedSYMBOL();
825 aText->SetUnresolvedFontName( FromUTF8() );
826 NeedRIGHT();
827 break;
828
829 case T_size:
830 {
831 VECTOR2I sz;
832 sz.y = parseInternalUnits( "text height" );
833 sz.x = parseInternalUnits( "text width" );
834 aText->SetTextSize( sz, aEnforceMinTextSize );
835 NeedRIGHT();
836 break;
837 }
838
839 case T_thickness:
840 aText->SetTextThickness( parseInternalUnits( "text thickness" ) );
841 NeedRIGHT();
842 break;
843
844 case T_bold:
845 bold = parseMaybeAbsentBool( true );
846 aText->SetBoldFlag( bold );
847 break;
848
849 case T_italic:
850 italic = parseMaybeAbsentBool( true );
851 aText->SetItalicFlag( italic );
852 break;
853
854 case T_color:
855 color.r = parseInt( "red" ) / 255.0;
856 color.g = parseInt( "green" ) / 255.0;
857 color.b = parseInt( "blue" ) / 255.0;
858 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
859 aText->SetTextColor( color );
860 NeedRIGHT();
861 break;
862
863 case T_line_spacing:
864 aText->SetLineSpacing( parseDouble( "line spacing" ) );
865 NeedRIGHT();
866 break;
867
868 default:
869 Expecting( "face, size, thickness, line_spacing, bold, or italic" );
870 }
871 }
872
873 break;
874
875 case T_justify:
876 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
877 {
878 switch( token )
879 {
880 case T_left: aText->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT ); break;
881 case T_right: aText->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT ); break;
882 case T_top: aText->SetVertJustify( GR_TEXT_V_ALIGN_TOP ); break;
883 case T_bottom: aText->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM ); break;
884 // Do not set mirror property for schematic text elements
885 case T_mirror: break;
886 default: Expecting( "left, right, top, bottom, or mirror" );
887 }
888 }
889
890 break;
891
892 case T_href:
893 {
894 NeedSYMBOL();
895 wxString hyperlink = FromUTF8();
896
897 if( !EDA_TEXT::ValidateHyperlink( hyperlink ) )
898 {
899 THROW_PARSE_ERROR( wxString::Format( _( "Invalid hyperlink url '%s'" ), hyperlink ),
900 CurSource(), CurLine(), CurLineNumber(), CurOffset() );
901 }
902 else
903 {
904 aText->SetHyperlink( hyperlink );
905 }
906
907 NeedRIGHT();
908 }
909 break;
910
911 case T_hide:
912 {
913 bool hide = parseMaybeAbsentBool( true );
914 aText->SetVisible( !hide );
915 break;
916 }
917
918 default:
919 Expecting( "font, justify, hide or href" );
920 }
921 }
922}
923
924
925void SCH_IO_KICAD_SEXPR_PARSER::parseHeader( TSCHEMATIC_T::T aHeaderType, int aFileVersion )
926{
927 if( CurTok() != aHeaderType )
928 {
929 THROW_PARSE_ERROR( wxString::Format( _( "Cannot parse '%s' as a header." ),
930 GetTokenString( CurTok() ) ),
931 CurSource(), CurLine(), CurLineNumber(), CurOffset() );
932 }
933
934 NeedLEFT();
935
936 T tok = NextTok();
937
938 if( tok == T_version )
939 {
940 m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
941 NeedRIGHT();
942 }
943 else
944 {
945 m_requiredVersion = aFileVersion;
946 }
947}
948
949
950void SCH_IO_KICAD_SEXPR_PARSER::parseBodyStyles( std::unique_ptr<LIB_SYMBOL>& aSymbol )
951{
952 wxCHECK_RET( CurTok() == T_body_styles,
953 "Cannot parse " + GetTokenString( CurTok() ) + " as a body_styles token." );
954
955 std::vector<wxString> names;
956
957 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
958 {
959 if( token == T_demorgan )
960 {
961 aSymbol->SetHasDeMorganBodyStyles( true );
962 continue;
963 }
964 else if( !IsSymbol( token ) )
965 {
966 THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
967 CurOffset() );
968 }
969
970 names.push_back( FromUTF8() );
971 }
972
973 if( !names.empty() )
974 aSymbol->SetBodyStyleNames( names );
975}
976
977
978void SCH_IO_KICAD_SEXPR_PARSER::parsePinNames( std::unique_ptr<LIB_SYMBOL>& aSymbol )
979{
980 wxCHECK_RET( CurTok() == T_pin_names,
981 "Cannot parse " + GetTokenString( CurTok() ) + " as a pin_name token." );
982
990
991 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
992 {
993 // Pre-20241004 format - bare 'hide' keyword
994 if( token == T_hide )
995 {
996 aSymbol->SetShowPinNames( false );
997 continue;
998 }
999
1000 if( token != T_LEFT )
1001 {
1002 Expecting( T_LEFT );
1003 }
1004
1005 token = NextTok();
1006
1007 switch( token )
1008 {
1009 case T_offset:
1010 aSymbol->SetPinNameOffset( parseInternalUnits( "pin name offset" ) );
1011 NeedRIGHT();
1012 break;
1013
1014 case T_hide:
1015 aSymbol->SetShowPinNames( !parseBool() );
1016 NeedRIGHT();
1017 break;
1018
1019 default:
1020 Expecting( "offset or hide" );
1021 }
1022 }
1023}
1024
1025
1026void SCH_IO_KICAD_SEXPR_PARSER::parsePinNumbers( std::unique_ptr<LIB_SYMBOL>& aSymbol )
1027{
1028 wxCHECK_RET( CurTok() == T_pin_numbers,
1029 "Cannot parse " + GetTokenString( CurTok() ) + " as a pin_number token." );
1030
1037
1038 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
1039 {
1040 // Pre-20241004 format - bare 'hide' keyword
1041 if( token == T_hide )
1042 {
1043 aSymbol->SetShowPinNumbers( false );
1044 continue;
1045 }
1046
1047 if( token != T_LEFT )
1048 {
1049 Expecting( T_LEFT );
1050 }
1051
1052 token = NextTok();
1053
1054 switch( token )
1055 {
1056 case T_hide:
1057 aSymbol->SetShowPinNumbers( !parseBool() );
1058 NeedRIGHT();
1059 break;
1060
1061 default:
1062 Expecting( "hide" );
1063 break;
1064 }
1065 }
1066}
1067
1068
1069SCH_FIELD* SCH_IO_KICAD_SEXPR_PARSER::parseProperty( std::unique_ptr<LIB_SYMBOL>& aSymbol )
1070{
1071 wxCHECK_MSG( CurTok() == T_property, nullptr,
1072 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a property." ) );
1073 wxCHECK( aSymbol, nullptr );
1074
1075 FIELD_T fieldId = FIELD_T::USER;
1076 wxString name;
1077 wxString value;
1078 bool isPrivate = false;
1079 bool isVisible = true;
1080
1081 T token = NextTok();
1082
1083 if( token == T_private )
1084 {
1085 isPrivate = true;
1086 token = NextTok();
1087 }
1088
1089 if( !IsSymbol( token ) )
1090 {
1091 THROW_PARSE_ERROR( _( "Invalid property name" ), CurSource(), CurLine(), CurLineNumber(),
1092 CurOffset() );
1093 }
1094
1095 name = FromUTF8();
1096
1097 if( name.IsEmpty() )
1098 {
1099 THROW_PARSE_ERROR( _( "Empty property name" ), CurSource(), CurLine(), CurLineNumber(),
1100 CurOffset() );
1101 }
1102
1103 // Correctly set the ID based on canonical (untranslated) field name
1104 for( FIELD_T id : MANDATORY_FIELDS )
1105 {
1106 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
1107 {
1108 fieldId = id;
1109 break;
1110 }
1111 }
1112
1113 auto field = std::make_unique<SCH_FIELD>( aSymbol.get(), fieldId, name );
1114 field->SetPrivate( isPrivate );
1115 field->SetVisible( isVisible );
1116
1117 token = NextTok();
1118
1119 if( !IsSymbol( token ) )
1120 {
1121 THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
1122 CurOffset() );
1123 }
1124
1125 // Empty property values are valid.
1126
1127 if( m_requiredVersion < 20250318 && CurStr() == "~" )
1128 value = wxEmptyString;
1129 else
1130 value = FromUTF8();
1131
1132 field->SetText( value );
1133
1134 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1135 {
1136 if( token != T_LEFT )
1137 Expecting( T_LEFT );
1138
1139 token = NextTok();
1140
1141 switch( token )
1142 {
1143 case T_id: // legacy token; ignore
1144 parseInt( "field ID" );
1145 NeedRIGHT();
1146 break;
1147
1148 case T_at:
1149 field->SetPosition( parseXY( true ) );
1150 field->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ) );
1151 NeedRIGHT();
1152 break;
1153
1154 case T_hide:
1155 field->SetVisible( !parseBool() );
1156 NeedRIGHT();
1157 break;
1158
1159 case T_effects:
1160 parseEDA_TEXT( static_cast<EDA_TEXT*>( field.get() ),
1161 field->GetId() == FIELD_T::VALUE );
1162 break;
1163
1164 case T_show_name:
1165 {
1166 bool show = parseMaybeAbsentBool( true );
1167 field->SetNameShown( show );
1168 break;
1169 }
1170
1171 case T_do_not_autoplace:
1172 {
1173 bool doNotAutoplace = parseMaybeAbsentBool( true );
1174 field->SetCanAutoplace( !doNotAutoplace );
1175 break;
1176 }
1177
1178 default:
1179 Expecting( "id, at, hide, show_name, do_not_autoplace, or effects" );
1180 }
1181 }
1182
1183 SCH_FIELD* existingField;
1184
1185 if( field->IsMandatory() )
1186 {
1187 existingField = aSymbol->GetField( field->GetId() );
1188
1189 *existingField = *field;
1190 return existingField;
1191 }
1192 else if( name == "ki_keywords" )
1193 {
1194 // Not a SCH_FIELD object yet.
1195 aSymbol->SetKeyWords( value );
1196 return nullptr;
1197 }
1198 // In v7 and earlier the description field didn't exist and was a key/value
1199 else if( name == "ki_description" )
1200 {
1201 aSymbol->SetDescription( value );
1202 return nullptr;
1203 }
1204 else if( name == "ki_fp_filters" )
1205 {
1206 // Not a SCH_FIELD object yet.
1207 wxArrayString filters;
1208 wxStringTokenizer tokenizer( value, " \t\r\n", wxTOKEN_STRTOK );
1209
1210 while( tokenizer.HasMoreTokens() )
1211 {
1212 wxString curr_token = UnescapeString( tokenizer.GetNextToken() );
1213 filters.Add( curr_token );
1214 }
1215
1216 aSymbol->SetFPFilters( filters );
1217 return nullptr;
1218 }
1219 else if( name == "ki_locked" )
1220 {
1221 // This is a temporary SCH_FIELD object until interchangeable units are determined on
1222 // the fly.
1223 aSymbol->LockUnits( true );
1224 return nullptr;
1225 }
1226 else
1227 {
1228 // At this point, a user field is read.
1229 existingField = aSymbol->GetField( field->GetCanonicalName() );
1230
1231#if 1 // Enable it to modify the name of the field to add if already existing
1232 // Disable it to skip the field having the same name as previous field
1233 if( existingField )
1234 {
1235 // We cannot handle 2 fields with the same name, so because the field name
1236 // is already in use, try to build a new name (oldname_x)
1237 wxString base_name = field->GetCanonicalName();
1238
1239 // Arbitrary limit 10 attempts to find a new name
1240 for( int ii = 1; ii < 10 && existingField; ii++ )
1241 {
1242 wxString newname = base_name;
1243 newname << '_' << ii;
1244
1245 existingField = aSymbol->GetField( newname );
1246
1247 if( !existingField ) // the modified name is not found, use it
1248 field->SetName( newname );
1249 }
1250 }
1251#endif
1252 if( !existingField )
1253 {
1254 aSymbol->AddDrawItem( field.get(), false );
1255 return field.release();
1256 }
1257 else
1258 {
1259 // We cannot handle 2 fields with the same name, so skip this one
1260 return nullptr;
1261 }
1262 }
1263}
1264
1265
1267{
1268 wxCHECK_MSG( CurTok() == T_arc, nullptr,
1269 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc." ) );
1270
1271 T token;
1272 VECTOR2I startPoint( 1, 0 ); // Initialize to a non-degenerate arc just for safety
1273 VECTOR2I midPoint( 1, 1 );
1274 VECTOR2I endPoint( 0, 1 );
1275 bool hasMidPoint = false;
1277 FILL_PARAMS fill;
1278
1279 // Parameters for legacy format
1280 VECTOR2I center( 0, 0 );
1281 EDA_ANGLE startAngle = ANGLE_0;
1282 EDA_ANGLE endAngle = ANGLE_90;
1283 bool hasAngles = false;
1284
1285 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC, LAYER_DEVICE );
1286
1287 arc->SetUnit( m_unit );
1288 arc->SetBodyStyle( m_bodyStyle );
1289
1290 token = NextTok();
1291
1292 if( token == T_private )
1293 {
1294 arc->SetPrivate( true );
1295 token = NextTok();
1296 }
1297
1298 for( ; token != T_RIGHT; token = NextTok() )
1299 {
1300 if( token != T_LEFT )
1301 Expecting( T_LEFT );
1302
1303 token = NextTok();
1304
1305 switch( token )
1306 {
1307 case T_start:
1308 startPoint = parseXY( true );
1309 NeedRIGHT();
1310 break;
1311
1312 case T_mid:
1313 midPoint = parseXY( true );
1314 NeedRIGHT();
1315 hasMidPoint = true;
1316 break;
1317
1318 case T_end:
1319 endPoint = parseXY( true );
1320 NeedRIGHT();
1321 break;
1322
1323 case T_radius:
1324 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1325 {
1326 if( token != T_LEFT )
1327 Expecting( T_LEFT );
1328
1329 token = NextTok();
1330
1331 switch( token )
1332 {
1333 case T_at:
1334 center = parseXY( true );
1335 NeedRIGHT();
1336 break;
1337
1338 case T_length:
1339 parseInternalUnits( "radius length" );
1340 NeedRIGHT();
1341 break;
1342
1343 case T_angles:
1344 {
1345 startAngle = EDA_ANGLE( parseDouble( "start radius angle" ), DEGREES_T );
1346 endAngle = EDA_ANGLE( parseDouble( "end radius angle" ), DEGREES_T );
1347 startAngle.Normalize();
1348 endAngle.Normalize();
1349 NeedRIGHT();
1350 hasAngles = true;
1351 break;
1352 }
1353
1354 default:
1355 Expecting( "at, length, or angles" );
1356 }
1357 }
1358
1359 break;
1360
1361 case T_stroke:
1362 parseStroke( stroke );
1363 arc->SetStroke( stroke );
1364 break;
1365
1366 case T_fill:
1367 parseFill( fill );
1368 arc->SetFillMode( fill.m_FillType );
1369 arc->SetFillColor( fill.m_Color );
1370 break;
1371
1372 default:
1373 Expecting( "start, mid, end, radius, stroke, or fill" );
1374 }
1375 }
1376
1377 if( hasMidPoint )
1378 {
1379 arc->SetArcGeometry( startPoint, midPoint, endPoint );
1380
1381 if( m_requiredVersion <= 20230121 ) // Versions before 7.0
1382 {
1383 // Should be not required. Unfortunately it is needed because some bugs created
1384 // incorrect data after conversion of old libraries to the new arc format using
1385 // startPoint, midPoint, endPoint
1386 // Until now, in Eeschema the arc angle should be <= 180 deg.
1387 // If > 180 (bug...) we need to swap arc ends.
1388 // However arc angle == 180 deg can also create issues in some cases (plotters, hittest)
1389 // so also avoid arc == 180 deg
1390 EDA_ANGLE arc_start, arc_end, arc_angle;
1391 arc->CalcArcAngles( arc_start, arc_end );
1392 arc_angle = arc_end - arc_start;
1393
1394 if( arc_angle > ANGLE_180 )
1395 {
1396 // Change arc to its complement (360deg - arc_angle)
1397 arc->SetStart( endPoint );
1398 arc->SetEnd( startPoint );
1399 VECTOR2I new_center =
1400 CalcArcCenter( arc->GetStart(), arc->GetEnd(), ANGLE_360 - arc_angle );
1401 arc->SetCenter( new_center );
1402 }
1403 else if( arc_angle == ANGLE_180 )
1404 {
1405 VECTOR2I new_center = CalcArcCenter( arc->GetStart(), arc->GetEnd(),
1406 EDA_ANGLE( 179.5, DEGREES_T ) );
1407 arc->SetCenter( new_center );
1408 }
1409 }
1410 }
1411 else if( hasAngles )
1412 {
1413 arc->SetCenter( center );
1414 /*
1415 * Older versions stored start-end with an implied winding, but the winding was different
1416 * between LibEdit and PCBNew. Since we now use a common class (EDA_SHAPE) for both we
1417 * need to flip one of them. LibEdit drew the short straw.
1418 */
1419 arc->SetStart( endPoint );
1420 arc->SetEnd( startPoint );
1421
1422 // Like previously, 180 degrees arcs that create issues are just modified
1423 // to be < 180 degrees to do not break some other functions ( Draw, Plot, HitTest)
1424 EDA_ANGLE arc_start, arc_end, arc_angle;
1425 arc->CalcArcAngles( arc_start, arc_end );
1426 arc_angle = arc_end - arc_start;
1427
1428 // The arc angle should be <= 180 deg in old libraries.
1429 // If > 180 we need to swap arc ends (the first choice was not good)
1430 if( arc_angle > ANGLE_180 )
1431 {
1432 arc->SetStart( startPoint );
1433 arc->SetEnd( endPoint );
1434 }
1435 else if( arc_angle == ANGLE_180 )
1436 {
1437 // Disabled (since the Y axis was reversed in library editor
1438 // and arcs having a 180 deg arc do not create issues
1439 // However keep it for info, just in case
1440 #if 0
1441 arc->SetStart( startPoint ); // not working with Y axis reversed
1442 arc->SetEnd( endPoint ); // not working with Y axis reversed
1443 // Useless now arcs >= 180 deg are well handled
1444 VECTOR2I new_center = CalcArcCenter( arc->GetStart(), arc->GetEnd(),
1445 EDA_ANGLE( 179.5, DEGREES_T ) );
1446 arc->SetCenter( new_center );
1447 #endif
1448 }
1449 }
1450 else
1451 {
1452 wxFAIL_MSG( "Setting arc without either midpoint or angles not implemented." );
1453 }
1454
1455 return arc.release();
1456}
1457
1458
1460{
1461 wxCHECK_MSG( CurTok() == T_bezier, nullptr,
1462 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
1463
1464 T token;
1466 FILL_PARAMS fill;
1467
1468 std::unique_ptr<SCH_SHAPE> bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER, LAYER_DEVICE );
1469
1470 bezier->SetUnit( m_unit );
1471 bezier->SetBodyStyle( m_bodyStyle );
1472
1473 token = NextTok();
1474
1475 if( token == T_private )
1476 {
1477 bezier->SetPrivate( true );
1478 token = NextTok();
1479 }
1480
1481 for( ; token != T_RIGHT; token = NextTok() )
1482 {
1483 if( token != T_LEFT )
1484 Expecting( T_LEFT );
1485
1486 token = NextTok();
1487
1488 switch( token )
1489 {
1490 case T_pts:
1491 {
1492 int ii = 0;
1493
1494 for( token = NextTok(); token != T_RIGHT; token = NextTok(), ++ii )
1495 {
1496 if( token != T_LEFT )
1497 Expecting( T_LEFT );
1498
1499 token = NextTok();
1500
1501 if( token != T_xy )
1502 Expecting( "xy" );
1503
1504 switch( ii )
1505 {
1506 case 0: bezier->SetStart( parseXY( true ) ); break;
1507 case 1: bezier->SetBezierC1( parseXY( true ) ); break;
1508 case 2: bezier->SetBezierC2( parseXY( true ) ); break;
1509 case 3: bezier->SetEnd( parseXY( true ) ); break;
1510 default: Unexpected( "control point" ); break;
1511 }
1512
1513 NeedRIGHT();
1514 }
1515 }
1516 break;
1517
1518 case T_stroke:
1519 parseStroke( stroke );
1520 bezier->SetStroke( stroke );
1521 break;
1522
1523 case T_fill:
1524 parseFill( fill );
1525 bezier->SetFillMode( fill.m_FillType );
1526 bezier->SetFillColor( fill.m_Color );
1527 break;
1528
1529 default:
1530 Expecting( "pts, stroke, or fill" );
1531 }
1532 }
1533
1534 bezier->RebuildBezierToSegmentsPointsList( m_maxError );
1535
1536 return bezier.release();
1537}
1538
1539
1541{
1542 wxCHECK_MSG( CurTok() == T_circle, nullptr,
1543 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle." ) );
1544
1545 T token;
1546 VECTOR2I center( 0, 0 );
1547 int radius = 1; // defaulting to 0 could result in troublesome math....
1549 FILL_PARAMS fill;
1550
1551 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE, LAYER_DEVICE );
1552
1553 circle->SetUnit( m_unit );
1554 circle->SetBodyStyle( m_bodyStyle );
1555
1556 token = NextTok();
1557
1558 if( token == T_private )
1559 {
1560 circle->SetPrivate( true );
1561 token = NextTok();
1562 }
1563
1564 for( ; token != T_RIGHT; token = NextTok() )
1565 {
1566 if( token != T_LEFT )
1567 Expecting( T_LEFT );
1568
1569 token = NextTok();
1570
1571 switch( token )
1572 {
1573 case T_center:
1574 center = parseXY( true );
1575 NeedRIGHT();
1576 break;
1577
1578 case T_radius:
1579 radius = parseInternalUnits( "radius length" );
1580 NeedRIGHT();
1581 break;
1582
1583 case T_stroke:
1584 parseStroke( stroke );
1585 circle->SetStroke( stroke );
1586 break;
1587
1588 case T_fill:
1589 parseFill( fill );
1590 circle->SetFillMode( fill.m_FillType );
1591 circle->SetFillColor( fill.m_Color );
1592 break;
1593
1594 default:
1595 Expecting( "center, radius, stroke, or fill" );
1596 }
1597 }
1598
1599 circle->SetCenter( center );
1600 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
1601
1602 return circle.release();
1603}
1604
1605
1607{
1608 wxCHECK_MSG( CurTok() == T_ellipse, nullptr,
1609 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an ellipse." ) );
1610
1611 auto ellipse = std::make_unique<SCH_SHAPE>( SHAPE_T::ELLIPSE, LAYER_DEVICE );
1612 ellipse->SetUnit( m_unit );
1613 ellipse->SetBodyStyle( m_bodyStyle );
1614
1615 T token = NextTok();
1616
1617 if( token == T_private )
1618 {
1619 ellipse->SetPrivate( true );
1620 token = NextTok();
1621 }
1622
1623 parseEllipseBody( ellipse.get(), /*isArc*/ false, /*isSchematic*/ false, token );
1624 return ellipse.release();
1625}
1626
1627
1629{
1630 wxCHECK_MSG( CurTok() == T_ellipse_arc, nullptr,
1631 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an elliptical arc." ) );
1632
1633 auto arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ELLIPSE_ARC, LAYER_DEVICE );
1634 arc->SetUnit( m_unit );
1635 arc->SetBodyStyle( m_bodyStyle );
1636
1637 T token = NextTok();
1638
1639 if( token == T_private )
1640 {
1641 arc->SetPrivate( true );
1642 token = NextTok();
1643 }
1644
1645 parseEllipseBody( arc.get(), /*isArc*/ true, /*isSchematic*/ false, token );
1646 return arc.release();
1647}
1648
1649
1651{
1652 auto parseType =
1653 [&]( T token ) -> ELECTRICAL_PINTYPE
1654 {
1655 switch( token )
1656 {
1657 case T_input: return ELECTRICAL_PINTYPE::PT_INPUT;
1658 case T_output: return ELECTRICAL_PINTYPE::PT_OUTPUT;
1659 case T_bidirectional: return ELECTRICAL_PINTYPE::PT_BIDI;
1660 case T_tri_state: return ELECTRICAL_PINTYPE::PT_TRISTATE;
1661 case T_passive: return ELECTRICAL_PINTYPE::PT_PASSIVE;
1662 case T_unspecified: return ELECTRICAL_PINTYPE::PT_UNSPECIFIED;
1663 case T_power_in: return ELECTRICAL_PINTYPE::PT_POWER_IN;
1664 case T_power_out: return ELECTRICAL_PINTYPE::PT_POWER_OUT;
1665 case T_open_collector: return ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR;
1666 case T_open_emitter: return ELECTRICAL_PINTYPE::PT_OPENEMITTER;
1667 case T_unconnected:
1668 case T_no_connect: return ELECTRICAL_PINTYPE::PT_NC;
1669 case T_free: return ELECTRICAL_PINTYPE::PT_NIC;
1670
1671 default:
1672 Expecting( "input, output, bidirectional, tri_state, passive, unspecified, "
1673 "power_in, power_out, open_collector, open_emitter, free or "
1674 "no_connect" );
1676 }
1677 };
1678
1679 auto parseShape =
1680 [&]( T token ) -> GRAPHIC_PINSHAPE
1681 {
1682 switch( token )
1683 {
1684 case T_line: return GRAPHIC_PINSHAPE::LINE;
1685 case T_inverted: return GRAPHIC_PINSHAPE::INVERTED;
1686 case T_clock: return GRAPHIC_PINSHAPE::CLOCK;
1687 case T_inverted_clock: return GRAPHIC_PINSHAPE::INVERTED_CLOCK;
1688 case T_input_low: return GRAPHIC_PINSHAPE::INPUT_LOW;
1689 case T_clock_low: return GRAPHIC_PINSHAPE::CLOCK_LOW;
1690 case T_output_low: return GRAPHIC_PINSHAPE::OUTPUT_LOW;
1691 case T_edge_clock_high: return GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK;
1692 case T_non_logic: return GRAPHIC_PINSHAPE::NONLOGIC;
1693
1694 default:
1695 Expecting( "line, inverted, clock, inverted_clock, input_low, clock_low, "
1696 "output_low, edge_clock_high, non_logic" );
1698 }
1699 };
1700
1701 wxCHECK_MSG( CurTok() == T_pin, nullptr,
1702 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a pin token." ) );
1703
1704 T token;
1705 std::unique_ptr<SCH_PIN> pin = std::make_unique<SCH_PIN>( nullptr );
1706
1707 pin->SetUnit( m_unit );
1708 pin->SetBodyStyle( m_bodyStyle );
1709
1710 // Pin electrical type.
1711 token = NextTok();
1712 pin->SetType( parseType( token ) );
1713
1714 // Pin shape.
1715 token = NextTok();
1716 pin->SetShape( parseShape( token ) );
1717
1718 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1719 {
1720 // Pre-2024104 format (bare 'hide' keyword)
1721 if( token == T_hide )
1722 {
1723 pin->SetVisible( false );
1724 continue;
1725 }
1726
1727 if( token != T_LEFT )
1728 Expecting( T_LEFT );
1729
1730 token = NextTok();
1731
1732 switch( token )
1733 {
1734 case T_at:
1735 pin->SetPosition( parseXY( true ) );
1736
1737 switch( parseInt( "pin orientation" ) )
1738 {
1739 case 0: pin->SetOrientation( PIN_ORIENTATION::PIN_RIGHT ); break;
1740 case 90: pin->SetOrientation( PIN_ORIENTATION::PIN_UP ); break;
1741 case 180: pin->SetOrientation( PIN_ORIENTATION::PIN_LEFT ); break;
1742 case 270: pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN ); break;
1743 default: Expecting( "0, 90, 180, or 270" );
1744 }
1745
1746 NeedRIGHT();
1747 break;
1748
1749 case T_length:
1750 pin->SetLength( parseInternalUnits( "pin length" ) );
1751 NeedRIGHT();
1752 break;
1753
1754 case T_hide:
1755 pin->SetVisible( !parseBool() );
1756 NeedRIGHT();
1757 break;
1758
1759 case T_name:
1760 token = NextTok();
1761
1762 if( !IsSymbol( token ) )
1763 {
1764 THROW_PARSE_ERROR( _( "Invalid pin name" ), CurSource(), CurLine(), CurLineNumber(),
1765 CurOffset() );
1766 }
1767
1768 if( m_requiredVersion < 20250318 && CurStr() == "~" )
1769 pin->SetName( wxEmptyString );
1770 else if( m_requiredVersion < 20210606 )
1771 pin->SetName( ConvertToNewOverbarNotation( FromUTF8() ) );
1772 else
1773 pin->SetName( FromUTF8() );
1774
1775 token = NextTok();
1776
1777 if( token != T_RIGHT )
1778 {
1779 token = NextTok();
1780
1781 if( token == T_effects )
1782 {
1783 // The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
1784 // so duplicate parsing is not required.
1786
1787 parseEDA_TEXT( &text, true, false );
1788 pin->SetNameTextSize( text.GetTextHeight() );
1789 NeedRIGHT();
1790 }
1791 else
1792 {
1793 Expecting( "effects" );
1794 }
1795 }
1796
1797 break;
1798
1799 case T_number:
1800 token = NextTok();
1801
1802 if( !IsSymbol( token ) )
1803 {
1804 THROW_PARSE_ERROR( _( "Invalid pin number" ), CurSource(), CurLine(),
1805 CurLineNumber(), CurOffset() );
1806 }
1807
1808 if( m_requiredVersion < 20250318 && CurStr() == "~" )
1809 pin->SetNumber( wxEmptyString );
1810 else if( m_requiredVersion < 20210606 )
1811 pin->SetNumber( ConvertToNewOverbarNotation( FromUTF8() ) );
1812 else
1813 pin->SetNumber( FromUTF8() );
1814
1815 token = NextTok();
1816
1817 if( token != T_RIGHT )
1818 {
1819 token = NextTok();
1820
1821 if( token == T_effects )
1822 {
1823 // The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
1824 // so duplicate parsing is not required.
1826
1827 parseEDA_TEXT( &text, false, false );
1828 pin->SetNumberTextSize( text.GetTextHeight() );
1829 NeedRIGHT();
1830 }
1831 else
1832 {
1833 Expecting( "effects" );
1834 }
1835 }
1836
1837 break;
1838
1839 case T_alternate:
1840 {
1841 SCH_PIN::ALT alt;
1842
1843 token = NextTok();
1844
1845 if( !IsSymbol( token ) )
1846 {
1847 THROW_PARSE_ERROR( _( "Invalid alternate pin name" ), CurSource(), CurLine(),
1848 CurLineNumber(), CurOffset() );
1849 }
1850
1851 alt.m_Name = FromUTF8();
1852
1853 token = NextTok();
1854 alt.m_Type = parseType( token );
1855
1856 token = NextTok();
1857 alt.m_Shape = parseShape( token );
1858
1859 pin->GetAlternates()[ alt.m_Name ] = alt;
1860
1861 NeedRIGHT();
1862 break;
1863 }
1864
1865 default:
1866 Expecting( "at, name, number, hide, length, or alternate" );
1867 }
1868 }
1869
1870 return pin.release();
1871}
1872
1873
1875{
1876 wxCHECK_MSG( CurTok() == T_polyline, nullptr,
1877 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a poly." ) );
1878
1879 T token;
1881 FILL_PARAMS fill;
1882 std::unique_ptr<SCH_SHAPE> poly = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_DEVICE );
1883
1884 poly->SetUnit( m_unit );
1885 poly->SetBodyStyle( m_bodyStyle );
1886
1887 token = NextTok();
1888
1889 if( token == T_private )
1890 {
1891 poly->SetPrivate( true );
1892 token = NextTok();
1893 }
1894
1895 for( ; token != T_RIGHT; token = NextTok() )
1896 {
1897 if( token != T_LEFT )
1898 Expecting( T_LEFT );
1899
1900 token = NextTok();
1901
1902 switch( token )
1903 {
1904 case T_pts:
1905 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1906 {
1907 if( token != T_LEFT )
1908 Expecting( T_LEFT );
1909
1910 token = NextTok();
1911
1912 if( token != T_xy )
1913 Expecting( "xy" );
1914
1915 poly->AddPoint( parseXY( true ) );
1916
1917 NeedRIGHT();
1918 }
1919
1920 break;
1921
1922 case T_stroke:
1923 parseStroke( stroke );
1924 poly->SetStroke( stroke );
1925 break;
1926
1927 case T_fill:
1928 parseFill( fill );
1929 poly->SetFillMode( fill.m_FillType );
1930 poly->SetFillColor( fill.m_Color );
1931 break;
1932
1933 default:
1934 Expecting( "pts, stroke, or fill" );
1935 }
1936 }
1937
1938 return poly.release();
1939}
1940
1941
1943{
1944 wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
1945 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle." ) );
1946
1947 T token;
1949 FILL_PARAMS fill;
1950 auto rectangle = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE, LAYER_DEVICE );
1951
1952 rectangle->SetUnit( m_unit );
1953 rectangle->SetBodyStyle( m_bodyStyle );
1954
1955 token = NextTok();
1956
1957 if( token == T_private )
1958 {
1959 rectangle->SetPrivate( true );
1960 token = NextTok();
1961 }
1962
1963 for( ; token != T_RIGHT; token = NextTok() )
1964 {
1965 if( token != T_LEFT )
1966 Expecting( T_LEFT );
1967
1968 token = NextTok();
1969
1970 switch( token )
1971 {
1972 case T_start:
1973 rectangle->SetPosition( parseXY( true ) );
1974 NeedRIGHT();
1975 break;
1976
1977 case T_end:
1978 rectangle->SetEnd( parseXY( true ) );
1979 NeedRIGHT();
1980 break;
1981
1982 case T_radius:
1983 rectangle->SetCornerRadius( parseDouble( "corner radius" ) * schIUScale.IU_PER_MM );
1984 NeedRIGHT();
1985 break;
1986
1987 case T_stroke:
1988 parseStroke( stroke );
1989 rectangle->SetStroke( stroke );
1990 break;
1991
1992 case T_fill:
1993 parseFill( fill );
1994 rectangle->SetFillMode( fill.m_FillType );
1995 rectangle->SetFillColor( fill.m_Color );
1996 break;
1997
1998 default:
1999 Expecting( "start, end, stroke, or fill" );
2000 }
2001 }
2002
2003 return rectangle.release();
2004}
2005
2006
2008{
2009 wxCHECK_MSG( CurTok() == T_text, nullptr,
2010 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text token." ) );
2011
2012 T token;
2013 std::unique_ptr<SCH_TEXT> text = std::make_unique<SCH_TEXT>();
2014
2015 text->SetLayer( LAYER_DEVICE );
2016 text->SetUnit( m_unit );
2017 text->SetBodyStyle( m_bodyStyle );
2018 token = NextTok();
2019
2020 if( token == T_private )
2021 {
2022 text->SetPrivate( true );
2023 token = NextTok();
2024 }
2025
2026 if( !IsSymbol( token ) )
2027 {
2028 THROW_PARSE_ERROR( _( "Invalid text string" ), CurSource(), CurLine(), CurLineNumber(),
2029 CurOffset() );
2030 }
2031
2032 text->SetText( FromUTF8() );
2033
2034 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2035 {
2036 if( token != T_LEFT )
2037 Expecting( T_LEFT );
2038
2039 token = NextTok();
2040
2041 switch( token )
2042 {
2043 case T_at:
2044 text->SetPosition( parseXY( true ) );
2045 // Yes, LIB_TEXT is really decidegrees even though all the others are degrees. :(
2046 text->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), TENTHS_OF_A_DEGREE_T ) );
2047 NeedRIGHT();
2048 break;
2049
2050 case T_effects:
2051 parseEDA_TEXT( text.get(), true );
2052 break;
2053
2054 default:
2055 Expecting( "at or effects" );
2056 }
2057 }
2058
2059 // Convert hidden symbol text (which is no longer supported) into a hidden field
2060 if( !text->IsVisible() )
2061 return new SCH_FIELD( text.get(), FIELD_T::USER );
2062
2063 return text.release();
2064}
2065
2066
2068{
2069 wxCHECK_MSG( CurTok() == T_text_box, nullptr,
2070 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text box." ) );
2071
2072 T token;
2073 VECTOR2I pos;
2074 VECTOR2I end;
2075 VECTOR2I size;
2076 int left;
2077 int top;
2078 int right;
2079 int bottom;
2081 FILL_PARAMS fill;
2082 bool foundEnd = false;
2083 bool foundSize = false;
2084 bool foundMargins = false;
2085
2086 std::unique_ptr<SCH_TEXTBOX> textBox = std::make_unique<SCH_TEXTBOX>( LAYER_DEVICE );
2087
2088 textBox->SetUnit( m_unit );
2089 textBox->SetBodyStyle( m_bodyStyle );
2090 token = NextTok();
2091
2092 if( token == T_private )
2093 {
2094 textBox->SetPrivate( true );
2095 token = NextTok();
2096 }
2097
2098 if( !IsSymbol( token ) )
2099 {
2100 THROW_PARSE_ERROR( _( "Invalid text string" ), CurSource(), CurLine(), CurLineNumber(),
2101 CurOffset() );
2102 }
2103
2104 textBox->SetText( FromUTF8() );
2105
2106 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2107 {
2108 if( token != T_LEFT )
2109 Expecting( T_LEFT );
2110
2111 token = NextTok();
2112
2113 switch( token )
2114 {
2115 case T_start: // Legacy token during 6.99 development; fails to handle angle
2116 pos = parseXY( true );
2117 NeedRIGHT();
2118 break;
2119
2120 case T_end: // Legacy token during 6.99 development; fails to handle angle
2121 end = parseXY( true );
2122 foundEnd = true;
2123 NeedRIGHT();
2124 break;
2125
2126 case T_at:
2127 pos = parseXY( true );
2128 textBox->SetTextAngle( EDA_ANGLE( parseDouble( "textbox angle" ), DEGREES_T ) );
2129 NeedRIGHT();
2130 break;
2131
2132 case T_size:
2133 size = parseXY( true );
2134 foundSize = true;
2135 NeedRIGHT();
2136 break;
2137
2138 case T_stroke:
2139 parseStroke( stroke );
2140 textBox->SetStroke( stroke );
2141 break;
2142
2143 case T_fill:
2144 parseFill( fill );
2145 textBox->SetFillMode( fill.m_FillType );
2146 textBox->SetFillColor( fill.m_Color );
2147 break;
2148
2149 case T_margins:
2150 parseMargins( left, top, right, bottom );
2151 textBox->SetMarginLeft( left );
2152 textBox->SetMarginTop( top );
2153 textBox->SetMarginRight( right );
2154 textBox->SetMarginBottom( bottom );
2155 foundMargins = true;
2156 NeedRIGHT();
2157 break;
2158
2159 case T_effects:
2160 parseEDA_TEXT( static_cast<EDA_TEXT*>( textBox.get() ), false );
2161 break;
2162
2163 default:
2164 Expecting( "at, size, stroke, fill or effects" );
2165 }
2166 }
2167
2168 textBox->SetPosition( pos );
2169
2170 if( foundEnd )
2171 textBox->SetEnd( end );
2172 else if( foundSize )
2173 textBox->SetEnd( pos + size );
2174 else
2175 Expecting( "size" );
2176
2177 if( !foundMargins )
2178 {
2179 int margin = textBox->GetLegacyTextMargin();
2180 textBox->SetMarginLeft( margin );
2181 textBox->SetMarginTop( margin );
2182 textBox->SetMarginRight( margin );
2183 textBox->SetMarginBottom( margin );
2184 }
2185
2186 return textBox.release();
2187}
2188
2189
2191{
2192 wxCHECK_RET( ( CurTok() == T_page && m_requiredVersion <= 20200506 ) || CurTok() == T_paper,
2193 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a PAGE_INFO." ) );
2194
2195 T token;
2196
2197 NeedSYMBOL();
2198
2199 wxString pageType = FromUTF8();
2200
2201 if( !aPageInfo.SetType( pageType ) )
2202 {
2203 THROW_PARSE_ERROR( _( "Invalid page type" ), CurSource(), CurLine(), CurLineNumber(),
2204 CurOffset() );
2205 }
2206
2207 if( aPageInfo.GetType() == PAGE_SIZE_TYPE::User )
2208 {
2209 double width = parseDouble( "width" );
2210
2211 // Perform some controls to avoid crashes if the size is edited by hands
2212 if( width < MIN_PAGE_SIZE_MM )
2213 width = MIN_PAGE_SIZE_MM;
2214 else if( width > MAX_PAGE_SIZE_EESCHEMA_MM )
2216
2217 double height = parseDouble( "height" );
2218
2219 if( height < MIN_PAGE_SIZE_MM )
2220 height = MIN_PAGE_SIZE_MM;
2221 else if( height > MAX_PAGE_SIZE_EESCHEMA_MM )
2223
2224 aPageInfo.SetWidthMM( width );
2225 aPageInfo.SetHeightMM( height );
2226 }
2227
2228 token = NextTok();
2229
2230 if( token == T_portrait )
2231 {
2232 aPageInfo.SetPortrait( true );
2233 NeedRIGHT();
2234 }
2235 else if( token != T_RIGHT )
2236 {
2237 Expecting( "portrait" );
2238 }
2239}
2240
2241
2243{
2244 wxCHECK_RET( CurTok() == T_title_block,
2245 "Cannot parse " + GetTokenString( CurTok() ) + " as a TITLE_BLOCK." );
2246
2247 T token;
2248
2249 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2250 {
2251 if( token != T_LEFT )
2252 Expecting( T_LEFT );
2253
2254 token = NextTok();
2255
2256 switch( token )
2257 {
2258 case T_title:
2259 NextTok();
2260 aTitleBlock.SetTitle( FromUTF8() );
2261 break;
2262
2263 case T_date:
2264 NextTok();
2265 aTitleBlock.SetDate( FromUTF8() );
2266 break;
2267
2268 case T_rev:
2269 NextTok();
2270 aTitleBlock.SetRevision( FromUTF8() );
2271 break;
2272
2273 case T_company:
2274 NextTok();
2275 aTitleBlock.SetCompany( FromUTF8() );
2276 break;
2277
2278 case T_comment:
2279 {
2280 int commentNumber = parseInt( "comment" );
2281
2282 switch( commentNumber )
2283 {
2284 case 1:
2285 NextTok();
2286 aTitleBlock.SetComment( 0, FromUTF8() );
2287 break;
2288
2289 case 2:
2290 NextTok();
2291 aTitleBlock.SetComment( 1, FromUTF8() );
2292 break;
2293
2294 case 3:
2295 NextTok();
2296 aTitleBlock.SetComment( 2, FromUTF8() );
2297 break;
2298
2299 case 4:
2300 NextTok();
2301 aTitleBlock.SetComment( 3, FromUTF8() );
2302 break;
2303
2304 case 5:
2305 NextTok();
2306 aTitleBlock.SetComment( 4, FromUTF8() );
2307 break;
2308
2309 case 6:
2310 NextTok();
2311 aTitleBlock.SetComment( 5, FromUTF8() );
2312 break;
2313
2314 case 7:
2315 NextTok();
2316 aTitleBlock.SetComment( 6, FromUTF8() );
2317 break;
2318
2319 case 8:
2320 NextTok();
2321 aTitleBlock.SetComment( 7, FromUTF8() );
2322 break;
2323
2324 case 9:
2325 NextTok();
2326 aTitleBlock.SetComment( 8, FromUTF8() );
2327 break;
2328
2329 default:
2330 THROW_PARSE_ERROR( _( "Invalid title block comment number" ), CurSource(),
2331 CurLine(), CurLineNumber(), CurOffset() );
2332 }
2333
2334 break;
2335 }
2336
2337 default:
2338 Expecting( "title, date, rev, company, or comment" );
2339 }
2340
2341 NeedRIGHT();
2342 }
2343}
2344
2345
2347{
2348 wxCHECK_MSG( CurTok() == T_property, nullptr,
2349 "Cannot parse " + GetTokenString( CurTok() ) + " as a property token." );
2350
2351 bool is_private = false;
2352
2353 T token = NextTok();
2354
2355 if( token == T_private )
2356 {
2357 is_private = true;
2358 token = NextTok();
2359 }
2360
2361 if( !IsSymbol( token ) )
2362 {
2363 THROW_PARSE_ERROR( _( "Invalid property name" ), CurSource(), CurLine(), CurLineNumber(),
2364 CurOffset() );
2365 }
2366
2367 wxString name = FromUTF8();
2368
2369 if( name.IsEmpty() )
2370 {
2371 THROW_PARSE_ERROR( _( "Empty property name" ), CurSource(), CurLine(), CurLineNumber(),
2372 CurOffset() );
2373 }
2374
2375 token = NextTok();
2376
2377 if( !IsSymbol( token ) )
2378 {
2379 THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
2380 CurOffset() );
2381 }
2382
2383 // Empty property values are valid.
2384 wxString value;
2385
2386 if( m_requiredVersion < 20250318 && CurStr() == "~" )
2387 value = wxEmptyString;
2388 else
2389 value = FromUTF8();
2390
2391 FIELD_T fieldId = FIELD_T::USER;
2392
2393 // Correctly set the ID based on canonical (untranslated) field name
2394 if( aParent->Type() == SCH_SYMBOL_T )
2395 {
2396 for( FIELD_T id : MANDATORY_FIELDS )
2397 {
2398 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
2399 {
2400 fieldId = id;
2401 break;
2402 }
2403 }
2404 }
2405 else if( aParent->Type() == SCH_SHEET_T )
2406 {
2407 fieldId = FIELD_T::SHEET_USER; // This is the default id for user fields
2408
2410 {
2411 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
2412 {
2413 fieldId = id;
2414 break;
2415 }
2416 }
2417
2418 // Legacy support for old field names
2419 if( name.CmpNoCase( wxT( "Sheet name" ) ) == 0 )
2420 fieldId = FIELD_T::SHEET_NAME;
2421 else if( name.CmpNoCase( wxT( "Sheet file" ) ) == 0 )
2422 fieldId = FIELD_T::SHEET_FILENAME;
2423 }
2424 else if( aParent->Type() == SCH_GLOBAL_LABEL_T )
2425 {
2427 {
2428 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
2429 {
2430 fieldId = id;
2431 break;
2432 }
2433 }
2434
2435 // Legacy support for old field names
2436 if( name.CmpNoCase( wxT( "Intersheet References" ) ) == 0 )
2437 fieldId = FIELD_T::INTERSHEET_REFS;
2438 }
2439
2440 std::unique_ptr<SCH_FIELD> field = std::make_unique<SCH_FIELD>( aParent, fieldId, name );
2441 field->SetText( value );
2442
2443 if( fieldId == FIELD_T::USER )
2444 field->SetPrivate( is_private );
2445
2446 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2447 {
2448 if( token != T_LEFT )
2449 Expecting( T_LEFT );
2450
2451 token = NextTok();
2452
2453 switch( token )
2454 {
2455 case T_id: // legacy token; ignore
2456 parseInt( "field ID" );
2457 NeedRIGHT();
2458 break;
2459
2460 case T_at:
2461 field->SetPosition( parseXY() );
2462 field->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ) );
2463 NeedRIGHT();
2464 break;
2465
2466 case T_hide:
2467 field->SetVisible( !parseBool() );
2468 NeedRIGHT();
2469 break;
2470
2471 case T_effects:
2472 parseEDA_TEXT( static_cast<EDA_TEXT*>( field.get() ),
2473 field->GetId() == FIELD_T::VALUE );
2474 break;
2475
2476 case T_show_name:
2477 {
2478 bool show = parseMaybeAbsentBool( true );
2479 field->SetNameShown( show );
2480 break;
2481 }
2482
2483 case T_do_not_autoplace:
2484 {
2485 bool doNotAutoplace = parseMaybeAbsentBool( true );
2486 field->SetCanAutoplace( !doNotAutoplace );
2487 break;
2488 }
2489
2490 default:
2491 Expecting( "id, at, hide, show_name, do_not_autoplace or effects" );
2492 }
2493 }
2494
2495 return field.release();
2496}
2497
2498
2500{
2501 wxCHECK_MSG( aSheet != nullptr, nullptr, "" );
2502 wxCHECK_MSG( CurTok() == T_pin, nullptr,
2503 "Cannot parse " + GetTokenString( CurTok() ) + " as a sheet pin token." );
2504
2505 T token = NextTok();
2506
2507 if( !IsSymbol( token ) )
2508 {
2509 THROW_PARSE_ERROR( _( "Invalid sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
2510 CurOffset() );
2511 }
2512
2513 wxString name = FromUTF8();
2514
2515 if( name.IsEmpty() )
2516 {
2517 THROW_PARSE_ERROR( _( "Empty sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
2518 CurOffset() );
2519 }
2520
2521 auto sheetPin = std::make_unique<SCH_SHEET_PIN>( aSheet, VECTOR2I( 0, 0 ), name );
2522
2523 token = NextTok();
2524
2525 switch( token )
2526 {
2527 case T_input: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
2528 case T_output: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
2529 case T_bidirectional: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
2530 case T_tri_state: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE ); break;
2531 case T_passive: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
2532 default:
2533 Expecting( "input, output, bidirectional, tri_state, or passive" );
2534 }
2535
2536 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2537 {
2538 if( token != T_LEFT )
2539 Expecting( T_LEFT );
2540
2541 token = NextTok();
2542
2543 switch( token )
2544 {
2545 case T_at:
2546 {
2547 sheetPin->SetPosition( parseXY() );
2548
2549 double angle = parseDouble( "sheet pin angle (side)" );
2550
2551 if( angle == 0.0 )
2552 sheetPin->SetSide( SHEET_SIDE::RIGHT );
2553 else if( angle == 90.0 )
2554 sheetPin->SetSide( SHEET_SIDE::TOP );
2555 else if( angle == 180.0 )
2556 sheetPin->SetSide( SHEET_SIDE::LEFT );
2557 else if( angle == 270.0 )
2558 sheetPin->SetSide( SHEET_SIDE::BOTTOM );
2559 else
2560 Expecting( "0, 90, 180, or 270" );
2561
2562 NeedRIGHT();
2563 break;
2564 }
2565
2566 case T_effects:
2567 parseEDA_TEXT( static_cast<EDA_TEXT*>( sheetPin.get() ), true );
2568 break;
2569
2570 case T_uuid:
2571 NeedSYMBOL();
2572 const_cast<KIID&>( sheetPin->m_Uuid ) = parseKIID();
2573 NeedRIGHT();
2574 break;
2575
2576 default:
2577 Expecting( "at, uuid or effects" );
2578 }
2579 }
2580
2581 return sheetPin.release();
2582}
2583
2584
2586{
2587 wxCHECK_RET( CurTok() == T_sheet_instances,
2588 "Cannot parse " + GetTokenString( CurTok() ) + " as an instances token." );
2589 wxCHECK( aScreen, /* void */ );
2590
2591 T token;
2592
2593 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2594 {
2595 if( token != T_LEFT )
2596 Expecting( T_LEFT );
2597
2598 token = NextTok();
2599
2600 switch( token )
2601 {
2602 case T_path:
2603 {
2604 NeedSYMBOL();
2605
2606 SCH_SHEET_INSTANCE instance;
2607
2608 instance.m_Path = KIID_PATH( FromUTF8() );
2609
2610 if( ( !m_appending && aRootSheet->GetScreen() == aScreen ) &&
2611 ( aScreen->GetFileFormatVersionAtLoad() < 20221002 ) )
2612 instance.m_Path.insert( instance.m_Path.begin(), m_rootUuid );
2613
2614 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2615 {
2616 if( token != T_LEFT )
2617 Expecting( T_LEFT );
2618
2619 token = NextTok();
2620
2621 std::vector<wxString> whitespaces = { wxT( "\r" ), wxT( "\n" ), wxT( "\t" ),
2622 wxT( " " ) };
2623
2624 size_t numReplacements = 0;
2625
2626 switch( token )
2627 {
2628 case T_page:
2629 NeedSYMBOL();
2630 instance.m_PageNumber = FromUTF8();
2631
2632 // Empty page numbers are not permitted
2633 if( instance.m_PageNumber.IsEmpty() )
2634 {
2635 // Use hash character instead
2636 instance.m_PageNumber = wxT( "#" );
2637 numReplacements++;
2638 }
2639 else
2640 {
2641 // Whitespaces are not permitted
2642 for( const wxString& ch : whitespaces )
2643 numReplacements += instance.m_PageNumber.Replace( ch, wxEmptyString );
2644
2645 }
2646
2647 // Set the file as modified so the user can be warned.
2648 if( numReplacements > 0 )
2649 aScreen->SetContentModified();
2650
2651 NeedRIGHT();
2652 break;
2653
2654 default:
2655 Expecting( "path or page" );
2656 }
2657 }
2658
2659 if( ( aScreen->GetFileFormatVersionAtLoad() >= 20221110 )
2660 && ( instance.m_Path.empty() ) )
2661 {
2662 SCH_SHEET_PATH rootSheetPath;
2663
2664 rootSheetPath.push_back( aRootSheet );
2665 rootSheetPath.SetPageNumber( instance.m_PageNumber );
2666 }
2667 else
2668 {
2669 aScreen->m_sheetInstances.emplace_back( instance );
2670 }
2671
2672 break;
2673 }
2674
2675 default:
2676 Expecting( "path" );
2677 }
2678 }
2679}
2680
2681
2683{
2684 wxCHECK_RET( CurTok() == T_symbol_instances,
2685 "Cannot parse " + GetTokenString( CurTok() ) + " as an instances token." );
2686 wxCHECK( aScreen, /* void */ );
2687 wxCHECK( m_rootUuid != NilUuid(), /* void */ );
2688
2689 T token;
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_path:
2701 {
2702 NeedSYMBOL();
2703
2704 SCH_SYMBOL_INSTANCE instance;
2705
2706 instance.m_Path = KIID_PATH( FromUTF8() );
2707
2708 if( !m_appending )
2709 instance.m_Path.insert( instance.m_Path.begin(), m_rootUuid );
2710
2711 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2712 {
2713 if( token != T_LEFT )
2714 Expecting( T_LEFT );
2715
2716 token = NextTok();
2717
2718 switch( token )
2719 {
2720 case T_reference:
2721 NeedSYMBOL();
2722 instance.m_Reference = FromUTF8();
2723 NeedRIGHT();
2724 break;
2725
2726 case T_unit:
2727 instance.m_Unit = parseInt( "symbol unit" );
2728 NeedRIGHT();
2729 break;
2730
2731 case T_value:
2732 NeedSYMBOL();
2733
2734 if( m_requiredVersion < 20250318 && CurStr() == "~" )
2735 instance.m_Value = wxEmptyString;
2736 else
2737 instance.m_Value = FromUTF8();
2738
2739 NeedRIGHT();
2740 break;
2741
2742 case T_footprint:
2743 NeedSYMBOL();
2744
2745 if( m_requiredVersion < 20250318 && CurStr() == "~" )
2746 instance.m_Footprint = wxEmptyString;
2747 else
2748 instance.m_Footprint = FromUTF8();
2749
2750 NeedRIGHT();
2751 break;
2752
2753 default:
2754 Expecting( "path, unit, value or footprint" );
2755 }
2756 }
2757
2758 aScreen->m_symbolInstances.emplace_back( instance );
2759 break;
2760 }
2761
2762 default:
2763 Expecting( "path" );
2764 }
2765 }
2766}
2767
2768
2769void SCH_IO_KICAD_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly,
2770 int aFileVersion )
2771{
2772 wxCHECK( aSheet != nullptr, /* void */ );
2773
2774 SCH_SCREEN* screen = aSheet->GetScreen();
2775
2776 wxCHECK( screen != nullptr, /* void */ );
2777
2778 if( SCHEMATIC* schematic = dynamic_cast<SCHEMATIC*>( screen->GetParent() ) )
2779 m_maxError = schematic->Settings().m_MaxError;
2780
2781 if( aIsCopyableOnly )
2782 m_requiredVersion = aFileVersion;
2783
2784 bool fileHasUuid = false;
2785
2786 auto checkVersion =
2787 [&]()
2788 {
2790 {
2791 throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
2793 }
2794 };
2795
2796 T token;
2797
2798 if( !aIsCopyableOnly )
2799 {
2800 NeedLEFT();
2801 NextTok();
2802
2803 if( CurTok() != T_kicad_sch )
2804 Expecting( "kicad_sch" );
2805
2807
2808 // Prior to this, bar was a valid string char for unquoted strings.
2809 SetKnowsBar( m_requiredVersion >= 20240620 );
2810
2811 // Prior to schematic file version 20210406, schematics did not have UUIDs so we need
2812 // to generate one for the root schematic for instance paths.
2813 if( m_requiredVersion < 20210406 )
2814 m_rootUuid = screen->GetUuid();
2815
2816 // Prior to version 20231120, generator_version was not present, so check the date here
2817 if( m_requiredVersion < 20231120 )
2818 checkVersion();
2819 }
2820
2822
2823 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2824 {
2825 if( aIsCopyableOnly && token == T_EOF )
2826 break;
2827
2828 if( token != T_LEFT )
2829 Expecting( T_LEFT );
2830
2831 token = NextTok();
2832
2833 checkpoint();
2834
2835 if( !aIsCopyableOnly && token == T_page && m_requiredVersion <= 20200506 )
2836 token = T_paper;
2837
2838 switch( token )
2839 {
2840 case T_group:
2841 parseGroup();
2842 break;
2843
2844 case T_generator:
2845 // (generator "genname"); we don't care about it at the moment.
2846 NeedSYMBOL();
2847 NeedRIGHT();
2848 break;
2849
2850 case T_host:
2851 {
2852 // (host eeschema ["5.99"]); old version of generator token
2853 NeedSYMBOL();
2854
2855 // Really old versions also included a host version
2856 if( m_requiredVersion < 20200827 )
2857 NeedSYMBOL();
2858
2859 NeedRIGHT();
2860 break;
2861 }
2862
2863 case T_generator_version:
2864 NextTok();
2865 m_generatorVersion = FromUTF8();
2866 NeedRIGHT();
2867 checkVersion();
2868 break;
2869
2870 case T_uuid:
2871 NeedSYMBOL();
2872 screen->m_uuid = parseKIID();
2873
2874 // Set the root sheet UUID with the schematic file UUID. Root sheets are virtual
2875 // and always get a new UUID so this prevents file churn now that the root UUID
2876 // is saved in the symbol instance path.
2877 if( aSheet == m_rootSheet )
2878 {
2879 const_cast<KIID&>( aSheet->m_Uuid ) = screen->GetUuid();
2880 m_rootUuid = screen->GetUuid();
2881 fileHasUuid = true;
2882 }
2883
2884 NeedRIGHT();
2885 break;
2886
2887 case T_paper:
2888 {
2889 if( aIsCopyableOnly )
2890 Unexpected( T_paper );
2891
2892 PAGE_INFO pageInfo;
2893 parsePAGE_INFO( pageInfo );
2894 screen->SetPageSettings( pageInfo );
2895 break;
2896 }
2897
2898 case T_page:
2899 {
2900 if( aIsCopyableOnly )
2901 Unexpected( T_page );
2902
2903 // Only saved for top-level sniffing in Kicad Manager frame and other external
2904 // tool usage with flat hierarchies
2905 NeedSYMBOLorNUMBER();
2906 NeedSYMBOLorNUMBER();
2907 NeedRIGHT();
2908 break;
2909 }
2910
2911 case T_title_block:
2912 {
2913 if( aIsCopyableOnly )
2914 Unexpected( T_title_block );
2915
2916 TITLE_BLOCK tb;
2917 parseTITLE_BLOCK( tb );
2918 screen->SetTitleBlock( tb );
2919 break;
2920 }
2921
2922 case T_lib_symbols:
2923 {
2924 // Dummy map. No derived symbols are allowed in the library cache.
2925 LIB_SYMBOL_MAP symbolLibMap;
2926 LIB_SYMBOL* symbol;
2927
2928 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2929 {
2930 if( token != T_LEFT )
2931 Expecting( T_LEFT );
2932
2933 token = NextTok();
2934
2935 switch( token )
2936 {
2937 case T_symbol:
2938 symbol = parseLibSymbol( symbolLibMap );
2939 screen->AddLibSymbol( symbol );
2940 break;
2941
2942 default:
2943 Expecting( "symbol" );
2944 }
2945 }
2946
2947 break;
2948 }
2949
2950 case T_symbol:
2951 screen->Append( static_cast<SCH_ITEM*>( parseSchematicSymbol() ) );
2952 break;
2953
2954 case T_image:
2955 screen->Append( static_cast<SCH_ITEM*>( parseImage() ) );
2956 break;
2957
2958 case T_sheet:
2959 {
2960 SCH_SHEET* sheet = parseSheet();
2961
2962 // Set the parent to aSheet. This effectively creates a method to find
2963 // the root sheet from any sheet so a pointer to the root sheet does not
2964 // need to be stored globally. Note: this is not the same as a hierarchy.
2965 // Complex hierarchies can have multiple copies of a sheet. This only
2966 // provides a simple tree to find the root sheet.
2967 sheet->SetParent( aSheet );
2968 screen->Append( sheet );
2969 break;
2970 }
2971
2972 case T_junction:
2973 screen->Append( parseJunction() );
2974 break;
2975
2976 case T_no_connect:
2977 screen->Append( parseNoConnect() );
2978 break;
2979
2980 case T_bus_entry:
2981 screen->Append( parseBusEntry() );
2982 break;
2983
2984 case T_polyline:
2985 {
2986 // polyline keyword is used in eeschema both for SCH_SHAPE and SCH_LINE items.
2987 // In symbols it describes a polygon, having n corners and can be filled
2988 // In schematic it describes a line (with no fill descr), but could be extended to a
2989 // polygon (for instance when importing files) because the schematic handles all
2990 // SCH_SHAPE.
2991
2992 // parseSchPolyLine() returns always a SCH_SHAPE, io convert it to a simple SCH_LINE
2993 // For compatibility reasons, keep SCH_SHAPE for a polygon and convert to SCH_LINE
2994 // when the item has only 2 corners, similar to a SCH_LINE
2995 SCH_SHAPE* poly = parseSchPolyLine();
2996
2997 if( poly->GetPointCount() < 2 )
2998 {
2999 delete poly;
3000 THROW_PARSE_ERROR( _( "Schematic polyline has too few points" ), CurSource(), CurLine(),
3001 CurLineNumber(), CurOffset() );
3002 }
3003 else if( poly->GetPointCount() > 2 )
3004 {
3005 screen->Append( poly );
3006 }
3007 else
3008 {
3009 // For SCH_SHAPE having only 2 points, this is a "old" SCH_LINE entity.
3010 // So convert the SCH_SHAPE to a simple SCH_LINE
3011 SCH_LINE* line = new SCH_LINE( VECTOR2I(), LAYER_NOTES );
3012 SHAPE_LINE_CHAIN& outline = poly->GetPolyShape().Outline(0);
3013 line->SetStartPoint( outline.CPoint(0) );
3014 line->SetEndPoint( outline.CPoint(1) );
3015 line->SetStroke( poly->GetStroke() );
3016 line->SetLocked( poly->IsLocked() );
3017 const_cast<KIID&>( line->m_Uuid ) = poly->m_Uuid;
3018
3019 screen->Append( line );
3020
3021 delete poly;
3022 }
3023 }
3024 break;
3025
3026 case T_bus:
3027 case T_wire:
3028 screen->Append( parseLine() );
3029 break;
3030
3031 case T_arc:
3032 screen->Append( parseSchArc() );
3033 break;
3034
3035 case T_circle:
3036 screen->Append( parseSchCircle() );
3037 break;
3038
3039 case T_rectangle:
3040 screen->Append( parseSchRectangle() );
3041 break;
3042
3043 case T_bezier:
3044 screen->Append( parseSchBezier() );
3045 break;
3046
3047 case T_ellipse: screen->Append( parseSchEllipse() ); break;
3048
3049 case T_ellipse_arc: screen->Append( parseSchEllipseArc() ); break;
3050
3051 case T_rule_area:
3052 screen->Append( parseSchRuleArea() );
3053 break;
3054
3055 case T_netclass_flag: // present only during early development of 7.0
3057
3058 case T_text:
3059 case T_label:
3060 case T_global_label:
3061 case T_hierarchical_label:
3062 case T_directive_label:
3063 screen->Append( parseSchText() );
3064 break;
3065
3066 case T_net_chain:
3068 break;
3069
3070 case T_text_box:
3071 screen->Append( parseSchTextBox() );
3072 break;
3073
3074 case T_table:
3075 screen->Append( parseSchTable() );
3076 break;
3077
3078 case T_sheet_instances:
3079 parseSchSheetInstances( aSheet, screen );
3080 break;
3081
3082 case T_symbol_instances:
3083 parseSchSymbolInstances( screen );
3084 break;
3085
3086 case T_bus_alias:
3087 if( aIsCopyableOnly )
3088 Unexpected( T_bus_alias );
3089
3090 parseBusAlias( screen );
3091 break;
3092
3093 case T_embedded_fonts:
3094 {
3095 SCHEMATIC* schematic = screen->Schematic();
3096
3097 if( !schematic )
3098 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
3099 CurLineNumber(), CurOffset() );
3100
3101 schematic->GetEmbeddedFiles()->SetAreFontsEmbedded( parseBool() );
3102 NeedRIGHT();
3103 break;
3104 }
3105
3106 case T_embedded_files:
3107 {
3108 SCHEMATIC* schematic = screen->Schematic();
3109
3110 if( !schematic )
3111 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
3112 CurLineNumber(), CurOffset() );
3113
3114 EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
3115 embeddedFilesParser.SyncLineReaderWith( *this );
3116
3117 try
3118 {
3119 embeddedFilesParser.ParseEmbedded( schematic->GetEmbeddedFiles() );
3120 }
3121 catch( const PARSE_ERROR& e )
3122 {
3123 m_parseWarnings.push_back( e.What() );
3124
3125 int depth = 0;
3126
3127 for( int tok = embeddedFilesParser.NextTok();
3128 tok != DSN_EOF;
3129 tok = embeddedFilesParser.NextTok() )
3130 {
3131 if( tok == DSN_LEFT )
3132 depth++;
3133 else if( tok == DSN_RIGHT && --depth < 0 )
3134 break;
3135 }
3136 }
3137
3138 SyncLineReaderWith( embeddedFilesParser );
3139 break;
3140 }
3141
3142
3143 default:
3144 Expecting( "arc, bezier, bitmap, bus, bus_alias, bus_entry, circle, class_label, "
3145 "directive_label, ellipse, ellipse_arc, embedded_fonts, embedded_files, "
3146 "global_label, hierarchical_label, image, junction, lib_symbols, "
3147 "netclass_flag, no_connect, polyline, rectangle, rule_area, sheet, "
3148 "sheet_instances, signal, symbol, symbol_instances, table, text, "
3149 "text_box, title_block, uuid, wire" );
3150 }
3151 }
3152
3153 // Older s-expression schematics may not have a UUID so use the one automatically generated
3154 // as the virtual root sheet UUID.
3155 if( ( aSheet == m_rootSheet ) && !fileHasUuid )
3156 {
3157 const_cast<KIID&>( aSheet->m_Uuid ) = screen->GetUuid();
3158 m_rootUuid = screen->GetUuid();
3159 }
3160
3161 screen->UpdateLocalLibSymbolLinks();
3162 screen->FixupEmbeddedData();
3163
3164 resolveGroups( screen );
3165
3166 SCHEMATIC* schematic = screen->Schematic();
3167
3168 if( !schematic )
3169 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
3170 CurLineNumber(), CurOffset() );
3171
3172 // When loading the schematic, take a moment to cache the fonts so that the font
3173 // picker can show the embedded fonts immediately.
3174 std::vector<std::string> fontNames;
3175 Fontconfig()->ListFonts( fontNames, std::string( Pgm().GetLanguageTag().utf8_str() ),
3176 schematic->GetEmbeddedFiles()->GetFontFiles(), true );
3177
3178 if( m_requiredVersion < 20200828 )
3180}
3181
3182
3184{
3185 wxCHECK_MSG( CurTok() == T_symbol, nullptr,
3186 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
3187
3188 T token;
3189 wxString libName;
3190 SCH_FIELD* field;
3191 std::unique_ptr<SCH_SYMBOL> symbol = std::make_unique<SCH_SYMBOL>();
3192 TRANSFORM transform;
3193 std::set<int> fieldIDsRead;
3194
3195 // We'll reset this if we find a fields_autoplaced token
3196 symbol->SetFieldsAutoplaced( AUTOPLACE_NONE );
3197
3198 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3199 {
3200 if( token != T_LEFT )
3201 Expecting( T_LEFT );
3202
3203 token = NextTok();
3204
3205 switch( token )
3206 {
3207 case T_lib_name:
3208 {
3209 LIB_ID libId;
3210
3211 token = NextTok();
3212
3213 if( !IsSymbol( token ) )
3214 {
3215 THROW_PARSE_ERROR( _( "Invalid symbol library name" ), CurSource(), CurLine(),
3216 CurLineNumber(), CurOffset() );
3217 }
3218
3219 libName = FromUTF8();
3220 // Some symbol LIB_IDs or lib_name have the '/' character escaped which can break
3221 // symbol links. The '/' character is no longer an illegal LIB_ID character so
3222 // it doesn't need to be escaped.
3223 libName.Replace( "{slash}", "/" );
3224
3225 NeedRIGHT();
3226 break;
3227 }
3228
3229 case T_lib_id:
3230 {
3231 token = NextTok();
3232
3233 if( !IsSymbol( token ) && token != T_NUMBER )
3234 Expecting( "symbol|number" );
3235
3236 LIB_ID libId;
3237 wxString name = FromUTF8();
3238 // Some symbol LIB_IDs have the '/' character escaped which can break
3239 // symbol links. The '/' character is no longer an illegal LIB_ID character so
3240 // it doesn't need to be escaped.
3241 name.Replace( "{slash}", "/" );
3242
3243 int bad_pos = libId.Parse( name );
3244
3245 if( bad_pos >= 0 )
3246 {
3247 if( static_cast<int>( name.size() ) > bad_pos )
3248 {
3249 wxString msg = wxString::Format( _( "Symbol %s contains invalid character '%c'" ),
3250 name,
3251 name[bad_pos] );
3252
3253 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
3254 }
3255
3256 THROW_PARSE_ERROR( _( "Invalid symbol library ID" ), CurSource(), CurLine(),
3257 CurLineNumber(), CurOffset() );
3258 }
3259
3260 symbol->SetLibId( libId );
3261 NeedRIGHT();
3262 break;
3263 }
3264
3265 case T_at:
3266 symbol->SetPosition( parseXY() );
3267
3268 switch( static_cast<int>( parseDouble( "symbol orientation" ) ) )
3269 {
3270 case 0: transform = TRANSFORM(); break;
3271 case 90: transform = TRANSFORM( 0, 1, -1, 0 ); break;
3272 case 180: transform = TRANSFORM( -1, 0, 0, -1 ); break;
3273 case 270: transform = TRANSFORM( 0, -1, 1, 0 ); break;
3274 default: Expecting( "0, 90, 180, or 270" );
3275 }
3276
3277 symbol->SetTransform( transform );
3278 NeedRIGHT();
3279 break;
3280
3281 case T_mirror:
3282 token = NextTok();
3283
3284 if( token == T_x )
3285 symbol->SetOrientation( SYM_MIRROR_X );
3286 else if( token == T_y )
3287 symbol->SetOrientation( SYM_MIRROR_Y );
3288 else
3289 Expecting( "x or y" );
3290
3291 NeedRIGHT();
3292 break;
3293
3294 case T_unit:
3295 symbol->SetUnit( parseInt( "symbol unit" ) );
3296 NeedRIGHT();
3297 break;
3298
3299 case T_convert: // Legacy token
3300 case T_body_style:
3301 symbol->SetBodyStyle( parseInt( "symbol body style" ) );
3302 NeedRIGHT();
3303 break;
3304
3305 case T_exclude_from_sim:
3306 symbol->SetExcludedFromSim( parseBool() );
3307 NeedRIGHT();
3308 break;
3309
3310 case T_in_bom:
3311 symbol->SetExcludedFromBOM( !parseBool() );
3312 NeedRIGHT();
3313 break;
3314
3315 case T_on_board:
3316 symbol->SetExcludedFromBoard( !parseBool() );
3317 NeedRIGHT();
3318 break;
3319
3320 case T_in_pos_files:
3321 symbol->SetExcludedFromPosFiles( !parseBool() );
3322 NeedRIGHT();
3323 break;
3324
3325 case T_dnp:
3326 symbol->SetDNP( parseBool() );
3327 NeedRIGHT();
3328 break;
3329
3330 case T_locked:
3331 symbol->SetLocked( parseBool() );
3332 NeedRIGHT();
3333 break;
3334
3335 case T_passthrough:
3336 {
3337 // (passthrough default|block|force)
3338 T t = NextTok();
3339
3340 // Expect a string-like token
3341 if( !IsSymbol( t ) )
3342 Expecting( "default, block or force" );
3343
3344 wxString mode = FromUTF8();
3345
3346 if( mode.IsSameAs( wxT("default"), false ) )
3347 symbol->SetPassthroughMode( SCH_SYMBOL::PASSTHROUGH_MODE::DEFAULT );
3348 else if( mode.IsSameAs( wxT("block"), false ) )
3349 symbol->SetPassthroughMode( SCH_SYMBOL::PASSTHROUGH_MODE::BLOCK );
3350 else if( mode.IsSameAs( wxT("force"), false ) )
3351 symbol->SetPassthroughMode( SCH_SYMBOL::PASSTHROUGH_MODE::FORCE );
3352 else
3353 Expecting( "default, block or force" );
3354
3355 NeedRIGHT();
3356 break;
3357 }
3358
3359 case T_fields_autoplaced:
3360 if( parseMaybeAbsentBool( true ) )
3361 symbol->SetFieldsAutoplaced( AUTOPLACE_AUTO );
3362
3363 break;
3364
3365 case T_uuid:
3366 NeedSYMBOL();
3367 const_cast<KIID&>( symbol->m_Uuid ) = parseKIID();
3368 NeedRIGHT();
3369 break;
3370
3371 case T_default_instance:
3372 {
3373 SCH_SYMBOL_INSTANCE defaultInstance;
3374
3375 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3376 {
3377 if( token != T_LEFT )
3378 Expecting( T_LEFT );
3379
3380 token = NextTok();
3381
3382 switch( token )
3383 {
3384 case T_reference:
3385 NeedSYMBOL();
3386 defaultInstance.m_Reference = FromUTF8();
3387 NeedRIGHT();
3388 break;
3389
3390 case T_unit:
3391 defaultInstance.m_Unit = parseInt( "symbol unit" );
3392 NeedRIGHT();
3393 break;
3394
3395 case T_value:
3396 NeedSYMBOL();
3397
3398 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3399 symbol->SetValueFieldText( wxEmptyString );
3400 else
3401 symbol->SetValueFieldText( FromUTF8() );
3402
3403 NeedRIGHT();
3404 break;
3405
3406 case T_footprint:
3407 NeedSYMBOL();
3408
3409 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3410 symbol->SetFootprintFieldText( wxEmptyString );
3411 else
3412 symbol->SetFootprintFieldText( FromUTF8() );
3413
3414 NeedRIGHT();
3415 break;
3416
3417 default:
3418 Expecting( "reference, unit, value or footprint" );
3419 }
3420 }
3421
3422 break;
3423 }
3424
3425 case T_instances:
3426 {
3427 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3428 {
3429 if( token != T_LEFT )
3430 Expecting( T_LEFT );
3431
3432 token = NextTok();
3433
3434 if( token != T_project )
3435 Expecting( "project" );
3436
3437 NeedSYMBOL();
3438
3439 wxString projectName = FromUTF8();
3440
3441 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3442 {
3443 if( token != T_LEFT )
3444 Expecting( T_LEFT );
3445
3446 token = NextTok();
3447
3448 if( token != T_path )
3449 Expecting( "path" );
3450
3451 SCH_SYMBOL_INSTANCE instance;
3452
3453 instance.m_ProjectName = projectName;
3454
3455 NeedSYMBOL();
3456 instance.m_Path = KIID_PATH( FromUTF8() );
3457
3458 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3459 {
3460 if( token != T_LEFT )
3461 Expecting( T_LEFT );
3462
3463 token = NextTok();
3464
3465 switch( token )
3466 {
3467 case T_reference:
3468 NeedSYMBOL();
3469 instance.m_Reference = FromUTF8();
3470 NeedRIGHT();
3471 break;
3472
3473 case T_unit:
3474 instance.m_Unit = parseInt( "symbol unit" );
3475 NeedRIGHT();
3476 break;
3477
3478 case T_value:
3479 NeedSYMBOL();
3480
3481 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3482 symbol->SetValueFieldText( wxEmptyString );
3483 else
3484 symbol->SetValueFieldText( FromUTF8() );
3485
3486 NeedRIGHT();
3487 break;
3488
3489 case T_footprint:
3490 NeedSYMBOL();
3491
3492 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3493 symbol->SetFootprintFieldText( wxEmptyString );
3494 else
3495 symbol->SetFootprintFieldText( FromUTF8() );
3496
3497 NeedRIGHT();
3498 break;
3499
3500
3501 case T_variant:
3502 {
3503 SCH_SYMBOL_VARIANT variant;
3504 variant.InitializeAttributes( *symbol );
3505
3506 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3507 {
3508 if( token != T_LEFT )
3509 Expecting( T_LEFT );
3510
3511 token = NextTok();
3512
3513 switch( token )
3514 {
3515 case T_name:
3516 NeedSYMBOL();
3517 variant.m_Name = FromUTF8();
3518 NeedRIGHT();
3519 break;
3520
3521 case T_dnp:
3522 variant.m_DNP = parseBool();
3523 NeedRIGHT();
3524 break;
3525
3526 case T_exclude_from_sim:
3527 variant.m_ExcludedFromSim = parseBool();
3528 NeedRIGHT();
3529 break;
3530
3531 case T_in_bom:
3532 variant.m_ExcludedFromBOM = parseBool();
3533
3534 // This fixes the incorrect logic from prior file versions. The "in_bom" token
3535 // used in the file format is the positive logic. However, in the UI the term
3536 // excluded from BOM is used which is the inverted logic.
3537 if( m_requiredVersion >= 20260306 )
3538 variant.m_ExcludedFromBOM = !variant.m_ExcludedFromBOM;
3539
3540 NeedRIGHT();
3541 break;
3542
3543 case T_on_board:
3544 variant.m_ExcludedFromBoard = !parseBool();
3545 NeedRIGHT();
3546 break;
3547
3548 case T_in_pos_files:
3549 variant.m_ExcludedFromPosFiles = !parseBool();
3550 NeedRIGHT();
3551 break;
3552
3553 case T_field:
3554 {
3555 wxString fieldName;
3556 wxString fieldValue;
3557
3558 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3559 {
3560 if( token != T_LEFT )
3561 Expecting( T_LEFT );
3562
3563 token = NextTok();
3564
3565 switch( token )
3566 {
3567 case T_name:
3568 NeedSYMBOL();
3569 fieldName = FromUTF8();
3570 NeedRIGHT();
3571 break;
3572
3573 case T_value:
3574 NeedSYMBOL();
3575 fieldValue = FromUTF8();
3576 NeedRIGHT();
3577 break;
3578
3579 default:
3580 Expecting( "name or value" );
3581 }
3582 }
3583
3584 variant.m_Fields[fieldName] = fieldValue;
3585 break;
3586 }
3587
3588 default:
3589 Expecting( "dnp, exclude_from_sim, field, in_bom, in_pos_files, name, or on_board" );
3590 }
3591
3592 instance.m_Variants[variant.m_Name] = variant;
3593 }
3594
3595 break;
3596 }
3597
3598 default:
3599 Expecting( "reference, unit, value, footprint, or variant" );
3600 }
3601 }
3602
3603 symbol->AddHierarchicalReference( instance );
3604 }
3605 }
3606
3607 break;
3608 }
3609
3610 case T_property:
3611 // The field parent symbol must be set and its orientation must be set before
3612 // the field positions are set.
3613 field = parseSchField( symbol.get() );
3614
3615 // Exclude from simulation used to be managed by a Sim.Enable field set to "0" when
3616 // simulation was disabled.
3618 {
3619 symbol->SetExcludedFromSim( field->GetText() == wxS( "0" ) );
3620 delete field;
3621 break;
3622 }
3623
3624 // Even longer ago, we had a "Spice_Netlist_Enabled" field
3626 {
3627 symbol->SetExcludedFromSim( field->GetText() == wxS( "N" ) );
3628 delete field;
3629 break;
3630 }
3631
3632 SCH_FIELD* existing;
3633
3634 if( field->IsMandatory() )
3635 existing = symbol->GetField( field->GetId() );
3636 else
3637 existing = symbol->GetField( field->GetName() );
3638
3639 if( existing )
3640 *existing = *field;
3641 else
3642 symbol->AddField( *field );
3643
3644 if( field->GetId() == FIELD_T::REFERENCE )
3645 symbol->UpdatePrefix();
3646
3647 delete field;
3648 break;
3649
3650 case T_pin:
3651 {
3652 // Read an alternate pin designation
3653 wxString number;
3654 KIID uuid;
3655 wxString alt;
3656
3657 NeedSYMBOL();
3658 number = FromUTF8();
3659
3660 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3661 {
3662 if( token != T_LEFT )
3663 Expecting( T_LEFT );
3664
3665 token = NextTok();
3666
3667 switch( token )
3668 {
3669 case T_alternate:
3670 NeedSYMBOL();
3671 alt = FromUTF8();
3672 NeedRIGHT();
3673 break;
3674
3675 case T_uuid:
3676 NeedSYMBOL();
3677
3678 // First version to write out pin uuids accidentally wrote out the symbol's
3679 // uuid for each pin, so ignore uuids coming from that version.
3680 if( m_requiredVersion >= 20210126 )
3681 uuid = parseKIID();
3682
3683 NeedRIGHT();
3684 break;
3685
3686 default:
3687 Expecting( "alternate or uuid" );
3688 }
3689 }
3690
3691 symbol->GetRawPins().emplace_back( std::make_unique<SCH_PIN>( symbol.get(), number,
3692 alt, uuid ) );
3693 break;
3694 }
3695
3696 default:
3697 Expecting( "lib_id, lib_name, at, mirror, uuid, exclude_from_sim, on_board, in_bom, dnp, passthrough, "
3698 "default_instance, property, pin, or instances" );
3699 }
3700 }
3701
3702 if( !libName.IsEmpty() && ( symbol->GetLibId().Format().wx_str() != libName ) )
3703 symbol->SetSchSymbolLibraryName( libName );
3704
3705 // Ensure edit/status flags are cleared after these initializations:
3706 symbol->ClearFlags();
3707
3708 return symbol.release();
3709}
3710
3711
3713{
3714 wxCHECK_MSG( CurTok() == T_image, nullptr,
3715 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an image." ) );
3716
3717 T token;
3718 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>();
3719 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
3720
3721 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3722 {
3723 if( token != T_LEFT )
3724 Expecting( T_LEFT );
3725
3726 token = NextTok();
3727
3728 switch( token )
3729 {
3730 case T_at:
3731 bitmap->SetPosition( parseXY() );
3732 NeedRIGHT();
3733 break;
3734
3735 case T_scale:
3736 {
3737 const double scale = parseDouble( "image scale factor" );
3738 refImage.SetImageScale( std::isnormal( scale ) ? scale : 1.0 );
3739
3740 NeedRIGHT();
3741 break;
3742 }
3743 case T_uuid:
3744 NeedSYMBOL();
3745 const_cast<KIID&>( bitmap->m_Uuid ) = parseKIID();
3746 NeedRIGHT();
3747 break;
3748
3749 case T_data:
3750 {
3751 token = NextTok();
3752
3753 wxString data;
3754
3755 // Reserve 128K because most image files are going to be larger than the default
3756 // 1K that wxString reserves.
3757 data.reserve( 1 << 17 );
3758
3759 while( token != T_RIGHT )
3760 {
3761 if( !IsSymbol( token ) )
3762 Expecting( "base64 image data" );
3763
3764 data += FromUTF8();
3765 token = NextTok();
3766 }
3767
3768 wxMemoryBuffer buffer = wxBase64Decode( data );
3769
3770 if( !refImage.ReadImageFile( buffer ) )
3771 THROW_IO_ERROR( _( "Failed to read image data." ) );
3772
3773 break;
3774 }
3775
3776 case T_locked:
3777 bitmap->SetLocked( parseBool() );
3778 NeedRIGHT();
3779 break;
3780
3781 default:
3782 Expecting( "at, scale, uuid, data or locked" );
3783 }
3784 }
3785
3786 // The image will be scaled by PPI in ReadImageFile.
3787
3788 // 20230121 or older file format versions assumed 300 image PPI at load/save.
3789 // Let's keep compatibility by changing image scale.
3790 if( m_requiredVersion <= 20230121 )
3791 {
3792 refImage.SetImageScale( refImage.GetImageScale() * refImage.GetImage().GetPPI() / 300.0 );
3793 }
3794
3795 return bitmap.release();
3796}
3797
3798
3800{
3801 wxCHECK_MSG( CurTok() == T_sheet, nullptr,
3802 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a sheet." ) );
3803
3804 T token;
3806 FILL_PARAMS fill;
3807 SCH_FIELD* field;
3808 std::vector<SCH_FIELD> fields;
3809 std::unique_ptr<SCH_SHEET> sheet = std::make_unique<SCH_SHEET>();
3810 std::set<int> fieldIDsRead;
3811
3812 // We'll reset this if we find a fields_autoplaced token
3813 sheet->SetFieldsAutoplaced( AUTOPLACE_NONE );
3814
3815 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3816 {
3817 if( token != T_LEFT )
3818 Expecting( T_LEFT );
3819
3820 token = NextTok();
3821
3822 switch( token )
3823 {
3824 case T_at:
3825 sheet->SetPosition( parseXY() );
3826 NeedRIGHT();
3827 break;
3828
3829 case T_size:
3830 {
3831 VECTOR2I size;
3832 size.x = parseInternalUnits( "sheet width" );
3833 size.y = parseInternalUnits( "sheet height" );
3834 sheet->SetSize( size );
3835 NeedRIGHT();
3836 break;
3837 }
3838
3839 case T_exclude_from_sim:
3840 sheet->SetExcludedFromSim( parseBool() );
3841 NeedRIGHT();
3842 break;
3843
3844 case T_in_bom:
3845 sheet->SetExcludedFromBOM( !parseBool() );
3846 NeedRIGHT();
3847 break;
3848
3849 case T_on_board:
3850 sheet->SetExcludedFromBoard( !parseBool() );
3851 NeedRIGHT();
3852 break;
3853
3854 case T_dnp:
3855 sheet->SetDNP( parseBool() );
3856 NeedRIGHT();
3857 break;
3858
3859 case T_locked:
3860 sheet->SetLocked( parseBool() );
3861 NeedRIGHT();
3862 break;
3863
3864 case T_fields_autoplaced:
3865 if( parseMaybeAbsentBool( true ) )
3866 sheet->SetFieldsAutoplaced( AUTOPLACE_AUTO );
3867
3868 break;
3869
3870 case T_stroke:
3871 parseStroke( stroke );
3872 sheet->SetBorderWidth( stroke.GetWidth() );
3873 sheet->SetBorderColor( stroke.GetColor() );
3874 break;
3875
3876 case T_fill:
3877 parseFill( fill );
3878 sheet->SetBackgroundColor( fill.m_Color );
3879 break;
3880
3881 case T_uuid:
3882 NeedSYMBOL();
3883 const_cast<KIID&>( sheet->m_Uuid ) = parseKIID();
3884 NeedRIGHT();
3885 break;
3886
3887 case T_property:
3888 field = parseSchField( sheet.get() );
3889
3890 if( m_requiredVersion <= 20200310 )
3891 {
3892 // Earlier versions had the wrong ids (and names) saved for sheet fields.
3893 // Fortunately they only saved the sheetname and sheetfilepath (and always
3894 // in that order), so we can hack in a recovery.
3895 if( fields.empty() )
3896 field->setId( FIELD_T::SHEET_NAME );
3897 else
3899 }
3900
3901 fields.emplace_back( *field );
3902
3903 delete field;
3904 break;
3905
3906 case T_pin:
3907 sheet->AddPin( parseSchSheetPin( sheet.get() ) );
3908 break;
3909
3910 case T_instances:
3911 {
3912 std::vector<SCH_SHEET_INSTANCE> instances;
3913
3914 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3915 {
3916 if( token != T_LEFT )
3917 Expecting( T_LEFT );
3918
3919 token = NextTok();
3920
3921 if( token != T_project )
3922 Expecting( "project" );
3923
3924 NeedSYMBOL();
3925
3926 wxString projectName = FromUTF8();
3927
3928 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3929 {
3930 if( token != T_LEFT )
3931 Expecting( T_LEFT );
3932
3933 token = NextTok();
3934
3935 if( token != T_path )
3936 Expecting( "path" );
3937
3938 SCH_SHEET_INSTANCE instance;
3939
3940 instance.m_ProjectName = projectName;
3941
3942 NeedSYMBOL();
3943 instance.m_Path = KIID_PATH( FromUTF8() );
3944
3945 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3946 {
3947 if( token != T_LEFT )
3948 Expecting( T_LEFT );
3949
3950 token = NextTok();
3951
3952 switch( token )
3953 {
3954 case T_page:
3955 {
3956 NeedSYMBOL();
3957 instance.m_PageNumber = FromUTF8();
3958
3959 // Empty page numbers are not permitted
3960 if( instance.m_PageNumber.IsEmpty() )
3961 {
3962 // Use hash character instead
3963 instance.m_PageNumber = wxT( "#" );
3964 }
3965 else
3966 {
3967 // Whitespaces are not permitted
3968 static std::vector<wxString> whitespaces =
3969 { wxT( "\r" ),
3970 wxT( "\n" ),
3971 wxT( "\t" ),
3972 wxT( " " ) };
3973
3974 for( wxString ch : whitespaces )
3975 instance.m_PageNumber.Replace( ch, wxEmptyString );
3976 }
3977
3978 NeedRIGHT();
3979 break;
3980 }
3981
3982 case T_variant:
3983 {
3984 SCH_SHEET_VARIANT variant;
3985 variant.InitializeAttributes( *sheet );
3986
3987 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3988 {
3989 if( token != T_LEFT )
3990 Expecting( T_LEFT );
3991
3992 token = NextTok();
3993
3994 switch( token )
3995 {
3996 case T_name:
3997 NeedSYMBOL();
3998 variant.m_Name = FromUTF8();
3999 NeedRIGHT();
4000 break;
4001
4002 case T_dnp:
4003 variant.m_DNP = parseBool();
4004 NeedRIGHT();
4005 break;
4006
4007 case T_exclude_from_sim:
4008 variant.m_ExcludedFromSim = parseBool();
4009 NeedRIGHT();
4010 break;
4011
4012 case T_in_bom:
4013 variant.m_ExcludedFromBOM = parseBool();
4014
4015 // This fixes the incorrect logic from prior file versions. The "in_bom" token
4016 // used in the file format is the positive logic. However, in the UI the term
4017 // excluded from BOM is used which is the inverted logic.
4018 if( m_requiredVersion >= 20260306 )
4019 variant.m_ExcludedFromBOM = !variant.m_ExcludedFromBOM;
4020
4021 NeedRIGHT();
4022 break;
4023
4024 case T_on_board:
4025 variant.m_ExcludedFromBoard = !parseBool();
4026 NeedRIGHT();
4027 break;
4028
4029 case T_in_pos_files:
4030 variant.m_ExcludedFromPosFiles = !parseBool();
4031 NeedRIGHT();
4032 break;
4033
4034 case T_field:
4035 {
4036 wxString fieldName;
4037 wxString fieldValue;
4038
4039 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4040 {
4041 if( token != T_LEFT )
4042 Expecting( T_LEFT );
4043
4044 token = NextTok();
4045
4046 switch( token )
4047 {
4048 case T_name:
4049 NeedSYMBOL();
4050 fieldName = FromUTF8();
4051 NeedRIGHT();
4052 break;
4053
4054 case T_value:
4055 NeedSYMBOL();
4056 fieldValue = FromUTF8();
4057 NeedRIGHT();
4058 break;
4059
4060 default:
4061 Expecting( "name or value" );
4062 }
4063 }
4064
4065 variant.m_Fields[fieldName] = fieldValue;
4066 break;
4067 }
4068
4069 default:
4070 Expecting( "dnp, exclude_from_sim, field, in_bom, in_pos_files, name, or on_board" );
4071 }
4072
4073 instance.m_Variants[variant.m_Name] = variant;
4074 }
4075
4076 break;
4077 }
4078
4079 default:
4080 Expecting( "page or variant" );
4081 }
4082 }
4083
4084 instances.emplace_back( instance );
4085 }
4086 }
4087
4088 sheet->setInstances( instances );
4089 break;
4090 }
4091
4092 default:
4093 Expecting( "at, size, stroke, background, instances, uuid, property, or pin" );
4094 }
4095 }
4096
4097 sheet->SetFields( fields );
4098
4099 if( !FindField( sheet->GetFields(), FIELD_T::SHEET_NAME ) )
4100 {
4101 THROW_PARSE_ERROR( _( "Missing sheet name property" ), CurSource(), CurLine(),
4102 CurLineNumber(), CurOffset() );
4103 }
4104
4105 if( !FindField( sheet->GetFields(), FIELD_T::SHEET_FILENAME ) )
4106 {
4107 THROW_PARSE_ERROR( _( "Missing sheet file property" ), CurSource(), CurLine(),
4108 CurLineNumber(), CurOffset() );
4109 }
4110
4111 return sheet.release();
4112}
4113
4114
4116{
4117 wxCHECK_MSG( CurTok() == T_junction, nullptr,
4118 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a junction." ) );
4119
4120 T token;
4121 std::unique_ptr<SCH_JUNCTION> junction = std::make_unique<SCH_JUNCTION>();
4122
4123 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4124 {
4125 if( token != T_LEFT )
4126 Expecting( T_LEFT );
4127
4128 token = NextTok();
4129
4130 switch( token )
4131 {
4132 case T_at:
4133 junction->SetPosition( parseXY() );
4134 NeedRIGHT();
4135 break;
4136
4137 case T_diameter:
4138 junction->SetDiameter( parseInternalUnits( "junction diameter" ) );
4139 NeedRIGHT();
4140 break;
4141
4142 case T_color:
4143 {
4144 COLOR4D color;
4145
4146 color.r = parseInt( "red" ) / 255.0;
4147 color.g = parseInt( "green" ) / 255.0;
4148 color.b = parseInt( "blue" ) / 255.0;
4149 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
4150
4151 junction->SetColor( color );
4152 NeedRIGHT();
4153 break;
4154 }
4155
4156 case T_uuid:
4157 NeedSYMBOL();
4158 const_cast<KIID&>( junction->m_Uuid ) = parseKIID();
4159 NeedRIGHT();
4160 break;
4161
4162 case T_locked:
4163 junction->SetLocked( parseBool() );
4164 NeedRIGHT();
4165 break;
4166
4167 default:
4168 Expecting( "at, diameter, color, uuid or locked" );
4169 }
4170 }
4171
4172 return junction.release();
4173}
4174
4175
4177{
4178 wxCHECK_MSG( CurTok() == T_no_connect, nullptr,
4179 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a no connect." ) );
4180
4181 T token;
4182 std::unique_ptr<SCH_NO_CONNECT> no_connect = std::make_unique<SCH_NO_CONNECT>();
4183
4184 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4185 {
4186 if( token != T_LEFT )
4187 Expecting( T_LEFT );
4188
4189 token = NextTok();
4190
4191 switch( token )
4192 {
4193 case T_at:
4194 no_connect->SetPosition( parseXY() );
4195 NeedRIGHT();
4196 break;
4197
4198 case T_uuid:
4199 NeedSYMBOL();
4200 const_cast<KIID&>( no_connect->m_Uuid ) = parseKIID();
4201 NeedRIGHT();
4202 break;
4203
4204 case T_locked:
4205 no_connect->SetLocked( parseBool() );
4206 NeedRIGHT();
4207 break;
4208
4209 default:
4210 Expecting( "at, uuid or locked" );
4211 }
4212 }
4213
4214 return no_connect.release();
4215}
4216
4217
4219{
4220 wxCHECK_MSG( CurTok() == T_bus_entry, nullptr,
4221 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus entry." ) );
4222
4223 T token;
4225 std::unique_ptr<SCH_BUS_WIRE_ENTRY> busEntry = std::make_unique<SCH_BUS_WIRE_ENTRY>();
4226
4227 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4228 {
4229 if( token != T_LEFT )
4230 Expecting( T_LEFT );
4231
4232 token = NextTok();
4233
4234 switch( token )
4235 {
4236 case T_at:
4237 busEntry->SetPosition( parseXY() );
4238 NeedRIGHT();
4239 break;
4240
4241 case T_size:
4242 {
4243 VECTOR2I size;
4244
4245 size.x = parseInternalUnits( "bus entry height" );
4246 size.y = parseInternalUnits( "bus entry width" );
4247 busEntry->SetSize( size );
4248 NeedRIGHT();
4249 break;
4250 }
4251
4252 case T_stroke:
4253 parseStroke( stroke );
4254 busEntry->SetStroke( stroke );
4255 break;
4256
4257 case T_uuid:
4258 NeedSYMBOL();
4259 const_cast<KIID&>( busEntry->m_Uuid ) = parseKIID();
4260 NeedRIGHT();
4261 break;
4262
4263 case T_locked:
4264 busEntry->SetLocked( parseBool() );
4265 NeedRIGHT();
4266 break;
4267
4268 default:
4269 Expecting( "at, size, uuid, stroke or locked" );
4270 }
4271 }
4272
4273 return busEntry.release();
4274}
4275
4276
4278{
4279 if( aShape->GetFillMode() == FILL_T::FILLED_SHAPE )
4280 {
4281 aShape->SetFillColor( aShape->GetStroke().GetColor() );
4283 }
4284}
4285
4286
4288{
4289 T token;
4291 FILL_PARAMS fill;
4292
4293 std::unique_ptr<SCH_SHAPE> polyline = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_NOTES );
4294
4295 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4296 {
4297 if( token != T_LEFT )
4298 Expecting( T_LEFT );
4299
4300 token = NextTok();
4301
4302 switch( token )
4303 {
4304 case T_pts:
4305 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4306 {
4307 if( token != T_LEFT )
4308 Expecting( T_LEFT );
4309
4310 token = NextTok();
4311
4312 if( token != T_xy )
4313 Expecting( "xy" );
4314
4315 polyline->AddPoint( parseXY() );
4316
4317 NeedRIGHT();
4318 }
4319 break;
4320
4321 case T_stroke:
4322 parseStroke( stroke );
4323
4324 // In 6.0, the default schematic line style was Dashed.
4325 if( m_requiredVersion <= 20211123 && stroke.GetLineStyle() == LINE_STYLE::DEFAULT )
4327
4328 polyline->SetStroke( stroke );
4329 break;
4330
4331 case T_fill:
4332 parseFill( fill );
4333 polyline->SetFillMode( fill.m_FillType );
4334 polyline->SetFillColor( fill.m_Color );
4335 fixupSchFillMode( polyline.get() );
4336 break;
4337
4338 case T_uuid:
4339 NeedSYMBOL();
4340 const_cast<KIID&>( polyline->m_Uuid ) = parseKIID();
4341 NeedRIGHT();
4342 break;
4343
4344 case T_locked:
4345 polyline->SetLocked( parseBool() );
4346 NeedRIGHT();
4347 break;
4348
4349 default:
4350 Expecting( "pts, uuid, stroke, fill or locked" );
4351 }
4352 }
4353
4354 return polyline.release();
4355}
4356
4357
4359{
4360 // Note: T_polyline is deprecated in this code: it is now handled by
4361 // parseSchPolyLine() that can handle true polygons, and not only one segment.
4362
4363 T token;
4365 int layer;
4366
4367 switch( CurTok() )
4368 {
4369 case T_polyline: layer = LAYER_NOTES; break;
4370 case T_wire: layer = LAYER_WIRE; break;
4371 case T_bus: layer = LAYER_BUS; break;
4372 default:
4373 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as a line." );
4374 }
4375
4376 std::unique_ptr<SCH_LINE> line = std::make_unique<SCH_LINE>( VECTOR2I(), layer );
4377
4378 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4379 {
4380 if( token != T_LEFT )
4381 Expecting( T_LEFT );
4382
4383 token = NextTok();
4384
4385 switch( token )
4386 {
4387 case T_pts:
4388 NeedLEFT();
4389 token = NextTok();
4390
4391 if( token != T_xy )
4392 Expecting( "xy" );
4393
4394 line->SetStartPoint( parseXY() );
4395 NeedRIGHT();
4396 NeedLEFT();
4397 token = NextTok();
4398
4399 if( token != T_xy )
4400 Expecting( "xy" );
4401
4402 line->SetEndPoint( parseXY() );
4403 NeedRIGHT();
4404 NeedRIGHT();
4405 break;
4406
4407 case T_stroke:
4408 parseStroke( stroke );
4409 line->SetStroke( stroke );
4410 break;
4411
4412 case T_uuid:
4413 NeedSYMBOL();
4414 const_cast<KIID&>( line->m_Uuid ) = parseKIID();
4415 NeedRIGHT();
4416 break;
4417
4418 case T_locked:
4419 line->SetLocked( parseBool() );
4420 NeedRIGHT();
4421 break;
4422
4423 default:
4424 Expecting( "pts, uuid, stroke or locked" );
4425 }
4426 }
4427
4428 return line.release();
4429}
4430
4431
4433{
4434 wxCHECK_MSG( CurTok() == T_arc, nullptr,
4435 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc." ) );
4436
4437 T token;
4438 VECTOR2I startPoint;
4439 VECTOR2I midPoint;
4440 VECTOR2I endPoint;
4442 FILL_PARAMS fill;
4443 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC );
4444
4445 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4446 {
4447 if( token != T_LEFT )
4448 Expecting( T_LEFT );
4449
4450 token = NextTok();
4451
4452 switch( token )
4453 {
4454 case T_start:
4455 startPoint = parseXY();
4456 NeedRIGHT();
4457 break;
4458
4459 case T_mid:
4460 midPoint = parseXY();
4461 NeedRIGHT();
4462 break;
4463
4464 case T_end:
4465 endPoint = parseXY();
4466 NeedRIGHT();
4467 break;
4468
4469 case T_stroke:
4470 parseStroke( stroke );
4471 arc->SetStroke( stroke );
4472 break;
4473
4474 case T_fill:
4475 parseFill( fill );
4476 arc->SetFillMode( fill.m_FillType );
4477 arc->SetFillColor( fill.m_Color );
4478 fixupSchFillMode( arc.get() );
4479 break;
4480
4481 case T_uuid:
4482 NeedSYMBOL();
4483 const_cast<KIID&>( arc->m_Uuid ) = parseKIID();
4484 NeedRIGHT();
4485 break;
4486
4487 case T_locked:
4488 arc->SetLocked( parseBool() );
4489 NeedRIGHT();
4490 break;
4491
4492 default:
4493 Expecting( "start, mid, end, stroke, fill, uuid or locked" );
4494 }
4495 }
4496
4497 arc->SetArcGeometry( startPoint, midPoint, endPoint );
4498
4499 return arc.release();
4500}
4501
4502
4504{
4505 wxCHECK_MSG( CurTok() == T_circle, nullptr,
4506 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle." ) );
4507
4508 T token;
4510 int radius = 0;
4512 FILL_PARAMS fill;
4513 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE );
4514
4515 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4516 {
4517 if( token != T_LEFT )
4518 Expecting( T_LEFT );
4519
4520 token = NextTok();
4521
4522 switch( token )
4523 {
4524 case T_center:
4525 center = parseXY();
4526 NeedRIGHT();
4527 break;
4528
4529 case T_radius:
4530 radius = parseInternalUnits( "radius length" );
4531 NeedRIGHT();
4532 break;
4533
4534 case T_stroke:
4535 parseStroke( stroke );
4536 circle->SetStroke( stroke );
4537 break;
4538
4539 case T_fill:
4540 parseFill( fill );
4541 circle->SetFillMode( fill.m_FillType );
4542 circle->SetFillColor( fill.m_Color );
4543 fixupSchFillMode( circle.get() );
4544 break;
4545
4546 case T_uuid:
4547 NeedSYMBOL();
4548 const_cast<KIID&>( circle->m_Uuid ) = parseKIID();
4549 NeedRIGHT();
4550 break;
4551
4552 case T_locked:
4553 circle->SetLocked( parseBool() );
4554 NeedRIGHT();
4555 break;
4556
4557 default:
4558 Expecting( "center, radius, stroke, fill, uuid or locked" );
4559 }
4560 }
4561
4562 circle->SetCenter( center );
4563 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
4564
4565 return circle.release();
4566}
4567
4568
4570{
4571 wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
4572 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle." ) );
4573
4574 T token;
4576 FILL_PARAMS fill;
4577 std::unique_ptr<SCH_SHAPE> rectangle = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE );
4578
4579 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4580 {
4581 if( token != T_LEFT )
4582 Expecting( T_LEFT );
4583
4584 token = NextTok();
4585
4586 switch( token )
4587 {
4588 case T_start:
4589 rectangle->SetPosition( parseXY() );
4590 NeedRIGHT();
4591 break;
4592
4593 case T_end:
4594 rectangle->SetEnd( parseXY() );
4595 NeedRIGHT();
4596 break;
4597
4598 case T_radius:
4599 rectangle->SetCornerRadius( parseDouble( "corner radius" ) * schIUScale.IU_PER_MM );
4600 NeedRIGHT();
4601 break;
4602
4603 case T_stroke:
4604 parseStroke( stroke );
4605 rectangle->SetStroke( stroke );
4606 break;
4607
4608 case T_fill:
4609 parseFill( fill );
4610 rectangle->SetFillMode( fill.m_FillType );
4611 rectangle->SetFillColor( fill.m_Color );
4612 fixupSchFillMode( rectangle.get() );
4613 break;
4614
4615 case T_uuid:
4616 NeedSYMBOL();
4617 const_cast<KIID&>( rectangle->m_Uuid ) = parseKIID();
4618 NeedRIGHT();
4619 break;
4620
4621 case T_locked:
4622 rectangle->SetLocked( parseBool() );
4623 NeedRIGHT();
4624 break;
4625
4626 default:
4627 Expecting( "start, end, stroke, fill, uuid or locked" );
4628 }
4629 }
4630
4631 return rectangle.release();
4632}
4633
4634
4636{
4637 wxCHECK_MSG( CurTok() == T_rule_area, nullptr,
4638 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rule area." ) );
4639
4640 T token;
4642 FILL_PARAMS fill;
4643 std::unique_ptr<SCH_RULE_AREA> ruleArea = std::make_unique<SCH_RULE_AREA>();
4644
4645 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4646 {
4647 if( token != T_LEFT )
4648 Expecting( T_LEFT );
4649
4650 token = NextTok();
4651
4652 switch( token )
4653 {
4654 case T_polyline:
4655 {
4656 std::unique_ptr<SCH_SHAPE> poly( parseSchPolyLine() );
4657 SHAPE_POLY_SET& sch_rule_poly = poly->GetPolyShape();
4658
4659 // The polygon must be closed, it is a schematic closed polyline:
4660 sch_rule_poly.Outline(0).SetClosed( true );
4661
4662 ruleArea->SetPolyShape( sch_rule_poly );
4663
4664 ruleArea->SetStroke( poly->GetStroke() );
4665 ruleArea->SetFillMode( poly->GetFillMode() );
4666 ruleArea->SetFillColor( poly->GetFillColor() );
4667
4668 // the uuid is saved to the shape but stored and saved out of the rulearea
4669 const_cast<KIID&>( ruleArea->m_Uuid ) = poly->m_Uuid;
4670 break;
4671 }
4672
4673 case T_exclude_from_sim:
4674 ruleArea->SetExcludedFromSim( parseBool() );
4675 NeedRIGHT();
4676 break;
4677
4678 case T_in_bom:
4679 ruleArea->SetExcludedFromBOM( !parseBool() );
4680 NeedRIGHT();
4681 break;
4682
4683 case T_on_board:
4684 ruleArea->SetExcludedFromBoard( !parseBool() );
4685 NeedRIGHT();
4686 break;
4687
4688 case T_dnp:
4689 ruleArea->SetDNP( parseBool() );
4690 NeedRIGHT();
4691 break;
4692
4693 case T_locked:
4694 ruleArea->SetLocked( parseBool() );
4695 NeedRIGHT();
4696 break;
4697
4698 default:
4699 Expecting( "exclude_from_sim, on_board, in_bom, dnp, locked, or polyline" );
4700 }
4701 }
4702
4703 return ruleArea.release();
4704}
4705
4706
4708{
4709 wxCHECK_MSG( CurTok() == T_bezier, nullptr,
4710 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
4711
4712 T token;
4714 FILL_PARAMS fill;
4715 std::unique_ptr<SCH_SHAPE> bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER );
4716
4717 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4718 {
4719 if( token != T_LEFT )
4720 Expecting( T_LEFT );
4721
4722 token = NextTok();
4723
4724 switch( token )
4725 {
4726 case T_pts:
4727 {
4728 int ii = 0;
4729
4730 for( token = NextTok(); token != T_RIGHT; token = NextTok(), ++ii )
4731 {
4732 if( token != T_LEFT )
4733 Expecting( T_LEFT );
4734
4735 token = NextTok();
4736
4737 if( token != T_xy )
4738 Expecting( "xy" );
4739
4740 switch( ii )
4741 {
4742 case 0: bezier->SetStart( parseXY() ); break;
4743 case 1: bezier->SetBezierC1( parseXY() ); break;
4744 case 2: bezier->SetBezierC2( parseXY() ); break;
4745 case 3: bezier->SetEnd( parseXY() ); break;
4746 default: Unexpected( "control point" ); break;
4747 }
4748
4749 NeedRIGHT();
4750 }
4751 }
4752 break;
4753
4754 case T_stroke:
4755 parseStroke( stroke );
4756 bezier->SetStroke( stroke );
4757 break;
4758
4759 case T_fill:
4760 parseFill( fill );
4761 bezier->SetFillMode( fill.m_FillType );
4762 bezier->SetFillColor( fill.m_Color );
4763 fixupSchFillMode( bezier.get() );
4764 break;
4765
4766 case T_uuid:
4767 NeedSYMBOL();
4768 const_cast<KIID&>( bezier->m_Uuid ) = parseKIID();
4769 NeedRIGHT();
4770 break;
4771
4772 case T_locked:
4773 bezier->SetLocked( parseBool() );
4774 NeedRIGHT();
4775 break;
4776
4777 default:
4778 Expecting( "pts, stroke, fill, uuid or locked" );
4779 }
4780 }
4781
4782 bezier->RebuildBezierToSegmentsPointsList( m_maxError );
4783
4784 return bezier.release();
4785}
4786
4787
4788void SCH_IO_KICAD_SEXPR_PARSER::parseEllipseBody( SCH_SHAPE* aShape, bool aIsArc, bool aIsSchematic,
4789 TSCHEMATIC_T::T aFirstTok )
4790{
4791 // Defaults for optional/missing fields. All fields are expected in a well-formed
4792 // file, but we fall back to safe defaults rather than rejecting on missing fields
4793 // to be forward-compatible with future format additions.
4794 VECTOR2I center( 0, 0 );
4795 int majorRadius = 1;
4796 int minorRadius = 1;
4797 EDA_ANGLE rotation = ANGLE_0;
4798 EDA_ANGLE startAngle = ANGLE_0;
4799 EDA_ANGLE endAngle = ANGLE_90;
4801 FILL_PARAMS fill;
4802
4803 for( T token = aFirstTok; token != T_RIGHT; token = NextTok() )
4804 {
4805 if( token != T_LEFT )
4806 Expecting( T_LEFT );
4807
4808 token = NextTok();
4809
4810 switch( token )
4811 {
4812 case T_center:
4813 // symbol parsers called parseXY( true ); sch parsers used the default.
4814 center = parseXY( !aIsSchematic );
4815 NeedRIGHT();
4816 break;
4817
4818 case T_major_radius:
4819 majorRadius = parseInternalUnits( "major radius" );
4820 NeedRIGHT();
4821 break;
4822
4823 case T_minor_radius:
4824 minorRadius = parseInternalUnits( "minor radius" );
4825 NeedRIGHT();
4826 break;
4827
4828 case T_rotation_angle:
4829 rotation = EDA_ANGLE( parseDouble( "rotation angle" ), DEGREES_T );
4830 NeedRIGHT();
4831 break;
4832
4833 case T_start_angle:
4834 if( !aIsArc )
4835 Expecting( "start_angle only valid inside ellipse_arc" );
4836 startAngle = EDA_ANGLE( parseDouble( "start angle" ), DEGREES_T );
4837 NeedRIGHT();
4838 break;
4839
4840 case T_end_angle:
4841 if( !aIsArc )
4842 Expecting( "end_angle only valid inside ellipse_arc" );
4843 endAngle = EDA_ANGLE( parseDouble( "end angle" ), DEGREES_T );
4844 NeedRIGHT();
4845 break;
4846
4847 case T_stroke:
4848 parseStroke( stroke );
4849 aShape->SetStroke( stroke );
4850 break;
4851
4852 case T_fill:
4853 parseFill( fill );
4854 aShape->SetFillMode( fill.m_FillType );
4855 aShape->SetFillColor( fill.m_Color );
4856 if( aIsSchematic )
4857 fixupSchFillMode( aShape );
4858 break;
4859
4860 case T_uuid:
4861 if( !aIsSchematic )
4862 Expecting( "uuid only valid in schematic context" );
4863 NeedSYMBOL();
4864 const_cast<KIID&>( aShape->m_Uuid ) = KIID( FromUTF8() );
4865 NeedRIGHT();
4866 break;
4867
4868 case T_locked:
4869 if( !aIsSchematic )
4870 Expecting( "locked only valid in schematic context" );
4871 aShape->SetLocked( parseBool() );
4872 NeedRIGHT();
4873 break;
4874
4875 default:
4876 Expecting( "center, major_radius, minor_radius, rotation_angle, "
4877 "start_angle, end_angle, stroke, fill, uuid, or locked" );
4878 }
4879 }
4880
4881 aShape->SetEllipseCenter( center );
4882 aShape->SetEllipseMajorRadius( majorRadius );
4883 aShape->SetEllipseMinorRadius( minorRadius );
4884 aShape->SetEllipseRotation( rotation );
4885
4886 if( aIsArc )
4887 {
4888 aShape->SetEllipseStartAngle( startAngle );
4889 aShape->SetEllipseEndAngle( endAngle );
4890 }
4891}
4892
4893
4895{
4896 wxCHECK_MSG( CurTok() == T_ellipse, nullptr,
4897 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an ellipse." ) );
4898
4899 auto ellipse = std::make_unique<SCH_SHAPE>( SHAPE_T::ELLIPSE );
4900 parseEllipseBody( ellipse.get(), /*isArc*/ false, /*isSchematic*/ true, NextTok() );
4901 return ellipse.release();
4902}
4903
4904
4906{
4907 wxCHECK_MSG( CurTok() == T_ellipse_arc, nullptr,
4908 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an elliptical arc." ) );
4909
4910 auto arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ELLIPSE_ARC );
4911 parseEllipseBody( arc.get(), /*isArc*/ true, /*isSchematic*/ true, NextTok() );
4912 return arc.release();
4913}
4914
4915
4917{
4918 T token;
4919 std::unique_ptr<SCH_TEXT> text;
4920
4921 switch( CurTok() )
4922 {
4923 case T_text: text = std::make_unique<SCH_TEXT>(); break;
4924 case T_label: text = std::make_unique<SCH_LABEL>(); break;
4925 case T_global_label: text = std::make_unique<SCH_GLOBALLABEL>(); break;
4926 case T_hierarchical_label: text = std::make_unique<SCH_HIERLABEL>(); break;
4927 case T_netclass_flag: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
4928 case T_directive_label: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
4929 default:
4930 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as text." );
4931 }
4932
4933 // We'll reset this if we find a fields_autoplaced token
4934 text->SetFieldsAutoplaced( AUTOPLACE_NONE );
4935
4936 NeedSYMBOL();
4937
4938 text->SetText( FromUTF8() );
4939
4940 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4941 {
4942 if( token != T_LEFT )
4943 Expecting( T_LEFT );
4944
4945 token = NextTok();
4946
4947 switch( token )
4948 {
4949 case T_exclude_from_sim:
4950 text->SetExcludedFromSim( parseBool() );
4951 NeedRIGHT();
4952 break;
4953
4954 case T_at:
4955 text->SetPosition( parseXY() );
4956 text->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ).KeepUpright() );
4957
4958 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() ) )
4959 {
4960 switch( static_cast<int>( label->GetTextAngle().AsDegrees() ) )
4961 {
4962 case 0: label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
4963 case 90: label->SetSpinStyle( SPIN_STYLE::UP ); break;
4964 case 180: label->SetSpinStyle( SPIN_STYLE::LEFT ); break;
4965 case 270: label->SetSpinStyle( SPIN_STYLE::BOTTOM ); break;
4966 default: wxFAIL; label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
4967 }
4968 }
4969
4970 NeedRIGHT();
4971 break;
4972
4973 case T_shape:
4974 {
4975 if( text->Type() == SCH_TEXT_T || text->Type() == SCH_LABEL_T )
4976 Unexpected( T_shape );
4977
4978 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( text.get() );
4979
4980 token = NextTok();
4981
4982 switch( token )
4983 {
4984 case T_input: label->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
4985 case T_output: label->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
4986 case T_bidirectional: label->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
4987 case T_tri_state: label->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE ); break;
4988 case T_passive: label->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
4989 case T_dot: label->SetShape( LABEL_FLAG_SHAPE::F_DOT ); break;
4990 case T_round: label->SetShape( LABEL_FLAG_SHAPE::F_ROUND ); break;
4991 case T_diamond: label->SetShape( LABEL_FLAG_SHAPE::F_DIAMOND ); break;
4992 case T_rectangle: label->SetShape( LABEL_FLAG_SHAPE::F_RECTANGLE ); break;
4993 default:
4994 Expecting( "input, output, bidirectional, tri_state, passive, dot, round, diamond"
4995 "or rectangle" );
4996 }
4997
4998 NeedRIGHT();
4999 break;
5000 }
5001
5002 case T_length:
5003 {
5004 if( text->Type() != SCH_DIRECTIVE_LABEL_T )
5005 Unexpected( T_length );
5006
5007 SCH_DIRECTIVE_LABEL* label = static_cast<SCH_DIRECTIVE_LABEL*>( text.get() );
5008
5009 label->SetPinLength( parseInternalUnits( "pin length" ) );
5010 NeedRIGHT();
5011 }
5012 break;
5013
5014 case T_fields_autoplaced:
5015 if( parseMaybeAbsentBool( true ) )
5016 text->SetFieldsAutoplaced( AUTOPLACE_AUTO );
5017
5018 break;
5019
5020 case T_effects:
5021 parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ), true );
5022
5023 // Hidden schematic text is no longer supported
5024 text->SetVisible( true );
5025 break;
5026
5027 case T_iref: // legacy format; current is a T_property (aka SCH_FIELD)
5028 if( text->Type() == SCH_GLOBAL_LABEL_T )
5029 {
5030 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
5031 SCH_FIELD* field = label->GetField( FIELD_T::INTERSHEET_REFS );
5032
5033 field->SetTextPos( parseXY() );
5034 NeedRIGHT();
5035
5036 field->SetVisible( true );
5037 }
5038 break;
5039
5040 case T_uuid:
5041 NeedSYMBOL();
5042 const_cast<KIID&>( text->m_Uuid ) = parseKIID();
5043 NeedRIGHT();
5044 break;
5045
5046 case T_property:
5047 {
5048 if( text->Type() == SCH_TEXT_T )
5049 Unexpected( T_property );
5050
5051 SCH_FIELD* field = parseSchField( text.get() );
5052
5053 // Intersheetrefs is a mandatory field and so will already exist
5054 if( text->Type() == SCH_GLOBAL_LABEL_T && field->IsMandatory() )
5055 {
5056 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
5057 *label->GetField( field->GetId() ) = *field;
5058 }
5059 else
5060 {
5061 static_cast<SCH_LABEL_BASE*>( text.get() )->GetFields().emplace_back( *field );
5062 }
5063
5064 delete field;
5065 break;
5066 }
5067
5068 case T_locked:
5069 text->SetLocked( parseBool() );
5070 NeedRIGHT();
5071 break;
5072
5073 default:
5074 Expecting( "at, shape, iref, uuid, effects or locked" );
5075 }
5076 }
5077
5078 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() );
5079
5080 if( label && label->GetFields().empty() )
5082
5083 return text.release();
5084}
5085
5086
5088{
5089 wxCHECK_MSG( CurTok() == T_text_box, nullptr,
5090 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text box." ) );
5091
5092 std::unique_ptr<SCH_TEXTBOX> textBox = std::make_unique<SCH_TEXTBOX>();
5093
5094 parseSchTextBoxContent( textBox.get() );
5095
5096 return textBox.release();
5097}
5098
5099
5101{
5102 wxCHECK_MSG( CurTok() == T_table_cell, nullptr,
5103 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table cell." ) );
5104
5105 std::unique_ptr<SCH_TABLECELL> cell = std::make_unique<SCH_TABLECELL>();
5106
5107 parseSchTextBoxContent( cell.get() );
5108
5109 return cell.release();
5110}
5111
5112
5114{
5115 T token;
5116 VECTOR2I pos;
5117 VECTOR2I end;
5118 VECTOR2I size;
5119 int left;
5120 int top;
5121 int right;
5122 int bottom;
5124 FILL_PARAMS fill;
5125 bool foundEnd = false;
5126 bool foundSize = false;
5127 bool foundMargins = false;
5128
5129 NeedSYMBOL();
5130
5131 aTextBox->SetText( FromUTF8() );
5132
5133 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5134 {
5135 if( token != T_LEFT )
5136 Expecting( T_LEFT );
5137
5138 token = NextTok();
5139
5140 switch( token )
5141 {
5142 case T_exclude_from_sim:
5143 aTextBox->SetExcludedFromSim( parseBool() );
5144 NeedRIGHT();
5145 break;
5146
5147 case T_start: // Legacy token during 6.99 development; fails to handle angle
5148 pos = parseXY();
5149 NeedRIGHT();
5150 break;
5151
5152 case T_end: // Legacy token during 6.99 development; fails to handle angle
5153 end = parseXY();
5154 foundEnd = true;
5155 NeedRIGHT();
5156 break;
5157
5158 case T_at:
5159 pos = parseXY();
5160 aTextBox->SetTextAngle( EDA_ANGLE( parseDouble( "textbox angle" ), DEGREES_T ) );
5161 NeedRIGHT();
5162 break;
5163
5164 case T_size:
5165 size = parseXY();
5166 foundSize = true;
5167 NeedRIGHT();
5168 break;
5169
5170 case T_span:
5171 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( aTextBox ) )
5172 {
5173 cell->SetColSpan( parseInt( "column span" ) );
5174 cell->SetRowSpan( parseInt( "row span" ) );
5175 }
5176 else
5177 {
5178 Expecting( "at, size, stroke, fill, effects or uuid" );
5179 }
5180
5181 NeedRIGHT();
5182 break;
5183
5184 case T_stroke:
5185 parseStroke( stroke );
5186 aTextBox->SetStroke( stroke );
5187 break;
5188
5189 case T_fill:
5190 parseFill( fill );
5191 aTextBox->SetFillMode( fill.m_FillType );
5192 aTextBox->SetFillColor( fill.m_Color );
5193 fixupSchFillMode( aTextBox );
5194 break;
5195
5196 case T_margins:
5197 parseMargins( left, top, right, bottom );
5198 aTextBox->SetMarginLeft( left );
5199 aTextBox->SetMarginTop( top );
5200 aTextBox->SetMarginRight( right );
5201 aTextBox->SetMarginBottom( bottom );
5202 foundMargins = true;
5203 NeedRIGHT();
5204 break;
5205
5206 case T_effects:
5207 parseEDA_TEXT( static_cast<EDA_TEXT*>( aTextBox ), false );
5208 break;
5209
5210 case T_uuid:
5211 NeedSYMBOL();
5212 const_cast<KIID&>( aTextBox->m_Uuid ) = parseKIID();
5213 NeedRIGHT();
5214 break;
5215
5216 case T_locked:
5217 aTextBox->SetLocked( parseBool() );
5218 NeedRIGHT();
5219 break;
5220
5221 default:
5222 if( dynamic_cast<SCH_TABLECELL*>( aTextBox ) != nullptr )
5223 Expecting( "at, size, stroke, fill, effects, span, uuid or locked" );
5224 else
5225 Expecting( "at, size, stroke, fill, effects, uuid or locked" );
5226 }
5227 }
5228
5229 aTextBox->SetPosition( pos );
5230
5231 if( foundEnd )
5232 aTextBox->SetEnd( end );
5233 else if( foundSize )
5234 aTextBox->SetEnd( pos + size );
5235 else
5236 Expecting( "size" );
5237
5238 if( !foundMargins )
5239 {
5240 int margin = aTextBox->GetLegacyTextMargin();
5241 aTextBox->SetMarginLeft( margin );
5242 aTextBox->SetMarginTop( margin );
5243 aTextBox->SetMarginRight( margin );
5244 aTextBox->SetMarginBottom( margin );
5245 }
5246}
5247
5248
5250{
5251 wxCHECK_MSG( CurTok() == T_table, nullptr,
5252 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table." ) );
5253
5254 T token;
5255 int defaultLineWidth = schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
5256 STROKE_PARAMS borderStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
5257 STROKE_PARAMS separatorsStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
5258 std::unique_ptr<SCH_TABLE> table = std::make_unique<SCH_TABLE>( defaultLineWidth );
5259
5260 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5261 {
5262 if( token != T_LEFT )
5263 Expecting( T_LEFT );
5264
5265 token = NextTok();
5266
5267 switch( token )
5268 {
5269 case T_column_count:
5270 table->SetColCount( parseInt( "column count" ) );
5271 NeedRIGHT();
5272 break;
5273
5274 case T_column_widths:
5275 {
5276 int col = 0;
5277
5278 while( ( token = NextTok() ) != T_RIGHT )
5279 table->SetColWidth( col++, parseInternalUnits() );
5280
5281 break;
5282 }
5283
5284 case T_row_heights:
5285 {
5286 int row = 0;
5287
5288 while( ( token = NextTok() ) != T_RIGHT )
5289 table->SetRowHeight( row++, parseInternalUnits() );
5290
5291 break;
5292 }
5293
5294 case T_cells:
5295 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5296 {
5297 if( token != T_LEFT )
5298 Expecting( T_LEFT );
5299
5300 token = NextTok();
5301
5302 if( token != T_table_cell )
5303 Expecting( "table_cell" );
5304
5305 table->AddCell( parseSchTableCell() );
5306 }
5307
5308 break;
5309
5310 case T_border:
5311 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5312 {
5313 if( token != T_LEFT )
5314 Expecting( T_LEFT );
5315
5316 token = NextTok();
5317
5318 switch( token )
5319 {
5320 case T_external:
5321 table->SetStrokeExternal( parseBool() );
5322 NeedRIGHT();
5323 break;
5324
5325 case T_header:
5326 table->SetStrokeHeaderSeparator( parseBool() );
5327 NeedRIGHT();
5328 break;
5329
5330 case T_stroke:
5331 parseStroke( borderStroke );
5332 table->SetBorderStroke( borderStroke );
5333 break;
5334
5335 default:
5336 Expecting( "external, header or stroke" );
5337 break;
5338 }
5339 }
5340
5341 break;
5342
5343 case T_separators:
5344 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5345 {
5346 if( token != T_LEFT )
5347 Expecting( T_LEFT );
5348
5349 token = NextTok();
5350
5351 switch( token )
5352 {
5353 case T_rows:
5354 table->SetStrokeRows( parseBool() );
5355 NeedRIGHT();
5356 break;
5357
5358 case T_cols:
5359 table->SetStrokeColumns( parseBool() );
5360 NeedRIGHT();
5361 break;
5362
5363 case T_stroke:
5364 parseStroke( separatorsStroke );
5365 table->SetSeparatorsStroke( separatorsStroke );
5366 break;
5367
5368 default:
5369 Expecting( "rows, cols, or stroke" );
5370 break;
5371 }
5372 }
5373
5374 break;
5375
5376 case T_uuid:
5377 NeedSYMBOL();
5378 const_cast<KIID&>( table->m_Uuid ) = parseKIID();
5379 NeedRIGHT();
5380 break;
5381
5382 case T_locked:
5383 table->SetLocked( parseBool() );
5384 NeedRIGHT();
5385 break;
5386
5387 default:
5388 Expecting( "columns, col_widths, row_heights, border, separators, uuid, locked, header or cells" );
5389 }
5390 }
5391
5392 if( !table->GetCell( 0, 0 ) )
5393 {
5394 THROW_PARSE_ERROR( _( "Invalid table: no cells defined" ), CurSource(), CurLine(), CurLineNumber(),
5395 CurOffset() );
5396 }
5397
5398 return table.release();
5399}
5400
5401
5403{
5404 wxCHECK_RET( CurTok() == T_bus_alias,
5405 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus alias." ) );
5406 wxCHECK( aScreen, /* void */ );
5407
5408 T token;
5409 std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>();
5410 wxString alias;
5411 wxString member;
5412
5413 NeedSYMBOL();
5414
5415 alias = FromUTF8();
5416
5417 if( m_requiredVersion < 20210621 )
5418 alias = ConvertToNewOverbarNotation( alias );
5419
5420 busAlias->SetName( alias );
5421
5422 NeedLEFT();
5423 token = NextTok();
5424
5425 if( token != T_members )
5426 Expecting( "members" );
5427
5428 token = NextTok();
5429
5430 while( token != T_RIGHT )
5431 {
5432 if( !IsSymbol( token ) )
5433 Expecting( "quoted string" );
5434
5435 member = FromUTF8();
5436
5437 if( m_requiredVersion < 20210621 )
5438 member = ConvertToNewOverbarNotation( member );
5439
5440 busAlias->AddMember( member );
5441
5442 token = NextTok();
5443 }
5444
5445 NeedRIGHT();
5446
5447 aScreen->AddBusAlias( busAlias );
5448}
5449
5450
5452{
5453 // (net_chain "name" [(from "ref" "pin")] [(to "ref" "pin")]
5454 // [(net_class "...")] [(color R G B A)] [(nets "n1" "n2" ...)])
5455 NeedSYMBOL();
5456 wxString name = FromUTF8();
5457
5458 wxString netClass;
5460 wxString fromRef, fromPin, toRef, toPin;
5461 std::set<wxString> memberNets;
5462
5463 for( T tok = NextTok(); tok != T_RIGHT; tok = NextTok() )
5464 {
5465 if( tok == T_LEFT )
5466 tok = NextTok();
5467
5468 if( tok == T_from )
5469 {
5470 NeedSYMBOLorNUMBER();
5471 fromRef = From_UTF8( CurText() );
5472 NeedSYMBOLorNUMBER();
5473 fromPin = From_UTF8( CurText() );
5474 NeedRIGHT();
5475 }
5476 else if( tok == T_to )
5477 {
5478 NeedSYMBOLorNUMBER();
5479 toRef = From_UTF8( CurText() );
5480 NeedSYMBOLorNUMBER();
5481 toPin = From_UTF8( CurText() );
5482 NeedRIGHT();
5483 }
5484 else if( tok == T_net_class )
5485 {
5486 NeedSYMBOLorNUMBER();
5487 netClass = FromUTF8();
5488 NeedRIGHT();
5489 }
5490 else if( tok == T_color )
5491 {
5492 int r = parseInt( "red" );
5493 int g = parseInt( "green" );
5494 int b = parseInt( "blue" );
5495 double al = parseDouble( "alpha" );
5496 color = COLOR4D( r / 255.0, g / 255.0, b / 255.0, al );
5497 NeedRIGHT();
5498 }
5499 else if( tok == T_nets )
5500 {
5501 for( T inner = NextTok(); inner != T_RIGHT; inner = NextTok() )
5502 {
5503 if( !IsSymbol( inner ) && inner != T_NUMBER )
5504 Expecting( "net name" );
5505
5506 memberNets.insert( FromUTF8() );
5507 }
5508 }
5509 else
5510 {
5511 // Skip unknown subsections
5512 int depth = 1;
5513
5514 while( depth > 0 )
5515 {
5516 T inner = NextTok();
5517
5518 if( inner == T_EOF )
5519 break;
5520
5521 if( inner == T_LEFT )
5522 ++depth;
5523 else if( inner == T_RIGHT )
5524 --depth;
5525 }
5526 }
5527 }
5528
5529 if( !fromRef.IsEmpty() && !toRef.IsEmpty() )
5530 {
5531 m_netChainTerminalRefs[name] = { { fromRef, fromPin }, { toRef, toPin } };
5532 }
5533
5534 if( !netClass.IsEmpty() )
5535 m_netChainNetClasses[name] = netClass;
5536
5537 if( color != KIGFX::COLOR4D::UNSPECIFIED )
5538 m_netChainColors[name] = color;
5539
5540 if( !memberNets.empty() )
5541 m_netChainMemberNets[name] = std::move( memberNets );
5542}
5543
5544
5546{
5547 T token;
5548
5549 while( ( token = NextTok() ) != T_RIGHT )
5550 {
5551 // This token is the Uuid of the item in the group.
5552 // Since groups are serialized at the end of the file/footprint, the Uuid should already
5553 // have been seen and exist in the board.
5554 KIID uuid( CurStr() );
5555 aGroupInfo.memberUuids.push_back( uuid );
5556 }
5557}
5558
5559
5561{
5562 wxCHECK_RET( CurTok() == T_group,
5563 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_GROUP." ) );
5564
5565 T token;
5566
5567 m_groupInfos.push_back( GROUP_INFO() );
5568 GROUP_INFO& groupInfo = m_groupInfos.back();
5569
5570 while( ( token = NextTok() ) != T_LEFT )
5571 {
5572 if( token == T_STRING )
5573 groupInfo.name = FromUTF8();
5574 else
5575 Expecting( "group name or locked" );
5576 }
5577
5578 for( ; token != T_RIGHT; token = NextTok() )
5579 {
5580 if( token != T_LEFT )
5581 Expecting( T_LEFT );
5582
5583 token = NextTok();
5584
5585 switch( token )
5586 {
5587 case T_uuid:
5588 NextTok();
5589 groupInfo.uuid = parseKIID();
5590 NeedRIGHT();
5591 break;
5592
5593 case T_lib_id:
5594 {
5595 token = NextTok();
5596
5597 if( !IsSymbol( token ) && token != T_NUMBER )
5598 Expecting( "symbol|number" );
5599
5600 LIB_ID libId;
5601 wxString name = FromUTF8();
5602 // Some symbol LIB_IDs have the '/' character escaped which can break
5603 // symbol links. The '/' character is no longer an illegal LIB_ID character so
5604 // it doesn't need to be escaped.
5605 name.Replace( "{slash}", "/" );
5606
5607 int bad_pos = groupInfo.libId.Parse( name );
5608
5609 if( bad_pos >= 0 )
5610 {
5611 if( static_cast<int>( name.size() ) > bad_pos )
5612 {
5613 wxString msg = wxString::Format( _( "Group library link %s contains invalid character '%c'" ), name,
5614 name[bad_pos] );
5615
5616 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
5617 }
5618
5619 THROW_PARSE_ERROR( _( "Invalid library ID" ), CurSource(), CurLine(), CurLineNumber(), CurOffset() );
5620 }
5621
5622 NeedRIGHT();
5623 break;
5624 }
5625
5626 case T_members:
5627 {
5628 parseGroupMembers( groupInfo );
5629 break;
5630 }
5631
5632 case T_locked:
5633 groupInfo.locked = parseBool();
5634 NeedRIGHT();
5635 break;
5636
5637 default:
5638 Expecting( "uuid, lib_id, members, locked" );
5639 }
5640 }
5641}
5642
5643
5645{
5646 if( !aParent )
5647 return;
5648
5649 auto getItem =
5650 [&]( const KIID& aId )
5651 {
5652 SCH_ITEM* aItem = nullptr;
5653
5654 for( SCH_ITEM* item : aParent->Items() )
5655 {
5656 if( item->m_Uuid == aId )
5657 {
5658 aItem = item;
5659 break;
5660 }
5661 }
5662
5663 return aItem;
5664 };
5665
5666 // Now that we've parsed the other Uuids in the file we can resolve the uuids referred
5667 // to in the group declarations we saw.
5668 //
5669 // First add all group objects so subsequent GetItem() calls for nested groups work.
5670 for( const GROUP_INFO& groupInfo : m_groupInfos )
5671 {
5672 SCH_GROUP* group = nullptr;
5673
5674 group = new SCH_GROUP( aParent );
5675 group->SetName( groupInfo.name );
5676
5677 const_cast<KIID&>( group->m_Uuid ) = groupInfo.uuid;
5678
5679 if( groupInfo.libId.IsValid() )
5680 group->SetDesignBlockLibId( groupInfo.libId );
5681
5682 group->SetLocked( groupInfo.locked );
5683
5684 aParent->Append( group );
5685 }
5686
5687 for( const GROUP_INFO& groupInfo : m_groupInfos )
5688 {
5689 SCH_GROUP* group = static_cast<SCH_GROUP*>( getItem( groupInfo.uuid ) );
5690
5691 if( group && group->Type() == SCH_GROUP_T )
5692 {
5693 for( const KIID& aUuid : groupInfo.memberUuids )
5694 {
5695 if( SCH_ITEM* gItem = getItem( aUuid ) )
5696 group->AddItem( gItem );
5697 }
5698 }
5699 }
5700
5701 aParent->GroupsSanityCheck( true );
5702}
5703
5704
5706{
5707 // Skip tokens until we exit the current S-expression block.
5708 // This is used for error recovery when parsing fails mid-symbol.
5709 while( aDepth > 0 )
5710 {
5711 T token = NextTok();
5712
5713 if( token == T_EOF )
5714 break;
5715 else if( token == T_LEFT )
5716 aDepth++;
5717 else if( token == T_RIGHT )
5718 aDepth--;
5719 }
5720}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:127
constexpr double ARC_LOW_DEF_MM
Definition base_units.h:131
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
void SetContentModified(bool aModified=true)
Definition base_screen.h:59
int GetPPI() const
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:402
EDA_ANGLE Normalize()
Definition eda_angle.h:229
const KIID m_Uuid
Definition eda_item.h:528
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
EDA_ITEM * GetParent() const
Definition eda_item.h:114
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:93
void SetEllipseRotation(const EDA_ANGLE &aA)
Definition eda_shape.h:308
void SetEllipseCenter(const VECTOR2I &aPt)
Definition eda_shape.h:281
FILL_T GetFillMode() const
Definition eda_shape.h:162
void SetEllipseStartAngle(const EDA_ANGLE &aA)
Definition eda_shape.h:318
SHAPE_POLY_SET & GetPolyShape()
void SetEllipseEndAngle(const EDA_ANGLE &aA)
Definition eda_shape.h:327
void SetFillColor(const COLOR4D &aColor)
Definition eda_shape.h:174
int GetPointCount() const
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:240
void SetEllipseMajorRadius(int aR)
Definition eda_shape.h:290
void SetFillMode(FILL_T aFill)
void SetEllipseMinorRadius(int aR)
Definition eda_shape.h:299
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:93
void SetTextColor(const COLOR4D &aColor)
Definition eda_text.h:294
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:532
void SetUnresolvedFontName(const wxString &aFontName)
Definition eda_text.h:274
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:114
void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:576
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:416
void SetBoldFlag(bool aBold)
Set only the bold flag, without changing the font.
Definition eda_text.cpp:377
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:385
void SetLineSpacing(double aLineSpacing)
Definition eda_text.cpp:524
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:283
static bool ValidateHyperlink(const wxString &aURL)
Check if aURL is a valid hyperlink.
void SetItalicFlag(bool aItalic)
Set only the italic flag, without changing the font.
Definition eda_text.cpp:326
void SetHyperlink(wxString aLink)
Definition eda_text.h:429
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:269
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:298
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:408
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.
Simple container to manage fill parameters.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
double r
Red component.
Definition color4d.h:393
double g
Green component.
Definition color4d.h:394
double a
Alpha component.
Definition color4d.h:396
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:402
double b
Blue component.
Definition color4d.h:395
Definition kiid.h:48
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:52
Define a library symbol object.
Definition lib_symbol.h:83
wxString GetName() const override
Definition lib_symbol.h:145
void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction, RECURSE_MODE aMode) override
EMBEDDED_FILES * GetEmbeddedFiles() override
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition richio.h:66
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:79
void SetPortrait(bool aIsPortrait)
Rotate the paper page 90 degrees.
bool SetType(PAGE_SIZE_TYPE aPageSize, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
void SetWidthMM(double aWidthInMM)
Definition page_info.h:140
void SetHeightMM(double aHeightInMM)
Definition page_info.h:145
const PAGE_SIZE_TYPE & GetType() const
Definition page_info.h:102
A progress reporter interface for use in multi-threaded environments.
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.
const BITMAP_BASE & GetImage() const
Get the underlying image.
double GetImageScale() const
void SetImageScale(double aScale)
Set the image "zoom" value.
Holds all the data relating to one schematic.
Definition schematic.h:89
Object to handle a bitmap image that can be inserted in a schematic.
Definition sch_bitmap.h:40
Class for a wire to bus entry.
void SetPinLength(int aLength)
Definition sch_label.h:486
bool IsMandatory() const
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:126
FIELD_T GetId() const
Definition sch_field.h:130
wxString GetCanonicalName() const
Get a non-language-specific name for a field which can be used for storage, variable look-up,...
void setId(FIELD_T aId)
wxString GetName(bool aUseDefaultName=true) const
Return the field name (not translated).
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this label.
A set of SCH_ITEMs (i.e., without duplicates).
Definition sch_group.h:52
bool m_appending
Appending load status.
SCH_FIELD * parseSchField(SCH_ITEM *aParent)
void parseSchSymbolInstances(SCH_SCREEN *aScreen)
void parsePinNumbers(std::unique_ptr< LIB_SYMBOL > &aSymbol)
LIB_SYMBOL * parseLibSymbol(LIB_SYMBOL_MAP &aSymbolLibMap)
void parseEllipseBody(SCH_SHAPE *aShape, bool aIsArc, bool aIsSchematic, TSCHEMATIC_T::T aFirstTok)
void parseBusAlias(SCH_SCREEN *aScreen)
void parseTITLE_BLOCK(TITLE_BLOCK &aTitleBlock)
void parseGroupMembers(GROUP_INFO &aGroupInfo)
void parseStroke(STROKE_PARAMS &aStroke)
Parse stroke definition aStroke.
int m_unit
The current unit being parsed.
std::map< wxString, std::set< wxString > > m_netChainMemberNets
void parseEDA_TEXT(EDA_TEXT *aText, bool aConvertOverbarSyntax, bool aEnforceMinTextSize=true)
void resolveGroups(SCH_SCREEN *aParent)
std::map< wxString, wxString > m_netChainNetClasses
SCH_SHEET * m_rootSheet
The rootsheet for full project loads or null for importing a schematic.
void ParseLib(LIB_SYMBOL_MAP &aSymbolLibMap)
std::vector< GROUP_INFO > m_groupInfos
void parseHeader(TSCHEMATIC_T::T aHeaderType, int aFileVersion)
void parseBodyStyles(std::unique_ptr< LIB_SYMBOL > &aSymbol)
void parseMargins(int &aLeft, int &aTop, int &aRight, int &aBottom)
wxString m_symbolName
The current symbol name.
std::map< wxString, CHAIN_TERMINALS > m_netChainTerminalRefs
VECTOR2I parseXY(bool aInvertY=false)
void parsePinNames(std::unique_ptr< LIB_SYMBOL > &aSymbol)
std::vector< wxString > m_parseWarnings
Non-fatal warnings collected during parsing.
void ParseSchematic(SCH_SHEET *aSheet, bool aIsCopyablyOnly=false, int aFileVersion=SEXPR_SCHEMATIC_FILE_VERSION)
Parse the internal LINE_READER object into aSheet.
SCH_SHEET_PIN * parseSchSheetPin(SCH_SHEET *aSheet)
int m_bodyStyle
The current body style being parsed.
std::map< wxString, COLOR4D > m_netChainColors
SCH_FIELD * parseProperty(std::unique_ptr< LIB_SYMBOL > &aSymbol)
void parseSchTextBoxContent(SCH_TEXTBOX *aTextBox)
bool parseMaybeAbsentBool(bool aDefaultValue)
Parses a boolean flag inside a list that existed before boolean normalization.
void parsePAGE_INFO(PAGE_INFO &aPageInfo)
int m_requiredVersion
Set to the symbol library file version required.
int m_maxError
Max deviation allowed when approximating bezier curves.
LIB_SYMBOL * ParseSymbol(LIB_SYMBOL_MAP &aSymbolLibMap, int aFileVersion=SEXPR_SYMBOL_LIB_FILE_VERSION)
Parse internal LINE_READER object into symbols and return all found.
void skipToBlockEnd(int aDepth=1)
Skip tokens until we reach the end of the current S-expression block.
void parseSchSheetInstances(SCH_SHEET *aRootSheet, SCH_SCREEN *aScreen)
SCH_IO_KICAD_SEXPR_PARSER(LINE_READER *aLineReader=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr, unsigned aLineCount=0, SCH_SHEET *aRootSheet=nullptr, bool aIsAppending=false)
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:168
void SetLocked(bool aLocked) override
Definition sch_item.h:257
bool IsLocked() const override
Definition sch_item.cpp:152
void SetFieldsAutoplaced(AUTOPLACE_ALGO aAlgo)
Definition sch_item.h:630
void SetShape(LABEL_FLAG_SHAPE aShape)
Definition sch_label.h:183
std::vector< SCH_FIELD > & GetFields()
Definition sch_label.h:214
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:42
void SetStartPoint(const VECTOR2I &aPosition)
Definition sch_line.h:140
virtual void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_line.h:202
void SetEndPoint(const VECTOR2I &aPosition)
Definition sch_line.h:149
void SetFileFormatVersionAtLoad(int aVersion)
Definition sch_screen.h:138
std::vector< SCH_SHEET_INSTANCE > m_sheetInstances
Definition sch_screen.h:728
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition sch_screen.h:168
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
void AddLibSymbol(LIB_SYMBOL *aLibSymbol)
Add aLibSymbol to the library symbol map.
void AddBusAlias(std::shared_ptr< BUS_ALIAS > aAlias)
Add a bus alias definition.
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition sch_screen.h:142
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:119
const KIID & GetUuid() const
Definition sch_screen.h:533
void UpdateLocalLibSymbolLinks()
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in this schematic with the local projec...
void SetLegacySymbolInstanceData()
Update the symbol value and footprint instance data for legacy designs.
SCHEMATIC * Schematic() const
void FixupEmbeddedData()
After loading a file from disk, the library symbols do not yet contain the full data for their embedd...
int GetFileFormatVersionAtLoad() const
Definition sch_screen.h:139
wxString GroupsSanityCheck(bool repair=false)
Consistency check of internal m_groups structure.
KIID m_uuid
A unique identifier for each schematic file.
Definition sch_screen.h:736
std::vector< SCH_SYMBOL_INSTANCE > m_symbolInstances
The list of symbol instances loaded from the schematic file.
Definition sch_screen.h:727
void SetPosition(const VECTOR2I &aPos) override
Definition sch_shape.h:89
void SetStroke(const STROKE_PARAMS &aStroke) override
STROKE_PARAMS GetStroke() const override
Definition sch_shape.h:61
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
void SetPageNumber(const wxString &aPageNumber)
Set the sheet instance user definable page number.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Variant information for a schematic sheet.
void InitializeAttributes(const SCH_SHEET &aSheet)
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:143
Variant information for a schematic symbol.
void InitializeAttributes(const SCH_SYMBOL &aSymbol)
Schematic symbol object.
Definition sch_symbol.h:76
void SetMarginBottom(int aBottom)
Definition sch_textbox.h:71
int GetLegacyTextMargin() const
void SetMarginLeft(int aLeft)
Definition sch_textbox.h:68
void SetMarginRight(int aRight)
Definition sch_textbox.h:70
void SetExcludedFromSim(bool aExclude, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
void SetMarginTop(int aTop)
Definition sch_textbox.h:69
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.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
void ParseStroke(STROKE_PARAMS &aStroke)
Simple container to manage line stroke parameters.
int GetWidth() const
void SetLineStyle(LINE_STYLE aLineStyle)
LINE_STYLE GetLineStyle() const
KIGFX::COLOR4D GetColor() const
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition title_block.h:41
void SetRevision(const wxString &aRevision)
Definition title_block.h:81
void SetComment(int aIdx, const wxString &aComment)
void SetTitle(const wxString &aTitle)
Definition title_block.h:58
void SetCompany(const wxString &aCompany)
Definition title_block.h:91
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition title_block.h:71
for transforming drawing coordinates for a wxDC device context.
Definition transform.h:46
wxString m_Name
bool m_ExcludedFromBOM
std::map< wxString, wxString > m_Fields
bool m_ExcludedFromPosFiles
bool m_ExcludedFromSim
bool m_ExcludedFromBoard
#define DEFAULT_LINE_WIDTH_MILS
The default wire width in mils. (can be changed in preference menu)
@ DSN_LEFT
Definition dsnlexer.h:67
@ DSN_RIGHT
Definition dsnlexer.h:66
@ DSN_STRING
Definition dsnlexer.h:68
@ DSN_EOF
Definition dsnlexer.h:69
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
@ TENTHS_OF_A_DEGREE_T
Definition eda_angle.h:30
@ DEGREES_T
Definition eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:417
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:415
@ NO_RECURSE
Definition eda_item.h:54
@ ELLIPSE
Definition eda_shape.h:56
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:51
@ ELLIPSE_ARC
Definition eda_shape.h:57
@ FILLED_WITH_COLOR
Definition eda_shape.h:67
@ NO_FILL
Definition eda_shape.h:64
@ REVERSE_HATCH
Definition eda_shape.h:69
@ HATCH
Definition eda_shape.h:68
@ FILLED_WITH_BG_BODYCOLOR
Definition eda_shape.h:66
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:65
@ CROSS_HATCH
Definition eda_shape.h:70
FONTCONFIG * Fontconfig()
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
KIID & NilUuid()
Definition kiid.cpp:68
@ LAYER_DEVICE
Definition layer_ids.h:468
@ LAYER_WIRE
Definition layer_ids.h:454
@ LAYER_NOTES
Definition layer_ids.h:469
@ LAYER_BUS
Definition layer_ids.h:455
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:83
#define MAX_PAGE_SIZE_EESCHEMA_MM
Definition page_info.h:41
#define MIN_PAGE_SIZE_MM
Min and max page sizes for clamping, in mm.
Definition page_info.h:39
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition pin_type.h:36
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:37
@ PT_NC
not connected (must be left open)
Definition pin_type.h:50
@ PT_OUTPUT
usual output
Definition pin_type.h:38
@ PT_TRISTATE
tri state bus pin
Definition pin_type.h:40
@ PT_NIC
not internally connected (may be connected to anything)
Definition pin_type.h:44
@ PT_BIDI
input or output (like port for a microprocessor)
Definition pin_type.h:39
@ PT_OPENEMITTER
pin type open emitter
Definition pin_type.h:49
@ PT_POWER_OUT
output of a regulator: intended to be connected to power input pins
Definition pin_type.h:47
@ PT_OPENCOLLECTOR
pin type open collector
Definition pin_type.h:48
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:46
@ PT_UNSPECIFIED
unknown electrical properties: creates always a warning when connected
Definition pin_type.h:45
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
Definition pin_type.h:43
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:127
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:111
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:118
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:135
GRAPHIC_PINSHAPE
Definition pin_type.h:84
const SCH_FIELD * FindField(const std::vector< SCH_FIELD > &aFields, FIELD_T aFieldId)
Definition sch_field.h:375
#define SEXPR_SYMBOL_LIB_FILE_VERSION
This file contains the file format version information for the s-expression schematic and symbol libr...
#define SEXPR_SCHEMATIC_FILE_VERSION
Schematic file version.
Class to handle a set of SCH_ITEMs.
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 ...
void fixupSchFillMode(SCH_SHAPE *aShape)
@ AUTOPLACE_NONE
Definition sch_item.h:70
@ AUTOPLACE_AUTO
Definition sch_item.h:71
@ L_BIDI
Definition sch_label.h:104
@ L_TRISTATE
Definition sch_label.h:105
@ L_UNSPECIFIED
Definition sch_label.h:106
@ F_DOT
Definition sch_label.h:109
@ F_ROUND
Definition sch_label.h:110
@ L_OUTPUT
Definition sch_label.h:103
@ F_DIAMOND
Definition sch_label.h:111
@ L_INPUT
Definition sch_label.h:102
@ F_RECTANGLE
Definition sch_label.h:112
#define SIM_LEGACY_ENABLE_FIELD
Definition sim_model.h:64
#define SIM_LEGACY_ENABLE_FIELD_V7
Definition sim_model.h:60
const int scale
wxString ConvertToNewOverbarNotation(const wxString &aOldStr)
Convert the old ~...~ overbar notation to the new ~{...} one.
wxString UnescapeString(const wxString &aSource)
wxString From_UTF8(const char *cstring)
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
A filename or source description, a problem input line, a line number, a byte offset,...
wxString m_Name
Definition sch_pin.h:45
GRAPHIC_PINSHAPE m_Shape
Definition sch_pin.h:46
ELECTRICAL_PINTYPE m_Type
Definition sch_pin.h:47
A simple container for sheet instance information.
std::map< wxString, SCH_SHEET_VARIANT > m_Variants
A list of sheet variants.
A simple container for schematic symbol instance information.
std::map< wxString, SCH_SYMBOL_VARIANT > m_Variants
A list of symbol variants.
@ SYM_MIRROR_Y
Definition symbol.h:44
@ SYM_MIRROR_X
Definition symbol.h:43
std::map< wxString, LIB_SYMBOL *, LibSymbolMapSort > LIB_SYMBOL_MAP
#define MANDATORY_FIELDS
FIELD_T
The set of all field indices assuming an array like sequence that a SCH_COMPONENT or LIB_PART can hol...
@ USER
The field ID hasn't been set yet; field is invalid.
@ INTERSHEET_REFS
Global label cross-reference page numbers.
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
#define SHEET_MANDATORY_FIELDS
#define GLOBALLABEL_MANDATORY_FIELDS
wxString GetCanonicalFieldName(FIELD_T aFieldType)
KIBIS top(path, &reporter)
KIBIS_PIN * pin
std::vector< std::vector< std::string > > table
VECTOR2I center
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
static constexpr double IU_PER_MM
Mock up a conversion function.
@ 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
const VECTOR2I CalcArcCenter(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Determine the center of an arc or circle given three points on its circumference.
Definition trigo.cpp:534
@ SCH_GROUP_T
Definition typeinfo.h:174
@ SCH_SYMBOL_T
Definition typeinfo.h:173
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:172
@ SCH_LABEL_T
Definition typeinfo.h:168
@ SCH_SHEET_T
Definition typeinfo.h:176
@ SCH_TEXT_T
Definition typeinfo.h:152
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:169
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687