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
1285{
1286 wxCHECK_MSG( CurTok() == T_symbol_override, LIB_ID(),
1287 "Cannot parse " + GetTokenString( CurTok() ) + " as a symbol_override token." );
1288
1289 T token = NextTok();
1290
1291 if( !IsSymbol( token ) )
1292 Expecting( "symbol LIB_ID" );
1293
1294 wxString name = FromUTF8();
1295 LIB_ID libId;
1296 int bad_pos = libId.Parse( name );
1297
1298 if( bad_pos >= 0 )
1299 {
1300 if( static_cast<int>( name.size() ) > bad_pos )
1301 {
1302 wxString msg = wxString::Format( _( "Variant symbol override contains invalid character '%c'" ),
1303 name[bad_pos] );
1304
1305 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
1306 }
1307
1308 THROW_PARSE_ERROR( _( "Invalid variant symbol override LIB_ID" ), CurSource(), CurLine(),
1309 CurLineNumber(), CurOffset() );
1310 }
1311
1312 NeedRIGHT();
1313 return libId;
1314}
1315
1316
1317SCH_FIELD* SCH_IO_KICAD_SEXPR_PARSER::parseProperty( std::unique_ptr<LIB_SYMBOL>& aSymbol )
1318{
1319 wxCHECK_MSG( CurTok() == T_property, nullptr,
1320 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a property." ) );
1321 wxCHECK( aSymbol, nullptr );
1322
1323 FIELD_T fieldId = FIELD_T::USER;
1324 wxString name;
1325 wxString value;
1326 bool isPrivate = false;
1327 bool isVisible = true;
1328
1329 T token = NextTok();
1330
1331 if( token == T_private )
1332 {
1333 isPrivate = true;
1334 token = NextTok();
1335 }
1336
1337 if( !IsSymbol( token ) )
1338 {
1339 THROW_PARSE_ERROR( _( "Invalid property name" ), CurSource(), CurLine(), CurLineNumber(),
1340 CurOffset() );
1341 }
1342
1343 name = FromUTF8();
1344
1345 if( name.IsEmpty() )
1346 {
1347 THROW_PARSE_ERROR( _( "Empty property name" ), CurSource(), CurLine(), CurLineNumber(),
1348 CurOffset() );
1349 }
1350
1351 // Correctly set the ID based on canonical (untranslated) field name
1352 for( FIELD_T id : MANDATORY_FIELDS )
1353 {
1354 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
1355 {
1356 fieldId = id;
1357 break;
1358 }
1359 }
1360
1361 auto field = std::make_unique<SCH_FIELD>( aSymbol.get(), fieldId, name );
1362 field->SetPrivate( isPrivate );
1363 field->SetVisible( isVisible );
1364
1365 token = NextTok();
1366
1367 if( !IsSymbol( token ) )
1368 {
1369 THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
1370 CurOffset() );
1371 }
1372
1373 // Empty property values are valid.
1374
1375 if( m_requiredVersion < 20250318 && CurStr() == "~" )
1376 value = wxEmptyString;
1377 else
1378 value = FromUTF8();
1379
1380 field->SetText( value );
1381
1382 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1383 {
1384 if( token != T_LEFT )
1385 Expecting( T_LEFT );
1386
1387 token = NextTok();
1388
1389 switch( token )
1390 {
1391 case T_id: // legacy token; ignore
1392 parseInt( "field ID" );
1393 NeedRIGHT();
1394 break;
1395
1396 case T_at:
1397 field->SetPosition( parseXY( true ) );
1398 field->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ) );
1399 NeedRIGHT();
1400 break;
1401
1402 case T_hide:
1403 field->SetVisible( !parseBool() );
1404 NeedRIGHT();
1405 break;
1406
1407 case T_effects:
1408 parseEDA_TEXT( static_cast<EDA_TEXT*>( field.get() ),
1409 field->GetId() == FIELD_T::VALUE );
1410 break;
1411
1412 case T_show_name:
1413 {
1414 bool show = parseMaybeAbsentBool( true );
1415 field->SetNameShown( show );
1416 break;
1417 }
1418
1419 case T_do_not_autoplace:
1420 {
1421 bool doNotAutoplace = parseMaybeAbsentBool( true );
1422 field->SetCanAutoplace( !doNotAutoplace );
1423 break;
1424 }
1425
1426 default:
1427 Expecting( "id, at, hide, show_name, do_not_autoplace, or effects" );
1428 }
1429 }
1430
1431 SCH_FIELD* existingField;
1432
1433 if( field->IsMandatory() )
1434 {
1435 existingField = aSymbol->GetField( field->GetId() );
1436
1437 *existingField = *field;
1438 return existingField;
1439 }
1440 else if( name == "ki_keywords" )
1441 {
1442 // Not a SCH_FIELD object yet.
1443 aSymbol->SetKeyWords( value );
1444 return nullptr;
1445 }
1446 // In v7 and earlier the description field didn't exist and was a key/value
1447 else if( name == "ki_description" )
1448 {
1449 aSymbol->SetDescription( value );
1450 return nullptr;
1451 }
1452 else if( name == "ki_fp_filters" )
1453 {
1454 // Not a SCH_FIELD object yet.
1455 wxArrayString filters;
1456 wxStringTokenizer tokenizer( value, " \t\r\n", wxTOKEN_STRTOK );
1457
1458 while( tokenizer.HasMoreTokens() )
1459 {
1460 wxString curr_token = UnescapeString( tokenizer.GetNextToken() );
1461 filters.Add( curr_token );
1462 }
1463
1464 aSymbol->SetFPFilters( filters );
1465 return nullptr;
1466 }
1467 else if( name == "ki_locked" )
1468 {
1469 // This is a temporary SCH_FIELD object until interchangeable units are determined on
1470 // the fly.
1471 aSymbol->LockUnits( true );
1472 return nullptr;
1473 }
1474 else
1475 {
1476 // At this point, a user field is read.
1477 existingField = aSymbol->GetField( field->GetCanonicalName() );
1478
1479#if 1 // Enable it to modify the name of the field to add if already existing
1480 // Disable it to skip the field having the same name as previous field
1481 if( existingField )
1482 {
1483 // We cannot handle 2 fields with the same name, so because the field name
1484 // is already in use, try to build a new name (oldname_x)
1485 wxString base_name = field->GetCanonicalName();
1486
1487 // Arbitrary limit 10 attempts to find a new name
1488 for( int ii = 1; ii < 10 && existingField; ii++ )
1489 {
1490 wxString newname = base_name;
1491 newname << '_' << ii;
1492
1493 existingField = aSymbol->GetField( newname );
1494
1495 if( !existingField ) // the modified name is not found, use it
1496 field->SetName( newname );
1497 }
1498 }
1499#endif
1500 if( !existingField )
1501 {
1502 aSymbol->AddDrawItem( field.get(), false );
1503 return field.release();
1504 }
1505 else
1506 {
1507 // We cannot handle 2 fields with the same name, so skip this one
1508 return nullptr;
1509 }
1510 }
1511}
1512
1513
1515{
1516 wxCHECK_MSG( CurTok() == T_arc, nullptr,
1517 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc." ) );
1518
1519 T token;
1520 VECTOR2I startPoint( 1, 0 ); // Initialize to a non-degenerate arc just for safety
1521 VECTOR2I midPoint( 1, 1 );
1522 VECTOR2I endPoint( 0, 1 );
1523 bool hasMidPoint = false;
1525 FILL_PARAMS fill;
1526
1527 // Parameters for legacy format
1528 VECTOR2I center( 0, 0 );
1529 EDA_ANGLE startAngle = ANGLE_0;
1530 EDA_ANGLE endAngle = ANGLE_90;
1531 bool hasAngles = false;
1532
1533 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC, LAYER_DEVICE );
1534
1535 arc->SetUnit( m_unit );
1536 arc->SetBodyStyle( m_bodyStyle );
1537
1538 token = NextTok();
1539
1540 if( token == T_private )
1541 {
1542 arc->SetPrivate( true );
1543 token = NextTok();
1544 }
1545
1546 for( ; token != T_RIGHT; token = NextTok() )
1547 {
1548 if( token != T_LEFT )
1549 Expecting( T_LEFT );
1550
1551 token = NextTok();
1552
1553 switch( token )
1554 {
1555 case T_start:
1556 startPoint = parseXY( true );
1557 NeedRIGHT();
1558 break;
1559
1560 case T_mid:
1561 midPoint = parseXY( true );
1562 NeedRIGHT();
1563 hasMidPoint = true;
1564 break;
1565
1566 case T_end:
1567 endPoint = parseXY( true );
1568 NeedRIGHT();
1569 break;
1570
1571 case T_radius:
1572 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1573 {
1574 if( token != T_LEFT )
1575 Expecting( T_LEFT );
1576
1577 token = NextTok();
1578
1579 switch( token )
1580 {
1581 case T_at:
1582 center = parseXY( true );
1583 NeedRIGHT();
1584 break;
1585
1586 case T_length:
1587 parseInternalUnits( "radius length" );
1588 NeedRIGHT();
1589 break;
1590
1591 case T_angles:
1592 {
1593 startAngle = EDA_ANGLE( parseDouble( "start radius angle" ), DEGREES_T );
1594 endAngle = EDA_ANGLE( parseDouble( "end radius angle" ), DEGREES_T );
1595 startAngle.Normalize();
1596 endAngle.Normalize();
1597 NeedRIGHT();
1598 hasAngles = true;
1599 break;
1600 }
1601
1602 default:
1603 Expecting( "at, length, or angles" );
1604 }
1605 }
1606
1607 break;
1608
1609 case T_stroke:
1610 parseStroke( stroke );
1611 arc->SetStroke( stroke );
1612 break;
1613
1614 case T_fill:
1615 parseFill( fill );
1616 arc->SetFillMode( fill.m_FillType );
1617 arc->SetFillColor( fill.m_Color );
1618 break;
1619
1620 default:
1621 Expecting( "start, mid, end, radius, stroke, or fill" );
1622 }
1623 }
1624
1625 if( hasMidPoint )
1626 {
1627 arc->SetArcGeometry( startPoint, midPoint, endPoint );
1628
1629 if( m_requiredVersion <= 20230121 ) // Versions before 7.0
1630 {
1631 // Should be not required. Unfortunately it is needed because some bugs created
1632 // incorrect data after conversion of old libraries to the new arc format using
1633 // startPoint, midPoint, endPoint
1634 // Until now, in Eeschema the arc angle should be <= 180 deg.
1635 // If > 180 (bug...) we need to swap arc ends.
1636 // However arc angle == 180 deg can also create issues in some cases (plotters, hittest)
1637 // so also avoid arc == 180 deg
1638 EDA_ANGLE arc_start, arc_end, arc_angle;
1639 arc->CalcArcAngles( arc_start, arc_end );
1640 arc_angle = arc_end - arc_start;
1641
1642 if( arc_angle > ANGLE_180 )
1643 {
1644 // Change arc to its complement (360deg - arc_angle)
1645 arc->SetStart( endPoint );
1646 arc->SetEnd( startPoint );
1647 VECTOR2I new_center =
1648 CalcArcCenter( arc->GetStart(), arc->GetEnd(), ANGLE_360 - arc_angle );
1649 arc->SetCenter( new_center );
1650 }
1651 else if( arc_angle == ANGLE_180 )
1652 {
1653 VECTOR2I new_center = CalcArcCenter( arc->GetStart(), arc->GetEnd(),
1654 EDA_ANGLE( 179.5, DEGREES_T ) );
1655 arc->SetCenter( new_center );
1656 }
1657 }
1658 }
1659 else if( hasAngles )
1660 {
1661 arc->SetCenter( center );
1662 /*
1663 * Older versions stored start-end with an implied winding, but the winding was different
1664 * between LibEdit and PCBNew. Since we now use a common class (EDA_SHAPE) for both we
1665 * need to flip one of them. LibEdit drew the short straw.
1666 */
1667 arc->SetStart( endPoint );
1668 arc->SetEnd( startPoint );
1669
1670 // Like previously, 180 degrees arcs that create issues are just modified
1671 // to be < 180 degrees to do not break some other functions ( Draw, Plot, HitTest)
1672 EDA_ANGLE arc_start, arc_end, arc_angle;
1673 arc->CalcArcAngles( arc_start, arc_end );
1674 arc_angle = arc_end - arc_start;
1675
1676 // The arc angle should be <= 180 deg in old libraries.
1677 // If > 180 we need to swap arc ends (the first choice was not good)
1678 if( arc_angle > ANGLE_180 )
1679 {
1680 arc->SetStart( startPoint );
1681 arc->SetEnd( endPoint );
1682 }
1683 else if( arc_angle == ANGLE_180 )
1684 {
1685 // Disabled (since the Y axis was reversed in library editor
1686 // and arcs having a 180 deg arc do not create issues
1687 // However keep it for info, just in case
1688 #if 0
1689 arc->SetStart( startPoint ); // not working with Y axis reversed
1690 arc->SetEnd( endPoint ); // not working with Y axis reversed
1691 // Useless now arcs >= 180 deg are well handled
1692 VECTOR2I new_center = CalcArcCenter( arc->GetStart(), arc->GetEnd(),
1693 EDA_ANGLE( 179.5, DEGREES_T ) );
1694 arc->SetCenter( new_center );
1695 #endif
1696 }
1697 }
1698 else
1699 {
1700 wxFAIL_MSG( "Setting arc without either midpoint or angles not implemented." );
1701 }
1702
1703 return arc.release();
1704}
1705
1706
1708{
1709 wxCHECK_MSG( CurTok() == T_bezier, nullptr,
1710 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
1711
1712 T token;
1714 FILL_PARAMS fill;
1715
1716 std::unique_ptr<SCH_SHAPE> bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER, LAYER_DEVICE );
1717
1718 bezier->SetUnit( m_unit );
1719 bezier->SetBodyStyle( m_bodyStyle );
1720
1721 token = NextTok();
1722
1723 if( token == T_private )
1724 {
1725 bezier->SetPrivate( true );
1726 token = NextTok();
1727 }
1728
1729 for( ; token != T_RIGHT; token = NextTok() )
1730 {
1731 if( token != T_LEFT )
1732 Expecting( T_LEFT );
1733
1734 token = NextTok();
1735
1736 switch( token )
1737 {
1738 case T_pts:
1739 {
1740 int ii = 0;
1741
1742 for( token = NextTok(); token != T_RIGHT; token = NextTok(), ++ii )
1743 {
1744 if( token != T_LEFT )
1745 Expecting( T_LEFT );
1746
1747 token = NextTok();
1748
1749 if( token != T_xy )
1750 Expecting( "xy" );
1751
1752 switch( ii )
1753 {
1754 case 0: bezier->SetStart( parseXY( true ) ); break;
1755 case 1: bezier->SetBezierC1( parseXY( true ) ); break;
1756 case 2: bezier->SetBezierC2( parseXY( true ) ); break;
1757 case 3: bezier->SetEnd( parseXY( true ) ); break;
1758 default: Unexpected( "control point" ); break;
1759 }
1760
1761 NeedRIGHT();
1762 }
1763 }
1764 break;
1765
1766 case T_stroke:
1767 parseStroke( stroke );
1768 bezier->SetStroke( stroke );
1769 break;
1770
1771 case T_fill:
1772 parseFill( fill );
1773 bezier->SetFillMode( fill.m_FillType );
1774 bezier->SetFillColor( fill.m_Color );
1775 break;
1776
1777 default:
1778 Expecting( "pts, stroke, or fill" );
1779 }
1780 }
1781
1782 bezier->RebuildBezierToSegmentsPointsList( m_maxError );
1783
1784 return bezier.release();
1785}
1786
1787
1789{
1790 wxCHECK_MSG( CurTok() == T_circle, nullptr,
1791 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle." ) );
1792
1793 T token;
1794 VECTOR2I center( 0, 0 );
1795 int radius = 1; // defaulting to 0 could result in troublesome math....
1797 FILL_PARAMS fill;
1798
1799 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE, LAYER_DEVICE );
1800
1801 circle->SetUnit( m_unit );
1802 circle->SetBodyStyle( m_bodyStyle );
1803
1804 token = NextTok();
1805
1806 if( token == T_private )
1807 {
1808 circle->SetPrivate( true );
1809 token = NextTok();
1810 }
1811
1812 for( ; token != T_RIGHT; token = NextTok() )
1813 {
1814 if( token != T_LEFT )
1815 Expecting( T_LEFT );
1816
1817 token = NextTok();
1818
1819 switch( token )
1820 {
1821 case T_center:
1822 center = parseXY( true );
1823 NeedRIGHT();
1824 break;
1825
1826 case T_radius:
1827 radius = parseInternalUnits( "radius length" );
1828 NeedRIGHT();
1829 break;
1830
1831 case T_stroke:
1832 parseStroke( stroke );
1833 circle->SetStroke( stroke );
1834 break;
1835
1836 case T_fill:
1837 parseFill( fill );
1838 circle->SetFillMode( fill.m_FillType );
1839 circle->SetFillColor( fill.m_Color );
1840 break;
1841
1842 default:
1843 Expecting( "center, radius, stroke, or fill" );
1844 }
1845 }
1846
1847 circle->SetCenter( center );
1848 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
1849
1850 return circle.release();
1851}
1852
1853
1855{
1856 wxCHECK_MSG( CurTok() == T_ellipse, nullptr,
1857 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an ellipse." ) );
1858
1859 auto ellipse = std::make_unique<SCH_SHAPE>( SHAPE_T::ELLIPSE, LAYER_DEVICE );
1860 ellipse->SetUnit( m_unit );
1861 ellipse->SetBodyStyle( m_bodyStyle );
1862
1863 T token = NextTok();
1864
1865 if( token == T_private )
1866 {
1867 ellipse->SetPrivate( true );
1868 token = NextTok();
1869 }
1870
1871 parseEllipseBody( ellipse.get(), /*isArc*/ false, /*isSchematic*/ false, token );
1872 return ellipse.release();
1873}
1874
1875
1877{
1878 wxCHECK_MSG( CurTok() == T_ellipse_arc, nullptr,
1879 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an elliptical arc." ) );
1880
1881 auto arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ELLIPSE_ARC, LAYER_DEVICE );
1882 arc->SetUnit( m_unit );
1883 arc->SetBodyStyle( m_bodyStyle );
1884
1885 T token = NextTok();
1886
1887 if( token == T_private )
1888 {
1889 arc->SetPrivate( true );
1890 token = NextTok();
1891 }
1892
1893 parseEllipseBody( arc.get(), /*isArc*/ true, /*isSchematic*/ false, token );
1894 return arc.release();
1895}
1896
1897
1899{
1900 auto parseType =
1901 [&]( T token ) -> ELECTRICAL_PINTYPE
1902 {
1903 switch( token )
1904 {
1905 case T_input: return ELECTRICAL_PINTYPE::PT_INPUT;
1906 case T_output: return ELECTRICAL_PINTYPE::PT_OUTPUT;
1907 case T_bidirectional: return ELECTRICAL_PINTYPE::PT_BIDI;
1908 case T_tri_state: return ELECTRICAL_PINTYPE::PT_TRISTATE;
1909 case T_passive: return ELECTRICAL_PINTYPE::PT_PASSIVE;
1910 case T_unspecified: return ELECTRICAL_PINTYPE::PT_UNSPECIFIED;
1911 case T_power_in: return ELECTRICAL_PINTYPE::PT_POWER_IN;
1912 case T_power_out: return ELECTRICAL_PINTYPE::PT_POWER_OUT;
1913 case T_open_collector: return ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR;
1914 case T_open_emitter: return ELECTRICAL_PINTYPE::PT_OPENEMITTER;
1915 case T_unconnected:
1916 case T_no_connect: return ELECTRICAL_PINTYPE::PT_NC;
1917 case T_free: return ELECTRICAL_PINTYPE::PT_NIC;
1918
1919 default:
1920 Expecting( "input, output, bidirectional, tri_state, passive, unspecified, "
1921 "power_in, power_out, open_collector, open_emitter, free or "
1922 "no_connect" );
1924 }
1925 };
1926
1927 auto parseShape =
1928 [&]( T token ) -> GRAPHIC_PINSHAPE
1929 {
1930 switch( token )
1931 {
1932 case T_line: return GRAPHIC_PINSHAPE::LINE;
1933 case T_inverted: return GRAPHIC_PINSHAPE::INVERTED;
1934 case T_clock: return GRAPHIC_PINSHAPE::CLOCK;
1935 case T_inverted_clock: return GRAPHIC_PINSHAPE::INVERTED_CLOCK;
1936 case T_input_low: return GRAPHIC_PINSHAPE::INPUT_LOW;
1937 case T_clock_low: return GRAPHIC_PINSHAPE::CLOCK_LOW;
1938 case T_output_low: return GRAPHIC_PINSHAPE::OUTPUT_LOW;
1939 case T_edge_clock_high: return GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK;
1940 case T_non_logic: return GRAPHIC_PINSHAPE::NONLOGIC;
1941
1942 default:
1943 Expecting( "line, inverted, clock, inverted_clock, input_low, clock_low, "
1944 "output_low, edge_clock_high, non_logic" );
1946 }
1947 };
1948
1949 wxCHECK_MSG( CurTok() == T_pin, nullptr,
1950 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a pin token." ) );
1951
1952 T token;
1953 std::unique_ptr<SCH_PIN> pin = std::make_unique<SCH_PIN>( nullptr );
1954
1955 pin->SetUnit( m_unit );
1956 pin->SetBodyStyle( m_bodyStyle );
1957
1958 // Pin electrical type.
1959 token = NextTok();
1960 pin->SetType( parseType( token ) );
1961
1962 // Pin shape.
1963 token = NextTok();
1964 pin->SetShape( parseShape( token ) );
1965
1966 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1967 {
1968 // Pre-2024104 format (bare 'hide' keyword)
1969 if( token == T_hide )
1970 {
1971 pin->SetVisible( false );
1972 continue;
1973 }
1974
1975 if( token != T_LEFT )
1976 Expecting( T_LEFT );
1977
1978 token = NextTok();
1979
1980 switch( token )
1981 {
1982 case T_at:
1983 pin->SetPosition( parseXY( true ) );
1984
1985 switch( parseInt( "pin orientation" ) )
1986 {
1987 case 0: pin->SetOrientation( PIN_ORIENTATION::PIN_RIGHT ); break;
1988 case 90: pin->SetOrientation( PIN_ORIENTATION::PIN_UP ); break;
1989 case 180: pin->SetOrientation( PIN_ORIENTATION::PIN_LEFT ); break;
1990 case 270: pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN ); break;
1991 default: Expecting( "0, 90, 180, or 270" );
1992 }
1993
1994 NeedRIGHT();
1995 break;
1996
1997 case T_length:
1998 pin->SetLength( parseInternalUnits( "pin length" ) );
1999 NeedRIGHT();
2000 break;
2001
2002 case T_hide:
2003 pin->SetVisible( !parseBool() );
2004 NeedRIGHT();
2005 break;
2006
2007 case T_name:
2008 token = NextTok();
2009
2010 if( !IsSymbol( token ) )
2011 {
2012 THROW_PARSE_ERROR( _( "Invalid pin name" ), CurSource(), CurLine(), CurLineNumber(),
2013 CurOffset() );
2014 }
2015
2016 if( m_requiredVersion < 20250318 && CurStr() == "~" )
2017 pin->SetName( wxEmptyString );
2018 else if( m_requiredVersion < 20210606 )
2019 pin->SetName( ConvertToNewOverbarNotation( FromUTF8() ) );
2020 else
2021 pin->SetName( FromUTF8() );
2022
2023 token = NextTok();
2024
2025 if( token != T_RIGHT )
2026 {
2027 token = NextTok();
2028
2029 if( token == T_effects )
2030 {
2031 // The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
2032 // so duplicate parsing is not required.
2034
2035 parseEDA_TEXT( &text, true, false );
2036 pin->SetNameTextSize( text.GetTextHeight() );
2037 NeedRIGHT();
2038 }
2039 else
2040 {
2041 Expecting( "effects" );
2042 }
2043 }
2044
2045 break;
2046
2047 case T_number:
2048 token = NextTok();
2049
2050 if( !IsSymbol( token ) )
2051 {
2052 THROW_PARSE_ERROR( _( "Invalid pin number" ), CurSource(), CurLine(),
2053 CurLineNumber(), CurOffset() );
2054 }
2055
2056 if( m_requiredVersion < 20250318 && CurStr() == "~" )
2057 pin->SetNumber( wxEmptyString );
2058 else if( m_requiredVersion < 20210606 )
2059 pin->SetNumber( ConvertToNewOverbarNotation( FromUTF8() ) );
2060 else
2061 pin->SetNumber( FromUTF8() );
2062
2063 token = NextTok();
2064
2065 if( token != T_RIGHT )
2066 {
2067 token = NextTok();
2068
2069 if( token == T_effects )
2070 {
2071 // The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
2072 // so duplicate parsing is not required.
2074
2075 parseEDA_TEXT( &text, false, false );
2076 pin->SetNumberTextSize( text.GetTextHeight() );
2077 NeedRIGHT();
2078 }
2079 else
2080 {
2081 Expecting( "effects" );
2082 }
2083 }
2084
2085 break;
2086
2087 case T_alternate:
2088 {
2089 SCH_PIN::ALT alt;
2090
2091 token = NextTok();
2092
2093 if( !IsSymbol( token ) )
2094 {
2095 THROW_PARSE_ERROR( _( "Invalid alternate pin name" ), CurSource(), CurLine(),
2096 CurLineNumber(), CurOffset() );
2097 }
2098
2099 alt.m_Name = FromUTF8();
2100
2101 token = NextTok();
2102 alt.m_Type = parseType( token );
2103
2104 token = NextTok();
2105 alt.m_Shape = parseShape( token );
2106
2107 pin->GetAlternates()[ alt.m_Name ] = alt;
2108
2109 NeedRIGHT();
2110 break;
2111 }
2112
2113 default:
2114 Expecting( "at, name, number, hide, length, or alternate" );
2115 }
2116 }
2117
2118 return pin.release();
2119}
2120
2121
2123{
2124 wxCHECK_MSG( CurTok() == T_polyline, nullptr,
2125 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a poly." ) );
2126
2127 T token;
2129 FILL_PARAMS fill;
2130 std::unique_ptr<SCH_SHAPE> poly = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_DEVICE );
2131
2132 poly->SetUnit( m_unit );
2133 poly->SetBodyStyle( m_bodyStyle );
2134
2135 token = NextTok();
2136
2137 if( token == T_private )
2138 {
2139 poly->SetPrivate( true );
2140 token = NextTok();
2141 }
2142
2143 for( ; token != T_RIGHT; token = NextTok() )
2144 {
2145 if( token != T_LEFT )
2146 Expecting( T_LEFT );
2147
2148 token = NextTok();
2149
2150 switch( token )
2151 {
2152 case T_pts:
2153 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2154 {
2155 if( token != T_LEFT )
2156 Expecting( T_LEFT );
2157
2158 token = NextTok();
2159
2160 if( token != T_xy )
2161 Expecting( "xy" );
2162
2163 poly->AddPoint( parseXY( true ) );
2164
2165 NeedRIGHT();
2166 }
2167
2168 break;
2169
2170 case T_stroke:
2171 parseStroke( stroke );
2172 poly->SetStroke( stroke );
2173 break;
2174
2175 case T_fill:
2176 parseFill( fill );
2177 poly->SetFillMode( fill.m_FillType );
2178 poly->SetFillColor( fill.m_Color );
2179 break;
2180
2181 default:
2182 Expecting( "pts, stroke, or fill" );
2183 }
2184 }
2185
2186 return poly.release();
2187}
2188
2189
2191{
2192 wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
2193 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle." ) );
2194
2195 T token;
2197 FILL_PARAMS fill;
2198 auto rectangle = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE, LAYER_DEVICE );
2199
2200 rectangle->SetUnit( m_unit );
2201 rectangle->SetBodyStyle( m_bodyStyle );
2202
2203 token = NextTok();
2204
2205 if( token == T_private )
2206 {
2207 rectangle->SetPrivate( true );
2208 token = NextTok();
2209 }
2210
2211 for( ; token != T_RIGHT; token = NextTok() )
2212 {
2213 if( token != T_LEFT )
2214 Expecting( T_LEFT );
2215
2216 token = NextTok();
2217
2218 switch( token )
2219 {
2220 case T_start:
2221 rectangle->SetPosition( parseXY( true ) );
2222 NeedRIGHT();
2223 break;
2224
2225 case T_end:
2226 rectangle->SetEnd( parseXY( true ) );
2227 NeedRIGHT();
2228 break;
2229
2230 case T_radius:
2231 rectangle->SetCornerRadius( parseDouble( "corner radius" ) * schIUScale.IU_PER_MM );
2232 NeedRIGHT();
2233 break;
2234
2235 case T_stroke:
2236 parseStroke( stroke );
2237 rectangle->SetStroke( stroke );
2238 break;
2239
2240 case T_fill:
2241 parseFill( fill );
2242 rectangle->SetFillMode( fill.m_FillType );
2243 rectangle->SetFillColor( fill.m_Color );
2244 break;
2245
2246 default:
2247 Expecting( "start, end, stroke, or fill" );
2248 }
2249 }
2250
2251 return rectangle.release();
2252}
2253
2254
2256{
2257 wxCHECK_MSG( CurTok() == T_text, nullptr,
2258 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text token." ) );
2259
2260 T token;
2261 std::unique_ptr<SCH_TEXT> text = std::make_unique<SCH_TEXT>();
2262
2263 text->SetLayer( LAYER_DEVICE );
2264 text->SetUnit( m_unit );
2265 text->SetBodyStyle( m_bodyStyle );
2266 token = NextTok();
2267
2268 if( token == T_private )
2269 {
2270 text->SetPrivate( true );
2271 token = NextTok();
2272 }
2273
2274 if( !IsSymbol( token ) )
2275 {
2276 THROW_PARSE_ERROR( _( "Invalid text string" ), CurSource(), CurLine(), CurLineNumber(),
2277 CurOffset() );
2278 }
2279
2280 text->SetText( FromUTF8() );
2281
2282 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2283 {
2284 if( token != T_LEFT )
2285 Expecting( T_LEFT );
2286
2287 token = NextTok();
2288
2289 switch( token )
2290 {
2291 case T_at:
2292 text->SetPosition( parseXY( true ) );
2293 // Yes, LIB_TEXT is really decidegrees even though all the others are degrees. :(
2294 text->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), TENTHS_OF_A_DEGREE_T ) );
2295 NeedRIGHT();
2296 break;
2297
2298 case T_effects:
2299 parseEDA_TEXT( text.get(), true );
2300 break;
2301
2302 default:
2303 Expecting( "at or effects" );
2304 }
2305 }
2306
2307 // Convert hidden symbol text (which is no longer supported) into a hidden field
2308 if( !text->IsVisible() )
2309 return new SCH_FIELD( text.get(), FIELD_T::USER );
2310
2311 return text.release();
2312}
2313
2314
2316{
2317 wxCHECK_MSG( CurTok() == T_text_box, nullptr,
2318 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text box." ) );
2319
2320 T token;
2321 VECTOR2I pos;
2322 VECTOR2I end;
2323 VECTOR2I size;
2324 int left;
2325 int top;
2326 int right;
2327 int bottom;
2329 FILL_PARAMS fill;
2330 bool foundEnd = false;
2331 bool foundSize = false;
2332 bool foundMargins = false;
2333
2334 std::unique_ptr<SCH_TEXTBOX> textBox = std::make_unique<SCH_TEXTBOX>( LAYER_DEVICE );
2335
2336 textBox->SetUnit( m_unit );
2337 textBox->SetBodyStyle( m_bodyStyle );
2338 token = NextTok();
2339
2340 if( token == T_private )
2341 {
2342 textBox->SetPrivate( true );
2343 token = NextTok();
2344 }
2345
2346 if( !IsSymbol( token ) )
2347 {
2348 THROW_PARSE_ERROR( _( "Invalid text string" ), CurSource(), CurLine(), CurLineNumber(),
2349 CurOffset() );
2350 }
2351
2352 textBox->SetText( FromUTF8() );
2353
2354 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2355 {
2356 if( token != T_LEFT )
2357 Expecting( T_LEFT );
2358
2359 token = NextTok();
2360
2361 switch( token )
2362 {
2363 case T_start: // Legacy token during 6.99 development; fails to handle angle
2364 pos = parseXY( true );
2365 NeedRIGHT();
2366 break;
2367
2368 case T_end: // Legacy token during 6.99 development; fails to handle angle
2369 end = parseXY( true );
2370 foundEnd = true;
2371 NeedRIGHT();
2372 break;
2373
2374 case T_at:
2375 pos = parseXY( true );
2376 textBox->SetTextAngle( EDA_ANGLE( parseDouble( "textbox angle" ), DEGREES_T ) );
2377 NeedRIGHT();
2378 break;
2379
2380 case T_size:
2381 size = parseXY( true );
2382 foundSize = true;
2383 NeedRIGHT();
2384 break;
2385
2386 case T_stroke:
2387 parseStroke( stroke );
2388 textBox->SetStroke( stroke );
2389 break;
2390
2391 case T_fill:
2392 parseFill( fill );
2393 textBox->SetFillMode( fill.m_FillType );
2394 textBox->SetFillColor( fill.m_Color );
2395 break;
2396
2397 case T_margins:
2398 parseMargins( left, top, right, bottom );
2399 textBox->SetMarginLeft( left );
2400 textBox->SetMarginTop( top );
2401 textBox->SetMarginRight( right );
2402 textBox->SetMarginBottom( bottom );
2403 foundMargins = true;
2404 NeedRIGHT();
2405 break;
2406
2407 case T_effects:
2408 parseEDA_TEXT( static_cast<EDA_TEXT*>( textBox.get() ), false );
2409 break;
2410
2411 default:
2412 Expecting( "at, size, stroke, fill or effects" );
2413 }
2414 }
2415
2416 textBox->SetPosition( pos );
2417
2418 if( foundEnd )
2419 textBox->SetEnd( end );
2420 else if( foundSize )
2421 textBox->SetEnd( pos + size );
2422 else
2423 Expecting( "size" );
2424
2425 if( !foundMargins )
2426 {
2427 int margin = textBox->GetLegacyTextMargin();
2428 textBox->SetMarginLeft( margin );
2429 textBox->SetMarginTop( margin );
2430 textBox->SetMarginRight( margin );
2431 textBox->SetMarginBottom( margin );
2432 }
2433
2434 return textBox.release();
2435}
2436
2437
2439{
2440 wxCHECK_RET( ( CurTok() == T_page && m_requiredVersion <= 20200506 ) || CurTok() == T_paper,
2441 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a PAGE_INFO." ) );
2442
2443 T token;
2444
2445 NeedSYMBOL();
2446
2447 wxString pageType = FromUTF8();
2448
2449 if( !aPageInfo.SetType( pageType ) )
2450 {
2451 THROW_PARSE_ERROR( _( "Invalid page type" ), CurSource(), CurLine(), CurLineNumber(),
2452 CurOffset() );
2453 }
2454
2455 if( aPageInfo.GetType() == PAGE_SIZE_TYPE::User )
2456 {
2457 double width = parseDouble( "width" );
2458
2459 // Perform some controls to avoid crashes if the size is edited by hands
2460 if( width < MIN_PAGE_SIZE_MM )
2461 width = MIN_PAGE_SIZE_MM;
2462 else if( width > MAX_PAGE_SIZE_EESCHEMA_MM )
2464
2465 double height = parseDouble( "height" );
2466
2467 if( height < MIN_PAGE_SIZE_MM )
2468 height = MIN_PAGE_SIZE_MM;
2469 else if( height > MAX_PAGE_SIZE_EESCHEMA_MM )
2471
2472 aPageInfo.SetWidthMM( width );
2473 aPageInfo.SetHeightMM( height );
2474 }
2475
2476 token = NextTok();
2477
2478 if( token == T_portrait )
2479 {
2480 aPageInfo.SetPortrait( true );
2481 NeedRIGHT();
2482 }
2483 else if( token != T_RIGHT )
2484 {
2485 Expecting( "portrait" );
2486 }
2487}
2488
2489
2491{
2492 wxCHECK_RET( CurTok() == T_title_block,
2493 "Cannot parse " + GetTokenString( CurTok() ) + " as a TITLE_BLOCK." );
2494
2495 T token;
2496
2497 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2498 {
2499 if( token != T_LEFT )
2500 Expecting( T_LEFT );
2501
2502 token = NextTok();
2503
2504 switch( token )
2505 {
2506 case T_title:
2507 NextTok();
2508 aTitleBlock.SetTitle( FromUTF8() );
2509 break;
2510
2511 case T_date:
2512 NextTok();
2513 aTitleBlock.SetDate( FromUTF8() );
2514 break;
2515
2516 case T_rev:
2517 NextTok();
2518 aTitleBlock.SetRevision( FromUTF8() );
2519 break;
2520
2521 case T_company:
2522 NextTok();
2523 aTitleBlock.SetCompany( FromUTF8() );
2524 break;
2525
2526 case T_comment:
2527 {
2528 int commentNumber = parseInt( "comment" );
2529
2530 switch( commentNumber )
2531 {
2532 case 1:
2533 NextTok();
2534 aTitleBlock.SetComment( 0, FromUTF8() );
2535 break;
2536
2537 case 2:
2538 NextTok();
2539 aTitleBlock.SetComment( 1, FromUTF8() );
2540 break;
2541
2542 case 3:
2543 NextTok();
2544 aTitleBlock.SetComment( 2, FromUTF8() );
2545 break;
2546
2547 case 4:
2548 NextTok();
2549 aTitleBlock.SetComment( 3, FromUTF8() );
2550 break;
2551
2552 case 5:
2553 NextTok();
2554 aTitleBlock.SetComment( 4, FromUTF8() );
2555 break;
2556
2557 case 6:
2558 NextTok();
2559 aTitleBlock.SetComment( 5, FromUTF8() );
2560 break;
2561
2562 case 7:
2563 NextTok();
2564 aTitleBlock.SetComment( 6, FromUTF8() );
2565 break;
2566
2567 case 8:
2568 NextTok();
2569 aTitleBlock.SetComment( 7, FromUTF8() );
2570 break;
2571
2572 case 9:
2573 NextTok();
2574 aTitleBlock.SetComment( 8, FromUTF8() );
2575 break;
2576
2577 default:
2578 THROW_PARSE_ERROR( _( "Invalid title block comment number" ), CurSource(),
2579 CurLine(), CurLineNumber(), CurOffset() );
2580 }
2581
2582 break;
2583 }
2584
2585 default:
2586 Expecting( "title, date, rev, company, or comment" );
2587 }
2588
2589 NeedRIGHT();
2590 }
2591}
2592
2593
2595{
2596 wxCHECK_MSG( CurTok() == T_property, nullptr,
2597 "Cannot parse " + GetTokenString( CurTok() ) + " as a property token." );
2598
2599 bool is_private = false;
2600
2601 T token = NextTok();
2602
2603 if( token == T_private )
2604 {
2605 is_private = true;
2606 token = NextTok();
2607 }
2608
2609 if( !IsSymbol( token ) )
2610 {
2611 THROW_PARSE_ERROR( _( "Invalid property name" ), CurSource(), CurLine(), CurLineNumber(),
2612 CurOffset() );
2613 }
2614
2615 wxString name = FromUTF8();
2616
2617 if( name.IsEmpty() )
2618 {
2619 THROW_PARSE_ERROR( _( "Empty property name" ), CurSource(), CurLine(), CurLineNumber(),
2620 CurOffset() );
2621 }
2622
2623 // Normalise legacy/cross-locale directive-label net class field names to the canonical
2624 // "Netclass" token as early as possible so every downstream consumer (including ones that
2625 // call GetName() directly instead of GetCanonicalName()) sees a consistent in-memory model.
2626 // See issue #24403.
2627 if( dynamic_cast<SCH_LABEL_BASE*>( aParent ) && SCH_FIELD::IsNetclassLabelFieldName( name ) )
2628 name = wxT( "Netclass" );
2629
2630 token = NextTok();
2631
2632 if( !IsSymbol( token ) )
2633 {
2634 THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
2635 CurOffset() );
2636 }
2637
2638 // Empty property values are valid.
2639 wxString value;
2640
2641 if( m_requiredVersion < 20250318 && CurStr() == "~" )
2642 value = wxEmptyString;
2643 else
2644 value = FromUTF8();
2645
2646 FIELD_T fieldId = FIELD_T::USER;
2647
2648 // Correctly set the ID based on canonical (untranslated) field name
2649 if( aParent->Type() == SCH_SYMBOL_T )
2650 {
2651 for( FIELD_T id : MANDATORY_FIELDS )
2652 {
2653 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
2654 {
2655 fieldId = id;
2656 break;
2657 }
2658 }
2659 }
2660 else if( aParent->Type() == SCH_SHEET_T )
2661 {
2662 fieldId = FIELD_T::SHEET_USER; // This is the default id for user fields
2663
2665 {
2666 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
2667 {
2668 fieldId = id;
2669 break;
2670 }
2671 }
2672
2673 // Legacy support for old field names
2674 if( name.CmpNoCase( wxT( "Sheet name" ) ) == 0 )
2675 fieldId = FIELD_T::SHEET_NAME;
2676 else if( name.CmpNoCase( wxT( "Sheet file" ) ) == 0 )
2677 fieldId = FIELD_T::SHEET_FILENAME;
2678 }
2679 else if( aParent->Type() == SCH_GLOBAL_LABEL_T )
2680 {
2682 {
2683 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
2684 {
2685 fieldId = id;
2686 break;
2687 }
2688 }
2689
2690 // Legacy support for old field names
2691 if( name.CmpNoCase( wxT( "Intersheet References" ) ) == 0 )
2692 fieldId = FIELD_T::INTERSHEET_REFS;
2693 }
2694
2695 std::unique_ptr<SCH_FIELD> field = std::make_unique<SCH_FIELD>( aParent, fieldId, name );
2696 field->SetText( value );
2697
2698 if( fieldId == FIELD_T::USER )
2699 field->SetPrivate( is_private );
2700
2701 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2702 {
2703 if( token != T_LEFT )
2704 Expecting( T_LEFT );
2705
2706 token = NextTok();
2707
2708 switch( token )
2709 {
2710 case T_id: // legacy token; ignore
2711 parseInt( "field ID" );
2712 NeedRIGHT();
2713 break;
2714
2715 case T_at:
2716 field->SetPosition( parseXY() );
2717 field->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ) );
2718 NeedRIGHT();
2719 break;
2720
2721 case T_hide:
2722 field->SetVisible( !parseBool() );
2723 NeedRIGHT();
2724 break;
2725
2726 case T_effects:
2727 parseEDA_TEXT( static_cast<EDA_TEXT*>( field.get() ),
2728 field->GetId() == FIELD_T::VALUE );
2729 break;
2730
2731 case T_show_name:
2732 {
2733 bool show = parseMaybeAbsentBool( true );
2734 field->SetNameShown( show );
2735 break;
2736 }
2737
2738 case T_do_not_autoplace:
2739 {
2740 bool doNotAutoplace = parseMaybeAbsentBool( true );
2741 field->SetCanAutoplace( !doNotAutoplace );
2742 break;
2743 }
2744
2745 default:
2746 Expecting( "id, at, hide, show_name, do_not_autoplace or effects" );
2747 }
2748 }
2749
2750 return field.release();
2751}
2752
2753
2755{
2756 wxCHECK_MSG( aSheet != nullptr, nullptr, "" );
2757 wxCHECK_MSG( CurTok() == T_pin, nullptr,
2758 "Cannot parse " + GetTokenString( CurTok() ) + " as a sheet pin token." );
2759
2760 T token = NextTok();
2761
2762 if( !IsSymbol( token ) )
2763 {
2764 THROW_PARSE_ERROR( _( "Invalid sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
2765 CurOffset() );
2766 }
2767
2768 wxString name = FromUTF8();
2769
2770 if( name.IsEmpty() )
2771 {
2772 THROW_PARSE_ERROR( _( "Empty sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
2773 CurOffset() );
2774 }
2775
2776 auto sheetPin = std::make_unique<SCH_SHEET_PIN>( aSheet, VECTOR2I( 0, 0 ), name );
2777
2778 token = NextTok();
2779
2780 switch( token )
2781 {
2782 case T_input: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
2783 case T_output: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
2784 case T_bidirectional: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
2785 case T_tri_state: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE ); break;
2786 case T_passive: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
2787 default:
2788 Expecting( "input, output, bidirectional, tri_state, or passive" );
2789 }
2790
2791 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2792 {
2793 if( token != T_LEFT )
2794 Expecting( T_LEFT );
2795
2796 token = NextTok();
2797
2798 switch( token )
2799 {
2800 case T_at:
2801 {
2802 sheetPin->SetPosition( parseXY() );
2803
2804 double angle = parseDouble( "sheet pin angle (side)" );
2805
2806 if( angle == 0.0 )
2807 sheetPin->SetSide( SHEET_SIDE::RIGHT );
2808 else if( angle == 90.0 )
2809 sheetPin->SetSide( SHEET_SIDE::TOP );
2810 else if( angle == 180.0 )
2811 sheetPin->SetSide( SHEET_SIDE::LEFT );
2812 else if( angle == 270.0 )
2813 sheetPin->SetSide( SHEET_SIDE::BOTTOM );
2814 else
2815 Expecting( "0, 90, 180, or 270" );
2816
2817 NeedRIGHT();
2818 break;
2819 }
2820
2821 case T_effects:
2822 parseEDA_TEXT( static_cast<EDA_TEXT*>( sheetPin.get() ), true );
2823 break;
2824
2825 case T_uuid:
2826 NeedSYMBOL();
2827 const_cast<KIID&>( sheetPin->m_Uuid ) = parseKIID();
2828 NeedRIGHT();
2829 break;
2830
2831 default:
2832 Expecting( "at, uuid or effects" );
2833 }
2834 }
2835
2836 return sheetPin.release();
2837}
2838
2839
2841{
2842 wxCHECK_RET( CurTok() == T_sheet_instances,
2843 "Cannot parse " + GetTokenString( CurTok() ) + " as an instances token." );
2844 wxCHECK( aScreen, /* void */ );
2845
2846 T token;
2847
2848 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2849 {
2850 if( token != T_LEFT )
2851 Expecting( T_LEFT );
2852
2853 token = NextTok();
2854
2855 switch( token )
2856 {
2857 case T_path:
2858 {
2859 NeedSYMBOL();
2860
2861 SCH_SHEET_INSTANCE instance;
2862
2863 instance.m_Path = KIID_PATH( FromUTF8() );
2864
2865 if( ( !m_appending && aRootSheet->GetScreen() == aScreen ) &&
2866 ( aScreen->GetFileFormatVersionAtLoad() < 20221002 ) )
2867 instance.m_Path.insert( instance.m_Path.begin(), m_rootUuid );
2868
2869 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2870 {
2871 if( token != T_LEFT )
2872 Expecting( T_LEFT );
2873
2874 token = NextTok();
2875
2876 std::vector<wxString> whitespaces = { wxT( "\r" ), wxT( "\n" ), wxT( "\t" ),
2877 wxT( " " ) };
2878
2879 size_t numReplacements = 0;
2880
2881 switch( token )
2882 {
2883 case T_page:
2884 NeedSYMBOL();
2885 instance.m_PageNumber = FromUTF8();
2886
2887 // Empty page numbers are not permitted
2888 if( instance.m_PageNumber.IsEmpty() )
2889 {
2890 // Use hash character instead
2891 instance.m_PageNumber = wxT( "#" );
2892 numReplacements++;
2893 }
2894 else
2895 {
2896 // Whitespaces are not permitted
2897 for( const wxString& ch : whitespaces )
2898 numReplacements += instance.m_PageNumber.Replace( ch, wxEmptyString );
2899
2900 }
2901
2902 // Set the file as modified so the user can be warned.
2903 if( numReplacements > 0 )
2904 aScreen->SetContentModified();
2905
2906 NeedRIGHT();
2907 break;
2908
2909 default:
2910 Expecting( "path or page" );
2911 }
2912 }
2913
2914 if( ( aScreen->GetFileFormatVersionAtLoad() >= 20221110 )
2915 && ( instance.m_Path.empty() ) )
2916 {
2917 SCH_SHEET_PATH rootSheetPath;
2918
2919 rootSheetPath.push_back( aRootSheet );
2920 rootSheetPath.SetPageNumber( instance.m_PageNumber );
2921 }
2922 else
2923 {
2924 aScreen->m_sheetInstances.emplace_back( instance );
2925 }
2926
2927 break;
2928 }
2929
2930 default:
2931 Expecting( "path" );
2932 }
2933 }
2934}
2935
2936
2938{
2939 wxCHECK_RET( CurTok() == T_symbol_instances,
2940 "Cannot parse " + GetTokenString( CurTok() ) + " as an instances token." );
2941 wxCHECK( aScreen, /* void */ );
2942 wxCHECK( m_rootUuid != NilUuid(), /* void */ );
2943
2944 T token;
2945
2946 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2947 {
2948 if( token != T_LEFT )
2949 Expecting( T_LEFT );
2950
2951 token = NextTok();
2952
2953 switch( token )
2954 {
2955 case T_path:
2956 {
2957 NeedSYMBOL();
2958
2959 SCH_SYMBOL_INSTANCE instance;
2960
2961 instance.m_Path = KIID_PATH( FromUTF8() );
2962
2963 if( !m_appending )
2964 instance.m_Path.insert( instance.m_Path.begin(), m_rootUuid );
2965
2966 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2967 {
2968 if( token != T_LEFT )
2969 Expecting( T_LEFT );
2970
2971 token = NextTok();
2972
2973 switch( token )
2974 {
2975 case T_reference:
2976 NeedSYMBOL();
2977 instance.m_Reference = FromUTF8();
2978 NeedRIGHT();
2979 break;
2980
2981 case T_unit:
2982 instance.m_Unit = parseInt( "symbol unit" );
2983 NeedRIGHT();
2984 break;
2985
2986 case T_value:
2987 NeedSYMBOL();
2988
2989 if( m_requiredVersion < 20250318 && CurStr() == "~" )
2990 instance.m_Value = wxEmptyString;
2991 else
2992 instance.m_Value = FromUTF8();
2993
2994 NeedRIGHT();
2995 break;
2996
2997 case T_footprint:
2998 NeedSYMBOL();
2999
3000 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3001 instance.m_Footprint = wxEmptyString;
3002 else
3003 instance.m_Footprint = FromUTF8();
3004
3005 NeedRIGHT();
3006 break;
3007
3008 default:
3009 Expecting( "path, unit, value or footprint" );
3010 }
3011 }
3012
3013 aScreen->m_symbolInstances.emplace_back( instance );
3014 break;
3015 }
3016
3017 default:
3018 Expecting( "path" );
3019 }
3020 }
3021}
3022
3023
3024void SCH_IO_KICAD_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly,
3025 int aFileVersion )
3026{
3027 wxCHECK( aSheet != nullptr, /* void */ );
3028
3029 SCH_SCREEN* screen = aSheet->GetScreen();
3030
3031 wxCHECK( screen != nullptr, /* void */ );
3032
3033 if( SCHEMATIC* schematic = dynamic_cast<SCHEMATIC*>( screen->GetParent() ) )
3034 m_maxError = schematic->Settings().m_MaxError;
3035
3036 if( aIsCopyableOnly )
3037 m_requiredVersion = aFileVersion;
3038
3039 bool fileHasUuid = false;
3040
3041 auto checkVersion =
3042 [&]()
3043 {
3045 {
3046 throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
3048 }
3049 };
3050
3051 T token;
3052
3053 if( !aIsCopyableOnly )
3054 {
3055 NeedLEFT();
3056 NextTok();
3057
3058 if( CurTok() != T_kicad_sch )
3059 Expecting( "kicad_sch" );
3060
3062
3063 // Prior to this, bar was a valid string char for unquoted strings.
3064 SetKnowsBar( m_requiredVersion >= 20240620 );
3065
3066 // Prior to schematic file version 20210406, schematics did not have UUIDs so we need
3067 // to generate one for the root schematic for instance paths.
3068 if( m_requiredVersion < 20210406 )
3069 m_rootUuid = screen->GetUuid();
3070
3071 // Prior to version 20231120, generator_version was not present, so check the date here
3072 if( m_requiredVersion < 20231120 )
3073 checkVersion();
3074 }
3075
3077
3078 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3079 {
3080 if( aIsCopyableOnly && token == T_EOF )
3081 break;
3082
3083 if( token != T_LEFT )
3084 Expecting( T_LEFT );
3085
3086 token = NextTok();
3087
3088 checkpoint();
3089
3090 if( !aIsCopyableOnly && token == T_page && m_requiredVersion <= 20200506 )
3091 token = T_paper;
3092
3093 switch( token )
3094 {
3095 case T_group:
3096 parseGroup();
3097 break;
3098
3099 case T_generator:
3100 // (generator "genname"); we don't care about it at the moment.
3101 NeedSYMBOL();
3102 NeedRIGHT();
3103 break;
3104
3105 case T_host:
3106 {
3107 // (host eeschema ["5.99"]); old version of generator token
3108 NeedSYMBOL();
3109
3110 // Really old versions also included a host version
3111 if( m_requiredVersion < 20200827 )
3112 NeedSYMBOL();
3113
3114 NeedRIGHT();
3115 break;
3116 }
3117
3118 case T_generator_version:
3119 NextTok();
3120 m_generatorVersion = FromUTF8();
3121 NeedRIGHT();
3122 checkVersion();
3123 break;
3124
3125 case T_uuid:
3126 NeedSYMBOL();
3127 screen->m_uuid = parseKIID();
3128
3129 // Set the root sheet UUID with the schematic file UUID. Root sheets are virtual
3130 // and always get a new UUID so this prevents file churn now that the root UUID
3131 // is saved in the symbol instance path.
3132 if( aSheet == m_rootSheet )
3133 {
3134 const_cast<KIID&>( aSheet->m_Uuid ) = screen->GetUuid();
3135 m_rootUuid = screen->GetUuid();
3136 fileHasUuid = true;
3137 }
3138
3139 NeedRIGHT();
3140 break;
3141
3142 case T_paper:
3143 {
3144 if( aIsCopyableOnly )
3145 Unexpected( T_paper );
3146
3147 PAGE_INFO pageInfo;
3148 parsePAGE_INFO( pageInfo );
3149 screen->SetPageSettings( pageInfo );
3150 break;
3151 }
3152
3153 case T_page:
3154 {
3155 if( aIsCopyableOnly )
3156 Unexpected( T_page );
3157
3158 // Only saved for top-level sniffing in Kicad Manager frame and other external
3159 // tool usage with flat hierarchies
3160 NeedSYMBOLorNUMBER();
3161 NeedSYMBOLorNUMBER();
3162 NeedRIGHT();
3163 break;
3164 }
3165
3166 case T_title_block:
3167 {
3168 if( aIsCopyableOnly )
3169 Unexpected( T_title_block );
3170
3171 TITLE_BLOCK tb;
3172 parseTITLE_BLOCK( tb );
3173 screen->SetTitleBlock( tb );
3174 break;
3175 }
3176
3177 case T_lib_symbols:
3178 {
3179 // Dummy map. No derived symbols are allowed in the library cache.
3180 LIB_SYMBOL_MAP symbolLibMap;
3181 LIB_SYMBOL* symbol;
3182
3183 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3184 {
3185 if( token != T_LEFT )
3186 Expecting( T_LEFT );
3187
3188 token = NextTok();
3189
3190 switch( token )
3191 {
3192 case T_symbol:
3193 symbol = parseLibSymbol( symbolLibMap );
3194 screen->AddLibSymbol( symbol );
3195 break;
3196
3197 default:
3198 Expecting( "symbol" );
3199 }
3200 }
3201
3202 break;
3203 }
3204
3205 case T_symbol:
3206 screen->Append( static_cast<SCH_ITEM*>( parseSchematicSymbol() ) );
3207 break;
3208
3209 case T_image:
3210 screen->Append( static_cast<SCH_ITEM*>( parseImage() ) );
3211 break;
3212
3213 case T_sheet:
3214 {
3215 SCH_SHEET* sheet = parseSheet();
3216
3217 // Set the parent to aSheet. This effectively creates a method to find
3218 // the root sheet from any sheet so a pointer to the root sheet does not
3219 // need to be stored globally. Note: this is not the same as a hierarchy.
3220 // Complex hierarchies can have multiple copies of a sheet. This only
3221 // provides a simple tree to find the root sheet.
3222 sheet->SetParent( aSheet );
3223 screen->Append( sheet );
3224 break;
3225 }
3226
3227 case T_junction:
3228 screen->Append( parseJunction() );
3229 break;
3230
3231 case T_no_connect:
3232 screen->Append( parseNoConnect() );
3233 break;
3234
3235 case T_bus_entry:
3236 screen->Append( parseBusEntry() );
3237 break;
3238
3239 case T_polyline:
3240 {
3241 // polyline keyword is used in eeschema both for SCH_SHAPE and SCH_LINE items.
3242 // In symbols it describes a polygon, having n corners and can be filled
3243 // In schematic it describes a line (with no fill descr), but could be extended to a
3244 // polygon (for instance when importing files) because the schematic handles all
3245 // SCH_SHAPE.
3246
3247 // parseSchPolyLine() returns always a SCH_SHAPE, io convert it to a simple SCH_LINE
3248 // For compatibility reasons, keep SCH_SHAPE for a polygon and convert to SCH_LINE
3249 // when the item has only 2 corners, similar to a SCH_LINE
3250 SCH_SHAPE* poly = parseSchPolyLine();
3251
3252 if( poly->GetPointCount() < 2 )
3253 {
3254 delete poly;
3255 THROW_PARSE_ERROR( _( "Schematic polyline has too few points" ), CurSource(), CurLine(),
3256 CurLineNumber(), CurOffset() );
3257 }
3258 else if( poly->GetPointCount() > 2 )
3259 {
3260 screen->Append( poly );
3261 }
3262 else
3263 {
3264 // For SCH_SHAPE having only 2 points, this is a "old" SCH_LINE entity.
3265 // So convert the SCH_SHAPE to a simple SCH_LINE
3266 SCH_LINE* line = new SCH_LINE( VECTOR2I(), LAYER_NOTES );
3267 SHAPE_LINE_CHAIN& outline = poly->GetPolyShape().Outline(0);
3268 line->SetStartPoint( outline.CPoint(0) );
3269 line->SetEndPoint( outline.CPoint(1) );
3270 line->SetStroke( poly->GetStroke() );
3271 line->SetLocked( poly->IsLocked() );
3272 const_cast<KIID&>( line->m_Uuid ) = poly->m_Uuid;
3273
3274 screen->Append( line );
3275
3276 delete poly;
3277 }
3278 }
3279 break;
3280
3281 case T_bus:
3282 case T_wire:
3283 screen->Append( parseLine() );
3284 break;
3285
3286 case T_arc:
3287 screen->Append( parseSchArc() );
3288 break;
3289
3290 case T_circle:
3291 screen->Append( parseSchCircle() );
3292 break;
3293
3294 case T_rectangle:
3295 screen->Append( parseSchRectangle() );
3296 break;
3297
3298 case T_bezier:
3299 screen->Append( parseSchBezier() );
3300 break;
3301
3302 case T_ellipse: screen->Append( parseSchEllipse() ); break;
3303
3304 case T_ellipse_arc: screen->Append( parseSchEllipseArc() ); break;
3305
3306 case T_rule_area:
3307 screen->Append( parseSchRuleArea() );
3308 break;
3309
3310 case T_netclass_flag: // present only during early development of 7.0
3312
3313 case T_text:
3314 case T_label:
3315 case T_global_label:
3316 case T_hierarchical_label:
3317 case T_directive_label:
3318 screen->Append( parseSchText() );
3319 break;
3320
3321 case T_net_chain:
3323 break;
3324
3325 case T_text_box:
3326 screen->Append( parseSchTextBox() );
3327 break;
3328
3329 case T_table:
3330 screen->Append( parseSchTable() );
3331 break;
3332
3333 case T_sheet_instances:
3334 parseSchSheetInstances( aSheet, screen );
3335 break;
3336
3337 case T_symbol_instances:
3338 parseSchSymbolInstances( screen );
3339 break;
3340
3341 case T_bus_alias:
3342 if( aIsCopyableOnly )
3343 Unexpected( T_bus_alias );
3344
3345 parseBusAlias( screen );
3346 break;
3347
3348 case T_embedded_fonts:
3349 {
3350 SCHEMATIC* schematic = screen->Schematic();
3351
3352 if( !schematic )
3353 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
3354 CurLineNumber(), CurOffset() );
3355
3356 schematic->GetEmbeddedFiles()->SetAreFontsEmbedded( parseBool() );
3357 NeedRIGHT();
3358 break;
3359 }
3360
3361 case T_embedded_files:
3362 {
3363 SCHEMATIC* schematic = screen->Schematic();
3364
3365 if( !schematic )
3366 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
3367 CurLineNumber(), CurOffset() );
3368
3369 EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
3370 embeddedFilesParser.SyncLineReaderWith( *this );
3371
3372 try
3373 {
3374 embeddedFilesParser.ParseEmbedded( schematic->GetEmbeddedFiles() );
3375 }
3376 catch( const PARSE_ERROR& e )
3377 {
3378 m_parseWarnings.push_back( e.What() );
3379
3380 int depth = 0;
3381
3382 for( int tok = embeddedFilesParser.NextTok();
3383 tok != DSN_EOF;
3384 tok = embeddedFilesParser.NextTok() )
3385 {
3386 if( tok == DSN_LEFT )
3387 depth++;
3388 else if( tok == DSN_RIGHT && --depth < 0 )
3389 break;
3390 }
3391 }
3392
3393 SyncLineReaderWith( embeddedFilesParser );
3394 break;
3395 }
3396
3397
3398 default:
3399 Expecting( "arc, bezier, bitmap, bus, bus_alias, bus_entry, circle, class_label, "
3400 "directive_label, ellipse, ellipse_arc, embedded_fonts, embedded_files, "
3401 "global_label, hierarchical_label, image, junction, lib_symbols, "
3402 "netclass_flag, no_connect, polyline, rectangle, rule_area, sheet, "
3403 "sheet_instances, signal, symbol, symbol_instances, table, text, "
3404 "text_box, title_block, uuid, wire" );
3405 }
3406 }
3407
3408 // Older s-expression schematics may not have a UUID so use the one automatically generated
3409 // as the virtual root sheet UUID.
3410 if( ( aSheet == m_rootSheet ) && !fileHasUuid )
3411 {
3412 const_cast<KIID&>( aSheet->m_Uuid ) = screen->GetUuid();
3413 m_rootUuid = screen->GetUuid();
3414 }
3415
3416 screen->UpdateLocalLibSymbolLinks();
3417 screen->FixupEmbeddedData();
3418
3419 resolveGroups( screen );
3420
3421 SCHEMATIC* schematic = screen->Schematic();
3422
3423 if( !schematic )
3424 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
3425 CurLineNumber(), CurOffset() );
3426
3427 // When loading the schematic, take a moment to cache the fonts so that the font
3428 // picker can show the embedded fonts immediately.
3429 std::vector<std::string> fontNames;
3430 Fontconfig()->ListFonts( fontNames, std::string( Pgm().GetLanguageTag().utf8_str() ),
3431 schematic->GetEmbeddedFiles()->GetFontFiles(), true );
3432
3433 if( m_requiredVersion < 20200828 )
3435}
3436
3437
3439{
3440 wxCHECK_MSG( CurTok() == T_symbol, nullptr,
3441 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
3442
3443 T token;
3444 wxString libName;
3445 SCH_FIELD* field;
3446 std::unique_ptr<SCH_SYMBOL> symbol = std::make_unique<SCH_SYMBOL>();
3447 TRANSFORM transform;
3448 std::set<int> fieldIDsRead;
3449
3450 // We'll reset this if we find a fields_autoplaced token
3451 symbol->SetFieldsAutoplaced( AUTOPLACE_NONE );
3452
3453 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3454 {
3455 if( token != T_LEFT )
3456 Expecting( T_LEFT );
3457
3458 token = NextTok();
3459
3460 switch( token )
3461 {
3462 case T_lib_name:
3463 {
3464 LIB_ID libId;
3465
3466 token = NextTok();
3467
3468 if( !IsSymbol( token ) )
3469 {
3470 THROW_PARSE_ERROR( _( "Invalid symbol library name" ), CurSource(), CurLine(),
3471 CurLineNumber(), CurOffset() );
3472 }
3473
3474 libName = FromUTF8();
3475 // Some symbol LIB_IDs or lib_name have the '/' character escaped which can break
3476 // symbol links. The '/' character is no longer an illegal LIB_ID character so
3477 // it doesn't need to be escaped.
3478 libName.Replace( "{slash}", "/" );
3479
3480 NeedRIGHT();
3481 break;
3482 }
3483
3484 case T_lib_id:
3485 {
3486 token = NextTok();
3487
3488 if( !IsSymbol( token ) && token != T_NUMBER )
3489 Expecting( "symbol|number" );
3490
3491 LIB_ID libId;
3492 wxString name = FromUTF8();
3493 // Some symbol LIB_IDs have the '/' character escaped which can break
3494 // symbol links. The '/' character is no longer an illegal LIB_ID character so
3495 // it doesn't need to be escaped.
3496 name.Replace( "{slash}", "/" );
3497
3498 int bad_pos = libId.Parse( name );
3499
3500 if( bad_pos >= 0 )
3501 {
3502 if( static_cast<int>( name.size() ) > bad_pos )
3503 {
3504 wxString msg = wxString::Format( _( "Symbol %s contains invalid character '%c'" ),
3505 name,
3506 name[bad_pos] );
3507
3508 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
3509 }
3510
3511 THROW_PARSE_ERROR( _( "Invalid symbol library ID" ), CurSource(), CurLine(),
3512 CurLineNumber(), CurOffset() );
3513 }
3514
3515 symbol->SetLibId( libId );
3516 NeedRIGHT();
3517 break;
3518 }
3519
3520 case T_at:
3521 symbol->SetPosition( parseXY() );
3522
3523 switch( static_cast<int>( parseDouble( "symbol orientation" ) ) )
3524 {
3525 case 0: transform = TRANSFORM(); break;
3526 case 90: transform = TRANSFORM( 0, 1, -1, 0 ); break;
3527 case 180: transform = TRANSFORM( -1, 0, 0, -1 ); break;
3528 case 270: transform = TRANSFORM( 0, -1, 1, 0 ); break;
3529 default: Expecting( "0, 90, 180, or 270" );
3530 }
3531
3532 symbol->SetTransform( transform );
3533 NeedRIGHT();
3534 break;
3535
3536 case T_mirror:
3537 token = NextTok();
3538
3539 if( token == T_x )
3540 symbol->SetOrientation( SYM_MIRROR_X );
3541 else if( token == T_y )
3542 symbol->SetOrientation( SYM_MIRROR_Y );
3543 else
3544 Expecting( "x or y" );
3545
3546 NeedRIGHT();
3547 break;
3548
3549 case T_unit:
3550 symbol->SetUnit( parseInt( "symbol unit" ) );
3551 NeedRIGHT();
3552 break;
3553
3554 case T_convert: // Legacy token
3555 case T_body_style:
3556 symbol->SetBodyStyle( parseInt( "symbol body style" ) );
3557 NeedRIGHT();
3558 break;
3559
3560 case T_exclude_from_sim:
3561 symbol->SetExcludedFromSim( parseBool() );
3562 NeedRIGHT();
3563 break;
3564
3565 case T_in_bom:
3566 symbol->SetExcludedFromBOM( !parseBool() );
3567 NeedRIGHT();
3568 break;
3569
3570 case T_on_board:
3571 symbol->SetExcludedFromBoard( !parseBool() );
3572 NeedRIGHT();
3573 break;
3574
3575 case T_in_pos_files:
3576 symbol->SetExcludedFromPosFiles( !parseBool() );
3577 NeedRIGHT();
3578 break;
3579
3580 case T_dnp:
3581 symbol->SetDNP( parseBool() );
3582 NeedRIGHT();
3583 break;
3584
3585 case T_pin_map_override: symbol->SetPinMapOverride( parsePinMapOverride() ); break;
3586
3587 case T_locked:
3588 symbol->SetLocked( parseBool() );
3589 NeedRIGHT();
3590 break;
3591
3592 case T_passthrough:
3593 {
3594 // (passthrough default|block|force)
3595 T t = NextTok();
3596
3597 // Expect a string-like token
3598 if( !IsSymbol( t ) )
3599 Expecting( "default, block or force" );
3600
3601 wxString mode = FromUTF8();
3602
3603 if( mode.IsSameAs( wxT("default"), false ) )
3604 symbol->SetPassthroughMode( SCH_SYMBOL::PASSTHROUGH_MODE::DEFAULT );
3605 else if( mode.IsSameAs( wxT("block"), false ) )
3606 symbol->SetPassthroughMode( SCH_SYMBOL::PASSTHROUGH_MODE::BLOCK );
3607 else if( mode.IsSameAs( wxT("force"), false ) )
3608 symbol->SetPassthroughMode( SCH_SYMBOL::PASSTHROUGH_MODE::FORCE );
3609 else
3610 Expecting( "default, block or force" );
3611
3612 NeedRIGHT();
3613 break;
3614 }
3615
3616 case T_fields_autoplaced:
3617 if( parseMaybeAbsentBool( true ) )
3618 symbol->SetFieldsAutoplaced( AUTOPLACE_AUTO );
3619
3620 break;
3621
3622 case T_uuid:
3623 NeedSYMBOL();
3624 const_cast<KIID&>( symbol->m_Uuid ) = parseKIID();
3625 NeedRIGHT();
3626 break;
3627
3628 case T_default_instance:
3629 {
3630 SCH_SYMBOL_INSTANCE defaultInstance;
3631
3632 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3633 {
3634 if( token != T_LEFT )
3635 Expecting( T_LEFT );
3636
3637 token = NextTok();
3638
3639 switch( token )
3640 {
3641 case T_reference:
3642 NeedSYMBOL();
3643 defaultInstance.m_Reference = FromUTF8();
3644 NeedRIGHT();
3645 break;
3646
3647 case T_unit:
3648 defaultInstance.m_Unit = parseInt( "symbol unit" );
3649 NeedRIGHT();
3650 break;
3651
3652 case T_value:
3653 NeedSYMBOL();
3654
3655 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3656 symbol->SetValueFieldText( wxEmptyString );
3657 else
3658 symbol->SetValueFieldText( FromUTF8() );
3659
3660 NeedRIGHT();
3661 break;
3662
3663 case T_footprint:
3664 NeedSYMBOL();
3665
3666 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3667 symbol->SetFootprintFieldText( wxEmptyString );
3668 else
3669 symbol->SetFootprintFieldText( FromUTF8() );
3670
3671 NeedRIGHT();
3672 break;
3673
3674 default:
3675 Expecting( "reference, unit, value or footprint" );
3676 }
3677 }
3678
3679 break;
3680 }
3681
3682 case T_instances:
3683 {
3684 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3685 {
3686 if( token != T_LEFT )
3687 Expecting( T_LEFT );
3688
3689 token = NextTok();
3690
3691 if( token != T_project )
3692 Expecting( "project" );
3693
3694 NeedSYMBOL();
3695
3696 wxString projectName = FromUTF8();
3697
3698 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3699 {
3700 if( token != T_LEFT )
3701 Expecting( T_LEFT );
3702
3703 token = NextTok();
3704
3705 if( token != T_path )
3706 Expecting( "path" );
3707
3708 SCH_SYMBOL_INSTANCE instance;
3709
3710 instance.m_ProjectName = projectName;
3711
3712 NeedSYMBOL();
3713 instance.m_Path = KIID_PATH( FromUTF8() );
3714
3715 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3716 {
3717 if( token != T_LEFT )
3718 Expecting( T_LEFT );
3719
3720 token = NextTok();
3721
3722 switch( token )
3723 {
3724 case T_reference:
3725 NeedSYMBOL();
3726 instance.m_Reference = FromUTF8();
3727 NeedRIGHT();
3728 break;
3729
3730 case T_unit:
3731 instance.m_Unit = parseInt( "symbol unit" );
3732 NeedRIGHT();
3733 break;
3734
3735 case T_value:
3736 NeedSYMBOL();
3737
3738 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3739 symbol->SetValueFieldText( wxEmptyString );
3740 else
3741 symbol->SetValueFieldText( FromUTF8() );
3742
3743 NeedRIGHT();
3744 break;
3745
3746 case T_footprint:
3747 NeedSYMBOL();
3748
3749 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3750 symbol->SetFootprintFieldText( wxEmptyString );
3751 else
3752 symbol->SetFootprintFieldText( FromUTF8() );
3753
3754 NeedRIGHT();
3755 break;
3756
3757
3758 case T_variant:
3759 {
3760 SCH_SYMBOL_VARIANT variant;
3761 variant.InitializeAttributes( *symbol );
3762
3763 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3764 {
3765 if( token != T_LEFT )
3766 Expecting( T_LEFT );
3767
3768 token = NextTok();
3769
3770 switch( token )
3771 {
3772 case T_name:
3773 NeedSYMBOL();
3774 variant.m_Name = FromUTF8();
3775 NeedRIGHT();
3776 break;
3777
3778 case T_dnp:
3779 variant.m_DNP = parseBool();
3780 NeedRIGHT();
3781 break;
3782
3783 case T_exclude_from_sim:
3784 variant.m_ExcludedFromSim = parseBool();
3785 NeedRIGHT();
3786 break;
3787
3788 case T_in_bom:
3789 variant.m_ExcludedFromBOM = parseBool();
3790
3791 // This fixes the incorrect logic from prior file versions. The "in_bom" token
3792 // used in the file format is the positive logic. However, in the UI the term
3793 // excluded from BOM is used which is the inverted logic.
3794 if( m_requiredVersion >= 20260306 )
3795 variant.m_ExcludedFromBOM = !variant.m_ExcludedFromBOM;
3796
3797 NeedRIGHT();
3798 break;
3799
3800 case T_on_board:
3801 variant.m_ExcludedFromBoard = !parseBool();
3802 NeedRIGHT();
3803 break;
3804
3805 case T_in_pos_files:
3806 variant.m_ExcludedFromPosFiles = !parseBool();
3807 NeedRIGHT();
3808 break;
3809
3810 case T_field:
3811 {
3812 wxString fieldName;
3813 wxString fieldValue;
3814
3815 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3816 {
3817 if( token != T_LEFT )
3818 Expecting( T_LEFT );
3819
3820 token = NextTok();
3821
3822 switch( token )
3823 {
3824 case T_name:
3825 NeedSYMBOL();
3826 fieldName = FromUTF8();
3827 NeedRIGHT();
3828 break;
3829
3830 case T_value:
3831 NeedSYMBOL();
3832 fieldValue = FromUTF8();
3833 NeedRIGHT();
3834 break;
3835
3836 default:
3837 Expecting( "name or value" );
3838 }
3839 }
3840
3841 variant.m_Fields[fieldName] = fieldValue;
3842 break;
3843 }
3844
3845 case T_pin_map_override: variant.m_PinMapOverride = parsePinMapOverride(); break;
3846
3847 case T_symbol_override: variant.m_SymbolOverride = parseSymbolOverride(); break;
3848
3849 default:
3850 Expecting( "dnp, exclude_from_sim, field, in_bom, in_pos_files, name, "
3851 "on_board, pin_map_override, or symbol_override" );
3852 }
3853
3854 instance.m_Variants[variant.m_Name] = variant;
3855 }
3856
3857 break;
3858 }
3859
3860 default:
3861 Expecting( "reference, unit, value, footprint, or variant" );
3862 }
3863 }
3864
3865 symbol->AddHierarchicalReference( instance );
3866 }
3867 }
3868
3869 break;
3870 }
3871
3872 case T_property:
3873 // The field parent symbol must be set and its orientation must be set before
3874 // the field positions are set.
3875 field = parseSchField( symbol.get() );
3876
3877 // Exclude from simulation used to be managed by a Sim.Enable field set to "0" when
3878 // simulation was disabled.
3880 {
3881 symbol->SetExcludedFromSim( field->GetText() == wxS( "0" ) );
3882 delete field;
3883 break;
3884 }
3885
3886 // Even longer ago, we had a "Spice_Netlist_Enabled" field
3888 {
3889 symbol->SetExcludedFromSim( field->GetText() == wxS( "N" ) );
3890 delete field;
3891 break;
3892 }
3893
3894 SCH_FIELD* existing;
3895
3896 if( field->IsMandatory() )
3897 existing = symbol->GetField( field->GetId() );
3898 else
3899 existing = symbol->GetField( field->GetName() );
3900
3901 if( existing )
3902 *existing = *field;
3903 else
3904 symbol->AddField( *field );
3905
3906 if( field->GetId() == FIELD_T::REFERENCE )
3907 symbol->UpdatePrefix();
3908
3909 delete field;
3910 break;
3911
3912 case T_pin:
3913 {
3914 // Read an alternate pin designation
3915 wxString number;
3916 KIID uuid;
3917 wxString alt;
3918
3919 NeedSYMBOL();
3920 number = FromUTF8();
3921
3922 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3923 {
3924 if( token != T_LEFT )
3925 Expecting( T_LEFT );
3926
3927 token = NextTok();
3928
3929 switch( token )
3930 {
3931 case T_alternate:
3932 NeedSYMBOL();
3933 alt = FromUTF8();
3934 NeedRIGHT();
3935 break;
3936
3937 case T_uuid:
3938 NeedSYMBOL();
3939
3940 // First version to write out pin uuids accidentally wrote out the symbol's
3941 // uuid for each pin, so ignore uuids coming from that version.
3942 if( m_requiredVersion >= 20210126 )
3943 uuid = parseKIID();
3944
3945 NeedRIGHT();
3946 break;
3947
3948 default:
3949 Expecting( "alternate or uuid" );
3950 }
3951 }
3952
3953 symbol->GetRawPins().emplace_back( std::make_unique<SCH_PIN>( symbol.get(), number,
3954 alt, uuid ) );
3955 break;
3956 }
3957
3958 default:
3959 Expecting( "lib_id, lib_name, at, mirror, uuid, exclude_from_sim, on_board, in_bom, dnp, passthrough, "
3960 "default_instance, property, pin, or instances" );
3961 }
3962 }
3963
3964 if( !libName.IsEmpty() && ( symbol->GetLibId().Format().wx_str() != libName ) )
3965 symbol->SetSchSymbolLibraryName( libName );
3966
3967 // Ensure edit/status flags are cleared after these initializations:
3968 symbol->ClearFlags();
3969
3970 return symbol.release();
3971}
3972
3973
3975{
3976 wxCHECK_MSG( CurTok() == T_image, nullptr,
3977 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an image." ) );
3978
3979 T token;
3980 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>();
3981 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
3982
3983 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3984 {
3985 if( token != T_LEFT )
3986 Expecting( T_LEFT );
3987
3988 token = NextTok();
3989
3990 switch( token )
3991 {
3992 case T_at:
3993 bitmap->SetPosition( parseXY() );
3994 NeedRIGHT();
3995 break;
3996
3997 case T_scale:
3998 {
3999 const double scale = parseDouble( "image scale factor" );
4000 refImage.SetImageScale( std::isnormal( scale ) ? scale : 1.0 );
4001
4002 NeedRIGHT();
4003 break;
4004 }
4005 case T_uuid:
4006 NeedSYMBOL();
4007 const_cast<KIID&>( bitmap->m_Uuid ) = parseKIID();
4008 NeedRIGHT();
4009 break;
4010
4011 case T_data:
4012 {
4013 token = NextTok();
4014
4015 wxString data;
4016
4017 // Reserve 128K because most image files are going to be larger than the default
4018 // 1K that wxString reserves.
4019 data.reserve( 1 << 17 );
4020
4021 while( token != T_RIGHT )
4022 {
4023 if( !IsSymbol( token ) )
4024 Expecting( "base64 image data" );
4025
4026 data += FromUTF8();
4027 token = NextTok();
4028 }
4029
4030 wxMemoryBuffer buffer = wxBase64Decode( data );
4031
4032 if( !refImage.ReadImageFile( buffer ) )
4033 THROW_IO_ERROR( _( "Failed to read image data." ) );
4034
4035 break;
4036 }
4037
4038 case T_locked:
4039 bitmap->SetLocked( parseBool() );
4040 NeedRIGHT();
4041 break;
4042
4043 default:
4044 Expecting( "at, scale, uuid, data or locked" );
4045 }
4046 }
4047
4048 // The image will be scaled by PPI in ReadImageFile.
4049
4050 // 20230121 or older file format versions assumed 300 image PPI at load/save.
4051 // Let's keep compatibility by changing image scale.
4052 if( m_requiredVersion <= 20230121 )
4053 {
4054 refImage.SetImageScale( refImage.GetImageScale() * refImage.GetImage().GetPPI() / 300.0 );
4055 }
4056 else if( m_requiredVersion < 20260623 )
4057 {
4058 // Between the 300-PPI era and 20260623 the PPI was computed from the embedded
4059 // resolution but pixels/cm was truncated to an integer, so the stored scale
4060 // compensated for the wrong PPI. Re-scale to the corrected PPI to preserve size.
4061 const BITMAP_BASE& image = refImage.GetImage();
4062 int legacyPPI = image.GetLegacyPPI();
4063
4064 if( legacyPPI > 0 && image.GetPPI() != legacyPPI )
4065 refImage.SetImageScale( refImage.GetImageScale() * image.GetPPI() / legacyPPI );
4066 }
4067
4068 return bitmap.release();
4069}
4070
4071
4073{
4074 wxCHECK_MSG( CurTok() == T_sheet, nullptr,
4075 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a sheet." ) );
4076
4077 T token;
4079 FILL_PARAMS fill;
4080 SCH_FIELD* field;
4081 std::vector<SCH_FIELD> fields;
4082 std::unique_ptr<SCH_SHEET> sheet = std::make_unique<SCH_SHEET>();
4083 std::set<int> fieldIDsRead;
4084
4085 // We'll reset this if we find a fields_autoplaced token
4086 sheet->SetFieldsAutoplaced( AUTOPLACE_NONE );
4087
4088 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4089 {
4090 if( token != T_LEFT )
4091 Expecting( T_LEFT );
4092
4093 token = NextTok();
4094
4095 switch( token )
4096 {
4097 case T_at:
4098 sheet->SetPosition( parseXY() );
4099 NeedRIGHT();
4100 break;
4101
4102 case T_size:
4103 {
4104 VECTOR2I size;
4105 size.x = parseInternalUnits( "sheet width" );
4106 size.y = parseInternalUnits( "sheet height" );
4107 sheet->SetSize( size );
4108 NeedRIGHT();
4109 break;
4110 }
4111
4112 case T_exclude_from_sim:
4113 sheet->SetExcludedFromSim( parseBool() );
4114 NeedRIGHT();
4115 break;
4116
4117 case T_in_bom:
4118 sheet->SetExcludedFromBOM( !parseBool() );
4119 NeedRIGHT();
4120 break;
4121
4122 case T_on_board:
4123 sheet->SetExcludedFromBoard( !parseBool() );
4124 NeedRIGHT();
4125 break;
4126
4127 case T_dnp:
4128 sheet->SetDNP( parseBool() );
4129 NeedRIGHT();
4130 break;
4131
4132 case T_locked:
4133 sheet->SetLocked( parseBool() );
4134 NeedRIGHT();
4135 break;
4136
4137 case T_fields_autoplaced:
4138 if( parseMaybeAbsentBool( true ) )
4139 sheet->SetFieldsAutoplaced( AUTOPLACE_AUTO );
4140
4141 break;
4142
4143 case T_stroke:
4144 parseStroke( stroke );
4145 sheet->SetBorderWidth( stroke.GetWidth() );
4146 sheet->SetBorderColor( stroke.GetColor() );
4147 break;
4148
4149 case T_fill:
4150 parseFill( fill );
4151 sheet->SetBackgroundColor( fill.m_Color );
4152 break;
4153
4154 case T_uuid:
4155 NeedSYMBOL();
4156 const_cast<KIID&>( sheet->m_Uuid ) = parseKIID();
4157 NeedRIGHT();
4158 break;
4159
4160 case T_property:
4161 field = parseSchField( sheet.get() );
4162
4163 if( m_requiredVersion <= 20200310 )
4164 {
4165 // Earlier versions had the wrong ids (and names) saved for sheet fields.
4166 // Fortunately they only saved the sheetname and sheetfilepath (and always
4167 // in that order), so we can hack in a recovery.
4168 if( fields.empty() )
4169 field->setId( FIELD_T::SHEET_NAME );
4170 else
4172 }
4173
4174 fields.emplace_back( *field );
4175
4176 delete field;
4177 break;
4178
4179 case T_pin:
4180 sheet->AddPin( parseSchSheetPin( sheet.get() ) );
4181 break;
4182
4183 case T_instances:
4184 {
4185 std::vector<SCH_SHEET_INSTANCE> instances;
4186
4187 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4188 {
4189 if( token != T_LEFT )
4190 Expecting( T_LEFT );
4191
4192 token = NextTok();
4193
4194 if( token != T_project )
4195 Expecting( "project" );
4196
4197 NeedSYMBOL();
4198
4199 wxString projectName = FromUTF8();
4200
4201 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4202 {
4203 if( token != T_LEFT )
4204 Expecting( T_LEFT );
4205
4206 token = NextTok();
4207
4208 if( token != T_path )
4209 Expecting( "path" );
4210
4211 SCH_SHEET_INSTANCE instance;
4212
4213 instance.m_ProjectName = projectName;
4214
4215 NeedSYMBOL();
4216 instance.m_Path = KIID_PATH( FromUTF8() );
4217
4218 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4219 {
4220 if( token != T_LEFT )
4221 Expecting( T_LEFT );
4222
4223 token = NextTok();
4224
4225 switch( token )
4226 {
4227 case T_page:
4228 {
4229 NeedSYMBOL();
4230 instance.m_PageNumber = FromUTF8();
4231
4232 // Empty page numbers are not permitted
4233 if( instance.m_PageNumber.IsEmpty() )
4234 {
4235 // Use hash character instead
4236 instance.m_PageNumber = wxT( "#" );
4237 }
4238 else
4239 {
4240 // Whitespaces are not permitted
4241 static std::vector<wxString> whitespaces =
4242 { wxT( "\r" ),
4243 wxT( "\n" ),
4244 wxT( "\t" ),
4245 wxT( " " ) };
4246
4247 for( wxString ch : whitespaces )
4248 instance.m_PageNumber.Replace( ch, wxEmptyString );
4249 }
4250
4251 NeedRIGHT();
4252 break;
4253 }
4254
4255 case T_variant:
4256 {
4257 SCH_SHEET_VARIANT variant;
4258 variant.InitializeAttributes( *sheet );
4259
4260 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4261 {
4262 if( token != T_LEFT )
4263 Expecting( T_LEFT );
4264
4265 token = NextTok();
4266
4267 switch( token )
4268 {
4269 case T_name:
4270 NeedSYMBOL();
4271 variant.m_Name = FromUTF8();
4272 NeedRIGHT();
4273 break;
4274
4275 case T_dnp:
4276 variant.m_DNP = parseBool();
4277 NeedRIGHT();
4278 break;
4279
4280 case T_exclude_from_sim:
4281 variant.m_ExcludedFromSim = parseBool();
4282 NeedRIGHT();
4283 break;
4284
4285 case T_in_bom:
4286 variant.m_ExcludedFromBOM = parseBool();
4287
4288 // This fixes the incorrect logic from prior file versions. The "in_bom" token
4289 // used in the file format is the positive logic. However, in the UI the term
4290 // excluded from BOM is used which is the inverted logic.
4291 if( m_requiredVersion >= 20260306 )
4292 variant.m_ExcludedFromBOM = !variant.m_ExcludedFromBOM;
4293
4294 NeedRIGHT();
4295 break;
4296
4297 case T_on_board:
4298 variant.m_ExcludedFromBoard = !parseBool();
4299 NeedRIGHT();
4300 break;
4301
4302 case T_in_pos_files:
4303 variant.m_ExcludedFromPosFiles = !parseBool();
4304 NeedRIGHT();
4305 break;
4306
4307 case T_field:
4308 {
4309 wxString fieldName;
4310 wxString fieldValue;
4311
4312 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4313 {
4314 if( token != T_LEFT )
4315 Expecting( T_LEFT );
4316
4317 token = NextTok();
4318
4319 switch( token )
4320 {
4321 case T_name:
4322 NeedSYMBOL();
4323 fieldName = FromUTF8();
4324 NeedRIGHT();
4325 break;
4326
4327 case T_value:
4328 NeedSYMBOL();
4329 fieldValue = FromUTF8();
4330 NeedRIGHT();
4331 break;
4332
4333 default:
4334 Expecting( "name or value" );
4335 }
4336 }
4337
4338 variant.m_Fields[fieldName] = fieldValue;
4339 break;
4340 }
4341
4342 default:
4343 Expecting( "dnp, exclude_from_sim, field, in_bom, in_pos_files, name, or on_board" );
4344 }
4345
4346 instance.m_Variants[variant.m_Name] = variant;
4347 }
4348
4349 break;
4350 }
4351
4352 default:
4353 Expecting( "page or variant" );
4354 }
4355 }
4356
4357 instances.emplace_back( instance );
4358 }
4359 }
4360
4361 sheet->setInstances( instances );
4362 break;
4363 }
4364
4365 default:
4366 Expecting( "at, size, stroke, background, instances, uuid, property, or pin" );
4367 }
4368 }
4369
4370 sheet->SetFields( fields );
4371
4372 if( !FindField( sheet->GetFields(), FIELD_T::SHEET_NAME ) )
4373 {
4374 THROW_PARSE_ERROR( _( "Missing sheet name property" ), CurSource(), CurLine(),
4375 CurLineNumber(), CurOffset() );
4376 }
4377
4378 if( !FindField( sheet->GetFields(), FIELD_T::SHEET_FILENAME ) )
4379 {
4380 THROW_PARSE_ERROR( _( "Missing sheet file property" ), CurSource(), CurLine(),
4381 CurLineNumber(), CurOffset() );
4382 }
4383
4384 return sheet.release();
4385}
4386
4387
4389{
4390 wxCHECK_MSG( CurTok() == T_junction, nullptr,
4391 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a junction." ) );
4392
4393 T token;
4394 std::unique_ptr<SCH_JUNCTION> junction = std::make_unique<SCH_JUNCTION>();
4395
4396 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4397 {
4398 if( token != T_LEFT )
4399 Expecting( T_LEFT );
4400
4401 token = NextTok();
4402
4403 switch( token )
4404 {
4405 case T_at:
4406 junction->SetPosition( parseXY() );
4407 NeedRIGHT();
4408 break;
4409
4410 case T_diameter:
4411 junction->SetDiameter( parseInternalUnits( "junction diameter" ) );
4412 NeedRIGHT();
4413 break;
4414
4415 case T_color:
4416 {
4417 COLOR4D color;
4418
4419 color.r = parseInt( "red" ) / 255.0;
4420 color.g = parseInt( "green" ) / 255.0;
4421 color.b = parseInt( "blue" ) / 255.0;
4422 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
4423
4424 junction->SetColor( color );
4425 NeedRIGHT();
4426 break;
4427 }
4428
4429 case T_uuid:
4430 NeedSYMBOL();
4431 const_cast<KIID&>( junction->m_Uuid ) = parseKIID();
4432 NeedRIGHT();
4433 break;
4434
4435 case T_locked:
4436 junction->SetLocked( parseBool() );
4437 NeedRIGHT();
4438 break;
4439
4440 default:
4441 Expecting( "at, diameter, color, uuid or locked" );
4442 }
4443 }
4444
4445 return junction.release();
4446}
4447
4448
4450{
4451 wxCHECK_MSG( CurTok() == T_no_connect, nullptr,
4452 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a no connect." ) );
4453
4454 T token;
4455 std::unique_ptr<SCH_NO_CONNECT> no_connect = std::make_unique<SCH_NO_CONNECT>();
4456
4457 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4458 {
4459 if( token != T_LEFT )
4460 Expecting( T_LEFT );
4461
4462 token = NextTok();
4463
4464 switch( token )
4465 {
4466 case T_at:
4467 no_connect->SetPosition( parseXY() );
4468 NeedRIGHT();
4469 break;
4470
4471 case T_uuid:
4472 NeedSYMBOL();
4473 const_cast<KIID&>( no_connect->m_Uuid ) = parseKIID();
4474 NeedRIGHT();
4475 break;
4476
4477 case T_locked:
4478 no_connect->SetLocked( parseBool() );
4479 NeedRIGHT();
4480 break;
4481
4482 default:
4483 Expecting( "at, uuid or locked" );
4484 }
4485 }
4486
4487 return no_connect.release();
4488}
4489
4490
4492{
4493 wxCHECK_MSG( CurTok() == T_bus_entry, nullptr,
4494 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus entry." ) );
4495
4496 T token;
4498 std::unique_ptr<SCH_BUS_WIRE_ENTRY> busEntry = std::make_unique<SCH_BUS_WIRE_ENTRY>();
4499
4500 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4501 {
4502 if( token != T_LEFT )
4503 Expecting( T_LEFT );
4504
4505 token = NextTok();
4506
4507 switch( token )
4508 {
4509 case T_at:
4510 busEntry->SetPosition( parseXY() );
4511 NeedRIGHT();
4512 break;
4513
4514 case T_size:
4515 {
4516 VECTOR2I size;
4517
4518 size.x = parseInternalUnits( "bus entry height" );
4519 size.y = parseInternalUnits( "bus entry width" );
4520 busEntry->SetSize( size );
4521 NeedRIGHT();
4522 break;
4523 }
4524
4525 case T_stroke:
4526 parseStroke( stroke );
4527 busEntry->SetStroke( stroke );
4528 break;
4529
4530 case T_uuid:
4531 NeedSYMBOL();
4532 const_cast<KIID&>( busEntry->m_Uuid ) = parseKIID();
4533 NeedRIGHT();
4534 break;
4535
4536 case T_locked:
4537 busEntry->SetLocked( parseBool() );
4538 NeedRIGHT();
4539 break;
4540
4541 default:
4542 Expecting( "at, size, uuid, stroke or locked" );
4543 }
4544 }
4545
4546 return busEntry.release();
4547}
4548
4549
4551{
4552 if( aShape->GetFillMode() == FILL_T::FILLED_SHAPE )
4553 {
4554 aShape->SetFillColor( aShape->GetStroke().GetColor() );
4556 }
4557}
4558
4559
4561{
4562 T token;
4564 FILL_PARAMS fill;
4565
4566 std::unique_ptr<SCH_SHAPE> polyline = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_NOTES );
4567
4568 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4569 {
4570 if( token != T_LEFT )
4571 Expecting( T_LEFT );
4572
4573 token = NextTok();
4574
4575 switch( token )
4576 {
4577 case T_pts:
4578 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4579 {
4580 if( token != T_LEFT )
4581 Expecting( T_LEFT );
4582
4583 token = NextTok();
4584
4585 if( token != T_xy )
4586 Expecting( "xy" );
4587
4588 polyline->AddPoint( parseXY() );
4589
4590 NeedRIGHT();
4591 }
4592 break;
4593
4594 case T_stroke:
4595 parseStroke( stroke );
4596
4597 // In 6.0, the default schematic line style was Dashed.
4598 if( m_requiredVersion <= 20211123 && stroke.GetLineStyle() == LINE_STYLE::DEFAULT )
4600
4601 polyline->SetStroke( stroke );
4602 break;
4603
4604 case T_fill:
4605 parseFill( fill );
4606 polyline->SetFillMode( fill.m_FillType );
4607 polyline->SetFillColor( fill.m_Color );
4608 fixupSchFillMode( polyline.get() );
4609 break;
4610
4611 case T_uuid:
4612 NeedSYMBOL();
4613 const_cast<KIID&>( polyline->m_Uuid ) = parseKIID();
4614 NeedRIGHT();
4615 break;
4616
4617 case T_locked:
4618 polyline->SetLocked( parseBool() );
4619 NeedRIGHT();
4620 break;
4621
4622 default:
4623 Expecting( "pts, uuid, stroke, fill or locked" );
4624 }
4625 }
4626
4627 return polyline.release();
4628}
4629
4630
4632{
4633 // Note: T_polyline is deprecated in this code: it is now handled by
4634 // parseSchPolyLine() that can handle true polygons, and not only one segment.
4635
4636 T token;
4638 int layer;
4639
4640 switch( CurTok() )
4641 {
4642 case T_polyline: layer = LAYER_NOTES; break;
4643 case T_wire: layer = LAYER_WIRE; break;
4644 case T_bus: layer = LAYER_BUS; break;
4645 default:
4646 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as a line." );
4647 }
4648
4649 std::unique_ptr<SCH_LINE> line = std::make_unique<SCH_LINE>( VECTOR2I(), layer );
4650
4651 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4652 {
4653 if( token != T_LEFT )
4654 Expecting( T_LEFT );
4655
4656 token = NextTok();
4657
4658 switch( token )
4659 {
4660 case T_pts:
4661 NeedLEFT();
4662 token = NextTok();
4663
4664 if( token != T_xy )
4665 Expecting( "xy" );
4666
4667 line->SetStartPoint( parseXY() );
4668 NeedRIGHT();
4669 NeedLEFT();
4670 token = NextTok();
4671
4672 if( token != T_xy )
4673 Expecting( "xy" );
4674
4675 line->SetEndPoint( parseXY() );
4676 NeedRIGHT();
4677 NeedRIGHT();
4678 break;
4679
4680 case T_stroke:
4681 parseStroke( stroke );
4682 line->SetStroke( stroke );
4683 break;
4684
4685 case T_uuid:
4686 NeedSYMBOL();
4687 const_cast<KIID&>( line->m_Uuid ) = parseKIID();
4688 NeedRIGHT();
4689 break;
4690
4691 case T_locked:
4692 line->SetLocked( parseBool() );
4693 NeedRIGHT();
4694 break;
4695
4696 default:
4697 Expecting( "pts, uuid, stroke or locked" );
4698 }
4699 }
4700
4701 return line.release();
4702}
4703
4704
4706{
4707 wxCHECK_MSG( CurTok() == T_arc, nullptr,
4708 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc." ) );
4709
4710 T token;
4711 VECTOR2I startPoint;
4712 VECTOR2I midPoint;
4713 VECTOR2I endPoint;
4715 FILL_PARAMS fill;
4716 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC );
4717
4718 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4719 {
4720 if( token != T_LEFT )
4721 Expecting( T_LEFT );
4722
4723 token = NextTok();
4724
4725 switch( token )
4726 {
4727 case T_start:
4728 startPoint = parseXY();
4729 NeedRIGHT();
4730 break;
4731
4732 case T_mid:
4733 midPoint = parseXY();
4734 NeedRIGHT();
4735 break;
4736
4737 case T_end:
4738 endPoint = parseXY();
4739 NeedRIGHT();
4740 break;
4741
4742 case T_stroke:
4743 parseStroke( stroke );
4744 arc->SetStroke( stroke );
4745 break;
4746
4747 case T_fill:
4748 parseFill( fill );
4749 arc->SetFillMode( fill.m_FillType );
4750 arc->SetFillColor( fill.m_Color );
4751 fixupSchFillMode( arc.get() );
4752 break;
4753
4754 case T_uuid:
4755 NeedSYMBOL();
4756 const_cast<KIID&>( arc->m_Uuid ) = parseKIID();
4757 NeedRIGHT();
4758 break;
4759
4760 case T_locked:
4761 arc->SetLocked( parseBool() );
4762 NeedRIGHT();
4763 break;
4764
4765 default:
4766 Expecting( "start, mid, end, stroke, fill, uuid or locked" );
4767 }
4768 }
4769
4770 arc->SetArcGeometry( startPoint, midPoint, endPoint );
4771
4772 return arc.release();
4773}
4774
4775
4777{
4778 wxCHECK_MSG( CurTok() == T_circle, nullptr,
4779 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle." ) );
4780
4781 T token;
4783 int radius = 0;
4785 FILL_PARAMS fill;
4786 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE );
4787
4788 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4789 {
4790 if( token != T_LEFT )
4791 Expecting( T_LEFT );
4792
4793 token = NextTok();
4794
4795 switch( token )
4796 {
4797 case T_center:
4798 center = parseXY();
4799 NeedRIGHT();
4800 break;
4801
4802 case T_radius:
4803 radius = parseInternalUnits( "radius length" );
4804 NeedRIGHT();
4805 break;
4806
4807 case T_stroke:
4808 parseStroke( stroke );
4809 circle->SetStroke( stroke );
4810 break;
4811
4812 case T_fill:
4813 parseFill( fill );
4814 circle->SetFillMode( fill.m_FillType );
4815 circle->SetFillColor( fill.m_Color );
4816 fixupSchFillMode( circle.get() );
4817 break;
4818
4819 case T_uuid:
4820 NeedSYMBOL();
4821 const_cast<KIID&>( circle->m_Uuid ) = parseKIID();
4822 NeedRIGHT();
4823 break;
4824
4825 case T_locked:
4826 circle->SetLocked( parseBool() );
4827 NeedRIGHT();
4828 break;
4829
4830 default:
4831 Expecting( "center, radius, stroke, fill, uuid or locked" );
4832 }
4833 }
4834
4835 circle->SetCenter( center );
4836 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
4837
4838 return circle.release();
4839}
4840
4841
4843{
4844 wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
4845 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle." ) );
4846
4847 T token;
4849 FILL_PARAMS fill;
4850 std::unique_ptr<SCH_SHAPE> rectangle = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE );
4851
4852 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4853 {
4854 if( token != T_LEFT )
4855 Expecting( T_LEFT );
4856
4857 token = NextTok();
4858
4859 switch( token )
4860 {
4861 case T_start:
4862 rectangle->SetPosition( parseXY() );
4863 NeedRIGHT();
4864 break;
4865
4866 case T_end:
4867 rectangle->SetEnd( parseXY() );
4868 NeedRIGHT();
4869 break;
4870
4871 case T_radius:
4872 rectangle->SetCornerRadius( parseDouble( "corner radius" ) * schIUScale.IU_PER_MM );
4873 NeedRIGHT();
4874 break;
4875
4876 case T_stroke:
4877 parseStroke( stroke );
4878 rectangle->SetStroke( stroke );
4879 break;
4880
4881 case T_fill:
4882 parseFill( fill );
4883 rectangle->SetFillMode( fill.m_FillType );
4884 rectangle->SetFillColor( fill.m_Color );
4885 fixupSchFillMode( rectangle.get() );
4886 break;
4887
4888 case T_uuid:
4889 NeedSYMBOL();
4890 const_cast<KIID&>( rectangle->m_Uuid ) = parseKIID();
4891 NeedRIGHT();
4892 break;
4893
4894 case T_locked:
4895 rectangle->SetLocked( parseBool() );
4896 NeedRIGHT();
4897 break;
4898
4899 default:
4900 Expecting( "start, end, stroke, fill, uuid or locked" );
4901 }
4902 }
4903
4904 return rectangle.release();
4905}
4906
4907
4909{
4910 wxCHECK_MSG( CurTok() == T_rule_area, nullptr,
4911 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rule area." ) );
4912
4913 T token;
4915 FILL_PARAMS fill;
4916 std::unique_ptr<SCH_RULE_AREA> ruleArea = std::make_unique<SCH_RULE_AREA>();
4917
4918 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4919 {
4920 if( token != T_LEFT )
4921 Expecting( T_LEFT );
4922
4923 token = NextTok();
4924
4925 switch( token )
4926 {
4927 case T_polyline:
4928 {
4929 std::unique_ptr<SCH_SHAPE> poly( parseSchPolyLine() );
4930 SHAPE_POLY_SET& sch_rule_poly = poly->GetPolyShape();
4931
4932 // The polygon must be closed, it is a schematic closed polyline:
4933 sch_rule_poly.Outline(0).SetClosed( true );
4934
4935 ruleArea->SetPolyShape( sch_rule_poly );
4936
4937 ruleArea->SetStroke( poly->GetStroke() );
4938 ruleArea->SetFillMode( poly->GetFillMode() );
4939 ruleArea->SetFillColor( poly->GetFillColor() );
4940
4941 // the uuid is saved to the shape but stored and saved out of the rulearea
4942 const_cast<KIID&>( ruleArea->m_Uuid ) = poly->m_Uuid;
4943 break;
4944 }
4945
4946 case T_exclude_from_sim:
4947 ruleArea->SetExcludedFromSim( parseBool() );
4948 NeedRIGHT();
4949 break;
4950
4951 case T_in_bom:
4952 ruleArea->SetExcludedFromBOM( !parseBool() );
4953 NeedRIGHT();
4954 break;
4955
4956 case T_on_board:
4957 ruleArea->SetExcludedFromBoard( !parseBool() );
4958 NeedRIGHT();
4959 break;
4960
4961 case T_dnp:
4962 ruleArea->SetDNP( parseBool() );
4963 NeedRIGHT();
4964 break;
4965
4966 case T_locked:
4967 ruleArea->SetLocked( parseBool() );
4968 NeedRIGHT();
4969 break;
4970
4971 default:
4972 Expecting( "exclude_from_sim, on_board, in_bom, dnp, locked, or polyline" );
4973 }
4974 }
4975
4976 return ruleArea.release();
4977}
4978
4979
4981{
4982 wxCHECK_MSG( CurTok() == T_bezier, nullptr,
4983 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
4984
4985 T token;
4987 FILL_PARAMS fill;
4988 std::unique_ptr<SCH_SHAPE> bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER );
4989
4990 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4991 {
4992 if( token != T_LEFT )
4993 Expecting( T_LEFT );
4994
4995 token = NextTok();
4996
4997 switch( token )
4998 {
4999 case T_pts:
5000 {
5001 int ii = 0;
5002
5003 for( token = NextTok(); token != T_RIGHT; token = NextTok(), ++ii )
5004 {
5005 if( token != T_LEFT )
5006 Expecting( T_LEFT );
5007
5008 token = NextTok();
5009
5010 if( token != T_xy )
5011 Expecting( "xy" );
5012
5013 switch( ii )
5014 {
5015 case 0: bezier->SetStart( parseXY() ); break;
5016 case 1: bezier->SetBezierC1( parseXY() ); break;
5017 case 2: bezier->SetBezierC2( parseXY() ); break;
5018 case 3: bezier->SetEnd( parseXY() ); break;
5019 default: Unexpected( "control point" ); break;
5020 }
5021
5022 NeedRIGHT();
5023 }
5024 }
5025 break;
5026
5027 case T_stroke:
5028 parseStroke( stroke );
5029 bezier->SetStroke( stroke );
5030 break;
5031
5032 case T_fill:
5033 parseFill( fill );
5034 bezier->SetFillMode( fill.m_FillType );
5035 bezier->SetFillColor( fill.m_Color );
5036 fixupSchFillMode( bezier.get() );
5037 break;
5038
5039 case T_uuid:
5040 NeedSYMBOL();
5041 const_cast<KIID&>( bezier->m_Uuid ) = parseKIID();
5042 NeedRIGHT();
5043 break;
5044
5045 case T_locked:
5046 bezier->SetLocked( parseBool() );
5047 NeedRIGHT();
5048 break;
5049
5050 default:
5051 Expecting( "pts, stroke, fill, uuid or locked" );
5052 }
5053 }
5054
5055 bezier->RebuildBezierToSegmentsPointsList( m_maxError );
5056
5057 return bezier.release();
5058}
5059
5060
5061void SCH_IO_KICAD_SEXPR_PARSER::parseEllipseBody( SCH_SHAPE* aShape, bool aIsArc, bool aIsSchematic,
5062 TSCHEMATIC_T::T aFirstTok )
5063{
5064 // Defaults for optional/missing fields. All fields are expected in a well-formed
5065 // file, but we fall back to safe defaults rather than rejecting on missing fields
5066 // to be forward-compatible with future format additions.
5067 VECTOR2I center( 0, 0 );
5068 int majorRadius = 1;
5069 int minorRadius = 1;
5070 EDA_ANGLE rotation = ANGLE_0;
5071 EDA_ANGLE startAngle = ANGLE_0;
5072 EDA_ANGLE endAngle = ANGLE_90;
5074 FILL_PARAMS fill;
5075
5076 for( T token = aFirstTok; token != T_RIGHT; token = NextTok() )
5077 {
5078 if( token != T_LEFT )
5079 Expecting( T_LEFT );
5080
5081 token = NextTok();
5082
5083 switch( token )
5084 {
5085 case T_center:
5086 // symbol parsers called parseXY( true ); sch parsers used the default.
5087 center = parseXY( !aIsSchematic );
5088 NeedRIGHT();
5089 break;
5090
5091 case T_major_radius:
5092 majorRadius = parseInternalUnits( "major radius" );
5093 NeedRIGHT();
5094 break;
5095
5096 case T_minor_radius:
5097 minorRadius = parseInternalUnits( "minor radius" );
5098 NeedRIGHT();
5099 break;
5100
5101 case T_rotation_angle:
5102 rotation = EDA_ANGLE( parseDouble( "rotation angle" ), DEGREES_T );
5103 NeedRIGHT();
5104 break;
5105
5106 case T_start_angle:
5107 if( !aIsArc )
5108 Expecting( "start_angle only valid inside ellipse_arc" );
5109 startAngle = EDA_ANGLE( parseDouble( "start angle" ), DEGREES_T );
5110 NeedRIGHT();
5111 break;
5112
5113 case T_end_angle:
5114 if( !aIsArc )
5115 Expecting( "end_angle only valid inside ellipse_arc" );
5116 endAngle = EDA_ANGLE( parseDouble( "end angle" ), DEGREES_T );
5117 NeedRIGHT();
5118 break;
5119
5120 case T_stroke:
5121 parseStroke( stroke );
5122 aShape->SetStroke( stroke );
5123 break;
5124
5125 case T_fill:
5126 parseFill( fill );
5127 aShape->SetFillMode( fill.m_FillType );
5128 aShape->SetFillColor( fill.m_Color );
5129 if( aIsSchematic )
5130 fixupSchFillMode( aShape );
5131 break;
5132
5133 case T_uuid:
5134 if( !aIsSchematic )
5135 Expecting( "uuid only valid in schematic context" );
5136 NeedSYMBOL();
5137 const_cast<KIID&>( aShape->m_Uuid ) = KIID( FromUTF8() );
5138 NeedRIGHT();
5139 break;
5140
5141 case T_locked:
5142 if( !aIsSchematic )
5143 Expecting( "locked only valid in schematic context" );
5144 aShape->SetLocked( parseBool() );
5145 NeedRIGHT();
5146 break;
5147
5148 default:
5149 Expecting( "center, major_radius, minor_radius, rotation_angle, "
5150 "start_angle, end_angle, stroke, fill, uuid, or locked" );
5151 }
5152 }
5153
5154 aShape->SetEllipseCenter( center );
5155 aShape->SetEllipseMajorRadius( majorRadius );
5156 aShape->SetEllipseMinorRadius( minorRadius );
5157 aShape->SetEllipseRotation( rotation );
5158
5159 if( aIsArc )
5160 {
5161 aShape->SetEllipseStartAngle( startAngle );
5162 aShape->SetEllipseEndAngle( endAngle );
5163 }
5164}
5165
5166
5168{
5169 wxCHECK_MSG( CurTok() == T_ellipse, nullptr,
5170 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an ellipse." ) );
5171
5172 auto ellipse = std::make_unique<SCH_SHAPE>( SHAPE_T::ELLIPSE );
5173 parseEllipseBody( ellipse.get(), /*isArc*/ false, /*isSchematic*/ true, NextTok() );
5174 return ellipse.release();
5175}
5176
5177
5179{
5180 wxCHECK_MSG( CurTok() == T_ellipse_arc, nullptr,
5181 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an elliptical arc." ) );
5182
5183 auto arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ELLIPSE_ARC );
5184 parseEllipseBody( arc.get(), /*isArc*/ true, /*isSchematic*/ true, NextTok() );
5185 return arc.release();
5186}
5187
5188
5190{
5191 T token;
5192 std::unique_ptr<SCH_TEXT> text;
5193
5194 switch( CurTok() )
5195 {
5196 case T_text: text = std::make_unique<SCH_TEXT>(); break;
5197 case T_label: text = std::make_unique<SCH_LABEL>(); break;
5198 case T_global_label: text = std::make_unique<SCH_GLOBALLABEL>(); break;
5199 case T_hierarchical_label: text = std::make_unique<SCH_HIERLABEL>(); break;
5200 case T_netclass_flag: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
5201 case T_directive_label: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
5202 default:
5203 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as text." );
5204 }
5205
5206 // We'll reset this if we find a fields_autoplaced token
5207 text->SetFieldsAutoplaced( AUTOPLACE_NONE );
5208
5209 NeedSYMBOL();
5210
5211 text->SetText( FromUTF8() );
5212
5213 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5214 {
5215 if( token != T_LEFT )
5216 Expecting( T_LEFT );
5217
5218 token = NextTok();
5219
5220 switch( token )
5221 {
5222 case T_exclude_from_sim:
5223 text->SetExcludedFromSim( parseBool() );
5224 NeedRIGHT();
5225 break;
5226
5227 case T_at:
5228 text->SetPosition( parseXY() );
5229 text->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ).KeepUpright() );
5230
5231 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() ) )
5232 {
5233 switch( static_cast<int>( label->GetTextAngle().AsDegrees() ) )
5234 {
5235 case 0: label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
5236 case 90: label->SetSpinStyle( SPIN_STYLE::UP ); break;
5237 case 180: label->SetSpinStyle( SPIN_STYLE::LEFT ); break;
5238 case 270: label->SetSpinStyle( SPIN_STYLE::BOTTOM ); break;
5239 default: wxFAIL; label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
5240 }
5241 }
5242
5243 NeedRIGHT();
5244 break;
5245
5246 case T_shape:
5247 {
5248 if( text->Type() == SCH_TEXT_T || text->Type() == SCH_LABEL_T )
5249 Unexpected( T_shape );
5250
5251 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( text.get() );
5252
5253 token = NextTok();
5254
5255 switch( token )
5256 {
5257 case T_input: label->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
5258 case T_output: label->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
5259 case T_bidirectional: label->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
5260 case T_tri_state: label->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE ); break;
5261 case T_passive: label->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
5262 case T_dot: label->SetShape( LABEL_FLAG_SHAPE::F_DOT ); break;
5263 case T_round: label->SetShape( LABEL_FLAG_SHAPE::F_ROUND ); break;
5264 case T_diamond: label->SetShape( LABEL_FLAG_SHAPE::F_DIAMOND ); break;
5265 case T_rectangle: label->SetShape( LABEL_FLAG_SHAPE::F_RECTANGLE ); break;
5266 default:
5267 Expecting( "input, output, bidirectional, tri_state, passive, dot, round, diamond"
5268 "or rectangle" );
5269 }
5270
5271 NeedRIGHT();
5272 break;
5273 }
5274
5275 case T_length:
5276 {
5277 if( text->Type() != SCH_DIRECTIVE_LABEL_T )
5278 Unexpected( T_length );
5279
5280 SCH_DIRECTIVE_LABEL* label = static_cast<SCH_DIRECTIVE_LABEL*>( text.get() );
5281
5282 label->SetPinLength( parseInternalUnits( "pin length" ) );
5283 NeedRIGHT();
5284 }
5285 break;
5286
5287 case T_fields_autoplaced:
5288 if( parseMaybeAbsentBool( true ) )
5289 text->SetFieldsAutoplaced( AUTOPLACE_AUTO );
5290
5291 break;
5292
5293 case T_effects:
5294 parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ), true );
5295
5296 // Hidden schematic text is no longer supported
5297 text->SetVisible( true );
5298 break;
5299
5300 case T_iref: // legacy format; current is a T_property (aka SCH_FIELD)
5301 if( text->Type() == SCH_GLOBAL_LABEL_T )
5302 {
5303 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
5304 SCH_FIELD* field = label->GetField( FIELD_T::INTERSHEET_REFS );
5305
5306 field->SetTextPos( parseXY() );
5307 NeedRIGHT();
5308
5309 field->SetVisible( true );
5310 }
5311 break;
5312
5313 case T_uuid:
5314 NeedSYMBOL();
5315 const_cast<KIID&>( text->m_Uuid ) = parseKIID();
5316 NeedRIGHT();
5317 break;
5318
5319 case T_property:
5320 {
5321 if( text->Type() == SCH_TEXT_T )
5322 Unexpected( T_property );
5323
5324 SCH_FIELD* field = parseSchField( text.get() );
5325
5326 // Intersheetrefs is a mandatory field and so will already exist
5327 if( text->Type() == SCH_GLOBAL_LABEL_T && field->IsMandatory() )
5328 {
5329 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
5330 *label->GetField( field->GetId() ) = *field;
5331 }
5332 else
5333 {
5334 static_cast<SCH_LABEL_BASE*>( text.get() )->GetFields().emplace_back( *field );
5335 }
5336
5337 delete field;
5338 break;
5339 }
5340
5341 case T_locked:
5342 text->SetLocked( parseBool() );
5343 NeedRIGHT();
5344 break;
5345
5346 default:
5347 Expecting( "at, shape, iref, uuid, effects or locked" );
5348 }
5349 }
5350
5351 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() );
5352
5353 if( label && label->GetFields().empty() )
5355
5356 return text.release();
5357}
5358
5359
5361{
5362 wxCHECK_MSG( CurTok() == T_text_box, nullptr,
5363 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text box." ) );
5364
5365 std::unique_ptr<SCH_TEXTBOX> textBox = std::make_unique<SCH_TEXTBOX>();
5366
5367 parseSchTextBoxContent( textBox.get() );
5368
5369 return textBox.release();
5370}
5371
5372
5374{
5375 wxCHECK_MSG( CurTok() == T_table_cell, nullptr,
5376 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table cell." ) );
5377
5378 std::unique_ptr<SCH_TABLECELL> cell = std::make_unique<SCH_TABLECELL>();
5379
5380 parseSchTextBoxContent( cell.get() );
5381
5382 return cell.release();
5383}
5384
5385
5387{
5388 T token;
5389 VECTOR2I pos;
5390 VECTOR2I end;
5391 VECTOR2I size;
5392 int left;
5393 int top;
5394 int right;
5395 int bottom;
5397 FILL_PARAMS fill;
5398 bool foundEnd = false;
5399 bool foundSize = false;
5400 bool foundMargins = false;
5401
5402 NeedSYMBOL();
5403
5404 aTextBox->SetText( FromUTF8() );
5405
5406 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5407 {
5408 if( token != T_LEFT )
5409 Expecting( T_LEFT );
5410
5411 token = NextTok();
5412
5413 switch( token )
5414 {
5415 case T_exclude_from_sim:
5416 aTextBox->SetExcludedFromSim( parseBool() );
5417 NeedRIGHT();
5418 break;
5419
5420 case T_start: // Legacy token during 6.99 development; fails to handle angle
5421 pos = parseXY();
5422 NeedRIGHT();
5423 break;
5424
5425 case T_end: // Legacy token during 6.99 development; fails to handle angle
5426 end = parseXY();
5427 foundEnd = true;
5428 NeedRIGHT();
5429 break;
5430
5431 case T_at:
5432 pos = parseXY();
5433 aTextBox->SetTextAngle( EDA_ANGLE( parseDouble( "textbox angle" ), DEGREES_T ) );
5434 NeedRIGHT();
5435 break;
5436
5437 case T_size:
5438 size = parseXY();
5439 foundSize = true;
5440 NeedRIGHT();
5441 break;
5442
5443 case T_span:
5444 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( aTextBox ) )
5445 {
5446 cell->SetColSpan( parseInt( "column span" ) );
5447 cell->SetRowSpan( parseInt( "row span" ) );
5448 }
5449 else
5450 {
5451 Expecting( "at, size, stroke, fill, effects or uuid" );
5452 }
5453
5454 NeedRIGHT();
5455 break;
5456
5457 case T_stroke:
5458 parseStroke( stroke );
5459 aTextBox->SetStroke( stroke );
5460 break;
5461
5462 case T_fill:
5463 parseFill( fill );
5464 aTextBox->SetFillMode( fill.m_FillType );
5465 aTextBox->SetFillColor( fill.m_Color );
5466 fixupSchFillMode( aTextBox );
5467 break;
5468
5469 case T_margins:
5470 parseMargins( left, top, right, bottom );
5471 aTextBox->SetMarginLeft( left );
5472 aTextBox->SetMarginTop( top );
5473 aTextBox->SetMarginRight( right );
5474 aTextBox->SetMarginBottom( bottom );
5475 foundMargins = true;
5476 NeedRIGHT();
5477 break;
5478
5479 case T_effects:
5480 parseEDA_TEXT( static_cast<EDA_TEXT*>( aTextBox ), false );
5481 break;
5482
5483 case T_uuid:
5484 NeedSYMBOL();
5485 const_cast<KIID&>( aTextBox->m_Uuid ) = parseKIID();
5486 NeedRIGHT();
5487 break;
5488
5489 case T_locked:
5490 aTextBox->SetLocked( parseBool() );
5491 NeedRIGHT();
5492 break;
5493
5494 default:
5495 if( dynamic_cast<SCH_TABLECELL*>( aTextBox ) != nullptr )
5496 Expecting( "at, size, stroke, fill, effects, span, uuid or locked" );
5497 else
5498 Expecting( "at, size, stroke, fill, effects, uuid or locked" );
5499 }
5500 }
5501
5502 aTextBox->SetPosition( pos );
5503
5504 if( foundEnd )
5505 aTextBox->SetEnd( end );
5506 else if( foundSize )
5507 aTextBox->SetEnd( pos + size );
5508 else
5509 Expecting( "size" );
5510
5511 if( !foundMargins )
5512 {
5513 int margin = aTextBox->GetLegacyTextMargin();
5514 aTextBox->SetMarginLeft( margin );
5515 aTextBox->SetMarginTop( margin );
5516 aTextBox->SetMarginRight( margin );
5517 aTextBox->SetMarginBottom( margin );
5518 }
5519}
5520
5521
5523{
5524 wxCHECK_MSG( CurTok() == T_table, nullptr,
5525 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table." ) );
5526
5527 T token;
5528 int defaultLineWidth = schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
5529 STROKE_PARAMS borderStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
5530 STROKE_PARAMS separatorsStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
5531 std::unique_ptr<SCH_TABLE> table = std::make_unique<SCH_TABLE>( defaultLineWidth );
5532
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 switch( token )
5541 {
5542 case T_column_count:
5543 table->SetColCount( parseInt( "column count" ) );
5544 NeedRIGHT();
5545 break;
5546
5547 case T_column_widths:
5548 {
5549 int col = 0;
5550
5551 while( ( token = NextTok() ) != T_RIGHT )
5552 table->SetColWidth( col++, parseInternalUnits() );
5553
5554 break;
5555 }
5556
5557 case T_row_heights:
5558 {
5559 int row = 0;
5560
5561 while( ( token = NextTok() ) != T_RIGHT )
5562 table->SetRowHeight( row++, parseInternalUnits() );
5563
5564 break;
5565 }
5566
5567 case T_cells:
5568 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5569 {
5570 if( token != T_LEFT )
5571 Expecting( T_LEFT );
5572
5573 token = NextTok();
5574
5575 if( token != T_table_cell )
5576 Expecting( "table_cell" );
5577
5578 table->AddCell( parseSchTableCell() );
5579 }
5580
5581 break;
5582
5583 case T_border:
5584 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5585 {
5586 if( token != T_LEFT )
5587 Expecting( T_LEFT );
5588
5589 token = NextTok();
5590
5591 switch( token )
5592 {
5593 case T_external:
5594 table->SetStrokeExternal( parseBool() );
5595 NeedRIGHT();
5596 break;
5597
5598 case T_header:
5599 table->SetStrokeHeaderSeparator( parseBool() );
5600 NeedRIGHT();
5601 break;
5602
5603 case T_stroke:
5604 parseStroke( borderStroke );
5605 table->SetBorderStroke( borderStroke );
5606 break;
5607
5608 default:
5609 Expecting( "external, header or stroke" );
5610 break;
5611 }
5612 }
5613
5614 break;
5615
5616 case T_separators:
5617 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5618 {
5619 if( token != T_LEFT )
5620 Expecting( T_LEFT );
5621
5622 token = NextTok();
5623
5624 switch( token )
5625 {
5626 case T_rows:
5627 table->SetStrokeRows( parseBool() );
5628 NeedRIGHT();
5629 break;
5630
5631 case T_cols:
5632 table->SetStrokeColumns( parseBool() );
5633 NeedRIGHT();
5634 break;
5635
5636 case T_stroke:
5637 parseStroke( separatorsStroke );
5638 table->SetSeparatorsStroke( separatorsStroke );
5639 break;
5640
5641 default:
5642 Expecting( "rows, cols, or stroke" );
5643 break;
5644 }
5645 }
5646
5647 break;
5648
5649 case T_uuid:
5650 NeedSYMBOL();
5651 const_cast<KIID&>( table->m_Uuid ) = parseKIID();
5652 NeedRIGHT();
5653 break;
5654
5655 case T_locked:
5656 table->SetLocked( parseBool() );
5657 NeedRIGHT();
5658 break;
5659
5660 default:
5661 Expecting( "columns, col_widths, row_heights, border, separators, uuid, locked, header or cells" );
5662 }
5663 }
5664
5665 if( !table->GetCell( 0, 0 ) )
5666 {
5667 THROW_PARSE_ERROR( _( "Invalid table: no cells defined" ), CurSource(), CurLine(), CurLineNumber(),
5668 CurOffset() );
5669 }
5670
5671 return table.release();
5672}
5673
5674
5676{
5677 wxCHECK_RET( CurTok() == T_bus_alias,
5678 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus alias." ) );
5679 wxCHECK( aScreen, /* void */ );
5680
5681 T token;
5682 std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>();
5683 wxString alias;
5684 wxString member;
5685
5686 NeedSYMBOL();
5687
5688 alias = FromUTF8();
5689
5690 if( m_requiredVersion < 20210621 )
5691 alias = ConvertToNewOverbarNotation( alias );
5692
5693 busAlias->SetName( alias );
5694
5695 NeedLEFT();
5696 token = NextTok();
5697
5698 if( token != T_members )
5699 Expecting( "members" );
5700
5701 token = NextTok();
5702
5703 while( token != T_RIGHT )
5704 {
5705 if( !IsSymbol( token ) )
5706 Expecting( "quoted string" );
5707
5708 member = FromUTF8();
5709
5710 if( m_requiredVersion < 20210621 )
5711 member = ConvertToNewOverbarNotation( member );
5712
5713 busAlias->AddMember( member );
5714
5715 token = NextTok();
5716 }
5717
5718 NeedRIGHT();
5719
5720 aScreen->AddBusAlias( busAlias );
5721}
5722
5723
5725{
5726 // (net_chain "name" [(from "ref" "pin")] [(to "ref" "pin")]
5727 // [(net_class "...")] [(color R G B A)] [(nets "n1" "n2" ...)])
5728 NeedSYMBOL();
5729 wxString name = FromUTF8();
5730
5731 wxString netClass;
5733 wxString fromRef, fromPin, toRef, toPin;
5734 std::set<wxString> memberNets;
5735
5736 for( T tok = NextTok(); tok != T_RIGHT; tok = NextTok() )
5737 {
5738 if( tok == T_LEFT )
5739 tok = NextTok();
5740
5741 if( tok == T_from )
5742 {
5743 NeedSYMBOLorNUMBER();
5744 fromRef = From_UTF8( CurText() );
5745 NeedSYMBOLorNUMBER();
5746 fromPin = From_UTF8( CurText() );
5747 NeedRIGHT();
5748 }
5749 else if( tok == T_to )
5750 {
5751 NeedSYMBOLorNUMBER();
5752 toRef = From_UTF8( CurText() );
5753 NeedSYMBOLorNUMBER();
5754 toPin = From_UTF8( CurText() );
5755 NeedRIGHT();
5756 }
5757 else if( tok == T_net_class )
5758 {
5759 NeedSYMBOLorNUMBER();
5760 netClass = FromUTF8();
5761 NeedRIGHT();
5762 }
5763 else if( tok == T_color )
5764 {
5765 int r = parseInt( "red" );
5766 int g = parseInt( "green" );
5767 int b = parseInt( "blue" );
5768 double al = parseDouble( "alpha" );
5769 color = COLOR4D( r / 255.0, g / 255.0, b / 255.0, al );
5770 NeedRIGHT();
5771 }
5772 else if( tok == T_nets )
5773 {
5774 for( T inner = NextTok(); inner != T_RIGHT; inner = NextTok() )
5775 {
5776 if( !IsSymbol( inner ) && inner != T_NUMBER )
5777 Expecting( "net name" );
5778
5779 memberNets.insert( FromUTF8() );
5780 }
5781 }
5782 else
5783 {
5784 // Skip unknown subsections
5785 int depth = 1;
5786
5787 while( depth > 0 )
5788 {
5789 T inner = NextTok();
5790
5791 if( inner == T_EOF )
5792 break;
5793
5794 if( inner == T_LEFT )
5795 ++depth;
5796 else if( inner == T_RIGHT )
5797 --depth;
5798 }
5799 }
5800 }
5801
5802 if( !fromRef.IsEmpty() && !toRef.IsEmpty() )
5803 {
5804 m_netChainTerminalRefs[name] = { { fromRef, fromPin }, { toRef, toPin } };
5805 }
5806
5807 if( !netClass.IsEmpty() )
5808 m_netChainNetClasses[name] = netClass;
5809
5810 if( color != KIGFX::COLOR4D::UNSPECIFIED )
5811 m_netChainColors[name] = color;
5812
5813 if( !memberNets.empty() )
5814 m_netChainMemberNets[name] = std::move( memberNets );
5815}
5816
5817
5819{
5820 T token;
5821
5822 while( ( token = NextTok() ) != T_RIGHT )
5823 {
5824 // This token is the Uuid of the item in the group.
5825 // Since groups are serialized at the end of the file/footprint, the Uuid should already
5826 // have been seen and exist in the board.
5827 KIID uuid( CurStr() );
5828 aGroupInfo.memberUuids.push_back( uuid );
5829 }
5830}
5831
5832
5834{
5835 wxCHECK_RET( CurTok() == T_group,
5836 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_GROUP." ) );
5837
5838 T token;
5839
5840 m_groupInfos.push_back( GROUP_INFO() );
5841 GROUP_INFO& groupInfo = m_groupInfos.back();
5842
5843 while( ( token = NextTok() ) != T_LEFT )
5844 {
5845 if( token == T_STRING )
5846 groupInfo.name = FromUTF8();
5847 else
5848 Expecting( "group name or locked" );
5849 }
5850
5851 for( ; token != T_RIGHT; token = NextTok() )
5852 {
5853 if( token != T_LEFT )
5854 Expecting( T_LEFT );
5855
5856 token = NextTok();
5857
5858 switch( token )
5859 {
5860 case T_uuid:
5861 NextTok();
5862 groupInfo.uuid = parseKIID();
5863 NeedRIGHT();
5864 break;
5865
5866 case T_lib_id:
5867 {
5868 token = NextTok();
5869
5870 if( !IsSymbol( token ) && token != T_NUMBER )
5871 Expecting( "symbol|number" );
5872
5873 LIB_ID libId;
5874 wxString name = FromUTF8();
5875 // Some symbol LIB_IDs have the '/' character escaped which can break
5876 // symbol links. The '/' character is no longer an illegal LIB_ID character so
5877 // it doesn't need to be escaped.
5878 name.Replace( "{slash}", "/" );
5879
5880 int bad_pos = groupInfo.libId.Parse( name );
5881
5882 if( bad_pos >= 0 )
5883 {
5884 if( static_cast<int>( name.size() ) > bad_pos )
5885 {
5886 wxString msg = wxString::Format( _( "Group library link %s contains invalid character '%c'" ), name,
5887 name[bad_pos] );
5888
5889 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
5890 }
5891
5892 THROW_PARSE_ERROR( _( "Invalid library ID" ), CurSource(), CurLine(), CurLineNumber(), CurOffset() );
5893 }
5894
5895 NeedRIGHT();
5896 break;
5897 }
5898
5899 case T_members:
5900 {
5901 parseGroupMembers( groupInfo );
5902 break;
5903 }
5904
5905 case T_locked:
5906 groupInfo.locked = parseBool();
5907 NeedRIGHT();
5908 break;
5909
5910 default:
5911 Expecting( "uuid, lib_id, members, locked" );
5912 }
5913 }
5914}
5915
5916
5918{
5919 if( !aParent )
5920 return;
5921
5922 auto getItem =
5923 [&]( const KIID& aId )
5924 {
5925 SCH_ITEM* aItem = nullptr;
5926
5927 for( SCH_ITEM* item : aParent->Items() )
5928 {
5929 if( item->m_Uuid == aId )
5930 {
5931 aItem = item;
5932 break;
5933 }
5934 }
5935
5936 return aItem;
5937 };
5938
5939 // Now that we've parsed the other Uuids in the file we can resolve the uuids referred
5940 // to in the group declarations we saw.
5941 //
5942 // First add all group objects so subsequent GetItem() calls for nested groups work.
5943 for( const GROUP_INFO& groupInfo : m_groupInfos )
5944 {
5945 SCH_GROUP* group = nullptr;
5946
5947 group = new SCH_GROUP( aParent );
5948 group->SetName( groupInfo.name );
5949
5950 const_cast<KIID&>( group->m_Uuid ) = groupInfo.uuid;
5951
5952 if( groupInfo.libId.IsValid() )
5953 group->SetDesignBlockLibId( groupInfo.libId );
5954
5955 group->SetLocked( groupInfo.locked );
5956
5957 aParent->Append( group );
5958 }
5959
5960 for( const GROUP_INFO& groupInfo : m_groupInfos )
5961 {
5962 SCH_GROUP* group = static_cast<SCH_GROUP*>( getItem( groupInfo.uuid ) );
5963
5964 if( group && group->Type() == SCH_GROUP_T )
5965 {
5966 for( const KIID& aUuid : groupInfo.memberUuids )
5967 {
5968 if( SCH_ITEM* gItem = getItem( aUuid ) )
5969 group->AddItem( gItem );
5970 }
5971 }
5972 }
5973
5974 aParent->GroupsSanityCheck( true );
5975}
5976
5977
5979{
5980 // Skip tokens until we exit the current S-expression block.
5981 // This is used for error recovery when parsing fails mid-symbol.
5982 while( aDepth > 0 )
5983 {
5984 T token = NextTok();
5985
5986 if( token == T_EOF )
5987 break;
5988 else if( token == T_LEFT )
5989 aDepth++;
5990 else if( token == T_RIGHT )
5991 aDepth--;
5992 }
5993}
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:46
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:114
wxString GetName() const override
Definition lib_symbol.h:176
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).
std::optional< LIB_ID > m_SymbolOverride
Alternate library symbol to substitute for the base symbol in this variant, if any.
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:67
@ LAYER_DEVICE
Definition layer_ids.h:472
@ LAYER_WIRE
Definition layer_ids.h:458
@ LAYER_NOTES
Definition layer_ids.h:473
@ LAYER_BUS
Definition layer_ids.h:459
#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
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:544
@ 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