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