一、需求背景
我们经常在多终端应用开发中会遇到这样的需求:用户在浏览商品时觉得不错,希望分享给朋友。此时终端(安卓、苹果、H5等)生成一张精美的商品海报,通过微信或者其他途径分享给他人。也可能会遇到需求:制作个人名片打印出来或者分享给他人。效果大概是这样的:
也可能是这样
(上面的图都是我从网上临时找的,只做参考学习,避免广告已经做了部分处理。)
刚开始设计这个需求的时候,我们是在前端来完成的,即由安卓、IOS、H5、微信几个端的开发者自主实现功能。各端完成开发后发布到全网,很快就发现一些用户机型上出现了兼容性的问题(C端安卓机型有很多版本)。用户手机安卓版本从5.N到最新的版本、从ip6到最新款,最大跨度超10年,这种兼容性问题对我们来说无法预料。曾尝试使用的dom-to-image和html2canvas效果都不太理想,解决了白屏、跨域问题又遇到低版本不支持标签的问题…。一通折腾,最后我们决定在服务端将海报图片生成好,在终端直接显示图片。
二、用JAVA生成海报
从上面的三张海报,我选《美女》图,作为今天演示效果。(在做此演示时并没有使用之前项目上的图,我也是首次照着图在开发)。
首先将海报分别为3类,背景图、其他图、文字、二维码。
如: 1-底图纯白,2-商品介绍图,3-头像 素材,4-“严选”字样的图素材,5-带色的二维码,6-标题、价格等文字,实际开发中根据视觉和美工给的图即可。
先上我完成的最终效果:
简单做一下,大概和视觉图差不多了, 虽然有点糙但足以表达到效果。
2.1 Easy Poster 易海报生成器
在开发海报过程中,我也找了不少资料,花费了较多时间,后来发现了Easy Poster的设计还是不错的。所以在开发结束以后,我对代码进行了重新整理完善:修复了一些问题,并增加了二维码功能,成为开箱即用的海报生成工具。
接下来,我将介绍一下如何用来快速的制作海报。
创建一个新的海报,只需要3步即可完成
修改pom.xml添加依赖和打包过滤字体库。
创建海报对象及配置相关属性。
传入参数,生成海报
2.2修改pom.xml添加依赖和打包过滤字体库。
pom.xml
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<lombok.version>1.16.18</lombok.version>
<hutool-all.version>5.7.19</hutool-all.version>
<zxing.version>3.4.1</zxing.version>
<testng.version>6.8.8</testng.version>
<slf4j.version>1.7.13</slf4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- 二维码生成 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>${zxing.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool-all.version}</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- SpringBoot项目的maven项目引用的字体库font经过maven的filter,会破坏font文件的二进制文件格式 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>ttf</nonFilteredFileExtension>
<nonFilteredFileExtension>woff</nonFilteredFileExtension>
<nonFilteredFileExtension>woff2</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
</plugins>
</build>
*注意:我们海报用到了字体作为资源文件引入,在经过maven的filter,会破坏font文件的二进制文件格式 导致打包异常,此处要一定要注意。
2.3 创建海报对象及配置相关属性
CommodityPosterPojo.java
package com.naiqing.poster.demo;
import com.quaint.poster.annotation.PosterBackground;
import com.quaint.poster.annotation.PosterBarCodeCss;
import com.quaint.poster.annotation.PosterFontCss;
import com.quaint.poster.annotation.PosterImageCss;
import com.quaint.poster.core.abst.AbstractDefaultPoster;
import com.quaint.poster.core.dto.PosterBarcode;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Tolerate;
import java.awt.*;
import java.awt.image.BufferedImage;
/**
* 〈商品分享海报元素及相关参数定义〉<br>
*
* @author naiqing
* @Date 2022/1/21
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Builder
public class CommodityPoster2Pojo extends AbstractDefaultPoster {
/**
* 背景图
*/
@PosterBackground(width = 540, height = 868)
private BufferedImage backgroundImage;
/**
* 商品图
*/
@PosterImageCss(position = {0, 0}, width = 540, height = 614)
private BufferedImage commodityImage;
/**
* 严选图标
*/
@PosterImageCss(position = {16, 625}, width = 50, height = 28)
private BufferedImage yanxuanImage;
/**
* 商品标题
*/
@PosterFontCss(position = {16, 625}, size = 22, canNewLine = {1, 482, 2}, style = Font.BOLD)
private String commodityTitle;
/**
* 特权价
*/
@PosterFontCss(position = {16, 706}, size = 20, color = {247,23,61})
private String vipPrice;
/**
* 原价
*/
@PosterFontCss(position = {16, 742}, size = 15, color = {51, 51, 51}, delLine = true)
private String oldPrice;
/**
* 头像
*/
@PosterImageCss(position = {10, 780}, width = 55, height = 55, circle = true)
private BufferedImage headImage;
/**
* 昵称
*/
@PosterFontCss(position = {77, 788}, color = {85,85,85}, name = "腾祥爱情体细简", style = Font.BOLD)
private String nickName;
/**
* 昵称下的描述
*/
@PosterFontCss(position = {75, 813}, size = 15, color = {104,104,104}, name = "腾祥爱情体细简")
private String nickDesc;
/**
* 二维码
*/
@PosterBarCodeCss(position = {376, 695}, width = 130, height = 130, foreColor = {17,125,124})
private PosterBarcode qrCode;
/**
* 二维码字
*/
@PosterFontCss(position = {386, 833}, size = 20, color = {104,104,104}, center = true)
private String qrCodeText;
@Tolerate
public CommodityPoster2Pojo() {
}
}
此处定义了海报全部的元素,通过注解:背景图片@PosterBackground、其他图片@PosterImageCss、字体
@PosterFontCss和二维码@PosterBarCodeCss,来配置位置、宽度等参数(更多参数可通过源码了解)。
@PosterBarCodeCss 二维码注解,只适用类PosterBarcodeDto
@PosterBackground 背景图片注解、@PosterImageCss,只适用类BufferedImage
@PosterFontCss文字注解,只适用类String
2.4如何快速得到x\y坐标和width\heigth尺寸
正常来说,视觉稿给到我们开发的时候,页面上都是可以快速得到每个元素的位置和尺寸,但此处因为是从网上找到整图,所以只能自己动手了。
此处我是通过ps打开图片,打开”窗口-信息“,可以查看到鼠标的坐标及圈选的尺寸,如下图可以快速得到各元素的位置:
2.5 传入参数,生成海报
1) 加载字体库
在此调用生成海报之前,我们要将用到的字体库放到项目中,比如说我这里用到了2个字体库:resources\fonts\simkai.ttf和resources\fonts\TTAiQingXiJ_0.ttf。
这里我要特别说明:如果不加载字体库在windows下也是可以直接使用的(会默认使用宋体),但是在将服务发布到服务器下Linux时所有中文都会乱码,因为Linux下并没有相应字体库。
2)生成海报
我用一个单元测试来实现海报生成调用示范,传入商品名称相关信息
package com.naiqing.poster.demo;
import com.quaint.poster.core.dto.PosterBarcode;
import com.quaint.poster.core.impl.PosterBarcodeImpl;
import lombok.SneakyThrows;
import org.testng.annotations.Test;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
/**
* 〈测试海报〉<br>
*
* @author naiqing
* @Date 2022/1/22
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public class CommodityPoster2Test {
@SneakyThrows
@Test
public void testCreate() {
//背景图,此处测试我是用的本地图,实际开发可换成网络获取图片(下面有示例)
BufferedImage backgroundImage = ImageIO.read(new File("D:\\temp\\s_bg.jpg"));
//商品主图
BufferedImage commodityImage = ImageIO.read(new File("D:\\temp\\s_commodity.jpg"));
//严选图标
BufferedImage yanxuanImage = ImageIO.read(new File("D:\\temp\\s_yanxuan.jpg"));
//头像
BufferedImage headImage = ImageIO.read(new File("D:\\temp\\s_head.jpg"));
//二维码logo
BufferedImage qrLogo = headImage;
//二维码内容
String qrContent = "http://www.baidu.com";
//全局默认字体(如果不将字体库资源引入,在windows下没问题,但linux没有默认的"宋体"会显示乱码)
InputStream in1 = this.getClass().getResourceAsStream("/fonts/simkai.ttf");
Font defaultFont = Font.createFont(Font.TRUETYPE_FONT, in1);
//昵称显示的字体(此字体库是从网上下载的,非windows自带),使用时引用FontName字体名称即可(字体名称:双击字体库可以看到字体名称)
InputStream in2 = this.getClass().getResourceAsStream("/fonts/TTAiQingXiJ_0.ttf");
Font txin2 = Font.createFont(Font.TRUETYPE_FONT, in2);
CommodityPoster2Pojo shareCommodityInfo = CommodityPoster2Pojo.builder()
.backgroundImage(backgroundImage)
.commodityImage(commodityImage)
.yanxuanImage(yanxuanImage)
.commodityTitle(" 雾中女孩2022冬季新款女装连帽拉链长袖棉服 WH88888")
.vipPrice("特权价¥0.01")
.oldPrice("售价¥268")
.headImage(headImage)
.nickName("叫我胡歌啊")
.nickDesc("邀请好友享受内部优惠价")
.qrCode(PosterBarcode.builder().content(qrContent).logo(qrLogo).build())
.qrCodeText("微信扫一扫")
.build();
//创建海报生成器
PosterBarcodeImpl<CommodityPoster2Pojo> poster = new PosterBarcodeImpl<>();
//设置默认字体库 /安装用到的全部字体库(支持传多个)
poster.defaultFont(defaultFont).installFonts(txin2,...);
//生成海报
BufferedImage image = poster.annotationDrawPoster(shareCommodityInfo).draw(null);
ImageIO.write(image, "png", new File("D:\\temp\\out2.png"));
}
}
可以看到用起来还是非常简单的,开箱即用!有了这个工具,5分钟可以搞一个海报出来!再也不需要去考虑APP端的不确定的兼容问题了,也不用兴师动众让安卓、苹果、H5等多端的开发者多人开发了。美滋滋~
我上面的示例是使用的本地图,也可以使用图片链接。
//远程图片
BufferedImage backgroundImage = ImageIO.read(new URL("https://img-blog.csdnimg.cn/8bf7094ae7b044f495035c8ea734dca8.png"));
如何使用自定义字体库?
当我们在制作海报时,视觉给的效果图可能除了颜色、粗体要求以外,可能还会要求使用指定的字体。此时我们要拿到此字体库文件”*.ttf“并放到项目资源目录下,比如我的是resources\fonts\*.ttf。
第二步,是我们要得到此字体库的字体名称,在创建海报时配置@PosterFontCss 设置字体需要指定字体名称。双击此字体库文件,第一行就能看到字体名称,如下图:
如何将海报图片转成base64返回给前端
//将生成的海报图片以base64形式回写至终端
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ImageIO.write(image, "png", stream);
String asBase64 = "data:image/png;base64,"+Base64.encode(stream.toByteArray());
System.out.println(asBase64);
三、 用到的技术及相关源码
3.1 Easy Poster 易海报生成器
以上的源码和示例代码均在gitee上,可直接下载。欢迎大家一起来修复完善此工具。
https://gitee.com/naiqing/easyposter
在此特别感谢框架Easy Poster 的作者,我是基于此框架,修复了一些问题后进行的二次开发。
3.3 二维码生成zxing
对于二维码的生成我是使用了谷歌的zxing,然后利用hutool-all来简化调用。