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, Clamp
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( Clamp<double>( -int_limit, retval, 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( Clamp<double>( -int_limit, retval, 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 = 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 = 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 symbol->SetBodyStyle( parseInt( "symbol body style" ) );
2999 NeedRIGHT();
3000 break;
3001
3002 case T_exclude_from_sim:
3003 symbol->SetExcludedFromSim( parseBool() );
3004 NeedRIGHT();
3005 break;
3006
3007 case T_in_bom:
3008 symbol->SetExcludedFromBOM( !parseBool() );
3009 NeedRIGHT();
3010 break;
3011
3012 case T_on_board:
3013 symbol->SetExcludedFromBoard( !parseBool() );
3014 NeedRIGHT();
3015 break;
3016
3017 case T_dnp:
3018 symbol->SetDNP( parseBool() );
3019 NeedRIGHT();
3020 break;
3021
3022 case T_fields_autoplaced:
3023 if( parseMaybeAbsentBool( true ) )
3024 symbol->SetFieldsAutoplaced();
3025
3026 break;
3027
3028 case T_uuid:
3029 NeedSYMBOL();
3030 const_cast<KIID&>( symbol->m_Uuid ) = parseKIID();
3031 NeedRIGHT();
3032 break;
3033
3034 case T_default_instance:
3035 {
3036 SCH_SYMBOL_INSTANCE defaultInstance;
3037
3038 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3039 {
3040 if( token != T_LEFT )
3041 Expecting( T_LEFT );
3042
3043 token = NextTok();
3044
3045 switch( token )
3046 {
3047 case T_reference:
3048 NeedSYMBOL();
3049 defaultInstance.m_Reference = FromUTF8();
3050 NeedRIGHT();
3051 break;
3052
3053 case T_unit:
3054 defaultInstance.m_Unit = parseInt( "symbol unit" );
3055 NeedRIGHT();
3056 break;
3057
3058 case T_value:
3059 NeedSYMBOL();
3060 symbol->SetValueFieldText( FromUTF8() );
3061 NeedRIGHT();
3062 break;
3063
3064 case T_footprint:
3065 NeedSYMBOL();
3066 symbol->SetFootprintFieldText( FromUTF8() );
3067 NeedRIGHT();
3068 break;
3069
3070 default:
3071 Expecting( "reference, unit, value or footprint" );
3072 }
3073 }
3074
3075 break;
3076 }
3077
3078 case T_instances:
3079 {
3080 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3081 {
3082 if( token != T_LEFT )
3083 Expecting( T_LEFT );
3084
3085 token = NextTok();
3086
3087 if( token != T_project )
3088 Expecting( "project" );
3089
3090 NeedSYMBOL();
3091
3092 wxString projectName = FromUTF8();
3093
3094 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3095 {
3096 if( token != T_LEFT )
3097 Expecting( T_LEFT );
3098
3099 token = NextTok();
3100
3101 if( token != T_path )
3102 Expecting( "path" );
3103
3104 SCH_SYMBOL_INSTANCE instance;
3105
3106 instance.m_ProjectName = projectName;
3107
3108 NeedSYMBOL();
3109 instance.m_Path = KIID_PATH( FromUTF8() );
3110
3111 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3112 {
3113 if( token != T_LEFT )
3114 Expecting( T_LEFT );
3115
3116 token = NextTok();
3117
3118 switch( token )
3119 {
3120 case T_reference:
3121 NeedSYMBOL();
3122 instance.m_Reference = FromUTF8();
3123 NeedRIGHT();
3124 break;
3125
3126 case T_unit:
3127 instance.m_Unit = parseInt( "symbol unit" );
3128 NeedRIGHT();
3129 break;
3130
3131 case T_value:
3132 NeedSYMBOL();
3133 symbol->SetValueFieldText( FromUTF8() );
3134 NeedRIGHT();
3135 break;
3136
3137 case T_footprint:
3138 NeedSYMBOL();
3139 symbol->SetFootprintFieldText( FromUTF8() );
3140 NeedRIGHT();
3141 break;
3142
3143 default:
3144 Expecting( "reference, unit, value or footprint" );
3145 }
3146
3147 symbol->AddHierarchicalReference( instance );
3148 }
3149 }
3150 }
3151
3152 break;
3153 }
3154
3155 case T_property:
3156 // The field parent symbol must be set and its orientation must be set before
3157 // the field positions are set.
3158 field = parseSchField( symbol.get() );
3159
3160 // Exclude from simulation used to be managed by a Sim.Enable field set to "0" when
3161 // simulation was disabled.
3163 {
3164 symbol->SetExcludedFromSim( field->GetText() == wxS( "0" ) );
3165 break;
3166 }
3167
3168 // Even longer ago, we had a "Spice_Netlist_Enabled" field
3170 {
3171 symbol->SetExcludedFromSim( field->GetText() == wxS( "N" ) );
3172 break;
3173 }
3174
3175 if( ( field->GetId() >= MANDATORY_FIELDS ) && m_fieldIDsRead.count( field->GetId() ) )
3176 {
3177 int nextAvailableId = field->GetId() + 1;
3178
3179 while( m_fieldIDsRead.count( nextAvailableId ) )
3180 nextAvailableId += 1;
3181
3182 field->SetId( nextAvailableId );
3183 }
3184
3185 if( symbol->GetFieldById( field->GetId() ) )
3186 *symbol->GetFieldById( field->GetId() ) = *field;
3187 else
3188 symbol->AddField( *field );
3189
3190 if( field->GetId() == REFERENCE_FIELD )
3191 symbol->UpdatePrefix();
3192
3193 m_fieldIDsRead.insert( field->GetId() );
3194
3195 delete field;
3196 break;
3197
3198 case T_pin:
3199 {
3200 // Read an alternate pin designation
3201 wxString number;
3202 KIID uuid;
3203 wxString alt;
3204
3205 NeedSYMBOL();
3206 number = FromUTF8();
3207
3208 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3209 {
3210 if( token != T_LEFT )
3211 Expecting( T_LEFT );
3212
3213 token = NextTok();
3214
3215 switch( token )
3216 {
3217 case T_alternate:
3218 NeedSYMBOL();
3219 alt = FromUTF8();
3220 NeedRIGHT();
3221 break;
3222
3223 case T_uuid:
3224 NeedSYMBOL();
3225
3226 // First version to write out pin uuids accidentally wrote out the symbol's
3227 // uuid for each pin, so ignore uuids coming from that version.
3228 if( m_requiredVersion >= 20210126 )
3229 uuid = parseKIID();
3230
3231 NeedRIGHT();
3232 break;
3233
3234 default:
3235 Expecting( "alternate or uuid" );
3236 }
3237 }
3238
3239 symbol->GetRawPins().emplace_back( std::make_unique<SCH_PIN>( symbol.get(), number,
3240 alt, uuid ) );
3241 break;
3242 }
3243
3244 default:
3245 Expecting( "lib_id, lib_name, at, mirror, uuid, on_board, in_bom, dnp, "
3246 "default_instance, property, pin, or instances" );
3247 }
3248 }
3249
3250 if( !libName.IsEmpty() && ( symbol->GetLibId().Format().wx_str() != libName ) )
3251 symbol->SetSchSymbolLibraryName( libName );
3252
3253 // Ensure edit/status flags are cleared after these initializations:
3254 symbol->ClearFlags();
3255
3256 return symbol.release();
3257}
3258
3259
3261{
3262 wxCHECK_MSG( CurTok() == T_image, nullptr,
3263 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an image." ) );
3264
3265 T token;
3266 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>();
3267
3268 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3269 {
3270 if( token != T_LEFT )
3271 Expecting( T_LEFT );
3272
3273 token = NextTok();
3274
3275 switch( token )
3276 {
3277 case T_at:
3278 bitmap->SetPosition( parseXY() );
3279 NeedRIGHT();
3280 break;
3281
3282 case T_scale:
3283 bitmap->GetImage()->SetScale( parseDouble( "image scale factor" ) );
3284
3285 if( !std::isnormal( bitmap->GetImage()->GetScale() ) )
3286 bitmap->GetImage()->SetScale( 1.0 );
3287
3288 NeedRIGHT();
3289 break;
3290
3291 case T_uuid:
3292 NeedSYMBOL();
3293 const_cast<KIID&>( bitmap->m_Uuid ) = parseKIID();
3294 NeedRIGHT();
3295 break;
3296
3297 case T_data:
3298 {
3299 token = NextTok();
3300
3301 wxString data;
3302
3303 // Reserve 128K because most image files are going to be larger than the default
3304 // 1K that wxString reserves.
3305 data.reserve( 1 << 17 );
3306
3307 while( token != T_RIGHT )
3308 {
3309 if( !IsSymbol( token ) )
3310 Expecting( "base64 image data" );
3311
3312 data += FromUTF8();
3313 token = NextTok();
3314 }
3315
3316 wxMemoryBuffer buffer = wxBase64Decode( data );
3317
3318 if( !bitmap->GetImage()->ReadImageFile( buffer ) )
3319 THROW_IO_ERROR( _( "Failed to read image data." ) );
3320
3321 break;
3322 }
3323
3324 default:
3325 Expecting( "at, scale, uuid or data" );
3326 }
3327 }
3328
3329 // Adjust the image pixel size in iu
3330 BITMAP_BASE* image = bitmap->GetImage();
3331 image->SetPixelSizeIu( (double) schIUScale.MilsToIU( 1000 ) / image->GetPPI() );
3332
3333 // 20230121 or older file format versions assumed 300 image PPI at load/save.
3334 // Let's keep compatibility by changing image scale.
3335 if( m_requiredVersion <= 20230121 )
3336 {
3337 image->SetScale( image->GetScale() * image->GetPPI() / 300.0 );
3338 }
3339
3340 return bitmap.release();
3341}
3342
3343
3345{
3346 wxCHECK_MSG( CurTok() == T_sheet, nullptr,
3347 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a sheet." ) );
3348
3349 T token;
3350 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3351 FILL_PARAMS fill;
3352 SCH_FIELD* field;
3353 std::vector<SCH_FIELD> fields;
3354 std::unique_ptr<SCH_SHEET> sheet = std::make_unique<SCH_SHEET>();
3355 std::set<int> fieldIDsRead;
3356
3357 // We'll reset this if we find a fields_autoplaced token
3358 sheet->ClearFieldsAutoplaced();
3359
3360 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3361 {
3362 if( token != T_LEFT )
3363 Expecting( T_LEFT );
3364
3365 token = NextTok();
3366
3367 switch( token )
3368 {
3369 case T_at:
3370 sheet->SetPosition( parseXY() );
3371 NeedRIGHT();
3372 break;
3373
3374 case T_size:
3375 {
3376 VECTOR2I size;
3377 size.x = parseInternalUnits( "sheet width" );
3378 size.y = parseInternalUnits( "sheet height" );
3379 sheet->SetSize( size );
3380 NeedRIGHT();
3381 break;
3382 }
3383
3384 case T_exclude_from_sim:
3385 sheet->SetExcludedFromSim( parseBool() );
3386 NeedRIGHT();
3387 break;
3388
3389 case T_in_bom:
3390 sheet->SetExcludedFromBOM( !parseBool() );
3391 NeedRIGHT();
3392 break;
3393
3394 case T_on_board:
3395 sheet->SetExcludedFromBoard( !parseBool() );
3396 NeedRIGHT();
3397 break;
3398
3399 case T_dnp:
3400 sheet->SetDNP( parseBool() );
3401 NeedRIGHT();
3402 break;
3403
3404 case T_fields_autoplaced:
3405 if( parseMaybeAbsentBool( true ) )
3406 sheet->SetFieldsAutoplaced();
3407
3408 break;
3409
3410 case T_stroke:
3411 parseStroke( stroke );
3412 sheet->SetBorderWidth( stroke.GetWidth() );
3413 sheet->SetBorderColor( stroke.GetColor() );
3414 break;
3415
3416 case T_fill:
3417 parseFill( fill );
3418 sheet->SetBackgroundColor( fill.m_Color );
3419 break;
3420
3421 case T_uuid:
3422 NeedSYMBOL();
3423 const_cast<KIID&>( sheet->m_Uuid ) = parseKIID();
3424 NeedRIGHT();
3425 break;
3426
3427 case T_property:
3428 field = parseSchField( sheet.get() );
3429
3430 if( m_requiredVersion <= 20200310 )
3431 {
3432 // Earlier versions had the wrong ids (and names) saved for sheet fields.
3433 // Fortunately they only saved the sheetname and sheetfilepath (and always
3434 // in that order), so we can hack in a recovery.
3435 if( fields.empty() )
3436 field->SetId( SHEETNAME );
3437 else
3438 field->SetId( SHEETFILENAME );
3439 }
3440
3441 // It would appear the problem persists past 20200310, but this time with the
3442 // earlier ids being re-used for later (user) fields. The easiest (and most
3443 // complete) solution is to disallow multiple instances of the same id (for all
3444 // files since the source of the error *might* in fact be hand-edited files).
3445 //
3446 // While no longer used, -1 is still a valid id for user field. We convert it to
3447 // the first available ID after the mandatory fields
3448
3449 if( field->GetId() < 0 )
3450 field->SetId( SHEET_MANDATORY_FIELDS );
3451
3452 while( !fieldIDsRead.insert( field->GetId() ).second )
3453 field->SetId( field->GetId() + 1 );
3454
3455 fields.emplace_back( *field );
3456 delete field;
3457 break;
3458
3459 case T_pin:
3460 sheet->AddPin( parseSchSheetPin( sheet.get() ) );
3461 break;
3462
3463 case T_instances:
3464 {
3465 std::vector<SCH_SHEET_INSTANCE> instances;
3466
3467 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3468 {
3469 if( token != T_LEFT )
3470 Expecting( T_LEFT );
3471
3472 token = NextTok();
3473
3474 if( token != T_project )
3475 Expecting( "project" );
3476
3477 NeedSYMBOL();
3478
3479 wxString projectName = FromUTF8();
3480
3481 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3482 {
3483 if( token != T_LEFT )
3484 Expecting( T_LEFT );
3485
3486 token = NextTok();
3487
3488 if( token != T_path )
3489 Expecting( "path" );
3490
3491 SCH_SHEET_INSTANCE instance;
3492
3493 instance.m_ProjectName = projectName;
3494
3495 NeedSYMBOL();
3496 instance.m_Path = KIID_PATH( FromUTF8() );
3497
3498 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3499 {
3500 if( token != T_LEFT )
3501 Expecting( T_LEFT );
3502
3503 token = NextTok();
3504
3505 switch( token )
3506 {
3507 case T_page:
3508 {
3509 NeedSYMBOL();
3510 instance.m_PageNumber = FromUTF8();
3511
3512 // Empty page numbers are not permitted
3513 if( instance.m_PageNumber.IsEmpty() )
3514 {
3515 // Use hash character instead
3516 instance.m_PageNumber = wxT( "#" );
3517 }
3518 else
3519 {
3520 // Whitespaces are not permitted
3521 static std::vector<wxString> whitespaces =
3522 { wxT( "\r" ),
3523 wxT( "\n" ),
3524 wxT( "\t" ),
3525 wxT( " " ) };
3526
3527 for( wxString ch : whitespaces )
3528 instance.m_PageNumber.Replace( ch, wxEmptyString );
3529 }
3530
3531 NeedRIGHT();
3532 break;
3533 }
3534
3535 default:
3536 Expecting( "page" );
3537 }
3538 }
3539
3540 instances.emplace_back( instance );
3541 }
3542 }
3543
3544 sheet->setInstances( instances );
3545 break;
3546 }
3547
3548 default:
3549 Expecting( "at, size, stroke, background, instances, uuid, property, or pin" );
3550 }
3551 }
3552
3553 sheet->SetFields( fields );
3554
3555 return sheet.release();
3556}
3557
3558
3560{
3561 wxCHECK_MSG( CurTok() == T_junction, nullptr,
3562 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a junction." ) );
3563
3564 T token;
3565 std::unique_ptr<SCH_JUNCTION> junction = std::make_unique<SCH_JUNCTION>();
3566
3567 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3568 {
3569 if( token != T_LEFT )
3570 Expecting( T_LEFT );
3571
3572 token = NextTok();
3573
3574 switch( token )
3575 {
3576 case T_at:
3577 junction->SetPosition( parseXY() );
3578 NeedRIGHT();
3579 break;
3580
3581 case T_diameter:
3582 junction->SetDiameter( parseInternalUnits( "junction diameter" ) );
3583 NeedRIGHT();
3584 break;
3585
3586 case T_color:
3587 {
3588 COLOR4D color;
3589
3590 color.r = parseInt( "red" ) / 255.0;
3591 color.g = parseInt( "green" ) / 255.0;
3592 color.b = parseInt( "blue" ) / 255.0;
3593 color.a = Clamp( parseDouble( "alpha" ), 0.0, 1.0 );
3594
3595 junction->SetColor( color );
3596 NeedRIGHT();
3597 break;
3598 }
3599
3600 case T_uuid:
3601 NeedSYMBOL();
3602 const_cast<KIID&>( junction->m_Uuid ) = parseKIID();
3603 NeedRIGHT();
3604 break;
3605
3606 default:
3607 Expecting( "at, diameter, color or uuid" );
3608 }
3609 }
3610
3611 return junction.release();
3612}
3613
3614
3616{
3617 wxCHECK_MSG( CurTok() == T_no_connect, nullptr,
3618 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a no connect." ) );
3619
3620 T token;
3621 std::unique_ptr<SCH_NO_CONNECT> no_connect = std::make_unique<SCH_NO_CONNECT>();
3622
3623 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3624 {
3625 if( token != T_LEFT )
3626 Expecting( T_LEFT );
3627
3628 token = NextTok();
3629
3630 switch( token )
3631 {
3632 case T_at:
3633 no_connect->SetPosition( parseXY() );
3634 NeedRIGHT();
3635 break;
3636
3637 case T_uuid:
3638 NeedSYMBOL();
3639 const_cast<KIID&>( no_connect->m_Uuid ) = parseKIID();
3640 NeedRIGHT();
3641 break;
3642
3643 default:
3644 Expecting( "at or uuid" );
3645 }
3646 }
3647
3648 return no_connect.release();
3649}
3650
3651
3653{
3654 wxCHECK_MSG( CurTok() == T_bus_entry, nullptr,
3655 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus entry." ) );
3656
3657 T token;
3658 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3659 std::unique_ptr<SCH_BUS_WIRE_ENTRY> busEntry = std::make_unique<SCH_BUS_WIRE_ENTRY>();
3660
3661 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3662 {
3663 if( token != T_LEFT )
3664 Expecting( T_LEFT );
3665
3666 token = NextTok();
3667
3668 switch( token )
3669 {
3670 case T_at:
3671 busEntry->SetPosition( parseXY() );
3672 NeedRIGHT();
3673 break;
3674
3675 case T_size:
3676 {
3677 VECTOR2I size;
3678
3679 size.x = parseInternalUnits( "bus entry height" );
3680 size.y = parseInternalUnits( "bus entry width" );
3681 busEntry->SetSize( size );
3682 NeedRIGHT();
3683 break;
3684 }
3685
3686 case T_stroke:
3687 parseStroke( stroke );
3688 busEntry->SetStroke( stroke );
3689 break;
3690
3691 case T_uuid:
3692 NeedSYMBOL();
3693 const_cast<KIID&>( busEntry->m_Uuid ) = parseKIID();
3694 NeedRIGHT();
3695 break;
3696
3697 default:
3698 Expecting( "at, size, uuid or stroke" );
3699 }
3700 }
3701
3702 return busEntry.release();
3703}
3704
3705
3707{
3708 T token;
3709 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3710 FILL_PARAMS fill;
3711
3712 std::unique_ptr<SCH_SHAPE> polyline = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_NOTES );
3713
3714 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3715 {
3716 if( token != T_LEFT )
3717 Expecting( T_LEFT );
3718
3719 token = NextTok();
3720
3721 switch( token )
3722 {
3723 case T_pts:
3724 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3725 {
3726 if( token != T_LEFT )
3727 Expecting( T_LEFT );
3728
3729 token = NextTok();
3730
3731 if( token != T_xy )
3732 Expecting( "xy" );
3733
3734 polyline->AddPoint( parseXY() );
3735
3736 NeedRIGHT();
3737 }
3738 break;
3739
3740 case T_stroke:
3741 parseStroke( stroke );
3742
3743 // In 6.0, the default schematic line style was Dashed.
3744 if( m_requiredVersion <= 20211123 && stroke.GetLineStyle() == LINE_STYLE::DEFAULT )
3745 stroke.SetLineStyle( LINE_STYLE::DASH );
3746
3747 polyline->SetStroke( stroke );
3748 break;
3749
3750 case T_fill:
3751 parseFill( fill );
3752 polyline->SetFillMode( fill.m_FillType );
3753 polyline->SetFillColor( fill.m_Color );
3754 break;
3755
3756 case T_uuid:
3757 NeedSYMBOL();
3758 const_cast<KIID&>( polyline->m_Uuid ) = parseKIID();
3759 NeedRIGHT();
3760 break;
3761
3762 default:
3763 Expecting( "pts, uuid, stroke, or fill" );
3764 }
3765 }
3766
3767 return polyline.release();
3768}
3769
3770
3772{
3773 // Note: T_polyline is deprecated in this code: it is now handled by
3774 // parseSchPolyLine() that can handle true polygons, and not only one segment.
3775
3776 T token;
3777 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3778 int layer;
3779
3780 switch( CurTok() )
3781 {
3782 case T_polyline: layer = LAYER_NOTES; break;
3783 case T_wire: layer = LAYER_WIRE; break;
3784 case T_bus: layer = LAYER_BUS; break;
3785 default:
3786 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as a line." );
3787 }
3788
3789 std::unique_ptr<SCH_LINE> line = std::make_unique<SCH_LINE>( VECTOR2I(), layer );
3790
3791 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3792 {
3793 if( token != T_LEFT )
3794 Expecting( T_LEFT );
3795
3796 token = NextTok();
3797
3798 switch( token )
3799 {
3800 case T_pts:
3801 NeedLEFT();
3802 token = NextTok();
3803
3804 if( token != T_xy )
3805 Expecting( "xy" );
3806
3807 line->SetStartPoint( parseXY() );
3808 NeedRIGHT();
3809 NeedLEFT();
3810 token = NextTok();
3811
3812 if( token != T_xy )
3813 Expecting( "xy" );
3814
3815 line->SetEndPoint( parseXY() );
3816 NeedRIGHT();
3817 NeedRIGHT();
3818 break;
3819
3820 case T_stroke:
3821 parseStroke( stroke );
3822 line->SetStroke( stroke );
3823 break;
3824
3825 case T_uuid:
3826 NeedSYMBOL();
3827 const_cast<KIID&>( line->m_Uuid ) = parseKIID();
3828 NeedRIGHT();
3829 break;
3830
3831 default:
3832 Expecting( "at, uuid or stroke" );
3833 }
3834 }
3835
3836 return line.release();
3837}
3838
3839
3841{
3842 wxCHECK_MSG( CurTok() == T_arc, nullptr,
3843 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc." ) );
3844
3845 T token;
3846 VECTOR2I startPoint;
3847 VECTOR2I midPoint;
3848 VECTOR2I endPoint;
3849 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3850 FILL_PARAMS fill;
3851 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC );
3852
3853 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3854 {
3855 if( token != T_LEFT )
3856 Expecting( T_LEFT );
3857
3858 token = NextTok();
3859
3860 switch( token )
3861 {
3862 case T_start:
3863 startPoint = parseXY();
3864 NeedRIGHT();
3865 break;
3866
3867 case T_mid:
3868 midPoint = parseXY();
3869 NeedRIGHT();
3870 break;
3871
3872 case T_end:
3873 endPoint = parseXY();
3874 NeedRIGHT();
3875 break;
3876
3877 case T_stroke:
3878 parseStroke( stroke );
3879 arc->SetStroke( stroke );
3880 break;
3881
3882 case T_fill:
3883 parseFill( fill );
3884 arc->SetFillMode( fill.m_FillType );
3885 arc->SetFillColor( fill.m_Color );
3886 break;
3887
3888 case T_uuid:
3889 NeedSYMBOL();
3890 const_cast<KIID&>( arc->m_Uuid ) = KIID( FromUTF8() );
3891 NeedRIGHT();
3892 break;
3893
3894 default:
3895 Expecting( "start, mid, end, stroke, fill or uuid" );
3896 }
3897 }
3898
3899 arc->SetArcGeometry( startPoint, midPoint, endPoint );
3900
3901 return arc.release();
3902}
3903
3904
3906{
3907 wxCHECK_MSG( CurTok() == T_circle, nullptr,
3908 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle." ) );
3909
3910 T token;
3911 VECTOR2I center;
3912 int radius = 0;
3913 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3914 FILL_PARAMS fill;
3915 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE );
3916
3917 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3918 {
3919 if( token != T_LEFT )
3920 Expecting( T_LEFT );
3921
3922 token = NextTok();
3923
3924 switch( token )
3925 {
3926 case T_center:
3927 center = parseXY();
3928 NeedRIGHT();
3929 break;
3930
3931 case T_radius:
3932 radius = parseInternalUnits( "radius length" );
3933 NeedRIGHT();
3934 break;
3935
3936 case T_stroke:
3937 parseStroke( stroke );
3938 circle->SetStroke( stroke );
3939 break;
3940
3941 case T_fill:
3942 parseFill( fill );
3943 circle->SetFillMode( fill.m_FillType );
3944 circle->SetFillColor( fill.m_Color );
3945 break;
3946
3947 case T_uuid:
3948 NeedSYMBOL();
3949 const_cast<KIID&>( circle->m_Uuid ) = KIID( FromUTF8() );
3950 NeedRIGHT();
3951 break;
3952
3953 default:
3954 Expecting( "center, radius, stroke, fill or uuid" );
3955 }
3956 }
3957
3958 circle->SetCenter( center );
3959 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
3960
3961 return circle.release();
3962}
3963
3964
3966{
3967 wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
3968 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle." ) );
3969
3970 T token;
3971 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
3972 FILL_PARAMS fill;
3973 std::unique_ptr<SCH_SHAPE> rectangle = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE );
3974
3975 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3976 {
3977 if( token != T_LEFT )
3978 Expecting( T_LEFT );
3979
3980 token = NextTok();
3981
3982 switch( token )
3983 {
3984 case T_start:
3985 rectangle->SetPosition( parseXY() );
3986 NeedRIGHT();
3987 break;
3988
3989 case T_end:
3990 rectangle->SetEnd( parseXY() );
3991 NeedRIGHT();
3992 break;
3993
3994 case T_stroke:
3995 parseStroke( stroke );
3996 rectangle->SetStroke( stroke );
3997 break;
3998
3999 case T_fill:
4000 parseFill( fill );
4001 rectangle->SetFillMode( fill.m_FillType );
4002 rectangle->SetFillColor( fill.m_Color );
4003 break;
4004
4005 case T_uuid:
4006 NeedSYMBOL();
4007 const_cast<KIID&>( rectangle->m_Uuid ) = KIID( FromUTF8() );
4008 NeedRIGHT();
4009 break;
4010
4011 default:
4012 Expecting( "start, end, stroke, fill or uuid" );
4013 }
4014 }
4015
4016 return rectangle.release();
4017}
4018
4019
4021{
4022 wxCHECK_MSG( CurTok() == T_rule_area, nullptr,
4023 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rule area." ) );
4024
4025 T token;
4026 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4027 FILL_PARAMS fill;
4028 std::unique_ptr<SCH_RULE_AREA> ruleArea = std::make_unique<SCH_RULE_AREA>();
4029
4030 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4031 {
4032 if( token != T_LEFT )
4033 Expecting( T_LEFT );
4034
4035 token = NextTok();
4036
4037 switch( token )
4038 {
4039 case T_polyline:
4040 {
4041 std::unique_ptr<SCH_SHAPE> poly( parseSchPolyLine() );
4042 SHAPE_POLY_SET& sch_rule_poly = poly->GetPolyShape();
4043
4044 // The polygon must be closed, it is a schematic closed polyline:
4045 sch_rule_poly.Outline(0).SetClosed( true );
4046
4047 ruleArea->SetPolyShape( sch_rule_poly );
4048
4049 ruleArea->SetStroke( poly->GetStroke() );
4050 ruleArea->SetFillMode( poly->GetFillMode() );
4051 ruleArea->SetFillColor( poly->GetFillColor() );
4052 break;
4053 }
4054 default:
4055 Expecting( "polyline" );
4056 }
4057 }
4058
4059 return ruleArea.release();
4060}
4061
4062
4064{
4065 wxCHECK_MSG( CurTok() == T_bezier, nullptr,
4066 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
4067
4068 T token;
4069 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4070 FILL_PARAMS fill;
4071 std::unique_ptr<SCH_SHAPE> bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER );
4072
4073 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4074 {
4075 if( token != T_LEFT )
4076 Expecting( T_LEFT );
4077
4078 token = NextTok();
4079
4080 switch( token )
4081 {
4082 case T_pts:
4083 {
4084 int ii = 0;
4085
4086 for( token = NextTok(); token != T_RIGHT; token = NextTok(), ++ii )
4087 {
4088 if( token != T_LEFT )
4089 Expecting( T_LEFT );
4090
4091 token = NextTok();
4092
4093 if( token != T_xy )
4094 Expecting( "xy" );
4095
4096 switch( ii )
4097 {
4098 case 0: bezier->SetStart( parseXY() ); break;
4099 case 1: bezier->SetBezierC1( parseXY() ); break;
4100 case 2: bezier->SetBezierC2( parseXY() ); break;
4101 case 3: bezier->SetEnd( parseXY() ); break;
4102 default: Unexpected( "control point" ); break;
4103 }
4104
4105 NeedRIGHT();
4106 }
4107 }
4108 break;
4109
4110 case T_stroke:
4111 parseStroke( stroke );
4112 bezier->SetStroke( stroke );
4113 break;
4114
4115 case T_fill:
4116 parseFill( fill );
4117 bezier->SetFillMode( fill.m_FillType );
4118 bezier->SetFillColor( fill.m_Color );
4119 break;
4120
4121 case T_uuid:
4122 NeedSYMBOL();
4123 const_cast<KIID&>( bezier->m_Uuid ) = KIID( FromUTF8() );
4124 NeedRIGHT();
4125 break;
4126
4127 default:
4128 Expecting( "pts, stroke, fill or uuid" );
4129 }
4130 }
4131
4132 return bezier.release();
4133}
4134
4135
4137{
4138 T token;
4139 std::unique_ptr<SCH_TEXT> text;
4140
4141 switch( CurTok() )
4142 {
4143 case T_text: text = std::make_unique<SCH_TEXT>(); break;
4144 case T_label: text = std::make_unique<SCH_LABEL>(); break;
4145 case T_global_label: text = std::make_unique<SCH_GLOBALLABEL>(); break;
4146 case T_hierarchical_label: text = std::make_unique<SCH_HIERLABEL>(); break;
4147 case T_netclass_flag: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
4148 case T_directive_label: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
4149 default:
4150 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as text." );
4151 }
4152
4153 // We'll reset this if we find a fields_autoplaced token
4154 text->ClearFieldsAutoplaced();
4155
4156 NeedSYMBOL();
4157
4158 text->SetText( FromUTF8() );
4159
4160 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4161 {
4162 if( token != T_LEFT )
4163 Expecting( T_LEFT );
4164
4165 token = NextTok();
4166
4167 switch( token )
4168 {
4169 case T_exclude_from_sim:
4170 text->SetExcludedFromSim( parseBool() );
4171 NeedRIGHT();
4172 break;
4173
4174 case T_at:
4175 text->SetPosition( parseXY() );
4176 text->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ).KeepUpright() );
4177
4178 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() ) )
4179 {
4180 switch( static_cast<int>( label->GetTextAngle().AsDegrees() ) )
4181 {
4182 case 0: label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
4183 case 90: label->SetSpinStyle( SPIN_STYLE::UP ); break;
4184 case 180: label->SetSpinStyle( SPIN_STYLE::LEFT ); break;
4185 case 270: label->SetSpinStyle( SPIN_STYLE::BOTTOM ); break;
4186 default: wxFAIL; label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
4187 }
4188 }
4189
4190 NeedRIGHT();
4191 break;
4192
4193 case T_shape:
4194 {
4195 if( text->Type() == SCH_TEXT_T || text->Type() == SCH_LABEL_T )
4196 Unexpected( T_shape );
4197
4198 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( text.get() );
4199
4200 token = NextTok();
4201
4202 switch( token )
4203 {
4204 case T_input: label->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
4205 case T_output: label->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
4206 case T_bidirectional: label->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
4207 case T_tri_state: label->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE ); break;
4208 case T_passive: label->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
4209 case T_dot: label->SetShape( LABEL_FLAG_SHAPE::F_DOT ); break;
4210 case T_round: label->SetShape( LABEL_FLAG_SHAPE::F_ROUND ); break;
4211 case T_diamond: label->SetShape( LABEL_FLAG_SHAPE::F_DIAMOND ); break;
4212 case T_rectangle: label->SetShape( LABEL_FLAG_SHAPE::F_RECTANGLE ); break;
4213 default:
4214 Expecting( "input, output, bidirectional, tri_state, passive, dot, round, diamond"
4215 "or rectangle" );
4216 }
4217
4218 NeedRIGHT();
4219 break;
4220 }
4221
4222 case T_length:
4223 {
4224 if( text->Type() != SCH_DIRECTIVE_LABEL_T )
4225 Unexpected( T_length );
4226
4227 SCH_DIRECTIVE_LABEL* label = static_cast<SCH_DIRECTIVE_LABEL*>( text.get() );
4228
4229 label->SetPinLength( parseInternalUnits( "pin length" ) );
4230 NeedRIGHT();
4231 }
4232 break;
4233
4234 case T_fields_autoplaced:
4235 if( parseMaybeAbsentBool( true ) )
4236 text->SetFieldsAutoplaced();
4237
4238 break;
4239
4240 case T_effects:
4241 parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ), true );
4242 break;
4243
4244 case T_iref: // legacy format; current is a T_property (aka SCH_FIELD)
4245 if( text->Type() == SCH_GLOBAL_LABEL_T )
4246 {
4247 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
4248 SCH_FIELD* field = &label->GetFields()[0];
4249
4250 field->SetTextPos( parseXY() );
4251 NeedRIGHT();
4252
4253 field->SetVisible( true );
4254 }
4255 break;
4256
4257 case T_uuid:
4258 NeedSYMBOL();
4259 const_cast<KIID&>( text->m_Uuid ) = parseKIID();
4260 NeedRIGHT();
4261 break;
4262
4263 case T_property:
4264 {
4265 if( text->Type() == SCH_TEXT_T )
4266 Unexpected( T_property );
4267
4268 SCH_FIELD* field = parseSchField( text.get() );
4269
4270 // If the field is a Intersheetrefs it is not handled like other fields:
4271 // It always exists and is the first in list
4272 if( text->Type() == SCH_GLOBAL_LABEL_T
4273 && ( field->GetInternalName() == wxT( "Intersheet References" ) // old name in V6.0
4274 || field->GetInternalName() == wxT( "Intersheetrefs" ) ) ) // Current name
4275 {
4276 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
4277 // Ensure the Id of this special and first field is 0, needed by
4278 // SCH_FIELD::IsHypertext() test
4279 field->SetId( 0 );
4280
4281 label->GetFields()[0] = *field;
4282 }
4283 else
4284 {
4285 static_cast<SCH_LABEL_BASE*>( text.get() )->GetFields().emplace_back( *field );
4286 }
4287
4288 delete field;
4289 break;
4290 }
4291
4292 default:
4293 Expecting( "at, shape, iref, uuid or effects" );
4294 }
4295 }
4296
4297 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() );
4298
4299 if( label && label->GetFields().empty() )
4300 label->SetFieldsAutoplaced();
4301
4302 return text.release();
4303}
4304
4305
4307{
4308 wxCHECK_MSG( CurTok() == T_text_box, nullptr,
4309 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text box." ) );
4310
4311 std::unique_ptr<SCH_TEXTBOX> textBox = std::make_unique<SCH_TEXTBOX>();
4312
4313 parseSchTextBoxContent( textBox.get() );
4314
4315 return textBox.release();
4316}
4317
4318
4320{
4321 wxCHECK_MSG( CurTok() == T_table_cell, nullptr,
4322 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table cell." ) );
4323
4324 std::unique_ptr<SCH_TABLECELL> cell = std::make_unique<SCH_TABLECELL>();
4325
4326 parseSchTextBoxContent( cell.get() );
4327
4328 return cell.release();
4329}
4330
4331
4333{
4334 T token;
4335 VECTOR2I pos;
4336 VECTOR2I end;
4337 VECTOR2I size;
4338 int left;
4339 int top;
4340 int right;
4341 int bottom;
4342 STROKE_PARAMS stroke( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ), LINE_STYLE::DEFAULT );
4343 FILL_PARAMS fill;
4344 bool foundEnd = false;
4345 bool foundSize = false;
4346 bool foundMargins = false;
4347
4348 NeedSYMBOL();
4349
4350 aTextBox->SetText( FromUTF8() );
4351
4352 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4353 {
4354 if( token != T_LEFT )
4355 Expecting( T_LEFT );
4356
4357 token = NextTok();
4358
4359 switch( token )
4360 {
4361 case T_exclude_from_sim:
4362 aTextBox->SetExcludedFromSim( parseBool() );
4363 NeedRIGHT();
4364 break;
4365
4366 case T_start: // Legacy token during 6.99 development; fails to handle angle
4367 pos = parseXY();
4368 NeedRIGHT();
4369 break;
4370
4371 case T_end: // Legacy token during 6.99 development; fails to handle angle
4372 end = parseXY();
4373 foundEnd = true;
4374 NeedRIGHT();
4375 break;
4376
4377 case T_at:
4378 pos = parseXY();
4379 aTextBox->SetTextAngle( EDA_ANGLE( parseDouble( "textbox angle" ), DEGREES_T ) );
4380 NeedRIGHT();
4381 break;
4382
4383 case T_size:
4384 size = parseXY();
4385 foundSize = true;
4386 NeedRIGHT();
4387 break;
4388
4389 case T_span:
4390 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( aTextBox ) )
4391 {
4392 cell->SetColSpan( parseInt( "column span" ) );
4393 cell->SetRowSpan( parseInt( "row span" ) );
4394 }
4395 else
4396 {
4397 Expecting( "at, size, stroke, fill, effects or uuid" );
4398 }
4399
4400 NeedRIGHT();
4401 break;
4402
4403 case T_stroke:
4404 parseStroke( stroke );
4405 aTextBox->SetStroke( stroke );
4406 break;
4407
4408 case T_fill:
4409 parseFill( fill );
4410 aTextBox->SetFillMode( fill.m_FillType );
4411 aTextBox->SetFillColor( fill.m_Color );
4412 break;
4413
4414 case T_margins:
4415 parseMargins( left, top, right, bottom );
4416 aTextBox->SetMarginLeft( left );
4417 aTextBox->SetMarginTop( top );
4418 aTextBox->SetMarginRight( right );
4419 aTextBox->SetMarginBottom( bottom );
4420 foundMargins = true;
4421 NeedRIGHT();
4422 break;
4423
4424 case T_effects:
4425 parseEDA_TEXT( static_cast<EDA_TEXT*>( aTextBox ), false );
4426 break;
4427
4428 case T_uuid:
4429 NeedSYMBOL();
4430 const_cast<KIID&>( aTextBox->m_Uuid ) = KIID( FromUTF8() );
4431 NeedRIGHT();
4432 break;
4433
4434 default:
4435 if( dynamic_cast<SCH_TABLECELL*>( aTextBox ) != nullptr )
4436 Expecting( "at, size, stroke, fill, effects, span or uuid" );
4437 else
4438 Expecting( "at, size, stroke, fill, effects or uuid" );
4439 }
4440 }
4441
4442 aTextBox->SetPosition( pos );
4443
4444 if( foundEnd )
4445 aTextBox->SetEnd( end );
4446 else if( foundSize )
4447 aTextBox->SetEnd( pos + size );
4448 else
4449 Expecting( "size" );
4450
4451 if( !foundMargins )
4452 {
4453 int margin = aTextBox->GetLegacyTextMargin();
4454 aTextBox->SetMarginLeft( margin );
4455 aTextBox->SetMarginTop( margin );
4456 aTextBox->SetMarginRight( margin );
4457 aTextBox->SetMarginBottom( margin );
4458 }
4459}
4460
4461
4463{
4464 wxCHECK_MSG( CurTok() == T_table, nullptr,
4465 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table." ) );
4466
4467 T token;
4468 int defaultLineWidth = schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
4469 STROKE_PARAMS borderStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
4470 STROKE_PARAMS separatorsStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
4471 std::unique_ptr<SCH_TABLE> table = std::make_unique<SCH_TABLE>( defaultLineWidth );
4472
4473 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4474 {
4475 if( token != T_LEFT )
4476 Expecting( T_LEFT );
4477
4478 token = NextTok();
4479
4480 switch( token )
4481 {
4482 case T_column_count:
4483 table->SetColCount( parseInt( "column count" ) );
4484 NeedRIGHT();
4485 break;
4486
4487 case T_column_widths:
4488 {
4489 int col = 0;
4490
4491 while( ( token = NextTok() ) != T_RIGHT )
4492 table->SetColWidth( col++, parseInternalUnits() );
4493
4494 break;
4495 }
4496
4497 case T_row_heights:
4498 {
4499 int row = 0;
4500
4501 while( ( token = NextTok() ) != T_RIGHT )
4502 table->SetRowHeight( row++, parseInternalUnits() );
4503
4504 break;
4505 }
4506
4507 case T_cells:
4508 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4509 {
4510 if( token != T_LEFT )
4511 Expecting( T_LEFT );
4512
4513 token = NextTok();
4514
4515 if( token != T_table_cell )
4516 Expecting( "table_cell" );
4517
4518 table->AddCell( parseSchTableCell() );
4519 }
4520
4521 break;
4522
4523 case T_border:
4524 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4525 {
4526 if( token != T_LEFT )
4527 Expecting( T_LEFT );
4528
4529 token = NextTok();
4530
4531 switch( token )
4532 {
4533 case T_external:
4534 table->SetStrokeExternal( parseBool() );
4535 NeedRIGHT();
4536 break;
4537
4538 case T_header:
4539 table->SetStrokeHeader( parseBool() );
4540 NeedRIGHT();
4541 break;
4542
4543 case T_stroke:
4544 parseStroke( borderStroke );
4545 table->SetBorderStroke( borderStroke );
4546 break;
4547
4548 default:
4549 Expecting( "external, header or stroke" );
4550 break;
4551 }
4552 }
4553
4554 break;
4555
4556 case T_separators:
4557 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4558 {
4559 if( token != T_LEFT )
4560 Expecting( T_LEFT );
4561
4562 token = NextTok();
4563
4564 switch( token )
4565 {
4566 case T_rows:
4567 table->SetStrokeRows( parseBool() );
4568 NeedRIGHT();
4569 break;
4570
4571 case T_cols:
4572 table->SetStrokeColumns( parseBool() );
4573 NeedRIGHT();
4574 break;
4575
4576 case T_stroke:
4577 parseStroke( separatorsStroke );
4578 table->SetSeparatorsStroke( separatorsStroke );
4579 break;
4580
4581 default:
4582 Expecting( "rows, cols, or stroke" );
4583 break;
4584 }
4585 }
4586
4587 break;
4588
4589 default:
4590 Expecting( "columns, col_widths, row_heights, border, separators, header or cells" );
4591 }
4592 }
4593
4594 return table.release();
4595}
4596
4597
4599{
4600 wxCHECK_RET( CurTok() == T_bus_alias,
4601 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus alias." ) );
4602 wxCHECK( aScreen, /* void */ );
4603
4604 T token;
4605 std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>( aScreen );
4606 wxString alias;
4607 wxString member;
4608
4609 NeedSYMBOL();
4610
4611 alias = FromUTF8();
4612
4613 if( m_requiredVersion < 20210621 )
4614 alias = ConvertToNewOverbarNotation( alias );
4615
4616 busAlias->SetName( alias );
4617
4618 NeedLEFT();
4619 token = NextTok();
4620
4621 if( token != T_members )
4622 Expecting( "members" );
4623
4624 token = NextTok();
4625
4626 while( token != T_RIGHT )
4627 {
4628 if( !IsSymbol( token ) )
4629 Expecting( "quoted string" );
4630
4631 member = FromUTF8();
4632
4633 if( m_requiredVersion < 20210621 )
4634 member = ConvertToNewOverbarNotation( member );
4635
4636 busAlias->Members().emplace_back( member );
4637
4638 token = NextTok();
4639 }
4640
4641 NeedRIGHT();
4642
4643 aScreen->AddBusAlias( busAlias );
4644}
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:1229
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:105
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:86
@ SYM_MIRROR_X
Definition: sch_symbol.h:85
#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:520
@ 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)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:121
constexpr T Clamp(const T &lower, const T &value, const T &upper)
Limit value within the range lower <= value <= upper.
Definition: util.h:65
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:673