From fb89eac7dcbc9338d467e2d56777c8b5979afdfd Mon Sep 17 00:00:00 2001 From: anon Date: Sat, 1 Apr 2023 11:47:08 +0100 Subject: [PATCH 1/4] Fix GetCodepointNext to return default value with size=0 on invalid input. Modify LoadCodepoints to work when GetCodepointNext returns a size of 0. All internal use of GetCodepointNext and GetCodepointPrev checked. This fix may break external code dealing with invalid input as the old code erroneously never returned a size of 0, external code that doesn't properly check for size=0 may endlessly loop or overflow a buffer on invalid input. --- src/rtext.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 2d01360eff15..32379551af1f 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1704,7 +1704,9 @@ int *LoadCodepoints(const char *text, int *count) for (int i = 0; i < textLength; codepointCount++) { codepoints[codepointCount] = GetCodepointNext(text + i, &codepointSize); - i += codepointSize; + + if (codepoints[codepointCount] == 0x3f) i += 1; + else i += codepointSize; } // Re-allocate buffer to the actual number of codepoints loaded @@ -1917,7 +1919,7 @@ int GetCodepointNext(const char *text, int *codepointSize) codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]); *codepointSize = 2; } - else + else if (0x00 == (0x80 & ptr[0])) { // 1 byte UTF-8 codepoint codepoint = ptr[0]; From ed02fbd36916d3a7d5f763f0b17a6c0070385129 Mon Sep 17 00:00:00 2001 From: anon Date: Sun, 2 Apr 2023 11:00:35 +0100 Subject: [PATCH 2/4] Change default behaviour of GetCodepointNext to return a size of 1 instead of 0. This matches existing prod behaviour and guarantees size 1..4 is returned. Simplify internal code that uses GetCodepointNext that previously had to account for size=0. --- src/rtext.c | 16 +++------------- src/rtextures.c | 4 ---- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 32379551af1f..951844098898 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1071,10 +1071,6 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f int codepoint = GetCodepointNext(&text[i], &codepointByteCount); int index = GetGlyphIndex(font, codepoint); - // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) - // but we need to draw all the bad bytes using the '?' symbol moving one byte - if (codepoint == 0x3f) codepointByteCount = 1; - if (codepoint == '\n') { // NOTE: Fixed line spacing of 1.5 line-height @@ -1213,9 +1209,6 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing letter = GetCodepointNext(&text[i], &next); index = GetGlyphIndex(font, letter); - // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) - // but we need to draw all the bad bytes using the '?' symbol so to not skip any we set next = 1 - if (letter == 0x3f) next = 1; i += next - 1; if (letter != '\n') @@ -1704,9 +1697,7 @@ int *LoadCodepoints(const char *text, int *count) for (int i = 0; i < textLength; codepointCount++) { codepoints[codepointCount] = GetCodepointNext(text + i, &codepointSize); - - if (codepoints[codepointCount] == 0x3f) i += 1; - else i += codepointSize; + i += codepointSize; } // Re-allocate buffer to the actual number of codepoints loaded @@ -1736,8 +1727,7 @@ int GetCodepointCount(const char *text) int next = 0; int letter = GetCodepointNext(ptr, &next); - if (letter == 0x3f) ptr += 1; - else ptr += next; + ptr += next; length++; } @@ -1898,7 +1888,7 @@ int GetCodepointNext(const char *text, int *codepointSize) { const char *ptr = text; int codepoint = 0x3f; // Codepoint (defaults to '?') - *codepointSize = 0; + *codepointSize = 1; // Get current codepoint and bytes processed if (0xf0 == (0xf8 & ptr[0])) diff --git a/src/rtextures.c b/src/rtextures.c index fb63f2ff05e9..2249b609f2ca 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1273,10 +1273,6 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co int codepoint = GetCodepointNext(&text[i], &codepointByteCount); // WARNING: Module required: rtext int index = GetGlyphIndex(font, codepoint); // WARNING: Module required: rtext - // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) - // but we need to draw all the bad bytes using the '?' symbol moving one byte - if (codepoint == 0x3f) codepointByteCount = 1; - if (codepoint == '\n') { // NOTE: Fixed line spacing of 1.5 line-height From 41e9eda7909164724db0935a6b46566d4585135e Mon Sep 17 00:00:00 2001 From: anon Date: Sun, 2 Apr 2023 11:09:04 +0100 Subject: [PATCH 3/4] Simplified progressing through a UTF-8 string in ImageTextEx and MeasureTextEx. This change matches existing precedent in DrawTextEx --- src/rtext.c | 4 ++-- src/rtextures.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 951844098898..c988533d09e7 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1201,7 +1201,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing int letter = 0; // Current character int index = 0; // Index position in sprite font - for (int i = 0; i < size; i++) + for (int i = 0; i < size;) { byteCounter++; @@ -1209,7 +1209,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing letter = GetCodepointNext(&text[i], &next); index = GetGlyphIndex(font, letter); - i += next - 1; + i += next; if (letter != '\n') { diff --git a/src/rtextures.c b/src/rtextures.c index 2249b609f2ca..bd652e6b3989 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1266,7 +1266,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co // Create image to store text imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK); - for (int i = 0; i < size; i++) + for (int i = 0; i < size;) { // Get next codepoint from byte string and glyph index in font int codepointByteCount = 0; @@ -1292,7 +1292,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co else textOffsetX += font.glyphs[index].advanceX + (int)spacing; } - i += (codepointByteCount - 1); // Move text bytes counter to next codepoint + i += codepointByteCount; // Move text bytes counter to next codepoint } // Scale image depending on text size From 101d30556e5aac799a93fda418376f7106cc6798 Mon Sep 17 00:00:00 2001 From: anon Date: Sun, 2 Apr 2023 12:28:55 +0100 Subject: [PATCH 4/4] GetCodepointNext: Add 10xxxxxx checks to multibyte encodings. --- src/rtext.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rtext.c b/src/rtext.c index c988533d09e7..dda994874cbc 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1894,18 +1894,21 @@ int GetCodepointNext(const char *text, int *codepointSize) if (0xf0 == (0xf8 & ptr[0])) { // 4 byte UTF-8 codepoint + if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]); *codepointSize = 4; } else if (0xe0 == (0xf0 & ptr[0])) { // 3 byte UTF-8 codepoint */ + if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]); *codepointSize = 3; } else if (0xc0 == (0xe0 & ptr[0])) { // 2 byte UTF-8 codepoint + if((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } //10xxxxxx checks codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]); *codepointSize = 2; }