From 375e94dc3a29603a643ca5992d5a1ea9f0571564 Mon Sep 17 00:00:00 2001 From: Kevin Martin Date: Sun, 26 Mar 2023 09:12:51 -0400 Subject: [PATCH 1/4] Fix encoding error for import from older DXF files The C++ importer incorrectly treated the contents of all TEXT and MTEXT objects as beind encoded as UTF-8, but this is not true for DXF files before AutoCAD 2007, where the encoding is "plain ASCII" plus some in-band \U+dddd encoding. This would cause errors if the text contained non-ASCII characters such as the Degree Sign. This change causes the correct encoding to be used. --- src/Mod/Import/App/dxf/dxf.cpp | 142 +++++++++++++++++++++++++++++++-- src/Mod/Import/App/dxf/dxf.h | 28 +++++++ 2 files changed, 165 insertions(+), 5 deletions(-) diff --git a/src/Mod/Import/App/dxf/dxf.cpp b/src/Mod/Import/App/dxf/dxf.cpp index b20d39b837..812fe1a6d3 100644 --- a/src/Mod/Import/App/dxf/dxf.cpp +++ b/src/Mod/Import/App/dxf/dxf.cpp @@ -18,7 +18,7 @@ #include #include #include - +#include #include "dxf.h" @@ -1766,11 +1766,16 @@ CDxfRead::CDxfRead(const char* filepath) } m_ifs->imbue(std::locale("C")); + m_version = RUnknown; + m_CodePage = NULL; + m_encoding = NULL; } CDxfRead::~CDxfRead() { delete m_ifs; + delete m_CodePage; + delete m_encoding; } double CDxfRead::mm( double value ) const @@ -2418,8 +2423,12 @@ bool CDxfRead::ReadText() // that ReadText() and all the other Read... methods return having already read a code 0. get_line(); DerefACI(); - textPrefix.append(m_str); - OnReadText(c, height * 25.4 / 72.0, textPrefix.c_str()); + { + const char* utfStr = (this->*stringToUTF8)(m_str); + OnReadText(c, height * 25.4 / 72.0, utfStr); + if (utfStr != m_str) + delete utfStr; + } return(true); case 62: @@ -3271,7 +3280,118 @@ bool CDxfRead::ReadLayer() return false; } -void CDxfRead::DoRead(const bool ignore_errors /* = false */ ) +bool CDxfRead::ReadVersion() +{ + static const std::vector VersionNames = { + // This table is indexed by eDXFVersion_t - (ROlder+1) + "AC1006", + "AC1009", + "AC1012", + "AC1014", + "AC1015", + "AC1018", + "AC1021", + "AC1024", + "AC1027", + "AC1032"}; + + assert(VersionNames.size() == RNewer - ROlder - 1); + get_line(); + get_line(); + std::vector::const_iterator first = VersionNames.cbegin(); + std::vector::const_iterator last = VersionNames.cend(); + std::vector::const_iterator found = std::lower_bound(first, last, m_str); + if (found == last) + m_version = RNewer; + else if (*found == m_str) + m_version = (eAutoCADVersion_t)(std::distance(first, found) + (ROlder + 1)); + else if (found == first) + m_version = ROlder; + else + m_version = RUnknown; + + return ResolveEncoding(); +} + +bool CDxfRead::ReadDWGCodePage() +{ + get_line(); + get_line(); + assert(m_CodePage == NULL); // If not, we have found two DWGCODEPAGE variables or DoRead was called twice on the same CDxfRead object. + m_CodePage = new std::string(m_str); + + return ResolveEncoding(); +} + +bool CDxfRead::ResolveEncoding() +{ + if (m_encoding != NULL) { + delete m_encoding; + m_encoding = NULL; + } + if (m_version >= R2007) { // Note this does not include RUnknown, but does include RLater + m_encoding = new std::string("utf_8"); + stringToUTF8 = &CDxfRead::UTF8ToUTF8; + } + else if (m_CodePage == NULL) { + // cp1252 + m_encoding = new std::string("cp1252"); + stringToUTF8 = &CDxfRead::GeneralToUTF8; + } + else { + // Names may be of the form "ansi_1252" which we map to "cp1252" but we don't map "ansi_x3xxxx" which means "ascii" + if (strncmp(m_CodePage->c_str(), "ansi_", 5) == 0 + && strncmp(m_CodePage->c_str(), "ansi_x3", 7) != 0) { + std::string* p = new std::string(*m_CodePage); + p->replace(0, 5, "cp"); + m_encoding = p; + } + else + m_encoding = new std::string(*m_CodePage); + // At this point we want to recognize synonyms for "utf_8" and use the custom decoder function. + // This is because this is one of the common cases and our decoder function is a fast no-op. + // We don't actually use the decoder function we get from PyCodec_Decoder because to call it we have to convert the (char *) text into + // a 'bytes' object first so we can pass it to the function using PyObject_Callxxx(), getting the PYObject containing the + // Python string, which we then decode back to UTF-8. It is simpler to call PyUnicode_DecodeXxxx which takes a (const char *) + // and is just a direct c++ callable. + PyObject* pyDecoder = PyCodec_Decoder(m_encoding->c_str()); + if (pyDecoder == NULL) + return false; // A key error exception will have been placed. + PyObject* pyUTF8Decoder = PyCodec_Decoder("utf_8"); + assert(pyUTF8Decoder != NULL); + if (pyDecoder == pyUTF8Decoder) + stringToUTF8 = &CDxfRead::UTF8ToUTF8; + else + stringToUTF8 = &CDxfRead::GeneralToUTF8; + Py_DECREF(pyDecoder); + Py_DECREF(pyUTF8Decoder); + } + return m_encoding != NULL; +} +const char* CDxfRead::UTF8ToUTF8(const char* encoded) const +{ + return encoded; +} +const char* CDxfRead::GeneralToUTF8(const char* encoded) const +{ + PyObject* decoded = PyUnicode_Decode(encoded, strlen(encoded), m_encoding->c_str(), "strict"); + if (decoded == NULL) + return NULL; + Py_ssize_t len; + const char* converted = PyUnicode_AsUTF8AndSize(decoded, &len); + char* result = NULL; + if (converted != NULL) { + // converted only has lifetime of decoded so we must save a copy. + result = (char *)malloc(len + 1); + if (result == NULL) + PyErr_SetString(PyExc_MemoryError, "Out of memory"); + else + memcpy(result, converted, len + 1); + } + Py_DECREF(decoded); + return result; +} +void CDxfRead::DoRead(const bool ignore_errors /* = false */) { m_ignore_errors = ignore_errors; if(m_fail) @@ -3298,7 +3418,19 @@ void CDxfRead::DoRead(const bool ignore_errors /* = false */ ) continue; } // End if - then - else if(!strcmp(m_str, "0")) + if (!strcmp(m_str, "$ACADVER")) { + if (!ReadVersion()) + return; + continue; + }// End if - then + + if (!strcmp(m_str, "$DWGCODEPAGE")) { + if (!ReadDWGCodePage()) + return; + continue; + }// End if - then + + if (!strcmp(m_str, "0")) { get_line(); if (!strcmp( m_str, "SECTION" )){ diff --git a/src/Mod/Import/App/dxf/dxf.h b/src/Mod/Import/App/dxf/dxf.h index 401ecb908b..6980bbd166 100644 --- a/src/Mod/Import/App/dxf/dxf.h +++ b/src/Mod/Import/App/dxf/dxf.h @@ -118,6 +118,22 @@ struct LWPolyDataOut std::vector Bulge; point3D Extr; }; +typedef enum +{ + RUnknown, + ROlder, + R10, + R11_12, + R13, + R14, + R2000, + R2004, + R2007, + R2010, + R2013, + R2018, + RNewer, +} eAutoCADVersion_t; //******************** class CDxfWrite{ @@ -277,6 +293,9 @@ private: bool ReadInsert(); bool ReadDimension(); bool ReadBlockInfo(); + bool ReadVersion(); + bool ReadDWGCodePage(); + bool ResolveEncoding(); void get_line(); void put_line(const char *value); @@ -284,6 +303,15 @@ private: protected: Aci_t m_aci; // manifest color name or 256 for layer color + eAutoCADVersion_t m_version;// Version from $ACADVER variable in DXF + const char* (CDxfRead::*stringToUTF8)(const char*) const; + +private: + const std::string* m_CodePage; // Code Page name from $DWGCODEPAGE or null if none/not read yet + // The following was going to be python's canonical name for the encoding, but this is (a) not easily found and (b) does not speed up finding the encoding object. + const std::string* m_encoding;// A name for the encoding implied by m_version and m_CodePage + const char* UTF8ToUTF8(const char* encoded) const; + const char* GeneralToUTF8(const char* encoded) const; public: ImportExport CDxfRead(const char* filepath); // this opens the file From 8b36716bf859fd0622f76e1a45965449c4eddedc Mon Sep 17 00:00:00 2001 From: Kevin Martin Date: Mon, 27 Mar 2023 09:11:52 -0400 Subject: [PATCH 2/4] Remove reference to a trademarked name --- src/Mod/Import/App/dxf/dxf.cpp | 2 +- src/Mod/Import/App/dxf/dxf.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mod/Import/App/dxf/dxf.cpp b/src/Mod/Import/App/dxf/dxf.cpp index 812fe1a6d3..a12e46ab7a 100644 --- a/src/Mod/Import/App/dxf/dxf.cpp +++ b/src/Mod/Import/App/dxf/dxf.cpp @@ -3304,7 +3304,7 @@ bool CDxfRead::ReadVersion() if (found == last) m_version = RNewer; else if (*found == m_str) - m_version = (eAutoCADVersion_t)(std::distance(first, found) + (ROlder + 1)); + m_version = (eDXFVersion_t)(std::distance(first, found) + (ROlder + 1)); else if (found == first) m_version = ROlder; else diff --git a/src/Mod/Import/App/dxf/dxf.h b/src/Mod/Import/App/dxf/dxf.h index 6980bbd166..07ec805ee0 100644 --- a/src/Mod/Import/App/dxf/dxf.h +++ b/src/Mod/Import/App/dxf/dxf.h @@ -133,7 +133,7 @@ typedef enum R2013, R2018, RNewer, -} eAutoCADVersion_t; +} eDXFVersion_t; //******************** class CDxfWrite{ @@ -303,7 +303,7 @@ private: protected: Aci_t m_aci; // manifest color name or 256 for layer color - eAutoCADVersion_t m_version;// Version from $ACADVER variable in DXF + eDXFVersion_t m_version;// Version from $ACADVER variable in DXF const char* (CDxfRead::*stringToUTF8)(const char*) const; private: From 4f2412784862753408e4abfc3bee8910ec61e893 Mon Sep 17 00:00:00 2001 From: Kevin Martin Date: Mon, 27 Mar 2023 09:48:16 -0400 Subject: [PATCH 3/4] Replace "aci" with the lesss cryptic "colorIndex" The "a" in "aci" refers to a trademark which we want to avoid mentioning in code. These values are indices into a color table (plus two special values for BYBLOCK and BYLAYER) so using the term colorIndex seems more readable. --- src/Mod/Import/App/dxf/dxf.cpp | 76 +++++++++++++++++----------------- src/Mod/Import/App/dxf/dxf.h | 11 ++--- 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/src/Mod/Import/App/dxf/dxf.cpp b/src/Mod/Import/App/dxf/dxf.cpp index a12e46ab7a..fa83d52ae7 100644 --- a/src/Mod/Import/App/dxf/dxf.cpp +++ b/src/Mod/Import/App/dxf/dxf.cpp @@ -1750,7 +1750,7 @@ CDxfRead::CDxfRead(const char* filepath) memset( m_str, '\0', sizeof(m_str) ); memset( m_unused_line, '\0', sizeof(m_unused_line) ); m_fail = false; - m_aci = 0; + m_ColorIndex = 0; m_eUnits = eMillimeters; m_measurement_inch = false; strcpy(m_layer_name, "0"); // Default layer name @@ -1841,7 +1841,7 @@ bool CDxfRead::ReadLine() switch(n){ case 0: // next item found, so finish with line - DerefACI(); + ResolveColorIndex(); OnReadLine(s, e, hidden); hidden = false; return true; @@ -1889,7 +1889,7 @@ bool CDxfRead::ReadLine() case 62: // color index get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; + ss.str(m_str); ss >> m_ColorIndex; if(ss.fail()) return false; break; case 100: @@ -1908,7 +1908,7 @@ bool CDxfRead::ReadLine() } try { - DerefACI(); + ResolveColorIndex(); OnReadLine(s, e, false); } catch(...) @@ -1939,7 +1939,7 @@ bool CDxfRead::ReadPoint() switch(n){ case 0: // next item found, so finish with line - DerefACI(); + ResolveColorIndex(); OnReadPoint(s); return true; @@ -1967,7 +1967,7 @@ bool CDxfRead::ReadPoint() case 62: // color index get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; + ss.str(m_str); ss >> m_ColorIndex; if(ss.fail()) return false; break; case 100: @@ -1987,7 +1987,7 @@ bool CDxfRead::ReadPoint() } try { - DerefACI(); + ResolveColorIndex(); OnReadPoint(s); } catch(...) @@ -2022,7 +2022,7 @@ bool CDxfRead::ReadArc() switch(n){ case 0: // next item found, so finish with arc - DerefACI(); + ResolveColorIndex(); OnReadArc(start_angle, end_angle, radius, c,z_extrusion_dir, hidden); hidden = false; return true; @@ -2070,7 +2070,7 @@ bool CDxfRead::ReadArc() case 62: // color index get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; + ss.str(m_str); ss >> m_ColorIndex; if(ss.fail()) return false; break; @@ -2094,7 +2094,7 @@ bool CDxfRead::ReadArc() break; } } - DerefACI(); + ResolveColorIndex(); OnReadArc(start_angle, end_angle, radius, c, z_extrusion_dir, false); return false; } @@ -2127,7 +2127,7 @@ bool CDxfRead::ReadSpline() switch(n){ case 0: // next item found, so finish with Spline - DerefACI(); + ResolveColorIndex(); OnReadSpline(sd); return true; case 8: // Layer name follows @@ -2137,7 +2137,7 @@ bool CDxfRead::ReadSpline() case 62: // color index get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; + ss.str(m_str); ss >> m_ColorIndex; if(ss.fail()) return false; break; case 210: // normal x @@ -2275,7 +2275,7 @@ bool CDxfRead::ReadSpline() break; } } - DerefACI(); + ResolveColorIndex(); OnReadSpline(sd); return false; } @@ -2301,7 +2301,7 @@ bool CDxfRead::ReadCircle() switch(n){ case 0: // next item found, so finish with Circle - DerefACI(); + ResolveColorIndex(); OnReadCircle(c, radius, hidden); hidden = false; return true; @@ -2339,7 +2339,7 @@ bool CDxfRead::ReadCircle() case 62: // color index get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; + ss.str(m_str); ss >> m_ColorIndex; if(ss.fail()) return false; break; case 100: @@ -2356,7 +2356,7 @@ bool CDxfRead::ReadCircle() break; } } - DerefACI(); + ResolveColorIndex(); OnReadCircle(c, radius, false); return false; } @@ -2422,7 +2422,7 @@ bool CDxfRead::ReadText() // all code 0 records. Changing this would require either some sort of peek/pushback ability or the understanding // that ReadText() and all the other Read... methods return having already read a code 0. get_line(); - DerefACI(); + ResolveColorIndex(); { const char* utfStr = (this->*stringToUTF8)(m_str); OnReadText(c, height * 25.4 / 72.0, utfStr); @@ -2434,7 +2434,7 @@ bool CDxfRead::ReadText() case 62: // color index get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; + ss.str(m_str); ss >> m_ColorIndex; if(ss.fail()) return false; break; case 100: @@ -2478,7 +2478,7 @@ bool CDxfRead::ReadEllipse() switch(n){ case 0: // next item found, so finish with Ellipse - DerefACI(); + ResolveColorIndex(); OnReadEllipse(c, m, ratio, start, end); return true; case 8: // Layer name follows @@ -2534,7 +2534,7 @@ bool CDxfRead::ReadEllipse() case 62: // color index get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; + ss.str(m_str); ss >> m_ColorIndex; if(ss.fail()) return false; break; case 100: case 210: @@ -2549,7 +2549,7 @@ bool CDxfRead::ReadEllipse() break; } } - DerefACI(); + ResolveColorIndex(); OnReadEllipse(c, m, ratio, start, end); return false; } @@ -2649,7 +2649,7 @@ bool CDxfRead::ReadLwPolyLine() case 0: // next item found - DerefACI(); + ResolveColorIndex(); if(x_found && y_found){ // add point AddPolyLinePoint(this, x, y, z, bulge_found, bulge); @@ -2703,7 +2703,7 @@ bool CDxfRead::ReadLwPolyLine() case 62: // color index get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; + ss.str(m_str); ss >> m_ColorIndex; if(ss.fail()) return false; break; default: // skip the next line @@ -2717,7 +2717,7 @@ bool CDxfRead::ReadLwPolyLine() if(closed && poly_first_found) { // repeat the first point - DerefACI(); + ResolveColorIndex(); AddPolyLinePoint(this, poly_first_x, poly_first_y, poly_first_z, false, 0.0); } return true; @@ -2753,7 +2753,7 @@ bool CDxfRead::ReadVertex(double *pVertex, bool *bulge_found, double *bulge) ss.imbue(std::locale("C")); switch(n){ case 0: - DerefACI(); + ResolveColorIndex(); put_line(m_str); // read one line too many. put it back. return(x_found && y_found); break; @@ -2789,7 +2789,7 @@ bool CDxfRead::ReadVertex(double *pVertex, bool *bulge_found, double *bulge) case 62: // color index get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; + ss.str(m_str); ss >> m_ColorIndex; if(ss.fail()) return false; break; default: @@ -2829,7 +2829,7 @@ bool CDxfRead::ReadPolyLine() switch(n){ case 0: // next item found - DerefACI(); + ResolveColorIndex(); get_line(); if (! strcmp(m_str,"VERTEX")) { @@ -2864,7 +2864,7 @@ bool CDxfRead::ReadPolyLine() case 62: // color index get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; + ss.str(m_str); ss >> m_ColorIndex; if(ss.fail()) return false; break; default: // skip the next line @@ -2952,7 +2952,7 @@ bool CDxfRead::ReadInsert() switch(n){ case 0: // next item found - DerefACI(); + ResolveColorIndex(); OnReadInsert(c, s, name, rot * M_PI/180); return(true); case 8: @@ -3003,7 +3003,7 @@ bool CDxfRead::ReadInsert() case 62: // color index get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; + ss.str(m_str); ss >> m_ColorIndex; if(ss.fail()) return false; break; case 100: case 39: @@ -3044,7 +3044,7 @@ bool CDxfRead::ReadDimension() switch(n){ case 0: // next item found - DerefACI(); + ResolveColorIndex(); OnReadDimension(s, e, p, rot * M_PI/180); return(true); case 8: @@ -3105,7 +3105,7 @@ bool CDxfRead::ReadDimension() case 62: // color index get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; + ss.str(m_str); ss >> m_ColorIndex; if(ss.fail()) return false; break; case 100: case 39: @@ -3225,7 +3225,7 @@ bool CDxfRead::ReadUnits() bool CDxfRead::ReadLayer() { std::string layername; - int aci = -1; + ColorIndex_t colorIndex = -1; while(!((*m_ifs).eof())) { @@ -3247,7 +3247,7 @@ bool CDxfRead::ReadLayer() printf("CDxfRead::ReadLayer() - no layer name\n"); return false; } - m_layer_aci[layername] = aci; + m_layer_ColorIndex_map[layername] = colorIndex; return true; case 2: // Layer name follows @@ -3258,7 +3258,7 @@ bool CDxfRead::ReadLayer() case 62: // layer color ; if negative, layer is off get_line(); - if(sscanf(m_str, "%d", &aci) != 1) + if(sscanf(m_str, "%d", &colorIndex) != 1) return false; break; @@ -3575,12 +3575,12 @@ void CDxfRead::DoRead(const bool ignore_errors /* = false */) } -void CDxfRead::DerefACI() +void CDxfRead::ResolveColorIndex() { - if (m_aci == 256) // if color = layer color, replace by color from layer + if (m_ColorIndex == ColorBylayer) // if color = layer color, replace by color from layer { - m_aci = m_layer_aci[std::string(m_layer_name)]; + m_ColorIndex = m_layer_ColorIndex_map[std::string(m_layer_name)]; } } diff --git a/src/Mod/Import/App/dxf/dxf.h b/src/Mod/Import/App/dxf/dxf.h index 07ec805ee0..8ab4d3b6d4 100644 --- a/src/Mod/Import/App/dxf/dxf.h +++ b/src/Mod/Import/App/dxf/dxf.h @@ -26,7 +26,7 @@ #define HAVE_IOSTREAM #endif -typedef int Aci_t; // AutoCAD color index +typedef int ColorIndex_t; // DXF color index typedef enum { @@ -272,8 +272,9 @@ private: bool m_ignore_errors; - typedef std::map< std::string,Aci_t > LayerAciMap_t; - LayerAciMap_t m_layer_aci; // layer names -> layer color aci map + std::map m_layer_ColorIndex_map; // Mapping from layer name -> layer color index + const ColorIndex_t ColorBylayer = 256; + const ColorIndex_t ColorByBlock = 0; bool ReadUnits(); bool ReadLayer(); @@ -299,10 +300,10 @@ private: void get_line(); void put_line(const char *value); - void DerefACI(); + void ResolveColorIndex(); protected: - Aci_t m_aci; // manifest color name or 256 for layer color + ColorIndex_t m_ColorIndex; eDXFVersion_t m_version;// Version from $ACADVER variable in DXF const char* (CDxfRead::*stringToUTF8)(const char*) const; From 9703853ee17ab912549eff8b66af14f64815177a Mon Sep 17 00:00:00 2001 From: Kevin Martin Date: Wed, 29 Mar 2023 16:09:35 -0400 Subject: [PATCH 4/4] Add GIL locking for Python calls, alter cp mapping Required GIL locking has been added where Python methods are called. The code mapping from codepage names like ansi_nnn to cpnnnn has been altered a bit to shorten it. --- src/Mod/Import/App/dxf/dxf.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Mod/Import/App/dxf/dxf.cpp b/src/Mod/Import/App/dxf/dxf.cpp index fa83d52ae7..9dd9206fe8 100644 --- a/src/Mod/Import/App/dxf/dxf.cpp +++ b/src/Mod/Import/App/dxf/dxf.cpp @@ -3339,21 +3339,19 @@ bool CDxfRead::ResolveEncoding() stringToUTF8 = &CDxfRead::GeneralToUTF8; } else { - // Names may be of the form "ansi_1252" which we map to "cp1252" but we don't map "ansi_x3xxxx" which means "ascii" - if (strncmp(m_CodePage->c_str(), "ansi_", 5) == 0 - && strncmp(m_CodePage->c_str(), "ansi_x3", 7) != 0) { - std::string* p = new std::string(*m_CodePage); + // Codepage names may be of the form "ansi_1252" which we map to "cp1252" but we don't map "ansi_x3xxxx" (which happens to mean "ascii") + std::string* p = new std::string(*m_CodePage); + if (strncmp(p->c_str(), "ansi_", 5) == 0 + && strncmp(p->c_str(), "ansi_x3", 7) != 0) p->replace(0, 5, "cp"); - m_encoding = p; - } - else - m_encoding = new std::string(*m_CodePage); + m_encoding = p; // At this point we want to recognize synonyms for "utf_8" and use the custom decoder function. // This is because this is one of the common cases and our decoder function is a fast no-op. // We don't actually use the decoder function we get from PyCodec_Decoder because to call it we have to convert the (char *) text into // a 'bytes' object first so we can pass it to the function using PyObject_Callxxx(), getting the PYObject containing the // Python string, which we then decode back to UTF-8. It is simpler to call PyUnicode_DecodeXxxx which takes a (const char *) // and is just a direct c++ callable. + Base::PyGILStateLocker lock; PyObject* pyDecoder = PyCodec_Decoder(m_encoding->c_str()); if (pyDecoder == NULL) return false; // A key error exception will have been placed. @@ -3374,6 +3372,7 @@ const char* CDxfRead::UTF8ToUTF8(const char* encoded) const } const char* CDxfRead::GeneralToUTF8(const char* encoded) const { + Base::PyGILStateLocker lock; PyObject* decoded = PyUnicode_Decode(encoded, strlen(encoded), m_encoding->c_str(), "strict"); if (decoded == NULL) return NULL;