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