ADDED: Support compressed STEP and VRML files

This adds support for opening .stpZ, step.gz and .wrz files where the
files have been compressed using ZIP or gzip according to the "standard"
published by the MBx volunteer forum at
https://www.cax-if.org/documents/rec_prac_file_compression_v12.pdf

Fixes https://gitlab.com/kicad/code/kicad/issues/2479
This commit is contained in:
Seth Hillbrand 2020-08-17 15:48:20 -07:00
parent d7b38d10c2
commit a38c2aad1f
10 changed files with 290 additions and 98 deletions

View File

@ -1,7 +1,7 @@
#
# This program source code file is part of KICAD, a free EDA CAD application.
#
# Copyright (C) 2007-2018 Kicad Developers, see AUTHORS.txt for contributors.
# Copyright (C) 2007-2020 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
@ -595,6 +595,13 @@ find_package( GLM 0.9.8 REQUIRED )
add_definitions( -DGLM_FORCE_CTOR_INIT )
include_directories( SYSTEM ${GLM_INCLUDE_DIR} )
#
# Find zlib library, required
#
find_package(ZLIB REQUIRED)
check_find_package_result( ZLIB_FOUND "ZLIB" )
include_directories( SYSTEM ${ZLIB_INCLUDE_DIRS} )
#
# Find CURL library, required
#

View File

@ -17,8 +17,12 @@ add_library( s3d_plugin_oce MODULE
loadmodel.cpp
)
target_link_libraries( s3d_plugin_oce kicad_3dsg ${OCC_LIBRARIES} ${wxWidgets_LIBRARIES} )
target_link_libraries( s3d_plugin_oce kicad_3dsg ${OCC_LIBRARIES} ${wxWidgets_LIBRARIES} ${ZLIB_LIBRARIES} )
target_include_directories( s3d_plugin_oce PRIVATE
$<TARGET_PROPERTY:gzip-hpp,INTERFACE_INCLUDE_DIRECTORIES>
)
if( APPLE )
# puts library into the main kicad.app bundle in build tree
set_target_properties( s3d_plugin_oce PROPERTIES

View File

@ -2,6 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
* Copyright (C) 2020 KiCad Developers, see CHANGELOG.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
@ -28,12 +29,12 @@
#include <cstring>
#include <map>
#include <vector>
#include <wx/filename.h>
#include <wx/stdpaths.h>
#include <wx/string.h>
#include <wx/wfstream.h>
#if ( defined( DEBUG_OCE ) && DEBUG_OCE > 3 )
#include <wx/filename.h>
#endif
#include <decompress.hpp>
#include <TDocStd_Document.hxx>
#include <TopoDS.hxx>
@ -261,19 +262,24 @@ struct DATA
enum FormatType
{
FMT_NONE = 0,
FMT_STEP = 1,
FMT_IGES = 2
FMT_STEP,
FMT_STPZ,
FMT_IGES
};
FormatType fileType( const char* aFileName )
{
wxString fname( wxString::FromUTF8Unchecked( aFileName ) );
wxFileInputStream ifile( fname );
wxFileName fname( wxString::FromUTF8Unchecked( aFileName ) );
wxFileInputStream ifile( fname.GetFullPath() );
if( !ifile.IsOk() )
return FMT_NONE;
if( fname.GetExt().MakeUpper().EndsWith( "STPZ" ) ||
fname.GetExt().MakeUpper().EndsWith( "GZ" ) )
return FMT_STPZ;
char iline[82];
memset( iline, 0, 82 );
ifile.Read( iline, 82 );
@ -451,6 +457,47 @@ bool readSTEP( Handle(TDocStd_Document)& m_doc, const char* fname )
}
bool readSTEPZ( Handle(TDocStd_Document)& m_doc, const char* aFileName )
{
wxFileName fname( wxString::FromUTF8Unchecked( aFileName ) );
wxFileInputStream ifile( fname.GetFullPath() );
wxFileName outFile( fname );
outFile.SetPath( wxStandardPaths::Get().GetTempDir() );
outFile.SetExt( "STEP" );
wxFileOffset size = ifile.GetLength();
if( size == wxInvalidOffset )
return false;
{
wxFileOutputStream ofile( outFile.GetFullPath() );
if( !ofile.IsOk() )
return false;
char *buffer = new char[size];
ifile.Read( buffer, size);
std::string expanded = gzip::decompress( buffer, size );
delete buffer;
ofile.Write( expanded.data(), expanded.size() );
ofile.Close();
}
bool retval = readSTEP( m_doc, outFile.GetFullPath() );
// Cleanup our temporary file
wxRemoveFile( outFile.GetFullPath() );
return retval;
}
SCENEGRAPH* LoadModel( char const* filename )
{
DATA data;
@ -473,6 +520,12 @@ SCENEGRAPH* LoadModel( char const* filename )
return NULL;
break;
case FMT_STPZ:
if( !readSTEPZ( data.m_doc, filename ) )
return NULL;
break;
default:
return NULL;
break;

View File

@ -2,6 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
* Copyright (C) 2020 KiCad Developers, see CHANGELOG.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
@ -30,6 +31,9 @@
#include "plugins/3d/3d_plugin.h"
#include "plugins/3dapi/ifsg_all.h"
#include <string>
#include <vector>
SCENEGRAPH* LoadModel( char const* filename );
#define PLUGIN_OCE_MAJOR 1
@ -62,55 +66,23 @@ void GetPluginVersion( unsigned char* Major,
return;
}
// number of extensions supported
#ifdef _WIN32
#define NEXTS 4
#else
#define NEXTS 8
#endif
// number of filter sets supported
#define NFILS 2
static char ext0[] = "stp";
static char ext1[] = "step";
static char ext2[] = "igs";
static char ext3[] = "iges";
#ifdef _WIN32
static char fil0[] = "STEP (*.stp;*.step)|*.stp;*.step";
static char fil1[] = "IGES (*.igs;*.iges)|*.igs;*.iges";
#else
static char ext4[] = "STP";
static char ext5[] = "STEP";
static char ext6[] = "IGS";
static char ext7[] = "IGES";
static char fil0[] = "STEP (*.stp;*.STP;*.step;*.STEP)|*.stp;*.STP;*.step;*.STEP";
static char fil1[] = "IGES (*.igs;*.IGS;*.iges;*.IGES)|*.igs;*.IGS;*.iges;*.IGES";
#endif
static struct FILE_DATA
{
char const* extensions[NEXTS];
char const* filters[NFILS];
std::vector<std::string> extensions;
std::vector<std::string> filters;
FILE_DATA()
{
extensions[0] = ext0;
extensions[1] = ext1;
extensions[2] = ext2;
extensions[3] = ext3;
filters[0] = fil0;
filters[1] = fil1;
#ifndef _WIN32
extensions[4] = ext4;
extensions[5] = ext5;
extensions[6] = ext6;
extensions[7] = ext7;
#ifdef _WIN32
extensions = { "stp","step","stpZ","step.gz","igs","iges" };
filters = { "STEP (*.stp;*.step;*.stpZ;*.step.gz)|*.stp;*.step;*.stpZ;*.step.gz",
"IGES (*.igs;*.iges)|*.igs;*.iges" };
#else
extensions = { "stp","STP","stpZ","STPZ","step","STEP","step.gz","STEP.GZ","igs","IGS","iges","IGES" };
filters = { "STEP (*.stp;*.STP;*.stpZ;*.STPZ;*.step;*.STEP;*.step.gz;*.STEP.GZ)"
"|*.stp;*.STP;*.stpZ;*.STPZ;*.step;*.STEP;*.step.gz;*.STEP.GZ",
"IGES (*.igs;*.IGS;*.iges;*.IGES)|*.igs;*.IGS;*.iges;*.IGES" };
#endif
return;
}
} file_data;
@ -118,31 +90,31 @@ static struct FILE_DATA
int GetNExtensions( void )
{
return NEXTS;
return file_data.extensions.size();
}
char const* GetModelExtension( int aIndex )
{
if( aIndex < 0 || aIndex >= NEXTS )
if( aIndex < 0 || aIndex >= int( file_data.extensions.size() ) )
return NULL;
return file_data.extensions[aIndex];
return file_data.extensions[aIndex].c_str();
}
int GetNFilters( void )
{
return NFILS;
return file_data.filters.size();
}
char const* GetFileFilter( int aIndex )
{
if( aIndex < 0 || aIndex >= NFILS )
if( aIndex < 0 || aIndex >= int( file_data.filters.size() ) )
return NULL;
return file_data.filters[aIndex];
return file_data.filters[aIndex].c_str();
}

View File

@ -65,7 +65,11 @@ add_library( s3d_plugin_vrml MODULE
x3d/x3d_transform.cpp
)
target_link_libraries( s3d_plugin_vrml kicad_3dsg ${OPENGL_LIBRARIES} ${wxWidgets_LIBRARIES} )
target_link_libraries( s3d_plugin_vrml kicad_3dsg ${OPENGL_LIBRARIES} ${wxWidgets_LIBRARIES} ${ZLIB_LIBRARIES} )
target_include_directories( s3d_plugin_vrml PRIVATE
$<TARGET_PROPERTY:gzip-hpp,INTERFACE_INCLUDE_DIRECTORIES>
)
if( APPLE )
# puts library into the main kicad.app bundle in build tree

View File

@ -2,6 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
* Copyright (C) 2020 KiCad Developers, see CHANGELOG.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
@ -40,8 +41,13 @@
#include "x3d.h"
#include <clocale>
#include <wx/filename.h>
#include <wx/stdpaths.h>
#include <wx/string.h>
#include <wx/wfstream.h>
#include <wx/log.h>
#include <decompress.hpp>
#define PLUGIN_VRML_MAJOR 1
#define PLUGIN_VRML_MINOR 3
@ -73,47 +79,24 @@ void GetPluginVersion( unsigned char* Major,
return;
}
// number of extensions supported
#ifdef _WIN32
#define NEXTS 2
#else
#define NEXTS 4
#endif
// number of filter sets supported
#define NFILS 2
static char ext0[] = "wrl";
static char ext1[] = "x3d";
#ifdef _WIN32
static char fil0[] = "VRML 1.0/2.0 (*.wrl)|*.wrl";
static char fil1[] = "X3D (*.x3d)|*.x3d";
#else
static char ext2[] = "WRL";
static char ext3[] = "X3D";
static char fil0[] = "VRML 1.0/2.0 (*.wrl;*.WRL)|*.wrl;*.WRL";
static char fil1[] = "X3D (*.x3d;*.X3D)|*.x3d;*.X3D";
#endif
static struct FILE_DATA
{
char const* extensions[NEXTS];
char const* filters[NFILS];
std::vector<std::string> extensions;
std::vector<std::string> filters;
FILE_DATA()
{
extensions[0] = ext0;
extensions[1] = ext1;
filters[0] = fil0;
filters[1] = fil1;
#ifndef _WIN32
extensions[2] = ext2;
extensions[3] = ext3;
#ifdef _WIN32
extensions = { "wrl", "wrz", "x3d" };
filters = { "VRML 1.0/2.0 (*.wrl;*.wrz)|*.wrl;*.wrz",
"X3D (*.x3d)|*.x3d" };
#else
extensions = { "wrl", "WRL", "wrz", "WRZ", "x3d", "X3D" };
filters = { "VRML 1.0/2.0 (*.wrl;*.WRL;*.wrz;*.WRZ)|*.wrl;*.WRL;*.wrz;*.WRZ",
"X3D (*.x3d;*.X3D)|*.x3d;*.X3D" };
#endif
return;
}
} file_data;
@ -121,31 +104,31 @@ static struct FILE_DATA
int GetNExtensions( void )
{
return NEXTS;
return file_data.extensions.size();
}
char const* GetModelExtension( int aIndex )
{
if( aIndex < 0 || aIndex >= NEXTS )
if( aIndex < 0 || aIndex >= int( file_data.extensions.size() ) )
return NULL;
return file_data.extensions[aIndex];
return file_data.extensions[aIndex].c_str();
}
int GetNFilters( void )
{
return NFILS;
return file_data.filters.size();
}
char const* GetFileFilter( int aIndex )
{
if( aIndex < 0 || aIndex >= NFILS )
if( aIndex < 0 || aIndex >= int( file_data.filters.size() ) )
return NULL;
return file_data.filters[aIndex];
return file_data.filters[aIndex].c_str();
}
@ -179,6 +162,39 @@ SCENEGRAPH* LoadVRML( const wxString& aFileName, bool useInline )
{
FILE_LINE_READER* modelFile = NULL;
SCENEGRAPH* scene = NULL;
wxString filename = aFileName;
wxFileName tmpfilename;
if( aFileName.Upper().EndsWith( "WRZ" ) )
{
wxFileInputStream ifile( aFileName );
tmpfilename = wxFileName( aFileName );
tmpfilename.SetExt( "WRL" );
wxFileOffset size = ifile.GetLength();
if( size == wxInvalidOffset )
return nullptr;
{
wxFileOutputStream ofile( tmpfilename.GetFullPath() );
if( !ofile.IsOk() )
return nullptr;
char *buffer = new char[size];
ifile.Read( buffer, size);
std::string expanded = gzip::decompress( buffer, size );
delete buffer;
ofile.Write( expanded.data(), expanded.size() );
ofile.Close();
}
filename = tmpfilename.GetFullPath();
}
try
{
@ -196,6 +212,10 @@ SCENEGRAPH* LoadVRML( const wxString& aFileName, bool useInline )
// VRML file processor
WRLPROC proc( modelFile );
// Cleanup our temporary file
if( tmpfilename.IsOk() )
wxRemoveFile( tmpfilename.GetFullPath() );
if( proc.GetVRMLType() == VRML_V1 )
{
wxLogTrace( MASK_VRML, " * [INFO] Processing VRML 1.0 file\n" );

View File

@ -25,6 +25,7 @@ add_subdirectory( clipper )
add_subdirectory( compoundfilereader )
add_subdirectory( delaunator )
add_subdirectory( dxflib_qcad )
add_subdirectory( gzip-hpp )
add_subdirectory( lemon )
add_subdirectory( libcontext )
add_subdirectory( markdown2html )

7
thirdparty/gzip-hpp/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,7 @@
add_library( gzip-hpp INTERFACE )
target_include_directories( gzip-hpp INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} )
target_sources( gzip-hpp INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/decompress.hpp
)

11
thirdparty/gzip-hpp/LICENSE.BSD2 vendored Normal file
View File

@ -0,0 +1,11 @@
Copyright (c) 2017, Mapbox Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

113
thirdparty/gzip-hpp/decompress.hpp vendored Normal file
View File

@ -0,0 +1,113 @@
#ifndef ZLIB_CONST
#define ZLIB_CONST
#endif
// zlib
#include <zlib.h>
// std
#include <limits>
#include <stdexcept>
#include <string>
namespace gzip {
inline void decompress(const char* data,
std::size_t size,
std::string& output,
std::size_t max_uncompressed_size = 0,
std::size_t buffering_size = 0)
{
if (buffering_size == 0)
{
buffering_size = (size * 2) - (size / 2) + 16;
}
z_stream inflate_s;
inflate_s.zalloc = Z_NULL;
inflate_s.zfree = Z_NULL;
inflate_s.opaque = Z_NULL;
inflate_s.avail_in = 0;
inflate_s.next_in = Z_NULL;
// The windowBits parameter is the base two logarithm of the window size (the size of the history buffer).
// It should be in the range 8..15 for this version of the library.
// Larger values of this parameter result in better compression at the expense of memory usage.
// This range of values also changes the decoding type:
// -8 to -15 for raw deflate
// 8 to 15 for zlib
// (8 to 15) + 16 for gzip
// (8 to 15) + 32 to automatically detect gzip/zlib header
constexpr int window_bits = 15 + 32; // auto with windowbits of 15
constexpr unsigned int max_uint = std::numeric_limits<unsigned int>::max();
const unsigned int size_step = buffering_size > max_uint ? max_uint : static_cast<unsigned int>(buffering_size);
if( max_uncompressed_size != 0 && size_step > max_uncompressed_size )
{
throw std::runtime_error(
"buffer size used during decompression of gzip will use more memory then allowed, "
"either increase the limit or reduce the buffer size" );
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
if (inflateInit2(&inflate_s, window_bits) != Z_OK)
{
throw std::runtime_error("inflate init failed");
}
#pragma GCC diagnostic pop
inflate_s.next_in = reinterpret_cast<z_const Bytef*>(data);
inflate_s.avail_in = static_cast<unsigned int>(size);
std::string buffer(static_cast<std::size_t>(size_step), char());
do
{
inflate_s.avail_out = size_step;
inflate_s.next_out = reinterpret_cast<Bytef*>(&buffer[0]);
const int ret = inflate(&inflate_s, Z_FINISH);
if (ret != Z_STREAM_END && ret != Z_OK && ret != Z_BUF_ERROR)
{
std::string error_msg = inflate_s.msg;
inflateEnd(&inflate_s);
throw std::runtime_error(error_msg);
}
if (max_uncompressed_size != 0 && (output.size() + size_step - inflate_s.avail_out) > max_uncompressed_size)
{
inflateEnd(&inflate_s);
throw std::runtime_error("size of output string will use more memory then intended when decompressing");
}
output.append(buffer, 0, size_step - inflate_s.avail_out);
} while (inflate_s.avail_out == 0);
const int ret2 = inflateEnd(&inflate_s);
if (ret2 != Z_OK)
{
throw std::runtime_error("Unexpected gzip decompression error, state of stream was inconsistent");
}
}
inline void decompress(std::string const& input,
std::string& output,
std::size_t max_uncompressed_size = 0,
std::size_t buffering_size = 0)
{
return decompress(input.data(), input.size(), output, max_uncompressed_size, buffering_size);
}
inline std::string decompress(const char* data,
std::size_t size,
std::size_t max_uncompressed_size = 0,
std::size_t buffering_size = 0)
{
std::string output;
decompress(data, size, output, max_uncompressed_size, buffering_size);
return output;
}
inline std::string decompress(std::string const& input,
std::size_t max_uncompressed_size = 0,
std::size_t buffering_size = 0)
{
std::string output;
decompress(input.data(), input.size(), output, max_uncompressed_size, buffering_size);
return output;
}
} // namespace gzip