Another TextUtilities wrap wrong

A discussion forum for the JCommon class library.
Locked
fischerw
Posts: 3
Joined: Wed Aug 25, 2010 6:16 pm
antibot: No, of course not.

Another TextUtilities wrap wrong

Post by fischerw » Tue Dec 13, 2011 4:49 am

I am using a pie chart to display data. The labels for the pie sections are sometimes very long, with few, if any, line break characters. I was setting the label width maximum, but getting bizarre results when the label was drawn.

For example, the label "JmsMessaging.someFunction()", was being drawn as
JmsM
essagi
ng.someFunction()
which caused some of the last line to be drawn out of the plot area.

I tracked the problem down to the private function nextLineBreak() in TextUtilities. It would call the BreakIterator next() method to get the next token, then shorten it until it would fit in the allotted maximum width. The first call to next() would return the index of the '.' in the label, the next would return the index of the ')', and the third would return -1 to indicate it was done.

Here is the original code.

Code: Select all

    private static int nextLineBreak(final String text, final int start,
            final float width, final BreakIterator iterator,
            final TextMeasurer measurer) {

        // this method is (loosely) based on code in JFreeReport's
        // TextParagraph class
        int current = start;
        int end;
        float x = 0.0f;
        boolean firstWord = true;
        int newline = text.indexOf('\n', start);
        if (newline < 0) {
            newline = Integer.MAX_VALUE;
        }
        while (((end = iterator.next()) != BreakIterator.DONE)) {
            if (end > newline) {
                return newline;
            }
            x += measurer.getStringWidth(text, current, end);
            if (x > width) {
                if (firstWord) {
                    while (measurer.getStringWidth(text, start, end) > width) {
                        end--;
                        if (end <= start) {
                            return end;
                        }
                    }
                    return end;
                }
                else {
                    end = iterator.previous();
                    return end;
                }
            }
            // we found at least one word that fits ...
            firstWord = false;
            current = end;
        }
        return BreakIterator.DONE;
    }
The problem is that when end is decremented, the iterator knows nothing about it, so the call to next() looks beyond the last returned index.

Here is the modified code.

Code: Select all

  private static int nextLineBreak(final String text, final int start,
          final float width, final BreakIterator iterator,
          final TextMeasurer measurer) {

      // this method is (loosely) based on code in JFreeReport's
      // TextParagraph class
      int current = start;
      int end;
      float x = 0.0f;
      boolean firstWord = true;
      int newline = text.indexOf('\n', start);
      if (newline < 0) {
          newline = Integer.MAX_VALUE;
      }
/***************************************************************************************/
/*  HERE IS THE CHANGE                                                                        */
      while (((end = iterator.following(current)) != BreakIterator.DONE)) {
/***************************************************************************************/
          if (end > newline) {
              return newline;
          }
          // This gives us the next line, we need to process all of the 
          // string returned before asking for the next line.
          x += measurer.getStringWidth(text, current, end);
          if (x > width) {
              if (firstWord) {
                  while (measurer.getStringWidth(text, start, end) > width) {
                      end--;
                      if (end <= start) {
                          return end;
                      }
                  }
                  return end;
              }
              else {
                  end = iterator.previous();
                  return end;
              }
          }
          // we found at least one word that fits ...
          firstWord = false;
          current = end;
      }
      return BreakIterator.DONE;
  }
Just wanted to let you know for possible inclusion in a later release.

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Re: Another TextUtilities wrap wrong

Post by david.gilbert » Wed Dec 14, 2011 9:28 pm

Thanks for the analysis. I committed the fix for inclusion in JCommon 1.0.18.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

venus6
Posts: 1
Joined: Wed Jun 13, 2012 7:35 am
antibot: No, of course not.
Contact:

Re: Another TextUtilities wrap wrong

Post by venus6 » Wed Jun 13, 2012 7:45 am

Thanks for the analysis.

lyzahundley
Posts: 1
Joined: Tue Dec 11, 2012 4:32 am
antibot: No, of course not.

Re: Another TextUtilities wrap wrong

Post by lyzahundley » Tue Dec 11, 2012 4:34 am

appreciate this one.

Locked