SDL_ttf: If wrapLength is 0, only wrap on new lines.

From 0a2f4c663cf5b97d1608fea4c4b5c8d330e92b78 Mon Sep 17 00:00:00 2001
From: Sylvain <[EMAIL REDACTED]>
Date: Mon, 3 Jan 2022 17:32:26 +0100
Subject: [PATCH] If wrapLength is 0, only wrap on new lines.

---
 SDL_ttf.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++------
 SDL_ttf.h |  3 +++
 2 files changed, 60 insertions(+), 7 deletions(-)

diff --git a/SDL_ttf.c b/SDL_ttf.c
index dc456bb..14dc40a 100644
--- a/SDL_ttf.c
+++ b/SDL_ttf.c
@@ -3285,6 +3285,14 @@ static SDL_bool CharacterIsDelimiter(Uint32 c)
     return SDL_FALSE;
 }
 
+static SDL_bool CharacterIsNewLine(Uint32 c)
+{
+    if (c == '\n') {
+        return SDL_TRUE;
+    }
+    return SDL_FALSE;
+}
+
 static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text, const str_type_t str_type,
         SDL_Color fg, SDL_Color bg, Uint32 wrapLength, const render_mode_t render_mode)
 {
@@ -3336,9 +3344,15 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text
         goto failure;
     }
 
+    /* wrapLength is unsigned, but don't allow negative values */
+    if ((int)wrapLength < 0) {
+        TTF_SetError("Invalid parameter 'wrapLength'");
+        goto failure;
+    }
+
     numLines = 1;
 
-    if (wrapLength > 0 && *text_cpy) {
+    if (*text_cpy) {
         int maxNumLines = 0;
         size_t textlen = SDL_strlen(text_cpy);
         numLines = 0;
@@ -3350,7 +3364,7 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text
 
             if (numLines >= maxNumLines) {
                 char **saved = strLines;
-                maxNumLines += (width / wrapLength) + 1;
+                maxNumLines += (width / (wrapLength + 1)) + 1;
                 strLines = (char **)SDL_realloc(strLines, maxNumLines * sizeof (*strLines));
                 if (strLines == NULL) {
                     strLines = saved;
@@ -3368,6 +3382,7 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text
 
             while (textlen > 0) {
                 int inc = 0;
+                int is_delim;
                 Uint32 c = UTF8_getch(text_cpy, textlen, &inc);
                 text_cpy += inc;
                 textlen -= inc;
@@ -3378,8 +3393,11 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text
 
                 char_count += 1;
 
+                /* With wrapLength == 0, normal text rendering but newline aware */
+                is_delim = (wrapLength > 0) ?  CharacterIsDelimiter(c) : CharacterIsNewLine(c);
+
                 /* Record last delimiter position */
-                if (CharacterIsDelimiter(c)) {
+                if (is_delim) {
                     save_textlen = textlen;
                     save_text = text_cpy;
                     /* Break, if new line */
@@ -3406,10 +3424,42 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text
     lineskip = TTF_FontLineSkip(font);
     rowHeight = SDL_max(height, lineskip);
 
-    width  = (numLines > 1) ? wrapLength : width;
-    /* Don't go above wrapLength if you have only 1 line which hasn't been cut */
-    if (wrapLength > 0) {
-        width = SDL_min((int)wrapLength, width);
+    if (wrapLength == 0) {
+        /* Find the max of all line lengths */
+        if (numLines > 1) {
+            width = 0;
+            for (i = 0; i < numLines; i++) {
+                char save_c = 0;
+                int w, h;
+
+                /* Add end-of-line */
+                if (strLines) {
+                    text = strLines[i];
+                    if (i + 1 < numLines) {
+                        save_c = strLines[i + 1][0];
+                        strLines[i + 1][0] = '\0';
+                    }
+                }
+
+                if (TTF_SizeUTF8(font, text, &w, &h) == 0) {
+                    width = SDL_max(w, width);
+                }
+
+                /* Remove end-of-line */
+                if (strLines) {
+                    if (i + 1 < numLines) {
+                        strLines[i + 1][0] = save_c;
+                    }
+                }
+            }
+        }
+    } else {
+        if (numLines > 1) {
+            width = wrapLength;
+        } else {
+            /* Don't go above wrapLength if you have only 1 line which hasn't been cut */
+            width = SDL_min((int)wrapLength, width);
+        }
     }
     height = rowHeight + lineskip * (numLines - 1);
 
diff --git a/SDL_ttf.h b/SDL_ttf.h
index b85fd2f..f362ccc 100644
--- a/SDL_ttf.h
+++ b/SDL_ttf.h
@@ -222,6 +222,7 @@ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Solid(TTF_Font *font,
    to the text color.
    Text is wrapped to multiple lines on line endings and on word boundaries
    if it extends beyond wrapLength in pixels.
+   If wrapLength is 0, only wrap on new lines.
    This function returns the new surface, or NULL if there was an error.
 */
 extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Solid_Wrapped(TTF_Font *font,
@@ -260,6 +261,7 @@ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Shaded(TTF_Font *font,
    while other pixels have varying degrees of the foreground color.
    Text is wrapped to multiple lines on line endings and on word boundaries
    if it extends beyond wrapLength in pixels.
+   If wrapLength is 0, only wrap on new lines.
    This function returns the new surface, or NULL if there was an error.
 */
 extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded_Wrapped(TTF_Font *font,
@@ -297,6 +299,7 @@ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Blended(TTF_Font *font,
    using alpha blending to dither the font with the given color.
    Text is wrapped to multiple lines on line endings and on word boundaries
    if it extends beyond wrapLength in pixels.
+   If wrapLength is 0, only wrap on new lines.
    This function returns the new surface, or NULL if there was an error.
 */
 extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended_Wrapped(TTF_Font *font,