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