Improvements to Altium PCB/footprint importer:
- Support unicode footprint names
- Set default footprint text size and thickness to match KLC
- Position Reference at the top, Value at the bottom of fp bounding box
(cherry picked from commit 24452f41ad
)
This commit is contained in:
parent
acc03e91f3
commit
4918ac6307
|
@ -2,6 +2,7 @@
|
|||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2019-2020 Thomas Pointhuber <thomas.pointhuber@gmx.at>
|
||||
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -88,26 +89,106 @@ ALTIUM_COMPOUND_FILE::ALTIUM_COMPOUND_FILE( const wxString& aFilePath )
|
|||
}
|
||||
|
||||
|
||||
static const CFB::COMPOUND_FILE_ENTRY*
|
||||
FindStreamSingleLevel( const CFB::CompoundFileReader& aReader,
|
||||
const CFB::COMPOUND_FILE_ENTRY* aEntry, const std::string aName,
|
||||
const bool aIsStream )
|
||||
std::map<wxString, wxString> ALTIUM_COMPOUND_FILE::ListLibFootprints() const
|
||||
{
|
||||
std::map<wxString, wxString> patternMap;
|
||||
|
||||
if( !m_reader )
|
||||
return patternMap;
|
||||
|
||||
const CFB::COMPOUND_FILE_ENTRY* root = m_reader->GetRootEntry();
|
||||
|
||||
if( !root )
|
||||
return patternMap;
|
||||
|
||||
m_reader->EnumFiles( root, 2,
|
||||
[&]( const CFB::COMPOUND_FILE_ENTRY* entry, const CFB::utf16string& dir,
|
||||
int level ) -> void
|
||||
{
|
||||
std::wstring dirName = UTF16ToWstring( dir.data(), dir.size() );
|
||||
std::wstring fileName = UTF16ToWstring( entry->name, entry->nameLen );
|
||||
|
||||
if( m_reader->IsStream( entry ) && fileName == L"Parameters" )
|
||||
{
|
||||
ALTIUM_PARSER parametersReader( *this, entry );
|
||||
std::map<wxString, wxString> parameterProperties =
|
||||
parametersReader.ReadProperties();
|
||||
|
||||
wxString key = ALTIUM_PARSER::ReadString(
|
||||
parameterProperties, wxT( "PATTERN" ), wxT( "" ) );
|
||||
|
||||
wxString fpName = ALTIUM_PARSER::ReadUnicodeString(
|
||||
parameterProperties, wxT( "PATTERN" ), wxT( "" ) );
|
||||
|
||||
patternMap.emplace( key, fpName );
|
||||
}
|
||||
} );
|
||||
|
||||
return patternMap;
|
||||
}
|
||||
|
||||
|
||||
wxString ALTIUM_COMPOUND_FILE::FindLibFootprintDirName( const wxString& aFpUnicodeName ) const
|
||||
{
|
||||
if( !m_reader )
|
||||
return wxEmptyString;
|
||||
|
||||
const CFB::COMPOUND_FILE_ENTRY* root = m_reader->GetRootEntry();
|
||||
|
||||
if( !root )
|
||||
return wxEmptyString;
|
||||
|
||||
wxString ret;
|
||||
|
||||
m_reader->EnumFiles( root, 2,
|
||||
[&]( const CFB::COMPOUND_FILE_ENTRY* entry, const CFB::utf16string& dir,
|
||||
int level ) -> void
|
||||
{
|
||||
std::wstring dirName = UTF16ToWstring( dir.data(), dir.size() );
|
||||
std::wstring fileName = UTF16ToWstring( entry->name, entry->nameLen );
|
||||
|
||||
if( m_reader->IsStream( entry ) && fileName == L"Parameters" )
|
||||
{
|
||||
ALTIUM_PARSER parametersReader( *this, entry );
|
||||
std::map<wxString, wxString> parameterProperties =
|
||||
parametersReader.ReadProperties();
|
||||
|
||||
wxString fpName = ALTIUM_PARSER::ReadUnicodeString(
|
||||
parameterProperties, wxT( "PATTERN" ), wxT( "" ) );
|
||||
|
||||
if( fpName == aFpUnicodeName )
|
||||
{
|
||||
ret = dirName;
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
const CFB::COMPOUND_FILE_ENTRY*
|
||||
ALTIUM_COMPOUND_FILE::FindStreamSingleLevel( const CFB::COMPOUND_FILE_ENTRY* aEntry,
|
||||
const std::string aName, const bool aIsStream ) const
|
||||
{
|
||||
if( !m_reader || !aEntry )
|
||||
return nullptr;
|
||||
|
||||
const CFB::COMPOUND_FILE_ENTRY* ret = nullptr;
|
||||
|
||||
aReader.EnumFiles( aEntry, 1,
|
||||
[&]( const CFB::COMPOUND_FILE_ENTRY* entry, const CFB::utf16string& dir,
|
||||
int level ) -> void
|
||||
{
|
||||
if( aReader.IsStream( entry ) == aIsStream )
|
||||
{
|
||||
std::string name = UTF16ToUTF8( entry->name );
|
||||
if( name == aName.c_str() )
|
||||
{
|
||||
ret = entry;
|
||||
}
|
||||
}
|
||||
} );
|
||||
m_reader->EnumFiles( aEntry, 1,
|
||||
[&]( const CFB::COMPOUND_FILE_ENTRY* entry, const CFB::utf16string& dir,
|
||||
int level ) -> void
|
||||
{
|
||||
if( m_reader->IsStream( entry ) == aIsStream )
|
||||
{
|
||||
std::string name = UTF16ToUTF8( entry->name );
|
||||
if( name == aName.c_str() )
|
||||
{
|
||||
ret = entry;
|
||||
}
|
||||
}
|
||||
} );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -116,9 +197,7 @@ const CFB::COMPOUND_FILE_ENTRY*
|
|||
ALTIUM_COMPOUND_FILE::FindStream( const std::vector<std::string>& aStreamPath ) const
|
||||
{
|
||||
if( !m_reader )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const CFB::COMPOUND_FILE_ENTRY* currentDirEntry = m_reader->GetRootEntry();
|
||||
|
||||
|
@ -129,12 +208,11 @@ ALTIUM_COMPOUND_FILE::FindStream( const std::vector<std::string>& aStreamPath )
|
|||
|
||||
if( ++it == aStreamPath.cend() )
|
||||
{
|
||||
return FindStreamSingleLevel( *m_reader.get(), currentDirEntry, name, true );
|
||||
return FindStreamSingleLevel( currentDirEntry, name, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
currentDirEntry =
|
||||
FindStreamSingleLevel( *m_reader.get(), currentDirEntry, name, false );
|
||||
currentDirEntry = FindStreamSingleLevel( currentDirEntry, name, false );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,9 +310,12 @@ std::map<wxString, wxString> ALTIUM_PARSER::ReadProperties()
|
|||
else
|
||||
value = wxString( valueS.c_str(), wxConvISO8859_1 );
|
||||
|
||||
// Breathless hack because I haven't a clue what the story is here (but this character
|
||||
// appears in a lot of radial dimensions and is rendered by Altium as a space).
|
||||
value.Replace( wxT( "ÿ" ), wxT( " " ) );
|
||||
if( canonicalKey != wxS( "PATTERN" ) && canonicalKey != wxS( "SOURCEFOOTPRINTLIBRARY" ) )
|
||||
{
|
||||
// Breathless hack because I haven't a clue what the story is here (but this character
|
||||
// appears in a lot of radial dimensions and is rendered by Altium as a space).
|
||||
value.Replace( wxT( "ÿ" ), wxT( " " ) );
|
||||
}
|
||||
|
||||
if( canonicalKey == wxT( "DESIGNATOR" )
|
||||
|| canonicalKey == wxT( "NAME" )
|
||||
|
@ -341,3 +422,28 @@ wxString ALTIUM_PARSER::ReadString( const std::map<wxString, wxString>& aProps,
|
|||
|
||||
return aDefault;
|
||||
}
|
||||
|
||||
|
||||
wxString ALTIUM_PARSER::ReadUnicodeString( const std::map<wxString, wxString>& aProps,
|
||||
const wxString& aKey, const wxString& aDefault )
|
||||
{
|
||||
const auto& unicodeFlag = aProps.find( wxS( "UNICODE" ) );
|
||||
|
||||
if( unicodeFlag != aProps.end() && unicodeFlag->second.Contains( wxS( "EXISTS" ) ) )
|
||||
{
|
||||
const auto& unicodeValue = aProps.find( wxString( "UNICODE__" ) + aKey );
|
||||
|
||||
if( unicodeValue != aProps.end() )
|
||||
{
|
||||
wxArrayString arr = wxSplit( unicodeValue->second, ',', '\0' );
|
||||
wxString out;
|
||||
|
||||
for( wxString part : arr )
|
||||
out += wxString( wchar_t( wxAtoi( part ) ) );
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
return ReadString( aProps, aKey, aDefault );
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2019-2020 Thomas Pointhuber <thomas.pointhuber@gmx.at>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* Copyright (C) 2020-2023 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -63,8 +63,16 @@ public:
|
|||
|
||||
const CFB::CompoundFileReader& GetCompoundFileReader() const { return *m_reader; }
|
||||
|
||||
std::map<wxString, wxString> ListLibFootprints() const;
|
||||
|
||||
wxString FindLibFootprintDirName( const wxString& aFpUnicodeName ) const;
|
||||
|
||||
const CFB::COMPOUND_FILE_ENTRY* FindStream( const std::vector<std::string>& aStreamPath ) const;
|
||||
|
||||
const CFB::COMPOUND_FILE_ENTRY* FindStreamSingleLevel( const CFB::COMPOUND_FILE_ENTRY* aEntry,
|
||||
const std::string aName,
|
||||
const bool aIsStream ) const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<CFB::CompoundFileReader> m_reader;
|
||||
std::vector<char> m_buffer;
|
||||
|
@ -107,24 +115,31 @@ public:
|
|||
return result;
|
||||
}
|
||||
|
||||
wxString ReadWxString()
|
||||
wxScopedCharBuffer ReadCharBuffer()
|
||||
{
|
||||
uint8_t len = Read<uint8_t>();
|
||||
if( GetRemainingBytes() >= len )
|
||||
{
|
||||
// TODO: Identify where the actual code page is stored. For now, this default code page
|
||||
// has limited impact, because recent Altium files come with a UTF16 string table
|
||||
wxString val = wxString( m_pos, wxConvISO8859_1, len );
|
||||
char* buf = new char[len];
|
||||
memcpy( buf, m_pos, len );
|
||||
m_pos += len;
|
||||
return val;
|
||||
|
||||
return wxScopedCharBuffer::CreateOwned( buf, len );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_error = true;
|
||||
return wxString( "" );
|
||||
return wxScopedCharBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
wxString ReadWxString()
|
||||
{
|
||||
// TODO: Identify where the actual code page is stored. For now, this default code page
|
||||
// has limited impact, because recent Altium files come with a UTF16 string table
|
||||
return wxString( ReadCharBuffer(), wxConvISO8859_1 );
|
||||
}
|
||||
|
||||
std::map<uint32_t, wxString> ReadWideStringTable()
|
||||
{
|
||||
std::map<uint32_t, wxString> table;
|
||||
|
@ -225,6 +240,9 @@ public:
|
|||
static wxString ReadString( const std::map<wxString, wxString>& aProps,
|
||||
const wxString& aKey, const wxString& aDefault );
|
||||
|
||||
static wxString ReadUnicodeString( const std::map<wxString, wxString>& aProps,
|
||||
const wxString& aKey, const wxString& aDefault );
|
||||
|
||||
void Skip( size_t aLength )
|
||||
{
|
||||
if( GetRemainingBytes() >= aLength )
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2019 Thomas Pointhuber <thomas.pointhuber@gmx.at>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* Copyright (C) 2020-2023 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -144,6 +144,9 @@ void ALTIUM_DESIGNER_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames
|
|||
|
||||
try
|
||||
{
|
||||
// Map code-page-dependent names to unicode names
|
||||
std::map<wxString, wxString> patternMap = altiumLibFile.ListLibFootprints();
|
||||
|
||||
const std::vector<std::string> streamName = { "Library", "Data" };
|
||||
const CFB::COMPOUND_FILE_ENTRY* libraryData = altiumLibFile.FindStream( streamName );
|
||||
if( libraryData == nullptr )
|
||||
|
@ -161,8 +164,20 @@ void ALTIUM_DESIGNER_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames
|
|||
for( size_t i = 0; i < numberOfFootprints; i++ )
|
||||
{
|
||||
parser.ReadAndSetSubrecordLength();
|
||||
wxString footprintName = parser.ReadWxString();
|
||||
aFootprintNames.Add( footprintName );
|
||||
|
||||
wxScopedCharBuffer charBuffer = parser.ReadCharBuffer();
|
||||
wxString fpPattern( charBuffer, wxConvISO8859_1 );
|
||||
|
||||
auto it = patternMap.find( fpPattern );
|
||||
if( it != patternMap.end() )
|
||||
{
|
||||
aFootprintNames.Add( it->second ); // Proper unicode name
|
||||
}
|
||||
else
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( "Component name not found: '%s'", fpPattern ) );
|
||||
}
|
||||
|
||||
parser.SkipSubrecord();
|
||||
}
|
||||
|
||||
|
|
|
@ -331,8 +331,10 @@ ACOMPONENT6::ACOMPONENT6( ALTIUM_PARSER& aReader )
|
|||
nameon = ALTIUM_PARSER::ReadBool( props, wxT( "NAMEON" ), true );
|
||||
commenton = ALTIUM_PARSER::ReadBool( props, wxT( "COMMENTON" ), false );
|
||||
sourcedesignator = ALTIUM_PARSER::ReadString( props, wxT( "SOURCEDESIGNATOR" ), wxT( "" ) );
|
||||
sourcefootprintlibrary = ALTIUM_PARSER::ReadString( props, wxT( "SOURCEFOOTPRINTLIBRARY" ), wxT( "" ) );
|
||||
pattern = ALTIUM_PARSER::ReadString( props, wxT( "PATTERN" ), wxT( "" ) );
|
||||
|
||||
sourcefootprintlibrary =
|
||||
ALTIUM_PARSER::ReadUnicodeString( props, wxT( "SOURCEFOOTPRINTLIBRARY" ), wxT( "" ) );
|
||||
pattern = ALTIUM_PARSER::ReadUnicodeString( props, wxT( "PATTERN" ), wxT( "" ) );
|
||||
|
||||
sourcecomponentlibrary = ALTIUM_PARSER::ReadString( props, wxT( "SOURCECOMPONENTLIBRARY" ), wxT( "" ) );
|
||||
sourcelibreference = ALTIUM_PARSER::ReadString( props, wxT( "SOURCELIBREFERENCE" ), wxT( "" ) );
|
||||
|
|
|
@ -666,6 +666,7 @@ FOOTPRINT* ALTIUM_PCB::ParseFootprint( const ALTIUM_COMPOUND_FILE& altiumLibFile
|
|||
|
||||
m_unicodeStrings.clear();
|
||||
m_extendedPrimitiveInformationMaps.clear();
|
||||
|
||||
// TODO: WideStrings are stored as parameterMap in the case of footprints, not as binary
|
||||
// std::string unicodeStringsStreamName = aFootprintName.ToStdString() + "\\WideStrings";
|
||||
// const CFB::COMPOUND_FILE_ENTRY* unicodeStringsData = altiumLibFile.FindStream( unicodeStringsStreamName );
|
||||
|
@ -674,7 +675,15 @@ FOOTPRINT* ALTIUM_PCB::ParseFootprint( const ALTIUM_COMPOUND_FILE& altiumLibFile
|
|||
// ParseWideStrings6Data( altiumLibFile, unicodeStringsData );
|
||||
// }
|
||||
|
||||
const std::vector<std::string> streamName{ aFootprintName.ToStdString(), "Data" };
|
||||
wxString fpDirName = altiumLibFile.FindLibFootprintDirName(aFootprintName);
|
||||
|
||||
if( fpDirName.IsEmpty() )
|
||||
{
|
||||
THROW_IO_ERROR(
|
||||
wxString::Format( _( "Footprint directory not found: '%s'." ), aFootprintName ) );
|
||||
}
|
||||
|
||||
const std::vector<std::string> streamName{ fpDirName.ToStdString(), "Data" };
|
||||
const CFB::COMPOUND_FILE_ENTRY* footprintData = altiumLibFile.FindStream( streamName );
|
||||
|
||||
if( footprintData == nullptr )
|
||||
|
@ -686,13 +695,13 @@ FOOTPRINT* ALTIUM_PCB::ParseFootprint( const ALTIUM_COMPOUND_FILE& altiumLibFile
|
|||
ALTIUM_PARSER parser( altiumLibFile, footprintData );
|
||||
|
||||
parser.ReadAndSetSubrecordLength();
|
||||
wxString footprintName = parser.ReadWxString();
|
||||
//wxString footprintName = parser.ReadWxString(); // Not used (single-byte char set)
|
||||
parser.SkipSubrecord();
|
||||
|
||||
LIB_ID fpID = AltiumToKiCadLibID( "", footprintName ); // TODO: library name
|
||||
LIB_ID fpID = AltiumToKiCadLibID( "", aFootprintName ); // TODO: library name
|
||||
footprint->SetFPID( fpID );
|
||||
|
||||
const std::vector<std::string> parametersStreamName{ aFootprintName.ToStdString(),
|
||||
const std::vector<std::string> parametersStreamName{ fpDirName.ToStdString(),
|
||||
"Parameters" };
|
||||
const CFB::COMPOUND_FILE_ENTRY* parametersData =
|
||||
altiumLibFile.FindStream( parametersStreamName );
|
||||
|
@ -713,7 +722,7 @@ FOOTPRINT* ALTIUM_PCB::ParseFootprint( const ALTIUM_COMPOUND_FILE& altiumLibFile
|
|||
}
|
||||
|
||||
const std::vector<std::string> extendedPrimitiveInformationStreamName{
|
||||
aFootprintName.ToStdString(), "ExtendedPrimitiveInformation", "Data"
|
||||
fpDirName.ToStdString(), "ExtendedPrimitiveInformation", "Data"
|
||||
};
|
||||
const CFB::COMPOUND_FILE_ENTRY* extendedPrimitiveInformationData =
|
||||
altiumLibFile.FindStream( extendedPrimitiveInformationStreamName );
|
||||
|
@ -722,10 +731,19 @@ FOOTPRINT* ALTIUM_PCB::ParseFootprint( const ALTIUM_COMPOUND_FILE& altiumLibFile
|
|||
ParseExtendedPrimitiveInformationData( altiumLibFile, extendedPrimitiveInformationData );
|
||||
|
||||
footprint->SetReference( wxT( "REF**" ) );
|
||||
footprint->SetValue( footprintName );
|
||||
footprint->SetValue( aFootprintName );
|
||||
footprint->Reference().SetVisible( true ); // TODO: extract visibility information
|
||||
footprint->Value().SetVisible( true );
|
||||
|
||||
const VECTOR2I defaultTextSize( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 1.0 ) );
|
||||
const int defaultTextThickness( pcbIUScale.mmToIU( 0.15 ) );
|
||||
|
||||
footprint->Reference().SetTextSize( defaultTextSize );
|
||||
footprint->Reference().SetTextThickness( defaultTextThickness );
|
||||
|
||||
footprint->Value().SetTextSize( defaultTextSize );
|
||||
footprint->Value().SetTextThickness( defaultTextThickness );
|
||||
|
||||
for( int primitiveIndex = 0; parser.GetRemainingBytes() >= 4; primitiveIndex++ )
|
||||
{
|
||||
ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( parser.Peek<uint8_t>() );
|
||||
|
@ -785,6 +803,22 @@ FOOTPRINT* ALTIUM_PCB::ParseFootprint( const ALTIUM_COMPOUND_FILE& altiumLibFile
|
|||
}
|
||||
}
|
||||
|
||||
// Auto-position reference and value
|
||||
BOX2I bbox = footprint.get()->GetBoundingBox( false, false );
|
||||
bbox.Inflate( pcbIUScale.mmToIU( 0.2 ) ); // Gap between graphics and text
|
||||
|
||||
if( footprint->Reference().GetPosition() == VECTOR2I( 0, 0 ) )
|
||||
{
|
||||
footprint->Reference().SetX( bbox.GetCenter().x );
|
||||
footprint->Reference().SetY( bbox.GetTop() - footprint->Reference().GetTextSize().y / 2 );
|
||||
}
|
||||
|
||||
if( footprint->Value().GetPosition() == VECTOR2I( 0, 0 ) )
|
||||
{
|
||||
footprint->Value().SetX( bbox.GetCenter().x );
|
||||
footprint->Value().SetY( bbox.GetBottom() + footprint->Value().GetTextSize().y / 2 );
|
||||
}
|
||||
|
||||
if( parser.HasParsingError() )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( wxT( "%s stream was not parsed correctly" ),
|
||||
|
@ -2158,7 +2192,7 @@ void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT*
|
|||
}
|
||||
else
|
||||
{
|
||||
PCB_SHAPE* shape = new PCB_SHAPE( aFootprint, SHAPE_T::POLY );
|
||||
FP_SHAPE* shape = new FP_SHAPE( aFootprint, SHAPE_T::POLY );
|
||||
|
||||
shape->SetPolyShape( polySet );
|
||||
shape->SetFilled( true );
|
||||
|
|
Loading…
Reference in New Issue