From e3d5f92a9f9bbbe9fd6af90bc6105260a9e70229 Mon Sep 17 00:00:00 2001
From: Sebastião Guerra <sebastiao.luiz.guerra@gmail.com>
Date: Sat, 3 Oct 2026 22:00:40 -0300
Subject: Reject lone surrogates converting TQString to Python
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Decode TQString UTF-8 with Python's strict decoder. Valid surrogate pairs become one supplementary Python code point, while lone surrogate encodings fail with UnicodeDecodeError. Python-to-TQString conversion is already in master.

Signed-off-by: Sebastião Guerra <sebastiao.luiz.guerra@gmail.com>
---
 sip/tqt/tqstring.sip | 43 ++++++-------------------------------------
 1 file changed, 6 insertions(+), 37 deletions(-)

diff --git a/sip/tqt/tqstring.sip b/sip/tqt/tqstring.sip
index 108706c..162913a 100644
--- a/sip/tqt/tqstring.sip
+++ b/sip/tqt/tqstring.sip
@@ -750,44 +750,13 @@ extern PyObject *PyTQt_tqt_PyObject_FromTQString(const TQString *tqstr);
 // Convert a TQString to a Python Unicode object.
 PyObject *PyTQt_tqt_PyObject_FromTQString(const TQString *tqstr)
 {
-    PyObject *obj;
+    TQCString utf8 = tqstr->utf8();
 
-    // We have to work out exactly which kind to use.  We assume ASCII while we
-    // are checking so that we only go through the string once in the most
-    // common case.  Note that we can't use PyUnicode_FromKindAndData() because
-    // it doesn't handle surrogates in UCS2 strings.
-
-    int tqt_len = tqstr->length();
-    Py_UCS4 maxchar = 0x007f;
-
-    for (int tqt_i = 0; tqt_i < tqt_len && maxchar < 0xffff; ++tqt_i)
-    {
-        Py_UCS4 uch = tqstr->at(tqt_i).unicode();
-
-        if (uch > 0x00ff)
-        {
-            maxchar = 0xffff;
-        }
-        else if (uch > 0x007f)
-        {
-            maxchar = 0x00ff;
-        }
-    }
-
-    // Create the correctly sized object.
-    if (!(obj = PyUnicode_New(tqt_len, maxchar)))
-        return nullptr;
-
-    int kind = PyUnicode_KIND(obj);
-    void *data = PyUnicode_DATA(obj);
-
-    for (int tqt_i = 0; tqt_i < tqt_len; ++tqt_i)
-    {
-        Py_UCS4 uch = tqstr->at(tqt_i).unicode();
-        PyUnicode_WRITE(kind, data, tqt_i, uch);
-    }
-
-    return obj;
+    // TQt encodes valid surrogate pairs as normal UTF-8, which Python decodes
+    // as one code point. Lone surrogates produce invalid UTF-8 and are rejected
+    // by the strict decoder. TQCString::length() stops at embedded NULs, so use
+    // size() - 1 to preserve the full payload while excluding the trailing NUL.
+    return PyUnicode_DecodeUTF8(utf8.data(), utf8.size() - 1, nullptr);
 }
 %End
 
-- 
cgit v1.2.3

