fix: use real UTF-8 for jstring -> native conversion
CI / Build Java (push) Has been cancelled
CI / Build Native (macos-latest - macos) (push) Has been cancelled
CI / Build Native (ubuntu-latest - linux) (push) Has been cancelled
CI / Build Native (ubuntu-latest - windows) (push) Has been cancelled
CI / Archive Natives (push) Has been cancelled
CI / Update Binaries (push) Has been cancelled
CI / Release (push) Has been cancelled
CI / Build Java (push) Has been cancelled
CI / Build Native (macos-latest - macos) (push) Has been cancelled
CI / Build Native (ubuntu-latest - linux) (push) Has been cancelled
CI / Build Native (ubuntu-latest - windows) (push) Has been cancelled
CI / Archive Natives (push) Has been cancelled
CI / Update Binaries (push) Has been cancelled
CI / Release (push) Has been cancelled
JNI's GetStringUTFChars emits MUTF-8, which encodes codepoints above U+FFFF as a surrogate-pair of two 3-byte sequences. ImGui's UTF-8 decoder rejects those, so SMP emoji (U+1F000+) never reach the glyph atlas even when the glyph is registered. Add Jni::ToTrueUtf8 / Jni::FreeTrueUtf8 helpers in jni_common that decode via GetStringChars (UTF-16) and emit proper 1/2/3/4-byte UTF-8, then route every generator- and manually-written JNI call site through them (1921 occurrences across the generated tree and 3 hand-written files, plus the jni_content.kt generator template so future regenerations stay consistent).
This commit is contained in:
1 parent
34c40304f6
commit
d9aa2c811b
20 files changed
+3897
-3846
No files matched your search
@@ -322,7 +322,7 @@ public final class ImGuiIO extends ImGuiStruct {
|
||||
const char* getClipboardTextStub(void* user_data) {
|
||||
JNIEnv* env = Jni::GetEnv();
|
||||
jstring jstr = Jni::CallImStrSupplier(env, _getClipboardTextCallback);
|
||||
return env->GetStringUTFChars(jstr, 0);
|
||||
return Jni::ToTrueUtf8(env, jstr);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
@@ -78,9 +78,9 @@ public final class TextEditor extends ImGuiStructDestroyable {
|
||||
for (int i = 0; i < keysLen; i++) {
|
||||
int key = keys[i];
|
||||
jstring string = (jstring)env->GetObjectArrayElement(values, i);
|
||||
const char* value = env->GetStringUTFChars(string, JNI_FALSE);
|
||||
const char* value = Jni::ToTrueUtf8(env, string);
|
||||
markers.emplace(std::pair<int, std::string>(key, std::string(value)));
|
||||
env->ReleaseStringUTFChars(string, value);
|
||||
Jni::FreeTrueUtf8(value);
|
||||
}
|
||||
THIS->SetErrorMarkers(markers);
|
||||
*/
|
||||
@@ -116,9 +116,9 @@ public final class TextEditor extends ImGuiStructDestroyable {
|
||||
vec.reserve(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
jstring string = (jstring)env->GetObjectArrayElement(lines, i);
|
||||
const char* raw = env->GetStringUTFChars(string, JNI_FALSE);
|
||||
const char* raw = Jni::ToTrueUtf8(env, string);
|
||||
vec.emplace_back(std::string(raw));
|
||||
env->ReleaseStringUTFChars(string, raw);
|
||||
Jni::FreeTrueUtf8(raw);
|
||||
}
|
||||
THIS->SetTextLines(vec);
|
||||
*/
|
||||
|
||||
+12
-12
@@ -49,9 +49,9 @@ public final class TextEditorLanguageDefinition extends ImGuiStructDestroyable {
|
||||
std::unordered_set<std::string> set;
|
||||
for (int i = 0; i < length; i++) {
|
||||
jstring string = (jstring)env->GetObjectArrayElement(keywords, i);
|
||||
const char* raw = env->GetStringUTFChars(string, JNI_FALSE);
|
||||
const char* raw = Jni::ToTrueUtf8(env, string);
|
||||
set.emplace(std::string(raw));
|
||||
env->ReleaseStringUTFChars(string, raw);
|
||||
Jni::FreeTrueUtf8(raw);
|
||||
}
|
||||
THIS->mKeywords = set;
|
||||
*/
|
||||
@@ -67,13 +67,13 @@ public final class TextEditorLanguageDefinition extends ImGuiStructDestroyable {
|
||||
for (int i = 0; i < keysLen; i++) {
|
||||
jstring string1 = (jstring)env->GetObjectArrayElement(keys, i);
|
||||
jstring string2 = (jstring)env->GetObjectArrayElement(decl, i);
|
||||
const char* key = env->GetStringUTFChars(string1, JNI_FALSE);
|
||||
const char* value = env->GetStringUTFChars(string2, JNI_FALSE);
|
||||
const char* key = Jni::ToTrueUtf8(env, string1);
|
||||
const char* value = Jni::ToTrueUtf8(env, string2);
|
||||
TextEditor::Identifier id;
|
||||
id.mDeclaration = std::string(value);
|
||||
identifiers.insert(std::pair<std::string, TextEditor::Identifier>(std::string(key), id));
|
||||
env->ReleaseStringUTFChars(string1, key);
|
||||
env->ReleaseStringUTFChars(string2, value);
|
||||
Jni::FreeTrueUtf8(key);
|
||||
Jni::FreeTrueUtf8(value);
|
||||
}
|
||||
THIS->mIdentifiers = identifiers;
|
||||
*/
|
||||
@@ -89,13 +89,13 @@ public final class TextEditorLanguageDefinition extends ImGuiStructDestroyable {
|
||||
for (int i = 0; i < keysLen; i++) {
|
||||
jstring string1 = (jstring)env->GetObjectArrayElement(keys, i);
|
||||
jstring string2 = (jstring)env->GetObjectArrayElement(decl, i);
|
||||
const char* key = env->GetStringUTFChars(string1, JNI_FALSE);
|
||||
const char* value = env->GetStringUTFChars(string2, JNI_FALSE);
|
||||
const char* key = Jni::ToTrueUtf8(env, string1);
|
||||
const char* value = Jni::ToTrueUtf8(env, string2);
|
||||
TextEditor::Identifier id;
|
||||
id.mDeclaration = std::string(value);
|
||||
preprocIdentifiers.insert(std::pair<std::string, TextEditor::Identifier>(std::string(key), id));
|
||||
env->ReleaseStringUTFChars(string1, key);
|
||||
env->ReleaseStringUTFChars(string2, value);
|
||||
Jni::FreeTrueUtf8(key);
|
||||
Jni::FreeTrueUtf8(value);
|
||||
}
|
||||
THIS->mPreprocIdentifiers = preprocIdentifiers;
|
||||
*/
|
||||
@@ -131,7 +131,7 @@ public final class TextEditorLanguageDefinition extends ImGuiStructDestroyable {
|
||||
std::vector<std::pair<std::string, TextEditor::PaletteIndex>> tokenRegexStrings;
|
||||
for (int i = 0; i < keysLen; i++) {
|
||||
jstring string = (jstring)env->GetObjectArrayElement(keys, i);
|
||||
const char* key = env->GetStringUTFChars(string, JNI_FALSE);
|
||||
const char* key = Jni::ToTrueUtf8(env, string);
|
||||
int value = paletteIndexes[i];
|
||||
tokenRegexStrings.emplace_back(
|
||||
std::pair<std::string, TextEditor::PaletteIndex>(
|
||||
@@ -139,7 +139,7 @@ public final class TextEditorLanguageDefinition extends ImGuiStructDestroyable {
|
||||
static_cast<TextEditor::PaletteIndex>(value)
|
||||
)
|
||||
);
|
||||
env->ReleaseStringUTFChars(string, key);
|
||||
Jni::FreeTrueUtf8(key);
|
||||
}
|
||||
THIS->mTokenRegexStrings = tokenRegexStrings;
|
||||
*/
|
||||
|
||||
@@ -119,4 +119,49 @@ namespace Jni
|
||||
env->DeleteLocalRef(value);
|
||||
}
|
||||
}
|
||||
|
||||
char* ToTrueUtf8(JNIEnv* env, jstring s) {
|
||||
if (s == nullptr) return nullptr;
|
||||
const jchar* chars = env->GetStringChars(s, nullptr);
|
||||
jsize len = env->GetStringLength(s);
|
||||
// Worst case: one codepoint → 4 UTF-8 bytes, plus null terminator.
|
||||
char* buf = (char*)malloc((size_t)len * 4 + 1);
|
||||
if (buf == nullptr) {
|
||||
env->ReleaseStringChars(s, chars);
|
||||
return nullptr;
|
||||
}
|
||||
char* out = buf;
|
||||
for (jsize i = 0; i < len; i++) {
|
||||
uint32_t cp = (uint32_t)chars[i];
|
||||
if (cp >= 0xD800 && cp <= 0xDBFF && i + 1 < len) {
|
||||
uint32_t low = (uint32_t)chars[i + 1];
|
||||
if (low >= 0xDC00 && low <= 0xDFFF) {
|
||||
cp = 0x10000 + ((cp - 0xD800) << 10) + (low - 0xDC00);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (cp < 0x80) {
|
||||
*out++ = (char)cp;
|
||||
} else if (cp < 0x800) {
|
||||
*out++ = (char)(0xC0 | (cp >> 6));
|
||||
*out++ = (char)(0x80 | (cp & 0x3F));
|
||||
} else if (cp < 0x10000) {
|
||||
*out++ = (char)(0xE0 | (cp >> 12));
|
||||
*out++ = (char)(0x80 | ((cp >> 6) & 0x3F));
|
||||
*out++ = (char)(0x80 | (cp & 0x3F));
|
||||
} else {
|
||||
*out++ = (char)(0xF0 | (cp >> 18));
|
||||
*out++ = (char)(0x80 | ((cp >> 12) & 0x3F));
|
||||
*out++ = (char)(0x80 | ((cp >> 6) & 0x3F));
|
||||
*out++ = (char)(0x80 | (cp & 0x3F));
|
||||
}
|
||||
}
|
||||
*out = 0;
|
||||
env->ReleaseStringChars(s, chars);
|
||||
return buf;
|
||||
}
|
||||
|
||||
void FreeTrueUtf8(char* p) {
|
||||
if (p != nullptr) free(p);
|
||||
}
|
||||
}
|
||||
@@ -47,4 +47,12 @@ namespace Jni
|
||||
|
||||
jobjectArray NewImGuiKeyDataArray(JNIEnv* env, const ImGuiKeyData* src, int size);
|
||||
void ImGuiKeyDataArrayCpy(JNIEnv* env, jobjectArray src, ImGuiKeyData* dst, int size);
|
||||
|
||||
// Converts a jstring to a freshly-malloc'd C string in real UTF-8 (not JNI's MUTF-8).
|
||||
// JNI's GetStringUTFChars encodes codepoints above U+FFFF as a surrogate pair of two
|
||||
// 3-byte sequences, which ImGui's UTF-8 decoder rejects — so SMP emoji never reach
|
||||
// the glyph atlas even when the glyph is registered. Caller must release with
|
||||
// FreeTrueUtf8(). Returns nullptr iff the input jstring is null.
|
||||
char* ToTrueUtf8(JNIEnv* env, jstring s);
|
||||
void FreeTrueUtf8(char* p);
|
||||
}
|
||||
Reference in new issue
Block a user