From a16bdb72887b0dfe59ed89d84eb2694a0d2ca55b Mon Sep 17 00:00:00 2001 From: Alex Shvartzkop Date: Sun, 15 Oct 2023 01:05:03 +0300 Subject: [PATCH] ADDED: Support EAGLE libraries directly in Symbol Library Table. Fixes https://gitlab.com/kicad/code/kicad/-/issues/2214 --- common/plugins/eagle/eagle_parser.cpp | 31 +++ common/plugins/eagle/eagle_parser.h | 3 + .../sch_plugins/eagle/sch_eagle_plugin.cpp | 212 +++++++++++++++--- eeschema/sch_plugins/eagle/sch_eagle_plugin.h | 27 ++- pcbnew/plugins/eagle/eagle_plugin.cpp | 5 +- 5 files changed, 240 insertions(+), 38 deletions(-) diff --git a/common/plugins/eagle/eagle_parser.cpp b/common/plugins/eagle/eagle_parser.cpp index f4cd57b3eb..1ce20caebd 100644 --- a/common/plugins/eagle/eagle_parser.cpp +++ b/common/plugins/eagle/eagle_parser.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -145,6 +146,36 @@ bool substituteVariable( wxString* aText ) } +wxString convertDescription( wxString aDescr ) +{ + aDescr.Replace( wxS( "\n" ), wxS( " " ) ); + aDescr.Replace( wxS( "\r" ), wxEmptyString ); + + wxRegEx( wxS( "]*?\\s+)?href=\"([^\"]*)\"[^>]*>" ) ) + .ReplaceAll( &aDescr, wxS( "\\1 " ) ); + + aDescr.Replace( wxS( "

" ), wxS( "\n\n" ) ); + aDescr.Replace( wxS( "

" ), wxS( "\n\n" ) ); + + aDescr.Replace( wxS( "
" ), wxS( "\n" ) ); + aDescr.Replace( wxS( "
    " ), wxS( "\n" ) ); + aDescr.Replace( wxS( "
" ), wxS( "\n\n" ) ); + aDescr.Replace( wxS( "
  • " ), wxS( "\n" ) ); + aDescr.Replace( wxS( "
  • " ), wxS( "\n \u2022 " ) ); // Bullet point + + aDescr = RemoveHTMLTags( aDescr ); + + wxRegEx( wxS( "\n +" ) ).ReplaceAll( &aDescr, wxS( "\n" ) ); + wxRegEx( wxS( " +\n" ) ).ReplaceAll( &aDescr, wxS( "\n" ) ); + + wxRegEx( wxS( "\n{3,}" ) ).ReplaceAll( &aDescr, wxS( "\n\n" ) ); + wxRegEx( wxS( "^\n+" ) ).ReplaceAll( &aDescr, wxEmptyString ); + wxRegEx( wxS( "\n+$" ) ).ReplaceAll( &aDescr, wxEmptyString ); + + return aDescr; +} + + template<> template<> OPTIONAL_XML_ATTRIBUTE::OPTIONAL_XML_ATTRIBUTE( wxString aData ) { diff --git a/common/plugins/eagle/eagle_parser.h b/common/plugins/eagle/eagle_parser.h index a30c4a85d5..a5ba22cd6f 100644 --- a/common/plugins/eagle/eagle_parser.h +++ b/common/plugins/eagle/eagle_parser.h @@ -59,6 +59,9 @@ 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 ); diff --git a/eeschema/sch_plugins/eagle/sch_eagle_plugin.cpp b/eeschema/sch_plugins/eagle/sch_eagle_plugin.cpp index bf1739bec7..d66b6d56e8 100644 --- a/eeschema/sch_plugins/eagle/sch_eagle_plugin.cpp +++ b/eeschema/sch_plugins/eagle/sch_eagle_plugin.cpp @@ -421,24 +421,7 @@ SCH_SHEET* SCH_EAGLE_PLUGIN::LoadSchematicFile( const wxString& aFileName, SCHEM } // Load the document - wxXmlDocument xmlDocument; - wxFFileInputStream stream( m_filename.GetFullPath() ); - - // read first line to check for Eagle XML format file - wxTextInputStream text( stream ); - wxString line = text.ReadLine(); - if( !line.StartsWith( wxT( " deleter( aAppendToMe ? nullptr : m_rootSheet ); @@ -527,6 +510,144 @@ SCH_SHEET* SCH_EAGLE_PLUGIN::LoadSchematicFile( const wxString& aFileName, SCHEM } +void SCH_EAGLE_PLUGIN::EnumerateSymbolLib( wxArrayString& aSymbolNameList, + const wxString& aLibraryPath, + const STRING_UTF8_MAP* aProperties ) +{ + m_filename = aLibraryPath; + m_libName = m_filename.GetName(); + + ensureLoadedLibrary( aLibraryPath ); + + auto it = m_eagleLibs.find( m_libName ); + + if( it != m_eagleLibs.end() ) + { + for( const auto& [symName, libSymbol] : it->second.KiCadSymbols ) + aSymbolNameList.push_back( symName ); + } +} + + +void SCH_EAGLE_PLUGIN::EnumerateSymbolLib( std::vector& aSymbolList, + const wxString& aLibraryPath, + const STRING_UTF8_MAP* aProperties ) +{ + m_filename = aLibraryPath; + m_libName = m_filename.GetName(); + + ensureLoadedLibrary( aLibraryPath ); + + auto it = m_eagleLibs.find( m_libName ); + + if( it != m_eagleLibs.end() ) + { + for( const auto& [symName, libSymbol] : it->second.KiCadSymbols ) + aSymbolList.push_back( libSymbol ); + } +} + + +LIB_SYMBOL* SCH_EAGLE_PLUGIN::LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName, + const STRING_UTF8_MAP* aProperties ) +{ + m_filename = aLibraryPath; + m_libName = m_filename.GetName(); + + ensureLoadedLibrary( aLibraryPath ); + + auto it = m_eagleLibs.find( m_libName ); + + if( it != m_eagleLibs.end() ) + { + auto it2 = it->second.KiCadSymbols.find( aAliasName ); + + if( it2 != it->second.KiCadSymbols.end() ) + return it2->second; + } + + return nullptr; +} + + +long long SCH_EAGLE_PLUGIN::getLibraryTimestamp( const wxString& aLibraryPath ) const +{ + wxFileName fn( aLibraryPath ); + + if( fn.IsFileReadable() && fn.GetModificationTime().IsValid() ) + return fn.GetModificationTime().GetValue().GetValue(); + else + return wxDateTime( 0.0 ).GetValue().GetValue(); +} + + +void SCH_EAGLE_PLUGIN::ensureLoadedLibrary( const wxString& aLibraryPath ) +{ + if( m_eagleLibs.find( m_libName ) != m_eagleLibs.end() ) + { + wxCHECK( m_timestamps.count( m_libName ), /*void*/ ); + + if( m_timestamps.at( m_libName ) == getLibraryTimestamp( aLibraryPath ) ) + return; + } + + LOCALE_IO toggle; // toggles on, then off, the C locale. + + if( m_progressReporter ) + { + m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aLibraryPath ) ); + + if( !m_progressReporter->KeepRefreshing() ) + THROW_IO_ERROR( ( "Open canceled by user." ) ); + } + + // Load the document + wxXmlDocument xmlDocument = loadXmlDocument( m_filename.GetFullPath() ); + + // Retrieve the root as current node + wxXmlNode* currentNode = xmlDocument.GetRoot(); + + // If the attribute is found, store the Eagle version; + // otherwise, store the dummy "0.0" version. + m_version = currentNode->GetAttribute( wxT( "version" ), wxT( "0.0" ) ); + + // Map all children into a readable dictionary + NODE_MAP children = MapChildren( currentNode ); + + // Load drawing + loadDrawing( children["drawing"] ); + + // Remember timestamp + m_timestamps[m_libName] = getLibraryTimestamp( aLibraryPath ); +} + + +wxXmlDocument SCH_EAGLE_PLUGIN::loadXmlDocument( const wxString& aFileName ) +{ + wxXmlDocument xmlDocument; + wxFFileInputStream stream( m_filename.GetFullPath() ); + + // read first line to check for Eagle XML format file + wxTextInputStream text( stream ); + wxString line = text.ReadLine(); + + if( !line.StartsWith( wxT( "GetContent() ) ); // For each device in the device set: while( deviceNode ) @@ -1939,26 +2073,42 @@ EAGLE_LIBRARY* SCH_EAGLE_PLUGIN::loadLibrary( wxXmlNode* aLibraryNode, // Don't set the footprint field if no package is defined in the Eagle schematic. if( edevice.package ) { - // assume that footprint library is identical to project name - wxString packageString = m_schematic->Prj().GetProjectName() + wxT( ":" ) + - aEagleLibrary->package[symbolName]; + wxString libName; + + if( m_schematic ) + { + // assume that footprint library is identical to project name + libName = m_schematic->Prj().GetProjectName(); + } + else + { + libName = m_libName; + } + + wxString packageString = libName + wxT( ":" ) + aEagleLibrary->package[symbolName]; + libSymbol->GetFootprintField().SetText( packageString ); } wxString libName = libSymbol->GetName(); libSymbol->SetName( libName ); + libSymbol->SetDescription( deviceSetDescr ); - // If duplicate symbol names exist in multiple Eagle symbol libraries, prefix the - // Eagle symbol library name to the symbol which should ensure that it is unique. - if( m_pi->LoadSymbol( getLibFileName().GetFullPath(), libName ) ) + if( m_pi ) { - libName = aEagleLibrary->name + wxT( "_" ) + libName; - libName = EscapeString( libName, CTX_LIBID ); - libSymbol->SetName( libName ); + // If duplicate symbol names exist in multiple Eagle symbol libraries, prefix the + // Eagle symbol library name to the symbol which should ensure that it is unique. + if( m_pi->LoadSymbol( getLibFileName().GetFullPath(), libName ) ) + { + libName = aEagleLibrary->name + wxT( "_" ) + libName; + libName = EscapeString( libName, CTX_LIBID ); + libSymbol->SetName( libName ); + } + + m_pi->SaveSymbol( getLibFileName().GetFullPath(), + new LIB_SYMBOL( *libSymbol.get() ), m_properties.get() ); } - m_pi->SaveSymbol( getLibFileName().GetFullPath(), new LIB_SYMBOL( *libSymbol.get() ), - m_properties.get() ); aEagleLibrary->KiCadSymbols.insert( libName, libSymbol.release() ); // Store information on whether the value of VALUE_FIELD for a part should be diff --git a/eeschema/sch_plugins/eagle/sch_eagle_plugin.h b/eeschema/sch_plugins/eagle/sch_eagle_plugin.h index 39be268cfc..537cc0176f 100644 --- a/eeschema/sch_plugins/eagle/sch_eagle_plugin.h +++ b/eeschema/sch_plugins/eagle/sch_eagle_plugin.h @@ -100,10 +100,10 @@ public: return PLUGIN_FILE_DESC( _HKI( "Eagle XML schematic files" ), { "sch" } ); } - /*const PLUGIN_FILE_DESC GetLibraryFileDesc() const override + const PLUGIN_FILE_DESC GetLibraryFileDesc() const override { return PLUGIN_FILE_DESC( _HKI( "Eagle XML library files" ), { "lbr" } ); - }*/ + } bool CanReadSchematicFile( const wxString& aFileName ) const override; bool CanReadLibrary( const wxString& aFileName ) const override; @@ -114,10 +114,24 @@ public: SCH_SHEET* aAppendToMe = nullptr, const STRING_UTF8_MAP* aProperties = nullptr ) override; + void EnumerateSymbolLib( wxArrayString& aSymbolNameList, const wxString& aLibraryPath, + const STRING_UTF8_MAP* aProperties ) override; + + void EnumerateSymbolLib( std::vector& aSymbolList, const wxString& aLibraryPath, + const STRING_UTF8_MAP* aProperties ) override; + + LIB_SYMBOL* LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName, + const STRING_UTF8_MAP* aProperties ) override; + + bool IsSymbolLibWritable( const wxString& aLibraryPath ) override { return false; } + private: void checkpoint(); - bool checkHeader( const wxString& aFileName ) const; + bool checkHeader( const wxString& aFileName ) const; + wxXmlDocument loadXmlDocument( const wxString& aFileName ); + long long getLibraryTimestamp( const wxString& aLibraryPath ) const; + void ensureLoadedLibrary( const wxString& aLibraryPath ); void loadDrawing( wxXmlNode* aDrawingNode ); void loadLayerDefs( wxXmlNode* aLayers ); @@ -239,9 +253,10 @@ private: wxString m_libName; ///< Library name to save symbols SCHEMATIC* m_schematic; ///< Passed to Load(), the schematic object being loaded - EPART_MAP m_partlist; - std::map m_eagleLibs; - std::unordered_map m_userValue; ///< deviceset/@uservalue for device. + EPART_MAP m_partlist; + std::map m_timestamps; + std::map m_eagleLibs; + std::unordered_map m_userValue; ///< deviceset/@uservalue for device. SCH_PLUGIN::SCH_PLUGIN_RELEASER m_pi; ///< PI to create KiCad symbol library. std::unique_ptr m_properties; ///< Library plugin properties. diff --git a/pcbnew/plugins/eagle/eagle_plugin.cpp b/pcbnew/plugins/eagle/eagle_plugin.cpp index 2a86073a90..3eac80550c 100644 --- a/pcbnew/plugins/eagle/eagle_plugin.cpp +++ b/pcbnew/plugins/eagle/eagle_plugin.cpp @@ -1786,7 +1786,10 @@ FOOTPRINT* EAGLE_PLUGIN::makeFootprint( wxXmlNode* aPackage, const wxString& aPk const wxString& itemName = packageItem->GetName(); if( itemName == wxT( "description" ) ) - m->SetLibDescription( packageItem->GetNodeContent() ); + { + wxString descr = convertDescription( UnescapeHTML( packageItem->GetNodeContent() ) ); + m->SetLibDescription( descr ); + } else if( itemName == wxT( "wire" ) ) packageWire( m.get(), packageItem ); else if( itemName == wxT( "pad" ) )