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