[Backport releases/FreeCAD-1-0] Import: DXF, handle non-standard 8859_1 encoding (#22840)
* Import: DXF, handle non-standard 8859_1 encoding (#21905)
* Import: improve DXF importer exception handling
* Import: DXF, pinpoint erroneous header section when reading
* Import: tackle 8859_1 non-standard encoding name
(cherry picked from commit 1e29a3273f)
* Use `Base::RuntimeError` instead of `Base::Exception` since it's constructor is protected in 1.0
---------
Co-authored-by: Furgo <[email protected]>
Co-authored-by: Adrian Insaurralde Avalos <[email protected]>
This commit is contained in:
co-authored by
Furgo
Adrian Insaurralde Avalos
parent
34b70663d1
commit
199b4c81d1
@@ -12,11 +12,14 @@
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
#include "dxf.h"
|
||||
#include <App/Application.h>
|
||||
#include <App/Color.h>
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/FileInfo.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include <Base/Stream.h>
|
||||
@@ -2564,6 +2567,12 @@ bool CDxfRead::ResolveEncoding()
|
||||
// Also some DXF files have the codepage name in uppercase so we lowercase it.
|
||||
m_encoding = m_CodePage;
|
||||
std::transform(m_encoding.begin(), m_encoding.end(), m_encoding.begin(), ::tolower);
|
||||
|
||||
// Add mapping for common non-standard encoding names.
|
||||
if (m_encoding == "8859_1") {
|
||||
m_encoding = "iso-8859-1"; // Replace with a name Python understands
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability/nolint)
|
||||
#define ANSI_ENCODING_PREFIX "ansi_" // NOLINT(cppcoreguidelines-macro-usage)
|
||||
if (m_encoding.rfind(ANSI_ENCODING_PREFIX, 0) == 0 && m_encoding.rfind("ansi_x3", 0) != 0) {
|
||||
@@ -2579,7 +2588,11 @@ bool CDxfRead::ResolveEncoding()
|
||||
Base::PyGILStateLocker lock;
|
||||
PyObject* pyDecoder = PyCodec_Decoder(m_encoding.c_str());
|
||||
if (pyDecoder == nullptr) {
|
||||
return false; // A key error exception will have been placed.
|
||||
// PyCodec_Decoder failed, which means Python could not find the encoding.
|
||||
// This sets a Python LookupError. We clear this low-level error because
|
||||
// our caller will throw a more informative, high-level exception.
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
PyObject* pyUTF8Decoder = PyCodec_Decoder("utf_8");
|
||||
assert(pyUTF8Decoder != nullptr);
|
||||
@@ -2628,64 +2641,98 @@ void CDxfRead::DoRead(const bool ignore_errors /* = false */)
|
||||
return;
|
||||
}
|
||||
|
||||
StartImport();
|
||||
// Loop reading the sections.
|
||||
while (get_next_record()) {
|
||||
if (m_record_type != eObjectType) {
|
||||
ImportError("Found type %d record when expecting start of a SECTION or EOF\n",
|
||||
(int)m_record_type);
|
||||
continue;
|
||||
try {
|
||||
StartImport();
|
||||
// Loop reading the sections.
|
||||
while (get_next_record()) {
|
||||
if (m_record_type != eObjectType) {
|
||||
ImportError("Found type %d record when expecting start of a SECTION or EOF\n",
|
||||
(int)m_record_type);
|
||||
continue;
|
||||
}
|
||||
if (IsObjectName("EOF")) { // TODO: Check for drivel beyond EOF record
|
||||
break;
|
||||
}
|
||||
if (!IsObjectName("SECTION")) {
|
||||
ImportError("Found %s record when expecting start of a SECTION\n",
|
||||
m_record_data.c_str());
|
||||
continue;
|
||||
}
|
||||
if (!ReadSection()) {
|
||||
throw Base::RuntimeError("Failed to read DXF section (returned false).");
|
||||
}
|
||||
}
|
||||
if (IsObjectName("EOF")) { // TODO: Check for drivel beyond EOF record
|
||||
break;
|
||||
}
|
||||
if (!IsObjectName("SECTION")) {
|
||||
ImportError("Found %s record when expecting start of a SECTION\n",
|
||||
m_record_data.c_str());
|
||||
continue;
|
||||
}
|
||||
if (!ReadSection()) {
|
||||
return;
|
||||
FinishImport();
|
||||
|
||||
// Flush out any unsupported features messages
|
||||
if (!m_unsupportedFeaturesNoted.empty()) {
|
||||
ImportError("Unsupported DXF features:\n");
|
||||
for (auto& featureInfo : m_unsupportedFeaturesNoted) {
|
||||
ImportError("%s: %d time(s) first at line %d\n",
|
||||
featureInfo.first,
|
||||
featureInfo.second.first,
|
||||
featureInfo.second.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
FinishImport();
|
||||
|
||||
// FLush out any unsupported features messages
|
||||
if (!m_unsupportedFeaturesNoted.empty()) {
|
||||
ImportError("Unsupported DXF features:\n");
|
||||
for (auto& featureInfo : m_unsupportedFeaturesNoted) {
|
||||
ImportError("%s: %d time(s) first at line %d\n",
|
||||
featureInfo.first,
|
||||
featureInfo.second.first,
|
||||
featureInfo.second.second);
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
// This catches specific FreeCAD exceptions and re-throws them.
|
||||
throw;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
// This catches all standard C++ exceptions and converts them
|
||||
// to a FreeCAD exception, which the binding layer can handle.
|
||||
throw Base::RuntimeError(e.what());
|
||||
}
|
||||
catch (...) {
|
||||
// This is a catch-all for any other non-standard C++ exceptions.
|
||||
throw Base::RuntimeError(
|
||||
"An unknown, non-standard C++ exception occurred during DXF import.");
|
||||
}
|
||||
}
|
||||
|
||||
bool CDxfRead::ReadSection()
|
||||
{
|
||||
if (!get_next_record()) {
|
||||
ImportError("Unclosed SECTION at end of file\n");
|
||||
return false;
|
||||
throw Base::RuntimeError("Unexpected end of file after SECTION tag.");
|
||||
}
|
||||
if (m_record_type != eName) {
|
||||
ImportError("Ignored SECTION with no name record\n");
|
||||
return ReadIgnoredSection();
|
||||
}
|
||||
|
||||
if (IsObjectName("HEADER")) {
|
||||
return ReadHeaderSection();
|
||||
if (!ReadHeaderSection()) {
|
||||
throw Base::RuntimeError("Failed while reading HEADER section.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (IsObjectName("TABLES")) {
|
||||
return ReadTablesSection();
|
||||
if (!ReadTablesSection()) {
|
||||
throw Base::RuntimeError("Failed while reading TABLES section.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (IsObjectName("BLOCKS")) {
|
||||
return ReadBlocksSection();
|
||||
if (!ReadBlocksSection()) {
|
||||
throw Base::RuntimeError("Failed while reading BLOCKS section.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (IsObjectName("ENTITIES")) {
|
||||
return ReadEntitiesSection();
|
||||
if (!ReadEntitiesSection()) {
|
||||
throw Base::RuntimeError("Failed while reading ENTITIES section.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return ReadIgnoredSection();
|
||||
|
||||
if (!ReadIgnoredSection()) {
|
||||
throw Base::RuntimeError("Failed while reading an unknown/ignored section.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDxfRead::ProcessLayerReference(CDxfRead* object, void* target)
|
||||
{
|
||||
if (object->Layers.count(object->m_record_data) == 0) {
|
||||
@@ -2774,11 +2821,18 @@ bool CDxfRead::ReadHeaderSection()
|
||||
if (m_record_type != eVariableName) {
|
||||
continue; // Quietly ignore unknown record types
|
||||
}
|
||||
|
||||
// Store the variable name before we try to read its value.
|
||||
std::string currentVarName = m_record_data;
|
||||
if (!ReadVariable()) {
|
||||
return false;
|
||||
// If ReadVariable returns false, throw an exception with the variable name.
|
||||
throw Base::RuntimeError("Failed while reading value for HEADER variable: "
|
||||
+ currentVarName);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
// If the loop finishes without finding ENDSEC, it's an error.
|
||||
throw Base::RuntimeError("Unexpected end of file inside HEADER section.");
|
||||
}
|
||||
|
||||
bool CDxfRead::ReadVariable()
|
||||
|
||||
Reference in New Issue
Block a user