Reputation: 9928
How can I create a Java/Swing text component that is both styled and has a custom font? I want to highlight a small portion of the text in red, but at the same time use a custom (embedded via Font.createFont
) font. JLabel accepts HTML text, which allows me to highlight a portion of the text, but it ignores font settings when rendering HTML. Other text components such as JTextArea will use the custom font, but they won't render HTML. What's the easiest way to do both?
Here's an example of using JTextPane unsuccessfully:
JTextPane textPane = new JTextPane();
textPane.setFont(myCustomFont);
textPane.setText(text);
MutableAttributeSet attributes = new SimpleAttributeSet();
StyleConstants.setForeground(attributes, Color.RED);
textPane.getStyledDocument().setCharacterAttributes(
text.indexOf(toHighlight),
toHighlight.length(),
attributes, true
);
This successfully displays the text with the "toHighlight" portion highlighted in red, but it doesn't use myCustomFont. Note that I could set a String font with StyleConstants.setFontFamily()
, but not a custom font.
Upvotes: 1
Views: 3566
Reputation:
I had the same problem when writing a program in Clojure, ie. using fonts loaded from TTF in a JEditorPane displaying HTML text. The solution here worked all right - I copy the interesting part here for future reference:
(def font1 (with-open [s (FileInputStream. "SomeFont.ttf")]
(.deriveFont (Font/createFont Font/TRUETYPE_FONT s) (float 14))))
(def font2 (Font. "SansSerif") Font/PLAIN 14)
(let [editor (JEditorPane. "text/html" "")]
(.setDocument editor
(proxy [HTMLDocument] []
(getFont [attr]
(if (= (.getAttribute attr StyleConstants/FontFamily)
"MyFont")
font1
font2)))))
This assumes that the HTML document refers to a font-family "MyFont", e.g. with a CSS snippet like
p { font-family: "MyFont" }
Note that with this you have to handle all font requests. This is because of the limitation of proxy not being able to call the member functions of the superclass. Also, if you want to handle different font sizes, you have to do that "manually", checking the StyleConstants/FontSize attribute and creating a font with deriveFont accordingly.
I hope this will help somebody :)
Upvotes: 0
Reputation: 9928
jfpoilpret's solution worked perfectly! For posterity's sake, here's a working code snippet:
JTextPane textPane = new JTextPane();
textPane.setStyledDocument(new DefaultStyledDocument(new StyleContext() {
@Override
public Font getFont(AttributeSet attr) {
return myCustomFont;
}
}));
textPane.setText(text);
MutableAttributeSet attributes = new SimpleAttributeSet();
StyleConstants.setForeground(attributes, Color.RED);
textPane.getStyledDocument().setCharacterAttributes(
text.indexOf(toHighlight),
toHighlight.length(),
attributes, true
);
Thanks, jfpoilpret!
Upvotes: 2
Reputation: 10519
OK, I see the problem better now.
After checking some Swing source code, it is clear you cannot use the DefaultStyledDocument
and have it use a physical font (one you created yourself with createFont
) out of the box.
However, what I think you could do is implement your own StyleContext
this way:
public class MyStyleContext extends javax.swing.text.StyleContext
{
@Override public Font getFont(AttributeSet attr)
{
Font font = attr.getAttribute("MyFont");
if (font != null)
return font;
else
return super.getFont(attr);
}
}
Then you have to:
DefaultStyledDocument
with
a new MyStyleContext()
JTextPane
attributes.addAttribute("MyFont", myCustomFont);
in your snippet aboveI did not try it but I think it should work or it might be a good path to investigate.
Upvotes: 2
Reputation: 10519
You should try to use JEditorPane or JTextPane instead.
They allow rich style in the content, at the price of a more complex API. Unfortunately, if you are in search of a pixel-prefect UI, they also have an additional problem: they don't support baseline-alignment (Java 6 feature).
Upvotes: 0