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

Reply via email to