先看下效果图
实现思路
PS 扣一个底层的模板图片
读取底层模板图片
绘制产品主图, 图片加圆角处理, 图片抗锯齿处理
绘制产品标题, 计算内容宽度并换行, 文字坐标计算, 字体抗锯齿处理
绘制产品价格
生成二维码图片, 二维码白边处理
PS 扣一个底层的模板图片
这个环节我们主要用PS抠图并记录下每个元素的坐标和大小
/** * 产品图片区域 */private Rectangle imageArea = new Rectangle(64, 64, 620, 620);/** * 标题区域 */private Rectangle titleArea = new Rectangle(86, 712, 300, 64);/** * 价格区域 */private Rectangle priceArea = new Rectangle(552, 720, 118, 43);/** * 二维码区域 */private Rectangle qrcodeArea = new Rectangle(87, 830, 100, 100);
读取底层模板图片
// 读取模版图片final BufferedImage cardImg = ImageIO.read(ClassLoader.getSystemResource(templatePath));final Graphics2D g = cardImg.createGraphics();
绘制产品主图,加圆角,抗锯齿
// 绘制封面BufferedImage productImg = ImageIO.read(product.getProductImageUrl());// 切圆角productImg = setRadius(productImg, 60);// 绘制g.drawImage(productImg, imageArea.x, imageArea.y, imageArea.width, imageArea.height, null); public static BufferedImage setRadius(BufferedImage srcImage, int radius) { int w = srcImage.getWidth(); int h = srcImage.getHeight(); BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = output.createGraphics(); g2.setComposite(AlphaComposite.SrcOut); // 抗锯齿 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(new Color(0, 0, 0)); g2.setBackground(Color.black); g2.setPaint(new Color(0, 0, 0)); g2.fill(new RoundRectangle2D.Float(0, 0, w, h, radius, radius)); g2.setComposite(AlphaComposite.SrcAtop); g2.drawImage(srcImage, 0, 0, null); g2.dispose(); return output;}
绘制产品标题,换行,坐标计算,抗锯齿
在使用drawString 绘制文本内容的时候如果, 你需要填写坐标 x, y 如果你直接把ps上面的坐标用在代码里面的话你会发现位置根本就不对, 那是为什么呢?
字体的高由个元素组成:ascent , descent
drawString中用的y坐标是指baseline的y坐标,即字体所在矩形的左上角y坐标+ascent
// 开启文本抗锯齿g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); private void drawTitle(ProductCard product, Graphics2D g) { g.setColor(Color.decode("#666666")); g.setFont(new Font("宋体", Font.BOLD, 28)); int ascent = g.getFontMetrics(g.getFont()).getAscent(); final Rectangle2D titleBounds = g.getFontMetrics().getStringBounds(product.getTitle(), g); // 一行最多 10 个字符 final String title = product.getTitle(); final int rowMaxWidth = titleArea.width; if (titleBounds.getWidth() > rowMaxWidth) { final char[] chars = product.getTitle().toCharArray(); for (int i = 0, w = 0, start = 0; i < chars.length; i++) { w += g.getFontMetrics().charWidth(chars[i]); if (w >= rowMaxWidth) { if (start == 0) { // 写第一行 int y = titleArea.y + ascent; g.drawString(title.substring(0, i), titleArea.x, y); start = i; w = 0; } else if (start > 0) { // 写第二行 String part2 = title.substring(start, i); // 判断是否需要追加点点点 if (titleBounds.getWidth() > rowMaxWidth * 2) part2 += "..."; // 绘制 int padding = 5; int y = titleArea.y + ascent + titleArea.height / 2 + padding; g.drawString(part2, titleArea.x, y); break; } } } } else { g.drawString(title, titleArea.x, titleArea.y + ascent); }}
绘制产品价格
标题会了价格就很简单了, 这里有个遗留问题就是:如果价格超过4位数会出现超出图片的问题, 大家可以修改模板或者调整字号来解决
g.setFont(new Font("Arial", Font.BOLD, 48));final FontMetrics fontMetrics = g.getFontMetrics(g.getFont());g.setColor(Color.decode("#ff4f13"));g.drawString(product.getPrice(), priceArea.x, priceArea.y + fontMetrics.getAscent());
生成二维码图片, 删除白边
生成二维码我们需要依赖一个第三方依赖
<dependency> <groupId>com.google.zxinggroupId> <artifactId>coreartifactId> <version>3.3.0version>dependency><dependency> <groupId>com.google.zxinggroupId> <artifactId>javaseartifactId> <version>3.3.0version>dependency>
生成二维码
// 生成二维码图片QRCodeWriter qrCodeWriter = new QRCodeWriter();BitMatrix bitMatrix = qrCodeWriter.encode(product.getProductUrl(), BarcodeFormat.QR_CODE, qrcodeArea.width, qrcodeArea.height); 删除白边public static BitMatrix deleteWhite(BitMatrix matrix) { int[] rec = matrix.getEnclosingRectangle(); int resWidth = rec[2] + 1; int resHeight = rec[3] + 1; BitMatrix resMatrix = new BitMatrix(resWidth, resHeight); resMatrix.clear(); for (int i = 0; i < resWidth; i++) { for (int j = 0; j < resHeight; j++) { if (matrix.get(i + rec[0], j + rec[1])) resMatrix.set(i, j); } } return resMatrix;}