Java——PDF水印接口设计

原创
2020/05/20 18:23
阅读数 3.1K

PDF水印接口设计

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();
    }
}

七、核心代码解释

  1. 具体添加水印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) {
	//具体代码有兴趣的同学可以自行研究
    }
  1. 水印开始位置计算方式

假设你要做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都可以直接调用,后续上传,有需要的提前私我拿

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
1
分享
返回顶部
顶部