Text and Fonts
In this part of the Java 2D tutorial, we will work with texts and fonts.Text and fonts
Rendering text is another pretty complicated topic. It would easily fill a specialized book. Here we only provide some basic examples.A character is a symbol that represents an item such as a letter, a digit, or punctuation. A glyph is a shape used to render a character or a sequence of characters. In Latin alphabet a glyph typically represents one character. In other writing systems, a character may be composed of several glyphs. Like ť, ž, ú, ô. These are latin characters with accents.
There are basically two types of fonts. Physical and logical. Physical fonts are the actual font libraries. Logical fonts are the five font families defined by the Java platform. Serif, SansSerif, Monospaced, Dialog, and DialogInput. Logical fonts are not actual font libraries. Logical font names are mapped to physical fonts by the Java runtime environment.
Text can be drawn on the window using various fonts. A font is a set of type characters of a particular typeface design and size. Various typefaces include Helvetica, Georgia, Times or Verdana. A collection of glyphs with a particular style form a font face. A collection of font faces forms a font family. (java.sun.com, answers.com)
System fonts
This console example will print all available fonts on your platform.AllFonts.java
package com.zetcode;Get all fonts.
import java.awt.Font;
import java.awt.GraphicsEnvironment;
public class AllFonts {
public static void main(String[] args) throws Exception {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Font[] fonts = ge.getAllFonts();
for (int i = 0; i < fonts.length; i++) {
System.out.print(fonts[i].getFontName() + " : ");
System.out.print(fonts[i].getFamily() + " : ");
System.out.print(fonts[i].getName());
System.out.println();
}
}
}
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();There are objects that are typical for a particular platform. Fonts are among these objects. The collection of fonts on a Unix, OS X and Windows platform differ. The
GraphicsEnvironment
class describes the collection GraphicsDevice
objects and Font
objects available on a particular platform. Font[] fonts = ge.getAllFonts();The
getAllFonts()
returns all fonts available in the GraphicsEnvironment
. ...This is an excerpt of all fonts on Ubuntu Linux.
Purisa : Purisa : Purisa
Rasheeq-Bold : Rasheeq-Bold : Rasheeq-Bold
Rehan : Rehan : Rehan
Rekha-normal : Rekha : Rekha-normal
Saab : Saab : Saab
Salem : Salem : Salem
Samanata : Samanata : Samanata
SansSerif.bold : SansSerif : SansSerif.bold
SansSerif.bolditalic : SansSerif : SansSerif.bolditalic
SansSerif.italic : SansSerif : SansSerif.italic
SansSerif.plain : SansSerif : SansSerif.plain
Serif.bold : Serif : Serif.bold
Serif.bolditalic : Serif : Serif.bolditalic
Serif.italic : Serif : Serif.italic
Serif.plain : Serif : Serif.plain
...
Soulmate
We will display some lyrics on the panel.Soulmate.java
package com.zetcode;Soulmate.
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Soulmate extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
RenderingHints rh =
new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(rh);
g2d.setFont(new Font("Purisa", Font.PLAIN, 13));
g2d.drawString("Most relationships seem so transitory", 20, 30);
g2d.drawString("They're all good but not the permanent one", 20, 60);
g2d.drawString("Who doesn't long for someone to hold", 20, 90);
g2d.drawString("Who knows how to love you without being told", 20, 120);
g2d.drawString("Somebody tell me why I'm on my own", 20, 150);
g2d.drawString("If there's a soulmate for everyone", 20, 180);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Soulmate");
frame.add(new Soulmate());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(420, 250);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
g2d.setFont(new Font("Purisa", Font.PLAIN, 13));We specify font type.
g2d.drawString("Most relationships seem so transitory", 20, 30);The
drawString()
method renders the text using the current text attribute state in the Graphics2D
context. Figure: Soulmate
Unicode
The next example demonstrates, how to display unicode text.$ cat fyodorWe have a file called fyodor where we have the text in azbuka.
Фёдор Михайлович Достоевский родился 30 октября (11 ноября) 1821 года в Москве.
Был вторым из 7 детей. Отец, Михаил Андреевич, работал в госпитале для бедных.
...
$ native2ascii fyodor unicodeWe use the tool called
native2ascii
, which can be found in the bin directory of the jdk. It converts a file with native-encoded characters to one with Unicode-encoded characters. The first parameter is the input file. The second parameter is the output file. $ cat unicodeThe same text in unicode encoding.
\u0424\u0451\u0434\u043e\u0440 \u041c\u0438\u0445\u0430\u0439\u043b\u043e\u0432\u0438\u0447
...
Unicode.java
package com.zetcode;Note, that the text would go outside source code in real world programs. Here the text is kept inside the source for simplicity reasons.
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Unicode extends JPanel {
String sent1 = "\u0424\u0451\u0434\u043e\u0440 \u041c\u0438\u0445" +
"\u0430\u0439\u043b\u043e\u0432\u0438\u0447 \u0414\u043e\u0441\u0442" +
"\u043e\u0435\u0432\u0441\u043a\u0438\u0439 \u0440\u043e\u0434\u0438" +
"\u043b\u0441\u044f 30 \u043e\u043a\u0442\u044f\u0431\u0440\u044f " +
"(11 \u043d\u043e\u044f\u0431\u0440\u044f) 1821 \u0433\u043e\u0434" +
"\u0430 \u0432 \u041c\u043e\u0441\u043a\u0432\u0435. ";
String sent2 = "\u0411\u044b\u043b \u0432\u0442\u043e\u0440\u044b\u043c " +
"\u0438\u0437 7 \u0434\u0435\u0442\u0435\u0439. \u041e\u0442\u0435\u0446, " +
"\u041c\u0438\u0445\u0430\u0438\u043b \u0410\u043d\u0434\u0440\u0435\u0435" +
"\u0432\u0438\u0447, \u0440\u0430\u0431\u043e\u0442\u0430\u043b \u0432 " +
"\u0433\u043e\u0441\u043f\u0438\u0442\u0430\u043b\u0435 \u0434\u043b\u044f " +
"\u0431\u0435\u0434\u043d\u044b\u0445.";
String sent3 = "\u041c\u0430\u0442\u044c, \u041c\u0430\u0440\u0438\u044f " +
"\u0424\u0451\u0434\u043e\u0440\u043e\u0432\u043d\u0430 " +
"(\u0432 \u0434\u0435\u0432\u0438\u0447\u0435\u0441\u0442\u0432\u0435 " +
"\u041d\u0435\u0447\u0430\u0435\u0432\u0430), \u043f\u0440\u043e\u0438\u0441" +
"\u0445\u043e\u0434\u0438\u043b\u0430 \u0438\u0437 \u043a\u0443\u043f\u0435" +
"\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0440\u043e\u0434\u0430.";
String sent4 = "\u041a\u043e\u0433\u0434\u0430 \u0414\u043e\u0441\u0442" +
"\u043e\u0435\u0432\u0441\u043a\u043e\u043c\u0443 \u0431\u044b\u043b\u043e 15 " +
"\u043b\u0435\u0442, \u0435\u0433\u043e \u043c\u0430\u0442\u044c " +
"\u0443\u043c\u0435\u0440\u043b\u0430 \u043e\u0442 \u0447\u0430\u0445\u043e" +
"\u0442\u043a\u0438, \u0438 \u043e\u0442\u0435\u0446 \u043e\u0442\u043f\u0440" +
"\u0430\u0432\u0438\u043b";
String sent5 = "\u0441\u0442\u0430\u0440\u0448\u0438\u0445 \u0441\u044b" +
"\u043d\u043e\u0432\u0435\u0439, \u0424\u0451\u0434\u043e\u0440\u0430 \u0438 " +
"\u041c\u0438\u0445\u0430\u0438\u043b\u0430 (\u0432\u043f\u043e\u0441\u043b" +
"\u0435\u0434\u0441\u0442\u0432\u0438\u0438 \u0442\u0430\u043a\u0436\u0435 " +
"\u0441\u0442\u0430\u0432\u0448\u0435\u0433\u043e \u043f\u0438\u0441\u0430" +
"\u0442\u0435\u043b\u0435\u043c),";
String sent6 = "\u0432 \u043f\u0430\u043d\u0441\u0438\u043e\u043d \u041a. " +
"\u0424. \u041a\u043e\u0441\u0442\u043e\u043c\u0430\u0440\u043e\u0432\u0430 " +
"\u0432 \u041f\u0435\u0442\u0435\u0440\u0431\u0443\u0440\u0433\u0435.";
public void paint(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
RenderingHints rh =
new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(rh);
g2d.setFont(new Font("Franklin Gothic Medium", Font.PLAIN, 11));
g2d.drawString(sent1, 20, 30);
g2d.drawString(sent2, 20, 55);
g2d.drawString(sent3, 20, 80);
g2d.drawString(sent4, 20, 120);
g2d.drawString(sent5, 20, 145);
g2d.drawString(sent6, 20, 170);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Unicode");
frame.add(new Unicode());
frame.setSize(520, 220);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
String sent1 = "\u0424\u0451\u0434\u043e\u0440 \u041c\u0438\u0445" +This is the first unicode line.
...
g2d.drawString(sent1, 20, 30);And the sentence is drawn.
Figure: Unicode
Shadow text
In the next example, we will create a shadowed text. The effect is created by drawing the same text two times. One text serves as the main text, the other one as a shadow. The shadowed text is moved a bit, coloured in light gray and blurred.ShadowText.java
import java.awt.Color;This time, we don't draw in the
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.TextLayout;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.io.IOException;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class ShadowText extends JFrame {
private int width = 490;
private int height = 150;
private String text = "Disciplin ist macht";
private TextLayout textLayout;
public ShadowText() throws IOException {
BufferedImage image = createImage();
add(new JLabel(new ImageIcon(image)));
setTitle("Shadowed Text");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
static void setRenderingHints(Graphics2D g) {
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
}
public BufferedImage createImage() throws IOException {
int x = 10;
int y = 100;
Font font = new Font("Georgia", Font.ITALIC, 50);
BufferedImage image =
new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g1 = image.createGraphics();
setRenderingHints(g1);
textLayout = new TextLayout(text, font, g1.getFontRenderContext());
g1.setPaint(Color.WHITE);
g1.fillRect(0, 0, width, height);
g1.setPaint(new Color(150, 150, 150));
textLayout.draw(g1, x+3, y+3);
g1.dispose();
float[] kernel = {
1f / 9f, 1f / 9f, 1f / 9f, 1f / 9f,
1f / 9f, 1f / 9f, 1f / 9f, 1f / 9f, 1f / 9f
};
ConvolveOp op =
new ConvolveOp(new Kernel(3, 3, kernel), ConvolveOp.EDGE_NO_OP,
null);
BufferedImage image2 = op.filter(image, null);
Graphics2D g2 = image2.createGraphics();
setRenderingHints(g2);
g2.setPaint(Color.BLACK);
textLayout.draw(g2, x, y);
return image2;
}
public static void main(String[] args) throws IOException {
new ShadowText();
}
}
paint()
method. We create a image, that we put inside a JLabel
. Font font = new Font("Georgia", Font.ITALIC, 50);Our font is Georgia, italic, of 50 points size.
BufferedImage image =We create the first buffered image.
new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g1 = image.createGraphics();
Graphics2D
object is created, which is used to draw into the buffered image. textLayout = new TextLayout(text, font, g1.getFontRenderContext());We create a
TextLayout
class. TextLayout is an immutable graphical representation of styled character data. It is used for advanced manipulation with the text and font. textLayout.draw(g1, x+3, y+3);The code renders this
TextLayout
at the specified location in the specified Graphics2D
context. The origin of the layout is placed at x, y. float[] kernel = {This creates the blur effect.
1f / 9f, 1f / 9f, 1f / 9f, 1f / 9f,
1f / 9f, 1f / 9f, 1f / 9f, 1f / 9f, 1f / 9f
};
ConvolveOp op =
new ConvolveOp(new Kernel(3, 3, kernel), ConvolveOp.EDGE_NO_OP,
null);
BufferedImage image2 = op.filter(image, null);We apply the blur effect on the first image and copy the outcome to the second buffered image.
textLayout.draw(g2, x, y);At this point, we have both the original text and the blurred text in the
TaxLayout
object. add(new JLabel(new ImageIcon(image)));We put the image inside a
JLabel
. And the label inside the JFrame
. Figure: Shadow text
Text attributes
When we draw text, we can control various its attributes. We can modify text rendering withFont
class, TextAttributes
, AttributeString
classes. The Font class represents fonts, which are used to render text. The TextAttribute class defines attribute keys and attribute values used for text rendering. Finally, the AttributedString class holds text and related attribute information. TextAttributes.java
import java.awt.Color;In our example, we demonstrate various text rendering possibilities.
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.TextAttribute;
import java.text.AttributedString;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TextAttributes extends JPanel {
String words = "Valour fate kinship darkness";
String java = "Java TM";
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Font font = new Font("Serif", Font.PLAIN, 40);
AttributedString as1 = new AttributedString(words);
as1.addAttribute(TextAttribute.FONT, font);
as1.addAttribute(TextAttribute.FOREGROUND, Color.red, 0, 6);
as1.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, 7, 11);
as1.addAttribute(TextAttribute.BACKGROUND, Color.LIGHT_GRAY, 12, 19);
as1.addAttribute(TextAttribute.STRIKETHROUGH,
TextAttribute.STRIKETHROUGH_ON, 20, 28);
g2d.drawString(as1.getIterator(), 15, 60);
AttributedString as2 = new AttributedString(java);
as2.addAttribute(TextAttribute.SIZE, 40);
as2.addAttribute(TextAttribute.SUPERSCRIPT,
TextAttribute.SUPERSCRIPT_SUPER, 5, 7);
g2d.drawString(as2.getIterator(), 130, 125);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Text attributes");
frame.add(new TextAttributes());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(620, 190);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
AttributedString as1 = new AttributedString(words);We create an
AttributeString
out of the words
string. as1.addAttribute(TextAttribute.FOREGROUND, Color.red, 0, 6);Here we add a new attribute to the
AttributeString
class. T his attribute says, that the first seven characters will be rendered in red color. g2d.drawString(as1.getIterator(), 15, 60);The first text is drawn on the panel. Because at the moment we work with a
AttributeString
class and not directly with the string, we use an overloaded drawString()
method, which takes a AttributedCharacterIterator
instance as its first parameter. Figure: Text Attributes
Rotated Text
In the last example, we will show a rotated text on the panel. To rotate a text, we will do rotation and translation operations. As we already stated, aglyph
is a shape used to render a character. So in our code example, we need to get all glyphs of our text, get their measurements and manipulate them one by one. We will work with several important classes. The
FontRenderContext
class is a container for the information needed to correctly measure text. The GlyphVector
object is a collection of glyphs containing geometric information for the placement of each glyph in a transformed coordinate space. RotatedText.java
import java.awt.Font;Rotated text example.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class RotatedText extends JPanel {
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
String s = "ZetCode, tutorials for programmers";
Font font = new Font("Courier", Font.PLAIN, 12);
g2d.translate(20, 20);
FontRenderContext frc = g2d.getFontRenderContext();
GlyphVector gv = font.createGlyphVector(frc, s);
int length = gv.getNumGlyphs();
for (int i = 0; i < length; i++) {
Point2D p = gv.getGlyphPosition(i);
double theta = (double) i / (double) (length - 1) * Math.PI / 3;
AffineTransform at = AffineTransform.getTranslateInstance(p.getX(),
p.getY());
at.rotate(theta);
Shape glyph = gv.getGlyphOutline(i);
Shape transformedGlyph = at.createTransformedShape(glyph);
g2d.fill(transformedGlyph);
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Rotated text");
frame.add(new RotatedText());
frame.setSize(400, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
String s = "ZetCode, tutorials for programmers";This is our text. Because the text is in Latin1 encoding, glyphs correspond to characters in a one-to-one manner.
GlyphVector gv = font.createGlyphVector(frc, s);Here we create a
GlyphVector
object. A GlyphVector is a collection of glyphs and their positions. int length = gv.getNumGlyphs();Here we get the number of glyphs of our text. If we print the number to the console, we get 34. So each character is a glyph, for our text.
Point2D p = gv.getGlyphPosition(i);We iterate through the vector of glyphs. For each glyph we calculate its position.
double theta = (double) i / (double) (length - 1) * Math.PI / 3;We calculate the degree, by which the glyph is going to be rotated.
AffineTransform at = AffineTransform.getTranslateInstance(p.getX(),We do an affine rotate transformation. Or better said, we create an affine transformation object.
p.getY());
at.rotate(theta);
Shape glyph = gv.getGlyphOutline(i);The
Shape transformedGlyph = at.createTransformedShape(glyph);
getGlyphOutline()
method returns a Shape
of the specified glyph. The createTransformedShape()
method returns a new Shape object modified by our affine transform operation. g2d.fill(transformedGlyph);Finally, we paint the glyph.
In this part of the Java 2D tutorial, we covered Text and Fonts.
0 comments:
Post a Comment