Another TextUtilities wrap wrong

A discussion forum for the JCommon class library.

Another TextUtilities wrap wrong

Postby 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.
fischerw
 
Posts: 3
Joined: Wed Aug 25, 2010 6:16 pm

Re: Another TextUtilities wrap wrong

Postby 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: Ask your company to buy the JFreeChart Developer Guide
:idea: Check out other products sold by my company Object Refinery Limited
david.gilbert
JFreeChart Project Leader
 
Posts: 11650
Joined: Fri Mar 14, 2003 10:29 am

Re: Another TextUtilities wrap wrong

Postby venus6 » Wed Jun 13, 2012 7:45 am

Thanks for the analysis.
venus6
 
Posts: 1
Joined: Wed Jun 13, 2012 7:35 am

Re: Another TextUtilities wrap wrong

Postby lyzahundley » Tue Dec 11, 2012 4:34 am

appreciate this one.
lyzahundley
 
Posts: 1
Joined: Tue Dec 11, 2012 4:32 am

Re: Another TextUtilities wrap wrong

Postby abxbe » Fri Apr 10, 2015 2:13 pm

fischerw wrote: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 Paris airport transportation 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.


Thanks for great job. your analysis is more helpfull. Now am able to fix my JCommom problem.
abxbe
 
Posts: 2
Joined: Fri Apr 10, 2015 2:07 pm


Return to JCommon

Who is online

Users browsing this forum: No registered users and 1 guest