diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 681bb46..10efaca 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -7,7 +7,8 @@ "Bash(grep -l \"EnginePoolManager\" /c/Projects/CLionProjects/ANSCORE/modules/ANSODEngine/*.cpp)", "Bash(grep -n \"g_processExiting\" /c/Projects/CLionProjects/ANSCORE/engines/TensorRTAPI/include/engine/*.h /c/Projects/CLionProjects/ANSCORE/modules/ANSODEngine/engine.h)", "Bash(ssh -T git@anscenter.ddns.net -p 2222)", - "Bash(ssh-add -l)" + "Bash(ssh-add -l)", + "Bash(dotnet build:*)" ] } } diff --git a/dotnet/ANSUnicodeHelper/ANSUnicodeHelper.cs b/dotnet/ANSUnicodeHelper/ANSUnicodeHelper.cs new file mode 100644 index 0000000..a699828 --- /dev/null +++ b/dotnet/ANSUnicodeHelper/ANSUnicodeHelper.cs @@ -0,0 +1,125 @@ +using System; +using System.Drawing; +using System.Text; +using System.Text.RegularExpressions; + +namespace ANSCENTER +{ + /// + /// Unicode helper for LabVIEW .NET interop. + /// All methods are static — no instance creation needed. + /// In LabVIEW: use Invoke Node with class type ANSCENTER.ANSUnicodeHelper. + /// Thread-safe: no shared mutable state. + /// + public class ANSUnicodeHelper + { + // Pre-compiled regex — compiled once at class load, zero per-call cost + private static readonly Regex UnicodeEscapeRegex = + new Regex(@"\\u([0-9a-fA-F]{4})", RegexOptions.Compiled); + + /// + /// Constructor for LabVIEW (if needed). Lightweight, no state. + /// + public ANSUnicodeHelper() { } + + /// + /// Decodes \uXXXX escape sequences to Unicode characters. + /// ASCII strings pass through unchanged (zero allocation). + /// + public static string Decode(string input) + { + if (string.IsNullOrEmpty(input)) return input ?? string.Empty; + if (input.IndexOf("\\u", StringComparison.Ordinal) < 0) return input; + return UnicodeEscapeRegex.Replace(input, match => + { + int codepoint = Convert.ToInt32(match.Groups[1].Value, 16); + return ((char)codepoint).ToString(); + }); + } + + /// + /// Draws Unicode string at position (x, y). + /// + public static void DrawString(Graphics g, string escapedText, Font font, Brush brush, float x, float y) + { + if (g == null || string.IsNullOrEmpty(escapedText)) return; + g.DrawString(Decode(escapedText), font, brush, x, y); + } + + /// + /// Draws Unicode string at position (x, y) with StringFormat. + /// + public static void DrawString(Graphics g, string escapedText, Font font, Brush brush, float x, float y, StringFormat format) + { + if (g == null || string.IsNullOrEmpty(escapedText)) return; + g.DrawString(Decode(escapedText), font, brush, x, y, format); + } + + /// + /// Draws Unicode string at PointF with StringFormat. + /// + public static void DrawString(Graphics g, string escapedText, Font font, Brush brush, PointF point, StringFormat format) + { + if (g == null || string.IsNullOrEmpty(escapedText)) return; + g.DrawString(Decode(escapedText), font, brush, point, format); + } + + /// + /// Draws Unicode string inside a RectangleF with word wrapping. + /// + public static void DrawString(Graphics g, string escapedText, Font font, Brush brush, RectangleF layoutRect) + { + if (g == null || string.IsNullOrEmpty(escapedText)) return; + g.DrawString(Decode(escapedText), font, brush, layoutRect); + } + + /// + /// Draws Unicode string inside a RectangleF with StringFormat. + /// + public static void DrawString(Graphics g, string escapedText, Font font, Brush brush, RectangleF layoutRect, StringFormat format) + { + if (g == null || string.IsNullOrEmpty(escapedText)) return; + g.DrawString(Decode(escapedText), font, brush, layoutRect, format); + } + + /// + /// Measures Unicode string size after decoding. + /// + public static SizeF MeasureString(Graphics g, string escapedText, Font font) + { + if (g == null || string.IsNullOrEmpty(escapedText)) return SizeF.Empty; + return g.MeasureString(Decode(escapedText), font); + } + + /// + /// Measures Unicode string size with layout area and StringFormat. + /// + public static SizeF MeasureString(Graphics g, string escapedText, Font font, SizeF layoutArea, StringFormat format) + { + if (g == null || string.IsNullOrEmpty(escapedText)) return SizeF.Empty; + return g.MeasureString(Decode(escapedText), font, layoutArea, format); + } + + /// + /// Checks if string contains \uXXXX escapes. + /// + public static bool HasUnicodeEscapes(string input) + { + if (string.IsNullOrEmpty(input)) return false; + return input.IndexOf("\\u", StringComparison.Ordinal) >= 0; + } + + /// + /// Checks if string is pure ASCII. + /// + public static bool IsAsciiOnly(string input) + { + if (string.IsNullOrEmpty(input)) return true; + foreach (char c in input) + { + if (c > 127) return false; + } + return true; + } + } +} diff --git a/dotnet/ANSUnicodeHelper/ANSUnicodeHelper.csproj b/dotnet/ANSUnicodeHelper/ANSUnicodeHelper.csproj new file mode 100644 index 0000000..f13fe8b --- /dev/null +++ b/dotnet/ANSUnicodeHelper/ANSUnicodeHelper.csproj @@ -0,0 +1,21 @@ + + + + + + + + net48 + ANSUnicodeHelper + ANSCENTER + true + 1.0.0.0 + 1.0.0.0 + ANSCENTER + ANSUnicodeHelper + Unicode helper for LabVIEW .NET interop - decodes \uXXXX escapes to .NET System.String + ..\..\cmake-build-release\bin\ + false + + + diff --git a/dotnet/ANSUnicodeHelper/obj/ANSUnicodeHelper.csproj.nuget.dgspec.json b/dotnet/ANSUnicodeHelper/obj/ANSUnicodeHelper.csproj.nuget.dgspec.json new file mode 100644 index 0000000..bbd2879 --- /dev/null +++ b/dotnet/ANSUnicodeHelper/obj/ANSUnicodeHelper.csproj.nuget.dgspec.json @@ -0,0 +1,58 @@ +{ + "format": 1, + "restore": { + "C:\\Projects\\CLionProjects\\ANSCORE\\dotnet\\ANSUnicodeHelper\\ANSUnicodeHelper.csproj": {} + }, + "projects": { + "C:\\Projects\\CLionProjects\\ANSCORE\\dotnet\\ANSUnicodeHelper\\ANSUnicodeHelper.csproj": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "C:\\Projects\\CLionProjects\\ANSCORE\\dotnet\\ANSUnicodeHelper\\ANSUnicodeHelper.csproj", + "projectName": "ANSUnicodeHelper", + "projectPath": "C:\\Projects\\CLionProjects\\ANSCORE\\dotnet\\ANSUnicodeHelper\\ANSUnicodeHelper.csproj", + "packagesPath": "C:\\Users\\nghia\\.nuget\\packages\\", + "outputPath": "C:\\Projects\\CLionProjects\\ANSCORE\\dotnet\\ANSUnicodeHelper\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" + ], + "configFilePaths": [ + "C:\\Users\\nghia\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "net48" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-11/nuget/v3/index.json": {}, + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "net48": { + "targetAlias": "net48", + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + }, + "SdkAnalysisLevel": "10.0.200" + }, + "frameworks": { + "net48": { + "targetAlias": "net48", + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.200\\RuntimeIdentifierGraph.json" + } + } + } + } +} \ No newline at end of file diff --git a/dotnet/ANSUnicodeHelper/obj/ANSUnicodeHelper.csproj.nuget.g.props b/dotnet/ANSUnicodeHelper/obj/ANSUnicodeHelper.csproj.nuget.g.props new file mode 100644 index 0000000..3ca1bb7 --- /dev/null +++ b/dotnet/ANSUnicodeHelper/obj/ANSUnicodeHelper.csproj.nuget.g.props @@ -0,0 +1,16 @@ + + + + True + NuGet + $(MSBuildThisFileDirectory)project.assets.json + $(UserProfile)\.nuget\packages\ + C:\Users\nghia\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages + PackageReference + 7.0.0 + + + + + + \ No newline at end of file diff --git a/dotnet/ANSUnicodeHelper/obj/ANSUnicodeHelper.csproj.nuget.g.targets b/dotnet/ANSUnicodeHelper/obj/ANSUnicodeHelper.csproj.nuget.g.targets new file mode 100644 index 0000000..3dc06ef --- /dev/null +++ b/dotnet/ANSUnicodeHelper/obj/ANSUnicodeHelper.csproj.nuget.g.targets @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/dotnet/ANSUnicodeHelper/obj/project.assets.json b/dotnet/ANSUnicodeHelper/obj/project.assets.json new file mode 100644 index 0000000..659a0fd --- /dev/null +++ b/dotnet/ANSUnicodeHelper/obj/project.assets.json @@ -0,0 +1,64 @@ +{ + "version": 3, + "targets": { + ".NETFramework,Version=v4.8": {} + }, + "libraries": {}, + "projectFileDependencyGroups": { + ".NETFramework,Version=v4.8": [] + }, + "packageFolders": { + "C:\\Users\\nghia\\.nuget\\packages\\": {}, + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {} + }, + "project": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "C:\\Projects\\CLionProjects\\ANSCORE\\dotnet\\ANSUnicodeHelper\\ANSUnicodeHelper.csproj", + "projectName": "ANSUnicodeHelper", + "projectPath": "C:\\Projects\\CLionProjects\\ANSCORE\\dotnet\\ANSUnicodeHelper\\ANSUnicodeHelper.csproj", + "packagesPath": "C:\\Users\\nghia\\.nuget\\packages\\", + "outputPath": "C:\\Projects\\CLionProjects\\ANSCORE\\dotnet\\ANSUnicodeHelper\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" + ], + "configFilePaths": [ + "C:\\Users\\nghia\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "net48" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-11/nuget/v3/index.json": {}, + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "net48": { + "targetAlias": "net48", + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + }, + "SdkAnalysisLevel": "10.0.200" + }, + "frameworks": { + "net48": { + "targetAlias": "net48", + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.200\\RuntimeIdentifierGraph.json" + } + } + } +} \ No newline at end of file diff --git a/dotnet/ANSUnicodeHelper/obj/project.nuget.cache b/dotnet/ANSUnicodeHelper/obj/project.nuget.cache new file mode 100644 index 0000000..751b2f9 --- /dev/null +++ b/dotnet/ANSUnicodeHelper/obj/project.nuget.cache @@ -0,0 +1,8 @@ +{ + "version": 2, + "dgSpecHash": "5VJRzMAJt58=", + "success": true, + "projectFilePath": "C:\\Projects\\CLionProjects\\ANSCORE\\dotnet\\ANSUnicodeHelper\\ANSUnicodeHelper.csproj", + "expectedPackageFiles": [], + "logs": [] +} \ No newline at end of file diff --git a/modules/ANSFR/ANSFR.cpp b/modules/ANSFR/ANSFR.cpp index 69cedff..475fb8a 100644 --- a/modules/ANSFR/ANSFR.cpp +++ b/modules/ANSFR/ANSFR.cpp @@ -2161,6 +2161,9 @@ namespace ANSCENTER { START_TIMER(postprocess); resultObjects=UpdateFaceAttributes(recognizedFaces, camera_id); END_TIMER(postprocess, "Update Face Attributes Time"); + + // Deduplicate: if two faces matched the same userId, keep only the highest confidence + ensureUniqueUserIdWithHighestConfidence(resultObjects); } } catch (const std::exception& e) { @@ -2405,7 +2408,7 @@ namespace ANSCENTER { END_TIMER(json_build, "JSON Build Time"); START_TIMER(json_serialize); - std::string result = root.dump(-1, ' ', true); + std::string result = root.dump(); END_TIMER(json_serialize, "JSON Serialize Time"); END_TIMER(json_total, "JSON Conversion Total Time"); @@ -2442,7 +2445,7 @@ namespace ANSCENTER { root["results"] = detectedObjects; - return root.dump(-1, ' ', true); + return root.dump(); } // Validation helper methods diff --git a/modules/ANSFR/ANSFR.h b/modules/ANSFR/ANSFR.h index b1f1a2f..e9d6958 100644 --- a/modules/ANSFR/ANSFR.h +++ b/modules/ANSFR/ANSFR.h @@ -352,7 +352,7 @@ extern "C" ANSFR_API int DeleteFacesByUser(ANSCENTER::ANSFacialRecognition * 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_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result, int includeBOM = 1); 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 f71ea03..90eefd1 100644 --- a/modules/ANSFR/dllmain.cpp +++ b/modules/ANSFR/dllmain.cpp @@ -751,27 +751,20 @@ 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) { +extern "C" ANSFR_API int ANSFR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result, int includeBOM) { try { if (!utf8Str || !result) return -1; int len = (int)strlen(utf8Str); if (len == 0) return 0; + const char bom[2] = { '\xFF', '\xFE' }; 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; + for (int i = 0; i + 1 < len; i++) { + if (utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') { hasUnicodeEscapes = true; break; } } if (hasUnicodeEscapes) { std::string utf16le; - utf16le.reserve(len * 2); + if (includeBOM) utf16le.assign(bom, 2); + utf16le.reserve(len * 2 + 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 }; @@ -797,11 +790,14 @@ extern "C" ANSFR_API int ANSFR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHan 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)); + int dataSize = wideLen * (int)sizeof(wchar_t); + int bomSize = includeBOM ? 2 : 0; + int totalSize = bomSize + dataSize; + MgErr error = DSSetHandleSize(result, sizeof(int32) + totalSize * sizeof(uChar)); if (error != noErr) return -2; - (*result)->cnt = size; - memcpy((*result)->str, wideStr.data(), size); + (*result)->cnt = totalSize; + if (includeBOM) memcpy((*result)->str, bom, 2); + memcpy((*result)->str + bomSize, wideStr.data(), dataSize); return 1; #else return 0; diff --git a/modules/ANSLPR/ANSLPR.cpp b/modules/ANSLPR/ANSLPR.cpp index d3710e6..d3c8db9 100644 --- a/modules/ANSLPR/ANSLPR.cpp +++ b/modules/ANSLPR/ANSLPR.cpp @@ -460,7 +460,7 @@ namespace ANSCENTER { {"kps", KeypointsToString(det.kps)} }); } - return root.dump(-1, ' ', true); + return root.dump(); } catch (const std::exception& e) { this->_logger.LogFatal("ANSALPR::VectorDetectionToJsonString", e.what(), __FILE__, __LINE__); diff --git a/modules/ANSLPR/ANSLPR.h b/modules/ANSLPR/ANSLPR.h index d952b29..d462cdc 100644 --- a/modules/ANSLPR/ANSLPR.h +++ b/modules/ANSLPR/ANSLPR.h @@ -143,7 +143,7 @@ extern "C" ANSLPR_API int ANSALPR_SetFormats(ANSCENTER::ANSALPR** Handle, co 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_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result, int includeBOM = 1); 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 5e85ae5..c5374d8 100644 --- a/modules/ANSLPR/dllmain.cpp +++ b/modules/ANSLPR/dllmain.cpp @@ -773,27 +773,20 @@ 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) { +extern "C" ANSLPR_API int ANSLPR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result, int includeBOM) { try { if (!utf8Str || !result) return -1; int len = (int)strlen(utf8Str); if (len == 0) return 0; + const char bom[2] = { '\xFF', '\xFE' }; 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; + for (int i = 0; i + 1 < len; i++) { + if (utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') { hasUnicodeEscapes = true; break; } } if (hasUnicodeEscapes) { std::string utf16le; - utf16le.reserve(len * 2); + if (includeBOM) utf16le.assign(bom, 2); + utf16le.reserve(len * 2 + 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 }; @@ -819,11 +812,14 @@ extern "C" ANSLPR_API int ANSLPR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrH 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)); + int dataSize = wideLen * (int)sizeof(wchar_t); + int bomSize = includeBOM ? 2 : 0; + int totalSize = bomSize + dataSize; + MgErr error = DSSetHandleSize(result, sizeof(int32) + totalSize * sizeof(uChar)); if (error != noErr) return -2; - (*result)->cnt = size; - memcpy((*result)->str, wideStr.data(), size); + (*result)->cnt = totalSize; + if (includeBOM) memcpy((*result)->str, bom, 2); + memcpy((*result)->str + bomSize, wideStr.data(), dataSize); return 1; #else return 0; diff --git a/modules/ANSOCR/ANSOCRBase.cpp b/modules/ANSOCR/ANSOCRBase.cpp index 0deb487..9a7b26e 100644 --- a/modules/ANSOCR/ANSOCRBase.cpp +++ b/modules/ANSOCR/ANSOCRBase.cpp @@ -259,7 +259,7 @@ namespace ANSCENTER { }); } - return root.dump(-1, ' ', true); + return root.dump(); } catch (const std::exception& e) { // Add your error logging here if needed @@ -893,7 +893,7 @@ namespace ANSCENTER { for (const auto& part : res.parts) { alprInfo[part.first] = part.second; } - std::string extraInfoStr = alprInfo.dump(-1, ' ', true); + std::string extraInfoStr = alprInfo.dump(); // Use the same field layout as OCRDetectionToJsonString jsonResults.push_back({ @@ -912,7 +912,7 @@ namespace ANSCENTER { {"kps", ""} }); } - return root.dump(-1, ' ', true); + return root.dump(); } catch (const std::exception&) { return R"({"results":[],"error":"ALPR serialization failed"})"; } diff --git a/modules/ANSOCR/ANSOCRBase.h b/modules/ANSOCR/ANSOCRBase.h index 3fa8404..a625e91 100644 --- a/modules/ANSOCR/ANSOCRBase.h +++ b/modules/ANSOCR/ANSOCRBase.h @@ -244,7 +244,7 @@ extern "C" ANSOCR_API int SetANSOCRCountry(ANSCENTER::ANSOCRBase** Handl 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_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result, int includeBOM = 1); 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) diff --git a/modules/ANSOCR/dllmain.cpp b/modules/ANSOCR/dllmain.cpp index a36557b..83a7aeb 100644 --- a/modules/ANSOCR/dllmain.cpp +++ b/modules/ANSOCR/dllmain.cpp @@ -427,33 +427,25 @@ extern "C" ANSOCR_API int SetANSOCRALPRFormat(ANSCENTER::ANSOCRBase** Handle, co // - 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) { +extern "C" ANSOCR_API int ANSOCR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result, int includeBOM) { 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 + // Always output UTF-16LE (required for LabVIEW "Force Unicode Text" indicators) + const char bom[2] = { '\xFF', '\xFE' }; + + // Check if input contains \uXXXX escapes 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; + for (int i = 0; i + 1 < len; i++) { + if (utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') { hasUnicodeEscapes = true; break; } } - // 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); + if (includeBOM) utf16le.assign(bom, 2); + utf16le.reserve(len * 2 + 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 }; @@ -462,7 +454,6 @@ extern "C" ANSOCR_API int ANSOCR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrH 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++; @@ -476,17 +467,19 @@ extern "C" ANSOCR_API int ANSOCR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrH 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)); + int dataSize = wideLen * (int)sizeof(wchar_t); + int bomSize = includeBOM ? 2 : 0; + int totalSize = bomSize + dataSize; + MgErr error = DSSetHandleSize(result, sizeof(int32) + totalSize * sizeof(uChar)); if (error != noErr) return -2; - (*result)->cnt = size; - memcpy((*result)->str, wideStr.data(), size); + (*result)->cnt = totalSize; + if (includeBOM) memcpy((*result)->str, bom, 2); + memcpy((*result)->str + bomSize, wideStr.data(), dataSize); return 1; #else return 0; diff --git a/modules/ANSODEngine/ANSEngineCommon.cpp b/modules/ANSODEngine/ANSEngineCommon.cpp index d90d33f..4710ecd 100644 --- a/modules/ANSODEngine/ANSEngineCommon.cpp +++ b/modules/ANSODEngine/ANSEngineCommon.cpp @@ -1135,7 +1135,7 @@ namespace ANSCENTER { }); } // ensure_ascii=true escapes non-ASCII chars as \uXXXX for LabVIEW compatibility - return root.dump(-1, ' ', true); + return root.dump(); } catch (const std::exception& e) { return R"({"results":[],"error":"Serialization failed"})"; @@ -2243,27 +2243,20 @@ 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_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result, int includeBOM) { try { if (!utf8Str || !result) return -1; int len = (int)strlen(utf8Str); if (len == 0) return 0; + const char bom[2] = { '\xFF', '\xFE' }; 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; + for (int i = 0; i + 1 < len; i++) { + if (utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') { hasUnicodeEscapes = true; break; } } if (hasUnicodeEscapes) { std::string utf16le; - utf16le.reserve(len * 2); + if (includeBOM) utf16le.assign(bom, 2); + utf16le.reserve(len * 2 + 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 }; @@ -2289,11 +2282,14 @@ extern "C" ANSENGINE_API int ANSEngine_ConvertUTF8ToUTF16LE(const char* utf8Str, 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)); + int dataSize = wideLen * (int)sizeof(wchar_t); + int bomSize = includeBOM ? 2 : 0; + int totalSize = bomSize + dataSize; + MgErr error = DSSetHandleSize(result, sizeof(int32) + totalSize * sizeof(uChar)); if (error != noErr) return -2; - (*result)->cnt = size; - memcpy((*result)->str, wideStr.data(), size); + (*result)->cnt = totalSize; + if (includeBOM) memcpy((*result)->str, bom, 2); + memcpy((*result)->str + bomSize, wideStr.data(), dataSize); return 1; #else return 0; diff --git a/modules/ANSODEngine/ANSEngineCommon.h b/modules/ANSODEngine/ANSEngineCommon.h index 5ff0824..86c1da2 100644 --- a/modules/ANSODEngine/ANSEngineCommon.h +++ b/modules/ANSODEngine/ANSEngineCommon.h @@ -1154,7 +1154,7 @@ 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_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result, int includeBOM = 1); 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.h b/modules/ANSUtilities/ANSUtilities.h index 6bd854f..56608ad 100644 --- a/modules/ANSUtilities/ANSUtilities.h +++ b/modules/ANSUtilities/ANSUtilities.h @@ -244,7 +244,7 @@ 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 ANSConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result, int includeBOM = 1); 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); diff --git a/modules/ANSUtilities/dllmain.cpp b/modules/ANSUtilities/dllmain.cpp index 78899b8..1903419 100644 --- a/modules/ANSUtilities/dllmain.cpp +++ b/modules/ANSUtilities/dllmain.cpp @@ -798,27 +798,20 @@ extern "C" ANSULT_API int RebootSystem() { } } -extern "C" ANSULT_API int ANSConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result) { +extern "C" ANSULT_API int ANSConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result, int includeBOM) { try { if (!utf8Str || !result) return -1; int len = (int)strlen(utf8Str); if (len == 0) return 0; + const char bom[2] = { '\xFF', '\xFE' }; 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; + for (int i = 0; i + 1 < len; i++) { + if (utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') { hasUnicodeEscapes = true; break; } } if (hasUnicodeEscapes) { std::string utf16le; - utf16le.reserve(len * 2); + if (includeBOM) utf16le.assign(bom, 2); + utf16le.reserve(len * 2 + 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 }; @@ -841,11 +834,14 @@ extern "C" ANSULT_API int ANSConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandl } 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)); + int dataSize = static_cast(converted.size()); + int bomSize = includeBOM ? 2 : 0; + int totalSize = bomSize + dataSize; + MgErr error = DSSetHandleSize(result, sizeof(int32) + totalSize * sizeof(uChar)); if (error != noErr) return -2; - (*result)->cnt = size; - memcpy((*result)->str, converted.data(), size); + (*result)->cnt = totalSize; + if (includeBOM) memcpy((*result)->str, bom, 2); + memcpy((*result)->str + bomSize, converted.data(), dataSize); return 1; } catch (...) { return -1; }