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