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