1 /*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
5 * Copyright (C) 2012-2021 KiCad Developers, see AUTHORS.txt for contributors.
6 * Copyright (C) 2017 CERN.
7 *
8 * @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, you may find one here:
22 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
23 * or you may search the http://www.gnu.org website for the version 2 license,
24 * or you may write to the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26 */
27
28 #include <plugins/eagle/eagle_parser.h>
29
30 #include <string_utils.h>
31 #include <richio.h>
32 #include <wx/log.h>
33
34 #include <functional>
35 #include <cstdio>
36
37 constexpr auto DEFAULT_ALIGNMENT = ETEXT::BOTTOM_LEFT;
38
39
escapeName(const wxString & aNetName)40 wxString escapeName( const wxString& aNetName )
41 {
42 wxString ret( aNetName );
43
44 ret.Replace( "!", "~" );
45
46 return ConvertToNewOverbarNotation( ret );
47 }
48
49
50 template<> template<>
OPTIONAL_XML_ATTRIBUTE(wxString aData)51 OPTIONAL_XML_ATTRIBUTE<wxString>::OPTIONAL_XML_ATTRIBUTE( wxString aData )
52 {
53 m_isAvailable = !aData.IsEmpty();
54
55 if( m_isAvailable )
56 Set( aData );
57 }
58
59
ECOORD(const wxString & aValue,enum ECOORD::EAGLE_UNIT aUnit)60 ECOORD::ECOORD( const wxString& aValue, enum ECOORD::EAGLE_UNIT aUnit )
61 {
62 // This array is used to adjust the fraction part value basing on the number of digits
63 // in the fraction.
64 constexpr int DIVIDERS[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
65 constexpr unsigned int DIVIDERS_MAX_IDX = sizeof( DIVIDERS ) / sizeof( DIVIDERS[0] ) - 1;
66
67 int integer, fraction, pre_fraction, post_fraction;
68
69 // The following check is needed to handle correctly negative fractions where the integer
70 // part == 0.
71 bool negative = ( aValue[0] == '-' );
72
73 // %n is used to find out how many digits contains the fraction part, e.g. 0.001 contains 3
74 // digits.
75 int ret = sscanf( aValue.c_str(), "%d.%n%d%n", &integer, &pre_fraction, &fraction,
76 &post_fraction );
77
78 if( ret == 0 )
79 throw XML_PARSER_ERROR( "Invalid coordinate" );
80
81 // process the integer part
82 value = ConvertToNm( integer, aUnit );
83
84 // process the fraction part
85 if( ret == 2 )
86 {
87 int digits = post_fraction - pre_fraction;
88
89 // adjust the number of digits if necessary as we cannot handle anything smaller than
90 // nanometers (rounding).
91 if( (unsigned) digits > DIVIDERS_MAX_IDX )
92 {
93 int diff = digits - DIVIDERS_MAX_IDX;
94 digits = DIVIDERS_MAX_IDX;
95 fraction /= DIVIDERS[diff];
96 }
97
98 int frac_value = ConvertToNm( fraction, aUnit ) / DIVIDERS[digits];
99
100 // keep the sign in mind
101 value = negative ? value - frac_value : value + frac_value;
102 }
103 }
104
105
ConvertToNm(int aValue,enum EAGLE_UNIT aUnit)106 long long int ECOORD::ConvertToNm( int aValue, enum EAGLE_UNIT aUnit )
107 {
108 long long int ret;
109
110 switch( aUnit )
111 {
112 default:
113 case EU_NM: ret = aValue; break;
114 case EU_MM: ret = (long long) aValue * 1000000; break;
115 case EU_INCH: ret = (long long) aValue * 25400000; break;
116 case EU_MIL: ret = (long long) aValue * 25400; break;
117 }
118
119 if( ( ret > 0 ) != ( aValue > 0 ) )
120 wxLogError( _( "Invalid size %lld: too large" ), aValue );
121
122 return ret;
123 }
124
125
126 // Template specializations below parse wxString to the used types:
127 // - wxString (preferred)
128 // - string
129 // - double
130 // - int
131 // - bool
132 // - EROT
133 // - ECOORD
134
135 template <>
Convert(const wxString & aValue)136 wxString Convert<wxString>( const wxString& aValue )
137 {
138 return aValue;
139 }
140
141
142 template <>
Convert(const wxString & aValue)143 std::string Convert<std::string>( const wxString& aValue )
144 {
145 return std::string( aValue.ToUTF8() );
146 }
147
148
149 template <>
Convert(const wxString & aValue)150 double Convert<double>( const wxString& aValue )
151 {
152 double value;
153
154 if( aValue.ToCDouble( &value ) )
155 return value;
156 else
157 throw XML_PARSER_ERROR( "Conversion to double failed. Original value: '" +
158 aValue.ToStdString() + "'." );
159 }
160
161
162 template <>
Convert(const wxString & aValue)163 int Convert<int>( const wxString& aValue )
164 {
165 if( aValue.IsEmpty() )
166 throw XML_PARSER_ERROR( "Conversion to int failed. Original value is empty." );
167
168 return wxAtoi( aValue );
169 }
170
171
172 template <>
Convert(const wxString & aValue)173 bool Convert<bool>( const wxString& aValue )
174 {
175 if( aValue != "yes" && aValue != "no" )
176 throw XML_PARSER_ERROR( "Conversion to bool failed. Original value, '" +
177 aValue.ToStdString() +
178 "', is neither 'yes' nor 'no'." );
179
180 return aValue == "yes";
181 }
182
183
184 /// parse an Eagle XML "rot" field. Unfortunately the DTD seems not to explain
185 /// this format very well. [S][M]R<degrees>. Examples: "R90", "MR180", "SR180"
186 template<>
Convert(const wxString & aRot)187 EROT Convert<EROT>( const wxString& aRot )
188 {
189 EROT value;
190
191 value.spin = aRot.find( 'S' ) != aRot.npos;
192 value.mirror = aRot.find( 'M' ) != aRot.npos;
193 value.degrees = strtod( aRot.c_str()
194 + 1 // skip leading 'R'
195 + int( value.spin ) // skip optional leading 'S'
196 + int( value.mirror ), // skip optional leading 'M'
197 nullptr );
198
199 return value;
200 }
201
202
203 template<>
Convert(const wxString & aCoord)204 ECOORD Convert<ECOORD>( const wxString& aCoord )
205 {
206 // Eagle uses millimeters as the default unit
207 return ECOORD( aCoord, ECOORD::EAGLE_UNIT::EU_MM );
208 }
209
210
211 /**
212 * Parse \a aAttribute of the XML node \a aNode.
213 *
214 * @param aNode is the node whose attribute will be parsed.
215 * @param aAttribute is the attribute that will be parsed.
216 * @throw XML_PARSER_ERROR - exception thrown if the required attribute is missing
217 * @return T - the attributed parsed as the specified type.
218 */
219 template<typename T>
parseRequiredAttribute(wxXmlNode * aNode,const wxString & aAttribute)220 T parseRequiredAttribute( wxXmlNode* aNode, const wxString& aAttribute )
221 {
222 wxString value;
223
224 if( aNode->GetAttribute( aAttribute, &value ) )
225 return Convert<T>( value );
226 else
227 throw XML_PARSER_ERROR( "The required attribute " + aAttribute + " is missing." );
228 }
229
230
231 /**
232 * Parse option \a aAttribute of the XML node \a aNode.
233 *
234 * @param aNode is the node whose attribute will be parsed.
235 * @param aAttribute is the attribute that will be parsed.
236 * @return OPTIONAL_XML_ATTRIBUTE<T> - an optional XML attribute, parsed as the specified type if
237 * found.
238 */
239 template<typename T>
parseOptionalAttribute(wxXmlNode * aNode,const wxString & aAttribute)240 OPTIONAL_XML_ATTRIBUTE<T> parseOptionalAttribute( wxXmlNode* aNode, const wxString& aAttribute )
241 {
242 return OPTIONAL_XML_ATTRIBUTE<T>( aNode->GetAttribute( aAttribute ) );
243 }
244
245
MapChildren(wxXmlNode * aCurrentNode)246 NODE_MAP MapChildren( wxXmlNode* aCurrentNode )
247 {
248 // Map node_name -> node_pointer
249 NODE_MAP nodesMap;
250
251 // Loop through all children mapping them in nodesMap
252 if( aCurrentNode )
253 aCurrentNode = aCurrentNode->GetChildren();
254
255 while( aCurrentNode )
256 {
257 // Create a new pair in the map
258 // key: current node name
259 // value: current node pointer
260 nodesMap[aCurrentNode->GetName()] = aCurrentNode;
261
262 // Get next child
263 aCurrentNode = aCurrentNode->GetNext();
264 }
265
266 return nodesMap;
267 }
268
269
ConvertArcCenter(const wxPoint & aStart,const wxPoint & aEnd,double aAngle)270 wxPoint ConvertArcCenter( const wxPoint& aStart, const wxPoint& aEnd, double aAngle )
271 {
272 // Eagle give us start and end.
273 // S_ARC wants start to give the center, and end to give the start.
274 double dx = aEnd.x - aStart.x, dy = aEnd.y - aStart.y;
275 wxPoint mid = ( aStart + aEnd ) / 2;
276
277 double dlen = sqrt( dx*dx + dy*dy );
278
279 if( !std::isnormal( dlen ) || !std::isnormal( aAngle ) )
280 {
281 THROW_IO_ERROR( wxString::Format( _( "Invalid Arc with radius %f and angle %f" ),
282 dlen,
283 aAngle ) );
284 }
285
286 double dist = dlen / ( 2 * tan( DEG2RAD( aAngle ) / 2 ) );
287
288 wxPoint center(
289 mid.x + dist * ( dy / dlen ),
290 mid.y - dist * ( dx / dlen )
291 );
292
293 return center;
294 }
295
296
parseAlignment(const wxString & aAlignment)297 static int parseAlignment( const wxString& aAlignment )
298 {
299 // (bottom-left | bottom-center | bottom-right | center-left |
300 // center | center-right | top-left | top-center | top-right)
301 if( aAlignment == "center" )
302 return ETEXT::CENTER;
303 else if( aAlignment == "center-right" )
304 return ETEXT::CENTER_RIGHT;
305 else if( aAlignment == "top-left" )
306 return ETEXT::TOP_LEFT;
307 else if( aAlignment == "top-center" )
308 return ETEXT::TOP_CENTER;
309 else if( aAlignment == "top-right" )
310 return ETEXT::TOP_RIGHT;
311 else if( aAlignment == "bottom-left" )
312 return ETEXT::BOTTOM_LEFT;
313 else if( aAlignment == "bottom-center" )
314 return ETEXT::BOTTOM_CENTER;
315 else if( aAlignment == "bottom-right" )
316 return ETEXT::BOTTOM_RIGHT;
317 else if( aAlignment == "center-left" )
318 return ETEXT::CENTER_LEFT;
319
320 return DEFAULT_ALIGNMENT;
321 }
322
323
EWIRE(wxXmlNode * aWire)324 EWIRE::EWIRE( wxXmlNode* aWire )
325 {
326 /*
327 <!ELEMENT wire EMPTY>
328 <!ATTLIST wire
329 x1 %Coord; #REQUIRED
330 y1 %Coord; #REQUIRED
331 x2 %Coord; #REQUIRED
332 y2 %Coord; #REQUIRED
333 width %Dimension; #REQUIRED
334 layer %Layer; #REQUIRED
335 extent %Extent; #IMPLIED -- only applicable for airwires --
336 style %WireStyle; "continuous"
337 curve %WireCurve; "0"
338 cap %WireCap; "round" -- only applicable if 'curve' is not zero --
339 >
340 */
341
342 x1 = parseRequiredAttribute<ECOORD>( aWire, "x1" );
343 y1 = parseRequiredAttribute<ECOORD>( aWire, "y1" );
344 x2 = parseRequiredAttribute<ECOORD>( aWire, "x2" );
345 y2 = parseRequiredAttribute<ECOORD>( aWire, "y2" );
346 width = parseRequiredAttribute<ECOORD>( aWire, "width" );
347 layer = parseRequiredAttribute<int>( aWire, "layer" );
348 curve = parseOptionalAttribute<double>( aWire, "curve" );
349
350 opt_wxString s = parseOptionalAttribute<wxString>( aWire, "style" );
351
352 if( s == "continuous" )
353 style = EWIRE::CONTINUOUS;
354 else if( s == "longdash" )
355 style = EWIRE::LONGDASH;
356 else if( s == "shortdash" )
357 style = EWIRE::SHORTDASH;
358 else if( s == "dashdot" )
359 style = EWIRE::DASHDOT;
360
361 s = parseOptionalAttribute<wxString>( aWire, "cap" );
362
363 if( s == "round" )
364 cap = EWIRE::ROUND;
365 else if( s == "flat" )
366 cap = EWIRE::FLAT;
367 }
368
369
EJUNCTION(wxXmlNode * aJunction)370 EJUNCTION::EJUNCTION( wxXmlNode* aJunction )
371 {
372 /*
373 <!ELEMENT junction EMPTY>
374 <!ATTLIST junction
375 x %Coord; #REQUIRED
376 y %Coord; #REQUIRED
377 >
378 */
379
380 x = parseRequiredAttribute<ECOORD>( aJunction, "x" );
381 y = parseRequiredAttribute<ECOORD>( aJunction, "y" );
382 }
383
384
ELABEL(wxXmlNode * aLabel,const wxString & aNetName)385 ELABEL::ELABEL( wxXmlNode* aLabel, const wxString& aNetName )
386 {
387 /*
388 <!ELEMENT label EMPTY>
389 <!ATTLIST label
390 x %Coord; #REQUIRED
391 y %Coord; #REQUIRED
392 size %Dimension; #REQUIRED
393 layer %Layer; #REQUIRED
394 font %TextFont; "proportional"
395 ratio %Int; "8"
396 rot %Rotation; "R0"
397 xref %Bool; "no"
398 >
399 */
400
401 x = parseRequiredAttribute<ECOORD>( aLabel, "x" );
402 y = parseRequiredAttribute<ECOORD>( aLabel, "y" );
403 size = parseRequiredAttribute<ECOORD>( aLabel, "size" );
404 layer = parseRequiredAttribute<int>( aLabel, "layer" );
405 rot = parseOptionalAttribute<EROT>( aLabel, "rot" );
406 xref = parseOptionalAttribute<wxString>( aLabel, "xref" );
407 netname = aNetName;
408 }
409
410
EVIA(wxXmlNode * aVia)411 EVIA::EVIA( wxXmlNode* aVia )
412 {
413 /*
414 <!ELEMENT via EMPTY>
415 <!ATTLIST via
416 x %Coord; #REQUIRED
417 y %Coord; #REQUIRED
418 extent %Extent; #REQUIRED
419 drill %Dimension; #REQUIRED
420 diameter %Dimension; "0"
421 shape %ViaShape; "round"
422 alwaysstop %Bool; "no"
423 >
424 */
425
426 x = parseRequiredAttribute<ECOORD>( aVia, "x" );
427 y = parseRequiredAttribute<ECOORD>( aVia, "y" );
428
429 wxString ext = parseRequiredAttribute<wxString>( aVia, "extent" );
430 sscanf( ext.c_str(), "%d-%d", &layer_front_most, &layer_back_most );
431
432 drill = parseRequiredAttribute<ECOORD>( aVia, "drill" );
433 diam = parseOptionalAttribute<ECOORD>( aVia, "diameter" );
434 shape = parseOptionalAttribute<wxString>( aVia, "shape" );
435 }
436
437
ECIRCLE(wxXmlNode * aCircle)438 ECIRCLE::ECIRCLE( wxXmlNode* aCircle )
439 {
440 /*
441 <!ELEMENT circle EMPTY>
442 <!ATTLIST circle
443 x %Coord; #REQUIRED
444 y %Coord; #REQUIRED
445 radius %Coord; #REQUIRED
446 width %Dimension; #REQUIRED
447 layer %Layer; #REQUIRED
448 >
449 */
450
451 x = parseRequiredAttribute<ECOORD>( aCircle, "x" );
452 y = parseRequiredAttribute<ECOORD>( aCircle, "y" );
453 radius = parseRequiredAttribute<ECOORD>( aCircle, "radius" );
454 width = parseRequiredAttribute<ECOORD>( aCircle, "width" );
455 layer = parseRequiredAttribute<int>( aCircle, "layer" );
456 }
457
458
ERECT(wxXmlNode * aRect)459 ERECT::ERECT( wxXmlNode* aRect )
460 {
461 /*
462 <!ELEMENT rectangle EMPTY>
463 <!ATTLIST rectangle
464 x1 %Coord; #REQUIRED
465 y1 %Coord; #REQUIRED
466 x2 %Coord; #REQUIRED
467 y2 %Coord; #REQUIRED
468 layer %Layer; #REQUIRED
469 rot %Rotation; "R0"
470 >
471 */
472
473 x1 = parseRequiredAttribute<ECOORD>( aRect, "x1" );
474 y1 = parseRequiredAttribute<ECOORD>( aRect, "y1" );
475 x2 = parseRequiredAttribute<ECOORD>( aRect, "x2" );
476 y2 = parseRequiredAttribute<ECOORD>( aRect, "y2" );
477 layer = parseRequiredAttribute<int>( aRect, "layer" );
478 rot = parseOptionalAttribute<EROT>( aRect, "rot" );
479 }
480
481
EATTR(wxXmlNode * aTree)482 EATTR::EATTR( wxXmlNode* aTree )
483 {
484 /*
485 <!ELEMENT attribute EMPTY>
486 <!ATTLIST attribute
487 name %String; #REQUIRED
488 value %String; #IMPLIED
489 x %Coord; #IMPLIED
490 y %Coord; #IMPLIED
491 size %Dimension; #IMPLIED
492 layer %Layer; #IMPLIED
493 font %TextFont; #IMPLIED
494 ratio %Int; #IMPLIED
495 rot %Rotation; "R0"
496 display %AttributeDisplay; "value" -- only in <element> or <instance> context --
497 constant %Bool; "no" -- only in <device> context --
498 >
499 */
500
501 name = parseRequiredAttribute<wxString>( aTree, "name" );
502 value = parseOptionalAttribute<wxString>( aTree, "value" );
503
504 x = parseOptionalAttribute<ECOORD>( aTree, "x" );
505 y = parseOptionalAttribute<ECOORD>( aTree, "y" );
506 size = parseOptionalAttribute<ECOORD>( aTree, "size" );
507
508 layer = parseOptionalAttribute<int>( aTree, "layer" );
509 ratio = parseOptionalAttribute<double>( aTree, "ratio" );
510 rot = parseOptionalAttribute<EROT>( aTree, "rot" );
511
512 opt_wxString stemp = parseOptionalAttribute<wxString>( aTree, "display" );
513
514 // (off | value | name | both)
515 if( stemp == "off" )
516 display = EATTR::Off;
517 else if( stemp == "name" )
518 display = EATTR::NAME;
519 else if( stemp == "both" )
520 display = EATTR::BOTH;
521 else // "value" is the default
522 display = EATTR::VALUE;
523
524 stemp = parseOptionalAttribute<wxString>( aTree, "align" );
525
526 align = stemp ? parseAlignment( *stemp ) : DEFAULT_ALIGNMENT;
527 }
528
529
EDIMENSION(wxXmlNode * aDimension)530 EDIMENSION::EDIMENSION( wxXmlNode* aDimension )
531 {
532 /*
533 <!ELEMENT dimension EMPTY>
534 <!ATTLIST dimension
535 x1 %Coord; #REQUIRED
536 y1 %Coord; #REQUIRED
537 x2 %Coord; #REQUIRED
538 y2 %Coord; #REQUIRED
539 x3 %Coord; #REQUIRED
540 y3 %Coord; #REQUIRED
541 layer %Layer; #REQUIRED
542 dtype %DimensionType; "parallel"
543 >
544 */
545
546 x1 = parseRequiredAttribute<ECOORD>( aDimension, "x1" );
547 y1 = parseRequiredAttribute<ECOORD>( aDimension, "y1" );
548 x2 = parseRequiredAttribute<ECOORD>( aDimension, "x2" );
549 y2 = parseRequiredAttribute<ECOORD>( aDimension, "y2" );
550 x3 = parseRequiredAttribute<ECOORD>( aDimension, "x3" );
551 y3 = parseRequiredAttribute<ECOORD>( aDimension, "y3" );
552 layer = parseRequiredAttribute<int>( aDimension, "layer" );
553 dimensionType = parseOptionalAttribute<wxString>( aDimension, "dtype" );
554 }
555
556
ETEXT(wxXmlNode * aText)557 ETEXT::ETEXT( wxXmlNode* aText )
558 {
559 /*
560 <!ELEMENT text (#PCDATA)>
561 <!ATTLIST text
562 x %Coord; #REQUIRED
563 y %Coord; #REQUIRED
564 size %Dimension; #REQUIRED
565 layer %Layer; #REQUIRED
566 font %TextFont; "proportional"
567 ratio %Int; "8"
568 rot %Rotation; "R0"
569 align %Align; "bottom-left"
570 >
571 */
572
573 text = aText->GetNodeContent();
574 x = parseRequiredAttribute<ECOORD>( aText, "x" );
575 y = parseRequiredAttribute<ECOORD>( aText, "y" );
576 size = parseRequiredAttribute<ECOORD>( aText, "size" );
577 layer = parseRequiredAttribute<int>( aText, "layer" );
578
579 font = parseOptionalAttribute<wxString>( aText, "font" );
580 ratio = parseOptionalAttribute<double>( aText, "ratio" );
581 rot = parseOptionalAttribute<EROT>( aText, "rot" );
582
583 opt_wxString stemp = parseOptionalAttribute<wxString>( aText, "align" );
584
585 align = stemp ? parseAlignment( *stemp ) : DEFAULT_ALIGNMENT;
586 }
587
588
ConvertSize() const589 wxSize ETEXT::ConvertSize() const
590 {
591 wxSize textsize;
592
593 if( font )
594 {
595 const wxString& fontName = font.CGet();
596
597 if( fontName == "vector" )
598 {
599 textsize = wxSize( size.ToSchUnits(), size.ToSchUnits() );
600 }
601 else if( fontName == "fixed" )
602 {
603 textsize = wxSize( size.ToSchUnits(), size.ToSchUnits() * 0.80 );
604 }
605 else
606 {
607 textsize = wxSize( size.ToSchUnits(), size.ToSchUnits() );
608 }
609 }
610 else
611 {
612 textsize = wxSize( size.ToSchUnits() * 0.85, size.ToSchUnits() );
613 }
614
615 return textsize;
616 }
617
618
EFRAME(wxXmlNode * aFrameNode)619 EFRAME::EFRAME( wxXmlNode* aFrameNode )
620 {
621 /*
622 * <!ELEMENT frame EMPTY>
623 * <!ATTLIST frame
624 * x1 %Coord; #REQUIRED
625 * y1 %Coord; #REQUIRED
626 * x2 %Coord; #REQUIRED
627 * y2 %Coord; #REQUIRED
628 * columns %Int; #REQUIRED
629 * rows %Int; #REQUIRED
630 * layer %Layer; #REQUIRED
631 * border-left %Bool; "yes"
632 * border-top %Bool; "yes"
633 * border-right %Bool; "yes"
634 * border-bottom %Bool; "yes"
635 * >
636 */
637 border_left = true;
638 border_top = true;
639 border_right = true;
640 border_bottom = true;
641
642 x1 = parseRequiredAttribute<ECOORD>( aFrameNode, "x1" );
643 y1 = parseRequiredAttribute<ECOORD>( aFrameNode, "y1" );
644 x2 = parseRequiredAttribute<ECOORD>( aFrameNode, "x2" );
645 y2 = parseRequiredAttribute<ECOORD>( aFrameNode, "y2" );
646 columns = parseRequiredAttribute<int>( aFrameNode, "columns" );
647 rows = parseRequiredAttribute<int>( aFrameNode, "rows" );
648 layer = parseRequiredAttribute<int>( aFrameNode, "layer" );
649 border_left = parseOptionalAttribute<bool>( aFrameNode, "border-left" );
650 border_top = parseOptionalAttribute<bool>( aFrameNode, "border-top" );
651 border_right = parseOptionalAttribute<bool>( aFrameNode, "border-right" );
652 border_bottom = parseOptionalAttribute<bool>( aFrameNode, "border-bottom" );
653 }
654
655
EPAD_COMMON(wxXmlNode * aPad)656 EPAD_COMMON::EPAD_COMMON( wxXmlNode* aPad )
657 {
658 // #REQUIRED says DTD, throw exception if not found
659 name = parseRequiredAttribute<wxString>( aPad, "name" );
660 x = parseRequiredAttribute<ECOORD>( aPad, "x" );
661 y = parseRequiredAttribute<ECOORD>( aPad, "y" );
662 rot = parseOptionalAttribute<EROT>( aPad, "rot" );
663 stop = parseOptionalAttribute<bool>( aPad, "stop" );
664 thermals = parseOptionalAttribute<bool>( aPad, "thermals" );
665 }
666
667
EPAD(wxXmlNode * aPad)668 EPAD::EPAD( wxXmlNode* aPad )
669 : EPAD_COMMON( aPad )
670 {
671 /*
672 <!ELEMENT pad EMPTY>
673 <!ATTLIST pad
674 name %String; #REQUIRED
675 x %Coord; #REQUIRED
676 y %Coord; #REQUIRED
677 drill %Dimension; #REQUIRED
678 diameter %Dimension; "0"
679 shape %PadShape; "round"
680 rot %Rotation; "R0"
681 stop %Bool; "yes"
682 thermals %Bool; "yes"
683 first %Bool; "no"
684 >
685 */
686
687 // #REQUIRED says DTD, throw exception if not found
688 drill = parseRequiredAttribute<ECOORD>( aPad, "drill" );
689
690 // Optional attributes
691 diameter = parseOptionalAttribute<ECOORD>( aPad, "diameter" );
692
693 opt_wxString s = parseOptionalAttribute<wxString>( aPad, "shape" );
694
695 // (square | round | octagon | long | offset)
696 if( s == "square" )
697 shape = EPAD::SQUARE;
698 else if( s == "round" )
699 shape = EPAD::ROUND;
700 else if( s == "octagon" )
701 shape = EPAD::OCTAGON;
702 else if( s == "long" )
703 shape = EPAD::LONG;
704 else if( s == "offset" )
705 shape = EPAD::OFFSET;
706
707 first = parseOptionalAttribute<bool>( aPad, "first" );
708 }
709
710
ESMD(wxXmlNode * aSMD)711 ESMD::ESMD( wxXmlNode* aSMD )
712 : EPAD_COMMON( aSMD )
713 {
714 /*
715 <!ATTLIST smd
716 name %String; #REQUIRED
717 x %Coord; #REQUIRED
718 y %Coord; #REQUIRED
719 dx %Dimension; #REQUIRED
720 dy %Dimension; #REQUIRED
721 layer %Layer; #REQUIRED
722 roundness %Int; "0"
723 rot %Rotation; "R0"
724 stop %Bool; "yes"
725 thermals %Bool; "yes"
726 cream %Bool; "yes"
727 >
728 */
729
730 // DTD #REQUIRED, throw exception if not found
731 dx = parseRequiredAttribute<ECOORD>( aSMD, "dx" );
732 dy = parseRequiredAttribute<ECOORD>( aSMD, "dy" );
733 layer = parseRequiredAttribute<int>( aSMD, "layer" );
734
735 roundness = parseOptionalAttribute<int>( aSMD, "roundness" );
736 cream = parseOptionalAttribute<bool>( aSMD, "cream" );
737 }
738
739
EPIN(wxXmlNode * aPin)740 EPIN::EPIN( wxXmlNode* aPin )
741 {
742 /*
743 <!ELEMENT pin EMPTY>
744 <!ATTLIST pin
745 name %String; #REQUIRED
746 x %Coord; #REQUIRED
747 y %Coord; #REQUIRED
748 visible %PinVisible; "both"
749 length %PinLength; "long"
750 direction %PinDirection; "io"
751 function %PinFunction; "none"
752 swaplevel %Int; "0"
753 rot %Rotation; "R0"
754 >
755 */
756
757 // DTD #REQUIRED, throw exception if not found
758 name = parseRequiredAttribute<wxString>( aPin, "name" );
759 x = parseRequiredAttribute<ECOORD>( aPin, "x" );
760 y = parseRequiredAttribute<ECOORD>( aPin, "y" );
761
762 visible = parseOptionalAttribute<wxString>( aPin, "visible" );
763 length = parseOptionalAttribute<wxString>( aPin, "length" );
764 direction = parseOptionalAttribute<wxString>( aPin, "direction" );
765 function = parseOptionalAttribute<wxString>( aPin, "function" );
766 swaplevel = parseOptionalAttribute<int>( aPin, "swaplevel" );
767 rot = parseOptionalAttribute<EROT>( aPin, "rot" );
768 }
769
770
EVERTEX(wxXmlNode * aVertex)771 EVERTEX::EVERTEX( wxXmlNode* aVertex )
772 {
773 /*
774 <!ELEMENT vertex EMPTY>
775 <!ATTLIST vertex
776 x %Coord; #REQUIRED
777 y %Coord; #REQUIRED
778 curve %WireCurve; "0" -- the curvature from this vertex to the next one --
779 >
780 */
781
782 x = parseRequiredAttribute<ECOORD>( aVertex, "x" );
783 y = parseRequiredAttribute<ECOORD>( aVertex, "y" );
784 curve = parseOptionalAttribute<double>( aVertex, "curve" );
785 }
786
787
EPOLYGON(wxXmlNode * aPolygon)788 EPOLYGON::EPOLYGON( wxXmlNode* aPolygon )
789 {
790 /*
791 <!ATTLIST polygon
792 width %Dimension; #REQUIRED
793 layer %Layer; #REQUIRED
794 spacing %Dimension; #IMPLIED
795 pour %PolygonPour; "solid"
796 isolate %Dimension; #IMPLIED -- only in <signal> or <package> context --
797 orphans %Bool; "no" -- only in <signal> context --
798 thermals %Bool; "yes" -- only in <signal> context --
799 rank %Int; "0" -- 1..6 in <signal> context, 0 or 7 in
800 <package> context --
801 >
802 */
803
804 width = parseRequiredAttribute<ECOORD>( aPolygon, "width" );
805 layer = parseRequiredAttribute<int>( aPolygon, "layer" );
806
807 spacing = parseOptionalAttribute<ECOORD>( aPolygon, "spacing" );
808 isolate = parseOptionalAttribute<ECOORD>( aPolygon, "isolate" );
809 opt_wxString s = parseOptionalAttribute<wxString>( aPolygon, "pour" );
810
811 // default pour to solid fill
812 pour = EPOLYGON::SOLID;
813
814 // (solid | hatch | cutout)
815 if( s == "hatch" )
816 pour = EPOLYGON::HATCH;
817 else if( s == "cutout" )
818 pour = EPOLYGON::CUTOUT;
819
820 orphans = parseOptionalAttribute<bool>( aPolygon, "orphans" );
821 thermals = parseOptionalAttribute<bool>( aPolygon, "thermals" );
822 rank = parseOptionalAttribute<int>( aPolygon, "rank" );
823 }
824
825
EHOLE(wxXmlNode * aHole)826 EHOLE::EHOLE( wxXmlNode* aHole )
827 {
828 /*
829 <!ELEMENT hole EMPTY>
830 <!ATTLIST hole
831 x %Coord; #REQUIRED
832 y %Coord; #REQUIRED
833 drill %Dimension; #REQUIRED
834 >
835 */
836
837 // #REQUIRED:
838 x = parseRequiredAttribute<ECOORD>( aHole, "x" );
839 y = parseRequiredAttribute<ECOORD>( aHole, "y" );
840 drill = parseRequiredAttribute<ECOORD>( aHole, "drill" );
841 }
842
843
EELEMENT(wxXmlNode * aElement)844 EELEMENT::EELEMENT( wxXmlNode* aElement )
845 {
846 /*
847 <!ELEMENT element (attribute*, variant*)>
848 <!ATTLIST element
849 name %String; #REQUIRED
850 library %String; #REQUIRED
851 package %String; #REQUIRED
852 value %String; #REQUIRED
853 x %Coord; #REQUIRED
854 y %Coord; #REQUIRED
855 locked %Bool; "no"
856 smashed %Bool; "no"
857 rot %Rotation; "R0"
858 >
859 */
860
861 // #REQUIRED
862 name = parseRequiredAttribute<wxString>( aElement, "name" );
863 library = parseRequiredAttribute<wxString>( aElement, "library" );
864 value = parseRequiredAttribute<wxString>( aElement, "value" );
865 std::string p = parseRequiredAttribute<std::string>( aElement, "package" );
866 ReplaceIllegalFileNameChars( &p, '_' );
867 package = wxString::FromUTF8( p.c_str() );
868
869 x = parseRequiredAttribute<ECOORD>( aElement, "x" );
870 y = parseRequiredAttribute<ECOORD>( aElement, "y" );
871
872 // optional
873 locked = parseOptionalAttribute<bool>( aElement, "locked" );
874 smashed = parseOptionalAttribute<bool>( aElement, "smashed" );
875 rot = parseOptionalAttribute<EROT>( aElement, "rot" );
876 }
877
878
ELAYER(wxXmlNode * aLayer)879 ELAYER::ELAYER( wxXmlNode* aLayer )
880 {
881 /*
882 <!ELEMENT layer EMPTY>
883 <!ATTLIST layer
884 number %Layer; #REQUIRED
885 name %String; #REQUIRED
886 color %Int; #REQUIRED
887 fill %Int; #REQUIRED
888 visible %Bool; "yes"
889 active %Bool; "yes"
890 >
891 */
892
893 number = parseRequiredAttribute<int>( aLayer, "number" );
894 name = parseRequiredAttribute<wxString>( aLayer, "name" );
895 color = parseRequiredAttribute<int>( aLayer, "color" );
896 fill = 1; // Temporary value.
897 visible = parseOptionalAttribute<bool>( aLayer, "visible" );
898 active = parseOptionalAttribute<bool>( aLayer, "active" );
899 }
900
901
EPART(wxXmlNode * aPart)902 EPART::EPART( wxXmlNode* aPart )
903 {
904 /*
905 * <!ELEMENT part (attribute*, variant*)>
906 * <!ATTLIST part
907 * name %String; #REQUIRED
908 * library %String; #REQUIRED
909 * deviceset %String; #REQUIRED
910 * device %String; #REQUIRED
911 * technology %String; ""
912 * value %String; #IMPLIED
913 * >
914 */
915 // #REQUIRED
916 name = parseRequiredAttribute<wxString>( aPart, "name" );
917 library = parseRequiredAttribute<wxString>( aPart, "library" );
918 deviceset = parseRequiredAttribute<wxString>( aPart, "deviceset" );
919 device = parseRequiredAttribute<wxString>( aPart, "device" );
920 technology = parseOptionalAttribute<wxString>( aPart, "technology" );
921 value = parseOptionalAttribute<wxString>( aPart, "value" );
922
923 for( wxXmlNode* child = aPart->GetChildren(); child; child = child->GetNext() )
924 {
925 if( child->GetName() == "attribute" )
926 {
927 std::string aname, avalue;
928
929 for( wxXmlAttribute* x = child->GetAttributes(); x; x = x->GetNext() )
930 {
931 if( x->GetName() == "name" )
932 aname = x->GetValue();
933 else if( x->GetName() == "value" )
934 avalue = x->GetValue();
935 }
936
937 if( aname.size() && avalue.size() )
938 attribute[aname] = avalue;
939 }
940 else if( child->GetName() == "variant" )
941 {
942 std::string aname, avalue;
943
944 for( wxXmlAttribute* x = child->GetAttributes(); x; x = x->GetNext() )
945 {
946 if( x->GetName() == "name" )
947 aname = x->GetValue();
948 else if( x->GetName() == "value" )
949 avalue = x->GetValue();
950 }
951
952 if( aname.size() && avalue.size() )
953 variant[aname] = avalue;
954 }
955 }
956 }
957
958
EINSTANCE(wxXmlNode * aInstance)959 EINSTANCE::EINSTANCE( wxXmlNode* aInstance )
960 {
961 /*
962 * <!ELEMENT instance (attribute)*>
963 * <!ATTLIST instance
964 * part %String; #REQUIRED
965 * gate %String; #REQUIRED
966 * x %Coord; #REQUIRED
967 * y %Coord; #REQUIRED
968 * smashed %Bool; "no"
969 * rot %Rotation; "R0"
970 * >
971 */
972 part = parseRequiredAttribute<wxString>( aInstance, "part" );
973 gate = parseRequiredAttribute<wxString>( aInstance, "gate" );
974
975 x = parseRequiredAttribute<ECOORD>( aInstance, "x" );
976 y = parseRequiredAttribute<ECOORD>( aInstance, "y" );
977
978 // optional
979 smashed = parseOptionalAttribute<bool>( aInstance, "smashed" );
980 rot = parseOptionalAttribute<EROT>( aInstance, "rot" );
981 }
982
983
EGATE(wxXmlNode * aGate)984 EGATE::EGATE( wxXmlNode* aGate )
985 {
986 /*
987 * <!ELEMENT gate EMPTY>
988 * <!ATTLIST gate
989 * name %String; #REQUIRED
990 * symbol %String; #REQUIRED
991 * x %Coord; #REQUIRED
992 * y %Coord; #REQUIRED
993 * addlevel %GateAddLevel; "next"
994 * swaplevel %Int; "0"
995 * >
996 */
997
998 name = parseRequiredAttribute<wxString>( aGate, "name" );
999 symbol = parseRequiredAttribute<wxString>( aGate, "symbol" );
1000
1001 x = parseRequiredAttribute<ECOORD>( aGate, "x" );
1002 y = parseRequiredAttribute<ECOORD>( aGate, "y" );
1003
1004 opt_wxString stemp = parseOptionalAttribute<wxString>( aGate, "addlevel" );
1005
1006 // (off | value | name | both)
1007 if( stemp == "must" )
1008 addlevel = EGATE::MUST;
1009 else if( stemp == "can" )
1010 addlevel = EGATE::CAN;
1011 else if( stemp == "next" )
1012 addlevel = EGATE::NEXT;
1013 else if( stemp == "request" )
1014 addlevel = EGATE::REQUEST;
1015 else if( stemp == "always" )
1016 addlevel = EGATE::ALWAYS;
1017 else
1018 addlevel = EGATE::NEXT;
1019 }
1020
1021
ECONNECT(wxXmlNode * aConnect)1022 ECONNECT::ECONNECT( wxXmlNode* aConnect )
1023 {
1024 /*
1025 * <!ELEMENT connect EMPTY>
1026 * <!ATTLIST connect
1027 * gate %String; #REQUIRED
1028 * pin %String; #REQUIRED
1029 * pad %String; #REQUIRED
1030 * route %ContactRoute; "all"
1031 * >
1032 */
1033 gate = parseRequiredAttribute<wxString>( aConnect, "gate" );
1034 pin = parseRequiredAttribute<wxString>( aConnect, "pin" );
1035 pad = parseRequiredAttribute<wxString>( aConnect, "pad" );
1036 }
1037
1038
EDEVICE(wxXmlNode * aDevice)1039 EDEVICE::EDEVICE( wxXmlNode* aDevice )
1040 {
1041 /*
1042 <!ELEMENT device (connects?, technologies?)>
1043 <!ATTLIST device
1044 name %String; ""
1045 package %String; #IMPLIED
1046 >
1047 */
1048 name = parseRequiredAttribute<wxString>( aDevice, "name" );
1049 opt_wxString pack = parseOptionalAttribute<wxString>( aDevice, "package" );
1050
1051 if( pack )
1052 {
1053 std::string p( pack->c_str() );
1054 ReplaceIllegalFileNameChars( &p, '_' );
1055 package.Set( wxString::FromUTF8( p.c_str() ) );
1056 }
1057
1058 NODE_MAP aDeviceChildren = MapChildren( aDevice );
1059 wxXmlNode* connectNode = getChildrenNodes( aDeviceChildren, "connects" );
1060
1061 while( connectNode )
1062 {
1063 connects.emplace_back( connectNode );
1064 connectNode = connectNode->GetNext();
1065 }
1066 }
1067
1068
EDEVICE_SET(wxXmlNode * aDeviceSet)1069 EDEVICE_SET::EDEVICE_SET( wxXmlNode* aDeviceSet )
1070 {
1071 /*
1072 <!ELEMENT deviceset (description?, gates, devices)>
1073 <!ATTLIST deviceset
1074 name %String; #REQUIRED
1075 prefix %String; ""
1076 uservalue %Bool; "no"
1077 >
1078 */
1079
1080 name = parseRequiredAttribute<wxString>( aDeviceSet, "name" );
1081 prefix = parseOptionalAttribute<wxString>( aDeviceSet, "prefix" );
1082 uservalue = parseOptionalAttribute<bool>( aDeviceSet, "uservalue" );
1083
1084 /* Russell: Parsing of devices and gates moved to sch_eagle_plugin.cpp
1085 *
1086 //TODO: description
1087
1088 NODE_MAP aDeviceSetChildren = MapChildren(aDeviceSet);
1089 wxXmlNode* deviceNode = getChildrenNodes(aDeviceSetChildren, "device");
1090
1091 while(deviceNode){
1092 devices.push_back(EDEVICE(deviceNode));
1093 deviceNode->GetNext();
1094 }
1095
1096 wxXmlNode* gateNode = getChildrenNodes(aDeviceSetChildren, "gate");
1097
1098 while(gateNode){
1099 gates.push_back(EGATE(gateNode));
1100 gateNode->GetNext();
1101 }
1102 */
1103
1104 }
1105
1106
ECLASS(wxXmlNode * aClass)1107 ECLASS::ECLASS( wxXmlNode* aClass )
1108 {
1109 number = parseRequiredAttribute<wxString>( aClass, "number" );
1110 name = parseRequiredAttribute<wxString>( aClass, "name" );
1111
1112 for( wxXmlNode* child = aClass->GetChildren(); child; child = child->GetNext() )
1113 {
1114 if( child->GetName() == "clearance" )
1115 {
1116 wxString to = parseRequiredAttribute<wxString>( child, "class" );
1117 ECOORD value = parseRequiredAttribute<ECOORD>( child, "value" );
1118
1119 clearanceMap[to] = value;
1120 }
1121 }
1122 }
1123