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