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 The 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
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
26
27// For some reason wxWidgets is built with wxUSE_BASE64 unset so expose the wxWidgets
28// base64 code.
29#include <charconv>
30
31#include <fmt/format.h>
32#define wxUSE_BASE64 1
33#include <wx/base64.h>
34#include <wx/log.h>
35#include <wx/mstream.h>
36#include <wx/tokenzr.h>
37
38#include <base_units.h>
39#include <bitmap_base.h>
41#include <lib_id.h>
42#include <sch_pin.h>
43#include <math/util.h> // KiROUND
44#include <font/font.h>
45#include <font/fontconfig.h>
46#include <pgm_base.h>
47#include <string_utils.h>
48#include <sch_bitmap.h>
49#include <sch_bus_entry.h>
50#include <sch_symbol.h>
51#include <sch_netchain.h>
52#include <sch_edit_frame.h> // SYM_ORIENT_XXX
53#include <sch_field.h>
54#include <sch_group.h>
55#include <sch_line.h>
56#include <sch_rule_area.h>
57#include <sch_textbox.h>
58#include <sch_table.h>
59#include <sch_tablecell.h>
60#include <sch_label.h>
61#include <sch_junction.h>
62#include <sch_no_connect.h>
63#include <sch_screen.h>
64#include <sch_shape.h>
65#include <sch_sheet_pin.h>
68#include <template_fieldnames.h>
69#include <trigo.h>
70#include <progress_reporter.h>
71#include <sim/sim_model.h>
72
73
74using namespace TSCHEMATIC_T;
75
76
78 PROGRESS_REPORTER* aProgressReporter,
79 unsigned aLineCount, SCH_SHEET* aRootSheet,
80 bool aIsAppending ) :
81 SCHEMATIC_LEXER( aLineReader ),
83 m_unit( 1 ),
84 m_bodyStyle( 1 ),
85 m_appending( aIsAppending ),
86 m_progressReporter( aProgressReporter ),
87 m_lineReader( aLineReader ),
89 m_lineCount( aLineCount ),
90 m_rootSheet( aRootSheet ),
92{
93}
94
95
97{
99 {
100 unsigned progressDelta = std::max( 50u, m_lineCount / 10 );
101 unsigned curLine = m_lineReader->LineNumber();
102
103 if( m_lastProgressLine == 0 || curLine > m_lastProgressLine + progressDelta )
104 {
105 m_progressReporter->SetCurrentProgress( ( (double) curLine )
106 / std::max( 1U, m_lineCount ) );
107
108 if( !m_progressReporter->KeepRefreshing() )
109 THROW_IO_ERROR( _( "Open canceled by user." ) );
110
111 m_lastProgressLine = curLine;
112 }
113 }
114}
115
116
118{
119 KIID id( FromUTF8() );
120
121 while( m_uuids.count( id ) )
122 id.Increment();
123
124 m_uuids.insert( id );
125
126 return id;
127}
128
129
131{
132 T token = NextTok();
133
134 if( token == T_yes )
135 return true;
136 else if( token == T_no )
137 return false;
138 else
139 Expecting( "yes or no" );
140
141 return false;
142}
143
144
145/*
146 * e.g. "hide", "hide)", "(hide yes)"
147 */
149{
150 bool ret = aDefaultValue;
151
152 if( PrevTok() == T_LEFT )
153 {
154 T token = NextTok();
155
156 // "hide)"
157 if( static_cast<int>( token ) == DSN_RIGHT )
158 return aDefaultValue;
159
160 if( token == T_yes )
161 ret = true;
162 else if( token == T_no )
163 ret = false;
164 else
165 Expecting( "yes or no" );
166
167 NeedRIGHT();
168 }
169 else
170 {
171 // "hide"
172 return aDefaultValue;
173 }
174
175 return ret;
176}
177
178
180{
181 T token;
182
183 NeedLEFT();
184 NextTok();
185 parseHeader( T_kicad_symbol_lib, SEXPR_SYMBOL_LIB_FILE_VERSION );
186
187 // Prior to this, bar was a valid string char for unquoted strings.
188 SetKnowsBar( m_requiredVersion >= 20240529 );
189
190 bool versionChecked = false;
191
192 auto checkVersion =
193 [&]()
194 {
195 if( !versionChecked && m_requiredVersion > SEXPR_SYMBOL_LIB_FILE_VERSION )
196 {
197 throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
199 }
200
201 versionChecked = true;
202 };
203
204 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
205 {
206 if( token != T_LEFT )
207 Expecting( T_LEFT );
208
209 token = NextTok();
210
211 switch( token )
212 {
213 case T_generator:
214 // (generator "genname"); we don't care about it at the moment.
215 NeedSYMBOL();
216 NeedRIGHT();
217 break;
218
219 case T_host:
220 {
221 // (host eeschema ["5.99"]); old version of generator token
222 NeedSYMBOL();
223
224 // Really old versions also included a host version
225 if( m_requiredVersion < 20200827 )
226 NeedSYMBOL();
227
228 NeedRIGHT();
229 break;
230 }
231
232 case T_generator_version:
233 {
234 NextTok();
235 m_generatorVersion = FromUTF8();
236 NeedRIGHT();
237
238 // If the format includes a generator version, by this point we have enough info to
239 // do the version check here
240 checkVersion();
241 break;
242 }
243
244 case T_symbol:
245 {
246 // By the time we get to the first symbol, we can check the version
247 checkVersion();
248
249 m_unit = 1;
250 m_bodyStyle = 1;
251
252 try
253 {
254 LIB_SYMBOL* symbol = parseLibSymbol( aSymbolLibMap );
255 aSymbolLibMap[symbol->GetName()] = symbol;
256 }
257 catch( const IO_ERROR& e )
258 {
259 // Record the error and skip to the end of this symbol block
260 wxString warning = wxString::Format(
261 _( "Error parsing symbol at line %d: %s\nSkipping symbol and continuing." ),
262 CurLineNumber(), e.What() );
263 m_parseWarnings.push_back( warning );
264
265 // Skip to the end of this symbol's S-expression block
266 // We're already past T_symbol, so we're inside the symbol definition
267 skipToBlockEnd( 1 );
268 }
269
270 break;
271 }
272
273 default:
274 Expecting( "symbol, generator, or generator_version" );
275 }
276 }
277}
278
279
281 int aFileVersion )
282{
283 LIB_SYMBOL* newSymbol = nullptr;
284
285 NextTok();
286
287 // If there actually isn't anything here, don't throw just return a nullptr
288 if( CurTok() == T_LEFT )
289 {
290 NextTok();
291
292 if( CurTok() == T_symbol )
293 {
294 m_requiredVersion = aFileVersion;
295 newSymbol = parseLibSymbol( aSymbolLibMap );
296
297 const std::vector<wxString>* embeddedFonts =
298 newSymbol->GetEmbeddedFiles()->UpdateFontFiles();
299
300 newSymbol->RunOnChildren(
301 [&]( SCH_ITEM* aChild )
302 {
303 if( EDA_TEXT* textItem = dynamic_cast<EDA_TEXT*>( aChild ) )
304 textItem->ResolveFont( embeddedFonts );
305 },
307 }
308 else
309 {
310 wxString msg = wxString::Format( _( "Cannot parse %s as a symbol" ),
311 GetTokenString( CurTok() ) );
312 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
313 }
314 }
315
316 return newSymbol;
317}
318
319
321{
322 wxCHECK_MSG( CurTok() == T_symbol, nullptr,
323 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
324
325 T token;
326 long tmp;
327 wxString name;
328 wxString error;
329 SCH_ITEM* item;
330 std::unique_ptr<LIB_SYMBOL> symbol = std::make_unique<LIB_SYMBOL>( wxEmptyString );
331
332 symbol->SetUnitCount( 1, true );
333
334 token = NextTok();
335
336 if( !IsSymbol( token ) )
337 THROW_PARSE_ERROR( _( "Invalid symbol name" ), CurSource(), CurLine(), CurLineNumber(), CurOffset() );
338
339 name = FromUTF8();
340
341 // Some symbol LIB_IDs have the '/' character escaped which can break derived symbol links.
342 // The '/' character is no longer an illegal LIB_ID character so it doesn't need to be
343 // escaped.
344 name.Replace( wxS( "{slash}" ), wxT( "/" ) );
345
346 LIB_ID id;
347 int bad_pos = id.Parse( name );
348
349 if( bad_pos >= 0 )
350 {
351 if( static_cast<int>( name.size() ) > bad_pos )
352 {
353 wxString msg = wxString::Format( _( "Symbol %s contains invalid character '%c'" ),
354 name,
355 name[bad_pos] );
356
357 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
358 }
359
360
361 THROW_PARSE_ERROR( _( "Invalid library identifier" ), CurSource(), CurLine(),
362 CurLineNumber(), CurOffset() );
363 }
364
365 m_symbolName = id.GetLibItemName().wx_str();
366 symbol->SetName( m_symbolName );
367 symbol->SetLibId( id );
368
369 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
370 {
371 if( token != T_LEFT )
372 Expecting( T_LEFT );
373
374 token = NextTok();
375
376 switch( token )
377 {
378 case T_power:
379 symbol->SetGlobalPower();
380 token = NextTok();
381
382 if( token == T_RIGHT )
383 break;
384
385 if( token == T_local )
386 symbol->SetLocalPower();
387 else if( token != T_global )
388 Expecting( "global or local" );
389
390 NeedRIGHT();
391 break;
392
393 case T_body_styles:
394 parseBodyStyles( symbol );
395 break;
396
397 case T_pin_names:
398 parsePinNames( symbol );
399 break;
400
401 case T_pin_numbers:
402 parsePinNumbers( symbol );
403 break;
404
405 case T_associated_footprints: parseAssociatedFootprints( symbol ); break;
406
407 case T_pin_maps: parsePinMaps( symbol ); break;
408
409 case T_exclude_from_sim:
410 symbol->SetExcludedFromSim( parseBool() );
411 NeedRIGHT();
412 break;
413
414 case T_in_bom:
415 symbol->SetExcludedFromBOM( !parseBool() );
416 NeedRIGHT();
417 break;
418
419 case T_on_board:
420 symbol->SetExcludedFromBoard( !parseBool() );
421 NeedRIGHT();
422 break;
423
424 case T_in_pos_files:
425 symbol->SetExcludedFromPosFiles( !parseBool() );
426 NeedRIGHT();
427 break;
428
429 case T_duplicate_pin_numbers_are_jumpers:
430 symbol->SetDuplicatePinNumbersAreJumpers( parseBool() );
431 NeedRIGHT();
432 break;
433
434 case T_jumper_pin_groups:
435 {
436 std::vector<std::set<wxString>>& groups = symbol->JumperPinGroups();
437 std::set<wxString>* currentGroup = nullptr;
438
439 for( token = NextTok(); currentGroup || token != T_RIGHT; token = NextTok() )
440 {
441 switch( static_cast<int>( token ) )
442 {
443 case T_LEFT:
444 currentGroup = &groups.emplace_back();
445 break;
446
447 case DSN_STRING:
448 currentGroup->insert( FromUTF8() );
449 break;
450
451 case T_RIGHT:
452 currentGroup = nullptr;
453 break;
454
455 default:
456 Expecting( "list of pin names" );
457 }
458 }
459
460 break;
461 }
462
463 case T_property:
464 parseProperty( symbol );
465 break;
466
467 case T_extends:
468 {
469 token = NextTok();
470
471 if( !IsSymbol( token ) )
472 {
473 THROW_PARSE_ERROR( _( "Invalid parent symbol name" ), CurSource(), CurLine(),
474 CurLineNumber(), CurOffset() );
475 }
476
477 name = FromUTF8();
478
479 // Some symbol LIB_IDs have the '/' character escaped which can break derived
480 // symbol links. The '/' character is no longer an illegal LIB_ID character so
481 // it doesn't need to be escaped.
482 name.Replace( wxS( "{slash}" ), wxT( "/" ) );
483
484 symbol->SetParentName( name );
485 NeedRIGHT();
486 break;
487 }
488
489 case T_symbol:
490 {
491 token = NextTok();
492
493 if( !IsSymbol( token ) )
494 {
495 THROW_PARSE_ERROR( _( "Invalid symbol unit name" ), CurSource(), CurLine(),
496 CurLineNumber(), CurOffset() );
497 }
498
499 name = FromUTF8();
500
501 // Some symbol LIB_IDs have the '/' character escaped which can break derived
502 // symbol links. The '/' character is no longer an illegal LIB_ID character so
503 // it doesn't need to be escaped.
504 name.Replace( wxS( "{slash}" ), wxT( "/" ) );
505
506 if( !name.StartsWith( m_symbolName ) )
507 {
508 error.Printf( _( "Invalid symbol unit name prefix %s" ), name.c_str() );
509 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
510 }
511
512 name = name.Right( name.Length() - m_symbolName.Length() - 1 );
513
514 wxStringTokenizer tokenizer( name, "_" );
515
516 if( tokenizer.CountTokens() != 2 )
517 {
518 error.Printf( _( "Invalid symbol unit name suffix %s" ), name.c_str() );
519 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
520 }
521
522 if( !tokenizer.GetNextToken().ToLong( &tmp ) )
523 {
524 error.Printf( _( "Invalid symbol unit number %s" ), name.c_str() );
525 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
526 }
527
528 m_unit = static_cast<int>( tmp );
529
530 if( !tokenizer.GetNextToken().ToLong( &tmp ) )
531 {
532 error.Printf( _( "Invalid symbol body style number %s" ), name.c_str() );
533 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
534 }
535
536 m_bodyStyle = static_cast<int>( tmp );
537
538 if( m_bodyStyle > symbol->GetBodyStyleCount() )
539 symbol->SetBodyStyleCount( m_bodyStyle, false, false );
540
541 if( m_unit > symbol->GetUnitCount() )
542 symbol->SetUnitCount( m_unit, false );
543
544 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
545 {
546 if( token != T_LEFT )
547 Expecting( T_LEFT );
548
549 token = NextTok();
550
551 switch( token )
552 {
553 case T_unit_name:
554 token = NextTok();
555
556 if( IsSymbol( token ) )
557 symbol->GetUnitDisplayNames()[m_unit] = FromUTF8();
558
559 NeedRIGHT();
560 break;
561
562 case T_arc:
563 case T_bezier:
564 case T_circle:
565 case T_ellipse:
566 case T_ellipse_arc:
567 case T_pin:
568 case T_polyline:
569 case T_rectangle:
570 case T_text:
571 case T_text_box:
572 item = ParseSymbolDrawItem();
573
574 wxCHECK_MSG( item, nullptr, "Invalid draw item pointer." );
575
576 item->SetParent( symbol.get() );
577 symbol->AddDrawItem( item, false );
578 break;
579
580 default:
581 Expecting( "arc, bezier, circle, ellipse, ellipse_arc, pin, polyline, "
582 "rectangle, or text" );
583 };
584 }
585
586 m_unit = 1;
587 m_bodyStyle = 1;
588 break;
589 }
590
591 case T_arc:
592 case T_bezier:
593 case T_circle:
594 case T_ellipse:
595 case T_ellipse_arc:
596 case T_pin:
597 case T_polyline:
598 case T_rectangle:
599 case T_text:
600 case T_text_box:
601 item = ParseSymbolDrawItem();
602
603 wxCHECK_MSG( item, nullptr, "Invalid draw item pointer." );
604
605 item->SetParent( symbol.get() );
606 symbol->AddDrawItem( item, false );
607 break;
608
609 case T_embedded_fonts:
610 {
611 symbol->SetAreFontsEmbedded( parseBool() );
612 NeedRIGHT();
613 break;
614 }
615
616 case T_embedded_files:
617 {
618 EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
619 embeddedFilesParser.SyncLineReaderWith( *this );
620
621 try
622 {
623 embeddedFilesParser.ParseEmbedded( symbol->GetEmbeddedFiles() );
624 }
625 catch( const IO_ERROR& e )
626 {
627 m_parseWarnings.push_back( e.What() );
628
629 int depth = 0;
630
631 for( int tok = embeddedFilesParser.NextTok();
632 tok != DSN_EOF;
633 tok = embeddedFilesParser.NextTok() )
634 {
635 if( tok == DSN_LEFT )
636 depth++;
637 else if( tok == DSN_RIGHT && --depth < 0 )
638 break;
639 }
640 }
641
642 SyncLineReaderWith( embeddedFilesParser );
643 break;
644 }
645
646 default:
647 Expecting( "pin_names, pin_numbers, arc, bezier, circle, ellipse, ellipse_arc, "
648 "pin, polyline, rectangle, or text" );
649 }
650 }
651
652 symbol->GetDrawItems().sort();
653 m_symbolName.clear();
654
655 const std::vector<wxString>* embeddedFonts = symbol->GetEmbeddedFiles()->UpdateFontFiles();
656
657 symbol->RunOnChildren(
658 [&]( SCH_ITEM* aChild )
659 {
660 if( EDA_TEXT* textItem = dynamic_cast<EDA_TEXT*>( aChild ) )
661 textItem->ResolveFont( embeddedFonts );
662 },
664
665 // Before V10 we didn't store the number of body styles in a symbol, we just looked at all its
666 // drawings each time we wanted to know.
667 if( m_requiredVersion < 20250827 )
668 symbol->SetHasDeMorganBodyStyles( symbol->HasLegacyAlternateBodyStyle() );
669
670 symbol->RefreshLibraryTreeCaches();
671
672 return symbol.release();
673}
674
675
677{
678 switch( CurTok() )
679 {
680 case T_arc: return parseSymbolArc(); break;
681 case T_bezier: return parseSymbolBezier(); break;
682 case T_circle: return parseSymbolCircle(); break;
683 case T_ellipse: return parseSymbolEllipse(); break;
684 case T_ellipse_arc: return parseSymbolEllipseArc(); break;
685 case T_pin: return parseSymbolPin(); break;
686 case T_polyline: return parseSymbolPolyLine(); break;
687 case T_rectangle: return parseSymbolRectangle(); break;
688 case T_text: return parseSymbolText(); break;
689 case T_text_box: return parseSymbolTextBox(); break;
690 default:
691 Expecting( "arc, bezier, circle, ellipse, ellipse_arc, pin, "
692 "polyline, rectangle, or text" );
693 }
694
695 return nullptr;
696}
697
698
700{
701 auto retval = parseDouble() * schIUScale.IU_PER_MM;
702
703 // Schematic internal units are represented as integers. Any values that are
704 // larger or smaller than the schematic units represent undefined behavior for
705 // the system. Limit values to the largest that can be displayed on the screen.
706 constexpr double int_limit = std::numeric_limits<int>::max() * 0.7071; // 0.7071 = roughly 1/sqrt(2)
707
708 return KiROUND( std::clamp( retval, -int_limit, int_limit ) );
709}
710
711
713{
714 auto retval = parseDouble( aExpected ) * schIUScale.IU_PER_MM;
715
716 constexpr double int_limit = std::numeric_limits<int>::max() * 0.7071;
717
718 return KiROUND( std::clamp( retval, -int_limit, int_limit ) );
719}
720
721
723{
724 STROKE_PARAMS_PARSER strokeParser( reader, schIUScale.IU_PER_MM );
725 strokeParser.SyncLineReaderWith( *this );
726
727 strokeParser.ParseStroke( aStroke );
728 SyncLineReaderWith( strokeParser );
729}
730
731
733{
734 wxCHECK_RET( CurTok() == T_fill, "Cannot parse " + GetTokenString( CurTok() ) + " as a fill." );
735
738
739 T token;
740
741 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
742 {
743 if( token != T_LEFT )
744 Expecting( T_LEFT );
745
746 token = NextTok();
747
748 switch( token )
749 {
750 case T_type:
751 {
752 token = NextTok();
753
754 switch( token )
755 {
756 case T_none: aFill.m_FillType = FILL_T::NO_FILL; break;
757 case T_outline: aFill.m_FillType = FILL_T::FILLED_SHAPE; break;
758 case T_background: aFill.m_FillType = FILL_T::FILLED_WITH_BG_BODYCOLOR; break;
759 case T_color: aFill.m_FillType = FILL_T::FILLED_WITH_COLOR; break;
760 case T_hatch: aFill.m_FillType = FILL_T::HATCH; break;
761 case T_reverse_hatch: aFill.m_FillType = FILL_T::REVERSE_HATCH; break;
762 case T_cross_hatch: aFill.m_FillType = FILL_T::CROSS_HATCH; break;
763 default: Expecting( "none, outline, hatch, reverse_hatch, "
764 "cross_hatch, color or background" );
765 }
766
767 NeedRIGHT();
768 break;
769 }
770
771 case T_color:
772 {
773 COLOR4D color;
774
775 color.r = parseInt( "red" ) / 255.0;
776 color.g = parseInt( "green" ) / 255.0;
777 color.b = parseInt( "blue" ) / 255.0;
778 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
779 aFill.m_Color = color;
780 NeedRIGHT();
781 break;
782 }
783
784 default:
785 Expecting( "type or color" );
786 }
787 }
788}
789
790
791void SCH_IO_KICAD_SEXPR_PARSER::parseEDA_TEXT( EDA_TEXT* aText, bool aConvertOverbarSyntax,
792 bool aEnforceMinTextSize )
793{
794 wxCHECK_RET( aText && ( CurTok() == T_effects || CurTok() == T_href ),
795 "Cannot parse " + GetTokenString( CurTok() ) + " as an EDA_TEXT." );
796
797 // In version 20210606 the notation for overbars was changed from `~...~` to `~{...}`.
798 // We need to convert the old syntax to the new one.
799 if( aConvertOverbarSyntax && m_requiredVersion < 20210606 )
800 aText->SetText( ConvertToNewOverbarNotation( aText->GetText() ) );
801
802 T token;
803 bool bold = false;
804 bool italic = false;
806
807 // Various text objects (text boxes, schematic text, etc.) all have their own defaults,
808 // but the file format default is {center,center} so we have to set that before parsing.
811
812 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
813 {
814 if( token == T_LEFT )
815 token = NextTok();
816
817 switch( token )
818 {
819 case T_font:
820 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
821 {
822 if( token == T_LEFT )
823 token = NextTok();
824
825 switch( token )
826 {
827 case T_face:
828 NeedSYMBOL();
829 aText->SetUnresolvedFontName( FromUTF8() );
830 NeedRIGHT();
831 break;
832
833 case T_size:
834 {
835 VECTOR2I sz;
836 sz.y = parseInternalUnits( "text height" );
837 sz.x = parseInternalUnits( "text width" );
838 aText->SetTextSize( sz, aEnforceMinTextSize );
839 NeedRIGHT();
840 break;
841 }
842
843 case T_thickness:
844 aText->SetTextThickness( parseInternalUnits( "text thickness" ) );
845 NeedRIGHT();
846 break;
847
848 case T_bold:
849 bold = parseMaybeAbsentBool( true );
850 aText->SetBoldFlag( bold );
851 break;
852
853 case T_italic:
854 italic = parseMaybeAbsentBool( true );
855 aText->SetItalicFlag( italic );
856 break;
857
858 case T_color:
859 color.r = parseInt( "red" ) / 255.0;
860 color.g = parseInt( "green" ) / 255.0;
861 color.b = parseInt( "blue" ) / 255.0;
862 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
863 aText->SetTextColor( color );
864 NeedRIGHT();
865 break;
866
867 case T_line_spacing:
868 aText->SetLineSpacing( parseDouble( "line spacing" ) );
869 NeedRIGHT();
870 break;
871
872 default:
873 Expecting( "face, size, thickness, line_spacing, bold, or italic" );
874 }
875 }
876
877 break;
878
879 case T_justify:
880 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
881 {
882 switch( token )
883 {
884 case T_left: aText->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT ); break;
885 case T_right: aText->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT ); break;
886 case T_top: aText->SetVertJustify( GR_TEXT_V_ALIGN_TOP ); break;
887 case T_bottom: aText->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM ); break;
888 // Do not set mirror property for schematic text elements
889 case T_mirror: break;
890 default: Expecting( "left, right, top, bottom, or mirror" );
891 }
892 }
893
894 break;
895
896 case T_href:
897 {
898 NeedSYMBOL();
899 wxString hyperlink = FromUTF8();
900
901 if( !EDA_TEXT::ValidateHyperlink( hyperlink ) )
902 {
903 THROW_PARSE_ERROR( wxString::Format( _( "Invalid hyperlink url '%s'" ), hyperlink ),
904 CurSource(), CurLine(), CurLineNumber(), CurOffset() );
905 }
906 else
907 {
908 aText->SetHyperlink( hyperlink );
909 }
910
911 NeedRIGHT();
912 }
913 break;
914
915 case T_hide:
916 {
917 bool hide = parseMaybeAbsentBool( true );
918 aText->SetVisible( !hide );
919 break;
920 }
921
922 default:
923 Expecting( "font, justify, hide or href" );
924 }
925 }
926}
927
928
929void SCH_IO_KICAD_SEXPR_PARSER::parseHeader( TSCHEMATIC_T::T aHeaderType, int aFileVersion )
930{
931 if( CurTok() != aHeaderType )
932 {
933 THROW_PARSE_ERROR( wxString::Format( _( "Cannot parse '%s' as a header." ),
934 GetTokenString( CurTok() ) ),
935 CurSource(), CurLine(), CurLineNumber(), CurOffset() );
936 }
937
938 NeedLEFT();
939
940 T tok = NextTok();
941
942 if( tok == T_version )
943 {
944 m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
945 NeedRIGHT();
946 }
947 else
948 {
949 m_requiredVersion = aFileVersion;
950 }
951}
952
953
954void SCH_IO_KICAD_SEXPR_PARSER::parseBodyStyles( std::unique_ptr<LIB_SYMBOL>& aSymbol )
955{
956 wxCHECK_RET( CurTok() == T_body_styles,
957 "Cannot parse " + GetTokenString( CurTok() ) + " as a body_styles token." );
958
959 std::vector<wxString> names;
960
961 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
962 {
963 if( token == T_demorgan )
964 {
965 aSymbol->SetHasDeMorganBodyStyles( true );
966 continue;
967 }
968 else if( !IsSymbol( token ) )
969 {
970 THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
971 CurOffset() );
972 }
973
974 names.push_back( FromUTF8() );
975 }
976
977 if( !names.empty() )
978 aSymbol->SetBodyStyleNames( names );
979}
980
981
982void SCH_IO_KICAD_SEXPR_PARSER::parsePinNames( std::unique_ptr<LIB_SYMBOL>& aSymbol )
983{
984 wxCHECK_RET( CurTok() == T_pin_names,
985 "Cannot parse " + GetTokenString( CurTok() ) + " as a pin_name token." );
986
994
995 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
996 {
997 // Pre-20241004 format - bare 'hide' keyword
998 if( token == T_hide )
999 {
1000 aSymbol->SetShowPinNames( false );
1001 continue;
1002 }
1003
1004 if( token != T_LEFT )
1005 {
1006 Expecting( T_LEFT );
1007 }
1008
1009 token = NextTok();
1010
1011 switch( token )
1012 {
1013 case T_offset:
1014 aSymbol->SetPinNameOffset( parseInternalUnits( "pin name offset" ) );
1015 NeedRIGHT();
1016 break;
1017
1018 case T_hide:
1019 aSymbol->SetShowPinNames( !parseBool() );
1020 NeedRIGHT();
1021 break;
1022
1023 default:
1024 Expecting( "offset or hide" );
1025 }
1026 }
1027}
1028
1029
1030void SCH_IO_KICAD_SEXPR_PARSER::parsePinNumbers( std::unique_ptr<LIB_SYMBOL>& aSymbol )
1031{
1032 wxCHECK_RET( CurTok() == T_pin_numbers,
1033 "Cannot parse " + GetTokenString( CurTok() ) + " as a pin_number token." );
1034
1041
1042 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
1043 {
1044 // Pre-20241004 format - bare 'hide' keyword
1045 if( token == T_hide )
1046 {
1047 aSymbol->SetShowPinNumbers( false );
1048 continue;
1049 }
1050
1051 if( token != T_LEFT )
1052 {
1053 Expecting( T_LEFT );
1054 }
1055
1056 token = NextTok();
1057
1058 switch( token )
1059 {
1060 case T_hide:
1061 aSymbol->SetShowPinNumbers( !parseBool() );
1062 NeedRIGHT();
1063 break;
1064
1065 default:
1066 Expecting( "hide" );
1067 break;
1068 }
1069 }
1070}
1071
1072
1073void SCH_IO_KICAD_SEXPR_PARSER::parseAssociatedFootprints( std::unique_ptr<LIB_SYMBOL>& aSymbol )
1074{
1075 wxCHECK_RET( CurTok() == T_associated_footprints,
1076 "Cannot parse " + GetTokenString( CurTok() ) + " as an associated_footprints token." );
1077
1084
1085 std::vector<ASSOCIATED_FOOTPRINT> list;
1086
1087 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
1088 {
1089 if( token != T_LEFT )
1090 Expecting( T_LEFT );
1091
1092 token = NextTok();
1093
1094 if( token != T_footprint )
1095 Expecting( "footprint" );
1096
1097 token = NextTok();
1098
1099 if( !IsSymbol( token ) )
1100 Expecting( "footprint library identifier" );
1101
1103 assoc.m_FootprintLibId.Parse( FromUTF8() );
1104
1105 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1106 {
1107 if( token != T_LEFT )
1108 Expecting( T_LEFT );
1109
1110 token = NextTok();
1111
1112 switch( token )
1113 {
1114 case T_map:
1115 token = NextTok();
1116
1117 if( !IsSymbol( token ) )
1118 Expecting( "map name" );
1119
1120 assoc.m_MapName = FromUTF8();
1121 NeedRIGHT();
1122 break;
1123
1124 default: Expecting( "map" ); break;
1125 }
1126 }
1127
1128 list.push_back( std::move( assoc ) );
1129 }
1130
1131 aSymbol->SetAssociatedFootprints( std::move( list ) );
1132}
1133
1134
1135void SCH_IO_KICAD_SEXPR_PARSER::parsePinMaps( std::unique_ptr<LIB_SYMBOL>& aSymbol )
1136{
1137 wxCHECK_RET( CurTok() == T_pin_maps, "Cannot parse " + GetTokenString( CurTok() ) + " as a pin_maps token." );
1138
1145
1146 PIN_MAP_SET set;
1147
1148 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
1149 {
1150 if( token != T_LEFT )
1151 Expecting( T_LEFT );
1152
1153 token = NextTok();
1154
1155 if( token != T_pin_map )
1156 Expecting( "pin_map" );
1157
1159 }
1160
1161 aSymbol->SetPinMaps( set );
1162}
1163
1164
1166{
1167 wxCHECK_MSG( CurTok() == T_pin_map, PIN_MAP(),
1168 "Cannot parse " + GetTokenString( CurTok() ) + " as a pin_map token." );
1169
1170 T token = NextTok();
1171
1172 if( !IsSymbol( token ) )
1173 Expecting( "pin map name" );
1174
1175 PIN_MAP map( FromUTF8() );
1176
1177 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1178 {
1179 if( token != T_LEFT )
1180 Expecting( T_LEFT );
1181
1182 token = NextTok();
1183
1184 if( token != T_entry )
1185 Expecting( "entry" );
1186
1187 token = NextTok();
1188
1189 if( !IsSymbol( token ) )
1190 Expecting( "pin number" );
1191
1192 wxString pinNumber = FromUTF8();
1193
1194 token = NextTok();
1195
1196 if( !IsSymbol( token ) )
1197 Expecting( "pad number" );
1198
1199 map.SetEntry( pinNumber, FromUTF8() );
1200
1201 NeedRIGHT();
1202 }
1203
1204 return map;
1205}
1206
1207
1209{
1210 wxCHECK_MSG( CurTok() == T_pin_map_override, PIN_MAP_INSTANCE_OVERRIDE(),
1211 "Cannot parse " + GetTokenString( CurTok() ) + " as a pin_map_override token." );
1212
1220
1222
1223 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
1224 {
1225 if( token != T_LEFT )
1226 Expecting( T_LEFT );
1227
1228 token = NextTok();
1229
1230 switch( token )
1231 {
1232 case T_mode:
1233 token = NextTok();
1234
1235 switch( token )
1236 {
1237 case T_library_default: override.m_Mode = PIN_MAP_OVERRIDE_MODE::USE_LIBRARY_DEFAULT; break;
1238 case T_named_map: override.m_Mode = PIN_MAP_OVERRIDE_MODE::USE_NAMED_MAP; break;
1239 case T_identity: override.m_Mode = PIN_MAP_OVERRIDE_MODE::FORCE_IDENTITY; break;
1240 case T_delegate: override.m_Mode = PIN_MAP_OVERRIDE_MODE::DELEGATE_TO_UNIT_1; break;
1241 default: Expecting( "library_default, named_map, identity, or delegate" );
1242 }
1243
1244 NeedRIGHT();
1245 break;
1246
1247 case T_map:
1248 token = NextTok();
1249
1250 if( !IsSymbol( token ) )
1251 Expecting( "map name" );
1252
1253 override.m_ActiveMapName = FromUTF8();
1254 NeedRIGHT();
1255 break;
1256
1257 case T_edit:
1258 {
1259 token = NextTok();
1260
1261 if( !IsSymbol( token ) )
1262 Expecting( "pin number" );
1263
1264 wxString pinNumber = FromUTF8();
1265
1266 token = NextTok();
1267
1268 if( !IsSymbol( token ) )
1269 Expecting( "pad number" );
1270
1271 override.m_Edits.push_back( { pinNumber, FromUTF8() } );
1272 NeedRIGHT();
1273 break;
1274 }
1275
1276 default: Expecting( "mode, map, or edit" );
1277 }
1278 }
1279
1280 return override;
1281}
1282
1283
1284SCH_FIELD* SCH_IO_KICAD_SEXPR_PARSER::parseProperty( std::unique_ptr<LIB_SYMBOL>& aSymbol )
1285{
1286 wxCHECK_MSG( CurTok() == T_property, nullptr,
1287 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a property." ) );
1288 wxCHECK( aSymbol, nullptr );
1289
1290 FIELD_T fieldId = FIELD_T::USER;
1291 wxString name;
1292 wxString value;
1293 bool isPrivate = false;
1294 bool isVisible = true;
1295
1296 T token = NextTok();
1297
1298 if( token == T_private )
1299 {
1300 isPrivate = true;
1301 token = NextTok();
1302 }
1303
1304 if( !IsSymbol( token ) )
1305 {
1306 THROW_PARSE_ERROR( _( "Invalid property name" ), CurSource(), CurLine(), CurLineNumber(),
1307 CurOffset() );
1308 }
1309
1310 name = FromUTF8();
1311
1312 if( name.IsEmpty() )
1313 {
1314 THROW_PARSE_ERROR( _( "Empty property name" ), CurSource(), CurLine(), CurLineNumber(),
1315 CurOffset() );
1316 }
1317
1318 // Correctly set the ID based on canonical (untranslated) field name
1319 for( FIELD_T id : MANDATORY_FIELDS )
1320 {
1321 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
1322 {
1323 fieldId = id;
1324 break;
1325 }
1326 }
1327
1328 auto field = std::make_unique<SCH_FIELD>( aSymbol.get(), fieldId, name );
1329 field->SetPrivate( isPrivate );
1330 field->SetVisible( isVisible );
1331
1332 token = NextTok();
1333
1334 if( !IsSymbol( token ) )
1335 {
1336 THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
1337 CurOffset() );
1338 }
1339
1340 // Empty property values are valid.
1341
1342 if( m_requiredVersion < 20250318 && CurStr() == "~" )
1343 value = wxEmptyString;
1344 else
1345 value = FromUTF8();
1346
1347 field->SetText( value );
1348
1349 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1350 {
1351 if( token != T_LEFT )
1352 Expecting( T_LEFT );
1353
1354 token = NextTok();
1355
1356 switch( token )
1357 {
1358 case T_id: // legacy token; ignore
1359 parseInt( "field ID" );
1360 NeedRIGHT();
1361 break;
1362
1363 case T_at:
1364 field->SetPosition( parseXY( true ) );
1365 field->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ) );
1366 NeedRIGHT();
1367 break;
1368
1369 case T_hide:
1370 field->SetVisible( !parseBool() );
1371 NeedRIGHT();
1372 break;
1373
1374 case T_effects:
1375 parseEDA_TEXT( static_cast<EDA_TEXT*>( field.get() ),
1376 field->GetId() == FIELD_T::VALUE );
1377 break;
1378
1379 case T_show_name:
1380 {
1381 bool show = parseMaybeAbsentBool( true );
1382 field->SetNameShown( show );
1383 break;
1384 }
1385
1386 case T_do_not_autoplace:
1387 {
1388 bool doNotAutoplace = parseMaybeAbsentBool( true );
1389 field->SetCanAutoplace( !doNotAutoplace );
1390 break;
1391 }
1392
1393 default:
1394 Expecting( "id, at, hide, show_name, do_not_autoplace, or effects" );
1395 }
1396 }
1397
1398 SCH_FIELD* existingField;
1399
1400 if( field->IsMandatory() )
1401 {
1402 existingField = aSymbol->GetField( field->GetId() );
1403
1404 *existingField = *field;
1405 return existingField;
1406 }
1407 else if( name == "ki_keywords" )
1408 {
1409 // Not a SCH_FIELD object yet.
1410 aSymbol->SetKeyWords( value );
1411 return nullptr;
1412 }
1413 // In v7 and earlier the description field didn't exist and was a key/value
1414 else if( name == "ki_description" )
1415 {
1416 aSymbol->SetDescription( value );
1417 return nullptr;
1418 }
1419 else if( name == "ki_fp_filters" )
1420 {
1421 // Not a SCH_FIELD object yet.
1422 wxArrayString filters;
1423 wxStringTokenizer tokenizer( value, " \t\r\n", wxTOKEN_STRTOK );
1424
1425 while( tokenizer.HasMoreTokens() )
1426 {
1427 wxString curr_token = UnescapeString( tokenizer.GetNextToken() );
1428 filters.Add( curr_token );
1429 }
1430
1431 aSymbol->SetFPFilters( filters );
1432 return nullptr;
1433 }
1434 else if( name == "ki_locked" )
1435 {
1436 // This is a temporary SCH_FIELD object until interchangeable units are determined on
1437 // the fly.
1438 aSymbol->LockUnits( true );
1439 return nullptr;
1440 }
1441 else
1442 {
1443 // At this point, a user field is read.
1444 existingField = aSymbol->GetField( field->GetCanonicalName() );
1445
1446#if 1 // Enable it to modify the name of the field to add if already existing
1447 // Disable it to skip the field having the same name as previous field
1448 if( existingField )
1449 {
1450 // We cannot handle 2 fields with the same name, so because the field name
1451 // is already in use, try to build a new name (oldname_x)
1452 wxString base_name = field->GetCanonicalName();
1453
1454 // Arbitrary limit 10 attempts to find a new name
1455 for( int ii = 1; ii < 10 && existingField; ii++ )
1456 {
1457 wxString newname = base_name;
1458 newname << '_' << ii;
1459
1460 existingField = aSymbol->GetField( newname );
1461
1462 if( !existingField ) // the modified name is not found, use it
1463 field->SetName( newname );
1464 }
1465 }
1466#endif
1467 if( !existingField )
1468 {
1469 aSymbol->AddDrawItem( field.get(), false );
1470 return field.release();
1471 }
1472 else
1473 {
1474 // We cannot handle 2 fields with the same name, so skip this one
1475 return nullptr;
1476 }
1477 }
1478}
1479
1480
1482{
1483 wxCHECK_MSG( CurTok() == T_arc, nullptr,
1484 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc." ) );
1485
1486 T token;
1487 VECTOR2I startPoint( 1, 0 ); // Initialize to a non-degenerate arc just for safety
1488 VECTOR2I midPoint( 1, 1 );
1489 VECTOR2I endPoint( 0, 1 );
1490 bool hasMidPoint = false;
1492 FILL_PARAMS fill;
1493
1494 // Parameters for legacy format
1495 VECTOR2I center( 0, 0 );
1496 EDA_ANGLE startAngle = ANGLE_0;
1497 EDA_ANGLE endAngle = ANGLE_90;
1498 bool hasAngles = false;
1499
1500 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC, LAYER_DEVICE );
1501
1502 arc->SetUnit( m_unit );
1503 arc->SetBodyStyle( m_bodyStyle );
1504
1505 token = NextTok();
1506
1507 if( token == T_private )
1508 {
1509 arc->SetPrivate( true );
1510 token = NextTok();
1511 }
1512
1513 for( ; token != T_RIGHT; token = NextTok() )
1514 {
1515 if( token != T_LEFT )
1516 Expecting( T_LEFT );
1517
1518 token = NextTok();
1519
1520 switch( token )
1521 {
1522 case T_start:
1523 startPoint = parseXY( true );
1524 NeedRIGHT();
1525 break;
1526
1527 case T_mid:
1528 midPoint = parseXY( true );
1529 NeedRIGHT();
1530 hasMidPoint = true;
1531 break;
1532
1533 case T_end:
1534 endPoint = parseXY( true );
1535 NeedRIGHT();
1536 break;
1537
1538 case T_radius:
1539 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1540 {
1541 if( token != T_LEFT )
1542 Expecting( T_LEFT );
1543
1544 token = NextTok();
1545
1546 switch( token )
1547 {
1548 case T_at:
1549 center = parseXY( true );
1550 NeedRIGHT();
1551 break;
1552
1553 case T_length:
1554 parseInternalUnits( "radius length" );
1555 NeedRIGHT();
1556 break;
1557
1558 case T_angles:
1559 {
1560 startAngle = EDA_ANGLE( parseDouble( "start radius angle" ), DEGREES_T );
1561 endAngle = EDA_ANGLE( parseDouble( "end radius angle" ), DEGREES_T );
1562 startAngle.Normalize();
1563 endAngle.Normalize();
1564 NeedRIGHT();
1565 hasAngles = true;
1566 break;
1567 }
1568
1569 default:
1570 Expecting( "at, length, or angles" );
1571 }
1572 }
1573
1574 break;
1575
1576 case T_stroke:
1577 parseStroke( stroke );
1578 arc->SetStroke( stroke );
1579 break;
1580
1581 case T_fill:
1582 parseFill( fill );
1583 arc->SetFillMode( fill.m_FillType );
1584 arc->SetFillColor( fill.m_Color );
1585 break;
1586
1587 default:
1588 Expecting( "start, mid, end, radius, stroke, or fill" );
1589 }
1590 }
1591
1592 if( hasMidPoint )
1593 {
1594 arc->SetArcGeometry( startPoint, midPoint, endPoint );
1595
1596 if( m_requiredVersion <= 20230121 ) // Versions before 7.0
1597 {
1598 // Should be not required. Unfortunately it is needed because some bugs created
1599 // incorrect data after conversion of old libraries to the new arc format using
1600 // startPoint, midPoint, endPoint
1601 // Until now, in Eeschema the arc angle should be <= 180 deg.
1602 // If > 180 (bug...) we need to swap arc ends.
1603 // However arc angle == 180 deg can also create issues in some cases (plotters, hittest)
1604 // so also avoid arc == 180 deg
1605 EDA_ANGLE arc_start, arc_end, arc_angle;
1606 arc->CalcArcAngles( arc_start, arc_end );
1607 arc_angle = arc_end - arc_start;
1608
1609 if( arc_angle > ANGLE_180 )
1610 {
1611 // Change arc to its complement (360deg - arc_angle)
1612 arc->SetStart( endPoint );
1613 arc->SetEnd( startPoint );
1614 VECTOR2I new_center =
1615 CalcArcCenter( arc->GetStart(), arc->GetEnd(), ANGLE_360 - arc_angle );
1616 arc->SetCenter( new_center );
1617 }
1618 else if( arc_angle == ANGLE_180 )
1619 {
1620 VECTOR2I new_center = CalcArcCenter( arc->GetStart(), arc->GetEnd(),
1621 EDA_ANGLE( 179.5, DEGREES_T ) );
1622 arc->SetCenter( new_center );
1623 }
1624 }
1625 }
1626 else if( hasAngles )
1627 {
1628 arc->SetCenter( center );
1629 /*
1630 * Older versions stored start-end with an implied winding, but the winding was different
1631 * between LibEdit and PCBNew. Since we now use a common class (EDA_SHAPE) for both we
1632 * need to flip one of them. LibEdit drew the short straw.
1633 */
1634 arc->SetStart( endPoint );
1635 arc->SetEnd( startPoint );
1636
1637 // Like previously, 180 degrees arcs that create issues are just modified
1638 // to be < 180 degrees to do not break some other functions ( Draw, Plot, HitTest)
1639 EDA_ANGLE arc_start, arc_end, arc_angle;
1640 arc->CalcArcAngles( arc_start, arc_end );
1641 arc_angle = arc_end - arc_start;
1642
1643 // The arc angle should be <= 180 deg in old libraries.
1644 // If > 180 we need to swap arc ends (the first choice was not good)
1645 if( arc_angle > ANGLE_180 )
1646 {
1647 arc->SetStart( startPoint );
1648 arc->SetEnd( endPoint );
1649 }
1650 else if( arc_angle == ANGLE_180 )
1651 {
1652 // Disabled (since the Y axis was reversed in library editor
1653 // and arcs having a 180 deg arc do not create issues
1654 // However keep it for info, just in case
1655 #if 0
1656 arc->SetStart( startPoint ); // not working with Y axis reversed
1657 arc->SetEnd( endPoint ); // not working with Y axis reversed
1658 // Useless now arcs >= 180 deg are well handled
1659 VECTOR2I new_center = CalcArcCenter( arc->GetStart(), arc->GetEnd(),
1660 EDA_ANGLE( 179.5, DEGREES_T ) );
1661 arc->SetCenter( new_center );
1662 #endif
1663 }
1664 }
1665 else
1666 {
1667 wxFAIL_MSG( "Setting arc without either midpoint or angles not implemented." );
1668 }
1669
1670 return arc.release();
1671}
1672
1673
1675{
1676 wxCHECK_MSG( CurTok() == T_bezier, nullptr,
1677 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
1678
1679 T token;
1681 FILL_PARAMS fill;
1682
1683 std::unique_ptr<SCH_SHAPE> bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER, LAYER_DEVICE );
1684
1685 bezier->SetUnit( m_unit );
1686 bezier->SetBodyStyle( m_bodyStyle );
1687
1688 token = NextTok();
1689
1690 if( token == T_private )
1691 {
1692 bezier->SetPrivate( true );
1693 token = NextTok();
1694 }
1695
1696 for( ; token != T_RIGHT; token = NextTok() )
1697 {
1698 if( token != T_LEFT )
1699 Expecting( T_LEFT );
1700
1701 token = NextTok();
1702
1703 switch( token )
1704 {
1705 case T_pts:
1706 {
1707 int ii = 0;
1708
1709 for( token = NextTok(); token != T_RIGHT; token = NextTok(), ++ii )
1710 {
1711 if( token != T_LEFT )
1712 Expecting( T_LEFT );
1713
1714 token = NextTok();
1715
1716 if( token != T_xy )
1717 Expecting( "xy" );
1718
1719 switch( ii )
1720 {
1721 case 0: bezier->SetStart( parseXY( true ) ); break;
1722 case 1: bezier->SetBezierC1( parseXY( true ) ); break;
1723 case 2: bezier->SetBezierC2( parseXY( true ) ); break;
1724 case 3: bezier->SetEnd( parseXY( true ) ); break;
1725 default: Unexpected( "control point" ); break;
1726 }
1727
1728 NeedRIGHT();
1729 }
1730 }
1731 break;
1732
1733 case T_stroke:
1734 parseStroke( stroke );
1735 bezier->SetStroke( stroke );
1736 break;
1737
1738 case T_fill:
1739 parseFill( fill );
1740 bezier->SetFillMode( fill.m_FillType );
1741 bezier->SetFillColor( fill.m_Color );
1742 break;
1743
1744 default:
1745 Expecting( "pts, stroke, or fill" );
1746 }
1747 }
1748
1749 bezier->RebuildBezierToSegmentsPointsList( m_maxError );
1750
1751 return bezier.release();
1752}
1753
1754
1756{
1757 wxCHECK_MSG( CurTok() == T_circle, nullptr,
1758 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle." ) );
1759
1760 T token;
1761 VECTOR2I center( 0, 0 );
1762 int radius = 1; // defaulting to 0 could result in troublesome math....
1764 FILL_PARAMS fill;
1765
1766 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE, LAYER_DEVICE );
1767
1768 circle->SetUnit( m_unit );
1769 circle->SetBodyStyle( m_bodyStyle );
1770
1771 token = NextTok();
1772
1773 if( token == T_private )
1774 {
1775 circle->SetPrivate( true );
1776 token = NextTok();
1777 }
1778
1779 for( ; token != T_RIGHT; token = NextTok() )
1780 {
1781 if( token != T_LEFT )
1782 Expecting( T_LEFT );
1783
1784 token = NextTok();
1785
1786 switch( token )
1787 {
1788 case T_center:
1789 center = parseXY( true );
1790 NeedRIGHT();
1791 break;
1792
1793 case T_radius:
1794 radius = parseInternalUnits( "radius length" );
1795 NeedRIGHT();
1796 break;
1797
1798 case T_stroke:
1799 parseStroke( stroke );
1800 circle->SetStroke( stroke );
1801 break;
1802
1803 case T_fill:
1804 parseFill( fill );
1805 circle->SetFillMode( fill.m_FillType );
1806 circle->SetFillColor( fill.m_Color );
1807 break;
1808
1809 default:
1810 Expecting( "center, radius, stroke, or fill" );
1811 }
1812 }
1813
1814 circle->SetCenter( center );
1815 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
1816
1817 return circle.release();
1818}
1819
1820
1822{
1823 wxCHECK_MSG( CurTok() == T_ellipse, nullptr,
1824 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an ellipse." ) );
1825
1826 auto ellipse = std::make_unique<SCH_SHAPE>( SHAPE_T::ELLIPSE, LAYER_DEVICE );
1827 ellipse->SetUnit( m_unit );
1828 ellipse->SetBodyStyle( m_bodyStyle );
1829
1830 T token = NextTok();
1831
1832 if( token == T_private )
1833 {
1834 ellipse->SetPrivate( true );
1835 token = NextTok();
1836 }
1837
1838 parseEllipseBody( ellipse.get(), /*isArc*/ false, /*isSchematic*/ false, token );
1839 return ellipse.release();
1840}
1841
1842
1844{
1845 wxCHECK_MSG( CurTok() == T_ellipse_arc, nullptr,
1846 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an elliptical arc." ) );
1847
1848 auto arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ELLIPSE_ARC, LAYER_DEVICE );
1849 arc->SetUnit( m_unit );
1850 arc->SetBodyStyle( m_bodyStyle );
1851
1852 T token = NextTok();
1853
1854 if( token == T_private )
1855 {
1856 arc->SetPrivate( true );
1857 token = NextTok();
1858 }
1859
1860 parseEllipseBody( arc.get(), /*isArc*/ true, /*isSchematic*/ false, token );
1861 return arc.release();
1862}
1863
1864
1866{
1867 auto parseType =
1868 [&]( T token ) -> ELECTRICAL_PINTYPE
1869 {
1870 switch( token )
1871 {
1872 case T_input: return ELECTRICAL_PINTYPE::PT_INPUT;
1873 case T_output: return ELECTRICAL_PINTYPE::PT_OUTPUT;
1874 case T_bidirectional: return ELECTRICAL_PINTYPE::PT_BIDI;
1875 case T_tri_state: return ELECTRICAL_PINTYPE::PT_TRISTATE;
1876 case T_passive: return ELECTRICAL_PINTYPE::PT_PASSIVE;
1877 case T_unspecified: return ELECTRICAL_PINTYPE::PT_UNSPECIFIED;
1878 case T_power_in: return ELECTRICAL_PINTYPE::PT_POWER_IN;
1879 case T_power_out: return ELECTRICAL_PINTYPE::PT_POWER_OUT;
1880 case T_open_collector: return ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR;
1881 case T_open_emitter: return ELECTRICAL_PINTYPE::PT_OPENEMITTER;
1882 case T_unconnected:
1883 case T_no_connect: return ELECTRICAL_PINTYPE::PT_NC;
1884 case T_free: return ELECTRICAL_PINTYPE::PT_NIC;
1885
1886 default:
1887 Expecting( "input, output, bidirectional, tri_state, passive, unspecified, "
1888 "power_in, power_out, open_collector, open_emitter, free or "
1889 "no_connect" );
1891 }
1892 };
1893
1894 auto parseShape =
1895 [&]( T token ) -> GRAPHIC_PINSHAPE
1896 {
1897 switch( token )
1898 {
1899 case T_line: return GRAPHIC_PINSHAPE::LINE;
1900 case T_inverted: return GRAPHIC_PINSHAPE::INVERTED;
1901 case T_clock: return GRAPHIC_PINSHAPE::CLOCK;
1902 case T_inverted_clock: return GRAPHIC_PINSHAPE::INVERTED_CLOCK;
1903 case T_input_low: return GRAPHIC_PINSHAPE::INPUT_LOW;
1904 case T_clock_low: return GRAPHIC_PINSHAPE::CLOCK_LOW;
1905 case T_output_low: return GRAPHIC_PINSHAPE::OUTPUT_LOW;
1906 case T_edge_clock_high: return GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK;
1907 case T_non_logic: return GRAPHIC_PINSHAPE::NONLOGIC;
1908
1909 default:
1910 Expecting( "line, inverted, clock, inverted_clock, input_low, clock_low, "
1911 "output_low, edge_clock_high, non_logic" );
1913 }
1914 };
1915
1916 wxCHECK_MSG( CurTok() == T_pin, nullptr,
1917 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a pin token." ) );
1918
1919 T token;
1920 std::unique_ptr<SCH_PIN> pin = std::make_unique<SCH_PIN>( nullptr );
1921
1922 pin->SetUnit( m_unit );
1923 pin->SetBodyStyle( m_bodyStyle );
1924
1925 // Pin electrical type.
1926 token = NextTok();
1927 pin->SetType( parseType( token ) );
1928
1929 // Pin shape.
1930 token = NextTok();
1931 pin->SetShape( parseShape( token ) );
1932
1933 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1934 {
1935 // Pre-2024104 format (bare 'hide' keyword)
1936 if( token == T_hide )
1937 {
1938 pin->SetVisible( false );
1939 continue;
1940 }
1941
1942 if( token != T_LEFT )
1943 Expecting( T_LEFT );
1944
1945 token = NextTok();
1946
1947 switch( token )
1948 {
1949 case T_at:
1950 pin->SetPosition( parseXY( true ) );
1951
1952 switch( parseInt( "pin orientation" ) )
1953 {
1954 case 0: pin->SetOrientation( PIN_ORIENTATION::PIN_RIGHT ); break;
1955 case 90: pin->SetOrientation( PIN_ORIENTATION::PIN_UP ); break;
1956 case 180: pin->SetOrientation( PIN_ORIENTATION::PIN_LEFT ); break;
1957 case 270: pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN ); break;
1958 default: Expecting( "0, 90, 180, or 270" );
1959 }
1960
1961 NeedRIGHT();
1962 break;
1963
1964 case T_length:
1965 pin->SetLength( parseInternalUnits( "pin length" ) );
1966 NeedRIGHT();
1967 break;
1968
1969 case T_hide:
1970 pin->SetVisible( !parseBool() );
1971 NeedRIGHT();
1972 break;
1973
1974 case T_name:
1975 token = NextTok();
1976
1977 if( !IsSymbol( token ) )
1978 {
1979 THROW_PARSE_ERROR( _( "Invalid pin name" ), CurSource(), CurLine(), CurLineNumber(),
1980 CurOffset() );
1981 }
1982
1983 if( m_requiredVersion < 20250318 && CurStr() == "~" )
1984 pin->SetName( wxEmptyString );
1985 else if( m_requiredVersion < 20210606 )
1986 pin->SetName( ConvertToNewOverbarNotation( FromUTF8() ) );
1987 else
1988 pin->SetName( FromUTF8() );
1989
1990 token = NextTok();
1991
1992 if( token != T_RIGHT )
1993 {
1994 token = NextTok();
1995
1996 if( token == T_effects )
1997 {
1998 // The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
1999 // so duplicate parsing is not required.
2001
2002 parseEDA_TEXT( &text, true, false );
2003 pin->SetNameTextSize( text.GetTextHeight() );
2004 NeedRIGHT();
2005 }
2006 else
2007 {
2008 Expecting( "effects" );
2009 }
2010 }
2011
2012 break;
2013
2014 case T_number:
2015 token = NextTok();
2016
2017 if( !IsSymbol( token ) )
2018 {
2019 THROW_PARSE_ERROR( _( "Invalid pin number" ), CurSource(), CurLine(),
2020 CurLineNumber(), CurOffset() );
2021 }
2022
2023 if( m_requiredVersion < 20250318 && CurStr() == "~" )
2024 pin->SetNumber( wxEmptyString );
2025 else if( m_requiredVersion < 20210606 )
2026 pin->SetNumber( ConvertToNewOverbarNotation( FromUTF8() ) );
2027 else
2028 pin->SetNumber( FromUTF8() );
2029
2030 token = NextTok();
2031
2032 if( token != T_RIGHT )
2033 {
2034 token = NextTok();
2035
2036 if( token == T_effects )
2037 {
2038 // The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
2039 // so duplicate parsing is not required.
2041
2042 parseEDA_TEXT( &text, false, false );
2043 pin->SetNumberTextSize( text.GetTextHeight() );
2044 NeedRIGHT();
2045 }
2046 else
2047 {
2048 Expecting( "effects" );
2049 }
2050 }
2051
2052 break;
2053
2054 case T_alternate:
2055 {
2056 SCH_PIN::ALT alt;
2057
2058 token = NextTok();
2059
2060 if( !IsSymbol( token ) )
2061 {
2062 THROW_PARSE_ERROR( _( "Invalid alternate pin name" ), CurSource(), CurLine(),
2063 CurLineNumber(), CurOffset() );
2064 }
2065
2066 alt.m_Name = FromUTF8();
2067
2068 token = NextTok();
2069 alt.m_Type = parseType( token );
2070
2071 token = NextTok();
2072 alt.m_Shape = parseShape( token );
2073
2074 pin->GetAlternates()[ alt.m_Name ] = alt;
2075
2076 NeedRIGHT();
2077 break;
2078 }
2079
2080 default:
2081 Expecting( "at, name, number, hide, length, or alternate" );
2082 }
2083 }
2084
2085 return pin.release();
2086}
2087
2088
2090{
2091 wxCHECK_MSG( CurTok() == T_polyline, nullptr,
2092 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a poly." ) );
2093
2094 T token;
2096 FILL_PARAMS fill;
2097 std::unique_ptr<SCH_SHAPE> poly = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_DEVICE );
2098
2099 poly->SetUnit( m_unit );
2100 poly->SetBodyStyle( m_bodyStyle );
2101
2102 token = NextTok();
2103
2104 if( token == T_private )
2105 {
2106 poly->SetPrivate( true );
2107 token = NextTok();
2108 }
2109
2110 for( ; token != T_RIGHT; token = NextTok() )
2111 {
2112 if( token != T_LEFT )
2113 Expecting( T_LEFT );
2114
2115 token = NextTok();
2116
2117 switch( token )
2118 {
2119 case T_pts:
2120 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2121 {
2122 if( token != T_LEFT )
2123 Expecting( T_LEFT );
2124
2125 token = NextTok();
2126
2127 if( token != T_xy )
2128 Expecting( "xy" );
2129
2130 poly->AddPoint( parseXY( true ) );
2131
2132 NeedRIGHT();
2133 }
2134
2135 break;
2136
2137 case T_stroke:
2138 parseStroke( stroke );
2139 poly->SetStroke( stroke );
2140 break;
2141
2142 case T_fill:
2143 parseFill( fill );
2144 poly->SetFillMode( fill.m_FillType );
2145 poly->SetFillColor( fill.m_Color );
2146 break;
2147
2148 default:
2149 Expecting( "pts, stroke, or fill" );
2150 }
2151 }
2152
2153 return poly.release();
2154}
2155
2156
2158{
2159 wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
2160 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle." ) );
2161
2162 T token;
2164 FILL_PARAMS fill;
2165 auto rectangle = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE, LAYER_DEVICE );
2166
2167 rectangle->SetUnit( m_unit );
2168 rectangle->SetBodyStyle( m_bodyStyle );
2169
2170 token = NextTok();
2171
2172 if( token == T_private )
2173 {
2174 rectangle->SetPrivate( true );
2175 token = NextTok();
2176 }
2177
2178 for( ; token != T_RIGHT; token = NextTok() )
2179 {
2180 if( token != T_LEFT )
2181 Expecting( T_LEFT );
2182
2183 token = NextTok();
2184
2185 switch( token )
2186 {
2187 case T_start:
2188 rectangle->SetPosition( parseXY( true ) );
2189 NeedRIGHT();
2190 break;
2191
2192 case T_end:
2193 rectangle->SetEnd( parseXY( true ) );
2194 NeedRIGHT();
2195 break;
2196
2197 case T_radius:
2198 rectangle->SetCornerRadius( parseDouble( "corner radius" ) * schIUScale.IU_PER_MM );
2199 NeedRIGHT();
2200 break;
2201
2202 case T_stroke:
2203 parseStroke( stroke );
2204 rectangle->SetStroke( stroke );
2205 break;
2206
2207 case T_fill:
2208 parseFill( fill );
2209 rectangle->SetFillMode( fill.m_FillType );
2210 rectangle->SetFillColor( fill.m_Color );
2211 break;
2212
2213 default:
2214 Expecting( "start, end, stroke, or fill" );
2215 }
2216 }
2217
2218 return rectangle.release();
2219}
2220
2221
2223{
2224 wxCHECK_MSG( CurTok() == T_text, nullptr,
2225 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text token." ) );
2226
2227 T token;
2228 std::unique_ptr<SCH_TEXT> text = std::make_unique<SCH_TEXT>();
2229
2230 text->SetLayer( LAYER_DEVICE );
2231 text->SetUnit( m_unit );
2232 text->SetBodyStyle( m_bodyStyle );
2233 token = NextTok();
2234
2235 if( token == T_private )
2236 {
2237 text->SetPrivate( true );
2238 token = NextTok();
2239 }
2240
2241 if( !IsSymbol( token ) )
2242 {
2243 THROW_PARSE_ERROR( _( "Invalid text string" ), CurSource(), CurLine(), CurLineNumber(),
2244 CurOffset() );
2245 }
2246
2247 text->SetText( FromUTF8() );
2248
2249 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2250 {
2251 if( token != T_LEFT )
2252 Expecting( T_LEFT );
2253
2254 token = NextTok();
2255
2256 switch( token )
2257 {
2258 case T_at:
2259 text->SetPosition( parseXY( true ) );
2260 // Yes, LIB_TEXT is really decidegrees even though all the others are degrees. :(
2261 text->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), TENTHS_OF_A_DEGREE_T ) );
2262 NeedRIGHT();
2263 break;
2264
2265 case T_effects:
2266 parseEDA_TEXT( text.get(), true );
2267 break;
2268
2269 default:
2270 Expecting( "at or effects" );
2271 }
2272 }
2273
2274 // Convert hidden symbol text (which is no longer supported) into a hidden field
2275 if( !text->IsVisible() )
2276 return new SCH_FIELD( text.get(), FIELD_T::USER );
2277
2278 return text.release();
2279}
2280
2281
2283{
2284 wxCHECK_MSG( CurTok() == T_text_box, nullptr,
2285 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text box." ) );
2286
2287 T token;
2288 VECTOR2I pos;
2289 VECTOR2I end;
2290 VECTOR2I size;
2291 int left;
2292 int top;
2293 int right;
2294 int bottom;
2296 FILL_PARAMS fill;
2297 bool foundEnd = false;
2298 bool foundSize = false;
2299 bool foundMargins = false;
2300
2301 std::unique_ptr<SCH_TEXTBOX> textBox = std::make_unique<SCH_TEXTBOX>( LAYER_DEVICE );
2302
2303 textBox->SetUnit( m_unit );
2304 textBox->SetBodyStyle( m_bodyStyle );
2305 token = NextTok();
2306
2307 if( token == T_private )
2308 {
2309 textBox->SetPrivate( true );
2310 token = NextTok();
2311 }
2312
2313 if( !IsSymbol( token ) )
2314 {
2315 THROW_PARSE_ERROR( _( "Invalid text string" ), CurSource(), CurLine(), CurLineNumber(),
2316 CurOffset() );
2317 }
2318
2319 textBox->SetText( FromUTF8() );
2320
2321 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2322 {
2323 if( token != T_LEFT )
2324 Expecting( T_LEFT );
2325
2326 token = NextTok();
2327
2328 switch( token )
2329 {
2330 case T_start: // Legacy token during 6.99 development; fails to handle angle
2331 pos = parseXY( true );
2332 NeedRIGHT();
2333 break;
2334
2335 case T_end: // Legacy token during 6.99 development; fails to handle angle
2336 end = parseXY( true );
2337 foundEnd = true;
2338 NeedRIGHT();
2339 break;
2340
2341 case T_at:
2342 pos = parseXY( true );
2343 textBox->SetTextAngle( EDA_ANGLE( parseDouble( "textbox angle" ), DEGREES_T ) );
2344 NeedRIGHT();
2345 break;
2346
2347 case T_size:
2348 size = parseXY( true );
2349 foundSize = true;
2350 NeedRIGHT();
2351 break;
2352
2353 case T_stroke:
2354 parseStroke( stroke );
2355 textBox->SetStroke( stroke );
2356 break;
2357
2358 case T_fill:
2359 parseFill( fill );
2360 textBox->SetFillMode( fill.m_FillType );
2361 textBox->SetFillColor( fill.m_Color );
2362 break;
2363
2364 case T_margins:
2365 parseMargins( left, top, right, bottom );
2366 textBox->SetMarginLeft( left );
2367 textBox->SetMarginTop( top );
2368 textBox->SetMarginRight( right );
2369 textBox->SetMarginBottom( bottom );
2370 foundMargins = true;
2371 NeedRIGHT();
2372 break;
2373
2374 case T_effects:
2375 parseEDA_TEXT( static_cast<EDA_TEXT*>( textBox.get() ), false );
2376 break;
2377
2378 default:
2379 Expecting( "at, size, stroke, fill or effects" );
2380 }
2381 }
2382
2383 textBox->SetPosition( pos );
2384
2385 if( foundEnd )
2386 textBox->SetEnd( end );
2387 else if( foundSize )
2388 textBox->SetEnd( pos + size );
2389 else
2390 Expecting( "size" );
2391
2392 if( !foundMargins )
2393 {
2394 int margin = textBox->GetLegacyTextMargin();
2395 textBox->SetMarginLeft( margin );
2396 textBox->SetMarginTop( margin );
2397 textBox->SetMarginRight( margin );
2398 textBox->SetMarginBottom( margin );
2399 }
2400
2401 return textBox.release();
2402}
2403
2404
2406{
2407 wxCHECK_RET( ( CurTok() == T_page && m_requiredVersion <= 20200506 ) || CurTok() == T_paper,
2408 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a PAGE_INFO." ) );
2409
2410 T token;
2411
2412 NeedSYMBOL();
2413
2414 wxString pageType = FromUTF8();
2415
2416 if( !aPageInfo.SetType( pageType ) )
2417 {
2418 THROW_PARSE_ERROR( _( "Invalid page type" ), CurSource(), CurLine(), CurLineNumber(),
2419 CurOffset() );
2420 }
2421
2422 if( aPageInfo.GetType() == PAGE_SIZE_TYPE::User )
2423 {
2424 double width = parseDouble( "width" );
2425
2426 // Perform some controls to avoid crashes if the size is edited by hands
2427 if( width < MIN_PAGE_SIZE_MM )
2428 width = MIN_PAGE_SIZE_MM;
2429 else if( width > MAX_PAGE_SIZE_EESCHEMA_MM )
2431
2432 double height = parseDouble( "height" );
2433
2434 if( height < MIN_PAGE_SIZE_MM )
2435 height = MIN_PAGE_SIZE_MM;
2436 else if( height > MAX_PAGE_SIZE_EESCHEMA_MM )
2438
2439 aPageInfo.SetWidthMM( width );
2440 aPageInfo.SetHeightMM( height );
2441 }
2442
2443 token = NextTok();
2444
2445 if( token == T_portrait )
2446 {
2447 aPageInfo.SetPortrait( true );
2448 NeedRIGHT();
2449 }
2450 else if( token != T_RIGHT )
2451 {
2452 Expecting( "portrait" );
2453 }
2454}
2455
2456
2458{
2459 wxCHECK_RET( CurTok() == T_title_block,
2460 "Cannot parse " + GetTokenString( CurTok() ) + " as a TITLE_BLOCK." );
2461
2462 T token;
2463
2464 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2465 {
2466 if( token != T_LEFT )
2467 Expecting( T_LEFT );
2468
2469 token = NextTok();
2470
2471 switch( token )
2472 {
2473 case T_title:
2474 NextTok();
2475 aTitleBlock.SetTitle( FromUTF8() );
2476 break;
2477
2478 case T_date:
2479 NextTok();
2480 aTitleBlock.SetDate( FromUTF8() );
2481 break;
2482
2483 case T_rev:
2484 NextTok();
2485 aTitleBlock.SetRevision( FromUTF8() );
2486 break;
2487
2488 case T_company:
2489 NextTok();
2490 aTitleBlock.SetCompany( FromUTF8() );
2491 break;
2492
2493 case T_comment:
2494 {
2495 int commentNumber = parseInt( "comment" );
2496
2497 switch( commentNumber )
2498 {
2499 case 1:
2500 NextTok();
2501 aTitleBlock.SetComment( 0, FromUTF8() );
2502 break;
2503
2504 case 2:
2505 NextTok();
2506 aTitleBlock.SetComment( 1, FromUTF8() );
2507 break;
2508
2509 case 3:
2510 NextTok();
2511 aTitleBlock.SetComment( 2, FromUTF8() );
2512 break;
2513
2514 case 4:
2515 NextTok();
2516 aTitleBlock.SetComment( 3, FromUTF8() );
2517 break;
2518
2519 case 5:
2520 NextTok();
2521 aTitleBlock.SetComment( 4, FromUTF8() );
2522 break;
2523
2524 case 6:
2525 NextTok();
2526 aTitleBlock.SetComment( 5, FromUTF8() );
2527 break;
2528
2529 case 7:
2530 NextTok();
2531 aTitleBlock.SetComment( 6, FromUTF8() );
2532 break;
2533
2534 case 8:
2535 NextTok();
2536 aTitleBlock.SetComment( 7, FromUTF8() );
2537 break;
2538
2539 case 9:
2540 NextTok();
2541 aTitleBlock.SetComment( 8, FromUTF8() );
2542 break;
2543
2544 default:
2545 THROW_PARSE_ERROR( _( "Invalid title block comment number" ), CurSource(),
2546 CurLine(), CurLineNumber(), CurOffset() );
2547 }
2548
2549 break;
2550 }
2551
2552 default:
2553 Expecting( "title, date, rev, company, or comment" );
2554 }
2555
2556 NeedRIGHT();
2557 }
2558}
2559
2560
2562{
2563 wxCHECK_MSG( CurTok() == T_property, nullptr,
2564 "Cannot parse " + GetTokenString( CurTok() ) + " as a property token." );
2565
2566 bool is_private = false;
2567
2568 T token = NextTok();
2569
2570 if( token == T_private )
2571 {
2572 is_private = true;
2573 token = NextTok();
2574 }
2575
2576 if( !IsSymbol( token ) )
2577 {
2578 THROW_PARSE_ERROR( _( "Invalid property name" ), CurSource(), CurLine(), CurLineNumber(),
2579 CurOffset() );
2580 }
2581
2582 wxString name = FromUTF8();
2583
2584 if( name.IsEmpty() )
2585 {
2586 THROW_PARSE_ERROR( _( "Empty property name" ), CurSource(), CurLine(), CurLineNumber(),
2587 CurOffset() );
2588 }
2589
2590 // Normalise legacy/cross-locale directive-label net class field names to the canonical
2591 // "Netclass" token as early as possible so every downstream consumer (including ones that
2592 // call GetName() directly instead of GetCanonicalName()) sees a consistent in-memory model.
2593 // See issue #24403.
2594 if( dynamic_cast<SCH_LABEL_BASE*>( aParent ) && SCH_FIELD::IsNetclassLabelFieldName( name ) )
2595 name = wxT( "Netclass" );
2596
2597 token = NextTok();
2598
2599 if( !IsSymbol( token ) )
2600 {
2601 THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
2602 CurOffset() );
2603 }
2604
2605 // Empty property values are valid.
2606 wxString value;
2607
2608 if( m_requiredVersion < 20250318 && CurStr() == "~" )
2609 value = wxEmptyString;
2610 else
2611 value = FromUTF8();
2612
2613 FIELD_T fieldId = FIELD_T::USER;
2614
2615 // Correctly set the ID based on canonical (untranslated) field name
2616 if( aParent->Type() == SCH_SYMBOL_T )
2617 {
2618 for( FIELD_T id : MANDATORY_FIELDS )
2619 {
2620 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
2621 {
2622 fieldId = id;
2623 break;
2624 }
2625 }
2626 }
2627 else if( aParent->Type() == SCH_SHEET_T )
2628 {
2629 fieldId = FIELD_T::SHEET_USER; // This is the default id for user fields
2630
2632 {
2633 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
2634 {
2635 fieldId = id;
2636 break;
2637 }
2638 }
2639
2640 // Legacy support for old field names
2641 if( name.CmpNoCase( wxT( "Sheet name" ) ) == 0 )
2642 fieldId = FIELD_T::SHEET_NAME;
2643 else if( name.CmpNoCase( wxT( "Sheet file" ) ) == 0 )
2644 fieldId = FIELD_T::SHEET_FILENAME;
2645 }
2646 else if( aParent->Type() == SCH_GLOBAL_LABEL_T )
2647 {
2649 {
2650 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
2651 {
2652 fieldId = id;
2653 break;
2654 }
2655 }
2656
2657 // Legacy support for old field names
2658 if( name.CmpNoCase( wxT( "Intersheet References" ) ) == 0 )
2659 fieldId = FIELD_T::INTERSHEET_REFS;
2660 }
2661
2662 std::unique_ptr<SCH_FIELD> field = std::make_unique<SCH_FIELD>( aParent, fieldId, name );
2663 field->SetText( value );
2664
2665 if( fieldId == FIELD_T::USER )
2666 field->SetPrivate( is_private );
2667
2668 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2669 {
2670 if( token != T_LEFT )
2671 Expecting( T_LEFT );
2672
2673 token = NextTok();
2674
2675 switch( token )
2676 {
2677 case T_id: // legacy token; ignore
2678 parseInt( "field ID" );
2679 NeedRIGHT();
2680 break;
2681
2682 case T_at:
2683 field->SetPosition( parseXY() );
2684 field->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ) );
2685 NeedRIGHT();
2686 break;
2687
2688 case T_hide:
2689 field->SetVisible( !parseBool() );
2690 NeedRIGHT();
2691 break;
2692
2693 case T_effects:
2694 parseEDA_TEXT( static_cast<EDA_TEXT*>( field.get() ),
2695 field->GetId() == FIELD_T::VALUE );
2696 break;
2697
2698 case T_show_name:
2699 {
2700 bool show = parseMaybeAbsentBool( true );
2701 field->SetNameShown( show );
2702 break;
2703 }
2704
2705 case T_do_not_autoplace:
2706 {
2707 bool doNotAutoplace = parseMaybeAbsentBool( true );
2708 field->SetCanAutoplace( !doNotAutoplace );
2709 break;
2710 }
2711
2712 default:
2713 Expecting( "id, at, hide, show_name, do_not_autoplace or effects" );
2714 }
2715 }
2716
2717 return field.release();
2718}
2719
2720
2722{
2723 wxCHECK_MSG( aSheet != nullptr, nullptr, "" );
2724 wxCHECK_MSG( CurTok() == T_pin, nullptr,
2725 "Cannot parse " + GetTokenString( CurTok() ) + " as a sheet pin token." );
2726
2727 T token = NextTok();
2728
2729 if( !IsSymbol( token ) )
2730 {
2731 THROW_PARSE_ERROR( _( "Invalid sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
2732 CurOffset() );
2733 }
2734
2735 wxString name = FromUTF8();
2736
2737 if( name.IsEmpty() )
2738 {
2739 THROW_PARSE_ERROR( _( "Empty sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
2740 CurOffset() );
2741 }
2742
2743 auto sheetPin = std::make_unique<SCH_SHEET_PIN>( aSheet, VECTOR2I( 0, 0 ), name );
2744
2745 token = NextTok();
2746
2747 switch( token )
2748 {
2749 case T_input: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
2750 case T_output: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
2751 case T_bidirectional: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
2752 case T_tri_state: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE ); break;
2753 case T_passive: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
2754 default:
2755 Expecting( "input, output, bidirectional, tri_state, or passive" );
2756 }
2757
2758 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2759 {
2760 if( token != T_LEFT )
2761 Expecting( T_LEFT );
2762
2763 token = NextTok();
2764
2765 switch( token )
2766 {
2767 case T_at:
2768 {
2769 sheetPin->SetPosition( parseXY() );
2770
2771 double angle = parseDouble( "sheet pin angle (side)" );
2772
2773 if( angle == 0.0 )
2774 sheetPin->SetSide( SHEET_SIDE::RIGHT );
2775 else if( angle == 90.0 )
2776 sheetPin->SetSide( SHEET_SIDE::TOP );
2777 else if( angle == 180.0 )
2778 sheetPin->SetSide( SHEET_SIDE::LEFT );
2779 else if( angle == 270.0 )
2780 sheetPin->SetSide( SHEET_SIDE::BOTTOM );
2781 else
2782 Expecting( "0, 90, 180, or 270" );
2783
2784 NeedRIGHT();
2785 break;
2786 }
2787
2788 case T_effects:
2789 parseEDA_TEXT( static_cast<EDA_TEXT*>( sheetPin.get() ), true );
2790 break;
2791
2792 case T_uuid:
2793 NeedSYMBOL();
2794 const_cast<KIID&>( sheetPin->m_Uuid ) = parseKIID();
2795 NeedRIGHT();
2796 break;
2797
2798 default:
2799 Expecting( "at, uuid or effects" );
2800 }
2801 }
2802
2803 return sheetPin.release();
2804}
2805
2806
2808{
2809 wxCHECK_RET( CurTok() == T_sheet_instances,
2810 "Cannot parse " + GetTokenString( CurTok() ) + " as an instances token." );
2811 wxCHECK( aScreen, /* void */ );
2812
2813 T token;
2814
2815 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2816 {
2817 if( token != T_LEFT )
2818 Expecting( T_LEFT );
2819
2820 token = NextTok();
2821
2822 switch( token )
2823 {
2824 case T_path:
2825 {
2826 NeedSYMBOL();
2827
2828 SCH_SHEET_INSTANCE instance;
2829
2830 instance.m_Path = KIID_PATH( FromUTF8() );
2831
2832 if( ( !m_appending && aRootSheet->GetScreen() == aScreen ) &&
2833 ( aScreen->GetFileFormatVersionAtLoad() < 20221002 ) )
2834 instance.m_Path.insert( instance.m_Path.begin(), m_rootUuid );
2835
2836 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2837 {
2838 if( token != T_LEFT )
2839 Expecting( T_LEFT );
2840
2841 token = NextTok();
2842
2843 std::vector<wxString> whitespaces = { wxT( "\r" ), wxT( "\n" ), wxT( "\t" ),
2844 wxT( " " ) };
2845
2846 size_t numReplacements = 0;
2847
2848 switch( token )
2849 {
2850 case T_page:
2851 NeedSYMBOL();
2852 instance.m_PageNumber = FromUTF8();
2853
2854 // Empty page numbers are not permitted
2855 if( instance.m_PageNumber.IsEmpty() )
2856 {
2857 // Use hash character instead
2858 instance.m_PageNumber = wxT( "#" );
2859 numReplacements++;
2860 }
2861 else
2862 {
2863 // Whitespaces are not permitted
2864 for( const wxString& ch : whitespaces )
2865 numReplacements += instance.m_PageNumber.Replace( ch, wxEmptyString );
2866
2867 }
2868
2869 // Set the file as modified so the user can be warned.
2870 if( numReplacements > 0 )
2871 aScreen->SetContentModified();
2872
2873 NeedRIGHT();
2874 break;
2875
2876 default:
2877 Expecting( "path or page" );
2878 }
2879 }
2880
2881 if( ( aScreen->GetFileFormatVersionAtLoad() >= 20221110 )
2882 && ( instance.m_Path.empty() ) )
2883 {
2884 SCH_SHEET_PATH rootSheetPath;
2885
2886 rootSheetPath.push_back( aRootSheet );
2887 rootSheetPath.SetPageNumber( instance.m_PageNumber );
2888 }
2889 else
2890 {
2891 aScreen->m_sheetInstances.emplace_back( instance );
2892 }
2893
2894 break;
2895 }
2896
2897 default:
2898 Expecting( "path" );
2899 }
2900 }
2901}
2902
2903
2905{
2906 wxCHECK_RET( CurTok() == T_symbol_instances,
2907 "Cannot parse " + GetTokenString( CurTok() ) + " as an instances token." );
2908 wxCHECK( aScreen, /* void */ );
2909 wxCHECK( m_rootUuid != NilUuid(), /* void */ );
2910
2911 T token;
2912
2913 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2914 {
2915 if( token != T_LEFT )
2916 Expecting( T_LEFT );
2917
2918 token = NextTok();
2919
2920 switch( token )
2921 {
2922 case T_path:
2923 {
2924 NeedSYMBOL();
2925
2926 SCH_SYMBOL_INSTANCE instance;
2927
2928 instance.m_Path = KIID_PATH( FromUTF8() );
2929
2930 if( !m_appending )
2931 instance.m_Path.insert( instance.m_Path.begin(), m_rootUuid );
2932
2933 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2934 {
2935 if( token != T_LEFT )
2936 Expecting( T_LEFT );
2937
2938 token = NextTok();
2939
2940 switch( token )
2941 {
2942 case T_reference:
2943 NeedSYMBOL();
2944 instance.m_Reference = FromUTF8();
2945 NeedRIGHT();
2946 break;
2947
2948 case T_unit:
2949 instance.m_Unit = parseInt( "symbol unit" );
2950 NeedRIGHT();
2951 break;
2952
2953 case T_value:
2954 NeedSYMBOL();
2955
2956 if( m_requiredVersion < 20250318 && CurStr() == "~" )
2957 instance.m_Value = wxEmptyString;
2958 else
2959 instance.m_Value = FromUTF8();
2960
2961 NeedRIGHT();
2962 break;
2963
2964 case T_footprint:
2965 NeedSYMBOL();
2966
2967 if( m_requiredVersion < 20250318 && CurStr() == "~" )
2968 instance.m_Footprint = wxEmptyString;
2969 else
2970 instance.m_Footprint = FromUTF8();
2971
2972 NeedRIGHT();
2973 break;
2974
2975 default:
2976 Expecting( "path, unit, value or footprint" );
2977 }
2978 }
2979
2980 aScreen->m_symbolInstances.emplace_back( instance );
2981 break;
2982 }
2983
2984 default:
2985 Expecting( "path" );
2986 }
2987 }
2988}
2989
2990
2991void SCH_IO_KICAD_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly,
2992 int aFileVersion )
2993{
2994 wxCHECK( aSheet != nullptr, /* void */ );
2995
2996 SCH_SCREEN* screen = aSheet->GetScreen();
2997
2998 wxCHECK( screen != nullptr, /* void */ );
2999
3000 if( SCHEMATIC* schematic = dynamic_cast<SCHEMATIC*>( screen->GetParent() ) )
3001 m_maxError = schematic->Settings().m_MaxError;
3002
3003 if( aIsCopyableOnly )
3004 m_requiredVersion = aFileVersion;
3005
3006 bool fileHasUuid = false;
3007
3008 auto checkVersion =
3009 [&]()
3010 {
3012 {
3013 throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
3015 }
3016 };
3017
3018 T token;
3019
3020 if( !aIsCopyableOnly )
3021 {
3022 NeedLEFT();
3023 NextTok();
3024
3025 if( CurTok() != T_kicad_sch )
3026 Expecting( "kicad_sch" );
3027
3029
3030 // Prior to this, bar was a valid string char for unquoted strings.
3031 SetKnowsBar( m_requiredVersion >= 20240620 );
3032
3033 // Prior to schematic file version 20210406, schematics did not have UUIDs so we need
3034 // to generate one for the root schematic for instance paths.
3035 if( m_requiredVersion < 20210406 )
3036 m_rootUuid = screen->GetUuid();
3037
3038 // Prior to version 20231120, generator_version was not present, so check the date here
3039 if( m_requiredVersion < 20231120 )
3040 checkVersion();
3041 }
3042
3044
3045 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3046 {
3047 if( aIsCopyableOnly && token == T_EOF )
3048 break;
3049
3050 if( token != T_LEFT )
3051 Expecting( T_LEFT );
3052
3053 token = NextTok();
3054
3055 checkpoint();
3056
3057 if( !aIsCopyableOnly && token == T_page && m_requiredVersion <= 20200506 )
3058 token = T_paper;
3059
3060 switch( token )
3061 {
3062 case T_group:
3063 parseGroup();
3064 break;
3065
3066 case T_generator:
3067 // (generator "genname"); we don't care about it at the moment.
3068 NeedSYMBOL();
3069 NeedRIGHT();
3070 break;
3071
3072 case T_host:
3073 {
3074 // (host eeschema ["5.99"]); old version of generator token
3075 NeedSYMBOL();
3076
3077 // Really old versions also included a host version
3078 if( m_requiredVersion < 20200827 )
3079 NeedSYMBOL();
3080
3081 NeedRIGHT();
3082 break;
3083 }
3084
3085 case T_generator_version:
3086 NextTok();
3087 m_generatorVersion = FromUTF8();
3088 NeedRIGHT();
3089 checkVersion();
3090 break;
3091
3092 case T_uuid:
3093 NeedSYMBOL();
3094 screen->m_uuid = parseKIID();
3095
3096 // Set the root sheet UUID with the schematic file UUID. Root sheets are virtual
3097 // and always get a new UUID so this prevents file churn now that the root UUID
3098 // is saved in the symbol instance path.
3099 if( aSheet == m_rootSheet )
3100 {
3101 const_cast<KIID&>( aSheet->m_Uuid ) = screen->GetUuid();
3102 m_rootUuid = screen->GetUuid();
3103 fileHasUuid = true;
3104 }
3105
3106 NeedRIGHT();
3107 break;
3108
3109 case T_paper:
3110 {
3111 if( aIsCopyableOnly )
3112 Unexpected( T_paper );
3113
3114 PAGE_INFO pageInfo;
3115 parsePAGE_INFO( pageInfo );
3116 screen->SetPageSettings( pageInfo );
3117 break;
3118 }
3119
3120 case T_page:
3121 {
3122 if( aIsCopyableOnly )
3123 Unexpected( T_page );
3124
3125 // Only saved for top-level sniffing in Kicad Manager frame and other external
3126 // tool usage with flat hierarchies
3127 NeedSYMBOLorNUMBER();
3128 NeedSYMBOLorNUMBER();
3129 NeedRIGHT();
3130 break;
3131 }
3132
3133 case T_title_block:
3134 {
3135 if( aIsCopyableOnly )
3136 Unexpected( T_title_block );
3137
3138 TITLE_BLOCK tb;
3139 parseTITLE_BLOCK( tb );
3140 screen->SetTitleBlock( tb );
3141 break;
3142 }
3143
3144 case T_lib_symbols:
3145 {
3146 // Dummy map. No derived symbols are allowed in the library cache.
3147 LIB_SYMBOL_MAP symbolLibMap;
3148 LIB_SYMBOL* symbol;
3149
3150 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3151 {
3152 if( token != T_LEFT )
3153 Expecting( T_LEFT );
3154
3155 token = NextTok();
3156
3157 switch( token )
3158 {
3159 case T_symbol:
3160 symbol = parseLibSymbol( symbolLibMap );
3161 screen->AddLibSymbol( symbol );
3162 break;
3163
3164 default:
3165 Expecting( "symbol" );
3166 }
3167 }
3168
3169 break;
3170 }
3171
3172 case T_symbol:
3173 screen->Append( static_cast<SCH_ITEM*>( parseSchematicSymbol() ) );
3174 break;
3175
3176 case T_image:
3177 screen->Append( static_cast<SCH_ITEM*>( parseImage() ) );
3178 break;
3179
3180 case T_sheet:
3181 {
3182 SCH_SHEET* sheet = parseSheet();
3183
3184 // Set the parent to aSheet. This effectively creates a method to find
3185 // the root sheet from any sheet so a pointer to the root sheet does not
3186 // need to be stored globally. Note: this is not the same as a hierarchy.
3187 // Complex hierarchies can have multiple copies of a sheet. This only
3188 // provides a simple tree to find the root sheet.
3189 sheet->SetParent( aSheet );
3190 screen->Append( sheet );
3191 break;
3192 }
3193
3194 case T_junction:
3195 screen->Append( parseJunction() );
3196 break;
3197
3198 case T_no_connect:
3199 screen->Append( parseNoConnect() );
3200 break;
3201
3202 case T_bus_entry:
3203 screen->Append( parseBusEntry() );
3204 break;
3205
3206 case T_polyline:
3207 {
3208 // polyline keyword is used in eeschema both for SCH_SHAPE and SCH_LINE items.
3209 // In symbols it describes a polygon, having n corners and can be filled
3210 // In schematic it describes a line (with no fill descr), but could be extended to a
3211 // polygon (for instance when importing files) because the schematic handles all
3212 // SCH_SHAPE.
3213
3214 // parseSchPolyLine() returns always a SCH_SHAPE, io convert it to a simple SCH_LINE
3215 // For compatibility reasons, keep SCH_SHAPE for a polygon and convert to SCH_LINE
3216 // when the item has only 2 corners, similar to a SCH_LINE
3217 SCH_SHAPE* poly = parseSchPolyLine();
3218
3219 if( poly->GetPointCount() < 2 )
3220 {
3221 delete poly;
3222 THROW_PARSE_ERROR( _( "Schematic polyline has too few points" ), CurSource(), CurLine(),
3223 CurLineNumber(), CurOffset() );
3224 }
3225 else if( poly->GetPointCount() > 2 )
3226 {
3227 screen->Append( poly );
3228 }
3229 else
3230 {
3231 // For SCH_SHAPE having only 2 points, this is a "old" SCH_LINE entity.
3232 // So convert the SCH_SHAPE to a simple SCH_LINE
3233 SCH_LINE* line = new SCH_LINE( VECTOR2I(), LAYER_NOTES );
3234 SHAPE_LINE_CHAIN& outline = poly->GetPolyShape().Outline(0);
3235 line->SetStartPoint( outline.CPoint(0) );
3236 line->SetEndPoint( outline.CPoint(1) );
3237 line->SetStroke( poly->GetStroke() );
3238 line->SetLocked( poly->IsLocked() );
3239 const_cast<KIID&>( line->m_Uuid ) = poly->m_Uuid;
3240
3241 screen->Append( line );
3242
3243 delete poly;
3244 }
3245 }
3246 break;
3247
3248 case T_bus:
3249 case T_wire:
3250 screen->Append( parseLine() );
3251 break;
3252
3253 case T_arc:
3254 screen->Append( parseSchArc() );
3255 break;
3256
3257 case T_circle:
3258 screen->Append( parseSchCircle() );
3259 break;
3260
3261 case T_rectangle:
3262 screen->Append( parseSchRectangle() );
3263 break;
3264
3265 case T_bezier:
3266 screen->Append( parseSchBezier() );
3267 break;
3268
3269 case T_ellipse: screen->Append( parseSchEllipse() ); break;
3270
3271 case T_ellipse_arc: screen->Append( parseSchEllipseArc() ); break;
3272
3273 case T_rule_area:
3274 screen->Append( parseSchRuleArea() );
3275 break;
3276
3277 case T_netclass_flag: // present only during early development of 7.0
3279
3280 case T_text:
3281 case T_label:
3282 case T_global_label:
3283 case T_hierarchical_label:
3284 case T_directive_label:
3285 screen->Append( parseSchText() );
3286 break;
3287
3288 case T_net_chain:
3290 break;
3291
3292 case T_text_box:
3293 screen->Append( parseSchTextBox() );
3294 break;
3295
3296 case T_table:
3297 screen->Append( parseSchTable() );
3298 break;
3299
3300 case T_sheet_instances:
3301 parseSchSheetInstances( aSheet, screen );
3302 break;
3303
3304 case T_symbol_instances:
3305 parseSchSymbolInstances( screen );
3306 break;
3307
3308 case T_bus_alias:
3309 if( aIsCopyableOnly )
3310 Unexpected( T_bus_alias );
3311
3312 parseBusAlias( screen );
3313 break;
3314
3315 case T_embedded_fonts:
3316 {
3317 SCHEMATIC* schematic = screen->Schematic();
3318
3319 if( !schematic )
3320 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
3321 CurLineNumber(), CurOffset() );
3322
3323 schematic->GetEmbeddedFiles()->SetAreFontsEmbedded( parseBool() );
3324 NeedRIGHT();
3325 break;
3326 }
3327
3328 case T_embedded_files:
3329 {
3330 SCHEMATIC* schematic = screen->Schematic();
3331
3332 if( !schematic )
3333 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
3334 CurLineNumber(), CurOffset() );
3335
3336 EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
3337 embeddedFilesParser.SyncLineReaderWith( *this );
3338
3339 try
3340 {
3341 embeddedFilesParser.ParseEmbedded( schematic->GetEmbeddedFiles() );
3342 }
3343 catch( const PARSE_ERROR& e )
3344 {
3345 m_parseWarnings.push_back( e.What() );
3346
3347 int depth = 0;
3348
3349 for( int tok = embeddedFilesParser.NextTok();
3350 tok != DSN_EOF;
3351 tok = embeddedFilesParser.NextTok() )
3352 {
3353 if( tok == DSN_LEFT )
3354 depth++;
3355 else if( tok == DSN_RIGHT && --depth < 0 )
3356 break;
3357 }
3358 }
3359
3360 SyncLineReaderWith( embeddedFilesParser );
3361 break;
3362 }
3363
3364
3365 default:
3366 Expecting( "arc, bezier, bitmap, bus, bus_alias, bus_entry, circle, class_label, "
3367 "directive_label, ellipse, ellipse_arc, embedded_fonts, embedded_files, "
3368 "global_label, hierarchical_label, image, junction, lib_symbols, "
3369 "netclass_flag, no_connect, polyline, rectangle, rule_area, sheet, "
3370 "sheet_instances, signal, symbol, symbol_instances, table, text, "
3371 "text_box, title_block, uuid, wire" );
3372 }
3373 }
3374
3375 // Older s-expression schematics may not have a UUID so use the one automatically generated
3376 // as the virtual root sheet UUID.
3377 if( ( aSheet == m_rootSheet ) && !fileHasUuid )
3378 {
3379 const_cast<KIID&>( aSheet->m_Uuid ) = screen->GetUuid();
3380 m_rootUuid = screen->GetUuid();
3381 }
3382
3383 screen->UpdateLocalLibSymbolLinks();
3384 screen->FixupEmbeddedData();
3385
3386 resolveGroups( screen );
3387
3388 SCHEMATIC* schematic = screen->Schematic();
3389
3390 if( !schematic )
3391 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
3392 CurLineNumber(), CurOffset() );
3393
3394 // When loading the schematic, take a moment to cache the fonts so that the font
3395 // picker can show the embedded fonts immediately.
3396 std::vector<std::string> fontNames;
3397 Fontconfig()->ListFonts( fontNames, std::string( Pgm().GetLanguageTag().utf8_str() ),
3398 schematic->GetEmbeddedFiles()->GetFontFiles(), true );
3399
3400 if( m_requiredVersion < 20200828 )
3402}
3403
3404
3406{
3407 wxCHECK_MSG( CurTok() == T_symbol, nullptr,
3408 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
3409
3410 T token;
3411 wxString libName;
3412 SCH_FIELD* field;
3413 std::unique_ptr<SCH_SYMBOL> symbol = std::make_unique<SCH_SYMBOL>();
3414 TRANSFORM transform;
3415 std::set<int> fieldIDsRead;
3416
3417 // We'll reset this if we find a fields_autoplaced token
3418 symbol->SetFieldsAutoplaced( AUTOPLACE_NONE );
3419
3420 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3421 {
3422 if( token != T_LEFT )
3423 Expecting( T_LEFT );
3424
3425 token = NextTok();
3426
3427 switch( token )
3428 {
3429 case T_lib_name:
3430 {
3431 LIB_ID libId;
3432
3433 token = NextTok();
3434
3435 if( !IsSymbol( token ) )
3436 {
3437 THROW_PARSE_ERROR( _( "Invalid symbol library name" ), CurSource(), CurLine(),
3438 CurLineNumber(), CurOffset() );
3439 }
3440
3441 libName = FromUTF8();
3442 // Some symbol LIB_IDs or lib_name have the '/' character escaped which can break
3443 // symbol links. The '/' character is no longer an illegal LIB_ID character so
3444 // it doesn't need to be escaped.
3445 libName.Replace( "{slash}", "/" );
3446
3447 NeedRIGHT();
3448 break;
3449 }
3450
3451 case T_lib_id:
3452 {
3453 token = NextTok();
3454
3455 if( !IsSymbol( token ) && token != T_NUMBER )
3456 Expecting( "symbol|number" );
3457
3458 LIB_ID libId;
3459 wxString name = FromUTF8();
3460 // Some symbol LIB_IDs have the '/' character escaped which can break
3461 // symbol links. The '/' character is no longer an illegal LIB_ID character so
3462 // it doesn't need to be escaped.
3463 name.Replace( "{slash}", "/" );
3464
3465 int bad_pos = libId.Parse( name );
3466
3467 if( bad_pos >= 0 )
3468 {
3469 if( static_cast<int>( name.size() ) > bad_pos )
3470 {
3471 wxString msg = wxString::Format( _( "Symbol %s contains invalid character '%c'" ),
3472 name,
3473 name[bad_pos] );
3474
3475 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
3476 }
3477
3478 THROW_PARSE_ERROR( _( "Invalid symbol library ID" ), CurSource(), CurLine(),
3479 CurLineNumber(), CurOffset() );
3480 }
3481
3482 symbol->SetLibId( libId );
3483 NeedRIGHT();
3484 break;
3485 }
3486
3487 case T_at:
3488 symbol->SetPosition( parseXY() );
3489
3490 switch( static_cast<int>( parseDouble( "symbol orientation" ) ) )
3491 {
3492 case 0: transform = TRANSFORM(); break;
3493 case 90: transform = TRANSFORM( 0, 1, -1, 0 ); break;
3494 case 180: transform = TRANSFORM( -1, 0, 0, -1 ); break;
3495 case 270: transform = TRANSFORM( 0, -1, 1, 0 ); break;
3496 default: Expecting( "0, 90, 180, or 270" );
3497 }
3498
3499 symbol->SetTransform( transform );
3500 NeedRIGHT();
3501 break;
3502
3503 case T_mirror:
3504 token = NextTok();
3505
3506 if( token == T_x )
3507 symbol->SetOrientation( SYM_MIRROR_X );
3508 else if( token == T_y )
3509 symbol->SetOrientation( SYM_MIRROR_Y );
3510 else
3511 Expecting( "x or y" );
3512
3513 NeedRIGHT();
3514 break;
3515
3516 case T_unit:
3517 symbol->SetUnit( parseInt( "symbol unit" ) );
3518 NeedRIGHT();
3519 break;
3520
3521 case T_convert: // Legacy token
3522 case T_body_style:
3523 symbol->SetBodyStyle( parseInt( "symbol body style" ) );
3524 NeedRIGHT();
3525 break;
3526
3527 case T_exclude_from_sim:
3528 symbol->SetExcludedFromSim( parseBool() );
3529 NeedRIGHT();
3530 break;
3531
3532 case T_in_bom:
3533 symbol->SetExcludedFromBOM( !parseBool() );
3534 NeedRIGHT();
3535 break;
3536
3537 case T_on_board:
3538 symbol->SetExcludedFromBoard( !parseBool() );
3539 NeedRIGHT();
3540 break;
3541
3542 case T_in_pos_files:
3543 symbol->SetExcludedFromPosFiles( !parseBool() );
3544 NeedRIGHT();
3545 break;
3546
3547 case T_dnp:
3548 symbol->SetDNP( parseBool() );
3549 NeedRIGHT();
3550 break;
3551
3552 case T_pin_map_override: symbol->SetPinMapOverride( parsePinMapOverride() ); break;
3553
3554 case T_locked:
3555 symbol->SetLocked( parseBool() );
3556 NeedRIGHT();
3557 break;
3558
3559 case T_passthrough:
3560 {
3561 // (passthrough default|block|force)
3562 T t = NextTok();
3563
3564 // Expect a string-like token
3565 if( !IsSymbol( t ) )
3566 Expecting( "default, block or force" );
3567
3568 wxString mode = FromUTF8();
3569
3570 if( mode.IsSameAs( wxT("default"), false ) )
3571 symbol->SetPassthroughMode( SCH_SYMBOL::PASSTHROUGH_MODE::DEFAULT );
3572 else if( mode.IsSameAs( wxT("block"), false ) )
3573 symbol->SetPassthroughMode( SCH_SYMBOL::PASSTHROUGH_MODE::BLOCK );
3574 else if( mode.IsSameAs( wxT("force"), false ) )
3575 symbol->SetPassthroughMode( SCH_SYMBOL::PASSTHROUGH_MODE::FORCE );
3576 else
3577 Expecting( "default, block or force" );
3578
3579 NeedRIGHT();
3580 break;
3581 }
3582
3583 case T_fields_autoplaced:
3584 if( parseMaybeAbsentBool( true ) )
3585 symbol->SetFieldsAutoplaced( AUTOPLACE_AUTO );
3586
3587 break;
3588
3589 case T_uuid:
3590 NeedSYMBOL();
3591 const_cast<KIID&>( symbol->m_Uuid ) = parseKIID();
3592 NeedRIGHT();
3593 break;
3594
3595 case T_default_instance:
3596 {
3597 SCH_SYMBOL_INSTANCE defaultInstance;
3598
3599 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3600 {
3601 if( token != T_LEFT )
3602 Expecting( T_LEFT );
3603
3604 token = NextTok();
3605
3606 switch( token )
3607 {
3608 case T_reference:
3609 NeedSYMBOL();
3610 defaultInstance.m_Reference = FromUTF8();
3611 NeedRIGHT();
3612 break;
3613
3614 case T_unit:
3615 defaultInstance.m_Unit = parseInt( "symbol unit" );
3616 NeedRIGHT();
3617 break;
3618
3619 case T_value:
3620 NeedSYMBOL();
3621
3622 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3623 symbol->SetValueFieldText( wxEmptyString );
3624 else
3625 symbol->SetValueFieldText( FromUTF8() );
3626
3627 NeedRIGHT();
3628 break;
3629
3630 case T_footprint:
3631 NeedSYMBOL();
3632
3633 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3634 symbol->SetFootprintFieldText( wxEmptyString );
3635 else
3636 symbol->SetFootprintFieldText( FromUTF8() );
3637
3638 NeedRIGHT();
3639 break;
3640
3641 default:
3642 Expecting( "reference, unit, value or footprint" );
3643 }
3644 }
3645
3646 break;
3647 }
3648
3649 case T_instances:
3650 {
3651 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3652 {
3653 if( token != T_LEFT )
3654 Expecting( T_LEFT );
3655
3656 token = NextTok();
3657
3658 if( token != T_project )
3659 Expecting( "project" );
3660
3661 NeedSYMBOL();
3662
3663 wxString projectName = FromUTF8();
3664
3665 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3666 {
3667 if( token != T_LEFT )
3668 Expecting( T_LEFT );
3669
3670 token = NextTok();
3671
3672 if( token != T_path )
3673 Expecting( "path" );
3674
3675 SCH_SYMBOL_INSTANCE instance;
3676
3677 instance.m_ProjectName = projectName;
3678
3679 NeedSYMBOL();
3680 instance.m_Path = KIID_PATH( FromUTF8() );
3681
3682 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3683 {
3684 if( token != T_LEFT )
3685 Expecting( T_LEFT );
3686
3687 token = NextTok();
3688
3689 switch( token )
3690 {
3691 case T_reference:
3692 NeedSYMBOL();
3693 instance.m_Reference = FromUTF8();
3694 NeedRIGHT();
3695 break;
3696
3697 case T_unit:
3698 instance.m_Unit = parseInt( "symbol unit" );
3699 NeedRIGHT();
3700 break;
3701
3702 case T_value:
3703 NeedSYMBOL();
3704
3705 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3706 symbol->SetValueFieldText( wxEmptyString );
3707 else
3708 symbol->SetValueFieldText( FromUTF8() );
3709
3710 NeedRIGHT();
3711 break;
3712
3713 case T_footprint:
3714 NeedSYMBOL();
3715
3716 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3717 symbol->SetFootprintFieldText( wxEmptyString );
3718 else
3719 symbol->SetFootprintFieldText( FromUTF8() );
3720
3721 NeedRIGHT();
3722 break;
3723
3724
3725 case T_variant:
3726 {
3727 SCH_SYMBOL_VARIANT variant;
3728 variant.InitializeAttributes( *symbol );
3729
3730 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3731 {
3732 if( token != T_LEFT )
3733 Expecting( T_LEFT );
3734
3735 token = NextTok();
3736
3737 switch( token )
3738 {
3739 case T_name:
3740 NeedSYMBOL();
3741 variant.m_Name = FromUTF8();
3742 NeedRIGHT();
3743 break;
3744
3745 case T_dnp:
3746 variant.m_DNP = parseBool();
3747 NeedRIGHT();
3748 break;
3749
3750 case T_exclude_from_sim:
3751 variant.m_ExcludedFromSim = parseBool();
3752 NeedRIGHT();
3753 break;
3754
3755 case T_in_bom:
3756 variant.m_ExcludedFromBOM = parseBool();
3757
3758 // This fixes the incorrect logic from prior file versions. The "in_bom" token
3759 // used in the file format is the positive logic. However, in the UI the term
3760 // excluded from BOM is used which is the inverted logic.
3761 if( m_requiredVersion >= 20260306 )
3762 variant.m_ExcludedFromBOM = !variant.m_ExcludedFromBOM;
3763
3764 NeedRIGHT();
3765 break;
3766
3767 case T_on_board:
3768 variant.m_ExcludedFromBoard = !parseBool();
3769 NeedRIGHT();
3770 break;
3771
3772 case T_in_pos_files:
3773 variant.m_ExcludedFromPosFiles = !parseBool();
3774 NeedRIGHT();
3775 break;
3776
3777 case T_field:
3778 {
3779 wxString fieldName;
3780 wxString fieldValue;
3781
3782 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3783 {
3784 if( token != T_LEFT )
3785 Expecting( T_LEFT );
3786
3787 token = NextTok();
3788
3789 switch( token )
3790 {
3791 case T_name:
3792 NeedSYMBOL();
3793 fieldName = FromUTF8();
3794 NeedRIGHT();
3795 break;
3796
3797 case T_value:
3798 NeedSYMBOL();
3799 fieldValue = FromUTF8();
3800 NeedRIGHT();
3801 break;
3802
3803 default:
3804 Expecting( "name or value" );
3805 }
3806 }
3807
3808 variant.m_Fields[fieldName] = fieldValue;
3809 break;
3810 }
3811
3812 case T_pin_map_override: variant.m_PinMapOverride = parsePinMapOverride(); break;
3813
3814 default:
3815 Expecting( "dnp, exclude_from_sim, field, in_bom, in_pos_files, name, "
3816 "on_board, or pin_map_override" );
3817 }
3818
3819 instance.m_Variants[variant.m_Name] = variant;
3820 }
3821
3822 break;
3823 }
3824
3825 default:
3826 Expecting( "reference, unit, value, footprint, or variant" );
3827 }
3828 }
3829
3830 symbol->AddHierarchicalReference( instance );
3831 }
3832 }
3833
3834 break;
3835 }
3836
3837 case T_property:
3838 // The field parent symbol must be set and its orientation must be set before
3839 // the field positions are set.
3840 field = parseSchField( symbol.get() );
3841
3842 // Exclude from simulation used to be managed by a Sim.Enable field set to "0" when
3843 // simulation was disabled.
3845 {
3846 symbol->SetExcludedFromSim( field->GetText() == wxS( "0" ) );
3847 delete field;
3848 break;
3849 }
3850
3851 // Even longer ago, we had a "Spice_Netlist_Enabled" field
3853 {
3854 symbol->SetExcludedFromSim( field->GetText() == wxS( "N" ) );
3855 delete field;
3856 break;
3857 }
3858
3859 SCH_FIELD* existing;
3860
3861 if( field->IsMandatory() )
3862 existing = symbol->GetField( field->GetId() );
3863 else
3864 existing = symbol->GetField( field->GetName() );
3865
3866 if( existing )
3867 *existing = *field;
3868 else
3869 symbol->AddField( *field );
3870
3871 if( field->GetId() == FIELD_T::REFERENCE )
3872 symbol->UpdatePrefix();
3873
3874 delete field;
3875 break;
3876
3877 case T_pin:
3878 {
3879 // Read an alternate pin designation
3880 wxString number;
3881 KIID uuid;
3882 wxString alt;
3883
3884 NeedSYMBOL();
3885 number = FromUTF8();
3886
3887 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3888 {
3889 if( token != T_LEFT )
3890 Expecting( T_LEFT );
3891
3892 token = NextTok();
3893
3894 switch( token )
3895 {
3896 case T_alternate:
3897 NeedSYMBOL();
3898 alt = FromUTF8();
3899 NeedRIGHT();
3900 break;
3901
3902 case T_uuid:
3903 NeedSYMBOL();
3904
3905 // First version to write out pin uuids accidentally wrote out the symbol's
3906 // uuid for each pin, so ignore uuids coming from that version.
3907 if( m_requiredVersion >= 20210126 )
3908 uuid = parseKIID();
3909
3910 NeedRIGHT();
3911 break;
3912
3913 default:
3914 Expecting( "alternate or uuid" );
3915 }
3916 }
3917
3918 symbol->GetRawPins().emplace_back( std::make_unique<SCH_PIN>( symbol.get(), number,
3919 alt, uuid ) );
3920 break;
3921 }
3922
3923 default:
3924 Expecting( "lib_id, lib_name, at, mirror, uuid, exclude_from_sim, on_board, in_bom, dnp, passthrough, "
3925 "default_instance, property, pin, or instances" );
3926 }
3927 }
3928
3929 if( !libName.IsEmpty() && ( symbol->GetLibId().Format().wx_str() != libName ) )
3930 symbol->SetSchSymbolLibraryName( libName );
3931
3932 // Ensure edit/status flags are cleared after these initializations:
3933 symbol->ClearFlags();
3934
3935 return symbol.release();
3936}
3937
3938
3940{
3941 wxCHECK_MSG( CurTok() == T_image, nullptr,
3942 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an image." ) );
3943
3944 T token;
3945 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>();
3946 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
3947
3948 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3949 {
3950 if( token != T_LEFT )
3951 Expecting( T_LEFT );
3952
3953 token = NextTok();
3954
3955 switch( token )
3956 {
3957 case T_at:
3958 bitmap->SetPosition( parseXY() );
3959 NeedRIGHT();
3960 break;
3961
3962 case T_scale:
3963 {
3964 const double scale = parseDouble( "image scale factor" );
3965 refImage.SetImageScale( std::isnormal( scale ) ? scale : 1.0 );
3966
3967 NeedRIGHT();
3968 break;
3969 }
3970 case T_uuid:
3971 NeedSYMBOL();
3972 const_cast<KIID&>( bitmap->m_Uuid ) = parseKIID();
3973 NeedRIGHT();
3974 break;
3975
3976 case T_data:
3977 {
3978 token = NextTok();
3979
3980 wxString data;
3981
3982 // Reserve 128K because most image files are going to be larger than the default
3983 // 1K that wxString reserves.
3984 data.reserve( 1 << 17 );
3985
3986 while( token != T_RIGHT )
3987 {
3988 if( !IsSymbol( token ) )
3989 Expecting( "base64 image data" );
3990
3991 data += FromUTF8();
3992 token = NextTok();
3993 }
3994
3995 wxMemoryBuffer buffer = wxBase64Decode( data );
3996
3997 if( !refImage.ReadImageFile( buffer ) )
3998 THROW_IO_ERROR( _( "Failed to read image data." ) );
3999
4000 break;
4001 }
4002
4003 case T_locked:
4004 bitmap->SetLocked( parseBool() );
4005 NeedRIGHT();
4006 break;
4007
4008 default:
4009 Expecting( "at, scale, uuid, data or locked" );
4010 }
4011 }
4012
4013 // The image will be scaled by PPI in ReadImageFile.
4014
4015 // 20230121 or older file format versions assumed 300 image PPI at load/save.
4016 // Let's keep compatibility by changing image scale.
4017 if( m_requiredVersion <= 20230121 )
4018 {
4019 refImage.SetImageScale( refImage.GetImageScale() * refImage.GetImage().GetPPI() / 300.0 );
4020 }
4021 else if( m_requiredVersion < 20260623 )
4022 {
4023 // Between the 300-PPI era and 20260623 the PPI was computed from the embedded
4024 // resolution but pixels/cm was truncated to an integer, so the stored scale
4025 // compensated for the wrong PPI. Re-scale to the corrected PPI to preserve size.
4026 const BITMAP_BASE& image = refImage.GetImage();
4027 int legacyPPI = image.GetLegacyPPI();
4028
4029 if( legacyPPI > 0 && image.GetPPI() != legacyPPI )
4030 refImage.SetImageScale( refImage.GetImageScale() * image.GetPPI() / legacyPPI );
4031 }
4032
4033 return bitmap.release();
4034}
4035
4036
4038{
4039 wxCHECK_MSG( CurTok() == T_sheet, nullptr,
4040 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a sheet." ) );
4041
4042 T token;
4044 FILL_PARAMS fill;
4045 SCH_FIELD* field;
4046 std::vector<SCH_FIELD> fields;
4047 std::unique_ptr<SCH_SHEET> sheet = std::make_unique<SCH_SHEET>();
4048 std::set<int> fieldIDsRead;
4049
4050 // We'll reset this if we find a fields_autoplaced token
4051 sheet->SetFieldsAutoplaced( AUTOPLACE_NONE );
4052
4053 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4054 {
4055 if( token != T_LEFT )
4056 Expecting( T_LEFT );
4057
4058 token = NextTok();
4059
4060 switch( token )
4061 {
4062 case T_at:
4063 sheet->SetPosition( parseXY() );
4064 NeedRIGHT();
4065 break;
4066
4067 case T_size:
4068 {
4069 VECTOR2I size;
4070 size.x = parseInternalUnits( "sheet width" );
4071 size.y = parseInternalUnits( "sheet height" );
4072 sheet->SetSize( size );
4073 NeedRIGHT();
4074 break;
4075 }
4076
4077 case T_exclude_from_sim:
4078 sheet->SetExcludedFromSim( parseBool() );
4079 NeedRIGHT();
4080 break;
4081
4082 case T_in_bom:
4083 sheet->SetExcludedFromBOM( !parseBool() );
4084 NeedRIGHT();
4085 break;
4086
4087 case T_on_board:
4088 sheet->SetExcludedFromBoard( !parseBool() );
4089 NeedRIGHT();
4090 break;
4091
4092 case T_dnp:
4093 sheet->SetDNP( parseBool() );
4094 NeedRIGHT();
4095 break;
4096
4097 case T_locked:
4098 sheet->SetLocked( parseBool() );
4099 NeedRIGHT();
4100 break;
4101
4102 case T_fields_autoplaced:
4103 if( parseMaybeAbsentBool( true ) )
4104 sheet->SetFieldsAutoplaced( AUTOPLACE_AUTO );
4105
4106 break;
4107
4108 case T_stroke:
4109 parseStroke( stroke );
4110 sheet->SetBorderWidth( stroke.GetWidth() );
4111 sheet->SetBorderColor( stroke.GetColor() );
4112 break;
4113
4114 case T_fill:
4115 parseFill( fill );
4116 sheet->SetBackgroundColor( fill.m_Color );
4117 break;
4118
4119 case T_uuid:
4120 NeedSYMBOL();
4121 const_cast<KIID&>( sheet->m_Uuid ) = parseKIID();
4122 NeedRIGHT();
4123 break;
4124
4125 case T_property:
4126 field = parseSchField( sheet.get() );
4127
4128 if( m_requiredVersion <= 20200310 )
4129 {
4130 // Earlier versions had the wrong ids (and names) saved for sheet fields.
4131 // Fortunately they only saved the sheetname and sheetfilepath (and always
4132 // in that order), so we can hack in a recovery.
4133 if( fields.empty() )
4134 field->setId( FIELD_T::SHEET_NAME );
4135 else
4137 }
4138
4139 fields.emplace_back( *field );
4140
4141 delete field;
4142 break;
4143
4144 case T_pin:
4145 sheet->AddPin( parseSchSheetPin( sheet.get() ) );
4146 break;
4147
4148 case T_instances:
4149 {
4150 std::vector<SCH_SHEET_INSTANCE> instances;
4151
4152 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4153 {
4154 if( token != T_LEFT )
4155 Expecting( T_LEFT );
4156
4157 token = NextTok();
4158
4159 if( token != T_project )
4160 Expecting( "project" );
4161
4162 NeedSYMBOL();
4163
4164 wxString projectName = FromUTF8();
4165
4166 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4167 {
4168 if( token != T_LEFT )
4169 Expecting( T_LEFT );
4170
4171 token = NextTok();
4172
4173 if( token != T_path )
4174 Expecting( "path" );
4175
4176 SCH_SHEET_INSTANCE instance;
4177
4178 instance.m_ProjectName = projectName;
4179
4180 NeedSYMBOL();
4181 instance.m_Path = KIID_PATH( FromUTF8() );
4182
4183 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4184 {
4185 if( token != T_LEFT )
4186 Expecting( T_LEFT );
4187
4188 token = NextTok();
4189
4190 switch( token )
4191 {
4192 case T_page:
4193 {
4194 NeedSYMBOL();
4195 instance.m_PageNumber = FromUTF8();
4196
4197 // Empty page numbers are not permitted
4198 if( instance.m_PageNumber.IsEmpty() )
4199 {
4200 // Use hash character instead
4201 instance.m_PageNumber = wxT( "#" );
4202 }
4203 else
4204 {
4205 // Whitespaces are not permitted
4206 static std::vector<wxString> whitespaces =
4207 { wxT( "\r" ),
4208 wxT( "\n" ),
4209 wxT( "\t" ),
4210 wxT( " " ) };
4211
4212 for( wxString ch : whitespaces )
4213 instance.m_PageNumber.Replace( ch, wxEmptyString );
4214 }
4215
4216 NeedRIGHT();
4217 break;
4218 }
4219
4220 case T_variant:
4221 {
4222 SCH_SHEET_VARIANT variant;
4223 variant.InitializeAttributes( *sheet );
4224
4225 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4226 {
4227 if( token != T_LEFT )
4228 Expecting( T_LEFT );
4229
4230 token = NextTok();
4231
4232 switch( token )
4233 {
4234 case T_name:
4235 NeedSYMBOL();
4236 variant.m_Name = FromUTF8();
4237 NeedRIGHT();
4238 break;
4239
4240 case T_dnp:
4241 variant.m_DNP = parseBool();
4242 NeedRIGHT();
4243 break;
4244
4245 case T_exclude_from_sim:
4246 variant.m_ExcludedFromSim = parseBool();
4247 NeedRIGHT();
4248 break;
4249
4250 case T_in_bom:
4251 variant.m_ExcludedFromBOM = parseBool();
4252
4253 // This fixes the incorrect logic from prior file versions. The "in_bom" token
4254 // used in the file format is the positive logic. However, in the UI the term
4255 // excluded from BOM is used which is the inverted logic.
4256 if( m_requiredVersion >= 20260306 )
4257 variant.m_ExcludedFromBOM = !variant.m_ExcludedFromBOM;
4258
4259 NeedRIGHT();
4260 break;
4261
4262 case T_on_board:
4263 variant.m_ExcludedFromBoard = !parseBool();
4264 NeedRIGHT();
4265 break;
4266
4267 case T_in_pos_files:
4268 variant.m_ExcludedFromPosFiles = !parseBool();
4269 NeedRIGHT();
4270 break;
4271
4272 case T_field:
4273 {
4274 wxString fieldName;
4275 wxString fieldValue;
4276
4277 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4278 {
4279 if( token != T_LEFT )
4280 Expecting( T_LEFT );
4281
4282 token = NextTok();
4283
4284 switch( token )
4285 {
4286 case T_name:
4287 NeedSYMBOL();
4288 fieldName = FromUTF8();
4289 NeedRIGHT();
4290 break;
4291
4292 case T_value:
4293 NeedSYMBOL();
4294 fieldValue = FromUTF8();
4295 NeedRIGHT();
4296 break;
4297
4298 default:
4299 Expecting( "name or value" );
4300 }
4301 }
4302
4303 variant.m_Fields[fieldName] = fieldValue;
4304 break;
4305 }
4306
4307 default:
4308 Expecting( "dnp, exclude_from_sim, field, in_bom, in_pos_files, name, or on_board" );
4309 }
4310
4311 instance.m_Variants[variant.m_Name] = variant;
4312 }
4313
4314 break;
4315 }
4316
4317 default:
4318 Expecting( "page or variant" );
4319 }
4320 }
4321
4322 instances.emplace_back( instance );
4323 }
4324 }
4325
4326 sheet->setInstances( instances );
4327 break;
4328 }
4329
4330 default:
4331 Expecting( "at, size, stroke, background, instances, uuid, property, or pin" );
4332 }
4333 }
4334
4335 sheet->SetFields( fields );
4336
4337 if( !FindField( sheet->GetFields(), FIELD_T::SHEET_NAME ) )
4338 {
4339 THROW_PARSE_ERROR( _( "Missing sheet name property" ), CurSource(), CurLine(),
4340 CurLineNumber(), CurOffset() );
4341 }
4342
4343 if( !FindField( sheet->GetFields(), FIELD_T::SHEET_FILENAME ) )
4344 {
4345 THROW_PARSE_ERROR( _( "Missing sheet file property" ), CurSource(), CurLine(),
4346 CurLineNumber(), CurOffset() );
4347 }
4348
4349 return sheet.release();
4350}
4351
4352
4354{
4355 wxCHECK_MSG( CurTok() == T_junction, nullptr,
4356 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a junction." ) );
4357
4358 T token;
4359 std::unique_ptr<SCH_JUNCTION> junction = std::make_unique<SCH_JUNCTION>();
4360
4361 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4362 {
4363 if( token != T_LEFT )
4364 Expecting( T_LEFT );
4365
4366 token = NextTok();
4367
4368 switch( token )
4369 {
4370 case T_at:
4371 junction->SetPosition( parseXY() );
4372 NeedRIGHT();
4373 break;
4374
4375 case T_diameter:
4376 junction->SetDiameter( parseInternalUnits( "junction diameter" ) );
4377 NeedRIGHT();
4378 break;
4379
4380 case T_color:
4381 {
4382 COLOR4D color;
4383
4384 color.r = parseInt( "red" ) / 255.0;
4385 color.g = parseInt( "green" ) / 255.0;
4386 color.b = parseInt( "blue" ) / 255.0;
4387 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
4388
4389 junction->SetColor( color );
4390 NeedRIGHT();
4391 break;
4392 }
4393
4394 case T_uuid:
4395 NeedSYMBOL();
4396 const_cast<KIID&>( junction->m_Uuid ) = parseKIID();
4397 NeedRIGHT();
4398 break;
4399
4400 case T_locked:
4401 junction->SetLocked( parseBool() );
4402 NeedRIGHT();
4403 break;
4404
4405 default:
4406 Expecting( "at, diameter, color, uuid or locked" );
4407 }
4408 }
4409
4410 return junction.release();
4411}
4412
4413
4415{
4416 wxCHECK_MSG( CurTok() == T_no_connect, nullptr,
4417 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a no connect." ) );
4418
4419 T token;
4420 std::unique_ptr<SCH_NO_CONNECT> no_connect = std::make_unique<SCH_NO_CONNECT>();
4421
4422 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4423 {
4424 if( token != T_LEFT )
4425 Expecting( T_LEFT );
4426
4427 token = NextTok();
4428
4429 switch( token )
4430 {
4431 case T_at:
4432 no_connect->SetPosition( parseXY() );
4433 NeedRIGHT();
4434 break;
4435
4436 case T_uuid:
4437 NeedSYMBOL();
4438 const_cast<KIID&>( no_connect->m_Uuid ) = parseKIID();
4439 NeedRIGHT();
4440 break;
4441
4442 case T_locked:
4443 no_connect->SetLocked( parseBool() );
4444 NeedRIGHT();
4445 break;
4446
4447 default:
4448 Expecting( "at, uuid or locked" );
4449 }
4450 }
4451
4452 return no_connect.release();
4453}
4454
4455
4457{
4458 wxCHECK_MSG( CurTok() == T_bus_entry, nullptr,
4459 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus entry." ) );
4460
4461 T token;
4463 std::unique_ptr<SCH_BUS_WIRE_ENTRY> busEntry = std::make_unique<SCH_BUS_WIRE_ENTRY>();
4464
4465 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4466 {
4467 if( token != T_LEFT )
4468 Expecting( T_LEFT );
4469
4470 token = NextTok();
4471
4472 switch( token )
4473 {
4474 case T_at:
4475 busEntry->SetPosition( parseXY() );
4476 NeedRIGHT();
4477 break;
4478
4479 case T_size:
4480 {
4481 VECTOR2I size;
4482
4483 size.x = parseInternalUnits( "bus entry height" );
4484 size.y = parseInternalUnits( "bus entry width" );
4485 busEntry->SetSize( size );
4486 NeedRIGHT();
4487 break;
4488 }
4489
4490 case T_stroke:
4491 parseStroke( stroke );
4492 busEntry->SetStroke( stroke );
4493 break;
4494
4495 case T_uuid:
4496 NeedSYMBOL();
4497 const_cast<KIID&>( busEntry->m_Uuid ) = parseKIID();
4498 NeedRIGHT();
4499 break;
4500
4501 case T_locked:
4502 busEntry->SetLocked( parseBool() );
4503 NeedRIGHT();
4504 break;
4505
4506 default:
4507 Expecting( "at, size, uuid, stroke or locked" );
4508 }
4509 }
4510
4511 return busEntry.release();
4512}
4513
4514
4516{
4517 if( aShape->GetFillMode() == FILL_T::FILLED_SHAPE )
4518 {
4519 aShape->SetFillColor( aShape->GetStroke().GetColor() );
4521 }
4522}
4523
4524
4526{
4527 T token;
4529 FILL_PARAMS fill;
4530
4531 std::unique_ptr<SCH_SHAPE> polyline = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_NOTES );
4532
4533 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4534 {
4535 if( token != T_LEFT )
4536 Expecting( T_LEFT );
4537
4538 token = NextTok();
4539
4540 switch( token )
4541 {
4542 case T_pts:
4543 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4544 {
4545 if( token != T_LEFT )
4546 Expecting( T_LEFT );
4547
4548 token = NextTok();
4549
4550 if( token != T_xy )
4551 Expecting( "xy" );
4552
4553 polyline->AddPoint( parseXY() );
4554
4555 NeedRIGHT();
4556 }
4557 break;
4558
4559 case T_stroke:
4560 parseStroke( stroke );
4561
4562 // In 6.0, the default schematic line style was Dashed.
4563 if( m_requiredVersion <= 20211123 && stroke.GetLineStyle() == LINE_STYLE::DEFAULT )
4565
4566 polyline->SetStroke( stroke );
4567 break;
4568
4569 case T_fill:
4570 parseFill( fill );
4571 polyline->SetFillMode( fill.m_FillType );
4572 polyline->SetFillColor( fill.m_Color );
4573 fixupSchFillMode( polyline.get() );
4574 break;
4575
4576 case T_uuid:
4577 NeedSYMBOL();
4578 const_cast<KIID&>( polyline->m_Uuid ) = parseKIID();
4579 NeedRIGHT();
4580 break;
4581
4582 case T_locked:
4583 polyline->SetLocked( parseBool() );
4584 NeedRIGHT();
4585 break;
4586
4587 default:
4588 Expecting( "pts, uuid, stroke, fill or locked" );
4589 }
4590 }
4591
4592 return polyline.release();
4593}
4594
4595
4597{
4598 // Note: T_polyline is deprecated in this code: it is now handled by
4599 // parseSchPolyLine() that can handle true polygons, and not only one segment.
4600
4601 T token;
4603 int layer;
4604
4605 switch( CurTok() )
4606 {
4607 case T_polyline: layer = LAYER_NOTES; break;
4608 case T_wire: layer = LAYER_WIRE; break;
4609 case T_bus: layer = LAYER_BUS; break;
4610 default:
4611 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as a line." );
4612 }
4613
4614 std::unique_ptr<SCH_LINE> line = std::make_unique<SCH_LINE>( VECTOR2I(), layer );
4615
4616 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4617 {
4618 if( token != T_LEFT )
4619 Expecting( T_LEFT );
4620
4621 token = NextTok();
4622
4623 switch( token )
4624 {
4625 case T_pts:
4626 NeedLEFT();
4627 token = NextTok();
4628
4629 if( token != T_xy )
4630 Expecting( "xy" );
4631
4632 line->SetStartPoint( parseXY() );
4633 NeedRIGHT();
4634 NeedLEFT();
4635 token = NextTok();
4636
4637 if( token != T_xy )
4638 Expecting( "xy" );
4639
4640 line->SetEndPoint( parseXY() );
4641 NeedRIGHT();
4642 NeedRIGHT();
4643 break;
4644
4645 case T_stroke:
4646 parseStroke( stroke );
4647 line->SetStroke( stroke );
4648 break;
4649
4650 case T_uuid:
4651 NeedSYMBOL();
4652 const_cast<KIID&>( line->m_Uuid ) = parseKIID();
4653 NeedRIGHT();
4654 break;
4655
4656 case T_locked:
4657 line->SetLocked( parseBool() );
4658 NeedRIGHT();
4659 break;
4660
4661 default:
4662 Expecting( "pts, uuid, stroke or locked" );
4663 }
4664 }
4665
4666 return line.release();
4667}
4668
4669
4671{
4672 wxCHECK_MSG( CurTok() == T_arc, nullptr,
4673 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc." ) );
4674
4675 T token;
4676 VECTOR2I startPoint;
4677 VECTOR2I midPoint;
4678 VECTOR2I endPoint;
4680 FILL_PARAMS fill;
4681 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC );
4682
4683 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4684 {
4685 if( token != T_LEFT )
4686 Expecting( T_LEFT );
4687
4688 token = NextTok();
4689
4690 switch( token )
4691 {
4692 case T_start:
4693 startPoint = parseXY();
4694 NeedRIGHT();
4695 break;
4696
4697 case T_mid:
4698 midPoint = parseXY();
4699 NeedRIGHT();
4700 break;
4701
4702 case T_end:
4703 endPoint = parseXY();
4704 NeedRIGHT();
4705 break;
4706
4707 case T_stroke:
4708 parseStroke( stroke );
4709 arc->SetStroke( stroke );
4710 break;
4711
4712 case T_fill:
4713 parseFill( fill );
4714 arc->SetFillMode( fill.m_FillType );
4715 arc->SetFillColor( fill.m_Color );
4716 fixupSchFillMode( arc.get() );
4717 break;
4718
4719 case T_uuid:
4720 NeedSYMBOL();
4721 const_cast<KIID&>( arc->m_Uuid ) = parseKIID();
4722 NeedRIGHT();
4723 break;
4724
4725 case T_locked:
4726 arc->SetLocked( parseBool() );
4727 NeedRIGHT();
4728 break;
4729
4730 default:
4731 Expecting( "start, mid, end, stroke, fill, uuid or locked" );
4732 }
4733 }
4734
4735 arc->SetArcGeometry( startPoint, midPoint, endPoint );
4736
4737 return arc.release();
4738}
4739
4740
4742{
4743 wxCHECK_MSG( CurTok() == T_circle, nullptr,
4744 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle." ) );
4745
4746 T token;
4748 int radius = 0;
4750 FILL_PARAMS fill;
4751 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE );
4752
4753 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4754 {
4755 if( token != T_LEFT )
4756 Expecting( T_LEFT );
4757
4758 token = NextTok();
4759
4760 switch( token )
4761 {
4762 case T_center:
4763 center = parseXY();
4764 NeedRIGHT();
4765 break;
4766
4767 case T_radius:
4768 radius = parseInternalUnits( "radius length" );
4769 NeedRIGHT();
4770 break;
4771
4772 case T_stroke:
4773 parseStroke( stroke );
4774 circle->SetStroke( stroke );
4775 break;
4776
4777 case T_fill:
4778 parseFill( fill );
4779 circle->SetFillMode( fill.m_FillType );
4780 circle->SetFillColor( fill.m_Color );
4781 fixupSchFillMode( circle.get() );
4782 break;
4783
4784 case T_uuid:
4785 NeedSYMBOL();
4786 const_cast<KIID&>( circle->m_Uuid ) = parseKIID();
4787 NeedRIGHT();
4788 break;
4789
4790 case T_locked:
4791 circle->SetLocked( parseBool() );
4792 NeedRIGHT();
4793 break;
4794
4795 default:
4796 Expecting( "center, radius, stroke, fill, uuid or locked" );
4797 }
4798 }
4799
4800 circle->SetCenter( center );
4801 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
4802
4803 return circle.release();
4804}
4805
4806
4808{
4809 wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
4810 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle." ) );
4811
4812 T token;
4814 FILL_PARAMS fill;
4815 std::unique_ptr<SCH_SHAPE> rectangle = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE );
4816
4817 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4818 {
4819 if( token != T_LEFT )
4820 Expecting( T_LEFT );
4821
4822 token = NextTok();
4823
4824 switch( token )
4825 {
4826 case T_start:
4827 rectangle->SetPosition( parseXY() );
4828 NeedRIGHT();
4829 break;
4830
4831 case T_end:
4832 rectangle->SetEnd( parseXY() );
4833 NeedRIGHT();
4834 break;
4835
4836 case T_radius:
4837 rectangle->SetCornerRadius( parseDouble( "corner radius" ) * schIUScale.IU_PER_MM );
4838 NeedRIGHT();
4839 break;
4840
4841 case T_stroke:
4842 parseStroke( stroke );
4843 rectangle->SetStroke( stroke );
4844 break;
4845
4846 case T_fill:
4847 parseFill( fill );
4848 rectangle->SetFillMode( fill.m_FillType );
4849 rectangle->SetFillColor( fill.m_Color );
4850 fixupSchFillMode( rectangle.get() );
4851 break;
4852
4853 case T_uuid:
4854 NeedSYMBOL();
4855 const_cast<KIID&>( rectangle->m_Uuid ) = parseKIID();
4856 NeedRIGHT();
4857 break;
4858
4859 case T_locked:
4860 rectangle->SetLocked( parseBool() );
4861 NeedRIGHT();
4862 break;
4863
4864 default:
4865 Expecting( "start, end, stroke, fill, uuid or locked" );
4866 }
4867 }
4868
4869 return rectangle.release();
4870}
4871
4872
4874{
4875 wxCHECK_MSG( CurTok() == T_rule_area, nullptr,
4876 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rule area." ) );
4877
4878 T token;
4880 FILL_PARAMS fill;
4881 std::unique_ptr<SCH_RULE_AREA> ruleArea = std::make_unique<SCH_RULE_AREA>();
4882
4883 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4884 {
4885 if( token != T_LEFT )
4886 Expecting( T_LEFT );
4887
4888 token = NextTok();
4889
4890 switch( token )
4891 {
4892 case T_polyline:
4893 {
4894 std::unique_ptr<SCH_SHAPE> poly( parseSchPolyLine() );
4895 SHAPE_POLY_SET& sch_rule_poly = poly->GetPolyShape();
4896
4897 // The polygon must be closed, it is a schematic closed polyline:
4898 sch_rule_poly.Outline(0).SetClosed( true );
4899
4900 ruleArea->SetPolyShape( sch_rule_poly );
4901
4902 ruleArea->SetStroke( poly->GetStroke() );
4903 ruleArea->SetFillMode( poly->GetFillMode() );
4904 ruleArea->SetFillColor( poly->GetFillColor() );
4905
4906 // the uuid is saved to the shape but stored and saved out of the rulearea
4907 const_cast<KIID&>( ruleArea->m_Uuid ) = poly->m_Uuid;
4908 break;
4909 }
4910
4911 case T_exclude_from_sim:
4912 ruleArea->SetExcludedFromSim( parseBool() );
4913 NeedRIGHT();
4914 break;
4915
4916 case T_in_bom:
4917 ruleArea->SetExcludedFromBOM( !parseBool() );
4918 NeedRIGHT();
4919 break;
4920
4921 case T_on_board:
4922 ruleArea->SetExcludedFromBoard( !parseBool() );
4923 NeedRIGHT();
4924 break;
4925
4926 case T_dnp:
4927 ruleArea->SetDNP( parseBool() );
4928 NeedRIGHT();
4929 break;
4930
4931 case T_locked:
4932 ruleArea->SetLocked( parseBool() );
4933 NeedRIGHT();
4934 break;
4935
4936 default:
4937 Expecting( "exclude_from_sim, on_board, in_bom, dnp, locked, or polyline" );
4938 }
4939 }
4940
4941 return ruleArea.release();
4942}
4943
4944
4946{
4947 wxCHECK_MSG( CurTok() == T_bezier, nullptr,
4948 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
4949
4950 T token;
4952 FILL_PARAMS fill;
4953 std::unique_ptr<SCH_SHAPE> bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER );
4954
4955 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4956 {
4957 if( token != T_LEFT )
4958 Expecting( T_LEFT );
4959
4960 token = NextTok();
4961
4962 switch( token )
4963 {
4964 case T_pts:
4965 {
4966 int ii = 0;
4967
4968 for( token = NextTok(); token != T_RIGHT; token = NextTok(), ++ii )
4969 {
4970 if( token != T_LEFT )
4971 Expecting( T_LEFT );
4972
4973 token = NextTok();
4974
4975 if( token != T_xy )
4976 Expecting( "xy" );
4977
4978 switch( ii )
4979 {
4980 case 0: bezier->SetStart( parseXY() ); break;
4981 case 1: bezier->SetBezierC1( parseXY() ); break;
4982 case 2: bezier->SetBezierC2( parseXY() ); break;
4983 case 3: bezier->SetEnd( parseXY() ); break;
4984 default: Unexpected( "control point" ); break;
4985 }
4986
4987 NeedRIGHT();
4988 }
4989 }
4990 break;
4991
4992 case T_stroke:
4993 parseStroke( stroke );
4994 bezier->SetStroke( stroke );
4995 break;
4996
4997 case T_fill:
4998 parseFill( fill );
4999 bezier->SetFillMode( fill.m_FillType );
5000 bezier->SetFillColor( fill.m_Color );
5001 fixupSchFillMode( bezier.get() );
5002 break;
5003
5004 case T_uuid:
5005 NeedSYMBOL();
5006 const_cast<KIID&>( bezier->m_Uuid ) = parseKIID();
5007 NeedRIGHT();
5008 break;
5009
5010 case T_locked:
5011 bezier->SetLocked( parseBool() );
5012 NeedRIGHT();
5013 break;
5014
5015 default:
5016 Expecting( "pts, stroke, fill, uuid or locked" );
5017 }
5018 }
5019
5020 bezier->RebuildBezierToSegmentsPointsList( m_maxError );
5021
5022 return bezier.release();
5023}
5024
5025
5026void SCH_IO_KICAD_SEXPR_PARSER::parseEllipseBody( SCH_SHAPE* aShape, bool aIsArc, bool aIsSchematic,
5027 TSCHEMATIC_T::T aFirstTok )
5028{
5029 // Defaults for optional/missing fields. All fields are expected in a well-formed
5030 // file, but we fall back to safe defaults rather than rejecting on missing fields
5031 // to be forward-compatible with future format additions.
5032 VECTOR2I center( 0, 0 );
5033 int majorRadius = 1;
5034 int minorRadius = 1;
5035 EDA_ANGLE rotation = ANGLE_0;
5036 EDA_ANGLE startAngle = ANGLE_0;
5037 EDA_ANGLE endAngle = ANGLE_90;
5039 FILL_PARAMS fill;
5040
5041 for( T token = aFirstTok; token != T_RIGHT; token = NextTok() )
5042 {
5043 if( token != T_LEFT )
5044 Expecting( T_LEFT );
5045
5046 token = NextTok();
5047
5048 switch( token )
5049 {
5050 case T_center:
5051 // symbol parsers called parseXY( true ); sch parsers used the default.
5052 center = parseXY( !aIsSchematic );
5053 NeedRIGHT();
5054 break;
5055
5056 case T_major_radius:
5057 majorRadius = parseInternalUnits( "major radius" );
5058 NeedRIGHT();
5059 break;
5060
5061 case T_minor_radius:
5062 minorRadius = parseInternalUnits( "minor radius" );
5063 NeedRIGHT();
5064 break;
5065
5066 case T_rotation_angle:
5067 rotation = EDA_ANGLE( parseDouble( "rotation angle" ), DEGREES_T );
5068 NeedRIGHT();
5069 break;
5070
5071 case T_start_angle:
5072 if( !aIsArc )
5073 Expecting( "start_angle only valid inside ellipse_arc" );
5074 startAngle = EDA_ANGLE( parseDouble( "start angle" ), DEGREES_T );
5075 NeedRIGHT();
5076 break;
5077
5078 case T_end_angle:
5079 if( !aIsArc )
5080 Expecting( "end_angle only valid inside ellipse_arc" );
5081 endAngle = EDA_ANGLE( parseDouble( "end angle" ), DEGREES_T );
5082 NeedRIGHT();
5083 break;
5084
5085 case T_stroke:
5086 parseStroke( stroke );
5087 aShape->SetStroke( stroke );
5088 break;
5089
5090 case T_fill:
5091 parseFill( fill );
5092 aShape->SetFillMode( fill.m_FillType );
5093 aShape->SetFillColor( fill.m_Color );
5094 if( aIsSchematic )
5095 fixupSchFillMode( aShape );
5096 break;
5097
5098 case T_uuid:
5099 if( !aIsSchematic )
5100 Expecting( "uuid only valid in schematic context" );
5101 NeedSYMBOL();
5102 const_cast<KIID&>( aShape->m_Uuid ) = KIID( FromUTF8() );
5103 NeedRIGHT();
5104 break;
5105
5106 case T_locked:
5107 if( !aIsSchematic )
5108 Expecting( "locked only valid in schematic context" );
5109 aShape->SetLocked( parseBool() );
5110 NeedRIGHT();
5111 break;
5112
5113 default:
5114 Expecting( "center, major_radius, minor_radius, rotation_angle, "
5115 "start_angle, end_angle, stroke, fill, uuid, or locked" );
5116 }
5117 }
5118
5119 aShape->SetEllipseCenter( center );
5120 aShape->SetEllipseMajorRadius( majorRadius );
5121 aShape->SetEllipseMinorRadius( minorRadius );
5122 aShape->SetEllipseRotation( rotation );
5123
5124 if( aIsArc )
5125 {
5126 aShape->SetEllipseStartAngle( startAngle );
5127 aShape->SetEllipseEndAngle( endAngle );
5128 }
5129}
5130
5131
5133{
5134 wxCHECK_MSG( CurTok() == T_ellipse, nullptr,
5135 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an ellipse." ) );
5136
5137 auto ellipse = std::make_unique<SCH_SHAPE>( SHAPE_T::ELLIPSE );
5138 parseEllipseBody( ellipse.get(), /*isArc*/ false, /*isSchematic*/ true, NextTok() );
5139 return ellipse.release();
5140}
5141
5142
5144{
5145 wxCHECK_MSG( CurTok() == T_ellipse_arc, nullptr,
5146 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an elliptical arc." ) );
5147
5148 auto arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ELLIPSE_ARC );
5149 parseEllipseBody( arc.get(), /*isArc*/ true, /*isSchematic*/ true, NextTok() );
5150 return arc.release();
5151}
5152
5153
5155{
5156 T token;
5157 std::unique_ptr<SCH_TEXT> text;
5158
5159 switch( CurTok() )
5160 {
5161 case T_text: text = std::make_unique<SCH_TEXT>(); break;
5162 case T_label: text = std::make_unique<SCH_LABEL>(); break;
5163 case T_global_label: text = std::make_unique<SCH_GLOBALLABEL>(); break;
5164 case T_hierarchical_label: text = std::make_unique<SCH_HIERLABEL>(); break;
5165 case T_netclass_flag: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
5166 case T_directive_label: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
5167 default:
5168 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as text." );
5169 }
5170
5171 // We'll reset this if we find a fields_autoplaced token
5172 text->SetFieldsAutoplaced( AUTOPLACE_NONE );
5173
5174 NeedSYMBOL();
5175
5176 text->SetText( FromUTF8() );
5177
5178 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5179 {
5180 if( token != T_LEFT )
5181 Expecting( T_LEFT );
5182
5183 token = NextTok();
5184
5185 switch( token )
5186 {
5187 case T_exclude_from_sim:
5188 text->SetExcludedFromSim( parseBool() );
5189 NeedRIGHT();
5190 break;
5191
5192 case T_at:
5193 text->SetPosition( parseXY() );
5194 text->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ).KeepUpright() );
5195
5196 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() ) )
5197 {
5198 switch( static_cast<int>( label->GetTextAngle().AsDegrees() ) )
5199 {
5200 case 0: label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
5201 case 90: label->SetSpinStyle( SPIN_STYLE::UP ); break;
5202 case 180: label->SetSpinStyle( SPIN_STYLE::LEFT ); break;
5203 case 270: label->SetSpinStyle( SPIN_STYLE::BOTTOM ); break;
5204 default: wxFAIL; label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
5205 }
5206 }
5207
5208 NeedRIGHT();
5209 break;
5210
5211 case T_shape:
5212 {
5213 if( text->Type() == SCH_TEXT_T || text->Type() == SCH_LABEL_T )
5214 Unexpected( T_shape );
5215
5216 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( text.get() );
5217
5218 token = NextTok();
5219
5220 switch( token )
5221 {
5222 case T_input: label->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
5223 case T_output: label->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
5224 case T_bidirectional: label->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
5225 case T_tri_state: label->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE ); break;
5226 case T_passive: label->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
5227 case T_dot: label->SetShape( LABEL_FLAG_SHAPE::F_DOT ); break;
5228 case T_round: label->SetShape( LABEL_FLAG_SHAPE::F_ROUND ); break;
5229 case T_diamond: label->SetShape( LABEL_FLAG_SHAPE::F_DIAMOND ); break;
5230 case T_rectangle: label->SetShape( LABEL_FLAG_SHAPE::F_RECTANGLE ); break;
5231 default:
5232 Expecting( "input, output, bidirectional, tri_state, passive, dot, round, diamond"
5233 "or rectangle" );
5234 }
5235
5236 NeedRIGHT();
5237 break;
5238 }
5239
5240 case T_length:
5241 {
5242 if( text->Type() != SCH_DIRECTIVE_LABEL_T )
5243 Unexpected( T_length );
5244
5245 SCH_DIRECTIVE_LABEL* label = static_cast<SCH_DIRECTIVE_LABEL*>( text.get() );
5246
5247 label->SetPinLength( parseInternalUnits( "pin length" ) );
5248 NeedRIGHT();
5249 }
5250 break;
5251
5252 case T_fields_autoplaced:
5253 if( parseMaybeAbsentBool( true ) )
5254 text->SetFieldsAutoplaced( AUTOPLACE_AUTO );
5255
5256 break;
5257
5258 case T_effects:
5259 parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ), true );
5260
5261 // Hidden schematic text is no longer supported
5262 text->SetVisible( true );
5263 break;
5264
5265 case T_iref: // legacy format; current is a T_property (aka SCH_FIELD)
5266 if( text->Type() == SCH_GLOBAL_LABEL_T )
5267 {
5268 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
5269 SCH_FIELD* field = label->GetField( FIELD_T::INTERSHEET_REFS );
5270
5271 field->SetTextPos( parseXY() );
5272 NeedRIGHT();
5273
5274 field->SetVisible( true );
5275 }
5276 break;
5277
5278 case T_uuid:
5279 NeedSYMBOL();
5280 const_cast<KIID&>( text->m_Uuid ) = parseKIID();
5281 NeedRIGHT();
5282 break;
5283
5284 case T_property:
5285 {
5286 if( text->Type() == SCH_TEXT_T )
5287 Unexpected( T_property );
5288
5289 SCH_FIELD* field = parseSchField( text.get() );
5290
5291 // Intersheetrefs is a mandatory field and so will already exist
5292 if( text->Type() == SCH_GLOBAL_LABEL_T && field->IsMandatory() )
5293 {
5294 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
5295 *label->GetField( field->GetId() ) = *field;
5296 }
5297 else
5298 {
5299 static_cast<SCH_LABEL_BASE*>( text.get() )->GetFields().emplace_back( *field );
5300 }
5301
5302 delete field;
5303 break;
5304 }
5305
5306 case T_locked:
5307 text->SetLocked( parseBool() );
5308 NeedRIGHT();
5309 break;
5310
5311 default:
5312 Expecting( "at, shape, iref, uuid, effects or locked" );
5313 }
5314 }
5315
5316 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() );
5317
5318 if( label && label->GetFields().empty() )
5320
5321 return text.release();
5322}
5323
5324
5326{
5327 wxCHECK_MSG( CurTok() == T_text_box, nullptr,
5328 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text box." ) );
5329
5330 std::unique_ptr<SCH_TEXTBOX> textBox = std::make_unique<SCH_TEXTBOX>();
5331
5332 parseSchTextBoxContent( textBox.get() );
5333
5334 return textBox.release();
5335}
5336
5337
5339{
5340 wxCHECK_MSG( CurTok() == T_table_cell, nullptr,
5341 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table cell." ) );
5342
5343 std::unique_ptr<SCH_TABLECELL> cell = std::make_unique<SCH_TABLECELL>();
5344
5345 parseSchTextBoxContent( cell.get() );
5346
5347 return cell.release();
5348}
5349
5350
5352{
5353 T token;
5354 VECTOR2I pos;
5355 VECTOR2I end;
5356 VECTOR2I size;
5357 int left;
5358 int top;
5359 int right;
5360 int bottom;
5362 FILL_PARAMS fill;
5363 bool foundEnd = false;
5364 bool foundSize = false;
5365 bool foundMargins = false;
5366
5367 NeedSYMBOL();
5368
5369 aTextBox->SetText( FromUTF8() );
5370
5371 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5372 {
5373 if( token != T_LEFT )
5374 Expecting( T_LEFT );
5375
5376 token = NextTok();
5377
5378 switch( token )
5379 {
5380 case T_exclude_from_sim:
5381 aTextBox->SetExcludedFromSim( parseBool() );
5382 NeedRIGHT();
5383 break;
5384
5385 case T_start: // Legacy token during 6.99 development; fails to handle angle
5386 pos = parseXY();
5387 NeedRIGHT();
5388 break;
5389
5390 case T_end: // Legacy token during 6.99 development; fails to handle angle
5391 end = parseXY();
5392 foundEnd = true;
5393 NeedRIGHT();
5394 break;
5395
5396 case T_at:
5397 pos = parseXY();
5398 aTextBox->SetTextAngle( EDA_ANGLE( parseDouble( "textbox angle" ), DEGREES_T ) );
5399 NeedRIGHT();
5400 break;
5401
5402 case T_size:
5403 size = parseXY();
5404 foundSize = true;
5405 NeedRIGHT();
5406 break;
5407
5408 case T_span:
5409 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( aTextBox ) )
5410 {
5411 cell->SetColSpan( parseInt( "column span" ) );
5412 cell->SetRowSpan( parseInt( "row span" ) );
5413 }
5414 else
5415 {
5416 Expecting( "at, size, stroke, fill, effects or uuid" );
5417 }
5418
5419 NeedRIGHT();
5420 break;
5421
5422 case T_stroke:
5423 parseStroke( stroke );
5424 aTextBox->SetStroke( stroke );
5425 break;
5426
5427 case T_fill:
5428 parseFill( fill );
5429 aTextBox->SetFillMode( fill.m_FillType );
5430 aTextBox->SetFillColor( fill.m_Color );
5431 fixupSchFillMode( aTextBox );
5432 break;
5433
5434 case T_margins:
5435 parseMargins( left, top, right, bottom );
5436 aTextBox->SetMarginLeft( left );
5437 aTextBox->SetMarginTop( top );
5438 aTextBox->SetMarginRight( right );
5439 aTextBox->SetMarginBottom( bottom );
5440 foundMargins = true;
5441 NeedRIGHT();
5442 break;
5443
5444 case T_effects:
5445 parseEDA_TEXT( static_cast<EDA_TEXT*>( aTextBox ), false );
5446 break;
5447
5448 case T_uuid:
5449 NeedSYMBOL();
5450 const_cast<KIID&>( aTextBox->m_Uuid ) = parseKIID();
5451 NeedRIGHT();
5452 break;
5453
5454 case T_locked:
5455 aTextBox->SetLocked( parseBool() );
5456 NeedRIGHT();
5457 break;
5458
5459 default:
5460 if( dynamic_cast<SCH_TABLECELL*>( aTextBox ) != nullptr )
5461 Expecting( "at, size, stroke, fill, effects, span, uuid or locked" );
5462 else
5463 Expecting( "at, size, stroke, fill, effects, uuid or locked" );
5464 }
5465 }
5466
5467 aTextBox->SetPosition( pos );
5468
5469 if( foundEnd )
5470 aTextBox->SetEnd( end );
5471 else if( foundSize )
5472 aTextBox->SetEnd( pos + size );
5473 else
5474 Expecting( "size" );
5475
5476 if( !foundMargins )
5477 {
5478 int margin = aTextBox->GetLegacyTextMargin();
5479 aTextBox->SetMarginLeft( margin );
5480 aTextBox->SetMarginTop( margin );
5481 aTextBox->SetMarginRight( margin );
5482 aTextBox->SetMarginBottom( margin );
5483 }
5484}
5485
5486
5488{
5489 wxCHECK_MSG( CurTok() == T_table, nullptr,
5490 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table." ) );
5491
5492 T token;
5493 int defaultLineWidth = schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
5494 STROKE_PARAMS borderStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
5495 STROKE_PARAMS separatorsStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
5496 std::unique_ptr<SCH_TABLE> table = std::make_unique<SCH_TABLE>( defaultLineWidth );
5497
5498 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5499 {
5500 if( token != T_LEFT )
5501 Expecting( T_LEFT );
5502
5503 token = NextTok();
5504
5505 switch( token )
5506 {
5507 case T_column_count:
5508 table->SetColCount( parseInt( "column count" ) );
5509 NeedRIGHT();
5510 break;
5511
5512 case T_column_widths:
5513 {
5514 int col = 0;
5515
5516 while( ( token = NextTok() ) != T_RIGHT )
5517 table->SetColWidth( col++, parseInternalUnits() );
5518
5519 break;
5520 }
5521
5522 case T_row_heights:
5523 {
5524 int row = 0;
5525
5526 while( ( token = NextTok() ) != T_RIGHT )
5527 table->SetRowHeight( row++, parseInternalUnits() );
5528
5529 break;
5530 }
5531
5532 case T_cells:
5533 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5534 {
5535 if( token != T_LEFT )
5536 Expecting( T_LEFT );
5537
5538 token = NextTok();
5539
5540 if( token != T_table_cell )
5541 Expecting( "table_cell" );
5542
5543 table->AddCell( parseSchTableCell() );
5544 }
5545
5546 break;
5547
5548 case T_border:
5549 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5550 {
5551 if( token != T_LEFT )
5552 Expecting( T_LEFT );
5553
5554 token = NextTok();
5555
5556 switch( token )
5557 {
5558 case T_external:
5559 table->SetStrokeExternal( parseBool() );
5560 NeedRIGHT();
5561 break;
5562
5563 case T_header:
5564 table->SetStrokeHeaderSeparator( parseBool() );
5565 NeedRIGHT();
5566 break;
5567
5568 case T_stroke:
5569 parseStroke( borderStroke );
5570 table->SetBorderStroke( borderStroke );
5571 break;
5572
5573 default:
5574 Expecting( "external, header or stroke" );
5575 break;
5576 }
5577 }
5578
5579 break;
5580
5581 case T_separators:
5582 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5583 {
5584 if( token != T_LEFT )
5585 Expecting( T_LEFT );
5586
5587 token = NextTok();
5588
5589 switch( token )
5590 {
5591 case T_rows:
5592 table->SetStrokeRows( parseBool() );
5593 NeedRIGHT();
5594 break;
5595
5596 case T_cols:
5597 table->SetStrokeColumns( parseBool() );
5598 NeedRIGHT();
5599 break;
5600
5601 case T_stroke:
5602 parseStroke( separatorsStroke );
5603 table->SetSeparatorsStroke( separatorsStroke );
5604 break;
5605
5606 default:
5607 Expecting( "rows, cols, or stroke" );
5608 break;
5609 }
5610 }
5611
5612 break;
5613
5614 case T_uuid:
5615 NeedSYMBOL();
5616 const_cast<KIID&>( table->m_Uuid ) = parseKIID();
5617 NeedRIGHT();
5618 break;
5619
5620 case T_locked:
5621 table->SetLocked( parseBool() );
5622 NeedRIGHT();
5623 break;
5624
5625 default:
5626 Expecting( "columns, col_widths, row_heights, border, separators, uuid, locked, header or cells" );
5627 }
5628 }
5629
5630 if( !table->GetCell( 0, 0 ) )
5631 {
5632 THROW_PARSE_ERROR( _( "Invalid table: no cells defined" ), CurSource(), CurLine(), CurLineNumber(),
5633 CurOffset() );
5634 }
5635
5636 return table.release();
5637}
5638
5639
5641{
5642 wxCHECK_RET( CurTok() == T_bus_alias,
5643 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus alias." ) );
5644 wxCHECK( aScreen, /* void */ );
5645
5646 T token;
5647 std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>();
5648 wxString alias;
5649 wxString member;
5650
5651 NeedSYMBOL();
5652
5653 alias = FromUTF8();
5654
5655 if( m_requiredVersion < 20210621 )
5656 alias = ConvertToNewOverbarNotation( alias );
5657
5658 busAlias->SetName( alias );
5659
5660 NeedLEFT();
5661 token = NextTok();
5662
5663 if( token != T_members )
5664 Expecting( "members" );
5665
5666 token = NextTok();
5667
5668 while( token != T_RIGHT )
5669 {
5670 if( !IsSymbol( token ) )
5671 Expecting( "quoted string" );
5672
5673 member = FromUTF8();
5674
5675 if( m_requiredVersion < 20210621 )
5676 member = ConvertToNewOverbarNotation( member );
5677
5678 busAlias->AddMember( member );
5679
5680 token = NextTok();
5681 }
5682
5683 NeedRIGHT();
5684
5685 aScreen->AddBusAlias( busAlias );
5686}
5687
5688
5690{
5691 // (net_chain "name" [(from "ref" "pin")] [(to "ref" "pin")]
5692 // [(net_class "...")] [(color R G B A)] [(nets "n1" "n2" ...)])
5693 NeedSYMBOL();
5694 wxString name = FromUTF8();
5695
5696 wxString netClass;
5698 wxString fromRef, fromPin, toRef, toPin;
5699 std::set<wxString> memberNets;
5700
5701 for( T tok = NextTok(); tok != T_RIGHT; tok = NextTok() )
5702 {
5703 if( tok == T_LEFT )
5704 tok = NextTok();
5705
5706 if( tok == T_from )
5707 {
5708 NeedSYMBOLorNUMBER();
5709 fromRef = From_UTF8( CurText() );
5710 NeedSYMBOLorNUMBER();
5711 fromPin = From_UTF8( CurText() );
5712 NeedRIGHT();
5713 }
5714 else if( tok == T_to )
5715 {
5716 NeedSYMBOLorNUMBER();
5717 toRef = From_UTF8( CurText() );
5718 NeedSYMBOLorNUMBER();
5719 toPin = From_UTF8( CurText() );
5720 NeedRIGHT();
5721 }
5722 else if( tok == T_net_class )
5723 {
5724 NeedSYMBOLorNUMBER();
5725 netClass = FromUTF8();
5726 NeedRIGHT();
5727 }
5728 else if( tok == T_color )
5729 {
5730 int r = parseInt( "red" );
5731 int g = parseInt( "green" );
5732 int b = parseInt( "blue" );
5733 double al = parseDouble( "alpha" );
5734 color = COLOR4D( r / 255.0, g / 255.0, b / 255.0, al );
5735 NeedRIGHT();
5736 }
5737 else if( tok == T_nets )
5738 {
5739 for( T inner = NextTok(); inner != T_RIGHT; inner = NextTok() )
5740 {
5741 if( !IsSymbol( inner ) && inner != T_NUMBER )
5742 Expecting( "net name" );
5743
5744 memberNets.insert( FromUTF8() );
5745 }
5746 }
5747 else
5748 {
5749 // Skip unknown subsections
5750 int depth = 1;
5751
5752 while( depth > 0 )
5753 {
5754 T inner = NextTok();
5755
5756 if( inner == T_EOF )
5757 break;
5758
5759 if( inner == T_LEFT )
5760 ++depth;
5761 else if( inner == T_RIGHT )
5762 --depth;
5763 }
5764 }
5765 }
5766
5767 if( !fromRef.IsEmpty() && !toRef.IsEmpty() )
5768 {
5769 m_netChainTerminalRefs[name] = { { fromRef, fromPin }, { toRef, toPin } };
5770 }
5771
5772 if( !netClass.IsEmpty() )
5773 m_netChainNetClasses[name] = netClass;
5774
5775 if( color != KIGFX::COLOR4D::UNSPECIFIED )
5776 m_netChainColors[name] = color;
5777
5778 if( !memberNets.empty() )
5779 m_netChainMemberNets[name] = std::move( memberNets );
5780}
5781
5782
5784{
5785 T token;
5786
5787 while( ( token = NextTok() ) != T_RIGHT )
5788 {
5789 // This token is the Uuid of the item in the group.
5790 // Since groups are serialized at the end of the file/footprint, the Uuid should already
5791 // have been seen and exist in the board.
5792 KIID uuid( CurStr() );
5793 aGroupInfo.memberUuids.push_back( uuid );
5794 }
5795}
5796
5797
5799{
5800 wxCHECK_RET( CurTok() == T_group,
5801 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_GROUP." ) );
5802
5803 T token;
5804
5805 m_groupInfos.push_back( GROUP_INFO() );
5806 GROUP_INFO& groupInfo = m_groupInfos.back();
5807
5808 while( ( token = NextTok() ) != T_LEFT )
5809 {
5810 if( token == T_STRING )
5811 groupInfo.name = FromUTF8();
5812 else
5813 Expecting( "group name or locked" );
5814 }
5815
5816 for( ; token != T_RIGHT; token = NextTok() )
5817 {
5818 if( token != T_LEFT )
5819 Expecting( T_LEFT );
5820
5821 token = NextTok();
5822
5823 switch( token )
5824 {
5825 case T_uuid:
5826 NextTok();
5827 groupInfo.uuid = parseKIID();
5828 NeedRIGHT();
5829 break;
5830
5831 case T_lib_id:
5832 {
5833 token = NextTok();
5834
5835 if( !IsSymbol( token ) && token != T_NUMBER )
5836 Expecting( "symbol|number" );
5837
5838 LIB_ID libId;
5839 wxString name = FromUTF8();
5840 // Some symbol LIB_IDs have the '/' character escaped which can break
5841 // symbol links. The '/' character is no longer an illegal LIB_ID character so
5842 // it doesn't need to be escaped.
5843 name.Replace( "{slash}", "/" );
5844
5845 int bad_pos = groupInfo.libId.Parse( name );
5846
5847 if( bad_pos >= 0 )
5848 {
5849 if( static_cast<int>( name.size() ) > bad_pos )
5850 {
5851 wxString msg = wxString::Format( _( "Group library link %s contains invalid character '%c'" ), name,
5852 name[bad_pos] );
5853
5854 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
5855 }
5856
5857 THROW_PARSE_ERROR( _( "Invalid library ID" ), CurSource(), CurLine(), CurLineNumber(), CurOffset() );
5858 }
5859
5860 NeedRIGHT();
5861 break;
5862 }
5863
5864 case T_members:
5865 {
5866 parseGroupMembers( groupInfo );
5867 break;
5868 }
5869
5870 case T_locked:
5871 groupInfo.locked = parseBool();
5872 NeedRIGHT();
5873 break;
5874
5875 default:
5876 Expecting( "uuid, lib_id, members, locked" );
5877 }
5878 }
5879}
5880
5881
5883{
5884 if( !aParent )
5885 return;
5886
5887 auto getItem =
5888 [&]( const KIID& aId )
5889 {
5890 SCH_ITEM* aItem = nullptr;
5891
5892 for( SCH_ITEM* item : aParent->Items() )
5893 {
5894 if( item->m_Uuid == aId )
5895 {
5896 aItem = item;
5897 break;
5898 }
5899 }
5900
5901 return aItem;
5902 };
5903
5904 // Now that we've parsed the other Uuids in the file we can resolve the uuids referred
5905 // to in the group declarations we saw.
5906 //
5907 // First add all group objects so subsequent GetItem() calls for nested groups work.
5908 for( const GROUP_INFO& groupInfo : m_groupInfos )
5909 {
5910 SCH_GROUP* group = nullptr;
5911
5912 group = new SCH_GROUP( aParent );
5913 group->SetName( groupInfo.name );
5914
5915 const_cast<KIID&>( group->m_Uuid ) = groupInfo.uuid;
5916
5917 if( groupInfo.libId.IsValid() )
5918 group->SetDesignBlockLibId( groupInfo.libId );
5919
5920 group->SetLocked( groupInfo.locked );
5921
5922 aParent->Append( group );
5923 }
5924
5925 for( const GROUP_INFO& groupInfo : m_groupInfos )
5926 {
5927 SCH_GROUP* group = static_cast<SCH_GROUP*>( getItem( groupInfo.uuid ) );
5928
5929 if( group && group->Type() == SCH_GROUP_T )
5930 {
5931 for( const KIID& aUuid : groupInfo.memberUuids )
5932 {
5933 if( SCH_ITEM* gItem = getItem( aUuid ) )
5934 group->AddItem( gItem );
5935 }
5936 }
5937 }
5938
5939 aParent->GroupsSanityCheck( true );
5940}
5941
5942
5944{
5945 // Skip tokens until we exit the current S-expression block.
5946 // This is used for error recovery when parsing fails mid-symbol.
5947 while( aDepth > 0 )
5948 {
5949 T token = NextTok();
5950
5951 if( token == T_EOF )
5952 break;
5953 else if( token == T_LEFT )
5954 aDepth++;
5955 else if( token == T_RIGHT )
5956 aDepth--;
5957 }
5958}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
constexpr double ARC_LOW_DEF_MM
Definition base_units.h:127
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
void SetContentModified(bool aModified=true)
Definition base_screen.h:55
This class handle bitmap images in KiCad.
Definition bitmap_base.h:45
int GetPPI() const
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:398
EDA_ANGLE Normalize()
Definition eda_angle.h:229
const KIID m_Uuid
Definition eda_item.h:531
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
EDA_ITEM * GetParent() const
Definition eda_item.h:110
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:89
virtual void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:244
FILL_T GetFillMode() const
Definition eda_shape.h:158
SHAPE_POLY_SET & GetPolyShape()
virtual void SetEllipseEndAngle(const EDA_ANGLE &aA)
Definition eda_shape.h:331
virtual void SetEllipseRotation(const EDA_ANGLE &aA)
Definition eda_shape.h:312
void SetFillColor(const COLOR4D &aColor)
Definition eda_shape.h:170
int GetPointCount() const
virtual void SetEllipseCenter(const VECTOR2I &aPt)
Definition eda_shape.h:285
virtual void SetEllipseMinorRadius(int aR)
Definition eda_shape.h:303
virtual void SetEllipseMajorRadius(int aR)
Definition eda_shape.h:294
virtual void SetEllipseStartAngle(const EDA_ANGLE &aA)
Definition eda_shape.h:322
void SetFillMode(FILL_T aFill)
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:89
void SetTextColor(const COLOR4D &aColor)
Definition eda_text.h:290
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:532
void SetUnresolvedFontName(const wxString &aFontName)
Definition eda_text.h:270
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:110
virtual void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:576
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:412
void SetBoldFlag(bool aBold)
Set only the bold flag, without changing the font.
Definition eda_text.cpp:373
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:381
void SetLineSpacing(double aLineSpacing)
Definition eda_text.cpp:524
virtual void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:279
static bool ValidateHyperlink(const wxString &aURL)
Check if aURL is a valid hyperlink.
void SetItalicFlag(bool aItalic)
Set only the italic flag, without changing the font.
Definition eda_text.cpp:322
void SetHyperlink(wxString aLink)
Definition eda_text.h:425
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:265
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:294
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:404
void ParseEmbedded(EMBEDDED_FILES *aFiles)
const std::vector< wxString > * UpdateFontFiles()
Helper function to get a list of fonts for fontconfig to add to the library.
Simple container to manage fill parameters.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double r
Red component.
Definition color4d.h:389
double g
Green component.
Definition color4d.h:390
double a
Alpha component.
Definition color4d.h:392
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:398
double b
Blue component.
Definition color4d.h:391
Definition kiid.h:44
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:65
Define a library symbol object.
Definition lib_symbol.h:80
wxString GetName() const override
Definition lib_symbol.h:142
void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction, RECURSE_MODE aMode) override
EMBEDDED_FILES * GetEmbeddedFiles() override
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition richio.h:62
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
void SetPortrait(bool aIsPortrait)
Rotate the paper page 90 degrees.
bool SetType(PAGE_SIZE_TYPE aPageSize, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
void SetWidthMM(double aWidthInMM)
Definition page_info.h:136
void SetHeightMM(double aHeightInMM)
Definition page_info.h:141
const PAGE_SIZE_TYPE & GetType() const
Definition page_info.h:98
A symbol-owned ordered set of named pin maps.
Definition pin_map.h:123
void AddOrReplace(PIN_MAP aMap)
Insert aMap, replacing any existing entry with the same name.
Definition pin_map.cpp:113
A named pin map.
Definition pin_map.h:64
void SetEntry(const wxString &aPinNumber, const wxString &aPadNumber)
Set the pad number for a symbol pin.
Definition pin_map.cpp:59
A progress reporter interface for use in multi-threaded environments.
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
bool ReadImageFile(const wxString &aFullFilename)
Read and store an image file.
const BITMAP_BASE & GetImage() const
Get the underlying image.
double GetImageScale() const
void SetImageScale(double aScale)
Set the image "zoom" value.
Holds all the data relating to one schematic.
Definition schematic.h:90
Object to handle a bitmap image that can be inserted in a schematic.
Definition sch_bitmap.h:36
Class for a wire to bus entry.
void SetPinLength(int aLength)
Definition sch_label.h:482
bool IsMandatory() const
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:128
FIELD_T GetId() const
Definition sch_field.h:132
wxString GetCanonicalName() const
Get a non-language-specific name for a field which can be used for storage, variable look-up,...
void setId(FIELD_T aId)
wxString GetName(bool aUseDefaultName=true) const
Return the field name (not translated).
static bool IsNetclassLabelFieldName(const wxString &aName)
Test whether aName is one of the known translations of the directive-label net class field name (used...
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this label.
A set of SCH_ITEMs (i.e., without duplicates).
Definition sch_group.h:48
bool m_appending
Appending load status.
SCH_FIELD * parseSchField(SCH_ITEM *aParent)
void parseSchSymbolInstances(SCH_SCREEN *aScreen)
void parsePinNumbers(std::unique_ptr< LIB_SYMBOL > &aSymbol)
LIB_SYMBOL * parseLibSymbol(LIB_SYMBOL_MAP &aSymbolLibMap)
void parseEllipseBody(SCH_SHAPE *aShape, bool aIsArc, bool aIsSchematic, TSCHEMATIC_T::T aFirstTok)
void parseBusAlias(SCH_SCREEN *aScreen)
void parseTITLE_BLOCK(TITLE_BLOCK &aTitleBlock)
void parseGroupMembers(GROUP_INFO &aGroupInfo)
void parseStroke(STROKE_PARAMS &aStroke)
Parse stroke definition aStroke.
int m_unit
The current unit being parsed.
std::map< wxString, std::set< wxString > > m_netChainMemberNets
void parseEDA_TEXT(EDA_TEXT *aText, bool aConvertOverbarSyntax, bool aEnforceMinTextSize=true)
void resolveGroups(SCH_SCREEN *aParent)
std::map< wxString, wxString > m_netChainNetClasses
SCH_SHEET * m_rootSheet
The rootsheet for full project loads or null for importing a schematic.
void ParseLib(LIB_SYMBOL_MAP &aSymbolLibMap)
std::vector< GROUP_INFO > m_groupInfos
void parseHeader(TSCHEMATIC_T::T aHeaderType, int aFileVersion)
void parseBodyStyles(std::unique_ptr< LIB_SYMBOL > &aSymbol)
void parsePinMaps(std::unique_ptr< LIB_SYMBOL > &aSymbol)
void parseMargins(int &aLeft, int &aTop, int &aRight, int &aBottom)
wxString m_symbolName
The current symbol name.
std::map< wxString, CHAIN_TERMINALS > m_netChainTerminalRefs
VECTOR2I parseXY(bool aInvertY=false)
void parsePinNames(std::unique_ptr< LIB_SYMBOL > &aSymbol)
void parseAssociatedFootprints(std::unique_ptr< LIB_SYMBOL > &aSymbol)
std::vector< wxString > m_parseWarnings
Non-fatal warnings collected during parsing.
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.
PIN_MAP_INSTANCE_OVERRIDE parsePinMapOverride()
std::map< wxString, COLOR4D > m_netChainColors
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.
int m_maxError
Max deviation allowed when approximating bezier curves.
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 skipToBlockEnd(int aDepth=1)
Skip tokens until we reach the end of the current S-expression block.
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:162
void SetLocked(bool aLocked) override
Definition sch_item.h:251
bool IsLocked() const override
Definition sch_item.cpp:148
void SetFieldsAutoplaced(AUTOPLACE_ALGO aAlgo)
Definition sch_item.h:624
void SetShape(LABEL_FLAG_SHAPE aShape)
Definition sch_label.h:179
std::vector< SCH_FIELD > & GetFields()
Definition sch_label.h:210
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:38
void SetStartPoint(const VECTOR2I &aPosition)
Definition sch_line.h:136
virtual void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_line.h:198
void SetEndPoint(const VECTOR2I &aPosition)
Definition sch_line.h:145
void SetFileFormatVersionAtLoad(int aVersion)
Definition sch_screen.h:134
std::vector< SCH_SHEET_INSTANCE > m_sheetInstances
Definition sch_screen.h:724
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition sch_screen.h:164
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
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.
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition sch_screen.h:138
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:115
const KIID & GetUuid() const
Definition sch_screen.h:529
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.
SCHEMATIC * Schematic() const
void FixupEmbeddedData()
After loading a file from disk, the library symbols do not yet contain the full data for their embedd...
int GetFileFormatVersionAtLoad() const
Definition sch_screen.h:135
wxString GroupsSanityCheck(bool repair=false)
Consistency check of internal m_groups structure.
KIID m_uuid
A unique identifier for each schematic file.
Definition sch_screen.h:732
std::vector< SCH_SYMBOL_INSTANCE > m_symbolInstances
The list of symbol instances loaded from the schematic file.
Definition sch_screen.h:723
void SetPosition(const VECTOR2I &aPos) override
Definition sch_shape.h:85
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_shape.cpp:98
STROKE_PARAMS GetStroke() const override
Definition sch_shape.h:57
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.
Variant information for a schematic sheet.
void InitializeAttributes(const SCH_SHEET &aSheet)
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:44
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:139
Variant information for a schematic symbol.
PIN_MAP_INSTANCE_OVERRIDE m_PinMapOverride
Per-instance pin-to-pad map override for this variant (issue #2282).
void InitializeAttributes(const SCH_SYMBOL &aSymbol)
Schematic symbol object.
Definition sch_symbol.h:69
void SetMarginBottom(int aBottom)
Definition sch_textbox.h:67
int GetLegacyTextMargin() const
void SetMarginLeft(int aLeft)
Definition sch_textbox.h:64
void SetMarginRight(int aRight)
Definition sch_textbox.h:66
void SetExcludedFromSim(bool aExclude, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Definition sch_textbox.h:98
void SetMarginTop(int aTop)
Definition sch_textbox.h:65
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
void ParseStroke(STROKE_PARAMS &aStroke)
Simple container to manage line stroke parameters.
int GetWidth() const
void SetLineStyle(LINE_STYLE aLineStyle)
LINE_STYLE GetLineStyle() const
KIGFX::COLOR4D GetColor() const
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition title_block.h:37
void SetRevision(const wxString &aRevision)
Definition title_block.h:77
void SetComment(int aIdx, const wxString &aComment)
Definition title_block.h:97
void SetTitle(const wxString &aTitle)
Definition title_block.h:54
void SetCompany(const wxString &aCompany)
Definition title_block.h:87
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition title_block.h:67
for transforming drawing coordinates for a wxDC device context.
Definition transform.h:42
wxString m_Name
bool m_ExcludedFromBOM
std::map< wxString, wxString > m_Fields
bool m_ExcludedFromPosFiles
bool m_ExcludedFromSim
bool m_ExcludedFromBoard
#define DEFAULT_LINE_WIDTH_MILS
The default wire width in mils. (can be changed in preference menu)
@ DSN_LEFT
Definition dsnlexer.h:63
@ DSN_RIGHT
Definition dsnlexer.h:62
@ DSN_STRING
Definition dsnlexer.h:64
@ DSN_EOF
Definition dsnlexer.h:65
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
@ 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:417
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:415
@ NO_RECURSE
Definition eda_item.h:50
@ ELLIPSE
Definition eda_shape.h:52
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:47
@ ELLIPSE_ARC
Definition eda_shape.h:53
@ FILLED_WITH_COLOR
Definition eda_shape.h:63
@ NO_FILL
Definition eda_shape.h:60
@ REVERSE_HATCH
Definition eda_shape.h:65
@ HATCH
Definition eda_shape.h:64
@ FILLED_WITH_BG_BODYCOLOR
Definition eda_shape.h:62
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:61
@ CROSS_HATCH
Definition eda_shape.h:66
FONTCONFIG * Fontconfig()
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
KIID & NilUuid()
Definition kiid.cpp:66
@ LAYER_DEVICE
Definition layer_ids.h:464
@ LAYER_WIRE
Definition layer_ids.h:450
@ LAYER_NOTES
Definition layer_ids.h:465
@ LAYER_BUS
Definition layer_ids.h:451
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:79
#define MAX_PAGE_SIZE_EESCHEMA_MM
Definition page_info.h:37
#define MIN_PAGE_SIZE_MM
Min and max page sizes for clamping, in mm.
Definition page_info.h:35
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition pin_type.h:32
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:33
@ PT_NC
not connected (must be left open)
Definition pin_type.h:46
@ PT_OUTPUT
usual output
Definition pin_type.h:34
@ PT_TRISTATE
tri state bus pin
Definition pin_type.h:36
@ PT_NIC
not internally connected (may be connected to anything)
Definition pin_type.h:40
@ PT_BIDI
input or output (like port for a microprocessor)
Definition pin_type.h:35
@ PT_OPENEMITTER
pin type open emitter
Definition pin_type.h:45
@ PT_POWER_OUT
output of a regulator: intended to be connected to power input pins
Definition pin_type.h:43
@ PT_OPENCOLLECTOR
pin type open collector
Definition pin_type.h:44
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:42
@ PT_UNSPECIFIED
unknown electrical properties: creates always a warning when connected
Definition pin_type.h:41
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
Definition pin_type.h:39
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:123
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:107
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:114
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:131
GRAPHIC_PINSHAPE
Definition pin_type.h:80
const SCH_FIELD * FindField(const std::vector< SCH_FIELD > &aFields, FIELD_T aFieldId)
Definition sch_field.h:383
#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.
Class to handle a set of SCH_ITEMs.
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 ...
void fixupSchFillMode(SCH_SHAPE *aShape)
@ AUTOPLACE_NONE
Definition sch_item.h:66
@ AUTOPLACE_AUTO
Definition sch_item.h:67
@ L_BIDI
Definition sch_label.h:100
@ L_TRISTATE
Definition sch_label.h:101
@ L_UNSPECIFIED
Definition sch_label.h:102
@ F_DOT
Definition sch_label.h:105
@ F_ROUND
Definition sch_label.h:106
@ L_OUTPUT
Definition sch_label.h:99
@ F_DIAMOND
Definition sch_label.h:107
@ L_INPUT
Definition sch_label.h:98
@ F_RECTANGLE
Definition sch_label.h:108
#define SIM_LEGACY_ENABLE_FIELD
Definition sim_model.h:62
#define SIM_LEGACY_ENABLE_FIELD_V7
Definition sim_model.h:58
const int scale
wxString ConvertToNewOverbarNotation(const wxString &aOldStr)
Convert the old ~...~ overbar notation to the new ~{...} one.
wxString UnescapeString(const wxString &aSource)
wxString From_UTF8(const char *cstring)
A first-class footprint choice on a LIB_SYMBOL, tied to a named pin map.
Definition pin_map.h:158
LIB_ID m_FootprintLibId
Definition pin_map.h:159
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
A filename or source description, a problem input line, a line number, a byte offset,...
Per-instance override of the active pin map and a sparse delta on top.
Definition pin_map.h:199
wxString m_Name
Definition sch_pin.h:48
GRAPHIC_PINSHAPE m_Shape
Definition sch_pin.h:49
ELECTRICAL_PINTYPE m_Type
Definition sch_pin.h:50
A simple container for sheet instance information.
std::map< wxString, SCH_SHEET_VARIANT > m_Variants
A list of sheet variants.
A simple container for schematic symbol instance information.
std::map< wxString, SCH_SYMBOL_VARIANT > m_Variants
A list of symbol variants.
@ SYM_MIRROR_Y
Definition symbol.h:40
@ SYM_MIRROR_X
Definition symbol.h:39
std::map< wxString, LIB_SYMBOL *, LibSymbolMapSort > LIB_SYMBOL_MAP
#define MANDATORY_FIELDS
FIELD_T
The set of all field indices assuming an array like sequence that a SCH_COMPONENT or LIB_PART can hol...
@ USER
The field ID hasn't been set yet; field is invalid.
@ INTERSHEET_REFS
Global label cross-reference page numbers.
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
#define SHEET_MANDATORY_FIELDS
#define GLOBALLABEL_MANDATORY_FIELDS
wxString GetCanonicalFieldName(FIELD_T aFieldType)
KIBIS top(path, &reporter)
KIBIS_PIN * pin
std::vector< std::vector< std::string > > table
VECTOR2I center
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
static constexpr double IU_PER_MM
Mock up a conversion function.
@ 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:530
@ SCH_GROUP_T
Definition typeinfo.h:170
@ SCH_SYMBOL_T
Definition typeinfo.h:169
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:168
@ SCH_LABEL_T
Definition typeinfo.h:164
@ SCH_SHEET_T
Definition typeinfo.h:172
@ SCH_TEXT_T
Definition typeinfo.h:148
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:165
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683