• Java 基于Graphics2D 实现海报(支持自定义颜色,背景,logo,贴图)


    效果:

    海报一:

    海报二:

    代码:

    参数实体类:

    1. package com.ly.cloud.dto;
    2. import lombok.Data;
    3. /**
    4. * @Author
    5. * @Date Created in 2024/4/24 下午2:16
    6. * @DESCRIPTION: 海报页面所需的参数 实体类
    7. * @Version V1.0
    8. */
    9. @Data
    10. public class PosterTemplateDto {
    11. /**
    12. * 用来判断是不是 需要把数据写死 0 写死的对象,1 自己传的
    13. */
    14. private String id;
    15. {
    16. id = "0";
    17. }
    18. /**
    19. * 海报编号 0: 横向海报 1: 竖向海报
    20. */
    21. private String posterId;
    22. /**
    23. * 海报颜色 0: 绿色 1: 蓝色 2: 红色
    24. */
    25. private String posterColor;
    26. /**
    27. * 海报背景图片名称
    28. */
    29. private String posterBackImage;
    30. /**
    31. * 海报logo
    32. */
    33. private String posterLogo;
    34. /**
    35. * 海报主题 xxxx 第 x 期
    36. */
    37. private String posterTopic;
    38. /**
    39. * 海报标题
    40. */
    41. private String posterTitle;
    42. /**
    43. * 海报年份
    44. */
    45. private String posterYear;
    46. /**
    47. * 海报具体时间 几月几号
    48. */
    49. private String posterMonth;
    50. /**
    51. * 海报具体时间 14:30 ~ 15:30(周四)
    52. */
    53. private String posterDay;
    54. /**
    55. * 讲座就行校区
    56. */
    57. private String posterSchool;
    58. /**
    59. * 讲座的具体地址
    60. */
    61. private String posterAddress;
    62. /**
    63. * 讲座主讲人
    64. */
    65. private String posterMainSpeaker;
    66. /**
    67. * 主讲人个人详细信息介绍
    68. */
    69. private String posterPeopleDetail;
    70. /**
    71. * 主持人
    72. */
    73. private String mainPeople;
    74. }

    工具类:

    FileUtil.java
    1. package com.ly.cloud.util;
    2. import com.alibaba.druid.support.logging.Log;
    3. import com.alibaba.druid.support.logging.LogFactory;
    4. import org.springframework.web.multipart.MultipartFile;
    5. import javax.imageio.ImageIO;
    6. import java.awt.*;
    7. import java.awt.image.BufferedImage;
    8. import java.io.*;
    9. import java.nio.file.Files;
    10. /**
    11. * @Author
    12. * @Date Created in 2024/4/11 上午11:13
    13. * @DESCRIPTION: 将 multipartFile --> file
    14. * @Version V1.0
    15. */
    16. public class FileUtil {
    17. private final static Log logger = LogFactory.getLog(FileUtil.class);
    18. /**
    19. * 将 multipartFile --> fileInputStream
    20. */
    21. public static FileInputStream convertMultipartFileToInputStream(MultipartFile multipartFile) throws IOException {
    22. // 创建临时文件
    23. File tempFile = File.createTempFile("temp", null);
    24. // 将MultipartFile的数据写入临时文件
    25. try (FileOutputStream fos = new FileOutputStream(tempFile)) {
    26. fos.write(multipartFile.getBytes());
    27. }
    28. // 将临时文件转换为FileInputStream
    29. FileInputStream fileInputStream = new FileInputStream(tempFile);
    30. // 删除临时文件
    31. tempFile.delete();
    32. return fileInputStream;
    33. }
    34. /**
    35. * 将上传的 logo图片保存在本地;
    36. */
    37. public static File convertInputStreamToFile(InputStream inputStream,String fileName) throws IOException {
    38. // 创建图片文件
    39. String resourcesPath = new File("src/main/resources/").getAbsolutePath();
    40. // 拼接images目录的路径
    41. String imagesPath = resourcesPath + "/static/imagesLogo/"; // 这里的static是resources目录下的一个示例子目录,你可以根据实际情况修改
    42. logger.info("我的路径是:{}"+imagesPath);
    43. File directory = new File(imagesPath);
    44. // 如果目标文件夹不存在,则创建它
    45. if (!directory.exists()) {
    46. directory.mkdirs();
    47. }
    48. File file = new File(directory, fileName);
    49. try (OutputStream outputStream = Files.newOutputStream(file.toPath())) {
    50. byte[] buffer = new byte[1024];
    51. int length;
    52. while ((length = inputStream.read(buffer)) != -1) {
    53. outputStream.write(buffer, 0, length);
    54. }
    55. }
    56. // deleteImage(imagesPath,fileName);
    57. return file;
    58. }
    59. /**
    60. * 删除图片
    61. */
    62. public static void deleteImage(String directoryPath, String imageName) {
    63. File directory = new File(directoryPath);
    64. if (!directory.exists() || !directory.isDirectory()) {
    65. return;
    66. }
    67. File imageFile = new File(directory, imageName);
    68. if (imageFile.exists() && imageFile.isFile()) {
    69. if (imageFile.delete()) {
    70. logger.info("删除图片成功!");
    71. } else {
    72. logger.error("删除图片失败!");
    73. }
    74. } else {
    75. System.out.println("Image file " + imageName + " does not exist.");
    76. }
    77. }
    78. /**
    79. * 修改图片的宽高 等比例扩大 海报背景图和 logo
    80. */
    81. public static byte[] resizeImage(byte[] imageData, int width, int height) throws IOException {
    82. ByteArrayInputStream bis = new ByteArrayInputStream(imageData);
    83. BufferedImage image = ImageIO.read(bis);
    84. Image resizedImage = image.getScaledInstance(width, height, Image.SCALE_SMOOTH);
    85. BufferedImage bufferedResizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    86. bufferedResizedImage.getGraphics().drawImage(resizedImage, 0, 0, null);
    87. ByteArrayOutputStream bos = new ByteArrayOutputStream();
    88. ImageIO.write(bufferedResizedImage, "jpg", bos);
    89. byte[] resizedImageData = bos.toByteArray();
    90. bis.close();
    91. bos.close();
    92. return resizedImageData;
    93. }
    94. // public static void main(String[] args) throws IOException {
    95. // String resourcesPath = new File("src/main/resources/").getAbsolutePath();
    96. // // 拼接images目录的路径
    97. // String imagesPath = resourcesPath + "/static/images/"; // 这里的static
    98. //
    99. // // 读取背景图片
    100. // BufferedImage backgroundImage = ImageIO.read(new File(imagesPath + "/" + "4c9a599ae920240424112637.png"));
    101. // ImageIO.write(backgroundImage, "PNG", new File("D:\\test1\\image.png"));
    102. //
    103. // }
    104. }
    PosterUtil.java 
    1. package com.ly.cloud.util;
    2. import sun.font.FontDesignMetrics;
    3. import java.awt.*;
    4. import java.awt.geom.AffineTransform;
    5. import java.awt.geom.Ellipse2D;
    6. import java.awt.image.BufferedImage;
    7. /**
    8. * @Author
    9. * @Date Created in 2024/4/24 下午2:45
    10. * @DESCRIPTION: 海报工具类
    11. * @Version V1.0
    12. */
    13. public class PosterUtil {
    14. /**
    15. * 从新设置海报图片的宽和高
    16. * @param originalImage 原始图片
    17. * @param targetWidth 宽
    18. * @param targetHeight 高
    19. * @return BufferedImage
    20. */
    21. public static BufferedImage scaleImage(BufferedImage originalImage, int targetWidth, int targetHeight) {
    22. int type = originalImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : originalImage.getType();
    23. BufferedImage scaledImage = new BufferedImage(targetWidth, targetHeight, type);
    24. Graphics2D g = scaledImage.createGraphics();
    25. // Calculate the ratio between the original and scaled image size
    26. double scaleX = (double) targetWidth / originalImage.getWidth();
    27. double scaleY = (double) targetHeight / originalImage.getHeight();
    28. double scale = Math.min(scaleX, scaleY);
    29. // Now we perform the actual scaling
    30. int newWidth = (int) (originalImage.getWidth() * scale);
    31. int newHeight = (int) (originalImage.getHeight() * scale);
    32. int x = (targetWidth - newWidth) / 2;
    33. int y = (targetHeight - newHeight) / 2;
    34. g.drawImage(originalImage.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH), x, y, null);
    35. g.dispose();
    36. return scaledImage;
    37. }
    38. /**
    39. * @Author
    40. * @Description 海报横向文字写字换行算法
    41. * @Date 18:08 2024/4/24
    42. * @Param 参数 Graphics2D 对象 、font 字体设置 、 文字、 x轴左边、 y轴坐标 、每行字体的换行宽度
    43. **/
    44. public static void drawWordAndLineFeed(Graphics2D g2d, Font font, String words, int wordsX, int wordsY, int wordsWidth) {
    45. FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
    46. // 获取字符的最高的高度
    47. int height = metrics.getHeight();
    48. int width = 0;
    49. int count = 0;
    50. int total = words.length();
    51. String subWords = words;
    52. int b = 0;
    53. for (int i = 0; i < total; i++) {
    54. // 统计字符串宽度 并与 预设好的宽度 作比较
    55. if (width <= wordsWidth) {
    56. width += metrics.charWidth(words.charAt(i)); // 获取每个字符的宽度
    57. count++;
    58. } else {
    59. // 画 除了最后一行的前几行
    60. String substring = subWords.substring(0, count);
    61. g2d.drawString(substring, wordsX, wordsY + (b * height));
    62. subWords = subWords.substring(count);
    63. b++;
    64. width = 0;
    65. count = 0;
    66. }
    67. // 画 最后一行字符串
    68. if (i == total - 1) {
    69. g2d.drawString(subWords, wordsX, wordsY + (b * height));
    70. }
    71. }
    72. }
    73. /**
    74. * 将上传的个人头像裁剪成对应的海报比例的头像,并裁剪成圆形
    75. * @param image 读取的头像找
    76. * @param size 宽高大小像素
    77. * @return BufferedImage
    78. */
    79. public static BufferedImage resizeAndClipToCircle(BufferedImage image, int size) {
    80. // 缩小图片
    81. BufferedImage resizedImage = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
    82. Graphics2D g2d = resizedImage.createGraphics();
    83. g2d.drawImage(image, 0, 0, size, size, null);
    84. g2d.dispose();
    85. // 裁剪成圆形
    86. BufferedImage circularImage = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
    87. Graphics2D g2d2 = circularImage.createGraphics();
    88. Ellipse2D.Double ellipse = new Ellipse2D.Double(0, 0, size, size);
    89. g2d2.setClip(ellipse);
    90. g2d2.drawImage(resizedImage, 0, 0, size, size, null);
    91. g2d2.dispose();
    92. return circularImage;
    93. }
    94. /**
    95. * 部分文字 垂直排序时,不满多列,最后一列居中显示
    96. * @param textGraphics Graphics 对象
    97. * @param text 要传入的海报描述
    98. */
    99. public static void drawMyString(Graphics textGraphics, String text,Color color) {
    100. // 每列显示的汉字数量
    101. int columnSize = 7;
    102. // 文字之间的垂直间距
    103. int verticalSpacing = 75;
    104. // 获取字体渲染上下文
    105. FontMetrics fm = textGraphics.getFontMetrics();
    106. // 获取字体的高度
    107. int fontHeight = fm.getHeight();
    108. System.out.println(fontHeight);
    109. // 计算每列的宽度
    110. int columnWidth = fontHeight + verticalSpacing;
    111. // 设置初始位置
    112. int x = 280;
    113. int y = 450;
    114. Font fontFour = new Font(" Source Han Sans CN", Font.BOLD, 100);
    115. textGraphics.setFont(fontFour);
    116. textGraphics.setColor(color);
    117. // 绘制文字
    118. int charCount = 0;
    119. int totalColumns = (int)Math.ceil((double)text.length() / columnSize); // 总列数
    120. int totalRows = Math.min(columnSize, text.length()); // 总行数
    121. int remainingChars = text.length() % columnSize; // 最后一列剩余字符数
    122. for (int columnIndex = 0; columnIndex < totalColumns; columnIndex++) {
    123. for (int rowIndex = 0; rowIndex < totalRows; rowIndex++) {
    124. if (charCount >= text.length()) break;
    125. char ch = text.charAt(charCount);
    126. // 计算当前位置
    127. int cx = x - columnIndex * columnWidth;
    128. int cy = y + rowIndex * fontHeight + rowIndex * verticalSpacing; // 加入垂直偏移量
    129. // 计算当前位置
    130. // int cx = x - columnIndex * columnWidth;
    131. // int cy = y + rowIndex * fontHeight + rowIndex * verticalSpacing + columnIndex ;
    132. // 如果是最后一列并且不满 7 个字符,则需要将剩余字符居中
    133. if (columnIndex == totalColumns - 1 && remainingChars > 0) {
    134. int extraVerticalSpace = (columnSize - remainingChars) * (fontHeight + verticalSpacing) / 2;
    135. cy += extraVerticalSpace;
    136. }
    137. // 绘制文字
    138. textGraphics.drawString(String.valueOf(ch), cx, cy);
    139. charCount++;
    140. }
    141. }
    142. }
    143. /**
    144. * 横向显示 垂直文字
    145. * @param textGraphics Graphics 对象
    146. * @param text 显示的 内容
    147. * @param number 没列显示汉字数量
    148. */
    149. public static void drawString(Graphics textGraphics,String text,int number){
    150. // 每列显示的汉字数量
    151. int columnSize = number;
    152. // 文字之间的垂直间距
    153. int verticalSpacing = 50;
    154. // 获取字体渲染上下文
    155. FontMetrics fm = textGraphics.getFontMetrics();
    156. // 获取字体的高度
    157. int fontHeight = fm.getHeight();
    158. // 计算每列的宽度
    159. int columnWidth = fontHeight + verticalSpacing;
    160. // 设置初始位置
    161. int x = 0;
    162. int y = 0;
    163. switch (number) {
    164. case 3:
    165. x = 1250;
    166. y = 790;
    167. break;
    168. case 10:
    169. x = 1150;
    170. y = 800;
    171. break;
    172. // 可以添加更多的条件分支
    173. default:
    174. // 默认情况下的坐标设置
    175. break;
    176. }
    177. if (number == 10) {
    178. Font fontFour = new Font(text, Font.BOLD, 60);
    179. textGraphics.setFont(fontFour);
    180. textGraphics.setColor(Color.WHITE);
    181. }
    182. // 绘制文字
    183. for (int i = 0; i < text.length(); i++) {
    184. char ch = text.charAt(i);
    185. // 计算当前位置
    186. int cx = x - (i / columnSize) * columnWidth;
    187. int cy = y + (i % columnSize) * fontHeight;
    188. // 绘制文字
    189. textGraphics.drawString(String.valueOf(ch), cx, cy);
    190. }
    191. }
    192. public static void drawCenteredText(Graphics graphics, String text, int oneY, int twoY) {
    193. // 设置字体和颜色
    194. Font font = new Font("宋体", Font.BOLD, 50);
    195. graphics.setFont(font);
    196. graphics.setColor(Color.WHITE);
    197. // 获取字体渲染上下文
    198. FontMetrics fm = graphics.getFontMetrics(font);
    199. // 截取第二行文字的部分
    200. String textTwo = text.substring(text.indexOf("第"));
    201. // 获取第一行文字的宽度
    202. String textOne = text.substring(0, text.indexOf("第"));
    203. // 获取第二行文字的宽度
    204. int textTwoWidth = fm.stringWidth(textTwo);
    205. // 计算第一行文字的起始x坐标,使其水平居中
    206. int oneX = 50;
    207. // 计算第二行文字的起始x坐标,使其水平居中
    208. int twoX = (450 - textTwoWidth) / 2;
    209. // 绘制第一行文字
    210. graphics.drawString(textOne, oneX, oneY);
    211. // 绘制第二行文字
    212. graphics.drawString(textTwo, twoX, twoY);
    213. }
    214. /**
    215. * 在Graphics2D对象上绘制竖排文字
    216. *
    217. * @param textGraphics Graphics2D对象
    218. * @param text 要绘制的文字
    219. */
    220. public static void drawMyLectureString(Graphics textGraphics, String text) {
    221. // 每列显示的汉字数量
    222. int columnSize = 25;
    223. // 文字之间的垂直间距
    224. int verticalSpacing = 1;
    225. // 获取字体渲染上下文
    226. FontMetrics fm = textGraphics.getFontMetrics();
    227. // 获取字体的高度
    228. int fontHeight = fm.getHeight() - 30;
    229. // 计算每列的宽度
    230. int columnWidth = fontHeight + verticalSpacing;
    231. // 设置初始位置
    232. int x = 800;
    233. int y = 190;
    234. Font fontFour = new Font("宋体", Font.BOLD, 40);
    235. textGraphics.setFont(fontFour);
    236. textGraphics.setColor(Color.WHITE);
    237. // 计算总列数
    238. int totalColumns = (int) Math.ceil((double) text.length() / columnSize); // 总列数
    239. // 绘制文字
    240. int charCount = 0;
    241. for (int rowIndex = 0; rowIndex < columnSize; rowIndex++) {
    242. for (int columnIndex = 0; columnIndex < totalColumns; columnIndex++) {
    243. int charIndex = rowIndex * totalColumns + columnIndex;
    244. if (charIndex >= text.length()) break;
    245. char ch = text.charAt(charIndex);
    246. // 计算当前位置
    247. int cx = x - columnIndex * columnWidth;
    248. int cy = y + rowIndex * fontHeight + rowIndex * verticalSpacing; // 加入垂直偏移量
    249. // 绘制文字
    250. textGraphics.drawString(String.valueOf(ch), cx, cy);
    251. }
    252. }
    253. }
    254. /**
    255. * 字体倾斜
    256. */
    257. public static void qinxie(Font fontMonth,Graphics2D graphics){
    258. // 创建 AffineTransform 对象
    259. AffineTransform month = new AffineTransform();
    260. // 设置水平倾斜
    261. month.shear(-0.2, 0); // + 向左 - 向右倾斜
    262. // 应用变换到字体
    263. fontMonth = fontMonth.deriveFont(month);
    264. graphics.setFont(fontMonth);
    265. }
    266. /**
    267. * 文字垂直
    268. */
    269. public static void drawSecondString(Graphics textGraphics, String text, int number) {
    270. // 每列显示的汉字数量
    271. int columnSize = number;
    272. // 文字之间的垂直间距
    273. int verticalSpacing = columnSize == 15 ? -500 : 50;
    274. // 获取字体渲染上下文
    275. FontMetrics fm = textGraphics.getFontMetrics();
    276. // 获取字体的高度
    277. int fontHeight = fm.getHeight();
    278. // 计算每列的宽度
    279. int columnWidth = columnSize == 15 ? 60 : fontHeight + verticalSpacing;
    280. // 设置初始位置
    281. int x = 0;
    282. int y = 0;
    283. switch (number) {
    284. case 3:
    285. x = 1100;
    286. y = 200;
    287. break;
    288. case 10:
    289. x = 1000;
    290. y = 210;
    291. break;
    292. case 15:
    293. x = 800;
    294. y = 210;
    295. break;
    296. // 可以添加更多的条件分支
    297. default:
    298. // 默认情况下的坐标设置
    299. break;
    300. }
    301. if (number == 10) {
    302. Font fontFour = new Font(text, Font.BOLD, 60);
    303. textGraphics.setFont(fontFour);
    304. textGraphics.setColor(Color.WHITE);
    305. } else if (number == 15) {
    306. Font fontFour = new Font(text, Font.BOLD, 40);
    307. textGraphics.setFont(fontFour);
    308. textGraphics.setColor(Color.WHITE);
    309. }
    310. // 绘制文字
    311. for (int i = 0; i < text.length(); i++) {
    312. char ch = text.charAt(i);
    313. // 计算当前位置
    314. int cx = x - (i / columnSize) * columnWidth;
    315. int cy = y + (i % columnSize) * fontHeight;
    316. textGraphics.drawString(String.valueOf(ch), cx, cy);
    317. }
    318. }
    319. }

    业务代码:

    首先新建3个目录用来存放一下固定的背景图片和 临时的文件

     generatePosterTemplate.java
    1. @Override
    2. public Map generatePosterTemplate(PosterTemplateDto template) throws IOException {
    3. // 首先读取文件的路径
    4. String imagesPath = new File("src/main/resources/").getAbsolutePath() + "/static/imagesFirst/";
    5. String imagesPathSecond = new File("src/main/resources/").getAbsolutePath() + "/static/imagesSecond/";
    6. String imagesPathLogo = new File("src/main/resources/").getAbsolutePath() + "/static/imagesLogo/";
    7. String temp = new File("src/main/resources/").getAbsolutePath() + "/static/temp/";
    8. // 创建颜色并设置透明度
    9. Map map = new HashMap<>();
    10. map.put("0", new Color(0, 88, 38));
    11. map.put("1", new Color(40, 50, 112));
    12. map.put("2", new Color(129, 2, 3));
    13. Color color = map.getOrDefault(template.getPosterColor(), new Color(0, 88, 38));
    14. Map fileMap = new HashMap<>();
    15. String fileName = IdUtil.simpleUUID().substring(0, 10) + DateTimeUtils.stringTime();
    16. PosterTemplateDto posterTemplate = "0".equals(template.getId())
    17. ? createPosterTemplate(template) : template;
    18. try {
    19. // 读取背景图片
    20. if ("0".equals(template.getPosterId())) {
    21. // 横向海报
    22. // 读取海报背景图片 BufferedImage backgroundImage = ImageIO.read(new File("D:\\test1\\backImage.png"));
    23. File originalBackFile = new File(StrUtil.isNotBlank(template.getPosterBackImage())
    24. ? getUpdatePhotoPath(imagesPathLogo, template.getPosterBackImage(), 1372, 1978) : imagesPath + "backImage.png");
    25. BufferedImage backgroundImage = ImageIO.read(originalBackFile);
    26. if (StrUtil.isNotBlank(template.getPosterBackImage())) {
    27. // 将保存到本地的图片名称返回给我
    28. fileMap.put("backImage", template.getPosterBackImage());
    29. }
    30. int alpha = 180; // 透明度(取值范围:0 - 255)
    31. // 创建 Graphics2D 对象
    32. Graphics2D g2d = backgroundImage.createGraphics();
    33. // 设置透明度
    34. AlphaComposite alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) alpha / 255);
    35. g2d.setComposite(alphaComposite);
    36. // 设置背景色
    37. g2d.setColor(color);
    38. // 填充整个图片区域
    39. g2d.fillRect(0, 0, backgroundImage.getWidth(), backgroundImage.getHeight());
    40. // 读取要贴的第一张小图片 "D:\\test1\\schoolLogo.png"
    41. File originalFile = new File(imagesPath + "schoolLogo.png");
    42. BufferedImage image = ImageIO.read(originalFile);
    43. // 调用 resizeImageOne 方法进行放大
    44. BufferedImage overlayImage1 = PosterUtil.scaleImage(image, 200, 90);
    45. // 读取要贴的第二张小图片
    46. BufferedImage overlayImage2 = ImageIO.read(new File(StrUtil.isNotBlank(template.getPosterLogo())
    47. ? getUpdatePhotoPath(imagesPathLogo, template.getPosterLogo(), 670, 66) : imagesPath + "collegeLogo.png"));
    48. if (StrUtil.isNotBlank(template.getPosterLogo())) {
    49. // 将保存到本地的图片名称返回给我
    50. fileMap.put("logo", template.getPosterLogo());
    51. }
    52. // 读取要贴的第三张小图片
    53. BufferedImage overlayImage3 = ImageIO.read(new File(imagesPath + "erweima.png"));
    54. // 读取头像图片
    55. BufferedImage avatarImage = ImageIO.read(new File(imagesPath + "touxiang.png"));
    56. // 缩小第五张图片并裁剪成圆形
    57. BufferedImage scaledCircularAvatar = PosterUtil.resizeAndClipToCircle(avatarImage, 400);
    58. // 计算贴图位置
    59. // 在背景图片上贴上第一张图片
    60. createImage(backgroundImage, overlayImage1, 50, 60);
    61. // 在背景图片上贴上第二张图片
    62. createImage(backgroundImage, overlayImage2, 280, 70);
    63. // 在背景图片上贴上第三张图片
    64. createImage(backgroundImage, overlayImage3, 50, 1200);
    65. // 在背景图片上贴上缩小并且裁剪成圆形后的头像图片
    66. createImage(backgroundImage, scaledCircularAvatar, 790, 800);
    67. // 在图片上写字
    68. // TODO 创建Graphics对象
    69. Graphics2D graphics = backgroundImage.createGraphics();
    70. // 设置第一行字体和颜色:添加海报主题文字
    71. Font font = new Font("", Font.BOLD, 50);
    72. graphics.setFont(font);
    73. graphics.setColor(Color.WHITE);
    74. graphics.drawString(posterTemplate.getPosterTopic(), 50, 280);
    75. // 设置第二行的字体和颜色
    76. // TODO 添加第二行文字
    77. Font fontTwo = new Font("", Font.BOLD, 140);
    78. graphics.setFont(fontTwo);
    79. graphics.setColor(Color.WHITE);
    80. PosterUtil.drawWordAndLineFeed(graphics, fontTwo, posterTemplate.getPosterTitle(), 45, 480, 1200);
    81. // TODO 添加第三行文字 具体地址文字
    82. Font fontThree = new Font("", Font.BOLD, 65);
    83. graphics.setFont(fontThree);
    84. graphics.setColor(Color.WHITE);
    85. graphics.drawString(posterTemplate.getPosterAddress(), 43, 1800);
    86. // TODO 添加第四行文字 校区 地址文字
    87. Font fontFour = new Font("", Font.BOLD, 45);
    88. graphics.setFont(fontFour);
    89. graphics.setColor(Color.WHITE);
    90. graphics.drawString(posterTemplate.getPosterSchool(), 48, 1870);
    91. //主讲人
    92. PosterUtil.drawString(graphics, "主讲人", 3);
    93. PosterUtil.drawString(graphics, posterTemplate.getPosterMainSpeaker(), 10);
    94. // TODO 1.18 日期
    95. Font fontDate = new Font("", Font.BOLD, posterTemplate.getPosterMonth().length() == 4 ? 150 : 120);
    96. graphics.setFont(fontDate);
    97. graphics.setColor(Color.WHITE);
    98. graphics.drawString(posterTemplate.getPosterMonth(), 135, 900);
    99. // TODO 14:30 ~ 15:30 (周四)
    100. Font fontMinutes = new Font("", Font.BOLD, 60);
    101. graphics.setFont(fontMinutes);
    102. graphics.setColor(Color.WHITE);
    103. PosterUtil.drawWordAndLineFeed(graphics, fontMinutes, posterTemplate.getPosterDay(), 50, 970, 350);
    104. // TODO 添加海报的文字日期 2024 年份
    105. int yearX = 55; // 文字起始位置 x 坐标
    106. int yearY = 930; // 文字起始位置 y 坐标
    107. Font yearFont = new Font("", Font.BOLD, 50);
    108. // 设置渲染提示以获得更好的质量
    109. graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    110. // 创建一个 AffineTransform 对象,并将其设置为顺时针旋转90度
    111. AffineTransform at = new AffineTransform();
    112. at.setToRotation(Math.PI / 2.0, yearX, yearY);
    113. // 应用 AffineTransform 对象
    114. graphics.setTransform(at);
    115. // 获取字体渲染上下文
    116. FontRenderContext frc = graphics.getFontRenderContext();
    117. // 获取文本宽度
    118. int textWidth = (int) graphics.getFont().getStringBounds(posterTemplate.getPosterYear(), frc).getWidth();
    119. // 调整起始位置,以确保文本在旋转后完全可见
    120. yearX -= textWidth;
    121. graphics.setFont(yearFont);
    122. // 绘制文字
    123. graphics.drawString(posterTemplate.getPosterYear(), yearX, yearY);
    124. // 释放Graphics对象
    125. graphics.dispose();
    126. InsertMinio(temp, backgroundImage, fileName, fileMap);
    127. } else {
    128. // 竖向 排版海报
    129. // 读取背景图片 getUpdatePhotoPath(imagesPathLogo, template.getPosterLogo(), 670, 66)
    130. File originalBackFile = new File(StrUtil.isNotBlank(template.getPosterBackImage())
    131. ? getUpdatePhotoPath(imagesPathLogo, template.getPosterBackImage(), 1472, 2102) : imagesPathSecond + "backImageSecondTwo.png");
    132. if (StrUtil.isNotBlank(template.getPosterBackImage())) {
    133. // 将保存到本地的图片名称返回给我
    134. fileMap.put("backImage", template.getPosterBackImage());
    135. }
    136. BufferedImage backgroundImage = ImageIO.read(originalBackFile);
    137. // 创建颜色并设置透明度
    138. int alpha = 240; // 透明度(取值范围:0 - 255)
    139. // 创建 Graphics2D 对象
    140. Graphics2D g2d = backgroundImage.createGraphics();
    141. // 设置透明度
    142. AlphaComposite alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) alpha / 255);
    143. g2d.setComposite(alphaComposite);
    144. // 设置左上角背景色
    145. g2d.setColor(color);
    146. // 填充整个图片区域
    147. g2d.fillRect(0, 0, 450, 230);
    148. // 设置背景色
    149. Graphics2D g3d = backgroundImage.createGraphics();
    150. // 设置最下面的背景色
    151. g3d.setColor(color);
    152. // 填充整个图片区域
    153. g3d.fillRect(0, 1980, backgroundImage.getWidth(), 230);
    154. // 读取要贴的第一张小图片
    155. File originalFile = new File(imagesPathSecond + "schoolImageLogo.png");
    156. BufferedImage image = ImageIO.read(originalFile);
    157. // 调用 resizeImageOne 方法进行放大
    158. int width = 280;
    159. int height = 230;
    160. BufferedImage overlayImage1 = PosterUtil.scaleImage(image, width, height);
    161. // 读取要贴的第二张小图片
    162. BufferedImage overlayImage2 = ImageIO.read(new File(imagesPathSecond + "back2.png"));
    163. // 读取要贴的第三张小图片
    164. Map hashMap = new HashMap<>();
    165. hashMap.put("0", "green.png");
    166. hashMap.put("1", "blue.png");
    167. hashMap.put("2", "red.png");
    168. BufferedImage overlayImage3 = ImageIO.read(new File(imagesPathSecond + hashMap.getOrDefault(template.getPosterColor(), "green.png")));
    169. BufferedImage overlayImage4 = ImageIO.read(new File(imagesPathSecond + hashMap.getOrDefault(template.getPosterColor(), "green.png")));
    170. // 读取头像图片
    171. BufferedImage avatarImage = ImageIO.read(new File(imagesPathSecond + "touxiang.png"));
    172. // 读取二维码
    173. File erweima = new File(imagesPathSecond + "erweima.png");
    174. BufferedImage erweimalogo = ImageIO.read(erweima);
    175. BufferedImage erweimaLogo = PosterUtil.scaleImage(erweimalogo, 200, 200);
    176. // 缩小第六张图片并裁剪成圆形
    177. BufferedImage scaledCircularAvatar = PosterUtil.resizeAndClipToCircle(avatarImage, 400);
    178. // 读取学校logo
    179. File schoolLogo = new File(imagesPathSecond + "schoolLogo.png");
    180. BufferedImage logo = ImageIO.read(schoolLogo);
    181. // 调用 resizeImageOne 方法进行放大
    182. BufferedImage imageLogo = PosterUtil.scaleImage(logo, 200, 100);
    183. // 读取学院logo getUpdatePhotoPath(imagesPathSecond, template.getPosterLogo(), 1472, 2102) 440 118
    184. // 读取学院logo
    185. BufferedImage images;
    186. if (StrUtil.isNotBlank(template.getPosterLogo())) {
    187. File collegeLogo = new File(getUpdatePhotoPath(imagesPathLogo, template.getPosterLogo(), 200, 100));
    188. if (StrUtil.isNotBlank(template.getPosterLogo())) {
    189. // 将保存到本地的图片名称返回给我
    190. fileMap.put("logo", template.getPosterLogo());
    191. }
    192. images = ImageIO.read(collegeLogo);
    193. } else {
    194. File collegeLogos = new File(imagesPathSecond + "collegelogos.png");
    195. BufferedImage collegeImage = ImageIO.read(collegeLogos);
    196. // 调用 resizeImageOne 方法进行放大
    197. images = PosterUtil.scaleImage(collegeImage, 200, 100);
    198. }
    199. // 读取白色圆圈
    200. File yuan = new File(imagesPathSecond + "yuan.png");
    201. BufferedImage yuanImage = ImageIO.read(yuan);
    202. // 调用 resizeImageOne 方法进行放大
    203. BufferedImage imagesYuan = PosterUtil.scaleImage(yuanImage, 450, 450);
    204. // 计算贴图位置 X,Y
    205. // 在背景图片上贴上第一张图片
    206. createImage(backgroundImage, overlayImage1, -5, 5);
    207. // 在背景图片上贴上第二张图片
    208. createImage(backgroundImage, overlayImage2, 0, 230);
    209. // 在背景图片上贴上第三张图片
    210. createImage(backgroundImage, overlayImage3, 50, 1400);
    211. //在背景图片上贴上第四张图片
    212. createImage(backgroundImage, overlayImage4, 50, 1580);
    213. // 在背景图片上贴上缩小并且裁剪成圆形后的头像图片
    214. createImage(backgroundImage, scaledCircularAvatar, 950, 370);
    215. //在背景图片上贴上第六张图片
    216. createImage(backgroundImage, erweimaLogo, 50, 1750);
    217. //在背景图片上贴上第7张图片
    218. createImage(backgroundImage, imageLogo, 900, 1990);
    219. //在背景图片上贴上第8张图片
    220. createImage(backgroundImage, images, 1200, 1990);
    221. //在背景图片上贴上第9张图片
    222. createImage(backgroundImage, imagesYuan, 925, 345);
    223. // 保存合成后的背景图片
    224. // TODO 标题
    225. Graphics2D graphics = backgroundImage.createGraphics();
    226. // TODO 添加第二行文字
    227. Font fontTwo = new Font("", Font.BOLD, 50);
    228. graphics.setFont(fontTwo);
    229. graphics.setColor(Color.WHITE);
    230. String posterTopic = posterTemplate.getPosterTopic();
    231. // drawWordAndLineFeed(graphics, fontTwo, textTwo, twoX, twoY, 320);
    232. PosterUtil.drawCenteredText(graphics, posterTopic, 100, 180);
    233. // TODO 主讲人信息
    234. PosterUtil.drawSecondString(graphics, "主讲人", 3);
    235. PosterUtil.drawSecondString(graphics, posterTemplate.getPosterMainSpeaker(), 10);
    236. // TODO 主讲人详细介绍信息
    237. PosterUtil.drawMyLectureString(graphics, posterTemplate.getPosterPeopleDetail());
    238. // 设置字体和颜色
    239. Font font = new Font("宋体", Font.PLAIN, 50);
    240. g2d.setFont(font);
    241. g2d.setColor(Color.BLACK);
    242. // 绘制竖排文字
    243. // TODO 讲座名称
    244. PosterUtil.drawMyString(graphics, posterTemplate.getPosterTitle(), color);
    245. //TODO 时间 2024
    246. Font fontYear = new Font("Source Han Sans CN", Font.PLAIN, 40);
    247. graphics.setColor(Color.BLACK);
    248. PosterUtil.qinxie(fontYear, graphics); //字体倾斜
    249. graphics.drawString(posterTemplate.getPosterYear(), 50, 1480);
    250. //TODO 时间 月份 10.23 1.23
    251. Font fontMonth = new Font("Source Han Sans CN", Font.BOLD, 75);
    252. graphics.setColor(Color.BLACK);
    253. PosterUtil.qinxie(fontMonth, graphics);
    254. String posterMonth = posterTemplate.getPosterMonth();
    255. graphics.drawString(posterMonth, posterMonth.length() == 5 ? 210 : 250, 1480);
    256. //TODO 具体时间 20:12 ~ 11:00 周五
    257. Font fontDate = new Font("Source Han Sans CN", Font.PLAIN, 40);
    258. graphics.setColor(Color.BLACK);
    259. PosterUtil.qinxie(fontDate, graphics);
    260. graphics.drawString(posterTemplate.getPosterDay(), 50, 1550);
    261. // TODO 具体举办地点
    262. Font fontDd = new Font("", Font.BOLD, 35);
    263. graphics.setFont(fontDd);
    264. graphics.setColor(Color.BLACK);
    265. PosterUtil.qinxie(fontDd, graphics);
    266. PosterUtil.drawWordAndLineFeed(graphics, fontDd, posterTemplate.getPosterAddress(), 50, 1630, 330);
    267. // TODO 主持人
    268. Font zcr = new Font(" Source Han Sans CN", Font.PLAIN, 40);
    269. graphics.setColor(Color.white);
    270. graphics.setFont(zcr);
    271. String zcrName = "主持人: " + posterTemplate.getMainPeople();
    272. graphics.drawString(zcrName, 50, 2060);
    273. // 设置抗锯齿
    274. graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    275. graphics.dispose();
    276. return InsertMinio(temp, backgroundImage, fileName, fileMap);
    277. }
    278. } catch (Exception e) {
    279. throw new BusinessException(e.getMessage());
    280. }
    281. return fileMap;
    282. }
    283. public void createImage(BufferedImage backgroundImage, BufferedImage imagesYuan, int x, int y) {
    284. Graphics2D graphics9 = backgroundImage.createGraphics();
    285. graphics9.drawImage(imagesYuan, x, y, null);
    286. graphics9.dispose();
    287. }
    288. public Map InsertMinio(String temp, BufferedImage backgroundImage, String fileName, Map fileMap) throws Exception {
    289. File file = new File(temp);
    290. ImageIO.write(backgroundImage, "PNG", file);
    291. // 创建 FileInputStream 流
    292. FileInputStream inputStream = new FileInputStream(file);
    293. // 现在你可以使用 inputStream 进行操作
    294. minioUtil.uploadInputStream("mpbucket", "sjs/wdjz/hbgl" + SLASH_SUFFIX + fileName + ".png", inputStream);
    295. InputStream download = minioUtil.download("mpbucket", "sjs/wdjz/hbgl" + SLASH_SUFFIX + fileName + ".png");
    296. // 将删除的图片文件保存到本地
    297. String name = fileName + ".png";
    298. byte[] bytes = IOUtils.toByteArray(download);
    299. String encoded = Base64.getEncoder().encodeToString(bytes);
    300. fileMap.put("posterTemplate", name);
    301. fileMap.put("code", BASE_64 + encoded);
    302. // 记得关闭流
    303. inputStream.close();
    304. // 删除临时文件
    305. file.delete();
    306. return fileMap;
    307. }
    308. /**
    309. * 修改图片尺寸
    310. *
    311. * @return 新的海报地址
    312. */
    313. public String getUpdatePhotoPath(String path, String fileName, int width, int height) throws Exception {
    314. File originalFile = new File(path + fileName);
    315. byte[] originalImageData = Files.readAllBytes(originalFile.toPath());
    316. byte[] resizedImageData = FileUtil.resizeImage(originalImageData, width, height);
    317. // 将 byte[] 转换为 ByteArrayInputStream
    318. ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(resizedImageData);
    319. // 创建临时文件(如果需要)
    320. File tempFile = File.createTempFile("temp", ".tmp");
    321. // 将 ByteArrayInputStream 中的数据写入临时文件
    322. IOUtils.copy(byteArrayInputStream, Files.newOutputStream(tempFile.toPath()));
    323. // 使用临时文件创建 FileInputStream
    324. FileInputStream fileInputStream = new FileInputStream(tempFile);
    325. // 现在你可以使用 fileInputStream 对象进行操作
    326. minioUtil.uploadInputStream("mpbucket", "sjs/wdjz/hbgl" + SLASH_SUFFIX + fileName, fileInputStream);
    327. InputStream download = minioUtil.download("mpbucket", "sjs/wdjz/hbgl" + SLASH_SUFFIX + fileName);
    328. // 将保存的logo 图片文件保存到本地
    329. FileUtil.convertInputStreamToFile(download, fileName);
    330. // 删除 minio中的图片
    331. minioUtil.deleteFile("mpbucket", fileName);
    332. // 关闭流和删除临时文件
    333. fileInputStream.close();
    334. tempFile.delete();
    335. return path + fileName;
    336. }
    337. //写死两个模板所需要的参数
    338. public PosterTemplateDto createPosterTemplate(PosterTemplateDto oldDto) {
    339. if ("0".equals(oldDto.getPosterId())){
    340. oldDto.setPosterTopic("物理与天文学院学术报告 第359期");
    341. oldDto.setPosterTitle("MIMO天线及其空口测试技术");
    342. oldDto.setPosterYear("2024");
    343. oldDto.setPosterMonth("10.18");
    344. oldDto.setPosterDay("14:30 ~ 15:30(周四)");
    345. oldDto.setPosterAddress("电信楼101讲学厅");
    346. oldDto.setPosterSchool("中山大学(东校园)");
    347. oldDto.setPosterMainSpeaker("欧阳上官");
    348. } else {
    349. oldDto.setPosterTopic("逸仙生命讲坛人第五十九讲");
    350. oldDto.setPosterTitle("时空相分离调控的职务细胞信号转导");
    351. oldDto.setPosterYear("2024");
    352. oldDto.setPosterMonth("1.23");
    353. oldDto.setPosterDay("20:12 ~ 11:00 周五");
    354. oldDto.setPosterAddress("中山大学(东校园)-电信楼101讲学厅");
    355. oldDto.setPosterMainSpeaker("欧阳上官");
    356. oldDto.setPosterPeopleDetail("我啥事打卡机大街上的情况为其较为气温气温其味无穷我的撒登记卡水电气网红,诶打算近期我后端数据啊按地区为背景墙网格袋安山东划算的挥洒的企鹅企鹅群。");
    357. oldDto.setMainPeople("李剑锋");
    358. }
    359. return oldDto;
    360. }

  • 相关阅读:
    java基于springboot+vue+elementui的饭店点菜外卖平台 前后端分离
    解决跨域的三种方案
    Python和Java哪个更好找工作?
    024. 解压报文[200 分]
    Leetcode137. 某一个数字出现一次,其余数字出现3次
    【剑指 Offer 66. 构建乘积数组】(很巧妙)
    JVM详解【三】JVM的内存结构
    行车记录仪E-mark认证要如何办理?
    Spring Security OAuth实现GitHub快捷登录
    一文读懂!异常检测全攻略!从统计方法到机器学习 ⛵
  • 原文地址:https://blog.csdn.net/XikYu/article/details/138194564