altium: import images from SchDoc
This commit is contained in:
parent
94afdcb92a
commit
f13eb13b9a
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include <math/util.h>
|
#include <math/util.h>
|
||||||
#include <wx/gdicmn.h>
|
#include <wx/gdicmn.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
namespace CFB
|
namespace CFB
|
||||||
|
@ -84,6 +85,21 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<char> ReadVector( size_t aSize )
|
||||||
|
{
|
||||||
|
if( aSize > GetRemainingBytes() )
|
||||||
|
{
|
||||||
|
m_error = true;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::vector<char> data( m_pos, m_pos + aSize );
|
||||||
|
m_pos += aSize;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int32_t ReadKicadUnit()
|
int32_t ReadKicadUnit()
|
||||||
{
|
{
|
||||||
return ConvertToKicadUnit( Read<int32_t>() );
|
return ConvertToKicadUnit( Read<int32_t>() );
|
||||||
|
|
|
@ -77,6 +77,20 @@ T PropertiesReadEnum( const std::map<wxString, wxString>& aProperties, const wxS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ASCH_STORAGE_FILE::ASCH_STORAGE_FILE( ALTIUM_PARSER& aReader )
|
||||||
|
{
|
||||||
|
aReader.Skip( 5 );
|
||||||
|
filename = aReader.ReadWxString();
|
||||||
|
uint32_t dataSize = aReader.Read<uint32_t>();
|
||||||
|
data = aReader.ReadVector( dataSize );
|
||||||
|
|
||||||
|
if( aReader.HasParsingError() )
|
||||||
|
{
|
||||||
|
THROW_IO_ERROR( "Storage stream was not parsed correctly" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ASCH_COMPONENT::ASCH_COMPONENT( const std::map<wxString, wxString>& aProperties )
|
ASCH_COMPONENT::ASCH_COMPONENT( const std::map<wxString, wxString>& aProperties )
|
||||||
{
|
{
|
||||||
wxASSERT( PropertiesReadRecord( aProperties ) == ALTIUM_SCH_RECORD::COMPONENT );
|
wxASSERT( PropertiesReadRecord( aProperties ) == ALTIUM_SCH_RECORD::COMPONENT );
|
||||||
|
@ -541,6 +555,26 @@ ASCH_JUNCTION::ASCH_JUNCTION( const std::map<wxString, wxString>& aProperties )
|
||||||
-PropertiesReadKiCadUnitFrac( aProperties, "LOCATION.Y" ) );
|
-PropertiesReadKiCadUnitFrac( aProperties, "LOCATION.Y" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ASCH_IMAGE::ASCH_IMAGE( const std::map<wxString, wxString>& aProperties )
|
||||||
|
{
|
||||||
|
wxASSERT( PropertiesReadRecord( aProperties ) == ALTIUM_SCH_RECORD::IMAGE );
|
||||||
|
|
||||||
|
indexinsheet = ALTIUM_PARSER::PropertiesReadInt( aProperties, "INDEXINSHEET", 0 );
|
||||||
|
ownerpartid = ALTIUM_PARSER::PropertiesReadInt( aProperties, "OWNERPARTID", -1 );
|
||||||
|
|
||||||
|
filename = ALTIUM_PARSER::PropertiesReadString( aProperties, "FILENAME", "" );
|
||||||
|
|
||||||
|
location = wxPoint( PropertiesReadKiCadUnitFrac( aProperties, "LOCATION.X" ),
|
||||||
|
-PropertiesReadKiCadUnitFrac( aProperties, "LOCATION.Y" ) );
|
||||||
|
corner = wxPoint( PropertiesReadKiCadUnitFrac( aProperties, "CORNER.X" ),
|
||||||
|
-PropertiesReadKiCadUnitFrac( aProperties, "CORNER.Y" ) );
|
||||||
|
|
||||||
|
embedimage = ALTIUM_PARSER::PropertiesReadBool( aProperties, "EMBEDIMAGE", false );
|
||||||
|
keepaspect = ALTIUM_PARSER::PropertiesReadBool( aProperties, "KEEPASPECT", false );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ASCH_SHEET_FONT::ASCH_SHEET_FONT( const std::map<wxString, wxString>& aProperties, int aId )
|
ASCH_SHEET_FONT::ASCH_SHEET_FONT( const std::map<wxString, wxString>& aProperties, int aId )
|
||||||
{
|
{
|
||||||
wxASSERT( PropertiesReadRecord( aProperties ) == ALTIUM_SCH_RECORD::SHEET );
|
wxASSERT( PropertiesReadRecord( aProperties ) == ALTIUM_SCH_RECORD::SHEET );
|
||||||
|
|
|
@ -35,6 +35,17 @@
|
||||||
// this constant specifies a item which is not inside an component
|
// this constant specifies a item which is not inside an component
|
||||||
const int ALTIUM_COMPONENT_NONE = -1;
|
const int ALTIUM_COMPONENT_NONE = -1;
|
||||||
|
|
||||||
|
class ALTIUM_PARSER;
|
||||||
|
|
||||||
|
struct ASCH_STORAGE_FILE
|
||||||
|
{
|
||||||
|
wxString filename;
|
||||||
|
std::vector<char> data;
|
||||||
|
|
||||||
|
explicit ASCH_STORAGE_FILE( ALTIUM_PARSER& aReader );
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
enum class ALTIUM_SCH_RECORD
|
enum class ALTIUM_SCH_RECORD
|
||||||
{
|
{
|
||||||
HEADER = 0,
|
HEADER = 0,
|
||||||
|
@ -540,6 +551,22 @@ struct ASCH_JUNCTION
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct ASCH_IMAGE
|
||||||
|
{
|
||||||
|
int indexinsheet;
|
||||||
|
int ownerpartid;
|
||||||
|
|
||||||
|
wxString filename;
|
||||||
|
wxPoint location;
|
||||||
|
wxPoint corner;
|
||||||
|
|
||||||
|
bool embedimage;
|
||||||
|
bool keepaspect;
|
||||||
|
|
||||||
|
explicit ASCH_IMAGE( const std::map<wxString, wxString>& aProperties );
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct ASCH_SHEET_FONT
|
struct ASCH_SHEET_FONT
|
||||||
{
|
{
|
||||||
wxString fontname;
|
wxString fontname;
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Can be viewed in: https://ide.kaitai.io/
|
||||||
|
#
|
||||||
|
# This file is a formal specification of the binary format used in Altium SchDoc for the Storage file.
|
||||||
|
# Files need to manually extracted using a program which can read the Microsoft Compound File Format.
|
||||||
|
#
|
||||||
|
# While I do not create a parser using this file, it is still very helpful to understand the binary
|
||||||
|
# format.
|
||||||
|
|
||||||
|
meta:
|
||||||
|
id: altium_storage
|
||||||
|
endian: le
|
||||||
|
encoding: UTF-8
|
||||||
|
|
||||||
|
seq:
|
||||||
|
- id: properties_length
|
||||||
|
type: u4
|
||||||
|
- id: properties
|
||||||
|
type: str
|
||||||
|
size: properties_length
|
||||||
|
- id: record
|
||||||
|
type: record
|
||||||
|
repeat: eos
|
||||||
|
|
||||||
|
|
||||||
|
types:
|
||||||
|
record:
|
||||||
|
seq:
|
||||||
|
- size: 5
|
||||||
|
- id: filename_length
|
||||||
|
type: u1
|
||||||
|
- id: filename
|
||||||
|
type: str
|
||||||
|
size: filename_length
|
||||||
|
- id: data_length
|
||||||
|
type: u4
|
||||||
|
- id: data
|
||||||
|
size: data_length
|
|
@ -58,6 +58,9 @@
|
||||||
#include <kicad_string.h>
|
#include <kicad_string.h>
|
||||||
#include <sch_edit_frame.h>
|
#include <sch_edit_frame.h>
|
||||||
#include <wildcards_and_files_ext.h>
|
#include <wildcards_and_files_ext.h>
|
||||||
|
#include <wx/mstream.h>
|
||||||
|
#include <wx/zstream.h>
|
||||||
|
#include <wx/wfstream.h>
|
||||||
|
|
||||||
|
|
||||||
const wxPoint GetRelativePosition( const wxPoint& aPosition, const SCH_COMPONENT* aComponent )
|
const wxPoint GetRelativePosition( const wxPoint& aPosition, const SCH_COMPONENT* aComponent )
|
||||||
|
@ -270,7 +273,8 @@ void SCH_ALTIUM_PLUGIN::ParseAltiumSch( const wxString& aFileName )
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CFB::CompoundFileReader reader( buffer.get(), bytesRead );
|
CFB::CompoundFileReader reader( buffer.get(), bytesRead );
|
||||||
Parse( reader );
|
ParseStorage( reader ); // we need this before parsing the FileHeader
|
||||||
|
ParseFileHeader( reader );
|
||||||
}
|
}
|
||||||
catch( CFB::CFBException& exception )
|
catch( CFB::CFBException& exception )
|
||||||
{
|
{
|
||||||
|
@ -279,7 +283,39 @@ void SCH_ALTIUM_PLUGIN::ParseAltiumSch( const wxString& aFileName )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SCH_ALTIUM_PLUGIN::Parse( const CFB::CompoundFileReader& aReader )
|
void SCH_ALTIUM_PLUGIN::ParseStorage( const CFB::CompoundFileReader& aReader )
|
||||||
|
{
|
||||||
|
const CFB::COMPOUND_FILE_ENTRY* file = FindStream( aReader, "Storage" );
|
||||||
|
|
||||||
|
if( file == nullptr )
|
||||||
|
return;
|
||||||
|
|
||||||
|
ALTIUM_PARSER reader( aReader, file );
|
||||||
|
|
||||||
|
std::map<wxString, wxString> properties = reader.ReadProperties();
|
||||||
|
wxString header = ALTIUM_PARSER::PropertiesReadString( properties, "HEADER", "" );
|
||||||
|
int weight = ALTIUM_PARSER::PropertiesReadInt( properties, "WEIGHT", 0 );
|
||||||
|
|
||||||
|
if( weight < 0 )
|
||||||
|
THROW_IO_ERROR( "Storage weight is negative!" );
|
||||||
|
|
||||||
|
for( int i = 0; i < weight; i++ )
|
||||||
|
{
|
||||||
|
m_altiumStorage.emplace_back( reader );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( reader.HasParsingError() )
|
||||||
|
THROW_IO_ERROR( "stream was not parsed correctly!" );
|
||||||
|
|
||||||
|
// TODO pointhi: is it possible to have multiple headers in one Storage file? Otherwise throw IO Error.
|
||||||
|
if( reader.GetRemainingBytes() != 0 )
|
||||||
|
wxLogError(
|
||||||
|
wxString::Format( "Storage file was not fully parsed as %d bytes are remaining.",
|
||||||
|
reader.GetRemainingBytes() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SCH_ALTIUM_PLUGIN::ParseFileHeader( const CFB::CompoundFileReader& aReader )
|
||||||
{
|
{
|
||||||
const CFB::COMPOUND_FILE_ENTRY* file = FindStream( aReader, "FileHeader" );
|
const CFB::COMPOUND_FILE_ENTRY* file = FindStream( aReader, "FileHeader" );
|
||||||
|
|
||||||
|
@ -391,8 +427,7 @@ void SCH_ALTIUM_PLUGIN::Parse( const CFB::CompoundFileReader& aReader )
|
||||||
case ALTIUM_SCH_RECORD::JUNCTION:
|
case ALTIUM_SCH_RECORD::JUNCTION:
|
||||||
ParseJunction( properties );
|
ParseJunction( properties );
|
||||||
break;
|
break;
|
||||||
case ALTIUM_SCH_RECORD::IMAGE:
|
case ALTIUM_SCH_RECORD::IMAGE: ParseImage( properties ); break;
|
||||||
break;
|
|
||||||
case ALTIUM_SCH_RECORD::SHEET:
|
case ALTIUM_SCH_RECORD::SHEET:
|
||||||
ParseSheet( properties );
|
ParseSheet( properties );
|
||||||
break;
|
break;
|
||||||
|
@ -493,6 +528,27 @@ bool SCH_ALTIUM_PLUGIN::IsComponentPartVisible( int aOwnerindex, int aOwnerpartd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const ASCH_STORAGE_FILE* SCH_ALTIUM_PLUGIN::GetFileFromStorage( const wxString& aFilename ) const
|
||||||
|
{
|
||||||
|
const ASCH_STORAGE_FILE* nonExactMatch = nullptr;
|
||||||
|
|
||||||
|
for( const ASCH_STORAGE_FILE& file : m_altiumStorage )
|
||||||
|
{
|
||||||
|
if( file.filename.IsSameAs( aFilename ) )
|
||||||
|
{
|
||||||
|
return &file;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( file.filename.EndsWith( aFilename ) )
|
||||||
|
{
|
||||||
|
nonExactMatch = &file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nonExactMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SCH_ALTIUM_PLUGIN::ParseComponent( int aIndex,
|
void SCH_ALTIUM_PLUGIN::ParseComponent( int aIndex,
|
||||||
const std::map<wxString, wxString>& aProperties )
|
const std::map<wxString, wxString>& aProperties )
|
||||||
{
|
{
|
||||||
|
@ -1874,6 +1930,69 @@ void SCH_ALTIUM_PLUGIN::ParseJunction( const std::map<wxString, wxString>& aProp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SCH_ALTIUM_PLUGIN::ParseImage( const std::map<wxString, wxString>& aProperties )
|
||||||
|
{
|
||||||
|
ASCH_IMAGE elem( aProperties );
|
||||||
|
|
||||||
|
wxPoint center = ( elem.location + elem.corner ) / 2 + m_sheetOffset;
|
||||||
|
std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>( center );
|
||||||
|
|
||||||
|
if( elem.embedimage )
|
||||||
|
{
|
||||||
|
const ASCH_STORAGE_FILE* storageFile = GetFileFromStorage( elem.filename );
|
||||||
|
|
||||||
|
if( !storageFile )
|
||||||
|
{
|
||||||
|
wxLogError(
|
||||||
|
wxString::Format( "Embedded file not found in storage: %s", elem.filename ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString storagePath = wxFileName::CreateTempFileName( "kicad_import_" );
|
||||||
|
|
||||||
|
// As wxZlibInputStream is not seekable, we need to write a temporary file
|
||||||
|
wxMemoryInputStream fileStream( storageFile->data.data(), storageFile->data.size() );
|
||||||
|
wxZlibInputStream zlibInputStream( fileStream );
|
||||||
|
wxFFileOutputStream outputStream( storagePath );
|
||||||
|
outputStream.Write( zlibInputStream );
|
||||||
|
outputStream.Close();
|
||||||
|
|
||||||
|
if( !bitmap->ReadImageFile( storagePath ) )
|
||||||
|
{
|
||||||
|
wxLogError( wxString::Format( "Error while reading image: %s", storagePath ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove temporary file
|
||||||
|
wxRemoveFile( storagePath );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( !wxFileExists( elem.filename ) )
|
||||||
|
{
|
||||||
|
wxLogError( wxString::Format( "File not found on disk: %s", elem.filename ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !bitmap->ReadImageFile( elem.filename ) )
|
||||||
|
{
|
||||||
|
wxLogError( wxString::Format( "Error while reading image: %s", elem.filename ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we only support one scale, thus we need to select one in case it does not keep aspect ratio
|
||||||
|
wxSize currentImageSize = bitmap->GetSize();
|
||||||
|
wxPoint expectedImageSize = elem.location - elem.corner;
|
||||||
|
double scaleX = std::abs( static_cast<double>( expectedImageSize.x ) / currentImageSize.x );
|
||||||
|
double scaleY = std::abs( static_cast<double>( expectedImageSize.y ) / currentImageSize.y );
|
||||||
|
bitmap->SetImageScale( std::min( scaleX, scaleY ) );
|
||||||
|
|
||||||
|
bitmap->SetFlags( IS_NEW );
|
||||||
|
m_currentSheet->GetScreen()->Append( bitmap.release() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SCH_ALTIUM_PLUGIN::ParseSheet( const std::map<wxString, wxString>& aProperties )
|
void SCH_ALTIUM_PLUGIN::ParseSheet( const std::map<wxString, wxString>& aProperties )
|
||||||
{
|
{
|
||||||
m_altiumSheet = std::make_unique<ASCH_SHEET>( aProperties );
|
m_altiumSheet = std::make_unique<ASCH_SHEET>( aProperties );
|
||||||
|
|
|
@ -104,10 +104,12 @@ public:
|
||||||
wxFileName getLibFileName();
|
wxFileName getLibFileName();
|
||||||
|
|
||||||
void ParseAltiumSch( const wxString& aFileName );
|
void ParseAltiumSch( const wxString& aFileName );
|
||||||
void Parse( const CFB::CompoundFileReader& aReader );
|
void ParseStorage( const CFB::CompoundFileReader& aReader );
|
||||||
|
void ParseFileHeader( const CFB::CompoundFileReader& aReader );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsComponentPartVisible( int aOwnerindex, int aOwnerpartdisplaymode ) const;
|
bool IsComponentPartVisible( int aOwnerindex, int aOwnerpartdisplaymode ) const;
|
||||||
|
const ASCH_STORAGE_FILE* GetFileFromStorage( const wxString& aFilename ) const;
|
||||||
|
|
||||||
void ParseComponent( int aIndex, const std::map<wxString, wxString>& aProperties );
|
void ParseComponent( int aIndex, const std::map<wxString, wxString>& aProperties );
|
||||||
void ParsePin( const std::map<wxString, wxString>& aProperties );
|
void ParsePin( const std::map<wxString, wxString>& aProperties );
|
||||||
|
@ -128,6 +130,7 @@ private:
|
||||||
void ParseBus( const std::map<wxString, wxString>& aProperties );
|
void ParseBus( const std::map<wxString, wxString>& aProperties );
|
||||||
void ParseWire( const std::map<wxString, wxString>& aProperties );
|
void ParseWire( const std::map<wxString, wxString>& aProperties );
|
||||||
void ParseJunction( const std::map<wxString, wxString>& aProperties );
|
void ParseJunction( const std::map<wxString, wxString>& aProperties );
|
||||||
|
void ParseImage( const std::map<wxString, wxString>& aProperties );
|
||||||
void ParseSheet( const std::map<wxString, wxString>& aProperties );
|
void ParseSheet( const std::map<wxString, wxString>& aProperties );
|
||||||
void ParseSheetName( const std::map<wxString, wxString>& aProperties );
|
void ParseSheetName( const std::map<wxString, wxString>& aProperties );
|
||||||
void ParseFileName( const std::map<wxString, wxString>& aProperties );
|
void ParseFileName( const std::map<wxString, wxString>& aProperties );
|
||||||
|
@ -156,6 +159,7 @@ private:
|
||||||
std::map<int, LIB_PART*> m_symbols; // every component has its unique symbol
|
std::map<int, LIB_PART*> m_symbols; // every component has its unique symbol
|
||||||
|
|
||||||
std::map<wxString, LIB_PART*> m_powerSymbols;
|
std::map<wxString, LIB_PART*> m_powerSymbols;
|
||||||
|
std::vector<ASCH_STORAGE_FILE> m_altiumStorage;
|
||||||
|
|
||||||
std::map<int, ASCH_COMPONENT> m_altiumComponents;
|
std::map<int, ASCH_COMPONENT> m_altiumComponents;
|
||||||
std::vector<ASCH_PORT> m_altiumPortsCurrentSheet; // we require all connections first
|
std::vector<ASCH_PORT> m_altiumPortsCurrentSheet; // we require all connections first
|
||||||
|
|
Loading…
Reference in New Issue