Hi, I have posted this before, but received no comments.
There is an off by one bug in GetStringBreak() and as such it returns too many characters in certain cases. Attached is a test case, which will need an 'arial.ttf' in the same directory as the test app is being run from. For this test case, GetStringWidth() will return 731 pixels instead of sth < 730 as one would expect. Basically because GetStringBreak() returns one character too much (in this case) in ret_str_length, i.e. for the test app it should be 59 instead of 60 (for the first line of text). The attached patch fixes the issue. Cheers, Andre'
#include <cstdio> #include <directfb.h> #include <string.h> #include <string> int main (int argc, char *argv[]) { IDirectFB *dfb; IDirectFBFont *font; DirectFBInit (&argc, &argv); DirectFBCreate (&dfb); DFBFontDescription fontDsc; fontDsc.flags = (DFBFontDescriptionFlags) (DFDESC_HEIGHT | DFDESC_ATTRIBUTES); fontDsc.height = 25; fontDsc.attributes = DFFA_NONE; //maximum pixel width per line int maxWidth = 730; int retWidth = 0, retStrLength = 0; //input text string // const char* str = "TTAppuyez sur la touche du menu contextuel de la télécommande de l'appareil. Si cette touche n'existe pas, appuyez sur la touche de mise sous tension (Power ou On-Off)."; const char *str = "Appuyez sur la touche du menu contextuel de la télécommande de l'appareil. Si cette touche n'existe pas, appuyez sur la touche de mise sous tension (Power ou On-Off)."; const char *retNextLine = NULL; //This requires a font named "arial.ttf" be in the same directory as the application. The standard Windows Arial font will work fine. dfb->CreateFont (dfb, "arial.ttf", &fontDsc, &font); do { font->GetStringBreak (font, str, -1, maxWidth, &retWidth, &retStrLength, &retNextLine); printf ("chars: %d\n", retStrLength); /* at this point retStrLength is the number of CHARACTERS that fit in the line (not the number of BYTES) - convert to bytes */ int idx; for (idx = 0; idx < retStrLength; ++idx) { if ((unsigned char)str[idx] >= 0xc2 && (unsigned char)str[idx] <= 0xdf) { /* UTF-8 2 byte character */ ++retStrLength; } else if ((unsigned char) str[idx] >= 0xe0 && (unsigned char) str[idx] <= 0xef) { /* UTF-8 3 byte character */ retStrLength += 2; } else if ((unsigned char) str[idx] >= 0xf0 && (unsigned char) str[idx] <= 0xf4) { /* UTF-8 4 byte character */ retStrLength += 3; } else if (str[idx] == '\n') { /* if there is a new line character in the string, cut it out. The string has already been broken into the correct number of lines so we don't actually need the newline character any more. */ --retStrLength; } } /* at this point retStrLength is the number of BYTES that fit in the line (not the number of CHARACTERS). */ printf ("bytes: %d\n", retStrLength); printf ("string: retWidth: %d after bytes: %d: '%.*s'\n next line: '%s'\n", retWidth, retStrLength, retStrLength, str, retNextLine); std::string currentLine = str; //get the substring that GetStringBreak should fit within maxWidth currentLine = currentLine.substr (0, retStrLength); printf ("cstring:'%s'\n", currentLine.c_str ()); printf ("string:'%.*s', bytes: %d\n", retStrLength, str, retStrLength); printf ("GetStringBreak retWidth:%d strLen:%d\n", retWidth, retStrLength); int returnWidth = 0; font->GetStringWidth (font, str, retStrLength, &returnWidth); printf ("GetStringWidth:%d len:%d \n", returnWidth, retStrLength); DFBRectangle logRect, inkRect; font->GetStringExtents (font, str, retStrLength, &logRect, &inkRect); printf ("GetStringExtents - logical x/w:%d/%d ink x/w:%d/%d\n\n", logRect.x, logRect.w, inkRect.x, inkRect.w); if (logRect.w > maxWidth || returnWidth > maxWidth) { printf ("!!!!!!!!!!! bug (logRect.w || returnWidth) > maxWidth !!!!!!!!!!!\n"); } //This is the new string that has the length modified based on the number of multi-bytes characters currentLine = currentLine.substr (0, retStrLength); printf ("\nnew string:*%s*\n\n", currentLine.c_str ()); //After modifing the string length to account for multibyte characters, the width returned by // GetStringWidth and GetStringExtents does not match the width returned by GetStringBreak, and // often will go beyond the maximum width specified as a parameter to GetStringBreak. printf ("*****************************************\n\n"); ///Check if the returned next line of text is equal to the line of text passed in. /// DirectFB says thats GetStringBreak() will return NULL or a pointer to the last /// line if there are no more breaks in the string. if (str == retNextLine) { break; } // str+=retStrLength; str = retNextLine; //str = retNextLine; } while (retNextLine != NULL); return 0; }
>From dc8b2c902eb3fab95b8a8b296600f09200903f9f Mon Sep 17 00:00:00 2001 From: =?utf-8?q?Andr=C3=A9=20Draszik?= <andre.dras...@st.com> Date: Thu, 3 Sep 2009 13:53:51 +0100 Subject: [PATCH 3/4] font: fix off-by-one bug in IDirectFBFont_GetStringBreak() MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit GetStringBreak() returns one character too much (in certain cases) in ret_str_length. Signed-off-by: André Draszik <andre.dras...@st.com> --- src/media/idirectfbfont.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/src/media/idirectfbfont.c b/src/media/idirectfbfont.c index b7f2e59..e440cf0 100644 --- a/src/media/idirectfbfont.c +++ b/src/media/idirectfbfont.c @@ -485,7 +485,6 @@ IDirectFBFont_GetStringBreak( IDirectFBFont *thiz, do { *ret_width = width; - length ++; current = DIRECT_UTF8_GET_CHAR( string ); @@ -498,6 +497,8 @@ IDirectFBFont_GetStringBreak( IDirectFBFont *thiz, *ret_width = width; } + length++; + ret = dfb_font_decode_character( font, data->encoding, current, &index ); if (ret) continue; -- 1.6.3.3
_______________________________________________ directfb-dev mailing list directfb-dev@directfb.org http://mail.directfb.org/cgi-bin/mailman/listinfo/directfb-dev