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