KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_io_kicad_sexpr_parser.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2020 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Wayne Stambaugh <[email protected]>
8 *
9 * This program is free software: you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
26
27// For some reason wxWidgets is built with wxUSE_BASE64 unset so expose the wxWidgets
28// base64 code.
29#include <charconv>
30
31#include <fmt/format.h>
32#define wxUSE_BASE64 1
33#include <wx/base64.h>
34#include <wx/log.h>
35#include <wx/mstream.h>
36#include <wx/tokenzr.h>
37
38#include <base_units.h>
39#include <bitmap_base.h>
41#include <lib_id.h>
42#include <sch_pin.h>
43#include <math/util.h> // KiROUND
44#include <font/font.h>
45#include <font/fontconfig.h>
46#include <pgm_base.h>
47#include <string_utils.h>
48#include <sch_bitmap.h>
49#include <sch_bus_entry.h>
50#include <sch_symbol.h>
51#include <sch_netchain.h>
52#include <sch_edit_frame.h> // SYM_ORIENT_XXX
53#include <sch_field.h>
54#include <sch_group.h>
55#include <sch_line.h>
56#include <sch_rule_area.h>
57#include <sch_textbox.h>
58#include <sch_table.h>
59#include <sch_tablecell.h>
60#include <sch_label.h>
61#include <sch_junction.h>
62#include <sch_no_connect.h>
63#include <sch_screen.h>
64#include <sch_shape.h>
65#include <sch_sheet_pin.h>
68#include <template_fieldnames.h>
69#include <trigo.h>
70#include <progress_reporter.h>
71#include <sim/sim_model.h>
72
73
74using namespace TSCHEMATIC_T;
75
76
78 PROGRESS_REPORTER* aProgressReporter,
79 unsigned aLineCount, SCH_SHEET* aRootSheet,
80 bool aIsAppending ) :
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 else if( m_requiredVersion < 20260623 )
3802 {
3803 // Between the 300-PPI era and 20260623 the PPI was computed from the embedded
3804 // resolution but pixels/cm was truncated to an integer, so the stored scale
3805 // compensated for the wrong PPI. Re-scale to the corrected PPI to preserve size.
3806 const BITMAP_BASE& image = refImage.GetImage();
3807 int legacyPPI = image.GetLegacyPPI();
3808
3809 if( legacyPPI > 0 && image.GetPPI() != legacyPPI )
3810 refImage.SetImageScale( refImage.GetImageScale() * image.GetPPI() / legacyPPI );
3811 }
3812
3813 return bitmap.release();
3814}
3815
3816
3818{
3819 wxCHECK_MSG( CurTok() == T_sheet, nullptr,
3820 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a sheet." ) );
3821
3822 T token;
3824 FILL_PARAMS fill;
3825 SCH_FIELD* field;
3826 std::vector<SCH_FIELD> fields;
3827 std::unique_ptr<SCH_SHEET> sheet = std::make_unique<SCH_SHEET>();
3828 std::set<int> fieldIDsRead;
3829
3830 // We'll reset this if we find a fields_autoplaced token
3831 sheet->SetFieldsAutoplaced( AUTOPLACE_NONE );
3832
3833 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3834 {
3835 if( token != T_LEFT )
3836 Expecting( T_LEFT );
3837
3838 token = NextTok();
3839
3840 switch( token )
3841 {
3842 case T_at:
3843 sheet->SetPosition( parseXY() );
3844 NeedRIGHT();
3845 break;
3846
3847 case T_size:
3848 {
3849 VECTOR2I size;
3850 size.x = parseInternalUnits( "sheet width" );
3851 size.y = parseInternalUnits( "sheet height" );
3852 sheet->SetSize( size );
3853 NeedRIGHT();
3854 break;
3855 }
3856
3857 case T_exclude_from_sim:
3858 sheet->SetExcludedFromSim( parseBool() );
3859 NeedRIGHT();
3860 break;
3861
3862 case T_in_bom:
3863 sheet->SetExcludedFromBOM( !parseBool() );
3864 NeedRIGHT();
3865 break;
3866
3867 case T_on_board:
3868 sheet->SetExcludedFromBoard( !parseBool() );
3869 NeedRIGHT();
3870 break;
3871
3872 case T_dnp:
3873 sheet->SetDNP( parseBool() );
3874 NeedRIGHT();
3875 break;
3876
3877 case T_locked:
3878 sheet->SetLocked( parseBool() );
3879 NeedRIGHT();
3880 break;
3881
3882 case T_fields_autoplaced:
3883 if( parseMaybeAbsentBool( true ) )
3884 sheet->SetFieldsAutoplaced( AUTOPLACE_AUTO );
3885
3886 break;
3887
3888 case T_stroke:
3889 parseStroke( stroke );
3890 sheet->SetBorderWidth( stroke.GetWidth() );
3891 sheet->SetBorderColor( stroke.GetColor() );
3892 break;
3893
3894 case T_fill:
3895 parseFill( fill );
3896 sheet->SetBackgroundColor( fill.m_Color );
3897 break;
3898
3899 case T_uuid:
3900 NeedSYMBOL();
3901 const_cast<KIID&>( sheet->m_Uuid ) = parseKIID();
3902 NeedRIGHT();
3903 break;
3904
3905 case T_property:
3906 field = parseSchField( sheet.get() );
3907
3908 if( m_requiredVersion <= 20200310 )
3909 {
3910 // Earlier versions had the wrong ids (and names) saved for sheet fields.
3911 // Fortunately they only saved the sheetname and sheetfilepath (and always
3912 // in that order), so we can hack in a recovery.
3913 if( fields.empty() )
3914 field->setId( FIELD_T::SHEET_NAME );
3915 else
3917 }
3918
3919 fields.emplace_back( *field );
3920
3921 delete field;
3922 break;
3923
3924 case T_pin:
3925 sheet->AddPin( parseSchSheetPin( sheet.get() ) );
3926 break;
3927
3928 case T_instances:
3929 {
3930 std::vector<SCH_SHEET_INSTANCE> instances;
3931
3932 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3933 {
3934 if( token != T_LEFT )
3935 Expecting( T_LEFT );
3936
3937 token = NextTok();
3938
3939 if( token != T_project )
3940 Expecting( "project" );
3941
3942 NeedSYMBOL();
3943
3944 wxString projectName = FromUTF8();
3945
3946 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3947 {
3948 if( token != T_LEFT )
3949 Expecting( T_LEFT );
3950
3951 token = NextTok();
3952
3953 if( token != T_path )
3954 Expecting( "path" );
3955
3956 SCH_SHEET_INSTANCE instance;
3957
3958 instance.m_ProjectName = projectName;
3959
3960 NeedSYMBOL();
3961 instance.m_Path = KIID_PATH( FromUTF8() );
3962
3963 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3964 {
3965 if( token != T_LEFT )
3966 Expecting( T_LEFT );
3967
3968 token = NextTok();
3969
3970 switch( token )
3971 {
3972 case T_page:
3973 {
3974 NeedSYMBOL();
3975 instance.m_PageNumber = FromUTF8();
3976
3977 // Empty page numbers are not permitted
3978 if( instance.m_PageNumber.IsEmpty() )
3979 {
3980 // Use hash character instead
3981 instance.m_PageNumber = wxT( "#" );
3982 }
3983 else
3984 {
3985 // Whitespaces are not permitted
3986 static std::vector<wxString> whitespaces =
3987 { wxT( "\r" ),
3988 wxT( "\n" ),
3989 wxT( "\t" ),
3990 wxT( " " ) };
3991
3992 for( wxString ch : whitespaces )
3993 instance.m_PageNumber.Replace( ch, wxEmptyString );
3994 }
3995
3996 NeedRIGHT();
3997 break;
3998 }
3999
4000 case T_variant:
4001 {
4002 SCH_SHEET_VARIANT variant;
4003 variant.InitializeAttributes( *sheet );
4004
4005 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4006 {
4007 if( token != T_LEFT )
4008 Expecting( T_LEFT );
4009
4010 token = NextTok();
4011
4012 switch( token )
4013 {
4014 case T_name:
4015 NeedSYMBOL();
4016 variant.m_Name = FromUTF8();
4017 NeedRIGHT();
4018 break;
4019
4020 case T_dnp:
4021 variant.m_DNP = parseBool();
4022 NeedRIGHT();
4023 break;
4024
4025 case T_exclude_from_sim:
4026 variant.m_ExcludedFromSim = parseBool();
4027 NeedRIGHT();
4028 break;
4029
4030 case T_in_bom:
4031 variant.m_ExcludedFromBOM = parseBool();
4032
4033 // This fixes the incorrect logic from prior file versions. The "in_bom" token
4034 // used in the file format is the positive logic. However, in the UI the term
4035 // excluded from BOM is used which is the inverted logic.
4036 if( m_requiredVersion >= 20260306 )
4037 variant.m_ExcludedFromBOM = !variant.m_ExcludedFromBOM;
4038
4039 NeedRIGHT();
4040 break;
4041
4042 case T_on_board:
4043 variant.m_ExcludedFromBoard = !parseBool();
4044 NeedRIGHT();
4045 break;
4046
4047 case T_in_pos_files:
4048 variant.m_ExcludedFromPosFiles = !parseBool();
4049 NeedRIGHT();
4050 break;
4051
4052 case T_field:
4053 {
4054 wxString fieldName;
4055 wxString fieldValue;
4056
4057 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4058 {
4059 if( token != T_LEFT )
4060 Expecting( T_LEFT );
4061
4062 token = NextTok();
4063
4064 switch( token )
4065 {
4066 case T_name:
4067 NeedSYMBOL();
4068 fieldName = FromUTF8();
4069 NeedRIGHT();
4070 break;
4071
4072 case T_value:
4073 NeedSYMBOL();
4074 fieldValue = FromUTF8();
4075 NeedRIGHT();
4076 break;
4077
4078 default:
4079 Expecting( "name or value" );
4080 }
4081 }
4082
4083 variant.m_Fields[fieldName] = fieldValue;
4084 break;
4085 }
4086
4087 default:
4088 Expecting( "dnp, exclude_from_sim, field, in_bom, in_pos_files, name, or on_board" );
4089 }
4090
4091 instance.m_Variants[variant.m_Name] = variant;
4092 }
4093
4094 break;
4095 }
4096
4097 default:
4098 Expecting( "page or variant" );
4099 }
4100 }
4101
4102 instances.emplace_back( instance );
4103 }
4104 }
4105
4106 sheet->setInstances( instances );
4107 break;
4108 }
4109
4110 default:
4111 Expecting( "at, size, stroke, background, instances, uuid, property, or pin" );
4112 }
4113 }
4114
4115 sheet->SetFields( fields );
4116
4117 if( !FindField( sheet->GetFields(), FIELD_T::SHEET_NAME ) )
4118 {
4119 THROW_PARSE_ERROR( _( "Missing sheet name property" ), CurSource(), CurLine(),
4120 CurLineNumber(), CurOffset() );
4121 }
4122
4123 if( !FindField( sheet->GetFields(), FIELD_T::SHEET_FILENAME ) )
4124 {
4125 THROW_PARSE_ERROR( _( "Missing sheet file property" ), CurSource(), CurLine(),
4126 CurLineNumber(), CurOffset() );
4127 }
4128
4129 return sheet.release();
4130}
4131
4132
4134{
4135 wxCHECK_MSG( CurTok() == T_junction, nullptr,
4136 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a junction." ) );
4137
4138 T token;
4139 std::unique_ptr<SCH_JUNCTION> junction = std::make_unique<SCH_JUNCTION>();
4140
4141 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4142 {
4143 if( token != T_LEFT )
4144 Expecting( T_LEFT );
4145
4146 token = NextTok();
4147
4148 switch( token )
4149 {
4150 case T_at:
4151 junction->SetPosition( parseXY() );
4152 NeedRIGHT();
4153 break;
4154
4155 case T_diameter:
4156 junction->SetDiameter( parseInternalUnits( "junction diameter" ) );
4157 NeedRIGHT();
4158 break;
4159
4160 case T_color:
4161 {
4162 COLOR4D color;
4163
4164 color.r = parseInt( "red" ) / 255.0;
4165 color.g = parseInt( "green" ) / 255.0;
4166 color.b = parseInt( "blue" ) / 255.0;
4167 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
4168
4169 junction->SetColor( color );
4170 NeedRIGHT();
4171 break;
4172 }
4173
4174 case T_uuid:
4175 NeedSYMBOL();
4176 const_cast<KIID&>( junction->m_Uuid ) = parseKIID();
4177 NeedRIGHT();
4178 break;
4179
4180 case T_locked:
4181 junction->SetLocked( parseBool() );
4182 NeedRIGHT();
4183 break;
4184
4185 default:
4186 Expecting( "at, diameter, color, uuid or locked" );
4187 }
4188 }
4189
4190 return junction.release();
4191}
4192
4193
4195{
4196 wxCHECK_MSG( CurTok() == T_no_connect, nullptr,
4197 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a no connect." ) );
4198
4199 T token;
4200 std::unique_ptr<SCH_NO_CONNECT> no_connect = std::make_unique<SCH_NO_CONNECT>();
4201
4202 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4203 {
4204 if( token != T_LEFT )
4205 Expecting( T_LEFT );
4206
4207 token = NextTok();
4208
4209 switch( token )
4210 {
4211 case T_at:
4212 no_connect->SetPosition( parseXY() );
4213 NeedRIGHT();
4214 break;
4215
4216 case T_uuid:
4217 NeedSYMBOL();
4218 const_cast<KIID&>( no_connect->m_Uuid ) = parseKIID();
4219 NeedRIGHT();
4220 break;
4221
4222 case T_locked:
4223 no_connect->SetLocked( parseBool() );
4224 NeedRIGHT();
4225 break;
4226
4227 default:
4228 Expecting( "at, uuid or locked" );
4229 }
4230 }
4231
4232 return no_connect.release();
4233}
4234
4235
4237{
4238 wxCHECK_MSG( CurTok() == T_bus_entry, nullptr,
4239 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus entry." ) );
4240
4241 T token;
4243 std::unique_ptr<SCH_BUS_WIRE_ENTRY> busEntry = std::make_unique<SCH_BUS_WIRE_ENTRY>();
4244
4245 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4246 {
4247 if( token != T_LEFT )
4248 Expecting( T_LEFT );
4249
4250 token = NextTok();
4251
4252 switch( token )
4253 {
4254 case T_at:
4255 busEntry->SetPosition( parseXY() );
4256 NeedRIGHT();
4257 break;
4258
4259 case T_size:
4260 {
4261 VECTOR2I size;
4262
4263 size.x = parseInternalUnits( "bus entry height" );
4264 size.y = parseInternalUnits( "bus entry width" );
4265 busEntry->SetSize( size );
4266 NeedRIGHT();
4267 break;
4268 }
4269
4270 case T_stroke:
4271 parseStroke( stroke );
4272 busEntry->SetStroke( stroke );
4273 break;
4274
4275 case T_uuid:
4276 NeedSYMBOL();
4277 const_cast<KIID&>( busEntry->m_Uuid ) = parseKIID();
4278 NeedRIGHT();
4279 break;
4280
4281 case T_locked:
4282 busEntry->SetLocked( parseBool() );
4283 NeedRIGHT();
4284 break;
4285
4286 default:
4287 Expecting( "at, size, uuid, stroke or locked" );
4288 }
4289 }
4290
4291 return busEntry.release();
4292}
4293
4294
4296{
4297 if( aShape->GetFillMode() == FILL_T::FILLED_SHAPE )
4298 {
4299 aShape->SetFillColor( aShape->GetStroke().GetColor() );
4301 }
4302}
4303
4304
4306{
4307 T token;
4309 FILL_PARAMS fill;
4310
4311 std::unique_ptr<SCH_SHAPE> polyline = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_NOTES );
4312
4313 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4314 {
4315 if( token != T_LEFT )
4316 Expecting( T_LEFT );
4317
4318 token = NextTok();
4319
4320 switch( token )
4321 {
4322 case T_pts:
4323 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4324 {
4325 if( token != T_LEFT )
4326 Expecting( T_LEFT );
4327
4328 token = NextTok();
4329
4330 if( token != T_xy )
4331 Expecting( "xy" );
4332
4333 polyline->AddPoint( parseXY() );
4334
4335 NeedRIGHT();
4336 }
4337 break;
4338
4339 case T_stroke:
4340 parseStroke( stroke );
4341
4342 // In 6.0, the default schematic line style was Dashed.
4343 if( m_requiredVersion <= 20211123 && stroke.GetLineStyle() == LINE_STYLE::DEFAULT )
4345
4346 polyline->SetStroke( stroke );
4347 break;
4348
4349 case T_fill:
4350 parseFill( fill );
4351 polyline->SetFillMode( fill.m_FillType );
4352 polyline->SetFillColor( fill.m_Color );
4353 fixupSchFillMode( polyline.get() );
4354 break;
4355
4356 case T_uuid:
4357 NeedSYMBOL();
4358 const_cast<KIID&>( polyline->m_Uuid ) = parseKIID();
4359 NeedRIGHT();
4360 break;
4361
4362 case T_locked:
4363 polyline->SetLocked( parseBool() );
4364 NeedRIGHT();
4365 break;
4366
4367 default:
4368 Expecting( "pts, uuid, stroke, fill or locked" );
4369 }
4370 }
4371
4372 return polyline.release();
4373}
4374
4375
4377{
4378 // Note: T_polyline is deprecated in this code: it is now handled by
4379 // parseSchPolyLine() that can handle true polygons, and not only one segment.
4380
4381 T token;
4383 int layer;
4384
4385 switch( CurTok() )
4386 {
4387 case T_polyline: layer = LAYER_NOTES; break;
4388 case T_wire: layer = LAYER_WIRE; break;
4389 case T_bus: layer = LAYER_BUS; break;
4390 default:
4391 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as a line." );
4392 }
4393
4394 std::unique_ptr<SCH_LINE> line = std::make_unique<SCH_LINE>( VECTOR2I(), layer );
4395
4396 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4397 {
4398 if( token != T_LEFT )
4399 Expecting( T_LEFT );
4400
4401 token = NextTok();
4402
4403 switch( token )
4404 {
4405 case T_pts:
4406 NeedLEFT();
4407 token = NextTok();
4408
4409 if( token != T_xy )
4410 Expecting( "xy" );
4411
4412 line->SetStartPoint( parseXY() );
4413 NeedRIGHT();
4414 NeedLEFT();
4415 token = NextTok();
4416
4417 if( token != T_xy )
4418 Expecting( "xy" );
4419
4420 line->SetEndPoint( parseXY() );
4421 NeedRIGHT();
4422 NeedRIGHT();
4423 break;
4424
4425 case T_stroke:
4426 parseStroke( stroke );
4427 line->SetStroke( stroke );
4428 break;
4429
4430 case T_uuid:
4431 NeedSYMBOL();
4432 const_cast<KIID&>( line->m_Uuid ) = parseKIID();
4433 NeedRIGHT();
4434 break;
4435
4436 case T_locked:
4437 line->SetLocked( parseBool() );
4438 NeedRIGHT();
4439 break;
4440
4441 default:
4442 Expecting( "pts, uuid, stroke or locked" );
4443 }
4444 }
4445
4446 return line.release();
4447}
4448
4449
4451{
4452 wxCHECK_MSG( CurTok() == T_arc, nullptr,
4453 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc." ) );
4454
4455 T token;
4456 VECTOR2I startPoint;
4457 VECTOR2I midPoint;
4458 VECTOR2I endPoint;
4460 FILL_PARAMS fill;
4461 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC );
4462
4463 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4464 {
4465 if( token != T_LEFT )
4466 Expecting( T_LEFT );
4467
4468 token = NextTok();
4469
4470 switch( token )
4471 {
4472 case T_start:
4473 startPoint = parseXY();
4474 NeedRIGHT();
4475 break;
4476
4477 case T_mid:
4478 midPoint = parseXY();
4479 NeedRIGHT();
4480 break;
4481
4482 case T_end:
4483 endPoint = parseXY();
4484 NeedRIGHT();
4485 break;
4486
4487 case T_stroke:
4488 parseStroke( stroke );
4489 arc->SetStroke( stroke );
4490 break;
4491
4492 case T_fill:
4493 parseFill( fill );
4494 arc->SetFillMode( fill.m_FillType );
4495 arc->SetFillColor( fill.m_Color );
4496 fixupSchFillMode( arc.get() );
4497 break;
4498
4499 case T_uuid:
4500 NeedSYMBOL();
4501 const_cast<KIID&>( arc->m_Uuid ) = parseKIID();
4502 NeedRIGHT();
4503 break;
4504
4505 case T_locked:
4506 arc->SetLocked( parseBool() );
4507 NeedRIGHT();
4508 break;
4509
4510 default:
4511 Expecting( "start, mid, end, stroke, fill, uuid or locked" );
4512 }
4513 }
4514
4515 arc->SetArcGeometry( startPoint, midPoint, endPoint );
4516
4517 return arc.release();
4518}
4519
4520
4522{
4523 wxCHECK_MSG( CurTok() == T_circle, nullptr,
4524 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle." ) );
4525
4526 T token;
4528 int radius = 0;
4530 FILL_PARAMS fill;
4531 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE );
4532
4533 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4534 {
4535 if( token != T_LEFT )
4536 Expecting( T_LEFT );
4537
4538 token = NextTok();
4539
4540 switch( token )
4541 {
4542 case T_center:
4543 center = parseXY();
4544 NeedRIGHT();
4545 break;
4546
4547 case T_radius:
4548 radius = parseInternalUnits( "radius length" );
4549 NeedRIGHT();
4550 break;
4551
4552 case T_stroke:
4553 parseStroke( stroke );
4554 circle->SetStroke( stroke );
4555 break;
4556
4557 case T_fill:
4558 parseFill( fill );
4559 circle->SetFillMode( fill.m_FillType );
4560 circle->SetFillColor( fill.m_Color );
4561 fixupSchFillMode( circle.get() );
4562 break;
4563
4564 case T_uuid:
4565 NeedSYMBOL();
4566 const_cast<KIID&>( circle->m_Uuid ) = parseKIID();
4567 NeedRIGHT();
4568 break;
4569
4570 case T_locked:
4571 circle->SetLocked( parseBool() );
4572 NeedRIGHT();
4573 break;
4574
4575 default:
4576 Expecting( "center, radius, stroke, fill, uuid or locked" );
4577 }
4578 }
4579
4580 circle->SetCenter( center );
4581 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
4582
4583 return circle.release();
4584}
4585
4586
4588{
4589 wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
4590 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle." ) );
4591
4592 T token;
4594 FILL_PARAMS fill;
4595 std::unique_ptr<SCH_SHAPE> rectangle = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE );
4596
4597 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4598 {
4599 if( token != T_LEFT )
4600 Expecting( T_LEFT );
4601
4602 token = NextTok();
4603
4604 switch( token )
4605 {
4606 case T_start:
4607 rectangle->SetPosition( parseXY() );
4608 NeedRIGHT();
4609 break;
4610
4611 case T_end:
4612 rectangle->SetEnd( parseXY() );
4613 NeedRIGHT();
4614 break;
4615
4616 case T_radius:
4617 rectangle->SetCornerRadius( parseDouble( "corner radius" ) * schIUScale.IU_PER_MM );
4618 NeedRIGHT();
4619 break;
4620
4621 case T_stroke:
4622 parseStroke( stroke );
4623 rectangle->SetStroke( stroke );
4624 break;
4625
4626 case T_fill:
4627 parseFill( fill );
4628 rectangle->SetFillMode( fill.m_FillType );
4629 rectangle->SetFillColor( fill.m_Color );
4630 fixupSchFillMode( rectangle.get() );
4631 break;
4632
4633 case T_uuid:
4634 NeedSYMBOL();
4635 const_cast<KIID&>( rectangle->m_Uuid ) = parseKIID();
4636 NeedRIGHT();
4637 break;
4638
4639 case T_locked:
4640 rectangle->SetLocked( parseBool() );
4641 NeedRIGHT();
4642 break;
4643
4644 default:
4645 Expecting( "start, end, stroke, fill, uuid or locked" );
4646 }
4647 }
4648
4649 return rectangle.release();
4650}
4651
4652
4654{
4655 wxCHECK_MSG( CurTok() == T_rule_area, nullptr,
4656 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rule area." ) );
4657
4658 T token;
4660 FILL_PARAMS fill;
4661 std::unique_ptr<SCH_RULE_AREA> ruleArea = std::make_unique<SCH_RULE_AREA>();
4662
4663 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4664 {
4665 if( token != T_LEFT )
4666 Expecting( T_LEFT );
4667
4668 token = NextTok();
4669
4670 switch( token )
4671 {
4672 case T_polyline:
4673 {
4674 std::unique_ptr<SCH_SHAPE> poly( parseSchPolyLine() );
4675 SHAPE_POLY_SET& sch_rule_poly = poly->GetPolyShape();
4676
4677 // The polygon must be closed, it is a schematic closed polyline:
4678 sch_rule_poly.Outline(0).SetClosed( true );
4679
4680 ruleArea->SetPolyShape( sch_rule_poly );
4681
4682 ruleArea->SetStroke( poly->GetStroke() );
4683 ruleArea->SetFillMode( poly->GetFillMode() );
4684 ruleArea->SetFillColor( poly->GetFillColor() );
4685
4686 // the uuid is saved to the shape but stored and saved out of the rulearea
4687 const_cast<KIID&>( ruleArea->m_Uuid ) = poly->m_Uuid;
4688 break;
4689 }
4690
4691 case T_exclude_from_sim:
4692 ruleArea->SetExcludedFromSim( parseBool() );
4693 NeedRIGHT();
4694 break;
4695
4696 case T_in_bom:
4697 ruleArea->SetExcludedFromBOM( !parseBool() );
4698 NeedRIGHT();
4699 break;
4700
4701 case T_on_board:
4702 ruleArea->SetExcludedFromBoard( !parseBool() );
4703 NeedRIGHT();
4704 break;
4705
4706 case T_dnp:
4707 ruleArea->SetDNP( parseBool() );
4708 NeedRIGHT();
4709 break;
4710
4711 case T_locked:
4712 ruleArea->SetLocked( parseBool() );
4713 NeedRIGHT();
4714 break;
4715
4716 default:
4717 Expecting( "exclude_from_sim, on_board, in_bom, dnp, locked, or polyline" );
4718 }
4719 }
4720
4721 return ruleArea.release();
4722}
4723
4724
4726{
4727 wxCHECK_MSG( CurTok() == T_bezier, nullptr,
4728 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
4729
4730 T token;
4732 FILL_PARAMS fill;
4733 std::unique_ptr<SCH_SHAPE> bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER );
4734
4735 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4736 {
4737 if( token != T_LEFT )
4738 Expecting( T_LEFT );
4739
4740 token = NextTok();
4741
4742 switch( token )
4743 {
4744 case T_pts:
4745 {
4746 int ii = 0;
4747
4748 for( token = NextTok(); token != T_RIGHT; token = NextTok(), ++ii )
4749 {
4750 if( token != T_LEFT )
4751 Expecting( T_LEFT );
4752
4753 token = NextTok();
4754
4755 if( token != T_xy )
4756 Expecting( "xy" );
4757
4758 switch( ii )
4759 {
4760 case 0: bezier->SetStart( parseXY() ); break;
4761 case 1: bezier->SetBezierC1( parseXY() ); break;
4762 case 2: bezier->SetBezierC2( parseXY() ); break;
4763 case 3: bezier->SetEnd( parseXY() ); break;
4764 default: Unexpected( "control point" ); break;
4765 }
4766
4767 NeedRIGHT();
4768 }
4769 }
4770 break;
4771
4772 case T_stroke:
4773 parseStroke( stroke );
4774 bezier->SetStroke( stroke );
4775 break;
4776
4777 case T_fill:
4778 parseFill( fill );
4779 bezier->SetFillMode( fill.m_FillType );
4780 bezier->SetFillColor( fill.m_Color );
4781 fixupSchFillMode( bezier.get() );
4782 break;
4783
4784 case T_uuid:
4785 NeedSYMBOL();
4786 const_cast<KIID&>( bezier->m_Uuid ) = parseKIID();
4787 NeedRIGHT();
4788 break;
4789
4790 case T_locked:
4791 bezier->SetLocked( parseBool() );
4792 NeedRIGHT();
4793 break;
4794
4795 default:
4796 Expecting( "pts, stroke, fill, uuid or locked" );
4797 }
4798 }
4799
4800 bezier->RebuildBezierToSegmentsPointsList( m_maxError );
4801
4802 return bezier.release();
4803}
4804
4805
4806void SCH_IO_KICAD_SEXPR_PARSER::parseEllipseBody( SCH_SHAPE* aShape, bool aIsArc, bool aIsSchematic,
4807 TSCHEMATIC_T::T aFirstTok )
4808{
4809 // Defaults for optional/missing fields. All fields are expected in a well-formed
4810 // file, but we fall back to safe defaults rather than rejecting on missing fields
4811 // to be forward-compatible with future format additions.
4812 VECTOR2I center( 0, 0 );
4813 int majorRadius = 1;
4814 int minorRadius = 1;
4815 EDA_ANGLE rotation = ANGLE_0;
4816 EDA_ANGLE startAngle = ANGLE_0;
4817 EDA_ANGLE endAngle = ANGLE_90;
4819 FILL_PARAMS fill;
4820
4821 for( T token = aFirstTok; token != T_RIGHT; token = NextTok() )
4822 {
4823 if( token != T_LEFT )
4824 Expecting( T_LEFT );
4825
4826 token = NextTok();
4827
4828 switch( token )
4829 {
4830 case T_center:
4831 // symbol parsers called parseXY( true ); sch parsers used the default.
4832 center = parseXY( !aIsSchematic );
4833 NeedRIGHT();
4834 break;
4835
4836 case T_major_radius:
4837 majorRadius = parseInternalUnits( "major radius" );
4838 NeedRIGHT();
4839 break;
4840
4841 case T_minor_radius:
4842 minorRadius = parseInternalUnits( "minor radius" );
4843 NeedRIGHT();
4844 break;
4845
4846 case T_rotation_angle:
4847 rotation = EDA_ANGLE( parseDouble( "rotation angle" ), DEGREES_T );
4848 NeedRIGHT();
4849 break;
4850
4851 case T_start_angle:
4852 if( !aIsArc )
4853 Expecting( "start_angle only valid inside ellipse_arc" );
4854 startAngle = EDA_ANGLE( parseDouble( "start angle" ), DEGREES_T );
4855 NeedRIGHT();
4856 break;
4857
4858 case T_end_angle:
4859 if( !aIsArc )
4860 Expecting( "end_angle only valid inside ellipse_arc" );
4861 endAngle = EDA_ANGLE( parseDouble( "end angle" ), DEGREES_T );
4862 NeedRIGHT();
4863 break;
4864
4865 case T_stroke:
4866 parseStroke( stroke );
4867 aShape->SetStroke( stroke );
4868 break;
4869
4870 case T_fill:
4871 parseFill( fill );
4872 aShape->SetFillMode( fill.m_FillType );
4873 aShape->SetFillColor( fill.m_Color );
4874 if( aIsSchematic )
4875 fixupSchFillMode( aShape );
4876 break;
4877
4878 case T_uuid:
4879 if( !aIsSchematic )
4880 Expecting( "uuid only valid in schematic context" );
4881 NeedSYMBOL();
4882 const_cast<KIID&>( aShape->m_Uuid ) = KIID( FromUTF8() );
4883 NeedRIGHT();
4884 break;
4885
4886 case T_locked:
4887 if( !aIsSchematic )
4888 Expecting( "locked only valid in schematic context" );
4889 aShape->SetLocked( parseBool() );
4890 NeedRIGHT();
4891 break;
4892
4893 default:
4894 Expecting( "center, major_radius, minor_radius, rotation_angle, "
4895 "start_angle, end_angle, stroke, fill, uuid, or locked" );
4896 }
4897 }
4898
4899 aShape->SetEllipseCenter( center );
4900 aShape->SetEllipseMajorRadius( majorRadius );
4901 aShape->SetEllipseMinorRadius( minorRadius );
4902 aShape->SetEllipseRotation( rotation );
4903
4904 if( aIsArc )
4905 {
4906 aShape->SetEllipseStartAngle( startAngle );
4907 aShape->SetEllipseEndAngle( endAngle );
4908 }
4909}
4910
4911
4913{
4914 wxCHECK_MSG( CurTok() == T_ellipse, nullptr,
4915 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an ellipse." ) );
4916
4917 auto ellipse = std::make_unique<SCH_SHAPE>( SHAPE_T::ELLIPSE );
4918 parseEllipseBody( ellipse.get(), /*isArc*/ false, /*isSchematic*/ true, NextTok() );
4919 return ellipse.release();
4920}
4921
4922
4924{
4925 wxCHECK_MSG( CurTok() == T_ellipse_arc, nullptr,
4926 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an elliptical arc." ) );
4927
4928 auto arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ELLIPSE_ARC );
4929 parseEllipseBody( arc.get(), /*isArc*/ true, /*isSchematic*/ true, NextTok() );
4930 return arc.release();
4931}
4932
4933
4935{
4936 T token;
4937 std::unique_ptr<SCH_TEXT> text;
4938
4939 switch( CurTok() )
4940 {
4941 case T_text: text = std::make_unique<SCH_TEXT>(); break;
4942 case T_label: text = std::make_unique<SCH_LABEL>(); break;
4943 case T_global_label: text = std::make_unique<SCH_GLOBALLABEL>(); break;
4944 case T_hierarchical_label: text = std::make_unique<SCH_HIERLABEL>(); break;
4945 case T_netclass_flag: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
4946 case T_directive_label: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
4947 default:
4948 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as text." );
4949 }
4950
4951 // We'll reset this if we find a fields_autoplaced token
4952 text->SetFieldsAutoplaced( AUTOPLACE_NONE );
4953
4954 NeedSYMBOL();
4955
4956 text->SetText( FromUTF8() );
4957
4958 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4959 {
4960 if( token != T_LEFT )
4961 Expecting( T_LEFT );
4962
4963 token = NextTok();
4964
4965 switch( token )
4966 {
4967 case T_exclude_from_sim:
4968 text->SetExcludedFromSim( parseBool() );
4969 NeedRIGHT();
4970 break;
4971
4972 case T_at:
4973 text->SetPosition( parseXY() );
4974 text->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ).KeepUpright() );
4975
4976 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() ) )
4977 {
4978 switch( static_cast<int>( label->GetTextAngle().AsDegrees() ) )
4979 {
4980 case 0: label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
4981 case 90: label->SetSpinStyle( SPIN_STYLE::UP ); break;
4982 case 180: label->SetSpinStyle( SPIN_STYLE::LEFT ); break;
4983 case 270: label->SetSpinStyle( SPIN_STYLE::BOTTOM ); break;
4984 default: wxFAIL; label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
4985 }
4986 }
4987
4988 NeedRIGHT();
4989 break;
4990
4991 case T_shape:
4992 {
4993 if( text->Type() == SCH_TEXT_T || text->Type() == SCH_LABEL_T )
4994 Unexpected( T_shape );
4995
4996 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( text.get() );
4997
4998 token = NextTok();
4999
5000 switch( token )
5001 {
5002 case T_input: label->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
5003 case T_output: label->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
5004 case T_bidirectional: label->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
5005 case T_tri_state: label->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE ); break;
5006 case T_passive: label->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
5007 case T_dot: label->SetShape( LABEL_FLAG_SHAPE::F_DOT ); break;
5008 case T_round: label->SetShape( LABEL_FLAG_SHAPE::F_ROUND ); break;
5009 case T_diamond: label->SetShape( LABEL_FLAG_SHAPE::F_DIAMOND ); break;
5010 case T_rectangle: label->SetShape( LABEL_FLAG_SHAPE::F_RECTANGLE ); break;
5011 default:
5012 Expecting( "input, output, bidirectional, tri_state, passive, dot, round, diamond"
5013 "or rectangle" );
5014 }
5015
5016 NeedRIGHT();
5017 break;
5018 }
5019
5020 case T_length:
5021 {
5022 if( text->Type() != SCH_DIRECTIVE_LABEL_T )
5023 Unexpected( T_length );
5024
5025 SCH_DIRECTIVE_LABEL* label = static_cast<SCH_DIRECTIVE_LABEL*>( text.get() );
5026
5027 label->SetPinLength( parseInternalUnits( "pin length" ) );
5028 NeedRIGHT();
5029 }
5030 break;
5031
5032 case T_fields_autoplaced:
5033 if( parseMaybeAbsentBool( true ) )
5034 text->SetFieldsAutoplaced( AUTOPLACE_AUTO );
5035
5036 break;
5037
5038 case T_effects:
5039 parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ), true );
5040
5041 // Hidden schematic text is no longer supported
5042 text->SetVisible( true );
5043 break;
5044
5045 case T_iref: // legacy format; current is a T_property (aka SCH_FIELD)
5046 if( text->Type() == SCH_GLOBAL_LABEL_T )
5047 {
5048 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
5049 SCH_FIELD* field = label->GetField( FIELD_T::INTERSHEET_REFS );
5050
5051 field->SetTextPos( parseXY() );
5052 NeedRIGHT();
5053
5054 field->SetVisible( true );
5055 }
5056 break;
5057
5058 case T_uuid:
5059 NeedSYMBOL();
5060 const_cast<KIID&>( text->m_Uuid ) = parseKIID();
5061 NeedRIGHT();
5062 break;
5063
5064 case T_property:
5065 {
5066 if( text->Type() == SCH_TEXT_T )
5067 Unexpected( T_property );
5068
5069 SCH_FIELD* field = parseSchField( text.get() );
5070
5071 // Intersheetrefs is a mandatory field and so will already exist
5072 if( text->Type() == SCH_GLOBAL_LABEL_T && field->IsMandatory() )
5073 {
5074 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
5075 *label->GetField( field->GetId() ) = *field;
5076 }
5077 else
5078 {
5079 static_cast<SCH_LABEL_BASE*>( text.get() )->GetFields().emplace_back( *field );
5080 }
5081
5082 delete field;
5083 break;
5084 }
5085
5086 case T_locked:
5087 text->SetLocked( parseBool() );
5088 NeedRIGHT();
5089 break;
5090
5091 default:
5092 Expecting( "at, shape, iref, uuid, effects or locked" );
5093 }
5094 }
5095
5096 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() );
5097
5098 if( label && label->GetFields().empty() )
5100
5101 return text.release();
5102}
5103
5104
5106{
5107 wxCHECK_MSG( CurTok() == T_text_box, nullptr,
5108 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text box." ) );
5109
5110 std::unique_ptr<SCH_TEXTBOX> textBox = std::make_unique<SCH_TEXTBOX>();
5111
5112 parseSchTextBoxContent( textBox.get() );
5113
5114 return textBox.release();
5115}
5116
5117
5119{
5120 wxCHECK_MSG( CurTok() == T_table_cell, nullptr,
5121 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table cell." ) );
5122
5123 std::unique_ptr<SCH_TABLECELL> cell = std::make_unique<SCH_TABLECELL>();
5124
5125 parseSchTextBoxContent( cell.get() );
5126
5127 return cell.release();
5128}
5129
5130
5132{
5133 T token;
5134 VECTOR2I pos;
5135 VECTOR2I end;
5136 VECTOR2I size;
5137 int left;
5138 int top;
5139 int right;
5140 int bottom;
5142 FILL_PARAMS fill;
5143 bool foundEnd = false;
5144 bool foundSize = false;
5145 bool foundMargins = false;
5146
5147 NeedSYMBOL();
5148
5149 aTextBox->SetText( FromUTF8() );
5150
5151 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5152 {
5153 if( token != T_LEFT )
5154 Expecting( T_LEFT );
5155
5156 token = NextTok();
5157
5158 switch( token )
5159 {
5160 case T_exclude_from_sim:
5161 aTextBox->SetExcludedFromSim( parseBool() );
5162 NeedRIGHT();
5163 break;
5164
5165 case T_start: // Legacy token during 6.99 development; fails to handle angle
5166 pos = parseXY();
5167 NeedRIGHT();
5168 break;
5169
5170 case T_end: // Legacy token during 6.99 development; fails to handle angle
5171 end = parseXY();
5172 foundEnd = true;
5173 NeedRIGHT();
5174 break;
5175
5176 case T_at:
5177 pos = parseXY();
5178 aTextBox->SetTextAngle( EDA_ANGLE( parseDouble( "textbox angle" ), DEGREES_T ) );
5179 NeedRIGHT();
5180 break;
5181
5182 case T_size:
5183 size = parseXY();
5184 foundSize = true;
5185 NeedRIGHT();
5186 break;
5187
5188 case T_span:
5189 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( aTextBox ) )
5190 {
5191 cell->SetColSpan( parseInt( "column span" ) );
5192 cell->SetRowSpan( parseInt( "row span" ) );
5193 }
5194 else
5195 {
5196 Expecting( "at, size, stroke, fill, effects or uuid" );
5197 }
5198
5199 NeedRIGHT();
5200 break;
5201
5202 case T_stroke:
5203 parseStroke( stroke );
5204 aTextBox->SetStroke( stroke );
5205 break;
5206
5207 case T_fill:
5208 parseFill( fill );
5209 aTextBox->SetFillMode( fill.m_FillType );
5210 aTextBox->SetFillColor( fill.m_Color );
5211 fixupSchFillMode( aTextBox );
5212 break;
5213
5214 case T_margins:
5215 parseMargins( left, top, right, bottom );
5216 aTextBox->SetMarginLeft( left );
5217 aTextBox->SetMarginTop( top );
5218 aTextBox->SetMarginRight( right );
5219 aTextBox->SetMarginBottom( bottom );
5220 foundMargins = true;
5221 NeedRIGHT();
5222 break;
5223
5224 case T_effects:
5225 parseEDA_TEXT( static_cast<EDA_TEXT*>( aTextBox ), false );
5226 break;
5227
5228 case T_uuid:
5229 NeedSYMBOL();
5230 const_cast<KIID&>( aTextBox->m_Uuid ) = parseKIID();
5231 NeedRIGHT();
5232 break;
5233
5234 case T_locked:
5235 aTextBox->SetLocked( parseBool() );
5236 NeedRIGHT();
5237 break;
5238
5239 default:
5240 if( dynamic_cast<SCH_TABLECELL*>( aTextBox ) != nullptr )
5241 Expecting( "at, size, stroke, fill, effects, span, uuid or locked" );
5242 else
5243 Expecting( "at, size, stroke, fill, effects, uuid or locked" );
5244 }
5245 }
5246
5247 aTextBox->SetPosition( pos );
5248
5249 if( foundEnd )
5250 aTextBox->SetEnd( end );
5251 else if( foundSize )
5252 aTextBox->SetEnd( pos + size );
5253 else
5254 Expecting( "size" );
5255
5256 if( !foundMargins )
5257 {
5258 int margin = aTextBox->GetLegacyTextMargin();
5259 aTextBox->SetMarginLeft( margin );
5260 aTextBox->SetMarginTop( margin );
5261 aTextBox->SetMarginRight( margin );
5262 aTextBox->SetMarginBottom( margin );
5263 }
5264}
5265
5266
5268{
5269 wxCHECK_MSG( CurTok() == T_table, nullptr,
5270 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table." ) );
5271
5272 T token;
5273 int defaultLineWidth = schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
5274 STROKE_PARAMS borderStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
5275 STROKE_PARAMS separatorsStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
5276 std::unique_ptr<SCH_TABLE> table = std::make_unique<SCH_TABLE>( defaultLineWidth );
5277
5278 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5279 {
5280 if( token != T_LEFT )
5281 Expecting( T_LEFT );
5282
5283 token = NextTok();
5284
5285 switch( token )
5286 {
5287 case T_column_count:
5288 table->SetColCount( parseInt( "column count" ) );
5289 NeedRIGHT();
5290 break;
5291
5292 case T_column_widths:
5293 {
5294 int col = 0;
5295
5296 while( ( token = NextTok() ) != T_RIGHT )
5297 table->SetColWidth( col++, parseInternalUnits() );
5298
5299 break;
5300 }
5301
5302 case T_row_heights:
5303 {
5304 int row = 0;
5305
5306 while( ( token = NextTok() ) != T_RIGHT )
5307 table->SetRowHeight( row++, parseInternalUnits() );
5308
5309 break;
5310 }
5311
5312 case T_cells:
5313 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5314 {
5315 if( token != T_LEFT )
5316 Expecting( T_LEFT );
5317
5318 token = NextTok();
5319
5320 if( token != T_table_cell )
5321 Expecting( "table_cell" );
5322
5323 table->AddCell( parseSchTableCell() );
5324 }
5325
5326 break;
5327
5328 case T_border:
5329 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5330 {
5331 if( token != T_LEFT )
5332 Expecting( T_LEFT );
5333
5334 token = NextTok();
5335
5336 switch( token )
5337 {
5338 case T_external:
5339 table->SetStrokeExternal( parseBool() );
5340 NeedRIGHT();
5341 break;
5342
5343 case T_header:
5344 table->SetStrokeHeaderSeparator( parseBool() );
5345 NeedRIGHT();
5346 break;
5347
5348 case T_stroke:
5349 parseStroke( borderStroke );
5350 table->SetBorderStroke( borderStroke );
5351 break;
5352
5353 default:
5354 Expecting( "external, header or stroke" );
5355 break;
5356 }
5357 }
5358
5359 break;
5360
5361 case T_separators:
5362 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5363 {
5364 if( token != T_LEFT )
5365 Expecting( T_LEFT );
5366
5367 token = NextTok();
5368
5369 switch( token )
5370 {
5371 case T_rows:
5372 table->SetStrokeRows( parseBool() );
5373 NeedRIGHT();
5374 break;
5375
5376 case T_cols:
5377 table->SetStrokeColumns( parseBool() );
5378 NeedRIGHT();
5379 break;
5380
5381 case T_stroke:
5382 parseStroke( separatorsStroke );
5383 table->SetSeparatorsStroke( separatorsStroke );
5384 break;
5385
5386 default:
5387 Expecting( "rows, cols, or stroke" );
5388 break;
5389 }
5390 }
5391
5392 break;
5393
5394 case T_uuid:
5395 NeedSYMBOL();
5396 const_cast<KIID&>( table->m_Uuid ) = parseKIID();
5397 NeedRIGHT();
5398 break;
5399
5400 case T_locked:
5401 table->SetLocked( parseBool() );
5402 NeedRIGHT();
5403 break;
5404
5405 default:
5406 Expecting( "columns, col_widths, row_heights, border, separators, uuid, locked, header or cells" );
5407 }
5408 }
5409
5410 if( !table->GetCell( 0, 0 ) )
5411 {
5412 THROW_PARSE_ERROR( _( "Invalid table: no cells defined" ), CurSource(), CurLine(), CurLineNumber(),
5413 CurOffset() );
5414 }
5415
5416 return table.release();
5417}
5418
5419
5421{
5422 wxCHECK_RET( CurTok() == T_bus_alias,
5423 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus alias." ) );
5424 wxCHECK( aScreen, /* void */ );
5425
5426 T token;
5427 std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>();
5428 wxString alias;
5429 wxString member;
5430
5431 NeedSYMBOL();
5432
5433 alias = FromUTF8();
5434
5435 if( m_requiredVersion < 20210621 )
5436 alias = ConvertToNewOverbarNotation( alias );
5437
5438 busAlias->SetName( alias );
5439
5440 NeedLEFT();
5441 token = NextTok();
5442
5443 if( token != T_members )
5444 Expecting( "members" );
5445
5446 token = NextTok();
5447
5448 while( token != T_RIGHT )
5449 {
5450 if( !IsSymbol( token ) )
5451 Expecting( "quoted string" );
5452
5453 member = FromUTF8();
5454
5455 if( m_requiredVersion < 20210621 )
5456 member = ConvertToNewOverbarNotation( member );
5457
5458 busAlias->AddMember( member );
5459
5460 token = NextTok();
5461 }
5462
5463 NeedRIGHT();
5464
5465 aScreen->AddBusAlias( busAlias );
5466}
5467
5468
5470{
5471 // (net_chain "name" [(from "ref" "pin")] [(to "ref" "pin")]
5472 // [(net_class "...")] [(color R G B A)] [(nets "n1" "n2" ...)])
5473 NeedSYMBOL();
5474 wxString name = FromUTF8();
5475
5476 wxString netClass;
5478 wxString fromRef, fromPin, toRef, toPin;
5479 std::set<wxString> memberNets;
5480
5481 for( T tok = NextTok(); tok != T_RIGHT; tok = NextTok() )
5482 {
5483 if( tok == T_LEFT )
5484 tok = NextTok();
5485
5486 if( tok == T_from )
5487 {
5488 NeedSYMBOLorNUMBER();
5489 fromRef = From_UTF8( CurText() );
5490 NeedSYMBOLorNUMBER();
5491 fromPin = From_UTF8( CurText() );
5492 NeedRIGHT();
5493 }
5494 else if( tok == T_to )
5495 {
5496 NeedSYMBOLorNUMBER();
5497 toRef = From_UTF8( CurText() );
5498 NeedSYMBOLorNUMBER();
5499 toPin = From_UTF8( CurText() );
5500 NeedRIGHT();
5501 }
5502 else if( tok == T_net_class )
5503 {
5504 NeedSYMBOLorNUMBER();
5505 netClass = FromUTF8();
5506 NeedRIGHT();
5507 }
5508 else if( tok == T_color )
5509 {
5510 int r = parseInt( "red" );
5511 int g = parseInt( "green" );
5512 int b = parseInt( "blue" );
5513 double al = parseDouble( "alpha" );
5514 color = COLOR4D( r / 255.0, g / 255.0, b / 255.0, al );
5515 NeedRIGHT();
5516 }
5517 else if( tok == T_nets )
5518 {
5519 for( T inner = NextTok(); inner != T_RIGHT; inner = NextTok() )
5520 {
5521 if( !IsSymbol( inner ) && inner != T_NUMBER )
5522 Expecting( "net name" );
5523
5524 memberNets.insert( FromUTF8() );
5525 }
5526 }
5527 else
5528 {
5529 // Skip unknown subsections
5530 int depth = 1;
5531
5532 while( depth > 0 )
5533 {
5534 T inner = NextTok();
5535
5536 if( inner == T_EOF )
5537 break;
5538
5539 if( inner == T_LEFT )
5540 ++depth;
5541 else if( inner == T_RIGHT )
5542 --depth;
5543 }
5544 }
5545 }
5546
5547 if( !fromRef.IsEmpty() && !toRef.IsEmpty() )
5548 {
5549 m_netChainTerminalRefs[name] = { { fromRef, fromPin }, { toRef, toPin } };
5550 }
5551
5552 if( !netClass.IsEmpty() )
5553 m_netChainNetClasses[name] = netClass;
5554
5555 if( color != KIGFX::COLOR4D::UNSPECIFIED )
5556 m_netChainColors[name] = color;
5557
5558 if( !memberNets.empty() )
5559 m_netChainMemberNets[name] = std::move( memberNets );
5560}
5561
5562
5564{
5565 T token;
5566
5567 while( ( token = NextTok() ) != T_RIGHT )
5568 {
5569 // This token is the Uuid of the item in the group.
5570 // Since groups are serialized at the end of the file/footprint, the Uuid should already
5571 // have been seen and exist in the board.
5572 KIID uuid( CurStr() );
5573 aGroupInfo.memberUuids.push_back( uuid );
5574 }
5575}
5576
5577
5579{
5580 wxCHECK_RET( CurTok() == T_group,
5581 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_GROUP." ) );
5582
5583 T token;
5584
5585 m_groupInfos.push_back( GROUP_INFO() );
5586 GROUP_INFO& groupInfo = m_groupInfos.back();
5587
5588 while( ( token = NextTok() ) != T_LEFT )
5589 {
5590 if( token == T_STRING )
5591 groupInfo.name = FromUTF8();
5592 else
5593 Expecting( "group name or locked" );
5594 }
5595
5596 for( ; token != T_RIGHT; token = NextTok() )
5597 {
5598 if( token != T_LEFT )
5599 Expecting( T_LEFT );
5600
5601 token = NextTok();
5602
5603 switch( token )
5604 {
5605 case T_uuid:
5606 NextTok();
5607 groupInfo.uuid = parseKIID();
5608 NeedRIGHT();
5609 break;
5610
5611 case T_lib_id:
5612 {
5613 token = NextTok();
5614
5615 if( !IsSymbol( token ) && token != T_NUMBER )
5616 Expecting( "symbol|number" );
5617
5618 LIB_ID libId;
5619 wxString name = FromUTF8();
5620 // Some symbol LIB_IDs have the '/' character escaped which can break
5621 // symbol links. The '/' character is no longer an illegal LIB_ID character so
5622 // it doesn't need to be escaped.
5623 name.Replace( "{slash}", "/" );
5624
5625 int bad_pos = groupInfo.libId.Parse( name );
5626
5627 if( bad_pos >= 0 )
5628 {
5629 if( static_cast<int>( name.size() ) > bad_pos )
5630 {
5631 wxString msg = wxString::Format( _( "Group library link %s contains invalid character '%c'" ), name,
5632 name[bad_pos] );
5633
5634 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
5635 }
5636
5637 THROW_PARSE_ERROR( _( "Invalid library ID" ), CurSource(), CurLine(), CurLineNumber(), CurOffset() );
5638 }
5639
5640 NeedRIGHT();
5641 break;
5642 }
5643
5644 case T_members:
5645 {
5646 parseGroupMembers( groupInfo );
5647 break;
5648 }
5649
5650 case T_locked:
5651 groupInfo.locked = parseBool();
5652 NeedRIGHT();
5653 break;
5654
5655 default:
5656 Expecting( "uuid, lib_id, members, locked" );
5657 }
5658 }
5659}
5660
5661
5663{
5664 if( !aParent )
5665 return;
5666
5667 auto getItem =
5668 [&]( const KIID& aId )
5669 {
5670 SCH_ITEM* aItem = nullptr;
5671
5672 for( SCH_ITEM* item : aParent->Items() )
5673 {
5674 if( item->m_Uuid == aId )
5675 {
5676 aItem = item;
5677 break;
5678 }
5679 }
5680
5681 return aItem;
5682 };
5683
5684 // Now that we've parsed the other Uuids in the file we can resolve the uuids referred
5685 // to in the group declarations we saw.
5686 //
5687 // First add all group objects so subsequent GetItem() calls for nested groups work.
5688 for( const GROUP_INFO& groupInfo : m_groupInfos )
5689 {
5690 SCH_GROUP* group = nullptr;
5691
5692 group = new SCH_GROUP( aParent );
5693 group->SetName( groupInfo.name );
5694
5695 const_cast<KIID&>( group->m_Uuid ) = groupInfo.uuid;
5696
5697 if( groupInfo.libId.IsValid() )
5698 group->SetDesignBlockLibId( groupInfo.libId );
5699
5700 group->SetLocked( groupInfo.locked );
5701
5702 aParent->Append( group );
5703 }
5704
5705 for( const GROUP_INFO& groupInfo : m_groupInfos )
5706 {
5707 SCH_GROUP* group = static_cast<SCH_GROUP*>( getItem( groupInfo.uuid ) );
5708
5709 if( group && group->Type() == SCH_GROUP_T )
5710 {
5711 for( const KIID& aUuid : groupInfo.memberUuids )
5712 {
5713 if( SCH_ITEM* gItem = getItem( aUuid ) )
5714 group->AddItem( gItem );
5715 }
5716 }
5717 }
5718
5719 aParent->GroupsSanityCheck( true );
5720}
5721
5722
5724{
5725 // Skip tokens until we exit the current S-expression block.
5726 // This is used for error recovery when parsing fails mid-symbol.
5727 while( aDepth > 0 )
5728 {
5729 T token = NextTok();
5730
5731 if( token == T_EOF )
5732 break;
5733 else if( token == T_LEFT )
5734 aDepth++;
5735 else if( token == T_RIGHT )
5736 aDepth--;
5737 }
5738}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
constexpr double ARC_LOW_DEF_MM
Definition base_units.h:127
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
void SetContentModified(bool aModified=true)
Definition base_screen.h:55
This class handle bitmap images in KiCad.
Definition bitmap_base.h:45
int GetPPI() const
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:398
EDA_ANGLE Normalize()
Definition eda_angle.h:229
const KIID m_Uuid
Definition eda_item.h:531
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
EDA_ITEM * GetParent() const
Definition eda_item.h:110
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:89
virtual void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:244
FILL_T GetFillMode() const
Definition eda_shape.h:158
SHAPE_POLY_SET & GetPolyShape()
virtual void SetEllipseEndAngle(const EDA_ANGLE &aA)
Definition eda_shape.h:331
virtual void SetEllipseRotation(const EDA_ANGLE &aA)
Definition eda_shape.h:312
void SetFillColor(const COLOR4D &aColor)
Definition eda_shape.h:170
int GetPointCount() const
virtual void SetEllipseCenter(const VECTOR2I &aPt)
Definition eda_shape.h:285
virtual void SetEllipseMinorRadius(int aR)
Definition eda_shape.h:303
virtual void SetEllipseMajorRadius(int aR)
Definition eda_shape.h:294
virtual void SetEllipseStartAngle(const EDA_ANGLE &aA)
Definition eda_shape.h:322
void SetFillMode(FILL_T aFill)
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:89
void SetTextColor(const COLOR4D &aColor)
Definition eda_text.h:290
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:532
void SetUnresolvedFontName(const wxString &aFontName)
Definition eda_text.h:270
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:110
virtual void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:576
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:412
void SetBoldFlag(bool aBold)
Set only the bold flag, without changing the font.
Definition eda_text.cpp:373
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:381
void SetLineSpacing(double aLineSpacing)
Definition eda_text.cpp:524
virtual void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:279
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:322
void SetHyperlink(wxString aLink)
Definition eda_text.h:425
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:265
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:294
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:404
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:101
double r
Red component.
Definition color4d.h:389
double g
Green component.
Definition color4d.h:390
double a
Alpha component.
Definition color4d.h:392
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:398
double b
Blue component.
Definition color4d.h:391
Definition kiid.h:44
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:48
Define a library symbol object.
Definition lib_symbol.h:79
wxString GetName() const override
Definition lib_symbol.h:141
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:62
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
void SetPortrait(bool aIsPortrait)
Rotate the paper page 90 degrees.
bool SetType(PAGE_SIZE_TYPE aPageSize, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
void SetWidthMM(double aWidthInMM)
Definition page_info.h:136
void SetHeightMM(double aHeightInMM)
Definition page_info.h:141
const PAGE_SIZE_TYPE & GetType() const
Definition page_info.h:98
A 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:90
Object to handle a bitmap image that can be inserted in a schematic.
Definition sch_bitmap.h:36
Class for a wire to bus entry.
void SetPinLength(int aLength)
Definition sch_label.h:482
bool IsMandatory() const
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:128
FIELD_T GetId() const
Definition sch_field.h:132
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:48
bool m_appending
Appending load status.
SCH_FIELD * parseSchField(SCH_ITEM *aParent)
void parseSchSymbolInstances(SCH_SCREEN *aScreen)
void parsePinNumbers(std::unique_ptr< LIB_SYMBOL > &aSymbol)
LIB_SYMBOL * parseLibSymbol(LIB_SYMBOL_MAP &aSymbolLibMap)
void parseEllipseBody(SCH_SHAPE *aShape, bool aIsArc, bool aIsSchematic, TSCHEMATIC_T::T aFirstTok)
void parseBusAlias(SCH_SCREEN *aScreen)
void parseTITLE_BLOCK(TITLE_BLOCK &aTitleBlock)
void parseGroupMembers(GROUP_INFO &aGroupInfo)
void 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:162
void SetLocked(bool aLocked) override
Definition sch_item.h:251
bool IsLocked() const override
Definition sch_item.cpp:148
void SetFieldsAutoplaced(AUTOPLACE_ALGO aAlgo)
Definition sch_item.h:624
void SetShape(LABEL_FLAG_SHAPE aShape)
Definition sch_label.h:179
std::vector< SCH_FIELD > & GetFields()
Definition sch_label.h:210
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:38
void SetStartPoint(const VECTOR2I &aPosition)
Definition sch_line.h:136
virtual void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_line.h:198
void SetEndPoint(const VECTOR2I &aPosition)
Definition sch_line.h:145
void SetFileFormatVersionAtLoad(int aVersion)
Definition sch_screen.h:134
std::vector< SCH_SHEET_INSTANCE > m_sheetInstances
Definition sch_screen.h:724
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition sch_screen.h:164
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:138
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:115
const KIID & GetUuid() const
Definition sch_screen.h:529
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:135
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:732
std::vector< SCH_SYMBOL_INSTANCE > m_symbolInstances
The list of symbol instances loaded from the schematic file.
Definition sch_screen.h:723
void SetPosition(const VECTOR2I &aPos) override
Definition sch_shape.h:85
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_shape.cpp:98
STROKE_PARAMS GetStroke() const override
Definition sch_shape.h:57
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
void SetPageNumber(const wxString &aPageNumber)
Set the sheet instance user definable page number.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Variant information for a schematic sheet.
void InitializeAttributes(const SCH_SHEET &aSheet)
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:44
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:139
Variant information for a schematic symbol.
void InitializeAttributes(const SCH_SYMBOL &aSymbol)
Schematic symbol object.
Definition sch_symbol.h:69
void SetMarginBottom(int aBottom)
Definition sch_textbox.h:67
int GetLegacyTextMargin() const
void SetMarginLeft(int aLeft)
Definition sch_textbox.h:64
void SetMarginRight(int aRight)
Definition sch_textbox.h:66
void SetExcludedFromSim(bool aExclude, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Definition sch_textbox.h:98
void SetMarginTop(int aTop)
Definition sch_textbox.h:65
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:37
void SetRevision(const wxString &aRevision)
Definition title_block.h:77
void SetComment(int aIdx, const wxString &aComment)
Definition title_block.h:97
void SetTitle(const wxString &aTitle)
Definition title_block.h:54
void SetCompany(const wxString &aCompany)
Definition title_block.h:87
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition title_block.h:67
for transforming drawing coordinates for a wxDC device context.
Definition transform.h:42
wxString m_Name
bool m_ExcludedFromBOM
std::map< wxString, wxString > m_Fields
bool m_ExcludedFromPosFiles
bool m_ExcludedFromSim
bool m_ExcludedFromBoard
#define DEFAULT_LINE_WIDTH_MILS
The default wire width in mils. (can be changed in preference menu)
@ DSN_LEFT
Definition dsnlexer.h:63
@ DSN_RIGHT
Definition dsnlexer.h:62
@ DSN_STRING
Definition dsnlexer.h:64
@ DSN_EOF
Definition dsnlexer.h:65
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h: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:50
@ ELLIPSE
Definition eda_shape.h:52
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:47
@ ELLIPSE_ARC
Definition eda_shape.h:53
@ FILLED_WITH_COLOR
Definition eda_shape.h:63
@ NO_FILL
Definition eda_shape.h:60
@ REVERSE_HATCH
Definition eda_shape.h:65
@ HATCH
Definition eda_shape.h:64
@ FILLED_WITH_BG_BODYCOLOR
Definition eda_shape.h:62
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:61
@ CROSS_HATCH
Definition eda_shape.h:66
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:66
@ LAYER_DEVICE
Definition layer_ids.h:464
@ LAYER_WIRE
Definition layer_ids.h:450
@ LAYER_NOTES
Definition layer_ids.h:465
@ LAYER_BUS
Definition layer_ids.h:451
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:79
#define MAX_PAGE_SIZE_EESCHEMA_MM
Definition page_info.h:37
#define MIN_PAGE_SIZE_MM
Min and max page sizes for clamping, in mm.
Definition page_info.h:35
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition pin_type.h:32
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:33
@ PT_NC
not connected (must be left open)
Definition pin_type.h:46
@ PT_OUTPUT
usual output
Definition pin_type.h:34
@ PT_TRISTATE
tri state bus pin
Definition pin_type.h:36
@ PT_NIC
not internally connected (may be connected to anything)
Definition pin_type.h:40
@ PT_BIDI
input or output (like port for a microprocessor)
Definition pin_type.h:35
@ PT_OPENEMITTER
pin type open emitter
Definition pin_type.h:45
@ PT_POWER_OUT
output of a regulator: intended to be connected to power input pins
Definition pin_type.h:43
@ PT_OPENCOLLECTOR
pin type open collector
Definition pin_type.h:44
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:42
@ PT_UNSPECIFIED
unknown electrical properties: creates always a warning when connected
Definition pin_type.h:41
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
Definition pin_type.h:39
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:123
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:107
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:114
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:131
GRAPHIC_PINSHAPE
Definition pin_type.h:80
const SCH_FIELD * FindField(const std::vector< SCH_FIELD > &aFields, FIELD_T aFieldId)
Definition sch_field.h:383
#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:66
@ AUTOPLACE_AUTO
Definition sch_item.h:67
@ L_BIDI
Definition sch_label.h:100
@ L_TRISTATE
Definition sch_label.h:101
@ L_UNSPECIFIED
Definition sch_label.h:102
@ F_DOT
Definition sch_label.h:105
@ F_ROUND
Definition sch_label.h:106
@ L_OUTPUT
Definition sch_label.h:99
@ F_DIAMOND
Definition sch_label.h:107
@ L_INPUT
Definition sch_label.h:98
@ F_RECTANGLE
Definition sch_label.h:108
#define SIM_LEGACY_ENABLE_FIELD
Definition sim_model.h:61
#define SIM_LEGACY_ENABLE_FIELD_V7
Definition sim_model.h:57
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:40
@ SYM_MIRROR_X
Definition symbol.h:39
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:530
@ SCH_GROUP_T
Definition typeinfo.h:170
@ SCH_SYMBOL_T
Definition typeinfo.h:169
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:168
@ SCH_LABEL_T
Definition typeinfo.h:164
@ SCH_SHEET_T
Definition typeinfo.h:172
@ SCH_TEXT_T
Definition typeinfo.h:148
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:165
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683