KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_io_kicad_sexpr_parser.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2020 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Wayne Stambaugh <[email protected]>
8 *
9 * This program is free software: you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
27// For some reason wxWidgets is built with wxUSE_BASE64 unset so expose the wxWidgets
28// base64 code.
29#include <charconv>
30
31#include <fmt/format.h>
32#define wxUSE_BASE64 1
33#include <wx/base64.h>
34#include <wx/log.h>
35#include <wx/mstream.h>
36#include <wx/tokenzr.h>
37
38#include <base_units.h>
39#include <bitmap_base.h>
41#include <lib_id.h>
42#include <sch_pin.h>
43#include <math/util.h> // KiROUND
44#include <font/font.h>
45#include <font/fontconfig.h>
46#include <pgm_base.h>
47#include <string_utils.h>
48#include <sch_bitmap.h>
49#include <sch_bus_entry.h>
50#include <sch_symbol.h>
51#include <sch_edit_frame.h> // SYM_ORIENT_XXX
52#include <sch_field.h>
53#include <sch_line.h>
54#include <sch_rule_area.h>
55#include <sch_textbox.h>
56#include <sch_table.h>
57#include <sch_tablecell.h>
58#include <sch_label.h>
59#include <sch_junction.h>
60#include <sch_no_connect.h>
61#include <sch_screen.h>
62#include <sch_shape.h>
63#include <sch_sheet_pin.h>
66#include <template_fieldnames.h>
67#include <trigo.h>
68#include <progress_reporter.h>
69#include <sim/sim_model.h>
70
71
72using namespace TSCHEMATIC_T;
73
74
76 PROGRESS_REPORTER* aProgressReporter,
77 unsigned aLineCount, SCH_SHEET* aRootSheet,
78 bool aIsAppending ) :
79 SCHEMATIC_LEXER( aLineReader ),
80 m_requiredVersion( 0 ),
81 m_unit( 1 ),
82 m_bodyStyle( 1 ),
83 m_appending( aIsAppending ),
84 m_progressReporter( aProgressReporter ),
85 m_lineReader( aLineReader ),
86 m_lastProgressLine( 0 ),
87 m_lineCount( aLineCount ),
88 m_rootSheet( aRootSheet )
89{
90}
91
92
94{
95 const unsigned PROGRESS_DELTA = 500;
96
98 {
99 unsigned curLine = m_lineReader->LineNumber();
100
101 if( curLine > m_lastProgressLine + PROGRESS_DELTA )
102 {
103 m_progressReporter->SetCurrentProgress( ( (double) curLine )
104 / std::max( 1U, m_lineCount ) );
105
107 THROW_IO_ERROR( _( "Open cancelled by user." ) );
108
109 m_lastProgressLine = curLine;
110 }
111 }
112}
113
114
116{
117 KIID id( FromUTF8() );
118
119 while( m_uuids.count( id ) )
120 id.Increment();
121
122 m_uuids.insert( id );
123
124 return id;
125}
126
127
129{
130 T token = NextTok();
131
132 if( token == T_yes )
133 return true;
134 else if( token == T_no )
135 return false;
136 else
137 Expecting( "yes or no" );
138
139 return false;
140}
141
142
143/*
144 * e.g. "hide", "hide)", "(hide yes)"
145 */
147{
148 bool ret = aDefaultValue;
149
150 if( PrevTok() == T_LEFT )
151 {
152 T token = NextTok();
153
154 // "hide)"
155 if( static_cast<int>( token ) == DSN_RIGHT )
156 return aDefaultValue;
157
158 if( token == T_yes )
159 ret = true;
160 else if( token == T_no )
161 ret = false;
162 else
163 Expecting( "yes or no" );
164
165 NeedRIGHT();
166 }
167 else
168 {
169 // "hide"
170 return aDefaultValue;
171 }
172
173 return ret;
174}
175
176
178{
179 T token;
180
181 NeedLEFT();
182 NextTok();
183 parseHeader( T_kicad_symbol_lib, SEXPR_SYMBOL_LIB_FILE_VERSION );
184
185 bool versionChecked = false;
186
187 auto checkVersion =
188 [&]()
189 {
190 if( !versionChecked && m_requiredVersion > SEXPR_SYMBOL_LIB_FILE_VERSION )
191 {
192 throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
194 }
195
196 versionChecked = true;
197 };
198
199 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
200 {
201 if( token != T_LEFT )
202 Expecting( T_LEFT );
203
204 token = NextTok();
205
206 switch( token )
207 {
208 case T_generator:
209 // (generator "genname"); we don't care about it at the moment.
210 NeedSYMBOL();
211 NeedRIGHT();
212 break;
213
214 case T_host:
215 {
216 // (host eeschema ["5.99"]); old version of generator token
217 NeedSYMBOL();
218
219 // Really old versions also included a host version
220 if( m_requiredVersion < 20200827 )
221 NeedSYMBOL();
222
223 NeedRIGHT();
224 break;
225 }
226
227 case T_generator_version:
228 {
229 NextTok();
230 m_generatorVersion = FromUTF8();
231 NeedRIGHT();
232
233 // If the format includes a generator version, by this point we have enough info to
234 // do the version check here
235 checkVersion();
236 break;
237 }
238
239 case T_symbol:
240 {
241 // By the time we get to the first symbol, we can check the version
242 checkVersion();
243
244 m_unit = 1;
245 m_bodyStyle = 1;
246 LIB_SYMBOL* symbol = parseLibSymbol( aSymbolLibMap );
247 aSymbolLibMap[symbol->GetName()] = symbol;
248 break;
249 }
250
251 default:
252 Expecting( "symbol, generator, or generator_version" );
253 }
254 }
255}
256
257
259 int aFileVersion )
260{
261 LIB_SYMBOL* newSymbol = nullptr;
262
263 NextTok();
264
265 // If there actually isn't anything here, don't throw just return a nullptr
266 if( CurTok() == T_LEFT )
267 {
268 NextTok();
269
270 if( CurTok() == T_symbol )
271 {
272 m_requiredVersion = aFileVersion;
273 newSymbol = parseLibSymbol( aSymbolLibMap );
274
275 const std::vector<wxString>* embeddedFonts =
276 newSymbol->GetEmbeddedFiles()->UpdateFontFiles();
277
278 newSymbol->RunOnChildren(
279 [&]( SCH_ITEM* aChild )
280 {
281 if( EDA_TEXT* textItem = dynamic_cast<EDA_TEXT*>( aChild ) )
282 textItem->ResolveFont( embeddedFonts );
283 } );
284 }
285 else
286 {
287 wxString msg = wxString::Format( _( "Cannot parse %s as a symbol" ),
288 GetTokenString( CurTok() ) );
289 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
290 }
291 }
292
293 return newSymbol;
294}
295
296
298{
299 wxCHECK_MSG( CurTok() == T_symbol, nullptr,
300 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
301
302 T token;
303 long tmp;
304 wxString name;
305 wxString error;
306 wxString unitDisplayName;
307 SCH_ITEM* item;
308 std::unique_ptr<LIB_SYMBOL> symbol = std::make_unique<LIB_SYMBOL>( wxEmptyString );
309
310 symbol->SetUnitCount( 1 );
311
312 m_fieldIDsRead.clear();
313
314 // Make sure the mandatory field IDs are reserved as already read,
315 // the field parser will set the field IDs to the correct value if
316 // the field name matches a mandatory field name
317 for( int fieldId : MANDATORY_FIELDS )
318 m_fieldIDsRead.insert( fieldId );
319
320 token = NextTok();
321
322 if( !IsSymbol( token ) )
323 {
324 THROW_PARSE_ERROR( _( "Invalid symbol name" ), CurSource(), CurLine(), CurLineNumber(),
325 CurOffset() );
326 }
327
328 name = FromUTF8();
329
330 // Some symbol LIB_IDs have the '/' character escaped which can break derived symbol links.
331 // The '/' character is no longer an illegal LIB_ID character so it doesn't need to be
332 // escaped.
333 name.Replace( wxS( "{slash}" ), wxT( "/" ) );
334
335 LIB_ID id;
336 int bad_pos = id.Parse( name );
337
338 if( bad_pos >= 0 )
339 {
340 if( static_cast<int>( name.size() ) > bad_pos )
341 {
342 wxString msg = wxString::Format( _( "Symbol %s contains invalid character '%c'" ),
343 name,
344 name[bad_pos] );
345
346 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
347 }
348
349
350 THROW_PARSE_ERROR( _( "Invalid library identifier" ), CurSource(), CurLine(),
351 CurLineNumber(), CurOffset() );
352 }
353
354 m_symbolName = id.GetLibItemName().wx_str();
355 symbol->SetName( m_symbolName );
356 symbol->SetLibId( id );
357
358 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
359 {
360 if( token != T_LEFT )
361 Expecting( T_LEFT );
362
363 token = NextTok();
364
365 switch( token )
366 {
367 case T_power:
368 symbol->SetPower();
369 NeedRIGHT();
370 break;
371
372 case T_pin_names:
373 parsePinNames( symbol );
374 break;
375
376 case T_pin_numbers:
377 parsePinNumbers( symbol );
378 break;
379
380 case T_exclude_from_sim:
381 symbol->SetExcludedFromSim( parseBool() );
382 NeedRIGHT();
383 break;
384
385 case T_in_bom:
386 symbol->SetExcludedFromBOM( !parseBool() );
387 NeedRIGHT();
388 break;
389
390 case T_on_board:
391 symbol->SetExcludedFromBoard( !parseBool() );
392 NeedRIGHT();
393 break;
394
395 case T_property:
396 parseProperty( symbol );
397 break;
398
399 case T_extends:
400 {
401 token = NextTok();
402
403 if( !IsSymbol( token ) )
404 {
405 THROW_PARSE_ERROR( _( "Invalid parent symbol name" ), CurSource(), CurLine(),
406 CurLineNumber(), CurOffset() );
407 }
408
409 name = FromUTF8();
410
411 // Some symbol LIB_IDs have the '/' character escaped which can break derived
412 // symbol links. The '/' character is no longer an illegal LIB_ID character so
413 // it doesn't need to be escaped.
414 name.Replace( wxS( "{slash}" ), wxT( "/" ) );
415
416 auto it = aSymbolLibMap.find( name );
417
418 if( it == aSymbolLibMap.end() )
419 {
420 error.Printf( _( "No parent for extended symbol %s" ), name.c_str() );
421 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
422 }
423
424 symbol->SetParent( it->second );
425 NeedRIGHT();
426 break;
427 }
428
429 case T_symbol:
430 {
431 token = NextTok();
432
433 if( !IsSymbol( token ) )
434 {
435 THROW_PARSE_ERROR( _( "Invalid symbol unit name" ), CurSource(), CurLine(),
436 CurLineNumber(), CurOffset() );
437 }
438
439 name = FromUTF8();
440
441 // Some symbol LIB_IDs have the '/' character escaped which can break derived
442 // symbol links. The '/' character is no longer an illegal LIB_ID character so
443 // it doesn't need to be escaped.
444 name.Replace( wxS( "{slash}" ), wxT( "/" ) );
445
446 if( !name.StartsWith( m_symbolName ) )
447 {
448 error.Printf( _( "Invalid symbol unit name prefix %s" ), name.c_str() );
449 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
450 }
451
452 name = name.Right( name.Length() - m_symbolName.Length() - 1 );
453
454 wxStringTokenizer tokenizer( name, "_" );
455
456 if( tokenizer.CountTokens() != 2 )
457 {
458 error.Printf( _( "Invalid symbol unit name suffix %s" ), name.c_str() );
459 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
460 }
461
462 if( !tokenizer.GetNextToken().ToLong( &tmp ) )
463 {
464 error.Printf( _( "Invalid symbol unit number %s" ), name.c_str() );
465 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
466 }
467
468 m_unit = static_cast<int>( tmp );
469
470 if( !tokenizer.GetNextToken().ToLong( &tmp ) )
471 {
472 error.Printf( _( "Invalid symbol convert number %s" ), name.c_str() );
473 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
474 }
475
476 m_bodyStyle = static_cast<int>( tmp );
477
478 if( m_bodyStyle > 1 )
479 symbol->SetHasAlternateBodyStyle( true, false );
480
481 if( m_unit > symbol->GetUnitCount() )
482 symbol->SetUnitCount( m_unit, false );
483
484 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
485 {
486 if( token != T_LEFT )
487 Expecting( T_LEFT );
488
489 token = NextTok();
490
491 switch( token )
492 {
493 case T_unit_name:
494 token = NextTok();
495
496 if( IsSymbol( token ) )
497 {
498 unitDisplayName = FromUTF8();
499 symbol->SetUnitDisplayName( m_unit, unitDisplayName );
500 }
501
502 NeedRIGHT();
503 break;
504
505 case T_arc:
506 case T_bezier:
507 case T_circle:
508 case T_pin:
509 case T_polyline:
510 case T_rectangle:
511 case T_text:
512 case T_text_box:
513 item = ParseSymbolDrawItem();
514
515 wxCHECK_MSG( item, nullptr, "Invalid draw item pointer." );
516
517 item->SetParent( symbol.get() );
518 symbol->AddDrawItem( item, false );
519 break;
520
521 default:
522 Expecting( "arc, bezier, circle, pin, polyline, rectangle, or text" );
523 };
524 }
525
526 m_unit = 1;
527 m_bodyStyle = 1;
528 break;
529 }
530
531 case T_arc:
532 case T_bezier:
533 case T_circle:
534 case T_pin:
535 case T_polyline:
536 case T_rectangle:
537 case T_text:
538 case T_text_box:
539 item = ParseSymbolDrawItem();
540
541 wxCHECK_MSG( item, nullptr, "Invalid draw item pointer." );
542
543 item->SetParent( symbol.get() );
544 symbol->AddDrawItem( item, false );
545 break;
546
547 case T_embedded_fonts:
548 {
549 symbol->SetAreFontsEmbedded( parseBool() );
550 NeedRIGHT();
551 break;
552 }
553
554 case T_embedded_files:
555 {
556 EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
557 embeddedFilesParser.SyncLineReaderWith( *this );
558
559 try
560 {
561 embeddedFilesParser.ParseEmbedded( symbol->GetEmbeddedFiles() );
562 }
563 catch( const IO_ERROR& e )
564 {
565 wxLogError( e.What() );
566 }
567
568 SyncLineReaderWith( embeddedFilesParser );
569 break;
570 }
571
572 default:
573 Expecting( "pin_names, pin_numbers, arc, bezier, circle, pin, polyline, "
574 "rectangle, or text" );
575 }
576 }
577
578 symbol->GetDrawItems().sort();
579 m_symbolName.clear();
580
581 const std::vector<wxString>* embeddedFonts =
582 symbol->GetEmbeddedFiles()->UpdateFontFiles();
583
584 symbol->RunOnChildren(
585 [&]( SCH_ITEM* aChild )
586 {
587 if( EDA_TEXT* textItem = dynamic_cast<EDA_TEXT*>( aChild ) )
588 textItem->ResolveFont( embeddedFonts );
589 } );
590
591 return symbol.release();
592}
593
594
596{
597 switch( CurTok() )
598 {
599 case T_arc: return parseSymbolArc(); break;
600 case T_bezier: return parseSymbolBezier(); break;
601 case T_circle: return parseSymbolCircle(); break;
602 case T_pin: return parseSymbolPin(); break;
603 case T_polyline: return parseSymbolPolyLine(); break;
604 case T_rectangle: return parseSymbolRectangle(); break;
605 case T_text: return parseSymbolText(); break;
606 case T_text_box: return parseSymbolTextBox(); break;
607 default: Expecting( "arc, bezier, circle, pin, polyline, rectangle, or text" );
608 }
609
610 return nullptr;
611}
612
613
615{
616 auto retval = parseDouble() * schIUScale.IU_PER_MM;
617
618 // Schematic internal units are represented as integers. Any values that are
619 // larger or smaller than the schematic units represent undefined behavior for
620 // the system. Limit values to the largest that can be displayed on the screen.
621 constexpr double int_limit = std::numeric_limits<int>::max() * 0.7071; // 0.7071 = roughly 1/sqrt(2)
622
623 return KiROUND( std::clamp( retval, -int_limit, int_limit ) );
624}
625
626
628{
629 auto retval = parseDouble( aExpected ) * schIUScale.IU_PER_MM;
630
631 constexpr double int_limit = std::numeric_limits<int>::max() * 0.7071;
632
633 return KiROUND( std::clamp( retval, -int_limit, int_limit ) );
634}
635
636
638{
639 STROKE_PARAMS_PARSER strokeParser( reader, schIUScale.IU_PER_MM );
640 strokeParser.SyncLineReaderWith( *this );
641
642 strokeParser.ParseStroke( aStroke );
643 SyncLineReaderWith( strokeParser );
644}
645
646
648{
649 wxCHECK_RET( CurTok() == T_fill, "Cannot parse " + GetTokenString( CurTok() ) + " as a fill." );
650
651 aFill.m_FillType = FILL_T::NO_FILL;
652 aFill.m_Color = COLOR4D::UNSPECIFIED;
653
654 T token;
655
656 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
657 {
658 if( token != T_LEFT )
659 Expecting( T_LEFT );
660
661 token = NextTok();
662
663 switch( token )
664 {
665 case T_type:
666 {
667 token = NextTok();
668
669 switch( token )
670 {
671 case T_none: aFill.m_FillType = FILL_T::NO_FILL; break;
672 case T_outline: aFill.m_FillType = FILL_T::FILLED_SHAPE; break;
673 case T_background: aFill.m_FillType = FILL_T::FILLED_WITH_BG_BODYCOLOR; break;
674 case T_color: aFill.m_FillType = FILL_T::FILLED_WITH_COLOR; break;
675 default: Expecting( "none, outline, color or background" );
676 }
677
678 NeedRIGHT();
679 break;
680 }
681
682 case T_color:
683 {
685
686 color.r = parseInt( "red" ) / 255.0;
687 color.g = parseInt( "green" ) / 255.0;
688 color.b = parseInt( "blue" ) / 255.0;
689 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
690 aFill.m_Color = color;
691 NeedRIGHT();
692 break;
693 }
694
695 default:
696 Expecting( "type or color" );
697 }
698 }
699}
700
701
702void SCH_IO_KICAD_SEXPR_PARSER::parseEDA_TEXT( EDA_TEXT* aText, bool aConvertOverbarSyntax,
703 bool aEnforceMinTextSize )
704{
705 wxCHECK_RET( aText && ( CurTok() == T_effects || CurTok() == T_href ),
706 "Cannot parse " + GetTokenString( CurTok() ) + " as an EDA_TEXT." );
707
708 // In version 20210606 the notation for overbars was changed from `~...~` to `~{...}`.
709 // We need to convert the old syntax to the new one.
710 if( aConvertOverbarSyntax && m_requiredVersion < 20210606 )
711 aText->SetText( ConvertToNewOverbarNotation( aText->GetText() ) );
712
713 T token;
714 bool bold = false;
715 bool italic = false;
716 COLOR4D color = COLOR4D::UNSPECIFIED;
717
718 // Various text objects (text boxes, schematic text, etc.) all have their own defaults,
719 // but the file format default is {center,center} so we have to set that before parsing.
722
723 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
724 {
725 if( token == T_LEFT )
726 token = NextTok();
727
728 switch( token )
729 {
730 case T_font:
731 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
732 {
733 if( token == T_LEFT )
734 token = NextTok();
735
736 switch( token )
737 {
738 case T_face:
739 NeedSYMBOL();
740 aText->SetUnresolvedFontName( FromUTF8() );
741 NeedRIGHT();
742 break;
743
744 case T_size:
745 {
746 VECTOR2I sz;
747 sz.y = parseInternalUnits( "text height" );
748 sz.x = parseInternalUnits( "text width" );
749 aText->SetTextSize( sz, aEnforceMinTextSize );
750 NeedRIGHT();
751 break;
752 }
753
754 case T_thickness:
755 aText->SetTextThickness( parseInternalUnits( "text thickness" ) );
756 NeedRIGHT();
757 break;
758
759 case T_bold:
760 bold = parseMaybeAbsentBool( true );
761 aText->SetBoldFlag( bold );
762 break;
763
764 case T_italic:
765 italic = parseMaybeAbsentBool( true );
766 aText->SetItalicFlag( italic );
767 break;
768
769 case T_color:
770 color.r = parseInt( "red" ) / 255.0;
771 color.g = parseInt( "green" ) / 255.0;
772 color.b = parseInt( "blue" ) / 255.0;
773 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
774 aText->SetTextColor( color );
775 NeedRIGHT();
776 break;
777
778 case T_line_spacing:
779 aText->SetLineSpacing( parseDouble( "line spacing" ) );
780 NeedRIGHT();
781 break;
782
783 default:
784 Expecting( "face, size, thickness, line_spacing, bold, or italic" );
785 }
786 }
787
788 break;
789
790 case T_justify:
791 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
792 {
793 switch( token )
794 {
795 case T_left: aText->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT ); break;
796 case T_right: aText->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT ); break;
797 case T_top: aText->SetVertJustify( GR_TEXT_V_ALIGN_TOP ); break;
798 case T_bottom: aText->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM ); break;
799 // Do not set mirror property for schematic text elements
800 case T_mirror: break;
801 default: Expecting( "left, right, top, bottom, or mirror" );
802 }
803 }
804
805 break;
806
807 case T_href:
808 {
809 NeedSYMBOL();
810 wxString hyperlink = FromUTF8();
811
812 if( !EDA_TEXT::ValidateHyperlink( hyperlink ) )
813 {
814 THROW_PARSE_ERROR( wxString::Format( _( "Invalid hyperlink url '%s'" ), hyperlink ),
815 CurSource(), CurLine(), CurLineNumber(), CurOffset() );
816 }
817 else
818 {
819 aText->SetHyperlink( hyperlink );
820 }
821
822 NeedRIGHT();
823 }
824 break;
825
826 case T_hide:
827 {
828 bool hide = parseMaybeAbsentBool( true );
829 aText->SetVisible( !hide );
830 break;
831 }
832
833 default:
834 Expecting( "font, justify, hide or href" );
835 }
836 }
837}
838
839
840void SCH_IO_KICAD_SEXPR_PARSER::parseHeader( TSCHEMATIC_T::T aHeaderType, int aFileVersion )
841{
842 wxCHECK_RET( CurTok() == aHeaderType,
843 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a header." ) );
844
845 NeedLEFT();
846
847 T tok = NextTok();
848
849 if( tok == T_version )
850 {
851 m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
852 NeedRIGHT();
853 }
854 else
855 {
856 m_requiredVersion = aFileVersion;
857 }
858}
859
860
861void SCH_IO_KICAD_SEXPR_PARSER::parsePinNames( std::unique_ptr<LIB_SYMBOL>& aSymbol )
862{
863 wxCHECK_RET( CurTok() == T_pin_names,
864 "Cannot parse " + GetTokenString( CurTok() ) + " as a pin_name token." );
865
874 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
875 {
876 // Pre-20241004 format - bare 'hide' keyword
877 if( token == T_hide )
878 {
879 aSymbol->SetShowPinNames( false );
880 continue;
881 }
882
883 if( token != T_LEFT )
884 {
885 Expecting( T_LEFT );
886 }
887
888 token = NextTok();
889
890 switch( token )
891 {
892 case T_offset:
893 aSymbol->SetPinNameOffset( parseInternalUnits( "pin name offset" ) );
894 NeedRIGHT();
895 break;
896
897 case T_hide:
898 aSymbol->SetShowPinNames( !parseBool() );
899 NeedRIGHT();
900 break;
901
902 default:
903 Expecting( "offset or hide" );
904 }
905 }
906}
907
908
909void SCH_IO_KICAD_SEXPR_PARSER::parsePinNumbers( std::unique_ptr<LIB_SYMBOL>& aSymbol )
910{
911 wxCHECK_RET( CurTok() == T_pin_numbers,
912 "Cannot parse " + GetTokenString( CurTok() ) + " as a pin_number token." );
913
921 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
922 {
923 // Pre-20241004 format - bare 'hide' keyword
924 if( token == T_hide )
925 {
926 aSymbol->SetShowPinNumbers( false );
927 continue;
928 }
929
930 if( token != T_LEFT )
931 {
932 Expecting( T_LEFT );
933 }
934
935 token = NextTok();
936
937 switch( token )
938 {
939 case T_hide:
940 aSymbol->SetShowPinNumbers( !parseBool() );
941 NeedRIGHT();
942 break;
943
944 default:
945 Expecting( "hide" );
946 break;
947 }
948 }
949}
950
951
952SCH_FIELD* SCH_IO_KICAD_SEXPR_PARSER::parseProperty( std::unique_ptr<LIB_SYMBOL>& aSymbol )
953{
954 wxCHECK_MSG( CurTok() == T_property, nullptr,
955 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a property." ) );
956 wxCHECK( aSymbol, nullptr );
957
958 wxString name;
959 wxString value;
960 auto field = std::make_unique<SCH_FIELD>( aSymbol.get(), aSymbol->GetNextAvailableFieldId() );
961
962 // By default, fieds are visible.
963 // Invisible fields have the hide style or keyword specified in file
964 field->SetVisible( true );
965
966 T token = NextTok();
967
968 if( token == T_private )
969 {
970 field->SetPrivate( true );
971 token = NextTok();
972 }
973
974 if( !IsSymbol( token ) )
975 {
976 THROW_PARSE_ERROR( _( "Invalid property name" ), CurSource(), CurLine(), CurLineNumber(),
977 CurOffset() );
978 }
979
980 name = FromUTF8();
981
982 if( name.IsEmpty() )
983 {
984 THROW_PARSE_ERROR( _( "Empty property name" ), CurSource(), CurLine(), CurLineNumber(),
985 CurOffset() );
986 }
987
988 field->SetName( name );
989
990 // Correctly set the ID based on canonical (untranslated) field name
991 // If ID is stored in the file (old versions), it will overwrite this
992 for( int fieldId : MANDATORY_FIELDS )
993 {
994 if( name.CmpNoCase( GetCanonicalFieldName( fieldId ) ) == 0 )
995 {
996 field->SetId( fieldId );
997 break;
998 }
999 }
1000
1001 token = NextTok();
1002
1003 if( !IsSymbol( token ) )
1004 {
1005 THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
1006 CurOffset() );
1007 }
1008
1009 // Empty property values are valid.
1010 value = FromUTF8();
1011
1012 field->SetText( value );
1013
1014 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1015 {
1016 if( token != T_LEFT )
1017 Expecting( T_LEFT );
1018
1019 token = NextTok();
1020
1021 switch( token )
1022 {
1023 // I am not sure we should even support parsing these IDs any more
1024 case T_id:
1025 {
1026 int id = parseInt( "field ID" );
1027
1028 // Only set an ID that isn't a MANDATORY_FIELD ID
1029 if( id >= MANDATORY_FIELD_COUNT )
1030 field->SetId( id );
1031
1032 NeedRIGHT();
1033 }
1034 break;
1035
1036 case T_at:
1037 field->SetPosition( parseXY( true ) );
1038 field->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ) );
1039 NeedRIGHT();
1040 break;
1041
1042 case T_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_FIELD_COUNT ) && 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 nextFieldId = 0;
2218
2219 if( aParent->Type() == SCH_SYMBOL_T )
2220 nextFieldId = MANDATORY_FIELD_COUNT;
2221 else if( aParent->Type() == SCH_SHEET_T )
2222 nextFieldId = SHEET_MANDATORY_FIELD_COUNT;
2223
2224 std::unique_ptr<SCH_FIELD> field = std::make_unique<SCH_FIELD>( VECTOR2I( -1, -1 ), nextFieldId,
2225 aParent, name );
2226 field->SetText( value );
2227 field->SetVisible( true );
2228 field->SetPrivate( is_private );
2229
2230 // Correctly set the ID based on canonical (untranslated) field name
2231 // If ID is stored in the file (old versions), it will overwrite this
2232 if( aParent->Type() == SCH_SYMBOL_T )
2233 {
2234 for( int fieldId : MANDATORY_FIELDS )
2235 {
2236 if( name == GetCanonicalFieldName( fieldId ) )
2237 {
2238 field->SetId( fieldId );
2239 field->SetPrivate( false );
2240 break;
2241 }
2242 }
2243 }
2244 else if( aParent->Type() == SCH_SHEET_T )
2245 {
2246 for( int fieldId : SHEET_MANDATORY_FIELDS )
2247 {
2249 {
2250 field->SetId( fieldId );
2251 field->SetPrivate( false );
2252 break;
2253 }
2254 }
2255
2256 // Legacy support for old field names
2257 if( name.CmpNoCase( wxT( "Sheet name" ) ) == 0 )
2258 field->SetId( SHEETNAME );
2259 else if( name.CmpNoCase( wxT( "Sheet file" ) ) == 0 )
2260 field->SetId( SHEETFILENAME );
2261 }
2262
2263 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2264 {
2265 if( token != T_LEFT )
2266 Expecting( T_LEFT );
2267
2268 token = NextTok();
2269
2270 switch( token )
2271 {
2272 // I am not sure we should even support parsing these IDs any more
2273 case T_id:
2274 {
2275 int id = parseInt( "field ID" );
2276
2277 // Only set an ID that isn't a mandatory field ID
2278 if( id >= nextFieldId )
2279 field->SetId( id );
2280
2281 NeedRIGHT();
2282 }
2283 break;
2284
2285 case T_at:
2286 field->SetPosition( parseXY() );
2287 field->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ) );
2288 NeedRIGHT();
2289 break;
2290
2291 case T_effects:
2292 parseEDA_TEXT( static_cast<EDA_TEXT*>( field.get() ), field->GetId() == VALUE_FIELD );
2293 break;
2294
2295 case T_show_name:
2296 {
2297 bool show = parseMaybeAbsentBool( true );
2298 field->SetNameShown( show );
2299 break;
2300 }
2301
2302 case T_do_not_autoplace:
2303 {
2304 bool doNotAutoplace = parseMaybeAbsentBool( true );
2305 field->SetCanAutoplace( !doNotAutoplace );
2306 break;
2307 }
2308
2309 default:
2310 Expecting( "id, at, show_name, do_not_autoplace or effects" );
2311 }
2312 }
2313
2314 return field.release();
2315}
2316
2317
2319{
2320 wxCHECK_MSG( aSheet != nullptr, nullptr, "" );
2321 wxCHECK_MSG( CurTok() == T_pin, nullptr,
2322 "Cannot parse " + GetTokenString( CurTok() ) + " as a sheet pin token." );
2323
2324 T token = NextTok();
2325
2326 if( !IsSymbol( token ) )
2327 {
2328 THROW_PARSE_ERROR( _( "Invalid sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
2329 CurOffset() );
2330 }
2331
2332 wxString name = FromUTF8();
2333
2334 if( name.IsEmpty() )
2335 {
2336 THROW_PARSE_ERROR( _( "Empty sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
2337 CurOffset() );
2338 }
2339
2340 auto sheetPin = std::make_unique<SCH_SHEET_PIN>( aSheet, VECTOR2I( 0, 0 ), name );
2341
2342 token = NextTok();
2343
2344 switch( token )
2345 {
2346 case T_input: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
2347 case T_output: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
2348 case T_bidirectional: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
2349 case T_tri_state: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE ); break;
2350 case T_passive: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
2351 default:
2352 Expecting( "input, output, bidirectional, tri_state, or passive" );
2353 }
2354
2355 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2356 {
2357 if( token != T_LEFT )
2358 Expecting( T_LEFT );
2359
2360 token = NextTok();
2361
2362 switch( token )
2363 {
2364 case T_at:
2365 {
2366 sheetPin->SetPosition( parseXY() );
2367
2368 double angle = parseDouble( "sheet pin angle (side)" );
2369
2370 if( angle == 0.0 )
2371 sheetPin->SetSide( SHEET_SIDE::RIGHT );
2372 else if( angle == 90.0 )
2373 sheetPin->SetSide( SHEET_SIDE::TOP );
2374 else if( angle == 180.0 )
2375 sheetPin->SetSide( SHEET_SIDE::LEFT );
2376 else if( angle == 270.0 )
2377 sheetPin->SetSide( SHEET_SIDE::BOTTOM );
2378 else
2379 Expecting( "0, 90, 180, or 270" );
2380
2381 NeedRIGHT();
2382 break;
2383 }
2384
2385 case T_effects:
2386 parseEDA_TEXT( static_cast<EDA_TEXT*>( sheetPin.get() ), true );
2387 break;
2388
2389 case T_uuid:
2390 NeedSYMBOL();
2391 const_cast<KIID&>( sheetPin->m_Uuid ) = parseKIID();
2392 NeedRIGHT();
2393 break;
2394
2395 default:
2396 Expecting( "at, uuid or effects" );
2397 }
2398 }
2399
2400 return sheetPin.release();
2401}
2402
2403
2405{
2406 wxCHECK_RET( CurTok() == T_sheet_instances,
2407 "Cannot parse " + GetTokenString( CurTok() ) + " as an instances token." );
2408 wxCHECK( aScreen, /* void */ );
2409
2410 T token;
2411
2412 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2413 {
2414 if( token != T_LEFT )
2415 Expecting( T_LEFT );
2416
2417 token = NextTok();
2418
2419 switch( token )
2420 {
2421 case T_path:
2422 {
2423 NeedSYMBOL();
2424
2425 SCH_SHEET_INSTANCE instance;
2426
2427 instance.m_Path = KIID_PATH( FromUTF8() );
2428
2429 if( ( !m_appending && aRootSheet->GetScreen() == aScreen ) &&
2430 ( aScreen->GetFileFormatVersionAtLoad() < 20221002 ) )
2431 instance.m_Path.insert( instance.m_Path.begin(), m_rootUuid );
2432
2433 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2434 {
2435 if( token != T_LEFT )
2436 Expecting( T_LEFT );
2437
2438 token = NextTok();
2439
2440 std::vector<wxString> whitespaces = { wxT( "\r" ), wxT( "\n" ), wxT( "\t" ),
2441 wxT( " " ) };
2442
2443 size_t numReplacements = 0;
2444
2445 switch( token )
2446 {
2447 case T_page:
2448 NeedSYMBOL();
2449 instance.m_PageNumber = FromUTF8();
2450
2451 // Empty page numbers are not permitted
2452 if( instance.m_PageNumber.IsEmpty() )
2453 {
2454 // Use hash character instead
2455 instance.m_PageNumber = wxT( "#" );
2456 numReplacements++;
2457 }
2458 else
2459 {
2460 // Whitespaces are not permitted
2461 for( const wxString& ch : whitespaces )
2462 numReplacements += instance.m_PageNumber.Replace( ch, wxEmptyString );
2463
2464 }
2465
2466 // Set the file as modified so the user can be warned.
2467 if( numReplacements > 0 )
2468 aScreen->SetContentModified();
2469
2470 NeedRIGHT();
2471 break;
2472
2473 default:
2474 Expecting( "path or page" );
2475 }
2476 }
2477
2478 if( ( aScreen->GetFileFormatVersionAtLoad() >= 20221110 )
2479 && ( instance.m_Path.empty() ) )
2480 {
2481 SCH_SHEET_PATH rootSheetPath;
2482
2483 rootSheetPath.push_back( aRootSheet );
2484 rootSheetPath.SetPageNumber( instance.m_PageNumber );
2485 }
2486 else
2487 {
2488 aScreen->m_sheetInstances.emplace_back( instance );
2489 }
2490
2491 break;
2492 }
2493
2494 default:
2495 Expecting( "path" );
2496 }
2497 }
2498}
2499
2500
2502{
2503 wxCHECK_RET( CurTok() == T_symbol_instances,
2504 "Cannot parse " + GetTokenString( CurTok() ) + " as an instances token." );
2505 wxCHECK( aScreen, /* void */ );
2506 wxCHECK( m_rootUuid != NilUuid(), /* void */ );
2507
2508 T token;
2509
2510 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2511 {
2512 if( token != T_LEFT )
2513 Expecting( T_LEFT );
2514
2515 token = NextTok();
2516
2517 switch( token )
2518 {
2519 case T_path:
2520 {
2521 NeedSYMBOL();
2522
2523 SCH_SYMBOL_INSTANCE instance;
2524
2525 instance.m_Path = KIID_PATH( FromUTF8() );
2526
2527 if( !m_appending )
2528 instance.m_Path.insert( instance.m_Path.begin(), m_rootUuid );
2529
2530 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2531 {
2532 if( token != T_LEFT )
2533 Expecting( T_LEFT );
2534
2535 token = NextTok();
2536
2537 switch( token )
2538 {
2539 case T_reference:
2540 NeedSYMBOL();
2541 instance.m_Reference = FromUTF8();
2542 NeedRIGHT();
2543 break;
2544
2545 case T_unit:
2546 instance.m_Unit = parseInt( "symbol unit" );
2547 NeedRIGHT();
2548 break;
2549
2550 case T_value:
2551 NeedSYMBOL();
2552 instance.m_Value = FromUTF8();
2553 NeedRIGHT();
2554 break;
2555
2556 case T_footprint:
2557 NeedSYMBOL();
2558 instance.m_Footprint = FromUTF8();
2559 NeedRIGHT();
2560 break;
2561
2562 default:
2563 Expecting( "path, unit, value or footprint" );
2564 }
2565 }
2566
2567 aScreen->m_symbolInstances.emplace_back( instance );
2568 break;
2569 }
2570
2571 default:
2572 Expecting( "path" );
2573 }
2574 }
2575}
2576
2577
2578void SCH_IO_KICAD_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly,
2579 int aFileVersion )
2580{
2581 wxCHECK( aSheet != nullptr, /* void */ );
2582
2583 SCH_SCREEN* screen = aSheet->GetScreen();
2584
2585 wxCHECK( screen != nullptr, /* void */ );
2586
2587 if( aIsCopyableOnly )
2588 m_requiredVersion = aFileVersion;
2589
2590 bool fileHasUuid = false;
2591
2592 auto checkVersion =
2593 [&]()
2594 {
2596 {
2597 throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
2599 }
2600 };
2601
2602 T token;
2603
2604 if( !aIsCopyableOnly )
2605 {
2606 NeedLEFT();
2607 NextTok();
2608
2609 if( CurTok() != T_kicad_sch )
2610 Expecting( "kicad_sch" );
2611
2613
2614 // Prior to schematic file version 20210406, schematics did not have UUIDs so we need
2615 // to generate one for the root schematic for instance paths.
2616 if( m_requiredVersion < 20210406 )
2617 m_rootUuid = screen->GetUuid();
2618
2619 // Prior to version 20231120, generator_version was not present, so check the date here
2620 if( m_requiredVersion < 20231120 )
2621 checkVersion();
2622 }
2623
2625
2626 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2627 {
2628 if( aIsCopyableOnly && token == T_EOF )
2629 break;
2630
2631 if( token != T_LEFT )
2632 Expecting( T_LEFT );
2633
2634 token = NextTok();
2635
2636 checkpoint();
2637
2638 if( !aIsCopyableOnly && token == T_page && m_requiredVersion <= 20200506 )
2639 token = T_paper;
2640
2641 switch( token )
2642 {
2643 case T_generator:
2644 // (generator "genname"); we don't care about it at the moment.
2645 NeedSYMBOL();
2646 NeedRIGHT();
2647 break;
2648
2649 case T_host:
2650 {
2651 // (host eeschema ["5.99"]); old version of generator token
2652 NeedSYMBOL();
2653
2654 // Really old versions also included a host version
2655 if( m_requiredVersion < 20200827 )
2656 NeedSYMBOL();
2657
2658 NeedRIGHT();
2659 break;
2660 }
2661
2662 case T_generator_version:
2663 NextTok();
2664 m_generatorVersion = FromUTF8();
2665 NeedRIGHT();
2666 checkVersion();
2667 break;
2668
2669 case T_uuid:
2670 NeedSYMBOL();
2671 screen->m_uuid = parseKIID();
2672
2673 // Set the root sheet UUID with the schematic file UUID. Root sheets are virtual
2674 // and always get a new UUID so this prevents file churn now that the root UUID
2675 // is saved in the symbol instance path.
2676 if( aSheet == m_rootSheet )
2677 {
2678 const_cast<KIID&>( aSheet->m_Uuid ) = screen->GetUuid();
2679 m_rootUuid = screen->GetUuid();
2680 fileHasUuid = true;
2681 }
2682
2683 NeedRIGHT();
2684 break;
2685
2686 case T_paper:
2687 {
2688 if( aIsCopyableOnly )
2689 Unexpected( T_paper );
2690
2691 PAGE_INFO pageInfo;
2692 parsePAGE_INFO( pageInfo );
2693 screen->SetPageSettings( pageInfo );
2694 break;
2695 }
2696
2697 case T_page:
2698 {
2699 if( aIsCopyableOnly )
2700 Unexpected( T_page );
2701
2702 // Only saved for top-level sniffing in Kicad Manager frame and other external
2703 // tool usage with flat hierarchies
2704 NeedSYMBOLorNUMBER();
2705 NeedSYMBOLorNUMBER();
2706 NeedRIGHT();
2707 break;
2708 }
2709
2710 case T_title_block:
2711 {
2712 if( aIsCopyableOnly )
2713 Unexpected( T_title_block );
2714
2715 TITLE_BLOCK tb;
2716 parseTITLE_BLOCK( tb );
2717 screen->SetTitleBlock( tb );
2718 break;
2719 }
2720
2721 case T_lib_symbols:
2722 {
2723 // Dummy map. No derived symbols are allowed in the library cache.
2724 LIB_SYMBOL_MAP symbolLibMap;
2725 LIB_SYMBOL* symbol;
2726
2727 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2728 {
2729 if( token != T_LEFT )
2730 Expecting( T_LEFT );
2731
2732 token = NextTok();
2733
2734 switch( token )
2735 {
2736 case T_symbol:
2737 symbol = parseLibSymbol( symbolLibMap );
2738 symbol->UpdateFieldOrdinals();
2739 screen->AddLibSymbol( symbol );
2740 break;
2741
2742 default:
2743 Expecting( "symbol" );
2744 }
2745 }
2746
2747 break;
2748 }
2749
2750 case T_symbol:
2751 screen->Append( static_cast<SCH_ITEM*>( parseSchematicSymbol() ) );
2752 break;
2753
2754 case T_image:
2755 screen->Append( static_cast<SCH_ITEM*>( parseImage() ) );
2756 break;
2757
2758 case T_sheet:
2759 {
2760 SCH_SHEET* sheet = parseSheet();
2761
2762 // Set the parent to aSheet. This effectively creates a method to find
2763 // the root sheet from any sheet so a pointer to the root sheet does not
2764 // need to be stored globally. Note: this is not the same as a hierarchy.
2765 // Complex hierarchies can have multiple copies of a sheet. This only
2766 // provides a simple tree to find the root sheet.
2767 sheet->SetParent( aSheet );
2768 screen->Append( sheet );
2769 break;
2770 }
2771
2772 case T_junction:
2773 screen->Append( parseJunction() );
2774 break;
2775
2776 case T_no_connect:
2777 screen->Append( parseNoConnect() );
2778 break;
2779
2780 case T_bus_entry:
2781 screen->Append( parseBusEntry() );
2782 break;
2783
2784 case T_polyline:
2785 {
2786 // polyline keyword is used in eeschema both for SCH_SHAPE and SCH_LINE items.
2787 // In symbols it describes a polygon, having n corners and can be filled
2788 // In schematic it describes a line (with no fill descr), but could be extended to a
2789 // polygon (for instance when importing files) because the schematic handles all
2790 // SCH_SHAPE.
2791
2792 // parseSchPolyLine() returns always a SCH_SHAPE, io convert it to a simple SCH_LINE
2793 // For compatibility reasons, keep SCH_SHAPE for a polygon and convert to SCH_LINE
2794 // when the item has only 2 corners, similar to a SCH_LINE
2795 SCH_SHAPE* poly = parseSchPolyLine();
2796
2797 if( poly->GetPointCount() > 2 )
2798 {
2799 screen->Append( poly );
2800 }
2801 else
2802 {
2803 // For SCH_SHAPE having only 2 points, this is a "old" SCH_LINE entity.
2804 // So convert the SCH_SHAPE to a simple SCH_LINE
2805 SCH_LINE* line = new SCH_LINE( VECTOR2I(), LAYER_NOTES );
2806 SHAPE_LINE_CHAIN& outline = poly->GetPolyShape().Outline(0);
2807 line->SetStartPoint( outline.CPoint(0) );
2808 line->SetEndPoint( outline.CPoint(1) );
2809 line->SetStroke( poly->GetStroke() );
2810 const_cast<KIID&>( line->m_Uuid ) = poly->m_Uuid;
2811
2812 screen->Append( line );
2813
2814 delete poly;
2815 }
2816 }
2817 break;
2818
2819 case T_bus:
2820 case T_wire:
2821 screen->Append( parseLine() );
2822 break;
2823
2824 case T_arc:
2825 screen->Append( parseSchArc() );
2826 break;
2827
2828 case T_circle:
2829 screen->Append( parseSchCircle() );
2830 break;
2831
2832 case T_rectangle:
2833 screen->Append( parseSchRectangle() );
2834 break;
2835
2836 case T_bezier:
2837 screen->Append( parseSchBezier() );
2838 break;
2839
2840 case T_rule_area:
2841 screen->Append( parseSchRuleArea() );
2842 break;
2843
2844 case T_netclass_flag: // present only during early development of 7.0
2846
2847 case T_text:
2848 case T_label:
2849 case T_global_label:
2850 case T_hierarchical_label:
2851 case T_directive_label:
2852 screen->Append( parseSchText() );
2853 break;
2854
2855 case T_text_box:
2856 screen->Append( parseSchTextBox() );
2857 break;
2858
2859 case T_table:
2860 screen->Append( parseSchTable() );
2861 break;
2862
2863 case T_sheet_instances:
2864 parseSchSheetInstances( aSheet, screen );
2865 break;
2866
2867 case T_symbol_instances:
2868 parseSchSymbolInstances( screen );
2869 break;
2870
2871 case T_bus_alias:
2872 if( aIsCopyableOnly )
2873 Unexpected( T_bus_alias );
2874
2875 parseBusAlias( screen );
2876 break;
2877
2878 case T_embedded_fonts:
2879 {
2880 SCHEMATIC* schematic = screen->Schematic();
2881
2882 if( !schematic )
2883 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
2884 CurLineNumber(), CurOffset() );
2885
2887 NeedRIGHT();
2888 break;
2889 }
2890
2891 case T_embedded_files:
2892 {
2893 SCHEMATIC* schematic = screen->Schematic();
2894
2895 if( !schematic )
2896 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
2897 CurLineNumber(), CurOffset() );
2898
2899 EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
2900 embeddedFilesParser.SyncLineReaderWith( *this );
2901
2902 try
2903 {
2904 embeddedFilesParser.ParseEmbedded( schematic->GetEmbeddedFiles() );
2905 }
2906 catch( const PARSE_ERROR& e )
2907 {
2908 wxLogError( e.What() );
2909 }
2910
2911 SyncLineReaderWith( embeddedFilesParser );
2912 break;
2913 }
2914
2915
2916 default:
2917 Expecting( "bitmap, bus, bus_alias, bus_entry, class_label, embedded_files, global_label, "
2918 "hierarchical_label, junction, label, line, no_connect, page, paper, rule_area, "
2919 "sheet, symbol, symbol_instances, text, title_block" );
2920 }
2921 }
2922
2923 // Older s-expression schematics may not have a UUID so use the one automatically generated
2924 // as the virtual root sheet UUID.
2925 if( ( aSheet == m_rootSheet ) && !fileHasUuid )
2926 {
2927 const_cast<KIID&>( aSheet->m_Uuid ) = screen->GetUuid();
2928 m_rootUuid = screen->GetUuid();
2929 }
2930
2931 screen->UpdateLocalLibSymbolLinks();
2932 screen->FixupEmbeddedData();
2933
2934 SCHEMATIC* schematic = screen->Schematic();
2935
2936 if( !schematic )
2937 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
2938 CurLineNumber(), CurOffset() );
2939
2940 // When loading the schematic, take a moment to cache the fonts so that the font
2941 // picker can show the embedded fonts immediately.
2942 std::vector<std::string> fontNames;
2943 Fontconfig()->ListFonts( fontNames, std::string( Pgm().GetLanguageTag().utf8_str() ),
2944 schematic->GetEmbeddedFiles()->GetFontFiles(), true );
2945
2946 if( m_requiredVersion < 20200828 )
2948}
2949
2950
2952{
2953 wxCHECK_MSG( CurTok() == T_symbol, nullptr,
2954 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
2955
2956 T token;
2957 wxString libName;
2958 SCH_FIELD* field;
2959 std::unique_ptr<SCH_SYMBOL> symbol = std::make_unique<SCH_SYMBOL>();
2960 TRANSFORM transform;
2961 std::set<int> fieldIDsRead;
2962
2963 // We'll reset this if we find a fields_autoplaced token
2964 symbol->SetFieldsAutoplaced( AUTOPLACE_NONE );
2965
2966 m_fieldIDsRead.clear();
2967
2968 // Make sure the mandatory field IDs are reserved as already read,
2969 // the field parser will set the field IDs to the correct value if
2970 // the field name matches a mandatory field name
2971 for( int fieldId : MANDATORY_FIELDS )
2972 m_fieldIDsRead.insert( fieldId );
2973
2974 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2975 {
2976 if( token != T_LEFT )
2977 Expecting( T_LEFT );
2978
2979 token = NextTok();
2980
2981 switch( token )
2982 {
2983 case T_lib_name:
2984 {
2985 LIB_ID libId;
2986
2987 token = NextTok();
2988
2989 if( !IsSymbol( token ) )
2990 {
2991 THROW_PARSE_ERROR( _( "Invalid symbol library name" ), CurSource(), CurLine(),
2992 CurLineNumber(), CurOffset() );
2993 }
2994
2995 libName = FromUTF8();
2996 // Some symbol LIB_IDs or lib_name have the '/' character escaped which can break
2997 // symbol links. The '/' character is no longer an illegal LIB_ID character so
2998 // it doesn't need to be escaped.
2999 libName.Replace( "{slash}", "/" );
3000
3001 NeedRIGHT();
3002 break;
3003 }
3004
3005 case T_lib_id:
3006 {
3007 token = NextTok();
3008
3009 if( !IsSymbol( token ) && token != T_NUMBER )
3010 Expecting( "symbol|number" );
3011
3012 LIB_ID libId;
3013 wxString name = FromUTF8();
3014 // Some symbol LIB_IDs have the '/' character escaped which can break
3015 // symbol links. The '/' character is no longer an illegal LIB_ID character so
3016 // it doesn't need to be escaped.
3017 name.Replace( "{slash}", "/" );
3018
3019 int bad_pos = libId.Parse( name );
3020
3021 if( bad_pos >= 0 )
3022 {
3023 if( static_cast<int>( name.size() ) > bad_pos )
3024 {
3025 wxString msg = wxString::Format(
3026 _( "Symbol %s contains invalid character '%c'" ), name,
3027 name[bad_pos] );
3028
3029 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
3030 }
3031
3032 THROW_PARSE_ERROR( _( "Invalid symbol library ID" ), CurSource(), CurLine(),
3033 CurLineNumber(), CurOffset() );
3034 }
3035
3036 symbol->SetLibId( libId );
3037 NeedRIGHT();
3038 break;
3039 }
3040
3041 case T_at:
3042 symbol->SetPosition( parseXY() );
3043
3044 switch( static_cast<int>( parseDouble( "symbol orientation" ) ) )
3045 {
3046 case 0: transform = TRANSFORM(); break;
3047 case 90: transform = TRANSFORM( 0, 1, -1, 0 ); break;
3048 case 180: transform = TRANSFORM( -1, 0, 0, -1 ); break;
3049 case 270: transform = TRANSFORM( 0, -1, 1, 0 ); break;
3050 default: Expecting( "0, 90, 180, or 270" );
3051 }
3052
3053 symbol->SetTransform( transform );
3054 NeedRIGHT();
3055 break;
3056
3057 case T_mirror:
3058 token = NextTok();
3059
3060 if( token == T_x )
3061 symbol->SetOrientation( SYM_MIRROR_X );
3062 else if( token == T_y )
3063 symbol->SetOrientation( SYM_MIRROR_Y );
3064 else
3065 Expecting( "x or y" );
3066
3067 NeedRIGHT();
3068 break;
3069
3070 case T_unit:
3071 symbol->SetUnit( parseInt( "symbol unit" ) );
3072 NeedRIGHT();
3073 break;
3074
3075 case T_convert:
3076 // Use SetBodyStyleUnconditional() because the full symbol properties
3077 // (including the corresponding LIB_SYMBOL) are not known
3078 symbol->SetBodyStyleUnconditional( parseInt( "symbol body style" ) );
3079 NeedRIGHT();
3080 break;
3081
3082 case T_exclude_from_sim:
3083 symbol->SetExcludedFromSim( parseBool() );
3084 NeedRIGHT();
3085 break;
3086
3087 case T_in_bom:
3088 symbol->SetExcludedFromBOM( !parseBool() );
3089 NeedRIGHT();
3090 break;
3091
3092 case T_on_board:
3093 symbol->SetExcludedFromBoard( !parseBool() );
3094 NeedRIGHT();
3095 break;
3096
3097 case T_dnp:
3098 symbol->SetDNP( parseBool() );
3099 NeedRIGHT();
3100 break;
3101
3102 case T_fields_autoplaced:
3103 if( parseMaybeAbsentBool( true ) )
3104 symbol->SetFieldsAutoplaced( AUTOPLACE_AUTO );
3105
3106 break;
3107
3108 case T_uuid:
3109 NeedSYMBOL();
3110 const_cast<KIID&>( symbol->m_Uuid ) = parseKIID();
3111 NeedRIGHT();
3112 break;
3113
3114 case T_default_instance:
3115 {
3116 SCH_SYMBOL_INSTANCE defaultInstance;
3117
3118 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3119 {
3120 if( token != T_LEFT )
3121 Expecting( T_LEFT );
3122
3123 token = NextTok();
3124
3125 switch( token )
3126 {
3127 case T_reference:
3128 NeedSYMBOL();
3129 defaultInstance.m_Reference = FromUTF8();
3130 NeedRIGHT();
3131 break;
3132
3133 case T_unit:
3134 defaultInstance.m_Unit = parseInt( "symbol unit" );
3135 NeedRIGHT();
3136 break;
3137
3138 case T_value:
3139 NeedSYMBOL();
3140 symbol->SetValueFieldText( FromUTF8() );
3141 NeedRIGHT();
3142 break;
3143
3144 case T_footprint:
3145 NeedSYMBOL();
3146 symbol->SetFootprintFieldText( FromUTF8() );
3147 NeedRIGHT();
3148 break;
3149
3150 default:
3151 Expecting( "reference, unit, value or footprint" );
3152 }
3153 }
3154
3155 break;
3156 }
3157
3158 case T_instances:
3159 {
3160 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3161 {
3162 if( token != T_LEFT )
3163 Expecting( T_LEFT );
3164
3165 token = NextTok();
3166
3167 if( token != T_project )
3168 Expecting( "project" );
3169
3170 NeedSYMBOL();
3171
3172 wxString projectName = FromUTF8();
3173
3174 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3175 {
3176 if( token != T_LEFT )
3177 Expecting( T_LEFT );
3178
3179 token = NextTok();
3180
3181 if( token != T_path )
3182 Expecting( "path" );
3183
3184 SCH_SYMBOL_INSTANCE instance;
3185
3186 instance.m_ProjectName = projectName;
3187
3188 NeedSYMBOL();
3189 instance.m_Path = KIID_PATH( FromUTF8() );
3190
3191 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3192 {
3193 if( token != T_LEFT )
3194 Expecting( T_LEFT );
3195
3196 token = NextTok();
3197
3198 switch( token )
3199 {
3200 case T_reference:
3201 NeedSYMBOL();
3202 instance.m_Reference = FromUTF8();
3203 NeedRIGHT();
3204 break;
3205
3206 case T_unit:
3207 instance.m_Unit = parseInt( "symbol unit" );
3208 NeedRIGHT();
3209 break;
3210
3211 case T_value:
3212 NeedSYMBOL();
3213 symbol->SetValueFieldText( FromUTF8() );
3214 NeedRIGHT();
3215 break;
3216
3217 case T_footprint:
3218 NeedSYMBOL();
3219 symbol->SetFootprintFieldText( FromUTF8() );
3220 NeedRIGHT();
3221 break;
3222
3223 default:
3224 Expecting( "reference, unit, value or footprint" );
3225 }
3226
3227 symbol->AddHierarchicalReference( instance );
3228 }
3229 }
3230 }
3231
3232 break;
3233 }
3234
3235 case T_property:
3236 // The field parent symbol must be set and its orientation must be set before
3237 // the field positions are set.
3238 field = parseSchField( symbol.get() );
3239
3240 // Exclude from simulation used to be managed by a Sim.Enable field set to "0" when
3241 // simulation was disabled.
3243 {
3244 symbol->SetExcludedFromSim( field->GetText() == wxS( "0" ) );
3245 break;
3246 }
3247
3248 // Even longer ago, we had a "Spice_Netlist_Enabled" field
3250 {
3251 symbol->SetExcludedFromSim( field->GetText() == wxS( "N" ) );
3252 break;
3253 }
3254
3255 if( ( field->GetId() >= MANDATORY_FIELD_COUNT ) && m_fieldIDsRead.count( field->GetId() ) )
3256 {
3257 int nextAvailableId = field->GetId() + 1;
3258
3259 while( m_fieldIDsRead.count( nextAvailableId ) )
3260 nextAvailableId += 1;
3261
3262 field->SetId( nextAvailableId );
3263 }
3264
3265 if( symbol->GetFieldById( field->GetId() ) )
3266 *symbol->GetFieldById( field->GetId() ) = *field;
3267 else
3268 symbol->AddField( *field );
3269
3270 if( field->GetId() == REFERENCE_FIELD )
3271 symbol->UpdatePrefix();
3272
3273 m_fieldIDsRead.insert( field->GetId() );
3274
3275 delete field;
3276 break;
3277
3278 case T_pin:
3279 {
3280 // Read an alternate pin designation
3281 wxString number;
3282 KIID uuid;
3283 wxString alt;
3284
3285 NeedSYMBOL();
3286 number = FromUTF8();
3287
3288 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3289 {
3290 if( token != T_LEFT )
3291 Expecting( T_LEFT );
3292
3293 token = NextTok();
3294
3295 switch( token )
3296 {
3297 case T_alternate:
3298 NeedSYMBOL();
3299 alt = FromUTF8();
3300 NeedRIGHT();
3301 break;
3302
3303 case T_uuid:
3304 NeedSYMBOL();
3305
3306 // First version to write out pin uuids accidentally wrote out the symbol's
3307 // uuid for each pin, so ignore uuids coming from that version.
3308 if( m_requiredVersion >= 20210126 )
3309 uuid = parseKIID();
3310
3311 NeedRIGHT();
3312 break;
3313
3314 default:
3315 Expecting( "alternate or uuid" );
3316 }
3317 }
3318
3319 symbol->GetRawPins().emplace_back( std::make_unique<SCH_PIN>( symbol.get(), number,
3320 alt, uuid ) );
3321 break;
3322 }
3323
3324 default:
3325 Expecting( "lib_id, lib_name, at, mirror, uuid, on_board, in_bom, dnp, "
3326 "default_instance, property, pin, or instances" );
3327 }
3328 }
3329
3330 if( !libName.IsEmpty() && ( symbol->GetLibId().Format().wx_str() != libName ) )
3331 symbol->SetSchSymbolLibraryName( libName );
3332
3333 // Ensure edit/status flags are cleared after these initializations:
3334 symbol->ClearFlags();
3335
3336 return symbol.release();
3337}
3338
3339
3341{
3342 wxCHECK_MSG( CurTok() == T_image, nullptr,
3343 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an image." ) );
3344
3345 T token;
3346 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>();
3347 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
3348
3349 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3350 {
3351 if( token != T_LEFT )
3352 Expecting( T_LEFT );
3353
3354 token = NextTok();
3355
3356 switch( token )
3357 {
3358 case T_at:
3359 bitmap->SetPosition( parseXY() );
3360 NeedRIGHT();
3361 break;
3362
3363 case T_scale:
3364 {
3365 const double scale = parseDouble( "image scale factor" );
3366 refImage.SetImageScale( std::isnormal( scale ) ? scale : 1.0 );
3367
3368 NeedRIGHT();
3369 break;
3370 }
3371 case T_uuid:
3372 NeedSYMBOL();
3373 const_cast<KIID&>( bitmap->m_Uuid ) = parseKIID();
3374 NeedRIGHT();
3375 break;
3376
3377 case T_data:
3378 {
3379 token = NextTok();
3380
3381 wxString data;
3382
3383 // Reserve 128K because most image files are going to be larger than the default
3384 // 1K that wxString reserves.
3385 data.reserve( 1 << 17 );
3386
3387 while( token != T_RIGHT )
3388 {
3389 if( !IsSymbol( token ) )
3390 Expecting( "base64 image data" );
3391
3392 data += FromUTF8();
3393 token = NextTok();
3394 }
3395
3396 wxMemoryBuffer buffer = wxBase64Decode( data );
3397
3398 if( !refImage.ReadImageFile( buffer ) )
3399 THROW_IO_ERROR( _( "Failed to read image data." ) );
3400
3401 break;
3402 }
3403
3404 default:
3405 Expecting( "at, scale, uuid or data" );
3406 }
3407 }
3408
3409 // The image will be scaled by PPI in ReadImageFile.
3410
3411 // 20230121 or older file format versions assumed 300 image PPI at load/save.
3412 // Let's keep compatibility by changing image scale.
3413 if( m_requiredVersion <= 20230121 )
3414 {
3415 refImage.SetImageScale( refImage.GetImageScale() * refImage.GetImage().GetPPI() / 300.0 );
3416 }
3417
3418 return bitmap.release();
3419}
3420
3421
3423{
3424 wxCHECK_MSG( CurTok() == T_sheet, nullptr,
3425 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a sheet." ) );
3426
3427 T token;
3428 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3429 FILL_PARAMS fill;
3430 SCH_FIELD* field;
3431 std::vector<SCH_FIELD> fields;
3432 std::unique_ptr<SCH_SHEET> sheet = std::make_unique<SCH_SHEET>();
3433 std::set<int> fieldIDsRead;
3434
3435 // We'll reset this if we find a fields_autoplaced token
3436 sheet->SetFieldsAutoplaced( AUTOPLACE_NONE );
3437
3438 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3439 {
3440 if( token != T_LEFT )
3441 Expecting( T_LEFT );
3442
3443 token = NextTok();
3444
3445 switch( token )
3446 {
3447 case T_at:
3448 sheet->SetPosition( parseXY() );
3449 NeedRIGHT();
3450 break;
3451
3452 case T_size:
3453 {
3454 VECTOR2I size;
3455 size.x = parseInternalUnits( "sheet width" );
3456 size.y = parseInternalUnits( "sheet height" );
3457 sheet->SetSize( size );
3458 NeedRIGHT();
3459 break;
3460 }
3461
3462 case T_exclude_from_sim:
3463 sheet->SetExcludedFromSim( parseBool() );
3464 NeedRIGHT();
3465 break;
3466
3467 case T_in_bom:
3468 sheet->SetExcludedFromBOM( !parseBool() );
3469 NeedRIGHT();
3470 break;
3471
3472 case T_on_board:
3473 sheet->SetExcludedFromBoard( !parseBool() );
3474 NeedRIGHT();
3475 break;
3476
3477 case T_dnp:
3478 sheet->SetDNP( parseBool() );
3479 NeedRIGHT();
3480 break;
3481
3482 case T_fields_autoplaced:
3483 if( parseMaybeAbsentBool( true ) )
3484 sheet->SetFieldsAutoplaced( AUTOPLACE_AUTO );
3485
3486 break;
3487
3488 case T_stroke:
3489 parseStroke( stroke );
3490 sheet->SetBorderWidth( stroke.GetWidth() );
3491 sheet->SetBorderColor( stroke.GetColor() );
3492 break;
3493
3494 case T_fill:
3495 parseFill( fill );
3496 sheet->SetBackgroundColor( fill.m_Color );
3497 break;
3498
3499 case T_uuid:
3500 NeedSYMBOL();
3501 const_cast<KIID&>( sheet->m_Uuid ) = parseKIID();
3502 NeedRIGHT();
3503 break;
3504
3505 case T_property:
3506 field = parseSchField( sheet.get() );
3507
3508 if( m_requiredVersion <= 20200310 )
3509 {
3510 // Earlier versions had the wrong ids (and names) saved for sheet fields.
3511 // Fortunately they only saved the sheetname and sheetfilepath (and always
3512 // in that order), so we can hack in a recovery.
3513 if( fields.empty() )
3514 field->SetId( SHEETNAME );
3515 else
3516 field->SetId( SHEETFILENAME );
3517 }
3518
3519 // It would appear the problem persists past 20200310, but this time with the
3520 // earlier ids being re-used for later (user) fields. The easiest (and most
3521 // complete) solution is to disallow multiple instances of the same id (for all
3522 // files since the source of the error *might* in fact be hand-edited files).
3523 //
3524 // While no longer used, -1 is still a valid id for user field. We convert it to
3525 // the first available ID after the mandatory fields
3526
3527 if( field->GetId() < 0 )
3529
3530 while( !fieldIDsRead.insert( field->GetId() ).second )
3531 field->SetId( field->GetId() + 1 );
3532
3533 fields.emplace_back( *field );
3534
3535 delete field;
3536 break;
3537
3538 case T_pin:
3539 sheet->AddPin( parseSchSheetPin( sheet.get() ) );
3540 break;
3541
3542 case T_instances:
3543 {
3544 std::vector<SCH_SHEET_INSTANCE> instances;
3545
3546 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3547 {
3548 if( token != T_LEFT )
3549 Expecting( T_LEFT );
3550
3551 token = NextTok();
3552
3553 if( token != T_project )
3554 Expecting( "project" );
3555
3556 NeedSYMBOL();
3557
3558 wxString projectName = FromUTF8();
3559
3560 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3561 {
3562 if( token != T_LEFT )
3563 Expecting( T_LEFT );
3564
3565 token = NextTok();
3566
3567 if( token != T_path )
3568 Expecting( "path" );
3569
3570 SCH_SHEET_INSTANCE instance;
3571
3572 instance.m_ProjectName = projectName;
3573
3574 NeedSYMBOL();
3575 instance.m_Path = KIID_PATH( FromUTF8() );
3576
3577 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3578 {
3579 if( token != T_LEFT )
3580 Expecting( T_LEFT );
3581
3582 token = NextTok();
3583
3584 switch( token )
3585 {
3586 case T_page:
3587 {
3588 NeedSYMBOL();
3589 instance.m_PageNumber = FromUTF8();
3590
3591 // Empty page numbers are not permitted
3592 if( instance.m_PageNumber.IsEmpty() )
3593 {
3594 // Use hash character instead
3595 instance.m_PageNumber = wxT( "#" );
3596 }
3597 else
3598 {
3599 // Whitespaces are not permitted
3600 static std::vector<wxString> whitespaces =
3601 { wxT( "\r" ),
3602 wxT( "\n" ),
3603 wxT( "\t" ),
3604 wxT( " " ) };
3605
3606 for( wxString ch : whitespaces )
3607 instance.m_PageNumber.Replace( ch, wxEmptyString );
3608 }
3609
3610 NeedRIGHT();
3611 break;
3612 }
3613
3614 default:
3615 Expecting( "page" );
3616 }
3617 }
3618
3619 instances.emplace_back( instance );
3620 }
3621 }
3622
3623 sheet->setInstances( instances );
3624 break;
3625 }
3626
3627 default:
3628 Expecting( "at, size, stroke, background, instances, uuid, property, or pin" );
3629 }
3630 }
3631
3632 sheet->SetFields( fields );
3633
3634 return sheet.release();
3635}
3636
3637
3639{
3640 wxCHECK_MSG( CurTok() == T_junction, nullptr,
3641 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a junction." ) );
3642
3643 T token;
3644 std::unique_ptr<SCH_JUNCTION> junction = std::make_unique<SCH_JUNCTION>();
3645
3646 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3647 {
3648 if( token != T_LEFT )
3649 Expecting( T_LEFT );
3650
3651 token = NextTok();
3652
3653 switch( token )
3654 {
3655 case T_at:
3656 junction->SetPosition( parseXY() );
3657 NeedRIGHT();
3658 break;
3659
3660 case T_diameter:
3661 junction->SetDiameter( parseInternalUnits( "junction diameter" ) );
3662 NeedRIGHT();
3663 break;
3664
3665 case T_color:
3666 {
3667 COLOR4D color;
3668
3669 color.r = parseInt( "red" ) / 255.0;
3670 color.g = parseInt( "green" ) / 255.0;
3671 color.b = parseInt( "blue" ) / 255.0;
3672 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
3673
3674 junction->SetColor( color );
3675 NeedRIGHT();
3676 break;
3677 }
3678
3679 case T_uuid:
3680 NeedSYMBOL();
3681 const_cast<KIID&>( junction->m_Uuid ) = parseKIID();
3682 NeedRIGHT();
3683 break;
3684
3685 default:
3686 Expecting( "at, diameter, color or uuid" );
3687 }
3688 }
3689
3690 return junction.release();
3691}
3692
3693
3695{
3696 wxCHECK_MSG( CurTok() == T_no_connect, nullptr,
3697 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a no connect." ) );
3698
3699 T token;
3700 std::unique_ptr<SCH_NO_CONNECT> no_connect = std::make_unique<SCH_NO_CONNECT>();
3701
3702 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3703 {
3704 if( token != T_LEFT )
3705 Expecting( T_LEFT );
3706
3707 token = NextTok();
3708
3709 switch( token )
3710 {
3711 case T_at:
3712 no_connect->SetPosition( parseXY() );
3713 NeedRIGHT();
3714 break;
3715
3716 case T_uuid:
3717 NeedSYMBOL();
3718 const_cast<KIID&>( no_connect->m_Uuid ) = parseKIID();
3719 NeedRIGHT();
3720 break;
3721
3722 default:
3723 Expecting( "at or uuid" );
3724 }
3725 }
3726
3727 return no_connect.release();
3728}
3729
3730
3732{
3733 wxCHECK_MSG( CurTok() == T_bus_entry, nullptr,
3734 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus entry." ) );
3735
3736 T token;
3737 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3738 std::unique_ptr<SCH_BUS_WIRE_ENTRY> busEntry = std::make_unique<SCH_BUS_WIRE_ENTRY>();
3739
3740 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3741 {
3742 if( token != T_LEFT )
3743 Expecting( T_LEFT );
3744
3745 token = NextTok();
3746
3747 switch( token )
3748 {
3749 case T_at:
3750 busEntry->SetPosition( parseXY() );
3751 NeedRIGHT();
3752 break;
3753
3754 case T_size:
3755 {
3756 VECTOR2I size;
3757
3758 size.x = parseInternalUnits( "bus entry height" );
3759 size.y = parseInternalUnits( "bus entry width" );
3760 busEntry->SetSize( size );
3761 NeedRIGHT();
3762 break;
3763 }
3764
3765 case T_stroke:
3766 parseStroke( stroke );
3767 busEntry->SetStroke( stroke );
3768 break;
3769
3770 case T_uuid:
3771 NeedSYMBOL();
3772 const_cast<KIID&>( busEntry->m_Uuid ) = parseKIID();
3773 NeedRIGHT();
3774 break;
3775
3776 default:
3777 Expecting( "at, size, uuid or stroke" );
3778 }
3779 }
3780
3781 return busEntry.release();
3782}
3783
3784
3786{
3787 if( aShape->GetFillMode() == FILL_T::FILLED_SHAPE )
3788 {
3789 aShape->SetFillColor( aShape->GetStroke().GetColor() );
3791 }
3792}
3793
3794
3796{
3797 T token;
3798 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3799 FILL_PARAMS fill;
3800
3801 std::unique_ptr<SCH_SHAPE> polyline = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_NOTES );
3802
3803 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3804 {
3805 if( token != T_LEFT )
3806 Expecting( T_LEFT );
3807
3808 token = NextTok();
3809
3810 switch( token )
3811 {
3812 case T_pts:
3813 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3814 {
3815 if( token != T_LEFT )
3816 Expecting( T_LEFT );
3817
3818 token = NextTok();
3819
3820 if( token != T_xy )
3821 Expecting( "xy" );
3822
3823 polyline->AddPoint( parseXY() );
3824
3825 NeedRIGHT();
3826 }
3827 break;
3828
3829 case T_stroke:
3830 parseStroke( stroke );
3831
3832 // In 6.0, the default schematic line style was Dashed.
3833 if( m_requiredVersion <= 20211123 && stroke.GetLineStyle() == LINE_STYLE::DEFAULT )
3834 stroke.SetLineStyle( LINE_STYLE::DASH );
3835
3836 polyline->SetStroke( stroke );
3837 break;
3838
3839 case T_fill:
3840 parseFill( fill );
3841 polyline->SetFillMode( fill.m_FillType );
3842 polyline->SetFillColor( fill.m_Color );
3843 fixupSchFillMode( polyline.get() );
3844 break;
3845
3846 case T_uuid:
3847 NeedSYMBOL();
3848 const_cast<KIID&>( polyline->m_Uuid ) = parseKIID();
3849 NeedRIGHT();
3850 break;
3851
3852 default:
3853 Expecting( "pts, uuid, stroke, or fill" );
3854 }
3855 }
3856
3857 return polyline.release();
3858}
3859
3860
3862{
3863 // Note: T_polyline is deprecated in this code: it is now handled by
3864 // parseSchPolyLine() that can handle true polygons, and not only one segment.
3865
3866 T token;
3867 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3868 int layer;
3869
3870 switch( CurTok() )
3871 {
3872 case T_polyline: layer = LAYER_NOTES; break;
3873 case T_wire: layer = LAYER_WIRE; break;
3874 case T_bus: layer = LAYER_BUS; break;
3875 default:
3876 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as a line." );
3877 }
3878
3879 std::unique_ptr<SCH_LINE> line = std::make_unique<SCH_LINE>( VECTOR2I(), layer );
3880
3881 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3882 {
3883 if( token != T_LEFT )
3884 Expecting( T_LEFT );
3885
3886 token = NextTok();
3887
3888 switch( token )
3889 {
3890 case T_pts:
3891 NeedLEFT();
3892 token = NextTok();
3893
3894 if( token != T_xy )
3895 Expecting( "xy" );
3896
3897 line->SetStartPoint( parseXY() );
3898 NeedRIGHT();
3899 NeedLEFT();
3900 token = NextTok();
3901
3902 if( token != T_xy )
3903 Expecting( "xy" );
3904
3905 line->SetEndPoint( parseXY() );
3906 NeedRIGHT();
3907 NeedRIGHT();
3908 break;
3909
3910 case T_stroke:
3911 parseStroke( stroke );
3912 line->SetStroke( stroke );
3913 break;
3914
3915 case T_uuid:
3916 NeedSYMBOL();
3917 const_cast<KIID&>( line->m_Uuid ) = parseKIID();
3918 NeedRIGHT();
3919 break;
3920
3921 default:
3922 Expecting( "at, uuid or stroke" );
3923 }
3924 }
3925
3926 return line.release();
3927}
3928
3929
3931{
3932 wxCHECK_MSG( CurTok() == T_arc, nullptr,
3933 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc." ) );
3934
3935 T token;
3936 VECTOR2I startPoint;
3937 VECTOR2I midPoint;
3938 VECTOR2I endPoint;
3939 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3940 FILL_PARAMS fill;
3941 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC );
3942
3943 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3944 {
3945 if( token != T_LEFT )
3946 Expecting( T_LEFT );
3947
3948 token = NextTok();
3949
3950 switch( token )
3951 {
3952 case T_start:
3953 startPoint = parseXY();
3954 NeedRIGHT();
3955 break;
3956
3957 case T_mid:
3958 midPoint = parseXY();
3959 NeedRIGHT();
3960 break;
3961
3962 case T_end:
3963 endPoint = parseXY();
3964 NeedRIGHT();
3965 break;
3966
3967 case T_stroke:
3968 parseStroke( stroke );
3969 arc->SetStroke( stroke );
3970 break;
3971
3972 case T_fill:
3973 parseFill( fill );
3974 arc->SetFillMode( fill.m_FillType );
3975 arc->SetFillColor( fill.m_Color );
3976 fixupSchFillMode( arc.get() );
3977 break;
3978
3979 case T_uuid:
3980 NeedSYMBOL();
3981 const_cast<KIID&>( arc->m_Uuid ) = KIID( FromUTF8() );
3982 NeedRIGHT();
3983 break;
3984
3985 default:
3986 Expecting( "start, mid, end, stroke, fill or uuid" );
3987 }
3988 }
3989
3990 arc->SetArcGeometry( startPoint, midPoint, endPoint );
3991
3992 return arc.release();
3993}
3994
3995
3997{
3998 wxCHECK_MSG( CurTok() == T_circle, nullptr,
3999 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle." ) );
4000
4001 T token;
4003 int radius = 0;
4004 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4005 FILL_PARAMS fill;
4006 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE );
4007
4008 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4009 {
4010 if( token != T_LEFT )
4011 Expecting( T_LEFT );
4012
4013 token = NextTok();
4014
4015 switch( token )
4016 {
4017 case T_center:
4018 center = parseXY();
4019 NeedRIGHT();
4020 break;
4021
4022 case T_radius:
4023 radius = parseInternalUnits( "radius length" );
4024 NeedRIGHT();
4025 break;
4026
4027 case T_stroke:
4028 parseStroke( stroke );
4029 circle->SetStroke( stroke );
4030 break;
4031
4032 case T_fill:
4033 parseFill( fill );
4034 circle->SetFillMode( fill.m_FillType );
4035 circle->SetFillColor( fill.m_Color );
4036 fixupSchFillMode( circle.get() );
4037 break;
4038
4039 case T_uuid:
4040 NeedSYMBOL();
4041 const_cast<KIID&>( circle->m_Uuid ) = KIID( FromUTF8() );
4042 NeedRIGHT();
4043 break;
4044
4045 default:
4046 Expecting( "center, radius, stroke, fill or uuid" );
4047 }
4048 }
4049
4050 circle->SetCenter( center );
4051 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
4052
4053 return circle.release();
4054}
4055
4056
4058{
4059 wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
4060 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle." ) );
4061
4062 T token;
4063 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4064 FILL_PARAMS fill;
4065 std::unique_ptr<SCH_SHAPE> rectangle = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE );
4066
4067 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4068 {
4069 if( token != T_LEFT )
4070 Expecting( T_LEFT );
4071
4072 token = NextTok();
4073
4074 switch( token )
4075 {
4076 case T_start:
4077 rectangle->SetPosition( parseXY() );
4078 NeedRIGHT();
4079 break;
4080
4081 case T_end:
4082 rectangle->SetEnd( parseXY() );
4083 NeedRIGHT();
4084 break;
4085
4086 case T_stroke:
4087 parseStroke( stroke );
4088 rectangle->SetStroke( stroke );
4089 break;
4090
4091 case T_fill:
4092 parseFill( fill );
4093 rectangle->SetFillMode( fill.m_FillType );
4094 rectangle->SetFillColor( fill.m_Color );
4095 fixupSchFillMode( rectangle.get() );
4096 break;
4097
4098 case T_uuid:
4099 NeedSYMBOL();
4100 const_cast<KIID&>( rectangle->m_Uuid ) = KIID( FromUTF8() );
4101 NeedRIGHT();
4102 break;
4103
4104 default:
4105 Expecting( "start, end, stroke, fill or uuid" );
4106 }
4107 }
4108
4109 return rectangle.release();
4110}
4111
4112
4114{
4115 wxCHECK_MSG( CurTok() == T_rule_area, nullptr,
4116 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rule area." ) );
4117
4118 T token;
4119 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4120 FILL_PARAMS fill;
4121 std::unique_ptr<SCH_RULE_AREA> ruleArea = std::make_unique<SCH_RULE_AREA>();
4122
4123 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4124 {
4125 if( token != T_LEFT )
4126 Expecting( T_LEFT );
4127
4128 token = NextTok();
4129
4130 switch( token )
4131 {
4132 case T_polyline:
4133 {
4134 std::unique_ptr<SCH_SHAPE> poly( parseSchPolyLine() );
4135 SHAPE_POLY_SET& sch_rule_poly = poly->GetPolyShape();
4136
4137 // The polygon must be closed, it is a schematic closed polyline:
4138 sch_rule_poly.Outline(0).SetClosed( true );
4139
4140 ruleArea->SetPolyShape( sch_rule_poly );
4141
4142 ruleArea->SetStroke( poly->GetStroke() );
4143 ruleArea->SetFillMode( poly->GetFillMode() );
4144 ruleArea->SetFillColor( poly->GetFillColor() );
4145 break;
4146 }
4147 default:
4148 Expecting( "polyline" );
4149 }
4150 }
4151
4152 return ruleArea.release();
4153}
4154
4155
4157{
4158 wxCHECK_MSG( CurTok() == T_bezier, nullptr,
4159 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
4160
4161 T token;
4162 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4163 FILL_PARAMS fill;
4164 std::unique_ptr<SCH_SHAPE> bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER );
4165
4166 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4167 {
4168 if( token != T_LEFT )
4169 Expecting( T_LEFT );
4170
4171 token = NextTok();
4172
4173 switch( token )
4174 {
4175 case T_pts:
4176 {
4177 int ii = 0;
4178
4179 for( token = NextTok(); token != T_RIGHT; token = NextTok(), ++ii )
4180 {
4181 if( token != T_LEFT )
4182 Expecting( T_LEFT );
4183
4184 token = NextTok();
4185
4186 if( token != T_xy )
4187 Expecting( "xy" );
4188
4189 switch( ii )
4190 {
4191 case 0: bezier->SetStart( parseXY() ); break;
4192 case 1: bezier->SetBezierC1( parseXY() ); break;
4193 case 2: bezier->SetBezierC2( parseXY() ); break;
4194 case 3: bezier->SetEnd( parseXY() ); break;
4195 default: Unexpected( "control point" ); break;
4196 }
4197
4198 NeedRIGHT();
4199 }
4200 }
4201 break;
4202
4203 case T_stroke:
4204 parseStroke( stroke );
4205 bezier->SetStroke( stroke );
4206 break;
4207
4208 case T_fill:
4209 parseFill( fill );
4210 bezier->SetFillMode( fill.m_FillType );
4211 bezier->SetFillColor( fill.m_Color );
4212 fixupSchFillMode( bezier.get() );
4213 break;
4214
4215 case T_uuid:
4216 NeedSYMBOL();
4217 const_cast<KIID&>( bezier->m_Uuid ) = KIID( FromUTF8() );
4218 NeedRIGHT();
4219 break;
4220
4221 default:
4222 Expecting( "pts, stroke, fill or uuid" );
4223 }
4224 }
4225
4226 bezier->RebuildBezierToSegmentsPointsList( bezier->GetPenWidth() / 2 );
4227
4228 return bezier.release();
4229}
4230
4231
4233{
4234 T token;
4235 std::unique_ptr<SCH_TEXT> text;
4236
4237 switch( CurTok() )
4238 {
4239 case T_text: text = std::make_unique<SCH_TEXT>(); break;
4240 case T_label: text = std::make_unique<SCH_LABEL>(); break;
4241 case T_global_label: text = std::make_unique<SCH_GLOBALLABEL>(); break;
4242 case T_hierarchical_label: text = std::make_unique<SCH_HIERLABEL>(); break;
4243 case T_netclass_flag: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
4244 case T_directive_label: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
4245 default:
4246 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as text." );
4247 }
4248
4249 // We'll reset this if we find a fields_autoplaced token
4250 text->SetFieldsAutoplaced( AUTOPLACE_NONE );
4251
4252 NeedSYMBOL();
4253
4254 text->SetText( FromUTF8() );
4255
4256 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4257 {
4258 if( token != T_LEFT )
4259 Expecting( T_LEFT );
4260
4261 token = NextTok();
4262
4263 switch( token )
4264 {
4265 case T_exclude_from_sim:
4266 text->SetExcludedFromSim( parseBool() );
4267 NeedRIGHT();
4268 break;
4269
4270 case T_at:
4271 text->SetPosition( parseXY() );
4272 text->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ).KeepUpright() );
4273
4274 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() ) )
4275 {
4276 switch( static_cast<int>( label->GetTextAngle().AsDegrees() ) )
4277 {
4278 case 0: label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
4279 case 90: label->SetSpinStyle( SPIN_STYLE::UP ); break;
4280 case 180: label->SetSpinStyle( SPIN_STYLE::LEFT ); break;
4281 case 270: label->SetSpinStyle( SPIN_STYLE::BOTTOM ); break;
4282 default: wxFAIL; label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
4283 }
4284 }
4285
4286 NeedRIGHT();
4287 break;
4288
4289 case T_shape:
4290 {
4291 if( text->Type() == SCH_TEXT_T || text->Type() == SCH_LABEL_T )
4292 Unexpected( T_shape );
4293
4294 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( text.get() );
4295
4296 token = NextTok();
4297
4298 switch( token )
4299 {
4300 case T_input: label->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
4301 case T_output: label->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
4302 case T_bidirectional: label->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
4303 case T_tri_state: label->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE ); break;
4304 case T_passive: label->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
4305 case T_dot: label->SetShape( LABEL_FLAG_SHAPE::F_DOT ); break;
4306 case T_round: label->SetShape( LABEL_FLAG_SHAPE::F_ROUND ); break;
4307 case T_diamond: label->SetShape( LABEL_FLAG_SHAPE::F_DIAMOND ); break;
4308 case T_rectangle: label->SetShape( LABEL_FLAG_SHAPE::F_RECTANGLE ); break;
4309 default:
4310 Expecting( "input, output, bidirectional, tri_state, passive, dot, round, diamond"
4311 "or rectangle" );
4312 }
4313
4314 NeedRIGHT();
4315 break;
4316 }
4317
4318 case T_length:
4319 {
4320 if( text->Type() != SCH_DIRECTIVE_LABEL_T )
4321 Unexpected( T_length );
4322
4323 SCH_DIRECTIVE_LABEL* label = static_cast<SCH_DIRECTIVE_LABEL*>( text.get() );
4324
4325 label->SetPinLength( parseInternalUnits( "pin length" ) );
4326 NeedRIGHT();
4327 }
4328 break;
4329
4330 case T_fields_autoplaced:
4331 if( parseMaybeAbsentBool( true ) )
4332 text->SetFieldsAutoplaced( AUTOPLACE_AUTO );
4333
4334 break;
4335
4336 case T_effects:
4337 parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ), true );
4338 break;
4339
4340 case T_iref: // legacy format; current is a T_property (aka SCH_FIELD)
4341 if( text->Type() == SCH_GLOBAL_LABEL_T )
4342 {
4343 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
4344 SCH_FIELD* field = &label->GetFields()[0];
4345
4346 field->SetTextPos( parseXY() );
4347 NeedRIGHT();
4348
4349 field->SetVisible( true );
4350 }
4351 break;
4352
4353 case T_uuid:
4354 NeedSYMBOL();
4355 const_cast<KIID&>( text->m_Uuid ) = parseKIID();
4356 NeedRIGHT();
4357 break;
4358
4359 case T_property:
4360 {
4361 if( text->Type() == SCH_TEXT_T )
4362 Unexpected( T_property );
4363
4364 SCH_FIELD* field = parseSchField( text.get() );
4365
4366 // If the field is a Intersheetrefs it is not handled like other fields:
4367 // It always exists and is the first in list
4368 if( text->Type() == SCH_GLOBAL_LABEL_T
4369 && ( field->GetInternalName() == wxT( "Intersheet References" ) // old name in V6.0
4370 || field->GetInternalName() == wxT( "Intersheetrefs" ) ) ) // Current name
4371 {
4372 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
4373 // Ensure the Id of this special and first field is 0, needed by
4374 // SCH_FIELD::IsHypertext() test
4375 field->SetId( 0 );
4376
4377 label->GetFields()[0] = *field;
4378 }
4379 else
4380 {
4381 static_cast<SCH_LABEL_BASE*>( text.get() )->GetFields().emplace_back( *field );
4382 }
4383
4384 delete field;
4385 break;
4386 }
4387
4388 default:
4389 Expecting( "at, shape, iref, uuid or effects" );
4390 }
4391 }
4392
4393 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() );
4394
4395 if( label && label->GetFields().empty() )
4397
4398 return text.release();
4399}
4400
4401
4403{
4404 wxCHECK_MSG( CurTok() == T_text_box, nullptr,
4405 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text box." ) );
4406
4407 std::unique_ptr<SCH_TEXTBOX> textBox = std::make_unique<SCH_TEXTBOX>();
4408
4409 parseSchTextBoxContent( textBox.get() );
4410
4411 return textBox.release();
4412}
4413
4414
4416{
4417 wxCHECK_MSG( CurTok() == T_table_cell, nullptr,
4418 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table cell." ) );
4419
4420 std::unique_ptr<SCH_TABLECELL> cell = std::make_unique<SCH_TABLECELL>();
4421
4422 parseSchTextBoxContent( cell.get() );
4423
4424 return cell.release();
4425}
4426
4427
4429{
4430 T token;
4431 VECTOR2I pos;
4432 VECTOR2I end;
4433 VECTOR2I size;
4434 int left;
4435 int top;
4436 int right;
4437 int bottom;
4438 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4439 FILL_PARAMS fill;
4440 bool foundEnd = false;
4441 bool foundSize = false;
4442 bool foundMargins = false;
4443
4444 NeedSYMBOL();
4445
4446 aTextBox->SetText( FromUTF8() );
4447
4448 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4449 {
4450 if( token != T_LEFT )
4451 Expecting( T_LEFT );
4452
4453 token = NextTok();
4454
4455 switch( token )
4456 {
4457 case T_exclude_from_sim:
4458 aTextBox->SetExcludedFromSim( parseBool() );
4459 NeedRIGHT();
4460 break;
4461
4462 case T_start: // Legacy token during 6.99 development; fails to handle angle
4463 pos = parseXY();
4464 NeedRIGHT();
4465 break;
4466
4467 case T_end: // Legacy token during 6.99 development; fails to handle angle
4468 end = parseXY();
4469 foundEnd = true;
4470 NeedRIGHT();
4471 break;
4472
4473 case T_at:
4474 pos = parseXY();
4475 aTextBox->SetTextAngle( EDA_ANGLE( parseDouble( "textbox angle" ), DEGREES_T ) );
4476 NeedRIGHT();
4477 break;
4478
4479 case T_size:
4480 size = parseXY();
4481 foundSize = true;
4482 NeedRIGHT();
4483 break;
4484
4485 case T_span:
4486 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( aTextBox ) )
4487 {
4488 cell->SetColSpan( parseInt( "column span" ) );
4489 cell->SetRowSpan( parseInt( "row span" ) );
4490 }
4491 else
4492 {
4493 Expecting( "at, size, stroke, fill, effects or uuid" );
4494 }
4495
4496 NeedRIGHT();
4497 break;
4498
4499 case T_stroke:
4500 parseStroke( stroke );
4501 aTextBox->SetStroke( stroke );
4502 break;
4503
4504 case T_fill:
4505 parseFill( fill );
4506 aTextBox->SetFillMode( fill.m_FillType );
4507 aTextBox->SetFillColor( fill.m_Color );
4508 fixupSchFillMode( aTextBox );
4509 break;
4510
4511 case T_margins:
4512 parseMargins( left, top, right, bottom );
4513 aTextBox->SetMarginLeft( left );
4514 aTextBox->SetMarginTop( top );
4515 aTextBox->SetMarginRight( right );
4516 aTextBox->SetMarginBottom( bottom );
4517 foundMargins = true;
4518 NeedRIGHT();
4519 break;
4520
4521 case T_effects:
4522 parseEDA_TEXT( static_cast<EDA_TEXT*>( aTextBox ), false );
4523 break;
4524
4525 case T_uuid:
4526 NeedSYMBOL();
4527 const_cast<KIID&>( aTextBox->m_Uuid ) = KIID( FromUTF8() );
4528 NeedRIGHT();
4529 break;
4530
4531 default:
4532 if( dynamic_cast<SCH_TABLECELL*>( aTextBox ) != nullptr )
4533 Expecting( "at, size, stroke, fill, effects, span or uuid" );
4534 else
4535 Expecting( "at, size, stroke, fill, effects or uuid" );
4536 }
4537 }
4538
4539 aTextBox->SetPosition( pos );
4540
4541 if( foundEnd )
4542 aTextBox->SetEnd( end );
4543 else if( foundSize )
4544 aTextBox->SetEnd( pos + size );
4545 else
4546 Expecting( "size" );
4547
4548 if( !foundMargins )
4549 {
4550 int margin = aTextBox->GetLegacyTextMargin();
4551 aTextBox->SetMarginLeft( margin );
4552 aTextBox->SetMarginTop( margin );
4553 aTextBox->SetMarginRight( margin );
4554 aTextBox->SetMarginBottom( margin );
4555 }
4556}
4557
4558
4560{
4561 wxCHECK_MSG( CurTok() == T_table, nullptr,
4562 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table." ) );
4563
4564 T token;
4565 int defaultLineWidth = schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
4566 STROKE_PARAMS borderStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
4567 STROKE_PARAMS separatorsStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
4568 std::unique_ptr<SCH_TABLE> table = std::make_unique<SCH_TABLE>( defaultLineWidth );
4569
4570 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4571 {
4572 if( token != T_LEFT )
4573 Expecting( T_LEFT );
4574
4575 token = NextTok();
4576
4577 switch( token )
4578 {
4579 case T_column_count:
4580 table->SetColCount( parseInt( "column count" ) );
4581 NeedRIGHT();
4582 break;
4583
4584 case T_column_widths:
4585 {
4586 int col = 0;
4587
4588 while( ( token = NextTok() ) != T_RIGHT )
4589 table->SetColWidth( col++, parseInternalUnits() );
4590
4591 break;
4592 }
4593
4594 case T_row_heights:
4595 {
4596 int row = 0;
4597
4598 while( ( token = NextTok() ) != T_RIGHT )
4599 table->SetRowHeight( row++, parseInternalUnits() );
4600
4601 break;
4602 }
4603
4604 case T_cells:
4605 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4606 {
4607 if( token != T_LEFT )
4608 Expecting( T_LEFT );
4609
4610 token = NextTok();
4611
4612 if( token != T_table_cell )
4613 Expecting( "table_cell" );
4614
4615 table->AddCell( parseSchTableCell() );
4616 }
4617
4618 break;
4619
4620 case T_border:
4621 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4622 {
4623 if( token != T_LEFT )
4624 Expecting( T_LEFT );
4625
4626 token = NextTok();
4627
4628 switch( token )
4629 {
4630 case T_external:
4631 table->SetStrokeExternal( parseBool() );
4632 NeedRIGHT();
4633 break;
4634
4635 case T_header:
4636 table->SetStrokeHeader( parseBool() );
4637 NeedRIGHT();
4638 break;
4639
4640 case T_stroke:
4641 parseStroke( borderStroke );
4642 table->SetBorderStroke( borderStroke );
4643 break;
4644
4645 default:
4646 Expecting( "external, header or stroke" );
4647 break;
4648 }
4649 }
4650
4651 break;
4652
4653 case T_separators:
4654 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4655 {
4656 if( token != T_LEFT )
4657 Expecting( T_LEFT );
4658
4659 token = NextTok();
4660
4661 switch( token )
4662 {
4663 case T_rows:
4664 table->SetStrokeRows( parseBool() );
4665 NeedRIGHT();
4666 break;
4667
4668 case T_cols:
4669 table->SetStrokeColumns( parseBool() );
4670 NeedRIGHT();
4671 break;
4672
4673 case T_stroke:
4674 parseStroke( separatorsStroke );
4675 table->SetSeparatorsStroke( separatorsStroke );
4676 break;
4677
4678 default:
4679 Expecting( "rows, cols, or stroke" );
4680 break;
4681 }
4682 }
4683
4684 break;
4685
4686 default:
4687 Expecting( "columns, col_widths, row_heights, border, separators, header or cells" );
4688 }
4689 }
4690
4691 return table.release();
4692}
4693
4694
4696{
4697 wxCHECK_RET( CurTok() == T_bus_alias,
4698 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus alias." ) );
4699 wxCHECK( aScreen, /* void */ );
4700
4701 T token;
4702 std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>( aScreen );
4703 wxString alias;
4704 wxString member;
4705
4706 NeedSYMBOL();
4707
4708 alias = FromUTF8();
4709
4710 if( m_requiredVersion < 20210621 )
4711 alias = ConvertToNewOverbarNotation( alias );
4712
4713 busAlias->SetName( alias );
4714
4715 NeedLEFT();
4716 token = NextTok();
4717
4718 if( token != T_members )
4719 Expecting( "members" );
4720
4721 token = NextTok();
4722
4723 while( token != T_RIGHT )
4724 {
4725 if( !IsSymbol( token ) )
4726 Expecting( "quoted string" );
4727
4728 member = FromUTF8();
4729
4730 if( m_requiredVersion < 20210621 )
4731 member = ConvertToNewOverbarNotation( member );
4732
4733 busAlias->Members().emplace_back( member );
4734
4735 token = NextTok();
4736 }
4737
4738 NeedRIGHT();
4739
4740 aScreen->AddBusAlias( busAlias );
4741}
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:83
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition: schematic.cpp:877
Object to handle a bitmap image that can be inserted in a schematic.
Definition: sch_bitmap.h:40
Class for a wire to bus entry.
void SetPinLength(int aLength)
Definition: sch_label.h:478
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h: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:1258
int GetId() const
Definition: sch_field.h:134
void SetId(int aId)
Definition: sch_field.cpp:164
const wxString & GetInternalName()
Get the initial name of the field set at creation (or set by SetName()).
Definition: sch_field.h:132
bool m_appending
Appending load status.
SCH_FIELD * parseSchField(SCH_ITEM *aParent)
void parseSchSymbolInstances(SCH_SCREEN *aScreen)
void parsePinNumbers(std::unique_ptr< LIB_SYMBOL > &aSymbol)
LIB_SYMBOL * parseLibSymbol(LIB_SYMBOL_MAP &aSymbolLibMap)
void parseFill(FILL_PARAMS &aFill)
void parseBusAlias(SCH_SCREEN *aScreen)
std::set< int > m_fieldIDsRead
Field IDs that have been read so far for the current symbol.
void parseTITLE_BLOCK(TITLE_BLOCK &aTitleBlock)
void parseStroke(STROKE_PARAMS &aStroke)
Parse stroke definition aStroke.
int m_unit
The current unit being parsed.
void parseEDA_TEXT(EDA_TEXT *aText, bool aConvertOverbarSyntax, bool aEnforceMinTextSize=true)
SCH_SHEET * m_rootSheet
The rootsheet for full project loads or null for importing a schematic.
void ParseLib(LIB_SYMBOL_MAP &aSymbolLibMap)
void parseHeader(TSCHEMATIC_T::T aHeaderType, int aFileVersion)
void parseMargins(int &aLeft, int &aTop, int &aRight, int &aBottom)
wxString m_symbolName
The current symbol name.
VECTOR2I parseXY(bool aInvertY=false)
void parsePinNames(std::unique_ptr< LIB_SYMBOL > &aSymbol)
void ParseSchematic(SCH_SHEET *aSheet, bool aIsCopyablyOnly=false, int aFileVersion=SEXPR_SCHEMATIC_FILE_VERSION)
Parse the internal LINE_READER object into aSheet.
SCH_SHEET_PIN * parseSchSheetPin(SCH_SHEET *aSheet)
int m_bodyStyle
The current body style being parsed.
SCH_FIELD * parseProperty(std::unique_ptr< LIB_SYMBOL > &aSymbol)
void parseSchTextBoxContent(SCH_TEXTBOX *aTextBox)
bool parseMaybeAbsentBool(bool aDefaultValue)
Parses a boolean flag inside a list that existed before boolean normalization.
void parsePAGE_INFO(PAGE_INFO &aPageInfo)
int m_requiredVersion
Set to the symbol library file version required.
LIB_SYMBOL * ParseSymbol(LIB_SYMBOL_MAP &aSymbolLibMap, int aFileVersion=SEXPR_SYMBOL_LIB_FILE_VERSION)
Parse internal LINE_READER object into symbols and return all found.
void parseSchSheetInstances(SCH_SHEET *aRootSheet, SCH_SCREEN *aScreen)
SCH_IO_KICAD_SEXPR_PARSER(LINE_READER *aLineReader=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr, unsigned aLineCount=0, SCH_SHEET *aRootSheet=nullptr, bool aIsAppending=false)
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:167
void SetFieldsAutoplaced(AUTOPLACE_ALGO aAlgo)
Definition: sch_item.h:559
void SetShape(LABEL_FLAG_SHAPE aShape)
Definition: sch_label.h:190
std::vector< SCH_FIELD > & GetFields()
Definition: sch_label.h:213
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:41
void SetStartPoint(const VECTOR2I &aPosition)
Definition: sch_line.h:139
virtual void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: sch_line.h:193
void SetEndPoint(const VECTOR2I &aPosition)
Definition: sch_line.h:144
void SetFileFormatVersionAtLoad(int aVersion)
Definition: sch_screen.h:128
std::vector< SCH_SHEET_INSTANCE > m_sheetInstances
Definition: sch_screen.h:694
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition: sch_screen.h:157
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Definition: sch_screen.cpp:155
void AddLibSymbol(LIB_SYMBOL *aLibSymbol)
Add aLibSymbol to the library symbol map.
void AddBusAlias(std::shared_ptr< BUS_ALIAS > aAlias)
Add a bus alias definition (and transfers ownership of the pointer).
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition: sch_screen.h:132
const KIID & GetUuid() const
Definition: sch_screen.h:535
void UpdateLocalLibSymbolLinks()
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in this schematic with the local projec...
Definition: sch_screen.cpp:826
void SetLegacySymbolInstanceData()
Update the symbol value and footprint instance data for legacy designs.
SCHEMATIC * Schematic() const
Definition: sch_screen.cpp:102
void FixupEmbeddedData()
After loading a file from disk, the library symbols do not yet contain the full data for their embedd...
int GetFileFormatVersionAtLoad() const
Definition: sch_screen.h:129
KIID m_uuid
A unique identifier for each schematic file.
Definition: sch_screen.h:702
std::vector< SCH_SYMBOL_INSTANCE > m_symbolInstances
The list of symbol instances loaded from the schematic file.
Definition: sch_screen.h:693
void SetPosition(const VECTOR2I &aPos) override
Definition: sch_shape.h:74
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: sch_shape.cpp:63
STROKE_PARAMS GetStroke() const override
Definition: sch_shape.h:56
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
void SetPageNumber(const wxString &aPageNumber)
Set the sheet instance user definable page number.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet_pin.h:66
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:59
static const wxString GetDefaultFieldName(int aFieldNdx, bool aTranslated)
Definition: sch_sheet.cpp:59
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:113
Schematic symbol object.
Definition: sch_symbol.h:77
void SetMarginBottom(int aBottom)
Definition: sch_textbox.h:61
int GetLegacyTextMargin() const
Definition: sch_textbox.cpp:76
void SetMarginLeft(int aLeft)
Definition: sch_textbox.h:58
void SetMarginRight(int aRight)
Definition: sch_textbox.h:60
void SetMarginTop(int aTop)
Definition: sch_textbox.h:59
void SetExcludedFromSim(bool aExclude) override
Definition: sch_textbox.h: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:94
int GetWidth() const
void SetLineStyle(LINE_STYLE aLineStyle)
LINE_STYLE GetLineStyle() const
KIGFX::COLOR4D GetColor() const
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition: title_block.h:41
void SetRevision(const wxString &aRevision)
Definition: title_block.h:81
void SetComment(int aIdx, const wxString &aComment)
Definition: title_block.h:101
void SetTitle(const wxString &aTitle)
Definition: title_block.h:58
void SetCompany(const wxString &aCompany)
Definition: title_block.h:91
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition: title_block.h:71
for transforming drawing coordinates for a wxDC device context.
Definition: transform.h:46
#define DEFAULT_LINE_WIDTH_MILS
The default wire width in mils. (can be changed in preference menu)
@ DSN_RIGHT
Definition: dsnlexer.h:68
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:401
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
@ TENTHS_OF_A_DEGREE_T
Definition: eda_angle.h:30
@ DEGREES_T
Definition: eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_360
Definition: eda_angle.h:407
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:405
@ FILLED_WITH_COLOR
@ FILLED_SHAPE
Fill with object color.
FONTCONFIG * Fontconfig()
Definition: fontconfig.cpp:104
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:165
KIID & NilUuid()
Definition: kiid.cpp:69
@ LAYER_DEVICE
Definition: layer_ids.h:455
@ LAYER_WIRE
Definition: layer_ids.h:441
@ LAYER_NOTES
Definition: layer_ids.h:456
@ LAYER_BUS
Definition: layer_ids.h:442
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
#define MAX_PAGE_SIZE_EESCHEMA_MM
Definition: page_info.h:45
#define MIN_PAGE_SIZE_MM
Min and max page sizes for clamping, in mm.
Definition: page_info.h:43
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:1073
see class PGM_BASE
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition: pin_type.h:36
GRAPHIC_PINSHAPE
Definition: pin_type.h:59
#define SEXPR_SYMBOL_LIB_FILE_VERSION
This file contains the file format version information for the s-expression schematic and symbol libr...
#define SEXPR_SCHEMATIC_FILE_VERSION
Schematic file version.
double parseDouble(LINE_READER &aReader, const char *aLine, const char **aOutput)
Parses an ASCII point string with possible leading whitespace into a double precision floating point ...
void fixupSchFillMode(SCH_SHAPE *aShape)
@ AUTOPLACE_NONE
Definition: sch_item.h:69
@ AUTOPLACE_AUTO
Definition: sch_item.h:70
@ SHEET_MANDATORY_FIELD_COUNT
The first 2 are mandatory, and must be instantiated in SCH_SHEET.
Definition: sch_sheet.h:49
@ SHEETNAME
Definition: sch_sheet.h:45
@ SHEETFILENAME
Definition: sch_sheet.h:46
#define SHEET_MANDATORY_FIELDS
Definition: sch_sheet.h:52
#define SIM_LEGACY_ENABLE_FIELD
Definition: sim_model.h:64
#define SIM_LEGACY_ENABLE_FIELD_V7
Definition: sim_model.h:60
const int scale
wxString ConvertToNewOverbarNotation(const wxString &aOldStr)
Convert the old ~...~ overbar notation to the new ~{...} one.
wxString UnescapeString(const wxString &aSource)
const double IU_PER_MM
Definition: base_units.h:76
constexpr int MilsToIU(int mils) const
Definition: base_units.h:93
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
Definition: ki_exception.h:176
A filename or source description, a problem input line, a line number, a byte offset,...
Definition: ki_exception.h:120
wxString m_Name
Definition: sch_pin.h:44
GRAPHIC_PINSHAPE m_Shape
Definition: sch_pin.h:45
ELECTRICAL_PINTYPE m_Type
Definition: sch_pin.h:46
A simple container for sheet instance information.
A simple container for schematic symbol instance information.
@ SYM_MIRROR_Y
Definition: symbol.h:44
@ SYM_MIRROR_X
Definition: symbol.h:43
std::map< wxString, LIB_SYMBOL *, LibSymbolMapSort > LIB_SYMBOL_MAP
#define DO_TRANSLATE
#define MANDATORY_FIELDS
wxString GetCanonicalFieldName(int idx)
@ VALUE_FIELD
Field Value of part, i.e. "3.3K".
@ MANDATORY_FIELD_COUNT
The first 5 are mandatory, and must be instantiated in SCH_COMPONENT, LIB_PART, and FOOTPRINT constru...
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".
VECTOR2I center
int radius
VECTOR2I end
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
const VECTOR2I CalcArcCenter(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Determine the center of an arc or circle given three points on its circumference.
Definition: trigo.cpp:521
@ SCH_SYMBOL_T
Definition: typeinfo.h:172
@ SCH_DIRECTIVE_LABEL_T
Definition: typeinfo.h:171
@ SCH_LABEL_T
Definition: typeinfo.h:167
@ SCH_SHEET_T
Definition: typeinfo.h:174
@ SCH_TEXT_T
Definition: typeinfo.h:151
@ SCH_GLOBAL_LABEL_T
Definition: typeinfo.h:168
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695