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