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