KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcad_sch_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) 2007, 2008 Lubo Racko <[email protected]>
5 * Copyright (C) 2012-2013 Alexander Lunev <[email protected]>
6 * Copyright (C) 2017 Eldar Khayrullin <[email protected]>
7 * Copyright (C) 2025 KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
24
26#include <ki_exception.h>
27#include <xnode.h>
28
29#include <wx/xml/xml.h>
30#include <wx/string.h>
31#include <wx/tokenzr.h>
32#include <wx/xlocale.h>
33
34#include <cmath>
35
36
37namespace PCAD_SCH
38{
39
40XNODE* PCAD_SCH_PARSER::FindChild( XNODE* aNode, const wxString& aTag )
41{
42 if( !aNode )
43 return nullptr;
44
45 for( XNODE* child = aNode->GetChildren(); child; child = child->GetNext() )
46 {
47 // case-insensitive like the board importer's FindNode
48 if( child->GetName().IsSameAs( aTag, false ) )
49 return child;
50 }
51
52 return nullptr;
53}
54
55
56// A node's value can land in two places depending on how the writer quoted it:
57// quoted strings are concatenated into the "Name" attribute, unquoted tokens
58// into the node text content.
60{
61 if( !aNode )
62 return wxEmptyString;
63
64 wxString name = aNode->GetAttribute( wxT( "Name" ) );
65
66 if( !name.IsEmpty() )
67 return name;
68
69 return aNode->GetNodeContent().Trim( true ).Trim( false );
70}
71
72
73wxString PCAD_SCH_PARSER::childStr( XNODE* aNode, const wxString& aTag,
74 const wxString& aDefault )
75{
76 XNODE* child = FindChild( aNode, aTag );
77
78 if( !child )
79 return aDefault;
80
81 return NodeText( child );
82}
83
84
85bool PCAD_SCH_PARSER::childFlag( XNODE* aNode, const wxString& aTag )
86{
87 return childStr( aNode, aTag ).CmpNoCase( wxT( "True" ) ) == 0;
88}
89
90
91// ---------------------------------------------------------------------------
92// Measurement handling. Values are stored as mils. A value token may carry
93// its unit attached ("31.115mm") or as the following token ("0.19843 mm");
94// bare numbers use the file default from (fileUnits ...).
95// ---------------------------------------------------------------------------
96
97double PCAD_SCH_PARSER::toMils( const wxString& aValue ) const
98{
99 wxString str = aValue;
100 str.Trim( true ).Trim( false );
101
102 if( str.IsEmpty() )
103 return 0.0;
104
105 bool isMm = m_isMetric;
106 wxString lower = str.Lower();
107
108 if( lower.EndsWith( wxT( "mm" ) ) )
109 {
110 isMm = true;
111 str.RemoveLast( 2 );
112 }
113 else if( lower.EndsWith( wxT( "mil" ) ) )
114 {
115 isMm = false;
116 str.RemoveLast( 3 );
117 }
118
119 str.Trim( true );
120
121 double val = 0.0;
122 str.ToCDouble( &val );
123
124 if( isMm )
125 val /= 0.0254;
126
127 return val;
128}
129
130
131// Split a node value into measurement tokens, re-attaching separated unit
132// suffixes ("0.19843 mm" is one value).
133static std::vector<wxString> splitMeasureTokens( const wxString& aContent )
134{
135 std::vector<wxString> result;
136 wxStringTokenizer tokenizer( aContent, wxT( " \t\r\n" ), wxTOKEN_STRTOK );
137
138 while( tokenizer.HasMoreTokens() )
139 {
140 wxString tok = tokenizer.GetNextToken();
141
142 if( !result.empty()
143 && ( tok.CmpNoCase( wxT( "mm" ) ) == 0 || tok.CmpNoCase( wxT( "mil" ) ) == 0 ) )
144 result.back() += tok;
145 else
146 result.push_back( tok );
147 }
148
149 return result;
150}
151
152
153bool PCAD_SCH_PARSER::parsePtNode( XNODE* aPtNode, double& aX, double& aY ) const
154{
155 if( !aPtNode )
156 return false;
157
158 std::vector<wxString> tokens = splitMeasureTokens( NodeText( aPtNode ) );
159
160 if( tokens.size() < 2 )
161 return false;
162
163 aX = toMils( tokens[0] );
164 aY = toMils( tokens[1] );
165 return true;
166}
167
168
169bool PCAD_SCH_PARSER::parsePt( XNODE* aNode, double& aX, double& aY ) const
170{
171 return parsePtNode( FindChild( aNode, wxT( "pt" ) ), aX, aY );
172}
173
174
175double PCAD_SCH_PARSER::childDouble( XNODE* aNode, const wxString& aTag, double aDefault ) const
176{
177 XNODE* child = FindChild( aNode, aTag );
178
179 if( !child )
180 return aDefault;
181
182 std::vector<wxString> tokens = splitMeasureTokens( NodeText( child ) );
183
184 if( tokens.empty() )
185 return aDefault;
186
187 return toMils( tokens[0] );
188}
189
190
191// Rotations and angles are plain decimal degrees, never measurements.
192static double childAngle( XNODE* aNode, const wxString& aTag, double aDefault = 0.0 )
193{
194 XNODE* child = PCAD_SCH_PARSER::FindChild( aNode, aTag );
195
196 if( !child )
197 return aDefault;
198
199 double val = aDefault;
200 PCAD_SCH_PARSER::NodeText( child ).ToCDouble( &val );
201 return val;
202}
203
204
205std::vector<std::pair<double, double>> PCAD_SCH_PARSER::collectPts( XNODE* aNode ) const
206{
207 std::vector<std::pair<double, double>> pts;
208
209 for( XNODE* child = aNode->GetChildren(); child; child = child->GetNext() )
210 {
211 if( child->GetName() == wxT( "pt" ) )
212 {
213 double x = 0, y = 0;
214
215 if( parsePtNode( child, x, y ) )
216 pts.emplace_back( x, y );
217 }
218 }
219
220 return pts;
221}
222
223
224JUSTIFY PCAD_SCH_PARSER::parseJustify( const wxString& aValue )
225{
226 static const std::map<wxString, JUSTIFY> justifyMap = {
227 { wxT( "LowerLeft" ), JUSTIFY::LOWER_LEFT },
228 { wxT( "LowerCenter" ), JUSTIFY::LOWER_CENTER },
229 { wxT( "LowerRight" ), JUSTIFY::LOWER_RIGHT },
230 { wxT( "UpperLeft" ), JUSTIFY::UPPER_LEFT },
231 { wxT( "UpperCenter" ), JUSTIFY::UPPER_CENTER },
232 { wxT( "UpperRight" ), JUSTIFY::UPPER_RIGHT },
233 { wxT( "Center" ), JUSTIFY::CENTER },
234 { wxT( "Right" ), JUSTIFY::RIGHT },
235 { wxT( "Left" ), JUSTIFY::LEFT },
236 };
237
238 auto it = justifyMap.find( aValue );
239
240 return it == justifyMap.end() ? JUSTIFY::LOWER_LEFT : it->second;
241}
242
243
244// ---------------------------------------------------------------------------
245// Top level
246// ---------------------------------------------------------------------------
247
248void PCAD_SCH_PARSER::LoadFromFile( const wxString& aFilename, SCHEMATIC& aSchematic )
249{
250 wxXmlDocument doc;
251 PCAD2KICAD::LoadInputFile( aFilename, &doc );
252
253 XNODE* root = static_cast<XNODE*>( doc.GetRoot() );
254
255 if( !root )
256 THROW_IO_ERROR( _( "Empty P-CAD document" ) );
257
258 // fileUnits must be known before any measurement is parsed.
259 if( XNODE* header = FindChild( root, wxT( "asciiHeader" ) ) )
260 parseHeader( header, aSchematic );
261
262 parseFieldSets( root, aSchematic );
263
264 for( XNODE* node = root->GetChildren(); node; node = node->GetNext() )
265 {
266 const wxString& tag = node->GetName();
267
268 if( tag == wxT( "library" ) )
269 parseLibrary( node, aSchematic );
270 else if( tag == wxT( "netlist" ) )
271 parseNetlist( node, aSchematic );
272 else if( tag == wxT( "schematicDesign" ) )
273 parseSchematicDesign( node, aSchematic );
274 }
275
276 for( const TEXT_STYLE& ts : aSchematic.textStyles )
277 aSchematic.textStylesByName[ts.name] = &ts;
278
279 // primary names win over originalName aliases on collision
280 for( const SYMBOL_DEF& sd : aSchematic.symbolDefs )
281 {
282 aSchematic.symbolDefsByName[sd.name] = &sd;
283
284 if( !sd.originalName.IsEmpty() )
285 aSchematic.symbolDefsByName.emplace( sd.originalName, &sd );
286 }
287
288 for( const COMP_DEF& cd : aSchematic.compDefs )
289 {
290 aSchematic.compDefsByName[cd.name] = &cd;
291
292 if( !cd.originalName.IsEmpty() )
293 aSchematic.compDefsByName.emplace( cd.originalName, &cd );
294 }
295
296 for( const COMP_INST& ci : aSchematic.compInsts )
297 aSchematic.compInstsByRef[ci.refDes] = &ci;
298}
299
300
302{
303 m_isMetric = ( childStr( aNode, wxT( "fileUnits" ) ).CmpNoCase( wxT( "mm" ) ) == 0 );
304}
305
306
307// ---------------------------------------------------------------------------
308// (library "name" ...)
309// ---------------------------------------------------------------------------
310
312{
313 for( XNODE* child = aNode->GetChildren(); child; child = child->GetNext() )
314 {
315 const wxString& tag = child->GetName();
316
317 if( tag == wxT( "textStyleDef" ) )
318 {
319 parseTextStyleDef( child, aSchematic );
320 }
321 else if( tag == wxT( "symbolDef" ) )
322 {
323 SYMBOL_DEF sd;
324 parseSymbolDef( child, sd );
325 aSchematic.symbolDefs.push_back( std::move( sd ) );
326 }
327 else if( tag == wxT( "compDef" ) )
328 {
329 COMP_DEF cd;
330 parseCompDef( child, cd );
331 aSchematic.compDefs.push_back( std::move( cd ) );
332 }
333 else if( tag == wxT( "compAlias" ) )
334 {
335 // (compAlias "ALIAS ORIGINAL") - first word aliases the rest
336 wxString both = child->GetAttribute( wxT( "Name" ) );
337 wxString alias = both.BeforeFirst( ' ' );
338 wxString original = both.AfterFirst( ' ' );
339 original.Trim( true ).Trim( false );
340
341 if( !alias.IsEmpty() && !original.IsEmpty() )
342 aSchematic.compAliases[alias] = original;
343 }
344 }
345}
346
347
349{
350 TEXT_STYLE style;
351 style.name = aNode->GetAttribute( wxT( "Name" ) );
352 style.displayTType = childFlag( aNode, wxT( "textStyleDisplayTType" ) );
353
354 for( XNODE* child = aNode->GetChildren(); child; child = child->GetNext() )
355 {
356 if( child->GetName() != wxT( "font" ) )
357 continue;
358
359 FONT font;
360 font.isTrueType = ( childStr( child, wxT( "fontType" ) ) == wxT( "TrueType" ) );
361 font.height = childDouble( child, wxT( "fontHeight" ), 100.0 );
362 font.strokeWidth = childDouble( child, wxT( "strokeWidth" ), 10.0 );
363 font.isItalic = childFlag( child, wxT( "fontItalic" ) );
364
365 wxString weight = childStr( child, wxT( "fontWeight" ) );
366 long weightVal = 0;
367
368 if( weight.ToLong( &weightVal ) )
369 font.isBold = ( weightVal >= 700 );
370
371 if( font.isTrueType )
372 {
373 style.ttfFont = font;
374 style.hasTtfFont = true;
375 }
376 else
377 {
378 style.strokeFont = font;
379 }
380 }
381
382 aSchematic.textStyles.push_back( std::move( style ) );
383}
384
385
386// ---------------------------------------------------------------------------
387// (symbolDef "name" ...)
388// ---------------------------------------------------------------------------
389
391{
392 aSymDef.name = aNode->GetAttribute( wxT( "Name" ) );
393 aSymDef.originalName = childStr( aNode, wxT( "originalName" ) );
394
395 for( XNODE* child = aNode->GetChildren(); child; child = child->GetNext() )
396 {
397 const wxString& tag = child->GetName();
398
399 if( tag == wxT( "pin" ) )
400 aSymDef.pins.push_back( parsePin( child ) );
401 else if( tag == wxT( "line" ) )
402 aSymDef.lines.push_back( parseLine( child ) );
403 else if( tag == wxT( "arc" ) )
404 aSymDef.arcs.push_back( parseArc( child ) );
405 else if( tag == wxT( "triplePointArc" ) )
406 aSymDef.arcs.push_back( parseTriplePointArc( child ) );
407 else if( tag == wxT( "poly" ) )
408 aSymDef.polys.push_back( parsePoly( child ) );
409 else if( tag == wxT( "text" ) )
410 aSymDef.texts.push_back( parseText( child ) );
411 else if( tag == wxT( "ieeeSymbol" ) )
412 aSymDef.ieeeSymbols.push_back( parseIeeeSymbol( child ) );
413 else if( tag == wxT( "attr" ) )
414 aSymDef.attrs.push_back( parseAttr( child ) );
415 }
416}
417
418
419// ---------------------------------------------------------------------------
420// (compDef "name" (compHeader ...) (compPin ...)... (attachedSymbol ...)...)
421// ---------------------------------------------------------------------------
422
424{
425 aCompDef.name = aNode->GetAttribute( wxT( "Name" ) );
426 aCompDef.originalName = childStr( aNode, wxT( "originalName" ) );
427
428 if( XNODE* header = FindChild( aNode, wxT( "compHeader" ) ) )
429 {
430 aCompDef.refDesPrefix = childStr( header, wxT( "refDesPrefix" ) );
431
432 long num = 1;
433
434 if( childStr( header, wxT( "numParts" ) ).ToLong( &num ) && num > 0 )
435 aCompDef.numParts = static_cast<int>( num );
436
437 aCompDef.isPower = ( childStr( header, wxT( "compType" ) ) == wxT( "Power" ) );
438 }
439
440 aCompDef.attachedSymbols.resize( aCompDef.numParts + 1 );
441
442 for( XNODE* child = aNode->GetChildren(); child; child = child->GetNext() )
443 {
444 const wxString& tag = child->GetName();
445
446 if( tag == wxT( "compPin" ) )
447 {
449 pin.padDes = child->GetAttribute( wxT( "Name" ) );
450 pin.pinName = childStr( child, wxT( "pinName" ) );
451 pin.symPinNum = childStr( child, wxT( "symPinNum" ) );
452 pin.pinType = childStr( child, wxT( "pinType" ) );
453
454 long part = 1;
455
456 if( childStr( child, wxT( "partNum" ) ).ToLong( &part ) && part > 0 )
457 pin.partNum = static_cast<int>( part );
458
459 aCompDef.compPins.push_back( std::move( pin ) );
460 }
461 else if( tag == wxT( "attachedSymbol" ) )
462 {
463 // Only the Normal alternate maps to the base KiCad body style.
464 if( childStr( child, wxT( "altType" ) ) != wxT( "Normal" ) )
465 continue;
466
467 long part = 1;
468 childStr( child, wxT( "partNum" ) ).ToLong( &part );
469
470 wxString symName = childStr( child, wxT( "symbolName" ) );
471
472 // parts beyond the declared unit count are never instantiated, and
473 // clamping keeps a corrupt partNum from forcing a huge allocation
474 if( part >= 1 && part <= aCompDef.numParts && !symName.IsEmpty() )
475 aCompDef.attachedSymbols[part] = symName;
476 }
477 else if( tag == wxT( "attachedPattern" ) )
478 {
479 aCompDef.attachedPattern = childStr( child, wxT( "patternName" ) );
480 }
481 else if( tag == wxT( "attr" ) )
482 {
483 // (attr "Description <text>")
484 wxString name = child->GetAttribute( wxT( "Name" ) );
485
486 if( name.StartsWith( wxT( "Description " ) ) )
487 aCompDef.description = name.AfterFirst( ' ' ).Trim( true ).Trim( false );
488 }
489 }
490}
491
492
493// ---------------------------------------------------------------------------
494// (pin (pinNum N) (pt X Y) (rotation R) [(isFlipped True)] [(pinLength L)]
495// [(outsideEdgeStyle Dot)] [(pinDisplay ...)] (pinDes (text ...))
496// (pinName (text ...)) [(defaultPinDes "D")])
497// ---------------------------------------------------------------------------
498
500{
501 PIN pin;
502
503 pin.pinNum = childStr( aNode, wxT( "pinNum" ) );
504 pin.defaultPinDes = childStr( aNode, wxT( "defaultPinDes" ) );
505 parsePt( aNode, pin.x, pin.y );
506 pin.rotation = childAngle( aNode, wxT( "rotation" ) );
507 pin.pinLength = childDouble( aNode, wxT( "pinLength" ), 300.0 );
508 pin.outsideEdgeStyle = childStr( aNode, wxT( "outsideEdgeStyle" ) );
509 pin.insideEdgeStyle = childStr( aNode, wxT( "insideEdgeStyle" ) );
510
511 if( XNODE* disp = FindChild( aNode, wxT( "pinDisplay" ) ) )
512 {
513 wxString des = childStr( disp, wxT( "dispPinDes" ) );
514
515 if( !des.IsEmpty() )
516 pin.showPinDes = ( des.CmpNoCase( wxT( "True" ) ) == 0 );
517
518 wxString name = childStr( disp, wxT( "dispPinName" ) );
519
520 if( !name.IsEmpty() )
521 pin.showPinName = ( name.CmpNoCase( wxT( "True" ) ) == 0 );
522 }
523
524 if( XNODE* pinDes = FindChild( aNode, wxT( "pinDes" ) ) )
525 {
526 if( XNODE* textNode = FindChild( pinDes, wxT( "text" ) ) )
527 pin.pinDesText = parseText( textNode );
528 }
529
530 if( XNODE* pinName = FindChild( aNode, wxT( "pinName" ) ) )
531 {
532 if( XNODE* textNode = FindChild( pinName, wxT( "text" ) ) )
533 pin.pinNameText = parseText( textNode );
534 }
535
536 return pin;
537}
538
539
540// ---------------------------------------------------------------------------
541// (line (pt X1 Y1) (pt X2 Y2)... [(width W)] [(style DashedLine)])
542// ---------------------------------------------------------------------------
543
545{
546 LINE line;
547
548 line.pts = collectPts( aNode );
549 line.width = childDouble( aNode, wxT( "width" ), 10.0 );
550
551 wxString style = childStr( aNode, wxT( "style" ) );
552
553 if( style == wxT( "DashedLine" ) )
555 else if( style == wxT( "DottedLine" ) )
557
558 return line;
559}
560
561
562// ---------------------------------------------------------------------------
563// (arc (pt CX CY) (radius R) (startAngle A) (sweepAngle S) [(width W)])
564// ---------------------------------------------------------------------------
565
567{
568 ARC arc;
569
570 parsePt( aNode, arc.x, arc.y );
571 arc.radius = childDouble( aNode, wxT( "radius" ) );
572 arc.startAngle = childAngle( aNode, wxT( "startAngle" ) );
573 arc.sweepAngle = childAngle( aNode, wxT( "sweepAngle" ) );
574 arc.width = childDouble( aNode, wxT( "width" ), 10.0 );
575
576 return arc;
577}
578
579
580// ---------------------------------------------------------------------------
581// (triplePointArc (pt CX CY) (pt X1 Y1) (pt X2 Y2) [(width W)])
582// Three points: center, start and end. Equal start/end means a full circle.
583// ---------------------------------------------------------------------------
584
586{
587 ARC arc;
588
589 std::vector<std::pair<double, double>> pts = collectPts( aNode );
590
591 arc.width = childDouble( aNode, wxT( "width" ), 10.0 );
592
593 if( pts.size() < 3 )
594 return arc;
595
596 arc.x = pts[0].first;
597 arc.y = pts[0].second;
598
599 double dx1 = pts[1].first - arc.x;
600 double dy1 = pts[1].second - arc.y;
601 double dx2 = pts[2].first - arc.x;
602 double dy2 = pts[2].second - arc.y;
603
604 arc.radius = std::sqrt( dx1 * dx1 + dy1 * dy1 );
605 arc.startAngle = atan2( dy1, dx1 ) * 180.0 / M_PI;
606
607 if( pts[1] == pts[2] )
608 {
609 arc.sweepAngle = 360.0;
610 }
611 else
612 {
613 double endAngle = atan2( dy2, dx2 ) * 180.0 / M_PI;
614 arc.sweepAngle = endAngle - arc.startAngle;
615
616 // P-CAD arcs sweep counterclockwise from start to end
617 if( arc.sweepAngle <= 0 )
618 arc.sweepAngle += 360.0;
619 }
620
621 return arc;
622}
623
624
626{
627 POLY poly;
628
629 poly.pts = collectPts( aNode );
630
631 return poly;
632}
633
634
635// ---------------------------------------------------------------------------
636// (text (pt X Y) "string" (textStyleRef "style") [(rotation R)]
637// [(isFlipped True)] [(justify J)] [(isVisible ...)])
638// ---------------------------------------------------------------------------
639
641{
642 TEXT_ITEM item;
643
644 item.text = aNode->GetAttribute( wxT( "Name" ) );
645 parsePt( aNode, item.x, item.y );
646 item.rotation = childAngle( aNode, wxT( "rotation" ) );
647 item.isFlipped = childFlag( aNode, wxT( "isFlipped" ) );
648 item.justify = parseJustify( childStr( aNode, wxT( "justify" ) ) );
649 item.styleRef = childStr( aNode, wxT( "textStyleRef" ) );
650
651 wxString visible = childStr( aNode, wxT( "isVisible" ) );
652
653 if( !visible.IsEmpty() )
654 item.isVisible = ( visible.CmpNoCase( wxT( "True" ) ) == 0 );
655
656 return item;
657}
658
659
661{
662 IEEE_SYMBOL sym;
663
664 wxString kind = NodeText( aNode );
665
666 if( kind == wxT( "Adder" ) ) sym.kind = IEEE_KIND::ADDER;
667 else if( kind == wxT( "Amplifier" ) ) sym.kind = IEEE_KIND::AMPLIFIER;
668 else if( kind == wxT( "Astable" ) ) sym.kind = IEEE_KIND::ASTABLE;
669 else if( kind == wxT( "Complex" ) ) sym.kind = IEEE_KIND::COMPLEX;
670 else if( kind == wxT( "Generator" ) ) sym.kind = IEEE_KIND::GENERATOR;
671 else if( kind == wxT( "Hysteresis" ) ) sym.kind = IEEE_KIND::HYSTERESIS;
672 else if( kind == wxT( "Multiplier" ) ) sym.kind = IEEE_KIND::MULTIPLIER;
673
674 parsePt( aNode, sym.x, sym.y );
675 sym.height = childDouble( aNode, wxT( "height" ) );
676 sym.rotation = childAngle( aNode, wxT( "rotation" ) );
677 sym.isFlipped = childFlag( aNode, wxT( "isFlipped" ) );
678
679 return sym;
680}
681
682
683// ---------------------------------------------------------------------------
684// (attr "Name Value" (pt X Y) (isVisible ...) (justify ...) (rotation ...)
685// (textStyleRef "..."))
686// ---------------------------------------------------------------------------
687
689{
690 ATTR attr;
691
692 wxString nameAttr = aNode->GetAttribute( wxT( "Name" ) );
693
694 // The quoted attribute name and quoted value are concatenated by the
695 // loader; the name is the first word, the value everything after it.
696 attr.name = nameAttr.BeforeFirst( ' ' );
697
698 attr.placement = parseText( aNode );
699 attr.placement.text = nameAttr.AfterFirst( ' ' ).Trim( true ).Trim( false );
700
701 return attr;
702}
703
704
705// ---------------------------------------------------------------------------
706// (netlist "name" (compInst "refDes" ...) ...)
707// ---------------------------------------------------------------------------
708
710{
711 for( XNODE* child = aNode->GetChildren(); child; child = child->GetNext() )
712 {
713 if( child->GetName() != wxT( "compInst" ) )
714 continue;
715
716 COMP_INST ci;
717 ci.refDes = child->GetAttribute( wxT( "Name" ) );
718 ci.compRef = childStr( child, wxT( "compRef" ) );
719 ci.originalName = childStr( child, wxT( "originalName" ) );
720 ci.value = childStr( child, wxT( "compValue" ) );
721
722 aSchematic.compInsts.push_back( std::move( ci ) );
723 }
724}
725
726
727// ---------------------------------------------------------------------------
728// (schematicDesign "name" (schDesignHeader ...) (titleSheet ...) (sheet ...)...)
729// ---------------------------------------------------------------------------
730
732{
733 if( XNODE* header = FindChild( aNode, wxT( "schDesignHeader" ) ) )
734 {
735 if( XNODE* wsNode = FindChild( header, wxT( "workspaceSize" ) ) )
736 {
737 double w = 0, h = 0;
738
739 if( parsePtNode( wsNode, w, h ) )
740 {
741 if( w > 0 )
742 aSchematic.workspaceWidth = w;
743
744 if( h > 0 )
745 aSchematic.workspaceHeight = h;
746 }
747 }
748 }
749
750 for( XNODE* child = aNode->GetChildren(); child; child = child->GetNext() )
751 {
752 if( child->GetName() == wxT( "sheet" ) )
753 {
754 SHEET sheet;
755 sheet.name = child->GetAttribute( wxT( "Name" ) );
756
757 long num = static_cast<long>( aSchematic.sheets.size() ) + 1;
758 childStr( child, wxT( "sheetNum" ) ).ToLong( &num );
759 sheet.sheetNum = static_cast<int>( num );
760
761 parseSheet( child, sheet );
762 aSchematic.sheets.push_back( std::move( sheet ) );
763 }
764 }
765}
766
767
768// ---------------------------------------------------------------------------
769// (fieldSet "name" (fieldDef "Name" "Value") ...) under (designInfo ...).
770// The loader concatenates the quoted name and value into one string, and
771// standard field names contain spaces, so split on the longest known name.
772// ---------------------------------------------------------------------------
773
774static const wxChar* const TITLE_FIELD_NAMES[] = {
775 wxT( "Approved By" ),
776 wxT( "Checked By" ),
777 wxT( "Company Name" ),
778 wxT( "Current Date" ),
779 wxT( "Current Time" ),
780 wxT( "Drawing Number" ),
781 wxT( "Drawn By" ),
782 wxT( "Sheet Number" ),
783 wxT( "Number Of Sheets" ),
784 wxT( "Variant Description" ),
785 wxT( "Variant Name" ),
786};
787
788
789static bool splitFieldDef( const wxString& aBoth, wxString& aName, wxString& aValue )
790{
791 for( const wxChar* known : TITLE_FIELD_NAMES )
792 {
793 if( aBoth == known )
794 {
795 aName = known;
796 aValue.clear();
797 return true;
798 }
799
800 if( aBoth.StartsWith( wxString( known ) + wxT( ' ' ) ) )
801 {
802 aName = known;
803 aValue = aBoth.Mid( aName.length() + 1 );
804 return true;
805 }
806 }
807
808 aName = aBoth.BeforeFirst( ' ' );
809 aValue = aBoth.AfterFirst( ' ' );
810 return !aName.IsEmpty();
811}
812
813
815{
816 if( aNode->GetName() == wxT( "fieldSet" ) )
817 {
818 for( XNODE* field = aNode->GetChildren(); field; field = field->GetNext() )
819 {
820 if( field->GetName() != wxT( "fieldDef" ) )
821 continue;
822
823 wxString both = field->GetAttribute( wxT( "Name" ) );
824 wxString name, value;
825
826 if( splitFieldDef( both, name, value ) )
827 {
828 value.Trim( true ).Trim( false );
829
830 if( !value.IsEmpty() )
831 aSchematic.titleSheet.fields[name] = value;
832 }
833 }
834
835 return;
836 }
837
838 // fieldSets live under designInfo; recursing only through the container
839 // nodes skips the geometry that dominates large files
840 const wxString& tag = aNode->GetName();
841
842 if( tag != wxT( "www.lura.sk" ) && tag != wxT( "schematicDesign" )
843 && tag != wxT( "schDesignHeader" ) && tag != wxT( "designInfo" ) )
844 {
845 return;
846 }
847
848 for( XNODE* child = static_cast<XNODE*>( aNode->GetChildren() ); child;
849 child = static_cast<XNODE*>( child->GetNext() ) )
850 {
851 parseFieldSets( child, aSchematic );
852 }
853}
854
855
856// ---------------------------------------------------------------------------
857// (wire (line (pt ...) (pt ...) [(endStyle ...)] (width W) (netNameRef "N"))
858// [(dispName True)] [(text ...)])
859// ---------------------------------------------------------------------------
860
862{
863 WIRE wire;
864
865 if( XNODE* lineNode = FindChild( aNode, wxT( "line" ) ) )
866 {
867 wire.pts = collectPts( lineNode );
868
869 if( XNODE* netRef = FindChild( lineNode, wxT( "netNameRef" ) ) )
870 wire.netName = NodeText( netRef );
871 }
872
873 wire.dispName = childFlag( aNode, wxT( "dispName" ) );
874
875 if( XNODE* textNode = FindChild( aNode, wxT( "text" ) ) )
876 {
877 wire.label = parseText( textNode );
878
879 if( wire.label.text.IsEmpty() )
880 wire.label.text = wire.netName;
881 }
882
883 return wire;
884}
885
886
887// ---------------------------------------------------------------------------
888// (bus "name" (pt ...) (pt ...) [(dispName True)] [(text ...)])
889// ---------------------------------------------------------------------------
890
892{
893 BUS bus;
894
895 bus.name = aNode->GetAttribute( wxT( "Name" ) );
896 bus.pts = collectPts( aNode );
897 bus.dispName = childFlag( aNode, wxT( "dispName" ) );
898
899 if( XNODE* textNode = FindChild( aNode, wxT( "text" ) ) )
900 {
901 bus.label = parseText( textNode );
902
903 if( bus.label.text.IsEmpty() )
904 bus.label.text = bus.name;
905 }
906
907 return bus;
908}
909
910
911// ---------------------------------------------------------------------------
912// (sheet "name" (sheetNum N) ...)
913// ---------------------------------------------------------------------------
914
916{
917 for( XNODE* child = aNode->GetChildren(); child; child = child->GetNext() )
918 {
919 const wxString& tag = child->GetName();
920
921 if( tag == wxT( "wire" ) )
922 {
923 WIRE wire = parseWire( child );
924
925 if( wire.pts.size() >= 2 )
926 aSheet.wires.push_back( std::move( wire ) );
927 }
928 else if( tag == wxT( "bus" ) )
929 {
930 BUS bus = parseBus( child );
931
932 if( bus.pts.size() >= 2 )
933 aSheet.buses.push_back( std::move( bus ) );
934 }
935 else if( tag == wxT( "busEntry" ) )
936 {
937 BUS_ENTRY entry;
938 entry.busNameRef = childStr( child, wxT( "busNameRef" ) );
939 parsePt( child, entry.x, entry.y );
940 entry.orient = childStr( child, wxT( "orient" ) );
941
942 aSheet.busEntries.push_back( std::move( entry ) );
943 }
944 else if( tag == wxT( "port" ) )
945 {
946 PORT port;
947 parsePt( child, port.x, port.y );
948
949 if( XNODE* netRef = FindChild( child, wxT( "netNameRef" ) ) )
950 port.netNameRef = NodeText( netRef );
951
952 port.portType = childStr( child, wxT( "portType" ) );
953 port.rotation = childAngle( child, wxT( "rotation" ) );
954 port.isFlipped = childFlag( child, wxT( "isFlipped" ) );
955
956 aSheet.ports.push_back( std::move( port ) );
957 }
958 else if( tag == wxT( "junction" ) )
959 {
960 JUNCTION junc;
961 parsePt( child, junc.x, junc.y );
962
963 if( XNODE* netRef = FindChild( child, wxT( "netNameRef" ) ) )
964 junc.netName = NodeText( netRef );
965
966 aSheet.junctions.push_back( std::move( junc ) );
967 }
968 else if( tag == wxT( "symbol" ) )
969 {
970 SYMBOL_INST inst;
971 inst.symbolRef = childStr( child, wxT( "symbolRef" ) );
972 inst.refDesRef = childStr( child, wxT( "refDesRef" ) );
973
974 long part = 1;
975
976 if( childStr( child, wxT( "partNum" ) ).ToLong( &part ) && part > 0 )
977 inst.partNum = static_cast<int>( part );
978
979 parsePt( child, inst.x, inst.y );
980 inst.rotation = childAngle( child, wxT( "rotation" ) );
981 inst.isFlipped = childFlag( child, wxT( "isFlipped" ) );
982
983 for( XNODE* sub = child->GetChildren(); sub; sub = sub->GetNext() )
984 {
985 if( sub->GetName() == wxT( "attr" ) )
986 inst.attrs.push_back( parseAttr( sub ) );
987 }
988
989 aSheet.symbols.push_back( std::move( inst ) );
990 }
991 else if( tag == wxT( "text" ) )
992 {
993 aSheet.texts.push_back( parseText( child ) );
994 }
995 else if( tag == wxT( "line" ) )
996 {
997 LINE line = parseLine( child );
998
999 if( line.pts.size() >= 2 )
1000 aSheet.lines.push_back( std::move( line ) );
1001 }
1002 else if( tag == wxT( "arc" ) )
1003 {
1004 aSheet.arcs.push_back( parseArc( child ) );
1005 }
1006 else if( tag == wxT( "triplePointArc" ) )
1007 {
1008 aSheet.arcs.push_back( parseTriplePointArc( child ) );
1009 }
1010 else if( tag == wxT( "poly" ) )
1011 {
1012 POLY poly = parsePoly( child );
1013
1014 if( poly.pts.size() >= 3 )
1015 aSheet.polys.push_back( std::move( poly ) );
1016 }
1017 else if( tag == wxT( "ieeeSymbol" ) )
1018 {
1019 aSheet.ieeeSymbols.push_back( parseIeeeSymbol( child ) );
1020 }
1021 else if( tag == wxT( "field" ) )
1022 {
1023 FIELD_PLACEMENT field;
1024 field.name = child->GetAttribute( wxT( "Name" ) );
1025 field.placement = parseText( child );
1026
1027 if( !field.name.IsEmpty() )
1028 aSheet.fields.push_back( std::move( field ) );
1029 }
1030 }
1031}
1032
1033} // namespace PCAD_SCH
const char * name
void LoadFromFile(const wxString &aFilename, SCHEMATIC &aSchematic)
void parseNetlist(XNODE *aNode, SCHEMATIC &aSchematic)
static bool childFlag(XNODE *aNode, const wxString &aTag)
void parseSymbolDef(XNODE *aNode, SYMBOL_DEF &aSymDef)
void parseSchematicDesign(XNODE *aNode, SCHEMATIC &aSchematic)
IEEE_SYMBOL parseIeeeSymbol(XNODE *aNode)
TEXT_ITEM parseText(XNODE *aNode)
void parseFieldSets(XNODE *aNode, SCHEMATIC &aSchematic)
std::vector< std::pair< double, double > > collectPts(XNODE *aNode) const
double childDouble(XNODE *aNode, const wxString &aTag, double aDefault=0.0) const
static wxString NodeText(XNODE *aNode)
void parseLibrary(XNODE *aNode, SCHEMATIC &aSchematic)
double toMils(const wxString &aValue) const
void parseCompDef(XNODE *aNode, COMP_DEF &aCompDef)
bool parsePtNode(XNODE *aPtNode, double &aX, double &aY) const
static XNODE * FindChild(XNODE *aNode, const wxString &aTag)
void parseTextStyleDef(XNODE *aNode, SCHEMATIC &aSchematic)
bool parsePt(XNODE *aNode, double &aX, double &aY) const
void parseSheet(XNODE *aNode, SHEET &aSheet)
void parseHeader(XNODE *aNode, SCHEMATIC &aSchematic)
static JUSTIFY parseJustify(const wxString &aValue)
static wxString childStr(XNODE *aNode, const wxString &aTag, const wxString &aDefault=wxEmptyString)
ARC parseTriplePointArc(XNODE *aNode)
An extension of wxXmlNode that can format its contents as KiCad-style s-expressions.
Definition xnode.h:67
XNODE * GetChildren() const
Definition xnode.h:97
XNODE * GetNext() const
Definition xnode.h:102
#define _(s)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
void LoadInputFile(const wxString &aFileName, wxXmlDocument *aXmlDoc)
static double childAngle(XNODE *aNode, const wxString &aTag, double aDefault=0.0)
static const wxChar *const TITLE_FIELD_NAMES[]
static std::vector< wxString > splitMeasureTokens(const wxString &aContent)
static bool splitFieldDef(const wxString &aBoth, wxString &aName, wxString &aValue)
Center/radius/angles form; triplePointArc is converted at parse time.
wxString orient
double x
double y
wxString busNameRef
std::vector< std::pair< double, double > > pts
std::vector< wxString > attachedSymbols
std::vector< COMP_PIN > compPins
A (compPin "padDes" ...) inside a compDef, mapping a symbol pin ordinal to the physical pad designato...
A placed title-block field whose name references the fieldDef holding the displayed text.
One font description inside a (textStyleDef ...).
std::vector< std::pair< double, double > > pts
std::vector< std::pair< double, double > > pts
std::map< wxString, const COMP_DEF * > compDefsByName
std::map< wxString, const COMP_INST * > compInstsByRef
std::vector< SHEET > sheets
std::vector< SYMBOL_DEF > symbolDefs
std::vector< TEXT_STYLE > textStyles
std::vector< COMP_INST > compInsts
std::map< wxString, const TEXT_STYLE * > textStylesByName
std::map< wxString, wxString > compAliases
compAlias name -> compDef name
std::vector< COMP_DEF > compDefs
std::map< wxString, const SYMBOL_DEF * > symbolDefsByName
std::vector< BUS > buses
std::vector< PORT > ports
std::vector< BUS_ENTRY > busEntries
std::vector< WIRE > wires
std::vector< JUNCTION > junctions
std::vector< POLY > polys
std::vector< TEXT_ITEM > texts
std::vector< LINE > lines
std::vector< FIELD_PLACEMENT > fields
std::vector< IEEE_SYMBOL > ieeeSymbols
std::vector< SYMBOL_INST > symbols
std::vector< ARC > arcs
std::vector< POLY > polys
std::vector< ATTR > attrs
std::vector< PIN > pins
std::vector< TEXT_ITEM > texts
std::vector< IEEE_SYMBOL > ieeeSymbols
std::vector< LINE > lines
std::vector< ARC > arcs
std::vector< ATTR > attrs
A positioned text.
std::map< wxString, wxString > fields
std::vector< std::pair< double, double > > pts
KIBIS_PIN * pin
std::vector< std::string > header
wxString result
Test unit parsing edge cases and error handling.
#define M_PI