2

Java使用Graphics2D生成海报

 2 years ago
source link: https://maxqiu.com/article/detail/124
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

示例代码:
GitHub:https://github.com/Max-Qiu/demo
Gitee:https://github.com/Max-Qiu/demo


参考文档:
沉默王二:Java生成二维码分享海报
CSDN:JAVA后台生成海报分享图片

  • 背景
    • 自定义背景图片
    • 自定义纯色背景
  • 文字
    • 自定义字体文件
    • 短标题文本
    • 长内容文本(自动换行)

海报对象实体

  1. import java.awt.*;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.util.List;
  5. import java.util.Objects;
  6. import lombok.Getter;
  7. /**
  8. * 海报实体
  9. *
  10. * @author Max_Qiu
  11. */
  12. @Getter
  13. public class PosterEntity {
  14. private Background background;
  15. private List<Text> textList;
  16. private List<Image> imageList;
  17. /**
  18. * 创建海报对象
  19. *
  20. * @param background
  21. * 背景图片位置(resources目录下)
  22. * @param textList
  23. * 文本集合
  24. * @param imageList
  25. * 图片集合
  26. */
  27. public PosterEntity(Background background, List<Text> textList, List<Image> imageList) {
  28. this.background = background;
  29. this.textList = textList;
  30. this.imageList = imageList;
  31. }
  32. /**
  33. * 背景对象
  34. */
  35. @Getter
  36. public static class Background {
  37. private boolean fromImage;
  38. private InputStream background;
  39. private int width;
  40. private int height;
  41. private Color color;
  42. /**
  43. * 创建背景对象(从图片)
  44. *
  45. * @param backgroundPath
  46. * 背景图片路径
  47. */
  48. public Background(String backgroundPath) {
  49. this.fromImage = true;
  50. ClassLoader loader = getClass().getClassLoader();
  51. this.background = loader.getResourceAsStream(backgroundPath);
  52. }
  53. /**
  54. * 创建背景对象(指定宽高和背景色)
  55. *
  56. * @param width
  57. * 宽
  58. * @param height
  59. * 高
  60. * @param color
  61. * 颜色
  62. */
  63. public Background(int width, int height, Color color) {
  64. this.fromImage = false;
  65. this.width = width;
  66. this.height = height;
  67. this.color = color;
  68. }
  69. }
  70. /**
  71. * 文本
  72. *
  73. * @author Max_Qiu
  74. */
  75. @Getter
  76. public static class Text {
  77. private Font font;
  78. private Color color;
  79. private String text;
  80. private int textX;
  81. private int textY;
  82. private boolean needBr;
  83. private int width;
  84. /**
  85. * 创建文本实体(短文本,例如标题)
  86. *
  87. * @param fontPath
  88. * 字体文件路径(resources目录下)
  89. * @param size
  90. * 字体大小
  91. * @param color
  92. * 字体颜色
  93. * @param text
  94. * 文本
  95. * @param textX
  96. * 坐标X(开始位置为文本左上角)
  97. * @param textY
  98. * 坐标Y(开始位置为文本左上角)
  99. */
  100. public Text(String fontPath, float size, Color color, String text, int textX, int textY) {
  101. ClassLoader loader = getClass().getClassLoader();
  102. try {
  103. // 从 resources 目录下加载字体文本
  104. InputStream inputStream = Objects.requireNonNull(loader.getResourceAsStream(fontPath));
  105. // 创建字体
  106. this.font = Font.createFont(0, inputStream).deriveFont(size);
  107. } catch (FontFormatException | IOException e) {
  108. e.printStackTrace();
  109. }
  110. this.color = color;
  111. this.text = text;
  112. this.textX = textX;
  113. this.textY = textY;
  114. this.needBr = false;
  115. }
  116. /**
  117. * 创建文本实体(短文本,例如内容)
  118. *
  119. * @param fontPath
  120. * 字体文件路径(resources目录下)
  121. * @param size
  122. * 字体大小
  123. * @param color
  124. * 字体颜色
  125. * @param text
  126. * 文本
  127. * @param textX
  128. * 坐标X(开始位置为文本左上角)
  129. * @param textY
  130. * 坐标Y(开始位置为文本左上角)
  131. * @param width
  132. * 长文本自动换行宽度
  133. */
  134. public Text(String fontPath, float size, Color color, String text, int textX, int textY, int width) {
  135. ClassLoader loader = getClass().getClassLoader();
  136. try {
  137. // 从 resources 目录下加载字体文本
  138. InputStream inputStream = Objects.requireNonNull(loader.getResourceAsStream(fontPath));
  139. // 创建字体
  140. this.font = Font.createFont(0, inputStream).deriveFont(size);
  141. } catch (FontFormatException | IOException e) {
  142. e.printStackTrace();
  143. }
  144. this.color = color;
  145. this.text = text;
  146. this.textX = textX;
  147. this.textY = textY;
  148. this.needBr = true;
  149. this.width = width;
  150. }
  151. }
  152. /**
  153. * 图片
  154. *
  155. * @author Max_Qiu
  156. */
  157. @Getter
  158. public static class Image {
  159. private InputStream image;
  160. private int imageWidth;
  161. private int imageHeight;
  162. private int imageX;
  163. private int imageY;
  164. /**
  165. * 创建图片对象
  166. *
  167. * @param imagePath
  168. * 图片路径(resources目录下)
  169. * @param imageWidth
  170. * 图片宽度(输出宽度)
  171. * @param imageHeight
  172. * 图片高度(输出高度)
  173. * @param imageX
  174. * 图片X(开始位置为图片左上角)
  175. * @param imageY
  176. * 图片Y(开始位置为图片左上角)
  177. */
  178. public Image(String imagePath, int imageWidth, int imageHeight, int imageX, int imageY) {
  179. ClassLoader loader = getClass().getClassLoader();
  180. this.image = loader.getResourceAsStream(imagePath);
  181. this.imageWidth = imageWidth;
  182. this.imageHeight = imageHeight;
  183. this.imageX = imageX;
  184. this.imageY = imageY;
  185. }
  186. }
  187. }

海报生成工具类

  1. package com.maxqiu.demo.utils;
  2. import java.awt.*;
  3. import java.awt.image.BufferedImage;
  4. import java.io.File;
  5. import java.io.IOException;
  6. import javax.imageio.ImageIO;
  7. import sun.font.FontDesignMetrics;
  8. /**
  9. * 海报生成工具类
  10. *
  11. * @author Max_Qiu
  12. */
  13. public class PosterUtil {
  14. /**
  15. * 生成工具
  16. *
  17. * @param poster
  18. * 海报实体
  19. * @param outputFile
  20. * 目标文件
  21. * @param formatName
  22. * 图片格式(jpg/png)
  23. * @throws IOException
  24. * IO异常
  25. */
  26. public static void drawPoster(PosterEntity poster, String outputFile, String formatName) throws IOException {
  27. BufferedImage bufferedImage;
  28. Graphics2D graphics;
  29. PosterEntity.Background background = poster.getBackground();
  30. if (background.isFromImage()) {
  31. // 读取背景图片
  32. bufferedImage = ImageIO.read(background.getBackground());
  33. // 获取 Graphics2D 对象
  34. graphics = (Graphics2D)bufferedImage.getGraphics();
  35. } else {
  36. // 创建背景图片
  37. bufferedImage =
  38. new BufferedImage(background.getWidth(), background.getHeight(), BufferedImage.TYPE_INT_RGB);
  39. // 获取 Graphics2D 对象
  40. graphics = (Graphics2D)bufferedImage.getGraphics();
  41. // 设置背景色
  42. graphics.setBackground(Color.WHITE);
  43. // 刷新颜色
  44. graphics.clearRect(0, 0, background.getWidth(), background.getHeight());
  45. }
  46. // 设置字体平滑
  47. graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
  48. // 遍历字体
  49. for (PosterEntity.Text text : poster.getTextList()) {
  50. if (text.isNeedBr()) {
  51. FontDesignMetrics metrics = FontDesignMetrics.getMetrics(text.getFont());
  52. String[] zhWraps = makeLineFeed(text.getText(), metrics, text.getWidth()).split("\n");
  53. // 字体
  54. graphics.setFont(text.getFont());
  55. // 颜色
  56. graphics.setColor(text.getColor());
  57. int y = text.getTextY();
  58. // 将每一行在海报背景上打印
  59. for (String wrap : zhWraps) {
  60. graphics.drawString(wrap, text.getTextX(), y);
  61. y += metrics.getHeight();
  62. }
  63. } else {
  64. // 字体
  65. graphics.setFont(text.getFont());
  66. // 颜色
  67. graphics.setColor(text.getColor());
  68. // 写入文本
  69. graphics.drawString(text.getText(), text.getTextX(), text.getTextY());
  70. }
  71. }
  72. // 遍历图片
  73. for (PosterEntity.Image image : poster.getImageList()) {
  74. // 读取贴图
  75. BufferedImage face = ImageIO.read(image.getImage());
  76. // 设置图片输出宽度和高度
  77. Image instance = face.getScaledInstance(image.getImageWidth(), image.getImageHeight(), Image.SCALE_DEFAULT);
  78. // 写入图片
  79. graphics.drawImage(instance, image.getImageX(), image.getImageY(), null);
  80. }
  81. // 释放资源
  82. graphics.dispose();
  83. // 输出文件
  84. ImageIO.write(bufferedImage, formatName, new File(outputFile));
  85. }
  86. /**
  87. * 切割字符串
  88. *
  89. * 本方法摘自:http://www.itwanger.com/java/2019/11/14/java-qrcode-poster.html
  90. *
  91. * @param zh
  92. * 文本
  93. * @param metrics
  94. * FontDesignMetrics
  95. * @param maxWidth
  96. * 最大宽度
  97. */
  98. public static String makeLineFeed(String zh, FontDesignMetrics metrics, int maxWidth) {
  99. StringBuilder sb = new StringBuilder();
  100. int lineWidth = 0;
  101. for (int i = 0; i < zh.length(); i++) {
  102. char c = zh.charAt(i);
  103. sb.append(c);
  104. // 如果主动换行则跳过
  105. if (sb.toString().endsWith("\n")) {
  106. lineWidth = 0;
  107. continue;
  108. }
  109. // FontDesignMetrics 的 charWidth() 方法可以计算字符的宽度
  110. int charWidth = metrics.charWidth(c);
  111. lineWidth += charWidth;
  112. // 如果当前字符的宽度加上之前字符串的已有宽度超出了海报的最大宽度,则换行
  113. if (lineWidth >= maxWidth - charWidth) {
  114. lineWidth = 0;
  115. sb.append("\n");
  116. }
  117. }
  118. return sb.toString();
  119. }
  120. }
  1. package com.maxqiu.demo;
  2. import java.awt.*;
  3. import java.io.IOException;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. import java.util.UUID;
  7. import com.maxqiu.demo.utils.PosterEntity;
  8. import com.maxqiu.demo.utils.PosterUtil;
  9. /**
  10. * 测试
  11. *
  12. * @author Max_Qiu
  13. */
  14. public class PosterUtilTest {
  15. /**
  16. * 文件输出根路径
  17. */
  18. private static final String BASE_OUTPUT_FILE_PATH = "/basePath/";
  19. public static void main(String[] args) {
  20. long startTime = System.currentTimeMillis();
  21. // 创建背景
  22. PosterEntity.Background background = new PosterEntity.Background("poster/background.jpg");
  23. // 也可以使用纯色背景
  24. PosterEntity.Background background2 = new PosterEntity.Background(1700, 1200, Color.WHITE);
  25. // 创建多个文本
  26. List<PosterEntity.Text> textList = new ArrayList<>();
  27. // 分别指定字体、大小、颜色(RGB创建颜色)、内容、坐标x、坐标y
  28. PosterEntity.Text title =
  29. new PosterEntity.Text("poster/SourceHanSerifCN-Bold.otf", 120, new Color(213, 28, 28), "标题", 600, 200);
  30. textList.add(title);
  31. String contentStr = "这是一段测试文本!This is a test text!这是一段测试文本!\n这是一段测试文本!This is a test text!";
  32. // 分别指定字体、大小、颜色(也可以使用枚举)、内容、坐标x、坐标y、长文本最大宽度
  33. PosterEntity.Text content =
  34. new PosterEntity.Text("poster/SourceHanSerifCN-Regular.otf", 50, Color.BLACK, contentStr, 400, 300, 800);
  35. textList.add(content);
  36. // 创建多个图片
  37. List<PosterEntity.Image> imageList = new ArrayList<>();
  38. PosterEntity.Image image1 = new PosterEntity.Image("poster/face.jpg", 400, 400, 300, 600);
  39. imageList.add(image1);
  40. PosterEntity.Image image2 = new PosterEntity.Image("poster/Q.png", 100, 100, 1200, 800);
  41. imageList.add(image2);
  42. // 创建海报实体
  43. PosterEntity posterEntity = new PosterEntity(background, textList, imageList);
  44. // 指定输出后的文件格式(一般同背景图片文件格式)
  45. String formatName = "jpg";
  46. // 指定文件名(此处使用UUID)
  47. String fileName = UUID.randomUUID() + "." + formatName;
  48. // 生成
  49. try {
  50. PosterUtil.drawPoster(posterEntity, BASE_OUTPUT_FILE_PATH + fileName, formatName);
  51. } catch (IOException e) {
  52. e.printStackTrace();
  53. }
  54. System.out.println("生成成功!");
  55. System.out.println("生成时长: " + (System.currentTimeMillis() - startTime) / 1000.0 + "s");
  56. System.out.println("文件路径: " + BASE_OUTPUT_FILE_PATH + fileName);
  57. }
  58. }

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK