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