RTFEditorKit Has No Bullet List Support
Posted by admin on
November 24, 2010
I was trying to make an editor component that included the same functionalities that you would see in editing softwares. Functions like bold, underline, italicize, left align and others. The component I used is a JTextPane and since the code deals with RTF, I used an RTFEditorKit as the EditorKit.
Common functions like bold, underline, italicize, left-right-center align, cut, copy and paste are very simple since Action classes are already part of DefaultEditorKit and RTFEditorKit respectively. However, I was stumped when I tried to incorporate bullet and numbered lists. It seems they are not supported in RTFEditorKit. If the JTextPane would have HTML formatting, it would have been possible but since the format is in RTF, the only possible way for this to happen is to create my own.
Well, I did not. It is not an easy task. I would have to familiarize myself with a bevy of other classes. I did manage to find a working sample of a bullet and numbered list but the code based itself on HTML content. Oh well … the last alternative would be to buy a commercial 3rd party library of an editor.
I also learned that it was not the Java Dev Team responsible for creating the RTF support. And unless they are going to add this feature in the next JDK version (7), I will consider the RTFEditorKit class as incomplete.
Donations appreciated. Every little $ helps. Or click Google +1.Convert New Line To Pilcrow & Space To Dot In JTextPane’s RTFEditorKit
Posted by admin on
August 16, 2010
An RTF document contains a set of code when you view it in a text editor, codes that only RTF readers and clients can understand. Replacing contents in an RTF document can be tricky. The source code in this post converts new line characters into pilcrow symbols and space characters as dot symbols (see image).
The source code was made by my friend sumpix. To correctly make use of this class, make sure that the RTFEditorKit uses a DefaultStyledDocument as its document. The JTextPane object passed to the RTFEditorKit class will have a KeyListener that when ALT-P is pressed, conversion of new lines and space characters to their intended symbols will occur. Any key pressed after that will convert it back to its original.
To use the class, do this:
JTextPane jTextPane1 = new JTextPane(); MyRTFEditorKit rtfkit = new MyRTFEditorKit(jTextPane1); jTextPane1.setEditorKit(rtfkit); rtfkit.setDocument(new DefaultStyledDocument(new StyleContext()));
Get the customized RTFEditorKit class here.
public class MyRTFEditorKit extends RTFEditorKit {
private JTextPane editorPane;
private MutableAttributeSet attr = null;
private String pilcrow = "\\\\'b6";
private Character space = new Character('\u0020');
private String middleDot = "\\\\'b7";
private String parRegex = "\\\\par";
private String par = "\\par";
private String[] extraSpace = new String[]{"\\ul0\\par", "\\ul0\\sl0\\par"};
private String[] duplicateMiddleDot = new String[]{"\\\\ul", "\\\\ul0", "\\\\b", "\\\\b0", "\\\\i", "\\\\i0"};
private String[] discardthis = new String[]{"\\\\pard"};
private String cfIndex = "\\cf";
private String cfIndexRegex = "\\\\cf";
private String fsIndexRegex = "\\\\fs";
private boolean onPilcrow;
private DefaultStyledDocument doc;
public MyRTFEditorKit(JTextPane editorPane) {
this.editorPane = editorPane;
this.editorPane.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent ke) {
if (ke.isAltDown() && ke.getKeyCode() == KeyEvent.VK_P) {
if (!onPilcrow) {
onPilcrow = true;
} else {
onPilcrow = false;
}
doReplaceSpaceNewLineMarker(false);
return;
}
if (onPilcrow) {
doReplaceSpaceNewLineMarker(true);
}
}
});
}
private String scanOnPilcrowMode(String source) {
int pos = 0;
String text = source;
String a = null;
String b = null;
int ipos = 0;
int kpos = 0;
String ib = null;
String kb = null;
Pattern pPar = Pattern.compile(parRegex);
Matcher mPar = pPar.matcher(text);
text = mPar.replaceAll(pilcrow + parRegex);
pos = text.indexOf(par);
a = text;
b = null;
source = "";
while (pos > 0) {
pos += 4;
b = a.substring(0, pos);
ipos = b.indexOf(cfIndex);
if (ipos > 0) {
ib = b.substring(ipos);
kpos = ib.indexOf(space);
if (kpos > 0) {
kb = ib.substring(kpos + 1);
kb = kb.replaceAll(Character.toString(space), middleDot);
if (b.charAt(0) != '{') {
b = b.substring(0, ipos + kpos + 1);
b = b.replaceAll(Character.toString(space), middleDot) + kb;
} else {
b = b.substring(0, ipos + kpos + 1) + kb;
}
} else {
if (b.charAt(0) != '{') {
b = b.replaceAll(Character.toString(space), middleDot);
}
}
} else {
b = b.replaceAll(Character.toString(space), middleDot);
}
source += b;
a = a.substring(pos);
pos = a.indexOf(par);
if (pos == -1 && !a.trim().equals("")) {
a += pilcrow.substring(1)+ par;
pos = a.indexOf(par);
}
}
text = scanMiddleDotDuplicate(source);
return text;
}
private String scanNotOnPilcrowMode(String source) {
Pattern pPilcrow = Pattern.compile(pilcrow + parRegex);
Matcher mPilcrow = pPilcrow.matcher(source);
source = mPilcrow.replaceAll(parRegex);
source = scanMiddleDotDuplicate(source);
source = source.replaceAll(middleDot, Character.toString(space));
return source;
}
private String scanMiddleDotDuplicate(String source) {
for (int i = 0; i < duplicateMiddleDot.length; i++) { if (onPilcrow) { source = source.replaceAll(duplicateMiddleDot[i] + middleDot, duplicateMiddleDot[i] + Character.toString(space)); } else { source = source.replaceAll(duplicateMiddleDot[i] + middleDot, duplicateMiddleDot[i] + Character.toString(space) + middleDot); } } String[] cfs = source.split(cfIndexRegex); if (cfs.length > 0) {
for (int i = 0; i < cfs.length; i++) { if (onPilcrow) { source = source.replaceAll((cfIndexRegex) + i + middleDot, (cfIndexRegex + i) + Character.toString(space)); } else { source = source.replaceAll((cfIndexRegex) + i + middleDot, (cfIndexRegex + i) + Character.toString(space) + middleDot); } } } String[] fss = source.split(fsIndexRegex); if (fss.length > 0) {
//test
int fssTemp = 100;
for (int i = 0; i < fssTemp; i++) {
if (onPilcrow) {
source = source.replaceAll((fsIndexRegex) + i + middleDot, (fsIndexRegex + i) + Character.toString(space));
} else {
source = source.replaceAll((fsIndexRegex) + i + middleDot, (fsIndexRegex + i) + Character.toString(space) + middleDot);
}
}
}
return source;
}
private String scanExtraSpace(String source) {
for (int i = 0; i < extraSpace.length; i++) { int pos = source.indexOf(extraSpace[i]); if (pos > 0) {
source = source.substring(0, pos);
}
}
return source;
}
private String scanDiscardItems(String source) {
for (int i = 0; i < discardthis.length; i++) {
Pattern p = Pattern.compile(discardthis[i]);
Matcher m = p.matcher(source);
source = m.replaceAll("");
}
return source;
}
private String getDocumentString() throws IOException, BadLocationException {
ByteArrayOutputStream str = new ByteArrayOutputStream();
write(str, doc, 0, doc.getLength());
return scanDiscardItems(str.toString());
}
public void doReplaceSpaceNewLineMarker(boolean onKeyPressed) {
try {
String text = getDocumentString();
System.out.println("before:" + text + "]");
text = scanExtraSpace(text);
if (onKeyPressed && onPilcrow) {
onPilcrow = false;
}
if (onPilcrow) {
text = scanOnPilcrowMode(text);
} else {
text = scanNotOnPilcrowMode(text);
}
System.out.println("after:" + text);
editorPane.setText(text);
} catch (Exception e) {
e.printStackTrace();
}
}
public void setDefaultStyledDocument(DefaultStyledDocument doc) {
this.doc = doc;
}
}
Donations appreciated. Every little $ helps. Or click Google +1.









