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; }