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
3519 // Remove field from m_fontTextMap (it is a temporary field.)
3520 // Copy also font data (if field is in list) to the new field
3521 // We don't know the final field (fields.back() is also temporary)
3522 // so we just copy font info
3523 removeEntryInFontTextMap( field, &fields.back(), false );
3524
3525 delete field;
3526 break;
3527
3528 case T_pin:
3529 sheet->AddPin( parseSchSheetPin( sheet.get() ) );
3530 break;
3531
3532 case T_instances:
3533 {
3534 std::vector<SCH_SHEET_INSTANCE> instances;
3535
3536 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3537 {
3538 if( token != T_LEFT )
3539 Expecting( T_LEFT );
3540
3541 token = NextTok();
3542
3543 if( token != T_project )
3544 Expecting( "project" );
3545
3546 NeedSYMBOL();
3547
3548 wxString projectName = FromUTF8();
3549
3550 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3551 {
3552 if( token != T_LEFT )
3553 Expecting( T_LEFT );
3554
3555 token = NextTok();
3556
3557 if( token != T_path )
3558 Expecting( "path" );
3559
3560 SCH_SHEET_INSTANCE instance;
3561
3562 instance.m_ProjectName = projectName;
3563
3564 NeedSYMBOL();
3565 instance.m_Path = KIID_PATH( FromUTF8() );
3566
3567 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3568 {
3569 if( token != T_LEFT )
3570 Expecting( T_LEFT );
3571
3572 token = NextTok();
3573
3574 switch( token )
3575 {
3576 case T_page:
3577 {
3578 NeedSYMBOL();
3579 instance.m_PageNumber = FromUTF8();
3580
3581 // Empty page numbers are not permitted
3582 if( instance.m_PageNumber.IsEmpty() )
3583 {
3584 // Use hash character instead
3585 instance.m_PageNumber = wxT( "#" );
3586 }
3587 else
3588 {
3589 // Whitespaces are not permitted
3590 static std::vector<wxString> whitespaces =
3591 { wxT( "\r" ),
3592 wxT( "\n" ),
3593 wxT( "\t" ),
3594 wxT( " " ) };
3595
3596 for( wxString ch : whitespaces )
3597 instance.m_PageNumber.Replace( ch, wxEmptyString );
3598 }
3599
3600 NeedRIGHT();
3601 break;
3602 }
3603
3604 default:
3605 Expecting( "page" );
3606 }
3607 }
3608
3609 instances.emplace_back( instance );
3610 }
3611 }
3612
3613 sheet->setInstances( instances );
3614 break;
3615 }
3616
3617 default:
3618 Expecting( "at, size, stroke, background, instances, uuid, property, or pin" );
3619 }
3620 }
3621
3622 sheet->SetFields( fields );
3623
3624 return sheet.release();
3625}
3626
3627
3629{
3630 wxCHECK_MSG( CurTok() == T_junction, nullptr,
3631 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a junction." ) );
3632
3633 T token;
3634 std::unique_ptr<SCH_JUNCTION> junction = std::make_unique<SCH_JUNCTION>();
3635
3636 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3637 {
3638 if( token != T_LEFT )
3639 Expecting( T_LEFT );
3640
3641 token = NextTok();
3642
3643 switch( token )
3644 {
3645 case T_at:
3646 junction->SetPosition( parseXY() );
3647 NeedRIGHT();
3648 break;
3649
3650 case T_diameter:
3651 junction->SetDiameter( parseInternalUnits( "junction diameter" ) );
3652 NeedRIGHT();
3653 break;
3654
3655 case T_color:
3656 {
3657 COLOR4D color;
3658
3659 color.r = parseInt( "red" ) / 255.0;
3660 color.g = parseInt( "green" ) / 255.0;
3661 color.b = parseInt( "blue" ) / 255.0;
3662 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
3663
3664 junction->SetColor( color );
3665 NeedRIGHT();
3666 break;
3667 }
3668
3669 case T_uuid:
3670 NeedSYMBOL();
3671 const_cast<KIID&>( junction->m_Uuid ) = parseKIID();
3672 NeedRIGHT();
3673 break;
3674
3675 default:
3676 Expecting( "at, diameter, color or uuid" );
3677 }
3678 }
3679
3680 return junction.release();
3681}
3682
3683
3685{
3686 wxCHECK_MSG( CurTok() == T_no_connect, nullptr,
3687 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a no connect." ) );
3688
3689 T token;
3690 std::unique_ptr<SCH_NO_CONNECT> no_connect = std::make_unique<SCH_NO_CONNECT>();
3691
3692 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3693 {
3694 if( token != T_LEFT )
3695 Expecting( T_LEFT );
3696
3697 token = NextTok();
3698
3699 switch( token )
3700 {
3701 case T_at:
3702 no_connect->SetPosition( parseXY() );
3703 NeedRIGHT();
3704 break;
3705
3706 case T_uuid:
3707 NeedSYMBOL();
3708 const_cast<KIID&>( no_connect->m_Uuid ) = parseKIID();
3709 NeedRIGHT();
3710 break;
3711
3712 default:
3713 Expecting( "at or uuid" );
3714 }
3715 }
3716
3717 return no_connect.release();
3718}
3719
3720
3722{
3723 wxCHECK_MSG( CurTok() == T_bus_entry, nullptr,
3724 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus entry." ) );
3725
3726 T token;
3727 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3728 std::unique_ptr<SCH_BUS_WIRE_ENTRY> busEntry = std::make_unique<SCH_BUS_WIRE_ENTRY>();
3729
3730 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3731 {
3732 if( token != T_LEFT )
3733 Expecting( T_LEFT );
3734
3735 token = NextTok();
3736
3737 switch( token )
3738 {
3739 case T_at:
3740 busEntry->SetPosition( parseXY() );
3741 NeedRIGHT();
3742 break;
3743
3744 case T_size:
3745 {
3746 VECTOR2I size;
3747
3748 size.x = parseInternalUnits( "bus entry height" );
3749 size.y = parseInternalUnits( "bus entry width" );
3750 busEntry->SetSize( size );
3751 NeedRIGHT();
3752 break;
3753 }
3754
3755 case T_stroke:
3756 parseStroke( stroke );
3757 busEntry->SetStroke( stroke );
3758 break;
3759
3760 case T_uuid:
3761 NeedSYMBOL();
3762 const_cast<KIID&>( busEntry->m_Uuid ) = parseKIID();
3763 NeedRIGHT();
3764 break;
3765
3766 default:
3767 Expecting( "at, size, uuid or stroke" );
3768 }
3769 }
3770
3771 return busEntry.release();
3772}
3773
3774
3776{
3777 T token;
3778 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3779 FILL_PARAMS fill;
3780
3781 std::unique_ptr<SCH_SHAPE> polyline = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_NOTES );
3782
3783 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3784 {
3785 if( token != T_LEFT )
3786 Expecting( T_LEFT );
3787
3788 token = NextTok();
3789
3790 switch( token )
3791 {
3792 case T_pts:
3793 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3794 {
3795 if( token != T_LEFT )
3796 Expecting( T_LEFT );
3797
3798 token = NextTok();
3799
3800 if( token != T_xy )
3801 Expecting( "xy" );
3802
3803 polyline->AddPoint( parseXY() );
3804
3805 NeedRIGHT();
3806 }
3807 break;
3808
3809 case T_stroke:
3810 parseStroke( stroke );
3811
3812 // In 6.0, the default schematic line style was Dashed.
3813 if( m_requiredVersion <= 20211123 && stroke.GetLineStyle() == LINE_STYLE::DEFAULT )
3814 stroke.SetLineStyle( LINE_STYLE::DASH );
3815
3816 polyline->SetStroke( stroke );
3817 break;
3818
3819 case T_fill:
3820 parseFill( fill );
3821 polyline->SetFillMode( fill.m_FillType );
3822 polyline->SetFillColor( fill.m_Color );
3823 break;
3824
3825 case T_uuid:
3826 NeedSYMBOL();
3827 const_cast<KIID&>( polyline->m_Uuid ) = parseKIID();
3828 NeedRIGHT();
3829 break;
3830
3831 default:
3832 Expecting( "pts, uuid, stroke, or fill" );
3833 }
3834 }
3835
3836 return polyline.release();
3837}
3838
3839
3841{
3842 // Note: T_polyline is deprecated in this code: it is now handled by
3843 // parseSchPolyLine() that can handle true polygons, and not only one segment.
3844
3845 T token;
3846 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3847 int layer;
3848
3849 switch( CurTok() )
3850 {
3851 case T_polyline: layer = LAYER_NOTES; break;
3852 case T_wire: layer = LAYER_WIRE; break;
3853 case T_bus: layer = LAYER_BUS; break;
3854 default:
3855 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as a line." );
3856 }
3857
3858 std::unique_ptr<SCH_LINE> line = std::make_unique<SCH_LINE>( VECTOR2I(), layer );
3859
3860 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3861 {
3862 if( token != T_LEFT )
3863 Expecting( T_LEFT );
3864
3865 token = NextTok();
3866
3867 switch( token )
3868 {
3869 case T_pts:
3870 NeedLEFT();
3871 token = NextTok();
3872
3873 if( token != T_xy )
3874 Expecting( "xy" );
3875
3876 line->SetStartPoint( parseXY() );
3877 NeedRIGHT();
3878 NeedLEFT();
3879 token = NextTok();
3880
3881 if( token != T_xy )
3882 Expecting( "xy" );
3883
3884 line->SetEndPoint( parseXY() );
3885 NeedRIGHT();
3886 NeedRIGHT();
3887 break;
3888
3889 case T_stroke:
3890 parseStroke( stroke );
3891 line->SetStroke( stroke );
3892 break;
3893
3894 case T_uuid:
3895 NeedSYMBOL();
3896 const_cast<KIID&>( line->m_Uuid ) = parseKIID();
3897 NeedRIGHT();
3898 break;
3899
3900 default:
3901 Expecting( "at, uuid or stroke" );
3902 }
3903 }
3904
3905 return line.release();
3906}
3907
3908
3910{
3911 wxCHECK_MSG( CurTok() == T_arc, nullptr,
3912 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc." ) );
3913
3914 T token;
3915 VECTOR2I startPoint;
3916 VECTOR2I midPoint;
3917 VECTOR2I endPoint;
3918 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3919 FILL_PARAMS fill;
3920 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC );
3921
3922 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3923 {
3924 if( token != T_LEFT )
3925 Expecting( T_LEFT );
3926
3927 token = NextTok();
3928
3929 switch( token )
3930 {
3931 case T_start:
3932 startPoint = parseXY();
3933 NeedRIGHT();
3934 break;
3935
3936 case T_mid:
3937 midPoint = parseXY();
3938 NeedRIGHT();
3939 break;
3940
3941 case T_end:
3942 endPoint = parseXY();
3943 NeedRIGHT();
3944 break;
3945
3946 case T_stroke:
3947 parseStroke( stroke );
3948 arc->SetStroke( stroke );
3949 break;
3950
3951 case T_fill:
3952 parseFill( fill );
3953 arc->SetFillMode( fill.m_FillType );
3954 arc->SetFillColor( fill.m_Color );
3955 break;
3956
3957 case T_uuid:
3958 NeedSYMBOL();
3959 const_cast<KIID&>( arc->m_Uuid ) = KIID( FromUTF8() );
3960 NeedRIGHT();
3961 break;
3962
3963 default:
3964 Expecting( "start, mid, end, stroke, fill or uuid" );
3965 }
3966 }
3967
3968 arc->SetArcGeometry( startPoint, midPoint, endPoint );
3969
3970 return arc.release();
3971}
3972
3973
3975{
3976 wxCHECK_MSG( CurTok() == T_circle, nullptr,
3977 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle." ) );
3978
3979 T token;
3980 VECTOR2I center;
3981 int radius = 0;
3982 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3983 FILL_PARAMS fill;
3984 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE );
3985
3986 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3987 {
3988 if( token != T_LEFT )
3989 Expecting( T_LEFT );
3990
3991 token = NextTok();
3992
3993 switch( token )
3994 {
3995 case T_center:
3996 center = parseXY();
3997 NeedRIGHT();
3998 break;
3999
4000 case T_radius:
4001 radius = parseInternalUnits( "radius length" );
4002 NeedRIGHT();
4003 break;
4004
4005 case T_stroke:
4006 parseStroke( stroke );
4007 circle->SetStroke( stroke );
4008 break;
4009
4010 case T_fill:
4011 parseFill( fill );
4012 circle->SetFillMode( fill.m_FillType );
4013 circle->SetFillColor( fill.m_Color );
4014 break;
4015
4016 case T_uuid:
4017 NeedSYMBOL();
4018 const_cast<KIID&>( circle->m_Uuid ) = KIID( FromUTF8() );
4019 NeedRIGHT();
4020 break;
4021
4022 default:
4023 Expecting( "center, radius, stroke, fill or uuid" );
4024 }
4025 }
4026
4027 circle->SetCenter( center );
4028 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
4029
4030 return circle.release();
4031}
4032
4033
4035{
4036 wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
4037 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle." ) );
4038
4039 T token;
4040 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4041 FILL_PARAMS fill;
4042 std::unique_ptr<SCH_SHAPE> rectangle = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE );
4043
4044 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4045 {
4046 if( token != T_LEFT )
4047 Expecting( T_LEFT );
4048
4049 token = NextTok();
4050
4051 switch( token )
4052 {
4053 case T_start:
4054 rectangle->SetPosition( parseXY() );
4055 NeedRIGHT();
4056 break;
4057
4058 case T_end:
4059 rectangle->SetEnd( parseXY() );
4060 NeedRIGHT();
4061 break;
4062
4063 case T_stroke:
4064 parseStroke( stroke );
4065 rectangle->SetStroke( stroke );
4066 break;
4067
4068 case T_fill:
4069 parseFill( fill );
4070 rectangle->SetFillMode( fill.m_FillType );
4071 rectangle->SetFillColor( fill.m_Color );
4072 break;
4073
4074 case T_uuid:
4075 NeedSYMBOL();
4076 const_cast<KIID&>( rectangle->m_Uuid ) = KIID( FromUTF8() );
4077 NeedRIGHT();
4078 break;
4079
4080 default:
4081 Expecting( "start, end, stroke, fill or uuid" );
4082 }
4083 }
4084
4085 return rectangle.release();
4086}
4087
4088
4090{
4091 wxCHECK_MSG( CurTok() == T_rule_area, nullptr,
4092 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rule area." ) );
4093
4094 T token;
4095 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4096 FILL_PARAMS fill;
4097 std::unique_ptr<SCH_RULE_AREA> ruleArea = std::make_unique<SCH_RULE_AREA>();
4098
4099 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4100 {
4101 if( token != T_LEFT )
4102 Expecting( T_LEFT );
4103
4104 token = NextTok();
4105
4106 switch( token )
4107 {
4108 case T_polyline:
4109 {
4110 std::unique_ptr<SCH_SHAPE> poly( parseSchPolyLine() );
4111 SHAPE_POLY_SET& sch_rule_poly = poly->GetPolyShape();
4112
4113 // The polygon must be closed, it is a schematic closed polyline:
4114 sch_rule_poly.Outline(0).SetClosed( true );
4115
4116 ruleArea->SetPolyShape( sch_rule_poly );
4117
4118 ruleArea->SetStroke( poly->GetStroke() );
4119 ruleArea->SetFillMode( poly->GetFillMode() );
4120 ruleArea->SetFillColor( poly->GetFillColor() );
4121 break;
4122 }
4123 default:
4124 Expecting( "polyline" );
4125 }
4126 }
4127
4128 return ruleArea.release();
4129}
4130
4131
4133{
4134 wxCHECK_MSG( CurTok() == T_bezier, nullptr,
4135 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
4136
4137 T token;
4138 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4139 FILL_PARAMS fill;
4140 std::unique_ptr<SCH_SHAPE> bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER );
4141
4142 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4143 {
4144 if( token != T_LEFT )
4145 Expecting( T_LEFT );
4146
4147 token = NextTok();
4148
4149 switch( token )
4150 {
4151 case T_pts:
4152 {
4153 int ii = 0;
4154
4155 for( token = NextTok(); token != T_RIGHT; token = NextTok(), ++ii )
4156 {
4157 if( token != T_LEFT )
4158 Expecting( T_LEFT );
4159
4160 token = NextTok();
4161
4162 if( token != T_xy )
4163 Expecting( "xy" );
4164
4165 switch( ii )
4166 {
4167 case 0: bezier->SetStart( parseXY() ); break;
4168 case 1: bezier->SetBezierC1( parseXY() ); break;
4169 case 2: bezier->SetBezierC2( parseXY() ); break;
4170 case 3: bezier->SetEnd( parseXY() ); break;
4171 default: Unexpected( "control point" ); break;
4172 }
4173
4174 NeedRIGHT();
4175 }
4176 }
4177 break;
4178
4179 case T_stroke:
4180 parseStroke( stroke );
4181 bezier->SetStroke( stroke );
4182 break;
4183
4184 case T_fill:
4185 parseFill( fill );
4186 bezier->SetFillMode( fill.m_FillType );
4187 bezier->SetFillColor( fill.m_Color );
4188 break;
4189
4190 case T_uuid:
4191 NeedSYMBOL();
4192 const_cast<KIID&>( bezier->m_Uuid ) = KIID( FromUTF8() );
4193 NeedRIGHT();
4194 break;
4195
4196 default:
4197 Expecting( "pts, stroke, fill or uuid" );
4198 }
4199 }
4200
4201 return bezier.release();
4202}
4203
4204
4206{
4207 T token;
4208 std::unique_ptr<SCH_TEXT> text;
4209
4210 switch( CurTok() )
4211 {
4212 case T_text: text = std::make_unique<SCH_TEXT>(); break;
4213 case T_label: text = std::make_unique<SCH_LABEL>(); break;
4214 case T_global_label: text = std::make_unique<SCH_GLOBALLABEL>(); break;
4215 case T_hierarchical_label: text = std::make_unique<SCH_HIERLABEL>(); break;
4216 case T_netclass_flag: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
4217 case T_directive_label: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
4218 default:
4219 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as text." );
4220 }
4221
4222 // We'll reset this if we find a fields_autoplaced token
4223 text->ClearFieldsAutoplaced();
4224
4225 NeedSYMBOL();
4226
4227 text->SetText( FromUTF8() );
4228
4229 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4230 {
4231 if( token != T_LEFT )
4232 Expecting( T_LEFT );
4233
4234 token = NextTok();
4235
4236 switch( token )
4237 {
4238 case T_exclude_from_sim:
4239 text->SetExcludedFromSim( parseBool() );
4240 NeedRIGHT();
4241 break;
4242
4243 case T_at:
4244 text->SetPosition( parseXY() );
4245 text->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ).KeepUpright() );
4246
4247 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() ) )
4248 {
4249 switch( static_cast<int>( label->GetTextAngle().AsDegrees() ) )
4250 {
4251 case 0: label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
4252 case 90: label->SetSpinStyle( SPIN_STYLE::UP ); break;
4253 case 180: label->SetSpinStyle( SPIN_STYLE::LEFT ); break;
4254 case 270: label->SetSpinStyle( SPIN_STYLE::BOTTOM ); break;
4255 default: wxFAIL; label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
4256 }
4257 }
4258
4259 NeedRIGHT();
4260 break;
4261
4262 case T_shape:
4263 {
4264 if( text->Type() == SCH_TEXT_T || text->Type() == SCH_LABEL_T )
4265 Unexpected( T_shape );
4266
4267 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( text.get() );
4268
4269 token = NextTok();
4270
4271 switch( token )
4272 {
4273 case T_input: label->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
4274 case T_output: label->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
4275 case T_bidirectional: label->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
4276 case T_tri_state: label->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE ); break;
4277 case T_passive: label->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
4278 case T_dot: label->SetShape( LABEL_FLAG_SHAPE::F_DOT ); break;
4279 case T_round: label->SetShape( LABEL_FLAG_SHAPE::F_ROUND ); break;
4280 case T_diamond: label->SetShape( LABEL_FLAG_SHAPE::F_DIAMOND ); break;
4281 case T_rectangle: label->SetShape( LABEL_FLAG_SHAPE::F_RECTANGLE ); break;
4282 default:
4283 Expecting( "input, output, bidirectional, tri_state, passive, dot, round, diamond"
4284 "or rectangle" );
4285 }
4286
4287 NeedRIGHT();
4288 break;
4289 }
4290
4291 case T_length:
4292 {
4293 if( text->Type() != SCH_DIRECTIVE_LABEL_T )
4294 Unexpected( T_length );
4295
4296 SCH_DIRECTIVE_LABEL* label = static_cast<SCH_DIRECTIVE_LABEL*>( text.get() );
4297
4298 label->SetPinLength( parseInternalUnits( "pin length" ) );
4299 NeedRIGHT();
4300 }
4301 break;
4302
4303 case T_fields_autoplaced:
4304 if( parseMaybeAbsentBool( true ) )
4305 text->SetFieldsAutoplaced();
4306
4307 break;
4308
4309 case T_effects:
4310 parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ), true );
4311 break;
4312
4313 case T_iref: // legacy format; current is a T_property (aka SCH_FIELD)
4314 if( text->Type() == SCH_GLOBAL_LABEL_T )
4315 {
4316 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
4317 SCH_FIELD* field = &label->GetFields()[0];
4318
4319 field->SetTextPos( parseXY() );
4320 NeedRIGHT();
4321
4322 field->SetVisible( true );
4323 }
4324 break;
4325
4326 case T_uuid:
4327 NeedSYMBOL();
4328 const_cast<KIID&>( text->m_Uuid ) = parseKIID();
4329 NeedRIGHT();
4330 break;
4331
4332 case T_property:
4333 {
4334 if( text->Type() == SCH_TEXT_T )
4335 Unexpected( T_property );
4336
4337 SCH_FIELD* field = parseSchField( text.get() );
4338
4339 // If the field is a Intersheetrefs it is not handled like other fields:
4340 // It always exists and is the first in list
4341 if( text->Type() == SCH_GLOBAL_LABEL_T
4342 && ( field->GetInternalName() == wxT( "Intersheet References" ) // old name in V6.0
4343 || field->GetInternalName() == wxT( "Intersheetrefs" ) ) ) // Current name
4344 {
4345 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
4346 // Ensure the Id of this special and first field is 0, needed by
4347 // SCH_FIELD::IsHypertext() test
4348 field->SetId( 0 );
4349
4350 label->GetFields()[0] = *field;
4351 }
4352 else
4353 {
4354 static_cast<SCH_LABEL_BASE*>( text.get() )->GetFields().emplace_back( *field );
4355 }
4356
4357 delete field;
4358 break;
4359 }
4360
4361 default:
4362 Expecting( "at, shape, iref, uuid or effects" );
4363 }
4364 }
4365
4366 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() );
4367
4368 if( label && label->GetFields().empty() )
4369 label->SetFieldsAutoplaced();
4370
4371 return text.release();
4372}
4373
4374
4376{
4377 wxCHECK_MSG( CurTok() == T_text_box, nullptr,
4378 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text box." ) );
4379
4380 std::unique_ptr<SCH_TEXTBOX> textBox = std::make_unique<SCH_TEXTBOX>();
4381
4382 parseSchTextBoxContent( textBox.get() );
4383
4384 return textBox.release();
4385}
4386
4387
4389{
4390 wxCHECK_MSG( CurTok() == T_table_cell, nullptr,
4391 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table cell." ) );
4392
4393 std::unique_ptr<SCH_TABLECELL> cell = std::make_unique<SCH_TABLECELL>();
4394
4395 parseSchTextBoxContent( cell.get() );
4396
4397 return cell.release();
4398}
4399
4400
4402{
4403 T token;
4404 VECTOR2I pos;
4405 VECTOR2I end;
4406 VECTOR2I size;
4407 int left;
4408 int top;
4409 int right;
4410 int bottom;
4411 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4412 FILL_PARAMS fill;
4413 bool foundEnd = false;
4414 bool foundSize = false;
4415 bool foundMargins = false;
4416
4417 NeedSYMBOL();
4418
4419 aTextBox->SetText( FromUTF8() );
4420
4421 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4422 {
4423 if( token != T_LEFT )
4424 Expecting( T_LEFT );
4425
4426 token = NextTok();
4427
4428 switch( token )
4429 {
4430 case T_exclude_from_sim:
4431 aTextBox->SetExcludedFromSim( parseBool() );
4432 NeedRIGHT();
4433 break;
4434
4435 case T_start: // Legacy token during 6.99 development; fails to handle angle
4436 pos = parseXY();
4437 NeedRIGHT();
4438 break;
4439
4440 case T_end: // Legacy token during 6.99 development; fails to handle angle
4441 end = parseXY();
4442 foundEnd = true;
4443 NeedRIGHT();
4444 break;
4445
4446 case T_at:
4447 pos = parseXY();
4448 aTextBox->SetTextAngle( EDA_ANGLE( parseDouble( "textbox angle" ), DEGREES_T ) );
4449 NeedRIGHT();
4450 break;
4451
4452 case T_size:
4453 size = parseXY();
4454 foundSize = true;
4455 NeedRIGHT();
4456 break;
4457
4458 case T_span:
4459 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( aTextBox ) )
4460 {
4461 cell->SetColSpan( parseInt( "column span" ) );
4462 cell->SetRowSpan( parseInt( "row span" ) );
4463 }
4464 else
4465 {
4466 Expecting( "at, size, stroke, fill, effects or uuid" );
4467 }
4468
4469 NeedRIGHT();
4470 break;
4471
4472 case T_stroke:
4473 parseStroke( stroke );
4474 aTextBox->SetStroke( stroke );
4475 break;
4476
4477 case T_fill:
4478 parseFill( fill );
4479 aTextBox->SetFillMode( fill.m_FillType );
4480 aTextBox->SetFillColor( fill.m_Color );
4481 break;
4482
4483 case T_margins:
4484 parseMargins( left, top, right, bottom );
4485 aTextBox->SetMarginLeft( left );
4486 aTextBox->SetMarginTop( top );
4487 aTextBox->SetMarginRight( right );
4488 aTextBox->SetMarginBottom( bottom );
4489 foundMargins = true;
4490 NeedRIGHT();
4491 break;
4492
4493 case T_effects:
4494 parseEDA_TEXT( static_cast<EDA_TEXT*>( aTextBox ), false );
4495 break;
4496
4497 case T_uuid:
4498 NeedSYMBOL();
4499 const_cast<KIID&>( aTextBox->m_Uuid ) = KIID( FromUTF8() );
4500 NeedRIGHT();
4501 break;
4502
4503 default:
4504 if( dynamic_cast<SCH_TABLECELL*>( aTextBox ) != nullptr )
4505 Expecting( "at, size, stroke, fill, effects, span or uuid" );
4506 else
4507 Expecting( "at, size, stroke, fill, effects or uuid" );
4508 }
4509 }
4510
4511 aTextBox->SetPosition( pos );
4512
4513 if( foundEnd )
4514 aTextBox->SetEnd( end );
4515 else if( foundSize )
4516 aTextBox->SetEnd( pos + size );
4517 else
4518 Expecting( "size" );
4519
4520 if( !foundMargins )
4521 {
4522 int margin = aTextBox->GetLegacyTextMargin();
4523 aTextBox->SetMarginLeft( margin );
4524 aTextBox->SetMarginTop( margin );
4525 aTextBox->SetMarginRight( margin );
4526 aTextBox->SetMarginBottom( margin );
4527 }
4528}
4529
4530
4532{
4533 wxCHECK_MSG( CurTok() == T_table, nullptr,
4534 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table." ) );
4535
4536 T token;
4537 int defaultLineWidth = schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
4538 STROKE_PARAMS borderStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
4539 STROKE_PARAMS separatorsStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
4540 std::unique_ptr<SCH_TABLE> table = std::make_unique<SCH_TABLE>( defaultLineWidth );
4541
4542 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4543 {
4544 if( token != T_LEFT )
4545 Expecting( T_LEFT );
4546
4547 token = NextTok();
4548
4549 switch( token )
4550 {
4551 case T_column_count:
4552 table->SetColCount( parseInt( "column count" ) );
4553 NeedRIGHT();
4554 break;
4555
4556 case T_column_widths:
4557 {
4558 int col = 0;
4559
4560 while( ( token = NextTok() ) != T_RIGHT )
4561 table->SetColWidth( col++, parseInternalUnits() );
4562
4563 break;
4564 }
4565
4566 case T_row_heights:
4567 {
4568 int row = 0;
4569
4570 while( ( token = NextTok() ) != T_RIGHT )
4571 table->SetRowHeight( row++, parseInternalUnits() );
4572
4573 break;
4574 }
4575
4576 case T_cells:
4577 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4578 {
4579 if( token != T_LEFT )
4580 Expecting( T_LEFT );
4581
4582 token = NextTok();
4583
4584 if( token != T_table_cell )
4585 Expecting( "table_cell" );
4586
4587 table->AddCell( parseSchTableCell() );
4588 }
4589
4590 break;
4591
4592 case T_border:
4593 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4594 {
4595 if( token != T_LEFT )
4596 Expecting( T_LEFT );
4597
4598 token = NextTok();
4599
4600 switch( token )
4601 {
4602 case T_external:
4603 table->SetStrokeExternal( parseBool() );
4604 NeedRIGHT();
4605 break;
4606
4607 case T_header:
4608 table->SetStrokeHeader( parseBool() );
4609 NeedRIGHT();
4610 break;
4611
4612 case T_stroke:
4613 parseStroke( borderStroke );
4614 table->SetBorderStroke( borderStroke );
4615 break;
4616
4617 default:
4618 Expecting( "external, header or stroke" );
4619 break;
4620 }
4621 }
4622
4623 break;
4624
4625 case T_separators:
4626 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4627 {
4628 if( token != T_LEFT )
4629 Expecting( T_LEFT );
4630
4631 token = NextTok();
4632
4633 switch( token )
4634 {
4635 case T_rows:
4636 table->SetStrokeRows( parseBool() );
4637 NeedRIGHT();
4638 break;
4639
4640 case T_cols:
4641 table->SetStrokeColumns( parseBool() );
4642 NeedRIGHT();
4643 break;
4644
4645 case T_stroke:
4646 parseStroke( separatorsStroke );
4647 table->SetSeparatorsStroke( separatorsStroke );
4648 break;
4649
4650 default:
4651 Expecting( "rows, cols, or stroke" );
4652 break;
4653 }
4654 }
4655
4656 break;
4657
4658 default:
4659 Expecting( "columns, col_widths, row_heights, border, separators, header or cells" );
4660 }
4661 }
4662
4663 return table.release();
4664}
4665
4666
4668{
4669 wxCHECK_RET( CurTok() == T_bus_alias,
4670 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus alias." ) );
4671 wxCHECK( aScreen, /* void */ );
4672
4673 T token;
4674 std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>( aScreen );
4675 wxString alias;
4676 wxString member;
4677
4678 NeedSYMBOL();
4679
4680 alias = FromUTF8();
4681
4682 if( m_requiredVersion < 20210621 )
4683 alias = ConvertToNewOverbarNotation( alias );
4684
4685 busAlias->SetName( alias );
4686
4687 NeedLEFT();
4688 token = NextTok();
4689
4690 if( token != T_members )
4691 Expecting( "members" );
4692
4693 token = NextTok();
4694
4695 while( token != T_RIGHT )
4696 {
4697 if( !IsSymbol( token ) )
4698 Expecting( "quoted string" );
4699
4700 member = FromUTF8();
4701
4702 if( m_requiredVersion < 20210621 )
4703 member = ConvertToNewOverbarNotation( member );
4704
4705 busAlias->Members().emplace_back( member );
4706
4707 token = NextTok();
4708 }
4709
4710 NeedRIGHT();
4711
4712 aScreen->AddBusAlias( busAlias );
4713}
4714
4715
4717 EDA_TEXT* aCandidate,
4718 bool aReplaceInList )
4719{
4720 if( auto it = m_fontTextMap.find( aEntry ); it != m_fontTextMap.end() )
4721 {
4722 // Copy font from aEntry to aCandidate if not nullptr
4723 if( aCandidate )
4724 {
4725 if( aReplaceInList )
4726 {
4727 m_fontTextMap[aCandidate] = it->second;
4728 }
4729 else
4730 {
4731 aCandidate->SetFont( KIFONT::FONT::GetFont( std::get<0>( it->second ),
4732 std::get<1>( it->second ),
4733 std::get<2>( it->second ) ) );
4734 }
4735 }
4736
4737 // Remove old entry from m_fontTextMap.
4738 m_fontTextMap.erase( it );
4739 }
4740}
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:419
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:464
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition: eda_text.cpp:321
void SetBoldFlag(bool aBold)
Set only the italic flag, without changing the font.
Definition: eda_text.cpp:282
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:290
void SetLineSpacing(double aLineSpacing)
Definition: eda_text.cpp:411
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:1126
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 SetFont(KIFONT::FONT *aFont)
Definition: eda_text.cpp:403
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:313
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 removeEntryInFontTextMap(EDA_TEXT *aEntry, EDA_TEXT *aCandidate, bool aReplaceInList)
Remove aEntry from m_fontTextMap.
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:370
@ LAYER_WIRE
Definition: layer_ids.h:356
@ LAYER_NOTES
Definition: layer_ids.h:371
@ LAYER_BUS
Definition: layer_ids.h:357
#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