25#include <wx/filename.h>
27#include <wx/mstream.h>
28#include <wx/wfstream.h>
71 if(
HasFile( aName.GetFullName() ) )
74 return m_files[aName.GetFullName()].get();
76 m_files.erase( aName.GetFullName() );
79 wxFFileInputStream file( aName.GetFullPath() );
80 wxMemoryBuffer buffer;
85 wxFileOffset length = file.GetLength();
87 std::shared_ptr<EMBEDDED_FILE> efile = std::make_shared<EMBEDDED_FILE>();
88 efile->name = aName.GetFullName();
89 efile->decompressedData.resize( length );
91 wxString ext = aName.GetExt().Upper();
94 if( ext ==
"STP" || ext ==
"STPZ" || ext ==
"STEP" || ext ==
"WRL" || ext ==
"WRZ" )
98 else if( ext ==
"WOFF" || ext ==
"WOFF2" || ext ==
"TTF" || ext ==
"OTF" )
102 else if( ext ==
"PDF" )
106 else if( ext ==
"KICAD_WKS" )
111 if( !efile->decompressedData.data() )
114 char* data = efile->decompressedData.data();
115 wxFileOffset total_read = 0;
117 while( !file.Eof() && total_read < length )
119 file.Read( data, length - total_read );
121 size_t read = file.LastRead();
129 efile->is_valid =
true;
132 m_files[aName.GetFullName()] = std::move( efile );
143 return AddFile( std::shared_ptr<EMBEDDED_FILE>( aFile ) );
152 wxString
name = aFile->name;
153 auto [it, inserted] =
m_files.emplace( std::move(
name ), std::move( aFile ) );
161 return it->second.get();
180 const std::set<wxString>& aExcludedNames )
183 std::map<wxString, std::shared_ptr<EMBEDDED_FILE>> filtered;
187 if( aExcludedNames.count( file->name ) )
190 filtered[
name] = file;
193 m_files = std::move( filtered );
213 aOut.
Print(
"(embedded_files " );
225 aOut.
Print(
"(file " );
228 const char* type =
nullptr;
236 default: type =
"other";
break;
239 aOut.
Print(
"(type %s)", type );
243 aOut.
Print(
"(data" );
254 aOut.
Print(
"\n%1s%.*s%s\n", first ?
"" :
"|", length, view.data(),
255 remaining == length ?
"|" :
"" );
273 std::vector<char> compressedData;
274 size_t estCompressedSize = ZSTD_compressBound( aFile.
decompressedData.size() );
275 compressedData.resize( estCompressedSize );
276 size_t compressedSize = ZSTD_compress( compressedData.data(), estCompressedSize,
280 if( ZSTD_isError( compressedSize ) )
282 compressedData.clear();
286 const size_t dstLen = wxBase64EncodedSize( compressedSize );
289 compressedData.data(), compressedSize );
291 if( retval != dstLen )
308 std::vector<char> compressedData;
311 if( compressedSize == 0 )
313 wxLogTrace( wxT(
"KICAD_EMBED" ),
314 wxT(
"%s:%s:%d\n * Base64DecodedSize failed for file '%s' with size %zu" ),
315 __FILE__, __FUNCTION__, __LINE__, aFile.
name,
320 compressedData.resize( compressedSize );
321 void* compressed = compressedData.data();
327 unsigned long long estDecompressedSize = ZSTD_getFrameContentSize( compressed, compressedSize );
329 if( estDecompressedSize > 1e9 )
332 if( estDecompressedSize == ZSTD_CONTENTSIZE_ERROR
333 || estDecompressedSize == ZSTD_CONTENTSIZE_UNKNOWN )
341 size_t decompressedSize = ZSTD_decompress( decompressed, estDecompressedSize,
342 compressed, compressedSize );
344 if( ZSTD_isError( decompressedSize ) )
346 wxLogTrace( wxT(
"KICAD_EMBED" ),
347 wxT(
"%s:%s:%d\n * ZSTD_decompress failed with error '%s'" ),
348 __FILE__, __FUNCTION__, __LINE__, ZSTD_getErrorName( decompressedSize ) );
359 if( aAllowEmptyHash && aFile.
data_hash.empty() )
361 wxLogTrace( wxT(
"KICAD_EMBED" ), wxT(
"Generated new hash for '%s'"), aFile.
name );
363 else if( aFile.
data_hash.length() == 64 )
366 std::string sha_hash;
371 wxLogTrace( wxT(
"KICAD_EMBED" ),
372 wxT(
"%s:%s:%d\n * Checksum error in embedded file '%s'" ),
373 __FILE__, __FUNCTION__, __LINE__, aFile.
name );
389 wxLogTrace( wxT(
"KICAD_EMBED" ),
390 wxT(
"%s:%s:%d\n * Checksum error in embedded file '%s'" ),
391 __FILE__, __FUNCTION__, __LINE__, aFile.
name );
406 if( !aFileName.FileExists() )
409 wxFFileInputStream file( aFileName.GetFullPath() );
414 wxFileOffset length = file.GetLength();
415 std::vector<char> data( length );
420 char* dataPtr = data.data();
421 wxFileOffset totalRead = 0;
423 while( !file.Eof() && totalRead < length )
425 file.Read( dataPtr, length - totalRead );
426 size_t bytesRead = file.LastRead();
427 dataPtr += bytesRead;
428 totalRead += bytesRead;
447 CurLineNumber(), CurOffset() );
449 using namespace EMBEDDED_FILES_T;
451 std::unique_ptr<EMBEDDED_FILES::EMBEDDED_FILE> file(
nullptr );
453 for(
T token = NextTok(); token != T_RIGHT; token = NextTok() )
455 if( token != T_LEFT )
460 if( token != T_file )
465 if( !file->compressedEncodedData.empty() )
471 CurSource(), CurLine(), CurLineNumber(), CurOffset() );
475 aFiles->
AddFile( file.release() );
478 file = std::unique_ptr<EMBEDDED_FILES::EMBEDDED_FILE>(
nullptr );
480 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
482 if( token != T_LEFT )
493 NeedSYMBOLorNUMBER();
495 if( !IsSymbol( token ) )
496 Expecting(
"checksum data" );
498 file->data_hash = CurStr();
513 if( curTok == T_RIGHT )
525 file->compressedEncodedData.reserve( 1 << 17 );
527 while( token != T_BAR )
529 if( !IsSymbol( token ) )
530 Expecting(
"base64 file data" );
532 file->compressedEncodedData += CurStr();
536 file->compressedEncodedData.shrink_to_fit();
544 wxLogTrace( wxT(
"KICAD_EMBED" ),
545 wxT(
"Duplicate 'name' tag in embedded file %s" ), file->name );
548 NeedSYMBOLorNUMBER();
550 file = std::make_unique<EMBEDDED_FILES::EMBEDDED_FILE>();
551 file->name = CurStr();
569 default: Expecting(
"datasheet, font, model, worksheet or other" );
break;
576 Expecting(
"checksum, data or name" );
584 if( !file->compressedEncodedData.empty() )
590 CurSource(), CurLine(), CurLineNumber(), CurOffset() );
594 aFiles->
AddFile( file.release() );
601 wxFileName cacheFile;
603 auto it =
m_files.find( aName );
609 cacheFile.AppendDir( wxT(
"embed" ) );
613 wxLogTrace( wxT(
"KICAD_EMBED" ),
614 wxT(
"%s:%s:%d\n * failed to create embed cache directory '%s'" ),
615 __FILE__, __FUNCTION__, __LINE__, cacheFile.GetPath() );
617 cacheFile.SetPath( wxFileName::GetTempDir() );
620 wxFileName inputName( aName );
624 cacheFile.SetName(
"kicad_embedded_" + it->second->data_hash );
625 cacheFile.SetExt( inputName.GetExt() );
627 if( cacheFile.FileExists() && cacheFile.IsFileReadable() )
630 wxFFileOutputStream out( cacheFile.GetFullPath() );
638 out.Write( it->second->decompressedData.data(), it->second->decompressedData.size() );
666 m_files( std::move( other.m_files ) ),
671 other.m_embedFonts =
false;
681 m_files = std::move( other.m_files );
685 other.m_embedFonts =
false;
718 m_files[
name] = std::make_shared<EMBEDDED_FILE>( *file );
740 m_files[
name] = std::make_shared<EMBEDDED_FILE>( *file );
void ParseEmbedded(EMBEDDED_FILES *aFiles)
std::vector< wxString > m_fontFiles
void RemoveFile(const wxString &name, bool aErase=true)
Remove a file from the collection and frees the memory.
static RETURN_CODE DecompressAndDecode(EMBEDDED_FILE &aFile, bool aAllowEmptyHash=false)
Takes data from the #compressedEncodedData buffer and Base64 decodes it.
@ OUT_OF_MEMORY
Could not allocate memory.
@ FILE_NOT_FOUND
File not found on disk.
@ CHECKSUM_ERROR
Checksum in file does not match data.
wxFileName GetTemporaryFileName(const wxString &aName) const
void WriteEmbeddedFiles(OUTPUTFORMATTER &aOut, bool aWriteData) const
Output formatter for the embedded files.
const std::vector< wxString > * UpdateFontFiles()
Helper function to get a list of fonts for fontconfig to add to the library.
FILE_ADDED_CALLBACK m_fileAddedCallback
static RETURN_CODE ComputeFileHash(const wxFileName &aFileName, std::string &aHash)
Compute the hash of a file on disk without fully embedding it.
bool HasFile(const wxString &name) const
void ClearEmbeddedFiles(bool aDeleteFiles=true)
void ClearEmbeddedFonts()
Remove all embedded fonts from the collection.
EMBEDDED_FILES & operator=(EMBEDDED_FILES &&other) noexcept
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Load a file from disk and adds it to the collection.
const std::vector< wxString > * GetFontFiles() const
If we just need the cached version of the font files, we can use this function which is const and wil...
std::map< wxString, std::shared_ptr< EMBEDDED_FILE > > m_files
static RETURN_CODE CompressAndEncode(EMBEDDED_FILE &aFile)
Take data from the #decompressedData buffer and compresses it using ZSTD into the #compressedEncodedD...
bool m_embedFonts
If set, fonts will be embedded in the element on save.
void AssignSharedFrom(const EMBEDDED_FILES &aSource, const std::set< wxString > &aExcludedNames={})
Replace this collection's files with references to aSource's files, skipping any whose name appears i...
A streaming C++ equivalent for MurmurHash3_x64_128.
FORCE_INLINE void addDataV1(const uint8_t *data, size_t length)
FORCE_INLINE void add(const std::string &input)
FORCE_INLINE HASH_128 digest()
static bool EnsurePathExists(const wxString &aPath, bool aPathToFile=false)
Attempts to create a given path if it does not exist.
static wxString GetUserCachePath()
Gets the stock (install) 3d viewer plugins path.
static const std::string KiCadUriPrefix
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
#define MIME_BASE64_LENGTH
std::vector< char > decompressedData
std::string compressedEncodedData
std::string ToString() const
A filename or source description, a problem input line, a line number, a byte offset,...
wxString result
Test unit parsing edge cases and error handling.
Definition of file extensions used in Kicad.