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