/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2012-2023 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2017 CERN * * @author Alejandro GarcĂ­a Montoro * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #ifndef _EAGLE_PARSER_H_ #define _EAGLE_PARSER_H_ #include #include #include #include #include #include #include #include #include class FOOTPRINT; struct EINSTANCE; struct EPART; struct ETEXT; typedef std::unordered_map NODE_MAP; typedef std::map EINSTANCE_MAP; typedef std::map> EPART_MAP; ///< Translates Eagle special characters to their counterparts in KiCad. wxString escapeName( const wxString& aNetName ); ///< Interprets special characters in Eagle text and converts them to KiCAD notation. wxString interpretText( const wxString& aText ); ///< Translates Eagle special text reference to a KiCad variable reference bool substituteVariable( wxString* aText ); ///< Converts Eagle's HTML description into KiCad description format wxString convertDescription( wxString aDescr ); static inline wxXmlNode* getChildrenNodes( NODE_MAP& aMap, const wxString& aName ) { auto it = aMap.find( aName ); return it == aMap.end() ? nullptr : it->second->GetChildren(); } /** * Implement a simple wrapper around runtime_error to isolate the errors thrown by the * Eagle XML parser. */ struct XML_PARSER_ERROR : std::runtime_error { /** * Build an XML error by just calling its parent class constructor, std::runtime_error, with * the passed message. * * @param aMessage is an explanatory error message. */ XML_PARSER_ERROR( const wxString& aMessage ) noexcept : std::runtime_error( "XML parser failed - " + aMessage.ToStdString() ) {} }; /// segment (element) of our XPATH into the Eagle XML document tree in PTREE form. struct TRIPLET { const char* element; const char* attribute; const char* value; TRIPLET( const char* aElement, const char* aAttribute = "", const char* aValue = "" ) : element( aElement ), attribute( aAttribute ), value( aValue ) {} }; /** * Keep track of what we are working on within a PTREE. * * Then if an exception is thrown, the place within the tree that gave us * grief can be reported almost accurately. To minimally impact * speed, merely assign const char* pointers during the tree walking * expedition. The const char* pointers must be to C strings residing either in * the data or code segment (i.e. "compiled in") or within the XML document, but * not on the stack, since the stack is unwound during the throwing of the * exception. The XML document will not immediately vanish since we capture * the xpath (using function Contents()) before the XML document tree (PTREE) * is destroyed. */ class XPATH { std::vector p; public: void push( const char* aPathSegment, const char* aAttribute="" ) { p.emplace_back( aPathSegment, aAttribute ); } void clear() { p.clear(); } void pop() { p.pop_back(); } /// modify the last path node's value void Value( const char* aValue ) { p.back().value = aValue; } /// modify the last path node's attribute void Attribute( const char* aAttribute ) { p.back().attribute = aAttribute; } /// return the contents of the XPATH as a single string wxString Contents() { typedef std::vector::const_iterator CITER_TRIPLET; wxString ret; for( CITER_TRIPLET it = p.begin(); it != p.end(); ++it ) { if( it != p.begin() ) ret += '.'; ret += it->element; if( it->attribute[0] && it->value[0] ) { ret += '['; ret += it->attribute; ret += '='; ret += it->value; ret += ']'; } } return ret; } }; /** * Convert a wxString to a generic type T. * * @param aValue is a wxString containing the value that will be converted to type T. * @throw XML_PARSER_ERROR - an exception is thrown if the parsing fails or if the conversion to * type T is unknown. */ template T Convert( const wxString& aValue ) { throw XML_PARSER_ERROR( "Conversion failed. Unknown type." ); } template <> wxString Convert( const wxString& aValue ); /** * Model an optional XML attribute. * * This was implemented as an alternative to std::optional. This class should be replaced with a * simple typedef per type using std::optional when C++17 is published. */ template class OPTIONAL_XML_ATTRIBUTE { private: /// A boolean indicating if the data is present or not. bool m_isAvailable; /// The actual data if m_isAvailable is true; otherwise, garbage. T m_data; public: /** * Construct a default OPTIONAL_XML_ATTRIBUTE, whose data is not available. */ OPTIONAL_XML_ATTRIBUTE() : m_isAvailable( false ), m_data( T() ) {} /** * @param aData is a wxString containing the value that should be converted to type T. If * aData is empty, the attribute is understood as unavailable; otherwise, the * conversion to T is tried. */ OPTIONAL_XML_ATTRIBUTE( const wxString& aData ) { m_data = T(); m_isAvailable = !aData.IsEmpty(); if( m_isAvailable ) Set( aData ); } /** * @param aData is the value of the XML attribute. If this constructor is called, the * attribute is available. */ template OPTIONAL_XML_ATTRIBUTE( T aData ) : m_isAvailable( true ), m_data( aData ) {} /** * @return bool the availability of the attribute. */ operator bool() const { return m_isAvailable; } /** * Assign to a string (optionally) containing the data. * * @param aData is a wxString that should be converted to T. If the string is empty, the * attribute is set to unavailable. */ OPTIONAL_XML_ATTRIBUTE& operator =( const wxString& aData ) { m_isAvailable = !aData.IsEmpty(); if( m_isAvailable ) Set( aData ); return *this; } /** * Assign to an object of the base type containing the data. * * @param aData is the actual value of the attribute. Calling this assignment, the attribute * is automatically made available. */ OPTIONAL_XML_ATTRIBUTE& operator =( T aData ) { m_data = aData; m_isAvailable = true; return *this; } /** * @param aOther is the object of the base type that should be compared with this one. */ bool operator ==( const T& aOther ) const { return m_isAvailable && ( aOther == m_data ); } /** * Attempt to convert a string to the base type. * * @param aString is the string that will be converted to the base type. */ void Set( const wxString& aString ) { m_data = Convert( aString ); m_isAvailable = !aString.IsEmpty(); } /** * Return a reference to the value of the attribute assuming it is available. * * @return T& - the value of the attribute. */ T& Get() { assert( m_isAvailable ); return m_data; } /** * Return a constant reference to the value of the attribute assuming it is available. * * @return const T& - the value of the attribute. */ const T& CGet() const { assert( m_isAvailable ); return m_data; } /** * Return a reference to the value of the attribute assuming it is available. * * @return T& - the value of the attribute. */ T& operator*() { return Get(); } /** * Return a constant reference to the value of the attribute assuming it is available. * * @return const T& - the value of the attribute. */ const T& operator*() const { return CGet(); } /** * Return a pointer to the value of the attribute assuming it is available. * * @return T* - the value of the attribute. */ T* operator->() { return &Get(); } /** * Return a constant pointer to the value of the attribute assuming it is available. * * @return const T* - the value of the attribute. */ const T* operator->() const { return &CGet(); } }; /** * Provide an easy access to the children of an XML node via their names. * * @param currentNode is a pointer to a wxXmlNode, whose children will be mapped. * @return NODE_MAP is a map linking the name of each children to the children itself (via a * wxXmlNode*) */ NODE_MAP MapChildren( wxXmlNode* aCurrentNode ); ///< Convert an Eagle curve end to a KiCad center for S_ARC VECTOR2I ConvertArcCenter( const VECTOR2I& aStart, const VECTOR2I& aEnd, double aAngle ); // Pre-declare for typedefs struct EROT; struct ECOORD; typedef OPTIONAL_XML_ATTRIBUTE opt_wxString; typedef OPTIONAL_XML_ATTRIBUTE opt_int; typedef OPTIONAL_XML_ATTRIBUTE opt_double; typedef OPTIONAL_XML_ATTRIBUTE opt_bool; typedef OPTIONAL_XML_ATTRIBUTE opt_erot; typedef OPTIONAL_XML_ATTRIBUTE opt_ecoord; // All of the 'E'STRUCTS below merely hold Eagle XML information verbatim, in binary. // For maintenance and troubleshooting purposes, it was thought that we'd need to // separate the conversion process into distinct steps. There is no intent to have KiCad // forms of information in these 'E'STRUCTS. They are only binary forms // of the Eagle information in the corresponding Eagle XML nodes. // Eagle coordinates struct ECOORD { enum EAGLE_UNIT { EU_NM, ///< nanometers EU_MM, ///< millimeters EU_INCH, ///< inches EU_MIL, ///< mils/thous }; ///< Value expressed in nanometers long long int value; ///< Unit used for the value field static constexpr EAGLE_UNIT ECOORD_UNIT = EU_NM; ECOORD() : value( 0 ) { } ECOORD( int aValue, enum EAGLE_UNIT aUnit ) : value( ConvertToNm( aValue, aUnit ) ) { } ECOORD( const wxString& aValue, enum EAGLE_UNIT aUnit ); int ToMils() const { return value / 25400; } int To100NanoMeters() const { return value / 100; } int ToNanoMeters() const { return value; } float ToMm() const { return value / 1000000.0; } int ToSchUnits() const { return To100NanoMeters(); } int ToPcbUnits() const { return ToNanoMeters(); } ECOORD operator+( const ECOORD& aOther ) const { return ECOORD( value + aOther.value, ECOORD_UNIT ); } ECOORD operator-( const ECOORD& aOther ) const { return ECOORD( value - aOther.value, ECOORD_UNIT ); } bool operator==( const ECOORD& aOther ) const { return value == aOther.value; } ///< Converts a size expressed in a certain unit to nanometers. static long long int ConvertToNm( int aValue, enum EAGLE_UNIT aUnit ); }; /// Eagle net struct ENET { int netcode; wxString netname; ENET( int aNetCode, const wxString& aNetName ) : netcode( aNetCode ), netname( aNetName ) {} ENET() : netcode( 0 ) {} }; /// Eagle rotation struct EROT { bool mirror; bool spin; double degrees; EROT() : mirror( false ), spin( false ), degrees( 0 ) {} EROT( double aDegrees ) : mirror( false ), spin( false ), degrees( aDegrees ) {} }; /// Eagle wire struct EWIRE { ECOORD x1; ECOORD y1; ECOORD x2; ECOORD y2; ECOORD width; int layer; // for style: (continuous | longdash | shortdash | dashdot) enum { CONTINUOUS, LONGDASH, SHORTDASH, DASHDOT }; opt_int style; opt_double curve; ///< range is -359.9..359.9 // for cap: (flat | round) enum { FLAT, ROUND }; opt_int cap; EWIRE( wxXmlNode* aWire ); }; /// Eagle Junction struct EJUNCTION { ECOORD x; ECOORD y; EJUNCTION( wxXmlNode* aJunction); }; /// Eagle label struct ELABEL { ECOORD x; ECOORD y; ECOORD size; int layer; opt_erot rot; opt_wxString xref; wxString netname; ELABEL( wxXmlNode* aLabel, const wxString& aNetName ); }; /// Eagle via struct EVIA { ECOORD x; ECOORD y; int layer_front_most; /// < extent int layer_back_most; /// < inclusive ECOORD drill; opt_ecoord diam; opt_wxString shape; EVIA( wxXmlNode* aVia ); }; /// Eagle circle struct ECIRCLE { ECOORD x; ECOORD y; ECOORD radius; ECOORD width; int layer; ECIRCLE( wxXmlNode* aCircle ); }; /// Eagle XML rectangle in binary struct ERECT { ECOORD x1; ECOORD y1; ECOORD x2; ECOORD y2; int layer; opt_erot rot; ERECT( wxXmlNode* aRect ); }; /** * Parse an Eagle "attribute" XML element. * * @note An attribute element is different than an XML element attribute. The attribute element * is a full XML node in and of itself, and has attributes of its own. Blame Eagle. */ struct EATTR { wxString name; opt_wxString value; opt_ecoord x; opt_ecoord y; opt_ecoord size; opt_int layer; opt_double ratio; opt_erot rot; enum { // for 'display' Off, VALUE, NAME, BOTH, }; opt_int display; opt_int align; EATTR( wxXmlNode* aTree ); EATTR() {} }; /// Eagle dimension element struct EDIMENSION { ECOORD x1; ECOORD y1; ECOORD x2; ECOORD y2; ECOORD x3; ECOORD y3; opt_ecoord textsize; int layer; opt_wxString dimensionType; EDIMENSION( wxXmlNode* aDimension ); }; /// Eagle text element struct ETEXT { wxString text; ECOORD x; ECOORD y; ECOORD size; int layer; opt_wxString font; opt_double ratio; opt_erot rot; enum { // for align CENTER = 0, CENTER_LEFT, TOP_CENTER, TOP_LEFT, TOP_RIGHT, // opposites are -1 x above, used by code tricks in here CENTER_RIGHT = -CENTER_LEFT, BOTTOM_CENTER = -TOP_CENTER, BOTTOM_LEFT = -TOP_RIGHT, BOTTOM_RIGHT = -TOP_LEFT, }; opt_int align; ETEXT( wxXmlNode* aText ); /// Calculate text size based on font type and size VECTOR2I ConvertSize() const; }; /** * Parse an Eagle frame element. */ struct EFRAME { ECOORD x1; ECOORD y1; ECOORD x2; ECOORD y2; int columns; int rows; int layer; opt_bool border_left; opt_bool border_top; opt_bool border_right; opt_bool border_bottom; EFRAME( wxXmlNode* aFrameNode ); }; /// Structure holding common properties for through-hole and SMD pads struct EPAD_COMMON { wxString name; ECOORD x, y; opt_erot rot; opt_bool stop; opt_bool thermals; EPAD_COMMON( wxXmlNode* aPad ); }; /// Eagle thru hole pad struct EPAD : public EPAD_COMMON { ECOORD drill; opt_ecoord diameter; // for shape: (square | round | octagon | long | offset) enum { UNDEF = -1, SQUARE, ROUND, OCTAGON, LONG, OFFSET, }; opt_int shape; opt_bool first; EPAD( wxXmlNode* aPad ); }; /// Eagle SMD pad struct ESMD : public EPAD_COMMON { ECOORD dx; ECOORD dy; int layer; opt_int roundness; opt_bool cream; ESMD( wxXmlNode* aSMD ); }; /// Eagle pin element struct EPIN { wxString name; ECOORD x; ECOORD y; opt_wxString visible; opt_wxString length; opt_wxString direction; opt_wxString function; opt_int swaplevel; opt_erot rot; EPIN( wxXmlNode* aPin ); }; /// Eagle vertex struct EVERTEX { ECOORD x; ECOORD y; opt_double curve; ///< range is -359.9..359.9 EVERTEX( wxXmlNode* aVertex ); }; /// Eagle polygon, without vertices which are parsed as needed struct EPOLYGON { ECOORD width; int layer; opt_ecoord spacing; // KiCad priority is opposite of Eagle rank, that is: // - Eagle Low rank drawn first // - KiCad high priority drawn first // So since Eagle has an upper limit we define this, used for the cases // where no rank is specified. static const int max_priority = 6; enum { // for pour SOLID, HATCH, CUTOUT, }; int pour; opt_ecoord isolate; opt_bool orphans; opt_bool thermals; opt_int rank; EPOLYGON( wxXmlNode* aPolygon ); }; /// Eagle hole element struct EHOLE { ECOORD x; ECOORD y; ECOORD drill; EHOLE( wxXmlNode* aHole ); }; /// Eagle element element struct EELEMENT { wxString name; wxString library; wxString package; wxString value; ECOORD x; ECOORD y; opt_bool locked; opt_bool smashed; opt_erot rot; EELEMENT( wxXmlNode* aElement ); }; struct ELAYER { int number; wxString name; int color; int fill; opt_bool visible; opt_bool active; ELAYER( wxXmlNode* aLayer ); }; struct EAGLE_LAYER { enum { TOP = 1, ROUTE2 = 2, ROUTE3 = 3, ROUTE4 = 4, ROUTE5 = 5, ROUTE6 = 6, ROUTE7 = 7, ROUTE8 = 8, ROUTE9 = 9, ROUTE10 = 10, ROUTE11 = 11, ROUTE12 = 12, ROUTE13 = 13, ROUTE14 = 14, ROUTE15 = 15, BOTTOM = 16, PADS = 17, VIAS = 18, UNROUTED = 19, DIMENSION = 20, TPLACE = 21, BPLACE = 22, TORIGINS = 23, BORIGINS = 24, TNAMES = 25, BNAMES = 26, TVALUES = 27, BVALUES = 28, TSTOP = 29, BSTOP = 30, TCREAM = 31, BCREAM = 32, TFINISH = 33, BFINISH = 34, TGLUE = 35, BGLUE = 36, TTEST = 37, BTEST = 38, TKEEPOUT = 39, BKEEPOUT = 40, TRESTRICT = 41, BRESTRICT = 42, VRESTRICT = 43, DRILLS = 44, HOLES = 45, MILLING = 46, MEASURES = 47, DOCUMENT = 48, REFERENCELC = 49, REFERENCELS = 50, TDOCU = 51, BDOCU = 52, NETS = 91, BUSSES = 92, PINS = 93, SYMBOLS = 94, NAMES = 95, VALUES = 96, INFO = 97, GUIDE = 98, USERLAYER1 = 160, USERLAYER2 = 161 }; }; struct EPART { /* * * */ wxString name; wxString library; wxString deviceset; wxString device; opt_wxString technology; opt_wxString value; std::map attribute; std::map variant; EPART( wxXmlNode* aPart ); }; struct EINSTANCE { /* * * */ wxString part; wxString gate; ECOORD x; ECOORD y; opt_bool smashed; opt_erot rot; EINSTANCE( wxXmlNode* aInstance ); }; struct EGATE { /* * * */ wxString name; wxString symbol; ECOORD x; ECOORD y; opt_int addlevel; opt_int swaplevel; enum { MUST, CAN, NEXT, REQUEST, ALWAYS }; EGATE( wxXmlNode* aGate ); }; struct ECONNECT { /* * * */ wxString gate; wxString pin; wxString pad; //int contactroute; // TODO ECONNECT( wxXmlNode* aConnect ); }; struct EDEVICE { /* * * */ wxString name; opt_wxString package; std::vector connects; EDEVICE( wxXmlNode* aDevice ); }; struct EDEVICE_SET { /* */ wxString name; opt_wxString prefix; opt_bool uservalue; //std::vector devices; //std::vector gates; EDEVICE_SET( wxXmlNode* aDeviceSet ); }; struct ECLASS { wxString number; wxString name; std::map clearanceMap; ECLASS( wxXmlNode* aClass ); }; #endif // _EAGLE_PARSER_H_