Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4463424

LineBreakMeasurer inconsistent with TextLayout



    • Type: Bug
    • Status: Resolved
    • Priority: P4
    • Resolution: Fixed
    • Affects Version/s: 1.3.1
    • Fix Version/s: 1.4.0
    • Component/s: client-libs
    • Labels:
    • Subcomponent:
    • Resolved In Build:
    • CPU:
    • OS:


      Name: bsC130419 Date: 05/25/2001

      java version "1.3.1-beta"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1-beta-b15)
      Java HotSpot(TM) Client VM (build 1.3.1beta-b15, mixed mode)

      LineBreakMeasurer.nextLayout() measures length in a way that is
      inconsistent with TextLayout.getBounds() and TextLayout.getAdvance(). We
      use TextLayout.getBounds() to measure the width of a string during the
      layout of the X axis of a custom component. After our component decides
      on an X-axis layout, it uses the LineBreakMeasurer to divide up the
      pargraph string into lines for Y-axis layout and then drawing. If a
      particular string is allocated exactly as much horizontal space as
      TextLayout.getBounds() indicates it should need, this algorithm fails.

      The problem is that using the width from TextLayout.getBounds() as the
      maxAdvance argument to LineBreakMeasurer.nextAdvance() doesn't yield a
      single TextLayout object with the whole string. If the string has no
      whitespace, the last character or few gets moved to a second line. If the
      string has whitespace in it, the last word is moved to a second line.
      The following code demonstrates the problem.

      import java.awt.*;
      import java.awt.font.*;
      import java.text.*;

      import javax.swing.*;

      public class TestLayout {
        static String longString = "VeryVeryVeryVeryVeryVeryVeryLongTestString";
        static String brokenString = "Line wraps where it should not.";
        JFrame jf = new JFrame();
        AttributedString asLong;
        AttributedString asBroken;
        LineBreakMeasurer lbmLong;
        LineBreakMeasurer lbmBroken;
        FontRenderContext frc;

        public TestLayout() {
          jf = new JFrame();
          jf.setVisible( true );
          Graphics g = jf.getGraphics();
          frc = ((Graphics2D) g).getFontRenderContext();

          asLong = new AttributedString( longString );
          lbmLong = new LineBreakMeasurer( asLong.getIterator(), frc );
          asBroken = new AttributedString( brokenString );
          lbmBroken = new LineBreakMeasurer( asBroken.getIterator(), frc );

        public void runTest() {
          lbmLong.setPosition( 0 );
          TextLayout tl1 = lbmLong.nextLayout( Short.MAX_VALUE );
          int neededSpace = (int) Math.ceil( tl1.getBounds().getWidth() );
          System.err.println( "End position after advancing the whole string: " +
      lbmLong.getPosition() );

          lbmLong.setPosition( 0 );
          TextLayout tl2 = lbmLong.nextLayout( neededSpace );
          // On our system, the previous println shows a position of 42
          // while this println shows a position of 41. The last character
          // is left to the next TextLayout.
          System.err.println( "End position after minimal advance: " +
      lbmLong.getPosition() );
          TextLayout tl3 = lbmLong.nextLayout( Short.MAX_VALUE );

          lbmBroken.setPosition( 0 );
          TextLayout tl4 = lbmBroken.nextLayout( Short.MAX_VALUE );
          neededSpace = (int) Math.ceil( tl4.getBounds().getWidth() );
          System.err.println( "End position after advancing the whole string: " +
      lbmBroken.getPosition() );

          lbmBroken.setPosition( 0 );
          TextLayout tl5 = lbmBroken.nextLayout( neededSpace );
          // On our system, the previous println shows a position of 31, while this
          // println shows a position of 27. The last word is left to the next
          // text layout.
          System.err.println( "End position after minimal advance: " +
      lbmBroken.getPosition() );
          TextLayout tl6 = lbmBroken.nextLayout( Short.MAX_VALUE );

          jf.setSize( 1000, 500 );
          Graphics g = jf.getGraphics();
          // tl1 shows the whole string on one line, this is the string being
          // measured.
          tl1.draw( (Graphics2D) g, 100, 100 );
          // tl2 shows all but the last character because the LineBreakMeasurer
          // doesn't return the whole string using the width measured with by
          // tl1.
          tl2.draw( (Graphics2D) g, 100, 200 );
          // tl3 shows the character that did not make it into tl2
          tl3.draw( (Graphics2D) g, 100, 220 );

          // tl4 shows the whole line the was used to measure the string
          tl4.draw( (Graphics2D) g, 100, 300 );

          // tl5 shows the line that nextLayout returned using the measurement
          // provided by tl4.getBounds().getWidth()
          tl5.draw( (Graphics2D) g, 100, 400 );

          // tl6 shows the part of the line that was left over.
          tl6.draw( (Graphics2D) g, 100, 420 );

        public static void main(String args[]) {
          TestLayout t = new TestLayout();

      We've tried using both TextLayout.getBounds() and TextLayout.getAdvance()
      to measure the string, and the problem is the same both ways. Simply
      adding some padding to the maxAdvance argument for the nextLayout call hasn't
      worked around the problem for us. The amount of padding necessary seems to
      depend on the formatting attributes of the string (though we're less
      certain of this -- we might just not have played with the padding constant
      (Review ID: 124280)




            dougfelt Doug Felt
            bstrathesunw Bill Strathearn (Inactive)
            0 Vote for this issue
            0 Start watching this issue