通过JAVA可以很容易绘制出各种各样的图形,如矩形,方型,椭圆以及正圆等,直线和文本当然更不再话下。主要是通过BufferedImage和Graphics2D实现。做了一个demo演示,效果如下:
下面来描述实现过程。
库文件主要包括两个类文本:FontProperty和PrintGraphics。其中FontProperty用于定义字体的各个属性,如粗体、斜体、下划线等等;PrintGraphics主要定义各种图形的绘制功能。
先看FontProperty.java:
public class FontProperty{
boolean bBold; //是否粗体
boolean bItalic; //是否斜体
boolean bUnderLine; //是否下划线
boolean bStrikeout; //是否删除线
int iSize; //字体大小
String sFace; //字体类型,默认留空("")即可,表示使用系统默认字体,当然也可以指定如"宋体"
//设置字体参数
public void setF(boolean bBold, boolean bItalic, boolean bUnderLine, boolean bStrikeout, int iSize, String sFace){
this.bBold = bBold;
this.bItalic = bItalic;
this.bUnderLine = bUnderLine;
this.bStrikeout = bStrikeout;
this.iSize = iSize;
this.sFace = sFace;
}
}
该文件很简单,就只有一个设置字体属性的方法。
接着看PrintGraphics.java文件:
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import java.io.File;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.util.Hashtable;
import java.awt.font.TextAttribute;
import javax.swing.JFrame;
public class PrintGraphics{
public BufferedImage bi = null;
public Graphics2D g2 = null;
public double length = 0.0;
public int width;
/**
* 获取图像的实际长度
* 因为画布定义的长度是其宽度的10倍,所以最后会根据使用的实际长度进行截取
*/
public int getLength(){
return (int)this.length+20;
}
/**
* 初始化画布BufferedImage和画笔Graphics2D,其中宽度为参数,长度为宽度的10倍
* 画布背景为白色,画笔为黑色
*/
public void initBuff(int width){
int height = 10*width;
this.bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
this.g2 = (Graphics2D)this.bi.getGraphics();
this.g2.setBackground(Color.WHITE);
this.g2.clearRect(0, 0, width, height);
this.g2.setPaint(Color.BLACK);
this.width = width;
}
// 根据初始的字体属性值对字体进行设置
public Font setFontProperty(FontProperty fp){
int fontStyle = 0;
int fontFlag = 0;
if(fp.sFace != ""){
String fontNames[];
//获取系统所有字体列表,然后判断用户指定的字体是否存在
fontNames = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
for(int i=0;i<fontNames.length;i++){
if(fontNames[i].equals(fp.sFace)){
fontFlag = 1;
break;
}
}
//如果不存在则留空,表示使用系统默认字体
if(fontFlag == 0){
System.out.println("Your font face \"" + fp.sFace + "\" is not exist, default is used.");
fp.sFace = "";
}
}
Font font = null;
//设置粗体
if(fp.bBold){
fontStyle = 1;
font = new Font(fp.sFace, Font.BOLD, fp.iSize);
}
//设置斜体
if(fp.bItalic){
if(fontStyle == 0){
fontStyle = 1;
font = new Font(fp.sFace, Font.ITALIC, fp.iSize);
}
else{
font = new Font(fp.sFace, Font.BOLD|Font.ITALIC, fp.iSize);
}
}
//设置常规体
if(fontStyle == 0){
font = new Font(fp.sFace, Font.PLAIN, fp.iSize);
}
//设置下划线和删除线
if(fp.bUnderLine || fp.bStrikeout){
Hashtable<TextAttribute, Object> map = new Hashtable<TextAttribute, Object>();
if(fp.bUnderLine){
map.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
}
if(fp.bStrikeout){
map.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
}
font = font.deriveFont(map);
}
return font;
}
//设置线宽
public Stroke setLineWidth(double width){
String f = "" + width + "f";
Stroke stk = new BasicStroke(Float.parseFloat(f));
return stk;
}
//使用指定的字体绘制文本,其中(x,y)是指文本起始位置的左下点坐标,所以y不可为0
public void drawText(double x, double y, String nStr, FontProperty fp){
Font font = this.setFontProperty(fp);
this.g2.setFont(font);
this.g2.drawString(nStr, (int)x, (int)y);
if(this.length < y){
this.length = y;
}
}
//使用指定的宽度绘制直线,其中(x1,y1)为起始点坐标,(x2,y2)为终点坐标
public void drawLine(double x1, double y1, double x2, double y2, double w){
Stroke stk = this.setLineWidth(w);
this.g2.setStroke(stk);
this.g2.draw(new Line2D.Double(x1, y1, x2, y2));
//获取画布实际使用长度,下同
double max = 0;
max = y1>y2?y1:y2;
if(this.length < max){
this.length = max;
}
}
//使用指定的宽度绘制矩形或方型,其中(x1,y1)为左上角坐标,width和height分别指定矩形或方型的宽度及高度
public void drawRectangle(double x, double y, double width, double height, double w){
Stroke stk = this.setLineWidth(w);
this.g2.setStroke(stk);
this.g2.draw(new Rectangle2D.Double(x, y, width, height));
if(this.length < y+height){
this.length = y+height;
}
}
//使用指定的宽度绘制椭圆或正圆,其中(x1,y1)为圆的外切矩形或方型的左上角坐标,(x2,y2)为圆的外切矩形或方型的右下角坐标
public void drawEllips(double x1, double y1, double x2, double y2, double w){
Stroke stk = this.setLineWidth(w);
this.g2.setStroke(stk);
Rectangle2D rect=new Rectangle2D.Double();
rect.setFrameFromDiagonal(x1, y1, x2, y2);
Ellipse2D ellipse=new Ellipse2D.Double();
ellipse.setFrame(rect);
this.g2.draw(ellipse);
double max = 0;
max = y1>y2?y1:y2;
if(this.length < max){
this.length = max;
}
}
//插入指定路径的图片,其中(x,y)是指图片起始位置的左上点坐标
public void drawImage(double x, double y, String pathname){
try{
BufferedImage image=ImageIO.read(new File(pathname));
this.g2.drawImage(image, (int)x, (int)y, null);
if(this.length < y+image.getHeight()){
this.length = y+image.getHeight();
}
}catch(java.io.IOException ioe){ioe.printStackTrace();}
}
//将图像通过frame窗口显示出来
public void printShow(){
JFrame jframe = new JFrame();
jframe.setTitle("Preview Image");
//设置frame是,长度和宽度都适当的增加了一点,这样最终的图片周边会留点白边,比较好看
jframe.setSize(this.width+20, this.getLength()+40);
//为frame窗口添加关闭属性
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//根据实际图像长度截取图像,(0,0)表示图像左上点坐标,后两个参数表示右下角坐标
BufferedImage img = this.bi.getSubimage(0,0,this.width,this.getLength());
ImageIcon ii = new ImageIcon(img);
JLabel lb = new JLabel(ii);
jframe.add(lb);
//显示frame窗口
jframe.setVisible(true);
//销毁g2对象
this.g2.dispose();
}
}
程序里面都有详细的注释信息,所以不难看懂的。
最后写一个测试demo.java程序:
import java.io.File;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
class Demo{
public static void main(String[] args){
//获取画图实例
PrintGraphics pg = new PrintGraphics();
//初始化画布,宽度为576px,则长度为5760px
pg.initBuff(576);
//插入一张图片,根据自己的图片位置进行更改即可
pg.drawImage(30, 0, "/home/zwang/tmp/logo.gif");
//获取字体实例
FontProperty fp = new FontProperty();
//初始化字体,黑体,24号
fp.setF(true, false, false, false, 24, "");
//绘制文本
pg.drawText(215, 45, "北京威控睿博科技有限公司", fp);
//绘制直线
pg.drawLine(0, 70, 576, 70, 5.0);
//绘制各种格式的文本
fp.setF(false, false, false, false, 18, "");
pg.drawText(150, 95, "这是一行普通文体", fp);
fp.setF(true, false, false, false, 18, "");
pg.drawText(150, 120, "这是一行粗体文体", fp);
fp.setF(false, true, false, false, 18, "");
pg.drawText(150, 145, "这是一行斜体文体", fp);
fp.setF(false, false, true, false, 18, "");
pg.drawText(150, 170, "这是一行下划线文体", fp);
fp.setF(false, false, false, true, 18, "");
pg.drawText(150, 195, "这是一行删除线文体", fp);
fp.setF(true, true, true, true, 18, "");
pg.drawText(150, 220, "这是一行混合格式文体", fp);
pg.drawLine(0, 230, 576, 230, 2.0);
//绘制矩形及说明文本
fp.setF(false, false, false, false, 15, "");
pg.drawRectangle(10, 250, 360, 180, 2.0);
pg.drawText(135, 340, "线宽2.0的矩形", fp);
//绘制方形及说明文本
pg.drawRectangle(380, 250, 180, 180, 4.0);
pg.drawText(420, 340, "线宽4.0的方形", fp);
//绘制椭圆及说明文本
pg.drawEllips(10, 450, 370, 630, 3.0);
pg.drawText(135, 540, "线宽3.0的椭圆", fp);
//绘制正圆及说明文本
pg.drawEllips(380, 450, 560, 630, 6.0);
pg.drawText(420, 540, "线宽6.0的正圆", fp);
pg.drawLine(0, 650, 576, 650, 4.0);
//显示图像窗口
pg.printShow();
//将图像保存到指定位置的jpg格式文件,当然也可以为png及其它格式
BufferedImage img=pg.bi.getSubimage(0,0,576,pg.getLength());
//pg.g2.dispose(); //free g2
//可根据实际系统更改这个路径
File file = new File("/tmp/demo.jpg");
try{
ImageIO.write(img, "jpg", file);
}catch(java.io.IOException ioe){ioe.printStackTrace();}
}
}
Demo文件的注释也很详细的,都是调用前面两个库文件定义的功能函数,最后会将图像通过窗口显示出来,同时会保存到/tmp/demo.jpg。显示效果见文档开始。
可以将三个java文件放到同一目录下,然后编译并运行:
$ javac demo.java
$ java Demo
注意,根据你的实际情况,demo.java中的两处路径可能需要修改一下。
说明:该方案仅适用于PC环境,如果是Android系统,需要其它方法实现,后面会讨论Android下如何实现。