2D文本指南
第二课: 绘制艺术字
作者: Monica Pawlan
September 1998
翻译: Cherami Liumin
2001年11月
[<<上一课] [目录] [下一课>>]
这一课解释如何使用 java.awt.font.TextLayout 类绘制简单和复杂的艺术字以创建有趣和令人愉快的视觉效果。
绘制一行文本
绘制多行文本
文本倾斜
用图像填充剪切区域
用线条填充剪切区域
用字符填充剪切区域
文本属性和替换图形
练习
绘制一行文本java.awt.font.TextLayout类可以让你使用字符,字体和属性集创建艺术字。一旦被创建,TextLayout对象就不可编辑,但是它的方法可以让你访问布局、字体、脱字符、选择和点击测试信息。
下面的代码使用Font,TextLayout和 FontRenderContext 对象绘制一个简单的文本,使用24点Times黑体。TimesB.java 是完整的源代码。
FontRenderContext frc = g2.getFontRenderContext();Font f = new Font("Helvetica",Font.BOLD, 24);
String s = new String("24 Pont Helvetica Bold");
TextLayout tl = new TextLayout(s, f, frc);
Dimension theSize=getSize();
g2.setColor(Color.green);
tl.draw(g2, theSize.width/30, theSize.height/2);
Java.awt.Font 代表系统中可用字体的一个实例。
java.awt.TextLayout 代表不可变的艺术字数据。
java.awt.font.FontRenderContext 包含绘制文本时需要的正确测量和定位它的信息。
绘制多行文本你可以用TextLayout和java.awt.LineBreakMeasurer类绘制一段艺术字。下一个例子使用 LineBreakMeasurer 对象创建并绘制数行文本,文本的布局符合组件的宽度。TextLayout的 getAscent和 getDescent方法返回的字体信息被用于定位组件中的行数。文本被作为一个AttributedCharacterIterator存储,因此字体和大小属性可以和文本一起被储存。
LineBreakSample.java是完整的源代码。SampleUtils.java类包含LineBreakSample程序要显示的文本。
注意: LineBreakSample程序支持外语文本。参看第四课: 外语支持获取更多信息。
public class LineBreakSample extends Component {
private LineBreakMeasurer lineMeasurer;
private int paragraphStart;
private int paragraphEnd;
public LineBreakSample
(AttributedCharacterIterator paragraph) {
FontRenderContext frc =
SampleUtils.getDefaultFontRenderContext();
paragraphStart = paragraph.getBeginIndex();
paragraphEnd = paragraph.getEndIndex();
lineMeasurer =
new LineBreakMeasurer(paragraph, frc);
}
public void paint(Graphics g) {
Graphics2D graphics2D = (Graphics2D) g;
Dimension size = getSize();
float formatWidth = (float) size.width;
float drawPosY = 0;
lineMeasurer.setPosition(paragraphStart);
while (lineMeasurer.getPosition() < paragraphEnd) {
TextLayout layout =
lineMeasurer.nextLayout(formatWidth);
drawPosY += layout.getAscent();
float drawPosX;
if (layout.isLeftToRight()) {
drawPosX = 0;
}
else {
drawPosX = formatWidth - layout.getAdvance();
}
layout.draw(graphics2D, drawPosX, drawPosY);
drawPosY += layout.getDescent() +
layout.getLeading();
}
}
倾斜文本你可以从一个提供如何旋转,缩放,移动或者剪切字体的信息的变换对象上创建一个Font对象。下一个例子使用一个剪切变换绘制一个倾斜文本.首先,字符串没有任何变换,然后它被绘制在一个不同的地方。 StillLife.java 是完整的源代码。
int w = getSize().width;
int h = getSize().height;
//创建变换
AffineTransform at = new AffineTransform();
at.setToTranslation(30, 50);
AffineTransform fontAT = new AffineTransform();
fontAT.shear(0.2, 0.0);
//创建字体和文本图层
FontRenderContext frc = g2.getFontRenderContext();
Font theFont = new Font("Times", Font.BOLD, w/25);
String s = new String("Still Life with Text");
TextLayout tstring = new TextLayout(s, theFont, frc);
Font theDerivedFont = theFont.deriveFont(fontAT);
String str = new String("Still Life with Slanted Text");
TextLayout tstring2 = new TextLayout(str,
theDerivedFont, frc);
//绘制普通字符串
g2.setColor(Color.blue);
g2.transform(at);
tstring.draw(g2, (float)0, (float)0);
//绘制倾斜字体字符串
g2.setColor(Color.green);
g2.transform(at);
tstring2.draw(g2, (float)0, (float)0);
用图像填充剪切区域下一个例子从文本串The Starry Night创建一个剪切区域,然后使用Vincent van Gogh的绘画 The Starry Night填充那个剪切区域。Starry.java是完整的源代码。
这个范例代码用Times黑体字符串The Starry Night创建一个文本图层。然后得到TextLayout范围的宽度。范围 包含图层可以绘制的所有象素。然后它得到文本图层的轮廓并用范围的宽度计算原始图层的X和Y位置。轮廓用于创建一个Shape对象,而Rectangle对象是从Shape对象的范围上创建的。
这时,图形环境的前景颜色被设置为蓝色并且Shape对象被绘制到图形环境。下面的图像和代码片断展示了所发生的事情。
FontRenderContext frc = g2.getFontRenderContext();Font f = new Font("Times",Font.BOLD,w/10);
String s = new String("The Starry Night");
TextLayout tl = new TextLayout(s, f, frc);
float sw = (float) tl.getBounds().getWidth();
AffineTransform transform = new AffineTransform();
transform.setToTranslation(w/2-sw/2, h/4);
Shape shape = tl.getOutline(transform);
Rectangle r = shape.getBounds();
g2.setColor(Color.blue);
g2.draw(shape);
最后,使用Shape对象在图形环境下建立剪切区域,然后starry.gif 被绘制到该剪切区域,起点在Rectangle对象的左上角(r.x 和 r.y)并且填充矩形对象的整个范围。
g2.setClip(shape);g2.drawImage(img, r.x, r.y, r.width, r.height, this);
用线条填充剪切区域为了得到一个稍微不同的效果,下面的例子从文本图层By创建一个剪切区域,该区域用蓝色填充,然后在蓝色剪切区域绘制黄色横线。TextLayout的 getAscent 方法返回的信息用于定位显示的线条。
Clipping.java 是完整的源代码。
FontRenderContext frc = g2.getFontRenderContext();Font f = new Font("Helvetica",Font.BOLD,w/8);
String s = new String("By");
TextLayout tl = new TextLayout(s, f, frc);
float sw = (float) tl.getBounds().getWidth();
AffineTransform transform = new AffineTransform();
transform.setToTranslation(w/2-sw/2,h/2);
Shape shape = tl.getOutline(transform);
g2.setClip(shape);
g2.setColor(Color.blue);
g2.fill(shape.getBounds());
g2.setColor(Color.yellow);
for (int j = shape.getBounds().y;
j < shape.getBounds().y +
shape.getBounds ().height; j=j+3) {
Line2D line = new Line2D.Float( 0.0f, (float) j,
(float) w, (float) j);
g2.draw(line);
}
用字符填充剪切区域下一个例子从文本图层Vincent van Gogh上创建一个剪切区域,使用蓝色填充,用白色的星号(*)(译注:源代码中实际使用的是加号+)填充该区域,得到蓝天上的白色星星的效果。 TextClipping.java 是完整的源代码。
FontRenderContext frc = g2.getFontRenderContext();Font f =
new Font("Times New Roman Bold",Font.PLAIN,w/8);
String s = new String("Vincent van Gogh");
TextLayout tl = new TextLayout(s, f, frc);
float sw = (float) tl.getBounds().getWidth();
AffineTransform transform = new AffineTransform();
transform.setToTranslation(w/2-sw/2,h-h/6);
Shape shape = tl.getOutline(transform);
g2.setClip(shape);
g2.setColor(Color.blue);
g2.fill(shape.getBounds());
g2.setColor(Color.white);
f = new Font("Helvetica",Font.BOLD,10);
tl = new TextLayout("+", f, frc);
sw = (float) tl.getBounds().getWidth();
Rectangle r = shape.getBounds();
int x = r.x;
int y = r.y;
while ( y < (r.y + r.height+(int) tl.getAscent()) ) {
tl.draw(g2, x, y);
if ((x += (int) sw) > (r.x+r.width)) {
x = r.x;
y += (int) tl.getAscent();
}
}
文本属性和替换图形TextAttribute类定义了文本的属性,因此你可以使用文本属性定义一个Attributed(属性化的)字符串。下一个例子使用文本属性为包含文本The Starry Night的java.awt.text.AttributedString定义字体和特性替换图像。一个从图像上创建的ImageGraphicAttribute 对象完成这个工作。 MakeImage.java 是完整的源代码。
public void paint(Graphics g) {Graphics2D g2;
g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
Font f = new Font("Times",Font.BOLD, 24);
AttributedString ats =
new AttributedString("The Starry Night");
ImageGraphicAttribute iga =
new ImageGraphicAttribute(img,
(int) BOTTOM_ALIGNMENT);
ats.addAttribute(
TextAttribute.CHAR_REPLACEMENT, iga, 4, 5);
ats.addAttribute(
TextAttribute.CHAR_REPLACEMENT, iga, 11, 12);
ats.addAttribute(TextAttribute.FONT, f, 0, 18);
AttributedCharacterIterator iter = ats.getIterator();
FontRenderContext frc = g2.getFontRenderContext();
tl = new TextLayout(iter, frc);
g2.setColor(Color.red);
tl.draw(g2, (float)20, (float)30);
}