altium: import images from SchDoc
This commit is contained in:
parent
94afdcb92a
commit
f13eb13b9a
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include <math/util.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <vector>
|
||||
|
||||
|
||||
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()
|
||||
{
|
||||
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 )
|
||||
{
|
||||
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" ) );
|
||||
}
|
||||
|
||||
|
||||
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 )
|
||||
{
|
||||
wxASSERT( PropertiesReadRecord( aProperties ) == ALTIUM_SCH_RECORD::SHEET );
|
||||
|
|
|
@ -35,6 +35,17 @@
|
|||
// this constant specifies a item which is not inside an component
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
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 <sch_edit_frame.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 )
|
||||
|
@ -270,7 +273,8 @@ void SCH_ALTIUM_PLUGIN::ParseAltiumSch( const wxString& aFileName )
|
|||
try
|
||||
{
|
||||
CFB::CompoundFileReader reader( buffer.get(), bytesRead );
|
||||
Parse( reader );
|
||||
ParseStorage( reader ); // we need this before parsing the FileHeader
|
||||
ParseFileHeader( reader );
|
||||
}
|
||||
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" );
|
||||
|
||||
|
@ -391,8 +427,7 @@ void SCH_ALTIUM_PLUGIN::Parse( const CFB::CompoundFileReader& aReader )
|
|||
case ALTIUM_SCH_RECORD::JUNCTION:
|
||||
ParseJunction( properties );
|
||||
break;
|
||||
case ALTIUM_SCH_RECORD::IMAGE:
|
||||
break;
|
||||
case ALTIUM_SCH_RECORD::IMAGE: ParseImage( properties ); break;
|
||||
case ALTIUM_SCH_RECORD::SHEET:
|
||||
ParseSheet( properties );
|
||||
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,
|
||||
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 )
|
||||
{
|
||||
m_altiumSheet = std::make_unique<ASCH_SHEET>( aProperties );
|
||||
|
|
|
@ -104,10 +104,12 @@ public:
|
|||
wxFileName getLibFileName();
|
||||
|
||||
void ParseAltiumSch( const wxString& aFileName );
|
||||
void Parse( const CFB::CompoundFileReader& aReader );
|
||||
void ParseStorage( const CFB::CompoundFileReader& aReader );
|
||||
void ParseFileHeader( const CFB::CompoundFileReader& aReader );
|
||||
|
||||
private:
|
||||
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 ParsePin( const std::map<wxString, wxString>& aProperties );
|
||||
|
@ -128,6 +130,7 @@ private:
|
|||
void ParseBus( const std::map<wxString, wxString>& aProperties );
|
||||
void ParseWire( 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 ParseSheetName( 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<wxString, LIB_PART*> m_powerSymbols;
|
||||
std::vector<ASCH_STORAGE_FILE> m_altiumStorage;
|
||||
|
||||
std::map<int, ASCH_COMPONENT> m_altiumComponents;
|
||||
std::vector<ASCH_PORT> m_altiumPortsCurrentSheet; // we require all connections first
|
||||
|
|
Loading…
Reference in New Issue