FULL PRODUCT VERSION :
java version "1.8.0_111"
Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 10.0.10586]
EXTRA RELEVANT SYSTEM CONFIGURATION :
JDK 1.8.111
A DESCRIPTION OF THE PROBLEM :
javax.swing.JTextPane has a method getText(), to return the text of its document. It uses StringWriter.
If we extend this class and define an utility method to append some text by:
public void append(String s) {
try {
Document doc = this.getDocument();
doc.insertString(doc.getLength(), s, null);
} catch(BadLocationException e) {
System.err.println(e);
}
}
And when adding text, we may use:
pane.setText(pane.getText() + newText);
or:
pane.append(newText());
alone and no problem. But if we use append() first and then setText(pane.getText + newText), before every \r\n another \r is added, and in output, extra blank line are added.
But if we override the getText() method with:
@Override
public String getText() {
String string = "";
try {
string = this.getDocument().getText(0, this.getDocument().getLength());
} catch (BadLocationException e) {
e.printStackTrace();
}
return string;
}
The problem disappears.
I guess it's because underneath getText() uses StringWriter.write() method, and once I have touched the attached document of a JTextPane, StringWriter behaves differently. But anyway, any side effect should be evaluated.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run my test case below to see the unexpected result, and uncomment the getText() methods to see the expected result.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Three panels should have same content.
ACTUAL -
The first panel has extra line breaks.
If the overwritten getText() method is uncommented, the problem dismisses.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package com.WindThunderStudio.JTextpaneLineSpacing;
import java.awt.BorderLayout;
import java.io.File;
import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.border.EtchedBorder;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import net.miginfocom.swing.MigLayout;
public class Test1_ChangeStyleAndAppend extends JFrame {
public class MyJTextPane extends JTextPane {
/**
* Append some text to this pane.
* @param s
*/
public void append(String s) {
try {
Document doc = this.getDocument();
doc.insertString(doc.getLength(), s, null);
} catch(BadLocationException e) {
System.err.println(e);
}
}
/**
* Append some text and change line.
* @param s
*/
public void appendLine(String s) {
try {
Document doc = this.getDocument();
doc.insertString(doc.getLength(), s + System.lineSeparator(), null);
} catch(BadLocationException e) {
System.err.println(e);
}
}
// @Override
// public String getText() {
// String string = "";
// try {
// string = this.getDocument().getText(0, this.getDocument().getLength());
// } catch (BadLocationException e) {
// e.printStackTrace();
// }
// return string;
// }
}
public Test1_ChangeStyleAndAppend() {
begin();
}
private void begin() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
MyJTextPane pane0 = new MyJTextPane();
pane0.appendLine("MyJTextPane using append() and then calling setText()");
pane0.appendLine("Second line. ");
pane0.appendLine("Third line");
pane0.setText(pane0.getText() + "At last" + System.lineSeparator());
pane0.setBorder(new EtchedBorder(EtchedBorder.RAISED));
add(pane0, BorderLayout.NORTH);
MyJTextPane pane = new MyJTextPane();
// changeLineSpacing(pane, 1.5f, false);
pane.appendLine("MyJTextPane calling appendLine()");
pane.appendLine("Second line. ");
pane.appendLine("Third line");
pane.appendLine("At last");
pane.setBorder(new EtchedBorder(EtchedBorder.RAISED));
add(pane, BorderLayout.CENTER);
JTextPane pane2 = new JTextPane();
pane2.setText("Normal JTextPane calling setText()");
pane2.setText(pane2.getText() + System.lineSeparator() + "Second line. ");
pane2.setText(pane2.getText() + System.lineSeparator() + "Third line");
pane2.setText(pane2.getText() + System.lineSeparator() + "At last");
pane2.setBorder(new EtchedBorder(EtchedBorder.RAISED));
add(pane2, BorderLayout.SOUTH);
pack();
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
Test1_ChangeStyleAndAppend frame = new Test1_ChangeStyleAndAppend();
}
});
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
1. Always stick to append() as I suggest, or setText(pane.getText() + newText()). Don't mix them.
2. Override the getText() method in JTextPane by extending it, like I do in test case.