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