背景
我目前参与小程序的产品研发,为了方便产品的转发和推广,会对课提供生成海报转发或者分享的功能,前期海报合成这个功能是由项目组的老同事负责开发,后来小程序海报这块功能需要单独做一个功能用于专门根据不同的课程类型来生成海报的功能,很荣幸这个任务交由我开发。编码过程这边就略过了,直接看代码如下所示:
EmojiUtils工具类
package com.xyq.maventest.util;import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/****
* Project Name:maventest
* <p>emoji标签转换工具类<br>
*
* @ClassName: EmojiUtils
* @date 2019年1月22日 下午4:29:26
*
* @author youqiang.xiong
* @version 1.0
* @since
* @see*/
public class EmojiUtils {private static Logger logger = LoggerFactory.getLogger(EmojiUtils.class);/*** 将emoji标签转换成utf8字符集保存进数据库* @param str* @return*/public static String emojiConvert(String str) {String patternString = "([\\x{10000}-\\x{10ffff}\\ud800-\\udfff])";Pattern pattern = Pattern.compile(patternString);Matcher matcher = pattern.matcher(str);StringBuffer sb = new StringBuffer();while(matcher.find()) {// matcher.appendReplacement(sb,"?");try {matcher.appendReplacement(sb,"[[" + URLEncoder.encode(matcher.group(1),"UTF-8") + "]]");} catch(UnsupportedEncodingException e) {logger.error("emojiConvert error"+ e.getMessage());return str;}}matcher.appendTail(sb);logger.debug("emojiConvert " + str + " to " + sb.toString() + ", len:" + sb.length());return sb.toString();}/*** 还原utf8数据库中保存的含转换后emoji表情的字符串* @param str* @return*/public static String emojiRecovery(String str) {if(StringUtils.isEmpty(str)){return "";}try{String patternString = "\\[\\[(.*?)\\]\\]";Pattern pattern = Pattern.compile(patternString);Matcher matcher = pattern.matcher(str);StringBuffer sb = new StringBuffer();while(matcher.find()) {try {matcher.appendReplacement(sb, URLDecoder.decode(matcher.group(1), "UTF-8"));} catch(UnsupportedEncodingException e) {logger.error("emojiRecovery error"+ e.getMessage());return "";}}matcher.appendTail(sb);logger.debug("emojiRecovery " + str + " to " + sb.toString());return sb.toString();}catch (Exception e){logger.error("emojiRecovery error",e);}return str;}}
ImageUtils 工具类
核心代码如下:
/****
* Project Name:wechat-management-util
* <p>各种海报合成图片工具类<br>
*
* @ClassName: ImageUtils
* @date 2019年1月22日 下午3:52:43
*
* @author youqiang.xiong
* @version 1.0
* @since
* @see*/
public class ImageUtils {private static final Logger logger = LoggerFactory.getLogger(ImageUtils.class);/***** * Project Name: wechat-management-util* <p>公开课小程序后台生成分享海报 <br> ** @author youqiang.xiong* @date 2019年1月11日 下午4:28:21* @version v1.0* @since * @param background * 背景图* @param poster* 教师形象图* @param qr* 二维码图片* @param avatar* 头像图片* @param font* 字体* @param nickname* 昵称* @param courseName* 课程名称* @param courseTip* 课程大纲* @return * @throws IOException*/public static BufferedImage createSharePoster(BufferedImage background, BufferedImage poster,BufferedImage qr,BufferedImage avatar,Font font, String nickname,String courseName,String courseTip ) throws IOException{// 开启抗锯齿RenderingHints renderingHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);// 使用高质量压缩renderingHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);renderingHints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);int height = background.getHeight() * 750 / background.getWidth();// 图片大小BufferedImage img = new BufferedImage(750, height, BufferedImage.TYPE_INT_RGB);// 开启画图Graphics2D g = (Graphics2D) img.getGraphics();try {g.setRenderingHints(renderingHints);// 教师海报图 - 需要将原图进行剪切 剪切后的长度是 750*830BufferedImage teacherImage = cutPic(poster, 158, 0, 750, 830);g.drawImage(teacherImage.getScaledInstance(750,teacherImage.getHeight(), Image.SCALE_FAST), 0, 0, null);// 画底图g.drawImage(background.getScaledInstance(750, height, Image.SCALE_FAST), 0, 0, null);// 画头像if (null != avatar) {g.drawImage(ImageUtils.roundImage(avatar, avatar.getWidth(), avatar.getHeight(), 360).getScaledInstance(45*2, 45*2, Image.SCALE_FAST), 25*2, 603*2, null);}// 画二维码if (null != qr) {g.drawImage(qr.getScaledInstance(70*2, 70*2, Image.SCALE_FAST), 286*2, 591*2, null);}// 设置字体大小font = font.deriveFont(34f);// 设置字体颜色Color color = new Color(55,69,97);g.setColor(color);g.setFont(font);//昵称 nickname = nickname == null ? "" : nickname;nickname = EmojiUtils.emojiRecovery(nickname);if(nickname.length() > 5) {nickname = nickname.substring(0, 5) + "...";}int stringWidth = getWidth(g.getFontRenderContext(), g, nickname);g.drawString(nickname, 84 * 2 , 623 * 2);//画'向您推荐好课' String recommendText = "向您推荐好课";font = font.deriveFont(24f);// 设置字体颜色color = new Color(55,69,97);g.setColor(color);g.setFont(font);g.drawString(recommendText, 84 * 2 + stringWidth , 623 * 2);// 写课程名称font = font.deriveFont(60.0f);color = new Color(69,75,106);g.setColor(color);g.setFont(font);int courseWidth = getWidth(g.getFontRenderContext(), g, courseName);if(courseWidth >= 334*2) {String c1 = courseName.substring(0, 10);String c2 = courseName.substring(10, courseName.length());int courseHeight = getHeight(g.getFontRenderContext(), g, c1);g.drawString(c1, 21 * 2 , (430 * 2) - 2);g.drawString(c2, 21 * 2 , 420 * 2 + courseHeight + 2);}else {g.drawString(courseName, 21 * 2 , 448 * 2);}//写课程大纲List<String> tipList = Arrays.asList(courseTip.split("\n"));String tip1 = tipList.get(0);if(tip1.length() >= 20) {tip1 = tip1.substring(0, 19) + "...";}String tip2 = tipList.get(1);if(tip2.length() >= 20) {tip2 = tip2.substring(0, 19) + "...";}String tip3 = tipList.get(2);if(tip3.length() >= 20) {tip3 = tip3.substring(0, 19) + "...";}font = font.deriveFont(30.0f);color = new Color(176,153,104);g.setColor(color);g.setFont(font);g.drawString(tip1, 47 * 2 , 494 * 2);g.drawString(tip2, 47 * 2 , 527 * 2);g.drawString(tip3, 47 * 2 , 559 * 2);img = Thumbnails.of(img).scale(1f).outputQuality(1f).asBufferedImage();} catch (IOException e) {throw new IOException(e.getMessage());} finally {g.dispose();}return img;}
}public static int getWidth(FontRenderContext context, Graphics2D g, String text) {return (int) g.getFont().getStringBounds(text, context).getWidth();}public static int getHeight(FontRenderContext context, Graphics2D g, String text) {return (int) g.getFont().getStringBounds(text, context).getHeight();}public static BufferedImage roundImage(BufferedImage image, int width, int height, int cornerRadius) {BufferedImage outputImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);Graphics2D g2 = outputImage.createGraphics();g2.setComposite(AlphaComposite.Src);g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);g2.setColor(Color.WHITE);g2.fill(new RoundRectangle2D.Float(0, 0, width, height, cornerRadius, cornerRadius));g2.setComposite(AlphaComposite.SrcAtop);g2.drawImage(image, 0, 0, null);g2.dispose();return outputImage;}public static BufferedImage roundImage(BufferedImage image, int cornerRadius) {return roundImage(image, image.getWidth(), image.getHeight(), cornerRadius);}public static Font getPingFang() {BufferedInputStream bis = null;Font definedFont = null;try {bis = new BufferedInputStream(new FileInputStream("C:\\Users\\youqiang.xiong\\git\\WechatManagement\\wechat-management-parent\\wechat-management-m-web\\src\\main\\resources\\pingfang.ttf"));} catch (FileNotFoundException e) {e.printStackTrace();}try {definedFont = Font.createFont(Font.TRUETYPE_FONT, bis);return definedFont;} catch (FontFormatException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}
pom.xml 新增依赖
<dependency><groupId>net.coobird</groupId><artifactId>thumbnailator</artifactId><version>0.4.8</version></dependency>
编写main方法,测试合成图片
public static void main(String[] args) {try {BufferedImage bg = ImageIO.read(new File("C:\\Users\\youqiang.xiong\\Desktop\\底图\\make_poster_base.png"));BufferedImage poster = ImageIO.read(new File("C:\\\\Users\\\\youqiang.xiong\\\\Desktop\\\\底图\\\\poster.jpg"));BufferedImage qr = ImageIO.read(new File("C:\\\\Users\\\\youqiang.xiong\\\\Desktop\\\\底图\\\\qr.jpg"));BufferedImage avatar = ImageIO.read(new URL("https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTK4DYtvaMV6YIficQnj2D8CiaabaSAq1kzNZUAZCWn5oGv6n8AnjuYTH3bBqELica2yeKbYiakSUMsQIQ/132"));String nickname = "易幻";String courseName = "做有知识的消费者,过1%的品质生活";String courseTip = "给人看和给人讲的PPT有什么区别?\n为什么用了模板还是很丑为什么用了模板还是很丑为什么用了模板还是很丑?\n如何体现PPT制作的专业性?";BufferedImage img = ImageUtils.createSharePoster(bg, poster, qr, avatar, getPingFang(), nickname, courseName, courseTip);ImageIO.write(img, "jpg", new File("C:\\Users\\youqiang.xiong\\Desktop\\底图\\result5.jpg"));} catch (Exception e) {e.printStackTrace();}}
上面的图片是读取我本地的图片,为了方便大家测试,我也把这几张底图和教师形象图上传,以便大家测试,下载图片到本地后,记得修改main方法中的图片路径
第1张海报底图make_poster_base.png
第2张教师形象图poster.jpg
第3张小程序码图片qr.jpg
图片准备好,运行main方法,在指定目录下会生成合成图片 如下图所示:
放大图片效果:
到这里一个完成合成小程序海报图的功能已经完成,还算是比较简单的,只需要根据ui给的设计稿来调整字体大小,颜色,xy坐标,长宽即可。