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