Page 1 of 1

Another TextUtilities wrap wrong

Posted: Tue Dec 13, 2011 4:49 am
by fischerw
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.

Re: Another TextUtilities wrap wrong

Posted: Wed Dec 14, 2011 9:28 pm
by david.gilbert
Thanks for the analysis. I committed the fix for inclusion in JCommon 1.0.18.

Re: Another TextUtilities wrap wrong

Posted: Wed Jun 13, 2012 7:45 am
by venus6
Thanks for the analysis.

Re: Another TextUtilities wrap wrong

Posted: Tue Dec 11, 2012 4:34 am
by lyzahundley
appreciate this one.