JDK-4280944 : Attributes on last newline character cause undesirable extra spacing.
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.3.0,1.4.0
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: generic,solaris_7
  • CPU: generic,sparc
  • Submitted: 1999-10-13
  • Updated: 2006-05-23
Related Reports
Duplicate :  
Description
When displaying text with font smaller than the default font, the last line contains extra spacing. This is a result of the last newline character being considered during layout.

Original report:
------------------------------------

Name: krT82822			Date: 10/13/99


10/13/99 eval1127@eng -- reproducible in kestrel-RA.  Am submitting reference bug #.

Take my application, run it, zomm in a few times, select all,
press button "smaller". You'll see that the line spacing
between the last row and the on before the last row is not
adapted.

See the following code:

import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
import java.lang.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class Test extends JFrame {
  public static final String ORIG_SIZE = "orig_size";
  StyleContext styles;
  float zoom = 1.0f;

  Hashtable actionTable = new Hashtable();

  public Test() {
    styles = new StyleContext();
    Style def = styles.getStyle(StyleContext.DEFAULT_STYLE);
    def.addAttribute(ORIG_SIZE, new Integer(12));
    StyleConstants.setFontSize(def, 12);

    Style sty = styles.addStyle("normal", def);
    sty.addAttribute(ORIG_SIZE, new Integer(12));
    StyleConstants.setFontFamily(sty, "TimesRoman");

    updateStyles();

    JTextPane textPane = new JTextPane();
    StyledDocument doc = textPane.getStyledDocument();

    Action[] actions = textPane.getActions();
    for (int i = 0; i < actions.length; i++) {
      actionTable.put(actions[i].getValue(Action.NAME), actions[i]);
    }
    
    textPane.setLogicalStyle(styles.getStyle("normal"));
   
    try {
      doc.insertString(0, "this\nis\nsome\ntext", styles.getStyle("normal"));
    } catch (Exception ex) {
      ex.printStackTrace();
    }

    JToolBar toolbar = new JToolBar();
    JButton button = null;
    
    Container contentPane = getContentPane();
    contentPane.add(toolbar, BorderLayout.NORTH);
    contentPane.add(textPane, BorderLayout.CENTER);

    TextAction inAction = new TextAction("In") {
      public void actionPerformed(ActionEvent e) {
        JTextPane activePane = (JTextPane)getTextComponent(e);
        if (activePane != null) {
          try {
            zoom = zoom * 2.0f;
            updateStyles();
            updateFontSize(activePane);
            activePane.revalidate();
          } catch (Exception ex) {
            ex.printStackTrace();
          }
        }
      }
    };

    TextAction outAction = new TextAction("Out") {
      public void actionPerformed(ActionEvent e) {
        JTextPane activePane = (JTextPane)getTextComponent(e);
        if (activePane != null) {
          try {
            zoom = zoom * 0.5f;
            updateStyles();
            updateFontSize(activePane);
            activePane.revalidate();
          } catch (Exception ex) {
            ex.printStackTrace();
          }
        }
      }
    };

    TextAction selAction = new TextAction("Smaller") {
      public void actionPerformed(ActionEvent e) {
        JTextPane activePane = (JTextPane)getTextComponent(e);
        StyledDocument sd = activePane.getStyledDocument();
        int selStart = activePane.getSelectionStart();
        int selEnd = activePane.getSelectionEnd();
        int start, end = selStart;
        while (end < selEnd) {
          Element element = sd.getCharacterElement(end);
          start = Math.max(selStart, element.getStartOffset());
          end = Math.min(selEnd, element.getEndOffset());
          AttributeSet oldset = element.getAttributes();
          MutableAttributeSet newset = new SimpleAttributeSet(oldset);

          newset.removeAttribute(ORIG_SIZE);
          newset.removeAttribute(StyleConstants.FontConstants.Size);
          int newrefsize = 8;
          int newsize = (int)((float)newrefsize * zoom);
          newset.addAttribute(ORIG_SIZE,  new Integer(newrefsize));
          newset.addAttribute(StyleConstants.FontConstants.Size,  new Integer(newsize));

          sd.setCharacterAttributes(start, end-start, newset, true);
        }
        start = selStart;
        end = selStart;
        while (end < selEnd) {
          Element element = sd.getParagraphElement(end);
          start = Math.max(selStart, element.getStartOffset());
          end = Math.min(selEnd, element.getEndOffset());
          AttributeSet oldset = element.getAttributes();
          MutableAttributeSet newset = new SimpleAttributeSet(oldset);

          newset.removeAttribute(ORIG_SIZE);
          newset.removeAttribute(StyleConstants.FontConstants.Size);
          int newrefsize = 8;
          int newsize = (int)((float)newrefsize * zoom);
          newset.addAttribute(ORIG_SIZE,  new Integer(newrefsize));
          newset.addAttribute(StyleConstants.FontConstants.Size,  new Integer(newsize));

          sd.setParagraphAttributes(start, end-start, newset, true);
        }
        activePane.revalidate();
      }
    };

    button = toolbar.add(inAction);
    button.setText("In");
    button.setToolTipText("in button");

    button = toolbar.add(outAction);
    button.setText("Out");
    button.setToolTipText("out button");

    button = toolbar.add(selAction);
    button.setText("Smaller");
    button.setToolTipText("smaller");
  }

  public void updateFontSize(JTextPane tp) {
    propagate(tp.getStyledDocument().getDefaultRootElement());
  }

  public void propagate(Element element) {
    Document document = element.getDocument();
    if (document instanceof StyledDocument) {
      StyledDocument styled = (StyledDocument)document;
      int start = element.getStartOffset();
      int end = element.getEndOffset();
      AttributeSet set = element.getAttributes();
      if (element.getElementCount() > 0) {
        AttributeSet as = styled.getParagraphElement(start).getAttributes();
        Enumeration enum = ((AbstractDocument.AbstractElement)element).children();
        while (enum.hasMoreElements()) {
          propagate((Element)enum.nextElement());
        }
      } else {
        styled.setCharacterAttributes(start, end-start, createAttributeSetFrom(set, zoom), true);
      }
    }
  }

  public AttributeSet createAttributeSetFrom(AttributeSet set, float zoom) {
    Object origSize = set.getAttribute(ORIG_SIZE);
    if (origSize != null) {
      int newSize = (int)((float)((Integer)origSize).intValue() * zoom);
      MutableAttributeSet newset = new SimpleAttributeSet(set);
      newset.addAttribute(StyleConstants.FontConstants.Size,  new Integer(newSize));
      return (AttributeSet)newset;
    } else {
      return set;
    }
  }

  public void updateStyles() {
    Style sty, ref;
    Enumeration names = styles.getStyleNames();
    while (names.hasMoreElements()) {
      String name = (String)names.nextElement();
      sty = styles.getStyle(name);
      Object o = sty.getAttribute(ORIG_SIZE);
      int i = ((Integer)o).intValue();
      StyleConstants.setFontSize(sty, (int)((float)i * zoom));
    }
  }

  public static void main(String[] args) {
    Test test = new Test();
    test.pack();
    test.setSize(500, 500);
    test.show();
  }
}
(Review ID: 96472) 
======================================================================

Comments
WORK AROUND Name: krT82822 Date: 10/13/99 No workaround found. ======================================================================
25-09-2004

EVALUATION Changed the synopsis to reflect the actual problem. Please also see 4480453 (in particular the work-around section). ###@###.### 2002-02-19 Name: dsR10138 Date: 04/14/2004 AbstractDocument.getLength() doesn't take into account the last character which is implied CR. So there's no possibility to change attributes on this implied CR. The idea of the fix is to update the attributes of the last implied CR in DefaultStyledDocument.setCharacterAttributes if the changes are being made till the document end. ###@###.### 2004-04-01 ======================================================================
01-04-2004