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