From 3bb1f4217dcb229c2e064e9f0e1d58a757474cc0 Mon Sep 17 00:00:00 2001 From: rubidium Date: Tue, 13 Oct 2009 20:19:34 +0000 Subject: [PATCH] (svn r17772) -Fix [FS#3264]: CJK languages don't have spaces, so for adding newlines (multi line strings) we need to (properly) handle the case when there are no spaces instead of truncating the string. --- src/gfx.cpp | 57 +++++++++++++++++++++++++------- src/gfx_func.h | 2 +- src/network/network_chat_gui.cpp | 2 +- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/src/gfx.cpp b/src/gfx.cpp index 328e6da1c6..8b9c093600 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -619,12 +619,13 @@ int DrawString(int left, int right, int top, StringID str, TextColour colour, St * starting with index 0 is the real string end. * * @param str string to check and correct for length restrictions + * @param last the last valid location (for '\0') in the buffer of str * @param maxw the maximum width the string can have on one line * @return return a 32bit wide number consisting of 2 packed values: * 0 - 15 the number of lines ADDED to the string * 16 - 31 the fontsize in which the length calculation was done at */ -uint32 FormatStringLinebreaks(char *str, int maxw) +uint32 FormatStringLinebreaks(char *str, const char *last, int maxw) { FontSize size = _cur_fontsize; int num = 0; @@ -632,6 +633,7 @@ uint32 FormatStringLinebreaks(char *str, int maxw) assert(maxw > 0); for (;;) { + /* The character *after* the last space. */ char *last_space = NULL; int w = 0; @@ -641,18 +643,49 @@ uint32 FormatStringLinebreaks(char *str, int maxw) if (IsWhitespace(c)) last_space = str; if (IsPrintable(c)) { - w += GetCharacterWidth(size, c); - /* string is longer than maximum width so we need to decide what to - * do. We can do two things: - * 1. If no whitespace was found at all up until now (on this line) then - * we will truncate the string and bail out. - * 2. In all other cases force a linebreak at the last seen whitespace */ + int char_w = GetCharacterWidth(size, c); + w += char_w; if (w > maxw) { - if (last_space == NULL) { - *Utf8PrevChar(str) = '\0'; + /* The string is longer than maximum width so we need to decide + * what to do with it. */ + if (w == char_w) { + /* The character is wider than allowed width; don't know + * what to do with this case... bail out! */ return num + (size << 16); } - str = last_space; + if (last_space == NULL) { + /* No space has been found. Just terminate at our current + * location. This usually happens for languages that do not + * require spaces in strings, like Chinese, Japanese and + * Korean. For other languages terminating mid-word might + * not be the best, but terminating the whole string instead + * of continuing the word at the next line is worse. */ + str = Utf8PrevChar(str); + size_t len = strlen(str); + char *terminator = str + len; + + /* The string location + length of the string + 1 for '\0' + * always fits; otherwise there's no trailing '\0' and it + * it not a valid string. */ + assert(terminator <= last); + assert(*terminator == '\0'); + + /* If the string is too long we have to terminate it earlier. */ + if (terminator == last) { + /* Get the 'begin' of the previous character and make that + * the terminator of the string; we truncate it 'early'. */ + *Utf8PrevChar(terminator) = '\0'; + len = strlen(str); + } + /* Also move the terminator! */ + memmove(str + 1, str, len + 1); + *str = '\0'; + /* str needs to point to the character *after* the last space */ + str++; + } else { + /* A space is found; perfect place to terminate */ + str = last_space; + } break; } } else { @@ -721,7 +754,7 @@ int GetStringHeight(StringID str, int maxw) GetString(buffer, str, lastof(buffer)); - uint32 tmp = FormatStringLinebreaks(buffer, maxw); + uint32 tmp = FormatStringLinebreaks(buffer, lastof(buffer), maxw); return GetMultilineStringHeight(buffer, GB(tmp, 0, 16)); } @@ -761,7 +794,7 @@ int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str, char buffer[DRAW_STRING_BUFFER]; GetString(buffer, str, lastof(buffer)); - uint32 tmp = FormatStringLinebreaks(buffer, maxw); + uint32 tmp = FormatStringLinebreaks(buffer, lastof(buffer), maxw); int num = GB(tmp, 0, 16); int mt = GetCharacterHeight((FontSize)GB(tmp, 16, 16)); diff --git a/src/gfx_func.h b/src/gfx_func.h index 5dbef79176..c8ba4c0d6a 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -115,7 +115,7 @@ void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3) Dimension GetStringBoundingBox(const char *str); Dimension GetStringBoundingBox(StringID strid); -uint32 FormatStringLinebreaks(char *str, int maxw); +uint32 FormatStringLinebreaks(char *str, const char *last, int maxw); int GetStringHeight(StringID str, int maxw); Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestion); void LoadStringWidthTable(); diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp index 8e9b4cc667..ef05ad931e 100644 --- a/src/network/network_chat_gui.cpp +++ b/src/network/network_chat_gui.cpp @@ -86,7 +86,7 @@ void CDECL NetworkAddChatMessage(TextColour colour, uint8 duration, const char * Utf8TrimString(buf, DRAW_STRING_BUFFER); /* Force linebreaks for strings that are too long */ - lines = GB(FormatStringLinebreaks(buf, _chatmsg_box.width - 8), 0, 16) + 1; + lines = GB(FormatStringLinebreaks(buf, lastof(buf), _chatmsg_box.width - 8), 0, 16) + 1; if (lines >= MAX_CHAT_MESSAGES) return; msg_count = GetChatMessageCount();