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 i = 0; i < MANDATORY_FIELDS; i++ )
318 m_fieldIDsRead.insert( i );
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(), MANDATORY_FIELDS );
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 ii = 0; ii < MANDATORY_FIELDS; ++ii )
993 {
994 if( !name.CmpNoCase( GetCanonicalFieldName( ii ) ) )
995 {
996 field->SetId( ii );
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_FIELDS ID
1029 if( id >= MANDATORY_FIELDS )
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_effects:
1043 parseEDA_TEXT( static_cast<EDA_TEXT*>( field.get() ), field->GetId() == VALUE_FIELD );
1044 break;
1045
1046 case T_show_name:
1047 {
1048 bool show = parseMaybeAbsentBool( true );
1049 field->SetNameShown( show );
1050 break;
1051 }
1052
1053 case T_do_not_autoplace:
1054 {
1055 bool doNotAutoplace = parseMaybeAbsentBool( true );
1056 field->SetCanAutoplace( !doNotAutoplace );
1057 break;
1058 }
1059
1060 default:
1061 Expecting( "id, at, show_name, do_not_autoplace, or effects" );
1062 }
1063 }
1064
1065 // Due to an bug when in #LIB_SYMBOL::Flatten, duplicate ids slipped through when writing
1066 // files. This section replaces duplicate #SCH_FIELD indices on load.
1067 if( ( field->GetId() >= MANDATORY_FIELDS ) && m_fieldIDsRead.count( field->GetId() ) )
1068 {
1069 int nextAvailableId = field->GetId() + 1;
1070
1071 while( m_fieldIDsRead.count( nextAvailableId ) )
1072 nextAvailableId += 1;
1073
1074 field->SetId( nextAvailableId );
1075 }
1076
1077 SCH_FIELD* existingField;
1078
1079 if( field->IsMandatory() )
1080 {
1081 existingField = aSymbol->GetFieldById( field->GetId() );
1082
1083 *existingField = *field;
1084 m_fieldIDsRead.insert( field->GetId() );
1085 return existingField;
1086 }
1087 else if( name == "ki_keywords" )
1088 {
1089 // Not a SCH_FIELD object yet.
1090 aSymbol->SetKeyWords( value );
1091 return nullptr;
1092 }
1093 // In v7 and earlier the description field didn't exist and was a key/value
1094 else if( name == "ki_description" )
1095 {
1096 aSymbol->SetDescription( value );
1097 return nullptr;
1098 }
1099 else if( name == "ki_fp_filters" )
1100 {
1101 // Not a SCH_FIELD object yet.
1102 wxArrayString filters;
1103 wxStringTokenizer tokenizer( value );
1104
1105 while( tokenizer.HasMoreTokens() )
1106 {
1107 wxString curr_token = UnescapeString( tokenizer.GetNextToken() );
1108 filters.Add( curr_token );
1109 }
1110
1111 aSymbol->SetFPFilters( filters );
1112 return nullptr;
1113 }
1114 else if( name == "ki_locked" )
1115 {
1116 // This is a temporary SCH_FIELD object until interchangeable units are determined on
1117 // the fly.
1118 aSymbol->LockUnits( true );
1119 return nullptr;
1120 }
1121 else
1122 {
1123 // At this point, a user field is read.
1124 existingField = aSymbol->FindField( field->GetCanonicalName() );
1125
1126#if 1 // Enable it to modify the name of the field to add if already existing
1127 // Disable it to skip the field having the same name as previous field
1128 if( existingField )
1129 {
1130 // We cannot handle 2 fields with the same name, so because the field name
1131 // is already in use, try to build a new name (oldname_x)
1132 wxString base_name = field->GetCanonicalName();
1133
1134 // Arbitrary limit 10 attempts to find a new name
1135 for( int ii = 1; ii < 10 && existingField; ii++ )
1136 {
1137 wxString newname = base_name;
1138 newname << '_' << ii;
1139
1140 existingField = aSymbol->FindField( newname );
1141
1142 if( !existingField ) // the modified name is not found, use it
1143 field->SetName( newname );
1144 }
1145 }
1146#endif
1147 if( !existingField )
1148 {
1149 aSymbol->AddDrawItem( field.get(), false );
1150 m_fieldIDsRead.insert( field->GetId() );
1151 return field.release();
1152 }
1153 else
1154 {
1155 // We cannot handle 2 fields with the same name, so skip this one
1156 return nullptr;
1157 }
1158 }
1159}
1160
1161
1163{
1164 wxCHECK_MSG( CurTok() == T_arc, nullptr,
1165 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc." ) );
1166
1167 T token;
1168 VECTOR2I startPoint( 1, 0 ); // Initialize to a non-degenerate arc just for safety
1169 VECTOR2I midPoint( 1, 1 );
1170 VECTOR2I endPoint( 0, 1 );
1171 bool hasMidPoint = false;
1172 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
1173 FILL_PARAMS fill;
1174
1175 // Parameters for legacy format
1176 VECTOR2I center( 0, 0 );
1177 EDA_ANGLE startAngle = ANGLE_0;
1178 EDA_ANGLE endAngle = ANGLE_90;
1179 bool hasAngles = false;
1180
1181 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC, LAYER_DEVICE );
1182
1183 arc->SetUnit( m_unit );
1184 arc->SetBodyStyle( m_bodyStyle );
1185
1186 token = NextTok();
1187
1188 if( token == T_private )
1189 {
1190 arc->SetPrivate( true );
1191 token = NextTok();
1192 }
1193
1194 for( ; token != T_RIGHT; token = NextTok() )
1195 {
1196 if( token != T_LEFT )
1197 Expecting( T_LEFT );
1198
1199 token = NextTok();
1200
1201 switch( token )
1202 {
1203 case T_start:
1204 startPoint = parseXY( true );
1205 NeedRIGHT();
1206 break;
1207
1208 case T_mid:
1209 midPoint = parseXY( true );
1210 NeedRIGHT();
1211 hasMidPoint = true;
1212 break;
1213
1214 case T_end:
1215 endPoint = parseXY( true );
1216 NeedRIGHT();
1217 break;
1218
1219 case T_radius:
1220 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1221 {
1222 if( token != T_LEFT )
1223 Expecting( T_LEFT );
1224
1225 token = NextTok();
1226
1227 switch( token )
1228 {
1229 case T_at:
1230 center = parseXY( true );
1231 NeedRIGHT();
1232 break;
1233
1234 case T_length:
1235 parseInternalUnits( "radius length" );
1236 NeedRIGHT();
1237 break;
1238
1239 case T_angles:
1240 {
1241 startAngle = EDA_ANGLE( parseDouble( "start radius angle" ), DEGREES_T );
1242 endAngle = EDA_ANGLE( parseDouble( "end radius angle" ), DEGREES_T );
1243 startAngle.Normalize();
1244 endAngle.Normalize();
1245 NeedRIGHT();
1246 hasAngles = true;
1247 break;
1248 }
1249
1250 default:
1251 Expecting( "at, length, or angles" );
1252 }
1253 }
1254
1255 break;
1256
1257 case T_stroke:
1258 parseStroke( stroke );
1259 arc->SetStroke( stroke );
1260 break;
1261
1262 case T_fill:
1263 parseFill( fill );
1264 arc->SetFillMode( fill.m_FillType );
1265 arc->SetFillColor( fill.m_Color );
1266 break;
1267
1268 default:
1269 Expecting( "start, mid, end, radius, stroke, or fill" );
1270 }
1271 }
1272
1273 if( hasMidPoint )
1274 {
1275 arc->SetArcGeometry( startPoint, midPoint, endPoint );
1276
1277 if( m_requiredVersion <= 20230121 ) // Versions before 7.0
1278 {
1279 // Should be not required. Unfortunately it is needed because some bugs created
1280 // incorrect data after conversion of old libraries to the new arc format using
1281 // startPoint, midPoint, endPoint
1282 // Until now, in Eeschema the arc angle should be <= 180 deg.
1283 // If > 180 (bug...) we need to swap arc ends.
1284 // However arc angle == 180 deg can also create issues in some cases (plotters, hittest)
1285 // so also avoid arc == 180 deg
1286 EDA_ANGLE arc_start, arc_end, arc_angle;
1287 arc->CalcArcAngles( arc_start, arc_end );
1288 arc_angle = arc_end - arc_start;
1289
1290 if( arc_angle > ANGLE_180 )
1291 {
1292 // Change arc to its complement (360deg - arc_angle)
1293 arc->SetStart( endPoint );
1294 arc->SetEnd( startPoint );
1295 VECTOR2I new_center =
1296 CalcArcCenter( arc->GetStart(), arc->GetEnd(), ANGLE_360 - arc_angle );
1297 arc->SetCenter( new_center );
1298 }
1299 else if( arc_angle == ANGLE_180 )
1300 {
1301 VECTOR2I new_center = CalcArcCenter( arc->GetStart(), arc->GetEnd(),
1302 EDA_ANGLE( 179.5, DEGREES_T ) );
1303 arc->SetCenter( new_center );
1304 }
1305 }
1306 }
1307 else if( hasAngles )
1308 {
1309 arc->SetCenter( center );
1310 /*
1311 * Older versions stored start-end with an implied winding, but the winding was different
1312 * between LibEdit and PCBNew. Since we now use a common class (EDA_SHAPE) for both we
1313 * need to flip one of them. LibEdit drew the short straw.
1314 */
1315 arc->SetStart( endPoint );
1316 arc->SetEnd( startPoint );
1317
1318 // Like previously, 180 degrees arcs that create issues are just modified
1319 // to be < 180 degrees to do not break some other functions ( Draw, Plot, HitTest)
1320 EDA_ANGLE arc_start, arc_end, arc_angle;
1321 arc->CalcArcAngles( arc_start, arc_end );
1322 arc_angle = arc_end - arc_start;
1323
1324 // The arc angle should be <= 180 deg.
1325 // If > 180 we need to swap arc ends (the first choice was not good)
1326 if( arc_angle > ANGLE_180 )
1327 {
1328 arc->SetStart( startPoint );
1329 arc->SetEnd( endPoint );
1330 }
1331 else if( arc_angle == ANGLE_180 )
1332 {
1333 arc->SetStart( startPoint );
1334 arc->SetEnd( endPoint );
1335 VECTOR2I new_center = CalcArcCenter( arc->GetStart(), arc->GetEnd(),
1336 EDA_ANGLE( 179.5, DEGREES_T ) );
1337 arc->SetCenter( new_center );
1338 }
1339 }
1340 else
1341 {
1342 wxFAIL_MSG( "Setting arc without either midpoint or angles not implemented." );
1343 }
1344
1345 return arc.release();
1346}
1347
1348
1350{
1351 wxCHECK_MSG( CurTok() == T_bezier, nullptr,
1352 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
1353
1354 T token;
1355 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
1356 FILL_PARAMS fill;
1357
1358 std::unique_ptr<SCH_SHAPE> bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER, LAYER_DEVICE );
1359
1360 bezier->SetUnit( m_unit );
1361 bezier->SetBodyStyle( m_bodyStyle );
1362
1363 token = NextTok();
1364
1365 if( token == T_private )
1366 {
1367 bezier->SetPrivate( true );
1368 token = NextTok();
1369 }
1370
1371 for( ; token != T_RIGHT; token = NextTok() )
1372 {
1373 if( token != T_LEFT )
1374 Expecting( T_LEFT );
1375
1376 token = NextTok();
1377
1378 switch( token )
1379 {
1380 case T_pts:
1381 {
1382 int ii = 0;
1383
1384 for( token = NextTok(); token != T_RIGHT; token = NextTok(), ++ii )
1385 {
1386 if( token != T_LEFT )
1387 Expecting( T_LEFT );
1388
1389 token = NextTok();
1390
1391 if( token != T_xy )
1392 Expecting( "xy" );
1393
1394 switch( ii )
1395 {
1396 case 0: bezier->SetStart( parseXY( true ) ); break;
1397 case 1: bezier->SetBezierC1( parseXY( true ) ); break;
1398 case 2: bezier->SetBezierC2( parseXY( true ) ); break;
1399 case 3: bezier->SetEnd( parseXY( true ) ); break;
1400 default: Unexpected( "control point" ); break;
1401 }
1402
1403 NeedRIGHT();
1404 }
1405 }
1406 break;
1407
1408 case T_stroke:
1409 parseStroke( stroke );
1410 bezier->SetStroke( stroke );
1411 break;
1412
1413 case T_fill:
1414 parseFill( fill );
1415 bezier->SetFillMode( fill.m_FillType );
1416 bezier->SetFillColor( fill.m_Color );
1417 break;
1418
1419 default:
1420 Expecting( "pts, stroke, or fill" );
1421 }
1422 }
1423
1424 bezier->RebuildBezierToSegmentsPointsList( bezier->GetPenWidth() / 2 );
1425
1426 return bezier.release();
1427}
1428
1429
1431{
1432 wxCHECK_MSG( CurTok() == T_circle, nullptr,
1433 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle." ) );
1434
1435 T token;
1436 VECTOR2I center( 0, 0 );
1437 int radius = 1; // defaulting to 0 could result in troublesome math....
1438 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
1439 FILL_PARAMS fill;
1440
1441 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE, LAYER_DEVICE );
1442
1443 circle->SetUnit( m_unit );
1444 circle->SetBodyStyle( m_bodyStyle );
1445
1446 token = NextTok();
1447
1448 if( token == T_private )
1449 {
1450 circle->SetPrivate( true );
1451 token = NextTok();
1452 }
1453
1454 for( ; token != T_RIGHT; token = NextTok() )
1455 {
1456 if( token != T_LEFT )
1457 Expecting( T_LEFT );
1458
1459 token = NextTok();
1460
1461 switch( token )
1462 {
1463 case T_center:
1464 center = parseXY( true );
1465 NeedRIGHT();
1466 break;
1467
1468 case T_radius:
1469 radius = parseInternalUnits( "radius length" );
1470 NeedRIGHT();
1471 break;
1472
1473 case T_stroke:
1474 parseStroke( stroke );
1475 circle->SetStroke( stroke );
1476 break;
1477
1478 case T_fill:
1479 parseFill( fill );
1480 circle->SetFillMode( fill.m_FillType );
1481 circle->SetFillColor( fill.m_Color );
1482 break;
1483
1484 default:
1485 Expecting( "center, radius, stroke, or fill" );
1486 }
1487 }
1488
1489 circle->SetCenter( center );
1490 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
1491
1492 return circle.release();
1493}
1494
1495
1497{
1498 auto parseType =
1499 [&]( T token ) -> ELECTRICAL_PINTYPE
1500 {
1501 switch( token )
1502 {
1503 case T_input: return ELECTRICAL_PINTYPE::PT_INPUT;
1504 case T_output: return ELECTRICAL_PINTYPE::PT_OUTPUT;
1505 case T_bidirectional: return ELECTRICAL_PINTYPE::PT_BIDI;
1506 case T_tri_state: return ELECTRICAL_PINTYPE::PT_TRISTATE;
1507 case T_passive: return ELECTRICAL_PINTYPE::PT_PASSIVE;
1508 case T_unspecified: return ELECTRICAL_PINTYPE::PT_UNSPECIFIED;
1509 case T_power_in: return ELECTRICAL_PINTYPE::PT_POWER_IN;
1510 case T_power_out: return ELECTRICAL_PINTYPE::PT_POWER_OUT;
1511 case T_open_collector: return ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR;
1512 case T_open_emitter: return ELECTRICAL_PINTYPE::PT_OPENEMITTER;
1513 case T_unconnected:
1514 case T_no_connect: return ELECTRICAL_PINTYPE::PT_NC;
1515 case T_free: return ELECTRICAL_PINTYPE::PT_NIC;
1516
1517 default:
1518 Expecting( "input, output, bidirectional, tri_state, passive, unspecified, "
1519 "power_in, power_out, open_collector, open_emitter, free or "
1520 "no_connect" );
1521 return ELECTRICAL_PINTYPE::PT_UNSPECIFIED;
1522 }
1523 };
1524
1525 auto parseShape =
1526 [&]( T token ) -> GRAPHIC_PINSHAPE
1527 {
1528 switch( token )
1529 {
1530 case T_line: return GRAPHIC_PINSHAPE::LINE;
1531 case T_inverted: return GRAPHIC_PINSHAPE::INVERTED;
1532 case T_clock: return GRAPHIC_PINSHAPE::CLOCK;
1533 case T_inverted_clock: return GRAPHIC_PINSHAPE::INVERTED_CLOCK;
1534 case T_input_low: return GRAPHIC_PINSHAPE::INPUT_LOW;
1535 case T_clock_low: return GRAPHIC_PINSHAPE::CLOCK_LOW;
1536 case T_output_low: return GRAPHIC_PINSHAPE::OUTPUT_LOW;
1537 case T_edge_clock_high: return GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK;
1538 case T_non_logic: return GRAPHIC_PINSHAPE::NONLOGIC;
1539
1540 default:
1541 Expecting( "line, inverted, clock, inverted_clock, input_low, clock_low, "
1542 "output_low, edge_clock_high, non_logic" );
1543 return GRAPHIC_PINSHAPE::LINE;
1544 }
1545 };
1546
1547 wxCHECK_MSG( CurTok() == T_pin, nullptr,
1548 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a pin token." ) );
1549
1550 T token;
1551 wxString tmp;
1552 wxString error;
1553 std::unique_ptr<SCH_PIN> pin = std::make_unique<SCH_PIN>( nullptr );
1554
1555 pin->SetUnit( m_unit );
1556 pin->SetBodyStyle( m_bodyStyle );
1557
1558 // Pin electrical type.
1559 token = NextTok();
1560 pin->SetType( parseType( token ) );
1561
1562 // Pin shape.
1563 token = NextTok();
1564 pin->SetShape( parseShape( token ) );
1565
1566 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1567 {
1568 // Pre-2024104 format (bare 'hide' keyword)
1569 if( token == T_hide )
1570 {
1571 pin->SetVisible( false );
1572 continue;
1573 }
1574
1575 if( token != T_LEFT )
1576 Expecting( T_LEFT );
1577
1578 token = NextTok();
1579
1580 switch( token )
1581 {
1582 case T_at:
1583 pin->SetPosition( parseXY( true ) );
1584
1585 switch( parseInt( "pin orientation" ) )
1586 {
1587 case 0: pin->SetOrientation( PIN_ORIENTATION::PIN_RIGHT ); break;
1588 case 90: pin->SetOrientation( PIN_ORIENTATION::PIN_UP ); break;
1589 case 180: pin->SetOrientation( PIN_ORIENTATION::PIN_LEFT ); break;
1590 case 270: pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN ); break;
1591 default: Expecting( "0, 90, 180, or 270" );
1592 }
1593
1594 NeedRIGHT();
1595 break;
1596
1597 case T_length:
1598 pin->SetLength( parseInternalUnits( "pin length" ) );
1599 NeedRIGHT();
1600 break;
1601
1602 case T_hide:
1603 pin->SetVisible( !parseBool() );
1604 NeedRIGHT();
1605 break;
1606
1607 case T_name:
1608 token = NextTok();
1609
1610 if( !IsSymbol( token ) )
1611 {
1612 THROW_PARSE_ERROR( _( "Invalid pin name" ), CurSource(), CurLine(), CurLineNumber(),
1613 CurOffset() );
1614 }
1615
1616 if( m_requiredVersion < 20210606 )
1617 pin->SetName( ConvertToNewOverbarNotation( FromUTF8() ) );
1618 else
1619 pin->SetName( FromUTF8() );
1620
1621 token = NextTok();
1622
1623 if( token != T_RIGHT )
1624 {
1625 token = NextTok();
1626
1627 if( token == T_effects )
1628 {
1629 // The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
1630 // so duplicate parsing is not required.
1632
1633 parseEDA_TEXT( &text, true, false );
1634 pin->SetNameTextSize( text.GetTextHeight() );
1635 NeedRIGHT();
1636 }
1637 else
1638 {
1639 Expecting( "effects" );
1640 }
1641 }
1642
1643 break;
1644
1645 case T_number:
1646 token = NextTok();
1647
1648 if( !IsSymbol( token ) )
1649 {
1650 THROW_PARSE_ERROR( _( "Invalid pin number" ), CurSource(), CurLine(),
1651 CurLineNumber(), CurOffset() );
1652 }
1653
1654 pin->SetNumber( FromUTF8() );
1655 token = NextTok();
1656
1657 if( token != T_RIGHT )
1658 {
1659 token = NextTok();
1660
1661 if( token == T_effects )
1662 {
1663 // The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
1664 // so duplicate parsing is not required.
1666
1667 parseEDA_TEXT( &text, false, false );
1668 pin->SetNumberTextSize( text.GetTextHeight() );
1669 NeedRIGHT();
1670 }
1671 else
1672 {
1673 Expecting( "effects" );
1674 }
1675 }
1676
1677 break;
1678
1679 case T_alternate:
1680 {
1681 SCH_PIN::ALT alt;
1682
1683 token = NextTok();
1684
1685 if( !IsSymbol( token ) )
1686 {
1687 THROW_PARSE_ERROR( _( "Invalid alternate pin name" ), CurSource(), CurLine(),
1688 CurLineNumber(), CurOffset() );
1689 }
1690
1691 alt.m_Name = FromUTF8();
1692
1693 token = NextTok();
1694 alt.m_Type = parseType( token );
1695
1696 token = NextTok();
1697 alt.m_Shape = parseShape( token );
1698
1699 pin->GetAlternates()[ alt.m_Name ] = alt;
1700
1701 NeedRIGHT();
1702 break;
1703 }
1704
1705 default:
1706 Expecting( "at, name, number, hide, length, or alternate" );
1707 }
1708 }
1709
1710 return pin.release();
1711}
1712
1713
1715{
1716 wxCHECK_MSG( CurTok() == T_polyline, nullptr,
1717 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a poly." ) );
1718
1719 T token;
1720 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
1721 FILL_PARAMS fill;
1722 std::unique_ptr<SCH_SHAPE> poly = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_DEVICE );
1723
1724 poly->SetUnit( m_unit );
1725 poly->SetBodyStyle( m_bodyStyle );
1726
1727 token = NextTok();
1728
1729 if( token == T_private )
1730 {
1731 poly->SetPrivate( true );
1732 token = NextTok();
1733 }
1734
1735 for( ; token != T_RIGHT; token = NextTok() )
1736 {
1737 if( token != T_LEFT )
1738 Expecting( T_LEFT );
1739
1740 token = NextTok();
1741
1742 switch( token )
1743 {
1744 case T_pts:
1745 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1746 {
1747 if( token != T_LEFT )
1748 Expecting( T_LEFT );
1749
1750 token = NextTok();
1751
1752 if( token != T_xy )
1753 Expecting( "xy" );
1754
1755 poly->AddPoint( parseXY( true ) );
1756
1757 NeedRIGHT();
1758 }
1759
1760 break;
1761
1762 case T_stroke:
1763 parseStroke( stroke );
1764 poly->SetStroke( stroke );
1765 break;
1766
1767 case T_fill:
1768 parseFill( fill );
1769 poly->SetFillMode( fill.m_FillType );
1770 poly->SetFillColor( fill.m_Color );
1771 break;
1772
1773 default:
1774 Expecting( "pts, stroke, or fill" );
1775 }
1776 }
1777
1778 return poly.release();
1779}
1780
1781
1783{
1784 wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
1785 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle." ) );
1786
1787 T token;
1788 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
1789 FILL_PARAMS fill;
1790 auto rectangle = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE, LAYER_DEVICE );
1791
1792 rectangle->SetUnit( m_unit );
1793 rectangle->SetBodyStyle( m_bodyStyle );
1794
1795 token = NextTok();
1796
1797 if( token == T_private )
1798 {
1799 rectangle->SetPrivate( true );
1800 token = NextTok();
1801 }
1802
1803 for( ; token != T_RIGHT; token = NextTok() )
1804 {
1805 if( token != T_LEFT )
1806 Expecting( T_LEFT );
1807
1808 token = NextTok();
1809
1810 switch( token )
1811 {
1812 case T_start:
1813 rectangle->SetPosition( parseXY( true ) );
1814 NeedRIGHT();
1815 break;
1816
1817 case T_end:
1818 rectangle->SetEnd( parseXY( true ) );
1819 NeedRIGHT();
1820 break;
1821
1822 case T_stroke:
1823 parseStroke( stroke );
1824 rectangle->SetStroke( stroke );
1825 break;
1826
1827 case T_fill:
1828 parseFill( fill );
1829 rectangle->SetFillMode( fill.m_FillType );
1830 rectangle->SetFillColor( fill.m_Color );
1831 break;
1832
1833 default:
1834 Expecting( "start, end, stroke, or fill" );
1835 }
1836 }
1837
1838 return rectangle.release();
1839}
1840
1841
1843{
1844 wxCHECK_MSG( CurTok() == T_text, nullptr,
1845 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text token." ) );
1846
1847 T token;
1848 std::unique_ptr<SCH_TEXT> text = std::make_unique<SCH_TEXT>();
1849
1850 text->SetLayer( LAYER_DEVICE );
1851 text->SetUnit( m_unit );
1852 text->SetBodyStyle( m_bodyStyle );
1853 token = NextTok();
1854
1855 if( token == T_private )
1856 {
1857 text->SetPrivate( true );
1858 token = NextTok();
1859 }
1860
1861 if( !IsSymbol( token ) )
1862 {
1863 THROW_PARSE_ERROR( _( "Invalid text string" ), CurSource(), CurLine(), CurLineNumber(),
1864 CurOffset() );
1865 }
1866
1867 text->SetText( FromUTF8() );
1868
1869 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1870 {
1871 if( token != T_LEFT )
1872 Expecting( T_LEFT );
1873
1874 token = NextTok();
1875
1876 switch( token )
1877 {
1878 case T_at:
1879 text->SetPosition( parseXY( true ) );
1880 // Yes, LIB_TEXT is really decidegrees even though all the others are degrees. :(
1881 text->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), TENTHS_OF_A_DEGREE_T ) );
1882 NeedRIGHT();
1883 break;
1884
1885 case T_effects:
1886 parseEDA_TEXT( text.get(), true );
1887 break;
1888
1889 default:
1890 Expecting( "at or effects" );
1891 }
1892 }
1893
1894 return text.release();
1895}
1896
1897
1899{
1900 wxCHECK_MSG( CurTok() == T_text_box, nullptr,
1901 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text box." ) );
1902
1903 T token;
1904 VECTOR2I pos;
1905 VECTOR2I end;
1906 VECTOR2I size;
1907 int left;
1908 int top;
1909 int right;
1910 int bottom;
1911 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
1912 FILL_PARAMS fill;
1913 bool foundEnd = false;
1914 bool foundSize = false;
1915 bool foundMargins = false;
1916
1917 std::unique_ptr<SCH_TEXTBOX> textBox = std::make_unique<SCH_TEXTBOX>( LAYER_DEVICE );
1918
1919 textBox->SetUnit( m_unit );
1920 textBox->SetBodyStyle( m_bodyStyle );
1921 token = NextTok();
1922
1923 if( token == T_private )
1924 {
1925 textBox->SetPrivate( true );
1926 token = NextTok();
1927 }
1928
1929 if( !IsSymbol( token ) )
1930 {
1931 THROW_PARSE_ERROR( _( "Invalid text string" ), CurSource(), CurLine(), CurLineNumber(),
1932 CurOffset() );
1933 }
1934
1935 textBox->SetText( FromUTF8() );
1936
1937 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1938 {
1939 if( token != T_LEFT )
1940 Expecting( T_LEFT );
1941
1942 token = NextTok();
1943
1944 switch( token )
1945 {
1946 case T_start: // Legacy token during 6.99 development; fails to handle angle
1947 pos = parseXY( true );
1948 NeedRIGHT();
1949 break;
1950
1951 case T_end: // Legacy token during 6.99 development; fails to handle angle
1952 end = parseXY( true );
1953 foundEnd = true;
1954 NeedRIGHT();
1955 break;
1956
1957 case T_at:
1958 pos = parseXY( true );
1959 textBox->SetTextAngle( EDA_ANGLE( parseDouble( "textbox angle" ), DEGREES_T ) );
1960 NeedRIGHT();
1961 break;
1962
1963 case T_size:
1964 size = parseXY( true );
1965 foundSize = true;
1966 NeedRIGHT();
1967 break;
1968
1969 case T_stroke:
1970 parseStroke( stroke );
1971 textBox->SetStroke( stroke );
1972 break;
1973
1974 case T_fill:
1975 parseFill( fill );
1976 textBox->SetFillMode( fill.m_FillType );
1977 textBox->SetFillColor( fill.m_Color );
1978 break;
1979
1980 case T_margins:
1981 parseMargins( left, top, right, bottom );
1982 textBox->SetMarginLeft( left );
1983 textBox->SetMarginTop( top );
1984 textBox->SetMarginRight( right );
1985 textBox->SetMarginBottom( bottom );
1986 foundMargins = true;
1987 NeedRIGHT();
1988 break;
1989
1990 case T_effects:
1991 parseEDA_TEXT( static_cast<EDA_TEXT*>( textBox.get() ), false );
1992 break;
1993
1994 default:
1995 Expecting( "at, size, stroke, fill or effects" );
1996 }
1997 }
1998
1999 textBox->SetPosition( pos );
2000
2001 if( foundEnd )
2002 textBox->SetEnd( end );
2003 else if( foundSize )
2004 textBox->SetEnd( pos + size );
2005 else
2006 Expecting( "size" );
2007
2008 if( !foundMargins )
2009 {
2010 int margin = textBox->GetLegacyTextMargin();
2011 textBox->SetMarginLeft( margin );
2012 textBox->SetMarginTop( margin );
2013 textBox->SetMarginRight( margin );
2014 textBox->SetMarginBottom( margin );
2015 }
2016
2017 return textBox.release();
2018}
2019
2020
2022{
2023 wxCHECK_RET( ( CurTok() == T_page && m_requiredVersion <= 20200506 ) || CurTok() == T_paper,
2024 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a PAGE_INFO." ) );
2025
2026 T token;
2027
2028 NeedSYMBOL();
2029
2030 wxString pageType = FromUTF8();
2031
2032 if( !aPageInfo.SetType( pageType ) )
2033 {
2034 THROW_PARSE_ERROR( _( "Invalid page type" ), CurSource(), CurLine(), CurLineNumber(),
2035 CurOffset() );
2036 }
2037
2038 if( pageType == PAGE_INFO::Custom )
2039 {
2040 double width = parseDouble( "width" );
2041
2042 // Perform some controls to avoid crashes if the size is edited by hands
2043 if( width < MIN_PAGE_SIZE_MM )
2044 width = MIN_PAGE_SIZE_MM;
2045 else if( width > MAX_PAGE_SIZE_EESCHEMA_MM )
2047
2048 double height = parseDouble( "height" );
2049
2050 if( height < MIN_PAGE_SIZE_MM )
2051 height = MIN_PAGE_SIZE_MM;
2052 else if( height > MAX_PAGE_SIZE_EESCHEMA_MM )
2054
2055 aPageInfo.SetWidthMM( width );
2056 aPageInfo.SetHeightMM( height );
2057 }
2058
2059 token = NextTok();
2060
2061 if( token == T_portrait )
2062 {
2063 aPageInfo.SetPortrait( true );
2064 NeedRIGHT();
2065 }
2066 else if( token != T_RIGHT )
2067 {
2068 Expecting( "portrait" );
2069 }
2070}
2071
2072
2074{
2075 wxCHECK_RET( CurTok() == T_title_block,
2076 "Cannot parse " + GetTokenString( CurTok() ) + " as a TITLE_BLOCK." );
2077
2078 T token;
2079
2080 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2081 {
2082 if( token != T_LEFT )
2083 Expecting( T_LEFT );
2084
2085 token = NextTok();
2086
2087 switch( token )
2088 {
2089 case T_title:
2090 NextTok();
2091 aTitleBlock.SetTitle( FromUTF8() );
2092 break;
2093
2094 case T_date:
2095 NextTok();
2096 aTitleBlock.SetDate( FromUTF8() );
2097 break;
2098
2099 case T_rev:
2100 NextTok();
2101 aTitleBlock.SetRevision( FromUTF8() );
2102 break;
2103
2104 case T_company:
2105 NextTok();
2106 aTitleBlock.SetCompany( FromUTF8() );
2107 break;
2108
2109 case T_comment:
2110 {
2111 int commentNumber = parseInt( "comment" );
2112
2113 switch( commentNumber )
2114 {
2115 case 1:
2116 NextTok();
2117 aTitleBlock.SetComment( 0, FromUTF8() );
2118 break;
2119
2120 case 2:
2121 NextTok();
2122 aTitleBlock.SetComment( 1, FromUTF8() );
2123 break;
2124
2125 case 3:
2126 NextTok();
2127 aTitleBlock.SetComment( 2, FromUTF8() );
2128 break;
2129
2130 case 4:
2131 NextTok();
2132 aTitleBlock.SetComment( 3, FromUTF8() );
2133 break;
2134
2135 case 5:
2136 NextTok();
2137 aTitleBlock.SetComment( 4, FromUTF8() );
2138 break;
2139
2140 case 6:
2141 NextTok();
2142 aTitleBlock.SetComment( 5, FromUTF8() );
2143 break;
2144
2145 case 7:
2146 NextTok();
2147 aTitleBlock.SetComment( 6, FromUTF8() );
2148 break;
2149
2150 case 8:
2151 NextTok();
2152 aTitleBlock.SetComment( 7, FromUTF8() );
2153 break;
2154
2155 case 9:
2156 NextTok();
2157 aTitleBlock.SetComment( 8, FromUTF8() );
2158 break;
2159
2160 default:
2161 THROW_PARSE_ERROR( _( "Invalid title block comment number" ), CurSource(),
2162 CurLine(), CurLineNumber(), CurOffset() );
2163 }
2164
2165 break;
2166 }
2167
2168 default:
2169 Expecting( "title, date, rev, company, or comment" );
2170 }
2171
2172 NeedRIGHT();
2173 }
2174}
2175
2176
2178{
2179 wxCHECK_MSG( CurTok() == T_property, nullptr,
2180 "Cannot parse " + GetTokenString( CurTok() ) + " as a property token." );
2181
2182 bool is_private = false;
2183
2184 T token = NextTok();
2185
2186 if( token == T_private )
2187 {
2188 is_private = true;
2189 token = NextTok();
2190 }
2191
2192 if( !IsSymbol( token ) )
2193 {
2194 THROW_PARSE_ERROR( _( "Invalid property name" ), CurSource(), CurLine(), CurLineNumber(),
2195 CurOffset() );
2196 }
2197
2198 wxString name = FromUTF8();
2199
2200 if( name.IsEmpty() )
2201 {
2202 THROW_PARSE_ERROR( _( "Empty property name" ), CurSource(), CurLine(), CurLineNumber(),
2203 CurOffset() );
2204 }
2205
2206 token = NextTok();
2207
2208 if( !IsSymbol( token ) )
2209 {
2210 THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
2211 CurOffset() );
2212 }
2213
2214 // Empty property values are valid.
2215 wxString value = FromUTF8();
2216
2217 int mandatoryFieldCount = 0;
2218
2219 if( aParent->Type() == SCH_SYMBOL_T )
2220 mandatoryFieldCount = MANDATORY_FIELDS;
2221 else if( aParent->Type() == SCH_SHEET_T )
2222 mandatoryFieldCount = SHEET_MANDATORY_FIELDS;
2223
2224 std::unique_ptr<SCH_FIELD> field = std::make_unique<SCH_FIELD>( VECTOR2I( -1, -1 ),
2225 mandatoryFieldCount,
2226 aParent, name );
2227 field->SetText( value );
2228 field->SetVisible( true );
2229 field->SetPrivate( is_private );
2230
2231 // Correctly set the ID based on canonical (untranslated) field name
2232 // If ID is stored in the file (old versions), it will overwrite this
2233 if( aParent->Type() == SCH_SYMBOL_T )
2234 {
2235 for( int ii = 0; ii < MANDATORY_FIELDS; ++ii )
2236 {
2237 if( name == GetCanonicalFieldName( ii ) )
2238 {
2239 field->SetId( ii );
2240 field->SetPrivate( false );
2241 break;
2242 }
2243 }
2244 }
2245 else if( aParent->Type() == SCH_SHEET_T )
2246 {
2247 for( int ii = 0; ii < SHEET_MANDATORY_FIELDS; ++ii )
2248 {
2249 if( name == SCH_SHEET::GetDefaultFieldName( ii, false ) )
2250 {
2251 field->SetId( ii );
2252 field->SetPrivate( false );
2253 break;
2254 }
2255 // Legacy support for old field names
2256 else if( !name.CmpNoCase( wxT( "Sheet name" ) ) )
2257 {
2258 field->SetId( SHEETNAME );
2259 break;
2260 }
2261 else if( !name.CmpNoCase( wxT( "Sheet file" ) ) )
2262 {
2263 field->SetId( SHEETFILENAME );
2264 break;
2265 }
2266 }
2267 }
2268
2269 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2270 {
2271 if( token != T_LEFT )
2272 Expecting( T_LEFT );
2273
2274 token = NextTok();
2275
2276 switch( token )
2277 {
2278 // I am not sure we should even support parsing these IDs any more
2279 case T_id:
2280 {
2281 int id = parseInt( "field ID" );
2282 // Only set an ID that isn't a MANDATORY_FIELDS ID
2283 if( id >= mandatoryFieldCount )
2284 field->SetId( id );
2285 NeedRIGHT();
2286 }
2287 break;
2288
2289 case T_at:
2290 field->SetPosition( parseXY() );
2291 field->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ) );
2292 NeedRIGHT();
2293 break;
2294
2295 case T_effects:
2296 parseEDA_TEXT( static_cast<EDA_TEXT*>( field.get() ), field->GetId() == VALUE_FIELD );
2297 break;
2298
2299 case T_show_name:
2300 {
2301 bool show = parseMaybeAbsentBool( true );
2302 field->SetNameShown( show );
2303 break;
2304 }
2305
2306 case T_do_not_autoplace:
2307 {
2308 bool doNotAutoplace = parseMaybeAbsentBool( true );
2309 field->SetCanAutoplace( !doNotAutoplace );
2310 break;
2311 }
2312
2313 default:
2314 Expecting( "id, at, show_name, do_not_autoplace or effects" );
2315 }
2316 }
2317
2318 return field.release();
2319}
2320
2321
2323{
2324 wxCHECK_MSG( aSheet != nullptr, nullptr, "" );
2325 wxCHECK_MSG( CurTok() == T_pin, nullptr,
2326 "Cannot parse " + GetTokenString( CurTok() ) + " as a sheet pin token." );
2327
2328 T token = NextTok();
2329
2330 if( !IsSymbol( token ) )
2331 {
2332 THROW_PARSE_ERROR( _( "Invalid sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
2333 CurOffset() );
2334 }
2335
2336 wxString name = FromUTF8();
2337
2338 if( name.IsEmpty() )
2339 {
2340 THROW_PARSE_ERROR( _( "Empty sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
2341 CurOffset() );
2342 }
2343
2344 auto sheetPin = std::make_unique<SCH_SHEET_PIN>( aSheet, VECTOR2I( 0, 0 ), name );
2345
2346 token = NextTok();
2347
2348 switch( token )
2349 {
2350 case T_input: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
2351 case T_output: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
2352 case T_bidirectional: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
2353 case T_tri_state: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE ); break;
2354 case T_passive: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
2355 default:
2356 Expecting( "input, output, bidirectional, tri_state, or passive" );
2357 }
2358
2359 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2360 {
2361 if( token != T_LEFT )
2362 Expecting( T_LEFT );
2363
2364 token = NextTok();
2365
2366 switch( token )
2367 {
2368 case T_at:
2369 {
2370 sheetPin->SetPosition( parseXY() );
2371
2372 double angle = parseDouble( "sheet pin angle (side)" );
2373
2374 if( angle == 0.0 )
2375 sheetPin->SetSide( SHEET_SIDE::RIGHT );
2376 else if( angle == 90.0 )
2377 sheetPin->SetSide( SHEET_SIDE::TOP );
2378 else if( angle == 180.0 )
2379 sheetPin->SetSide( SHEET_SIDE::LEFT );
2380 else if( angle == 270.0 )
2381 sheetPin->SetSide( SHEET_SIDE::BOTTOM );
2382 else
2383 Expecting( "0, 90, 180, or 270" );
2384
2385 NeedRIGHT();
2386 break;
2387 }
2388
2389 case T_effects:
2390 parseEDA_TEXT( static_cast<EDA_TEXT*>( sheetPin.get() ), true );
2391 break;
2392
2393 case T_uuid:
2394 NeedSYMBOL();
2395 const_cast<KIID&>( sheetPin->m_Uuid ) = parseKIID();
2396 NeedRIGHT();
2397 break;
2398
2399 default:
2400 Expecting( "at, uuid or effects" );
2401 }
2402 }
2403
2404 return sheetPin.release();
2405}
2406
2407
2409{
2410 wxCHECK_RET( CurTok() == T_sheet_instances,
2411 "Cannot parse " + GetTokenString( CurTok() ) + " as an instances token." );
2412 wxCHECK( aScreen, /* void */ );
2413
2414 T token;
2415
2416 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2417 {
2418 if( token != T_LEFT )
2419 Expecting( T_LEFT );
2420
2421 token = NextTok();
2422
2423 switch( token )
2424 {
2425 case T_path:
2426 {
2427 NeedSYMBOL();
2428
2429 SCH_SHEET_INSTANCE instance;
2430
2431 instance.m_Path = KIID_PATH( FromUTF8() );
2432
2433 if( ( !m_appending && aRootSheet->GetScreen() == aScreen ) &&
2434 ( aScreen->GetFileFormatVersionAtLoad() < 20221002 ) )
2435 instance.m_Path.insert( instance.m_Path.begin(), m_rootUuid );
2436
2437 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2438 {
2439 if( token != T_LEFT )
2440 Expecting( T_LEFT );
2441
2442 token = NextTok();
2443
2444 std::vector<wxString> whitespaces = { wxT( "\r" ), wxT( "\n" ), wxT( "\t" ),
2445 wxT( " " ) };
2446
2447 size_t numReplacements = 0;
2448
2449 switch( token )
2450 {
2451 case T_page:
2452 NeedSYMBOL();
2453 instance.m_PageNumber = FromUTF8();
2454
2455 // Empty page numbers are not permitted
2456 if( instance.m_PageNumber.IsEmpty() )
2457 {
2458 // Use hash character instead
2459 instance.m_PageNumber = wxT( "#" );
2460 numReplacements++;
2461 }
2462 else
2463 {
2464 // Whitespaces are not permitted
2465 for( const wxString& ch : whitespaces )
2466 numReplacements += instance.m_PageNumber.Replace( ch, wxEmptyString );
2467
2468 }
2469
2470 // Set the file as modified so the user can be warned.
2471 if( numReplacements > 0 )
2472 aScreen->SetContentModified();
2473
2474 NeedRIGHT();
2475 break;
2476
2477 default:
2478 Expecting( "path or page" );
2479 }
2480 }
2481
2482 if( ( aScreen->GetFileFormatVersionAtLoad() >= 20221110 )
2483 && ( instance.m_Path.empty() ) )
2484 {
2485 SCH_SHEET_PATH rootSheetPath;
2486
2487 rootSheetPath.push_back( aRootSheet );
2488 rootSheetPath.SetPageNumber( instance.m_PageNumber );
2489 }
2490 else
2491 {
2492 aScreen->m_sheetInstances.emplace_back( instance );
2493 }
2494
2495 break;
2496 }
2497
2498 default:
2499 Expecting( "path" );
2500 }
2501 }
2502}
2503
2504
2506{
2507 wxCHECK_RET( CurTok() == T_symbol_instances,
2508 "Cannot parse " + GetTokenString( CurTok() ) + " as an instances token." );
2509 wxCHECK( aScreen, /* void */ );
2510 wxCHECK( m_rootUuid != NilUuid(), /* void */ );
2511
2512 T token;
2513
2514 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2515 {
2516 if( token != T_LEFT )
2517 Expecting( T_LEFT );
2518
2519 token = NextTok();
2520
2521 switch( token )
2522 {
2523 case T_path:
2524 {
2525 NeedSYMBOL();
2526
2527 SCH_SYMBOL_INSTANCE instance;
2528
2529 instance.m_Path = KIID_PATH( FromUTF8() );
2530
2531 if( !m_appending )
2532 instance.m_Path.insert( instance.m_Path.begin(), m_rootUuid );
2533
2534 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2535 {
2536 if( token != T_LEFT )
2537 Expecting( T_LEFT );
2538
2539 token = NextTok();
2540
2541 switch( token )
2542 {
2543 case T_reference:
2544 NeedSYMBOL();
2545 instance.m_Reference = FromUTF8();
2546 NeedRIGHT();
2547 break;
2548
2549 case T_unit:
2550 instance.m_Unit = parseInt( "symbol unit" );
2551 NeedRIGHT();
2552 break;
2553
2554 case T_value:
2555 NeedSYMBOL();
2556 instance.m_Value = FromUTF8();
2557 NeedRIGHT();
2558 break;
2559
2560 case T_footprint:
2561 NeedSYMBOL();
2562 instance.m_Footprint = FromUTF8();
2563 NeedRIGHT();
2564 break;
2565
2566 default:
2567 Expecting( "path, unit, value or footprint" );
2568 }
2569 }
2570
2571 aScreen->m_symbolInstances.emplace_back( instance );
2572 break;
2573 }
2574
2575 default:
2576 Expecting( "path" );
2577 }
2578 }
2579}
2580
2581
2582void SCH_IO_KICAD_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly,
2583 int aFileVersion )
2584{
2585 wxCHECK( aSheet != nullptr, /* void */ );
2586
2587 SCH_SCREEN* screen = aSheet->GetScreen();
2588
2589 wxCHECK( screen != nullptr, /* void */ );
2590
2591 if( aIsCopyableOnly )
2592 m_requiredVersion = aFileVersion;
2593
2594 bool fileHasUuid = false;
2595
2596 auto checkVersion =
2597 [&]()
2598 {
2600 {
2601 throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
2603 }
2604 };
2605
2606 T token;
2607
2608 if( !aIsCopyableOnly )
2609 {
2610 NeedLEFT();
2611 NextTok();
2612
2613 if( CurTok() != T_kicad_sch )
2614 Expecting( "kicad_sch" );
2615
2617
2618 // Prior to schematic file version 20210406, schematics did not have UUIDs so we need
2619 // to generate one for the root schematic for instance paths.
2620 if( m_requiredVersion < 20210406 )
2621 m_rootUuid = screen->GetUuid();
2622
2623 // Prior to version 20231120, generator_version was not present, so check the date here
2624 if( m_requiredVersion < 20231120 )
2625 checkVersion();
2626 }
2627
2629
2630 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2631 {
2632 if( aIsCopyableOnly && token == T_EOF )
2633 break;
2634
2635 if( token != T_LEFT )
2636 Expecting( T_LEFT );
2637
2638 token = NextTok();
2639
2640 checkpoint();
2641
2642 if( !aIsCopyableOnly && token == T_page && m_requiredVersion <= 20200506 )
2643 token = T_paper;
2644
2645 switch( token )
2646 {
2647 case T_generator:
2648 // (generator "genname"); we don't care about it at the moment.
2649 NeedSYMBOL();
2650 NeedRIGHT();
2651 break;
2652
2653 case T_host:
2654 {
2655 // (host eeschema ["5.99"]); old version of generator token
2656 NeedSYMBOL();
2657
2658 // Really old versions also included a host version
2659 if( m_requiredVersion < 20200827 )
2660 NeedSYMBOL();
2661
2662 NeedRIGHT();
2663 break;
2664 }
2665
2666 case T_generator_version:
2667 NextTok();
2668 m_generatorVersion = FromUTF8();
2669 NeedRIGHT();
2670 checkVersion();
2671 break;
2672
2673 case T_uuid:
2674 NeedSYMBOL();
2675 screen->m_uuid = parseKIID();
2676
2677 // Set the root sheet UUID with the schematic file UUID. Root sheets are virtual
2678 // and always get a new UUID so this prevents file churn now that the root UUID
2679 // is saved in the symbol instance path.
2680 if( aSheet == m_rootSheet )
2681 {
2682 const_cast<KIID&>( aSheet->m_Uuid ) = screen->GetUuid();
2683 m_rootUuid = screen->GetUuid();
2684 fileHasUuid = true;
2685 }
2686
2687 NeedRIGHT();
2688 break;
2689
2690 case T_paper:
2691 {
2692 if( aIsCopyableOnly )
2693 Unexpected( T_paper );
2694
2695 PAGE_INFO pageInfo;
2696 parsePAGE_INFO( pageInfo );
2697 screen->SetPageSettings( pageInfo );
2698 break;
2699 }
2700
2701 case T_page:
2702 {
2703 if( aIsCopyableOnly )
2704 Unexpected( T_page );
2705
2706 // Only saved for top-level sniffing in Kicad Manager frame and other external
2707 // tool usage with flat hierarchies
2708 NeedSYMBOLorNUMBER();
2709 NeedSYMBOLorNUMBER();
2710 NeedRIGHT();
2711 break;
2712 }
2713
2714 case T_title_block:
2715 {
2716 if( aIsCopyableOnly )
2717 Unexpected( T_title_block );
2718
2719 TITLE_BLOCK tb;
2720 parseTITLE_BLOCK( tb );
2721 screen->SetTitleBlock( tb );
2722 break;
2723 }
2724
2725 case T_lib_symbols:
2726 {
2727 // Dummy map. No derived symbols are allowed in the library cache.
2728 LIB_SYMBOL_MAP symbolLibMap;
2729 LIB_SYMBOL* symbol;
2730
2731 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2732 {
2733 if( token != T_LEFT )
2734 Expecting( T_LEFT );
2735
2736 token = NextTok();
2737
2738 switch( token )
2739 {
2740 case T_symbol:
2741 symbol = parseLibSymbol( symbolLibMap );
2742 symbol->UpdateFieldOrdinals();
2743 screen->AddLibSymbol( symbol );
2744 break;
2745
2746 default:
2747 Expecting( "symbol" );
2748 }
2749 }
2750
2751 break;
2752 }
2753
2754 case T_symbol:
2755 screen->Append( static_cast<SCH_ITEM*>( parseSchematicSymbol() ) );
2756 break;
2757
2758 case T_image:
2759 screen->Append( static_cast<SCH_ITEM*>( parseImage() ) );
2760 break;
2761
2762 case T_sheet:
2763 {
2764 SCH_SHEET* sheet = parseSheet();
2765
2766 // Set the parent to aSheet. This effectively creates a method to find
2767 // the root sheet from any sheet so a pointer to the root sheet does not
2768 // need to be stored globally. Note: this is not the same as a hierarchy.
2769 // Complex hierarchies can have multiple copies of a sheet. This only
2770 // provides a simple tree to find the root sheet.
2771 sheet->SetParent( aSheet );
2772 screen->Append( sheet );
2773 break;
2774 }
2775
2776 case T_junction:
2777 screen->Append( parseJunction() );
2778 break;
2779
2780 case T_no_connect:
2781 screen->Append( parseNoConnect() );
2782 break;
2783
2784 case T_bus_entry:
2785 screen->Append( parseBusEntry() );
2786 break;
2787
2788 case T_polyline:
2789 {
2790 // polyline keyword is used in eeschema both for SCH_SHAPE and SCH_LINE items.
2791 // In symbols it describes a polygon, having n corners and can be filled
2792 // In schematic it describes a line (with no fill descr), but could be extended to a
2793 // polygon (for instance when importing files) because the schematic handles all
2794 // SCH_SHAPE.
2795
2796 // parseSchPolyLine() returns always a SCH_SHAPE, io convert it to a simple SCH_LINE
2797 // For compatibility reasons, keep SCH_SHAPE for a polygon and convert to SCH_LINE
2798 // when the item has only 2 corners, similar to a SCH_LINE
2799 SCH_SHAPE* poly = parseSchPolyLine();
2800
2801 if( poly->GetPointCount() > 2 )
2802 {
2803 screen->Append( poly );
2804 }
2805 else
2806 {
2807 // For SCH_SHAPE having only 2 points, this is a "old" SCH_LINE entity.
2808 // So convert the SCH_SHAPE to a simple SCH_LINE
2809 SCH_LINE* line = new SCH_LINE( VECTOR2I(), LAYER_NOTES );
2810 SHAPE_LINE_CHAIN& outline = poly->GetPolyShape().Outline(0);
2811 line->SetStartPoint( outline.CPoint(0) );
2812 line->SetEndPoint( outline.CPoint(1) );
2813 line->SetStroke( poly->GetStroke() );
2814 const_cast<KIID&>( line->m_Uuid ) = poly->m_Uuid;
2815
2816 screen->Append( line );
2817
2818 delete poly;
2819 }
2820 }
2821 break;
2822
2823 case T_bus:
2824 case T_wire:
2825 screen->Append( parseLine() );
2826 break;
2827
2828 case T_arc:
2829 screen->Append( parseSchArc() );
2830 break;
2831
2832 case T_circle:
2833 screen->Append( parseSchCircle() );
2834 break;
2835
2836 case T_rectangle:
2837 screen->Append( parseSchRectangle() );
2838 break;
2839
2840 case T_bezier:
2841 screen->Append( parseSchBezier() );
2842 break;
2843
2844 case T_rule_area:
2845 screen->Append( parseSchRuleArea() );
2846 break;
2847
2848 case T_netclass_flag: // present only during early development of 7.0
2850
2851 case T_text:
2852 case T_label:
2853 case T_global_label:
2854 case T_hierarchical_label:
2855 case T_directive_label:
2856 screen->Append( parseSchText() );
2857 break;
2858
2859 case T_text_box:
2860 screen->Append( parseSchTextBox() );
2861 break;
2862
2863 case T_table:
2864 screen->Append( parseSchTable() );
2865 break;
2866
2867 case T_sheet_instances:
2868 parseSchSheetInstances( aSheet, screen );
2869 break;
2870
2871 case T_symbol_instances:
2872 parseSchSymbolInstances( screen );
2873 break;
2874
2875 case T_bus_alias:
2876 if( aIsCopyableOnly )
2877 Unexpected( T_bus_alias );
2878
2879 parseBusAlias( screen );
2880 break;
2881
2882 case T_embedded_fonts:
2883 {
2884 SCHEMATIC* schematic = screen->Schematic();
2885
2886 if( !schematic )
2887 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
2888 CurLineNumber(), CurOffset() );
2889
2891 NeedRIGHT();
2892 break;
2893 }
2894
2895 case T_embedded_files:
2896 {
2897 SCHEMATIC* schematic = screen->Schematic();
2898
2899 if( !schematic )
2900 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
2901 CurLineNumber(), CurOffset() );
2902
2903 EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
2904 embeddedFilesParser.SyncLineReaderWith( *this );
2905
2906 try
2907 {
2908 embeddedFilesParser.ParseEmbedded( schematic->GetEmbeddedFiles() );
2909 }
2910 catch( const PARSE_ERROR& e )
2911 {
2912 wxLogError( e.What() );
2913 }
2914
2915 SyncLineReaderWith( embeddedFilesParser );
2916 break;
2917 }
2918
2919
2920 default:
2921 Expecting( "bitmap, bus, bus_alias, bus_entry, class_label, embedded_files, global_label, "
2922 "hierarchical_label, junction, label, line, no_connect, page, paper, rule_area, "
2923 "sheet, symbol, symbol_instances, text, title_block" );
2924 }
2925 }
2926
2927 // Older s-expression schematics may not have a UUID so use the one automatically generated
2928 // as the virtual root sheet UUID.
2929 if( ( aSheet == m_rootSheet ) && !fileHasUuid )
2930 {
2931 const_cast<KIID&>( aSheet->m_Uuid ) = screen->GetUuid();
2932 m_rootUuid = screen->GetUuid();
2933 }
2934
2935 screen->UpdateLocalLibSymbolLinks();
2936 screen->FixupEmbeddedData();
2937
2938 SCHEMATIC* schematic = screen->Schematic();
2939
2940 if( !schematic )
2941 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
2942 CurLineNumber(), CurOffset() );
2943
2944 // When loading the schematic, take a moment to cache the fonts so that the font
2945 // picker can show the embedded fonts immediately.
2946 std::vector<std::string> fontNames;
2947 Fontconfig()->ListFonts( fontNames, std::string( Pgm().GetLanguageTag().utf8_str() ),
2948 schematic->GetEmbeddedFiles()->GetFontFiles(), true );
2949
2950 if( m_requiredVersion < 20200828 )
2952}
2953
2954
2956{
2957 wxCHECK_MSG( CurTok() == T_symbol, nullptr,
2958 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
2959
2960 T token;
2961 wxString libName;
2962 SCH_FIELD* field;
2963 std::unique_ptr<SCH_SYMBOL> symbol = std::make_unique<SCH_SYMBOL>();
2964 TRANSFORM transform;
2965 std::set<int> fieldIDsRead;
2966
2967 // We'll reset this if we find a fields_autoplaced token
2968 symbol->SetFieldsAutoplaced( AUTOPLACE_NONE );
2969
2970 m_fieldIDsRead.clear();
2971
2972 // Make sure the mandatory field IDs are reserved as already read,
2973 // the field parser will set the field IDs to the correct value if
2974 // the field name matches a mandatory field name
2975 for( int i = 0; i < MANDATORY_FIELDS; i++ )
2976 m_fieldIDsRead.insert( i );
2977
2978 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2979 {
2980 if( token != T_LEFT )
2981 Expecting( T_LEFT );
2982
2983 token = NextTok();
2984
2985 switch( token )
2986 {
2987 case T_lib_name:
2988 {
2989 LIB_ID libId;
2990
2991 token = NextTok();
2992
2993 if( !IsSymbol( token ) )
2994 {
2995 THROW_PARSE_ERROR( _( "Invalid symbol library name" ), CurSource(), CurLine(),
2996 CurLineNumber(), CurOffset() );
2997 }
2998
2999 libName = FromUTF8();
3000 // Some symbol LIB_IDs or lib_name have the '/' character escaped which can break
3001 // symbol links. The '/' character is no longer an illegal LIB_ID character so
3002 // it doesn't need to be escaped.
3003 libName.Replace( "{slash}", "/" );
3004
3005 NeedRIGHT();
3006 break;
3007 }
3008
3009 case T_lib_id:
3010 {
3011 token = NextTok();
3012
3013 if( !IsSymbol( token ) && token != T_NUMBER )
3014 Expecting( "symbol|number" );
3015
3016 LIB_ID libId;
3017 wxString name = FromUTF8();
3018 // Some symbol LIB_IDs have the '/' character escaped which can break
3019 // symbol links. The '/' character is no longer an illegal LIB_ID character so
3020 // it doesn't need to be escaped.
3021 name.Replace( "{slash}", "/" );
3022
3023 int bad_pos = libId.Parse( name );
3024
3025 if( bad_pos >= 0 )
3026 {
3027 if( static_cast<int>( name.size() ) > bad_pos )
3028 {
3029 wxString msg = wxString::Format(
3030 _( "Symbol %s contains invalid character '%c'" ), name,
3031 name[bad_pos] );
3032
3033 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
3034 }
3035
3036 THROW_PARSE_ERROR( _( "Invalid symbol library ID" ), CurSource(), CurLine(),
3037 CurLineNumber(), CurOffset() );
3038 }
3039
3040 symbol->SetLibId( libId );
3041 NeedRIGHT();
3042 break;
3043 }
3044
3045 case T_at:
3046 symbol->SetPosition( parseXY() );
3047
3048 switch( static_cast<int>( parseDouble( "symbol orientation" ) ) )
3049 {
3050 case 0: transform = TRANSFORM(); break;
3051 case 90: transform = TRANSFORM( 0, 1, -1, 0 ); break;
3052 case 180: transform = TRANSFORM( -1, 0, 0, -1 ); break;
3053 case 270: transform = TRANSFORM( 0, -1, 1, 0 ); break;
3054 default: Expecting( "0, 90, 180, or 270" );
3055 }
3056
3057 symbol->SetTransform( transform );
3058 NeedRIGHT();
3059 break;
3060
3061 case T_mirror:
3062 token = NextTok();
3063
3064 if( token == T_x )
3065 symbol->SetOrientation( SYM_MIRROR_X );
3066 else if( token == T_y )
3067 symbol->SetOrientation( SYM_MIRROR_Y );
3068 else
3069 Expecting( "x or y" );
3070
3071 NeedRIGHT();
3072 break;
3073
3074 case T_unit:
3075 symbol->SetUnit( parseInt( "symbol unit" ) );
3076 NeedRIGHT();
3077 break;
3078
3079 case T_convert:
3080 // Use SetBodyStyleUnconditional() because the full symbol properties
3081 // (including the corresponding LIB_SYMBOL) are not known
3082 symbol->SetBodyStyleUnconditional( parseInt( "symbol body style" ) );
3083 NeedRIGHT();
3084 break;
3085
3086 case T_exclude_from_sim:
3087 symbol->SetExcludedFromSim( parseBool() );
3088 NeedRIGHT();
3089 break;
3090
3091 case T_in_bom:
3092 symbol->SetExcludedFromBOM( !parseBool() );
3093 NeedRIGHT();
3094 break;
3095
3096 case T_on_board:
3097 symbol->SetExcludedFromBoard( !parseBool() );
3098 NeedRIGHT();
3099 break;
3100
3101 case T_dnp:
3102 symbol->SetDNP( parseBool() );
3103 NeedRIGHT();
3104 break;
3105
3106 case T_fields_autoplaced:
3107 if( parseMaybeAbsentBool( true ) )
3108 symbol->SetFieldsAutoplaced( AUTOPLACE_AUTO );
3109
3110 break;
3111
3112 case T_uuid:
3113 NeedSYMBOL();
3114 const_cast<KIID&>( symbol->m_Uuid ) = parseKIID();
3115 NeedRIGHT();
3116 break;
3117
3118 case T_default_instance:
3119 {
3120 SCH_SYMBOL_INSTANCE defaultInstance;
3121
3122 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3123 {
3124 if( token != T_LEFT )
3125 Expecting( T_LEFT );
3126
3127 token = NextTok();
3128
3129 switch( token )
3130 {
3131 case T_reference:
3132 NeedSYMBOL();
3133 defaultInstance.m_Reference = FromUTF8();
3134 NeedRIGHT();
3135 break;
3136
3137 case T_unit:
3138 defaultInstance.m_Unit = parseInt( "symbol unit" );
3139 NeedRIGHT();
3140 break;
3141
3142 case T_value:
3143 NeedSYMBOL();
3144 symbol->SetValueFieldText( FromUTF8() );
3145 NeedRIGHT();
3146 break;
3147
3148 case T_footprint:
3149 NeedSYMBOL();
3150 symbol->SetFootprintFieldText( FromUTF8() );
3151 NeedRIGHT();
3152 break;
3153
3154 default:
3155 Expecting( "reference, unit, value or footprint" );
3156 }
3157 }
3158
3159 break;
3160 }
3161
3162 case T_instances:
3163 {
3164 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3165 {
3166 if( token != T_LEFT )
3167 Expecting( T_LEFT );
3168
3169 token = NextTok();
3170
3171 if( token != T_project )
3172 Expecting( "project" );
3173
3174 NeedSYMBOL();
3175
3176 wxString projectName = FromUTF8();
3177
3178 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3179 {
3180 if( token != T_LEFT )
3181 Expecting( T_LEFT );
3182
3183 token = NextTok();
3184
3185 if( token != T_path )
3186 Expecting( "path" );
3187
3188 SCH_SYMBOL_INSTANCE instance;
3189
3190 instance.m_ProjectName = projectName;
3191
3192 NeedSYMBOL();
3193 instance.m_Path = KIID_PATH( FromUTF8() );
3194
3195 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3196 {
3197 if( token != T_LEFT )
3198 Expecting( T_LEFT );
3199
3200 token = NextTok();
3201
3202 switch( token )
3203 {
3204 case T_reference:
3205 NeedSYMBOL();
3206 instance.m_Reference = FromUTF8();
3207 NeedRIGHT();
3208 break;
3209
3210 case T_unit:
3211 instance.m_Unit = parseInt( "symbol unit" );
3212 NeedRIGHT();
3213 break;
3214
3215 case T_value:
3216 NeedSYMBOL();
3217 symbol->SetValueFieldText( FromUTF8() );
3218 NeedRIGHT();
3219 break;
3220
3221 case T_footprint:
3222 NeedSYMBOL();
3223 symbol->SetFootprintFieldText( FromUTF8() );
3224 NeedRIGHT();
3225 break;
3226
3227 default:
3228 Expecting( "reference, unit, value or footprint" );
3229 }
3230
3231 symbol->AddHierarchicalReference( instance );
3232 }
3233 }
3234 }
3235
3236 break;
3237 }
3238
3239 case T_property:
3240 // The field parent symbol must be set and its orientation must be set before
3241 // the field positions are set.
3242 field = parseSchField( symbol.get() );
3243
3244 // Exclude from simulation used to be managed by a Sim.Enable field set to "0" when
3245 // simulation was disabled.
3247 {
3248 symbol->SetExcludedFromSim( field->GetText() == wxS( "0" ) );
3249 break;
3250 }
3251
3252 // Even longer ago, we had a "Spice_Netlist_Enabled" field
3254 {
3255 symbol->SetExcludedFromSim( field->GetText() == wxS( "N" ) );
3256 break;
3257 }
3258
3259 if( ( field->GetId() >= MANDATORY_FIELDS ) && m_fieldIDsRead.count( field->GetId() ) )
3260 {
3261 int nextAvailableId = field->GetId() + 1;
3262
3263 while( m_fieldIDsRead.count( nextAvailableId ) )
3264 nextAvailableId += 1;
3265
3266 field->SetId( nextAvailableId );
3267 }
3268
3269 if( symbol->GetFieldById( field->GetId() ) )
3270 *symbol->GetFieldById( field->GetId() ) = *field;
3271 else
3272 symbol->AddField( *field );
3273
3274 if( field->GetId() == REFERENCE_FIELD )
3275 symbol->UpdatePrefix();
3276
3277 m_fieldIDsRead.insert( field->GetId() );
3278
3279 delete field;
3280 break;
3281
3282 case T_pin:
3283 {
3284 // Read an alternate pin designation
3285 wxString number;
3286 KIID uuid;
3287 wxString alt;
3288
3289 NeedSYMBOL();
3290 number = FromUTF8();
3291
3292 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3293 {
3294 if( token != T_LEFT )
3295 Expecting( T_LEFT );
3296
3297 token = NextTok();
3298
3299 switch( token )
3300 {
3301 case T_alternate:
3302 NeedSYMBOL();
3303 alt = FromUTF8();
3304 NeedRIGHT();
3305 break;
3306
3307 case T_uuid:
3308 NeedSYMBOL();
3309
3310 // First version to write out pin uuids accidentally wrote out the symbol's
3311 // uuid for each pin, so ignore uuids coming from that version.
3312 if( m_requiredVersion >= 20210126 )
3313 uuid = parseKIID();
3314
3315 NeedRIGHT();
3316 break;
3317
3318 default:
3319 Expecting( "alternate or uuid" );
3320 }
3321 }
3322
3323 symbol->GetRawPins().emplace_back( std::make_unique<SCH_PIN>( symbol.get(), number,
3324 alt, uuid ) );
3325 break;
3326 }
3327
3328 default:
3329 Expecting( "lib_id, lib_name, at, mirror, uuid, on_board, in_bom, dnp, "
3330 "default_instance, property, pin, or instances" );
3331 }
3332 }
3333
3334 if( !libName.IsEmpty() && ( symbol->GetLibId().Format().wx_str() != libName ) )
3335 symbol->SetSchSymbolLibraryName( libName );
3336
3337 // Ensure edit/status flags are cleared after these initializations:
3338 symbol->ClearFlags();
3339
3340 return symbol.release();
3341}
3342
3343
3345{
3346 wxCHECK_MSG( CurTok() == T_image, nullptr,
3347 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an image." ) );
3348
3349 T token;
3350 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>();
3351 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
3352
3353 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3354 {
3355 if( token != T_LEFT )
3356 Expecting( T_LEFT );
3357
3358 token = NextTok();
3359
3360 switch( token )
3361 {
3362 case T_at:
3363 bitmap->SetPosition( parseXY() );
3364 NeedRIGHT();
3365 break;
3366
3367 case T_scale:
3368 {
3369 const double scale = parseDouble( "image scale factor" );
3370 refImage.SetImageScale( std::isnormal( scale ) ? scale : 1.0 );
3371
3372 NeedRIGHT();
3373 break;
3374 }
3375 case T_uuid:
3376 NeedSYMBOL();
3377 const_cast<KIID&>( bitmap->m_Uuid ) = parseKIID();
3378 NeedRIGHT();
3379 break;
3380
3381 case T_data:
3382 {
3383 token = NextTok();
3384
3385 wxString data;
3386
3387 // Reserve 128K because most image files are going to be larger than the default
3388 // 1K that wxString reserves.
3389 data.reserve( 1 << 17 );
3390
3391 while( token != T_RIGHT )
3392 {
3393 if( !IsSymbol( token ) )
3394 Expecting( "base64 image data" );
3395
3396 data += FromUTF8();
3397 token = NextTok();
3398 }
3399
3400 wxMemoryBuffer buffer = wxBase64Decode( data );
3401
3402 if( !refImage.ReadImageFile( buffer ) )
3403 THROW_IO_ERROR( _( "Failed to read image data." ) );
3404
3405 break;
3406 }
3407
3408 default:
3409 Expecting( "at, scale, uuid or data" );
3410 }
3411 }
3412
3413 // The image will be scaled by PPI in ReadImageFile.
3414
3415 // 20230121 or older file format versions assumed 300 image PPI at load/save.
3416 // Let's keep compatibility by changing image scale.
3417 if( m_requiredVersion <= 20230121 )
3418 {
3419 refImage.SetImageScale( refImage.GetImageScale() * refImage.GetImage().GetPPI() / 300.0 );
3420 }
3421
3422 return bitmap.release();
3423}
3424
3425
3427{
3428 wxCHECK_MSG( CurTok() == T_sheet, nullptr,
3429 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a sheet." ) );
3430
3431 T token;
3432 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3433 FILL_PARAMS fill;
3434 SCH_FIELD* field;
3435 std::vector<SCH_FIELD> fields;
3436 std::unique_ptr<SCH_SHEET> sheet = std::make_unique<SCH_SHEET>();
3437 std::set<int> fieldIDsRead;
3438
3439 // We'll reset this if we find a fields_autoplaced token
3440 sheet->SetFieldsAutoplaced( AUTOPLACE_NONE );
3441
3442 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3443 {
3444 if( token != T_LEFT )
3445 Expecting( T_LEFT );
3446
3447 token = NextTok();
3448
3449 switch( token )
3450 {
3451 case T_at:
3452 sheet->SetPosition( parseXY() );
3453 NeedRIGHT();
3454 break;
3455
3456 case T_size:
3457 {
3458 VECTOR2I size;
3459 size.x = parseInternalUnits( "sheet width" );
3460 size.y = parseInternalUnits( "sheet height" );
3461 sheet->SetSize( size );
3462 NeedRIGHT();
3463 break;
3464 }
3465
3466 case T_exclude_from_sim:
3467 sheet->SetExcludedFromSim( parseBool() );
3468 NeedRIGHT();
3469 break;
3470
3471 case T_in_bom:
3472 sheet->SetExcludedFromBOM( !parseBool() );
3473 NeedRIGHT();
3474 break;
3475
3476 case T_on_board:
3477 sheet->SetExcludedFromBoard( !parseBool() );
3478 NeedRIGHT();
3479 break;
3480
3481 case T_dnp:
3482 sheet->SetDNP( parseBool() );
3483 NeedRIGHT();
3484 break;
3485
3486 case T_fields_autoplaced:
3487 if( parseMaybeAbsentBool( true ) )
3488 sheet->SetFieldsAutoplaced( AUTOPLACE_AUTO );
3489
3490 break;
3491
3492 case T_stroke:
3493 parseStroke( stroke );
3494 sheet->SetBorderWidth( stroke.GetWidth() );
3495 sheet->SetBorderColor( stroke.GetColor() );
3496 break;
3497
3498 case T_fill:
3499 parseFill( fill );
3500 sheet->SetBackgroundColor( fill.m_Color );
3501 break;
3502
3503 case T_uuid:
3504 NeedSYMBOL();
3505 const_cast<KIID&>( sheet->m_Uuid ) = parseKIID();
3506 NeedRIGHT();
3507 break;
3508
3509 case T_property:
3510 field = parseSchField( sheet.get() );
3511
3512 if( m_requiredVersion <= 20200310 )
3513 {
3514 // Earlier versions had the wrong ids (and names) saved for sheet fields.
3515 // Fortunately they only saved the sheetname and sheetfilepath (and always
3516 // in that order), so we can hack in a recovery.
3517 if( fields.empty() )
3518 field->SetId( SHEETNAME );
3519 else
3520 field->SetId( SHEETFILENAME );
3521 }
3522
3523 // It would appear the problem persists past 20200310, but this time with the
3524 // earlier ids being re-used for later (user) fields. The easiest (and most
3525 // complete) solution is to disallow multiple instances of the same id (for all
3526 // files since the source of the error *might* in fact be hand-edited files).
3527 //
3528 // While no longer used, -1 is still a valid id for user field. We convert it to
3529 // the first available ID after the mandatory fields
3530
3531 if( field->GetId() < 0 )
3532 field->SetId( SHEET_MANDATORY_FIELDS );
3533
3534 while( !fieldIDsRead.insert( field->GetId() ).second )
3535 field->SetId( field->GetId() + 1 );
3536
3537 fields.emplace_back( *field );
3538
3539 delete field;
3540 break;
3541
3542 case T_pin:
3543 sheet->AddPin( parseSchSheetPin( sheet.get() ) );
3544 break;
3545
3546 case T_instances:
3547 {
3548 std::vector<SCH_SHEET_INSTANCE> instances;
3549
3550 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3551 {
3552 if( token != T_LEFT )
3553 Expecting( T_LEFT );
3554
3555 token = NextTok();
3556
3557 if( token != T_project )
3558 Expecting( "project" );
3559
3560 NeedSYMBOL();
3561
3562 wxString projectName = FromUTF8();
3563
3564 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3565 {
3566 if( token != T_LEFT )
3567 Expecting( T_LEFT );
3568
3569 token = NextTok();
3570
3571 if( token != T_path )
3572 Expecting( "path" );
3573
3574 SCH_SHEET_INSTANCE instance;
3575
3576 instance.m_ProjectName = projectName;
3577
3578 NeedSYMBOL();
3579 instance.m_Path = KIID_PATH( FromUTF8() );
3580
3581 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3582 {
3583 if( token != T_LEFT )
3584 Expecting( T_LEFT );
3585
3586 token = NextTok();
3587
3588 switch( token )
3589 {
3590 case T_page:
3591 {
3592 NeedSYMBOL();
3593 instance.m_PageNumber = FromUTF8();
3594
3595 // Empty page numbers are not permitted
3596 if( instance.m_PageNumber.IsEmpty() )
3597 {
3598 // Use hash character instead
3599 instance.m_PageNumber = wxT( "#" );
3600 }
3601 else
3602 {
3603 // Whitespaces are not permitted
3604 static std::vector<wxString> whitespaces =
3605 { wxT( "\r" ),
3606 wxT( "\n" ),
3607 wxT( "\t" ),
3608 wxT( " " ) };
3609
3610 for( wxString ch : whitespaces )
3611 instance.m_PageNumber.Replace( ch, wxEmptyString );
3612 }
3613
3614 NeedRIGHT();
3615 break;
3616 }
3617
3618 default:
3619 Expecting( "page" );
3620 }
3621 }
3622
3623 instances.emplace_back( instance );
3624 }
3625 }
3626
3627 sheet->setInstances( instances );
3628 break;
3629 }
3630
3631 default:
3632 Expecting( "at, size, stroke, background, instances, uuid, property, or pin" );
3633 }
3634 }
3635
3636 sheet->SetFields( fields );
3637
3638 return sheet.release();
3639}
3640
3641
3643{
3644 wxCHECK_MSG( CurTok() == T_junction, nullptr,
3645 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a junction." ) );
3646
3647 T token;
3648 std::unique_ptr<SCH_JUNCTION> junction = std::make_unique<SCH_JUNCTION>();
3649
3650 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3651 {
3652 if( token != T_LEFT )
3653 Expecting( T_LEFT );
3654
3655 token = NextTok();
3656
3657 switch( token )
3658 {
3659 case T_at:
3660 junction->SetPosition( parseXY() );
3661 NeedRIGHT();
3662 break;
3663
3664 case T_diameter:
3665 junction->SetDiameter( parseInternalUnits( "junction diameter" ) );
3666 NeedRIGHT();
3667 break;
3668
3669 case T_color:
3670 {
3671 COLOR4D color;
3672
3673 color.r = parseInt( "red" ) / 255.0;
3674 color.g = parseInt( "green" ) / 255.0;
3675 color.b = parseInt( "blue" ) / 255.0;
3676 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
3677
3678 junction->SetColor( color );
3679 NeedRIGHT();
3680 break;
3681 }
3682
3683 case T_uuid:
3684 NeedSYMBOL();
3685 const_cast<KIID&>( junction->m_Uuid ) = parseKIID();
3686 NeedRIGHT();
3687 break;
3688
3689 default:
3690 Expecting( "at, diameter, color or uuid" );
3691 }
3692 }
3693
3694 return junction.release();
3695}
3696
3697
3699{
3700 wxCHECK_MSG( CurTok() == T_no_connect, nullptr,
3701 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a no connect." ) );
3702
3703 T token;
3704 std::unique_ptr<SCH_NO_CONNECT> no_connect = std::make_unique<SCH_NO_CONNECT>();
3705
3706 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3707 {
3708 if( token != T_LEFT )
3709 Expecting( T_LEFT );
3710
3711 token = NextTok();
3712
3713 switch( token )
3714 {
3715 case T_at:
3716 no_connect->SetPosition( parseXY() );
3717 NeedRIGHT();
3718 break;
3719
3720 case T_uuid:
3721 NeedSYMBOL();
3722 const_cast<KIID&>( no_connect->m_Uuid ) = parseKIID();
3723 NeedRIGHT();
3724 break;
3725
3726 default:
3727 Expecting( "at or uuid" );
3728 }
3729 }
3730
3731 return no_connect.release();
3732}
3733
3734
3736{
3737 wxCHECK_MSG( CurTok() == T_bus_entry, nullptr,
3738 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus entry." ) );
3739
3740 T token;
3741 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3742 std::unique_ptr<SCH_BUS_WIRE_ENTRY> busEntry = std::make_unique<SCH_BUS_WIRE_ENTRY>();
3743
3744 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3745 {
3746 if( token != T_LEFT )
3747 Expecting( T_LEFT );
3748
3749 token = NextTok();
3750
3751 switch( token )
3752 {
3753 case T_at:
3754 busEntry->SetPosition( parseXY() );
3755 NeedRIGHT();
3756 break;
3757
3758 case T_size:
3759 {
3760 VECTOR2I size;
3761
3762 size.x = parseInternalUnits( "bus entry height" );
3763 size.y = parseInternalUnits( "bus entry width" );
3764 busEntry->SetSize( size );
3765 NeedRIGHT();
3766 break;
3767 }
3768
3769 case T_stroke:
3770 parseStroke( stroke );
3771 busEntry->SetStroke( stroke );
3772 break;
3773
3774 case T_uuid:
3775 NeedSYMBOL();
3776 const_cast<KIID&>( busEntry->m_Uuid ) = parseKIID();
3777 NeedRIGHT();
3778 break;
3779
3780 default:
3781 Expecting( "at, size, uuid or stroke" );
3782 }
3783 }
3784
3785 return busEntry.release();
3786}
3787
3788
3790{
3791 if( aShape->GetFillMode() == FILL_T::FILLED_SHAPE )
3792 {
3793 aShape->SetFillColor( aShape->GetStroke().GetColor() );
3795 }
3796}
3797
3798
3800{
3801 T token;
3802 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3803 FILL_PARAMS fill;
3804
3805 std::unique_ptr<SCH_SHAPE> polyline = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_NOTES );
3806
3807 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3808 {
3809 if( token != T_LEFT )
3810 Expecting( T_LEFT );
3811
3812 token = NextTok();
3813
3814 switch( token )
3815 {
3816 case T_pts:
3817 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3818 {
3819 if( token != T_LEFT )
3820 Expecting( T_LEFT );
3821
3822 token = NextTok();
3823
3824 if( token != T_xy )
3825 Expecting( "xy" );
3826
3827 polyline->AddPoint( parseXY() );
3828
3829 NeedRIGHT();
3830 }
3831 break;
3832
3833 case T_stroke:
3834 parseStroke( stroke );
3835
3836 // In 6.0, the default schematic line style was Dashed.
3837 if( m_requiredVersion <= 20211123 && stroke.GetLineStyle() == LINE_STYLE::DEFAULT )
3838 stroke.SetLineStyle( LINE_STYLE::DASH );
3839
3840 polyline->SetStroke( stroke );
3841 break;
3842
3843 case T_fill:
3844 parseFill( fill );
3845 polyline->SetFillMode( fill.m_FillType );
3846 polyline->SetFillColor( fill.m_Color );
3847 fixupSchFillMode( polyline.get() );
3848 break;
3849
3850 case T_uuid:
3851 NeedSYMBOL();
3852 const_cast<KIID&>( polyline->m_Uuid ) = parseKIID();
3853 NeedRIGHT();
3854 break;
3855
3856 default:
3857 Expecting( "pts, uuid, stroke, or fill" );
3858 }
3859 }
3860
3861 return polyline.release();
3862}
3863
3864
3866{
3867 // Note: T_polyline is deprecated in this code: it is now handled by
3868 // parseSchPolyLine() that can handle true polygons, and not only one segment.
3869
3870 T token;
3871 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3872 int layer;
3873
3874 switch( CurTok() )
3875 {
3876 case T_polyline: layer = LAYER_NOTES; break;
3877 case T_wire: layer = LAYER_WIRE; break;
3878 case T_bus: layer = LAYER_BUS; break;
3879 default:
3880 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as a line." );
3881 }
3882
3883 std::unique_ptr<SCH_LINE> line = std::make_unique<SCH_LINE>( VECTOR2I(), layer );
3884
3885 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3886 {
3887 if( token != T_LEFT )
3888 Expecting( T_LEFT );
3889
3890 token = NextTok();
3891
3892 switch( token )
3893 {
3894 case T_pts:
3895 NeedLEFT();
3896 token = NextTok();
3897
3898 if( token != T_xy )
3899 Expecting( "xy" );
3900
3901 line->SetStartPoint( parseXY() );
3902 NeedRIGHT();
3903 NeedLEFT();
3904 token = NextTok();
3905
3906 if( token != T_xy )
3907 Expecting( "xy" );
3908
3909 line->SetEndPoint( parseXY() );
3910 NeedRIGHT();
3911 NeedRIGHT();
3912 break;
3913
3914 case T_stroke:
3915 parseStroke( stroke );
3916 line->SetStroke( stroke );
3917 break;
3918
3919 case T_uuid:
3920 NeedSYMBOL();
3921 const_cast<KIID&>( line->m_Uuid ) = parseKIID();
3922 NeedRIGHT();
3923 break;
3924
3925 default:
3926 Expecting( "at, uuid or stroke" );
3927 }
3928 }
3929
3930 return line.release();
3931}
3932
3933
3935{
3936 wxCHECK_MSG( CurTok() == T_arc, nullptr,
3937 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc." ) );
3938
3939 T token;
3940 VECTOR2I startPoint;
3941 VECTOR2I midPoint;
3942 VECTOR2I endPoint;
3943 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3944 FILL_PARAMS fill;
3945 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC );
3946
3947 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3948 {
3949 if( token != T_LEFT )
3950 Expecting( T_LEFT );
3951
3952 token = NextTok();
3953
3954 switch( token )
3955 {
3956 case T_start:
3957 startPoint = parseXY();
3958 NeedRIGHT();
3959 break;
3960
3961 case T_mid:
3962 midPoint = parseXY();
3963 NeedRIGHT();
3964 break;
3965
3966 case T_end:
3967 endPoint = parseXY();
3968 NeedRIGHT();
3969 break;
3970
3971 case T_stroke:
3972 parseStroke( stroke );
3973 arc->SetStroke( stroke );
3974 break;
3975
3976 case T_fill:
3977 parseFill( fill );
3978 arc->SetFillMode( fill.m_FillType );
3979 arc->SetFillColor( fill.m_Color );
3980 fixupSchFillMode( arc.get() );
3981 break;
3982
3983 case T_uuid:
3984 NeedSYMBOL();
3985 const_cast<KIID&>( arc->m_Uuid ) = KIID( FromUTF8() );
3986 NeedRIGHT();
3987 break;
3988
3989 default:
3990 Expecting( "start, mid, end, stroke, fill or uuid" );
3991 }
3992 }
3993
3994 arc->SetArcGeometry( startPoint, midPoint, endPoint );
3995
3996 return arc.release();
3997}
3998
3999
4001{
4002 wxCHECK_MSG( CurTok() == T_circle, nullptr,
4003 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle." ) );
4004
4005 T token;
4006 VECTOR2I center;
4007 int radius = 0;
4008 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4009 FILL_PARAMS fill;
4010 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE );
4011
4012 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4013 {
4014 if( token != T_LEFT )
4015 Expecting( T_LEFT );
4016
4017 token = NextTok();
4018
4019 switch( token )
4020 {
4021 case T_center:
4022 center = parseXY();
4023 NeedRIGHT();
4024 break;
4025
4026 case T_radius:
4027 radius = parseInternalUnits( "radius length" );
4028 NeedRIGHT();
4029 break;
4030
4031 case T_stroke:
4032 parseStroke( stroke );
4033 circle->SetStroke( stroke );
4034 break;
4035
4036 case T_fill:
4037 parseFill( fill );
4038 circle->SetFillMode( fill.m_FillType );
4039 circle->SetFillColor( fill.m_Color );
4040 fixupSchFillMode( circle.get() );
4041 break;
4042
4043 case T_uuid:
4044 NeedSYMBOL();
4045 const_cast<KIID&>( circle->m_Uuid ) = KIID( FromUTF8() );
4046 NeedRIGHT();
4047 break;
4048
4049 default:
4050 Expecting( "center, radius, stroke, fill or uuid" );
4051 }
4052 }
4053
4054 circle->SetCenter( center );
4055 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
4056
4057 return circle.release();
4058}
4059
4060
4062{
4063 wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
4064 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle." ) );
4065
4066 T token;
4067 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4068 FILL_PARAMS fill;
4069 std::unique_ptr<SCH_SHAPE> rectangle = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE );
4070
4071 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4072 {
4073 if( token != T_LEFT )
4074 Expecting( T_LEFT );
4075
4076 token = NextTok();
4077
4078 switch( token )
4079 {
4080 case T_start:
4081 rectangle->SetPosition( parseXY() );
4082 NeedRIGHT();
4083 break;
4084
4085 case T_end:
4086 rectangle->SetEnd( parseXY() );
4087 NeedRIGHT();
4088 break;
4089
4090 case T_stroke:
4091 parseStroke( stroke );
4092 rectangle->SetStroke( stroke );
4093 break;
4094
4095 case T_fill:
4096 parseFill( fill );
4097 rectangle->SetFillMode( fill.m_FillType );
4098 rectangle->SetFillColor( fill.m_Color );
4099 fixupSchFillMode( rectangle.get() );
4100 break;
4101
4102 case T_uuid:
4103 NeedSYMBOL();
4104 const_cast<KIID&>( rectangle->m_Uuid ) = KIID( FromUTF8() );
4105 NeedRIGHT();
4106 break;
4107
4108 default:
4109 Expecting( "start, end, stroke, fill or uuid" );
4110 }
4111 }
4112
4113 return rectangle.release();
4114}
4115
4116
4118{
4119 wxCHECK_MSG( CurTok() == T_rule_area, nullptr,
4120 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rule area." ) );
4121
4122 T token;
4123 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4124 FILL_PARAMS fill;
4125 std::unique_ptr<SCH_RULE_AREA> ruleArea = std::make_unique<SCH_RULE_AREA>();
4126
4127 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4128 {
4129 if( token != T_LEFT )
4130 Expecting( T_LEFT );
4131
4132 token = NextTok();
4133
4134 switch( token )
4135 {
4136 case T_polyline:
4137 {
4138 std::unique_ptr<SCH_SHAPE> poly( parseSchPolyLine() );
4139 SHAPE_POLY_SET& sch_rule_poly = poly->GetPolyShape();
4140
4141 // The polygon must be closed, it is a schematic closed polyline:
4142 sch_rule_poly.Outline(0).SetClosed( true );
4143
4144 ruleArea->SetPolyShape( sch_rule_poly );
4145
4146 ruleArea->SetStroke( poly->GetStroke() );
4147 ruleArea->SetFillMode( poly->GetFillMode() );
4148 ruleArea->SetFillColor( poly->GetFillColor() );
4149 break;
4150 }
4151 default:
4152 Expecting( "polyline" );
4153 }
4154 }
4155
4156 return ruleArea.release();
4157}
4158
4159
4161{
4162 wxCHECK_MSG( CurTok() == T_bezier, nullptr,
4163 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
4164
4165 T token;
4166 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4167 FILL_PARAMS fill;
4168 std::unique_ptr<SCH_SHAPE> bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER );
4169
4170 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4171 {
4172 if( token != T_LEFT )
4173 Expecting( T_LEFT );
4174
4175 token = NextTok();
4176
4177 switch( token )
4178 {
4179 case T_pts:
4180 {
4181 int ii = 0;
4182
4183 for( token = NextTok(); token != T_RIGHT; token = NextTok(), ++ii )
4184 {
4185 if( token != T_LEFT )
4186 Expecting( T_LEFT );
4187
4188 token = NextTok();
4189
4190 if( token != T_xy )
4191 Expecting( "xy" );
4192
4193 switch( ii )
4194 {
4195 case 0: bezier->SetStart( parseXY() ); break;
4196 case 1: bezier->SetBezierC1( parseXY() ); break;
4197 case 2: bezier->SetBezierC2( parseXY() ); break;
4198 case 3: bezier->SetEnd( parseXY() ); break;
4199 default: Unexpected( "control point" ); break;
4200 }
4201
4202 NeedRIGHT();
4203 }
4204 }
4205 break;
4206
4207 case T_stroke:
4208 parseStroke( stroke );
4209 bezier->SetStroke( stroke );
4210 break;
4211
4212 case T_fill:
4213 parseFill( fill );
4214 bezier->SetFillMode( fill.m_FillType );
4215 bezier->SetFillColor( fill.m_Color );
4216 fixupSchFillMode( bezier.get() );
4217 break;
4218
4219 case T_uuid:
4220 NeedSYMBOL();
4221 const_cast<KIID&>( bezier->m_Uuid ) = KIID( FromUTF8() );
4222 NeedRIGHT();
4223 break;
4224
4225 default:
4226 Expecting( "pts, stroke, fill or uuid" );
4227 }
4228 }
4229
4230 bezier->RebuildBezierToSegmentsPointsList( bezier->GetPenWidth() / 2 );
4231
4232 return bezier.release();
4233}
4234
4235
4237{
4238 T token;
4239 std::unique_ptr<SCH_TEXT> text;
4240
4241 switch( CurTok() )
4242 {
4243 case T_text: text = std::make_unique<SCH_TEXT>(); break;
4244 case T_label: text = std::make_unique<SCH_LABEL>(); break;
4245 case T_global_label: text = std::make_unique<SCH_GLOBALLABEL>(); break;
4246 case T_hierarchical_label: text = std::make_unique<SCH_HIERLABEL>(); break;
4247 case T_netclass_flag: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
4248 case T_directive_label: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
4249 default:
4250 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as text." );
4251 }
4252
4253 // We'll reset this if we find a fields_autoplaced token
4254 text->SetFieldsAutoplaced( AUTOPLACE_NONE );
4255
4256 NeedSYMBOL();
4257
4258 text->SetText( FromUTF8() );
4259
4260 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4261 {
4262 if( token != T_LEFT )
4263 Expecting( T_LEFT );
4264
4265 token = NextTok();
4266
4267 switch( token )
4268 {
4269 case T_exclude_from_sim:
4270 text->SetExcludedFromSim( parseBool() );
4271 NeedRIGHT();
4272 break;
4273
4274 case T_at:
4275 text->SetPosition( parseXY() );
4276 text->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ).KeepUpright() );
4277
4278 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() ) )
4279 {
4280 switch( static_cast<int>( label->GetTextAngle().AsDegrees() ) )
4281 {
4282 case 0: label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
4283 case 90: label->SetSpinStyle( SPIN_STYLE::UP ); break;
4284 case 180: label->SetSpinStyle( SPIN_STYLE::LEFT ); break;
4285 case 270: label->SetSpinStyle( SPIN_STYLE::BOTTOM ); break;
4286 default: wxFAIL; label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
4287 }
4288 }
4289
4290 NeedRIGHT();
4291 break;
4292
4293 case T_shape:
4294 {
4295 if( text->Type() == SCH_TEXT_T || text->Type() == SCH_LABEL_T )
4296 Unexpected( T_shape );
4297
4298 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( text.get() );
4299
4300 token = NextTok();
4301
4302 switch( token )
4303 {
4304 case T_input: label->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
4305 case T_output: label->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
4306 case T_bidirectional: label->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
4307 case T_tri_state: label->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE ); break;
4308 case T_passive: label->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
4309 case T_dot: label->SetShape( LABEL_FLAG_SHAPE::F_DOT ); break;
4310 case T_round: label->SetShape( LABEL_FLAG_SHAPE::F_ROUND ); break;
4311 case T_diamond: label->SetShape( LABEL_FLAG_SHAPE::F_DIAMOND ); break;
4312 case T_rectangle: label->SetShape( LABEL_FLAG_SHAPE::F_RECTANGLE ); break;
4313 default:
4314 Expecting( "input, output, bidirectional, tri_state, passive, dot, round, diamond"
4315 "or rectangle" );
4316 }
4317
4318 NeedRIGHT();
4319 break;
4320 }
4321
4322 case T_length:
4323 {
4324 if( text->Type() != SCH_DIRECTIVE_LABEL_T )
4325 Unexpected( T_length );
4326
4327 SCH_DIRECTIVE_LABEL* label = static_cast<SCH_DIRECTIVE_LABEL*>( text.get() );
4328
4329 label->SetPinLength( parseInternalUnits( "pin length" ) );
4330 NeedRIGHT();
4331 }
4332 break;
4333
4334 case T_fields_autoplaced:
4335 if( parseMaybeAbsentBool( true ) )
4336 text->SetFieldsAutoplaced( AUTOPLACE_AUTO );
4337
4338 break;
4339
4340 case T_effects:
4341 parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ), true );
4342 break;
4343
4344 case T_iref: // legacy format; current is a T_property (aka SCH_FIELD)
4345 if( text->Type() == SCH_GLOBAL_LABEL_T )
4346 {
4347 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
4348 SCH_FIELD* field = &label->GetFields()[0];
4349
4350 field->SetTextPos( parseXY() );
4351 NeedRIGHT();
4352
4353 field->SetVisible( true );
4354 }
4355 break;
4356
4357 case T_uuid:
4358 NeedSYMBOL();
4359 const_cast<KIID&>( text->m_Uuid ) = parseKIID();
4360 NeedRIGHT();
4361 break;
4362
4363 case T_property:
4364 {
4365 if( text->Type() == SCH_TEXT_T )
4366 Unexpected( T_property );
4367
4368 SCH_FIELD* field = parseSchField( text.get() );
4369
4370 // If the field is a Intersheetrefs it is not handled like other fields:
4371 // It always exists and is the first in list
4372 if( text->Type() == SCH_GLOBAL_LABEL_T
4373 && ( field->GetInternalName() == wxT( "Intersheet References" ) // old name in V6.0
4374 || field->GetInternalName() == wxT( "Intersheetrefs" ) ) ) // Current name
4375 {
4376 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
4377 // Ensure the Id of this special and first field is 0, needed by
4378 // SCH_FIELD::IsHypertext() test
4379 field->SetId( 0 );
4380
4381 label->GetFields()[0] = *field;
4382 }
4383 else
4384 {
4385 static_cast<SCH_LABEL_BASE*>( text.get() )->GetFields().emplace_back( *field );
4386 }
4387
4388 delete field;
4389 break;
4390 }
4391
4392 default:
4393 Expecting( "at, shape, iref, uuid or effects" );
4394 }
4395 }
4396
4397 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() );
4398
4399 if( label && label->GetFields().empty() )
4401
4402 return text.release();
4403}
4404
4405
4407{
4408 wxCHECK_MSG( CurTok() == T_text_box, nullptr,
4409 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text box." ) );
4410
4411 std::unique_ptr<SCH_TEXTBOX> textBox = std::make_unique<SCH_TEXTBOX>();
4412
4413 parseSchTextBoxContent( textBox.get() );
4414
4415 return textBox.release();
4416}
4417
4418
4420{
4421 wxCHECK_MSG( CurTok() == T_table_cell, nullptr,
4422 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table cell." ) );
4423
4424 std::unique_ptr<SCH_TABLECELL> cell = std::make_unique<SCH_TABLECELL>();
4425
4426 parseSchTextBoxContent( cell.get() );
4427
4428 return cell.release();
4429}
4430
4431
4433{
4434 T token;
4435 VECTOR2I pos;
4436 VECTOR2I end;
4437 VECTOR2I size;
4438 int left;
4439 int top;
4440 int right;
4441 int bottom;
4442 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4443 FILL_PARAMS fill;
4444 bool foundEnd = false;
4445 bool foundSize = false;
4446 bool foundMargins = false;
4447
4448 NeedSYMBOL();
4449
4450 aTextBox->SetText( FromUTF8() );
4451
4452 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4453 {
4454 if( token != T_LEFT )
4455 Expecting( T_LEFT );
4456
4457 token = NextTok();
4458
4459 switch( token )
4460 {
4461 case T_exclude_from_sim:
4462 aTextBox->SetExcludedFromSim( parseBool() );
4463 NeedRIGHT();
4464 break;
4465
4466 case T_start: // Legacy token during 6.99 development; fails to handle angle
4467 pos = parseXY();
4468 NeedRIGHT();
4469 break;
4470
4471 case T_end: // Legacy token during 6.99 development; fails to handle angle
4472 end = parseXY();
4473 foundEnd = true;
4474 NeedRIGHT();
4475 break;
4476
4477 case T_at:
4478 pos = parseXY();
4479 aTextBox->SetTextAngle( EDA_ANGLE( parseDouble( "textbox angle" ), DEGREES_T ) );
4480 NeedRIGHT();
4481 break;
4482
4483 case T_size:
4484 size = parseXY();
4485 foundSize = true;
4486 NeedRIGHT();
4487 break;
4488
4489 case T_span:
4490 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( aTextBox ) )
4491 {
4492 cell->SetColSpan( parseInt( "column span" ) );
4493 cell->SetRowSpan( parseInt( "row span" ) );
4494 }
4495 else
4496 {
4497 Expecting( "at, size, stroke, fill, effects or uuid" );
4498 }
4499
4500 NeedRIGHT();
4501 break;
4502
4503 case T_stroke:
4504 parseStroke( stroke );
4505 aTextBox->SetStroke( stroke );
4506 break;
4507
4508 case T_fill:
4509 parseFill( fill );
4510 aTextBox->SetFillMode( fill.m_FillType );
4511 aTextBox->SetFillColor( fill.m_Color );
4512 fixupSchFillMode( aTextBox );
4513 break;
4514
4515 case T_margins:
4516 parseMargins( left, top, right, bottom );
4517 aTextBox->SetMarginLeft( left );
4518 aTextBox->SetMarginTop( top );
4519 aTextBox->SetMarginRight( right );
4520 aTextBox->SetMarginBottom( bottom );
4521 foundMargins = true;
4522 NeedRIGHT();
4523 break;
4524
4525 case T_effects:
4526 parseEDA_TEXT( static_cast<EDA_TEXT*>( aTextBox ), false );
4527 break;
4528
4529 case T_uuid:
4530 NeedSYMBOL();
4531 const_cast<KIID&>( aTextBox->m_Uuid ) = KIID( FromUTF8() );
4532 NeedRIGHT();
4533 break;
4534
4535 default:
4536 if( dynamic_cast<SCH_TABLECELL*>( aTextBox ) != nullptr )
4537 Expecting( "at, size, stroke, fill, effects, span or uuid" );
4538 else
4539 Expecting( "at, size, stroke, fill, effects or uuid" );
4540 }
4541 }
4542
4543 aTextBox->SetPosition( pos );
4544
4545 if( foundEnd )
4546 aTextBox->SetEnd( end );
4547 else if( foundSize )
4548 aTextBox->SetEnd( pos + size );
4549 else
4550 Expecting( "size" );
4551
4552 if( !foundMargins )
4553 {
4554 int margin = aTextBox->GetLegacyTextMargin();
4555 aTextBox->SetMarginLeft( margin );
4556 aTextBox->SetMarginTop( margin );
4557 aTextBox->SetMarginRight( margin );
4558 aTextBox->SetMarginBottom( margin );
4559 }
4560}
4561
4562
4564{
4565 wxCHECK_MSG( CurTok() == T_table, nullptr,
4566 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table." ) );
4567
4568 T token;
4569 int defaultLineWidth = schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
4570 STROKE_PARAMS borderStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
4571 STROKE_PARAMS separatorsStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
4572 std::unique_ptr<SCH_TABLE> table = std::make_unique<SCH_TABLE>( defaultLineWidth );
4573
4574 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4575 {
4576 if( token != T_LEFT )
4577 Expecting( T_LEFT );
4578
4579 token = NextTok();
4580
4581 switch( token )
4582 {
4583 case T_column_count:
4584 table->SetColCount( parseInt( "column count" ) );
4585 NeedRIGHT();
4586 break;
4587
4588 case T_column_widths:
4589 {
4590 int col = 0;
4591
4592 while( ( token = NextTok() ) != T_RIGHT )
4593 table->SetColWidth( col++, parseInternalUnits() );
4594
4595 break;
4596 }
4597
4598 case T_row_heights:
4599 {
4600 int row = 0;
4601
4602 while( ( token = NextTok() ) != T_RIGHT )
4603 table->SetRowHeight( row++, parseInternalUnits() );
4604
4605 break;
4606 }
4607
4608 case T_cells:
4609 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4610 {
4611 if( token != T_LEFT )
4612 Expecting( T_LEFT );
4613
4614 token = NextTok();
4615
4616 if( token != T_table_cell )
4617 Expecting( "table_cell" );
4618
4619 table->AddCell( parseSchTableCell() );
4620 }
4621
4622 break;
4623
4624 case T_border:
4625 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4626 {
4627 if( token != T_LEFT )
4628 Expecting( T_LEFT );
4629
4630 token = NextTok();
4631
4632 switch( token )
4633 {
4634 case T_external:
4635 table->SetStrokeExternal( parseBool() );
4636 NeedRIGHT();
4637 break;
4638
4639 case T_header:
4640 table->SetStrokeHeader( parseBool() );
4641 NeedRIGHT();
4642 break;
4643
4644 case T_stroke:
4645 parseStroke( borderStroke );
4646 table->SetBorderStroke( borderStroke );
4647 break;
4648
4649 default:
4650 Expecting( "external, header or stroke" );
4651 break;
4652 }
4653 }
4654
4655 break;
4656
4657 case T_separators:
4658 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4659 {
4660 if( token != T_LEFT )
4661 Expecting( T_LEFT );
4662
4663 token = NextTok();
4664
4665 switch( token )
4666 {
4667 case T_rows:
4668 table->SetStrokeRows( parseBool() );
4669 NeedRIGHT();
4670 break;
4671
4672 case T_cols:
4673 table->SetStrokeColumns( parseBool() );
4674 NeedRIGHT();
4675 break;
4676
4677 case T_stroke:
4678 parseStroke( separatorsStroke );
4679 table->SetSeparatorsStroke( separatorsStroke );
4680 break;
4681
4682 default:
4683 Expecting( "rows, cols, or stroke" );
4684 break;
4685 }
4686 }
4687
4688 break;
4689
4690 default:
4691 Expecting( "columns, col_widths, row_heights, border, separators, header or cells" );
4692 }
4693 }
4694
4695 return table.release();
4696}
4697
4698
4700{
4701 wxCHECK_RET( CurTok() == T_bus_alias,
4702 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus alias." ) );
4703 wxCHECK( aScreen, /* void */ );
4704
4705 T token;
4706 std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>( aScreen );
4707 wxString alias;
4708 wxString member;
4709
4710 NeedSYMBOL();
4711
4712 alias = FromUTF8();
4713
4714 if( m_requiredVersion < 20210621 )
4715 alias = ConvertToNewOverbarNotation( alias );
4716
4717 busAlias->SetName( alias );
4718
4719 NeedLEFT();
4720 token = NextTok();
4721
4722 if( token != T_members )
4723 Expecting( "members" );
4724
4725 token = NextTok();
4726
4727 while( token != T_RIGHT )
4728 {
4729 if( !IsSymbol( token ) )
4730 Expecting( "quoted string" );
4731
4732 member = FromUTF8();
4733
4734 if( m_requiredVersion < 20210621 )
4735 member = ConvertToNewOverbarNotation( member );
4736
4737 busAlias->Members().emplace_back( member );
4738
4739 token = NextTok();
4740 }
4741
4742 NeedRIGHT();
4743
4744 aScreen->AddBusAlias( busAlias );
4745}
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:490
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:524
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:569
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition: eda_text.cpp:408
void SetBoldFlag(bool aBold)
Set only the italic flag, without changing the font.
Definition: eda_text.cpp:369
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:377
void SetLineSpacing(double aLineSpacing)
Definition: eda_text.cpp:516
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:282
static bool ValidateHyperlink(const wxString &aURL)
Check if aURL is a valid hyperlink.
Definition: eda_text.cpp:1227
void SetItalicFlag(bool aItalic)
Set only the italic flag, without changing the font.
Definition: eda_text.cpp:318
void SetHyperlink(wxString aLink)
Definition: eda_text.h:388
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:268
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:290
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:400
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:82
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition: schematic.cpp:876
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:467
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:51
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:1252
int GetId() const
Definition: sch_field.h:133
void SetId(int aId)
Definition: sch_field.cpp:161
const wxString & GetInternalName()
Get the initial name of the field set at creation (or set by SetName()).
Definition: sch_field.h:131
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:550
void SetShape(LABEL_FLAG_SHAPE aShape)
Definition: sch_label.h:178
std::vector< SCH_FIELD > & GetFields()
Definition: sch_label.h:201
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:137
virtual void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: sch_line.h:190
void SetEndPoint(const VECTOR2I &aPosition)
Definition: sch_line.h:142
void SetFileFormatVersionAtLoad(int aVersion)
Definition: sch_screen.h:127
std::vector< SCH_SHEET_INSTANCE > m_sheetInstances
Definition: sch_screen.h:690
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition: sch_screen.h:156
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Definition: sch_screen.cpp:153
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:131
const KIID & GetUuid() const
Definition: sch_screen.h:531
void UpdateLocalLibSymbolLinks()
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in this schematic with the local projec...
Definition: sch_screen.cpp:821
void SetLegacySymbolInstanceData()
Update the symbol value and footprint instance data for legacy designs.
SCHEMATIC * Schematic() const
Definition: sch_screen.cpp:100
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:128
KIID m_uuid
A unique identifier for each schematic file.
Definition: sch_screen.h:698
std::vector< SCH_SYMBOL_INSTANCE > m_symbolInstances
The list of symbol instances loaded from the schematic file.
Definition: sch_screen.h:689
void SetPosition(const VECTOR2I &aPos) override
Definition: sch_shape.h:73
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: sch_shape.cpp:63
STROKE_PARAMS GetStroke() const override
Definition: sch_shape.h:55
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:57
static const wxString GetDefaultFieldName(int aFieldNdx, bool aTranslated=true)
Definition: sch_sheet.cpp:57
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:109
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:93
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:93
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:102
#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:418
@ LAYER_WIRE
Definition: layer_ids.h:404
@ LAYER_NOTES
Definition: layer_ids.h:419
@ LAYER_BUS
Definition: layer_ids.h:405
#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:57
#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_FIELDS
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 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
wxString GetCanonicalFieldName(int idx)
@ VALUE_FIELD
Field Value of part, i.e. "3.3K".
@ MANDATORY_FIELDS
The first 5 are mandatory, and must be instantiated in SCH_COMPONENT and LIB_PART constructors.
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".
@ 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