PDF水印接口设计
水印元素的样式
{字体、 字号、 颜色、 深浅、 排列、 角度、透明度}
一、需要依赖
<!-- itext-pdf 依赖 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>${itextpdf.version}</version>
<!-- 本文采用版本:5.5.7 -->
</dependency>
二、字体设计
将ttf格式的字体文件,预置到系统内,然后通过此方法读取,加载成代码可用字体,中英文和汉字最好不用同一套字体,否则很丑
/**
* 判断是否包含中文
* @param text
* @return
*/
public static boolean isChinese(String text){
Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
Matcher m = p.matcher(text);
if (m.find()) {
return true;
}
return false;
}
/**
* 判断是不是中文,拿不同的字体
* @param isChinese
* @return
*/
private static BaseFont getFont(boolean isChinese){
BaseFont base = null;
try {
// 设置字体
if (isChinese) {
base = BaseFont.createFont(CN_FONT_NAME, CN_FONT_ENCODING, BaseFont.NOT_EMBEDDED);
} else {
base = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);
}
} catch (Exception e) {
logger.error("水印构建字体出错:",e);
e.printStackTrace();
}
return base;
}
三、水印文字的颜色
/**
* 构建一个颜色用于填充水印, 传进来的是 #999999这种的颜色
* @param color 16进制颜色,ex:#9999999
* @return
*/
private static BaseColor getBaseColor(String color){
int rgb = Color.decode(color).getRGB();
BaseColor baseColor = new BaseColor(rgb);
return baseColor;
}
四、透明度
/**
* 设置水印画笔透明度
* @return
*/
public static PdfGState getDefaultGState(){
PdfGState gs = new PdfGState();
// 设置透明度为0.4
gs.setFillOpacity(0.4f);
gs.setStrokeOpacity(0.4f);
return gs;
}
五、倾斜度计算
为了能够让水印撑满对角,需要动态计算倾斜度,或者手动设置固定倾斜度,以pdf页面高度和宽度为两条直角边,计算arctan(width, height)
/**
* 计算对角的倾斜度 arctan(x/y)
* @param x 页面宽度
* @param y 页面高度
* @return
*/
public static float getRotation(float x, float y){
return (float) Math.toDegrees(Math.atan2(y,x));
}
六、具体添加水印方法
public static void addWaterMark(PdfStamper stamper, String watermark, String color, int intensive, int fontSize, float rotation) {
PdfGState gs = getDefaultGState();
//先找好字体
BaseFont baseFont = getFont(isChinese(watermark));
//找到颜色
BaseColor baseColor = getBaseColor(color);
try {
if (baseFont == null || stamper == null) {
return;
}
while(watermark.length() <= 100){
watermark += " " + watermark;
}
PdfContentByte content = null;
Rectangle pageRectangle = null;
int toPage = stamper.getReader().getNumberOfPages();
for (int i = 1; i <= toPage; i++) {
pageRectangle = stamper.getReader().getPageSizeWithRotation(i);
// 计算水印X,Y坐标
float x = pageRectangle.getWidth();
float y = pageRectangle.getHeight();
//按照页面对角计算倾斜度
if(rotation == 0f){
rotation = getRotation(x, y);
}
// 获得PDF最顶层
content = stamper.getOverContent(i);
content.saveState();
// 设置画布
content.setGState(gs);
content.beginText();
content.setColorFill(baseColor);
content.setFontAndSize(baseFont, fontSize);
//6行水印,则拆分成7条线, 3行水印,则拆分成4条线
int totalLine = intensive + 1;
for (int j = 1; j <= intensive; j++) {
content.showTextAligned(Element.ALIGN_CENTER, watermark, x * (intensive- j) / totalLine, y * j / totalLine, rotation);
}
content.endText();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
七、核心代码解释
- 具体添加水印api
/**
* Shows text right, left or center aligned with rotation.
* @param 对齐方式 ALIGN_CENTER, ALIGN_RIGHT or ALIGN_LEFT
* @param text 需要显示的文本
* @param x 横向开始位置
* @param y 纵向开始位置
* @param rotation 倾斜度
*/
public void showTextAligned(final int alignment, final String text, final float x, final float y, final float rotation) {
//具体代码有兴趣的同学可以自行研究
}
- 水印开始位置计算方式
假设你要做6行水印,则你可以考虑把整个页面分成7份,横向开始处为 x * (6- i) / 7,纵向开始处为 y * j / 7, 如下图
八、重载方法,更方便应用
大家可以自行重载,此处仅做演示
/**
* 中文水印的字体
*/
private final static String CN_FONT_NAME = "STSongStd-Light";
/**
* 中文下字符编码
*/
private final static String CN_FONT_ENCODING = "UniGB-UCS2-H";
/**
* 默认字号
*/
private final static int DEFAULT_FONT_SIZE = 26;
/**
* 默认颜色
*/
private final static String DEFAULT_COLOR = "#999999";
/**
* 默认列数
*/
private final static int DEFAULT_INTENSIVE = 3;
/**
* <p>
* 重载方法
* </p>
* @param stamper pdf文件
* @param watermark 水印文字
*/
public static void addWaterMark(PdfStamper stamper, String watermark){
addWaterMark(stamper, watermark, DEFAULT_COLOR, DEFAULT_INTENSIVE, DEFAULT_FONT_SIZE, 0f);
}
/**
* <p>
* 重载方法
* </p>
* @param stamper pdf文件
* @param watermark 水印文字
* @param color 颜色 #999999类型
* @param intensive 水印行数
*/
public static void addWaterMark(PdfStamper stamper, String watermark, String color, int intensive){
addWaterMark(stamper, watermark, color, intensive, DEFAULT_FONT_SIZE, 0f);
}
九、调用演示
/**
* <p>
* 测试水印工具
* </p>
*/
public void test() throws IOException {
String filePath = "/data/test/";
String file = filePath + "test1.pdf";
InputStream inputStream = new FileInputStream(file);
PdfReader reader = new PdfReader(inputStream);
String localName = UUID.randomUUID().toString().replace("-","")+ ".pdf";
try {
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream( file + localName));
addWaterMark(stamper, "测试水印");
} catch (DocumentException e) {
e.printStackTrace();
}
}
最后:经过调研,发现pdf-box更适合做成服务,功能也更多。关于pdf-box添加水印的方式,本人已经书写完毕,写成了服务,加持dubbo或者SpringCloud都可以直接调用,后续上传,有需要的提前私我拿