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