05、利用 Graphics2D 将网络图片绘制成海报封面
Graphics2D 类扩展了 Graphics 类,提供了对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制,是用于呈现二维形状、文本和图像的基础类。
BufferedImage 使用可访问的图像数据缓冲区描述图像,由颜色模型和图像数据栅格组成,所有 BufferedImage 对象的左上角坐标为(0,0)。
可以利用 BufferedImage 类的 createGraphics() 方法获取 Graphics2D 对象。
第一步,将海报背景和海报封面读入到 BufferedImage 对象中。注意,deleteOnExit() 方法请求在虚拟机终止时删除此抽象路径名所表示的文件或目录。
// 背景 File bgFile = FileUtil.read("bg_", ".jpg", "default_bgimg.jpg"); bgFile.deleteOnExit(); BufferedImage bgImage = ImageIO.read(bgFile); // 封面图 File picFile = CapturePic.capture(); picFile.deleteOnExit(); BufferedImage picImage = ImageIO.read(picFile);
第二步,计算封面图的起始坐标,以及高度和宽度。
// 封面图的起始坐标
int pic_x = MARGIN, pic_y = MARGIN;
// 封面图的宽度
int pic_width = bgImage.getWidth() - MARGIN * 2;
// 封面图的高度
int pic_height = picImage.getHeight() * pic_width / picImage.getWidth();
第三步,在海报背景上绘制封面图。
Graphics2D graphics2d = bgImage.createGraphics();
// 在背景上绘制封面图
graphics2d.drawImage(picImage, pic_x, pic_y, pic_width, pic_height, null);
// 释放图形上下文,以及它正在使用的任何系统资源。
graphics2d.dispose();
第四步,将绘制好的图像输出到文件中。
File posterFile = Files.createTempFile(FileUtil.DIRECTORY, "poster_", ".jpg").toFile();
ImageIO.write(bgImage, "jpg", posterFile);
在指定的临时目录下可以查看海报,如下所示。
06、利用 Graphics2D 在海报上打印中文
Font 类表示字体,用于以可见的方式呈现文本。字体提供了将字符序列映射到象形文字序列以及在图形和组件对象上呈现象形文字序列所需的信息。
第一步,通过 GraphicsEnvironment 类的 getAvailableFontFamilyNames() 查看计算机上允许使用的字体。
String[] fontNames = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
for (String fontName : fontNames) {
System.out.println(fontName);
}
大致的中文字体有这么一些(还有更多,未列出):
宋体
幼圆
微软雅黑
微软雅黑 Light
新宋体
方正姚体
方正舒体
楷体
隶书
黑体
第二步,设置字体和颜色。
// Font 的构造参数依次是字体名字,字体式样,字体大小
Font font = new Font("微软雅黑", Font.PLAIN, 28);
g.setFont(font);
// RGB
g.setColor(new Color(71, 71, 71));
第三步,根据当前字体下每个中文字符的宽度,以及海报可容纳的最大文本宽度,对文本进行换行。
计算每个字体的宽度时,需要用到 sun.font.FontDesignMetrics,它扩展了 java.awt.FontMetrics。FentMetrics 类定义了一个字体度量对象,该对象封装了有关在特定屏幕上呈现特定字体的信息。FontDesignMetrics 提供了更多指标的 Font 信息。
FontDesignMetrics 有几个重要的值需要说明一下:
基准点是 baseline
ascent 是 baseline 之上至字符最高处的距离
descent 是 baseline 之下至字符最低处的距离
leading 文档说的很含糊,其实是上一行字符的 descent 到下一行的 ascent 之间的距离
top 指的是指的是最高字符到 baseline 的值,即 ascent 的最大值
bottom 指的是最下字符到 baseline 的值,即 descent 的最大值
FontDesignMetrics 的 charWidth() 方法可以计算字符的宽度。
public static String makeLineFeed(String zh, FontDesignMetrics metrics, int max_width) { StringBuilder sb = new StringBuilder(); int line_width = 0; for (int i = 0; i < zh.length(); i++) { char c = zh.charAt(i); sb.append(c); // 如果主动换行则跳过 if (sb.toString().endsWith("\n")) { line_width = 0; continue; } // FontDesignMetrics 的 charWidth() 方法可以计算字符的宽度 int char_width = metrics.charWidth(c); line_width += char_width; // 如果当前字符的宽度加上之前字符串的已有宽度超出了海报的最大宽度,则换行 if (line_width >= max_width - char_width) { line_width = 0; sb.append("\n"); } } return sb.toString(); }
假如文本是“沉默王二,《Web 全栈开发进阶之路》作者;一个不止写代码的程序员,还写有趣有益的文字,给不喜欢严肃的你。”我们来通过 makeLineFeed() 方法试验一下。
Font font = new Font("微软雅黑", Font.PLAIN, 28); FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font); String zh = "沉默王二,《Web 全栈开发进阶之路》作者;一个不止写代码的程序员,还写有趣有益的文字,给不喜欢严肃的你。"; String[] rows = makeLineFeed(zh, metrics, 600).split("\n"); for (int i = 0; i < rows.length; i++) { System.out.println(rows[i]); }
其结果如下所示。
沉默王二,《Web 全栈开发进阶之路》作者;
一个不止写代码的程序员,还写有趣有益的文字
,给不喜欢严肃的你。