From 70be68d0fc60a6b962890877b88a2de77d011ab7 Mon Sep 17 00:00:00 2001 From: Tuan Nghia Nguyen Date: Tue, 31 Mar 2026 14:10:21 +1100 Subject: [PATCH] Support UTF8 to UTF16 LE --- modules/ANSFR/ANSFR.cpp | 32 ++++- modules/ANSFR/ANSFR.h | 4 + modules/ANSFR/dllmain.cpp | 97 +++++++++++++ modules/ANSLPR/ANSLPR.cpp | 30 +++- modules/ANSLPR/ANSLPR.h | 4 + modules/ANSLPR/dllmain.cpp | 97 +++++++++++++ modules/ANSOCR/ANSOCRBase.cpp | 59 +++++++- modules/ANSOCR/ANSOCRBase.h | 4 + modules/ANSOCR/dllmain.cpp | 117 ++++++++++++++++ modules/ANSODEngine/ANSEngineCommon.cpp | 174 +++++++++++++++++++++++- modules/ANSODEngine/ANSEngineCommon.h | 15 ++ modules/ANSUtilities/ANSUtilities.cpp | 53 ++++++++ modules/ANSUtilities/ANSUtilities.h | 17 +++ modules/ANSUtilities/dllmain.cpp | 98 +++++++++++++ 14 files changed, 790 insertions(+), 11 deletions(-) diff --git a/modules/ANSFR/ANSFR.cpp b/modules/ANSFR/ANSFR.cpp index 682a45c..69cedff 100644 --- a/modules/ANSFR/ANSFR.cpp +++ b/modules/ANSFR/ANSFR.cpp @@ -1583,7 +1583,7 @@ namespace ANSCENTER { } LogThreadSafe("ANSFacialRecognition::UpdateUser", - "Successfully updated user ID " + std::to_string(userId)); + "Successfully updated user ID " + std::to_string(userId), LogLevel::Info); return result; } @@ -2350,6 +2350,32 @@ namespace ANSCENTER { } // Using nlohmann/json (much faster) + static std::string DoubleEscapeUnicode(const std::string& utf8Str) { + bool hasNonAscii = false; + for (unsigned char c : utf8Str) { + if (c >= 0x80) { hasNonAscii = true; break; } + } + if (!hasNonAscii) return utf8Str; + std::string result; + result.reserve(utf8Str.size() * 2); + size_t i = 0; + while (i < utf8Str.size()) { + unsigned char c = static_cast(utf8Str[i]); + if (c < 0x80) { result += utf8Str[i++]; continue; } + uint32_t cp = 0; + if ((c & 0xE0) == 0xC0 && i + 1 < utf8Str.size()) { + cp = ((c & 0x1F) << 6) | (static_cast(utf8Str[i + 1]) & 0x3F); i += 2; + } else if ((c & 0xF0) == 0xE0 && i + 2 < utf8Str.size()) { + cp = ((c & 0x0F) << 12) | ((static_cast(utf8Str[i + 1]) & 0x3F) << 6) | (static_cast(utf8Str[i + 2]) & 0x3F); i += 3; + } else if ((c & 0xF8) == 0xF0 && i + 3 < utf8Str.size()) { + cp = ((c & 0x07) << 18) | ((static_cast(utf8Str[i + 1]) & 0x3F) << 12) | ((static_cast(utf8Str[i + 2]) & 0x3F) << 6) | (static_cast(utf8Str[i + 3]) & 0x3F); i += 4; + } else { i++; continue; } + if (cp <= 0xFFFF) { char buf[8]; snprintf(buf, sizeof(buf), "\\u%04x", cp); result += buf; } + else { cp -= 0x10000; char buf[16]; snprintf(buf, sizeof(buf), "\\u%04x\\u%04x", 0xD800 + (uint16_t)(cp >> 10), 0xDC00 + (uint16_t)(cp & 0x3FF)); result += buf; } + } + return result; + } + std::string ANSFacialRecognition::FaceObjectsToJsonString(const std::vector& faces) { START_TIMER(json_total); @@ -2361,7 +2387,7 @@ namespace ANSCENTER { for (const auto& face : faces) { results.push_back({ {"user_id", face.userId}, - {"user_name", face.userName}, + {"user_name", DoubleEscapeUnicode(face.userName)}, {"similarity", std::to_string(face.similarity)}, {"is_unknown", std::to_string(face.isUnknown)}, {"prob", std::to_string(face.confidence)}, @@ -2399,7 +2425,7 @@ namespace ANSCENTER { nlohmann::json detectedNode; detectedNode["class_id"] = std::to_string(det.classId); detectedNode["track_id"] = std::to_string(det.trackId); - detectedNode["class_name"] = det.className; + detectedNode["class_name"] = DoubleEscapeUnicode(det.className); detectedNode["prob"] = std::to_string(det.confidence); detectedNode["x"] = std::to_string(det.box.x); detectedNode["y"] = std::to_string(det.box.y); diff --git a/modules/ANSFR/ANSFR.h b/modules/ANSFR/ANSFR.h index 7b683de..b1f1a2f 100644 --- a/modules/ANSFR/ANSFR.h +++ b/modules/ANSFR/ANSFR.h @@ -351,4 +351,8 @@ extern "C" ANSFR_API int GetFaces(ANSCENTER::ANSFacialRecognition * *Handle,i extern "C" ANSFR_API int DeleteFacesByUser(ANSCENTER::ANSFacialRecognition * *Handle,int userId); extern "C" ANSFR_API double BlurCalculation(unsigned char* jpeg_string, unsigned int bufferLength); +// Unicode conversion utilities for LabVIEW wrapper classes +extern "C" ANSFR_API int ANSFR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result); +extern "C" ANSFR_API int ANSFR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result); + #endif diff --git a/modules/ANSFR/dllmain.cpp b/modules/ANSFR/dllmain.cpp index 4e40909..f71ea03 100644 --- a/modules/ANSFR/dllmain.cpp +++ b/modules/ANSFR/dllmain.cpp @@ -750,6 +750,103 @@ extern "C" ANSFR_API double BlurCalculation(unsigned char* jpeg_string, un } +// Unicode conversion utilities for LabVIEW wrapper classes +extern "C" ANSFR_API int ANSFR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result) { + try { + if (!utf8Str || !result) return -1; + int len = (int)strlen(utf8Str); + if (len == 0) return 0; + bool hasUnicodeEscapes = false; + bool hasNonAscii = false; + for (int i = 0; i < len; i++) { + if ((unsigned char)utf8Str[i] >= 0x80) hasNonAscii = true; + if (i + 1 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') hasUnicodeEscapes = true; + } + if (!hasNonAscii && !hasUnicodeEscapes) { + MgErr error = DSSetHandleSize(result, sizeof(int32) + len * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = len; + memcpy((*result)->str, utf8Str, len); + return 1; + } + if (hasUnicodeEscapes) { + std::string utf16le; + utf16le.reserve(len * 2); + for (int i = 0; i < len; ) { + if (i + 5 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') { + char hex[5] = { utf8Str[i + 2], utf8Str[i + 3], utf8Str[i + 4], utf8Str[i + 5], 0 }; + uint16_t cp = (uint16_t)strtoul(hex, nullptr, 16); + utf16le += static_cast(cp & 0xFF); + utf16le += static_cast((cp >> 8) & 0xFF); + i += 6; + } else { + utf16le += utf8Str[i]; + utf16le += '\0'; + i++; + } + } + int size = (int)utf16le.size(); + MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = size; + memcpy((*result)->str, utf16le.data(), size); + return 1; + } +#ifdef _WIN32 + int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, nullptr, 0); + if (wideLen <= 0) return 0; + std::wstring wideStr(wideLen, 0); + MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, &wideStr[0], wideLen); + int size = wideLen * (int)sizeof(wchar_t); + MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = size; + memcpy((*result)->str, wideStr.data(), size); + return 1; +#else + return 0; +#endif + } + catch (...) { return -1; } +} + +extern "C" ANSFR_API int ANSFR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result) { + try { + if (!utf16leBytes || byteLen <= 0 || !result) return -1; + bool isUtf16le = (byteLen >= 2 && byteLen % 2 == 0); + if (isUtf16le) { + bool isAscii = true; + for (int i = 1; i < byteLen; i += 2) { + if (utf16leBytes[i] != 0x00) { isAscii = false; break; } + } + if (isAscii) { + int asciiLen = byteLen / 2; + MgErr error = DSSetHandleSize(result, sizeof(int32) + asciiLen * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = asciiLen; + for (int i = 0; i < asciiLen; i++) (*result)->str[i] = utf16leBytes[i * 2]; + return 1; + } + } +#ifdef _WIN32 + int wideLen = byteLen / (int)sizeof(wchar_t); + const wchar_t* wideStr = reinterpret_cast(utf16leBytes); + int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, nullptr, 0, nullptr, nullptr); + if (utf8Len <= 0) return 0; + std::string utf8Str(utf8Len, 0); + WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, &utf8Str[0], utf8Len, nullptr, nullptr); + MgErr error = DSSetHandleSize(result, sizeof(int32) + utf8Len * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = utf8Len; + memcpy((*result)->str, utf8Str.data(), utf8Len); + return 1; +#else + return 0; +#endif + } + catch (...) { return -1; } +} + extern "C" ANSFR_API int UpdateParameters(ANSCENTER::ANSFacialRecognition** Handle, float knownPersonThreshold, int enableAgeGender, int enableFaceEmotions, int enableHeadPose, int minFaceSize, float faceDetectorThreshold, int enableFaceliveness, int antiSpoof, int removeFakeFaces) { try { if (!Handle || !*Handle) return -1; diff --git a/modules/ANSLPR/ANSLPR.cpp b/modules/ANSLPR/ANSLPR.cpp index e7aacaa..d3710e6 100644 --- a/modules/ANSLPR/ANSLPR.cpp +++ b/modules/ANSLPR/ANSLPR.cpp @@ -408,6 +408,32 @@ namespace ANSCENTER { return result; } + static std::string DoubleEscapeUnicode(const std::string& utf8Str) { + bool hasNonAscii = false; + for (unsigned char c : utf8Str) { + if (c >= 0x80) { hasNonAscii = true; break; } + } + if (!hasNonAscii) return utf8Str; + std::string result; + result.reserve(utf8Str.size() * 2); + size_t i = 0; + while (i < utf8Str.size()) { + unsigned char c = static_cast(utf8Str[i]); + if (c < 0x80) { result += utf8Str[i++]; continue; } + uint32_t cp = 0; + if ((c & 0xE0) == 0xC0 && i + 1 < utf8Str.size()) { + cp = ((c & 0x1F) << 6) | (static_cast(utf8Str[i + 1]) & 0x3F); i += 2; + } else if ((c & 0xF0) == 0xE0 && i + 2 < utf8Str.size()) { + cp = ((c & 0x0F) << 12) | ((static_cast(utf8Str[i + 1]) & 0x3F) << 6) | (static_cast(utf8Str[i + 2]) & 0x3F); i += 3; + } else if ((c & 0xF8) == 0xF0 && i + 3 < utf8Str.size()) { + cp = ((c & 0x07) << 18) | ((static_cast(utf8Str[i + 1]) & 0x3F) << 12) | ((static_cast(utf8Str[i + 2]) & 0x3F) << 6) | (static_cast(utf8Str[i + 3]) & 0x3F); i += 4; + } else { i++; continue; } + if (cp <= 0xFFFF) { char buf[8]; snprintf(buf, sizeof(buf), "\\u%04x", cp); result += buf; } + else { cp -= 0x10000; char buf[16]; snprintf(buf, sizeof(buf), "\\u%04x\\u%04x", 0xD800 + (uint16_t)(cp >> 10), 0xDC00 + (uint16_t)(cp & 0x3FF)); result += buf; } + } + return result; + } + std::string ANSALPR::VectorDetectionToJsonString(const std::vector& dets) { if (dets.empty()) { return R"({"results":[]})"; @@ -421,13 +447,13 @@ namespace ANSCENTER { {"class_id", std::to_string(det.classId)}, //{"track_id", std::to_string(det.trackId)}, {"track_id", std::to_string(0)}, - {"class_name", det.className}, + {"class_name", DoubleEscapeUnicode(det.className)}, {"prob", std::to_string(det.confidence)}, {"x", std::to_string(det.box.x)}, {"y", std::to_string(det.box.y)}, {"width", std::to_string(det.box.width)}, {"height", std::to_string(det.box.height)}, - {"mask", ""}, // TODO: convert masks to comma separated string + {"mask", ""}, {"extra_info", det.extraInfo}, {"camera_id", det.cameraId}, {"polygon", PolygonToString(det.polygon)}, diff --git a/modules/ANSLPR/ANSLPR.h b/modules/ANSLPR/ANSLPR.h index 46824e8..d952b29 100644 --- a/modules/ANSLPR/ANSLPR.h +++ b/modules/ANSLPR/ANSLPR.h @@ -142,4 +142,8 @@ extern "C" ANSLPR_API int ANSALPR_SetFormat(ANSCENTER::ANSALPR** Handle, con extern "C" ANSLPR_API int ANSALPR_SetFormats(ANSCENTER::ANSALPR** Handle, const char* formats);// comma separated formats extern "C" ANSLPR_API int ANSALPR_GetFormats(ANSCENTER::ANSALPR** Handle, LStrHandle formats);// comma separated formats +// Unicode conversion utilities for LabVIEW wrapper classes +extern "C" ANSLPR_API int ANSLPR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result); +extern "C" ANSLPR_API int ANSLPR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result); + #endif \ No newline at end of file diff --git a/modules/ANSLPR/dllmain.cpp b/modules/ANSLPR/dllmain.cpp index f15ef68..5e85ae5 100644 --- a/modules/ANSLPR/dllmain.cpp +++ b/modules/ANSLPR/dllmain.cpp @@ -772,6 +772,103 @@ extern "C" ANSLPR_API int ANSALPR_GetFormats(ANSCENTER::ANSALPR** Handle, LS } +// Unicode conversion utilities for LabVIEW wrapper classes +extern "C" ANSLPR_API int ANSLPR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result) { + try { + if (!utf8Str || !result) return -1; + int len = (int)strlen(utf8Str); + if (len == 0) return 0; + bool hasUnicodeEscapes = false; + bool hasNonAscii = false; + for (int i = 0; i < len; i++) { + if ((unsigned char)utf8Str[i] >= 0x80) hasNonAscii = true; + if (i + 1 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') hasUnicodeEscapes = true; + } + if (!hasNonAscii && !hasUnicodeEscapes) { + MgErr error = DSSetHandleSize(result, sizeof(int32) + len * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = len; + memcpy((*result)->str, utf8Str, len); + return 1; + } + if (hasUnicodeEscapes) { + std::string utf16le; + utf16le.reserve(len * 2); + for (int i = 0; i < len; ) { + if (i + 5 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') { + char hex[5] = { utf8Str[i + 2], utf8Str[i + 3], utf8Str[i + 4], utf8Str[i + 5], 0 }; + uint16_t cp = (uint16_t)strtoul(hex, nullptr, 16); + utf16le += static_cast(cp & 0xFF); + utf16le += static_cast((cp >> 8) & 0xFF); + i += 6; + } else { + utf16le += utf8Str[i]; + utf16le += '\0'; + i++; + } + } + int size = (int)utf16le.size(); + MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = size; + memcpy((*result)->str, utf16le.data(), size); + return 1; + } +#ifdef _WIN32 + int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, nullptr, 0); + if (wideLen <= 0) return 0; + std::wstring wideStr(wideLen, 0); + MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, &wideStr[0], wideLen); + int size = wideLen * (int)sizeof(wchar_t); + MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = size; + memcpy((*result)->str, wideStr.data(), size); + return 1; +#else + return 0; +#endif + } + catch (...) { return -1; } +} + +extern "C" ANSLPR_API int ANSLPR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result) { + try { + if (!utf16leBytes || byteLen <= 0 || !result) return -1; + bool isUtf16le = (byteLen >= 2 && byteLen % 2 == 0); + if (isUtf16le) { + bool isAscii = true; + for (int i = 1; i < byteLen; i += 2) { + if (utf16leBytes[i] != 0x00) { isAscii = false; break; } + } + if (isAscii) { + int asciiLen = byteLen / 2; + MgErr error = DSSetHandleSize(result, sizeof(int32) + asciiLen * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = asciiLen; + for (int i = 0; i < asciiLen; i++) (*result)->str[i] = utf16leBytes[i * 2]; + return 1; + } + } +#ifdef _WIN32 + int wideLen = byteLen / (int)sizeof(wchar_t); + const wchar_t* wideStr = reinterpret_cast(utf16leBytes); + int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, nullptr, 0, nullptr, nullptr); + if (utf8Len <= 0) return 0; + std::string utf8Str(utf8Len, 0); + WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, &utf8Str[0], utf8Len, nullptr, nullptr); + MgErr error = DSSetHandleSize(result, sizeof(int32) + utf8Len * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = utf8Len; + memcpy((*result)->str, utf8Str.data(), utf8Len); + return 1; +#else + return 0; +#endif + } + catch (...) { return -1; } +} + // ============================================================================ // V2 API — accepts uint64_t handle by value (eliminates LabVIEW buffer reuse bug) // ============================================================================ diff --git a/modules/ANSOCR/ANSOCRBase.cpp b/modules/ANSOCR/ANSOCRBase.cpp index 0a5848f..0deb487 100644 --- a/modules/ANSOCR/ANSOCRBase.cpp +++ b/modules/ANSOCR/ANSOCRBase.cpp @@ -177,6 +177,60 @@ namespace ANSCENTER { return false; } } + // Helper: convert non-ASCII UTF-8 chars to literal \\uXXXX text so that + // JSON parsers (e.g., LabVIEW) preserve them as displayable escape sequences. + // Pure ASCII strings pass through unchanged (zero overhead). + static std::string DoubleEscapeUnicode(const std::string& utf8Str) { + bool hasNonAscii = false; + for (unsigned char c : utf8Str) { + if (c >= 0x80) { hasNonAscii = true; break; } + } + if (!hasNonAscii) return utf8Str; + + std::string result; + result.reserve(utf8Str.size() * 2); + size_t i = 0; + while (i < utf8Str.size()) { + unsigned char c = static_cast(utf8Str[i]); + if (c < 0x80) { + result += utf8Str[i++]; + } else { + // Decode UTF-8 codepoint + uint32_t cp = 0; + if ((c & 0xE0) == 0xC0 && i + 1 < utf8Str.size()) { + cp = ((c & 0x1F) << 6) | (static_cast(utf8Str[i + 1]) & 0x3F); + i += 2; + } else if ((c & 0xF0) == 0xE0 && i + 2 < utf8Str.size()) { + cp = ((c & 0x0F) << 12) | ((static_cast(utf8Str[i + 1]) & 0x3F) << 6) + | (static_cast(utf8Str[i + 2]) & 0x3F); + i += 3; + } else if ((c & 0xF8) == 0xF0 && i + 3 < utf8Str.size()) { + cp = ((c & 0x07) << 18) | ((static_cast(utf8Str[i + 1]) & 0x3F) << 12) + | ((static_cast(utf8Str[i + 2]) & 0x3F) << 6) + | (static_cast(utf8Str[i + 3]) & 0x3F); + i += 4; + } else { + i++; continue; // skip invalid byte + } + // Encode as \\uXXXX (literal backslash + u + 4 hex digits) + if (cp <= 0xFFFF) { + char buf[8]; + snprintf(buf, sizeof(buf), "\\u%04x", cp); + result += buf; + } else { + // Surrogate pair for codepoints > 0xFFFF + cp -= 0x10000; + uint16_t hi = 0xD800 + (uint16_t)(cp >> 10); + uint16_t lo = 0xDC00 + (uint16_t)(cp & 0x3FF); + char buf[16]; + snprintf(buf, sizeof(buf), "\\u%04x\\u%04x", hi, lo); + result += buf; + } + } + } + return result; + } + std::string ANSCENTER::ANSOCRUtility::OCRDetectionToJsonString(const std::vector& dets) { if (dets.empty()) { @@ -191,7 +245,7 @@ namespace ANSCENTER { results.push_back({ {"class_id", std::to_string(det.classId)}, {"track_id", std::to_string(det.trackId)}, - {"class_name", det.className}, + {"class_name", DoubleEscapeUnicode(det.className)}, {"prob", std::to_string(det.confidence)}, {"x", std::to_string(det.box.x)}, {"y", std::to_string(det.box.y)}, @@ -205,7 +259,6 @@ namespace ANSCENTER { }); } - // ensure_ascii=true escapes non-ASCII chars as \uXXXX for LabVIEW compatibility return root.dump(-1, ' ', true); } catch (const std::exception& e) { @@ -846,7 +899,7 @@ namespace ANSCENTER { jsonResults.push_back({ {"class_id", "0"}, {"track_id", "0"}, - {"class_name", res.fullPlateText}, + {"class_name", DoubleEscapeUnicode(res.fullPlateText)}, {"prob", std::to_string(res.confidence)}, {"x", std::to_string(res.plateBox.x)}, {"y", std::to_string(res.plateBox.y)}, diff --git a/modules/ANSOCR/ANSOCRBase.h b/modules/ANSOCR/ANSOCRBase.h index a7460cf..3fa8404 100644 --- a/modules/ANSOCR/ANSOCRBase.h +++ b/modules/ANSOCR/ANSOCRBase.h @@ -243,6 +243,10 @@ extern "C" ANSOCR_API int SetANSOCRMode(ANSCENTER::ANSOCRBase** Handle, extern "C" ANSOCR_API int SetANSOCRCountry(ANSCENTER::ANSOCRBase** Handle, int country); extern "C" ANSOCR_API int SetANSOCRALPRFormat(ANSCENTER::ANSOCRBase** Handle, const char* formatJson); +// Unicode conversion utilities for LabVIEW wrapper classes +extern "C" ANSOCR_API int ANSOCR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result); +extern "C" ANSOCR_API int ANSOCR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result); + // V2 Create / Release — handle as uint64_t by value (no pointer-to-pointer) extern "C" ANSOCR_API uint64_t CreateANSOCRHandleEx_V2(const char* licenseKey, const char* modelFilePath, const char* modelFileZipPassword, int language, int engineMode, int gpuId, diff --git a/modules/ANSOCR/dllmain.cpp b/modules/ANSOCR/dllmain.cpp index 11a4a9f..a36557b 100644 --- a/modules/ANSOCR/dllmain.cpp +++ b/modules/ANSOCR/dllmain.cpp @@ -422,6 +422,123 @@ extern "C" ANSOCR_API int SetANSOCRALPRFormat(ANSCENTER::ANSOCRBase** Handle, co } } +// Unicode conversion utilities for LabVIEW wrapper classes +// Converts input string to UTF-16LE. Handles both: +// - JSON Unicode escapes (\uXXXX) from ensure_ascii=true output +// - Raw UTF-8 encoded strings +// Pure ASCII input is passed through directly (no conversion overhead). +extern "C" ANSOCR_API int ANSOCR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result) { + try { + if (!utf8Str || !result) return -1; + int len = (int)strlen(utf8Str); + if (len == 0) return 0; + + // Check if input contains \uXXXX escapes or non-ASCII bytes + bool hasUnicodeEscapes = false; + bool hasNonAscii = false; + for (int i = 0; i < len; i++) { + if ((unsigned char)utf8Str[i] >= 0x80) hasNonAscii = true; + if (i + 1 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') hasUnicodeEscapes = true; + } + + // Pure ASCII with no escapes — pass through directly + if (!hasNonAscii && !hasUnicodeEscapes) { + MgErr error = DSSetHandleSize(result, sizeof(int32) + len * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = len; + memcpy((*result)->str, utf8Str, len); + return 1; + } + + // If contains \uXXXX escapes, decode them to UTF-16LE directly + if (hasUnicodeEscapes) { + std::string utf16le; + utf16le.reserve(len * 2); + for (int i = 0; i < len; ) { + if (i + 5 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') { + char hex[5] = { utf8Str[i + 2], utf8Str[i + 3], utf8Str[i + 4], utf8Str[i + 5], 0 }; + uint16_t cp = (uint16_t)strtoul(hex, nullptr, 16); + utf16le += static_cast(cp & 0xFF); + utf16le += static_cast((cp >> 8) & 0xFF); + i += 6; + } else { + // ASCII or raw UTF-8 byte — convert as single char + utf16le += utf8Str[i]; + utf16le += '\0'; + i++; + } + } + int size = (int)utf16le.size(); + MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = size; + memcpy((*result)->str, utf16le.data(), size); + return 1; + } + + // Raw UTF-8 — convert via Windows API +#ifdef _WIN32 + int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, nullptr, 0); + if (wideLen <= 0) return 0; + std::wstring wideStr(wideLen, 0); + MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, &wideStr[0], wideLen); + int size = wideLen * (int)sizeof(wchar_t); + MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = size; + memcpy((*result)->str, wideStr.data(), size); + return 1; +#else + return 0; +#endif + } + catch (...) { return -1; } +} + +extern "C" ANSOCR_API int ANSOCR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result) { + try { + if (!utf16leBytes || byteLen <= 0 || !result) return -1; + // Check if input is already pure ASCII (no high bytes, or not valid UTF-16LE) + // If all bytes < 0x80 and byteLen has no null bytes in odd positions pattern, + // treat as already-UTF8 ASCII and pass through + bool isAlreadyAscii = true; + bool isUtf16le = (byteLen >= 2 && byteLen % 2 == 0); + if (isUtf16le) { + // Check if all high bytes (odd indices) are 0x00 — means pure ASCII in UTF-16LE + for (int i = 1; i < byteLen; i += 2) { + if (utf16leBytes[i] != 0x00) { isAlreadyAscii = false; break; } + } + if (isAlreadyAscii) { + // Extract just the low bytes (ASCII characters) + int asciiLen = byteLen / 2; + MgErr error = DSSetHandleSize(result, sizeof(int32) + asciiLen * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = asciiLen; + for (int i = 0; i < asciiLen; i++) { + (*result)->str[i] = utf16leBytes[i * 2]; + } + return 1; + } + } +#ifdef _WIN32 + int wideLen = byteLen / (int)sizeof(wchar_t); + const wchar_t* wideStr = reinterpret_cast(utf16leBytes); + int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, nullptr, 0, nullptr, nullptr); + if (utf8Len <= 0) return 0; + std::string utf8Str(utf8Len, 0); + WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, &utf8Str[0], utf8Len, nullptr, nullptr); + MgErr error = DSSetHandleSize(result, sizeof(int32) + utf8Len * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = utf8Len; + memcpy((*result)->str, utf8Str.data(), utf8Len); + return 1; +#else + return 0; +#endif + } + catch (...) { return -1; } +} + extern "C" ANSOCR_API std::string RunInferenceImagePath(ANSCENTER::ANSOCRBase** Handle, const char* imageFilePath) { if (!Handle || !*Handle) return ""; OCRHandleGuard guard(AcquireOCRHandle(*Handle)); diff --git a/modules/ANSODEngine/ANSEngineCommon.cpp b/modules/ANSODEngine/ANSEngineCommon.cpp index cef4ff3..d90d33f 100644 --- a/modules/ANSODEngine/ANSEngineCommon.cpp +++ b/modules/ANSODEngine/ANSEngineCommon.cpp @@ -1,4 +1,8 @@ #pragma once +#ifdef _WIN32 +#define NOMINMAX +#include +#endif #include "ANSODEngine.h" #include "ANSYOLOOD.h" #include "ANSTENSORRTOD.h" @@ -1078,6 +1082,32 @@ namespace ANSCENTER { return boundingBox; } + static std::string DoubleEscapeUnicode(const std::string& utf8Str) { + bool hasNonAscii = false; + for (unsigned char c : utf8Str) { + if (c >= 0x80) { hasNonAscii = true; break; } + } + if (!hasNonAscii) return utf8Str; + std::string result; + result.reserve(utf8Str.size() * 2); + size_t i = 0; + while (i < utf8Str.size()) { + unsigned char c = static_cast(utf8Str[i]); + if (c < 0x80) { result += utf8Str[i++]; continue; } + uint32_t cp = 0; + if ((c & 0xE0) == 0xC0 && i + 1 < utf8Str.size()) { + cp = ((c & 0x1F) << 6) | (static_cast(utf8Str[i + 1]) & 0x3F); i += 2; + } else if ((c & 0xF0) == 0xE0 && i + 2 < utf8Str.size()) { + cp = ((c & 0x0F) << 12) | ((static_cast(utf8Str[i + 1]) & 0x3F) << 6) | (static_cast(utf8Str[i + 2]) & 0x3F); i += 3; + } else if ((c & 0xF8) == 0xF0 && i + 3 < utf8Str.size()) { + cp = ((c & 0x07) << 18) | ((static_cast(utf8Str[i + 1]) & 0x3F) << 12) | ((static_cast(utf8Str[i + 2]) & 0x3F) << 6) | (static_cast(utf8Str[i + 3]) & 0x3F); i += 4; + } else { i++; continue; } + if (cp <= 0xFFFF) { char buf[8]; snprintf(buf, sizeof(buf), "\\u%04x", cp); result += buf; } + else { cp -= 0x10000; char buf[16]; snprintf(buf, sizeof(buf), "\\u%04x\\u%04x", 0xD800 + (uint16_t)(cp >> 10), 0xDC00 + (uint16_t)(cp & 0x3FF)); result += buf; } + } + return result; + } + std::string ANSCENTER::ANSUtilityHelper::VectorDetectionToJsonString(const std::vector& dets) { if (dets.empty()) { @@ -1091,14 +1121,14 @@ namespace ANSCENTER { {"class_id", std::to_string(det.classId)}, //{"track_id", std::to_string(det.trackId)}, {"track_id", "0"}, - {"class_name", det.className}, + {"class_name", DoubleEscapeUnicode(det.className)}, {"prob", std::to_string(det.confidence)}, {"x", std::to_string(det.box.x)}, {"y", std::to_string(det.box.y)}, {"width", std::to_string(det.box.width)}, {"height", std::to_string(det.box.height)}, - {"mask", ""}, // TODO: convert masks to comma separated string - {"extra_info",det.extraInfo}, + {"mask", ""}, + {"extra_info", det.extraInfo}, {"camera_id", det.cameraId}, {"polygon", PolygonToString(det.polygon)}, {"kps", KeypointsToString(det.kps)} @@ -2212,6 +2242,144 @@ namespace ANSCENTER { return polygon; } +// Unicode conversion utilities for LabVIEW wrapper classes +extern "C" ANSENGINE_API int ANSEngine_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result) { + try { + if (!utf8Str || !result) return -1; + int len = (int)strlen(utf8Str); + if (len == 0) return 0; + bool hasUnicodeEscapes = false; + bool hasNonAscii = false; + for (int i = 0; i < len; i++) { + if ((unsigned char)utf8Str[i] >= 0x80) hasNonAscii = true; + if (i + 1 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') hasUnicodeEscapes = true; + } + if (!hasNonAscii && !hasUnicodeEscapes) { + MgErr error = DSSetHandleSize(result, sizeof(int32) + len * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = len; + memcpy((*result)->str, utf8Str, len); + return 1; + } + if (hasUnicodeEscapes) { + std::string utf16le; + utf16le.reserve(len * 2); + for (int i = 0; i < len; ) { + if (i + 5 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') { + char hex[5] = { utf8Str[i + 2], utf8Str[i + 3], utf8Str[i + 4], utf8Str[i + 5], 0 }; + uint16_t cp = (uint16_t)strtoul(hex, nullptr, 16); + utf16le += static_cast(cp & 0xFF); + utf16le += static_cast((cp >> 8) & 0xFF); + i += 6; + } else { + utf16le += utf8Str[i]; + utf16le += '\0'; + i++; + } + } + int size = (int)utf16le.size(); + MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = size; + memcpy((*result)->str, utf16le.data(), size); + return 1; + } +#ifdef _WIN32 + int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, nullptr, 0); + if (wideLen <= 0) return 0; + std::wstring wideStr(wideLen, 0); + MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, &wideStr[0], wideLen); + int size = wideLen * (int)sizeof(wchar_t); + MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = size; + memcpy((*result)->str, wideStr.data(), size); + return 1; +#else + return 0; +#endif + } + catch (...) { return -1; } +} + +extern "C" ANSENGINE_API int ANSEngine_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result) { + try { + if (!utf16leBytes || byteLen <= 0 || !result) return -1; + bool isUtf16le = (byteLen >= 2 && byteLen % 2 == 0); + if (isUtf16le) { + bool isAscii = true; + for (int i = 1; i < byteLen; i += 2) { + if (utf16leBytes[i] != 0x00) { isAscii = false; break; } + } + if (isAscii) { + int asciiLen = byteLen / 2; + MgErr error = DSSetHandleSize(result, sizeof(int32) + asciiLen * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = asciiLen; + for (int i = 0; i < asciiLen; i++) (*result)->str[i] = utf16leBytes[i * 2]; + return 1; + } + } +#ifdef _WIN32 + int wideLen = byteLen / (int)sizeof(wchar_t); + const wchar_t* wideStr = reinterpret_cast(utf16leBytes); + int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, nullptr, 0, nullptr, nullptr); + if (utf8Len <= 0) return 0; + std::string utf8Str(utf8Len, 0); + WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, &utf8Str[0], utf8Len, nullptr, nullptr); + MgErr error = DSSetHandleSize(result, sizeof(int32) + utf8Len * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = utf8Len; + memcpy((*result)->str, utf8Str.data(), utf8Len); + return 1; +#else + return 0; +#endif + } + catch (...) { return -1; } +} + + std::string ANSUtilityHelper::DecodeJsonUnicodeToUTF16LE(const std::string& escapedStr) { + std::string result; + result.reserve(escapedStr.size() * 2); + size_t i = 0; + while (i < escapedStr.size()) { + if (i + 5 < escapedStr.size() && escapedStr[i] == '\\' && escapedStr[i + 1] == 'u') { + // Parse \uXXXX + char hex[5] = { escapedStr[i + 2], escapedStr[i + 3], escapedStr[i + 4], escapedStr[i + 5], 0 }; + uint16_t codepoint = (uint16_t)strtoul(hex, nullptr, 16); + // UTF-16LE: low byte first, high byte second + result += static_cast(codepoint & 0xFF); + result += static_cast((codepoint >> 8) & 0xFF); + i += 6; + } else { + // ASCII character -> UTF-16LE (2 bytes: char, 0x00) + result += escapedStr[i]; + result += '\0'; + i++; + } + } + return result; + } + + std::string ANSUtilityHelper::ConvertUTF8ToUTF16LE(const std::string& utf8Str) { +#ifdef _WIN32 + if (utf8Str.empty()) return ""; + // First call: get required buffer size + int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), (int)utf8Str.size(), nullptr, 0); + if (wideLen <= 0) return ""; + // Allocate wide string buffer + std::wstring wideStr(wideLen, 0); + MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), (int)utf8Str.size(), &wideStr[0], wideLen); + // Convert wchar_t buffer to raw bytes (UTF-16LE on Windows) + const char* rawBytes = reinterpret_cast(wideStr.data()); + return std::string(rawBytes, wideLen * sizeof(wchar_t)); +#else + // Non-Windows: return UTF-8 as-is (LabVIEW is primarily Windows) + return utf8Str; +#endif + } + float ANSUtilityHelper::calculate_intersection_area(const cv::Rect& box1, const cv::Rect& box2) { int xx1 = std::max(box1.x, box2.x); int yy1 = std::max(box1.y, box2.y); diff --git a/modules/ANSODEngine/ANSEngineCommon.h b/modules/ANSODEngine/ANSEngineCommon.h index 18adf83..5ff0824 100644 --- a/modules/ANSODEngine/ANSEngineCommon.h +++ b/modules/ANSODEngine/ANSEngineCommon.h @@ -660,6 +660,17 @@ namespace ANSCENTER static std::string PolygonToString(const std::vector& polygon); static std::string KeypointsToString(const std::vector& kps); static std::vector RectToNormalizedPolygon(const cv::Rect& rect, float imageWidth, float imageHeight); + + // Convert a UTF-8 encoded string to UTF-16LE byte string. + // Useful for LabVIEW which can display UTF-16LE Unicode text on Windows. + // Returns a std::string containing the raw UTF-16LE bytes (2 bytes per character). + static std::string ConvertUTF8ToUTF16LE(const std::string& utf8Str); + + // Decode JSON Unicode escape sequences (\uXXXX) to UTF-16LE byte string. + // Input: ASCII string with \uXXXX escapes (e.g., "\\u6c5f\\u6771 599") + // Output: UTF-16LE byte string for LabVIEW display. + // ASCII characters pass through as 2-byte UTF-16LE (e.g., 'A' -> 0x41 0x00). + static std::string DecodeJsonUnicodeToUTF16LE(const std::string& escapedStr); static std::vector MaskToNormalizedPolygon(const cv::Mat& binaryMask, const cv::Rect& boundingBox, float imageWidth, float imageHeight, float simplificationEpsilon = 2.0f, int minContourArea = 10, int maxPoints = 50); }; @@ -1142,4 +1153,8 @@ namespace ANSCENTER }; } +// Unicode conversion utilities for LabVIEW wrapper classes +extern "C" ANSENGINE_API int ANSEngine_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result); +extern "C" ANSENGINE_API int ANSEngine_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result); + #endif \ No newline at end of file diff --git a/modules/ANSUtilities/ANSUtilities.cpp b/modules/ANSUtilities/ANSUtilities.cpp index f574f0a..bdacf07 100644 --- a/modules/ANSUtilities/ANSUtilities.cpp +++ b/modules/ANSUtilities/ANSUtilities.cpp @@ -1,4 +1,7 @@ #include "ANSUtilities.h" +#ifdef _WIN32 +#include +#endif #include #include #include @@ -949,5 +952,55 @@ namespace ANSCENTER return false; } } + + std::string ANSUtilities::DecodeJsonUnicodeToUTF16LE(const std::string& escapedStr) { + std::string result; + result.reserve(escapedStr.size() * 2); + size_t i = 0; + while (i < escapedStr.size()) { + if (i + 5 < escapedStr.size() && escapedStr[i] == '\\' && escapedStr[i + 1] == 'u') { + char hex[5] = { escapedStr[i + 2], escapedStr[i + 3], escapedStr[i + 4], escapedStr[i + 5], 0 }; + uint16_t codepoint = (uint16_t)strtoul(hex, nullptr, 16); + result += static_cast(codepoint & 0xFF); + result += static_cast((codepoint >> 8) & 0xFF); + i += 6; + } else { + result += escapedStr[i]; + result += '\0'; + i++; + } + } + return result; + } + + std::string ANSUtilities::ConvertUTF16LEToUTF8(const char* utf16leBytes, int byteLen) { +#ifdef _WIN32 + if (!utf16leBytes || byteLen <= 0) return ""; + int wideLen = byteLen / (int)sizeof(wchar_t); + const wchar_t* wideStr = reinterpret_cast(utf16leBytes); + // First call: get required UTF-8 buffer size + int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, nullptr, 0, nullptr, nullptr); + if (utf8Len <= 0) return ""; + std::string utf8Str(utf8Len, 0); + WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, &utf8Str[0], utf8Len, nullptr, nullptr); + return utf8Str; +#else + return std::string(utf16leBytes, byteLen); +#endif + } + + std::string ANSUtilities::ConvertUTF8ToUTF16LE(const std::string& utf8Str) { +#ifdef _WIN32 + if (utf8Str.empty()) return ""; + int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), (int)utf8Str.size(), nullptr, 0); + if (wideLen <= 0) return ""; + std::wstring wideStr(wideLen, 0); + MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), (int)utf8Str.size(), &wideStr[0], wideLen); + const char* rawBytes = reinterpret_cast(wideStr.data()); + return std::string(rawBytes, wideLen * sizeof(wchar_t)); +#else + return utf8Str; +#endif + } } diff --git a/modules/ANSUtilities/ANSUtilities.h b/modules/ANSUtilities/ANSUtilities.h index fadaba4..6bd854f 100644 --- a/modules/ANSUtilities/ANSUtilities.h +++ b/modules/ANSUtilities/ANSUtilities.h @@ -89,6 +89,19 @@ namespace ANSCENTER { // Restart PC [[nodiscard]] static bool RestartPC(); + + // Convert a UTF-8 encoded string to UTF-16LE byte string. + // Useful for LabVIEW which can display UTF-16LE Unicode text on Windows. + static std::string ConvertUTF8ToUTF16LE(const std::string& utf8Str); + + // Decode JSON Unicode escape sequences (\uXXXX) to UTF-16LE byte string. + // Input: ASCII string with \uXXXX escapes (e.g., "\\u6c5f\\u6771 599") + // Output: UTF-16LE byte string for LabVIEW display. + static std::string DecodeJsonUnicodeToUTF16LE(const std::string& escapedStr); + + // Convert a UTF-16LE byte string to UTF-8. + // Useful for receiving Unicode text from LabVIEW and converting to UTF-8 for internal processing. + static std::string ConvertUTF16LEToUTF8(const char* utf16leBytes, int byteLen); }; // Connection bundle for pool struct S3Connection { @@ -230,6 +243,10 @@ extern "C" ANSULT_API int SendEmail(const char* smtpServer, int port, extern "C" ANSULT_API int RebootSystem(); +// Unicode conversion utilities for LabVIEW +extern "C" ANSULT_API int ANSConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result); +extern "C" ANSULT_API int ANSDecodeJsonUnicodeToUTF16LE(const char* escapedStr, LStrHandle result); +extern "C" ANSULT_API int ANSConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result); // AWS S3 class diff --git a/modules/ANSUtilities/dllmain.cpp b/modules/ANSUtilities/dllmain.cpp index 3ce4a23..78899b8 100644 --- a/modules/ANSUtilities/dllmain.cpp +++ b/modules/ANSUtilities/dllmain.cpp @@ -798,6 +798,104 @@ extern "C" ANSULT_API int RebootSystem() { } } +extern "C" ANSULT_API int ANSConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result) { + try { + if (!utf8Str || !result) return -1; + int len = (int)strlen(utf8Str); + if (len == 0) return 0; + bool hasUnicodeEscapes = false; + bool hasNonAscii = false; + for (int i = 0; i < len; i++) { + if ((unsigned char)utf8Str[i] >= 0x80) hasNonAscii = true; + if (i + 1 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') hasUnicodeEscapes = true; + } + if (!hasNonAscii && !hasUnicodeEscapes) { + MgErr error = DSSetHandleSize(result, sizeof(int32) + len * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = len; + memcpy((*result)->str, utf8Str, len); + return 1; + } + if (hasUnicodeEscapes) { + std::string utf16le; + utf16le.reserve(len * 2); + for (int i = 0; i < len; ) { + if (i + 5 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') { + char hex[5] = { utf8Str[i + 2], utf8Str[i + 3], utf8Str[i + 4], utf8Str[i + 5], 0 }; + uint16_t cp = (uint16_t)strtoul(hex, nullptr, 16); + utf16le += static_cast(cp & 0xFF); + utf16le += static_cast((cp >> 8) & 0xFF); + i += 6; + } else { + utf16le += utf8Str[i]; + utf16le += '\0'; + i++; + } + } + int size = (int)utf16le.size(); + MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = size; + memcpy((*result)->str, utf16le.data(), size); + return 1; + } + std::string converted = ANSCENTER::ANSUtilities::ConvertUTF8ToUTF16LE(utf8Str); + if (converted.empty()) return 0; + int size = static_cast(converted.size()); + MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = size; + memcpy((*result)->str, converted.data(), size); + return 1; + } + catch (...) { return -1; } +} + +extern "C" ANSULT_API int ANSConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result) { + try { + if (!utf16leBytes || byteLen <= 0 || !result) return -1; + bool isUtf16le = (byteLen >= 2 && byteLen % 2 == 0); + if (isUtf16le) { + bool isAscii = true; + for (int i = 1; i < byteLen; i += 2) { + if (utf16leBytes[i] != 0x00) { isAscii = false; break; } + } + if (isAscii) { + int asciiLen = byteLen / 2; + MgErr error = DSSetHandleSize(result, sizeof(int32) + asciiLen * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = asciiLen; + for (int i = 0; i < asciiLen; i++) (*result)->str[i] = utf16leBytes[i * 2]; + return 1; + } + } + std::string converted = ANSCENTER::ANSUtilities::ConvertUTF16LEToUTF8( + reinterpret_cast(utf16leBytes), byteLen); + if (converted.empty()) return 0; + int size = static_cast(converted.size()); + MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = size; + memcpy((*result)->str, converted.data(), size); + return 1; + } + catch (...) { return -1; } +} + +extern "C" ANSULT_API int ANSDecodeJsonUnicodeToUTF16LE(const char* escapedStr, LStrHandle result) { + try { + if (!escapedStr || !result) return -1; + std::string decoded = ANSCENTER::ANSUtilities::DecodeJsonUnicodeToUTF16LE(escapedStr); + if (decoded.empty()) return 0; + int size = static_cast(decoded.size()); + MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); + if (error != noErr) return -2; + (*result)->cnt = size; + memcpy((*result)->str, decoded.data(), size); + return 1; + } + catch (...) { return -1; } +}