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