一、笔者在开发过程中遇到生成分享海报的需求
需要后端动态生成分享图(最终前端自己实现的,哈哈);记录下过程中遇到的一些问题和解决办法。
二、Graphics2D常用API
首先获取Graphics2D实例
BufferedImage bi = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB); Graphics2D graphics = bi.createGraphics(); // 开启抗锯齿 RenderingHints renderingHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // 使用高质量压缩 renderingHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); graphics.setRenderingHints(renderingHints);
1、graphics.drawImage(),往画布添加图片,参数有位置及图片宽高
BufferedImage bgmImage = ImageIO.read(new ByteArrayInputStream(FileUtils.readFileToByteArray(new File("filePath")))); graphics.drawImage(bgmImage, x, y, width, height, null);
2、graphics.drawString(),往画布上添加文字(自动换行需自己实现)
graphics.setFont(new Font("PingFangSC-Regular", Font.PLAIN, 24)); graphics.setColor(Color.WHITE); graphics.drawString(title, x, y);
3、graphics.fillRoundRect(),带背景色的圆角矩形(用于给文字画背景色,注意要先画矩形背景,再画文字上去),最后两个参数是设置圆角弧度
// 背景色矩形 graphics.setColor(new Color(254, 68, 82)); graphics.fillRoundRect(x, y, width, height, 4, 4);
三、上才艺
1、刚刚提到的画文字自动换行需要自己实现,这里简单说下实现原理;其实就是根据设置的行宽及要画进去的字符串做一个计算,每个文字的行宽是可以拿到的,这样就能算出每行能展示多少个字,然后就能算出总共分多少行展示,最后循环调用graphics.drawString()方法画上去就好了;下面是换行的核心代码:
private static int drawStringAutoLineFeed(Graphics g, String strContent, int rowWidth, int x, int y) { String[] split = strContent.split("\n"); int total = 0; for (String str : split) { int height = drawStringAutoLine(g, str, rowWidth, x, y); total += height; y += height; } return total; } /** * 根据指定宽度自动换行 * * @param g * @param rowWidth * @param strContent * @param x * @param y */ private static int drawStringAutoLine(Graphics g, String strContent, int rowWidth, int x, int y) { // 获取字符串 字符的总宽度 int strWidth = getStringLength(g, strContent); // 获取字符高度 int strHeight = getStringHeight(g); // 字符串总个数 int rows = 0; if (strWidth > rowWidth) { int rowStrNum = getRowStrNum(strContent.length(), rowWidth, strWidth); rows = getRows(strWidth, rowWidth); String temp = ""; for (int i = 0; i < rows; i++) { // 获取各行的String if (i == rows - 1) { // 最后一行 temp = strContent.substring(i * rowStrNum, strContent.length()); } else { temp = strContent.substring(i * rowStrNum, i * rowStrNum + rowStrNum); } if (i > 0) { // 第一行不需要增加字符高度,以后的每一行在换行的时候都需要增加字符高度 y = y + strHeight; } g.drawString(temp, x, y); } } else { // 直接绘制 g.drawString(strContent, x, y); } return strHeight * rows; } private static int getDrawStringAutoLineHeight(Graphics g, String strContent, int rowWidth) { String[] split = strContent.split("\n"); int height = 0; for (String str : split) { // 获取字符串 字符的总宽度 int strWidth = getStringLength(g, str); // 获取字符高度 height += getStringHeight(g) * getRows(strWidth, rowWidth); } return height; } private static int getStringLength(Graphics g, String str) { char[] strChar = str.toCharArray(); return g.getFontMetrics().charsWidth(strChar, 0, str.length()); } // 每一行字符的个数 private static int getRowStrNum(int strNum, int rowWidth, int strWidth) { int rowsNum = 0; rowsNum = (rowWidth * strNum) / strWidth; return rowsNum; } // 字符行数 private static int getRows(int strWidth, int rowWidth) { int rows = 0; if (strWidth % rowWidth > 0) { rows = strWidth / rowWidth + 1; } else { rows = strWidth / rowWidth; } return rows; } // 字符高度 private static int getStringHeight(Graphics g) { return g.getFontMetrics().getHeight(); }
使用方法:
graphics.setFont(new Font("PingFangSC-Regular", Font.PLAIN, 12)); graphics.setColor(Color.GRAY); drawStringAutoLineFeed(graphics, strContent, rowWidth, x, y);
四、输出图片
1、输出图片字节数组
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ImageIO.write(bi, "jpg", outputStream); return outputStream.toByteArray();
2、直接输出图片文件
ImageIO.write(bi, "jpg", new File("outFilePath"));
五、总结
1、简单的并且图片是固定尺寸和样式的场景还比较好使
2、调试过程非常的恶心,不断生成图片看效果
3、样式较为负责的推荐让前端html2image,笔者的前端同事已经实现,效果比后端画图好很多!
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。