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