微信小程序海报生成图片合成工具类

ゝ一世哀愁。 2022-03-24 18:40 544阅读 0赞

背景

我目前参与小程序的产品研发,为了方便产品的转发和推广,会对课提供生成海报转发或者分享的功能,前期海报合成这个功能是由项目组的老同事负责开发,后来小程序海报这块功能需要单独做一个功能用于专门根据不同的课程类型来生成海报的功能,很荣幸这个任务交由我开发。编码过程这边就略过了,直接看代码如下所示:

EmojiUtils工具类

  1. package com.xyq.maventest.util;
  2. import java.io.UnsupportedEncodingException;
  3. import java.net.URLDecoder;
  4. import java.net.URLEncoder;
  5. import java.util.regex.Matcher;
  6. import java.util.regex.Pattern;
  7. import org.apache.commons.lang.StringUtils;
  8. import org.slf4j.Logger;
  9. import org.slf4j.LoggerFactory;
  10. /***
  11. *
  12. * Project Name:maventest
  13. * <p>emoji标签转换工具类<br>
  14. *
  15. * @ClassName: EmojiUtils
  16. * @date 2019年1月22日 下午4:29:26
  17. *
  18. * @author youqiang.xiong
  19. * @version 1.0
  20. * @since
  21. * @see
  22. */
  23. public class EmojiUtils {
  24. private static Logger logger = LoggerFactory.getLogger(EmojiUtils.class);
  25. /**
  26. * 将emoji标签转换成utf8字符集保存进数据库
  27. * @param str
  28. * @return
  29. */
  30. public static String emojiConvert(String str) {
  31. String patternString = "([\\x{10000}-\\x{10ffff}\\ud800-\\udfff])";
  32. Pattern pattern = Pattern.compile(patternString);
  33. Matcher matcher = pattern.matcher(str);
  34. StringBuffer sb = new StringBuffer();
  35. while(matcher.find()) {
  36. // matcher.appendReplacement(sb,"?");
  37. try {
  38. matcher.appendReplacement(sb,"[[" + URLEncoder.encode(matcher.group(1),"UTF-8") + "]]");
  39. } catch(UnsupportedEncodingException e) {
  40. logger.error("emojiConvert error"+ e.getMessage());
  41. return str;
  42. }
  43. }
  44. matcher.appendTail(sb);
  45. logger.debug("emojiConvert " + str + " to " + sb.toString() + ", len:" + sb.length());
  46. return sb.toString();
  47. }
  48. /**
  49. * 还原utf8数据库中保存的含转换后emoji表情的字符串
  50. * @param str
  51. * @return
  52. */
  53. public static String emojiRecovery(String str) {
  54. if(StringUtils.isEmpty(str)){
  55. return "";
  56. }
  57. try{
  58. String patternString = "\\[\\[(.*?)\\]\\]";
  59. Pattern pattern = Pattern.compile(patternString);
  60. Matcher matcher = pattern.matcher(str);
  61. StringBuffer sb = new StringBuffer();
  62. while(matcher.find()) {
  63. try {
  64. matcher.appendReplacement(sb, URLDecoder.decode(matcher.group(1), "UTF-8"));
  65. } catch(UnsupportedEncodingException e) {
  66. logger.error("emojiRecovery error"+ e.getMessage());
  67. return "";
  68. }
  69. }
  70. matcher.appendTail(sb);
  71. logger.debug("emojiRecovery " + str + " to " + sb.toString());
  72. return sb.toString();
  73. }catch (Exception e){
  74. logger.error("emojiRecovery error",e);
  75. }
  76. return str;
  77. }
  78. }

ImageUtils 工具类

核心代码如下:

  1. /***
  2. *
  3. * Project Name:wechat-management-util
  4. * <p>各种海报合成图片工具类<br>
  5. *
  6. * @ClassName: ImageUtils
  7. * @date 2019年1月22日 下午3:52:43
  8. *
  9. * @author youqiang.xiong
  10. * @version 1.0
  11. * @since
  12. * @see
  13. */
  14. public class ImageUtils {
  15. private static final Logger logger = LoggerFactory.getLogger(ImageUtils.class);
  16. /****
  17. *
  18. * Project Name: wechat-management-util
  19. * <p>公开课小程序后台生成分享海报 <br>
  20. *
  21. * @author youqiang.xiong
  22. * @date 2019年1月11日 下午4:28:21
  23. * @version v1.0
  24. * @since
  25. * @param background
  26. * 背景图
  27. * @param poster
  28. * 教师形象图
  29. * @param qr
  30. * 二维码图片
  31. * @param avatar
  32. * 头像图片
  33. * @param font
  34. * 字体
  35. * @param nickname
  36. * 昵称
  37. * @param courseName
  38. * 课程名称
  39. * @param courseTip
  40. * 课程大纲
  41. * @return
  42. * @throws IOException
  43. */
  44. public static BufferedImage createSharePoster(BufferedImage background, BufferedImage poster,
  45. BufferedImage qr,BufferedImage avatar,Font font, String nickname,String courseName,String courseTip ) throws IOException{
  46. // 开启抗锯齿
  47. RenderingHints renderingHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  48. // 使用高质量压缩
  49. renderingHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
  50. renderingHints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
  51. int height = background.getHeight() * 750 / background.getWidth();
  52. // 图片大小
  53. BufferedImage img = new BufferedImage(750, height, BufferedImage.TYPE_INT_RGB);
  54. // 开启画图
  55. Graphics2D g = (Graphics2D) img.getGraphics();
  56. try {
  57. g.setRenderingHints(renderingHints);
  58. // 教师海报图 - 需要将原图进行剪切 剪切后的长度是 750*830
  59. BufferedImage teacherImage = cutPic(poster, 158, 0, 750, 830);
  60. g.drawImage(teacherImage.getScaledInstance(750,teacherImage.getHeight(), Image.SCALE_FAST), 0, 0, null);
  61. // 画底图
  62. g.drawImage(background.getScaledInstance(750, height, Image.SCALE_FAST), 0, 0, null);
  63. // 画头像
  64. if (null != avatar) {
  65. g.drawImage(ImageUtils.roundImage(avatar, avatar.getWidth(), avatar.getHeight(), 360).getScaledInstance(45*2, 45*2, Image.SCALE_FAST), 25*2, 603*2, null);
  66. }
  67. // 画二维码
  68. if (null != qr) {
  69. g.drawImage(qr.getScaledInstance(70*2, 70*2, Image.SCALE_FAST), 286*2, 591*2, null);
  70. }
  71. // 设置字体大小
  72. font = font.deriveFont(34f);
  73. // 设置字体颜色
  74. Color color = new Color(55,69,97);
  75. g.setColor(color);
  76. g.setFont(font);
  77. //昵称
  78. nickname = nickname == null ? "" : nickname;
  79. nickname = EmojiUtils.emojiRecovery(nickname);
  80. if(nickname.length() > 5) {
  81. nickname = nickname.substring(0, 5) + "...";
  82. }
  83. int stringWidth = getWidth(g.getFontRenderContext(), g, nickname);
  84. g.drawString(nickname, 84 * 2 , 623 * 2);
  85. //画'向您推荐好课'
  86. String recommendText = "向您推荐好课";
  87. font = font.deriveFont(24f);
  88. // 设置字体颜色
  89. color = new Color(55,69,97);
  90. g.setColor(color);
  91. g.setFont(font);
  92. g.drawString(recommendText, 84 * 2 + stringWidth , 623 * 2);
  93. // 写课程名称
  94. font = font.deriveFont(60.0f);
  95. color = new Color(69,75,106);
  96. g.setColor(color);
  97. g.setFont(font);
  98. int courseWidth = getWidth(g.getFontRenderContext(), g, courseName);
  99. if(courseWidth >= 334*2) {
  100. String c1 = courseName.substring(0, 10);
  101. String c2 = courseName.substring(10, courseName.length());
  102. int courseHeight = getHeight(g.getFontRenderContext(), g, c1);
  103. g.drawString(c1, 21 * 2 , (430 * 2) - 2);
  104. g.drawString(c2, 21 * 2 , 420 * 2 + courseHeight + 2);
  105. }else {
  106. g.drawString(courseName, 21 * 2 , 448 * 2);
  107. }
  108. //写课程大纲
  109. List<String> tipList = Arrays.asList(courseTip.split("\n"));
  110. String tip1 = tipList.get(0);
  111. if(tip1.length() >= 20) {
  112. tip1 = tip1.substring(0, 19) + "...";
  113. }
  114. String tip2 = tipList.get(1);
  115. if(tip2.length() >= 20) {
  116. tip2 = tip2.substring(0, 19) + "...";
  117. }
  118. String tip3 = tipList.get(2);
  119. if(tip3.length() >= 20) {
  120. tip3 = tip3.substring(0, 19) + "...";
  121. }
  122. font = font.deriveFont(30.0f);
  123. color = new Color(176,153,104);
  124. g.setColor(color);
  125. g.setFont(font);
  126. g.drawString(tip1, 47 * 2 , 494 * 2);
  127. g.drawString(tip2, 47 * 2 , 527 * 2);
  128. g.drawString(tip3, 47 * 2 , 559 * 2);
  129. img = Thumbnails.of(img).scale(1f).outputQuality(1f).asBufferedImage();
  130. } catch (IOException e) {
  131. throw new IOException(e.getMessage());
  132. } finally {
  133. g.dispose();
  134. }
  135. return img;
  136. }
  137. }
  138. public static int getWidth(FontRenderContext context, Graphics2D g, String text) {
  139. return (int) g.getFont().getStringBounds(text, context).getWidth();
  140. }
  141. public static int getHeight(FontRenderContext context, Graphics2D g, String text) {
  142. return (int) g.getFont().getStringBounds(text, context).getHeight();
  143. }
  144. public static BufferedImage roundImage(BufferedImage image, int width, int height, int cornerRadius) {
  145. BufferedImage outputImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
  146. Graphics2D g2 = outputImage.createGraphics();
  147. g2.setComposite(AlphaComposite.Src);
  148. g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  149. g2.setColor(Color.WHITE);
  150. g2.fill(new RoundRectangle2D.Float(0, 0, width, height, cornerRadius, cornerRadius));
  151. g2.setComposite(AlphaComposite.SrcAtop);
  152. g2.drawImage(image, 0, 0, null);
  153. g2.dispose();
  154. return outputImage;
  155. }
  156. public static BufferedImage roundImage(BufferedImage image, int cornerRadius) {
  157. return roundImage(image, image.getWidth(), image.getHeight(), cornerRadius);
  158. }
  159. public static Font getPingFang() {
  160. BufferedInputStream bis = null;
  161. Font definedFont = null;
  162. try {
  163. bis = new BufferedInputStream(new FileInputStream(
  164. "C:\\Users\\youqiang.xiong\\git\\WechatManagement\\wechat-management-parent\\wechat-management-m-web\\src\\main\\resources\\pingfang.ttf"));
  165. } catch (FileNotFoundException e) {
  166. e.printStackTrace();
  167. }
  168. try {
  169. definedFont = Font.createFont(Font.TRUETYPE_FONT, bis);
  170. return definedFont;
  171. } catch (FontFormatException e) {
  172. e.printStackTrace();
  173. } catch (IOException e) {
  174. e.printStackTrace();
  175. }
  176. return null;
  177. }

pom.xml 新增依赖

  1. <dependency>
  2. <groupId>net.coobird</groupId>
  3. <artifactId>thumbnailator</artifactId>
  4. <version>0.4.8</version>
  5. </dependency>

编写main方法,测试合成图片

  1. public static void main(String[] args) {
  2. try {
  3. BufferedImage bg = ImageIO.read(new File("C:\\Users\\youqiang.xiong\\Desktop\\底图\\make_poster_base.png"));
  4. BufferedImage poster = ImageIO.read(new File("C:\\\\Users\\\\youqiang.xiong\\\\Desktop\\\\底图\\\\poster.jpg"));
  5. BufferedImage qr = ImageIO.read(new File("C:\\\\Users\\\\youqiang.xiong\\\\Desktop\\\\底图\\\\qr.jpg"));
  6. BufferedImage avatar = ImageIO.read(new URL("https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTK4DYtvaMV6YIficQnj2D8CiaabaSAq1kzNZUAZCWn5oGv6n8AnjuYTH3bBqELica2yeKbYiakSUMsQIQ/132"));
  7. String nickname = "易幻";
  8. String courseName = "做有知识的消费者,过1%的品质生活";
  9. String courseTip = "给人看和给人讲的PPT有什么区别?\n为什么用了模板还是很丑为什么用了模板还是很丑为什么用了模板还是很丑?\n如何体现PPT制作的专业性?";
  10. BufferedImage img = ImageUtils.createSharePoster(bg, poster, qr, avatar, getPingFang(), nickname, courseName, courseTip);
  11. ImageIO.write(img, "jpg", new File("C:\\Users\\youqiang.xiong\\Desktop\\底图\\result5.jpg"));
  12. } catch (Exception e) {
  13. e.printStackTrace();
  14. }
  15. }

上面的图片是读取我本地的图片,为了方便大家测试,我也把这几张底图和教师形象图上传,以便大家测试,下载图片到本地后,记得修改main方法中的图片路径

第1张海报底图make_poster_base.png
在这里插入图片描述

第2张教师形象图poster.jpg
在这里插入图片描述

第3张小程序码图片qr.jpg

在这里插入图片描述

图片准备好,运行main方法,在指定目录下会生成合成图片 如下图所示:
在这里插入图片描述

放大图片效果:

在这里插入图片描述

到这里一个完成合成小程序海报图的功能已经完成,还算是比较简单的,只需要根据ui给的设计稿来调整字体大小,颜色,xy坐标,长宽即可。

发表评论

表情:
评论列表 (有 0 条评论,544人围观)

还没有评论,来说两句吧...

相关阅读