以下是我在实际工作的项目中,想要实现从后端生成海报的功能,本来项目使用golang开发的,但是对于golang的画图不是很熟悉,所以想到使用Java,但是对于Java的画图其实也不熟悉,但是通过在网上找资料,也算是实现了,本文介绍一下如何实现的步骤
用来处理图像压缩的好用工具
thumbnailator
处理生成二维码的工具
zxing
<dependency> <groupId>net.coobird</groupId> <artifactId>thumbnailator</artifactId> <version>0.4.14</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <!--打包的时候可以不用包进去,别的设施会提供。事实上该依赖理论上可以参与编译,测试,运行等周期。 相当于compile,但是打包阶段做了exclude操作--> <scope>provided</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>3.4.1</version> </dependency>
以下是主要代码
public static BufferedImage generate(int type, String msgTitle, String link) throws Exception { BufferedImage bimg = null; try { bimg = ImageIO.read(Objects.requireNonNull(UltimateImgGenerator.class.getClassLoader().getResourceAsStream("img.png"))); System.out.println(bimg.getHeight()); System.out.println(bimg.getWidth()); } catch (IOException e1) { e1.printStackTrace(); } Graphics2D g2d = (Graphics2D) bimg.getGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2d.setColor(Color.red); //矩形区域颜色 Msg[] msgs = { new Msg(new Color(0, 95, 249), "事件提醒", 1), new Msg(new Color(252, 193, 32), "天气预报", 2), new Msg(new Color(43, 197, 154), "星座运势", 3), new Msg(new Color(249, 94, 100), "灾害预警", 4), new Msg(new Color(102, 80, 252), "消息推送", 5), }; Msg msg = msgs[type - 1]; //设置圆角矩形的颜色 Color color = msg.color; g2d.setColor(color); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); //画圆角曲形 int width = 20/*左边距离*/ + msg.title.length() * 30/*字体总宽度,每个字30*/ + 20/*右边距离*/; g2d.fillRoundRect(60, 366, width, 54, 6, 6);//涂一个圆角矩形块 Font font = new Font("Source Han Sans CN Bold", 0, 30); g2d.setFont(font); FontMetrics metrics = g2d.getFontMetrics(font); //为了使标题能够在所画的矩形区域中居中显示 开始 // 字符打印实际需要的宽度,可能小于矩形的宽,也可能大于矩形的宽 int stringWidth = metrics.stringWidth(msg.title); int rectWidth = 20 * 2 + stringWidth; int rectX = 60; int rectY = 366; int rectHeight = 54; // 字符打印的x坐标 = 矩形的起点坐标 + 字符居中打印情况下字符的边与矩形边的距离(可能是正,也可能是负数) int x = rectX + (rectWidth - stringWidth) / 2; // 字符打印的y坐标(基线的y坐标)= 矩形的起点y坐标 + 字符居中打印下字符的上面与矩形上面距离 + 字符上面到基线的距离 int y = rectY + (rectHeight - metrics.getHeight()) / 2 + metrics.getAscent(); // 矩形区域中字体的颜色 color = new Color(255, 255, 255); g2d.setColor(color); g2d.drawString(msg.title, x, y); //为了使标题能够在所画的矩形区域中居中显示 结束 g2d.setColor(Color.black); font = new Font("Source Han Sans CN Bold", 0, 102); g2d.setFont(font); g2d.setStroke(new BasicStroke(15)); metrics = FontDesignMetrics.getMetrics(font); int currentWidth = 0; int lineNum = 0; int i = 0; StringBuilder currentLineStr = new StringBuilder(); //为了使每一行的字符长度不超过规定的长度 do { if (i > msgTitle.length() - 1) { //跳出之前检查是否花完毕,如果有,则画完再退出 if (currentLineStr.length() > 0) { g2d.drawString(currentLineStr.toString(), 60, 620 + 122 * lineNum); } break; } char c = msgTitle.charAt(i); // log.info("c:{}",c); currentWidth += metrics.charWidth(c); currentLineStr.append(c); boolean flag = false; if (lineNum == 2) { //如果是最后一行,尝试着每次都增加一个省略号,如果加上省略号已经大于840,那么就剪去最后一个字符然后写入并退出 if (currentWidth + 75 > 840) { currentLineStr.deleteCharAt(currentLineStr.length() - 1); // 汉字:[0x4e00,0x9fa5] 或 十进制[19968,40869] // 数字:[0x30,0x39] 或 十进制[48, 57] // 小写字母:[0x61,0x7a] 或 十进制[97, 122] // 大写字母:[0x41,0x5a] 或 十进制[65, 90] //此处使为了不让最后一个字符使特殊字符 char last = currentLineStr.charAt(currentLineStr.length() - 1); //如果所有文字的最后一个字符绝对不能是特殊字符,则用while while (!(last >= 48 && last <= 57 || (last >= 65 && last <= 90) || (last >= 97 && last <= 122) || (last >= 19968 && last <= 40869))) { if (currentLineStr.length() >= 1) { currentLineStr.deleteCharAt(currentLineStr.length() - 1); if (currentLineStr.length() >= 1) { last = currentLineStr.charAt(currentLineStr.length() - 1); } } else { break; } } //如果所有文字的只处理一次最后一个字符绝对不能是特殊字符,则用if // if (!(last >= 48 && last <= 57 || (last >= 65 && last <= 90) || (last >= 97 && last <= 122) || (last >= 19968 && last <= 40869))) { // if (currentLineStr.length() >= 1) { // currentLineStr.deleteCharAt(currentLineStr.length() - 1); // if (currentLineStr.length() >= 1) { // last = currentLineStr.charAt(currentLineStr.length() - 1); // } // } // } currentLineStr.append("..."); g2d.drawString(currentLineStr.toString(), 60, 620 + 122 * lineNum); break; } } if (currentWidth <= 840) { //如果当前长度小于840,那么尝试着加上后面一个字符,如果加上之后,大于840,那么此时就应该写入 if (i + 1 < msgTitle.length() - 1) { int tmpWidth = currentWidth + metrics.charWidth(msgTitle.charAt(i + 1)); if (tmpWidth > 840) { flag = true; } } } else { flag = true; } if (flag) { g2d.drawString(currentLineStr.toString(), 60, 620 + 122 * lineNum); currentLineStr = new StringBuilder(); lineNum++; currentWidth = 0; } i++; } while (lineNum <= 2); QRCodeWriter qrCodeWriter = new QRCodeWriter(); BitMatrix bitMatrix = qrCodeWriter.encode(link, BarcodeFormat.QR_CODE, 200, 200); System.out.println("-------------"); System.out.println(Arrays.toString(bitMatrix.getBottomRightOnBit())); System.out.println(Arrays.toString(bitMatrix.getTopLeftOnBit())); System.out.println("-------------"); // Path path = FileSystems.getDefault().getPath(filePath); // MatrixToImageWriter.writeToPath(bitMatrix, "PNG", path); BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix); //此处动态的获取了二维码在图片中的真实的位置,然后把它剪切下来, // 然后按照需要的尺寸进行了缩放,缩放之后才把这个缩放得到的图片粘贴到了大的图片上 BufferedImage simage = bufferedImage.getSubimage(bitMatrix.getTopLeftOnBit()[0], bitMatrix.getTopLeftOnBit()[1], bitMatrix.getBottomRightOnBit()[0] - bitMatrix.getTopLeftOnBit()[0], bitMatrix.getBottomRightOnBit()[1] - bitMatrix.getTopLeftOnBit()[1]); //缩放图片 BufferedImage image = new BufferedImage(126, 126, BufferedImage.TYPE_INT_RGB); Graphics2D graphics = image.createGraphics(); Image img = simage.getScaledInstance(126, 126, Image.SCALE_DEFAULT); graphics.drawImage(img, 0, 0, null); // //一定要释放资源 // graphics.dispose(); g2d.drawImage(image, null, 774, 1100); return bimg; }
使用的是springboot框架,在controller里面调用
@RequestMapping(value = "/generate2", produces = MediaType.IMAGE_PNG_VALUE) @ResponseBody public byte[] generate2 (@RequestParam(name = "type") int type, @RequestParam(name = "link") String link, @RequestParam(name = "msg") String title, @RequestParam(name = "id") String id) { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); String[] fontFamilies = ge.getAvailableFontFamilyNames(); for (String s : fontFamilies) { log.info("字体:{}",s); } if (type < 1 || type > 5) { return new Result(500, "type只能是1-5的数字", null).toString().getBytes(StandardCharsets.UTF_8); } log.info("type:{},link:{},msg:{},id:{}", type, link, title, id); Pattern pattern = Pattern.compile("^[0-9a-zA-Z-$]{6,32}$"); Matcher matcher = pattern.matcher(id); if (!matcher.matches()) { return new Result(501, "id只能是6-32位的(数字、字母、中横线、$)的组合", null).toString().getBytes(StandardCharsets.UTF_8); } try { BufferedImage bimg = UltimateImgGenerator.generate(type, title, link); return bufferedImageToByteArray(bimg); } catch (Exception e) { return new Result(502, "生成图片异常", null).toString().getBytes(StandardCharsets.UTF_8); } } public static InputStream bufferedImageToInputStream(BufferedImage image) { ByteArrayOutputStream os = new ByteArrayOutputStream(); try { ImageIO.write(image, "png", os); InputStream input = new ByteArrayInputStream(os.toByteArray()); return input; } catch (IOException e) { log.error("提示:", e); } return null; } public static byte[] bufferedImageToByteArray(BufferedImage image) { ByteArrayOutputStream os = new ByteArrayOutputStream(); try { ImageIO.write(image, "png", os); return os.toByteArray(); } catch (IOException e) { log.error("提示:", e); } return null; }
启动本地服务地址https://localhost:8080/generate2?id=wuElsjkd&type=5&msg=聚合云推是用来帮助用户完成日常事件提醒的工具&link=http://tui.juhe.cn